From ace9429bb58fd418f0c81d4c2835699bddf6bde6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:27:49 +0200 Subject: Adding upstream version 6.6.15. Signed-off-by: Daniel Baumann --- sound/soc/codecs/88pm860x-codec.c | 1402 ++++++ sound/soc/codecs/88pm860x-codec.h | 93 + sound/soc/codecs/Kconfig | 2407 +++++++++++ sound/soc/codecs/Makefile | 774 ++++ sound/soc/codecs/ab8500-codec.c | 2574 +++++++++++ sound/soc/codecs/ab8500-codec.h | 587 +++ sound/soc/codecs/ac97.c | 143 + sound/soc/codecs/ad1836.c | 409 ++ sound/soc/codecs/ad1836.h | 50 + sound/soc/codecs/ad193x-i2c.c | 48 + sound/soc/codecs/ad193x-spi.c | 53 + sound/soc/codecs/ad193x.c | 560 +++ sound/soc/codecs/ad193x.h | 110 + sound/soc/codecs/ad1980.c | 325 ++ sound/soc/codecs/ad73311.c | 81 + sound/soc/codecs/ad73311.h | 73 + sound/soc/codecs/adau-utils.c | 60 + sound/soc/codecs/adau-utils.h | 8 + sound/soc/codecs/adau1372-i2c.c | 40 + sound/soc/codecs/adau1372-spi.c | 58 + sound/soc/codecs/adau1372.c | 1065 +++++ sound/soc/codecs/adau1372.h | 21 + sound/soc/codecs/adau1373.c | 1516 +++++++ sound/soc/codecs/adau1373.h | 30 + sound/soc/codecs/adau1701.c | 888 ++++ sound/soc/codecs/adau1701.h | 16 + sound/soc/codecs/adau1761-i2c.c | 71 + sound/soc/codecs/adau1761-spi.c | 86 + sound/soc/codecs/adau1761.c | 1023 +++++ sound/soc/codecs/adau1761.h | 22 + sound/soc/codecs/adau1781-i2c.c | 67 + sound/soc/codecs/adau1781-spi.c | 82 + sound/soc/codecs/adau1781.c | 507 +++ sound/soc/codecs/adau1781.h | 22 + sound/soc/codecs/adau17x1.c | 1123 +++++ sound/soc/codecs/adau17x1.h | 134 + sound/soc/codecs/adau1977-i2c.c | 52 + sound/soc/codecs/adau1977-spi.c | 78 + sound/soc/codecs/adau1977.c | 1002 +++++ sound/soc/codecs/adau1977.h | 36 + sound/soc/codecs/adau7002.c | 131 + sound/soc/codecs/adau7118-hw.c | 43 + sound/soc/codecs/adau7118-i2c.c | 88 + sound/soc/codecs/adau7118.c | 568 +++ sound/soc/codecs/adau7118.h | 24 + sound/soc/codecs/adav801.c | 44 + sound/soc/codecs/adav803.c | 40 + sound/soc/codecs/adav80x.c | 882 ++++ sound/soc/codecs/adav80x.h | 41 + sound/soc/codecs/ads117x.c | 95 + sound/soc/codecs/ak4104.c | 342 ++ sound/soc/codecs/ak4118.c | 417 ++ sound/soc/codecs/ak4375.c | 607 +++ sound/soc/codecs/ak4458.c | 830 ++++ sound/soc/codecs/ak4458.h | 90 + sound/soc/codecs/ak4535.c | 450 ++ sound/soc/codecs/ak4535.h | 34 + sound/soc/codecs/ak4554.c | 96 + sound/soc/codecs/ak4613.c | 927 ++++ sound/soc/codecs/ak4641.c | 640 +++ sound/soc/codecs/ak4642.c | 709 +++ sound/soc/codecs/ak4671.c | 666 +++ sound/soc/codecs/ak4671.h | 146 + sound/soc/codecs/ak5386.c | 208 + sound/soc/codecs/ak5558.c | 509 +++ sound/soc/codecs/ak5558.h | 52 + sound/soc/codecs/alc5623.c | 1094 +++++ sound/soc/codecs/alc5623.h | 157 + sound/soc/codecs/alc5632.c | 1193 +++++ sound/soc/codecs/alc5632.h | 249 ++ sound/soc/codecs/arizona-jack.c | 1657 +++++++ sound/soc/codecs/arizona.c | 2861 ++++++++++++ sound/soc/codecs/arizona.h | 398 ++ sound/soc/codecs/audio-iio-aux.c | 344 ++ sound/soc/codecs/aw8738.c | 104 + sound/soc/codecs/aw88261.c | 1300 ++++++ sound/soc/codecs/aw88261.h | 459 ++ sound/soc/codecs/aw88395/aw88395.c | 579 +++ sound/soc/codecs/aw88395/aw88395.h | 58 + sound/soc/codecs/aw88395/aw88395_data_type.h | 142 + sound/soc/codecs/aw88395/aw88395_device.c | 1748 ++++++++ sound/soc/codecs/aw88395/aw88395_device.h | 194 + sound/soc/codecs/aw88395/aw88395_lib.c | 1227 ++++++ sound/soc/codecs/aw88395/aw88395_lib.h | 92 + sound/soc/codecs/aw88395/aw88395_reg.h | 384 ++ sound/soc/codecs/bd28623.c | 241 ++ sound/soc/codecs/bt-sco.c | 114 + sound/soc/codecs/chv3-codec.c | 41 + sound/soc/codecs/cirrus_legacy.h | 21 + sound/soc/codecs/cpcap.c | 1689 ++++++++ sound/soc/codecs/cq93vc.c | 149 + sound/soc/codecs/cros_ec_codec.c | 1069 +++++ sound/soc/codecs/cs35l32.c | 583 +++ sound/soc/codecs/cs35l32.h | 89 + sound/soc/codecs/cs35l33.c | 1291 ++++++ sound/soc/codecs/cs35l33.h | 217 + sound/soc/codecs/cs35l34.c | 1242 ++++++ sound/soc/codecs/cs35l34.h | 265 ++ sound/soc/codecs/cs35l35.c | 1665 +++++++ sound/soc/codecs/cs35l35.h | 297 ++ sound/soc/codecs/cs35l36.c | 1954 +++++++++ sound/soc/codecs/cs35l36.h | 446 ++ sound/soc/codecs/cs35l41-i2c.c | 99 + sound/soc/codecs/cs35l41-lib.c | 1584 +++++++ sound/soc/codecs/cs35l41-spi.c | 99 + sound/soc/codecs/cs35l41.c | 1469 +++++++ sound/soc/codecs/cs35l41.h | 41 + sound/soc/codecs/cs35l45-i2c.c | 76 + sound/soc/codecs/cs35l45-spi.c | 78 + sound/soc/codecs/cs35l45-tables.c | 329 ++ sound/soc/codecs/cs35l45.c | 1346 ++++++ sound/soc/codecs/cs35l45.h | 482 +++ sound/soc/codecs/cs35l56-i2c.c | 90 + sound/soc/codecs/cs35l56-sdw.c | 568 +++ sound/soc/codecs/cs35l56-shared.c | 807 ++++ sound/soc/codecs/cs35l56-spi.c | 88 + sound/soc/codecs/cs35l56.c | 1250 ++++++ sound/soc/codecs/cs35l56.h | 70 + sound/soc/codecs/cs4234.c | 916 ++++ sound/soc/codecs/cs4234.h | 287 ++ sound/soc/codecs/cs4265.c | 660 +++ sound/soc/codecs/cs4265.h | 60 + sound/soc/codecs/cs4270.c | 762 ++++ sound/soc/codecs/cs4271-i2c.c | 43 + sound/soc/codecs/cs4271-spi.c | 37 + sound/soc/codecs/cs4271.c | 720 ++++ sound/soc/codecs/cs4271.h | 12 + sound/soc/codecs/cs42l42-i2c.c | 104 + sound/soc/codecs/cs42l42-sdw.c | 625 +++ sound/soc/codecs/cs42l42.c | 2500 +++++++++++ sound/soc/codecs/cs42l42.h | 82 + sound/soc/codecs/cs42l43-jack.c | 946 ++++ sound/soc/codecs/cs42l43-sdw.c | 74 + sound/soc/codecs/cs42l43.c | 2281 ++++++++++ sound/soc/codecs/cs42l43.h | 131 + sound/soc/codecs/cs42l51-i2c.c | 61 + sound/soc/codecs/cs42l51.c | 828 ++++ sound/soc/codecs/cs42l51.h | 161 + sound/soc/codecs/cs42l52.c | 1237 ++++++ sound/soc/codecs/cs42l52.h | 270 ++ sound/soc/codecs/cs42l56.c | 1352 ++++++ sound/soc/codecs/cs42l56.h | 173 + sound/soc/codecs/cs42l73.c | 1396 ++++++ sound/soc/codecs/cs42l73.h | 212 + sound/soc/codecs/cs42l83-i2c.c | 240 ++ sound/soc/codecs/cs42xx8-i2c.c | 82 + sound/soc/codecs/cs42xx8.c | 679 +++ sound/soc/codecs/cs42xx8.h | 238 + sound/soc/codecs/cs43130.c | 2708 ++++++++++++ sound/soc/codecs/cs43130.h | 538 +++ sound/soc/codecs/cs4341.c | 352 ++ sound/soc/codecs/cs4349.c | 386 ++ sound/soc/codecs/cs4349.h | 127 + sound/soc/codecs/cs47l15.c | 1506 +++++++ sound/soc/codecs/cs47l24.c | 1355 ++++++ sound/soc/codecs/cs47l24.h | 20 + sound/soc/codecs/cs47l35.c | 1781 ++++++++ sound/soc/codecs/cs47l85.c | 2732 ++++++++++++ sound/soc/codecs/cs47l90.c | 2656 ++++++++++++ sound/soc/codecs/cs47l92.c | 2104 +++++++++ sound/soc/codecs/cs53l30.c | 1128 +++++ sound/soc/codecs/cs53l30.h | 455 ++ sound/soc/codecs/cx20442.c | 434 ++ sound/soc/codecs/cx20442.h | 13 + sound/soc/codecs/cx2072x.c | 1725 ++++++++ sound/soc/codecs/cx2072x.h | 314 ++ sound/soc/codecs/da7210.c | 1364 ++++++ sound/soc/codecs/da7213.c | 2081 +++++++++ sound/soc/codecs/da7213.h | 550 +++ sound/soc/codecs/da7218.c | 3328 ++++++++++++++ sound/soc/codecs/da7218.h | 1411 ++++++ sound/soc/codecs/da7219-aad.c | 1071 +++++ sound/soc/codecs/da7219-aad.h | 222 + sound/soc/codecs/da7219.c | 2725 ++++++++++++ sound/soc/codecs/da7219.h | 839 ++++ sound/soc/codecs/da732x.c | 1567 +++++++ sound/soc/codecs/da732x.h | 123 + sound/soc/codecs/da732x_reg.h | 651 +++ sound/soc/codecs/da9055.c | 1542 +++++++ sound/soc/codecs/dmic.c | 193 + sound/soc/codecs/es7134.c | 316 ++ sound/soc/codecs/es7241.c | 311 ++ sound/soc/codecs/es8316.c | 903 ++++ sound/soc/codecs/es8316.h | 132 + sound/soc/codecs/es8326.c | 1061 +++++ sound/soc/codecs/es8326.h | 196 + sound/soc/codecs/es8328-i2c.c | 51 + sound/soc/codecs/es8328-spi.c | 39 + sound/soc/codecs/es8328.c | 883 ++++ sound/soc/codecs/es8328.h | 290 ++ sound/soc/codecs/gtm601.c | 109 + sound/soc/codecs/hda-dai.c | 102 + sound/soc/codecs/hda.c | 393 ++ sound/soc/codecs/hda.h | 19 + sound/soc/codecs/hdac_hda.c | 647 +++ sound/soc/codecs/hdac_hda.h | 33 + sound/soc/codecs/hdac_hdmi.c | 2347 ++++++++++ sound/soc/codecs/hdac_hdmi.h | 10 + sound/soc/codecs/hdmi-codec.c | 1108 +++++ sound/soc/codecs/ics43432.c | 74 + sound/soc/codecs/idt821034.c | 1178 +++++ sound/soc/codecs/inno_rk3036.c | 486 +++ sound/soc/codecs/inno_rk3036.h | 124 + sound/soc/codecs/isabelle.c | 1154 +++++ sound/soc/codecs/isabelle.h | 139 + sound/soc/codecs/jz4725b.c | 667 +++ sound/soc/codecs/jz4740.c | 356 ++ sound/soc/codecs/jz4760.c | 895 ++++ sound/soc/codecs/jz4770.c | 947 ++++ sound/soc/codecs/lm4857.c | 148 + sound/soc/codecs/lm49453.c | 1462 +++++++ sound/soc/codecs/lm49453.h | 376 ++ sound/soc/codecs/lochnagar-sc.c | 266 ++ sound/soc/codecs/lpass-macro-common.c | 70 + sound/soc/codecs/lpass-macro-common.h | 20 + sound/soc/codecs/lpass-rx-macro.c | 3750 ++++++++++++++++ sound/soc/codecs/lpass-tx-macro.c | 2196 ++++++++++ sound/soc/codecs/lpass-va-macro.c | 1645 +++++++ sound/soc/codecs/lpass-wsa-macro.c | 2598 +++++++++++ sound/soc/codecs/lpass-wsa-macro.h | 17 + sound/soc/codecs/madera.c | 4811 +++++++++++++++++++++ sound/soc/codecs/madera.h | 458 ++ sound/soc/codecs/max9759.c | 198 + sound/soc/codecs/max9768.c | 224 + sound/soc/codecs/max98088.c | 1800 ++++++++ sound/soc/codecs/max98088.h | 203 + sound/soc/codecs/max98090.c | 2706 ++++++++++++ sound/soc/codecs/max98090.h | 1549 +++++++ sound/soc/codecs/max98095.c | 2166 ++++++++++ sound/soc/codecs/max98095.h | 318 ++ sound/soc/codecs/max98357a.c | 183 + sound/soc/codecs/max98363.c | 466 ++ sound/soc/codecs/max98363.h | 36 + sound/soc/codecs/max98371.c | 430 ++ sound/soc/codecs/max98371.h | 63 + sound/soc/codecs/max98373-i2c.c | 635 +++ sound/soc/codecs/max98373-sdw.c | 890 ++++ sound/soc/codecs/max98373-sdw.h | 72 + sound/soc/codecs/max98373.c | 506 +++ sound/soc/codecs/max98373.h | 240 ++ sound/soc/codecs/max98388.c | 1013 +++++ sound/soc/codecs/max98388.h | 234 + sound/soc/codecs/max98390.c | 1144 +++++ sound/soc/codecs/max98390.h | 667 +++ sound/soc/codecs/max98396.c | 1918 +++++++++ sound/soc/codecs/max98396.h | 327 ++ sound/soc/codecs/max9850.c | 340 ++ sound/soc/codecs/max9850.h | 33 + sound/soc/codecs/max98504.c | 380 ++ sound/soc/codecs/max98504.h | 56 + sound/soc/codecs/max98520.c | 768 ++++ sound/soc/codecs/max98520.h | 159 + sound/soc/codecs/max9860.c | 740 ++++ sound/soc/codecs/max9860.h | 154 + sound/soc/codecs/max9867.c | 713 +++ sound/soc/codecs/max9867.h | 67 + sound/soc/codecs/max9877.c | 171 + sound/soc/codecs/max9877.h | 30 + sound/soc/codecs/max98925.c | 646 +++ sound/soc/codecs/max98925.h | 829 ++++ sound/soc/codecs/max98926.c | 593 +++ sound/soc/codecs/max98926.h | 846 ++++ sound/soc/codecs/max98927.c | 918 ++++ sound/soc/codecs/max98927.h | 271 ++ sound/soc/codecs/mc13783.c | 789 ++++ sound/soc/codecs/mc13783.h | 16 + sound/soc/codecs/ml26124.c | 592 +++ sound/soc/codecs/ml26124.h | 172 + sound/soc/codecs/msm8916-wcd-analog.c | 1280 ++++++ sound/soc/codecs/msm8916-wcd-digital.c | 1251 ++++++ sound/soc/codecs/mt6351.c | 1496 +++++++ sound/soc/codecs/mt6351.h | 105 + sound/soc/codecs/mt6358.c | 2499 +++++++++++ sound/soc/codecs/mt6358.h | 2314 ++++++++++ sound/soc/codecs/mt6359-accdet.c | 1066 +++++ sound/soc/codecs/mt6359-accdet.h | 128 + sound/soc/codecs/mt6359.c | 2959 +++++++++++++ sound/soc/codecs/mt6359.h | 4289 ++++++++++++++++++ sound/soc/codecs/mt6660.c | 582 +++ sound/soc/codecs/mt6660.h | 77 + sound/soc/codecs/nau8315.c | 167 + sound/soc/codecs/nau8540.c | 900 ++++ sound/soc/codecs/nau8540.h | 239 ++ sound/soc/codecs/nau8810.c | 925 ++++ sound/soc/codecs/nau8810.h | 286 ++ sound/soc/codecs/nau8821.c | 1951 +++++++++ sound/soc/codecs/nau8821.h | 582 +++ sound/soc/codecs/nau8822.c | 1179 +++++ sound/soc/codecs/nau8822.h | 224 + sound/soc/codecs/nau8824.c | 2041 +++++++++ sound/soc/codecs/nau8824.h | 477 ++ sound/soc/codecs/nau8825.c | 2976 +++++++++++++ sound/soc/codecs/nau8825.h | 538 +++ sound/soc/codecs/pcm1681.c | 337 ++ sound/soc/codecs/pcm1789-i2c.c | 63 + sound/soc/codecs/pcm1789.c | 271 ++ sound/soc/codecs/pcm1789.h | 17 + sound/soc/codecs/pcm179x-i2c.c | 59 + sound/soc/codecs/pcm179x-spi.c | 58 + sound/soc/codecs/pcm179x.c | 231 + sound/soc/codecs/pcm179x.h | 18 + sound/soc/codecs/pcm186x-i2c.c | 61 + sound/soc/codecs/pcm186x-spi.c | 61 + sound/soc/codecs/pcm186x.c | 704 +++ sound/soc/codecs/pcm186x.h | 219 + sound/soc/codecs/pcm3008.c | 163 + sound/soc/codecs/pcm3008.h | 19 + sound/soc/codecs/pcm3060-i2c.c | 59 + sound/soc/codecs/pcm3060-spi.c | 59 + sound/soc/codecs/pcm3060.c | 347 ++ sound/soc/codecs/pcm3060.h | 96 + sound/soc/codecs/pcm3168a-i2c.c | 60 + sound/soc/codecs/pcm3168a-spi.c | 60 + sound/soc/codecs/pcm3168a.c | 915 ++++ sound/soc/codecs/pcm3168a.h | 97 + sound/soc/codecs/pcm5102a.c | 57 + sound/soc/codecs/pcm512x-i2c.c | 84 + sound/soc/codecs/pcm512x-spi.c | 67 + sound/soc/codecs/pcm512x.c | 1769 ++++++++ sound/soc/codecs/pcm512x.h | 264 ++ sound/soc/codecs/peb2466.c | 2071 +++++++++ sound/soc/codecs/rk3328_codec.c | 534 +++ sound/soc/codecs/rk3328_codec.h | 210 + sound/soc/codecs/rk817_codec.c | 541 +++ sound/soc/codecs/rl6231.c | 253 ++ sound/soc/codecs/rl6231.h | 33 + sound/soc/codecs/rl6347a.c | 108 + sound/soc/codecs/rl6347a.h | 31 + sound/soc/codecs/rt1011.c | 2492 +++++++++++ sound/soc/codecs/rt1011.h | 704 +++ sound/soc/codecs/rt1015.c | 1180 +++++ sound/soc/codecs/rt1015.h | 449 ++ sound/soc/codecs/rt1015p.c | 154 + sound/soc/codecs/rt1016.c | 693 +++ sound/soc/codecs/rt1016.h | 232 + sound/soc/codecs/rt1017-sdca-sdw.c | 824 ++++ sound/soc/codecs/rt1017-sdca-sdw.h | 183 + sound/soc/codecs/rt1019.c | 609 +++ sound/soc/codecs/rt1019.h | 164 + sound/soc/codecs/rt1305.c | 1179 +++++ sound/soc/codecs/rt1305.h | 273 ++ sound/soc/codecs/rt1308-sdw.c | 819 ++++ sound/soc/codecs/rt1308-sdw.h | 172 + sound/soc/codecs/rt1308.c | 871 ++++ sound/soc/codecs/rt1308.h | 294 ++ sound/soc/codecs/rt1316-sdw.c | 796 ++++ sound/soc/codecs/rt1316-sdw.h | 52 + sound/soc/codecs/rt1318-sdw.c | 870 ++++ sound/soc/codecs/rt1318-sdw.h | 96 + sound/soc/codecs/rt274.c | 1233 ++++++ sound/soc/codecs/rt274.h | 214 + sound/soc/codecs/rt286.c | 1275 ++++++ sound/soc/codecs/rt286.h | 200 + sound/soc/codecs/rt298.c | 1323 ++++++ sound/soc/codecs/rt298.h | 211 + sound/soc/codecs/rt5514-spi.c | 514 +++ sound/soc/codecs/rt5514-spi.h | 37 + sound/soc/codecs/rt5514.c | 1337 ++++++ sound/soc/codecs/rt5514.h | 286 ++ sound/soc/codecs/rt5616.c | 1416 ++++++ sound/soc/codecs/rt5616.h | 1816 ++++++++ sound/soc/codecs/rt5631.c | 1740 ++++++++ sound/soc/codecs/rt5631.h | 702 +++ sound/soc/codecs/rt5640.c | 3060 +++++++++++++ sound/soc/codecs/rt5640.h | 2193 ++++++++++ sound/soc/codecs/rt5645.c | 4261 ++++++++++++++++++ sound/soc/codecs/rt5645.h | 2204 ++++++++++ sound/soc/codecs/rt5651.c | 2289 ++++++++++ sound/soc/codecs/rt5651.h | 2102 +++++++++ sound/soc/codecs/rt5659.c | 4346 +++++++++++++++++++ sound/soc/codecs/rt5659.h | 1821 ++++++++ sound/soc/codecs/rt5660.c | 1349 ++++++ sound/soc/codecs/rt5660.h | 847 ++++ sound/soc/codecs/rt5663.c | 3745 ++++++++++++++++ sound/soc/codecs/rt5663.h | 1128 +++++ sound/soc/codecs/rt5665.c | 4980 +++++++++++++++++++++ sound/soc/codecs/rt5665.h | 2005 +++++++++ sound/soc/codecs/rt5668.c | 2628 +++++++++++ sound/soc/codecs/rt5668.h | 1315 ++++++ sound/soc/codecs/rt5670-dsp.h | 51 + sound/soc/codecs/rt5670.c | 3340 ++++++++++++++ sound/soc/codecs/rt5670.h | 2029 +++++++++ sound/soc/codecs/rt5677-spi.c | 637 +++ sound/soc/codecs/rt5677-spi.h | 33 + sound/soc/codecs/rt5677.c | 5675 ++++++++++++++++++++++++ sound/soc/codecs/rt5677.h | 1808 ++++++++ sound/soc/codecs/rt5682-i2c.c | 342 ++ sound/soc/codecs/rt5682-sdw.c | 813 ++++ sound/soc/codecs/rt5682.c | 3179 ++++++++++++++ sound/soc/codecs/rt5682.h | 1497 +++++++ sound/soc/codecs/rt5682s.c | 3320 ++++++++++++++ sound/soc/codecs/rt5682s.h | 1485 +++++++ sound/soc/codecs/rt700-sdw.c | 573 +++ sound/soc/codecs/rt700-sdw.h | 335 ++ sound/soc/codecs/rt700.c | 1243 ++++++ sound/soc/codecs/rt700.h | 171 + sound/soc/codecs/rt711-sdca-sdw.c | 489 +++ sound/soc/codecs/rt711-sdca-sdw.h | 99 + sound/soc/codecs/rt711-sdca.c | 1602 +++++++ sound/soc/codecs/rt711-sdca.h | 243 ++ sound/soc/codecs/rt711-sdw.c | 584 +++ sound/soc/codecs/rt711-sdw.h | 283 ++ sound/soc/codecs/rt711.c | 1343 ++++++ sound/soc/codecs/rt711.h | 251 ++ sound/soc/codecs/rt712-sdca-dmic.c | 991 +++++ sound/soc/codecs/rt712-sdca-dmic.h | 107 + sound/soc/codecs/rt712-sdca-sdw.c | 489 +++ sound/soc/codecs/rt712-sdca-sdw.h | 108 + sound/soc/codecs/rt712-sdca.c | 1341 ++++++ sound/soc/codecs/rt712-sdca.h | 215 + sound/soc/codecs/rt715-sdca-sdw.c | 285 ++ sound/soc/codecs/rt715-sdca-sdw.h | 171 + sound/soc/codecs/rt715-sdca.c | 1084 +++++ sound/soc/codecs/rt715-sdca.h | 132 + sound/soc/codecs/rt715-sdw.c | 592 +++ sound/soc/codecs/rt715-sdw.h | 337 ++ sound/soc/codecs/rt715.c | 1151 +++++ sound/soc/codecs/rt715.h | 227 + sound/soc/codecs/rt722-sdca-sdw.c | 515 +++ sound/soc/codecs/rt722-sdca-sdw.h | 124 + sound/soc/codecs/rt722-sdca.c | 1554 +++++++ sound/soc/codecs/rt722-sdca.h | 237 + sound/soc/codecs/rt9120.c | 643 +++ sound/soc/codecs/sdw-mockup.c | 276 ++ sound/soc/codecs/sgtl5000.c | 1839 ++++++++ sound/soc/codecs/sgtl5000.h | 408 ++ sound/soc/codecs/si476x.c | 263 ++ sound/soc/codecs/sigmadsp-i2c.c | 96 + sound/soc/codecs/sigmadsp-regmap.c | 59 + sound/soc/codecs/sigmadsp.c | 807 ++++ sound/soc/codecs/sigmadsp.h | 64 + sound/soc/codecs/simple-amplifier.c | 110 + sound/soc/codecs/simple-mux.c | 129 + sound/soc/codecs/sma1303.c | 1820 ++++++++ sound/soc/codecs/sma1303.h | 609 +++ sound/soc/codecs/spdif_receiver.c | 86 + sound/soc/codecs/spdif_transmitter.c | 87 + sound/soc/codecs/src4xxx-i2c.c | 46 + sound/soc/codecs/src4xxx.c | 518 +++ sound/soc/codecs/src4xxx.h | 113 + sound/soc/codecs/ssm2305.c | 99 + sound/soc/codecs/ssm2518.c | 813 ++++ sound/soc/codecs/ssm2518.h | 19 + sound/soc/codecs/ssm2602-i2c.c | 59 + sound/soc/codecs/ssm2602-spi.c | 39 + sound/soc/codecs/ssm2602.c | 694 +++ sound/soc/codecs/ssm2602.h | 125 + sound/soc/codecs/ssm3515.c | 448 ++ sound/soc/codecs/ssm4567.c | 510 +++ sound/soc/codecs/sta32x.c | 1178 +++++ sound/soc/codecs/sta32x.h | 207 + sound/soc/codecs/sta350.c | 1261 ++++++ sound/soc/codecs/sta350.h | 234 + sound/soc/codecs/sta529.c | 390 ++ sound/soc/codecs/stac9766.c | 336 ++ sound/soc/codecs/sti-sas.c | 476 ++ sound/soc/codecs/tas2552.c | 773 ++++ sound/soc/codecs/tas2552.h | 138 + sound/soc/codecs/tas2562.c | 794 ++++ sound/soc/codecs/tas2562.h | 90 + sound/soc/codecs/tas2764.c | 766 ++++ sound/soc/codecs/tas2764.h | 113 + sound/soc/codecs/tas2770.c | 730 ++++ sound/soc/codecs/tas2770.h | 145 + sound/soc/codecs/tas2780.c | 655 +++ sound/soc/codecs/tas2780.h | 101 + sound/soc/codecs/tas2781-comlib.c | 533 +++ sound/soc/codecs/tas2781-fmwlib.c | 2431 +++++++++++ sound/soc/codecs/tas2781-i2c.c | 764 ++++ sound/soc/codecs/tas5086.c | 1001 +++++ sound/soc/codecs/tas571x.c | 986 +++++ sound/soc/codecs/tas571x.h | 107 + sound/soc/codecs/tas5720.c | 831 ++++ sound/soc/codecs/tas5720.h | 127 + sound/soc/codecs/tas5805m.c | 613 +++ sound/soc/codecs/tas6424.c | 814 ++++ sound/soc/codecs/tas6424.h | 158 + sound/soc/codecs/tda7419.c | 640 +++ sound/soc/codecs/tfa9879.c | 323 ++ sound/soc/codecs/tfa9879.h | 197 + sound/soc/codecs/tfa989x.c | 425 ++ sound/soc/codecs/tlv320adc3xxx.c | 1463 +++++++ sound/soc/codecs/tlv320adcx140.c | 1218 ++++++ sound/soc/codecs/tlv320adcx140.h | 159 + sound/soc/codecs/tlv320aic23-i2c.c | 58 + sound/soc/codecs/tlv320aic23-spi.c | 45 + sound/soc/codecs/tlv320aic23.c | 614 +++ sound/soc/codecs/tlv320aic23.h | 122 + sound/soc/codecs/tlv320aic26.c | 378 ++ sound/soc/codecs/tlv320aic26.h | 91 + sound/soc/codecs/tlv320aic31xx.c | 1756 ++++++++ sound/soc/codecs/tlv320aic31xx.h | 244 ++ sound/soc/codecs/tlv320aic32x4-clk.c | 499 +++ sound/soc/codecs/tlv320aic32x4-i2c.c | 83 + sound/soc/codecs/tlv320aic32x4-spi.c | 83 + sound/soc/codecs/tlv320aic32x4.c | 1437 +++++++ sound/soc/codecs/tlv320aic32x4.h | 236 + sound/soc/codecs/tlv320aic3x-i2c.c | 73 + sound/soc/codecs/tlv320aic3x-spi.c | 78 + sound/soc/codecs/tlv320aic3x.c | 1855 ++++++++ sound/soc/codecs/tlv320aic3x.h | 344 ++ sound/soc/codecs/tlv320dac33.c | 1572 +++++++ sound/soc/codecs/tlv320dac33.h | 250 ++ sound/soc/codecs/tpa6130a2.c | 330 ++ sound/soc/codecs/tpa6130a2.h | 46 + sound/soc/codecs/ts3a227e.c | 465 ++ sound/soc/codecs/ts3a227e.h | 14 + sound/soc/codecs/tscs42xx.c | 1514 +++++++ sound/soc/codecs/tscs42xx.h | 2701 ++++++++++++ sound/soc/codecs/tscs454.c | 3484 +++++++++++++++ sound/soc/codecs/tscs454.h | 2323 ++++++++++ sound/soc/codecs/twl4030.c | 2222 ++++++++++ sound/soc/codecs/twl6040.c | 1176 +++++ sound/soc/codecs/twl6040.h | 30 + sound/soc/codecs/uda1334.c | 294 ++ sound/soc/codecs/uda1380.c | 809 ++++ sound/soc/codecs/uda1380.h | 72 + sound/soc/codecs/wcd-clsh-v2.c | 905 ++++ sound/soc/codecs/wcd-clsh-v2.h | 65 + sound/soc/codecs/wcd-mbhc-v2.c | 1584 +++++++ sound/soc/codecs/wcd-mbhc-v2.h | 340 ++ sound/soc/codecs/wcd9335.c | 5250 ++++++++++++++++++++++ sound/soc/codecs/wcd9335.h | 641 +++ sound/soc/codecs/wcd934x.c | 5974 ++++++++++++++++++++++++++ sound/soc/codecs/wcd938x-sdw.c | 1358 ++++++ sound/soc/codecs/wcd938x.c | 3657 ++++++++++++++++ sound/soc/codecs/wcd938x.h | 718 ++++ sound/soc/codecs/wl1273.c | 500 +++ sound/soc/codecs/wl1273.h | 16 + sound/soc/codecs/wm0010.c | 999 +++++ sound/soc/codecs/wm1250-ev1.c | 255 ++ sound/soc/codecs/wm2000.c | 949 ++++ sound/soc/codecs/wm2000.h | 71 + sound/soc/codecs/wm2200.c | 2497 +++++++++++ sound/soc/codecs/wm2200.h | 3670 ++++++++++++++++ sound/soc/codecs/wm5100-tables.c | 1481 +++++++ sound/soc/codecs/wm5100.c | 2721 ++++++++++++ sound/soc/codecs/wm5100.h | 5311 +++++++++++++++++++++++ sound/soc/codecs/wm5102.c | 2185 ++++++++++ sound/soc/codecs/wm5102.h | 20 + sound/soc/codecs/wm5110.c | 2545 +++++++++++ sound/soc/codecs/wm5110.h | 20 + sound/soc/codecs/wm8350.c | 1637 +++++++ sound/soc/codecs/wm8350.h | 25 + sound/soc/codecs/wm8400.c | 1346 ++++++ sound/soc/codecs/wm8400.h | 54 + sound/soc/codecs/wm8510.c | 720 ++++ sound/soc/codecs/wm8510.h | 99 + sound/soc/codecs/wm8523.c | 538 +++ sound/soc/codecs/wm8523.h | 154 + sound/soc/codecs/wm8524.c | 253 ++ sound/soc/codecs/wm8580.c | 1061 +++++ sound/soc/codecs/wm8580.h | 30 + sound/soc/codecs/wm8711.c | 506 +++ sound/soc/codecs/wm8711.h | 36 + sound/soc/codecs/wm8727.c | 78 + sound/soc/codecs/wm8728.c | 347 ++ sound/soc/codecs/wm8728.h | 18 + sound/soc/codecs/wm8731-i2c.c | 68 + sound/soc/codecs/wm8731-spi.c | 59 + sound/soc/codecs/wm8731.c | 653 +++ sound/soc/codecs/wm8731.h | 63 + sound/soc/codecs/wm8737.c | 733 ++++ sound/soc/codecs/wm8737.h | 319 ++ sound/soc/codecs/wm8741.c | 708 +++ sound/soc/codecs/wm8741.h | 218 + sound/soc/codecs/wm8750.c | 855 ++++ sound/soc/codecs/wm8750.h | 56 + sound/soc/codecs/wm8753.c | 1632 +++++++ sound/soc/codecs/wm8753.h | 110 + sound/soc/codecs/wm8770.c | 700 +++ sound/soc/codecs/wm8770.h | 48 + sound/soc/codecs/wm8776.c | 565 +++ sound/soc/codecs/wm8776.h | 45 + sound/soc/codecs/wm8782.c | 148 + sound/soc/codecs/wm8804-i2c.c | 72 + sound/soc/codecs/wm8804-spi.c | 52 + sound/soc/codecs/wm8804.c | 726 ++++ sound/soc/codecs/wm8804.h | 70 + sound/soc/codecs/wm8900.c | 1338 ++++++ sound/soc/codecs/wm8900.h | 52 + sound/soc/codecs/wm8903.c | 2221 ++++++++++ sound/soc/codecs/wm8903.h | 1221 ++++++ sound/soc/codecs/wm8904.c | 2351 ++++++++++ sound/soc/codecs/wm8904.h | 1590 +++++++ sound/soc/codecs/wm8940.c | 871 ++++ sound/soc/codecs/wm8940.h | 102 + sound/soc/codecs/wm8955.c | 1014 +++++ sound/soc/codecs/wm8955.h | 483 +++ sound/soc/codecs/wm8958-dsp2.c | 1032 +++++ sound/soc/codecs/wm8960.c | 1589 +++++++ sound/soc/codecs/wm8960.h | 111 + sound/soc/codecs/wm8961.c | 993 +++++ sound/soc/codecs/wm8961.h | 860 ++++ sound/soc/codecs/wm8962.c | 3958 +++++++++++++++++ sound/soc/codecs/wm8962.h | 3781 ++++++++++++++++ sound/soc/codecs/wm8971.c | 711 +++ sound/soc/codecs/wm8971.h | 51 + sound/soc/codecs/wm8974.c | 734 ++++ sound/soc/codecs/wm8974.h | 83 + sound/soc/codecs/wm8978.c | 1083 +++++ sound/soc/codecs/wm8978.h | 82 + sound/soc/codecs/wm8983.c | 1111 +++++ sound/soc/codecs/wm8983.h | 1026 +++++ sound/soc/codecs/wm8985.c | 1249 ++++++ sound/soc/codecs/wm8985.h | 1080 +++++ sound/soc/codecs/wm8988.c | 948 ++++ sound/soc/codecs/wm8988.h | 53 + sound/soc/codecs/wm8990.c | 1258 ++++++ sound/soc/codecs/wm8990.h | 821 ++++ sound/soc/codecs/wm8991.c | 1334 ++++++ sound/soc/codecs/wm8991.h | 815 ++++ sound/soc/codecs/wm8993.c | 1753 ++++++++ sound/soc/codecs/wm8993.h | 2139 +++++++++ sound/soc/codecs/wm8994.c | 4710 ++++++++++++++++++++ sound/soc/codecs/wm8994.h | 173 + sound/soc/codecs/wm8995.c | 2313 ++++++++++ sound/soc/codecs/wm8995.h | 4263 ++++++++++++++++++ sound/soc/codecs/wm8996.c | 3098 +++++++++++++ sound/soc/codecs/wm8996.h | 3717 ++++++++++++++++ sound/soc/codecs/wm8997.c | 1221 ++++++ sound/soc/codecs/wm8997.h | 20 + sound/soc/codecs/wm8998.c | 1437 +++++++ sound/soc/codecs/wm8998.h | 20 + sound/soc/codecs/wm9081.c | 1381 ++++++ sound/soc/codecs/wm9081.h | 781 ++++ sound/soc/codecs/wm9090.c | 627 +++ sound/soc/codecs/wm9090.h | 700 +++ sound/soc/codecs/wm9705.c | 400 ++ sound/soc/codecs/wm9712.c | 726 ++++ sound/soc/codecs/wm9713.c | 1291 ++++++ sound/soc/codecs/wm9713.h | 45 + sound/soc/codecs/wm_adsp.c | 2107 +++++++++ sound/soc/codecs/wm_adsp.h | 138 + sound/soc/codecs/wm_hubs.c | 1309 ++++++ sound/soc/codecs/wm_hubs.h | 71 + sound/soc/codecs/wsa881x.c | 1235 ++++++ sound/soc/codecs/wsa883x.c | 1474 +++++++ sound/soc/codecs/wsa884x.c | 1936 +++++++++ sound/soc/codecs/zl38060.c | 635 +++ 640 files changed, 514501 insertions(+) create mode 100644 sound/soc/codecs/88pm860x-codec.c create mode 100644 sound/soc/codecs/88pm860x-codec.h create mode 100644 sound/soc/codecs/Kconfig create mode 100644 sound/soc/codecs/Makefile create mode 100644 sound/soc/codecs/ab8500-codec.c create mode 100644 sound/soc/codecs/ab8500-codec.h create mode 100644 sound/soc/codecs/ac97.c create mode 100644 sound/soc/codecs/ad1836.c create mode 100644 sound/soc/codecs/ad1836.h create mode 100644 sound/soc/codecs/ad193x-i2c.c create mode 100644 sound/soc/codecs/ad193x-spi.c create mode 100644 sound/soc/codecs/ad193x.c create mode 100644 sound/soc/codecs/ad193x.h create mode 100644 sound/soc/codecs/ad1980.c create mode 100644 sound/soc/codecs/ad73311.c create mode 100644 sound/soc/codecs/ad73311.h create mode 100644 sound/soc/codecs/adau-utils.c create mode 100644 sound/soc/codecs/adau-utils.h create mode 100644 sound/soc/codecs/adau1372-i2c.c create mode 100644 sound/soc/codecs/adau1372-spi.c create mode 100644 sound/soc/codecs/adau1372.c create mode 100644 sound/soc/codecs/adau1372.h create mode 100644 sound/soc/codecs/adau1373.c create mode 100644 sound/soc/codecs/adau1373.h create mode 100644 sound/soc/codecs/adau1701.c create mode 100644 sound/soc/codecs/adau1701.h create mode 100644 sound/soc/codecs/adau1761-i2c.c create mode 100644 sound/soc/codecs/adau1761-spi.c create mode 100644 sound/soc/codecs/adau1761.c create mode 100644 sound/soc/codecs/adau1761.h create mode 100644 sound/soc/codecs/adau1781-i2c.c create mode 100644 sound/soc/codecs/adau1781-spi.c create mode 100644 sound/soc/codecs/adau1781.c create mode 100644 sound/soc/codecs/adau1781.h create mode 100644 sound/soc/codecs/adau17x1.c create mode 100644 sound/soc/codecs/adau17x1.h create mode 100644 sound/soc/codecs/adau1977-i2c.c create mode 100644 sound/soc/codecs/adau1977-spi.c create mode 100644 sound/soc/codecs/adau1977.c create mode 100644 sound/soc/codecs/adau1977.h create mode 100644 sound/soc/codecs/adau7002.c create mode 100644 sound/soc/codecs/adau7118-hw.c create mode 100644 sound/soc/codecs/adau7118-i2c.c create mode 100644 sound/soc/codecs/adau7118.c create mode 100644 sound/soc/codecs/adau7118.h create mode 100644 sound/soc/codecs/adav801.c create mode 100644 sound/soc/codecs/adav803.c create mode 100644 sound/soc/codecs/adav80x.c create mode 100644 sound/soc/codecs/adav80x.h create mode 100644 sound/soc/codecs/ads117x.c create mode 100644 sound/soc/codecs/ak4104.c create mode 100644 sound/soc/codecs/ak4118.c create mode 100644 sound/soc/codecs/ak4375.c create mode 100644 sound/soc/codecs/ak4458.c create mode 100644 sound/soc/codecs/ak4458.h create mode 100644 sound/soc/codecs/ak4535.c create mode 100644 sound/soc/codecs/ak4535.h create mode 100644 sound/soc/codecs/ak4554.c create mode 100644 sound/soc/codecs/ak4613.c create mode 100644 sound/soc/codecs/ak4641.c create mode 100644 sound/soc/codecs/ak4642.c create mode 100644 sound/soc/codecs/ak4671.c create mode 100644 sound/soc/codecs/ak4671.h create mode 100644 sound/soc/codecs/ak5386.c create mode 100644 sound/soc/codecs/ak5558.c create mode 100644 sound/soc/codecs/ak5558.h create mode 100644 sound/soc/codecs/alc5623.c create mode 100644 sound/soc/codecs/alc5623.h create mode 100644 sound/soc/codecs/alc5632.c create mode 100644 sound/soc/codecs/alc5632.h create mode 100644 sound/soc/codecs/arizona-jack.c create mode 100644 sound/soc/codecs/arizona.c create mode 100644 sound/soc/codecs/arizona.h create mode 100644 sound/soc/codecs/audio-iio-aux.c create mode 100644 sound/soc/codecs/aw8738.c create mode 100644 sound/soc/codecs/aw88261.c create mode 100644 sound/soc/codecs/aw88261.h create mode 100644 sound/soc/codecs/aw88395/aw88395.c create mode 100644 sound/soc/codecs/aw88395/aw88395.h create mode 100644 sound/soc/codecs/aw88395/aw88395_data_type.h create mode 100644 sound/soc/codecs/aw88395/aw88395_device.c create mode 100644 sound/soc/codecs/aw88395/aw88395_device.h create mode 100644 sound/soc/codecs/aw88395/aw88395_lib.c create mode 100644 sound/soc/codecs/aw88395/aw88395_lib.h create mode 100644 sound/soc/codecs/aw88395/aw88395_reg.h create mode 100644 sound/soc/codecs/bd28623.c create mode 100644 sound/soc/codecs/bt-sco.c create mode 100644 sound/soc/codecs/chv3-codec.c create mode 100644 sound/soc/codecs/cirrus_legacy.h create mode 100644 sound/soc/codecs/cpcap.c create mode 100644 sound/soc/codecs/cq93vc.c create mode 100644 sound/soc/codecs/cros_ec_codec.c create mode 100644 sound/soc/codecs/cs35l32.c create mode 100644 sound/soc/codecs/cs35l32.h create mode 100644 sound/soc/codecs/cs35l33.c create mode 100644 sound/soc/codecs/cs35l33.h create mode 100644 sound/soc/codecs/cs35l34.c create mode 100644 sound/soc/codecs/cs35l34.h create mode 100644 sound/soc/codecs/cs35l35.c create mode 100644 sound/soc/codecs/cs35l35.h create mode 100644 sound/soc/codecs/cs35l36.c create mode 100644 sound/soc/codecs/cs35l36.h create mode 100644 sound/soc/codecs/cs35l41-i2c.c create mode 100644 sound/soc/codecs/cs35l41-lib.c create mode 100644 sound/soc/codecs/cs35l41-spi.c create mode 100644 sound/soc/codecs/cs35l41.c create mode 100644 sound/soc/codecs/cs35l41.h create mode 100644 sound/soc/codecs/cs35l45-i2c.c create mode 100644 sound/soc/codecs/cs35l45-spi.c create mode 100644 sound/soc/codecs/cs35l45-tables.c create mode 100644 sound/soc/codecs/cs35l45.c create mode 100644 sound/soc/codecs/cs35l45.h create mode 100644 sound/soc/codecs/cs35l56-i2c.c create mode 100644 sound/soc/codecs/cs35l56-sdw.c create mode 100644 sound/soc/codecs/cs35l56-shared.c create mode 100644 sound/soc/codecs/cs35l56-spi.c create mode 100644 sound/soc/codecs/cs35l56.c create mode 100644 sound/soc/codecs/cs35l56.h create mode 100644 sound/soc/codecs/cs4234.c create mode 100644 sound/soc/codecs/cs4234.h create mode 100644 sound/soc/codecs/cs4265.c create mode 100644 sound/soc/codecs/cs4265.h create mode 100644 sound/soc/codecs/cs4270.c create mode 100644 sound/soc/codecs/cs4271-i2c.c create mode 100644 sound/soc/codecs/cs4271-spi.c create mode 100644 sound/soc/codecs/cs4271.c create mode 100644 sound/soc/codecs/cs4271.h create mode 100644 sound/soc/codecs/cs42l42-i2c.c create mode 100644 sound/soc/codecs/cs42l42-sdw.c create mode 100644 sound/soc/codecs/cs42l42.c create mode 100644 sound/soc/codecs/cs42l42.h create mode 100644 sound/soc/codecs/cs42l43-jack.c create mode 100644 sound/soc/codecs/cs42l43-sdw.c create mode 100644 sound/soc/codecs/cs42l43.c create mode 100644 sound/soc/codecs/cs42l43.h create mode 100644 sound/soc/codecs/cs42l51-i2c.c create mode 100644 sound/soc/codecs/cs42l51.c create mode 100644 sound/soc/codecs/cs42l51.h create mode 100644 sound/soc/codecs/cs42l52.c create mode 100644 sound/soc/codecs/cs42l52.h create mode 100644 sound/soc/codecs/cs42l56.c create mode 100644 sound/soc/codecs/cs42l56.h create mode 100644 sound/soc/codecs/cs42l73.c create mode 100644 sound/soc/codecs/cs42l73.h create mode 100644 sound/soc/codecs/cs42l83-i2c.c create mode 100644 sound/soc/codecs/cs42xx8-i2c.c create mode 100644 sound/soc/codecs/cs42xx8.c create mode 100644 sound/soc/codecs/cs42xx8.h create mode 100644 sound/soc/codecs/cs43130.c create mode 100644 sound/soc/codecs/cs43130.h create mode 100644 sound/soc/codecs/cs4341.c create mode 100644 sound/soc/codecs/cs4349.c create mode 100644 sound/soc/codecs/cs4349.h create mode 100644 sound/soc/codecs/cs47l15.c create mode 100644 sound/soc/codecs/cs47l24.c create mode 100644 sound/soc/codecs/cs47l24.h create mode 100644 sound/soc/codecs/cs47l35.c create mode 100644 sound/soc/codecs/cs47l85.c create mode 100644 sound/soc/codecs/cs47l90.c create mode 100644 sound/soc/codecs/cs47l92.c create mode 100644 sound/soc/codecs/cs53l30.c create mode 100644 sound/soc/codecs/cs53l30.h create mode 100644 sound/soc/codecs/cx20442.c create mode 100644 sound/soc/codecs/cx20442.h create mode 100644 sound/soc/codecs/cx2072x.c create mode 100644 sound/soc/codecs/cx2072x.h create mode 100644 sound/soc/codecs/da7210.c create mode 100644 sound/soc/codecs/da7213.c create mode 100644 sound/soc/codecs/da7213.h create mode 100644 sound/soc/codecs/da7218.c create mode 100644 sound/soc/codecs/da7218.h create mode 100644 sound/soc/codecs/da7219-aad.c create mode 100644 sound/soc/codecs/da7219-aad.h create mode 100644 sound/soc/codecs/da7219.c create mode 100644 sound/soc/codecs/da7219.h create mode 100644 sound/soc/codecs/da732x.c create mode 100644 sound/soc/codecs/da732x.h create mode 100644 sound/soc/codecs/da732x_reg.h create mode 100644 sound/soc/codecs/da9055.c create mode 100644 sound/soc/codecs/dmic.c create mode 100644 sound/soc/codecs/es7134.c create mode 100644 sound/soc/codecs/es7241.c create mode 100644 sound/soc/codecs/es8316.c create mode 100644 sound/soc/codecs/es8316.h create mode 100644 sound/soc/codecs/es8326.c create mode 100644 sound/soc/codecs/es8326.h create mode 100644 sound/soc/codecs/es8328-i2c.c create mode 100644 sound/soc/codecs/es8328-spi.c create mode 100644 sound/soc/codecs/es8328.c create mode 100644 sound/soc/codecs/es8328.h create mode 100644 sound/soc/codecs/gtm601.c create mode 100644 sound/soc/codecs/hda-dai.c create mode 100644 sound/soc/codecs/hda.c create mode 100644 sound/soc/codecs/hda.h create mode 100644 sound/soc/codecs/hdac_hda.c create mode 100644 sound/soc/codecs/hdac_hda.h create mode 100644 sound/soc/codecs/hdac_hdmi.c create mode 100644 sound/soc/codecs/hdac_hdmi.h create mode 100644 sound/soc/codecs/hdmi-codec.c create mode 100644 sound/soc/codecs/ics43432.c create mode 100644 sound/soc/codecs/idt821034.c create mode 100644 sound/soc/codecs/inno_rk3036.c create mode 100644 sound/soc/codecs/inno_rk3036.h create mode 100644 sound/soc/codecs/isabelle.c create mode 100644 sound/soc/codecs/isabelle.h create mode 100644 sound/soc/codecs/jz4725b.c create mode 100644 sound/soc/codecs/jz4740.c create mode 100644 sound/soc/codecs/jz4760.c create mode 100644 sound/soc/codecs/jz4770.c create mode 100644 sound/soc/codecs/lm4857.c create mode 100644 sound/soc/codecs/lm49453.c create mode 100644 sound/soc/codecs/lm49453.h create mode 100644 sound/soc/codecs/lochnagar-sc.c create mode 100644 sound/soc/codecs/lpass-macro-common.c create mode 100644 sound/soc/codecs/lpass-macro-common.h create mode 100644 sound/soc/codecs/lpass-rx-macro.c create mode 100644 sound/soc/codecs/lpass-tx-macro.c create mode 100644 sound/soc/codecs/lpass-va-macro.c create mode 100644 sound/soc/codecs/lpass-wsa-macro.c create mode 100644 sound/soc/codecs/lpass-wsa-macro.h create mode 100644 sound/soc/codecs/madera.c create mode 100644 sound/soc/codecs/madera.h create mode 100644 sound/soc/codecs/max9759.c create mode 100644 sound/soc/codecs/max9768.c create mode 100644 sound/soc/codecs/max98088.c create mode 100644 sound/soc/codecs/max98088.h create mode 100644 sound/soc/codecs/max98090.c create mode 100644 sound/soc/codecs/max98090.h create mode 100644 sound/soc/codecs/max98095.c create mode 100644 sound/soc/codecs/max98095.h create mode 100644 sound/soc/codecs/max98357a.c create mode 100644 sound/soc/codecs/max98363.c create mode 100644 sound/soc/codecs/max98363.h create mode 100644 sound/soc/codecs/max98371.c create mode 100644 sound/soc/codecs/max98371.h create mode 100644 sound/soc/codecs/max98373-i2c.c create mode 100644 sound/soc/codecs/max98373-sdw.c create mode 100644 sound/soc/codecs/max98373-sdw.h create mode 100644 sound/soc/codecs/max98373.c create mode 100644 sound/soc/codecs/max98373.h create mode 100644 sound/soc/codecs/max98388.c create mode 100644 sound/soc/codecs/max98388.h create mode 100644 sound/soc/codecs/max98390.c create mode 100644 sound/soc/codecs/max98390.h create mode 100644 sound/soc/codecs/max98396.c create mode 100644 sound/soc/codecs/max98396.h create mode 100644 sound/soc/codecs/max9850.c create mode 100644 sound/soc/codecs/max9850.h create mode 100644 sound/soc/codecs/max98504.c create mode 100644 sound/soc/codecs/max98504.h create mode 100644 sound/soc/codecs/max98520.c create mode 100644 sound/soc/codecs/max98520.h create mode 100644 sound/soc/codecs/max9860.c create mode 100644 sound/soc/codecs/max9860.h create mode 100644 sound/soc/codecs/max9867.c create mode 100644 sound/soc/codecs/max9867.h create mode 100644 sound/soc/codecs/max9877.c create mode 100644 sound/soc/codecs/max9877.h create mode 100644 sound/soc/codecs/max98925.c create mode 100644 sound/soc/codecs/max98925.h create mode 100644 sound/soc/codecs/max98926.c create mode 100644 sound/soc/codecs/max98926.h create mode 100644 sound/soc/codecs/max98927.c create mode 100644 sound/soc/codecs/max98927.h create mode 100644 sound/soc/codecs/mc13783.c create mode 100644 sound/soc/codecs/mc13783.h create mode 100644 sound/soc/codecs/ml26124.c create mode 100644 sound/soc/codecs/ml26124.h create mode 100644 sound/soc/codecs/msm8916-wcd-analog.c create mode 100644 sound/soc/codecs/msm8916-wcd-digital.c create mode 100644 sound/soc/codecs/mt6351.c create mode 100644 sound/soc/codecs/mt6351.h create mode 100644 sound/soc/codecs/mt6358.c create mode 100644 sound/soc/codecs/mt6358.h create mode 100644 sound/soc/codecs/mt6359-accdet.c create mode 100644 sound/soc/codecs/mt6359-accdet.h create mode 100644 sound/soc/codecs/mt6359.c create mode 100644 sound/soc/codecs/mt6359.h create mode 100644 sound/soc/codecs/mt6660.c create mode 100644 sound/soc/codecs/mt6660.h create mode 100644 sound/soc/codecs/nau8315.c create mode 100644 sound/soc/codecs/nau8540.c create mode 100644 sound/soc/codecs/nau8540.h create mode 100644 sound/soc/codecs/nau8810.c create mode 100644 sound/soc/codecs/nau8810.h create mode 100644 sound/soc/codecs/nau8821.c create mode 100644 sound/soc/codecs/nau8821.h create mode 100644 sound/soc/codecs/nau8822.c create mode 100644 sound/soc/codecs/nau8822.h create mode 100644 sound/soc/codecs/nau8824.c create mode 100644 sound/soc/codecs/nau8824.h create mode 100644 sound/soc/codecs/nau8825.c create mode 100644 sound/soc/codecs/nau8825.h create mode 100644 sound/soc/codecs/pcm1681.c create mode 100644 sound/soc/codecs/pcm1789-i2c.c create mode 100644 sound/soc/codecs/pcm1789.c create mode 100644 sound/soc/codecs/pcm1789.h create mode 100644 sound/soc/codecs/pcm179x-i2c.c create mode 100644 sound/soc/codecs/pcm179x-spi.c create mode 100644 sound/soc/codecs/pcm179x.c create mode 100644 sound/soc/codecs/pcm179x.h create mode 100644 sound/soc/codecs/pcm186x-i2c.c create mode 100644 sound/soc/codecs/pcm186x-spi.c create mode 100644 sound/soc/codecs/pcm186x.c create mode 100644 sound/soc/codecs/pcm186x.h create mode 100644 sound/soc/codecs/pcm3008.c create mode 100644 sound/soc/codecs/pcm3008.h create mode 100644 sound/soc/codecs/pcm3060-i2c.c create mode 100644 sound/soc/codecs/pcm3060-spi.c create mode 100644 sound/soc/codecs/pcm3060.c create mode 100644 sound/soc/codecs/pcm3060.h create mode 100644 sound/soc/codecs/pcm3168a-i2c.c create mode 100644 sound/soc/codecs/pcm3168a-spi.c create mode 100644 sound/soc/codecs/pcm3168a.c create mode 100644 sound/soc/codecs/pcm3168a.h create mode 100644 sound/soc/codecs/pcm5102a.c create mode 100644 sound/soc/codecs/pcm512x-i2c.c create mode 100644 sound/soc/codecs/pcm512x-spi.c create mode 100644 sound/soc/codecs/pcm512x.c create mode 100644 sound/soc/codecs/pcm512x.h create mode 100644 sound/soc/codecs/peb2466.c create mode 100644 sound/soc/codecs/rk3328_codec.c create mode 100644 sound/soc/codecs/rk3328_codec.h create mode 100644 sound/soc/codecs/rk817_codec.c create mode 100644 sound/soc/codecs/rl6231.c create mode 100644 sound/soc/codecs/rl6231.h create mode 100644 sound/soc/codecs/rl6347a.c create mode 100644 sound/soc/codecs/rl6347a.h create mode 100644 sound/soc/codecs/rt1011.c create mode 100644 sound/soc/codecs/rt1011.h create mode 100644 sound/soc/codecs/rt1015.c create mode 100644 sound/soc/codecs/rt1015.h create mode 100644 sound/soc/codecs/rt1015p.c create mode 100644 sound/soc/codecs/rt1016.c create mode 100644 sound/soc/codecs/rt1016.h create mode 100644 sound/soc/codecs/rt1017-sdca-sdw.c create mode 100644 sound/soc/codecs/rt1017-sdca-sdw.h create mode 100644 sound/soc/codecs/rt1019.c create mode 100644 sound/soc/codecs/rt1019.h create mode 100644 sound/soc/codecs/rt1305.c create mode 100644 sound/soc/codecs/rt1305.h create mode 100644 sound/soc/codecs/rt1308-sdw.c create mode 100644 sound/soc/codecs/rt1308-sdw.h create mode 100644 sound/soc/codecs/rt1308.c create mode 100644 sound/soc/codecs/rt1308.h create mode 100644 sound/soc/codecs/rt1316-sdw.c create mode 100644 sound/soc/codecs/rt1316-sdw.h create mode 100644 sound/soc/codecs/rt1318-sdw.c create mode 100644 sound/soc/codecs/rt1318-sdw.h create mode 100644 sound/soc/codecs/rt274.c create mode 100644 sound/soc/codecs/rt274.h create mode 100644 sound/soc/codecs/rt286.c create mode 100644 sound/soc/codecs/rt286.h create mode 100644 sound/soc/codecs/rt298.c create mode 100644 sound/soc/codecs/rt298.h create mode 100644 sound/soc/codecs/rt5514-spi.c create mode 100644 sound/soc/codecs/rt5514-spi.h create mode 100644 sound/soc/codecs/rt5514.c create mode 100644 sound/soc/codecs/rt5514.h create mode 100644 sound/soc/codecs/rt5616.c create mode 100644 sound/soc/codecs/rt5616.h create mode 100644 sound/soc/codecs/rt5631.c create mode 100644 sound/soc/codecs/rt5631.h create mode 100644 sound/soc/codecs/rt5640.c create mode 100644 sound/soc/codecs/rt5640.h create mode 100644 sound/soc/codecs/rt5645.c create mode 100644 sound/soc/codecs/rt5645.h create mode 100644 sound/soc/codecs/rt5651.c create mode 100644 sound/soc/codecs/rt5651.h create mode 100644 sound/soc/codecs/rt5659.c create mode 100644 sound/soc/codecs/rt5659.h create mode 100644 sound/soc/codecs/rt5660.c create mode 100644 sound/soc/codecs/rt5660.h create mode 100644 sound/soc/codecs/rt5663.c create mode 100644 sound/soc/codecs/rt5663.h create mode 100644 sound/soc/codecs/rt5665.c create mode 100644 sound/soc/codecs/rt5665.h create mode 100644 sound/soc/codecs/rt5668.c create mode 100644 sound/soc/codecs/rt5668.h create mode 100644 sound/soc/codecs/rt5670-dsp.h create mode 100644 sound/soc/codecs/rt5670.c create mode 100644 sound/soc/codecs/rt5670.h create mode 100644 sound/soc/codecs/rt5677-spi.c create mode 100644 sound/soc/codecs/rt5677-spi.h create mode 100644 sound/soc/codecs/rt5677.c create mode 100644 sound/soc/codecs/rt5677.h create mode 100644 sound/soc/codecs/rt5682-i2c.c create mode 100644 sound/soc/codecs/rt5682-sdw.c create mode 100644 sound/soc/codecs/rt5682.c create mode 100644 sound/soc/codecs/rt5682.h create mode 100644 sound/soc/codecs/rt5682s.c create mode 100644 sound/soc/codecs/rt5682s.h create mode 100644 sound/soc/codecs/rt700-sdw.c create mode 100644 sound/soc/codecs/rt700-sdw.h create mode 100644 sound/soc/codecs/rt700.c create mode 100644 sound/soc/codecs/rt700.h create mode 100644 sound/soc/codecs/rt711-sdca-sdw.c create mode 100644 sound/soc/codecs/rt711-sdca-sdw.h create mode 100644 sound/soc/codecs/rt711-sdca.c create mode 100644 sound/soc/codecs/rt711-sdca.h create mode 100644 sound/soc/codecs/rt711-sdw.c create mode 100644 sound/soc/codecs/rt711-sdw.h create mode 100644 sound/soc/codecs/rt711.c create mode 100644 sound/soc/codecs/rt711.h create mode 100644 sound/soc/codecs/rt712-sdca-dmic.c create mode 100644 sound/soc/codecs/rt712-sdca-dmic.h create mode 100644 sound/soc/codecs/rt712-sdca-sdw.c create mode 100644 sound/soc/codecs/rt712-sdca-sdw.h create mode 100644 sound/soc/codecs/rt712-sdca.c create mode 100644 sound/soc/codecs/rt712-sdca.h create mode 100644 sound/soc/codecs/rt715-sdca-sdw.c create mode 100644 sound/soc/codecs/rt715-sdca-sdw.h create mode 100644 sound/soc/codecs/rt715-sdca.c create mode 100644 sound/soc/codecs/rt715-sdca.h create mode 100644 sound/soc/codecs/rt715-sdw.c create mode 100644 sound/soc/codecs/rt715-sdw.h create mode 100644 sound/soc/codecs/rt715.c create mode 100644 sound/soc/codecs/rt715.h create mode 100644 sound/soc/codecs/rt722-sdca-sdw.c create mode 100644 sound/soc/codecs/rt722-sdca-sdw.h create mode 100644 sound/soc/codecs/rt722-sdca.c create mode 100644 sound/soc/codecs/rt722-sdca.h create mode 100644 sound/soc/codecs/rt9120.c create mode 100644 sound/soc/codecs/sdw-mockup.c create mode 100644 sound/soc/codecs/sgtl5000.c create mode 100644 sound/soc/codecs/sgtl5000.h create mode 100644 sound/soc/codecs/si476x.c create mode 100644 sound/soc/codecs/sigmadsp-i2c.c create mode 100644 sound/soc/codecs/sigmadsp-regmap.c create mode 100644 sound/soc/codecs/sigmadsp.c create mode 100644 sound/soc/codecs/sigmadsp.h create mode 100644 sound/soc/codecs/simple-amplifier.c create mode 100644 sound/soc/codecs/simple-mux.c create mode 100644 sound/soc/codecs/sma1303.c create mode 100644 sound/soc/codecs/sma1303.h create mode 100644 sound/soc/codecs/spdif_receiver.c create mode 100644 sound/soc/codecs/spdif_transmitter.c create mode 100644 sound/soc/codecs/src4xxx-i2c.c create mode 100644 sound/soc/codecs/src4xxx.c create mode 100644 sound/soc/codecs/src4xxx.h create mode 100644 sound/soc/codecs/ssm2305.c create mode 100644 sound/soc/codecs/ssm2518.c create mode 100644 sound/soc/codecs/ssm2518.h create mode 100644 sound/soc/codecs/ssm2602-i2c.c create mode 100644 sound/soc/codecs/ssm2602-spi.c create mode 100644 sound/soc/codecs/ssm2602.c create mode 100644 sound/soc/codecs/ssm2602.h create mode 100644 sound/soc/codecs/ssm3515.c create mode 100644 sound/soc/codecs/ssm4567.c create mode 100644 sound/soc/codecs/sta32x.c create mode 100644 sound/soc/codecs/sta32x.h create mode 100644 sound/soc/codecs/sta350.c create mode 100644 sound/soc/codecs/sta350.h create mode 100644 sound/soc/codecs/sta529.c create mode 100644 sound/soc/codecs/stac9766.c create mode 100644 sound/soc/codecs/sti-sas.c create mode 100644 sound/soc/codecs/tas2552.c create mode 100644 sound/soc/codecs/tas2552.h create mode 100644 sound/soc/codecs/tas2562.c create mode 100644 sound/soc/codecs/tas2562.h create mode 100644 sound/soc/codecs/tas2764.c create mode 100644 sound/soc/codecs/tas2764.h create mode 100644 sound/soc/codecs/tas2770.c create mode 100644 sound/soc/codecs/tas2770.h create mode 100644 sound/soc/codecs/tas2780.c create mode 100644 sound/soc/codecs/tas2780.h create mode 100644 sound/soc/codecs/tas2781-comlib.c create mode 100644 sound/soc/codecs/tas2781-fmwlib.c create mode 100644 sound/soc/codecs/tas2781-i2c.c create mode 100644 sound/soc/codecs/tas5086.c create mode 100644 sound/soc/codecs/tas571x.c create mode 100644 sound/soc/codecs/tas571x.h create mode 100644 sound/soc/codecs/tas5720.c create mode 100644 sound/soc/codecs/tas5720.h create mode 100644 sound/soc/codecs/tas5805m.c create mode 100644 sound/soc/codecs/tas6424.c create mode 100644 sound/soc/codecs/tas6424.h create mode 100644 sound/soc/codecs/tda7419.c create mode 100644 sound/soc/codecs/tfa9879.c create mode 100644 sound/soc/codecs/tfa9879.h create mode 100644 sound/soc/codecs/tfa989x.c create mode 100644 sound/soc/codecs/tlv320adc3xxx.c create mode 100644 sound/soc/codecs/tlv320adcx140.c create mode 100644 sound/soc/codecs/tlv320adcx140.h create mode 100644 sound/soc/codecs/tlv320aic23-i2c.c create mode 100644 sound/soc/codecs/tlv320aic23-spi.c create mode 100644 sound/soc/codecs/tlv320aic23.c create mode 100644 sound/soc/codecs/tlv320aic23.h create mode 100644 sound/soc/codecs/tlv320aic26.c create mode 100644 sound/soc/codecs/tlv320aic26.h create mode 100644 sound/soc/codecs/tlv320aic31xx.c create mode 100644 sound/soc/codecs/tlv320aic31xx.h create mode 100644 sound/soc/codecs/tlv320aic32x4-clk.c create mode 100644 sound/soc/codecs/tlv320aic32x4-i2c.c create mode 100644 sound/soc/codecs/tlv320aic32x4-spi.c create mode 100644 sound/soc/codecs/tlv320aic32x4.c create mode 100644 sound/soc/codecs/tlv320aic32x4.h create mode 100644 sound/soc/codecs/tlv320aic3x-i2c.c create mode 100644 sound/soc/codecs/tlv320aic3x-spi.c create mode 100644 sound/soc/codecs/tlv320aic3x.c create mode 100644 sound/soc/codecs/tlv320aic3x.h create mode 100644 sound/soc/codecs/tlv320dac33.c create mode 100644 sound/soc/codecs/tlv320dac33.h create mode 100644 sound/soc/codecs/tpa6130a2.c create mode 100644 sound/soc/codecs/tpa6130a2.h create mode 100644 sound/soc/codecs/ts3a227e.c create mode 100644 sound/soc/codecs/ts3a227e.h create mode 100644 sound/soc/codecs/tscs42xx.c create mode 100644 sound/soc/codecs/tscs42xx.h create mode 100644 sound/soc/codecs/tscs454.c create mode 100644 sound/soc/codecs/tscs454.h create mode 100644 sound/soc/codecs/twl4030.c create mode 100644 sound/soc/codecs/twl6040.c create mode 100644 sound/soc/codecs/twl6040.h create mode 100644 sound/soc/codecs/uda1334.c create mode 100644 sound/soc/codecs/uda1380.c create mode 100644 sound/soc/codecs/uda1380.h create mode 100644 sound/soc/codecs/wcd-clsh-v2.c create mode 100644 sound/soc/codecs/wcd-clsh-v2.h create mode 100644 sound/soc/codecs/wcd-mbhc-v2.c create mode 100644 sound/soc/codecs/wcd-mbhc-v2.h create mode 100644 sound/soc/codecs/wcd9335.c create mode 100644 sound/soc/codecs/wcd9335.h create mode 100644 sound/soc/codecs/wcd934x.c create mode 100644 sound/soc/codecs/wcd938x-sdw.c create mode 100644 sound/soc/codecs/wcd938x.c create mode 100644 sound/soc/codecs/wcd938x.h create mode 100644 sound/soc/codecs/wl1273.c create mode 100644 sound/soc/codecs/wl1273.h create mode 100644 sound/soc/codecs/wm0010.c create mode 100644 sound/soc/codecs/wm1250-ev1.c create mode 100644 sound/soc/codecs/wm2000.c create mode 100644 sound/soc/codecs/wm2000.h create mode 100644 sound/soc/codecs/wm2200.c create mode 100644 sound/soc/codecs/wm2200.h create mode 100644 sound/soc/codecs/wm5100-tables.c create mode 100644 sound/soc/codecs/wm5100.c create mode 100644 sound/soc/codecs/wm5100.h create mode 100644 sound/soc/codecs/wm5102.c create mode 100644 sound/soc/codecs/wm5102.h create mode 100644 sound/soc/codecs/wm5110.c create mode 100644 sound/soc/codecs/wm5110.h create mode 100644 sound/soc/codecs/wm8350.c create mode 100644 sound/soc/codecs/wm8350.h create mode 100644 sound/soc/codecs/wm8400.c create mode 100644 sound/soc/codecs/wm8400.h create mode 100644 sound/soc/codecs/wm8510.c create mode 100644 sound/soc/codecs/wm8510.h create mode 100644 sound/soc/codecs/wm8523.c create mode 100644 sound/soc/codecs/wm8523.h create mode 100644 sound/soc/codecs/wm8524.c create mode 100644 sound/soc/codecs/wm8580.c create mode 100644 sound/soc/codecs/wm8580.h create mode 100644 sound/soc/codecs/wm8711.c create mode 100644 sound/soc/codecs/wm8711.h create mode 100644 sound/soc/codecs/wm8727.c create mode 100644 sound/soc/codecs/wm8728.c create mode 100644 sound/soc/codecs/wm8728.h create mode 100644 sound/soc/codecs/wm8731-i2c.c create mode 100644 sound/soc/codecs/wm8731-spi.c create mode 100644 sound/soc/codecs/wm8731.c create mode 100644 sound/soc/codecs/wm8731.h create mode 100644 sound/soc/codecs/wm8737.c create mode 100644 sound/soc/codecs/wm8737.h create mode 100644 sound/soc/codecs/wm8741.c create mode 100644 sound/soc/codecs/wm8741.h create mode 100644 sound/soc/codecs/wm8750.c create mode 100644 sound/soc/codecs/wm8750.h create mode 100644 sound/soc/codecs/wm8753.c create mode 100644 sound/soc/codecs/wm8753.h create mode 100644 sound/soc/codecs/wm8770.c create mode 100644 sound/soc/codecs/wm8770.h create mode 100644 sound/soc/codecs/wm8776.c create mode 100644 sound/soc/codecs/wm8776.h create mode 100644 sound/soc/codecs/wm8782.c create mode 100644 sound/soc/codecs/wm8804-i2c.c create mode 100644 sound/soc/codecs/wm8804-spi.c create mode 100644 sound/soc/codecs/wm8804.c create mode 100644 sound/soc/codecs/wm8804.h create mode 100644 sound/soc/codecs/wm8900.c create mode 100644 sound/soc/codecs/wm8900.h create mode 100644 sound/soc/codecs/wm8903.c create mode 100644 sound/soc/codecs/wm8903.h create mode 100644 sound/soc/codecs/wm8904.c create mode 100644 sound/soc/codecs/wm8904.h create mode 100644 sound/soc/codecs/wm8940.c create mode 100644 sound/soc/codecs/wm8940.h create mode 100644 sound/soc/codecs/wm8955.c create mode 100644 sound/soc/codecs/wm8955.h create mode 100644 sound/soc/codecs/wm8958-dsp2.c create mode 100644 sound/soc/codecs/wm8960.c create mode 100644 sound/soc/codecs/wm8960.h create mode 100644 sound/soc/codecs/wm8961.c create mode 100644 sound/soc/codecs/wm8961.h create mode 100644 sound/soc/codecs/wm8962.c create mode 100644 sound/soc/codecs/wm8962.h create mode 100644 sound/soc/codecs/wm8971.c create mode 100644 sound/soc/codecs/wm8971.h create mode 100644 sound/soc/codecs/wm8974.c create mode 100644 sound/soc/codecs/wm8974.h create mode 100644 sound/soc/codecs/wm8978.c create mode 100644 sound/soc/codecs/wm8978.h create mode 100644 sound/soc/codecs/wm8983.c create mode 100644 sound/soc/codecs/wm8983.h create mode 100644 sound/soc/codecs/wm8985.c create mode 100644 sound/soc/codecs/wm8985.h create mode 100644 sound/soc/codecs/wm8988.c create mode 100644 sound/soc/codecs/wm8988.h create mode 100644 sound/soc/codecs/wm8990.c create mode 100644 sound/soc/codecs/wm8990.h create mode 100644 sound/soc/codecs/wm8991.c create mode 100644 sound/soc/codecs/wm8991.h create mode 100644 sound/soc/codecs/wm8993.c create mode 100644 sound/soc/codecs/wm8993.h create mode 100644 sound/soc/codecs/wm8994.c create mode 100644 sound/soc/codecs/wm8994.h create mode 100644 sound/soc/codecs/wm8995.c create mode 100644 sound/soc/codecs/wm8995.h create mode 100644 sound/soc/codecs/wm8996.c create mode 100644 sound/soc/codecs/wm8996.h create mode 100644 sound/soc/codecs/wm8997.c create mode 100644 sound/soc/codecs/wm8997.h create mode 100644 sound/soc/codecs/wm8998.c create mode 100644 sound/soc/codecs/wm8998.h create mode 100644 sound/soc/codecs/wm9081.c create mode 100644 sound/soc/codecs/wm9081.h create mode 100644 sound/soc/codecs/wm9090.c create mode 100644 sound/soc/codecs/wm9090.h create mode 100644 sound/soc/codecs/wm9705.c create mode 100644 sound/soc/codecs/wm9712.c create mode 100644 sound/soc/codecs/wm9713.c create mode 100644 sound/soc/codecs/wm9713.h create mode 100644 sound/soc/codecs/wm_adsp.c create mode 100644 sound/soc/codecs/wm_adsp.h create mode 100644 sound/soc/codecs/wm_hubs.c create mode 100644 sound/soc/codecs/wm_hubs.h create mode 100644 sound/soc/codecs/wsa881x.c create mode 100644 sound/soc/codecs/wsa883x.c create mode 100644 sound/soc/codecs/wsa884x.c create mode 100644 sound/soc/codecs/zl38060.c (limited to 'sound/soc/codecs') diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c new file mode 100644 index 0000000000..d99b674d57 --- /dev/null +++ b/sound/soc/codecs/88pm860x-codec.c @@ -0,0 +1,1402 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 88pm860x-codec.c -- 88PM860x ALSA SoC Audio Driver + * + * Copyright 2010 Marvell International Ltd. + * Author: Haojian Zhuang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "88pm860x-codec.h" + +#define MAX_NAME_LEN 20 +#define REG_CACHE_SIZE 0x40 +#define REG_CACHE_BASE 0xb0 + +/* Status Register 1 (0x01) */ +#define REG_STATUS_1 0x01 +#define MIC_STATUS (1 << 7) +#define HOOK_STATUS (1 << 6) +#define HEADSET_STATUS (1 << 5) + +/* Mic Detection Register (0x37) */ +#define REG_MIC_DET 0x37 +#define CONTINUOUS_POLLING (3 << 1) +#define EN_MIC_DET (1 << 0) +#define MICDET_MASK 0x07 + +/* Headset Detection Register (0x38) */ +#define REG_HS_DET 0x38 +#define EN_HS_DET (1 << 0) + +/* Misc2 Register (0x42) */ +#define REG_MISC2 0x42 +#define AUDIO_PLL (1 << 5) +#define AUDIO_SECTION_RESET (1 << 4) +#define AUDIO_SECTION_ON (1 << 3) + +/* PCM Interface Register 2 (0xb1) */ +#define PCM_INF2_BCLK (1 << 6) /* Bit clock polarity */ +#define PCM_INF2_FS (1 << 5) /* Frame Sync polarity */ +#define PCM_INF2_MASTER (1 << 4) /* Master / Slave */ +#define PCM_INF2_18WL (1 << 3) /* 18 / 16 bits */ +#define PCM_GENERAL_I2S 0 +#define PCM_EXACT_I2S 1 +#define PCM_LEFT_I2S 2 +#define PCM_RIGHT_I2S 3 +#define PCM_SHORT_FS 4 +#define PCM_LONG_FS 5 +#define PCM_MODE_MASK 7 + +/* I2S Interface Register 4 (0xbe) */ +#define I2S_EQU_BYP (1 << 6) + +/* DAC Offset Register (0xcb) */ +#define DAC_MUTE (1 << 7) +#define MUTE_LEFT (1 << 6) +#define MUTE_RIGHT (1 << 2) + +/* ADC Analog Register 1 (0xd0) */ +#define REG_ADC_ANA_1 0xd0 +#define MIC1BIAS_MASK 0x60 + +/* Earpiece/Speaker Control Register 2 (0xda) */ +#define REG_EAR2 0xda +#define RSYNC_CHANGE (1 << 2) + +/* Audio Supplies Register 2 (0xdc) */ +#define REG_SUPPLIES2 0xdc +#define LDO15_READY (1 << 4) +#define LDO15_EN (1 << 3) +#define CPUMP_READY (1 << 2) +#define CPUMP_EN (1 << 1) +#define AUDIO_EN (1 << 0) +#define SUPPLY_MASK (LDO15_EN | CPUMP_EN | AUDIO_EN) + +/* Audio Enable Register 1 (0xdd) */ +#define ADC_MOD_RIGHT (1 << 1) +#define ADC_MOD_LEFT (1 << 0) + +/* Audio Enable Register 2 (0xde) */ +#define ADC_LEFT (1 << 5) +#define ADC_RIGHT (1 << 4) + +/* DAC Enable Register 2 (0xe1) */ +#define DAC_LEFT (1 << 5) +#define DAC_RIGHT (1 << 4) +#define MODULATOR (1 << 3) + +/* Shorts Register (0xeb) */ +#define REG_SHORTS 0xeb +#define CLR_SHORT_LO2 (1 << 7) +#define SHORT_LO2 (1 << 6) +#define CLR_SHORT_LO1 (1 << 5) +#define SHORT_LO1 (1 << 4) +#define CLR_SHORT_HS2 (1 << 3) +#define SHORT_HS2 (1 << 2) +#define CLR_SHORT_HS1 (1 << 1) +#define SHORT_HS1 (1 << 0) + +/* + * This widget should be just after DAC & PGA in DAPM power-on sequence and + * before DAC & PGA in DAPM power-off sequence. + */ +#define PM860X_DAPM_OUTPUT(wname, wevent) \ + SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, 0, 0, NULL, 0, wevent, \ + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD) + +struct pm860x_det { + struct snd_soc_jack *hp_jack; + struct snd_soc_jack *mic_jack; + int hp_det; + int mic_det; + int hook_det; + int hs_shrt; + int lo_shrt; +}; + +struct pm860x_priv { + unsigned int sysclk; + unsigned int pcmclk; + unsigned int dir; + unsigned int filter; + struct snd_soc_component *component; + struct i2c_client *i2c; + struct regmap *regmap; + struct pm860x_chip *chip; + struct pm860x_det det; + + int irq[4]; + unsigned char name[4][MAX_NAME_LEN]; +}; + +/* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */ +static const DECLARE_TLV_DB_SCALE(dpga_tlv, -9450, 150, 1); + +/* -9dB to 0db in 3dB steps */ +static const DECLARE_TLV_DB_SCALE(adc_tlv, -900, 300, 0); + +/* {-23, -17, -13.5, -11, -9, -6, -3, 0}dB */ +static const DECLARE_TLV_DB_RANGE(mic_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-2300, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(-1700, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(-1350, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(-1100, 0, 0), + 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0) +); + +/* {0, 0, 0, -6, 0, 6, 12, 18}dB */ +static const DECLARE_TLV_DB_RANGE(aux_tlv, + 0, 2, TLV_DB_SCALE_ITEM(0, 0, 0), + 3, 7, TLV_DB_SCALE_ITEM(-600, 600, 0) +); + +/* {-16, -13, -10, -7, -5.2, -3,3, -2.2, 0}dB, mute instead of -16dB */ +static const DECLARE_TLV_DB_RANGE(out_tlv, + 0, 3, TLV_DB_SCALE_ITEM(-1600, 300, 1), + 4, 4, TLV_DB_SCALE_ITEM(-520, 0, 0), + 5, 5, TLV_DB_SCALE_ITEM(-330, 0, 0), + 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0) +); + +static const DECLARE_TLV_DB_RANGE(st_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-12041, 602, 0), + 2, 3, TLV_DB_SCALE_ITEM(-11087, 250, 0), + 4, 5, TLV_DB_SCALE_ITEM(-10643, 158, 0), + 6, 7, TLV_DB_SCALE_ITEM(-10351, 116, 0), + 8, 9, TLV_DB_SCALE_ITEM(-10133, 92, 0), + 10, 13, TLV_DB_SCALE_ITEM(-9958, 70, 0), + 14, 17, TLV_DB_SCALE_ITEM(-9689, 53, 0), + 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0) +); + +/* Sidetone Gain = M * 2^(-5-N) */ +struct st_gain { + unsigned int db; + unsigned int m; + unsigned int n; +}; + +static struct st_gain st_table[] = { + {-12041, 1, 15}, {-11439, 1, 14}, {-11087, 3, 15}, {-10837, 1, 13}, + {-10643, 5, 15}, {-10485, 3, 14}, {-10351, 7, 15}, {-10235, 1, 12}, + {-10133, 9, 15}, {-10041, 5, 14}, { -9958, 11, 15}, { -9883, 3, 13}, + { -9813, 13, 15}, { -9749, 7, 14}, { -9689, 15, 15}, { -9633, 1, 11}, + { -9580, 17, 15}, { -9531, 9, 14}, { -9484, 19, 15}, { -9439, 5, 13}, + { -9397, 21, 15}, { -9356, 11, 14}, { -9318, 23, 15}, { -9281, 3, 12}, + { -9245, 25, 15}, { -9211, 13, 14}, { -9178, 27, 15}, { -9147, 7, 13}, + { -9116, 29, 15}, { -9087, 15, 14}, { -9058, 31, 15}, { -9031, 1, 10}, + { -8978, 17, 14}, { -8929, 9, 13}, { -8882, 19, 14}, { -8837, 5, 12}, + { -8795, 21, 14}, { -8754, 11, 13}, { -8716, 23, 14}, { -8679, 3, 11}, + { -8643, 25, 14}, { -8609, 13, 13}, { -8576, 27, 14}, { -8545, 7, 12}, + { -8514, 29, 14}, { -8485, 15, 13}, { -8456, 31, 14}, { -8429, 1, 9}, + { -8376, 17, 13}, { -8327, 9, 12}, { -8280, 19, 13}, { -8235, 5, 11}, + { -8193, 21, 13}, { -8152, 11, 12}, { -8114, 23, 13}, { -8077, 3, 10}, + { -8041, 25, 13}, { -8007, 13, 12}, { -7974, 27, 13}, { -7943, 7, 11}, + { -7912, 29, 13}, { -7883, 15, 12}, { -7854, 31, 13}, { -7827, 1, 8}, + { -7774, 17, 12}, { -7724, 9, 11}, { -7678, 19, 12}, { -7633, 5, 10}, + { -7591, 21, 12}, { -7550, 11, 11}, { -7512, 23, 12}, { -7475, 3, 9}, + { -7439, 25, 12}, { -7405, 13, 11}, { -7372, 27, 12}, { -7341, 7, 10}, + { -7310, 29, 12}, { -7281, 15, 11}, { -7252, 31, 12}, { -7225, 1, 7}, + { -7172, 17, 11}, { -7122, 9, 10}, { -7075, 19, 11}, { -7031, 5, 9}, + { -6989, 21, 11}, { -6948, 11, 10}, { -6910, 23, 11}, { -6873, 3, 8}, + { -6837, 25, 11}, { -6803, 13, 10}, { -6770, 27, 11}, { -6739, 7, 9}, + { -6708, 29, 11}, { -6679, 15, 10}, { -6650, 31, 11}, { -6623, 1, 6}, + { -6570, 17, 10}, { -6520, 9, 9}, { -6473, 19, 10}, { -6429, 5, 8}, + { -6386, 21, 10}, { -6346, 11, 9}, { -6307, 23, 10}, { -6270, 3, 7}, + { -6235, 25, 10}, { -6201, 13, 9}, { -6168, 27, 10}, { -6137, 7, 8}, + { -6106, 29, 10}, { -6077, 15, 9}, { -6048, 31, 10}, { -6021, 1, 5}, + { -5968, 17, 9}, { -5918, 9, 8}, { -5871, 19, 9}, { -5827, 5, 7}, + { -5784, 21, 9}, { -5744, 11, 8}, { -5705, 23, 9}, { -5668, 3, 6}, + { -5633, 25, 9}, { -5599, 13, 8}, { -5566, 27, 9}, { -5535, 7, 7}, + { -5504, 29, 9}, { -5475, 15, 8}, { -5446, 31, 9}, { -5419, 1, 4}, + { -5366, 17, 8}, { -5316, 9, 7}, { -5269, 19, 8}, { -5225, 5, 6}, + { -5182, 21, 8}, { -5142, 11, 7}, { -5103, 23, 8}, { -5066, 3, 5}, + { -5031, 25, 8}, { -4997, 13, 7}, { -4964, 27, 8}, { -4932, 7, 6}, + { -4902, 29, 8}, { -4873, 15, 7}, { -4844, 31, 8}, { -4816, 1, 3}, + { -4764, 17, 7}, { -4714, 9, 6}, { -4667, 19, 7}, { -4623, 5, 5}, + { -4580, 21, 7}, { -4540, 11, 6}, { -4501, 23, 7}, { -4464, 3, 4}, + { -4429, 25, 7}, { -4395, 13, 6}, { -4362, 27, 7}, { -4330, 7, 5}, + { -4300, 29, 7}, { -4270, 15, 6}, { -4242, 31, 7}, { -4214, 1, 2}, + { -4162, 17, 6}, { -4112, 9, 5}, { -4065, 19, 6}, { -4021, 5, 4}, + { -3978, 21, 6}, { -3938, 11, 5}, { -3899, 23, 6}, { -3862, 3, 3}, + { -3827, 25, 6}, { -3793, 13, 5}, { -3760, 27, 6}, { -3728, 7, 4}, + { -3698, 29, 6}, { -3668, 15, 5}, { -3640, 31, 6}, { -3612, 1, 1}, + { -3560, 17, 5}, { -3510, 9, 4}, { -3463, 19, 5}, { -3419, 5, 3}, + { -3376, 21, 5}, { -3336, 11, 4}, { -3297, 23, 5}, { -3260, 3, 2}, + { -3225, 25, 5}, { -3191, 13, 4}, { -3158, 27, 5}, { -3126, 7, 3}, + { -3096, 29, 5}, { -3066, 15, 4}, { -3038, 31, 5}, { -3010, 1, 0}, + { -2958, 17, 4}, { -2908, 9, 3}, { -2861, 19, 4}, { -2816, 5, 2}, + { -2774, 21, 4}, { -2734, 11, 3}, { -2695, 23, 4}, { -2658, 3, 1}, + { -2623, 25, 4}, { -2589, 13, 3}, { -2556, 27, 4}, { -2524, 7, 2}, + { -2494, 29, 4}, { -2464, 15, 3}, { -2436, 31, 4}, { -2408, 2, 0}, + { -2356, 17, 3}, { -2306, 9, 2}, { -2259, 19, 3}, { -2214, 5, 1}, + { -2172, 21, 3}, { -2132, 11, 2}, { -2093, 23, 3}, { -2056, 3, 0}, + { -2021, 25, 3}, { -1987, 13, 2}, { -1954, 27, 3}, { -1922, 7, 1}, + { -1892, 29, 3}, { -1862, 15, 2}, { -1834, 31, 3}, { -1806, 4, 0}, + { -1754, 17, 2}, { -1704, 9, 1}, { -1657, 19, 2}, { -1612, 5, 0}, + { -1570, 21, 2}, { -1530, 11, 1}, { -1491, 23, 2}, { -1454, 6, 0}, + { -1419, 25, 2}, { -1384, 13, 1}, { -1352, 27, 2}, { -1320, 7, 0}, + { -1290, 29, 2}, { -1260, 15, 1}, { -1232, 31, 2}, { -1204, 8, 0}, + { -1151, 17, 1}, { -1102, 9, 0}, { -1055, 19, 1}, { -1010, 10, 0}, + { -968, 21, 1}, { -928, 11, 0}, { -889, 23, 1}, { -852, 12, 0}, + { -816, 25, 1}, { -782, 13, 0}, { -750, 27, 1}, { -718, 14, 0}, + { -688, 29, 1}, { -658, 15, 0}, { -630, 31, 1}, { -602, 16, 0}, + { -549, 17, 0}, { -500, 18, 0}, { -453, 19, 0}, { -408, 20, 0}, + { -366, 21, 0}, { -325, 22, 0}, { -287, 23, 0}, { -250, 24, 0}, + { -214, 25, 0}, { -180, 26, 0}, { -148, 27, 0}, { -116, 28, 0}, + { -86, 29, 0}, { -56, 30, 0}, { -28, 31, 0}, { 0, 0, 0}, +}; + +static int snd_soc_get_volsw_2r_st(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + int val[2], val2[2], i; + + val[0] = snd_soc_component_read(component, reg) & 0x3f; + val[1] = (snd_soc_component_read(component, PM860X_SIDETONE_SHIFT) >> 4) & 0xf; + val2[0] = snd_soc_component_read(component, reg2) & 0x3f; + val2[1] = (snd_soc_component_read(component, PM860X_SIDETONE_SHIFT)) & 0xf; + + for (i = 0; i < ARRAY_SIZE(st_table); i++) { + if ((st_table[i].m == val[0]) && (st_table[i].n == val[1])) + ucontrol->value.integer.value[0] = i; + if ((st_table[i].m == val2[0]) && (st_table[i].n == val2[1])) + ucontrol->value.integer.value[1] = i; + } + return 0; +} + +static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + int err; + unsigned int val, val2; + + val = ucontrol->value.integer.value[0]; + val2 = ucontrol->value.integer.value[1]; + + if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table)) + return -EINVAL; + + err = snd_soc_component_update_bits(component, reg, 0x3f, st_table[val].m); + if (err < 0) + return err; + err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, 0xf0, + st_table[val].n << 4); + if (err < 0) + return err; + + err = snd_soc_component_update_bits(component, reg2, 0x3f, st_table[val2].m); + if (err < 0) + return err; + err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, 0x0f, + st_table[val2].n); + return err; +} + +static int snd_soc_get_volsw_2r_out(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + int max = mc->max, val, val2; + unsigned int mask = (1 << fls(max)) - 1; + + val = snd_soc_component_read(component, reg) >> shift; + val2 = snd_soc_component_read(component, reg2) >> shift; + ucontrol->value.integer.value[0] = (max - val) & mask; + ucontrol->value.integer.value[1] = (max - val2) & mask; + + return 0; +} + +static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + int err; + unsigned int val, val2, val_mask; + + val_mask = mask << shift; + val = ((max - ucontrol->value.integer.value[0]) & mask); + val2 = ((max - ucontrol->value.integer.value[1]) & mask); + + val = val << shift; + val2 = val2 << shift; + + err = snd_soc_component_update_bits(component, reg, val_mask, val); + if (err < 0) + return err; + + err = snd_soc_component_update_bits(component, reg2, val_mask, val2); + return err; +} + +/* DAPM Widget Events */ +/* + * A lot registers are belong to RSYNC domain. It requires enabling RSYNC bit + * after updating these registers. Otherwise, these updated registers won't + * be effective. + */ +static int pm860x_rsync_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + /* + * In order to avoid current on the load, mute power-on and power-off + * should be transients. + * Unmute by DAC_MUTE. It should be unmuted when DAPM sequence is + * finished. + */ + snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, DAC_MUTE, 0); + snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, + RSYNC_CHANGE, RSYNC_CHANGE); + return 0; +} + +static int pm860x_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int dac = 0; + int data; + + if (!strcmp(w->name, "Left DAC")) + dac = DAC_LEFT; + if (!strcmp(w->name, "Right DAC")) + dac = DAC_RIGHT; + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (dac) { + /* Auto mute in power-on sequence. */ + dac |= MODULATOR; + snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, + DAC_MUTE, DAC_MUTE); + snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, + RSYNC_CHANGE, RSYNC_CHANGE); + /* update dac */ + snd_soc_component_update_bits(component, PM860X_DAC_EN_2, + dac, dac); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (dac) { + /* Auto mute in power-off sequence. */ + snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, + DAC_MUTE, DAC_MUTE); + snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, + RSYNC_CHANGE, RSYNC_CHANGE); + /* update dac */ + data = snd_soc_component_read(component, PM860X_DAC_EN_2); + data &= ~dac; + if (!(data & (DAC_LEFT | DAC_RIGHT))) + data &= ~MODULATOR; + snd_soc_component_write(component, PM860X_DAC_EN_2, data); + } + break; + } + return 0; +} + +static const char *pm860x_opamp_texts[] = {"-50%", "-25%", "0%", "75%"}; + +static const char *pm860x_pa_texts[] = {"-33%", "0%", "33%", "66%"}; + +static SOC_ENUM_SINGLE_DECL(pm860x_hs1_opamp_enum, + PM860X_HS1_CTRL, 5, pm860x_opamp_texts); + +static SOC_ENUM_SINGLE_DECL(pm860x_hs2_opamp_enum, + PM860X_HS2_CTRL, 5, pm860x_opamp_texts); + +static SOC_ENUM_SINGLE_DECL(pm860x_hs1_pa_enum, + PM860X_HS1_CTRL, 3, pm860x_pa_texts); + +static SOC_ENUM_SINGLE_DECL(pm860x_hs2_pa_enum, + PM860X_HS2_CTRL, 3, pm860x_pa_texts); + +static SOC_ENUM_SINGLE_DECL(pm860x_lo1_opamp_enum, + PM860X_LO1_CTRL, 5, pm860x_opamp_texts); + +static SOC_ENUM_SINGLE_DECL(pm860x_lo2_opamp_enum, + PM860X_LO2_CTRL, 5, pm860x_opamp_texts); + +static SOC_ENUM_SINGLE_DECL(pm860x_lo1_pa_enum, + PM860X_LO1_CTRL, 3, pm860x_pa_texts); + +static SOC_ENUM_SINGLE_DECL(pm860x_lo2_pa_enum, + PM860X_LO2_CTRL, 3, pm860x_pa_texts); + +static SOC_ENUM_SINGLE_DECL(pm860x_spk_pa_enum, + PM860X_EAR_CTRL_1, 5, pm860x_pa_texts); + +static SOC_ENUM_SINGLE_DECL(pm860x_ear_pa_enum, + PM860X_EAR_CTRL_2, 0, pm860x_pa_texts); + +static SOC_ENUM_SINGLE_DECL(pm860x_spk_ear_opamp_enum, + PM860X_EAR_CTRL_1, 3, pm860x_opamp_texts); + +static const struct snd_kcontrol_new pm860x_snd_controls[] = { + SOC_DOUBLE_R_TLV("ADC Capture Volume", PM860X_ADC_ANA_2, + PM860X_ADC_ANA_3, 6, 3, 0, adc_tlv), + SOC_DOUBLE_TLV("AUX Capture Volume", PM860X_ADC_ANA_3, 0, 3, 7, 0, + aux_tlv), + SOC_SINGLE_TLV("MIC1 Capture Volume", PM860X_ADC_ANA_2, 0, 7, 0, + mic_tlv), + SOC_SINGLE_TLV("MIC3 Capture Volume", PM860X_ADC_ANA_2, 3, 7, 0, + mic_tlv), + SOC_DOUBLE_R_EXT_TLV("Sidetone Volume", PM860X_SIDETONE_L_GAIN, + PM860X_SIDETONE_R_GAIN, 0, ARRAY_SIZE(st_table)-1, + 0, snd_soc_get_volsw_2r_st, + snd_soc_put_volsw_2r_st, st_tlv), + SOC_SINGLE_TLV("Speaker Playback Volume", PM860X_EAR_CTRL_1, + 0, 7, 0, out_tlv), + SOC_DOUBLE_R_TLV("Line Playback Volume", PM860X_LO1_CTRL, + PM860X_LO2_CTRL, 0, 7, 0, out_tlv), + SOC_DOUBLE_R_TLV("Headset Playback Volume", PM860X_HS1_CTRL, + PM860X_HS2_CTRL, 0, 7, 0, out_tlv), + SOC_DOUBLE_R_EXT_TLV("Hifi Left Playback Volume", + PM860X_HIFIL_GAIN_LEFT, + PM860X_HIFIL_GAIN_RIGHT, 0, 63, 0, + snd_soc_get_volsw_2r_out, + snd_soc_put_volsw_2r_out, dpga_tlv), + SOC_DOUBLE_R_EXT_TLV("Hifi Right Playback Volume", + PM860X_HIFIR_GAIN_LEFT, + PM860X_HIFIR_GAIN_RIGHT, 0, 63, 0, + snd_soc_get_volsw_2r_out, + snd_soc_put_volsw_2r_out, dpga_tlv), + SOC_DOUBLE_R_EXT_TLV("Lofi Playback Volume", PM860X_LOFI_GAIN_LEFT, + PM860X_LOFI_GAIN_RIGHT, 0, 63, 0, + snd_soc_get_volsw_2r_out, + snd_soc_put_volsw_2r_out, dpga_tlv), + SOC_ENUM("Headset1 Operational Amplifier Current", + pm860x_hs1_opamp_enum), + SOC_ENUM("Headset2 Operational Amplifier Current", + pm860x_hs2_opamp_enum), + SOC_ENUM("Headset1 Amplifier Current", pm860x_hs1_pa_enum), + SOC_ENUM("Headset2 Amplifier Current", pm860x_hs2_pa_enum), + SOC_ENUM("Lineout1 Operational Amplifier Current", + pm860x_lo1_opamp_enum), + SOC_ENUM("Lineout2 Operational Amplifier Current", + pm860x_lo2_opamp_enum), + SOC_ENUM("Lineout1 Amplifier Current", pm860x_lo1_pa_enum), + SOC_ENUM("Lineout2 Amplifier Current", pm860x_lo2_pa_enum), + SOC_ENUM("Speaker Operational Amplifier Current", + pm860x_spk_ear_opamp_enum), + SOC_ENUM("Speaker Amplifier Current", pm860x_spk_pa_enum), + SOC_ENUM("Earpiece Amplifier Current", pm860x_ear_pa_enum), +}; + +/* + * DAPM Controls + */ + +/* AUX1 Switch */ +static const struct snd_kcontrol_new aux1_switch_controls = + SOC_DAPM_SINGLE("Switch", PM860X_ANA_TO_ANA, 4, 1, 0); + +/* AUX2 Switch */ +static const struct snd_kcontrol_new aux2_switch_controls = + SOC_DAPM_SINGLE("Switch", PM860X_ANA_TO_ANA, 5, 1, 0); + +/* Left Ex. PA Switch */ +static const struct snd_kcontrol_new lepa_switch_controls = + SOC_DAPM_SINGLE("Switch", PM860X_DAC_EN_2, 2, 1, 0); + +/* Right Ex. PA Switch */ +static const struct snd_kcontrol_new repa_switch_controls = + SOC_DAPM_SINGLE("Switch", PM860X_DAC_EN_2, 1, 1, 0); + +/* I2S Mux / Mux9 */ +static const char *i2s_din_text[] = { + "DIN", "DIN1", +}; + +static SOC_ENUM_SINGLE_DECL(i2s_din_enum, + PM860X_I2S_IFACE_3, 1, i2s_din_text); + +static const struct snd_kcontrol_new i2s_din_mux = + SOC_DAPM_ENUM("I2S DIN Mux", i2s_din_enum); + +/* I2S Mic Mux / Mux8 */ +static const char *i2s_mic_text[] = { + "Ex PA", "ADC", +}; + +static SOC_ENUM_SINGLE_DECL(i2s_mic_enum, + PM860X_I2S_IFACE_3, 4, i2s_mic_text); + +static const struct snd_kcontrol_new i2s_mic_mux = + SOC_DAPM_ENUM("I2S Mic Mux", i2s_mic_enum); + +/* ADCL Mux / Mux2 */ +static const char *adcl_text[] = { + "ADCR", "ADCL", +}; + +static SOC_ENUM_SINGLE_DECL(adcl_enum, + PM860X_PCM_IFACE_3, 4, adcl_text); + +static const struct snd_kcontrol_new adcl_mux = + SOC_DAPM_ENUM("ADC Left Mux", adcl_enum); + +/* ADCR Mux / Mux3 */ +static const char *adcr_text[] = { + "ADCL", "ADCR", +}; + +static SOC_ENUM_SINGLE_DECL(adcr_enum, + PM860X_PCM_IFACE_3, 2, adcr_text); + +static const struct snd_kcontrol_new adcr_mux = + SOC_DAPM_ENUM("ADC Right Mux", adcr_enum); + +/* ADCR EC Mux / Mux6 */ +static const char *adcr_ec_text[] = { + "ADCR", "EC", +}; + +static SOC_ENUM_SINGLE_DECL(adcr_ec_enum, + PM860X_ADC_EN_2, 3, adcr_ec_text); + +static const struct snd_kcontrol_new adcr_ec_mux = + SOC_DAPM_ENUM("ADCR EC Mux", adcr_ec_enum); + +/* EC Mux / Mux4 */ +static const char *ec_text[] = { + "Left", "Right", "Left + Right", +}; + +static SOC_ENUM_SINGLE_DECL(ec_enum, + PM860X_EC_PATH, 1, ec_text); + +static const struct snd_kcontrol_new ec_mux = + SOC_DAPM_ENUM("EC Mux", ec_enum); + +static const char *dac_text[] = { + "No input", "Right", "Left", "No input", +}; + +/* DAC Headset 1 Mux / Mux10 */ +static SOC_ENUM_SINGLE_DECL(dac_hs1_enum, + PM860X_ANA_INPUT_SEL_1, 0, dac_text); + +static const struct snd_kcontrol_new dac_hs1_mux = + SOC_DAPM_ENUM("DAC HS1 Mux", dac_hs1_enum); + +/* DAC Headset 2 Mux / Mux11 */ +static SOC_ENUM_SINGLE_DECL(dac_hs2_enum, + PM860X_ANA_INPUT_SEL_1, 2, dac_text); + +static const struct snd_kcontrol_new dac_hs2_mux = + SOC_DAPM_ENUM("DAC HS2 Mux", dac_hs2_enum); + +/* DAC Lineout 1 Mux / Mux12 */ +static SOC_ENUM_SINGLE_DECL(dac_lo1_enum, + PM860X_ANA_INPUT_SEL_1, 4, dac_text); + +static const struct snd_kcontrol_new dac_lo1_mux = + SOC_DAPM_ENUM("DAC LO1 Mux", dac_lo1_enum); + +/* DAC Lineout 2 Mux / Mux13 */ +static SOC_ENUM_SINGLE_DECL(dac_lo2_enum, + PM860X_ANA_INPUT_SEL_1, 6, dac_text); + +static const struct snd_kcontrol_new dac_lo2_mux = + SOC_DAPM_ENUM("DAC LO2 Mux", dac_lo2_enum); + +/* DAC Spearker Earphone Mux / Mux14 */ +static SOC_ENUM_SINGLE_DECL(dac_spk_ear_enum, + PM860X_ANA_INPUT_SEL_2, 0, dac_text); + +static const struct snd_kcontrol_new dac_spk_ear_mux = + SOC_DAPM_ENUM("DAC SP Mux", dac_spk_ear_enum); + +/* Headset 1 Mux / Mux15 */ +static const char *in_text[] = { + "Digital", "Analog", +}; + +static SOC_ENUM_SINGLE_DECL(hs1_enum, + PM860X_ANA_TO_ANA, 0, in_text); + +static const struct snd_kcontrol_new hs1_mux = + SOC_DAPM_ENUM("Headset1 Mux", hs1_enum); + +/* Headset 2 Mux / Mux16 */ +static SOC_ENUM_SINGLE_DECL(hs2_enum, + PM860X_ANA_TO_ANA, 1, in_text); + +static const struct snd_kcontrol_new hs2_mux = + SOC_DAPM_ENUM("Headset2 Mux", hs2_enum); + +/* Lineout 1 Mux / Mux17 */ +static SOC_ENUM_SINGLE_DECL(lo1_enum, + PM860X_ANA_TO_ANA, 2, in_text); + +static const struct snd_kcontrol_new lo1_mux = + SOC_DAPM_ENUM("Lineout1 Mux", lo1_enum); + +/* Lineout 2 Mux / Mux18 */ +static SOC_ENUM_SINGLE_DECL(lo2_enum, + PM860X_ANA_TO_ANA, 3, in_text); + +static const struct snd_kcontrol_new lo2_mux = + SOC_DAPM_ENUM("Lineout2 Mux", lo2_enum); + +/* Speaker Earpiece Demux */ +static const char *spk_text[] = { + "Earpiece", "Speaker", +}; + +static SOC_ENUM_SINGLE_DECL(spk_enum, + PM860X_ANA_TO_ANA, 6, spk_text); + +static const struct snd_kcontrol_new spk_demux = + SOC_DAPM_ENUM("Speaker Earpiece Demux", spk_enum); + +/* MIC Mux / Mux1 */ +static const char *mic_text[] = { + "Mic 1", "Mic 2", +}; + +static SOC_ENUM_SINGLE_DECL(mic_enum, + PM860X_ADC_ANA_4, 4, mic_text); + +static const struct snd_kcontrol_new mic_mux = + SOC_DAPM_ENUM("MIC Mux", mic_enum); + +static const struct snd_soc_dapm_widget pm860x_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("PCM SDI", "PCM Playback", 0, + PM860X_ADC_EN_2, 0, 0), + SND_SOC_DAPM_AIF_OUT("PCM SDO", "PCM Capture", 0, + PM860X_PCM_IFACE_3, 1, 1), + + + SND_SOC_DAPM_AIF_IN("I2S DIN", "I2S Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S DIN1", "I2S Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S DOUT", "I2S Capture", 0, + PM860X_I2S_IFACE_3, 5, 1), + SND_SOC_DAPM_SUPPLY("I2S CLK", PM860X_DAC_EN_2, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("I2S Mic Mux", SND_SOC_NOPM, 0, 0, &i2s_mic_mux), + SND_SOC_DAPM_MUX("ADC Left Mux", SND_SOC_NOPM, 0, 0, &adcl_mux), + SND_SOC_DAPM_MUX("ADC Right Mux", SND_SOC_NOPM, 0, 0, &adcr_mux), + SND_SOC_DAPM_MUX("EC Mux", SND_SOC_NOPM, 0, 0, &ec_mux), + SND_SOC_DAPM_MUX("ADCR EC Mux", SND_SOC_NOPM, 0, 0, &adcr_ec_mux), + SND_SOC_DAPM_SWITCH("Left EPA", SND_SOC_NOPM, 0, 0, + &lepa_switch_controls), + SND_SOC_DAPM_SWITCH("Right EPA", SND_SOC_NOPM, 0, 0, + &repa_switch_controls), + + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Left ADC MOD", PM860X_ADC_EN_1, + 0, 1, 1, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Right ADC MOD", PM860X_ADC_EN_1, + 1, 1, 1, 0), + SND_SOC_DAPM_ADC("Left ADC", NULL, PM860X_ADC_EN_2, 5, 0), + SND_SOC_DAPM_ADC("Right ADC", NULL, PM860X_ADC_EN_2, 4, 0), + + SND_SOC_DAPM_SWITCH("AUX1 Switch", SND_SOC_NOPM, 0, 0, + &aux1_switch_controls), + SND_SOC_DAPM_SWITCH("AUX2 Switch", SND_SOC_NOPM, 0, 0, + &aux2_switch_controls), + + SND_SOC_DAPM_MUX("MIC Mux", SND_SOC_NOPM, 0, 0, &mic_mux), + SND_SOC_DAPM_MICBIAS("Mic1 Bias", PM860X_ADC_ANA_1, 2, 0), + SND_SOC_DAPM_MICBIAS("Mic3 Bias", PM860X_ADC_ANA_1, 7, 0), + SND_SOC_DAPM_PGA("MIC1 Volume", PM860X_ADC_EN_1, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC3 Volume", PM860X_ADC_EN_1, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("AUX1 Volume", PM860X_ADC_EN_1, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("AUX2 Volume", PM860X_ADC_EN_1, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Sidetone PGA", PM860X_ADC_EN_2, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("Lofi PGA", PM860X_ADC_EN_2, 2, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("AUX1"), + SND_SOC_DAPM_INPUT("AUX2"), + SND_SOC_DAPM_INPUT("MIC1P"), + SND_SOC_DAPM_INPUT("MIC1N"), + SND_SOC_DAPM_INPUT("MIC2P"), + SND_SOC_DAPM_INPUT("MIC2N"), + SND_SOC_DAPM_INPUT("MIC3P"), + SND_SOC_DAPM_INPUT("MIC3N"), + + SND_SOC_DAPM_DAC_E("Left DAC", NULL, SND_SOC_NOPM, 0, 0, + pm860x_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("Right DAC", NULL, SND_SOC_NOPM, 0, 0, + pm860x_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX("I2S DIN Mux", SND_SOC_NOPM, 0, 0, &i2s_din_mux), + SND_SOC_DAPM_MUX("DAC HS1 Mux", SND_SOC_NOPM, 0, 0, &dac_hs1_mux), + SND_SOC_DAPM_MUX("DAC HS2 Mux", SND_SOC_NOPM, 0, 0, &dac_hs2_mux), + SND_SOC_DAPM_MUX("DAC LO1 Mux", SND_SOC_NOPM, 0, 0, &dac_lo1_mux), + SND_SOC_DAPM_MUX("DAC LO2 Mux", SND_SOC_NOPM, 0, 0, &dac_lo2_mux), + SND_SOC_DAPM_MUX("DAC SP Mux", SND_SOC_NOPM, 0, 0, &dac_spk_ear_mux), + SND_SOC_DAPM_MUX("Headset1 Mux", SND_SOC_NOPM, 0, 0, &hs1_mux), + SND_SOC_DAPM_MUX("Headset2 Mux", SND_SOC_NOPM, 0, 0, &hs2_mux), + SND_SOC_DAPM_MUX("Lineout1 Mux", SND_SOC_NOPM, 0, 0, &lo1_mux), + SND_SOC_DAPM_MUX("Lineout2 Mux", SND_SOC_NOPM, 0, 0, &lo2_mux), + SND_SOC_DAPM_MUX("Speaker Earpiece Demux", SND_SOC_NOPM, 0, 0, + &spk_demux), + + + SND_SOC_DAPM_PGA("Headset1 PGA", PM860X_DAC_EN_1, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Headset2 PGA", PM860X_DAC_EN_1, 1, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("HS1"), + SND_SOC_DAPM_OUTPUT("HS2"), + SND_SOC_DAPM_PGA("Lineout1 PGA", PM860X_DAC_EN_1, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("Lineout2 PGA", PM860X_DAC_EN_1, 3, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_PGA("Earpiece PGA", PM860X_DAC_EN_1, 4, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("EARP"), + SND_SOC_DAPM_OUTPUT("EARN"), + SND_SOC_DAPM_PGA("Speaker PGA", PM860X_DAC_EN_1, 5, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("LSP"), + SND_SOC_DAPM_OUTPUT("LSN"), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "VCODEC", PM860X_AUDIO_SUPPLIES_2, + 0, SUPPLY_MASK, SUPPLY_MASK, 0), + + PM860X_DAPM_OUTPUT("RSYNC", pm860x_rsync_event), +}; + +static const struct snd_soc_dapm_route pm860x_dapm_routes[] = { + /* supply */ + {"Left DAC", NULL, "VCODEC"}, + {"Right DAC", NULL, "VCODEC"}, + {"Left ADC", NULL, "VCODEC"}, + {"Right ADC", NULL, "VCODEC"}, + {"Left ADC", NULL, "Left ADC MOD"}, + {"Right ADC", NULL, "Right ADC MOD"}, + + /* I2S Clock */ + {"I2S DIN", NULL, "I2S CLK"}, + {"I2S DIN1", NULL, "I2S CLK"}, + {"I2S DOUT", NULL, "I2S CLK"}, + + /* PCM/AIF1 Inputs */ + {"PCM SDO", NULL, "ADC Left Mux"}, + {"PCM SDO", NULL, "ADCR EC Mux"}, + + /* PCM/AFI2 Outputs */ + {"Lofi PGA", NULL, "PCM SDI"}, + {"Lofi PGA", NULL, "Sidetone PGA"}, + {"Left DAC", NULL, "Lofi PGA"}, + {"Right DAC", NULL, "Lofi PGA"}, + + /* I2S/AIF2 Inputs */ + {"MIC Mux", "Mic 1", "MIC1P"}, + {"MIC Mux", "Mic 1", "MIC1N"}, + {"MIC Mux", "Mic 2", "MIC2P"}, + {"MIC Mux", "Mic 2", "MIC2N"}, + {"MIC1 Volume", NULL, "MIC Mux"}, + {"MIC3 Volume", NULL, "MIC3P"}, + {"MIC3 Volume", NULL, "MIC3N"}, + {"Left ADC", NULL, "MIC1 Volume"}, + {"Right ADC", NULL, "MIC3 Volume"}, + {"ADC Left Mux", "ADCR", "Right ADC"}, + {"ADC Left Mux", "ADCL", "Left ADC"}, + {"ADC Right Mux", "ADCL", "Left ADC"}, + {"ADC Right Mux", "ADCR", "Right ADC"}, + {"Left EPA", "Switch", "Left DAC"}, + {"Right EPA", "Switch", "Right DAC"}, + {"EC Mux", "Left", "Left DAC"}, + {"EC Mux", "Right", "Right DAC"}, + {"EC Mux", "Left + Right", "Left DAC"}, + {"EC Mux", "Left + Right", "Right DAC"}, + {"ADCR EC Mux", "ADCR", "ADC Right Mux"}, + {"ADCR EC Mux", "EC", "EC Mux"}, + {"I2S Mic Mux", "Ex PA", "Left EPA"}, + {"I2S Mic Mux", "Ex PA", "Right EPA"}, + {"I2S Mic Mux", "ADC", "ADC Left Mux"}, + {"I2S Mic Mux", "ADC", "ADCR EC Mux"}, + {"I2S DOUT", NULL, "I2S Mic Mux"}, + + /* I2S/AIF2 Outputs */ + {"I2S DIN Mux", "DIN", "I2S DIN"}, + {"I2S DIN Mux", "DIN1", "I2S DIN1"}, + {"Left DAC", NULL, "I2S DIN Mux"}, + {"Right DAC", NULL, "I2S DIN Mux"}, + {"DAC HS1 Mux", "Left", "Left DAC"}, + {"DAC HS1 Mux", "Right", "Right DAC"}, + {"DAC HS2 Mux", "Left", "Left DAC"}, + {"DAC HS2 Mux", "Right", "Right DAC"}, + {"DAC LO1 Mux", "Left", "Left DAC"}, + {"DAC LO1 Mux", "Right", "Right DAC"}, + {"DAC LO2 Mux", "Left", "Left DAC"}, + {"DAC LO2 Mux", "Right", "Right DAC"}, + {"Headset1 Mux", "Digital", "DAC HS1 Mux"}, + {"Headset2 Mux", "Digital", "DAC HS2 Mux"}, + {"Lineout1 Mux", "Digital", "DAC LO1 Mux"}, + {"Lineout2 Mux", "Digital", "DAC LO2 Mux"}, + {"Headset1 PGA", NULL, "Headset1 Mux"}, + {"Headset2 PGA", NULL, "Headset2 Mux"}, + {"Lineout1 PGA", NULL, "Lineout1 Mux"}, + {"Lineout2 PGA", NULL, "Lineout2 Mux"}, + {"DAC SP Mux", "Left", "Left DAC"}, + {"DAC SP Mux", "Right", "Right DAC"}, + {"Speaker Earpiece Demux", "Speaker", "DAC SP Mux"}, + {"Speaker PGA", NULL, "Speaker Earpiece Demux"}, + {"Earpiece PGA", NULL, "Speaker Earpiece Demux"}, + + {"RSYNC", NULL, "Headset1 PGA"}, + {"RSYNC", NULL, "Headset2 PGA"}, + {"RSYNC", NULL, "Lineout1 PGA"}, + {"RSYNC", NULL, "Lineout2 PGA"}, + {"RSYNC", NULL, "Speaker PGA"}, + {"RSYNC", NULL, "Speaker PGA"}, + {"RSYNC", NULL, "Earpiece PGA"}, + {"RSYNC", NULL, "Earpiece PGA"}, + + {"HS1", NULL, "RSYNC"}, + {"HS2", NULL, "RSYNC"}, + {"LINEOUT1", NULL, "RSYNC"}, + {"LINEOUT2", NULL, "RSYNC"}, + {"LSP", NULL, "RSYNC"}, + {"LSN", NULL, "RSYNC"}, + {"EARP", NULL, "RSYNC"}, + {"EARN", NULL, "RSYNC"}, +}; + +/* + * Use MUTE_LEFT & MUTE_RIGHT to implement digital mute. + * These bits can also be used to mute. + */ +static int pm860x_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + int data = 0, mask = MUTE_LEFT | MUTE_RIGHT; + + if (mute) + data = mask; + snd_soc_component_update_bits(component, PM860X_DAC_OFFSET, mask, data); + snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2, + RSYNC_CHANGE, RSYNC_CHANGE); + return 0; +} + +static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned char inf = 0, mask = 0; + + /* bit size */ + switch (params_width(params)) { + case 16: + inf &= ~PCM_INF2_18WL; + break; + case 18: + inf |= PCM_INF2_18WL; + break; + default: + return -EINVAL; + } + mask |= PCM_INF2_18WL; + snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, inf); + + /* sample rate */ + switch (params_rate(params)) { + case 8000: + inf = 0; + break; + case 16000: + inf = 3; + break; + case 32000: + inf = 6; + break; + case 48000: + inf = 8; + break; + default: + return -EINVAL; + } + snd_soc_component_update_bits(component, PM860X_PCM_RATE, 0x0f, inf); + + return 0; +} + +static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); + unsigned char inf = 0, mask = 0; + int ret = -EINVAL; + + mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER; + + /* set audio interface clocking */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + case SND_SOC_DAIFMT_CBP_CFC: + if (pm860x->dir == PM860X_CLK_DIR_OUT) { + inf |= PCM_INF2_MASTER; + ret = 0; + } + break; + case SND_SOC_DAIFMT_CBC_CFC: + if (pm860x->dir == PM860X_CLK_DIR_IN) { + inf &= ~PCM_INF2_MASTER; + ret = 0; + } + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + inf |= PCM_EXACT_I2S; + ret = 0; + break; + } + mask |= PCM_MODE_MASK; + if (ret) + return ret; + snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, inf); + return 0; +} + +static int pm860x_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); + + if (dir == PM860X_CLK_DIR_OUT) + pm860x->dir = PM860X_CLK_DIR_OUT; + else /* Slave mode is not supported */ + return -EINVAL; + + return 0; +} + +static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned char inf; + + /* bit size */ + switch (params_width(params)) { + case 16: + inf = 0; + break; + case 18: + inf = PCM_INF2_18WL; + break; + default: + return -EINVAL; + } + snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, PCM_INF2_18WL, inf); + + /* sample rate */ + switch (params_rate(params)) { + case 8000: + inf = 0; + break; + case 11025: + inf = 1; + break; + case 16000: + inf = 3; + break; + case 22050: + inf = 4; + break; + case 32000: + inf = 6; + break; + case 44100: + inf = 7; + break; + case 48000: + inf = 8; + break; + default: + return -EINVAL; + } + snd_soc_component_update_bits(component, PM860X_I2S_IFACE_4, 0xf, inf); + + return 0; +} + +static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); + unsigned char inf = 0, mask = 0; + + mask |= PCM_INF2_BCLK | PCM_INF2_FS | PCM_INF2_MASTER; + + /* set audio interface clocking */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + if (pm860x->dir == PM860X_CLK_DIR_OUT) + inf |= PCM_INF2_MASTER; + else + return -EINVAL; + break; + case SND_SOC_DAIFMT_CBC_CFC: + if (pm860x->dir == PM860X_CLK_DIR_IN) + inf &= ~PCM_INF2_MASTER; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + inf |= PCM_EXACT_I2S; + break; + default: + return -EINVAL; + } + mask |= PCM_MODE_MASK; + snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, mask, inf); + return 0; +} + +static int pm860x_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); + int data; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Enable Audio PLL & Audio section */ + data = AUDIO_PLL | AUDIO_SECTION_ON; + pm860x_reg_write(pm860x->i2c, REG_MISC2, data); + udelay(300); + data = AUDIO_PLL | AUDIO_SECTION_RESET + | AUDIO_SECTION_ON; + pm860x_reg_write(pm860x->i2c, REG_MISC2, data); + } + break; + + case SND_SOC_BIAS_OFF: + data = AUDIO_PLL | AUDIO_SECTION_RESET | AUDIO_SECTION_ON; + pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0); + break; + } + return 0; +} + +static const struct snd_soc_dai_ops pm860x_pcm_dai_ops = { + .mute_stream = pm860x_mute_stream, + .hw_params = pm860x_pcm_hw_params, + .set_fmt = pm860x_pcm_set_dai_fmt, + .set_sysclk = pm860x_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops pm860x_i2s_dai_ops = { + .mute_stream = pm860x_mute_stream, + .hw_params = pm860x_i2s_hw_params, + .set_fmt = pm860x_i2s_set_dai_fmt, + .set_sysclk = pm860x_set_dai_sysclk, + .no_capture_mute = 1, +}; + +#define PM860X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) + +static struct snd_soc_dai_driver pm860x_dai[] = { + { + /* DAI PCM */ + .name = "88pm860x-pcm", + .id = 1, + .playback = { + .stream_name = "PCM Playback", + .channels_min = 2, + .channels_max = 2, + .rates = PM860X_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, + }, + .capture = { + .stream_name = "PCM Capture", + .channels_min = 2, + .channels_max = 2, + .rates = PM860X_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, + }, + .ops = &pm860x_pcm_dai_ops, + }, { + /* DAI I2S */ + .name = "88pm860x-i2s", + .id = 2, + .playback = { + .stream_name = "I2S Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, + }, + .capture = { + .stream_name = "I2S Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE, + }, + .ops = &pm860x_i2s_dai_ops, + }, +}; + +static irqreturn_t pm860x_component_handler(int irq, void *data) +{ + struct pm860x_priv *pm860x = data; + int status, shrt, report = 0, mic_report = 0; + int mask; + + status = pm860x_reg_read(pm860x->i2c, REG_STATUS_1); + shrt = pm860x_reg_read(pm860x->i2c, REG_SHORTS); + mask = pm860x->det.hs_shrt | pm860x->det.hook_det | pm860x->det.lo_shrt + | pm860x->det.hp_det; + +#ifndef CONFIG_SND_SOC_88PM860X_MODULE + if (status & (HEADSET_STATUS | MIC_STATUS | SHORT_HS1 | SHORT_HS2 | + SHORT_LO1 | SHORT_LO2)) + trace_snd_soc_jack_irq(dev_name(pm860x->component->dev)); +#endif + + if ((pm860x->det.hp_det & SND_JACK_HEADPHONE) + && (status & HEADSET_STATUS)) + report |= SND_JACK_HEADPHONE; + + if ((pm860x->det.mic_det & SND_JACK_MICROPHONE) + && (status & MIC_STATUS)) + mic_report |= SND_JACK_MICROPHONE; + + if (pm860x->det.hs_shrt && (shrt & (SHORT_HS1 | SHORT_HS2))) + report |= pm860x->det.hs_shrt; + + if (pm860x->det.hook_det && (status & HOOK_STATUS)) + report |= pm860x->det.hook_det; + + if (pm860x->det.lo_shrt && (shrt & (SHORT_LO1 | SHORT_LO2))) + report |= pm860x->det.lo_shrt; + + if (report) + snd_soc_jack_report(pm860x->det.hp_jack, report, mask); + if (mic_report) + snd_soc_jack_report(pm860x->det.mic_jack, SND_JACK_MICROPHONE, + SND_JACK_MICROPHONE); + + dev_dbg(pm860x->component->dev, "headphone report:0x%x, mask:%x\n", + report, mask); + dev_dbg(pm860x->component->dev, "microphone report:0x%x\n", mic_report); + return IRQ_HANDLED; +} + +int pm860x_hs_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, + int det, int hook, int hs_shrt, int lo_shrt) +{ + struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); + int data; + + pm860x->det.hp_jack = jack; + pm860x->det.hp_det = det; + pm860x->det.hook_det = hook; + pm860x->det.hs_shrt = hs_shrt; + pm860x->det.lo_shrt = lo_shrt; + + if (det & SND_JACK_HEADPHONE) + pm860x_set_bits(pm860x->i2c, REG_HS_DET, + EN_HS_DET, EN_HS_DET); + /* headset short detect */ + if (hs_shrt) { + data = CLR_SHORT_HS2 | CLR_SHORT_HS1; + pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data); + } + /* Lineout short detect */ + if (lo_shrt) { + data = CLR_SHORT_LO2 | CLR_SHORT_LO1; + pm860x_set_bits(pm860x->i2c, REG_SHORTS, data, data); + } + + /* sync status */ + pm860x_component_handler(0, pm860x); + return 0; +} +EXPORT_SYMBOL_GPL(pm860x_hs_jack_detect); + +int pm860x_mic_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, int det) +{ + struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); + + pm860x->det.mic_jack = jack; + pm860x->det.mic_det = det; + + if (det & SND_JACK_MICROPHONE) + pm860x_set_bits(pm860x->i2c, REG_MIC_DET, + MICDET_MASK, MICDET_MASK); + + /* sync status */ + pm860x_component_handler(0, pm860x); + return 0; +} +EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect); + +static int pm860x_probe(struct snd_soc_component *component) +{ + struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); + int i, ret; + + pm860x->component = component; + snd_soc_component_init_regmap(component, pm860x->regmap); + + for (i = 0; i < 4; i++) { + ret = request_threaded_irq(pm860x->irq[i], NULL, + pm860x_component_handler, IRQF_ONESHOT, + pm860x->name[i], pm860x); + if (ret < 0) { + dev_err(component->dev, "Failed to request IRQ!\n"); + goto out; + } + } + + return 0; + +out: + while (--i >= 0) + free_irq(pm860x->irq[i], pm860x); + return ret; +} + +static void pm860x_remove(struct snd_soc_component *component) +{ + struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component); + int i; + + for (i = 3; i >= 0; i--) + free_irq(pm860x->irq[i], pm860x); +} + +static const struct snd_soc_component_driver soc_component_dev_pm860x = { + .probe = pm860x_probe, + .remove = pm860x_remove, + .set_bias_level = pm860x_set_bias_level, + .controls = pm860x_snd_controls, + .num_controls = ARRAY_SIZE(pm860x_snd_controls), + .dapm_widgets = pm860x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pm860x_dapm_widgets), + .dapm_routes = pm860x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pm860x_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int pm860x_codec_probe(struct platform_device *pdev) +{ + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm860x_priv *pm860x; + struct resource *res; + int i, ret; + + pm860x = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_priv), + GFP_KERNEL); + if (pm860x == NULL) + return -ENOMEM; + + pm860x->chip = chip; + pm860x->i2c = (chip->id == CHIP_PM8607) ? chip->client + : chip->companion; + pm860x->regmap = (chip->id == CHIP_PM8607) ? chip->regmap + : chip->regmap_companion; + platform_set_drvdata(pdev, pm860x); + + for (i = 0; i < 4; i++) { + res = platform_get_resource(pdev, IORESOURCE_IRQ, i); + if (!res) { + dev_err(&pdev->dev, "Failed to get IRQ resources\n"); + return -EINVAL; + } + pm860x->irq[i] = res->start + chip->irq_base; + strscpy(pm860x->name[i], res->name, MAX_NAME_LEN); + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_pm860x, + pm860x_dai, ARRAY_SIZE(pm860x_dai)); + if (ret) { + dev_err(&pdev->dev, "Failed to register component\n"); + return -EINVAL; + } + return ret; +} + +static struct platform_driver pm860x_codec_driver = { + .driver = { + .name = "88pm860x-codec", + }, + .probe = pm860x_codec_probe, +}; + +module_platform_driver(pm860x_codec_driver); + +MODULE_DESCRIPTION("ASoC 88PM860x driver"); +MODULE_AUTHOR("Haojian Zhuang "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:88pm860x-codec"); + diff --git a/sound/soc/codecs/88pm860x-codec.h b/sound/soc/codecs/88pm860x-codec.h new file mode 100644 index 0000000000..f025146e50 --- /dev/null +++ b/sound/soc/codecs/88pm860x-codec.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 88pm860x-codec.h -- 88PM860x ALSA SoC Audio Driver + * + * Copyright 2010 Marvell International Ltd. + * Haojian Zhuang + */ + +#ifndef __88PM860X_H +#define __88PM860X_H + +#define PM860X_PCM_IFACE_1 0xb0 +#define PM860X_PCM_IFACE_2 0xb1 +#define PM860X_PCM_IFACE_3 0xb2 +#define PM860X_PCM_RATE 0xb3 +#define PM860X_EC_PATH 0xb4 +#define PM860X_SIDETONE_L_GAIN 0xb5 +#define PM860X_SIDETONE_R_GAIN 0xb6 +#define PM860X_SIDETONE_SHIFT 0xb7 +#define PM860X_ADC_OFFSET_1 0xb8 +#define PM860X_ADC_OFFSET_2 0xb9 +#define PM860X_DMIC_DELAY 0xba + +#define PM860X_I2S_IFACE_1 0xbb +#define PM860X_I2S_IFACE_2 0xbc +#define PM860X_I2S_IFACE_3 0xbd +#define PM860X_I2S_IFACE_4 0xbe +#define PM860X_EQUALIZER_N0_1 0xbf +#define PM860X_EQUALIZER_N0_2 0xc0 +#define PM860X_EQUALIZER_N1_1 0xc1 +#define PM860X_EQUALIZER_N1_2 0xc2 +#define PM860X_EQUALIZER_D1_1 0xc3 +#define PM860X_EQUALIZER_D1_2 0xc4 +#define PM860X_LOFI_GAIN_LEFT 0xc5 +#define PM860X_LOFI_GAIN_RIGHT 0xc6 +#define PM860X_HIFIL_GAIN_LEFT 0xc7 +#define PM860X_HIFIL_GAIN_RIGHT 0xc8 +#define PM860X_HIFIR_GAIN_LEFT 0xc9 +#define PM860X_HIFIR_GAIN_RIGHT 0xca +#define PM860X_DAC_OFFSET 0xcb +#define PM860X_OFFSET_LEFT_1 0xcc +#define PM860X_OFFSET_LEFT_2 0xcd +#define PM860X_OFFSET_RIGHT_1 0xce +#define PM860X_OFFSET_RIGHT_2 0xcf +#define PM860X_ADC_ANA_1 0xd0 +#define PM860X_ADC_ANA_2 0xd1 +#define PM860X_ADC_ANA_3 0xd2 +#define PM860X_ADC_ANA_4 0xd3 +#define PM860X_ANA_TO_ANA 0xd4 +#define PM860X_HS1_CTRL 0xd5 +#define PM860X_HS2_CTRL 0xd6 +#define PM860X_LO1_CTRL 0xd7 +#define PM860X_LO2_CTRL 0xd8 +#define PM860X_EAR_CTRL_1 0xd9 +#define PM860X_EAR_CTRL_2 0xda +#define PM860X_AUDIO_SUPPLIES_1 0xdb +#define PM860X_AUDIO_SUPPLIES_2 0xdc +#define PM860X_ADC_EN_1 0xdd +#define PM860X_ADC_EN_2 0xde +#define PM860X_DAC_EN_1 0xdf +#define PM860X_DAC_EN_2 0xe1 +#define PM860X_AUDIO_CAL_1 0xe2 +#define PM860X_AUDIO_CAL_2 0xe3 +#define PM860X_AUDIO_CAL_3 0xe4 +#define PM860X_AUDIO_CAL_4 0xe5 +#define PM860X_AUDIO_CAL_5 0xe6 +#define PM860X_ANA_INPUT_SEL_1 0xe7 +#define PM860X_ANA_INPUT_SEL_2 0xe8 + +#define PM860X_PCM_IFACE_4 0xe9 +#define PM860X_I2S_IFACE_5 0xea + +#define PM860X_SHORTS 0x3b +#define PM860X_PLL_ADJ_1 0x3c +#define PM860X_PLL_ADJ_2 0x3d + +/* bits definition */ +#define PM860X_CLK_DIR_IN 0 +#define PM860X_CLK_DIR_OUT 1 + +#define PM860X_DET_HEADSET (1 << 0) +#define PM860X_DET_MIC (1 << 1) +#define PM860X_DET_HOOK (1 << 2) +#define PM860X_SHORT_HEADSET (1 << 3) +#define PM860X_SHORT_LINEOUT (1 << 4) +#define PM860X_DET_MASK 0x1F + +extern int pm860x_hs_jack_detect(struct snd_soc_component *, struct snd_soc_jack *, + int, int, int, int); +extern int pm860x_mic_jack_detect(struct snd_soc_component *, struct snd_soc_jack *, + int); + +#endif /* __88PM860X_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig new file mode 100644 index 0000000000..f1e1dbc509 --- /dev/null +++ b/sound/soc/codecs/Kconfig @@ -0,0 +1,2407 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Helper to resolve issues with configs that have SPI enabled but I2C +# modular, meaning we can't build the codec driver in with I2C support. +# We use an ordered list of conditional defaults to pick the appropriate +# setting - SPI can't be modular so that case doesn't need to be covered. +config SND_SOC_I2C_AND_SPI + tristate + default m if I2C=m + default y if I2C=y + default y if SPI_MASTER=y + +menu "CODEC drivers" + +config SND_SOC_ALL_CODECS + tristate "Build all ASoC CODEC drivers" + depends on COMPILE_TEST + imply SND_SOC_88PM860X + imply SND_SOC_AB8500_CODEC + imply SND_SOC_AC97_CODEC + imply SND_SOC_AD1836 + imply SND_SOC_AD193X_SPI + imply SND_SOC_AD193X_I2C + imply SND_SOC_AD1980 + imply SND_SOC_AD73311 + imply SND_SOC_ADAU1372_I2C + imply SND_SOC_ADAU1372_SPI + imply SND_SOC_ADAU1373 + imply SND_SOC_ADAU1761_I2C + imply SND_SOC_ADAU1761_SPI + imply SND_SOC_ADAU1781_I2C + imply SND_SOC_ADAU1781_SPI + imply SND_SOC_ADAV801 + imply SND_SOC_ADAV803 + imply SND_SOC_ADAU1977_SPI + imply SND_SOC_ADAU1977_I2C + imply SND_SOC_ADAU1701 + imply SND_SOC_ADAU7002 + imply SND_SOC_ADAU7118_I2C + imply SND_SOC_ADAU7118_HW + imply SND_SOC_ADS117X + imply SND_SOC_AK4104 + imply SND_SOC_AK4118 + imply SND_SOC_AK4375 + imply SND_SOC_AK4458 + imply SND_SOC_AK4535 + imply SND_SOC_AK4554 + imply SND_SOC_AK4613 + imply SND_SOC_AK4641 + imply SND_SOC_AK4642 + imply SND_SOC_AK4671 + imply SND_SOC_AK5386 + imply SND_SOC_AK5558 + imply SND_SOC_ALC5623 + imply SND_SOC_ALC5632 + imply SND_SOC_AUDIO_IIO_AUX + imply SND_SOC_AW8738 + imply SND_SOC_AW88395 + imply SND_SOC_AW88261 + imply SND_SOC_BT_SCO + imply SND_SOC_BD28623 + imply SND_SOC_CHV3_CODEC + imply SND_SOC_CQ0093VC + imply SND_SOC_CROS_EC_CODEC + imply SND_SOC_CS35L32 + imply SND_SOC_CS35L33 + imply SND_SOC_CS35L34 + imply SND_SOC_CS35L35 + imply SND_SOC_CS35L36 + imply SND_SOC_CS35L41_SPI + imply SND_SOC_CS35L41_I2C + imply SND_SOC_CS35L45_I2C + imply SND_SOC_CS35L45_SPI + imply SND_SOC_CS35L56_I2C + imply SND_SOC_CS35L56_SPI + imply SND_SOC_CS35L56_SDW + imply SND_SOC_CS42L42 + imply SND_SOC_CS42L42_SDW + imply SND_SOC_CS42L43 + imply SND_SOC_CS42L43_SDW + imply SND_SOC_CS42L51_I2C + imply SND_SOC_CS42L52 + imply SND_SOC_CS42L56 + imply SND_SOC_CS42L73 + imply SND_SOC_CS4234 + imply SND_SOC_CS4265 + imply SND_SOC_CS4270 + imply SND_SOC_CS4271_I2C + imply SND_SOC_CS4271_SPI + imply SND_SOC_CS42XX8_I2C + imply SND_SOC_CS43130 + imply SND_SOC_CS4341 + imply SND_SOC_CS4349 + imply SND_SOC_CS47L15 + imply SND_SOC_CS47L24 + imply SND_SOC_CS47L35 + imply SND_SOC_CS47L85 + imply SND_SOC_CS47L90 + imply SND_SOC_CS47L92 + imply SND_SOC_CS53L30 + imply SND_SOC_CX20442 + imply SND_SOC_CX2072X + imply SND_SOC_DA7210 + imply SND_SOC_DA7213 + imply SND_SOC_DA7218 + imply SND_SOC_DA7219 + imply SND_SOC_DA732X + imply SND_SOC_DA9055 + imply SND_SOC_DMIC + imply SND_SOC_ES8316 + imply SND_SOC_ES8326 + imply SND_SOC_ES8328_SPI + imply SND_SOC_ES8328_I2C + imply SND_SOC_ES7134 + imply SND_SOC_ES7241 + imply SND_SOC_GTM601 + imply SND_SOC_HDAC_HDMI + imply SND_SOC_HDAC_HDA + imply SND_SOC_ICS43432 + imply SND_SOC_IDT821034 + imply SND_SOC_INNO_RK3036 + imply SND_SOC_ISABELLE + imply SND_SOC_JZ4740_CODEC + imply SND_SOC_JZ4725B_CODEC + imply SND_SOC_JZ4760_CODEC + imply SND_SOC_JZ4770_CODEC + imply SND_SOC_LM4857 + imply SND_SOC_LM49453 + imply SND_SOC_LOCHNAGAR_SC + imply SND_SOC_MAX98088 + imply SND_SOC_MAX98090 + imply SND_SOC_MAX98095 + imply SND_SOC_MAX98357A + imply SND_SOC_MAX98371 + imply SND_SOC_MAX98504 + imply SND_SOC_MAX98520 + imply SND_SOC_MAX9867 + imply SND_SOC_MAX98925 + imply SND_SOC_MAX98926 + imply SND_SOC_MAX98927 + imply SND_SOC_MAX98363 + imply SND_SOC_MAX98373_I2C + imply SND_SOC_MAX98373_SDW + imply SND_SOC_MAX98388 + imply SND_SOC_MAX98390 + imply SND_SOC_MAX98396 + imply SND_SOC_MAX9850 + imply SND_SOC_MAX9860 + imply SND_SOC_MAX9759 + imply SND_SOC_MAX9768 + imply SND_SOC_MAX9877 + imply SND_SOC_MC13783 + imply SND_SOC_ML26124 + imply SND_SOC_MT6351 + imply SND_SOC_MT6358 + imply SND_SOC_MT6359 + imply SND_SOC_MT6660 + imply SND_SOC_NAU8315 + imply SND_SOC_NAU8540 + imply SND_SOC_NAU8810 + imply SND_SOC_NAU8821 + imply SND_SOC_NAU8822 + imply SND_SOC_NAU8824 + imply SND_SOC_NAU8825 + imply SND_SOC_HDMI_CODEC + imply SND_SOC_PCM1681 + imply SND_SOC_PCM1789_I2C + imply SND_SOC_PCM179X_I2C + imply SND_SOC_PCM179X_SPI + imply SND_SOC_PCM186X_I2C + imply SND_SOC_PCM186X_SPI + imply SND_SOC_PCM3008 + imply SND_SOC_PCM3060_I2C + imply SND_SOC_PCM3060_SPI + imply SND_SOC_PCM3168A_I2C + imply SND_SOC_PCM3168A_SPI + imply SND_SOC_PCM5102A + imply SND_SOC_PCM512x_I2C + imply SND_SOC_PCM512x_SPI + imply SND_SOC_PEB2466 + imply SND_SOC_RK3328 + imply SND_SOC_RK817 + imply SND_SOC_RT274 + imply SND_SOC_RT286 + imply SND_SOC_RT298 + imply SND_SOC_RT1011 + imply SND_SOC_RT1015 + imply SND_SOC_RT1015P + imply SND_SOC_RT1016 + imply SND_SOC_RT1017_SDCA_SDW + imply SND_SOC_RT1019 + imply SND_SOC_RT1305 + imply SND_SOC_RT1308 + imply SND_SOC_RT5514 + imply SND_SOC_RT5616 + imply SND_SOC_RT5631 + imply SND_SOC_RT5640 + imply SND_SOC_RT5645 + imply SND_SOC_RT5651 + imply SND_SOC_RT5659 + imply SND_SOC_RT5660 + imply SND_SOC_RT5663 + imply SND_SOC_RT5665 + imply SND_SOC_RT5668 + imply SND_SOC_RT5670 + imply SND_SOC_RT5677 + imply SND_SOC_RT5682_I2C + imply SND_SOC_RT5682_SDW + imply SND_SOC_RT5682S + imply SND_SOC_RT700_SDW + imply SND_SOC_RT711_SDW + imply SND_SOC_RT711_SDCA_SDW + imply SND_SOC_RT712_SDCA_SDW + imply SND_SOC_RT712_SDCA_DMIC_SDW + imply SND_SOC_RT715_SDW + imply SND_SOC_RT715_SDCA_SDW + imply SND_SOC_RT722_SDCA_SDW + imply SND_SOC_RT1308_SDW + imply SND_SOC_RT1316_SDW + imply SND_SOC_RT1318_SDW + imply SND_SOC_RT9120 + imply SND_SOC_SDW_MOCKUP + imply SND_SOC_SGTL5000 + imply SND_SOC_SI476X + imply SND_SOC_SIMPLE_AMPLIFIER + imply SND_SOC_SIMPLE_MUX + imply SND_SOC_SMA1303 + imply SND_SOC_SPDIF + imply SND_SOC_SRC4XXX_I2C + imply SND_SOC_SSM2305 + imply SND_SOC_SSM2518 + imply SND_SOC_SSM2602_SPI + imply SND_SOC_SSM2602_I2C + imply SND_SOC_SSM4567 + imply SND_SOC_STA32X + imply SND_SOC_STA350 + imply SND_SOC_STA529 + imply SND_SOC_STAC9766 + imply SND_SOC_STI_SAS + imply SND_SOC_TAS2552 + imply SND_SOC_TAS2562 + imply SND_SOC_TAS2764 + imply SND_SOC_TAS2770 + imply SND_SOC_TAS2780 + imply SND_SOC_TAS2781_COMLIB + imply SND_SOC_TAS2781_FMWLIB + imply SND_SOC_TAS2781_I2C + imply SND_SOC_TAS5086 + imply SND_SOC_TAS571X + imply SND_SOC_TAS5720 + imply SND_SOC_TAS6424 + imply SND_SOC_TDA7419 + imply SND_SOC_TFA9879 + imply SND_SOC_TFA989X + imply SND_SOC_TLV320ADC3XXX + imply SND_SOC_TLV320ADCX140 + imply SND_SOC_TLV320AIC23_I2C + imply SND_SOC_TLV320AIC23_SPI + imply SND_SOC_TLV320AIC26 + imply SND_SOC_TLV320AIC31XX + imply SND_SOC_TLV320AIC32X4_I2C + imply SND_SOC_TLV320AIC32X4_SPI + imply SND_SOC_TLV320AIC3X_I2C + imply SND_SOC_TLV320AIC3X_SPI + imply SND_SOC_TPA6130A2 + imply SND_SOC_TLV320DAC33 + imply SND_SOC_TSCS42XX + imply SND_SOC_TSCS454 + imply SND_SOC_TS3A227E + imply SND_SOC_TWL4030 + imply SND_SOC_TWL6040 + imply SND_SOC_UDA1334 + imply SND_SOC_UDA1380 + imply SND_SOC_WCD9335 + imply SND_SOC_WCD934X + imply SND_SOC_WCD938X_SDW + imply SND_SOC_LPASS_MACRO_COMMON + imply SND_SOC_LPASS_RX_MACRO + imply SND_SOC_LPASS_TX_MACRO + imply SND_SOC_WL1273 + imply SND_SOC_WM0010 + imply SND_SOC_WM1250_EV1 + imply SND_SOC_WM2000 + imply SND_SOC_WM2200 + imply SND_SOC_WM5100 + imply SND_SOC_WM5102 + imply SND_SOC_WM5110 + imply SND_SOC_WM8350 + imply SND_SOC_WM8400 + imply SND_SOC_WM8510 + imply SND_SOC_WM8523 + imply SND_SOC_WM8524 + imply SND_SOC_WM8580 + imply SND_SOC_WM8711 + imply SND_SOC_WM8727 + imply SND_SOC_WM8728 + imply SND_SOC_WM8731_I2C + imply SND_SOC_WM8731_SPI + imply SND_SOC_WM8737 + imply SND_SOC_WM8741 + imply SND_SOC_WM8750 + imply SND_SOC_WM8753 + imply SND_SOC_WM8770 + imply SND_SOC_WM8776 + imply SND_SOC_WM8782 + imply SND_SOC_WM8804_I2C + imply SND_SOC_WM8804_SPI + imply SND_SOC_WM8900 + imply SND_SOC_WM8903 + imply SND_SOC_WM8904 + imply SND_SOC_WM8940 + imply SND_SOC_WM8955 + imply SND_SOC_WM8960 + imply SND_SOC_WM8961 + imply SND_SOC_WM8962 + imply SND_SOC_WM8971 + imply SND_SOC_WM8974 + imply SND_SOC_WM8978 + imply SND_SOC_WM8983 + imply SND_SOC_WM8985 + imply SND_SOC_WM8988 + imply SND_SOC_WM8990 + imply SND_SOC_WM8991 + imply SND_SOC_WM8993 + imply SND_SOC_WM8994 + imply SND_SOC_WM8995 + imply SND_SOC_WM8996 + imply SND_SOC_WM8997 + imply SND_SOC_WM8998 + imply SND_SOC_WM9081 + imply SND_SOC_WM9090 + imply SND_SOC_WM9705 + imply SND_SOC_WM9712 + imply SND_SOC_WM9713 + imply SND_SOC_WSA881X + imply SND_SOC_WSA883X + imply SND_SOC_WSA884X + imply SND_SOC_ZL38060 + help + Normally ASoC codec drivers are only built if a machine driver which + uses them is also built since they are only usable with a machine + driver. Selecting this option will allow these drivers to be built + without an explicit machine driver for test and development purposes. + + Support for the bus types used to access the codecs to be built must + be selected separately. + + If unsure select "N". + +config SND_SOC_88PM860X + tristate + depends on MFD_88PM860X + +config SND_SOC_ARIZONA + tristate + default y if SND_SOC_CS47L24=y + default y if SND_SOC_WM5102=y + default y if SND_SOC_WM5110=y + default y if SND_SOC_WM8997=y + default y if SND_SOC_WM8998=y + default m if SND_SOC_CS47L24=m + default m if SND_SOC_WM5102=m + default m if SND_SOC_WM5110=m + default m if SND_SOC_WM8997=m + default m if SND_SOC_WM8998=m + +config SND_SOC_WM_HUBS + tristate + default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y + default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m + +config SND_SOC_WM_ADSP + tristate + select FW_CS_DSP + select SND_SOC_COMPRESS + default y if SND_SOC_MADERA=y + default y if SND_SOC_CS47L24=y + default y if SND_SOC_WM5102=y + default y if SND_SOC_WM5110=y + default y if SND_SOC_WM2200=y + default y if SND_SOC_CS35L41_SPI=y + default y if SND_SOC_CS35L41_I2C=y + default y if SND_SOC_CS35L45_SPI=y + default y if SND_SOC_CS35L45_I2C=y + default y if SND_SOC_CS35L56=y + default m if SND_SOC_MADERA=m + default m if SND_SOC_CS47L24=m + default m if SND_SOC_WM5102=m + default m if SND_SOC_WM5110=m + default m if SND_SOC_WM2200=m + default m if SND_SOC_CS35L41_SPI=m + default m if SND_SOC_CS35L41_I2C=m + default m if SND_SOC_CS35L45_SPI=m + default m if SND_SOC_CS35L45_I2C=m + default m if SND_SOC_CS35L56=m + +config SND_SOC_AB8500_CODEC + tristate + depends on ABX500_CORE + +config SND_SOC_AC97_CODEC + tristate "Build generic ASoC AC97 CODEC driver" + select SND_AC97_CODEC + select SND_SOC_AC97_BUS + +config SND_SOC_AD1836 + tristate + depends on SPI_MASTER + +config SND_SOC_AD193X + tristate + +config SND_SOC_AD193X_SPI + tristate + depends on SPI_MASTER + select SND_SOC_AD193X + +config SND_SOC_AD193X_I2C + tristate + depends on I2C + select SND_SOC_AD193X + +config SND_SOC_AD1980 + tristate + depends on SND_SOC_AC97_BUS + select REGMAP_AC97 + +config SND_SOC_AD73311 + tristate + +config SND_SOC_ADAU_UTILS + tristate + +config SND_SOC_ADAU1372 + tristate + select SND_SOC_ADAU_UTILS + +config SND_SOC_ADAU1372_I2C + tristate "Analog Devices ADAU1372 CODEC (I2C)" + depends on I2C + select SND_SOC_ADAU1372 + select REGMAP_I2C + +config SND_SOC_ADAU1372_SPI + tristate "Analog Devices ADAU1372 CODEC (SPI)" + depends on SPI + select SND_SOC_ADAU1372 + select REGMAP_SPI + +config SND_SOC_ADAU1373 + tristate + depends on I2C + select SND_SOC_ADAU_UTILS + +config SND_SOC_ADAU1701 + tristate "Analog Devices ADAU1701 CODEC" + depends on I2C + select SND_SOC_SIGMADSP_I2C + +config SND_SOC_ADAU17X1 + tristate + select SND_SOC_SIGMADSP_REGMAP + select SND_SOC_ADAU_UTILS + +config SND_SOC_ADAU1761 + tristate + select SND_SOC_ADAU17X1 + +config SND_SOC_ADAU1761_I2C + tristate "Analog Devices AU1761 CODEC - I2C" + depends on I2C + select SND_SOC_ADAU1761 + select REGMAP_I2C + +config SND_SOC_ADAU1761_SPI + tristate "Analog Devices AU1761 CODEC - SPI" + depends on SPI + select SND_SOC_ADAU1761 + select REGMAP_SPI + +config SND_SOC_ADAU1781 + select SND_SOC_ADAU17X1 + tristate + +config SND_SOC_ADAU1781_I2C + tristate + depends on I2C + select SND_SOC_ADAU1781 + select REGMAP_I2C + +config SND_SOC_ADAU1781_SPI + tristate + depends on SPI_MASTER + select SND_SOC_ADAU1781 + select REGMAP_SPI + +config SND_SOC_ADAU1977 + tristate + +config SND_SOC_ADAU1977_SPI + tristate + depends on SPI_MASTER + select SND_SOC_ADAU1977 + select REGMAP_SPI + +config SND_SOC_ADAU1977_I2C + tristate + depends on I2C + select SND_SOC_ADAU1977 + select REGMAP_I2C + +config SND_SOC_ADAU7002 + tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter" + +config SND_SOC_ADAU7118 + tristate + +config SND_SOC_ADAU7118_HW + tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - HW Mode" + select SND_SOC_ADAU7118 + help + Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM + Converter. In this mode, the device works in standalone mode which + means that there is no bus to communicate with it. Stereo mode is not + supported in this mode. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-adau7118-hw. + +config SND_SOC_ADAU7118_I2C + tristate "Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM Converter - I2C" + depends on I2C + select SND_SOC_ADAU7118 + select REGMAP_I2C + help + Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM + Converter over I2C. This gives full support over the device. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-adau7118-i2c. + +config SND_SOC_ADAV80X + tristate + +config SND_SOC_ADAV801 + tristate + depends on SPI_MASTER + select SND_SOC_ADAV80X + +config SND_SOC_ADAV803 + tristate + depends on I2C + select SND_SOC_ADAV80X + +config SND_SOC_ADS117X + tristate + +config SND_SOC_AK4104 + tristate "AKM AK4104 CODEC" + depends on SPI_MASTER + +config SND_SOC_AK4118 + tristate "AKM AK4118 CODEC" + depends on I2C + select REGMAP_I2C + +config SND_SOC_AK4375 + tristate "AKM AK4375 CODEC" + depends on I2C + select REGMAP_I2C + help + Enable support for the Asahi-Kasei AK4375 codec. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-ak4375. + +config SND_SOC_AK4458 + tristate "AKM AK4458 CODEC" + depends on I2C + select REGMAP_I2C + +config SND_SOC_AK4535 + tristate + depends on I2C + +config SND_SOC_AK4554 + tristate "AKM AK4554 CODEC" + +config SND_SOC_AK4613 + tristate "AKM AK4613 CODEC" + depends on I2C + +config SND_SOC_AK4641 + tristate + depends on I2C + +config SND_SOC_AK4642 + tristate "AKM AK4642 CODEC" + depends on I2C + +config SND_SOC_AK4671 + tristate + depends on I2C + +config SND_SOC_AK5386 + tristate "AKM AK5638 CODEC" + +config SND_SOC_AK5558 + tristate "AKM AK5558 CODEC" + depends on I2C + select REGMAP_I2C + +config SND_SOC_ALC5623 + tristate "Realtek ALC5623 CODEC" + depends on I2C + +config SND_SOC_ALC5632 + tristate + depends on I2C + +config SND_SOC_AUDIO_IIO_AUX + tristate "Audio IIO Auxiliary device" + depends on IIO + help + Enable support for Industrial I/O devices as audio auxiliary devices. + This allows to have an IIO device present in the audio path and + controlled using mixer controls. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-audio-iio-aux. + +config SND_SOC_AW8738 + tristate "Awinic AW8738 Audio Amplifier" + select GPIOLIB + help + Enable support for the Awinic AW8738 audio amplifier (or similar). + The driver supports simple audio amplifiers similar to + SND_SOC_SIMPLE_AMPLIFIER, but additionally allows setting the + operation mode using the Awinic-specific one-wire pulse control. + +config SND_SOC_AW88395_LIB + tristate + +config SND_SOC_AW88395 + tristate "Soc Audio for awinic aw88395" + depends on I2C + select CRC8 + select CRC32 + select REGMAP_I2C + select GPIOLIB + select SND_SOC_AW88395_LIB + help + this option enables support for aw88395 Smart PA. + The Awinic AW88395 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier with an integrated 10V + smart boost convert. + +config SND_SOC_AW88261 + tristate "Soc Audio for awinic aw88261" + depends on I2C + select CRC8 + select REGMAP_I2C + select GPIOLIB + select SND_SOC_AW88395_LIB + help + This option enables support for aw88261 Smart PA. + The awinic AW88261 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier. The output voltage of + boost converter can be adjusted smartly according to + the input amplitude. + +config SND_SOC_BD28623 + tristate "ROHM BD28623 CODEC" + help + Enable support for ROHM BD28623MUV Class D speaker amplifier. + This codec does not have any control buses such as I2C, it + detect format of I2S automatically. + +config SND_SOC_BT_SCO + tristate "Dummy BT SCO codec driver" + +config SND_SOC_CHV3_CODEC + tristate "Google Chameleon v3 codec driver" + help + Enable support for the Google Chameleon v3 audio codec. + This codec does not have a control interface, it always outputs + 8 channel S32_LE audio. + +config SND_SOC_CPCAP + tristate "Motorola CPCAP codec" + depends on MFD_CPCAP || COMPILE_TEST + +config SND_SOC_CQ0093VC + tristate + +config SND_SOC_CROS_EC_CODEC + tristate "codec driver for ChromeOS EC" + depends on CROS_EC + select CRYPTO + select CRYPTO_LIB_SHA256 + help + If you say yes here you will get support for the + ChromeOS Embedded Controller's Audio Codec. + +config SND_SOC_CS35L32 + tristate "Cirrus Logic CS35L32 CODEC" + depends on I2C + +config SND_SOC_CS35L33 + tristate "Cirrus Logic CS35L33 CODEC" + depends on I2C + +config SND_SOC_CS35L34 + tristate "Cirrus Logic CS35L34 CODEC" + depends on I2C + +config SND_SOC_CS35L35 + tristate "Cirrus Logic CS35L35 CODEC" + depends on I2C + +config SND_SOC_CS35L36 + tristate "Cirrus Logic CS35L36 CODEC" + depends on I2C + +config SND_SOC_CS35L41_LIB + tristate + +config SND_SOC_CS35L41 + tristate + +config SND_SOC_CS35L41_SPI + tristate "Cirrus Logic CS35L41 CODEC (SPI)" + depends on SPI_MASTER + select SND_SOC_CS35L41_LIB + select SND_SOC_CS35L41 + select REGMAP_SPI + +config SND_SOC_CS35L41_I2C + tristate "Cirrus Logic CS35L41 CODEC (I2C)" + depends on I2C + select SND_SOC_CS35L41_LIB + select SND_SOC_CS35L41 + select REGMAP_I2C + +config SND_SOC_CS35L45 + tristate + select REGMAP_IRQ + +config SND_SOC_CS35L45_SPI + tristate "Cirrus Logic CS35L45 CODEC (SPI)" + depends on SPI_MASTER + select REGMAP + select REGMAP_SPI + select SND_SOC_CS35L45 + help + Enable support for Cirrus Logic CS35L45 smart speaker amplifier + with SPI control. + +config SND_SOC_CS35L45_I2C + tristate "Cirrus Logic CS35L45 CODEC (I2C)" + depends on I2C + select REGMAP + select REGMAP_I2C + select SND_SOC_CS35L45 + help + Enable support for Cirrus Logic CS35L45 smart speaker amplifier + with I2C control. + +config SND_SOC_CS35L56 + tristate + +config SND_SOC_CS35L56_SHARED + tristate + +config SND_SOC_CS35L56_I2C + tristate "Cirrus Logic CS35L56 CODEC (I2C)" + depends on I2C + depends on SOUNDWIRE || !SOUNDWIRE + select REGMAP_I2C + select SND_SOC_CS35L56 + select SND_SOC_CS35L56_SHARED + help + Enable support for Cirrus Logic CS35L56 boosted amplifier with I2C control + +config SND_SOC_CS35L56_SPI + tristate "Cirrus Logic CS35L56 CODEC (SPI)" + depends on SPI_MASTER + depends on SOUNDWIRE || !SOUNDWIRE + select REGMAP_SPI + select SND_SOC_CS35L56 + select SND_SOC_CS35L56_SHARED + help + Enable support for Cirrus Logic CS35L56 boosted amplifier with SPI control + +config SND_SOC_CS35L56_SDW + tristate "Cirrus Logic CS35L56 CODEC (SDW)" + depends on SOUNDWIRE + select REGMAP + select SND_SOC_CS35L56 + select SND_SOC_CS35L56_SHARED + help + Enable support for Cirrus Logic CS35L56 boosted amplifier with SoundWire control + +config SND_SOC_CS42L42_CORE + tristate + +config SND_SOC_CS42L42 + tristate "Cirrus Logic CS42L42 CODEC (I2C)" + depends on I2C + select REGMAP + select REGMAP_I2C + select SND_SOC_CS42L42_CORE + +config SND_SOC_CS42L42_SDW + tristate "Cirrus Logic CS42L42 CODEC on Soundwire" + depends on SOUNDWIRE + select SND_SOC_CS42L42_CORE + help + Enable support for Cirrus Logic CS42L42 codec with Soundwire control + +config SND_SOC_CS42L43 + tristate "Cirrus Logic CS42L43 CODEC" + depends on MFD_CS42L43 + help + Select this to support the audio functions of the Cirrus Logic + CS42L43 PC CODEC. + +config SND_SOC_CS42L43_SDW + tristate "Cirrus Logic CS42L43 CODEC (SoundWire)" + depends on SND_SOC_CS42L43 && MFD_CS42L43_SDW + help + Select this to support the audio functions of the Cirrus Logic + CS42L43 PC CODEC over SoundWire. + +config SND_SOC_CS42L51 + tristate + +config SND_SOC_CS42L51_I2C + tristate "Cirrus Logic CS42L51 CODEC (I2C)" + depends on I2C + select SND_SOC_CS42L51 + +config SND_SOC_CS42L52 + tristate "Cirrus Logic CS42L52 CODEC" + depends on I2C && INPUT + +config SND_SOC_CS42L56 + tristate "Cirrus Logic CS42L56 CODEC" + depends on I2C && INPUT + +config SND_SOC_CS42L73 + tristate "Cirrus Logic CS42L73 CODEC" + depends on I2C + +config SND_SOC_CS42L83 + tristate "Cirrus Logic CS42L83 CODEC" + depends on I2C + select REGMAP + select REGMAP_I2C + select SND_SOC_CS42L42_CORE + +config SND_SOC_CS4234 + tristate "Cirrus Logic CS4234 CODEC" + depends on I2C + select REGMAP_I2C + +config SND_SOC_CS4265 + tristate "Cirrus Logic CS4265 CODEC" + depends on I2C + select REGMAP_I2C + +# Cirrus Logic CS4270 Codec +config SND_SOC_CS4270 + tristate "Cirrus Logic CS4270 CODEC" + depends on I2C + +# Cirrus Logic CS4270 Codec VD = 3.3V Errata +# Select if you are affected by the errata where the part will not function +# if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will +# not select any sample rates that require MCLK to be divided by 1.5. +config SND_SOC_CS4270_VD33_ERRATA + bool + depends on SND_SOC_CS4270 + +config SND_SOC_CS4271 + tristate + +config SND_SOC_CS4271_I2C + tristate "Cirrus Logic CS4271 CODEC (I2C)" + depends on I2C + select SND_SOC_CS4271 + select REGMAP_I2C + +config SND_SOC_CS4271_SPI + tristate "Cirrus Logic CS4271 CODEC (SPI)" + depends on SPI_MASTER + select SND_SOC_CS4271 + select REGMAP_SPI + +config SND_SOC_CS42XX8 + tristate + +config SND_SOC_CS42XX8_I2C + tristate "Cirrus Logic CS42448/CS42888 CODEC (I2C)" + depends on I2C + select SND_SOC_CS42XX8 + select REGMAP_I2C + +# Cirrus Logic CS43130 HiFi DAC +config SND_SOC_CS43130 + tristate "Cirrus Logic CS43130 CODEC" + depends on I2C + +config SND_SOC_CS4341 + tristate "Cirrus Logic CS4341 CODEC" + depends on SND_SOC_I2C_AND_SPI + select REGMAP_I2C if I2C + select REGMAP_SPI if SPI_MASTER + +# Cirrus Logic CS4349 HiFi DAC +config SND_SOC_CS4349 + tristate "Cirrus Logic CS4349 CODEC" + depends on I2C + +config SND_SOC_CS47L15 + tristate + depends on MFD_CS47L15 + +config SND_SOC_CS47L24 + tristate + depends on MFD_CS47L24 && MFD_ARIZONA + +config SND_SOC_CS47L35 + tristate + depends on MFD_CS47L35 + +config SND_SOC_CS47L85 + tristate + depends on MFD_CS47L85 + +config SND_SOC_CS47L90 + tristate + depends on MFD_CS47L90 + +config SND_SOC_CS47L92 + tristate + depends on MFD_CS47L92 + +# Cirrus Logic Quad-Channel ADC +config SND_SOC_CS53L30 + tristate "Cirrus Logic CS53L30 CODEC" + depends on I2C + +config SND_SOC_CX20442 + tristate + depends on TTY + +config SND_SOC_CX2072X + tristate "Conexant CX2072X CODEC" + depends on I2C + help + Enable support for Conexant CX20721 and CX20723 codec chips. + +config SND_SOC_JZ4740_CODEC + depends on MACH_INGENIC || COMPILE_TEST + depends on OF + select REGMAP_MMIO + tristate "Ingenic JZ4740 internal CODEC" + help + Enable support for the internal CODEC found in the JZ4740 SoC + from Ingenic. + + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4740-codec. + +config SND_SOC_JZ4725B_CODEC + depends on MACH_INGENIC || COMPILE_TEST + depends on OF + select REGMAP + tristate "Ingenic JZ4725B internal CODEC" + help + Enable support for the internal CODEC found in the JZ4725B SoC + from Ingenic. + + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4725b-codec. + +config SND_SOC_JZ4760_CODEC + depends on MACH_INGENIC || COMPILE_TEST + depends on OF + select REGMAP + tristate "Ingenic JZ4760 internal CODEC" + help + Enable support for the internal CODEC found in the JZ4760 SoC + from Ingenic. + + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4760-codec. + +config SND_SOC_JZ4770_CODEC + depends on MACH_INGENIC || COMPILE_TEST + depends on OF + select REGMAP + tristate "Ingenic JZ4770 internal CODEC" + help + Enable support for the internal CODEC found in the JZ4770 SoC + from Ingenic. + + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4770-codec. + +config SND_SOC_DA7210 + tristate + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_DA7213 + tristate "Dialog DA7213 CODEC" + depends on I2C + +config SND_SOC_DA7218 + tristate + depends on I2C + +config SND_SOC_DA7219 + tristate + depends on I2C + +config SND_SOC_DA732X + tristate + depends on I2C + +config SND_SOC_DA9055 + tristate + depends on I2C + +config SND_SOC_DMIC + tristate "Generic Digital Microphone CODEC" + help + Enable support for the Generic Digital Microphone CODEC. + Select this if your sound card has DMICs. + +config SND_SOC_HDMI_CODEC + tristate + select SND_PCM_ELD + select SND_PCM_IEC958 + select HDMI + +config SND_SOC_ES7134 + tristate "Everest Semi ES7134 CODEC" + +config SND_SOC_ES7241 + tristate "Everest Semi ES7241 CODEC" + +config SND_SOC_ES8316 + tristate "Everest Semi ES8316 CODEC" + depends on I2C + +config SND_SOC_ES8326 + tristate "Everest Semi ES8326 CODEC" + depends on I2C + +config SND_SOC_ES8328 + tristate + +config SND_SOC_ES8328_I2C + tristate "Everest Semi ES8328 CODEC (I2C)" + depends on I2C + select SND_SOC_ES8328 + +config SND_SOC_ES8328_SPI + tristate "Everest Semi ES8328 CODEC (SPI)" + depends on SPI_MASTER + select SND_SOC_ES8328 + +config SND_SOC_GTM601 + tristate 'GTM601 UMTS modem audio codec' + +config SND_SOC_HDAC_HDMI + tristate + select SND_HDA_EXT_CORE + select SND_PCM_ELD + select HDMI + +config SND_SOC_HDAC_HDA + tristate + select SND_HDA + +config SND_SOC_HDA + tristate "HD-Audio codec driver" + select SND_HDA_EXT_CORE + select SND_HDA + help + This enables HD-Audio codec support in ASoC subsystem. Compared + to SND_SOC_HDAC_HDA, driver's behavior is identical to HD-Audio + legacy solution - including the dynamic resource allocation + based on actual codec capabilities. + +config SND_SOC_ICS43432 + tristate "ICS43423 and compatible i2s microphones" + +config SND_SOC_IDT821034 + tristate "Renesas IDT821034 quad PCM codec" + depends on SPI + help + Enable support for the Renesas IDT821034 quad PCM with + programmable gain codec. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-idt821034. + +config SND_SOC_INNO_RK3036 + tristate "Inno codec driver for RK3036 SoC" + select REGMAP_MMIO + +config SND_SOC_ISABELLE + tristate + depends on I2C + +config SND_SOC_LM49453 + tristate + depends on I2C + +config SND_SOC_LOCHNAGAR_SC + tristate "Lochnagar Sound Card" + depends on MFD_LOCHNAGAR || COMPILE_TEST + help + This driver support the sound card functionality of the Cirrus + Logic Lochnagar audio development board. + +config SND_SOC_MADERA + tristate + default y if SND_SOC_CS47L15=y + default y if SND_SOC_CS47L35=y + default y if SND_SOC_CS47L85=y + default y if SND_SOC_CS47L90=y + default y if SND_SOC_CS47L92=y + default m if SND_SOC_CS47L15=m + default m if SND_SOC_CS47L35=m + default m if SND_SOC_CS47L85=m + default m if SND_SOC_CS47L90=m + default m if SND_SOC_CS47L92=m + +config SND_SOC_MAX98088 + tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec" + depends on I2C + +config SND_SOC_MAX98090 + tristate "Maxim MAX98090 CODEC" + depends on I2C + +config SND_SOC_MAX98095 + tristate + depends on I2C + +config SND_SOC_MAX98357A + tristate "Maxim MAX98357A CODEC" + +config SND_SOC_MAX98371 + tristate + depends on I2C + +config SND_SOC_MAX98504 + tristate "Maxim MAX98504 speaker amplifier" + depends on I2C + +config SND_SOC_MAX9867 + tristate "Maxim MAX9867 CODEC" + depends on I2C + +config SND_SOC_MAX98925 + tristate + depends on I2C + +config SND_SOC_MAX98926 + tristate + depends on I2C + +config SND_SOC_MAX98927 + tristate "Maxim Integrated MAX98927 Speaker Amplifier" + depends on I2C + +config SND_SOC_MAX98520 + tristate "Maxim Integrated MAX98520 Speaker Amplifier" + depends on I2C + help + Enable support for Maxim Integrated MAX98520 audio + amplifier, which implements a tripler charge pump + based boost converter and supports sample rates of + 8KHz to 192KHz. + + To compile this driver as a module, choose M here. + +config SND_SOC_MAX98363 + tristate "Analog Devices MAX98363 Soundwire Speaker Amplifier" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + help + Enable support for Analog Devices MAX98363 Soundwire + amplifier. MAX98363 supports the MIPI SoundWire v1.2 + compatible interface for audio and control data. + This amplifier does not support I2C and I2S. + +config SND_SOC_MAX98373 + tristate + +config SND_SOC_MAX98373_I2C + tristate "Maxim Integrated MAX98373 Speaker Amplifier" + depends on I2C + select SND_SOC_MAX98373 + +config SND_SOC_MAX98373_SDW + tristate "Maxim Integrated MAX98373 Speaker Amplifier - SDW" + depends on SOUNDWIRE + select SND_SOC_MAX98373 + select REGMAP_SOUNDWIRE + help + Enable support for Maxim Integrated MAX98373 Soundwire + amplifier. MAX98373 supports either the MIPI SoundWire + compatible interface for audio and control data, or + the PCM interface for audio data and a standard I2C + interface for control data. Select this if MAX98373 is + connected via soundwire. + +config SND_SOC_MAX98388 + tristate "Analog Devices MAX98388 Speaker Amplifier" + depends on I2C + help + Enable support for Analog Devices MAX98388 audio + amplifier. The device provides a PCM interface for + audio data and a standard I2C interface for control + data communication. + +config SND_SOC_MAX98390 + tristate "Maxim Integrated MAX98390 Speaker Amplifier" + depends on I2C + +config SND_SOC_MAX98396 + tristate "Analog Devices MAX98396 Speaker Amplifier" + depends on I2C + help + Enable support for Analog Devices MAX98396 audio + amplifier. The device provides a PCM interface for + audio data and a standard I2C interface for control + data communication. + +config SND_SOC_MAX9850 + tristate + depends on I2C + +config SND_SOC_MAX9860 + tristate "Maxim MAX9860 Mono Audio Voice Codec" + depends on I2C + select REGMAP_I2C + +config SND_SOC_MSM8916_WCD_ANALOG + tristate "Qualcomm MSM8916 WCD Analog Codec" + depends on SPMI || COMPILE_TEST + +config SND_SOC_MSM8916_WCD_DIGITAL + tristate "Qualcomm MSM8916 WCD DIGITAL Codec" + select REGMAP_MMIO + +config SND_SOC_PCM1681 + tristate "Texas Instruments PCM1681 CODEC" + depends on I2C + +config SND_SOC_PCM1789 + tristate + +config SND_SOC_PCM1789_I2C + tristate "Texas Instruments PCM1789 CODEC (I2C)" + depends on I2C + select SND_SOC_PCM1789 + help + Enable support for Texas Instruments PCM1789 CODEC. + Select this if your PCM1789 is connected via an I2C bus. + +config SND_SOC_PCM179X + tristate + +config SND_SOC_PCM179X_I2C + tristate "Texas Instruments PCM179X CODEC (I2C)" + depends on I2C + select SND_SOC_PCM179X + help + Enable support for Texas Instruments PCM179x CODEC. + Select this if your PCM179x is connected via an I2C bus. + +config SND_SOC_PCM179X_SPI + tristate "Texas Instruments PCM179X CODEC (SPI)" + depends on SPI_MASTER + select SND_SOC_PCM179X + help + Enable support for Texas Instruments PCM179x CODEC. + Select this if your PCM179x is connected via an SPI bus. + +config SND_SOC_PCM186X + tristate + +config SND_SOC_PCM186X_I2C + tristate "Texas Instruments PCM186x CODECs - I2C" + depends on I2C + select SND_SOC_PCM186X + select REGMAP_I2C + +config SND_SOC_PCM186X_SPI + tristate "Texas Instruments PCM186x CODECs - SPI" + depends on SPI_MASTER + select SND_SOC_PCM186X + select REGMAP_SPI + +config SND_SOC_PCM3008 + tristate + +config SND_SOC_PCM3060 + tristate + +config SND_SOC_PCM3060_I2C + tristate "Texas Instruments PCM3060 CODEC - I2C" + depends on I2C + select SND_SOC_PCM3060 + select REGMAP_I2C + +config SND_SOC_PCM3060_SPI + tristate "Texas Instruments PCM3060 CODEC - SPI" + depends on SPI_MASTER + select SND_SOC_PCM3060 + select REGMAP_SPI + +config SND_SOC_PCM3168A + tristate + +config SND_SOC_PCM3168A_I2C + tristate "Texas Instruments PCM3168A CODEC - I2C" + depends on I2C + select SND_SOC_PCM3168A + select REGMAP_I2C + +config SND_SOC_PCM3168A_SPI + tristate "Texas Instruments PCM3168A CODEC - SPI" + depends on SPI_MASTER + select SND_SOC_PCM3168A + select REGMAP_SPI + +config SND_SOC_PCM5102A + tristate "Texas Instruments PCM5102A CODEC" + +config SND_SOC_PCM512x + tristate + +config SND_SOC_PCM512x_I2C + tristate "Texas Instruments PCM512x CODECs - I2C" + depends on I2C + select SND_SOC_PCM512x + select REGMAP_I2C + +config SND_SOC_PCM512x_SPI + tristate "Texas Instruments PCM512x CODECs - SPI" + depends on SPI_MASTER + select SND_SOC_PCM512x + select REGMAP_SPI + +config SND_SOC_PEB2466 + tristate "Infineon PEB2466 quad PCM codec" + depends on SPI + select REGMAP_SPI + help + Enable support for the Infineon PEB2466 quad PCM codec, + also named SICOFI 4-uC. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-peb2466. + +config SND_SOC_RK3328 + tristate "Rockchip RK3328 audio CODEC" + select REGMAP_MMIO + +config SND_SOC_RK817 + tristate "Rockchip RK817 audio CODEC" + depends on MFD_RK8XX || COMPILE_TEST + +config SND_SOC_RL6231 + tristate + default y if SND_SOC_RT5514=y + default y if SND_SOC_RT5616=y + default y if SND_SOC_RT5640=y + default y if SND_SOC_RT5645=y + default y if SND_SOC_RT5651=y + default y if SND_SOC_RT5659=y + default y if SND_SOC_RT5660=y + default y if SND_SOC_RT5663=y + default y if SND_SOC_RT5665=y + default y if SND_SOC_RT5668=y + default y if SND_SOC_RT5670=y + default y if SND_SOC_RT5677=y + default y if SND_SOC_RT5682=y + default y if SND_SOC_RT1011=y + default y if SND_SOC_RT1015=y + default y if SND_SOC_RT1015P=y + default y if SND_SOC_RT1019=y + default y if SND_SOC_RT1305=y + default y if SND_SOC_RT1308=y + default m if SND_SOC_RT5514=m + default m if SND_SOC_RT5616=m + default m if SND_SOC_RT5640=m + default m if SND_SOC_RT5645=m + default m if SND_SOC_RT5651=m + default m if SND_SOC_RT5659=m + default m if SND_SOC_RT5660=m + default m if SND_SOC_RT5663=m + default m if SND_SOC_RT5665=m + default m if SND_SOC_RT5668=m + default m if SND_SOC_RT5670=m + default m if SND_SOC_RT5677=m + default m if SND_SOC_RT5682=m + default m if SND_SOC_RT1011=m + default m if SND_SOC_RT1015=m + default m if SND_SOC_RT1015P=m + default m if SND_SOC_RT1019=m + default m if SND_SOC_RT1305=m + default m if SND_SOC_RT1308=m + +config SND_SOC_RL6347A + tristate + default y if SND_SOC_RT274=y + default y if SND_SOC_RT286=y + default y if SND_SOC_RT298=y + default m if SND_SOC_RT274=m + default m if SND_SOC_RT286=m + default m if SND_SOC_RT298=m + +config SND_SOC_RT274 + tristate + depends on I2C + +config SND_SOC_RT286 + tristate + depends on I2C + +config SND_SOC_RT298 + tristate + depends on I2C + +config SND_SOC_RT1011 + tristate + depends on I2C + +config SND_SOC_RT1015 + tristate + depends on I2C + +config SND_SOC_RT1015P + tristate + +config SND_SOC_RT1016 + tristate + depends on I2C + +config SND_SOC_RT1017_SDCA_SDW + tristate "Realtek RT1017 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + +config SND_SOC_RT1019 + tristate + depends on I2C + +config SND_SOC_RT1305 + tristate + depends on I2C + +config SND_SOC_RT1308 + tristate + depends on I2C + +config SND_SOC_RT1308_SDW + tristate "Realtek RT1308 Codec - SDW" + depends on I2C && SOUNDWIRE + select REGMAP_SOUNDWIRE + +config SND_SOC_RT1316_SDW + tristate "Realtek RT1316 Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + +config SND_SOC_RT1318_SDW + tristate "Realtek RT1318 Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + +config SND_SOC_RT5514 + tristate + depends on I2C + +config SND_SOC_RT5514_SPI + tristate + depends on SPI_MASTER + +config SND_SOC_RT5514_SPI_BUILTIN + bool # force RT5514_SPI to be built-in to avoid link errors + default SND_SOC_RT5514=y && SND_SOC_RT5514_SPI=m + +config SND_SOC_RT5616 + tristate "Realtek RT5616 CODEC" + depends on I2C + +config SND_SOC_RT5631 + tristate "Realtek ALC5631/RT5631 CODEC" + depends on I2C + +config SND_SOC_RT5640 + tristate "Realtek RT5640/RT5639 Codec" + depends on I2C + +config SND_SOC_RT5645 + tristate + depends on I2C + +config SND_SOC_RT5651 + tristate + depends on I2C + +config SND_SOC_RT5659 + tristate "Realtek RT5658/RT5659 Codec" + depends on I2C + +config SND_SOC_RT5660 + tristate + depends on I2C + +config SND_SOC_RT5663 + tristate + depends on I2C + +config SND_SOC_RT5665 + tristate + depends on I2C + +config SND_SOC_RT5668 + tristate + depends on I2C + +config SND_SOC_RT5670 + tristate + depends on I2C + +config SND_SOC_RT5677 + tristate + depends on I2C + select REGMAP_I2C + select REGMAP_IRQ + +config SND_SOC_RT5677_SPI + tristate + default SND_SOC_RT5677 && SPI + +config SND_SOC_RT5682 + tristate + +config SND_SOC_RT5682_I2C + tristate + depends on I2C + select SND_SOC_RT5682 + +config SND_SOC_RT5682_SDW + tristate "Realtek RT5682 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT5682 + select REGMAP_SOUNDWIRE + +config SND_SOC_RT5682S + tristate + depends on I2C + +config SND_SOC_RT700 + tristate + +config SND_SOC_RT700_SDW + tristate "Realtek RT700 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT700 + select REGMAP_SOUNDWIRE + +config SND_SOC_RT711 + tristate + +config SND_SOC_RT711_SDW + tristate "Realtek RT711 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT711 + select REGMAP_SOUNDWIRE + +config SND_SOC_RT711_SDCA_SDW + tristate "Realtek RT711 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + +config SND_SOC_RT712_SDCA_SDW + tristate "Realtek RT712 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + +config SND_SOC_RT712_SDCA_DMIC_SDW + tristate "Realtek RT712 SDCA DMIC Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + +config SND_SOC_RT722_SDCA_SDW + tristate "Realtek RT722 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + +config SND_SOC_RT715 + tristate + +config SND_SOC_RT715_SDW + tristate "Realtek RT715 Codec - SDW" + depends on SOUNDWIRE + select SND_SOC_RT715 + select REGMAP_SOUNDWIRE + +config SND_SOC_RT715_SDCA_SDW + tristate "Realtek RT715 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + +config SND_SOC_RT9120 + tristate "Richtek RT9120 Stereo Class-D Amplifier" + depends on I2C + select REGMAP_I2C + help + Enable support for Richtek RT9120 20W, stereo, inductor-less, + high-efficiency Class-D audio amplifier. + +config SND_SOC_SDW_MOCKUP + tristate "SoundWire mockup codec" + depends on EXPERT + depends on SOUNDWIRE + help + This option enables a SoundWire mockup codec that does not drive the + bus, take part in the command/command protocol or generate data on a + Source port. + This option is only intended to be used for tests on a device + with a connector, in combination with a bus analyzer, or to test new + topologies that differ from the actual hardware layout. + This mockup device could be totally virtual but could also be a + real physical one with one key restriction: it is not allowed by the + SoundWire specification to be configured via a sideband mechanism and + generate audio data for capture. However, nothing prevents such a + peripheral device from snooping the bus. + +#Freescale sgtl5000 codec +config SND_SOC_SGTL5000 + tristate "Freescale SGTL5000 CODEC" + depends on I2C + +config SND_SOC_SI476X + tristate + +config SND_SOC_SIGMADSP + tristate + select CRC32 + +config SND_SOC_SIGMADSP_I2C + tristate + select SND_SOC_SIGMADSP + +config SND_SOC_SIGMADSP_REGMAP + tristate + select SND_SOC_SIGMADSP + +config SND_SOC_SIMPLE_AMPLIFIER + tristate "Simple Audio Amplifier" + +config SND_SOC_SIMPLE_MUX + tristate "Simple Audio Mux" + depends on GPIOLIB + +config SND_SOC_SMA1303 + tristate "Iron Device SMA1303 Audio Amplifier" + depends on I2C + help + Enable support for Iron Device SMA1303 Boosted Class-D amplifier + +config SND_SOC_SPDIF + tristate "S/PDIF CODEC" + +config SND_SOC_SRC4XXX_I2C + tristate "Texas Instruments SRC4XXX DIR/DIT and SRC codecs" + depends on I2C + select SND_SOC_SRC4XXX + help + Enable support for the TI SRC4XXX family of codecs. These include the + scr4392 which has digital receivers, transmitters, and + a sample rate converter, including numerous ports. + +config SND_SOC_SRC4XXX + tristate + +config SND_SOC_SSM2305 + tristate "Analog Devices SSM2305 Class-D Amplifier" + help + Enable support for Analog Devices SSM2305 filterless + high-efficiency mono Class-D audio power amplifiers. + +config SND_SOC_SSM2518 + tristate "Analog Devices SSM2518 Class-D Amplifier" + depends on I2C + +config SND_SOC_SSM2602 + tristate + +config SND_SOC_SSM2602_SPI + tristate "Analog Devices SSM2602 CODEC - SPI" + depends on SPI_MASTER + select SND_SOC_SSM2602 + select REGMAP_SPI + +config SND_SOC_SSM2602_I2C + tristate "Analog Devices SSM2602 CODEC - I2C" + depends on I2C + select SND_SOC_SSM2602 + select REGMAP_I2C + +config SND_SOC_SSM3515 + tristate "Analog Devices SSM3515 amplifier driver" + select REGMAP_I2C + depends on I2C + depends on OF + +config SND_SOC_SSM4567 + tristate "Analog Devices ssm4567 amplifier driver support" + depends on I2C + +config SND_SOC_STA32X + tristate "STA326, STA328 and STA329 speaker amplifier" + depends on I2C + select REGMAP_I2C + +config SND_SOC_STA350 + tristate "STA350 speaker amplifier" + depends on I2C + +config SND_SOC_STA529 + tristate + depends on I2C + +config SND_SOC_STAC9766 + tristate + depends on SND_SOC_AC97_BUS + select REGMAP_AC97 + +config SND_SOC_STI_SAS + tristate "codec Audio support for STI SAS codec" + +config SND_SOC_TAS2552 + tristate "Texas Instruments TAS2552 Mono Audio amplifier" + depends on I2C + +config SND_SOC_TAS2562 + tristate "Texas Instruments TAS2562 Mono Audio amplifier" + depends on I2C + +config SND_SOC_TAS2764 + tristate "Texas Instruments TAS2764 Mono Audio amplifier" + depends on I2C + +config SND_SOC_TAS2770 + tristate "Texas Instruments TAS2770 speaker amplifier" + depends on I2C + +config SND_SOC_TAS2780 + tristate "Texas Instruments TAS2780 Mono Audio amplifier" + depends on I2C + help + Enable support for Texas Instruments TAS2780 high-efficiency + digital input mono Class-D audio power amplifiers. + +config SND_SOC_TAS2781_COMLIB + depends on I2C + select CRC8 + select REGMAP_I2C + tristate + +config SND_SOC_TAS2781_FMWLIB + depends on SND_SOC_TAS2781_COMLIB + tristate + default n + +config SND_SOC_TAS2781_I2C + tristate "Texas Instruments TAS2781 speaker amplifier based on I2C" + depends on I2C + select SND_SOC_TAS2781_COMLIB + select SND_SOC_TAS2781_FMWLIB + help + Enable support for Texas Instruments TAS2781 Smart Amplifier + Digital input mono Class-D and DSP-inside audio power amplifiers. + Note the TAS2781 driver implements a flexible and configurable + algo coefficient setting, for one, two or even multiple TAS2781 + chips. + +config SND_SOC_TAS5086 + tristate "Texas Instruments TAS5086 speaker amplifier" + depends on I2C + +config SND_SOC_TAS571X + tristate "Texas Instruments TAS571x power amplifiers" + depends on I2C + help + Enable support for Texas Instruments TAS5707, TAS5711, TAS5717, + TAS5719 and TAS5721 power amplifiers + +config SND_SOC_TAS5720 + tristate "Texas Instruments TAS5720 Mono Audio amplifier" + depends on I2C + help + Enable support for Texas Instruments TAS5720L/M high-efficiency mono + Class-D audio power amplifiers. + +config SND_SOC_TAS5805M + tristate "Texas Instruments TAS5805M speaker amplifier" + depends on I2C + help + Enable support for Texas Instruments TAS5805M Class-D + amplifiers. This is a speaker amplifier with an integrated + DSP. DSP configuration for each instance needs to be supplied + via a device-tree attribute. + +config SND_SOC_TAS6424 + tristate "Texas Instruments TAS6424 Quad-Channel Audio amplifier" + depends on I2C + help + Enable support for Texas Instruments TAS6424 high-efficiency + digital input quad-channel Class-D audio power amplifiers. + +config SND_SOC_TDA7419 + tristate "ST TDA7419 audio processor" + depends on I2C + select REGMAP_I2C + +config SND_SOC_TFA9879 + tristate "NXP Semiconductors TFA9879 amplifier" + depends on I2C + +config SND_SOC_TFA989X + tristate "NXP/Goodix TFA989X (TFA1) amplifiers" + depends on I2C + select REGMAP_I2C + help + Enable support for NXP (now Goodix) TFA989X (TFA1 family) speaker + amplifiers, e.g. TFA9895. + Note that the driver currently bypasses the built-in "CoolFlux DSP" + and does not support (hardware) volume control. + +config SND_SOC_TLV320ADC3XXX + tristate "Texas Instruments TLV320ADC3001/3101 audio ADC" + depends on I2C + depends on GPIOLIB + help + Enable support for Texas Instruments TLV320ADC3001 and TLV320ADC3101 + ADCs. + +config SND_SOC_TLV320AIC23 + tristate + +config SND_SOC_TLV320AIC23_I2C + tristate "Texas Instruments TLV320AIC23 audio CODEC - I2C" + depends on I2C + select SND_SOC_TLV320AIC23 + +config SND_SOC_TLV320AIC23_SPI + tristate "Texas Instruments TLV320AIC23 audio CODEC - SPI" + depends on SPI_MASTER + select SND_SOC_TLV320AIC23 + +config SND_SOC_TLV320AIC26 + tristate + depends on SPI + +config SND_SOC_TLV320AIC31XX + tristate "Texas Instruments TLV320AIC31xx CODECs" + depends on I2C + select REGMAP_I2C + +config SND_SOC_TLV320AIC32X4 + tristate + depends on COMMON_CLK + +config SND_SOC_TLV320AIC32X4_I2C + tristate "Texas Instruments TLV320AIC32x4 audio CODECs - I2C" + depends on I2C + depends on COMMON_CLK + select SND_SOC_TLV320AIC32X4 + +config SND_SOC_TLV320AIC32X4_SPI + tristate "Texas Instruments TLV320AIC32x4 audio CODECs - SPI" + depends on SPI_MASTER + depends on COMMON_CLK + select SND_SOC_TLV320AIC32X4 + +config SND_SOC_TLV320AIC3X + tristate + +config SND_SOC_TLV320AIC3X_I2C + tristate "Texas Instruments TLV320AIC3x audio CODECs - I2C" + depends on I2C + select SND_SOC_TLV320AIC3X + select REGMAP_I2C + +config SND_SOC_TLV320AIC3X_SPI + tristate "Texas Instruments TLV320AIC3x audio CODECs - SPI" + depends on SPI_MASTER + select SND_SOC_TLV320AIC3X + select REGMAP_SPI + +config SND_SOC_TLV320DAC33 + tristate + depends on I2C + +config SND_SOC_TLV320ADCX140 + tristate "Texas Instruments TLV320ADCX140 CODEC family" + depends on I2C + select REGMAP_I2C + help + Add support for Texas Instruments tlv320adc3140, tlv320adc5140 and + tlv320adc6140 quad channel ADCs. + +config SND_SOC_TS3A227E + tristate "TI Headset/Mic detect and keypress chip" + depends on I2C + +config SND_SOC_TSCS42XX + tristate "Tempo Semiconductor TSCS42xx CODEC" + depends on I2C + select REGMAP_I2C + help + Add support for Tempo Semiconductor's TSCS42xx audio CODEC. + +config SND_SOC_TSCS454 + tristate "Tempo Semiconductor TSCS454 CODEC" + depends on I2C + select REGMAP_I2C + help + Add support for Tempo Semiconductor's TSCS454 audio CODEC. + +config SND_SOC_TWL4030 + tristate + depends on TWL4030_CORE + select MFD_TWL4030_AUDIO + +config SND_SOC_TWL6040 + tristate + depends on TWL6040_CORE + +config SND_SOC_UDA1334 + tristate "NXP UDA1334 DAC" + depends on GPIOLIB + help + The UDA1334 is an NXP audio codec, supports the I2S-bus data format + and has basic features such as de-emphasis (at 44.1 kHz sampling + rate) and mute. + +config SND_SOC_UDA1380 + tristate + depends on I2C + +config SND_SOC_WCD_CLASSH + tristate + +config SND_SOC_WCD9335 + tristate "WCD9335 Codec" + depends on SLIMBUS + select REGMAP_SLIMBUS + select REGMAP_IRQ + select SND_SOC_WCD_CLASSH + help + The WCD9335 is a standalone Hi-Fi audio CODEC IC, supports + Qualcomm Technologies, Inc. (QTI) multimedia solutions, + including the MSM8996, MSM8976, and MSM8956 chipsets. + +config SND_SOC_WCD_MBHC + tristate + +config SND_SOC_WCD934X + tristate "WCD9340/WCD9341 Codec" + depends on COMMON_CLK + depends on SLIMBUS + select REGMAP_IRQ + select REGMAP_SLIMBUS + select SND_SOC_WCD_CLASSH + select SND_SOC_WCD_MBHC + depends on MFD_WCD934X || COMPILE_TEST + help + The WCD9340/9341 is a audio codec IC Integrated in + Qualcomm SoCs like SDM845. + +config SND_SOC_WCD938X + depends on SND_SOC_WCD938X_SDW + tristate + depends on SOUNDWIRE || !SOUNDWIRE + select SND_SOC_WCD_CLASSH + +config SND_SOC_WCD938X_SDW + tristate "WCD9380/WCD9385 Codec - SDW" + select SND_SOC_WCD938X + select SND_SOC_WCD_MBHC + select REGMAP_IRQ + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + help + The WCD9380/9385 is a audio codec IC Integrated in + Qualcomm SoCs like SM8250. + +config SND_SOC_WL1273 + tristate + +config SND_SOC_WM0010 + tristate + depends on SPI_MASTER + +config SND_SOC_WM1250_EV1 + tristate + depends on I2C + +config SND_SOC_WM2000 + tristate + depends on I2C + +config SND_SOC_WM2200 + tristate + depends on I2C + +config SND_SOC_WM5100 + tristate + depends on I2C + +config SND_SOC_WM5102 + tristate + depends on MFD_WM5102 && MFD_ARIZONA + +config SND_SOC_WM5110 + tristate + depends on MFD_WM5110 && MFD_ARIZONA + +config SND_SOC_WM8350 + tristate + depends on MFD_WM8350 + +config SND_SOC_WM8400 + tristate + # FIXME nothing selects SND_SOC_WM8400?? + depends on MFD_WM8400 + +config SND_SOC_WM8510 + tristate "Wolfson Microelectronics WM8510 CODEC" + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8523 + tristate "Wolfson Microelectronics WM8523 DAC" + depends on I2C + +config SND_SOC_WM8524 + tristate "Wolfson Microelectronics WM8524 DAC" + depends on GPIOLIB + +config SND_SOC_WM8580 + tristate "Wolfson Microelectronics WM8580 and WM8581 CODECs" + depends on I2C + +config SND_SOC_WM8711 + tristate "Wolfson Microelectronics WM8711 CODEC" + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8727 + tristate + +config SND_SOC_WM8728 + tristate "Wolfson Microelectronics WM8728 DAC" + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8731 + tristate + +config SND_SOC_WM8731_I2C + tristate "Wolfson Microelectronics WM8731 CODEC with I2C" + depends on I2C + select REGMAP + select SND_SOC_WM8731 + +config SND_SOC_WM8731_SPI + tristate "Wolfson Microelectronics WM8731 CODEC with SPI" + depends on SPI + select REGMAP + select SND_SOC_WM8731 + +config SND_SOC_WM8737 + tristate "Wolfson Microelectronics WM8737 ADC" + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8741 + tristate "Wolfson Microelectronics WM8741 DAC" + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8750 + tristate "Wolfson Microelectronics WM8750 CODEC" + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8753 + tristate "Wolfson Microelectronics WM8753 CODEC" + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8770 + tristate "Wolfson Microelectronics WM8770 CODEC" + depends on SPI_MASTER + +config SND_SOC_WM8776 + tristate "Wolfson Microelectronics WM8776 CODEC" + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8782 + tristate "Wolfson Microelectronics WM8782 ADC" + +config SND_SOC_WM8804 + tristate + +config SND_SOC_WM8804_I2C + tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver I2C" + depends on I2C + select SND_SOC_WM8804 + select REGMAP_I2C + +config SND_SOC_WM8804_SPI + tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver SPI" + depends on SPI_MASTER + select SND_SOC_WM8804 + select REGMAP_SPI + +config SND_SOC_WM8900 + tristate + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8903 + tristate "Wolfson Microelectronics WM8903 CODEC" + depends on I2C + +config SND_SOC_WM8904 + tristate "Wolfson Microelectronics WM8904 CODEC" + depends on I2C + +config SND_SOC_WM8940 + tristate "Wolfson Microelectronics WM8940 codec" + depends on I2C + +config SND_SOC_WM8955 + tristate + depends on I2C + +config SND_SOC_WM8960 + tristate "Wolfson Microelectronics WM8960 CODEC" + depends on I2C + +config SND_SOC_WM8961 + tristate "Wolfson Microelectronics WM8961 CODEC" + depends on I2C + +config SND_SOC_WM8962 + tristate "Wolfson Microelectronics WM8962 CODEC" + depends on I2C && INPUT + +config SND_SOC_WM8971 + tristate + depends on I2C + +config SND_SOC_WM8974 + tristate "Wolfson Microelectronics WM8974 codec" + depends on I2C + +config SND_SOC_WM8978 + tristate "Wolfson Microelectronics WM8978 codec" + depends on I2C + +config SND_SOC_WM8983 + tristate + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8985 + tristate "Wolfson Microelectronics WM8985 and WM8758 codec driver" + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8988 + tristate + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8990 + tristate + depends on I2C + +config SND_SOC_WM8991 + tristate + depends on I2C + +config SND_SOC_WM8993 + tristate + depends on I2C + +config SND_SOC_WM8994 + tristate + +config SND_SOC_WM8995 + tristate + depends on SND_SOC_I2C_AND_SPI + +config SND_SOC_WM8996 + tristate + depends on I2C + +config SND_SOC_WM8997 + tristate + depends on MFD_WM8997 && MFD_ARIZONA + +config SND_SOC_WM8998 + tristate + depends on MFD_WM8998 && MFD_ARIZONA + +config SND_SOC_WM9081 + tristate + depends on I2C + +config SND_SOC_WM9090 + tristate + depends on I2C + +config SND_SOC_WM9705 + tristate + depends on SND_SOC_AC97_BUS || AC97_BUS_NEW + select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW + +config SND_SOC_WM9712 + tristate + depends on SND_SOC_AC97_BUS || AC97_BUS_NEW + select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW + +config SND_SOC_WM9713 + tristate + depends on SND_SOC_AC97_BUS || AC97_BUS_NEW + select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW + +config SND_SOC_WSA881X + tristate "WSA881X Codec" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + tristate + help + This enables support for Qualcomm WSA8810/WSA8815 Class-D + Smart Speaker Amplifier. + +config SND_SOC_WSA883X + tristate "WSA883X Codec" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + tristate + help + This enables support for Qualcomm WSA8830/WSA8835 Class-D + Smart Speaker Amplifier. + +config SND_SOC_WSA884X + tristate "WSA884X Codec" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + tristate + help + This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D + Smart Speaker Amplifier. + +config SND_SOC_ZL38060 + tristate "Microsemi ZL38060 Connected Home Audio Processor" + depends on SPI_MASTER + depends on GPIOLIB + select REGMAP + help + Support for ZL38060 Connected Home Audio Processor from Microsemi, + which consists of a Digital Signal Processor (DSP), several Digital + Audio Interfaces (DAIs), analog outputs, and a block of 14 GPIOs. + +# Amp +config SND_SOC_LM4857 + tristate + depends on I2C + +config SND_SOC_MAX9759 + tristate "Maxim MAX9759 speaker Amplifier" + depends on GPIOLIB + +config SND_SOC_MAX9768 + tristate + depends on I2C + +config SND_SOC_MAX9877 + tristate + depends on I2C + +config SND_SOC_MC13783 + tristate + depends on MFD_MC13XXX + +config SND_SOC_ML26124 + tristate + depends on I2C + +config SND_SOC_MT6351 + tristate "MediaTek MT6351 Codec" + +config SND_SOC_MT6358 + tristate "MediaTek MT6358 Codec" + help + Enable support for the platform which uses MT6358 as + external codec device. + +config SND_SOC_MT6359 + tristate "MediaTek MT6359 Codec" + depends on MTK_PMIC_WRAP + help + Enable support for the platform which uses MT6359 as + external codec device. + +config SND_SOC_MT6359_ACCDET + tristate "MediaTek MT6359 ACCDET driver" + depends on MTK_PMIC_WRAP + help + ACCDET means Accessory Detection technology, MediaTek develop it + for ASoC codec soc-jack detection mechanism. + Select N if you don't have jack on board. + +config SND_SOC_MT6660 + tristate "Mediatek MT6660 Speaker Amplifier" + depends on I2C + help + MediaTek MT6660 is a smart power amplifier which contain + speaker protection, multi-band DRC, equalizer functions. + Select N if you don't have MT6660 on board. + Select M to build this as module. + +config SND_SOC_NAU8315 + tristate "Nuvoton Technology Corporation NAU8315 CODEC" + +config SND_SOC_NAU8540 + tristate "Nuvoton Technology Corporation NAU85L40 CODEC" + depends on I2C + +config SND_SOC_NAU8810 + tristate "Nuvoton Technology Corporation NAU88C10 CODEC" + depends on I2C + +config SND_SOC_NAU8821 + tristate "Nuvoton Technology Corporation NAU88L21 CODEC" + depends on I2C + +config SND_SOC_NAU8822 + tristate "Nuvoton Technology Corporation NAU88C22 CODEC" + depends on I2C + +config SND_SOC_NAU8824 + tristate "Nuvoton Technology Corporation NAU88L24 CODEC" + depends on I2C + +config SND_SOC_NAU8825 + tristate + depends on I2C + +config SND_SOC_TPA6130A2 + tristate "Texas Instruments TPA6130A2 headphone amplifier" + depends on I2C + +config SND_SOC_LPASS_MACRO_COMMON + tristate + +config SND_SOC_LPASS_WSA_MACRO + depends on COMMON_CLK + select REGMAP_MMIO + tristate "Qualcomm WSA Macro in LPASS(Low Power Audio SubSystem)" + +config SND_SOC_LPASS_VA_MACRO + depends on COMMON_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm VA Macro in LPASS(Low Power Audio SubSystem)" + +config SND_SOC_LPASS_RX_MACRO + depends on COMMON_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm RX Macro in LPASS(Low Power Audio SubSystem)" + +config SND_SOC_LPASS_TX_MACRO + depends on COMMON_CLK + select REGMAP_MMIO + select SND_SOC_LPASS_MACRO_COMMON + tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" + +endmenu diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile new file mode 100644 index 0000000000..a87e56938c --- /dev/null +++ b/sound/soc/codecs/Makefile @@ -0,0 +1,774 @@ +# SPDX-License-Identifier: GPL-2.0 +snd-soc-88pm860x-objs := 88pm860x-codec.o +snd-soc-ab8500-codec-objs := ab8500-codec.o +snd-soc-ac97-objs := ac97.o +snd-soc-ad1836-objs := ad1836.o +snd-soc-ad193x-objs := ad193x.o +snd-soc-ad193x-spi-objs := ad193x-spi.o +snd-soc-ad193x-i2c-objs := ad193x-i2c.o +snd-soc-ad1980-objs := ad1980.o +snd-soc-ad73311-objs := ad73311.o +snd-soc-adau-utils-objs := adau-utils.o +snd-soc-adau1372-objs := adau1372.o +snd-soc-adau1372-i2c-objs := adau1372-i2c.o +snd-soc-adau1372-spi-objs := adau1372-spi.o +snd-soc-adau1373-objs := adau1373.o +snd-soc-adau1701-objs := adau1701.o +snd-soc-adau17x1-objs := adau17x1.o +snd-soc-adau1761-objs := adau1761.o +snd-soc-adau1761-i2c-objs := adau1761-i2c.o +snd-soc-adau1761-spi-objs := adau1761-spi.o +snd-soc-adau1781-objs := adau1781.o +snd-soc-adau1781-i2c-objs := adau1781-i2c.o +snd-soc-adau1781-spi-objs := adau1781-spi.o +snd-soc-adau1977-objs := adau1977.o +snd-soc-adau1977-spi-objs := adau1977-spi.o +snd-soc-adau1977-i2c-objs := adau1977-i2c.o +snd-soc-adau7002-objs := adau7002.o +snd-soc-adau7118-objs := adau7118.o +snd-soc-adau7118-i2c-objs := adau7118-i2c.o +snd-soc-adau7118-hw-objs := adau7118-hw.o +snd-soc-adav80x-objs := adav80x.o +snd-soc-adav801-objs := adav801.o +snd-soc-adav803-objs := adav803.o +snd-soc-ads117x-objs := ads117x.o +snd-soc-ak4104-objs := ak4104.o +snd-soc-ak4118-objs := ak4118.o +snd-soc-ak4375-objs := ak4375.o +snd-soc-ak4458-objs := ak4458.o +snd-soc-ak4535-objs := ak4535.o +snd-soc-ak4554-objs := ak4554.o +snd-soc-ak4613-objs := ak4613.o +snd-soc-ak4641-objs := ak4641.o +snd-soc-ak4642-objs := ak4642.o +snd-soc-ak4671-objs := ak4671.o +snd-soc-ak5386-objs := ak5386.o +snd-soc-ak5558-objs := ak5558.o +snd-soc-arizona-objs := arizona.o arizona-jack.o +snd-soc-audio-iio-aux-objs := audio-iio-aux.o +snd-soc-aw8738-objs := aw8738.o +snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o +snd-soc-aw88395-objs := aw88395/aw88395.o \ + aw88395/aw88395_device.o +snd-soc-aw88261-objs := aw88261.o +snd-soc-bd28623-objs := bd28623.o +snd-soc-bt-sco-objs := bt-sco.o +snd-soc-chv3-codec-objs := chv3-codec.o +snd-soc-cpcap-objs := cpcap.o +snd-soc-cq93vc-objs := cq93vc.o +snd-soc-cros-ec-codec-objs := cros_ec_codec.o +snd-soc-cs35l32-objs := cs35l32.o +snd-soc-cs35l33-objs := cs35l33.o +snd-soc-cs35l34-objs := cs35l34.o +snd-soc-cs35l35-objs := cs35l35.o +snd-soc-cs35l36-objs := cs35l36.o +snd-soc-cs35l41-lib-objs := cs35l41-lib.o +snd-soc-cs35l41-objs := cs35l41.o +snd-soc-cs35l41-spi-objs := cs35l41-spi.o +snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o +snd-soc-cs35l45-objs := cs35l45.o cs35l45-tables.o +snd-soc-cs35l45-spi-objs := cs35l45-spi.o +snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o +snd-soc-cs35l56-objs := cs35l56.o +snd-soc-cs35l56-shared-objs := cs35l56-shared.o +snd-soc-cs35l56-i2c-objs := cs35l56-i2c.o +snd-soc-cs35l56-spi-objs := cs35l56-spi.o +snd-soc-cs35l56-sdw-objs := cs35l56-sdw.o +snd-soc-cs42l42-objs := cs42l42.o +snd-soc-cs42l42-i2c-objs := cs42l42-i2c.o +snd-soc-cs42l42-sdw-objs := cs42l42-sdw.o +snd-soc-cs42l43-objs := cs42l43.o cs42l43-jack.o +snd-soc-cs42l43-sdw-objs := cs42l43-sdw.o +snd-soc-cs42l51-objs := cs42l51.o +snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o +snd-soc-cs42l52-objs := cs42l52.o +snd-soc-cs42l56-objs := cs42l56.o +snd-soc-cs42l73-objs := cs42l73.o +snd-soc-cs42l83-i2c-objs := cs42l83-i2c.o +snd-soc-cs4234-objs := cs4234.o +snd-soc-cs4265-objs := cs4265.o +snd-soc-cs4270-objs := cs4270.o +snd-soc-cs4271-objs := cs4271.o +snd-soc-cs4271-i2c-objs := cs4271-i2c.o +snd-soc-cs4271-spi-objs := cs4271-spi.o +snd-soc-cs42xx8-objs := cs42xx8.o +snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o +snd-soc-cs43130-objs := cs43130.o +snd-soc-cs4341-objs := cs4341.o +snd-soc-cs4349-objs := cs4349.o +snd-soc-cs47l15-objs := cs47l15.o +snd-soc-cs47l24-objs := cs47l24.o +snd-soc-cs47l35-objs := cs47l35.o +snd-soc-cs47l85-objs := cs47l85.o +snd-soc-cs47l90-objs := cs47l90.o +snd-soc-cs47l92-objs := cs47l92.o +snd-soc-cs53l30-objs := cs53l30.o +snd-soc-cx20442-objs := cx20442.o +snd-soc-cx2072x-objs := cx2072x.o +snd-soc-da7210-objs := da7210.o +snd-soc-da7213-objs := da7213.o +snd-soc-da7218-objs := da7218.o +snd-soc-da7219-objs := da7219.o da7219-aad.o +snd-soc-da732x-objs := da732x.o +snd-soc-da9055-objs := da9055.o +snd-soc-dmic-objs := dmic.o +snd-soc-es7134-objs := es7134.o +snd-soc-es7241-objs := es7241.o +snd-soc-es8316-objs := es8316.o +snd-soc-es8326-objs := es8326.o +snd-soc-es8328-objs := es8328.o +snd-soc-es8328-i2c-objs := es8328-i2c.o +snd-soc-es8328-spi-objs := es8328-spi.o +snd-soc-gtm601-objs := gtm601.o +snd-soc-hdac-hdmi-objs := hdac_hdmi.o +snd-soc-hdac-hda-objs := hdac_hda.o +snd-soc-hda-codec-objs := hda.o hda-dai.o +snd-soc-ics43432-objs := ics43432.o +snd-soc-idt821034-objs := idt821034.o +snd-soc-inno-rk3036-objs := inno_rk3036.o +snd-soc-isabelle-objs := isabelle.o +snd-soc-jz4740-codec-objs := jz4740.o +snd-soc-jz4725b-codec-objs := jz4725b.o +snd-soc-jz4760-codec-objs := jz4760.o +snd-soc-jz4770-codec-objs := jz4770.o +snd-soc-lm4857-objs := lm4857.o +snd-soc-lm49453-objs := lm49453.o +snd-soc-lochnagar-sc-objs := lochnagar-sc.o +snd-soc-lpass-macro-common-objs := lpass-macro-common.o +snd-soc-lpass-rx-macro-objs := lpass-rx-macro.o +snd-soc-lpass-tx-macro-objs := lpass-tx-macro.o +snd-soc-lpass-wsa-macro-objs := lpass-wsa-macro.o +snd-soc-lpass-va-macro-objs := lpass-va-macro.o +snd-soc-madera-objs := madera.o +snd-soc-max9759-objs := max9759.o +snd-soc-max9768-objs := max9768.o +snd-soc-max98088-objs := max98088.o +snd-soc-max98090-objs := max98090.o +snd-soc-max98095-objs := max98095.o +snd-soc-max98357a-objs := max98357a.o +snd-soc-max98371-objs := max98371.o +snd-soc-max9867-objs := max9867.o +snd-soc-max98925-objs := max98925.o +snd-soc-max98926-objs := max98926.o +snd-soc-max98927-objs := max98927.o +snd-soc-max98520-objs := max98520.o +snd-soc-max98363-objs := max98363.o +snd-soc-max98373-objs := max98373.o +snd-soc-max98373-i2c-objs := max98373-i2c.o +snd-soc-max98373-sdw-objs := max98373-sdw.o +snd-soc-max98388-objs := max98388.o +snd-soc-max98390-objs := max98390.o +snd-soc-max98396-objs := max98396.o +snd-soc-max9850-objs := max9850.o +snd-soc-max9860-objs := max9860.o +snd-soc-mc13783-objs := mc13783.o +snd-soc-ml26124-objs := ml26124.o +snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o +snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o +snd-soc-mt6351-objs := mt6351.o +snd-soc-mt6358-objs := mt6358.o +snd-soc-mt6359-objs := mt6359.o +snd-soc-mt6359-accdet-objs := mt6359-accdet.o +snd-soc-mt6660-objs := mt6660.o +snd-soc-nau8315-objs := nau8315.o +snd-soc-nau8540-objs := nau8540.o +snd-soc-nau8810-objs := nau8810.o +snd-soc-nau8821-objs := nau8821.o +snd-soc-nau8822-objs := nau8822.o +snd-soc-nau8824-objs := nau8824.o +snd-soc-nau8825-objs := nau8825.o +snd-soc-hdmi-codec-objs := hdmi-codec.o +snd-soc-pcm1681-objs := pcm1681.o +snd-soc-pcm1789-codec-objs := pcm1789.o +snd-soc-pcm1789-i2c-objs := pcm1789-i2c.o +snd-soc-pcm179x-codec-objs := pcm179x.o +snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o +snd-soc-pcm179x-spi-objs := pcm179x-spi.o +snd-soc-pcm186x-objs := pcm186x.o +snd-soc-pcm186x-i2c-objs := pcm186x-i2c.o +snd-soc-pcm186x-spi-objs := pcm186x-spi.o +snd-soc-pcm3008-objs := pcm3008.o +snd-soc-pcm3060-objs := pcm3060.o +snd-soc-pcm3060-i2c-objs := pcm3060-i2c.o +snd-soc-pcm3060-spi-objs := pcm3060-spi.o +snd-soc-pcm3168a-objs := pcm3168a.o +snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o +snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o +snd-soc-pcm5102a-objs := pcm5102a.o +snd-soc-pcm512x-objs := pcm512x.o +snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o +snd-soc-pcm512x-spi-objs := pcm512x-spi.o +snd-soc-peb2466-objs := peb2466.o +snd-soc-rk3328-objs := rk3328_codec.o +snd-soc-rk817-objs := rk817_codec.o +snd-soc-rl6231-objs := rl6231.o +snd-soc-rl6347a-objs := rl6347a.o +snd-soc-rt1011-objs := rt1011.o +snd-soc-rt1015-objs := rt1015.o +snd-soc-rt1015p-objs := rt1015p.o +snd-soc-rt1016-objs := rt1016.o +snd-soc-rt1017-sdca-objs := rt1017-sdca-sdw.o +snd-soc-rt1019-objs := rt1019.o +snd-soc-rt1305-objs := rt1305.o +snd-soc-rt1308-objs := rt1308.o +snd-soc-rt1308-sdw-objs := rt1308-sdw.o +snd-soc-rt1316-sdw-objs := rt1316-sdw.o +snd-soc-rt1318-sdw-objs := rt1318-sdw.o +snd-soc-rt274-objs := rt274.o +snd-soc-rt286-objs := rt286.o +snd-soc-rt298-objs := rt298.o +snd-soc-rt5514-objs := rt5514.o +snd-soc-rt5514-spi-objs := rt5514-spi.o +snd-soc-rt5616-objs := rt5616.o +snd-soc-rt5631-objs := rt5631.o +snd-soc-rt5640-objs := rt5640.o +snd-soc-rt5645-objs := rt5645.o +snd-soc-rt5651-objs := rt5651.o +snd-soc-rt5659-objs := rt5659.o +snd-soc-rt5660-objs := rt5660.o +snd-soc-rt5663-objs := rt5663.o +snd-soc-rt5665-objs := rt5665.o +snd-soc-rt5668-objs := rt5668.o +snd-soc-rt5670-objs := rt5670.o +snd-soc-rt5677-objs := rt5677.o +snd-soc-rt5677-spi-objs := rt5677-spi.o +snd-soc-rt5682-objs := rt5682.o +snd-soc-rt5682-sdw-objs := rt5682-sdw.o +snd-soc-rt5682-i2c-objs := rt5682-i2c.o +snd-soc-rt5682s-objs := rt5682s.o +snd-soc-rt700-objs := rt700.o rt700-sdw.o +snd-soc-rt711-objs := rt711.o rt711-sdw.o +snd-soc-rt711-sdca-objs := rt711-sdca.o rt711-sdca-sdw.o +snd-soc-rt712-sdca-objs := rt712-sdca.o rt712-sdca-sdw.o +snd-soc-rt712-sdca-dmic-objs := rt712-sdca-dmic.o +snd-soc-rt715-objs := rt715.o rt715-sdw.o +snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o +snd-soc-rt722-sdca-objs := rt722-sdca.o rt722-sdca-sdw.o +snd-soc-rt9120-objs := rt9120.o +snd-soc-sdw-mockup-objs := sdw-mockup.o +snd-soc-sgtl5000-objs := sgtl5000.o +snd-soc-alc5623-objs := alc5623.o +snd-soc-alc5632-objs := alc5632.o +snd-soc-sigmadsp-objs := sigmadsp.o +snd-soc-sigmadsp-i2c-objs := sigmadsp-i2c.o +snd-soc-sigmadsp-regmap-objs := sigmadsp-regmap.o +snd-soc-si476x-objs := si476x.o +snd-soc-sma1303-objs := sma1303.o +snd-soc-spdif-tx-objs := spdif_transmitter.o +snd-soc-spdif-rx-objs := spdif_receiver.o +snd-soc-src4xxx-objs := src4xxx.o +snd-soc-src4xxx-i2c-objs := src4xxx-i2c.o +snd-soc-ssm2305-objs := ssm2305.o +snd-soc-ssm2518-objs := ssm2518.o +snd-soc-ssm2602-objs := ssm2602.o +snd-soc-ssm2602-spi-objs := ssm2602-spi.o +snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o +snd-soc-ssm3515-objs := ssm3515.o +snd-soc-ssm4567-objs := ssm4567.o +snd-soc-sta32x-objs := sta32x.o +snd-soc-sta350-objs := sta350.o +snd-soc-sta529-objs := sta529.o +snd-soc-stac9766-objs := stac9766.o +snd-soc-sti-sas-objs := sti-sas.o +snd-soc-tas5086-objs := tas5086.o +snd-soc-tas571x-objs := tas571x.o +snd-soc-tas5720-objs := tas5720.o +snd-soc-tas5805m-objs := tas5805m.o +snd-soc-tas6424-objs := tas6424.o +snd-soc-tda7419-objs := tda7419.o +snd-soc-tas2770-objs := tas2770.o +snd-soc-tas2781-comlib-objs := tas2781-comlib.o +snd-soc-tas2781-fmwlib-objs := tas2781-fmwlib.o +snd-soc-tas2781-i2c-objs := tas2781-i2c.o +snd-soc-tfa9879-objs := tfa9879.o +snd-soc-tfa989x-objs := tfa989x.o +snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o +snd-soc-tlv320aic23-objs := tlv320aic23.o +snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o +snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o +snd-soc-tlv320aic26-objs := tlv320aic26.o +snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o +snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o tlv320aic32x4-clk.o +snd-soc-tlv320aic32x4-i2c-objs := tlv320aic32x4-i2c.o +snd-soc-tlv320aic32x4-spi-objs := tlv320aic32x4-spi.o +snd-soc-tlv320aic3x-objs := tlv320aic3x.o +snd-soc-tlv320aic3x-i2c-objs := tlv320aic3x-i2c.o +snd-soc-tlv320aic3x-spi-objs := tlv320aic3x-spi.o +snd-soc-tlv320dac33-objs := tlv320dac33.o +snd-soc-tlv320adcx140-objs := tlv320adcx140.o +snd-soc-tscs42xx-objs := tscs42xx.o +snd-soc-tscs454-objs := tscs454.o +snd-soc-ts3a227e-objs := ts3a227e.o +snd-soc-twl4030-objs := twl4030.o +snd-soc-twl6040-objs := twl6040.o +snd-soc-uda1334-objs := uda1334.o +snd-soc-uda1380-objs := uda1380.o +snd-soc-wcd-classh-objs := wcd-clsh-v2.o +snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o +snd-soc-wcd9335-objs := wcd9335.o +snd-soc-wcd934x-objs := wcd934x.o +snd-soc-wcd938x-objs := wcd938x.o +snd-soc-wcd938x-sdw-objs := wcd938x-sdw.o +snd-soc-wl1273-objs := wl1273.o +snd-soc-wm-adsp-objs := wm_adsp.o +snd-soc-wm0010-objs := wm0010.o +snd-soc-wm1250-ev1-objs := wm1250-ev1.o +snd-soc-wm2000-objs := wm2000.o +snd-soc-wm2200-objs := wm2200.o +snd-soc-wm5100-objs := wm5100.o wm5100-tables.o +snd-soc-wm5102-objs := wm5102.o +snd-soc-wm5110-objs := wm5110.o +snd-soc-wm8350-objs := wm8350.o +snd-soc-wm8400-objs := wm8400.o +snd-soc-wm8510-objs := wm8510.o +snd-soc-wm8523-objs := wm8523.o +snd-soc-wm8524-objs := wm8524.o +snd-soc-wm8580-objs := wm8580.o +snd-soc-wm8711-objs := wm8711.o +snd-soc-wm8727-objs := wm8727.o +snd-soc-wm8728-objs := wm8728.o +snd-soc-wm8731-objs := wm8731.o +snd-soc-wm8731-i2c-objs := wm8731-i2c.o +snd-soc-wm8731-spi-objs := wm8731-spi.o +snd-soc-wm8737-objs := wm8737.o +snd-soc-wm8741-objs := wm8741.o +snd-soc-wm8750-objs := wm8750.o +snd-soc-wm8753-objs := wm8753.o +snd-soc-wm8770-objs := wm8770.o +snd-soc-wm8776-objs := wm8776.o +snd-soc-wm8782-objs := wm8782.o +snd-soc-wm8804-objs := wm8804.o +snd-soc-wm8804-i2c-objs := wm8804-i2c.o +snd-soc-wm8804-spi-objs := wm8804-spi.o +snd-soc-wm8900-objs := wm8900.o +snd-soc-wm8903-objs := wm8903.o +snd-soc-wm8904-objs := wm8904.o +snd-soc-wm8996-objs := wm8996.o +snd-soc-wm8940-objs := wm8940.o +snd-soc-wm8955-objs := wm8955.o +snd-soc-wm8960-objs := wm8960.o +snd-soc-wm8961-objs := wm8961.o +snd-soc-wm8962-objs := wm8962.o +snd-soc-wm8971-objs := wm8971.o +snd-soc-wm8974-objs := wm8974.o +snd-soc-wm8978-objs := wm8978.o +snd-soc-wm8983-objs := wm8983.o +snd-soc-wm8985-objs := wm8985.o +snd-soc-wm8988-objs := wm8988.o +snd-soc-wm8990-objs := wm8990.o +snd-soc-wm8991-objs := wm8991.o +snd-soc-wm8993-objs := wm8993.o +snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o +snd-soc-wm8995-objs := wm8995.o +snd-soc-wm8997-objs := wm8997.o +snd-soc-wm8998-objs := wm8998.o +snd-soc-wm9081-objs := wm9081.o +snd-soc-wm9090-objs := wm9090.o +snd-soc-wm9705-objs := wm9705.o +snd-soc-wm9712-objs := wm9712.o +snd-soc-wm9713-objs := wm9713.o +snd-soc-wm-hubs-objs := wm_hubs.o +snd-soc-wsa881x-objs := wsa881x.o +snd-soc-wsa883x-objs := wsa883x.o +snd-soc-wsa884x-objs := wsa884x.o +snd-soc-zl38060-objs := zl38060.o +# Amp +snd-soc-max9877-objs := max9877.o +snd-soc-max98504-objs := max98504.o +snd-soc-simple-amplifier-objs := simple-amplifier.o +snd-soc-tpa6130a2-objs := tpa6130a2.o +snd-soc-tas2552-objs := tas2552.o +snd-soc-tas2562-objs := tas2562.o +snd-soc-tas2764-objs := tas2764.o +snd-soc-tas2780-objs := tas2780.o +# Mux +snd-soc-simple-mux-objs := simple-mux.o + +obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o +obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o +obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o +obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o +obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o +obj-$(CONFIG_SND_SOC_AD193X_SPI) += snd-soc-ad193x-spi.o +obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o +obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o +obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o +obj-$(CONFIG_SND_SOC_ADAU_UTILS) += snd-soc-adau-utils.o +obj-$(CONFIG_SND_SOC_ADAU1372) += snd-soc-adau1372.o +obj-$(CONFIG_SND_SOC_ADAU1372_I2C) += snd-soc-adau1372-i2c.o +obj-$(CONFIG_SND_SOC_ADAU1372_SPI) += snd-soc-adau1372-spi.o +obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o +obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o +obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o +obj-$(CONFIG_SND_SOC_ADAU1761) += snd-soc-adau1761.o +obj-$(CONFIG_SND_SOC_ADAU1761_I2C) += snd-soc-adau1761-i2c.o +obj-$(CONFIG_SND_SOC_ADAU1761_SPI) += snd-soc-adau1761-spi.o +obj-$(CONFIG_SND_SOC_ADAU1781) += snd-soc-adau1781.o +obj-$(CONFIG_SND_SOC_ADAU1781_I2C) += snd-soc-adau1781-i2c.o +obj-$(CONFIG_SND_SOC_ADAU1781_SPI) += snd-soc-adau1781-spi.o +obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o +obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o +obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o +obj-$(CONFIG_SND_SOC_ADAU7002) += snd-soc-adau7002.o +obj-$(CONFIG_SND_SOC_ADAU7118) += snd-soc-adau7118.o +obj-$(CONFIG_SND_SOC_ADAU7118_I2C) += snd-soc-adau7118-i2c.o +obj-$(CONFIG_SND_SOC_ADAU7118_HW) += snd-soc-adau7118-hw.o +obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o +obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o +obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o +obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o +obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o +obj-$(CONFIG_SND_SOC_AK4118) += snd-soc-ak4118.o +obj-$(CONFIG_SND_SOC_AK4375) += snd-soc-ak4375.o +obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o +obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o +obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o +obj-$(CONFIG_SND_SOC_AK4613) += snd-soc-ak4613.o +obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o +obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o +obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o +obj-$(CONFIG_SND_SOC_AK5386) += snd-soc-ak5386.o +obj-$(CONFIG_SND_SOC_AK5558) += snd-soc-ak5558.o +obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o +obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o +obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o +obj-$(CONFIG_SND_SOC_AUDIO_IIO_AUX) += snd-soc-audio-iio-aux.o +obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o +obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o +obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o +obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o +obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o +obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o +obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o +obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o +obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o +obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o +obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o +obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o +obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o +obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o +obj-$(CONFIG_SND_SOC_CS35L36) += snd-soc-cs35l36.o +obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o +obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.o +obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o +obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o +obj-$(CONFIG_SND_SOC_CS35L45) += snd-soc-cs35l45.o +obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o +obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o +obj-$(CONFIG_SND_SOC_CS35L56) += snd-soc-cs35l56.o +obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o +obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o +obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o +obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o +obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o +obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o +obj-$(CONFIG_SND_SOC_CS42L42_SDW) += snd-soc-cs42l42-sdw.o +obj-$(CONFIG_SND_SOC_CS42L43) += snd-soc-cs42l43.o +obj-$(CONFIG_SND_SOC_CS42L43_SDW) += snd-soc-cs42l43-sdw.o +obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o +obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o +obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o +obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o +obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o +obj-$(CONFIG_SND_SOC_CS42L83) += snd-soc-cs42l83-i2c.o +obj-$(CONFIG_SND_SOC_CS4234) += snd-soc-cs4234.o +obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o +obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o +obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o +obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o +obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o +obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o +obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o +obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.o +obj-$(CONFIG_SND_SOC_CS4341) += snd-soc-cs4341.o +obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o +obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o +obj-$(CONFIG_SND_SOC_CS47L15) += snd-soc-cs47l15.o +obj-$(CONFIG_SND_SOC_CS47L35) += snd-soc-cs47l35.o +obj-$(CONFIG_SND_SOC_CS47L85) += snd-soc-cs47l85.o +obj-$(CONFIG_SND_SOC_CS47L90) += snd-soc-cs47l90.o +obj-$(CONFIG_SND_SOC_CS47L92) += snd-soc-cs47l92.o +obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o +obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o +obj-$(CONFIG_SND_SOC_CX2072X) += snd-soc-cx2072x.o +obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o +obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o +obj-$(CONFIG_SND_SOC_DA7218) += snd-soc-da7218.o +obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o +obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o +obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o +obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o +obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o +obj-$(CONFIG_SND_SOC_ES7241) += snd-soc-es7241.o +obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o +obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o +obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o +obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o +obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o +obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o +obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o +obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o +obj-$(CONFIG_SND_SOC_HDA) += snd-soc-hda-codec.o +obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o +obj-$(CONFIG_SND_SOC_IDT821034) += snd-soc-idt821034.o +obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o +obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o +obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o +obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o +obj-$(CONFIG_SND_SOC_JZ4760_CODEC) += snd-soc-jz4760-codec.o +obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o +obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o +obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o +obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o +obj-$(CONFIG_SND_SOC_MADERA) += snd-soc-madera.o +obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o +obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o +obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o +obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o +obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o +obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o +obj-$(CONFIG_SND_SOC_MAX98371) += snd-soc-max98371.o +obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o +obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o +obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o +obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o +obj-$(CONFIG_SND_SOC_MAX98520) += snd-soc-max98520.o +obj-$(CONFIG_SND_SOC_MAX98363) += snd-soc-max98363.o +obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o +obj-$(CONFIG_SND_SOC_MAX98373_I2C) += snd-soc-max98373-i2c.o +obj-$(CONFIG_SND_SOC_MAX98373_SDW) += snd-soc-max98373-sdw.o +obj-$(CONFIG_SND_SOC_MAX98388) += snd-soc-max98388.o +obj-$(CONFIG_SND_SOC_MAX98390) += snd-soc-max98390.o +obj-$(CONFIG_SND_SOC_MAX98396) += snd-soc-max98396.o +obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o +obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o +obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o +obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o +obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o +obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o +obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o +obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o +obj-$(CONFIG_SND_SOC_MT6359) += snd-soc-mt6359.o +obj-$(CONFIG_SND_SOC_MT6359_ACCDET) += mt6359-accdet.o +obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o +obj-$(CONFIG_SND_SOC_NAU8315) += snd-soc-nau8315.o +obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o +obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o +obj-$(CONFIG_SND_SOC_NAU8821) += snd-soc-nau8821.o +obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o +obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o +obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o +obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o +obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o +obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o +obj-$(CONFIG_SND_SOC_PCM1789_I2C) += snd-soc-pcm1789-i2c.o +obj-$(CONFIG_SND_SOC_PCM1789) += snd-soc-pcm1789-codec.o +obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o +obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o +obj-$(CONFIG_SND_SOC_PCM186X) += snd-soc-pcm186x.o +obj-$(CONFIG_SND_SOC_PCM186X_I2C) += snd-soc-pcm186x-i2c.o +obj-$(CONFIG_SND_SOC_PCM186X_SPI) += snd-soc-pcm186x-spi.o +obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o +obj-$(CONFIG_SND_SOC_PCM3060) += snd-soc-pcm3060.o +obj-$(CONFIG_SND_SOC_PCM3060_I2C) += snd-soc-pcm3060-i2c.o +obj-$(CONFIG_SND_SOC_PCM3060_SPI) += snd-soc-pcm3060-spi.o +obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o +obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o +obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o +obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o +obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o +obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o +obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o +obj-$(CONFIG_SND_SOC_PEB2466) += snd-soc-peb2466.o +obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o +obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o +obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o +obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o +obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o +obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o +obj-$(CONFIG_SND_SOC_RT1015P) += snd-soc-rt1015p.o +obj-$(CONFIG_SND_SOC_RT1016) += snd-soc-rt1016.o +obj-$(CONFIG_SND_SOC_RT1017_SDCA_SDW) += snd-soc-rt1017-sdca.o +obj-$(CONFIG_SND_SOC_RT1019) += snd-soc-rt1019.o +obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o +obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o +obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o +obj-$(CONFIG_SND_SOC_RT1316_SDW) += snd-soc-rt1316-sdw.o +obj-$(CONFIG_SND_SOC_RT1318_SDW) += snd-soc-rt1318-sdw.o +obj-$(CONFIG_SND_SOC_RT274) += snd-soc-rt274.o +obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o +obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o +obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o +obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o +obj-$(CONFIG_SND_SOC_RT5514_SPI_BUILTIN) += snd-soc-rt5514-spi.o +obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o +obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o +obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o +obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o +obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o +obj-$(CONFIG_SND_SOC_RT5659) += snd-soc-rt5659.o +obj-$(CONFIG_SND_SOC_RT5660) += snd-soc-rt5660.o +obj-$(CONFIG_SND_SOC_RT5663) += snd-soc-rt5663.o +obj-$(CONFIG_SND_SOC_RT5665) += snd-soc-rt5665.o +obj-$(CONFIG_SND_SOC_RT5668) += snd-soc-rt5668.o +obj-$(CONFIG_SND_SOC_RT5670) += snd-soc-rt5670.o +obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o +obj-$(CONFIG_SND_SOC_RT5677_SPI) += snd-soc-rt5677-spi.o +obj-$(CONFIG_SND_SOC_RT5682) += snd-soc-rt5682.o +obj-$(CONFIG_SND_SOC_RT5682_I2C) += snd-soc-rt5682-i2c.o +obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o +obj-$(CONFIG_SND_SOC_RT5682S) += snd-soc-rt5682s.o +obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o +obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o +obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW) += snd-soc-rt711-sdca.o +obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW) += snd-soc-rt712-sdca.o +obj-$(CONFIG_SND_SOC_RT712_SDCA_DMIC_SDW) += snd-soc-rt712-sdca-dmic.o +obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o +obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o +obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o +obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o +obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o +obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o +obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o +obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o +obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o +obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o +obj-$(CONFIG_SND_SOC_SMA1303) += snd-soc-sma1303.o +obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o +obj-$(CONFIG_SND_SOC_SRC4XXX) += snd-soc-src4xxx.o +obj-$(CONFIG_SND_SOC_SRC4XXX_I2C) += snd-soc-src4xxx-i2c.o +obj-$(CONFIG_SND_SOC_SSM2305) += snd-soc-ssm2305.o +obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o +obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o +obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o +obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o +obj-$(CONFIG_SND_SOC_SSM3515) += snd-soc-ssm3515.o +obj-$(CONFIG_SND_SOC_SSM4567) += snd-soc-ssm4567.o +obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o +obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o +obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o +obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o +obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o +obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o +obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o +obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o +obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o +obj-$(CONFIG_SND_SOC_TAS2781_COMLIB) += snd-soc-tas2781-comlib.o +obj-$(CONFIG_SND_SOC_TAS2781_FMWLIB) += snd-soc-tas2781-fmwlib.o +obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.o +obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o +obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o +obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o +obj-$(CONFIG_SND_SOC_TAS5805M) += snd-soc-tas5805m.o +obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o +obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o +obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o +obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o +obj-$(CONFIG_SND_SOC_TFA989X) += snd-soc-tfa989x.o +obj-$(CONFIG_SND_SOC_TLV320ADC3XXX) += snd-soc-tlv320adc3xxx.o +obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o +obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o +obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o +obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o +obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o +obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o +obj-$(CONFIG_SND_SOC_TLV320AIC32X4_I2C) += snd-soc-tlv320aic32x4-i2c.o +obj-$(CONFIG_SND_SOC_TLV320AIC32X4_SPI) += snd-soc-tlv320aic32x4-spi.o +obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o +obj-$(CONFIG_SND_SOC_TLV320AIC3X_I2C) += snd-soc-tlv320aic3x-i2c.o +obj-$(CONFIG_SND_SOC_TLV320AIC3X_SPI) += snd-soc-tlv320aic3x-spi.o +obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o +obj-$(CONFIG_SND_SOC_TLV320ADCX140) += snd-soc-tlv320adcx140.o +obj-$(CONFIG_SND_SOC_TSCS42XX) += snd-soc-tscs42xx.o +obj-$(CONFIG_SND_SOC_TSCS454) += snd-soc-tscs454.o +obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o +obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o +obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o +obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o +obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o +obj-$(CONFIG_SND_SOC_WCD_CLASSH) += snd-soc-wcd-classh.o +obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o +obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o +obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o +obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x.o +ifdef CONFIG_SND_SOC_WCD938X_SDW +# avoid link failure by forcing sdw code built-in when needed +obj-$(CONFIG_SND_SOC_WCD938X) += snd-soc-wcd938x-sdw.o +endif +obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o +obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o +obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o +obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o +obj-$(CONFIG_SND_SOC_WM2200) += snd-soc-wm2200.o +obj-$(CONFIG_SND_SOC_WM5100) += snd-soc-wm5100.o +obj-$(CONFIG_SND_SOC_WM5102) += snd-soc-wm5102.o +obj-$(CONFIG_SND_SOC_WM5110) += snd-soc-wm5110.o +obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o +obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o +obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o +obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o +obj-$(CONFIG_SND_SOC_WM8524) += snd-soc-wm8524.o +obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o +obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o +obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o +obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o +obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o +obj-$(CONFIG_SND_SOC_WM8731_I2C) += snd-soc-wm8731-i2c.o +obj-$(CONFIG_SND_SOC_WM8731_SPI) += snd-soc-wm8731-spi.o +obj-$(CONFIG_SND_SOC_WM8737) += snd-soc-wm8737.o +obj-$(CONFIG_SND_SOC_WM8741) += snd-soc-wm8741.o +obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o +obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o +obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o +obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o +obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o +obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o +obj-$(CONFIG_SND_SOC_WM8804_I2C) += snd-soc-wm8804-i2c.o +obj-$(CONFIG_SND_SOC_WM8804_SPI) += snd-soc-wm8804-spi.o +obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o +obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o +obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o +obj-$(CONFIG_SND_SOC_WM8996) += snd-soc-wm8996.o +obj-$(CONFIG_SND_SOC_WM8940) += snd-soc-wm8940.o +obj-$(CONFIG_SND_SOC_WM8955) += snd-soc-wm8955.o +obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o +obj-$(CONFIG_SND_SOC_WM8961) += snd-soc-wm8961.o +obj-$(CONFIG_SND_SOC_WM8962) += snd-soc-wm8962.o +obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o +obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o +obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o +obj-$(CONFIG_SND_SOC_WM8983) += snd-soc-wm8983.o +obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o +obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o +obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o +obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o +obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o +obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o +obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o +obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o +obj-$(CONFIG_SND_SOC_WM8998) += snd-soc-wm8998.o +obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o +obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o +obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o +obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o +obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o +obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o +obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o +obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o +obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o +obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o +obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o + +# Amp +obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o +obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o +obj-$(CONFIG_SND_SOC_SIMPLE_AMPLIFIER) += snd-soc-simple-amplifier.o +obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o +obj-$(CONFIG_SND_SOC_LPASS_MACRO_COMMON) += snd-soc-lpass-macro-common.o +obj-$(CONFIG_SND_SOC_LPASS_WSA_MACRO) += snd-soc-lpass-wsa-macro.o +obj-$(CONFIG_SND_SOC_LPASS_VA_MACRO) += snd-soc-lpass-va-macro.o +obj-$(CONFIG_SND_SOC_LPASS_RX_MACRO) += snd-soc-lpass-rx-macro.o +obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO) += snd-soc-lpass-tx-macro.o + +# Mux +obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c new file mode 100644 index 0000000000..6834291741 --- /dev/null +++ b/sound/soc/codecs/ab8500-codec.c @@ -0,0 +1,2574 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * Author: Ola Lilja , + * Kristoffer Karlsson , + * Roger Nilsson , + * for ST-Ericsson. + * + * Based on the early work done by: + * Mikko J. Lehto , + * Mikko Sarmanne , + * Jarmo K. Kuronen , + * for ST-Ericsson. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ab8500-codec.h" + +/* Macrocell value definitions */ +#define CLK_32K_OUT2_DISABLE 0x01 +#define INACTIVE_RESET_AUDIO 0x02 +#define ENABLE_AUDIO_CLK_TO_AUDIO_BLK 0x10 +#define ENABLE_VINTCORE12_SUPPLY 0x04 +#define GPIO27_DIR_OUTPUT 0x04 +#define GPIO29_DIR_OUTPUT 0x10 +#define GPIO31_DIR_OUTPUT 0x40 + +/* Macrocell register definitions */ +#define AB8500_GPIO_DIR4_REG 0x13 /* Bank AB8500_MISC */ + +/* Nr of FIR/IIR-coeff banks in ANC-block */ +#define AB8500_NR_OF_ANC_COEFF_BANKS 2 + +/* Minimum duration to keep ANC IIR Init bit high or +low before proceeding with the configuration sequence */ +#define AB8500_ANC_SM_DELAY 2000 + +#define AB8500_FILTER_CONTROL(xname, xcount, xmin, xmax) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = filter_control_info, \ + .get = filter_control_get, .put = filter_control_put, \ + .private_value = (unsigned long)&(struct filter_control) \ + {.count = xcount, .min = xmin, .max = xmax} } + +struct filter_control { + long min, max; + unsigned int count; + long value[128]; +}; + +/* Sidetone states */ +static const char * const enum_sid_state[] = { + "Unconfigured", + "Apply FIR", + "FIR is configured", +}; +enum sid_state { + SID_UNCONFIGURED = 0, + SID_APPLY_FIR = 1, + SID_FIR_CONFIGURED = 2, +}; + +static const char * const enum_anc_state[] = { + "Unconfigured", + "Apply FIR and IIR", + "FIR and IIR are configured", + "Apply FIR", + "FIR is configured", + "Apply IIR", + "IIR is configured" +}; +enum anc_state { + ANC_UNCONFIGURED = 0, + ANC_APPLY_FIR_IIR = 1, + ANC_FIR_IIR_CONFIGURED = 2, + ANC_APPLY_FIR = 3, + ANC_FIR_CONFIGURED = 4, + ANC_APPLY_IIR = 5, + ANC_IIR_CONFIGURED = 6 +}; + +/* Analog microphones */ +enum amic_idx { + AMIC_IDX_1A, + AMIC_IDX_1B, + AMIC_IDX_2 +}; + +/* Private data for AB8500 device-driver */ +struct ab8500_codec_drvdata { + struct regmap *regmap; + struct mutex ctrl_lock; + + /* Sidetone */ + long *sid_fir_values; + enum sid_state sid_status; + + /* ANC */ + long *anc_fir_values; + long *anc_iir_values; + enum anc_state anc_status; +}; + +static inline const char *amic_micbias_str(enum amic_micbias micbias) +{ + switch (micbias) { + case AMIC_MICBIAS_VAMIC1: + return "VAMIC1"; + case AMIC_MICBIAS_VAMIC2: + return "VAMIC2"; + default: + return "Unknown"; + } +} + +static inline const char *amic_type_str(enum amic_type type) +{ + switch (type) { + case AMIC_TYPE_DIFFERENTIAL: + return "DIFFERENTIAL"; + case AMIC_TYPE_SINGLE_ENDED: + return "SINGLE ENDED"; + default: + return "Unknown"; + } +} + +/* + * Read'n'write functions + */ + +/* Read a register from the audio-bank of AB8500 */ +static int ab8500_codec_read_reg(void *context, unsigned int reg, + unsigned int *value) +{ + struct device *dev = context; + int status; + + u8 value8; + status = abx500_get_register_interruptible(dev, AB8500_AUDIO, + reg, &value8); + *value = (unsigned int)value8; + + return status; +} + +/* Write to a register in the audio-bank of AB8500 */ +static int ab8500_codec_write_reg(void *context, unsigned int reg, + unsigned int value) +{ + struct device *dev = context; + + return abx500_set_register_interruptible(dev, AB8500_AUDIO, + reg, value); +} + +static const struct regmap_config ab8500_codec_regmap = { + .reg_read = ab8500_codec_read_reg, + .reg_write = ab8500_codec_write_reg, +}; + +/* + * Controls - DAPM + */ + +/* Earpiece */ + +/* Earpiece source selector */ +static const char * const enum_ear_lineout_source[] = {"Headset Left", + "Speaker Left"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ear_lineout_source, AB8500_DMICFILTCONF, + AB8500_DMICFILTCONF_DA3TOEAR, enum_ear_lineout_source); +static const struct snd_kcontrol_new dapm_ear_lineout_source = + SOC_DAPM_ENUM("Earpiece or LineOut Mono Source", + dapm_enum_ear_lineout_source); + +/* LineOut */ + +/* LineOut source selector */ +static const char * const enum_lineout_source[] = {"Mono Path", "Stereo Path"}; +static SOC_ENUM_DOUBLE_DECL(dapm_enum_lineout_source, AB8500_ANACONF5, + AB8500_ANACONF5_HSLDACTOLOL, + AB8500_ANACONF5_HSRDACTOLOR, enum_lineout_source); +static const struct snd_kcontrol_new dapm_lineout_source[] = { + SOC_DAPM_ENUM("LineOut Source", dapm_enum_lineout_source), +}; + +/* Handsfree */ + +/* Speaker Left - ANC selector */ +static const char * const enum_HFx_sel[] = {"Audio Path", "ANC"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_HFl_sel, AB8500_DIGMULTCONF2, + AB8500_DIGMULTCONF2_HFLSEL, enum_HFx_sel); +static const struct snd_kcontrol_new dapm_HFl_select[] = { + SOC_DAPM_ENUM("Speaker Left Source", dapm_enum_HFl_sel), +}; + +/* Speaker Right - ANC selector */ +static SOC_ENUM_SINGLE_DECL(dapm_enum_HFr_sel, AB8500_DIGMULTCONF2, + AB8500_DIGMULTCONF2_HFRSEL, enum_HFx_sel); +static const struct snd_kcontrol_new dapm_HFr_select[] = { + SOC_DAPM_ENUM("Speaker Right Source", dapm_enum_HFr_sel), +}; + +/* Mic 1 */ + +/* Mic 1 - Mic 1a or 1b selector */ +static const char * const enum_mic1ab_sel[] = {"Mic 1b", "Mic 1a"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_mic1ab_sel, AB8500_ANACONF3, + AB8500_ANACONF3_MIC1SEL, enum_mic1ab_sel); +static const struct snd_kcontrol_new dapm_mic1ab_mux[] = { + SOC_DAPM_ENUM("Mic 1a or 1b Select", dapm_enum_mic1ab_sel), +}; + +/* Mic 1 - AD3 - Mic 1 or DMic 3 selector */ +static const char * const enum_ad3_sel[] = {"Mic 1", "DMic 3"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ad3_sel, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_AD3SEL, enum_ad3_sel); +static const struct snd_kcontrol_new dapm_ad3_select[] = { + SOC_DAPM_ENUM("AD3 Source Select", dapm_enum_ad3_sel), +}; + +/* Mic 1 - AD6 - Mic 1 or DMic 6 selector */ +static const char * const enum_ad6_sel[] = {"Mic 1", "DMic 6"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ad6_sel, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_AD6SEL, enum_ad6_sel); +static const struct snd_kcontrol_new dapm_ad6_select[] = { + SOC_DAPM_ENUM("AD6 Source Select", dapm_enum_ad6_sel), +}; + +/* Mic 2 */ + +/* Mic 2 - AD5 - Mic 2 or DMic 5 selector */ +static const char * const enum_ad5_sel[] = {"Mic 2", "DMic 5"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ad5_sel, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_AD5SEL, enum_ad5_sel); +static const struct snd_kcontrol_new dapm_ad5_select[] = { + SOC_DAPM_ENUM("AD5 Source Select", dapm_enum_ad5_sel), +}; + +/* LineIn */ + +/* LineIn left - AD1 - LineIn Left or DMic 1 selector */ +static const char * const enum_ad1_sel[] = {"LineIn Left", "DMic 1"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ad1_sel, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_AD1SEL, enum_ad1_sel); +static const struct snd_kcontrol_new dapm_ad1_select[] = { + SOC_DAPM_ENUM("AD1 Source Select", dapm_enum_ad1_sel), +}; + +/* LineIn right - Mic 2 or LineIn Right selector */ +static const char * const enum_mic2lr_sel[] = {"Mic 2", "LineIn Right"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_mic2lr_sel, AB8500_ANACONF3, + AB8500_ANACONF3_LINRSEL, enum_mic2lr_sel); +static const struct snd_kcontrol_new dapm_mic2lr_select[] = { + SOC_DAPM_ENUM("Mic 2 or LINR Select", dapm_enum_mic2lr_sel), +}; + +/* LineIn right - AD2 - LineIn Right or DMic2 selector */ +static const char * const enum_ad2_sel[] = {"LineIn Right", "DMic 2"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_ad2_sel, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_AD2SEL, enum_ad2_sel); +static const struct snd_kcontrol_new dapm_ad2_select[] = { + SOC_DAPM_ENUM("AD2 Source Select", dapm_enum_ad2_sel), +}; + + +/* ANC */ + +static const char * const enum_anc_in_sel[] = {"Mic 1 / DMic 6", + "Mic 2 / DMic 5"}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_anc_in_sel, AB8500_DMICFILTCONF, + AB8500_DMICFILTCONF_ANCINSEL, enum_anc_in_sel); +static const struct snd_kcontrol_new dapm_anc_in_select[] = { + SOC_DAPM_ENUM("ANC Source", dapm_enum_anc_in_sel), +}; + +/* ANC - Enable/Disable */ +static const struct snd_kcontrol_new dapm_anc_enable[] = { + SOC_DAPM_SINGLE("Switch", AB8500_ANCCONF1, + AB8500_ANCCONF1_ENANC, 0, 0), +}; + +/* ANC to Earpiece - Mute */ +static const struct snd_kcontrol_new dapm_anc_ear_mute[] = { + SOC_DAPM_SINGLE("Switch", AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_ANCSEL, 1, 0), +}; + + + +/* Sidetone left */ + +/* Sidetone left - Input selector */ +static const char * const enum_stfir1_in_sel[] = { + "LineIn Left", "LineIn Right", "Mic 1", "Headset Left" +}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_stfir1_in_sel, AB8500_DIGMULTCONF2, + AB8500_DIGMULTCONF2_FIRSID1SEL, enum_stfir1_in_sel); +static const struct snd_kcontrol_new dapm_stfir1_in_select[] = { + SOC_DAPM_ENUM("Sidetone Left Source", dapm_enum_stfir1_in_sel), +}; + +/* Sidetone right path */ + +/* Sidetone right - Input selector */ +static const char * const enum_stfir2_in_sel[] = { + "LineIn Right", "Mic 1", "DMic 4", "Headset Right" +}; +static SOC_ENUM_SINGLE_DECL(dapm_enum_stfir2_in_sel, AB8500_DIGMULTCONF2, + AB8500_DIGMULTCONF2_FIRSID2SEL, enum_stfir2_in_sel); +static const struct snd_kcontrol_new dapm_stfir2_in_select[] = { + SOC_DAPM_ENUM("Sidetone Right Source", dapm_enum_stfir2_in_sel), +}; + +/* Vibra */ + +static const char * const enum_pwm2vibx[] = {"Audio Path", "PWM Generator"}; + +static SOC_ENUM_SINGLE_DECL(dapm_enum_pwm2vib1, AB8500_PWMGENCONF1, + AB8500_PWMGENCONF1_PWMTOVIB1, enum_pwm2vibx); + +static const struct snd_kcontrol_new dapm_pwm2vib1[] = { + SOC_DAPM_ENUM("Vibra 1 Controller", dapm_enum_pwm2vib1), +}; + +static SOC_ENUM_SINGLE_DECL(dapm_enum_pwm2vib2, AB8500_PWMGENCONF1, + AB8500_PWMGENCONF1_PWMTOVIB2, enum_pwm2vibx); + +static const struct snd_kcontrol_new dapm_pwm2vib2[] = { + SOC_DAPM_ENUM("Vibra 2 Controller", dapm_enum_pwm2vib2), +}; + +/* + * DAPM-widgets + */ + +static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = { + + /* Clocks */ + SND_SOC_DAPM_CLOCK_SUPPLY("audioclk"), + + /* Regulators */ + SND_SOC_DAPM_REGULATOR_SUPPLY("V-AUD", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC1", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC2", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("V-DMIC", 0, 0), + + /* Power */ + SND_SOC_DAPM_SUPPLY("Audio Power", + AB8500_POWERUP, AB8500_POWERUP_POWERUP, 0, + NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("Audio Analog Power", + AB8500_POWERUP, AB8500_POWERUP_ENANA, 0, + NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Main supply node */ + SND_SOC_DAPM_SUPPLY("Main Supply", SND_SOC_NOPM, 0, 0, + NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* DA/AD */ + + SND_SOC_DAPM_INPUT("ADC Input"), + SND_SOC_DAPM_ADC("ADC", "ab8500_0c", SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("DAC Output"), + + SND_SOC_DAPM_AIF_IN("DA_IN1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DA_IN2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DA_IN3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DA_IN4", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DA_IN5", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DA_IN6", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT4", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT57", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AD_OUT68", NULL, 0, SND_SOC_NOPM, 0, 0), + + /* Headset path */ + + SND_SOC_DAPM_SUPPLY("Charge Pump", AB8500_ANACONF5, + AB8500_ANACONF5_ENCPHS, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DA1 Enable", "ab8500_0p", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA1, 0), + SND_SOC_DAPM_DAC("DA2 Enable", "ab8500_0p", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA2, 0), + + SND_SOC_DAPM_PGA("HSL Digital Volume", SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_PGA("HSR Digital Volume", SND_SOC_NOPM, 0, 0, + NULL, 0), + + SND_SOC_DAPM_DAC("HSL DAC", "ab8500_0p", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHSL, 0), + SND_SOC_DAPM_DAC("HSR DAC", "ab8500_0p", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHSR, 0), + SND_SOC_DAPM_MIXER("HSL DAC Mute", AB8500_MUTECONF, + AB8500_MUTECONF_MUTDACHSL, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("HSR DAC Mute", AB8500_MUTECONF, + AB8500_MUTECONF_MUTDACHSR, 1, + NULL, 0), + SND_SOC_DAPM_DAC("HSL DAC Driver", "ab8500_0p", + AB8500_ANACONF3, AB8500_ANACONF3_ENDRVHSL, 0), + SND_SOC_DAPM_DAC("HSR DAC Driver", "ab8500_0p", + AB8500_ANACONF3, AB8500_ANACONF3_ENDRVHSR, 0), + + SND_SOC_DAPM_MIXER("HSL Mute", + AB8500_MUTECONF, AB8500_MUTECONF_MUTHSL, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("HSR Mute", + AB8500_MUTECONF, AB8500_MUTECONF_MUTHSR, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("HSL Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENHSL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("HSR Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENHSR, 0, + NULL, 0), + SND_SOC_DAPM_PGA("HSL Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_PGA("HSR Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("Headset Left"), + SND_SOC_DAPM_OUTPUT("Headset Right"), + + /* LineOut path */ + + SND_SOC_DAPM_MUX("LineOut Source", + SND_SOC_NOPM, 0, 0, dapm_lineout_source), + + SND_SOC_DAPM_MIXER("LOL Disable HFL", + AB8500_ANACONF4, AB8500_ANACONF4_ENHFL, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("LOR Disable HFR", + AB8500_ANACONF4, AB8500_ANACONF4_ENHFR, 1, + NULL, 0), + + SND_SOC_DAPM_MIXER("LOL Enable", + AB8500_ANACONF5, AB8500_ANACONF5_ENLOL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("LOR Enable", + AB8500_ANACONF5, AB8500_ANACONF5_ENLOR, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("LineOut Left"), + SND_SOC_DAPM_OUTPUT("LineOut Right"), + + /* Earpiece path */ + + SND_SOC_DAPM_MUX("Earpiece or LineOut Mono Source", + SND_SOC_NOPM, 0, 0, &dapm_ear_lineout_source), + SND_SOC_DAPM_MIXER("EAR DAC", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACEAR, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("EAR Mute", + AB8500_MUTECONF, AB8500_MUTECONF_MUTEAR, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("EAR Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENEAR, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("Earpiece"), + + /* Handsfree path */ + + SND_SOC_DAPM_MIXER("DA3 Channel Volume", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA3, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DA4 Channel Volume", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA4, 0, + NULL, 0), + SND_SOC_DAPM_MUX("Speaker Left Source", + SND_SOC_NOPM, 0, 0, dapm_HFl_select), + SND_SOC_DAPM_MUX("Speaker Right Source", + SND_SOC_NOPM, 0, 0, dapm_HFr_select), + SND_SOC_DAPM_MIXER("HFL DAC", AB8500_DAPATHCONF, + AB8500_DAPATHCONF_ENDACHFL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("HFR DAC", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHFR, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DA4 or ANC path to HfR", + AB8500_DIGMULTCONF2, AB8500_DIGMULTCONF2_DATOHFREN, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DA3 or ANC path to HfL", + AB8500_DIGMULTCONF2, AB8500_DIGMULTCONF2_DATOHFLEN, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("HFL Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENHFL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("HFR Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENHFR, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("Speaker Left"), + SND_SOC_DAPM_OUTPUT("Speaker Right"), + + /* Vibrator path */ + + SND_SOC_DAPM_INPUT("PWMGEN1"), + SND_SOC_DAPM_INPUT("PWMGEN2"), + + SND_SOC_DAPM_MIXER("DA5 Channel Volume", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA5, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DA6 Channel Volume", + AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA6, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("VIB1 DAC", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACVIB1, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("VIB2 DAC", + AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACVIB2, 0, + NULL, 0), + SND_SOC_DAPM_MUX("Vibra 1 Controller", + SND_SOC_NOPM, 0, 0, dapm_pwm2vib1), + SND_SOC_DAPM_MUX("Vibra 2 Controller", + SND_SOC_NOPM, 0, 0, dapm_pwm2vib2), + SND_SOC_DAPM_MIXER("VIB1 Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENVIB1, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("VIB2 Enable", + AB8500_ANACONF4, AB8500_ANACONF4_ENVIB2, 0, + NULL, 0), + + SND_SOC_DAPM_OUTPUT("Vibra 1"), + SND_SOC_DAPM_OUTPUT("Vibra 2"), + + /* Mic 1 */ + + SND_SOC_DAPM_INPUT("Mic 1"), + + SND_SOC_DAPM_MUX("Mic 1a or 1b Select", + SND_SOC_NOPM, 0, 0, dapm_mic1ab_mux), + SND_SOC_DAPM_MIXER("MIC1 Mute", + AB8500_ANACONF2, AB8500_ANACONF2_MUTMIC1, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("MIC1A V-AMICx Enable", + AB8500_ANACONF2, AB8500_ANACONF2_ENMIC1, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("MIC1B V-AMICx Enable", + AB8500_ANACONF2, AB8500_ANACONF2_ENMIC1, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("MIC1 ADC", + AB8500_ANACONF3, AB8500_ANACONF3_ENADCMIC, 0, + NULL, 0), + SND_SOC_DAPM_MUX("AD3 Source Select", + SND_SOC_NOPM, 0, 0, dapm_ad3_select), + SND_SOC_DAPM_MIXER("AD3 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD3 Enable", + AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34, 0, + NULL, 0), + + /* Mic 2 */ + + SND_SOC_DAPM_INPUT("Mic 2"), + + SND_SOC_DAPM_MIXER("MIC2 Mute", + AB8500_ANACONF2, AB8500_ANACONF2_MUTMIC2, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("MIC2 V-AMICx Enable", AB8500_ANACONF2, + AB8500_ANACONF2_ENMIC2, 0, + NULL, 0), + + /* LineIn */ + + SND_SOC_DAPM_INPUT("LineIn Left"), + SND_SOC_DAPM_INPUT("LineIn Right"), + + SND_SOC_DAPM_MIXER("LINL Mute", + AB8500_ANACONF2, AB8500_ANACONF2_MUTLINL, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("LINR Mute", + AB8500_ANACONF2, AB8500_ANACONF2_MUTLINR, 1, + NULL, 0), + SND_SOC_DAPM_MIXER("LINL Enable", AB8500_ANACONF2, + AB8500_ANACONF2_ENLINL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("LINR Enable", AB8500_ANACONF2, + AB8500_ANACONF2_ENLINR, 0, + NULL, 0), + + /* LineIn Bypass path */ + SND_SOC_DAPM_MIXER("LINL to HSL Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("LINR to HSR Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + + /* LineIn, Mic 2 */ + SND_SOC_DAPM_MUX("Mic 2 or LINR Select", + SND_SOC_NOPM, 0, 0, dapm_mic2lr_select), + SND_SOC_DAPM_MIXER("LINL ADC", AB8500_ANACONF3, + AB8500_ANACONF3_ENADCLINL, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("LINR ADC", AB8500_ANACONF3, + AB8500_ANACONF3_ENADCLINR, 0, + NULL, 0), + SND_SOC_DAPM_MUX("AD1 Source Select", + SND_SOC_NOPM, 0, 0, dapm_ad1_select), + SND_SOC_DAPM_MUX("AD2 Source Select", + SND_SOC_NOPM, 0, 0, dapm_ad2_select), + SND_SOC_DAPM_MIXER("AD1 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD2 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + + SND_SOC_DAPM_MIXER("AD12 Enable", + AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD12, 0, + NULL, 0), + + /* HD Capture path */ + + SND_SOC_DAPM_MUX("AD5 Source Select", + SND_SOC_NOPM, 0, 0, dapm_ad5_select), + SND_SOC_DAPM_MUX("AD6 Source Select", + SND_SOC_NOPM, 0, 0, dapm_ad6_select), + SND_SOC_DAPM_MIXER("AD5 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD6 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD57 Enable", + AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD68 Enable", + AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0, + NULL, 0), + + /* Digital Microphone path */ + + SND_SOC_DAPM_INPUT("DMic 1"), + SND_SOC_DAPM_INPUT("DMic 2"), + SND_SOC_DAPM_INPUT("DMic 3"), + SND_SOC_DAPM_INPUT("DMic 4"), + SND_SOC_DAPM_INPUT("DMic 5"), + SND_SOC_DAPM_INPUT("DMic 6"), + + SND_SOC_DAPM_MIXER("DMIC1", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC1, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DMIC2", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC2, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DMIC3", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC3, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DMIC4", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC4, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DMIC5", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC5, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("DMIC6", + AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC6, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD4 Channel Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("AD4 Enable", + AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34, + 0, NULL, 0), + + /* Acoustical Noise Cancellation path */ + + SND_SOC_DAPM_INPUT("ANC Configure Input"), + SND_SOC_DAPM_OUTPUT("ANC Configure Output"), + + SND_SOC_DAPM_MUX("ANC Source", + SND_SOC_NOPM, 0, 0, + dapm_anc_in_select), + SND_SOC_DAPM_SWITCH("ANC", + SND_SOC_NOPM, 0, 0, + dapm_anc_enable), + SND_SOC_DAPM_SWITCH("ANC to Earpiece", + SND_SOC_NOPM, 0, 0, + dapm_anc_ear_mute), + + /* Sidetone Filter path */ + + SND_SOC_DAPM_MUX("Sidetone Left Source", + SND_SOC_NOPM, 0, 0, + dapm_stfir1_in_select), + SND_SOC_DAPM_MUX("Sidetone Right Source", + SND_SOC_NOPM, 0, 0, + dapm_stfir2_in_select), + SND_SOC_DAPM_MIXER("STFIR1 Control", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("STFIR2 Control", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("STFIR1 Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("STFIR2 Volume", + SND_SOC_NOPM, 0, 0, + NULL, 0), +}; + +/* + * DAPM-routes + */ +static const struct snd_soc_dapm_route ab8500_dapm_routes[] = { + /* Power AB8500 audio-block when AD/DA is active */ + {"Main Supply", NULL, "V-AUD"}, + {"Main Supply", NULL, "audioclk"}, + {"Main Supply", NULL, "Audio Power"}, + {"Main Supply", NULL, "Audio Analog Power"}, + + {"DAC", NULL, "ab8500_0p"}, + {"DAC", NULL, "Main Supply"}, + {"ADC", NULL, "ab8500_0c"}, + {"ADC", NULL, "Main Supply"}, + + /* ANC Configure */ + {"ANC Configure Input", NULL, "Main Supply"}, + {"ANC Configure Output", NULL, "ANC Configure Input"}, + + /* AD/DA */ + {"ADC", NULL, "ADC Input"}, + {"DAC Output", NULL, "DAC"}, + + /* Powerup charge pump if DA1/2 is in use */ + + {"DA_IN1", NULL, "ab8500_0p"}, + {"DA_IN1", NULL, "Charge Pump"}, + {"DA_IN2", NULL, "ab8500_0p"}, + {"DA_IN2", NULL, "Charge Pump"}, + + /* Headset path */ + + {"DA1 Enable", NULL, "DA_IN1"}, + {"DA2 Enable", NULL, "DA_IN2"}, + + {"HSL Digital Volume", NULL, "DA1 Enable"}, + {"HSR Digital Volume", NULL, "DA2 Enable"}, + + {"HSL DAC", NULL, "HSL Digital Volume"}, + {"HSR DAC", NULL, "HSR Digital Volume"}, + + {"HSL DAC Mute", NULL, "HSL DAC"}, + {"HSR DAC Mute", NULL, "HSR DAC"}, + + {"HSL DAC Driver", NULL, "HSL DAC Mute"}, + {"HSR DAC Driver", NULL, "HSR DAC Mute"}, + + {"HSL Mute", NULL, "HSL DAC Driver"}, + {"HSR Mute", NULL, "HSR DAC Driver"}, + + {"HSL Enable", NULL, "HSL Mute"}, + {"HSR Enable", NULL, "HSR Mute"}, + + {"HSL Volume", NULL, "HSL Enable"}, + {"HSR Volume", NULL, "HSR Enable"}, + + {"Headset Left", NULL, "HSL Volume"}, + {"Headset Right", NULL, "HSR Volume"}, + + /* HF or LineOut path */ + + {"DA_IN3", NULL, "ab8500_0p"}, + {"DA3 Channel Volume", NULL, "DA_IN3"}, + {"DA_IN4", NULL, "ab8500_0p"}, + {"DA4 Channel Volume", NULL, "DA_IN4"}, + + {"Speaker Left Source", "Audio Path", "DA3 Channel Volume"}, + {"Speaker Right Source", "Audio Path", "DA4 Channel Volume"}, + + {"DA3 or ANC path to HfL", NULL, "Speaker Left Source"}, + {"DA4 or ANC path to HfR", NULL, "Speaker Right Source"}, + + /* HF path */ + + {"HFL DAC", NULL, "DA3 or ANC path to HfL"}, + {"HFR DAC", NULL, "DA4 or ANC path to HfR"}, + + {"HFL Enable", NULL, "HFL DAC"}, + {"HFR Enable", NULL, "HFR DAC"}, + + {"Speaker Left", NULL, "HFL Enable"}, + {"Speaker Right", NULL, "HFR Enable"}, + + /* Earpiece path */ + + {"Earpiece or LineOut Mono Source", "Headset Left", + "HSL Digital Volume"}, + {"Earpiece or LineOut Mono Source", "Speaker Left", + "DA3 or ANC path to HfL"}, + + {"EAR DAC", NULL, "Earpiece or LineOut Mono Source"}, + + {"EAR Mute", NULL, "EAR DAC"}, + + {"EAR Enable", NULL, "EAR Mute"}, + + {"Earpiece", NULL, "EAR Enable"}, + + /* LineOut path stereo */ + + {"LineOut Source", "Stereo Path", "HSL DAC Driver"}, + {"LineOut Source", "Stereo Path", "HSR DAC Driver"}, + + /* LineOut path mono */ + + {"LineOut Source", "Mono Path", "EAR DAC"}, + + /* LineOut path */ + + {"LOL Disable HFL", NULL, "LineOut Source"}, + {"LOR Disable HFR", NULL, "LineOut Source"}, + + {"LOL Enable", NULL, "LOL Disable HFL"}, + {"LOR Enable", NULL, "LOR Disable HFR"}, + + {"LineOut Left", NULL, "LOL Enable"}, + {"LineOut Right", NULL, "LOR Enable"}, + + /* Vibrator path */ + + {"DA_IN5", NULL, "ab8500_0p"}, + {"DA5 Channel Volume", NULL, "DA_IN5"}, + {"DA_IN6", NULL, "ab8500_0p"}, + {"DA6 Channel Volume", NULL, "DA_IN6"}, + + {"VIB1 DAC", NULL, "DA5 Channel Volume"}, + {"VIB2 DAC", NULL, "DA6 Channel Volume"}, + + {"Vibra 1 Controller", "Audio Path", "VIB1 DAC"}, + {"Vibra 2 Controller", "Audio Path", "VIB2 DAC"}, + {"Vibra 1 Controller", "PWM Generator", "PWMGEN1"}, + {"Vibra 2 Controller", "PWM Generator", "PWMGEN2"}, + + {"VIB1 Enable", NULL, "Vibra 1 Controller"}, + {"VIB2 Enable", NULL, "Vibra 2 Controller"}, + + {"Vibra 1", NULL, "VIB1 Enable"}, + {"Vibra 2", NULL, "VIB2 Enable"}, + + + /* Mic 2 */ + + {"MIC2 V-AMICx Enable", NULL, "Mic 2"}, + + /* LineIn */ + {"LINL Mute", NULL, "LineIn Left"}, + {"LINR Mute", NULL, "LineIn Right"}, + + {"LINL Enable", NULL, "LINL Mute"}, + {"LINR Enable", NULL, "LINR Mute"}, + + /* LineIn, Mic 2 */ + {"Mic 2 or LINR Select", "LineIn Right", "LINR Enable"}, + {"Mic 2 or LINR Select", "Mic 2", "MIC2 V-AMICx Enable"}, + + {"LINL ADC", NULL, "LINL Enable"}, + {"LINR ADC", NULL, "Mic 2 or LINR Select"}, + + {"AD1 Source Select", "LineIn Left", "LINL ADC"}, + {"AD2 Source Select", "LineIn Right", "LINR ADC"}, + + {"AD1 Channel Volume", NULL, "AD1 Source Select"}, + {"AD2 Channel Volume", NULL, "AD2 Source Select"}, + + {"AD12 Enable", NULL, "AD1 Channel Volume"}, + {"AD12 Enable", NULL, "AD2 Channel Volume"}, + + {"AD_OUT1", NULL, "ab8500_0c"}, + {"AD_OUT1", NULL, "AD12 Enable"}, + {"AD_OUT2", NULL, "ab8500_0c"}, + {"AD_OUT2", NULL, "AD12 Enable"}, + + /* Mic 1 */ + + {"MIC1 Mute", NULL, "Mic 1"}, + + {"MIC1A V-AMICx Enable", NULL, "MIC1 Mute"}, + {"MIC1B V-AMICx Enable", NULL, "MIC1 Mute"}, + + {"Mic 1a or 1b Select", "Mic 1a", "MIC1A V-AMICx Enable"}, + {"Mic 1a or 1b Select", "Mic 1b", "MIC1B V-AMICx Enable"}, + + {"MIC1 ADC", NULL, "Mic 1a or 1b Select"}, + + {"AD3 Source Select", "Mic 1", "MIC1 ADC"}, + + {"AD3 Channel Volume", NULL, "AD3 Source Select"}, + + {"AD3 Enable", NULL, "AD3 Channel Volume"}, + + {"AD_OUT3", NULL, "ab8500_0c"}, + {"AD_OUT3", NULL, "AD3 Enable"}, + + /* HD Capture path */ + + {"AD5 Source Select", "Mic 2", "LINR ADC"}, + {"AD6 Source Select", "Mic 1", "MIC1 ADC"}, + + {"AD5 Channel Volume", NULL, "AD5 Source Select"}, + {"AD6 Channel Volume", NULL, "AD6 Source Select"}, + + {"AD57 Enable", NULL, "AD5 Channel Volume"}, + {"AD68 Enable", NULL, "AD6 Channel Volume"}, + + {"AD_OUT57", NULL, "ab8500_0c"}, + {"AD_OUT57", NULL, "AD57 Enable"}, + {"AD_OUT68", NULL, "ab8500_0c"}, + {"AD_OUT68", NULL, "AD68 Enable"}, + + /* Digital Microphone path */ + + {"DMic 1", NULL, "V-DMIC"}, + {"DMic 2", NULL, "V-DMIC"}, + {"DMic 3", NULL, "V-DMIC"}, + {"DMic 4", NULL, "V-DMIC"}, + {"DMic 5", NULL, "V-DMIC"}, + {"DMic 6", NULL, "V-DMIC"}, + + {"AD1 Source Select", NULL, "DMic 1"}, + {"AD2 Source Select", NULL, "DMic 2"}, + {"AD3 Source Select", NULL, "DMic 3"}, + {"AD5 Source Select", NULL, "DMic 5"}, + {"AD6 Source Select", NULL, "DMic 6"}, + + {"AD4 Channel Volume", NULL, "DMic 4"}, + {"AD4 Enable", NULL, "AD4 Channel Volume"}, + + {"AD_OUT4", NULL, "ab8500_0c"}, + {"AD_OUT4", NULL, "AD4 Enable"}, + + /* LineIn Bypass path */ + + {"LINL to HSL Volume", NULL, "LINL Enable"}, + {"LINR to HSR Volume", NULL, "LINR Enable"}, + + {"HSL DAC Driver", NULL, "LINL to HSL Volume"}, + {"HSR DAC Driver", NULL, "LINR to HSR Volume"}, + + /* ANC path (Acoustic Noise Cancellation) */ + + {"ANC Source", "Mic 2 / DMic 5", "AD5 Channel Volume"}, + {"ANC Source", "Mic 1 / DMic 6", "AD6 Channel Volume"}, + + {"ANC", "Switch", "ANC Source"}, + + {"Speaker Left Source", "ANC", "ANC"}, + {"Speaker Right Source", "ANC", "ANC"}, + {"ANC to Earpiece", "Switch", "ANC"}, + + {"HSL Digital Volume", NULL, "ANC to Earpiece"}, + + /* Sidetone Filter path */ + + {"Sidetone Left Source", "LineIn Left", "AD12 Enable"}, + {"Sidetone Left Source", "LineIn Right", "AD12 Enable"}, + {"Sidetone Left Source", "Mic 1", "AD3 Enable"}, + {"Sidetone Left Source", "Headset Left", "DA_IN1"}, + {"Sidetone Right Source", "LineIn Right", "AD12 Enable"}, + {"Sidetone Right Source", "Mic 1", "AD3 Enable"}, + {"Sidetone Right Source", "DMic 4", "AD4 Enable"}, + {"Sidetone Right Source", "Headset Right", "DA_IN2"}, + + {"STFIR1 Control", NULL, "Sidetone Left Source"}, + {"STFIR2 Control", NULL, "Sidetone Right Source"}, + + {"STFIR1 Volume", NULL, "STFIR1 Control"}, + {"STFIR2 Volume", NULL, "STFIR2 Control"}, + + {"DA1 Enable", NULL, "STFIR1 Volume"}, + {"DA2 Enable", NULL, "STFIR2 Volume"}, +}; + +static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1a_vamicx[] = { + {"MIC1A V-AMICx Enable", NULL, "V-AMIC1"}, + {"MIC1A V-AMICx Enable", NULL, "V-AMIC2"}, +}; + +static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1b_vamicx[] = { + {"MIC1B V-AMICx Enable", NULL, "V-AMIC1"}, + {"MIC1B V-AMICx Enable", NULL, "V-AMIC2"}, +}; + +static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = { + {"MIC2 V-AMICx Enable", NULL, "V-AMIC1"}, + {"MIC2 V-AMICx Enable", NULL, "V-AMIC2"}, +}; + +/* ANC FIR-coefficients configuration sequence */ +static void anc_fir(struct snd_soc_component *component, + unsigned int bnk, unsigned int par, unsigned int val) +{ + if (par == 0 && bnk == 0) + snd_soc_component_update_bits(component, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCFIRUPDATE), + BIT(AB8500_ANCCONF1_ANCFIRUPDATE)); + + snd_soc_component_write(component, AB8500_ANCCONF5, val >> 8 & 0xff); + snd_soc_component_write(component, AB8500_ANCCONF6, val & 0xff); + + if (par == AB8500_ANC_FIR_COEFFS - 1 && bnk == 1) + snd_soc_component_update_bits(component, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCFIRUPDATE), 0); +} + +/* ANC IIR-coefficients configuration sequence */ +static void anc_iir(struct snd_soc_component *component, unsigned int bnk, + unsigned int par, unsigned int val) +{ + if (par == 0) { + if (bnk == 0) { + snd_soc_component_update_bits(component, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCIIRINIT), + BIT(AB8500_ANCCONF1_ANCIIRINIT)); + usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2); + snd_soc_component_update_bits(component, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCIIRINIT), 0); + usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2); + } else { + snd_soc_component_update_bits(component, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCIIRUPDATE), + BIT(AB8500_ANCCONF1_ANCIIRUPDATE)); + } + } else if (par > 3) { + snd_soc_component_write(component, AB8500_ANCCONF7, 0); + snd_soc_component_write(component, AB8500_ANCCONF8, val >> 16 & 0xff); + } + + snd_soc_component_write(component, AB8500_ANCCONF7, val >> 8 & 0xff); + snd_soc_component_write(component, AB8500_ANCCONF8, val & 0xff); + + if (par == AB8500_ANC_IIR_COEFFS - 1 && bnk == 1) + snd_soc_component_update_bits(component, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ANCIIRUPDATE), 0); +} + +/* ANC IIR-/FIR-coefficients configuration sequence */ +static void anc_configure(struct snd_soc_component *component, + bool apply_fir, bool apply_iir) +{ + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); + unsigned int bnk, par, val; + + dev_dbg(component->dev, "%s: Enter.\n", __func__); + + if (apply_fir) + snd_soc_component_update_bits(component, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ENANC), 0); + + snd_soc_component_update_bits(component, AB8500_ANCCONF1, + BIT(AB8500_ANCCONF1_ENANC), BIT(AB8500_ANCCONF1_ENANC)); + + if (apply_fir) + for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++) + for (par = 0; par < AB8500_ANC_FIR_COEFFS; par++) { + val = snd_soc_component_read(component, + drvdata->anc_fir_values[par]); + anc_fir(component, bnk, par, val); + } + + if (apply_iir) + for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++) + for (par = 0; par < AB8500_ANC_IIR_COEFFS; par++) { + val = snd_soc_component_read(component, + drvdata->anc_iir_values[par]); + anc_iir(component, bnk, par, val); + } + + dev_dbg(component->dev, "%s: Exit.\n", __func__); +} + +/* + * Control-events + */ + +static int sid_status_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); + + mutex_lock(&drvdata->ctrl_lock); + ucontrol->value.enumerated.item[0] = drvdata->sid_status; + mutex_unlock(&drvdata->ctrl_lock); + + return 0; +} + +/* Write sidetone FIR-coefficients configuration sequence */ +static int sid_status_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); + unsigned int param, sidconf, val; + int status = 1; + + dev_dbg(component->dev, "%s: Enter\n", __func__); + + if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) { + dev_err(component->dev, + "%s: ERROR: This control supports '%s' only!\n", + __func__, enum_sid_state[SID_APPLY_FIR]); + return -EIO; + } + + mutex_lock(&drvdata->ctrl_lock); + + sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF); + if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) { + if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) { + dev_err(component->dev, "%s: Sidetone busy while off!\n", + __func__); + status = -EPERM; + } else { + status = -EBUSY; + } + goto out; + } + + snd_soc_component_write(component, AB8500_SIDFIRADR, 0); + + for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) { + val = snd_soc_component_read(component, drvdata->sid_fir_values[param]); + snd_soc_component_write(component, AB8500_SIDFIRCOEF1, val >> 8 & 0xff); + snd_soc_component_write(component, AB8500_SIDFIRCOEF2, val & 0xff); + } + + snd_soc_component_update_bits(component, AB8500_SIDFIRADR, + BIT(AB8500_SIDFIRADR_FIRSIDSET), + BIT(AB8500_SIDFIRADR_FIRSIDSET)); + snd_soc_component_update_bits(component, AB8500_SIDFIRADR, + BIT(AB8500_SIDFIRADR_FIRSIDSET), 0); + + drvdata->sid_status = SID_FIR_CONFIGURED; + +out: + mutex_unlock(&drvdata->ctrl_lock); + + dev_dbg(component->dev, "%s: Exit\n", __func__); + + return status; +} + +static int anc_status_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); + + mutex_lock(&drvdata->ctrl_lock); + ucontrol->value.enumerated.item[0] = drvdata->anc_status; + mutex_unlock(&drvdata->ctrl_lock); + + return 0; +} + +static int anc_status_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev); + struct device *dev = component->dev; + bool apply_fir, apply_iir; + unsigned int req; + int status; + + dev_dbg(dev, "%s: Enter.\n", __func__); + + mutex_lock(&drvdata->ctrl_lock); + + req = ucontrol->value.enumerated.item[0]; + if (req >= ARRAY_SIZE(enum_anc_state)) { + status = -EINVAL; + goto cleanup; + } + if (req != ANC_APPLY_FIR_IIR && req != ANC_APPLY_FIR && + req != ANC_APPLY_IIR) { + dev_err(dev, "%s: ERROR: Unsupported status to set '%s'!\n", + __func__, enum_anc_state[req]); + status = -EINVAL; + goto cleanup; + } + apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR; + apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR; + + status = snd_soc_dapm_force_enable_pin(dapm, "ANC Configure Input"); + if (status < 0) { + dev_err(dev, + "%s: ERROR: Failed to enable power (status = %d)!\n", + __func__, status); + goto cleanup; + } + snd_soc_dapm_sync(dapm); + + anc_configure(component, apply_fir, apply_iir); + + if (apply_fir) { + if (drvdata->anc_status == ANC_IIR_CONFIGURED) + drvdata->anc_status = ANC_FIR_IIR_CONFIGURED; + else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED) + drvdata->anc_status = ANC_FIR_CONFIGURED; + } + if (apply_iir) { + if (drvdata->anc_status == ANC_FIR_CONFIGURED) + drvdata->anc_status = ANC_FIR_IIR_CONFIGURED; + else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED) + drvdata->anc_status = ANC_IIR_CONFIGURED; + } + + status = snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); + snd_soc_dapm_sync(dapm); + +cleanup: + mutex_unlock(&drvdata->ctrl_lock); + + if (status < 0) + dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n", + __func__, status); + + dev_dbg(dev, "%s: Exit.\n", __func__); + + return (status < 0) ? status : 1; +} + +static int filter_control_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct filter_control *fc = + (struct filter_control *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = fc->count; + uinfo->value.integer.min = fc->min; + uinfo->value.integer.max = fc->max; + + return 0; +} + +static int filter_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ab8500_codec_drvdata *drvdata = snd_soc_component_get_drvdata(component); + struct filter_control *fc = + (struct filter_control *)kcontrol->private_value; + unsigned int i; + + mutex_lock(&drvdata->ctrl_lock); + for (i = 0; i < fc->count; i++) + ucontrol->value.integer.value[i] = fc->value[i]; + mutex_unlock(&drvdata->ctrl_lock); + + return 0; +} + +static int filter_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ab8500_codec_drvdata *drvdata = snd_soc_component_get_drvdata(component); + struct filter_control *fc = + (struct filter_control *)kcontrol->private_value; + unsigned int i; + + mutex_lock(&drvdata->ctrl_lock); + for (i = 0; i < fc->count; i++) + fc->value[i] = ucontrol->value.integer.value[i]; + mutex_unlock(&drvdata->ctrl_lock); + + return 0; +} + +/* + * Controls - Non-DAPM ASoC + */ + +static DECLARE_TLV_DB_SCALE(adx_dig_gain_tlv, -3200, 100, 1); +/* -32dB = Mute */ + +static DECLARE_TLV_DB_SCALE(dax_dig_gain_tlv, -6300, 100, 1); +/* -63dB = Mute */ + +static DECLARE_TLV_DB_SCALE(hs_ear_dig_gain_tlv, -100, 100, 1); +/* -1dB = Mute */ + +static const DECLARE_TLV_DB_RANGE(hs_gain_tlv, + 0, 3, TLV_DB_SCALE_ITEM(-3200, 400, 0), + 4, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0) +); + +static DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0); + +static DECLARE_TLV_DB_SCALE(lin_gain_tlv, -1000, 200, 0); + +static DECLARE_TLV_DB_SCALE(lin2hs_gain_tlv, -3800, 200, 1); +/* -38dB = Mute */ + +static const char * const enum_hsfadspeed[] = {"2ms", "0.5ms", "10.6ms", + "5ms"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_hsfadspeed, + AB8500_DIGMICCONF, AB8500_DIGMICCONF_HSFADSPEED, enum_hsfadspeed); + +static const char * const enum_envdetthre[] = { + "250mV", "300mV", "350mV", "400mV", + "450mV", "500mV", "550mV", "600mV", + "650mV", "700mV", "750mV", "800mV", + "850mV", "900mV", "950mV", "1.00V" }; +static SOC_ENUM_SINGLE_DECL(soc_enum_envdeththre, + AB8500_ENVCPCONF, AB8500_ENVCPCONF_ENVDETHTHRE, enum_envdetthre); +static SOC_ENUM_SINGLE_DECL(soc_enum_envdetlthre, + AB8500_ENVCPCONF, AB8500_ENVCPCONF_ENVDETLTHRE, enum_envdetthre); +static const char * const enum_envdettime[] = { + "26.6us", "53.2us", "106us", "213us", + "426us", "851us", "1.70ms", "3.40ms", + "6.81ms", "13.6ms", "27.2ms", "54.5ms", + "109ms", "218ms", "436ms", "872ms" }; +static SOC_ENUM_SINGLE_DECL(soc_enum_envdettime, + AB8500_SIGENVCONF, AB8500_SIGENVCONF_ENVDETTIME, enum_envdettime); + +static const char * const enum_sinc31[] = {"Sinc 3", "Sinc 1"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_hsesinc, AB8500_HSLEARDIGGAIN, + AB8500_HSLEARDIGGAIN_HSSINC1, enum_sinc31); + +static const char * const enum_fadespeed[] = {"1ms", "4ms", "8ms", "16ms"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_fadespeed, AB8500_HSRDIGGAIN, + AB8500_HSRDIGGAIN_FADESPEED, enum_fadespeed); + +/* Earpiece */ + +static const char * const enum_lowpow[] = {"Normal", "Low Power"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_eardaclowpow, AB8500_ANACONF1, + AB8500_ANACONF1_EARDACLOWPOW, enum_lowpow); +static SOC_ENUM_SINGLE_DECL(soc_enum_eardrvlowpow, AB8500_ANACONF1, + AB8500_ANACONF1_EARDRVLOWPOW, enum_lowpow); + +static const char * const enum_av_mode[] = {"Audio", "Voice"}; +static SOC_ENUM_DOUBLE_DECL(soc_enum_ad12voice, AB8500_ADFILTCONF, + AB8500_ADFILTCONF_AD1VOICE, AB8500_ADFILTCONF_AD2VOICE, enum_av_mode); +static SOC_ENUM_DOUBLE_DECL(soc_enum_ad34voice, AB8500_ADFILTCONF, + AB8500_ADFILTCONF_AD3VOICE, AB8500_ADFILTCONF_AD4VOICE, enum_av_mode); + +/* DA */ + +static SOC_ENUM_SINGLE_DECL(soc_enum_da12voice, + AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_DA12VOICE, + enum_av_mode); +static SOC_ENUM_SINGLE_DECL(soc_enum_da34voice, + AB8500_DASLOTCONF3, AB8500_DASLOTCONF3_DA34VOICE, + enum_av_mode); +static SOC_ENUM_SINGLE_DECL(soc_enum_da56voice, + AB8500_DASLOTCONF5, AB8500_DASLOTCONF5_DA56VOICE, + enum_av_mode); + +static const char * const enum_da2hslr[] = {"Sidetone", "Audio Path"}; +static SOC_ENUM_DOUBLE_DECL(soc_enum_da2hslr, AB8500_DIGMULTCONF1, + AB8500_DIGMULTCONF1_DATOHSLEN, + AB8500_DIGMULTCONF1_DATOHSREN, enum_da2hslr); + +static const char * const enum_sinc53[] = {"Sinc 5", "Sinc 3"}; +static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic12sinc, AB8500_DMICFILTCONF, + AB8500_DMICFILTCONF_DMIC1SINC3, + AB8500_DMICFILTCONF_DMIC2SINC3, enum_sinc53); +static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic34sinc, AB8500_DMICFILTCONF, + AB8500_DMICFILTCONF_DMIC3SINC3, + AB8500_DMICFILTCONF_DMIC4SINC3, enum_sinc53); +static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic56sinc, AB8500_DMICFILTCONF, + AB8500_DMICFILTCONF_DMIC5SINC3, + AB8500_DMICFILTCONF_DMIC6SINC3, enum_sinc53); + +/* Digital interface - DA from slot mapping */ +static const char * const enum_da_from_slot_map[] = {"SLOT0", + "SLOT1", + "SLOT2", + "SLOT3", + "SLOT4", + "SLOT5", + "SLOT6", + "SLOT7", + "SLOT8", + "SLOT9", + "SLOT10", + "SLOT11", + "SLOT12", + "SLOT13", + "SLOT14", + "SLOT15", + "SLOT16", + "SLOT17", + "SLOT18", + "SLOT19", + "SLOT20", + "SLOT21", + "SLOT22", + "SLOT23", + "SLOT24", + "SLOT25", + "SLOT26", + "SLOT27", + "SLOT28", + "SLOT29", + "SLOT30", + "SLOT31"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_da1slotmap, + AB8500_DASLOTCONF1, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da2slotmap, + AB8500_DASLOTCONF2, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da3slotmap, + AB8500_DASLOTCONF3, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da4slotmap, + AB8500_DASLOTCONF4, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da5slotmap, + AB8500_DASLOTCONF5, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da6slotmap, + AB8500_DASLOTCONF6, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da7slotmap, + AB8500_DASLOTCONF7, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_da8slotmap, + AB8500_DASLOTCONF8, AB8500_DASLOTCONFX_SLTODAX_SHIFT, + enum_da_from_slot_map); + +/* Digital interface - AD to slot mapping */ +static const char * const enum_ad_to_slot_map[] = {"AD_OUT1", + "AD_OUT2", + "AD_OUT3", + "AD_OUT4", + "AD_OUT5", + "AD_OUT6", + "AD_OUT7", + "AD_OUT8", + "zeroes", + "zeroes", + "zeroes", + "zeroes", + "tristate", + "tristate", + "tristate", + "tristate"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot0map, + AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot1map, + AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot2map, + AB8500_ADSLOTSEL2, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot3map, + AB8500_ADSLOTSEL2, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot4map, + AB8500_ADSLOTSEL3, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot5map, + AB8500_ADSLOTSEL3, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot6map, + AB8500_ADSLOTSEL4, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot7map, + AB8500_ADSLOTSEL4, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot8map, + AB8500_ADSLOTSEL5, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot9map, + AB8500_ADSLOTSEL5, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot10map, + AB8500_ADSLOTSEL6, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot11map, + AB8500_ADSLOTSEL6, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot12map, + AB8500_ADSLOTSEL7, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot13map, + AB8500_ADSLOTSEL7, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot14map, + AB8500_ADSLOTSEL8, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot15map, + AB8500_ADSLOTSEL8, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot16map, + AB8500_ADSLOTSEL9, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot17map, + AB8500_ADSLOTSEL9, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot18map, + AB8500_ADSLOTSEL10, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot19map, + AB8500_ADSLOTSEL10, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot20map, + AB8500_ADSLOTSEL11, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot21map, + AB8500_ADSLOTSEL11, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot22map, + AB8500_ADSLOTSEL12, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot23map, + AB8500_ADSLOTSEL12, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot24map, + AB8500_ADSLOTSEL13, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot25map, + AB8500_ADSLOTSEL13, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot26map, + AB8500_ADSLOTSEL14, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot27map, + AB8500_ADSLOTSEL14, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot28map, + AB8500_ADSLOTSEL15, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot29map, + AB8500_ADSLOTSEL15, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot30map, + AB8500_ADSLOTSEL16, AB8500_ADSLOTSELX_EVEN_SHIFT, + enum_ad_to_slot_map); +static SOC_ENUM_SINGLE_DECL(soc_enum_adslot31map, + AB8500_ADSLOTSEL16, AB8500_ADSLOTSELX_ODD_SHIFT, + enum_ad_to_slot_map); + +/* Digital interface - Burst mode */ +static const char * const enum_mask[] = {"Unmasked", "Masked"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomask, + AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFOMASK, + enum_mask); +static const char * const enum_bitclk0[] = {"19_2_MHz", "38_4_MHz"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_bfifo19m2, + AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFO19M2, + enum_bitclk0); +static const char * const enum_slavemaster[] = {"Slave", "Master"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast, + AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT, + enum_slavemaster); + +/* Sidetone */ +static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state); + +/* ANC */ +static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_ancstate, enum_anc_state); + +static struct snd_kcontrol_new ab8500_ctrls[] = { + /* Charge pump */ + SOC_ENUM("Charge Pump High Threshold For Low Voltage", + soc_enum_envdeththre), + SOC_ENUM("Charge Pump Low Threshold For Low Voltage", + soc_enum_envdetlthre), + SOC_SINGLE("Charge Pump Envelope Detection Switch", + AB8500_SIGENVCONF, AB8500_SIGENVCONF_ENVDETCPEN, + 1, 0), + SOC_ENUM("Charge Pump Envelope Detection Decay Time", + soc_enum_envdettime), + + /* Headset */ + SOC_ENUM("Headset Mode", soc_enum_da12voice), + SOC_SINGLE("Headset High Pass Switch", + AB8500_ANACONF1, AB8500_ANACONF1_HSHPEN, + 1, 0), + SOC_SINGLE("Headset Low Power Switch", + AB8500_ANACONF1, AB8500_ANACONF1_HSLOWPOW, + 1, 0), + SOC_SINGLE("Headset DAC Low Power Switch", + AB8500_ANACONF1, AB8500_ANACONF1_DACLOWPOW1, + 1, 0), + SOC_SINGLE("Headset DAC Drv Low Power Switch", + AB8500_ANACONF1, AB8500_ANACONF1_DACLOWPOW0, + 1, 0), + SOC_ENUM("Headset Fade Speed", soc_enum_hsfadspeed), + SOC_ENUM("Headset Source", soc_enum_da2hslr), + SOC_ENUM("Headset Filter", soc_enum_hsesinc), + SOC_DOUBLE_R_TLV("Headset Master Volume", + AB8500_DADIGGAIN1, AB8500_DADIGGAIN2, + 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv), + SOC_DOUBLE_R_TLV("Headset Digital Volume", + AB8500_HSLEARDIGGAIN, AB8500_HSRDIGGAIN, + 0, AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX, 1, hs_ear_dig_gain_tlv), + SOC_DOUBLE_TLV("Headset Volume", + AB8500_ANAGAIN3, + AB8500_ANAGAIN3_HSLGAIN, AB8500_ANAGAIN3_HSRGAIN, + AB8500_ANAGAIN3_HSXGAIN_MAX, 1, hs_gain_tlv), + + /* Earpiece */ + SOC_ENUM("Earpiece DAC Mode", + soc_enum_eardaclowpow), + SOC_ENUM("Earpiece DAC Drv Mode", + soc_enum_eardrvlowpow), + + /* HandsFree */ + SOC_ENUM("HF Mode", soc_enum_da34voice), + SOC_SINGLE("HF and Headset Swap Switch", + AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_SWAPDA12_34, + 1, 0), + SOC_DOUBLE("HF Low EMI Mode Switch", + AB8500_CLASSDCONF1, + AB8500_CLASSDCONF1_HFLSWAPEN, AB8500_CLASSDCONF1_HFRSWAPEN, + 1, 0), + SOC_DOUBLE("HF FIR Bypass Switch", + AB8500_CLASSDCONF2, + AB8500_CLASSDCONF2_FIRBYP0, AB8500_CLASSDCONF2_FIRBYP1, + 1, 0), + SOC_DOUBLE("HF High Volume Switch", + AB8500_CLASSDCONF2, + AB8500_CLASSDCONF2_HIGHVOLEN0, AB8500_CLASSDCONF2_HIGHVOLEN1, + 1, 0), + SOC_SINGLE("HF L and R Bridge Switch", + AB8500_CLASSDCONF1, AB8500_CLASSDCONF1_PARLHF, + 1, 0), + SOC_DOUBLE_R_TLV("HF Master Volume", + AB8500_DADIGGAIN3, AB8500_DADIGGAIN4, + 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv), + + /* Vibra */ + SOC_DOUBLE("Vibra High Volume Switch", + AB8500_CLASSDCONF2, + AB8500_CLASSDCONF2_HIGHVOLEN2, AB8500_CLASSDCONF2_HIGHVOLEN3, + 1, 0), + SOC_DOUBLE("Vibra Low EMI Mode Switch", + AB8500_CLASSDCONF1, + AB8500_CLASSDCONF1_VIB1SWAPEN, AB8500_CLASSDCONF1_VIB2SWAPEN, + 1, 0), + SOC_DOUBLE("Vibra FIR Bypass Switch", + AB8500_CLASSDCONF2, + AB8500_CLASSDCONF2_FIRBYP2, AB8500_CLASSDCONF2_FIRBYP3, + 1, 0), + SOC_ENUM("Vibra Mode", soc_enum_da56voice), + SOC_DOUBLE_R("Vibra PWM Duty Cycle N", + AB8500_PWMGENCONF3, AB8500_PWMGENCONF5, + AB8500_PWMGENCONFX_PWMVIBXDUTCYC, + AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX, 0), + SOC_DOUBLE_R("Vibra PWM Duty Cycle P", + AB8500_PWMGENCONF2, AB8500_PWMGENCONF4, + AB8500_PWMGENCONFX_PWMVIBXDUTCYC, + AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX, 0), + SOC_SINGLE("Vibra 1 and 2 Bridge Switch", + AB8500_CLASSDCONF1, AB8500_CLASSDCONF1_PARLVIB, + 1, 0), + SOC_DOUBLE_R_TLV("Vibra Master Volume", + AB8500_DADIGGAIN5, AB8500_DADIGGAIN6, + 0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv), + + /* HandsFree, Vibra */ + SOC_SINGLE("ClassD High Pass Volume", + AB8500_CLASSDCONF3, AB8500_CLASSDCONF3_DITHHPGAIN, + AB8500_CLASSDCONF3_DITHHPGAIN_MAX, 0), + SOC_SINGLE("ClassD White Volume", + AB8500_CLASSDCONF3, AB8500_CLASSDCONF3_DITHWGAIN, + AB8500_CLASSDCONF3_DITHWGAIN_MAX, 0), + + /* Mic 1, Mic 2, LineIn */ + SOC_DOUBLE_R_TLV("Mic Master Volume", + AB8500_ADDIGGAIN3, AB8500_ADDIGGAIN4, + 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv), + + /* Mic 1 */ + SOC_SINGLE_TLV("Mic 1", + AB8500_ANAGAIN1, + AB8500_ANAGAINX_MICXGAIN, + AB8500_ANAGAINX_MICXGAIN_MAX, 0, mic_gain_tlv), + SOC_SINGLE("Mic 1 Low Power Switch", + AB8500_ANAGAIN1, AB8500_ANAGAINX_LOWPOWMICX, + 1, 0), + + /* Mic 2 */ + SOC_DOUBLE("Mic High Pass Switch", + AB8500_ADFILTCONF, + AB8500_ADFILTCONF_AD3NH, AB8500_ADFILTCONF_AD4NH, + 1, 1), + SOC_ENUM("Mic Mode", soc_enum_ad34voice), + SOC_ENUM("Mic Filter", soc_enum_dmic34sinc), + SOC_SINGLE_TLV("Mic 2", + AB8500_ANAGAIN2, + AB8500_ANAGAINX_MICXGAIN, + AB8500_ANAGAINX_MICXGAIN_MAX, 0, mic_gain_tlv), + SOC_SINGLE("Mic 2 Low Power Switch", + AB8500_ANAGAIN2, AB8500_ANAGAINX_LOWPOWMICX, + 1, 0), + + /* LineIn */ + SOC_DOUBLE("LineIn High Pass Switch", + AB8500_ADFILTCONF, + AB8500_ADFILTCONF_AD1NH, AB8500_ADFILTCONF_AD2NH, + 1, 1), + SOC_ENUM("LineIn Filter", soc_enum_dmic12sinc), + SOC_ENUM("LineIn Mode", soc_enum_ad12voice), + SOC_DOUBLE_R_TLV("LineIn Master Volume", + AB8500_ADDIGGAIN1, AB8500_ADDIGGAIN2, + 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv), + SOC_DOUBLE_TLV("LineIn", + AB8500_ANAGAIN4, + AB8500_ANAGAIN4_LINLGAIN, AB8500_ANAGAIN4_LINRGAIN, + AB8500_ANAGAIN4_LINXGAIN_MAX, 0, lin_gain_tlv), + SOC_DOUBLE_R_TLV("LineIn to Headset Volume", + AB8500_DIGLINHSLGAIN, AB8500_DIGLINHSRGAIN, + AB8500_DIGLINHSXGAIN_LINTOHSXGAIN, + AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX, + 1, lin2hs_gain_tlv), + + /* DMic */ + SOC_ENUM("DMic Filter", soc_enum_dmic56sinc), + SOC_DOUBLE_R_TLV("DMic Master Volume", + AB8500_ADDIGGAIN5, AB8500_ADDIGGAIN6, + 0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv), + + /* Digital gains */ + SOC_ENUM("Digital Gain Fade Speed", soc_enum_fadespeed), + + /* Analog loopback */ + SOC_DOUBLE_R_TLV("Analog Loopback Volume", + AB8500_ADDIGLOOPGAIN1, AB8500_ADDIGLOOPGAIN2, + 0, AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX, 1, dax_dig_gain_tlv), + + /* Digital interface - DA from slot mapping */ + SOC_ENUM("Digital Interface DA 1 From Slot Map", soc_enum_da1slotmap), + SOC_ENUM("Digital Interface DA 2 From Slot Map", soc_enum_da2slotmap), + SOC_ENUM("Digital Interface DA 3 From Slot Map", soc_enum_da3slotmap), + SOC_ENUM("Digital Interface DA 4 From Slot Map", soc_enum_da4slotmap), + SOC_ENUM("Digital Interface DA 5 From Slot Map", soc_enum_da5slotmap), + SOC_ENUM("Digital Interface DA 6 From Slot Map", soc_enum_da6slotmap), + SOC_ENUM("Digital Interface DA 7 From Slot Map", soc_enum_da7slotmap), + SOC_ENUM("Digital Interface DA 8 From Slot Map", soc_enum_da8slotmap), + + /* Digital interface - AD to slot mapping */ + SOC_ENUM("Digital Interface AD To Slot 0 Map", soc_enum_adslot0map), + SOC_ENUM("Digital Interface AD To Slot 1 Map", soc_enum_adslot1map), + SOC_ENUM("Digital Interface AD To Slot 2 Map", soc_enum_adslot2map), + SOC_ENUM("Digital Interface AD To Slot 3 Map", soc_enum_adslot3map), + SOC_ENUM("Digital Interface AD To Slot 4 Map", soc_enum_adslot4map), + SOC_ENUM("Digital Interface AD To Slot 5 Map", soc_enum_adslot5map), + SOC_ENUM("Digital Interface AD To Slot 6 Map", soc_enum_adslot6map), + SOC_ENUM("Digital Interface AD To Slot 7 Map", soc_enum_adslot7map), + SOC_ENUM("Digital Interface AD To Slot 8 Map", soc_enum_adslot8map), + SOC_ENUM("Digital Interface AD To Slot 9 Map", soc_enum_adslot9map), + SOC_ENUM("Digital Interface AD To Slot 10 Map", soc_enum_adslot10map), + SOC_ENUM("Digital Interface AD To Slot 11 Map", soc_enum_adslot11map), + SOC_ENUM("Digital Interface AD To Slot 12 Map", soc_enum_adslot12map), + SOC_ENUM("Digital Interface AD To Slot 13 Map", soc_enum_adslot13map), + SOC_ENUM("Digital Interface AD To Slot 14 Map", soc_enum_adslot14map), + SOC_ENUM("Digital Interface AD To Slot 15 Map", soc_enum_adslot15map), + SOC_ENUM("Digital Interface AD To Slot 16 Map", soc_enum_adslot16map), + SOC_ENUM("Digital Interface AD To Slot 17 Map", soc_enum_adslot17map), + SOC_ENUM("Digital Interface AD To Slot 18 Map", soc_enum_adslot18map), + SOC_ENUM("Digital Interface AD To Slot 19 Map", soc_enum_adslot19map), + SOC_ENUM("Digital Interface AD To Slot 20 Map", soc_enum_adslot20map), + SOC_ENUM("Digital Interface AD To Slot 21 Map", soc_enum_adslot21map), + SOC_ENUM("Digital Interface AD To Slot 22 Map", soc_enum_adslot22map), + SOC_ENUM("Digital Interface AD To Slot 23 Map", soc_enum_adslot23map), + SOC_ENUM("Digital Interface AD To Slot 24 Map", soc_enum_adslot24map), + SOC_ENUM("Digital Interface AD To Slot 25 Map", soc_enum_adslot25map), + SOC_ENUM("Digital Interface AD To Slot 26 Map", soc_enum_adslot26map), + SOC_ENUM("Digital Interface AD To Slot 27 Map", soc_enum_adslot27map), + SOC_ENUM("Digital Interface AD To Slot 28 Map", soc_enum_adslot28map), + SOC_ENUM("Digital Interface AD To Slot 29 Map", soc_enum_adslot29map), + SOC_ENUM("Digital Interface AD To Slot 30 Map", soc_enum_adslot30map), + SOC_ENUM("Digital Interface AD To Slot 31 Map", soc_enum_adslot31map), + + /* Digital interface - Loopback */ + SOC_SINGLE("Digital Interface AD 1 Loopback Switch", + AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_DAI7TOADO1, + 1, 0), + SOC_SINGLE("Digital Interface AD 2 Loopback Switch", + AB8500_DASLOTCONF2, AB8500_DASLOTCONF2_DAI8TOADO2, + 1, 0), + SOC_SINGLE("Digital Interface AD 3 Loopback Switch", + AB8500_DASLOTCONF3, AB8500_DASLOTCONF3_DAI7TOADO3, + 1, 0), + SOC_SINGLE("Digital Interface AD 4 Loopback Switch", + AB8500_DASLOTCONF4, AB8500_DASLOTCONF4_DAI8TOADO4, + 1, 0), + SOC_SINGLE("Digital Interface AD 5 Loopback Switch", + AB8500_DASLOTCONF5, AB8500_DASLOTCONF5_DAI7TOADO5, + 1, 0), + SOC_SINGLE("Digital Interface AD 6 Loopback Switch", + AB8500_DASLOTCONF6, AB8500_DASLOTCONF6_DAI8TOADO6, + 1, 0), + SOC_SINGLE("Digital Interface AD 7 Loopback Switch", + AB8500_DASLOTCONF7, AB8500_DASLOTCONF7_DAI8TOADO7, + 1, 0), + SOC_SINGLE("Digital Interface AD 8 Loopback Switch", + AB8500_DASLOTCONF8, AB8500_DASLOTCONF8_DAI7TOADO8, + 1, 0), + + /* Digital interface - Burst FIFO */ + SOC_SINGLE("Digital Interface 0 FIFO Enable Switch", + AB8500_DIGIFCONF3, AB8500_DIGIFCONF3_IF0BFIFOEN, + 1, 0), + SOC_ENUM("Burst FIFO Mask", soc_enum_bfifomask), + SOC_ENUM("Burst FIFO Bit-clock Frequency", soc_enum_bfifo19m2), + SOC_SINGLE("Burst FIFO Threshold", + AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFOINT_SHIFT, + AB8500_FIFOCONF1_BFIFOINT_MAX, 0), + SOC_SINGLE("Burst FIFO Length", + AB8500_FIFOCONF2, AB8500_FIFOCONF2_BFIFOTX_SHIFT, + AB8500_FIFOCONF2_BFIFOTX_MAX, 0), + SOC_SINGLE("Burst FIFO EOS Extra Slots", + AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOEXSL_SHIFT, + AB8500_FIFOCONF3_BFIFOEXSL_MAX, 0), + SOC_SINGLE("Burst FIFO FS Extra Bit-clocks", + AB8500_FIFOCONF3, AB8500_FIFOCONF3_PREBITCLK0_SHIFT, + AB8500_FIFOCONF3_PREBITCLK0_MAX, 0), + SOC_ENUM("Burst FIFO Interface Mode", soc_enum_bfifomast), + + SOC_SINGLE("Burst FIFO Interface Switch", + AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFORUN_SHIFT, + 1, 0), + SOC_SINGLE("Burst FIFO Switch Frame Number", + AB8500_FIFOCONF4, AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT, + AB8500_FIFOCONF4_BFIFOFRAMSW_MAX, 0), + SOC_SINGLE("Burst FIFO Wake Up Delay", + AB8500_FIFOCONF5, AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT, + AB8500_FIFOCONF5_BFIFOWAKEUP_MAX, 0), + SOC_SINGLE("Burst FIFO Samples In FIFO", + AB8500_FIFOCONF6, AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT, + AB8500_FIFOCONF6_BFIFOSAMPLE_MAX, 0), + + /* ANC */ + SOC_ENUM_EXT("ANC Status", soc_enum_ancstate, + anc_status_control_get, anc_status_control_put), + SOC_SINGLE_XR_SX("ANC Warp Delay Shift", + AB8500_ANCCONF2, 1, AB8500_ANCCONF2_SHIFT, + AB8500_ANCCONF2_MIN, AB8500_ANCCONF2_MAX, 0), + SOC_SINGLE_XR_SX("ANC FIR Output Shift", + AB8500_ANCCONF3, 1, AB8500_ANCCONF3_SHIFT, + AB8500_ANCCONF3_MIN, AB8500_ANCCONF3_MAX, 0), + SOC_SINGLE_XR_SX("ANC IIR Output Shift", + AB8500_ANCCONF4, 1, AB8500_ANCCONF4_SHIFT, + AB8500_ANCCONF4_MIN, AB8500_ANCCONF4_MAX, 0), + SOC_SINGLE_XR_SX("ANC Warp Delay", + AB8500_ANCCONF9, 2, AB8500_ANC_WARP_DELAY_SHIFT, + AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0), + + /* Sidetone */ + SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate, + sid_status_control_get, sid_status_control_put), + SOC_SINGLE_STROBE("Sidetone Reset", + AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0), +}; + +static struct snd_kcontrol_new ab8500_filter_controls[] = { + AB8500_FILTER_CONTROL("ANC FIR Coefficients", AB8500_ANC_FIR_COEFFS, + AB8500_ANC_FIR_COEFF_MIN, AB8500_ANC_FIR_COEFF_MAX), + AB8500_FILTER_CONTROL("ANC IIR Coefficients", AB8500_ANC_IIR_COEFFS, + AB8500_ANC_IIR_COEFF_MIN, AB8500_ANC_IIR_COEFF_MAX), + AB8500_FILTER_CONTROL("Sidetone FIR Coefficients", + AB8500_SID_FIR_COEFFS, AB8500_SID_FIR_COEFF_MIN, + AB8500_SID_FIR_COEFF_MAX) +}; +enum ab8500_filter { + AB8500_FILTER_ANC_FIR = 0, + AB8500_FILTER_ANC_IIR = 1, + AB8500_FILTER_SID_FIR = 2, +}; + +/* + * Extended interface for codec-driver + */ + +static int ab8500_audio_init_audioblock(struct snd_soc_component *component) +{ + int status; + + dev_dbg(component->dev, "%s: Enter.\n", __func__); + + /* Reset audio-registers and disable 32kHz-clock output 2 */ + status = ab8500_sysctrl_write(AB8500_STW4500CTRL3, + AB8500_STW4500CTRL3_CLK32KOUT2DIS | + AB8500_STW4500CTRL3_RESETAUDN, + AB8500_STW4500CTRL3_RESETAUDN); + if (status < 0) + return status; + + return 0; +} + +static int ab8500_audio_setup_mics(struct snd_soc_component *component, + struct amic_settings *amics) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + u8 value8; + unsigned int value; + int status; + const struct snd_soc_dapm_route *route; + + dev_dbg(component->dev, "%s: Enter.\n", __func__); + + /* Set DMic-clocks to outputs */ + status = abx500_get_register_interruptible(component->dev, AB8500_MISC, + AB8500_GPIO_DIR4_REG, + &value8); + if (status < 0) + return status; + value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT | + GPIO31_DIR_OUTPUT; + status = abx500_set_register_interruptible(component->dev, + AB8500_MISC, + AB8500_GPIO_DIR4_REG, + value); + if (status < 0) + return status; + + /* Attach regulators to AMic DAPM-paths */ + dev_dbg(component->dev, "%s: Mic 1a regulator: %s\n", __func__, + amic_micbias_str(amics->mic1a_micbias)); + route = &ab8500_dapm_routes_mic1a_vamicx[amics->mic1a_micbias]; + status = snd_soc_dapm_add_routes(dapm, route, 1); + dev_dbg(component->dev, "%s: Mic 1b regulator: %s\n", __func__, + amic_micbias_str(amics->mic1b_micbias)); + route = &ab8500_dapm_routes_mic1b_vamicx[amics->mic1b_micbias]; + status |= snd_soc_dapm_add_routes(dapm, route, 1); + dev_dbg(component->dev, "%s: Mic 2 regulator: %s\n", __func__, + amic_micbias_str(amics->mic2_micbias)); + route = &ab8500_dapm_routes_mic2_vamicx[amics->mic2_micbias]; + status |= snd_soc_dapm_add_routes(dapm, route, 1); + if (status < 0) { + dev_err(component->dev, + "%s: Failed to add AMic-regulator DAPM-routes (%d).\n", + __func__, status); + return status; + } + + /* Set AMic-configuration */ + dev_dbg(component->dev, "%s: Mic 1 mic-type: %s\n", __func__, + amic_type_str(amics->mic1_type)); + snd_soc_component_update_bits(component, AB8500_ANAGAIN1, AB8500_ANAGAINX_ENSEMICX, + amics->mic1_type == AMIC_TYPE_DIFFERENTIAL ? + 0 : AB8500_ANAGAINX_ENSEMICX); + dev_dbg(component->dev, "%s: Mic 2 mic-type: %s\n", __func__, + amic_type_str(amics->mic2_type)); + snd_soc_component_update_bits(component, AB8500_ANAGAIN2, AB8500_ANAGAINX_ENSEMICX, + amics->mic2_type == AMIC_TYPE_DIFFERENTIAL ? + 0 : AB8500_ANAGAINX_ENSEMICX); + + return 0; +} + +static int ab8500_audio_set_ear_cmv(struct snd_soc_component *component, + enum ear_cm_voltage ear_cmv) +{ + char *cmv_str; + + switch (ear_cmv) { + case EAR_CMV_0_95V: + cmv_str = "0.95V"; + break; + case EAR_CMV_1_10V: + cmv_str = "1.10V"; + break; + case EAR_CMV_1_27V: + cmv_str = "1.27V"; + break; + case EAR_CMV_1_58V: + cmv_str = "1.58V"; + break; + default: + dev_err(component->dev, + "%s: Unknown earpiece CM-voltage (%d)!\n", + __func__, (int)ear_cmv); + return -EINVAL; + } + dev_dbg(component->dev, "%s: Earpiece CM-voltage: %s\n", __func__, + cmv_str); + snd_soc_component_update_bits(component, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM, + ear_cmv); + + return 0; +} + +static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai, + unsigned int delay) +{ + unsigned int mask, val; + struct snd_soc_component *component = dai->component; + + mask = BIT(AB8500_DIGIFCONF2_IF0DEL); + val = 0; + + switch (delay) { + case 0: + break; + case 1: + val |= BIT(AB8500_DIGIFCONF2_IF0DEL); + break; + default: + dev_err(dai->component->dev, + "%s: ERROR: Unsupported bit-delay (0x%x)!\n", + __func__, delay); + return -EINVAL; + } + + dev_dbg(dai->component->dev, "%s: IF0 Bit-delay: %d bits.\n", + __func__, delay); + snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val); + + return 0; +} + +/* Gates clocking according format mask */ +static int ab8500_codec_set_dai_clock_gate(struct snd_soc_component *component, + unsigned int fmt) +{ + unsigned int mask; + unsigned int val; + + mask = BIT(AB8500_DIGIFCONF1_ENMASTGEN) | + BIT(AB8500_DIGIFCONF1_ENFSBITCLK0); + + val = BIT(AB8500_DIGIFCONF1_ENMASTGEN); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: /* continuous clock */ + dev_dbg(component->dev, "%s: IF0 Clock is continuous.\n", + __func__); + val |= BIT(AB8500_DIGIFCONF1_ENFSBITCLK0); + break; + case SND_SOC_DAIFMT_GATED: /* clock is gated */ + dev_dbg(component->dev, "%s: IF0 Clock is gated.\n", + __func__); + break; + default: + dev_err(component->dev, + "%s: ERROR: Unsupported clock mask (0x%x)!\n", + __func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val); + + return 0; +} + +static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + unsigned int mask; + unsigned int val; + struct snd_soc_component *component = dai->component; + int status; + + dev_dbg(component->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt); + + mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) | + BIT(AB8500_DIGIFCONF3_IF1CLKTOIF0CLK) | + BIT(AB8500_DIGIFCONF3_IF0BFIFOEN) | + BIT(AB8500_DIGIFCONF3_IF0MASTER); + val = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + dev_dbg(dai->component->dev, + "%s: IF0 Master-mode: AB8500 provider.\n", __func__); + val |= BIT(AB8500_DIGIFCONF3_IF0MASTER); + break; + case SND_SOC_DAIFMT_CBC_CFC: + dev_dbg(dai->component->dev, + "%s: IF0 Master-mode: AB8500 consumer.\n", __func__); + break; + case SND_SOC_DAIFMT_CBC_CFP: + case SND_SOC_DAIFMT_CBP_CFC: + dev_err(dai->component->dev, + "%s: ERROR: The device is either a provider or a consumer.\n", + __func__); + fallthrough; + default: + dev_err(dai->component->dev, + "%s: ERROR: Unsupporter clocking mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AB8500_DIGIFCONF3, mask, val); + + /* Set clock gating */ + status = ab8500_codec_set_dai_clock_gate(component, fmt); + if (status) { + dev_err(dai->component->dev, + "%s: ERROR: Failed to set clock gate (%d).\n", + __func__, status); + return status; + } + + /* Setting data transfer format */ + + mask = BIT(AB8500_DIGIFCONF2_IF0FORMAT0) | + BIT(AB8500_DIGIFCONF2_IF0FORMAT1) | + BIT(AB8500_DIGIFCONF2_FSYNC0P) | + BIT(AB8500_DIGIFCONF2_BITCLK0P); + val = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: /* I2S mode */ + dev_dbg(dai->component->dev, "%s: IF0 Protocol: I2S\n", __func__); + val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT1); + ab8500_audio_set_bit_delay(dai, 0); + break; + + case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */ + dev_dbg(dai->component->dev, + "%s: IF0 Protocol: DSP A (TDM)\n", __func__); + val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0); + ab8500_audio_set_bit_delay(dai, 1); + break; + + case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */ + dev_dbg(dai->component->dev, + "%s: IF0 Protocol: DSP B (TDM)\n", __func__); + val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0); + ab8500_audio_set_bit_delay(dai, 0); + break; + + default: + dev_err(dai->component->dev, + "%s: ERROR: Unsupported format (0x%x)!\n", + __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ + dev_dbg(dai->component->dev, + "%s: IF0: Normal bit clock, normal frame\n", + __func__); + break; + case SND_SOC_DAIFMT_NB_IF: /* normal BCLK + inv FRM */ + dev_dbg(dai->component->dev, + "%s: IF0: Normal bit clock, inverted frame\n", + __func__); + val |= BIT(AB8500_DIGIFCONF2_FSYNC0P); + break; + case SND_SOC_DAIFMT_IB_NF: /* invert BCLK + nor FRM */ + dev_dbg(dai->component->dev, + "%s: IF0: Inverted bit clock, normal frame\n", + __func__); + val |= BIT(AB8500_DIGIFCONF2_BITCLK0P); + break; + case SND_SOC_DAIFMT_IB_IF: /* invert BCLK + FRM */ + dev_dbg(dai->component->dev, + "%s: IF0: Inverted bit clock, inverted frame\n", + __func__); + val |= BIT(AB8500_DIGIFCONF2_FSYNC0P); + val |= BIT(AB8500_DIGIFCONF2_BITCLK0P); + break; + default: + dev_err(dai->component->dev, + "%s: ERROR: Unsupported INV mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val); + + return 0; +} + +static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int val, mask, slot, slots_active; + + mask = BIT(AB8500_DIGIFCONF2_IF0WL0) | + BIT(AB8500_DIGIFCONF2_IF0WL1); + val = 0; + + switch (slot_width) { + case 16: + break; + case 20: + val |= BIT(AB8500_DIGIFCONF2_IF0WL0); + break; + case 24: + val |= BIT(AB8500_DIGIFCONF2_IF0WL1); + break; + case 32: + val |= BIT(AB8500_DIGIFCONF2_IF0WL1) | + BIT(AB8500_DIGIFCONF2_IF0WL0); + break; + default: + dev_err(dai->component->dev, "%s: Unsupported slot-width 0x%x\n", + __func__, slot_width); + return -EINVAL; + } + + dev_dbg(dai->component->dev, "%s: IF0 slot-width: %d bits.\n", + __func__, slot_width); + snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val); + + /* Setup TDM clocking according to slot count */ + dev_dbg(dai->component->dev, "%s: Slots, total: %d\n", __func__, slots); + mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) | + BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); + switch (slots) { + case 2: + val = AB8500_MASK_NONE; + break; + case 4: + val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0); + break; + case 8: + val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); + break; + case 16: + val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) | + BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1); + break; + default: + dev_err(dai->component->dev, + "%s: ERROR: Unsupported number of slots (%d)!\n", + __func__, slots); + return -EINVAL; + } + snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val); + + /* Setup TDM DA according to active tx slots */ + + if (tx_mask & ~0xff) + return -EINVAL; + + mask = AB8500_DASLOTCONFX_SLTODAX_MASK; + tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET; + slots_active = hweight32(tx_mask); + + dev_dbg(dai->component->dev, "%s: Slots, active, TX: %d\n", __func__, + slots_active); + + switch (slots_active) { + case 0: + break; + case 1: + slot = ffs(tx_mask); + snd_soc_component_update_bits(component, AB8500_DASLOTCONF1, mask, slot); + snd_soc_component_update_bits(component, AB8500_DASLOTCONF3, mask, slot); + snd_soc_component_update_bits(component, AB8500_DASLOTCONF2, mask, slot); + snd_soc_component_update_bits(component, AB8500_DASLOTCONF4, mask, slot); + break; + case 2: + slot = ffs(tx_mask); + snd_soc_component_update_bits(component, AB8500_DASLOTCONF1, mask, slot); + snd_soc_component_update_bits(component, AB8500_DASLOTCONF3, mask, slot); + slot = fls(tx_mask); + snd_soc_component_update_bits(component, AB8500_DASLOTCONF2, mask, slot); + snd_soc_component_update_bits(component, AB8500_DASLOTCONF4, mask, slot); + break; + case 8: + dev_dbg(dai->component->dev, + "%s: In 8-channel mode DA-from-slot mapping is set manually.", + __func__); + break; + default: + dev_err(dai->component->dev, + "%s: Unsupported number of active TX-slots (%d)!\n", + __func__, slots_active); + return -EINVAL; + } + + /* Setup TDM AD according to active RX-slots */ + + if (rx_mask & ~0xff) + return -EINVAL; + + rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET; + slots_active = hweight32(rx_mask); + + dev_dbg(dai->component->dev, "%s: Slots, active, RX: %d\n", __func__, + slots_active); + + switch (slots_active) { + case 0: + break; + case 1: + slot = ffs(rx_mask); + snd_soc_component_update_bits(component, AB8500_ADSLOTSEL(slot), + AB8500_MASK_SLOT(slot), + AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot)); + break; + case 2: + slot = ffs(rx_mask); + snd_soc_component_update_bits(component, + AB8500_ADSLOTSEL(slot), + AB8500_MASK_SLOT(slot), + AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot)); + slot = fls(rx_mask); + snd_soc_component_update_bits(component, + AB8500_ADSLOTSEL(slot), + AB8500_MASK_SLOT(slot), + AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2, slot)); + break; + case 8: + dev_dbg(dai->component->dev, + "%s: In 8-channel mode AD-to-slot mapping is set manually.", + __func__); + break; + default: + dev_err(dai->component->dev, + "%s: Unsupported number of active RX-slots (%d)!\n", + __func__, slots_active); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops ab8500_codec_ops = { + .set_fmt = ab8500_codec_set_dai_fmt, + .set_tdm_slot = ab8500_codec_set_dai_tdm_slot, +}; + +static struct snd_soc_dai_driver ab8500_codec_dai[] = { + { + .name = "ab8500-codec-dai.0", + .id = 0, + .playback = { + .stream_name = "ab8500_0p", + .channels_min = 1, + .channels_max = 8, + .rates = AB8500_SUPPORTED_RATE, + .formats = AB8500_SUPPORTED_FMT, + }, + .ops = &ab8500_codec_ops, + .symmetric_rate = 1 + }, + { + .name = "ab8500-codec-dai.1", + .id = 1, + .capture = { + .stream_name = "ab8500_0c", + .channels_min = 1, + .channels_max = 8, + .rates = AB8500_SUPPORTED_RATE, + .formats = AB8500_SUPPORTED_FMT, + }, + .ops = &ab8500_codec_ops, + .symmetric_rate = 1 + } +}; + +static void ab8500_codec_of_probe(struct device *dev, struct device_node *np, + struct ab8500_codec_platform_data *codec) +{ + u32 value; + + if (of_property_read_bool(np, "stericsson,amic1-type-single-ended")) + codec->amics.mic1_type = AMIC_TYPE_SINGLE_ENDED; + else + codec->amics.mic1_type = AMIC_TYPE_DIFFERENTIAL; + + if (of_property_read_bool(np, "stericsson,amic2-type-single-ended")) + codec->amics.mic2_type = AMIC_TYPE_SINGLE_ENDED; + else + codec->amics.mic2_type = AMIC_TYPE_DIFFERENTIAL; + + /* Has a non-standard Vamic been requested? */ + if (of_property_read_bool(np, "stericsson,amic1a-bias-vamic2")) + codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC2; + else + codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC1; + + if (of_property_read_bool(np, "stericsson,amic1b-bias-vamic2")) + codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC2; + else + codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC1; + + if (of_property_read_bool(np, "stericsson,amic2-bias-vamic1")) + codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC1; + else + codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC2; + + if (!of_property_read_u32(np, "stericsson,earpeice-cmv", &value)) { + switch (value) { + case 950 : + codec->ear_cmv = EAR_CMV_0_95V; + break; + case 1100 : + codec->ear_cmv = EAR_CMV_1_10V; + break; + case 1270 : + codec->ear_cmv = EAR_CMV_1_27V; + break; + case 1580 : + codec->ear_cmv = EAR_CMV_1_58V; + break; + default : + codec->ear_cmv = EAR_CMV_UNKNOWN; + dev_err(dev, "Unsuitable earpiece voltage found in DT\n"); + } + } else { + dev_warn(dev, "No earpiece voltage found in DT - using default\n"); + codec->ear_cmv = EAR_CMV_0_95V; + } +} + +static int ab8500_codec_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct device *dev = component->dev; + struct device_node *np = dev->of_node; + struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev); + struct ab8500_codec_platform_data codec_pdata; + struct filter_control *fc; + int status; + + dev_dbg(dev, "%s: Enter.\n", __func__); + + ab8500_codec_of_probe(dev, np, &codec_pdata); + + status = ab8500_audio_setup_mics(component, &codec_pdata.amics); + if (status < 0) { + pr_err("%s: Failed to setup mics (%d)!\n", __func__, status); + return status; + } + status = ab8500_audio_set_ear_cmv(component, codec_pdata.ear_cmv); + if (status < 0) { + pr_err("%s: Failed to set earpiece CM-voltage (%d)!\n", + __func__, status); + return status; + } + + status = ab8500_audio_init_audioblock(component); + if (status < 0) { + dev_err(dev, "%s: failed to init audio-block (%d)!\n", + __func__, status); + return status; + } + + /* Override HW-defaults */ + snd_soc_component_write(component, AB8500_ANACONF5, + BIT(AB8500_ANACONF5_HSAUTOEN)); + snd_soc_component_write(component, AB8500_SHORTCIRCONF, + BIT(AB8500_SHORTCIRCONF_HSZCDDIS)); + + /* Add filter controls */ + status = snd_soc_add_component_controls(component, ab8500_filter_controls, + ARRAY_SIZE(ab8500_filter_controls)); + if (status < 0) { + dev_err(dev, + "%s: failed to add ab8500 filter controls (%d).\n", + __func__, status); + return status; + } + fc = (struct filter_control *) + &ab8500_filter_controls[AB8500_FILTER_ANC_FIR].private_value; + drvdata->anc_fir_values = (long *)fc->value; + fc = (struct filter_control *) + &ab8500_filter_controls[AB8500_FILTER_ANC_IIR].private_value; + drvdata->anc_iir_values = (long *)fc->value; + fc = (struct filter_control *) + &ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value; + drvdata->sid_fir_values = (long *)fc->value; + + snd_soc_dapm_disable_pin(dapm, "ANC Configure Input"); + + mutex_init(&drvdata->ctrl_lock); + + return status; +} + +static const struct snd_soc_component_driver ab8500_component_driver = { + .probe = ab8500_codec_probe, + .controls = ab8500_ctrls, + .num_controls = ARRAY_SIZE(ab8500_ctrls), + .dapm_widgets = ab8500_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ab8500_dapm_widgets), + .dapm_routes = ab8500_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ab8500_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int ab8500_codec_driver_probe(struct platform_device *pdev) +{ + int status; + struct ab8500_codec_drvdata *drvdata; + + dev_dbg(&pdev->dev, "%s: Enter.\n", __func__); + + /* Create driver private-data struct */ + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_codec_drvdata), + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + drvdata->sid_status = SID_UNCONFIGURED; + drvdata->anc_status = ANC_UNCONFIGURED; + dev_set_drvdata(&pdev->dev, drvdata); + + drvdata->regmap = devm_regmap_init(&pdev->dev, NULL, &pdev->dev, + &ab8500_codec_regmap); + if (IS_ERR(drvdata->regmap)) { + status = PTR_ERR(drvdata->regmap); + dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n", + __func__, status); + return status; + } + + dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__); + status = devm_snd_soc_register_component(&pdev->dev, + &ab8500_component_driver, + ab8500_codec_dai, + ARRAY_SIZE(ab8500_codec_dai)); + if (status < 0) + dev_err(&pdev->dev, + "%s: Error: Failed to register codec (%d).\n", + __func__, status); + + return status; +} + +static struct platform_driver ab8500_codec_platform_driver = { + .driver = { + .name = "ab8500-codec", + }, + .probe = ab8500_codec_driver_probe, +}; +module_platform_driver(ab8500_codec_platform_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ab8500-codec.h b/sound/soc/codecs/ab8500-codec.h new file mode 100644 index 0000000000..2a6f6409f1 --- /dev/null +++ b/sound/soc/codecs/ab8500-codec.h @@ -0,0 +1,587 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) ST-Ericsson SA 2012 + * + * Author: Ola Lilja , + * Kristoffer Karlsson , + * Roger Nilsson , + * for ST-Ericsson. + * + * Based on the early work done by: + * Mikko J. Lehto , + * Mikko Sarmanne , + * for ST-Ericsson. + */ + +#ifndef AB8500_CODEC_REGISTERS_H +#define AB8500_CODEC_REGISTERS_H + +#define AB8500_SUPPORTED_RATE (SNDRV_PCM_RATE_48000) +#define AB8500_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE) + +/* AB8500 interface slot offset definitions */ + +#define AB8500_AD_DATA0_OFFSET 0 +#define AB8500_DA_DATA0_OFFSET 8 +#define AB8500_AD_DATA1_OFFSET 16 +#define AB8500_DA_DATA1_OFFSET 24 + +/* AB8500 audio bank (0x0d) register definitions */ + +#define AB8500_POWERUP 0x00 +#define AB8500_AUDSWRESET 0x01 +#define AB8500_ADPATHENA 0x02 +#define AB8500_DAPATHENA 0x03 +#define AB8500_ANACONF1 0x04 +#define AB8500_ANACONF2 0x05 +#define AB8500_DIGMICCONF 0x06 +#define AB8500_ANACONF3 0x07 +#define AB8500_ANACONF4 0x08 +#define AB8500_DAPATHCONF 0x09 +#define AB8500_MUTECONF 0x0A +#define AB8500_SHORTCIRCONF 0x0B +#define AB8500_ANACONF5 0x0C +#define AB8500_ENVCPCONF 0x0D +#define AB8500_SIGENVCONF 0x0E +#define AB8500_PWMGENCONF1 0x0F +#define AB8500_PWMGENCONF2 0x10 +#define AB8500_PWMGENCONF3 0x11 +#define AB8500_PWMGENCONF4 0x12 +#define AB8500_PWMGENCONF5 0x13 +#define AB8500_ANAGAIN1 0x14 +#define AB8500_ANAGAIN2 0x15 +#define AB8500_ANAGAIN3 0x16 +#define AB8500_ANAGAIN4 0x17 +#define AB8500_DIGLINHSLGAIN 0x18 +#define AB8500_DIGLINHSRGAIN 0x19 +#define AB8500_ADFILTCONF 0x1A +#define AB8500_DIGIFCONF1 0x1B +#define AB8500_DIGIFCONF2 0x1C +#define AB8500_DIGIFCONF3 0x1D +#define AB8500_DIGIFCONF4 0x1E +#define AB8500_ADSLOTSEL1 0x1F +#define AB8500_ADSLOTSEL2 0x20 +#define AB8500_ADSLOTSEL3 0x21 +#define AB8500_ADSLOTSEL4 0x22 +#define AB8500_ADSLOTSEL5 0x23 +#define AB8500_ADSLOTSEL6 0x24 +#define AB8500_ADSLOTSEL7 0x25 +#define AB8500_ADSLOTSEL8 0x26 +#define AB8500_ADSLOTSEL9 0x27 +#define AB8500_ADSLOTSEL10 0x28 +#define AB8500_ADSLOTSEL11 0x29 +#define AB8500_ADSLOTSEL12 0x2A +#define AB8500_ADSLOTSEL13 0x2B +#define AB8500_ADSLOTSEL14 0x2C +#define AB8500_ADSLOTSEL15 0x2D +#define AB8500_ADSLOTSEL16 0x2E +#define AB8500_ADSLOTSEL(slot) (AB8500_ADSLOTSEL1 + (slot >> 1)) +#define AB8500_ADSLOTHIZCTRL1 0x2F +#define AB8500_ADSLOTHIZCTRL2 0x30 +#define AB8500_ADSLOTHIZCTRL3 0x31 +#define AB8500_ADSLOTHIZCTRL4 0x32 +#define AB8500_DASLOTCONF1 0x33 +#define AB8500_DASLOTCONF2 0x34 +#define AB8500_DASLOTCONF3 0x35 +#define AB8500_DASLOTCONF4 0x36 +#define AB8500_DASLOTCONF5 0x37 +#define AB8500_DASLOTCONF6 0x38 +#define AB8500_DASLOTCONF7 0x39 +#define AB8500_DASLOTCONF8 0x3A +#define AB8500_CLASSDCONF1 0x3B +#define AB8500_CLASSDCONF2 0x3C +#define AB8500_CLASSDCONF3 0x3D +#define AB8500_DMICFILTCONF 0x3E +#define AB8500_DIGMULTCONF1 0x3F +#define AB8500_DIGMULTCONF2 0x40 +#define AB8500_ADDIGGAIN1 0x41 +#define AB8500_ADDIGGAIN2 0x42 +#define AB8500_ADDIGGAIN3 0x43 +#define AB8500_ADDIGGAIN4 0x44 +#define AB8500_ADDIGGAIN5 0x45 +#define AB8500_ADDIGGAIN6 0x46 +#define AB8500_DADIGGAIN1 0x47 +#define AB8500_DADIGGAIN2 0x48 +#define AB8500_DADIGGAIN3 0x49 +#define AB8500_DADIGGAIN4 0x4A +#define AB8500_DADIGGAIN5 0x4B +#define AB8500_DADIGGAIN6 0x4C +#define AB8500_ADDIGLOOPGAIN1 0x4D +#define AB8500_ADDIGLOOPGAIN2 0x4E +#define AB8500_HSLEARDIGGAIN 0x4F +#define AB8500_HSRDIGGAIN 0x50 +#define AB8500_SIDFIRGAIN1 0x51 +#define AB8500_SIDFIRGAIN2 0x52 +#define AB8500_ANCCONF1 0x53 +#define AB8500_ANCCONF2 0x54 +#define AB8500_ANCCONF3 0x55 +#define AB8500_ANCCONF4 0x56 +#define AB8500_ANCCONF5 0x57 +#define AB8500_ANCCONF6 0x58 +#define AB8500_ANCCONF7 0x59 +#define AB8500_ANCCONF8 0x5A +#define AB8500_ANCCONF9 0x5B +#define AB8500_ANCCONF10 0x5C +#define AB8500_ANCCONF11 0x5D +#define AB8500_ANCCONF12 0x5E +#define AB8500_ANCCONF13 0x5F +#define AB8500_ANCCONF14 0x60 +#define AB8500_SIDFIRADR 0x61 +#define AB8500_SIDFIRCOEF1 0x62 +#define AB8500_SIDFIRCOEF2 0x63 +#define AB8500_SIDFIRCONF 0x64 +#define AB8500_AUDINTMASK1 0x65 +#define AB8500_AUDINTSOURCE1 0x66 +#define AB8500_AUDINTMASK2 0x67 +#define AB8500_AUDINTSOURCE2 0x68 +#define AB8500_FIFOCONF1 0x69 +#define AB8500_FIFOCONF2 0x6A +#define AB8500_FIFOCONF3 0x6B +#define AB8500_FIFOCONF4 0x6C +#define AB8500_FIFOCONF5 0x6D +#define AB8500_FIFOCONF6 0x6E +#define AB8500_AUDREV 0x6F + +#define AB8500_FIRST_REG AB8500_POWERUP +#define AB8500_LAST_REG AB8500_AUDREV +#define AB8500_CACHEREGNUM (AB8500_LAST_REG + 1) + +#define AB8500_MASK_ALL 0xFF +#define AB8500_MASK_SLOT(slot) ((slot & 1) ? 0xF0 : 0x0F) +#define AB8500_MASK_NONE 0x00 + +/* AB8500_POWERUP */ +#define AB8500_POWERUP_POWERUP 7 +#define AB8500_POWERUP_ENANA 3 + +/* AB8500_AUDSWRESET */ +#define AB8500_AUDSWRESET_SWRESET 7 + +/* AB8500_ADPATHENA */ +#define AB8500_ADPATHENA_ENAD12 7 +#define AB8500_ADPATHENA_ENAD34 5 +#define AB8500_ADPATHENA_ENAD5768 3 + +/* AB8500_DAPATHENA */ +#define AB8500_DAPATHENA_ENDA1 7 +#define AB8500_DAPATHENA_ENDA2 6 +#define AB8500_DAPATHENA_ENDA3 5 +#define AB8500_DAPATHENA_ENDA4 4 +#define AB8500_DAPATHENA_ENDA5 3 +#define AB8500_DAPATHENA_ENDA6 2 + +/* AB8500_ANACONF1 */ +#define AB8500_ANACONF1_HSLOWPOW 7 +#define AB8500_ANACONF1_DACLOWPOW1 6 +#define AB8500_ANACONF1_DACLOWPOW0 5 +#define AB8500_ANACONF1_EARDACLOWPOW 4 +#define AB8500_ANACONF1_EARSELCM 2 +#define AB8500_ANACONF1_HSHPEN 1 +#define AB8500_ANACONF1_EARDRVLOWPOW 0 + +/* AB8500_ANACONF2 */ +#define AB8500_ANACONF2_ENMIC1 7 +#define AB8500_ANACONF2_ENMIC2 6 +#define AB8500_ANACONF2_ENLINL 5 +#define AB8500_ANACONF2_ENLINR 4 +#define AB8500_ANACONF2_MUTMIC1 3 +#define AB8500_ANACONF2_MUTMIC2 2 +#define AB8500_ANACONF2_MUTLINL 1 +#define AB8500_ANACONF2_MUTLINR 0 + +/* AB8500_DIGMICCONF */ +#define AB8500_DIGMICCONF_ENDMIC1 7 +#define AB8500_DIGMICCONF_ENDMIC2 6 +#define AB8500_DIGMICCONF_ENDMIC3 5 +#define AB8500_DIGMICCONF_ENDMIC4 4 +#define AB8500_DIGMICCONF_ENDMIC5 3 +#define AB8500_DIGMICCONF_ENDMIC6 2 +#define AB8500_DIGMICCONF_HSFADSPEED 0 + +/* AB8500_ANACONF3 */ +#define AB8500_ANACONF3_MIC1SEL 7 +#define AB8500_ANACONF3_LINRSEL 6 +#define AB8500_ANACONF3_ENDRVHSL 5 +#define AB8500_ANACONF3_ENDRVHSR 4 +#define AB8500_ANACONF3_ENADCMIC 2 +#define AB8500_ANACONF3_ENADCLINL 1 +#define AB8500_ANACONF3_ENADCLINR 0 + +/* AB8500_ANACONF4 */ +#define AB8500_ANACONF4_DISPDVSS 7 +#define AB8500_ANACONF4_ENEAR 6 +#define AB8500_ANACONF4_ENHSL 5 +#define AB8500_ANACONF4_ENHSR 4 +#define AB8500_ANACONF4_ENHFL 3 +#define AB8500_ANACONF4_ENHFR 2 +#define AB8500_ANACONF4_ENVIB1 1 +#define AB8500_ANACONF4_ENVIB2 0 + +/* AB8500_DAPATHCONF */ +#define AB8500_DAPATHCONF_ENDACEAR 6 +#define AB8500_DAPATHCONF_ENDACHSL 5 +#define AB8500_DAPATHCONF_ENDACHSR 4 +#define AB8500_DAPATHCONF_ENDACHFL 3 +#define AB8500_DAPATHCONF_ENDACHFR 2 +#define AB8500_DAPATHCONF_ENDACVIB1 1 +#define AB8500_DAPATHCONF_ENDACVIB2 0 + +/* AB8500_MUTECONF */ +#define AB8500_MUTECONF_MUTEAR 6 +#define AB8500_MUTECONF_MUTHSL 5 +#define AB8500_MUTECONF_MUTHSR 4 +#define AB8500_MUTECONF_MUTDACEAR 2 +#define AB8500_MUTECONF_MUTDACHSL 1 +#define AB8500_MUTECONF_MUTDACHSR 0 + +/* AB8500_SHORTCIRCONF */ +#define AB8500_SHORTCIRCONF_ENSHORTPWD 7 +#define AB8500_SHORTCIRCONF_EARSHORTDIS 6 +#define AB8500_SHORTCIRCONF_HSSHORTDIS 5 +#define AB8500_SHORTCIRCONF_HSPULLDEN 4 +#define AB8500_SHORTCIRCONF_HSOSCEN 2 +#define AB8500_SHORTCIRCONF_HSFADDIS 1 +#define AB8500_SHORTCIRCONF_HSZCDDIS 0 +/* Zero cross should be disabled */ + +/* AB8500_ANACONF5 */ +#define AB8500_ANACONF5_ENCPHS 7 +#define AB8500_ANACONF5_HSLDACTOLOL 5 +#define AB8500_ANACONF5_HSRDACTOLOR 4 +#define AB8500_ANACONF5_ENLOL 3 +#define AB8500_ANACONF5_ENLOR 2 +#define AB8500_ANACONF5_HSAUTOEN 0 + +/* AB8500_ENVCPCONF */ +#define AB8500_ENVCPCONF_ENVDETHTHRE 4 +#define AB8500_ENVCPCONF_ENVDETLTHRE 0 +#define AB8500_ENVCPCONF_ENVDETHTHRE_MAX 0x0F +#define AB8500_ENVCPCONF_ENVDETLTHRE_MAX 0x0F + +/* AB8500_SIGENVCONF */ +#define AB8500_SIGENVCONF_CPLVEN 5 +#define AB8500_SIGENVCONF_ENVDETCPEN 4 +#define AB8500_SIGENVCONF_ENVDETTIME 0 +#define AB8500_SIGENVCONF_ENVDETTIME_MAX 0x0F + +/* AB8500_PWMGENCONF1 */ +#define AB8500_PWMGENCONF1_PWMTOVIB1 7 +#define AB8500_PWMGENCONF1_PWMTOVIB2 6 +#define AB8500_PWMGENCONF1_PWM1CTRL 5 +#define AB8500_PWMGENCONF1_PWM2CTRL 4 +#define AB8500_PWMGENCONF1_PWM1NCTRL 3 +#define AB8500_PWMGENCONF1_PWM1PCTRL 2 +#define AB8500_PWMGENCONF1_PWM2NCTRL 1 +#define AB8500_PWMGENCONF1_PWM2PCTRL 0 + +/* AB8500_PWMGENCONF2 */ +/* AB8500_PWMGENCONF3 */ +/* AB8500_PWMGENCONF4 */ +/* AB8500_PWMGENCONF5 */ +#define AB8500_PWMGENCONFX_PWMVIBXPOL 7 +#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC 0 +#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX 0x64 + +/* AB8500_ANAGAIN1 */ +/* AB8500_ANAGAIN2 */ +#define AB8500_ANAGAINX_ENSEMICX 7 +#define AB8500_ANAGAINX_LOWPOWMICX 6 +#define AB8500_ANAGAINX_MICXGAIN 0 +#define AB8500_ANAGAINX_MICXGAIN_MAX 0x1F + +/* AB8500_ANAGAIN3 */ +#define AB8500_ANAGAIN3_HSLGAIN 4 +#define AB8500_ANAGAIN3_HSRGAIN 0 +#define AB8500_ANAGAIN3_HSXGAIN_MAX 0x0F + +/* AB8500_ANAGAIN4 */ +#define AB8500_ANAGAIN4_LINLGAIN 4 +#define AB8500_ANAGAIN4_LINRGAIN 0 +#define AB8500_ANAGAIN4_LINXGAIN_MAX 0x0F + +/* AB8500_DIGLINHSLGAIN */ +/* AB8500_DIGLINHSRGAIN */ +#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN 0 +#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX 0x13 + +/* AB8500_ADFILTCONF */ +#define AB8500_ADFILTCONF_AD1NH 7 +#define AB8500_ADFILTCONF_AD2NH 6 +#define AB8500_ADFILTCONF_AD3NH 5 +#define AB8500_ADFILTCONF_AD4NH 4 +#define AB8500_ADFILTCONF_AD1VOICE 3 +#define AB8500_ADFILTCONF_AD2VOICE 2 +#define AB8500_ADFILTCONF_AD3VOICE 1 +#define AB8500_ADFILTCONF_AD4VOICE 0 + +/* AB8500_DIGIFCONF1 */ +#define AB8500_DIGIFCONF1_ENMASTGEN 7 +#define AB8500_DIGIFCONF1_IF1BITCLKOS1 6 +#define AB8500_DIGIFCONF1_IF1BITCLKOS0 5 +#define AB8500_DIGIFCONF1_ENFSBITCLK1 4 +#define AB8500_DIGIFCONF1_IF0BITCLKOS1 2 +#define AB8500_DIGIFCONF1_IF0BITCLKOS0 1 +#define AB8500_DIGIFCONF1_ENFSBITCLK0 0 + +/* AB8500_DIGIFCONF2 */ +#define AB8500_DIGIFCONF2_FSYNC0P 6 +#define AB8500_DIGIFCONF2_BITCLK0P 5 +#define AB8500_DIGIFCONF2_IF0DEL 4 +#define AB8500_DIGIFCONF2_IF0FORMAT1 3 +#define AB8500_DIGIFCONF2_IF0FORMAT0 2 +#define AB8500_DIGIFCONF2_IF0WL1 1 +#define AB8500_DIGIFCONF2_IF0WL0 0 + +/* AB8500_DIGIFCONF3 */ +#define AB8500_DIGIFCONF3_IF0DATOIF1AD 7 +#define AB8500_DIGIFCONF3_IF0CLKTOIF1CLK 6 +#define AB8500_DIGIFCONF3_IF1MASTER 5 +#define AB8500_DIGIFCONF3_IF1DATOIF0AD 3 +#define AB8500_DIGIFCONF3_IF1CLKTOIF0CLK 2 +#define AB8500_DIGIFCONF3_IF0MASTER 1 +#define AB8500_DIGIFCONF3_IF0BFIFOEN 0 + +/* AB8500_DIGIFCONF4 */ +#define AB8500_DIGIFCONF4_FSYNC1P 6 +#define AB8500_DIGIFCONF4_BITCLK1P 5 +#define AB8500_DIGIFCONF4_IF1DEL 4 +#define AB8500_DIGIFCONF4_IF1FORMAT1 3 +#define AB8500_DIGIFCONF4_IF1FORMAT0 2 +#define AB8500_DIGIFCONF4_IF1WL1 1 +#define AB8500_DIGIFCONF4_IF1WL0 0 + +/* AB8500_ADSLOTSELX */ +#define AB8500_AD_OUT1 0x0 +#define AB8500_AD_OUT2 0x1 +#define AB8500_AD_OUT3 0x2 +#define AB8500_AD_OUT4 0x3 +#define AB8500_AD_OUT5 0x4 +#define AB8500_AD_OUT6 0x5 +#define AB8500_AD_OUT7 0x6 +#define AB8500_AD_OUT8 0x7 +#define AB8500_ZEROES 0x8 +#define AB8500_TRISTATE 0xF +#define AB8500_ADSLOTSELX_EVEN_SHIFT 0 +#define AB8500_ADSLOTSELX_ODD_SHIFT 4 +#define AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(out, slot) \ + ((out) << (((slot) & 1) ? \ + AB8500_ADSLOTSELX_ODD_SHIFT : AB8500_ADSLOTSELX_EVEN_SHIFT)) + +/* AB8500_ADSLOTHIZCTRL1 */ +/* AB8500_ADSLOTHIZCTRL2 */ +/* AB8500_ADSLOTHIZCTRL3 */ +/* AB8500_ADSLOTHIZCTRL4 */ +/* AB8500_DASLOTCONF1 */ +#define AB8500_DASLOTCONF1_DA12VOICE 7 +#define AB8500_DASLOTCONF1_SWAPDA12_34 6 +#define AB8500_DASLOTCONF1_DAI7TOADO1 5 + +/* AB8500_DASLOTCONF2 */ +#define AB8500_DASLOTCONF2_DAI8TOADO2 5 + +/* AB8500_DASLOTCONF3 */ +#define AB8500_DASLOTCONF3_DA34VOICE 7 +#define AB8500_DASLOTCONF3_DAI7TOADO3 5 + +/* AB8500_DASLOTCONF4 */ +#define AB8500_DASLOTCONF4_DAI8TOADO4 5 + +/* AB8500_DASLOTCONF5 */ +#define AB8500_DASLOTCONF5_DA56VOICE 7 +#define AB8500_DASLOTCONF5_DAI7TOADO5 5 + +/* AB8500_DASLOTCONF6 */ +#define AB8500_DASLOTCONF6_DAI8TOADO6 5 + +/* AB8500_DASLOTCONF7 */ +#define AB8500_DASLOTCONF7_DAI8TOADO7 5 + +/* AB8500_DASLOTCONF8 */ +#define AB8500_DASLOTCONF8_DAI7TOADO8 5 + +#define AB8500_DASLOTCONFX_SLTODAX_SHIFT 0 +#define AB8500_DASLOTCONFX_SLTODAX_MASK 0x1F + +/* AB8500_CLASSDCONF1 */ +#define AB8500_CLASSDCONF1_PARLHF 7 +#define AB8500_CLASSDCONF1_PARLVIB 6 +#define AB8500_CLASSDCONF1_VIB1SWAPEN 3 +#define AB8500_CLASSDCONF1_VIB2SWAPEN 2 +#define AB8500_CLASSDCONF1_HFLSWAPEN 1 +#define AB8500_CLASSDCONF1_HFRSWAPEN 0 + +/* AB8500_CLASSDCONF2 */ +#define AB8500_CLASSDCONF2_FIRBYP3 7 +#define AB8500_CLASSDCONF2_FIRBYP2 6 +#define AB8500_CLASSDCONF2_FIRBYP1 5 +#define AB8500_CLASSDCONF2_FIRBYP0 4 +#define AB8500_CLASSDCONF2_HIGHVOLEN3 3 +#define AB8500_CLASSDCONF2_HIGHVOLEN2 2 +#define AB8500_CLASSDCONF2_HIGHVOLEN1 1 +#define AB8500_CLASSDCONF2_HIGHVOLEN0 0 + +/* AB8500_CLASSDCONF3 */ +#define AB8500_CLASSDCONF3_DITHHPGAIN 4 +#define AB8500_CLASSDCONF3_DITHHPGAIN_MAX 0x0A +#define AB8500_CLASSDCONF3_DITHWGAIN 0 +#define AB8500_CLASSDCONF3_DITHWGAIN_MAX 0x0A + +/* AB8500_DMICFILTCONF */ +#define AB8500_DMICFILTCONF_ANCINSEL 7 +#define AB8500_DMICFILTCONF_DA3TOEAR 6 +#define AB8500_DMICFILTCONF_DMIC1SINC3 5 +#define AB8500_DMICFILTCONF_DMIC2SINC3 4 +#define AB8500_DMICFILTCONF_DMIC3SINC3 3 +#define AB8500_DMICFILTCONF_DMIC4SINC3 2 +#define AB8500_DMICFILTCONF_DMIC5SINC3 1 +#define AB8500_DMICFILTCONF_DMIC6SINC3 0 + +/* AB8500_DIGMULTCONF1 */ +#define AB8500_DIGMULTCONF1_DATOHSLEN 7 +#define AB8500_DIGMULTCONF1_DATOHSREN 6 +#define AB8500_DIGMULTCONF1_AD1SEL 5 +#define AB8500_DIGMULTCONF1_AD2SEL 4 +#define AB8500_DIGMULTCONF1_AD3SEL 3 +#define AB8500_DIGMULTCONF1_AD5SEL 2 +#define AB8500_DIGMULTCONF1_AD6SEL 1 +#define AB8500_DIGMULTCONF1_ANCSEL 0 + +/* AB8500_DIGMULTCONF2 */ +#define AB8500_DIGMULTCONF2_DATOHFREN 7 +#define AB8500_DIGMULTCONF2_DATOHFLEN 6 +#define AB8500_DIGMULTCONF2_HFRSEL 5 +#define AB8500_DIGMULTCONF2_HFLSEL 4 +#define AB8500_DIGMULTCONF2_FIRSID1SEL 2 +#define AB8500_DIGMULTCONF2_FIRSID2SEL 0 + +/* AB8500_ADDIGGAIN1 */ +/* AB8500_ADDIGGAIN2 */ +/* AB8500_ADDIGGAIN3 */ +/* AB8500_ADDIGGAIN4 */ +/* AB8500_ADDIGGAIN5 */ +/* AB8500_ADDIGGAIN6 */ +#define AB8500_ADDIGGAINX_FADEDISADX 6 +#define AB8500_ADDIGGAINX_ADXGAIN_MAX 0x3F + +/* AB8500_DADIGGAIN1 */ +/* AB8500_DADIGGAIN2 */ +/* AB8500_DADIGGAIN3 */ +/* AB8500_DADIGGAIN4 */ +/* AB8500_DADIGGAIN5 */ +/* AB8500_DADIGGAIN6 */ +#define AB8500_DADIGGAINX_FADEDISDAX 6 +#define AB8500_DADIGGAINX_DAXGAIN_MAX 0x3F + +/* AB8500_ADDIGLOOPGAIN1 */ +/* AB8500_ADDIGLOOPGAIN2 */ +#define AB8500_ADDIGLOOPGAINX_FADEDISADXL 6 +#define AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX 0x3F + +/* AB8500_HSLEARDIGGAIN */ +#define AB8500_HSLEARDIGGAIN_HSSINC1 7 +#define AB8500_HSLEARDIGGAIN_FADEDISHSL 4 +#define AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX 0x09 + +/* AB8500_HSRDIGGAIN */ +#define AB8500_HSRDIGGAIN_FADESPEED 6 +#define AB8500_HSRDIGGAIN_FADEDISHSR 4 +#define AB8500_HSRDIGGAIN_HSRDGAIN_MAX 0x09 + +/* AB8500_SIDFIRGAIN1 */ +/* AB8500_SIDFIRGAIN2 */ +#define AB8500_SIDFIRGAINX_FIRSIDXGAIN_MAX 0x1F + +/* AB8500_ANCCONF1 */ +#define AB8500_ANCCONF1_ANCIIRUPDATE 3 +#define AB8500_ANCCONF1_ENANC 2 +#define AB8500_ANCCONF1_ANCIIRINIT 1 +#define AB8500_ANCCONF1_ANCFIRUPDATE 0 + +/* AB8500_ANCCONF2 */ +#define AB8500_ANCCONF2_SHIFT 5 +#define AB8500_ANCCONF2_MIN -0x10 +#define AB8500_ANCCONF2_MAX 0xF + +/* AB8500_ANCCONF3 */ +#define AB8500_ANCCONF3_SHIFT 5 +#define AB8500_ANCCONF3_MIN -0x10 +#define AB8500_ANCCONF3_MAX 0xF + +/* AB8500_ANCCONF4 */ +#define AB8500_ANCCONF4_SHIFT 5 +#define AB8500_ANCCONF4_MIN -0x10 +#define AB8500_ANCCONF4_MAX 0xF + +/* AB8500_ANC_FIR_COEFFS */ +#define AB8500_ANC_FIR_COEFF_MIN -0x8000 +#define AB8500_ANC_FIR_COEFF_MAX 0x7FFF +#define AB8500_ANC_FIR_COEFFS 15 + +/* AB8500_ANC_IIR_COEFFS */ +#define AB8500_ANC_IIR_COEFF_MIN -0x800000 +#define AB8500_ANC_IIR_COEFF_MAX 0x7FFFFF +#define AB8500_ANC_IIR_COEFFS 24 +/* AB8500_ANC_WARP_DELAY */ +#define AB8500_ANC_WARP_DELAY_SHIFT 16 +#define AB8500_ANC_WARP_DELAY_MIN 0x0000 +#define AB8500_ANC_WARP_DELAY_MAX 0xFFFF + +/* AB8500_ANCCONF11 */ +/* AB8500_ANCCONF12 */ +/* AB8500_ANCCONF13 */ +/* AB8500_ANCCONF14 */ + +/* AB8500_SIDFIRADR */ +#define AB8500_SIDFIRADR_FIRSIDSET 7 +#define AB8500_SIDFIRADR_ADDRESS_SHIFT 0 +#define AB8500_SIDFIRADR_ADDRESS_MAX 0x7F + +/* AB8500_SIDFIRCOEF1 */ +/* AB8500_SIDFIRCOEF2 */ +#define AB8500_SID_FIR_COEFF_MIN 0 +#define AB8500_SID_FIR_COEFF_MAX 0xFFFF +#define AB8500_SID_FIR_COEFFS 128 + +/* AB8500_SIDFIRCONF */ +#define AB8500_SIDFIRCONF_ENFIRSIDS 2 +#define AB8500_SIDFIRCONF_FIRSIDSTOIF1 1 +#define AB8500_SIDFIRCONF_FIRSIDBUSY 0 + +/* AB8500_AUDINTMASK1 */ +/* AB8500_AUDINTSOURCE1 */ +/* AB8500_AUDINTMASK2 */ +/* AB8500_AUDINTSOURCE2 */ + +/* AB8500_FIFOCONF1 */ +#define AB8500_FIFOCONF1_BFIFOMASK 0x80 +#define AB8500_FIFOCONF1_BFIFO19M2 0x40 +#define AB8500_FIFOCONF1_BFIFOINT_SHIFT 0 +#define AB8500_FIFOCONF1_BFIFOINT_MAX 0x3F + +/* AB8500_FIFOCONF2 */ +#define AB8500_FIFOCONF2_BFIFOTX_SHIFT 0 +#define AB8500_FIFOCONF2_BFIFOTX_MAX 0xFF + +/* AB8500_FIFOCONF3 */ +#define AB8500_FIFOCONF3_BFIFOEXSL_SHIFT 5 +#define AB8500_FIFOCONF3_BFIFOEXSL_MAX 0x5 +#define AB8500_FIFOCONF3_PREBITCLK0_SHIFT 2 +#define AB8500_FIFOCONF3_PREBITCLK0_MAX 0x7 +#define AB8500_FIFOCONF3_BFIFOMAST_SHIFT 1 +#define AB8500_FIFOCONF3_BFIFORUN_SHIFT 0 + +/* AB8500_FIFOCONF4 */ +#define AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT 0 +#define AB8500_FIFOCONF4_BFIFOFRAMSW_MAX 0xFF + +/* AB8500_FIFOCONF5 */ +#define AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT 0 +#define AB8500_FIFOCONF5_BFIFOWAKEUP_MAX 0xFF + +/* AB8500_FIFOCONF6 */ +#define AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT 0 +#define AB8500_FIFOCONF6_BFIFOSAMPLE_MAX 0xFF + +/* AB8500_AUDREV */ + +#endif diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c new file mode 100644 index 0000000000..0e013edfe6 --- /dev/null +++ b/sound/soc/codecs/ac97.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ac97.c -- ALSA Soc AC97 codec support + * + * Copyright 2005 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * + * Generic AC97 support. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct snd_soc_dapm_widget ac97_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route ac97_routes[] = { + { "AC97 Capture", NULL, "RX" }, + { "TX", NULL, "AC97 Playback" }, +}; + +static int ac97_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component); + + int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE; + return snd_ac97_set_rate(ac97, reg, substream->runtime->rate); +} + +static const struct snd_soc_dai_ops ac97_dai_ops = { + .prepare = ac97_prepare, +}; + +static struct snd_soc_dai_driver ac97_dai = { + .name = "ac97-hifi", + .playback = { + .stream_name = "AC97 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SND_SOC_STD_AC97_FMTS,}, + .capture = { + .stream_name = "AC97 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SND_SOC_STD_AC97_FMTS,}, + .ops = &ac97_dai_ops, +}; + +static int ac97_soc_probe(struct snd_soc_component *component) +{ + struct snd_ac97 *ac97; + struct snd_ac97_bus *ac97_bus; + struct snd_ac97_template ac97_template; + int ret; + + /* add codec as bus device for standard ac97 */ + ret = snd_ac97_bus(component->card->snd_card, 0, soc_ac97_ops, + NULL, &ac97_bus); + if (ret < 0) + return ret; + + memset(&ac97_template, 0, sizeof(struct snd_ac97_template)); + ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97); + if (ret < 0) + return ret; + + snd_soc_component_set_drvdata(component, ac97); + + return 0; +} + +#ifdef CONFIG_PM +static int ac97_soc_suspend(struct snd_soc_component *component) +{ + struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component); + + snd_ac97_suspend(ac97); + + return 0; +} + +static int ac97_soc_resume(struct snd_soc_component *component) +{ + + struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component); + + snd_ac97_resume(ac97); + + return 0; +} +#else +#define ac97_soc_suspend NULL +#define ac97_soc_resume NULL +#endif + +static const struct snd_soc_component_driver soc_component_dev_ac97 = { + .probe = ac97_soc_probe, + .suspend = ac97_soc_suspend, + .resume = ac97_soc_resume, + .dapm_widgets = ac97_widgets, + .num_dapm_widgets = ARRAY_SIZE(ac97_widgets), + .dapm_routes = ac97_routes, + .num_dapm_routes = ARRAY_SIZE(ac97_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int ac97_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_ac97, &ac97_dai, 1); +} + +static struct platform_driver ac97_codec_driver = { + .driver = { + .name = "ac97-codec", + }, + + .probe = ac97_probe, +}; + +module_platform_driver(ac97_codec_driver); + +MODULE_DESCRIPTION("Soc Generic AC97 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ac97-codec"); diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c new file mode 100644 index 0000000000..949077108b --- /dev/null +++ b/sound/soc/codecs/ad1836.c @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + /* + * Audio Codec driver supporting: + * AD1835A, AD1836, AD1837A, AD1838A, AD1839A + * + * Copyright 2009-2011 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ad1836.h" + +enum ad1836_type { + AD1835, + AD1836, + AD1838, +}; + +/* codec private data */ +struct ad1836_priv { + enum ad1836_type type; + struct regmap *regmap; +}; + +/* + * AD1836 volume/mute/de-emphasis etc. controls + */ +static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"}; + +static SOC_ENUM_SINGLE_DECL(ad1836_deemp_enum, + AD1836_DAC_CTRL1, 8, ad1836_deemp); + +#define AD1836_DAC_VOLUME(x) \ + SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \ + AD1836_DAC_R_VOL(x), 0, 0x3FF, 0) + +#define AD1836_DAC_SWITCH(x) \ + SOC_DOUBLE("DAC" #x " Playback Switch", AD1836_DAC_CTRL2, \ + AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1) + +#define AD1836_ADC_SWITCH(x) \ + SOC_DOUBLE("ADC" #x " Capture Switch", AD1836_ADC_CTRL2, \ + AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1) + +static const struct snd_kcontrol_new ad183x_dac_controls[] = { + AD1836_DAC_VOLUME(1), + AD1836_DAC_SWITCH(1), + AD1836_DAC_VOLUME(2), + AD1836_DAC_SWITCH(2), + AD1836_DAC_VOLUME(3), + AD1836_DAC_SWITCH(3), + AD1836_DAC_VOLUME(4), + AD1836_DAC_SWITCH(4), +}; + +static const struct snd_soc_dapm_widget ad183x_dac_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("DAC1OUT"), + SND_SOC_DAPM_OUTPUT("DAC2OUT"), + SND_SOC_DAPM_OUTPUT("DAC3OUT"), + SND_SOC_DAPM_OUTPUT("DAC4OUT"), +}; + +static const struct snd_soc_dapm_route ad183x_dac_routes[] = { + { "DAC1OUT", NULL, "DAC" }, + { "DAC2OUT", NULL, "DAC" }, + { "DAC3OUT", NULL, "DAC" }, + { "DAC4OUT", NULL, "DAC" }, +}; + +static const struct snd_kcontrol_new ad183x_adc_controls[] = { + AD1836_ADC_SWITCH(1), + AD1836_ADC_SWITCH(2), + AD1836_ADC_SWITCH(3), +}; + +static const struct snd_soc_dapm_widget ad183x_adc_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("ADC1IN"), + SND_SOC_DAPM_INPUT("ADC2IN"), +}; + +static const struct snd_soc_dapm_route ad183x_adc_routes[] = { + { "ADC", NULL, "ADC1IN" }, + { "ADC", NULL, "ADC2IN" }, +}; + +static const struct snd_kcontrol_new ad183x_controls[] = { + /* ADC high-pass filter */ + SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1, + AD1836_ADC_HIGHPASS_FILTER, 1, 0), + + /* DAC de-emphasis */ + SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum), +}; + +static const struct snd_soc_dapm_widget ad183x_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1, + AD1836_DAC_POWERDOWN, 1), + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1, + AD1836_ADC_POWERDOWN, 1, NULL, 0), +}; + +static const struct snd_soc_dapm_route ad183x_dapm_routes[] = { + { "DAC", NULL, "ADC_PWR" }, + { "ADC", NULL, "ADC_PWR" }, +}; + +static const DECLARE_TLV_DB_SCALE(ad1836_in_tlv, 0, 300, 0); + +static const struct snd_kcontrol_new ad1836_controls[] = { + SOC_DOUBLE_TLV("ADC2 Capture Volume", AD1836_ADC_CTRL1, 3, 0, 4, 0, + ad1836_in_tlv), +}; + +/* + * DAI ops entries + */ + +static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + /* at present, we support adc aux mode to interface with + * blackfin sport tdm mode + */ + case SND_SOC_DAIFMT_DSP_A: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + /* ALCLK,ABCLK are both output, AD1836 can only be provider */ + case SND_SOC_DAIFMT_CBP_CFP: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ad1836_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(dai->component); + int word_len = 0; + + /* bit size */ + switch (params_width(params)) { + case 16: + word_len = AD1836_WORD_LEN_16; + break; + case 20: + word_len = AD1836_WORD_LEN_20; + break; + case 24: + case 32: + word_len = AD1836_WORD_LEN_24; + break; + default: + return -EINVAL; + } + + regmap_update_bits(ad1836->regmap, AD1836_DAC_CTRL1, + AD1836_DAC_WORD_LEN_MASK, + word_len << AD1836_DAC_WORD_LEN_OFFSET); + + regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2, + AD1836_ADC_WORD_LEN_MASK, + word_len << AD1836_ADC_WORD_OFFSET); + + return 0; +} + +static const struct snd_soc_dai_ops ad1836_dai_ops = { + .hw_params = ad1836_hw_params, + .set_fmt = ad1836_set_dai_fmt, +}; + +#define AD183X_DAI(_name, num_dacs, num_adcs) \ +{ \ + .name = _name "-hifi", \ + .playback = { \ + .stream_name = "Playback", \ + .channels_min = 2, \ + .channels_max = (num_dacs) * 2, \ + .rates = SNDRV_PCM_RATE_48000, \ + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \ + }, \ + .capture = { \ + .stream_name = "Capture", \ + .channels_min = 2, \ + .channels_max = (num_adcs) * 2, \ + .rates = SNDRV_PCM_RATE_48000, \ + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \ + }, \ + .ops = &ad1836_dai_ops, \ +} + +static struct snd_soc_dai_driver ad183x_dais[] = { + [AD1835] = AD183X_DAI("ad1835", 4, 1), + [AD1836] = AD183X_DAI("ad1836", 3, 2), + [AD1838] = AD183X_DAI("ad1838", 3, 1), +}; + +#ifdef CONFIG_PM +static int ad1836_suspend(struct snd_soc_component *component) +{ + struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component); + /* reset clock control mode */ + return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2, + AD1836_ADC_SERFMT_MASK, 0); +} + +static int ad1836_resume(struct snd_soc_component *component) +{ + struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component); + /* restore clock control mode */ + return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2, + AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX); +} +#else +#define ad1836_suspend NULL +#define ad1836_resume NULL +#endif + +static int ad1836_probe(struct snd_soc_component *component) +{ + struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int num_dacs, num_adcs; + int ret = 0; + int i; + + num_dacs = ad183x_dais[ad1836->type].playback.channels_max / 2; + num_adcs = ad183x_dais[ad1836->type].capture.channels_max / 2; + + /* default setting for ad1836 */ + /* de-emphasis: 48kHz, power-on dac */ + regmap_write(ad1836->regmap, AD1836_DAC_CTRL1, 0x300); + /* unmute dac channels */ + regmap_write(ad1836->regmap, AD1836_DAC_CTRL2, 0x0); + /* high-pass filter enable, power-on adc */ + regmap_write(ad1836->regmap, AD1836_ADC_CTRL1, 0x100); + /* unmute adc channles, adc aux mode */ + regmap_write(ad1836->regmap, AD1836_ADC_CTRL2, 0x180); + /* volume */ + for (i = 1; i <= num_dacs; ++i) { + regmap_write(ad1836->regmap, AD1836_DAC_L_VOL(i), 0x3FF); + regmap_write(ad1836->regmap, AD1836_DAC_R_VOL(i), 0x3FF); + } + + if (ad1836->type == AD1836) { + /* left/right diff:PGA/MUX */ + regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x3A); + ret = snd_soc_add_component_controls(component, ad1836_controls, + ARRAY_SIZE(ad1836_controls)); + if (ret) + return ret; + } else { + regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x00); + } + + ret = snd_soc_add_component_controls(component, ad183x_dac_controls, num_dacs * 2); + if (ret) + return ret; + + ret = snd_soc_add_component_controls(component, ad183x_adc_controls, num_adcs); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, ad183x_dac_dapm_widgets, num_dacs); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, ad183x_adc_dapm_widgets, num_adcs); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, ad183x_dac_routes, num_dacs); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs); + + return ret; +} + +/* power down chip */ +static void ad1836_remove(struct snd_soc_component *component) +{ + struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component); + /* reset clock control mode */ + regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2, + AD1836_ADC_SERFMT_MASK, 0); +} + +static const struct snd_soc_component_driver soc_component_dev_ad1836 = { + .probe = ad1836_probe, + .remove = ad1836_remove, + .suspend = ad1836_suspend, + .resume = ad1836_resume, + .controls = ad183x_controls, + .num_controls = ARRAY_SIZE(ad183x_controls), + .dapm_widgets = ad183x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets), + .dapm_routes = ad183x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct reg_default ad1836_reg_defaults[] = { + { AD1836_DAC_CTRL1, 0x0000 }, + { AD1836_DAC_CTRL2, 0x0000 }, + { AD1836_DAC_L_VOL(0), 0x0000 }, + { AD1836_DAC_R_VOL(0), 0x0000 }, + { AD1836_DAC_L_VOL(1), 0x0000 }, + { AD1836_DAC_R_VOL(1), 0x0000 }, + { AD1836_DAC_L_VOL(2), 0x0000 }, + { AD1836_DAC_R_VOL(2), 0x0000 }, + { AD1836_DAC_L_VOL(3), 0x0000 }, + { AD1836_DAC_R_VOL(3), 0x0000 }, + { AD1836_ADC_CTRL1, 0x0000 }, + { AD1836_ADC_CTRL2, 0x0000 }, + { AD1836_ADC_CTRL3, 0x0000 }, +}; + +static const struct regmap_config ad1836_regmap_config = { + .val_bits = 12, + .reg_bits = 4, + .read_flag_mask = 0x08, + + .max_register = AD1836_ADC_CTRL3, + .reg_defaults = ad1836_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ad1836_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static int ad1836_spi_probe(struct spi_device *spi) +{ + struct ad1836_priv *ad1836; + int ret; + + ad1836 = devm_kzalloc(&spi->dev, sizeof(struct ad1836_priv), + GFP_KERNEL); + if (ad1836 == NULL) + return -ENOMEM; + + ad1836->regmap = devm_regmap_init_spi(spi, &ad1836_regmap_config); + if (IS_ERR(ad1836->regmap)) + return PTR_ERR(ad1836->regmap); + + ad1836->type = spi_get_device_id(spi)->driver_data; + + spi_set_drvdata(spi, ad1836); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_ad1836, &ad183x_dais[ad1836->type], 1); + return ret; +} + +static const struct spi_device_id ad1836_ids[] = { + { "ad1835", AD1835 }, + { "ad1836", AD1836 }, + { "ad1837", AD1835 }, + { "ad1838", AD1838 }, + { "ad1839", AD1838 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, ad1836_ids); + +static struct spi_driver ad1836_spi_driver = { + .driver = { + .name = "ad1836", + }, + .probe = ad1836_spi_probe, + .id_table = ad1836_ids, +}; + +module_spi_driver(ad1836_spi_driver); + +MODULE_DESCRIPTION("ASoC ad1836 driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h new file mode 100644 index 0000000000..05711fab68 --- /dev/null +++ b/sound/soc/codecs/ad1836.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Audio Codec driver supporting: + * AD1835A, AD1836, AD1837A, AD1838A, AD1839A + * + * Copyright 2009-2011 Analog Devices Inc. + */ + +#ifndef __AD1836_H__ +#define __AD1836_H__ + +#define AD1836_DAC_CTRL1 0 +#define AD1836_DAC_POWERDOWN 2 +#define AD1836_DAC_SERFMT_MASK 0xE0 +#define AD1836_DAC_SERFMT_PCK256 (0x4 << 5) +#define AD1836_DAC_SERFMT_PCK128 (0x5 << 5) +#define AD1836_DAC_WORD_LEN_MASK 0x18 +#define AD1836_DAC_WORD_LEN_OFFSET 3 + +#define AD1836_DAC_CTRL2 1 + +/* These macros are one-based. So AD183X_MUTE_LEFT(1) will return the mute bit + * for the first ADC/DAC */ +#define AD1836_MUTE_LEFT(x) (((x) * 2) - 2) +#define AD1836_MUTE_RIGHT(x) (((x) * 2) - 1) + +#define AD1836_DAC_L_VOL(x) ((x) * 2) +#define AD1836_DAC_R_VOL(x) (1 + ((x) * 2)) + +#define AD1836_ADC_CTRL1 12 +#define AD1836_ADC_POWERDOWN 7 +#define AD1836_ADC_HIGHPASS_FILTER 8 + +#define AD1836_ADC_CTRL2 13 +#define AD1836_ADC_WORD_LEN_MASK 0x30 +#define AD1836_ADC_WORD_OFFSET 4 +#define AD1836_ADC_SERFMT_MASK (7 << 6) +#define AD1836_ADC_SERFMT_PCK256 (0x4 << 6) +#define AD1836_ADC_SERFMT_PCK128 (0x5 << 6) +#define AD1836_ADC_AUX (0x6 << 6) + +#define AD1836_ADC_CTRL3 14 + +#define AD1836_NUM_REGS 16 + +#define AD1836_WORD_LEN_24 0x0 +#define AD1836_WORD_LEN_20 0x1 +#define AD1836_WORD_LEN_16 0x2 + +#endif diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c new file mode 100644 index 0000000000..15d74bb31c --- /dev/null +++ b/sound/soc/codecs/ad193x-i2c.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD1936/AD1937 audio driver + * + * Copyright 2014 Analog Devices Inc. + */ + +#include +#include +#include + +#include + +#include "ad193x.h" + +static const struct i2c_device_id ad193x_id[] = { + { "ad1936", AD193X }, + { "ad1937", AD193X }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad193x_id); + +static int ad193x_i2c_probe(struct i2c_client *client) +{ + struct regmap_config config; + const struct i2c_device_id *id = i2c_match_id(ad193x_id, client); + + config = ad193x_regmap_config; + config.val_bits = 8; + config.reg_bits = 8; + + return ad193x_probe(&client->dev, + devm_regmap_init_i2c(client, &config), + (enum ad193x_type)id->driver_data); +} + +static struct i2c_driver ad193x_i2c_driver = { + .driver = { + .name = "ad193x", + }, + .probe = ad193x_i2c_probe, + .id_table = ad193x_id, +}; +module_i2c_driver(ad193x_i2c_driver); + +MODULE_DESCRIPTION("ASoC AD1936/AD1937 audio CODEC driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c new file mode 100644 index 0000000000..bce96a3d81 --- /dev/null +++ b/sound/soc/codecs/ad193x-spi.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD1938/AD1939 audio driver + * + * Copyright 2014 Analog Devices Inc. + */ + +#include +#include +#include + +#include + +#include "ad193x.h" + +static int ad193x_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap_config config; + + config = ad193x_regmap_config; + config.val_bits = 8; + config.reg_bits = 16; + config.read_flag_mask = 0x09; + config.write_flag_mask = 0x08; + + return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config), + (enum ad193x_type)id->driver_data); +} + +static const struct spi_device_id ad193x_spi_id[] = { + { "ad193x", AD193X }, + { "ad1933", AD1933 }, + { "ad1934", AD1934 }, + { "ad1938", AD193X }, + { "ad1939", AD193X }, + { "adau1328", AD193X }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad193x_spi_id); + +static struct spi_driver ad193x_spi_driver = { + .driver = { + .name = "ad193x", + }, + .probe = ad193x_spi_probe, + .id_table = ad193x_spi_id, +}; +module_spi_driver(ad193x_spi_driver); + +MODULE_DESCRIPTION("ASoC AD1938/AD1939 audio CODEC driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c new file mode 100644 index 0000000000..1d3c4d94b4 --- /dev/null +++ b/sound/soc/codecs/ad193x.c @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AD193X Audio Codec driver supporting AD1936/7/8/9 + * + * Copyright 2010 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ad193x.h" + +/* codec private data */ +struct ad193x_priv { + struct regmap *regmap; + enum ad193x_type type; + int sysclk; +}; + +/* + * AD193X volume/mute/de-emphasis etc. controls + */ +static const char * const ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"}; + +static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1, + ad193x_deemp); + +static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0); + +static const unsigned int ad193x_sb[] = {32}; + +static struct snd_pcm_hw_constraint_list constr = { + .list = ad193x_sb, + .count = ARRAY_SIZE(ad193x_sb), +}; + +static const struct snd_kcontrol_new ad193x_snd_controls[] = { + /* DAC volume control */ + SOC_DOUBLE_R_TLV("DAC1 Volume", AD193X_DAC_L1_VOL, + AD193X_DAC_R1_VOL, 0, 0xFF, 1, adau193x_tlv), + SOC_DOUBLE_R_TLV("DAC2 Volume", AD193X_DAC_L2_VOL, + AD193X_DAC_R2_VOL, 0, 0xFF, 1, adau193x_tlv), + SOC_DOUBLE_R_TLV("DAC3 Volume", AD193X_DAC_L3_VOL, + AD193X_DAC_R3_VOL, 0, 0xFF, 1, adau193x_tlv), + SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL, + AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv), + + /* DAC switch control */ + SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE, + AD193X_DACR1_MUTE, 1, 1), + SOC_DOUBLE("DAC2 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL2_MUTE, + AD193X_DACR2_MUTE, 1, 1), + SOC_DOUBLE("DAC3 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL3_MUTE, + AD193X_DACR3_MUTE, 1, 1), + SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE, + AD193X_DACR4_MUTE, 1, 1), + + /* DAC de-emphasis */ + SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum), +}; + +static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = { + /* ADC switch control */ + SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE, + AD193X_ADCR1_MUTE, 1, 1), + SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE, + AD193X_ADCR2_MUTE, 1, 1), + + /* ADC high-pass filter */ + SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0, + AD193X_ADC_HIGHPASS_FILTER, 1, 0), +}; + +static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0), + SND_SOC_DAPM_VMID("VMID"), + SND_SOC_DAPM_OUTPUT("DAC1OUT"), + SND_SOC_DAPM_OUTPUT("DAC2OUT"), + SND_SOC_DAPM_OUTPUT("DAC3OUT"), + SND_SOC_DAPM_OUTPUT("DAC4OUT"), +}; + +static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = { + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0), + SND_SOC_DAPM_INPUT("ADC1IN"), + SND_SOC_DAPM_INPUT("ADC2IN"), +}; + +static int ad193x_check_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + + return !!ad193x->sysclk; +} + +static const struct snd_soc_dapm_route audio_paths[] = { + { "DAC", NULL, "SYSCLK" }, + { "DAC Output", NULL, "DAC" }, + { "DAC Output", NULL, "VMID" }, + { "DAC1OUT", NULL, "DAC Output" }, + { "DAC2OUT", NULL, "DAC Output" }, + { "DAC3OUT", NULL, "DAC Output" }, + { "DAC4OUT", NULL, "DAC Output" }, + { "SYSCLK", NULL, "PLL_PWR", &ad193x_check_pll }, +}; + +static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = { + { "ADC", NULL, "SYSCLK" }, + { "ADC", NULL, "ADC_PWR" }, + { "ADC", NULL, "ADC1IN" }, + { "ADC", NULL, "ADC2IN" }, +}; + +static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x) +{ + switch (ad193x->type) { + case AD1933: + case AD1934: + return false; + default: + break; + } + + return true; +} + +/* + * DAI ops entries + */ + +static int ad193x_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component); + + if (mute) + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2, + AD193X_DAC_MASTER_MUTE, + AD193X_DAC_MASTER_MUTE); + else + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2, + AD193X_DAC_MASTER_MUTE, 0); + + return 0; +} + +static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component); + unsigned int channels; + + switch (slots) { + case 2: + channels = AD193X_2_CHANNELS; + break; + case 4: + channels = AD193X_4_CHANNELS; + break; + case 8: + channels = AD193X_8_CHANNELS; + break; + case 16: + channels = AD193X_16_CHANNELS; + break; + default: + return -EINVAL; + } + + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, + AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT); + if (ad193x_has_adc(ad193x)) + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, + AD193X_ADC_CHAN_MASK, + channels << AD193X_ADC_CHAN_SHFT); + + return 0; +} + +static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(codec_dai->component); + unsigned int adc_serfmt = 0; + unsigned int dac_serfmt = 0; + unsigned int adc_fmt = 0; + unsigned int dac_fmt = 0; + + /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S + * with TDM), ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) and DAC I2S mode + * (SND_SOC_DAIFMT_I2S) + */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + adc_serfmt |= AD193X_ADC_SERFMT_TDM; + dac_serfmt |= AD193X_DAC_SERFMT_STEREO; + break; + case SND_SOC_DAIFMT_DSP_A: + adc_serfmt |= AD193X_ADC_SERFMT_AUX; + dac_serfmt |= AD193X_DAC_SERFMT_TDM; + break; + default: + if (ad193x_has_adc(ad193x)) + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ + break; + case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */ + adc_fmt |= AD193X_ADC_LEFT_HIGH; + dac_fmt |= AD193X_DAC_LEFT_HIGH; + break; + case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */ + adc_fmt |= AD193X_ADC_BCLK_INV; + dac_fmt |= AD193X_DAC_BCLK_INV; + break; + case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */ + adc_fmt |= AD193X_ADC_LEFT_HIGH; + adc_fmt |= AD193X_ADC_BCLK_INV; + dac_fmt |= AD193X_DAC_LEFT_HIGH; + dac_fmt |= AD193X_DAC_BCLK_INV; + break; + default: + return -EINVAL; + } + + /* For DSP_*, LRCLK's polarity must be inverted */ + if (fmt & SND_SOC_DAIFMT_DSP_A) + dac_fmt ^= AD193X_DAC_LEFT_HIGH; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + adc_fmt |= AD193X_ADC_LCR_MASTER; + adc_fmt |= AD193X_ADC_BCLK_MASTER; + dac_fmt |= AD193X_DAC_LCR_MASTER; + dac_fmt |= AD193X_DAC_BCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFP: + adc_fmt |= AD193X_ADC_LCR_MASTER; + dac_fmt |= AD193X_DAC_LCR_MASTER; + break; + case SND_SOC_DAIFMT_CBP_CFC: + adc_fmt |= AD193X_ADC_BCLK_MASTER; + dac_fmt |= AD193X_DAC_BCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + if (ad193x_has_adc(ad193x)) { + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, + AD193X_ADC_SERFMT_MASK, adc_serfmt); + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, + AD193X_ADC_FMT_MASK, adc_fmt); + } + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, + AD193X_DAC_SERFMT_MASK, dac_serfmt); + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, + AD193X_DAC_FMT_MASK, dac_fmt); + + return 0; +} + +static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + + if (clk_id == AD193X_SYSCLK_MCLK) { + /* MCLK must be 512 x fs */ + if (dir == SND_SOC_CLOCK_OUT || freq != 24576000) + return -EINVAL; + + regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL1, + AD193X_PLL_SRC_MASK, + AD193X_PLL_DAC_SRC_MCLK | + AD193X_PLL_CLK_SRC_MCLK); + + snd_soc_dapm_sync(dapm); + return 0; + } + switch (freq) { + case 12288000: + case 18432000: + case 24576000: + case 36864000: + ad193x->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int ad193x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int word_len = 0, master_rate = 0; + struct snd_soc_component *component = dai->component; + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u8 dacc0; + + dev_dbg(dai->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", + __func__, params_rate(params), params_format(params), + params_width(params), params_channels(params)); + + + /* bit size */ + switch (params_width(params)) { + case 16: + word_len = 3; + break; + case 20: + word_len = 1; + break; + case 24: + case 32: + word_len = 0; + break; + } + + switch (ad193x->sysclk) { + case 12288000: + master_rate = AD193X_PLL_INPUT_256; + break; + case 18432000: + master_rate = AD193X_PLL_INPUT_384; + break; + case 24576000: + master_rate = AD193X_PLL_INPUT_512; + break; + case 36864000: + master_rate = AD193X_PLL_INPUT_768; + break; + } + + if (is_playback) { + switch (params_rate(params)) { + case 48000: + dacc0 = AD193X_DAC_SR_48; + break; + case 96000: + dacc0 = AD193X_DAC_SR_96; + break; + case 192000: + dacc0 = AD193X_DAC_SR_192; + break; + default: + dev_err(dai->dev, "invalid sampling rate: %d\n", params_rate(params)); + return -EINVAL; + } + + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, AD193X_DAC_SR_MASK, dacc0); + } + + regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL0, + AD193X_PLL_INPUT_MASK, master_rate); + + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2, + AD193X_DAC_WORD_LEN_MASK, + word_len << AD193X_DAC_WORD_LEN_SHFT); + + if (ad193x_has_adc(ad193x)) + regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1, + AD193X_ADC_WORD_LEN_MASK, word_len); + + return 0; +} + +static int ad193x_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &constr); +} + +static const struct snd_soc_dai_ops ad193x_dai_ops = { + .startup = ad193x_startup, + .hw_params = ad193x_hw_params, + .mute_stream = ad193x_mute, + .set_tdm_slot = ad193x_set_tdm_slot, + .set_sysclk = ad193x_set_dai_sysclk, + .set_fmt = ad193x_set_dai_fmt, + .no_capture_mute = 1, +}; + +/* codec DAI instance */ +static struct snd_soc_dai_driver ad193x_dai = { + .name = "ad193x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ad193x_dai_ops, +}; + +/* codec DAI instance for DAC only */ +static struct snd_soc_dai_driver ad193x_no_adc_dai = { + .name = "ad193x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ad193x_dai_ops, +}; + +/* codec register values to set after reset */ +static void ad193x_reg_default_init(struct ad193x_priv *ad193x) +{ + static const struct reg_sequence reg_init[] = { + { 0, 0x99 }, /* PLL_CLK_CTRL0: pll input: mclki/xi 12.288Mhz */ + { 1, 0x04 }, /* PLL_CLK_CTRL1: no on-chip Vref */ + { 2, 0x40 }, /* DAC_CTRL0: TDM mode */ + { 3, 0x00 }, /* DAC_CTRL1: reset */ + { 4, 0x1A }, /* DAC_CTRL2: 48kHz de-emphasis, unmute dac */ + { 5, 0x00 }, /* DAC_CHNL_MUTE: unmute DAC channels */ + { 6, 0x00 }, /* DAC_L1_VOL: no attenuation */ + { 7, 0x00 }, /* DAC_R1_VOL: no attenuation */ + { 8, 0x00 }, /* DAC_L2_VOL: no attenuation */ + { 9, 0x00 }, /* DAC_R2_VOL: no attenuation */ + { 10, 0x00 }, /* DAC_L3_VOL: no attenuation */ + { 11, 0x00 }, /* DAC_R3_VOL: no attenuation */ + { 12, 0x00 }, /* DAC_L4_VOL: no attenuation */ + { 13, 0x00 }, /* DAC_R4_VOL: no attenuation */ + }; + static const struct reg_sequence reg_adc_init[] = { + { 14, 0x03 }, /* ADC_CTRL0: high-pass filter enable */ + { 15, 0x43 }, /* ADC_CTRL1: sata delay=1, adc aux mode */ + { 16, 0x00 }, /* ADC_CTRL2: reset */ + }; + + regmap_multi_reg_write(ad193x->regmap, reg_init, ARRAY_SIZE(reg_init)); + + if (ad193x_has_adc(ad193x)) { + regmap_multi_reg_write(ad193x->regmap, reg_adc_init, + ARRAY_SIZE(reg_adc_init)); + } +} + +static int ad193x_component_probe(struct snd_soc_component *component) +{ + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int num, ret; + + /* default setting for ad193x */ + ad193x_reg_default_init(ad193x); + + /* adc only */ + if (ad193x_has_adc(ad193x)) { + /* add adc controls */ + num = ARRAY_SIZE(ad193x_adc_snd_controls); + ret = snd_soc_add_component_controls(component, + ad193x_adc_snd_controls, + num); + if (ret) + return ret; + + /* add adc widgets */ + num = ARRAY_SIZE(ad193x_adc_widgets); + ret = snd_soc_dapm_new_controls(dapm, + ad193x_adc_widgets, + num); + if (ret) + return ret; + + /* add adc routes */ + num = ARRAY_SIZE(ad193x_adc_audio_paths); + ret = snd_soc_dapm_add_routes(dapm, + ad193x_adc_audio_paths, + num); + if (ret) + return ret; + } + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_ad193x = { + .probe = ad193x_component_probe, + .controls = ad193x_snd_controls, + .num_controls = ARRAY_SIZE(ad193x_snd_controls), + .dapm_widgets = ad193x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad193x_dapm_widgets), + .dapm_routes = audio_paths, + .num_dapm_routes = ARRAY_SIZE(audio_paths), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +const struct regmap_config ad193x_regmap_config = { + .max_register = AD193X_NUM_REGS - 1, +}; +EXPORT_SYMBOL_GPL(ad193x_regmap_config); + +int ad193x_probe(struct device *dev, struct regmap *regmap, + enum ad193x_type type) +{ + struct ad193x_priv *ad193x; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ad193x = devm_kzalloc(dev, sizeof(*ad193x), GFP_KERNEL); + if (ad193x == NULL) + return -ENOMEM; + + ad193x->regmap = regmap; + ad193x->type = type; + + dev_set_drvdata(dev, ad193x); + + if (ad193x_has_adc(ad193x)) + return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, + &ad193x_dai, 1); + return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, + &ad193x_no_adc_dai, 1); +} +EXPORT_SYMBOL_GPL(ad193x_probe); + +MODULE_DESCRIPTION("ASoC ad193x driver"); +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h new file mode 100644 index 0000000000..61f4648861 --- /dev/null +++ b/sound/soc/codecs/ad193x.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AD193X Audio Codec driver + * + * Copyright 2010 Analog Devices Inc. + */ + +#ifndef __AD193X_H__ +#define __AD193X_H__ + +#include + +struct device; + +enum ad193x_type { + AD193X, + AD1933, + AD1934, +}; + +extern const struct regmap_config ad193x_regmap_config; +int ad193x_probe(struct device *dev, struct regmap *regmap, + enum ad193x_type type); + +#define AD193X_PLL_CLK_CTRL0 0x00 +#define AD193X_PLL_POWERDOWN 0x01 +#define AD193X_PLL_INPUT_MASK 0x6 +#define AD193X_PLL_INPUT_256 (0 << 1) +#define AD193X_PLL_INPUT_384 (1 << 1) +#define AD193X_PLL_INPUT_512 (2 << 1) +#define AD193X_PLL_INPUT_768 (3 << 1) +#define AD193X_PLL_CLK_CTRL1 0x01 +#define AD193X_PLL_SRC_MASK 0x03 +#define AD193X_PLL_DAC_SRC_PLL 0 +#define AD193X_PLL_DAC_SRC_MCLK 1 +#define AD193X_PLL_CLK_SRC_PLL (0 << 1) +#define AD193X_PLL_CLK_SRC_MCLK (1 << 1) +#define AD193X_DAC_CTRL0 0x02 +#define AD193X_DAC_POWERDOWN 0x01 +#define AD193X_DAC_SR_MASK 0x06 +#define AD193X_DAC_SR_48 (0 << 1) +#define AD193X_DAC_SR_96 (1 << 1) +#define AD193X_DAC_SR_192 (2 << 1) +#define AD193X_DAC_SERFMT_MASK 0xC0 +#define AD193X_DAC_SERFMT_STEREO (0 << 6) +#define AD193X_DAC_SERFMT_TDM (1 << 6) +#define AD193X_DAC_CTRL1 0x03 +#define AD193X_DAC_CHAN_SHFT 1 +#define AD193X_DAC_CHAN_MASK (3 << AD193X_DAC_CHAN_SHFT) +#define AD193X_DAC_LCR_MASTER (1 << 4) +#define AD193X_DAC_BCLK_MASTER (1 << 5) +#define AD193X_DAC_LEFT_HIGH (1 << 3) +#define AD193X_DAC_BCLK_INV (1 << 7) +#define AD193X_DAC_FMT_MASK (AD193X_DAC_LCR_MASTER | \ + AD193X_DAC_BCLK_MASTER | AD193X_DAC_LEFT_HIGH | AD193X_DAC_BCLK_INV) +#define AD193X_DAC_CTRL2 0x04 +#define AD193X_DAC_WORD_LEN_SHFT 3 +#define AD193X_DAC_WORD_LEN_MASK 0x18 +#define AD193X_DAC_MASTER_MUTE 1 +#define AD193X_DAC_CHNL_MUTE 0x05 +#define AD193X_DACL1_MUTE 0 +#define AD193X_DACR1_MUTE 1 +#define AD193X_DACL2_MUTE 2 +#define AD193X_DACR2_MUTE 3 +#define AD193X_DACL3_MUTE 4 +#define AD193X_DACR3_MUTE 5 +#define AD193X_DACL4_MUTE 6 +#define AD193X_DACR4_MUTE 7 +#define AD193X_DAC_L1_VOL 0x06 +#define AD193X_DAC_R1_VOL 0x07 +#define AD193X_DAC_L2_VOL 0x08 +#define AD193X_DAC_R2_VOL 0x09 +#define AD193X_DAC_L3_VOL 0x0a +#define AD193X_DAC_R3_VOL 0x0b +#define AD193X_DAC_L4_VOL 0x0c +#define AD193X_DAC_R4_VOL 0x0d +#define AD193X_ADC_CTRL0 0x0e +#define AD193X_ADC_POWERDOWN 0x01 +#define AD193X_ADC_HIGHPASS_FILTER 1 +#define AD193X_ADCL1_MUTE 2 +#define AD193X_ADCR1_MUTE 3 +#define AD193X_ADCL2_MUTE 4 +#define AD193X_ADCR2_MUTE 5 +#define AD193X_ADC_CTRL1 0x0f +#define AD193X_ADC_SERFMT_MASK 0x60 +#define AD193X_ADC_SERFMT_STEREO (0 << 5) +#define AD193X_ADC_SERFMT_TDM (1 << 5) +#define AD193X_ADC_SERFMT_AUX (2 << 5) +#define AD193X_ADC_WORD_LEN_MASK 0x3 +#define AD193X_ADC_CTRL2 0x10 +#define AD193X_ADC_CHAN_SHFT 4 +#define AD193X_ADC_CHAN_MASK (3 << AD193X_ADC_CHAN_SHFT) +#define AD193X_ADC_LCR_MASTER (1 << 3) +#define AD193X_ADC_BCLK_MASTER (1 << 6) +#define AD193X_ADC_LEFT_HIGH (1 << 2) +#define AD193X_ADC_BCLK_INV (1 << 1) +#define AD193X_ADC_FMT_MASK (AD193X_ADC_LCR_MASTER | \ + AD193X_ADC_BCLK_MASTER | AD193X_ADC_LEFT_HIGH | AD193X_ADC_BCLK_INV) + +#define AD193X_2_CHANNELS 0 +#define AD193X_4_CHANNELS 1 +#define AD193X_8_CHANNELS 2 +#define AD193X_16_CHANNELS 3 + +#define AD193X_NUM_REGS 17 + +#define AD193X_SYSCLK_PLL 0 +#define AD193X_SYSCLK_MCLK 1 + +#endif diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c new file mode 100644 index 0000000000..3c1ae13c1a --- /dev/null +++ b/sound/soc/codecs/ad1980.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ad1980.c -- ALSA Soc AD1980 codec support + * + * Copyright: Analog Devices Inc. + * Author: Roy Huang + * Cliff Cai + */ + +/* + * WARNING: + * + * Because Analog Devices Inc. discontinued the ad1980 sound chip since + * Sep. 2009, this ad1980 driver is not maintained, tested and supported + * by ADI now. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct reg_default ad1980_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x0c, 0x8008 }, + { 0x0e, 0x8008 }, + { 0x10, 0x8808 }, + { 0x12, 0x8808 }, + { 0x16, 0x8808 }, + { 0x18, 0x8808 }, + { 0x1a, 0x0000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x28, 0x03c7 }, + { 0x2c, 0xbb80 }, + { 0x2e, 0xbb80 }, + { 0x30, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x36, 0x8080 }, + { 0x38, 0x8080 }, + { 0x3a, 0x2000 }, + { 0x60, 0x0000 }, + { 0x62, 0x0000 }, + { 0x72, 0x0000 }, + { 0x74, 0x1001 }, + { 0x76, 0x0000 }, +}; + +static bool ad1980_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_RESET ... AC97_MASTER_MONO: + case AC97_PHONE ... AC97_CD: + case AC97_AUX ... AC97_GENERAL_PURPOSE: + case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE: + case AC97_SPDIF: + case AC97_CODEC_CLASS_REV: + case AC97_PCI_SVID: + case AC97_AD_CODEC_CFG: + case AC97_AD_JACK_SPDIF: + case AC97_AD_SERIAL_CFG: + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool ad1980_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return false; + default: + return ad1980_readable_reg(dev, reg); + } +} + +static const struct regmap_config ad1980_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = ad1980_readable_reg, + .writeable_reg = ad1980_writeable_reg, + + .reg_defaults = ad1980_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ad1980_reg_defaults), +}; + +static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line", + "Stereo Mix", "Mono Mix", "Phone"}; + +static SOC_ENUM_DOUBLE_DECL(ad1980_cap_src, + AC97_REC_SEL, 8, 0, ad1980_rec_sel); + +static const struct snd_kcontrol_new ad1980_snd_ac97_controls[] = { +SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1), +SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1), + +SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), +SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1), + +SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1), +SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1), + +SOC_DOUBLE("PCM Capture Volume", AC97_REC_GAIN, 8, 0, 31, 0), +SOC_SINGLE("PCM Capture Switch", AC97_REC_GAIN, 15, 1, 1), + +SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1), +SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), + +SOC_SINGLE("Phone Capture Volume", AC97_PHONE, 0, 31, 1), +SOC_SINGLE("Phone Capture Switch", AC97_PHONE, 15, 1, 1), + +SOC_SINGLE("Mic Volume", AC97_MIC, 0, 31, 1), +SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1), + +SOC_SINGLE("Stereo Mic Switch", AC97_AD_MISC, 6, 1, 0), +SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0), + +SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1), +SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1), + +SOC_DOUBLE("Center/LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 0, 31, 1), +SOC_DOUBLE("Center/LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 7, 1, 1), + +SOC_ENUM("Capture Source", ad1980_cap_src), + +SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0), +}; + +static const struct snd_soc_dapm_widget ad1980_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +SND_SOC_DAPM_INPUT("CD_L"), +SND_SOC_DAPM_INPUT("CD_R"), +SND_SOC_DAPM_INPUT("AUX_L"), +SND_SOC_DAPM_INPUT("AUX_R"), +SND_SOC_DAPM_INPUT("LINE_IN_L"), +SND_SOC_DAPM_INPUT("LINE_IN_R"), + +SND_SOC_DAPM_OUTPUT("LFE_OUT"), +SND_SOC_DAPM_OUTPUT("CENTER_OUT"), +SND_SOC_DAPM_OUTPUT("LINE_OUT_L"), +SND_SOC_DAPM_OUTPUT("LINE_OUT_R"), +SND_SOC_DAPM_OUTPUT("MONO_OUT"), +SND_SOC_DAPM_OUTPUT("HP_OUT_L"), +SND_SOC_DAPM_OUTPUT("HP_OUT_R"), +}; + +static const struct snd_soc_dapm_route ad1980_dapm_routes[] = { + { "Capture", NULL, "MIC1" }, + { "Capture", NULL, "MIC2" }, + { "Capture", NULL, "CD_L" }, + { "Capture", NULL, "CD_R" }, + { "Capture", NULL, "AUX_L" }, + { "Capture", NULL, "AUX_R" }, + { "Capture", NULL, "LINE_IN_L" }, + { "Capture", NULL, "LINE_IN_R" }, + + { "LFE_OUT", NULL, "Playback" }, + { "CENTER_OUT", NULL, "Playback" }, + { "LINE_OUT_L", NULL, "Playback" }, + { "LINE_OUT_R", NULL, "Playback" }, + { "MONO_OUT", NULL, "Playback" }, + { "HP_OUT_L", NULL, "Playback" }, + { "HP_OUT_R", NULL, "Playback" }, +}; + +static struct snd_soc_dai_driver ad1980_dai = { + .name = "ad1980-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 6, + .rates = SNDRV_PCM_RATE_48000, + .formats = SND_SOC_STD_AC97_FMTS, }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SND_SOC_STD_AC97_FMTS, }, +}; + +#define AD1980_VENDOR_ID 0x41445300 +#define AD1980_VENDOR_MASK 0xffffff00 + +static int ad1980_reset(struct snd_soc_component *component, int try_warm) +{ + struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component); + unsigned int retry_cnt = 0; + int ret; + + do { + ret = snd_ac97_reset(ac97, true, AD1980_VENDOR_ID, + AD1980_VENDOR_MASK); + if (ret >= 0) + return 0; + + /* + * Set bit 16slot in register 74h, then every slot will has only + * 16 bits. This command is sent out in 20bit mode, in which + * case the first nibble of data is eaten by the addr. (Tag is + * always 16 bit) + */ + snd_soc_component_write(component, AC97_AD_SERIAL_CFG, 0x9900); + + } while (retry_cnt++ < 10); + + dev_err(component->dev, "Failed to reset: AC97 link error\n"); + + return -EIO; +} + +static int ad1980_soc_probe(struct snd_soc_component *component) +{ + struct snd_ac97 *ac97; + struct regmap *regmap; + int ret; + u16 vendor_id2; + u16 ext_status; + + ac97 = snd_soc_new_ac97_component(component, 0, 0); + if (IS_ERR(ac97)) { + ret = PTR_ERR(ac97); + dev_err(component->dev, "Failed to register AC97 component: %d\n", ret); + return ret; + } + + regmap = regmap_init_ac97(ac97, &ad1980_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_free_ac97; + } + + snd_soc_component_init_regmap(component, regmap); + snd_soc_component_set_drvdata(component, ac97); + + ret = ad1980_reset(component, 0); + if (ret < 0) + goto reset_err; + + vendor_id2 = snd_soc_component_read(component, AC97_VENDOR_ID2); + if (vendor_id2 == 0x5374) { + dev_warn(component->dev, + "Found AD1981 - only 2/2 IN/OUT Channels supported\n"); + } + + /* unmute captures and playbacks volume */ + snd_soc_component_write(component, AC97_MASTER, 0x0000); + snd_soc_component_write(component, AC97_PCM, 0x0000); + snd_soc_component_write(component, AC97_REC_GAIN, 0x0000); + snd_soc_component_write(component, AC97_CENTER_LFE_MASTER, 0x0000); + snd_soc_component_write(component, AC97_SURROUND_MASTER, 0x0000); + + /*power on LFE/CENTER/Surround DACs*/ + ext_status = snd_soc_component_read(component, AC97_EXTENDED_STATUS); + snd_soc_component_write(component, AC97_EXTENDED_STATUS, ext_status&~0x3800); + + return 0; + +reset_err: + snd_soc_component_exit_regmap(component); +err_free_ac97: + snd_soc_free_ac97_component(ac97); + return ret; +} + +static void ad1980_soc_remove(struct snd_soc_component *component) +{ + struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component); + + snd_soc_component_exit_regmap(component); + snd_soc_free_ac97_component(ac97); +} + +static const struct snd_soc_component_driver soc_component_dev_ad1980 = { + .probe = ad1980_soc_probe, + .remove = ad1980_soc_remove, + .controls = ad1980_snd_ac97_controls, + .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls), + .dapm_widgets = ad1980_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets), + .dapm_routes = ad1980_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int ad1980_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_ad1980, &ad1980_dai, 1); +} + +static struct platform_driver ad1980_codec_driver = { + .driver = { + .name = "ad1980", + }, + + .probe = ad1980_probe, +}; + +module_platform_driver(ad1980_codec_driver); + +MODULE_DESCRIPTION("ASoC ad1980 driver (Obsolete)"); +MODULE_AUTHOR("Roy Huang, Cliff Cai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c new file mode 100644 index 0000000000..f6090ac57e --- /dev/null +++ b/sound/soc/codecs/ad73311.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ad73311.c -- ALSA Soc AD73311 codec support + * + * Copyright: Analog Devices Inc. + * Author: Cliff Cai + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ad73311.h" + +static const struct snd_soc_dapm_widget ad73311_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("VINP"), +SND_SOC_DAPM_INPUT("VINN"), +SND_SOC_DAPM_OUTPUT("VOUTN"), +SND_SOC_DAPM_OUTPUT("VOUTP"), +}; + +static const struct snd_soc_dapm_route ad73311_dapm_routes[] = { + { "Capture", NULL, "VINP" }, + { "Capture", NULL, "VINN" }, + + { "VOUTN", NULL, "Playback" }, + { "VOUTP", NULL, "Playback" }, +}; + +static struct snd_soc_dai_driver ad73311_dai = { + .name = "ad73311-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}; + +static const struct snd_soc_component_driver soc_component_dev_ad73311 = { + .dapm_widgets = ad73311_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets), + .dapm_routes = ad73311_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int ad73311_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_ad73311, &ad73311_dai, 1); +} + +static struct platform_driver ad73311_codec_driver = { + .driver = { + .name = "ad73311", + }, + + .probe = ad73311_probe, +}; + +module_platform_driver(ad73311_codec_driver); + +MODULE_DESCRIPTION("ASoC ad73311 driver"); +MODULE_AUTHOR("Cliff Cai "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h new file mode 100644 index 0000000000..774c62d561 --- /dev/null +++ b/sound/soc/codecs/ad73311.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * File: sound/soc/codec/ad73311.h + * Based on: + * Author: Cliff Cai + * + * Created: Thur Sep 25, 2008 + * Description: definitions for AD73311 registers + * + * Modified: + * Copyright 2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + */ + +#ifndef __AD73311_H__ +#define __AD73311_H__ + +#define AD_CONTROL 0x8000 +#define AD_DATA 0x0000 +#define AD_READ 0x4000 +#define AD_WRITE 0x0000 + +/* Control register A */ +#define CTRL_REG_A (0 << 8) + +#define REGA_MODE_PRO 0x00 +#define REGA_MODE_DATA 0x01 +#define REGA_MODE_MIXED 0x03 +#define REGA_DLB 0x04 +#define REGA_SLB 0x08 +#define REGA_DEVC(x) ((x & 0x7) << 4) +#define REGA_RESET 0x80 + +/* Control register B */ +#define CTRL_REG_B (1 << 8) + +#define REGB_DIRATE(x) (x & 0x3) +#define REGB_SCDIV(x) ((x & 0x3) << 2) +#define REGB_MCDIV(x) ((x & 0x7) << 4) +#define REGB_CEE (1 << 7) + +/* Control register C */ +#define CTRL_REG_C (2 << 8) + +#define REGC_PUDEV (1 << 0) +#define REGC_PUADC (1 << 3) +#define REGC_PUDAC (1 << 4) +#define REGC_PUREF (1 << 5) +#define REGC_REFUSE (1 << 6) + +/* Control register D */ +#define CTRL_REG_D (3 << 8) + +#define REGD_IGS(x) (x & 0x7) +#define REGD_RMOD (1 << 3) +#define REGD_OGS(x) ((x & 0x7) << 4) +#define REGD_MUTE (1 << 7) + +/* Control register E */ +#define CTRL_REG_E (4 << 8) + +#define REGE_DA(x) (x & 0x1f) +#define REGE_IBYP (1 << 5) + +/* Control register F */ +#define CTRL_REG_F (5 << 8) + +#define REGF_SEEN (1 << 5) +#define REGF_INV (1 << 6) +#define REGF_ALB (1 << 7) + +#endif diff --git a/sound/soc/codecs/adau-utils.c b/sound/soc/codecs/adau-utils.c new file mode 100644 index 0000000000..836940f2ab --- /dev/null +++ b/sound/soc/codecs/adau-utils.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Shared helper functions for devices from the ADAU family + * + * Copyright 2011-2016 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include + +#include "adau-utils.h" + +int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out, + uint8_t regs[5]) +{ + unsigned int r, n, m, i, j; + unsigned int div; + + if (!freq_out) { + r = 0; + n = 0; + m = 0; + div = 0; + } else { + if (freq_out % freq_in != 0) { + div = DIV_ROUND_UP(freq_in, 13500000); + freq_in /= div; + r = freq_out / freq_in; + i = freq_out % freq_in; + j = gcd(i, freq_in); + n = i / j; + m = freq_in / j; + div--; + } else { + r = freq_out / freq_in; + n = 0; + m = 0; + div = 0; + } + if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2) + return -EINVAL; + } + + regs[0] = m >> 8; + regs[1] = m & 0xff; + regs[2] = n >> 8; + regs[3] = n & 0xff; + regs[4] = (r << 3) | (div << 1); + if (m != 0) + regs[4] |= 1; /* Fractional mode */ + + return 0; +} +EXPORT_SYMBOL_GPL(adau_calc_pll_cfg); + +MODULE_DESCRIPTION("ASoC ADAU audio CODECs shared helper functions"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/adau-utils.h b/sound/soc/codecs/adau-utils.h new file mode 100644 index 0000000000..bf5947b353 --- /dev/null +++ b/sound/soc/codecs/adau-utils.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef SOUND_SOC_CODECS_ADAU_PLL_H +#define SOUND_SOC_CODECS_ADAU_PLL_H + +int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out, + uint8_t regs[5]); + +#endif diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c new file mode 100644 index 0000000000..132b9e2cca --- /dev/null +++ b/sound/soc/codecs/adau1372-i2c.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ADAU1372 codec + * + * Copyright 2016 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include + +#include "adau1372.h" + +static int adau1372_i2c_probe(struct i2c_client *client) +{ + return adau1372_probe(&client->dev, + devm_regmap_init_i2c(client, &adau1372_regmap_config), NULL); +} + +static const struct i2c_device_id adau1372_i2c_ids[] = { + { "adau1372", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1372_i2c_ids); + +static struct i2c_driver adau1372_i2c_driver = { + .driver = { + .name = "adau1372", + }, + .probe = adau1372_i2c_probe, + .id_table = adau1372_i2c_ids, +}; +module_i2c_driver(adau1372_i2c_driver); + +MODULE_DESCRIPTION("ASoC ADAU1372 CODEC I2C driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/adau1372-spi.c b/sound/soc/codecs/adau1372-spi.c new file mode 100644 index 0000000000..51298e00fb --- /dev/null +++ b/sound/soc/codecs/adau1372-spi.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ADAU1372 codec + * + * Copyright 2016 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include + +#include "adau1372.h" + +static void adau1372_spi_switch_mode(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + /* + * To get the device into SPI mode CLATCH has to be pulled low three + * times. Do this by issuing three dummy reads. + */ + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); +} + +static int adau1372_spi_probe(struct spi_device *spi) +{ + struct regmap_config config; + + config = adau1372_regmap_config; + config.read_flag_mask = 0x1; + + return adau1372_probe(&spi->dev, + devm_regmap_init_spi(spi, &config), adau1372_spi_switch_mode); +} + +static const struct spi_device_id adau1372_spi_id[] = { + { "adau1372", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adau1372_spi_id); + +static struct spi_driver adau1372_spi_driver = { + .driver = { + .name = "adau1372", + }, + .probe = adau1372_spi_probe, + .id_table = adau1372_spi_id, +}; +module_spi_driver(adau1372_spi_driver); + +MODULE_DESCRIPTION("ASoC ADAU1372 CODEC SPI driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c new file mode 100644 index 0000000000..98380a7ce6 --- /dev/null +++ b/sound/soc/codecs/adau1372.c @@ -0,0 +1,1065 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices ADAU1372 Audio Codec driver + * + * Copyright 2016 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "adau1372.h" +#include "adau-utils.h" + +struct adau1372 { + struct regmap *regmap; + void (*switch_mode)(struct device *dev); + bool use_pll; + bool enabled; + bool clock_provider; + + struct snd_pcm_hw_constraint_list rate_constraints; + unsigned int slot_width; + + struct clk *mclk; + struct gpio_desc *pd_gpio; + struct device *dev; +}; + +#define ADAU1372_REG_CLK_CTRL 0x00 +#define ADAU1372_REG_PLL(x) (0x01 + (x)) +#define ADAU1372_REG_DAC_SOURCE 0x11 +#define ADAU1372_REG_SOUT_SOURCE_0_1 0x13 +#define ADAU1372_REG_SOUT_SOURCE_2_3 0x14 +#define ADAU1372_REG_SOUT_SOURCE_4_5 0x15 +#define ADAU1372_REG_SOUT_SOURCE_6_7 0x16 +#define ADAU1372_REG_ADC_SDATA_CH 0x17 +#define ADAU1372_REG_ASRCO_SOURCE_0_1 0x18 +#define ADAU1372_REG_ASRCO_SOURCE_2_3 0x19 +#define ADAU1372_REG_ASRC_MODE 0x1a +#define ADAU1372_REG_ADC_CTRL0 0x1b +#define ADAU1372_REG_ADC_CTRL1 0x1c +#define ADAU1372_REG_ADC_CTRL2 0x1d +#define ADAU1372_REG_ADC_CTRL3 0x1e +#define ADAU1372_REG_ADC_VOL(x) (0x1f + (x)) +#define ADAU1372_REG_PGA_CTRL(x) (0x23 + (x)) +#define ADAU1372_REG_PGA_BOOST 0x28 +#define ADAU1372_REG_MICBIAS 0x2d +#define ADAU1372_REG_DAC_CTRL 0x2e +#define ADAU1372_REG_DAC_VOL(x) (0x2f + (x)) +#define ADAU1372_REG_OP_STAGE_MUTE 0x31 +#define ADAU1372_REG_SAI0 0x32 +#define ADAU1372_REG_SAI1 0x33 +#define ADAU1372_REG_SOUT_CTRL 0x34 +#define ADAU1372_REG_MODE_MP(x) (0x38 + (x)) +#define ADAU1372_REG_OP_STAGE_CTRL 0x43 +#define ADAU1372_REG_DECIM_PWR 0x44 +#define ADAU1372_REG_INTERP_PWR 0x45 +#define ADAU1372_REG_BIAS_CTRL0 0x46 +#define ADAU1372_REG_BIAS_CTRL1 0x47 + +#define ADAU1372_CLK_CTRL_PLL_EN BIT(7) +#define ADAU1372_CLK_CTRL_XTAL_DIS BIT(4) +#define ADAU1372_CLK_CTRL_CLKSRC BIT(3) +#define ADAU1372_CLK_CTRL_CC_MDIV BIT(1) +#define ADAU1372_CLK_CTRL_MCLK_EN BIT(0) + +#define ADAU1372_SAI0_DELAY1 (0x0 << 6) +#define ADAU1372_SAI0_DELAY0 (0x1 << 6) +#define ADAU1372_SAI0_DELAY_MASK (0x3 << 6) +#define ADAU1372_SAI0_SAI_I2S (0x0 << 4) +#define ADAU1372_SAI0_SAI_TDM2 (0x1 << 4) +#define ADAU1372_SAI0_SAI_TDM4 (0x2 << 4) +#define ADAU1372_SAI0_SAI_TDM8 (0x3 << 4) +#define ADAU1372_SAI0_SAI_MASK (0x3 << 4) +#define ADAU1372_SAI0_FS_48 0x0 +#define ADAU1372_SAI0_FS_8 0x1 +#define ADAU1372_SAI0_FS_12 0x2 +#define ADAU1372_SAI0_FS_16 0x3 +#define ADAU1372_SAI0_FS_24 0x4 +#define ADAU1372_SAI0_FS_32 0x5 +#define ADAU1372_SAI0_FS_96 0x6 +#define ADAU1372_SAI0_FS_192 0x7 +#define ADAU1372_SAI0_FS_MASK 0xf + +#define ADAU1372_SAI1_TDM_TS BIT(7) +#define ADAU1372_SAI1_BCLK_TDMC BIT(6) +#define ADAU1372_SAI1_LR_MODE BIT(5) +#define ADAU1372_SAI1_LR_POL BIT(4) +#define ADAU1372_SAI1_BCLKRATE BIT(2) +#define ADAU1372_SAI1_BCLKEDGE BIT(1) +#define ADAU1372_SAI1_MS BIT(0) + +static const unsigned int adau1372_rates[] = { + [ADAU1372_SAI0_FS_8] = 8000, + [ADAU1372_SAI0_FS_12] = 12000, + [ADAU1372_SAI0_FS_16] = 16000, + [ADAU1372_SAI0_FS_24] = 24000, + [ADAU1372_SAI0_FS_32] = 32000, + [ADAU1372_SAI0_FS_48] = 48000, + [ADAU1372_SAI0_FS_96] = 96000, + [ADAU1372_SAI0_FS_192] = 192000, +}; + +/* 8k, 12k, 24k, 48k */ +#define ADAU1372_RATE_MASK_TDM8 0x17 +/* + 16k, 96k */ +#define ADAU1372_RATE_MASK_TDM4_MASTER (ADAU1372_RATE_MASK_TDM8 | 0x48 | 0x20) +/* +32k */ +#define ADAU1372_RATE_MASK_TDM4 (ADAU1372_RATE_MASK_TDM4_MASTER | 0x20) +/* + 192k */ +#define ADAU1372_RATE_MASK_TDM2 (ADAU1372_RATE_MASK_TDM4 | 0x80) + +static const DECLARE_TLV_DB_MINMAX(adau1372_digital_tlv, -9563, 0); +static const DECLARE_TLV_DB_SCALE(adau1372_pga_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(adau1372_pga_boost_tlv, 0, 1000, 0); + +static const char * const adau1372_bias_text[] = { + "Normal operation", "Extreme power saving", "Enhanced performance", + "Power saving", +}; + +static const unsigned int adau1372_bias_adc_values[] = { + 0, 2, 3, +}; + +static const char * const adau1372_bias_adc_text[] = { + "Normal operation", "Enhanced performance", "Power saving", +}; + +static const char * const adau1372_bias_dac_text[] = { + "Normal operation", "Power saving", "Superior performance", + "Enhanced performance", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_bias_hp_enum, + ADAU1372_REG_BIAS_CTRL0, 6, adau1372_bias_text); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe0_1_enum, + ADAU1372_REG_BIAS_CTRL0, 4, adau1372_bias_text); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc2_3_enum, + ADAU1372_REG_BIAS_CTRL0, 2, 0x3, adau1372_bias_adc_text, + adau1372_bias_adc_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_bias_adc0_1_enum, + ADAU1372_REG_BIAS_CTRL0, 0, 0x3, adau1372_bias_adc_text, + adau1372_bias_adc_values); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_afe2_3_enum, + ADAU1372_REG_BIAS_CTRL1, 4, adau1372_bias_text); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_mic_enum, + ADAU1372_REG_BIAS_CTRL1, 2, adau1372_bias_text); +static SOC_ENUM_SINGLE_DECL(adau1372_bias_dac_enum, + ADAU1372_REG_BIAS_CTRL1, 0, adau1372_bias_dac_text); + +static const char * const adau1372_hpf_text[] = { + "Off", + "1 Hz", + "4 Hz", + "8 Hz", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_hpf0_1_enum, ADAU1372_REG_ADC_CTRL2, 5, + adau1372_hpf_text); +static SOC_ENUM_SINGLE_DECL(adau1372_hpf2_3_enum, ADAU1372_REG_ADC_CTRL3, 5, + adau1372_hpf_text); +static const struct snd_kcontrol_new adau1372_controls[] = { + SOC_SINGLE_TLV("ADC 0 Capture Volume", ADAU1372_REG_ADC_VOL(0), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("ADC 1 Capture Volume", ADAU1372_REG_ADC_VOL(1), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("ADC 2 Capture Volume", ADAU1372_REG_ADC_VOL(2), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("ADC 3 Capture Volume", ADAU1372_REG_ADC_VOL(3), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE("ADC 0 Capture Switch", ADAU1372_REG_ADC_CTRL0, 3, 1, 1), + SOC_SINGLE("ADC 1 Capture Switch", ADAU1372_REG_ADC_CTRL0, 4, 1, 1), + SOC_SINGLE("ADC 2 Capture Switch", ADAU1372_REG_ADC_CTRL1, 3, 1, 1), + SOC_SINGLE("ADC 3 Capture Switch", ADAU1372_REG_ADC_CTRL1, 4, 1, 1), + + SOC_ENUM("ADC 0+1 High-Pass-Filter", adau1372_hpf0_1_enum), + SOC_ENUM("ADC 2+3 High-Pass-Filter", adau1372_hpf2_3_enum), + + SOC_SINGLE_TLV("PGA 0 Capture Volume", ADAU1372_REG_PGA_CTRL(0), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 1 Capture Volume", ADAU1372_REG_PGA_CTRL(1), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 2 Capture Volume", ADAU1372_REG_PGA_CTRL(2), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 3 Capture Volume", ADAU1372_REG_PGA_CTRL(3), + 0, 0x3f, 0, adau1372_pga_tlv), + SOC_SINGLE_TLV("PGA 0 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 0, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE_TLV("PGA 1 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 1, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE_TLV("PGA 2 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 2, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE_TLV("PGA 3 Boost Capture Volume", ADAU1372_REG_PGA_BOOST, + 3, 1, 0, adau1372_pga_boost_tlv), + SOC_SINGLE("PGA 0 Capture Switch", ADAU1372_REG_PGA_CTRL(0), 7, 1, 0), + SOC_SINGLE("PGA 1 Capture Switch", ADAU1372_REG_PGA_CTRL(1), 7, 1, 0), + SOC_SINGLE("PGA 2 Capture Switch", ADAU1372_REG_PGA_CTRL(2), 7, 1, 0), + SOC_SINGLE("PGA 3 Capture Switch", ADAU1372_REG_PGA_CTRL(3), 7, 1, 0), + + SOC_SINGLE_TLV("DAC 0 Playback Volume", ADAU1372_REG_DAC_VOL(0), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE_TLV("DAC 1 Playback Volume", ADAU1372_REG_DAC_VOL(1), + 0, 0xff, 1, adau1372_digital_tlv), + SOC_SINGLE("DAC 0 Playback Switch", ADAU1372_REG_DAC_CTRL, 3, 1, 1), + SOC_SINGLE("DAC 1 Playback Switch", ADAU1372_REG_DAC_CTRL, 4, 1, 1), + + SOC_ENUM("Headphone Bias", adau1372_bias_hp_enum), + SOC_ENUM("Microphone Bias", adau1372_bias_mic_enum), + SOC_ENUM("AFE 0+1 Bias", adau1372_bias_afe0_1_enum), + SOC_ENUM("AFE 2+3 Bias", adau1372_bias_afe2_3_enum), + SOC_ENUM("ADC 0+1 Bias", adau1372_bias_adc0_1_enum), + SOC_ENUM("ADC 2+3 Bias", adau1372_bias_adc2_3_enum), + SOC_ENUM("DAC 0+1 Bias", adau1372_bias_dac_enum), +}; + +static const char * const adau1372_decimator_mux_text[] = { + "ADC", + "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_decimator0_1_mux_enum, ADAU1372_REG_ADC_CTRL2, + 2, adau1372_decimator_mux_text); + +static const struct snd_kcontrol_new adau1372_decimator0_1_mux_control = + SOC_DAPM_ENUM("Decimator 0+1 Capture Mux", adau1372_decimator0_1_mux_enum); + +static SOC_ENUM_SINGLE_DECL(adau1372_decimator2_3_mux_enum, ADAU1372_REG_ADC_CTRL3, + 2, adau1372_decimator_mux_text); + +static const struct snd_kcontrol_new adau1372_decimator2_3_mux_control = + SOC_DAPM_ENUM("Decimator 2+3 Capture Mux", adau1372_decimator2_3_mux_enum); + +static const unsigned int adau1372_asrco_mux_values[] = { + 4, 5, 6, 7, +}; + +static const char * const adau1372_asrco_mux_text[] = { + "Decimator0", + "Decimator1", + "Decimator2", + "Decimator3", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco0_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1, + 0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco1_mux_enum, ADAU1372_REG_ASRCO_SOURCE_0_1, + 4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco2_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3, + 0, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_asrco3_mux_enum, ADAU1372_REG_ASRCO_SOURCE_2_3, + 4, 0xf, adau1372_asrco_mux_text, adau1372_asrco_mux_values); + +static const struct snd_kcontrol_new adau1372_asrco0_mux_control = + SOC_DAPM_ENUM("Output ASRC0 Capture Mux", adau1372_asrco0_mux_enum); +static const struct snd_kcontrol_new adau1372_asrco1_mux_control = + SOC_DAPM_ENUM("Output ASRC1 Capture Mux", adau1372_asrco1_mux_enum); +static const struct snd_kcontrol_new adau1372_asrco2_mux_control = + SOC_DAPM_ENUM("Output ASRC2 Capture Mux", adau1372_asrco2_mux_enum); +static const struct snd_kcontrol_new adau1372_asrco3_mux_control = + SOC_DAPM_ENUM("Output ASRC3 Capture Mux", adau1372_asrco3_mux_enum); + +static const unsigned int adau1372_sout_mux_values[] = { + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +static const char * const adau1372_sout_mux_text[] = { + "Output ASRC0", + "Output ASRC1", + "Output ASRC2", + "Output ASRC3", + "Serial Input 0", + "Serial Input 1", + "Serial Input 2", + "Serial Input 3", + "Serial Input 4", + "Serial Input 5", + "Serial Input 6", + "Serial Input 7", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout0_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout1_mux_enum, ADAU1372_REG_SOUT_SOURCE_0_1, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout2_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout3_mux_enum, ADAU1372_REG_SOUT_SOURCE_2_3, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout4_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout5_mux_enum, ADAU1372_REG_SOUT_SOURCE_4_5, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout6_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7, + 0, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_sout7_mux_enum, ADAU1372_REG_SOUT_SOURCE_6_7, + 4, 0xf, adau1372_sout_mux_text, adau1372_sout_mux_values); + +static const struct snd_kcontrol_new adau1372_sout0_mux_control = + SOC_DAPM_ENUM("Serial Output 0 Capture Mux", adau1372_sout0_mux_enum); +static const struct snd_kcontrol_new adau1372_sout1_mux_control = + SOC_DAPM_ENUM("Serial Output 1 Capture Mux", adau1372_sout1_mux_enum); +static const struct snd_kcontrol_new adau1372_sout2_mux_control = + SOC_DAPM_ENUM("Serial Output 2 Capture Mux", adau1372_sout2_mux_enum); +static const struct snd_kcontrol_new adau1372_sout3_mux_control = + SOC_DAPM_ENUM("Serial Output 3 Capture Mux", adau1372_sout3_mux_enum); +static const struct snd_kcontrol_new adau1372_sout4_mux_control = + SOC_DAPM_ENUM("Serial Output 4 Capture Mux", adau1372_sout4_mux_enum); +static const struct snd_kcontrol_new adau1372_sout5_mux_control = + SOC_DAPM_ENUM("Serial Output 5 Capture Mux", adau1372_sout5_mux_enum); +static const struct snd_kcontrol_new adau1372_sout6_mux_control = + SOC_DAPM_ENUM("Serial Output 6 Capture Mux", adau1372_sout6_mux_enum); +static const struct snd_kcontrol_new adau1372_sout7_mux_control = + SOC_DAPM_ENUM("Serial Output 7 Capture Mux", adau1372_sout7_mux_enum); + +static const char * const adau1372_asrci_mux_text[] = { + "Serial Input 0+1", + "Serial Input 2+3", + "Serial Input 4+5", + "Serial Input 6+7", +}; + +static SOC_ENUM_SINGLE_DECL(adau1372_asrci_mux_enum, + ADAU1372_REG_ASRC_MODE, 2, adau1372_asrci_mux_text); + +static const struct snd_kcontrol_new adau1372_asrci_mux_control = + SOC_DAPM_ENUM("Input ASRC Playback Mux", adau1372_asrci_mux_enum); + +static const unsigned int adau1372_dac_mux_values[] = { + 12, 13 +}; + +static const char * const adau1372_dac_mux_text[] = { + "Input ASRC0", + "Input ASRC1", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac0_mux_enum, ADAU1372_REG_DAC_SOURCE, + 0, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1372_dac1_mux_enum, ADAU1372_REG_DAC_SOURCE, + 4, 0xf, adau1372_dac_mux_text, adau1372_dac_mux_values); + +static const struct snd_kcontrol_new adau1372_dac0_mux_control = + SOC_DAPM_ENUM("DAC 0 Playback Mux", adau1372_dac0_mux_enum); +static const struct snd_kcontrol_new adau1372_dac1_mux_control = + SOC_DAPM_ENUM("DAC 1 Playback Mux", adau1372_dac1_mux_enum); + +static const struct snd_soc_dapm_widget adau1372_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("AIN0"), + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("DMIC0_1"), + SND_SOC_DAPM_INPUT("DMIC2_3"), + + SND_SOC_DAPM_SUPPLY("MICBIAS0", ADAU1372_REG_MICBIAS, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1372_REG_MICBIAS, 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("PGA0", ADAU1372_REG_PGA_CTRL(0), 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("PGA1", ADAU1372_REG_PGA_CTRL(1), 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("PGA2", ADAU1372_REG_PGA_CTRL(2), 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("PGA3", ADAU1372_REG_PGA_CTRL(3), 6, 1, NULL, 0), + SND_SOC_DAPM_ADC("ADC0", NULL, ADAU1372_REG_ADC_CTRL2, 0, 0), + SND_SOC_DAPM_ADC("ADC1", NULL, ADAU1372_REG_ADC_CTRL2, 1, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, ADAU1372_REG_ADC_CTRL3, 0, 0), + SND_SOC_DAPM_ADC("ADC3", NULL, ADAU1372_REG_ADC_CTRL3, 1, 0), + + SND_SOC_DAPM_SUPPLY("ADC0 Filter", ADAU1372_REG_DECIM_PWR, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 Filter", ADAU1372_REG_DECIM_PWR, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 Filter", ADAU1372_REG_DECIM_PWR, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC3 Filter", ADAU1372_REG_DECIM_PWR, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC0 Decimator", ADAU1372_REG_DECIM_PWR, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC1 Decimator", ADAU1372_REG_DECIM_PWR, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC2 Decimator", ADAU1372_REG_DECIM_PWR, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Output ASRC3 Decimator", ADAU1372_REG_DECIM_PWR, 7, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Decimator0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control), + SND_SOC_DAPM_MUX("Decimator1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator0_1_mux_control), + SND_SOC_DAPM_MUX("Decimator2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control), + SND_SOC_DAPM_MUX("Decimator3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_decimator2_3_mux_control), + + SND_SOC_DAPM_MUX("Output ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco0_mux_control), + SND_SOC_DAPM_MUX("Output ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco1_mux_control), + SND_SOC_DAPM_MUX("Output ASRC2 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco2_mux_control), + SND_SOC_DAPM_MUX("Output ASRC3 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrco3_mux_control), + SND_SOC_DAPM_MUX("Serial Output 0 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout0_mux_control), + SND_SOC_DAPM_MUX("Serial Output 1 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout1_mux_control), + SND_SOC_DAPM_MUX("Serial Output 2 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout2_mux_control), + SND_SOC_DAPM_MUX("Serial Output 3 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout3_mux_control), + SND_SOC_DAPM_MUX("Serial Output 4 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout4_mux_control), + SND_SOC_DAPM_MUX("Serial Output 5 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout5_mux_control), + SND_SOC_DAPM_MUX("Serial Output 6 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout6_mux_control), + SND_SOC_DAPM_MUX("Serial Output 7 Capture Mux", SND_SOC_NOPM, 0, 0, + &adau1372_sout7_mux_control), + + SND_SOC_DAPM_AIF_IN("Serial Input 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 1", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 2", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 3", NULL, 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 4", NULL, 4, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 5", NULL, 5, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 6", NULL, 6, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Serial Input 7", NULL, 7, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("Serial Output 0", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 1", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 2", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 3", NULL, 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 4", NULL, 4, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 5", NULL, 5, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 6", NULL, 6, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Serial Output 7", NULL, 7, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("Output ASRC Supply", ADAU1372_REG_ASRC_MODE, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Input ASRC Supply", ADAU1372_REG_ASRC_MODE, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC1 Modulator", ADAU1372_REG_INTERP_PWR, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC0 Modulator", ADAU1372_REG_INTERP_PWR, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Input ASRC1 Interpolator", ADAU1372_REG_INTERP_PWR, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Input ASRC0 Interpolator", ADAU1372_REG_INTERP_PWR, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Input ASRC0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control), + SND_SOC_DAPM_MUX("Input ASRC1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_asrci_mux_control), + + SND_SOC_DAPM_MUX("DAC 0 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac0_mux_control), + SND_SOC_DAPM_MUX("DAC 1 Mux", SND_SOC_NOPM, 0, 0, &adau1372_dac1_mux_control), + + SND_SOC_DAPM_DAC("DAC0", NULL, ADAU1372_REG_DAC_CTRL, 0, 0), + SND_SOC_DAPM_DAC("DAC1", NULL, ADAU1372_REG_DAC_CTRL, 1, 0), + + SND_SOC_DAPM_OUT_DRV("OP_STAGE_LP", ADAU1372_REG_OP_STAGE_CTRL, 0, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("OP_STAGE_LN", ADAU1372_REG_OP_STAGE_CTRL, 1, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("OP_STAGE_RP", ADAU1372_REG_OP_STAGE_CTRL, 2, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("OP_STAGE_RN", ADAU1372_REG_OP_STAGE_CTRL, 3, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), +}; + +#define ADAU1372_SOUT_ROUTES(x) \ + { "Serial Output " #x " Capture Mux", "Output ASRC0", "Output ASRC0 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Output ASRC1", "Output ASRC1 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Output ASRC2", "Output ASRC2 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Output ASRC3", "Output ASRC3 Mux" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 0", "Serial Input 0" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 1", "Serial Input 1" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 2", "Serial Input 2" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 3", "Serial Input 3" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 4", "Serial Input 4" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 5", "Serial Input 5" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 6", "Serial Input 6" }, \ + { "Serial Output " #x " Capture Mux", "Serial Input 7", "Serial Input 7" }, \ + { "Serial Output " #x, NULL, "Serial Output " #x " Capture Mux" }, \ + { "Capture", NULL, "Serial Output " #x } + +#define ADAU1372_ASRCO_ROUTES(x) \ + { "Output ASRC" #x " Mux", "Decimator0", "Decimator0 Mux" }, \ + { "Output ASRC" #x " Mux", "Decimator1", "Decimator1 Mux" }, \ + { "Output ASRC" #x " Mux", "Decimator2", "Decimator2 Mux" }, \ + { "Output ASRC" #x " Mux", "Decimator3", "Decimator3 Mux" } + +static const struct snd_soc_dapm_route adau1372_dapm_routes[] = { + { "PGA0", NULL, "AIN0" }, + { "PGA1", NULL, "AIN1" }, + { "PGA2", NULL, "AIN2" }, + { "PGA3", NULL, "AIN3" }, + + { "ADC0", NULL, "PGA0" }, + { "ADC1", NULL, "PGA1" }, + { "ADC2", NULL, "PGA2" }, + { "ADC3", NULL, "PGA3" }, + + { "Decimator0 Mux", "ADC", "ADC0" }, + { "Decimator1 Mux", "ADC", "ADC1" }, + { "Decimator2 Mux", "ADC", "ADC2" }, + { "Decimator3 Mux", "ADC", "ADC3" }, + + { "Decimator0 Mux", "DMIC", "DMIC0_1" }, + { "Decimator1 Mux", "DMIC", "DMIC0_1" }, + { "Decimator2 Mux", "DMIC", "DMIC2_3" }, + { "Decimator3 Mux", "DMIC", "DMIC2_3" }, + + { "Decimator0 Mux", NULL, "ADC0 Filter" }, + { "Decimator1 Mux", NULL, "ADC1 Filter" }, + { "Decimator2 Mux", NULL, "ADC2 Filter" }, + { "Decimator3 Mux", NULL, "ADC3 Filter" }, + + { "Output ASRC0 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC1 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC2 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC3 Mux", NULL, "Output ASRC Supply" }, + { "Output ASRC0 Mux", NULL, "Output ASRC0 Decimator" }, + { "Output ASRC1 Mux", NULL, "Output ASRC1 Decimator" }, + { "Output ASRC2 Mux", NULL, "Output ASRC2 Decimator" }, + { "Output ASRC3 Mux", NULL, "Output ASRC3 Decimator" }, + + ADAU1372_ASRCO_ROUTES(0), + ADAU1372_ASRCO_ROUTES(1), + ADAU1372_ASRCO_ROUTES(2), + ADAU1372_ASRCO_ROUTES(3), + + ADAU1372_SOUT_ROUTES(0), + ADAU1372_SOUT_ROUTES(1), + ADAU1372_SOUT_ROUTES(2), + ADAU1372_SOUT_ROUTES(3), + ADAU1372_SOUT_ROUTES(4), + ADAU1372_SOUT_ROUTES(5), + ADAU1372_SOUT_ROUTES(6), + ADAU1372_SOUT_ROUTES(7), + + { "Serial Input 0", NULL, "Playback" }, + { "Serial Input 1", NULL, "Playback" }, + { "Serial Input 2", NULL, "Playback" }, + { "Serial Input 3", NULL, "Playback" }, + { "Serial Input 4", NULL, "Playback" }, + { "Serial Input 5", NULL, "Playback" }, + { "Serial Input 6", NULL, "Playback" }, + { "Serial Input 7", NULL, "Playback" }, + + { "Input ASRC0 Mux", "Serial Input 0+1", "Serial Input 0" }, + { "Input ASRC1 Mux", "Serial Input 0+1", "Serial Input 1" }, + { "Input ASRC0 Mux", "Serial Input 2+3", "Serial Input 2" }, + { "Input ASRC1 Mux", "Serial Input 2+3", "Serial Input 3" }, + { "Input ASRC0 Mux", "Serial Input 4+5", "Serial Input 4" }, + { "Input ASRC1 Mux", "Serial Input 4+5", "Serial Input 5" }, + { "Input ASRC0 Mux", "Serial Input 6+7", "Serial Input 6" }, + { "Input ASRC1 Mux", "Serial Input 6+7", "Serial Input 7" }, + { "Input ASRC0 Mux", NULL, "Input ASRC Supply" }, + { "Input ASRC1 Mux", NULL, "Input ASRC Supply" }, + { "Input ASRC0 Mux", NULL, "Input ASRC0 Interpolator" }, + { "Input ASRC1 Mux", NULL, "Input ASRC1 Interpolator" }, + + { "DAC 0 Mux", "Input ASRC0", "Input ASRC0 Mux" }, + { "DAC 0 Mux", "Input ASRC1", "Input ASRC1 Mux" }, + { "DAC 1 Mux", "Input ASRC0", "Input ASRC0 Mux" }, + { "DAC 1 Mux", "Input ASRC1", "Input ASRC1 Mux" }, + + { "DAC0", NULL, "DAC 0 Mux" }, + { "DAC1", NULL, "DAC 1 Mux" }, + { "DAC0", NULL, "DAC0 Modulator" }, + { "DAC1", NULL, "DAC1 Modulator" }, + + { "OP_STAGE_LP", NULL, "DAC0" }, + { "OP_STAGE_LN", NULL, "DAC0" }, + { "OP_STAGE_RP", NULL, "DAC1" }, + { "OP_STAGE_RN", NULL, "DAC1" }, + + { "HPOUTL", NULL, "OP_STAGE_LP" }, + { "HPOUTL", NULL, "OP_STAGE_LN" }, + { "HPOUTR", NULL, "OP_STAGE_RP" }, + { "HPOUTR", NULL, "OP_STAGE_RN" }, +}; + +static int adau1372_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int sai0 = 0, sai1 = 0; + bool invert_lrclk = false; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + adau1372->clock_provider = true; + sai1 |= ADAU1372_SAI1_MS; + break; + case SND_SOC_DAIFMT_CBC_CFC: + adau1372->clock_provider = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_lrclk = true; + break; + case SND_SOC_DAIFMT_IB_NF: + invert_lrclk = false; + sai1 |= ADAU1372_SAI1_BCLKEDGE; + break; + case SND_SOC_DAIFMT_IB_IF: + invert_lrclk = true; + sai1 |= ADAU1372_SAI1_BCLKEDGE; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + sai0 |= ADAU1372_SAI0_DELAY1; + break; + case SND_SOC_DAIFMT_LEFT_J: + sai0 |= ADAU1372_SAI0_DELAY0; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_DSP_A: + sai0 |= ADAU1372_SAI0_DELAY1; + sai1 |= ADAU1372_SAI1_LR_MODE; + break; + case SND_SOC_DAIFMT_DSP_B: + sai0 |= ADAU1372_SAI0_DELAY0; + sai1 |= ADAU1372_SAI1_LR_MODE; + break; + } + + if (invert_lrclk) + sai1 |= ADAU1372_SAI1_LR_POL; + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_DELAY_MASK, sai0); + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, + ADAU1372_SAI1_MS | ADAU1372_SAI1_BCLKEDGE | + ADAU1372_SAI1_LR_MODE | ADAU1372_SAI1_LR_POL, sai1); + + return 0; +} + +static int adau1372_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + unsigned int slot_width; + unsigned int sai0, sai1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(adau1372_rates); i++) { + if (rate == adau1372_rates[i]) + break; + } + + if (i == ARRAY_SIZE(adau1372_rates)) + return -EINVAL; + + sai0 = i; + + slot_width = adau1372->slot_width; + if (slot_width == 0) + slot_width = params_width(params); + + switch (slot_width) { + case 16: + sai1 = ADAU1372_SAI1_BCLKRATE; + break; + case 24: + case 32: + sai1 = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_FS_MASK, sai0); + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLKRATE, sai1); + + return 0; +} + +static int adau1372_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int sai0, sai1; + + /* I2S mode */ + if (slots == 0) { + /* The other settings dont matter in I2S mode */ + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, + ADAU1372_SAI0_SAI_MASK, ADAU1372_SAI0_SAI_I2S); + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2; + adau1372->slot_width = 0; + return 0; + } + + /* We have 8 channels anything outside that is not supported */ + if ((tx_mask & ~0xff) != 0 || (rx_mask & ~0xff) != 0) + return -EINVAL; + + switch (width) { + case 16: + sai1 = ADAU1372_SAI1_BCLK_TDMC; + break; + case 24: + case 32: + sai1 = 0; + break; + default: + return -EINVAL; + } + + switch (slots) { + case 2: + sai0 = ADAU1372_SAI0_SAI_TDM2; + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2; + break; + case 4: + sai0 = ADAU1372_SAI0_SAI_TDM4; + if (adau1372->clock_provider) + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4_MASTER; + else + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM4; + break; + case 8: + sai0 = ADAU1372_SAI0_SAI_TDM8; + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM8; + break; + default: + return -EINVAL; + } + + adau1372->slot_width = width; + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI0, ADAU1372_SAI0_SAI_MASK, sai0); + regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_BCLK_TDMC, sai1); + + /* Mask is inverted in hardware */ + regmap_write(adau1372->regmap, ADAU1372_REG_SOUT_CTRL, ~tx_mask); + + return 0; +} + +static int adau1372_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + unsigned int sai1; + + if (tristate) + sai1 = ADAU1372_SAI1_TDM_TS; + else + sai1 = 0; + + return regmap_update_bits(adau1372->regmap, ADAU1372_REG_SAI1, ADAU1372_SAI1_TDM_TS, sai1); +} + +static int adau1372_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct adau1372 *adau1372 = snd_soc_dai_get_drvdata(dai); + + snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &adau1372->rate_constraints); + + return 0; +} + +static void adau1372_enable_pll(struct adau1372 *adau1372) +{ + unsigned int val, timeout = 0; + int ret; + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_PLL_EN, ADAU1372_CLK_CTRL_PLL_EN); + do { + /* Takes about 1ms to lock */ + usleep_range(1000, 2000); + ret = regmap_read(adau1372->regmap, ADAU1372_REG_PLL(5), &val); + if (ret) + break; + timeout++; + } while (!(val & 1) && timeout < 3); + + if (ret < 0 || !(val & 1)) + dev_err(adau1372->dev, "Failed to lock PLL\n"); +} + +static void adau1372_set_power(struct adau1372 *adau1372, bool enable) +{ + if (adau1372->enabled == enable) + return; + + if (enable) { + unsigned int clk_ctrl = ADAU1372_CLK_CTRL_MCLK_EN; + + clk_prepare_enable(adau1372->mclk); + if (adau1372->pd_gpio) + gpiod_set_value(adau1372->pd_gpio, 0); + + if (adau1372->switch_mode) + adau1372->switch_mode(adau1372->dev); + + regcache_cache_only(adau1372->regmap, false); + + /* + * Clocks needs to be enabled before any other register can be + * accessed. + */ + if (adau1372->use_pll) { + adau1372_enable_pll(adau1372); + clk_ctrl |= ADAU1372_CLK_CTRL_CLKSRC; + } + + regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_CLKSRC, clk_ctrl); + regcache_sync(adau1372->regmap); + } else { + if (adau1372->pd_gpio) { + /* + * This will turn everything off and reset the register + * map. No need to do any register writes to manually + * turn things off. + */ + gpiod_set_value(adau1372->pd_gpio, 1); + regcache_mark_dirty(adau1372->regmap); + } else { + regmap_update_bits(adau1372->regmap, ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_MCLK_EN | ADAU1372_CLK_CTRL_PLL_EN, 0); + } + clk_disable_unprepare(adau1372->mclk); + regcache_cache_only(adau1372->regmap, true); + } + + adau1372->enabled = enable; +} + +static int adau1372_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adau1372 *adau1372 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + adau1372_set_power(adau1372, true); + break; + case SND_SOC_BIAS_OFF: + adau1372_set_power(adau1372, false); + break; + } + + return 0; +} + +static const struct snd_soc_component_driver adau1372_driver = { + .set_bias_level = adau1372_set_bias_level, + .controls = adau1372_controls, + .num_controls = ARRAY_SIZE(adau1372_controls), + .dapm_widgets = adau1372_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1372_dapm_widgets), + .dapm_routes = adau1372_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1372_dapm_routes), + .endianness = 1, +}; + +static const struct snd_soc_dai_ops adau1372_dai_ops = { + .set_fmt = adau1372_set_dai_fmt, + .set_tdm_slot = adau1372_set_tdm_slot, + .set_tristate = adau1372_set_tristate, + .hw_params = adau1372_hw_params, + .startup = adau1372_startup, +}; + +#define ADAU1372_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver adau1372_dai_driver = { + .name = "adau1372", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = ADAU1372_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = ADAU1372_FORMATS, + .sig_bits = 24, + }, + .ops = &adau1372_dai_ops, + .symmetric_rate = 1, +}; + +static int adau1372_setup_pll(struct adau1372 *adau1372, unsigned int rate) +{ + u8 regs[5]; + unsigned int i; + int ret; + + ret = adau_calc_pll_cfg(rate, 49152000, regs); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(regs); i++) + regmap_write(adau1372->regmap, ADAU1372_REG_PLL(i), regs[i]); + + return 0; +} + +int adau1372_probe(struct device *dev, struct regmap *regmap, + void (*switch_mode)(struct device *dev)) +{ + struct adau1372 *adau1372; + unsigned int clk_ctrl; + unsigned long rate; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + adau1372 = devm_kzalloc(dev, sizeof(*adau1372), GFP_KERNEL); + if (!adau1372) + return -ENOMEM; + + adau1372->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(adau1372->mclk)) + return PTR_ERR(adau1372->mclk); + + adau1372->pd_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(adau1372->pd_gpio)) + return PTR_ERR(adau1372->pd_gpio); + + adau1372->regmap = regmap; + adau1372->switch_mode = switch_mode; + adau1372->dev = dev; + adau1372->rate_constraints.list = adau1372_rates; + adau1372->rate_constraints.count = ARRAY_SIZE(adau1372_rates); + adau1372->rate_constraints.mask = ADAU1372_RATE_MASK_TDM2; + + dev_set_drvdata(dev, adau1372); + + /* + * The datasheet says that the internal MCLK always needs to run at + * 12.288MHz. Automatically choose a valid configuration from the + * external clock. + */ + rate = clk_get_rate(adau1372->mclk); + + switch (rate) { + case 12288000: + clk_ctrl = ADAU1372_CLK_CTRL_CC_MDIV; + break; + case 24576000: + clk_ctrl = 0; + break; + default: + clk_ctrl = 0; + ret = adau1372_setup_pll(adau1372, rate); + if (ret < 0) + return ret; + adau1372->use_pll = true; + break; + } + + /* + * Most of the registers are inaccessible unless the internal clock is + * enabled. + */ + regcache_cache_only(regmap, true); + + regmap_update_bits(regmap, ADAU1372_REG_CLK_CTRL, ADAU1372_CLK_CTRL_CC_MDIV, clk_ctrl); + + /* + * No pinctrl support yet, put the multi-purpose pins in the most + * sensible mode for general purpose CODEC operation. + */ + regmap_write(regmap, ADAU1372_REG_MODE_MP(1), 0x00); /* SDATA OUT */ + regmap_write(regmap, ADAU1372_REG_MODE_MP(6), 0x12); /* CLOCKOUT */ + + regmap_write(regmap, ADAU1372_REG_OP_STAGE_MUTE, 0x0); + + regmap_write(regmap, 0x7, 0x01); /* CLOCK OUT */ + + return devm_snd_soc_register_component(dev, &adau1372_driver, &adau1372_dai_driver, 1); +} +EXPORT_SYMBOL(adau1372_probe); + +static const struct reg_default adau1372_reg_defaults[] = { + { ADAU1372_REG_CLK_CTRL, 0x00 }, + { ADAU1372_REG_PLL(0), 0x00 }, + { ADAU1372_REG_PLL(1), 0x00 }, + { ADAU1372_REG_PLL(2), 0x00 }, + { ADAU1372_REG_PLL(3), 0x00 }, + { ADAU1372_REG_PLL(4), 0x00 }, + { ADAU1372_REG_PLL(5), 0x00 }, + { ADAU1372_REG_DAC_SOURCE, 0x10 }, + { ADAU1372_REG_SOUT_SOURCE_0_1, 0x54 }, + { ADAU1372_REG_SOUT_SOURCE_2_3, 0x76 }, + { ADAU1372_REG_SOUT_SOURCE_4_5, 0x54 }, + { ADAU1372_REG_SOUT_SOURCE_6_7, 0x76 }, + { ADAU1372_REG_ADC_SDATA_CH, 0x04 }, + { ADAU1372_REG_ASRCO_SOURCE_0_1, 0x10 }, + { ADAU1372_REG_ASRCO_SOURCE_2_3, 0x32 }, + { ADAU1372_REG_ASRC_MODE, 0x00 }, + { ADAU1372_REG_ADC_CTRL0, 0x19 }, + { ADAU1372_REG_ADC_CTRL1, 0x19 }, + { ADAU1372_REG_ADC_CTRL2, 0x00 }, + { ADAU1372_REG_ADC_CTRL3, 0x00 }, + { ADAU1372_REG_ADC_VOL(0), 0x00 }, + { ADAU1372_REG_ADC_VOL(1), 0x00 }, + { ADAU1372_REG_ADC_VOL(2), 0x00 }, + { ADAU1372_REG_ADC_VOL(3), 0x00 }, + { ADAU1372_REG_PGA_CTRL(0), 0x40 }, + { ADAU1372_REG_PGA_CTRL(1), 0x40 }, + { ADAU1372_REG_PGA_CTRL(2), 0x40 }, + { ADAU1372_REG_PGA_CTRL(3), 0x40 }, + { ADAU1372_REG_PGA_BOOST, 0x00 }, + { ADAU1372_REG_MICBIAS, 0x00 }, + { ADAU1372_REG_DAC_CTRL, 0x18 }, + { ADAU1372_REG_DAC_VOL(0), 0x00 }, + { ADAU1372_REG_DAC_VOL(1), 0x00 }, + { ADAU1372_REG_OP_STAGE_MUTE, 0x0f }, + { ADAU1372_REG_SAI0, 0x00 }, + { ADAU1372_REG_SAI1, 0x00 }, + { ADAU1372_REG_SOUT_CTRL, 0x00 }, + { ADAU1372_REG_MODE_MP(0), 0x00 }, + { ADAU1372_REG_MODE_MP(1), 0x10 }, + { ADAU1372_REG_MODE_MP(4), 0x00 }, + { ADAU1372_REG_MODE_MP(5), 0x00 }, + { ADAU1372_REG_MODE_MP(6), 0x11 }, + { ADAU1372_REG_OP_STAGE_CTRL, 0x0f }, + { ADAU1372_REG_DECIM_PWR, 0x00 }, + { ADAU1372_REG_INTERP_PWR, 0x00 }, + { ADAU1372_REG_BIAS_CTRL0, 0x00 }, + { ADAU1372_REG_BIAS_CTRL1, 0x00 }, +}; + +static bool adau1372_volatile_register(struct device *dev, unsigned int reg) +{ + if (reg == ADAU1372_REG_PLL(5)) + return true; + + return false; +} + +const struct regmap_config adau1372_regmap_config = { + .val_bits = 8, + .reg_bits = 16, + .max_register = 0x4d, + + .reg_defaults = adau1372_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1372_reg_defaults), + .volatile_reg = adau1372_volatile_register, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_GPL(adau1372_regmap_config); + +MODULE_DESCRIPTION("ASoC ADAU1372 CODEC driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/adau1372.h b/sound/soc/codecs/adau1372.h new file mode 100644 index 0000000000..a9d2c59b73 --- /dev/null +++ b/sound/soc/codecs/adau1372.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADAU1372 driver + * + * Copyright 2016 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#ifndef SOUND_SOC_CODECS_ADAU1372_H +#define SOUND_SOC_CODECS_ADAU1372_H + +#include + +struct device; + +int adau1372_probe(struct device *dev, struct regmap *regmap, + void (*switch_mode)(struct device *dev)); + +extern const struct regmap_config adau1372_regmap_config; + +#endif diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c new file mode 100644 index 0000000000..b0ab0a69b2 --- /dev/null +++ b/sound/soc/codecs/adau1373.c @@ -0,0 +1,1516 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices ADAU1373 Audio Codec drive + * + * Copyright 2011 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "adau1373.h" +#include "adau-utils.h" + +struct adau1373_dai { + unsigned int clk_src; + unsigned int sysclk; + bool enable_src; + bool clock_provider; +}; + +struct adau1373 { + struct regmap *regmap; + struct adau1373_dai dais[3]; +}; + +#define ADAU1373_INPUT_MODE 0x00 +#define ADAU1373_AINL_CTRL(x) (0x01 + (x) * 2) +#define ADAU1373_AINR_CTRL(x) (0x02 + (x) * 2) +#define ADAU1373_LLINE_OUT(x) (0x9 + (x) * 2) +#define ADAU1373_RLINE_OUT(x) (0xa + (x) * 2) +#define ADAU1373_LSPK_OUT 0x0d +#define ADAU1373_RSPK_OUT 0x0e +#define ADAU1373_LHP_OUT 0x0f +#define ADAU1373_RHP_OUT 0x10 +#define ADAU1373_ADC_GAIN 0x11 +#define ADAU1373_LADC_MIXER 0x12 +#define ADAU1373_RADC_MIXER 0x13 +#define ADAU1373_LLINE1_MIX 0x14 +#define ADAU1373_RLINE1_MIX 0x15 +#define ADAU1373_LLINE2_MIX 0x16 +#define ADAU1373_RLINE2_MIX 0x17 +#define ADAU1373_LSPK_MIX 0x18 +#define ADAU1373_RSPK_MIX 0x19 +#define ADAU1373_LHP_MIX 0x1a +#define ADAU1373_RHP_MIX 0x1b +#define ADAU1373_EP_MIX 0x1c +#define ADAU1373_HP_CTRL 0x1d +#define ADAU1373_HP_CTRL2 0x1e +#define ADAU1373_LS_CTRL 0x1f +#define ADAU1373_EP_CTRL 0x21 +#define ADAU1373_MICBIAS_CTRL1 0x22 +#define ADAU1373_MICBIAS_CTRL2 0x23 +#define ADAU1373_OUTPUT_CTRL 0x24 +#define ADAU1373_PWDN_CTRL1 0x25 +#define ADAU1373_PWDN_CTRL2 0x26 +#define ADAU1373_PWDN_CTRL3 0x27 +#define ADAU1373_DPLL_CTRL(x) (0x28 + (x) * 7) +#define ADAU1373_PLL_CTRL1(x) (0x29 + (x) * 7) +#define ADAU1373_PLL_CTRL2(x) (0x2a + (x) * 7) +#define ADAU1373_PLL_CTRL3(x) (0x2b + (x) * 7) +#define ADAU1373_PLL_CTRL4(x) (0x2c + (x) * 7) +#define ADAU1373_PLL_CTRL5(x) (0x2d + (x) * 7) +#define ADAU1373_PLL_CTRL6(x) (0x2e + (x) * 7) +#define ADAU1373_HEADDECT 0x36 +#define ADAU1373_ADC_DAC_STATUS 0x37 +#define ADAU1373_ADC_CTRL 0x3c +#define ADAU1373_DAI(x) (0x44 + (x)) +#define ADAU1373_CLK_SRC_DIV(x) (0x40 + (x) * 2) +#define ADAU1373_BCLKDIV(x) (0x47 + (x)) +#define ADAU1373_SRC_RATIOA(x) (0x4a + (x) * 2) +#define ADAU1373_SRC_RATIOB(x) (0x4b + (x) * 2) +#define ADAU1373_DEEMP_CTRL 0x50 +#define ADAU1373_SRC_DAI_CTRL(x) (0x51 + (x)) +#define ADAU1373_DIN_MIX_CTRL(x) (0x56 + (x)) +#define ADAU1373_DOUT_MIX_CTRL(x) (0x5b + (x)) +#define ADAU1373_DAI_PBL_VOL(x) (0x62 + (x) * 2) +#define ADAU1373_DAI_PBR_VOL(x) (0x63 + (x) * 2) +#define ADAU1373_DAI_RECL_VOL(x) (0x68 + (x) * 2) +#define ADAU1373_DAI_RECR_VOL(x) (0x69 + (x) * 2) +#define ADAU1373_DAC1_PBL_VOL 0x6e +#define ADAU1373_DAC1_PBR_VOL 0x6f +#define ADAU1373_DAC2_PBL_VOL 0x70 +#define ADAU1373_DAC2_PBR_VOL 0x71 +#define ADAU1373_ADC_RECL_VOL 0x72 +#define ADAU1373_ADC_RECR_VOL 0x73 +#define ADAU1373_DMIC_RECL_VOL 0x74 +#define ADAU1373_DMIC_RECR_VOL 0x75 +#define ADAU1373_VOL_GAIN1 0x76 +#define ADAU1373_VOL_GAIN2 0x77 +#define ADAU1373_VOL_GAIN3 0x78 +#define ADAU1373_HPF_CTRL 0x7d +#define ADAU1373_BASS1 0x7e +#define ADAU1373_BASS2 0x7f +#define ADAU1373_DRC(x) (0x80 + (x) * 0x10) +#define ADAU1373_3D_CTRL1 0xc0 +#define ADAU1373_3D_CTRL2 0xc1 +#define ADAU1373_FDSP_SEL1 0xdc +#define ADAU1373_FDSP_SEL2 0xdd +#define ADAU1373_FDSP_SEL3 0xde +#define ADAU1373_FDSP_SEL4 0xdf +#define ADAU1373_DIGMICCTRL 0xe2 +#define ADAU1373_DIGEN 0xeb +#define ADAU1373_SOFT_RESET 0xff + + +#define ADAU1373_PLL_CTRL6_DPLL_BYPASS BIT(1) +#define ADAU1373_PLL_CTRL6_PLL_EN BIT(0) + +#define ADAU1373_DAI_INVERT_BCLK BIT(7) +#define ADAU1373_DAI_MASTER BIT(6) +#define ADAU1373_DAI_INVERT_LRCLK BIT(4) +#define ADAU1373_DAI_WLEN_16 0x0 +#define ADAU1373_DAI_WLEN_20 0x4 +#define ADAU1373_DAI_WLEN_24 0x8 +#define ADAU1373_DAI_WLEN_32 0xc +#define ADAU1373_DAI_WLEN_MASK 0xc +#define ADAU1373_DAI_FORMAT_RIGHT_J 0x0 +#define ADAU1373_DAI_FORMAT_LEFT_J 0x1 +#define ADAU1373_DAI_FORMAT_I2S 0x2 +#define ADAU1373_DAI_FORMAT_DSP 0x3 + +#define ADAU1373_BCLKDIV_SOURCE BIT(5) +#define ADAU1373_BCLKDIV_SR_MASK (0x07 << 2) +#define ADAU1373_BCLKDIV_BCLK_MASK 0x03 +#define ADAU1373_BCLKDIV_32 0x03 +#define ADAU1373_BCLKDIV_64 0x02 +#define ADAU1373_BCLKDIV_128 0x01 +#define ADAU1373_BCLKDIV_256 0x00 + +#define ADAU1373_ADC_CTRL_PEAK_DETECT BIT(0) +#define ADAU1373_ADC_CTRL_RESET BIT(1) +#define ADAU1373_ADC_CTRL_RESET_FORCE BIT(2) + +#define ADAU1373_OUTPUT_CTRL_LDIFF BIT(3) +#define ADAU1373_OUTPUT_CTRL_LNFBEN BIT(2) + +#define ADAU1373_PWDN_CTRL3_PWR_EN BIT(0) + +#define ADAU1373_EP_CTRL_MICBIAS1_OFFSET 4 +#define ADAU1373_EP_CTRL_MICBIAS2_OFFSET 2 + +static const struct reg_default adau1373_reg_defaults[] = { + { ADAU1373_INPUT_MODE, 0x00 }, + { ADAU1373_AINL_CTRL(0), 0x00 }, + { ADAU1373_AINR_CTRL(0), 0x00 }, + { ADAU1373_AINL_CTRL(1), 0x00 }, + { ADAU1373_AINR_CTRL(1), 0x00 }, + { ADAU1373_AINL_CTRL(2), 0x00 }, + { ADAU1373_AINR_CTRL(2), 0x00 }, + { ADAU1373_AINL_CTRL(3), 0x00 }, + { ADAU1373_AINR_CTRL(3), 0x00 }, + { ADAU1373_LLINE_OUT(0), 0x00 }, + { ADAU1373_RLINE_OUT(0), 0x00 }, + { ADAU1373_LLINE_OUT(1), 0x00 }, + { ADAU1373_RLINE_OUT(1), 0x00 }, + { ADAU1373_LSPK_OUT, 0x00 }, + { ADAU1373_RSPK_OUT, 0x00 }, + { ADAU1373_LHP_OUT, 0x00 }, + { ADAU1373_RHP_OUT, 0x00 }, + { ADAU1373_ADC_GAIN, 0x00 }, + { ADAU1373_LADC_MIXER, 0x00 }, + { ADAU1373_RADC_MIXER, 0x00 }, + { ADAU1373_LLINE1_MIX, 0x00 }, + { ADAU1373_RLINE1_MIX, 0x00 }, + { ADAU1373_LLINE2_MIX, 0x00 }, + { ADAU1373_RLINE2_MIX, 0x00 }, + { ADAU1373_LSPK_MIX, 0x00 }, + { ADAU1373_RSPK_MIX, 0x00 }, + { ADAU1373_LHP_MIX, 0x00 }, + { ADAU1373_RHP_MIX, 0x00 }, + { ADAU1373_EP_MIX, 0x00 }, + { ADAU1373_HP_CTRL, 0x00 }, + { ADAU1373_HP_CTRL2, 0x00 }, + { ADAU1373_LS_CTRL, 0x00 }, + { ADAU1373_EP_CTRL, 0x00 }, + { ADAU1373_MICBIAS_CTRL1, 0x00 }, + { ADAU1373_MICBIAS_CTRL2, 0x00 }, + { ADAU1373_OUTPUT_CTRL, 0x00 }, + { ADAU1373_PWDN_CTRL1, 0x00 }, + { ADAU1373_PWDN_CTRL2, 0x00 }, + { ADAU1373_PWDN_CTRL3, 0x00 }, + { ADAU1373_DPLL_CTRL(0), 0x00 }, + { ADAU1373_PLL_CTRL1(0), 0x00 }, + { ADAU1373_PLL_CTRL2(0), 0x00 }, + { ADAU1373_PLL_CTRL3(0), 0x00 }, + { ADAU1373_PLL_CTRL4(0), 0x00 }, + { ADAU1373_PLL_CTRL5(0), 0x00 }, + { ADAU1373_PLL_CTRL6(0), 0x02 }, + { ADAU1373_DPLL_CTRL(1), 0x00 }, + { ADAU1373_PLL_CTRL1(1), 0x00 }, + { ADAU1373_PLL_CTRL2(1), 0x00 }, + { ADAU1373_PLL_CTRL3(1), 0x00 }, + { ADAU1373_PLL_CTRL4(1), 0x00 }, + { ADAU1373_PLL_CTRL5(1), 0x00 }, + { ADAU1373_PLL_CTRL6(1), 0x02 }, + { ADAU1373_HEADDECT, 0x00 }, + { ADAU1373_ADC_CTRL, 0x00 }, + { ADAU1373_CLK_SRC_DIV(0), 0x00 }, + { ADAU1373_CLK_SRC_DIV(1), 0x00 }, + { ADAU1373_DAI(0), 0x0a }, + { ADAU1373_DAI(1), 0x0a }, + { ADAU1373_DAI(2), 0x0a }, + { ADAU1373_BCLKDIV(0), 0x00 }, + { ADAU1373_BCLKDIV(1), 0x00 }, + { ADAU1373_BCLKDIV(2), 0x00 }, + { ADAU1373_SRC_RATIOA(0), 0x00 }, + { ADAU1373_SRC_RATIOB(0), 0x00 }, + { ADAU1373_SRC_RATIOA(1), 0x00 }, + { ADAU1373_SRC_RATIOB(1), 0x00 }, + { ADAU1373_SRC_RATIOA(2), 0x00 }, + { ADAU1373_SRC_RATIOB(2), 0x00 }, + { ADAU1373_DEEMP_CTRL, 0x00 }, + { ADAU1373_SRC_DAI_CTRL(0), 0x08 }, + { ADAU1373_SRC_DAI_CTRL(1), 0x08 }, + { ADAU1373_SRC_DAI_CTRL(2), 0x08 }, + { ADAU1373_DIN_MIX_CTRL(0), 0x00 }, + { ADAU1373_DIN_MIX_CTRL(1), 0x00 }, + { ADAU1373_DIN_MIX_CTRL(2), 0x00 }, + { ADAU1373_DIN_MIX_CTRL(3), 0x00 }, + { ADAU1373_DIN_MIX_CTRL(4), 0x00 }, + { ADAU1373_DOUT_MIX_CTRL(0), 0x00 }, + { ADAU1373_DOUT_MIX_CTRL(1), 0x00 }, + { ADAU1373_DOUT_MIX_CTRL(2), 0x00 }, + { ADAU1373_DOUT_MIX_CTRL(3), 0x00 }, + { ADAU1373_DOUT_MIX_CTRL(4), 0x00 }, + { ADAU1373_DAI_PBL_VOL(0), 0x00 }, + { ADAU1373_DAI_PBR_VOL(0), 0x00 }, + { ADAU1373_DAI_PBL_VOL(1), 0x00 }, + { ADAU1373_DAI_PBR_VOL(1), 0x00 }, + { ADAU1373_DAI_PBL_VOL(2), 0x00 }, + { ADAU1373_DAI_PBR_VOL(2), 0x00 }, + { ADAU1373_DAI_RECL_VOL(0), 0x00 }, + { ADAU1373_DAI_RECR_VOL(0), 0x00 }, + { ADAU1373_DAI_RECL_VOL(1), 0x00 }, + { ADAU1373_DAI_RECR_VOL(1), 0x00 }, + { ADAU1373_DAI_RECL_VOL(2), 0x00 }, + { ADAU1373_DAI_RECR_VOL(2), 0x00 }, + { ADAU1373_DAC1_PBL_VOL, 0x00 }, + { ADAU1373_DAC1_PBR_VOL, 0x00 }, + { ADAU1373_DAC2_PBL_VOL, 0x00 }, + { ADAU1373_DAC2_PBR_VOL, 0x00 }, + { ADAU1373_ADC_RECL_VOL, 0x00 }, + { ADAU1373_ADC_RECR_VOL, 0x00 }, + { ADAU1373_DMIC_RECL_VOL, 0x00 }, + { ADAU1373_DMIC_RECR_VOL, 0x00 }, + { ADAU1373_VOL_GAIN1, 0x00 }, + { ADAU1373_VOL_GAIN2, 0x00 }, + { ADAU1373_VOL_GAIN3, 0x00 }, + { ADAU1373_HPF_CTRL, 0x00 }, + { ADAU1373_BASS1, 0x00 }, + { ADAU1373_BASS2, 0x00 }, + { ADAU1373_DRC(0) + 0x0, 0x78 }, + { ADAU1373_DRC(0) + 0x1, 0x18 }, + { ADAU1373_DRC(0) + 0x2, 0x00 }, + { ADAU1373_DRC(0) + 0x3, 0x00 }, + { ADAU1373_DRC(0) + 0x4, 0x00 }, + { ADAU1373_DRC(0) + 0x5, 0xc0 }, + { ADAU1373_DRC(0) + 0x6, 0x00 }, + { ADAU1373_DRC(0) + 0x7, 0x00 }, + { ADAU1373_DRC(0) + 0x8, 0x00 }, + { ADAU1373_DRC(0) + 0x9, 0xc0 }, + { ADAU1373_DRC(0) + 0xa, 0x88 }, + { ADAU1373_DRC(0) + 0xb, 0x7a }, + { ADAU1373_DRC(0) + 0xc, 0xdf }, + { ADAU1373_DRC(0) + 0xd, 0x20 }, + { ADAU1373_DRC(0) + 0xe, 0x00 }, + { ADAU1373_DRC(0) + 0xf, 0x00 }, + { ADAU1373_DRC(1) + 0x0, 0x78 }, + { ADAU1373_DRC(1) + 0x1, 0x18 }, + { ADAU1373_DRC(1) + 0x2, 0x00 }, + { ADAU1373_DRC(1) + 0x3, 0x00 }, + { ADAU1373_DRC(1) + 0x4, 0x00 }, + { ADAU1373_DRC(1) + 0x5, 0xc0 }, + { ADAU1373_DRC(1) + 0x6, 0x00 }, + { ADAU1373_DRC(1) + 0x7, 0x00 }, + { ADAU1373_DRC(1) + 0x8, 0x00 }, + { ADAU1373_DRC(1) + 0x9, 0xc0 }, + { ADAU1373_DRC(1) + 0xa, 0x88 }, + { ADAU1373_DRC(1) + 0xb, 0x7a }, + { ADAU1373_DRC(1) + 0xc, 0xdf }, + { ADAU1373_DRC(1) + 0xd, 0x20 }, + { ADAU1373_DRC(1) + 0xe, 0x00 }, + { ADAU1373_DRC(1) + 0xf, 0x00 }, + { ADAU1373_DRC(2) + 0x0, 0x78 }, + { ADAU1373_DRC(2) + 0x1, 0x18 }, + { ADAU1373_DRC(2) + 0x2, 0x00 }, + { ADAU1373_DRC(2) + 0x3, 0x00 }, + { ADAU1373_DRC(2) + 0x4, 0x00 }, + { ADAU1373_DRC(2) + 0x5, 0xc0 }, + { ADAU1373_DRC(2) + 0x6, 0x00 }, + { ADAU1373_DRC(2) + 0x7, 0x00 }, + { ADAU1373_DRC(2) + 0x8, 0x00 }, + { ADAU1373_DRC(2) + 0x9, 0xc0 }, + { ADAU1373_DRC(2) + 0xa, 0x88 }, + { ADAU1373_DRC(2) + 0xb, 0x7a }, + { ADAU1373_DRC(2) + 0xc, 0xdf }, + { ADAU1373_DRC(2) + 0xd, 0x20 }, + { ADAU1373_DRC(2) + 0xe, 0x00 }, + { ADAU1373_DRC(2) + 0xf, 0x00 }, + { ADAU1373_3D_CTRL1, 0x00 }, + { ADAU1373_3D_CTRL2, 0x00 }, + { ADAU1373_FDSP_SEL1, 0x00 }, + { ADAU1373_FDSP_SEL2, 0x00 }, + { ADAU1373_FDSP_SEL2, 0x00 }, + { ADAU1373_FDSP_SEL4, 0x00 }, + { ADAU1373_DIGMICCTRL, 0x00 }, + { ADAU1373_DIGEN, 0x00 }, +}; + +static const DECLARE_TLV_DB_RANGE(adau1373_out_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1), + 8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0), + 16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0), + 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0) +); + +static const DECLARE_TLV_DB_MINMAX(adau1373_digital_tlv, -9563, 0); +static const DECLARE_TLV_DB_SCALE(adau1373_in_pga_tlv, -1300, 100, 1); +static const DECLARE_TLV_DB_SCALE(adau1373_ep_tlv, -600, 600, 1); + +static const DECLARE_TLV_DB_SCALE(adau1373_input_boost_tlv, 0, 2000, 0); +static const DECLARE_TLV_DB_SCALE(adau1373_gain_boost_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(adau1373_speaker_boost_tlv, 1200, 600, 0); + +static const char *adau1373_fdsp_sel_text[] = { + "None", + "Channel 1", + "Channel 2", + "Channel 3", + "Channel 4", + "Channel 5", +}; + +static SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum, + ADAU1373_FDSP_SEL1, 4, adau1373_fdsp_sel_text); +static SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum, + ADAU1373_FDSP_SEL1, 0, adau1373_fdsp_sel_text); +static SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum, + ADAU1373_FDSP_SEL2, 0, adau1373_fdsp_sel_text); +static SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum, + ADAU1373_FDSP_SEL3, 0, adau1373_fdsp_sel_text); +static SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum, + ADAU1373_FDSP_SEL4, 4, adau1373_fdsp_sel_text); + +static const char *adau1373_hpf_cutoff_text[] = { + "3.7Hz", "50Hz", "100Hz", "150Hz", "200Hz", "250Hz", "300Hz", "350Hz", + "400Hz", "450Hz", "500Hz", "550Hz", "600Hz", "650Hz", "700Hz", "750Hz", + "800Hz", +}; + +static SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum, + ADAU1373_HPF_CTRL, 3, adau1373_hpf_cutoff_text); + +static const char *adau1373_bass_lpf_cutoff_text[] = { + "801Hz", "1001Hz", +}; + +static const char *adau1373_bass_clip_level_text[] = { + "0.125", "0.250", "0.370", "0.500", "0.625", "0.750", "0.875", +}; + +static const unsigned int adau1373_bass_clip_level_values[] = { + 1, 2, 3, 4, 5, 6, 7, +}; + +static const char *adau1373_bass_hpf_cutoff_text[] = { + "158Hz", "232Hz", "347Hz", "520Hz", +}; + +static const DECLARE_TLV_DB_RANGE(adau1373_bass_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-600, 600, 1), + 3, 4, TLV_DB_SCALE_ITEM(950, 250, 0), + 5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0) +); + +static SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum, + ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text); + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum, + ADAU1373_BASS1, 2, 7, adau1373_bass_clip_level_text, + adau1373_bass_clip_level_values); + +static SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum, + ADAU1373_BASS1, 0, adau1373_bass_hpf_cutoff_text); + +static const char *adau1373_3d_level_text[] = { + "0%", "6.67%", "13.33%", "20%", "26.67%", "33.33%", + "40%", "46.67%", "53.33%", "60%", "66.67%", "73.33%", + "80%", "86.67", "99.33%", "100%" +}; + +static const char *adau1373_3d_cutoff_text[] = { + "No 3D", "0.03125 fs", "0.04583 fs", "0.075 fs", "0.11458 fs", + "0.16875 fs", "0.27083 fs" +}; + +static SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum, + ADAU1373_3D_CTRL1, 4, adau1373_3d_level_text); +static SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum, + ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text); + +static const DECLARE_TLV_DB_RANGE(adau1373_3d_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 7, TLV_DB_LINEAR_ITEM(-1800, -120) +); + +static const char *adau1373_lr_mux_text[] = { + "Mute", + "Right Channel (L+R)", + "Left Channel (L+R)", + "Stereo", +}; + +static SOC_ENUM_SINGLE_DECL(adau1373_lineout1_lr_mux_enum, + ADAU1373_OUTPUT_CTRL, 4, adau1373_lr_mux_text); +static SOC_ENUM_SINGLE_DECL(adau1373_lineout2_lr_mux_enum, + ADAU1373_OUTPUT_CTRL, 6, adau1373_lr_mux_text); +static SOC_ENUM_SINGLE_DECL(adau1373_speaker_lr_mux_enum, + ADAU1373_LS_CTRL, 4, adau1373_lr_mux_text); + +static const struct snd_kcontrol_new adau1373_controls[] = { + SOC_DOUBLE_R_TLV("AIF1 Capture Volume", ADAU1373_DAI_RECL_VOL(0), + ADAU1373_DAI_RECR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv), + SOC_DOUBLE_R_TLV("AIF2 Capture Volume", ADAU1373_DAI_RECL_VOL(1), + ADAU1373_DAI_RECR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv), + SOC_DOUBLE_R_TLV("AIF3 Capture Volume", ADAU1373_DAI_RECL_VOL(2), + ADAU1373_DAI_RECR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv), + + SOC_DOUBLE_R_TLV("ADC Capture Volume", ADAU1373_ADC_RECL_VOL, + ADAU1373_ADC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv), + SOC_DOUBLE_R_TLV("DMIC Capture Volume", ADAU1373_DMIC_RECL_VOL, + ADAU1373_DMIC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv), + + SOC_DOUBLE_R_TLV("AIF1 Playback Volume", ADAU1373_DAI_PBL_VOL(0), + ADAU1373_DAI_PBR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv), + SOC_DOUBLE_R_TLV("AIF2 Playback Volume", ADAU1373_DAI_PBL_VOL(1), + ADAU1373_DAI_PBR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv), + SOC_DOUBLE_R_TLV("AIF3 Playback Volume", ADAU1373_DAI_PBL_VOL(2), + ADAU1373_DAI_PBR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv), + + SOC_DOUBLE_R_TLV("DAC1 Playback Volume", ADAU1373_DAC1_PBL_VOL, + ADAU1373_DAC1_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv), + SOC_DOUBLE_R_TLV("DAC2 Playback Volume", ADAU1373_DAC2_PBL_VOL, + ADAU1373_DAC2_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv), + + SOC_DOUBLE_R_TLV("Lineout1 Playback Volume", ADAU1373_LLINE_OUT(0), + ADAU1373_RLINE_OUT(0), 0, 0x1f, 0, adau1373_out_tlv), + SOC_DOUBLE_R_TLV("Speaker Playback Volume", ADAU1373_LSPK_OUT, + ADAU1373_RSPK_OUT, 0, 0x1f, 0, adau1373_out_tlv), + SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1373_LHP_OUT, + ADAU1373_RHP_OUT, 0, 0x1f, 0, adau1373_out_tlv), + + SOC_DOUBLE_R_TLV("Input 1 Capture Volume", ADAU1373_AINL_CTRL(0), + ADAU1373_AINR_CTRL(0), 0, 0x1f, 0, adau1373_in_pga_tlv), + SOC_DOUBLE_R_TLV("Input 2 Capture Volume", ADAU1373_AINL_CTRL(1), + ADAU1373_AINR_CTRL(1), 0, 0x1f, 0, adau1373_in_pga_tlv), + SOC_DOUBLE_R_TLV("Input 3 Capture Volume", ADAU1373_AINL_CTRL(2), + ADAU1373_AINR_CTRL(2), 0, 0x1f, 0, adau1373_in_pga_tlv), + SOC_DOUBLE_R_TLV("Input 4 Capture Volume", ADAU1373_AINL_CTRL(3), + ADAU1373_AINR_CTRL(3), 0, 0x1f, 0, adau1373_in_pga_tlv), + + SOC_SINGLE_TLV("Earpiece Playback Volume", ADAU1373_EP_CTRL, 0, 3, 0, + adau1373_ep_tlv), + + SOC_DOUBLE_TLV("AIF3 Boost Playback Volume", ADAU1373_VOL_GAIN1, 4, 5, + 1, 0, adau1373_gain_boost_tlv), + SOC_DOUBLE_TLV("AIF2 Boost Playback Volume", ADAU1373_VOL_GAIN1, 2, 3, + 1, 0, adau1373_gain_boost_tlv), + SOC_DOUBLE_TLV("AIF1 Boost Playback Volume", ADAU1373_VOL_GAIN1, 0, 1, + 1, 0, adau1373_gain_boost_tlv), + SOC_DOUBLE_TLV("AIF3 Boost Capture Volume", ADAU1373_VOL_GAIN2, 4, 5, + 1, 0, adau1373_gain_boost_tlv), + SOC_DOUBLE_TLV("AIF2 Boost Capture Volume", ADAU1373_VOL_GAIN2, 2, 3, + 1, 0, adau1373_gain_boost_tlv), + SOC_DOUBLE_TLV("AIF1 Boost Capture Volume", ADAU1373_VOL_GAIN2, 0, 1, + 1, 0, adau1373_gain_boost_tlv), + SOC_DOUBLE_TLV("DMIC Boost Capture Volume", ADAU1373_VOL_GAIN3, 6, 7, + 1, 0, adau1373_gain_boost_tlv), + SOC_DOUBLE_TLV("ADC Boost Capture Volume", ADAU1373_VOL_GAIN3, 4, 5, + 1, 0, adau1373_gain_boost_tlv), + SOC_DOUBLE_TLV("DAC2 Boost Playback Volume", ADAU1373_VOL_GAIN3, 2, 3, + 1, 0, adau1373_gain_boost_tlv), + SOC_DOUBLE_TLV("DAC1 Boost Playback Volume", ADAU1373_VOL_GAIN3, 0, 1, + 1, 0, adau1373_gain_boost_tlv), + + SOC_DOUBLE_TLV("Input 1 Boost Capture Volume", ADAU1373_ADC_GAIN, 0, 4, + 1, 0, adau1373_input_boost_tlv), + SOC_DOUBLE_TLV("Input 2 Boost Capture Volume", ADAU1373_ADC_GAIN, 1, 5, + 1, 0, adau1373_input_boost_tlv), + SOC_DOUBLE_TLV("Input 3 Boost Capture Volume", ADAU1373_ADC_GAIN, 2, 6, + 1, 0, adau1373_input_boost_tlv), + SOC_DOUBLE_TLV("Input 4 Boost Capture Volume", ADAU1373_ADC_GAIN, 3, 7, + 1, 0, adau1373_input_boost_tlv), + + SOC_DOUBLE_TLV("Speaker Boost Playback Volume", ADAU1373_LS_CTRL, 2, 3, + 1, 0, adau1373_speaker_boost_tlv), + + SOC_ENUM("Lineout1 LR Mux", adau1373_lineout1_lr_mux_enum), + SOC_ENUM("Speaker LR Mux", adau1373_speaker_lr_mux_enum), + + SOC_ENUM("HPF Cutoff", adau1373_hpf_cutoff_enum), + SOC_DOUBLE("HPF Switch", ADAU1373_HPF_CTRL, 1, 0, 1, 0), + SOC_ENUM("HPF Channel", adau1373_hpf_channel_enum), + + SOC_ENUM("Bass HPF Cutoff", adau1373_bass_hpf_cutoff_enum), + SOC_ENUM("Bass Clip Level Threshold", adau1373_bass_clip_level_enum), + SOC_ENUM("Bass LPF Cutoff", adau1373_bass_lpf_cutoff_enum), + SOC_DOUBLE("Bass Playback Switch", ADAU1373_BASS2, 0, 1, 1, 0), + SOC_SINGLE_TLV("Bass Playback Volume", ADAU1373_BASS2, 2, 7, 0, + adau1373_bass_tlv), + SOC_ENUM("Bass Channel", adau1373_bass_channel_enum), + + SOC_ENUM("3D Freq", adau1373_3d_cutoff_enum), + SOC_ENUM("3D Level", adau1373_3d_level_enum), + SOC_SINGLE("3D Playback Switch", ADAU1373_3D_CTRL2, 0, 1, 0), + SOC_SINGLE_TLV("3D Playback Volume", ADAU1373_3D_CTRL2, 2, 7, 0, + adau1373_3d_tlv), + SOC_ENUM("3D Channel", adau1373_bass_channel_enum), + + SOC_SINGLE("Zero Cross Switch", ADAU1373_PWDN_CTRL3, 7, 1, 0), +}; + +static const struct snd_kcontrol_new adau1373_lineout2_controls[] = { + SOC_DOUBLE_R_TLV("Lineout2 Playback Volume", ADAU1373_LLINE_OUT(1), + ADAU1373_RLINE_OUT(1), 0, 0x1f, 0, adau1373_out_tlv), + SOC_ENUM("Lineout2 LR Mux", adau1373_lineout2_lr_mux_enum), +}; + +static const struct snd_kcontrol_new adau1373_drc_controls[] = { + SOC_ENUM("DRC1 Channel", adau1373_drc1_channel_enum), + SOC_ENUM("DRC2 Channel", adau1373_drc2_channel_enum), + SOC_ENUM("DRC3 Channel", adau1373_drc3_channel_enum), +}; + +static int adau1373_pll_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component); + unsigned int pll_id = w->name[3] - '1'; + unsigned int val; + + if (SND_SOC_DAPM_EVENT_ON(event)) + val = ADAU1373_PLL_CTRL6_PLL_EN; + else + val = 0; + + regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id), + ADAU1373_PLL_CTRL6_PLL_EN, val); + + if (SND_SOC_DAPM_EVENT_ON(event)) + mdelay(5); + + return 0; +} + +static const char *adau1373_decimator_text[] = { + "ADC", + "DMIC1", +}; + +static SOC_ENUM_SINGLE_VIRT_DECL(adau1373_decimator_enum, + adau1373_decimator_text); + +static const struct snd_kcontrol_new adau1373_decimator_mux = + SOC_DAPM_ENUM("Decimator Mux", adau1373_decimator_enum); + +static const struct snd_kcontrol_new adau1373_left_adc_mixer_controls[] = { + SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_LADC_MIXER, 4, 1, 0), + SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_LADC_MIXER, 3, 1, 0), + SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_LADC_MIXER, 2, 1, 0), + SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_LADC_MIXER, 1, 1, 0), + SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_LADC_MIXER, 0, 1, 0), +}; + +static const struct snd_kcontrol_new adau1373_right_adc_mixer_controls[] = { + SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_RADC_MIXER, 4, 1, 0), + SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_RADC_MIXER, 3, 1, 0), + SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_RADC_MIXER, 2, 1, 0), + SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_RADC_MIXER, 1, 1, 0), + SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_RADC_MIXER, 0, 1, 0), +}; + +#define DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(_name, _reg) \ +const struct snd_kcontrol_new _name[] = { \ + SOC_DAPM_SINGLE("Left DAC2 Switch", _reg, 7, 1, 0), \ + SOC_DAPM_SINGLE("Right DAC2 Switch", _reg, 6, 1, 0), \ + SOC_DAPM_SINGLE("Left DAC1 Switch", _reg, 5, 1, 0), \ + SOC_DAPM_SINGLE("Right DAC1 Switch", _reg, 4, 1, 0), \ + SOC_DAPM_SINGLE("Input 4 Bypass Switch", _reg, 3, 1, 0), \ + SOC_DAPM_SINGLE("Input 3 Bypass Switch", _reg, 2, 1, 0), \ + SOC_DAPM_SINGLE("Input 2 Bypass Switch", _reg, 1, 1, 0), \ + SOC_DAPM_SINGLE("Input 1 Bypass Switch", _reg, 0, 1, 0), \ +} + +static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line1_mixer_controls, + ADAU1373_LLINE1_MIX); +static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line1_mixer_controls, + ADAU1373_RLINE1_MIX); +static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line2_mixer_controls, + ADAU1373_LLINE2_MIX); +static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line2_mixer_controls, + ADAU1373_RLINE2_MIX); +static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_spk_mixer_controls, + ADAU1373_LSPK_MIX); +static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_spk_mixer_controls, + ADAU1373_RSPK_MIX); +static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_ep_mixer_controls, + ADAU1373_EP_MIX); + +static const struct snd_kcontrol_new adau1373_left_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", ADAU1373_LHP_MIX, 5, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", ADAU1373_LHP_MIX, 4, 1, 0), + SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_LHP_MIX, 3, 1, 0), + SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_LHP_MIX, 2, 1, 0), + SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_LHP_MIX, 1, 1, 0), + SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_LHP_MIX, 0, 1, 0), +}; + +static const struct snd_kcontrol_new adau1373_right_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Right DAC1 Switch", ADAU1373_RHP_MIX, 5, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", ADAU1373_RHP_MIX, 4, 1, 0), + SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_RHP_MIX, 3, 1, 0), + SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_RHP_MIX, 2, 1, 0), + SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_RHP_MIX, 1, 1, 0), + SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_RHP_MIX, 0, 1, 0), +}; + +#define DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(_name, _reg) \ +const struct snd_kcontrol_new _name[] = { \ + SOC_DAPM_SINGLE("DMIC2 Swapped Switch", _reg, 6, 1, 0), \ + SOC_DAPM_SINGLE("DMIC2 Switch", _reg, 5, 1, 0), \ + SOC_DAPM_SINGLE("ADC/DMIC1 Swapped Switch", _reg, 4, 1, 0), \ + SOC_DAPM_SINGLE("ADC/DMIC1 Switch", _reg, 3, 1, 0), \ + SOC_DAPM_SINGLE("AIF3 Switch", _reg, 2, 1, 0), \ + SOC_DAPM_SINGLE("AIF2 Switch", _reg, 1, 1, 0), \ + SOC_DAPM_SINGLE("AIF1 Switch", _reg, 0, 1, 0), \ +} + +static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel1_mixer_controls, + ADAU1373_DIN_MIX_CTRL(0)); +static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel2_mixer_controls, + ADAU1373_DIN_MIX_CTRL(1)); +static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel3_mixer_controls, + ADAU1373_DIN_MIX_CTRL(2)); +static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel4_mixer_controls, + ADAU1373_DIN_MIX_CTRL(3)); +static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel5_mixer_controls, + ADAU1373_DIN_MIX_CTRL(4)); + +#define DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(_name, _reg) \ +const struct snd_kcontrol_new _name[] = { \ + SOC_DAPM_SINGLE("DSP Channel5 Switch", _reg, 4, 1, 0), \ + SOC_DAPM_SINGLE("DSP Channel4 Switch", _reg, 3, 1, 0), \ + SOC_DAPM_SINGLE("DSP Channel3 Switch", _reg, 2, 1, 0), \ + SOC_DAPM_SINGLE("DSP Channel2 Switch", _reg, 1, 1, 0), \ + SOC_DAPM_SINGLE("DSP Channel1 Switch", _reg, 0, 1, 0), \ +} + +static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif1_mixer_controls, + ADAU1373_DOUT_MIX_CTRL(0)); +static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif2_mixer_controls, + ADAU1373_DOUT_MIX_CTRL(1)); +static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif3_mixer_controls, + ADAU1373_DOUT_MIX_CTRL(2)); +static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac1_mixer_controls, + ADAU1373_DOUT_MIX_CTRL(3)); +static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac2_mixer_controls, + ADAU1373_DOUT_MIX_CTRL(4)); + +static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = { + /* Datasheet claims Left ADC is bit 6 and Right ADC is bit 7, but that + * doesn't seem to be the case. */ + SND_SOC_DAPM_ADC("Left ADC", NULL, ADAU1373_PWDN_CTRL1, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", NULL, ADAU1373_PWDN_CTRL1, 6, 0), + + SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1373_DIGMICCTRL, 0, 0), + SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1373_DIGMICCTRL, 2, 0), + + SND_SOC_DAPM_MUX("Decimator Mux", SND_SOC_NOPM, 0, 0, + &adau1373_decimator_mux), + + SND_SOC_DAPM_SUPPLY("MICBIAS2", ADAU1373_PWDN_CTRL1, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1373_PWDN_CTRL1, 4, 0, NULL, 0), + + SND_SOC_DAPM_PGA("IN4PGA", ADAU1373_PWDN_CTRL1, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("IN3PGA", ADAU1373_PWDN_CTRL1, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("IN2PGA", ADAU1373_PWDN_CTRL1, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("IN1PGA", ADAU1373_PWDN_CTRL1, 0, 0, NULL, 0), + + SND_SOC_DAPM_DAC("Left DAC2", NULL, ADAU1373_PWDN_CTRL2, 7, 0), + SND_SOC_DAPM_DAC("Right DAC2", NULL, ADAU1373_PWDN_CTRL2, 6, 0), + SND_SOC_DAPM_DAC("Left DAC1", NULL, ADAU1373_PWDN_CTRL2, 5, 0), + SND_SOC_DAPM_DAC("Right DAC1", NULL, ADAU1373_PWDN_CTRL2, 4, 0), + + SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0, + adau1373_left_adc_mixer_controls), + SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0, + adau1373_right_adc_mixer_controls), + + SOC_MIXER_ARRAY("Left Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 3, 0, + adau1373_left_line2_mixer_controls), + SOC_MIXER_ARRAY("Right Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 2, 0, + adau1373_right_line2_mixer_controls), + SOC_MIXER_ARRAY("Left Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 1, 0, + adau1373_left_line1_mixer_controls), + SOC_MIXER_ARRAY("Right Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 0, 0, + adau1373_right_line1_mixer_controls), + + SOC_MIXER_ARRAY("Earpiece Mixer", ADAU1373_PWDN_CTRL3, 4, 0, + adau1373_ep_mixer_controls), + SOC_MIXER_ARRAY("Left Speaker Mixer", ADAU1373_PWDN_CTRL3, 3, 0, + adau1373_left_spk_mixer_controls), + SOC_MIXER_ARRAY("Right Speaker Mixer", ADAU1373_PWDN_CTRL3, 2, 0, + adau1373_right_spk_mixer_controls), + SOC_MIXER_ARRAY("Left Headphone Mixer", SND_SOC_NOPM, 0, 0, + adau1373_left_hp_mixer_controls), + SOC_MIXER_ARRAY("Right Headphone Mixer", SND_SOC_NOPM, 0, 0, + adau1373_right_hp_mixer_controls), + SND_SOC_DAPM_SUPPLY("Headphone Enable", ADAU1373_PWDN_CTRL3, 1, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY("AIF1 CLK", ADAU1373_SRC_DAI_CTRL(0), 0, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF2 CLK", ADAU1373_SRC_DAI_CTRL(1), 0, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF3 CLK", ADAU1373_SRC_DAI_CTRL(2), 0, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF1 IN SRC", ADAU1373_SRC_DAI_CTRL(0), 2, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF1 OUT SRC", ADAU1373_SRC_DAI_CTRL(0), 1, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF2 IN SRC", ADAU1373_SRC_DAI_CTRL(1), 2, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF2 OUT SRC", ADAU1373_SRC_DAI_CTRL(1), 1, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF3 IN SRC", ADAU1373_SRC_DAI_CTRL(2), 2, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF3 OUT SRC", ADAU1373_SRC_DAI_CTRL(2), 1, 0, + NULL, 0), + + SND_SOC_DAPM_AIF_IN("AIF1 IN", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1 OUT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2 IN", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2 OUT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF3 IN", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF3 OUT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0), + + SOC_MIXER_ARRAY("DSP Channel1 Mixer", SND_SOC_NOPM, 0, 0, + adau1373_dsp_channel1_mixer_controls), + SOC_MIXER_ARRAY("DSP Channel2 Mixer", SND_SOC_NOPM, 0, 0, + adau1373_dsp_channel2_mixer_controls), + SOC_MIXER_ARRAY("DSP Channel3 Mixer", SND_SOC_NOPM, 0, 0, + adau1373_dsp_channel3_mixer_controls), + SOC_MIXER_ARRAY("DSP Channel4 Mixer", SND_SOC_NOPM, 0, 0, + adau1373_dsp_channel4_mixer_controls), + SOC_MIXER_ARRAY("DSP Channel5 Mixer", SND_SOC_NOPM, 0, 0, + adau1373_dsp_channel5_mixer_controls), + + SOC_MIXER_ARRAY("AIF1 Mixer", SND_SOC_NOPM, 0, 0, + adau1373_aif1_mixer_controls), + SOC_MIXER_ARRAY("AIF2 Mixer", SND_SOC_NOPM, 0, 0, + adau1373_aif2_mixer_controls), + SOC_MIXER_ARRAY("AIF3 Mixer", SND_SOC_NOPM, 0, 0, + adau1373_aif3_mixer_controls), + SOC_MIXER_ARRAY("DAC1 Mixer", SND_SOC_NOPM, 0, 0, + adau1373_dac1_mixer_controls), + SOC_MIXER_ARRAY("DAC2 Mixer", SND_SOC_NOPM, 0, 0, + adau1373_dac2_mixer_controls), + + SND_SOC_DAPM_SUPPLY("DSP", ADAU1373_DIGEN, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Recording Engine B", ADAU1373_DIGEN, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Recording Engine A", ADAU1373_DIGEN, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Playback Engine B", ADAU1373_DIGEN, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Playback Engine A", ADAU1373_DIGEN, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PLL1", SND_SOC_NOPM, 0, 0, adau1373_pll_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("PLL2", SND_SOC_NOPM, 0, 0, adau1373_pll_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SYSCLK1", ADAU1373_CLK_SRC_DIV(0), 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SYSCLK2", ADAU1373_CLK_SRC_DIV(1), 7, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN2R"), + SND_SOC_DAPM_INPUT("AIN3L"), + SND_SOC_DAPM_INPUT("AIN3R"), + SND_SOC_DAPM_INPUT("AIN4L"), + SND_SOC_DAPM_INPUT("AIN4R"), + + SND_SOC_DAPM_INPUT("DMIC1DAT"), + SND_SOC_DAPM_INPUT("DMIC2DAT"), + + SND_SOC_DAPM_OUTPUT("LOUT1L"), + SND_SOC_DAPM_OUTPUT("LOUT1R"), + SND_SOC_DAPM_OUTPUT("LOUT2L"), + SND_SOC_DAPM_OUTPUT("LOUT2R"), + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("EP"), +}; + +static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component); + unsigned int dai; + const char *clk; + + dai = sink->name[3] - '1'; + + if (!adau1373->dais[dai].clock_provider) + return 0; + + if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1) + clk = "SYSCLK1"; + else + clk = "SYSCLK2"; + + return strcmp(source->name, clk) == 0; +} + +static int adau1373_check_src(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component); + unsigned int dai; + + dai = sink->name[3] - '1'; + + return adau1373->dais[dai].enable_src; +} + +#define DSP_CHANNEL_MIXER_ROUTES(_sink) \ + { _sink, "DMIC2 Swapped Switch", "DMIC2" }, \ + { _sink, "DMIC2 Switch", "DMIC2" }, \ + { _sink, "ADC/DMIC1 Swapped Switch", "Decimator Mux" }, \ + { _sink, "ADC/DMIC1 Switch", "Decimator Mux" }, \ + { _sink, "AIF1 Switch", "AIF1 IN" }, \ + { _sink, "AIF2 Switch", "AIF2 IN" }, \ + { _sink, "AIF3 Switch", "AIF3 IN" } + +#define DSP_OUTPUT_MIXER_ROUTES(_sink) \ + { _sink, "DSP Channel1 Switch", "DSP Channel1 Mixer" }, \ + { _sink, "DSP Channel2 Switch", "DSP Channel2 Mixer" }, \ + { _sink, "DSP Channel3 Switch", "DSP Channel3 Mixer" }, \ + { _sink, "DSP Channel4 Switch", "DSP Channel4 Mixer" }, \ + { _sink, "DSP Channel5 Switch", "DSP Channel5 Mixer" } + +#define LEFT_OUTPUT_MIXER_ROUTES(_sink) \ + { _sink, "Right DAC2 Switch", "Right DAC2" }, \ + { _sink, "Left DAC2 Switch", "Left DAC2" }, \ + { _sink, "Right DAC1 Switch", "Right DAC1" }, \ + { _sink, "Left DAC1 Switch", "Left DAC1" }, \ + { _sink, "Input 1 Bypass Switch", "IN1PGA" }, \ + { _sink, "Input 2 Bypass Switch", "IN2PGA" }, \ + { _sink, "Input 3 Bypass Switch", "IN3PGA" }, \ + { _sink, "Input 4 Bypass Switch", "IN4PGA" } + +#define RIGHT_OUTPUT_MIXER_ROUTES(_sink) \ + { _sink, "Right DAC2 Switch", "Right DAC2" }, \ + { _sink, "Left DAC2 Switch", "Left DAC2" }, \ + { _sink, "Right DAC1 Switch", "Right DAC1" }, \ + { _sink, "Left DAC1 Switch", "Left DAC1" }, \ + { _sink, "Input 1 Bypass Switch", "IN1PGA" }, \ + { _sink, "Input 2 Bypass Switch", "IN2PGA" }, \ + { _sink, "Input 3 Bypass Switch", "IN3PGA" }, \ + { _sink, "Input 4 Bypass Switch", "IN4PGA" } + +static const struct snd_soc_dapm_route adau1373_dapm_routes[] = { + { "Left ADC Mixer", "DAC1 Switch", "Left DAC1" }, + { "Left ADC Mixer", "Input 1 Switch", "IN1PGA" }, + { "Left ADC Mixer", "Input 2 Switch", "IN2PGA" }, + { "Left ADC Mixer", "Input 3 Switch", "IN3PGA" }, + { "Left ADC Mixer", "Input 4 Switch", "IN4PGA" }, + + { "Right ADC Mixer", "DAC1 Switch", "Right DAC1" }, + { "Right ADC Mixer", "Input 1 Switch", "IN1PGA" }, + { "Right ADC Mixer", "Input 2 Switch", "IN2PGA" }, + { "Right ADC Mixer", "Input 3 Switch", "IN3PGA" }, + { "Right ADC Mixer", "Input 4 Switch", "IN4PGA" }, + + { "Left ADC", NULL, "Left ADC Mixer" }, + { "Right ADC", NULL, "Right ADC Mixer" }, + + { "Decimator Mux", "ADC", "Left ADC" }, + { "Decimator Mux", "ADC", "Right ADC" }, + { "Decimator Mux", "DMIC1", "DMIC1" }, + + DSP_CHANNEL_MIXER_ROUTES("DSP Channel1 Mixer"), + DSP_CHANNEL_MIXER_ROUTES("DSP Channel2 Mixer"), + DSP_CHANNEL_MIXER_ROUTES("DSP Channel3 Mixer"), + DSP_CHANNEL_MIXER_ROUTES("DSP Channel4 Mixer"), + DSP_CHANNEL_MIXER_ROUTES("DSP Channel5 Mixer"), + + DSP_OUTPUT_MIXER_ROUTES("AIF1 Mixer"), + DSP_OUTPUT_MIXER_ROUTES("AIF2 Mixer"), + DSP_OUTPUT_MIXER_ROUTES("AIF3 Mixer"), + DSP_OUTPUT_MIXER_ROUTES("DAC1 Mixer"), + DSP_OUTPUT_MIXER_ROUTES("DAC2 Mixer"), + + { "AIF1 OUT", NULL, "AIF1 Mixer" }, + { "AIF2 OUT", NULL, "AIF2 Mixer" }, + { "AIF3 OUT", NULL, "AIF3 Mixer" }, + { "Left DAC1", NULL, "DAC1 Mixer" }, + { "Right DAC1", NULL, "DAC1 Mixer" }, + { "Left DAC2", NULL, "DAC2 Mixer" }, + { "Right DAC2", NULL, "DAC2 Mixer" }, + + LEFT_OUTPUT_MIXER_ROUTES("Left Lineout1 Mixer"), + RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout1 Mixer"), + LEFT_OUTPUT_MIXER_ROUTES("Left Lineout2 Mixer"), + RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout2 Mixer"), + LEFT_OUTPUT_MIXER_ROUTES("Left Speaker Mixer"), + RIGHT_OUTPUT_MIXER_ROUTES("Right Speaker Mixer"), + + { "Left Headphone Mixer", "Left DAC2 Switch", "Left DAC2" }, + { "Left Headphone Mixer", "Left DAC1 Switch", "Left DAC1" }, + { "Left Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" }, + { "Left Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" }, + { "Left Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" }, + { "Left Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" }, + { "Right Headphone Mixer", "Right DAC2 Switch", "Right DAC2" }, + { "Right Headphone Mixer", "Right DAC1 Switch", "Right DAC1" }, + { "Right Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" }, + { "Right Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" }, + { "Right Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" }, + { "Right Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" }, + + { "Left Headphone Mixer", NULL, "Headphone Enable" }, + { "Right Headphone Mixer", NULL, "Headphone Enable" }, + + { "Earpiece Mixer", "Right DAC2 Switch", "Right DAC2" }, + { "Earpiece Mixer", "Left DAC2 Switch", "Left DAC2" }, + { "Earpiece Mixer", "Right DAC1 Switch", "Right DAC1" }, + { "Earpiece Mixer", "Left DAC1 Switch", "Left DAC1" }, + { "Earpiece Mixer", "Input 1 Bypass Switch", "IN1PGA" }, + { "Earpiece Mixer", "Input 2 Bypass Switch", "IN2PGA" }, + { "Earpiece Mixer", "Input 3 Bypass Switch", "IN3PGA" }, + { "Earpiece Mixer", "Input 4 Bypass Switch", "IN4PGA" }, + + { "LOUT1L", NULL, "Left Lineout1 Mixer" }, + { "LOUT1R", NULL, "Right Lineout1 Mixer" }, + { "LOUT2L", NULL, "Left Lineout2 Mixer" }, + { "LOUT2R", NULL, "Right Lineout2 Mixer" }, + { "SPKL", NULL, "Left Speaker Mixer" }, + { "SPKR", NULL, "Right Speaker Mixer" }, + { "HPL", NULL, "Left Headphone Mixer" }, + { "HPR", NULL, "Right Headphone Mixer" }, + { "EP", NULL, "Earpiece Mixer" }, + + { "IN1PGA", NULL, "AIN1L" }, + { "IN2PGA", NULL, "AIN2L" }, + { "IN3PGA", NULL, "AIN3L" }, + { "IN4PGA", NULL, "AIN4L" }, + { "IN1PGA", NULL, "AIN1R" }, + { "IN2PGA", NULL, "AIN2R" }, + { "IN3PGA", NULL, "AIN3R" }, + { "IN4PGA", NULL, "AIN4R" }, + + { "SYSCLK1", NULL, "PLL1" }, + { "SYSCLK2", NULL, "PLL2" }, + + { "Left DAC1", NULL, "SYSCLK1" }, + { "Right DAC1", NULL, "SYSCLK1" }, + { "Left DAC2", NULL, "SYSCLK1" }, + { "Right DAC2", NULL, "SYSCLK1" }, + { "Left ADC", NULL, "SYSCLK1" }, + { "Right ADC", NULL, "SYSCLK1" }, + + { "DSP", NULL, "SYSCLK1" }, + + { "AIF1 Mixer", NULL, "DSP" }, + { "AIF2 Mixer", NULL, "DSP" }, + { "AIF3 Mixer", NULL, "DSP" }, + { "DAC1 Mixer", NULL, "DSP" }, + { "DAC2 Mixer", NULL, "DSP" }, + { "DAC1 Mixer", NULL, "Playback Engine A" }, + { "DAC2 Mixer", NULL, "Playback Engine B" }, + { "Left ADC Mixer", NULL, "Recording Engine A" }, + { "Right ADC Mixer", NULL, "Recording Engine A" }, + + { "AIF1 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk }, + { "AIF2 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk }, + { "AIF3 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk }, + { "AIF1 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk }, + { "AIF2 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk }, + { "AIF3 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk }, + + { "AIF1 IN", NULL, "AIF1 CLK" }, + { "AIF1 OUT", NULL, "AIF1 CLK" }, + { "AIF2 IN", NULL, "AIF2 CLK" }, + { "AIF2 OUT", NULL, "AIF2 CLK" }, + { "AIF3 IN", NULL, "AIF3 CLK" }, + { "AIF3 OUT", NULL, "AIF3 CLK" }, + { "AIF1 IN", NULL, "AIF1 IN SRC", adau1373_check_src }, + { "AIF1 OUT", NULL, "AIF1 OUT SRC", adau1373_check_src }, + { "AIF2 IN", NULL, "AIF2 IN SRC", adau1373_check_src }, + { "AIF2 OUT", NULL, "AIF2 OUT SRC", adau1373_check_src }, + { "AIF3 IN", NULL, "AIF3 IN SRC", adau1373_check_src }, + { "AIF3 OUT", NULL, "AIF3 OUT SRC", adau1373_check_src }, + + { "DMIC1", NULL, "DMIC1DAT" }, + { "DMIC1", NULL, "SYSCLK1" }, + { "DMIC1", NULL, "Recording Engine A" }, + { "DMIC2", NULL, "DMIC2DAT" }, + { "DMIC2", NULL, "SYSCLK1" }, + { "DMIC2", NULL, "Recording Engine B" }, +}; + +static int adau1373_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component); + struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id]; + unsigned int div; + unsigned int freq; + unsigned int ctrl; + + freq = adau1373_dai->sysclk; + + if (freq % params_rate(params) != 0) + return -EINVAL; + + switch (freq / params_rate(params)) { + case 1024: /* sysclk / 256 */ + div = 0; + break; + case 1536: /* 2/3 sysclk / 256 */ + div = 1; + break; + case 2048: /* 1/2 sysclk / 256 */ + div = 2; + break; + case 3072: /* 1/3 sysclk / 256 */ + div = 3; + break; + case 4096: /* 1/4 sysclk / 256 */ + div = 4; + break; + case 6144: /* 1/6 sysclk / 256 */ + div = 5; + break; + case 5632: /* 2/11 sysclk / 256 */ + div = 6; + break; + default: + return -EINVAL; + } + + adau1373_dai->enable_src = (div != 0); + + regmap_update_bits(adau1373->regmap, ADAU1373_BCLKDIV(dai->id), + ADAU1373_BCLKDIV_SR_MASK | ADAU1373_BCLKDIV_BCLK_MASK, + (div << 2) | ADAU1373_BCLKDIV_64); + + switch (params_width(params)) { + case 16: + ctrl = ADAU1373_DAI_WLEN_16; + break; + case 20: + ctrl = ADAU1373_DAI_WLEN_20; + break; + case 24: + ctrl = ADAU1373_DAI_WLEN_24; + break; + case 32: + ctrl = ADAU1373_DAI_WLEN_32; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(adau1373->regmap, ADAU1373_DAI(dai->id), + ADAU1373_DAI_WLEN_MASK, ctrl); +} + +static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component); + struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id]; + unsigned int ctrl; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + ctrl = ADAU1373_DAI_MASTER; + adau1373_dai->clock_provider = true; + break; + case SND_SOC_DAIFMT_CBC_CFC: + ctrl = 0; + adau1373_dai->clock_provider = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl |= ADAU1373_DAI_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl |= ADAU1373_DAI_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl |= ADAU1373_DAI_FORMAT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl |= ADAU1373_DAI_FORMAT_DSP; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl |= ADAU1373_DAI_INVERT_BCLK; + break; + case SND_SOC_DAIFMT_NB_IF: + ctrl |= ADAU1373_DAI_INVERT_LRCLK; + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl |= ADAU1373_DAI_INVERT_LRCLK | ADAU1373_DAI_INVERT_BCLK; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adau1373->regmap, ADAU1373_DAI(dai->id), + ~ADAU1373_DAI_WLEN_MASK, ctrl); + + return 0; +} + +static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct adau1373 *adau1373 = snd_soc_component_get_drvdata(dai->component); + struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id]; + + switch (clk_id) { + case ADAU1373_CLK_SRC_PLL1: + case ADAU1373_CLK_SRC_PLL2: + break; + default: + return -EINVAL; + } + + adau1373_dai->sysclk = freq; + adau1373_dai->clk_src = clk_id; + + regmap_update_bits(adau1373->regmap, ADAU1373_BCLKDIV(dai->id), + ADAU1373_BCLKDIV_SOURCE, clk_id << 5); + + return 0; +} + +static const struct snd_soc_dai_ops adau1373_dai_ops = { + .hw_params = adau1373_hw_params, + .set_sysclk = adau1373_set_dai_sysclk, + .set_fmt = adau1373_set_dai_fmt, +}; + +#define ADAU1373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver adau1373_dai_driver[] = { + { + .id = 0, + .name = "adau1373-aif1", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ADAU1373_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ADAU1373_FORMATS, + }, + .ops = &adau1373_dai_ops, + .symmetric_rate = 1, + }, + { + .id = 1, + .name = "adau1373-aif2", + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ADAU1373_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ADAU1373_FORMATS, + }, + .ops = &adau1373_dai_ops, + .symmetric_rate = 1, + }, + { + .id = 2, + .name = "adau1373-aif3", + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ADAU1373_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ADAU1373_FORMATS, + }, + .ops = &adau1373_dai_ops, + .symmetric_rate = 1, + }, +}; + +static int adau1373_set_pll(struct snd_soc_component *component, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component); + unsigned int dpll_div = 0; + uint8_t pll_regs[5]; + int ret; + + switch (pll_id) { + case ADAU1373_PLL1: + case ADAU1373_PLL2: + break; + default: + return -EINVAL; + } + + switch (source) { + case ADAU1373_PLL_SRC_BCLK1: + case ADAU1373_PLL_SRC_BCLK2: + case ADAU1373_PLL_SRC_BCLK3: + case ADAU1373_PLL_SRC_LRCLK1: + case ADAU1373_PLL_SRC_LRCLK2: + case ADAU1373_PLL_SRC_LRCLK3: + case ADAU1373_PLL_SRC_MCLK1: + case ADAU1373_PLL_SRC_MCLK2: + case ADAU1373_PLL_SRC_GPIO1: + case ADAU1373_PLL_SRC_GPIO2: + case ADAU1373_PLL_SRC_GPIO3: + case ADAU1373_PLL_SRC_GPIO4: + break; + default: + return -EINVAL; + } + + if (freq_in < 7813 || freq_in > 27000000) + return -EINVAL; + + if (freq_out < 45158000 || freq_out > 49152000) + return -EINVAL; + + /* APLL input needs to be >= 8Mhz, so in case freq_in is less we use the + * DPLL to get it there. DPLL_out = (DPLL_in / div) * 1024 */ + while (freq_in < 8000000) { + freq_in *= 2; + dpll_div++; + } + + ret = adau_calc_pll_cfg(freq_in, freq_out, pll_regs); + if (ret) + return -EINVAL; + + if (dpll_div) { + dpll_div = 11 - dpll_div; + regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id), + ADAU1373_PLL_CTRL6_DPLL_BYPASS, 0); + } else { + regmap_update_bits(adau1373->regmap, ADAU1373_PLL_CTRL6(pll_id), + ADAU1373_PLL_CTRL6_DPLL_BYPASS, + ADAU1373_PLL_CTRL6_DPLL_BYPASS); + } + + regmap_write(adau1373->regmap, ADAU1373_DPLL_CTRL(pll_id), + (source << 4) | dpll_div); + regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), pll_regs[0]); + regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), pll_regs[1]); + regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), pll_regs[2]); + regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), pll_regs[3]); + regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id), pll_regs[4]); + + /* Set sysclk to pll_rate / 4 */ + regmap_update_bits(adau1373->regmap, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09); + + return 0; +} + +static void adau1373_load_drc_settings(struct adau1373 *adau1373, + unsigned int nr, uint8_t *drc) +{ + unsigned int i; + + for (i = 0; i < ADAU1373_DRC_SIZE; ++i) + regmap_write(adau1373->regmap, ADAU1373_DRC(nr) + i, drc[i]); +} + +static bool adau1373_valid_micbias(enum adau1373_micbias_voltage micbias) +{ + switch (micbias) { + case ADAU1373_MICBIAS_2_9V: + case ADAU1373_MICBIAS_2_2V: + case ADAU1373_MICBIAS_2_6V: + case ADAU1373_MICBIAS_1_8V: + return true; + default: + break; + } + return false; +} + +static int adau1373_probe(struct snd_soc_component *component) +{ + struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component); + struct adau1373_platform_data *pdata = component->dev->platform_data; + bool lineout_differential = false; + unsigned int val; + int i; + + if (pdata) { + if (pdata->num_drc > ARRAY_SIZE(pdata->drc_setting)) + return -EINVAL; + + if (!adau1373_valid_micbias(pdata->micbias1) || + !adau1373_valid_micbias(pdata->micbias2)) + return -EINVAL; + + for (i = 0; i < pdata->num_drc; ++i) { + adau1373_load_drc_settings(adau1373, i, + pdata->drc_setting[i]); + } + + snd_soc_add_component_controls(component, adau1373_drc_controls, + pdata->num_drc); + + val = 0; + for (i = 0; i < 4; ++i) { + if (pdata->input_differential[i]) + val |= BIT(i); + } + regmap_write(adau1373->regmap, ADAU1373_INPUT_MODE, val); + + val = 0; + if (pdata->lineout_differential) + val |= ADAU1373_OUTPUT_CTRL_LDIFF; + if (pdata->lineout_ground_sense) + val |= ADAU1373_OUTPUT_CTRL_LNFBEN; + regmap_write(adau1373->regmap, ADAU1373_OUTPUT_CTRL, val); + + lineout_differential = pdata->lineout_differential; + + regmap_write(adau1373->regmap, ADAU1373_EP_CTRL, + (pdata->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) | + (pdata->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET)); + } + + if (!lineout_differential) { + snd_soc_add_component_controls(component, adau1373_lineout2_controls, + ARRAY_SIZE(adau1373_lineout2_controls)); + } + + regmap_write(adau1373->regmap, ADAU1373_ADC_CTRL, + ADAU1373_ADC_CTRL_RESET_FORCE | ADAU1373_ADC_CTRL_PEAK_DETECT); + + return 0; +} + +static int adau1373_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(adau1373->regmap, ADAU1373_PWDN_CTRL3, + ADAU1373_PWDN_CTRL3_PWR_EN, ADAU1373_PWDN_CTRL3_PWR_EN); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(adau1373->regmap, ADAU1373_PWDN_CTRL3, + ADAU1373_PWDN_CTRL3_PWR_EN, 0); + break; + } + return 0; +} + +static int adau1373_resume(struct snd_soc_component *component) +{ + struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component); + + regcache_sync(adau1373->regmap); + + return 0; +} + +static bool adau1373_register_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADAU1373_SOFT_RESET: + case ADAU1373_ADC_DAC_STATUS: + return true; + default: + return false; + } +} + +static const struct regmap_config adau1373_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + + .volatile_reg = adau1373_register_volatile, + .max_register = ADAU1373_SOFT_RESET, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = adau1373_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1373_reg_defaults), +}; + +static const struct snd_soc_component_driver adau1373_component_driver = { + .probe = adau1373_probe, + .resume = adau1373_resume, + .set_bias_level = adau1373_set_bias_level, + .set_pll = adau1373_set_pll, + .controls = adau1373_controls, + .num_controls = ARRAY_SIZE(adau1373_controls), + .dapm_widgets = adau1373_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets), + .dapm_routes = adau1373_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int adau1373_i2c_probe(struct i2c_client *client) +{ + struct adau1373 *adau1373; + int ret; + + adau1373 = devm_kzalloc(&client->dev, sizeof(*adau1373), GFP_KERNEL); + if (!adau1373) + return -ENOMEM; + + adau1373->regmap = devm_regmap_init_i2c(client, + &adau1373_regmap_config); + if (IS_ERR(adau1373->regmap)) + return PTR_ERR(adau1373->regmap); + + regmap_write(adau1373->regmap, ADAU1373_SOFT_RESET, 0x00); + + dev_set_drvdata(&client->dev, adau1373); + + ret = devm_snd_soc_register_component(&client->dev, + &adau1373_component_driver, + adau1373_dai_driver, ARRAY_SIZE(adau1373_dai_driver)); + return ret; +} + +static const struct i2c_device_id adau1373_i2c_id[] = { + { "adau1373", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id); + +static struct i2c_driver adau1373_i2c_driver = { + .driver = { + .name = "adau1373", + }, + .probe = adau1373_i2c_probe, + .id_table = adau1373_i2c_id, +}; + +module_i2c_driver(adau1373_i2c_driver); + +MODULE_DESCRIPTION("ASoC ADAU1373 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1373.h b/sound/soc/codecs/adau1373.h new file mode 100644 index 0000000000..56320d5e32 --- /dev/null +++ b/sound/soc/codecs/adau1373.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ADAU1373_H__ +#define __ADAU1373_H__ + +enum adau1373_pll_src { + ADAU1373_PLL_SRC_MCLK1 = 0, + ADAU1373_PLL_SRC_BCLK1 = 1, + ADAU1373_PLL_SRC_BCLK2 = 2, + ADAU1373_PLL_SRC_BCLK3 = 3, + ADAU1373_PLL_SRC_LRCLK1 = 4, + ADAU1373_PLL_SRC_LRCLK2 = 5, + ADAU1373_PLL_SRC_LRCLK3 = 6, + ADAU1373_PLL_SRC_GPIO1 = 7, + ADAU1373_PLL_SRC_GPIO2 = 8, + ADAU1373_PLL_SRC_GPIO3 = 9, + ADAU1373_PLL_SRC_GPIO4 = 10, + ADAU1373_PLL_SRC_MCLK2 = 11, +}; + +enum adau1373_pll { + ADAU1373_PLL1 = 0, + ADAU1373_PLL2 = 1, +}; + +enum adau1373_clk_src { + ADAU1373_CLK_SRC_PLL1 = 0, + ADAU1373_CLK_SRC_PLL2 = 1, +}; + +#endif diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c new file mode 100644 index 0000000000..94831aad7a --- /dev/null +++ b/sound/soc/codecs/adau1701.c @@ -0,0 +1,888 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for ADAU1701 SigmaDSP processor + * + * Copyright 2011 Analog Devices Inc. + * Author: Lars-Peter Clausen + * based on an inital version by Cliff Cai + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sigmadsp.h" +#include "adau1701.h" + +#define ADAU1701_SAFELOAD_DATA(i) (0x0810 + (i)) +#define ADAU1701_SAFELOAD_ADDR(i) (0x0815 + (i)) + +#define ADAU1701_DSPCTRL 0x081c +#define ADAU1701_SEROCTL 0x081e +#define ADAU1701_SERICTL 0x081f + +#define ADAU1701_AUXNPOW 0x0822 +#define ADAU1701_PINCONF_0 0x0820 +#define ADAU1701_PINCONF_1 0x0821 +#define ADAU1701_AUXNPOW 0x0822 + +#define ADAU1701_OSCIPOW 0x0826 +#define ADAU1701_DACSET 0x0827 + +#define ADAU1701_MAX_REGISTER 0x0828 + +#define ADAU1701_DSPCTRL_CR (1 << 2) +#define ADAU1701_DSPCTRL_DAM (1 << 3) +#define ADAU1701_DSPCTRL_ADM (1 << 4) +#define ADAU1701_DSPCTRL_IST (1 << 5) +#define ADAU1701_DSPCTRL_SR_48 0x00 +#define ADAU1701_DSPCTRL_SR_96 0x01 +#define ADAU1701_DSPCTRL_SR_192 0x02 +#define ADAU1701_DSPCTRL_SR_MASK 0x03 + +#define ADAU1701_SEROCTL_INV_LRCLK 0x2000 +#define ADAU1701_SEROCTL_INV_BCLK 0x1000 +#define ADAU1701_SEROCTL_MASTER 0x0800 + +#define ADAU1701_SEROCTL_OBF16 0x0000 +#define ADAU1701_SEROCTL_OBF8 0x0200 +#define ADAU1701_SEROCTL_OBF4 0x0400 +#define ADAU1701_SEROCTL_OBF2 0x0600 +#define ADAU1701_SEROCTL_OBF_MASK 0x0600 + +#define ADAU1701_SEROCTL_OLF1024 0x0000 +#define ADAU1701_SEROCTL_OLF512 0x0080 +#define ADAU1701_SEROCTL_OLF256 0x0100 +#define ADAU1701_SEROCTL_OLF_MASK 0x0180 + +#define ADAU1701_SEROCTL_MSB_DEALY1 0x0000 +#define ADAU1701_SEROCTL_MSB_DEALY0 0x0004 +#define ADAU1701_SEROCTL_MSB_DEALY8 0x0008 +#define ADAU1701_SEROCTL_MSB_DEALY12 0x000c +#define ADAU1701_SEROCTL_MSB_DEALY16 0x0010 +#define ADAU1701_SEROCTL_MSB_DEALY_MASK 0x001c + +#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000 +#define ADAU1701_SEROCTL_WORD_LEN_20 0x0001 +#define ADAU1701_SEROCTL_WORD_LEN_16 0x0002 +#define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003 + +#define ADAU1701_AUXNPOW_VBPD 0x40 +#define ADAU1701_AUXNPOW_VRPD 0x20 + +#define ADAU1701_SERICTL_I2S 0 +#define ADAU1701_SERICTL_LEFTJ 1 +#define ADAU1701_SERICTL_TDM 2 +#define ADAU1701_SERICTL_RIGHTJ_24 3 +#define ADAU1701_SERICTL_RIGHTJ_20 4 +#define ADAU1701_SERICTL_RIGHTJ_18 5 +#define ADAU1701_SERICTL_RIGHTJ_16 6 +#define ADAU1701_SERICTL_MODE_MASK 7 +#define ADAU1701_SERICTL_INV_BCLK BIT(3) +#define ADAU1701_SERICTL_INV_LRCLK BIT(4) + +#define ADAU1701_OSCIPOW_OPD 0x04 +#define ADAU1701_DACSET_DACINIT 1 + +#define ADAU1707_CLKDIV_UNSET (-1U) + +#define ADAU1701_FIRMWARE "adau1701.bin" + +static const char * const supply_names[] = { + "dvdd", "avdd" +}; + +struct adau1701 { + struct gpio_desc *gpio_nreset; + struct gpio_descs *gpio_pll_mode; + unsigned int dai_fmt; + unsigned int pll_clkdiv; + unsigned int sysclk; + struct regmap *regmap; + struct i2c_client *client; + u8 pin_config[12]; + + struct sigmadsp *sigmadsp; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; +}; + +static const struct snd_kcontrol_new adau1701_controls[] = { + SOC_SINGLE("Master Capture Switch", ADAU1701_DSPCTRL, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget adau1701_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC0", "Playback", ADAU1701_AUXNPOW, 3, 1), + SND_SOC_DAPM_DAC("DAC1", "Playback", ADAU1701_AUXNPOW, 2, 1), + SND_SOC_DAPM_DAC("DAC2", "Playback", ADAU1701_AUXNPOW, 1, 1), + SND_SOC_DAPM_DAC("DAC3", "Playback", ADAU1701_AUXNPOW, 0, 1), + SND_SOC_DAPM_ADC("ADC", "Capture", ADAU1701_AUXNPOW, 7, 1), + + SND_SOC_DAPM_OUTPUT("OUT0"), + SND_SOC_DAPM_OUTPUT("OUT1"), + SND_SOC_DAPM_OUTPUT("OUT2"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_INPUT("IN0"), + SND_SOC_DAPM_INPUT("IN1"), +}; + +static const struct snd_soc_dapm_route adau1701_dapm_routes[] = { + { "OUT0", NULL, "DAC0" }, + { "OUT1", NULL, "DAC1" }, + { "OUT2", NULL, "DAC2" }, + { "OUT3", NULL, "DAC3" }, + + { "ADC", NULL, "IN0" }, + { "ADC", NULL, "IN1" }, +}; + +static unsigned int adau1701_register_size(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case ADAU1701_PINCONF_0: + case ADAU1701_PINCONF_1: + return 3; + case ADAU1701_DSPCTRL: + case ADAU1701_SEROCTL: + case ADAU1701_AUXNPOW: + case ADAU1701_OSCIPOW: + case ADAU1701_DACSET: + return 2; + case ADAU1701_SERICTL: + return 1; + } + + dev_err(dev, "Unsupported register address: %d\n", reg); + return 0; +} + +static bool adau1701_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADAU1701_DACSET: + case ADAU1701_DSPCTRL: + return true; + default: + return false; + } +} + +static int adau1701_reg_write(void *context, unsigned int reg, + unsigned int value) +{ + struct i2c_client *client = context; + unsigned int i; + unsigned int size; + uint8_t buf[5]; + int ret; + + size = adau1701_register_size(&client->dev, reg); + if (size == 0) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + for (i = size + 1; i >= 2; --i) { + buf[i] = value; + value >>= 8; + } + + ret = i2c_master_send(client, buf, size + 2); + if (ret == size + 2) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int adau1701_reg_read(void *context, unsigned int reg, + unsigned int *value) +{ + int ret; + unsigned int i; + unsigned int size; + uint8_t send_buf[2], recv_buf[3]; + struct i2c_client *client = context; + struct i2c_msg msgs[2]; + + size = adau1701_register_size(&client->dev, reg); + if (size == 0) + return -EINVAL; + + send_buf[0] = reg >> 8; + send_buf[1] = reg & 0xff; + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(send_buf); + msgs[0].buf = send_buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = size; + msgs[1].buf = recv_buf; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + else if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *value = 0; + + for (i = 0; i < size; i++) { + *value <<= 8; + *value |= recv_buf[i]; + } + + return 0; +} + +static int adau1701_safeload(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t bytes[], size_t len) +{ + struct i2c_client *client = to_i2c_client(sigmadsp->dev); + struct adau1701 *adau1701 = i2c_get_clientdata(client); + unsigned int val; + unsigned int i; + uint8_t buf[10]; + int ret; + + ret = regmap_read(adau1701->regmap, ADAU1701_DSPCTRL, &val); + if (ret) + return ret; + + if (val & ADAU1701_DSPCTRL_IST) + msleep(50); + + for (i = 0; i < len / 4; i++) { + put_unaligned_le16(ADAU1701_SAFELOAD_DATA(i), buf); + buf[2] = 0x00; + memcpy(buf + 3, bytes + i * 4, 4); + ret = i2c_master_send(client, buf, 7); + if (ret < 0) + return ret; + else if (ret != 7) + return -EIO; + + put_unaligned_le16(ADAU1701_SAFELOAD_ADDR(i), buf); + put_unaligned_le16(addr + i, buf + 2); + ret = i2c_master_send(client, buf, 4); + if (ret < 0) + return ret; + else if (ret != 4) + return -EIO; + } + + return regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, + ADAU1701_DSPCTRL_IST, ADAU1701_DSPCTRL_IST); +} + +static const struct sigmadsp_ops adau1701_sigmadsp_ops = { + .safeload = adau1701_safeload, +}; + +static int adau1701_reset(struct snd_soc_component *component, unsigned int clkdiv, + unsigned int rate) +{ + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + int ret; + + DECLARE_BITMAP(values, 2); + sigmadsp_reset(adau1701->sigmadsp); + + if (clkdiv != ADAU1707_CLKDIV_UNSET && adau1701->gpio_pll_mode) { + switch (clkdiv) { + case 64: + __assign_bit(0, values, 0); + __assign_bit(1, values, 0); + break; + case 256: + __assign_bit(0, values, 0); + __assign_bit(1, values, 1); + break; + case 384: + __assign_bit(0, values, 1); + __assign_bit(1, values, 0); + break; + case 0: /* fallback */ + case 512: + __assign_bit(0, values, 1); + __assign_bit(1, values, 1); + break; + } + gpiod_set_array_value_cansleep(adau1701->gpio_pll_mode->ndescs, + adau1701->gpio_pll_mode->desc, adau1701->gpio_pll_mode->info, + values); + } + + adau1701->pll_clkdiv = clkdiv; + + if (adau1701->gpio_nreset) { + gpiod_set_value_cansleep(adau1701->gpio_nreset, 0); + /* minimum reset time is 20ns */ + udelay(1); + gpiod_set_value_cansleep(adau1701->gpio_nreset, 1); + /* power-up time may be as long as 85ms */ + mdelay(85); + } + + /* + * Postpone the firmware download to a point in time when we + * know the correct PLL setup + */ + if (clkdiv != ADAU1707_CLKDIV_UNSET) { + ret = sigmadsp_setup(adau1701->sigmadsp, rate); + if (ret) { + dev_warn(component->dev, "Failed to load firmware\n"); + return ret; + } + } + + regmap_write(adau1701->regmap, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT); + regmap_write(adau1701->regmap, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR); + + regcache_mark_dirty(adau1701->regmap); + regcache_sync(adau1701->regmap); + + return 0; +} + +static int adau1701_set_capture_pcm_format(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK; + unsigned int val; + + switch (params_width(params)) { + case 16: + val = ADAU1701_SEROCTL_WORD_LEN_16; + break; + case 20: + val = ADAU1701_SEROCTL_WORD_LEN_20; + break; + case 24: + val = ADAU1701_SEROCTL_WORD_LEN_24; + break; + default: + return -EINVAL; + } + + if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) { + switch (params_width(params)) { + case 16: + val |= ADAU1701_SEROCTL_MSB_DEALY16; + break; + case 20: + val |= ADAU1701_SEROCTL_MSB_DEALY12; + break; + case 24: + val |= ADAU1701_SEROCTL_MSB_DEALY8; + break; + } + mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK; + } + + regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL, mask, val); + + return 0; +} + +static int adau1701_set_playback_pcm_format(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J) + return 0; + + switch (params_width(params)) { + case 16: + val = ADAU1701_SERICTL_RIGHTJ_16; + break; + case 20: + val = ADAU1701_SERICTL_RIGHTJ_20; + break; + case 24: + val = ADAU1701_SERICTL_RIGHTJ_24; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adau1701->regmap, ADAU1701_SERICTL, + ADAU1701_SERICTL_MODE_MASK, val); + + return 0; +} + +static int adau1701_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + unsigned int clkdiv = adau1701->sysclk / params_rate(params); + unsigned int val; + int ret; + + /* + * If the mclk/lrclk ratio changes, the chip needs updated PLL + * mode GPIO settings, and a full reset cycle, including a new + * firmware upload. + */ + if (clkdiv != adau1701->pll_clkdiv) { + ret = adau1701_reset(component, clkdiv, params_rate(params)); + if (ret < 0) + return ret; + } + + switch (params_rate(params)) { + case 192000: + val = ADAU1701_DSPCTRL_SR_192; + break; + case 96000: + val = ADAU1701_DSPCTRL_SR_96; + break; + case 48000: + val = ADAU1701_DSPCTRL_SR_48; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, + ADAU1701_DSPCTRL_SR_MASK, val); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return adau1701_set_playback_pcm_format(component, params); + else + return adau1701_set_capture_pcm_format(component, params); +} + +static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + unsigned int serictl = 0x00, seroctl = 0x00; + bool invert_lrclk; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + /* master, 64-bits per sample, 1 frame per sample */ + seroctl |= ADAU1701_SEROCTL_MASTER | ADAU1701_SEROCTL_OBF16 + | ADAU1701_SEROCTL_OLF1024; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_lrclk = true; + break; + case SND_SOC_DAIFMT_IB_NF: + invert_lrclk = false; + serictl |= ADAU1701_SERICTL_INV_BCLK; + seroctl |= ADAU1701_SEROCTL_INV_BCLK; + break; + case SND_SOC_DAIFMT_IB_IF: + invert_lrclk = true; + serictl |= ADAU1701_SERICTL_INV_BCLK; + seroctl |= ADAU1701_SEROCTL_INV_BCLK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + serictl |= ADAU1701_SERICTL_LEFTJ; + seroctl |= ADAU1701_SEROCTL_MSB_DEALY0; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_RIGHT_J: + serictl |= ADAU1701_SERICTL_RIGHTJ_24; + seroctl |= ADAU1701_SEROCTL_MSB_DEALY8; + invert_lrclk = !invert_lrclk; + break; + default: + return -EINVAL; + } + + if (invert_lrclk) { + seroctl |= ADAU1701_SEROCTL_INV_LRCLK; + serictl |= ADAU1701_SERICTL_INV_LRCLK; + } + + adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + regmap_write(adau1701->regmap, ADAU1701_SERICTL, serictl); + regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL, + ~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl); + + return 0; +} + +static int adau1701_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD; + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* Enable VREF and VREF buffer */ + regmap_update_bits(adau1701->regmap, + ADAU1701_AUXNPOW, mask, 0x00); + break; + case SND_SOC_BIAS_OFF: + /* Disable VREF and VREF buffer */ + regmap_update_bits(adau1701->regmap, + ADAU1701_AUXNPOW, mask, mask); + break; + } + + return 0; +} + +static int adau1701_mute_stream(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + unsigned int mask = ADAU1701_DSPCTRL_DAM; + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (mute) + val = 0; + else + val = mask; + + regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, mask, val); + + return 0; +} + +static int adau1701_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + unsigned int val; + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case ADAU1701_CLK_SRC_OSC: + val = 0x0; + break; + case ADAU1701_CLK_SRC_MCLK: + val = ADAU1701_OSCIPOW_OPD; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adau1701->regmap, ADAU1701_OSCIPOW, + ADAU1701_OSCIPOW_OPD, val); + adau1701->sysclk = freq; + + return 0; +} + +static int adau1701_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(dai->component); + + return sigmadsp_restrict_params(adau1701->sigmadsp, substream); +} + +#define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) + +#define ADAU1701_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops adau1701_dai_ops = { + .set_fmt = adau1701_set_dai_fmt, + .hw_params = adau1701_hw_params, + .mute_stream = adau1701_mute_stream, + .startup = adau1701_startup, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver adau1701_dai = { + .name = "adau1701", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = ADAU1701_RATES, + .formats = ADAU1701_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = ADAU1701_RATES, + .formats = ADAU1701_FORMATS, + }, + .ops = &adau1701_dai_ops, + .symmetric_rate = 1, +}; + +#ifdef CONFIG_OF +static const struct of_device_id adau1701_dt_ids[] = { + { .compatible = "adi,adau1701", }, + { } +}; +MODULE_DEVICE_TABLE(of, adau1701_dt_ids); +#endif + +static int adau1701_probe(struct snd_soc_component *component) +{ + int i, ret; + unsigned int val; + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + + ret = sigmadsp_attach(adau1701->sigmadsp, component); + if (ret) + return ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(component->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + /* + * Let the pll_clkdiv variable default to something that won't happen + * at runtime. That way, we can postpone the firmware download from + * adau1701_reset() to a point in time when we know the correct PLL + * mode parameters. + */ + adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET; + + /* initalize with pre-configured pll mode settings */ + ret = adau1701_reset(component, adau1701->pll_clkdiv, 0); + if (ret < 0) + goto exit_regulators_disable; + + /* set up pin config */ + val = 0; + for (i = 0; i < 6; i++) + val |= adau1701->pin_config[i] << (i * 4); + + regmap_write(adau1701->regmap, ADAU1701_PINCONF_0, val); + + val = 0; + for (i = 0; i < 6; i++) + val |= adau1701->pin_config[i + 6] << (i * 4); + + regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val); + + return 0; + +exit_regulators_disable: + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); + return ret; +} + +static void adau1701_remove(struct snd_soc_component *component) +{ + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + + if (adau1701->gpio_nreset) + gpiod_set_value_cansleep(adau1701->gpio_nreset, 0); + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); +} + +#ifdef CONFIG_PM +static int adau1701_suspend(struct snd_soc_component *component) +{ + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + + return 0; +} + +static int adau1701_resume(struct snd_soc_component *component) +{ + struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(component->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + return adau1701_reset(component, adau1701->pll_clkdiv, 0); +} +#else +#define adau1701_resume NULL +#define adau1701_suspend NULL +#endif /* CONFIG_PM */ + +static const struct snd_soc_component_driver adau1701_component_drv = { + .probe = adau1701_probe, + .remove = adau1701_remove, + .resume = adau1701_resume, + .suspend = adau1701_suspend, + .set_bias_level = adau1701_set_bias_level, + .controls = adau1701_controls, + .num_controls = ARRAY_SIZE(adau1701_controls), + .dapm_widgets = adau1701_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1701_dapm_widgets), + .dapm_routes = adau1701_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes), + .set_sysclk = adau1701_set_sysclk, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config adau1701_regmap = { + .reg_bits = 16, + .val_bits = 32, + .max_register = ADAU1701_MAX_REGISTER, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = adau1701_volatile_reg, + .reg_write = adau1701_reg_write, + .reg_read = adau1701_reg_read, +}; + +static int adau1701_i2c_probe(struct i2c_client *client) +{ + struct adau1701 *adau1701; + struct device *dev = &client->dev; + int ret, i; + + adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL); + if (!adau1701) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + adau1701->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(adau1701->supplies), + adau1701->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + adau1701->client = client; + adau1701->regmap = devm_regmap_init(dev, NULL, client, + &adau1701_regmap); + if (IS_ERR(adau1701->regmap)) { + ret = PTR_ERR(adau1701->regmap); + goto exit_regulators_disable; + } + + + if (dev->of_node) { + of_property_read_u32(dev->of_node, "adi,pll-clkdiv", + &adau1701->pll_clkdiv); + + of_property_read_u8_array(dev->of_node, "adi,pin-config", + adau1701->pin_config, + ARRAY_SIZE(adau1701->pin_config)); + } + + adau1701->gpio_nreset = devm_gpiod_get_optional(dev, "reset", GPIOD_IN); + + if (IS_ERR(adau1701->gpio_nreset)) { + ret = PTR_ERR(adau1701->gpio_nreset); + goto exit_regulators_disable; + } + + adau1701->gpio_pll_mode = devm_gpiod_get_array_optional(dev, "adi,pll-mode", GPIOD_OUT_LOW); + + if (IS_ERR(adau1701->gpio_pll_mode)) { + ret = PTR_ERR(adau1701->gpio_pll_mode); + goto exit_regulators_disable; + } + + i2c_set_clientdata(client, adau1701); + + adau1701->sigmadsp = devm_sigmadsp_init_i2c(client, + &adau1701_sigmadsp_ops, ADAU1701_FIRMWARE); + if (IS_ERR(adau1701->sigmadsp)) { + ret = PTR_ERR(adau1701->sigmadsp); + goto exit_regulators_disable; + } + + ret = devm_snd_soc_register_component(&client->dev, + &adau1701_component_drv, + &adau1701_dai, 1); + +exit_regulators_disable: + + regulator_bulk_disable(ARRAY_SIZE(adau1701->supplies), adau1701->supplies); + return ret; +} + +static const struct i2c_device_id adau1701_i2c_id[] = { + { "adau1401", 0 }, + { "adau1401a", 0 }, + { "adau1701", 0 }, + { "adau1702", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id); + +static struct i2c_driver adau1701_i2c_driver = { + .driver = { + .name = "adau1701", + .of_match_table = of_match_ptr(adau1701_dt_ids), + }, + .probe = adau1701_i2c_probe, + .id_table = adau1701_i2c_id, +}; + +module_i2c_driver(adau1701_i2c_driver); + +MODULE_DESCRIPTION("ASoC ADAU1701 SigmaDSP driver"); +MODULE_AUTHOR("Cliff Cai "); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1701.h b/sound/soc/codecs/adau1701.h new file mode 100644 index 0000000000..19b41e453a --- /dev/null +++ b/sound/soc/codecs/adau1701.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * header file for ADAU1701 SigmaDSP processor + * + * Copyright 2011 Analog Devices Inc. + */ + +#ifndef _ADAU1701_H +#define _ADAU1701_H + +enum adau1701_clk_src { + ADAU1701_CLK_SRC_OSC, + ADAU1701_CLK_SRC_MCLK, +}; + +#endif diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c new file mode 100644 index 0000000000..a554255186 --- /dev/null +++ b/sound/soc/codecs/adau1761-i2c.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include + +#include "adau1761.h" + +static const struct i2c_device_id adau1761_i2c_ids[]; + +static int adau1761_i2c_probe(struct i2c_client *client) +{ + struct regmap_config config; + const struct i2c_device_id *id = i2c_match_id(adau1761_i2c_ids, client); + + config = adau1761_regmap_config; + config.val_bits = 8; + config.reg_bits = 16; + + return adau1761_probe(&client->dev, + devm_regmap_init_i2c(client, &config), + id->driver_data, NULL); +} + +static void adau1761_i2c_remove(struct i2c_client *client) +{ + adau17x1_remove(&client->dev); +} + +static const struct i2c_device_id adau1761_i2c_ids[] = { + { "adau1361", ADAU1361 }, + { "adau1461", ADAU1761 }, + { "adau1761", ADAU1761 }, + { "adau1961", ADAU1361 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids); + +#if defined(CONFIG_OF) +static const struct of_device_id adau1761_i2c_dt_ids[] = { + { .compatible = "adi,adau1361", }, + { .compatible = "adi,adau1461", }, + { .compatible = "adi,adau1761", }, + { .compatible = "adi,adau1961", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1761_i2c_dt_ids); +#endif + +static struct i2c_driver adau1761_i2c_driver = { + .driver = { + .name = "adau1761", + .of_match_table = of_match_ptr(adau1761_i2c_dt_ids), + }, + .probe = adau1761_i2c_probe, + .remove = adau1761_i2c_remove, + .id_table = adau1761_i2c_ids, +}; +module_i2c_driver(adau1761_i2c_driver); + +MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC I2C driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c new file mode 100644 index 0000000000..7c9242c2ff --- /dev/null +++ b/sound/soc/codecs/adau1761-spi.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include + +#include "adau1761.h" + +static void adau1761_spi_switch_mode(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + /* + * To get the device into SPI mode CLATCH has to be pulled low three + * times. Do this by issuing three dummy reads. + */ + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); +} + +static int adau1761_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap_config config; + + if (!id) + return -EINVAL; + + config = adau1761_regmap_config; + config.val_bits = 8; + config.reg_bits = 24; + config.read_flag_mask = 0x1; + + return adau1761_probe(&spi->dev, + devm_regmap_init_spi(spi, &config), + id->driver_data, adau1761_spi_switch_mode); +} + +static void adau1761_spi_remove(struct spi_device *spi) +{ + adau17x1_remove(&spi->dev); +} + +static const struct spi_device_id adau1761_spi_id[] = { + { "adau1361", ADAU1361 }, + { "adau1461", ADAU1761 }, + { "adau1761", ADAU1761 }, + { "adau1961", ADAU1361 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adau1761_spi_id); + +#if defined(CONFIG_OF) +static const struct of_device_id adau1761_spi_dt_ids[] = { + { .compatible = "adi,adau1361", }, + { .compatible = "adi,adau1461", }, + { .compatible = "adi,adau1761", }, + { .compatible = "adi,adau1961", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1761_spi_dt_ids); +#endif + +static struct spi_driver adau1761_spi_driver = { + .driver = { + .name = "adau1761", + .of_match_table = of_match_ptr(adau1761_spi_dt_ids), + }, + .probe = adau1761_spi_probe, + .remove = adau1761_spi_remove, + .id_table = adau1761_spi_id, +}; +module_spi_driver(adau1761_spi_driver); + +MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC SPI driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c new file mode 100644 index 0000000000..1f09ea385f --- /dev/null +++ b/sound/soc/codecs/adau1761.c @@ -0,0 +1,1023 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec + * + * Copyright 2011-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adau17x1.h" +#include "adau1761.h" + +#define ADAU1761_DIGMIC_JACKDETECT 0x4008 +#define ADAU1761_REC_MIXER_LEFT0 0x400a +#define ADAU1761_REC_MIXER_LEFT1 0x400b +#define ADAU1761_REC_MIXER_RIGHT0 0x400c +#define ADAU1761_REC_MIXER_RIGHT1 0x400d +#define ADAU1761_LEFT_DIFF_INPUT_VOL 0x400e +#define ADAU1761_RIGHT_DIFF_INPUT_VOL 0x400f +#define ADAU1761_ALC_CTRL0 0x4011 +#define ADAU1761_ALC_CTRL1 0x4012 +#define ADAU1761_ALC_CTRL2 0x4013 +#define ADAU1761_ALC_CTRL3 0x4014 +#define ADAU1761_PLAY_LR_MIXER_LEFT 0x4020 +#define ADAU1761_PLAY_MIXER_LEFT0 0x401c +#define ADAU1761_PLAY_MIXER_LEFT1 0x401d +#define ADAU1761_PLAY_MIXER_RIGHT0 0x401e +#define ADAU1761_PLAY_MIXER_RIGHT1 0x401f +#define ADAU1761_PLAY_LR_MIXER_RIGHT 0x4021 +#define ADAU1761_PLAY_MIXER_MONO 0x4022 +#define ADAU1761_PLAY_HP_LEFT_VOL 0x4023 +#define ADAU1761_PLAY_HP_RIGHT_VOL 0x4024 +#define ADAU1761_PLAY_LINE_LEFT_VOL 0x4025 +#define ADAU1761_PLAY_LINE_RIGHT_VOL 0x4026 +#define ADAU1761_PLAY_MONO_OUTPUT_VOL 0x4027 +#define ADAU1761_POP_CLICK_SUPPRESS 0x4028 +#define ADAU1761_JACK_DETECT_PIN 0x4031 +#define ADAU1761_DEJITTER 0x4036 +#define ADAU1761_CLK_ENABLE0 0x40f9 +#define ADAU1761_CLK_ENABLE1 0x40fa + +#define ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW BIT(0) +#define ADAU1761_DIGMIC_JACKDETECT_DIGMIC BIT(5) + +#define ADAU1761_DIFF_INPUT_VOL_LDEN BIT(0) + +#define ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP BIT(0) +#define ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE BIT(1) + +#define ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP BIT(0) + +#define ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP BIT(0) + +#define ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP BIT(0) + + +#define ADAU1761_FIRMWARE "adau1761.bin" + +static const struct reg_default adau1761_reg_defaults[] = { + { ADAU1761_DEJITTER, 0x03 }, + { ADAU1761_DIGMIC_JACKDETECT, 0x00 }, + { ADAU1761_REC_MIXER_LEFT0, 0x00 }, + { ADAU1761_REC_MIXER_LEFT1, 0x00 }, + { ADAU1761_REC_MIXER_RIGHT0, 0x00 }, + { ADAU1761_REC_MIXER_RIGHT1, 0x00 }, + { ADAU1761_LEFT_DIFF_INPUT_VOL, 0x00 }, + { ADAU1761_ALC_CTRL0, 0x00 }, + { ADAU1761_ALC_CTRL1, 0x00 }, + { ADAU1761_ALC_CTRL2, 0x00 }, + { ADAU1761_ALC_CTRL3, 0x00 }, + { ADAU1761_RIGHT_DIFF_INPUT_VOL, 0x00 }, + { ADAU1761_PLAY_LR_MIXER_LEFT, 0x00 }, + { ADAU1761_PLAY_MIXER_LEFT0, 0x00 }, + { ADAU1761_PLAY_MIXER_LEFT1, 0x00 }, + { ADAU1761_PLAY_MIXER_RIGHT0, 0x00 }, + { ADAU1761_PLAY_MIXER_RIGHT1, 0x00 }, + { ADAU1761_PLAY_LR_MIXER_RIGHT, 0x00 }, + { ADAU1761_PLAY_MIXER_MONO, 0x00 }, + { ADAU1761_PLAY_HP_LEFT_VOL, 0x00 }, + { ADAU1761_PLAY_HP_RIGHT_VOL, 0x00 }, + { ADAU1761_PLAY_LINE_LEFT_VOL, 0x00 }, + { ADAU1761_PLAY_LINE_RIGHT_VOL, 0x00 }, + { ADAU1761_PLAY_MONO_OUTPUT_VOL, 0x00 }, + { ADAU1761_POP_CLICK_SUPPRESS, 0x00 }, + { ADAU1761_JACK_DETECT_PIN, 0x00 }, + { ADAU1761_CLK_ENABLE0, 0x00 }, + { ADAU1761_CLK_ENABLE1, 0x00 }, + { ADAU17X1_CLOCK_CONTROL, 0x00 }, + { ADAU17X1_PLL_CONTROL, 0x00 }, + { ADAU17X1_REC_POWER_MGMT, 0x00 }, + { ADAU17X1_MICBIAS, 0x00 }, + { ADAU17X1_SERIAL_PORT0, 0x00 }, + { ADAU17X1_SERIAL_PORT1, 0x00 }, + { ADAU17X1_CONVERTER0, 0x00 }, + { ADAU17X1_CONVERTER1, 0x00 }, + { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 }, + { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 }, + { ADAU17X1_ADC_CONTROL, 0x00 }, + { ADAU17X1_PLAY_POWER_MGMT, 0x00 }, + { ADAU17X1_DAC_CONTROL0, 0x00 }, + { ADAU17X1_DAC_CONTROL1, 0x00 }, + { ADAU17X1_DAC_CONTROL2, 0x00 }, + { ADAU17X1_SERIAL_PORT_PAD, 0xaa }, + { ADAU17X1_CONTROL_PORT_PAD0, 0xaa }, + { ADAU17X1_CONTROL_PORT_PAD1, 0x00 }, + { ADAU17X1_DSP_SAMPLING_RATE, 0x01 }, + { ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 }, + { ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 }, + { ADAU17X1_DSP_ENABLE, 0x00 }, + { ADAU17X1_DSP_RUN, 0x00 }, + { ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 }, +}; + +static const DECLARE_TLV_DB_SCALE(adau1761_sing_in_tlv, -1500, 300, 1); +static const DECLARE_TLV_DB_SCALE(adau1761_diff_in_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(adau1761_out_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(adau1761_sidetone_tlv, -1800, 300, 1); +static const DECLARE_TLV_DB_SCALE(adau1761_boost_tlv, -600, 600, 1); +static const DECLARE_TLV_DB_SCALE(adau1761_pga_boost_tlv, -2000, 2000, 1); + +static const DECLARE_TLV_DB_SCALE(adau1761_alc_max_gain_tlv, -1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(adau1761_alc_target_tlv, -2850, 150, 0); +static const DECLARE_TLV_DB_SCALE(adau1761_alc_ng_threshold_tlv, -7650, 150, 0); + +static const unsigned int adau1761_bias_select_values[] = { + 0, 2, 3, +}; + +static const char * const adau1761_bias_select_text[] = { + "Normal operation", "Enhanced performance", "Power saving", +}; + +static const char * const adau1761_bias_select_extreme_text[] = { + "Normal operation", "Extreme power saving", "Enhanced performance", + "Power saving", +}; + +static SOC_ENUM_SINGLE_DECL(adau1761_adc_bias_enum, + ADAU17X1_REC_POWER_MGMT, 3, adau1761_bias_select_extreme_text); +static SOC_ENUM_SINGLE_DECL(adau1761_hp_bias_enum, + ADAU17X1_PLAY_POWER_MGMT, 6, adau1761_bias_select_extreme_text); +static SOC_ENUM_SINGLE_DECL(adau1761_dac_bias_enum, + ADAU17X1_PLAY_POWER_MGMT, 4, adau1761_bias_select_extreme_text); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_playback_bias_enum, + ADAU17X1_PLAY_POWER_MGMT, 2, 0x3, adau1761_bias_select_text, + adau1761_bias_select_values); +static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_capture_bias_enum, + ADAU17X1_REC_POWER_MGMT, 1, 0x3, adau1761_bias_select_text, + adau1761_bias_select_values); + +static const unsigned int adau1761_pga_slew_time_values[] = { + 3, 0, 1, 2, +}; + +static const char * const adau1761_pga_slew_time_text[] = { + "Off", + "24 ms", + "48 ms", + "96 ms", +}; + +static const char * const adau1761_alc_function_text[] = { + "Off", + "Right", + "Left", + "Stereo", + "DSP control", +}; + +static const char * const adau1761_alc_hold_time_text[] = { + "2.67 ms", + "5.34 ms", + "10.68 ms", + "21.36 ms", + "42.72 ms", + "85.44 ms", + "170.88 ms", + "341.76 ms", + "683.52 ms", + "1367 ms", + "2734.1 ms", + "5468.2 ms", + "10936 ms", + "21873 ms", + "43745 ms", + "87491 ms", +}; + +static const char * const adau1761_alc_attack_time_text[] = { + "6 ms", + "12 ms", + "24 ms", + "48 ms", + "96 ms", + "192 ms", + "384 ms", + "768 ms", + "1540 ms", + "3070 ms", + "6140 ms", + "12290 ms", + "24580 ms", + "49150 ms", + "98300 ms", + "196610 ms", +}; + +static const char * const adau1761_alc_decay_time_text[] = { + "24 ms", + "48 ms", + "96 ms", + "192 ms", + "384 ms", + "768 ms", + "15400 ms", + "30700 ms", + "61400 ms", + "12290 ms", + "24580 ms", + "49150 ms", + "98300 ms", + "196610 ms", + "393220 ms", + "786430 ms", +}; + +static const char * const adau1761_alc_ng_type_text[] = { + "Hold", + "Mute", + "Fade", + "Fade + Mute", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adau1761_pga_slew_time_enum, + ADAU1761_ALC_CTRL0, 6, 0x3, adau1761_pga_slew_time_text, + adau1761_pga_slew_time_values); +static SOC_ENUM_SINGLE_DECL(adau1761_alc_function_enum, + ADAU1761_ALC_CTRL0, 0, adau1761_alc_function_text); +static SOC_ENUM_SINGLE_DECL(adau1761_alc_hold_time_enum, + ADAU1761_ALC_CTRL1, 4, adau1761_alc_hold_time_text); +static SOC_ENUM_SINGLE_DECL(adau1761_alc_attack_time_enum, + ADAU1761_ALC_CTRL2, 4, adau1761_alc_attack_time_text); +static SOC_ENUM_SINGLE_DECL(adau1761_alc_decay_time_enum, + ADAU1761_ALC_CTRL2, 0, adau1761_alc_decay_time_text); +static SOC_ENUM_SINGLE_DECL(adau1761_alc_ng_type_enum, + ADAU1761_ALC_CTRL3, 6, adau1761_alc_ng_type_text); + +static const struct snd_kcontrol_new adau1761_jack_detect_controls[] = { + SOC_SINGLE("Speaker Auto-mute Switch", ADAU1761_DIGMIC_JACKDETECT, + 4, 1, 0), +}; + +static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = { + SOC_DOUBLE_R_TLV("Capture Volume", ADAU1761_LEFT_DIFF_INPUT_VOL, + ADAU1761_RIGHT_DIFF_INPUT_VOL, 2, 0x3f, 0, + adau1761_diff_in_tlv), + SOC_DOUBLE_R("Capture Switch", ADAU1761_LEFT_DIFF_INPUT_VOL, + ADAU1761_RIGHT_DIFF_INPUT_VOL, 1, 1, 0), + + SOC_DOUBLE_R_TLV("PGA Boost Capture Volume", ADAU1761_REC_MIXER_LEFT1, + ADAU1761_REC_MIXER_RIGHT1, 3, 2, 0, adau1761_pga_boost_tlv), + + SOC_ENUM("PGA Capture Slew Time", adau1761_pga_slew_time_enum), + + SOC_SINGLE_TLV("ALC Capture Max Gain Volume", ADAU1761_ALC_CTRL0, + 3, 7, 0, adau1761_alc_max_gain_tlv), + SOC_ENUM("ALC Capture Function", adau1761_alc_function_enum), + SOC_ENUM("ALC Capture Hold Time", adau1761_alc_hold_time_enum), + SOC_SINGLE_TLV("ALC Capture Target Volume", ADAU1761_ALC_CTRL1, + 0, 15, 0, adau1761_alc_target_tlv), + SOC_ENUM("ALC Capture Attack Time", adau1761_alc_decay_time_enum), + SOC_ENUM("ALC Capture Decay Time", adau1761_alc_attack_time_enum), + SOC_ENUM("ALC Capture Noise Gate Type", adau1761_alc_ng_type_enum), + SOC_SINGLE("ALC Capture Noise Gate Switch", + ADAU1761_ALC_CTRL3, 5, 1, 0), + SOC_SINGLE_TLV("ALC Capture Noise Gate Threshold Volume", + ADAU1761_ALC_CTRL3, 0, 31, 0, adau1761_alc_ng_threshold_tlv), +}; + +static const struct snd_kcontrol_new adau1761_single_mode_controls[] = { + SOC_SINGLE_TLV("Input 1 Capture Volume", ADAU1761_REC_MIXER_LEFT0, + 4, 7, 0, adau1761_sing_in_tlv), + SOC_SINGLE_TLV("Input 2 Capture Volume", ADAU1761_REC_MIXER_LEFT0, + 1, 7, 0, adau1761_sing_in_tlv), + SOC_SINGLE_TLV("Input 3 Capture Volume", ADAU1761_REC_MIXER_RIGHT0, + 4, 7, 0, adau1761_sing_in_tlv), + SOC_SINGLE_TLV("Input 4 Capture Volume", ADAU1761_REC_MIXER_RIGHT0, + 1, 7, 0, adau1761_sing_in_tlv), +}; + +static const struct snd_kcontrol_new adau1761_controls[] = { + SOC_DOUBLE_R_TLV("Aux Capture Volume", ADAU1761_REC_MIXER_LEFT1, + ADAU1761_REC_MIXER_RIGHT1, 0, 7, 0, adau1761_sing_in_tlv), + + SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1761_PLAY_HP_LEFT_VOL, + ADAU1761_PLAY_HP_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv), + SOC_DOUBLE_R("Headphone Playback Switch", ADAU1761_PLAY_HP_LEFT_VOL, + ADAU1761_PLAY_HP_RIGHT_VOL, 1, 1, 0), + SOC_DOUBLE_R_TLV("Lineout Playback Volume", ADAU1761_PLAY_LINE_LEFT_VOL, + ADAU1761_PLAY_LINE_RIGHT_VOL, 2, 0x3f, 0, adau1761_out_tlv), + SOC_DOUBLE_R("Lineout Playback Switch", ADAU1761_PLAY_LINE_LEFT_VOL, + ADAU1761_PLAY_LINE_RIGHT_VOL, 1, 1, 0), + + SOC_ENUM("ADC Bias", adau1761_adc_bias_enum), + SOC_ENUM("DAC Bias", adau1761_dac_bias_enum), + SOC_ENUM("Capture Bias", adau1761_capture_bias_enum), + SOC_ENUM("Playback Bias", adau1761_playback_bias_enum), + SOC_ENUM("Headphone Bias", adau1761_hp_bias_enum), +}; + +static const struct snd_kcontrol_new adau1761_mono_controls[] = { + SOC_SINGLE_TLV("Mono Playback Volume", ADAU1761_PLAY_MONO_OUTPUT_VOL, + 2, 0x3f, 0, adau1761_out_tlv), + SOC_SINGLE("Mono Playback Switch", ADAU1761_PLAY_MONO_OUTPUT_VOL, + 1, 1, 0), +}; + +static const struct snd_kcontrol_new adau1761_left_mixer_controls[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch", + ADAU1761_PLAY_MIXER_LEFT0, 5, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch", + ADAU1761_PLAY_MIXER_LEFT0, 6, 1, 0), + SOC_DAPM_SINGLE_TLV("Aux Bypass Volume", + ADAU1761_PLAY_MIXER_LEFT0, 1, 8, 0, adau1761_sidetone_tlv), + SOC_DAPM_SINGLE_TLV("Right Bypass Volume", + ADAU1761_PLAY_MIXER_LEFT1, 4, 8, 0, adau1761_sidetone_tlv), + SOC_DAPM_SINGLE_TLV("Left Bypass Volume", + ADAU1761_PLAY_MIXER_LEFT1, 0, 8, 0, adau1761_sidetone_tlv), +}; + +static const struct snd_kcontrol_new adau1761_right_mixer_controls[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Left DAC Switch", + ADAU1761_PLAY_MIXER_RIGHT0, 5, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("Right DAC Switch", + ADAU1761_PLAY_MIXER_RIGHT0, 6, 1, 0), + SOC_DAPM_SINGLE_TLV("Aux Bypass Volume", + ADAU1761_PLAY_MIXER_RIGHT0, 1, 8, 0, adau1761_sidetone_tlv), + SOC_DAPM_SINGLE_TLV("Right Bypass Volume", + ADAU1761_PLAY_MIXER_RIGHT1, 4, 8, 0, adau1761_sidetone_tlv), + SOC_DAPM_SINGLE_TLV("Left Bypass Volume", + ADAU1761_PLAY_MIXER_RIGHT1, 0, 8, 0, adau1761_sidetone_tlv), +}; + +static const struct snd_kcontrol_new adau1761_left_lr_mixer_controls[] = { + SOC_DAPM_SINGLE_TLV("Left Volume", + ADAU1761_PLAY_LR_MIXER_LEFT, 1, 2, 0, adau1761_boost_tlv), + SOC_DAPM_SINGLE_TLV("Right Volume", + ADAU1761_PLAY_LR_MIXER_LEFT, 3, 2, 0, adau1761_boost_tlv), +}; + +static const struct snd_kcontrol_new adau1761_right_lr_mixer_controls[] = { + SOC_DAPM_SINGLE_TLV("Left Volume", + ADAU1761_PLAY_LR_MIXER_RIGHT, 1, 2, 0, adau1761_boost_tlv), + SOC_DAPM_SINGLE_TLV("Right Volume", + ADAU1761_PLAY_LR_MIXER_RIGHT, 3, 2, 0, adau1761_boost_tlv), +}; + +static const char * const adau1761_input_mux_text[] = { + "ADC", "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL(adau1761_input_mux_enum, + ADAU17X1_ADC_CONTROL, 2, adau1761_input_mux_text); + +static const struct snd_kcontrol_new adau1761_input_mux_control = + SOC_DAPM_ENUM("Input Select", adau1761_input_mux_enum); + +static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct adau *adau = snd_soc_component_get_drvdata(component); + + /* After any power changes have been made the dejitter circuit + * has to be reinitialized. */ + regmap_write(adau->regmap, ADAU1761_DEJITTER, 0); + if (!adau->master) + regmap_write(adau->regmap, ADAU1761_DEJITTER, 3); + + return 0; +} + +static const struct snd_soc_dapm_widget adau1x61_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Left Input Mixer", ADAU1761_REC_MIXER_LEFT0, 0, 0, + NULL, 0), + SND_SOC_DAPM_MIXER("Right Input Mixer", ADAU1761_REC_MIXER_RIGHT0, 0, 0, + NULL, 0), + + SOC_MIXER_ARRAY("Left Playback Mixer", ADAU1761_PLAY_MIXER_LEFT0, + 0, 0, adau1761_left_mixer_controls), + SOC_MIXER_ARRAY("Right Playback Mixer", ADAU1761_PLAY_MIXER_RIGHT0, + 0, 0, adau1761_right_mixer_controls), + SOC_MIXER_ARRAY("Left LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_LEFT, + 0, 0, adau1761_left_lr_mixer_controls), + SOC_MIXER_ARRAY("Right LR Playback Mixer", ADAU1761_PLAY_LR_MIXER_RIGHT, + 0, 0, adau1761_right_lr_mixer_controls), + + SND_SOC_DAPM_SUPPLY("Headphone", ADAU1761_PLAY_HP_LEFT_VOL, + 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("SYSCLK", 2, SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_POST("Dejitter fixup", adau1761_dejitter_fixup), + + SND_SOC_DAPM_INPUT("LAUX"), + SND_SOC_DAPM_INPUT("RAUX"), + SND_SOC_DAPM_INPUT("LINP"), + SND_SOC_DAPM_INPUT("LINN"), + SND_SOC_DAPM_INPUT("RINP"), + SND_SOC_DAPM_INPUT("RINN"), + + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), + SND_SOC_DAPM_OUTPUT("LHP"), + SND_SOC_DAPM_OUTPUT("RHP"), +}; + +static const struct snd_soc_dapm_widget adau1761_mono_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Mono Playback Mixer", ADAU1761_PLAY_MIXER_MONO, + 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("MONOOUT"), +}; + +static const struct snd_soc_dapm_widget adau1761_capless_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("Headphone VGND", 1, ADAU1761_PLAY_MIXER_MONO, + 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route adau1x61_dapm_routes[] = { + { "Left Input Mixer", NULL, "LINP" }, + { "Left Input Mixer", NULL, "LINN" }, + { "Left Input Mixer", NULL, "LAUX" }, + + { "Right Input Mixer", NULL, "RINP" }, + { "Right Input Mixer", NULL, "RINN" }, + { "Right Input Mixer", NULL, "RAUX" }, + + { "Left Playback Mixer", NULL, "Left Playback Enable"}, + { "Right Playback Mixer", NULL, "Right Playback Enable"}, + { "Left LR Playback Mixer", NULL, "Left Playback Enable"}, + { "Right LR Playback Mixer", NULL, "Right Playback Enable"}, + + { "Left Playback Mixer", "Left DAC Switch", "Left DAC" }, + { "Left Playback Mixer", "Right DAC Switch", "Right DAC" }, + + { "Right Playback Mixer", "Left DAC Switch", "Left DAC" }, + { "Right Playback Mixer", "Right DAC Switch", "Right DAC" }, + + { "Left LR Playback Mixer", "Left Volume", "Left Playback Mixer" }, + { "Left LR Playback Mixer", "Right Volume", "Right Playback Mixer" }, + + { "Right LR Playback Mixer", "Left Volume", "Left Playback Mixer" }, + { "Right LR Playback Mixer", "Right Volume", "Right Playback Mixer" }, + + { "LHP", NULL, "Left Playback Mixer" }, + { "RHP", NULL, "Right Playback Mixer" }, + + { "LHP", NULL, "Headphone" }, + { "RHP", NULL, "Headphone" }, + + { "LOUT", NULL, "Left LR Playback Mixer" }, + { "ROUT", NULL, "Right LR Playback Mixer" }, + + { "Left Playback Mixer", "Aux Bypass Volume", "LAUX" }, + { "Left Playback Mixer", "Left Bypass Volume", "Left Input Mixer" }, + { "Left Playback Mixer", "Right Bypass Volume", "Right Input Mixer" }, + { "Right Playback Mixer", "Aux Bypass Volume", "RAUX" }, + { "Right Playback Mixer", "Left Bypass Volume", "Left Input Mixer" }, + { "Right Playback Mixer", "Right Bypass Volume", "Right Input Mixer" }, +}; + +static const struct snd_soc_dapm_route adau1761_mono_dapm_routes[] = { + { "Mono Playback Mixer", NULL, "Left Playback Mixer" }, + { "Mono Playback Mixer", NULL, "Right Playback Mixer" }, + + { "MONOOUT", NULL, "Mono Playback Mixer" }, +}; + +static const struct snd_soc_dapm_route adau1761_capless_dapm_routes[] = { + { "Headphone", NULL, "Headphone VGND" }, +}; + +static const struct snd_soc_dapm_widget adau1761_dmic_widgets[] = { + SND_SOC_DAPM_MUX("Left Decimator Mux", SND_SOC_NOPM, 0, 0, + &adau1761_input_mux_control), + SND_SOC_DAPM_MUX("Right Decimator Mux", SND_SOC_NOPM, 0, 0, + &adau1761_input_mux_control), + + SND_SOC_DAPM_INPUT("DMIC"), +}; + +static const struct snd_soc_dapm_route adau1761_dmic_routes[] = { + { "Left Decimator Mux", "ADC", "Left Input Mixer" }, + { "Left Decimator Mux", "DMIC", "DMIC" }, + { "Right Decimator Mux", "ADC", "Right Input Mixer" }, + { "Right Decimator Mux", "DMIC", "DMIC" }, + + { "Left Decimator", NULL, "Left Decimator Mux" }, + { "Right Decimator", NULL, "Right Decimator Mux" }, +}; + +static const struct snd_soc_dapm_route adau1761_no_dmic_routes[] = { + { "Left Decimator", NULL, "Left Input Mixer" }, + { "Right Decimator", NULL, "Right Input Mixer" }, +}; + +static const struct snd_soc_dapm_widget adau1761_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Serial Port Clock", ADAU1761_CLK_ENABLE0, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Serial Input Routing Clock", ADAU1761_CLK_ENABLE0, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Serial Output Routing Clock", ADAU1761_CLK_ENABLE0, + 3, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Decimator Resync Clock", ADAU1761_CLK_ENABLE0, + 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Interpolator Resync Clock", ADAU1761_CLK_ENABLE0, + 2, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Slew Clock", ADAU1761_CLK_ENABLE0, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ALC Clock", ADAU1761_CLK_ENABLE0, 5, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("Digital Clock 0", 1, ADAU1761_CLK_ENABLE1, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Digital Clock 1", 1, ADAU1761_CLK_ENABLE1, + 1, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route adau1761_dapm_routes[] = { + { "Left Decimator", NULL, "Digital Clock 0", }, + { "Right Decimator", NULL, "Digital Clock 0", }, + { "Left DAC", NULL, "Digital Clock 0", }, + { "Right DAC", NULL, "Digital Clock 0", }, + + { "AIFCLK", NULL, "Digital Clock 1" }, + + { "Playback", NULL, "Serial Port Clock" }, + { "Capture", NULL, "Serial Port Clock" }, + { "Playback", NULL, "Serial Input Routing Clock" }, + { "Capture", NULL, "Serial Output Routing Clock" }, + + { "Left Decimator", NULL, "Decimator Resync Clock" }, + { "Right Decimator", NULL, "Decimator Resync Clock" }, + { "Left DAC", NULL, "Interpolator Resync Clock" }, + { "Right DAC", NULL, "Interpolator Resync Clock" }, + + { "Slew Clock", NULL, "Digital Clock 0" }, + { "Right Playback Mixer", NULL, "Slew Clock" }, + { "Left Playback Mixer", NULL, "Slew Clock" }, + + { "Left Input Mixer", NULL, "ALC Clock" }, + { "Right Input Mixer", NULL, "ALC Clock" }, + + { "Digital Clock 0", NULL, "SYSCLK" }, + { "Digital Clock 1", NULL, "SYSCLK" }, +}; + +static const struct snd_soc_dapm_route adau1761_dapm_dsp_routes[] = { + { "DSP", NULL, "Digital Clock 0" }, +}; + +static int adau1761_compatibility_probe(struct device *dev) +{ + struct adau *adau = dev_get_drvdata(dev); + struct regmap *regmap = adau->regmap; + int val, ret = 0; + + /* Only consider compatibility mode when ADAU1361 was specified. */ + if (adau->type != ADAU1361) + return 0; + + regcache_cache_bypass(regmap, true); + + /* + * This will enable the core clock and bypass the PLL, + * so that we can access the registers for probing purposes + * (without having to set up the PLL). + */ + regmap_write(regmap, ADAU17X1_CLOCK_CONTROL, + ADAU17X1_CLOCK_CONTROL_SYSCLK_EN); + + /* + * ADAU17X1_SERIAL_SAMPLING_RATE doesn't exist in non-DSP chips; + * reading it results in zero at all times, and write is a no-op. + * Use this register to probe for ADAU1761. + */ + regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 1); + ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val); + if (ret) + goto exit; + if (val != 1) + goto exit; + regmap_write(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, 0); + ret = regmap_read(regmap, ADAU17X1_SERIAL_SAMPLING_RATE, &val); + if (ret) + goto exit; + if (val != 0) + goto exit; + + adau->type = ADAU1761_AS_1361; +exit: + /* Disable core clock after probing. */ + regmap_write(regmap, ADAU17X1_CLOCK_CONTROL, 0); + regcache_cache_bypass(regmap, false); + return ret; +} + +static int adau1761_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adau *adau = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + regcache_cache_only(adau->regmap, false); + regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, + ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, + ADAU17X1_CLOCK_CONTROL_SYSCLK_EN); + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + regcache_sync(adau->regmap); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, + ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0); + regcache_cache_only(adau->regmap, true); + break; + + } + return 0; +} + +static enum adau1761_output_mode adau1761_get_lineout_mode( + struct snd_soc_component *component) +{ + struct adau1761_platform_data *pdata = component->dev->platform_data; + + if (pdata) + return pdata->lineout_mode; + + return ADAU1761_OUTPUT_MODE_LINE; +} + +static int adau1761_setup_digmic_jackdetect(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adau1761_platform_data *pdata = component->dev->platform_data; + struct adau *adau = snd_soc_component_get_drvdata(component); + enum adau1761_digmic_jackdet_pin_mode mode; + unsigned int val = 0; + int ret; + + if (pdata) + mode = pdata->digmic_jackdetect_pin_mode; + else + mode = ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE; + + switch (mode) { + case ADAU1761_DIGMIC_JACKDET_PIN_MODE_JACKDETECT: + switch (pdata->jackdetect_debounce_time) { + case ADAU1761_JACKDETECT_DEBOUNCE_5MS: + case ADAU1761_JACKDETECT_DEBOUNCE_10MS: + case ADAU1761_JACKDETECT_DEBOUNCE_20MS: + case ADAU1761_JACKDETECT_DEBOUNCE_40MS: + val |= pdata->jackdetect_debounce_time << 6; + break; + default: + return -EINVAL; + } + if (pdata->jackdetect_active_low) + val |= ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW; + + ret = snd_soc_add_component_controls(component, + adau1761_jack_detect_controls, + ARRAY_SIZE(adau1761_jack_detect_controls)); + if (ret) + return ret; + fallthrough; + case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: + ret = snd_soc_dapm_add_routes(dapm, adau1761_no_dmic_routes, + ARRAY_SIZE(adau1761_no_dmic_routes)); + if (ret) + return ret; + break; + case ADAU1761_DIGMIC_JACKDET_PIN_MODE_DIGMIC: + ret = snd_soc_dapm_new_controls(dapm, adau1761_dmic_widgets, + ARRAY_SIZE(adau1761_dmic_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, adau1761_dmic_routes, + ARRAY_SIZE(adau1761_dmic_routes)); + if (ret) + return ret; + + val |= ADAU1761_DIGMIC_JACKDETECT_DIGMIC; + break; + default: + return -EINVAL; + } + + regmap_write(adau->regmap, ADAU1761_DIGMIC_JACKDETECT, val); + + return 0; +} + +static int adau1761_setup_headphone_mode(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adau *adau = snd_soc_component_get_drvdata(component); + struct adau1761_platform_data *pdata = component->dev->platform_data; + enum adau1761_output_mode mode; + int ret; + + if (pdata) + mode = pdata->headphone_mode; + else + mode = ADAU1761_OUTPUT_MODE_HEADPHONE; + + switch (mode) { + case ADAU1761_OUTPUT_MODE_LINE: + break; + case ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS: + regmap_update_bits(adau->regmap, ADAU1761_PLAY_MONO_OUTPUT_VOL, + ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP | + ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE, + ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP | + ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE); + fallthrough; + case ADAU1761_OUTPUT_MODE_HEADPHONE: + regmap_update_bits(adau->regmap, ADAU1761_PLAY_HP_RIGHT_VOL, + ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP, + ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP); + break; + default: + return -EINVAL; + } + + if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) { + ret = snd_soc_dapm_new_controls(dapm, + adau1761_capless_dapm_widgets, + ARRAY_SIZE(adau1761_capless_dapm_widgets)); + if (ret) + return ret; + ret = snd_soc_dapm_add_routes(dapm, + adau1761_capless_dapm_routes, + ARRAY_SIZE(adau1761_capless_dapm_routes)); + } else { + ret = snd_soc_add_component_controls(component, adau1761_mono_controls, + ARRAY_SIZE(adau1761_mono_controls)); + if (ret) + return ret; + ret = snd_soc_dapm_new_controls(dapm, + adau1761_mono_dapm_widgets, + ARRAY_SIZE(adau1761_mono_dapm_widgets)); + if (ret) + return ret; + ret = snd_soc_dapm_add_routes(dapm, + adau1761_mono_dapm_routes, + ARRAY_SIZE(adau1761_mono_dapm_routes)); + } + + return ret; +} + +static bool adau1761_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADAU1761_DIGMIC_JACKDETECT: + case ADAU1761_REC_MIXER_LEFT0: + case ADAU1761_REC_MIXER_LEFT1: + case ADAU1761_REC_MIXER_RIGHT0: + case ADAU1761_REC_MIXER_RIGHT1: + case ADAU1761_LEFT_DIFF_INPUT_VOL: + case ADAU1761_RIGHT_DIFF_INPUT_VOL: + case ADAU1761_PLAY_LR_MIXER_LEFT: + case ADAU1761_PLAY_MIXER_LEFT0: + case ADAU1761_PLAY_MIXER_LEFT1: + case ADAU1761_PLAY_MIXER_RIGHT0: + case ADAU1761_PLAY_MIXER_RIGHT1: + case ADAU1761_PLAY_LR_MIXER_RIGHT: + case ADAU1761_PLAY_MIXER_MONO: + case ADAU1761_PLAY_HP_LEFT_VOL: + case ADAU1761_PLAY_HP_RIGHT_VOL: + case ADAU1761_PLAY_LINE_LEFT_VOL: + case ADAU1761_PLAY_LINE_RIGHT_VOL: + case ADAU1761_PLAY_MONO_OUTPUT_VOL: + case ADAU1761_POP_CLICK_SUPPRESS: + case ADAU1761_JACK_DETECT_PIN: + case ADAU1761_DEJITTER: + case ADAU1761_CLK_ENABLE0: + case ADAU1761_CLK_ENABLE1: + case ADAU1761_ALC_CTRL0: + case ADAU1761_ALC_CTRL1: + case ADAU1761_ALC_CTRL2: + case ADAU1761_ALC_CTRL3: + return true; + default: + break; + } + + return adau17x1_readable_register(dev, reg); +} + +static int adau1761_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adau1761_platform_data *pdata = component->dev->platform_data; + struct adau *adau = snd_soc_component_get_drvdata(component); + int ret; + + ret = adau17x1_add_widgets(component); + if (ret < 0) + return ret; + + if (pdata && pdata->input_differential) { + regmap_update_bits(adau->regmap, ADAU1761_LEFT_DIFF_INPUT_VOL, + ADAU1761_DIFF_INPUT_VOL_LDEN, + ADAU1761_DIFF_INPUT_VOL_LDEN); + regmap_update_bits(adau->regmap, ADAU1761_RIGHT_DIFF_INPUT_VOL, + ADAU1761_DIFF_INPUT_VOL_LDEN, + ADAU1761_DIFF_INPUT_VOL_LDEN); + ret = snd_soc_add_component_controls(component, + adau1761_differential_mode_controls, + ARRAY_SIZE(adau1761_differential_mode_controls)); + if (ret) + return ret; + } else { + ret = snd_soc_add_component_controls(component, + adau1761_single_mode_controls, + ARRAY_SIZE(adau1761_single_mode_controls)); + if (ret) + return ret; + } + + switch (adau1761_get_lineout_mode(component)) { + case ADAU1761_OUTPUT_MODE_LINE: + break; + case ADAU1761_OUTPUT_MODE_HEADPHONE: + regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_LEFT_VOL, + ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP, + ADAU1761_PLAY_LINE_LEFT_VOL_MODE_HP); + regmap_update_bits(adau->regmap, ADAU1761_PLAY_LINE_RIGHT_VOL, + ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP, + ADAU1761_PLAY_LINE_RIGHT_VOL_MODE_HP); + break; + default: + return -EINVAL; + } + + ret = adau1761_setup_headphone_mode(component); + if (ret) + return ret; + + ret = adau1761_setup_digmic_jackdetect(component); + if (ret) + return ret; + + /* + * If we've got an ADAU1761, or an ADAU1761 operating as an + * ADAU1361, we need these non-DSP related DAPM widgets and routes. + */ + if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361) { + ret = snd_soc_dapm_new_controls(dapm, adau1761_dapm_widgets, + ARRAY_SIZE(adau1761_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_routes, + ARRAY_SIZE(adau1761_dapm_routes)); + if (ret) + return ret; + } + /* + * These routes are DSP related and only used when we have a + * bona fide ADAU1761. + */ + if (adau->type == ADAU1761) { + ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_dsp_routes, + ARRAY_SIZE(adau1761_dapm_dsp_routes)); + if (ret) + return ret; + } + /* + * In the ADAU1761, by default, the AIF is routed to the DSP, whereas + * for the ADAU1361, the AIF is permanently routed to the ADC and DAC. + * Thus, if we have an ADAU1761 masquerading as an ADAU1361, + * we need to explicitly route the AIF to the ADC and DAC. + * For the ADAU1761, this is normally done by set_tdm_slot, but this + * function is not necessarily called during stream setup, so set up + * the compatible AIF routings here from the start. + */ + if (adau->type == ADAU1761_AS_1361) { + regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE, 0x01); + regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x01); + } + ret = adau17x1_add_routes(component); + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver adau1761_component_driver = { + .probe = adau1761_component_probe, + .resume = adau17x1_resume, + .set_bias_level = adau1761_set_bias_level, + .controls = adau1761_controls, + .num_controls = ARRAY_SIZE(adau1761_controls), + .dapm_widgets = adau1x61_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1x61_dapm_widgets), + .dapm_routes = adau1x61_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1x61_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +#define ADAU1761_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver adau1361_dai_driver = { + .name = "adau-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = ADAU1761_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = ADAU1761_FORMATS, + }, + .ops = &adau17x1_dai_ops, +}; + +static struct snd_soc_dai_driver adau1761_dai_driver = { + .name = "adau-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = ADAU1761_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = ADAU1761_FORMATS, + }, + .ops = &adau17x1_dai_ops, +}; + +int adau1761_probe(struct device *dev, struct regmap *regmap, + enum adau17x1_type type, void (*switch_mode)(struct device *dev)) +{ + struct snd_soc_dai_driver *dai_drv; + const char *firmware_name; + int ret; + + if (type == ADAU1361) { + dai_drv = &adau1361_dai_driver; + firmware_name = NULL; + } else { + dai_drv = &adau1761_dai_driver; + firmware_name = ADAU1761_FIRMWARE; + } + + ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name); + if (ret) + return ret; + + ret = adau1761_compatibility_probe(dev); + if (ret) + return ret; + + /* Enable cache only mode as we could miss writes before bias level + * reaches standby and the core clock is enabled */ + regcache_cache_only(regmap, true); + + return devm_snd_soc_register_component(dev, &adau1761_component_driver, + dai_drv, 1); +} +EXPORT_SYMBOL_GPL(adau1761_probe); + +const struct regmap_config adau1761_regmap_config = { + .val_bits = 8, + .reg_bits = 16, + .max_register = 0x40fa, + .reg_defaults = adau1761_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1761_reg_defaults), + .readable_reg = adau1761_readable_register, + .volatile_reg = adau17x1_volatile_register, + .precious_reg = adau17x1_precious_register, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_GPL(adau1761_regmap_config); + +MODULE_DESCRIPTION("ASoC ADAU1361/ADAU1461/ADAU1761/ADAU1961 CODEC driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1761.h b/sound/soc/codecs/adau1761.h new file mode 100644 index 0000000000..7beabf448a --- /dev/null +++ b/sound/soc/codecs/adau1761.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADAU1361/ADAU1461/ADAU1761/ADAU1961 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#ifndef __SOUND_SOC_CODECS_ADAU1761_H__ +#define __SOUND_SOC_CODECS_ADAU1761_H__ + +#include +#include "adau17x1.h" + +struct device; + +int adau1761_probe(struct device *dev, struct regmap *regmap, + enum adau17x1_type type, void (*switch_mode)(struct device *dev)); + +extern const struct regmap_config adau1761_regmap_config; + +#endif diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c new file mode 100644 index 0000000000..3a170fd78f --- /dev/null +++ b/sound/soc/codecs/adau1781-i2c.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ADAU1381/ADAU1781 CODEC + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include + +#include "adau1781.h" + +static const struct i2c_device_id adau1781_i2c_ids[]; + +static int adau1781_i2c_probe(struct i2c_client *client) +{ + struct regmap_config config; + const struct i2c_device_id *id = i2c_match_id(adau1781_i2c_ids, client); + + config = adau1781_regmap_config; + config.val_bits = 8; + config.reg_bits = 16; + + return adau1781_probe(&client->dev, + devm_regmap_init_i2c(client, &config), + id->driver_data, NULL); +} + +static void adau1781_i2c_remove(struct i2c_client *client) +{ + adau17x1_remove(&client->dev); +} + +static const struct i2c_device_id adau1781_i2c_ids[] = { + { "adau1381", ADAU1381 }, + { "adau1781", ADAU1781 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids); + +#if defined(CONFIG_OF) +static const struct of_device_id adau1781_i2c_dt_ids[] = { + { .compatible = "adi,adau1381", }, + { .compatible = "adi,adau1781", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1781_i2c_dt_ids); +#endif + +static struct i2c_driver adau1781_i2c_driver = { + .driver = { + .name = "adau1781", + .of_match_table = of_match_ptr(adau1781_i2c_dt_ids), + }, + .probe = adau1781_i2c_probe, + .remove = adau1781_i2c_remove, + .id_table = adau1781_i2c_ids, +}; +module_i2c_driver(adau1781_i2c_driver); + +MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC I2C driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c new file mode 100644 index 0000000000..1a09633d5a --- /dev/null +++ b/sound/soc/codecs/adau1781-spi.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ADAU1381/ADAU1781 CODEC + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include + +#include "adau1781.h" + +static void adau1781_spi_switch_mode(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + /* + * To get the device into SPI mode CLATCH has to be pulled low three + * times. Do this by issuing three dummy reads. + */ + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); +} + +static int adau1781_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap_config config; + + if (!id) + return -EINVAL; + + config = adau1781_regmap_config; + config.val_bits = 8; + config.reg_bits = 24; + config.read_flag_mask = 0x1; + + return adau1781_probe(&spi->dev, + devm_regmap_init_spi(spi, &config), + id->driver_data, adau1781_spi_switch_mode); +} + +static void adau1781_spi_remove(struct spi_device *spi) +{ + adau17x1_remove(&spi->dev); +} + +static const struct spi_device_id adau1781_spi_id[] = { + { "adau1381", ADAU1381 }, + { "adau1781", ADAU1781 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adau1781_spi_id); + +#if defined(CONFIG_OF) +static const struct of_device_id adau1781_spi_dt_ids[] = { + { .compatible = "adi,adau1381", }, + { .compatible = "adi,adau1781", }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1781_spi_dt_ids); +#endif + +static struct spi_driver adau1781_spi_driver = { + .driver = { + .name = "adau1781", + .of_match_table = of_match_ptr(adau1781_spi_dt_ids), + }, + .probe = adau1781_spi_probe, + .remove = adau1781_spi_remove, + .id_table = adau1781_spi_id, +}; +module_spi_driver(adau1781_spi_driver); + +MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 CODEC SPI driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c new file mode 100644 index 0000000000..faad2f9f8d --- /dev/null +++ b/sound/soc/codecs/adau1781.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for ADAU1381/ADAU1781 codec + * + * Copyright 2011-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adau17x1.h" +#include "adau1781.h" + +#define ADAU1781_DMIC_BEEP_CTRL 0x4008 +#define ADAU1781_LEFT_PGA 0x400e +#define ADAU1781_RIGHT_PGA 0x400f +#define ADAU1781_LEFT_PLAYBACK_MIXER 0x401c +#define ADAU1781_RIGHT_PLAYBACK_MIXER 0x401e +#define ADAU1781_MONO_PLAYBACK_MIXER 0x401f +#define ADAU1781_LEFT_LINEOUT 0x4025 +#define ADAU1781_RIGHT_LINEOUT 0x4026 +#define ADAU1781_SPEAKER 0x4027 +#define ADAU1781_BEEP_ZC 0x4028 +#define ADAU1781_DEJITTER 0x4032 +#define ADAU1781_DIG_PWDN0 0x4080 +#define ADAU1781_DIG_PWDN1 0x4081 + +#define ADAU1781_INPUT_DIFFERNTIAL BIT(3) + +#define ADAU1381_FIRMWARE "adau1381.bin" +#define ADAU1781_FIRMWARE "adau1781.bin" + +static const struct reg_default adau1781_reg_defaults[] = { + { ADAU1781_DMIC_BEEP_CTRL, 0x00 }, + { ADAU1781_LEFT_PGA, 0xc7 }, + { ADAU1781_RIGHT_PGA, 0xc7 }, + { ADAU1781_LEFT_PLAYBACK_MIXER, 0x00 }, + { ADAU1781_RIGHT_PLAYBACK_MIXER, 0x00 }, + { ADAU1781_MONO_PLAYBACK_MIXER, 0x00 }, + { ADAU1781_LEFT_LINEOUT, 0x00 }, + { ADAU1781_RIGHT_LINEOUT, 0x00 }, + { ADAU1781_SPEAKER, 0x00 }, + { ADAU1781_BEEP_ZC, 0x19 }, + { ADAU1781_DEJITTER, 0x60 }, + { ADAU1781_DIG_PWDN1, 0x0c }, + { ADAU1781_DIG_PWDN1, 0x00 }, + { ADAU17X1_CLOCK_CONTROL, 0x00 }, + { ADAU17X1_PLL_CONTROL, 0x00 }, + { ADAU17X1_REC_POWER_MGMT, 0x00 }, + { ADAU17X1_MICBIAS, 0x04 }, + { ADAU17X1_SERIAL_PORT0, 0x00 }, + { ADAU17X1_SERIAL_PORT1, 0x00 }, + { ADAU17X1_CONVERTER0, 0x00 }, + { ADAU17X1_CONVERTER1, 0x00 }, + { ADAU17X1_LEFT_INPUT_DIGITAL_VOL, 0x00 }, + { ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, 0x00 }, + { ADAU17X1_ADC_CONTROL, 0x00 }, + { ADAU17X1_PLAY_POWER_MGMT, 0x00 }, + { ADAU17X1_DAC_CONTROL0, 0x00 }, + { ADAU17X1_DAC_CONTROL1, 0x00 }, + { ADAU17X1_DAC_CONTROL2, 0x00 }, + { ADAU17X1_SERIAL_PORT_PAD, 0x00 }, + { ADAU17X1_CONTROL_PORT_PAD0, 0x00 }, + { ADAU17X1_CONTROL_PORT_PAD1, 0x00 }, + { ADAU17X1_DSP_SAMPLING_RATE, 0x01 }, + { ADAU17X1_SERIAL_INPUT_ROUTE, 0x00 }, + { ADAU17X1_SERIAL_OUTPUT_ROUTE, 0x00 }, + { ADAU17X1_DSP_ENABLE, 0x00 }, + { ADAU17X1_DSP_RUN, 0x00 }, + { ADAU17X1_SERIAL_SAMPLING_RATE, 0x00 }, +}; + +static const DECLARE_TLV_DB_SCALE(adau1781_speaker_tlv, 0, 200, 0); + +static const DECLARE_TLV_DB_RANGE(adau1781_pga_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 600, 0), + 2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0), + 4, 4, TLV_DB_SCALE_ITEM(1700, 0, 0), + 5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0) +); + +static const DECLARE_TLV_DB_RANGE(adau1781_beep_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 600, 0), + 2, 3, TLV_DB_SCALE_ITEM(1000, 400, 0), + 4, 4, TLV_DB_SCALE_ITEM(-2300, 0, 0), + 5, 7, TLV_DB_SCALE_ITEM(2000, 600, 0) +); + +static const DECLARE_TLV_DB_SCALE(adau1781_sidetone_tlv, -1800, 300, 1); + +static const char * const adau1781_speaker_bias_select_text[] = { + "Normal operation", "Power saving", "Enhanced performance", +}; + +static const char * const adau1781_bias_select_text[] = { + "Normal operation", "Extreme power saving", "Power saving", + "Enhanced performance", +}; + +static SOC_ENUM_SINGLE_DECL(adau1781_adc_bias_enum, + ADAU17X1_REC_POWER_MGMT, 3, adau1781_bias_select_text); +static SOC_ENUM_SINGLE_DECL(adau1781_speaker_bias_enum, + ADAU17X1_PLAY_POWER_MGMT, 6, adau1781_speaker_bias_select_text); +static SOC_ENUM_SINGLE_DECL(adau1781_dac_bias_enum, + ADAU17X1_PLAY_POWER_MGMT, 4, adau1781_bias_select_text); +static SOC_ENUM_SINGLE_DECL(adau1781_playback_bias_enum, + ADAU17X1_PLAY_POWER_MGMT, 2, adau1781_bias_select_text); +static SOC_ENUM_SINGLE_DECL(adau1781_capture_bias_enum, + ADAU17X1_REC_POWER_MGMT, 1, adau1781_bias_select_text); + +static const struct snd_kcontrol_new adau1781_controls[] = { + SOC_SINGLE_TLV("Beep Capture Volume", ADAU1781_DMIC_BEEP_CTRL, 0, 7, 0, + adau1781_beep_tlv), + SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAU1781_LEFT_PGA, + ADAU1781_RIGHT_PGA, 5, 7, 0, adau1781_pga_tlv), + SOC_DOUBLE_R("PGA Capture Switch", ADAU1781_LEFT_PGA, + ADAU1781_RIGHT_PGA, 1, 1, 0), + + SOC_DOUBLE_R("Lineout Playback Switch", ADAU1781_LEFT_LINEOUT, + ADAU1781_RIGHT_LINEOUT, 1, 1, 0), + SOC_SINGLE("Beep ZC Switch", ADAU1781_BEEP_ZC, 0, 1, 0), + + SOC_SINGLE("Mono Playback Switch", ADAU1781_MONO_PLAYBACK_MIXER, + 0, 1, 0), + SOC_SINGLE_TLV("Mono Playback Volume", ADAU1781_SPEAKER, 6, 3, 0, + adau1781_speaker_tlv), + + SOC_ENUM("ADC Bias", adau1781_adc_bias_enum), + SOC_ENUM("DAC Bias", adau1781_dac_bias_enum), + SOC_ENUM("Capture Bias", adau1781_capture_bias_enum), + SOC_ENUM("Playback Bias", adau1781_playback_bias_enum), + SOC_ENUM("Speaker Bias", adau1781_speaker_bias_enum), +}; + +static const struct snd_kcontrol_new adau1781_beep_mixer_controls[] = { + SOC_DAPM_SINGLE("Beep Capture Switch", ADAU1781_DMIC_BEEP_CTRL, + 3, 1, 0), +}; + +static const struct snd_kcontrol_new adau1781_left_mixer_controls[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + ADAU1781_LEFT_PLAYBACK_MIXER, 5, 1, 0), + SOC_DAPM_SINGLE_TLV("Beep Playback Volume", + ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv), +}; + +static const struct snd_kcontrol_new adau1781_right_mixer_controls[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + ADAU1781_RIGHT_PLAYBACK_MIXER, 6, 1, 0), + SOC_DAPM_SINGLE_TLV("Beep Playback Volume", + ADAU1781_LEFT_PLAYBACK_MIXER, 1, 8, 0, adau1781_sidetone_tlv), +}; + +static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Left Switch", + ADAU1781_MONO_PLAYBACK_MIXER, 7, 1, 0), + SOC_DAPM_SINGLE_AUTODISABLE("Right Switch", + ADAU1781_MONO_PLAYBACK_MIXER, 6, 1, 0), + SOC_DAPM_SINGLE_TLV("Beep Playback Volume", + ADAU1781_MONO_PLAYBACK_MIXER, 2, 8, 0, adau1781_sidetone_tlv), +}; + +static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct adau *adau = snd_soc_component_get_drvdata(component); + + /* After any power changes have been made the dejitter circuit + * has to be reinitialized. */ + regmap_write(adau->regmap, ADAU1781_DEJITTER, 0); + if (!adau->master) + regmap_write(adau->regmap, ADAU1781_DEJITTER, 5); + + return 0; +} + +static const struct snd_soc_dapm_widget adau1781_dapm_widgets[] = { + SND_SOC_DAPM_PGA("Left PGA", ADAU1781_LEFT_PGA, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right PGA", ADAU1781_RIGHT_PGA, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("Speaker", ADAU1781_SPEAKER, 0, 0, NULL, 0), + + SOC_MIXER_NAMED_CTL_ARRAY("Beep Mixer", ADAU17X1_MICBIAS, 4, 0, + adau1781_beep_mixer_controls), + + SOC_MIXER_ARRAY("Left Lineout Mixer", SND_SOC_NOPM, 0, 0, + adau1781_left_mixer_controls), + SOC_MIXER_ARRAY("Right Lineout Mixer", SND_SOC_NOPM, 0, 0, + adau1781_right_mixer_controls), + SOC_MIXER_ARRAY("Mono Mixer", SND_SOC_NOPM, 0, 0, + adau1781_mono_mixer_controls), + + SND_SOC_DAPM_SUPPLY("Serial Input Routing", ADAU1781_DIG_PWDN0, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Serial Output Routing", ADAU1781_DIG_PWDN0, + 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Clock Domain Transfer", ADAU1781_DIG_PWDN0, + 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Serial Ports", ADAU1781_DIG_PWDN0, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Engine", ADAU1781_DIG_PWDN0, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Engine", ADAU1781_DIG_PWDN1, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Digital Mic", ADAU1781_DIG_PWDN1, 1, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Sound Engine", ADAU1781_DIG_PWDN0, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, ADAU1781_DIG_PWDN0, 1, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Zero Crossing Detector", ADAU1781_DIG_PWDN1, 2, 0, + NULL, 0), + + SND_SOC_DAPM_POST("Dejitter fixup", adau1781_dejitter_fixup), + + SND_SOC_DAPM_INPUT("BEEP"), + + SND_SOC_DAPM_OUTPUT("AOUTL"), + SND_SOC_DAPM_OUTPUT("AOUTR"), + SND_SOC_DAPM_OUTPUT("SP"), + SND_SOC_DAPM_INPUT("LMIC"), + SND_SOC_DAPM_INPUT("RMIC"), +}; + +static const struct snd_soc_dapm_route adau1781_dapm_routes[] = { + { "Left Lineout Mixer", NULL, "Left Playback Enable" }, + { "Right Lineout Mixer", NULL, "Right Playback Enable" }, + + { "Left Lineout Mixer", "Beep Playback Volume", "Beep Mixer" }, + { "Left Lineout Mixer", "Switch", "Left DAC" }, + + { "Right Lineout Mixer", "Beep Playback Volume", "Beep Mixer" }, + { "Right Lineout Mixer", "Switch", "Right DAC" }, + + { "Mono Mixer", "Beep Playback Volume", "Beep Mixer" }, + { "Mono Mixer", "Right Switch", "Right DAC" }, + { "Mono Mixer", "Left Switch", "Left DAC" }, + { "Speaker", NULL, "Mono Mixer" }, + + { "Mono Mixer", NULL, "SYSCLK" }, + { "Left Lineout Mixer", NULL, "SYSCLK" }, + { "Left Lineout Mixer", NULL, "SYSCLK" }, + + { "Beep Mixer", "Beep Capture Switch", "BEEP" }, + { "Beep Mixer", NULL, "Zero Crossing Detector" }, + + { "Left DAC", NULL, "DAC Engine" }, + { "Right DAC", NULL, "DAC Engine" }, + + { "Sound Engine", NULL, "SYSCLK" }, + { "DSP", NULL, "Sound Engine" }, + + { "Left Decimator", NULL, "ADC Engine" }, + { "Right Decimator", NULL, "ADC Engine" }, + + { "AIFCLK", NULL, "SYSCLK" }, + + { "Playback", NULL, "Serial Input Routing" }, + { "Playback", NULL, "Serial Ports" }, + { "Playback", NULL, "Clock Domain Transfer" }, + { "Capture", NULL, "Serial Output Routing" }, + { "Capture", NULL, "Serial Ports" }, + { "Capture", NULL, "Clock Domain Transfer" }, + + { "AOUTL", NULL, "Left Lineout Mixer" }, + { "AOUTR", NULL, "Right Lineout Mixer" }, + { "SP", NULL, "Speaker" }, +}; + +static const struct snd_soc_dapm_route adau1781_adc_dapm_routes[] = { + { "Left PGA", NULL, "LMIC" }, + { "Right PGA", NULL, "RMIC" }, + + { "Left Decimator", NULL, "Left PGA" }, + { "Right Decimator", NULL, "Right PGA" }, +}; + +static const char * const adau1781_dmic_select_text[] = { + "DMIC1", "DMIC2", +}; + +static SOC_ENUM_SINGLE_VIRT_DECL(adau1781_dmic_select_enum, + adau1781_dmic_select_text); + +static const struct snd_kcontrol_new adau1781_dmic_mux = + SOC_DAPM_ENUM("DMIC Select", adau1781_dmic_select_enum); + +static const struct snd_soc_dapm_widget adau1781_dmic_dapm_widgets[] = { + SND_SOC_DAPM_MUX("DMIC Select", SND_SOC_NOPM, 0, 0, &adau1781_dmic_mux), + + SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1781_DMIC_BEEP_CTRL, 4, 0), + SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1781_DMIC_BEEP_CTRL, 5, 0), +}; + +static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = { + { "DMIC1", NULL, "LMIC" }, + { "DMIC2", NULL, "RMIC" }, + + { "DMIC1", NULL, "Digital Mic" }, + { "DMIC2", NULL, "Digital Mic" }, + + { "DMIC Select", "DMIC1", "DMIC1" }, + { "DMIC Select", "DMIC2", "DMIC2" }, + + { "Left Decimator", NULL, "DMIC Select" }, + { "Right Decimator", NULL, "DMIC Select" }, +}; + +static int adau1781_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adau *adau = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, + ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, + ADAU17X1_CLOCK_CONTROL_SYSCLK_EN); + + /* Precharge */ + regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0x8, 0x8); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(adau->regmap, ADAU1781_DIG_PWDN1, 0xc, 0x0); + regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, + ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0); + break; + } + + return 0; +} + +static bool adau1781_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADAU1781_DMIC_BEEP_CTRL: + case ADAU1781_LEFT_PGA: + case ADAU1781_RIGHT_PGA: + case ADAU1781_LEFT_PLAYBACK_MIXER: + case ADAU1781_RIGHT_PLAYBACK_MIXER: + case ADAU1781_MONO_PLAYBACK_MIXER: + case ADAU1781_LEFT_LINEOUT: + case ADAU1781_RIGHT_LINEOUT: + case ADAU1781_SPEAKER: + case ADAU1781_BEEP_ZC: + case ADAU1781_DEJITTER: + case ADAU1781_DIG_PWDN0: + case ADAU1781_DIG_PWDN1: + return true; + default: + break; + } + + return adau17x1_readable_register(dev, reg); +} + +static int adau1781_set_input_mode(struct adau *adau, unsigned int reg, + bool differential) +{ + unsigned int val; + + if (differential) + val = ADAU1781_INPUT_DIFFERNTIAL; + else + val = 0; + + return regmap_update_bits(adau->regmap, reg, + ADAU1781_INPUT_DIFFERNTIAL, val); +} + +static int adau1781_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adau1781_platform_data *pdata = dev_get_platdata(component->dev); + struct adau *adau = snd_soc_component_get_drvdata(component); + int ret; + + ret = adau17x1_add_widgets(component); + if (ret) + return ret; + + if (pdata) { + ret = adau1781_set_input_mode(adau, ADAU1781_LEFT_PGA, + pdata->left_input_differential); + if (ret) + return ret; + ret = adau1781_set_input_mode(adau, ADAU1781_RIGHT_PGA, + pdata->right_input_differential); + if (ret) + return ret; + } + + if (pdata && pdata->use_dmic) { + ret = snd_soc_dapm_new_controls(dapm, + adau1781_dmic_dapm_widgets, + ARRAY_SIZE(adau1781_dmic_dapm_widgets)); + if (ret) + return ret; + ret = snd_soc_dapm_add_routes(dapm, adau1781_dmic_dapm_routes, + ARRAY_SIZE(adau1781_dmic_dapm_routes)); + if (ret) + return ret; + } else { + ret = snd_soc_dapm_add_routes(dapm, adau1781_adc_dapm_routes, + ARRAY_SIZE(adau1781_adc_dapm_routes)); + if (ret) + return ret; + } + + ret = adau17x1_add_routes(component); + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver adau1781_component_driver = { + .probe = adau1781_component_probe, + .resume = adau17x1_resume, + .set_bias_level = adau1781_set_bias_level, + .controls = adau1781_controls, + .num_controls = ARRAY_SIZE(adau1781_controls), + .dapm_widgets = adau1781_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets), + .dapm_routes = adau1781_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver adau1781_dai_driver = { + .name = "adau-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = ADAU1781_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = ADAU1781_FORMATS, + }, + .ops = &adau17x1_dai_ops, +}; + +const struct regmap_config adau1781_regmap_config = { + .val_bits = 8, + .reg_bits = 16, + .max_register = 0x40f8, + .reg_defaults = adau1781_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1781_reg_defaults), + .readable_reg = adau1781_readable_register, + .volatile_reg = adau17x1_volatile_register, + .precious_reg = adau17x1_precious_register, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_GPL(adau1781_regmap_config); + +int adau1781_probe(struct device *dev, struct regmap *regmap, + enum adau17x1_type type, void (*switch_mode)(struct device *dev)) +{ + const char *firmware_name; + int ret; + + switch (type) { + case ADAU1381: + firmware_name = ADAU1381_FIRMWARE; + break; + case ADAU1781: + firmware_name = ADAU1781_FIRMWARE; + break; + default: + return -EINVAL; + } + + ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name); + if (ret) + return ret; + + return devm_snd_soc_register_component(dev, &adau1781_component_driver, + &adau1781_dai_driver, 1); +} +EXPORT_SYMBOL_GPL(adau1781_probe); + +MODULE_DESCRIPTION("ASoC ADAU1381/ADAU1781 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1781.h b/sound/soc/codecs/adau1781.h new file mode 100644 index 0000000000..ac8b8acbdb --- /dev/null +++ b/sound/soc/codecs/adau1781.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADAU1381/ADAU1781 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#ifndef __SOUND_SOC_CODECS_ADAU1781_H__ +#define __SOUND_SOC_CODECS_ADAU1781_H__ + +#include +#include "adau17x1.h" + +struct device; + +int adau1781_probe(struct device *dev, struct regmap *regmap, + enum adau17x1_type type, void (*switch_mode)(struct device *dev)); + +extern const struct regmap_config adau1781_regmap_config; + +#endif diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c new file mode 100644 index 0000000000..f2932713b4 --- /dev/null +++ b/sound/soc/codecs/adau17x1.c @@ -0,0 +1,1123 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Common code for ADAU1X61 and ADAU1X81 codecs + * + * Copyright 2011-2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sigmadsp.h" +#include "adau17x1.h" +#include "adau-utils.h" + +#define ADAU17X1_SAFELOAD_TARGET_ADDRESS 0x0006 +#define ADAU17X1_SAFELOAD_TRIGGER 0x0007 +#define ADAU17X1_SAFELOAD_DATA 0x0001 +#define ADAU17X1_SAFELOAD_DATA_SIZE 20 +#define ADAU17X1_WORD_SIZE 4 + +static const char * const adau17x1_capture_mixer_boost_text[] = { + "Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3", +}; + +static SOC_ENUM_SINGLE_DECL(adau17x1_capture_boost_enum, + ADAU17X1_REC_POWER_MGMT, 5, adau17x1_capture_mixer_boost_text); + +static const char * const adau17x1_mic_bias_mode_text[] = { + "Normal operation", "High performance", +}; + +static SOC_ENUM_SINGLE_DECL(adau17x1_mic_bias_mode_enum, + ADAU17X1_MICBIAS, 3, adau17x1_mic_bias_mode_text); + +static const DECLARE_TLV_DB_MINMAX(adau17x1_digital_tlv, -9563, 0); + +static const struct snd_kcontrol_new adau17x1_controls[] = { + SOC_DOUBLE_R_TLV("Digital Capture Volume", + ADAU17X1_LEFT_INPUT_DIGITAL_VOL, + ADAU17X1_RIGHT_INPUT_DIGITAL_VOL, + 0, 0xff, 1, adau17x1_digital_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume", ADAU17X1_DAC_CONTROL1, + ADAU17X1_DAC_CONTROL2, 0, 0xff, 1, adau17x1_digital_tlv), + + SOC_SINGLE("ADC High Pass Filter Switch", ADAU17X1_ADC_CONTROL, + 5, 1, 0), + SOC_SINGLE("Playback De-emphasis Switch", ADAU17X1_DAC_CONTROL0, + 2, 1, 0), + + SOC_ENUM("Capture Boost", adau17x1_capture_boost_enum), + + SOC_ENUM("Mic Bias Mode", adau17x1_mic_bias_mode_enum), +}; + +static int adau17x1_setup_firmware(struct snd_soc_component *component, + unsigned int rate); + +static int adau17x1_pll_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct adau *adau = snd_soc_component_get_drvdata(component); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + adau->pll_regs[5] = 1; + } else { + adau->pll_regs[5] = 0; + /* Bypass the PLL when disabled, otherwise registers will become + * inaccessible. */ + regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, + ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL, 0); + } + + /* The PLL register is 6 bytes long and can only be written at once. */ + regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL, + adau->pll_regs, ARRAY_SIZE(adau->pll_regs)); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + mdelay(5); + regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, + ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL, + ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL); + } + + return 0; +} + +static int adau17x1_adc_fixup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct adau *adau = snd_soc_component_get_drvdata(component); + + /* + * If we are capturing, toggle the ADOSR bit in Converter Control 0 to + * avoid losing SNR (workaround from ADI). This must be done after + * the ADC(s) have been enabled. According to the data sheet, it is + * normally illegal to set this bit when the sampling rate is 96 kHz, + * but according to ADI it is acceptable for this workaround. + */ + regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, + ADAU17X1_CONVERTER0_ADOSR, ADAU17X1_CONVERTER0_ADOSR); + regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, + ADAU17X1_CONVERTER0_ADOSR, 0); + + return 0; +} + +static const char * const adau17x1_mono_stereo_text[] = { + "Stereo", + "Mono Left Channel (L+R)", + "Mono Right Channel (L+R)", + "Mono (L+R)", +}; + +static SOC_ENUM_SINGLE_DECL(adau17x1_dac_mode_enum, + ADAU17X1_DAC_CONTROL0, 6, adau17x1_mono_stereo_text); + +static const struct snd_kcontrol_new adau17x1_dac_mode_mux = + SOC_DAPM_ENUM("DAC Mono-Stereo-Mode", adau17x1_dac_mode_enum); + +static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("PLL", 3, SND_SOC_NOPM, 0, 0, adau17x1_pll_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("AIFCLK", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU17X1_MICBIAS, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Left Playback Enable", ADAU17X1_PLAY_POWER_MGMT, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Right Playback Enable", ADAU17X1_PLAY_POWER_MGMT, + 1, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Left DAC Mode Mux", SND_SOC_NOPM, 0, 0, + &adau17x1_dac_mode_mux), + SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0, + &adau17x1_dac_mode_mux), + + SND_SOC_DAPM_ADC_E("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0, + adau17x1_adc_fixup, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_ADC("Right Decimator", NULL, ADAU17X1_ADC_CONTROL, 1, 0), + SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0), + SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0), +}; + +static const struct snd_soc_dapm_route adau17x1_dapm_routes[] = { + { "Left Decimator", NULL, "SYSCLK" }, + { "Right Decimator", NULL, "SYSCLK" }, + { "Left DAC", NULL, "SYSCLK" }, + { "Right DAC", NULL, "SYSCLK" }, + { "Capture", NULL, "SYSCLK" }, + { "Playback", NULL, "SYSCLK" }, + + { "Left DAC", NULL, "Left DAC Mode Mux" }, + { "Right DAC", NULL, "Right DAC Mode Mux" }, + + { "Capture", NULL, "AIFCLK" }, + { "Playback", NULL, "AIFCLK" }, +}; + +static const struct snd_soc_dapm_route adau17x1_dapm_pll_route = { + "SYSCLK", NULL, "PLL", +}; + +/* + * The MUX register for the Capture and Playback MUXs selects either DSP as + * source/destination or one of the TDM slots. The TDM slot is selected via + * snd_soc_dai_set_tdm_slot(), so we only expose whether to go to the DSP or + * directly to the DAI interface with this control. + */ +static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adau *adau = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update update = {}; + unsigned int stream = e->shift_l; + unsigned int val, change; + int reg; + + if (ucontrol->value.enumerated.item[0] >= e->items) + return -EINVAL; + + switch (ucontrol->value.enumerated.item[0]) { + case 0: + val = 0; + adau->dsp_bypass[stream] = false; + break; + default: + val = (adau->tdm_slot[stream] * 2) + 1; + adau->dsp_bypass[stream] = true; + break; + } + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = ADAU17X1_SERIAL_INPUT_ROUTE; + else + reg = ADAU17X1_SERIAL_OUTPUT_ROUTE; + + change = snd_soc_component_test_bits(component, reg, 0xff, val); + if (change) { + update.kcontrol = kcontrol; + update.mask = 0xff; + update.reg = reg; + update.val = val; + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + ucontrol->value.enumerated.item[0], e, &update); + } + + return change; +} + +static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct adau *adau = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int stream = e->shift_l; + unsigned int reg, val; + int ret; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = ADAU17X1_SERIAL_INPUT_ROUTE; + else + reg = ADAU17X1_SERIAL_OUTPUT_ROUTE; + + ret = regmap_read(adau->regmap, reg, &val); + if (ret) + return ret; + + if (val != 0) + val = 1; + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +#define DECLARE_ADAU17X1_DSP_MUX_CTRL(_name, _label, _stream, _text) \ + const struct snd_kcontrol_new _name = \ + SOC_DAPM_ENUM_EXT(_label, (const struct soc_enum)\ + SOC_ENUM_SINGLE(SND_SOC_NOPM, _stream, \ + ARRAY_SIZE(_text), _text), \ + adau17x1_dsp_mux_enum_get, adau17x1_dsp_mux_enum_put) + +static const char * const adau17x1_dac_mux_text[] = { + "DSP", + "AIFIN", +}; + +static const char * const adau17x1_capture_mux_text[] = { + "DSP", + "Decimator", +}; + +static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_dac_mux, "DAC Playback Mux", + SNDRV_PCM_STREAM_PLAYBACK, adau17x1_dac_mux_text); + +static DECLARE_ADAU17X1_DSP_MUX_CTRL(adau17x1_capture_mux, "Capture Mux", + SNDRV_PCM_STREAM_CAPTURE, adau17x1_capture_mux_text); + +static const struct snd_soc_dapm_widget adau17x1_dsp_dapm_widgets[] = { + SND_SOC_DAPM_PGA("DSP", ADAU17X1_DSP_RUN, 0, 0, NULL, 0), + SND_SOC_DAPM_SIGGEN("DSP Siggen"), + + SND_SOC_DAPM_MUX("DAC Playback Mux", SND_SOC_NOPM, 0, 0, + &adau17x1_dac_mux), + SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, + &adau17x1_capture_mux), +}; + +static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = { + { "DAC Playback Mux", "DSP", "DSP" }, + { "DAC Playback Mux", "AIFIN", "Playback" }, + + { "Left DAC Mode Mux", "Stereo", "DAC Playback Mux" }, + { "Left DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" }, + { "Left DAC Mode Mux", "Mono Left Channel (L+R)", "DAC Playback Mux" }, + { "Right DAC Mode Mux", "Stereo", "DAC Playback Mux" }, + { "Right DAC Mode Mux", "Mono (L+R)", "DAC Playback Mux" }, + { "Right DAC Mode Mux", "Mono Right Channel (L+R)", "DAC Playback Mux" }, + + { "Capture Mux", "DSP", "DSP" }, + { "Capture Mux", "Decimator", "Left Decimator" }, + { "Capture Mux", "Decimator", "Right Decimator" }, + + { "Capture", NULL, "Capture Mux" }, + + { "DSP", NULL, "DSP Siggen" }, + + { "DSP", NULL, "Left Decimator" }, + { "DSP", NULL, "Right Decimator" }, + { "DSP", NULL, "Playback" }, +}; + +static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = { + { "Left DAC Mode Mux", "Stereo", "Playback" }, + { "Left DAC Mode Mux", "Mono (L+R)", "Playback" }, + { "Left DAC Mode Mux", "Mono Left Channel (L+R)", "Playback" }, + { "Right DAC Mode Mux", "Stereo", "Playback" }, + { "Right DAC Mode Mux", "Mono (L+R)", "Playback" }, + { "Right DAC Mode Mux", "Mono Right Channel (L+R)", "Playback" }, + { "Capture", NULL, "Left Decimator" }, + { "Capture", NULL, "Right Decimator" }, +}; + +static bool adau17x1_has_dsp(struct adau *adau) +{ + switch (adau->type) { + case ADAU1761: + case ADAU1381: + case ADAU1781: + return true; + default: + return false; + } +} + +/* Chip has a DSP but we're pretending it doesn't. */ +static bool adau17x1_has_disused_dsp(struct adau *adau) +{ + switch (adau->type) { + case ADAU1761_AS_1361: + return true; + default: + return false; + } +} + +static bool adau17x1_has_safeload(struct adau *adau) +{ + switch (adau->type) { + case ADAU1761: + case ADAU1781: + return true; + default: + return false; + } +} + +static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct adau *adau = snd_soc_component_get_drvdata(component); + int ret; + + if (freq_in < 8000000 || freq_in > 27000000) + return -EINVAL; + + ret = adau_calc_pll_cfg(freq_in, freq_out, adau->pll_regs); + if (ret < 0) + return ret; + + /* The PLL register is 6 bytes long and can only be written at once. */ + ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL, + adau->pll_regs, ARRAY_SIZE(adau->pll_regs)); + if (ret) + return ret; + + adau->pll_freq = freq_out; + + return 0; +} + +static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(dai->component); + struct adau *adau = snd_soc_component_get_drvdata(dai->component); + bool is_pll; + bool was_pll; + + switch (clk_id) { + case ADAU17X1_CLK_SRC_MCLK: + is_pll = false; + break; + case ADAU17X1_CLK_SRC_PLL_AUTO: + if (!adau->mclk) + return -EINVAL; + fallthrough; + case ADAU17X1_CLK_SRC_PLL: + is_pll = true; + break; + default: + return -EINVAL; + } + + switch (adau->clk_src) { + case ADAU17X1_CLK_SRC_MCLK: + was_pll = false; + break; + case ADAU17X1_CLK_SRC_PLL: + case ADAU17X1_CLK_SRC_PLL_AUTO: + was_pll = true; + break; + default: + return -EINVAL; + } + + adau->sysclk = freq; + + if (is_pll != was_pll) { + if (is_pll) { + snd_soc_dapm_add_routes(dapm, + &adau17x1_dapm_pll_route, 1); + } else { + snd_soc_dapm_del_routes(dapm, + &adau17x1_dapm_pll_route, 1); + } + } + + adau->clk_src = clk_id; + + return 0; +} + +static int adau17x1_auto_pll(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params) +{ + struct adau *adau = snd_soc_dai_get_drvdata(dai); + unsigned int pll_rate; + + switch (params_rate(params)) { + case 48000: + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 96000: + pll_rate = 48000 * 1024; + break; + case 44100: + case 7350: + case 11025: + case 14700: + case 22050: + case 29400: + case 88200: + pll_rate = 44100 * 1024; + break; + default: + return -EINVAL; + } + + return adau17x1_set_dai_pll(dai, ADAU17X1_PLL, ADAU17X1_PLL_SRC_MCLK, + clk_get_rate(adau->mclk), pll_rate); +} + +static int adau17x1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct adau *adau = snd_soc_component_get_drvdata(component); + unsigned int val, div, dsp_div; + unsigned int freq; + int ret; + + switch (adau->clk_src) { + case ADAU17X1_CLK_SRC_PLL_AUTO: + ret = adau17x1_auto_pll(dai, params); + if (ret) + return ret; + fallthrough; + case ADAU17X1_CLK_SRC_PLL: + freq = adau->pll_freq; + break; + default: + freq = adau->sysclk; + break; + } + + if (freq % params_rate(params) != 0) + return -EINVAL; + + switch (freq / params_rate(params)) { + case 1024: /* fs */ + div = 0; + dsp_div = 1; + break; + case 6144: /* fs / 6 */ + div = 1; + dsp_div = 6; + break; + case 4096: /* fs / 4 */ + div = 2; + dsp_div = 5; + break; + case 3072: /* fs / 3 */ + div = 3; + dsp_div = 4; + break; + case 2048: /* fs / 2 */ + div = 4; + dsp_div = 3; + break; + case 1536: /* fs / 1.5 */ + div = 5; + dsp_div = 2; + break; + case 512: /* fs / 0.5 */ + div = 6; + dsp_div = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, + ADAU17X1_CONVERTER0_CONVSR_MASK, div); + + if (adau17x1_has_dsp(adau) || adau17x1_has_disused_dsp(adau)) + regmap_write(adau->regmap, ADAU17X1_SERIAL_SAMPLING_RATE, div); + if (adau17x1_has_dsp(adau)) + regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div); + + if (adau->sigmadsp) { + ret = adau17x1_setup_firmware(component, params_rate(params)); + if (ret < 0) + return ret; + } + + if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J) + return 0; + + switch (params_width(params)) { + case 16: + val = ADAU17X1_SERIAL_PORT1_DELAY16; + break; + case 24: + val = ADAU17X1_SERIAL_PORT1_DELAY8; + break; + case 32: + val = ADAU17X1_SERIAL_PORT1_DELAY0; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1, + ADAU17X1_SERIAL_PORT1_DELAY_MASK, val); +} + +static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct adau *adau = snd_soc_component_get_drvdata(dai->component); + unsigned int ctrl0, ctrl1; + unsigned int ctrl0_mask; + int lrclk_pol; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + ctrl0 = ADAU17X1_SERIAL_PORT0_MASTER; + adau->master = true; + break; + case SND_SOC_DAIFMT_CBC_CFC: + ctrl0 = 0; + adau->master = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + lrclk_pol = 0; + ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1; + break; + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + lrclk_pol = 1; + ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0; + break; + case SND_SOC_DAIFMT_DSP_A: + lrclk_pol = 1; + ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE; + ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY1; + break; + case SND_SOC_DAIFMT_DSP_B: + lrclk_pol = 1; + ctrl0 |= ADAU17X1_SERIAL_PORT0_PULSE_MODE; + ctrl1 = ADAU17X1_SERIAL_PORT1_DELAY0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL; + break; + case SND_SOC_DAIFMT_NB_IF: + lrclk_pol = !lrclk_pol; + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl0 |= ADAU17X1_SERIAL_PORT0_BCLK_POL; + lrclk_pol = !lrclk_pol; + break; + default: + return -EINVAL; + } + + if (lrclk_pol) + ctrl0 |= ADAU17X1_SERIAL_PORT0_LRCLK_POL; + + /* Set the mask to update all relevant bits in ADAU17X1_SERIAL_PORT0 */ + ctrl0_mask = ADAU17X1_SERIAL_PORT0_MASTER | + ADAU17X1_SERIAL_PORT0_LRCLK_POL | + ADAU17X1_SERIAL_PORT0_BCLK_POL | + ADAU17X1_SERIAL_PORT0_PULSE_MODE; + + regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0_mask, + ctrl0); + regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1, + ADAU17X1_SERIAL_PORT1_DELAY_MASK, ctrl1); + + adau->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + return 0; +} + +static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct adau *adau = snd_soc_component_get_drvdata(dai->component); + unsigned int ser_ctrl0, ser_ctrl1; + unsigned int conv_ctrl0, conv_ctrl1; + + /* I2S mode */ + if (slots == 0) { + slots = 2; + rx_mask = 3; + tx_mask = 3; + slot_width = 32; + } + + switch (slots) { + case 2: + ser_ctrl0 = ADAU17X1_SERIAL_PORT0_STEREO; + break; + case 4: + ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM4; + break; + case 8: + if (adau->type == ADAU1361) + return -EINVAL; + + ser_ctrl0 = ADAU17X1_SERIAL_PORT0_TDM8; + break; + default: + return -EINVAL; + } + + switch (slot_width * slots) { + case 32: + if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361) + return -EINVAL; + + ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32; + break; + case 64: + ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK64; + break; + case 48: + ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK48; + break; + case 128: + ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK128; + break; + case 256: + if (adau->type == ADAU1361) + return -EINVAL; + + ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK256; + break; + default: + return -EINVAL; + } + + switch (rx_mask) { + case 0x03: + conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(1); + adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 0; + break; + case 0x0c: + conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(2); + adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 1; + break; + case 0x30: + conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(3); + adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 2; + break; + case 0xc0: + conv_ctrl1 = ADAU17X1_CONVERTER1_ADC_PAIR(4); + adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] = 3; + break; + default: + return -EINVAL; + } + + switch (tx_mask) { + case 0x03: + conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(1); + adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 0; + break; + case 0x0c: + conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(2); + adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 1; + break; + case 0x30: + conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(3); + adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 2; + break; + case 0xc0: + conv_ctrl0 = ADAU17X1_CONVERTER0_DAC_PAIR(4); + adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] = 3; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0, + ADAU17X1_CONVERTER0_DAC_PAIR_MASK, conv_ctrl0); + regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER1, + ADAU17X1_CONVERTER1_ADC_PAIR_MASK, conv_ctrl1); + regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT0, + ADAU17X1_SERIAL_PORT0_TDM_MASK, ser_ctrl0); + regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1, + ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1); + + if (!adau17x1_has_dsp(adau) && !adau17x1_has_disused_dsp(adau)) + return 0; + + if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) { + regmap_write(adau->regmap, ADAU17X1_SERIAL_INPUT_ROUTE, + (adau->tdm_slot[SNDRV_PCM_STREAM_PLAYBACK] * 2) + 1); + } + + if (adau->dsp_bypass[SNDRV_PCM_STREAM_CAPTURE]) { + regmap_write(adau->regmap, ADAU17X1_SERIAL_OUTPUT_ROUTE, + (adau->tdm_slot[SNDRV_PCM_STREAM_CAPTURE] * 2) + 1); + } + + return 0; +} + +static int adau17x1_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct adau *adau = snd_soc_component_get_drvdata(dai->component); + + if (adau->sigmadsp) + return sigmadsp_restrict_params(adau->sigmadsp, substream); + + return 0; +} + +const struct snd_soc_dai_ops adau17x1_dai_ops = { + .hw_params = adau17x1_hw_params, + .set_sysclk = adau17x1_set_dai_sysclk, + .set_fmt = adau17x1_set_dai_fmt, + .set_pll = adau17x1_set_dai_pll, + .set_tdm_slot = adau17x1_set_dai_tdm_slot, + .startup = adau17x1_startup, +}; +EXPORT_SYMBOL_GPL(adau17x1_dai_ops); + +int adau17x1_set_micbias_voltage(struct snd_soc_component *component, + enum adau17x1_micbias_voltage micbias) +{ + struct adau *adau = snd_soc_component_get_drvdata(component); + + switch (micbias) { + case ADAU17X1_MICBIAS_0_90_AVDD: + case ADAU17X1_MICBIAS_0_65_AVDD: + break; + default: + return -EINVAL; + } + + return regmap_write(adau->regmap, ADAU17X1_MICBIAS, micbias << 2); +} +EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage); + +bool adau17x1_precious_register(struct device *dev, unsigned int reg) +{ + /* SigmaDSP parameter memory */ + if (reg < 0x400) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(adau17x1_precious_register); + +bool adau17x1_readable_register(struct device *dev, unsigned int reg) +{ + /* SigmaDSP parameter memory */ + if (reg < 0x400) + return true; + + switch (reg) { + case ADAU17X1_CLOCK_CONTROL: + case ADAU17X1_PLL_CONTROL: + case ADAU17X1_REC_POWER_MGMT: + case ADAU17X1_MICBIAS: + case ADAU17X1_SERIAL_PORT0: + case ADAU17X1_SERIAL_PORT1: + case ADAU17X1_CONVERTER0: + case ADAU17X1_CONVERTER1: + case ADAU17X1_LEFT_INPUT_DIGITAL_VOL: + case ADAU17X1_RIGHT_INPUT_DIGITAL_VOL: + case ADAU17X1_ADC_CONTROL: + case ADAU17X1_PLAY_POWER_MGMT: + case ADAU17X1_DAC_CONTROL0: + case ADAU17X1_DAC_CONTROL1: + case ADAU17X1_DAC_CONTROL2: + case ADAU17X1_SERIAL_PORT_PAD: + case ADAU17X1_CONTROL_PORT_PAD0: + case ADAU17X1_CONTROL_PORT_PAD1: + case ADAU17X1_DSP_SAMPLING_RATE: + case ADAU17X1_SERIAL_INPUT_ROUTE: + case ADAU17X1_SERIAL_OUTPUT_ROUTE: + case ADAU17X1_DSP_ENABLE: + case ADAU17X1_DSP_RUN: + case ADAU17X1_SERIAL_SAMPLING_RATE: + return true; + default: + break; + } + return false; +} +EXPORT_SYMBOL_GPL(adau17x1_readable_register); + +bool adau17x1_volatile_register(struct device *dev, unsigned int reg) +{ + /* SigmaDSP parameter and program memory */ + if (reg < 0x4000) + return true; + + switch (reg) { + /* The PLL register is 6 bytes long */ + case ADAU17X1_PLL_CONTROL: + case ADAU17X1_PLL_CONTROL + 1: + case ADAU17X1_PLL_CONTROL + 2: + case ADAU17X1_PLL_CONTROL + 3: + case ADAU17X1_PLL_CONTROL + 4: + case ADAU17X1_PLL_CONTROL + 5: + return true; + default: + break; + } + + return false; +} +EXPORT_SYMBOL_GPL(adau17x1_volatile_register); + +static int adau17x1_setup_firmware(struct snd_soc_component *component, + unsigned int rate) +{ + int ret; + int dspsr, dsp_run; + struct adau *adau = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + /* Check if sample rate is the same as before. If it is there is no + * point in performing the below steps as the call to + * sigmadsp_setup(...) will return directly when it finds the sample + * rate to be the same as before. By checking this we can prevent an + * audiable popping noise which occours when toggling DSP_RUN. + */ + if (adau->sigmadsp->current_samplerate == rate) + return 0; + + snd_soc_dapm_mutex_lock(dapm); + + ret = regmap_read(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, &dspsr); + if (ret) + goto err; + + ret = regmap_read(adau->regmap, ADAU17X1_DSP_RUN, &dsp_run); + if (ret) + goto err; + + regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1); + regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf); + regmap_write(adau->regmap, ADAU17X1_DSP_RUN, 0); + + ret = sigmadsp_setup(adau->sigmadsp, rate); + if (ret) { + regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0); + goto err; + } + regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dspsr); + regmap_write(adau->regmap, ADAU17X1_DSP_RUN, dsp_run); + +err: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +int adau17x1_add_widgets(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adau *adau = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_add_component_controls(component, adau17x1_controls, + ARRAY_SIZE(adau17x1_controls)); + if (ret) + return ret; + ret = snd_soc_dapm_new_controls(dapm, adau17x1_dapm_widgets, + ARRAY_SIZE(adau17x1_dapm_widgets)); + if (ret) + return ret; + + if (adau17x1_has_dsp(adau)) { + ret = snd_soc_dapm_new_controls(dapm, adau17x1_dsp_dapm_widgets, + ARRAY_SIZE(adau17x1_dsp_dapm_widgets)); + if (ret) + return ret; + + if (!adau->sigmadsp) + return 0; + + ret = sigmadsp_attach(adau->sigmadsp, component); + if (ret) { + dev_err(component->dev, "Failed to attach firmware: %d\n", + ret); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(adau17x1_add_widgets); + +int adau17x1_add_routes(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adau *adau = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_dapm_add_routes(dapm, adau17x1_dapm_routes, + ARRAY_SIZE(adau17x1_dapm_routes)); + if (ret) + return ret; + + if (adau17x1_has_dsp(adau)) { + ret = snd_soc_dapm_add_routes(dapm, adau17x1_dsp_dapm_routes, + ARRAY_SIZE(adau17x1_dsp_dapm_routes)); + } else { + ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes, + ARRAY_SIZE(adau17x1_no_dsp_dapm_routes)); + } + + if (adau->clk_src != ADAU17X1_CLK_SRC_MCLK) + snd_soc_dapm_add_routes(dapm, &adau17x1_dapm_pll_route, 1); + + return ret; +} +EXPORT_SYMBOL_GPL(adau17x1_add_routes); + +int adau17x1_resume(struct snd_soc_component *component) +{ + struct adau *adau = snd_soc_component_get_drvdata(component); + + if (adau->switch_mode) + adau->switch_mode(component->dev); + + regcache_sync(adau->regmap); + + return 0; +} +EXPORT_SYMBOL_GPL(adau17x1_resume); + +static int adau17x1_safeload(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t bytes[], size_t len) +{ + uint8_t buf[ADAU17X1_WORD_SIZE]; + uint8_t data[ADAU17X1_SAFELOAD_DATA_SIZE]; + unsigned int addr_offset; + unsigned int nbr_words; + int ret; + + /* write data to safeload addresses. Check if len is not a multiple of + * 4 bytes, if so we need to zero pad. + */ + nbr_words = len / ADAU17X1_WORD_SIZE; + if ((len - nbr_words * ADAU17X1_WORD_SIZE) == 0) { + ret = regmap_raw_write(sigmadsp->control_data, + ADAU17X1_SAFELOAD_DATA, bytes, len); + } else { + nbr_words++; + memset(data, 0, ADAU17X1_SAFELOAD_DATA_SIZE); + memcpy(data, bytes, len); + ret = regmap_raw_write(sigmadsp->control_data, + ADAU17X1_SAFELOAD_DATA, data, + nbr_words * ADAU17X1_WORD_SIZE); + } + + if (ret < 0) + return ret; + + /* Write target address, target address is offset by 1 */ + addr_offset = addr - 1; + put_unaligned_be32(addr_offset, buf); + ret = regmap_raw_write(sigmadsp->control_data, + ADAU17X1_SAFELOAD_TARGET_ADDRESS, buf, ADAU17X1_WORD_SIZE); + if (ret < 0) + return ret; + + /* write nbr of words to trigger address */ + put_unaligned_be32(nbr_words, buf); + ret = regmap_raw_write(sigmadsp->control_data, + ADAU17X1_SAFELOAD_TRIGGER, buf, ADAU17X1_WORD_SIZE); + if (ret < 0) + return ret; + + return 0; +} + +static const struct sigmadsp_ops adau17x1_sigmadsp_ops = { + .safeload = adau17x1_safeload, +}; + +int adau17x1_probe(struct device *dev, struct regmap *regmap, + enum adau17x1_type type, void (*switch_mode)(struct device *dev), + const char *firmware_name) +{ + struct adau *adau; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + adau = devm_kzalloc(dev, sizeof(*adau), GFP_KERNEL); + if (!adau) + return -ENOMEM; + + /* Clock is optional (for the driver) */ + adau->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(adau->mclk)) + return PTR_ERR(adau->mclk); + + if (adau->mclk) { + adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO; + + /* + * Any valid PLL output rate will work at this point, use one + * that is likely to be chosen later as well. The register will + * be written when the PLL is powered up for the first time. + */ + ret = adau_calc_pll_cfg(clk_get_rate(adau->mclk), 48000 * 1024, + adau->pll_regs); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(adau->mclk); + if (ret) + return ret; + } + + adau->regmap = regmap; + adau->switch_mode = switch_mode; + adau->type = type; + + dev_set_drvdata(dev, adau); + + if (firmware_name) { + if (adau17x1_has_safeload(adau)) { + adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, + &adau17x1_sigmadsp_ops, firmware_name); + } else { + adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, + NULL, firmware_name); + } + if (IS_ERR(adau->sigmadsp)) { + dev_warn(dev, "Could not find firmware file: %ld\n", + PTR_ERR(adau->sigmadsp)); + adau->sigmadsp = NULL; + } + } + + if (switch_mode) + switch_mode(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(adau17x1_probe); + +void adau17x1_remove(struct device *dev) +{ + struct adau *adau = dev_get_drvdata(dev); + + clk_disable_unprepare(adau->mclk); +} +EXPORT_SYMBOL_GPL(adau17x1_remove); + +MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h new file mode 100644 index 0000000000..5e58abfffc --- /dev/null +++ b/sound/soc/codecs/adau17x1.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ADAU17X1_H__ +#define __ADAU17X1_H__ + +#include +#include + +#include "sigmadsp.h" + +enum adau17x1_type { + ADAU1361, + ADAU1761, + ADAU1761_AS_1361, + ADAU1381, + ADAU1781, +}; + +enum adau17x1_pll { + ADAU17X1_PLL, +}; + +enum adau17x1_pll_src { + ADAU17X1_PLL_SRC_MCLK, +}; + +enum adau17x1_clk_src { + /* Automatically configure PLL based on the sample rate */ + ADAU17X1_CLK_SRC_PLL_AUTO, + ADAU17X1_CLK_SRC_MCLK, + ADAU17X1_CLK_SRC_PLL, +}; + +struct clk; + +struct adau { + unsigned int sysclk; + unsigned int pll_freq; + struct clk *mclk; + + enum adau17x1_clk_src clk_src; + enum adau17x1_type type; + void (*switch_mode)(struct device *dev); + + unsigned int dai_fmt; + + uint8_t pll_regs[6]; + + bool master; + + unsigned int tdm_slot[2]; + bool dsp_bypass[2]; + + struct regmap *regmap; + struct sigmadsp *sigmadsp; +}; + +int adau17x1_add_widgets(struct snd_soc_component *component); +int adau17x1_add_routes(struct snd_soc_component *component); +int adau17x1_probe(struct device *dev, struct regmap *regmap, + enum adau17x1_type type, void (*switch_mode)(struct device *dev), + const char *firmware_name); +void adau17x1_remove(struct device *dev); +int adau17x1_set_micbias_voltage(struct snd_soc_component *component, + enum adau17x1_micbias_voltage micbias); +bool adau17x1_readable_register(struct device *dev, unsigned int reg); +bool adau17x1_volatile_register(struct device *dev, unsigned int reg); +bool adau17x1_precious_register(struct device *dev, unsigned int reg); +int adau17x1_resume(struct snd_soc_component *component); + +extern const struct snd_soc_dai_ops adau17x1_dai_ops; + +#define ADAU17X1_CLOCK_CONTROL 0x4000 +#define ADAU17X1_PLL_CONTROL 0x4002 +#define ADAU17X1_REC_POWER_MGMT 0x4009 +#define ADAU17X1_MICBIAS 0x4010 +#define ADAU17X1_SERIAL_PORT0 0x4015 +#define ADAU17X1_SERIAL_PORT1 0x4016 +#define ADAU17X1_CONVERTER0 0x4017 +#define ADAU17X1_CONVERTER1 0x4018 +#define ADAU17X1_LEFT_INPUT_DIGITAL_VOL 0x401a +#define ADAU17X1_RIGHT_INPUT_DIGITAL_VOL 0x401b +#define ADAU17X1_ADC_CONTROL 0x4019 +#define ADAU17X1_PLAY_POWER_MGMT 0x4029 +#define ADAU17X1_DAC_CONTROL0 0x402a +#define ADAU17X1_DAC_CONTROL1 0x402b +#define ADAU17X1_DAC_CONTROL2 0x402c +#define ADAU17X1_SERIAL_PORT_PAD 0x402d +#define ADAU17X1_CONTROL_PORT_PAD0 0x402f +#define ADAU17X1_CONTROL_PORT_PAD1 0x4030 +#define ADAU17X1_DSP_SAMPLING_RATE 0x40eb +#define ADAU17X1_SERIAL_INPUT_ROUTE 0x40f2 +#define ADAU17X1_SERIAL_OUTPUT_ROUTE 0x40f3 +#define ADAU17X1_DSP_ENABLE 0x40f5 +#define ADAU17X1_DSP_RUN 0x40f6 +#define ADAU17X1_SERIAL_SAMPLING_RATE 0x40f8 + +#define ADAU17X1_SERIAL_PORT0_BCLK_POL BIT(4) +#define ADAU17X1_SERIAL_PORT0_LRCLK_POL BIT(3) +#define ADAU17X1_SERIAL_PORT0_MASTER BIT(0) + +#define ADAU17X1_SERIAL_PORT1_DELAY1 0x00 +#define ADAU17X1_SERIAL_PORT1_DELAY0 0x01 +#define ADAU17X1_SERIAL_PORT1_DELAY8 0x02 +#define ADAU17X1_SERIAL_PORT1_DELAY16 0x03 +#define ADAU17X1_SERIAL_PORT1_DELAY_MASK 0x03 + +#define ADAU17X1_CLOCK_CONTROL_INFREQ_MASK 0x6 +#define ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL BIT(3) +#define ADAU17X1_CLOCK_CONTROL_SYSCLK_EN BIT(0) + +#define ADAU17X1_SERIAL_PORT1_BCLK64 (0x0 << 5) +#define ADAU17X1_SERIAL_PORT1_BCLK32 (0x1 << 5) +#define ADAU17X1_SERIAL_PORT1_BCLK48 (0x2 << 5) +#define ADAU17X1_SERIAL_PORT1_BCLK128 (0x3 << 5) +#define ADAU17X1_SERIAL_PORT1_BCLK256 (0x4 << 5) +#define ADAU17X1_SERIAL_PORT1_BCLK_MASK (0x7 << 5) + +#define ADAU17X1_SERIAL_PORT0_STEREO (0x0 << 1) +#define ADAU17X1_SERIAL_PORT0_TDM4 (0x1 << 1) +#define ADAU17X1_SERIAL_PORT0_TDM8 (0x2 << 1) +#define ADAU17X1_SERIAL_PORT0_TDM_MASK (0x3 << 1) +#define ADAU17X1_SERIAL_PORT0_PULSE_MODE BIT(5) + +#define ADAU17X1_CONVERTER0_DAC_PAIR(x) (((x) - 1) << 5) +#define ADAU17X1_CONVERTER0_DAC_PAIR_MASK (0x3 << 5) +#define ADAU17X1_CONVERTER1_ADC_PAIR(x) ((x) - 1) +#define ADAU17X1_CONVERTER1_ADC_PAIR_MASK 0x3 + +#define ADAU17X1_CONVERTER0_CONVSR_MASK 0x7 + +#define ADAU17X1_CONVERTER0_ADOSR BIT(3) + + +#endif diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c new file mode 100644 index 0000000000..24c7b9c84c --- /dev/null +++ b/sound/soc/codecs/adau1977-i2c.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include + +#include "adau1977.h" + +static const struct i2c_device_id adau1977_i2c_ids[]; + +static int adau1977_i2c_probe(struct i2c_client *client) +{ + struct regmap_config config; + const struct i2c_device_id *id = i2c_match_id(adau1977_i2c_ids, client); + + config = adau1977_regmap_config; + config.val_bits = 8; + config.reg_bits = 8; + + return adau1977_probe(&client->dev, + devm_regmap_init_i2c(client, &config), + id->driver_data, NULL); +} + +static const struct i2c_device_id adau1977_i2c_ids[] = { + { "adau1977", ADAU1977 }, + { "adau1978", ADAU1978 }, + { "adau1979", ADAU1978 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); + +static struct i2c_driver adau1977_i2c_driver = { + .driver = { + .name = "adau1977", + }, + .probe = adau1977_i2c_probe, + .id_table = adau1977_i2c_ids, +}; +module_i2c_driver(adau1977_i2c_driver); + +MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c new file mode 100644 index 0000000000..207c5c95f3 --- /dev/null +++ b/sound/soc/codecs/adau1977-spi.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "adau1977.h" + +static void adau1977_spi_switch_mode(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + + /* + * To get the device into SPI mode CLATCH has to be pulled low three + * times. Do this by issuing three dummy reads. + */ + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); + spi_w8r8(spi, 0x00); +} + +static int adau1977_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap_config config; + + if (!id) + return -EINVAL; + + config = adau1977_regmap_config; + config.val_bits = 8; + config.reg_bits = 16; + config.read_flag_mask = 0x1; + + return adau1977_probe(&spi->dev, + devm_regmap_init_spi(spi, &config), + id->driver_data, adau1977_spi_switch_mode); +} + +static const struct spi_device_id adau1977_spi_ids[] = { + { "adau1977", ADAU1977 }, + { "adau1978", ADAU1978 }, + { "adau1979", ADAU1978 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adau1977_spi_ids); + +static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = { + { .compatible = "adi,adau1977" }, + { .compatible = "adi,adau1978" }, + { .compatible = "adi,adau1979" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1977_spi_of_match); + +static struct spi_driver adau1977_spi_driver = { + .driver = { + .name = "adau1977", + .of_match_table = of_match_ptr(adau1977_spi_of_match), + }, + .probe = adau1977_spi_probe, + .id_table = adau1977_spi_ids, +}; +module_spi_driver(adau1977_spi_driver); + +MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c new file mode 100644 index 0000000000..ae59efb38f --- /dev/null +++ b/sound/soc/codecs/adau1977.c @@ -0,0 +1,1002 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "adau1977.h" + +#define ADAU1977_REG_POWER 0x00 +#define ADAU1977_REG_PLL 0x01 +#define ADAU1977_REG_BOOST 0x02 +#define ADAU1977_REG_MICBIAS 0x03 +#define ADAU1977_REG_BLOCK_POWER_SAI 0x04 +#define ADAU1977_REG_SAI_CTRL0 0x05 +#define ADAU1977_REG_SAI_CTRL1 0x06 +#define ADAU1977_REG_CMAP12 0x07 +#define ADAU1977_REG_CMAP34 0x08 +#define ADAU1977_REG_SAI_OVERTEMP 0x09 +#define ADAU1977_REG_POST_ADC_GAIN(x) (0x0a + (x)) +#define ADAU1977_REG_MISC_CONTROL 0x0e +#define ADAU1977_REG_DIAG_CONTROL 0x10 +#define ADAU1977_REG_STATUS(x) (0x11 + (x)) +#define ADAU1977_REG_DIAG_IRQ1 0x15 +#define ADAU1977_REG_DIAG_IRQ2 0x16 +#define ADAU1977_REG_ADJUST1 0x17 +#define ADAU1977_REG_ADJUST2 0x18 +#define ADAU1977_REG_ADC_CLIP 0x19 +#define ADAU1977_REG_DC_HPF_CAL 0x1a + +#define ADAU1977_POWER_RESET BIT(7) +#define ADAU1977_POWER_PWUP BIT(0) + +#define ADAU1977_PLL_CLK_S BIT(4) +#define ADAU1977_PLL_MCS_MASK 0x7 + +#define ADAU1977_MICBIAS_MB_VOLTS_MASK 0xf0 +#define ADAU1977_MICBIAS_MB_VOLTS_OFFSET 4 + +#define ADAU1977_BLOCK_POWER_SAI_LR_POL BIT(7) +#define ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE BIT(6) +#define ADAU1977_BLOCK_POWER_SAI_LDO_EN BIT(5) + +#define ADAU1977_SAI_CTRL0_FMT_MASK (0x3 << 6) +#define ADAU1977_SAI_CTRL0_FMT_I2S (0x0 << 6) +#define ADAU1977_SAI_CTRL0_FMT_LJ (0x1 << 6) +#define ADAU1977_SAI_CTRL0_FMT_RJ_24BIT (0x2 << 6) +#define ADAU1977_SAI_CTRL0_FMT_RJ_16BIT (0x3 << 6) + +#define ADAU1977_SAI_CTRL0_SAI_MASK (0x7 << 3) +#define ADAU1977_SAI_CTRL0_SAI_I2S (0x0 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_2 (0x1 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_4 (0x2 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_8 (0x3 << 3) +#define ADAU1977_SAI_CTRL0_SAI_TDM_16 (0x4 << 3) + +#define ADAU1977_SAI_CTRL0_FS_MASK (0x7) +#define ADAU1977_SAI_CTRL0_FS_8000_12000 (0x0) +#define ADAU1977_SAI_CTRL0_FS_16000_24000 (0x1) +#define ADAU1977_SAI_CTRL0_FS_32000_48000 (0x2) +#define ADAU1977_SAI_CTRL0_FS_64000_96000 (0x3) +#define ADAU1977_SAI_CTRL0_FS_128000_192000 (0x4) + +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK (0x3 << 5) +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_32 (0x0 << 5) +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_24 (0x1 << 5) +#define ADAU1977_SAI_CTRL1_SLOT_WIDTH_16 (0x2 << 5) +#define ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK (0x1 << 4) +#define ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT (0x1 << 4) +#define ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT (0x0 << 4) +#define ADAU1977_SAI_CTRL1_LRCLK_PULSE BIT(3) +#define ADAU1977_SAI_CTRL1_MSB BIT(2) +#define ADAU1977_SAI_CTRL1_BCLKRATE_16 (0x1 << 1) +#define ADAU1977_SAI_CTRL1_BCLKRATE_32 (0x0 << 1) +#define ADAU1977_SAI_CTRL1_BCLKRATE_MASK (0x1 << 1) +#define ADAU1977_SAI_CTRL1_MASTER BIT(0) + +#define ADAU1977_SAI_OVERTEMP_DRV_C(x) BIT(4 + (x)) +#define ADAU1977_SAI_OVERTEMP_DRV_HIZ BIT(3) + +#define ADAU1977_MISC_CONTROL_SUM_MODE_MASK (0x3 << 6) +#define ADAU1977_MISC_CONTROL_SUM_MODE_1CH (0x2 << 6) +#define ADAU1977_MISC_CONTROL_SUM_MODE_2CH (0x1 << 6) +#define ADAU1977_MISC_CONTROL_SUM_MODE_4CH (0x0 << 6) +#define ADAU1977_MISC_CONTROL_MMUTE BIT(4) +#define ADAU1977_MISC_CONTROL_DC_CAL BIT(0) + +#define ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET 4 +#define ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET 0 + +struct adau1977 { + struct regmap *regmap; + bool right_j; + unsigned int sysclk; + enum adau1977_sysclk_src sysclk_src; + struct gpio_desc *reset_gpio; + enum adau1977_type type; + + struct regulator *avdd_reg; + struct regulator *dvdd_reg; + + struct snd_pcm_hw_constraint_list constraints; + + struct device *dev; + void (*switch_mode)(struct device *dev); + + unsigned int max_clock_provider_fs; + unsigned int slot_width; + bool enabled; + bool clock_provider; +}; + +static const struct reg_default adau1977_reg_defaults[] = { + { 0x00, 0x00 }, + { 0x01, 0x41 }, + { 0x02, 0x4a }, + { 0x03, 0x7d }, + { 0x04, 0x3d }, + { 0x05, 0x02 }, + { 0x06, 0x00 }, + { 0x07, 0x10 }, + { 0x08, 0x32 }, + { 0x09, 0xf0 }, + { 0x0a, 0xa0 }, + { 0x0b, 0xa0 }, + { 0x0c, 0xa0 }, + { 0x0d, 0xa0 }, + { 0x0e, 0x02 }, + { 0x10, 0x0f }, + { 0x15, 0x20 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, + { 0x18, 0x00 }, + { 0x1a, 0x00 }, +}; + +static const DECLARE_TLV_DB_MINMAX_MUTE(adau1977_adc_gain, -3562, 6000); + +static const struct snd_soc_dapm_widget adau1977_micbias_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("MICBIAS", ADAU1977_REG_MICBIAS, + 3, 0, NULL, 0) +}; + +static const struct snd_soc_dapm_widget adau1977_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Vref", ADAU1977_REG_BLOCK_POWER_SAI, + 4, 0, NULL, 0), + + SND_SOC_DAPM_ADC("ADC1", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 0, 0), + SND_SOC_DAPM_ADC("ADC2", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 1, 0), + SND_SOC_DAPM_ADC("ADC3", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 2, 0), + SND_SOC_DAPM_ADC("ADC4", "Capture", ADAU1977_REG_BLOCK_POWER_SAI, 3, 0), + + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("AIN4"), + + SND_SOC_DAPM_OUTPUT("VREF"), +}; + +static const struct snd_soc_dapm_route adau1977_dapm_routes[] = { + { "ADC1", NULL, "AIN1" }, + { "ADC2", NULL, "AIN2" }, + { "ADC3", NULL, "AIN3" }, + { "ADC4", NULL, "AIN4" }, + + { "ADC1", NULL, "Vref" }, + { "ADC2", NULL, "Vref" }, + { "ADC3", NULL, "Vref" }, + { "ADC4", NULL, "Vref" }, + + { "VREF", NULL, "Vref" }, +}; + +#define ADAU1977_VOLUME(x) \ + SOC_SINGLE_TLV("ADC" #x " Capture Volume", \ + ADAU1977_REG_POST_ADC_GAIN((x) - 1), \ + 0, 255, 1, adau1977_adc_gain) + +#define ADAU1977_HPF_SWITCH(x) \ + SOC_SINGLE("ADC" #x " Highpass-Filter Capture Switch", \ + ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0) + +#define ADAU1977_DC_SUB_SWITCH(x) \ + SOC_SINGLE("ADC" #x " DC Subtraction Capture Switch", \ + ADAU1977_REG_DC_HPF_CAL, (x) + 3, 1, 0) + +static const struct snd_kcontrol_new adau1977_snd_controls[] = { + ADAU1977_VOLUME(1), + ADAU1977_VOLUME(2), + ADAU1977_VOLUME(3), + ADAU1977_VOLUME(4), + + ADAU1977_HPF_SWITCH(1), + ADAU1977_HPF_SWITCH(2), + ADAU1977_HPF_SWITCH(3), + ADAU1977_HPF_SWITCH(4), + + ADAU1977_DC_SUB_SWITCH(1), + ADAU1977_DC_SUB_SWITCH(2), + ADAU1977_DC_SUB_SWITCH(3), + ADAU1977_DC_SUB_SWITCH(4), +}; + +static int adau1977_reset(struct adau1977 *adau1977) +{ + int ret; + + /* + * The reset bit is obviously volatile, but we need to be able to cache + * the other bits in the register, so we can't just mark the whole + * register as volatile. Since this is the only place where we'll ever + * touch the reset bit just bypass the cache for this operation. + */ + regcache_cache_bypass(adau1977->regmap, true); + ret = regmap_write(adau1977->regmap, ADAU1977_REG_POWER, + ADAU1977_POWER_RESET); + regcache_cache_bypass(adau1977->regmap, false); + + return ret; +} + +/* + * Returns the appropriate setting for ths FS field in the CTRL0 register + * depending on the rate. + */ +static int adau1977_lookup_fs(unsigned int rate) +{ + if (rate >= 8000 && rate <= 12000) + return ADAU1977_SAI_CTRL0_FS_8000_12000; + else if (rate >= 16000 && rate <= 24000) + return ADAU1977_SAI_CTRL0_FS_16000_24000; + else if (rate >= 32000 && rate <= 48000) + return ADAU1977_SAI_CTRL0_FS_32000_48000; + else if (rate >= 64000 && rate <= 96000) + return ADAU1977_SAI_CTRL0_FS_64000_96000; + else if (rate >= 128000 && rate <= 192000) + return ADAU1977_SAI_CTRL0_FS_128000_192000; + else + return -EINVAL; +} + +static int adau1977_lookup_mcs(struct adau1977 *adau1977, unsigned int rate, + unsigned int fs) +{ + unsigned int mcs; + + /* + * rate = sysclk / (512 * mcs_lut[mcs]) * 2**fs + * => mcs_lut[mcs] = sysclk / (512 * rate) * 2**fs + * => mcs_lut[mcs] = sysclk / ((512 / 2**fs) * rate) + */ + + rate *= 512 >> fs; + + if (adau1977->sysclk % rate != 0) + return -EINVAL; + + mcs = adau1977->sysclk / rate; + + /* The factors configured by MCS are 1, 2, 3, 4, 6 */ + if (mcs < 1 || mcs > 6 || mcs == 5) + return -EINVAL; + + mcs = mcs - 1; + if (mcs == 5) + mcs = 4; + + return mcs; +} + +static int adau1977_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component); + unsigned int rate = params_rate(params); + unsigned int slot_width; + unsigned int ctrl0, ctrl0_mask; + unsigned int ctrl1; + int mcs, fs; + int ret; + + fs = adau1977_lookup_fs(rate); + if (fs < 0) + return fs; + + if (adau1977->sysclk_src == ADAU1977_SYSCLK_SRC_MCLK) { + mcs = adau1977_lookup_mcs(adau1977, rate, fs); + if (mcs < 0) + return mcs; + } else { + mcs = 0; + } + + ctrl0_mask = ADAU1977_SAI_CTRL0_FS_MASK; + ctrl0 = fs; + + if (adau1977->right_j) { + switch (params_width(params)) { + case 16: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_16BIT; + break; + case 24: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT; + break; + default: + return -EINVAL; + } + ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK; + } + + if (adau1977->clock_provider) { + switch (params_width(params)) { + case 16: + ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT; + slot_width = 16; + break; + case 24: + case 32: + ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_24BIT; + slot_width = 32; + break; + default: + return -EINVAL; + } + + /* In TDM mode there is a fixed slot width */ + if (adau1977->slot_width) + slot_width = adau1977->slot_width; + + if (slot_width == 16) + ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_16; + else + ctrl1 |= ADAU1977_SAI_CTRL1_BCLKRATE_32; + + ret = regmap_update_bits(adau1977->regmap, + ADAU1977_REG_SAI_CTRL1, + ADAU1977_SAI_CTRL1_DATA_WIDTH_MASK | + ADAU1977_SAI_CTRL1_BCLKRATE_MASK, + ctrl1); + if (ret < 0) + return ret; + } + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0, + ctrl0_mask, ctrl0); + if (ret < 0) + return ret; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL, + ADAU1977_PLL_MCS_MASK, mcs); +} + +static int adau1977_power_disable(struct adau1977 *adau1977) +{ + int ret = 0; + + if (!adau1977->enabled) + return 0; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER, + ADAU1977_POWER_PWUP, 0); + if (ret) + return ret; + + regcache_mark_dirty(adau1977->regmap); + + gpiod_set_value_cansleep(adau1977->reset_gpio, 0); + + regcache_cache_only(adau1977->regmap, true); + + regulator_disable(adau1977->avdd_reg); + if (adau1977->dvdd_reg) + regulator_disable(adau1977->dvdd_reg); + + adau1977->enabled = false; + + return 0; +} + +static int adau1977_power_enable(struct adau1977 *adau1977) +{ + unsigned int val; + int ret = 0; + + if (adau1977->enabled) + return 0; + + ret = regulator_enable(adau1977->avdd_reg); + if (ret) + return ret; + + if (adau1977->dvdd_reg) { + ret = regulator_enable(adau1977->dvdd_reg); + if (ret) + goto err_disable_avdd; + } + + gpiod_set_value_cansleep(adau1977->reset_gpio, 1); + + regcache_cache_only(adau1977->regmap, false); + + if (adau1977->switch_mode) + adau1977->switch_mode(adau1977->dev); + + ret = adau1977_reset(adau1977); + if (ret) + goto err_disable_dvdd; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_POWER, + ADAU1977_POWER_PWUP, ADAU1977_POWER_PWUP); + if (ret) + goto err_disable_dvdd; + + ret = regcache_sync(adau1977->regmap); + if (ret) + goto err_disable_dvdd; + + /* + * The PLL register is not affected by the software reset. It is + * possible that the value of the register was changed to the + * default value while we were in cache only mode. In this case + * regcache_sync will skip over it and we have to manually sync + * it. + */ + ret = regmap_read(adau1977->regmap, ADAU1977_REG_PLL, &val); + if (ret) + goto err_disable_dvdd; + + if (val == 0x41) { + regcache_cache_bypass(adau1977->regmap, true); + ret = regmap_write(adau1977->regmap, ADAU1977_REG_PLL, + 0x41); + if (ret) + goto err_disable_dvdd; + regcache_cache_bypass(adau1977->regmap, false); + } + + adau1977->enabled = true; + + return ret; + +err_disable_dvdd: + if (adau1977->dvdd_reg) + regulator_disable(adau1977->dvdd_reg); +err_disable_avdd: + regulator_disable(adau1977->avdd_reg); + return ret; +} + +static int adau1977_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + ret = adau1977_power_enable(adau1977); + break; + case SND_SOC_BIAS_OFF: + ret = adau1977_power_disable(adau1977); + break; + } + + return ret; +} + +static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component); + unsigned int ctrl0, ctrl1, drv; + unsigned int slot[4]; + unsigned int i; + int ret; + + if (slots == 0) { + /* 0 = No fixed slot width */ + adau1977->slot_width = 0; + adau1977->max_clock_provider_fs = 192000; + return regmap_update_bits(adau1977->regmap, + ADAU1977_REG_SAI_CTRL0, ADAU1977_SAI_CTRL0_SAI_MASK, + ADAU1977_SAI_CTRL0_SAI_I2S); + } + + if (rx_mask == 0 || tx_mask != 0) + return -EINVAL; + + drv = 0; + for (i = 0; i < 4; i++) { + slot[i] = __ffs(rx_mask); + drv |= ADAU1977_SAI_OVERTEMP_DRV_C(i); + rx_mask &= ~(1 << slot[i]); + if (slot[i] >= slots) + return -EINVAL; + if (rx_mask == 0) + break; + } + + if (rx_mask != 0) + return -EINVAL; + + switch (width) { + case 16: + ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_16; + break; + case 24: + /* We can only generate 16 bit or 32 bit wide slots */ + if (adau1977->clock_provider) + return -EINVAL; + ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24; + break; + case 32: + ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_32; + break; + default: + return -EINVAL; + } + + switch (slots) { + case 2: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_2; + break; + case 4: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_4; + break; + case 8: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_8; + break; + case 16: + ctrl0 = ADAU1977_SAI_CTRL0_SAI_TDM_16; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP, + ADAU1977_SAI_OVERTEMP_DRV_C(0) | + ADAU1977_SAI_OVERTEMP_DRV_C(1) | + ADAU1977_SAI_OVERTEMP_DRV_C(2) | + ADAU1977_SAI_OVERTEMP_DRV_C(3), drv); + if (ret) + return ret; + + ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP12, + (slot[1] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) | + (slot[0] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET)); + if (ret) + return ret; + + ret = regmap_write(adau1977->regmap, ADAU1977_REG_CMAP34, + (slot[3] << ADAU1977_CHAN_MAP_SECOND_SLOT_OFFSET) | + (slot[2] << ADAU1977_CHAN_MAP_FIRST_SLOT_OFFSET)); + if (ret) + return ret; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0, + ADAU1977_SAI_CTRL0_SAI_MASK, ctrl0); + if (ret) + return ret; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1, + ADAU1977_SAI_CTRL1_SLOT_WIDTH_MASK, ctrl1); + if (ret) + return ret; + + adau1977->slot_width = width; + + /* In clock provider mode the maximum bitclock is 24.576 MHz */ + adau1977->max_clock_provider_fs = min(192000, 24576000 / width / slots); + + return 0; +} + +static int adau1977_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + if (mute) + val = ADAU1977_MISC_CONTROL_MMUTE; + else + val = 0; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MISC_CONTROL, + ADAU1977_MISC_CONTROL_MMUTE, val); +} + +static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component); + unsigned int ctrl0 = 0, ctrl1 = 0, block_power = 0; + bool invert_lrclk; + int ret; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + adau1977->clock_provider = false; + break; + case SND_SOC_DAIFMT_CBP_CFP: + ctrl1 |= ADAU1977_SAI_CTRL1_MASTER; + adau1977->clock_provider = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_IB_NF: + block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE; + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_lrclk = true; + break; + case SND_SOC_DAIFMT_IB_IF: + block_power |= ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE; + invert_lrclk = true; + break; + default: + return -EINVAL; + } + + adau1977->right_j = false; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_RJ_24BIT; + adau1977->right_j = true; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE; + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_I2S; + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1 |= ADAU1977_SAI_CTRL1_LRCLK_PULSE; + ctrl0 |= ADAU1977_SAI_CTRL0_FMT_LJ; + invert_lrclk = false; + break; + default: + return -EINVAL; + } + + if (invert_lrclk) + block_power |= ADAU1977_BLOCK_POWER_SAI_LR_POL; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI, + ADAU1977_BLOCK_POWER_SAI_LR_POL | + ADAU1977_BLOCK_POWER_SAI_BCLK_EDGE, block_power); + if (ret) + return ret; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL0, + ADAU1977_SAI_CTRL0_FMT_MASK, + ctrl0); + if (ret) + return ret; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_CTRL1, + ADAU1977_SAI_CTRL1_MASTER | ADAU1977_SAI_CTRL1_LRCLK_PULSE, + ctrl1); +} + +static int adau1977_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component); + u64 formats = 0; + + if (adau1977->slot_width == 16) + formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE; + else if (adau1977->right_j || adau1977->slot_width == 24) + formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE; + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints); + + if (adau1977->clock_provider) + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 8000, + adau1977->max_clock_provider_fs); + + if (formats != 0) + snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, formats); + + return 0; +} + +static int adau1977_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + if (tristate) + val = ADAU1977_SAI_OVERTEMP_DRV_HIZ; + else + val = 0; + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_SAI_OVERTEMP, + ADAU1977_SAI_OVERTEMP_DRV_HIZ, val); +} + +static const struct snd_soc_dai_ops adau1977_dai_ops = { + .startup = adau1977_startup, + .hw_params = adau1977_hw_params, + .mute_stream = adau1977_mute, + .set_fmt = adau1977_set_dai_fmt, + .set_tdm_slot = adau1977_set_tdm_slot, + .set_tristate = adau1977_set_tristate, +}; + +static struct snd_soc_dai_driver adau1977_dai = { + .name = "adau1977-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .sig_bits = 24, + }, + .ops = &adau1977_dai_ops, +}; + +static const unsigned int adau1977_rates[] = { + 8000, 16000, 32000, 64000, 128000, + 11025, 22050, 44100, 88200, 172400, + 12000, 24000, 48000, 96000, 192000, +}; + +#define ADAU1977_RATE_CONSTRAINT_MASK_32000 0x001f +#define ADAU1977_RATE_CONSTRAINT_MASK_44100 0x03e0 +#define ADAU1977_RATE_CONSTRAINT_MASK_48000 0x7c00 +/* All rates >= 32000 */ +#define ADAU1977_RATE_CONSTRAINT_MASK_LRCLK 0x739c + +static bool adau1977_check_sysclk(unsigned int mclk, unsigned int base_freq) +{ + unsigned int mcs; + + if (mclk % (base_freq * 128) != 0) + return false; + + mcs = mclk / (128 * base_freq); + if (mcs < 1 || mcs > 6 || mcs == 5) + return false; + + return true; +} + +static int adau1977_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component); + unsigned int mask = 0; + unsigned int clk_src; + unsigned int ret; + + if (dir != SND_SOC_CLOCK_IN) + return -EINVAL; + + if (clk_id != ADAU1977_SYSCLK) + return -EINVAL; + + switch (source) { + case ADAU1977_SYSCLK_SRC_MCLK: + clk_src = 0; + break; + case ADAU1977_SYSCLK_SRC_LRCLK: + clk_src = ADAU1977_PLL_CLK_S; + break; + default: + return -EINVAL; + } + + if (freq != 0 && source == ADAU1977_SYSCLK_SRC_MCLK) { + if (freq < 4000000 || freq > 36864000) + return -EINVAL; + + if (adau1977_check_sysclk(freq, 32000)) + mask |= ADAU1977_RATE_CONSTRAINT_MASK_32000; + if (adau1977_check_sysclk(freq, 44100)) + mask |= ADAU1977_RATE_CONSTRAINT_MASK_44100; + if (adau1977_check_sysclk(freq, 48000)) + mask |= ADAU1977_RATE_CONSTRAINT_MASK_48000; + + if (mask == 0) + return -EINVAL; + } else if (source == ADAU1977_SYSCLK_SRC_LRCLK) { + mask = ADAU1977_RATE_CONSTRAINT_MASK_LRCLK; + } + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_PLL, + ADAU1977_PLL_CLK_S, clk_src); + if (ret) + return ret; + + adau1977->constraints.mask = mask; + adau1977->sysclk_src = source; + adau1977->sysclk = freq; + + return 0; +} + +static int adau1977_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component); + int ret; + + switch (adau1977->type) { + case ADAU1977: + ret = snd_soc_dapm_new_controls(dapm, + adau1977_micbias_dapm_widgets, + ARRAY_SIZE(adau1977_micbias_dapm_widgets)); + if (ret < 0) + return ret; + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_component_driver adau1977_component_driver = { + .probe = adau1977_component_probe, + .set_bias_level = adau1977_set_bias_level, + .set_sysclk = adau1977_set_sysclk, + .controls = adau1977_snd_controls, + .num_controls = ARRAY_SIZE(adau1977_snd_controls), + .dapm_widgets = adau1977_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1977_dapm_widgets), + .dapm_routes = adau1977_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1977_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int adau1977_setup_micbias(struct adau1977 *adau1977) +{ + unsigned int micbias; + + if (device_property_read_u32(adau1977->dev, "adi,micbias", &micbias)) + micbias = ADAU1977_MICBIAS_8V5; + + if (micbias > ADAU1977_MICBIAS_9V0) { + dev_err(adau1977->dev, "Invalid value for 'adi,micbias'\n"); + return -EINVAL; + } + + return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MICBIAS, + ADAU1977_MICBIAS_MB_VOLTS_MASK, + micbias << ADAU1977_MICBIAS_MB_VOLTS_OFFSET); +} + +int adau1977_probe(struct device *dev, struct regmap *regmap, + enum adau1977_type type, void (*switch_mode)(struct device *dev)) +{ + unsigned int power_off_mask; + struct adau1977 *adau1977; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + adau1977 = devm_kzalloc(dev, sizeof(*adau1977), GFP_KERNEL); + if (adau1977 == NULL) + return -ENOMEM; + + adau1977->dev = dev; + adau1977->type = type; + adau1977->regmap = regmap; + adau1977->switch_mode = switch_mode; + adau1977->max_clock_provider_fs = 192000; + + adau1977->constraints.list = adau1977_rates; + adau1977->constraints.count = ARRAY_SIZE(adau1977_rates); + + adau1977->avdd_reg = devm_regulator_get(dev, "AVDD"); + if (IS_ERR(adau1977->avdd_reg)) + return PTR_ERR(adau1977->avdd_reg); + + adau1977->dvdd_reg = devm_regulator_get_optional(dev, "DVDD"); + if (IS_ERR(adau1977->dvdd_reg)) { + if (PTR_ERR(adau1977->dvdd_reg) != -ENODEV) + return PTR_ERR(adau1977->dvdd_reg); + adau1977->dvdd_reg = NULL; + } + + adau1977->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(adau1977->reset_gpio)) + return PTR_ERR(adau1977->reset_gpio); + + dev_set_drvdata(dev, adau1977); + + if (adau1977->reset_gpio) + ndelay(100); + + ret = adau1977_power_enable(adau1977); + if (ret) + return ret; + + if (type == ADAU1977) { + ret = adau1977_setup_micbias(adau1977); + if (ret) + goto err_poweroff; + } + + if (adau1977->dvdd_reg) + power_off_mask = ~0; + else + power_off_mask = (unsigned int)~ADAU1977_BLOCK_POWER_SAI_LDO_EN; + + ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI, + power_off_mask, 0x00); + if (ret) + goto err_poweroff; + + ret = adau1977_power_disable(adau1977); + if (ret) + return ret; + + return devm_snd_soc_register_component(dev, &adau1977_component_driver, + &adau1977_dai, 1); + +err_poweroff: + adau1977_power_disable(adau1977); + return ret; + +} +EXPORT_SYMBOL_GPL(adau1977_probe); + +static bool adau1977_register_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADAU1977_REG_STATUS(0): + case ADAU1977_REG_STATUS(1): + case ADAU1977_REG_STATUS(2): + case ADAU1977_REG_STATUS(3): + case ADAU1977_REG_ADC_CLIP: + return true; + } + + return false; +} + +const struct regmap_config adau1977_regmap_config = { + .max_register = ADAU1977_REG_DC_HPF_CAL, + .volatile_reg = adau1977_register_volatile, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = adau1977_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults), +}; +EXPORT_SYMBOL_GPL(adau1977_regmap_config); + +MODULE_DESCRIPTION("ASoC ADAU1977/ADAU1978/ADAU1979 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1977.h b/sound/soc/codecs/adau1977.h new file mode 100644 index 0000000000..80baeb4f39 --- /dev/null +++ b/sound/soc/codecs/adau1977.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADAU1977/ADAU1978/ADAU1979 driver + * + * Copyright 2014 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#ifndef __SOUND_SOC_CODECS_ADAU1977_H__ +#define __SOUND_SOC_CODECS_ADAU1977_H__ + +#include + +struct device; + +enum adau1977_type { + ADAU1977, + ADAU1978, + ADAU1979, +}; + +int adau1977_probe(struct device *dev, struct regmap *regmap, + enum adau1977_type type, void (*switch_mode)(struct device *dev)); + +extern const struct regmap_config adau1977_regmap_config; + +enum adau1977_clk_id { + ADAU1977_SYSCLK, +}; + +enum adau1977_sysclk_src { + ADAU1977_SYSCLK_SRC_MCLK, + ADAU1977_SYSCLK_SRC_LRCLK, +}; + +#endif diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c new file mode 100644 index 0000000000..c9134e1de0 --- /dev/null +++ b/sound/soc/codecs/adau7002.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADAU7002 Stereo PDM-to-I2S/TDM converter driver + * + * Copyright 2014-2016 Analog Devices + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include + +#include + +struct adau7002_priv { + int wakeup_delay; +}; + +static int adau7002_aif_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct adau7002_priv *adau7002 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (adau7002->wakeup_delay) + msleep(adau7002->wakeup_delay); + break; + } + + return 0; +} + +static int adau7002_component_probe(struct snd_soc_component *component) +{ + struct adau7002_priv *adau7002; + + adau7002 = devm_kzalloc(component->dev, sizeof(*adau7002), + GFP_KERNEL); + if (!adau7002) + return -ENOMEM; + + device_property_read_u32(component->dev, "wakeup-delay-ms", + &adau7002->wakeup_delay); + + snd_soc_component_set_drvdata(component, adau7002); + + return 0; +} + +static const struct snd_soc_dapm_widget adau7002_widgets[] = { + SND_SOC_DAPM_AIF_OUT_E("ADAU AIF", "Capture", 0, + SND_SOC_NOPM, 0, 0, adau7002_aif_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_INPUT("PDM_DAT"), + SND_SOC_DAPM_REGULATOR_SUPPLY("IOVDD", 0, 0), +}; + +static const struct snd_soc_dapm_route adau7002_routes[] = { + { "ADAU AIF", NULL, "PDM_DAT"}, + { "Capture", NULL, "PDM_DAT" }, + { "Capture", NULL, "IOVDD" }, +}; + +static struct snd_soc_dai_driver adau7002_dai = { + .name = "adau7002-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE, + .sig_bits = 20, + }, +}; + +static const struct snd_soc_component_driver adau7002_component_driver = { + .probe = adau7002_component_probe, + .dapm_widgets = adau7002_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets), + .dapm_routes = adau7002_routes, + .num_dapm_routes = ARRAY_SIZE(adau7002_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int adau7002_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &adau7002_component_driver, + &adau7002_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id adau7002_dt_ids[] = { + { .compatible = "adi,adau7002", }, + { } +}; +MODULE_DEVICE_TABLE(of, adau7002_dt_ids); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id adau7002_acpi_match[] = { + { "ADAU7002", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, adau7002_acpi_match); +#endif + +static struct platform_driver adau7002_driver = { + .driver = { + .name = "adau7002", + .of_match_table = of_match_ptr(adau7002_dt_ids), + .acpi_match_table = ACPI_PTR(adau7002_acpi_match), + }, + .probe = adau7002_probe, +}; +module_platform_driver(adau7002_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("ADAU7002 Stereo PDM-to-I2S/TDM Converter driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c new file mode 100644 index 0000000000..45a5d2dcc0 --- /dev/null +++ b/sound/soc/codecs/adau7118-hw.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter Standalone Hw +// driver +// +// Copyright 2019 Analog Devices Inc. + +#include +#include +#include + +#include "adau7118.h" + +static int adau7118_probe_hw(struct platform_device *pdev) +{ + return adau7118_probe(&pdev->dev, NULL, true); +} + +static const struct of_device_id adau7118_of_match[] = { + { .compatible = "adi,adau7118" }, + {} +}; +MODULE_DEVICE_TABLE(of, adau7118_of_match); + +static const struct platform_device_id adau7118_id[] = { + { .name = "adau7118" }, + { } +}; +MODULE_DEVICE_TABLE(platform, adau7118_id); + +static struct platform_driver adau7118_driver_hw = { + .driver = { + .name = "adau7118", + .of_match_table = adau7118_of_match, + }, + .probe = adau7118_probe_hw, + .id_table = adau7118_id, +}; +module_platform_driver(adau7118_driver_hw); + +MODULE_AUTHOR("Nuno Sa "); +MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver for standalone hw mode"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c new file mode 100644 index 0000000000..b302b28eca --- /dev/null +++ b/sound/soc/codecs/adau7118-i2c.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C +// +// Copyright 2019 Analog Devices Inc. + +#include +#include +#include + +#include "adau7118.h" + +static const struct reg_default adau7118_reg_defaults[] = { + { ADAU7118_REG_VENDOR_ID, 0x41 }, + { ADAU7118_REG_DEVICE_ID1, 0x71 }, + { ADAU7118_REG_DEVICE_ID2, 0x18 }, + { ADAU7118_REG_REVISION_ID, 0x00 }, + { ADAU7118_REG_ENABLES, 0x3F }, + { ADAU7118_REG_DEC_RATIO_CLK_MAP, 0xC0 }, + { ADAU7118_REG_HPF_CONTROL, 0xD0 }, + { ADAU7118_REG_SPT_CTRL1, 0x41 }, + { ADAU7118_REG_SPT_CTRL2, 0x00 }, + { ADAU7118_REG_SPT_CX(0), 0x01 }, + { ADAU7118_REG_SPT_CX(1), 0x11 }, + { ADAU7118_REG_SPT_CX(2), 0x21 }, + { ADAU7118_REG_SPT_CX(3), 0x31 }, + { ADAU7118_REG_SPT_CX(4), 0x41 }, + { ADAU7118_REG_SPT_CX(5), 0x51 }, + { ADAU7118_REG_SPT_CX(6), 0x61 }, + { ADAU7118_REG_SPT_CX(7), 0x71 }, + { ADAU7118_REG_DRIVE_STRENGTH, 0x2a }, + { ADAU7118_REG_RESET, 0x00 }, +}; + +static bool adau7118_volatile(struct device *dev, unsigned int reg) +{ + return (reg == ADAU7118_REG_RESET); +} + + +static const struct regmap_config adau7118_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .reg_defaults = adau7118_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adau7118_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .max_register = ADAU7118_REG_RESET, + .volatile_reg = adau7118_volatile, +}; + +static int adau7118_probe_i2c(struct i2c_client *i2c) +{ + struct regmap *map; + + map = devm_regmap_init_i2c(i2c, &adau7118_regmap_config); + if (IS_ERR(map)) { + dev_err(&i2c->dev, "Failed to init regmap %ld\n", PTR_ERR(map)); + return PTR_ERR(map); + } + + return adau7118_probe(&i2c->dev, map, false); +} + +static const struct of_device_id adau7118_of_match[] = { + { .compatible = "adi,adau7118" }, + {} +}; +MODULE_DEVICE_TABLE(of, adau7118_of_match); + +static const struct i2c_device_id adau7118_id[] = { + {"adau7118", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, adau7118_id); + +static struct i2c_driver adau7118_driver = { + .driver = { + .name = "adau7118", + .of_match_table = adau7118_of_match, + }, + .probe = adau7118_probe_i2c, + .id_table = adau7118_id, +}; +module_i2c_driver(adau7118_driver); + +MODULE_AUTHOR("Nuno Sa "); +MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver over I2C"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau7118.c b/sound/soc/codecs/adau7118.c new file mode 100644 index 0000000000..a663d37e57 --- /dev/null +++ b/sound/soc/codecs/adau7118.c @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver +// +// Copyright 2019 Analog Devices Inc. + +#include +#include +#include +#include +#include +#include + +#include "adau7118.h" + +#define ADAU7118_DEC_RATIO_MASK GENMASK(1, 0) +#define ADAU7118_DEC_RATIO(x) FIELD_PREP(ADAU7118_DEC_RATIO_MASK, x) +#define ADAU7118_CLK_MAP_MASK GENMASK(7, 4) +#define ADAU7118_SLOT_WIDTH_MASK GENMASK(5, 4) +#define ADAU7118_SLOT_WIDTH(x) FIELD_PREP(ADAU7118_SLOT_WIDTH_MASK, x) +#define ADAU7118_TRISTATE_MASK BIT(6) +#define ADAU7118_TRISTATE(x) FIELD_PREP(ADAU7118_TRISTATE_MASK, x) +#define ADAU7118_DATA_FMT_MASK GENMASK(3, 1) +#define ADAU7118_DATA_FMT(x) FIELD_PREP(ADAU7118_DATA_FMT_MASK, x) +#define ADAU7118_SAI_MODE_MASK BIT(0) +#define ADAU7118_SAI_MODE(x) FIELD_PREP(ADAU7118_SAI_MODE_MASK, x) +#define ADAU7118_LRCLK_BCLK_POL_MASK GENMASK(1, 0) +#define ADAU7118_LRCLK_BCLK_POL(x) \ + FIELD_PREP(ADAU7118_LRCLK_BCLK_POL_MASK, x) +#define ADAU7118_SPT_SLOT_MASK GENMASK(7, 4) +#define ADAU7118_SPT_SLOT(x) FIELD_PREP(ADAU7118_SPT_SLOT_MASK, x) +#define ADAU7118_FULL_SOFT_R_MASK BIT(1) +#define ADAU7118_FULL_SOFT_R(x) FIELD_PREP(ADAU7118_FULL_SOFT_R_MASK, x) + +struct adau7118_data { + struct regmap *map; + struct device *dev; + struct regulator *iovdd; + struct regulator *dvdd; + u32 slot_width; + u32 slots; + bool hw_mode; + bool right_j; +}; + +/* Input Enable */ +static const struct snd_kcontrol_new adau7118_dapm_pdm_control[4] = { + SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 0, 1, 0), + SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 1, 1, 0), + SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 2, 1, 0), + SOC_DAPM_SINGLE("Capture Switch", ADAU7118_REG_ENABLES, 3, 1, 0), +}; + +static const struct snd_soc_dapm_widget adau7118_widgets_sw[] = { + /* Input Enable Switches */ + SND_SOC_DAPM_SWITCH("PDM0", SND_SOC_NOPM, 0, 0, + &adau7118_dapm_pdm_control[0]), + SND_SOC_DAPM_SWITCH("PDM1", SND_SOC_NOPM, 0, 0, + &adau7118_dapm_pdm_control[1]), + SND_SOC_DAPM_SWITCH("PDM2", SND_SOC_NOPM, 0, 0, + &adau7118_dapm_pdm_control[2]), + SND_SOC_DAPM_SWITCH("PDM3", SND_SOC_NOPM, 0, 0, + &adau7118_dapm_pdm_control[3]), + + /* PDM Clocks */ + SND_SOC_DAPM_SUPPLY("PDM_CLK0", ADAU7118_REG_ENABLES, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PDM_CLK1", ADAU7118_REG_ENABLES, 5, 0, NULL, 0), + + /* Output channels */ + SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, ADAU7118_REG_SPT_CX(0), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 0, ADAU7118_REG_SPT_CX(1), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 0, ADAU7118_REG_SPT_CX(2), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 0, ADAU7118_REG_SPT_CX(3), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 0, ADAU7118_REG_SPT_CX(4), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 0, ADAU7118_REG_SPT_CX(5), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX7", "Capture", 0, ADAU7118_REG_SPT_CX(6), + 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX8", "Capture", 0, ADAU7118_REG_SPT_CX(7), + 0, 0), +}; + +static const struct snd_soc_dapm_route adau7118_routes_sw[] = { + { "PDM0", "Capture Switch", "PDM_DAT0" }, + { "PDM1", "Capture Switch", "PDM_DAT1" }, + { "PDM2", "Capture Switch", "PDM_DAT2" }, + { "PDM3", "Capture Switch", "PDM_DAT3" }, + { "AIF1TX1", NULL, "PDM0" }, + { "AIF1TX2", NULL, "PDM0" }, + { "AIF1TX3", NULL, "PDM1" }, + { "AIF1TX4", NULL, "PDM1" }, + { "AIF1TX5", NULL, "PDM2" }, + { "AIF1TX6", NULL, "PDM2" }, + { "AIF1TX7", NULL, "PDM3" }, + { "AIF1TX8", NULL, "PDM3" }, + { "Capture", NULL, "PDM_CLK0" }, + { "Capture", NULL, "PDM_CLK1" }, +}; + +static const struct snd_soc_dapm_widget adau7118_widgets_hw[] = { + SND_SOC_DAPM_AIF_OUT("AIF1TX", "Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route adau7118_routes_hw[] = { + { "AIF1TX", NULL, "PDM_DAT0" }, + { "AIF1TX", NULL, "PDM_DAT1" }, + { "AIF1TX", NULL, "PDM_DAT2" }, + { "AIF1TX", NULL, "PDM_DAT3" }, +}; + +static const struct snd_soc_dapm_widget adau7118_widgets[] = { + SND_SOC_DAPM_INPUT("PDM_DAT0"), + SND_SOC_DAPM_INPUT("PDM_DAT1"), + SND_SOC_DAPM_INPUT("PDM_DAT2"), + SND_SOC_DAPM_INPUT("PDM_DAT3"), +}; + +static int adau7118_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct adau7118_data *st = + snd_soc_component_get_drvdata(dai->component); + int chan, ret; + + dev_dbg(st->dev, "Set channel map, %d", tx_num); + + for (chan = 0; chan < tx_num; chan++) { + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CX(chan), + ADAU7118_SPT_SLOT_MASK, + ADAU7118_SPT_SLOT(tx_slot[chan])); + if (ret < 0) + return ret; + } + + return 0; +} + +static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct adau7118_data *st = + snd_soc_component_get_drvdata(dai->component); + int ret = 0; + u32 regval; + + dev_dbg(st->dev, "Set format, fmt:%d\n", fmt); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_DATA_FMT_MASK, + ADAU7118_DATA_FMT(0)); + break; + case SND_SOC_DAIFMT_LEFT_J: + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_DATA_FMT_MASK, + ADAU7118_DATA_FMT(1)); + break; + case SND_SOC_DAIFMT_RIGHT_J: + st->right_j = true; + break; + default: + dev_err(st->dev, "Invalid format %d", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + if (ret < 0) + return ret; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + regval = ADAU7118_LRCLK_BCLK_POL(0); + break; + case SND_SOC_DAIFMT_NB_IF: + regval = ADAU7118_LRCLK_BCLK_POL(2); + break; + case SND_SOC_DAIFMT_IB_NF: + regval = ADAU7118_LRCLK_BCLK_POL(1); + break; + case SND_SOC_DAIFMT_IB_IF: + regval = ADAU7118_LRCLK_BCLK_POL(3); + break; + default: + dev_err(st->dev, "Invalid Inv mask %d", + fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL2, + ADAU7118_LRCLK_BCLK_POL_MASK, + regval); + if (ret < 0) + return ret; + + return 0; +} + +static int adau7118_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct adau7118_data *st = + snd_soc_component_get_drvdata(dai->component); + int ret; + + dev_dbg(st->dev, "Set tristate, %d\n", tristate); + + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_TRISTATE_MASK, + ADAU7118_TRISTATE(tristate)); + if (ret < 0) + return ret; + + return 0; +} + +static int adau7118_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, + int slot_width) +{ + struct adau7118_data *st = + snd_soc_component_get_drvdata(dai->component); + int ret = 0; + u32 regval; + + dev_dbg(st->dev, "Set tdm, slots:%d width:%d\n", slots, slot_width); + + switch (slot_width) { + case 32: + regval = ADAU7118_SLOT_WIDTH(0); + break; + case 24: + regval = ADAU7118_SLOT_WIDTH(2); + break; + case 16: + regval = ADAU7118_SLOT_WIDTH(1); + break; + default: + dev_err(st->dev, "Invalid slot width:%d\n", slot_width); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_SLOT_WIDTH_MASK, regval); + if (ret < 0) + return ret; + + st->slot_width = slot_width; + st->slots = slots; + + return 0; +} + +static int adau7118_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct adau7118_data *st = + snd_soc_component_get_drvdata(dai->component); + u32 data_width = params_width(params), slots_width; + int ret; + u32 regval; + + if (!st->slots) { + /* set stereo mode */ + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_SAI_MODE_MASK, + ADAU7118_SAI_MODE(0)); + if (ret < 0) + return ret; + + slots_width = 32; + } else { + slots_width = st->slot_width; + } + + if (data_width > slots_width) { + dev_err(st->dev, "Invalid data_width:%d, slots_width:%d", + data_width, slots_width); + return -EINVAL; + } + + if (st->right_j) { + switch (slots_width - data_width) { + case 8: + /* delay bclck by 8 */ + regval = ADAU7118_DATA_FMT(2); + break; + case 12: + /* delay bclck by 12 */ + regval = ADAU7118_DATA_FMT(3); + break; + case 16: + /* delay bclck by 16 */ + regval = ADAU7118_DATA_FMT(4); + break; + default: + dev_err(st->dev, + "Cannot set right_j setting, slot_w:%d, data_w:%d\n", + slots_width, data_width); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(dai->component, + ADAU7118_REG_SPT_CTRL1, + ADAU7118_DATA_FMT_MASK, + regval); + if (ret < 0) + return ret; + } + + return 0; +} + +static int adau7118_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adau7118_data *st = snd_soc_component_get_drvdata(component); + int ret = 0; + + dev_dbg(st->dev, "Set bias level %d\n", level); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == + SND_SOC_BIAS_OFF) { + /* power on */ + ret = regulator_enable(st->iovdd); + if (ret) + return ret; + + /* there's no timing constraints before enabling dvdd */ + ret = regulator_enable(st->dvdd); + if (ret) { + regulator_disable(st->iovdd); + return ret; + } + + if (st->hw_mode) + return 0; + + regcache_cache_only(st->map, false); + /* sync cache */ + ret = snd_soc_component_cache_sync(component); + } + break; + case SND_SOC_BIAS_OFF: + /* power off */ + ret = regulator_disable(st->dvdd); + if (ret) + return ret; + + ret = regulator_disable(st->iovdd); + if (ret) + return ret; + + if (st->hw_mode) + return 0; + + /* cache only */ + regcache_mark_dirty(st->map); + regcache_cache_only(st->map, true); + + break; + } + + return ret; +} + +static int adau7118_component_probe(struct snd_soc_component *component) +{ + struct adau7118_data *st = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + int ret = 0; + + if (st->hw_mode) { + ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_hw, + ARRAY_SIZE(adau7118_widgets_hw)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_hw, + ARRAY_SIZE(adau7118_routes_hw)); + } else { + snd_soc_component_init_regmap(component, st->map); + ret = snd_soc_dapm_new_controls(dapm, adau7118_widgets_sw, + ARRAY_SIZE(adau7118_widgets_sw)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, adau7118_routes_sw, + ARRAY_SIZE(adau7118_routes_sw)); + } + + return ret; +} + +static const struct snd_soc_dai_ops adau7118_ops = { + .hw_params = adau7118_hw_params, + .set_channel_map = adau7118_set_channel_map, + .set_fmt = adau7118_set_fmt, + .set_tdm_slot = adau7118_set_tdm_slot, + .set_tristate = adau7118_set_tristate, +}; + +static struct snd_soc_dai_driver adau7118_dai = { + .name = "adau7118-hifi-capture", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 4000, + .rate_max = 192000, + .sig_bits = 24, + }, +}; + +static const struct snd_soc_component_driver adau7118_component_driver = { + .probe = adau7118_component_probe, + .set_bias_level = adau7118_set_bias_level, + .dapm_widgets = adau7118_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau7118_widgets), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int adau7118_regulator_setup(struct adau7118_data *st) +{ + st->iovdd = devm_regulator_get(st->dev, "iovdd"); + if (IS_ERR(st->iovdd)) { + dev_err(st->dev, "Could not get iovdd: %ld\n", + PTR_ERR(st->iovdd)); + return PTR_ERR(st->iovdd); + } + + st->dvdd = devm_regulator_get(st->dev, "dvdd"); + if (IS_ERR(st->dvdd)) { + dev_err(st->dev, "Could not get dvdd: %ld\n", + PTR_ERR(st->dvdd)); + return PTR_ERR(st->dvdd); + } + /* just assume the device is in reset */ + if (!st->hw_mode) { + regcache_mark_dirty(st->map); + regcache_cache_only(st->map, true); + } + + return 0; +} + +static int adau7118_parset_dt(const struct adau7118_data *st) +{ + int ret; + u32 dec_ratio = 0; + /* 4 inputs */ + u32 clk_map[4], regval; + + if (st->hw_mode) + return 0; + + ret = device_property_read_u32(st->dev, "adi,decimation-ratio", + &dec_ratio); + if (!ret) { + switch (dec_ratio) { + case 64: + regval = ADAU7118_DEC_RATIO(0); + break; + case 32: + regval = ADAU7118_DEC_RATIO(1); + break; + case 16: + regval = ADAU7118_DEC_RATIO(2); + break; + default: + dev_err(st->dev, "Invalid dec ratio: %u", dec_ratio); + return -EINVAL; + } + + ret = regmap_update_bits(st->map, + ADAU7118_REG_DEC_RATIO_CLK_MAP, + ADAU7118_DEC_RATIO_MASK, regval); + if (ret) + return ret; + } + + ret = device_property_read_u32_array(st->dev, "adi,pdm-clk-map", + clk_map, ARRAY_SIZE(clk_map)); + if (!ret) { + int pdm; + u32 _clk_map = 0; + + for (pdm = 0; pdm < ARRAY_SIZE(clk_map); pdm++) + _clk_map |= (clk_map[pdm] << (pdm + 4)); + + ret = regmap_update_bits(st->map, + ADAU7118_REG_DEC_RATIO_CLK_MAP, + ADAU7118_CLK_MAP_MASK, _clk_map); + if (ret) + return ret; + } + + return 0; +} + +int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode) +{ + struct adau7118_data *st; + int ret; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->dev = dev; + st->hw_mode = hw_mode; + dev_set_drvdata(dev, st); + + if (!hw_mode) { + st->map = map; + adau7118_dai.ops = &adau7118_ops; + /* + * Perform a full soft reset. This will set all register's + * with their reset values. + */ + ret = regmap_update_bits(map, ADAU7118_REG_RESET, + ADAU7118_FULL_SOFT_R_MASK, + ADAU7118_FULL_SOFT_R(1)); + if (ret) + return ret; + } + + ret = adau7118_parset_dt(st); + if (ret) + return ret; + + ret = adau7118_regulator_setup(st); + if (ret) + return ret; + + return devm_snd_soc_register_component(dev, + &adau7118_component_driver, + &adau7118_dai, 1); +} +EXPORT_SYMBOL_GPL(adau7118_probe); + +MODULE_AUTHOR("Nuno Sa "); +MODULE_DESCRIPTION("ADAU7118 8 channel PDM-to-I2S/TDM Converter driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau7118.h b/sound/soc/codecs/adau7118.h new file mode 100644 index 0000000000..c65679a4df --- /dev/null +++ b/sound/soc/codecs/adau7118.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_ADAU7118_H +#define _LINUX_ADAU7118_H + +struct regmap; +struct device; + +/* register map */ +#define ADAU7118_REG_VENDOR_ID 0x00 +#define ADAU7118_REG_DEVICE_ID1 0x01 +#define ADAU7118_REG_DEVICE_ID2 0x02 +#define ADAU7118_REG_REVISION_ID 0x03 +#define ADAU7118_REG_ENABLES 0x04 +#define ADAU7118_REG_DEC_RATIO_CLK_MAP 0x05 +#define ADAU7118_REG_HPF_CONTROL 0x06 +#define ADAU7118_REG_SPT_CTRL1 0x07 +#define ADAU7118_REG_SPT_CTRL2 0x08 +#define ADAU7118_REG_SPT_CX(num) (0x09 + (num)) +#define ADAU7118_REG_DRIVE_STRENGTH 0x11 +#define ADAU7118_REG_RESET 0x12 + +int adau7118_probe(struct device *dev, struct regmap *map, bool hw_mode); + +#endif diff --git a/sound/soc/codecs/adav801.c b/sound/soc/codecs/adav801.c new file mode 100644 index 0000000000..f734c71211 --- /dev/null +++ b/sound/soc/codecs/adav801.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADAV801 audio driver + * + * Copyright 2014 Analog Devices Inc. + */ + +#include +#include +#include + +#include + +#include "adav80x.h" + +static const struct spi_device_id adav80x_spi_id[] = { + { "adav801", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adav80x_spi_id); + +static int adav80x_spi_probe(struct spi_device *spi) +{ + struct regmap_config config; + + config = adav80x_regmap_config; + config.read_flag_mask = 0x01; + + return adav80x_bus_probe(&spi->dev, devm_regmap_init_spi(spi, &config)); +} + +static struct spi_driver adav80x_spi_driver = { + .driver = { + .name = "adav801", + }, + .probe = adav80x_spi_probe, + .id_table = adav80x_spi_id, +}; +module_spi_driver(adav80x_spi_driver); + +MODULE_DESCRIPTION("ASoC ADAV801 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_AUTHOR("Yi Li >"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c new file mode 100644 index 0000000000..78a317947d --- /dev/null +++ b/sound/soc/codecs/adav803.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADAV803 audio driver + * + * Copyright 2014 Analog Devices Inc. + */ + +#include +#include +#include + +#include + +#include "adav80x.h" + +static const struct i2c_device_id adav803_id[] = { + { "adav803", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adav803_id); + +static int adav803_probe(struct i2c_client *client) +{ + return adav80x_bus_probe(&client->dev, + devm_regmap_init_i2c(client, &adav80x_regmap_config)); +} + +static struct i2c_driver adav803_driver = { + .driver = { + .name = "adav803", + }, + .probe = adav803_probe, + .id_table = adav803_id, +}; +module_i2c_driver(adav803_driver); + +MODULE_DESCRIPTION("ASoC ADAV803 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_AUTHOR("Yi Li >"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c new file mode 100644 index 0000000000..bb08969c59 --- /dev/null +++ b/sound/soc/codecs/adav80x.c @@ -0,0 +1,882 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ADAV80X Audio Codec driver supporting ADAV801, ADAV803 + * + * Copyright 2011 Analog Devices Inc. + * Author: Yi Li + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adav80x.h" + +#define ADAV80X_PLAYBACK_CTRL 0x04 +#define ADAV80X_AUX_IN_CTRL 0x05 +#define ADAV80X_REC_CTRL 0x06 +#define ADAV80X_AUX_OUT_CTRL 0x07 +#define ADAV80X_DPATH_CTRL1 0x62 +#define ADAV80X_DPATH_CTRL2 0x63 +#define ADAV80X_DAC_CTRL1 0x64 +#define ADAV80X_DAC_CTRL2 0x65 +#define ADAV80X_DAC_CTRL3 0x66 +#define ADAV80X_DAC_L_VOL 0x68 +#define ADAV80X_DAC_R_VOL 0x69 +#define ADAV80X_PGA_L_VOL 0x6c +#define ADAV80X_PGA_R_VOL 0x6d +#define ADAV80X_ADC_CTRL1 0x6e +#define ADAV80X_ADC_CTRL2 0x6f +#define ADAV80X_ADC_L_VOL 0x70 +#define ADAV80X_ADC_R_VOL 0x71 +#define ADAV80X_PLL_CTRL1 0x74 +#define ADAV80X_PLL_CTRL2 0x75 +#define ADAV80X_ICLK_CTRL1 0x76 +#define ADAV80X_ICLK_CTRL2 0x77 +#define ADAV80X_PLL_CLK_SRC 0x78 +#define ADAV80X_PLL_OUTE 0x7a + +#define ADAV80X_PLL_CLK_SRC_PLL_XIN(pll) 0x00 +#define ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll) (0x40 << (pll)) +#define ADAV80X_PLL_CLK_SRC_PLL_MASK(pll) (0x40 << (pll)) + +#define ADAV80X_ICLK_CTRL1_DAC_SRC(src) ((src) << 5) +#define ADAV80X_ICLK_CTRL1_ADC_SRC(src) ((src) << 2) +#define ADAV80X_ICLK_CTRL1_ICLK2_SRC(src) (src) +#define ADAV80X_ICLK_CTRL2_ICLK1_SRC(src) ((src) << 3) + +#define ADAV80X_PLL_CTRL1_PLLDIV 0x10 +#define ADAV80X_PLL_CTRL1_PLLPD(pll) (0x04 << (pll)) +#define ADAV80X_PLL_CTRL1_XTLPD 0x02 + +#define ADAV80X_PLL_CTRL2_FIELD(pll, x) ((x) << ((pll) * 4)) + +#define ADAV80X_PLL_CTRL2_FS_48(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x00) +#define ADAV80X_PLL_CTRL2_FS_32(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x08) +#define ADAV80X_PLL_CTRL2_FS_44(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0c) + +#define ADAV80X_PLL_CTRL2_SEL(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x02) +#define ADAV80X_PLL_CTRL2_DOUB(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x01) +#define ADAV80X_PLL_CTRL2_PLL_MASK(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0f) + +#define ADAV80X_ADC_CTRL1_MODULATOR_MASK 0x80 +#define ADAV80X_ADC_CTRL1_MODULATOR_128FS 0x00 +#define ADAV80X_ADC_CTRL1_MODULATOR_64FS 0x80 + +#define ADAV80X_DAC_CTRL1_PD 0x80 + +#define ADAV80X_DAC_CTRL2_DIV1 0x00 +#define ADAV80X_DAC_CTRL2_DIV1_5 0x10 +#define ADAV80X_DAC_CTRL2_DIV2 0x20 +#define ADAV80X_DAC_CTRL2_DIV3 0x30 +#define ADAV80X_DAC_CTRL2_DIV_MASK 0x30 + +#define ADAV80X_DAC_CTRL2_INTERPOL_256FS 0x00 +#define ADAV80X_DAC_CTRL2_INTERPOL_128FS 0x40 +#define ADAV80X_DAC_CTRL2_INTERPOL_64FS 0x80 +#define ADAV80X_DAC_CTRL2_INTERPOL_MASK 0xc0 + +#define ADAV80X_DAC_CTRL2_DEEMPH_NONE 0x00 +#define ADAV80X_DAC_CTRL2_DEEMPH_44 0x01 +#define ADAV80X_DAC_CTRL2_DEEMPH_32 0x02 +#define ADAV80X_DAC_CTRL2_DEEMPH_48 0x03 +#define ADAV80X_DAC_CTRL2_DEEMPH_MASK 0x01 + +#define ADAV80X_CAPTURE_MODE_MASTER 0x20 +#define ADAV80X_CAPTURE_WORD_LEN24 0x00 +#define ADAV80X_CAPTURE_WORD_LEN20 0x04 +#define ADAV80X_CAPTRUE_WORD_LEN18 0x08 +#define ADAV80X_CAPTURE_WORD_LEN16 0x0c +#define ADAV80X_CAPTURE_WORD_LEN_MASK 0x0c + +#define ADAV80X_CAPTURE_MODE_LEFT_J 0x00 +#define ADAV80X_CAPTURE_MODE_I2S 0x01 +#define ADAV80X_CAPTURE_MODE_RIGHT_J 0x03 +#define ADAV80X_CAPTURE_MODE_MASK 0x03 + +#define ADAV80X_PLAYBACK_MODE_MASTER 0x10 +#define ADAV80X_PLAYBACK_MODE_LEFT_J 0x00 +#define ADAV80X_PLAYBACK_MODE_I2S 0x01 +#define ADAV80X_PLAYBACK_MODE_RIGHT_J_24 0x04 +#define ADAV80X_PLAYBACK_MODE_RIGHT_J_20 0x05 +#define ADAV80X_PLAYBACK_MODE_RIGHT_J_18 0x06 +#define ADAV80X_PLAYBACK_MODE_RIGHT_J_16 0x07 +#define ADAV80X_PLAYBACK_MODE_MASK 0x07 + +#define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x)) + +static const struct reg_default adav80x_reg_defaults[] = { + { ADAV80X_PLAYBACK_CTRL, 0x01 }, + { ADAV80X_AUX_IN_CTRL, 0x01 }, + { ADAV80X_REC_CTRL, 0x02 }, + { ADAV80X_AUX_OUT_CTRL, 0x01 }, + { ADAV80X_DPATH_CTRL1, 0xc0 }, + { ADAV80X_DPATH_CTRL2, 0x11 }, + { ADAV80X_DAC_CTRL1, 0x00 }, + { ADAV80X_DAC_CTRL2, 0x00 }, + { ADAV80X_DAC_CTRL3, 0x00 }, + { ADAV80X_DAC_L_VOL, 0xff }, + { ADAV80X_DAC_R_VOL, 0xff }, + { ADAV80X_PGA_L_VOL, 0x00 }, + { ADAV80X_PGA_R_VOL, 0x00 }, + { ADAV80X_ADC_CTRL1, 0x00 }, + { ADAV80X_ADC_CTRL2, 0x00 }, + { ADAV80X_ADC_L_VOL, 0xff }, + { ADAV80X_ADC_R_VOL, 0xff }, + { ADAV80X_PLL_CTRL1, 0x00 }, + { ADAV80X_PLL_CTRL2, 0x00 }, + { ADAV80X_ICLK_CTRL1, 0x00 }, + { ADAV80X_ICLK_CTRL2, 0x00 }, + { ADAV80X_PLL_CLK_SRC, 0x00 }, + { ADAV80X_PLL_OUTE, 0x00 }, +}; + +struct adav80x { + struct regmap *regmap; + + enum adav80x_clk_src clk_src; + unsigned int sysclk; + enum adav80x_pll_src pll_src; + + unsigned int dai_fmt[2]; + unsigned int rate; + bool deemph; + bool sysclk_pd[3]; +}; + +static const char *adav80x_mux_text[] = { + "ADC", + "Playback", + "Aux Playback", +}; + +static const unsigned int adav80x_mux_values[] = { + 0, 2, 3, +}; + +#define ADAV80X_MUX_ENUM_DECL(name, reg, shift) \ + SOC_VALUE_ENUM_DOUBLE_DECL(name, reg, shift, 7, \ + ARRAY_SIZE(adav80x_mux_text), adav80x_mux_text, \ + adav80x_mux_values) + +static ADAV80X_MUX_ENUM_DECL(adav80x_aux_capture_enum, ADAV80X_DPATH_CTRL1, 0); +static ADAV80X_MUX_ENUM_DECL(adav80x_capture_enum, ADAV80X_DPATH_CTRL1, 3); +static ADAV80X_MUX_ENUM_DECL(adav80x_dac_enum, ADAV80X_DPATH_CTRL2, 3); + +static const struct snd_kcontrol_new adav80x_aux_capture_mux_ctrl = + SOC_DAPM_ENUM("Route", adav80x_aux_capture_enum); +static const struct snd_kcontrol_new adav80x_capture_mux_ctrl = + SOC_DAPM_ENUM("Route", adav80x_capture_enum); +static const struct snd_kcontrol_new adav80x_dac_mux_ctrl = + SOC_DAPM_ENUM("Route", adav80x_dac_enum); + +#define ADAV80X_MUX(name, ctrl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl) + +static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", NULL, ADAV80X_DAC_CTRL1, 7, 1), + SND_SOC_DAPM_ADC("ADC", NULL, ADAV80X_ADC_CTRL1, 5, 1), + + SND_SOC_DAPM_PGA("Right PGA", ADAV80X_ADC_CTRL1, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("Left PGA", ADAV80X_ADC_CTRL1, 1, 1, NULL, 0), + + SND_SOC_DAPM_AIF_OUT("AIFOUT", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("AIFAUXOUT", "Aux Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFAUXIN", "Aux Playback", 0, SND_SOC_NOPM, 0, 0), + + ADAV80X_MUX("Aux Capture Select", &adav80x_aux_capture_mux_ctrl), + ADAV80X_MUX("Capture Select", &adav80x_capture_mux_ctrl), + ADAV80X_MUX("DAC Select", &adav80x_dac_mux_ctrl), + + SND_SOC_DAPM_INPUT("VINR"), + SND_SOC_DAPM_INPUT("VINL"), + SND_SOC_DAPM_OUTPUT("VOUTR"), + SND_SOC_DAPM_OUTPUT("VOUTL"), + + SND_SOC_DAPM_SUPPLY("SYSCLK", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", ADAV80X_PLL_CTRL1, 2, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2", ADAV80X_PLL_CTRL1, 3, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("OSC", ADAV80X_PLL_CTRL1, 1, 1, NULL, 0), +}; + +static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + const char *clk; + + switch (adav80x->clk_src) { + case ADAV80X_CLK_PLL1: + clk = "PLL1"; + break; + case ADAV80X_CLK_PLL2: + clk = "PLL2"; + break; + case ADAV80X_CLK_XTAL: + clk = "OSC"; + break; + default: + return 0; + } + + return strcmp(source->name, clk) == 0; +} + +static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + + return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL; +} + + +static const struct snd_soc_dapm_route adav80x_dapm_routes[] = { + { "DAC Select", "ADC", "ADC" }, + { "DAC Select", "Playback", "AIFIN" }, + { "DAC Select", "Aux Playback", "AIFAUXIN" }, + { "DAC", NULL, "DAC Select" }, + + { "Capture Select", "ADC", "ADC" }, + { "Capture Select", "Playback", "AIFIN" }, + { "Capture Select", "Aux Playback", "AIFAUXIN" }, + { "AIFOUT", NULL, "Capture Select" }, + + { "Aux Capture Select", "ADC", "ADC" }, + { "Aux Capture Select", "Playback", "AIFIN" }, + { "Aux Capture Select", "Aux Playback", "AIFAUXIN" }, + { "AIFAUXOUT", NULL, "Aux Capture Select" }, + + { "VOUTR", NULL, "DAC" }, + { "VOUTL", NULL, "DAC" }, + + { "Left PGA", NULL, "VINL" }, + { "Right PGA", NULL, "VINR" }, + { "ADC", NULL, "Left PGA" }, + { "ADC", NULL, "Right PGA" }, + + { "SYSCLK", NULL, "PLL1", adav80x_dapm_sysclk_check }, + { "SYSCLK", NULL, "PLL2", adav80x_dapm_sysclk_check }, + { "SYSCLK", NULL, "OSC", adav80x_dapm_sysclk_check }, + { "PLL1", NULL, "OSC", adav80x_dapm_pll_check }, + { "PLL2", NULL, "OSC", adav80x_dapm_pll_check }, + + { "ADC", NULL, "SYSCLK" }, + { "DAC", NULL, "SYSCLK" }, + { "AIFOUT", NULL, "SYSCLK" }, + { "AIFAUXOUT", NULL, "SYSCLK" }, + { "AIFIN", NULL, "SYSCLK" }, + { "AIFAUXIN", NULL, "SYSCLK" }, +}; + +static int adav80x_set_deemph(struct snd_soc_component *component) +{ + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (adav80x->deemph) { + switch (adav80x->rate) { + case 32000: + val = ADAV80X_DAC_CTRL2_DEEMPH_32; + break; + case 44100: + val = ADAV80X_DAC_CTRL2_DEEMPH_44; + break; + case 48000: + case 64000: + case 88200: + case 96000: + val = ADAV80X_DAC_CTRL2_DEEMPH_48; + break; + default: + val = ADAV80X_DAC_CTRL2_DEEMPH_NONE; + break; + } + } else { + val = ADAV80X_DAC_CTRL2_DEEMPH_NONE; + } + + return regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL2, + ADAV80X_DAC_CTRL2_DEEMPH_MASK, val); +} + +static int adav80x_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; + + if (deemph > 1) + return -EINVAL; + + adav80x->deemph = deemph; + + return adav80x_set_deemph(component); +} + +static int adav80x_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = adav80x->deemph; + return 0; +}; + +static const DECLARE_TLV_DB_SCALE(adav80x_inpga_tlv, 0, 50, 0); +static const DECLARE_TLV_DB_MINMAX(adav80x_digital_tlv, -9563, 0); + +static const struct snd_kcontrol_new adav80x_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", ADAV80X_DAC_L_VOL, + ADAV80X_DAC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv), + SOC_DOUBLE_R_TLV("Master Capture Volume", ADAV80X_ADC_L_VOL, + ADAV80X_ADC_R_VOL, 0, 0xff, 0, adav80x_digital_tlv), + + SOC_DOUBLE_R_TLV("PGA Capture Volume", ADAV80X_PGA_L_VOL, + ADAV80X_PGA_R_VOL, 0, 0x30, 0, adav80x_inpga_tlv), + + SOC_DOUBLE("Master Playback Switch", ADAV80X_DAC_CTRL1, 0, 1, 1, 0), + SOC_DOUBLE("Master Capture Switch", ADAV80X_ADC_CTRL1, 2, 3, 1, 1), + + SOC_SINGLE("ADC High Pass Filter Switch", ADAV80X_ADC_CTRL1, 6, 1, 0), + + SOC_SINGLE_BOOL_EXT("Playback De-emphasis Switch", 0, + adav80x_get_deemph, adav80x_put_deemph), +}; + +static unsigned int adav80x_port_ctrl_regs[2][2] = { + { ADAV80X_REC_CTRL, ADAV80X_PLAYBACK_CTRL, }, + { ADAV80X_AUX_OUT_CTRL, ADAV80X_AUX_IN_CTRL }, +}; + +static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + unsigned int capture = 0x00; + unsigned int playback = 0x00; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + capture |= ADAV80X_CAPTURE_MODE_MASTER; + playback |= ADAV80X_PLAYBACK_MODE_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + capture |= ADAV80X_CAPTURE_MODE_I2S; + playback |= ADAV80X_PLAYBACK_MODE_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + capture |= ADAV80X_CAPTURE_MODE_LEFT_J; + playback |= ADAV80X_PLAYBACK_MODE_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + capture |= ADAV80X_CAPTURE_MODE_RIGHT_J; + playback |= ADAV80X_PLAYBACK_MODE_RIGHT_J_24; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][0], + ADAV80X_CAPTURE_MODE_MASK | ADAV80X_CAPTURE_MODE_MASTER, + capture); + regmap_write(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][1], + playback); + + adav80x->dai_fmt[dai->id] = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + return 0; +} + +static int adav80x_set_adc_clock(struct snd_soc_component *component, + unsigned int sample_rate) +{ + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (sample_rate <= 48000) + val = ADAV80X_ADC_CTRL1_MODULATOR_128FS; + else + val = ADAV80X_ADC_CTRL1_MODULATOR_64FS; + + regmap_update_bits(adav80x->regmap, ADAV80X_ADC_CTRL1, + ADAV80X_ADC_CTRL1_MODULATOR_MASK, val); + + return 0; +} + +static int adav80x_set_dac_clock(struct snd_soc_component *component, + unsigned int sample_rate) +{ + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (sample_rate <= 48000) + val = ADAV80X_DAC_CTRL2_DIV1 | ADAV80X_DAC_CTRL2_INTERPOL_256FS; + else + val = ADAV80X_DAC_CTRL2_DIV2 | ADAV80X_DAC_CTRL2_INTERPOL_128FS; + + regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL2, + ADAV80X_DAC_CTRL2_DIV_MASK | ADAV80X_DAC_CTRL2_INTERPOL_MASK, + val); + + return 0; +} + +static int adav80x_set_capture_pcm_format(struct snd_soc_component *component, + struct snd_soc_dai *dai, struct snd_pcm_hw_params *params) +{ + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + unsigned int val; + + switch (params_width(params)) { + case 16: + val = ADAV80X_CAPTURE_WORD_LEN16; + break; + case 18: + val = ADAV80X_CAPTRUE_WORD_LEN18; + break; + case 20: + val = ADAV80X_CAPTURE_WORD_LEN20; + break; + case 24: + val = ADAV80X_CAPTURE_WORD_LEN24; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][0], + ADAV80X_CAPTURE_WORD_LEN_MASK, val); + + return 0; +} + +static int adav80x_set_playback_pcm_format(struct snd_soc_component *component, + struct snd_soc_dai *dai, struct snd_pcm_hw_params *params) +{ + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J) + return 0; + + switch (params_width(params)) { + case 16: + val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16; + break; + case 18: + val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18; + break; + case 20: + val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20; + break; + case 24: + val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24; + break; + default: + return -EINVAL; + } + + regmap_update_bits(adav80x->regmap, adav80x_port_ctrl_regs[dai->id][1], + ADAV80X_PLAYBACK_MODE_MASK, val); + + return 0; +} + +static int adav80x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + unsigned int rate = params_rate(params); + + if (rate * 256 != adav80x->sysclk) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + adav80x_set_playback_pcm_format(component, dai, params); + adav80x_set_dac_clock(component, rate); + } else { + adav80x_set_capture_pcm_format(component, dai, params); + adav80x_set_adc_clock(component, rate); + } + adav80x->rate = rate; + adav80x_set_deemph(component); + + return 0; +} + +static int adav80x_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, + unsigned int freq, int dir) +{ + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + if (dir == SND_SOC_CLOCK_IN) { + switch (clk_id) { + case ADAV80X_CLK_XIN: + case ADAV80X_CLK_XTAL: + case ADAV80X_CLK_MCLKI: + case ADAV80X_CLK_PLL1: + case ADAV80X_CLK_PLL2: + break; + default: + return -EINVAL; + } + + adav80x->sysclk = freq; + + if (adav80x->clk_src != clk_id) { + unsigned int iclk_ctrl1, iclk_ctrl2; + + adav80x->clk_src = clk_id; + if (clk_id == ADAV80X_CLK_XTAL) + clk_id = ADAV80X_CLK_XIN; + + iclk_ctrl1 = ADAV80X_ICLK_CTRL1_DAC_SRC(clk_id) | + ADAV80X_ICLK_CTRL1_ADC_SRC(clk_id) | + ADAV80X_ICLK_CTRL1_ICLK2_SRC(clk_id); + iclk_ctrl2 = ADAV80X_ICLK_CTRL2_ICLK1_SRC(clk_id); + + regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL1, + iclk_ctrl1); + regmap_write(adav80x->regmap, ADAV80X_ICLK_CTRL2, + iclk_ctrl2); + + snd_soc_dapm_sync(dapm); + } + } else { + unsigned int mask; + + switch (clk_id) { + case ADAV80X_CLK_SYSCLK1: + case ADAV80X_CLK_SYSCLK2: + case ADAV80X_CLK_SYSCLK3: + break; + default: + return -EINVAL; + } + + clk_id -= ADAV80X_CLK_SYSCLK1; + mask = ADAV80X_PLL_OUTE_SYSCLKPD(clk_id); + + if (freq == 0) { + regmap_update_bits(adav80x->regmap, ADAV80X_PLL_OUTE, + mask, mask); + adav80x->sysclk_pd[clk_id] = true; + } else { + regmap_update_bits(adav80x->regmap, ADAV80X_PLL_OUTE, + mask, 0); + adav80x->sysclk_pd[clk_id] = false; + } + + snd_soc_dapm_mutex_lock(dapm); + + if (adav80x->sysclk_pd[0]) + snd_soc_dapm_disable_pin_unlocked(dapm, "PLL1"); + else + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL1"); + + if (adav80x->sysclk_pd[1] || adav80x->sysclk_pd[2]) + snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2"); + else + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2"); + + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); + } + + return 0; +} + +static int adav80x_set_pll(struct snd_soc_component *component, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + unsigned int pll_ctrl1 = 0; + unsigned int pll_ctrl2 = 0; + unsigned int pll_src; + + switch (source) { + case ADAV80X_PLL_SRC_XTAL: + case ADAV80X_PLL_SRC_XIN: + case ADAV80X_PLL_SRC_MCLKI: + break; + default: + return -EINVAL; + } + + if (!freq_out) + return 0; + + switch (freq_in) { + case 27000000: + break; + case 54000000: + if (source == ADAV80X_PLL_SRC_XIN) { + pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV; + break; + } + fallthrough; + default: + return -EINVAL; + } + + if (freq_out > 12288000) { + pll_ctrl2 |= ADAV80X_PLL_CTRL2_DOUB(pll_id); + freq_out /= 2; + } + + /* freq_out = sample_rate * 256 */ + switch (freq_out) { + case 8192000: + pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_32(pll_id); + break; + case 11289600: + pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_44(pll_id); + break; + case 12288000: + pll_ctrl2 |= ADAV80X_PLL_CTRL2_FS_48(pll_id); + break; + default: + return -EINVAL; + } + + regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CTRL1, + ADAV80X_PLL_CTRL1_PLLDIV, pll_ctrl1); + regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CTRL2, + ADAV80X_PLL_CTRL2_PLL_MASK(pll_id), pll_ctrl2); + + if (source != adav80x->pll_src) { + if (source == ADAV80X_PLL_SRC_MCLKI) + pll_src = ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll_id); + else + pll_src = ADAV80X_PLL_CLK_SRC_PLL_XIN(pll_id); + + regmap_update_bits(adav80x->regmap, ADAV80X_PLL_CLK_SRC, + ADAV80X_PLL_CLK_SRC_PLL_MASK(pll_id), pll_src); + + adav80x->pll_src = source; + + snd_soc_dapm_sync(dapm); + } + + return 0; +} + +static int adav80x_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + unsigned int mask = ADAV80X_DAC_CTRL1_PD; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL1, mask, + 0x00); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(adav80x->regmap, ADAV80X_DAC_CTRL1, mask, + mask); + break; + } + + return 0; +} + +/* Enforce the same sample rate on all audio interfaces */ +static int adav80x_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + + if (!snd_soc_component_active(component) || !adav80x->rate) + return 0; + + return snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, adav80x->rate); +} + +static void adav80x_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + + if (!snd_soc_component_active(component)) + adav80x->rate = 0; +} + +static const struct snd_soc_dai_ops adav80x_dai_ops = { + .set_fmt = adav80x_set_dai_fmt, + .hw_params = adav80x_hw_params, + .startup = adav80x_dai_startup, + .shutdown = adav80x_dai_shutdown, +}; + +#define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000) + +#define ADAV80X_CAPTURE_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) + +#define ADAV80X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver adav80x_dais[] = { + { + .name = "adav80x-hifi", + .id = 0, + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ADAV80X_PLAYBACK_RATES, + .formats = ADAV80X_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 2, + .channels_max = 2, + .rates = ADAV80X_CAPTURE_RATES, + .formats = ADAV80X_FORMATS, + }, + .ops = &adav80x_dai_ops, + }, + { + .name = "adav80x-aux", + .id = 1, + .playback = { + .stream_name = "Aux Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ADAV80X_PLAYBACK_RATES, + .formats = ADAV80X_FORMATS, + }, + .capture = { + .stream_name = "Aux Capture", + .channels_min = 2, + .channels_max = 2, + .rates = ADAV80X_CAPTURE_RATES, + .formats = ADAV80X_FORMATS, + }, + .ops = &adav80x_dai_ops, + }, +}; + +static int adav80x_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + + /* Force PLLs on for SYSCLK output */ + snd_soc_dapm_force_enable_pin(dapm, "PLL1"); + snd_soc_dapm_force_enable_pin(dapm, "PLL2"); + + /* Power down S/PDIF receiver, since it is currently not supported */ + regmap_write(adav80x->regmap, ADAV80X_PLL_OUTE, 0x20); + /* Disable DAC zero flag */ + regmap_write(adav80x->regmap, ADAV80X_DAC_CTRL3, 0x6); + + return 0; +} + +static int adav80x_resume(struct snd_soc_component *component) +{ + struct adav80x *adav80x = snd_soc_component_get_drvdata(component); + + regcache_sync(adav80x->regmap); + + return 0; +} + +static const struct snd_soc_component_driver adav80x_component_driver = { + .probe = adav80x_probe, + .resume = adav80x_resume, + .set_bias_level = adav80x_set_bias_level, + .set_pll = adav80x_set_pll, + .set_sysclk = adav80x_set_sysclk, + .controls = adav80x_controls, + .num_controls = ARRAY_SIZE(adav80x_controls), + .dapm_widgets = adav80x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets), + .dapm_routes = adav80x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +int adav80x_bus_probe(struct device *dev, struct regmap *regmap) +{ + struct adav80x *adav80x; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + adav80x = devm_kzalloc(dev, sizeof(*adav80x), GFP_KERNEL); + if (!adav80x) + return -ENOMEM; + + dev_set_drvdata(dev, adav80x); + adav80x->regmap = regmap; + + return devm_snd_soc_register_component(dev, &adav80x_component_driver, + adav80x_dais, ARRAY_SIZE(adav80x_dais)); +} +EXPORT_SYMBOL_GPL(adav80x_bus_probe); + +const struct regmap_config adav80x_regmap_config = { + .val_bits = 8, + .pad_bits = 1, + .reg_bits = 7, + + .max_register = ADAV80X_PLL_OUTE, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = adav80x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults), +}; +EXPORT_SYMBOL_GPL(adav80x_regmap_config); + +MODULE_DESCRIPTION("ASoC ADAV80x driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_AUTHOR("Yi Li >"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adav80x.h b/sound/soc/codecs/adav80x.h new file mode 100644 index 0000000000..fb50ff57e3 --- /dev/null +++ b/sound/soc/codecs/adav80x.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * header file for ADAV80X parts + * + * Copyright 2011 Analog Devices Inc. + */ + +#ifndef _ADAV80X_H +#define _ADAV80X_H + +#include + +struct device; + +extern const struct regmap_config adav80x_regmap_config; +int adav80x_bus_probe(struct device *dev, struct regmap *regmap); + +enum adav80x_pll_src { + ADAV80X_PLL_SRC_XIN, + ADAV80X_PLL_SRC_XTAL, + ADAV80X_PLL_SRC_MCLKI, +}; + +enum adav80x_pll { + ADAV80X_PLL1 = 0, + ADAV80X_PLL2 = 1, +}; + +enum adav80x_clk_src { + ADAV80X_CLK_XIN = 0, + ADAV80X_CLK_MCLKI = 1, + ADAV80X_CLK_PLL1 = 2, + ADAV80X_CLK_PLL2 = 3, + ADAV80X_CLK_XTAL = 6, + + ADAV80X_CLK_SYSCLK1 = 6, + ADAV80X_CLK_SYSCLK2 = 7, + ADAV80X_CLK_SYSCLK3 = 8, +}; + +#endif diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c new file mode 100644 index 0000000000..44aa06e034 --- /dev/null +++ b/sound/soc/codecs/ads117x.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ads117x.c -- Driver for ads1174/8 ADC chips + * + * Copyright 2009 ShotSpotter Inc. + * Author: Graeme Gregory + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000) +#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +static const struct snd_soc_dapm_widget ads117x_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("Input1"), +SND_SOC_DAPM_INPUT("Input2"), +SND_SOC_DAPM_INPUT("Input3"), +SND_SOC_DAPM_INPUT("Input4"), +SND_SOC_DAPM_INPUT("Input5"), +SND_SOC_DAPM_INPUT("Input6"), +SND_SOC_DAPM_INPUT("Input7"), +SND_SOC_DAPM_INPUT("Input8"), +}; + +static const struct snd_soc_dapm_route ads117x_dapm_routes[] = { + { "Capture", NULL, "Input1" }, + { "Capture", NULL, "Input2" }, + { "Capture", NULL, "Input3" }, + { "Capture", NULL, "Input4" }, + { "Capture", NULL, "Input5" }, + { "Capture", NULL, "Input6" }, + { "Capture", NULL, "Input7" }, + { "Capture", NULL, "Input8" }, +}; + +static struct snd_soc_dai_driver ads117x_dai = { +/* ADC */ + .name = "ads117x-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 32, + .rates = ADS117X_RATES, + .formats = ADS117X_FORMATS,}, +}; + +static const struct snd_soc_component_driver soc_component_dev_ads117x = { + .dapm_widgets = ads117x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets), + .dapm_routes = ads117x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int ads117x_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_ads117x, &ads117x_dai, 1); +} + +#if defined(CONFIG_OF) +static const struct of_device_id ads117x_dt_ids[] = { + { .compatible = "ti,ads1174" }, + { .compatible = "ti,ads1178" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ads117x_dt_ids); +#endif + +static struct platform_driver ads117x_codec_driver = { + .driver = { + .name = "ads117x-codec", + .of_match_table = of_match_ptr(ads117x_dt_ids), + }, + + .probe = ads117x_probe, +}; + +module_platform_driver(ads117x_codec_driver); + +MODULE_DESCRIPTION("ASoC ads117x driver"); +MODULE_AUTHOR("Graeme Gregory"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c new file mode 100644 index 0000000000..ce99f30b46 --- /dev/null +++ b/sound/soc/codecs/ak4104.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AK4104 ALSA SoC (ASoC) driver + * + * Copyright (c) 2009 Daniel Mack + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AK4104 registers addresses */ +#define AK4104_REG_CONTROL1 0x00 +#define AK4104_REG_RESERVED 0x01 +#define AK4104_REG_CONTROL2 0x02 +#define AK4104_REG_TX 0x03 +#define AK4104_REG_CHN_STATUS(x) ((x) + 0x04) +#define AK4104_NUM_REGS 10 + +#define AK4104_REG_MASK 0x1f +#define AK4104_READ 0xc0 +#define AK4104_WRITE 0xe0 +#define AK4104_RESERVED_VAL 0x5b + +/* Bit masks for AK4104 registers */ +#define AK4104_CONTROL1_RSTN (1 << 0) +#define AK4104_CONTROL1_PW (1 << 1) +#define AK4104_CONTROL1_DIF0 (1 << 2) +#define AK4104_CONTROL1_DIF1 (1 << 3) + +#define AK4104_CONTROL2_SEL0 (1 << 0) +#define AK4104_CONTROL2_SEL1 (1 << 1) +#define AK4104_CONTROL2_MODE (1 << 2) + +#define AK4104_TX_TXE (1 << 0) +#define AK4104_TX_V (1 << 1) + +struct ak4104_private { + struct regmap *regmap; + struct regulator *regulator; +}; + +static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = { +SND_SOC_DAPM_PGA("TXE", AK4104_REG_TX, AK4104_TX_TXE, 0, NULL, 0), + +SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route ak4104_dapm_routes[] = { + { "TXE", NULL, "Playback" }, + { "TX", NULL, "TXE" }, +}; + +static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component); + int val = 0; + int ret; + + /* set DAI format */ + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + val |= AK4104_CONTROL1_DIF0; + break; + case SND_SOC_DAIFMT_I2S: + val |= AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1; + break; + default: + dev_err(component->dev, "invalid dai format\n"); + return -EINVAL; + } + + /* This device can only be consumer */ + if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) + return -EINVAL; + + ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1, + AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1, + val); + if (ret < 0) + return ret; + + return 0; +} + +static int ak4104_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component); + int ret, val = 0; + + /* set the IEC958 bits: consumer mode, no copyright bit */ + val |= IEC958_AES0_CON_NOT_COPYRIGHT; + regmap_write(ak4104->regmap, AK4104_REG_CHN_STATUS(0), val); + + val = 0; + + switch (params_rate(params)) { + case 22050: + val |= IEC958_AES3_CON_FS_22050; + break; + case 24000: + val |= IEC958_AES3_CON_FS_24000; + break; + case 32000: + val |= IEC958_AES3_CON_FS_32000; + break; + case 44100: + val |= IEC958_AES3_CON_FS_44100; + break; + case 48000: + val |= IEC958_AES3_CON_FS_48000; + break; + case 88200: + val |= IEC958_AES3_CON_FS_88200; + break; + case 96000: + val |= IEC958_AES3_CON_FS_96000; + break; + case 176400: + val |= IEC958_AES3_CON_FS_176400; + break; + case 192000: + val |= IEC958_AES3_CON_FS_192000; + break; + default: + dev_err(component->dev, "unsupported sampling rate\n"); + return -EINVAL; + } + + ret = regmap_write(ak4104->regmap, AK4104_REG_CHN_STATUS(3), val); + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_soc_dai_ops ak4101_dai_ops = { + .hw_params = ak4104_hw_params, + .set_fmt = ak4104_set_dai_fmt, +}; + +static struct snd_soc_dai_driver ak4104_dai = { + .name = "ak4104-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE + }, + .ops = &ak4101_dai_ops, +}; + +static int ak4104_probe(struct snd_soc_component *component) +{ + struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component); + int ret; + + ret = regulator_enable(ak4104->regulator); + if (ret < 0) { + dev_err(component->dev, "Unable to enable regulator: %d\n", ret); + return ret; + } + + /* set power-up and non-reset bits */ + ret = regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1, + AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN, + AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN); + if (ret < 0) + goto exit_disable_regulator; + + /* enable transmitter */ + ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX, + AK4104_TX_TXE, AK4104_TX_TXE); + if (ret < 0) + goto exit_disable_regulator; + + return 0; + +exit_disable_regulator: + regulator_disable(ak4104->regulator); + return ret; +} + +static void ak4104_remove(struct snd_soc_component *component) +{ + struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component); + + regmap_update_bits(ak4104->regmap, AK4104_REG_CONTROL1, + AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN, 0); + regulator_disable(ak4104->regulator); +} + +#ifdef CONFIG_PM +static int ak4104_soc_suspend(struct snd_soc_component *component) +{ + struct ak4104_private *priv = snd_soc_component_get_drvdata(component); + + regulator_disable(priv->regulator); + + return 0; +} + +static int ak4104_soc_resume(struct snd_soc_component *component) +{ + struct ak4104_private *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = regulator_enable(priv->regulator); + if (ret < 0) + return ret; + + return 0; +} +#else +#define ak4104_soc_suspend NULL +#define ak4104_soc_resume NULL +#endif /* CONFIG_PM */ + +static const struct snd_soc_component_driver soc_component_device_ak4104 = { + .probe = ak4104_probe, + .remove = ak4104_remove, + .suspend = ak4104_soc_suspend, + .resume = ak4104_soc_resume, + .dapm_widgets = ak4104_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets), + .dapm_routes = ak4104_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config ak4104_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AK4104_NUM_REGS - 1, + .read_flag_mask = AK4104_READ, + .write_flag_mask = AK4104_WRITE, + + .cache_type = REGCACHE_RBTREE, +}; + +static int ak4104_spi_probe(struct spi_device *spi) +{ + struct ak4104_private *ak4104; + struct gpio_desc *reset_gpiod; + unsigned int val; + int ret; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + ak4104 = devm_kzalloc(&spi->dev, sizeof(struct ak4104_private), + GFP_KERNEL); + if (ak4104 == NULL) + return -ENOMEM; + + ak4104->regulator = devm_regulator_get(&spi->dev, "vdd"); + if (IS_ERR(ak4104->regulator)) { + ret = PTR_ERR(ak4104->regulator); + dev_err(&spi->dev, "Unable to get Vdd regulator: %d\n", ret); + return ret; + } + + ak4104->regmap = devm_regmap_init_spi(spi, &ak4104_regmap); + if (IS_ERR(ak4104->regmap)) { + ret = PTR_ERR(ak4104->regmap); + return ret; + } + + reset_gpiod = devm_gpiod_get_optional(&spi->dev, "reset", + GPIOD_OUT_HIGH); + if (PTR_ERR(reset_gpiod) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + /* read the 'reserved' register - according to the datasheet, it + * should contain 0x5b. Not a good way to verify the presence of + * the device, but there is no hardware ID register. */ + ret = regmap_read(ak4104->regmap, AK4104_REG_RESERVED, &val); + if (ret != 0) + return ret; + if (val != AK4104_RESERVED_VAL) + return -ENODEV; + + spi_set_drvdata(spi, ak4104); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_device_ak4104, &ak4104_dai, 1); + return ret; +} + +static const struct of_device_id ak4104_of_match[] = { + { .compatible = "asahi-kasei,ak4104", }, + { } +}; +MODULE_DEVICE_TABLE(of, ak4104_of_match); + +static const struct spi_device_id ak4104_id_table[] = { + { "ak4104", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ak4104_id_table); + +static struct spi_driver ak4104_spi_driver = { + .driver = { + .name = "ak4104", + .of_match_table = ak4104_of_match, + }, + .id_table = ak4104_id_table, + .probe = ak4104_spi_probe, +}; + +module_spi_driver(ak4104_spi_driver); + +MODULE_AUTHOR("Daniel Mack "); +MODULE_DESCRIPTION("Asahi Kasei AK4104 ALSA SoC driver"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c new file mode 100644 index 0000000000..e34e553376 --- /dev/null +++ b/sound/soc/codecs/ak4118.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ak4118.c -- Asahi Kasei ALSA Soc Audio driver + * + * Copyright 2018 DEVIALET + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AK4118_REG_CLK_PWR_CTL 0x00 +#define AK4118_REG_FORMAT_CTL 0x01 +#define AK4118_REG_IO_CTL0 0x02 +#define AK4118_REG_IO_CTL1 0x03 +#define AK4118_REG_INT0_MASK 0x04 +#define AK4118_REG_INT1_MASK 0x05 +#define AK4118_REG_RCV_STATUS0 0x06 +#define AK4118_REG_RCV_STATUS1 0x07 +#define AK4118_REG_RXCHAN_STATUS0 0x08 +#define AK4118_REG_RXCHAN_STATUS1 0x09 +#define AK4118_REG_RXCHAN_STATUS2 0x0a +#define AK4118_REG_RXCHAN_STATUS3 0x0b +#define AK4118_REG_RXCHAN_STATUS4 0x0c +#define AK4118_REG_TXCHAN_STATUS0 0x0d +#define AK4118_REG_TXCHAN_STATUS1 0x0e +#define AK4118_REG_TXCHAN_STATUS2 0x0f +#define AK4118_REG_TXCHAN_STATUS3 0x10 +#define AK4118_REG_TXCHAN_STATUS4 0x11 +#define AK4118_REG_BURST_PREAMB_PC0 0x12 +#define AK4118_REG_BURST_PREAMB_PC1 0x13 +#define AK4118_REG_BURST_PREAMB_PD0 0x14 +#define AK4118_REG_BURST_PREAMB_PD1 0x15 +#define AK4118_REG_QSUB_CTL 0x16 +#define AK4118_REG_QSUB_TRACK 0x17 +#define AK4118_REG_QSUB_INDEX 0x18 +#define AK4118_REG_QSUB_MIN 0x19 +#define AK4118_REG_QSUB_SEC 0x1a +#define AK4118_REG_QSUB_FRAME 0x1b +#define AK4118_REG_QSUB_ZERO 0x1c +#define AK4118_REG_QSUB_ABS_MIN 0x1d +#define AK4118_REG_QSUB_ABS_SEC 0x1e +#define AK4118_REG_QSUB_ABS_FRAME 0x1f +#define AK4118_REG_GPE 0x20 +#define AK4118_REG_GPDR 0x21 +#define AK4118_REG_GPSCR 0x22 +#define AK4118_REG_GPLR 0x23 +#define AK4118_REG_DAT_MASK_DTS 0x24 +#define AK4118_REG_RX_DETECT 0x25 +#define AK4118_REG_STC_DAT_DETECT 0x26 +#define AK4118_REG_RXCHAN_STATUS5 0x27 +#define AK4118_REG_TXCHAN_STATUS5 0x28 +#define AK4118_REG_MAX 0x29 + +#define AK4118_REG_FORMAT_CTL_DIF0 (1 << 4) +#define AK4118_REG_FORMAT_CTL_DIF1 (1 << 5) +#define AK4118_REG_FORMAT_CTL_DIF2 (1 << 6) + +struct ak4118_priv { + struct regmap *regmap; + struct gpio_desc *reset; + struct gpio_desc *irq; + struct snd_soc_component *component; +}; + +static const struct reg_default ak4118_reg_defaults[] = { + {AK4118_REG_CLK_PWR_CTL, 0x43}, + {AK4118_REG_FORMAT_CTL, 0x6a}, + {AK4118_REG_IO_CTL0, 0x88}, + {AK4118_REG_IO_CTL1, 0x48}, + {AK4118_REG_INT0_MASK, 0xee}, + {AK4118_REG_INT1_MASK, 0xb5}, + {AK4118_REG_RCV_STATUS0, 0x00}, + {AK4118_REG_RCV_STATUS1, 0x10}, + {AK4118_REG_TXCHAN_STATUS0, 0x00}, + {AK4118_REG_TXCHAN_STATUS1, 0x00}, + {AK4118_REG_TXCHAN_STATUS2, 0x00}, + {AK4118_REG_TXCHAN_STATUS3, 0x00}, + {AK4118_REG_TXCHAN_STATUS4, 0x00}, + {AK4118_REG_GPE, 0x77}, + {AK4118_REG_GPDR, 0x00}, + {AK4118_REG_GPSCR, 0x00}, + {AK4118_REG_GPLR, 0x00}, + {AK4118_REG_DAT_MASK_DTS, 0x3f}, + {AK4118_REG_RX_DETECT, 0x00}, + {AK4118_REG_STC_DAT_DETECT, 0x00}, + {AK4118_REG_TXCHAN_STATUS5, 0x00}, +}; + +static const char * const ak4118_input_select_txt[] = { + "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7", +}; +static SOC_ENUM_SINGLE_DECL(ak4118_insel_enum, AK4118_REG_IO_CTL1, 0x0, + ak4118_input_select_txt); + +static const struct snd_kcontrol_new ak4118_input_mux_controls = + SOC_DAPM_ENUM("Input Select", ak4118_insel_enum); + +static const char * const ak4118_iec958_fs_txt[] = { + "44100", "48000", "32000", "22050", "11025", "24000", "16000", "88200", + "8000", "96000", "64000", "176400", "192000", +}; + +static const int ak4118_iec958_fs_val[] = { + 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xE, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(ak4118_iec958_fs_enum, AK4118_REG_RCV_STATUS1, + 0x4, 0x4, ak4118_iec958_fs_txt, + ak4118_iec958_fs_val); + +static struct snd_kcontrol_new ak4118_iec958_controls[] = { + SOC_SINGLE("IEC958 Parity Errors", AK4118_REG_RCV_STATUS0, 0, 1, 0), + SOC_SINGLE("IEC958 No Audio", AK4118_REG_RCV_STATUS0, 1, 1, 0), + SOC_SINGLE("IEC958 PLL Lock", AK4118_REG_RCV_STATUS0, 4, 1, 1), + SOC_SINGLE("IEC958 Non PCM", AK4118_REG_RCV_STATUS0, 6, 1, 0), + SOC_ENUM("IEC958 Sampling Freq", ak4118_iec958_fs_enum), +}; + +static const struct snd_soc_dapm_widget ak4118_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("INRX0"), + SND_SOC_DAPM_INPUT("INRX1"), + SND_SOC_DAPM_INPUT("INRX2"), + SND_SOC_DAPM_INPUT("INRX3"), + SND_SOC_DAPM_INPUT("INRX4"), + SND_SOC_DAPM_INPUT("INRX5"), + SND_SOC_DAPM_INPUT("INRX6"), + SND_SOC_DAPM_INPUT("INRX7"), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, + &ak4118_input_mux_controls), +}; + +static const struct snd_soc_dapm_route ak4118_dapm_routes[] = { + {"Input Mux", "RX0", "INRX0"}, + {"Input Mux", "RX1", "INRX1"}, + {"Input Mux", "RX2", "INRX2"}, + {"Input Mux", "RX3", "INRX3"}, + {"Input Mux", "RX4", "INRX4"}, + {"Input Mux", "RX5", "INRX5"}, + {"Input Mux", "RX6", "INRX6"}, + {"Input Mux", "RX7", "INRX7"}, +}; + + +static int ak4118_set_dai_fmt_provider(struct ak4118_priv *ak4118, + unsigned int format) +{ + int dif; + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1; + break; + case SND_SOC_DAIFMT_LEFT_J: + dif = AK4118_REG_FORMAT_CTL_DIF2; + break; + default: + return -ENOTSUPP; + } + + return dif; +} + +static int ak4118_set_dai_fmt_consumer(struct ak4118_priv *ak4118, + unsigned int format) +{ + int dif; + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1 | + AK4118_REG_FORMAT_CTL_DIF2; + break; + case SND_SOC_DAIFMT_LEFT_J: + dif = AK4118_REG_FORMAT_CTL_DIF1 | AK4118_REG_FORMAT_CTL_DIF2; + break; + default: + return -ENOTSUPP; + } + + return dif; +} + +static int ak4118_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int format) +{ + struct snd_soc_component *component = dai->component; + struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component); + int dif; + int ret = 0; + + switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + dif = ak4118_set_dai_fmt_provider(ak4118, format); + break; + case SND_SOC_DAIFMT_CBC_CFC: + dif = ak4118_set_dai_fmt_consumer(ak4118, format); + break; + default: + ret = -ENOTSUPP; + goto exit; + } + + /* format not supported */ + if (dif < 0) { + ret = dif; + goto exit; + } + + ret = regmap_update_bits(ak4118->regmap, AK4118_REG_FORMAT_CTL, + AK4118_REG_FORMAT_CTL_DIF0 | + AK4118_REG_FORMAT_CTL_DIF1 | + AK4118_REG_FORMAT_CTL_DIF2, dif); + if (ret < 0) + goto exit; + +exit: + return ret; +} + +static int ak4118_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return 0; +} + +static const struct snd_soc_dai_ops ak4118_dai_ops = { + .hw_params = ak4118_hw_params, + .set_fmt = ak4118_set_dai_fmt, +}; + +static struct snd_soc_dai_driver ak4118_dai = { + .name = "ak4118-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE + }, + .ops = &ak4118_dai_ops, +}; + +static irqreturn_t ak4118_irq_handler(int irq, void *data) +{ + struct ak4118_priv *ak4118 = data; + struct snd_soc_component *component = ak4118->component; + struct snd_kcontrol_new *kctl_new; + unsigned int i; + + if (!component) + return IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) { + kctl_new = &ak4118_iec958_controls[i]; + + snd_soc_component_notify_control(component, kctl_new->name); + } + + return IRQ_HANDLED; +} + +static int ak4118_probe(struct snd_soc_component *component) +{ + struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component); + int ret = 0; + + ak4118->component = component; + + /* release reset */ + gpiod_set_value(ak4118->reset, 0); + + /* unmask all int1 sources */ + ret = regmap_write(ak4118->regmap, AK4118_REG_INT1_MASK, 0x00); + if (ret < 0) { + dev_err(component->dev, + "failed to write regmap 0x%x 0x%x: %d\n", + AK4118_REG_INT1_MASK, 0x00, ret); + return ret; + } + + /* rx detect enable on all channels */ + ret = regmap_write(ak4118->regmap, AK4118_REG_RX_DETECT, 0xff); + if (ret < 0) { + dev_err(component->dev, + "failed to write regmap 0x%x 0x%x: %d\n", + AK4118_REG_RX_DETECT, 0xff, ret); + return ret; + } + + ret = snd_soc_add_component_controls(component, ak4118_iec958_controls, + ARRAY_SIZE(ak4118_iec958_controls)); + if (ret) { + dev_err(component->dev, + "failed to add component kcontrols: %d\n", ret); + return ret; + } + + return 0; +} + +static void ak4118_remove(struct snd_soc_component *component) +{ + struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component); + + /* hold reset */ + gpiod_set_value(ak4118->reset, 1); +} + +static const struct snd_soc_component_driver soc_component_drv_ak4118 = { + .probe = ak4118_probe, + .remove = ak4118_remove, + .dapm_widgets = ak4118_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4118_dapm_widgets), + .dapm_routes = ak4118_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4118_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config ak4118_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = ak4118_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4118_reg_defaults), + + .cache_type = REGCACHE_NONE, + .max_register = AK4118_REG_MAX - 1, +}; + +static int ak4118_i2c_probe(struct i2c_client *i2c) +{ + struct ak4118_priv *ak4118; + int ret; + + ak4118 = devm_kzalloc(&i2c->dev, sizeof(struct ak4118_priv), + GFP_KERNEL); + if (ak4118 == NULL) + return -ENOMEM; + + ak4118->regmap = devm_regmap_init_i2c(i2c, &ak4118_regmap); + if (IS_ERR(ak4118->regmap)) + return PTR_ERR(ak4118->regmap); + + i2c_set_clientdata(i2c, ak4118); + + ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ak4118->reset)) + return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->reset), + "Failed to get reset\n"); + + ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN); + if (IS_ERR(ak4118->irq)) + return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->irq), + "Failed to get IRQ\n"); + + ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq), + NULL, ak4118_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "ak4118-irq", ak4118); + if (ret < 0) { + dev_err(&i2c->dev, "Fail to request_irq: %d\n", ret); + return ret; + } + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_drv_ak4118, &ak4118_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id ak4118_of_match[] = { + { .compatible = "asahi-kasei,ak4118", }, + {} +}; +MODULE_DEVICE_TABLE(of, ak4118_of_match); +#endif + +static const struct i2c_device_id ak4118_id_table[] = { + { "ak4118", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ak4118_id_table); + +static struct i2c_driver ak4118_i2c_driver = { + .driver = { + .name = "ak4118", + .of_match_table = of_match_ptr(ak4118_of_match), + }, + .id_table = ak4118_id_table, + .probe = ak4118_i2c_probe, +}; + +module_i2c_driver(ak4118_i2c_driver); + +MODULE_DESCRIPTION("Asahi Kasei AK4118 ALSA SoC driver"); +MODULE_AUTHOR("Adrien Charruel "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c new file mode 100644 index 0000000000..f287acb986 --- /dev/null +++ b/sound/soc/codecs/ak4375.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Based on code by Hu Jin + * Copyright (C) 2014 Asahi Kasei Microdevices Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers and fields */ +#define AK4375_00_POWER_MANAGEMENT1 0x00 +#define PMPLL BIT(0) /* 0: PLL off, 1: PLL on */ +#define AK4375_01_POWER_MANAGEMENT2 0x01 +#define PMCP1 BIT(0) /* Charge Pump 1: LDO1 and DAC */ +#define PMCP2 BIT(1) /* Charge Pump 2: Class-G HP Amp */ +#define PMLDO1P BIT(4) +#define PMLDO1N BIT(5) +#define PMLDO (PMLDO1P | PMLDO1N) +#define AK4375_02_POWER_MANAGEMENT3 0x02 +#define AK4375_03_POWER_MANAGEMENT4 0x03 +#define AK4375_04_OUTPUT_MODE_SETTING 0x04 +#define AK4375_05_CLOCK_MODE_SELECT 0x05 +#define FS_MASK GENMASK(4, 0) +#define FS_8KHZ 0x00 +#define FS_11_025KHZ 0x01 +#define FS_16KHZ 0x04 +#define FS_22_05KHZ 0x05 +#define FS_32KHZ 0x08 +#define FS_44_1KHZ 0x09 +#define FS_48KHZ 0x0a +#define FS_88_2KHZ 0x0d +#define FS_96KHZ 0x0e +#define FS_176_4KHZ 0x11 +#define FS_192KHZ 0x12 +#define CM_MASK GENMASK(6, 5) /* For SRC Bypass mode */ +#define CM_0 (0x0 << 5) +#define CM_1 (0x1 << 5) +#define CM_2 (0x2 << 5) +#define CM_3 (0x3 << 5) +#define AK4375_06_DIGITAL_FILTER_SELECT 0x06 +#define DADFSEL BIT(5) /* 0: in SRC Bypass mode, 1: in SRC mode */ +#define DASL BIT(6) +#define DASD BIT(7) +#define AK4375_07_DAC_MONO_MIXING 0x07 +#define DACMUTE_MASK (GENMASK(5, 4) | GENMASK(1, 0)) /* Clear to mute */ +#define AK4375_08_JITTER_CLEANER_SETTING1 0x08 +#define AK4375_09_JITTER_CLEANER_SETTING2 0x09 +#define AK4375_0A_JITTER_CLEANER_SETTING3 0x0a +#define SELDAIN BIT(1) /* 0: SRC Bypass mode, 1: SRC mode */ +#define XCKSEL BIT(6) /* 0: PLL0, 1: MCKI */ +#define XCKCPSEL BIT(7) /* Should be equal to SELDAIN and XCKSEL */ +#define AK4375_0B_LCH_OUTPUT_VOLUME 0x0b +#define AK4375_0C_RCH_OUTPUT_VOLUME 0x0c +#define AK4375_0D_HP_VOLUME_CONTROL 0x0d +#define AK4375_0E_PLL_CLK_SOURCE_SELECT 0x0e +#define PLS BIT(0) /* 0: MCKI, 1: BCLK */ +#define AK4375_0F_PLL_REF_CLK_DIVIDER1 0x0f /* Reference clock divider [15:8] bits */ +#define AK4375_10_PLL_REF_CLK_DIVIDER2 0x10 /* Reference clock divider [7:0] bis */ +#define AK4375_11_PLL_FB_CLK_DIVIDER1 0x11 /* Feedback clock divider [15:8] bits */ +#define AK4375_12_PLL_FB_CLK_DIVIDER2 0x12 /* Feedback clock divider [7:0] bits */ +#define AK4375_13_SRC_CLK_SOURCE 0x13 /* SRC Bypass: SRCCKS=XCKSEL=SELDAIN=0 */ +#define SRCCKS BIT(0) /* SRC Clock source 0: MCKI, 1: PLL0 */ +#define DIV BIT(4) +#define AK4375_14_DAC_CLK_DIVIDER 0x14 +#define AK4375_15_AUDIO_IF_FORMAT 0x15 +#define DEVICEID_MASK GENMASK(7, 5) +#define AK4375_24_MODE_CONTROL 0x24 + +#define AK4375_PLL_FREQ_OUT_112896000 112896000 /* 44.1 kHz base rate */ +#define AK4375_PLL_FREQ_OUT_122880000 122880000 /* 32 and 48 kHz base rates */ + +#define DEVICEID_AK4375 0x00 +#define DEVICEID_AK4375A 0x01 +#define DEVICEID_AK4376A 0x02 +#define DEVICEID_AK4377 0x03 +#define DEVICEID_AK4331 0x07 + +static const char * const supply_names[] = { + "avdd", "tvdd" +}; + +struct ak4375_drvdata { + struct snd_soc_dai_driver *dai_drv; + const struct snd_soc_component_driver *comp_drv; +}; + +struct ak4375_priv { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *pdn_gpiod; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; + unsigned int rate; + unsigned int pld; + u8 mute_save; +}; + +static const struct reg_default ak4375_reg_defaults[] = { + { 0x00, 0x00 }, { 0x01, 0x00 }, { 0x02, 0x00 }, + { 0x03, 0x00 }, { 0x04, 0x00 }, { 0x05, 0x00 }, + { 0x06, 0x00 }, { 0x07, 0x00 }, { 0x08, 0x00 }, + { 0x09, 0x00 }, { 0x0a, 0x00 }, { 0x0b, 0x19 }, + { 0x0c, 0x19 }, { 0x0d, 0x75 }, { 0x0e, 0x01 }, + { 0x0f, 0x00 }, { 0x10, 0x00 }, { 0x11, 0x00 }, + { 0x12, 0x00 }, { 0x13, 0x00 }, { 0x14, 0x00 }, + { 0x15, 0x00 }, { 0x24, 0x00 }, +}; + +/* + * Output Digital volume control: + * from -12.5 to 3 dB in 0.5 dB steps (mute instead of -12.5 dB) + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, -1250, 50, 0); + +/* + * HP-Amp Analog volume control: + * from -4.2 to 6 dB in 2 dB steps (mute instead of -4.2 dB) + */ +static DECLARE_TLV_DB_SCALE(hpg_tlv, -4200, 20, 0); + +static const char * const ak4375_ovolcn_select_texts[] = { "Dependent", "Independent" }; +static const char * const ak4375_mdac_select_texts[] = { "x1", "x1/2" }; +static const char * const ak4375_cpmode_select_texts[] = { + "Automatic Switching", + "+-VDD Operation", + "+-1/2VDD Operation" +}; + +/* + * DASD, DASL bits Digital Filter Setting + * 0, 0 : Sharp Roll-Off Filter + * 0, 1 : Slow Roll-Off Filter + * 1, 0 : Short delay Sharp Roll-Off Filter + * 1, 1 : Short delay Slow Roll-Off Filter + */ +static const char * const ak4375_digfil_select_texts[] = { + "Sharp Roll-Off Filter", + "Slow Roll-Off Filter", + "Short delay Sharp Roll-Off Filter", + "Short delay Slow Roll-Off Filter", +}; + +static const struct soc_enum ak4375_ovolcn_enum = + SOC_ENUM_SINGLE(AK4375_0B_LCH_OUTPUT_VOLUME, 7, + ARRAY_SIZE(ak4375_ovolcn_select_texts), ak4375_ovolcn_select_texts); +static const struct soc_enum ak4375_mdacl_enum = + SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 2, + ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts); +static const struct soc_enum ak4375_mdacr_enum = + SOC_ENUM_SINGLE(AK4375_07_DAC_MONO_MIXING, 6, + ARRAY_SIZE(ak4375_mdac_select_texts), ak4375_mdac_select_texts); +static const struct soc_enum ak4375_cpmode_enum = + SOC_ENUM_SINGLE(AK4375_03_POWER_MANAGEMENT4, 2, + ARRAY_SIZE(ak4375_cpmode_select_texts), ak4375_cpmode_select_texts); +static const struct soc_enum ak4375_digfil_enum = + SOC_ENUM_SINGLE(AK4375_06_DIGITAL_FILTER_SELECT, 6, + ARRAY_SIZE(ak4375_digfil_select_texts), ak4375_digfil_select_texts); + +static const struct snd_kcontrol_new ak4375_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Output Volume", AK4375_0B_LCH_OUTPUT_VOLUME, + AK4375_0C_RCH_OUTPUT_VOLUME, 0, 0x1f, 0, dac_tlv), + SOC_SINGLE_TLV("HP-Amp Analog Volume", + AK4375_0D_HP_VOLUME_CONTROL, 0, 0x1f, 0, hpg_tlv), + + SOC_DOUBLE("DAC Signal Invert Switch", AK4375_07_DAC_MONO_MIXING, 3, 7, 1, 0), + + SOC_ENUM("Digital Volume Control", ak4375_ovolcn_enum), + SOC_ENUM("DACL Signal Level", ak4375_mdacl_enum), + SOC_ENUM("DACR Signal Level", ak4375_mdacr_enum), + SOC_ENUM("Charge Pump Mode", ak4375_cpmode_enum), + SOC_ENUM("DAC Digital Filter Mode", ak4375_digfil_enum), +}; + +static const struct snd_kcontrol_new ak4375_hpl_mixer_controls[] = { + SOC_DAPM_SINGLE("LDACL Switch", AK4375_07_DAC_MONO_MIXING, 0, 1, 0), + SOC_DAPM_SINGLE("RDACL Switch", AK4375_07_DAC_MONO_MIXING, 1, 1, 0), +}; + +static const struct snd_kcontrol_new ak4375_hpr_mixer_controls[] = { + SOC_DAPM_SINGLE("LDACR Switch", AK4375_07_DAC_MONO_MIXING, 4, 1, 0), + SOC_DAPM_SINGLE("RDACR Switch", AK4375_07_DAC_MONO_MIXING, 5, 1, 0), +}; + +static int ak4375_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, PMPLL); + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, PMCP1); + usleep_range(6500, 7000); + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, PMLDO); + usleep_range(1000, 2000); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, PMCP2); + usleep_range(4500, 5000); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP2, 0x0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMLDO, 0x0); + snd_soc_component_update_bits(component, AK4375_01_POWER_MANAGEMENT2, PMCP1, 0x0); + snd_soc_component_update_bits(component, AK4375_00_POWER_MANAGEMENT1, PMPLL, 0x0); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget ak4375_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("DAC", NULL, AK4375_02_POWER_MANAGEMENT3, 0, 0, ak4375_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_IN("SDTI", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + + SND_SOC_DAPM_MIXER("HPR Mixer", AK4375_03_POWER_MANAGEMENT4, 1, 0, + &ak4375_hpr_mixer_controls[0], ARRAY_SIZE(ak4375_hpr_mixer_controls)), + SND_SOC_DAPM_MIXER("HPL Mixer", AK4375_03_POWER_MANAGEMENT4, 0, 0, + &ak4375_hpl_mixer_controls[0], ARRAY_SIZE(ak4375_hpl_mixer_controls)), +}; + +static const struct snd_soc_dapm_route ak4375_intercon[] = { + { "DAC", NULL, "SDTI" }, + + { "HPL Mixer", "LDACL Switch", "DAC" }, + { "HPL Mixer", "RDACL Switch", "DAC" }, + { "HPR Mixer", "LDACR Switch", "DAC" }, + { "HPR Mixer", "RDACR Switch", "DAC" }, + + { "HPL", NULL, "HPL Mixer" }, + { "HPR", NULL, "HPR Mixer" }, +}; + +static int ak4375_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component); + unsigned int freq_in, freq_out; + + ak4375->rate = params_rate(params); + + if (ak4375->rate <= 96000) + ak4375->pld = 0; + else + ak4375->pld = 1; + + freq_in = 32 * ak4375->rate / (ak4375->pld + 1); + + if ((ak4375->rate % 8000) == 0) + freq_out = AK4375_PLL_FREQ_OUT_122880000; + else + freq_out = AK4375_PLL_FREQ_OUT_112896000; + + return snd_soc_dai_set_pll(dai, 0, 0, freq_in, freq_out); +} + +static int ak4375_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component); + unsigned int mclk, plm, mdiv, div; + u8 cms, fs, cm; + + cms = snd_soc_component_read(component, AK4375_05_CLOCK_MODE_SELECT); + fs = cms & ~FS_MASK; + cm = cms & ~CM_MASK; + + switch (ak4375->rate) { + case 8000: + fs |= FS_8KHZ; + break; + case 11025: + fs |= FS_11_025KHZ; + break; + case 16000: + fs |= FS_16KHZ; + break; + case 22050: + fs |= FS_22_05KHZ; + break; + case 32000: + fs |= FS_32KHZ; + break; + case 44100: + fs |= FS_44_1KHZ; + break; + case 48000: + fs |= FS_48KHZ; + break; + case 88200: + fs |= FS_88_2KHZ; + break; + case 96000: + fs |= FS_96KHZ; + break; + case 176400: + fs |= FS_176_4KHZ; + break; + case 192000: + fs |= FS_192KHZ; + break; + default: + return -EINVAL; + } + + if (ak4375->rate <= 24000) { + cm |= CM_1; + mclk = 512 * ak4375->rate; + mdiv = freq_out / mclk - 1; + div = 0; + } else if (ak4375->rate <= 96000) { + cm |= CM_0; + mclk = 256 * ak4375->rate; + mdiv = freq_out / mclk - 1; + div = 0; + } else { + cm |= CM_3; + mclk = 128 * ak4375->rate; + mdiv = 4; + div = 1; + } + + /* Writing both fields in one go seems to make playback choppy on start */ + snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, FS_MASK, fs); + snd_soc_component_update_bits(component, AK4375_05_CLOCK_MODE_SELECT, CM_MASK, cm); + + snd_soc_component_write(component, AK4375_0F_PLL_REF_CLK_DIVIDER1, + (ak4375->pld & 0xff00) >> 8); + snd_soc_component_write(component, AK4375_10_PLL_REF_CLK_DIVIDER2, + ak4375->pld & 0x00ff); + + plm = freq_out / freq_in - 1; + snd_soc_component_write(component, AK4375_11_PLL_FB_CLK_DIVIDER1, (plm & 0xff00) >> 8); + snd_soc_component_write(component, AK4375_12_PLL_FB_CLK_DIVIDER2, plm & 0x00ff); + + snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, DIV, div); + + /* SRCCKS bit: force to 1 for SRC PLL source clock */ + snd_soc_component_update_bits(component, AK4375_13_SRC_CLK_SOURCE, SRCCKS, SRCCKS); + + snd_soc_component_write(component, AK4375_14_DAC_CLK_DIVIDER, mdiv); + + dev_dbg(ak4375->dev, "rate=%d mclk=%d f_in=%d f_out=%d PLD=%d PLM=%d MDIV=%d DIV=%d\n", + ak4375->rate, mclk, freq_in, freq_out, ak4375->pld, plm, mdiv, div); + + return 0; +} + +static int ak4375_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct ak4375_priv *ak4375 = snd_soc_component_get_drvdata(component); + u8 val = snd_soc_component_read(component, AK4375_07_DAC_MONO_MIXING); + + dev_dbg(ak4375->dev, "mute=%d val=%d\n", mute, val); + + if (mute) { + ak4375->mute_save = val & DACMUTE_MASK; + val &= ~DACMUTE_MASK; + } else { + val |= ak4375->mute_save; + } + + snd_soc_component_write(component, AK4375_07_DAC_MONO_MIXING, val); + + return 0; +} + +#define AK4375_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +#define AK4375_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops ak4375_dai_ops = { + .hw_params = ak4375_hw_params, + .mute_stream = ak4375_mute, + .set_pll = ak4375_dai_set_pll, +}; + +static struct snd_soc_dai_driver ak4375_dai = { + .name = "ak4375-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AK4375_RATES, + .rate_min = 8000, + .rate_max = 192000, + .formats = AK4375_FORMATS, + }, + .ops = &ak4375_dai_ops, +}; + +static void ak4375_power_off(struct ak4375_priv *ak4375) +{ + gpiod_set_value_cansleep(ak4375->pdn_gpiod, 0); + usleep_range(1000, 2000); + + regulator_bulk_disable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies); +} + +static int ak4375_power_on(struct ak4375_priv *ak4375) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ak4375->supplies), ak4375->supplies); + if (ret < 0) { + dev_err(ak4375->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + usleep_range(3000, 4000); + + gpiod_set_value_cansleep(ak4375->pdn_gpiod, 1); + usleep_range(1000, 2000); + + return 0; +} + +static int __maybe_unused ak4375_runtime_suspend(struct device *dev) +{ + struct ak4375_priv *ak4375 = dev_get_drvdata(dev); + + regcache_cache_only(ak4375->regmap, true); + ak4375_power_off(ak4375); + + return 0; +} + +static int __maybe_unused ak4375_runtime_resume(struct device *dev) +{ + struct ak4375_priv *ak4375 = dev_get_drvdata(dev); + int ret; + + ret = ak4375_power_on(ak4375); + if (ret < 0) + return ret; + + regcache_cache_only(ak4375->regmap, false); + regcache_mark_dirty(ak4375->regmap); + + return regcache_sync(ak4375->regmap); +} + +static const struct snd_soc_component_driver soc_codec_dev_ak4375 = { + .controls = ak4375_snd_controls, + .num_controls = ARRAY_SIZE(ak4375_snd_controls), + .dapm_widgets = ak4375_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4375_dapm_widgets), + .dapm_routes = ak4375_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4375_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config ak4375_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AK4375_24_MODE_CONTROL, + .reg_defaults = ak4375_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4375_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct ak4375_drvdata ak4375_drvdata = { + .dai_drv = &ak4375_dai, + .comp_drv = &soc_codec_dev_ak4375, +}; + +static const struct dev_pm_ops ak4375_pm = { + SET_RUNTIME_PM_OPS(ak4375_runtime_suspend, ak4375_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static int ak4375_i2c_probe(struct i2c_client *i2c) +{ + struct ak4375_priv *ak4375; + const struct ak4375_drvdata *drvdata; + unsigned int deviceid; + int ret, i; + + ak4375 = devm_kzalloc(&i2c->dev, sizeof(*ak4375), GFP_KERNEL); + if (!ak4375) + return -ENOMEM; + + ak4375->regmap = devm_regmap_init_i2c(i2c, &ak4375_regmap); + if (IS_ERR(ak4375->regmap)) + return PTR_ERR(ak4375->regmap); + + i2c_set_clientdata(i2c, ak4375); + ak4375->dev = &i2c->dev; + + drvdata = of_device_get_match_data(&i2c->dev); + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + ak4375->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(ak4375->dev, ARRAY_SIZE(ak4375->supplies), ak4375->supplies); + if (ret < 0) { + dev_err(ak4375->dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + ak4375->pdn_gpiod = devm_gpiod_get_optional(ak4375->dev, "pdn", GPIOD_OUT_LOW); + if (IS_ERR(ak4375->pdn_gpiod)) + return dev_err_probe(ak4375->dev, PTR_ERR(ak4375->pdn_gpiod), + "failed to get pdn\n"); + + ret = ak4375_power_on(ak4375); + if (ret < 0) + return ret; + + /* Don't read deviceid from cache */ + regcache_cache_bypass(ak4375->regmap, true); + + ret = regmap_read(ak4375->regmap, AK4375_15_AUDIO_IF_FORMAT, &deviceid); + if (ret < 0) { + dev_err(ak4375->dev, "unable to read DEVICEID!\n"); + return ret; + } + + regcache_cache_bypass(ak4375->regmap, false); + + deviceid = (deviceid & DEVICEID_MASK) >> 5; + + switch (deviceid) { + case DEVICEID_AK4331: + dev_err(ak4375->dev, "found untested AK4331\n"); + return -EINVAL; + case DEVICEID_AK4375: + dev_dbg(ak4375->dev, "found AK4375\n"); + break; + case DEVICEID_AK4375A: + dev_dbg(ak4375->dev, "found AK4375A\n"); + break; + case DEVICEID_AK4376A: + dev_err(ak4375->dev, "found unsupported AK4376/A!\n"); + return -EINVAL; + case DEVICEID_AK4377: + dev_err(ak4375->dev, "found unsupported AK4377!\n"); + return -EINVAL; + default: + dev_err(ak4375->dev, "unrecognized DEVICEID!\n"); + return -EINVAL; + } + + pm_runtime_set_active(ak4375->dev); + pm_runtime_enable(ak4375->dev); + + ret = devm_snd_soc_register_component(ak4375->dev, drvdata->comp_drv, + drvdata->dai_drv, 1); + if (ret < 0) { + dev_err(ak4375->dev, "Failed to register CODEC: %d\n", ret); + return ret; + } + + return 0; +} + +static void ak4375_i2c_remove(struct i2c_client *i2c) +{ + pm_runtime_disable(&i2c->dev); +} + +static const struct of_device_id ak4375_of_match[] = { + { .compatible = "asahi-kasei,ak4375", .data = &ak4375_drvdata }, + { }, +}; +MODULE_DEVICE_TABLE(of, ak4375_of_match); + +static struct i2c_driver ak4375_i2c_driver = { + .driver = { + .name = "ak4375", + .pm = &ak4375_pm, + .of_match_table = ak4375_of_match, + }, + .probe = ak4375_i2c_probe, + .remove = ak4375_i2c_remove, +}; +module_i2c_driver(ak4375_i2c_driver); + +MODULE_AUTHOR("Vincent Knecht "); +MODULE_DESCRIPTION("ASoC AK4375 DAC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c new file mode 100644 index 0000000000..77678f85ad --- /dev/null +++ b/sound/soc/codecs/ak4458.c @@ -0,0 +1,830 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Audio driver for AK4458 DAC +// +// Copyright (C) 2016 Asahi Kasei Microdevices Corporation +// Copyright 2018 NXP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ak4458.h" + +#define AK4458_NUM_SUPPLIES 2 +static const char *ak4458_supply_names[AK4458_NUM_SUPPLIES] = { + "DVDD", + "AVDD", +}; + +enum ak4458_type { + AK4458 = 0, + AK4497 = 1, +}; + +struct ak4458_drvdata { + struct snd_soc_dai_driver *dai_drv; + const struct snd_soc_component_driver *comp_drv; + enum ak4458_type type; +}; + +/* AK4458 Codec Private Data */ +struct ak4458_priv { + struct regulator_bulk_data supplies[AK4458_NUM_SUPPLIES]; + const struct ak4458_drvdata *drvdata; + struct device *dev; + struct regmap *regmap; + struct gpio_desc *reset_gpiod; + struct reset_control *reset; + struct gpio_desc *mute_gpiod; + int digfil; /* SSLOW, SD, SLOW bits */ + int fs; /* sampling rate */ + int fmt; + int slots; + int slot_width; + u32 dsd_path; /* For ak4497 */ +}; + +static const struct reg_default ak4458_reg_defaults[] = { + { 0x00, 0x0C }, /* 0x00 AK4458_00_CONTROL1 */ + { 0x01, 0x22 }, /* 0x01 AK4458_01_CONTROL2 */ + { 0x02, 0x00 }, /* 0x02 AK4458_02_CONTROL3 */ + { 0x03, 0xFF }, /* 0x03 AK4458_03_LCHATT */ + { 0x04, 0xFF }, /* 0x04 AK4458_04_RCHATT */ + { 0x05, 0x00 }, /* 0x05 AK4458_05_CONTROL4 */ + { 0x06, 0x00 }, /* 0x06 AK4458_06_DSD1 */ + { 0x07, 0x03 }, /* 0x07 AK4458_07_CONTROL5 */ + { 0x08, 0x00 }, /* 0x08 AK4458_08_SOUND_CONTROL */ + { 0x09, 0x00 }, /* 0x09 AK4458_09_DSD2 */ + { 0x0A, 0x0D }, /* 0x0A AK4458_0A_CONTROL6 */ + { 0x0B, 0x0C }, /* 0x0B AK4458_0B_CONTROL7 */ + { 0x0C, 0x00 }, /* 0x0C AK4458_0C_CONTROL8 */ + { 0x0D, 0x00 }, /* 0x0D AK4458_0D_CONTROL9 */ + { 0x0E, 0x50 }, /* 0x0E AK4458_0E_CONTROL10 */ + { 0x0F, 0xFF }, /* 0x0F AK4458_0F_L2CHATT */ + { 0x10, 0xFF }, /* 0x10 AK4458_10_R2CHATT */ + { 0x11, 0xFF }, /* 0x11 AK4458_11_L3CHATT */ + { 0x12, 0xFF }, /* 0x12 AK4458_12_R3CHATT */ + { 0x13, 0xFF }, /* 0x13 AK4458_13_L4CHATT */ + { 0x14, 0xFF }, /* 0x14 AK4458_14_R4CHATT */ +}; + +/* + * Volume control: + * from -127 to 0 dB in 0.5 dB steps (mute instead of -127.5 dB) + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); + +/* + * DEM1 bit DEM0 bit Mode + * 0 0 44.1kHz + * 0 1 OFF (default) + * 1 0 48kHz + * 1 1 32kHz + */ +static const char * const ak4458_dem_select_texts[] = { + "44.1kHz", "OFF", "48kHz", "32kHz" +}; + +/* + * SSLOW, SD, SLOW bits Digital Filter Setting + * 0, 0, 0 : Sharp Roll-Off Filter + * 0, 0, 1 : Slow Roll-Off Filter + * 0, 1, 0 : Short delay Sharp Roll-Off Filter + * 0, 1, 1 : Short delay Slow Roll-Off Filter + * 1, *, * : Super Slow Roll-Off Filter + */ +static const char * const ak4458_digfil_select_texts[] = { + "Sharp Roll-Off Filter", + "Slow Roll-Off Filter", + "Short delay Sharp Roll-Off Filter", + "Short delay Slow Roll-Off Filter", + "Super Slow Roll-Off Filter" +}; + +/* + * DZFB: Inverting Enable of DZF + * 0: DZF goes H at Zero Detection + * 1: DZF goes L at Zero Detection + */ +static const char * const ak4458_dzfb_select_texts[] = {"H", "L"}; + +/* + * SC1-0 bits: Sound Mode Setting + * 0 0 : Sound Mode 0 + * 0 1 : Sound Mode 1 + * 1 0 : Sound Mode 2 + * 1 1 : Reserved + */ +static const char * const ak4458_sc_select_texts[] = { + "Sound Mode 0", "Sound Mode 1", "Sound Mode 2" +}; + +/* FIR2-0 bits: FIR Filter Mode Setting */ +static const char * const ak4458_fir_select_texts[] = { + "Mode 0", "Mode 1", "Mode 2", "Mode 3", + "Mode 4", "Mode 5", "Mode 6", "Mode 7", +}; + +/* ATS1-0 bits Attenuation Speed */ +static const char * const ak4458_ats_select_texts[] = { + "4080/fs", "2040/fs", "510/fs", "255/fs", +}; + +/* DIF2 bit Audio Interface Format Setting(BICK fs) */ +static const char * const ak4458_dif_select_texts[] = {"32fs,48fs", "64fs",}; + +static const struct soc_enum ak4458_dac1_dem_enum = + SOC_ENUM_SINGLE(AK4458_01_CONTROL2, 1, + ARRAY_SIZE(ak4458_dem_select_texts), + ak4458_dem_select_texts); +static const struct soc_enum ak4458_dac2_dem_enum = + SOC_ENUM_SINGLE(AK4458_0A_CONTROL6, 0, + ARRAY_SIZE(ak4458_dem_select_texts), + ak4458_dem_select_texts); +static const struct soc_enum ak4458_dac3_dem_enum = + SOC_ENUM_SINGLE(AK4458_0E_CONTROL10, 4, + ARRAY_SIZE(ak4458_dem_select_texts), + ak4458_dem_select_texts); +static const struct soc_enum ak4458_dac4_dem_enum = + SOC_ENUM_SINGLE(AK4458_0E_CONTROL10, 6, + ARRAY_SIZE(ak4458_dem_select_texts), + ak4458_dem_select_texts); +static const struct soc_enum ak4458_digfil_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ak4458_digfil_select_texts), + ak4458_digfil_select_texts); +static const struct soc_enum ak4458_dzfb_enum = + SOC_ENUM_SINGLE(AK4458_02_CONTROL3, 2, + ARRAY_SIZE(ak4458_dzfb_select_texts), + ak4458_dzfb_select_texts); +static const struct soc_enum ak4458_sm_enum = + SOC_ENUM_SINGLE(AK4458_08_SOUND_CONTROL, 0, + ARRAY_SIZE(ak4458_sc_select_texts), + ak4458_sc_select_texts); +static const struct soc_enum ak4458_fir_enum = + SOC_ENUM_SINGLE(AK4458_0C_CONTROL8, 0, + ARRAY_SIZE(ak4458_fir_select_texts), + ak4458_fir_select_texts); +static const struct soc_enum ak4458_ats_enum = + SOC_ENUM_SINGLE(AK4458_0B_CONTROL7, 6, + ARRAY_SIZE(ak4458_ats_select_texts), + ak4458_ats_select_texts); +static const struct soc_enum ak4458_dif_enum = + SOC_ENUM_SINGLE(AK4458_00_CONTROL1, 3, + ARRAY_SIZE(ak4458_dif_select_texts), + ak4458_dif_select_texts); + +static int get_digfil(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = ak4458->digfil; + + return 0; +} + +static int set_digfil(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); + int num; + + num = ucontrol->value.enumerated.item[0]; + if (num > 4) + return -EINVAL; + + ak4458->digfil = num; + + /* write SD bit */ + snd_soc_component_update_bits(component, AK4458_01_CONTROL2, + AK4458_SD_MASK, + ((ak4458->digfil & 0x02) << 4)); + + /* write SLOW bit */ + snd_soc_component_update_bits(component, AK4458_02_CONTROL3, + AK4458_SLOW_MASK, + (ak4458->digfil & 0x01)); + + /* write SSLOW bit */ + snd_soc_component_update_bits(component, AK4458_05_CONTROL4, + AK4458_SSLOW_MASK, + ((ak4458->digfil & 0x04) >> 2)); + + return 0; +} + +static const struct snd_kcontrol_new ak4458_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC1 Playback Volume", AK4458_03_LCHATT, + AK4458_04_RCHATT, 0, 0xFF, 0, dac_tlv), + SOC_DOUBLE_R_TLV("DAC2 Playback Volume", AK4458_0F_L2CHATT, + AK4458_10_R2CHATT, 0, 0xFF, 0, dac_tlv), + SOC_DOUBLE_R_TLV("DAC3 Playback Volume", AK4458_11_L3CHATT, + AK4458_12_R3CHATT, 0, 0xFF, 0, dac_tlv), + SOC_DOUBLE_R_TLV("DAC4 Playback Volume", AK4458_13_L4CHATT, + AK4458_14_R4CHATT, 0, 0xFF, 0, dac_tlv), + SOC_ENUM("AK4458 De-emphasis Response DAC1", ak4458_dac1_dem_enum), + SOC_ENUM("AK4458 De-emphasis Response DAC2", ak4458_dac2_dem_enum), + SOC_ENUM("AK4458 De-emphasis Response DAC3", ak4458_dac3_dem_enum), + SOC_ENUM("AK4458 De-emphasis Response DAC4", ak4458_dac4_dem_enum), + SOC_ENUM_EXT("AK4458 Digital Filter Setting", ak4458_digfil_enum, + get_digfil, set_digfil), + SOC_ENUM("AK4458 Inverting Enable of DZFB", ak4458_dzfb_enum), + SOC_ENUM("AK4458 Sound Mode", ak4458_sm_enum), + SOC_ENUM("AK4458 FIR Filter Mode Setting", ak4458_fir_enum), + SOC_ENUM("AK4458 Attenuation transition Time Setting", + ak4458_ats_enum), + SOC_ENUM("AK4458 BICK fs Setting", ak4458_dif_enum), +}; + +/* ak4458 dapm widgets */ +static const struct snd_soc_dapm_widget ak4458_dapm_widgets[] = { + SND_SOC_DAPM_DAC("AK4458 DAC1", NULL, AK4458_0A_CONTROL6, 2, 0),/*pw*/ + SND_SOC_DAPM_AIF_IN("AK4458 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("AK4458 AOUTA"), + + SND_SOC_DAPM_DAC("AK4458 DAC2", NULL, AK4458_0A_CONTROL6, 3, 0),/*pw*/ + SND_SOC_DAPM_OUTPUT("AK4458 AOUTB"), + + SND_SOC_DAPM_DAC("AK4458 DAC3", NULL, AK4458_0B_CONTROL7, 2, 0),/*pw*/ + SND_SOC_DAPM_OUTPUT("AK4458 AOUTC"), + + SND_SOC_DAPM_DAC("AK4458 DAC4", NULL, AK4458_0B_CONTROL7, 3, 0),/*pw*/ + SND_SOC_DAPM_OUTPUT("AK4458 AOUTD"), +}; + +static const struct snd_soc_dapm_route ak4458_intercon[] = { + {"AK4458 DAC1", NULL, "AK4458 SDTI"}, + {"AK4458 AOUTA", NULL, "AK4458 DAC1"}, + + {"AK4458 DAC2", NULL, "AK4458 SDTI"}, + {"AK4458 AOUTB", NULL, "AK4458 DAC2"}, + + {"AK4458 DAC3", NULL, "AK4458 SDTI"}, + {"AK4458 AOUTC", NULL, "AK4458 DAC3"}, + + {"AK4458 DAC4", NULL, "AK4458 SDTI"}, + {"AK4458 AOUTD", NULL, "AK4458 DAC4"}, +}; + +/* ak4497 controls */ +static const struct snd_kcontrol_new ak4497_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC Playback Volume", AK4458_03_LCHATT, + AK4458_04_RCHATT, 0, 0xFF, 0, dac_tlv), + SOC_ENUM("AK4497 De-emphasis Response DAC", ak4458_dac1_dem_enum), + SOC_ENUM_EXT("AK4497 Digital Filter Setting", ak4458_digfil_enum, + get_digfil, set_digfil), + SOC_ENUM("AK4497 Inverting Enable of DZFB", ak4458_dzfb_enum), + SOC_ENUM("AK4497 Sound Mode", ak4458_sm_enum), + SOC_ENUM("AK4497 Attenuation transition Time Setting", + ak4458_ats_enum), +}; + +/* ak4497 dapm widgets */ +static const struct snd_soc_dapm_widget ak4497_dapm_widgets[] = { + SND_SOC_DAPM_DAC("AK4497 DAC", NULL, AK4458_0A_CONTROL6, 2, 0), + SND_SOC_DAPM_AIF_IN("AK4497 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("AK4497 AOUT"), +}; + +/* ak4497 dapm routes */ +static const struct snd_soc_dapm_route ak4497_intercon[] = { + {"AK4497 DAC", NULL, "AK4497 SDTI"}, + {"AK4497 AOUT", NULL, "AK4497 DAC"}, + +}; + +static int ak4458_get_tdm_mode(struct ak4458_priv *ak4458) +{ + switch (ak4458->slots * ak4458->slot_width) { + case 128: + return 1; + case 256: + return 2; + case 512: + return 3; + default: + return 0; + } +} + +static int ak4458_rstn_control(struct snd_soc_component *component, int bit) +{ + int ret; + + if (bit) + ret = snd_soc_component_update_bits(component, + AK4458_00_CONTROL1, + AK4458_RSTN_MASK, + 0x1); + else + ret = snd_soc_component_update_bits(component, + AK4458_00_CONTROL1, + AK4458_RSTN_MASK, + 0x0); + if (ret < 0) + return ret; + + return 0; +} + +static int ak4458_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); + int pcm_width = max(params_physical_width(params), ak4458->slot_width); + u8 format, dsdsel0, dsdsel1, dchn; + int nfs1, dsd_bclk, ret, channels, channels_max; + + nfs1 = params_rate(params); + ak4458->fs = nfs1; + + /* calculate bit clock */ + channels = params_channels(params); + channels_max = dai->driver->playback.channels_max; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_DSD_U8: + case SNDRV_PCM_FORMAT_DSD_U16_LE: + case SNDRV_PCM_FORMAT_DSD_U16_BE: + case SNDRV_PCM_FORMAT_DSD_U32_LE: + case SNDRV_PCM_FORMAT_DSD_U32_BE: + dsd_bclk = nfs1 * params_physical_width(params); + switch (dsd_bclk) { + case 2822400: + dsdsel0 = 0; + dsdsel1 = 0; + break; + case 5644800: + dsdsel0 = 1; + dsdsel1 = 0; + break; + case 11289600: + dsdsel0 = 0; + dsdsel1 = 1; + break; + case 22579200: + if (ak4458->drvdata->type == AK4497) { + dsdsel0 = 1; + dsdsel1 = 1; + } else { + dev_err(dai->dev, "DSD512 not supported.\n"); + return -EINVAL; + } + break; + default: + dev_err(dai->dev, "Unsupported dsd bclk.\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AK4458_06_DSD1, + AK4458_DSDSEL_MASK, dsdsel0); + snd_soc_component_update_bits(component, AK4458_09_DSD2, + AK4458_DSDSEL_MASK, dsdsel1); + break; + } + + /* Master Clock Frequency Auto Setting Mode Enable */ + snd_soc_component_update_bits(component, AK4458_00_CONTROL1, 0x80, 0x80); + + switch (pcm_width) { + case 16: + if (ak4458->fmt == SND_SOC_DAIFMT_I2S) + format = AK4458_DIF_24BIT_I2S; + else + format = AK4458_DIF_16BIT_LSB; + break; + case 32: + switch (ak4458->fmt) { + case SND_SOC_DAIFMT_I2S: + format = AK4458_DIF_32BIT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = AK4458_DIF_32BIT_MSB; + break; + case SND_SOC_DAIFMT_RIGHT_J: + format = AK4458_DIF_32BIT_LSB; + break; + case SND_SOC_DAIFMT_DSP_B: + format = AK4458_DIF_32BIT_MSB; + break; + case SND_SOC_DAIFMT_PDM: + format = AK4458_DIF_32BIT_MSB; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, AK4458_00_CONTROL1, + AK4458_DIF_MASK, format); + + /* + * Enable/disable Daisy Chain if in TDM mode and the number of played + * channels is bigger than the maximum supported number of channels + */ + dchn = ak4458_get_tdm_mode(ak4458) && + (ak4458->fmt == SND_SOC_DAIFMT_DSP_B) && + (channels > channels_max) ? AK4458_DCHAIN_MASK : 0; + + snd_soc_component_update_bits(component, AK4458_0B_CONTROL7, + AK4458_DCHAIN_MASK, dchn); + + if (ak4458->drvdata->type == AK4497) { + ret = snd_soc_component_update_bits(component, AK4458_09_DSD2, + 0x4, (ak4458->dsd_path << 2)); + if (ret < 0) + return ret; + } + + ret = ak4458_rstn_control(component, 0); + if (ret) + return ret; + + ret = ak4458_rstn_control(component, 1); + if (ret) + return ret; + + return 0; +} + +static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); + int ret; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: /* Consumer Mode */ + break; + case SND_SOC_DAIFMT_CBP_CFP: /* Provider Mode is not supported */ + case SND_SOC_DAIFMT_CBC_CFP: + case SND_SOC_DAIFMT_CBP_CFC: + default: + dev_err(component->dev, "Clock provider mode unsupported\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_DSP_B: + case SND_SOC_DAIFMT_PDM: + ak4458->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + dev_err(component->dev, "Audio format 0x%02X unsupported\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + /* DSD mode */ + snd_soc_component_update_bits(component, AK4458_02_CONTROL3, + AK4458_DP_MASK, + ak4458->fmt == SND_SOC_DAIFMT_PDM ? + AK4458_DP_MASK : 0); + + ret = ak4458_rstn_control(component, 0); + if (ret) + return ret; + + ret = ak4458_rstn_control(component, 1); + if (ret) + return ret; + + return 0; +} + +static const int att_speed[] = { 4080, 2040, 510, 255 }; + +static int ak4458_set_dai_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); + int nfs, ndt, reg; + int ats; + + nfs = ak4458->fs; + + reg = snd_soc_component_read(component, AK4458_0B_CONTROL7); + ats = (reg & AK4458_ATS_MASK) >> AK4458_ATS_SHIFT; + + ndt = att_speed[ats] / (nfs / 1000); + + if (mute) { + snd_soc_component_update_bits(component, AK4458_01_CONTROL2, 0x01, 1); + mdelay(ndt); + if (ak4458->mute_gpiod) + gpiod_set_value_cansleep(ak4458->mute_gpiod, 1); + } else { + if (ak4458->mute_gpiod) + gpiod_set_value_cansleep(ak4458->mute_gpiod, 0); + snd_soc_component_update_bits(component, AK4458_01_CONTROL2, 0x01, 0); + mdelay(ndt); + } + + return 0; +} + +static int ak4458_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); + int mode; + + ak4458->slots = slots; + ak4458->slot_width = slot_width; + + mode = ak4458_get_tdm_mode(ak4458) << AK4458_MODE_SHIFT; + + snd_soc_component_update_bits(component, AK4458_0A_CONTROL6, + AK4458_MODE_MASK, + mode); + + return 0; +} + +#define AK4458_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE |\ + SNDRV_PCM_FMTBIT_DSD_U8 |\ + SNDRV_PCM_FMTBIT_DSD_U16_LE |\ + SNDRV_PCM_FMTBIT_DSD_U32_LE) + +static const unsigned int ak4458_rates[] = { + 8000, 11025, 16000, 22050, + 32000, 44100, 48000, 88200, + 96000, 176400, 192000, 352800, + 384000, 705600, 768000, 1411200, + 2822400, +}; + +static const struct snd_pcm_hw_constraint_list ak4458_rate_constraints = { + .count = ARRAY_SIZE(ak4458_rates), + .list = ak4458_rates, +}; + +static int ak4458_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &ak4458_rate_constraints); + + return ret; +} + +static const struct snd_soc_dai_ops ak4458_dai_ops = { + .startup = ak4458_startup, + .hw_params = ak4458_hw_params, + .set_fmt = ak4458_set_dai_fmt, + .mute_stream = ak4458_set_dai_mute, + .set_tdm_slot = ak4458_set_tdm_slot, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver ak4458_dai = { + .name = "ak4458-aif", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = AK4458_FORMATS, + }, + .ops = &ak4458_dai_ops, +}; + +static struct snd_soc_dai_driver ak4497_dai = { + .name = "ak4497-aif", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = AK4458_FORMATS, + }, + .ops = &ak4458_dai_ops, +}; + +static void ak4458_reset(struct ak4458_priv *ak4458, bool active) +{ + if (ak4458->reset_gpiod) { + gpiod_set_value_cansleep(ak4458->reset_gpiod, active); + usleep_range(1000, 2000); + } else if (!IS_ERR_OR_NULL(ak4458->reset)) { + if (active) + reset_control_assert(ak4458->reset); + else + reset_control_deassert(ak4458->reset); + usleep_range(1000, 2000); + } +} + +#ifdef CONFIG_PM +static int __maybe_unused ak4458_runtime_suspend(struct device *dev) +{ + struct ak4458_priv *ak4458 = dev_get_drvdata(dev); + + regcache_cache_only(ak4458->regmap, true); + + ak4458_reset(ak4458, true); + + if (ak4458->mute_gpiod) + gpiod_set_value_cansleep(ak4458->mute_gpiod, 0); + + regulator_bulk_disable(ARRAY_SIZE(ak4458->supplies), + ak4458->supplies); + return 0; +} + +static int __maybe_unused ak4458_runtime_resume(struct device *dev) +{ + struct ak4458_priv *ak4458 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ak4458->supplies), + ak4458->supplies); + if (ret != 0) { + dev_err(ak4458->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + if (ak4458->mute_gpiod) + gpiod_set_value_cansleep(ak4458->mute_gpiod, 1); + + ak4458_reset(ak4458, false); + + regcache_cache_only(ak4458->regmap, false); + regcache_mark_dirty(ak4458->regmap); + + return regcache_sync(ak4458->regmap); +} +#endif /* CONFIG_PM */ + +static const struct snd_soc_component_driver soc_codec_dev_ak4458 = { + .controls = ak4458_snd_controls, + .num_controls = ARRAY_SIZE(ak4458_snd_controls), + .dapm_widgets = ak4458_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4458_dapm_widgets), + .dapm_routes = ak4458_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4458_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_component_driver soc_codec_dev_ak4497 = { + .controls = ak4497_snd_controls, + .num_controls = ARRAY_SIZE(ak4497_snd_controls), + .dapm_widgets = ak4497_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4497_dapm_widgets), + .dapm_routes = ak4497_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4497_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config ak4458_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AK4458_14_R4CHATT, + .reg_defaults = ak4458_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4458_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct ak4458_drvdata ak4458_drvdata = { + .dai_drv = &ak4458_dai, + .comp_drv = &soc_codec_dev_ak4458, + .type = AK4458, +}; + +static const struct ak4458_drvdata ak4497_drvdata = { + .dai_drv = &ak4497_dai, + .comp_drv = &soc_codec_dev_ak4497, + .type = AK4497, +}; + +static const struct dev_pm_ops ak4458_pm = { + SET_RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static int ak4458_i2c_probe(struct i2c_client *i2c) +{ + struct ak4458_priv *ak4458; + int ret, i; + + ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL); + if (!ak4458) + return -ENOMEM; + + ak4458->regmap = devm_regmap_init_i2c(i2c, &ak4458_regmap); + if (IS_ERR(ak4458->regmap)) + return PTR_ERR(ak4458->regmap); + + i2c_set_clientdata(i2c, ak4458); + ak4458->dev = &i2c->dev; + + ak4458->drvdata = of_device_get_match_data(&i2c->dev); + + ak4458->reset = devm_reset_control_get_optional_shared(ak4458->dev, NULL); + if (IS_ERR(ak4458->reset)) + return PTR_ERR(ak4458->reset); + + ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ak4458->reset_gpiod)) + return PTR_ERR(ak4458->reset_gpiod); + + ak4458->mute_gpiod = devm_gpiod_get_optional(ak4458->dev, "mute", + GPIOD_OUT_LOW); + if (IS_ERR(ak4458->mute_gpiod)) + return PTR_ERR(ak4458->mute_gpiod); + + /* Optional property for ak4497 */ + of_property_read_u32(i2c->dev.of_node, "dsd-path", &ak4458->dsd_path); + + for (i = 0; i < ARRAY_SIZE(ak4458->supplies); i++) + ak4458->supplies[i].supply = ak4458_supply_names[i]; + + ret = devm_regulator_bulk_get(ak4458->dev, ARRAY_SIZE(ak4458->supplies), + ak4458->supplies); + if (ret != 0) { + dev_err(ak4458->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(ak4458->dev, + ak4458->drvdata->comp_drv, + ak4458->drvdata->dai_drv, 1); + if (ret < 0) { + dev_err(ak4458->dev, "Failed to register CODEC: %d\n", ret); + return ret; + } + + pm_runtime_enable(&i2c->dev); + regcache_cache_only(ak4458->regmap, true); + ak4458_reset(ak4458, false); + + return 0; +} + +static void ak4458_i2c_remove(struct i2c_client *i2c) +{ + struct ak4458_priv *ak4458 = i2c_get_clientdata(i2c); + + ak4458_reset(ak4458, true); + pm_runtime_disable(&i2c->dev); +} + +static const struct of_device_id ak4458_of_match[] = { + { .compatible = "asahi-kasei,ak4458", .data = &ak4458_drvdata}, + { .compatible = "asahi-kasei,ak4497", .data = &ak4497_drvdata}, + { }, +}; +MODULE_DEVICE_TABLE(of, ak4458_of_match); + +static struct i2c_driver ak4458_i2c_driver = { + .driver = { + .name = "ak4458", + .pm = &ak4458_pm, + .of_match_table = ak4458_of_match, + }, + .probe = ak4458_i2c_probe, + .remove = ak4458_i2c_remove, +}; + +module_i2c_driver(ak4458_i2c_driver); + +MODULE_AUTHOR("Junichi Wakasugi "); +MODULE_AUTHOR("Mihai Serban "); +MODULE_DESCRIPTION("ASoC AK4458 DAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ak4458.h b/sound/soc/codecs/ak4458.h new file mode 100644 index 0000000000..9ad869575f --- /dev/null +++ b/sound/soc/codecs/ak4458.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Audio driver for AK4458 + * + * Copyright (C) 2016 Asahi Kasei Microdevices Corporation + * Copyright 2018 NXP + */ + +#ifndef _AK4458_H +#define _AK4458_H + +#include + +/* Settings */ + +#define AK4458_00_CONTROL1 0x00 +#define AK4458_01_CONTROL2 0x01 +#define AK4458_02_CONTROL3 0x02 +#define AK4458_03_LCHATT 0x03 +#define AK4458_04_RCHATT 0x04 +#define AK4458_05_CONTROL4 0x05 +#define AK4458_06_DSD1 0x06 +#define AK4458_07_CONTROL5 0x07 +#define AK4458_08_SOUND_CONTROL 0x08 +#define AK4458_09_DSD2 0x09 +#define AK4458_0A_CONTROL6 0x0A +#define AK4458_0B_CONTROL7 0x0B +#define AK4458_0C_CONTROL8 0x0C +#define AK4458_0D_CONTROL9 0x0D +#define AK4458_0E_CONTROL10 0x0E +#define AK4458_0F_L2CHATT 0x0F +#define AK4458_10_R2CHATT 0x10 +#define AK4458_11_L3CHATT 0x11 +#define AK4458_12_R3CHATT 0x12 +#define AK4458_13_L4CHATT 0x13 +#define AK4458_14_R4CHATT 0x14 + +/* Bitfield Definitions */ + +/* AK4458_00_CONTROL1 (0x00) Fields + * Addr Register Name D7 D6 D5 D4 D3 D2 D1 D0 + * 00H Control 1 ACKS 0 0 0 DIF2 DIF1 DIF0 RSTN + */ + +/* Digital Filter (SD, SLOW, SSLOW) */ +#define AK4458_SD_MASK GENMASK(5, 5) +#define AK4458_SLOW_MASK GENMASK(0, 0) +#define AK4458_SSLOW_MASK GENMASK(0, 0) + +/* DIF2 1 0 + * x 1 0 MSB justified Figure 3 (default) + * x 1 1 I2S Compliment Figure 4 + */ +#define AK4458_DIF_SHIFT 1 +#define AK4458_DIF_MASK GENMASK(3, 1) + +#define AK4458_DIF_16BIT_LSB (0 << 1) +#define AK4458_DIF_24BIT_I2S (3 << 1) +#define AK4458_DIF_32BIT_LSB (5 << 1) +#define AK4458_DIF_32BIT_MSB (6 << 1) +#define AK4458_DIF_32BIT_I2S (7 << 1) + +/* AK4458_00_CONTROL1 (0x00) D0 bit */ +#define AK4458_RSTN_MASK GENMASK(0, 0) +#define AK4458_RSTN (0x1 << 0) + +/* AK4458_0A_CONTROL6 Mode bits */ +#define AK4458_MODE_SHIFT 6 +#define AK4458_MODE_MASK GENMASK(7, 6) +#define AK4458_MODE_NORMAL (0 << AK4458_MODE_SHIFT) +#define AK4458_MODE_TDM128 (1 << AK4458_MODE_SHIFT) +#define AK4458_MODE_TDM256 (2 << AK4458_MODE_SHIFT) +#define AK4458_MODE_TDM512 (3 << AK4458_MODE_SHIFT) + +/* DAC Digital attenuator transition time setting + * Table 19 + * Mode ATS1 ATS2 ATT speed + * 0 0 0 4080/fs + * 1 0 1 2040/fs + * 2 1 0 510/fs + * 3 1 1 255/fs + * */ +#define AK4458_ATS_SHIFT 6 +#define AK4458_ATS_MASK GENMASK(7, 6) +#define AK4458_DCHAIN_MASK (0x1 << 1) + +#define AK4458_DSDSEL_MASK (0x1 << 0) +#define AK4458_DP_MASK (0x1 << 7) + +#endif diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c new file mode 100644 index 0000000000..904bf91090 --- /dev/null +++ b/sound/soc/codecs/ak4535.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ak4535.c -- AK4535 ALSA Soc Audio driver + * + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * Based on wm8753.c by Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ak4535.h" + +/* codec private data */ +struct ak4535_priv { + struct regmap *regmap; + unsigned int sysclk; +}; + +/* + * ak4535 register cache + */ +static const struct reg_default ak4535_reg_defaults[] = { + { 0, 0x00 }, + { 1, 0x80 }, + { 2, 0x00 }, + { 3, 0x03 }, + { 4, 0x02 }, + { 5, 0x00 }, + { 6, 0x11 }, + { 7, 0x01 }, + { 8, 0x00 }, + { 9, 0x40 }, + { 10, 0x36 }, + { 11, 0x10 }, + { 12, 0x00 }, + { 13, 0x00 }, + { 14, 0x57 }, +}; + +static bool ak4535_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AK4535_STATUS: + return true; + default: + return false; + } +} + +static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"}; +static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"}; +static const char *ak4535_hp_out[] = {"Stereo", "Mono"}; +static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"}; +static const char *ak4535_mic_select[] = {"Internal", "External"}; + +static const struct soc_enum ak4535_enum[] = { + SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain), + SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out), + SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out), + SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp), + SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select), +}; + +static const struct snd_kcontrol_new ak4535_snd_controls[] = { + SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0), + SOC_ENUM("Mono 1 Output", ak4535_enum[1]), + SOC_ENUM("Mono 1 Gain", ak4535_enum[0]), + SOC_ENUM("Headphone Output", ak4535_enum[2]), + SOC_ENUM("Playback Deemphasis", ak4535_enum[3]), + SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0), + SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0), + SOC_ENUM("Mic Select", ak4535_enum[4]), + SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0), + SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0), + SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0), + SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0), + SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0), + SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0), + SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0), + SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1), + SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1), + SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0), + SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0), +}; + +/* Mono 1 Mixer */ +static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = { + SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0), + SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0), +}; + +/* Stereo Mixer */ +static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = { + SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0), + SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0), +}; + +/* Input Mixer */ +static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = { + SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0), + SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0), +}; + +/* Input mux */ +static const struct snd_kcontrol_new ak4535_input_mux_control = + SOC_DAPM_ENUM("Input Select", ak4535_enum[4]); + +/* HP L switch */ +static const struct snd_kcontrol_new ak4535_hpl_control = + SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1); + +/* HP R switch */ +static const struct snd_kcontrol_new ak4535_hpr_control = + SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1); + +/* mono 2 switch */ +static const struct snd_kcontrol_new ak4535_mono2_control = + SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0); + +/* Line out switch */ +static const struct snd_kcontrol_new ak4535_line_control = + SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0); + +/* ak4535 dapm widgets */ +static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0, + &ak4535_stereo_mixer_controls[0], + ARRAY_SIZE(ak4535_stereo_mixer_controls)), + SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0, + &ak4535_mono1_mixer_controls[0], + ARRAY_SIZE(ak4535_mono1_mixer_controls)), + SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, + &ak4535_input_mixer_controls[0], + ARRAY_SIZE(ak4535_input_mixer_controls)), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, + &ak4535_input_mux_control), + SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0), + SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0, + &ak4535_mono2_control), + /* speaker powersave bit */ + SND_SOC_DAPM_PGA("Speaker Enable", AK4535_MODE2, 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0, + &ak4535_line_control), + SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0, + &ak4535_hpl_control), + SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0, + &ak4535_hpr_control), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("ROUT"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPP"), + SND_SOC_DAPM_OUTPUT("SPN"), + SND_SOC_DAPM_OUTPUT("MOUT1"), + SND_SOC_DAPM_OUTPUT("MOUT2"), + SND_SOC_DAPM_OUTPUT("MICOUT"), + SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 0), + SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0), + + SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0), + SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0), + SND_SOC_DAPM_INPUT("MICIN"), + SND_SOC_DAPM_INPUT("MICEXT"), + SND_SOC_DAPM_INPUT("AUX"), + SND_SOC_DAPM_INPUT("MIN"), + SND_SOC_DAPM_INPUT("AIN"), +}; + +static const struct snd_soc_dapm_route ak4535_audio_map[] = { + /*stereo mixer */ + {"Stereo Mixer", "Playback Switch", "DAC"}, + {"Stereo Mixer", "Mic Sidetone Switch", "Mic"}, + {"Stereo Mixer", "Aux Bypass Switch", "AUX In"}, + + /* mono1 mixer */ + {"Mono1 Mixer", "Mic Sidetone Switch", "Mic"}, + {"Mono1 Mixer", "Mono Playback Switch", "DAC"}, + + /* Mic */ + {"Mic", NULL, "AIN"}, + {"Input Mux", "Internal", "Mic Int Bias"}, + {"Input Mux", "External", "Mic Ext Bias"}, + {"Mic Int Bias", NULL, "MICIN"}, + {"Mic Ext Bias", NULL, "MICEXT"}, + {"MICOUT", NULL, "Input Mux"}, + + /* line out */ + {"LOUT", NULL, "Line Out Enable"}, + {"ROUT", NULL, "Line Out Enable"}, + {"Line Out Enable", "Switch", "Line Out"}, + {"Line Out", NULL, "Stereo Mixer"}, + + /* mono1 out */ + {"MOUT1", NULL, "Mono Out"}, + {"Mono Out", NULL, "Mono1 Mixer"}, + + /* left HP */ + {"HPL", NULL, "Left HP Enable"}, + {"Left HP Enable", "Switch", "HP L Amp"}, + {"HP L Amp", NULL, "Stereo Mixer"}, + + /* right HP */ + {"HPR", NULL, "Right HP Enable"}, + {"Right HP Enable", "Switch", "HP R Amp"}, + {"HP R Amp", NULL, "Stereo Mixer"}, + + /* speaker */ + {"SPP", NULL, "Speaker Enable"}, + {"SPN", NULL, "Speaker Enable"}, + {"Speaker Enable", "Switch", "Spk Amp"}, + {"Spk Amp", NULL, "MIN"}, + + /* mono 2 */ + {"MOUT2", NULL, "Mono 2 Enable"}, + {"Mono 2 Enable", "Switch", "Stereo Mixer"}, + + /* Aux In */ + {"Aux In", NULL, "AUX"}, + + /* ADC */ + {"ADC", NULL, "Input Mixer"}, + {"Input Mixer", "Mic Capture Switch", "Mic"}, + {"Input Mixer", "Aux Capture Switch", "Aux In"}, +}; + +static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct ak4535_priv *ak4535 = snd_soc_component_get_drvdata(component); + + ak4535->sysclk = freq; + return 0; +} + +static int ak4535_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4535_priv *ak4535 = snd_soc_component_get_drvdata(component); + u8 mode2 = snd_soc_component_read(component, AK4535_MODE2) & ~(0x3 << 5); + int rate = params_rate(params), fs = 256; + + if (rate) + fs = ak4535->sysclk / rate; + + /* set fs */ + switch (fs) { + case 1024: + mode2 |= (0x2 << 5); + break; + case 512: + mode2 |= (0x1 << 5); + break; + case 256: + break; + } + + /* set rate */ + snd_soc_component_write(component, AK4535_MODE2, mode2); + return 0; +} + +static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u8 mode1 = 0; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + mode1 = 0x0002; + break; + case SND_SOC_DAIFMT_LEFT_J: + mode1 = 0x0001; + break; + default: + return -EINVAL; + } + + /* use 32 fs for BCLK to save power */ + mode1 |= 0x4; + + snd_soc_component_write(component, AK4535_MODE1, mode1); + return 0; +} + +static int ak4535_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, AK4535_DAC); + + if (!mute) + snd_soc_component_write(component, AK4535_DAC, mute_reg & ~0x20); + else + snd_soc_component_write(component, AK4535_DAC, mute_reg | 0x20); + return 0; +} + +static int ak4535_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_update_bits(component, AK4535_DAC, 0x20, 0); + break; + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, AK4535_DAC, 0x20, 0x20); + break; + case SND_SOC_BIAS_STANDBY: + snd_soc_component_update_bits(component, AK4535_PM1, 0x80, 0x80); + snd_soc_component_update_bits(component, AK4535_PM2, 0x80, 0); + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, AK4535_PM1, 0x80, 0); + break; + } + return 0; +} + +#define AK4535_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +static const struct snd_soc_dai_ops ak4535_dai_ops = { + .hw_params = ak4535_hw_params, + .set_fmt = ak4535_set_dai_fmt, + .mute_stream = ak4535_mute, + .set_sysclk = ak4535_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver ak4535_dai = { + .name = "ak4535-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AK4535_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AK4535_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = &ak4535_dai_ops, +}; + +static int ak4535_resume(struct snd_soc_component *component) +{ + snd_soc_component_cache_sync(component); + return 0; +} + +static const struct regmap_config ak4535_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AK4535_STATUS, + .volatile_reg = ak4535_volatile, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ak4535_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4535_reg_defaults), +}; + +static const struct snd_soc_component_driver soc_component_dev_ak4535 = { + .resume = ak4535_resume, + .set_bias_level = ak4535_set_bias_level, + .controls = ak4535_snd_controls, + .num_controls = ARRAY_SIZE(ak4535_snd_controls), + .dapm_widgets = ak4535_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets), + .dapm_routes = ak4535_audio_map, + .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int ak4535_i2c_probe(struct i2c_client *i2c) +{ + struct ak4535_priv *ak4535; + int ret; + + ak4535 = devm_kzalloc(&i2c->dev, sizeof(struct ak4535_priv), + GFP_KERNEL); + if (ak4535 == NULL) + return -ENOMEM; + + ak4535->regmap = devm_regmap_init_i2c(i2c, &ak4535_regmap); + if (IS_ERR(ak4535->regmap)) { + ret = PTR_ERR(ak4535->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, ak4535); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_ak4535, &ak4535_dai, 1); + + return ret; +} + +static const struct i2c_device_id ak4535_i2c_id[] = { + { "ak4535", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id); + +static struct i2c_driver ak4535_i2c_driver = { + .driver = { + .name = "ak4535", + }, + .probe = ak4535_i2c_probe, + .id_table = ak4535_i2c_id, +}; + +module_i2c_driver(ak4535_i2c_driver); + +MODULE_DESCRIPTION("Soc AK4535 driver"); +MODULE_AUTHOR("Richard Purdie"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h new file mode 100644 index 0000000000..978caf5214 --- /dev/null +++ b/sound/soc/codecs/ak4535.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ak4535.h -- AK4535 Soc Audio driver + * + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * Based on wm8753.h + */ + +#ifndef _AK4535_H +#define _AK4535_H + +/* AK4535 register space */ + +#define AK4535_PM1 0x0 +#define AK4535_PM2 0x1 +#define AK4535_SIG1 0x2 +#define AK4535_SIG2 0x3 +#define AK4535_MODE1 0x4 +#define AK4535_MODE2 0x5 +#define AK4535_DAC 0x6 +#define AK4535_MIC 0x7 +#define AK4535_TIMER 0x8 +#define AK4535_ALC1 0x9 +#define AK4535_ALC2 0xa +#define AK4535_PGA 0xb +#define AK4535_LATT 0xc +#define AK4535_RATT 0xd +#define AK4535_VOL 0xe +#define AK4535_STATUS 0xf + +#endif diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c new file mode 100644 index 0000000000..b9607de5a1 --- /dev/null +++ b/sound/soc/codecs/ak4554.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +// ak4554.c +// +// Copyright (C) 2013 Renesas Solutions Corp. +// Kuninori Morimoto + +#include +#include + +/* + * ak4554 is very simple DA/AD converter which has no setting register. + * + * CAUTION + * + * ak4554 playback format is SND_SOC_DAIFMT_RIGHT_J, + * and, capture format is SND_SOC_DAIFMT_LEFT_J + * on same bit clock, LR clock. + * But, this driver doesn't have snd_soc_dai_ops :: set_fmt + * + * CPU/Codec DAI image + * + * CPU-DAI1 (plaback only fmt = RIGHT_J) --+-- ak4554 + * | + * CPU-DAI2 (capture only fmt = LEFT_J) ---+ + */ + +static const struct snd_soc_dapm_widget ak4554_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), + +SND_SOC_DAPM_OUTPUT("AOUTL"), +SND_SOC_DAPM_OUTPUT("AOUTR"), +}; + +static const struct snd_soc_dapm_route ak4554_dapm_routes[] = { + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, + + { "AOUTL", NULL, "Playback" }, + { "AOUTR", NULL, "Playback" }, +}; + +static struct snd_soc_dai_driver ak4554_dai = { + .name = "ak4554-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .symmetric_rate = 1, +}; + +static const struct snd_soc_component_driver soc_component_dev_ak4554 = { + .dapm_widgets = ak4554_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets), + .dapm_routes = ak4554_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int ak4554_soc_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_ak4554, + &ak4554_dai, 1); +} + +static const struct of_device_id ak4554_of_match[] = { + { .compatible = "asahi-kasei,ak4554" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4554_of_match); + +static struct platform_driver ak4554_driver = { + .driver = { + .name = "ak4554-adc-dac", + .of_match_table = ak4554_of_match, + }, + .probe = ak4554_soc_probe, +}; +module_platform_driver(ak4554_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SoC AK4554 driver"); +MODULE_AUTHOR("Kuninori Morimoto "); diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c new file mode 100644 index 0000000000..619a817ee9 --- /dev/null +++ b/sound/soc/codecs/ak4613.c @@ -0,0 +1,927 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ak4613.c -- Asahi Kasei ALSA Soc Audio driver +// +// Copyright (C) 2015 Renesas Electronics Corporation +// Kuninori Morimoto +// +// Based on ak4642.c by Kuninori Morimoto +// Based on wm8731.c by Richard Purdie +// Based on ak4535.c by Richard Purdie +// Based on wm8753.c by Liam Girdwood + +/* + * +-------+ + * |AK4613 | + * SDTO1 <-| | + * | | + * SDTI1 ->| | + * SDTI2 ->| | + * SDTI3 ->| | + * +-------+ + * + * +---+ + * clk | |___________________________________________... + * + * [TDM512] + * SDTO1 [L1][R1][L2][R2] + * SDTI1 [L1][R1][L2][R2][L3][R3][L4][R4][L5][R5][L6][R6] + * + * [TDM256] + * SDTO1 [L1][R1][L2][R2] + * SDTI1 [L1][R1][L2][R2][L3][R3][L4][R4] + * SDTI2 [L5][R5][L6][R6] + * + * [TDM128] + * SDTO1 [L1][R1][L2][R2] + * SDTI1 [L1][R1][L2][R2] + * SDTI2 [L3][R3][L4][R4] + * SDTI3 [L5][R5][L6][R6] + * + * [STEREO] + * Playback 2ch : SDTI1 + * Capture 2ch : SDTO1 + * + * [TDM512] + * Playback 12ch : SDTI1 + * Capture 4ch : SDTO1 + * + * [TDM256] + * Playback 12ch : SDTI1 + SDTI2 + * Playback 8ch : SDTI1 + * Capture 4ch : SDTO1 + * + * [TDM128] + * Playback 12ch : SDTI1 + SDTI2 + SDTI3 + * Playback 8ch : SDTI1 + SDTI2 + * Playback 4ch : SDTI1 + * Capture 4ch : SDTO1 + * + * + * !!! NOTE !!! + * + * Renesas is the only user of ak4613 on upstream so far, + * but the chip connection is like below. + * Thus, Renesas can't test all connection case. + * Tested TDM is very limited. + * + * +-----+ +-----------+ + * | SoC | | AK4613 | + * | |<-----|SDTO1 IN1|<-- Mic + * | | | IN2| + * | | | | + * | |----->|SDTI1 OUT1|--> Headphone + * +-----+ |SDTI2 OUT2| + * |SDTI3 OUT3| + * | OUT4| + * | OUT5| + * | OUT6| + * +-----------+ + * + * Renesas SoC can handle [2, 6,8] channels. + * Ak4613 can handle [2,4, 8,12] channels. + * + * Because of above HW connection and available channels number, + * Renesas could test are ... + * + * [STEREO] Playback 2ch : SDTI1 + * Capture 2ch : SDTO1 + * [TDM256] Playback 8ch : SDTI1 (*) + * + * (*) it used 8ch data between SoC <-> AK4613 on TDM256 mode, + * but could confirm is only first 2ch because only 1 + * Headphone is connected. + * + * see + * AK4613_ENABLE_TDM_TEST + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PW_MGMT1 0x00 /* Power Management 1 */ +#define PW_MGMT2 0x01 /* Power Management 2 */ +#define PW_MGMT3 0x02 /* Power Management 3 */ +#define CTRL1 0x03 /* Control 1 */ +#define CTRL2 0x04 /* Control 2 */ +#define DEMP1 0x05 /* De-emphasis1 */ +#define DEMP2 0x06 /* De-emphasis2 */ +#define OFD 0x07 /* Overflow Detect */ +#define ZRD 0x08 /* Zero Detect */ +#define ICTRL 0x09 /* Input Control */ +#define OCTRL 0x0a /* Output Control */ +#define LOUT1 0x0b /* LOUT1 Volume Control */ +#define ROUT1 0x0c /* ROUT1 Volume Control */ +#define LOUT2 0x0d /* LOUT2 Volume Control */ +#define ROUT2 0x0e /* ROUT2 Volume Control */ +#define LOUT3 0x0f /* LOUT3 Volume Control */ +#define ROUT3 0x10 /* ROUT3 Volume Control */ +#define LOUT4 0x11 /* LOUT4 Volume Control */ +#define ROUT4 0x12 /* ROUT4 Volume Control */ +#define LOUT5 0x13 /* LOUT5 Volume Control */ +#define ROUT5 0x14 /* ROUT5 Volume Control */ +#define LOUT6 0x15 /* LOUT6 Volume Control */ +#define ROUT6 0x16 /* ROUT6 Volume Control */ + +/* PW_MGMT1 */ +#define RSTN BIT(0) +#define PMDAC BIT(1) +#define PMADC BIT(2) +#define PMVR BIT(3) + +/* PW_MGMT2 */ +#define PMAD_ALL 0x7 + +/* PW_MGMT3 */ +#define PMDA_ALL 0x3f + +/* CTRL1 */ +#define DIF0 BIT(3) +#define DIF1 BIT(4) +#define DIF2 BIT(5) +#define TDM0 BIT(6) +#define TDM1 BIT(7) +#define NO_FMT (0xff) +#define FMT_MASK (0xf8) + +/* CTRL2 */ +#define DFS_MASK (3 << 2) +#define DFS_NORMAL_SPEED (0 << 2) +#define DFS_DOUBLE_SPEED (1 << 2) +#define DFS_QUAD_SPEED (2 << 2) + +/* ICTRL */ +#define ICTRL_MASK (0x3) + +/* OCTRL */ +#define OCTRL_MASK (0x3F) + +/* + * configs + * + * 0x000000BA + * + * B : AK4613_CONFIG_SDTI_x + * A : AK4613_CONFIG_MODE_x + */ +#define AK4613_CONFIG_SET(priv, x) priv->configs |= AK4613_CONFIG_##x +#define AK4613_CONFIG_GET(priv, x) (priv->configs & AK4613_CONFIG_##x##_MASK) + +/* + * AK4613_CONFIG_SDTI_x + * + * It indicates how many SDTIx is connected. + */ +#define AK4613_CONFIG_SDTI_MASK (0xF << 4) +#define AK4613_CONFIG_SDTI(x) (((x) & 0xF) << 4) +#define AK4613_CONFIG_SDTI_set(priv, x) AK4613_CONFIG_SET(priv, SDTI(x)) +#define AK4613_CONFIG_SDTI_get(priv) ((AK4613_CONFIG_GET(priv, SDTI) >> 4) & 0xF) + +/* + * AK4613_CONFIG_MODE_x + * + * Same as Ctrl1 :: TDM1/TDM0 + * No shift is requested + * see + * AK4613_CTRL1_TO_MODE() + * Table 11/12/13/14 + */ +#define AK4613_CONFIG_MODE_MASK (0xF) +#define AK4613_CONFIG_MODE_STEREO (0x0) +#define AK4613_CONFIG_MODE_TDM512 (0x1) +#define AK4613_CONFIG_MODE_TDM256 (0x2) +#define AK4613_CONFIG_MODE_TDM128 (0x3) + +/* + * !!!! FIXME !!!! + * + * Because of testable HW limitation, TDM256 8ch TDM was only tested. + * This driver uses AK4613_ENABLE_TDM_TEST instead of new DT property so far. + * Don't hesitate to update driver, you don't need to care compatible + * with Renesas. + * + * #define AK4613_ENABLE_TDM_TEST + */ + +struct ak4613_interface { + unsigned int width; + unsigned int fmt; + u8 dif; +}; + +struct ak4613_priv { + struct mutex lock; + struct snd_pcm_hw_constraint_list constraint_rates; + struct snd_pcm_hw_constraint_list constraint_channels; + struct work_struct dummy_write_work; + struct snd_soc_component *component; + unsigned int rate; + unsigned int sysclk; + + unsigned int fmt; + unsigned int configs; + int cnt; + u8 ctrl1; + u8 oc; + u8 ic; +}; + +/* + * Playback Volume + * + * max : 0x00 : 0 dB + * ( 0.5 dB step ) + * min : 0xFE : -127.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1); + +static const struct snd_kcontrol_new ak4613_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5, + 0, 0xFF, 1, out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6, + 0, 0xFF, 1, out_tlv), +}; + +static const struct reg_default ak4613_reg[] = { + { 0x0, 0x0f }, { 0x1, 0x07 }, { 0x2, 0x3f }, { 0x3, 0x20 }, + { 0x4, 0x20 }, { 0x5, 0x55 }, { 0x6, 0x05 }, { 0x7, 0x07 }, + { 0x8, 0x0f }, { 0x9, 0x07 }, { 0xa, 0x3f }, { 0xb, 0x00 }, + { 0xc, 0x00 }, { 0xd, 0x00 }, { 0xe, 0x00 }, { 0xf, 0x00 }, + { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 }, + { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 }, +}; + +/* + * CTRL1 register + * see + * Table 11/12/13/14 + */ +#define AUDIO_IFACE(_dif, _width, _fmt) \ + { \ + .dif = _dif, \ + .width = _width, \ + .fmt = SND_SOC_DAIFMT_##_fmt,\ + } +static const struct ak4613_interface ak4613_iface[] = { + /* It doesn't support asymmetric format */ + + AUDIO_IFACE(0x03, 24, LEFT_J), + AUDIO_IFACE(0x04, 24, I2S), +}; +#define AK4613_CTRL1_TO_MODE(priv) ((priv)->ctrl1 >> 6) /* AK4613_CONFIG_MODE_x */ + +static const struct regmap_config ak4613_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x16, + .reg_defaults = ak4613_reg, + .num_reg_defaults = ARRAY_SIZE(ak4613_reg), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct of_device_id ak4613_of_match[] = { + { .compatible = "asahi-kasei,ak4613", .data = &ak4613_regmap_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4613_of_match); + +static const struct i2c_device_id ak4613_i2c_id[] = { + { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id); + +static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = { + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("LOUT3"), + SND_SOC_DAPM_OUTPUT("LOUT4"), + SND_SOC_DAPM_OUTPUT("LOUT5"), + SND_SOC_DAPM_OUTPUT("LOUT6"), + + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("ROUT3"), + SND_SOC_DAPM_OUTPUT("ROUT4"), + SND_SOC_DAPM_OUTPUT("ROUT5"), + SND_SOC_DAPM_OUTPUT("ROUT6"), + + /* Inputs */ + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("RIN2"), + + /* DAC */ + SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0), + SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0), + SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0), + SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0), + SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0), + SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0), + SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0), +}; + +static const struct snd_soc_dapm_route ak4613_intercon[] = { + {"LOUT1", NULL, "DAC1"}, + {"LOUT2", NULL, "DAC2"}, + {"LOUT3", NULL, "DAC3"}, + {"LOUT4", NULL, "DAC4"}, + {"LOUT5", NULL, "DAC5"}, + {"LOUT6", NULL, "DAC6"}, + + {"ROUT1", NULL, "DAC1"}, + {"ROUT2", NULL, "DAC2"}, + {"ROUT3", NULL, "DAC3"}, + {"ROUT4", NULL, "DAC4"}, + {"ROUT5", NULL, "DAC5"}, + {"ROUT6", NULL, "DAC6"}, + + {"DAC1", NULL, "Playback"}, + {"DAC2", NULL, "Playback"}, + {"DAC3", NULL, "Playback"}, + {"DAC4", NULL, "Playback"}, + {"DAC5", NULL, "Playback"}, + {"DAC6", NULL, "Playback"}, + + {"Capture", NULL, "ADC1"}, + {"Capture", NULL, "ADC2"}, + + {"ADC1", NULL, "LIN1"}, + {"ADC2", NULL, "LIN2"}, + + {"ADC1", NULL, "RIN1"}, + {"ADC2", NULL, "RIN2"}, +}; + +static void ak4613_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + + mutex_lock(&priv->lock); + priv->cnt--; + if (priv->cnt < 0) { + dev_err(dev, "unexpected counter error\n"); + priv->cnt = 0; + } + if (!priv->cnt) + priv->ctrl1 = 0; + mutex_unlock(&priv->lock); +} + +static void ak4613_hw_constraints(struct ak4613_priv *priv, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + static const unsigned int ak4613_rates[] = { + 32000, + 44100, + 48000, + 64000, + 88200, + 96000, + 176400, + 192000, + }; +#define AK4613_CHANNEL_2 0 +#define AK4613_CHANNEL_4 1 +#define AK4613_CHANNEL_8 2 +#define AK4613_CHANNEL_12 3 +#define AK4613_CHANNEL_NONE -1 + static const unsigned int ak4613_channels[] = { + [AK4613_CHANNEL_2] = 2, + [AK4613_CHANNEL_4] = 4, + [AK4613_CHANNEL_8] = 8, + [AK4613_CHANNEL_12] = 12, + }; +#define MODE_MAX 4 +#define SDTx_MAX 4 +#define MASK(x) (1 << AK4613_CHANNEL_##x) + static const int mask_list[MODE_MAX][SDTx_MAX] = { + /* SDTO SDTIx1 SDTIx2 SDTIx3 */ + [AK4613_CONFIG_MODE_STEREO] = { MASK(2), MASK(2), MASK(2), MASK(2)}, + [AK4613_CONFIG_MODE_TDM512] = { MASK(4), MASK(12), MASK(12), MASK(12)}, + [AK4613_CONFIG_MODE_TDM256] = { MASK(4), MASK(8), MASK(8)|MASK(12), MASK(8)|MASK(12)}, + [AK4613_CONFIG_MODE_TDM128] = { MASK(4), MASK(4), MASK(4)|MASK(8), MASK(4)|MASK(8)|MASK(12)}, + }; + struct snd_pcm_hw_constraint_list *constraint; + unsigned int mask; + unsigned int mode; + unsigned int fs; + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + int sdti_num; + int i; + + constraint = &priv->constraint_rates; + constraint->list = ak4613_rates; + constraint->mask = 0; + constraint->count = 0; + + /* + * Slave Mode + * Normal: [32kHz, 48kHz] : 256fs,384fs or 512fs + * Double: [64kHz, 96kHz] : 256fs + * Quad : [128kHz,192kHz]: 128fs + * + * Master mode + * Normal: [32kHz, 48kHz] : 256fs or 512fs + * Double: [64kHz, 96kHz] : 256fs + * Quad : [128kHz,192kHz]: 128fs + */ + for (i = 0; i < ARRAY_SIZE(ak4613_rates); i++) { + /* minimum fs on each range */ + fs = (ak4613_rates[i] <= 96000) ? 256 : 128; + + if (priv->sysclk >= ak4613_rates[i] * fs) + constraint->count = i + 1; + } + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, constraint); + + + sdti_num = AK4613_CONFIG_SDTI_get(priv); + if (WARN_ON(sdti_num >= SDTx_MAX)) + return; + + if (priv->cnt) { + /* + * If it was already working, + * the constraint is same as working mode. + */ + mode = AK4613_CTRL1_TO_MODE(priv); + mask = 0; /* no default */ + } else { + /* + * It is not yet working, + * the constraint is based on board configs. + * STEREO mask is default + */ + mode = AK4613_CONFIG_GET(priv, MODE); + mask = mask_list[AK4613_CONFIG_MODE_STEREO][is_play * sdti_num]; + } + + if (WARN_ON(mode >= MODE_MAX)) + return; + + /* add each mode mask */ + mask |= mask_list[mode][is_play * sdti_num]; + + constraint = &priv->constraint_channels; + constraint->list = ak4613_channels; + constraint->mask = mask; + constraint->count = sizeof(ak4613_channels); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, constraint); +} + +static int ak4613_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + + mutex_lock(&priv->lock); + ak4613_hw_constraints(priv, substream); + priv->cnt++; + mutex_unlock(&priv->lock); + + return 0; +} + +static int ak4613_dai_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + + priv->sysclk = freq; + + return 0; +} + +static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct snd_soc_component *component = dai->component; + struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int fmt; + + fmt = format & SND_SOC_DAIFMT_FORMAT_MASK; + switch (fmt) { + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_I2S: + priv->fmt = fmt; + break; + default: + return -EINVAL; + } + + fmt = format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; + switch (fmt) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + /* + * SUPPORTME + * + * "clock provider" is not yet supperted + */ + return -EINVAL; + } + + return 0; +} + +static int ak4613_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + unsigned int width = params_width(params); + unsigned int fmt = priv->fmt; + unsigned int rate; + int i, ret; + u8 ctrl2; + + rate = params_rate(params); + switch (rate) { + case 32000: + case 44100: + case 48000: + ctrl2 = DFS_NORMAL_SPEED; + break; + case 64000: + case 88200: + case 96000: + ctrl2 = DFS_DOUBLE_SPEED; + break; + case 176400: + case 192000: + ctrl2 = DFS_QUAD_SPEED; + break; + default: + return -EINVAL; + } + priv->rate = rate; + + /* + * FIXME + * + * It doesn't have full TDM suppert yet + */ + ret = -EINVAL; + + mutex_lock(&priv->lock); + if (priv->cnt > 1) { + /* + * If it was already working, use current priv->ctrl1 + */ + ret = 0; + } else { + /* + * It is not yet working, + */ + unsigned int channel = params_channels(params); + u8 tdm; + + /* STEREO or TDM */ + if (channel == 2) + tdm = AK4613_CONFIG_MODE_STEREO; + else + tdm = AK4613_CONFIG_GET(priv, MODE); + + for (i = ARRAY_SIZE(ak4613_iface) - 1; i >= 0; i--) { + const struct ak4613_interface *iface = ak4613_iface + i; + + if ((iface->fmt == fmt) && (iface->width == width)) { + /* + * Ctrl1 + * | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | + * |TDM1|TDM0|DIF2|DIF1|DIF0|ATS1|ATS0|SMUTE| + * < tdm > < iface->dif > + */ + priv->ctrl1 = (tdm << 6) | (iface->dif << 3); + ret = 0; + break; + } + } + } + mutex_unlock(&priv->lock); + + if (ret < 0) + goto hw_params_end; + + snd_soc_component_update_bits(component, CTRL1, FMT_MASK, priv->ctrl1); + snd_soc_component_update_bits(component, CTRL2, DFS_MASK, ctrl2); + + snd_soc_component_update_bits(component, ICTRL, ICTRL_MASK, priv->ic); + snd_soc_component_update_bits(component, OCTRL, OCTRL_MASK, priv->oc); + +hw_params_end: + if (ret < 0) + dev_warn(dev, "unsupported data width/format combination\n"); + + return ret; +} + +static int ak4613_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + u8 mgmt1 = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + mgmt1 |= RSTN; + fallthrough; + case SND_SOC_BIAS_PREPARE: + mgmt1 |= PMADC | PMDAC; + fallthrough; + case SND_SOC_BIAS_STANDBY: + mgmt1 |= PMVR; + fallthrough; + case SND_SOC_BIAS_OFF: + default: + break; + } + + snd_soc_component_write(component, PW_MGMT1, mgmt1); + + return 0; +} + +static void ak4613_dummy_write(struct work_struct *work) +{ + struct ak4613_priv *priv = container_of(work, + struct ak4613_priv, + dummy_write_work); + struct snd_soc_component *component = priv->component; + unsigned int mgmt1; + unsigned int mgmt3; + + /* + * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks + * + * Note + * + * To avoid extra delay, we want to avoid preemption here, + * but we can't. Because it uses I2C access which is using IRQ + * and sleep. Thus, delay might be more than 5 LR clocks + * see also + * ak4613_dai_trigger() + */ + udelay(5000000 / priv->rate); + + mgmt1 = snd_soc_component_read(component, PW_MGMT1); + mgmt3 = snd_soc_component_read(component, PW_MGMT3); + + snd_soc_component_write(component, PW_MGMT1, mgmt1); + snd_soc_component_write(component, PW_MGMT3, mgmt3); +} + +static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4613_priv *priv = snd_soc_component_get_drvdata(component); + + /* + * FIXME + * + * PW_MGMT1 / PW_MGMT3 needs dummy write at least after 5 LR clocks + * from Power Down Release. Otherwise, Playback volume will be 0dB. + * To avoid complex multiple delay/dummy_write method from + * ak4613_set_bias_level() / SND_SOC_DAPM_DAC_E("DACx", ...), + * call it once here. + * + * But, unfortunately, we can't "write" here because here is atomic + * context (It uses I2C access for writing). + * Thus, use schedule_work() to switching to normal context + * immediately. + * + * Note + * + * Calling ak4613_dummy_write() function might be delayed. + * In such case, ak4613 volume might be temporarily 0dB when + * beggining of playback. + * see also + * ak4613_dummy_write() + */ + + if ((cmd != SNDRV_PCM_TRIGGER_START) && + (cmd != SNDRV_PCM_TRIGGER_RESUME)) + return 0; + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + priv->component = component; + schedule_work(&priv->dummy_write_work); + + return 0; +} + +/* + * Select below from Sound Card, not Auto + * SND_SOC_DAIFMT_CBC_CFC + * SND_SOC_DAIFMT_CBP_CFP + */ +static u64 ak4613_dai_formats = + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J; + +static const struct snd_soc_dai_ops ak4613_dai_ops = { + .startup = ak4613_dai_startup, + .shutdown = ak4613_dai_shutdown, + .set_sysclk = ak4613_dai_set_sysclk, + .set_fmt = ak4613_dai_set_fmt, + .trigger = ak4613_dai_trigger, + .hw_params = ak4613_dai_hw_params, + .auto_selectable_formats = &ak4613_dai_formats, + .num_auto_selectable_formats = 1, +}; + +#define AK4613_PCM_RATE (SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_64000 |\ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) +#define AK4613_PCM_FMTBIT (SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver ak4613_dai = { + .name = "ak4613-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 12, + .rates = AK4613_PCM_RATE, + .formats = AK4613_PCM_FMTBIT, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 4, + .rates = AK4613_PCM_RATE, + .formats = AK4613_PCM_FMTBIT, + }, + .ops = &ak4613_dai_ops, + .symmetric_rate = 1, +}; + +static int ak4613_suspend(struct snd_soc_component *component) +{ + struct regmap *regmap = dev_get_regmap(component->dev, NULL); + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + return 0; +} + +static int ak4613_resume(struct snd_soc_component *component) +{ + struct regmap *regmap = dev_get_regmap(component->dev, NULL); + + regcache_cache_only(regmap, false); + return regcache_sync(regmap); +} + +static const struct snd_soc_component_driver soc_component_dev_ak4613 = { + .suspend = ak4613_suspend, + .resume = ak4613_resume, + .set_bias_level = ak4613_set_bias_level, + .controls = ak4613_snd_controls, + .num_controls = ARRAY_SIZE(ak4613_snd_controls), + .dapm_widgets = ak4613_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets), + .dapm_routes = ak4613_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4613_intercon), + .idle_bias_on = 1, + .endianness = 1, +}; + +static void ak4613_parse_of(struct ak4613_priv *priv, + struct device *dev) +{ + struct device_node *np = dev->of_node; + char prop[32]; + int sdti_num; + int i; + + /* Input 1 - 2 */ + for (i = 0; i < 2; i++) { + snprintf(prop, sizeof(prop), "asahi-kasei,in%d-single-end", i + 1); + if (!of_get_property(np, prop, NULL)) + priv->ic |= 1 << i; + } + + /* Output 1 - 6 */ + for (i = 0; i < 6; i++) { + snprintf(prop, sizeof(prop), "asahi-kasei,out%d-single-end", i + 1); + if (!of_get_property(np, prop, NULL)) + priv->oc |= 1 << i; + } + + /* + * enable TDM256 test + * + * !!! FIXME !!! + * + * It should be configured by DT or other way + * if it was full supported. + * But it is using ifdef style for now for test + * purpose. + */ +#if defined(AK4613_ENABLE_TDM_TEST) + AK4613_CONFIG_SET(priv, MODE_TDM256); +#endif + + /* + * connected STDI + * TDM support is assuming it is probed via Audio-Graph-Card style here. + * Default is SDTIx1 if it was probed via Simple-Audio-Card for now. + */ + sdti_num = of_graph_get_endpoint_count(np); + if ((sdti_num >= SDTx_MAX) || (sdti_num < 1)) + sdti_num = 1; + + AK4613_CONFIG_SDTI_set(priv, sdti_num); +} + +static int ak4613_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + const struct regmap_config *regmap_cfg; + struct regmap *regmap; + struct ak4613_priv *priv; + + regmap_cfg = i2c_get_match_data(i2c); + if (!regmap_cfg) + return -EINVAL; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ak4613_parse_of(priv, dev); + + priv->ctrl1 = 0; + priv->cnt = 0; + priv->sysclk = 0; + INIT_WORK(&priv->dummy_write_work, ak4613_dummy_write); + + mutex_init(&priv->lock); + + i2c_set_clientdata(i2c, priv); + + regmap = devm_regmap_init_i2c(i2c, regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return devm_snd_soc_register_component(dev, &soc_component_dev_ak4613, + &ak4613_dai, 1); +} + +static struct i2c_driver ak4613_i2c_driver = { + .driver = { + .name = "ak4613-codec", + .of_match_table = ak4613_of_match, + }, + .probe = ak4613_i2c_probe, + .id_table = ak4613_i2c_id, +}; + +module_i2c_driver(ak4613_i2c_driver); + +MODULE_DESCRIPTION("Soc AK4613 driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c new file mode 100644 index 0000000000..5b7df2f0dd --- /dev/null +++ b/sound/soc/codecs/ak4641.c @@ -0,0 +1,640 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ak4641.c -- AK4641 ALSA Soc Audio driver + * + * Copyright (C) 2008 Harald Welte + * Copyright (C) 2011 Dmitry Artamonow + * + * Based on ak4535.c by Richard Purdie + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AK4641 register space */ +#define AK4641_PM1 0x00 +#define AK4641_PM2 0x01 +#define AK4641_SIG1 0x02 +#define AK4641_SIG2 0x03 +#define AK4641_MODE1 0x04 +#define AK4641_MODE2 0x05 +#define AK4641_DAC 0x06 +#define AK4641_MIC 0x07 +#define AK4641_TIMER 0x08 +#define AK4641_ALC1 0x09 +#define AK4641_ALC2 0x0a +#define AK4641_PGA 0x0b +#define AK4641_LATT 0x0c +#define AK4641_RATT 0x0d +#define AK4641_VOL 0x0e +#define AK4641_STATUS 0x0f +#define AK4641_EQLO 0x10 +#define AK4641_EQMID 0x11 +#define AK4641_EQHI 0x12 +#define AK4641_BTIF 0x13 + +/* codec private data */ +struct ak4641_priv { + struct regmap *regmap; + unsigned int sysclk; + int deemph; + int playback_fs; +}; + +/* + * ak4641 register cache + */ +static const struct reg_default ak4641_reg_defaults[] = { + { 0, 0x00 }, { 1, 0x80 }, { 2, 0x00 }, { 3, 0x80 }, + { 4, 0x02 }, { 5, 0x00 }, { 6, 0x11 }, { 7, 0x05 }, + { 8, 0x00 }, { 9, 0x00 }, { 10, 0x36 }, { 11, 0x10 }, + { 12, 0x00 }, { 13, 0x00 }, { 14, 0x57 }, { 15, 0x00 }, + { 16, 0x88 }, { 17, 0x88 }, { 18, 0x08 }, { 19, 0x08 } +}; + +static const int deemph_settings[] = {44100, 0, 48000, 32000}; + +static int ak4641_set_deemph(struct snd_soc_component *component) +{ + struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); + int i, best = 0; + + for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) { + /* if deemphasis is on, select the nearest available rate */ + if (ak4641->deemph && deemph_settings[i] != 0 && + abs(deemph_settings[i] - ak4641->playback_fs) < + abs(deemph_settings[best] - ak4641->playback_fs)) + best = i; + + if (!ak4641->deemph && deemph_settings[i] == 0) + best = i; + } + + dev_dbg(component->dev, "Set deemphasis %d\n", best); + + return snd_soc_component_update_bits(component, AK4641_DAC, 0x3, best); +} + +static int ak4641_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); + int deemph = ucontrol->value.integer.value[0]; + + if (deemph > 1) + return -EINVAL; + + ak4641->deemph = deemph; + + return ak4641_set_deemph(component); +} + +static int ak4641_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = ak4641->deemph; + return 0; +}; + +static const char *ak4641_mono_out[] = {"(L + R)/2", "Hi-Z"}; +static const char *ak4641_hp_out[] = {"Stereo", "Mono"}; +static const char *ak4641_mic_select[] = {"Internal", "External"}; +static const char *ak4641_mic_or_dac[] = {"Microphone", "Voice DAC"}; + + +static const DECLARE_TLV_DB_SCALE(mono_gain_tlv, -1700, 2300, 0); +static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 2000, 0); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1050, 150, 0); +static const DECLARE_TLV_DB_SCALE(master_tlv, -12750, 50, 0); +static const DECLARE_TLV_DB_SCALE(mic_stereo_sidetone_tlv, -2700, 300, 0); +static const DECLARE_TLV_DB_SCALE(mic_mono_sidetone_tlv, -400, 400, 0); +static const DECLARE_TLV_DB_SCALE(capture_tlv, -800, 50, 0); +static const DECLARE_TLV_DB_SCALE(alc_tlv, -800, 50, 0); +static const DECLARE_TLV_DB_SCALE(aux_in_tlv, -2100, 300, 0); + + +static SOC_ENUM_SINGLE_DECL(ak4641_mono_out_enum, + AK4641_SIG1, 6, ak4641_mono_out); +static SOC_ENUM_SINGLE_DECL(ak4641_hp_out_enum, + AK4641_MODE2, 2, ak4641_hp_out); +static SOC_ENUM_SINGLE_DECL(ak4641_mic_select_enum, + AK4641_MIC, 1, ak4641_mic_select); +static SOC_ENUM_SINGLE_DECL(ak4641_mic_or_dac_enum, + AK4641_BTIF, 4, ak4641_mic_or_dac); + +static const struct snd_kcontrol_new ak4641_snd_controls[] = { + SOC_ENUM("Mono 1 Output", ak4641_mono_out_enum), + SOC_SINGLE_TLV("Mono 1 Gain Volume", AK4641_SIG1, 7, 1, 1, + mono_gain_tlv), + SOC_ENUM("Headphone Output", ak4641_hp_out_enum), + SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, + ak4641_get_deemph, ak4641_put_deemph), + + SOC_SINGLE_TLV("Mic Boost Volume", AK4641_MIC, 0, 1, 0, mic_boost_tlv), + + SOC_SINGLE("ALC Operation Time", AK4641_TIMER, 0, 3, 0), + SOC_SINGLE("ALC Recovery Time", AK4641_TIMER, 2, 3, 0), + SOC_SINGLE("ALC ZC Time", AK4641_TIMER, 4, 3, 0), + + SOC_SINGLE("ALC 1 Switch", AK4641_ALC1, 5, 1, 0), + + SOC_SINGLE_TLV("ALC Volume", AK4641_ALC2, 0, 71, 0, alc_tlv), + SOC_SINGLE("Left Out Enable Switch", AK4641_SIG2, 1, 1, 0), + SOC_SINGLE("Right Out Enable Switch", AK4641_SIG2, 0, 1, 0), + + SOC_SINGLE_TLV("Capture Volume", AK4641_PGA, 0, 71, 0, capture_tlv), + + SOC_DOUBLE_R_TLV("Master Playback Volume", AK4641_LATT, + AK4641_RATT, 0, 255, 1, master_tlv), + + SOC_SINGLE_TLV("AUX In Volume", AK4641_VOL, 0, 15, 0, aux_in_tlv), + + SOC_SINGLE("Equalizer Switch", AK4641_DAC, 2, 1, 0), + SOC_SINGLE_TLV("EQ1 100 Hz Volume", AK4641_EQLO, 0, 15, 1, eq_tlv), + SOC_SINGLE_TLV("EQ2 250 Hz Volume", AK4641_EQLO, 4, 15, 1, eq_tlv), + SOC_SINGLE_TLV("EQ3 1 kHz Volume", AK4641_EQMID, 0, 15, 1, eq_tlv), + SOC_SINGLE_TLV("EQ4 3.5 kHz Volume", AK4641_EQMID, 4, 15, 1, eq_tlv), + SOC_SINGLE_TLV("EQ5 10 kHz Volume", AK4641_EQHI, 0, 15, 1, eq_tlv), +}; + +/* Mono 1 Mixer */ +static const struct snd_kcontrol_new ak4641_mono1_mixer_controls[] = { + SOC_DAPM_SINGLE_TLV("Mic Mono Sidetone Volume", AK4641_VOL, 7, 1, 0, + mic_mono_sidetone_tlv), + SOC_DAPM_SINGLE("Mic Mono Sidetone Switch", AK4641_SIG1, 4, 1, 0), + SOC_DAPM_SINGLE("Mono Playback Switch", AK4641_SIG1, 5, 1, 0), +}; + +/* Stereo Mixer */ +static const struct snd_kcontrol_new ak4641_stereo_mixer_controls[] = { + SOC_DAPM_SINGLE_TLV("Mic Sidetone Volume", AK4641_VOL, 4, 7, 0, + mic_stereo_sidetone_tlv), + SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4641_SIG2, 4, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", AK4641_SIG2, 7, 1, 0), + SOC_DAPM_SINGLE("Aux Bypass Switch", AK4641_SIG2, 5, 1, 0), +}; + +/* Input Mixer */ +static const struct snd_kcontrol_new ak4641_input_mixer_controls[] = { + SOC_DAPM_SINGLE("Mic Capture Switch", AK4641_MIC, 2, 1, 0), + SOC_DAPM_SINGLE("Aux Capture Switch", AK4641_MIC, 5, 1, 0), +}; + +/* Mic mux */ +static const struct snd_kcontrol_new ak4641_mic_mux_control = + SOC_DAPM_ENUM("Mic Select", ak4641_mic_select_enum); + +/* Input mux */ +static const struct snd_kcontrol_new ak4641_input_mux_control = + SOC_DAPM_ENUM("Input Select", ak4641_mic_or_dac_enum); + +/* mono 2 switch */ +static const struct snd_kcontrol_new ak4641_mono2_control = + SOC_DAPM_SINGLE("Switch", AK4641_SIG1, 0, 1, 0); + +/* ak4641 dapm widgets */ +static const struct snd_soc_dapm_widget ak4641_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0, + &ak4641_stereo_mixer_controls[0], + ARRAY_SIZE(ak4641_stereo_mixer_controls)), + SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0, + &ak4641_mono1_mixer_controls[0], + ARRAY_SIZE(ak4641_mono1_mixer_controls)), + SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, + &ak4641_input_mixer_controls[0], + ARRAY_SIZE(ak4641_input_mixer_controls)), + SND_SOC_DAPM_MUX("Mic Mux", SND_SOC_NOPM, 0, 0, + &ak4641_mic_mux_control), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, + &ak4641_input_mux_control), + SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0, + &ak4641_mono2_control), + + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), + SND_SOC_DAPM_OUTPUT("MOUT1"), + SND_SOC_DAPM_OUTPUT("MOUT2"), + SND_SOC_DAPM_OUTPUT("MICOUT"), + + SND_SOC_DAPM_ADC("ADC", "HiFi Capture", AK4641_PM1, 0, 0), + SND_SOC_DAPM_PGA("Mic", AK4641_PM1, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("AUX In", AK4641_PM1, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono Out", AK4641_PM1, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Line Out", AK4641_PM1, 4, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DAC", "HiFi Playback", AK4641_PM2, 0, 0), + SND_SOC_DAPM_PGA("Mono Out 2", AK4641_PM2, 3, 0, NULL, 0), + + SND_SOC_DAPM_ADC("Voice ADC", "Voice Capture", AK4641_BTIF, 0, 0), + SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AK4641_BTIF, 1, 0), + + SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4641_MIC, 3, 0), + SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4641_MIC, 4, 0), + + SND_SOC_DAPM_INPUT("MICIN"), + SND_SOC_DAPM_INPUT("MICEXT"), + SND_SOC_DAPM_INPUT("AUX"), + SND_SOC_DAPM_INPUT("AIN"), +}; + +static const struct snd_soc_dapm_route ak4641_audio_map[] = { + /* Stereo Mixer */ + {"Stereo Mixer", "Playback Switch", "DAC"}, + {"Stereo Mixer", "Mic Sidetone Switch", "Input Mux"}, + {"Stereo Mixer", "Aux Bypass Switch", "AUX In"}, + + /* Mono 1 Mixer */ + {"Mono1 Mixer", "Mic Mono Sidetone Switch", "Input Mux"}, + {"Mono1 Mixer", "Mono Playback Switch", "DAC"}, + + /* Mic */ + {"Mic", NULL, "AIN"}, + {"Mic Mux", "Internal", "Mic Int Bias"}, + {"Mic Mux", "External", "Mic Ext Bias"}, + {"Mic Int Bias", NULL, "MICIN"}, + {"Mic Ext Bias", NULL, "MICEXT"}, + {"MICOUT", NULL, "Mic Mux"}, + + /* Input Mux */ + {"Input Mux", "Microphone", "Mic"}, + {"Input Mux", "Voice DAC", "Voice DAC"}, + + /* Line Out */ + {"LOUT", NULL, "Line Out"}, + {"ROUT", NULL, "Line Out"}, + {"Line Out", NULL, "Stereo Mixer"}, + + /* Mono 1 Out */ + {"MOUT1", NULL, "Mono Out"}, + {"Mono Out", NULL, "Mono1 Mixer"}, + + /* Mono 2 Out */ + {"MOUT2", NULL, "Mono 2 Enable"}, + {"Mono 2 Enable", "Switch", "Mono Out 2"}, + {"Mono Out 2", NULL, "Stereo Mixer"}, + + {"Voice ADC", NULL, "Mono 2 Enable"}, + + /* Aux In */ + {"AUX In", NULL, "AUX"}, + + /* ADC */ + {"ADC", NULL, "Input Mixer"}, + {"Input Mixer", "Mic Capture Switch", "Mic"}, + {"Input Mixer", "Aux Capture Switch", "AUX In"}, +}; + +static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); + + ak4641->sysclk = freq; + return 0; +} + +static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); + int rate = params_rate(params), fs = 256; + u8 mode2; + + if (rate) + fs = ak4641->sysclk / rate; + else + return -EINVAL; + + /* set fs */ + switch (fs) { + case 1024: + mode2 = (0x2 << 5); + break; + case 512: + mode2 = (0x1 << 5); + break; + case 256: + mode2 = (0x0 << 5); + break; + default: + dev_err(component->dev, "Error: unsupported fs=%d\n", fs); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AK4641_MODE2, (0x3 << 5), mode2); + + /* Update de-emphasis filter for the new rate */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ak4641->playback_fs = rate; + ak4641_set_deemph(component); + } + + return 0; +} + +static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u8 btif; + int ret; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + btif = (0x3 << 5); + break; + case SND_SOC_DAIFMT_LEFT_J: + btif = (0x2 << 5); + break; + case SND_SOC_DAIFMT_DSP_A: /* MSB after FRM */ + btif = (0x0 << 5); + break; + case SND_SOC_DAIFMT_DSP_B: /* MSB during FRM */ + btif = (0x1 << 5); + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, AK4641_BTIF, (0x3 << 5), btif); + if (ret < 0) + return ret; + + return 0; +} + +static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u8 mode1 = 0; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + mode1 = 0x02; + break; + case SND_SOC_DAIFMT_LEFT_J: + mode1 = 0x01; + break; + default: + return -EINVAL; + } + + return snd_soc_component_write(component, AK4641_MODE1, mode1); +} + +static int ak4641_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + return snd_soc_component_update_bits(component, AK4641_DAC, 0x20, mute ? 0x20 : 0); +} + +static int ak4641_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component); + struct ak4641_platform_data *pdata = component->dev->platform_data; + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + /* unmute */ + snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0); + break; + case SND_SOC_BIAS_PREPARE: + /* mute */ + snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0x20); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + if (pdata && gpio_is_valid(pdata->gpio_power)) + gpio_set_value(pdata->gpio_power, 1); + mdelay(1); + if (pdata && gpio_is_valid(pdata->gpio_npdn)) + gpio_set_value(pdata->gpio_npdn, 1); + mdelay(1); + + ret = regcache_sync(ak4641->regmap); + if (ret) { + dev_err(component->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0x80); + snd_soc_component_update_bits(component, AK4641_PM2, 0x80, 0); + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, AK4641_PM1, 0x80, 0); + if (pdata && gpio_is_valid(pdata->gpio_npdn)) + gpio_set_value(pdata->gpio_npdn, 0); + if (pdata && gpio_is_valid(pdata->gpio_power)) + gpio_set_value(pdata->gpio_power, 0); + regcache_mark_dirty(ak4641->regmap); + break; + } + return 0; +} + +#define AK4641_RATES (SNDRV_PCM_RATE_8000_48000) +#define AK4641_RATES_BT (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000) +#define AK4641_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +static const struct snd_soc_dai_ops ak4641_i2s_dai_ops = { + .hw_params = ak4641_i2s_hw_params, + .set_fmt = ak4641_i2s_set_dai_fmt, + .mute_stream = ak4641_mute, + .set_sysclk = ak4641_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops ak4641_pcm_dai_ops = { + .hw_params = NULL, /* rates are controlled by BT chip */ + .set_fmt = ak4641_pcm_set_dai_fmt, + .mute_stream = ak4641_mute, + .set_sysclk = ak4641_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver ak4641_dai[] = { +{ + .name = "ak4641-hifi", + .id = 1, + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AK4641_RATES, + .formats = AK4641_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AK4641_RATES, + .formats = AK4641_FORMATS, + }, + .ops = &ak4641_i2s_dai_ops, + .symmetric_rate = 1, +}, +{ + .name = "ak4641-voice", + .id = 1, + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = AK4641_RATES_BT, + .formats = AK4641_FORMATS, + }, + .capture = { + .stream_name = "Voice Capture", + .channels_min = 1, + .channels_max = 1, + .rates = AK4641_RATES_BT, + .formats = AK4641_FORMATS, + }, + .ops = &ak4641_pcm_dai_ops, + .symmetric_rate = 1, +}, +}; + +static const struct snd_soc_component_driver soc_component_dev_ak4641 = { + .controls = ak4641_snd_controls, + .num_controls = ARRAY_SIZE(ak4641_snd_controls), + .dapm_widgets = ak4641_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets), + .dapm_routes = ak4641_audio_map, + .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map), + .set_bias_level = ak4641_set_bias_level, + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config ak4641_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AK4641_BTIF, + .reg_defaults = ak4641_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4641_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static int ak4641_i2c_probe(struct i2c_client *i2c) +{ + struct ak4641_platform_data *pdata = i2c->dev.platform_data; + struct ak4641_priv *ak4641; + int ret; + + ak4641 = devm_kzalloc(&i2c->dev, sizeof(struct ak4641_priv), + GFP_KERNEL); + if (!ak4641) + return -ENOMEM; + + ak4641->regmap = devm_regmap_init_i2c(i2c, &ak4641_regmap); + if (IS_ERR(ak4641->regmap)) + return PTR_ERR(ak4641->regmap); + + if (pdata) { + if (gpio_is_valid(pdata->gpio_power)) { + ret = gpio_request_one(pdata->gpio_power, + GPIOF_OUT_INIT_LOW, "ak4641 power"); + if (ret) + goto err_out; + } + if (gpio_is_valid(pdata->gpio_npdn)) { + ret = gpio_request_one(pdata->gpio_npdn, + GPIOF_OUT_INIT_LOW, "ak4641 npdn"); + if (ret) + goto err_gpio; + + udelay(1); /* > 150 ns */ + gpio_set_value(pdata->gpio_npdn, 1); + } + } + + i2c_set_clientdata(i2c, ak4641); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_ak4641, + ak4641_dai, ARRAY_SIZE(ak4641_dai)); + if (ret != 0) + goto err_gpio2; + + return 0; + +err_gpio2: + if (pdata) { + if (gpio_is_valid(pdata->gpio_power)) + gpio_set_value(pdata->gpio_power, 0); + if (gpio_is_valid(pdata->gpio_npdn)) + gpio_free(pdata->gpio_npdn); + } +err_gpio: + if (pdata && gpio_is_valid(pdata->gpio_power)) + gpio_free(pdata->gpio_power); +err_out: + return ret; +} + +static void ak4641_i2c_remove(struct i2c_client *i2c) +{ + struct ak4641_platform_data *pdata = i2c->dev.platform_data; + + if (pdata) { + if (gpio_is_valid(pdata->gpio_power)) { + gpio_set_value(pdata->gpio_power, 0); + gpio_free(pdata->gpio_power); + } + if (gpio_is_valid(pdata->gpio_npdn)) + gpio_free(pdata->gpio_npdn); + } +} + +static const struct i2c_device_id ak4641_i2c_id[] = { + { "ak4641", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id); + +static struct i2c_driver ak4641_i2c_driver = { + .driver = { + .name = "ak4641", + }, + .probe = ak4641_i2c_probe, + .remove = ak4641_i2c_remove, + .id_table = ak4641_i2c_id, +}; + +module_i2c_driver(ak4641_i2c_driver); + +MODULE_DESCRIPTION("SoC AK4641 driver"); +MODULE_AUTHOR("Harald Welte "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c new file mode 100644 index 0000000000..2a8984c1fa --- /dev/null +++ b/sound/soc/codecs/ak4642.c @@ -0,0 +1,709 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver +// +// Copyright (C) 2009 Renesas Solutions Corp. +// Kuninori Morimoto +// +// Based on wm8731.c by Richard Purdie +// Based on ak4535.c by Richard Purdie +// Based on wm8753.c by Liam Girdwood + +/* ** CAUTION ** + * + * This is very simple driver. + * It can use headphone output / stereo input only + * + * AK4642 is tested. + * AK4643 is tested. + * AK4648 is tested. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PW_MGMT1 0x00 +#define PW_MGMT2 0x01 +#define SG_SL1 0x02 +#define SG_SL2 0x03 +#define MD_CTL1 0x04 +#define MD_CTL2 0x05 +#define TIMER 0x06 +#define ALC_CTL1 0x07 +#define ALC_CTL2 0x08 +#define L_IVC 0x09 +#define L_DVC 0x0a +#define ALC_CTL3 0x0b +#define R_IVC 0x0c +#define R_DVC 0x0d +#define MD_CTL3 0x0e +#define MD_CTL4 0x0f +#define PW_MGMT3 0x10 +#define DF_S 0x11 +#define FIL3_0 0x12 +#define FIL3_1 0x13 +#define FIL3_2 0x14 +#define FIL3_3 0x15 +#define EQ_0 0x16 +#define EQ_1 0x17 +#define EQ_2 0x18 +#define EQ_3 0x19 +#define EQ_4 0x1a +#define EQ_5 0x1b +#define FIL1_0 0x1c +#define FIL1_1 0x1d +#define FIL1_2 0x1e +#define FIL1_3 0x1f /* The maximum valid register for ak4642 */ +#define PW_MGMT4 0x20 +#define MD_CTL5 0x21 +#define LO_MS 0x22 +#define HP_MS 0x23 +#define SPK_MS 0x24 /* The maximum valid register for ak4643 */ +#define EQ_FBEQAB 0x25 +#define EQ_FBEQCD 0x26 +#define EQ_FBEQE 0x27 /* The maximum valid register for ak4648 */ + +/* PW_MGMT1*/ +#define PMVCM (1 << 6) /* VCOM Power Management */ +#define PMMIN (1 << 5) /* MIN Input Power Management */ +#define PMDAC (1 << 2) /* DAC Power Management */ +#define PMADL (1 << 0) /* MIC Amp Lch and ADC Lch Power Management */ + +/* PW_MGMT2 */ +#define HPMTN (1 << 6) +#define PMHPL (1 << 5) +#define PMHPR (1 << 4) +#define MS (1 << 3) /* master/slave select */ +#define MCKO (1 << 1) +#define PMPLL (1 << 0) + +#define PMHP_MASK (PMHPL | PMHPR) +#define PMHP PMHP_MASK + +/* PW_MGMT3 */ +#define PMADR (1 << 0) /* MIC L / ADC R Power Management */ + +/* SG_SL1 */ +#define MINS (1 << 6) /* Switch from MIN to Speaker */ +#define DACL (1 << 4) /* Switch from DAC to Stereo or Receiver */ +#define PMMP (1 << 2) /* MPWR pin Power Management */ +#define MGAIN0 (1 << 0) /* MIC amp gain*/ + +/* SG_SL2 */ +#define LOPS (1 << 6) /* Stero Line-out Power Save Mode */ + +/* TIMER */ +#define ZTM(param) ((param & 0x3) << 4) /* ALC Zero Crossing TimeOut */ +#define WTM(param) (((param & 0x4) << 4) | ((param & 0x3) << 2)) + +/* ALC_CTL1 */ +#define ALC (1 << 5) /* ALC Enable */ +#define LMTH0 (1 << 0) /* ALC Limiter / Recovery Level */ + +/* MD_CTL1 */ +#define PLL3 (1 << 7) +#define PLL2 (1 << 6) +#define PLL1 (1 << 5) +#define PLL0 (1 << 4) +#define PLL_MASK (PLL3 | PLL2 | PLL1 | PLL0) + +#define BCKO_MASK (1 << 3) +#define BCKO_64 BCKO_MASK + +#define DIF_MASK (3 << 0) +#define DSP (0 << 0) +#define RIGHT_J (1 << 0) +#define LEFT_J (2 << 0) +#define I2S (3 << 0) + +/* MD_CTL2 */ +#define FSs(val) (((val & 0x7) << 0) | ((val & 0x8) << 2)) +#define PSs(val) ((val & 0x3) << 6) + +/* MD_CTL3 */ +#define BST1 (1 << 3) + +/* MD_CTL4 */ +#define DACH (1 << 0) + +struct ak4642_drvdata { + const struct regmap_config *regmap_config; + int extended_frequencies; +}; + +struct ak4642_priv { + const struct ak4642_drvdata *drvdata; + struct clk *mcko; +}; + +/* + * Playback Volume (table 39) + * + * max : 0x00 : +12.0 dB + * ( 0.5 dB step ) + * min : 0xFE : -115.0 dB + * mute: 0xFF + */ +static const DECLARE_TLV_DB_SCALE(out_tlv, -11550, 50, 1); + +static const struct snd_kcontrol_new ak4642_snd_controls[] = { + + SOC_DOUBLE_R_TLV("Digital Playback Volume", L_DVC, R_DVC, + 0, 0xFF, 1, out_tlv), + SOC_SINGLE("ALC Capture Switch", ALC_CTL1, 5, 1, 0), + SOC_SINGLE("ALC Capture ZC Switch", ALC_CTL1, 4, 1, 1), +}; + +static const struct snd_kcontrol_new ak4642_headphone_control = + SOC_DAPM_SINGLE("Switch", PW_MGMT2, 6, 1, 0); + +static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = { + SOC_DAPM_SINGLE("DACL", SG_SL1, 4, 1, 0), +}; + +/* event handlers */ +static int ak4642_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMD: + case SND_SOC_DAPM_PRE_PMU: + /* Power save mode ON */ + snd_soc_component_update_bits(component, SG_SL2, LOPS, LOPS); + break; + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + /* Power save mode OFF */ + msleep(300); + snd_soc_component_update_bits(component, SG_SL2, LOPS, 0); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = { + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), + SND_SOC_DAPM_OUTPUT("LINEOUT"), + + SND_SOC_DAPM_PGA("HPL Out", PW_MGMT2, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPR Out", PW_MGMT2, 4, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("Headphone Enable", SND_SOC_NOPM, 0, 0, + &ak4642_headphone_control), + + SND_SOC_DAPM_PGA("DACH", MD_CTL4, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("LINEOUT Mixer", PW_MGMT1, 3, 0, + &ak4642_lout_mixer_controls[0], + ARRAY_SIZE(ak4642_lout_mixer_controls), + ak4642_lout_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + /* DAC */ + SND_SOC_DAPM_DAC("DAC", NULL, PW_MGMT1, 2, 0), +}; + +static const struct snd_soc_dapm_route ak4642_intercon[] = { + + /* Outputs */ + {"HPOUTL", NULL, "HPL Out"}, + {"HPOUTR", NULL, "HPR Out"}, + {"LINEOUT", NULL, "LINEOUT Mixer"}, + + {"HPL Out", NULL, "Headphone Enable"}, + {"HPR Out", NULL, "Headphone Enable"}, + + {"Headphone Enable", "Switch", "DACH"}, + + {"DACH", NULL, "DAC"}, + + {"LINEOUT Mixer", "DACL", "DAC"}, + + { "DAC", NULL, "Playback" }, +}; + +/* + * ak4642 register cache + */ +static const struct reg_default ak4643_reg[] = { + { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 }, + { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 }, + { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 }, + { 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0x08 }, + { 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 }, + { 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 }, + { 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 }, + { 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 }, + { 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 }, + { 36, 0x00 }, +}; + +/* The default settings for 0x0 ~ 0x1f registers are the same for ak4642 + and ak4643. So we reuse the ak4643 reg_default for ak4642. + The valid registers for ak4642 are 0x0 ~ 0x1f which is a subset of ak4643, + so define NUM_AK4642_REG_DEFAULTS for ak4642. +*/ +#define ak4642_reg ak4643_reg +#define NUM_AK4642_REG_DEFAULTS (FIL1_3 + 1) + +static const struct reg_default ak4648_reg[] = { + { 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 }, + { 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 }, + { 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 }, + { 12, 0xe1 }, { 13, 0x18 }, { 14, 0x11 }, { 15, 0xb8 }, + { 16, 0x00 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x00 }, + { 20, 0x00 }, { 21, 0x00 }, { 22, 0x00 }, { 23, 0x00 }, + { 24, 0x00 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0x00 }, + { 28, 0x00 }, { 29, 0x00 }, { 30, 0x00 }, { 31, 0x00 }, + { 32, 0x00 }, { 33, 0x00 }, { 34, 0x00 }, { 35, 0x00 }, + { 36, 0x00 }, { 37, 0x88 }, { 38, 0x88 }, { 39, 0x08 }, +}; + +static int ak4642_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct snd_soc_component *component = dai->component; + + if (is_play) { + /* + * start headphone output + * + * PLL, Master Mode + * Audio I/F Format :MSB justified (ADC & DAC) + * Bass Boost Level : Middle + * + * This operation came from example code of + * "ASAHI KASEI AK4642" (japanese) manual p97. + */ + snd_soc_component_write(component, L_IVC, 0x91); /* volume */ + snd_soc_component_write(component, R_IVC, 0x91); /* volume */ + } else { + /* + * start stereo input + * + * PLL Master Mode + * Audio I/F Format:MSB justified (ADC & DAC) + * Pre MIC AMP:+20dB + * MIC Power On + * ALC setting:Refer to Table 35 + * ALC bit=“1” + * + * This operation came from example code of + * "ASAHI KASEI AK4642" (japanese) manual p94. + */ + snd_soc_component_update_bits(component, SG_SL1, PMMP | MGAIN0, PMMP | MGAIN0); + snd_soc_component_write(component, TIMER, ZTM(0x3) | WTM(0x3)); + snd_soc_component_write(component, ALC_CTL1, ALC | LMTH0); + snd_soc_component_update_bits(component, PW_MGMT1, PMADL, PMADL); + snd_soc_component_update_bits(component, PW_MGMT3, PMADR, PMADR); + } + + return 0; +} + +static void ak4642_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct snd_soc_component *component = dai->component; + + if (is_play) { + } else { + /* stop stereo input */ + snd_soc_component_update_bits(component, PW_MGMT1, PMADL, 0); + snd_soc_component_update_bits(component, PW_MGMT3, PMADR, 0); + snd_soc_component_update_bits(component, ALC_CTL1, ALC, 0); + } +} + +static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct ak4642_priv *priv = snd_soc_component_get_drvdata(component); + u8 pll; + int extended_freq = 0; + + switch (freq) { + case 11289600: + pll = PLL2; + break; + case 12288000: + pll = PLL2 | PLL0; + break; + case 12000000: + pll = PLL2 | PLL1; + break; + case 24000000: + pll = PLL2 | PLL1 | PLL0; + break; + case 13500000: + pll = PLL3 | PLL2; + break; + case 27000000: + pll = PLL3 | PLL2 | PLL0; + break; + case 19200000: + pll = PLL3; + extended_freq = 1; + break; + case 13000000: + pll = PLL3 | PLL2 | PLL1; + extended_freq = 1; + break; + case 26000000: + pll = PLL3 | PLL2 | PLL1 | PLL0; + extended_freq = 1; + break; + default: + return -EINVAL; + } + + if (extended_freq && !priv->drvdata->extended_frequencies) + return -EINVAL; + + snd_soc_component_update_bits(component, MD_CTL1, PLL_MASK, pll); + + return 0; +} + +static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u8 data; + u8 bcko; + + data = MCKO | PMPLL; /* use MCKO */ + bcko = 0; + + /* set clocking for audio interface */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + data |= MS; + bcko = BCKO_64; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + snd_soc_component_update_bits(component, PW_MGMT2, MS | MCKO | PMPLL, data); + snd_soc_component_update_bits(component, MD_CTL1, BCKO_MASK, bcko); + + /* format type */ + data = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + data = LEFT_J; + break; + case SND_SOC_DAIFMT_I2S: + data = I2S; + break; + /* FIXME + * Please add RIGHT_J / DSP support here + */ + default: + return -EINVAL; + } + snd_soc_component_update_bits(component, MD_CTL1, DIF_MASK, data); + + return 0; +} + +static int ak4642_set_mcko(struct snd_soc_component *component, + u32 frequency) +{ + static const u32 fs_list[] = { + [0] = 8000, + [1] = 12000, + [2] = 16000, + [3] = 24000, + [4] = 7350, + [5] = 11025, + [6] = 14700, + [7] = 22050, + [10] = 32000, + [11] = 48000, + [14] = 29400, + [15] = 44100, + }; + static const u32 ps_list[] = { + [0] = 256, + [1] = 128, + [2] = 64, + [3] = 32 + }; + int ps, fs; + + for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) { + for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) { + if (frequency == ps_list[ps] * fs_list[fs]) { + snd_soc_component_write(component, MD_CTL2, + PSs(ps) | FSs(fs)); + return 0; + } + } + } + + return 0; +} + +static int ak4642_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak4642_priv *priv = snd_soc_component_get_drvdata(component); + u32 rate = clk_get_rate(priv->mcko); + + if (!rate) + rate = params_rate(params) * 256; + + return ak4642_set_mcko(component, rate); +} + +static int ak4642_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, PW_MGMT1, 0x00); + break; + default: + snd_soc_component_update_bits(component, PW_MGMT1, PMVCM, PMVCM); + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops ak4642_dai_ops = { + .startup = ak4642_dai_startup, + .shutdown = ak4642_dai_shutdown, + .set_sysclk = ak4642_dai_set_sysclk, + .set_fmt = ak4642_dai_set_fmt, + .hw_params = ak4642_dai_hw_params, +}; + +static struct snd_soc_dai_driver ak4642_dai = { + .name = "ak4642-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE }, + .ops = &ak4642_dai_ops, + .symmetric_rate = 1, +}; + +static int ak4642_suspend(struct snd_soc_component *component) +{ + struct regmap *regmap = dev_get_regmap(component->dev, NULL); + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + return 0; +} + +static int ak4642_resume(struct snd_soc_component *component) +{ + struct regmap *regmap = dev_get_regmap(component->dev, NULL); + + regcache_cache_only(regmap, false); + regcache_sync(regmap); + return 0; +} +static int ak4642_probe(struct snd_soc_component *component) +{ + struct ak4642_priv *priv = snd_soc_component_get_drvdata(component); + + if (priv->mcko) + ak4642_set_mcko(component, clk_get_rate(priv->mcko)); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_ak4642 = { + .probe = ak4642_probe, + .suspend = ak4642_suspend, + .resume = ak4642_resume, + .set_bias_level = ak4642_set_bias_level, + .controls = ak4642_snd_controls, + .num_controls = ARRAY_SIZE(ak4642_snd_controls), + .dapm_widgets = ak4642_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets), + .dapm_routes = ak4642_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4642_intercon), + .idle_bias_on = 1, + .endianness = 1, +}; + +static const struct regmap_config ak4642_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = FIL1_3, + .reg_defaults = ak4642_reg, + .num_reg_defaults = NUM_AK4642_REG_DEFAULTS, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct regmap_config ak4643_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = SPK_MS, + .reg_defaults = ak4643_reg, + .num_reg_defaults = ARRAY_SIZE(ak4643_reg), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct regmap_config ak4648_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = EQ_FBEQE, + .reg_defaults = ak4648_reg, + .num_reg_defaults = ARRAY_SIZE(ak4648_reg), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct ak4642_drvdata ak4642_drvdata = { + .regmap_config = &ak4642_regmap, +}; + +static const struct ak4642_drvdata ak4643_drvdata = { + .regmap_config = &ak4643_regmap, +}; + +static const struct ak4642_drvdata ak4648_drvdata = { + .regmap_config = &ak4648_regmap, + .extended_frequencies = 1, +}; + +#ifdef CONFIG_COMMON_CLK +static struct clk *ak4642_of_parse_mcko(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct clk *clk; + const char *clk_name = np->name; + const char *parent_clk_name = NULL; + u32 rate; + + if (of_property_read_u32(np, "clock-frequency", &rate)) + return NULL; + + if (of_property_read_bool(np, "clocks")) + parent_clk_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &clk_name); + + clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, 0, rate); + if (!IS_ERR(clk)) + of_clk_add_provider(np, of_clk_src_simple_get, clk); + + return clk; +} +#else +#define ak4642_of_parse_mcko(d) 0 +#endif + +static const struct of_device_id ak4642_of_match[]; +static const struct i2c_device_id ak4642_i2c_id[]; +static int ak4642_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct device_node *np = dev->of_node; + const struct ak4642_drvdata *drvdata = NULL; + struct regmap *regmap; + struct ak4642_priv *priv; + struct clk *mcko = NULL; + + if (np) { + const struct of_device_id *of_id; + + mcko = ak4642_of_parse_mcko(dev); + if (IS_ERR(mcko)) + mcko = NULL; + + of_id = of_match_device(ak4642_of_match, dev); + if (of_id) + drvdata = of_id->data; + } else { + const struct i2c_device_id *id = + i2c_match_id(ak4642_i2c_id, i2c); + drvdata = (const struct ak4642_drvdata *)id->driver_data; + } + + if (!drvdata) { + dev_err(dev, "Unknown device type\n"); + return -EINVAL; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->drvdata = drvdata; + priv->mcko = mcko; + + i2c_set_clientdata(i2c, priv); + + regmap = devm_regmap_init_i2c(i2c, drvdata->regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return devm_snd_soc_register_component(dev, + &soc_component_dev_ak4642, &ak4642_dai, 1); +} + +static const struct of_device_id ak4642_of_match[] = { + { .compatible = "asahi-kasei,ak4642", .data = &ak4642_drvdata}, + { .compatible = "asahi-kasei,ak4643", .data = &ak4643_drvdata}, + { .compatible = "asahi-kasei,ak4648", .data = &ak4648_drvdata}, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4642_of_match); + +static const struct i2c_device_id ak4642_i2c_id[] = { + { "ak4642", (kernel_ulong_t)&ak4642_drvdata }, + { "ak4643", (kernel_ulong_t)&ak4643_drvdata }, + { "ak4648", (kernel_ulong_t)&ak4648_drvdata }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id); + +static struct i2c_driver ak4642_i2c_driver = { + .driver = { + .name = "ak4642-codec", + .of_match_table = ak4642_of_match, + }, + .probe = ak4642_i2c_probe, + .id_table = ak4642_i2c_id, +}; + +module_i2c_driver(ak4642_i2c_driver); + +MODULE_DESCRIPTION("Soc AK4642 driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c new file mode 100644 index 0000000000..5b849b390c --- /dev/null +++ b/sound/soc/codecs/ak4671.c @@ -0,0 +1,666 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ak4671.c -- audio driver for AK4671 + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ak4671.h" + + +/* ak4671 register cache & default register settings */ +static const struct reg_default ak4671_reg_defaults[] = { + { 0x00, 0x00 }, /* AK4671_AD_DA_POWER_MANAGEMENT (0x00) */ + { 0x01, 0xf6 }, /* AK4671_PLL_MODE_SELECT0 (0x01) */ + { 0x02, 0x00 }, /* AK4671_PLL_MODE_SELECT1 (0x02) */ + { 0x03, 0x02 }, /* AK4671_FORMAT_SELECT (0x03) */ + { 0x04, 0x00 }, /* AK4671_MIC_SIGNAL_SELECT (0x04) */ + { 0x05, 0x55 }, /* AK4671_MIC_AMP_GAIN (0x05) */ + { 0x06, 0x00 }, /* AK4671_MIXING_POWER_MANAGEMENT0 (0x06) */ + { 0x07, 0x00 }, /* AK4671_MIXING_POWER_MANAGEMENT1 (0x07) */ + { 0x08, 0xb5 }, /* AK4671_OUTPUT_VOLUME_CONTROL (0x08) */ + { 0x09, 0x00 }, /* AK4671_LOUT1_SIGNAL_SELECT (0x09) */ + { 0x0a, 0x00 }, /* AK4671_ROUT1_SIGNAL_SELECT (0x0a) */ + { 0x0b, 0x00 }, /* AK4671_LOUT2_SIGNAL_SELECT (0x0b) */ + { 0x0c, 0x00 }, /* AK4671_ROUT2_SIGNAL_SELECT (0x0c) */ + { 0x0d, 0x00 }, /* AK4671_LOUT3_SIGNAL_SELECT (0x0d) */ + { 0x0e, 0x00 }, /* AK4671_ROUT3_SIGNAL_SELECT (0x0e) */ + { 0x0f, 0x00 }, /* AK4671_LOUT1_POWER_MANAGERMENT (0x0f) */ + { 0x10, 0x00 }, /* AK4671_LOUT2_POWER_MANAGERMENT (0x10) */ + { 0x11, 0x80 }, /* AK4671_LOUT3_POWER_MANAGERMENT (0x11) */ + { 0x12, 0x91 }, /* AK4671_LCH_INPUT_VOLUME_CONTROL (0x12) */ + { 0x13, 0x91 }, /* AK4671_RCH_INPUT_VOLUME_CONTROL (0x13) */ + { 0x14, 0xe1 }, /* AK4671_ALC_REFERENCE_SELECT (0x14) */ + { 0x15, 0x00 }, /* AK4671_DIGITAL_MIXING_CONTROL (0x15) */ + { 0x16, 0x00 }, /* AK4671_ALC_TIMER_SELECT (0x16) */ + { 0x17, 0x00 }, /* AK4671_ALC_MODE_CONTROL (0x17) */ + { 0x18, 0x02 }, /* AK4671_MODE_CONTROL1 (0x18) */ + { 0x19, 0x01 }, /* AK4671_MODE_CONTROL2 (0x19) */ + { 0x1a, 0x18 }, /* AK4671_LCH_OUTPUT_VOLUME_CONTROL (0x1a) */ + { 0x1b, 0x18 }, /* AK4671_RCH_OUTPUT_VOLUME_CONTROL (0x1b) */ + { 0x1c, 0x00 }, /* AK4671_SIDETONE_A_CONTROL (0x1c) */ + { 0x1d, 0x02 }, /* AK4671_DIGITAL_FILTER_SELECT (0x1d) */ + { 0x1e, 0x00 }, /* AK4671_FIL3_COEFFICIENT0 (0x1e) */ + { 0x1f, 0x00 }, /* AK4671_FIL3_COEFFICIENT1 (0x1f) */ + { 0x20, 0x00 }, /* AK4671_FIL3_COEFFICIENT2 (0x20) */ + { 0x21, 0x00 }, /* AK4671_FIL3_COEFFICIENT3 (0x21) */ + { 0x22, 0x00 }, /* AK4671_EQ_COEFFICIENT0 (0x22) */ + { 0x23, 0x00 }, /* AK4671_EQ_COEFFICIENT1 (0x23) */ + { 0x24, 0x00 }, /* AK4671_EQ_COEFFICIENT2 (0x24) */ + { 0x25, 0x00 }, /* AK4671_EQ_COEFFICIENT3 (0x25) */ + { 0x26, 0x00 }, /* AK4671_EQ_COEFFICIENT4 (0x26) */ + { 0x27, 0x00 }, /* AK4671_EQ_COEFFICIENT5 (0x27) */ + { 0x28, 0xa9 }, /* AK4671_FIL1_COEFFICIENT0 (0x28) */ + { 0x29, 0x1f }, /* AK4671_FIL1_COEFFICIENT1 (0x29) */ + { 0x2a, 0xad }, /* AK4671_FIL1_COEFFICIENT2 (0x2a) */ + { 0x2b, 0x20 }, /* AK4671_FIL1_COEFFICIENT3 (0x2b) */ + { 0x2c, 0x00 }, /* AK4671_FIL2_COEFFICIENT0 (0x2c) */ + { 0x2d, 0x00 }, /* AK4671_FIL2_COEFFICIENT1 (0x2d) */ + { 0x2e, 0x00 }, /* AK4671_FIL2_COEFFICIENT2 (0x2e) */ + { 0x2f, 0x00 }, /* AK4671_FIL2_COEFFICIENT3 (0x2f) */ + { 0x30, 0x00 }, /* AK4671_DIGITAL_FILTER_SELECT2 (0x30) */ + + { 0x32, 0x00 }, /* AK4671_E1_COEFFICIENT0 (0x32) */ + { 0x33, 0x00 }, /* AK4671_E1_COEFFICIENT1 (0x33) */ + { 0x34, 0x00 }, /* AK4671_E1_COEFFICIENT2 (0x34) */ + { 0x35, 0x00 }, /* AK4671_E1_COEFFICIENT3 (0x35) */ + { 0x36, 0x00 }, /* AK4671_E1_COEFFICIENT4 (0x36) */ + { 0x37, 0x00 }, /* AK4671_E1_COEFFICIENT5 (0x37) */ + { 0x38, 0x00 }, /* AK4671_E2_COEFFICIENT0 (0x38) */ + { 0x39, 0x00 }, /* AK4671_E2_COEFFICIENT1 (0x39) */ + { 0x3a, 0x00 }, /* AK4671_E2_COEFFICIENT2 (0x3a) */ + { 0x3b, 0x00 }, /* AK4671_E2_COEFFICIENT3 (0x3b) */ + { 0x3c, 0x00 }, /* AK4671_E2_COEFFICIENT4 (0x3c) */ + { 0x3d, 0x00 }, /* AK4671_E2_COEFFICIENT5 (0x3d) */ + { 0x3e, 0x00 }, /* AK4671_E3_COEFFICIENT0 (0x3e) */ + { 0x3f, 0x00 }, /* AK4671_E3_COEFFICIENT1 (0x3f) */ + { 0x40, 0x00 }, /* AK4671_E3_COEFFICIENT2 (0x40) */ + { 0x41, 0x00 }, /* AK4671_E3_COEFFICIENT3 (0x41) */ + { 0x42, 0x00 }, /* AK4671_E3_COEFFICIENT4 (0x42) */ + { 0x43, 0x00 }, /* AK4671_E3_COEFFICIENT5 (0x43) */ + { 0x44, 0x00 }, /* AK4671_E4_COEFFICIENT0 (0x44) */ + { 0x45, 0x00 }, /* AK4671_E4_COEFFICIENT1 (0x45) */ + { 0x46, 0x00 }, /* AK4671_E4_COEFFICIENT2 (0x46) */ + { 0x47, 0x00 }, /* AK4671_E4_COEFFICIENT3 (0x47) */ + { 0x48, 0x00 }, /* AK4671_E4_COEFFICIENT4 (0x48) */ + { 0x49, 0x00 }, /* AK4671_E4_COEFFICIENT5 (0x49) */ + { 0x4a, 0x00 }, /* AK4671_E5_COEFFICIENT0 (0x4a) */ + { 0x4b, 0x00 }, /* AK4671_E5_COEFFICIENT1 (0x4b) */ + { 0x4c, 0x00 }, /* AK4671_E5_COEFFICIENT2 (0x4c) */ + { 0x4d, 0x00 }, /* AK4671_E5_COEFFICIENT3 (0x4d) */ + { 0x4e, 0x00 }, /* AK4671_E5_COEFFICIENT4 (0x4e) */ + { 0x4f, 0x00 }, /* AK4671_E5_COEFFICIENT5 (0x4f) */ + { 0x50, 0x88 }, /* AK4671_EQ_CONTROL_250HZ_100HZ (0x50) */ + { 0x51, 0x88 }, /* AK4671_EQ_CONTROL_3500HZ_1KHZ (0x51) */ + { 0x52, 0x08 }, /* AK4671_EQ_CONTRO_10KHZ (0x52) */ + { 0x53, 0x00 }, /* AK4671_PCM_IF_CONTROL0 (0x53) */ + { 0x54, 0x00 }, /* AK4671_PCM_IF_CONTROL1 (0x54) */ + { 0x55, 0x00 }, /* AK4671_PCM_IF_CONTROL2 (0x55) */ + { 0x56, 0x18 }, /* AK4671_DIGITAL_VOLUME_B_CONTROL (0x56) */ + { 0x57, 0x18 }, /* AK4671_DIGITAL_VOLUME_C_CONTROL (0x57) */ + { 0x58, 0x00 }, /* AK4671_SIDETONE_VOLUME_CONTROL (0x58) */ + { 0x59, 0x00 }, /* AK4671_DIGITAL_MIXING_CONTROL2 (0x59) */ + { 0x5a, 0x00 }, /* AK4671_SAR_ADC_CONTROL (0x5a) */ +}; + +/* + * LOUT1/ROUT1 output volume control: + * from -24 to 6 dB in 6 dB steps (mute instead of -30 dB) + */ +static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1); + +/* + * LOUT2/ROUT2 output volume control: + * from -33 to 6 dB in 3 dB steps (mute instead of -33 dB) + */ +static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1); + +/* + * LOUT3/ROUT3 output volume control: + * from -6 to 3 dB in 3 dB steps + */ +static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0); + +/* + * Mic amp gain control: + * from -15 to 30 dB in 3 dB steps + * REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not + * available + */ +static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0); + +static const struct snd_kcontrol_new ak4671_snd_controls[] = { + /* Common playback gain controls */ + SOC_SINGLE_TLV("Line Output1 Playback Volume", + AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv), + SOC_SINGLE_TLV("Headphone Output2 Playback Volume", + AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv), + SOC_SINGLE_TLV("Line Output3 Playback Volume", + AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv), + + /* Common capture gain controls */ + SOC_DOUBLE_TLV("Mic Amp Capture Volume", + AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv), +}; + +/* event handlers */ +static int ak4671_out2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, AK4671_LOUT2_POWER_MANAGERMENT, + AK4671_MUTEN, AK4671_MUTEN); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, AK4671_LOUT2_POWER_MANAGERMENT, + AK4671_MUTEN, 0); + break; + } + + return 0; +} + +/* Output Mixers */ +static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = { + SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0), +}; + +static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = { + SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0), +}; + +static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = { + SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0), +}; + +static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = { + SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0), +}; + +static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = { + SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0), +}; + +static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = { + SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0), + SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0), +}; + +/* Input MUXs */ +static const char *ak4671_lin_mux_texts[] = + {"LIN1", "LIN2", "LIN3", "LIN4"}; +static SOC_ENUM_SINGLE_DECL(ak4671_lin_mux_enum, + AK4671_MIC_SIGNAL_SELECT, 0, + ak4671_lin_mux_texts); +static const struct snd_kcontrol_new ak4671_lin_mux_control = + SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum); + +static const char *ak4671_rin_mux_texts[] = + {"RIN1", "RIN2", "RIN3", "RIN4"}; +static SOC_ENUM_SINGLE_DECL(ak4671_rin_mux_enum, + AK4671_MIC_SIGNAL_SELECT, 2, + ak4671_rin_mux_texts); +static const struct snd_kcontrol_new ak4671_rin_mux_control = + SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum); + +static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = { + /* Inputs */ + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + SND_SOC_DAPM_INPUT("RIN2"), + SND_SOC_DAPM_INPUT("LIN3"), + SND_SOC_DAPM_INPUT("RIN3"), + SND_SOC_DAPM_INPUT("LIN4"), + SND_SOC_DAPM_INPUT("RIN4"), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("LOUT3"), + SND_SOC_DAPM_OUTPUT("ROUT3"), + + /* DAC */ + SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback", + AK4671_AD_DA_POWER_MANAGEMENT, 6, 0), + SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback", + AK4671_AD_DA_POWER_MANAGEMENT, 7, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture", + AK4671_AD_DA_POWER_MANAGEMENT, 4, 0), + SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture", + AK4671_AD_DA_POWER_MANAGEMENT, 5, 0), + + /* PGA */ + SND_SOC_DAPM_PGA("LOUT2 Mix Amp", + AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("ROUT2 Mix Amp", + AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0), + + SND_SOC_DAPM_PGA("LIN1 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("RIN1 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("LIN2 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("RIN2 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("LIN3 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("RIN3 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("LIN4 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("RIN4 Mixing Circuit", + AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0, + &ak4671_lout1_mixer_controls[0], + ARRAY_SIZE(ak4671_lout1_mixer_controls)), + SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0, + &ak4671_rout1_mixer_controls[0], + ARRAY_SIZE(ak4671_rout1_mixer_controls)), + SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT, + 0, 0, &ak4671_lout2_mixer_controls[0], + ARRAY_SIZE(ak4671_lout2_mixer_controls), + ak4671_out2_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT, + 1, 0, &ak4671_rout2_mixer_controls[0], + ARRAY_SIZE(ak4671_rout2_mixer_controls), + ak4671_out2_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0, + &ak4671_lout3_mixer_controls[0], + ARRAY_SIZE(ak4671_lout3_mixer_controls)), + SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0, + &ak4671_rout3_mixer_controls[0], + ARRAY_SIZE(ak4671_rout3_mixer_controls)), + + /* Input MUXs */ + SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0, + &ak4671_lin_mux_control), + SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0, + &ak4671_rin_mux_control), + + /* Mic Power */ + SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0), + + /* Supply */ + SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route ak4671_intercon[] = { + {"DAC Left", NULL, "PMPLL"}, + {"DAC Right", NULL, "PMPLL"}, + {"ADC Left", NULL, "PMPLL"}, + {"ADC Right", NULL, "PMPLL"}, + + /* Outputs */ + {"LOUT1", NULL, "LOUT1 Mixer"}, + {"ROUT1", NULL, "ROUT1 Mixer"}, + {"LOUT2", NULL, "LOUT2 Mix Amp"}, + {"ROUT2", NULL, "ROUT2 Mix Amp"}, + {"LOUT3", NULL, "LOUT3 Mixer"}, + {"ROUT3", NULL, "ROUT3 Mixer"}, + + {"LOUT1 Mixer", "DACL", "DAC Left"}, + {"ROUT1 Mixer", "DACR", "DAC Right"}, + {"LOUT2 Mixer", "DACHL", "DAC Left"}, + {"ROUT2 Mixer", "DACHR", "DAC Right"}, + {"LOUT2 Mix Amp", NULL, "LOUT2 Mixer"}, + {"ROUT2 Mix Amp", NULL, "ROUT2 Mixer"}, + {"LOUT3 Mixer", "DACSL", "DAC Left"}, + {"ROUT3 Mixer", "DACSR", "DAC Right"}, + + /* Inputs */ + {"LIN MUX", "LIN1", "LIN1"}, + {"LIN MUX", "LIN2", "LIN2"}, + {"LIN MUX", "LIN3", "LIN3"}, + {"LIN MUX", "LIN4", "LIN4"}, + + {"RIN MUX", "RIN1", "RIN1"}, + {"RIN MUX", "RIN2", "RIN2"}, + {"RIN MUX", "RIN3", "RIN3"}, + {"RIN MUX", "RIN4", "RIN4"}, + + {"LIN1", NULL, "Mic Bias"}, + {"RIN1", NULL, "Mic Bias"}, + {"LIN2", NULL, "Mic Bias"}, + {"RIN2", NULL, "Mic Bias"}, + + {"ADC Left", NULL, "LIN MUX"}, + {"ADC Right", NULL, "RIN MUX"}, + + /* Analog Loops */ + {"LIN1 Mixing Circuit", NULL, "LIN1"}, + {"RIN1 Mixing Circuit", NULL, "RIN1"}, + {"LIN2 Mixing Circuit", NULL, "LIN2"}, + {"RIN2 Mixing Circuit", NULL, "RIN2"}, + {"LIN3 Mixing Circuit", NULL, "LIN3"}, + {"RIN3 Mixing Circuit", NULL, "RIN3"}, + {"LIN4 Mixing Circuit", NULL, "LIN4"}, + {"RIN4 Mixing Circuit", NULL, "RIN4"}, + + {"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"}, + {"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"}, + {"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"}, + {"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"}, + {"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"}, + {"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"}, + + {"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"}, + {"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"}, + {"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"}, + {"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"}, + {"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"}, + {"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"}, + + {"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"}, + {"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"}, + {"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"}, + {"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"}, + {"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"}, + {"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"}, + + {"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"}, + {"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"}, + {"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"}, + {"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"}, + {"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"}, + {"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"}, +}; + +static int ak4671_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u8 fs; + + fs = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT0); + fs &= ~AK4671_FS; + + switch (params_rate(params)) { + case 8000: + fs |= AK4671_FS_8KHZ; + break; + case 12000: + fs |= AK4671_FS_12KHZ; + break; + case 16000: + fs |= AK4671_FS_16KHZ; + break; + case 24000: + fs |= AK4671_FS_24KHZ; + break; + case 11025: + fs |= AK4671_FS_11_025KHZ; + break; + case 22050: + fs |= AK4671_FS_22_05KHZ; + break; + case 32000: + fs |= AK4671_FS_32KHZ; + break; + case 44100: + fs |= AK4671_FS_44_1KHZ; + break; + case 48000: + fs |= AK4671_FS_48KHZ; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, AK4671_PLL_MODE_SELECT0, fs); + + return 0; +} + +static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + u8 pll; + + pll = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT0); + pll &= ~AK4671_PLL; + + switch (freq) { + case 11289600: + pll |= AK4671_PLL_11_2896MHZ; + break; + case 12000000: + pll |= AK4671_PLL_12MHZ; + break; + case 12288000: + pll |= AK4671_PLL_12_288MHZ; + break; + case 13000000: + pll |= AK4671_PLL_13MHZ; + break; + case 13500000: + pll |= AK4671_PLL_13_5MHZ; + break; + case 19200000: + pll |= AK4671_PLL_19_2MHZ; + break; + case 24000000: + pll |= AK4671_PLL_24MHZ; + break; + case 26000000: + pll |= AK4671_PLL_26MHZ; + break; + case 27000000: + pll |= AK4671_PLL_27MHZ; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, AK4671_PLL_MODE_SELECT0, pll); + + return 0; +} + +static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u8 mode; + u8 format; + + /* set master/slave audio interface */ + mode = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT1); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + mode |= AK4671_M_S; + break; + case SND_SOC_DAIFMT_CBP_CFC: + mode &= ~(AK4671_M_S); + break; + default: + return -EINVAL; + } + + /* interface format */ + format = snd_soc_component_read(component, AK4671_FORMAT_SELECT); + format &= ~AK4671_DIF; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format |= AK4671_DIF_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + format |= AK4671_DIF_MSB_MODE; + break; + case SND_SOC_DAIFMT_DSP_A: + format |= AK4671_DIF_DSP_MODE; + format |= AK4671_BCKP; + format |= AK4671_MSBS; + break; + default: + return -EINVAL; + } + + /* set mode and format */ + snd_soc_component_write(component, AK4671_PLL_MODE_SELECT1, mode); + snd_soc_component_write(component, AK4671_FORMAT_SELECT, format); + + return 0; +} + +static int ak4671_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + case SND_SOC_BIAS_STANDBY: + snd_soc_component_update_bits(component, AK4671_AD_DA_POWER_MANAGEMENT, + AK4671_PMVCM, AK4671_PMVCM); + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, AK4671_AD_DA_POWER_MANAGEMENT, 0x00); + break; + } + return 0; +} + +#define AK4671_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000) + +#define AK4671_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +static const struct snd_soc_dai_ops ak4671_dai_ops = { + .hw_params = ak4671_hw_params, + .set_sysclk = ak4671_set_dai_sysclk, + .set_fmt = ak4671_set_dai_fmt, +}; + +static struct snd_soc_dai_driver ak4671_dai = { + .name = "ak4671-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AK4671_RATES, + .formats = AK4671_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AK4671_RATES, + .formats = AK4671_FORMATS,}, + .ops = &ak4671_dai_ops, +}; + +static const struct snd_soc_component_driver soc_component_dev_ak4671 = { + .set_bias_level = ak4671_set_bias_level, + .controls = ak4671_snd_controls, + .num_controls = ARRAY_SIZE(ak4671_snd_controls), + .dapm_widgets = ak4671_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets), + .dapm_routes = ak4671_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4671_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config ak4671_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AK4671_SAR_ADC_CONTROL, + .reg_defaults = ak4671_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4671_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static int ak4671_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_i2c(client, &ak4671_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&client->dev, "Failed to create regmap: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_ak4671, &ak4671_dai, 1); + return ret; +} + +static const struct i2c_device_id ak4671_i2c_id[] = { + { "ak4671", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id); + +static struct i2c_driver ak4671_i2c_driver = { + .driver = { + .name = "ak4671-codec", + }, + .probe = ak4671_i2c_probe, + .id_table = ak4671_i2c_id, +}; + +module_i2c_driver(ak4671_i2c_driver); + +MODULE_DESCRIPTION("ASoC AK4671 codec driver"); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h new file mode 100644 index 0000000000..3dac0a1ae7 --- /dev/null +++ b/sound/soc/codecs/ak4671.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * ak4671.h -- audio driver for AK4671 + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + */ + +#ifndef _AK4671_H +#define _AK4671_H + +#define AK4671_AD_DA_POWER_MANAGEMENT 0x00 +#define AK4671_PLL_MODE_SELECT0 0x01 +#define AK4671_PLL_MODE_SELECT1 0x02 +#define AK4671_FORMAT_SELECT 0x03 +#define AK4671_MIC_SIGNAL_SELECT 0x04 +#define AK4671_MIC_AMP_GAIN 0x05 +#define AK4671_MIXING_POWER_MANAGEMENT0 0x06 +#define AK4671_MIXING_POWER_MANAGEMENT1 0x07 +#define AK4671_OUTPUT_VOLUME_CONTROL 0x08 +#define AK4671_LOUT1_SIGNAL_SELECT 0x09 +#define AK4671_ROUT1_SIGNAL_SELECT 0x0a +#define AK4671_LOUT2_SIGNAL_SELECT 0x0b +#define AK4671_ROUT2_SIGNAL_SELECT 0x0c +#define AK4671_LOUT3_SIGNAL_SELECT 0x0d +#define AK4671_ROUT3_SIGNAL_SELECT 0x0e +#define AK4671_LOUT1_POWER_MANAGERMENT 0x0f +#define AK4671_LOUT2_POWER_MANAGERMENT 0x10 +#define AK4671_LOUT3_POWER_MANAGERMENT 0x11 +#define AK4671_LCH_INPUT_VOLUME_CONTROL 0x12 +#define AK4671_RCH_INPUT_VOLUME_CONTROL 0x13 +#define AK4671_ALC_REFERENCE_SELECT 0x14 +#define AK4671_DIGITAL_MIXING_CONTROL 0x15 +#define AK4671_ALC_TIMER_SELECT 0x16 +#define AK4671_ALC_MODE_CONTROL 0x17 +#define AK4671_MODE_CONTROL1 0x18 +#define AK4671_MODE_CONTROL2 0x19 +#define AK4671_LCH_OUTPUT_VOLUME_CONTROL 0x1a +#define AK4671_RCH_OUTPUT_VOLUME_CONTROL 0x1b +#define AK4671_SIDETONE_A_CONTROL 0x1c +#define AK4671_DIGITAL_FILTER_SELECT 0x1d +#define AK4671_FIL3_COEFFICIENT0 0x1e +#define AK4671_FIL3_COEFFICIENT1 0x1f +#define AK4671_FIL3_COEFFICIENT2 0x20 +#define AK4671_FIL3_COEFFICIENT3 0x21 +#define AK4671_EQ_COEFFICIENT0 0x22 +#define AK4671_EQ_COEFFICIENT1 0x23 +#define AK4671_EQ_COEFFICIENT2 0x24 +#define AK4671_EQ_COEFFICIENT3 0x25 +#define AK4671_EQ_COEFFICIENT4 0x26 +#define AK4671_EQ_COEFFICIENT5 0x27 +#define AK4671_FIL1_COEFFICIENT0 0x28 +#define AK4671_FIL1_COEFFICIENT1 0x29 +#define AK4671_FIL1_COEFFICIENT2 0x2a +#define AK4671_FIL1_COEFFICIENT3 0x2b +#define AK4671_FIL2_COEFFICIENT0 0x2c +#define AK4671_FIL2_COEFFICIENT1 0x2d +#define AK4671_FIL2_COEFFICIENT2 0x2e +#define AK4671_FIL2_COEFFICIENT3 0x2f +#define AK4671_DIGITAL_FILTER_SELECT2 0x30 +#define AK4671_E1_COEFFICIENT0 0x32 +#define AK4671_E1_COEFFICIENT1 0x33 +#define AK4671_E1_COEFFICIENT2 0x34 +#define AK4671_E1_COEFFICIENT3 0x35 +#define AK4671_E1_COEFFICIENT4 0x36 +#define AK4671_E1_COEFFICIENT5 0x37 +#define AK4671_E2_COEFFICIENT0 0x38 +#define AK4671_E2_COEFFICIENT1 0x39 +#define AK4671_E2_COEFFICIENT2 0x3a +#define AK4671_E2_COEFFICIENT3 0x3b +#define AK4671_E2_COEFFICIENT4 0x3c +#define AK4671_E2_COEFFICIENT5 0x3d +#define AK4671_E3_COEFFICIENT0 0x3e +#define AK4671_E3_COEFFICIENT1 0x3f +#define AK4671_E3_COEFFICIENT2 0x40 +#define AK4671_E3_COEFFICIENT3 0x41 +#define AK4671_E3_COEFFICIENT4 0x42 +#define AK4671_E3_COEFFICIENT5 0x43 +#define AK4671_E4_COEFFICIENT0 0x44 +#define AK4671_E4_COEFFICIENT1 0x45 +#define AK4671_E4_COEFFICIENT2 0x46 +#define AK4671_E4_COEFFICIENT3 0x47 +#define AK4671_E4_COEFFICIENT4 0x48 +#define AK4671_E4_COEFFICIENT5 0x49 +#define AK4671_E5_COEFFICIENT0 0x4a +#define AK4671_E5_COEFFICIENT1 0x4b +#define AK4671_E5_COEFFICIENT2 0x4c +#define AK4671_E5_COEFFICIENT3 0x4d +#define AK4671_E5_COEFFICIENT4 0x4e +#define AK4671_E5_COEFFICIENT5 0x4f +#define AK4671_EQ_CONTROL_250HZ_100HZ 0x50 +#define AK4671_EQ_CONTROL_3500HZ_1KHZ 0x51 +#define AK4671_EQ_CONTRO_10KHZ 0x52 +#define AK4671_PCM_IF_CONTROL0 0x53 +#define AK4671_PCM_IF_CONTROL1 0x54 +#define AK4671_PCM_IF_CONTROL2 0x55 +#define AK4671_DIGITAL_VOLUME_B_CONTROL 0x56 +#define AK4671_DIGITAL_VOLUME_C_CONTROL 0x57 +#define AK4671_SIDETONE_VOLUME_CONTROL 0x58 +#define AK4671_DIGITAL_MIXING_CONTROL2 0x59 +#define AK4671_SAR_ADC_CONTROL 0x5a + +/* Bitfield Definitions */ + +/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */ +#define AK4671_PMVCM 0x01 + +/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */ +#define AK4671_PLL 0x0f +#define AK4671_PLL_11_2896MHZ (4 << 0) +#define AK4671_PLL_12_288MHZ (5 << 0) +#define AK4671_PLL_12MHZ (6 << 0) +#define AK4671_PLL_24MHZ (7 << 0) +#define AK4671_PLL_19_2MHZ (8 << 0) +#define AK4671_PLL_13_5MHZ (12 << 0) +#define AK4671_PLL_27MHZ (13 << 0) +#define AK4671_PLL_13MHZ (14 << 0) +#define AK4671_PLL_26MHZ (15 << 0) +#define AK4671_FS 0xf0 +#define AK4671_FS_8KHZ (0 << 4) +#define AK4671_FS_12KHZ (1 << 4) +#define AK4671_FS_16KHZ (2 << 4) +#define AK4671_FS_24KHZ (3 << 4) +#define AK4671_FS_11_025KHZ (5 << 4) +#define AK4671_FS_22_05KHZ (7 << 4) +#define AK4671_FS_32KHZ (10 << 4) +#define AK4671_FS_48KHZ (11 << 4) +#define AK4671_FS_44_1KHZ (15 << 4) + +/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */ +#define AK4671_PMPLL 0x01 +#define AK4671_M_S 0x02 + +/* AK4671_FORMAT_SELECT (0x03) Fields */ +#define AK4671_DIF 0x03 +#define AK4671_DIF_DSP_MODE (0 << 0) +#define AK4671_DIF_MSB_MODE (2 << 0) +#define AK4671_DIF_I2S_MODE (3 << 0) +#define AK4671_BCKP 0x04 +#define AK4671_MSBS 0x08 +#define AK4671_SDOD 0x10 + +/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */ +#define AK4671_MUTEN 0x04 + +#endif diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c new file mode 100644 index 0000000000..0c5e00679c --- /dev/null +++ b/sound/soc/codecs/ak5386.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC driver for + * Asahi Kasei AK5386 Single-ended 24-Bit 192kHz delta-sigma ADC + * + * (c) 2013 Daniel Mack + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char * const supply_names[] = { + "va", "vd" +}; + +struct ak5386_priv { + int reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; +}; + +static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), +}; + +static const struct snd_soc_dapm_route ak5386_dapm_routes[] = { + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, +}; + +static int ak5386_soc_probe(struct snd_soc_component *component) +{ + struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); + return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); +} + +static void ak5386_soc_remove(struct snd_soc_component *component) +{ + struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); +} + +#ifdef CONFIG_PM +static int ak5386_soc_suspend(struct snd_soc_component *component) +{ + struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); + return 0; +} + +static int ak5386_soc_resume(struct snd_soc_component *component) +{ + struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); + return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); +} +#else +#define ak5386_soc_suspend NULL +#define ak5386_soc_resume NULL +#endif /* CONFIG_PM */ + +static const struct snd_soc_component_driver soc_component_ak5386 = { + .probe = ak5386_soc_probe, + .remove = ak5386_soc_remove, + .suspend = ak5386_soc_suspend, + .resume = ak5386_soc_resume, + .dapm_widgets = ak5386_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets), + .dapm_routes = ak5386_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + + format &= SND_SOC_DAIFMT_FORMAT_MASK; + if (format != SND_SOC_DAIFMT_LEFT_J && + format != SND_SOC_DAIFMT_I2S) { + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + return 0; +} + +static int ak5386_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); + + /* + * From the datasheet: + * + * All external clocks (MCLK, SCLK and LRCK) must be present unless + * PDN pin = “L”. If these clocks are not provided, the AK5386 may + * draw excess current due to its use of internal dynamically + * refreshed logic. If the external clocks are not present, place + * the AK5386 in power-down mode (PDN pin = “L”). + */ + + if (gpio_is_valid(priv->reset_gpio)) + gpio_set_value(priv->reset_gpio, 1); + + return 0; +} + +static int ak5386_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak5386_priv *priv = snd_soc_component_get_drvdata(component); + + if (gpio_is_valid(priv->reset_gpio)) + gpio_set_value(priv->reset_gpio, 0); + + return 0; +} + +static const struct snd_soc_dai_ops ak5386_dai_ops = { + .set_fmt = ak5386_set_dai_fmt, + .hw_params = ak5386_hw_params, + .hw_free = ak5386_hw_free, +}; + +static struct snd_soc_dai_driver ak5386_dai = { + .name = "ak5386-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + }, + .ops = &ak5386_dai_ops, +}; + +#ifdef CONFIG_OF +static const struct of_device_id ak5386_dt_ids[] = { + { .compatible = "asahi-kasei,ak5386", }, + { } +}; +MODULE_DEVICE_TABLE(of, ak5386_dt_ids); +#endif + +static int ak5386_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ak5386_priv *priv; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->reset_gpio = -EINVAL; + dev_set_drvdata(dev, priv); + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + priv->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret < 0) + return ret; + + if (of_match_device(of_match_ptr(ak5386_dt_ids), dev)) + priv->reset_gpio = of_get_named_gpio(dev->of_node, + "reset-gpio", 0); + + if (gpio_is_valid(priv->reset_gpio)) + if (devm_gpio_request_one(dev, priv->reset_gpio, + GPIOF_OUT_INIT_LOW, + "AK5386 Reset")) + priv->reset_gpio = -EINVAL; + + return devm_snd_soc_register_component(dev, &soc_component_ak5386, + &ak5386_dai, 1); +} + +static struct platform_driver ak5386_driver = { + .probe = ak5386_probe, + .driver = { + .name = "ak5386", + .of_match_table = of_match_ptr(ak5386_dt_ids), + }, +}; + +module_platform_driver(ak5386_driver); + +MODULE_DESCRIPTION("ASoC driver for AK5386 ADC"); +MODULE_AUTHOR("Daniel Mack "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c new file mode 100644 index 0000000000..442e2cb42d --- /dev/null +++ b/sound/soc/codecs/ak5558.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Audio driver for AK5558 ADC +// +// Copyright (C) 2015 Asahi Kasei Microdevices Corporation +// Copyright 2018 NXP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ak5558.h" + +enum ak555x_type { + AK5558, + AK5552, +}; + +#define AK5558_NUM_SUPPLIES 2 +static const char *ak5558_supply_names[AK5558_NUM_SUPPLIES] = { + "DVDD", + "AVDD", +}; + +/* AK5558 Codec Private Data */ +struct ak5558_priv { + struct regulator_bulk_data supplies[AK5558_NUM_SUPPLIES]; + struct snd_soc_component component; + struct regmap *regmap; + struct i2c_client *i2c; + struct gpio_desc *reset_gpiod; /* Reset & Power down GPIO */ + int slots; + int slot_width; +}; + +/* ak5558 register cache & default register settings */ +static const struct reg_default ak5558_reg[] = { + { 0x0, 0xFF }, /* 0x00 AK5558_00_POWER_MANAGEMENT1 */ + { 0x1, 0x01 }, /* 0x01 AK5558_01_POWER_MANAGEMENT2 */ + { 0x2, 0x01 }, /* 0x02 AK5558_02_CONTROL1 */ + { 0x3, 0x00 }, /* 0x03 AK5558_03_CONTROL2 */ + { 0x4, 0x00 }, /* 0x04 AK5558_04_CONTROL3 */ + { 0x5, 0x00 } /* 0x05 AK5558_05_DSD */ +}; + +static const char * const mono_texts[] = { + "8 Slot", "2 Slot", "4 Slot", "1 Slot", +}; + +static const struct soc_enum ak5558_mono_enum[] = { + SOC_ENUM_SINGLE(AK5558_01_POWER_MANAGEMENT2, 1, + ARRAY_SIZE(mono_texts), mono_texts), +}; + +static const char * const mono_5552_texts[] = { + "2 Slot", "1 Slot (Fixed)", "2 Slot", "1 Slot (Optimal)", +}; + +static const struct soc_enum ak5552_mono_enum[] = { + SOC_ENUM_SINGLE(AK5558_01_POWER_MANAGEMENT2, 1, + ARRAY_SIZE(mono_5552_texts), mono_5552_texts), +}; + +static const char * const digfil_texts[] = { + "Sharp Roll-Off", "Slow Roll-Off", + "Short Delay Sharp Roll-Off", "Short Delay Slow Roll-Off", +}; + +static const struct soc_enum ak5558_adcset_enum[] = { + SOC_ENUM_SINGLE(AK5558_04_CONTROL3, 0, + ARRAY_SIZE(digfil_texts), digfil_texts), +}; + +static const struct snd_kcontrol_new ak5558_snd_controls[] = { + SOC_ENUM("Monaural Mode", ak5558_mono_enum[0]), + SOC_ENUM("Digital Filter", ak5558_adcset_enum[0]), +}; + +static const struct snd_kcontrol_new ak5552_snd_controls[] = { + SOC_ENUM("Monaural Mode", ak5552_mono_enum[0]), + SOC_ENUM("Digital Filter", ak5558_adcset_enum[0]), +}; + +static const struct snd_soc_dapm_widget ak5558_dapm_widgets[] = { + /* Analog Input */ + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("AIN4"), + SND_SOC_DAPM_INPUT("AIN5"), + SND_SOC_DAPM_INPUT("AIN6"), + SND_SOC_DAPM_INPUT("AIN7"), + SND_SOC_DAPM_INPUT("AIN8"), + + SND_SOC_DAPM_ADC("ADC Ch1", NULL, AK5558_00_POWER_MANAGEMENT1, 0, 0), + SND_SOC_DAPM_ADC("ADC Ch2", NULL, AK5558_00_POWER_MANAGEMENT1, 1, 0), + SND_SOC_DAPM_ADC("ADC Ch3", NULL, AK5558_00_POWER_MANAGEMENT1, 2, 0), + SND_SOC_DAPM_ADC("ADC Ch4", NULL, AK5558_00_POWER_MANAGEMENT1, 3, 0), + SND_SOC_DAPM_ADC("ADC Ch5", NULL, AK5558_00_POWER_MANAGEMENT1, 4, 0), + SND_SOC_DAPM_ADC("ADC Ch6", NULL, AK5558_00_POWER_MANAGEMENT1, 5, 0), + SND_SOC_DAPM_ADC("ADC Ch7", NULL, AK5558_00_POWER_MANAGEMENT1, 6, 0), + SND_SOC_DAPM_ADC("ADC Ch8", NULL, AK5558_00_POWER_MANAGEMENT1, 7, 0), + + SND_SOC_DAPM_AIF_OUT("SDTO", "Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_widget ak5552_dapm_widgets[] = { + /* Analog Input */ + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + + SND_SOC_DAPM_ADC("ADC Ch1", NULL, AK5558_00_POWER_MANAGEMENT1, 0, 0), + SND_SOC_DAPM_ADC("ADC Ch2", NULL, AK5558_00_POWER_MANAGEMENT1, 1, 0), + + SND_SOC_DAPM_AIF_OUT("SDTO", "Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route ak5558_intercon[] = { + {"ADC Ch1", NULL, "AIN1"}, + {"SDTO", NULL, "ADC Ch1"}, + + {"ADC Ch2", NULL, "AIN2"}, + {"SDTO", NULL, "ADC Ch2"}, + + {"ADC Ch3", NULL, "AIN3"}, + {"SDTO", NULL, "ADC Ch3"}, + + {"ADC Ch4", NULL, "AIN4"}, + {"SDTO", NULL, "ADC Ch4"}, + + {"ADC Ch5", NULL, "AIN5"}, + {"SDTO", NULL, "ADC Ch5"}, + + {"ADC Ch6", NULL, "AIN6"}, + {"SDTO", NULL, "ADC Ch6"}, + + {"ADC Ch7", NULL, "AIN7"}, + {"SDTO", NULL, "ADC Ch7"}, + + {"ADC Ch8", NULL, "AIN8"}, + {"SDTO", NULL, "ADC Ch8"}, +}; + +static const struct snd_soc_dapm_route ak5552_intercon[] = { + {"ADC Ch1", NULL, "AIN1"}, + {"SDTO", NULL, "ADC Ch1"}, + + {"ADC Ch2", NULL, "AIN2"}, + {"SDTO", NULL, "ADC Ch2"}, +}; + +static int ak5558_set_mcki(struct snd_soc_component *component) +{ + return snd_soc_component_update_bits(component, AK5558_02_CONTROL1, AK5558_CKS, + AK5558_CKS_AUTO); +} + +static int ak5558_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component); + u8 bits; + int pcm_width = max(params_physical_width(params), ak5558->slot_width); + + switch (pcm_width) { + case 16: + bits = AK5558_DIF_24BIT_MODE; + break; + case 32: + bits = AK5558_DIF_32BIT_MODE; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, AK5558_02_CONTROL1, AK5558_BITS, bits); + + return 0; +} + +static int ak5558_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u8 format; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + case SND_SOC_DAIFMT_CBP_CFP: + break; + case SND_SOC_DAIFMT_CBC_CFP: + case SND_SOC_DAIFMT_CBP_CFC: + default: + dev_err(dai->dev, "Clock mode unsupported"); + return -EINVAL; + } + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = AK5558_DIF_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = AK5558_DIF_MSB_MODE; + break; + case SND_SOC_DAIFMT_DSP_B: + format = AK5558_DIF_MSB_MODE; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, AK5558_02_CONTROL1, AK5558_DIF, format); + + return 0; +} + +static int ak5558_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, + int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component); + int tdm_mode; + + ak5558->slots = slots; + ak5558->slot_width = slot_width; + + switch (slots * slot_width) { + case 128: + tdm_mode = AK5558_MODE_TDM128; + break; + case 256: + tdm_mode = AK5558_MODE_TDM256; + break; + case 512: + tdm_mode = AK5558_MODE_TDM512; + break; + default: + tdm_mode = AK5558_MODE_NORMAL; + break; + } + + snd_soc_component_update_bits(component, AK5558_03_CONTROL2, AK5558_MODE_BITS, + tdm_mode); + return 0; +} + +#define AK5558_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static const unsigned int ak5558_rates[] = { + 8000, 11025, 16000, 22050, + 32000, 44100, 48000, 88200, + 96000, 176400, 192000, 352800, + 384000, 705600, 768000, 1411200, + 2822400, +}; + +static const struct snd_pcm_hw_constraint_list ak5558_rate_constraints = { + .count = ARRAY_SIZE(ak5558_rates), + .list = ak5558_rates, +}; + +static int ak5558_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &ak5558_rate_constraints); +} + +static const struct snd_soc_dai_ops ak5558_dai_ops = { + .startup = ak5558_startup, + .hw_params = ak5558_hw_params, + + .set_fmt = ak5558_set_dai_fmt, + .set_tdm_slot = ak5558_set_tdm_slot, +}; + +static struct snd_soc_dai_driver ak5558_dai = { + .name = "ak5558-aif", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = AK5558_FORMATS, + }, + .ops = &ak5558_dai_ops, +}; + +static struct snd_soc_dai_driver ak5552_dai = { + .name = "ak5552-aif", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = AK5558_FORMATS, + }, + .ops = &ak5558_dai_ops, +}; + +static void ak5558_reset(struct ak5558_priv *ak5558, bool active) +{ + if (!ak5558->reset_gpiod) + return; + + gpiod_set_value_cansleep(ak5558->reset_gpiod, active); + usleep_range(1000, 2000); +} + +static int ak5558_probe(struct snd_soc_component *component) +{ + struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component); + + ak5558_reset(ak5558, false); + return ak5558_set_mcki(component); +} + +static void ak5558_remove(struct snd_soc_component *component) +{ + struct ak5558_priv *ak5558 = snd_soc_component_get_drvdata(component); + + ak5558_reset(ak5558, true); +} + +static int __maybe_unused ak5558_runtime_suspend(struct device *dev) +{ + struct ak5558_priv *ak5558 = dev_get_drvdata(dev); + + regcache_cache_only(ak5558->regmap, true); + ak5558_reset(ak5558, true); + + regulator_bulk_disable(ARRAY_SIZE(ak5558->supplies), + ak5558->supplies); + return 0; +} + +static int __maybe_unused ak5558_runtime_resume(struct device *dev) +{ + struct ak5558_priv *ak5558 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ak5558->supplies), + ak5558->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ak5558_reset(ak5558, true); + ak5558_reset(ak5558, false); + + regcache_cache_only(ak5558->regmap, false); + regcache_mark_dirty(ak5558->regmap); + + return regcache_sync(ak5558->regmap); +} + +static const struct dev_pm_ops ak5558_pm = { + SET_RUNTIME_PM_OPS(ak5558_runtime_suspend, ak5558_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static const struct snd_soc_component_driver soc_codec_dev_ak5558 = { + .probe = ak5558_probe, + .remove = ak5558_remove, + .controls = ak5558_snd_controls, + .num_controls = ARRAY_SIZE(ak5558_snd_controls), + .dapm_widgets = ak5558_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak5558_dapm_widgets), + .dapm_routes = ak5558_intercon, + .num_dapm_routes = ARRAY_SIZE(ak5558_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_component_driver soc_codec_dev_ak5552 = { + .probe = ak5558_probe, + .remove = ak5558_remove, + .controls = ak5552_snd_controls, + .num_controls = ARRAY_SIZE(ak5552_snd_controls), + .dapm_widgets = ak5552_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak5552_dapm_widgets), + .dapm_routes = ak5552_intercon, + .num_dapm_routes = ARRAY_SIZE(ak5552_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config ak5558_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AK5558_05_DSD, + .reg_defaults = ak5558_reg, + .num_reg_defaults = ARRAY_SIZE(ak5558_reg), + .cache_type = REGCACHE_RBTREE, +}; + +static int ak5558_i2c_probe(struct i2c_client *i2c) +{ + struct ak5558_priv *ak5558; + int ret = 0; + int dev_id; + int i; + + ak5558 = devm_kzalloc(&i2c->dev, sizeof(*ak5558), GFP_KERNEL); + if (!ak5558) + return -ENOMEM; + + ak5558->regmap = devm_regmap_init_i2c(i2c, &ak5558_regmap); + if (IS_ERR(ak5558->regmap)) + return PTR_ERR(ak5558->regmap); + + i2c_set_clientdata(i2c, ak5558); + ak5558->i2c = i2c; + + ak5558->reset_gpiod = devm_gpiod_get_optional(&i2c->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ak5558->reset_gpiod)) + return PTR_ERR(ak5558->reset_gpiod); + + for (i = 0; i < ARRAY_SIZE(ak5558->supplies); i++) + ak5558->supplies[i].supply = ak5558_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(ak5558->supplies), + ak5558->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + dev_id = (uintptr_t)of_device_get_match_data(&i2c->dev); + switch (dev_id) { + case AK5552: + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_ak5552, + &ak5552_dai, 1); + break; + case AK5558: + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_ak5558, + &ak5558_dai, 1); + break; + default: + dev_err(&i2c->dev, "unexpected device type\n"); + return -EINVAL; + } + if (ret < 0) { + dev_err(&i2c->dev, "failed to register component: %d\n", ret); + return ret; + } + + pm_runtime_enable(&i2c->dev); + regcache_cache_only(ak5558->regmap, true); + + return 0; +} + +static void ak5558_i2c_remove(struct i2c_client *i2c) +{ + pm_runtime_disable(&i2c->dev); +} + +static const struct of_device_id ak5558_i2c_dt_ids[] __maybe_unused = { + { .compatible = "asahi-kasei,ak5558", .data = (void *) AK5558 }, + { .compatible = "asahi-kasei,ak5552", .data = (void *) AK5552 }, + { } +}; +MODULE_DEVICE_TABLE(of, ak5558_i2c_dt_ids); + +static struct i2c_driver ak5558_i2c_driver = { + .driver = { + .name = "ak5558", + .of_match_table = of_match_ptr(ak5558_i2c_dt_ids), + .pm = &ak5558_pm, + }, + .probe = ak5558_i2c_probe, + .remove = ak5558_i2c_remove, +}; + +module_i2c_driver(ak5558_i2c_driver); + +MODULE_AUTHOR("Junichi Wakasugi "); +MODULE_AUTHOR("Mihai Serban "); +MODULE_DESCRIPTION("ASoC AK5558 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ak5558.h b/sound/soc/codecs/ak5558.h new file mode 100644 index 0000000000..61059086f1 --- /dev/null +++ b/sound/soc/codecs/ak5558.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Audio driver header for AK5558 + * + * Copyright (C) 2016 Asahi Kasei Microdevices Corporation + * Copyright 2018 NXP + */ + +#ifndef _AK5558_H +#define _AK5558_H + +#define AK5558_00_POWER_MANAGEMENT1 0x00 +#define AK5558_01_POWER_MANAGEMENT2 0x01 +#define AK5558_02_CONTROL1 0x02 +#define AK5558_03_CONTROL2 0x03 +#define AK5558_04_CONTROL3 0x04 +#define AK5558_05_DSD 0x05 + +/* AK5558_02_CONTROL1 fields */ +#define AK5558_DIF GENMASK(1, 1) +#define AK5558_DIF_MSB_MODE (0 << 1) +#define AK5558_DIF_I2S_MODE (1 << 1) + +#define AK5558_BITS GENMASK(2, 2) +#define AK5558_DIF_24BIT_MODE (0 << 2) +#define AK5558_DIF_32BIT_MODE (1 << 2) + +#define AK5558_CKS GENMASK(6, 3) +#define AK5558_CKS_128FS_192KHZ (0 << 3) +#define AK5558_CKS_192FS_192KHZ (1 << 3) +#define AK5558_CKS_256FS_48KHZ (2 << 3) +#define AK5558_CKS_256FS_96KHZ (3 << 3) +#define AK5558_CKS_384FS_96KHZ (4 << 3) +#define AK5558_CKS_384FS_48KHZ (5 << 3) +#define AK5558_CKS_512FS_48KHZ (6 << 3) +#define AK5558_CKS_768FS_48KHZ (7 << 3) +#define AK5558_CKS_64FS_384KHZ (8 << 3) +#define AK5558_CKS_32FS_768KHZ (9 << 3) +#define AK5558_CKS_96FS_384KHZ (10 << 3) +#define AK5558_CKS_48FS_768KHZ (11 << 3) +#define AK5558_CKS_64FS_768KHZ (12 << 3) +#define AK5558_CKS_1024FS_16KHZ (13 << 3) +#define AK5558_CKS_AUTO (15 << 3) + +/* AK5558_03_CONTROL2 fields */ +#define AK5558_MODE_BITS GENMASK(6, 5) +#define AK5558_MODE_NORMAL (0 << 5) +#define AK5558_MODE_TDM128 (1 << 5) +#define AK5558_MODE_TDM256 (2 << 5) +#define AK5558_MODE_TDM512 (3 << 5) + +#endif diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c new file mode 100644 index 0000000000..b24c322068 --- /dev/null +++ b/sound/soc/codecs/alc5623.c @@ -0,0 +1,1094 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * alc5623.c -- alc562[123] ALSA Soc Audio driver + * + * Copyright 2008 Realtek Microelectronics + * Author: flove Ethan + * + * Copyright 2010 Arnaud Patard + * + * Based on WM8753.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "alc5623.h" + +static int caps_charge = 2000; +module_param(caps_charge, int, 0); +MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)"); + +/* codec private data */ +struct alc5623_priv { + struct regmap *regmap; + u8 id; + unsigned int sysclk; + unsigned int add_ctrl; + unsigned int jack_det_ctrl; +}; + +static inline int alc5623_reset(struct snd_soc_component *component) +{ + return snd_soc_component_write(component, ALC5623_RESET, 0); +} + +static int amp_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + /* to power-on/off class-d amp generators/speaker */ + /* need to write to 'index-46h' register : */ + /* so write index num (here 0x46) to reg 0x6a */ + /* and then 0xffff/0 to reg 0x6c */ + snd_soc_component_write(component, ALC5623_HID_CTRL_INDEX, 0x46); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, 0xFFFF); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, 0); + break; + } + + return 0; +} + +/* + * ALC5623 Controls + */ + +static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0); +static const DECLARE_TLV_DB_RANGE(boost_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0) +); +static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new alc5621_vol_snd_controls[] = { + SOC_DOUBLE_TLV("Speaker Playback Volume", + ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Speaker Playback Switch", + ALC5623_SPK_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE_TLV("Headphone Playback Volume", + ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Headphone Playback Switch", + ALC5623_HP_OUT_VOL, 15, 7, 1, 1), +}; + +static const struct snd_kcontrol_new alc5622_vol_snd_controls[] = { + SOC_DOUBLE_TLV("Speaker Playback Volume", + ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Speaker Playback Switch", + ALC5623_SPK_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE_TLV("Line Playback Volume", + ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Line Playback Switch", + ALC5623_HP_OUT_VOL, 15, 7, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_vol_snd_controls[] = { + SOC_DOUBLE_TLV("Line Playback Volume", + ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Line Playback Switch", + ALC5623_SPK_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE_TLV("Headphone Playback Volume", + ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Headphone Playback Switch", + ALC5623_HP_OUT_VOL, 15, 7, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_snd_controls[] = { + SOC_DOUBLE_TLV("Auxout Playback Volume", + ALC5623_MONO_AUX_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Auxout Playback Switch", + ALC5623_MONO_AUX_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE_TLV("PCM Playback Volume", + ALC5623_STEREO_DAC_VOL, 8, 0, 31, 1, vol_tlv), + SOC_DOUBLE_TLV("AuxI Capture Volume", + ALC5623_AUXIN_VOL, 8, 0, 31, 1, vol_tlv), + SOC_DOUBLE_TLV("LineIn Capture Volume", + ALC5623_LINE_IN_VOL, 8, 0, 31, 1, vol_tlv), + SOC_SINGLE_TLV("Mic1 Capture Volume", + ALC5623_MIC_VOL, 8, 31, 1, vol_tlv), + SOC_SINGLE_TLV("Mic2 Capture Volume", + ALC5623_MIC_VOL, 0, 31, 1, vol_tlv), + SOC_DOUBLE_TLV("Rec Capture Volume", + ALC5623_ADC_REC_GAIN, 7, 0, 31, 0, adc_rec_tlv), + SOC_SINGLE_TLV("Mic 1 Boost Volume", + ALC5623_MIC_CTRL, 10, 2, 0, boost_tlv), + SOC_SINGLE_TLV("Mic 2 Boost Volume", + ALC5623_MIC_CTRL, 8, 2, 0, boost_tlv), + SOC_SINGLE_TLV("Digital Boost Volume", + ALC5623_ADD_CTRL_REG, 4, 3, 0, dig_tlv), +}; + +/* + * DAPM Controls + */ +static const struct snd_kcontrol_new alc5623_hp_mixer_controls[] = { +SOC_DAPM_SINGLE("LI2HP Playback Switch", ALC5623_LINE_IN_VOL, 15, 1, 1), +SOC_DAPM_SINGLE("AUXI2HP Playback Switch", ALC5623_AUXIN_VOL, 15, 1, 1), +SOC_DAPM_SINGLE("MIC12HP Playback Switch", ALC5623_MIC_ROUTING_CTRL, 15, 1, 1), +SOC_DAPM_SINGLE("MIC22HP Playback Switch", ALC5623_MIC_ROUTING_CTRL, 7, 1, 1), +SOC_DAPM_SINGLE("DAC2HP Playback Switch", ALC5623_STEREO_DAC_VOL, 15, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_hpl_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC2HP_L Playback Switch", ALC5623_ADC_REC_GAIN, 15, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_hpr_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC2HP_R Playback Switch", ALC5623_ADC_REC_GAIN, 14, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC2MONO_L Playback Switch", ALC5623_ADC_REC_GAIN, 13, 1, 1), +SOC_DAPM_SINGLE("ADC2MONO_R Playback Switch", ALC5623_ADC_REC_GAIN, 12, 1, 1), +SOC_DAPM_SINGLE("LI2MONO Playback Switch", ALC5623_LINE_IN_VOL, 13, 1, 1), +SOC_DAPM_SINGLE("AUXI2MONO Playback Switch", ALC5623_AUXIN_VOL, 13, 1, 1), +SOC_DAPM_SINGLE("MIC12MONO Playback Switch", ALC5623_MIC_ROUTING_CTRL, 13, 1, 1), +SOC_DAPM_SINGLE("MIC22MONO Playback Switch", ALC5623_MIC_ROUTING_CTRL, 5, 1, 1), +SOC_DAPM_SINGLE("DAC2MONO Playback Switch", ALC5623_STEREO_DAC_VOL, 13, 1, 1), +}; + +static const struct snd_kcontrol_new alc5623_speaker_mixer_controls[] = { +SOC_DAPM_SINGLE("LI2SPK Playback Switch", ALC5623_LINE_IN_VOL, 14, 1, 1), +SOC_DAPM_SINGLE("AUXI2SPK Playback Switch", ALC5623_AUXIN_VOL, 14, 1, 1), +SOC_DAPM_SINGLE("MIC12SPK Playback Switch", ALC5623_MIC_ROUTING_CTRL, 14, 1, 1), +SOC_DAPM_SINGLE("MIC22SPK Playback Switch", ALC5623_MIC_ROUTING_CTRL, 6, 1, 1), +SOC_DAPM_SINGLE("DAC2SPK Playback Switch", ALC5623_STEREO_DAC_VOL, 14, 1, 1), +}; + +/* Left Record Mixer */ +static const struct snd_kcontrol_new alc5623_captureL_mixer_controls[] = { +SOC_DAPM_SINGLE("Mic1 Capture Switch", ALC5623_ADC_REC_MIXER, 14, 1, 1), +SOC_DAPM_SINGLE("Mic2 Capture Switch", ALC5623_ADC_REC_MIXER, 13, 1, 1), +SOC_DAPM_SINGLE("LineInL Capture Switch", ALC5623_ADC_REC_MIXER, 12, 1, 1), +SOC_DAPM_SINGLE("Left AuxI Capture Switch", ALC5623_ADC_REC_MIXER, 11, 1, 1), +SOC_DAPM_SINGLE("HPMixerL Capture Switch", ALC5623_ADC_REC_MIXER, 10, 1, 1), +SOC_DAPM_SINGLE("SPKMixer Capture Switch", ALC5623_ADC_REC_MIXER, 9, 1, 1), +SOC_DAPM_SINGLE("MonoMixer Capture Switch", ALC5623_ADC_REC_MIXER, 8, 1, 1), +}; + +/* Right Record Mixer */ +static const struct snd_kcontrol_new alc5623_captureR_mixer_controls[] = { +SOC_DAPM_SINGLE("Mic1 Capture Switch", ALC5623_ADC_REC_MIXER, 6, 1, 1), +SOC_DAPM_SINGLE("Mic2 Capture Switch", ALC5623_ADC_REC_MIXER, 5, 1, 1), +SOC_DAPM_SINGLE("LineInR Capture Switch", ALC5623_ADC_REC_MIXER, 4, 1, 1), +SOC_DAPM_SINGLE("Right AuxI Capture Switch", ALC5623_ADC_REC_MIXER, 3, 1, 1), +SOC_DAPM_SINGLE("HPMixerR Capture Switch", ALC5623_ADC_REC_MIXER, 2, 1, 1), +SOC_DAPM_SINGLE("SPKMixer Capture Switch", ALC5623_ADC_REC_MIXER, 1, 1, 1), +SOC_DAPM_SINGLE("MonoMixer Capture Switch", ALC5623_ADC_REC_MIXER, 0, 1, 1), +}; + +static const char *alc5623_spk_n_sour_sel[] = { + "RN/-R", "RP/+R", "LN/-R", "Vmid" }; +static const char *alc5623_hpl_out_input_sel[] = { + "Vmid", "HP Left Mix"}; +static const char *alc5623_hpr_out_input_sel[] = { + "Vmid", "HP Right Mix"}; +static const char *alc5623_spkout_input_sel[] = { + "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"}; +static const char *alc5623_aux_out_input_sel[] = { + "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"}; + +/* auxout output mux */ +static SOC_ENUM_SINGLE_DECL(alc5623_aux_out_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 6, + alc5623_aux_out_input_sel); +static const struct snd_kcontrol_new alc5623_auxout_mux_controls = +SOC_DAPM_ENUM("Route", alc5623_aux_out_input_enum); + +/* speaker output mux */ +static SOC_ENUM_SINGLE_DECL(alc5623_spkout_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 10, + alc5623_spkout_input_sel); +static const struct snd_kcontrol_new alc5623_spkout_mux_controls = +SOC_DAPM_ENUM("Route", alc5623_spkout_input_enum); + +/* headphone left output mux */ +static SOC_ENUM_SINGLE_DECL(alc5623_hpl_out_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 9, + alc5623_hpl_out_input_sel); +static const struct snd_kcontrol_new alc5623_hpl_out_mux_controls = +SOC_DAPM_ENUM("Route", alc5623_hpl_out_input_enum); + +/* headphone right output mux */ +static SOC_ENUM_SINGLE_DECL(alc5623_hpr_out_input_enum, + ALC5623_OUTPUT_MIXER_CTRL, 8, + alc5623_hpr_out_input_sel); +static const struct snd_kcontrol_new alc5623_hpr_out_mux_controls = +SOC_DAPM_ENUM("Route", alc5623_hpr_out_input_enum); + +/* speaker output N select */ +static SOC_ENUM_SINGLE_DECL(alc5623_spk_n_sour_enum, + ALC5623_OUTPUT_MIXER_CTRL, 14, + alc5623_spk_n_sour_sel); +static const struct snd_kcontrol_new alc5623_spkoutn_mux_controls = +SOC_DAPM_ENUM("Route", alc5623_spk_n_sour_enum); + +static const struct snd_soc_dapm_widget alc5623_dapm_widgets[] = { +/* Muxes */ +SND_SOC_DAPM_MUX("AuxOut Mux", SND_SOC_NOPM, 0, 0, + &alc5623_auxout_mux_controls), +SND_SOC_DAPM_MUX("SpeakerOut Mux", SND_SOC_NOPM, 0, 0, + &alc5623_spkout_mux_controls), +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, + &alc5623_hpl_out_mux_controls), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, + &alc5623_hpr_out_mux_controls), +SND_SOC_DAPM_MUX("SpeakerOut N Mux", SND_SOC_NOPM, 0, 0, + &alc5623_spkoutn_mux_controls), + +/* output mixers */ +SND_SOC_DAPM_MIXER("HP Mix", SND_SOC_NOPM, 0, 0, + &alc5623_hp_mixer_controls[0], + ARRAY_SIZE(alc5623_hp_mixer_controls)), +SND_SOC_DAPM_MIXER("HPR Mix", ALC5623_PWR_MANAG_ADD2, 4, 0, + &alc5623_hpr_mixer_controls[0], + ARRAY_SIZE(alc5623_hpr_mixer_controls)), +SND_SOC_DAPM_MIXER("HPL Mix", ALC5623_PWR_MANAG_ADD2, 5, 0, + &alc5623_hpl_mixer_controls[0], + ARRAY_SIZE(alc5623_hpl_mixer_controls)), +SND_SOC_DAPM_MIXER("HPOut Mix", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Mono Mix", ALC5623_PWR_MANAG_ADD2, 2, 0, + &alc5623_mono_mixer_controls[0], + ARRAY_SIZE(alc5623_mono_mixer_controls)), +SND_SOC_DAPM_MIXER("Speaker Mix", ALC5623_PWR_MANAG_ADD2, 3, 0, + &alc5623_speaker_mixer_controls[0], + ARRAY_SIZE(alc5623_speaker_mixer_controls)), + +/* input mixers */ +SND_SOC_DAPM_MIXER("Left Capture Mix", ALC5623_PWR_MANAG_ADD2, 1, 0, + &alc5623_captureL_mixer_controls[0], + ARRAY_SIZE(alc5623_captureL_mixer_controls)), +SND_SOC_DAPM_MIXER("Right Capture Mix", ALC5623_PWR_MANAG_ADD2, 0, 0, + &alc5623_captureR_mixer_controls[0], + ARRAY_SIZE(alc5623_captureR_mixer_controls)), + +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", + ALC5623_PWR_MANAG_ADD2, 9, 0), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", + ALC5623_PWR_MANAG_ADD2, 8, 0), +SND_SOC_DAPM_MIXER("I2S Mix", ALC5623_PWR_MANAG_ADD1, 15, 0, NULL, 0), +SND_SOC_DAPM_MIXER("AuxI Mix", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Line Mix", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", + ALC5623_PWR_MANAG_ADD2, 7, 0), +SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", + ALC5623_PWR_MANAG_ADD2, 6, 0), +SND_SOC_DAPM_PGA("Left Headphone", ALC5623_PWR_MANAG_ADD3, 10, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Headphone", ALC5623_PWR_MANAG_ADD3, 9, 0, NULL, 0), +SND_SOC_DAPM_PGA("SpeakerOut", ALC5623_PWR_MANAG_ADD3, 12, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left AuxOut", ALC5623_PWR_MANAG_ADD3, 14, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right AuxOut", ALC5623_PWR_MANAG_ADD3, 13, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left LineIn", ALC5623_PWR_MANAG_ADD3, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right LineIn", ALC5623_PWR_MANAG_ADD3, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left AuxI", ALC5623_PWR_MANAG_ADD3, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right AuxI", ALC5623_PWR_MANAG_ADD3, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC1 PGA", ALC5623_PWR_MANAG_ADD3, 3, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC2 PGA", ALC5623_PWR_MANAG_ADD3, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC1 Pre Amp", ALC5623_PWR_MANAG_ADD3, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC2 Pre Amp", ALC5623_PWR_MANAG_ADD3, 0, 0, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias1", ALC5623_PWR_MANAG_ADD1, 11, 0), + +SND_SOC_DAPM_OUTPUT("AUXOUTL"), +SND_SOC_DAPM_OUTPUT("AUXOUTR"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_OUTPUT("SPKOUT"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +SND_SOC_DAPM_INPUT("LINEINL"), +SND_SOC_DAPM_INPUT("LINEINR"), +SND_SOC_DAPM_INPUT("AUXINL"), +SND_SOC_DAPM_INPUT("AUXINR"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +SND_SOC_DAPM_VMID("Vmid"), +}; + +static const char *alc5623_amp_names[] = {"AB Amp", "D Amp"}; +static SOC_ENUM_SINGLE_DECL(alc5623_amp_enum, + ALC5623_OUTPUT_MIXER_CTRL, 13, + alc5623_amp_names); +static const struct snd_kcontrol_new alc5623_amp_mux_controls = + SOC_DAPM_ENUM("Route", alc5623_amp_enum); + +static const struct snd_soc_dapm_widget alc5623_dapm_amp_widgets[] = { +SND_SOC_DAPM_PGA_E("D Amp", ALC5623_PWR_MANAG_ADD2, 14, 0, NULL, 0, + amp_mixer_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_PGA("AB Amp", ALC5623_PWR_MANAG_ADD2, 15, 0, NULL, 0), +SND_SOC_DAPM_MUX("AB-D Amp Mux", SND_SOC_NOPM, 0, 0, + &alc5623_amp_mux_controls), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* virtual mixer - mixes left & right channels */ + {"I2S Mix", NULL, "Left DAC"}, + {"I2S Mix", NULL, "Right DAC"}, + {"Line Mix", NULL, "Right LineIn"}, + {"Line Mix", NULL, "Left LineIn"}, + {"AuxI Mix", NULL, "Left AuxI"}, + {"AuxI Mix", NULL, "Right AuxI"}, + {"AUXOUTL", NULL, "Left AuxOut"}, + {"AUXOUTR", NULL, "Right AuxOut"}, + + /* HP mixer */ + {"HPL Mix", "ADC2HP_L Playback Switch", "Left Capture Mix"}, + {"HPL Mix", NULL, "HP Mix"}, + {"HPR Mix", "ADC2HP_R Playback Switch", "Right Capture Mix"}, + {"HPR Mix", NULL, "HP Mix"}, + {"HP Mix", "LI2HP Playback Switch", "Line Mix"}, + {"HP Mix", "AUXI2HP Playback Switch", "AuxI Mix"}, + {"HP Mix", "MIC12HP Playback Switch", "MIC1 PGA"}, + {"HP Mix", "MIC22HP Playback Switch", "MIC2 PGA"}, + {"HP Mix", "DAC2HP Playback Switch", "I2S Mix"}, + + /* speaker mixer */ + {"Speaker Mix", "LI2SPK Playback Switch", "Line Mix"}, + {"Speaker Mix", "AUXI2SPK Playback Switch", "AuxI Mix"}, + {"Speaker Mix", "MIC12SPK Playback Switch", "MIC1 PGA"}, + {"Speaker Mix", "MIC22SPK Playback Switch", "MIC2 PGA"}, + {"Speaker Mix", "DAC2SPK Playback Switch", "I2S Mix"}, + + /* mono mixer */ + {"Mono Mix", "ADC2MONO_L Playback Switch", "Left Capture Mix"}, + {"Mono Mix", "ADC2MONO_R Playback Switch", "Right Capture Mix"}, + {"Mono Mix", "LI2MONO Playback Switch", "Line Mix"}, + {"Mono Mix", "AUXI2MONO Playback Switch", "AuxI Mix"}, + {"Mono Mix", "MIC12MONO Playback Switch", "MIC1 PGA"}, + {"Mono Mix", "MIC22MONO Playback Switch", "MIC2 PGA"}, + {"Mono Mix", "DAC2MONO Playback Switch", "I2S Mix"}, + + /* Left record mixer */ + {"Left Capture Mix", "LineInL Capture Switch", "LINEINL"}, + {"Left Capture Mix", "Left AuxI Capture Switch", "AUXINL"}, + {"Left Capture Mix", "Mic1 Capture Switch", "MIC1 Pre Amp"}, + {"Left Capture Mix", "Mic2 Capture Switch", "MIC2 Pre Amp"}, + {"Left Capture Mix", "HPMixerL Capture Switch", "HPL Mix"}, + {"Left Capture Mix", "SPKMixer Capture Switch", "Speaker Mix"}, + {"Left Capture Mix", "MonoMixer Capture Switch", "Mono Mix"}, + + /*Right record mixer */ + {"Right Capture Mix", "LineInR Capture Switch", "LINEINR"}, + {"Right Capture Mix", "Right AuxI Capture Switch", "AUXINR"}, + {"Right Capture Mix", "Mic1 Capture Switch", "MIC1 Pre Amp"}, + {"Right Capture Mix", "Mic2 Capture Switch", "MIC2 Pre Amp"}, + {"Right Capture Mix", "HPMixerR Capture Switch", "HPR Mix"}, + {"Right Capture Mix", "SPKMixer Capture Switch", "Speaker Mix"}, + {"Right Capture Mix", "MonoMixer Capture Switch", "Mono Mix"}, + + /* headphone left mux */ + {"Left Headphone Mux", "HP Left Mix", "HPL Mix"}, + {"Left Headphone Mux", "Vmid", "Vmid"}, + + /* headphone right mux */ + {"Right Headphone Mux", "HP Right Mix", "HPR Mix"}, + {"Right Headphone Mux", "Vmid", "Vmid"}, + + /* speaker out mux */ + {"SpeakerOut Mux", "Vmid", "Vmid"}, + {"SpeakerOut Mux", "HPOut Mix", "HPOut Mix"}, + {"SpeakerOut Mux", "Speaker Mix", "Speaker Mix"}, + {"SpeakerOut Mux", "Mono Mix", "Mono Mix"}, + + /* Mono/Aux Out mux */ + {"AuxOut Mux", "Vmid", "Vmid"}, + {"AuxOut Mux", "HPOut Mix", "HPOut Mix"}, + {"AuxOut Mux", "Speaker Mix", "Speaker Mix"}, + {"AuxOut Mux", "Mono Mix", "Mono Mix"}, + + /* output pga */ + {"HPL", NULL, "Left Headphone"}, + {"Left Headphone", NULL, "Left Headphone Mux"}, + {"HPR", NULL, "Right Headphone"}, + {"Right Headphone", NULL, "Right Headphone Mux"}, + {"Left AuxOut", NULL, "AuxOut Mux"}, + {"Right AuxOut", NULL, "AuxOut Mux"}, + + /* input pga */ + {"Left LineIn", NULL, "LINEINL"}, + {"Right LineIn", NULL, "LINEINR"}, + {"Left AuxI", NULL, "AUXINL"}, + {"Right AuxI", NULL, "AUXINR"}, + {"MIC1 Pre Amp", NULL, "MIC1"}, + {"MIC2 Pre Amp", NULL, "MIC2"}, + {"MIC1 PGA", NULL, "MIC1 Pre Amp"}, + {"MIC2 PGA", NULL, "MIC2 Pre Amp"}, + + /* left ADC */ + {"Left ADC", NULL, "Left Capture Mix"}, + + /* right ADC */ + {"Right ADC", NULL, "Right Capture Mix"}, + + {"SpeakerOut N Mux", "RN/-R", "SpeakerOut"}, + {"SpeakerOut N Mux", "RP/+R", "SpeakerOut"}, + {"SpeakerOut N Mux", "LN/-R", "SpeakerOut"}, + {"SpeakerOut N Mux", "Vmid", "Vmid"}, + + {"SPKOUT", NULL, "SpeakerOut"}, + {"SPKOUTN", NULL, "SpeakerOut N Mux"}, +}; + +static const struct snd_soc_dapm_route intercon_spk[] = { + {"SpeakerOut", NULL, "SpeakerOut Mux"}, +}; + +static const struct snd_soc_dapm_route intercon_amp_spk[] = { + {"AB Amp", NULL, "SpeakerOut Mux"}, + {"D Amp", NULL, "SpeakerOut Mux"}, + {"AB-D Amp Mux", "AB Amp", "AB Amp"}, + {"AB-D Amp Mux", "D Amp", "D Amp"}, + {"SpeakerOut", NULL, "AB-D Amp Mux"}, +}; + +/* PLL divisors */ +struct _pll_div { + u32 pll_in; + u32 pll_out; + u16 regvalue; +}; + +/* Note : pll code from original alc5623 driver. Not sure of how good it is */ +/* useful only for master mode */ +static const struct _pll_div codec_master_pll_div[] = { + + { 2048000, 8192000, 0x0ea0}, + { 3686400, 8192000, 0x4e27}, + { 12000000, 8192000, 0x456b}, + { 13000000, 8192000, 0x495f}, + { 13100000, 8192000, 0x0320}, + { 2048000, 11289600, 0xf637}, + { 3686400, 11289600, 0x2f22}, + { 12000000, 11289600, 0x3e2f}, + { 13000000, 11289600, 0x4d5b}, + { 13100000, 11289600, 0x363b}, + { 2048000, 16384000, 0x1ea0}, + { 3686400, 16384000, 0x9e27}, + { 12000000, 16384000, 0x452b}, + { 13000000, 16384000, 0x542f}, + { 13100000, 16384000, 0x03a0}, + { 2048000, 16934400, 0xe625}, + { 3686400, 16934400, 0x9126}, + { 12000000, 16934400, 0x4d2c}, + { 13000000, 16934400, 0x742f}, + { 13100000, 16934400, 0x3c27}, + { 2048000, 22579200, 0x2aa0}, + { 3686400, 22579200, 0x2f20}, + { 12000000, 22579200, 0x7e2f}, + { 13000000, 22579200, 0x742f}, + { 13100000, 22579200, 0x3c27}, + { 2048000, 24576000, 0x2ea0}, + { 3686400, 24576000, 0xee27}, + { 12000000, 24576000, 0x2915}, + { 13000000, 24576000, 0x772e}, + { 13100000, 24576000, 0x0d20}, +}; + +static const struct _pll_div codec_slave_pll_div[] = { + + { 1024000, 16384000, 0x3ea0}, + { 1411200, 22579200, 0x3ea0}, + { 1536000, 24576000, 0x3ea0}, + { 2048000, 16384000, 0x1ea0}, + { 2822400, 22579200, 0x1ea0}, + { 3072000, 24576000, 0x1ea0}, + +}; + +static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + int i; + struct snd_soc_component *component = codec_dai->component; + int gbl_clk = 0, pll_div = 0; + u16 reg; + + if (pll_id < ALC5623_PLL_FR_MCLK || pll_id > ALC5623_PLL_FR_BCK) + return -ENODEV; + + /* Disable PLL power */ + snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD2, + ALC5623_PWR_ADD2_PLL, + 0); + + /* pll is not used in slave mode */ + reg = snd_soc_component_read(component, ALC5623_DAI_CONTROL); + if (reg & ALC5623_DAI_SDP_SLAVE_MODE) + return 0; + + if (!freq_in || !freq_out) + return 0; + + switch (pll_id) { + case ALC5623_PLL_FR_MCLK: + for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) { + if (codec_master_pll_div[i].pll_in == freq_in + && codec_master_pll_div[i].pll_out == freq_out) { + /* PLL source from MCLK */ + pll_div = codec_master_pll_div[i].regvalue; + break; + } + } + break; + case ALC5623_PLL_FR_BCK: + for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) { + if (codec_slave_pll_div[i].pll_in == freq_in + && codec_slave_pll_div[i].pll_out == freq_out) { + /* PLL source from Bitclk */ + gbl_clk = ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK; + pll_div = codec_slave_pll_div[i].regvalue; + break; + } + } + break; + default: + return -EINVAL; + } + + if (!pll_div) + return -EINVAL; + + snd_soc_component_write(component, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk); + snd_soc_component_write(component, ALC5623_PLL_CTRL, pll_div); + snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD2, + ALC5623_PWR_ADD2_PLL, + ALC5623_PWR_ADD2_PLL); + gbl_clk |= ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL; + snd_soc_component_write(component, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk); + + return 0; +} + +struct _coeff_div { + u16 fs; + u16 regvalue; +}; + +/* codec hifi mclk (after PLL) clock divider coefficients */ +/* values inspired from column BCLK=32Fs of Appendix A table */ +static const struct _coeff_div coeff_div[] = { + {256*8, 0x3a69}, + {384*8, 0x3c6b}, + {256*4, 0x2a69}, + {384*4, 0x2c6b}, + {256*2, 0x1a69}, + {384*2, 0x1c6b}, + {256*1, 0x0a69}, + {384*1, 0x0c6b}, +}; + +static int get_coeff(struct snd_soc_component *component, int rate) +{ + struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component); + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].fs * rate == alc5623->sysclk) + return i; + } + return -EINVAL; +} + +/* + * Clock after PLL and dividers + */ +static int alc5623_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case 8192000: + case 11289600: + case 12288000: + case 16384000: + case 16934400: + case 18432000: + case 22579200: + case 24576000: + alc5623->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = 0; + + /* set audio interface clocking */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + iface = ALC5623_DAI_SDP_MASTER_MODE; + break; + case SND_SOC_DAIFMT_CBC_CFC: + iface = ALC5623_DAI_SDP_SLAVE_MODE; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= ALC5623_DAI_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface |= ALC5623_DAI_I2S_DF_RIGHT; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= ALC5623_DAI_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= ALC5623_DAI_I2S_DF_PCM; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= ALC5623_DAI_I2S_DF_PCM | ALC5623_DAI_I2S_PCM_MODE; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL; + break; + case SND_SOC_DAIFMT_NB_IF: + break; + default: + return -EINVAL; + } + + return snd_soc_component_write(component, ALC5623_DAI_CONTROL, iface); +} + +static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component); + int coeff, rate; + u16 iface; + + iface = snd_soc_component_read(component, ALC5623_DAI_CONTROL); + iface &= ~ALC5623_DAI_I2S_DL_MASK; + + /* bit size */ + switch (params_width(params)) { + case 16: + iface |= ALC5623_DAI_I2S_DL_16; + break; + case 20: + iface |= ALC5623_DAI_I2S_DL_20; + break; + case 24: + iface |= ALC5623_DAI_I2S_DL_24; + break; + case 32: + iface |= ALC5623_DAI_I2S_DL_32; + break; + default: + return -EINVAL; + } + + /* set iface & srate */ + snd_soc_component_write(component, ALC5623_DAI_CONTROL, iface); + rate = params_rate(params); + coeff = get_coeff(component, rate); + if (coeff < 0) + return -EINVAL; + + coeff = coeff_div[coeff].regvalue; + dev_dbg(component->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n", + __func__, alc5623->sysclk, rate, coeff); + snd_soc_component_write(component, ALC5623_STEREO_AD_DA_CLK_CTRL, coeff); + + return 0; +} + +static int alc5623_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 hp_mute = ALC5623_MISC_M_DAC_L_INPUT | ALC5623_MISC_M_DAC_R_INPUT; + u16 mute_reg = snd_soc_component_read(component, ALC5623_MISC_CTRL) & ~hp_mute; + + if (mute) + mute_reg |= hp_mute; + + return snd_soc_component_write(component, ALC5623_MISC_CTRL, mute_reg); +} + +#define ALC5623_ADD2_POWER_EN (ALC5623_PWR_ADD2_VREF \ + | ALC5623_PWR_ADD2_DAC_REF_CIR) + +#define ALC5623_ADD3_POWER_EN (ALC5623_PWR_ADD3_MAIN_BIAS \ + | ALC5623_PWR_ADD3_MIC1_BOOST_AD) + +#define ALC5623_ADD1_POWER_EN \ + (ALC5623_PWR_ADD1_SHORT_CURR_DET_EN | ALC5623_PWR_ADD1_SOFTGEN_EN \ + | ALC5623_PWR_ADD1_DEPOP_BUF_HP | ALC5623_PWR_ADD1_HP_OUT_AMP \ + | ALC5623_PWR_ADD1_HP_OUT_ENH_AMP) + +#define ALC5623_ADD1_POWER_EN_5622 \ + (ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \ + | ALC5623_PWR_ADD1_HP_OUT_AMP) + +static void enable_power_depop(struct snd_soc_component *component) +{ + struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD1, + ALC5623_PWR_ADD1_SOFTGEN_EN, + ALC5623_PWR_ADD1_SOFTGEN_EN); + + snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN); + + snd_soc_component_update_bits(component, ALC5623_MISC_CTRL, + ALC5623_MISC_HP_DEPOP_MODE2_EN, + ALC5623_MISC_HP_DEPOP_MODE2_EN); + + msleep(500); + + snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2, ALC5623_ADD2_POWER_EN); + + /* avoid writing '1' into 5622 reserved bits */ + if (alc5623->id == 0x22) + snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1, + ALC5623_ADD1_POWER_EN_5622); + else + snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1, + ALC5623_ADD1_POWER_EN); + + /* disable HP Depop2 */ + snd_soc_component_update_bits(component, ALC5623_MISC_CTRL, + ALC5623_MISC_HP_DEPOP_MODE2_EN, + 0); + +} + +static int alc5623_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + enable_power_depop(component); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* everything off except vref/vmid, */ + snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2, + ALC5623_PWR_ADD2_VREF); + snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, + ALC5623_PWR_ADD3_MAIN_BIAS); + break; + case SND_SOC_BIAS_OFF: + /* everything off, dac mute, inactive */ + snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2, 0); + snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, 0); + snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1, 0); + break; + } + return 0; +} + +#define ALC5623_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \ + | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops alc5623_dai_ops = { + .hw_params = alc5623_pcm_hw_params, + .mute_stream = alc5623_mute, + .set_fmt = alc5623_set_dai_fmt, + .set_sysclk = alc5623_set_dai_sysclk, + .set_pll = alc5623_set_dai_pll, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver alc5623_dai = { + .name = "alc5623-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ALC5623_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ALC5623_FORMATS,}, + + .ops = &alc5623_dai_ops, +}; + +static int alc5623_suspend(struct snd_soc_component *component) +{ + struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(alc5623->regmap, true); + + return 0; +} + +static int alc5623_resume(struct snd_soc_component *component) +{ + struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component); + int ret; + + /* Sync reg_cache with the hardware */ + regcache_cache_only(alc5623->regmap, false); + ret = regcache_sync(alc5623->regmap); + if (ret != 0) { + dev_err(component->dev, "Failed to sync register cache: %d\n", + ret); + regcache_cache_only(alc5623->regmap, true); + return ret; + } + + return 0; +} + +static int alc5623_probe(struct snd_soc_component *component) +{ + struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + alc5623_reset(component); + + if (alc5623->add_ctrl) { + snd_soc_component_write(component, ALC5623_ADD_CTRL_REG, + alc5623->add_ctrl); + } + + if (alc5623->jack_det_ctrl) { + snd_soc_component_write(component, ALC5623_JACK_DET_CTRL, + alc5623->jack_det_ctrl); + } + + switch (alc5623->id) { + case 0x21: + snd_soc_add_component_controls(component, alc5621_vol_snd_controls, + ARRAY_SIZE(alc5621_vol_snd_controls)); + break; + case 0x22: + snd_soc_add_component_controls(component, alc5622_vol_snd_controls, + ARRAY_SIZE(alc5622_vol_snd_controls)); + break; + case 0x23: + snd_soc_add_component_controls(component, alc5623_vol_snd_controls, + ARRAY_SIZE(alc5623_vol_snd_controls)); + break; + default: + return -EINVAL; + } + + snd_soc_add_component_controls(component, alc5623_snd_controls, + ARRAY_SIZE(alc5623_snd_controls)); + + snd_soc_dapm_new_controls(dapm, alc5623_dapm_widgets, + ARRAY_SIZE(alc5623_dapm_widgets)); + + /* set up audio path interconnects */ + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + + switch (alc5623->id) { + case 0x21: + case 0x22: + snd_soc_dapm_new_controls(dapm, alc5623_dapm_amp_widgets, + ARRAY_SIZE(alc5623_dapm_amp_widgets)); + snd_soc_dapm_add_routes(dapm, intercon_amp_spk, + ARRAY_SIZE(intercon_amp_spk)); + break; + case 0x23: + snd_soc_dapm_add_routes(dapm, intercon_spk, + ARRAY_SIZE(intercon_spk)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_component_driver soc_component_device_alc5623 = { + .probe = alc5623_probe, + .suspend = alc5623_suspend, + .resume = alc5623_resume, + .set_bias_level = alc5623_set_bias_level, + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config alc5623_regmap = { + .reg_bits = 8, + .val_bits = 16, + .reg_stride = 2, + + .max_register = ALC5623_VENDOR_ID2, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct i2c_device_id alc5623_i2c_table[] = { + {"alc5621", 0x21}, + {"alc5622", 0x22}, + {"alc5623", 0x23}, + {} +}; +MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table); + +/* + * ALC5623 2 wire address is determined by A1 pin + * state during powerup. + * low = 0x1a + * high = 0x1b + */ +static int alc5623_i2c_probe(struct i2c_client *client) +{ + struct alc5623_platform_data *pdata; + struct alc5623_priv *alc5623; + struct device_node *np; + unsigned int vid1, vid2; + int ret; + u32 val32; + const struct i2c_device_id *id; + + alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv), + GFP_KERNEL); + if (alc5623 == NULL) + return -ENOMEM; + + alc5623->regmap = devm_regmap_init_i2c(client, &alc5623_regmap); + if (IS_ERR(alc5623->regmap)) { + ret = PTR_ERR(alc5623->regmap); + dev_err(&client->dev, "Failed to initialise I/O: %d\n", ret); + return ret; + } + + ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID1, &vid1); + if (ret < 0) { + dev_err(&client->dev, "failed to read vendor ID1: %d\n", ret); + return ret; + } + + ret = regmap_read(alc5623->regmap, ALC5623_VENDOR_ID2, &vid2); + if (ret < 0) { + dev_err(&client->dev, "failed to read vendor ID2: %d\n", ret); + return ret; + } + vid2 >>= 8; + + id = i2c_match_id(alc5623_i2c_table, client); + + if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) { + dev_err(&client->dev, "unknown or wrong codec\n"); + dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n", + 0x10ec, id->driver_data, + vid1, vid2); + return -ENODEV; + } + + dev_dbg(&client->dev, "Found codec id : alc56%02x\n", vid2); + + pdata = client->dev.platform_data; + if (pdata) { + alc5623->add_ctrl = pdata->add_ctrl; + alc5623->jack_det_ctrl = pdata->jack_det_ctrl; + } else { + if (client->dev.of_node) { + np = client->dev.of_node; + ret = of_property_read_u32(np, "add-ctrl", &val32); + if (!ret) + alc5623->add_ctrl = val32; + ret = of_property_read_u32(np, "jack-det-ctrl", &val32); + if (!ret) + alc5623->jack_det_ctrl = val32; + } + } + + alc5623->id = vid2; + switch (alc5623->id) { + case 0x21: + alc5623_dai.name = "alc5621-hifi"; + break; + case 0x22: + alc5623_dai.name = "alc5622-hifi"; + break; + case 0x23: + alc5623_dai.name = "alc5623-hifi"; + break; + default: + return -EINVAL; + } + + i2c_set_clientdata(client, alc5623); + + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_device_alc5623, &alc5623_dai, 1); + if (ret != 0) + dev_err(&client->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id alc5623_of_match[] = { + { .compatible = "realtek,alc5623", }, + { } +}; +MODULE_DEVICE_TABLE(of, alc5623_of_match); +#endif + +/* i2c codec control layer */ +static struct i2c_driver alc5623_i2c_driver = { + .driver = { + .name = "alc562x-codec", + .of_match_table = of_match_ptr(alc5623_of_match), + }, + .probe = alc5623_i2c_probe, + .id_table = alc5623_i2c_table, +}; + +module_i2c_driver(alc5623_i2c_driver); + +MODULE_DESCRIPTION("ASoC alc5621/2/3 driver"); +MODULE_AUTHOR("Arnaud Patard "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/alc5623.h b/sound/soc/codecs/alc5623.h new file mode 100644 index 0000000000..1dd88c7725 --- /dev/null +++ b/sound/soc/codecs/alc5623.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * alc5623.h -- alc562[123] ALSA Soc Audio driver + * + * Copyright 2008 Realtek Microelectronics + * Copyright 2010 Arnaud Patard + * + * Author: flove + * Arnaud Patard + */ + +#ifndef _ALC5623_H +#define _ALC5623_H + +#define ALC5623_RESET 0x00 +/* 5621 5622 5623 */ +/* speaker output vol 2 2 */ +/* line output vol 4 2 */ +/* HP output vol 4 0 4 */ +#define ALC5623_SPK_OUT_VOL 0x02 +#define ALC5623_HP_OUT_VOL 0x04 +#define ALC5623_MONO_AUX_OUT_VOL 0x06 +#define ALC5623_AUXIN_VOL 0x08 +#define ALC5623_LINE_IN_VOL 0x0A +#define ALC5623_STEREO_DAC_VOL 0x0C +#define ALC5623_MIC_VOL 0x0E +#define ALC5623_MIC_ROUTING_CTRL 0x10 +#define ALC5623_ADC_REC_GAIN 0x12 +#define ALC5623_ADC_REC_MIXER 0x14 +#define ALC5623_SOFT_VOL_CTRL_TIME 0x16 +/* ALC5623_OUTPUT_MIXER_CTRL : */ +/* same remark as for reg 2 line vs speaker */ +#define ALC5623_OUTPUT_MIXER_CTRL 0x1C +#define ALC5623_MIC_CTRL 0x22 + +#define ALC5623_DAI_CONTROL 0x34 +#define ALC5623_DAI_SDP_MASTER_MODE (0 << 15) +#define ALC5623_DAI_SDP_SLAVE_MODE (1 << 15) +#define ALC5623_DAI_I2S_PCM_MODE (1 << 14) +#define ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL (1 << 7) +#define ALC5623_DAI_ADC_DATA_L_R_SWAP (1 << 5) +#define ALC5623_DAI_DAC_DATA_L_R_SWAP (1 << 4) +#define ALC5623_DAI_I2S_DL_MASK (3 << 2) +#define ALC5623_DAI_I2S_DL_32 (3 << 2) +#define ALC5623_DAI_I2S_DL_24 (2 << 2) +#define ALC5623_DAI_I2S_DL_20 (1 << 2) +#define ALC5623_DAI_I2S_DL_16 (0 << 2) +#define ALC5623_DAI_I2S_DF_PCM (3 << 0) +#define ALC5623_DAI_I2S_DF_LEFT (2 << 0) +#define ALC5623_DAI_I2S_DF_RIGHT (1 << 0) +#define ALC5623_DAI_I2S_DF_I2S (0 << 0) + +#define ALC5623_STEREO_AD_DA_CLK_CTRL 0x36 +#define ALC5623_COMPANDING_CTRL 0x38 + +#define ALC5623_PWR_MANAG_ADD1 0x3A +#define ALC5623_PWR_ADD1_MAIN_I2S_EN (1 << 15) +#define ALC5623_PWR_ADD1_ZC_DET_PD_EN (1 << 14) +#define ALC5623_PWR_ADD1_MIC1_BIAS_EN (1 << 11) +#define ALC5623_PWR_ADD1_SHORT_CURR_DET_EN (1 << 10) +#define ALC5623_PWR_ADD1_SOFTGEN_EN (1 << 8) /* rsvd on 5622 */ +#define ALC5623_PWR_ADD1_DEPOP_BUF_HP (1 << 6) /* rsvd on 5622 */ +#define ALC5623_PWR_ADD1_HP_OUT_AMP (1 << 5) +#define ALC5623_PWR_ADD1_HP_OUT_ENH_AMP (1 << 4) /* rsvd on 5622 */ +#define ALC5623_PWR_ADD1_DEPOP_BUF_AUX (1 << 2) +#define ALC5623_PWR_ADD1_AUX_OUT_AMP (1 << 1) +#define ALC5623_PWR_ADD1_AUX_OUT_ENH_AMP (1 << 0) /* rsvd on 5622 */ + +#define ALC5623_PWR_MANAG_ADD2 0x3C +#define ALC5623_PWR_ADD2_LINEOUT (1 << 15) /* rt5623 */ +#define ALC5623_PWR_ADD2_CLASS_AB (1 << 15) /* rt5621 */ +#define ALC5623_PWR_ADD2_CLASS_D (1 << 14) /* rt5621 */ +#define ALC5623_PWR_ADD2_VREF (1 << 13) +#define ALC5623_PWR_ADD2_PLL (1 << 12) +#define ALC5623_PWR_ADD2_DAC_REF_CIR (1 << 10) +#define ALC5623_PWR_ADD2_L_DAC_CLK (1 << 9) +#define ALC5623_PWR_ADD2_R_DAC_CLK (1 << 8) +#define ALC5623_PWR_ADD2_L_ADC_CLK_GAIN (1 << 7) +#define ALC5623_PWR_ADD2_R_ADC_CLK_GAIN (1 << 6) +#define ALC5623_PWR_ADD2_L_HP_MIXER (1 << 5) +#define ALC5623_PWR_ADD2_R_HP_MIXER (1 << 4) +#define ALC5623_PWR_ADD2_SPK_MIXER (1 << 3) +#define ALC5623_PWR_ADD2_MONO_MIXER (1 << 2) +#define ALC5623_PWR_ADD2_L_ADC_REC_MIXER (1 << 1) +#define ALC5623_PWR_ADD2_R_ADC_REC_MIXER (1 << 0) + +#define ALC5623_PWR_MANAG_ADD3 0x3E +#define ALC5623_PWR_ADD3_MAIN_BIAS (1 << 15) +#define ALC5623_PWR_ADD3_AUXOUT_L_VOL_AMP (1 << 14) +#define ALC5623_PWR_ADD3_AUXOUT_R_VOL_AMP (1 << 13) +#define ALC5623_PWR_ADD3_SPK_OUT (1 << 12) +#define ALC5623_PWR_ADD3_HP_L_OUT_VOL (1 << 10) +#define ALC5623_PWR_ADD3_HP_R_OUT_VOL (1 << 9) +#define ALC5623_PWR_ADD3_LINEIN_L_VOL (1 << 7) +#define ALC5623_PWR_ADD3_LINEIN_R_VOL (1 << 6) +#define ALC5623_PWR_ADD3_AUXIN_L_VOL (1 << 5) +#define ALC5623_PWR_ADD3_AUXIN_R_VOL (1 << 4) +#define ALC5623_PWR_ADD3_MIC1_FUN_CTRL (1 << 3) +#define ALC5623_PWR_ADD3_MIC2_FUN_CTRL (1 << 2) +#define ALC5623_PWR_ADD3_MIC1_BOOST_AD (1 << 1) +#define ALC5623_PWR_ADD3_MIC2_BOOST_AD (1 << 0) + +#define ALC5623_ADD_CTRL_REG 0x40 + +#define ALC5623_GLOBAL_CLK_CTRL_REG 0x42 +#define ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL (1 << 15) +#define ALC5623_GBL_CLK_SYS_SOUR_SEL_MCLK (0 << 15) +#define ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK (1 << 14) +#define ALC5623_GBL_CLK_PLL_SOUR_SEL_MCLK (0 << 14) +#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV8 (3 << 1) +#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV4 (2 << 1) +#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV2 (1 << 1) +#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV1 (0 << 1) +#define ALC5623_GBL_CLK_PLL_PRE_DIV2 (1 << 0) +#define ALC5623_GBL_CLK_PLL_PRE_DIV1 (0 << 0) + +#define ALC5623_PLL_CTRL 0x44 +#define ALC5623_PLL_CTRL_N_VAL(n) (((n)&0xff) << 8) +#define ALC5623_PLL_CTRL_K_VAL(k) (((k)&0x7) << 4) +#define ALC5623_PLL_CTRL_M_VAL(m) ((m)&0xf) + +#define ALC5623_GPIO_OUTPUT_PIN_CTRL 0x4A +#define ALC5623_GPIO_PIN_CONFIG 0x4C +#define ALC5623_GPIO_PIN_POLARITY 0x4E +#define ALC5623_GPIO_PIN_STICKY 0x50 +#define ALC5623_GPIO_PIN_WAKEUP 0x52 +#define ALC5623_GPIO_PIN_STATUS 0x54 +#define ALC5623_GPIO_PIN_SHARING 0x56 +#define ALC5623_OVER_CURR_STATUS 0x58 +#define ALC5623_JACK_DET_CTRL 0x5A + +#define ALC5623_MISC_CTRL 0x5E +#define ALC5623_MISC_DISABLE_FAST_VREG (1 << 15) +#define ALC5623_MISC_SPK_CLASS_AB_OC_PD (1 << 13) /* 5621 */ +#define ALC5623_MISC_SPK_CLASS_AB_OC_DET (1 << 12) /* 5621 */ +#define ALC5623_MISC_HP_DEPOP_MODE3_EN (1 << 10) +#define ALC5623_MISC_HP_DEPOP_MODE2_EN (1 << 9) +#define ALC5623_MISC_HP_DEPOP_MODE1_EN (1 << 8) +#define ALC5623_MISC_AUXOUT_DEPOP_MODE3_EN (1 << 6) +#define ALC5623_MISC_AUXOUT_DEPOP_MODE2_EN (1 << 5) +#define ALC5623_MISC_AUXOUT_DEPOP_MODE1_EN (1 << 4) +#define ALC5623_MISC_M_DAC_L_INPUT (1 << 3) +#define ALC5623_MISC_M_DAC_R_INPUT (1 << 2) +#define ALC5623_MISC_IRQOUT_INV_CTRL (1 << 0) + +#define ALC5623_PSEDUEO_SPATIAL_CTRL 0x60 +#define ALC5623_EQ_CTRL 0x62 +#define ALC5623_EQ_MODE_ENABLE 0x66 +#define ALC5623_AVC_CTRL 0x68 +#define ALC5623_HID_CTRL_INDEX 0x6A +#define ALC5623_HID_CTRL_DATA 0x6C +#define ALC5623_VENDOR_ID1 0x7C +#define ALC5623_VENDOR_ID2 0x7E + +#define ALC5623_PLL_FR_MCLK 0 +#define ALC5623_PLL_FR_BCK 1 +#endif diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c new file mode 100644 index 0000000000..d5021f2669 --- /dev/null +++ b/sound/soc/codecs/alc5632.c @@ -0,0 +1,1193 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* alc5632.c -- ALC5632 ALSA SoC Audio Codec +* +* Copyright (C) 2011 The AC100 Kernel Team +* +* Authors: Leon Romanovsky +* Andrey Danin +* Ilya Petrov +* Marc Dietrich +* +* Based on alc5623.c by Arnaud Patard +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "alc5632.h" + +/* + * ALC5632 register cache + */ +static const struct reg_default alc5632_reg_defaults[] = { + { 2, 0x8080 }, /* R2 - Speaker Output Volume */ + { 4, 0x8080 }, /* R4 - Headphone Output Volume */ + { 6, 0x8080 }, /* R6 - AUXOUT Volume */ + { 8, 0xC800 }, /* R8 - Phone Input */ + { 10, 0xE808 }, /* R10 - LINE_IN Volume */ + { 12, 0x1010 }, /* R12 - STEREO DAC Input Volume */ + { 14, 0x0808 }, /* R14 - MIC Input Volume */ + { 16, 0xEE0F }, /* R16 - Stereo DAC and MIC Routing Control */ + { 18, 0xCBCB }, /* R18 - ADC Record Gain */ + { 20, 0x7F7F }, /* R20 - ADC Record Mixer Control */ + { 24, 0xE010 }, /* R24 - Voice DAC Volume */ + { 28, 0x8008 }, /* R28 - Output Mixer Control */ + { 34, 0x0000 }, /* R34 - Microphone Control */ + { 36, 0x00C0 }, /* R36 - Codec Digital MIC/Digital Boost + Control */ + { 46, 0x0000 }, /* R46 - Stereo DAC/Voice DAC/Stereo ADC + Function Select */ + { 52, 0x8000 }, /* R52 - Main Serial Data Port Control + (Stereo I2S) */ + { 54, 0x0000 }, /* R54 - Extend Serial Data Port Control + (VoDAC_I2S/PCM) */ + { 58, 0x0000 }, /* R58 - Power Management Addition 1 */ + { 60, 0x0000 }, /* R60 - Power Management Addition 2 */ + { 62, 0x8000 }, /* R62 - Power Management Addition 3 */ + { 64, 0x0C0A }, /* R64 - General Purpose Control Register 1 */ + { 66, 0x0000 }, /* R66 - General Purpose Control Register 2 */ + { 68, 0x0000 }, /* R68 - PLL1 Control */ + { 70, 0x0000 }, /* R70 - PLL2 Control */ + { 76, 0xBE3E }, /* R76 - GPIO Pin Configuration */ + { 78, 0xBE3E }, /* R78 - GPIO Pin Polarity */ + { 80, 0x0000 }, /* R80 - GPIO Pin Sticky */ + { 82, 0x0000 }, /* R82 - GPIO Pin Wake Up */ + { 86, 0x0000 }, /* R86 - Pin Sharing */ + { 90, 0x0009 }, /* R90 - Soft Volume Control Setting */ + { 92, 0x0000 }, /* R92 - GPIO_Output Pin Control */ + { 94, 0x3000 }, /* R94 - MISC Control */ + { 96, 0x3075 }, /* R96 - Stereo DAC Clock Control_1 */ + { 98, 0x1010 }, /* R98 - Stereo DAC Clock Control_2 */ + { 100, 0x3110 }, /* R100 - VoDAC_PCM Clock Control_1 */ + { 104, 0x0553 }, /* R104 - Pseudo Stereo and Spatial Effect + Block Control */ + { 106, 0x0000 }, /* R106 - Private Register Address */ +}; + +/* codec private data */ +struct alc5632_priv { + struct regmap *regmap; + u8 id; + unsigned int sysclk; +}; + +static bool alc5632_volatile_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case ALC5632_RESET: + case ALC5632_PWR_DOWN_CTRL_STATUS: + case ALC5632_GPIO_PIN_STATUS: + case ALC5632_OVER_CURR_STATUS: + case ALC5632_HID_CTRL_DATA: + case ALC5632_EQ_CTRL: + case ALC5632_VENDOR_ID1: + case ALC5632_VENDOR_ID2: + return true; + + default: + break; + } + + return false; +} + +static inline int alc5632_reset(struct regmap *map) +{ + return regmap_write(map, ALC5632_RESET, 0x59B4); +} + +static int amp_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + /* to power-on/off class-d amp generators/speaker */ + /* need to write to 'index-46h' register : */ + /* so write index num (here 0x46) to reg 0x6a */ + /* and then 0xffff/0 to reg 0x6c */ + snd_soc_component_write(component, ALC5632_HID_CTRL_INDEX, 0x46); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write(component, ALC5632_HID_CTRL_DATA, 0xFFFF); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write(component, ALC5632_HID_CTRL_DATA, 0); + break; + } + + return 0; +} + +/* + * ALC5632 Controls + */ + +/* -34.5db min scale, 1.5db steps, no mute */ +static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0); +/* -46.5db min scale, 1.5db steps, no mute */ +static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0); +/* -16.5db min scale, 1.5db steps, no mute */ +static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0); +static const DECLARE_TLV_DB_RANGE(boost_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0) +); +/* 0db min scale, 6 db steps, no mute */ +static const DECLARE_TLV_DB_SCALE(dig_tlv, 0, 600, 0); +/* 0db min scalem 0.75db steps, no mute */ +static const DECLARE_TLV_DB_SCALE(vdac_tlv, -3525, 75, 0); + +static const struct snd_kcontrol_new alc5632_vol_snd_controls[] = { + /* left starts at bit 8, right at bit 0 */ + /* 31 steps (5 bit), -46.5db scale */ + SOC_DOUBLE_TLV("Speaker Playback Volume", + ALC5632_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv), + /* bit 15 mutes left, bit 7 right */ + SOC_DOUBLE("Speaker Playback Switch", + ALC5632_SPK_OUT_VOL, 15, 7, 1, 1), + SOC_DOUBLE_TLV("Headphone Playback Volume", + ALC5632_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Headphone Playback Switch", + ALC5632_HP_OUT_VOL, 15, 7, 1, 1), +}; + +static const struct snd_kcontrol_new alc5632_snd_controls[] = { + SOC_DOUBLE_TLV("Auxout Playback Volume", + ALC5632_AUX_OUT_VOL, 8, 0, 31, 1, hp_tlv), + SOC_DOUBLE("Auxout Playback Switch", + ALC5632_AUX_OUT_VOL, 15, 7, 1, 1), + SOC_SINGLE_TLV("Voice DAC Playback Volume", + ALC5632_VOICE_DAC_VOL, 0, 63, 0, vdac_tlv), + SOC_SINGLE("Voice DAC Playback Switch", + ALC5632_VOICE_DAC_VOL, 12, 1, 1), + SOC_SINGLE_TLV("Phone Playback Volume", + ALC5632_PHONE_IN_VOL, 8, 31, 1, vol_tlv), + SOC_DOUBLE_TLV("LineIn Playback Volume", + ALC5632_LINE_IN_VOL, 8, 0, 31, 1, vol_tlv), + SOC_DOUBLE_TLV("Master Playback Volume", + ALC5632_STEREO_DAC_IN_VOL, 8, 0, 63, 1, vdac_tlv), + SOC_DOUBLE("Master Playback Switch", + ALC5632_STEREO_DAC_IN_VOL, 15, 7, 1, 1), + SOC_SINGLE_TLV("Mic1 Playback Volume", + ALC5632_MIC_VOL, 8, 31, 1, vol_tlv), + SOC_SINGLE_TLV("Mic2 Playback Volume", + ALC5632_MIC_VOL, 0, 31, 1, vol_tlv), + SOC_DOUBLE_TLV("Rec Capture Volume", + ALC5632_ADC_REC_GAIN, 8, 0, 31, 0, adc_rec_tlv), + SOC_SINGLE_TLV("Mic 1 Boost Volume", + ALC5632_MIC_CTRL, 10, 3, 0, boost_tlv), + SOC_SINGLE_TLV("Mic 2 Boost Volume", + ALC5632_MIC_CTRL, 8, 3, 0, boost_tlv), + SOC_SINGLE_TLV("DMIC Boost Capture Volume", + ALC5632_DIGI_BOOST_CTRL, 0, 7, 0, dig_tlv), + SOC_SINGLE("DMIC En Capture Switch", + ALC5632_DIGI_BOOST_CTRL, 15, 1, 0), + SOC_SINGLE("DMIC PreFilter Capture Switch", + ALC5632_DIGI_BOOST_CTRL, 12, 1, 0), +}; + +/* + * DAPM Controls + */ +static const struct snd_kcontrol_new alc5632_hp_mixer_controls[] = { +SOC_DAPM_SINGLE("LI2HP Playback Switch", ALC5632_LINE_IN_VOL, 15, 1, 1), +SOC_DAPM_SINGLE("PHONE2HP Playback Switch", ALC5632_PHONE_IN_VOL, 15, 1, 1), +SOC_DAPM_SINGLE("MIC12HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 15, 1, 1), +SOC_DAPM_SINGLE("MIC22HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 11, 1, 1), +SOC_DAPM_SINGLE("VOICE2HP Playback Switch", ALC5632_VOICE_DAC_VOL, 15, 1, 1), +}; + +static const struct snd_kcontrol_new alc5632_hpl_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC2HP_L Playback Switch", ALC5632_ADC_REC_GAIN, 15, 1, 1), +SOC_DAPM_SINGLE("DACL2HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 3, 1, 1), +}; + +static const struct snd_kcontrol_new alc5632_hpr_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC2HP_R Playback Switch", ALC5632_ADC_REC_GAIN, 7, 1, 1), +SOC_DAPM_SINGLE("DACR2HP Playback Switch", ALC5632_MIC_ROUTING_CTRL, 2, 1, 1), +}; + +static const struct snd_kcontrol_new alc5632_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("ADC2MONO_L Playback Switch", ALC5632_ADC_REC_GAIN, 14, 1, 1), +SOC_DAPM_SINGLE("ADC2MONO_R Playback Switch", ALC5632_ADC_REC_GAIN, 6, 1, 1), +SOC_DAPM_SINGLE("LI2MONO Playback Switch", ALC5632_LINE_IN_VOL, 13, 1, 1), +SOC_DAPM_SINGLE("MIC12MONO Playback Switch", + ALC5632_MIC_ROUTING_CTRL, 13, 1, 1), +SOC_DAPM_SINGLE("MIC22MONO Playback Switch", + ALC5632_MIC_ROUTING_CTRL, 9, 1, 1), +SOC_DAPM_SINGLE("DAC2MONO Playback Switch", ALC5632_MIC_ROUTING_CTRL, 0, 1, 1), +SOC_DAPM_SINGLE("VOICE2MONO Playback Switch", ALC5632_VOICE_DAC_VOL, 13, 1, 1), +}; + +static const struct snd_kcontrol_new alc5632_speaker_mixer_controls[] = { +SOC_DAPM_SINGLE("LI2SPK Playback Switch", ALC5632_LINE_IN_VOL, 14, 1, 1), +SOC_DAPM_SINGLE("PHONE2SPK Playback Switch", ALC5632_PHONE_IN_VOL, 14, 1, 1), +SOC_DAPM_SINGLE("MIC12SPK Playback Switch", + ALC5632_MIC_ROUTING_CTRL, 14, 1, 1), +SOC_DAPM_SINGLE("MIC22SPK Playback Switch", + ALC5632_MIC_ROUTING_CTRL, 10, 1, 1), +SOC_DAPM_SINGLE("DAC2SPK Playback Switch", ALC5632_MIC_ROUTING_CTRL, 1, 1, 1), +SOC_DAPM_SINGLE("VOICE2SPK Playback Switch", ALC5632_VOICE_DAC_VOL, 14, 1, 1), +}; + +/* Left Record Mixer */ +static const struct snd_kcontrol_new alc5632_captureL_mixer_controls[] = { +SOC_DAPM_SINGLE("MIC12REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 14, 1, 1), +SOC_DAPM_SINGLE("MIC22REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 13, 1, 1), +SOC_DAPM_SINGLE("LIL2REC Capture Switch", ALC5632_ADC_REC_MIXER, 12, 1, 1), +SOC_DAPM_SINGLE("PH2REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 11, 1, 1), +SOC_DAPM_SINGLE("HPL2REC Capture Switch", ALC5632_ADC_REC_MIXER, 10, 1, 1), +SOC_DAPM_SINGLE("SPK2REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 9, 1, 1), +SOC_DAPM_SINGLE("MONO2REC_L Capture Switch", ALC5632_ADC_REC_MIXER, 8, 1, 1), +}; + +/* Right Record Mixer */ +static const struct snd_kcontrol_new alc5632_captureR_mixer_controls[] = { +SOC_DAPM_SINGLE("MIC12REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 6, 1, 1), +SOC_DAPM_SINGLE("MIC22REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 5, 1, 1), +SOC_DAPM_SINGLE("LIR2REC Capture Switch", ALC5632_ADC_REC_MIXER, 4, 1, 1), +SOC_DAPM_SINGLE("PH2REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 3, 1, 1), +SOC_DAPM_SINGLE("HPR2REC Capture Switch", ALC5632_ADC_REC_MIXER, 2, 1, 1), +SOC_DAPM_SINGLE("SPK2REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 1, 1, 1), +SOC_DAPM_SINGLE("MONO2REC_R Capture Switch", ALC5632_ADC_REC_MIXER, 0, 1, 1), +}; + +/* Dmic Mixer */ +static const struct snd_kcontrol_new alc5632_dmicl_mixer_controls[] = { +SOC_DAPM_SINGLE("DMICL2ADC Capture Switch", ALC5632_DIGI_BOOST_CTRL, 7, 1, 1), +}; +static const struct snd_kcontrol_new alc5632_dmicr_mixer_controls[] = { +SOC_DAPM_SINGLE("DMICR2ADC Capture Switch", ALC5632_DIGI_BOOST_CTRL, 6, 1, 1), +}; + +static const char * const alc5632_spk_n_sour_sel[] = { + "RN/-R", "RP/+R", "LN/-R", "Mute"}; +static const char * const alc5632_hpl_out_input_sel[] = { + "Vmid", "HP Left Mix"}; +static const char * const alc5632_hpr_out_input_sel[] = { + "Vmid", "HP Right Mix"}; +static const char * const alc5632_spkout_input_sel[] = { + "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"}; +static const char * const alc5632_aux_out_input_sel[] = { + "Vmid", "HPOut Mix", "Speaker Mix", "Mono Mix"}; +static const char * const alc5632_adcr_func_sel[] = { + "Stereo ADC", "Voice ADC"}; +static const char * const alc5632_i2s_out_sel[] = { + "ADC LR", "Voice Stereo Digital"}; + +/* auxout output mux */ +static SOC_ENUM_SINGLE_DECL(alc5632_aux_out_input_enum, + ALC5632_OUTPUT_MIXER_CTRL, 6, + alc5632_aux_out_input_sel); +static const struct snd_kcontrol_new alc5632_auxout_mux_controls = +SOC_DAPM_ENUM("AuxOut Mux", alc5632_aux_out_input_enum); + +/* speaker output mux */ +static SOC_ENUM_SINGLE_DECL(alc5632_spkout_input_enum, + ALC5632_OUTPUT_MIXER_CTRL, 10, + alc5632_spkout_input_sel); +static const struct snd_kcontrol_new alc5632_spkout_mux_controls = +SOC_DAPM_ENUM("SpeakerOut Mux", alc5632_spkout_input_enum); + +/* headphone left output mux */ +static SOC_ENUM_SINGLE_DECL(alc5632_hpl_out_input_enum, + ALC5632_OUTPUT_MIXER_CTRL, 9, + alc5632_hpl_out_input_sel); +static const struct snd_kcontrol_new alc5632_hpl_out_mux_controls = +SOC_DAPM_ENUM("Left Headphone Mux", alc5632_hpl_out_input_enum); + +/* headphone right output mux */ +static SOC_ENUM_SINGLE_DECL(alc5632_hpr_out_input_enum, + ALC5632_OUTPUT_MIXER_CTRL, 8, + alc5632_hpr_out_input_sel); +static const struct snd_kcontrol_new alc5632_hpr_out_mux_controls = +SOC_DAPM_ENUM("Right Headphone Mux", alc5632_hpr_out_input_enum); + +/* speaker output N select */ +static SOC_ENUM_SINGLE_DECL(alc5632_spk_n_sour_enum, + ALC5632_OUTPUT_MIXER_CTRL, 14, + alc5632_spk_n_sour_sel); +static const struct snd_kcontrol_new alc5632_spkoutn_mux_controls = +SOC_DAPM_ENUM("SpeakerOut N Mux", alc5632_spk_n_sour_enum); + +/* speaker amplifier */ +static const char *alc5632_amp_names[] = {"AB Amp", "D Amp"}; +static SOC_ENUM_SINGLE_DECL(alc5632_amp_enum, + ALC5632_OUTPUT_MIXER_CTRL, 13, + alc5632_amp_names); +static const struct snd_kcontrol_new alc5632_amp_mux_controls = + SOC_DAPM_ENUM("AB-D Amp Mux", alc5632_amp_enum); + +/* ADC output select */ +static SOC_ENUM_SINGLE_DECL(alc5632_adcr_func_enum, + ALC5632_DAC_FUNC_SELECT, 5, + alc5632_adcr_func_sel); +static const struct snd_kcontrol_new alc5632_adcr_func_controls = + SOC_DAPM_ENUM("ADCR Mux", alc5632_adcr_func_enum); + +/* I2S out select */ +static SOC_ENUM_SINGLE_DECL(alc5632_i2s_out_enum, + ALC5632_I2S_OUT_CTL, 5, + alc5632_i2s_out_sel); +static const struct snd_kcontrol_new alc5632_i2s_out_controls = + SOC_DAPM_ENUM("I2SOut Mux", alc5632_i2s_out_enum); + +static const struct snd_soc_dapm_widget alc5632_dapm_widgets[] = { +/* Muxes */ +SND_SOC_DAPM_MUX("AuxOut Mux", SND_SOC_NOPM, 0, 0, + &alc5632_auxout_mux_controls), +SND_SOC_DAPM_MUX("SpeakerOut Mux", SND_SOC_NOPM, 0, 0, + &alc5632_spkout_mux_controls), +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, + &alc5632_hpl_out_mux_controls), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, + &alc5632_hpr_out_mux_controls), +SND_SOC_DAPM_MUX("SpeakerOut N Mux", SND_SOC_NOPM, 0, 0, + &alc5632_spkoutn_mux_controls), +SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0, + &alc5632_adcr_func_controls), +SND_SOC_DAPM_MUX("I2SOut Mux", ALC5632_PWR_MANAG_ADD1, 11, 0, + &alc5632_i2s_out_controls), + +/* output mixers */ +SND_SOC_DAPM_MIXER("HP Mix", SND_SOC_NOPM, 0, 0, + &alc5632_hp_mixer_controls[0], + ARRAY_SIZE(alc5632_hp_mixer_controls)), +SND_SOC_DAPM_MIXER("HPR Mix", ALC5632_PWR_MANAG_ADD2, 4, 0, + &alc5632_hpr_mixer_controls[0], + ARRAY_SIZE(alc5632_hpr_mixer_controls)), +SND_SOC_DAPM_MIXER("HPL Mix", ALC5632_PWR_MANAG_ADD2, 5, 0, + &alc5632_hpl_mixer_controls[0], + ARRAY_SIZE(alc5632_hpl_mixer_controls)), +SND_SOC_DAPM_MIXER("HPOut Mix", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Mono Mix", ALC5632_PWR_MANAG_ADD2, 2, 0, + &alc5632_mono_mixer_controls[0], + ARRAY_SIZE(alc5632_mono_mixer_controls)), +SND_SOC_DAPM_MIXER("Speaker Mix", ALC5632_PWR_MANAG_ADD2, 3, 0, + &alc5632_speaker_mixer_controls[0], + ARRAY_SIZE(alc5632_speaker_mixer_controls)), +SND_SOC_DAPM_MIXER("DMICL Mix", SND_SOC_NOPM, 0, 0, + &alc5632_dmicl_mixer_controls[0], + ARRAY_SIZE(alc5632_dmicl_mixer_controls)), +SND_SOC_DAPM_MIXER("DMICR Mix", SND_SOC_NOPM, 0, 0, + &alc5632_dmicr_mixer_controls[0], + ARRAY_SIZE(alc5632_dmicr_mixer_controls)), + +/* input mixers */ +SND_SOC_DAPM_MIXER("Left Capture Mix", ALC5632_PWR_MANAG_ADD2, 1, 0, + &alc5632_captureL_mixer_controls[0], + ARRAY_SIZE(alc5632_captureL_mixer_controls)), +SND_SOC_DAPM_MIXER("Right Capture Mix", ALC5632_PWR_MANAG_ADD2, 0, 0, + &alc5632_captureR_mixer_controls[0], + ARRAY_SIZE(alc5632_captureR_mixer_controls)), + +SND_SOC_DAPM_AIF_IN("AIFRXL", "Left HiFi Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFRXR", "Right HiFi Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("VAIFRX", "Voice Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("VAIFTX", "Voice Capture", 0, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_DAC("Voice DAC", NULL, ALC5632_PWR_MANAG_ADD2, 10, 0), +SND_SOC_DAPM_DAC("Left DAC", NULL, ALC5632_PWR_MANAG_ADD2, 9, 0), +SND_SOC_DAPM_DAC("Right DAC", NULL, ALC5632_PWR_MANAG_ADD2, 8, 0), +SND_SOC_DAPM_ADC("Left ADC", NULL, ALC5632_PWR_MANAG_ADD2, 7, 0), +SND_SOC_DAPM_ADC("Right ADC", NULL, ALC5632_PWR_MANAG_ADD2, 6, 0), + +SND_SOC_DAPM_MIXER("DAC Left Channel", ALC5632_PWR_MANAG_ADD1, 15, 0, NULL, 0), +SND_SOC_DAPM_MIXER("DAC Right Channel", + ALC5632_PWR_MANAG_ADD1, 14, 0, NULL, 0), +SND_SOC_DAPM_MIXER("I2S Mix", ALC5632_PWR_MANAG_ADD1, 11, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Phone Mix", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Line Mix", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Voice Mix", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("ADCLR", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Left Headphone", ALC5632_PWR_MANAG_ADD3, 11, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Headphone", ALC5632_PWR_MANAG_ADD3, 10, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left Speaker", ALC5632_PWR_MANAG_ADD3, 13, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Speaker", ALC5632_PWR_MANAG_ADD3, 12, 0, NULL, 0), +SND_SOC_DAPM_PGA("Aux Out", ALC5632_PWR_MANAG_ADD3, 14, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left LineIn", ALC5632_PWR_MANAG_ADD3, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right LineIn", ALC5632_PWR_MANAG_ADD3, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("Phone", ALC5632_PWR_MANAG_ADD3, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("Phone ADMix", ALC5632_PWR_MANAG_ADD3, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC1 PGA", ALC5632_PWR_MANAG_ADD3, 3, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC2 PGA", ALC5632_PWR_MANAG_ADD3, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC1 Pre Amp", ALC5632_PWR_MANAG_ADD3, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("MIC2 Pre Amp", ALC5632_PWR_MANAG_ADD3, 0, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1", ALC5632_PWR_MANAG_ADD1, 3, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ALC5632_PWR_MANAG_ADD1, 2, 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("D Amp", ALC5632_PWR_MANAG_ADD2, 14, 0, NULL, 0, + amp_mixer_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_PGA("AB Amp", ALC5632_PWR_MANAG_ADD2, 15, 0, NULL, 0), +SND_SOC_DAPM_MUX("AB-D Amp Mux", ALC5632_PWR_MANAG_ADD1, 10, 0, + &alc5632_amp_mux_controls), + +SND_SOC_DAPM_OUTPUT("AUXOUT"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_OUTPUT("SPKOUT"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), + +SND_SOC_DAPM_INPUT("LINEINL"), +SND_SOC_DAPM_INPUT("LINEINR"), +SND_SOC_DAPM_INPUT("PHONEP"), +SND_SOC_DAPM_INPUT("PHONEN"), +SND_SOC_DAPM_INPUT("DMICDAT"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +SND_SOC_DAPM_VMID("Vmid"), +}; + + +static const struct snd_soc_dapm_route alc5632_dapm_routes[] = { + /* Playback streams */ + {"Left DAC", NULL, "AIFRXL"}, + {"Right DAC", NULL, "AIFRXR"}, + + /* virtual mixer - mixes left & right channels */ + {"I2S Mix", NULL, "Left DAC"}, + {"I2S Mix", NULL, "Right DAC"}, + {"Line Mix", NULL, "Right LineIn"}, + {"Line Mix", NULL, "Left LineIn"}, + {"Phone Mix", NULL, "Phone"}, + {"Phone Mix", NULL, "Phone ADMix"}, + {"AUXOUT", NULL, "Aux Out"}, + + /* DAC */ + {"DAC Right Channel", NULL, "I2S Mix"}, + {"DAC Left Channel", NULL, "I2S Mix"}, + + /* HP mixer */ + {"HPL Mix", "ADC2HP_L Playback Switch", "Left Capture Mix"}, + {"HPL Mix", NULL, "HP Mix"}, + {"HPR Mix", "ADC2HP_R Playback Switch", "Right Capture Mix"}, + {"HPR Mix", NULL, "HP Mix"}, + {"HP Mix", "LI2HP Playback Switch", "Line Mix"}, + {"HP Mix", "PHONE2HP Playback Switch", "Phone Mix"}, + {"HP Mix", "MIC12HP Playback Switch", "MIC1 PGA"}, + {"HP Mix", "MIC22HP Playback Switch", "MIC2 PGA"}, + {"HP Mix", "VOICE2HP Playback Switch", "Voice Mix"}, + {"HPR Mix", "DACR2HP Playback Switch", "DAC Right Channel"}, + {"HPL Mix", "DACL2HP Playback Switch", "DAC Left Channel"}, + {"HPOut Mix", NULL, "HP Mix"}, + {"HPOut Mix", NULL, "HPR Mix"}, + {"HPOut Mix", NULL, "HPL Mix"}, + + /* speaker mixer */ + {"Speaker Mix", "LI2SPK Playback Switch", "Line Mix"}, + {"Speaker Mix", "PHONE2SPK Playback Switch", "Phone Mix"}, + {"Speaker Mix", "MIC12SPK Playback Switch", "MIC1 PGA"}, + {"Speaker Mix", "MIC22SPK Playback Switch", "MIC2 PGA"}, + {"Speaker Mix", "DAC2SPK Playback Switch", "DAC Left Channel"}, + {"Speaker Mix", "VOICE2SPK Playback Switch", "Voice Mix"}, + + /* mono mixer */ + {"Mono Mix", "ADC2MONO_L Playback Switch", "Left Capture Mix"}, + {"Mono Mix", "ADC2MONO_R Playback Switch", "Right Capture Mix"}, + {"Mono Mix", "LI2MONO Playback Switch", "Line Mix"}, + {"Mono Mix", "MIC12MONO Playback Switch", "MIC1 PGA"}, + {"Mono Mix", "MIC22MONO Playback Switch", "MIC2 PGA"}, + {"Mono Mix", "DAC2MONO Playback Switch", "DAC Left Channel"}, + {"Mono Mix", "VOICE2MONO Playback Switch", "Voice Mix"}, + + /* Left record mixer */ + {"Left Capture Mix", "LIL2REC Capture Switch", "LINEINL"}, + {"Left Capture Mix", "PH2REC_L Capture Switch", "PHONEN"}, + {"Left Capture Mix", "MIC12REC_L Capture Switch", "MIC1 Pre Amp"}, + {"Left Capture Mix", "MIC22REC_L Capture Switch", "MIC2 Pre Amp"}, + {"Left Capture Mix", "HPL2REC Capture Switch", "HPL Mix"}, + {"Left Capture Mix", "SPK2REC_L Capture Switch", "Speaker Mix"}, + {"Left Capture Mix", "MONO2REC_L Capture Switch", "Mono Mix"}, + + /*Right record mixer */ + {"Right Capture Mix", "LIR2REC Capture Switch", "LINEINR"}, + {"Right Capture Mix", "PH2REC_R Capture Switch", "PHONEP"}, + {"Right Capture Mix", "MIC12REC_R Capture Switch", "MIC1 Pre Amp"}, + {"Right Capture Mix", "MIC22REC_R Capture Switch", "MIC2 Pre Amp"}, + {"Right Capture Mix", "HPR2REC Capture Switch", "HPR Mix"}, + {"Right Capture Mix", "SPK2REC_R Capture Switch", "Speaker Mix"}, + {"Right Capture Mix", "MONO2REC_R Capture Switch", "Mono Mix"}, + + /* headphone left mux */ + {"Left Headphone Mux", "HP Left Mix", "HPL Mix"}, + {"Left Headphone Mux", "Vmid", "Vmid"}, + + /* headphone right mux */ + {"Right Headphone Mux", "HP Right Mix", "HPR Mix"}, + {"Right Headphone Mux", "Vmid", "Vmid"}, + + /* speaker out mux */ + {"SpeakerOut Mux", "Vmid", "Vmid"}, + {"SpeakerOut Mux", "HPOut Mix", "HPOut Mix"}, + {"SpeakerOut Mux", "Speaker Mix", "Speaker Mix"}, + {"SpeakerOut Mux", "Mono Mix", "Mono Mix"}, + + /* Mono/Aux Out mux */ + {"AuxOut Mux", "Vmid", "Vmid"}, + {"AuxOut Mux", "HPOut Mix", "HPOut Mix"}, + {"AuxOut Mux", "Speaker Mix", "Speaker Mix"}, + {"AuxOut Mux", "Mono Mix", "Mono Mix"}, + + /* output pga */ + {"HPL", NULL, "Left Headphone"}, + {"Left Headphone", NULL, "Left Headphone Mux"}, + {"HPR", NULL, "Right Headphone"}, + {"Right Headphone", NULL, "Right Headphone Mux"}, + {"Aux Out", NULL, "AuxOut Mux"}, + + /* input pga */ + {"Left LineIn", NULL, "LINEINL"}, + {"Right LineIn", NULL, "LINEINR"}, + {"Phone", NULL, "PHONEP"}, + {"MIC1 Pre Amp", NULL, "MIC1"}, + {"MIC2 Pre Amp", NULL, "MIC2"}, + {"MIC1 PGA", NULL, "MIC1 Pre Amp"}, + {"MIC2 PGA", NULL, "MIC2 Pre Amp"}, + + /* left ADC */ + {"Left ADC", NULL, "Left Capture Mix"}, + {"DMICL Mix", "DMICL2ADC Capture Switch", "DMICDAT"}, + {"Left ADC", NULL, "DMICL Mix"}, + {"ADCLR", NULL, "Left ADC"}, + + /* right ADC */ + {"Right ADC", NULL, "Right Capture Mix"}, + {"DMICR Mix", "DMICR2ADC Capture Switch", "DMICDAT"}, + {"Right ADC", NULL, "DMICR Mix"}, + {"ADCR Mux", "Stereo ADC", "Right ADC"}, + {"ADCR Mux", "Voice ADC", "Right ADC"}, + {"ADCLR", NULL, "ADCR Mux"}, + {"VAIFTX", NULL, "ADCR Mux"}, + + /* Digital I2S out */ + {"I2SOut Mux", "ADC LR", "ADCLR"}, + {"I2SOut Mux", "Voice Stereo Digital", "VAIFRX"}, + {"AIFTXL", NULL, "I2SOut Mux"}, + {"AIFTXR", NULL, "I2SOut Mux"}, + + /* Voice Mix */ + {"Voice DAC", NULL, "VAIFRX"}, + {"Voice Mix", NULL, "Voice DAC"}, + + /* Speaker Output */ + {"SpeakerOut N Mux", "RN/-R", "Left Speaker"}, + {"SpeakerOut N Mux", "RP/+R", "Left Speaker"}, + {"SpeakerOut N Mux", "LN/-R", "Left Speaker"}, + {"SpeakerOut N Mux", "Mute", "Vmid"}, + + {"SpeakerOut N Mux", "RN/-R", "Right Speaker"}, + {"SpeakerOut N Mux", "RP/+R", "Right Speaker"}, + {"SpeakerOut N Mux", "LN/-R", "Right Speaker"}, + {"SpeakerOut N Mux", "Mute", "Vmid"}, + + {"AB Amp", NULL, "SpeakerOut Mux"}, + {"D Amp", NULL, "SpeakerOut Mux"}, + {"AB-D Amp Mux", "AB Amp", "AB Amp"}, + {"AB-D Amp Mux", "D Amp", "D Amp"}, + {"Left Speaker", NULL, "AB-D Amp Mux"}, + {"Right Speaker", NULL, "AB-D Amp Mux"}, + + {"SPKOUT", NULL, "Left Speaker"}, + {"SPKOUT", NULL, "Right Speaker"}, + + {"SPKOUTN", NULL, "SpeakerOut N Mux"}, + +}; + +/* PLL divisors */ +struct _pll_div { + u32 pll_in; + u32 pll_out; + u16 regvalue; +}; + +/* Note : pll code from original alc5632 driver. Not sure of how good it is */ +/* useful only for master mode */ +static const struct _pll_div codec_master_pll_div[] = { + + { 2048000, 8192000, 0x0ea0}, + { 3686400, 8192000, 0x4e27}, + { 12000000, 8192000, 0x456b}, + { 13000000, 8192000, 0x495f}, + { 13100000, 8192000, 0x0320}, + { 2048000, 11289600, 0xf637}, + { 3686400, 11289600, 0x2f22}, + { 12000000, 11289600, 0x3e2f}, + { 13000000, 11289600, 0x4d5b}, + { 13100000, 11289600, 0x363b}, + { 2048000, 16384000, 0x1ea0}, + { 3686400, 16384000, 0x9e27}, + { 12000000, 16384000, 0x452b}, + { 13000000, 16384000, 0x542f}, + { 13100000, 16384000, 0x03a0}, + { 2048000, 16934400, 0xe625}, + { 3686400, 16934400, 0x9126}, + { 12000000, 16934400, 0x4d2c}, + { 13000000, 16934400, 0x742f}, + { 13100000, 16934400, 0x3c27}, + { 2048000, 22579200, 0x2aa0}, + { 3686400, 22579200, 0x2f20}, + { 12000000, 22579200, 0x7e2f}, + { 13000000, 22579200, 0x742f}, + { 13100000, 22579200, 0x3c27}, + { 2048000, 24576000, 0x2ea0}, + { 3686400, 24576000, 0xee27}, + { 12000000, 24576000, 0x2915}, + { 13000000, 24576000, 0x772e}, + { 13100000, 24576000, 0x0d20}, +}; + +/* FOUT = MCLK*(N+2)/((M+2)*(K+2)) + N: bit 15:8 (div 2 .. div 257) + K: bit 6:4 typical 2 + M: bit 3:0 (div 2 .. div 17) + + same as for 5623 - thanks! +*/ + +static const struct _pll_div codec_slave_pll_div[] = { + + { 1024000, 16384000, 0x3ea0}, + { 1411200, 22579200, 0x3ea0}, + { 1536000, 24576000, 0x3ea0}, + { 2048000, 16384000, 0x1ea0}, + { 2822400, 22579200, 0x1ea0}, + { 3072000, 24576000, 0x1ea0}, + +}; + +static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + int i; + struct snd_soc_component *component = codec_dai->component; + int gbl_clk = 0, pll_div = 0; + u16 reg; + + if (pll_id < ALC5632_PLL_FR_MCLK || pll_id > ALC5632_PLL_FR_VBCLK) + return -EINVAL; + + /* Disable PLL power */ + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2, + ALC5632_PWR_ADD2_PLL1, + 0); + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2, + ALC5632_PWR_ADD2_PLL2, + 0); + + /* pll is not used in slave mode */ + reg = snd_soc_component_read(component, ALC5632_DAI_CONTROL); + if (reg & ALC5632_DAI_SDP_SLAVE_MODE) + return 0; + + if (!freq_in || !freq_out) + return 0; + + switch (pll_id) { + case ALC5632_PLL_FR_MCLK: + for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) { + if (codec_master_pll_div[i].pll_in == freq_in + && codec_master_pll_div[i].pll_out == freq_out) { + /* PLL source from MCLK */ + pll_div = codec_master_pll_div[i].regvalue; + break; + } + } + break; + case ALC5632_PLL_FR_BCLK: + for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) { + if (codec_slave_pll_div[i].pll_in == freq_in + && codec_slave_pll_div[i].pll_out == freq_out) { + /* PLL source from Bitclk */ + gbl_clk = ALC5632_PLL_FR_BCLK; + pll_div = codec_slave_pll_div[i].regvalue; + break; + } + } + break; + case ALC5632_PLL_FR_VBCLK: + for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) { + if (codec_slave_pll_div[i].pll_in == freq_in + && codec_slave_pll_div[i].pll_out == freq_out) { + /* PLL source from voice clock */ + gbl_clk = ALC5632_PLL_FR_VBCLK; + pll_div = codec_slave_pll_div[i].regvalue; + break; + } + } + break; + default: + return -EINVAL; + } + + if (!pll_div) + return -EINVAL; + + /* choose MCLK/BCLK/VBCLK */ + snd_soc_component_write(component, ALC5632_GPCR2, gbl_clk); + /* choose PLL1 clock rate */ + snd_soc_component_write(component, ALC5632_PLL1_CTRL, pll_div); + /* enable PLL1 */ + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2, + ALC5632_PWR_ADD2_PLL1, + ALC5632_PWR_ADD2_PLL1); + /* enable PLL2 */ + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2, + ALC5632_PWR_ADD2_PLL2, + ALC5632_PWR_ADD2_PLL2); + /* use PLL1 as main SYSCLK */ + snd_soc_component_update_bits(component, ALC5632_GPCR1, + ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1, + ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1); + + return 0; +} + +struct _coeff_div { + u16 fs; + u16 regvalue; +}; + +/* codec hifi mclk (after PLL) clock divider coefficients */ +/* values inspired from column BCLK=32Fs of Appendix A table */ +static const struct _coeff_div coeff_div[] = { + {512*1, 0x3075}, +}; + +static int get_coeff(struct snd_soc_component *component, int rate) +{ + struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component); + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].fs * rate == alc5632->sysclk) + return i; + } + return -EINVAL; +} + +/* + * Clock after PLL and dividers + */ +static int alc5632_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case 4096000: + case 8192000: + case 11289600: + case 12288000: + case 16384000: + case 16934400: + case 18432000: + case 22579200: + case 24576000: + alc5632->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = 0; + + /* set audio interface clocking */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + iface = ALC5632_DAI_SDP_MASTER_MODE; + break; + case SND_SOC_DAIFMT_CBC_CFC: + iface = ALC5632_DAI_SDP_SLAVE_MODE; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= ALC5632_DAI_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= ALC5632_DAI_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= ALC5632_DAI_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= ALC5632_DAI_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= ALC5632_DAI_MAIN_I2S_BCLK_POL_CTRL; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= ALC5632_DAI_MAIN_I2S_BCLK_POL_CTRL; + break; + case SND_SOC_DAIFMT_NB_IF: + break; + default: + return -EINVAL; + } + + return snd_soc_component_write(component, ALC5632_DAI_CONTROL, iface); +} + +static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int coeff, rate; + u16 iface; + + iface = snd_soc_component_read(component, ALC5632_DAI_CONTROL); + iface &= ~ALC5632_DAI_I2S_DL_MASK; + + /* bit size */ + switch (params_width(params)) { + case 16: + iface |= ALC5632_DAI_I2S_DL_16; + break; + case 20: + iface |= ALC5632_DAI_I2S_DL_20; + break; + case 24: + iface |= ALC5632_DAI_I2S_DL_24; + break; + default: + return -EINVAL; + } + + /* set iface & srate */ + snd_soc_component_write(component, ALC5632_DAI_CONTROL, iface); + rate = params_rate(params); + coeff = get_coeff(component, rate); + if (coeff < 0) + return -EINVAL; + + coeff = coeff_div[coeff].regvalue; + snd_soc_component_write(component, ALC5632_DAC_CLK_CTRL1, coeff); + + return 0; +} + +static int alc5632_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 hp_mute = ALC5632_MISC_HP_DEPOP_MUTE_L + |ALC5632_MISC_HP_DEPOP_MUTE_R; + u16 mute_reg = snd_soc_component_read(component, ALC5632_MISC_CTRL) & ~hp_mute; + + if (mute) + mute_reg |= hp_mute; + + return snd_soc_component_write(component, ALC5632_MISC_CTRL, mute_reg); +} + +#define ALC5632_ADD2_POWER_EN (ALC5632_PWR_ADD2_VREF) + +#define ALC5632_ADD3_POWER_EN (ALC5632_PWR_ADD3_MIC1_BOOST_AD) + +#define ALC5632_ADD1_POWER_EN \ + (ALC5632_PWR_ADD1_DAC_REF \ + | ALC5632_PWR_ADD1_SOFTGEN_EN \ + | ALC5632_PWR_ADD1_HP_OUT_AMP \ + | ALC5632_PWR_ADD1_HP_OUT_ENH_AMP \ + | ALC5632_PWR_ADD1_MAIN_BIAS) + +static void enable_power_depop(struct snd_soc_component *component) +{ + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1, + ALC5632_PWR_ADD1_SOFTGEN_EN, + ALC5632_PWR_ADD1_SOFTGEN_EN); + + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD3, + ALC5632_ADD3_POWER_EN, + ALC5632_ADD3_POWER_EN); + + snd_soc_component_update_bits(component, ALC5632_MISC_CTRL, + ALC5632_MISC_HP_DEPOP_MODE2_EN, + ALC5632_MISC_HP_DEPOP_MODE2_EN); + + /* "normal" mode: 0 @ 26 */ + /* set all PR0-7 mixers to 0 */ + snd_soc_component_update_bits(component, ALC5632_PWR_DOWN_CTRL_STATUS, + ALC5632_PWR_DOWN_CTRL_STATUS_MASK, + 0); + + msleep(500); + + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2, + ALC5632_ADD2_POWER_EN, + ALC5632_ADD2_POWER_EN); + + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1, + ALC5632_ADD1_POWER_EN, + ALC5632_ADD1_POWER_EN); + + /* disable HP Depop2 */ + snd_soc_component_update_bits(component, ALC5632_MISC_CTRL, + ALC5632_MISC_HP_DEPOP_MODE2_EN, + 0); + +} + +static int alc5632_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + enable_power_depop(component); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* everything off except vref/vmid, */ + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1, + ALC5632_PWR_MANAG_ADD1_MASK, + ALC5632_PWR_ADD1_MAIN_BIAS); + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2, + ALC5632_PWR_MANAG_ADD2_MASK, + ALC5632_PWR_ADD2_VREF); + /* "normal" mode: 0 @ 26 */ + snd_soc_component_update_bits(component, ALC5632_PWR_DOWN_CTRL_STATUS, + ALC5632_PWR_DOWN_CTRL_STATUS_MASK, + 0xffff ^ (ALC5632_PWR_VREF_PR3 + | ALC5632_PWR_VREF_PR2)); + break; + case SND_SOC_BIAS_OFF: + /* everything off, dac mute, inactive */ + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2, + ALC5632_PWR_MANAG_ADD2_MASK, 0); + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD3, + ALC5632_PWR_MANAG_ADD3_MASK, 0); + snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1, + ALC5632_PWR_MANAG_ADD1_MASK, 0); + break; + } + return 0; +} + +#define ALC5632_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \ + | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops alc5632_dai_ops = { + .hw_params = alc5632_pcm_hw_params, + .mute_stream = alc5632_mute, + .set_fmt = alc5632_set_dai_fmt, + .set_sysclk = alc5632_set_dai_sysclk, + .set_pll = alc5632_set_dai_pll, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver alc5632_dai = { + .name = "alc5632-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ALC5632_FORMATS,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ALC5632_FORMATS,}, + + .ops = &alc5632_dai_ops, + .symmetric_rate = 1, +}; + +#ifdef CONFIG_PM +static int alc5632_resume(struct snd_soc_component *component) +{ + struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component); + + regcache_sync(alc5632->regmap); + + return 0; +} +#else +#define alc5632_resume NULL +#endif + +static int alc5632_probe(struct snd_soc_component *component) +{ + struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component); + + switch (alc5632->id) { + case 0x5c: + snd_soc_add_component_controls(component, alc5632_vol_snd_controls, + ARRAY_SIZE(alc5632_vol_snd_controls)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_component_driver soc_component_device_alc5632 = { + .probe = alc5632_probe, + .resume = alc5632_resume, + .set_bias_level = alc5632_set_bias_level, + .controls = alc5632_snd_controls, + .num_controls = ARRAY_SIZE(alc5632_snd_controls), + .dapm_widgets = alc5632_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(alc5632_dapm_widgets), + .dapm_routes = alc5632_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(alc5632_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config alc5632_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = ALC5632_MAX_REGISTER, + .reg_defaults = alc5632_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(alc5632_reg_defaults), + .volatile_reg = alc5632_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct i2c_device_id alc5632_i2c_table[] = { + {"alc5632", 0x5c}, + {} +}; +MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table); + +/* + * alc5632 2 wire address is determined by A1 pin + * state during powerup. + * low = 0x1a + * high = 0x1b + */ +static int alc5632_i2c_probe(struct i2c_client *client) +{ + struct alc5632_priv *alc5632; + int ret, ret1, ret2; + unsigned int vid1, vid2; + const struct i2c_device_id *id; + + alc5632 = devm_kzalloc(&client->dev, + sizeof(struct alc5632_priv), GFP_KERNEL); + if (alc5632 == NULL) + return -ENOMEM; + + i2c_set_clientdata(client, alc5632); + + alc5632->regmap = devm_regmap_init_i2c(client, &alc5632_regmap); + if (IS_ERR(alc5632->regmap)) { + ret = PTR_ERR(alc5632->regmap); + dev_err(&client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret1 = regmap_read(alc5632->regmap, ALC5632_VENDOR_ID1, &vid1); + ret2 = regmap_read(alc5632->regmap, ALC5632_VENDOR_ID2, &vid2); + if (ret1 != 0 || ret2 != 0) { + dev_err(&client->dev, + "Failed to read chip ID: ret1=%d, ret2=%d\n", ret1, ret2); + return -EIO; + } + + vid2 >>= 8; + + id = i2c_match_id(alc5632_i2c_table, client); + + if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) { + dev_err(&client->dev, + "Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2); + return -EINVAL; + } + + ret = alc5632_reset(alc5632->regmap); + if (ret < 0) { + dev_err(&client->dev, "Failed to issue reset\n"); + return ret; + } + + alc5632->id = vid2; + switch (alc5632->id) { + case 0x5c: + alc5632_dai.name = "alc5632-hifi"; + break; + default: + return -EINVAL; + } + + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_device_alc5632, &alc5632_dai, 1); + + if (ret < 0) { + dev_err(&client->dev, "Failed to register component: %d\n", ret); + return ret; + } + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id alc5632_of_match[] = { + { .compatible = "realtek,alc5632", }, + { } +}; +MODULE_DEVICE_TABLE(of, alc5632_of_match); +#endif + +/* i2c codec control layer */ +static struct i2c_driver alc5632_i2c_driver = { + .driver = { + .name = "alc5632", + .of_match_table = of_match_ptr(alc5632_of_match), + }, + .probe = alc5632_i2c_probe, + .id_table = alc5632_i2c_table, +}; + +module_i2c_driver(alc5632_i2c_driver); + +MODULE_DESCRIPTION("ASoC ALC5632 driver"); +MODULE_AUTHOR("Leon Romanovsky "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/alc5632.h b/sound/soc/codecs/alc5632.h new file mode 100644 index 0000000000..a2bb5f9c71 --- /dev/null +++ b/sound/soc/codecs/alc5632.h @@ -0,0 +1,249 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +* alc5632.h -- ALC5632 ALSA SoC Audio Codec +* +* Copyright (C) 2011 The AC100 Kernel Team +* +* Authors: Leon Romanovsky +* Andrey Danin +* Ilya Petrov +* Marc Dietrich +* +* Based on alc5623.h by Arnaud Patard +*/ + +#ifndef _ALC5632_H +#define _ALC5632_H + +#define ALC5632_RESET 0x00 +/* speaker output vol 2 2 */ +/* line output vol 4 2 */ +/* HP output vol 4 0 4 */ +#define ALC5632_SPK_OUT_VOL 0x02 /* spe out vol */ +#define ALC5632_SPK_OUT_VOL_STEP 1.5 +#define ALC5632_HP_OUT_VOL 0x04 /* hp out vol */ +#define ALC5632_AUX_OUT_VOL 0x06 /* aux out vol */ +#define ALC5632_PHONE_IN_VOL 0x08 /* phone in vol */ +#define ALC5632_LINE_IN_VOL 0x0A /* line in vol */ +#define ALC5632_STEREO_DAC_IN_VOL 0x0C /* stereo dac in vol */ +#define ALC5632_MIC_VOL 0x0E /* mic in vol */ +/* stero dac/mic routing */ +#define ALC5632_MIC_ROUTING_CTRL 0x10 +#define ALC5632_MIC_ROUTE_MONOMIX (1 << 0) +#define ALC5632_MIC_ROUTE_SPK (1 << 1) +#define ALC5632_MIC_ROUTE_HP (1 << 2) + +#define ALC5632_ADC_REC_GAIN 0x12 /* rec gain */ +#define ALC5632_ADC_REC_GAIN_RANGE 0x1F1F +#define ALC5632_ADC_REC_GAIN_BASE (-16.5) +#define ALC5632_ADC_REC_GAIN_STEP 1.5 + +#define ALC5632_ADC_REC_MIXER 0x14 /* mixer control */ +#define ALC5632_ADC_REC_MIC1 (1 << 6) +#define ALC5632_ADC_REC_MIC2 (1 << 5) +#define ALC5632_ADC_REC_LINE_IN (1 << 4) +#define ALC5632_ADC_REC_AUX (1 << 3) +#define ALC5632_ADC_REC_HP (1 << 2) +#define ALC5632_ADC_REC_SPK (1 << 1) +#define ALC5632_ADC_REC_MONOMIX (1 << 0) + +#define ALC5632_VOICE_DAC_VOL 0x18 /* voice dac vol */ +#define ALC5632_I2S_OUT_CTL 0x1A /* undocumented reg. found in path scheme */ +/* ALC5632_OUTPUT_MIXER_CTRL : */ +/* same remark as for reg 2 line vs speaker */ +#define ALC5632_OUTPUT_MIXER_CTRL 0x1C /* out mix ctrl */ +#define ALC5632_OUTPUT_MIXER_RP (1 << 14) +#define ALC5632_OUTPUT_MIXER_WEEK (1 << 12) +#define ALC5632_OUTPUT_MIXER_HP (1 << 10) +#define ALC5632_OUTPUT_MIXER_AUX_SPK (2 << 6) +#define ALC5632_OUTPUT_MIXER_AUX_HP_LR (1 << 6) +#define ALC5632_OUTPUT_MIXER_HP_R (1 << 8) +#define ALC5632_OUTPUT_MIXER_HP_L (1 << 9) + +#define ALC5632_MIC_CTRL 0x22 /* mic phone ctrl */ +#define ALC5632_MIC_BOOST_BYPASS 0 +#define ALC5632_MIC_BOOST_20DB 1 +#define ALC5632_MIC_BOOST_30DB 2 +#define ALC5632_MIC_BOOST_40DB 3 + +#define ALC5632_DIGI_BOOST_CTRL 0x24 /* digi mic / bost ctl */ +#define ALC5632_MIC_BOOST_RANGE 7 +#define ALC5632_MIC_BOOST_STEP 6 +#define ALC5632_PWR_DOWN_CTRL_STATUS 0x26 +#define ALC5632_PWR_DOWN_CTRL_STATUS_MASK 0xEF00 +#define ALC5632_PWR_VREF_PR3 (1 << 11) +#define ALC5632_PWR_VREF_PR2 (1 << 10) +#define ALC5632_PWR_VREF_STATUS (1 << 3) +#define ALC5632_PWR_AMIX_STATUS (1 << 2) +#define ALC5632_PWR_DAC_STATUS (1 << 1) +#define ALC5632_PWR_ADC_STATUS (1 << 0) +/* stereo/voice DAC / stereo adc func ctrl */ +#define ALC5632_DAC_FUNC_SELECT 0x2E + +/* Main serial data port ctrl (i2s) */ +#define ALC5632_DAI_CONTROL 0x34 + +#define ALC5632_DAI_SDP_MASTER_MODE (0 << 15) +#define ALC5632_DAI_SDP_SLAVE_MODE (1 << 15) +#define ALC5632_DAI_SADLRCK_MODE (1 << 14) +/* 0:voice, 1:main */ +#define ALC5632_DAI_MAIN_I2S_SYSCLK_SEL (1 << 8) +#define ALC5632_DAI_MAIN_I2S_BCLK_POL_CTRL (1 << 7) +/* 0:normal, 1:invert */ +#define ALC5632_DAI_MAIN_I2S_LRCK_INV (1 << 6) +#define ALC5632_DAI_I2S_DL_MASK (3 << 2) +#define ALC5632_DAI_I2S_DL_8 (3 << 2) +#define ALC5632_DAI_I2S_DL_24 (2 << 2) +#define ALC5632_DAI_I2S_DL_20 (1 << 2) +#define ALC5632_DAI_I2S_DL_16 (0 << 2) +#define ALC5632_DAI_I2S_DF_MASK (3 << 0) +#define ALC5632_DAI_I2S_DF_PCM_B (3 << 0) +#define ALC5632_DAI_I2S_DF_PCM_A (2 << 0) +#define ALC5632_DAI_I2S_DF_LEFT (1 << 0) +#define ALC5632_DAI_I2S_DF_I2S (0 << 0) +/* extend serial data port control (VoDAC_i2c/pcm) */ +#define ALC5632_DAI_CONTROL2 0x36 +/* 0:gpio func, 1:voice pcm */ +#define ALC5632_DAI_VOICE_PCM_ENABLE (1 << 15) +/* 0:master, 1:slave */ +#define ALC5632_DAI_VOICE_MODE_SEL (1 << 14) +/* 0:disable, 1:enable */ +#define ALC5632_DAI_HPF_CLK_CTRL (1 << 13) +/* 0:main, 1:voice */ +#define ALC5632_DAI_VOICE_I2S_SYSCLK_SEL (1 << 8) +/* 0:normal, 1:invert */ +#define ALC5632_DAI_VOICE_VBCLK_SYSCLK_SEL (1 << 7) +/* 0:normal, 1:invert */ +#define ALC5632_DAI_VOICE_I2S_LR_INV (1 << 6) +#define ALC5632_DAI_VOICE_DL_MASK (3 << 2) +#define ALC5632_DAI_VOICE_DL_16 (0 << 2) +#define ALC5632_DAI_VOICE_DL_20 (1 << 2) +#define ALC5632_DAI_VOICE_DL_24 (2 << 2) +#define ALC5632_DAI_VOICE_DL_8 (3 << 2) +#define ALC5632_DAI_VOICE_DF_MASK (3 << 0) +#define ALC5632_DAI_VOICE_DF_I2S (0 << 0) +#define ALC5632_DAI_VOICE_DF_LEFT (1 << 0) +#define ALC5632_DAI_VOICE_DF_PCM_A (2 << 0) +#define ALC5632_DAI_VOICE_DF_PCM_B (3 << 0) + +#define ALC5632_PWR_MANAG_ADD1 0x3A +#define ALC5632_PWR_MANAG_ADD1_MASK 0xEFFF +#define ALC5632_PWR_ADD1_DAC_L_EN (1 << 15) +#define ALC5632_PWR_ADD1_DAC_R_EN (1 << 14) +#define ALC5632_PWR_ADD1_ZERO_CROSS (1 << 13) +#define ALC5632_PWR_ADD1_MAIN_I2S_EN (1 << 11) +#define ALC5632_PWR_ADD1_SPK_AMP_EN (1 << 10) +#define ALC5632_PWR_ADD1_HP_OUT_AMP (1 << 9) +#define ALC5632_PWR_ADD1_HP_OUT_ENH_AMP (1 << 8) +#define ALC5632_PWR_ADD1_VOICE_DAC_MIX (1 << 7) +#define ALC5632_PWR_ADD1_SOFTGEN_EN (1 << 6) +#define ALC5632_PWR_ADD1_MIC1_SHORT_CURR (1 << 5) +#define ALC5632_PWR_ADD1_MIC2_SHORT_CURR (1 << 4) +#define ALC5632_PWR_ADD1_MIC1_EN (1 << 3) +#define ALC5632_PWR_ADD1_MIC2_EN (1 << 2) +#define ALC5632_PWR_ADD1_MAIN_BIAS (1 << 1) +#define ALC5632_PWR_ADD1_DAC_REF (1 << 0) + +#define ALC5632_PWR_MANAG_ADD2 0x3C +#define ALC5632_PWR_MANAG_ADD2_MASK 0x7FFF +#define ALC5632_PWR_ADD2_PLL1 (1 << 15) +#define ALC5632_PWR_ADD2_PLL2 (1 << 14) +#define ALC5632_PWR_ADD2_VREF (1 << 13) +#define ALC5632_PWR_ADD2_OVT_DET (1 << 12) +#define ALC5632_PWR_ADD2_VOICE_DAC (1 << 10) +#define ALC5632_PWR_ADD2_L_DAC_CLK (1 << 9) +#define ALC5632_PWR_ADD2_R_DAC_CLK (1 << 8) +#define ALC5632_PWR_ADD2_L_ADC_CLK_GAIN (1 << 7) +#define ALC5632_PWR_ADD2_R_ADC_CLK_GAIN (1 << 6) +#define ALC5632_PWR_ADD2_L_HP_MIXER (1 << 5) +#define ALC5632_PWR_ADD2_R_HP_MIXER (1 << 4) +#define ALC5632_PWR_ADD2_SPK_MIXER (1 << 3) +#define ALC5632_PWR_ADD2_MONO_MIXER (1 << 2) +#define ALC5632_PWR_ADD2_L_ADC_REC_MIXER (1 << 1) +#define ALC5632_PWR_ADD2_R_ADC_REC_MIXER (1 << 0) + +#define ALC5632_PWR_MANAG_ADD3 0x3E +#define ALC5632_PWR_MANAG_ADD3_MASK 0x7CFF +#define ALC5632_PWR_ADD3_AUXOUT_VOL (1 << 14) +#define ALC5632_PWR_ADD3_SPK_L_OUT (1 << 13) +#define ALC5632_PWR_ADD3_SPK_R_OUT (1 << 12) +#define ALC5632_PWR_ADD3_HP_L_OUT_VOL (1 << 11) +#define ALC5632_PWR_ADD3_HP_R_OUT_VOL (1 << 10) +#define ALC5632_PWR_ADD3_LINEIN_L_VOL (1 << 7) +#define ALC5632_PWR_ADD3_LINEIN_R_VOL (1 << 6) +#define ALC5632_PWR_ADD3_AUXIN_VOL (1 << 5) +#define ALC5632_PWR_ADD3_AUXIN_MIX (1 << 4) +#define ALC5632_PWR_ADD3_MIC1_VOL (1 << 3) +#define ALC5632_PWR_ADD3_MIC2_VOL (1 << 2) +#define ALC5632_PWR_ADD3_MIC1_BOOST_AD (1 << 1) +#define ALC5632_PWR_ADD3_MIC2_BOOST_AD (1 << 0) + +#define ALC5632_GPCR1 0x40 +#define ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1 (1 << 15) +#define ALC5632_GPCR1_CLK_SYS_SRC_SEL_MCLK (0 << 15) +#define ALC5632_GPCR1_DAC_HI_FLT_EN (1 << 10) +#define ALC5632_GPCR1_SPK_AMP_CTRL (7 << 1) +#define ALC5632_GPCR1_VDD_100 (5 << 1) +#define ALC5632_GPCR1_VDD_125 (4 << 1) +#define ALC5632_GPCR1_VDD_150 (3 << 1) +#define ALC5632_GPCR1_VDD_175 (2 << 1) +#define ALC5632_GPCR1_VDD_200 (1 << 1) +#define ALC5632_GPCR1_VDD_225 (0 << 1) + +#define ALC5632_GPCR2 0x42 +#define ALC5632_GPCR2_PLL1_SOUR_SEL (3 << 12) +#define ALC5632_PLL_FR_MCLK (0 << 12) +#define ALC5632_PLL_FR_BCLK (2 << 12) +#define ALC5632_PLL_FR_VBCLK (3 << 12) +#define ALC5632_GPCR2_CLK_PLL_PRE_DIV1 (0 << 0) + +#define ALC5632_PLL1_CTRL 0x44 +#define ALC5632_PLL1_CTRL_N_VAL(n) (((n) & 0x0f) << 8) +#define ALC5632_PLL1_M_BYPASS (1 << 7) +#define ALC5632_PLL1_CTRL_K_VAL(k) (((k) & 0x07) << 4) +#define ALC5632_PLL1_CTRL_M_VAL(m) (((m) & 0x0f) << 0) + +#define ALC5632_PLL2_CTRL 0x46 +#define ALC5632_PLL2_EN (1 << 15) +#define ALC5632_PLL2_RATIO (0 << 15) + +#define ALC5632_GPIO_PIN_CONFIG 0x4C +#define ALC5632_GPIO_PIN_POLARITY 0x4E +#define ALC5632_GPIO_PIN_STICKY 0x50 +#define ALC5632_GPIO_PIN_WAKEUP 0x52 +#define ALC5632_GPIO_PIN_STATUS 0x54 +#define ALC5632_GPIO_PIN_SHARING 0x56 +#define ALC5632_OVER_CURR_STATUS 0x58 +#define ALC5632_SOFTVOL_CTRL 0x5A +#define ALC5632_GPIO_OUPUT_PIN_CTRL 0x5C + +#define ALC5632_MISC_CTRL 0x5E +#define ALC5632_MISC_DISABLE_FAST_VREG (1 << 15) +#define ALC5632_MISC_AVC_TRGT_SEL (3 << 12) +#define ALC5632_MISC_AVC_TRGT_RIGHT (1 << 12) +#define ALC5632_MISC_AVC_TRGT_LEFT (2 << 12) +#define ALC5632_MISC_AVC_TRGT_BOTH (3 << 12) +#define ALC5632_MISC_HP_DEPOP_MODE1_EN (1 << 9) +#define ALC5632_MISC_HP_DEPOP_MODE2_EN (1 << 8) +#define ALC5632_MISC_HP_DEPOP_MUTE_L (1 << 7) +#define ALC5632_MISC_HP_DEPOP_MUTE_R (1 << 6) +#define ALC5632_MISC_HP_DEPOP_MUTE (1 << 5) +#define ALC5632_MISC_GPIO_WAKEUP_CTRL (1 << 1) +#define ALC5632_MISC_IRQOUT_INV_CTRL (1 << 0) + +#define ALC5632_DAC_CLK_CTRL1 0x60 +#define ALC5632_DAC_CLK_CTRL2 0x62 +#define ALC5632_DAC_CLK_CTRL2_DIV1_2 (1 << 0) +#define ALC5632_VOICE_DAC_PCM_CLK_CTRL1 0x64 +#define ALC5632_PSEUDO_SPATIAL_CTRL 0x68 +#define ALC5632_HID_CTRL_INDEX 0x6A +#define ALC5632_HID_CTRL_DATA 0x6C +#define ALC5632_EQ_CTRL 0x6E + +/* undocumented */ +#define ALC5632_VENDOR_ID1 0x7C +#define ALC5632_VENDOR_ID2 0x7E + +#define ALC5632_MAX_REGISTER 0x7E + +#endif diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c new file mode 100644 index 0000000000..9c15ddba60 --- /dev/null +++ b/sound/soc/codecs/arizona-jack.c @@ -0,0 +1,1657 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * extcon-arizona.c - Extcon driver Wolfson Arizona devices + * + * Copyright (C) 2012-2014 Wolfson Microelectronics plc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "arizona.h" + +#define ARIZONA_MAX_MICD_RANGE 8 + +/* + * The hardware supports 8 ranges / buttons, but the snd-jack interface + * only supports 6 buttons (button 0-5). + */ +#define ARIZONA_MAX_MICD_BUTTONS 6 + +#define ARIZONA_MICD_CLAMP_MODE_JDL 0x4 +#define ARIZONA_MICD_CLAMP_MODE_JDH 0x5 +#define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9 +#define ARIZONA_MICD_CLAMP_MODE_JDH_GP5H 0xb + +#define ARIZONA_TST_CAP_DEFAULT 0x3 +#define ARIZONA_TST_CAP_CLAMP 0x1 + +#define ARIZONA_HPDET_MAX 10000 + +#define HPDET_DEBOUNCE 500 +#define DEFAULT_MICD_TIMEOUT 2000 + +#define ARIZONA_HPDET_WAIT_COUNT 15 +#define ARIZONA_HPDET_WAIT_DELAY_MS 20 + +#define QUICK_HEADPHONE_MAX_OHM 3 +#define MICROPHONE_MIN_OHM 1257 +#define MICROPHONE_MAX_OHM 30000 + +#define MICD_DBTIME_TWO_READINGS 2 +#define MICD_DBTIME_FOUR_READINGS 4 + +#define MICD_LVL_1_TO_7 (ARIZONA_MICD_LVL_1 | ARIZONA_MICD_LVL_2 | \ + ARIZONA_MICD_LVL_3 | ARIZONA_MICD_LVL_4 | \ + ARIZONA_MICD_LVL_5 | ARIZONA_MICD_LVL_6 | \ + ARIZONA_MICD_LVL_7) + +#define MICD_LVL_0_TO_7 (ARIZONA_MICD_LVL_0 | MICD_LVL_1_TO_7) + +#define MICD_LVL_0_TO_8 (MICD_LVL_0_TO_7 | ARIZONA_MICD_LVL_8) + +static const struct arizona_micd_config micd_default_modes[] = { + { ARIZONA_ACCDET_SRC, 1, 0 }, + { 0, 2, 1 }, +}; + +static const struct arizona_micd_range micd_default_ranges[] = { + { .max = 11, .key = BTN_0 }, + { .max = 28, .key = BTN_1 }, + { .max = 54, .key = BTN_2 }, + { .max = 100, .key = BTN_3 }, + { .max = 186, .key = BTN_4 }, + { .max = 430, .key = BTN_5 }, +}; + +/* The number of levels in arizona_micd_levels valid for button thresholds */ +#define ARIZONA_NUM_MICD_BUTTON_LEVELS 64 + +static const int arizona_micd_levels[] = { + 3, 6, 8, 11, 13, 16, 18, 21, 23, 26, 28, 31, 34, 36, 39, 41, 44, 46, + 49, 52, 54, 57, 60, 62, 65, 67, 70, 73, 75, 78, 81, 83, 89, 94, 100, + 105, 111, 116, 122, 127, 139, 150, 161, 173, 186, 196, 209, 220, 245, + 270, 295, 321, 348, 375, 402, 430, 489, 550, 614, 681, 752, 903, 1071, + 1257, 30000, +}; + +static void arizona_start_hpdet_acc_id(struct arizona_priv *info); + +static void arizona_extcon_hp_clamp(struct arizona_priv *info, + bool clamp) +{ + struct arizona *arizona = info->arizona; + unsigned int mask = 0, val = 0; + unsigned int cap_sel = 0; + int ret; + + switch (arizona->type) { + case WM8998: + case WM1814: + mask = 0; + break; + case WM5110: + case WM8280: + mask = ARIZONA_HP1L_SHRTO | ARIZONA_HP1L_FLWR | + ARIZONA_HP1L_SHRTI; + if (clamp) { + val = ARIZONA_HP1L_SHRTO; + cap_sel = ARIZONA_TST_CAP_CLAMP; + } else { + val = ARIZONA_HP1L_FLWR | ARIZONA_HP1L_SHRTI; + cap_sel = ARIZONA_TST_CAP_DEFAULT; + } + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HP_TEST_CTRL_1, + ARIZONA_HP1_TST_CAP_SEL_MASK, + cap_sel); + if (ret) + dev_warn(arizona->dev, "Failed to set TST_CAP_SEL: %d\n", ret); + break; + default: + mask = ARIZONA_RMV_SHRT_HP1L; + if (clamp) + val = ARIZONA_RMV_SHRT_HP1L; + break; + } + + snd_soc_dapm_mutex_lock(arizona->dapm); + + arizona->hpdet_clamp = clamp; + + /* Keep the HP output stages disabled while doing the clamp */ + if (clamp) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT1L_ENA | + ARIZONA_OUT1R_ENA, 0); + if (ret) + dev_warn(arizona->dev, "Failed to disable headphone outputs: %d\n", ret); + } + + if (mask) { + ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L, + mask, val); + if (ret) + dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret); + + ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R, + mask, val); + if (ret) + dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret); + } + + /* Restore the desired state while not doing the clamp */ + if (!clamp) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT1L_ENA | + ARIZONA_OUT1R_ENA, arizona->hp_ena); + if (ret) + dev_warn(arizona->dev, "Failed to restore headphone outputs: %d\n", ret); + } + + snd_soc_dapm_mutex_unlock(arizona->dapm); +} + +static void arizona_extcon_set_mode(struct arizona_priv *info, int mode) +{ + struct arizona *arizona = info->arizona; + + mode %= info->micd_num_modes; + + gpiod_set_value_cansleep(info->micd_pol_gpio, + info->micd_modes[mode].gpio); + + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_BIAS_SRC_MASK, + info->micd_modes[mode].bias << + ARIZONA_MICD_BIAS_SRC_SHIFT); + regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_SRC, info->micd_modes[mode].src); + + info->micd_mode = mode; + + dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode); +} + +static const char *arizona_extcon_get_micbias(struct arizona_priv *info) +{ + switch (info->micd_modes[0].bias) { + case 1: + return "MICBIAS1"; + case 2: + return "MICBIAS2"; + case 3: + return "MICBIAS3"; + default: + return "MICVDD"; + } +} + +static void arizona_extcon_pulse_micbias(struct arizona_priv *info) +{ + struct arizona *arizona = info->arizona; + const char *widget = arizona_extcon_get_micbias(info); + struct snd_soc_dapm_context *dapm = arizona->dapm; + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + int ret; + + ret = snd_soc_component_force_enable_pin(component, widget); + if (ret) + dev_warn(arizona->dev, "Failed to enable %s: %d\n", widget, ret); + + snd_soc_dapm_sync(dapm); + + if (!arizona->pdata.micd_force_micbias) { + ret = snd_soc_component_disable_pin(component, widget); + if (ret) + dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret); + + snd_soc_dapm_sync(dapm); + } +} + +static void arizona_start_mic(struct arizona_priv *info) +{ + struct arizona *arizona = info->arizona; + bool change; + int ret; + unsigned int mode; + + /* Microphone detection can't use idle mode */ + pm_runtime_get_sync(arizona->dev); + + if (info->detecting) { + ret = regulator_allow_bypass(info->micvdd, false); + if (ret) + dev_err(arizona->dev, "Failed to regulate MICVDD: %d\n", ret); + } + + ret = regulator_enable(info->micvdd); + if (ret) + dev_err(arizona->dev, "Failed to enable MICVDD: %d\n", ret); + + if (info->micd_reva) { + const struct reg_sequence reva[] = { + { 0x80, 0x3 }, + { 0x294, 0x0 }, + { 0x80, 0x0 }, + }; + + regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva)); + } + + if (info->detecting && arizona->pdata.micd_software_compare) + mode = ARIZONA_ACCDET_MODE_ADC; + else + mode = ARIZONA_ACCDET_MODE_MIC; + + regmap_update_bits(arizona->regmap, + ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_MODE_MASK, mode); + + arizona_extcon_pulse_micbias(info); + + ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, ARIZONA_MICD_ENA, + &change); + if (ret < 0) { + dev_err(arizona->dev, "Failed to enable micd: %d\n", ret); + } else if (!change) { + regulator_disable(info->micvdd); + pm_runtime_put_autosuspend(arizona->dev); + } +} + +static void arizona_stop_mic(struct arizona_priv *info) +{ + struct arizona *arizona = info->arizona; + const char *widget = arizona_extcon_get_micbias(info); + struct snd_soc_dapm_context *dapm = arizona->dapm; + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + bool change = false; + int ret; + + ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, 0, + &change); + if (ret < 0) + dev_err(arizona->dev, "Failed to disable micd: %d\n", ret); + + ret = snd_soc_component_disable_pin(component, widget); + if (ret) + dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret); + + snd_soc_dapm_sync(dapm); + + if (info->micd_reva) { + const struct reg_sequence reva[] = { + { 0x80, 0x3 }, + { 0x294, 0x2 }, + { 0x80, 0x0 }, + }; + + regmap_multi_reg_write(arizona->regmap, reva, ARRAY_SIZE(reva)); + } + + ret = regulator_allow_bypass(info->micvdd, true); + if (ret) + dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", ret); + + if (change) { + regulator_disable(info->micvdd); + pm_runtime_mark_last_busy(arizona->dev); + pm_runtime_put_autosuspend(arizona->dev); + } +} + +static struct { + unsigned int threshold; + unsigned int factor_a; + unsigned int factor_b; +} arizona_hpdet_b_ranges[] = { + { 100, 5528, 362464 }, + { 169, 11084, 6186851 }, + { 169, 11065, 65460395 }, +}; + +#define ARIZONA_HPDET_B_RANGE_MAX 0x3fb + +static struct { + int min; + int max; +} arizona_hpdet_c_ranges[] = { + { 0, 30 }, + { 8, 100 }, + { 100, 1000 }, + { 1000, 10000 }, +}; + +static int arizona_hpdet_read(struct arizona_priv *info) +{ + struct arizona *arizona = info->arizona; + unsigned int val, range; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2, &val); + if (ret) { + dev_err(arizona->dev, "Failed to read HPDET status: %d\n", ret); + return ret; + } + + switch (info->hpdet_ip_version) { + case 0: + if (!(val & ARIZONA_HP_DONE)) { + dev_err(arizona->dev, "HPDET did not complete: %x\n", val); + return -EAGAIN; + } + + val &= ARIZONA_HP_LVL_MASK; + break; + + case 1: + if (!(val & ARIZONA_HP_DONE_B)) { + dev_err(arizona->dev, "HPDET did not complete: %x\n", val); + return -EAGAIN; + } + + ret = regmap_read(arizona->regmap, ARIZONA_HP_DACVAL, &val); + if (ret) { + dev_err(arizona->dev, "Failed to read HP value: %d\n", ret); + return -EAGAIN; + } + + regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, + &range); + range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK) + >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; + + if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 && + (val < arizona_hpdet_b_ranges[range].threshold || + val >= ARIZONA_HPDET_B_RANGE_MAX)) { + range++; + dev_dbg(arizona->dev, "Moving to HPDET range %d\n", range); + regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_IMPEDANCE_RANGE_MASK, + range << + ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); + return -EAGAIN; + } + + /* If we go out of range report top of range */ + if (val < arizona_hpdet_b_ranges[range].threshold || + val >= ARIZONA_HPDET_B_RANGE_MAX) { + dev_dbg(arizona->dev, "Measurement out of range\n"); + return ARIZONA_HPDET_MAX; + } + + dev_dbg(arizona->dev, "HPDET read %d in range %d\n", val, range); + + val = arizona_hpdet_b_ranges[range].factor_b + / ((val * 100) - + arizona_hpdet_b_ranges[range].factor_a); + break; + + case 2: + if (!(val & ARIZONA_HP_DONE_B)) { + dev_err(arizona->dev, "HPDET did not complete: %x\n", val); + return -EAGAIN; + } + + val &= ARIZONA_HP_LVL_B_MASK; + /* Convert to ohms, the value is in 0.5 ohm increments */ + val /= 2; + + regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, + &range); + range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK) + >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; + + /* Skip up a range, or report? */ + if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 && + (val >= arizona_hpdet_c_ranges[range].max)) { + range++; + dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n", + arizona_hpdet_c_ranges[range].min, + arizona_hpdet_c_ranges[range].max); + regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_IMPEDANCE_RANGE_MASK, + range << + ARIZONA_HP_IMPEDANCE_RANGE_SHIFT); + return -EAGAIN; + } + + if (range && (val < arizona_hpdet_c_ranges[range].min)) { + dev_dbg(arizona->dev, "Reporting range boundary %d\n", + arizona_hpdet_c_ranges[range].min); + val = arizona_hpdet_c_ranges[range].min; + } + break; + + default: + dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n", info->hpdet_ip_version); + return -EINVAL; + } + + dev_dbg(arizona->dev, "HP impedance %d ohms\n", val); + return val; +} + +static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading, + bool *mic) +{ + struct arizona *arizona = info->arizona; + int id_gpio = arizona->pdata.hpdet_id_gpio; + + if (!arizona->pdata.hpdet_acc_id) + return 0; + + /* + * If we're using HPDET for accessory identification we need + * to take multiple measurements, step through them in sequence. + */ + info->hpdet_res[info->num_hpdet_res++] = *reading; + + /* Only check the mic directly if we didn't already ID it */ + if (id_gpio && info->num_hpdet_res == 1) { + dev_dbg(arizona->dev, "Measuring mic\n"); + + regmap_update_bits(arizona->regmap, + ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_MODE_MASK | + ARIZONA_ACCDET_SRC, + ARIZONA_ACCDET_MODE_HPR | + info->micd_modes[0].src); + + gpio_set_value_cansleep(id_gpio, 1); + + regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_POLL, ARIZONA_HP_POLL); + return -EAGAIN; + } + + /* OK, got both. Now, compare... */ + dev_dbg(arizona->dev, "HPDET measured %d %d\n", + info->hpdet_res[0], info->hpdet_res[1]); + + /* Take the headphone impedance for the main report */ + *reading = info->hpdet_res[0]; + + /* Sometimes we get false readings due to slow insert */ + if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) { + dev_dbg(arizona->dev, "Retrying high impedance\n"); + info->num_hpdet_res = 0; + info->hpdet_retried = true; + arizona_start_hpdet_acc_id(info); + pm_runtime_put(arizona->dev); + return -EAGAIN; + } + + /* + * If we measure the mic as high impedance + */ + if (!id_gpio || info->hpdet_res[1] > 50) { + dev_dbg(arizona->dev, "Detected mic\n"); + *mic = true; + info->detecting = true; + } else { + dev_dbg(arizona->dev, "Detected headphone\n"); + } + + /* Make sure everything is reset back to the real polarity */ + regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_SRC, info->micd_modes[0].src); + + return 0; +} + +static irqreturn_t arizona_hpdet_irq(int irq, void *data) +{ + struct arizona_priv *info = data; + struct arizona *arizona = info->arizona; + int id_gpio = arizona->pdata.hpdet_id_gpio; + int ret, reading, state, report; + bool mic = false; + + mutex_lock(&info->lock); + + /* If we got a spurious IRQ for some reason then ignore it */ + if (!info->hpdet_active) { + dev_warn(arizona->dev, "Spurious HPDET IRQ\n"); + mutex_unlock(&info->lock); + return IRQ_NONE; + } + + /* If the cable was removed while measuring ignore the result */ + state = info->jack->status & SND_JACK_MECHANICAL; + if (!state) { + dev_dbg(arizona->dev, "Ignoring HPDET for removed cable\n"); + goto done; + } + + ret = arizona_hpdet_read(info); + if (ret == -EAGAIN) + goto out; + else if (ret < 0) + goto done; + reading = ret; + + /* Reset back to starting range */ + regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL, + 0); + + ret = arizona_hpdet_do_id(info, &reading, &mic); + if (ret == -EAGAIN) + goto out; + else if (ret < 0) + goto done; + + /* Report high impedence cables as line outputs */ + if (reading >= 5000) + report = SND_JACK_LINEOUT; + else + report = SND_JACK_HEADPHONE; + + snd_soc_jack_report(info->jack, report, SND_JACK_LINEOUT | SND_JACK_HEADPHONE); + +done: + /* Reset back to starting range */ + regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL, + 0); + + arizona_extcon_hp_clamp(info, false); + + if (id_gpio) + gpio_set_value_cansleep(id_gpio, 0); + + /* If we have a mic then reenable MICDET */ + if (state && (mic || info->mic)) + arizona_start_mic(info); + + if (info->hpdet_active) { + pm_runtime_put_autosuspend(arizona->dev); + info->hpdet_active = false; + } + + /* Do not set hp_det done when the cable has been unplugged */ + if (state) + info->hpdet_done = true; + +out: + mutex_unlock(&info->lock); + + return IRQ_HANDLED; +} + +static void arizona_identify_headphone(struct arizona_priv *info) +{ + struct arizona *arizona = info->arizona; + int ret; + + if (info->hpdet_done) + return; + + dev_dbg(arizona->dev, "Starting HPDET\n"); + + /* Make sure we keep the device enabled during the measurement */ + pm_runtime_get_sync(arizona->dev); + + info->hpdet_active = true; + + arizona_stop_mic(info); + + arizona_extcon_hp_clamp(info, true); + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_MODE_MASK, + arizona->pdata.hpdet_channel); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret); + goto err; + } + + ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_POLL, ARIZONA_HP_POLL); + if (ret) { + dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n", ret); + goto err; + } + + return; + +err: + arizona_extcon_hp_clamp(info, false); + pm_runtime_put_autosuspend(arizona->dev); + + /* Just report headphone */ + snd_soc_jack_report(info->jack, SND_JACK_HEADPHONE, + SND_JACK_LINEOUT | SND_JACK_HEADPHONE); + + if (info->mic) + arizona_start_mic(info); + + info->hpdet_active = false; +} + +static void arizona_start_hpdet_acc_id(struct arizona_priv *info) +{ + struct arizona *arizona = info->arizona; + int hp_reading = 32; + bool mic; + int ret; + + dev_dbg(arizona->dev, "Starting identification via HPDET\n"); + + /* Make sure we keep the device enabled during the measurement */ + pm_runtime_get_sync(arizona->dev); + + info->hpdet_active = true; + + arizona_extcon_hp_clamp(info, true); + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_ACCESSORY_DETECT_MODE_1, + ARIZONA_ACCDET_SRC | ARIZONA_ACCDET_MODE_MASK, + info->micd_modes[0].src | + arizona->pdata.hpdet_channel); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set HPDET mode: %d\n", ret); + goto err; + } + + if (arizona->pdata.hpdet_acc_id_line) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HEADPHONE_DETECT_1, + ARIZONA_HP_POLL, ARIZONA_HP_POLL); + if (ret) { + dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n", ret); + goto err; + } + } else { + arizona_hpdet_do_id(info, &hp_reading, &mic); + } + + return; + +err: + /* Just report headphone */ + snd_soc_jack_report(info->jack, SND_JACK_HEADPHONE, + SND_JACK_LINEOUT | SND_JACK_HEADPHONE); + + info->hpdet_active = false; +} + +static void arizona_micd_timeout_work(struct work_struct *work) +{ + struct arizona_priv *info = container_of(work, + struct arizona_priv, + micd_timeout_work.work); + + mutex_lock(&info->lock); + + dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); + + info->detecting = false; + + arizona_identify_headphone(info); + + mutex_unlock(&info->lock); +} + +static int arizona_micd_adc_read(struct arizona_priv *info) +{ + struct arizona *arizona = info->arizona; + unsigned int val; + int ret; + + /* Must disable MICD before we read the ADCVAL */ + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, 0); + + ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_4, &val); + if (ret) { + dev_err(arizona->dev, "Failed to read MICDET_ADCVAL: %d\n", ret); + return ret; + } + + dev_dbg(arizona->dev, "MICDET_ADCVAL: %x\n", val); + + val &= ARIZONA_MICDET_ADCVAL_MASK; + if (val < ARRAY_SIZE(arizona_micd_levels)) + val = arizona_micd_levels[val]; + else + val = INT_MAX; + + if (val <= QUICK_HEADPHONE_MAX_OHM) + val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_0; + else if (val <= MICROPHONE_MIN_OHM) + val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_1; + else if (val <= MICROPHONE_MAX_OHM) + val = ARIZONA_MICD_STS | ARIZONA_MICD_LVL_8; + else + val = ARIZONA_MICD_LVL_8; + + return val; +} + +static int arizona_micd_read(struct arizona_priv *info) +{ + struct arizona *arizona = info->arizona; + unsigned int val = 0; + int ret, i; + + for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) { + ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val); + if (ret) { + dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret); + return ret; + } + + dev_dbg(arizona->dev, "MICDET: %x\n", val); + + if (!(val & ARIZONA_MICD_VALID)) { + dev_warn(arizona->dev, "Microphone detection state invalid\n"); + return -EINVAL; + } + } + + if (i == 10 && !(val & MICD_LVL_0_TO_8)) { + dev_err(arizona->dev, "Failed to get valid MICDET value\n"); + return -EINVAL; + } + + return val; +} + +static int arizona_micdet_reading(void *priv) +{ + struct arizona_priv *info = priv; + struct arizona *arizona = info->arizona; + int ret, val; + + if (info->detecting && arizona->pdata.micd_software_compare) + ret = arizona_micd_adc_read(info); + else + ret = arizona_micd_read(info); + if (ret < 0) + return ret; + + val = ret; + + /* Due to jack detect this should never happen */ + if (!(val & ARIZONA_MICD_STS)) { + dev_warn(arizona->dev, "Detected open circuit\n"); + info->mic = false; + info->detecting = false; + arizona_identify_headphone(info); + return 0; + } + + /* If we got a high impedence we should have a headset, report it. */ + if (val & ARIZONA_MICD_LVL_8) { + info->mic = true; + info->detecting = false; + + arizona_identify_headphone(info); + + snd_soc_jack_report(info->jack, SND_JACK_MICROPHONE, SND_JACK_MICROPHONE); + + /* Don't need to regulate for button detection */ + ret = regulator_allow_bypass(info->micvdd, true); + if (ret) + dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", ret); + + return 0; + } + + /* If we detected a lower impedence during initial startup + * then we probably have the wrong polarity, flip it. Don't + * do this for the lowest impedences to speed up detection of + * plain headphones. If both polarities report a low + * impedence then give up and report headphones. + */ + if (val & MICD_LVL_1_TO_7) { + if (info->jack_flips >= info->micd_num_modes * 10) { + dev_dbg(arizona->dev, "Detected HP/line\n"); + + info->detecting = false; + + arizona_identify_headphone(info); + } else { + info->micd_mode++; + if (info->micd_mode == info->micd_num_modes) + info->micd_mode = 0; + arizona_extcon_set_mode(info, info->micd_mode); + + info->jack_flips++; + + if (arizona->pdata.micd_software_compare) + regmap_update_bits(arizona->regmap, + ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, + ARIZONA_MICD_ENA); + + queue_delayed_work(system_power_efficient_wq, + &info->micd_timeout_work, + msecs_to_jiffies(arizona->pdata.micd_timeout)); + } + + return 0; + } + + /* + * If we're still detecting and we detect a short then we've + * got a headphone. + */ + dev_dbg(arizona->dev, "Headphone detected\n"); + info->detecting = false; + + arizona_identify_headphone(info); + + return 0; +} + +static int arizona_button_reading(void *priv) +{ + struct arizona_priv *info = priv; + struct arizona *arizona = info->arizona; + int val, key, lvl; + + val = arizona_micd_read(info); + if (val < 0) + return val; + + /* + * If we're still detecting and we detect a short then we've + * got a headphone. Otherwise it's a button press. + */ + if (val & MICD_LVL_0_TO_7) { + if (info->mic) { + dev_dbg(arizona->dev, "Mic button detected\n"); + + lvl = val & ARIZONA_MICD_LVL_MASK; + lvl >>= ARIZONA_MICD_LVL_SHIFT; + + if (lvl && ffs(lvl) - 1 < info->num_micd_ranges) { + key = ffs(lvl) - 1; + snd_soc_jack_report(info->jack, + SND_JACK_BTN_0 >> key, + info->micd_button_mask); + } else { + dev_err(arizona->dev, "Button out of range\n"); + } + } else { + dev_warn(arizona->dev, "Button with no mic: %x\n", val); + } + } else { + dev_dbg(arizona->dev, "Mic button released\n"); + snd_soc_jack_report(info->jack, 0, info->micd_button_mask); + arizona_extcon_pulse_micbias(info); + } + + return 0; +} + +static void arizona_micd_detect(struct work_struct *work) +{ + struct arizona_priv *info = container_of(work, + struct arizona_priv, + micd_detect_work.work); + struct arizona *arizona = info->arizona; + + cancel_delayed_work_sync(&info->micd_timeout_work); + + mutex_lock(&info->lock); + + /* If the cable was removed while measuring ignore the result */ + if (!(info->jack->status & SND_JACK_MECHANICAL)) { + dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n"); + mutex_unlock(&info->lock); + return; + } + + if (info->detecting) + arizona_micdet_reading(info); + else + arizona_button_reading(info); + + pm_runtime_mark_last_busy(arizona->dev); + mutex_unlock(&info->lock); +} + +static irqreturn_t arizona_micdet(int irq, void *data) +{ + struct arizona_priv *info = data; + struct arizona *arizona = info->arizona; + int debounce = arizona->pdata.micd_detect_debounce; + + cancel_delayed_work_sync(&info->micd_detect_work); + cancel_delayed_work_sync(&info->micd_timeout_work); + + mutex_lock(&info->lock); + if (!info->detecting) + debounce = 0; + mutex_unlock(&info->lock); + + if (debounce) + queue_delayed_work(system_power_efficient_wq, + &info->micd_detect_work, + msecs_to_jiffies(debounce)); + else + arizona_micd_detect(&info->micd_detect_work.work); + + return IRQ_HANDLED; +} + +static void arizona_hpdet_work(struct work_struct *work) +{ + struct arizona_priv *info = container_of(work, + struct arizona_priv, + hpdet_work.work); + + mutex_lock(&info->lock); + arizona_start_hpdet_acc_id(info); + mutex_unlock(&info->lock); +} + +static int arizona_hpdet_wait(struct arizona_priv *info) +{ + struct arizona *arizona = info->arizona; + unsigned int val; + int i, ret; + + for (i = 0; i < ARIZONA_HPDET_WAIT_COUNT; i++) { + ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2, + &val); + if (ret) { + dev_err(arizona->dev, "Failed to read HPDET state: %d\n", ret); + return ret; + } + + switch (info->hpdet_ip_version) { + case 0: + if (val & ARIZONA_HP_DONE) + return 0; + break; + default: + if (val & ARIZONA_HP_DONE_B) + return 0; + break; + } + + msleep(ARIZONA_HPDET_WAIT_DELAY_MS); + } + + dev_warn(arizona->dev, "HPDET did not appear to complete\n"); + + return -ETIMEDOUT; +} + +static irqreturn_t arizona_jackdet(int irq, void *data) +{ + struct arizona_priv *info = data; + struct arizona *arizona = info->arizona; + unsigned int val, present, mask; + bool cancelled_hp, cancelled_mic; + int ret, i; + + cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work); + cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work); + + pm_runtime_get_sync(arizona->dev); + + mutex_lock(&info->lock); + + if (info->micd_clamp) { + mask = ARIZONA_MICD_CLAMP_STS; + present = 0; + } else { + mask = ARIZONA_JD1_STS; + if (arizona->pdata.jd_invert) + present = 0; + else + present = ARIZONA_JD1_STS; + } + + ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); + if (ret) { + dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret); + mutex_unlock(&info->lock); + pm_runtime_put_autosuspend(arizona->dev); + return IRQ_NONE; + } + + val &= mask; + if (val == info->last_jackdet) { + dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n"); + if (cancelled_hp) + queue_delayed_work(system_power_efficient_wq, + &info->hpdet_work, + msecs_to_jiffies(HPDET_DEBOUNCE)); + + if (cancelled_mic) { + int micd_timeout = arizona->pdata.micd_timeout; + + queue_delayed_work(system_power_efficient_wq, + &info->micd_timeout_work, + msecs_to_jiffies(micd_timeout)); + } + + goto out; + } + info->last_jackdet = val; + + if (info->last_jackdet == present) { + dev_dbg(arizona->dev, "Detected jack\n"); + snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); + + info->detecting = true; + info->mic = false; + info->jack_flips = 0; + + if (!arizona->pdata.hpdet_acc_id) { + arizona_start_mic(info); + } else { + queue_delayed_work(system_power_efficient_wq, + &info->hpdet_work, + msecs_to_jiffies(HPDET_DEBOUNCE)); + } + + if (info->micd_clamp || !arizona->pdata.jd_invert) + regmap_update_bits(arizona->regmap, + ARIZONA_JACK_DETECT_DEBOUNCE, + ARIZONA_MICD_CLAMP_DB | + ARIZONA_JD1_DB, 0); + } else { + dev_dbg(arizona->dev, "Detected jack removal\n"); + + arizona_stop_mic(info); + + info->num_hpdet_res = 0; + for (i = 0; i < ARRAY_SIZE(info->hpdet_res); i++) + info->hpdet_res[i] = 0; + info->mic = false; + info->hpdet_done = false; + info->hpdet_retried = false; + + snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask); + + /* + * If the jack was removed during a headphone detection we + * need to wait for the headphone detection to finish, as + * it can not be aborted. We don't want to be able to start + * a new headphone detection from a fresh insert until this + * one is finished. + */ + arizona_hpdet_wait(info); + + regmap_update_bits(arizona->regmap, + ARIZONA_JACK_DETECT_DEBOUNCE, + ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB, + ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB); + } + +out: + /* Clear trig_sts to make sure DCVDD is not forced up */ + regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG, + ARIZONA_MICD_CLAMP_FALL_TRIG_STS | + ARIZONA_MICD_CLAMP_RISE_TRIG_STS | + ARIZONA_JD1_FALL_TRIG_STS | + ARIZONA_JD1_RISE_TRIG_STS); + + mutex_unlock(&info->lock); + + pm_runtime_mark_last_busy(arizona->dev); + pm_runtime_put_autosuspend(arizona->dev); + + return IRQ_HANDLED; +} + +/* Map a level onto a slot in the register bank */ +static void arizona_micd_set_level(struct arizona *arizona, int index, + unsigned int level) +{ + int reg; + unsigned int mask; + + reg = ARIZONA_MIC_DETECT_LEVEL_4 - (index / 2); + + if (!(index % 2)) { + mask = 0x3f00; + level <<= 8; + } else { + mask = 0x3f; + } + + /* Program the level itself */ + regmap_update_bits(arizona->regmap, reg, mask, level); +} + +static int arizona_extcon_get_micd_configs(struct device *dev, + struct arizona *arizona) +{ + const char * const prop = "wlf,micd-configs"; + const int entries_per_config = 3; + struct arizona_micd_config *micd_configs; + int nconfs, ret; + int i, j; + u32 *vals; + + nconfs = device_property_count_u32(arizona->dev, prop); + if (nconfs <= 0) + return 0; + + vals = kcalloc(nconfs, sizeof(u32), GFP_KERNEL); + if (!vals) + return -ENOMEM; + + ret = device_property_read_u32_array(arizona->dev, prop, vals, nconfs); + if (ret < 0) + goto out; + + nconfs /= entries_per_config; + micd_configs = devm_kcalloc(dev, nconfs, sizeof(*micd_configs), + GFP_KERNEL); + if (!micd_configs) { + ret = -ENOMEM; + goto out; + } + + for (i = 0, j = 0; i < nconfs; ++i) { + micd_configs[i].src = vals[j++] ? ARIZONA_ACCDET_SRC : 0; + micd_configs[i].bias = vals[j++]; + micd_configs[i].gpio = vals[j++]; + } + + arizona->pdata.micd_configs = micd_configs; + arizona->pdata.num_micd_configs = nconfs; + +out: + kfree(vals); + return ret; +} + +static int arizona_extcon_device_get_pdata(struct device *dev, + struct arizona *arizona) +{ + struct arizona_pdata *pdata = &arizona->pdata; + unsigned int val = ARIZONA_ACCDET_MODE_HPL; + int ret; + + device_property_read_u32(arizona->dev, "wlf,hpdet-channel", &val); + switch (val) { + case ARIZONA_ACCDET_MODE_HPL: + case ARIZONA_ACCDET_MODE_HPR: + pdata->hpdet_channel = val; + break; + default: + dev_err(arizona->dev, "Wrong wlf,hpdet-channel DT value %d\n", val); + pdata->hpdet_channel = ARIZONA_ACCDET_MODE_HPL; + } + + device_property_read_u32(arizona->dev, "wlf,micd-detect-debounce", + &pdata->micd_detect_debounce); + + device_property_read_u32(arizona->dev, "wlf,micd-bias-start-time", + &pdata->micd_bias_start_time); + + device_property_read_u32(arizona->dev, "wlf,micd-rate", + &pdata->micd_rate); + + device_property_read_u32(arizona->dev, "wlf,micd-dbtime", + &pdata->micd_dbtime); + + device_property_read_u32(arizona->dev, "wlf,micd-timeout-ms", + &pdata->micd_timeout); + + pdata->micd_force_micbias = device_property_read_bool(arizona->dev, + "wlf,micd-force-micbias"); + + pdata->micd_software_compare = device_property_read_bool(arizona->dev, + "wlf,micd-software-compare"); + + pdata->jd_invert = device_property_read_bool(arizona->dev, + "wlf,jd-invert"); + + device_property_read_u32(arizona->dev, "wlf,gpsw", &pdata->gpsw); + + pdata->jd_gpio5 = device_property_read_bool(arizona->dev, + "wlf,use-jd2"); + pdata->jd_gpio5_nopull = device_property_read_bool(arizona->dev, + "wlf,use-jd2-nopull"); + + ret = arizona_extcon_get_micd_configs(dev, arizona); + if (ret < 0) + dev_err(arizona->dev, "Failed to read micd configs: %d\n", ret); + + return 0; +} + +int arizona_jack_codec_dev_probe(struct arizona_priv *info, struct device *dev) +{ + struct arizona *arizona = info->arizona; + struct arizona_pdata *pdata = &arizona->pdata; + int ret, mode; + + if (!dev_get_platdata(arizona->dev)) + arizona_extcon_device_get_pdata(dev, arizona); + + info->micvdd = devm_regulator_get(dev, "MICVDD"); + if (IS_ERR(info->micvdd)) + return dev_err_probe(arizona->dev, PTR_ERR(info->micvdd), "getting MICVDD\n"); + + mutex_init(&info->lock); + info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS); + INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work); + INIT_DELAYED_WORK(&info->micd_detect_work, arizona_micd_detect); + INIT_DELAYED_WORK(&info->micd_timeout_work, arizona_micd_timeout_work); + + switch (arizona->type) { + case WM5102: + switch (arizona->rev) { + case 0: + info->micd_reva = true; + break; + default: + info->micd_clamp = true; + info->hpdet_ip_version = 1; + break; + } + break; + case WM5110: + case WM8280: + switch (arizona->rev) { + case 0 ... 2: + break; + default: + info->micd_clamp = true; + info->hpdet_ip_version = 2; + break; + } + break; + case WM8998: + case WM1814: + info->micd_clamp = true; + info->hpdet_ip_version = 2; + break; + default: + break; + } + + if (!pdata->micd_timeout) + pdata->micd_timeout = DEFAULT_MICD_TIMEOUT; + + if (pdata->num_micd_configs) { + info->micd_modes = pdata->micd_configs; + info->micd_num_modes = pdata->num_micd_configs; + } else { + info->micd_modes = micd_default_modes; + info->micd_num_modes = ARRAY_SIZE(micd_default_modes); + } + + if (arizona->pdata.gpsw > 0) + regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1, + ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw); + + if (pdata->micd_pol_gpio > 0) { + if (info->micd_modes[0].gpio) + mode = GPIOF_OUT_INIT_HIGH; + else + mode = GPIOF_OUT_INIT_LOW; + + ret = devm_gpio_request_one(dev, pdata->micd_pol_gpio, + mode, "MICD polarity"); + if (ret != 0) { + dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", + pdata->micd_pol_gpio, ret); + return ret; + } + + info->micd_pol_gpio = gpio_to_desc(pdata->micd_pol_gpio); + } else { + if (info->micd_modes[0].gpio) + mode = GPIOD_OUT_HIGH; + else + mode = GPIOD_OUT_LOW; + + /* We can't use devm here because we need to do the get + * against the MFD device, as that is where the of_node + * will reside, but if we devm against that the GPIO + * will not be freed if the extcon driver is unloaded. + */ + info->micd_pol_gpio = gpiod_get_optional(arizona->dev, + "wlf,micd-pol", + mode); + if (IS_ERR(info->micd_pol_gpio)) { + ret = PTR_ERR(info->micd_pol_gpio); + dev_err_probe(arizona->dev, ret, "getting microphone polarity GPIO\n"); + return ret; + } + } + + if (arizona->pdata.hpdet_id_gpio > 0) { + ret = devm_gpio_request_one(dev, arizona->pdata.hpdet_id_gpio, + GPIOF_OUT_INIT_LOW, + "HPDET"); + if (ret != 0) { + dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", + arizona->pdata.hpdet_id_gpio, ret); + gpiod_put(info->micd_pol_gpio); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_probe); + +int arizona_jack_codec_dev_remove(struct arizona_priv *info) +{ + gpiod_put(info->micd_pol_gpio); + return 0; +} +EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_remove); + +static int arizona_jack_enable_jack_detect(struct arizona_priv *info, + struct snd_soc_jack *jack) +{ + struct arizona *arizona = info->arizona; + struct arizona_pdata *pdata = &arizona->pdata; + unsigned int val; + unsigned int clamp_mode; + int jack_irq_fall, jack_irq_rise; + int ret, i, j; + + if (arizona->pdata.micd_bias_start_time) + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_BIAS_STARTTIME_MASK, + arizona->pdata.micd_bias_start_time + << ARIZONA_MICD_BIAS_STARTTIME_SHIFT); + + if (arizona->pdata.micd_rate) + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_RATE_MASK, + arizona->pdata.micd_rate + << ARIZONA_MICD_RATE_SHIFT); + + switch (arizona->pdata.micd_dbtime) { + case MICD_DBTIME_FOUR_READINGS: + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_DBTIME_MASK, + ARIZONA_MICD_DBTIME); + break; + case MICD_DBTIME_TWO_READINGS: + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_DBTIME_MASK, 0); + break; + default: + break; + } + + BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) < + ARIZONA_NUM_MICD_BUTTON_LEVELS); + + if (arizona->pdata.num_micd_ranges) { + info->micd_ranges = pdata->micd_ranges; + info->num_micd_ranges = pdata->num_micd_ranges; + } else { + info->micd_ranges = micd_default_ranges; + info->num_micd_ranges = ARRAY_SIZE(micd_default_ranges); + } + + if (arizona->pdata.num_micd_ranges > ARIZONA_MAX_MICD_BUTTONS) { + dev_err(arizona->dev, "Too many MICD ranges: %d > %d\n", + arizona->pdata.num_micd_ranges, ARIZONA_MAX_MICD_BUTTONS); + return -EINVAL; + } + + if (info->num_micd_ranges > 1) { + for (i = 1; i < info->num_micd_ranges; i++) { + if (info->micd_ranges[i - 1].max > + info->micd_ranges[i].max) { + dev_err(arizona->dev, "MICD ranges must be sorted\n"); + return -EINVAL; + } + } + } + + /* Disable all buttons by default */ + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2, + ARIZONA_MICD_LVL_SEL_MASK, 0x81); + + /* Set up all the buttons the user specified */ + for (i = 0; i < info->num_micd_ranges; i++) { + for (j = 0; j < ARIZONA_NUM_MICD_BUTTON_LEVELS; j++) + if (arizona_micd_levels[j] >= info->micd_ranges[i].max) + break; + + if (j == ARIZONA_NUM_MICD_BUTTON_LEVELS) { + dev_err(arizona->dev, "Unsupported MICD level %d\n", + info->micd_ranges[i].max); + return -EINVAL; + } + + dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n", + arizona_micd_levels[j], i); + + arizona_micd_set_level(arizona, i, j); + + /* SND_JACK_BTN_# masks start with the most significant bit */ + info->micd_button_mask |= SND_JACK_BTN_0 >> i; + snd_jack_set_key(jack->jack, SND_JACK_BTN_0 >> i, + info->micd_ranges[i].key); + + /* Enable reporting of that range */ + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2, + 1 << i, 1 << i); + } + + /* Set all the remaining keys to a maximum */ + for (; i < ARIZONA_MAX_MICD_RANGE; i++) + arizona_micd_set_level(arizona, i, 0x3f); + + /* + * If we have a clamp use it, activating in conjunction with + * GPIO5 if that is connected for jack detect operation. + */ + if (info->micd_clamp) { + if (arizona->pdata.jd_gpio5) { + /* Put the GPIO into input mode with optional pull */ + val = 0xc101; + if (arizona->pdata.jd_gpio5_nopull) + val &= ~ARIZONA_GPN_PU; + + regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL, + val); + + if (arizona->pdata.jd_invert) + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH_GP5H; + else + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL_GP5H; + } else { + if (arizona->pdata.jd_invert) + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH; + else + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL; + } + + regmap_update_bits(arizona->regmap, + ARIZONA_MICD_CLAMP_CONTROL, + ARIZONA_MICD_CLAMP_MODE_MASK, clamp_mode); + + regmap_update_bits(arizona->regmap, + ARIZONA_JACK_DETECT_DEBOUNCE, + ARIZONA_MICD_CLAMP_DB, + ARIZONA_MICD_CLAMP_DB); + } + + arizona_extcon_set_mode(info, 0); + + info->jack = jack; + + pm_runtime_get_sync(arizona->dev); + + if (info->micd_clamp) { + jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE; + jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL; + } else { + jack_irq_rise = ARIZONA_IRQ_JD_RISE; + jack_irq_fall = ARIZONA_IRQ_JD_FALL; + } + + ret = arizona_request_irq(arizona, jack_irq_rise, + "JACKDET rise", arizona_jackdet, info); + if (ret != 0) { + dev_err(arizona->dev, "Failed to get JACKDET rise IRQ: %d\n", ret); + goto err_pm; + } + + ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set JD rise IRQ wake: %d\n", ret); + goto err_rise; + } + + ret = arizona_request_irq(arizona, jack_irq_fall, + "JACKDET fall", arizona_jackdet, info); + if (ret != 0) { + dev_err(arizona->dev, "Failed to get JD fall IRQ: %d\n", ret); + goto err_rise_wake; + } + + ret = arizona_set_irq_wake(arizona, jack_irq_fall, 1); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set JD fall IRQ wake: %d\n", ret); + goto err_fall; + } + + ret = arizona_request_irq(arizona, ARIZONA_IRQ_MICDET, + "MICDET", arizona_micdet, info); + if (ret != 0) { + dev_err(arizona->dev, "Failed to get MICDET IRQ: %d\n", ret); + goto err_fall_wake; + } + + ret = arizona_request_irq(arizona, ARIZONA_IRQ_HPDET, + "HPDET", arizona_hpdet_irq, info); + if (ret != 0) { + dev_err(arizona->dev, "Failed to get HPDET IRQ: %d\n", ret); + goto err_micdet; + } + + arizona_clk32k_enable(arizona); + regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_DEBOUNCE, + ARIZONA_JD1_DB, ARIZONA_JD1_DB); + regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, + ARIZONA_JD1_ENA, ARIZONA_JD1_ENA); + + ret = regulator_allow_bypass(info->micvdd, true); + if (ret != 0) + dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n", ret); + + pm_runtime_put(arizona->dev); + + return 0; + +err_micdet: + arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info); +err_fall_wake: + arizona_set_irq_wake(arizona, jack_irq_fall, 0); +err_fall: + arizona_free_irq(arizona, jack_irq_fall, info); +err_rise_wake: + arizona_set_irq_wake(arizona, jack_irq_rise, 0); +err_rise: + arizona_free_irq(arizona, jack_irq_rise, info); +err_pm: + pm_runtime_put(arizona->dev); + info->jack = NULL; + return ret; +} + +static int arizona_jack_disable_jack_detect(struct arizona_priv *info) +{ + struct arizona *arizona = info->arizona; + int jack_irq_rise, jack_irq_fall; + bool change; + int ret; + + if (!info->jack) + return 0; + + if (info->micd_clamp) { + jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE; + jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL; + } else { + jack_irq_rise = ARIZONA_IRQ_JD_RISE; + jack_irq_fall = ARIZONA_IRQ_JD_FALL; + } + + arizona_set_irq_wake(arizona, jack_irq_rise, 0); + arizona_set_irq_wake(arizona, jack_irq_fall, 0); + arizona_free_irq(arizona, ARIZONA_IRQ_HPDET, info); + arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info); + arizona_free_irq(arizona, jack_irq_rise, info); + arizona_free_irq(arizona, jack_irq_fall, info); + cancel_delayed_work_sync(&info->hpdet_work); + cancel_delayed_work_sync(&info->micd_detect_work); + cancel_delayed_work_sync(&info->micd_timeout_work); + + ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_ENA, 0, + &change); + if (ret < 0) { + dev_err(arizona->dev, "Failed to disable micd on remove: %d\n", ret); + } else if (change) { + regulator_disable(info->micvdd); + pm_runtime_put(arizona->dev); + } + + regmap_update_bits(arizona->regmap, + ARIZONA_MICD_CLAMP_CONTROL, + ARIZONA_MICD_CLAMP_MODE_MASK, 0); + regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, + ARIZONA_JD1_ENA, 0); + arizona_clk32k_disable(arizona); + info->jack = NULL; + + return 0; +} + +int arizona_jack_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + struct arizona_priv *info = snd_soc_component_get_drvdata(component); + + if (jack) + return arizona_jack_enable_jack_detect(info, jack); + else + return arizona_jack_disable_jack_detect(info); +} +EXPORT_SYMBOL_GPL(arizona_jack_set_jack); diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c new file mode 100644 index 0000000000..7434aeeda2 --- /dev/null +++ b/sound/soc/codecs/arizona.c @@ -0,0 +1,2861 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * arizona.c - Wolfson Arizona class device shared support + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "arizona.h" + +#define ARIZONA_AIF_BCLK_CTRL 0x00 +#define ARIZONA_AIF_TX_PIN_CTRL 0x01 +#define ARIZONA_AIF_RX_PIN_CTRL 0x02 +#define ARIZONA_AIF_RATE_CTRL 0x03 +#define ARIZONA_AIF_FORMAT 0x04 +#define ARIZONA_AIF_TX_BCLK_RATE 0x05 +#define ARIZONA_AIF_RX_BCLK_RATE 0x06 +#define ARIZONA_AIF_FRAME_CTRL_1 0x07 +#define ARIZONA_AIF_FRAME_CTRL_2 0x08 +#define ARIZONA_AIF_FRAME_CTRL_3 0x09 +#define ARIZONA_AIF_FRAME_CTRL_4 0x0A +#define ARIZONA_AIF_FRAME_CTRL_5 0x0B +#define ARIZONA_AIF_FRAME_CTRL_6 0x0C +#define ARIZONA_AIF_FRAME_CTRL_7 0x0D +#define ARIZONA_AIF_FRAME_CTRL_8 0x0E +#define ARIZONA_AIF_FRAME_CTRL_9 0x0F +#define ARIZONA_AIF_FRAME_CTRL_10 0x10 +#define ARIZONA_AIF_FRAME_CTRL_11 0x11 +#define ARIZONA_AIF_FRAME_CTRL_12 0x12 +#define ARIZONA_AIF_FRAME_CTRL_13 0x13 +#define ARIZONA_AIF_FRAME_CTRL_14 0x14 +#define ARIZONA_AIF_FRAME_CTRL_15 0x15 +#define ARIZONA_AIF_FRAME_CTRL_16 0x16 +#define ARIZONA_AIF_FRAME_CTRL_17 0x17 +#define ARIZONA_AIF_FRAME_CTRL_18 0x18 +#define ARIZONA_AIF_TX_ENABLES 0x19 +#define ARIZONA_AIF_RX_ENABLES 0x1A +#define ARIZONA_AIF_FORCE_WRITE 0x1B + +#define ARIZONA_FLL_VCO_CORNER 141900000 +#define ARIZONA_FLL_MAX_FREF 13500000 +#define ARIZONA_FLL_MIN_FVCO 90000000 +#define ARIZONA_FLL_MAX_FRATIO 16 +#define ARIZONA_FLL_MAX_REFDIV 8 +#define ARIZONA_FLL_MIN_OUTDIV 2 +#define ARIZONA_FLL_MAX_OUTDIV 7 + +#define ARIZONA_FMT_DSP_MODE_A 0 +#define ARIZONA_FMT_DSP_MODE_B 1 +#define ARIZONA_FMT_I2S_MODE 2 +#define ARIZONA_FMT_LEFT_JUSTIFIED_MODE 3 + +#define arizona_fll_err(_fll, fmt, ...) \ + dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) +#define arizona_fll_warn(_fll, fmt, ...) \ + dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) +#define arizona_fll_dbg(_fll, fmt, ...) \ + dev_dbg(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) + +#define arizona_aif_err(_dai, fmt, ...) \ + dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) +#define arizona_aif_warn(_dai, fmt, ...) \ + dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) +#define arizona_aif_dbg(_dai, fmt, ...) \ + dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) + +static int arizona_spk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + int val; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read(component, + ARIZONA_INTERRUPT_RAW_STATUS_3); + if (val & ARIZONA_SPK_OVERHEAT_STS) { + dev_crit(arizona->dev, + "Speaker not enabled due to temperature\n"); + return -EBUSY; + } + + regmap_update_bits_async(arizona->regmap, + ARIZONA_OUTPUT_ENABLES_1, + 1 << w->shift, 1 << w->shift); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits_async(arizona->regmap, + ARIZONA_OUTPUT_ENABLES_1, + 1 << w->shift, 0); + break; + default: + break; + } + + return arizona_out_ev(w, kcontrol, event); +} + +static irqreturn_t arizona_thermal_warn(int irq, void *data) +{ + struct arizona *arizona = data; + unsigned int val; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_3, + &val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read thermal status: %d\n", + ret); + } else if (val & ARIZONA_SPK_OVERHEAT_WARN_STS) { + dev_crit(arizona->dev, "Thermal warning\n"); + } + + return IRQ_HANDLED; +} + +static irqreturn_t arizona_thermal_shutdown(int irq, void *data) +{ + struct arizona *arizona = data; + unsigned int val; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_INTERRUPT_RAW_STATUS_3, + &val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to read thermal status: %d\n", + ret); + } else if (val & ARIZONA_SPK_OVERHEAT_STS) { + dev_crit(arizona->dev, "Thermal shutdown\n"); + ret = regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT4L_ENA | + ARIZONA_OUT4R_ENA, 0); + if (ret != 0) + dev_crit(arizona->dev, + "Failed to disable speaker outputs: %d\n", + ret); + } + + return IRQ_HANDLED; +} + +static const struct snd_soc_dapm_widget arizona_spkl = + SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM, + ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD); + +static const struct snd_soc_dapm_widget arizona_spkr = + SND_SOC_DAPM_PGA_E("OUT4R", SND_SOC_NOPM, + ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_spk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD); + +int arizona_init_spk(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + int ret; + + ret = snd_soc_dapm_new_controls(dapm, &arizona_spkl, 1); + if (ret != 0) + return ret; + + switch (arizona->type) { + case WM8997: + case CS47L24: + case WM1831: + break; + default: + ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1); + if (ret != 0) + return ret; + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_spk); + +int arizona_init_spk_irqs(struct arizona *arizona) +{ + int ret; + + ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, + "Thermal warning", arizona_thermal_warn, + arizona); + if (ret != 0) + dev_err(arizona->dev, + "Failed to get thermal warning IRQ: %d\n", + ret); + + ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, + "Thermal shutdown", arizona_thermal_shutdown, + arizona); + if (ret != 0) + dev_err(arizona->dev, + "Failed to get thermal shutdown IRQ: %d\n", + ret); + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_spk_irqs); + +int arizona_free_spk_irqs(struct arizona *arizona) +{ + arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, arizona); + arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, arizona); + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_free_spk_irqs); + +static const struct snd_soc_dapm_route arizona_mono_routes[] = { + { "OUT1R", NULL, "OUT1L" }, + { "OUT2R", NULL, "OUT2L" }, + { "OUT3R", NULL, "OUT3L" }, + { "OUT4R", NULL, "OUT4L" }, + { "OUT5R", NULL, "OUT5L" }, + { "OUT6R", NULL, "OUT6L" }, +}; + +int arizona_init_mono(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + int i; + + for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) { + if (arizona->pdata.out_mono[i]) + snd_soc_dapm_add_routes(dapm, + &arizona_mono_routes[i], 1); + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_mono); + +int arizona_init_gpio(struct snd_soc_component *component) +{ + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + int i; + + switch (arizona->type) { + case WM5110: + case WM8280: + snd_soc_component_disable_pin(component, + "DRC2 Signal Activity"); + break; + default: + break; + } + + snd_soc_component_disable_pin(component, "DRC1 Signal Activity"); + + for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { + switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) { + case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT: + snd_soc_component_enable_pin(component, + "DRC1 Signal Activity"); + break; + case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT: + snd_soc_component_enable_pin(component, + "DRC2 Signal Activity"); + break; + default: + break; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_gpio); + +int arizona_init_common(struct arizona *arizona) +{ + struct arizona_pdata *pdata = &arizona->pdata; + unsigned int val, mask; + int i; + + BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier); + + for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) { + /* Default is 0 so noop with defaults */ + if (pdata->out_mono[i]) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8), + ARIZONA_OUT1_MONO, val); + } + + for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { + if (pdata->spk_mute[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_1 + (i * 2), + ARIZONA_SPK1_MUTE_ENDIAN_MASK | + ARIZONA_SPK1_MUTE_SEQ1_MASK, + pdata->spk_mute[i]); + + if (pdata->spk_fmt[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_2 + (i * 2), + ARIZONA_SPK1_FMT_MASK, + pdata->spk_fmt[i]); + } + + for (i = 0; i < ARIZONA_MAX_INPUT; i++) { + /* Default for both is 0 so noop with defaults */ + val = pdata->dmic_ref[i] << ARIZONA_IN1_DMIC_SUP_SHIFT; + if (pdata->inmode[i] & ARIZONA_INMODE_DMIC) + val |= 1 << ARIZONA_IN1_MODE_SHIFT; + + switch (arizona->type) { + case WM8998: + case WM1814: + regmap_update_bits(arizona->regmap, + ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 8), + ARIZONA_IN1L_SRC_SE_MASK, + (pdata->inmode[i] & ARIZONA_INMODE_SE) + << ARIZONA_IN1L_SRC_SE_SHIFT); + + regmap_update_bits(arizona->regmap, + ARIZONA_ADC_DIGITAL_VOLUME_1R + (i * 8), + ARIZONA_IN1R_SRC_SE_MASK, + (pdata->inmode[i] & ARIZONA_INMODE_SE) + << ARIZONA_IN1R_SRC_SE_SHIFT); + + mask = ARIZONA_IN1_DMIC_SUP_MASK | + ARIZONA_IN1_MODE_MASK; + break; + default: + if (pdata->inmode[i] & ARIZONA_INMODE_SE) + val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT; + + mask = ARIZONA_IN1_DMIC_SUP_MASK | + ARIZONA_IN1_MODE_MASK | + ARIZONA_IN1_SINGLE_ENDED_MASK; + break; + } + + regmap_update_bits(arizona->regmap, + ARIZONA_IN1L_CONTROL + (i * 8), + mask, val); + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_common); + +int arizona_init_vol_limit(struct arizona *arizona) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(arizona->pdata.out_vol_limit); ++i) { + if (arizona->pdata.out_vol_limit[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_DAC_VOLUME_LIMIT_1L + i * 4, + ARIZONA_OUT1L_VOL_LIM_MASK, + arizona->pdata.out_vol_limit[i]); + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_vol_limit); + +const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { + "None", + "Tone Generator 1", + "Tone Generator 2", + "Haptics", + "AEC", + "AEC2", + "Mic Mute Mixer", + "Noise Generator", + "IN1L", + "IN1R", + "IN2L", + "IN2R", + "IN3L", + "IN3R", + "IN4L", + "IN4R", + "AIF1RX1", + "AIF1RX2", + "AIF1RX3", + "AIF1RX4", + "AIF1RX5", + "AIF1RX6", + "AIF1RX7", + "AIF1RX8", + "AIF2RX1", + "AIF2RX2", + "AIF2RX3", + "AIF2RX4", + "AIF2RX5", + "AIF2RX6", + "AIF3RX1", + "AIF3RX2", + "SLIMRX1", + "SLIMRX2", + "SLIMRX3", + "SLIMRX4", + "SLIMRX5", + "SLIMRX6", + "SLIMRX7", + "SLIMRX8", + "EQ1", + "EQ2", + "EQ3", + "EQ4", + "DRC1L", + "DRC1R", + "DRC2L", + "DRC2R", + "LHPF1", + "LHPF2", + "LHPF3", + "LHPF4", + "DSP1.1", + "DSP1.2", + "DSP1.3", + "DSP1.4", + "DSP1.5", + "DSP1.6", + "DSP2.1", + "DSP2.2", + "DSP2.3", + "DSP2.4", + "DSP2.5", + "DSP2.6", + "DSP3.1", + "DSP3.2", + "DSP3.3", + "DSP3.4", + "DSP3.5", + "DSP3.6", + "DSP4.1", + "DSP4.2", + "DSP4.3", + "DSP4.4", + "DSP4.5", + "DSP4.6", + "ASRC1L", + "ASRC1R", + "ASRC2L", + "ASRC2R", + "ISRC1INT1", + "ISRC1INT2", + "ISRC1INT3", + "ISRC1INT4", + "ISRC1DEC1", + "ISRC1DEC2", + "ISRC1DEC3", + "ISRC1DEC4", + "ISRC2INT1", + "ISRC2INT2", + "ISRC2INT3", + "ISRC2INT4", + "ISRC2DEC1", + "ISRC2DEC2", + "ISRC2DEC3", + "ISRC2DEC4", + "ISRC3INT1", + "ISRC3INT2", + "ISRC3INT3", + "ISRC3INT4", + "ISRC3DEC1", + "ISRC3DEC2", + "ISRC3DEC3", + "ISRC3DEC4", +}; +EXPORT_SYMBOL_GPL(arizona_mixer_texts); + +unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { + 0x00, /* None */ + 0x04, /* Tone */ + 0x05, + 0x06, /* Haptics */ + 0x08, /* AEC */ + 0x09, /* AEC2 */ + 0x0c, /* Noise mixer */ + 0x0d, /* Comfort noise */ + 0x10, /* IN1L */ + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x20, /* AIF1RX1 */ + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, + 0x26, + 0x27, + 0x28, /* AIF2RX1 */ + 0x29, + 0x2a, + 0x2b, + 0x2c, + 0x2d, + 0x30, /* AIF3RX1 */ + 0x31, + 0x38, /* SLIMRX1 */ + 0x39, + 0x3a, + 0x3b, + 0x3c, + 0x3d, + 0x3e, + 0x3f, + 0x50, /* EQ1 */ + 0x51, + 0x52, + 0x53, + 0x58, /* DRC1L */ + 0x59, + 0x5a, + 0x5b, + 0x60, /* LHPF1 */ + 0x61, + 0x62, + 0x63, + 0x68, /* DSP1.1 */ + 0x69, + 0x6a, + 0x6b, + 0x6c, + 0x6d, + 0x70, /* DSP2.1 */ + 0x71, + 0x72, + 0x73, + 0x74, + 0x75, + 0x78, /* DSP3.1 */ + 0x79, + 0x7a, + 0x7b, + 0x7c, + 0x7d, + 0x80, /* DSP4.1 */ + 0x81, + 0x82, + 0x83, + 0x84, + 0x85, + 0x90, /* ASRC1L */ + 0x91, + 0x92, + 0x93, + 0xa0, /* ISRC1INT1 */ + 0xa1, + 0xa2, + 0xa3, + 0xa4, /* ISRC1DEC1 */ + 0xa5, + 0xa6, + 0xa7, + 0xa8, /* ISRC2DEC1 */ + 0xa9, + 0xaa, + 0xab, + 0xac, /* ISRC2INT1 */ + 0xad, + 0xae, + 0xaf, + 0xb0, /* ISRC3DEC1 */ + 0xb1, + 0xb2, + 0xb3, + 0xb4, /* ISRC3INT1 */ + 0xb5, + 0xb6, + 0xb7, +}; +EXPORT_SYMBOL_GPL(arizona_mixer_values); + +const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0); +EXPORT_SYMBOL_GPL(arizona_mixer_tlv); + +const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = { + "12kHz", "24kHz", "48kHz", "96kHz", "192kHz", + "11.025kHz", "22.05kHz", "44.1kHz", "88.2kHz", "176.4kHz", + "4kHz", "8kHz", "16kHz", "32kHz", +}; +EXPORT_SYMBOL_GPL(arizona_sample_rate_text); + +const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x10, 0x11, 0x12, 0x13, +}; +EXPORT_SYMBOL_GPL(arizona_sample_rate_val); + +const char *arizona_sample_rate_val_to_name(unsigned int rate_val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(arizona_sample_rate_val); ++i) { + if (arizona_sample_rate_val[i] == rate_val) + return arizona_sample_rate_text[i]; + } + + return "Illegal"; +} +EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name); + +const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = { + "SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate", +}; +EXPORT_SYMBOL_GPL(arizona_rate_text); + +const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = { + 0, 1, 2, 8, +}; +EXPORT_SYMBOL_GPL(arizona_rate_val); + +const struct soc_enum arizona_isrc_fsh[] = { + SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_1, + ARIZONA_ISRC1_FSH_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_2_CTRL_1, + ARIZONA_ISRC2_FSH_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_3_CTRL_1, + ARIZONA_ISRC3_FSH_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), +}; +EXPORT_SYMBOL_GPL(arizona_isrc_fsh); + +const struct soc_enum arizona_isrc_fsl[] = { + SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_1_CTRL_2, + ARIZONA_ISRC1_FSL_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_2_CTRL_2, + ARIZONA_ISRC2_FSL_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_ISRC_3_CTRL_2, + ARIZONA_ISRC3_FSL_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), +}; +EXPORT_SYMBOL_GPL(arizona_isrc_fsl); + +const struct soc_enum arizona_asrc_rate1 = + SOC_VALUE_ENUM_SINGLE(ARIZONA_ASRC_RATE1, + ARIZONA_ASRC_RATE1_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE - 1, + arizona_rate_text, arizona_rate_val); +EXPORT_SYMBOL_GPL(arizona_asrc_rate1); + +static const char * const arizona_vol_ramp_text[] = { + "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", + "15ms/6dB", "30ms/6dB", +}; + +SOC_ENUM_SINGLE_DECL(arizona_in_vd_ramp, + ARIZONA_INPUT_VOLUME_RAMP, + ARIZONA_IN_VD_RAMP_SHIFT, + arizona_vol_ramp_text); +EXPORT_SYMBOL_GPL(arizona_in_vd_ramp); + +SOC_ENUM_SINGLE_DECL(arizona_in_vi_ramp, + ARIZONA_INPUT_VOLUME_RAMP, + ARIZONA_IN_VI_RAMP_SHIFT, + arizona_vol_ramp_text); +EXPORT_SYMBOL_GPL(arizona_in_vi_ramp); + +SOC_ENUM_SINGLE_DECL(arizona_out_vd_ramp, + ARIZONA_OUTPUT_VOLUME_RAMP, + ARIZONA_OUT_VD_RAMP_SHIFT, + arizona_vol_ramp_text); +EXPORT_SYMBOL_GPL(arizona_out_vd_ramp); + +SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp, + ARIZONA_OUTPUT_VOLUME_RAMP, + ARIZONA_OUT_VI_RAMP_SHIFT, + arizona_vol_ramp_text); +EXPORT_SYMBOL_GPL(arizona_out_vi_ramp); + +static const char * const arizona_lhpf_mode_text[] = { + "Low-pass", "High-pass" +}; + +SOC_ENUM_SINGLE_DECL(arizona_lhpf1_mode, + ARIZONA_HPLPF1_1, + ARIZONA_LHPF1_MODE_SHIFT, + arizona_lhpf_mode_text); +EXPORT_SYMBOL_GPL(arizona_lhpf1_mode); + +SOC_ENUM_SINGLE_DECL(arizona_lhpf2_mode, + ARIZONA_HPLPF2_1, + ARIZONA_LHPF2_MODE_SHIFT, + arizona_lhpf_mode_text); +EXPORT_SYMBOL_GPL(arizona_lhpf2_mode); + +SOC_ENUM_SINGLE_DECL(arizona_lhpf3_mode, + ARIZONA_HPLPF3_1, + ARIZONA_LHPF3_MODE_SHIFT, + arizona_lhpf_mode_text); +EXPORT_SYMBOL_GPL(arizona_lhpf3_mode); + +SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode, + ARIZONA_HPLPF4_1, + ARIZONA_LHPF4_MODE_SHIFT, + arizona_lhpf_mode_text); +EXPORT_SYMBOL_GPL(arizona_lhpf4_mode); + +static const char * const arizona_ng_hold_text[] = { + "30ms", "120ms", "250ms", "500ms", +}; + +SOC_ENUM_SINGLE_DECL(arizona_ng_hold, + ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_HOLD_SHIFT, + arizona_ng_hold_text); +EXPORT_SYMBOL_GPL(arizona_ng_hold); + +static const char * const arizona_in_hpf_cut_text[] = { + "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz" +}; + +SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum, + ARIZONA_HPF_CONTROL, + ARIZONA_IN_HPF_CUT_SHIFT, + arizona_in_hpf_cut_text); +EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum); + +static const char * const arizona_in_dmic_osr_text[] = { + "1.536MHz", "3.072MHz", "6.144MHz", "768kHz", +}; + +const struct soc_enum arizona_in_dmic_osr[] = { + SOC_ENUM_SINGLE(ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), + SOC_ENUM_SINGLE(ARIZONA_IN2L_CONTROL, ARIZONA_IN2_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), + SOC_ENUM_SINGLE(ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), + SOC_ENUM_SINGLE(ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), +}; +EXPORT_SYMBOL_GPL(arizona_in_dmic_osr); + +static const char * const arizona_anc_input_src_text[] = { + "None", "IN1", "IN2", "IN3", "IN4", +}; + +static const char * const arizona_anc_channel_src_text[] = { + "None", "Left", "Right", "Combine", +}; + +const struct soc_enum arizona_anc_input_src[] = { + SOC_ENUM_SINGLE(ARIZONA_ANC_SRC, + ARIZONA_IN_RXANCL_SEL_SHIFT, + ARRAY_SIZE(arizona_anc_input_src_text), + arizona_anc_input_src_text), + SOC_ENUM_SINGLE(ARIZONA_FCL_ADC_REFORMATTER_CONTROL, + ARIZONA_FCL_MIC_MODE_SEL_SHIFT, + ARRAY_SIZE(arizona_anc_channel_src_text), + arizona_anc_channel_src_text), + SOC_ENUM_SINGLE(ARIZONA_ANC_SRC, + ARIZONA_IN_RXANCR_SEL_SHIFT, + ARRAY_SIZE(arizona_anc_input_src_text), + arizona_anc_input_src_text), + SOC_ENUM_SINGLE(ARIZONA_FCR_ADC_REFORMATTER_CONTROL, + ARIZONA_FCR_MIC_MODE_SEL_SHIFT, + ARRAY_SIZE(arizona_anc_channel_src_text), + arizona_anc_channel_src_text), +}; +EXPORT_SYMBOL_GPL(arizona_anc_input_src); + +static const char * const arizona_anc_ng_texts[] = { + "None", + "Internal", + "External", +}; + +SOC_ENUM_SINGLE_DECL(arizona_anc_ng_enum, SND_SOC_NOPM, 0, + arizona_anc_ng_texts); +EXPORT_SYMBOL_GPL(arizona_anc_ng_enum); + +static const char * const arizona_output_anc_src_text[] = { + "None", "RXANCL", "RXANCR", +}; + +const struct soc_enum arizona_output_anc_src[] = { + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L, + ARIZONA_OUT1L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1R, + ARIZONA_OUT1R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L, + ARIZONA_OUT2L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2R, + ARIZONA_OUT2R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L, + ARIZONA_OUT3L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_DAC_VOLUME_LIMIT_3R, + ARIZONA_OUT3R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4L, + ARIZONA_OUT4L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_4R, + ARIZONA_OUT4R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5L, + ARIZONA_OUT5L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_5R, + ARIZONA_OUT5R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6L, + ARIZONA_OUT6L_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), + SOC_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_6R, + ARIZONA_OUT6R_ANC_SRC_SHIFT, + ARRAY_SIZE(arizona_output_anc_src_text), + arizona_output_anc_src_text), +}; +EXPORT_SYMBOL_GPL(arizona_output_anc_src); + +const struct snd_kcontrol_new arizona_voice_trigger_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 1, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 2, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 3, 1, 0), +}; +EXPORT_SYMBOL_GPL(arizona_voice_trigger_switch); + +static void arizona_in_set_vu(struct snd_soc_component *component, int ena) +{ + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int val; + int i; + + if (ena) + val = ARIZONA_IN_VU; + else + val = 0; + + for (i = 0; i < priv->num_inputs; i++) + snd_soc_component_update_bits(component, + ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 4), + ARIZONA_IN_VU, val); +} + +bool arizona_input_analog(struct snd_soc_component *component, int shift) +{ + unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8); + unsigned int val = snd_soc_component_read(component, reg); + + return !(val & ARIZONA_IN1_MODE_MASK); +} +EXPORT_SYMBOL_GPL(arizona_input_analog); + +int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int reg; + + if (w->shift % 2) + reg = ARIZONA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8); + else + reg = ARIZONA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + priv->in_pending++; + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, reg, + ARIZONA_IN1L_MUTE, 0); + + /* If this is the last input pending then allow VU */ + priv->in_pending--; + if (priv->in_pending == 0) { + msleep(1); + arizona_in_set_vu(component, 1); + } + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, reg, + ARIZONA_IN1L_MUTE | ARIZONA_IN_VU, + ARIZONA_IN1L_MUTE | ARIZONA_IN_VU); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable volume updates if no inputs are enabled */ + reg = snd_soc_component_read(component, ARIZONA_INPUT_ENABLES); + if (reg == 0) + arizona_in_set_vu(component, 0); + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_in_ev); + +int arizona_out_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case ARIZONA_OUT1L_ENA_SHIFT: + case ARIZONA_OUT1R_ENA_SHIFT: + case ARIZONA_OUT2L_ENA_SHIFT: + case ARIZONA_OUT2R_ENA_SHIFT: + case ARIZONA_OUT3L_ENA_SHIFT: + case ARIZONA_OUT3R_ENA_SHIFT: + priv->out_up_pending++; + priv->out_up_delay += 17; + break; + case ARIZONA_OUT4L_ENA_SHIFT: + case ARIZONA_OUT4R_ENA_SHIFT: + priv->out_up_pending++; + switch (arizona->type) { + case WM5102: + case WM8997: + break; + default: + priv->out_up_delay += 10; + break; + } + break; + default: + break; + } + break; + case SND_SOC_DAPM_POST_PMU: + switch (w->shift) { + case ARIZONA_OUT1L_ENA_SHIFT: + case ARIZONA_OUT1R_ENA_SHIFT: + case ARIZONA_OUT2L_ENA_SHIFT: + case ARIZONA_OUT2R_ENA_SHIFT: + case ARIZONA_OUT3L_ENA_SHIFT: + case ARIZONA_OUT3R_ENA_SHIFT: + case ARIZONA_OUT4L_ENA_SHIFT: + case ARIZONA_OUT4R_ENA_SHIFT: + priv->out_up_pending--; + if (!priv->out_up_pending && priv->out_up_delay) { + dev_dbg(component->dev, "Power up delay: %d\n", + priv->out_up_delay); + msleep(priv->out_up_delay); + priv->out_up_delay = 0; + } + break; + + default: + break; + } + break; + case SND_SOC_DAPM_PRE_PMD: + switch (w->shift) { + case ARIZONA_OUT1L_ENA_SHIFT: + case ARIZONA_OUT1R_ENA_SHIFT: + case ARIZONA_OUT2L_ENA_SHIFT: + case ARIZONA_OUT2R_ENA_SHIFT: + case ARIZONA_OUT3L_ENA_SHIFT: + case ARIZONA_OUT3R_ENA_SHIFT: + priv->out_down_pending++; + priv->out_down_delay++; + break; + case ARIZONA_OUT4L_ENA_SHIFT: + case ARIZONA_OUT4R_ENA_SHIFT: + priv->out_down_pending++; + switch (arizona->type) { + case WM5102: + case WM8997: + break; + case WM8998: + case WM1814: + priv->out_down_delay += 5; + break; + default: + priv->out_down_delay++; + break; + } + break; + default: + break; + } + break; + case SND_SOC_DAPM_POST_PMD: + switch (w->shift) { + case ARIZONA_OUT1L_ENA_SHIFT: + case ARIZONA_OUT1R_ENA_SHIFT: + case ARIZONA_OUT2L_ENA_SHIFT: + case ARIZONA_OUT2R_ENA_SHIFT: + case ARIZONA_OUT3L_ENA_SHIFT: + case ARIZONA_OUT3R_ENA_SHIFT: + case ARIZONA_OUT4L_ENA_SHIFT: + case ARIZONA_OUT4R_ENA_SHIFT: + priv->out_down_pending--; + if (!priv->out_down_pending && priv->out_down_delay) { + dev_dbg(component->dev, "Power down delay: %d\n", + priv->out_down_delay); + msleep(priv->out_down_delay); + priv->out_down_delay = 0; + } + break; + default: + break; + } + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_out_ev); + +int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + unsigned int mask = 1 << w->shift; + unsigned int val; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = mask; + break; + case SND_SOC_DAPM_PRE_PMD: + val = 0; + break; + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + return arizona_out_ev(w, kcontrol, event); + default: + return -EINVAL; + } + + /* Store the desired state for the HP outputs */ + priv->arizona->hp_ena &= ~mask; + priv->arizona->hp_ena |= val; + + /* Force off if HPDET clamp is active */ + if (priv->arizona->hpdet_clamp) + val = 0; + + regmap_update_bits_async(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, + mask, val); + + return arizona_out_ev(w, kcontrol, event); +} +EXPORT_SYMBOL_GPL(arizona_hp_ev); + +static int arizona_dvfs_enable(struct snd_soc_component *component) +{ + const struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + int ret; + + ret = regulator_set_voltage(arizona->dcvdd, 1800000, 1800000); + if (ret) { + dev_err(component->dev, "Failed to boost DCVDD: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, + ARIZONA_SUBSYS_MAX_FREQ, + ARIZONA_SUBSYS_MAX_FREQ); + if (ret) { + dev_err(component->dev, "Failed to enable subsys max: %d\n", ret); + regulator_set_voltage(arizona->dcvdd, 1200000, 1800000); + return ret; + } + + return 0; +} + +static int arizona_dvfs_disable(struct snd_soc_component *component) +{ + const struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + int ret; + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_DYNAMIC_FREQUENCY_SCALING_1, + ARIZONA_SUBSYS_MAX_FREQ, 0); + if (ret) { + dev_err(component->dev, "Failed to disable subsys max: %d\n", ret); + return ret; + } + + ret = regulator_set_voltage(arizona->dcvdd, 1200000, 1800000); + if (ret) { + dev_err(component->dev, "Failed to unboost DCVDD: %d\n", ret); + return ret; + } + + return 0; +} + +int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags) +{ + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + int ret = 0; + + mutex_lock(&priv->dvfs_lock); + + if (!priv->dvfs_cached && !priv->dvfs_reqs) { + ret = arizona_dvfs_enable(component); + if (ret) + goto err; + } + + priv->dvfs_reqs |= flags; +err: + mutex_unlock(&priv->dvfs_lock); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dvfs_up); + +int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags) +{ + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int old_reqs; + int ret = 0; + + mutex_lock(&priv->dvfs_lock); + + old_reqs = priv->dvfs_reqs; + priv->dvfs_reqs &= ~flags; + + if (!priv->dvfs_cached && old_reqs && !priv->dvfs_reqs) + ret = arizona_dvfs_disable(component); + + mutex_unlock(&priv->dvfs_lock); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dvfs_down); + +int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + int ret = 0; + + mutex_lock(&priv->dvfs_lock); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (priv->dvfs_reqs) + ret = arizona_dvfs_enable(component); + + priv->dvfs_cached = false; + break; + case SND_SOC_DAPM_PRE_PMD: + /* We must ensure DVFS is disabled before the codec goes into + * suspend so that we are never in an illegal state of DVFS + * enabled without enough DCVDD + */ + priv->dvfs_cached = true; + + if (priv->dvfs_reqs) + ret = arizona_dvfs_disable(component); + break; + default: + break; + } + + mutex_unlock(&priv->dvfs_lock); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_dvfs_sysclk_ev); + +void arizona_init_dvfs(struct arizona_priv *priv) +{ + mutex_init(&priv->dvfs_lock); +} +EXPORT_SYMBOL_GPL(arizona_init_dvfs); + +int arizona_anc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int val; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = 1 << w->shift; + break; + case SND_SOC_DAPM_PRE_PMD: + val = 1 << (w->shift + 1); + break; + default: + return 0; + } + + snd_soc_component_write(component, ARIZONA_CLOCK_CONTROL, val); + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_anc_ev); + +static unsigned int arizona_opclk_ref_48k_rates[] = { + 6144000, + 12288000, + 24576000, + 49152000, +}; + +static unsigned int arizona_opclk_ref_44k1_rates[] = { + 5644800, + 11289600, + 22579200, + 45158400, +}; + +static int arizona_set_opclk(struct snd_soc_component *component, + unsigned int clk, unsigned int freq) +{ + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int reg; + unsigned int *rates; + int ref, div, refclk; + + switch (clk) { + case ARIZONA_CLK_OPCLK: + reg = ARIZONA_OUTPUT_SYSTEM_CLOCK; + refclk = priv->sysclk; + break; + case ARIZONA_CLK_ASYNC_OPCLK: + reg = ARIZONA_OUTPUT_ASYNC_CLOCK; + refclk = priv->asyncclk; + break; + default: + return -EINVAL; + } + + if (refclk % 8000) + rates = arizona_opclk_ref_44k1_rates; + else + rates = arizona_opclk_ref_48k_rates; + + for (ref = 0; ref < ARRAY_SIZE(arizona_opclk_ref_48k_rates) && + rates[ref] <= refclk; ref++) { + div = 1; + while (rates[ref] / div >= freq && div < 32) { + if (rates[ref] / div == freq) { + dev_dbg(component->dev, "Configured %dHz OPCLK\n", + freq); + snd_soc_component_update_bits(component, reg, + ARIZONA_OPCLK_DIV_MASK | + ARIZONA_OPCLK_SEL_MASK, + (div << + ARIZONA_OPCLK_DIV_SHIFT) | + ref); + return 0; + } + div++; + } + } + + dev_err(component->dev, "Unable to generate %dHz OPCLK\n", freq); + return -EINVAL; +} + +int arizona_clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + unsigned int val; + int clk_idx; + int ret; + + ret = regmap_read(arizona->regmap, w->reg, &val); + if (ret) { + dev_err(component->dev, "Failed to check clock source: %d\n", ret); + return ret; + } + + val = (val & ARIZONA_SYSCLK_SRC_MASK) >> ARIZONA_SYSCLK_SRC_SHIFT; + + switch (val) { + case ARIZONA_CLK_SRC_MCLK1: + clk_idx = ARIZONA_MCLK1; + break; + case ARIZONA_CLK_SRC_MCLK2: + clk_idx = ARIZONA_MCLK2; + break; + default: + return 0; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return clk_prepare_enable(arizona->mclk[clk_idx]); + case SND_SOC_DAPM_POST_PMD: + clk_disable_unprepare(arizona->mclk[clk_idx]); + return 0; + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(arizona_clk_ev); + +int arizona_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + char *name; + unsigned int reg; + unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK; + unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT; + int *clk; + + switch (clk_id) { + case ARIZONA_CLK_SYSCLK: + name = "SYSCLK"; + reg = ARIZONA_SYSTEM_CLOCK_1; + clk = &priv->sysclk; + mask |= ARIZONA_SYSCLK_FRAC; + break; + case ARIZONA_CLK_ASYNCCLK: + name = "ASYNCCLK"; + reg = ARIZONA_ASYNC_CLOCK_1; + clk = &priv->asyncclk; + break; + case ARIZONA_CLK_OPCLK: + case ARIZONA_CLK_ASYNC_OPCLK: + return arizona_set_opclk(component, clk_id, freq); + default: + return -EINVAL; + } + + switch (freq) { + case 5644800: + case 6144000: + break; + case 11289600: + case 12288000: + val |= ARIZONA_CLK_12MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; + break; + case 22579200: + case 24576000: + val |= ARIZONA_CLK_24MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; + break; + case 45158400: + case 49152000: + val |= ARIZONA_CLK_49MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; + break; + case 67737600: + case 73728000: + val |= ARIZONA_CLK_73MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; + break; + case 90316800: + case 98304000: + val |= ARIZONA_CLK_98MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; + break; + case 135475200: + case 147456000: + val |= ARIZONA_CLK_147MHZ << ARIZONA_SYSCLK_FREQ_SHIFT; + break; + case 0: + dev_dbg(arizona->dev, "%s cleared\n", name); + *clk = freq; + return 0; + default: + return -EINVAL; + } + + *clk = freq; + + if (freq % 6144000) + val |= ARIZONA_SYSCLK_FRAC; + + dev_dbg(arizona->dev, "%s set to %uHz", name, freq); + + return regmap_update_bits(arizona->regmap, reg, mask, val); +} +EXPORT_SYMBOL_GPL(arizona_set_sysclk); + +static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + int lrclk, bclk, mode, base; + + base = dai->driver->base; + + lrclk = 0; + bclk = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + mode = ARIZONA_FMT_DSP_MODE_A; + break; + case SND_SOC_DAIFMT_DSP_B: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) + != SND_SOC_DAIFMT_CBM_CFM) { + arizona_aif_err(dai, "DSP_B not valid in slave mode\n"); + return -EINVAL; + } + mode = ARIZONA_FMT_DSP_MODE_B; + break; + case SND_SOC_DAIFMT_I2S: + mode = ARIZONA_FMT_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) + != SND_SOC_DAIFMT_CBM_CFM) { + arizona_aif_err(dai, "LEFT_J not valid in slave mode\n"); + return -EINVAL; + } + mode = ARIZONA_FMT_LEFT_JUSTIFIED_MODE; + break; + default: + arizona_aif_err(dai, "Unsupported DAI format %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBS_CFM: + lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + bclk |= ARIZONA_AIF1_BCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + bclk |= ARIZONA_AIF1_BCLK_MSTR; + lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR; + break; + default: + arizona_aif_err(dai, "Unsupported master mode %d\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + bclk |= ARIZONA_AIF1_BCLK_INV; + lrclk |= ARIZONA_AIF1TX_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + bclk |= ARIZONA_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + lrclk |= ARIZONA_AIF1TX_LRCLK_INV; + break; + default: + return -EINVAL; + } + + regmap_update_bits_async(arizona->regmap, base + ARIZONA_AIF_BCLK_CTRL, + ARIZONA_AIF1_BCLK_INV | + ARIZONA_AIF1_BCLK_MSTR, + bclk); + regmap_update_bits_async(arizona->regmap, base + ARIZONA_AIF_TX_PIN_CTRL, + ARIZONA_AIF1TX_LRCLK_INV | + ARIZONA_AIF1TX_LRCLK_MSTR, lrclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_RX_PIN_CTRL, + ARIZONA_AIF1RX_LRCLK_INV | + ARIZONA_AIF1RX_LRCLK_MSTR, lrclk); + regmap_update_bits(arizona->regmap, base + ARIZONA_AIF_FORMAT, + ARIZONA_AIF1_FMT_MASK, mode); + + return 0; +} + +static const int arizona_48k_bclk_rates[] = { + -1, + 48000, + 64000, + 96000, + 128000, + 192000, + 256000, + 384000, + 512000, + 768000, + 1024000, + 1536000, + 2048000, + 3072000, + 4096000, + 6144000, + 8192000, + 12288000, + 24576000, +}; + +static const int arizona_44k1_bclk_rates[] = { + -1, + 44100, + 58800, + 88200, + 117600, + 177640, + 235200, + 352800, + 470400, + 705600, + 940800, + 1411200, + 1881600, + 2822400, + 3763200, + 5644800, + 7526400, + 11289600, + 22579200, +}; + +static const unsigned int arizona_sr_vals[] = { + 0, + 12000, + 24000, + 48000, + 96000, + 192000, + 384000, + 768000, + 0, + 11025, + 22050, + 44100, + 88200, + 176400, + 352800, + 705600, + 4000, + 8000, + 16000, + 32000, + 64000, + 128000, + 256000, + 512000, +}; + +#define ARIZONA_48K_RATE_MASK 0x0F003E +#define ARIZONA_44K1_RATE_MASK 0x003E00 +#define ARIZONA_RATE_MASK (ARIZONA_48K_RATE_MASK | ARIZONA_44K1_RATE_MASK) + +static const struct snd_pcm_hw_constraint_list arizona_constraint = { + .count = ARRAY_SIZE(arizona_sr_vals), + .list = arizona_sr_vals, +}; + +static int arizona_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + unsigned int base_rate; + + if (!substream->runtime) + return 0; + + switch (dai_priv->clk) { + case ARIZONA_CLK_SYSCLK: + base_rate = priv->sysclk; + break; + case ARIZONA_CLK_ASYNCCLK: + base_rate = priv->asyncclk; + break; + default: + return 0; + } + + if (base_rate == 0) + dai_priv->constraint.mask = ARIZONA_RATE_MASK; + else if (base_rate % 8000) + dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK; + else + dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &dai_priv->constraint); +} + +static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component, + unsigned int rate) +{ + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + struct reg_sequence dac_comp[] = { + { 0x80, 0x3 }, + { ARIZONA_DAC_COMP_1, 0 }, + { ARIZONA_DAC_COMP_2, 0 }, + { 0x80, 0x0 }, + }; + + mutex_lock(&arizona->dac_comp_lock); + + dac_comp[1].def = arizona->dac_comp_coeff; + if (rate >= 176400) + dac_comp[2].def = arizona->dac_comp_enabled; + + mutex_unlock(&arizona->dac_comp_lock); + + regmap_multi_reg_write(arizona->regmap, + dac_comp, + ARRAY_SIZE(dac_comp)); +} + +static int arizona_hw_params_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + int base = dai->driver->base; + int i, sr_val, ret; + + /* + * We will need to be more flexible than this in future, + * currently we use a single sample rate for SYSCLK. + */ + for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) + if (arizona_sr_vals[i] == params_rate(params)) + break; + if (i == ARRAY_SIZE(arizona_sr_vals)) { + arizona_aif_err(dai, "Unsupported sample rate %dHz\n", + params_rate(params)); + return -EINVAL; + } + sr_val = i; + + switch (priv->arizona->type) { + case WM5102: + case WM8997: + if (arizona_sr_vals[sr_val] >= 88200) + ret = arizona_dvfs_up(component, ARIZONA_DVFS_SR1_RQ); + else + ret = arizona_dvfs_down(component, ARIZONA_DVFS_SR1_RQ); + + if (ret) { + arizona_aif_err(dai, "Failed to change DVFS %d\n", ret); + return ret; + } + break; + default: + break; + } + + switch (dai_priv->clk) { + case ARIZONA_CLK_SYSCLK: + switch (priv->arizona->type) { + case WM5102: + arizona_wm5102_set_dac_comp(component, + params_rate(params)); + break; + default: + break; + } + + snd_soc_component_update_bits(component, ARIZONA_SAMPLE_RATE_1, + ARIZONA_SAMPLE_RATE_1_MASK, + sr_val); + if (base) + snd_soc_component_update_bits(component, + base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, 0); + break; + case ARIZONA_CLK_ASYNCCLK: + snd_soc_component_update_bits(component, + ARIZONA_ASYNC_SAMPLE_RATE_1, + ARIZONA_ASYNC_SAMPLE_RATE_1_MASK, + sr_val); + if (base) + snd_soc_component_update_bits(component, + base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, + 8 << ARIZONA_AIF1_RATE_SHIFT); + break; + default: + arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); + return -EINVAL; + } + + return 0; +} + +static bool arizona_aif_cfg_changed(struct snd_soc_component *component, + int base, int bclk, int lrclk, int frame) +{ + int val; + + val = snd_soc_component_read(component, base + ARIZONA_AIF_BCLK_CTRL); + if (bclk != (val & ARIZONA_AIF1_BCLK_FREQ_MASK)) + return true; + + val = snd_soc_component_read(component, base + ARIZONA_AIF_RX_BCLK_RATE); + if (lrclk != (val & ARIZONA_AIF1RX_BCPF_MASK)) + return true; + + val = snd_soc_component_read(component, base + ARIZONA_AIF_FRAME_CTRL_1); + if (frame != (val & (ARIZONA_AIF1TX_WL_MASK | + ARIZONA_AIF1TX_SLOT_LEN_MASK))) + return true; + + return false; +} + +static int arizona_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + int base = dai->driver->base; + const int *rates; + int i, ret, val; + int channels = params_channels(params); + int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; + int tdm_width = arizona->tdm_width[dai->id - 1]; + int tdm_slots = arizona->tdm_slots[dai->id - 1]; + int bclk, lrclk, wl, frame, bclk_target; + bool reconfig; + unsigned int aif_tx_state, aif_rx_state; + + if (params_rate(params) % 4000) + rates = &arizona_44k1_bclk_rates[0]; + else + rates = &arizona_48k_bclk_rates[0]; + + wl = params_width(params); + + if (tdm_slots) { + arizona_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n", + tdm_slots, tdm_width); + bclk_target = tdm_slots * tdm_width * params_rate(params); + channels = tdm_slots; + } else { + bclk_target = snd_soc_params_to_bclk(params); + tdm_width = wl; + } + + if (chan_limit && chan_limit < channels) { + arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); + bclk_target /= channels; + bclk_target *= chan_limit; + } + + /* Force multiple of 2 channels for I2S mode */ + val = snd_soc_component_read(component, base + ARIZONA_AIF_FORMAT); + val &= ARIZONA_AIF1_FMT_MASK; + if ((channels & 1) && (val == ARIZONA_FMT_I2S_MODE)) { + arizona_aif_dbg(dai, "Forcing stereo mode\n"); + bclk_target /= channels; + bclk_target *= channels + 1; + } + + for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { + if (rates[i] >= bclk_target && + rates[i] % params_rate(params) == 0) { + bclk = i; + break; + } + } + if (i == ARRAY_SIZE(arizona_44k1_bclk_rates)) { + arizona_aif_err(dai, "Unsupported sample rate %dHz\n", + params_rate(params)); + return -EINVAL; + } + + lrclk = rates[bclk] / params_rate(params); + + arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", + rates[bclk], rates[bclk] / lrclk); + + frame = wl << ARIZONA_AIF1TX_WL_SHIFT | tdm_width; + + reconfig = arizona_aif_cfg_changed(component, base, bclk, lrclk, frame); + + if (reconfig) { + /* Save AIF TX/RX state */ + aif_tx_state = snd_soc_component_read(component, + base + ARIZONA_AIF_TX_ENABLES); + aif_rx_state = snd_soc_component_read(component, + base + ARIZONA_AIF_RX_ENABLES); + /* Disable AIF TX/RX before reconfiguring it */ + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_TX_ENABLES, + 0xff, 0x0); + regmap_update_bits(arizona->regmap, + base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0); + } + + ret = arizona_hw_params_rate(substream, params, dai); + if (ret != 0) + goto restore_aif; + + if (reconfig) { + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_BCLK_CTRL, + ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_TX_BCLK_RATE, + ARIZONA_AIF1TX_BCPF_MASK, lrclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_RX_BCLK_RATE, + ARIZONA_AIF1RX_BCPF_MASK, lrclk); + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_FRAME_CTRL_1, + ARIZONA_AIF1TX_WL_MASK | + ARIZONA_AIF1TX_SLOT_LEN_MASK, frame); + regmap_update_bits(arizona->regmap, + base + ARIZONA_AIF_FRAME_CTRL_2, + ARIZONA_AIF1RX_WL_MASK | + ARIZONA_AIF1RX_SLOT_LEN_MASK, frame); + } + +restore_aif: + if (reconfig) { + /* Restore AIF TX/RX state */ + regmap_update_bits_async(arizona->regmap, + base + ARIZONA_AIF_TX_ENABLES, + 0xff, aif_tx_state); + regmap_update_bits(arizona->regmap, + base + ARIZONA_AIF_RX_ENABLES, + 0xff, aif_rx_state); + } + return ret; +} + +static const char *arizona_dai_clk_str(int clk_id) +{ + switch (clk_id) { + case ARIZONA_CLK_SYSCLK: + return "SYSCLK"; + case ARIZONA_CLK_ASYNCCLK: + return "ASYNCCLK"; + default: + return "Unknown clock"; + } +} + +static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + struct snd_soc_dapm_route routes[2]; + + switch (clk_id) { + case ARIZONA_CLK_SYSCLK: + case ARIZONA_CLK_ASYNCCLK: + break; + default: + return -EINVAL; + } + + if (clk_id == dai_priv->clk) + return 0; + + if (snd_soc_dai_active(dai)) { + dev_err(component->dev, "Can't change clock on active DAI %d\n", + dai->id); + return -EBUSY; + } + + dev_dbg(component->dev, "Setting AIF%d to %s\n", dai->id + 1, + arizona_dai_clk_str(clk_id)); + + memset(&routes, 0, sizeof(routes)); + routes[0].sink = dai->driver->capture.stream_name; + routes[1].sink = dai->driver->playback.stream_name; + + routes[0].source = arizona_dai_clk_str(dai_priv->clk); + routes[1].source = arizona_dai_clk_str(dai_priv->clk); + snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes)); + + routes[0].source = arizona_dai_clk_str(clk_id); + routes[1].source = arizona_dai_clk_str(clk_id); + snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes)); + + dai_priv->clk = clk_id; + + return snd_soc_dapm_sync(dapm); +} + +static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_component *component = dai->component; + int base = dai->driver->base; + unsigned int reg; + + if (tristate) + reg = ARIZONA_AIF1_TRI; + else + reg = 0; + + return snd_soc_component_update_bits(component, + base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_TRI, reg); +} + +static void arizona_set_channels_to_mask(struct snd_soc_dai *dai, + unsigned int base, + int channels, unsigned int mask) +{ + struct snd_soc_component *component = dai->component; + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + int slot, i; + + for (i = 0; i < channels; ++i) { + slot = ffs(mask) - 1; + if (slot < 0) + return; + + regmap_write(arizona->regmap, base + i, slot); + + mask &= ~(1 << slot); + } + + if (mask) + arizona_aif_warn(dai, "Too many channels in TDM mask\n"); +} + +static int arizona_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + int base = dai->driver->base; + int rx_max_chan = dai->driver->playback.channels_max; + int tx_max_chan = dai->driver->capture.channels_max; + + /* Only support TDM for the physical AIFs */ + if (dai->id > ARIZONA_MAX_AIF) + return -ENOTSUPP; + + if (slots == 0) { + tx_mask = (1 << tx_max_chan) - 1; + rx_mask = (1 << rx_max_chan) - 1; + } + + arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_3, + tx_max_chan, tx_mask); + arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_11, + rx_max_chan, rx_mask); + + arizona->tdm_width[dai->id - 1] = slot_width; + arizona->tdm_slots[dai->id - 1] = slots; + + return 0; +} + +const struct snd_soc_dai_ops arizona_dai_ops = { + .startup = arizona_startup, + .set_fmt = arizona_set_fmt, + .set_tdm_slot = arizona_set_tdm_slot, + .hw_params = arizona_hw_params, + .set_sysclk = arizona_dai_set_sysclk, + .set_tristate = arizona_set_tristate, +}; +EXPORT_SYMBOL_GPL(arizona_dai_ops); + +const struct snd_soc_dai_ops arizona_simple_dai_ops = { + .startup = arizona_startup, + .hw_params = arizona_hw_params_rate, + .set_sysclk = arizona_dai_set_sysclk, +}; +EXPORT_SYMBOL_GPL(arizona_simple_dai_ops); + +int arizona_init_dai(struct arizona_priv *priv, int id) +{ + struct arizona_dai_priv *dai_priv = &priv->dai[id]; + + dai_priv->clk = ARIZONA_CLK_SYSCLK; + dai_priv->constraint = arizona_constraint; + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_dai); + +static struct { + unsigned int min; + unsigned int max; + u16 fratio; + int ratio; +} fll_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +static const unsigned int pseudo_fref_max[ARIZONA_FLL_MAX_FRATIO] = { + 13500000, + 6144000, + 6144000, + 3072000, + 3072000, + 2822400, + 2822400, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 768000, +}; + +static struct { + unsigned int min; + unsigned int max; + u16 gain; +} fll_gains[] = { + { 0, 256000, 0 }, + { 256000, 1000000, 2 }, + { 1000000, 13500000, 4 }, +}; + +struct arizona_fll_cfg { + int n; + unsigned int theta; + unsigned int lambda; + int refdiv; + int outdiv; + int fratio; + int gain; +}; + +static int arizona_validate_fll(struct arizona_fll *fll, + unsigned int Fref, + unsigned int Fout) +{ + unsigned int Fvco_min; + + if (fll->fout && Fout != fll->fout) { + arizona_fll_err(fll, + "Can't change output on active FLL\n"); + return -EINVAL; + } + + if (Fref / ARIZONA_FLL_MAX_REFDIV > ARIZONA_FLL_MAX_FREF) { + arizona_fll_err(fll, + "Can't scale %dMHz in to <=13.5MHz\n", + Fref); + return -EINVAL; + } + + Fvco_min = ARIZONA_FLL_MIN_FVCO * fll->vco_mult; + if (Fout * ARIZONA_FLL_MAX_OUTDIV < Fvco_min) { + arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + + return 0; +} + +static int arizona_find_fratio(unsigned int Fref, int *fratio) +{ + int i; + + /* Find an appropriate FLL_FRATIO */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + if (fratio) + *fratio = fll_fratios[i].fratio; + return fll_fratios[i].ratio; + } + } + + return -EINVAL; +} + +static int arizona_calc_fratio(struct arizona_fll *fll, + struct arizona_fll_cfg *cfg, + unsigned int target, + unsigned int Fref, bool sync) +{ + int init_ratio, ratio; + int refdiv, div; + + /* Fref must be <=13.5MHz, find initial refdiv */ + div = 1; + cfg->refdiv = 0; + while (Fref > ARIZONA_FLL_MAX_FREF) { + div *= 2; + Fref /= 2; + cfg->refdiv++; + + if (div > ARIZONA_FLL_MAX_REFDIV) + return -EINVAL; + } + + /* Find an appropriate FLL_FRATIO */ + init_ratio = arizona_find_fratio(Fref, &cfg->fratio); + if (init_ratio < 0) { + arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n", + Fref); + return init_ratio; + } + + switch (fll->arizona->type) { + case WM5102: + case WM8997: + return init_ratio; + case WM5110: + case WM8280: + if (fll->arizona->rev < 3 || sync) + return init_ratio; + break; + default: + if (sync) + return init_ratio; + break; + } + + cfg->fratio = init_ratio - 1; + + /* Adjust FRATIO/refdiv to avoid integer mode if possible */ + refdiv = cfg->refdiv; + + arizona_fll_dbg(fll, "pseudo: initial ratio=%u fref=%u refdiv=%u\n", + init_ratio, Fref, refdiv); + + while (div <= ARIZONA_FLL_MAX_REFDIV) { + /* start from init_ratio because this may already give a + * fractional N.K + */ + for (ratio = init_ratio; ratio > 0; ratio--) { + if (target % (ratio * Fref)) { + cfg->refdiv = refdiv; + cfg->fratio = ratio - 1; + arizona_fll_dbg(fll, + "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n", + Fref, refdiv, div, ratio); + return ratio; + } + } + + for (ratio = init_ratio + 1; ratio <= ARIZONA_FLL_MAX_FRATIO; + ratio++) { + if ((ARIZONA_FLL_VCO_CORNER / 2) / + (fll->vco_mult * ratio) < Fref) { + arizona_fll_dbg(fll, "pseudo: hit VCO corner\n"); + break; + } + + if (Fref > pseudo_fref_max[ratio - 1]) { + arizona_fll_dbg(fll, + "pseudo: exceeded max fref(%u) for ratio=%u\n", + pseudo_fref_max[ratio - 1], + ratio); + break; + } + + if (target % (ratio * Fref)) { + cfg->refdiv = refdiv; + cfg->fratio = ratio - 1; + arizona_fll_dbg(fll, + "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n", + Fref, refdiv, div, ratio); + return ratio; + } + } + + div *= 2; + Fref /= 2; + refdiv++; + init_ratio = arizona_find_fratio(Fref, NULL); + arizona_fll_dbg(fll, + "pseudo: change fref=%u refdiv=%d(%d) ratio=%u\n", + Fref, refdiv, div, init_ratio); + } + + arizona_fll_warn(fll, "Falling back to integer mode operation\n"); + return cfg->fratio + 1; +} + +static int arizona_calc_fll(struct arizona_fll *fll, + struct arizona_fll_cfg *cfg, + unsigned int Fref, bool sync) +{ + unsigned int target, div, gcd_fll; + int i, ratio; + + arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, fll->fout); + + /* Fvco should be over the targt; don't check the upper bound */ + div = ARIZONA_FLL_MIN_OUTDIV; + while (fll->fout * div < ARIZONA_FLL_MIN_FVCO * fll->vco_mult) { + div++; + if (div > ARIZONA_FLL_MAX_OUTDIV) + return -EINVAL; + } + target = fll->fout * div / fll->vco_mult; + cfg->outdiv = div; + + arizona_fll_dbg(fll, "Fvco=%dHz\n", target); + + /* Find an appropriate FLL_FRATIO and refdiv */ + ratio = arizona_calc_fratio(fll, cfg, target, Fref, sync); + if (ratio < 0) + return ratio; + + /* Apply the division for our remaining calculations */ + Fref = Fref / (1 << cfg->refdiv); + + cfg->n = target / (ratio * Fref); + + if (target % (ratio * Fref)) { + gcd_fll = gcd(target, ratio * Fref); + arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll); + + cfg->theta = (target - (cfg->n * ratio * Fref)) + / gcd_fll; + cfg->lambda = (ratio * Fref) / gcd_fll; + } else { + cfg->theta = 0; + cfg->lambda = 0; + } + + /* Round down to 16bit range with cost of accuracy lost. + * Denominator must be bigger than numerator so we only + * take care of it. + */ + while (cfg->lambda >= (1 << 16)) { + cfg->theta >>= 1; + cfg->lambda >>= 1; + } + + for (i = 0; i < ARRAY_SIZE(fll_gains); i++) { + if (fll_gains[i].min <= Fref && Fref <= fll_gains[i].max) { + cfg->gain = fll_gains[i].gain; + break; + } + } + if (i == ARRAY_SIZE(fll_gains)) { + arizona_fll_err(fll, "Unable to find gain for Fref=%uHz\n", + Fref); + return -EINVAL; + } + + arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n", + cfg->n, cfg->theta, cfg->lambda); + arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n", + cfg->fratio, ratio, cfg->outdiv, + cfg->refdiv, 1 << cfg->refdiv); + arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain); + + return 0; +} + +static void arizona_apply_fll(struct arizona *arizona, unsigned int base, + struct arizona_fll_cfg *cfg, int source, + bool sync) +{ + regmap_update_bits_async(arizona->regmap, base + 3, + ARIZONA_FLL1_THETA_MASK, cfg->theta); + regmap_update_bits_async(arizona->regmap, base + 4, + ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda); + regmap_update_bits_async(arizona->regmap, base + 5, + ARIZONA_FLL1_FRATIO_MASK, + cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT); + regmap_update_bits_async(arizona->regmap, base + 6, + ARIZONA_FLL1_CLK_REF_DIV_MASK | + ARIZONA_FLL1_CLK_REF_SRC_MASK, + cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT | + source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT); + + if (sync) { + regmap_update_bits(arizona->regmap, base + 0x7, + ARIZONA_FLL1_GAIN_MASK, + cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); + } else { + regmap_update_bits(arizona->regmap, base + 0x5, + ARIZONA_FLL1_OUTDIV_MASK, + cfg->outdiv << ARIZONA_FLL1_OUTDIV_SHIFT); + regmap_update_bits(arizona->regmap, base + 0x9, + ARIZONA_FLL1_GAIN_MASK, + cfg->gain << ARIZONA_FLL1_GAIN_SHIFT); + } + + regmap_update_bits_async(arizona->regmap, base + 2, + ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK, + ARIZONA_FLL1_CTRL_UPD | cfg->n); +} + +static int arizona_is_enabled_fll(struct arizona_fll *fll, int base) +{ + struct arizona *arizona = fll->arizona; + unsigned int reg; + int ret; + + ret = regmap_read(arizona->regmap, base + 1, ®); + if (ret != 0) { + arizona_fll_err(fll, "Failed to read current state: %d\n", + ret); + return ret; + } + + return reg & ARIZONA_FLL1_ENA; +} + +static int arizona_set_fll_clks(struct arizona_fll *fll, int base, bool ena) +{ + struct arizona *arizona = fll->arizona; + unsigned int val; + struct clk *clk; + int ret; + + ret = regmap_read(arizona->regmap, base + 6, &val); + if (ret != 0) { + arizona_fll_err(fll, "Failed to read current source: %d\n", + ret); + return ret; + } + + val &= ARIZONA_FLL1_CLK_REF_SRC_MASK; + val >>= ARIZONA_FLL1_CLK_REF_SRC_SHIFT; + + switch (val) { + case ARIZONA_FLL_SRC_MCLK1: + clk = arizona->mclk[ARIZONA_MCLK1]; + break; + case ARIZONA_FLL_SRC_MCLK2: + clk = arizona->mclk[ARIZONA_MCLK2]; + break; + default: + return 0; + } + + if (ena) { + return clk_prepare_enable(clk); + } else { + clk_disable_unprepare(clk); + return 0; + } +} + +static int arizona_enable_fll(struct arizona_fll *fll) +{ + struct arizona *arizona = fll->arizona; + bool use_sync = false; + int already_enabled = arizona_is_enabled_fll(fll, fll->base); + int sync_enabled = arizona_is_enabled_fll(fll, fll->base + 0x10); + struct arizona_fll_cfg cfg; + int i; + unsigned int val; + + if (already_enabled < 0) + return already_enabled; + if (sync_enabled < 0) + return sync_enabled; + + if (already_enabled) { + /* Facilitate smooth refclk across the transition */ + regmap_update_bits(fll->arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN); + udelay(32); + regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9, + ARIZONA_FLL1_GAIN_MASK, 0); + + if (arizona_is_enabled_fll(fll, fll->base + 0x10) > 0) + arizona_set_fll_clks(fll, fll->base + 0x10, false); + arizona_set_fll_clks(fll, fll->base, false); + } + + /* + * If we have both REFCLK and SYNCCLK then enable both, + * otherwise apply the SYNCCLK settings to REFCLK. + */ + if (fll->ref_src >= 0 && fll->ref_freq && + fll->ref_src != fll->sync_src) { + arizona_calc_fll(fll, &cfg, fll->ref_freq, false); + + /* Ref path hardcodes lambda to 65536 when sync is on */ + if (fll->sync_src >= 0 && cfg.lambda) + cfg.theta = (cfg.theta * (1 << 16)) / cfg.lambda; + + arizona_apply_fll(arizona, fll->base, &cfg, fll->ref_src, + false); + if (fll->sync_src >= 0) { + arizona_calc_fll(fll, &cfg, fll->sync_freq, true); + + arizona_apply_fll(arizona, fll->base + 0x10, &cfg, + fll->sync_src, true); + use_sync = true; + } + } else if (fll->sync_src >= 0) { + arizona_calc_fll(fll, &cfg, fll->sync_freq, false); + + arizona_apply_fll(arizona, fll->base, &cfg, + fll->sync_src, false); + + regmap_update_bits_async(arizona->regmap, fll->base + 0x11, + ARIZONA_FLL1_SYNC_ENA, 0); + } else { + arizona_fll_err(fll, "No clocks provided\n"); + return -EINVAL; + } + + if (already_enabled && !!sync_enabled != use_sync) + arizona_fll_warn(fll, "Synchroniser changed on active FLL\n"); + + /* + * Increase the bandwidth if we're not using a low frequency + * sync source. + */ + if (use_sync && fll->sync_freq > 100000) + regmap_update_bits_async(arizona->regmap, fll->base + 0x17, + ARIZONA_FLL1_SYNC_BW, 0); + else + regmap_update_bits_async(arizona->regmap, fll->base + 0x17, + ARIZONA_FLL1_SYNC_BW, + ARIZONA_FLL1_SYNC_BW); + + if (!already_enabled) + pm_runtime_get_sync(arizona->dev); + + if (use_sync) { + arizona_set_fll_clks(fll, fll->base + 0x10, true); + regmap_update_bits_async(arizona->regmap, fll->base + 0x11, + ARIZONA_FLL1_SYNC_ENA, + ARIZONA_FLL1_SYNC_ENA); + } + arizona_set_fll_clks(fll, fll->base, true); + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); + + if (already_enabled) + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, 0); + + arizona_fll_dbg(fll, "Waiting for FLL lock...\n"); + val = 0; + for (i = 0; i < 15; i++) { + if (i < 5) + usleep_range(200, 400); + else + msleep(20); + + regmap_read(arizona->regmap, + ARIZONA_INTERRUPT_RAW_STATUS_5, + &val); + if (val & (ARIZONA_FLL1_CLOCK_OK_STS << (fll->id - 1))) + break; + } + if (i == 15) + arizona_fll_warn(fll, "Timed out waiting for lock\n"); + else + arizona_fll_dbg(fll, "FLL locked (%d polls)\n", i); + + return 0; +} + +static void arizona_disable_fll(struct arizona_fll *fll) +{ + struct arizona *arizona = fll->arizona; + bool ref_change, sync_change; + + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN); + regmap_update_bits_check(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_ENA, 0, &ref_change); + regmap_update_bits_check(arizona->regmap, fll->base + 0x11, + ARIZONA_FLL1_SYNC_ENA, 0, &sync_change); + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, 0); + + if (sync_change) + arizona_set_fll_clks(fll, fll->base + 0x10, false); + + if (ref_change) { + arizona_set_fll_clks(fll, fll->base, false); + pm_runtime_put_autosuspend(arizona->dev); + } +} + +int arizona_set_fll_refclk(struct arizona_fll *fll, int source, + unsigned int Fref, unsigned int Fout) +{ + int ret = 0; + + if (fll->ref_src == source && fll->ref_freq == Fref) + return 0; + + if (fll->fout && Fref > 0) { + ret = arizona_validate_fll(fll, Fref, fll->fout); + if (ret != 0) + return ret; + } + + fll->ref_src = source; + fll->ref_freq = Fref; + + if (fll->fout && Fref > 0) + ret = arizona_enable_fll(fll); + + return ret; +} +EXPORT_SYMBOL_GPL(arizona_set_fll_refclk); + +int arizona_set_fll(struct arizona_fll *fll, int source, + unsigned int Fref, unsigned int Fout) +{ + int ret = 0; + + if (fll->sync_src == source && + fll->sync_freq == Fref && fll->fout == Fout) + return 0; + + if (Fout) { + if (fll->ref_src >= 0) { + ret = arizona_validate_fll(fll, fll->ref_freq, Fout); + if (ret != 0) + return ret; + } + + ret = arizona_validate_fll(fll, Fref, Fout); + if (ret != 0) + return ret; + } + + fll->sync_src = source; + fll->sync_freq = Fref; + fll->fout = Fout; + + if (Fout) + ret = arizona_enable_fll(fll); + else + arizona_disable_fll(fll); + + return ret; +} +EXPORT_SYMBOL_GPL(arizona_set_fll); + +int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, + int ok_irq, struct arizona_fll *fll) +{ + unsigned int val; + + fll->id = id; + fll->base = base; + fll->arizona = arizona; + fll->sync_src = ARIZONA_FLL_SRC_NONE; + + /* Configure default refclk to 32kHz if we have one */ + regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val); + switch (val & ARIZONA_CLK_32K_SRC_MASK) { + case ARIZONA_CLK_SRC_MCLK1: + case ARIZONA_CLK_SRC_MCLK2: + fll->ref_src = val & ARIZONA_CLK_32K_SRC_MASK; + break; + default: + fll->ref_src = ARIZONA_FLL_SRC_NONE; + } + fll->ref_freq = 32768; + + snprintf(fll->lock_name, sizeof(fll->lock_name), "FLL%d lock", id); + snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), + "FLL%d clock OK", id); + + regmap_update_bits(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_fll); + +/** + * arizona_set_output_mode - Set the mode of the specified output + * + * @component: Device to configure + * @output: Output number + * @diff: True to set the output to differential mode + * + * Some systems use external analogue switches to connect more + * analogue devices to the CODEC than are supported by the device. In + * some systems this requires changing the switched output from single + * ended to differential mode dynamically at runtime, an operation + * supported using this function. + * + * Most systems have a single static configuration and should use + * platform data instead. + */ +int arizona_set_output_mode(struct snd_soc_component *component, int output, + bool diff) +{ + unsigned int reg, val; + + if (output < 1 || output > 6) + return -EINVAL; + + reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8; + + if (diff) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + return snd_soc_component_update_bits(component, reg, + ARIZONA_OUT1_MONO, val); +} +EXPORT_SYMBOL_GPL(arizona_set_output_mode); + +static const struct soc_enum arizona_adsp2_rate_enum[] = { + SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP1_CONTROL_1, + ARIZONA_DSP1_RATE_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP2_CONTROL_1, + ARIZONA_DSP1_RATE_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP3_CONTROL_1, + ARIZONA_DSP1_RATE_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_DSP4_CONTROL_1, + ARIZONA_DSP1_RATE_SHIFT, 0xf, + ARIZONA_RATE_ENUM_SIZE, + arizona_rate_text, arizona_rate_val), +}; + +const struct snd_kcontrol_new arizona_adsp2_rate_controls[] = { + SOC_ENUM("DSP1 Rate", arizona_adsp2_rate_enum[0]), + SOC_ENUM("DSP2 Rate", arizona_adsp2_rate_enum[1]), + SOC_ENUM("DSP3 Rate", arizona_adsp2_rate_enum[2]), + SOC_ENUM("DSP4 Rate", arizona_adsp2_rate_enum[3]), +}; +EXPORT_SYMBOL_GPL(arizona_adsp2_rate_controls); + +static bool arizona_eq_filter_unstable(bool mode, __be16 _a, __be16 _b) +{ + s16 a = be16_to_cpu(_a); + s16 b = be16_to_cpu(_b); + + if (!mode) { + return abs(a) >= 4096; + } else { + if (abs(b) >= 4096) + return true; + + return (abs((a << 16) / (4096 - b)) >= 4096 << 4); + } +} + +int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + struct soc_bytes *params = (void *)kcontrol->private_value; + unsigned int val; + __be16 *data; + int len; + int ret; + + len = params->num_regs * regmap_get_val_bytes(arizona->regmap); + + data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + data[0] &= cpu_to_be16(ARIZONA_EQ1_B1_MODE); + + if (arizona_eq_filter_unstable(!!data[0], data[1], data[2]) || + arizona_eq_filter_unstable(true, data[4], data[5]) || + arizona_eq_filter_unstable(true, data[8], data[9]) || + arizona_eq_filter_unstable(true, data[12], data[13]) || + arizona_eq_filter_unstable(false, data[16], data[17])) { + dev_err(arizona->dev, "Rejecting unstable EQ coefficients\n"); + ret = -EINVAL; + goto out; + } + + ret = regmap_read(arizona->regmap, params->base, &val); + if (ret != 0) + goto out; + + val &= ~ARIZONA_EQ1_B1_MODE; + data[0] |= cpu_to_be16(val); + + ret = regmap_raw_write(arizona->regmap, params->base, data, len); + +out: + kfree(data); + return ret; +} +EXPORT_SYMBOL_GPL(arizona_eq_coeff_put); + +int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + __be16 *data = (__be16 *)ucontrol->value.bytes.data; + s16 val = be16_to_cpu(*data); + + if (abs(val) >= 4096) { + dev_err(arizona->dev, "Rejecting unstable LHPF coefficients\n"); + return -EINVAL; + } + + return snd_soc_bytes_put(kcontrol, ucontrol); +} +EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put); + +int arizona_of_get_audio_pdata(struct arizona *arizona) +{ + struct arizona_pdata *pdata = &arizona->pdata; + struct device_node *np = arizona->dev->of_node; + struct property *prop; + const __be32 *cur; + u32 val; + u32 pdm_val[ARIZONA_MAX_PDM_SPK]; + int ret; + int count = 0; + + count = 0; + of_property_for_each_u32(np, "wlf,inmode", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->inmode)) + break; + + pdata->inmode[count] = val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,dmic-ref", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->dmic_ref)) + break; + + pdata->dmic_ref[count] = val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,out-mono", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->out_mono)) + break; + + pdata->out_mono[count] = !!val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,max-channels-clocked", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->max_channels_clocked)) + break; + + pdata->max_channels_clocked[count] = val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,out-volume-limit", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->out_vol_limit)) + break; + + pdata->out_vol_limit[count] = val; + count++; + } + + ret = of_property_read_u32_array(np, "wlf,spk-fmt", + pdm_val, ARRAY_SIZE(pdm_val)); + + if (ret >= 0) + for (count = 0; count < ARRAY_SIZE(pdata->spk_fmt); ++count) + pdata->spk_fmt[count] = pdm_val[count]; + + ret = of_property_read_u32_array(np, "wlf,spk-mute", + pdm_val, ARRAY_SIZE(pdm_val)); + + if (ret >= 0) + for (count = 0; count < ARRAY_SIZE(pdata->spk_mute); ++count) + pdata->spk_mute[count] = pdm_val[count]; + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_of_get_audio_pdata); + +MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h new file mode 100644 index 0000000000..ecd8890eef --- /dev/null +++ b/sound/soc/codecs/arizona.h @@ -0,0 +1,398 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * arizona.h - Wolfson Arizona class device shared support + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#ifndef _ASOC_ARIZONA_H +#define _ASOC_ARIZONA_H + +#include +#include +#include + +#include + +#include "wm_adsp.h" + +#define ARIZONA_CLK_SYSCLK 1 +#define ARIZONA_CLK_ASYNCCLK 2 +#define ARIZONA_CLK_OPCLK 3 +#define ARIZONA_CLK_ASYNC_OPCLK 4 + +#define ARIZONA_CLK_SRC_MCLK1 0x0 +#define ARIZONA_CLK_SRC_MCLK2 0x1 +#define ARIZONA_CLK_SRC_FLL1 0x4 +#define ARIZONA_CLK_SRC_FLL2 0x5 +#define ARIZONA_CLK_SRC_AIF1BCLK 0x8 +#define ARIZONA_CLK_SRC_AIF2BCLK 0x9 +#define ARIZONA_CLK_SRC_AIF3BCLK 0xa + +#define ARIZONA_FLL_SRC_NONE -1 +#define ARIZONA_FLL_SRC_MCLK1 0 +#define ARIZONA_FLL_SRC_MCLK2 1 +#define ARIZONA_FLL_SRC_SLIMCLK 3 +#define ARIZONA_FLL_SRC_FLL1 4 +#define ARIZONA_FLL_SRC_FLL2 5 +#define ARIZONA_FLL_SRC_AIF1BCLK 8 +#define ARIZONA_FLL_SRC_AIF2BCLK 9 +#define ARIZONA_FLL_SRC_AIF3BCLK 10 +#define ARIZONA_FLL_SRC_AIF1LRCLK 12 +#define ARIZONA_FLL_SRC_AIF2LRCLK 13 +#define ARIZONA_FLL_SRC_AIF3LRCLK 14 + +#define ARIZONA_MIXER_VOL_MASK 0x00FE +#define ARIZONA_MIXER_VOL_SHIFT 1 +#define ARIZONA_MIXER_VOL_WIDTH 7 + +#define ARIZONA_CLK_6MHZ 0 +#define ARIZONA_CLK_12MHZ 1 +#define ARIZONA_CLK_24MHZ 2 +#define ARIZONA_CLK_49MHZ 3 +#define ARIZONA_CLK_73MHZ 4 +#define ARIZONA_CLK_98MHZ 5 +#define ARIZONA_CLK_147MHZ 6 + +#define ARIZONA_MAX_DAI 10 +#define ARIZONA_MAX_ADSP 4 + +#define ARIZONA_DVFS_SR1_RQ 0x001 +#define ARIZONA_DVFS_ADSP1_RQ 0x100 + +/* Notifier events */ +#define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1 + +struct wm_adsp; + +struct arizona_dai_priv { + int clk; + + struct snd_pcm_hw_constraint_list constraint; +}; + +struct arizona_priv { + struct wm_adsp adsp[ARIZONA_MAX_ADSP]; + struct arizona *arizona; + int sysclk; + int asyncclk; + struct arizona_dai_priv dai[ARIZONA_MAX_DAI]; + + int num_inputs; + unsigned int in_pending; + + unsigned int out_up_pending; + unsigned int out_up_delay; + unsigned int out_down_pending; + unsigned int out_down_delay; + + unsigned int dvfs_reqs; + struct mutex dvfs_lock; + bool dvfs_cached; + + /* Variables used by arizona-jack.c code */ + struct mutex lock; + struct delayed_work hpdet_work; + struct delayed_work micd_detect_work; + struct delayed_work micd_timeout_work; + struct snd_soc_jack *jack; + struct regulator *micvdd; + struct gpio_desc *micd_pol_gpio; + + u16 last_jackdet; + + int micd_mode; + const struct arizona_micd_config *micd_modes; + int micd_num_modes; + + int micd_button_mask; + const struct arizona_micd_range *micd_ranges; + int num_micd_ranges; + + bool micd_reva; + bool micd_clamp; + + bool hpdet_active; + bool hpdet_done; + bool hpdet_retried; + + bool mic; + bool detecting; + + int num_hpdet_res; + unsigned int hpdet_res[3]; + + int jack_flips; + int hpdet_ip_version; +}; + +struct arizona_voice_trigger_info { + int core; +}; + +#define ARIZONA_NUM_MIXER_INPUTS 104 + +extern const unsigned int arizona_mixer_tlv[]; +extern const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; +extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; + +#define ARIZONA_GAINMUX_CONTROLS(name, base) \ + SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv) + +#define ARIZONA_MIXER_CONTROLS(name, base) \ + SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 3, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 5, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 7, \ + ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + arizona_mixer_tlv) + +#define ARIZONA_MUX_ENUM_DECL(name, reg) \ + SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL( \ + name, reg, 0, 0xff, arizona_mixer_texts, arizona_mixer_values) + +#define ARIZONA_MUX_CTL_DECL(name) \ + const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM("Route", name##_enum) + +#define ARIZONA_MUX_ENUMS(name, base_reg) \ + static ARIZONA_MUX_ENUM_DECL(name##_enum, base_reg); \ + static ARIZONA_MUX_CTL_DECL(name) + +#define ARIZONA_MIXER_ENUMS(name, base_reg) \ + ARIZONA_MUX_ENUMS(name##_in1, base_reg); \ + ARIZONA_MUX_ENUMS(name##_in2, base_reg + 2); \ + ARIZONA_MUX_ENUMS(name##_in3, base_reg + 4); \ + ARIZONA_MUX_ENUMS(name##_in4, base_reg + 6) + +#define ARIZONA_DSP_AUX_ENUMS(name, base_reg) \ + ARIZONA_MUX_ENUMS(name##_aux1, base_reg); \ + ARIZONA_MUX_ENUMS(name##_aux2, base_reg + 8); \ + ARIZONA_MUX_ENUMS(name##_aux3, base_reg + 16); \ + ARIZONA_MUX_ENUMS(name##_aux4, base_reg + 24); \ + ARIZONA_MUX_ENUMS(name##_aux5, base_reg + 32); \ + ARIZONA_MUX_ENUMS(name##_aux6, base_reg + 40) + +#define ARIZONA_MUX(name, ctrl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl) + +#define ARIZONA_MUX_WIDGETS(name, name_str) \ + ARIZONA_MUX(name_str " Input", &name##_mux) + +#define ARIZONA_MIXER_WIDGETS(name, name_str) \ + ARIZONA_MUX(name_str " Input 1", &name##_in1_mux), \ + ARIZONA_MUX(name_str " Input 2", &name##_in2_mux), \ + ARIZONA_MUX(name_str " Input 3", &name##_in3_mux), \ + ARIZONA_MUX(name_str " Input 4", &name##_in4_mux), \ + SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0) + +#define ARIZONA_DSP_WIDGETS(name, name_str) \ + ARIZONA_MIXER_WIDGETS(name##L, name_str "L"), \ + ARIZONA_MIXER_WIDGETS(name##R, name_str "R"), \ + ARIZONA_MUX(name_str " Aux 1", &name##_aux1_mux), \ + ARIZONA_MUX(name_str " Aux 2", &name##_aux2_mux), \ + ARIZONA_MUX(name_str " Aux 3", &name##_aux3_mux), \ + ARIZONA_MUX(name_str " Aux 4", &name##_aux4_mux), \ + ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \ + ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux) + +#define ARIZONA_MUX_ROUTES(widget, name) \ + { widget, NULL, name " Input" }, \ + ARIZONA_MIXER_INPUT_ROUTES(name " Input") + +#define ARIZONA_MIXER_ROUTES(widget, name) \ + { widget, NULL, name " Mixer" }, \ + { name " Mixer", NULL, name " Input 1" }, \ + { name " Mixer", NULL, name " Input 2" }, \ + { name " Mixer", NULL, name " Input 3" }, \ + { name " Mixer", NULL, name " Input 4" }, \ + ARIZONA_MIXER_INPUT_ROUTES(name " Input 1"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Input 2"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Input 4") + +#define ARIZONA_DSP_ROUTES(name) \ + { name, NULL, name " Preloader"}, \ + { name " Preloader", NULL, "SYSCLK" }, \ + { name " Preload", NULL, name " Preloader"}, \ + { name, NULL, name " Aux 1" }, \ + { name, NULL, name " Aux 2" }, \ + { name, NULL, name " Aux 3" }, \ + { name, NULL, name " Aux 4" }, \ + { name, NULL, name " Aux 5" }, \ + { name, NULL, name " Aux 6" }, \ + ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \ + ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \ + ARIZONA_MIXER_ROUTES(name, name "L"), \ + ARIZONA_MIXER_ROUTES(name, name "R") + +#define ARIZONA_EQ_CONTROL(xname, xbase) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \ + .put = arizona_eq_coeff_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) { .base = xbase, \ + .num_regs = 20, .mask = ~ARIZONA_EQ1_B1_MODE }) } + +#define ARIZONA_LHPF_CONTROL(xname, xbase) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \ + .put = arizona_lhpf_coeff_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) { .base = xbase, \ + .num_regs = 1 }) } + +#define ARIZONA_RATE_ENUM_SIZE 4 +#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14 + +/* SND_JACK_* mask for supported cable/switch types */ +#define ARIZONA_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_MECHANICAL) + +extern const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE]; +extern const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE]; +extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE]; +extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE]; + +extern const struct soc_enum arizona_isrc_fsl[]; +extern const struct soc_enum arizona_isrc_fsh[]; +extern const struct soc_enum arizona_asrc_rate1; + +extern const struct soc_enum arizona_in_vi_ramp; +extern const struct soc_enum arizona_in_vd_ramp; + +extern const struct soc_enum arizona_out_vi_ramp; +extern const struct soc_enum arizona_out_vd_ramp; + +extern const struct soc_enum arizona_lhpf1_mode; +extern const struct soc_enum arizona_lhpf2_mode; +extern const struct soc_enum arizona_lhpf3_mode; +extern const struct soc_enum arizona_lhpf4_mode; + +extern const struct soc_enum arizona_ng_hold; +extern const struct soc_enum arizona_in_hpf_cut_enum; +extern const struct soc_enum arizona_in_dmic_osr[]; + +extern const struct snd_kcontrol_new arizona_adsp2_rate_controls[]; + +extern const struct soc_enum arizona_anc_input_src[]; +extern const struct soc_enum arizona_anc_ng_enum; +extern const struct soc_enum arizona_output_anc_src[]; + +extern const struct snd_kcontrol_new arizona_voice_trigger_switch[]; + +int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event); +int arizona_out_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event); +int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event); +int arizona_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event); + +int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int arizona_clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event); +int arizona_set_sysclk(struct snd_soc_component *component, int clk_id, int source, + unsigned int freq, int dir); + +extern const struct snd_soc_dai_ops arizona_dai_ops; +extern const struct snd_soc_dai_ops arizona_simple_dai_ops; + +#define ARIZONA_FLL_NAME_LEN 20 + +struct arizona_fll { + struct arizona *arizona; + int id; + unsigned int base; + unsigned int vco_mult; + + unsigned int fout; + int sync_src; + unsigned int sync_freq; + int ref_src; + unsigned int ref_freq; + + char lock_name[ARIZONA_FLL_NAME_LEN]; + char clock_ok_name[ARIZONA_FLL_NAME_LEN]; +}; + +int arizona_dvfs_up(struct snd_soc_component *component, unsigned int flags); +int arizona_dvfs_down(struct snd_soc_component *component, unsigned int flags); +int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +void arizona_init_dvfs(struct arizona_priv *priv); + +int arizona_init_fll(struct arizona *arizona, int id, int base, + int lock_irq, int ok_irq, struct arizona_fll *fll); +int arizona_set_fll_refclk(struct arizona_fll *fll, int source, + unsigned int Fref, unsigned int Fout); +int arizona_set_fll(struct arizona_fll *fll, int source, + unsigned int Fref, unsigned int Fout); + +int arizona_init_spk(struct snd_soc_component *component); +int arizona_init_gpio(struct snd_soc_component *component); +int arizona_init_mono(struct snd_soc_component *component); + +int arizona_init_common(struct arizona *arizona); +int arizona_init_vol_limit(struct arizona *arizona); + +int arizona_init_spk_irqs(struct arizona *arizona); +int arizona_free_spk_irqs(struct arizona *arizona); + +int arizona_init_dai(struct arizona_priv *priv, int id); + +int arizona_set_output_mode(struct snd_soc_component *component, int output, + bool diff); + +bool arizona_input_analog(struct snd_soc_component *component, int shift); + +const char *arizona_sample_rate_val_to_name(unsigned int rate_val); + +static inline int arizona_register_notifier(struct snd_soc_component *component, + struct notifier_block *nb, + int (*notify) + (struct notifier_block *nb, + unsigned long action, void *data)) +{ + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + + nb->notifier_call = notify; + + return blocking_notifier_chain_register(&arizona->notifier, nb); +} + +static inline int arizona_unregister_notifier(struct snd_soc_component *component, + struct notifier_block *nb) +{ + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + + return blocking_notifier_chain_unregister(&arizona->notifier, nb); +} + +int arizona_of_get_audio_pdata(struct arizona *arizona); + +int arizona_jack_codec_dev_probe(struct arizona_priv *info, struct device *dev); +int arizona_jack_codec_dev_remove(struct arizona_priv *info); + +int arizona_jack_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data); + +#endif diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c new file mode 100644 index 0000000000..a8bf14239b --- /dev/null +++ b/sound/soc/codecs/audio-iio-aux.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// ALSA SoC glue to use IIO devices as audio components +// +// Copyright 2023 CS GROUP France +// +// Author: Herve Codina + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct audio_iio_aux_chan { + struct iio_channel *iio_chan; + const char *name; + int max; + int min; + bool is_invert_range; +}; + +struct audio_iio_aux { + struct device *dev; + struct audio_iio_aux_chan *chans; + unsigned int num_chans; +}; + +static int audio_iio_aux_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value; + + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = chan->max - chan->min; + uinfo->type = (uinfo->value.integer.max == 1) ? + SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + return 0; +} + +static int audio_iio_aux_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value; + int max = chan->max; + int min = chan->min; + bool invert_range = chan->is_invert_range; + int ret; + int val; + + ret = iio_read_channel_raw(chan->iio_chan, &val); + if (ret < 0) + return ret; + + ucontrol->value.integer.value[0] = val - min; + if (invert_range) + ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; + + return 0; +} + +static int audio_iio_aux_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct audio_iio_aux_chan *chan = (struct audio_iio_aux_chan *)kcontrol->private_value; + int max = chan->max; + int min = chan->min; + bool invert_range = chan->is_invert_range; + int val; + int ret; + int tmp; + + val = ucontrol->value.integer.value[0]; + if (val < 0) + return -EINVAL; + if (val > max - min) + return -EINVAL; + + val = val + min; + if (invert_range) + val = max - val; + + ret = iio_read_channel_raw(chan->iio_chan, &tmp); + if (ret < 0) + return ret; + + if (tmp == val) + return 0; + + ret = iio_write_channel_raw(chan->iio_chan, val); + if (ret) + return ret; + + return 1; /* The value changed */ +} + +static int audio_iio_aux_add_controls(struct snd_soc_component *component, + struct audio_iio_aux_chan *chan) +{ + struct snd_kcontrol_new control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = chan->name, + .info = audio_iio_aux_info_volsw, + .get = audio_iio_aux_get_volsw, + .put = audio_iio_aux_put_volsw, + .private_value = (unsigned long)chan, + }; + + return snd_soc_add_component_controls(component, &control, 1); +} + +/* + * These data could be on stack but they are pretty big. + * As ASoC internally copy them and protect them against concurrent accesses + * (snd_soc_bind_card() protects using client_mutex), keep them in the global + * data area. + */ +static struct snd_soc_dapm_widget widgets[3]; +static struct snd_soc_dapm_route routes[2]; + +/* Be sure sizes are correct (need 3 widgets and 2 routes) */ +static_assert(ARRAY_SIZE(widgets) >= 3, "3 widgets are needed"); +static_assert(ARRAY_SIZE(routes) >= 2, "2 routes are needed"); + +static int audio_iio_aux_add_dapms(struct snd_soc_component *component, + struct audio_iio_aux_chan *chan) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + char *output_name; + char *input_name; + char *pga_name; + int ret; + + input_name = kasprintf(GFP_KERNEL, "%s IN", chan->name); + if (!input_name) + return -ENOMEM; + + output_name = kasprintf(GFP_KERNEL, "%s OUT", chan->name); + if (!output_name) { + ret = -ENOMEM; + goto out_free_input_name; + } + + pga_name = kasprintf(GFP_KERNEL, "%s PGA", chan->name); + if (!pga_name) { + ret = -ENOMEM; + goto out_free_output_name; + } + + widgets[0] = SND_SOC_DAPM_INPUT(input_name); + widgets[1] = SND_SOC_DAPM_OUTPUT(output_name); + widgets[2] = SND_SOC_DAPM_PGA(pga_name, SND_SOC_NOPM, 0, 0, NULL, 0); + ret = snd_soc_dapm_new_controls(dapm, widgets, 3); + if (ret) + goto out_free_pga_name; + + routes[0].sink = pga_name; + routes[0].control = NULL; + routes[0].source = input_name; + routes[1].sink = output_name; + routes[1].control = NULL; + routes[1].source = pga_name; + ret = snd_soc_dapm_add_routes(dapm, routes, 2); + + /* Allocated names are no more needed (duplicated in ASoC internals) */ + +out_free_pga_name: + kfree(pga_name); +out_free_output_name: + kfree(output_name); +out_free_input_name: + kfree(input_name); + return ret; +} + +static int audio_iio_aux_component_probe(struct snd_soc_component *component) +{ + struct audio_iio_aux *iio_aux = snd_soc_component_get_drvdata(component); + struct audio_iio_aux_chan *chan; + int ret; + int i; + + for (i = 0; i < iio_aux->num_chans; i++) { + chan = iio_aux->chans + i; + + ret = iio_read_max_channel_raw(chan->iio_chan, &chan->max); + if (ret) + return dev_err_probe(component->dev, ret, + "chan[%d] %s: Cannot get max raw value\n", + i, chan->name); + + ret = iio_read_min_channel_raw(chan->iio_chan, &chan->min); + if (ret) + return dev_err_probe(component->dev, ret, + "chan[%d] %s: Cannot get min raw value\n", + i, chan->name); + + if (chan->min > chan->max) { + /* + * This should never happen but to avoid any check + * later, just swap values here to ensure that the + * minimum value is lower than the maximum value. + */ + dev_dbg(component->dev, "chan[%d] %s: Swap min and max\n", + i, chan->name); + swap(chan->min, chan->max); + } + + /* Set initial value */ + ret = iio_write_channel_raw(chan->iio_chan, + chan->is_invert_range ? chan->max : chan->min); + if (ret) + return dev_err_probe(component->dev, ret, + "chan[%d] %s: Cannot set initial value\n", + i, chan->name); + + ret = audio_iio_aux_add_controls(component, chan); + if (ret) + return ret; + + ret = audio_iio_aux_add_dapms(component, chan); + if (ret) + return ret; + + dev_dbg(component->dev, "chan[%d]: Added %s (min=%d, max=%d, invert=%s)\n", + i, chan->name, chan->min, chan->max, + str_on_off(chan->is_invert_range)); + } + + return 0; +} + +static const struct snd_soc_component_driver audio_iio_aux_component_driver = { + .probe = audio_iio_aux_component_probe, +}; + +static int audio_iio_aux_probe(struct platform_device *pdev) +{ + struct audio_iio_aux_chan *iio_aux_chan; + struct device *dev = &pdev->dev; + struct audio_iio_aux *iio_aux; + const char **names; + u32 *invert_ranges; + int count; + int ret; + int i; + + iio_aux = devm_kzalloc(dev, sizeof(*iio_aux), GFP_KERNEL); + if (!iio_aux) + return -ENOMEM; + + iio_aux->dev = dev; + + count = device_property_string_array_count(dev, "io-channel-names"); + if (count < 0) + return dev_err_probe(dev, count, "failed to count io-channel-names\n"); + + iio_aux->num_chans = count; + + iio_aux->chans = devm_kmalloc_array(dev, iio_aux->num_chans, + sizeof(*iio_aux->chans), GFP_KERNEL); + if (!iio_aux->chans) + return -ENOMEM; + + names = kcalloc(iio_aux->num_chans, sizeof(*names), GFP_KERNEL); + if (!names) + return -ENOMEM; + + invert_ranges = kcalloc(iio_aux->num_chans, sizeof(*invert_ranges), GFP_KERNEL); + if (!invert_ranges) { + ret = -ENOMEM; + goto out_free_names; + } + + ret = device_property_read_string_array(dev, "io-channel-names", + names, iio_aux->num_chans); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to read io-channel-names\n"); + goto out_free_invert_ranges; + } + + /* + * snd-control-invert-range is optional and can contain fewer items + * than the number of channels. Unset values default to 0. + */ + count = device_property_count_u32(dev, "snd-control-invert-range"); + if (count > 0) { + count = min_t(unsigned int, count, iio_aux->num_chans); + ret = device_property_read_u32_array(dev, "snd-control-invert-range", + invert_ranges, count); + if (ret < 0) { + dev_err_probe(dev, ret, "failed to read snd-control-invert-range\n"); + goto out_free_invert_ranges; + } + } + + for (i = 0; i < iio_aux->num_chans; i++) { + iio_aux_chan = iio_aux->chans + i; + iio_aux_chan->name = names[i]; + iio_aux_chan->is_invert_range = invert_ranges[i]; + + iio_aux_chan->iio_chan = devm_iio_channel_get(dev, iio_aux_chan->name); + if (IS_ERR(iio_aux_chan->iio_chan)) { + ret = PTR_ERR(iio_aux_chan->iio_chan); + dev_err_probe(dev, ret, "get IIO channel '%s' failed\n", + iio_aux_chan->name); + goto out_free_invert_ranges; + } + } + + platform_set_drvdata(pdev, iio_aux); + + ret = devm_snd_soc_register_component(dev, &audio_iio_aux_component_driver, + NULL, 0); +out_free_invert_ranges: + kfree(invert_ranges); +out_free_names: + kfree(names); + return ret; +} + +static const struct of_device_id audio_iio_aux_ids[] = { + { .compatible = "audio-iio-aux" }, + { } +}; +MODULE_DEVICE_TABLE(of, audio_iio_aux_ids); + +static struct platform_driver audio_iio_aux_driver = { + .driver = { + .name = "audio-iio-aux", + .of_match_table = audio_iio_aux_ids, + }, + .probe = audio_iio_aux_probe, +}; +module_platform_driver(audio_iio_aux_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("IIO ALSA SoC aux driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/aw8738.c b/sound/soc/codecs/aw8738.c new file mode 100644 index 0000000000..0fe8af1603 --- /dev/null +++ b/sound/soc/codecs/aw8738.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include + +struct aw8738_priv { + struct gpio_desc *gpiod_mode; + unsigned int mode; +}; + +static int aw8738_drv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct aw8738_priv *aw = snd_soc_component_get_drvdata(c); + int i; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + for (i = 0; i < aw->mode; i++) { + gpiod_set_value_cansleep(aw->gpiod_mode, 0); + udelay(2); + gpiod_set_value_cansleep(aw->gpiod_mode, 1); + udelay(2); + } + msleep(40); + break; + case SND_SOC_DAPM_PRE_PMD: + gpiod_set_value_cansleep(aw->gpiod_mode, 0); + usleep_range(1000, 2000); + break; + default: + WARN(1, "Unexpected event"); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_widget aw8738_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, aw8738_drv_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route aw8738_dapm_routes[] = { + { "DRV", NULL, "IN" }, + { "OUT", NULL, "DRV" }, +}; + +static const struct snd_soc_component_driver aw8738_component_driver = { + .dapm_widgets = aw8738_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aw8738_dapm_widgets), + .dapm_routes = aw8738_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aw8738_dapm_routes), +}; + +static int aw8738_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct aw8738_priv *aw; + int ret; + + aw = devm_kzalloc(dev, sizeof(*aw), GFP_KERNEL); + if (!aw) + return -ENOMEM; + platform_set_drvdata(pdev, aw); + + aw->gpiod_mode = devm_gpiod_get(dev, "mode", GPIOD_OUT_LOW); + if (IS_ERR(aw->gpiod_mode)) + return dev_err_probe(dev, PTR_ERR(aw->gpiod_mode), + "Failed to get 'mode' gpio"); + + ret = device_property_read_u32(dev, "awinic,mode", &aw->mode); + if (ret) + return -EINVAL; + + return devm_snd_soc_register_component(&pdev->dev, + &aw8738_component_driver, + NULL, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id aw8738_of_match[] = { + { .compatible = "awinic,aw8738" }, + { } +}; +MODULE_DEVICE_TABLE(of, aw8738_of_match); +#endif + +static struct platform_driver aw8738_driver = { + .probe = aw8738_probe, + .driver = { + .name = "aw8738", + .of_match_table = of_match_ptr(aw8738_of_match), + }, +}; +module_platform_driver(aw8738_driver); + +MODULE_DESCRIPTION("Awinic AW8738 Amplifier Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c new file mode 100644 index 0000000000..a697b5006b --- /dev/null +++ b/sound/soc/codecs/aw88261.c @@ -0,0 +1,1300 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88261.c -- AW88261 ALSA SoC Audio driver +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Jimmy Zhang +// Author: Weidong Wang +// + +#include +#include +#include +#include +#include +#include "aw88261.h" +#include "aw88395/aw88395_data_type.h" +#include "aw88395/aw88395_device.h" + +static const struct regmap_config aw88261_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88261_REG_MAX - 1, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int real_value, volume; + unsigned int reg_value; + + volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL); + real_value = DB_TO_REG_VAL(volume); + + regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, ®_value); + + real_value = (real_value | (reg_value & AW88261_VOL_START_MASK)); + + dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value); + + regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value); +} + +static void aw88261_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_in_vol = desc->ctl_volume; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw88261_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw88261_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, + aw_dev->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw88261_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw88261_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) { + aw88261_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i != AW88261_MUTE_VOL) { + aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + if (flag) + regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG, + ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG, + ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE); +} + +static void aw88261_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + if (pwd) + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_PWDN_MASK, AW88261_PWDN_POWER_DOWN_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_PWDN_MASK, AW88261_PWDN_WORKING_VALUE); +} + +static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + if (amppd) + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_AMPPD_MASK, AW88261_AMPPD_POWER_DOWN_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_AMPPD_MASK, AW88261_AMPPD_WORKING_VALUE); +} + +static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + if (is_mute) { + aw88261_dev_fade_out(aw_dev); + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE); + } else { + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE); + aw88261_dev_fade_in(aw_dev); + } +} + +static void aw88261_dev_clear_int_status(struct aw_device *aw_dev) +{ + unsigned int int_status; + + /* read int status and clear */ + regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status); + /* make sure int status is clear */ + regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status); + + dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status); +} + +static int aw88261_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, ®_val); + if (ret) + return ret; + if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) { + dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return ret; +} + +static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = aw88261_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + return ret; + } + } + + return -EPERM; +} + +static int aw88261_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, ®_val); + if (ret) + return ret; + + reg_val &= (~AW88261_CCO_MUX_MASK); + if (reg_val == AW88261_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG, + ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = aw88261_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG, + ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE); + if (ret == 0) { + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = aw88261_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +static int aw88261_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret = aw88261_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw88261_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return ret; +} + +static int aw88261_dev_check_sysst(struct aw_device *aw_dev) +{ + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, ®_val); + if (ret) + return ret; + + check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK) + & AW88261_BIT_SYSST_CHECK; + if (check_val != AW88261_BIT_SYSST_CHECK) { + dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x", + reg_val, AW88261_BIT_SYSST_CHECK); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static void aw88261_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute) +{ + if (uls_hmute) + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_ULS_HMUTE_MASK, + AW88261_ULS_HMUTE_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_ULS_HMUTE_MASK, + AW88261_ULS_HMUTE_DISABLE_VALUE); +} + +static void aw88261_reg_force_set(struct aw88261 *aw88261) +{ + if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) { + /* set FORCE_PWM */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG, + AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); + /* set BOOST_OS_WIDTH */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG, + AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); + /* set BURST_LOOPR */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG, + AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); + /* set RSQN_DLY */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG, + AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); + /* set BURST_SSMODE */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG, + AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); + /* set BST_BURST */ + regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG, + AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); + } else { + dev_dbg(aw88261->aw_pa->dev, "needn't set reg value"); + } +} + +static int aw88261_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + u16 reg_icalk, reg_icalkl; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88261_EFRH4_REG, ®_val); + if (ret) + return ret; + + reg_icalk = reg_val & (~AW88261_EF_ISN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88261_EFRL4_REG, ®_val); + if (ret) + return ret; + + reg_icalkl = reg_val & (~AW88261_EF_ISN_GESLP_L_MASK); + + reg_icalk = (reg_icalk >> AW88261_ICALK_SHIFT) & (reg_icalkl >> AW88261_ICALKL_SHIFT); + + if (reg_icalk & (~AW88261_EF_ISN_GESLP_SIGN_MASK)) + reg_icalk = reg_icalk | ~AW88261_EF_ISN_GESLP_NEG; + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw88261_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + u16 reg_vcalk, reg_vcalkl; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88261_EFRH3_REG, ®_val); + if (ret) + return ret; + + reg_vcalk = (u16)reg_val & (~AW88261_EF_VSN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88261_EFRL3_REG, ®_val); + if (ret) + return ret; + + reg_vcalkl = (u16)reg_val & (~AW88261_EF_VSN_GESLP_L_MASK); + + reg_vcalk = (reg_vcalk >> AW88261_VCALK_SHIFT) & (reg_vcalkl >> AW88261_VCALKL_SHIFT); + + if (reg_vcalk & AW88261_EF_VSN_GESLP_SIGN_MASK) + reg_vcalk = reg_vcalk | (~AW88261_EF_VSN_GESLP_NEG); + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw88261_dev_set_vcalb(struct aw_device *aw_dev) +{ + int16_t icalk_val, vcalk_val; + int icalk, vcalk, vcalb; + u32 reg_val; + int ret; + + ret = aw88261_dev_get_icalk(aw_dev, &icalk_val); + if (ret) + return ret; + + ret = aw88261_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret) + return ret; + + icalk = AW88261_CABL_BASE_VALUE + AW88261_ICABLK_FACTOR * icalk_val; + vcalk = AW88261_CABL_BASE_VALUE + AW88261_VCABLK_FACTOR * vcalk_val; + if (!vcalk) + return -EINVAL; + + vcalb = AW88261_VCAL_FACTOR * icalk / vcalk; + reg_val = (unsigned int)vcalb; + + dev_dbg(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x", + icalk, vcalk, vcalb, reg_val); + ret = regmap_write(aw_dev->regmap, AW88261_VSNTM1_REG, reg_val); + + return ret; +} + +static int aw88261_dev_reg_update(struct aw88261 *aw88261, + unsigned char *data, unsigned int len) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int read_val, efcheck_val, read_vol; + int data_len, i, ret; + int16_t *reg_data; + u16 reg_val; + u8 reg_addr; + + if (!len || !data) { + dev_err(aw_dev->dev, "reg data is null or len is 0"); + return -EINVAL; + } + + reg_data = (int16_t *)data; + data_len = len >> 1; + + if (data_len & 0x1) { + dev_err(aw_dev->dev, "data len:%d unsupported", data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + reg_addr = reg_data[i]; + reg_val = reg_data[i + 1]; + + if (reg_addr == AW88261_SYSCTRL_REG) { + aw88261->amppd_st = reg_val & (~AW88261_AMPPD_MASK); + ret = regmap_read(aw_dev->regmap, reg_addr, &read_val); + if (ret) + break; + + read_val &= (~AW88261_AMPPD_MASK) | (~AW88261_PWDN_MASK) | + (~AW88261_HMUTE_MASK); + reg_val &= (AW88261_AMPPD_MASK | AW88261_PWDN_MASK | AW88261_HMUTE_MASK); + reg_val |= read_val; + + /* enable uls hmute */ + reg_val &= AW88261_ULS_HMUTE_MASK; + reg_val |= AW88261_ULS_HMUTE_ENABLE_VALUE; + } + + if (reg_addr == AW88261_DBGCTRL_REG) { + efcheck_val = reg_val & (~AW88261_EF_DBMD_MASK); + if (efcheck_val == AW88261_OR_VALUE) + aw88261->efuse_check = AW88261_EF_OR_CHECK; + else + aw88261->efuse_check = AW88261_EF_AND_CHECK; + } + + /* i2stxen */ + if (reg_addr == AW88261_I2SCTRL3_REG) { + /* close tx */ + reg_val &= AW88261_I2STXEN_MASK; + reg_val |= AW88261_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr == AW88261_SYSCTRL2_REG) { + read_vol = (reg_val & (~AW88261_VOL_MASK)) >> + AW88261_VOL_START_BIT; + aw_dev->volume_desc.init_volume = + REG_VAL_TO_DB(read_vol); + } + + if (reg_addr == AW88261_VSNTM1_REG) + continue; + + ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + break; + } + + ret = aw88261_dev_set_vcalb(aw_dev); + if (ret) + return ret; + + if (aw_dev->prof_cur != aw_dev->prof_index) + vol_desc->ctl_volume = 0; + + /* keep min volume */ + aw88261_dev_set_volume(aw_dev, vol_desc->mute_volume); + + return ret; +} + +static char *aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return prof_info->prof_name_list[prof_desc->id]; +} + +static int aw88261_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} + +static int aw88261_dev_fw_update(struct aw88261 *aw88261) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + prof_name = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index); + if (!prof_name) { + dev_err(aw_dev->dev, "get prof name failed"); + return -EINVAL; + } + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw88261_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) + return ret; + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw88261_dev_reg_update(aw88261, sec_desc[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw_dev->prof_cur = aw_dev->prof_index; + + return ret; +} + +static int aw88261_dev_start(struct aw88261 *aw88261) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + int ret; + + if (aw_dev->status == AW88261_DEV_PW_ON) { + dev_info(aw_dev->dev, "already power on"); + return 0; + } + + /* power on */ + aw88261_dev_pwd(aw_dev, false); + usleep_range(AW88261_2000_US, AW88261_2000_US + 10); + + ret = aw88261_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start"); + goto pll_check_fail; + } + + /* amppd on */ + aw88261_dev_amppd(aw_dev, false); + usleep_range(AW88261_1000_US, AW88261_1000_US + 50); + + /* check i2s status */ + ret = aw88261_dev_check_sysst(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "sysst check failed"); + goto sysst_check_fail; + } + + /* enable tx feedback */ + aw88261_dev_i2s_tx_enable(aw_dev, true); + + if (aw88261->amppd_st) + aw88261_dev_amppd(aw_dev, true); + + aw88261_reg_force_set(aw88261); + + /* close uls mute */ + aw88261_dev_uls_hmute(aw_dev, false); + + /* close mute */ + if (!aw88261->mute_st) + aw88261_dev_mute(aw_dev, false); + + /* clear inturrupt */ + aw88261_dev_clear_int_status(aw_dev); + aw_dev->status = AW88261_DEV_PW_ON; + + return 0; + +sysst_check_fail: + aw88261_dev_i2s_tx_enable(aw_dev, false); + aw88261_dev_clear_int_status(aw_dev); + aw88261_dev_amppd(aw_dev, true); +pll_check_fail: + aw88261_dev_pwd(aw_dev, true); + aw_dev->status = AW88261_DEV_PW_OFF; + + return ret; +} + +static int aw88261_dev_stop(struct aw_device *aw_dev) +{ + if (aw_dev->status == AW88261_DEV_PW_OFF) { + dev_info(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88261_DEV_PW_OFF; + + /* clear inturrupt */ + aw88261_dev_clear_int_status(aw_dev); + + aw88261_dev_uls_hmute(aw_dev, true); + /* set mute */ + aw88261_dev_mute(aw_dev, true); + + /* close tx feedback */ + aw88261_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88261_1000_US, AW88261_1000_US + 100); + + /* enable amppd */ + aw88261_dev_amppd(aw_dev, true); + + /* set power down */ + aw88261_dev_pwd(aw_dev, true); + + return 0; +} + +static int aw88261_reg_update(struct aw88261 *aw88261, bool force) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + int ret; + + if (force) { + ret = regmap_write(aw_dev->regmap, + AW88261_ID_REG, AW88261_SOFT_RESET_VALUE); + if (ret) + return ret; + + ret = aw88261_dev_fw_update(aw88261); + if (ret) + return ret; + } else { + if (aw_dev->prof_cur != aw_dev->prof_index) { + ret = aw88261_dev_fw_update(aw88261); + if (ret) + return ret; + } else { + ret = 0; + } + } + + aw_dev->prof_cur = aw_dev->prof_index; + + return ret; +} + +static void aw88261_start_pa(struct aw88261 *aw88261) +{ + int ret, i; + + for (i = 0; i < AW88261_START_RETRIES; i++) { + ret = aw88261_reg_update(aw88261, aw88261->phase_sync); + if (ret) { + dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i); + continue; + } + ret = aw88261_dev_start(aw88261); + if (ret) { + dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i); + continue; + } else { + dev_info(aw88261->aw_pa->dev, "start success\n"); + break; + } + } +} + +static void aw88261_startup_work(struct work_struct *work) +{ + struct aw88261 *aw88261 = + container_of(work, struct aw88261, start_work.work); + + mutex_lock(&aw88261->lock); + aw88261_start_pa(aw88261); + mutex_unlock(&aw88261->lock); +} + +static void aw88261_start(struct aw88261 *aw88261, bool sync_start) +{ + if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK) + return; + + if (aw88261->aw_pa->status == AW88261_DEV_PW_ON) + return; + + if (sync_start == AW88261_SYNC_START) + aw88261_start_pa(aw88261); + else + queue_delayed_work(system_wq, + &aw88261->start_work, + AW88261_START_WORK_DELAY_MS); +} + +static struct snd_soc_dai_driver aw88261_dai[] = { + { + .name = "aw88261-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW88261_RATES, + .formats = AW88261_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW88261_RATES, + .formats = AW88261_FORMATS, + }, + }, +}; + +static int aw88261_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88261->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_in_time; + + return 0; +} + +static int aw88261_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88261->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_in_time) { + aw_dev->fade_in_time = time; + return 1; + } + + return 0; +} + +static int aw88261_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88261->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_out_time; + + return 0; +} + +static int aw88261_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88261->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_out_time) { + aw_dev->fade_out_time = time; + return 1; + } + + return 0; +} + +static int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + /* check the index whether is valid */ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + /* check the index whether change */ + if (aw_dev->prof_index == index) + return -EPERM; + + aw_dev->prof_index = index; + + return 0; +} + +static int aw88261_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + const char *prof_name; + char *name; + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw88261->aw_pa->prof_info.count; + if (count <= 0) { + uinfo->value.enumerated.items = 0; + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + + prof_name = aw88261_dev_get_prof_name(aw88261->aw_pa, count); + if (!prof_name) { + strscpy(uinfo->value.enumerated.name, "null", + strlen("null") + 1); + return 0; + } + + strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw88261_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88261->aw_pa->prof_index; + + return 0; +} + +static int aw88261_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + int ret; + + /* pa stop or stopping just set profile */ + mutex_lock(&aw88261->lock); + ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change"); + mutex_unlock(&aw88261->lock); + return 0; + } + + if (aw88261->aw_pa->status) { + aw88261_dev_stop(aw88261->aw_pa); + aw88261_start(aw88261, AW88261_SYNC_START); + } + + mutex_unlock(&aw88261->lock); + + return 1; +} + +static int aw88261_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc; + + ucontrol->value.integer.value[0] = vol_desc->ctl_volume; + + return 0; +} + +static int aw88261_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (vol_desc->ctl_volume != value) { + vol_desc->ctl_volume = value; + aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume); + + return 1; + } + + return 0; +} + +static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step; + + return 0; +} + +static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw88261->aw_pa->fade_step != value) { + aw88261->aw_pa->fade_step = value; + return 1; + } + + return 0; +} + +static const struct snd_kcontrol_new aw88261_controls[] = { + SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG, + 6, AW88261_MUTE_VOL, 0, aw88261_volume_get, + aw88261_volume_set), + SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0, + aw88261_get_fade_step, aw88261_set_fade_step), + SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88261_get_fade_in_time, aw88261_set_fade_in_time), + SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88261_get_fade_out_time, aw88261_set_fade_out_time), + AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info, + aw88261_profile_get, aw88261_profile_set), +}; + +static int aw88261_playback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + + mutex_lock(&aw88261->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88261_start(aw88261, AW88261_ASYNC_START); + break; + case SND_SOC_DAPM_POST_PMD: + aw88261_dev_stop(aw88261->aw_pa); + break; + default: + break; + } + mutex_unlock(&aw88261->lock); + + return 0; +} + +static const struct snd_soc_dapm_widget aw88261_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0, + aw88261_playback_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("DAC Output"), + + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("ADC Input"), +}; + +static const struct snd_soc_dapm_route aw88261_audio_map[] = { + {"DAC Output", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "ADC Input"}, +}; + +static int aw88261_frcset_check(struct aw88261 *aw88261) +{ + unsigned int reg_val; + u16 temh, teml, tem; + int ret; + + ret = regmap_read(aw88261->regmap, AW88261_EFRH3_REG, ®_val); + if (ret) + return ret; + temh = ((u16)reg_val & (~AW88261_TEMH_MASK)); + + ret = regmap_read(aw88261->regmap, AW88261_EFRL3_REG, ®_val); + if (ret) + return ret; + teml = ((u16)reg_val & (~AW88261_TEML_MASK)); + + if (aw88261->efuse_check == AW88261_EF_OR_CHECK) + tem = (temh | teml); + else + tem = (temh & teml); + + if (tem == AW88261_DEFAULT_CFG) + aw88261->frcset_en = AW88261_FRCSET_ENABLE; + else + aw88261->frcset_en = AW88261_FRCSET_DISABLE; + + dev_dbg(aw88261->aw_pa->dev, "tem is 0x%04x, frcset_en is %d", + tem, aw88261->frcset_en); + + return ret; +} + +static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + int ret; + + ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + + ret = regmap_write(aw_dev->regmap, AW88261_ID_REG, AW88261_SOFT_RESET_VALUE); + if (ret) + return ret; + + aw_dev->fade_in_time = AW88261_500_US; + aw_dev->fade_out_time = AW88261_500_US; + aw_dev->prof_cur = AW88261_INIT_PROFILE; + aw_dev->prof_index = AW88261_INIT_PROFILE; + + ret = aw88261_dev_fw_update(aw88261); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); + return ret; + } + + ret = aw88261_frcset_check(aw88261); + if (ret) { + dev_err(aw_dev->dev, "aw88261_frcset_check ret = %d\n", ret); + return ret; + } + + aw88261_dev_clear_int_status(aw_dev); + + aw88261_dev_uls_hmute(aw_dev, true); + + aw88261_dev_mute(aw_dev, true); + + aw88261_dev_i2s_tx_enable(aw_dev, false); + + usleep_range(AW88261_1000_US, AW88261_1000_US + 100); + + aw88261_dev_amppd(aw_dev, true); + + aw88261_dev_pwd(aw_dev, true); + + return 0; +} + +static int aw88261_request_firmware_file(struct aw88261 *aw88261) +{ + const struct firmware *cont = NULL; + int ret; + + aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev); + if (ret) + return dev_err_probe(aw88261->aw_pa->dev, ret, + "load [%s] failed!", AW88261_ACF_FILE); + + dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n", + AW88261_ACF_FILE, cont ? cont->size : 0); + + aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); + if (!aw88261->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw88261->aw_cfg->len = (int)cont->size; + memcpy(aw88261->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg); + if (ret) { + dev_err(aw88261->aw_pa->dev, "load [%s] failed !", AW88261_ACF_FILE); + return ret; + } + + mutex_lock(&aw88261->lock); + /* aw device init */ + ret = aw88261_dev_init(aw88261, aw88261->aw_cfg); + if (ret) + dev_err(aw88261->aw_pa->dev, "dev init failed"); + mutex_unlock(&aw88261->lock); + + return ret; +} + +static int aw88261_codec_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); + int ret; + + INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work); + + ret = aw88261_request_firmware_file(aw88261); + if (ret) + return dev_err_probe(aw88261->aw_pa->dev, ret, + "aw88261_request_firmware_file failed\n"); + + /* add widgets */ + ret = snd_soc_dapm_new_controls(dapm, aw88261_dapm_widgets, + ARRAY_SIZE(aw88261_dapm_widgets)); + if (ret) + return ret; + + /* add route */ + ret = snd_soc_dapm_add_routes(dapm, aw88261_audio_map, + ARRAY_SIZE(aw88261_audio_map)); + if (ret) + return ret; + + ret = snd_soc_add_component_controls(component, aw88261_controls, + ARRAY_SIZE(aw88261_controls)); + + return ret; +} + +static void aw88261_codec_remove(struct snd_soc_component *aw_codec) +{ + struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec); + + cancel_delayed_work_sync(&aw88261->start_work); +} + +static const struct snd_soc_component_driver soc_codec_dev_aw88261 = { + .probe = aw88261_codec_probe, + .remove = aw88261_codec_remove, +}; + +static void aw88261_hw_reset(struct aw88261 *aw88261) +{ + gpiod_set_value_cansleep(aw88261->reset_gpio, 0); + usleep_range(AW88261_1000_US, AW88261_1000_US + 10); + gpiod_set_value_cansleep(aw88261->reset_gpio, 1); + usleep_range(AW88261_1000_US, AW88261_1000_US + 10); +} + +static void aw88261_parse_channel_dt(struct aw88261 *aw88261) +{ + struct aw_device *aw_dev = aw88261->aw_pa; + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value = AW88261_DEV_DEFAULT_CH; + u32 sync_enable = false; + + of_property_read_u32(np, "sound-channel", &channel_value); + of_property_read_u32(np, "sync-flag", &sync_enable); + + aw_dev->channel = channel_value; + aw88261->phase_sync = sync_enable; +} + +static int aw88261_init(struct aw88261 **aw88261, struct i2c_client *i2c, struct regmap *regmap) +{ + struct aw_device *aw_dev; + unsigned int chip_id; + int ret; + + /* read chip id */ + ret = regmap_read(regmap, AW88261_ID_REG, &chip_id); + if (ret) { + dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret); + return ret; + } + if (chip_id != AW88261_CHIP_ID) { + dev_err(&i2c->dev, "unsupported device"); + return -ENXIO; + } + + dev_info(&i2c->dev, "chip id = %x\n", chip_id); + + aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); + if (!aw_dev) + return -ENOMEM; + + (*aw88261)->aw_pa = aw_dev; + aw_dev->i2c = i2c; + aw_dev->regmap = regmap; + aw_dev->dev = &i2c->dev; + aw_dev->chip_id = AW88261_CHIP_ID; + aw_dev->acf = NULL; + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = 0; + aw_dev->fw_status = AW88261_DEV_FW_FAILED; + aw_dev->fade_step = AW88261_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE; + aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL; + aw88261_parse_channel_dt(*aw88261); + + return ret; +} + +static int aw88261_i2c_probe(struct i2c_client *i2c) +{ + struct aw88261 *aw88261; + int ret; + + ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C); + if (!ret) + return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed"); + + aw88261 = devm_kzalloc(&i2c->dev, sizeof(*aw88261), GFP_KERNEL); + if (!aw88261) + return -ENOMEM; + + mutex_init(&aw88261->lock); + + i2c_set_clientdata(i2c, aw88261); + + aw88261->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(aw88261->reset_gpio)) + dev_info(&i2c->dev, "reset gpio not defined\n"); + else + aw88261_hw_reset(aw88261); + + aw88261->regmap = devm_regmap_init_i2c(i2c, &aw88261_remap_config); + if (IS_ERR(aw88261->regmap)) { + ret = PTR_ERR(aw88261->regmap); + return dev_err_probe(&i2c->dev, ret, "failed to init regmap: %d\n", ret); + } + + /* aw pa init */ + ret = aw88261_init(&aw88261, i2c, aw88261->regmap); + if (ret) + return ret; + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw88261, + aw88261_dai, ARRAY_SIZE(aw88261_dai)); + if (ret) + dev_err(&i2c->dev, "failed to register aw88261: %d", ret); + + return ret; +} + +static const struct i2c_device_id aw88261_i2c_id[] = { + { AW88261_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id); + +static struct i2c_driver aw88261_i2c_driver = { + .driver = { + .name = AW88261_I2C_NAME, + }, + .probe = aw88261_i2c_probe, + .id_table = aw88261_i2c_id, +}; +module_i2c_driver(aw88261_i2c_driver); + +MODULE_DESCRIPTION("ASoC AW88261 Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h new file mode 100644 index 0000000000..4f3dbf4385 --- /dev/null +++ b/sound/soc/codecs/aw88261.h @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88261.h -- AW88261 ALSA SoC Audio driver +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Jimmy Zhang +// Author: Weidong Wang +// + +#ifndef __AW88261_H__ +#define __AW88261_H__ + +#define AW88261_ID_REG (0x00) +#define AW88261_SYSST_REG (0x01) +#define AW88261_SYSINT_REG (0x02) +#define AW88261_SYSINTM_REG (0x03) +#define AW88261_SYSCTRL_REG (0x04) +#define AW88261_SYSCTRL2_REG (0x05) +#define AW88261_I2SCTRL1_REG (0x06) +#define AW88261_I2SCTRL2_REG (0x07) +#define AW88261_I2SCTRL3_REG (0x08) +#define AW88261_DACCFG1_REG (0x09) +#define AW88261_DACCFG2_REG (0x0A) +#define AW88261_DACCFG3_REG (0x0B) +#define AW88261_DACCFG4_REG (0x0C) +#define AW88261_DACCFG5_REG (0x0D) +#define AW88261_DACCFG6_REG (0x0E) +#define AW88261_DACCFG7_REG (0x0F) +#define AW88261_DACCFG8_REG (0x10) +#define AW88261_PWMCTRL1_REG (0x11) +#define AW88261_PWMCTRL2_REG (0x12) +#define AW88261_I2SCFG1_REG (0x13) +#define AW88261_DBGCTRL_REG (0x14) +#define AW88261_DACCFG9_REG (0x15) +#define AW88261_DACCFG10_REG (0x16) +#define AW88261_DACST_REG (0x20) +#define AW88261_VBAT_REG (0x21) +#define AW88261_TEMP_REG (0x22) +#define AW88261_PVDD_REG (0x23) +#define AW88261_ISNDAT_REG (0x24) +#define AW88261_VSNDAT_REG (0x25) +#define AW88261_I2SINT_REG (0x26) +#define AW88261_I2SCAPCNT_REG (0x27) +#define AW88261_ANASTA1_REG (0x28) +#define AW88261_ANASTA2_REG (0x29) +#define AW88261_ANASTA3_REG (0x2A) +#define AW88261_TESTDET_REG (0x2B) +#define AW88261_DSMCFG1_REG (0x30) +#define AW88261_DSMCFG2_REG (0x31) +#define AW88261_DSMCFG3_REG (0x32) +#define AW88261_DSMCFG4_REG (0x33) +#define AW88261_DSMCFG5_REG (0x34) +#define AW88261_DSMCFG6_REG (0x35) +#define AW88261_DSMCFG7_REG (0x36) +#define AW88261_DSMCFG8_REG (0x37) +#define AW88261_TESTIN_REG (0x38) +#define AW88261_TESTOUT_REG (0x39) +#define AW88261_SADCCTRL1_REG (0x3A) +#define AW88261_SADCCTRL2_REG (0x3B) +#define AW88261_SADCCTRL3_REG (0x3C) +#define AW88261_SADCCTRL4_REG (0x3D) +#define AW88261_SADCCTRL5_REG (0x3E) +#define AW88261_SADCCTRL6_REG (0x3F) +#define AW88261_SADCCTRL7_REG (0x40) +#define AW88261_VSNTM1_REG (0x50) +#define AW88261_VSNTM2_REG (0x51) +#define AW88261_ISNCTRL1_REG (0x52) +#define AW88261_ISNCTRL2_REG (0x53) +#define AW88261_PLLCTRL1_REG (0x54) +#define AW88261_PLLCTRL2_REG (0x55) +#define AW88261_PLLCTRL3_REG (0x56) +#define AW88261_CDACTRL1_REG (0x57) +#define AW88261_CDACTRL2_REG (0x58) +#define AW88261_DITHERCFG1_REG (0x59) +#define AW88261_DITHERCFG2_REG (0x5A) +#define AW88261_DITHERCFG3_REG (0x5B) +#define AW88261_CPCTRL_REG (0x5C) +#define AW88261_BSTCTRL1_REG (0x60) +#define AW88261_BSTCTRL2_REG (0x61) +#define AW88261_BSTCTRL3_REG (0x62) +#define AW88261_BSTCTRL4_REG (0x63) +#define AW88261_BSTCTRL5_REG (0x64) +#define AW88261_BSTCTRL6_REG (0x65) +#define AW88261_BSTCTRL7_REG (0x66) +#define AW88261_BSTCTRL8_REG (0x67) +#define AW88261_BSTCTRL9_REG (0x68) +#define AW88261_TM_REG (0x6F) +#define AW88261_TESTCTRL1_REG (0x70) +#define AW88261_TESTCTRL2_REG (0x71) +#define AW88261_EFCTRL1_REG (0x72) +#define AW88261_EFCTRL2_REG (0x73) +#define AW88261_EFWH_REG (0x74) +#define AW88261_EFWM2_REG (0x75) +#define AW88261_EFWM1_REG (0x76) +#define AW88261_EFWL_REG (0x77) +#define AW88261_EFRH4_REG (0x78) +#define AW88261_EFRH3_REG (0x79) +#define AW88261_EFRH2_REG (0x7A) +#define AW88261_EFRH1_REG (0x7B) +#define AW88261_EFRL4_REG (0x7C) +#define AW88261_EFRL3_REG (0x7D) +#define AW88261_EFRL2_REG (0x7E) +#define AW88261_EFRL1_REG (0x7F) + +#define AW88261_REG_MAX (0x80) +#define AW88261_EF_DBMD_MASK (0xfff7) +#define AW88261_OR_VALUE (0x0008) + +#define AW88261_TEMH_MASK (0x83ff) +#define AW88261_TEML_MASK (0x83ff) +#define AW88261_DEFAULT_CFG (0x0000) + +#define AW88261_ICALK_SHIFT (0) +#define AW88261_ICALKL_SHIFT (0) +#define AW88261_VCALK_SHIFT (0) +#define AW88261_VCALKL_SHIFT (0) + +#define AW88261_AMPPD_START_BIT (1) +#define AW88261_AMPPD_BITS_LEN (1) +#define AW88261_AMPPD_MASK \ + (~(((1<> AW88261_VOL_6DB_START) * \ + AW88261_VOLUME_STEP_DB) + \ + ((value) & AW88261_REG_TO_DB)) +#define DB_TO_REG_VAL(value) ((((value) / AW88261_VOLUME_STEP_DB) << \ + AW88261_VOL_6DB_START) + \ + ((value) % AW88261_VOLUME_STEP_DB)) + +#define AW88261_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = profile_info, \ + .get = profile_get, \ + .put = profile_set, \ +} + +enum { + AW88261_SYNC_START = 0, + AW88261_ASYNC_START, +}; + +enum aw88261_id { + AW88261_CHIP_ID = 0x2113, +}; + +enum { + AW88261_500_US = 500, + AW88261_1000_US = 1000, + AW88261_2000_US = 2000, +}; + +enum { + AW88261_DEV_PW_OFF = 0, + AW88261_DEV_PW_ON, +}; + +enum { + AW88261_DEV_FW_FAILED = 0, + AW88261_DEV_FW_OK, +}; + +enum { + AW88261_EF_AND_CHECK = 0, + AW88261_EF_OR_CHECK, +}; + +enum { + AW88261_FRCSET_DISABLE = 0, + AW88261_FRCSET_ENABLE, +}; + +struct aw88261 { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; + + int efuse_check; + int frcset_en; + unsigned int mute_st; + unsigned int amppd_st; + + unsigned char phase_sync; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c new file mode 100644 index 0000000000..9dcd75dd79 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395.c -- ALSA SoC AW88395 codec support +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// Author: Weidong Wang +// + +#include +#include +#include +#include +#include +#include "aw88395.h" +#include "aw88395_device.h" +#include "aw88395_lib.h" +#include "aw88395_reg.h" + +static const struct regmap_config aw88395_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88395_REG_MAX - 1, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static void aw88395_start_pa(struct aw88395 *aw88395) +{ + int ret, i; + + for (i = 0; i < AW88395_START_RETRIES; i++) { + ret = aw88395_dev_start(aw88395->aw_pa); + if (ret) { + dev_err(aw88395->aw_pa->dev, "aw88395 device start failed. retry = %d", i); + ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_ON, true); + if (ret < 0) { + dev_err(aw88395->aw_pa->dev, "fw update failed"); + continue; + } + } else { + dev_info(aw88395->aw_pa->dev, "start success\n"); + break; + } + } +} + +static void aw88395_startup_work(struct work_struct *work) +{ + struct aw88395 *aw88395 = + container_of(work, struct aw88395, start_work.work); + + mutex_lock(&aw88395->lock); + aw88395_start_pa(aw88395); + mutex_unlock(&aw88395->lock); +} + +static void aw88395_start(struct aw88395 *aw88395, bool sync_start) +{ + int ret; + + if (aw88395->aw_pa->fw_status != AW88395_DEV_FW_OK) + return; + + if (aw88395->aw_pa->status == AW88395_DEV_PW_ON) + return; + + ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_OFF, true); + if (ret < 0) { + dev_err(aw88395->aw_pa->dev, "fw update failed."); + return; + } + + if (sync_start == AW88395_SYNC_START) + aw88395_start_pa(aw88395); + else + queue_delayed_work(system_wq, + &aw88395->start_work, + AW88395_START_WORK_DELAY_MS); +} + +static struct snd_soc_dai_driver aw88395_dai[] = { + { + .name = "aw88395-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW88395_RATES, + .formats = AW88395_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW88395_RATES, + .formats = AW88395_FORMATS, + }, + }, +}; + +static int aw88395_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88395->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_in_time; + + return 0; +} + +static int aw88395_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88395->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_in_time) { + aw_dev->fade_in_time = time; + return 1; + } + + return 0; +} + +static int aw88395_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88395->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_out_time; + + return 0; +} + +static int aw88395_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88395->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_out_time) { + aw_dev->fade_out_time = time; + return 1; + } + + return 0; +} + +static int aw88395_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + const char *prof_name; + char *name; + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw88395_dev_get_profile_count(aw88395->aw_pa); + if (count <= 0) { + uinfo->value.enumerated.items = 0; + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + + prof_name = aw88395_dev_get_prof_name(aw88395->aw_pa, count); + if (!prof_name) { + strscpy(uinfo->value.enumerated.name, "null", + strlen("null") + 1); + return 0; + } + + strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw88395_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88395_dev_get_profile_index(aw88395->aw_pa); + + return 0; +} + +static int aw88395_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + int ret; + + /* pa stop or stopping just set profile */ + mutex_lock(&aw88395->lock); + ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]); + if (ret < 0) { + dev_dbg(codec->dev, "profile index does not change"); + mutex_unlock(&aw88395->lock); + return 0; + } + + if (aw88395->aw_pa->status) { + aw88395_dev_stop(aw88395->aw_pa); + aw88395_start(aw88395, AW88395_SYNC_START); + } + + mutex_unlock(&aw88395->lock); + + return 1; +} + +static int aw88395_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc; + + ucontrol->value.integer.value[0] = vol_desc->ctl_volume; + + return 0; +} + +static int aw88395_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (vol_desc->ctl_volume != value) { + vol_desc->ctl_volume = value; + aw88395_dev_set_volume(aw88395->aw_pa, vol_desc->ctl_volume); + + return 1; + } + + return 0; +} + +static int aw88395_get_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88395->aw_pa->fade_step; + + return 0; +} + +static int aw88395_set_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw88395->aw_pa->fade_step != value) { + aw88395->aw_pa->fade_step = value; + return 1; + } + + return 0; +} + +static int aw88395_re_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw88395->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re; + + return 0; +} + +static int aw88395_re_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88395->aw_pa; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw_dev->cali_desc.cali_re != value) { + aw_dev->cali_desc.cali_re = value; + return 1; + } + + return 0; +} + +static const struct snd_kcontrol_new aw88395_controls[] = { + SOC_SINGLE_EXT("PCM Playback Volume", AW88395_SYSCTRL2_REG, + 6, AW88395_MUTE_VOL, 0, aw88395_volume_get, + aw88395_volume_set), + SOC_SINGLE_EXT("Fade Step", 0, 0, AW88395_MUTE_VOL, 0, + aw88395_get_fade_step, aw88395_set_fade_step), + SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88395_get_fade_in_time, aw88395_set_fade_in_time), + SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88395_get_fade_out_time, aw88395_set_fade_out_time), + SOC_SINGLE_EXT("Calib", 0, 0, 100, 0, + aw88395_re_get, aw88395_re_set), + AW88395_PROFILE_EXT("Profile Set", aw88395_profile_info, + aw88395_profile_get, aw88395_profile_set), +}; + +static int aw88395_playback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + + mutex_lock(&aw88395->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88395_start(aw88395, AW88395_ASYNC_START); + break; + case SND_SOC_DAPM_POST_PMD: + aw88395_dev_stop(aw88395->aw_pa); + break; + default: + break; + } + mutex_unlock(&aw88395->lock); + + return 0; +} + +static const struct snd_soc_dapm_widget aw88395_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0, + aw88395_playback_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("DAC Output"), + + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("ADC Input"), +}; + +static const struct snd_soc_dapm_route aw88395_audio_map[] = { + {"DAC Output", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "ADC Input"}, +}; + +static int aw88395_codec_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + int ret; + + INIT_DELAYED_WORK(&aw88395->start_work, aw88395_startup_work); + + /* add widgets */ + ret = snd_soc_dapm_new_controls(dapm, aw88395_dapm_widgets, + ARRAY_SIZE(aw88395_dapm_widgets)); + if (ret < 0) + return ret; + + /* add route */ + ret = snd_soc_dapm_add_routes(dapm, aw88395_audio_map, + ARRAY_SIZE(aw88395_audio_map)); + if (ret < 0) + return ret; + + ret = snd_soc_add_component_controls(component, aw88395_controls, + ARRAY_SIZE(aw88395_controls)); + + return ret; +} + +static void aw88395_codec_remove(struct snd_soc_component *aw_codec) +{ + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(aw_codec); + + cancel_delayed_work_sync(&aw88395->start_work); +} + +static const struct snd_soc_component_driver soc_codec_dev_aw88395 = { + .probe = aw88395_codec_probe, + .remove = aw88395_codec_remove, +}; + +static struct aw88395 *aw88395_malloc_init(struct i2c_client *i2c) +{ + struct aw88395 *aw88395 = devm_kzalloc(&i2c->dev, + sizeof(struct aw88395), GFP_KERNEL); + if (!aw88395) + return NULL; + + mutex_init(&aw88395->lock); + + return aw88395; +} + +static void aw88395_hw_reset(struct aw88395 *aw88395) +{ + if (aw88395->reset_gpio) { + gpiod_set_value_cansleep(aw88395->reset_gpio, 0); + usleep_range(AW88395_1000_US, AW88395_1000_US + 10); + gpiod_set_value_cansleep(aw88395->reset_gpio, 1); + usleep_range(AW88395_1000_US, AW88395_1000_US + 10); + } else { + dev_err(aw88395->aw_pa->dev, "%s failed", __func__); + } +} + +static int aw88395_request_firmware_file(struct aw88395 *aw88395) +{ + const struct firmware *cont = NULL; + int ret; + + aw88395->aw_pa->fw_status = AW88395_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88395_ACF_FILE, aw88395->aw_pa->dev); + if ((ret < 0) || (!cont)) { + dev_err(aw88395->aw_pa->dev, "load [%s] failed!", AW88395_ACF_FILE); + return ret; + } + + dev_info(aw88395->aw_pa->dev, "loaded %s - size: %zu\n", + AW88395_ACF_FILE, cont ? cont->size : 0); + + aw88395->aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); + if (!aw88395->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw88395->aw_cfg->len = (int)cont->size; + memcpy(aw88395->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88395->aw_pa, aw88395->aw_cfg); + if (ret < 0) { + dev_err(aw88395->aw_pa->dev, "Load [%s] failed ....!", AW88395_ACF_FILE); + return ret; + } + + dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__); + + mutex_lock(&aw88395->lock); + /* aw device init */ + ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); + if (ret < 0) + dev_err(aw88395->aw_pa->dev, "dev init failed"); + mutex_unlock(&aw88395->lock); + + return ret; +} + +static int aw88395_i2c_probe(struct i2c_client *i2c) +{ + struct aw88395 *aw88395; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed"); + return -EIO; + } + + aw88395 = aw88395_malloc_init(i2c); + if (!aw88395) { + dev_err(&i2c->dev, "malloc aw88395 failed"); + return -ENOMEM; + } + i2c_set_clientdata(i2c, aw88395); + + aw88395->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(aw88395->reset_gpio)) + dev_info(&i2c->dev, "reset gpio not defined\n"); + + /* hardware reset */ + aw88395_hw_reset(aw88395); + + aw88395->regmap = devm_regmap_init_i2c(i2c, &aw88395_remap_config); + if (IS_ERR(aw88395->regmap)) { + ret = PTR_ERR(aw88395->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + /* aw pa init */ + ret = aw88395_init(&aw88395->aw_pa, i2c, aw88395->regmap); + if (ret < 0) + return ret; + + ret = aw88395_request_firmware_file(aw88395); + if (ret < 0) { + dev_err(&i2c->dev, "%s failed\n", __func__); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw88395, + aw88395_dai, ARRAY_SIZE(aw88395_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "failed to register aw88395: %d", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id aw88395_i2c_id[] = { + { AW88395_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id); + +static struct i2c_driver aw88395_i2c_driver = { + .driver = { + .name = AW88395_I2C_NAME, + }, + .probe = aw88395_i2c_probe, + .id_table = aw88395_i2c_id, +}; +module_i2c_driver(aw88395_i2c_driver); + +MODULE_DESCRIPTION("ASoC AW88395 Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88395/aw88395.h b/sound/soc/codecs/aw88395/aw88395.h new file mode 100644 index 0000000000..8036ba27f6 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395.h -- ALSA SoC AW88395 codec support +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#ifndef __AW88395_H__ +#define __AW88395_H__ + +#define AW88395_CHIP_ID_REG (0x00) +#define AW88395_START_RETRIES (5) +#define AW88395_START_WORK_DELAY_MS (0) + +#define AW88395_DSP_16_DATA_MASK (0x0000ffff) + +#define AW88395_I2C_NAME "aw88395_smartpa" + +#define AW88395_RATES (SNDRV_PCM_RATE_8000_48000 | \ + SNDRV_PCM_RATE_96000) +#define AW88395_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define FADE_TIME_MAX 100000 +#define FADE_TIME_MIN 0 + +#define AW88395_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = profile_info, \ + .get = profile_get, \ + .put = profile_set, \ +} + +enum { + AW88395_SYNC_START = 0, + AW88395_ASYNC_START, +}; + +enum { + AW88395_STREAM_CLOSE = 0, + AW88395_STREAM_OPEN, +}; + +struct aw88395 { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_data_type.h b/sound/soc/codecs/aw88395/aw88395_data_type.h new file mode 100644 index 0000000000..e7aa56178b --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_data_type.h @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw883_data_type.h -- The data type of the AW88395 chip +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#ifndef __AW88395_DATA_TYPE_H__ +#define __AW88395_DATA_TYPE_H__ + +#define PROJECT_NAME_MAX (24) +#define CUSTOMER_NAME_MAX (16) +#define CFG_VERSION_MAX (4) +#define DEV_NAME_MAX (16) +#define PROFILE_STR_MAX (32) + +#define ACF_FILE_ID (0xa15f908) + +enum aw_cfg_hdr_version { + AW88395_CFG_HDR_VER = 0x00000001, + AW88395_CFG_HDR_VER_V1 = 0x01000000, +}; + +enum aw_cfg_dde_type { + AW88395_DEV_NONE_TYPE_ID = 0xFFFFFFFF, + AW88395_DEV_TYPE_ID = 0x00000000, + AW88395_SKT_TYPE_ID = 0x00000001, + AW88395_DEV_DEFAULT_TYPE_ID = 0x00000002, +}; + +enum aw_sec_type { + ACF_SEC_TYPE_REG = 0, + ACF_SEC_TYPE_DSP, + ACF_SEC_TYPE_DSP_CFG, + ACF_SEC_TYPE_DSP_FW, + ACF_SEC_TYPE_HDR_REG, + ACF_SEC_TYPE_HDR_DSP_CFG, + ACF_SEC_TYPE_HDR_DSP_FW, + ACF_SEC_TYPE_MULTIPLE_BIN, + ACF_SEC_TYPE_SKT_PROJECT, + ACF_SEC_TYPE_DSP_PROJECT, + ACF_SEC_TYPE_MONITOR, + ACF_SEC_TYPE_MAX, +}; + +enum profile_data_type { + AW88395_DATA_TYPE_REG = 0, + AW88395_DATA_TYPE_DSP_CFG, + AW88395_DATA_TYPE_DSP_FW, + AW88395_DATA_TYPE_MAX, +}; + +enum aw_prof_type { + AW88395_PROFILE_MUSIC = 0, + AW88395_PROFILE_VOICE, + AW88395_PROFILE_VOIP, + AW88395_PROFILE_RINGTONE, + AW88395_PROFILE_RINGTONE_HS, + AW88395_PROFILE_LOWPOWER, + AW88395_PROFILE_BYPASS, + AW88395_PROFILE_MMI, + AW88395_PROFILE_FM, + AW88395_PROFILE_NOTIFICATION, + AW88395_PROFILE_RECEIVER, + AW88395_PROFILE_MAX, +}; + +enum aw_profile_status { + AW88395_PROFILE_WAIT = 0, + AW88395_PROFILE_OK, +}; + +struct aw_cfg_hdr { + u32 id; + char project[PROJECT_NAME_MAX]; + char custom[CUSTOMER_NAME_MAX]; + char version[CFG_VERSION_MAX]; + u32 author_id; + u32 ddt_size; + u32 ddt_num; + u32 hdr_offset; + u32 hdr_version; + u32 reserved[3]; +}; + +struct aw_cfg_dde { + u32 type; + char dev_name[DEV_NAME_MAX]; + u16 dev_index; + u16 dev_bus; + u16 dev_addr; + u16 dev_profile; + u32 data_type; + u32 data_size; + u32 data_offset; + u32 data_crc; + u32 reserved[5]; +}; + +struct aw_cfg_dde_v1 { + u32 type; + char dev_name[DEV_NAME_MAX]; + u16 dev_index; + u16 dev_bus; + u16 dev_addr; + u16 dev_profile; + u32 data_type; + u32 data_size; + u32 data_offset; + u32 data_crc; + char dev_profile_str[PROFILE_STR_MAX]; + u32 chip_id; + u32 reserved[4]; +}; + +struct aw_sec_data_desc { + u32 len; + u8 *data; +}; + +struct aw_prof_desc { + u32 id; + u32 prof_st; + char *prf_str; + u32 fw_ver; + struct aw_sec_data_desc sec_desc[AW88395_DATA_TYPE_MAX]; +}; + +struct aw_all_prof_info { + struct aw_prof_desc prof_desc[AW88395_PROFILE_MAX]; +}; + +struct aw_prof_info { + int count; + int prof_type; + char **prof_name_list; + struct aw_prof_desc *prof_desc; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c new file mode 100644 index 0000000000..33eda37414 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_device.c @@ -0,0 +1,1748 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_device.c -- AW88395 function for ALSA Audio Driver +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// Author: Ben Yi +// + +#include +#include +#include +#include "aw88395_device.h" +#include "aw88395_reg.h" + +static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)dsp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write data error, ret=%d", __func__, ret); + return ret; + } + + return 0; +} + +static int aw_dev_dsp_write_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + u16 temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data & AW88395_DSP_16_DATA_MASK; + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write datal error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data >> 16; + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret); + return ret; + } + + return 0; +} + +static int aw_dev_dsp_write(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type) +{ + u32 reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW88395_DSP_16_DATA: + ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed", + (u32)dsp_addr, dsp_data); + break; + case AW88395_DSP_32_DATA: + ret = aw_dev_dsp_write_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 32-bit dsp_data[0x%x] failed", + (u32)dsp_addr, dsp_data); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + +static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + unsigned int temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data = temp_data; + + return 0; +} + +static int aw_dev_dsp_read_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + unsigned int temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data = temp_data; + + ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data |= (temp_data << 16); + + return 0; +} + +static int aw_dev_dsp_read(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type) +{ + u32 reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW88395_DSP_16_DATA: + ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "read dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed", + (u32)dsp_addr, *dsp_data); + break; + case AW88395_DSP_32_DATA: + ret = aw_dev_dsp_read_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "read dsp_addr[0x%x] 32r-bit dsp_data[0x%x] failed", + (u32)dsp_addr, *dsp_data); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + + +static int aw_dev_read_chipid(struct aw_device *aw_dev, u16 *chip_id) +{ + int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_CHIP_ID_REG, ®_val); + if (ret) { + dev_err(aw_dev->dev, "%s read chipid error. ret = %d", __func__, ret); + return ret; + } + + dev_info(aw_dev->dev, "chip id = %x\n", reg_val); + *chip_id = reg_val; + + return 0; +} + +static unsigned int reg_val_to_db(unsigned int value) +{ + return (((value >> AW88395_VOL_6DB_START) * AW88395_VOLUME_STEP_DB) + + ((value & 0x3f) % AW88395_VOLUME_STEP_DB)); +} + +static unsigned short db_to_reg_val(unsigned short value) +{ + return (((value / AW88395_VOLUME_STEP_DB) << AW88395_VOL_6DB_START) + + (value % AW88395_VOLUME_STEP_DB)); +} + +static int aw_dev_dsp_fw_check(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_fw_desc; + struct aw_prof_desc *set_prof_desc; + u16 base_addr = AW88395_DSP_FW_ADDR; + u16 addr = base_addr; + u32 dsp_val; + u16 bin_val; + int ret, i; + + ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_cur, &set_prof_desc); + if (ret) + return ret; + + /* update reg */ + dsp_fw_desc = &set_prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW]; + + for (i = 0; i < AW88395_FW_CHECK_PART; i++) { + ret = aw_dev_dsp_read(aw_dev, addr, &dsp_val, AW88395_DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "dsp read failed"); + return ret; + } + + bin_val = be16_to_cpup((void *)&dsp_fw_desc->data[2 * (addr - base_addr)]); + + if (dsp_val != bin_val) { + dev_err(aw_dev->dev, "fw check failed, addr[0x%x], read[0x%x] != bindata[0x%x]", + addr, dsp_val, bin_val); + return -EINVAL; + } + + addr += (dsp_fw_desc->len / 2) / AW88395_FW_CHECK_PART; + if ((addr - base_addr) > dsp_fw_desc->len) { + dev_err(aw_dev->dev, "fw check failed, addr[0x%x] too large", addr); + return -EINVAL; + } + } + + return 0; +} + +static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int reg_value; + u16 real_value, volume; + int ret; + + volume = min((value + vol_desc->init_volume), (unsigned int)AW88395_MUTE_VOL); + real_value = db_to_reg_val(volume); + + /* cal real value */ + ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL2_REG, ®_value); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); + + /* [15 : 6] volume */ + real_value = (real_value << AW88395_VOL_START_BIT) | (reg_value & AW88395_VOL_MASK); + + /* write value */ + ret = regmap_write(aw_dev->regmap, AW88395_SYSCTRL2_REG, real_value); + + return ret; +} + +void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol) +{ + int ret; + + ret = aw_dev_set_volume(aw_dev, set_vol); + if (ret) + dev_dbg(aw_dev->dev, "set volume failed"); +} +EXPORT_SYMBOL_GPL(aw88395_dev_set_volume); + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + u16 fade_in_vol = desc->ctl_volume; + int fade_step = aw_dev->fade_step; + int i; + + if (!aw_dev->fade_en) + return; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88395_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + int i; + + if (!aw_dev->fade_en) + return; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88395_MUTE_VOL; i += fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i != AW88395_MUTE_VOL) { + aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +static int aw_dev_modify_dsp_cfg(struct aw_device *aw_dev, + unsigned int addr, unsigned int dsp_data, unsigned char data_type) +{ + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + unsigned int addr_offset; + __le16 data1; + __le32 data2; + + dev_dbg(aw_dev->dev, "addr:0x%x, dsp_data:0x%x", addr, dsp_data); + + addr_offset = (addr - AW88395_DSP_CFG_ADDR) * 2; + if (addr_offset > crc_dsp_cfg->len) { + dev_err(aw_dev->dev, "addr_offset[%d] > crc_dsp_cfg->len[%d]", + addr_offset, crc_dsp_cfg->len); + return -EINVAL; + } + switch (data_type) { + case AW88395_DSP_16_DATA: + data1 = cpu_to_le16((u16)dsp_data); + memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data1, 2); + break; + case AW88395_DSP_32_DATA: + data2 = cpu_to_le32(dsp_data); + memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data2, 4); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_dsp_set_cali_re(struct aw_device *aw_dev) +{ + u32 cali_re; + int ret; + + cali_re = AW88395_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + + aw_dev->cali_desc.ra), AW88395_DSP_RE_SHIFT); + + /* set cali re to device */ + ret = aw_dev_dsp_write(aw_dev, + AW88395_DSP_REG_CFG_ADPZ_RE, cali_re, AW88395_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "set cali re error"); + return ret; + } + + ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RE, + cali_re, AW88395_DSP_32_DATA); + if (ret) + dev_err(aw_dev->dev, "modify dsp cfg failed"); + + return ret; +} + +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG, + ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_ENABLE_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG, + ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_DISABLE_VALUE); + } + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static int aw_dev_dsp_set_crc32(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + u32 crc_value, crc_data_len; + + /* get crc data len */ + crc_data_len = (AW88395_DSP_REG_CRC_ADDR - AW88395_DSP_CFG_ADDR) * 2; + if (crc_data_len > crc_dsp_cfg->len) { + dev_err(aw_dev->dev, "crc data len :%d > cfg_data len:%d", + crc_data_len, crc_dsp_cfg->len); + return -EINVAL; + } + + if (crc_data_len & 0x11) { + dev_err(aw_dev->dev, "The crc data len :%d unsupport", crc_data_len); + return -EINVAL; + } + + crc_value = __crc32c_le(0xFFFFFFFF, crc_dsp_cfg->data, crc_data_len) ^ 0xFFFFFFFF; + + return aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_CRC_ADDR, crc_value, + AW88395_DSP_32_DATA); +} + +static void aw_dev_dsp_check_crc_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG, + ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_ENABLE_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG, + ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_DISABLE_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static int aw_dev_dsp_check_st(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + int i; + + for (i = 0; i < AW88395_DSP_ST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, ®_val); + if (ret) { + dev_err(aw_dev->dev, "read reg0x%x failed", AW88395_SYSST_REG); + continue; + } + + if ((reg_val & (~AW88395_DSPS_MASK)) != AW88395_DSPS_NORMAL_VALUE) { + dev_err(aw_dev->dev, "check dsp st fail,reg_val:0x%04x", reg_val); + ret = -EPERM; + continue; + } else { + dev_dbg(aw_dev->dev, "dsp st check ok, reg_val:0x%04x", reg_val); + return 0; + } + } + + return ret; +} + +static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + + if (is_enable) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_DSPBY_MASK, AW88395_DSPBY_WORKING_VALUE); + if (ret) + dev_dbg(aw_dev->dev, "enable dsp failed"); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_DSPBY_MASK, AW88395_DSPBY_BYPASS_VALUE); + if (ret) + dev_dbg(aw_dev->dev, "disable dsp failed"); + } +} + +static int aw_dev_dsp_check_crc32(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->dsp_cfg == AW88395_DEV_DSP_BYPASS) { + dev_info(aw_dev->dev, "dsp bypass"); + return 0; + } + + ret = aw_dev_dsp_set_crc32(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "set dsp crc32 failed"); + return ret; + } + + aw_dev_dsp_check_crc_enable(aw_dev, true); + + /* dsp enable */ + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW88395_5000_US, AW88395_5000_US + 100); + + ret = aw_dev_dsp_check_st(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check crc32 fail"); + } else { + aw_dev_dsp_check_crc_enable(aw_dev, false); + aw_dev->dsp_crc_st = AW88395_DSP_CRC_OK; + } + + return ret; +} + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + int ret; + + if (pwd) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_PWDN_MASK, AW88395_PWDN_POWER_DOWN_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_PWDN_MASK, AW88395_PWDN_WORKING_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + int ret; + + if (amppd) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_AMPPD_MASK, AW88395_AMPPD_POWER_DOWN_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_AMPPD_MASK, AW88395_AMPPD_WORKING_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + int ret; + + if (is_mute) { + aw_dev_fade_out(aw_dev); + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_HMUTE_MASK, AW88395_HMUTE_ENABLE_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_HMUTE_MASK, AW88395_HMUTE_DISABLE_VALUE); + aw_dev_fade_in(aw_dev); + } + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} +EXPORT_SYMBOL_GPL(aw88395_dev_mute); + +static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + unsigned int reg_val; + u16 reg_icalk; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, ®_val); + if (ret) + return ret; + + reg_icalk = reg_val & (~AW88395_EF_ISN_GESLP_MASK); + + if (reg_icalk & (~AW88395_EF_ISN_GESLP_SIGN_MASK)) + reg_icalk = reg_icalk | AW88395_EF_ISN_GESLP_SIGN_NEG; + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + unsigned int reg_val; + u16 reg_vcalk; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_EFRH_REG, ®_val); + if (ret) + return ret; + + reg_val = reg_val >> AW88395_EF_VSENSE_GAIN_SHIFT; + + reg_vcalk = (u16)reg_val & (~AW88395_EF_VSN_GESLP_MASK); + + if (reg_vcalk & (~AW88395_EF_VSN_GESLP_SIGN_MASK)) + reg_vcalk = reg_vcalk | AW88395_EF_VSN_GESLP_SIGN_NEG; + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_get_vcalk_dac(struct aw_device *aw_dev, int16_t *vcalk) +{ + unsigned int reg_val; + u16 reg_vcalk; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, ®_val); + if (ret) + return ret; + + reg_vcalk = reg_val >> AW88395_EF_DAC_GESLP_SHIFT; + + if (reg_vcalk & AW88395_EF_DAC_GESLP_SIGN_MASK) + reg_vcalk = reg_vcalk | AW88395_EF_DAC_GESLP_SIGN_NEG; + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_vsense_select(struct aw_device *aw_dev, int *vsense_select) +{ + unsigned int vsense_reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_I2SCFG3_REG, &vsense_reg_val); + if (ret) { + dev_err(aw_dev->dev, "read vsense_reg_val failed"); + return ret; + } + dev_dbg(aw_dev->dev, "vsense_reg = 0x%x", vsense_reg_val); + + if (vsense_reg_val & (~AW88395_VDSEL_MASK)) { + *vsense_select = AW88395_DEV_VDSEL_VSENSE; + dev_dbg(aw_dev->dev, "vsense outside"); + } else { + *vsense_select = AW88395_DEV_VDSEL_DAC; + dev_dbg(aw_dev->dev, "vsense inside"); + } + + return 0; +} + +static int aw_dev_set_vcalb(struct aw_device *aw_dev) +{ + int16_t icalk_val, vcalk_val; + int icalk, vsense_select; + u32 vcalb_adj, reg_val; + int vcalb, vcalk; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VCALB, &vcalb_adj, AW88395_DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "read vcalb_adj failed"); + return ret; + } + + ret = aw_dev_vsense_select(aw_dev, &vsense_select); + if (ret) + return ret; + dev_dbg(aw_dev->dev, "vsense_select = %d", vsense_select); + + ret = aw_dev_get_icalk(aw_dev, &icalk_val); + if (ret) + return ret; + icalk = AW88395_CABL_BASE_VALUE + AW88395_ICABLK_FACTOR * icalk_val; + + switch (vsense_select) { + case AW88395_DEV_VDSEL_VSENSE: + ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret) + return ret; + vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR * vcalk_val; + vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR / + AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj; + + dev_dbg(aw_dev->dev, "vcalk_factor=%d, vscal_factor=%d, icalk=%d, vcalk=%d", + AW88395_VCABLK_FACTOR, AW88395_VSCAL_FACTOR, icalk, vcalk); + break; + case AW88395_DEV_VDSEL_DAC: + ret = aw_dev_get_vcalk_dac(aw_dev, &vcalk_val); + if (ret) + return ret; + vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR_DAC * vcalk_val; + vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR_DAC / + AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj; + + dev_dbg(aw_dev->dev, "vcalk_dac_factor=%d, vscal_dac_factor=%d, icalk=%d, vcalk=%d", + AW88395_VCABLK_FACTOR_DAC, + AW88395_VSCAL_FACTOR_DAC, icalk, vcalk); + break; + default: + dev_err(aw_dev->dev, "unsupport vsense status"); + return -EINVAL; + } + + if ((vcalk == 0) || (AW88395_ISCAL_FACTOR == 0)) { + dev_err(aw_dev->dev, "vcalk:%d or desc->iscal_factor:%d unsupported", + vcalk, AW88395_ISCAL_FACTOR); + return -EINVAL; + } + + vcalb = vcalb >> AW88395_VCALB_ADJ_FACTOR; + reg_val = (u32)vcalb; + + dev_dbg(aw_dev->dev, "vcalb=%d, reg_val=0x%x, vcalb_adj =0x%x", + vcalb, reg_val, vcalb_adj); + + ret = aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_VCALB, reg_val, AW88395_DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "write vcalb failed"); + return ret; + } + + ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_VCALB, + (u32)reg_val, AW88395_DSP_16_DATA); + if (ret) + dev_err(aw_dev->dev, "modify dsp cfg failed"); + + return ret; +} + +static int aw_dev_get_cali_f0_delay(struct aw_device *aw_dev) +{ + struct aw_cali_delay_desc *desc = &aw_dev->cali_delay_desc; + u32 cali_delay; + int ret; + + ret = aw_dev_dsp_read(aw_dev, + AW88395_DSP_CALI_F0_DELAY, &cali_delay, AW88395_DSP_16_DATA); + if (ret) + dev_err(aw_dev->dev, "read cali delay failed, ret=%d", ret); + else + desc->delay = AW88395_CALI_DELAY_CACL(cali_delay); + + dev_dbg(aw_dev->dev, "read cali delay: %d ms", desc->delay); + + return ret; +} + +static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSINT_REG, ®_val); + if (ret) + dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status); +} + +static void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + u16 int_status; + + /* read int status and clear */ + aw_dev_get_int_status(aw_dev, &int_status); + /* make sure int status is clear */ + aw_dev_get_int_status(aw_dev, &int_status); + if (int_status) + dev_info(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); +} + +static int aw_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, ®_val); + if (ret) + return -EIO; + if ((reg_val & AW88395_BIT_PLL_CHECK) != AW88395_BIT_PLL_CHECK) { + dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88395_PLLCTRL1_REG, ®_val); + if (ret) + return ret; + + reg_val &= (~AW88395_CCO_MUX_MASK); + if (reg_val == AW88395_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG, + ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG, + ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_BYPASS_VALUE); + if (ret == 0) { + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_check_mode1_pll(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +static int aw_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret = aw_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return ret; +} + +static int aw_dev_check_sysst(struct aw_device *aw_dev) +{ + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, ®_val); + if (ret) + return ret; + + check_val = reg_val & (~AW88395_BIT_SYSST_CHECK_MASK) + & AW88395_BIT_SYSST_CHECK; + if (check_val != AW88395_BIT_SYSST_CHECK) { + dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x", + i, reg_val, AW88395_BIT_SYSST_CHECK); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_sysint(struct aw_device *aw_dev) +{ + u16 reg_val; + + aw_dev_get_int_status(aw_dev, ®_val); + + if (reg_val & AW88395_BIT_SYSINT_CHECK) { + dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) +{ + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if ((reg_val & (~AW88395_RCV_MODE_MASK)) == AW88395_RCV_MODE_RECEIVER_VALUE) + profctrl_desc->cur_mode = AW88395_RCV_MODE; + else + profctrl_desc->cur_mode = AW88395_NOT_RCV_MODE; +} + +static void aw_dev_get_dsp_config(struct aw_device *aw_dev, unsigned char *dsp_cfg) +{ + unsigned int reg_val = 0; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if (reg_val & (~AW88395_DSPBY_MASK)) + *dsp_cfg = AW88395_DEV_DSP_BYPASS; + else + *dsp_cfg = AW88395_DEV_DSP_WORK; +} + +static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag) +{ + int ret; + + switch (flag) { + case AW88395_DEV_MEMCLK_PLL: + ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG, + ~AW88395_MEM_CLKSEL_MASK, + AW88395_MEM_CLKSEL_DAP_HCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select pll failed"); + break; + case AW88395_DEV_MEMCLK_OSC: + ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG, + ~AW88395_MEM_CLKSEL_MASK, + AW88395_MEM_CLKSEL_OSC_CLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select OSC failed"); + break; + default: + dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag); + break; + } +} + +static int aw_dev_get_dsp_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_WDT_REG, ®_val); + if (ret) + return ret; + if (!(reg_val & (~AW88395_WDT_CNT_MASK))) + ret = -EPERM; + + return ret; +} + +static int aw_dev_get_vmax(struct aw_device *aw_dev, unsigned int *vmax) +{ + return aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VMAX, vmax, AW88395_DSP_16_DATA); +} + +static int aw_dev_update_reg_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int read_val; + int16_t *reg_data; + int data_len; + u16 read_vol; + u16 reg_val; + u8 reg_addr; + int i, ret; + + reg_data = (int16_t *)data; + data_len = len >> 1; + + if (data_len & 0x1) { + dev_err(aw_dev->dev, "data len:%d unsupported", data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + reg_addr = reg_data[i]; + reg_val = reg_data[i + 1]; + + if (reg_addr == AW88395_SYSCTRL_REG) { + ret = regmap_read(aw_dev->regmap, reg_addr, &read_val); + if (ret) + break; + read_val &= (~AW88395_HMUTE_MASK); + reg_val &= AW88395_HMUTE_MASK; + reg_val |= read_val; + } + if (reg_addr == AW88395_HAGCCFG7_REG) + reg_val &= AW88395_AGC_DSP_CTL_MASK; + + if (reg_addr == AW88395_I2SCFG1_REG) { + /* close tx */ + reg_val &= AW88395_I2STXEN_MASK; + reg_val |= AW88395_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr == AW88395_SYSCTRL2_REG) { + read_vol = (reg_val & (~AW88395_VOL_MASK)) >> + AW88395_VOL_START_BIT; + aw_dev->volume_desc.init_volume = + reg_val_to_db(read_vol); + } + ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + break; + + } + + aw_dev_get_cur_mode_st(aw_dev); + + if (aw_dev->prof_cur != aw_dev->prof_index) { + /* clear control volume when PA change profile */ + vol_desc->ctl_volume = 0; + } else { + /* keep control volume when PA start with sync mode */ + aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); + } + + /* keep min volume */ + if (aw_dev->fade_en) + aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); + + aw_dev_get_dsp_config(aw_dev, &aw_dev->dsp_cfg); + + return ret; +} + +static int aw_dev_reg_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + if (!len || !data) { + dev_err(aw_dev->dev, "reg data is null or len is 0"); + return -EINVAL; + } + + ret = aw_dev_update_reg_container(aw_dev, data, len); + if (ret) { + dev_err(aw_dev->dev, "reg update failed"); + return ret; + } + + return 0; +} + +static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + u32 dsp_ra; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RA, + &dsp_ra, AW88395_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "read ra error"); + return ret; + } + + cali_desc->ra = AW88395_DSP_RE_TO_SHOW_RE(dsp_ra, + AW88395_DSP_RE_SHIFT); + + return ret; +} + +static int aw_dev_dsp_update_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len, unsigned short base) +{ + int i, ret; + +#ifdef AW88395_DSP_I2C_WRITES + u32 tmp_len; + + mutex_lock(&aw_dev->dsp_lock); + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); + if (ret) + goto error_operation; + + for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) { + if ((len - i) < AW88395_MAX_RAM_WRITE_BYTE_SIZE) + tmp_len = len - i; + else + tmp_len = AW88395_MAX_RAM_WRITE_BYTE_SIZE; + + ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG, + &data[i], tmp_len); + if (ret) + goto error_operation; + } + mutex_unlock(&aw_dev->dsp_lock); +#else + __be16 reg_val; + + mutex_lock(&aw_dev->dsp_lock); + /* i2c write */ + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); + if (ret) + goto error_operation; + for (i = 0; i < len; i += 2) { + reg_val = cpu_to_be16p((u16 *)(data + i)); + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, + (u16)reg_val); + if (ret) + goto error_operation; + } + mutex_unlock(&aw_dev->dsp_lock); +#endif + + return 0; + +error_operation: + mutex_unlock(&aw_dev->dsp_lock); + return ret; +} + +static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + + dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); + return -EINVAL; + } + aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_FW_ADDR); + aw_dev->dsp_fw_len = len; + + return 0; +} + +static int aw_dev_copy_to_crc_dsp_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int size) +{ + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + + if (!crc_dsp_cfg->data) { + crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL); + if (!crc_dsp_cfg->data) + return -ENOMEM; + crc_dsp_cfg->len = size; + } else if (crc_dsp_cfg->len < size) { + devm_kfree(aw_dev->dev, crc_dsp_cfg->data); + crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL); + if (!crc_dsp_cfg->data) + return -ENOMEM; + crc_dsp_cfg->len = size; + } + memcpy(crc_dsp_cfg->data, data, size); + swab16_array((u16 *)crc_dsp_cfg->data, size >> 1); + + return 0; +} + +static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp config len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp config data is null or len is 0"); + return -EINVAL; + } + + aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_CFG_ADDR); + aw_dev->dsp_cfg_len = len; + + ret = aw_dev_copy_to_crc_dsp_cfg(aw_dev, data, len); + if (ret) + return ret; + + ret = aw_dev_set_vcalb(aw_dev); + if (ret) + return ret; + ret = aw_dev_get_ra(&aw_dev->cali_desc); + if (ret) + return ret; + ret = aw_dev_get_cali_f0_delay(aw_dev); + if (ret) + return ret; + + ret = aw_dev_get_vmax(aw_dev, &aw_dev->vmax_desc.init_vmax); + if (ret) { + dev_err(aw_dev->dev, "get vmax failed"); + return ret; + } + dev_dbg(aw_dev->dev, "get init vmax:0x%x", aw_dev->vmax_desc.init_vmax); + aw_dev->dsp_crc_st = AW88395_DSP_CRC_NA; + + return 0; +} + +static int aw_dev_check_sram(struct aw_device *aw_dev) +{ + unsigned int reg_val; + + mutex_lock(&aw_dev->dsp_lock); + /* check the odd bits of reg 0x40 */ + regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST); + regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); + if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); + goto error; + } + + /* check the even bits of reg 0x40 */ + regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_EVEN_NUM_BIT_TEST); + regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); + if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); + goto error; + } + + /* check dsp_fw_base_addr */ + aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_FW_ADDR, AW88395_DSP_EVEN_NUM_BIT_TEST); + aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_FW_ADDR, ®_val); + if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); + goto error; + } + + /* check dsp_cfg_base_addr */ + aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_CFG_ADDR, AW88395_DSP_ODD_NUM_BIT_TEST); + aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_CFG_ADDR, ®_val); + if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); + goto error; + } + mutex_unlock(&aw_dev->dsp_lock); + + return 0; + +error: + mutex_unlock(&aw_dev->dsp_lock); + return -EPERM; +} + +int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en) +{ + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + if ((aw_dev->prof_cur == aw_dev->prof_index) && + (force_up_en == AW88395_FORCE_UPDATE_OFF)) { + dev_dbg(aw_dev->dev, "scene no change, not update"); + return 0; + } + + if (aw_dev->fw_status == AW88395_DEV_FW_FAILED) { + dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); + return -EPERM; + } + + prof_name = aw88395_dev_get_prof_name(aw_dev, aw_dev->prof_index); + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) + return ret; + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw_dev_reg_update(aw_dev, sec_desc[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw88395_dev_mute(aw_dev, true); + + if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK) + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC); + + if (up_dsp_fw_en) { + ret = aw_dev_check_sram(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check sram failed"); + goto error; + } + + /* update dsp firmware */ + dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); + ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data, + sec_desc[AW88395_DATA_TYPE_DSP_FW].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp fw failed"); + goto error; + } + } + + /* update dsp config */ + ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data, + sec_desc[AW88395_DATA_TYPE_DSP_CFG].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp cfg failed"); + goto error; + } + + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL); + + aw_dev->prof_cur = aw_dev->prof_index; + + return 0; + +error: + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL); + return ret; +} +EXPORT_SYMBOL_GPL(aw88395_dev_fw_update); + +static int aw_dev_dsp_check(struct aw_device *aw_dev) +{ + int ret, i; + + switch (aw_dev->dsp_cfg) { + case AW88395_DEV_DSP_BYPASS: + dev_dbg(aw_dev->dev, "dsp bypass"); + ret = 0; + break; + case AW88395_DEV_DSP_WORK: + aw_dev_dsp_enable(aw_dev, false); + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW88395_1000_US, AW88395_1000_US + 10); + for (i = 0; i < AW88395_DEV_DSP_CHECK_MAX; i++) { + ret = aw_dev_get_dsp_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } + } + break; + default: + dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); + ret = -EINVAL; + break; + } + + return ret; +} + +static void aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + int ret; + + if ((aw_dev->cali_desc.cali_re < AW88395_CALI_RE_MAX) && + (aw_dev->cali_desc.cali_re > AW88395_CALI_RE_MIN)) { + + ret = aw_dev_dsp_set_cali_re(aw_dev); + if (ret) + dev_err(aw_dev->dev, "set cali re failed"); + } +} + +int aw88395_dev_start(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->status == AW88395_DEV_PW_ON) { + dev_info(aw_dev->dev, "already power on"); + return 0; + } + /* power on */ + aw_dev_pwd(aw_dev, false); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + + ret = aw_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start"); + goto pll_check_fail; + } + + /* amppd on */ + aw_dev_amppd(aw_dev, false); + usleep_range(AW88395_1000_US, AW88395_1000_US + 50); + + /* check i2s status */ + ret = aw_dev_check_sysst(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "sysst check failed"); + goto sysst_check_fail; + } + + if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK) { + /* dsp bypass */ + aw_dev_dsp_enable(aw_dev, false); + ret = aw_dev_dsp_fw_check(aw_dev); + if (ret) + goto dev_dsp_fw_check_fail; + + aw_dev_update_cali_re(&aw_dev->cali_desc); + + if (aw_dev->dsp_crc_st != AW88395_DSP_CRC_OK) { + ret = aw_dev_dsp_check_crc32(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp crc check failed"); + goto crc_check_fail; + } + } + + ret = aw_dev_dsp_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp status check failed"); + goto dsp_check_fail; + } + } else { + dev_dbg(aw_dev->dev, "start pa with dsp bypass"); + } + + /* enable tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, true); + + /* close mute */ + aw88395_dev_mute(aw_dev, false); + /* clear inturrupt */ + aw_dev_clear_int_status(aw_dev); + aw_dev->status = AW88395_DEV_PW_ON; + + return 0; + +dsp_check_fail: +crc_check_fail: + aw_dev_dsp_enable(aw_dev, false); +dev_dsp_fw_check_fail: +sysst_check_fail: + aw_dev_clear_int_status(aw_dev); + aw_dev_amppd(aw_dev, true); +pll_check_fail: + aw_dev_pwd(aw_dev, true); + aw_dev->status = AW88395_DEV_PW_OFF; + + return ret; +} +EXPORT_SYMBOL_GPL(aw88395_dev_start); + +int aw88395_dev_stop(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_cfg = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG]; + struct aw_sec_data_desc *dsp_fw = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW]; + int int_st = 0; + int ret; + + if (aw_dev->status == AW88395_DEV_PW_OFF) { + dev_info(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88395_DEV_PW_OFF; + + /* set mute */ + aw88395_dev_mute(aw_dev, true); + usleep_range(AW88395_4000_US, AW88395_4000_US + 100); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88395_1000_US, AW88395_1000_US + 100); + + /* check sysint state */ + int_st = aw_dev_check_sysint(aw_dev); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + + if (int_st < 0) { + /* system status anomaly */ + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC); + ret = aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); + if (ret) + dev_err(aw_dev->dev, "update dsp fw failed"); + ret = aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); + if (ret) + dev_err(aw_dev->dev, "update dsp cfg failed"); + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL); + } + + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_stop); + +int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + int ret; + + if ((!aw_dev) || (!aw_cfg)) { + pr_err("aw_dev is NULL or aw_cfg is NULL"); + return -ENOMEM; + } + ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + aw_dev->fade_in_time = AW88395_1000_US / 10; + aw_dev->fade_out_time = AW88395_1000_US >> 1; + aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id; + aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id; + + ret = aw88395_dev_fw_update(aw_dev, AW88395_FORCE_UPDATE_ON, AW88395_DSP_FW_UPDATE_ON); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); + return ret; + } + + /* set mute */ + aw88395_dev_mute(aw_dev, true); + usleep_range(AW88395_4000_US, AW88395_4000_US + 100); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88395_1000_US, AW88395_1000_US + 100); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_init); + +static void aw88395_parse_channel_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value; + int ret; + + ret = of_property_read_u32(np, "sound-channel", &channel_value); + if (ret) { + dev_dbg(aw_dev->dev, + "read sound-channel failed,use default 0"); + aw_dev->channel = AW88395_DEV_DEFAULT_CH; + return; + } + + dev_dbg(aw_dev->dev, "read sound-channel value is: %d", + channel_value); + aw_dev->channel = channel_value; +} + +static void aw88395_parse_fade_enable_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + u32 fade_en; + int ret; + + ret = of_property_read_u32(np, "fade-enable", &fade_en); + if (ret) { + dev_dbg(aw_dev->dev, + "read fade-enable failed, close fade_in_out"); + fade_en = AW88395_FADE_IN_OUT_DEFAULT; + } + + dev_dbg(aw_dev->dev, "read fade-enable value is: %d", fade_en); + + aw_dev->fade_en = fade_en; +} + +static int aw_dev_init(struct aw_device *aw_dev) +{ + aw_dev->chip_id = AW88395_CHIP_ID; + /* call aw device init func */ + aw_dev->acf = NULL; + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = 0; + aw_dev->fw_status = AW88395_DEV_FW_FAILED; + + aw_dev->fade_step = AW88395_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume = AW88395_VOL_DEFAULT_VALUE; + aw88395_parse_channel_dt(aw_dev); + aw88395_parse_fade_enable_dt(aw_dev); + + return 0; +} + +int aw88395_dev_get_profile_count(struct aw_device *aw_dev) +{ + return aw_dev->prof_info.count; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_count); + +int aw88395_dev_get_profile_index(struct aw_device *aw_dev) +{ + return aw_dev->prof_index; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_index); + +int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + /* check the index whether is valid */ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + /* check the index whether change */ + if (aw_dev->prof_index == index) + return -EINVAL; + + aw_dev->prof_index = index; + dev_dbg(aw_dev->dev, "set prof[%s]", + aw_dev->prof_info.prof_name_list[aw_dev->prof_info.prof_desc[index].id]); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_set_profile_index); + +char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return prof_info->prof_name_list[prof_desc->id]; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_name); + +int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_data); + +int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap) +{ + u16 chip_id; + int ret; + + if (*aw_dev) { + dev_info(&i2c->dev, "it should be initialized here.\n"); + } else { + *aw_dev = devm_kzalloc(&i2c->dev, sizeof(struct aw_device), GFP_KERNEL); + if (!(*aw_dev)) + return -ENOMEM; + } + + (*aw_dev)->i2c = i2c; + (*aw_dev)->dev = &i2c->dev; + (*aw_dev)->regmap = regmap; + mutex_init(&(*aw_dev)->dsp_lock); + + /* read chip id */ + ret = aw_dev_read_chipid((*aw_dev), &chip_id); + if (ret) { + dev_err(&i2c->dev, "dev_read_chipid failed ret=%d", ret); + return ret; + } + + switch (chip_id) { + case AW88395_CHIP_ID: + ret = aw_dev_init((*aw_dev)); + break; + default: + ret = -EINVAL; + dev_err((*aw_dev)->dev, "unsupported device"); + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(aw88395_init); + +MODULE_DESCRIPTION("AW88395 device lib"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h new file mode 100644 index 0000000000..caf7307531 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_device.h @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_device.h -- AW88395 function for ALSA Audio Driver +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#ifndef __AW88395_DEVICE_FILE_H__ +#define __AW88395_DEVICE_FILE_H__ + +#include "aw88395.h" +#include "aw88395_data_type.h" +#include "aw88395_lib.h" + +#define AW88395_DEV_DEFAULT_CH (0) +#define AW88395_DEV_DSP_CHECK_MAX (5) +#define AW88395_DSP_I2C_WRITES +#define AW88395_MAX_RAM_WRITE_BYTE_SIZE (128) +#define AW88395_DSP_ODD_NUM_BIT_TEST (0x5555) +#define AW88395_DSP_EVEN_NUM_BIT_TEST (0xAAAA) +#define AW88395_DSP_ST_CHECK_MAX (2) +#define AW88395_FADE_IN_OUT_DEFAULT (0) +#define AW88395_CALI_RE_MAX (15000) +#define AW88395_CALI_RE_MIN (4000) +#define AW88395_CALI_DELAY_CACL(value) ((value * 32) / 48) + +#define AW88395_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift)) +#define AW88395_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) + +#define AW88395_ACF_FILE "aw88395_acf.bin" +#define AW88395_DEV_SYSST_CHECK_MAX (10) + +enum { + AW88395_DEV_VDSEL_DAC = 0, + AW88395_DEV_VDSEL_VSENSE = 1, +}; + +enum { + AW88395_DSP_CRC_NA = 0, + AW88395_DSP_CRC_OK = 1, +}; + +enum { + AW88395_DSP_FW_UPDATE_OFF = 0, + AW88395_DSP_FW_UPDATE_ON = 1, +}; + +enum { + AW88395_FORCE_UPDATE_OFF = 0, + AW88395_FORCE_UPDATE_ON = 1, +}; + +enum { + AW88395_1000_US = 1000, + AW88395_2000_US = 2000, + AW88395_3000_US = 3000, + AW88395_4000_US = 4000, + AW88395_5000_US = 5000, + AW88395_10000_US = 10000, + AW88395_100000_US = 100000, +}; + +enum { + AW88395_DEV_TYPE_OK = 0, + AW88395_DEV_TYPE_NONE = 1, +}; + + +enum AW88395_DEV_STATUS { + AW88395_DEV_PW_OFF = 0, + AW88395_DEV_PW_ON, +}; + +enum AW88395_DEV_FW_STATUS { + AW88395_DEV_FW_FAILED = 0, + AW88395_DEV_FW_OK, +}; + +enum AW88395_DEV_MEMCLK { + AW88395_DEV_MEMCLK_OSC = 0, + AW88395_DEV_MEMCLK_PLL = 1, +}; + +enum AW88395_DEV_DSP_CFG { + AW88395_DEV_DSP_WORK = 0, + AW88395_DEV_DSP_BYPASS = 1, +}; + +enum { + AW88395_DSP_16_DATA = 0, + AW88395_DSP_32_DATA = 1, +}; + +enum { + AW88395_NOT_RCV_MODE = 0, + AW88395_RCV_MODE = 1, +}; + +struct aw_profctrl_desc { + unsigned int cur_mode; +}; + +struct aw_volume_desc { + unsigned int init_volume; + unsigned int mute_volume; + unsigned int ctl_volume; + unsigned int max_volume; +}; + +struct aw_dsp_mem_desc { + unsigned int dsp_madd_reg; + unsigned int dsp_mdat_reg; + unsigned int dsp_fw_base_addr; + unsigned int dsp_cfg_base_addr; +}; + +struct aw_vmax_desc { + unsigned int init_vmax; +}; + +struct aw_cali_delay_desc { + unsigned int delay; +}; + +struct aw_cali_desc { + u32 cali_re; + u32 ra; +}; + +struct aw_container { + int len; + u8 data[]; +}; + +struct aw_device { + int status; + struct mutex dsp_lock; + + unsigned char prof_cur; + unsigned char prof_index; + unsigned char dsp_crc_st; + u16 chip_id; + + unsigned int channel; + unsigned int fade_step; + + struct i2c_client *i2c; + struct device *dev; + struct regmap *regmap; + char *acf; + + u32 fade_en; + unsigned char dsp_cfg; + + u32 dsp_fw_len; + u32 dsp_cfg_len; + u8 platform; + u8 fw_status; + + unsigned int fade_in_time; + unsigned int fade_out_time; + + struct aw_prof_info prof_info; + struct aw_sec_data_desc crc_dsp_cfg; + struct aw_profctrl_desc profctrl_desc; + struct aw_volume_desc volume_desc; + struct aw_dsp_mem_desc dsp_mem_desc; + struct aw_vmax_desc vmax_desc; + + struct aw_cali_delay_desc cali_delay_desc; + struct aw_cali_desc cali_desc; + +}; + +int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap); +int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw88395_dev_start(struct aw_device *aw_dev); +int aw88395_dev_stop(struct aw_device *aw_dev); +int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en); + +void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol); +int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc); +char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index); +int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index); +int aw88395_dev_get_profile_index(struct aw_device *aw_dev); +int aw88395_dev_get_profile_count(struct aw_device *aw_dev); +int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg); +void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute); + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c new file mode 100644 index 0000000000..87dd0ccade --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -0,0 +1,1227 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_lib.c -- ACF bin parsing and check library file for aw88395 +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#include +#include +#include "aw88395_lib.h" +#include "aw88395_device.h" +#include "aw88395_reg.h" + +#define AW88395_CRC8_POLYNOMIAL 0x8C +DECLARE_CRC8_TABLE(aw_crc8_table); + +static char *profile_name[AW88395_PROFILE_MAX] = { + "Music", "Voice", "Voip", "Ringtone", + "Ringtone_hs", "Lowpower", "Bypass", + "Mmi", "Fm", "Notification", "Receiver" +}; + +static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin); + +static int aw_check_sum(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + unsigned char *p_check_sum; + unsigned int sum_data = 0; + unsigned int check_sum; + unsigned int i, len; + + p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + len = bin->header_info[bin_num].bin_data_len + bin->header_info[bin_num].header_len; + check_sum = le32_to_cpup((void *)p_check_sum); + + for (i = 4; i < len; i++) + sum_data += *(p_check_sum + i); + + dev_dbg(aw_dev->dev, "%s -- check_sum = %p, check_sum = 0x%x, sum_data = 0x%x", + __func__, p_check_sum, check_sum, sum_data); + if (sum_data != check_sum) { + dev_err(aw_dev->dev, "%s. CheckSum Fail.bin_num=%d, CheckSum:0x%x, SumData:0x%x", + __func__, bin_num, check_sum, sum_data); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_version(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + if (bin->header_info[bin_num].bin_data_ver < DATA_VERSION_V1 || + bin->header_info[bin_num].bin_data_ver > DATA_VERSION_MAX) { + dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin data version\n"); + return -EINVAL; + } + + return 0; +} + +static int aw_check_register_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + struct bin_header_info temp_info = bin->header_info[bin_num]; + unsigned int check_register_num, parse_register_num; + unsigned char *p_check_sum; + + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + + parse_register_num = le32_to_cpup((void *)p_check_sum); + check_register_num = (bin->header_info[bin_num].bin_data_len - CHECK_REGISTER_NUM_OFFSET) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + dev_dbg(aw_dev->dev, "%s,parse_register_num = 0x%x,check_register_num = 0x%x\n", + __func__, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + dev_err(aw_dev->dev, "%s parse_register_num = 0x%x,check_register_num = 0x%x\n", + __func__, parse_register_num, check_register_num); + return -EINVAL; + } + + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + VALID_DATA_ADDR; + + return 0; +} + +static int aw_check_dsp_reg_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + struct bin_header_info temp_info = bin->header_info[bin_num]; + unsigned int check_dsp_reg_num, parse_dsp_reg_num; + unsigned char *p_check_sum; + + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + + parse_dsp_reg_num = le32_to_cpup((void *)(p_check_sum + PARSE_DSP_REG_NUM)); + bin->header_info[bin_num].reg_data_byte_len = + le32_to_cpup((void *)(p_check_sum + REG_DATA_BYTP_LEN)); + check_dsp_reg_num = (bin->header_info[bin_num].bin_data_len - CHECK_DSP_REG_NUM) / + bin->header_info[bin_num].reg_data_byte_len; + dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x", + __func__, bin_num, check_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + dev_err(aw_dev->dev, "aw_bin_parse check dsp reg num error\n"); + dev_err(aw_dev->dev, "%s parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x", + __func__, check_dsp_reg_num, check_dsp_reg_num); + return -EINVAL; + } + + bin->header_info[bin_num].download_addr = le32_to_cpup((void *)p_check_sum); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - DSP_VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + + DSP_VALID_DATA_ADDR; + + return 0; +} + +static int aw_check_soc_app_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + struct bin_header_info temp_info = bin->header_info[bin_num]; + unsigned int check_soc_app_num, parse_soc_app_num; + unsigned char *p_check_sum; + + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + + bin->header_info[bin_num].app_version = le32_to_cpup((void *)p_check_sum); + parse_soc_app_num = le32_to_cpup((void *)(p_check_sum + PARSE_SOC_APP_NUM)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - CHECK_SOC_APP_NUM; + dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n", + __func__, bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + dev_err(aw_dev->dev, "%s parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n", + __func__, parse_soc_app_num, check_soc_app_num); + return -EINVAL; + } + + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = le32_to_cpup((void *)(p_check_sum + + APP_DOWNLOAD_ADDR)); + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - APP_VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + + APP_VALID_DATA_ADDR; + + return 0; +} + +static void aw_get_single_bin_header(struct aw_bin *bin) +{ + memcpy((void *)&bin->header_info[bin->all_bin_parse_num], bin->p_addr, DATA_LEN); + + bin->header_info[bin->all_bin_parse_num].header_len = HEADER_LEN; + bin->all_bin_parse_num += 1; +} + +static int aw_parse_one_of_multi_bins(struct aw_device *aw_dev, unsigned int bin_num, + int bin_serial_num, struct aw_bin *bin) +{ + struct bin_header_info aw_bin_header_info; + unsigned int bin_start_addr; + unsigned int valid_data_len; + + if (bin->info.len < sizeof(struct bin_header_info)) { + dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n", + (int)sizeof(struct bin_header_info), bin->info.len); + return -EINVAL; + } + + aw_bin_header_info = bin->header_info[bin->all_bin_parse_num - 1]; + if (!bin_serial_num) { + bin_start_addr = le32_to_cpup((void *)(bin->p_addr + START_ADDR_OFFSET)); + bin->p_addr += (HEADER_LEN + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + aw_bin_header_info.valid_data_addr + VALID_DATA_ADDR + 8 * bin_num + + VALID_DATA_ADDR_OFFSET; + } else { + valid_data_len = aw_bin_header_info.bin_data_len; + bin->p_addr += (HDADER_LEN + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + aw_bin_header_info.valid_data_addr + aw_bin_header_info.bin_data_len + + VALID_DATA_ADDR_OFFSET; + } + + return aw_parse_bin_header(aw_dev, bin); +} + +static int aw_get_multi_bin_header(struct aw_device *aw_dev, struct aw_bin *bin) +{ + unsigned int bin_num, i; + int ret; + + bin_num = le32_to_cpup((void *)(bin->p_addr + VALID_DATA_ADDR_OFFSET)); + if (bin->multi_bin_parse_num == 1) + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + VALID_DATA_ADDR_OFFSET; + + aw_get_single_bin_header(bin); + + for (i = 0; i < bin_num; i++) { + dev_dbg(aw_dev->dev, "aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_one_of_multi_bins(aw_dev, bin_num, i, bin); + if (ret < 0) + return ret; + } + + return 0; +} + +static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin) +{ + unsigned int bin_data_type; + + if (bin->info.len < sizeof(struct bin_header_info)) { + dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n", + (int)sizeof(struct bin_header_info), bin->info.len); + return -EINVAL; + } + + bin_data_type = le32_to_cpup((void *)(bin->p_addr + BIN_DATA_TYPE_OFFSET)); + dev_dbg(aw_dev->dev, "aw_bin_parse bin_data_type 0x%x\n", bin_data_type); + switch (bin_data_type) { + case DATA_TYPE_REGISTER: + case DATA_TYPE_DSP_REG: + case DATA_TYPE_SOC_APP: + bin->single_bin_parse_num += 1; + dev_dbg(aw_dev->dev, "%s bin->single_bin_parse_num is %d\n", __func__, + bin->single_bin_parse_num); + if (!bin->multi_bin_parse_num) + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + VALID_DATA_ADDR_OFFSET; + aw_get_single_bin_header(bin); + return 0; + case DATA_TYPE_MULTI_BINS: + bin->multi_bin_parse_num += 1; + dev_dbg(aw_dev->dev, "%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + return aw_get_multi_bin_header(aw_dev, bin); + default: + dev_dbg(aw_dev->dev, "%s There is no corresponding type\n", __func__); + return 0; + } +} + +static int aw_check_bin_header_version(struct aw_device *aw_dev, struct aw_bin *bin) +{ + unsigned int header_version; + + header_version = le32_to_cpup((void *)(bin->p_addr + HEADER_VERSION_OFFSET)); + dev_dbg(aw_dev->dev, "aw_bin_parse header_version 0x%x\n", header_version); + + switch (header_version) { + case HEADER_VERSION_V1: + return aw_parse_bin_header(aw_dev, bin); + default: + dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin header version\n"); + return -EINVAL; + } +} + +static int aw_parsing_bin_file(struct aw_device *aw_dev, struct aw_bin *bin) +{ + int ret = -EINVAL; + int i; + + if (!bin) { + dev_err(aw_dev->dev, "aw_bin_parse bin is NULL\n"); + return ret; + } + bin->p_addr = bin->info.data; + bin->all_bin_parse_num = 0; + bin->multi_bin_parse_num = 0; + bin->single_bin_parse_num = 0; + + ret = aw_check_bin_header_version(aw_dev, bin); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check bin header version error\n"); + return ret; + } + + for (i = 0; i < bin->all_bin_parse_num; i++) { + ret = aw_check_sum(aw_dev, bin, i); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check sum data error\n"); + return ret; + } + ret = aw_check_data_version(aw_dev, bin, i); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check data version error\n"); + return ret; + } + if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { + switch (bin->header_info[i].bin_data_type) { + case DATA_TYPE_REGISTER: + ret = aw_check_register_num(aw_dev, bin, i); + break; + case DATA_TYPE_DSP_REG: + ret = aw_check_dsp_reg_num(aw_dev, bin, i); + break; + case DATA_TYPE_SOC_APP: + ret = aw_check_soc_app_num(aw_dev, bin, i); + break; + default: + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + ret = 0; + break; + } + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int aw_dev_parse_raw_reg(unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = data; + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = data_len; + + prof_desc->prof_st = AW88395_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp_cfg(unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + if (data_len & 0x01) + return -EINVAL; + + swab16_array((u16 *)data, data_len >> 1); + + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = data; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = data_len; + + prof_desc->prof_st = AW88395_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + if (data_len & 0x01) + return -EINVAL; + + swab16_array((u16 *)data, data_len >> 1); + + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = data; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = data_len; + + prof_desc->prof_st = AW88395_PROFILE_OK; + + return 0; +} + +static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *data, + unsigned int data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin; + int ret; + int i; + + aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL); + if (!aw_bin) + return -ENOMEM; + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw_parsing_bin_file(aw_dev, aw_bin); + if (ret < 0) { + dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + for (i = 0; i < aw_bin->all_bin_parse_num; i++) { + switch (aw_bin->header_info[i].bin_data_type) { + case DATA_TYPE_REGISTER: + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + case DATA_TYPE_DSP_REG: + if (aw_bin->header_info[i].valid_data_len & 0x01) { + ret = -EINVAL; + goto parse_bin_failed; + } + + swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), + aw_bin->header_info[i].valid_data_len >> 1); + + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + case DATA_TYPE_DSP_FW: + case DATA_TYPE_SOC_APP: + if (aw_bin->header_info[i].valid_data_len & 0x01) { + ret = -EINVAL; + goto parse_bin_failed; + } + + swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), + aw_bin->header_info[i].valid_data_len >> 1); + + prof_desc->fw_ver = aw_bin->header_info[i].app_version; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + default: + dev_dbg(aw_dev->dev, "bin_data_type not found"); + break; + } + } + prof_desc->prof_st = AW88395_PROFILE_OK; + ret = 0; + +parse_bin_failed: + devm_kfree(aw_dev->dev, aw_bin); + return ret; +} + +static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin; + int ret; + + aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(*aw_bin), GFP_KERNEL); + if (!aw_bin) + return -ENOMEM; + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw_parsing_bin_file(aw_dev, aw_bin); + if (ret < 0) { + dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + if ((aw_bin->all_bin_parse_num != 1) || + (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { + dev_err(aw_dev->dev, "bin num or type error"); + ret = -EINVAL; + goto parse_bin_failed; + } + + if (aw_bin->header_info[0].valid_data_len % 4) { + dev_err(aw_dev->dev, "bin data len get error!"); + ret = -EINVAL; + goto parse_bin_failed; + } + + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = + data + aw_bin->header_info[0].valid_data_addr; + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = + aw_bin->header_info[0].valid_data_len; + prof_desc->prof_st = AW88395_PROFILE_OK; + + devm_kfree(aw_dev->dev, aw_bin); + aw_bin = NULL; + + return 0; + +parse_bin_failed: + devm_kfree(aw_dev->dev, aw_bin); + aw_bin = NULL; + return ret; +} + +static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, + struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc) +{ + switch (cfg_dde->data_type) { + case ACF_SEC_TYPE_REG: + return aw_dev_parse_raw_reg((u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_DSP_CFG: + return aw_dev_parse_raw_dsp_cfg((u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_DSP_FW: + return aw_dev_parse_raw_dsp_fw( + (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_MULTIPLE_BIN: + return aw_dev_prof_parse_multi_bin( + aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_HDR_REG: + return aw_dev_parse_reg_bin_with_hdr(aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + default: + dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type); + break; + } + + return 0; +} + +static int aw_dev_parse_dev_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset); + int sec_num = 0; + int ret, i; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && + (cfg_dde[i].type == AW88395_DEV_TYPE_ID) && + (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) { + if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) { + dev_err(aw_dev->dev, "dev_profile [%d] overflow", + cfg_dde[i].dev_profile); + return -EINVAL; + } + + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + sec_num++; + } + } + + if (sec_num == 0) { + dev_dbg(aw_dev->dev, "get dev type num is %d, please use default", sec_num); + return AW88395_DEV_TYPE_NONE; + } + + return AW88395_DEV_TYPE_OK; +} + +static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset); + int sec_num = 0; + int ret, i; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->channel == cfg_dde[i].dev_index) && + (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) && + (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) { + if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) { + dev_err(aw_dev->dev, "dev_profile [%d] overflow", + cfg_dde[i].dev_profile); + return -EINVAL; + } + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + sec_num++; + } + } + + if (sec_num == 0) { + dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num); + return -EINVAL; + } + + return 0; +} + +static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev, + struct aw_all_prof_info all_prof_info) +{ + struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + int num = 0; + int i; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) + prof_info->count++; + } + + dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count); + + if (!prof_info->count) { + dev_err(aw_dev->dev, "no profile data"); + return -EPERM; + } + + prof_info->prof_desc = devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { + if (num >= prof_info->count) { + dev_err(aw_dev->dev, "overflow count[%d]", + prof_info->count); + return -EINVAL; + } + prof_info->prof_desc[num] = prof_desc[i]; + prof_info->prof_desc[num].id = i; + num++; + } + } + + return 0; +} + +static int aw88395_dev_cfg_get_valid_prof(struct aw_device *aw_dev, + struct aw_all_prof_info all_prof_info) +{ + struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_sec_data_desc *sec_desc; + int num = 0; + int i; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { + sec_desc = prof_desc[i].sec_desc; + if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_REG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) + prof_info->count++; + } + } + + dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count); + + if (!prof_info->count) { + dev_err(aw_dev->dev, "no profile data"); + return -EPERM; + } + + prof_info->prof_desc = devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { + sec_desc = prof_desc[i].sec_desc; + if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_REG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) { + if (num >= prof_info->count) { + dev_err(aw_dev->dev, "overflow count[%d]", + prof_info->count); + return -EINVAL; + } + prof_info->prof_desc[num] = prof_desc[i]; + prof_info->prof_desc[num].id = i; + num++; + } + } + } + + return 0; +} + +static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_all_prof_info *all_prof_info; + int ret; + + all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL); + if (!all_prof_info) + return -ENOMEM; + + ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) { + goto exit; + } else if (ret == AW88395_DEV_TYPE_NONE) { + dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev"); + ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) + goto exit; + } + + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + ret = aw88395_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + break; + case AW88261_CHIP_ID: + ret = aw88261_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + break; + default: + dev_err(aw_dev->dev, "valid prof unsupported"); + ret = -EINVAL; + break; + } + + aw_dev->prof_info.prof_name_list = profile_name; + +exit: + devm_kfree(aw_dev->dev, all_prof_info); + return ret; +} + +static int aw_dev_create_prof_name_list_v1(struct aw_device *aw_dev) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc = prof_info->prof_desc; + int i; + + if (!prof_desc) { + dev_err(aw_dev->dev, "prof_desc is NULL"); + return -EINVAL; + } + + prof_info->prof_name_list = devm_kzalloc(aw_dev->dev, + prof_info->count * PROFILE_STR_MAX, + GFP_KERNEL); + if (!prof_info->prof_name_list) + return -ENOMEM; + + for (i = 0; i < prof_info->count; i++) { + prof_desc[i].id = i; + prof_info->prof_name_list[i] = prof_desc[i].prf_str; + dev_dbg(aw_dev->dev, "prof name is %s", prof_info->prof_name_list[i]); + } + + return 0; +} + +static int aw_get_dde_type_info(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + int default_num = 0; + int dev_num = 0; + unsigned int i; + + for (i = 0; i < cfg_hdr->ddt_num; i++) { + if (cfg_dde[i].type == AW88395_DEV_TYPE_ID) + dev_num++; + + if (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) + default_num++; + } + + if (dev_num != 0) { + aw_dev->prof_info.prof_type = AW88395_DEV_TYPE_ID; + } else if (default_num != 0) { + aw_dev->prof_info.prof_type = AW88395_DEV_DEFAULT_TYPE_ID; + } else { + dev_err(aw_dev->dev, "can't find scene"); + return -EINVAL; + } + + return 0; +} + +static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg, + unsigned int *scene_num) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + unsigned int i; + int ret; + + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) + (*scene_num)++; + } + ret = 0; + break; + case AW88261_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || + (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) + (*scene_num)++; + } + ret = 0; + break; + default: + dev_err(aw_dev->dev, "unsupported device"); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg, + unsigned int *scene_num) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + unsigned int i; + int ret; + + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; + } + ret = 0; + break; + case AW88261_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || + (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; + } + ret = 0; + break; + default: + dev_err(aw_dev->dev, "unsupported device"); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg, + unsigned int *count) +{ + int ret; + + ret = aw_get_dde_type_info(aw_dev, aw_cfg); + if (ret < 0) + return ret; + + switch (aw_dev->prof_info.prof_type) { + case AW88395_DEV_TYPE_ID: + ret = aw_get_dev_scene_count_v1(aw_dev, aw_cfg, count); + break; + case AW88395_DEV_DEFAULT_TYPE_ID: + ret = aw_get_default_scene_count_v1(aw_dev, aw_cfg, count); + break; + default: + dev_err(aw_dev->dev, "unsupported prof_type[%x]", aw_dev->prof_info.prof_type); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, + struct aw_cfg_dde_v1 *cfg_dde, + int *cur_scene_id) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + int ret; + + switch (cfg_dde->data_type) { + case ACF_SEC_TYPE_MULTIPLE_BIN: + ret = aw_dev_prof_parse_multi_bin(aw_dev, (u8 *)prof_hdr + cfg_dde->data_offset, + cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse multi bin failed"); + return ret; + } + prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str; + prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; + (*cur_scene_id)++; + break; + case ACF_SEC_TYPE_HDR_REG: + ret = aw_dev_parse_reg_bin_with_hdr(aw_dev, + (uint8_t *)prof_hdr + cfg_dde->data_offset, + cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse reg bin with hdr failed"); + return ret; + } + prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str; + prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; + (*cur_scene_id)++; + break; + default: + dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_dev_type_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset); + int cur_scene_id = 0; + unsigned int i; + int ret; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && + (aw_dev->chip_id == cfg_dde[i].chip_id)) { + ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr, + &cfg_dde[i], &cur_scene_id); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + } + } + + if (cur_scene_id == 0) { + dev_err(aw_dev->dev, "get dev type failed, get num [%d]", cur_scene_id); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_default_type_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset); + int cur_scene_id = 0; + unsigned int i; + int ret; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->channel == cfg_dde[i].dev_index) && + (aw_dev->chip_id == cfg_dde[i].chip_id)) { + ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr, + &cfg_dde[i], &cur_scene_id); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + } + } + + if (cur_scene_id == 0) { + dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", cur_scene_id); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_by_hdr_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *cfg_hdr) +{ + int ret; + + switch (aw_dev->prof_info.prof_type) { + case AW88395_DEV_TYPE_ID: + ret = aw_dev_parse_dev_type_v1(aw_dev, cfg_hdr); + break; + case AW88395_DEV_DEFAULT_TYPE_ID: + ret = aw_dev_parse_default_type_v1(aw_dev, cfg_hdr); + break; + default: + dev_err(aw_dev->dev, "prof type matched failed, get num[%d]", + aw_dev->prof_info.prof_type); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_load_cfg_by_hdr_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + int ret; + + ret = aw_dev_parse_scene_count_v1(aw_dev, aw_cfg, &prof_info->count); + if (ret < 0) { + dev_err(aw_dev->dev, "get scene count failed"); + return ret; + } + + prof_info->prof_desc = devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + ret = aw_dev_parse_by_hdr_v1(aw_dev, cfg_hdr); + if (ret < 0) { + dev_err(aw_dev->dev, "parse hdr failed"); + return ret; + } + + ret = aw_dev_create_prof_name_list_v1(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "create prof name list failed"); + return ret; + } + + return 0; +} + +int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr; + int ret; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + switch (cfg_hdr->hdr_version) { + case AW88395_CFG_HDR_VER: + ret = aw_dev_load_cfg_by_hdr(aw_dev, cfg_hdr); + if (ret < 0) { + dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed", + cfg_hdr->hdr_version); + return ret; + } + break; + case AW88395_CFG_HDR_VER_V1: + ret = aw_dev_load_cfg_by_hdr_v1(aw_dev, aw_cfg); + if (ret < 0) { + dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed", + cfg_hdr->hdr_version); + return ret; + } + break; + default: + dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version); + return -EINVAL; + } + aw_dev->fw_status = AW88395_DEV_FW_OK; + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_cfg_load); + +static int aw_dev_check_cfg_by_hdr(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + unsigned int end_data_offset; + struct aw_cfg_hdr *cfg_hdr; + struct aw_cfg_dde *cfg_dde; + unsigned int act_data = 0; + unsigned int hdr_ddt_len; + unsigned int i; + u8 act_crc8; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + /* check file type id is awinic acf file */ + if (cfg_hdr->id != ACF_FILE_ID) { + dev_err(aw_dev->dev, "not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + dev_err(aw_dev->dev, "hdr_len with ddt_len [%d] overflow file size[%d]", + cfg_hdr->hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /* check data size */ + cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset, + cfg_dde[i].data_size, 0); + if (act_crc8 != cfg_dde[i].data_crc) { + dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc:0x%x", + i, (u32)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + return 0; +} + +static int aw_dev_check_acf_by_hdr_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_dde_v1 *cfg_dde; + unsigned int end_data_offset; + struct aw_cfg_hdr *cfg_hdr; + unsigned int act_data = 0; + unsigned int hdr_ddt_len; + u8 act_crc8; + int i; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + /* check file type id is awinic acf file */ + if (cfg_hdr->id != ACF_FILE_ID) { + dev_err(aw_dev->dev, "not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + dev_err(aw_dev->dev, "hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /* check data size */ + cfg_dde = (struct aw_cfg_dde_v1 *)((char *)aw_cfg->data + cfg_hdr->hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset, + cfg_dde[i].data_size, 0); + if (act_crc8 != cfg_dde[i].data_crc) { + dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc 0x%x", + i, (u32)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + return 0; +} + +int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr; + + if (!aw_cfg) { + dev_err(aw_dev->dev, "aw_prof is NULL"); + return -EINVAL; + } + + if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) { + dev_err(aw_dev->dev, "cfg hdr size[%d] overflow file size[%d]", + aw_cfg->len, (int)sizeof(struct aw_cfg_hdr)); + return -EINVAL; + } + + crc8_populate_lsb(aw_crc8_table, AW88395_CRC8_POLYNOMIAL); + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + switch (cfg_hdr->hdr_version) { + case AW88395_CFG_HDR_VER: + return aw_dev_check_cfg_by_hdr(aw_dev, aw_cfg); + case AW88395_CFG_HDR_VER_V1: + return aw_dev_check_acf_by_hdr_v1(aw_dev, aw_cfg); + default: + dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_load_acf_check); + +MODULE_DESCRIPTION("AW88395 ACF File Parsing Lib"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88395/aw88395_lib.h b/sound/soc/codecs/aw88395/aw88395_lib.h new file mode 100644 index 0000000000..8a620920d8 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_lib.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_lib.h -- ACF bin parsing and check library file for aw88395 +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#ifndef __AW88395_LIB_H__ +#define __AW88395_LIB_H__ + +#define CHECK_REGISTER_NUM_OFFSET (4) +#define VALID_DATA_LEN (4) +#define VALID_DATA_ADDR (4) +#define PARSE_DSP_REG_NUM (4) +#define REG_DATA_BYTP_LEN (8) +#define CHECK_DSP_REG_NUM (12) +#define DSP_VALID_DATA_LEN (12) +#define DSP_VALID_DATA_ADDR (12) +#define PARSE_SOC_APP_NUM (8) +#define CHECK_SOC_APP_NUM (12) +#define APP_DOWNLOAD_ADDR (4) +#define APP_VALID_DATA_LEN (12) +#define APP_VALID_DATA_ADDR (12) +#define BIN_NUM_MAX (100) +#define HEADER_LEN (60) +#define BIN_DATA_TYPE_OFFSET (8) +#define DATA_LEN (44) +#define VALID_DATA_ADDR_OFFSET (60) +#define START_ADDR_OFFSET (64) + +#define AW88395_FW_CHECK_PART (10) +#define HDADER_LEN (60) + +#define HEADER_VERSION_OFFSET (4) + +enum bin_header_version_enum { + HEADER_VERSION_V1 = 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER = 0x00000000, + DATA_TYPE_DSP_REG = 0x00000010, + DATA_TYPE_DSP_CFG = 0x00000011, + DATA_TYPE_SOC_REG = 0x00000020, + DATA_TYPE_SOC_APP = 0x00000021, + DATA_TYPE_DSP_FW = 0x00000022, + DATA_TYPE_MULTI_BINS = 0x00002000, +}; + +enum data_version_enum { + DATA_VERSION_V1 = 0x00000001, + DATA_VERSION_MAX, +}; + +struct bin_header_info { + unsigned int check_sum; + unsigned int header_ver; + unsigned int bin_data_type; + unsigned int bin_data_ver; + unsigned int bin_data_len; + unsigned int ui_ver; + unsigned char chip_type[8]; + unsigned int reg_byte_len; + unsigned int data_byte_len; + unsigned int device_addr; + unsigned int valid_data_len; + unsigned int valid_data_addr; + + unsigned int reg_num; + unsigned int reg_data_byte_len; + unsigned int download_addr; + unsigned int app_version; + unsigned int header_len; +}; + +struct bin_container { + unsigned int len; + unsigned char data[]; +}; + +struct aw_bin { + unsigned char *p_addr; + unsigned int all_bin_parse_num; + unsigned int multi_bin_parse_num; + unsigned int single_bin_parse_num; + struct bin_header_info header_info[BIN_NUM_MAX]; + struct bin_container info; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h new file mode 100644 index 0000000000..e7a7c02efa --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_reg.h @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_reg.h -- AW88395 chip register file +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#ifndef __AW88395_REG_H__ +#define __AW88395_REG_H__ + +#define AW88395_ID_REG (0x00) +#define AW88395_SYSST_REG (0x01) +#define AW88395_SYSINT_REG (0x02) +#define AW88395_SYSINTM_REG (0x03) +#define AW88395_SYSCTRL_REG (0x04) +#define AW88395_SYSCTRL2_REG (0x05) +#define AW88395_I2SCTRL_REG (0x06) +#define AW88395_I2SCFG1_REG (0x07) +#define AW88395_I2SCFG2_REG (0x08) +#define AW88395_HAGCCFG1_REG (0x09) +#define AW88395_HAGCCFG2_REG (0x0A) +#define AW88395_HAGCCFG3_REG (0x0B) +#define AW88395_HAGCCFG4_REG (0x0C) +#define AW88395_HAGCCFG5_REG (0x0D) +#define AW88395_HAGCCFG6_REG (0x0E) +#define AW88395_HAGCCFG7_REG (0x0F) +#define AW88395_MPDCFG_REG (0x10) +#define AW88395_PWMCTRL_REG (0x11) +#define AW88395_I2SCFG3_REG (0x12) +#define AW88395_DBGCTRL_REG (0x13) +#define AW88395_HAGCST_REG (0x20) +#define AW88395_VBAT_REG (0x21) +#define AW88395_TEMP_REG (0x22) +#define AW88395_PVDD_REG (0x23) +#define AW88395_ISNDAT_REG (0x24) +#define AW88395_VSNDAT_REG (0x25) +#define AW88395_I2SINT_REG (0x26) +#define AW88395_I2SCAPCNT_REG (0x27) +#define AW88395_ANASTA1_REG (0x28) +#define AW88395_ANASTA2_REG (0x29) +#define AW88395_ANASTA3_REG (0x2A) +#define AW88395_ANASTA4_REG (0x2B) +#define AW88395_TESTDET_REG (0x2C) +#define AW88395_TESTIN_REG (0x38) +#define AW88395_TESTOUT_REG (0x39) +#define AW88395_DSPMADD_REG (0x40) +#define AW88395_DSPMDAT_REG (0x41) +#define AW88395_WDT_REG (0x42) +#define AW88395_ACR1_REG (0x43) +#define AW88395_ACR2_REG (0x44) +#define AW88395_ASR1_REG (0x45) +#define AW88395_ASR2_REG (0x46) +#define AW88395_DSPCFG_REG (0x47) +#define AW88395_ASR3_REG (0x48) +#define AW88395_ASR4_REG (0x49) +#define AW88395_VSNCTRL1_REG (0x50) +#define AW88395_ISNCTRL1_REG (0x51) +#define AW88395_PLLCTRL1_REG (0x52) +#define AW88395_PLLCTRL2_REG (0x53) +#define AW88395_PLLCTRL3_REG (0x54) +#define AW88395_CDACTRL1_REG (0x55) +#define AW88395_CDACTRL2_REG (0x56) +#define AW88395_SADCCTRL1_REG (0x57) +#define AW88395_SADCCTRL2_REG (0x58) +#define AW88395_CPCTRL1_REG (0x59) +#define AW88395_BSTCTRL1_REG (0x60) +#define AW88395_BSTCTRL2_REG (0x61) +#define AW88395_BSTCTRL3_REG (0x62) +#define AW88395_BSTCTRL4_REG (0x63) +#define AW88395_BSTCTRL5_REG (0x64) +#define AW88395_BSTCTRL6_REG (0x65) +#define AW88395_BSTCTRL7_REG (0x66) +#define AW88395_DSMCFG1_REG (0x67) +#define AW88395_DSMCFG2_REG (0x68) +#define AW88395_DSMCFG3_REG (0x69) +#define AW88395_DSMCFG4_REG (0x6A) +#define AW88395_DSMCFG5_REG (0x6B) +#define AW88395_DSMCFG6_REG (0x6C) +#define AW88395_DSMCFG7_REG (0x6D) +#define AW88395_DSMCFG8_REG (0x6E) +#define AW88395_TESTCTRL1_REG (0x70) +#define AW88395_TESTCTRL2_REG (0x71) +#define AW88395_EFCTRL1_REG (0x72) +#define AW88395_EFCTRL2_REG (0x73) +#define AW88395_EFWH_REG (0x74) +#define AW88395_EFWM2_REG (0x75) +#define AW88395_EFWM1_REG (0x76) +#define AW88395_EFWL_REG (0x77) +#define AW88395_EFRH_REG (0x78) +#define AW88395_EFRM2_REG (0x79) +#define AW88395_EFRM1_REG (0x7A) +#define AW88395_EFRL_REG (0x7B) +#define AW88395_TM_REG (0x7C) + +enum aw88395_id { + AW88395_CHIP_ID = 0x2049, + AW88261_CHIP_ID = 0x2113, +}; + +#define AW88395_REG_MAX (0x7D) + +#define AW88395_VOLUME_STEP_DB (6 * 8) + +#define AW88395_UVLS_START_BIT (14) +#define AW88395_UVLS_NORMAL (0) +#define AW88395_UVLS_NORMAL_VALUE \ + (AW88395_UVLS_NORMAL << AW88395_UVLS_START_BIT) + +#define AW88395_DSPS_START_BIT (12) +#define AW88395_DSPS_BITS_LEN (1) +#define AW88395_DSPS_MASK \ + (~(((1< +#include +#include +#include +#include +#include +#include + +#define BD28623_NUM_SUPPLIES 3 + +static const char *const bd28623_supply_names[BD28623_NUM_SUPPLIES] = { + "VCCA", + "VCCP1", + "VCCP2", +}; + +struct bd28623_priv { + struct device *dev; + struct regulator_bulk_data supplies[BD28623_NUM_SUPPLIES]; + struct gpio_desc *reset_gpio; + struct gpio_desc *mute_gpio; + + int switch_spk; +}; + +static const struct snd_soc_dapm_widget bd28623_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OUT1P"), + SND_SOC_DAPM_OUTPUT("OUT1N"), + SND_SOC_DAPM_OUTPUT("OUT2P"), + SND_SOC_DAPM_OUTPUT("OUT2N"), +}; + +static const struct snd_soc_dapm_route bd28623_routes[] = { + { "OUT1P", NULL, "DAC" }, + { "OUT1N", NULL, "DAC" }, + { "OUT2P", NULL, "DAC" }, + { "OUT2N", NULL, "DAC" }, +}; + +static int bd28623_power_on(struct bd28623_priv *bd) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(bd->supplies), bd->supplies); + if (ret) { + dev_err(bd->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(bd->reset_gpio, 0); + usleep_range(300000, 400000); + + return 0; +} + +static void bd28623_power_off(struct bd28623_priv *bd) +{ + gpiod_set_value_cansleep(bd->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(bd->supplies), bd->supplies); +} + +static int bd28623_get_switch_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct bd28623_priv *bd = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = bd->switch_spk; + + return 0; +} + +static int bd28623_set_switch_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct bd28623_priv *bd = snd_soc_component_get_drvdata(component); + + if (bd->switch_spk == ucontrol->value.integer.value[0]) + return 0; + + bd->switch_spk = ucontrol->value.integer.value[0]; + + gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1); + + return 0; +} + +static const struct snd_kcontrol_new bd28623_controls[] = { + SOC_SINGLE_BOOL_EXT("Speaker Switch", 0, + bd28623_get_switch_spk, bd28623_set_switch_spk), +}; + +static int bd28623_codec_probe(struct snd_soc_component *component) +{ + struct bd28623_priv *bd = snd_soc_component_get_drvdata(component); + int ret; + + bd->switch_spk = 1; + + ret = bd28623_power_on(bd); + if (ret) + return ret; + + gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1); + + return 0; +} + +static void bd28623_codec_remove(struct snd_soc_component *component) +{ + struct bd28623_priv *bd = snd_soc_component_get_drvdata(component); + + bd28623_power_off(bd); +} + +static int bd28623_codec_suspend(struct snd_soc_component *component) +{ + struct bd28623_priv *bd = snd_soc_component_get_drvdata(component); + + bd28623_power_off(bd); + + return 0; +} + +static int bd28623_codec_resume(struct snd_soc_component *component) +{ + struct bd28623_priv *bd = snd_soc_component_get_drvdata(component); + int ret; + + ret = bd28623_power_on(bd); + if (ret) + return ret; + + gpiod_set_value_cansleep(bd->mute_gpio, bd->switch_spk ? 0 : 1); + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_bd = { + .probe = bd28623_codec_probe, + .remove = bd28623_codec_remove, + .suspend = bd28623_codec_suspend, + .resume = bd28623_codec_resume, + .dapm_widgets = bd28623_widgets, + .num_dapm_widgets = ARRAY_SIZE(bd28623_widgets), + .dapm_routes = bd28623_routes, + .num_dapm_routes = ARRAY_SIZE(bd28623_routes), + .controls = bd28623_controls, + .num_controls = ARRAY_SIZE(bd28623_controls), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct snd_soc_dai_driver soc_dai_bd = { + .name = "bd28623-speaker", + .playback = { + .stream_name = "Playback", + .formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_32000, + .channels_min = 2, + .channels_max = 2, + }, +}; + +static int bd28623_probe(struct platform_device *pdev) +{ + struct bd28623_priv *bd; + struct device *dev = &pdev->dev; + int i, ret; + + bd = devm_kzalloc(&pdev->dev, sizeof(struct bd28623_priv), GFP_KERNEL); + if (!bd) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(bd->supplies); i++) + bd->supplies[i].supply = bd28623_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(bd->supplies), + bd->supplies); + if (ret) { + dev_err(dev, "Failed to get supplies: %d\n", ret); + return ret; + } + + bd->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(bd->reset_gpio)) { + dev_err(dev, "Failed to request reset_gpio: %ld\n", + PTR_ERR(bd->reset_gpio)); + return PTR_ERR(bd->reset_gpio); + } + + bd->mute_gpio = devm_gpiod_get_optional(dev, "mute", + GPIOD_OUT_HIGH); + if (IS_ERR(bd->mute_gpio)) { + dev_err(dev, "Failed to request mute_gpio: %ld\n", + PTR_ERR(bd->mute_gpio)); + return PTR_ERR(bd->mute_gpio); + } + + platform_set_drvdata(pdev, bd); + bd->dev = dev; + + return devm_snd_soc_register_component(dev, &soc_codec_bd, + &soc_dai_bd, 1); +} + +static const struct of_device_id bd28623_of_match[] __maybe_unused = { + { .compatible = "rohm,bd28623", }, + {} +}; +MODULE_DEVICE_TABLE(of, bd28623_of_match); + +static struct platform_driver bd28623_codec_driver = { + .driver = { + .name = "bd28623", + .of_match_table = of_match_ptr(bd28623_of_match), + }, + .probe = bd28623_probe, +}; +module_platform_driver(bd28623_codec_driver); + +MODULE_AUTHOR("Katsuhiro Suzuki "); +MODULE_DESCRIPTION("ROHM BD28623 speaker amplifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c new file mode 100644 index 0000000000..3afcef2dfa --- /dev/null +++ b/sound/soc/codecs/bt-sco.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for generic Bluetooth SCO link + * Copyright 2011 Lars-Peter Clausen + */ + +#include +#include +#include + +#include + +static const struct snd_soc_dapm_widget bt_sco_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + SND_SOC_DAPM_OUTPUT("TX"), + SND_SOC_DAPM_AIF_IN("BT_SCO_RX", "Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("BT_SCO_TX", "Capture", 0, + SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route bt_sco_routes[] = { + { "BT_SCO_TX", NULL, "RX" }, + { "TX", NULL, "BT_SCO_RX" }, +}; + +static struct snd_soc_dai_driver bt_sco_dai[] = { + { + .name = "bt-sco-pcm", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, + { + .name = "bt-sco-pcm-wb", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + } +}; + +static const struct snd_soc_component_driver soc_component_dev_bt_sco = { + .dapm_widgets = bt_sco_widgets, + .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets), + .dapm_routes = bt_sco_routes, + .num_dapm_routes = ARRAY_SIZE(bt_sco_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int bt_sco_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_bt_sco, + bt_sco_dai, ARRAY_SIZE(bt_sco_dai)); +} + +static const struct platform_device_id bt_sco_driver_ids[] = { + { + .name = "dfbmcs320", + }, + { + .name = "bt-sco", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); + +#if defined(CONFIG_OF) +static const struct of_device_id bt_sco_codec_of_match[] = { + { .compatible = "delta,dfbmcs320", }, + { .compatible = "linux,bt-sco", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match); +#endif + +static struct platform_driver bt_sco_driver = { + .driver = { + .name = "bt-sco", + .of_match_table = of_match_ptr(bt_sco_codec_of_match), + }, + .probe = bt_sco_probe, + .id_table = bt_sco_driver_ids, +}; + +module_platform_driver(bt_sco_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("ASoC generic bluetooth sco link driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/chv3-codec.c b/sound/soc/codecs/chv3-codec.c new file mode 100644 index 0000000000..ab99effa68 --- /dev/null +++ b/sound/soc/codecs/chv3-codec.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include + +static struct snd_soc_dai_driver chv3_codec_dai = { + .name = "chv3-codec-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 8, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_chv3_codec = { +}; + +static int chv3_codec_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_chv3_codec, &chv3_codec_dai, 1); +} + +static const struct of_device_id chv3_codec_of_match[] = { + { .compatible = "google,chv3-codec", }, + { } +}; + +static struct platform_driver chv3_codec_platform_driver = { + .driver = { + .name = "chv3-codec", + .of_match_table = chv3_codec_of_match, + }, + .probe = chv3_codec_probe, +}; +module_platform_driver(chv3_codec_platform_driver); + +MODULE_DESCRIPTION("ASoC Chameleon v3 codec driver"); +MODULE_AUTHOR("Pawel Anikiel "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cirrus_legacy.h b/sound/soc/codecs/cirrus_legacy.h new file mode 100644 index 0000000000..87c6fd7929 --- /dev/null +++ b/sound/soc/codecs/cirrus_legacy.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Some small helpers for older Cirrus Logic parts. + * + * Copyright (C) 2021 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +static inline int cirrus_read_device_id(struct regmap *regmap, unsigned int reg) +{ + u8 devid[3]; + int ret; + + ret = regmap_bulk_read(regmap, reg, devid, ARRAY_SIZE(devid)); + if (ret < 0) + return ret; + + return ((devid[0] & 0xFF) << 12) | + ((devid[1] & 0xFF) << 4) | + ((devid[2] & 0xF0) >> 4); +} diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c new file mode 100644 index 0000000000..4f9dabd9d7 --- /dev/null +++ b/sound/soc/codecs/cpcap.c @@ -0,0 +1,1689 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC CPCAP codec driver + * + * Copyright (C) 2017 - 2018 Sebastian Reichel + * + * Very loosely based on original driver from Motorola: + * Copyright (C) 2007 - 2009 Motorola, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */ +#define CPCAP_BIT_AUDIO_LOW_PWR 6 +#define CPCAP_BIT_AUD_LOWPWR_SPEED 5 +#define CPCAP_BIT_VAUDIOPRISTBY 4 +#define CPCAP_BIT_VAUDIO_MODE1 2 +#define CPCAP_BIT_VAUDIO_MODE0 1 +#define CPCAP_BIT_V_AUDIO_EN 0 + +/* Register 513 CPCAP_REG_CC --- CODEC */ +#define CPCAP_BIT_CDC_CLK2 15 +#define CPCAP_BIT_CDC_CLK1 14 +#define CPCAP_BIT_CDC_CLK0 13 +#define CPCAP_BIT_CDC_SR3 12 +#define CPCAP_BIT_CDC_SR2 11 +#define CPCAP_BIT_CDC_SR1 10 +#define CPCAP_BIT_CDC_SR0 9 +#define CPCAP_BIT_CDC_CLOCK_TREE_RESET 8 +#define CPCAP_BIT_MIC2_CDC_EN 7 +#define CPCAP_BIT_CDC_EN_RX 6 +#define CPCAP_BIT_DF_RESET 5 +#define CPCAP_BIT_MIC1_CDC_EN 4 +#define CPCAP_BIT_AUDOHPF_1 3 +#define CPCAP_BIT_AUDOHPF_0 2 +#define CPCAP_BIT_AUDIHPF_1 1 +#define CPCAP_BIT_AUDIHPF_0 0 + +/* Register 514 CPCAP_REG_CDI --- CODEC Digital Audio Interface */ +#define CPCAP_BIT_CDC_PLL_SEL 15 +#define CPCAP_BIT_CLK_IN_SEL 13 +#define CPCAP_BIT_DIG_AUD_IN 12 +#define CPCAP_BIT_CDC_CLK_EN 11 +#define CPCAP_BIT_CDC_DIG_AUD_FS1 10 +#define CPCAP_BIT_CDC_DIG_AUD_FS0 9 +#define CPCAP_BIT_MIC2_TIMESLOT2 8 +#define CPCAP_BIT_MIC2_TIMESLOT1 7 +#define CPCAP_BIT_MIC2_TIMESLOT0 6 +#define CPCAP_BIT_MIC1_RX_TIMESLOT2 5 +#define CPCAP_BIT_MIC1_RX_TIMESLOT1 4 +#define CPCAP_BIT_MIC1_RX_TIMESLOT0 3 +#define CPCAP_BIT_FS_INV 2 +#define CPCAP_BIT_CLK_INV 1 +#define CPCAP_BIT_SMB_CDC 0 + +/* Register 515 CPCAP_REG_SDAC --- Stereo DAC */ +#define CPCAP_BIT_FSYNC_CLK_IN_COMMON 11 +#define CPCAP_BIT_SLAVE_PLL_CLK_INPUT 10 +#define CPCAP_BIT_ST_CLOCK_TREE_RESET 9 +#define CPCAP_BIT_DF_RESET_ST_DAC 8 +#define CPCAP_BIT_ST_SR3 7 +#define CPCAP_BIT_ST_SR2 6 +#define CPCAP_BIT_ST_SR1 5 +#define CPCAP_BIT_ST_SR0 4 +#define CPCAP_BIT_ST_DAC_CLK2 3 +#define CPCAP_BIT_ST_DAC_CLK1 2 +#define CPCAP_BIT_ST_DAC_CLK0 1 +#define CPCAP_BIT_ST_DAC_EN 0 + +/* Register 516 CPCAP_REG_SDACDI --- Stereo DAC Digital Audio Interface */ +#define CPCAP_BIT_ST_L_TIMESLOT2 13 +#define CPCAP_BIT_ST_L_TIMESLOT1 12 +#define CPCAP_BIT_ST_L_TIMESLOT0 11 +#define CPCAP_BIT_ST_R_TIMESLOT2 10 +#define CPCAP_BIT_ST_R_TIMESLOT1 9 +#define CPCAP_BIT_ST_R_TIMESLOT0 8 +#define CPCAP_BIT_ST_DAC_CLK_IN_SEL 7 +#define CPCAP_BIT_ST_FS_INV 6 +#define CPCAP_BIT_ST_CLK_INV 5 +#define CPCAP_BIT_ST_DIG_AUD_FS1 4 +#define CPCAP_BIT_ST_DIG_AUD_FS0 3 +#define CPCAP_BIT_DIG_AUD_IN_ST_DAC 2 +#define CPCAP_BIT_ST_CLK_EN 1 +#define CPCAP_BIT_SMB_ST_DAC 0 + +/* Register 517 CPCAP_REG_TXI --- TX Interface */ +#define CPCAP_BIT_PTT_TH 15 +#define CPCAP_BIT_PTT_CMP_EN 14 +#define CPCAP_BIT_HS_ID_TX 13 +#define CPCAP_BIT_MB_ON2 12 +#define CPCAP_BIT_MB_ON1L 11 +#define CPCAP_BIT_MB_ON1R 10 +#define CPCAP_BIT_RX_L_ENCODE 9 +#define CPCAP_BIT_RX_R_ENCODE 8 +#define CPCAP_BIT_MIC2_MUX 7 +#define CPCAP_BIT_MIC2_PGA_EN 6 +#define CPCAP_BIT_CDET_DIS 5 +#define CPCAP_BIT_EMU_MIC_MUX 4 +#define CPCAP_BIT_HS_MIC_MUX 3 +#define CPCAP_BIT_MIC1_MUX 2 +#define CPCAP_BIT_MIC1_PGA_EN 1 +#define CPCAP_BIT_DLM 0 + +/* Register 518 CPCAP_REG_TXMP --- Mic Gain */ +#define CPCAP_BIT_MB_BIAS_R1 11 +#define CPCAP_BIT_MB_BIAS_R0 10 +#define CPCAP_BIT_MIC2_GAIN_4 9 +#define CPCAP_BIT_MIC2_GAIN_3 8 +#define CPCAP_BIT_MIC2_GAIN_2 7 +#define CPCAP_BIT_MIC2_GAIN_1 6 +#define CPCAP_BIT_MIC2_GAIN_0 5 +#define CPCAP_BIT_MIC1_GAIN_4 4 +#define CPCAP_BIT_MIC1_GAIN_3 3 +#define CPCAP_BIT_MIC1_GAIN_2 2 +#define CPCAP_BIT_MIC1_GAIN_1 1 +#define CPCAP_BIT_MIC1_GAIN_0 0 + +/* Register 519 CPCAP_REG_RXOA --- RX Output Amplifier */ +#define CPCAP_BIT_UNUSED_519_15 15 +#define CPCAP_BIT_UNUSED_519_14 14 +#define CPCAP_BIT_UNUSED_519_13 13 +#define CPCAP_BIT_STDAC_LOW_PWR_DISABLE 12 +#define CPCAP_BIT_HS_LOW_PWR 11 +#define CPCAP_BIT_HS_ID_RX 10 +#define CPCAP_BIT_ST_HS_CP_EN 9 +#define CPCAP_BIT_EMU_SPKR_R_EN 8 +#define CPCAP_BIT_EMU_SPKR_L_EN 7 +#define CPCAP_BIT_HS_L_EN 6 +#define CPCAP_BIT_HS_R_EN 5 +#define CPCAP_BIT_A4_LINEOUT_L_EN 4 +#define CPCAP_BIT_A4_LINEOUT_R_EN 3 +#define CPCAP_BIT_A2_LDSP_L_EN 2 +#define CPCAP_BIT_A2_LDSP_R_EN 1 +#define CPCAP_BIT_A1_EAR_EN 0 + +/* Register 520 CPCAP_REG_RXVC --- RX Volume Control */ +#define CPCAP_BIT_VOL_EXT3 15 +#define CPCAP_BIT_VOL_EXT2 14 +#define CPCAP_BIT_VOL_EXT1 13 +#define CPCAP_BIT_VOL_EXT0 12 +#define CPCAP_BIT_VOL_DAC3 11 +#define CPCAP_BIT_VOL_DAC2 10 +#define CPCAP_BIT_VOL_DAC1 9 +#define CPCAP_BIT_VOL_DAC0 8 +#define CPCAP_BIT_VOL_DAC_LSB_1dB1 7 +#define CPCAP_BIT_VOL_DAC_LSB_1dB0 6 +#define CPCAP_BIT_VOL_CDC3 5 +#define CPCAP_BIT_VOL_CDC2 4 +#define CPCAP_BIT_VOL_CDC1 3 +#define CPCAP_BIT_VOL_CDC0 2 +#define CPCAP_BIT_VOL_CDC_LSB_1dB1 1 +#define CPCAP_BIT_VOL_CDC_LSB_1dB0 0 + +/* Register 521 CPCAP_REG_RXCOA --- Codec to Output Amp Switches */ +#define CPCAP_BIT_PGA_CDC_EN 10 +#define CPCAP_BIT_CDC_SW 9 +#define CPCAP_BIT_PGA_OUTR_USBDP_CDC_SW 8 +#define CPCAP_BIT_PGA_OUTL_USBDN_CDC_SW 7 +#define CPCAP_BIT_ALEFT_HS_CDC_SW 6 +#define CPCAP_BIT_ARIGHT_HS_CDC_SW 5 +#define CPCAP_BIT_A4_LINEOUT_L_CDC_SW 4 +#define CPCAP_BIT_A4_LINEOUT_R_CDC_SW 3 +#define CPCAP_BIT_A2_LDSP_L_CDC_SW 2 +#define CPCAP_BIT_A2_LDSP_R_CDC_SW 1 +#define CPCAP_BIT_A1_EAR_CDC_SW 0 + +/* Register 522 CPCAP_REG_RXSDOA --- RX Stereo DAC to Output Amp Switches */ +#define CPCAP_BIT_PGA_DAC_EN 12 +#define CPCAP_BIT_ST_DAC_SW 11 +#define CPCAP_BIT_MONO_DAC1 10 +#define CPCAP_BIT_MONO_DAC0 9 +#define CPCAP_BIT_PGA_OUTR_USBDP_DAC_SW 8 +#define CPCAP_BIT_PGA_OUTL_USBDN_DAC_SW 7 +#define CPCAP_BIT_ALEFT_HS_DAC_SW 6 +#define CPCAP_BIT_ARIGHT_HS_DAC_SW 5 +#define CPCAP_BIT_A4_LINEOUT_L_DAC_SW 4 +#define CPCAP_BIT_A4_LINEOUT_R_DAC_SW 3 +#define CPCAP_BIT_A2_LDSP_L_DAC_SW 2 +#define CPCAP_BIT_A2_LDSP_R_DAC_SW 1 +#define CPCAP_BIT_A1_EAR_DAC_SW 0 + +/* Register 523 CPCAP_REG_RXEPOA --- RX External PGA to Output Amp Switches */ +#define CPCAP_BIT_PGA_EXT_L_EN 14 +#define CPCAP_BIT_PGA_EXT_R_EN 13 +#define CPCAP_BIT_PGA_IN_L_SW 12 +#define CPCAP_BIT_PGA_IN_R_SW 11 +#define CPCAP_BIT_MONO_EXT1 10 +#define CPCAP_BIT_MONO_EXT0 9 +#define CPCAP_BIT_PGA_OUTR_USBDP_EXT_SW 8 +#define CPCAP_BIT_PGA_OUTL_USBDN_EXT_SW 7 +#define CPCAP_BIT_ALEFT_HS_EXT_SW 6 +#define CPCAP_BIT_ARIGHT_HS_EXT_SW 5 +#define CPCAP_BIT_A4_LINEOUT_L_EXT_SW 4 +#define CPCAP_BIT_A4_LINEOUT_R_EXT_SW 3 +#define CPCAP_BIT_A2_LDSP_L_EXT_SW 2 +#define CPCAP_BIT_A2_LDSP_R_EXT_SW 1 +#define CPCAP_BIT_A1_EAR_EXT_SW 0 + +/* Register 525 CPCAP_REG_A2LA --- SPK Amplifier and Clock Config for Headset */ +#define CPCAP_BIT_NCP_CLK_SYNC 7 +#define CPCAP_BIT_A2_CLK_SYNC 6 +#define CPCAP_BIT_A2_FREE_RUN 5 +#define CPCAP_BIT_A2_CLK2 4 +#define CPCAP_BIT_A2_CLK1 3 +#define CPCAP_BIT_A2_CLK0 2 +#define CPCAP_BIT_A2_CLK_IN 1 +#define CPCAP_BIT_A2_CONFIG 0 + +#define SLEEP_ACTIVATE_POWER 2 +#define CLOCK_TREE_RESET_TIME 1 + +/* constants for ST delay workaround */ +#define STM_STDAC_ACTIVATE_RAMP_TIME 1 +#define STM_STDAC_EN_TEST_PRE 0x090C +#define STM_STDAC_EN_TEST_POST 0x0000 +#define STM_STDAC_EN_ST_TEST1_PRE 0x2400 +#define STM_STDAC_EN_ST_TEST1_POST 0x0400 + +struct cpcap_reg_info { + u16 reg; + u16 mask; + u16 val; +}; + +static const struct cpcap_reg_info cpcap_default_regs[] = { + { CPCAP_REG_VAUDIOC, 0x003F, 0x0000 }, + { CPCAP_REG_CC, 0xFFFF, 0x0000 }, + { CPCAP_REG_CC, 0xFFFF, 0x0000 }, + { CPCAP_REG_CDI, 0xBFFF, 0x0000 }, + { CPCAP_REG_SDAC, 0x0FFF, 0x0000 }, + { CPCAP_REG_SDACDI, 0x3FFF, 0x0000 }, + { CPCAP_REG_TXI, 0x0FDF, 0x0000 }, + { CPCAP_REG_TXMP, 0x0FFF, 0x0400 }, + { CPCAP_REG_RXOA, 0x01FF, 0x0000 }, + { CPCAP_REG_RXVC, 0xFF3C, 0x0000 }, + { CPCAP_REG_RXCOA, 0x07FF, 0x0000 }, + { CPCAP_REG_RXSDOA, 0x1FFF, 0x0000 }, + { CPCAP_REG_RXEPOA, 0x7FFF, 0x0000 }, + { CPCAP_REG_A2LA, BIT(CPCAP_BIT_A2_FREE_RUN), + BIT(CPCAP_BIT_A2_FREE_RUN) }, +}; + +enum cpcap_dai { + CPCAP_DAI_HIFI, + CPCAP_DAI_VOICE, +}; + +struct cpcap_audio { + struct snd_soc_component *component; + struct regmap *regmap; + + u16 vendor; + + int codec_clk_id; + int codec_freq; + int codec_format; +}; + +static int cpcap_st_workaround(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + int err = 0; + + /* Only CPCAP from ST requires workaround */ + if (cpcap->vendor != CPCAP_VENDOR_ST) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + err = regmap_write(cpcap->regmap, CPCAP_REG_TEST, + STM_STDAC_EN_TEST_PRE); + if (err) + return err; + err = regmap_write(cpcap->regmap, CPCAP_REG_ST_TEST1, + STM_STDAC_EN_ST_TEST1_PRE); + break; + case SND_SOC_DAPM_POST_PMU: + msleep(STM_STDAC_ACTIVATE_RAMP_TIME); + + err = regmap_write(cpcap->regmap, CPCAP_REG_ST_TEST1, + STM_STDAC_EN_ST_TEST1_POST); + if (err) + return err; + err = regmap_write(cpcap->regmap, CPCAP_REG_TEST, + STM_STDAC_EN_TEST_POST); + break; + default: + break; + } + + return err; +} + +/* Capture Gain Control: 0dB to 31dB in 1dB steps */ +static const DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0); + +/* Playback Gain Control: -33dB to 12dB in 3dB steps */ +static const DECLARE_TLV_DB_SCALE(vol_tlv, -3300, 300, 0); + +static const struct snd_kcontrol_new cpcap_snd_controls[] = { + /* Playback Gain */ + SOC_SINGLE_TLV("HiFi Playback Volume", + CPCAP_REG_RXVC, CPCAP_BIT_VOL_DAC0, 0xF, 0, vol_tlv), + SOC_SINGLE_TLV("Voice Playback Volume", + CPCAP_REG_RXVC, CPCAP_BIT_VOL_CDC0, 0xF, 0, vol_tlv), + SOC_SINGLE_TLV("Ext Playback Volume", + CPCAP_REG_RXVC, CPCAP_BIT_VOL_EXT0, 0xF, 0, vol_tlv), + + /* Capture Gain */ + SOC_SINGLE_TLV("Mic1 Capture Volume", + CPCAP_REG_TXMP, CPCAP_BIT_MIC1_GAIN_0, 0x1F, 0, mic_gain_tlv), + SOC_SINGLE_TLV("Mic2 Capture Volume", + CPCAP_REG_TXMP, CPCAP_BIT_MIC2_GAIN_0, 0x1F, 0, mic_gain_tlv), + + /* Phase Invert */ + SOC_SINGLE("Hifi Left Phase Invert Switch", + CPCAP_REG_RXSDOA, CPCAP_BIT_MONO_DAC0, 1, 0), + SOC_SINGLE("Ext Left Phase Invert Switch", + CPCAP_REG_RXEPOA, CPCAP_BIT_MONO_EXT0, 1, 0), +}; + +static const char * const cpcap_out_mux_texts[] = { + "Off", "Voice", "HiFi", "Ext" +}; + +static const char * const cpcap_in_right_mux_texts[] = { + "Off", "Mic 1", "Headset Mic", "EMU Mic", "Ext Right" +}; + +static const char * const cpcap_in_left_mux_texts[] = { + "Off", "Mic 2", "Ext Left" +}; + +/* + * input muxes use unusual register layout, so that we need to use custom + * getter/setter methods + */ +static SOC_ENUM_SINGLE_EXT_DECL(cpcap_input_left_mux_enum, + cpcap_in_left_mux_texts); +static SOC_ENUM_SINGLE_EXT_DECL(cpcap_input_right_mux_enum, + cpcap_in_right_mux_texts); + +/* + * mux uses same bit in CPCAP_REG_RXCOA, CPCAP_REG_RXSDOA & CPCAP_REG_RXEPOA; + * even though the register layout makes it look like a mixer, this is a mux. + * Enabling multiple inputs will result in no audio being forwarded. + */ +static SOC_ENUM_SINGLE_DECL(cpcap_earpiece_mux_enum, 0, 0, cpcap_out_mux_texts); +static SOC_ENUM_SINGLE_DECL(cpcap_spkr_r_mux_enum, 0, 1, cpcap_out_mux_texts); +static SOC_ENUM_SINGLE_DECL(cpcap_spkr_l_mux_enum, 0, 2, cpcap_out_mux_texts); +static SOC_ENUM_SINGLE_DECL(cpcap_line_r_mux_enum, 0, 3, cpcap_out_mux_texts); +static SOC_ENUM_SINGLE_DECL(cpcap_line_l_mux_enum, 0, 4, cpcap_out_mux_texts); +static SOC_ENUM_SINGLE_DECL(cpcap_hs_r_mux_enum, 0, 5, cpcap_out_mux_texts); +static SOC_ENUM_SINGLE_DECL(cpcap_hs_l_mux_enum, 0, 6, cpcap_out_mux_texts); +static SOC_ENUM_SINGLE_DECL(cpcap_emu_l_mux_enum, 0, 7, cpcap_out_mux_texts); +static SOC_ENUM_SINGLE_DECL(cpcap_emu_r_mux_enum, 0, 8, cpcap_out_mux_texts); + +static int cpcap_output_mux_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int shift = e->shift_l; + int reg_voice, reg_hifi, reg_ext, status; + int err; + + err = regmap_read(cpcap->regmap, CPCAP_REG_RXCOA, ®_voice); + if (err) + return err; + err = regmap_read(cpcap->regmap, CPCAP_REG_RXSDOA, ®_hifi); + if (err) + return err; + err = regmap_read(cpcap->regmap, CPCAP_REG_RXEPOA, ®_ext); + if (err) + return err; + + reg_voice = (reg_voice >> shift) & 1; + reg_hifi = (reg_hifi >> shift) & 1; + reg_ext = (reg_ext >> shift) & 1; + status = reg_ext << 2 | reg_hifi << 1 | reg_voice; + + switch (status) { + case 0x04: + ucontrol->value.enumerated.item[0] = 3; + break; + case 0x02: + ucontrol->value.enumerated.item[0] = 2; + break; + case 0x01: + ucontrol->value.enumerated.item[0] = 1; + break; + default: + ucontrol->value.enumerated.item[0] = 0; + break; + } + + return 0; +} + +static int cpcap_output_mux_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int muxval = ucontrol->value.enumerated.item[0]; + unsigned int mask = BIT(e->shift_l); + u16 reg_voice = 0x00, reg_hifi = 0x00, reg_ext = 0x00; + int err; + + switch (muxval) { + case 1: + reg_voice = mask; + break; + case 2: + reg_hifi = mask; + break; + case 3: + reg_ext = mask; + break; + default: + break; + } + + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA, + mask, reg_voice); + if (err) + return err; + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXSDOA, + mask, reg_hifi); + if (err) + return err; + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXEPOA, + mask, reg_ext); + if (err) + return err; + + snd_soc_dapm_mux_update_power(dapm, kcontrol, muxval, e, NULL); + + return 0; +} + +static int cpcap_input_right_mux_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + int regval, mask; + int err; + + err = regmap_read(cpcap->regmap, CPCAP_REG_TXI, ®val); + if (err) + return err; + + mask = 0; + mask |= BIT(CPCAP_BIT_MIC1_MUX); + mask |= BIT(CPCAP_BIT_HS_MIC_MUX); + mask |= BIT(CPCAP_BIT_EMU_MIC_MUX); + mask |= BIT(CPCAP_BIT_RX_R_ENCODE); + + switch (regval & mask) { + case BIT(CPCAP_BIT_RX_R_ENCODE): + ucontrol->value.enumerated.item[0] = 4; + break; + case BIT(CPCAP_BIT_EMU_MIC_MUX): + ucontrol->value.enumerated.item[0] = 3; + break; + case BIT(CPCAP_BIT_HS_MIC_MUX): + ucontrol->value.enumerated.item[0] = 2; + break; + case BIT(CPCAP_BIT_MIC1_MUX): + ucontrol->value.enumerated.item[0] = 1; + break; + default: + ucontrol->value.enumerated.item[0] = 0; + break; + } + + return 0; +} + +static int cpcap_input_right_mux_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int muxval = ucontrol->value.enumerated.item[0]; + int regval = 0, mask; + int err; + + mask = 0; + mask |= BIT(CPCAP_BIT_MIC1_MUX); + mask |= BIT(CPCAP_BIT_HS_MIC_MUX); + mask |= BIT(CPCAP_BIT_EMU_MIC_MUX); + mask |= BIT(CPCAP_BIT_RX_R_ENCODE); + + switch (muxval) { + case 1: + regval = BIT(CPCAP_BIT_MIC1_MUX); + break; + case 2: + regval = BIT(CPCAP_BIT_HS_MIC_MUX); + break; + case 3: + regval = BIT(CPCAP_BIT_EMU_MIC_MUX); + break; + case 4: + regval = BIT(CPCAP_BIT_RX_R_ENCODE); + break; + default: + break; + } + + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, + mask, regval); + if (err) + return err; + + snd_soc_dapm_mux_update_power(dapm, kcontrol, muxval, e, NULL); + + return 0; +} + +static int cpcap_input_left_mux_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + int regval, mask; + int err; + + err = regmap_read(cpcap->regmap, CPCAP_REG_TXI, ®val); + if (err) + return err; + + mask = 0; + mask |= BIT(CPCAP_BIT_MIC2_MUX); + mask |= BIT(CPCAP_BIT_RX_L_ENCODE); + + switch (regval & mask) { + case BIT(CPCAP_BIT_RX_L_ENCODE): + ucontrol->value.enumerated.item[0] = 2; + break; + case BIT(CPCAP_BIT_MIC2_MUX): + ucontrol->value.enumerated.item[0] = 1; + break; + default: + ucontrol->value.enumerated.item[0] = 0; + break; + } + + return 0; +} + +static int cpcap_input_left_mux_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int muxval = ucontrol->value.enumerated.item[0]; + int regval = 0, mask; + int err; + + mask = 0; + mask |= BIT(CPCAP_BIT_MIC2_MUX); + mask |= BIT(CPCAP_BIT_RX_L_ENCODE); + + switch (muxval) { + case 1: + regval = BIT(CPCAP_BIT_MIC2_MUX); + break; + case 2: + regval = BIT(CPCAP_BIT_RX_L_ENCODE); + break; + default: + break; + } + + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, + mask, regval); + if (err) + return err; + + snd_soc_dapm_mux_update_power(dapm, kcontrol, muxval, e, NULL); + + return 0; +} + +static const struct snd_kcontrol_new cpcap_input_left_mux = + SOC_DAPM_ENUM_EXT("Input Left", cpcap_input_left_mux_enum, + cpcap_input_left_mux_get_enum, + cpcap_input_left_mux_put_enum); +static const struct snd_kcontrol_new cpcap_input_right_mux = + SOC_DAPM_ENUM_EXT("Input Right", cpcap_input_right_mux_enum, + cpcap_input_right_mux_get_enum, + cpcap_input_right_mux_put_enum); +static const struct snd_kcontrol_new cpcap_emu_left_mux = + SOC_DAPM_ENUM_EXT("EMU Left", cpcap_emu_l_mux_enum, + cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); +static const struct snd_kcontrol_new cpcap_emu_right_mux = + SOC_DAPM_ENUM_EXT("EMU Right", cpcap_emu_r_mux_enum, + cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); +static const struct snd_kcontrol_new cpcap_hs_left_mux = + SOC_DAPM_ENUM_EXT("Headset Left", cpcap_hs_l_mux_enum, + cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); +static const struct snd_kcontrol_new cpcap_hs_right_mux = + SOC_DAPM_ENUM_EXT("Headset Right", cpcap_hs_r_mux_enum, + cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); +static const struct snd_kcontrol_new cpcap_line_left_mux = + SOC_DAPM_ENUM_EXT("Line Left", cpcap_line_l_mux_enum, + cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); +static const struct snd_kcontrol_new cpcap_line_right_mux = + SOC_DAPM_ENUM_EXT("Line Right", cpcap_line_r_mux_enum, + cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); +static const struct snd_kcontrol_new cpcap_speaker_left_mux = + SOC_DAPM_ENUM_EXT("Speaker Left", cpcap_spkr_l_mux_enum, + cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); +static const struct snd_kcontrol_new cpcap_speaker_right_mux = + SOC_DAPM_ENUM_EXT("Speaker Right", cpcap_spkr_r_mux_enum, + cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); +static const struct snd_kcontrol_new cpcap_earpiece_mux = + SOC_DAPM_ENUM_EXT("Earpiece", cpcap_earpiece_mux_enum, + cpcap_output_mux_get_enum, cpcap_output_mux_put_enum); + +static const struct snd_kcontrol_new cpcap_hifi_mono_mixer_controls[] = { + SOC_DAPM_SINGLE("HiFi Mono Playback Switch", + CPCAP_REG_RXSDOA, CPCAP_BIT_MONO_DAC1, 1, 0), +}; +static const struct snd_kcontrol_new cpcap_ext_mono_mixer_controls[] = { + SOC_DAPM_SINGLE("Ext Mono Playback Switch", + CPCAP_REG_RXEPOA, CPCAP_BIT_MONO_EXT0, 1, 0), +}; + +static const struct snd_kcontrol_new cpcap_extr_mute_control = + SOC_DAPM_SINGLE("Switch", + CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_IN_R_SW, 1, 0); +static const struct snd_kcontrol_new cpcap_extl_mute_control = + SOC_DAPM_SINGLE("Switch", + CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_IN_L_SW, 1, 0); + +static const struct snd_kcontrol_new cpcap_voice_loopback = + SOC_DAPM_SINGLE("Switch", + CPCAP_REG_TXI, CPCAP_BIT_DLM, 1, 0); + +static const struct snd_soc_dapm_widget cpcap_dapm_widgets[] = { + /* DAIs */ + SND_SOC_DAPM_AIF_IN("HiFi RX", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Voice RX", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Voice TX", NULL, 0, SND_SOC_NOPM, 0, 0), + + /* Power Supply */ + SND_SOC_DAPM_REGULATOR_SUPPLY("VAUDIO", SLEEP_ACTIVATE_POWER, 0), + + /* Highpass Filters */ + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Highpass Filter RX", + CPCAP_REG_CC, CPCAP_BIT_AUDIHPF_0, 0x3, 0x3, 0x0), + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Highpass Filter TX", + CPCAP_REG_CC, CPCAP_BIT_AUDOHPF_0, 0x3, 0x3, 0x0), + + /* Clocks */ + SND_SOC_DAPM_SUPPLY("HiFi DAI Clock", + CPCAP_REG_SDACDI, CPCAP_BIT_ST_CLK_EN, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Voice DAI Clock", + CPCAP_REG_CDI, CPCAP_BIT_CDC_CLK_EN, 0, NULL, 0), + + /* Microphone Bias */ + SND_SOC_DAPM_SUPPLY("MIC1R Bias", + CPCAP_REG_TXI, CPCAP_BIT_MB_ON1R, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC1L Bias", + CPCAP_REG_TXI, CPCAP_BIT_MB_ON1L, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC2 Bias", + CPCAP_REG_TXI, CPCAP_BIT_MB_ON2, 0, NULL, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("MICR"), + SND_SOC_DAPM_INPUT("HSMIC"), + SND_SOC_DAPM_INPUT("EMUMIC"), + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("EXTR"), + SND_SOC_DAPM_INPUT("EXTL"), + + /* Capture Route */ + SND_SOC_DAPM_MUX("Right Capture Route", + SND_SOC_NOPM, 0, 0, &cpcap_input_right_mux), + SND_SOC_DAPM_MUX("Left Capture Route", + SND_SOC_NOPM, 0, 0, &cpcap_input_left_mux), + + /* Capture PGAs */ + SND_SOC_DAPM_PGA("Microphone 1 PGA", + CPCAP_REG_TXI, CPCAP_BIT_MIC1_PGA_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Microphone 2 PGA", + CPCAP_REG_TXI, CPCAP_BIT_MIC2_PGA_EN, 0, NULL, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC Right", NULL, + CPCAP_REG_CC, CPCAP_BIT_MIC1_CDC_EN, 0), + SND_SOC_DAPM_ADC("ADC Left", NULL, + CPCAP_REG_CC, CPCAP_BIT_MIC2_CDC_EN, 0), + + /* DAC */ + SND_SOC_DAPM_DAC_E("DAC HiFi", NULL, + CPCAP_REG_SDAC, CPCAP_BIT_ST_DAC_EN, 0, + cpcap_st_workaround, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_DAC_E("DAC Voice", NULL, + CPCAP_REG_CC, CPCAP_BIT_CDC_EN_RX, 0, + cpcap_st_workaround, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* Playback PGA */ + SND_SOC_DAPM_PGA("HiFi PGA", + CPCAP_REG_RXSDOA, CPCAP_BIT_PGA_DAC_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Voice PGA", + CPCAP_REG_RXCOA, CPCAP_BIT_PGA_CDC_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("Ext Right PGA", + CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_EXT_R_EN, 0, + NULL, 0, + cpcap_st_workaround, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("Ext Left PGA", + CPCAP_REG_RXEPOA, CPCAP_BIT_PGA_EXT_L_EN, 0, + NULL, 0, + cpcap_st_workaround, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* Playback Switch */ + SND_SOC_DAPM_SWITCH("Ext Right Enable", SND_SOC_NOPM, 0, 0, + &cpcap_extr_mute_control), + SND_SOC_DAPM_SWITCH("Ext Left Enable", SND_SOC_NOPM, 0, 0, + &cpcap_extl_mute_control), + + /* Loopback Switch */ + SND_SOC_DAPM_SWITCH("Voice Loopback", SND_SOC_NOPM, 0, 0, + &cpcap_voice_loopback), + + /* Mono Mixer */ + SOC_MIXER_ARRAY("HiFi Mono Left Mixer", SND_SOC_NOPM, 0, 0, + cpcap_hifi_mono_mixer_controls), + SOC_MIXER_ARRAY("HiFi Mono Right Mixer", SND_SOC_NOPM, 0, 0, + cpcap_hifi_mono_mixer_controls), + SOC_MIXER_ARRAY("Ext Mono Left Mixer", SND_SOC_NOPM, 0, 0, + cpcap_ext_mono_mixer_controls), + SOC_MIXER_ARRAY("Ext Mono Right Mixer", SND_SOC_NOPM, 0, 0, + cpcap_ext_mono_mixer_controls), + + /* Output Routes */ + SND_SOC_DAPM_MUX("Earpiece Playback Route", SND_SOC_NOPM, 0, 0, + &cpcap_earpiece_mux), + SND_SOC_DAPM_MUX("Speaker Right Playback Route", SND_SOC_NOPM, 0, 0, + &cpcap_speaker_right_mux), + SND_SOC_DAPM_MUX("Speaker Left Playback Route", SND_SOC_NOPM, 0, 0, + &cpcap_speaker_left_mux), + SND_SOC_DAPM_MUX("Lineout Right Playback Route", SND_SOC_NOPM, 0, 0, + &cpcap_line_right_mux), + SND_SOC_DAPM_MUX("Lineout Left Playback Route", SND_SOC_NOPM, 0, 0, + &cpcap_line_left_mux), + SND_SOC_DAPM_MUX("Headset Right Playback Route", SND_SOC_NOPM, 0, 0, + &cpcap_hs_right_mux), + SND_SOC_DAPM_MUX("Headset Left Playback Route", SND_SOC_NOPM, 0, 0, + &cpcap_hs_left_mux), + SND_SOC_DAPM_MUX("EMU Right Playback Route", SND_SOC_NOPM, 0, 0, + &cpcap_emu_right_mux), + SND_SOC_DAPM_MUX("EMU Left Playback Route", SND_SOC_NOPM, 0, 0, + &cpcap_emu_left_mux), + + /* Output Amplifier */ + SND_SOC_DAPM_PGA("Earpiece PGA", + CPCAP_REG_RXOA, CPCAP_BIT_A1_EAR_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker Right PGA", + CPCAP_REG_RXOA, CPCAP_BIT_A2_LDSP_R_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker Left PGA", + CPCAP_REG_RXOA, CPCAP_BIT_A2_LDSP_L_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Lineout Right PGA", + CPCAP_REG_RXOA, CPCAP_BIT_A4_LINEOUT_R_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Lineout Left PGA", + CPCAP_REG_RXOA, CPCAP_BIT_A4_LINEOUT_L_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Headset Right PGA", + CPCAP_REG_RXOA, CPCAP_BIT_HS_R_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("Headset Left PGA", + CPCAP_REG_RXOA, CPCAP_BIT_HS_L_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("EMU Right PGA", + CPCAP_REG_RXOA, CPCAP_BIT_EMU_SPKR_R_EN, 0, NULL, 0), + SND_SOC_DAPM_PGA("EMU Left PGA", + CPCAP_REG_RXOA, CPCAP_BIT_EMU_SPKR_L_EN, 0, NULL, 0), + + /* Headet Charge Pump */ + SND_SOC_DAPM_SUPPLY("Headset Charge Pump", + CPCAP_REG_RXOA, CPCAP_BIT_ST_HS_CP_EN, 0, NULL, 0), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("EP"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("LINER"), + SND_SOC_DAPM_OUTPUT("LINEL"), + SND_SOC_DAPM_OUTPUT("HSR"), + SND_SOC_DAPM_OUTPUT("HSL"), + SND_SOC_DAPM_OUTPUT("EMUR"), + SND_SOC_DAPM_OUTPUT("EMUL"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* Power Supply */ + {"HiFi PGA", NULL, "VAUDIO"}, + {"Voice PGA", NULL, "VAUDIO"}, + {"Ext Right PGA", NULL, "VAUDIO"}, + {"Ext Left PGA", NULL, "VAUDIO"}, + {"Microphone 1 PGA", NULL, "VAUDIO"}, + {"Microphone 2 PGA", NULL, "VAUDIO"}, + + /* Stream -> AIF */ + {"HiFi RX", NULL, "HiFi Playback"}, + {"Voice RX", NULL, "Voice Playback"}, + {"Voice Capture", NULL, "Voice TX"}, + + /* AIF clocks */ + {"HiFi RX", NULL, "HiFi DAI Clock"}, + {"Voice RX", NULL, "Voice DAI Clock"}, + {"Voice TX", NULL, "Voice DAI Clock"}, + + /* Digital Loopback */ + {"Voice Loopback", "Switch", "Voice TX"}, + {"Voice RX", NULL, "Voice Loopback"}, + + /* Highpass Filters */ + {"Highpass Filter RX", NULL, "Voice RX"}, + {"Voice TX", NULL, "Highpass Filter TX"}, + + /* AIF -> DAC mapping */ + {"DAC HiFi", NULL, "HiFi RX"}, + {"DAC Voice", NULL, "Highpass Filter RX"}, + + /* DAC -> PGA */ + {"HiFi PGA", NULL, "DAC HiFi"}, + {"Voice PGA", NULL, "DAC Voice"}, + + /* Ext Input -> PGA */ + {"Ext Right PGA", NULL, "EXTR"}, + {"Ext Left PGA", NULL, "EXTL"}, + + /* Ext PGA -> Ext Playback Switch */ + {"Ext Right Enable", "Switch", "Ext Right PGA"}, + {"Ext Left Enable", "Switch", "Ext Left PGA"}, + + /* HiFi PGA -> Mono Mixer */ + {"HiFi Mono Left Mixer", NULL, "HiFi PGA"}, + {"HiFi Mono Left Mixer", "HiFi Mono Playback Switch", "HiFi PGA"}, + {"HiFi Mono Right Mixer", NULL, "HiFi PGA"}, + {"HiFi Mono Right Mixer", "HiFi Mono Playback Switch", "HiFi PGA"}, + + /* Ext Playback Switch -> Ext Mono Mixer */ + {"Ext Mono Right Mixer", NULL, "Ext Right Enable"}, + {"Ext Mono Right Mixer", "Ext Mono Playback Switch", "Ext Left Enable"}, + {"Ext Mono Left Mixer", NULL, "Ext Left Enable"}, + {"Ext Mono Left Mixer", "Ext Mono Playback Switch", "Ext Right Enable"}, + + /* HiFi Mono Mixer -> Output Route */ + {"Earpiece Playback Route", "HiFi", "HiFi Mono Right Mixer"}, + {"Speaker Right Playback Route", "HiFi", "HiFi Mono Right Mixer"}, + {"Speaker Left Playback Route", "HiFi", "HiFi Mono Left Mixer"}, + {"Lineout Right Playback Route", "HiFi", "HiFi Mono Right Mixer"}, + {"Lineout Left Playback Route", "HiFi", "HiFi Mono Left Mixer"}, + {"Headset Right Playback Route", "HiFi", "HiFi Mono Right Mixer"}, + {"Headset Left Playback Route", "HiFi", "HiFi Mono Left Mixer"}, + {"EMU Right Playback Route", "HiFi", "HiFi Mono Right Mixer"}, + {"EMU Left Playback Route", "HiFi", "HiFi Mono Left Mixer"}, + + /* Voice PGA -> Output Route */ + {"Earpiece Playback Route", "Voice", "Voice PGA"}, + {"Speaker Right Playback Route", "Voice", "Voice PGA"}, + {"Speaker Left Playback Route", "Voice", "Voice PGA"}, + {"Lineout Right Playback Route", "Voice", "Voice PGA"}, + {"Lineout Left Playback Route", "Voice", "Voice PGA"}, + {"Headset Right Playback Route", "Voice", "Voice PGA"}, + {"Headset Left Playback Route", "Voice", "Voice PGA"}, + {"EMU Right Playback Route", "Voice", "Voice PGA"}, + {"EMU Left Playback Route", "Voice", "Voice PGA"}, + + /* Ext Mono Mixer -> Output Route */ + {"Earpiece Playback Route", "Ext", "Ext Mono Right Mixer"}, + {"Speaker Right Playback Route", "Ext", "Ext Mono Right Mixer"}, + {"Speaker Left Playback Route", "Ext", "Ext Mono Left Mixer"}, + {"Lineout Right Playback Route", "Ext", "Ext Mono Right Mixer"}, + {"Lineout Left Playback Route", "Ext", "Ext Mono Left Mixer"}, + {"Headset Right Playback Route", "Ext", "Ext Mono Right Mixer"}, + {"Headset Left Playback Route", "Ext", "Ext Mono Left Mixer"}, + {"EMU Right Playback Route", "Ext", "Ext Mono Right Mixer"}, + {"EMU Left Playback Route", "Ext", "Ext Mono Left Mixer"}, + + /* Output Route -> Output Amplifier */ + {"Earpiece PGA", NULL, "Earpiece Playback Route"}, + {"Speaker Right PGA", NULL, "Speaker Right Playback Route"}, + {"Speaker Left PGA", NULL, "Speaker Left Playback Route"}, + {"Lineout Right PGA", NULL, "Lineout Right Playback Route"}, + {"Lineout Left PGA", NULL, "Lineout Left Playback Route"}, + {"Headset Right PGA", NULL, "Headset Right Playback Route"}, + {"Headset Left PGA", NULL, "Headset Left Playback Route"}, + {"EMU Right PGA", NULL, "EMU Right Playback Route"}, + {"EMU Left PGA", NULL, "EMU Left Playback Route"}, + + /* Output Amplifier -> Output */ + {"EP", NULL, "Earpiece PGA"}, + {"SPKR", NULL, "Speaker Right PGA"}, + {"SPKL", NULL, "Speaker Left PGA"}, + {"LINER", NULL, "Lineout Right PGA"}, + {"LINEL", NULL, "Lineout Left PGA"}, + {"HSR", NULL, "Headset Right PGA"}, + {"HSL", NULL, "Headset Left PGA"}, + {"EMUR", NULL, "EMU Right PGA"}, + {"EMUL", NULL, "EMU Left PGA"}, + + /* Headset Charge Pump -> Headset */ + {"HSR", NULL, "Headset Charge Pump"}, + {"HSL", NULL, "Headset Charge Pump"}, + + /* Mic -> Mic Route */ + {"Right Capture Route", "Mic 1", "MICR"}, + {"Right Capture Route", "Headset Mic", "HSMIC"}, + {"Right Capture Route", "EMU Mic", "EMUMIC"}, + {"Right Capture Route", "Ext Right", "EXTR"}, + {"Left Capture Route", "Mic 2", "MICL"}, + {"Left Capture Route", "Ext Left", "EXTL"}, + + /* Input Route -> Microphone PGA */ + {"Microphone 1 PGA", NULL, "Right Capture Route"}, + {"Microphone 2 PGA", NULL, "Left Capture Route"}, + + /* Microphone PGA -> ADC */ + {"ADC Right", NULL, "Microphone 1 PGA"}, + {"ADC Left", NULL, "Microphone 2 PGA"}, + + /* ADC -> Stream */ + {"Highpass Filter TX", NULL, "ADC Right"}, + {"Highpass Filter TX", NULL, "ADC Left"}, + + /* Mic Bias */ + {"MICL", NULL, "MIC1L Bias"}, + {"MICR", NULL, "MIC1R Bias"}, +}; + +static int cpcap_set_sysclk(struct cpcap_audio *cpcap, enum cpcap_dai dai, + int clk_id, int freq) +{ + u16 clkfreqreg, clkfreqshift; + u16 clkfreqmask, clkfreqval; + u16 clkidreg, clkidshift; + u16 mask, val; + int err; + + switch (dai) { + case CPCAP_DAI_HIFI: + clkfreqreg = CPCAP_REG_SDAC; + clkfreqshift = CPCAP_BIT_ST_DAC_CLK0; + clkidreg = CPCAP_REG_SDACDI; + clkidshift = CPCAP_BIT_ST_DAC_CLK_IN_SEL; + break; + case CPCAP_DAI_VOICE: + clkfreqreg = CPCAP_REG_CC; + clkfreqshift = CPCAP_BIT_CDC_CLK0; + clkidreg = CPCAP_REG_CDI; + clkidshift = CPCAP_BIT_CLK_IN_SEL; + break; + default: + dev_err(cpcap->component->dev, "invalid DAI: %d", dai); + return -EINVAL; + } + + /* setup clk id */ + if (clk_id < 0 || clk_id > 1) { + dev_err(cpcap->component->dev, "invalid clk id %d", clk_id); + return -EINVAL; + } + err = regmap_update_bits(cpcap->regmap, clkidreg, BIT(clkidshift), + clk_id ? BIT(clkidshift) : 0); + if (err) + return err; + + /* enable PLL for Voice DAI */ + if (dai == CPCAP_DAI_VOICE) { + mask = BIT(CPCAP_BIT_CDC_PLL_SEL); + val = BIT(CPCAP_BIT_CDC_PLL_SEL); + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, + mask, val); + if (err) + return err; + } + + /* setup frequency */ + clkfreqmask = 0x7 << clkfreqshift; + switch (freq) { + case 15360000: + clkfreqval = 0x01 << clkfreqshift; + break; + case 16800000: + clkfreqval = 0x02 << clkfreqshift; + break; + case 19200000: + clkfreqval = 0x03 << clkfreqshift; + break; + case 26000000: + clkfreqval = 0x04 << clkfreqshift; + break; + case 33600000: + clkfreqval = 0x05 << clkfreqshift; + break; + case 38400000: + clkfreqval = 0x06 << clkfreqshift; + break; + default: + dev_err(cpcap->component->dev, "unsupported freq %u", freq); + return -EINVAL; + } + + err = regmap_update_bits(cpcap->regmap, clkfreqreg, + clkfreqmask, clkfreqval); + if (err) + return err; + + if (dai == CPCAP_DAI_VOICE) { + cpcap->codec_clk_id = clk_id; + cpcap->codec_freq = freq; + } + + return 0; +} + +static int cpcap_set_samprate(struct cpcap_audio *cpcap, enum cpcap_dai dai, + int samplerate) +{ + struct snd_soc_component *component = cpcap->component; + u16 sampreg, sampmask, sampshift, sampval, sampreset; + int err, sampreadval; + + switch (dai) { + case CPCAP_DAI_HIFI: + sampreg = CPCAP_REG_SDAC; + sampshift = CPCAP_BIT_ST_SR0; + sampreset = BIT(CPCAP_BIT_DF_RESET_ST_DAC) | + BIT(CPCAP_BIT_ST_CLOCK_TREE_RESET); + break; + case CPCAP_DAI_VOICE: + sampreg = CPCAP_REG_CC; + sampshift = CPCAP_BIT_CDC_SR0; + sampreset = BIT(CPCAP_BIT_DF_RESET) | + BIT(CPCAP_BIT_CDC_CLOCK_TREE_RESET); + break; + default: + dev_err(component->dev, "invalid DAI: %d", dai); + return -EINVAL; + } + + sampmask = 0xF << sampshift | sampreset; + switch (samplerate) { + case 48000: + sampval = 0x8 << sampshift; + break; + case 44100: + sampval = 0x7 << sampshift; + break; + case 32000: + sampval = 0x6 << sampshift; + break; + case 24000: + sampval = 0x5 << sampshift; + break; + case 22050: + sampval = 0x4 << sampshift; + break; + case 16000: + sampval = 0x3 << sampshift; + break; + case 12000: + sampval = 0x2 << sampshift; + break; + case 11025: + sampval = 0x1 << sampshift; + break; + case 8000: + sampval = 0x0 << sampshift; + break; + default: + dev_err(component->dev, "unsupported samplerate %d", samplerate); + return -EINVAL; + } + err = regmap_update_bits(cpcap->regmap, sampreg, + sampmask, sampval | sampreset); + if (err) + return err; + + /* Wait for clock tree reset to complete */ + mdelay(CLOCK_TREE_RESET_TIME); + + err = regmap_read(cpcap->regmap, sampreg, &sampreadval); + if (err) + return err; + + if (sampreadval & sampreset) { + dev_err(component->dev, "reset self-clear failed: %04x", + sampreadval); + return -EIO; + } + + return 0; +} + +static int cpcap_hifi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + int rate = params_rate(params); + + dev_dbg(component->dev, "HiFi setup HW params: rate=%d", rate); + return cpcap_set_samprate(cpcap, CPCAP_DAI_HIFI, rate); +} + +static int cpcap_hifi_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + + dev_dbg(dev, "HiFi setup sysclk: clk_id=%u, freq=%u", clk_id, freq); + return cpcap_set_sysclk(cpcap, CPCAP_DAI_HIFI, clk_id, freq); +} + +static int cpcap_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + static const u16 reg = CPCAP_REG_SDACDI; + static const u16 mask = + BIT(CPCAP_BIT_SMB_ST_DAC) | + BIT(CPCAP_BIT_ST_CLK_INV) | + BIT(CPCAP_BIT_ST_FS_INV) | + BIT(CPCAP_BIT_ST_DIG_AUD_FS0) | + BIT(CPCAP_BIT_ST_DIG_AUD_FS1) | + BIT(CPCAP_BIT_ST_L_TIMESLOT0) | + BIT(CPCAP_BIT_ST_L_TIMESLOT1) | + BIT(CPCAP_BIT_ST_L_TIMESLOT2) | + BIT(CPCAP_BIT_ST_R_TIMESLOT0) | + BIT(CPCAP_BIT_ST_R_TIMESLOT1) | + BIT(CPCAP_BIT_ST_R_TIMESLOT2); + u16 val = 0x0000; + + dev_dbg(dev, "HiFi setup dai format (%08x)", fmt); + + /* + * "HiFi Playback" should always be configured as + * SND_SOC_DAIFMT_CBP_CFP - codec clk & frm provider + * SND_SOC_DAIFMT_I2S - I2S mode + */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + val &= ~BIT(CPCAP_BIT_SMB_ST_DAC); + break; + default: + dev_err(dev, "HiFi dai fmt failed: CPCAP should be provider"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + val |= BIT(CPCAP_BIT_ST_FS_INV); + val |= BIT(CPCAP_BIT_ST_CLK_INV); + break; + case SND_SOC_DAIFMT_IB_NF: + val &= ~BIT(CPCAP_BIT_ST_FS_INV); + val |= BIT(CPCAP_BIT_ST_CLK_INV); + break; + case SND_SOC_DAIFMT_NB_IF: + val |= BIT(CPCAP_BIT_ST_FS_INV); + val &= ~BIT(CPCAP_BIT_ST_CLK_INV); + break; + case SND_SOC_DAIFMT_NB_NF: + val &= ~BIT(CPCAP_BIT_ST_FS_INV); + val &= ~BIT(CPCAP_BIT_ST_CLK_INV); + break; + default: + dev_err(dev, "HiFi dai fmt failed: unsupported clock invert mode"); + return -EINVAL; + } + + if (val & BIT(CPCAP_BIT_ST_CLK_INV)) + val &= ~BIT(CPCAP_BIT_ST_CLK_INV); + else + val |= BIT(CPCAP_BIT_ST_CLK_INV); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS0); + val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS1); + break; + default: + /* 01 - 4 slots network mode */ + val |= BIT(CPCAP_BIT_ST_DIG_AUD_FS0); + val &= ~BIT(CPCAP_BIT_ST_DIG_AUD_FS1); + /* L on slot 1 */ + val |= BIT(CPCAP_BIT_ST_L_TIMESLOT0); + break; + } + + dev_dbg(dev, "HiFi dai format: val=%04x", val); + return regmap_update_bits(cpcap->regmap, reg, mask, val); +} + +static int cpcap_hifi_set_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + static const u16 reg = CPCAP_REG_RXSDOA; + static const u16 mask = BIT(CPCAP_BIT_ST_DAC_SW); + u16 val; + + if (mute) + val = 0; + else + val = BIT(CPCAP_BIT_ST_DAC_SW); + + dev_dbg(component->dev, "HiFi mute: %d", mute); + return regmap_update_bits(cpcap->regmap, reg, mask, val); +} + +static const struct snd_soc_dai_ops cpcap_dai_hifi_ops = { + .hw_params = cpcap_hifi_hw_params, + .set_sysclk = cpcap_hifi_set_dai_sysclk, + .set_fmt = cpcap_hifi_set_dai_fmt, + .mute_stream = cpcap_hifi_set_mute, + .no_capture_mute = 1, +}; + +static int cpcap_voice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct device *dev = component->dev; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + static const u16 reg_cdi = CPCAP_REG_CDI; + int rate = params_rate(params); + int channels = params_channels(params); + int direction = substream->stream; + u16 val, mask; + int err; + + dev_dbg(dev, "Voice setup HW params: rate=%d, direction=%d, chan=%d", + rate, direction, channels); + + err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, rate); + if (err) + return err; + + if (direction == SNDRV_PCM_STREAM_CAPTURE) { + mask = 0x0000; + mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT0); + mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT1); + mask |= BIT(CPCAP_BIT_MIC1_RX_TIMESLOT2); + mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT0); + mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT1); + mask |= BIT(CPCAP_BIT_MIC2_TIMESLOT2); + val = 0x0000; + if (channels >= 2) + val = BIT(CPCAP_BIT_MIC1_RX_TIMESLOT0); + err = regmap_update_bits(cpcap->regmap, reg_cdi, mask, val); + if (err) + return err; + } + + return 0; +} + +static int cpcap_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "Voice setup sysclk: clk_id=%u, freq=%u", + clk_id, freq); + return cpcap_set_sysclk(cpcap, CPCAP_DAI_VOICE, clk_id, freq); +} + +static int cpcap_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + static const u16 mask = BIT(CPCAP_BIT_SMB_CDC) | + BIT(CPCAP_BIT_CLK_INV) | + BIT(CPCAP_BIT_FS_INV) | + BIT(CPCAP_BIT_CDC_DIG_AUD_FS0) | + BIT(CPCAP_BIT_CDC_DIG_AUD_FS1); + u16 val = 0x0000; + int err; + + dev_dbg(component->dev, "Voice setup dai format (%08x)", fmt); + + /* + * "Voice Playback" and "Voice Capture" should always be + * configured as SND_SOC_DAIFMT_CBP_CFP - codec clk & frm + * provider + */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + val &= ~BIT(CPCAP_BIT_SMB_CDC); + break; + default: + dev_err(component->dev, "Voice dai fmt failed: CPCAP should be the provider"); + val &= ~BIT(CPCAP_BIT_SMB_CDC); + break; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + val |= BIT(CPCAP_BIT_CLK_INV); + val |= BIT(CPCAP_BIT_FS_INV); + break; + case SND_SOC_DAIFMT_IB_NF: + val |= BIT(CPCAP_BIT_CLK_INV); + val &= ~BIT(CPCAP_BIT_FS_INV); + break; + case SND_SOC_DAIFMT_NB_IF: + val &= ~BIT(CPCAP_BIT_CLK_INV); + val |= BIT(CPCAP_BIT_FS_INV); + break; + case SND_SOC_DAIFMT_NB_NF: + val &= ~BIT(CPCAP_BIT_CLK_INV); + val &= ~BIT(CPCAP_BIT_FS_INV); + break; + default: + dev_err(component->dev, "Voice dai fmt failed: unsupported clock invert mode"); + break; + } + + if (val & BIT(CPCAP_BIT_CLK_INV)) + val &= ~BIT(CPCAP_BIT_CLK_INV); + else + val |= BIT(CPCAP_BIT_CLK_INV); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* 11 - true I2S mode */ + val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS0); + val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS1); + break; + default: + /* 4 timeslots network mode */ + val |= BIT(CPCAP_BIT_CDC_DIG_AUD_FS0); + val &= ~BIT(CPCAP_BIT_CDC_DIG_AUD_FS1); + break; + } + + dev_dbg(component->dev, "Voice dai format: val=%04x", val); + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, mask, val); + if (err) + return err; + + cpcap->codec_format = val; + return 0; +} + + +/* + * Configure codec for voice call if requested. + * + * We can configure most with snd_soc_dai_set_sysclk(), snd_soc_dai_set_fmt() + * and snd_soc_dai_set_tdm_slot(). This function configures the rest of the + * cpcap related hardware as CPU is not involved in the voice call. + */ +static int cpcap_voice_call(struct cpcap_audio *cpcap, struct snd_soc_dai *dai, + bool voice_call) +{ + int mask, err; + + /* Modem to codec VAUDIO_MODE1 */ + mask = BIT(CPCAP_BIT_VAUDIO_MODE1); + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_VAUDIOC, + mask, voice_call ? mask : 0); + if (err) + return err; + + /* Clear MIC1_MUX for call */ + mask = BIT(CPCAP_BIT_MIC1_MUX); + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, + mask, voice_call ? 0 : mask); + if (err) + return err; + + /* Set MIC2_MUX for call */ + mask = BIT(CPCAP_BIT_MB_ON1L) | BIT(CPCAP_BIT_MB_ON1R) | + BIT(CPCAP_BIT_MIC2_MUX) | BIT(CPCAP_BIT_MIC2_PGA_EN); + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, + mask, voice_call ? mask : 0); + if (err) + return err; + + /* Enable LDSP for call */ + mask = BIT(CPCAP_BIT_A2_LDSP_L_EN) | BIT(CPCAP_BIT_A2_LDSP_R_EN); + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, + mask, voice_call ? mask : 0); + if (err) + return err; + + /* Enable CPCAP_BIT_PGA_CDC_EN for call */ + mask = BIT(CPCAP_BIT_PGA_CDC_EN); + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_RXCOA, + mask, voice_call ? mask : 0); + if (err) + return err; + + /* Unmute voice for call */ + if (dai) { + err = snd_soc_dai_digital_mute(dai, !voice_call, + SNDRV_PCM_STREAM_PLAYBACK); + if (err) + return err; + } + + /* Set modem to codec mic CDC and HPF for call */ + mask = BIT(CPCAP_BIT_MIC2_CDC_EN) | BIT(CPCAP_BIT_CDC_EN_RX) | + BIT(CPCAP_BIT_AUDOHPF_1) | BIT(CPCAP_BIT_AUDOHPF_0) | + BIT(CPCAP_BIT_AUDIHPF_1) | BIT(CPCAP_BIT_AUDIHPF_0); + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CC, + mask, voice_call ? mask : 0); + if (err) + return err; + + /* Enable modem to codec CDC for call*/ + mask = BIT(CPCAP_BIT_CDC_CLK_EN); + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, + mask, voice_call ? mask : 0); + + return err; +} + +static int cpcap_voice_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + int err, ts_mask, mask; + bool voice_call; + + /* + * Primitive test for voice call, probably needs more checks + * later on for 16-bit calls detected, Bluetooth headset etc. + */ + if (tx_mask == 0 && rx_mask == 1 && slot_width == 8) + voice_call = true; + else + voice_call = false; + + ts_mask = 0x7 << CPCAP_BIT_MIC2_TIMESLOT0; + ts_mask |= 0x7 << CPCAP_BIT_MIC1_RX_TIMESLOT0; + + mask = (tx_mask & 0x7) << CPCAP_BIT_MIC2_TIMESLOT0; + mask |= (rx_mask & 0x7) << CPCAP_BIT_MIC1_RX_TIMESLOT0; + + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, + ts_mask, mask); + if (err) + return err; + + err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, slot_width * 1000); + if (err) + return err; + + err = cpcap_voice_call(cpcap, dai, voice_call); + if (err) + return err; + + return 0; +} + +static int cpcap_voice_set_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + static const u16 reg = CPCAP_REG_RXCOA; + static const u16 mask = BIT(CPCAP_BIT_CDC_SW); + u16 val; + + if (mute) + val = 0; + else + val = BIT(CPCAP_BIT_CDC_SW); + + dev_dbg(component->dev, "Voice mute: %d", mute); + return regmap_update_bits(cpcap->regmap, reg, mask, val); +}; + +static const struct snd_soc_dai_ops cpcap_dai_voice_ops = { + .hw_params = cpcap_voice_hw_params, + .set_sysclk = cpcap_voice_set_dai_sysclk, + .set_fmt = cpcap_voice_set_dai_fmt, + .set_tdm_slot = cpcap_voice_set_tdm_slot, + .mute_stream = cpcap_voice_set_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver cpcap_dai[] = { +{ + .id = 0, + .name = "cpcap-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE, + }, + .ops = &cpcap_dai_hifi_ops, +}, +{ + .id = 1, + .name = "cpcap-voice", + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Voice Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &cpcap_dai_voice_ops, +}, +}; + +static int cpcap_dai_mux(struct cpcap_audio *cpcap, bool swap_dai_configuration) +{ + u16 hifi_val, voice_val; + u16 hifi_mask = BIT(CPCAP_BIT_DIG_AUD_IN_ST_DAC); + u16 voice_mask = BIT(CPCAP_BIT_DIG_AUD_IN); + int err; + + + + if (!swap_dai_configuration) { + /* Codec on DAI0, HiFi on DAI1 */ + voice_val = 0; + hifi_val = hifi_mask; + } else { + /* Codec on DAI1, HiFi on DAI0 */ + voice_val = voice_mask; + hifi_val = 0; + } + + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_CDI, + voice_mask, voice_val); + if (err) + return err; + + err = regmap_update_bits(cpcap->regmap, CPCAP_REG_SDACDI, + hifi_mask, hifi_val); + if (err) + return err; + + return 0; +} + +static int cpcap_audio_reset(struct snd_soc_component *component, + bool swap_dai_configuration) +{ + struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component); + int i, err = 0; + + dev_dbg(component->dev, "init audio codec"); + + for (i = 0; i < ARRAY_SIZE(cpcap_default_regs); i++) { + err = regmap_update_bits(cpcap->regmap, + cpcap_default_regs[i].reg, + cpcap_default_regs[i].mask, + cpcap_default_regs[i].val); + if (err) + return err; + } + + /* setup default settings */ + err = cpcap_dai_mux(cpcap, swap_dai_configuration); + if (err) + return err; + + err = cpcap_set_sysclk(cpcap, CPCAP_DAI_HIFI, 0, 26000000); + if (err) + return err; + err = cpcap_set_sysclk(cpcap, CPCAP_DAI_VOICE, 0, 26000000); + if (err) + return err; + + err = cpcap_set_samprate(cpcap, CPCAP_DAI_HIFI, 48000); + if (err) + return err; + + err = cpcap_set_samprate(cpcap, CPCAP_DAI_VOICE, 48000); + if (err) + return err; + + return 0; +} + +static int cpcap_soc_probe(struct snd_soc_component *component) +{ + struct cpcap_audio *cpcap; + int err; + + cpcap = devm_kzalloc(component->dev, sizeof(*cpcap), GFP_KERNEL); + if (!cpcap) + return -ENOMEM; + snd_soc_component_set_drvdata(component, cpcap); + cpcap->component = component; + + cpcap->regmap = dev_get_regmap(component->dev->parent, NULL); + if (!cpcap->regmap) + return -ENODEV; + snd_soc_component_init_regmap(component, cpcap->regmap); + + err = cpcap_get_vendor(component->dev, cpcap->regmap, &cpcap->vendor); + if (err) + return err; + + return cpcap_audio_reset(component, false); +} + +static struct snd_soc_component_driver soc_codec_dev_cpcap = { + .probe = cpcap_soc_probe, + .controls = cpcap_snd_controls, + .num_controls = ARRAY_SIZE(cpcap_snd_controls), + .dapm_widgets = cpcap_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cpcap_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cpcap_codec_probe(struct platform_device *pdev) +{ + struct device_node *codec_node = + of_get_child_by_name(pdev->dev.parent->of_node, "audio-codec"); + if (!codec_node) + return -ENODEV; + + pdev->dev.of_node = codec_node; + + return devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_cpcap, + cpcap_dai, ARRAY_SIZE(cpcap_dai)); +} + +static struct platform_driver cpcap_codec_driver = { + .probe = cpcap_codec_probe, + .driver = { + .name = "cpcap-codec", + }, +}; +module_platform_driver(cpcap_codec_driver); + +MODULE_ALIAS("platform:cpcap-codec"); +MODULE_DESCRIPTION("ASoC CPCAP codec driver"); +MODULE_AUTHOR("Sebastian Reichel"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c new file mode 100644 index 0000000000..32b6a417d0 --- /dev/null +++ b/sound/soc/codecs/cq93vc.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ALSA SoC CQ0093 Voice Codec Driver for DaVinci platforms + * + * Copyright (C) 2010 Texas Instruments, Inc + * + * Author: Miguel Aguilar + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static const struct snd_kcontrol_new cq93vc_snd_controls[] = { + SOC_SINGLE("PGA Capture Volume", DAVINCI_VC_REG05, 0, 0x03, 0), + SOC_SINGLE("Mono DAC Playback Volume", DAVINCI_VC_REG09, 0, 0x3f, 0), +}; + +static int cq93vc_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u8 reg; + + if (mute) + reg = DAVINCI_VC_REG09_MUTE; + else + reg = 0; + + snd_soc_component_update_bits(component, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE, + reg); + + return 0; +} + +static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + switch (freq) { + case 22579200: + case 27000000: + case 33868800: + return 0; + } + + return -EINVAL; +} + +static int cq93vc_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_write(component, DAVINCI_VC_REG12, + DAVINCI_VC_REG12_POWER_ALL_ON); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + snd_soc_component_write(component, DAVINCI_VC_REG12, + DAVINCI_VC_REG12_POWER_ALL_OFF); + break; + case SND_SOC_BIAS_OFF: + /* force all power off */ + snd_soc_component_write(component, DAVINCI_VC_REG12, + DAVINCI_VC_REG12_POWER_ALL_OFF); + break; + } + + return 0; +} + +#define CQ93VC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000) +#define CQ93VC_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE) + +static const struct snd_soc_dai_ops cq93vc_dai_ops = { + .mute_stream = cq93vc_mute, + .set_sysclk = cq93vc_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver cq93vc_dai = { + .name = "cq93vc-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CQ93VC_RATES, + .formats = CQ93VC_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CQ93VC_RATES, + .formats = CQ93VC_FORMATS,}, + .ops = &cq93vc_dai_ops, +}; + +static int cq93vc_probe(struct snd_soc_component *component) +{ + struct davinci_vc *davinci_vc = component->dev->platform_data; + + snd_soc_component_init_regmap(component, davinci_vc->regmap); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_cq93vc = { + .set_bias_level = cq93vc_set_bias_level, + .probe = cq93vc_probe, + .controls = cq93vc_snd_controls, + .num_controls = ARRAY_SIZE(cq93vc_snd_controls), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cq93vc_platform_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_cq93vc, &cq93vc_dai, 1); +} + +static struct platform_driver cq93vc_codec_driver = { + .driver = { + .name = "cq93vc-codec", + }, + + .probe = cq93vc_platform_probe, +}; + +module_platform_driver(cq93vc_codec_driver); + +MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC CQ0093 Voice Codec Driver"); +MODULE_AUTHOR("Miguel Aguilar"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c new file mode 100644 index 0000000000..11e7b3f6d4 --- /dev/null +++ b/sound/soc/codecs/cros_ec_codec.c @@ -0,0 +1,1069 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google, Inc. + * + * ChromeOS Embedded Controller codec driver. + * + * This driver uses the cros-ec interface to communicate with the ChromeOS + * EC for audio function. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cros_ec_codec_priv { + struct device *dev; + struct cros_ec_device *ec_device; + + /* common */ + uint32_t ec_capabilities; + + uint64_t ec_shm_addr; + uint32_t ec_shm_len; + + uint64_t ap_shm_phys_addr; + uint32_t ap_shm_len; + uint64_t ap_shm_addr; + uint64_t ap_shm_last_alloc; + + /* DMIC */ + atomic_t dmic_probed; + + /* I2S_RX */ + uint32_t i2s_rx_bclk_ratio; + + /* WoV */ + bool wov_enabled; + uint8_t *wov_audio_shm_p; + uint32_t wov_audio_shm_len; + uint8_t wov_audio_shm_type; + uint8_t *wov_lang_shm_p; + uint32_t wov_lang_shm_len; + uint8_t wov_lang_shm_type; + + struct mutex wov_dma_lock; + uint8_t wov_buf[64000]; + uint32_t wov_rp, wov_wp; + size_t wov_dma_offset; + bool wov_burst_read; + struct snd_pcm_substream *wov_substream; + struct delayed_work wov_copy_work; + struct notifier_block wov_notifier; +}; + +static int ec_codec_capable(struct cros_ec_codec_priv *priv, uint8_t cap) +{ + return priv->ec_capabilities & BIT(cap); +} + +static int send_ec_host_command(struct cros_ec_device *ec_dev, uint32_t cmd, + uint8_t *out, size_t outsize, + uint8_t *in, size_t insize) +{ + int ret; + struct cros_ec_command *msg; + + msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = 0; + msg->command = cmd; + msg->outsize = outsize; + msg->insize = insize; + + if (outsize) + memcpy(msg->data, out, outsize); + + ret = cros_ec_cmd_xfer_status(ec_dev, msg); + if (ret < 0) + goto error; + + if (in && insize) + memcpy(in, msg->data, insize); + + ret = 0; +error: + kfree(msg); + return ret; +} + +static int dmic_get_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_dmic p; + struct ec_response_ec_codec_dmic_get_gain_idx r; + int ret; + + p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX; + p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret < 0) + return ret; + ucontrol->value.integer.value[0] = r.gain; + + p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX; + p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret < 0) + return ret; + ucontrol->value.integer.value[1] = r.gain; + + return 0; +} + +static int dmic_put_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct soc_mixer_control *control = + (struct soc_mixer_control *)kcontrol->private_value; + int max_dmic_gain = control->max; + int left = ucontrol->value.integer.value[0]; + int right = ucontrol->value.integer.value[1]; + struct ec_param_ec_codec_dmic p; + int ret; + + if (left > max_dmic_gain || right > max_dmic_gain) + return -EINVAL; + + dev_dbg(component->dev, "set mic gain to %u, %u\n", left, right); + + p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX; + p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0; + p.set_gain_idx_param.gain = left; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret < 0) + return ret; + + p.cmd = EC_CODEC_DMIC_SET_GAIN_IDX; + p.set_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_1; + p.set_gain_idx_param.gain = right; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), NULL, 0); +} + +static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 100, 0); + +enum { + DMIC_CTL_GAIN = 0, +}; + +static struct snd_kcontrol_new dmic_controls[] = { + [DMIC_CTL_GAIN] = + SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, + 0, 0, 0, dmic_get_gain, dmic_put_gain, + dmic_gain_tlv), +}; + +static int dmic_probe(struct snd_soc_component *component) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct device *dev = priv->dev; + struct soc_mixer_control *control; + struct ec_param_ec_codec_dmic p; + struct ec_response_ec_codec_dmic_get_max_gain r; + int ret; + + if (!atomic_add_unless(&priv->dmic_probed, 1, 1)) + return 0; + + p.cmd = EC_CODEC_DMIC_GET_MAX_GAIN; + + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_DMIC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret < 0) { + dev_warn(dev, "get_max_gain() unsupported\n"); + return 0; + } + + dev_dbg(dev, "max gain = %d\n", r.max_gain); + + control = (struct soc_mixer_control *) + dmic_controls[DMIC_CTL_GAIN].private_value; + control->max = r.max_gain; + control->platform_max = r.max_gain; + + return snd_soc_add_component_controls(component, + &dmic_controls[DMIC_CTL_GAIN], 1); +} + +static int i2s_rx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_i2s_rx p; + enum ec_codec_i2s_rx_sample_depth depth; + uint32_t bclk; + int ret; + + if (params_rate(params) != 48000) + return -EINVAL; + + switch (params_width(params)) { + case 16: + depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_16; + break; + case 24: + depth = EC_CODEC_I2S_RX_SAMPLE_DEPTH_24; + break; + default: + return -EINVAL; + } + + dev_dbg(component->dev, "set depth to %u\n", depth); + + p.cmd = EC_CODEC_I2S_RX_SET_SAMPLE_DEPTH; + p.set_sample_depth_param.depth = depth; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret < 0) + return ret; + + if (priv->i2s_rx_bclk_ratio) + bclk = params_rate(params) * priv->i2s_rx_bclk_ratio; + else + bclk = snd_soc_params_to_bclk(params); + + dev_dbg(component->dev, "set bclk to %u\n", bclk); + + p.cmd = EC_CODEC_I2S_RX_SET_BCLK; + p.set_bclk_param.bclk = bclk; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); +} + +static int i2s_rx_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + + priv->i2s_rx_bclk_ratio = ratio; + return 0; +} + +static int i2s_rx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_i2s_rx p; + enum ec_codec_i2s_rx_daifmt daifmt; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + daifmt = EC_CODEC_I2S_RX_DAIFMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + daifmt = EC_CODEC_I2S_RX_DAIFMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + daifmt = EC_CODEC_I2S_RX_DAIFMT_LEFT_J; + break; + default: + return -EINVAL; + } + + dev_dbg(component->dev, "set format to %u\n", daifmt); + + p.cmd = EC_CODEC_I2S_RX_SET_DAIFMT; + p.set_daifmt_param.daifmt = daifmt; + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); +} + +static const struct snd_soc_dai_ops i2s_rx_dai_ops = { + .hw_params = i2s_rx_hw_params, + .set_fmt = i2s_rx_set_fmt, + .set_bclk_ratio = i2s_rx_set_bclk_ratio, +}; + +static int i2s_rx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_i2s_rx p = {}; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(component->dev, "enable I2S RX\n"); + p.cmd = EC_CODEC_I2S_RX_ENABLE; + break; + case SND_SOC_DAPM_PRE_PMD: + dev_dbg(component->dev, "disable I2S RX\n"); + p.cmd = EC_CODEC_I2S_RX_DISABLE; + break; + default: + return 0; + } + + return send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); +} + +static struct snd_soc_dapm_widget i2s_rx_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_SUPPLY("I2S RX Enable", SND_SOC_NOPM, 0, 0, i2s_rx_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_AIF_OUT("I2S RX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static struct snd_soc_dapm_route i2s_rx_dapm_routes[] = { + {"I2S RX", NULL, "DMIC"}, + {"I2S RX", NULL, "I2S RX Enable"}, +}; + +static struct snd_soc_dai_driver i2s_rx_dai_driver = { + .name = "EC Codec I2S RX", + .capture = { + .stream_name = "I2S Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &i2s_rx_dai_ops, +}; + +static int i2s_rx_probe(struct snd_soc_component *component) +{ + return dmic_probe(component); +} + +static const struct snd_soc_component_driver i2s_rx_component_driver = { + .probe = i2s_rx_probe, + .dapm_widgets = i2s_rx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(i2s_rx_dapm_widgets), + .dapm_routes = i2s_rx_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(i2s_rx_dapm_routes), + .endianness = 1, +}; + +static void *wov_map_shm(struct cros_ec_codec_priv *priv, + uint8_t shm_id, uint32_t *len, uint8_t *type) +{ + struct ec_param_ec_codec p; + struct ec_response_ec_codec_get_shm_addr r; + uint32_t req, offset; + + p.cmd = EC_CODEC_GET_SHM_ADDR; + p.get_shm_addr_param.shm_id = shm_id; + if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)) < 0) { + dev_err(priv->dev, "failed to EC_CODEC_GET_SHM_ADDR\n"); + return NULL; + } + + dev_dbg(priv->dev, "phys_addr=%#llx, len=%#x\n", r.phys_addr, r.len); + + *len = r.len; + *type = r.type; + + switch (r.type) { + case EC_CODEC_SHM_TYPE_EC_RAM: + return (void __force *)devm_ioremap_wc(priv->dev, + r.phys_addr + priv->ec_shm_addr, r.len); + case EC_CODEC_SHM_TYPE_SYSTEM_RAM: + if (r.phys_addr) { + dev_err(priv->dev, "unknown status\n"); + return NULL; + } + + req = round_up(r.len, PAGE_SIZE); + dev_dbg(priv->dev, "round up from %u to %u\n", r.len, req); + + if (priv->ap_shm_last_alloc + req > + priv->ap_shm_phys_addr + priv->ap_shm_len) { + dev_err(priv->dev, "insufficient space for AP SHM\n"); + return NULL; + } + + dev_dbg(priv->dev, "alloc AP SHM addr=%#llx, len=%#x\n", + priv->ap_shm_last_alloc, req); + + p.cmd = EC_CODEC_SET_SHM_ADDR; + p.set_shm_addr_param.phys_addr = priv->ap_shm_last_alloc; + p.set_shm_addr_param.len = req; + p.set_shm_addr_param.shm_id = shm_id; + if (send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, + (uint8_t *)&p, sizeof(p), + NULL, 0) < 0) { + dev_err(priv->dev, "failed to EC_CODEC_SET_SHM_ADDR\n"); + return NULL; + } + + /* + * Note: EC codec only requests for `r.len' but we allocate + * round up PAGE_SIZE `req'. + */ + offset = priv->ap_shm_last_alloc - priv->ap_shm_phys_addr; + priv->ap_shm_last_alloc += req; + + return (void *)(uintptr_t)(priv->ap_shm_addr + offset); + default: + return NULL; + } +} + +static bool wov_queue_full(struct cros_ec_codec_priv *priv) +{ + return ((priv->wov_wp + 1) % sizeof(priv->wov_buf)) == priv->wov_rp; +} + +static size_t wov_queue_size(struct cros_ec_codec_priv *priv) +{ + if (priv->wov_wp >= priv->wov_rp) + return priv->wov_wp - priv->wov_rp; + else + return sizeof(priv->wov_buf) - priv->wov_rp + priv->wov_wp; +} + +static void wov_queue_dequeue(struct cros_ec_codec_priv *priv, size_t len) +{ + struct snd_pcm_runtime *runtime = priv->wov_substream->runtime; + size_t req; + + while (len) { + req = min(len, runtime->dma_bytes - priv->wov_dma_offset); + if (priv->wov_wp >= priv->wov_rp) + req = min(req, (size_t)priv->wov_wp - priv->wov_rp); + else + req = min(req, sizeof(priv->wov_buf) - priv->wov_rp); + + memcpy(runtime->dma_area + priv->wov_dma_offset, + priv->wov_buf + priv->wov_rp, req); + + priv->wov_dma_offset += req; + if (priv->wov_dma_offset == runtime->dma_bytes) + priv->wov_dma_offset = 0; + + priv->wov_rp += req; + if (priv->wov_rp == sizeof(priv->wov_buf)) + priv->wov_rp = 0; + + len -= req; + } + + snd_pcm_period_elapsed(priv->wov_substream); +} + +static void wov_queue_try_dequeue(struct cros_ec_codec_priv *priv) +{ + size_t period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream); + + while (period_bytes && wov_queue_size(priv) >= period_bytes) { + wov_queue_dequeue(priv, period_bytes); + period_bytes = snd_pcm_lib_period_bytes(priv->wov_substream); + } +} + +static void wov_queue_enqueue(struct cros_ec_codec_priv *priv, + uint8_t *addr, size_t len, bool iomem) +{ + size_t req; + + while (len) { + if (wov_queue_full(priv)) { + wov_queue_try_dequeue(priv); + + if (wov_queue_full(priv)) { + dev_err(priv->dev, "overrun detected\n"); + return; + } + } + + if (priv->wov_wp >= priv->wov_rp) + req = sizeof(priv->wov_buf) - priv->wov_wp; + else + /* Note: waste 1-byte to differentiate full and empty */ + req = priv->wov_rp - priv->wov_wp - 1; + req = min(req, len); + + if (iomem) + memcpy_fromio(priv->wov_buf + priv->wov_wp, + (void __force __iomem *)addr, req); + else + memcpy(priv->wov_buf + priv->wov_wp, addr, req); + + priv->wov_wp += req; + if (priv->wov_wp == sizeof(priv->wov_buf)) + priv->wov_wp = 0; + + addr += req; + len -= req; + } + + wov_queue_try_dequeue(priv); +} + +static int wov_read_audio_shm(struct cros_ec_codec_priv *priv) +{ + struct ec_param_ec_codec_wov p; + struct ec_response_ec_codec_wov_read_audio_shm r; + int ret; + + p.cmd = EC_CODEC_WOV_READ_AUDIO_SHM; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) { + dev_err(priv->dev, "failed to EC_CODEC_WOV_READ_AUDIO_SHM\n"); + return ret; + } + + if (!r.len) + dev_dbg(priv->dev, "no data, sleep\n"); + else + wov_queue_enqueue(priv, priv->wov_audio_shm_p + r.offset, r.len, + priv->wov_audio_shm_type == EC_CODEC_SHM_TYPE_EC_RAM); + return -EAGAIN; +} + +static int wov_read_audio(struct cros_ec_codec_priv *priv) +{ + struct ec_param_ec_codec_wov p; + struct ec_response_ec_codec_wov_read_audio r; + int remain = priv->wov_burst_read ? 16000 : 320; + int ret; + + while (remain >= 0) { + p.cmd = EC_CODEC_WOV_READ_AUDIO; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) { + dev_err(priv->dev, + "failed to EC_CODEC_WOV_READ_AUDIO\n"); + return ret; + } + + if (!r.len) { + dev_dbg(priv->dev, "no data, sleep\n"); + priv->wov_burst_read = false; + break; + } + + wov_queue_enqueue(priv, r.buf, r.len, false); + remain -= r.len; + } + + return -EAGAIN; +} + +static void wov_copy_work(struct work_struct *w) +{ + struct cros_ec_codec_priv *priv = + container_of(w, struct cros_ec_codec_priv, wov_copy_work.work); + int ret; + + mutex_lock(&priv->wov_dma_lock); + if (!priv->wov_substream) { + dev_warn(priv->dev, "no pcm substream\n"); + goto leave; + } + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) + ret = wov_read_audio_shm(priv); + else + ret = wov_read_audio(priv); + + if (ret == -EAGAIN) + schedule_delayed_work(&priv->wov_copy_work, + msecs_to_jiffies(10)); + else if (ret) + dev_err(priv->dev, "failed to read audio data\n"); +leave: + mutex_unlock(&priv->wov_dma_lock); +} + +static int wov_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = priv->wov_enabled; + return 0; +} + +static int wov_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_priv *priv = snd_soc_component_get_drvdata(c); + int enabled = ucontrol->value.integer.value[0]; + struct ec_param_ec_codec_wov p; + int ret; + + if (priv->wov_enabled != enabled) { + if (enabled) + p.cmd = EC_CODEC_WOV_ENABLE; + else + p.cmd = EC_CODEC_WOV_DISABLE; + + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret) { + dev_err(priv->dev, "failed to %s wov\n", + enabled ? "enable" : "disable"); + return ret; + } + + priv->wov_enabled = enabled; + } + + return 0; +} + +static int wov_set_lang_shm(struct cros_ec_codec_priv *priv, + uint8_t *buf, size_t size, uint8_t *digest) +{ + struct ec_param_ec_codec_wov p; + struct ec_param_ec_codec_wov_set_lang_shm *pp = &p.set_lang_shm_param; + int ret; + + if (size > priv->wov_lang_shm_len) { + dev_err(priv->dev, "no enough SHM size: %d\n", + priv->wov_lang_shm_len); + return -EIO; + } + + switch (priv->wov_lang_shm_type) { + case EC_CODEC_SHM_TYPE_EC_RAM: + memcpy_toio((void __force __iomem *)priv->wov_lang_shm_p, + buf, size); + memset_io((void __force __iomem *)priv->wov_lang_shm_p + size, + 0, priv->wov_lang_shm_len - size); + break; + case EC_CODEC_SHM_TYPE_SYSTEM_RAM: + memcpy(priv->wov_lang_shm_p, buf, size); + memset(priv->wov_lang_shm_p + size, 0, + priv->wov_lang_shm_len - size); + + /* make sure write to memory before calling host command */ + wmb(); + break; + } + + p.cmd = EC_CODEC_WOV_SET_LANG_SHM; + memcpy(pp->hash, digest, SHA256_DIGEST_SIZE); + pp->total_len = size; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret) { + dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG_SHM\n"); + return ret; + } + + return 0; +} + +static int wov_set_lang(struct cros_ec_codec_priv *priv, + uint8_t *buf, size_t size, uint8_t *digest) +{ + struct ec_param_ec_codec_wov p; + struct ec_param_ec_codec_wov_set_lang *pp = &p.set_lang_param; + size_t i, req; + int ret; + + for (i = 0; i < size; i += req) { + req = min(size - i, ARRAY_SIZE(pp->buf)); + + p.cmd = EC_CODEC_WOV_SET_LANG; + memcpy(pp->hash, digest, SHA256_DIGEST_SIZE); + pp->total_len = size; + pp->offset = i; + memcpy(pp->buf, buf + i, req); + pp->len = req; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret) { + dev_err(priv->dev, "failed to EC_CODEC_WOV_SET_LANG\n"); + return ret; + } + } + + return 0; +} + +static int wov_hotword_model_put(struct snd_kcontrol *kcontrol, + const unsigned int __user *bytes, + unsigned int size) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + struct ec_param_ec_codec_wov p; + struct ec_response_ec_codec_wov_get_lang r; + uint8_t digest[SHA256_DIGEST_SIZE]; + uint8_t *buf; + int ret; + + /* Skips the TLV header. */ + bytes += 2; + size -= 8; + + dev_dbg(priv->dev, "%s: size=%d\n", __func__, size); + + buf = memdup_user(bytes, size); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + sha256(buf, size, digest); + dev_dbg(priv->dev, "hash=%*phN\n", SHA256_DIGEST_SIZE, digest); + + p.cmd = EC_CODEC_WOV_GET_LANG; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_WOV, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) + goto leave; + + if (memcmp(digest, r.hash, SHA256_DIGEST_SIZE) == 0) { + dev_dbg(priv->dev, "not updated"); + goto leave; + } + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) + ret = wov_set_lang_shm(priv, buf, size, digest); + else + ret = wov_set_lang(priv, buf, size, digest); + +leave: + kfree(buf); + return ret; +} + +static struct snd_kcontrol_new wov_controls[] = { + SOC_SINGLE_BOOL_EXT("Wake-on-Voice Switch", 0, + wov_enable_get, wov_enable_put), + SND_SOC_BYTES_TLV("Hotword Model", 0x11000, NULL, + wov_hotword_model_put), +}; + +static struct snd_soc_dai_driver wov_dai_driver = { + .name = "Wake on Voice", + .capture = { + .stream_name = "WoV Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static int wov_host_event(struct notifier_block *nb, + unsigned long queued_during_suspend, void *notify) +{ + struct cros_ec_codec_priv *priv = + container_of(nb, struct cros_ec_codec_priv, wov_notifier); + u32 host_event; + + dev_dbg(priv->dev, "%s\n", __func__); + + host_event = cros_ec_get_host_event(priv->ec_device); + if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_WOV)) { + schedule_delayed_work(&priv->wov_copy_work, 0); + return NOTIFY_OK; + } else { + return NOTIFY_DONE; + } +} + +static int wov_probe(struct snd_soc_component *component) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + int ret; + + mutex_init(&priv->wov_dma_lock); + INIT_DELAYED_WORK(&priv->wov_copy_work, wov_copy_work); + + priv->wov_notifier.notifier_call = wov_host_event; + ret = blocking_notifier_chain_register( + &priv->ec_device->event_notifier, &priv->wov_notifier); + if (ret) + return ret; + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_LANG_SHM)) { + priv->wov_lang_shm_p = wov_map_shm(priv, + EC_CODEC_SHM_ID_WOV_LANG, + &priv->wov_lang_shm_len, + &priv->wov_lang_shm_type); + if (!priv->wov_lang_shm_p) + return -EFAULT; + } + + if (ec_codec_capable(priv, EC_CODEC_CAP_WOV_AUDIO_SHM)) { + priv->wov_audio_shm_p = wov_map_shm(priv, + EC_CODEC_SHM_ID_WOV_AUDIO, + &priv->wov_audio_shm_len, + &priv->wov_audio_shm_type); + if (!priv->wov_audio_shm_p) + return -EFAULT; + } + + return dmic_probe(component); +} + +static void wov_remove(struct snd_soc_component *component) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + + blocking_notifier_chain_unregister( + &priv->ec_device->event_notifier, &priv->wov_notifier); +} + +static int wov_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hw_param = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_16000, + .channels_min = 1, + .channels_max = 1, + .period_bytes_min = PAGE_SIZE, + .period_bytes_max = 0x20000 / 8, + .periods_min = 8, + .periods_max = 8, + .buffer_bytes_max = 0x20000, + }; + + return snd_soc_set_runtime_hwparams(substream, &hw_param); +} + +static int wov_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + + mutex_lock(&priv->wov_dma_lock); + priv->wov_substream = substream; + priv->wov_rp = priv->wov_wp = 0; + priv->wov_dma_offset = 0; + priv->wov_burst_read = true; + mutex_unlock(&priv->wov_dma_lock); + + return 0; +} + +static int wov_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + + mutex_lock(&priv->wov_dma_lock); + wov_queue_dequeue(priv, wov_queue_size(priv)); + priv->wov_substream = NULL; + mutex_unlock(&priv->wov_dma_lock); + + cancel_delayed_work_sync(&priv->wov_copy_work); + + return 0; +} + +static snd_pcm_uframes_t wov_pcm_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct cros_ec_codec_priv *priv = + snd_soc_component_get_drvdata(component); + + return bytes_to_frames(runtime, priv->wov_dma_offset); +} + +static int wov_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC, + NULL, 0, 0); + return 0; +} + +static const struct snd_soc_component_driver wov_component_driver = { + .probe = wov_probe, + .remove = wov_remove, + .controls = wov_controls, + .num_controls = ARRAY_SIZE(wov_controls), + .open = wov_pcm_open, + .hw_params = wov_pcm_hw_params, + .hw_free = wov_pcm_hw_free, + .pointer = wov_pcm_pointer, + .pcm_construct = wov_pcm_new, +}; + +static int cros_ec_codec_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cros_ec_device *ec_device = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_codec_priv *priv; + struct ec_param_ec_codec p; + struct ec_response_ec_codec_get_capabilities r; + int ret; +#ifdef CONFIG_OF + struct device_node *node; + struct resource res; + u64 ec_shm_size; + const __be32 *regaddr_p; +#endif + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + +#ifdef CONFIG_OF + regaddr_p = of_get_address(dev->of_node, 0, &ec_shm_size, NULL); + if (regaddr_p) { + priv->ec_shm_addr = of_read_number(regaddr_p, 2); + priv->ec_shm_len = ec_shm_size; + + dev_dbg(dev, "ec_shm_addr=%#llx len=%#x\n", + priv->ec_shm_addr, priv->ec_shm_len); + } + + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (node) { + ret = of_address_to_resource(node, 0, &res); + if (!ret) { + priv->ap_shm_phys_addr = res.start; + priv->ap_shm_len = resource_size(&res); + priv->ap_shm_addr = + (uint64_t)(uintptr_t)devm_ioremap_wc( + dev, priv->ap_shm_phys_addr, + priv->ap_shm_len); + priv->ap_shm_last_alloc = priv->ap_shm_phys_addr; + + dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n", + priv->ap_shm_phys_addr, priv->ap_shm_len); + } + of_node_put(node); + } +#endif + + priv->dev = dev; + priv->ec_device = ec_device; + atomic_set(&priv->dmic_probed, 0); + + p.cmd = EC_CODEC_GET_CAPABILITIES; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC, + (uint8_t *)&p, sizeof(p), + (uint8_t *)&r, sizeof(r)); + if (ret) { + dev_err(dev, "failed to EC_CODEC_GET_CAPABILITIES\n"); + return ret; + } + priv->ec_capabilities = r.capabilities; + + /* Reset EC codec i2s rx. */ + p.cmd = EC_CODEC_I2S_RX_RESET; + ret = send_ec_host_command(priv->ec_device, EC_CMD_EC_CODEC_I2S_RX, + (uint8_t *)&p, sizeof(p), NULL, 0); + if (ret == -ENOPROTOOPT) { + dev_info(dev, + "Missing reset command. Please update EC firmware.\n"); + } else if (ret) { + dev_err(dev, "failed to EC_CODEC_I2S_RESET: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, priv); + + ret = devm_snd_soc_register_component(dev, &i2s_rx_component_driver, + &i2s_rx_dai_driver, 1); + if (ret) + return ret; + + return devm_snd_soc_register_component(dev, &wov_component_driver, + &wov_dai_driver, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id cros_ec_codec_of_match[] = { + { .compatible = "google,cros-ec-codec" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id cros_ec_codec_acpi_id[] = { + { "GOOG0013", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_ec_codec_acpi_id); +#endif + +static struct platform_driver cros_ec_codec_platform_driver = { + .driver = { + .name = "cros-ec-codec", + .of_match_table = of_match_ptr(cros_ec_codec_of_match), + .acpi_match_table = ACPI_PTR(cros_ec_codec_acpi_id), + }, + .probe = cros_ec_codec_platform_probe, +}; + +module_platform_driver(cros_ec_codec_platform_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS EC codec driver"); +MODULE_AUTHOR("Cheng-Yi Chiang "); +MODULE_ALIAS("platform:cros-ec-codec"); diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c new file mode 100644 index 0000000000..6e658bb16f --- /dev/null +++ b/sound/soc/codecs/cs35l32.c @@ -0,0 +1,583 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs35l32.c -- CS35L32 ALSA SoC audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l32.h" +#include "cirrus_legacy.h" + +#define CS35L32_NUM_SUPPLIES 2 +static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = { + "VA", + "VP", +}; + +struct cs35l32_private { + struct regmap *regmap; + struct snd_soc_component *component; + struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES]; + struct cs35l32_platform_data pdata; + struct gpio_desc *reset_gpio; +}; + +static const struct reg_default cs35l32_reg_defaults[] = { + + { 0x06, 0x04 }, /* Power Ctl 1 */ + { 0x07, 0xE8 }, /* Power Ctl 2 */ + { 0x08, 0x40 }, /* Clock Ctl */ + { 0x09, 0x20 }, /* Low Battery Threshold */ + { 0x0A, 0x00 }, /* Voltage Monitor [RO] */ + { 0x0B, 0x40 }, /* Conv Peak Curr Protection CTL */ + { 0x0C, 0x07 }, /* IMON Scaling */ + { 0x0D, 0x03 }, /* Audio/LED Pwr Manager */ + { 0x0F, 0x20 }, /* Serial Port Control */ + { 0x10, 0x14 }, /* Class D Amp CTL */ + { 0x11, 0x00 }, /* Protection Release CTL */ + { 0x12, 0xFF }, /* Interrupt Mask 1 */ + { 0x13, 0xFF }, /* Interrupt Mask 2 */ + { 0x14, 0xFF }, /* Interrupt Mask 3 */ + { 0x19, 0x00 }, /* LED Flash Mode Current */ + { 0x1A, 0x00 }, /* LED Movie Mode Current */ + { 0x1B, 0x20 }, /* LED Flash Timer */ + { 0x1C, 0x00 }, /* LED Flash Inhibit Current */ +}; + +static bool cs35l32_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L32_DEVID_AB ... CS35L32_AUDIO_LED_MNGR: + case CS35L32_ADSP_CTL ... CS35L32_FLASH_INHIBIT: + return true; + default: + return false; + } +} + +static bool cs35l32_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L32_DEVID_AB ... CS35L32_REV_ID: + case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS: + return true; + default: + return false; + } +} + +static bool cs35l32_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS: + return true; + default: + return false; + } +} + +static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 300, 0); + +static const struct snd_kcontrol_new imon_ctl = + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 6, 1, 1); + +static const struct snd_kcontrol_new vmon_ctl = + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 7, 1, 1); + +static const struct snd_kcontrol_new vpmon_ctl = + SOC_DAPM_SINGLE("Switch", CS35L32_PWRCTL2, 5, 1, 1); + +static const struct snd_kcontrol_new cs35l32_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", CS35L32_CLASSD_CTL, + 3, 0x04, 1, classd_ctl_tlv), + SOC_SINGLE("Zero Cross Switch", CS35L32_CLASSD_CTL, 2, 1, 0), + SOC_SINGLE("Gain Manager Switch", CS35L32_AUDIO_LED_MNGR, 3, 1, 0), +}; + +static const struct snd_soc_dapm_widget cs35l32_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("BOOST", CS35L32_PWRCTL1, 2, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Speaker", CS35L32_PWRCTL1, 7, 1, NULL, 0), + + SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L32_PWRCTL2, 3, 1), + + SND_SOC_DAPM_INPUT("VP"), + SND_SOC_DAPM_INPUT("ISENSE"), + SND_SOC_DAPM_INPUT("VSENSE"), + + SND_SOC_DAPM_SWITCH("VMON ADC", CS35L32_PWRCTL2, 7, 1, &vmon_ctl), + SND_SOC_DAPM_SWITCH("IMON ADC", CS35L32_PWRCTL2, 6, 1, &imon_ctl), + SND_SOC_DAPM_SWITCH("VPMON ADC", CS35L32_PWRCTL2, 5, 1, &vpmon_ctl), +}; + +static const struct snd_soc_dapm_route cs35l32_audio_map[] = { + + {"Speaker", NULL, "BOOST"}, + + {"VMON ADC", NULL, "VSENSE"}, + {"IMON ADC", NULL, "ISENSE"}, + {"VPMON ADC", NULL, "VP"}, + + {"SDOUT", "Switch", "VMON ADC"}, + {"SDOUT", "Switch", "IMON ADC"}, + {"SDOUT", "Switch", "VPMON ADC"}, + + {"Capture", NULL, "SDOUT"}, +}; + +static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + snd_soc_component_update_bits(component, CS35L32_ADSP_CTL, + CS35L32_ADSP_MASTER_MASK, + CS35L32_ADSP_MASTER_MASK); + break; + case SND_SOC_DAIFMT_CBS_CFS: + snd_soc_component_update_bits(component, CS35L32_ADSP_CTL, + CS35L32_ADSP_MASTER_MASK, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_component *component = dai->component; + + return snd_soc_component_update_bits(component, CS35L32_PWRCTL2, + CS35L32_SDOUT_3ST, tristate << 3); +} + +static const struct snd_soc_dai_ops cs35l32_ops = { + .set_fmt = cs35l32_set_dai_fmt, + .set_tristate = cs35l32_set_tristate, +}; + +static struct snd_soc_dai_driver cs35l32_dai[] = { + { + .name = "cs35l32-monitor", + .id = 0, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = CS35L32_RATES, + .formats = CS35L32_FORMATS, + }, + .ops = &cs35l32_ops, + .symmetric_rate = 1, + } +}; + +static int cs35l32_component_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + unsigned int val; + + switch (freq) { + case 6000000: + val = CS35L32_MCLK_RATIO; + break; + case 12000000: + val = CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO; + break; + case 6144000: + val = 0; + break; + case 12288000: + val = CS35L32_MCLK_DIV2_MASK; + break; + default: + return -EINVAL; + } + + return snd_soc_component_update_bits(component, CS35L32_CLK_CTL, + CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val); +} + +static const struct snd_soc_component_driver soc_component_dev_cs35l32 = { + .set_sysclk = cs35l32_component_set_sysclk, + .controls = cs35l32_snd_controls, + .num_controls = ARRAY_SIZE(cs35l32_snd_controls), + .dapm_widgets = cs35l32_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l32_dapm_widgets), + .dapm_routes = cs35l32_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l32_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +/* Current and threshold powerup sequence Pg37 in datasheet */ +static const struct reg_sequence cs35l32_monitor_patch[] = { + + { 0x00, 0x99 }, + { 0x48, 0x17 }, + { 0x49, 0x56 }, + { 0x43, 0x01 }, + { 0x3B, 0x62 }, + { 0x3C, 0x80 }, + { 0x00, 0x00 }, +}; + +static const struct regmap_config cs35l32_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS35L32_MAX_REGISTER, + .reg_defaults = cs35l32_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l32_reg_defaults), + .volatile_reg = cs35l32_volatile_register, + .readable_reg = cs35l32_readable_register, + .precious_reg = cs35l32_precious_register, + .cache_type = REGCACHE_MAPLE, + + .use_single_read = true, + .use_single_write = true, +}; + +static int cs35l32_handle_of_data(struct i2c_client *i2c_client, + struct cs35l32_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + unsigned int val; + + if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0) + pdata->sdout_share = val; + + if (of_property_read_u32(np, "cirrus,boost-manager", &val)) + val = -1u; + + switch (val) { + case CS35L32_BOOST_MGR_AUTO: + case CS35L32_BOOST_MGR_AUTO_AUDIO: + case CS35L32_BOOST_MGR_BYPASS: + case CS35L32_BOOST_MGR_FIXED: + pdata->boost_mng = val; + break; + case -1u: + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,boost-manager DT value %d\n", val); + pdata->boost_mng = CS35L32_BOOST_MGR_BYPASS; + } + + if (of_property_read_u32(np, "cirrus,sdout-datacfg", &val)) + val = -1u; + switch (val) { + case CS35L32_DATA_CFG_LR_VP: + case CS35L32_DATA_CFG_LR_STAT: + case CS35L32_DATA_CFG_LR: + case CS35L32_DATA_CFG_LR_VPSTAT: + pdata->sdout_datacfg = val; + break; + case -1u: + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,sdout-datacfg DT value %d\n", val); + pdata->sdout_datacfg = CS35L32_DATA_CFG_LR; + } + + if (of_property_read_u32(np, "cirrus,battery-threshold", &val)) + val = -1u; + switch (val) { + case CS35L32_BATT_THRESH_3_1V: + case CS35L32_BATT_THRESH_3_2V: + case CS35L32_BATT_THRESH_3_3V: + case CS35L32_BATT_THRESH_3_4V: + pdata->batt_thresh = val; + break; + case -1u: + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,battery-threshold DT value %d\n", val); + pdata->batt_thresh = CS35L32_BATT_THRESH_3_3V; + } + + if (of_property_read_u32(np, "cirrus,battery-recovery", &val)) + val = -1u; + switch (val) { + case CS35L32_BATT_RECOV_3_1V: + case CS35L32_BATT_RECOV_3_2V: + case CS35L32_BATT_RECOV_3_3V: + case CS35L32_BATT_RECOV_3_4V: + case CS35L32_BATT_RECOV_3_5V: + case CS35L32_BATT_RECOV_3_6V: + pdata->batt_recov = val; + break; + case -1u: + default: + dev_err(&i2c_client->dev, + "Wrong cirrus,battery-recovery DT value %d\n", val); + pdata->batt_recov = CS35L32_BATT_RECOV_3_4V; + } + + return 0; +} + +static int cs35l32_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs35l32_private *cs35l32; + struct cs35l32_platform_data *pdata = + dev_get_platdata(&i2c_client->dev); + int ret, i, devid; + unsigned int reg; + + cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l32), GFP_KERNEL); + if (!cs35l32) + return -ENOMEM; + + i2c_set_clientdata(i2c_client, cs35l32); + + cs35l32->regmap = devm_regmap_init_i2c(i2c_client, &cs35l32_regmap); + if (IS_ERR(cs35l32->regmap)) { + ret = PTR_ERR(cs35l32->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + if (pdata) { + cs35l32->pdata = *pdata; + } else { + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (i2c_client->dev.of_node) { + ret = cs35l32_handle_of_data(i2c_client, + &cs35l32->pdata); + if (ret != 0) + return ret; + } + } + + for (i = 0; i < ARRAY_SIZE(cs35l32->supplies); i++) + cs35l32->supplies[i].supply = cs35l32_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c_client->dev, + ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to enable supplies: %d\n", ret); + return ret; + } + + /* Reset the Device */ + cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs35l32->reset_gpio)) { + ret = PTR_ERR(cs35l32->reset_gpio); + goto err_supplies; + } + + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); + + /* initialize codec */ + devid = cirrus_read_device_id(cs35l32->regmap, CS35L32_DEVID_AB); + if (devid < 0) { + ret = devid; + dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret); + goto err_disable; + } + + if (devid != CS35L32_CHIP_ID) { + ret = -ENODEV; + dev_err(&i2c_client->dev, + "CS35L32 Device ID (%X). Expected %X\n", + devid, CS35L32_CHIP_ID); + goto err_disable; + } + + ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, ®); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed\n"); + goto err_disable; + } + + ret = regmap_register_patch(cs35l32->regmap, cs35l32_monitor_patch, + ARRAY_SIZE(cs35l32_monitor_patch)); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to apply errata patch\n"); + goto err_disable; + } + + dev_info(&i2c_client->dev, + "Cirrus Logic CS35L32, Revision: %02X\n", reg & 0xFF); + + /* Setup VBOOST Management */ + if (cs35l32->pdata.boost_mng) + regmap_update_bits(cs35l32->regmap, CS35L32_AUDIO_LED_MNGR, + CS35L32_BOOST_MASK, + cs35l32->pdata.boost_mng); + + /* Setup ADSP Format Config */ + if (cs35l32->pdata.sdout_share) + regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL, + CS35L32_ADSP_SHARE_MASK, + cs35l32->pdata.sdout_share << 3); + + /* Setup ADSP Data Configuration */ + if (cs35l32->pdata.sdout_datacfg) + regmap_update_bits(cs35l32->regmap, CS35L32_ADSP_CTL, + CS35L32_ADSP_DATACFG_MASK, + cs35l32->pdata.sdout_datacfg << 4); + + /* Setup Low Battery Recovery */ + if (cs35l32->pdata.batt_recov) + regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD, + CS35L32_BATT_REC_MASK, + cs35l32->pdata.batt_recov << 1); + + /* Setup Low Battery Threshold */ + if (cs35l32->pdata.batt_thresh) + regmap_update_bits(cs35l32->regmap, CS35L32_BATT_THRESHOLD, + CS35L32_BATT_THRESH_MASK, + cs35l32->pdata.batt_thresh << 4); + + /* Power down the AMP */ + regmap_update_bits(cs35l32->regmap, CS35L32_PWRCTL1, CS35L32_PDN_AMP, + CS35L32_PDN_AMP); + + /* Clear MCLK Error Bit since we don't have the clock yet */ + regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, ®); + + ret = devm_snd_soc_register_component(&i2c_client->dev, + &soc_component_dev_cs35l32, cs35l32_dai, + ARRAY_SIZE(cs35l32_dai)); + if (ret < 0) + goto err_disable; + + return 0; + +err_disable: + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); +err_supplies: + regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + return ret; +} + +static void cs35l32_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client); + + /* Hold down reset */ + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); +} + +#ifdef CONFIG_PM +static int cs35l32_runtime_suspend(struct device *dev) +{ + struct cs35l32_private *cs35l32 = dev_get_drvdata(dev); + + regcache_cache_only(cs35l32->regmap, true); + regcache_mark_dirty(cs35l32->regmap); + + /* Hold down reset */ + gpiod_set_value_cansleep(cs35l32->reset_gpio, 0); + + /* remove power */ + regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + + return 0; +} + +static int cs35l32_runtime_resume(struct device *dev) +{ + struct cs35l32_private *cs35l32 = dev_get_drvdata(dev); + int ret; + + /* Enable power */ + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l32->supplies), + cs35l32->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", + ret); + return ret; + } + + gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); + + regcache_cache_only(cs35l32->regmap, false); + regcache_sync(cs35l32->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops cs35l32_runtime_pm = { + SET_RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume, + NULL) +}; + +static const struct of_device_id cs35l32_of_match[] = { + { .compatible = "cirrus,cs35l32", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l32_of_match); + + +static const struct i2c_device_id cs35l32_id[] = { + {"cs35l32", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs35l32_id); + +static struct i2c_driver cs35l32_i2c_driver = { + .driver = { + .name = "cs35l32", + .pm = &cs35l32_runtime_pm, + .of_match_table = cs35l32_of_match, + }, + .id_table = cs35l32_id, + .probe = cs35l32_i2c_probe, + .remove = cs35l32_i2c_remove, +}; + +module_i2c_driver(cs35l32_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L32 driver"); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l32.h b/sound/soc/codecs/cs35l32.h new file mode 100644 index 0000000000..9471a30e91 --- /dev/null +++ b/sound/soc/codecs/cs35l32.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * cs35l32.h -- CS35L32 ALSA SoC audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin + */ + +#ifndef __CS35L32_H__ +#define __CS35L32_H__ + +struct cs35l32_platform_data { + /* Low Battery Threshold */ + unsigned int batt_thresh; + /* Low Battery Recovery */ + unsigned int batt_recov; + /* LED Current Management*/ + unsigned int led_mng; + /* Audio Gain w/ LED */ + unsigned int audiogain_mng; + /* Boost Management */ + unsigned int boost_mng; + /* Data CFG for DUAL device */ + unsigned int sdout_datacfg; + /* SDOUT Sharing */ + unsigned int sdout_share; +}; + +#define CS35L32_CHIP_ID 0x00035A32 +#define CS35L32_DEVID_AB 0x01 /* Device ID A & B [RO] */ +#define CS35L32_DEVID_CD 0x02 /* Device ID C & D [RO] */ +#define CS35L32_DEVID_E 0x03 /* Device ID E [RO] */ +#define CS35L32_FAB_ID 0x04 /* Fab ID [RO] */ +#define CS35L32_REV_ID 0x05 /* Revision ID [RO] */ +#define CS35L32_PWRCTL1 0x06 /* Power Ctl 1 */ +#define CS35L32_PWRCTL2 0x07 /* Power Ctl 2 */ +#define CS35L32_CLK_CTL 0x08 /* Clock Ctl */ +#define CS35L32_BATT_THRESHOLD 0x09 /* Low Battery Threshold */ +#define CS35L32_VMON 0x0A /* Voltage Monitor [RO] */ +#define CS35L32_BST_CPCP_CTL 0x0B /* Conv Peak Curr Protection CTL */ +#define CS35L32_IMON_SCALING 0x0C /* IMON Scaling */ +#define CS35L32_AUDIO_LED_MNGR 0x0D /* Audio/LED Pwr Manager */ +#define CS35L32_ADSP_CTL 0x0F /* Serial Port Control */ +#define CS35L32_CLASSD_CTL 0x10 /* Class D Amp CTL */ +#define CS35L32_PROTECT_CTL 0x11 /* Protection Release CTL */ +#define CS35L32_INT_MASK_1 0x12 /* Interrupt Mask 1 */ +#define CS35L32_INT_MASK_2 0x13 /* Interrupt Mask 2 */ +#define CS35L32_INT_MASK_3 0x14 /* Interrupt Mask 3 */ +#define CS35L32_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */ +#define CS35L32_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */ +#define CS35L32_INT_STATUS_3 0x17 /* Interrupt Status 3 [RO] */ +#define CS35L32_LED_STATUS 0x18 /* LED Lighting Status [RO] */ +#define CS35L32_FLASH_MODE 0x19 /* LED Flash Mode Current */ +#define CS35L32_MOVIE_MODE 0x1A /* LED Movie Mode Current */ +#define CS35L32_FLASH_TIMER 0x1B /* LED Flash Timer */ +#define CS35L32_FLASH_INHIBIT 0x1C /* LED Flash Inhibit Current */ +#define CS35L32_MAX_REGISTER 0x1C + +#define CS35L32_MCLK_DIV2 0x01 +#define CS35L32_MCLK_RATIO 0x01 +#define CS35L32_MCLKDIS 0x80 +#define CS35L32_PDN_ALL 0x01 +#define CS35L32_PDN_AMP 0x80 +#define CS35L32_PDN_BOOST 0x04 +#define CS35L32_PDN_IMON 0x40 +#define CS35L32_PDN_VMON 0x80 +#define CS35L32_PDN_VPMON 0x20 +#define CS35L32_PDN_ADSP 0x08 + +#define CS35L32_MCLK_DIV2_MASK 0x40 +#define CS35L32_MCLK_RATIO_MASK 0x01 +#define CS35L32_MCLK_MASK 0x41 +#define CS35L32_ADSP_MASTER_MASK 0x40 +#define CS35L32_BOOST_MASK 0x03 +#define CS35L32_GAIN_MGR_MASK 0x08 +#define CS35L32_ADSP_SHARE_MASK 0x08 +#define CS35L32_ADSP_DATACFG_MASK 0x30 +#define CS35L32_SDOUT_3ST 0x08 +#define CS35L32_BATT_REC_MASK 0x0E +#define CS35L32_BATT_THRESH_MASK 0x30 + +#define CS35L32_RATES (SNDRV_PCM_RATE_48000) +#define CS35L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + + +#endif diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c new file mode 100644 index 0000000000..d25455f395 --- /dev/null +++ b/sound/soc/codecs/cs35l33.c @@ -0,0 +1,1291 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs35l33.c -- CS35L33 ALSA SoC audio driver + * + * Copyright 2016 Cirrus Logic, Inc. + * + * Author: Paul Handrigan + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l33.h" +#include "cirrus_legacy.h" + +#define CS35L33_BOOT_DELAY 50 + +struct cs35l33_private { + struct snd_soc_component *component; + struct cs35l33_pdata pdata; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + bool amp_cal; + int mclk_int; + struct regulator_bulk_data core_supplies[2]; + int num_core_supplies; + bool is_tdm_mode; + bool enable_soft_ramp; +}; + +static const struct reg_default cs35l33_reg[] = { + {CS35L33_PWRCTL1, 0x85}, + {CS35L33_PWRCTL2, 0xFE}, + {CS35L33_CLK_CTL, 0x0C}, + {CS35L33_BST_PEAK_CTL, 0x90}, + {CS35L33_PROTECT_CTL, 0x55}, + {CS35L33_BST_CTL1, 0x00}, + {CS35L33_BST_CTL2, 0x01}, + {CS35L33_ADSP_CTL, 0x00}, + {CS35L33_ADC_CTL, 0xC8}, + {CS35L33_DAC_CTL, 0x14}, + {CS35L33_DIG_VOL_CTL, 0x00}, + {CS35L33_CLASSD_CTL, 0x04}, + {CS35L33_AMP_CTL, 0x90}, + {CS35L33_INT_MASK_1, 0xFF}, + {CS35L33_INT_MASK_2, 0xFF}, + {CS35L33_DIAG_LOCK, 0x00}, + {CS35L33_DIAG_CTRL_1, 0x40}, + {CS35L33_DIAG_CTRL_2, 0x00}, + {CS35L33_HG_MEMLDO_CTL, 0x62}, + {CS35L33_HG_REL_RATE, 0x03}, + {CS35L33_LDO_DEL, 0x12}, + {CS35L33_HG_HEAD, 0x0A}, + {CS35L33_HG_EN, 0x05}, + {CS35L33_TX_VMON, 0x00}, + {CS35L33_TX_IMON, 0x03}, + {CS35L33_TX_VPMON, 0x02}, + {CS35L33_TX_VBSTMON, 0x05}, + {CS35L33_TX_FLAG, 0x06}, + {CS35L33_TX_EN1, 0x00}, + {CS35L33_TX_EN2, 0x00}, + {CS35L33_TX_EN3, 0x00}, + {CS35L33_TX_EN4, 0x00}, + {CS35L33_RX_AUD, 0x40}, + {CS35L33_RX_SPLY, 0x03}, + {CS35L33_RX_ALIVE, 0x04}, + {CS35L33_BST_CTL4, 0x63}, +}; + +static const struct reg_sequence cs35l33_patch[] = { + { 0x00, 0x99, 0 }, + { 0x59, 0x02, 0 }, + { 0x52, 0x30, 0 }, + { 0x39, 0x45, 0 }, + { 0x57, 0x30, 0 }, + { 0x2C, 0x68, 0 }, + { 0x00, 0x00, 0 }, +}; + +static bool cs35l33_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L33_DEVID_AB: + case CS35L33_DEVID_CD: + case CS35L33_DEVID_E: + case CS35L33_REV_ID: + case CS35L33_INT_STATUS_1: + case CS35L33_INT_STATUS_2: + case CS35L33_HG_STATUS: + return true; + default: + return false; + } +} + +static bool cs35l33_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + /* these are read only registers */ + case CS35L33_DEVID_AB: + case CS35L33_DEVID_CD: + case CS35L33_DEVID_E: + case CS35L33_REV_ID: + case CS35L33_INT_STATUS_1: + case CS35L33_INT_STATUS_2: + case CS35L33_HG_STATUS: + return false; + default: + return true; + } +} + +static bool cs35l33_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L33_DEVID_AB: + case CS35L33_DEVID_CD: + case CS35L33_DEVID_E: + case CS35L33_REV_ID: + case CS35L33_PWRCTL1: + case CS35L33_PWRCTL2: + case CS35L33_CLK_CTL: + case CS35L33_BST_PEAK_CTL: + case CS35L33_PROTECT_CTL: + case CS35L33_BST_CTL1: + case CS35L33_BST_CTL2: + case CS35L33_ADSP_CTL: + case CS35L33_ADC_CTL: + case CS35L33_DAC_CTL: + case CS35L33_DIG_VOL_CTL: + case CS35L33_CLASSD_CTL: + case CS35L33_AMP_CTL: + case CS35L33_INT_MASK_1: + case CS35L33_INT_MASK_2: + case CS35L33_INT_STATUS_1: + case CS35L33_INT_STATUS_2: + case CS35L33_DIAG_LOCK: + case CS35L33_DIAG_CTRL_1: + case CS35L33_DIAG_CTRL_2: + case CS35L33_HG_MEMLDO_CTL: + case CS35L33_HG_REL_RATE: + case CS35L33_LDO_DEL: + case CS35L33_HG_HEAD: + case CS35L33_HG_EN: + case CS35L33_TX_VMON: + case CS35L33_TX_IMON: + case CS35L33_TX_VPMON: + case CS35L33_TX_VBSTMON: + case CS35L33_TX_FLAG: + case CS35L33_TX_EN1: + case CS35L33_TX_EN2: + case CS35L33_TX_EN3: + case CS35L33_TX_EN4: + case CS35L33_RX_AUD: + case CS35L33_RX_SPLY: + case CS35L33_RX_ALIVE: + case CS35L33_BST_CTL4: + return true; + default: + return false; + } +} + +static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 100, 0); +static DECLARE_TLV_DB_SCALE(dac_tlv, -10200, 50, 0); + +static const struct snd_kcontrol_new cs35l33_snd_controls[] = { + + SOC_SINGLE_TLV("SPK Amp Volume", CS35L33_AMP_CTL, + 4, 0x09, 0, classd_ctl_tlv), + SOC_SINGLE_SX_TLV("DAC Volume", CS35L33_DIG_VOL_CTL, + 0, 0x34, 0xE4, dac_tlv), +}; + +static int cs35l33_spkrdrv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l33_private *priv = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!priv->amp_cal) { + usleep_range(8000, 9000); + priv->amp_cal = true; + regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL, + CS35L33_AMP_CAL, 0); + dev_dbg(component->dev, "Amp calibration done\n"); + } + dev_dbg(component->dev, "Amp turned on\n"); + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(component->dev, "Amp turned off\n"); + break; + default: + dev_err(component->dev, "Invalid event = 0x%x\n", event); + break; + } + + return 0; +} + +static int cs35l33_sdin_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l33_private *priv = snd_soc_component_get_drvdata(component); + unsigned int val; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(priv->regmap, CS35L33_PWRCTL1, + CS35L33_PDN_BST, 0); + val = priv->is_tdm_mode ? 0 : CS35L33_PDN_TDM; + regmap_update_bits(priv->regmap, CS35L33_PWRCTL2, + CS35L33_PDN_TDM, val); + dev_dbg(component->dev, "BST turned on\n"); + break; + case SND_SOC_DAPM_POST_PMU: + dev_dbg(component->dev, "SDIN turned on\n"); + if (!priv->amp_cal) { + regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL, + CS35L33_AMP_CAL, CS35L33_AMP_CAL); + dev_dbg(component->dev, "Amp calibration started\n"); + usleep_range(10000, 11000); + } + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(priv->regmap, CS35L33_PWRCTL2, + CS35L33_PDN_TDM, CS35L33_PDN_TDM); + usleep_range(4000, 4100); + regmap_update_bits(priv->regmap, CS35L33_PWRCTL1, + CS35L33_PDN_BST, CS35L33_PDN_BST); + dev_dbg(component->dev, "BST and SDIN turned off\n"); + break; + default: + dev_err(component->dev, "Invalid event = 0x%x\n", event); + + } + + return 0; +} + +static int cs35l33_sdout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l33_private *priv = snd_soc_component_get_drvdata(component); + unsigned int mask = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM; + unsigned int mask2 = CS35L33_SDOUT_3ST_TDM; + unsigned int val, val2; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (priv->is_tdm_mode) { + /* set sdout_3st_i2s and reset pdn_tdm */ + val = CS35L33_SDOUT_3ST_I2S; + /* reset sdout_3st_tdm */ + val2 = 0; + } else { + /* reset sdout_3st_i2s and set pdn_tdm */ + val = CS35L33_PDN_TDM; + /* set sdout_3st_tdm */ + val2 = CS35L33_SDOUT_3ST_TDM; + } + dev_dbg(component->dev, "SDOUT turned on\n"); + break; + case SND_SOC_DAPM_PRE_PMD: + val = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM; + val2 = CS35L33_SDOUT_3ST_TDM; + dev_dbg(component->dev, "SDOUT turned off\n"); + break; + default: + dev_err(component->dev, "Invalid event = 0x%x\n", event); + return 0; + } + + regmap_update_bits(priv->regmap, CS35L33_PWRCTL2, + mask, val); + regmap_update_bits(priv->regmap, CS35L33_CLK_CTL, + mask2, val2); + + return 0; +} + +static const struct snd_soc_dapm_widget cs35l33_dapm_widgets[] = { + + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_OUT_DRV_E("SPKDRV", CS35L33_PWRCTL1, 7, 1, NULL, 0, + cs35l33_spkrdrv_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L33_PWRCTL2, + 2, 1, cs35l33_sdin_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("MON"), + + SND_SOC_DAPM_ADC("VMON", NULL, + CS35L33_PWRCTL2, CS35L33_PDN_VMON_SHIFT, 1), + SND_SOC_DAPM_ADC("IMON", NULL, + CS35L33_PWRCTL2, CS35L33_PDN_IMON_SHIFT, 1), + SND_SOC_DAPM_ADC("VPMON", NULL, + CS35L33_PWRCTL2, CS35L33_PDN_VPMON_SHIFT, 1), + SND_SOC_DAPM_ADC("VBSTMON", NULL, + CS35L33_PWRCTL2, CS35L33_PDN_VBSTMON_SHIFT, 1), + + SND_SOC_DAPM_AIF_OUT_E("SDOUT", NULL, 0, SND_SOC_NOPM, 0, 0, + cs35l33_sdout_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD), +}; + +static const struct snd_soc_dapm_route cs35l33_audio_map[] = { + {"SDIN", NULL, "CS35L33 Playback"}, + {"SPKDRV", NULL, "SDIN"}, + {"SPK", NULL, "SPKDRV"}, + + {"VMON", NULL, "MON"}, + {"IMON", NULL, "MON"}, + + {"SDOUT", NULL, "VMON"}, + {"SDOUT", NULL, "IMON"}, + {"CS35L33 Capture", NULL, "SDOUT"}, +}; + +static const struct snd_soc_dapm_route cs35l33_vphg_auto_route[] = { + {"SPKDRV", NULL, "VPMON"}, + {"VPMON", NULL, "CS35L33 Playback"}, +}; + +static const struct snd_soc_dapm_route cs35l33_vp_vbst_mon_route[] = { + {"SDOUT", NULL, "VPMON"}, + {"VPMON", NULL, "MON"}, + {"SDOUT", NULL, "VBSTMON"}, + {"VBSTMON", NULL, "MON"}, +}; + +static int cs35l33_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + unsigned int val; + struct cs35l33_private *priv = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(priv->regmap, CS35L33_PWRCTL1, + CS35L33_PDN_ALL, 0); + regmap_update_bits(priv->regmap, CS35L33_CLK_CTL, + CS35L33_MCLKDIS, 0); + break; + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(priv->regmap, CS35L33_PWRCTL1, + CS35L33_PDN_ALL, CS35L33_PDN_ALL); + regmap_read(priv->regmap, CS35L33_INT_STATUS_2, &val); + usleep_range(1000, 1100); + if (val & CS35L33_PDN_DONE) + regmap_update_bits(priv->regmap, CS35L33_CLK_CTL, + CS35L33_MCLKDIS, CS35L33_MCLKDIS); + break; + case SND_SOC_BIAS_OFF: + break; + default: + return -EINVAL; + } + + return 0; +} + +struct cs35l33_mclk_div { + int mclk; + int srate; + u8 adsp_rate; + u8 int_fs_ratio; +}; + +static const struct cs35l33_mclk_div cs35l33_mclk_coeffs[] = { + /* MCLK, Sample Rate, adsp_rate, int_fs_ratio */ + {5644800, 11025, 0x4, CS35L33_INT_FS_RATE}, + {5644800, 22050, 0x8, CS35L33_INT_FS_RATE}, + {5644800, 44100, 0xC, CS35L33_INT_FS_RATE}, + + {6000000, 8000, 0x1, 0}, + {6000000, 11025, 0x2, 0}, + {6000000, 11029, 0x3, 0}, + {6000000, 12000, 0x4, 0}, + {6000000, 16000, 0x5, 0}, + {6000000, 22050, 0x6, 0}, + {6000000, 22059, 0x7, 0}, + {6000000, 24000, 0x8, 0}, + {6000000, 32000, 0x9, 0}, + {6000000, 44100, 0xA, 0}, + {6000000, 44118, 0xB, 0}, + {6000000, 48000, 0xC, 0}, + + {6144000, 8000, 0x1, CS35L33_INT_FS_RATE}, + {6144000, 12000, 0x4, CS35L33_INT_FS_RATE}, + {6144000, 16000, 0x5, CS35L33_INT_FS_RATE}, + {6144000, 24000, 0x8, CS35L33_INT_FS_RATE}, + {6144000, 32000, 0x9, CS35L33_INT_FS_RATE}, + {6144000, 48000, 0xC, CS35L33_INT_FS_RATE}, +}; + +static int cs35l33_get_mclk_coeff(int mclk, int srate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l33_mclk_coeffs); i++) { + if (cs35l33_mclk_coeffs[i].mclk == mclk && + cs35l33_mclk_coeffs[i].srate == srate) + return i; + } + return -EINVAL; +} + +static int cs35l33_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs35l33_private *priv = snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL, + CS35L33_MS_MASK, CS35L33_MS_MASK); + dev_dbg(component->dev, "Audio port in master mode\n"); + break; + case SND_SOC_DAIFMT_CBS_CFS: + regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL, + CS35L33_MS_MASK, 0); + dev_dbg(component->dev, "Audio port in slave mode\n"); + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + /* + * tdm mode in cs35l33 resembles dsp-a mode very + * closely, it is dsp-a with fsync shifted left by half bclk + */ + priv->is_tdm_mode = true; + dev_dbg(component->dev, "Audio port in TDM mode\n"); + break; + case SND_SOC_DAIFMT_I2S: + priv->is_tdm_mode = false; + dev_dbg(component->dev, "Audio port in I2S mode\n"); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs35l33_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs35l33_private *priv = snd_soc_component_get_drvdata(component); + int sample_size = params_width(params); + int coeff = cs35l33_get_mclk_coeff(priv->mclk_int, params_rate(params)); + + if (coeff < 0) + return coeff; + + regmap_update_bits(priv->regmap, CS35L33_CLK_CTL, + CS35L33_ADSP_FS | CS35L33_INT_FS_RATE, + cs35l33_mclk_coeffs[coeff].int_fs_ratio + | cs35l33_mclk_coeffs[coeff].adsp_rate); + + if (priv->is_tdm_mode) { + sample_size = (sample_size / 8) - 1; + if (sample_size > 2) + sample_size = 2; + regmap_update_bits(priv->regmap, CS35L33_RX_AUD, + CS35L33_AUDIN_RX_DEPTH, + sample_size << CS35L33_AUDIN_RX_DEPTH_SHIFT); + } + + dev_dbg(component->dev, "sample rate=%d, bits per sample=%d\n", + params_rate(params), params_width(params)); + + return 0; +} + +static const unsigned int cs35l33_src_rates[] = { + 8000, 11025, 11029, 12000, 16000, 22050, + 22059, 24000, 32000, 44100, 44118, 48000 +}; + +static const struct snd_pcm_hw_constraint_list cs35l33_constraints = { + .count = ARRAY_SIZE(cs35l33_src_rates), + .list = cs35l33_src_rates, +}; + +static int cs35l33_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &cs35l33_constraints); + return 0; +} + +static int cs35l33_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_component *component = dai->component; + struct cs35l33_private *priv = snd_soc_component_get_drvdata(component); + + if (tristate) { + regmap_update_bits(priv->regmap, CS35L33_PWRCTL2, + CS35L33_SDOUT_3ST_I2S, CS35L33_SDOUT_3ST_I2S); + regmap_update_bits(priv->regmap, CS35L33_CLK_CTL, + CS35L33_SDOUT_3ST_TDM, CS35L33_SDOUT_3ST_TDM); + } else { + regmap_update_bits(priv->regmap, CS35L33_PWRCTL2, + CS35L33_SDOUT_3ST_I2S, 0); + regmap_update_bits(priv->regmap, CS35L33_CLK_CTL, + CS35L33_SDOUT_3ST_TDM, 0); + } + + return 0; +} + +static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs35l33_private *priv = snd_soc_component_get_drvdata(component); + unsigned int reg, bit_pos, i; + int slot, slot_num; + + if (slot_width != 8) + return -EINVAL; + + /* scan rx_mask for aud slot */ + slot = ffs(rx_mask) - 1; + if (slot >= 0) { + regmap_update_bits(priv->regmap, CS35L33_RX_AUD, + CS35L33_X_LOC, slot); + dev_dbg(component->dev, "Audio starts from slots %d", slot); + } + + /* + * scan tx_mask: vmon(2 slots); imon (2 slots); + * vpmon (1 slot) vbstmon (1 slot) + */ + slot = ffs(tx_mask) - 1; + slot_num = 0; + + for (i = 0; i < 2 ; i++) { + /* disable vpmon/vbstmon: enable later if set in tx_mask */ + regmap_update_bits(priv->regmap, CS35L33_TX_VPMON + i, + CS35L33_X_STATE | CS35L33_X_LOC, CS35L33_X_STATE + | CS35L33_X_LOC); + } + + /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/ + snd_soc_dapm_del_routes(dapm, cs35l33_vp_vbst_mon_route, + ARRAY_SIZE(cs35l33_vp_vbst_mon_route)); + + while (slot >= 0) { + /* configure VMON_TX_LOC */ + if (slot_num == 0) { + regmap_update_bits(priv->regmap, CS35L33_TX_VMON, + CS35L33_X_STATE | CS35L33_X_LOC, slot); + dev_dbg(component->dev, "VMON enabled in slots %d-%d", + slot, slot + 1); + } + + /* configure IMON_TX_LOC */ + if (slot_num == 3) { + regmap_update_bits(priv->regmap, CS35L33_TX_IMON, + CS35L33_X_STATE | CS35L33_X_LOC, slot); + dev_dbg(component->dev, "IMON enabled in slots %d-%d", + slot, slot + 1); + } + + /* configure VPMON_TX_LOC */ + if (slot_num == 4) { + regmap_update_bits(priv->regmap, CS35L33_TX_VPMON, + CS35L33_X_STATE | CS35L33_X_LOC, slot); + snd_soc_dapm_add_routes(dapm, + &cs35l33_vp_vbst_mon_route[0], 2); + dev_dbg(component->dev, "VPMON enabled in slots %d", slot); + } + + /* configure VBSTMON_TX_LOC */ + if (slot_num == 5) { + regmap_update_bits(priv->regmap, CS35L33_TX_VBSTMON, + CS35L33_X_STATE | CS35L33_X_LOC, slot); + snd_soc_dapm_add_routes(dapm, + &cs35l33_vp_vbst_mon_route[2], 2); + dev_dbg(component->dev, + "VBSTMON enabled in slots %d", slot); + } + + /* Enable the relevant tx slot */ + reg = CS35L33_TX_EN4 - (slot/8); + bit_pos = slot - ((slot / 8) * (8)); + regmap_update_bits(priv->regmap, reg, + 1 << bit_pos, 1 << bit_pos); + + tx_mask &= ~(1 << slot); + slot = ffs(tx_mask) - 1; + slot_num++; + } + + return 0; +} + +static int cs35l33_component_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case CS35L33_MCLK_5644: + case CS35L33_MCLK_6: + case CS35L33_MCLK_6144: + regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL, + CS35L33_MCLKDIV2, 0); + cs35l33->mclk_int = freq; + break; + case CS35L33_MCLK_11289: + case CS35L33_MCLK_12: + case CS35L33_MCLK_12288: + regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL, + CS35L33_MCLKDIV2, CS35L33_MCLKDIV2); + cs35l33->mclk_int = freq/2; + break; + default: + cs35l33->mclk_int = 0; + return -EINVAL; + } + + dev_dbg(component->dev, "external mclk freq=%d, internal mclk freq=%d\n", + freq, cs35l33->mclk_int); + + return 0; +} + +static const struct snd_soc_dai_ops cs35l33_ops = { + .startup = cs35l33_pcm_startup, + .set_tristate = cs35l33_set_tristate, + .set_fmt = cs35l33_set_dai_fmt, + .hw_params = cs35l33_pcm_hw_params, + .set_tdm_slot = cs35l33_set_tdm_slot, +}; + +static struct snd_soc_dai_driver cs35l33_dai = { + .name = "cs35l33-dai", + .id = 0, + .playback = { + .stream_name = "CS35L33 Playback", + .channels_min = 1, + .channels_max = 1, + .rates = CS35L33_RATES, + .formats = CS35L33_FORMATS, + }, + .capture = { + .stream_name = "CS35L33 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = CS35L33_RATES, + .formats = CS35L33_FORMATS, + }, + .ops = &cs35l33_ops, + .symmetric_rate = 1, +}; + +static int cs35l33_set_hg_data(struct snd_soc_component *component, + struct cs35l33_pdata *pdata) +{ + struct cs35l33_hg *hg_config = &pdata->hg_config; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs35l33_private *priv = snd_soc_component_get_drvdata(component); + + if (hg_config->enable_hg_algo) { + regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL, + CS35L33_MEM_DEPTH_MASK, + hg_config->mem_depth << CS35L33_MEM_DEPTH_SHIFT); + regmap_write(priv->regmap, CS35L33_HG_REL_RATE, + hg_config->release_rate); + regmap_update_bits(priv->regmap, CS35L33_HG_HEAD, + CS35L33_HD_RM_MASK, + hg_config->hd_rm << CS35L33_HD_RM_SHIFT); + regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL, + CS35L33_LDO_THLD_MASK, + hg_config->ldo_thld << CS35L33_LDO_THLD_SHIFT); + regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL, + CS35L33_LDO_DISABLE_MASK, + hg_config->ldo_path_disable << + CS35L33_LDO_DISABLE_SHIFT); + regmap_update_bits(priv->regmap, CS35L33_LDO_DEL, + CS35L33_LDO_ENTRY_DELAY_MASK, + hg_config->ldo_entry_delay << + CS35L33_LDO_ENTRY_DELAY_SHIFT); + if (hg_config->vp_hg_auto) { + regmap_update_bits(priv->regmap, CS35L33_HG_EN, + CS35L33_VP_HG_AUTO_MASK, + CS35L33_VP_HG_AUTO_MASK); + snd_soc_dapm_add_routes(dapm, cs35l33_vphg_auto_route, + ARRAY_SIZE(cs35l33_vphg_auto_route)); + } + regmap_update_bits(priv->regmap, CS35L33_HG_EN, + CS35L33_VP_HG_MASK, + hg_config->vp_hg << CS35L33_VP_HG_SHIFT); + regmap_update_bits(priv->regmap, CS35L33_LDO_DEL, + CS35L33_VP_HG_RATE_MASK, + hg_config->vp_hg_rate << CS35L33_VP_HG_RATE_SHIFT); + regmap_update_bits(priv->regmap, CS35L33_LDO_DEL, + CS35L33_VP_HG_VA_MASK, + hg_config->vp_hg_va << CS35L33_VP_HG_VA_SHIFT); + regmap_update_bits(priv->regmap, CS35L33_HG_EN, + CS35L33_CLASS_HG_EN_MASK, CS35L33_CLASS_HG_EN_MASK); + } + return 0; +} + +static int cs35l33_set_bst_ipk(struct snd_soc_component *component, unsigned int bst) +{ + struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component); + int ret = 0, steps = 0; + + /* Boost current in uA */ + if (bst > 3600000 || bst < 1850000) { + dev_err(component->dev, "Invalid boost current %d\n", bst); + ret = -EINVAL; + goto err; + } + + if (bst % 15625) { + dev_err(component->dev, "Current not a multiple of 15625uA (%d)\n", + bst); + ret = -EINVAL; + goto err; + } + + while (bst > 1850000) { + bst -= 15625; + steps++; + } + + regmap_write(cs35l33->regmap, CS35L33_BST_PEAK_CTL, + steps+0x70); + +err: + return ret; +} + +static int cs35l33_probe(struct snd_soc_component *component) +{ + struct cs35l33_private *cs35l33 = snd_soc_component_get_drvdata(component); + + cs35l33->component = component; + pm_runtime_get_sync(component->dev); + + regmap_update_bits(cs35l33->regmap, CS35L33_PROTECT_CTL, + CS35L33_ALIVE_WD_DIS, 0x8); + regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL2, + CS35L33_ALIVE_WD_DIS2, + CS35L33_ALIVE_WD_DIS2); + + /* Set Platform Data */ + regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL1, + CS35L33_BST_CTL_MASK, cs35l33->pdata.boost_ctl); + regmap_update_bits(cs35l33->regmap, CS35L33_CLASSD_CTL, + CS35L33_AMP_DRV_SEL_MASK, + cs35l33->pdata.amp_drv_sel << CS35L33_AMP_DRV_SEL_SHIFT); + + if (cs35l33->pdata.boost_ipk) + cs35l33_set_bst_ipk(component, cs35l33->pdata.boost_ipk); + + if (cs35l33->enable_soft_ramp) { + snd_soc_component_update_bits(component, CS35L33_DAC_CTL, + CS35L33_DIGSFT, CS35L33_DIGSFT); + snd_soc_component_update_bits(component, CS35L33_DAC_CTL, + CS35L33_DSR_RATE, cs35l33->pdata.ramp_rate); + } else { + snd_soc_component_update_bits(component, CS35L33_DAC_CTL, + CS35L33_DIGSFT, 0); + } + + /* update IMON scaling rate if different from default of 0x8 */ + if (cs35l33->pdata.imon_adc_scale != 0x8) + snd_soc_component_update_bits(component, CS35L33_ADC_CTL, + CS35L33_IMON_SCALE, cs35l33->pdata.imon_adc_scale); + + cs35l33_set_hg_data(component, &(cs35l33->pdata)); + + /* + * unmask important interrupts that causes the chip to enter + * speaker safe mode and hence deserves user attention + */ + regmap_update_bits(cs35l33->regmap, CS35L33_INT_MASK_1, + CS35L33_M_OTE | CS35L33_M_OTW | CS35L33_M_AMP_SHORT | + CS35L33_M_CAL_ERR, 0); + + pm_runtime_put_sync(component->dev); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_cs35l33 = { + .probe = cs35l33_probe, + .set_bias_level = cs35l33_set_bias_level, + .set_sysclk = cs35l33_component_set_sysclk, + .controls = cs35l33_snd_controls, + .num_controls = ARRAY_SIZE(cs35l33_snd_controls), + .dapm_widgets = cs35l33_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l33_dapm_widgets), + .dapm_routes = cs35l33_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l33_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config cs35l33_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS35L33_MAX_REGISTER, + .reg_defaults = cs35l33_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l33_reg), + .volatile_reg = cs35l33_volatile_register, + .readable_reg = cs35l33_readable_register, + .writeable_reg = cs35l33_writeable_register, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int __maybe_unused cs35l33_runtime_resume(struct device *dev) +{ + struct cs35l33_private *cs35l33 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "%s\n", __func__); + + gpiod_set_value_cansleep(cs35l33->reset_gpio, 0); + + ret = regulator_bulk_enable(cs35l33->num_core_supplies, + cs35l33->core_supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", ret); + return ret; + } + + regcache_cache_only(cs35l33->regmap, false); + + gpiod_set_value_cansleep(cs35l33->reset_gpio, 1); + + msleep(CS35L33_BOOT_DELAY); + + ret = regcache_sync(cs35l33->regmap); + if (ret != 0) { + dev_err(dev, "Failed to restore register cache\n"); + goto err; + } + + return 0; + +err: + regcache_cache_only(cs35l33->regmap, true); + regulator_bulk_disable(cs35l33->num_core_supplies, + cs35l33->core_supplies); + + return ret; +} + +static int __maybe_unused cs35l33_runtime_suspend(struct device *dev) +{ + struct cs35l33_private *cs35l33 = dev_get_drvdata(dev); + + dev_dbg(dev, "%s\n", __func__); + + /* redo the calibration in next power up */ + cs35l33->amp_cal = false; + + regcache_cache_only(cs35l33->regmap, true); + regcache_mark_dirty(cs35l33->regmap); + regulator_bulk_disable(cs35l33->num_core_supplies, + cs35l33->core_supplies); + + return 0; +} + +static const struct dev_pm_ops cs35l33_pm_ops = { + SET_RUNTIME_PM_OPS(cs35l33_runtime_suspend, + cs35l33_runtime_resume, + NULL) +}; + +static int cs35l33_get_hg_data(const struct device_node *np, + struct cs35l33_pdata *pdata) +{ + struct device_node *hg; + struct cs35l33_hg *hg_config = &pdata->hg_config; + u32 val32; + + hg = of_get_child_by_name(np, "cirrus,hg-algo"); + hg_config->enable_hg_algo = hg ? true : false; + + if (hg_config->enable_hg_algo) { + if (of_property_read_u32(hg, "cirrus,mem-depth", &val32) >= 0) + hg_config->mem_depth = val32; + if (of_property_read_u32(hg, "cirrus,release-rate", + &val32) >= 0) + hg_config->release_rate = val32; + if (of_property_read_u32(hg, "cirrus,ldo-thld", &val32) >= 0) + hg_config->ldo_thld = val32; + if (of_property_read_u32(hg, "cirrus,ldo-path-disable", + &val32) >= 0) + hg_config->ldo_path_disable = val32; + if (of_property_read_u32(hg, "cirrus,ldo-entry-delay", + &val32) >= 0) + hg_config->ldo_entry_delay = val32; + + hg_config->vp_hg_auto = of_property_read_bool(hg, + "cirrus,vp-hg-auto"); + + if (of_property_read_u32(hg, "cirrus,vp-hg", &val32) >= 0) + hg_config->vp_hg = val32; + if (of_property_read_u32(hg, "cirrus,vp-hg-rate", &val32) >= 0) + hg_config->vp_hg_rate = val32; + if (of_property_read_u32(hg, "cirrus,vp-hg-va", &val32) >= 0) + hg_config->vp_hg_va = val32; + } + + of_node_put(hg); + + return 0; +} + +static irqreturn_t cs35l33_irq_thread(int irq, void *data) +{ + struct cs35l33_private *cs35l33 = data; + struct snd_soc_component *component = cs35l33->component; + unsigned int sticky_val1, sticky_val2, current_val, mask1, mask2; + + regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_2, + &sticky_val2); + regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1, + &sticky_val1); + regmap_read(cs35l33->regmap, CS35L33_INT_MASK_2, &mask2); + regmap_read(cs35l33->regmap, CS35L33_INT_MASK_1, &mask1); + + /* Check to see if the unmasked bits are active, + * if not then exit. + */ + if (!(sticky_val1 & ~mask1) && !(sticky_val2 & ~mask2)) + return IRQ_NONE; + + regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1, + ¤t_val); + + /* handle the interrupts */ + + if (sticky_val1 & CS35L33_AMP_SHORT) { + dev_crit(component->dev, "Amp short error\n"); + if (!(current_val & CS35L33_AMP_SHORT)) { + dev_dbg(component->dev, + "Amp short error release\n"); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, + CS35L33_AMP_SHORT_RLS, 0); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, + CS35L33_AMP_SHORT_RLS, + CS35L33_AMP_SHORT_RLS); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, CS35L33_AMP_SHORT_RLS, + 0); + } + } + + if (sticky_val1 & CS35L33_CAL_ERR) { + dev_err(component->dev, "Cal error\n"); + + /* redo the calibration in next power up */ + cs35l33->amp_cal = false; + + if (!(current_val & CS35L33_CAL_ERR)) { + dev_dbg(component->dev, "Cal error release\n"); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS, + 0); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS, + CS35L33_CAL_ERR_RLS); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS, + 0); + } + } + + if (sticky_val1 & CS35L33_OTE) { + dev_crit(component->dev, "Over temperature error\n"); + if (!(current_val & CS35L33_OTE)) { + dev_dbg(component->dev, + "Over temperature error release\n"); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, CS35L33_OTE_RLS, + CS35L33_OTE_RLS); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0); + } + } + + if (sticky_val1 & CS35L33_OTW) { + dev_err(component->dev, "Over temperature warning\n"); + if (!(current_val & CS35L33_OTW)) { + dev_dbg(component->dev, + "Over temperature warning release\n"); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, CS35L33_OTW_RLS, + CS35L33_OTW_RLS); + regmap_update_bits(cs35l33->regmap, + CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0); + } + } + if (CS35L33_ALIVE_ERR & sticky_val1) + dev_err(component->dev, "ERROR: ADSPCLK Interrupt\n"); + + if (CS35L33_MCLK_ERR & sticky_val1) + dev_err(component->dev, "ERROR: MCLK Interrupt\n"); + + if (CS35L33_VMON_OVFL & sticky_val2) + dev_err(component->dev, + "ERROR: VMON Overflow Interrupt\n"); + + if (CS35L33_IMON_OVFL & sticky_val2) + dev_err(component->dev, + "ERROR: IMON Overflow Interrupt\n"); + + if (CS35L33_VPMON_OVFL & sticky_val2) + dev_err(component->dev, + "ERROR: VPMON Overflow Interrupt\n"); + + return IRQ_HANDLED; +} + +static const char * const cs35l33_core_supplies[] = { + "VA", + "VP", +}; + +static int cs35l33_of_get_pdata(struct device *dev, + struct cs35l33_private *cs35l33) +{ + struct device_node *np = dev->of_node; + struct cs35l33_pdata *pdata = &cs35l33->pdata; + u32 val32; + + if (!np) + return 0; + + if (of_property_read_u32(np, "cirrus,boost-ctl", &val32) >= 0) { + pdata->boost_ctl = val32; + pdata->amp_drv_sel = 1; + } + + if (of_property_read_u32(np, "cirrus,ramp-rate", &val32) >= 0) { + pdata->ramp_rate = val32; + cs35l33->enable_soft_ramp = true; + } + + if (of_property_read_u32(np, "cirrus,boost-ipk", &val32) >= 0) + pdata->boost_ipk = val32; + + if (of_property_read_u32(np, "cirrus,imon-adc-scale", &val32) >= 0) { + if ((val32 == 0x0) || (val32 == 0x7) || (val32 == 0x6)) + pdata->imon_adc_scale = val32; + else + /* use default value */ + pdata->imon_adc_scale = 0x8; + } else { + /* use default value */ + pdata->imon_adc_scale = 0x8; + } + + cs35l33_get_hg_data(np, pdata); + + return 0; +} + +static int cs35l33_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs35l33_private *cs35l33; + struct cs35l33_pdata *pdata = dev_get_platdata(&i2c_client->dev); + int ret, devid, i; + unsigned int reg; + + cs35l33 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l33_private), + GFP_KERNEL); + if (!cs35l33) + return -ENOMEM; + + i2c_set_clientdata(i2c_client, cs35l33); + cs35l33->regmap = devm_regmap_init_i2c(i2c_client, &cs35l33_regmap); + if (IS_ERR(cs35l33->regmap)) { + ret = PTR_ERR(cs35l33->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + regcache_cache_only(cs35l33->regmap, true); + + for (i = 0; i < ARRAY_SIZE(cs35l33_core_supplies); i++) + cs35l33->core_supplies[i].supply + = cs35l33_core_supplies[i]; + cs35l33->num_core_supplies = ARRAY_SIZE(cs35l33_core_supplies); + + ret = devm_regulator_bulk_get(&i2c_client->dev, + cs35l33->num_core_supplies, + cs35l33->core_supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request core supplies: %d\n", + ret); + return ret; + } + + if (pdata) { + cs35l33->pdata = *pdata; + } else { + cs35l33_of_get_pdata(&i2c_client->dev, cs35l33); + pdata = &cs35l33->pdata; + } + + ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL, + cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs35l33", cs35l33); + if (ret != 0) + dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret); + + /* We could issue !RST or skip it based on AMP topology */ + cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(cs35l33->reset_gpio)) { + dev_err(&i2c_client->dev, "%s ERROR: Can't get reset GPIO\n", + __func__); + return PTR_ERR(cs35l33->reset_gpio); + } + + ret = regulator_bulk_enable(cs35l33->num_core_supplies, + cs35l33->core_supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to enable core supplies: %d\n", + ret); + return ret; + } + + gpiod_set_value_cansleep(cs35l33->reset_gpio, 1); + + msleep(CS35L33_BOOT_DELAY); + regcache_cache_only(cs35l33->regmap, false); + + /* initialize codec */ + devid = cirrus_read_device_id(cs35l33->regmap, CS35L33_DEVID_AB); + if (devid < 0) { + ret = devid; + dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret); + goto err_enable; + } + + if (devid != CS35L33_CHIP_ID) { + dev_err(&i2c_client->dev, + "CS35L33 Device ID (%X). Expected ID %X\n", + devid, CS35L33_CHIP_ID); + ret = -EINVAL; + goto err_enable; + } + + ret = regmap_read(cs35l33->regmap, CS35L33_REV_ID, ®); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed\n"); + goto err_enable; + } + + dev_info(&i2c_client->dev, + "Cirrus Logic CS35L33, Revision: %02X\n", reg & 0xFF); + + ret = regmap_register_patch(cs35l33->regmap, + cs35l33_patch, ARRAY_SIZE(cs35l33_patch)); + if (ret < 0) { + dev_err(&i2c_client->dev, + "Error in applying regmap patch: %d\n", ret); + goto err_enable; + } + + /* disable mclk and tdm */ + regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL, + CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM, + CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM); + + pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100); + pm_runtime_use_autosuspend(&i2c_client->dev); + pm_runtime_set_active(&i2c_client->dev); + pm_runtime_enable(&i2c_client->dev); + + ret = devm_snd_soc_register_component(&i2c_client->dev, + &soc_component_dev_cs35l33, &cs35l33_dai, 1); + if (ret < 0) { + dev_err(&i2c_client->dev, "%s: Register component failed\n", + __func__); + goto err_enable; + } + + return 0; + +err_enable: + gpiod_set_value_cansleep(cs35l33->reset_gpio, 0); + + regulator_bulk_disable(cs35l33->num_core_supplies, + cs35l33->core_supplies); + + return ret; +} + +static void cs35l33_i2c_remove(struct i2c_client *client) +{ + struct cs35l33_private *cs35l33 = i2c_get_clientdata(client); + + gpiod_set_value_cansleep(cs35l33->reset_gpio, 0); + + pm_runtime_disable(&client->dev); + regulator_bulk_disable(cs35l33->num_core_supplies, + cs35l33->core_supplies); +} + +static const struct of_device_id cs35l33_of_match[] = { + { .compatible = "cirrus,cs35l33", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l33_of_match); + +static const struct i2c_device_id cs35l33_id[] = { + {"cs35l33", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs35l33_id); + +static struct i2c_driver cs35l33_i2c_driver = { + .driver = { + .name = "cs35l33", + .pm = &cs35l33_pm_ops, + .of_match_table = cs35l33_of_match, + + }, + .id_table = cs35l33_id, + .probe = cs35l33_i2c_probe, + .remove = cs35l33_i2c_remove, + +}; +module_i2c_driver(cs35l33_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L33 driver"); +MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l33.h b/sound/soc/codecs/cs35l33.h new file mode 100644 index 0000000000..fcb5e1723b --- /dev/null +++ b/sound/soc/codecs/cs35l33.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * cs35l33.h -- CS35L33 ALSA SoC audio driver + * + * Copyright 2016 Cirrus Logic, Inc. + * + * Author: Paul Handrigan + */ + +#ifndef __CS35L33_H__ +#define __CS35L33_H__ + +#define CS35L33_CHIP_ID 0x00035A33 +#define CS35L33_DEVID_AB 0x01 /* Device ID A & B [RO] */ +#define CS35L33_DEVID_CD 0x02 /* Device ID C & D [RO] */ +#define CS35L33_DEVID_E 0x03 /* Device ID E [RO] */ +#define CS35L33_FAB_ID 0x04 /* Fab ID [RO] */ +#define CS35L33_REV_ID 0x05 /* Revision ID [RO] */ +#define CS35L33_PWRCTL1 0x06 /* Power Ctl 1 */ +#define CS35L33_PWRCTL2 0x07 /* Power Ctl 2 */ +#define CS35L33_CLK_CTL 0x08 /* Clock Ctl */ +#define CS35L33_BST_PEAK_CTL 0x09 /* Max Current for Boost */ +#define CS35L33_PROTECT_CTL 0x0A /* Amp Protection Parameters */ +#define CS35L33_BST_CTL1 0x0B /* Boost Converter CTL1 */ +#define CS35L33_BST_CTL2 0x0C /* Boost Converter CTL2 */ +#define CS35L33_ADSP_CTL 0x0D /* Serial Port Control */ +#define CS35L33_ADC_CTL 0x0E /* ADC Control */ +#define CS35L33_DAC_CTL 0x0F /* DAC Control */ +#define CS35L33_DIG_VOL_CTL 0x10 /* Digital Volume CTL */ +#define CS35L33_CLASSD_CTL 0x11 /* Class D Amp CTL */ +#define CS35L33_AMP_CTL 0x12 /* Amp Gain/Protecton Release CTL */ +#define CS35L33_INT_MASK_1 0x13 /* Interrupt Mask 1 */ +#define CS35L33_INT_MASK_2 0x14 /* Interrupt Mask 2 */ +#define CS35L33_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */ +#define CS35L33_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */ +#define CS35L33_DIAG_LOCK 0x17 /* Diagnostic Mode Register Lock */ +#define CS35L33_DIAG_CTRL_1 0x18 /* Diagnostic Mode Register Control */ +#define CS35L33_DIAG_CTRL_2 0x19 /* Diagnostic Mode Register Control 2 */ +#define CS35L33_HG_MEMLDO_CTL 0x23 /* H/G Memory/LDO CTL */ +#define CS35L33_HG_REL_RATE 0x24 /* H/G Release Rate */ +#define CS35L33_LDO_DEL 0x25 /* LDO Entry Delay/VPhg Control 1 */ +#define CS35L33_HG_HEAD 0x29 /* H/G Headroom */ +#define CS35L33_HG_EN 0x2A /* H/G Enable/VPhg CNT2 */ +#define CS35L33_TX_VMON 0x2D /* TDM TX Control 1 (VMON) */ +#define CS35L33_TX_IMON 0x2E /* TDM TX Control 2 (IMON) */ +#define CS35L33_TX_VPMON 0x2F /* TDM TX Control 3 (VPMON) */ +#define CS35L33_TX_VBSTMON 0x30 /* TDM TX Control 4 (VBSTMON) */ +#define CS35L33_TX_FLAG 0x31 /* TDM TX Control 5 (FLAG) */ +#define CS35L33_TX_EN1 0x32 /* TDM TX Enable 1 */ +#define CS35L33_TX_EN2 0x33 /* TDM TX Enable 2 */ +#define CS35L33_TX_EN3 0x34 /* TDM TX Enable 3 */ +#define CS35L33_TX_EN4 0x35 /* TDM TX Enable 4 */ +#define CS35L33_RX_AUD 0x36 /* TDM RX Control 1 */ +#define CS35L33_RX_SPLY 0x37 /* TDM RX Control 2 */ +#define CS35L33_RX_ALIVE 0x38 /* TDM RX Control 3 */ +#define CS35L33_BST_CTL4 0x39 /* Boost Converter Control 4 */ +#define CS35L33_HG_STATUS 0x3F /* H/G Status */ +#define CS35L33_MAX_REGISTER 0x59 + +#define CS35L33_MCLK_5644 5644800 +#define CS35L33_MCLK_6144 6144000 +#define CS35L33_MCLK_6 6000000 +#define CS35L33_MCLK_11289 11289600 +#define CS35L33_MCLK_12 12000000 +#define CS35L33_MCLK_12288 12288000 + +/* CS35L33_PWRCTL1 */ +#define CS35L33_PDN_AMP (1 << 7) +#define CS35L33_PDN_BST (1 << 2) +#define CS35L33_PDN_ALL 1 + +/* CS35L33_PWRCTL2 */ +#define CS35L33_PDN_VMON_SHIFT 7 +#define CS35L33_PDN_VMON (1 << CS35L33_PDN_VMON_SHIFT) +#define CS35L33_PDN_IMON_SHIFT 6 +#define CS35L33_PDN_IMON (1 << CS35L33_PDN_IMON_SHIFT) +#define CS35L33_PDN_VPMON_SHIFT 5 +#define CS35L33_PDN_VPMON (1 << CS35L33_PDN_VPMON_SHIFT) +#define CS35L33_PDN_VBSTMON_SHIFT 4 +#define CS35L33_PDN_VBSTMON (1 << CS35L33_PDN_VBSTMON_SHIFT) +#define CS35L33_SDOUT_3ST_I2S_SHIFT 3 +#define CS35L33_SDOUT_3ST_I2S (1 << CS35L33_SDOUT_3ST_I2S_SHIFT) +#define CS35L33_PDN_SDIN_SHIFT 2 +#define CS35L33_PDN_SDIN (1 << CS35L33_PDN_SDIN_SHIFT) +#define CS35L33_PDN_TDM_SHIFT 1 +#define CS35L33_PDN_TDM (1 << CS35L33_PDN_TDM_SHIFT) + +/* CS35L33_CLK_CTL */ +#define CS35L33_MCLKDIS (1 << 7) +#define CS35L33_MCLKDIV2 (1 << 6) +#define CS35L33_SDOUT_3ST_TDM (1 << 5) +#define CS35L33_INT_FS_RATE (1 << 4) +#define CS35L33_ADSP_FS 0xF + +/* CS35L33_PROTECT_CTL */ +#define CS35L33_ALIVE_WD_DIS (3 << 2) + +/* CS35L33_BST_CTL1 */ +#define CS35L33_BST_CTL_SRC (1 << 6) +#define CS35L33_BST_CTL_SHIFT (1 << 5) +#define CS35L33_BST_CTL_MASK 0x3F + +/* CS35L33_BST_CTL2 */ +#define CS35L33_TDM_WD_SEL (1 << 4) +#define CS35L33_ALIVE_WD_DIS2 (1 << 3) +#define CS35L33_VBST_SR_STEP 0x3 + +/* CS35L33_ADSP_CTL */ +#define CS35L33_ADSP_DRIVE (1 << 7) +#define CS35L33_MS_MASK (1 << 6) +#define CS35L33_SDIN_LOC (3 << 4) +#define CS35L33_ALIVE_RATE 0x3 + +/* CS35L33_ADC_CTL */ +#define CS35L33_INV_VMON (1 << 7) +#define CS35L33_INV_IMON (1 << 6) +#define CS35L33_ADC_NOTCH_DIS (1 << 5) +#define CS35L33_IMON_SCALE 0xF + +/* CS35L33_DAC_CTL */ +#define CS35L33_INV_DAC (1 << 7) +#define CS35L33_DAC_NOTCH_DIS (1 << 5) +#define CS35L33_DIGSFT (1 << 4) +#define CS35L33_DSR_RATE 0xF + +/* CS35L33_CLASSD_CTL */ +#define CS35L33_AMP_SD (1 << 6) +#define CS35L33_AMP_DRV_SEL_SRC (1 << 5) +#define CS35L33_AMP_DRV_SEL_MASK 0x10 +#define CS35L33_AMP_DRV_SEL_SHIFT 4 +#define CS35L33_AMP_CAL (1 << 3) +#define CS35L33_GAIN_CHG_ZC_MASK 0x04 +#define CS35L33_GAIN_CHG_ZC_SHIFT 2 +#define CS35L33_CLASS_D_CTL_MASK 0x3F + +/* CS35L33_AMP_CTL */ +#define CS35L33_AMP_GAIN 0xF0 +#define CS35L33_CAL_ERR_RLS (1 << 3) +#define CS35L33_AMP_SHORT_RLS (1 << 2) +#define CS35L33_OTW_RLS (1 << 1) +#define CS35L33_OTE_RLS 1 + +/* CS35L33_INT_MASK_1 */ +#define CS35L33_M_CAL_ERR_SHIFT 6 +#define CS35L33_M_CAL_ERR (1 << CS35L33_M_CAL_ERR_SHIFT) +#define CS35L33_M_ALIVE_ERR_SHIFT 5 +#define CS35L33_M_ALIVE_ERR (1 << CS35L33_M_ALIVE_ERR_SHIFT) +#define CS35L33_M_AMP_SHORT_SHIFT 2 +#define CS35L33_M_AMP_SHORT (1 << CS35L33_M_AMP_SHORT_SHIFT) +#define CS35L33_M_OTW_SHIFT 1 +#define CS35L33_M_OTW (1 << CS35L33_M_OTW_SHIFT) +#define CS35L33_M_OTE_SHIFT 0 +#define CS35L33_M_OTE (1 << CS35L33_M_OTE_SHIFT) + +/* CS35L33_INT_STATUS_1 */ +#define CS35L33_CAL_ERR (1 << 6) +#define CS35L33_ALIVE_ERR (1 << 5) +#define CS35L33_ADSPCLK_ERR (1 << 4) +#define CS35L33_MCLK_ERR (1 << 3) +#define CS35L33_AMP_SHORT (1 << 2) +#define CS35L33_OTW (1 << 1) +#define CS35L33_OTE (1 << 0) + +/* CS35L33_INT_STATUS_2 */ +#define CS35L33_VMON_OVFL (1 << 7) +#define CS35L33_IMON_OVFL (1 << 6) +#define CS35L33_VPMON_OVFL (1 << 5) +#define CS35L33_VBSTMON_OVFL (1 << 4) +#define CS35L33_PDN_DONE 1 + +/* CS35L33_BST_CTL4 */ +#define CS35L33_BST_RGS 0x70 +#define CS35L33_BST_COEFF3 0xF + +/* CS35L33_HG_MEMLDO_CTL */ +#define CS35L33_MEM_DEPTH_SHIFT 5 +#define CS35L33_MEM_DEPTH_MASK (0x3 << CS35L33_MEM_DEPTH_SHIFT) +#define CS35L33_LDO_THLD_SHIFT 1 +#define CS35L33_LDO_THLD_MASK (0xF << CS35L33_LDO_THLD_SHIFT) +#define CS35L33_LDO_DISABLE_SHIFT 0 +#define CS35L33_LDO_DISABLE_MASK (0x1 << CS35L33_LDO_DISABLE_SHIFT) + +/* CS35L33_LDO_DEL */ +#define CS35L33_VP_HG_VA_SHIFT 5 +#define CS35L33_VP_HG_VA_MASK (0x7 << CS35L33_VP_HG_VA_SHIFT) +#define CS35L33_LDO_ENTRY_DELAY_SHIFT 2 +#define CS35L33_LDO_ENTRY_DELAY_MASK (0x7 << CS35L33_LDO_ENTRY_DELAY_SHIFT) +#define CS35L33_VP_HG_RATE_SHIFT 0 +#define CS35L33_VP_HG_RATE_MASK (0x3 << CS35L33_VP_HG_RATE_SHIFT) + +/* CS35L33_HG_HEAD */ +#define CS35L33_HD_RM_SHIFT 0 +#define CS35L33_HD_RM_MASK (0x7F << CS35L33_HD_RM_SHIFT) + +/* CS35L33_HG_EN */ +#define CS35L33_CLASS_HG_ENA_SHIFT 7 +#define CS35L33_CLASS_HG_EN_MASK (0x1 << CS35L33_CLASS_HG_ENA_SHIFT) +#define CS35L33_VP_HG_AUTO_SHIFT 6 +#define CS35L33_VP_HG_AUTO_MASK (0x1 << 6) +#define CS35L33_VP_HG_SHIFT 0 +#define CS35L33_VP_HG_MASK (0x1F << CS35L33_VP_HG_SHIFT) + +#define CS35L33_RATES (SNDRV_PCM_RATE_8000_48000) +#define CS35L33_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/* CS35L33_{RX,TX}_X */ +#define CS35L33_X_STATE_SHIFT 7 +#define CS35L33_X_STATE (1 << CS35L33_X_STATE_SHIFT) +#define CS35L33_X_LOC_SHIFT 0 +#define CS35L33_X_LOC (0x1F << CS35L33_X_LOC_SHIFT) + +/* CS35L33_RX_AUD */ +#define CS35L33_AUDIN_RX_DEPTH_SHIFT 5 +#define CS35L33_AUDIN_RX_DEPTH (0x7 << CS35L33_AUDIN_RX_DEPTH_SHIFT) + +#endif diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c new file mode 100644 index 0000000000..6974dd4614 --- /dev/null +++ b/sound/soc/codecs/cs35l34.c @@ -0,0 +1,1242 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs35l34.c -- CS35l34 ALSA SoC audio driver + * + * Copyright 2016 Cirrus Logic, Inc. + * + * Author: Paul Handrigan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l34.h" +#include "cirrus_legacy.h" + +#define PDN_DONE_ATTEMPTS 10 +#define CS35L34_START_DELAY 50 + +struct cs35l34_private { + struct snd_soc_component *component; + struct cs35l34_platform_data pdata; + struct regmap *regmap; + struct regulator_bulk_data core_supplies[2]; + int num_core_supplies; + int mclk_int; + bool tdm_mode; + struct gpio_desc *reset_gpio; /* Active-low reset GPIO */ +}; + +static const struct reg_default cs35l34_reg[] = { + {CS35L34_PWRCTL1, 0x01}, + {CS35L34_PWRCTL2, 0x19}, + {CS35L34_PWRCTL3, 0x01}, + {CS35L34_ADSP_CLK_CTL, 0x08}, + {CS35L34_MCLK_CTL, 0x11}, + {CS35L34_AMP_INP_DRV_CTL, 0x01}, + {CS35L34_AMP_DIG_VOL_CTL, 0x12}, + {CS35L34_AMP_DIG_VOL, 0x00}, + {CS35L34_AMP_ANLG_GAIN_CTL, 0x0F}, + {CS35L34_PROTECT_CTL, 0x06}, + {CS35L34_AMP_KEEP_ALIVE_CTL, 0x04}, + {CS35L34_BST_CVTR_V_CTL, 0x00}, + {CS35L34_BST_PEAK_I, 0x10}, + {CS35L34_BST_RAMP_CTL, 0x87}, + {CS35L34_BST_CONV_COEF_1, 0x24}, + {CS35L34_BST_CONV_COEF_2, 0x24}, + {CS35L34_BST_CONV_SLOPE_COMP, 0x4E}, + {CS35L34_BST_CONV_SW_FREQ, 0x08}, + {CS35L34_CLASS_H_CTL, 0x0D}, + {CS35L34_CLASS_H_HEADRM_CTL, 0x0D}, + {CS35L34_CLASS_H_RELEASE_RATE, 0x08}, + {CS35L34_CLASS_H_FET_DRIVE_CTL, 0x41}, + {CS35L34_CLASS_H_STATUS, 0x05}, + {CS35L34_VPBR_CTL, 0x0A}, + {CS35L34_VPBR_VOL_CTL, 0x90}, + {CS35L34_VPBR_TIMING_CTL, 0x6A}, + {CS35L34_PRED_MAX_ATTEN_SPK_LOAD, 0x95}, + {CS35L34_PRED_BROWNOUT_THRESH, 0x1C}, + {CS35L34_PRED_BROWNOUT_VOL_CTL, 0x00}, + {CS35L34_PRED_BROWNOUT_RATE_CTL, 0x10}, + {CS35L34_PRED_WAIT_CTL, 0x10}, + {CS35L34_PRED_ZVP_INIT_IMP_CTL, 0x08}, + {CS35L34_PRED_MAN_SAFE_VPI_CTL, 0x80}, + {CS35L34_VPBR_ATTEN_STATUS, 0x00}, + {CS35L34_PRED_BRWNOUT_ATT_STATUS, 0x00}, + {CS35L34_SPKR_MON_CTL, 0xC6}, + {CS35L34_ADSP_I2S_CTL, 0x00}, + {CS35L34_ADSP_TDM_CTL, 0x00}, + {CS35L34_TDM_TX_CTL_1_VMON, 0x00}, + {CS35L34_TDM_TX_CTL_2_IMON, 0x04}, + {CS35L34_TDM_TX_CTL_3_VPMON, 0x03}, + {CS35L34_TDM_TX_CTL_4_VBSTMON, 0x07}, + {CS35L34_TDM_TX_CTL_5_FLAG1, 0x08}, + {CS35L34_TDM_TX_CTL_6_FLAG2, 0x09}, + {CS35L34_TDM_TX_SLOT_EN_1, 0x00}, + {CS35L34_TDM_TX_SLOT_EN_2, 0x00}, + {CS35L34_TDM_TX_SLOT_EN_3, 0x00}, + {CS35L34_TDM_TX_SLOT_EN_4, 0x00}, + {CS35L34_TDM_RX_CTL_1_AUDIN, 0x40}, + {CS35L34_TDM_RX_CTL_3_ALIVE, 0x04}, + {CS35L34_MULT_DEV_SYNCH1, 0x00}, + {CS35L34_MULT_DEV_SYNCH2, 0x80}, + {CS35L34_PROT_RELEASE_CTL, 0x00}, + {CS35L34_DIAG_MODE_REG_LOCK, 0x00}, + {CS35L34_DIAG_MODE_CTL_1, 0x00}, + {CS35L34_DIAG_MODE_CTL_2, 0x00}, + {CS35L34_INT_MASK_1, 0xFF}, + {CS35L34_INT_MASK_2, 0xFF}, + {CS35L34_INT_MASK_3, 0xFF}, + {CS35L34_INT_MASK_4, 0xFF}, + {CS35L34_INT_STATUS_1, 0x30}, + {CS35L34_INT_STATUS_2, 0x05}, + {CS35L34_INT_STATUS_3, 0x00}, + {CS35L34_INT_STATUS_4, 0x00}, + {CS35L34_OTP_TRIM_STATUS, 0x00}, +}; + +static bool cs35l34_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L34_DEVID_AB: + case CS35L34_DEVID_CD: + case CS35L34_DEVID_E: + case CS35L34_FAB_ID: + case CS35L34_REV_ID: + case CS35L34_INT_STATUS_1: + case CS35L34_INT_STATUS_2: + case CS35L34_INT_STATUS_3: + case CS35L34_INT_STATUS_4: + case CS35L34_CLASS_H_STATUS: + case CS35L34_VPBR_ATTEN_STATUS: + case CS35L34_OTP_TRIM_STATUS: + return true; + default: + return false; + } +} + +static bool cs35l34_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L34_DEVID_AB: + case CS35L34_DEVID_CD: + case CS35L34_DEVID_E: + case CS35L34_FAB_ID: + case CS35L34_REV_ID: + case CS35L34_PWRCTL1: + case CS35L34_PWRCTL2: + case CS35L34_PWRCTL3: + case CS35L34_ADSP_CLK_CTL: + case CS35L34_MCLK_CTL: + case CS35L34_AMP_INP_DRV_CTL: + case CS35L34_AMP_DIG_VOL_CTL: + case CS35L34_AMP_DIG_VOL: + case CS35L34_AMP_ANLG_GAIN_CTL: + case CS35L34_PROTECT_CTL: + case CS35L34_AMP_KEEP_ALIVE_CTL: + case CS35L34_BST_CVTR_V_CTL: + case CS35L34_BST_PEAK_I: + case CS35L34_BST_RAMP_CTL: + case CS35L34_BST_CONV_COEF_1: + case CS35L34_BST_CONV_COEF_2: + case CS35L34_BST_CONV_SLOPE_COMP: + case CS35L34_BST_CONV_SW_FREQ: + case CS35L34_CLASS_H_CTL: + case CS35L34_CLASS_H_HEADRM_CTL: + case CS35L34_CLASS_H_RELEASE_RATE: + case CS35L34_CLASS_H_FET_DRIVE_CTL: + case CS35L34_CLASS_H_STATUS: + case CS35L34_VPBR_CTL: + case CS35L34_VPBR_VOL_CTL: + case CS35L34_VPBR_TIMING_CTL: + case CS35L34_PRED_MAX_ATTEN_SPK_LOAD: + case CS35L34_PRED_BROWNOUT_THRESH: + case CS35L34_PRED_BROWNOUT_VOL_CTL: + case CS35L34_PRED_BROWNOUT_RATE_CTL: + case CS35L34_PRED_WAIT_CTL: + case CS35L34_PRED_ZVP_INIT_IMP_CTL: + case CS35L34_PRED_MAN_SAFE_VPI_CTL: + case CS35L34_VPBR_ATTEN_STATUS: + case CS35L34_PRED_BRWNOUT_ATT_STATUS: + case CS35L34_SPKR_MON_CTL: + case CS35L34_ADSP_I2S_CTL: + case CS35L34_ADSP_TDM_CTL: + case CS35L34_TDM_TX_CTL_1_VMON: + case CS35L34_TDM_TX_CTL_2_IMON: + case CS35L34_TDM_TX_CTL_3_VPMON: + case CS35L34_TDM_TX_CTL_4_VBSTMON: + case CS35L34_TDM_TX_CTL_5_FLAG1: + case CS35L34_TDM_TX_CTL_6_FLAG2: + case CS35L34_TDM_TX_SLOT_EN_1: + case CS35L34_TDM_TX_SLOT_EN_2: + case CS35L34_TDM_TX_SLOT_EN_3: + case CS35L34_TDM_TX_SLOT_EN_4: + case CS35L34_TDM_RX_CTL_1_AUDIN: + case CS35L34_TDM_RX_CTL_3_ALIVE: + case CS35L34_MULT_DEV_SYNCH1: + case CS35L34_MULT_DEV_SYNCH2: + case CS35L34_PROT_RELEASE_CTL: + case CS35L34_DIAG_MODE_REG_LOCK: + case CS35L34_DIAG_MODE_CTL_1: + case CS35L34_DIAG_MODE_CTL_2: + case CS35L34_INT_MASK_1: + case CS35L34_INT_MASK_2: + case CS35L34_INT_MASK_3: + case CS35L34_INT_MASK_4: + case CS35L34_INT_STATUS_1: + case CS35L34_INT_STATUS_2: + case CS35L34_INT_STATUS_3: + case CS35L34_INT_STATUS_4: + case CS35L34_OTP_TRIM_STATUS: + return true; + default: + return false; + } +} + +static bool cs35l34_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L34_INT_STATUS_1: + case CS35L34_INT_STATUS_2: + case CS35L34_INT_STATUS_3: + case CS35L34_INT_STATUS_4: + return true; + default: + return false; + } +} + +static int cs35l34_sdin_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l34_private *priv = snd_soc_component_get_drvdata(component); + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (priv->tdm_mode) + regmap_update_bits(priv->regmap, CS35L34_PWRCTL3, + CS35L34_PDN_TDM, 0x00); + + ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1, + CS35L34_PDN_ALL, 0); + if (ret < 0) { + dev_err(component->dev, "Cannot set Power bits %d\n", ret); + return ret; + } + usleep_range(5000, 5100); + break; + case SND_SOC_DAPM_POST_PMD: + if (priv->tdm_mode) { + regmap_update_bits(priv->regmap, CS35L34_PWRCTL3, + CS35L34_PDN_TDM, CS35L34_PDN_TDM); + } + ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1, + CS35L34_PDN_ALL, CS35L34_PDN_ALL); + break; + default: + pr_err("Invalid event = 0x%x\n", event); + } + return 0; +} + +static int cs35l34_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct cs35l34_private *priv = snd_soc_component_get_drvdata(component); + unsigned int reg, bit_pos; + int slot, slot_num; + + if (slot_width != 8) + return -EINVAL; + + priv->tdm_mode = true; + /* scan rx_mask for aud slot */ + slot = ffs(rx_mask) - 1; + if (slot >= 0) + snd_soc_component_update_bits(component, CS35L34_TDM_RX_CTL_1_AUDIN, + CS35L34_X_LOC, slot); + + /* scan tx_mask: vmon(2 slots); imon (2 slots); vpmon (1 slot) + * vbstmon (1 slot) + */ + slot = ffs(tx_mask) - 1; + slot_num = 0; + + /* disable vpmon/vbstmon: enable later if set in tx_mask */ + snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_3_VPMON, + CS35L34_X_STATE | CS35L34_X_LOC, + CS35L34_X_STATE | CS35L34_X_LOC); + snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_4_VBSTMON, + CS35L34_X_STATE | CS35L34_X_LOC, + CS35L34_X_STATE | CS35L34_X_LOC); + + /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/ + while (slot >= 0) { + /* configure VMON_TX_LOC */ + if (slot_num == 0) + snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_1_VMON, + CS35L34_X_STATE | CS35L34_X_LOC, slot); + + /* configure IMON_TX_LOC */ + if (slot_num == 4) { + snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_2_IMON, + CS35L34_X_STATE | CS35L34_X_LOC, slot); + } + /* configure VPMON_TX_LOC */ + if (slot_num == 3) { + snd_soc_component_update_bits(component, CS35L34_TDM_TX_CTL_3_VPMON, + CS35L34_X_STATE | CS35L34_X_LOC, slot); + } + /* configure VBSTMON_TX_LOC */ + if (slot_num == 7) { + snd_soc_component_update_bits(component, + CS35L34_TDM_TX_CTL_4_VBSTMON, + CS35L34_X_STATE | CS35L34_X_LOC, slot); + } + + /* Enable the relevant tx slot */ + reg = CS35L34_TDM_TX_SLOT_EN_4 - (slot/8); + bit_pos = slot - ((slot / 8) * (8)); + snd_soc_component_update_bits(component, reg, + 1 << bit_pos, 1 << bit_pos); + + tx_mask &= ~(1 << slot); + slot = ffs(tx_mask) - 1; + slot_num++; + } + + return 0; +} + +static int cs35l34_main_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l34_private *priv = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(priv->regmap, CS35L34_BST_CVTR_V_CTL, + CS35L34_BST_CVTL_MASK, priv->pdata.boost_vtge); + usleep_range(5000, 5100); + regmap_update_bits(priv->regmap, CS35L34_PROTECT_CTL, + CS35L34_MUTE, 0); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(priv->regmap, CS35L34_BST_CVTR_V_CTL, + CS35L34_BST_CVTL_MASK, 0); + regmap_update_bits(priv->regmap, CS35L34_PROTECT_CTL, + CS35L34_MUTE, CS35L34_MUTE); + usleep_range(5000, 5100); + break; + default: + pr_err("Invalid event = 0x%x\n", event); + } + return 0; +} + +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0); + +static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 300, 100, 0); + + +static const struct snd_kcontrol_new cs35l34_snd_controls[] = { + SOC_SINGLE_SX_TLV("Digital Volume", CS35L34_AMP_DIG_VOL, + 0, 0x34, 0xE4, dig_vol_tlv), + SOC_SINGLE_TLV("Amp Gain Volume", CS35L34_AMP_ANLG_GAIN_CTL, + 0, 0xF, 0, amp_gain_tlv), +}; + + +static int cs35l34_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l34_private *priv = snd_soc_component_get_drvdata(component); + int ret, i; + unsigned int reg; + + switch (event) { + case SND_SOC_DAPM_PRE_PMD: + ret = regmap_read(priv->regmap, CS35L34_AMP_DIG_VOL_CTL, + ®); + if (ret != 0) { + pr_err("%s regmap read failure %d\n", __func__, ret); + return ret; + } + if (reg & CS35L34_AMP_DIGSFT) + msleep(40); + else + usleep_range(2000, 2100); + + for (i = 0; i < PDN_DONE_ATTEMPTS; i++) { + ret = regmap_read(priv->regmap, CS35L34_INT_STATUS_2, + ®); + if (ret != 0) { + pr_err("%s regmap read failure %d\n", + __func__, ret); + return ret; + } + if (reg & CS35L34_PDN_DONE) + break; + + usleep_range(5000, 5100); + } + if (i == PDN_DONE_ATTEMPTS) + pr_err("%s Device did not power down properly\n", + __func__); + break; + default: + pr_err("Invalid event = 0x%x\n", event); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget cs35l34_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L34_PWRCTL3, + 1, 1, cs35l34_sdin_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L34_PWRCTL3, 2, 1), + + SND_SOC_DAPM_SUPPLY("EXTCLK", CS35L34_PWRCTL3, 7, 1, + cs35l34_mclk_event, SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("SPK"), + + SND_SOC_DAPM_INPUT("VP"), + SND_SOC_DAPM_INPUT("VPST"), + SND_SOC_DAPM_INPUT("ISENSE"), + SND_SOC_DAPM_INPUT("VSENSE"), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L34_PWRCTL2, 7, 1), + SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L34_PWRCTL2, 6, 1), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L34_PWRCTL3, 3, 1), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L34_PWRCTL3, 4, 1), + SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L34_PWRCTL2, 5, 1), + SND_SOC_DAPM_ADC("BOOST", NULL, CS35L34_PWRCTL2, 2, 1), + + SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L34_PWRCTL2, 0, 1, NULL, 0, + cs35l34_main_amp_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route cs35l34_audio_map[] = { + {"SDIN", NULL, "AMP Playback"}, + {"BOOST", NULL, "SDIN"}, + {"CLASS H", NULL, "BOOST"}, + {"Main AMP", NULL, "CLASS H"}, + {"SPK", NULL, "Main AMP"}, + + {"VPMON ADC", NULL, "CLASS H"}, + {"VBSTMON ADC", NULL, "CLASS H"}, + {"SPK", NULL, "VPMON ADC"}, + {"SPK", NULL, "VBSTMON ADC"}, + + {"IMON ADC", NULL, "ISENSE"}, + {"VMON ADC", NULL, "VSENSE"}, + {"SDOUT", NULL, "IMON ADC"}, + {"SDOUT", NULL, "VMON ADC"}, + {"AMP Capture", NULL, "SDOUT"}, + + {"SDIN", NULL, "EXTCLK"}, + {"SDOUT", NULL, "EXTCLK"}, +}; + +struct cs35l34_mclk_div { + int mclk; + int srate; + u8 adsp_rate; +}; + +static struct cs35l34_mclk_div cs35l34_mclk_coeffs[] = { + + /* MCLK, Sample Rate, adsp_rate */ + + {5644800, 11025, 0x1}, + {5644800, 22050, 0x4}, + {5644800, 44100, 0x7}, + + {6000000, 8000, 0x0}, + {6000000, 11025, 0x1}, + {6000000, 12000, 0x2}, + {6000000, 16000, 0x3}, + {6000000, 22050, 0x4}, + {6000000, 24000, 0x5}, + {6000000, 32000, 0x6}, + {6000000, 44100, 0x7}, + {6000000, 48000, 0x8}, + + {6144000, 8000, 0x0}, + {6144000, 11025, 0x1}, + {6144000, 12000, 0x2}, + {6144000, 16000, 0x3}, + {6144000, 22050, 0x4}, + {6144000, 24000, 0x5}, + {6144000, 32000, 0x6}, + {6144000, 44100, 0x7}, + {6144000, 48000, 0x8}, +}; + +static int cs35l34_get_mclk_coeff(int mclk, int srate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l34_mclk_coeffs); i++) { + if (cs35l34_mclk_coeffs[i].mclk == mclk && + cs35l34_mclk_coeffs[i].srate == srate) + return i; + } + return -EINVAL; +} + +static int cs35l34_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs35l34_private *priv = snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL, + 0x80, 0x80); + break; + case SND_SOC_DAIFMT_CBS_CFS: + regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL, + 0x80, 0x00); + break; + default: + return -EINVAL; + } + return 0; +} + +static int cs35l34_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs35l34_private *priv = snd_soc_component_get_drvdata(component); + int srate = params_rate(params); + int ret; + + int coeff = cs35l34_get_mclk_coeff(priv->mclk_int, srate); + + if (coeff < 0) { + dev_err(component->dev, "ERROR: Invalid mclk %d and/or srate %d\n", + priv->mclk_int, srate); + return coeff; + } + + ret = regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL, + CS35L34_ADSP_RATE, cs35l34_mclk_coeffs[coeff].adsp_rate); + if (ret != 0) + dev_err(component->dev, "Failed to set clock state %d\n", ret); + + return ret; +} + +static const unsigned int cs35l34_src_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 +}; + + +static const struct snd_pcm_hw_constraint_list cs35l34_constraints = { + .count = ARRAY_SIZE(cs35l34_src_rates), + .list = cs35l34_src_rates, +}; + +static int cs35l34_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &cs35l34_constraints); + return 0; +} + + +static int cs35l34_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + + struct snd_soc_component *component = dai->component; + + if (tristate) + snd_soc_component_update_bits(component, CS35L34_PWRCTL3, + CS35L34_PDN_SDOUT, CS35L34_PDN_SDOUT); + else + snd_soc_component_update_bits(component, CS35L34_PWRCTL3, + CS35L34_PDN_SDOUT, 0); + return 0; +} + +static int cs35l34_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs35l34_private *cs35l34 = snd_soc_component_get_drvdata(component); + unsigned int value; + + switch (freq) { + case CS35L34_MCLK_5644: + value = CS35L34_MCLK_RATE_5P6448; + cs35l34->mclk_int = freq; + break; + case CS35L34_MCLK_6: + value = CS35L34_MCLK_RATE_6P0000; + cs35l34->mclk_int = freq; + break; + case CS35L34_MCLK_6144: + value = CS35L34_MCLK_RATE_6P1440; + cs35l34->mclk_int = freq; + break; + case CS35L34_MCLK_11289: + value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_5P6448; + cs35l34->mclk_int = freq / 2; + break; + case CS35L34_MCLK_12: + value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_6P0000; + cs35l34->mclk_int = freq / 2; + break; + case CS35L34_MCLK_12288: + value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_6P1440; + cs35l34->mclk_int = freq / 2; + break; + default: + dev_err(component->dev, "ERROR: Invalid Frequency %d\n", freq); + cs35l34->mclk_int = 0; + return -EINVAL; + } + regmap_update_bits(cs35l34->regmap, CS35L34_MCLK_CTL, + CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_MASK, value); + return 0; +} + +static const struct snd_soc_dai_ops cs35l34_ops = { + .startup = cs35l34_pcm_startup, + .set_tristate = cs35l34_set_tristate, + .set_fmt = cs35l34_set_dai_fmt, + .hw_params = cs35l34_pcm_hw_params, + .set_sysclk = cs35l34_dai_set_sysclk, + .set_tdm_slot = cs35l34_set_tdm_slot, +}; + +static struct snd_soc_dai_driver cs35l34_dai = { + .name = "cs35l34", + .id = 0, + .playback = { + .stream_name = "AMP Playback", + .channels_min = 1, + .channels_max = 8, + .rates = CS35L34_RATES, + .formats = CS35L34_FORMATS, + }, + .capture = { + .stream_name = "AMP Capture", + .channels_min = 1, + .channels_max = 8, + .rates = CS35L34_RATES, + .formats = CS35L34_FORMATS, + }, + .ops = &cs35l34_ops, + .symmetric_rate = 1, +}; + +static int cs35l34_boost_inductor(struct cs35l34_private *cs35l34, + unsigned int inductor) +{ + struct snd_soc_component *component = cs35l34->component; + + switch (inductor) { + case 1000: /* 1 uH */ + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x24); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x24); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP, + 0x4E); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 0); + break; + case 1200: /* 1.2 uH */ + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x20); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x20); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP, + 0x47); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 1); + break; + case 1500: /* 1.5uH */ + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x20); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x20); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP, + 0x3C); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 2); + break; + case 2200: /* 2.2uH */ + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x19); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x25); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP, + 0x23); + regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 3); + break; + default: + dev_err(component->dev, "%s Invalid Inductor Value %d uH\n", + __func__, inductor); + return -EINVAL; + } + return 0; +} + +static int cs35l34_probe(struct snd_soc_component *component) +{ + int ret = 0; + struct cs35l34_private *cs35l34 = snd_soc_component_get_drvdata(component); + + pm_runtime_get_sync(component->dev); + + /* Set over temperature warning attenuation to 6 dB */ + regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL, + CS35L34_OTW_ATTN_MASK, 0x8); + + /* Set Power control registers 2 and 3 to have everything + * powered down at initialization + */ + regmap_write(cs35l34->regmap, CS35L34_PWRCTL2, 0xFD); + regmap_write(cs35l34->regmap, CS35L34_PWRCTL3, 0x1F); + + /* Set mute bit at startup */ + regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL, + CS35L34_MUTE, CS35L34_MUTE); + + /* Set Platform Data */ + if (cs35l34->pdata.boost_peak) + regmap_update_bits(cs35l34->regmap, CS35L34_BST_PEAK_I, + CS35L34_BST_PEAK_MASK, + cs35l34->pdata.boost_peak); + + if (cs35l34->pdata.gain_zc_disable) + regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL, + CS35L34_GAIN_ZC_MASK, 0); + else + regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL, + CS35L34_GAIN_ZC_MASK, CS35L34_GAIN_ZC_MASK); + + if (cs35l34->pdata.aif_half_drv) + regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_CLK_CTL, + CS35L34_ADSP_DRIVE, 0); + + if (cs35l34->pdata.digsft_disable) + regmap_update_bits(cs35l34->regmap, CS35L34_AMP_DIG_VOL_CTL, + CS35L34_AMP_DIGSFT, 0); + + if (cs35l34->pdata.amp_inv) + regmap_update_bits(cs35l34->regmap, CS35L34_AMP_DIG_VOL_CTL, + CS35L34_INV, CS35L34_INV); + + if (cs35l34->pdata.boost_ind) + ret = cs35l34_boost_inductor(cs35l34, cs35l34->pdata.boost_ind); + + if (cs35l34->pdata.i2s_sdinloc) + regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_I2S_CTL, + CS35L34_I2S_LOC_MASK, + cs35l34->pdata.i2s_sdinloc << CS35L34_I2S_LOC_SHIFT); + + if (cs35l34->pdata.tdm_rising_edge) + regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_TDM_CTL, + 1, 1); + + pm_runtime_put_sync(component->dev); + + return ret; +} + + +static const struct snd_soc_component_driver soc_component_dev_cs35l34 = { + .probe = cs35l34_probe, + .dapm_widgets = cs35l34_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l34_dapm_widgets), + .dapm_routes = cs35l34_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l34_audio_map), + .controls = cs35l34_snd_controls, + .num_controls = ARRAY_SIZE(cs35l34_snd_controls), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct regmap_config cs35l34_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS35L34_MAX_REGISTER, + .reg_defaults = cs35l34_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l34_reg), + .volatile_reg = cs35l34_volatile_register, + .readable_reg = cs35l34_readable_register, + .precious_reg = cs35l34_precious_register, + .cache_type = REGCACHE_MAPLE, + + .use_single_read = true, + .use_single_write = true, +}; + +static int cs35l34_handle_of_data(struct i2c_client *i2c_client, + struct cs35l34_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + unsigned int val; + + if (of_property_read_u32(np, "cirrus,boost-vtge-millivolt", + &val) >= 0) { + /* Boost Voltage has a maximum of 8V */ + if (val > 8000 || (val < 3300 && val > 0)) { + dev_err(&i2c_client->dev, + "Invalid Boost Voltage %d mV\n", val); + return -EINVAL; + } + if (val == 0) + pdata->boost_vtge = 0; /* Use VP */ + else + pdata->boost_vtge = ((val - 3300)/100) + 1; + } else { + dev_warn(&i2c_client->dev, + "Boost Voltage not specified. Using VP\n"); + } + + if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) { + pdata->boost_ind = val; + } else { + dev_err(&i2c_client->dev, "Inductor not specified.\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val) >= 0) { + if (val > 3840 || val < 1200) { + dev_err(&i2c_client->dev, + "Invalid Boost Peak Current %d mA\n", val); + return -EINVAL; + } + pdata->boost_peak = ((val - 1200)/80) + 1; + } + + pdata->aif_half_drv = of_property_read_bool(np, + "cirrus,aif-half-drv"); + pdata->digsft_disable = of_property_read_bool(np, + "cirrus,digsft-disable"); + + pdata->gain_zc_disable = of_property_read_bool(np, + "cirrus,gain-zc-disable"); + pdata->amp_inv = of_property_read_bool(np, "cirrus,amp-inv"); + + if (of_property_read_u32(np, "cirrus,i2s-sdinloc", &val) >= 0) + pdata->i2s_sdinloc = val; + if (of_property_read_u32(np, "cirrus,tdm-rising-edge", &val) >= 0) + pdata->tdm_rising_edge = val; + + return 0; +} + +static irqreturn_t cs35l34_irq_thread(int irq, void *data) +{ + struct cs35l34_private *cs35l34 = data; + struct snd_soc_component *component = cs35l34->component; + unsigned int sticky1, sticky2, sticky3, sticky4; + unsigned int mask1, mask2, mask3, mask4, current1; + + + /* ack the irq by reading all status registers */ + regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_4, &sticky4); + regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_3, &sticky3); + regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_2, &sticky2); + regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, &sticky1); + + regmap_read(cs35l34->regmap, CS35L34_INT_MASK_4, &mask4); + regmap_read(cs35l34->regmap, CS35L34_INT_MASK_3, &mask3); + regmap_read(cs35l34->regmap, CS35L34_INT_MASK_2, &mask2); + regmap_read(cs35l34->regmap, CS35L34_INT_MASK_1, &mask1); + + if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3) + && !(sticky4 & ~mask4)) + return IRQ_NONE; + + regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, ¤t1); + + if (sticky1 & CS35L34_CAL_ERR) { + dev_err(component->dev, "Cal error\n"); + + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L34_CAL_ERR)) { + dev_dbg(component->dev, "Cal error release\n"); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_CAL_ERR_RLS, 0); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_CAL_ERR_RLS, + CS35L34_CAL_ERR_RLS); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_CAL_ERR_RLS, 0); + /* note: amp will re-calibrate on next resume */ + } + } + + if (sticky1 & CS35L34_ALIVE_ERR) + dev_err(component->dev, "Alive error\n"); + + if (sticky1 & CS35L34_AMP_SHORT) { + dev_crit(component->dev, "Amp short error\n"); + + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L34_AMP_SHORT)) { + dev_dbg(component->dev, + "Amp short error release\n"); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_SHORT_RLS, 0); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_SHORT_RLS, + CS35L34_SHORT_RLS); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_SHORT_RLS, 0); + } + } + + if (sticky1 & CS35L34_OTW) { + dev_crit(component->dev, "Over temperature warning\n"); + + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L34_OTW)) { + dev_dbg(component->dev, + "Over temperature warning release\n"); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTW_RLS, 0); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTW_RLS, + CS35L34_OTW_RLS); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTW_RLS, 0); + } + } + + if (sticky1 & CS35L34_OTE) { + dev_crit(component->dev, "Over temperature error\n"); + + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L34_OTE)) { + dev_dbg(component->dev, + "Over temperature error release\n"); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTE_RLS, 0); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTE_RLS, + CS35L34_OTE_RLS); + regmap_update_bits(cs35l34->regmap, + CS35L34_PROT_RELEASE_CTL, + CS35L34_OTE_RLS, 0); + } + } + + if (sticky3 & CS35L34_BST_HIGH) { + dev_crit(component->dev, "VBST too high error; powering off!\n"); + regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL2, + CS35L34_PDN_AMP, CS35L34_PDN_AMP); + regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL1, + CS35L34_PDN_ALL, CS35L34_PDN_ALL); + } + + if (sticky3 & CS35L34_LBST_SHORT) { + dev_crit(component->dev, "LBST short error; powering off!\n"); + regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL2, + CS35L34_PDN_AMP, CS35L34_PDN_AMP); + regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL1, + CS35L34_PDN_ALL, CS35L34_PDN_ALL); + } + + return IRQ_HANDLED; +} + +static const char * const cs35l34_core_supplies[] = { + "VA", + "VP", +}; + +static int cs35l34_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs35l34_private *cs35l34; + struct cs35l34_platform_data *pdata = + dev_get_platdata(&i2c_client->dev); + int i, devid; + int ret; + unsigned int reg; + + cs35l34 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l34), GFP_KERNEL); + if (!cs35l34) + return -ENOMEM; + + i2c_set_clientdata(i2c_client, cs35l34); + cs35l34->regmap = devm_regmap_init_i2c(i2c_client, &cs35l34_regmap); + if (IS_ERR(cs35l34->regmap)) { + ret = PTR_ERR(cs35l34->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + cs35l34->num_core_supplies = ARRAY_SIZE(cs35l34_core_supplies); + for (i = 0; i < ARRAY_SIZE(cs35l34_core_supplies); i++) + cs35l34->core_supplies[i].supply = cs35l34_core_supplies[i]; + + ret = devm_regulator_bulk_get(&i2c_client->dev, + cs35l34->num_core_supplies, + cs35l34->core_supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request core supplies %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to enable core supplies: %d\n", ret); + return ret; + } + + if (pdata) { + cs35l34->pdata = *pdata; + } else { + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), + GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto err_regulator; + } + + if (i2c_client->dev.of_node) { + ret = cs35l34_handle_of_data(i2c_client, pdata); + if (ret != 0) + goto err_regulator; + + } + cs35l34->pdata = *pdata; + } + + ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL, + cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs35l34", cs35l34); + if (ret != 0) + dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret); + + cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset-gpios", GPIOD_OUT_LOW); + if (IS_ERR(cs35l34->reset_gpio)) { + ret = PTR_ERR(cs35l34->reset_gpio); + goto err_regulator; + } + + gpiod_set_value_cansleep(cs35l34->reset_gpio, 1); + + msleep(CS35L34_START_DELAY); + + devid = cirrus_read_device_id(cs35l34->regmap, CS35L34_DEVID_AB); + if (devid < 0) { + ret = devid; + dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret); + goto err_reset; + } + + if (devid != CS35L34_CHIP_ID) { + dev_err(&i2c_client->dev, + "CS35l34 Device ID (%X). Expected ID %X\n", + devid, CS35L34_CHIP_ID); + ret = -ENODEV; + goto err_reset; + } + + ret = regmap_read(cs35l34->regmap, CS35L34_REV_ID, ®); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed\n"); + goto err_reset; + } + + dev_info(&i2c_client->dev, + "Cirrus Logic CS35l34 (%x), Revision: %02X\n", devid, + reg & 0xFF); + + /* Unmask critical interrupts */ + regmap_update_bits(cs35l34->regmap, CS35L34_INT_MASK_1, + CS35L34_M_CAL_ERR | CS35L34_M_ALIVE_ERR | + CS35L34_M_AMP_SHORT | CS35L34_M_OTW | + CS35L34_M_OTE, 0); + regmap_update_bits(cs35l34->regmap, CS35L34_INT_MASK_3, + CS35L34_M_BST_HIGH | CS35L34_M_LBST_SHORT, 0); + + pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100); + pm_runtime_use_autosuspend(&i2c_client->dev); + pm_runtime_set_active(&i2c_client->dev); + pm_runtime_enable(&i2c_client->dev); + + ret = devm_snd_soc_register_component(&i2c_client->dev, + &soc_component_dev_cs35l34, &cs35l34_dai, 1); + if (ret < 0) { + dev_err(&i2c_client->dev, + "%s: Register component failed\n", __func__); + goto err_reset; + } + + return 0; + +err_reset: + gpiod_set_value_cansleep(cs35l34->reset_gpio, 0); +err_regulator: + regulator_bulk_disable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + + return ret; +} + +static void cs35l34_i2c_remove(struct i2c_client *client) +{ + struct cs35l34_private *cs35l34 = i2c_get_clientdata(client); + + gpiod_set_value_cansleep(cs35l34->reset_gpio, 0); + + pm_runtime_disable(&client->dev); + regulator_bulk_disable(cs35l34->num_core_supplies, + cs35l34->core_supplies); +} + +static int __maybe_unused cs35l34_runtime_resume(struct device *dev) +{ + struct cs35l34_private *cs35l34 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", + ret); + return ret; + } + + regcache_cache_only(cs35l34->regmap, false); + + gpiod_set_value_cansleep(cs35l34->reset_gpio, 1); + msleep(CS35L34_START_DELAY); + + ret = regcache_sync(cs35l34->regmap); + if (ret != 0) { + dev_err(dev, "Failed to restore register cache\n"); + goto err; + } + return 0; +err: + regcache_cache_only(cs35l34->regmap, true); + regulator_bulk_disable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + + return ret; +} + +static int __maybe_unused cs35l34_runtime_suspend(struct device *dev) +{ + struct cs35l34_private *cs35l34 = dev_get_drvdata(dev); + + regcache_cache_only(cs35l34->regmap, true); + regcache_mark_dirty(cs35l34->regmap); + + gpiod_set_value_cansleep(cs35l34->reset_gpio, 0); + + regulator_bulk_disable(cs35l34->num_core_supplies, + cs35l34->core_supplies); + + return 0; +} + +static const struct dev_pm_ops cs35l34_pm_ops = { + SET_RUNTIME_PM_OPS(cs35l34_runtime_suspend, + cs35l34_runtime_resume, + NULL) +}; + +static const struct of_device_id cs35l34_of_match[] = { + {.compatible = "cirrus,cs35l34"}, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l34_of_match); + +static const struct i2c_device_id cs35l34_id[] = { + {"cs35l34", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs35l34_id); + +static struct i2c_driver cs35l34_i2c_driver = { + .driver = { + .name = "cs35l34", + .pm = &cs35l34_pm_ops, + .of_match_table = cs35l34_of_match, + + }, + .id_table = cs35l34_id, + .probe = cs35l34_i2c_probe, + .remove = cs35l34_i2c_remove, + +}; + +static int __init cs35l34_modinit(void) +{ + int ret; + + ret = i2c_add_driver(&cs35l34_i2c_driver); + if (ret != 0) { + pr_err("Failed to register CS35l34 I2C driver: %d\n", ret); + return ret; + } + return 0; +} +module_init(cs35l34_modinit); + +static void __exit cs35l34_exit(void) +{ + i2c_del_driver(&cs35l34_i2c_driver); +} +module_exit(cs35l34_exit); + +MODULE_DESCRIPTION("ASoC CS35l34 driver"); +MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l34.h b/sound/soc/codecs/cs35l34.h new file mode 100644 index 0000000000..97959e334f --- /dev/null +++ b/sound/soc/codecs/cs35l34.h @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * cs35l34.h -- CS35L34 ALSA SoC audio driver + * + * Copyright 2016 Cirrus Logic, Inc. + * + * Author: Paul Handrigan + */ + +#ifndef __CS35L34_H__ +#define __CS35L34_H__ + +#define CS35L34_CHIP_ID 0x00035A34 +#define CS35L34_DEVID_AB 0x01 /* Device ID A & B [RO] */ +#define CS35L34_DEVID_CD 0x02 /* Device ID C & D [RO] */ +#define CS35L34_DEVID_E 0x03 /* Device ID E [RO] */ +#define CS35L34_FAB_ID 0x04 /* Fab ID [RO] */ +#define CS35L34_REV_ID 0x05 /* Revision ID [RO] */ +#define CS35L34_PWRCTL1 0x06 /* Power Ctl 1 */ +#define CS35L34_PWRCTL2 0x07 /* Power Ctl 2 */ +#define CS35L34_PWRCTL3 0x08 /* Power Ctl 3 */ +#define CS35L34_ADSP_CLK_CTL 0x0A /* (ADSP) Clock Ctl */ +#define CS35L34_MCLK_CTL 0x0B /* Master Clocking Ctl */ +#define CS35L34_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */ +#define CS35L34_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */ +#define CS35L34_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */ +#define CS35L34_AMP_ANLG_GAIN_CTL 0x17 /* Amplifier Analog Gain Ctl */ +#define CS35L34_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */ +#define CS35L34_AMP_KEEP_ALIVE_CTL 0x1A /* Amplifier Keep Alive Ctl */ +#define CS35L34_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */ +#define CS35L34_BST_PEAK_I 0x1E /* Boost Conv Peak Current */ +#define CS35L34_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */ +#define CS35L34_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */ +#define CS35L34_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */ +#define CS35L34_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */ +#define CS35L34_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */ +#define CS35L34_CLASS_H_CTL 0x30 /* CLS H Control */ +#define CS35L34_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */ +#define CS35L34_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */ +#define CS35L34_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */ +#define CS35L34_CLASS_H_STATUS 0x38 /* CLS H Status */ +#define CS35L34_VPBR_CTL 0x3A /* VPBR Ctl */ +#define CS35L34_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */ +#define CS35L34_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */ +#define CS35L34_PRED_MAX_ATTEN_SPK_LOAD 0x40 /* PRD Max Atten / Spkr Load */ +#define CS35L34_PRED_BROWNOUT_THRESH 0x41 /* PRD Brownout Threshold */ +#define CS35L34_PRED_BROWNOUT_VOL_CTL 0x42 /* PRD Brownout Volume Ctl */ +#define CS35L34_PRED_BROWNOUT_RATE_CTL 0x43 /* PRD Brownout Rate Ctl */ +#define CS35L34_PRED_WAIT_CTL 0x44 /* PRD Wait Ctl */ +#define CS35L34_PRED_ZVP_INIT_IMP_CTL 0x46 /* PRD ZVP Initial Imp Ctl */ +#define CS35L34_PRED_MAN_SAFE_VPI_CTL 0x47 /* PRD Manual Safe VPI Ctl */ +#define CS35L34_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */ +#define CS35L34_PRED_BRWNOUT_ATT_STATUS 0x4C /* PRD Brownout Atten Status */ +#define CS35L34_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */ +#define CS35L34_ADSP_I2S_CTL 0x50 /* ADSP I2S Ctl */ +#define CS35L34_ADSP_TDM_CTL 0x51 /* ADSP TDM Ctl */ +#define CS35L34_TDM_TX_CTL_1_VMON 0x52 /* TDM TX Ctl 1 (VMON) */ +#define CS35L34_TDM_TX_CTL_2_IMON 0x53 /* TDM TX Ctl 2 (IMON) */ +#define CS35L34_TDM_TX_CTL_3_VPMON 0x54 /* TDM TX Ctl 3 (VPMON) */ +#define CS35L34_TDM_TX_CTL_4_VBSTMON 0x55 /* TDM TX Ctl 4 (VBSTMON) */ +#define CS35L34_TDM_TX_CTL_5_FLAG1 0x56 /* TDM TX Ctl 5 (FLAG1) */ +#define CS35L34_TDM_TX_CTL_6_FLAG2 0x57 /* TDM TX Ctl 6 (FLAG2) */ +#define CS35L34_TDM_TX_SLOT_EN_1 0x5A /* TDM TX Slot Enable */ +#define CS35L34_TDM_TX_SLOT_EN_2 0x5B /* TDM TX Slot Enable */ +#define CS35L34_TDM_TX_SLOT_EN_3 0x5C /* TDM TX Slot Enable */ +#define CS35L34_TDM_TX_SLOT_EN_4 0x5D /* TDM TX Slot Enable */ +#define CS35L34_TDM_RX_CTL_1_AUDIN 0x5E /* TDM RX Ctl 1 */ +#define CS35L34_TDM_RX_CTL_3_ALIVE 0x60 /* TDM RX Ctl 3 (ALIVE) */ +#define CS35L34_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */ +#define CS35L34_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */ +#define CS35L34_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */ +#define CS35L34_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */ +#define CS35L34_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */ +#define CS35L34_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */ +#define CS35L34_INT_MASK_1 0x70 /* Interrupt Mask 1 */ +#define CS35L34_INT_MASK_2 0x71 /* Interrupt Mask 2 */ +#define CS35L34_INT_MASK_3 0x72 /* Interrupt Mask 3 */ +#define CS35L34_INT_MASK_4 0x73 /* Interrupt Mask 4 */ +#define CS35L34_INT_STATUS_1 0x74 /* Interrupt Status 1 */ +#define CS35L34_INT_STATUS_2 0x75 /* Interrupt Status 2 */ +#define CS35L34_INT_STATUS_3 0x76 /* Interrupt Status 3 */ +#define CS35L34_INT_STATUS_4 0x77 /* Interrupt Status 4 */ +#define CS35L34_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */ + +#define CS35L34_MAX_REGISTER 0x7F +#define CS35L34_REGISTER_COUNT 0x4E + +#define CS35L34_MCLK_5644 5644800 +#define CS35L34_MCLK_6144 6144000 +#define CS35L34_MCLK_6 6000000 +#define CS35L34_MCLK_11289 11289600 +#define CS35L34_MCLK_12 12000000 +#define CS35L34_MCLK_12288 12288000 + +/* CS35L34_PWRCTL1 */ +#define CS35L34_SFT_RST (1 << 7) +#define CS35L34_DISCHG_FLT (1 << 1) +#define CS35L34_PDN_ALL 1 + +/* CS35L34_PWRCTL2 */ +#define CS35L34_PDN_VMON (1 << 7) +#define CS35L34_PDN_IMON (1 << 6) +#define CS35L34_PDN_CLASSH (1 << 5) +#define CS35L34_PDN_VPBR (1 << 4) +#define CS35L34_PDN_PRED (1 << 3) +#define CS35L34_PDN_BST (1 << 2) +#define CS35L34_PDN_AMP 1 + +/* CS35L34_PWRCTL3 */ +#define CS35L34_MCLK_DIS (1 << 7) +#define CS35L34_PDN_VBSTMON_OUT (1 << 4) +#define CS35L34_PDN_VMON_OUT (1 << 3) +/* Tristate the ADSP SDOUT when in I2C mode */ +#define CS35L34_PDN_SDOUT (1 << 2) +#define CS35L34_PDN_SDIN (1 << 1) +#define CS35L34_PDN_TDM 1 + +/* CS35L34_ADSP_CLK_CTL */ +#define CS35L34_ADSP_RATE 0xF +#define CS35L34_ADSP_DRIVE (1 << 4) +#define CS35L34_ADSP_M_S (1 << 7) + +/* CS35L34_MCLK_CTL */ +#define CS35L34_MCLK_DIV (1 << 4) +#define CS35L34_MCLK_RATE_MASK 0x7 +#define CS35L34_MCLK_RATE_6P1440 0x2 +#define CS35L34_MCLK_RATE_6P0000 0x1 +#define CS35L34_MCLK_RATE_5P6448 0x0 +#define CS35L34_MCLKDIS (1 << 7) +#define CS35L34_MCLKDIV2 (1 << 6) +#define CS35L34_SDOUT_3ST_TDM (1 << 5) +#define CS35L34_INT_FS_RATE (1 << 4) +#define CS35L34_ADSP_FS 0xF + +/* CS35L34_AMP_INP_DRV_CTL */ +#define CS35L34_DRV_STR_SRC (1 << 1) +#define CS35L34_DRV_STR 1 + +/* CS35L34_AMP_DIG_VOL_CTL */ +#define CS35L34_AMP_DSR_RATE_MASK 0xF0 +#define CS35L34_AMP_DSR_RATE_SHIFT (1 << 4) +#define CS35L34_NOTCH_DIS (1 << 3) +#define CS35L34_AMP_DIGSFT (1 << 1) +#define CS35L34_INV 1 + +/* CS35L34_PROTECT_CTL */ +#define CS35L34_OTW_ATTN_MASK 0xC +#define CS35L34_OTW_THRD_MASK 0x3 +#define CS35L34_MUTE (1 << 5) +#define CS35L34_GAIN_ZC (1 << 4) +#define CS35L34_GAIN_ZC_MASK 0x10 +#define CS35L34_GAIN_ZC_SHIFT 4 + +/* CS35L34_AMP_KEEP_ALIVE_CTL */ +#define CS35L34_ALIVE_WD_DIS (1 << 2) + +/* CS35L34_BST_CVTR_V_CTL */ +#define CS35L34_BST_CVTL_MASK 0x3F + +/* CS35L34_BST_PEAK_I */ +#define CS35L34_BST_PEAK_MASK 0x3F + +/* CS35L34_ADSP_I2S_CTL */ +#define CS35L34_I2S_LOC_MASK 0xC +#define CS35L34_I2S_LOC_SHIFT 2 + +/* CS35L34_MULT_DEV_SYNCH2 */ +#define CS35L34_SYNC2_MASK 0xF + +/* CS35L34_PROT_RELEASE_CTL */ +#define CS35L34_CAL_ERR_RLS (1 << 7) +#define CS35L34_SHORT_RLS (1 << 2) +#define CS35L34_OTW_RLS (1 << 1) +#define CS35L34_OTE_RLS 1 + +/* CS35L34_INT_MASK_1 */ +#define CS35L34_M_CAL_ERR_SHIFT 7 +#define CS35L34_M_CAL_ERR (1 << CS35L34_M_CAL_ERR_SHIFT) +#define CS35L34_M_ALIVE_ERR_SHIFT 5 +#define CS35L34_M_ALIVE_ERR (1 << CS35L34_M_ALIVE_ERR_SHIFT) +#define CS35L34_M_ADSP_CLK_SHIFT 4 +#define CS35L34_M_ADSP_CLK_ERR (1 << CS35L34_M_ADSP_CLK_SHIFT) +#define CS35L34_M_MCLK_SHIFT 3 +#define CS35L34_M_MCLK_ERR (1 << CS35L34_M_MCLK_SHIFT) +#define CS35L34_M_AMP_SHORT_SHIFT 2 +#define CS35L34_M_AMP_SHORT (1 << CS35L34_M_AMP_SHORT_SHIFT) +#define CS35L34_M_OTW_SHIFT 1 +#define CS35L34_M_OTW (1 << CS35L34_M_OTW_SHIFT) +#define CS35L34_M_OTE_SHIFT 0 +#define CS35L34_M_OTE (1 << CS35L34_M_OTE_SHIFT) + +/* CS35L34_INT_MASK_2 */ +#define CS35L34_M_PDN_DONE_SHIFT 4 +#define CS35L34_M_PDN_DONE (1 << CS35L34_M_PDN_DONE_SHIFT) +#define CS35L34_M_PRED_SHIFT 3 +#define CS35L34_M_PRED_ERR (1 << CS35L34_M_PRED_SHIFT) +#define CS35L34_M_PRED_CLR_SHIFT 2 +#define CS35L34_M_PRED_CLR (1 << CS35L34_M_PRED_CLR_SHIFT) +#define CS35L34_M_VPBR_SHIFT 1 +#define CS35L34_M_VPBR_ERR (1 << CS35L34_M_VPBR_SHIFT) +#define CS35L34_M_VPBR_CLR_SHIFT 0 +#define CS35L34_M_VPBR_CLR (1 << CS35L34_M_VPBR_CLR_SHIFT) + +/* CS35L34_INT_MASK_3 */ +#define CS35L34_M_BST_HIGH_SHIFT 4 +#define CS35L34_M_BST_HIGH (1 << CS35L34_M_BST_HIGH_SHIFT) +#define CS35L34_M_BST_HIGH_FLAG_SHIFT 3 +#define CS35L34_M_BST_HIGH_FLAG (1 << CS35L34_M_BST_HIGH_FLAG_SHIFT) +#define CS35L34_M_BST_IPK_FLAG_SHIFT 2 +#define CS35L34_M_BST_IPK_FLAG (1 << CS35L34_M_BST_IPK_FLAG_SHIFT) +#define CS35L34_M_LBST_SHORT_SHIFT 0 +#define CS35L34_M_LBST_SHORT (1 << CS35L34_M_LBST_SHORT_SHIFT) + +/* CS35L34_INT_MASK_4 */ +#define CS35L34_M_VMON_OVFL_SHIFT 3 +#define CS35L34_M_VMON_OVFL (1 << CS35L34_M_VMON_OVFL_SHIFT) +#define CS35L34_M_IMON_OVFL_SHIFT 2 +#define CS35L34_M_IMON_OVFL (1 << CS35L34_M_IMON_OVFL_SHIFT) +#define CS35L34_M_VPMON_OVFL_SHIFT 1 +#define CS35L34_M_VPMON_OVFL (1 << CS35L34_M_VPMON_OVFL_SHIFT) +#define CS35L34_M_VBSTMON_OVFL_SHIFT 1 +#define CS35L34_M_VBSTMON_OVFL (1 << CS35L34_M_VBSTMON_OVFL_SHIFT) + +/* CS35L34_INT_1 */ +#define CS35L34_CAL_ERR (1 << CS35L34_M_CAL_ERR_SHIFT) +#define CS35L34_ALIVE_ERR (1 << CS35L34_M_ALIVE_ERR_SHIFT) +#define CS35L34_M_ADSP_CLK_ERR (1 << CS35L34_M_ADSP_CLK_SHIFT) +#define CS35L34_MCLK_ERR (1 << CS35L34_M_MCLK_SHIFT) +#define CS35L34_AMP_SHORT (1 << CS35L34_M_AMP_SHORT_SHIFT) +#define CS35L34_OTW (1 << CS35L34_M_OTW_SHIFT) +#define CS35L34_OTE (1 << CS35L34_M_OTE_SHIFT) + +/* CS35L34_INT_2 */ +#define CS35L34_PDN_DONE (1 << CS35L34_M_PDN_DONE_SHIFT) +#define CS35L34_PRED_ERR (1 << CS35L34_M_PRED_SHIFT) +#define CS35L34_PRED_CLR (1 << CS35L34_M_PRED_CLR_SHIFT) +#define CS35L34_VPBR_ERR (1 << CS35L34_M_VPBR_SHIFT) +#define CS35L34_VPBR_CLR (1 << CS35L34_M_VPBR_CLR_SHIFT) + +/* CS35L34_INT_3 */ +#define CS35L34_BST_HIGH (1 << CS35L34_M_BST_HIGH_SHIFT) +#define CS35L34_BST_HIGH_FLAG (1 << CS35L34_M_BST_HIGH_FLAG_SHIFT) +#define CS35L34_BST_IPK_FLAG (1 << CS35L34_M_BST_IPK_FLAG_SHIFT) +#define CS35L34_LBST_SHORT (1 << CS35L34_M_LBST_SHORT_SHIFT) + +/* CS35L34_INT_4 */ +#define CS35L34_VMON_OVFL (1 << CS35L34_M_VMON_OVFL_SHIFT) +#define CS35L34_IMON_OVFL (1 << CS35L34_M_IMON_OVFL_SHIFT) +#define CS35L34_VPMON_OVFL (1 << CS35L34_M_VPMON_OVFL_SHIFT) +#define CS35L34_VBSTMON_OVFL (1 << CS35L34_M_VBSTMON_OVFL_SHIFT) + +/* CS35L34_{RX,TX}_X */ +#define CS35L34_X_STATE_SHIFT 7 +#define CS35L34_X_STATE (1 << CS35L34_X_STATE_SHIFT) +#define CS35L34_X_LOC_SHIFT 0 +#define CS35L34_X_LOC (0x1F << CS35L34_X_LOC_SHIFT) + +#define CS35L34_RATES (SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_32000) +#define CS35L34_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#endif diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c new file mode 100644 index 0000000000..0a4b5aa781 --- /dev/null +++ b/sound/soc/codecs/cs35l35.c @@ -0,0 +1,1665 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs35l35.c -- CS35L35 ALSA SoC audio driver + * + * Copyright 2017 Cirrus Logic, Inc. + * + * Author: Brian Austin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l35.h" +#include "cirrus_legacy.h" + +/* + * Some fields take zero as a valid value so use a high bit flag that won't + * get written to the device to mark those. + */ +#define CS35L35_VALID_PDATA 0x80000000 + +static const struct reg_default cs35l35_reg[] = { + {CS35L35_PWRCTL1, 0x01}, + {CS35L35_PWRCTL2, 0x11}, + {CS35L35_PWRCTL3, 0x00}, + {CS35L35_CLK_CTL1, 0x04}, + {CS35L35_CLK_CTL2, 0x12}, + {CS35L35_CLK_CTL3, 0xCF}, + {CS35L35_SP_FMT_CTL1, 0x20}, + {CS35L35_SP_FMT_CTL2, 0x00}, + {CS35L35_SP_FMT_CTL3, 0x02}, + {CS35L35_MAG_COMP_CTL, 0x00}, + {CS35L35_AMP_INP_DRV_CTL, 0x01}, + {CS35L35_AMP_DIG_VOL_CTL, 0x12}, + {CS35L35_AMP_DIG_VOL, 0x00}, + {CS35L35_ADV_DIG_VOL, 0x00}, + {CS35L35_PROTECT_CTL, 0x06}, + {CS35L35_AMP_GAIN_AUD_CTL, 0x13}, + {CS35L35_AMP_GAIN_PDM_CTL, 0x00}, + {CS35L35_AMP_GAIN_ADV_CTL, 0x00}, + {CS35L35_GPI_CTL, 0x00}, + {CS35L35_BST_CVTR_V_CTL, 0x00}, + {CS35L35_BST_PEAK_I, 0x07}, + {CS35L35_BST_RAMP_CTL, 0x85}, + {CS35L35_BST_CONV_COEF_1, 0x24}, + {CS35L35_BST_CONV_COEF_2, 0x24}, + {CS35L35_BST_CONV_SLOPE_COMP, 0x4E}, + {CS35L35_BST_CONV_SW_FREQ, 0x04}, + {CS35L35_CLASS_H_CTL, 0x0B}, + {CS35L35_CLASS_H_HEADRM_CTL, 0x0B}, + {CS35L35_CLASS_H_RELEASE_RATE, 0x08}, + {CS35L35_CLASS_H_FET_DRIVE_CTL, 0x41}, + {CS35L35_CLASS_H_VP_CTL, 0xC5}, + {CS35L35_VPBR_CTL, 0x0A}, + {CS35L35_VPBR_VOL_CTL, 0x90}, + {CS35L35_VPBR_TIMING_CTL, 0x6A}, + {CS35L35_VPBR_MODE_VOL_CTL, 0x00}, + {CS35L35_SPKR_MON_CTL, 0xC0}, + {CS35L35_IMON_SCALE_CTL, 0x30}, + {CS35L35_AUDIN_RXLOC_CTL, 0x00}, + {CS35L35_ADVIN_RXLOC_CTL, 0x80}, + {CS35L35_VMON_TXLOC_CTL, 0x00}, + {CS35L35_IMON_TXLOC_CTL, 0x80}, + {CS35L35_VPMON_TXLOC_CTL, 0x04}, + {CS35L35_VBSTMON_TXLOC_CTL, 0x84}, + {CS35L35_VPBR_STATUS_TXLOC_CTL, 0x04}, + {CS35L35_ZERO_FILL_LOC_CTL, 0x00}, + {CS35L35_AUDIN_DEPTH_CTL, 0x0F}, + {CS35L35_SPKMON_DEPTH_CTL, 0x0F}, + {CS35L35_SUPMON_DEPTH_CTL, 0x0F}, + {CS35L35_ZEROFILL_DEPTH_CTL, 0x00}, + {CS35L35_MULT_DEV_SYNCH1, 0x02}, + {CS35L35_MULT_DEV_SYNCH2, 0x80}, + {CS35L35_PROT_RELEASE_CTL, 0x00}, + {CS35L35_DIAG_MODE_REG_LOCK, 0x00}, + {CS35L35_DIAG_MODE_CTL_1, 0x40}, + {CS35L35_DIAG_MODE_CTL_2, 0x00}, + {CS35L35_INT_MASK_1, 0xFF}, + {CS35L35_INT_MASK_2, 0xFF}, + {CS35L35_INT_MASK_3, 0xFF}, + {CS35L35_INT_MASK_4, 0xFF}, + +}; + +static bool cs35l35_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L35_INT_STATUS_1: + case CS35L35_INT_STATUS_2: + case CS35L35_INT_STATUS_3: + case CS35L35_INT_STATUS_4: + case CS35L35_PLL_STATUS: + case CS35L35_OTP_TRIM_STATUS: + return true; + default: + return false; + } +} + +static bool cs35l35_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L35_DEVID_AB ... CS35L35_PWRCTL3: + case CS35L35_CLK_CTL1 ... CS35L35_SP_FMT_CTL3: + case CS35L35_MAG_COMP_CTL ... CS35L35_AMP_GAIN_AUD_CTL: + case CS35L35_AMP_GAIN_PDM_CTL ... CS35L35_BST_PEAK_I: + case CS35L35_BST_RAMP_CTL ... CS35L35_BST_CONV_SW_FREQ: + case CS35L35_CLASS_H_CTL ... CS35L35_CLASS_H_VP_CTL: + case CS35L35_CLASS_H_STATUS: + case CS35L35_VPBR_CTL ... CS35L35_VPBR_MODE_VOL_CTL: + case CS35L35_VPBR_ATTEN_STATUS: + case CS35L35_SPKR_MON_CTL: + case CS35L35_IMON_SCALE_CTL ... CS35L35_ZEROFILL_DEPTH_CTL: + case CS35L35_MULT_DEV_SYNCH1 ... CS35L35_PROT_RELEASE_CTL: + case CS35L35_DIAG_MODE_REG_LOCK ... CS35L35_DIAG_MODE_CTL_2: + case CS35L35_INT_MASK_1 ... CS35L35_PLL_STATUS: + case CS35L35_OTP_TRIM_STATUS: + return true; + default: + return false; + } +} + +static bool cs35l35_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L35_INT_STATUS_1: + case CS35L35_INT_STATUS_2: + case CS35L35_INT_STATUS_3: + case CS35L35_INT_STATUS_4: + case CS35L35_PLL_STATUS: + case CS35L35_OTP_TRIM_STATUS: + return true; + default: + return false; + } +} + +static void cs35l35_reset(struct cs35l35_private *cs35l35) +{ + gpiod_set_value_cansleep(cs35l35->reset_gpio, 0); + usleep_range(2000, 2100); + gpiod_set_value_cansleep(cs35l35->reset_gpio, 1); + usleep_range(1000, 1100); +} + +static int cs35l35_wait_for_pdn(struct cs35l35_private *cs35l35) +{ + int ret; + + if (cs35l35->pdata.ext_bst) { + usleep_range(5000, 5500); + return 0; + } + + reinit_completion(&cs35l35->pdn_done); + + ret = wait_for_completion_timeout(&cs35l35->pdn_done, + msecs_to_jiffies(100)); + if (ret == 0) { + dev_err(cs35l35->dev, "PDN_DONE did not complete\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, + CS35L35_MCLK_DIS_MASK, + 0 << CS35L35_MCLK_DIS_SHIFT); + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, + CS35L35_DISCHG_FILT_MASK, + 0 << CS35L35_DISCHG_FILT_SHIFT); + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, + CS35L35_PDN_ALL_MASK, 0); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, + CS35L35_DISCHG_FILT_MASK, + 1 << CS35L35_DISCHG_FILT_SHIFT); + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, + CS35L35_PDN_ALL_MASK, 1); + + /* Already muted, so disable volume ramp for faster shutdown */ + regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL, + CS35L35_AMP_DIGSFT_MASK, 0); + + ret = cs35l35_wait_for_pdn(cs35l35); + + regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, + CS35L35_MCLK_DIS_MASK, + 1 << CS35L35_MCLK_DIS_SHIFT); + + regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL, + CS35L35_AMP_DIGSFT_MASK, + 1 << CS35L35_AMP_DIGSFT_SHIFT); + break; + default: + dev_err(component->dev, "Invalid event = 0x%x\n", event); + ret = -EINVAL; + } + return ret; +} + +static int cs35l35_main_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component); + unsigned int reg[4]; + int i; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (cs35l35->pdata.bst_pdn_fet_on) + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, + CS35L35_PDN_BST_MASK, + 0 << CS35L35_PDN_BST_FETON_SHIFT); + else + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, + CS35L35_PDN_BST_MASK, + 0 << CS35L35_PDN_BST_FETOFF_SHIFT); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(5000, 5100); + /* If in PDM mode we must use VP for Voltage control */ + if (cs35l35->pdm_mode) + regmap_update_bits(cs35l35->regmap, + CS35L35_BST_CVTR_V_CTL, + CS35L35_BST_CTL_MASK, + 0 << CS35L35_BST_CTL_SHIFT); + + regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL, + CS35L35_AMP_MUTE_MASK, 0); + + for (i = 0; i < 2; i++) + regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1, + ®, ARRAY_SIZE(reg)); + + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL, + CS35L35_AMP_MUTE_MASK, + 1 << CS35L35_AMP_MUTE_SHIFT); + if (cs35l35->pdata.bst_pdn_fet_on) + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, + CS35L35_PDN_BST_MASK, + 1 << CS35L35_PDN_BST_FETON_SHIFT); + else + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, + CS35L35_PDN_BST_MASK, + 1 << CS35L35_PDN_BST_FETOFF_SHIFT); + break; + case SND_SOC_DAPM_POST_PMD: + usleep_range(5000, 5100); + /* + * If PDM mode we should switch back to pdata value + * for Voltage control when we go down + */ + if (cs35l35->pdm_mode) + regmap_update_bits(cs35l35->regmap, + CS35L35_BST_CVTR_V_CTL, + CS35L35_BST_CTL_MASK, + cs35l35->pdata.bst_vctl + << CS35L35_BST_CTL_SHIFT); + + break; + default: + dev_err(component->dev, "Invalid event = 0x%x\n", event); + } + return 0; +} + +static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1); +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0); + +static const struct snd_kcontrol_new cs35l35_aud_controls[] = { + SOC_SINGLE_SX_TLV("Digital Audio Volume", CS35L35_AMP_DIG_VOL, + 0, 0x34, 0xE4, dig_vol_tlv), + SOC_SINGLE_TLV("Analog Audio Volume", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0, + amp_gain_tlv), + SOC_SINGLE_TLV("PDM Volume", CS35L35_AMP_GAIN_PDM_CTL, 0, 19, 0, + amp_gain_tlv), +}; + +static const struct snd_kcontrol_new cs35l35_adv_controls[] = { + SOC_SINGLE_SX_TLV("Digital Advisory Volume", CS35L35_ADV_DIG_VOL, + 0, 0x34, 0xE4, dig_vol_tlv), + SOC_SINGLE_TLV("Analog Advisory Volume", CS35L35_AMP_GAIN_ADV_CTL, 0, 19, 0, + amp_gain_tlv), +}; + +static const struct snd_soc_dapm_widget cs35l35_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L35_PWRCTL3, 1, 1, + cs35l35_sdin_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L35_PWRCTL3, 2, 1), + + SND_SOC_DAPM_OUTPUT("SPK"), + + SND_SOC_DAPM_INPUT("VP"), + SND_SOC_DAPM_INPUT("VBST"), + SND_SOC_DAPM_INPUT("ISENSE"), + SND_SOC_DAPM_INPUT("VSENSE"), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L35_PWRCTL2, 7, 1), + SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L35_PWRCTL2, 6, 1), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L35_PWRCTL3, 3, 1), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L35_PWRCTL3, 4, 1), + SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L35_PWRCTL2, 5, 1), + + SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L35_PWRCTL2, 0, 1, NULL, 0, + cs35l35_main_amp_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), +}; + +static const struct snd_soc_dapm_route cs35l35_audio_map[] = { + {"VPMON ADC", NULL, "VP"}, + {"VBSTMON ADC", NULL, "VBST"}, + {"IMON ADC", NULL, "ISENSE"}, + {"VMON ADC", NULL, "VSENSE"}, + {"SDOUT", NULL, "IMON ADC"}, + {"SDOUT", NULL, "VMON ADC"}, + {"SDOUT", NULL, "VBSTMON ADC"}, + {"SDOUT", NULL, "VPMON ADC"}, + {"AMP Capture", NULL, "SDOUT"}, + + {"SDIN", NULL, "AMP Playback"}, + {"CLASS H", NULL, "SDIN"}, + {"Main AMP", NULL, "CLASS H"}, + {"SPK", NULL, "Main AMP"}, +}; + +static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, + CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT); + cs35l35->clock_consumer = false; + break; + case SND_SOC_DAIFMT_CBC_CFC: + regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, + CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT); + cs35l35->clock_consumer = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + cs35l35->i2s_mode = true; + cs35l35->pdm_mode = false; + break; + case SND_SOC_DAIFMT_PDM: + cs35l35->pdm_mode = true; + cs35l35->i2s_mode = false; + break; + default: + return -EINVAL; + } + + return 0; +} + +struct cs35l35_sysclk_config { + int sysclk; + int srate; + u8 clk_cfg; +}; + +static struct cs35l35_sysclk_config cs35l35_clk_ctl[] = { + + /* SYSCLK, Sample Rate, Serial Port Cfg */ + {5644800, 44100, 0x00}, + {5644800, 88200, 0x40}, + {6144000, 48000, 0x10}, + {6144000, 96000, 0x50}, + {11289600, 44100, 0x01}, + {11289600, 88200, 0x41}, + {11289600, 176400, 0x81}, + {12000000, 44100, 0x03}, + {12000000, 48000, 0x13}, + {12000000, 88200, 0x43}, + {12000000, 96000, 0x53}, + {12000000, 176400, 0x83}, + {12000000, 192000, 0x93}, + {12288000, 48000, 0x11}, + {12288000, 96000, 0x51}, + {12288000, 192000, 0x91}, + {13000000, 44100, 0x07}, + {13000000, 48000, 0x17}, + {13000000, 88200, 0x47}, + {13000000, 96000, 0x57}, + {13000000, 176400, 0x87}, + {13000000, 192000, 0x97}, + {22579200, 44100, 0x02}, + {22579200, 88200, 0x42}, + {22579200, 176400, 0x82}, + {24000000, 44100, 0x0B}, + {24000000, 48000, 0x1B}, + {24000000, 88200, 0x4B}, + {24000000, 96000, 0x5B}, + {24000000, 176400, 0x8B}, + {24000000, 192000, 0x9B}, + {24576000, 48000, 0x12}, + {24576000, 96000, 0x52}, + {24576000, 192000, 0x92}, + {26000000, 44100, 0x0F}, + {26000000, 48000, 0x1F}, + {26000000, 88200, 0x4F}, + {26000000, 96000, 0x5F}, + {26000000, 176400, 0x8F}, + {26000000, 192000, 0x9F}, +}; + +static int cs35l35_get_clk_config(int sysclk, int srate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) { + if (cs35l35_clk_ctl[i].sysclk == sysclk && + cs35l35_clk_ctl[i].srate == srate) + return cs35l35_clk_ctl[i].clk_cfg; + } + return -EINVAL; +} + +static int cs35l35_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component); + struct classh_cfg *classh = &cs35l35->pdata.classh_algo; + int srate = params_rate(params); + int ret = 0; + u8 sp_sclks; + int audin_format; + int errata_chk; + + int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate); + + if (clk_ctl < 0) { + dev_err(component->dev, "Invalid CLK:Rate %d:%d\n", + cs35l35->sysclk, srate); + return -EINVAL; + } + + ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2, + CS35L35_CLK_CTL2_MASK, clk_ctl); + if (ret != 0) { + dev_err(component->dev, "Failed to set port config %d\n", ret); + return ret; + } + + /* + * Rev A0 Errata + * When configured for the weak-drive detection path (CH_WKFET_DIS = 0) + * the Class H algorithm does not enable weak-drive operation for + * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10 + */ + errata_chk = (clk_ctl & CS35L35_SP_RATE_MASK) >> CS35L35_SP_RATE_SHIFT; + + if (classh->classh_wk_fet_disable == 0x00 && + (errata_chk == 0x01 || errata_chk == 0x02)) { + ret = regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_FET_DRIVE_CTL, + CS35L35_CH_WKFET_DEL_MASK, + 0 << CS35L35_CH_WKFET_DEL_SHIFT); + if (ret != 0) { + dev_err(component->dev, "Failed to set fet config %d\n", + ret); + return ret; + } + } + + /* + * You can pull more Monitor data from the SDOUT pin than going to SDIN + * Just make sure your SCLK is fast enough to fill the frame + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (params_width(params)) { + case 8: + audin_format = CS35L35_SDIN_DEPTH_8; + break; + case 16: + audin_format = CS35L35_SDIN_DEPTH_16; + break; + case 24: + audin_format = CS35L35_SDIN_DEPTH_24; + break; + default: + dev_err(component->dev, "Unsupported Width %d\n", + params_width(params)); + return -EINVAL; + } + regmap_update_bits(cs35l35->regmap, + CS35L35_AUDIN_DEPTH_CTL, + CS35L35_AUDIN_DEPTH_MASK, + audin_format << + CS35L35_AUDIN_DEPTH_SHIFT); + if (cs35l35->pdata.stereo) { + regmap_update_bits(cs35l35->regmap, + CS35L35_AUDIN_DEPTH_CTL, + CS35L35_ADVIN_DEPTH_MASK, + audin_format << + CS35L35_ADVIN_DEPTH_SHIFT); + } + } + + if (cs35l35->i2s_mode) { + /* We have to take the SCLK to derive num sclks + * to configure the CLOCK_CTL3 register correctly + */ + if ((cs35l35->sclk / srate) % 4) { + dev_err(component->dev, "Unsupported sclk/fs ratio %d:%d\n", + cs35l35->sclk, srate); + return -EINVAL; + } + sp_sclks = ((cs35l35->sclk / srate) / 4) - 1; + + /* Only certain ratios supported when device is a clock consumer */ + if (cs35l35->clock_consumer) { + switch (sp_sclks) { + case CS35L35_SP_SCLKS_32FS: + case CS35L35_SP_SCLKS_48FS: + case CS35L35_SP_SCLKS_64FS: + break; + default: + dev_err(component->dev, "ratio not supported\n"); + return -EINVAL; + } + } else { + /* Only certain ratios supported when device is a clock provider */ + switch (sp_sclks) { + case CS35L35_SP_SCLKS_32FS: + case CS35L35_SP_SCLKS_64FS: + break; + default: + dev_err(component->dev, "ratio not supported\n"); + return -EINVAL; + } + } + ret = regmap_update_bits(cs35l35->regmap, + CS35L35_CLK_CTL3, + CS35L35_SP_SCLKS_MASK, sp_sclks << + CS35L35_SP_SCLKS_SHIFT); + if (ret != 0) { + dev_err(component->dev, "Failed to set fsclk %d\n", ret); + return ret; + } + } + + return ret; +} + +static const unsigned int cs35l35_src_rates[] = { + 44100, 48000, 88200, 96000, 176400, 192000 +}; + +static const struct snd_pcm_hw_constraint_list cs35l35_constraints = { + .count = ARRAY_SIZE(cs35l35_src_rates), + .list = cs35l35_src_rates, +}; + +static int cs35l35_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component); + + if (!substream->runtime) + return 0; + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &cs35l35_constraints); + + regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL, + CS35L35_PDM_MODE_MASK, + 0 << CS35L35_PDM_MODE_SHIFT); + + return 0; +} + +static const unsigned int cs35l35_pdm_rates[] = { + 44100, 48000, 88200, 96000 +}; + +static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = { + .count = ARRAY_SIZE(cs35l35_pdm_rates), + .list = cs35l35_pdm_rates, +}; + +static int cs35l35_pdm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component); + + if (!substream->runtime) + return 0; + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &cs35l35_pdm_constraints); + + regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL, + CS35L35_PDM_MODE_MASK, + 1 << CS35L35_PDM_MODE_SHIFT); + + return 0; +} + +static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component); + + /* Need the SCLK Frequency regardless of sysclk source for I2S */ + cs35l35->sclk = freq; + + return 0; +} + +static const struct snd_soc_dai_ops cs35l35_ops = { + .startup = cs35l35_pcm_startup, + .set_fmt = cs35l35_set_dai_fmt, + .hw_params = cs35l35_hw_params, + .set_sysclk = cs35l35_dai_set_sysclk, +}; + +static const struct snd_soc_dai_ops cs35l35_pdm_ops = { + .startup = cs35l35_pdm_startup, + .set_fmt = cs35l35_set_dai_fmt, + .hw_params = cs35l35_hw_params, +}; + +static struct snd_soc_dai_driver cs35l35_dai[] = { + { + .name = "cs35l35-pcm", + .id = 0, + .playback = { + .stream_name = "AMP Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L35_FORMATS, + }, + .capture = { + .stream_name = "AMP Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L35_FORMATS, + }, + .ops = &cs35l35_ops, + .symmetric_rate = 1, + }, + { + .name = "cs35l35-pdm", + .id = 1, + .playback = { + .stream_name = "PDM Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L35_FORMATS, + }, + .ops = &cs35l35_pdm_ops, + }, +}; + +static int cs35l35_component_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, + int dir) +{ + struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component); + int clksrc; + int ret = 0; + + switch (clk_id) { + case 0: + clksrc = CS35L35_CLK_SOURCE_MCLK; + break; + case 1: + clksrc = CS35L35_CLK_SOURCE_SCLK; + break; + case 2: + clksrc = CS35L35_CLK_SOURCE_PDM; + break; + default: + dev_err(component->dev, "Invalid CLK Source\n"); + return -EINVAL; + } + + switch (freq) { + case 5644800: + case 6144000: + case 11289600: + case 12000000: + case 12288000: + case 13000000: + case 22579200: + case 24000000: + case 24576000: + case 26000000: + cs35l35->sysclk = freq; + break; + default: + dev_err(component->dev, "Invalid CLK Frequency Input : %d\n", freq); + return -EINVAL; + } + + ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, + CS35L35_CLK_SOURCE_MASK, + clksrc << CS35L35_CLK_SOURCE_SHIFT); + if (ret != 0) { + dev_err(component->dev, "Failed to set sysclk %d\n", ret); + return ret; + } + + return ret; +} + +static int cs35l35_boost_inductor(struct cs35l35_private *cs35l35, + int inductor) +{ + struct regmap *regmap = cs35l35->regmap; + unsigned int bst_ipk = 0; + + /* + * Digital Boost Converter Configuration for feedback, + * ramping, switching frequency, and estimation block seeding. + */ + + regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ, + CS35L35_BST_CONV_SWFREQ_MASK, 0x00); + + regmap_read(regmap, CS35L35_BST_PEAK_I, &bst_ipk); + bst_ipk &= CS35L35_BST_IPK_MASK; + + switch (inductor) { + case 1000: /* 1 uH */ + regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x24); + regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x24); + regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ, + CS35L35_BST_CONV_LBST_MASK, 0x00); + + if (bst_ipk < 0x04) + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B); + else + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x4E); + break; + case 1200: /* 1.2 uH */ + regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x20); + regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x20); + regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ, + CS35L35_BST_CONV_LBST_MASK, 0x01); + + if (bst_ipk < 0x04) + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B); + else + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x47); + break; + case 1500: /* 1.5uH */ + regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x20); + regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x20); + regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ, + CS35L35_BST_CONV_LBST_MASK, 0x02); + + if (bst_ipk < 0x04) + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B); + else + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x3C); + break; + case 2200: /* 2.2uH */ + regmap_write(regmap, CS35L35_BST_CONV_COEF_1, 0x19); + regmap_write(regmap, CS35L35_BST_CONV_COEF_2, 0x25); + regmap_update_bits(regmap, CS35L35_BST_CONV_SW_FREQ, + CS35L35_BST_CONV_LBST_MASK, 0x03); + + if (bst_ipk < 0x04) + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x1B); + else + regmap_write(regmap, CS35L35_BST_CONV_SLOPE_COMP, 0x23); + break; + default: + dev_err(cs35l35->dev, "Invalid Inductor Value %d uH\n", + inductor); + return -EINVAL; + } + return 0; +} + +static int cs35l35_component_probe(struct snd_soc_component *component) +{ + struct cs35l35_private *cs35l35 = snd_soc_component_get_drvdata(component); + struct classh_cfg *classh = &cs35l35->pdata.classh_algo; + struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg; + int ret; + + /* Set Platform Data */ + if (cs35l35->pdata.bst_vctl) + regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL, + CS35L35_BST_CTL_MASK, + cs35l35->pdata.bst_vctl); + + if (cs35l35->pdata.bst_ipk) + regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I, + CS35L35_BST_IPK_MASK, + cs35l35->pdata.bst_ipk << + CS35L35_BST_IPK_SHIFT); + + ret = cs35l35_boost_inductor(cs35l35, cs35l35->pdata.boost_ind); + if (ret) + return ret; + + if (cs35l35->pdata.gain_zc) + regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL, + CS35L35_AMP_GAIN_ZC_MASK, + cs35l35->pdata.gain_zc << + CS35L35_AMP_GAIN_ZC_SHIFT); + + if (cs35l35->pdata.aud_channel) + regmap_update_bits(cs35l35->regmap, + CS35L35_AUDIN_RXLOC_CTL, + CS35L35_AUD_IN_LR_MASK, + cs35l35->pdata.aud_channel << + CS35L35_AUD_IN_LR_SHIFT); + + if (cs35l35->pdata.stereo) { + regmap_update_bits(cs35l35->regmap, + CS35L35_ADVIN_RXLOC_CTL, + CS35L35_ADV_IN_LR_MASK, + cs35l35->pdata.adv_channel << + CS35L35_ADV_IN_LR_SHIFT); + if (cs35l35->pdata.shared_bst) + regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL, + CS35L35_CH_STEREO_MASK, + 1 << CS35L35_CH_STEREO_SHIFT); + ret = snd_soc_add_component_controls(component, cs35l35_adv_controls, + ARRAY_SIZE(cs35l35_adv_controls)); + if (ret) + return ret; + } + + if (cs35l35->pdata.sp_drv_str) + regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1, + CS35L35_SP_DRV_MASK, + cs35l35->pdata.sp_drv_str << + CS35L35_SP_DRV_SHIFT); + if (cs35l35->pdata.sp_drv_unused) + regmap_update_bits(cs35l35->regmap, CS35L35_SP_FMT_CTL3, + CS35L35_SP_I2S_DRV_MASK, + cs35l35->pdata.sp_drv_unused << + CS35L35_SP_I2S_DRV_SHIFT); + + if (classh->classh_algo_enable) { + if (classh->classh_bst_override) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_CTL, + CS35L35_CH_BST_OVR_MASK, + classh->classh_bst_override << + CS35L35_CH_BST_OVR_SHIFT); + if (classh->classh_bst_max_limit) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_CTL, + CS35L35_CH_BST_LIM_MASK, + classh->classh_bst_max_limit << + CS35L35_CH_BST_LIM_SHIFT); + if (classh->classh_mem_depth) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_CTL, + CS35L35_CH_MEM_DEPTH_MASK, + classh->classh_mem_depth << + CS35L35_CH_MEM_DEPTH_SHIFT); + if (classh->classh_headroom) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_HEADRM_CTL, + CS35L35_CH_HDRM_CTL_MASK, + classh->classh_headroom << + CS35L35_CH_HDRM_CTL_SHIFT); + if (classh->classh_release_rate) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_RELEASE_RATE, + CS35L35_CH_REL_RATE_MASK, + classh->classh_release_rate << + CS35L35_CH_REL_RATE_SHIFT); + if (classh->classh_wk_fet_disable) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_FET_DRIVE_CTL, + CS35L35_CH_WKFET_DIS_MASK, + classh->classh_wk_fet_disable << + CS35L35_CH_WKFET_DIS_SHIFT); + if (classh->classh_wk_fet_delay) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_FET_DRIVE_CTL, + CS35L35_CH_WKFET_DEL_MASK, + classh->classh_wk_fet_delay << + CS35L35_CH_WKFET_DEL_SHIFT); + if (classh->classh_wk_fet_thld) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_FET_DRIVE_CTL, + CS35L35_CH_WKFET_THLD_MASK, + classh->classh_wk_fet_thld << + CS35L35_CH_WKFET_THLD_SHIFT); + if (classh->classh_vpch_auto) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_VP_CTL, + CS35L35_CH_VP_AUTO_MASK, + classh->classh_vpch_auto << + CS35L35_CH_VP_AUTO_SHIFT); + if (classh->classh_vpch_rate) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_VP_CTL, + CS35L35_CH_VP_RATE_MASK, + classh->classh_vpch_rate << + CS35L35_CH_VP_RATE_SHIFT); + if (classh->classh_vpch_man) + regmap_update_bits(cs35l35->regmap, + CS35L35_CLASS_H_VP_CTL, + CS35L35_CH_VP_MAN_MASK, + classh->classh_vpch_man << + CS35L35_CH_VP_MAN_SHIFT); + } + + if (monitor_config->is_present) { + if (monitor_config->vmon_specs) { + regmap_update_bits(cs35l35->regmap, + CS35L35_SPKMON_DEPTH_CTL, + CS35L35_VMON_DEPTH_MASK, + monitor_config->vmon_dpth << + CS35L35_VMON_DEPTH_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_VMON_TXLOC_CTL, + CS35L35_MON_TXLOC_MASK, + monitor_config->vmon_loc << + CS35L35_MON_TXLOC_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_VMON_TXLOC_CTL, + CS35L35_MON_FRM_MASK, + monitor_config->vmon_frm << + CS35L35_MON_FRM_SHIFT); + } + if (monitor_config->imon_specs) { + regmap_update_bits(cs35l35->regmap, + CS35L35_SPKMON_DEPTH_CTL, + CS35L35_IMON_DEPTH_MASK, + monitor_config->imon_dpth << + CS35L35_IMON_DEPTH_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_IMON_TXLOC_CTL, + CS35L35_MON_TXLOC_MASK, + monitor_config->imon_loc << + CS35L35_MON_TXLOC_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_IMON_TXLOC_CTL, + CS35L35_MON_FRM_MASK, + monitor_config->imon_frm << + CS35L35_MON_FRM_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_IMON_SCALE_CTL, + CS35L35_IMON_SCALE_MASK, + monitor_config->imon_scale << + CS35L35_IMON_SCALE_SHIFT); + } + if (monitor_config->vpmon_specs) { + regmap_update_bits(cs35l35->regmap, + CS35L35_SUPMON_DEPTH_CTL, + CS35L35_VPMON_DEPTH_MASK, + monitor_config->vpmon_dpth << + CS35L35_VPMON_DEPTH_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_VPMON_TXLOC_CTL, + CS35L35_MON_TXLOC_MASK, + monitor_config->vpmon_loc << + CS35L35_MON_TXLOC_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_VPMON_TXLOC_CTL, + CS35L35_MON_FRM_MASK, + monitor_config->vpmon_frm << + CS35L35_MON_FRM_SHIFT); + } + if (monitor_config->vbstmon_specs) { + regmap_update_bits(cs35l35->regmap, + CS35L35_SUPMON_DEPTH_CTL, + CS35L35_VBSTMON_DEPTH_MASK, + monitor_config->vpmon_dpth << + CS35L35_VBSTMON_DEPTH_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_VBSTMON_TXLOC_CTL, + CS35L35_MON_TXLOC_MASK, + monitor_config->vbstmon_loc << + CS35L35_MON_TXLOC_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_VBSTMON_TXLOC_CTL, + CS35L35_MON_FRM_MASK, + monitor_config->vbstmon_frm << + CS35L35_MON_FRM_SHIFT); + } + if (monitor_config->vpbrstat_specs) { + regmap_update_bits(cs35l35->regmap, + CS35L35_SUPMON_DEPTH_CTL, + CS35L35_VPBRSTAT_DEPTH_MASK, + monitor_config->vpbrstat_dpth << + CS35L35_VPBRSTAT_DEPTH_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_VPBR_STATUS_TXLOC_CTL, + CS35L35_MON_TXLOC_MASK, + monitor_config->vpbrstat_loc << + CS35L35_MON_TXLOC_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_VPBR_STATUS_TXLOC_CTL, + CS35L35_MON_FRM_MASK, + monitor_config->vpbrstat_frm << + CS35L35_MON_FRM_SHIFT); + } + if (monitor_config->zerofill_specs) { + regmap_update_bits(cs35l35->regmap, + CS35L35_SUPMON_DEPTH_CTL, + CS35L35_ZEROFILL_DEPTH_MASK, + monitor_config->zerofill_dpth << + CS35L35_ZEROFILL_DEPTH_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_ZERO_FILL_LOC_CTL, + CS35L35_MON_TXLOC_MASK, + monitor_config->zerofill_loc << + CS35L35_MON_TXLOC_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_ZERO_FILL_LOC_CTL, + CS35L35_MON_FRM_MASK, + monitor_config->zerofill_frm << + CS35L35_MON_FRM_SHIFT); + } + } + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_cs35l35 = { + .probe = cs35l35_component_probe, + .set_sysclk = cs35l35_component_set_sysclk, + .dapm_widgets = cs35l35_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l35_dapm_widgets), + .dapm_routes = cs35l35_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l35_audio_map), + .controls = cs35l35_aud_controls, + .num_controls = ARRAY_SIZE(cs35l35_aud_controls), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct regmap_config cs35l35_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS35L35_MAX_REGISTER, + .reg_defaults = cs35l35_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l35_reg), + .volatile_reg = cs35l35_volatile_register, + .readable_reg = cs35l35_readable_register, + .precious_reg = cs35l35_precious_register, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static irqreturn_t cs35l35_irq(int irq, void *data) +{ + struct cs35l35_private *cs35l35 = data; + unsigned int sticky1, sticky2, sticky3, sticky4; + unsigned int mask1, mask2, mask3, mask4, current1; + + /* ack the irq by reading all status registers */ + regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_4, &sticky4); + regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_3, &sticky3); + regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_2, &sticky2); + regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &sticky1); + + regmap_read(cs35l35->regmap, CS35L35_INT_MASK_4, &mask4); + regmap_read(cs35l35->regmap, CS35L35_INT_MASK_3, &mask3); + regmap_read(cs35l35->regmap, CS35L35_INT_MASK_2, &mask2); + regmap_read(cs35l35->regmap, CS35L35_INT_MASK_1, &mask1); + + /* Check to see if unmasked bits are active */ + if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3) + && !(sticky4 & ~mask4)) + return IRQ_NONE; + + if (sticky2 & CS35L35_PDN_DONE) + complete(&cs35l35->pdn_done); + + /* read the current values */ + regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, ¤t1); + + /* handle the interrupts */ + if (sticky1 & CS35L35_CAL_ERR) { + dev_crit(cs35l35->dev, "Calibration Error\n"); + + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L35_CAL_ERR)) { + pr_debug("%s : Cal error release\n", __func__); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_CAL_ERR_RLS, 0); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_CAL_ERR_RLS, + CS35L35_CAL_ERR_RLS); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_CAL_ERR_RLS, 0); + } + } + + if (sticky1 & CS35L35_AMP_SHORT) { + dev_crit(cs35l35->dev, "AMP Short Error\n"); + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L35_AMP_SHORT)) { + dev_dbg(cs35l35->dev, "Amp short error release\n"); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_SHORT_RLS, 0); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_SHORT_RLS, + CS35L35_SHORT_RLS); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_SHORT_RLS, 0); + } + } + + if (sticky1 & CS35L35_OTW) { + dev_warn(cs35l35->dev, "Over temperature warning\n"); + + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L35_OTW)) { + dev_dbg(cs35l35->dev, "Over temperature warn release\n"); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_OTW_RLS, 0); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_OTW_RLS, + CS35L35_OTW_RLS); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_OTW_RLS, 0); + } + } + + if (sticky1 & CS35L35_OTE) { + dev_crit(cs35l35->dev, "Over temperature error\n"); + /* error is no longer asserted; safe to reset */ + if (!(current1 & CS35L35_OTE)) { + dev_dbg(cs35l35->dev, "Over temperature error release\n"); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_OTE_RLS, 0); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_OTE_RLS, + CS35L35_OTE_RLS); + regmap_update_bits(cs35l35->regmap, + CS35L35_PROT_RELEASE_CTL, + CS35L35_OTE_RLS, 0); + } + } + + if (sticky3 & CS35L35_BST_HIGH) { + dev_crit(cs35l35->dev, "VBST error: powering off!\n"); + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, + CS35L35_PDN_AMP, CS35L35_PDN_AMP); + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, + CS35L35_PDN_ALL, CS35L35_PDN_ALL); + } + + if (sticky3 & CS35L35_LBST_SHORT) { + dev_crit(cs35l35->dev, "LBST error: powering off!\n"); + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, + CS35L35_PDN_AMP, CS35L35_PDN_AMP); + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1, + CS35L35_PDN_ALL, CS35L35_PDN_ALL); + } + + if (sticky2 & CS35L35_VPBR_ERR) + dev_dbg(cs35l35->dev, "Error: Reactive Brownout\n"); + + if (sticky4 & CS35L35_VMON_OVFL) + dev_dbg(cs35l35->dev, "Error: VMON overflow\n"); + + if (sticky4 & CS35L35_IMON_OVFL) + dev_dbg(cs35l35->dev, "Error: IMON overflow\n"); + + return IRQ_HANDLED; +} + + +static int cs35l35_handle_of_data(struct i2c_client *i2c_client, + struct cs35l35_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + struct device_node *classh, *signal_format; + struct classh_cfg *classh_config = &pdata->classh_algo; + struct monitor_cfg *monitor_config = &pdata->mon_cfg; + unsigned int val32 = 0; + u8 monitor_array[4]; + const int imon_array_size = ARRAY_SIZE(monitor_array); + const int mon_array_size = imon_array_size - 1; + int ret = 0; + + if (!np) + return 0; + + pdata->bst_pdn_fet_on = of_property_read_bool(np, + "cirrus,boost-pdn-fet-on"); + + ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32); + if (ret >= 0) { + if (val32 < 2600 || val32 > 9000) { + dev_err(&i2c_client->dev, + "Invalid Boost Voltage %d mV\n", val32); + return -EINVAL; + } + pdata->bst_vctl = ((val32 - 2600) / 100) + 1; + } + + ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val32); + if (ret >= 0) { + if (val32 < 1680 || val32 > 4480) { + dev_err(&i2c_client->dev, + "Invalid Boost Peak Current %u mA\n", val32); + return -EINVAL; + } + + pdata->bst_ipk = ((val32 - 1680) / 110) | CS35L35_VALID_PDATA; + } + + ret = of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val32); + if (ret >= 0) { + pdata->boost_ind = val32; + } else { + dev_err(&i2c_client->dev, "Inductor not specified.\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0) + pdata->sp_drv_str = val32; + if (of_property_read_u32(np, "cirrus,sp-drv-unused", &val32) >= 0) + pdata->sp_drv_unused = val32 | CS35L35_VALID_PDATA; + + pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config"); + + if (pdata->stereo) { + ret = of_property_read_u32(np, "cirrus,audio-channel", &val32); + if (ret >= 0) + pdata->aud_channel = val32; + + ret = of_property_read_u32(np, "cirrus,advisory-channel", + &val32); + if (ret >= 0) + pdata->adv_channel = val32; + + pdata->shared_bst = of_property_read_bool(np, + "cirrus,shared-boost"); + } + + pdata->ext_bst = of_property_read_bool(np, "cirrus,external-boost"); + + pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc"); + + classh = of_get_child_by_name(np, "cirrus,classh-internal-algo"); + classh_config->classh_algo_enable = (classh != NULL); + + if (classh_config->classh_algo_enable) { + classh_config->classh_bst_override = + of_property_read_bool(np, "cirrus,classh-bst-overide"); + + ret = of_property_read_u32(classh, + "cirrus,classh-bst-max-limit", + &val32); + if (ret >= 0) { + val32 |= CS35L35_VALID_PDATA; + classh_config->classh_bst_max_limit = val32; + } + + ret = of_property_read_u32(classh, + "cirrus,classh-bst-max-limit", + &val32); + if (ret >= 0) { + val32 |= CS35L35_VALID_PDATA; + classh_config->classh_bst_max_limit = val32; + } + + ret = of_property_read_u32(classh, "cirrus,classh-mem-depth", + &val32); + if (ret >= 0) { + val32 |= CS35L35_VALID_PDATA; + classh_config->classh_mem_depth = val32; + } + + ret = of_property_read_u32(classh, "cirrus,classh-release-rate", + &val32); + if (ret >= 0) + classh_config->classh_release_rate = val32; + + ret = of_property_read_u32(classh, "cirrus,classh-headroom", + &val32); + if (ret >= 0) { + val32 |= CS35L35_VALID_PDATA; + classh_config->classh_headroom = val32; + } + + ret = of_property_read_u32(classh, + "cirrus,classh-wk-fet-disable", + &val32); + if (ret >= 0) + classh_config->classh_wk_fet_disable = val32; + + ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-delay", + &val32); + if (ret >= 0) { + val32 |= CS35L35_VALID_PDATA; + classh_config->classh_wk_fet_delay = val32; + } + + ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-thld", + &val32); + if (ret >= 0) + classh_config->classh_wk_fet_thld = val32; + + ret = of_property_read_u32(classh, "cirrus,classh-vpch-auto", + &val32); + if (ret >= 0) { + val32 |= CS35L35_VALID_PDATA; + classh_config->classh_vpch_auto = val32; + } + + ret = of_property_read_u32(classh, "cirrus,classh-vpch-rate", + &val32); + if (ret >= 0) { + val32 |= CS35L35_VALID_PDATA; + classh_config->classh_vpch_rate = val32; + } + + ret = of_property_read_u32(classh, "cirrus,classh-vpch-man", + &val32); + if (ret >= 0) + classh_config->classh_vpch_man = val32; + } + of_node_put(classh); + + /* frame depth location */ + signal_format = of_get_child_by_name(np, "cirrus,monitor-signal-format"); + monitor_config->is_present = signal_format ? true : false; + if (monitor_config->is_present) { + ret = of_property_read_u8_array(signal_format, "cirrus,imon", + monitor_array, imon_array_size); + if (!ret) { + monitor_config->imon_specs = true; + monitor_config->imon_dpth = monitor_array[0]; + monitor_config->imon_loc = monitor_array[1]; + monitor_config->imon_frm = monitor_array[2]; + monitor_config->imon_scale = monitor_array[3]; + } + ret = of_property_read_u8_array(signal_format, "cirrus,vmon", + monitor_array, mon_array_size); + if (!ret) { + monitor_config->vmon_specs = true; + monitor_config->vmon_dpth = monitor_array[0]; + monitor_config->vmon_loc = monitor_array[1]; + monitor_config->vmon_frm = monitor_array[2]; + } + ret = of_property_read_u8_array(signal_format, "cirrus,vpmon", + monitor_array, mon_array_size); + if (!ret) { + monitor_config->vpmon_specs = true; + monitor_config->vpmon_dpth = monitor_array[0]; + monitor_config->vpmon_loc = monitor_array[1]; + monitor_config->vpmon_frm = monitor_array[2]; + } + ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon", + monitor_array, mon_array_size); + if (!ret) { + monitor_config->vbstmon_specs = true; + monitor_config->vbstmon_dpth = monitor_array[0]; + monitor_config->vbstmon_loc = monitor_array[1]; + monitor_config->vbstmon_frm = monitor_array[2]; + } + ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat", + monitor_array, mon_array_size); + if (!ret) { + monitor_config->vpbrstat_specs = true; + monitor_config->vpbrstat_dpth = monitor_array[0]; + monitor_config->vpbrstat_loc = monitor_array[1]; + monitor_config->vpbrstat_frm = monitor_array[2]; + } + ret = of_property_read_u8_array(signal_format, "cirrus,zerofill", + monitor_array, mon_array_size); + if (!ret) { + monitor_config->zerofill_specs = true; + monitor_config->zerofill_dpth = monitor_array[0]; + monitor_config->zerofill_loc = monitor_array[1]; + monitor_config->zerofill_frm = monitor_array[2]; + } + } + of_node_put(signal_format); + + return 0; +} + +/* Errata Rev A0 */ +static const struct reg_sequence cs35l35_errata_patch[] = { + + { 0x7F, 0x99 }, + { 0x00, 0x99 }, + { 0x52, 0x22 }, + { 0x04, 0x14 }, + { 0x6D, 0x44 }, + { 0x24, 0x10 }, + { 0x58, 0xC4 }, + { 0x00, 0x98 }, + { 0x18, 0x08 }, + { 0x00, 0x00 }, + { 0x7F, 0x00 }, +}; + +static int cs35l35_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs35l35_private *cs35l35; + struct device *dev = &i2c_client->dev; + struct cs35l35_platform_data *pdata = dev_get_platdata(dev); + int i, devid; + int ret; + unsigned int reg; + + cs35l35 = devm_kzalloc(dev, sizeof(struct cs35l35_private), GFP_KERNEL); + if (!cs35l35) + return -ENOMEM; + + cs35l35->dev = dev; + + i2c_set_clientdata(i2c_client, cs35l35); + cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap); + if (IS_ERR(cs35l35->regmap)) { + ret = PTR_ERR(cs35l35->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++) + cs35l35->supplies[i].supply = cs35l35_supplies[i]; + + cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies); + + ret = devm_regulator_bulk_get(dev, cs35l35->num_supplies, + cs35l35->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request core supplies: %d\n", ret); + return ret; + } + + if (pdata) { + cs35l35->pdata = *pdata; + } else { + pdata = devm_kzalloc(dev, sizeof(struct cs35l35_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + if (i2c_client->dev.of_node) { + ret = cs35l35_handle_of_data(i2c_client, pdata); + if (ret != 0) + return ret; + + } + cs35l35->pdata = *pdata; + } + + ret = regulator_bulk_enable(cs35l35->num_supplies, + cs35l35->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", ret); + return ret; + } + + /* returning NULL can be valid if in stereo mode */ + cs35l35->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs35l35->reset_gpio)) { + ret = PTR_ERR(cs35l35->reset_gpio); + cs35l35->reset_gpio = NULL; + if (ret == -EBUSY) { + dev_info(dev, + "Reset line busy, assuming shared reset\n"); + } else { + dev_err(dev, "Failed to get reset GPIO: %d\n", ret); + goto err; + } + } + + cs35l35_reset(cs35l35); + + init_completion(&cs35l35->pdn_done); + + ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l35_irq, + IRQF_ONESHOT | IRQF_TRIGGER_LOW | + IRQF_SHARED, "cs35l35", cs35l35); + if (ret != 0) { + dev_err(dev, "Failed to request IRQ: %d\n", ret); + goto err; + } + /* initialize codec */ + devid = cirrus_read_device_id(cs35l35->regmap, CS35L35_DEVID_AB); + if (devid < 0) { + ret = devid; + dev_err(dev, "Failed to read device ID: %d\n", ret); + goto err; + } + + if (devid != CS35L35_CHIP_ID) { + dev_err(dev, "CS35L35 Device ID (%X). Expected ID %X\n", + devid, CS35L35_CHIP_ID); + ret = -ENODEV; + goto err; + } + + ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, ®); + if (ret < 0) { + dev_err(dev, "Get Revision ID failed: %d\n", ret); + goto err; + } + + ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch, + ARRAY_SIZE(cs35l35_errata_patch)); + if (ret < 0) { + dev_err(dev, "Failed to apply errata patch: %d\n", ret); + goto err; + } + + dev_info(dev, "Cirrus Logic CS35L35 (%x), Revision: %02X\n", + devid, reg & 0xFF); + + /* Set the INT Masks for critical errors */ + regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1, + CS35L35_INT1_CRIT_MASK); + regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2, + CS35L35_INT2_CRIT_MASK); + regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3, + CS35L35_INT3_CRIT_MASK); + regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4, + CS35L35_INT4_CRIT_MASK); + + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, + CS35L35_PWR2_PDN_MASK, + CS35L35_PWR2_PDN_MASK); + + if (cs35l35->pdata.bst_pdn_fet_on) + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, + CS35L35_PDN_BST_MASK, + 1 << CS35L35_PDN_BST_FETON_SHIFT); + else + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2, + CS35L35_PDN_BST_MASK, + 1 << CS35L35_PDN_BST_FETOFF_SHIFT); + + regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3, + CS35L35_PWR3_PDN_MASK, + CS35L35_PWR3_PDN_MASK); + + regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL, + CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT); + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_cs35l35, + cs35l35_dai, ARRAY_SIZE(cs35l35_dai)); + if (ret < 0) { + dev_err(dev, "Failed to register component: %d\n", ret); + goto err; + } + + return 0; + +err: + regulator_bulk_disable(cs35l35->num_supplies, + cs35l35->supplies); + gpiod_set_value_cansleep(cs35l35->reset_gpio, 0); + + return ret; +} + +static void cs35l35_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs35l35_private *cs35l35 = i2c_get_clientdata(i2c_client); + + regulator_bulk_disable(cs35l35->num_supplies, cs35l35->supplies); + gpiod_set_value_cansleep(cs35l35->reset_gpio, 0); +} + +static const struct of_device_id cs35l35_of_match[] = { + {.compatible = "cirrus,cs35l35"}, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l35_of_match); + +static const struct i2c_device_id cs35l35_id[] = { + {"cs35l35", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs35l35_id); + +static struct i2c_driver cs35l35_i2c_driver = { + .driver = { + .name = "cs35l35", + .of_match_table = cs35l35_of_match, + }, + .id_table = cs35l35_id, + .probe = cs35l35_i2c_probe, + .remove = cs35l35_i2c_remove, +}; + +module_i2c_driver(cs35l35_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L35 driver"); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h new file mode 100644 index 0000000000..5e4509f41b --- /dev/null +++ b/sound/soc/codecs/cs35l35.h @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * cs35l35.h -- CS35L35 ALSA SoC audio driver + * + * Copyright 2016 Cirrus Logic, Inc. + * + * Author: Brian Austin + */ + +#ifndef __CS35L35_H__ +#define __CS35L35_H__ + +#define CS35L35_FIRSTREG 0x01 +#define CS35L35_LASTREG 0x7E +#define CS35L35_CHIP_ID 0x00035A35 +#define CS35L35_DEVID_AB 0x01 /* Device ID A & B [RO] */ +#define CS35L35_DEVID_CD 0x02 /* Device ID C & D [RO] */ +#define CS35L35_DEVID_E 0x03 /* Device ID E [RO] */ +#define CS35L35_FAB_ID 0x04 /* Fab ID [RO] */ +#define CS35L35_REV_ID 0x05 /* Revision ID [RO] */ +#define CS35L35_PWRCTL1 0x06 /* Power Ctl 1 */ +#define CS35L35_PWRCTL2 0x07 /* Power Ctl 2 */ +#define CS35L35_PWRCTL3 0x08 /* Power Ctl 3 */ +#define CS35L35_CLK_CTL1 0x0A /* Clocking Ctl 1 */ +#define CS35L35_CLK_CTL2 0x0B /* Clocking Ctl 2 */ +#define CS35L35_CLK_CTL3 0x0C /* Clocking Ctl 3 */ +#define CS35L35_SP_FMT_CTL1 0x0D /* Serial Port Format CTL1 */ +#define CS35L35_SP_FMT_CTL2 0x0E /* Serial Port Format CTL2 */ +#define CS35L35_SP_FMT_CTL3 0x0F /* Serial Port Format CTL3 */ +#define CS35L35_MAG_COMP_CTL 0x13 /* Magnitude Comp CTL */ +#define CS35L35_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */ +#define CS35L35_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */ +#define CS35L35_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */ +#define CS35L35_ADV_DIG_VOL 0x17 /* Advisory Digital Volume */ +#define CS35L35_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */ +#define CS35L35_AMP_GAIN_AUD_CTL 0x19 /* Amp Serial Port Gain Ctl */ +#define CS35L35_AMP_GAIN_PDM_CTL 0x1A /* Amplifier Gain PDM Ctl */ +#define CS35L35_AMP_GAIN_ADV_CTL 0x1B /* Amplifier Gain Ctl */ +#define CS35L35_GPI_CTL 0x1C /* GPI Ctl */ +#define CS35L35_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */ +#define CS35L35_BST_PEAK_I 0x1E /* Boost Conv Peak Current */ +#define CS35L35_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */ +#define CS35L35_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */ +#define CS35L35_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */ +#define CS35L35_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */ +#define CS35L35_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */ +#define CS35L35_CLASS_H_CTL 0x30 /* CLS H Control */ +#define CS35L35_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */ +#define CS35L35_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */ +#define CS35L35_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */ +#define CS35L35_CLASS_H_VP_CTL 0x34 /* CLS H VP Ctl */ +#define CS35L35_CLASS_H_STATUS 0x38 /* CLS H Status */ +#define CS35L35_VPBR_CTL 0x3A /* VPBR Ctl */ +#define CS35L35_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */ +#define CS35L35_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */ +#define CS35L35_VPBR_MODE_VOL_CTL 0x3D /* VPBR Mode/Attack Vol Ctl */ +#define CS35L35_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */ +#define CS35L35_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */ +#define CS35L35_IMON_SCALE_CTL 0x51 /* IMON Scale Ctl */ +#define CS35L35_AUDIN_RXLOC_CTL 0x52 /* Audio Input RX Loc Ctl */ +#define CS35L35_ADVIN_RXLOC_CTL 0x53 /* Advisory Input RX Loc Ctl */ +#define CS35L35_VMON_TXLOC_CTL 0x54 /* VMON TX Loc Ctl */ +#define CS35L35_IMON_TXLOC_CTL 0x55 /* IMON TX Loc Ctl */ +#define CS35L35_VPMON_TXLOC_CTL 0x56 /* VPMON TX Loc Ctl */ +#define CS35L35_VBSTMON_TXLOC_CTL 0x57 /* VBSTMON TX Loc Ctl */ +#define CS35L35_VPBR_STATUS_TXLOC_CTL 0x58 /* VPBR Status TX Loc Ctl */ +#define CS35L35_ZERO_FILL_LOC_CTL 0x59 /* Zero Fill Loc Ctl */ +#define CS35L35_AUDIN_DEPTH_CTL 0x5A /* Audio Input Depth Ctl */ +#define CS35L35_SPKMON_DEPTH_CTL 0x5B /* SPK Mon Output Depth Ctl */ +#define CS35L35_SUPMON_DEPTH_CTL 0x5C /* Supply Mon Out Depth Ctl */ +#define CS35L35_ZEROFILL_DEPTH_CTL 0x5D /* Zero Fill Mon Output Ctl */ +#define CS35L35_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */ +#define CS35L35_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */ +#define CS35L35_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */ +#define CS35L35_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */ +#define CS35L35_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */ +#define CS35L35_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */ +#define CS35L35_INT_MASK_1 0x70 /* Interrupt Mask 1 */ +#define CS35L35_INT_MASK_2 0x71 /* Interrupt Mask 2 */ +#define CS35L35_INT_MASK_3 0x72 /* Interrupt Mask 3 */ +#define CS35L35_INT_MASK_4 0x73 /* Interrupt Mask 4 */ +#define CS35L35_INT_STATUS_1 0x74 /* Interrupt Status 1 */ +#define CS35L35_INT_STATUS_2 0x75 /* Interrupt Status 2 */ +#define CS35L35_INT_STATUS_3 0x76 /* Interrupt Status 3 */ +#define CS35L35_INT_STATUS_4 0x77 /* Interrupt Status 4 */ +#define CS35L35_PLL_STATUS 0x78 /* PLL Status */ +#define CS35L35_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */ + +#define CS35L35_MAX_REGISTER 0x7F + +/* CS35L35_PWRCTL1 */ +#define CS35L35_SFT_RST 0x80 +#define CS35L35_DISCHG_FLT 0x02 +#define CS35L35_PDN_ALL 0x01 + +/* CS35L35_PWRCTL2 */ +#define CS35L35_PDN_VMON 0x80 +#define CS35L35_PDN_IMON 0x40 +#define CS35L35_PDN_CLASSH 0x20 +#define CS35L35_PDN_VPBR 0x10 +#define CS35L35_PDN_BST 0x04 +#define CS35L35_PDN_AMP 0x01 + +/* CS35L35_PWRCTL3 */ +#define CS35L35_PDN_VBSTMON_OUT 0x10 +#define CS35L35_PDN_VMON_OUT 0x08 + +#define CS35L35_AUDIN_DEPTH_MASK 0x03 +#define CS35L35_AUDIN_DEPTH_SHIFT 0 +#define CS35L35_ADVIN_DEPTH_MASK 0x0C +#define CS35L35_ADVIN_DEPTH_SHIFT 2 +#define CS35L35_SDIN_DEPTH_8 0x01 +#define CS35L35_SDIN_DEPTH_16 0x02 +#define CS35L35_SDIN_DEPTH_24 0x03 + +#define CS35L35_SDOUT_DEPTH_8 0x01 +#define CS35L35_SDOUT_DEPTH_12 0x02 +#define CS35L35_SDOUT_DEPTH_16 0x03 + +#define CS35L35_AUD_IN_LR_MASK 0x80 +#define CS35L35_AUD_IN_LR_SHIFT 7 +#define CS35L35_ADV_IN_LR_MASK 0x80 +#define CS35L35_ADV_IN_LR_SHIFT 7 +#define CS35L35_AUD_IN_LOC_MASK 0x0F +#define CS35L35_AUD_IN_LOC_SHIFT 0 +#define CS35L35_ADV_IN_LOC_MASK 0x0F +#define CS35L35_ADV_IN_LOC_SHIFT 0 + +#define CS35L35_IMON_DEPTH_MASK 0x03 +#define CS35L35_IMON_DEPTH_SHIFT 0 +#define CS35L35_VMON_DEPTH_MASK 0x0C +#define CS35L35_VMON_DEPTH_SHIFT 2 +#define CS35L35_VBSTMON_DEPTH_MASK 0x03 +#define CS35L35_VBSTMON_DEPTH_SHIFT 0 +#define CS35L35_VPMON_DEPTH_MASK 0x0C +#define CS35L35_VPMON_DEPTH_SHIFT 2 +#define CS35L35_VPBRSTAT_DEPTH_MASK 0x30 +#define CS35L35_VPBRSTAT_DEPTH_SHIFT 4 +#define CS35L35_ZEROFILL_DEPTH_MASK 0x03 +#define CS35L35_ZEROFILL_DEPTH_SHIFT 0x00 + +#define CS35L35_MON_TXLOC_MASK 0x3F +#define CS35L35_MON_TXLOC_SHIFT 0 +#define CS35L35_MON_FRM_MASK 0x80 +#define CS35L35_MON_FRM_SHIFT 7 + +#define CS35L35_IMON_SCALE_MASK 0xF8 +#define CS35L35_IMON_SCALE_SHIFT 3 + +#define CS35L35_MS_MASK 0x80 +#define CS35L35_MS_SHIFT 7 +#define CS35L35_SPMODE_MASK 0x40 +#define CS35L35_SP_DRV_MASK 0x10 +#define CS35L35_SP_DRV_SHIFT 4 +#define CS35L35_CLK_CTL2_MASK 0xFF +#define CS35L35_PDM_MODE_MASK 0x40 +#define CS35L35_PDM_MODE_SHIFT 6 +#define CS35L35_CLK_SOURCE_MASK 0x03 +#define CS35L35_CLK_SOURCE_SHIFT 0 +#define CS35L35_CLK_SOURCE_MCLK 0 +#define CS35L35_CLK_SOURCE_SCLK 1 +#define CS35L35_CLK_SOURCE_PDM 2 + +#define CS35L35_SP_SCLKS_MASK 0x0F +#define CS35L35_SP_SCLKS_SHIFT 0x00 +#define CS35L35_SP_SCLKS_16FS 0x03 +#define CS35L35_SP_SCLKS_32FS 0x07 +#define CS35L35_SP_SCLKS_48FS 0x0B +#define CS35L35_SP_SCLKS_64FS 0x0F +#define CS35L35_SP_RATE_MASK 0xC0 +#define CS35L35_SP_RATE_SHIFT 6 + +#define CS35L35_PDN_BST_MASK 0x06 +#define CS35L35_PDN_BST_FETON_SHIFT 1 +#define CS35L35_PDN_BST_FETOFF_SHIFT 2 +#define CS35L35_PWR2_PDN_MASK 0xE0 +#define CS35L35_PWR3_PDN_MASK 0x1E +#define CS35L35_PDN_ALL_MASK 0x01 +#define CS35L35_DISCHG_FILT_MASK 0x02 +#define CS35L35_DISCHG_FILT_SHIFT 1 +#define CS35L35_MCLK_DIS_MASK 0x04 +#define CS35L35_MCLK_DIS_SHIFT 2 + +#define CS35L35_BST_CTL_MASK 0x7F +#define CS35L35_BST_CTL_SHIFT 0 +#define CS35L35_BST_IPK_MASK 0x1F +#define CS35L35_BST_IPK_SHIFT 0 +#define CS35L35_AMP_MUTE_MASK 0x20 +#define CS35L35_AMP_MUTE_SHIFT 5 +#define CS35L35_AMP_GAIN_ZC_MASK 0x10 +#define CS35L35_AMP_GAIN_ZC_SHIFT 4 + +#define CS35L35_AMP_DIGSFT_MASK 0x02 +#define CS35L35_AMP_DIGSFT_SHIFT 1 + +/* CS35L35_SP_FMT_CTL3 */ +#define CS35L35_SP_I2S_DRV_MASK 0x03 +#define CS35L35_SP_I2S_DRV_SHIFT 0 + +/* Boost Converter Config */ +#define CS35L35_BST_CONV_COEFF_MASK 0xFF +#define CS35L35_BST_CONV_SLOPE_MASK 0xFF +#define CS35L35_BST_CONV_LBST_MASK 0x03 +#define CS35L35_BST_CONV_SWFREQ_MASK 0xF0 + +/* Class H Algorithm Control */ +#define CS35L35_CH_STEREO_MASK 0x40 +#define CS35L35_CH_STEREO_SHIFT 6 +#define CS35L35_CH_BST_OVR_MASK 0x04 +#define CS35L35_CH_BST_OVR_SHIFT 2 +#define CS35L35_CH_BST_LIM_MASK 0x08 +#define CS35L35_CH_BST_LIM_SHIFT 3 +#define CS35L35_CH_MEM_DEPTH_MASK 0x01 +#define CS35L35_CH_MEM_DEPTH_SHIFT 0 +#define CS35L35_CH_HDRM_CTL_MASK 0x3F +#define CS35L35_CH_HDRM_CTL_SHIFT 0 +#define CS35L35_CH_REL_RATE_MASK 0xFF +#define CS35L35_CH_REL_RATE_SHIFT 0 +#define CS35L35_CH_WKFET_DIS_MASK 0x80 +#define CS35L35_CH_WKFET_DIS_SHIFT 7 +#define CS35L35_CH_WKFET_DEL_MASK 0x70 +#define CS35L35_CH_WKFET_DEL_SHIFT 4 +#define CS35L35_CH_WKFET_THLD_MASK 0x0F +#define CS35L35_CH_WKFET_THLD_SHIFT 0 +#define CS35L35_CH_VP_AUTO_MASK 0x80 +#define CS35L35_CH_VP_AUTO_SHIFT 7 +#define CS35L35_CH_VP_RATE_MASK 0x60 +#define CS35L35_CH_VP_RATE_SHIFT 5 +#define CS35L35_CH_VP_MAN_MASK 0x1F +#define CS35L35_CH_VP_MAN_SHIFT 0 + +/* CS35L35_PROT_RELEASE_CTL */ +#define CS35L35_CAL_ERR_RLS 0x80 +#define CS35L35_SHORT_RLS 0x04 +#define CS35L35_OTW_RLS 0x02 +#define CS35L35_OTE_RLS 0x01 + +/* INT Mask Registers */ +#define CS35L35_INT1_CRIT_MASK 0x38 +#define CS35L35_INT2_CRIT_MASK 0xEF +#define CS35L35_INT3_CRIT_MASK 0xEE +#define CS35L35_INT4_CRIT_MASK 0xFF + +/* PDN DONE Masks */ +#define CS35L35_M_PDN_DONE_SHIFT 4 +#define CS35L35_M_PDN_DONE_MASK 0x10 + +/* CS35L35_INT_1 */ +#define CS35L35_CAL_ERR 0x80 +#define CS35L35_OTP_ERR 0x40 +#define CS35L35_LRCLK_ERR 0x20 +#define CS35L35_SPCLK_ERR 0x10 +#define CS35L35_MCLK_ERR 0x08 +#define CS35L35_AMP_SHORT 0x04 +#define CS35L35_OTW 0x02 +#define CS35L35_OTE 0x01 + +/* CS35L35_INT_2 */ +#define CS35L35_PDN_DONE 0x10 +#define CS35L35_VPBR_ERR 0x02 +#define CS35L35_VPBR_CLR 0x01 + +/* CS35L35_INT_3 */ +#define CS35L35_BST_HIGH 0x10 +#define CS35L35_BST_HIGH_FLAG 0x08 +#define CS35L35_BST_IPK_FLAG 0x04 +#define CS35L35_LBST_SHORT 0x01 + +/* CS35L35_INT_4 */ +#define CS35L35_VMON_OVFL 0x08 +#define CS35L35_IMON_OVFL 0x04 + +#define CS35L35_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct cs35l35_private { + struct device *dev; + struct cs35l35_platform_data pdata; + struct regmap *regmap; + struct regulator_bulk_data supplies[2]; + int num_supplies; + int sysclk; + int sclk; + bool pdm_mode; + bool i2s_mode; + bool clock_consumer; + /* GPIO for /RST */ + struct gpio_desc *reset_gpio; + struct completion pdn_done; +}; + +static const char * const cs35l35_supplies[] = { + "VA", + "VP", +}; + +#endif diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c new file mode 100644 index 0000000000..20084c7d3a --- /dev/null +++ b/sound/soc/codecs/cs35l36.c @@ -0,0 +1,1954 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l36.c -- CS35L36 ALSA SoC audio driver +// +// Copyright 2018 Cirrus Logic, Inc. +// +// Author: James Schulman + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l36.h" + +/* + * Some fields take zero as a valid value so use a high bit flag that won't + * get written to the device to mark those. + */ +#define CS35L36_VALID_PDATA 0x80000000 + +static const char * const cs35l36_supplies[] = { + "VA", + "VP", +}; + +struct cs35l36_private { + struct device *dev; + struct cs35l36_platform_data pdata; + struct regmap *regmap; + struct regulator_bulk_data supplies[2]; + int num_supplies; + int clksrc; + int chip_version; + int rev_id; + int ldm_mode_sel; + struct gpio_desc *reset_gpio; +}; + +struct cs35l36_pll_config { + int freq; + int clk_cfg; + int fll_igain; +}; + +static const struct cs35l36_pll_config cs35l36_pll_sysclk[] = { + {32768, 0x00, 0x05}, + {8000, 0x01, 0x03}, + {11025, 0x02, 0x03}, + {12000, 0x03, 0x03}, + {16000, 0x04, 0x04}, + {22050, 0x05, 0x04}, + {24000, 0x06, 0x04}, + {32000, 0x07, 0x05}, + {44100, 0x08, 0x05}, + {48000, 0x09, 0x05}, + {88200, 0x0A, 0x06}, + {96000, 0x0B, 0x06}, + {128000, 0x0C, 0x07}, + {176400, 0x0D, 0x07}, + {192000, 0x0E, 0x07}, + {256000, 0x0F, 0x08}, + {352800, 0x10, 0x08}, + {384000, 0x11, 0x08}, + {512000, 0x12, 0x09}, + {705600, 0x13, 0x09}, + {750000, 0x14, 0x09}, + {768000, 0x15, 0x09}, + {1000000, 0x16, 0x0A}, + {1024000, 0x17, 0x0A}, + {1200000, 0x18, 0x0A}, + {1411200, 0x19, 0x0A}, + {1500000, 0x1A, 0x0A}, + {1536000, 0x1B, 0x0A}, + {2000000, 0x1C, 0x0A}, + {2048000, 0x1D, 0x0A}, + {2400000, 0x1E, 0x0A}, + {2822400, 0x1F, 0x0A}, + {3000000, 0x20, 0x0A}, + {3072000, 0x21, 0x0A}, + {3200000, 0x22, 0x0A}, + {4000000, 0x23, 0x0A}, + {4096000, 0x24, 0x0A}, + {4800000, 0x25, 0x0A}, + {5644800, 0x26, 0x0A}, + {6000000, 0x27, 0x0A}, + {6144000, 0x28, 0x0A}, + {6250000, 0x29, 0x08}, + {6400000, 0x2A, 0x0A}, + {6500000, 0x2B, 0x08}, + {6750000, 0x2C, 0x09}, + {7526400, 0x2D, 0x0A}, + {8000000, 0x2E, 0x0A}, + {8192000, 0x2F, 0x0A}, + {9600000, 0x30, 0x0A}, + {11289600, 0x31, 0x0A}, + {12000000, 0x32, 0x0A}, + {12288000, 0x33, 0x0A}, + {12500000, 0x34, 0x08}, + {12800000, 0x35, 0x0A}, + {13000000, 0x36, 0x0A}, + {13500000, 0x37, 0x0A}, + {19200000, 0x38, 0x0A}, + {22579200, 0x39, 0x0A}, + {24000000, 0x3A, 0x0A}, + {24576000, 0x3B, 0x0A}, + {25000000, 0x3C, 0x0A}, + {25600000, 0x3D, 0x0A}, + {26000000, 0x3E, 0x0A}, + {27000000, 0x3F, 0x0A}, +}; + +static struct reg_default cs35l36_reg[] = { + {CS35L36_TESTKEY_CTRL, 0x00000000}, + {CS35L36_USERKEY_CTL, 0x00000000}, + {CS35L36_OTP_CTRL1, 0x00002460}, + {CS35L36_OTP_CTRL2, 0x00000000}, + {CS35L36_OTP_CTRL3, 0x00000000}, + {CS35L36_OTP_CTRL4, 0x00000000}, + {CS35L36_OTP_CTRL5, 0x00000000}, + {CS35L36_PAC_CTL1, 0x00000004}, + {CS35L36_PAC_CTL2, 0x00000000}, + {CS35L36_PAC_CTL3, 0x00000000}, + {CS35L36_PWR_CTRL1, 0x00000000}, + {CS35L36_PWR_CTRL2, 0x00003321}, + {CS35L36_PWR_CTRL3, 0x01000010}, + {CS35L36_CTRL_OVRRIDE, 0x00000002}, + {CS35L36_AMP_OUT_MUTE, 0x00000000}, + {CS35L36_OTP_TRIM_STATUS, 0x00000000}, + {CS35L36_DISCH_FILT, 0x00000000}, + {CS35L36_PROTECT_REL_ERR, 0x00000000}, + {CS35L36_PAD_INTERFACE, 0x00000038}, + {CS35L36_PLL_CLK_CTRL, 0x00000010}, + {CS35L36_GLOBAL_CLK_CTRL, 0x00000003}, + {CS35L36_ADC_CLK_CTRL, 0x00000000}, + {CS35L36_SWIRE_CLK_CTRL, 0x00000000}, + {CS35L36_SP_SCLK_CLK_CTRL, 0x00000000}, + {CS35L36_MDSYNC_EN, 0x00000000}, + {CS35L36_MDSYNC_TX_ID, 0x00000000}, + {CS35L36_MDSYNC_PWR_CTRL, 0x00000000}, + {CS35L36_MDSYNC_DATA_TX, 0x00000000}, + {CS35L36_MDSYNC_TX_STATUS, 0x00000002}, + {CS35L36_MDSYNC_RX_STATUS, 0x00000000}, + {CS35L36_MDSYNC_ERR_STATUS, 0x00000000}, + {CS35L36_BSTCVRT_VCTRL1, 0x00000000}, + {CS35L36_BSTCVRT_VCTRL2, 0x00000001}, + {CS35L36_BSTCVRT_PEAK_CUR, 0x0000004A}, + {CS35L36_BSTCVRT_SFT_RAMP, 0x00000003}, + {CS35L36_BSTCVRT_COEFF, 0x00002424}, + {CS35L36_BSTCVRT_SLOPE_LBST, 0x00005800}, + {CS35L36_BSTCVRT_SW_FREQ, 0x00010000}, + {CS35L36_BSTCVRT_DCM_CTRL, 0x00002001}, + {CS35L36_BSTCVRT_DCM_MODE_FORCE, 0x00000000}, + {CS35L36_BSTCVRT_OVERVOLT_CTRL, 0x00000130}, + {CS35L36_VPI_LIMIT_MODE, 0x00000000}, + {CS35L36_VPI_LIMIT_MINMAX, 0x00003000}, + {CS35L36_VPI_VP_THLD, 0x00101010}, + {CS35L36_VPI_TRACK_CTRL, 0x00000000}, + {CS35L36_VPI_TRIG_MODE_CTRL, 0x00000000}, + {CS35L36_VPI_TRIG_STEPS, 0x00000000}, + {CS35L36_VI_SPKMON_FILT, 0x00000003}, + {CS35L36_VI_SPKMON_GAIN, 0x00000909}, + {CS35L36_VI_SPKMON_IP_SEL, 0x00000000}, + {CS35L36_DTEMP_WARN_THLD, 0x00000002}, + {CS35L36_DTEMP_STATUS, 0x00000000}, + {CS35L36_VPVBST_FS_SEL, 0x00000001}, + {CS35L36_VPVBST_VP_CTRL, 0x000001C0}, + {CS35L36_VPVBST_VBST_CTRL, 0x000001C0}, + {CS35L36_ASP_TX_PIN_CTRL, 0x00000028}, + {CS35L36_ASP_RATE_CTRL, 0x00090000}, + {CS35L36_ASP_FORMAT, 0x00000002}, + {CS35L36_ASP_FRAME_CTRL, 0x00180018}, + {CS35L36_ASP_TX1_TX2_SLOT, 0x00010000}, + {CS35L36_ASP_TX3_TX4_SLOT, 0x00030002}, + {CS35L36_ASP_TX5_TX6_SLOT, 0x00050004}, + {CS35L36_ASP_TX7_TX8_SLOT, 0x00070006}, + {CS35L36_ASP_RX1_SLOT, 0x00000000}, + {CS35L36_ASP_RX_TX_EN, 0x00000000}, + {CS35L36_ASP_RX1_SEL, 0x00000008}, + {CS35L36_ASP_TX1_SEL, 0x00000018}, + {CS35L36_ASP_TX2_SEL, 0x00000019}, + {CS35L36_ASP_TX3_SEL, 0x00000028}, + {CS35L36_ASP_TX4_SEL, 0x00000029}, + {CS35L36_ASP_TX5_SEL, 0x00000020}, + {CS35L36_ASP_TX6_SEL, 0x00000000}, + {CS35L36_SWIRE_P1_TX1_SEL, 0x00000018}, + {CS35L36_SWIRE_P1_TX2_SEL, 0x00000019}, + {CS35L36_SWIRE_P2_TX1_SEL, 0x00000028}, + {CS35L36_SWIRE_P2_TX2_SEL, 0x00000029}, + {CS35L36_SWIRE_P2_TX3_SEL, 0x00000020}, + {CS35L36_SWIRE_DP1_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_DP2_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_DP3_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_PCM_RX_DATA, 0x00000000}, + {CS35L36_SWIRE_FS_SEL, 0x00000001}, + {CS35L36_AMP_DIG_VOL_CTRL, 0x00008000}, + {CS35L36_VPBR_CFG, 0x02AA1905}, + {CS35L36_VBBR_CFG, 0x02AA1905}, + {CS35L36_VPBR_STATUS, 0x00000000}, + {CS35L36_VBBR_STATUS, 0x00000000}, + {CS35L36_OVERTEMP_CFG, 0x00000001}, + {CS35L36_AMP_ERR_VOL, 0x00000000}, + {CS35L36_CLASSH_CFG, 0x000B0405}, + {CS35L36_CLASSH_FET_DRV_CFG, 0x00000111}, + {CS35L36_NG_CFG, 0x00000033}, + {CS35L36_AMP_GAIN_CTRL, 0x00000273}, + {CS35L36_PWM_MOD_IO_CTRL, 0x00000000}, + {CS35L36_PWM_MOD_STATUS, 0x00000000}, + {CS35L36_DAC_MSM_CFG, 0x00000000}, + {CS35L36_AMP_SLOPE_CTRL, 0x00000B00}, + {CS35L36_AMP_PDM_VOLUME, 0x00000000}, + {CS35L36_AMP_PDM_RATE_CTRL, 0x00000000}, + {CS35L36_PDM_CH_SEL, 0x00000000}, + {CS35L36_AMP_NG_CTRL, 0x0000212F}, + {CS35L36_PDM_HIGHFILT_CTRL, 0x00000000}, + {CS35L36_PAC_INT0_CTRL, 0x00000001}, + {CS35L36_PAC_INT1_CTRL, 0x00000001}, + {CS35L36_PAC_INT2_CTRL, 0x00000001}, + {CS35L36_PAC_INT3_CTRL, 0x00000001}, + {CS35L36_PAC_INT4_CTRL, 0x00000001}, + {CS35L36_PAC_INT5_CTRL, 0x00000001}, + {CS35L36_PAC_INT6_CTRL, 0x00000001}, + {CS35L36_PAC_INT7_CTRL, 0x00000001}, +}; + +static bool cs35l36_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_SW_RESET: + case CS35L36_SW_REV: + case CS35L36_HW_REV: + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_OTP_MEM30: + case CS35L36_OTP_CTRL1: + case CS35L36_OTP_CTRL2: + case CS35L36_OTP_CTRL3: + case CS35L36_OTP_CTRL4: + case CS35L36_OTP_CTRL5: + case CS35L36_PAC_CTL1: + case CS35L36_PAC_CTL2: + case CS35L36_PAC_CTL3: + case CS35L36_DEVICE_ID: + case CS35L36_FAB_ID: + case CS35L36_REV_ID: + case CS35L36_PWR_CTRL1: + case CS35L36_PWR_CTRL2: + case CS35L36_PWR_CTRL3: + case CS35L36_CTRL_OVRRIDE: + case CS35L36_AMP_OUT_MUTE: + case CS35L36_OTP_TRIM_STATUS: + case CS35L36_DISCH_FILT: + case CS35L36_PROTECT_REL_ERR: + case CS35L36_PAD_INTERFACE: + case CS35L36_PLL_CLK_CTRL: + case CS35L36_GLOBAL_CLK_CTRL: + case CS35L36_ADC_CLK_CTRL: + case CS35L36_SWIRE_CLK_CTRL: + case CS35L36_SP_SCLK_CLK_CTRL: + case CS35L36_TST_FS_MON0: + case CS35L36_MDSYNC_EN: + case CS35L36_MDSYNC_TX_ID: + case CS35L36_MDSYNC_PWR_CTRL: + case CS35L36_MDSYNC_DATA_TX: + case CS35L36_MDSYNC_TX_STATUS: + case CS35L36_MDSYNC_RX_STATUS: + case CS35L36_MDSYNC_ERR_STATUS: + case CS35L36_BSTCVRT_VCTRL1: + case CS35L36_BSTCVRT_VCTRL2: + case CS35L36_BSTCVRT_PEAK_CUR: + case CS35L36_BSTCVRT_SFT_RAMP: + case CS35L36_BSTCVRT_COEFF: + case CS35L36_BSTCVRT_SLOPE_LBST: + case CS35L36_BSTCVRT_SW_FREQ: + case CS35L36_BSTCVRT_DCM_CTRL: + case CS35L36_BSTCVRT_DCM_MODE_FORCE: + case CS35L36_BSTCVRT_OVERVOLT_CTRL: + case CS35L36_BST_TST_MANUAL: + case CS35L36_BST_ANA2_TEST: + case CS35L36_VPI_LIMIT_MODE: + case CS35L36_VPI_LIMIT_MINMAX: + case CS35L36_VPI_VP_THLD: + case CS35L36_VPI_TRACK_CTRL: + case CS35L36_VPI_TRIG_MODE_CTRL: + case CS35L36_VPI_TRIG_STEPS: + case CS35L36_VI_SPKMON_FILT: + case CS35L36_VI_SPKMON_GAIN: + case CS35L36_VI_SPKMON_IP_SEL: + case CS35L36_DTEMP_WARN_THLD: + case CS35L36_DTEMP_STATUS: + case CS35L36_VPVBST_FS_SEL: + case CS35L36_VPVBST_VP_CTRL: + case CS35L36_VPVBST_VBST_CTRL: + case CS35L36_ASP_TX_PIN_CTRL: + case CS35L36_ASP_RATE_CTRL: + case CS35L36_ASP_FORMAT: + case CS35L36_ASP_FRAME_CTRL: + case CS35L36_ASP_TX1_TX2_SLOT: + case CS35L36_ASP_TX3_TX4_SLOT: + case CS35L36_ASP_TX5_TX6_SLOT: + case CS35L36_ASP_TX7_TX8_SLOT: + case CS35L36_ASP_RX1_SLOT: + case CS35L36_ASP_RX_TX_EN: + case CS35L36_ASP_RX1_SEL: + case CS35L36_ASP_TX1_SEL: + case CS35L36_ASP_TX2_SEL: + case CS35L36_ASP_TX3_SEL: + case CS35L36_ASP_TX4_SEL: + case CS35L36_ASP_TX5_SEL: + case CS35L36_ASP_TX6_SEL: + case CS35L36_SWIRE_P1_TX1_SEL: + case CS35L36_SWIRE_P1_TX2_SEL: + case CS35L36_SWIRE_P2_TX1_SEL: + case CS35L36_SWIRE_P2_TX2_SEL: + case CS35L36_SWIRE_P2_TX3_SEL: + case CS35L36_SWIRE_DP1_FIFO_CFG: + case CS35L36_SWIRE_DP2_FIFO_CFG: + case CS35L36_SWIRE_DP3_FIFO_CFG: + case CS35L36_SWIRE_PCM_RX_DATA: + case CS35L36_SWIRE_FS_SEL: + case CS35L36_AMP_DIG_VOL_CTRL: + case CS35L36_VPBR_CFG: + case CS35L36_VBBR_CFG: + case CS35L36_VPBR_STATUS: + case CS35L36_VBBR_STATUS: + case CS35L36_OVERTEMP_CFG: + case CS35L36_AMP_ERR_VOL: + case CS35L36_CLASSH_CFG: + case CS35L36_CLASSH_FET_DRV_CFG: + case CS35L36_NG_CFG: + case CS35L36_AMP_GAIN_CTRL: + case CS35L36_PWM_MOD_IO_CTRL: + case CS35L36_PWM_MOD_STATUS: + case CS35L36_DAC_MSM_CFG: + case CS35L36_AMP_SLOPE_CTRL: + case CS35L36_AMP_PDM_VOLUME: + case CS35L36_AMP_PDM_RATE_CTRL: + case CS35L36_PDM_CH_SEL: + case CS35L36_AMP_NG_CTRL: + case CS35L36_PDM_HIGHFILT_CTRL: + case CS35L36_INT1_STATUS: + case CS35L36_INT2_STATUS: + case CS35L36_INT3_STATUS: + case CS35L36_INT4_STATUS: + case CS35L36_INT1_RAW_STATUS: + case CS35L36_INT2_RAW_STATUS: + case CS35L36_INT3_RAW_STATUS: + case CS35L36_INT4_RAW_STATUS: + case CS35L36_INT1_MASK: + case CS35L36_INT2_MASK: + case CS35L36_INT3_MASK: + case CS35L36_INT4_MASK: + case CS35L36_INT1_EDGE_LVL_CTRL: + case CS35L36_INT3_EDGE_LVL_CTRL: + case CS35L36_PAC_INT_STATUS: + case CS35L36_PAC_INT_RAW_STATUS: + case CS35L36_PAC_INT_FLUSH_CTRL: + case CS35L36_PAC_INT0_CTRL: + case CS35L36_PAC_INT1_CTRL: + case CS35L36_PAC_INT2_CTRL: + case CS35L36_PAC_INT3_CTRL: + case CS35L36_PAC_INT4_CTRL: + case CS35L36_PAC_INT5_CTRL: + case CS35L36_PAC_INT6_CTRL: + case CS35L36_PAC_INT7_CTRL: + return true; + default: + if (reg >= CS35L36_PAC_PMEM_WORD0 && + reg <= CS35L36_PAC_PMEM_WORD1023) + return true; + else + return false; + } +} + +static bool cs35l36_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_TST_FS_MON0: + return true; + default: + return false; + } +} + +static bool cs35l36_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_SW_RESET: + case CS35L36_SW_REV: + case CS35L36_HW_REV: + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_DEVICE_ID: + case CS35L36_FAB_ID: + case CS35L36_REV_ID: + case CS35L36_INT1_STATUS: + case CS35L36_INT2_STATUS: + case CS35L36_INT3_STATUS: + case CS35L36_INT4_STATUS: + case CS35L36_INT1_RAW_STATUS: + case CS35L36_INT2_RAW_STATUS: + case CS35L36_INT3_RAW_STATUS: + case CS35L36_INT4_RAW_STATUS: + case CS35L36_INT1_MASK: + case CS35L36_INT2_MASK: + case CS35L36_INT3_MASK: + case CS35L36_INT4_MASK: + case CS35L36_INT1_EDGE_LVL_CTRL: + case CS35L36_INT3_EDGE_LVL_CTRL: + case CS35L36_PAC_INT_STATUS: + case CS35L36_PAC_INT_RAW_STATUS: + case CS35L36_PAC_INT_FLUSH_CTRL: + return true; + default: + if (reg >= CS35L36_PAC_PMEM_WORD0 && + reg <= CS35L36_PAC_PMEM_WORD1023) + return true; + else + return false; + } +} + +static const DECLARE_TLV_DB_RANGE(dig_vol_tlv, 0, 912, + TLV_DB_MINMAX_ITEM(-10200, 1200)); +static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1); + +static const char * const cs35l36_pcm_sftramp_text[] = { + "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"}; + +static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L36_AMP_DIG_VOL_CTRL, 0, + cs35l36_pcm_sftramp_text); + +static int cs35l36_ldm_sel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = cs35l36->ldm_mode_sel; + + return 0; +} + +static int cs35l36_ldm_sel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int val = (ucontrol->value.integer.value[0]) ? CS35L36_NG_AMP_EN_MASK : + 0; + + cs35l36->ldm_mode_sel = val; + + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_AMP_EN_MASK, val); + + return 0; +} + +static const struct snd_kcontrol_new cs35l36_aud_controls[] = { + SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L36_AMP_DIG_VOL_CTRL, + 3, 0x4D0, 0x390, dig_vol_tlv), + SOC_SINGLE_TLV("Analog PCM Volume", CS35L36_AMP_GAIN_CTRL, 5, 0x13, 0, + amp_gain_tlv), + SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp), + SOC_SINGLE("Amp Gain Zero-Cross Switch", CS35L36_AMP_GAIN_CTRL, + CS35L36_AMP_ZC_SHIFT, 1, 0), + SOC_SINGLE("PDM LDM Enter Ramp Switch", CS35L36_DAC_MSM_CFG, + CS35L36_PDM_LDM_ENTER_SHIFT, 1, 0), + SOC_SINGLE("PDM LDM Exit Ramp Switch", CS35L36_DAC_MSM_CFG, + CS35L36_PDM_LDM_EXIT_SHIFT, 1, 0), + SOC_SINGLE_BOOL_EXT("LDM Select Switch", 0, cs35l36_ldm_sel_get, + cs35l36_ldm_sel_put), +}; + +static int cs35l36_main_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + u32 reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1, + CS35L36_GLOBAL_EN_MASK, + 1 << CS35L36_GLOBAL_EN_SHIFT); + + usleep_range(2000, 2100); + + regmap_read(cs35l36->regmap, CS35L36_INT4_RAW_STATUS, ®); + + if (WARN_ON_ONCE(reg & CS35L36_PLL_UNLOCK_MASK)) + dev_crit(cs35l36->dev, "PLL Unlocked\n"); + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL, + CS35L36_PCM_RX_SEL_MASK, + CS35L36_PCM_RX_SEL_PCM); + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_MASK, + 0 << CS35L36_AMP_MUTE_SHIFT); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL, + CS35L36_PCM_RX_SEL_MASK, + CS35L36_PCM_RX_SEL_ZERO); + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_MASK, + 1 << CS35L36_AMP_MUTE_SHIFT); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1, + CS35L36_GLOBAL_EN_MASK, + 0 << CS35L36_GLOBAL_EN_SHIFT); + + usleep_range(2000, 2100); + break; + default: + dev_dbg(component->dev, "Invalid event = 0x%x\n", event); + return -EINVAL; + } + + return 0; +} + +static int cs35l36_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!cs35l36->pdata.extern_boost) + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2, + CS35L36_BST_EN_MASK, + CS35L36_BST_EN << + CS35L36_BST_EN_SHIFT); + break; + case SND_SOC_DAPM_POST_PMD: + if (!cs35l36->pdata.extern_boost) + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2, + CS35L36_BST_EN_MASK, + CS35L36_BST_DIS_VP << + CS35L36_BST_EN_SHIFT); + break; + default: + dev_dbg(component->dev, "Invalid event = 0x%x\n", event); + return -EINVAL; + } + + return 0; +} + +static const char * const cs35l36_chan_text[] = { + "RX1", + "RX2", +}; + +static SOC_ENUM_SINGLE_DECL(chansel_enum, CS35L36_ASP_RX1_SLOT, 0, + cs35l36_chan_text); + +static const struct snd_kcontrol_new cs35l36_chan_mux = + SOC_DAPM_ENUM("Input Mux", chansel_enum); + +static const struct snd_kcontrol_new amp_enable_ctrl = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_SHIFT, 1, 1); + +static const struct snd_kcontrol_new boost_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const char * const asp_tx_src_text[] = { + "Zero Fill", "ASPRX1", "VMON", "IMON", "ERRVOL", "VPMON", "VBSTMON" +}; + +static const unsigned int asp_tx_src_values[] = { + 0x00, 0x08, 0x18, 0x19, 0x20, 0x28, 0x29 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx1_src_enum, CS35L36_ASP_TX1_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx1_src = + SOC_DAPM_ENUM("ASPTX1SRC", asp_tx1_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx2_src_enum, CS35L36_ASP_TX2_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx2_src = + SOC_DAPM_ENUM("ASPTX2SRC", asp_tx2_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx3_src_enum, CS35L36_ASP_TX3_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx3_src = + SOC_DAPM_ENUM("ASPTX3SRC", asp_tx3_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx4_src_enum, CS35L36_ASP_TX4_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx4_src = + SOC_DAPM_ENUM("ASPTX4SRC", asp_tx4_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx5_src_enum, CS35L36_ASP_TX5_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx5_src = + SOC_DAPM_ENUM("ASPTX5SRC", asp_tx5_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx6_src_enum, CS35L36_ASP_TX6_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx6_src = + SOC_DAPM_ENUM("ASPTX6SRC", asp_tx6_src_enum); + +static const struct snd_soc_dapm_widget cs35l36_dapm_widgets[] = { + SND_SOC_DAPM_MUX("Channel Mux", SND_SOC_NOPM, 0, 0, &cs35l36_chan_mux), + SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS35L36_ASP_RX_TX_EN, 16, 0), + + SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L36_PWR_CTRL2, 0, 0, NULL, 0, + cs35l36_main_amp_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1, &_enable_ctrl), + SND_SOC_DAPM_MIXER("CLASS H", CS35L36_PWR_CTRL3, 4, 0, NULL, 0), + SND_SOC_DAPM_SWITCH_E("BOOST Enable", SND_SOC_NOPM, 0, 0, &boost_ctrl, + cs35l36_boost_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L36_ASP_RX_TX_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1, CS35L36_ASP_RX_TX_EN, 1, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2, CS35L36_ASP_RX_TX_EN, 2, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3, CS35L36_ASP_RX_TX_EN, 3, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4, CS35L36_ASP_RX_TX_EN, 4, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5, CS35L36_ASP_RX_TX_EN, 5, 0), + + SND_SOC_DAPM_MUX("ASPTX1SRC", SND_SOC_NOPM, 0, 0, &asp_tx1_src), + SND_SOC_DAPM_MUX("ASPTX2SRC", SND_SOC_NOPM, 0, 0, &asp_tx2_src), + SND_SOC_DAPM_MUX("ASPTX3SRC", SND_SOC_NOPM, 0, 0, &asp_tx3_src), + SND_SOC_DAPM_MUX("ASPTX4SRC", SND_SOC_NOPM, 0, 0, &asp_tx4_src), + SND_SOC_DAPM_MUX("ASPTX5SRC", SND_SOC_NOPM, 0, 0, &asp_tx5_src), + SND_SOC_DAPM_MUX("ASPTX6SRC", SND_SOC_NOPM, 0, 0, &asp_tx6_src), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L36_PWR_CTRL2, 12, 0), + SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L36_PWR_CTRL2, 13, 0), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L36_PWR_CTRL2, 8, 0), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L36_PWR_CTRL2, 9, 0), + + SND_SOC_DAPM_INPUT("VP"), + SND_SOC_DAPM_INPUT("VBST"), + SND_SOC_DAPM_INPUT("VSENSE"), +}; + +static const struct snd_soc_dapm_route cs35l36_audio_map[] = { + {"VPMON ADC", NULL, "VP"}, + {"VBSTMON ADC", NULL, "VBST"}, + {"IMON ADC", NULL, "VSENSE"}, + {"VMON ADC", NULL, "VSENSE"}, + + {"ASPTX1SRC", "IMON", "IMON ADC"}, + {"ASPTX1SRC", "VMON", "VMON ADC"}, + {"ASPTX1SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX1SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX2SRC", "IMON", "IMON ADC"}, + {"ASPTX2SRC", "VMON", "VMON ADC"}, + {"ASPTX2SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX2SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX3SRC", "IMON", "IMON ADC"}, + {"ASPTX3SRC", "VMON", "VMON ADC"}, + {"ASPTX3SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX3SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX4SRC", "IMON", "IMON ADC"}, + {"ASPTX4SRC", "VMON", "VMON ADC"}, + {"ASPTX4SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX4SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX5SRC", "IMON", "IMON ADC"}, + {"ASPTX5SRC", "VMON", "VMON ADC"}, + {"ASPTX5SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX5SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX6SRC", "IMON", "IMON ADC"}, + {"ASPTX6SRC", "VMON", "VMON ADC"}, + {"ASPTX6SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX6SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX1", NULL, "ASPTX1SRC"}, + {"ASPTX2", NULL, "ASPTX2SRC"}, + {"ASPTX3", NULL, "ASPTX3SRC"}, + {"ASPTX4", NULL, "ASPTX4SRC"}, + {"ASPTX5", NULL, "ASPTX5SRC"}, + {"ASPTX6", NULL, "ASPTX6SRC"}, + + {"AMP Capture", NULL, "ASPTX1"}, + {"AMP Capture", NULL, "ASPTX2"}, + {"AMP Capture", NULL, "ASPTX3"}, + {"AMP Capture", NULL, "ASPTX4"}, + {"AMP Capture", NULL, "ASPTX5"}, + {"AMP Capture", NULL, "ASPTX6"}, + + {"AMP Enable", "Switch", "AMP Playback"}, + {"SDIN", NULL, "AMP Enable"}, + {"Channel Mux", "RX1", "SDIN"}, + {"Channel Mux", "RX2", "SDIN"}, + {"BOOST Enable", "Switch", "Channel Mux"}, + {"CLASS H", NULL, "BOOST Enable"}, + {"Main AMP", NULL, "Channel Mux"}, + {"Main AMP", NULL, "CLASS H"}, + {"SPK", NULL, "Main AMP"}, +}; + +static int cs35l36_set_dai_fmt(struct snd_soc_dai *component_dai, + unsigned int fmt) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component_dai->component); + unsigned int asp_fmt, lrclk_fmt, sclk_fmt, clock_provider, clk_frc; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + clock_provider = 1; + break; + case SND_SOC_DAIFMT_CBC_CFC: + clock_provider = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_MSTR_MASK, + clock_provider << CS35L36_SCLK_MSTR_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_MSTR_MASK, + clock_provider << CS35L36_LRCLK_MSTR_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: + clk_frc = 1; + break; + case SND_SOC_DAIFMT_GATED: + clk_frc = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_FRC_MASK, clk_frc << + CS35L36_SCLK_FRC_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_FRC_MASK, clk_frc << + CS35L36_LRCLK_FRC_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + asp_fmt = 0; + break; + case SND_SOC_DAIFMT_I2S: + asp_fmt = 2; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + lrclk_fmt = 1; + sclk_fmt = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + lrclk_fmt = 0; + sclk_fmt = 1; + break; + case SND_SOC_DAIFMT_IB_IF: + lrclk_fmt = 1; + sclk_fmt = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + lrclk_fmt = 0; + sclk_fmt = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_INV_MASK, + lrclk_fmt << CS35L36_LRCLK_INV_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_INV_MASK, + sclk_fmt << CS35L36_SCLK_INV_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FORMAT, + CS35L36_ASP_FMT_MASK, asp_fmt); + + return 0; +} + +struct cs35l36_global_fs_config { + int rate; + int fs_cfg; +}; + +static const struct cs35l36_global_fs_config cs35l36_fs_rates[] = { + {12000, 0x01}, + {24000, 0x02}, + {48000, 0x03}, + {96000, 0x04}, + {192000, 0x05}, + {384000, 0x06}, + {11025, 0x09}, + {22050, 0x0A}, + {44100, 0x0B}, + {88200, 0x0C}, + {176400, 0x0D}, + {8000, 0x11}, + {16000, 0x12}, + {32000, 0x13}, +}; + +static int cs35l36_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(dai->component); + unsigned int asp_width, global_fs = params_rate(params); + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l36_fs_rates); i++) { + if (global_fs == cs35l36_fs_rates[i].rate) + regmap_update_bits(cs35l36->regmap, + CS35L36_GLOBAL_CLK_CTRL, + CS35L36_GLOBAL_FS_MASK, + cs35l36_fs_rates[i].fs_cfg << + CS35L36_GLOBAL_FS_SHIFT); + } + + switch (params_width(params)) { + case 16: + asp_width = CS35L36_ASP_WIDTH_16; + break; + case 24: + asp_width = CS35L36_ASP_WIDTH_24; + break; + case 32: + asp_width = CS35L36_ASP_WIDTH_32; + break; + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL, + CS35L36_ASP_RX_WIDTH_MASK, + asp_width << CS35L36_ASP_RX_WIDTH_SHIFT); + } else { + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL, + CS35L36_ASP_TX_WIDTH_MASK, + asp_width << CS35L36_ASP_TX_WIDTH_SHIFT); + } + + return 0; +} + +static int cs35l36_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int fs1, fs2; + + if (freq > CS35L36_FS_NOM_6MHZ) { + fs1 = CS35L36_FS1_DEFAULT_VAL; + fs2 = CS35L36_FS2_DEFAULT_VAL; + } else { + fs1 = 3 * DIV_ROUND_UP(CS35L36_FS_NOM_6MHZ * 4, freq) + 4; + fs2 = 5 * DIV_ROUND_UP(CS35L36_FS_NOM_6MHZ * 4, freq) + 4; + } + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_TST_FS_MON0, + CS35L36_FS1_WINDOW_MASK | CS35L36_FS2_WINDOW_MASK, + fs1 | (fs2 << CS35L36_FS2_WINDOW_SHIFT)); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + return 0; +} + +static const struct cs35l36_pll_config *cs35l36_get_clk_config( + struct cs35l36_private *cs35l36, int freq) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l36_pll_sysclk); i++) { + if (cs35l36_pll_sysclk[i].freq == freq) + return &cs35l36_pll_sysclk[i]; + } + + return NULL; +} + +static const unsigned int cs35l36_src_rates[] = { + 8000, 12000, 11025, 16000, 22050, 24000, 32000, + 44100, 48000, 88200, 96000, 176400, 192000, 384000 +}; + +static const struct snd_pcm_hw_constraint_list cs35l36_constraints = { + .count = ARRAY_SIZE(cs35l36_src_rates), + .list = cs35l36_src_rates, +}; + +static int cs35l36_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &cs35l36_constraints); + + return 0; +} + +static const struct snd_soc_dai_ops cs35l36_ops = { + .startup = cs35l36_pcm_startup, + .set_fmt = cs35l36_set_dai_fmt, + .hw_params = cs35l36_pcm_hw_params, + .set_sysclk = cs35l36_dai_set_sysclk, +}; + +static struct snd_soc_dai_driver cs35l36_dai[] = { + { + .name = "cs35l36-pcm", + .id = 0, + .playback = { + .stream_name = "AMP Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L36_RX_FORMATS, + }, + .capture = { + .stream_name = "AMP Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L36_TX_FORMATS, + }, + .ops = &cs35l36_ops, + .symmetric_rate = 1, + }, +}; + +static int cs35l36_component_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, + int dir) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + const struct cs35l36_pll_config *clk_cfg; + int prev_clksrc; + bool pdm_switch; + + prev_clksrc = cs35l36->clksrc; + + switch (clk_id) { + case 0: + cs35l36->clksrc = CS35L36_PLLSRC_SCLK; + break; + case 1: + cs35l36->clksrc = CS35L36_PLLSRC_LRCLK; + break; + case 2: + cs35l36->clksrc = CS35L36_PLLSRC_PDMCLK; + break; + case 3: + cs35l36->clksrc = CS35L36_PLLSRC_SELF; + break; + case 4: + cs35l36->clksrc = CS35L36_PLLSRC_MCLK; + break; + default: + return -EINVAL; + } + + clk_cfg = cs35l36_get_clk_config(cs35l36, freq); + if (clk_cfg == NULL) { + dev_err(component->dev, "Invalid CLK Config Freq: %d\n", freq); + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_OPENLOOP_MASK, + 1 << CS35L36_PLL_OPENLOOP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_REFCLK_FREQ_MASK, + clk_cfg->clk_cfg << CS35L36_REFCLK_FREQ_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_REFCLK_EN_MASK, + 0 << CS35L36_PLL_REFCLK_EN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_CLK_SEL_MASK, + cs35l36->clksrc); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_OPENLOOP_MASK, + 0 << CS35L36_PLL_OPENLOOP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_REFCLK_EN_MASK, + 1 << CS35L36_PLL_REFCLK_EN_SHIFT); + + if (cs35l36->rev_id == CS35L36_REV_A0) { + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_write(cs35l36->regmap, CS35L36_DCO_CTRL, 0x00036DA8); + regmap_write(cs35l36->regmap, CS35L36_MISC_CTRL, 0x0100EE0E); + + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS, + CS35L36_PLL_IGAIN_MASK, + CS35L36_PLL_IGAIN << + CS35L36_PLL_IGAIN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS, + CS35L36_PLL_FFL_IGAIN_MASK, + clk_cfg->fll_igain); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + if (cs35l36->clksrc == CS35L36_PLLSRC_PDMCLK) { + pdm_switch = cs35l36->ldm_mode_sel && + (prev_clksrc != CS35L36_PLLSRC_PDMCLK); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 0 << CS35L36_NG_DELAY_SHIFT); + + regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG, + CS35L36_PDM_MODE_MASK, + 1 << CS35L36_PDM_MODE_SHIFT); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 3 << CS35L36_NG_DELAY_SHIFT); + } else { + pdm_switch = cs35l36->ldm_mode_sel && + (prev_clksrc == CS35L36_PLLSRC_PDMCLK); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 0 << CS35L36_NG_DELAY_SHIFT); + + regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG, + CS35L36_PDM_MODE_MASK, + 0 << CS35L36_PDM_MODE_SHIFT); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 3 << CS35L36_NG_DELAY_SHIFT); + } + + return 0; +} + +static int cs35l36_boost_inductor(struct cs35l36_private *cs35l36, int inductor) +{ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF, + CS35L36_BSTCVRT_K1_MASK, 0x3C); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF, + CS35L36_BSTCVRT_K2_MASK, + 0x3C << CS35L36_BSTCVRT_K2_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SW_FREQ, + CS35L36_BSTCVRT_CCMFREQ_MASK, 0x00); + + switch (inductor) { + case 1000: /* 1 uH */ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_SLOPE_MASK, + 0x75 << CS35L36_BSTCVRT_SLOPE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_LBSTVAL_MASK, 0x00); + break; + case 1200: /* 1.2 uH */ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_SLOPE_MASK, + 0x6B << CS35L36_BSTCVRT_SLOPE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_LBSTVAL_MASK, 0x01); + break; + default: + dev_err(cs35l36->dev, "%s Invalid Inductor Value %d uH\n", + __func__, inductor); + return -EINVAL; + } + + return 0; +} + +static int cs35l36_component_probe(struct snd_soc_component *component) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int ret; + + if ((cs35l36->rev_id == CS35L36_REV_A0) && cs35l36->pdata.dcm_mode) { + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_DCM_CTRL, + CS35L36_DCM_AUTO_MASK, + CS35L36_DCM_AUTO_MASK); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL, + CS35L36_BST_MAN_IPKCOMP_MASK, + 0 << CS35L36_BST_MAN_IPKCOMP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL, + CS35L36_BST_MAN_IPKCOMP_EN_MASK, + CS35L36_BST_MAN_IPKCOMP_EN_MASK); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + if (cs35l36->pdata.amp_pcm_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_DIG_VOL_CTRL, + CS35L36_AMP_PCM_INV_MASK, + CS35L36_AMP_PCM_INV_MASK); + + if (cs35l36->pdata.multi_amp_mode) + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_ASP_TX_HIZ_MASK, + CS35L36_ASP_TX_HIZ_MASK); + + if (cs35l36->pdata.imon_pol_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT, + CS35L36_IMON_POL_MASK, 0); + + if (cs35l36->pdata.vmon_pol_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT, + CS35L36_VMON_POL_MASK, 0); + + if (cs35l36->pdata.bst_vctl) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1, + CS35L35_BSTCVRT_CTL_MASK, + cs35l36->pdata.bst_vctl); + + if (cs35l36->pdata.bst_vctl_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2, + CS35L35_BSTCVRT_CTL_SEL_MASK, + cs35l36->pdata.bst_vctl_sel); + + if (cs35l36->pdata.bst_ipk) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_PEAK_CUR, + CS35L36_BST_IPK_MASK, + cs35l36->pdata.bst_ipk); + + if (cs35l36->pdata.boost_ind) { + ret = cs35l36_boost_inductor(cs35l36, cs35l36->pdata.boost_ind); + if (ret < 0) { + dev_err(cs35l36->dev, + "Boost inductor config failed(%d)\n", ret); + return ret; + } + } + + if (cs35l36->pdata.temp_warn_thld) + regmap_update_bits(cs35l36->regmap, CS35L36_DTEMP_WARN_THLD, + CS35L36_TEMP_THLD_MASK, + cs35l36->pdata.temp_warn_thld); + + if (cs35l36->pdata.irq_drv_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_DRV_SEL_MASK, + cs35l36->pdata.irq_drv_sel << + CS35L36_INT_DRV_SEL_SHIFT); + + if (cs35l36->pdata.irq_gpio_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_GPIO_SEL_MASK, + cs35l36->pdata.irq_gpio_sel << + CS35L36_INT_GPIO_SEL_SHIFT); + + /* + * Rev B0 has 2 versions + * L36 is 10V + * L37 is 12V + * If L36 we need to clamp some values for safety + * after probe has setup dt values. We want to make + * sure we dont miss any values set in probe + */ + if (cs35l36->chip_version == CS35L36_10V_L36) { + regmap_update_bits(cs35l36->regmap, + CS35L36_BSTCVRT_OVERVOLT_CTRL, + CS35L36_BST_OVP_THLD_MASK, + CS35L36_BST_OVP_THLD_11V); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_BST_ANA2_TEST, + CS35L36_BST_OVP_TRIM_MASK, + CS35L36_BST_OVP_TRIM_11V << + CS35L36_BST_OVP_TRIM_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2, + CS35L36_BST_CTRL_LIM_MASK, + 1 << CS35L36_BST_CTRL_LIM_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1, + CS35L35_BSTCVRT_CTL_MASK, + CS35L36_BST_CTRL_10V_CLAMP); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + /* + * RevA and B require the disabling of + * SYNC_GLOBAL_OVR when GLOBAL_EN = 0. + * Just turn it off from default + */ + regmap_update_bits(cs35l36->regmap, CS35L36_CTRL_OVRRIDE, + CS35L36_SYNC_GLOBAL_OVR_MASK, + 0 << CS35L36_SYNC_GLOBAL_OVR_SHIFT); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_cs35l36 = { + .probe = &cs35l36_component_probe, + .set_sysclk = cs35l36_component_set_sysclk, + .dapm_widgets = cs35l36_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l36_dapm_widgets), + .dapm_routes = cs35l36_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l36_audio_map), + .controls = cs35l36_aud_controls, + .num_controls = ARRAY_SIZE(cs35l36_aud_controls), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct regmap_config cs35l36_regmap = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = CS35L36_PAC_PMEM_WORD1023, + .reg_defaults = cs35l36_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l36_reg), + .precious_reg = cs35l36_precious_reg, + .volatile_reg = cs35l36_volatile_reg, + .readable_reg = cs35l36_readable_reg, + .cache_type = REGCACHE_MAPLE, +}; + +static irqreturn_t cs35l36_irq(int irq, void *data) +{ + struct cs35l36_private *cs35l36 = data; + unsigned int status[4]; + unsigned int masks[4]; + int ret = IRQ_NONE; + + /* ack the irq by reading all status registers */ + regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_STATUS, status, + ARRAY_SIZE(status)); + + regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_MASK, masks, + ARRAY_SIZE(masks)); + + /* Check to see if unmasked bits are active */ + if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) && + !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) { + return IRQ_NONE; + } + + /* + * The following interrupts require a + * protection release cycle to get the + * speaker out of Safe-Mode. + */ + if (status[2] & CS35L36_AMP_SHORT_ERR) { + dev_crit(cs35l36->dev, "Amp short error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, + CS35L36_AMP_SHORT_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT3_STATUS, + CS35L36_AMP_SHORT_ERR, + CS35L36_AMP_SHORT_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_TEMP_WARN) { + dev_crit(cs35l36->dev, "Over temperature warning\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, + CS35L36_TEMP_WARN_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_TEMP_WARN, CS35L36_TEMP_WARN); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_TEMP_ERR) { + dev_crit(cs35l36->dev, "Over temperature error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_TEMP_ERR, CS35L36_TEMP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_OVP_ERR) { + dev_crit(cs35l36->dev, "VBST Over Voltage error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_OVP_ERR, CS35L36_BST_OVP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_DCM_UVP_ERR) { + dev_crit(cs35l36->dev, "DCM VBST Under Voltage Error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, + CS35L36_BST_UVP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_DCM_UVP_ERR, + CS35L36_BST_DCM_UVP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_SHORT_ERR) { + dev_crit(cs35l36->dev, "LBST SHORT error!\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, + CS35L36_BST_SHORT_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_SHORT_ERR, + CS35L36_BST_SHORT_ERR); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int cs35l36_handle_of_data(struct i2c_client *i2c_client, + struct cs35l36_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config; + struct device_node *vpbr_node; + unsigned int val; + int ret; + + if (!np) + return 0; + + ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val); + if (!ret) { + if (val < 2550 || val > 12000) { + dev_err(&i2c_client->dev, + "Invalid Boost Voltage %d mV\n", val); + return -EINVAL; + } + pdata->bst_vctl = (((val - 2550) / 100) + 1) << 1; + } else { + dev_err(&i2c_client->dev, + "Unable to find required parameter 'cirrus,boost-ctl-millivolt'"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "cirrus,boost-ctl-select", &val); + if (!ret) + pdata->bst_vctl_sel = val | CS35L36_VALID_PDATA; + + ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val); + if (!ret) { + if (val < 1600 || val > 4500) { + dev_err(&i2c_client->dev, + "Invalid Boost Peak Current %u mA\n", val); + return -EINVAL; + } + + pdata->bst_ipk = (val - 1600) / 50; + } else { + dev_err(&i2c_client->dev, + "Unable to find required parameter 'cirrus,boost-peak-milliamp'"); + return -EINVAL; + } + + pdata->multi_amp_mode = of_property_read_bool(np, + "cirrus,multi-amp-mode"); + + pdata->dcm_mode = of_property_read_bool(np, + "cirrus,dcm-mode-enable"); + + pdata->amp_pcm_inv = of_property_read_bool(np, + "cirrus,amp-pcm-inv"); + + pdata->imon_pol_inv = of_property_read_bool(np, + "cirrus,imon-pol-inv"); + + pdata->vmon_pol_inv = of_property_read_bool(np, + "cirrus,vmon-pol-inv"); + + if (of_property_read_u32(np, "cirrus,temp-warn-threshold", &val) >= 0) + pdata->temp_warn_thld = val | CS35L36_VALID_PDATA; + + if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) { + pdata->boost_ind = val; + } else { + dev_err(&i2c_client->dev, "Inductor not specified.\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "cirrus,irq-drive-select", &val) >= 0) + pdata->irq_drv_sel = val | CS35L36_VALID_PDATA; + + if (of_property_read_u32(np, "cirrus,irq-gpio-select", &val) >= 0) + pdata->irq_gpio_sel = val | CS35L36_VALID_PDATA; + + /* VPBR Config */ + vpbr_node = of_get_child_by_name(np, "cirrus,vpbr-config"); + vpbr_config->is_present = vpbr_node ? true : false; + if (vpbr_config->is_present) { + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-en", + &val) >= 0) + vpbr_config->vpbr_en = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-thld", + &val) >= 0) + vpbr_config->vpbr_thld = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-rate", + &val) >= 0) + vpbr_config->vpbr_atk_rate = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-vol", + &val) >= 0) + vpbr_config->vpbr_atk_vol = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-max-attn", + &val) >= 0) + vpbr_config->vpbr_max_attn = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-wait", + &val) >= 0) + vpbr_config->vpbr_wait = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-rel-rate", + &val) >= 0) + vpbr_config->vpbr_rel_rate = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-mute-en", + &val) >= 0) + vpbr_config->vpbr_mute_en = val; + } + of_node_put(vpbr_node); + + return 0; +} + +static int cs35l36_pac(struct cs35l36_private *cs35l36) +{ + int ret, count; + unsigned int val; + + if (cs35l36->rev_id != CS35L36_REV_B0) + return 0; + + /* + * Magic code for internal PAC + */ + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + usleep_range(9500, 10500); + + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_RESET); + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3, + CS35L36_PAC_MEM_ACCESS); + regmap_write(cs35l36->regmap, CS35L36_PAC_PMEM_WORD0, + CS35L36_B0_PAC_PATCH); + + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3, + CS35L36_PAC_MEM_ACCESS_CLR); + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_ENABLE_MASK); + + usleep_range(9500, 10500); + + ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS, &val); + if (ret < 0) { + dev_err(cs35l36->dev, "Failed to read int4_status %d\n", ret); + return ret; + } + + count = 0; + while (!(val & CS35L36_MCU_CONFIG_CLR)) { + usleep_range(100, 200); + count++; + + ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS, + &val); + if (ret < 0) { + dev_err(cs35l36->dev, "Failed to read int4_status %d\n", + ret); + return ret; + } + + if (count >= 100) + return -EINVAL; + } + + regmap_write(cs35l36->regmap, CS35L36_INT4_STATUS, + CS35L36_MCU_CONFIG_CLR); + regmap_update_bits(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_ENABLE_MASK, 0); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + + return 0; +} + +static void cs35l36_apply_vpbr_config(struct cs35l36_private *cs35l36) +{ + struct cs35l36_platform_data *pdata = &cs35l36->pdata; + struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config; + + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL3, + CS35L36_VPBR_EN_MASK, + vpbr_config->vpbr_en << + CS35L36_VPBR_EN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_THLD_MASK, + vpbr_config->vpbr_thld << + CS35L36_VPBR_THLD_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_MAX_ATTN_MASK, + vpbr_config->vpbr_max_attn << + CS35L36_VPBR_MAX_ATTN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_ATK_VOL_MASK, + vpbr_config->vpbr_atk_vol << + CS35L36_VPBR_ATK_VOL_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_ATK_RATE_MASK, + vpbr_config->vpbr_atk_rate << + CS35L36_VPBR_ATK_RATE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_WAIT_MASK, + vpbr_config->vpbr_wait << + CS35L36_VPBR_WAIT_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_REL_RATE_MASK, + vpbr_config->vpbr_rel_rate << + CS35L36_VPBR_REL_RATE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_MUTE_EN_MASK, + vpbr_config->vpbr_mute_en << + CS35L36_VPBR_MUTE_EN_SHIFT); +} + +static const struct reg_sequence cs35l36_reva0_errata_patch[] = { + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 }, + /* Errata Writes */ + { CS35L36_OTP_CTRL1, 0x00002060 }, + { CS35L36_OTP_CTRL2, 0x00000001 }, + { CS35L36_OTP_CTRL1, 0x00002460 }, + { CS35L36_OTP_CTRL2, 0x00000001 }, + { 0x00002088, 0x012A1838 }, + { 0x00003014, 0x0100EE0E }, + { 0x00003008, 0x0008184A }, + { 0x00007418, 0x509001C8 }, + { 0x00007064, 0x0929A800 }, + { 0x00002D10, 0x0002C01C }, + { 0x0000410C, 0x00000A11 }, + { 0x00006E08, 0x8B19140C }, + { 0x00006454, 0x0300000A }, + { CS35L36_AMP_NG_CTRL, 0x000020EF }, + { 0x00007E34, 0x0000000E }, + { 0x0000410C, 0x00000A11 }, + { 0x00007410, 0x20514B00 }, + /* PAC Config */ + { CS35L36_CTRL_OVRRIDE, 0x00000000 }, + { CS35L36_PAC_INT0_CTRL, 0x00860001 }, + { CS35L36_PAC_INT1_CTRL, 0x00860001 }, + { CS35L36_PAC_INT2_CTRL, 0x00860001 }, + { CS35L36_PAC_INT3_CTRL, 0x00860001 }, + { CS35L36_PAC_INT4_CTRL, 0x00860001 }, + { CS35L36_PAC_INT5_CTRL, 0x00860001 }, + { CS35L36_PAC_INT6_CTRL, 0x00860001 }, + { CS35L36_PAC_INT7_CTRL, 0x00860001 }, + { CS35L36_PAC_INT_FLUSH_CTRL, 0x000000FF }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 }, +}; + +static const struct reg_sequence cs35l36_revb0_errata_patch[] = { + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 }, + { 0x00007064, 0x0929A800 }, + { 0x00007850, 0x00002FA9 }, + { 0x00007854, 0x0003F1D5 }, + { 0x00007858, 0x0003F5E3 }, + { 0x0000785C, 0x00001137 }, + { 0x00007860, 0x0001A7A5 }, + { 0x00007864, 0x0002F16A }, + { 0x00007868, 0x00003E21 }, + { 0x00007848, 0x00000001 }, + { 0x00003854, 0x05180240 }, + { 0x00007418, 0x509001C8 }, + { 0x0000394C, 0x028764BD }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 }, +}; + +static int cs35l36_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs35l36_private *cs35l36; + struct device *dev = &i2c_client->dev; + struct cs35l36_platform_data *pdata = dev_get_platdata(dev); + struct irq_data *irq_d; + int ret, irq_pol, chip_irq_pol, i; + u32 reg_id, reg_revid, l37_id_reg; + + cs35l36 = devm_kzalloc(dev, sizeof(struct cs35l36_private), GFP_KERNEL); + if (!cs35l36) + return -ENOMEM; + + cs35l36->dev = dev; + + i2c_set_clientdata(i2c_client, cs35l36); + cs35l36->regmap = devm_regmap_init_i2c(i2c_client, &cs35l36_regmap); + if (IS_ERR(cs35l36->regmap)) { + ret = PTR_ERR(cs35l36->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + cs35l36->num_supplies = ARRAY_SIZE(cs35l36_supplies); + for (i = 0; i < ARRAY_SIZE(cs35l36_supplies); i++) + cs35l36->supplies[i].supply = cs35l36_supplies[i]; + + ret = devm_regulator_bulk_get(dev, cs35l36->num_supplies, + cs35l36->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request core supplies: %d\n", ret); + return ret; + } + + if (pdata) { + cs35l36->pdata = *pdata; + } else { + pdata = devm_kzalloc(dev, sizeof(struct cs35l36_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (i2c_client->dev.of_node) { + ret = cs35l36_handle_of_data(i2c_client, pdata); + if (ret != 0) + return ret; + + } + + cs35l36->pdata = *pdata; + } + + ret = regulator_bulk_enable(cs35l36->num_supplies, cs35l36->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", ret); + return ret; + } + + /* returning NULL can be an option if in stereo mode */ + cs35l36->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs35l36->reset_gpio)) { + ret = PTR_ERR(cs35l36->reset_gpio); + cs35l36->reset_gpio = NULL; + if (ret == -EBUSY) { + dev_info(dev, "Reset line busy, assuming shared reset\n"); + } else { + dev_err(dev, "Failed to get reset GPIO: %d\n", ret); + goto err_disable_regs; + } + } + + if (cs35l36->reset_gpio) + gpiod_set_value_cansleep(cs35l36->reset_gpio, 1); + + usleep_range(2000, 2100); + + /* initialize amplifier */ + ret = regmap_read(cs35l36->regmap, CS35L36_SW_RESET, ®_id); + if (ret < 0) { + dev_err(dev, "Get Device ID failed %d\n", ret); + goto err; + } + + if (reg_id != CS35L36_CHIP_ID) { + dev_err(dev, "Device ID (%X). Expected ID %X\n", reg_id, + CS35L36_CHIP_ID); + ret = -ENODEV; + goto err; + } + + ret = regmap_read(cs35l36->regmap, CS35L36_REV_ID, ®_revid); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed %d\n", ret); + goto err; + } + + cs35l36->rev_id = reg_revid >> 8; + + ret = regmap_read(cs35l36->regmap, CS35L36_OTP_MEM30, &l37_id_reg); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to read otp_id Register %d\n", + ret); + goto err; + } + + if ((l37_id_reg & CS35L36_OTP_REV_MASK) == CS35L36_OTP_REV_L37) + cs35l36->chip_version = CS35L36_12V_L37; + else + cs35l36->chip_version = CS35L36_10V_L36; + + switch (cs35l36->rev_id) { + case CS35L36_REV_A0: + ret = regmap_register_patch(cs35l36->regmap, + cs35l36_reva0_errata_patch, + ARRAY_SIZE(cs35l36_reva0_errata_patch)); + if (ret < 0) { + dev_err(dev, "Failed to apply A0 errata patch %d\n", + ret); + goto err; + } + break; + case CS35L36_REV_B0: + ret = cs35l36_pac(cs35l36); + if (ret < 0) { + dev_err(dev, "Failed to Trim OTP %d\n", ret); + goto err; + } + + ret = regmap_register_patch(cs35l36->regmap, + cs35l36_revb0_errata_patch, + ARRAY_SIZE(cs35l36_revb0_errata_patch)); + if (ret < 0) { + dev_err(dev, "Failed to apply B0 errata patch %d\n", + ret); + goto err; + } + break; + } + + if (pdata->vpbr_config.is_present) + cs35l36_apply_vpbr_config(cs35l36); + + irq_d = irq_get_irq_data(i2c_client->irq); + if (!irq_d) { + dev_err(&i2c_client->dev, "Invalid IRQ: %d\n", i2c_client->irq); + ret = -ENODEV; + goto err; + } + + irq_pol = irqd_get_trigger_type(irq_d); + + switch (irq_pol) { + case IRQF_TRIGGER_FALLING: + case IRQF_TRIGGER_LOW: + chip_irq_pol = 0; + break; + case IRQF_TRIGGER_RISING: + case IRQF_TRIGGER_HIGH: + chip_irq_pol = 1; + break; + default: + dev_err(cs35l36->dev, "Invalid IRQ polarity: %d\n", irq_pol); + ret = -EINVAL; + goto err; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_POL_SEL_MASK, + chip_irq_pol << CS35L36_INT_POL_SEL_SHIFT); + + ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l36_irq, + IRQF_ONESHOT | irq_pol, "cs35l36", + cs35l36); + if (ret != 0) { + dev_err(dev, "Failed to request IRQ: %d\n", ret); + goto err; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_OUTPUT_EN_MASK, 1); + + /* Set interrupt masks for critical errors */ + regmap_write(cs35l36->regmap, CS35L36_INT1_MASK, + CS35L36_INT1_MASK_DEFAULT); + regmap_write(cs35l36->regmap, CS35L36_INT3_MASK, + CS35L36_INT3_MASK_DEFAULT); + + dev_info(&i2c_client->dev, "Cirrus Logic CS35L%d, Revision: %02X\n", + cs35l36->chip_version, reg_revid >> 8); + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_cs35l36, + cs35l36_dai, + ARRAY_SIZE(cs35l36_dai)); + if (ret < 0) { + dev_err(dev, "%s: Register component failed %d\n", __func__, + ret); + goto err; + } + + return 0; + +err: + gpiod_set_value_cansleep(cs35l36->reset_gpio, 0); + +err_disable_regs: + regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies); + return ret; +} + +static void cs35l36_i2c_remove(struct i2c_client *client) +{ + struct cs35l36_private *cs35l36 = i2c_get_clientdata(client); + + /* Reset interrupt masks for device removal */ + regmap_write(cs35l36->regmap, CS35L36_INT1_MASK, + CS35L36_INT1_MASK_RESET); + regmap_write(cs35l36->regmap, CS35L36_INT3_MASK, + CS35L36_INT3_MASK_RESET); + + if (cs35l36->reset_gpio) + gpiod_set_value_cansleep(cs35l36->reset_gpio, 0); + + regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies); +} +static const struct of_device_id cs35l36_of_match[] = { + {.compatible = "cirrus,cs35l36"}, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l36_of_match); + +static const struct i2c_device_id cs35l36_id[] = { + {"cs35l36", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs35l36_id); + +static struct i2c_driver cs35l36_i2c_driver = { + .driver = { + .name = "cs35l36", + .of_match_table = cs35l36_of_match, + }, + .id_table = cs35l36_id, + .probe = cs35l36_i2c_probe, + .remove = cs35l36_i2c_remove, +}; +module_i2c_driver(cs35l36_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L36 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l36.h b/sound/soc/codecs/cs35l36.h new file mode 100644 index 0000000000..f6e38c633b --- /dev/null +++ b/sound/soc/codecs/cs35l36.h @@ -0,0 +1,446 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cs35l36.h -- CS35L36 ALSA SoC audio driver + * + * Copyright 2018 Cirrus Logic, Inc. + * + * Author: James Schulman + * + */ + +#ifndef __CS35L36_H__ +#define __CS35L36_H__ + +#include + +#define CS35L36_FIRSTREG 0x00000000 +#define CS35L36_LASTREG 0x00E037FC +#define CS35L36_SW_RESET 0x00000000 +#define CS35L36_SW_REV 0x00000004 +#define CS35L36_HW_REV 0x00000008 +#define CS35L36_TESTKEY_CTRL 0x00000020 +#define CS35L36_USERKEY_CTL 0x00000024 +#define CS35L36_OTP_MEM30 0x00000478 +#define CS35L36_OTP_CTRL1 0x00000500 +#define CS35L36_OTP_CTRL2 0x00000504 +#define CS35L36_OTP_CTRL3 0x00000508 +#define CS35L36_OTP_CTRL4 0x0000050C +#define CS35L36_OTP_CTRL5 0x00000510 +#define CS35L36_PAC_CTL1 0x00000C00 +#define CS35L36_PAC_CTL2 0x00000C04 +#define CS35L36_PAC_CTL3 0x00000C08 +#define CS35L36_DEVICE_ID 0x00002004 +#define CS35L36_FAB_ID 0x00002008 +#define CS35L36_REV_ID 0x0000200C +#define CS35L36_PWR_CTRL1 0x00002014 +#define CS35L36_PWR_CTRL2 0x00002018 +#define CS35L36_PWR_CTRL3 0x0000201C +#define CS35L36_CTRL_OVRRIDE 0x00002020 +#define CS35L36_AMP_OUT_MUTE 0x00002024 +#define CS35L36_OTP_TRIM_STATUS 0x00002028 +#define CS35L36_DISCH_FILT 0x0000202C +#define CS35L36_OSC_TRIM 0x00002030 +#define CS35L36_PROTECT_REL_ERR 0x00002034 +#define CS35L36_PAD_INTERFACE 0x00002400 +#define CS35L36_PLL_CLK_CTRL 0x00002C04 +#define CS35L36_GLOBAL_CLK_CTRL 0x00002C0C +#define CS35L36_ADC_CLK_CTRL 0x00002C10 +#define CS35L36_SWIRE_CLK_CTRL 0x00002C14 +#define CS35L36_SP_SCLK_CLK_CTRL 0x00002D00 +#define CS35L36_TST_FS_MON0 0x00002D10 +#define CS35L36_PLL_LOOP_PARAMS 0x00003008 +#define CS35L36_DCO_CTRL 0x00003010 +#define CS35L36_MISC_CTRL 0x00003014 +#define CS35L36_MDSYNC_EN 0x00003404 +#define CS35L36_MDSYNC_TX_ID 0x00003408 +#define CS35L36_MDSYNC_PWR_CTRL 0x0000340C +#define CS35L36_MDSYNC_DATA_TX 0x00003410 +#define CS35L36_MDSYNC_TX_STATUS 0x0000341C +#define CS35L36_MDSYNC_RX_STATUS 0x00003420 +#define CS35L36_MDSYNC_ERR_STATUS 0x00003424 +#define CS35L36_BSTCVRT_VCTRL1 0x00003800 +#define CS35L36_BSTCVRT_VCTRL2 0x00003804 +#define CS35L36_BSTCVRT_PEAK_CUR 0x00003808 +#define CS35L36_BSTCVRT_SFT_RAMP 0x0000380C +#define CS35L36_BSTCVRT_COEFF 0x00003810 +#define CS35L36_BSTCVRT_SLOPE_LBST 0x00003814 +#define CS35L36_BSTCVRT_SW_FREQ 0x00003818 +#define CS35L36_BSTCVRT_DCM_CTRL 0x0000381C +#define CS35L36_BSTCVRT_DCM_MODE_FORCE 0x00003820 +#define CS35L36_BSTCVRT_OVERVOLT_CTRL 0x00003830 +#define CS35L36_BST_TST_MANUAL 0x0000393C +#define CS35L36_BST_ANA2_TEST 0x0000394C +#define CS35L36_VPI_LIMIT_MODE 0x00003C04 +#define CS35L36_VPI_LIMIT_MINMAX 0x00003C08 +#define CS35L36_VPI_VP_THLD 0x00003C0C +#define CS35L36_VPI_TRACK_CTRL 0x00003C10 +#define CS35L36_VPI_TRIG_MODE_CTRL 0x00003C14 +#define CS35L36_VPI_TRIG_STEPS 0x00003C18 +#define CS35L36_VI_SPKMON_FILT 0x00004004 +#define CS35L36_VI_SPKMON_GAIN 0x00004008 +#define CS35L36_VI_SPKMON_IP_SEL 0x00004100 +#define CS35L36_DTEMP_WARN_THLD 0x00004220 +#define CS35L36_DTEMP_STATUS 0x00004300 +#define CS35L36_VPVBST_FS_SEL 0x00004400 +#define CS35L36_VPVBST_VP_CTRL 0x00004440 +#define CS35L36_VPVBST_VBST_CTRL 0x00004444 +#define CS35L36_ASP_TX_PIN_CTRL 0x00004800 +#define CS35L36_ASP_RATE_CTRL 0x00004804 +#define CS35L36_ASP_FORMAT 0x00004808 +#define CS35L36_ASP_FRAME_CTRL 0x00004818 +#define CS35L36_ASP_TX1_TX2_SLOT 0x0000481C +#define CS35L36_ASP_TX3_TX4_SLOT 0x00004820 +#define CS35L36_ASP_TX5_TX6_SLOT 0x00004824 +#define CS35L36_ASP_TX7_TX8_SLOT 0x00004828 +#define CS35L36_ASP_RX1_SLOT 0x0000482C +#define CS35L36_ASP_RX_TX_EN 0x0000483C +#define CS35L36_ASP_RX1_SEL 0x00004C00 +#define CS35L36_ASP_TX1_SEL 0x00004C20 +#define CS35L36_ASP_TX2_SEL 0x00004C24 +#define CS35L36_ASP_TX3_SEL 0x00004C28 +#define CS35L36_ASP_TX4_SEL 0x00004C2C +#define CS35L36_ASP_TX5_SEL 0x00004C30 +#define CS35L36_ASP_TX6_SEL 0x00004C34 +#define CS35L36_SWIRE_P1_TX1_SEL 0x00004C40 +#define CS35L36_SWIRE_P1_TX2_SEL 0x00004C44 +#define CS35L36_SWIRE_P2_TX1_SEL 0x00004C60 +#define CS35L36_SWIRE_P2_TX2_SEL 0x00004C64 +#define CS35L36_SWIRE_P2_TX3_SEL 0x00004C68 +#define CS35L36_SWIRE_DP1_FIFO_CFG 0x00005000 +#define CS35L36_SWIRE_DP2_FIFO_CFG 0x00005004 +#define CS35L36_SWIRE_DP3_FIFO_CFG 0x00005008 +#define CS35L36_SWIRE_PCM_RX_DATA 0x0000500C +#define CS35L36_SWIRE_FS_SEL 0x00005010 +#define CS35L36_SPARE_CP_BITS 0x00005C00 +#define CS35L36_AMP_DIG_VOL_CTRL 0x00006000 +#define CS35L36_VPBR_CFG 0x00006404 +#define CS35L36_VBBR_CFG 0x00006408 +#define CS35L36_VPBR_STATUS 0x0000640C +#define CS35L36_VBBR_STATUS 0x00006410 +#define CS35L36_OVERTEMP_CFG 0x00006414 +#define CS35L36_AMP_ERR_VOL 0x00006418 +#define CS35L36_CLASSH_CFG 0x00006800 +#define CS35L36_CLASSH_FET_DRV_CFG 0x00006804 +#define CS35L36_NG_CFG 0x00006808 +#define CS35L36_AMP_GAIN_CTRL 0x00006C04 +#define CS35L36_PWM_MOD_IO_CTRL 0x0000706C +#define CS35L36_PWM_MOD_STATUS 0x00007070 +#define CS35L36_DAC_MSM_CFG 0x00007400 +#define CS35L36_AMP_SLOPE_CTRL 0x00007410 +#define CS35L36_AMP_PDM_VOLUME 0x00007E04 +#define CS35L36_AMP_PDM_RATE_CTRL 0x00007E08 +#define CS35L36_PDM_CH_SEL 0x00007E10 +#define CS35L36_AMP_NG_CTRL 0x00007E14 +#define CS35L36_PDM_HIGHFILT_CTRL 0x00007E3C +#define CS35L36_INT1_STATUS 0x00D00000 +#define CS35L36_INT2_STATUS 0x00D00004 +#define CS35L36_INT3_STATUS 0x00D00008 +#define CS35L36_INT4_STATUS 0x00D0000C +#define CS35L36_INT1_RAW_STATUS 0x00D00020 +#define CS35L36_INT2_RAW_STATUS 0x00D00024 +#define CS35L36_INT3_RAW_STATUS 0x00D00028 +#define CS35L36_INT4_RAW_STATUS 0x00D0002C +#define CS35L36_INT1_MASK 0x00D00040 +#define CS35L36_INT2_MASK 0x00D00044 +#define CS35L36_INT3_MASK 0x00D00048 +#define CS35L36_INT4_MASK 0x00D0004C +#define CS35L36_INT1_EDGE_LVL_CTRL 0x00D00060 +#define CS35L36_INT3_EDGE_LVL_CTRL 0x00D00068 +#define CS35L36_PAC_INT_STATUS 0x00D00200 +#define CS35L36_PAC_INT_RAW_STATUS 0x00D00210 +#define CS35L36_PAC_INT_FLUSH_CTRL 0x00D00218 +#define CS35L36_PAC_INT0_CTRL 0x00D00220 +#define CS35L36_PAC_INT1_CTRL 0x00D00224 +#define CS35L36_PAC_INT2_CTRL 0x00D00228 +#define CS35L36_PAC_INT3_CTRL 0x00D0022C +#define CS35L36_PAC_INT4_CTRL 0x00D00230 +#define CS35L36_PAC_INT5_CTRL 0x00D00234 +#define CS35L36_PAC_INT6_CTRL 0x00D00238 +#define CS35L36_PAC_INT7_CTRL 0x00D0023C +#define CS35L36_PAC_PMEM_WORD0 0x00E02800 +#define CS35L36_PAC_PMEM_WORD1 0x00E02804 +#define CS35L36_PAC_PMEM_WORD1023 0x00E037FC + +#define CS35L36_INTPAC_REG_COUNT 25 +#define CS35L36_CHIP_ID 0x00035A36 + +#define CS35L36_INT_OUTPUT_EN_MASK 0x01 +#define CS35L36_INT_GPIO_SEL_MASK 0x02 +#define CS35L36_INT_GPIO_SEL_SHIFT 1 +#define CS35L36_INT_POL_SEL_MASK 0x04 +#define CS35L36_INT_POL_SEL_SHIFT 2 +#define CS35L36_INT_DRV_SEL_MASK 0x20 +#define CS35L36_INT_DRV_SEL_SHIFT 5 +#define CS35L36_IRQ_SRC_MASK 0x08 +#define CS35L36_IRQ_SRC_SHIFT 3 + +#define CS35L36_SCLK_MSTR_MASK 0x40 +#define CS35L36_SCLK_MSTR_SHIFT 6 +#define CS35L36_LRCLK_MSTR_MASK 0x01 +#define CS35L36_LRCLK_MSTR_SHIFT 0 +#define CS35L36_SCLK_INV_MASK 0x100 +#define CS35L36_SCLK_INV_SHIFT 8 +#define CS35L36_LRCLK_INV_MASK 0x04 +#define CS35L36_LRCLK_INV_SHIFT 2 +#define CS35L36_SCLK_FRC_MASK 0x80 +#define CS35L36_SCLK_FRC_SHIFT 7 +#define CS35L36_LRCLK_FRC_MASK 0x02 +#define CS35L36_LRCLK_FRC_SHIFT 1 + +#define CS35L36_PDM_MODE_MASK 0x01 +#define CS35L36_PDM_MODE_SHIFT 0 + +#define CS35L36_ASP_FMT_MASK 0x07 +#define CS35L36_ASP_FMT_SHIFT 0 + +#define CS35L36_ASP_RX_WIDTH_MASK 0xFF0000 +#define CS35L36_ASP_RX_WIDTH_SHIFT 16 +#define CS35L36_ASP_TX_WIDTH_MASK 0xFF +#define CS35L36_ASP_TX_WIDTH_SHIFT 0 +#define CS35L36_ASP_WIDTH_16 0x10 +#define CS35L36_ASP_WIDTH_24 0x18 +#define CS35L36_ASP_WIDTH_32 0x20 + +#define CS35L36_ASP_RX1_SLOT_MASK 0x3F +#define CS35L36_ASP_RX1_EN_MASK 0x00010000 +#define CS35L36_ASP_RX1_EN_SHIFT 16 + +#define CS35L36_ASP_TX1_SLOT_MASK 0x3F +#define CS35L36_ASP_TX2_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX2_SLOT_SHIFT 16 +#define CS35L36_ASP_TX3_SLOT_MASK 0x3F +#define CS35L36_ASP_TX4_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX4_SLOT_SHIFT 16 +#define CS35L36_ASP_TX5_SLOT_MASK 0x3F +#define CS35L36_ASP_TX6_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX6_SLOT_SHIFT 16 +#define CS35L36_ASP_TX7_SLOT_MASK 0x3F +#define CS35L36_ASP_TX8_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX8_SLOT_SHIFT 16 +#define CS35L36_ASP_TX_HIZ_MASK 0x200000 + +#define CS35L36_APS_TX_SEL_MASK 0x7F + +#define CS35L36_ASP_TX1_EN_MASK 0x01 +#define CS35L36_ASP_TX2_EN_MASK 0x02 +#define CS35L36_ASP_TX2_EN_SHIFT 1 +#define CS35L36_ASP_TX3_EN_MASK 0x04 +#define CS35L36_ASP_TX3_EN_SHIFT 2 +#define CS35L36_ASP_TX4_EN_MASK 0x08 +#define CS35L36_ASP_TX4_EN_SHIFT 3 +#define CS35L36_ASP_TX5_EN_MASK 0x10 +#define CS35L36_ASP_TX5_EN_SHIFT 4 +#define CS35L36_ASP_TX6_EN_MASK 0x20 +#define CS35L36_ASP_TX6_EN_SHIFT 5 +#define CS35L36_ASP_TX7_EN_MASK 0x40 +#define CS35L36_ASP_TX7_EN_SHIFT 6 +#define CS35L36_ASP_TX8_EN_MASK 0x80 +#define CS35L36_ASP_TX8_EN_SHIFT 7 + + +#define CS35L36_PLL_CLK_SEL_MASK 0x07 +#define CS35L36_PLL_CLK_SEL_SHIFT 0 +#define CS35L36_PLLSRC_SCLK 0 +#define CS35L36_PLLSRC_LRCLK 1 +#define CS35L36_PLLSRC_SELF 3 +#define CS35L36_PLLSRC_PDMCLK 4 +#define CS35L36_PLLSRC_MCLK 5 +#define CS35L36_PLLSRC_SWIRE 7 +#define CS35L36_REFCLK_FREQ_MASK 0x7E0 +#define CS35L36_REFCLK_FREQ_SHIFT 5 +#define CS35L36_PLL_OPENLOOP_MASK 0x800 +#define CS35L36_PLL_OPENLOOP_SHIFT 11 +#define CS35L36_PLL_REFCLK_EN_MASK 0x10 +#define CS35L36_PLL_REFCLK_EN_SHIFT 4 + + +#define CS35L36_GLOBAL_FS_MASK 0x1F +#define CS35L36_GLOBAL_FS_SHIFT 0 + +#define CS35L36_HPF_PCM_EN_MASK 0x800 +#define CS35L36_HPF_PCM_EN_SHIFT 15 +#define CS35L36_PCM_RX_SEL_MASK 0x7F +#define CS35L36_PCM_RX_SEL_SHIFT 0 + +#define CS35L36_PCM_RX_SEL_ZERO 0x00 +#define CS35L36_PCM_RX_SEL_PCM 0x08 +#define CS35L36_PCM_RX_SEL_SWIRE 0x10 +#define CS35L36_PCM_RX_SEL_DIAG 0x04 + +#define CS35L36_GLOBAL_EN_MASK 0x01 +#define CS35L36_GLOBAL_EN_SHIFT 0x00 + +#define CS35L36_AMP_PCM_INV_MASK 0x4000 +#define CS35L36_AMP_PCM_INV_SHIFT 14 + +#define CS35L36_AMP_VOL_PCM_MASK 0x3FF8 +#define CS35L36_AMP_VOL_PCM_SHIFT 3 +#define CS35L36_DIGITAL_MUTE 0x04CF + +#define CS35L36_AMP_RAMP_MASK 0x0007 +#define CS35L36_AMP_RAMP_SHIFT 0 + +#define CS35L36_AMP_MUTE_MASK 0x0010 +#define CS35L36_AMP_MUTE_SHIFT 4 + +#define CS35L36_GLOBAL_RESYNC_FS1_MASK 0x00000200 +#define CS35L36_GLOBAL_RESYNC_FS2_MASK 0x00000400 +#define CS35L36_SYNC_GLOBAL_OVR_MASK 0x00000002 +#define CS35L36_SYNC_GLOBAL_OVR_SHIFT 1 + +#define CS35L36_REFCLK_IN_MASK 0x00100000 +#define CS35L36_PLL_UNLOCK_MASK 0x00002000 + +#define CS35L36_ASP_RX_UDF_MASK 0x00000040 +#define CS35L36_ASP_RX_OVF_MASK 0x00000080 + +#define CS35L36_IMON_POL_MASK 0x02 +#define CS35L36_IMON_POL_SHIFT 1 + +#define CS35L36_VMON_POL_MASK 0x01 +#define CS35L36_VMON_POL_SHIFT 0 + +#define CS35L36_PDN_DONE 0x40 +#define CS35L36_PDN_DONE_SHIFT 6 +#define CS35L36_PUP_DONE 0x80 +#define CS35L36_PUP_DONE_SHIFT 7 +#define CS35L36_GLOBAL_EN_ASSRT 0x20 +#define CS35L36_PUP_DONE_IRQ_UNMASK 0x7F +#define CS35L36_PUP_DONE_IRQ_MASK 0xBF + +#define CS35L36_FS1_WINDOW_MASK 0x000007FF +#define CS35L36_FS2_WINDOW_MASK 0x00FFF800 +#define CS35L36_FS2_WINDOW_SHIFT 12 + +#define CS35L36_PLL_FFL_IGAIN_MASK 0x0F +#define CS35L36_PLL_IGAIN_MASK 0x3F0 +#define CS35L36_PLL_IGAIN_SHIFT 4 +#define CS35L36_PLL_IGAIN 0x04 + +#define CS35L36_BST_EN_MASK 0x30 +#define CS35L36_BST_EN 0x02 +#define CS35L36_BST_DIS_VP 0x01 +#define CS35L36_BST_DIS_EXTN 0x00 +#define CS35L36_BST_EN_SHIFT 4 +#define CS35L36_BST_MAN_IPKCOMP_MASK 0x200 +#define CS35L36_BST_MAN_IPKCOMP_SHIFT 9 + +#define CS35L36_BST_MAN_IPKCOMP_EN_MASK 0x100 +#define CS35L36_BST_MAN_IPKCOMP_EN_SHIFT 8 + +#define CS35L36_BST_IPK_MASK 0x7F +#define CS35L36_BST_OVP_THLD_MASK 0x3F +#define CS35L36_BST_OVP_THLD_11V 0x10 +#define CS35L36_BST_OVP_TRIM_MASK 0x00078000 +#define CS35L36_BST_OVP_TRIM_SHIFT 15 +#define CS35L36_BST_OVP_TRIM_11V 0x0C +#define CS35L36_BST_CTRL_LIM_MASK 0x04 +#define CS35L36_BST_CTRL_LIM_SHIFT 2 +#define CS35L36_BST_CTRL_10V_CLAMP 0x96 + +#define CS35L36_NG_AMP_EN_MASK 0x3F00 +#define CS35L36_NG_DELAY_MASK 0x70 +#define CS35L36_NG_DELAY_SHIFT 4 +#define CS35L36_AMP_ZC_SHIFT 10 +#define CS35L36_PDM_LDM_ENTER_SHIFT 3 +#define CS35L36_PDM_LDM_EXIT_SHIFT 4 + +#define CS35L36_BSTCVRT_K1_MASK 0xFF +#define CS35L36_BSTCVRT_K2_MASK 0xFF00 +#define CS35L36_BSTCVRT_K2_SHIFT 8 +#define CS35L36_BSTCVRT_SLOPE_MASK 0xFF00 +#define CS35L36_BSTCVRT_SLOPE_SHIFT 8 +#define CS35L36_BSTCVRT_CCMFREQ_MASK 0x0F +#define CS35L36_BSTCVRT_LBSTVAL_MASK 0x03 +#define CS35L35_BSTCVRT_CTL_MASK 0xFF +#define CS35L35_BSTCVRT_CTL_SEL_MASK 0x03 +#define CS35L36_DCM_AUTO_MASK 0x01 + +#define CS35L36_INT1_MASK_DEFAULT 0xF9BA7FFF +#define CS35L36_INT1_MASK_RESET 0xFFFFFFFF +#define CS35L36_INT3_MASK_DEFAULT 0xFFFFEFFF +#define CS35L36_INT3_MASK_RESET 0xFFFFFFFF + + +#define CS35L36_AMP_SHORT_ERR 0x1000 +#define CS35L36_BST_SHORT_ERR 0x40000 +#define CS35L36_TEMP_WARN 0x2000000 +#define CS35L36_TEMP_ERR 0x4000000 +#define CS35L36_BST_OVP_ERR 0x10000 +#define CS35L36_BST_DCM_UVP_ERR 0x20000 + +#define CS35L36_AMP_SHORT_ERR_RLS 0x02 +#define CS35L36_BST_SHORT_ERR_RLS 0x04 +#define CS35L36_BST_OVP_ERR_RLS 0x08 +#define CS35L36_BST_UVP_ERR_RLS 0x10 +#define CS35L36_TEMP_WARN_ERR_RLS 0x20 +#define CS35L36_TEMP_ERR_RLS 0x40 +#define CS35L36_TEMP_THLD_MASK 0x03 + +#define CS35L36_REV_B0 0xb0 +#define CS35L36_REV_A0 0xa0 +#define CS35L36_B0_PAC_PATCH 0x00DD0102 + +#define CS35L36_OTP_ECC_EN_MASK 0x400 +#define CS35L36_OTP_ECC_EN_SHIFT 10 +#define CS35L36_OTP_RUN_BOOT_MASK 0x01 +#define CS35L36_OTP_BOOT_DONE 0x2000000 +#define CS35L36_PAC_RESET_MASK 0x04 +#define CS35L36_PAC_RESET_SHIFT 2 +#define CS35L36_PAC_STALL_MASK 0x02 +#define CS35L36_PAC_STALL_SHIFT 1 +#define CS35L36_PAC_ENABLE_MASK 0x00000001 +#define CS35L36_PAC_MEM_ACCESS 0x01 +#define CS35L36_PAC_MEM_ACCESS_CLR 0 +#define CS35L36_SOFT_RESET 0x5AAA +#define CS35L36_MCU_BOOT_COMPLETE 0x02 +#define CS35L36_MCU_CONFIG_UNMASK 0x00FEFFFF +#define CS35L36_MCU_CONFIG_CLR 0x00010000 +#define CS35L36_MCU_CONFIG_MASK 0x00FFFFFF +#define CS35L36_GPIO_INT_SEL_MASK 0x0000003B +#define CS35L36_GPIO_INT_SEL_UNMASK 0x0000003A +#define CS35L36_PAC_RESET 0x00000000 +#define CS35L36_OTP_REV_MASK 0x00FF0000 +#define CS35L36_OTP_REV_L37 0x00CC0000 +#define CS35L36_12V_L37 37 +#define CS35L36_10V_L36 36 + +#define CS35L36_VPBR_EN_MASK 0x00001000 +#define CS35L36_VPBR_EN_SHIFT 12 + +#define CS35L36_VPBR_THLD_MASK 0x0000001F +#define CS35L36_VPBR_THLD_SHIFT 0 +#define CS35L36_VPBR_MAX_ATTN_MASK 0x00000F00 +#define CS35L36_VPBR_MAX_ATTN_SHIFT 8 +#define CS35L36_VPBR_ATK_VOL_MASK 0x0000F000 +#define CS35L36_VPBR_ATK_VOL_SHIFT 12 +#define CS35L36_VPBR_ATK_RATE_MASK 0x00070000 +#define CS35L36_VPBR_ATK_RATE_SHIFT 16 +#define CS35L36_VPBR_WAIT_MASK 0x00180000 +#define CS35L36_VPBR_WAIT_SHIFT 19 +#define CS35L36_VPBR_REL_RATE_MASK 0x00E00000 +#define CS35L36_VPBR_REL_RATE_SHIFT 21 +#define CS35L36_VPBR_MUTE_EN_MASK 0x01000000 +#define CS35L36_VPBR_MUTE_EN_SHIFT 24 + +#define CS35L36_OSC_FREQ_TRIM_MASK 0x070 +#define CS35L36_OSC_TRIM_DONE 0x08 + +#define CS35L36_FS1_DEFAULT_VAL 16 +#define CS35L36_FS2_DEFAULT_VAL 36 +#define CS35L36_FS_NOM_6MHZ 6000000 + +#define CS35L36_TEST_UNLOCK1 0x00005555 +#define CS35L36_TEST_UNLOCK2 0x0000AAAA +#define CS35L36_TEST_LOCK1 0x0000CCCC +#define CS35L36_TEST_LOCK2 0x00003333 + +#define CS35L36_PAC_PROG_MEM 512 + +#define CS35L36_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#define CS35L36_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +extern const int cs35l36_a0_pac_patch[CS35L36_PAC_PROG_MEM]; + +#endif diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c new file mode 100644 index 0000000000..7ea890d7d3 --- /dev/null +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41-i2c.c -- CS35l41 I2C driver +// +// Copyright 2017-2021 Cirrus Logic, Inc. +// +// Author: David Rhodes + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l41.h" + +static const struct i2c_device_id cs35l41_id_i2c[] = { + { "cs35l40", 0 }, + { "cs35l41", 0 }, + { "cs35l51", 0 }, + { "cs35l53", 0 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c); + +static int cs35l41_i2c_probe(struct i2c_client *client) +{ + struct cs35l41_private *cs35l41; + struct device *dev = &client->dev; + struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(dev); + const struct regmap_config *regmap_config = &cs35l41_regmap_i2c; + int ret; + + cs35l41 = devm_kzalloc(dev, sizeof(struct cs35l41_private), GFP_KERNEL); + + if (!cs35l41) + return -ENOMEM; + + cs35l41->dev = dev; + cs35l41->irq = client->irq; + + i2c_set_clientdata(client, cs35l41); + cs35l41->regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(cs35l41->regmap)) { + ret = PTR_ERR(cs35l41->regmap); + dev_err(cs35l41->dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + return cs35l41_probe(cs35l41, hw_cfg); +} + +static void cs35l41_i2c_remove(struct i2c_client *client) +{ + struct cs35l41_private *cs35l41 = i2c_get_clientdata(client); + + cs35l41_remove(cs35l41); +} + +#ifdef CONFIG_OF +static const struct of_device_id cs35l41_of_match[] = { + { .compatible = "cirrus,cs35l40" }, + { .compatible = "cirrus,cs35l41" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l41_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id cs35l41_acpi_match[] = { + { "CSC3541", 0 }, /* Cirrus Logic PnP ID + part ID */ + {}, +}; +MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match); +#endif + +static struct i2c_driver cs35l41_i2c_driver = { + .driver = { + .name = "cs35l41", + .pm = &cs35l41_pm_ops, + .of_match_table = of_match_ptr(cs35l41_of_match), + .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), + }, + .id_table = cs35l41_id_i2c, + .probe = cs35l41_i2c_probe, + .remove = cs35l41_i2c_remove, +}; + +module_i2c_driver(cs35l41_i2c_driver); + +MODULE_DESCRIPTION("I2C CS35L41 driver"); +MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c new file mode 100644 index 0000000000..2ec5fdc875 --- /dev/null +++ b/sound/soc/codecs/cs35l41-lib.c @@ -0,0 +1,1584 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41-lib.c -- CS35L41 Common functions for HDA and ASoC Audio drivers +// +// Copyright 2017-2021 Cirrus Logic, Inc. +// +// Author: David Rhodes +// Author: Lucas Tanure + +#include +#include +#include +#include +#include +#include + +#include + +static const struct reg_default cs35l41_reg[] = { + { CS35L41_PWR_CTRL1, 0x00000000 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_PWR_CTRL3, 0x01000010 }, + { CS35L41_GPIO_PAD_CONTROL, 0x00000000 }, + { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, + { CS35L41_TST_FS_MON0, 0x00020016 }, + { CS35L41_BSTCVRT_COEFF, 0x00002424 }, + { CS35L41_BSTCVRT_SLOPE_LBST, 0x00007500 }, + { CS35L41_BSTCVRT_PEAK_CUR, 0x0000004A }, + { CS35L41_SP_ENABLES, 0x00000000 }, + { CS35L41_SP_RATE_CTRL, 0x00000028 }, + { CS35L41_SP_FORMAT, 0x18180200 }, + { CS35L41_SP_HIZ_CTRL, 0x00000002 }, + { CS35L41_SP_FRAME_TX_SLOT, 0x03020100 }, + { CS35L41_SP_FRAME_RX_SLOT, 0x00000100 }, + { CS35L41_SP_TX_WL, 0x00000018 }, + { CS35L41_SP_RX_WL, 0x00000018 }, + { CS35L41_DAC_PCM1_SRC, 0x00000008 }, + { CS35L41_ASP_TX1_SRC, 0x00000018 }, + { CS35L41_ASP_TX2_SRC, 0x00000019 }, + { CS35L41_ASP_TX3_SRC, 0x00000000 }, + { CS35L41_ASP_TX4_SRC, 0x00000000 }, + { CS35L41_DSP1_RX1_SRC, 0x00000008 }, + { CS35L41_DSP1_RX2_SRC, 0x00000009 }, + { CS35L41_DSP1_RX3_SRC, 0x00000018 }, + { CS35L41_DSP1_RX4_SRC, 0x00000019 }, + { CS35L41_DSP1_RX5_SRC, 0x00000020 }, + { CS35L41_DSP1_RX6_SRC, 0x00000021 }, + { CS35L41_DSP1_RX7_SRC, 0x0000003A }, + { CS35L41_DSP1_RX8_SRC, 0x0000003B }, + { CS35L41_NGATE1_SRC, 0x00000008 }, + { CS35L41_NGATE2_SRC, 0x00000009 }, + { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, + { CS35L41_CLASSH_CFG, 0x000B0405 }, + { CS35L41_WKFET_CFG, 0x00000111 }, + { CS35L41_NG_CFG, 0x00000033 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, + { CS35L41_IRQ1_MASK1, 0xFFFFFFFF }, + { CS35L41_IRQ1_MASK2, 0xFFFFFFFF }, + { CS35L41_IRQ1_MASK3, 0xFFFF87FF }, + { CS35L41_IRQ1_MASK4, 0xFEFFFFFF }, + { CS35L41_GPIO1_CTRL1, 0x81000001 }, + { CS35L41_GPIO2_CTRL1, 0x81000001 }, + { CS35L41_MIXER_NGATE_CFG, 0x00000000 }, + { CS35L41_MIXER_NGATE_CH1_CFG, 0x00000303 }, + { CS35L41_MIXER_NGATE_CH2_CFG, 0x00000303 }, + { CS35L41_DSP1_CCM_CORE_CTRL, 0x00000101 }, +}; + +static bool cs35l41_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L41_DEVID: + case CS35L41_REVID: + case CS35L41_FABID: + case CS35L41_RELID: + case CS35L41_OTPID: + case CS35L41_TEST_KEY_CTL: + case CS35L41_USER_KEY_CTL: + case CS35L41_OTP_CTRL0: + case CS35L41_OTP_CTRL3: + case CS35L41_OTP_CTRL4: + case CS35L41_OTP_CTRL5: + case CS35L41_OTP_CTRL6: + case CS35L41_OTP_CTRL7: + case CS35L41_OTP_CTRL8: + case CS35L41_PWR_CTRL1: + case CS35L41_PWR_CTRL2: + case CS35L41_PWR_CTRL3: + case CS35L41_CTRL_OVRRIDE: + case CS35L41_AMP_OUT_MUTE: + case CS35L41_PROTECT_REL_ERR_IGN: + case CS35L41_GPIO_PAD_CONTROL: + case CS35L41_JTAG_CONTROL: + case CS35L41_PWRMGT_CTL: + case CS35L41_WAKESRC_CTL: + case CS35L41_PWRMGT_STS: + case CS35L41_PLL_CLK_CTRL: + case CS35L41_DSP_CLK_CTRL: + case CS35L41_GLOBAL_CLK_CTRL: + case CS35L41_DATA_FS_SEL: + case CS35L41_TST_FS_MON0: + case CS35L41_MDSYNC_EN: + case CS35L41_MDSYNC_TX_ID: + case CS35L41_MDSYNC_PWR_CTRL: + case CS35L41_MDSYNC_DATA_TX: + case CS35L41_MDSYNC_TX_STATUS: + case CS35L41_MDSYNC_DATA_RX: + case CS35L41_MDSYNC_RX_STATUS: + case CS35L41_MDSYNC_ERR_STATUS: + case CS35L41_MDSYNC_SYNC_PTE2: + case CS35L41_MDSYNC_SYNC_PTE3: + case CS35L41_MDSYNC_SYNC_MSM_STATUS: + case CS35L41_BSTCVRT_VCTRL1: + case CS35L41_BSTCVRT_VCTRL2: + case CS35L41_BSTCVRT_PEAK_CUR: + case CS35L41_BSTCVRT_SFT_RAMP: + case CS35L41_BSTCVRT_COEFF: + case CS35L41_BSTCVRT_SLOPE_LBST: + case CS35L41_BSTCVRT_SW_FREQ: + case CS35L41_BSTCVRT_DCM_CTRL: + case CS35L41_BSTCVRT_DCM_MODE_FORCE: + case CS35L41_BSTCVRT_OVERVOLT_CTRL: + case CS35L41_VI_VOL_POL: + case CS35L41_DTEMP_WARN_THLD: + case CS35L41_DTEMP_CFG: + case CS35L41_DTEMP_EN: + case CS35L41_VPVBST_FS_SEL: + case CS35L41_SP_ENABLES: + case CS35L41_SP_RATE_CTRL: + case CS35L41_SP_FORMAT: + case CS35L41_SP_HIZ_CTRL: + case CS35L41_SP_FRAME_TX_SLOT: + case CS35L41_SP_FRAME_RX_SLOT: + case CS35L41_SP_TX_WL: + case CS35L41_SP_RX_WL: + case CS35L41_DAC_PCM1_SRC: + case CS35L41_ASP_TX1_SRC: + case CS35L41_ASP_TX2_SRC: + case CS35L41_ASP_TX3_SRC: + case CS35L41_ASP_TX4_SRC: + case CS35L41_DSP1_RX1_SRC: + case CS35L41_DSP1_RX2_SRC: + case CS35L41_DSP1_RX3_SRC: + case CS35L41_DSP1_RX4_SRC: + case CS35L41_DSP1_RX5_SRC: + case CS35L41_DSP1_RX6_SRC: + case CS35L41_DSP1_RX7_SRC: + case CS35L41_DSP1_RX8_SRC: + case CS35L41_NGATE1_SRC: + case CS35L41_NGATE2_SRC: + case CS35L41_AMP_DIG_VOL_CTRL: + case CS35L41_VPBR_CFG: + case CS35L41_VBBR_CFG: + case CS35L41_VPBR_STATUS: + case CS35L41_VBBR_STATUS: + case CS35L41_OVERTEMP_CFG: + case CS35L41_AMP_ERR_VOL: + case CS35L41_VOL_STATUS_TO_DSP: + case CS35L41_CLASSH_CFG: + case CS35L41_WKFET_CFG: + case CS35L41_NG_CFG: + case CS35L41_AMP_GAIN_CTRL: + case CS35L41_DAC_MSM_CFG: + case CS35L41_IRQ1_CFG: + case CS35L41_IRQ1_STATUS: + case CS35L41_IRQ1_STATUS1: + case CS35L41_IRQ1_STATUS2: + case CS35L41_IRQ1_STATUS3: + case CS35L41_IRQ1_STATUS4: + case CS35L41_IRQ1_RAW_STATUS1: + case CS35L41_IRQ1_RAW_STATUS2: + case CS35L41_IRQ1_RAW_STATUS3: + case CS35L41_IRQ1_RAW_STATUS4: + case CS35L41_IRQ1_MASK1: + case CS35L41_IRQ1_MASK2: + case CS35L41_IRQ1_MASK3: + case CS35L41_IRQ1_MASK4: + case CS35L41_IRQ1_FRC1: + case CS35L41_IRQ1_FRC2: + case CS35L41_IRQ1_FRC3: + case CS35L41_IRQ1_FRC4: + case CS35L41_IRQ1_EDGE1: + case CS35L41_IRQ1_EDGE4: + case CS35L41_IRQ1_POL1: + case CS35L41_IRQ1_POL2: + case CS35L41_IRQ1_POL3: + case CS35L41_IRQ1_POL4: + case CS35L41_IRQ1_DB3: + case CS35L41_IRQ2_CFG: + case CS35L41_IRQ2_STATUS: + case CS35L41_IRQ2_STATUS1: + case CS35L41_IRQ2_STATUS2: + case CS35L41_IRQ2_STATUS3: + case CS35L41_IRQ2_STATUS4: + case CS35L41_IRQ2_RAW_STATUS1: + case CS35L41_IRQ2_RAW_STATUS2: + case CS35L41_IRQ2_RAW_STATUS3: + case CS35L41_IRQ2_RAW_STATUS4: + case CS35L41_IRQ2_MASK1: + case CS35L41_IRQ2_MASK2: + case CS35L41_IRQ2_MASK3: + case CS35L41_IRQ2_MASK4: + case CS35L41_IRQ2_FRC1: + case CS35L41_IRQ2_FRC2: + case CS35L41_IRQ2_FRC3: + case CS35L41_IRQ2_FRC4: + case CS35L41_IRQ2_EDGE1: + case CS35L41_IRQ2_EDGE4: + case CS35L41_IRQ2_POL1: + case CS35L41_IRQ2_POL2: + case CS35L41_IRQ2_POL3: + case CS35L41_IRQ2_POL4: + case CS35L41_IRQ2_DB3: + case CS35L41_GPIO_STATUS1: + case CS35L41_GPIO1_CTRL1: + case CS35L41_GPIO2_CTRL1: + case CS35L41_MIXER_NGATE_CFG: + case CS35L41_MIXER_NGATE_CH1_CFG: + case CS35L41_MIXER_NGATE_CH2_CFG: + case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8: + case CS35L41_CLOCK_DETECT_1: + case CS35L41_DIE_STS1: + case CS35L41_DIE_STS2: + case CS35L41_TEMP_CAL1: + case CS35L41_TEMP_CAL2: + case CS35L41_DSP1_TIMESTAMP_COUNT: + case CS35L41_DSP1_SYS_ID: + case CS35L41_DSP1_SYS_VERSION: + case CS35L41_DSP1_SYS_CORE_ID: + case CS35L41_DSP1_SYS_AHB_ADDR: + case CS35L41_DSP1_SYS_XSRAM_SIZE: + case CS35L41_DSP1_SYS_YSRAM_SIZE: + case CS35L41_DSP1_SYS_PSRAM_SIZE: + case CS35L41_DSP1_SYS_PM_BOOT_SIZE: + case CS35L41_DSP1_SYS_FEATURES: + case CS35L41_DSP1_SYS_FIR_FILTERS: + case CS35L41_DSP1_SYS_LMS_FILTERS: + case CS35L41_DSP1_SYS_XM_BANK_SIZE: + case CS35L41_DSP1_SYS_YM_BANK_SIZE: + case CS35L41_DSP1_SYS_PM_BANK_SIZE: + case CS35L41_DSP1_RX1_RATE: + case CS35L41_DSP1_RX2_RATE: + case CS35L41_DSP1_RX3_RATE: + case CS35L41_DSP1_RX4_RATE: + case CS35L41_DSP1_RX5_RATE: + case CS35L41_DSP1_RX6_RATE: + case CS35L41_DSP1_RX7_RATE: + case CS35L41_DSP1_RX8_RATE: + case CS35L41_DSP1_TX1_RATE: + case CS35L41_DSP1_TX2_RATE: + case CS35L41_DSP1_TX3_RATE: + case CS35L41_DSP1_TX4_RATE: + case CS35L41_DSP1_TX5_RATE: + case CS35L41_DSP1_TX6_RATE: + case CS35L41_DSP1_TX7_RATE: + case CS35L41_DSP1_TX8_RATE: + case CS35L41_DSP1_SCRATCH1: + case CS35L41_DSP1_SCRATCH2: + case CS35L41_DSP1_SCRATCH3: + case CS35L41_DSP1_SCRATCH4: + case CS35L41_DSP1_CCM_CORE_CTRL: + case CS35L41_DSP1_CCM_CLK_OVERRIDE: + case CS35L41_DSP1_XM_MSTR_EN: + case CS35L41_DSP1_XM_CORE_PRI: + case CS35L41_DSP1_XM_AHB_PACK_PL_PRI: + case CS35L41_DSP1_XM_AHB_UP_PL_PRI: + case CS35L41_DSP1_XM_ACCEL_PL0_PRI: + case CS35L41_DSP1_XM_NPL0_PRI: + case CS35L41_DSP1_YM_MSTR_EN: + case CS35L41_DSP1_YM_CORE_PRI: + case CS35L41_DSP1_YM_AHB_PACK_PL_PRI: + case CS35L41_DSP1_YM_AHB_UP_PL_PRI: + case CS35L41_DSP1_YM_ACCEL_PL0_PRI: + case CS35L41_DSP1_YM_NPL0_PRI: + case CS35L41_DSP1_MPU_XM_ACCESS0: + case CS35L41_DSP1_MPU_YM_ACCESS0: + case CS35L41_DSP1_MPU_WNDW_ACCESS0: + case CS35L41_DSP1_MPU_XREG_ACCESS0: + case CS35L41_DSP1_MPU_YREG_ACCESS0: + case CS35L41_DSP1_MPU_XM_ACCESS1: + case CS35L41_DSP1_MPU_YM_ACCESS1: + case CS35L41_DSP1_MPU_WNDW_ACCESS1: + case CS35L41_DSP1_MPU_XREG_ACCESS1: + case CS35L41_DSP1_MPU_YREG_ACCESS1: + case CS35L41_DSP1_MPU_XM_ACCESS2: + case CS35L41_DSP1_MPU_YM_ACCESS2: + case CS35L41_DSP1_MPU_WNDW_ACCESS2: + case CS35L41_DSP1_MPU_XREG_ACCESS2: + case CS35L41_DSP1_MPU_YREG_ACCESS2: + case CS35L41_DSP1_MPU_XM_ACCESS3: + case CS35L41_DSP1_MPU_YM_ACCESS3: + case CS35L41_DSP1_MPU_WNDW_ACCESS3: + case CS35L41_DSP1_MPU_XREG_ACCESS3: + case CS35L41_DSP1_MPU_YREG_ACCESS3: + case CS35L41_DSP1_MPU_XM_VIO_ADDR: + case CS35L41_DSP1_MPU_XM_VIO_STATUS: + case CS35L41_DSP1_MPU_YM_VIO_ADDR: + case CS35L41_DSP1_MPU_YM_VIO_STATUS: + case CS35L41_DSP1_MPU_PM_VIO_ADDR: + case CS35L41_DSP1_MPU_PM_VIO_STATUS: + case CS35L41_DSP1_MPU_LOCK_CONFIG: + case CS35L41_DSP1_MPU_WDT_RST_CTRL: + case CS35L41_OTP_TRIM_1: + case CS35L41_OTP_TRIM_2: + case CS35L41_OTP_TRIM_3: + case CS35L41_OTP_TRIM_4: + case CS35L41_OTP_TRIM_5: + case CS35L41_OTP_TRIM_6: + case CS35L41_OTP_TRIM_7: + case CS35L41_OTP_TRIM_8: + case CS35L41_OTP_TRIM_9: + case CS35L41_OTP_TRIM_10: + case CS35L41_OTP_TRIM_11: + case CS35L41_OTP_TRIM_12: + case CS35L41_OTP_TRIM_13: + case CS35L41_OTP_TRIM_14: + case CS35L41_OTP_TRIM_15: + case CS35L41_OTP_TRIM_16: + case CS35L41_OTP_TRIM_17: + case CS35L41_OTP_TRIM_18: + case CS35L41_OTP_TRIM_19: + case CS35L41_OTP_TRIM_20: + case CS35L41_OTP_TRIM_21: + case CS35L41_OTP_TRIM_22: + case CS35L41_OTP_TRIM_23: + case CS35L41_OTP_TRIM_24: + case CS35L41_OTP_TRIM_25: + case CS35L41_OTP_TRIM_26: + case CS35L41_OTP_TRIM_27: + case CS35L41_OTP_TRIM_28: + case CS35L41_OTP_TRIM_29: + case CS35L41_OTP_TRIM_30: + case CS35L41_OTP_TRIM_31: + case CS35L41_OTP_TRIM_32: + case CS35L41_OTP_TRIM_33: + case CS35L41_OTP_TRIM_34: + case CS35L41_OTP_TRIM_35: + case CS35L41_OTP_TRIM_36: + case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: + case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: + case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: + /*test regs*/ + case CS35L41_PLL_OVR: + case CS35L41_BST_TEST_DUTY: + case CS35L41_DIGPWM_IOCTRL: + return true; + default: + return false; + } +} + +static bool cs35l41_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L41_TEST_KEY_CTL: + case CS35L41_USER_KEY_CTL: + case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + case CS35L41_TST_FS_MON0: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: + return true; + default: + return false; + } +} + +static bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L41_DEVID: + case CS35L41_SFT_RESET: + case CS35L41_FABID: + case CS35L41_REVID: + case CS35L41_OTPID: + case CS35L41_TEST_KEY_CTL: + case CS35L41_USER_KEY_CTL: + case CS35L41_PWRMGT_CTL: + case CS35L41_WAKESRC_CTL: + case CS35L41_PWRMGT_STS: + case CS35L41_DTEMP_EN: + case CS35L41_IRQ1_STATUS: + case CS35L41_IRQ1_STATUS1: + case CS35L41_IRQ1_STATUS2: + case CS35L41_IRQ1_STATUS3: + case CS35L41_IRQ1_STATUS4: + case CS35L41_IRQ1_RAW_STATUS1: + case CS35L41_IRQ1_RAW_STATUS2: + case CS35L41_IRQ1_RAW_STATUS3: + case CS35L41_IRQ1_RAW_STATUS4: + case CS35L41_IRQ2_STATUS: + case CS35L41_IRQ2_STATUS1: + case CS35L41_IRQ2_STATUS2: + case CS35L41_IRQ2_STATUS3: + case CS35L41_IRQ2_STATUS4: + case CS35L41_IRQ2_RAW_STATUS1: + case CS35L41_IRQ2_RAW_STATUS2: + case CS35L41_IRQ2_RAW_STATUS3: + case CS35L41_IRQ2_RAW_STATUS4: + case CS35L41_GPIO_STATUS1: + case CS35L41_DSP_MBOX_1 ... CS35L41_DSP_VIRT2_MBOX_8: + case CS35L41_DSP1_XMEM_PACK_0 ... CS35L41_DSP1_XMEM_PACK_3068: + case CS35L41_DSP1_XMEM_UNPACK32_0 ... CS35L41_DSP1_XMEM_UNPACK32_2046: + case CS35L41_DSP1_XMEM_UNPACK24_0 ... CS35L41_DSP1_XMEM_UNPACK24_4093: + case CS35L41_DSP1_YMEM_PACK_0 ... CS35L41_DSP1_YMEM_PACK_1532: + case CS35L41_DSP1_YMEM_UNPACK32_0 ... CS35L41_DSP1_YMEM_UNPACK32_1022: + case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: + case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: + case CS35L41_DSP1_SCRATCH1: + case CS35L41_DSP1_SCRATCH2: + case CS35L41_DSP1_SCRATCH3: + case CS35L41_DSP1_SCRATCH4: + case CS35L41_DSP1_CCM_CLK_OVERRIDE ... CS35L41_DSP1_WDT_STATUS: + case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: + return true; + default: + return false; + } +} + +static const struct cs35l41_otp_packed_element_t otp_map_1[] = { + /* addr shift size */ + { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/ + { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/ + { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/ + { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/ + { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/ + { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/ + { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/ + { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/ + { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/ + { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/ + { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/ + { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/ + { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/ + { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/ + { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/ + { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/ + { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/ + { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/ + { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/ + { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/ + { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/ + { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/ + { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/ + { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/ + { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/ + { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/ + { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/ + { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/ + { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/ + { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/ + { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/ + { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/ + { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/ + { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/ + { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/ + { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/ + { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/ + { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/ + { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/ + { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/ + { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/ + { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/ + { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/ + { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/ + { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/ + { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/ + { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/ + { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/ + { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/ + { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/ + { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/ + { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/ + { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/ + { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/ + { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/ + { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/ + { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/ + { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/ + { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/ + { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/ + { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/ + { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/ + { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/ + { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/ + { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/ + { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/ + { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/ + { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/ + { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/ + { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/ + { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/ + { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/ + { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/ + { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/ + { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/ + { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/ + { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/ + { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/ + { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/ + { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/ + { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/ + { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/ + { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/ + { 0x00006E64, 0, 10 }, /*VOFF_INT1*/ + { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/ + { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/ + { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/ + { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/ + { 0x00007434, 17, 1 }, /*FORCE_CAL*/ + { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/ + { 0x00007068, 0, 9 }, /*MODIX*/ + { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/ + { 0x0000400C, 0, 7 }, /*VIMON_DLY*/ + { 0x00000000, 0, 1 }, /*extra bit*/ + { 0x00017040, 0, 8 }, /*X_COORDINATE*/ + { 0x00017040, 8, 8 }, /*Y_COORDINATE*/ + { 0x00017040, 16, 8 }, /*WAFER_ID*/ + { 0x00017040, 24, 8 }, /*DVS*/ + { 0x00017044, 0, 24 }, /*LOT_NUMBER*/ +}; + +static const struct cs35l41_otp_packed_element_t otp_map_2[] = { + /* addr shift size */ + { 0x00002030, 0, 4 }, /*TRIM_OSC_FREQ_TRIM*/ + { 0x00002030, 7, 1 }, /*TRIM_OSC_TRIM_DONE*/ + { 0x0000208c, 24, 6 }, /*TST_DIGREG_VREF_TRIM*/ + { 0x00002090, 14, 4 }, /*TST_REF_TRIM*/ + { 0x00002090, 10, 4 }, /*TST_REF_TEMPCO_TRIM*/ + { 0x0000300C, 11, 4 }, /*PLL_LDOA_TST_VREF_TRIM*/ + { 0x0000394C, 23, 2 }, /*BST_ATEST_CM_VOFF*/ + { 0x00003950, 0, 7 }, /*BST_ATRIM_IADC_OFFSET*/ + { 0x00003950, 8, 7 }, /*BST_ATRIM_IADC_GAIN1*/ + { 0x00003950, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET1*/ + { 0x00003950, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN1*/ + { 0x00003954, 0, 7 }, /*BST_ATRIM_IADC_OFFSET2*/ + { 0x00003954, 8, 7 }, /*BST_ATRIM_IADC_GAIN2*/ + { 0x00003954, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET2*/ + { 0x00003954, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN2*/ + { 0x00003958, 0, 7 }, /*BST_ATRIM_IADC_OFFSET3*/ + { 0x00003958, 8, 7 }, /*BST_ATRIM_IADC_GAIN3*/ + { 0x00003958, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET3*/ + { 0x00003958, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN3*/ + { 0x0000395C, 0, 7 }, /*BST_ATRIM_IADC_OFFSET4*/ + { 0x0000395C, 8, 7 }, /*BST_ATRIM_IADC_GAIN4*/ + { 0x0000395C, 16, 8 }, /*BST_ATRIM_IPKCOMP_OFFSET4*/ + { 0x0000395C, 24, 8 }, /*BST_ATRIM_IPKCOMP_GAIN4*/ + { 0x0000416C, 0, 8 }, /*VMON_GAIN_OTP_VAL*/ + { 0x00004160, 0, 7 }, /*VMON_OFFSET_OTP_VAL*/ + { 0x0000416C, 8, 8 }, /*IMON_GAIN_OTP_VAL*/ + { 0x00004160, 16, 10 }, /*IMON_OFFSET_OTP_VAL*/ + { 0x0000416C, 16, 12 }, /*VMON_CM_GAIN_OTP_VAL*/ + { 0x0000416C, 28, 1 }, /*VMON_CM_GAIN_SIGN_OTP_VAL*/ + { 0x00004170, 0, 6 }, /*IMON_CAL_TEMPCO_OTP_VAL*/ + { 0x00004170, 6, 1 }, /*IMON_CAL_TEMPCO_SIGN_OTP*/ + { 0x00004170, 8, 6 }, /*IMON_CAL_TEMPCO2_OTP_VAL*/ + { 0x00004170, 14, 1 }, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/ + { 0x00004170, 16, 9 }, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/ + { 0x00004360, 0, 5 }, /*TEMP_GAIN_OTP_VAL*/ + { 0x00004360, 6, 9 }, /*TEMP_OFFSET_OTP_VAL*/ + { 0x00004448, 0, 8 }, /*VP_SARADC_OFFSET*/ + { 0x00004448, 8, 8 }, /*VP_GAIN_INDEX*/ + { 0x00004448, 16, 8 }, /*VBST_SARADC_OFFSET*/ + { 0x00004448, 24, 8 }, /*VBST_GAIN_INDEX*/ + { 0x0000444C, 0, 3 }, /*ANA_SELINVREF*/ + { 0x00006E30, 0, 5 }, /*GAIN_ERR_COEFF_0*/ + { 0x00006E30, 8, 5 }, /*GAIN_ERR_COEFF_1*/ + { 0x00006E30, 16, 5 }, /*GAIN_ERR_COEFF_2*/ + { 0x00006E30, 24, 5 }, /*GAIN_ERR_COEFF_3*/ + { 0x00006E34, 0, 5 }, /*GAIN_ERR_COEFF_4*/ + { 0x00006E34, 8, 5 }, /*GAIN_ERR_COEFF_5*/ + { 0x00006E34, 16, 5 }, /*GAIN_ERR_COEFF_6*/ + { 0x00006E34, 24, 5 }, /*GAIN_ERR_COEFF_7*/ + { 0x00006E38, 0, 5 }, /*GAIN_ERR_COEFF_8*/ + { 0x00006E38, 8, 5 }, /*GAIN_ERR_COEFF_9*/ + { 0x00006E38, 16, 5 }, /*GAIN_ERR_COEFF_10*/ + { 0x00006E38, 24, 5 }, /*GAIN_ERR_COEFF_11*/ + { 0x00006E3C, 0, 5 }, /*GAIN_ERR_COEFF_12*/ + { 0x00006E3C, 8, 5 }, /*GAIN_ERR_COEFF_13*/ + { 0x00006E3C, 16, 5 }, /*GAIN_ERR_COEFF_14*/ + { 0x00006E3C, 24, 5 }, /*GAIN_ERR_COEFF_15*/ + { 0x00006E40, 0, 5 }, /*GAIN_ERR_COEFF_16*/ + { 0x00006E40, 8, 5 }, /*GAIN_ERR_COEFF_17*/ + { 0x00006E40, 16, 5 }, /*GAIN_ERR_COEFF_18*/ + { 0x00006E40, 24, 5 }, /*GAIN_ERR_COEFF_19*/ + { 0x00006E44, 0, 5 }, /*GAIN_ERR_COEFF_20*/ + { 0x00006E48, 0, 10 }, /*VOFF_GAIN_0*/ + { 0x00006E48, 10, 10 }, /*VOFF_GAIN_1*/ + { 0x00006E48, 20, 10 }, /*VOFF_GAIN_2*/ + { 0x00006E4C, 0, 10 }, /*VOFF_GAIN_3*/ + { 0x00006E4C, 10, 10 }, /*VOFF_GAIN_4*/ + { 0x00006E4C, 20, 10 }, /*VOFF_GAIN_5*/ + { 0x00006E50, 0, 10 }, /*VOFF_GAIN_6*/ + { 0x00006E50, 10, 10 }, /*VOFF_GAIN_7*/ + { 0x00006E50, 20, 10 }, /*VOFF_GAIN_8*/ + { 0x00006E54, 0, 10 }, /*VOFF_GAIN_9*/ + { 0x00006E54, 10, 10 }, /*VOFF_GAIN_10*/ + { 0x00006E54, 20, 10 }, /*VOFF_GAIN_11*/ + { 0x00006E58, 0, 10 }, /*VOFF_GAIN_12*/ + { 0x00006E58, 10, 10 }, /*VOFF_GAIN_13*/ + { 0x00006E58, 20, 10 }, /*VOFF_GAIN_14*/ + { 0x00006E5C, 0, 10 }, /*VOFF_GAIN_15*/ + { 0x00006E5C, 10, 10 }, /*VOFF_GAIN_16*/ + { 0x00006E5C, 20, 10 }, /*VOFF_GAIN_17*/ + { 0x00006E60, 0, 10 }, /*VOFF_GAIN_18*/ + { 0x00006E60, 10, 10 }, /*VOFF_GAIN_19*/ + { 0x00006E60, 20, 10 }, /*VOFF_GAIN_20*/ + { 0x00006E64, 0, 10 }, /*VOFF_INT1*/ + { 0x00007418, 7, 5 }, /*DS_SPK_INT1_CAP_TRIM*/ + { 0x0000741C, 0, 5 }, /*DS_SPK_INT2_CAP_TRIM*/ + { 0x0000741C, 11, 4 }, /*DS_SPK_LPF_CAP_TRIM*/ + { 0x0000741C, 19, 4 }, /*DS_SPK_QUAN_CAP_TRIM*/ + { 0x00007434, 17, 1 }, /*FORCE_CAL*/ + { 0x00007434, 18, 7 }, /*CAL_OVERRIDE*/ + { 0x00007068, 0, 9 }, /*MODIX*/ + { 0x0000410C, 7, 1 }, /*VIMON_DLY_NOT_COMB*/ + { 0x0000400C, 0, 7 }, /*VIMON_DLY*/ + { 0x00004000, 11, 1 }, /*VMON_POL*/ + { 0x00017040, 0, 8 }, /*X_COORDINATE*/ + { 0x00017040, 8, 8 }, /*Y_COORDINATE*/ + { 0x00017040, 16, 8 }, /*WAFER_ID*/ + { 0x00017040, 24, 8 }, /*DVS*/ + { 0x00017044, 0, 24 }, /*LOT_NUMBER*/ +}; + +static const struct reg_sequence cs35l41_reva0_errata_patch[] = { + { 0x00003854, 0x05180240 }, + { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, + { 0x00004310, 0x00000000 }, + { CS35L41_VPVBST_FS_SEL, 0x00000000 }, + { CS35L41_OTP_TRIM_30, 0x9091A1C8 }, + { 0x00003014, 0x0200EE0E }, + { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, + { 0x00000054, 0x00000004 }, + { CS35L41_IRQ1_DB3, 0x00000000 }, + { CS35L41_IRQ2_DB3, 0x00000000 }, + { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, + { CS35L41_ASP_TX3_SRC, 0x00000000 }, + { CS35L41_ASP_TX4_SRC, 0x00000000 }, +}; + +static const struct reg_sequence cs35l41_revb0_errata_patch[] = { + { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, + { 0x00004310, 0x00000000 }, + { CS35L41_VPVBST_FS_SEL, 0x00000000 }, + { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, + { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, + { CS35L41_ASP_TX3_SRC, 0x00000000 }, + { CS35L41_ASP_TX4_SRC, 0x00000000 }, +}; + +static const struct reg_sequence cs35l41_revb2_errata_patch[] = { + { CS35L41_VIMON_SPKMON_RESYNC, 0x00000000 }, + { 0x00004310, 0x00000000 }, + { CS35L41_VPVBST_FS_SEL, 0x00000000 }, + { CS35L41_BSTCVRT_DCM_CTRL, 0x00000051 }, + { CS35L41_DSP1_YM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_DSP1_XM_ACCEL_PL0_PRI, 0x00000000 }, + { CS35L41_PWR_CTRL2, 0x00000000 }, + { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, + { CS35L41_ASP_TX3_SRC, 0x00000000 }, + { CS35L41_ASP_TX4_SRC, 0x00000000 }, +}; + +static const struct reg_sequence cs35l41_fs_errata_patch[] = { + { CS35L41_DSP1_RX1_RATE, 0x00000001 }, + { CS35L41_DSP1_RX2_RATE, 0x00000001 }, + { CS35L41_DSP1_RX3_RATE, 0x00000001 }, + { CS35L41_DSP1_RX4_RATE, 0x00000001 }, + { CS35L41_DSP1_RX5_RATE, 0x00000001 }, + { CS35L41_DSP1_RX6_RATE, 0x00000001 }, + { CS35L41_DSP1_RX7_RATE, 0x00000001 }, + { CS35L41_DSP1_RX8_RATE, 0x00000001 }, + { CS35L41_DSP1_TX1_RATE, 0x00000001 }, + { CS35L41_DSP1_TX2_RATE, 0x00000001 }, + { CS35L41_DSP1_TX3_RATE, 0x00000001 }, + { CS35L41_DSP1_TX4_RATE, 0x00000001 }, + { CS35L41_DSP1_TX5_RATE, 0x00000001 }, + { CS35L41_DSP1_TX6_RATE, 0x00000001 }, + { CS35L41_DSP1_TX7_RATE, 0x00000001 }, + { CS35L41_DSP1_TX8_RATE, 0x00000001 }, +}; + +static const struct cs35l41_otp_map_element_t cs35l41_otp_map_map[] = { + { + .id = 0x01, + .map = otp_map_1, + .num_elements = ARRAY_SIZE(otp_map_1), + .bit_offset = 16, + .word_offset = 2, + }, + { + .id = 0x02, + .map = otp_map_2, + .num_elements = ARRAY_SIZE(otp_map_2), + .bit_offset = 16, + .word_offset = 2, + }, + { + .id = 0x03, + .map = otp_map_2, + .num_elements = ARRAY_SIZE(otp_map_2), + .bit_offset = 16, + .word_offset = 2, + }, + { + .id = 0x06, + .map = otp_map_2, + .num_elements = ARRAY_SIZE(otp_map_2), + .bit_offset = 16, + .word_offset = 2, + }, + { + .id = 0x08, + .map = otp_map_1, + .num_elements = ARRAY_SIZE(otp_map_1), + .bit_offset = 16, + .word_offset = 2, + }, +}; + +struct regmap_config cs35l41_regmap_i2c = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = CS35L41_REGSTRIDE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L41_LASTREG, + .reg_defaults = cs35l41_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), + .volatile_reg = cs35l41_volatile_reg, + .readable_reg = cs35l41_readable_reg, + .precious_reg = cs35l41_precious_reg, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_GPL(cs35l41_regmap_i2c); + +struct regmap_config cs35l41_regmap_spi = { + .reg_bits = 32, + .val_bits = 32, + .pad_bits = 16, + .reg_stride = CS35L41_REGSTRIDE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L41_LASTREG, + .reg_defaults = cs35l41_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l41_reg), + .volatile_reg = cs35l41_volatile_reg, + .readable_reg = cs35l41_readable_reg, + .precious_reg = cs35l41_precious_reg, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_GPL(cs35l41_regmap_spi); + +static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) { + if (cs35l41_otp_map_map[i].id == otp_id) + return &cs35l41_otp_map_map[i]; + } + + return NULL; +} + +int cs35l41_test_key_unlock(struct device *dev, struct regmap *regmap) +{ + static const struct reg_sequence unlock[] = { + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, + }; + int ret; + + ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock)); + if (ret) + dev_err(dev, "Failed to unlock test key: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_test_key_unlock); + +int cs35l41_test_key_lock(struct device *dev, struct regmap *regmap) +{ + static const struct reg_sequence unlock[] = { + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, + }; + int ret; + + ret = regmap_multi_reg_write(regmap, unlock, ARRAY_SIZE(unlock)); + if (ret) + dev_err(dev, "Failed to lock test key: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_test_key_lock); + +/* Must be called with the TEST_KEY unlocked */ +int cs35l41_otp_unpack(struct device *dev, struct regmap *regmap) +{ + const struct cs35l41_otp_map_element_t *otp_map_match; + const struct cs35l41_otp_packed_element_t *otp_map; + int bit_offset, word_offset, ret, i; + unsigned int bit_sum = 8; + u32 otp_val, otp_id_reg; + u32 *otp_mem; + + otp_mem = kmalloc_array(CS35L41_OTP_SIZE_WORDS, sizeof(*otp_mem), GFP_KERNEL); + if (!otp_mem) + return -ENOMEM; + + ret = regmap_read(regmap, CS35L41_OTPID, &otp_id_reg); + if (ret) { + dev_err(dev, "Read OTP ID failed: %d\n", ret); + goto err_otp_unpack; + } + + otp_map_match = cs35l41_find_otp_map(otp_id_reg); + + if (!otp_map_match) { + dev_err(dev, "OTP Map matching ID %d not found\n", otp_id_reg); + ret = -EINVAL; + goto err_otp_unpack; + } + + ret = regmap_bulk_read(regmap, CS35L41_OTP_MEM0, otp_mem, CS35L41_OTP_SIZE_WORDS); + if (ret) { + dev_err(dev, "Read OTP Mem failed: %d\n", ret); + goto err_otp_unpack; + } + + otp_map = otp_map_match->map; + + bit_offset = otp_map_match->bit_offset; + word_offset = otp_map_match->word_offset; + + for (i = 0; i < otp_map_match->num_elements; i++) { + dev_dbg(dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d, otp_map[i].size = %u\n", + bit_offset, word_offset, bit_sum % 32, otp_map[i].size); + if (bit_offset + otp_map[i].size - 1 >= 32) { + otp_val = (otp_mem[word_offset] & + GENMASK(31, bit_offset)) >> bit_offset; + otp_val |= (otp_mem[++word_offset] & + GENMASK(bit_offset + otp_map[i].size - 33, 0)) << + (32 - bit_offset); + bit_offset += otp_map[i].size - 32; + } else if (bit_offset + otp_map[i].size - 1 >= 0) { + otp_val = (otp_mem[word_offset] & + GENMASK(bit_offset + otp_map[i].size - 1, bit_offset) + ) >> bit_offset; + bit_offset += otp_map[i].size; + } else /* both bit_offset and otp_map[i].size are 0 */ + otp_val = 0; + + bit_sum += otp_map[i].size; + + if (bit_offset == 32) { + bit_offset = 0; + word_offset++; + } + + if (otp_map[i].reg != 0) { + ret = regmap_update_bits(regmap, otp_map[i].reg, + GENMASK(otp_map[i].shift + otp_map[i].size - 1, + otp_map[i].shift), + otp_val << otp_map[i].shift); + if (ret < 0) { + dev_err(dev, "Write OTP val failed: %d\n", ret); + goto err_otp_unpack; + } + } + } + + ret = 0; + +err_otp_unpack: + kfree(otp_mem); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_otp_unpack); + +/* Must be called with the TEST_KEY unlocked */ +int cs35l41_register_errata_patch(struct device *dev, struct regmap *reg, unsigned int reg_revid) +{ + char *rev; + int ret; + + switch (reg_revid) { + case CS35L41_REVID_A0: + ret = regmap_register_patch(reg, cs35l41_reva0_errata_patch, + ARRAY_SIZE(cs35l41_reva0_errata_patch)); + rev = "A0"; + break; + case CS35L41_REVID_B0: + ret = regmap_register_patch(reg, cs35l41_revb0_errata_patch, + ARRAY_SIZE(cs35l41_revb0_errata_patch)); + rev = "B0"; + break; + case CS35L41_REVID_B2: + ret = regmap_register_patch(reg, cs35l41_revb2_errata_patch, + ARRAY_SIZE(cs35l41_revb2_errata_patch)); + rev = "B2"; + break; + default: + ret = -EINVAL; + rev = "XX"; + break; + } + + if (ret) + dev_err(dev, "Failed to apply %s errata patch: %d\n", rev, ret); + + ret = regmap_write(reg, CS35L41_DSP1_CCM_CORE_CTRL, 0); + if (ret < 0) + dev_err(dev, "Write CCM_CORE_CTRL failed: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_register_errata_patch); + +int cs35l41_set_channels(struct device *dev, struct regmap *reg, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + unsigned int val, mask; + int i; + + if (tx_num > 4 || rx_num > 2) + return -EINVAL; + + val = 0; + mask = 0; + for (i = 0; i < rx_num; i++) { + dev_dbg(dev, "rx slot %d position = %d\n", i, rx_slot[i]); + val |= rx_slot[i] << (i * 8); + mask |= 0x3F << (i * 8); + } + regmap_update_bits(reg, CS35L41_SP_FRAME_RX_SLOT, mask, val); + + val = 0; + mask = 0; + for (i = 0; i < tx_num; i++) { + dev_dbg(dev, "tx slot %d position = %d\n", i, tx_slot[i]); + val |= tx_slot[i] << (i * 8); + mask |= 0x3F << (i * 8); + } + regmap_update_bits(reg, CS35L41_SP_FRAME_TX_SLOT, mask, val); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l41_set_channels); + +static const unsigned char cs35l41_bst_k1_table[4][5] = { + { 0x24, 0x32, 0x32, 0x4F, 0x57 }, + { 0x24, 0x32, 0x32, 0x4F, 0x57 }, + { 0x40, 0x32, 0x32, 0x4F, 0x57 }, + { 0x40, 0x32, 0x32, 0x4F, 0x57 } +}; + +static const unsigned char cs35l41_bst_k2_table[4][5] = { + { 0x24, 0x49, 0x66, 0xA3, 0xEA }, + { 0x24, 0x49, 0x66, 0xA3, 0xEA }, + { 0x48, 0x49, 0x66, 0xA3, 0xEA }, + { 0x48, 0x49, 0x66, 0xA3, 0xEA } +}; + +static const unsigned char cs35l41_bst_slope_table[4] = { + 0x75, 0x6B, 0x3B, 0x28 +}; + +static int cs35l41_boost_config(struct device *dev, struct regmap *regmap, int boost_ind, + int boost_cap, int boost_ipk) +{ + unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled; + int ret; + + switch (boost_ind) { + case 1000: /* 1.0 uH */ + bst_lbst_val = 0; + break; + case 1200: /* 1.2 uH */ + bst_lbst_val = 1; + break; + case 1500: /* 1.5 uH */ + bst_lbst_val = 2; + break; + case 2200: /* 2.2 uH */ + bst_lbst_val = 3; + break; + default: + dev_err(dev, "Invalid boost inductor value: %d nH\n", boost_ind); + return -EINVAL; + } + + switch (boost_cap) { + case 0 ... 19: + bst_cbst_range = 0; + break; + case 20 ... 50: + bst_cbst_range = 1; + break; + case 51 ... 100: + bst_cbst_range = 2; + break; + case 101 ... 200: + bst_cbst_range = 3; + break; + default: + if (boost_cap < 0) { + dev_err(dev, "Invalid boost capacitor value: %d nH\n", boost_cap); + return -EINVAL; + } + /* 201 uF and greater */ + bst_cbst_range = 4; + } + + if (boost_ipk < 1600 || boost_ipk > 4500) { + dev_err(dev, "Invalid boost inductor peak current: %d mA\n", boost_ipk); + return -EINVAL; + } + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF, + CS35L41_BST_K1_MASK | CS35L41_BST_K2_MASK, + cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range] + << CS35L41_BST_K1_SHIFT | + cs35l41_bst_k2_table[bst_lbst_val][bst_cbst_range] + << CS35L41_BST_K2_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost coefficients: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST, + CS35L41_BST_SLOPE_MASK | CS35L41_BST_LBST_VAL_MASK, + cs35l41_bst_slope_table[bst_lbst_val] + << CS35L41_BST_SLOPE_SHIFT | + bst_lbst_val << CS35L41_BST_LBST_VAL_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost slope/inductor value: %d\n", ret); + return ret; + } + + bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10; + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR, CS35L41_BST_IPK_MASK, + bst_ipk_scaled << CS35L41_BST_IPK_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost inductor peak current: %d\n", ret); + return ret; + } + + regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK, + CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT); + + return 0; +} + +static const struct reg_sequence cs35l41_safe_to_reset[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x0000393C, 0x000000C0, 6000}, + { 0x0000393C, 0x00000000 }, + { 0x00007414, 0x00C82222 }, + { 0x0000742C, 0x00000000 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct reg_sequence cs35l41_active_to_safe_start[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00007438, 0x00585941 }, + { CS35L41_PWR_CTRL1, 0x00000000 }, + { 0x0000742C, 0x00000009 }, +}; + +static const struct reg_sequence cs35l41_active_to_safe_end[] = { + { 0x00007438, 0x00580941 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct reg_sequence cs35l41_safe_to_active_start[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x0000742C, 0x0000000F }, + { 0x0000742C, 0x00000079 }, + { 0x00007438, 0x00585941 }, + { CS35L41_PWR_CTRL1, 0x00000001 }, // GLOBAL_EN = 1 +}; + +static const struct reg_sequence cs35l41_safe_to_active_en_spk[] = { + { 0x0000742C, 0x000000F9 }, + { 0x00007438, 0x00580941 }, +}; + +static const struct reg_sequence cs35l41_reset_to_safe[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00007438, 0x00585941 }, + { 0x00007414, 0x08C82222 }, + { 0x0000742C, 0x00000009 }, + { 0x00000040, 0x000000CC }, + { 0x00000040, 0x00000033 }, +}; + +static const struct reg_sequence cs35l41_actv_seq[] = { + /* SYNC_BST_CTL_RX_EN = 1; SYNC_BST_CTL_TX_EN = 1 */ + {CS35L41_MDSYNC_EN, 0x00003000}, + /* BST_CTL_SEL = MDSYNC */ + {CS35L41_BSTCVRT_VCTRL2, 0x00000002}, +}; + +static const struct reg_sequence cs35l41_pass_seq[] = { + /* SYNC_BST_CTL_RX_EN = 0; SYNC_BST_CTL_TX_EN = 1 */ + {CS35L41_MDSYNC_EN, 0x00001000}, + /* BST_EN = 0 */ + {CS35L41_PWR_CTRL2, 0x00003300}, + /* BST_CTL_SEL = MDSYNC */ + {CS35L41_BSTCVRT_VCTRL2, 0x00000002}, +}; + +int cs35l41_init_boost(struct device *dev, struct regmap *regmap, + struct cs35l41_hw_cfg *hw_cfg) +{ + int ret; + + switch (hw_cfg->bst_type) { + case CS35L41_SHD_BOOST_ACTV: + regmap_multi_reg_write(regmap, cs35l41_actv_seq, ARRAY_SIZE(cs35l41_actv_seq)); + fallthrough; + case CS35L41_INT_BOOST: + ret = cs35l41_boost_config(dev, regmap, hw_cfg->bst_ind, + hw_cfg->bst_cap, hw_cfg->bst_ipk); + if (ret) + dev_err(dev, "Error in Boost DT config: %d\n", ret); + break; + case CS35L41_EXT_BOOST: + case CS35L41_EXT_BOOST_NO_VSPK_SWITCH: + /* Only CLSA0100 doesn't use GPIO as VSPK switch, but even on that laptop we can + * toggle GPIO1 as is not connected to anything. + * There will be no other device without VSPK switch. + */ + regmap_write(regmap, CS35L41_GPIO1_CTRL1, 0x00000001); + regmap_multi_reg_write(regmap, cs35l41_reset_to_safe, + ARRAY_SIZE(cs35l41_reset_to_safe)); + ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK, + CS35L41_BST_DIS_FET_OFF << CS35L41_BST_EN_SHIFT); + break; + case CS35L41_SHD_BOOST_PASS: + ret = regmap_multi_reg_write(regmap, cs35l41_pass_seq, + ARRAY_SIZE(cs35l41_pass_seq)); + break; + default: + dev_err(dev, "Boost type %d not supported\n", hw_cfg->bst_type); + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_init_boost); + +bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type) +{ + switch (b_type) { + /* There is only one laptop that doesn't have VSPK switch. */ + case CS35L41_EXT_BOOST_NO_VSPK_SWITCH: + return false; + case CS35L41_EXT_BOOST: + regmap_write(regmap, CS35L41_GPIO1_CTRL1, 0x00000001); + regmap_multi_reg_write(regmap, cs35l41_safe_to_reset, + ARRAY_SIZE(cs35l41_safe_to_reset)); + return true; + default: + return true; + } +} +EXPORT_SYMBOL_GPL(cs35l41_safe_reset); + +/* + * Enabling the CS35L41_SHD_BOOST_ACTV and CS35L41_SHD_BOOST_PASS shared boosts + * does also require a call to cs35l41_mdsync_up(), but not before getting the + * PLL Lock signal. + * + * PLL Lock seems to be triggered soon after snd_pcm_start() is executed and + * SNDRV_PCM_TRIGGER_START command is processed, which happens (long) after the + * SND_SOC_DAPM_PRE_PMU event handler is invoked as part of snd_pcm_prepare(). + * + * This event handler is where cs35l41_global_enable() is normally called from, + * but waiting for PLL Lock here will time out. Increasing the wait duration + * will not help, as the only consequence of it would be to add an unnecessary + * delay in the invocation of snd_pcm_start(). + * + * Trying to move the wait in the SNDRV_PCM_TRIGGER_START callback is not a + * solution either, as the trigger is executed in an IRQ-off atomic context. + * + * The current approach is to invoke cs35l41_mdsync_up() right after receiving + * the PLL Lock interrupt, in the IRQ handler. + */ +int cs35l41_global_enable(struct device *dev, struct regmap *regmap, enum cs35l41_boost_type b_type, + int enable, bool firmware_running) +{ + int ret; + unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3, int_status, pup_pdn_mask; + unsigned int pwr_ctl1_val; + struct reg_sequence cs35l41_mdsync_down_seq[] = { + {CS35L41_PWR_CTRL3, 0}, + {CS35L41_GPIO_PAD_CONTROL, 0}, + {CS35L41_PWR_CTRL1, 0, 3000}, + }; + + pup_pdn_mask = enable ? CS35L41_PUP_DONE_MASK : CS35L41_PDN_DONE_MASK; + + ret = regmap_read(regmap, CS35L41_PWR_CTRL1, &pwr_ctl1_val); + if (ret) + return ret; + + if ((pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && enable) { + dev_dbg(dev, "Cannot set Global Enable - already set.\n"); + return 0; + } else if (!(pwr_ctl1_val & CS35L41_GLOBAL_EN_MASK) && !enable) { + dev_dbg(dev, "Cannot unset Global Enable - not set.\n"); + return 0; + } + + switch (b_type) { + case CS35L41_SHD_BOOST_ACTV: + case CS35L41_SHD_BOOST_PASS: + regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3); + regmap_read(regmap, CS35L41_GPIO_PAD_CONTROL, &pad_control); + + pwr_ctrl3 &= ~CS35L41_SYNC_EN_MASK; + pwr_ctrl1 = enable << CS35L41_GLOBAL_EN_SHIFT; + + gpio1_func = enable ? CS35L41_GPIO1_MDSYNC : CS35L41_GPIO1_HIZ; + gpio1_func <<= CS35L41_GPIO1_CTRL_SHIFT; + + pad_control &= ~CS35L41_GPIO1_CTRL_MASK; + pad_control |= gpio1_func & CS35L41_GPIO1_CTRL_MASK; + + cs35l41_mdsync_down_seq[0].def = pwr_ctrl3; + cs35l41_mdsync_down_seq[1].def = pad_control; + cs35l41_mdsync_down_seq[2].def = pwr_ctrl1; + + ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_down_seq, + ARRAY_SIZE(cs35l41_mdsync_down_seq)); + /* Activation to be completed later via cs35l41_mdsync_up() */ + if (ret || enable) + return ret; + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, + int_status, int_status & pup_pdn_mask, + 1000, 100000); + if (ret) + dev_err(dev, "Enable(%d) failed: %d\n", enable, ret); + + /* Clear PUP/PDN status */ + regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask); + break; + case CS35L41_INT_BOOST: + ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK, + enable << CS35L41_GLOBAL_EN_SHIFT); + if (ret) { + dev_err(dev, "CS35L41_PWR_CTRL1 set failed: %d\n", ret); + return ret; + } + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, + int_status, int_status & pup_pdn_mask, + 1000, 100000); + if (ret) + dev_err(dev, "Enable(%d) failed: %d\n", enable, ret); + + /* Clear PUP/PDN status */ + regmap_write(regmap, CS35L41_IRQ1_STATUS1, pup_pdn_mask); + break; + case CS35L41_EXT_BOOST: + case CS35L41_EXT_BOOST_NO_VSPK_SWITCH: + if (enable) { + /* Test Key is unlocked here */ + ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_start, + ARRAY_SIZE(cs35l41_safe_to_active_start)); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status, + int_status & CS35L41_PUP_DONE_MASK, 1000, 100000); + if (ret) { + dev_err(dev, "Failed waiting for CS35L41_PUP_DONE_MASK: %d\n", ret); + /* Lock the test key, it was unlocked during the multi_reg_write */ + cs35l41_test_key_lock(dev, regmap); + return ret; + } + regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PUP_DONE_MASK); + + if (firmware_running) + ret = cs35l41_set_cspl_mbox_cmd(dev, regmap, + CSPL_MBOX_CMD_SPK_OUT_ENABLE); + else + ret = regmap_multi_reg_write(regmap, cs35l41_safe_to_active_en_spk, + ARRAY_SIZE(cs35l41_safe_to_active_en_spk)); + + /* Lock the test key, it was unlocked during the multi_reg_write */ + cs35l41_test_key_lock(dev, regmap); + } else { + /* Test Key is unlocked here */ + ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_start, + ARRAY_SIZE(cs35l41_active_to_safe_start)); + if (ret) { + /* Lock the test key, it was unlocked during the multi_reg_write */ + cs35l41_test_key_lock(dev, regmap); + return ret; + } + + ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1, int_status, + int_status & CS35L41_PDN_DONE_MASK, 1000, 100000); + if (ret) { + dev_err(dev, "Failed waiting for CS35L41_PDN_DONE_MASK: %d\n", ret); + /* Lock the test key, it was unlocked during the multi_reg_write */ + cs35l41_test_key_lock(dev, regmap); + return ret; + } + regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PDN_DONE_MASK); + + /* Test Key is locked here */ + ret = regmap_multi_reg_write(regmap, cs35l41_active_to_safe_end, + ARRAY_SIZE(cs35l41_active_to_safe_end)); + } + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_global_enable); + +/* + * To be called after receiving the IRQ Lock interrupt, in order to complete + * any shared boost activation initiated by cs35l41_global_enable(). + */ +int cs35l41_mdsync_up(struct regmap *regmap) +{ + return regmap_update_bits(regmap, CS35L41_PWR_CTRL3, + CS35L41_SYNC_EN_MASK, CS35L41_SYNC_EN_MASK); +} +EXPORT_SYMBOL_GPL(cs35l41_mdsync_up); + +int cs35l41_gpio_config(struct regmap *regmap, struct cs35l41_hw_cfg *hw_cfg) +{ + struct cs35l41_gpio_cfg *gpio1 = &hw_cfg->gpio1; + struct cs35l41_gpio_cfg *gpio2 = &hw_cfg->gpio2; + int irq_pol = IRQF_TRIGGER_NONE; + + regmap_update_bits(regmap, CS35L41_GPIO1_CTRL1, + CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK, + gpio1->pol_inv << CS35L41_GPIO_POL_SHIFT | + !gpio1->out_en << CS35L41_GPIO_DIR_SHIFT); + + regmap_update_bits(regmap, CS35L41_GPIO2_CTRL1, + CS35L41_GPIO_POL_MASK | CS35L41_GPIO_DIR_MASK, + gpio2->pol_inv << CS35L41_GPIO_POL_SHIFT | + !gpio2->out_en << CS35L41_GPIO_DIR_SHIFT); + + if (gpio1->valid) + regmap_update_bits(regmap, CS35L41_GPIO_PAD_CONTROL, CS35L41_GPIO1_CTRL_MASK, + gpio1->func << CS35L41_GPIO1_CTRL_SHIFT); + + if (gpio2->valid) { + regmap_update_bits(regmap, CS35L41_GPIO_PAD_CONTROL, CS35L41_GPIO2_CTRL_MASK, + gpio2->func << CS35L41_GPIO2_CTRL_SHIFT); + + switch (gpio2->func) { + case CS35L41_GPIO2_INT_PUSH_PULL_LOW: + case CS35L41_GPIO2_INT_OPEN_DRAIN: + irq_pol = IRQF_TRIGGER_LOW; + break; + case CS35L41_GPIO2_INT_PUSH_PULL_HIGH: + irq_pol = IRQF_TRIGGER_HIGH; + break; + default: + break; + } + } + + return irq_pol; +} +EXPORT_SYMBOL_GPL(cs35l41_gpio_config); + +static const struct cs_dsp_region cs35l41_dsp1_regions[] = { + { .type = WMFW_HALO_PM_PACKED, .base = CS35L41_DSP1_PMEM_0 }, + { .type = WMFW_HALO_XM_PACKED, .base = CS35L41_DSP1_XMEM_PACK_0 }, + { .type = WMFW_HALO_YM_PACKED, .base = CS35L41_DSP1_YMEM_PACK_0 }, + {. type = WMFW_ADSP2_XM, .base = CS35L41_DSP1_XMEM_UNPACK24_0}, + {. type = WMFW_ADSP2_YM, .base = CS35L41_DSP1_YMEM_UNPACK24_0}, +}; + +void cs35l41_configure_cs_dsp(struct device *dev, struct regmap *reg, struct cs_dsp *dsp) +{ + dsp->num = 1; + dsp->type = WMFW_HALO; + dsp->rev = 0; + dsp->dev = dev; + dsp->regmap = reg; + dsp->base = CS35L41_DSP1_CTRL_BASE; + dsp->base_sysinfo = CS35L41_DSP1_SYS_ID; + dsp->mem = cs35l41_dsp1_regions; + dsp->num_mems = ARRAY_SIZE(cs35l41_dsp1_regions); + dsp->lock_regions = 0xFFFFFFFF; +} +EXPORT_SYMBOL_GPL(cs35l41_configure_cs_dsp); + +static bool cs35l41_check_cspl_mbox_sts(enum cs35l41_cspl_mbox_cmd cmd, + enum cs35l41_cspl_mbox_status sts) +{ + switch (cmd) { + case CSPL_MBOX_CMD_NONE: + case CSPL_MBOX_CMD_UNKNOWN_CMD: + return true; + case CSPL_MBOX_CMD_PAUSE: + case CSPL_MBOX_CMD_OUT_OF_HIBERNATE: + return (sts == CSPL_MBOX_STS_PAUSED); + case CSPL_MBOX_CMD_RESUME: + return (sts == CSPL_MBOX_STS_RUNNING); + case CSPL_MBOX_CMD_REINIT: + return (sts == CSPL_MBOX_STS_RUNNING); + case CSPL_MBOX_CMD_STOP_PRE_REINIT: + return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT); + case CSPL_MBOX_CMD_SPK_OUT_ENABLE: + return (sts == CSPL_MBOX_STS_RUNNING); + default: + return false; + } +} + +int cs35l41_set_cspl_mbox_cmd(struct device *dev, struct regmap *regmap, + enum cs35l41_cspl_mbox_cmd cmd) +{ + unsigned int sts = 0, i; + int ret; + + // Set mailbox cmd + ret = regmap_write(regmap, CS35L41_DSP_VIRT1_MBOX_1, cmd); + if (ret < 0) { + if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE) + dev_err(dev, "Failed to write MBOX: %d\n", ret); + return ret; + } + + // Read mailbox status and verify it is appropriate for the given cmd + for (i = 0; i < 5; i++) { + usleep_range(1000, 1100); + + ret = regmap_read(regmap, CS35L41_DSP_MBOX_2, &sts); + if (ret < 0) { + dev_err(dev, "Failed to read MBOX STS: %d\n", ret); + continue; + } + + if (!cs35l41_check_cspl_mbox_sts(cmd, sts)) + dev_dbg(dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts); + else + return 0; + } + + if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE) + dev_err(dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts); + + return -ENOMSG; +} +EXPORT_SYMBOL_GPL(cs35l41_set_cspl_mbox_cmd); + +int cs35l41_write_fs_errata(struct device *dev, struct regmap *regmap) +{ + int ret; + + ret = regmap_multi_reg_write(regmap, cs35l41_fs_errata_patch, + ARRAY_SIZE(cs35l41_fs_errata_patch)); + if (ret < 0) + dev_err(dev, "Failed to write fs errata: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_write_fs_errata); + +int cs35l41_enter_hibernate(struct device *dev, struct regmap *regmap, + enum cs35l41_boost_type b_type) +{ + if (!cs35l41_safe_reset(regmap, b_type)) { + dev_dbg(dev, "System does not support Suspend\n"); + return -EINVAL; + } + + dev_dbg(dev, "Enter hibernate\n"); + regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0088); + regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0188); + + // Don't wait for ACK since bus activity would wake the device + regmap_write(regmap, CS35L41_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l41_enter_hibernate); + +static void cs35l41_wait_for_pwrmgt_sts(struct device *dev, struct regmap *regmap) +{ + const int pwrmgt_retries = 10; + unsigned int sts; + int i, ret; + + for (i = 0; i < pwrmgt_retries; i++) { + ret = regmap_read(regmap, CS35L41_PWRMGT_STS, &sts); + if (ret) + dev_err(dev, "Failed to read PWRMGT_STS: %d\n", ret); + else if (!(sts & CS35L41_WR_PEND_STS_MASK)) + return; + + udelay(20); + } + + dev_err(dev, "Timed out reading PWRMGT_STS\n"); +} + +int cs35l41_exit_hibernate(struct device *dev, struct regmap *regmap) +{ + const int wake_retries = 20; + const int sleep_retries = 5; + int ret, i, j; + + for (i = 0; i < sleep_retries; i++) { + dev_dbg(dev, "Exit hibernate\n"); + + for (j = 0; j < wake_retries; j++) { + ret = cs35l41_set_cspl_mbox_cmd(dev, regmap, + CSPL_MBOX_CMD_OUT_OF_HIBERNATE); + if (!ret) + break; + + usleep_range(100, 200); + } + + if (j < wake_retries) { + dev_dbg(dev, "Wake success at cycle: %d\n", j); + return 0; + } + + dev_err(dev, "Wake failed, re-enter hibernate: %d\n", ret); + + cs35l41_wait_for_pwrmgt_sts(dev, regmap); + regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0088); + + cs35l41_wait_for_pwrmgt_sts(dev, regmap); + regmap_write(regmap, CS35L41_WAKESRC_CTL, 0x0188); + + cs35l41_wait_for_pwrmgt_sts(dev, regmap); + regmap_write(regmap, CS35L41_PWRMGT_CTL, 0x3); + } + + dev_err(dev, "Timed out waking device\n"); + + return -ETIMEDOUT; +} +EXPORT_SYMBOL_GPL(cs35l41_exit_hibernate); + +MODULE_DESCRIPTION("CS35L41 library"); +MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); +MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c new file mode 100644 index 0000000000..5c8bb24909 --- /dev/null +++ b/sound/soc/codecs/cs35l41-spi.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41-spi.c -- CS35l41 SPI driver +// +// Copyright 2017-2021 Cirrus Logic, Inc. +// +// Author: David Rhodes + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l41.h" + +static const struct spi_device_id cs35l41_id_spi[] = { + { "cs35l40", 0 }, + { "cs35l41", 0 }, + { "cs35l51", 0 }, + { "cs35l53", 0 }, + {} +}; + +MODULE_DEVICE_TABLE(spi, cs35l41_id_spi); + +static int cs35l41_spi_probe(struct spi_device *spi) +{ + const struct regmap_config *regmap_config = &cs35l41_regmap_spi; + struct cs35l41_hw_cfg *hw_cfg = dev_get_platdata(&spi->dev); + struct cs35l41_private *cs35l41; + int ret; + + cs35l41 = devm_kzalloc(&spi->dev, sizeof(struct cs35l41_private), GFP_KERNEL); + if (!cs35l41) + return -ENOMEM; + + spi->max_speed_hz = CS35L41_SPI_MAX_FREQ; + spi_setup(spi); + + spi_set_drvdata(spi, cs35l41); + cs35l41->regmap = devm_regmap_init_spi(spi, regmap_config); + if (IS_ERR(cs35l41->regmap)) { + ret = PTR_ERR(cs35l41->regmap); + dev_err(&spi->dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + cs35l41->dev = &spi->dev; + cs35l41->irq = spi->irq; + + return cs35l41_probe(cs35l41, hw_cfg); +} + +static void cs35l41_spi_remove(struct spi_device *spi) +{ + struct cs35l41_private *cs35l41 = spi_get_drvdata(spi); + + cs35l41_remove(cs35l41); +} + +#ifdef CONFIG_OF +static const struct of_device_id cs35l41_of_match[] = { + { .compatible = "cirrus,cs35l40" }, + { .compatible = "cirrus,cs35l41" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l41_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id cs35l41_acpi_match[] = { + { "CSC3541", 0 }, /* Cirrus Logic PnP ID + part ID */ + { "CLSA3541", 0 }, /* Cirrus Logic PnP ID + part ID */ + {}, +}; +MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_match); +#endif + +static struct spi_driver cs35l41_spi_driver = { + .driver = { + .name = "cs35l41", + .pm = &cs35l41_pm_ops, + .of_match_table = of_match_ptr(cs35l41_of_match), + .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), + }, + .id_table = cs35l41_id_spi, + .probe = cs35l41_spi_probe, + .remove = cs35l41_spi_remove, +}; + +module_spi_driver(cs35l41_spi_driver); + +MODULE_DESCRIPTION("SPI CS35L41 driver"); +MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c new file mode 100644 index 0000000000..5456e6bfa2 --- /dev/null +++ b/sound/soc/codecs/cs35l41.c @@ -0,0 +1,1469 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l41.c -- CS35l41 ALSA SoC audio driver +// +// Copyright 2017-2021 Cirrus Logic, Inc. +// +// Author: David Rhodes + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l41.h" + +static const char * const cs35l41_supplies[CS35L41_NUM_SUPPLIES] = { + "VA", + "VP", +}; + +struct cs35l41_pll_sysclk_config { + int freq; + int clk_cfg; +}; + +static const struct cs35l41_pll_sysclk_config cs35l41_pll_sysclk[] = { + { 32768, 0x00 }, + { 8000, 0x01 }, + { 11025, 0x02 }, + { 12000, 0x03 }, + { 16000, 0x04 }, + { 22050, 0x05 }, + { 24000, 0x06 }, + { 32000, 0x07 }, + { 44100, 0x08 }, + { 48000, 0x09 }, + { 88200, 0x0A }, + { 96000, 0x0B }, + { 128000, 0x0C }, + { 176400, 0x0D }, + { 192000, 0x0E }, + { 256000, 0x0F }, + { 352800, 0x10 }, + { 384000, 0x11 }, + { 512000, 0x12 }, + { 705600, 0x13 }, + { 750000, 0x14 }, + { 768000, 0x15 }, + { 1000000, 0x16 }, + { 1024000, 0x17 }, + { 1200000, 0x18 }, + { 1411200, 0x19 }, + { 1500000, 0x1A }, + { 1536000, 0x1B }, + { 2000000, 0x1C }, + { 2048000, 0x1D }, + { 2400000, 0x1E }, + { 2822400, 0x1F }, + { 3000000, 0x20 }, + { 3072000, 0x21 }, + { 3200000, 0x22 }, + { 4000000, 0x23 }, + { 4096000, 0x24 }, + { 4800000, 0x25 }, + { 5644800, 0x26 }, + { 6000000, 0x27 }, + { 6144000, 0x28 }, + { 6250000, 0x29 }, + { 6400000, 0x2A }, + { 6500000, 0x2B }, + { 6750000, 0x2C }, + { 7526400, 0x2D }, + { 8000000, 0x2E }, + { 8192000, 0x2F }, + { 9600000, 0x30 }, + { 11289600, 0x31 }, + { 12000000, 0x32 }, + { 12288000, 0x33 }, + { 12500000, 0x34 }, + { 12800000, 0x35 }, + { 13000000, 0x36 }, + { 13500000, 0x37 }, + { 19200000, 0x38 }, + { 22579200, 0x39 }, + { 24000000, 0x3A }, + { 24576000, 0x3B }, + { 25000000, 0x3C }, + { 25600000, 0x3D }, + { 26000000, 0x3E }, + { 27000000, 0x3F }, +}; + +struct cs35l41_fs_mon_config { + int freq; + unsigned int fs1; + unsigned int fs2; +}; + +static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = { + { 32768, 2254, 3754 }, + { 8000, 9220, 15364 }, + { 11025, 6148, 10244 }, + { 12000, 6148, 10244 }, + { 16000, 4612, 7684 }, + { 22050, 3076, 5124 }, + { 24000, 3076, 5124 }, + { 32000, 2308, 3844 }, + { 44100, 1540, 2564 }, + { 48000, 1540, 2564 }, + { 88200, 772, 1284 }, + { 96000, 772, 1284 }, + { 128000, 580, 964 }, + { 176400, 388, 644 }, + { 192000, 388, 644 }, + { 256000, 292, 484 }, + { 352800, 196, 324 }, + { 384000, 196, 324 }, + { 512000, 148, 244 }, + { 705600, 100, 164 }, + { 750000, 100, 164 }, + { 768000, 100, 164 }, + { 1000000, 76, 124 }, + { 1024000, 76, 124 }, + { 1200000, 64, 104 }, + { 1411200, 52, 84 }, + { 1500000, 52, 84 }, + { 1536000, 52, 84 }, + { 2000000, 40, 64 }, + { 2048000, 40, 64 }, + { 2400000, 34, 54 }, + { 2822400, 28, 44 }, + { 3000000, 28, 44 }, + { 3072000, 28, 44 }, + { 3200000, 27, 42 }, + { 4000000, 22, 34 }, + { 4096000, 22, 34 }, + { 4800000, 19, 29 }, + { 5644800, 16, 24 }, + { 6000000, 16, 24 }, + { 6144000, 16, 24 }, + { 12288000, 0, 0 }, +}; + +static int cs35l41_get_fs_mon_config_index(int freq) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l41_fs_mon); i++) { + if (cs35l41_fs_mon[i].freq == freq) + return i; + } + + return -EINVAL; +} + +static const DECLARE_TLV_DB_RANGE(dig_vol_tlv, + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + 1, 913, TLV_DB_MINMAX_ITEM(-10200, 1200)); +static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 50, 100, 0); + +static const struct snd_kcontrol_new dre_ctrl = + SOC_DAPM_SINGLE("Switch", CS35L41_PWR_CTRL3, 20, 1, 0); + +static const char * const cs35l41_pcm_sftramp_text[] = { + "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms" +}; + +static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, + CS35L41_AMP_DIG_VOL_CTRL, 0, + cs35l41_pcm_sftramp_text); + +static int cs35l41_dsp_preload_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (cs35l41->dsp.cs_dsp.booted) + return 0; + + return wm_adsp_early_event(w, kcontrol, event); + case SND_SOC_DAPM_PRE_PMD: + if (cs35l41->dsp.preloaded) + return 0; + + if (cs35l41->dsp.cs_dsp.running) { + ret = wm_adsp_event(w, kcontrol, event); + if (ret) + return ret; + } + + return wm_adsp_early_event(w, kcontrol, event); + default: + return 0; + } +} + +static int cs35l41_dsp_audio_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + unsigned int fw_status; + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!cs35l41->dsp.cs_dsp.running) + return wm_adsp_event(w, kcontrol, event); + + ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &fw_status); + if (ret < 0) { + dev_err(cs35l41->dev, + "Failed to read firmware status: %d\n", ret); + return ret; + } + + switch (fw_status) { + case CSPL_MBOX_STS_RUNNING: + case CSPL_MBOX_STS_PAUSED: + break; + default: + dev_err(cs35l41->dev, "Firmware status is invalid: %u\n", + fw_status); + return -EINVAL; + } + + return cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, + CSPL_MBOX_CMD_RESUME); + case SND_SOC_DAPM_PRE_PMD: + return cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, + CSPL_MBOX_CMD_PAUSE); + default: + return 0; + } +} + +static const char * const cs35l41_pcm_source_texts[] = {"ASP", "DSP"}; +static const unsigned int cs35l41_pcm_source_values[] = {0x08, 0x32}; +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum, + CS35L41_DAC_PCM1_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_pcm_source_texts, + cs35l41_pcm_source_values); + +static const struct snd_kcontrol_new pcm_source_mux = + SOC_DAPM_ENUM("PCM Source", cs35l41_pcm_source_enum); + +static const char * const cs35l41_tx_input_texts[] = { + "Zero", "ASPRX1", "ASPRX2", "VMON", "IMON", + "VPMON", "VBSTMON", "DSPTX1", "DSPTX2" +}; + +static const unsigned int cs35l41_tx_input_values[] = { + 0x00, CS35L41_INPUT_SRC_ASPRX1, CS35L41_INPUT_SRC_ASPRX2, + CS35L41_INPUT_SRC_VMON, CS35L41_INPUT_SRC_IMON, CS35L41_INPUT_SRC_VPMON, + CS35L41_INPUT_SRC_VBSTMON, CS35L41_INPUT_DSP_TX1, CS35L41_INPUT_DSP_TX2 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx1_enum, + CS35L41_ASP_TX1_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new asp_tx1_mux = + SOC_DAPM_ENUM("ASPTX1 SRC", cs35l41_asptx1_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx2_enum, + CS35L41_ASP_TX2_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new asp_tx2_mux = + SOC_DAPM_ENUM("ASPTX2 SRC", cs35l41_asptx2_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx3_enum, + CS35L41_ASP_TX3_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new asp_tx3_mux = + SOC_DAPM_ENUM("ASPTX3 SRC", cs35l41_asptx3_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum, + CS35L41_ASP_TX4_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new asp_tx4_mux = + SOC_DAPM_ENUM("ASPTX4 SRC", cs35l41_asptx4_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx1_enum, + CS35L41_DSP1_RX1_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new dsp_rx1_mux = + SOC_DAPM_ENUM("DSPRX1 SRC", cs35l41_dsprx1_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx2_enum, + CS35L41_DSP1_RX2_SRC, + 0, CS35L41_ASP_SOURCE_MASK, + cs35l41_tx_input_texts, + cs35l41_tx_input_values); + +static const struct snd_kcontrol_new dsp_rx2_mux = + SOC_DAPM_ENUM("DSPRX2 SRC", cs35l41_dsprx2_enum); + +static const struct snd_kcontrol_new cs35l41_aud_controls[] = { + SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L41_AMP_DIG_VOL_CTRL, + 3, 0x4CF, 0x391, dig_vol_tlv), + SOC_SINGLE_TLV("Analog PCM Volume", CS35L41_AMP_GAIN_CTRL, 5, 0x14, 0, + amp_gain_tlv), + SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp), + SOC_SINGLE("HW Noise Gate Enable", CS35L41_NG_CFG, 8, 63, 0), + SOC_SINGLE("HW Noise Gate Delay", CS35L41_NG_CFG, 4, 7, 0), + SOC_SINGLE("HW Noise Gate Threshold", CS35L41_NG_CFG, 0, 7, 0), + SOC_SINGLE("Aux Noise Gate CH1 Switch", + CS35L41_MIXER_NGATE_CH1_CFG, 16, 1, 0), + SOC_SINGLE("Aux Noise Gate CH1 Entry Delay", + CS35L41_MIXER_NGATE_CH1_CFG, 8, 15, 0), + SOC_SINGLE("Aux Noise Gate CH1 Threshold", + CS35L41_MIXER_NGATE_CH1_CFG, 0, 7, 0), + SOC_SINGLE("Aux Noise Gate CH2 Entry Delay", + CS35L41_MIXER_NGATE_CH2_CFG, 8, 15, 0), + SOC_SINGLE("Aux Noise Gate CH2 Switch", + CS35L41_MIXER_NGATE_CH2_CFG, 16, 1, 0), + SOC_SINGLE("Aux Noise Gate CH2 Threshold", + CS35L41_MIXER_NGATE_CH2_CFG, 0, 7, 0), + SOC_SINGLE("SCLK Force Switch", CS35L41_SP_FORMAT, CS35L41_SCLK_FRC_SHIFT, 1, 0), + SOC_SINGLE("LRCLK Force Switch", CS35L41_SP_FORMAT, CS35L41_LRCLK_FRC_SHIFT, 1, 0), + SOC_SINGLE("Invert Class D Switch", CS35L41_AMP_DIG_VOL_CTRL, + CS35L41_AMP_INV_PCM_SHIFT, 1, 0), + SOC_SINGLE("Amp Gain ZC Switch", CS35L41_AMP_GAIN_CTRL, + CS35L41_AMP_GAIN_ZC_SHIFT, 1, 0), + WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + WM_ADSP_FW_CONTROL("DSP1", 0), +}; + +static void cs35l41_boost_enable(struct cs35l41_private *cs35l41, unsigned int enable) +{ + switch (cs35l41->hw_cfg.bst_type) { + case CS35L41_INT_BOOST: + case CS35L41_SHD_BOOST_ACTV: + enable = enable ? CS35L41_BST_EN_DEFAULT : CS35L41_BST_DIS_FET_OFF; + regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK, + enable << CS35L41_BST_EN_SHIFT); + break; + default: + break; + } +} + + +static void cs35l41_error_release(struct cs35l41_private *cs35l41, unsigned int irq_err_bit, + unsigned int rel_err_bit) +{ + regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1, irq_err_bit); + regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0); + regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, rel_err_bit); + regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, 0); +} + +static irqreturn_t cs35l41_irq(int irq, void *data) +{ + struct cs35l41_private *cs35l41 = data; + unsigned int status[4] = { 0, 0, 0, 0 }; + unsigned int masks[4] = { 0, 0, 0, 0 }; + unsigned int i; + int ret; + + ret = pm_runtime_resume_and_get(cs35l41->dev); + if (ret < 0) { + dev_err(cs35l41->dev, + "pm_runtime_resume_and_get failed in %s: %d\n", + __func__, ret); + return IRQ_NONE; + } + + ret = IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(status); i++) { + regmap_read(cs35l41->regmap, + CS35L41_IRQ1_STATUS1 + (i * CS35L41_REGSTRIDE), + &status[i]); + regmap_read(cs35l41->regmap, + CS35L41_IRQ1_MASK1 + (i * CS35L41_REGSTRIDE), + &masks[i]); + } + + /* Check to see if unmasked bits are active */ + if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) && + !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) + goto done; + + if (status[3] & CS35L41_OTP_BOOT_DONE) { + regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK4, + CS35L41_OTP_BOOT_DONE, CS35L41_OTP_BOOT_DONE); + } + + /* + * The following interrupts require a + * protection release cycle to get the + * speaker out of Safe-Mode. + */ + if (status[0] & CS35L41_AMP_SHORT_ERR) { + dev_crit_ratelimited(cs35l41->dev, "Amp short error\n"); + cs35l41_error_release(cs35l41, CS35L41_AMP_SHORT_ERR, CS35L41_AMP_SHORT_ERR_RLS); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L41_TEMP_WARN) { + dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n"); + cs35l41_error_release(cs35l41, CS35L41_TEMP_WARN, CS35L41_TEMP_WARN_ERR_RLS); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L41_TEMP_ERR) { + dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n"); + cs35l41_error_release(cs35l41, CS35L41_TEMP_ERR, CS35L41_TEMP_ERR_RLS); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L41_BST_OVP_ERR) { + dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n"); + cs35l41_boost_enable(cs35l41, 0); + cs35l41_error_release(cs35l41, CS35L41_BST_OVP_ERR, CS35L41_BST_OVP_ERR_RLS); + cs35l41_boost_enable(cs35l41, 1); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L41_BST_DCM_UVP_ERR) { + dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n"); + cs35l41_boost_enable(cs35l41, 0); + cs35l41_error_release(cs35l41, CS35L41_BST_DCM_UVP_ERR, CS35L41_BST_UVP_ERR_RLS); + cs35l41_boost_enable(cs35l41, 1); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L41_BST_SHORT_ERR) { + dev_crit_ratelimited(cs35l41->dev, "LBST error: powering off!\n"); + cs35l41_boost_enable(cs35l41, 0); + cs35l41_error_release(cs35l41, CS35L41_BST_SHORT_ERR, CS35L41_BST_SHORT_ERR_RLS); + cs35l41_boost_enable(cs35l41, 1); + ret = IRQ_HANDLED; + } + + if (status[2] & CS35L41_PLL_LOCK) { + regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS3, CS35L41_PLL_LOCK); + + if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV || + cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS) { + ret = cs35l41_mdsync_up(cs35l41->regmap); + if (ret) + dev_err(cs35l41->dev, "MDSYNC-up failed: %d\n", ret); + else + dev_dbg(cs35l41->dev, "MDSYNC-up done\n"); + + dev_dbg(cs35l41->dev, "PUP-done status: %d\n", + !!(status[0] & CS35L41_PUP_DONE_MASK)); + } + + ret = IRQ_HANDLED; + } + +done: + pm_runtime_mark_last_busy(cs35l41->dev); + pm_runtime_put_autosuspend(cs35l41->dev); + + return ret; +} + +static const struct reg_sequence cs35l41_pup_patch[] = { + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, + { 0x00002084, 0x002F1AA0 }, + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, +}; + +static const struct reg_sequence cs35l41_pdn_patch[] = { + { CS35L41_TEST_KEY_CTL, 0x00000055 }, + { CS35L41_TEST_KEY_CTL, 0x000000AA }, + { 0x00002084, 0x002F1AA3 }, + { CS35L41_TEST_KEY_CTL, 0x000000CC }, + { CS35L41_TEST_KEY_CTL, 0x00000033 }, +}; + +static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_multi_reg_write_bypassed(cs35l41->regmap, + cs35l41_pup_patch, + ARRAY_SIZE(cs35l41_pup_patch)); + + ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type, + 1, cs35l41->dsp.cs_dsp.running); + break; + case SND_SOC_DAPM_POST_PMD: + ret = cs35l41_global_enable(cs35l41->dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type, + 0, cs35l41->dsp.cs_dsp.running); + + regmap_multi_reg_write_bypassed(cs35l41->regmap, + cs35l41_pdn_patch, + ARRAY_SIZE(cs35l41_pdn_patch)); + break; + default: + dev_err(cs35l41->dev, "Invalid event = 0x%x\n", event); + ret = -EINVAL; + } + + return ret; +} + +static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { + SND_SOC_DAPM_SPK("DSP1 Preload", NULL), + SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0, + cs35l41_dsp_preload_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, + cs35l41_dsp_audio_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("SPK"), + + SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, CS35L41_SP_ENABLES, 16, 0), + SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 0, CS35L41_SP_ENABLES, 17, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L41_SP_ENABLES, 0, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 0, CS35L41_SP_ENABLES, 1, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 0, CS35L41_SP_ENABLES, 2, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 0, CS35L41_SP_ENABLES, 3, 0), + + SND_SOC_DAPM_SIGGEN("VSENSE"), + SND_SOC_DAPM_SIGGEN("ISENSE"), + SND_SOC_DAPM_SIGGEN("VP"), + SND_SOC_DAPM_SIGGEN("VBST"), + SND_SOC_DAPM_SIGGEN("TEMP"), + + SND_SOC_DAPM_SUPPLY("VMON", CS35L41_PWR_CTRL2, 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("IMON", CS35L41_PWR_CTRL2, 13, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VPMON", CS35L41_PWR_CTRL2, 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VBSTMON", CS35L41_PWR_CTRL2, 9, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TEMPMON", CS35L41_PWR_CTRL2, 10, 0, NULL, 0), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("IMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("TEMPMON ADC", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L41_PWR_CTRL3, 4, 0), + + SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L41_PWR_CTRL2, 0, 0, NULL, 0, + cs35l41_main_amp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_MUX("ASP TX1 Source", SND_SOC_NOPM, 0, 0, &asp_tx1_mux), + SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux), + SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux), + SND_SOC_DAPM_MUX("ASP TX4 Source", SND_SOC_NOPM, 0, 0, &asp_tx4_mux), + SND_SOC_DAPM_MUX("DSP RX1 Source", SND_SOC_NOPM, 0, 0, &dsp_rx1_mux), + SND_SOC_DAPM_MUX("DSP RX2 Source", SND_SOC_NOPM, 0, 0, &dsp_rx2_mux), + SND_SOC_DAPM_MUX("PCM Source", SND_SOC_NOPM, 0, 0, &pcm_source_mux), + SND_SOC_DAPM_SWITCH("DRE", SND_SOC_NOPM, 0, 0, &dre_ctrl), +}; + +static const struct snd_soc_dapm_route cs35l41_audio_map[] = { + {"DSP RX1 Source", "ASPRX1", "ASPRX1"}, + {"DSP RX1 Source", "ASPRX2", "ASPRX2"}, + {"DSP RX2 Source", "ASPRX1", "ASPRX1"}, + {"DSP RX2 Source", "ASPRX2", "ASPRX2"}, + + {"DSP1", NULL, "DSP RX1 Source"}, + {"DSP1", NULL, "DSP RX2 Source"}, + + {"ASP TX1 Source", "VMON", "VMON ADC"}, + {"ASP TX1 Source", "IMON", "IMON ADC"}, + {"ASP TX1 Source", "VPMON", "VPMON ADC"}, + {"ASP TX1 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX1 Source", "DSPTX1", "DSP1"}, + {"ASP TX1 Source", "DSPTX2", "DSP1"}, + {"ASP TX1 Source", "ASPRX1", "ASPRX1" }, + {"ASP TX1 Source", "ASPRX2", "ASPRX2" }, + {"ASP TX2 Source", "VMON", "VMON ADC"}, + {"ASP TX2 Source", "IMON", "IMON ADC"}, + {"ASP TX2 Source", "VPMON", "VPMON ADC"}, + {"ASP TX2 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX2 Source", "DSPTX1", "DSP1"}, + {"ASP TX2 Source", "DSPTX2", "DSP1"}, + {"ASP TX2 Source", "ASPRX1", "ASPRX1" }, + {"ASP TX2 Source", "ASPRX2", "ASPRX2" }, + {"ASP TX3 Source", "VMON", "VMON ADC"}, + {"ASP TX3 Source", "IMON", "IMON ADC"}, + {"ASP TX3 Source", "VPMON", "VPMON ADC"}, + {"ASP TX3 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX3 Source", "DSPTX1", "DSP1"}, + {"ASP TX3 Source", "DSPTX2", "DSP1"}, + {"ASP TX3 Source", "ASPRX1", "ASPRX1" }, + {"ASP TX3 Source", "ASPRX2", "ASPRX2" }, + {"ASP TX4 Source", "VMON", "VMON ADC"}, + {"ASP TX4 Source", "IMON", "IMON ADC"}, + {"ASP TX4 Source", "VPMON", "VPMON ADC"}, + {"ASP TX4 Source", "VBSTMON", "VBSTMON ADC"}, + {"ASP TX4 Source", "DSPTX1", "DSP1"}, + {"ASP TX4 Source", "DSPTX2", "DSP1"}, + {"ASP TX4 Source", "ASPRX1", "ASPRX1" }, + {"ASP TX4 Source", "ASPRX2", "ASPRX2" }, + {"ASPTX1", NULL, "ASP TX1 Source"}, + {"ASPTX2", NULL, "ASP TX2 Source"}, + {"ASPTX3", NULL, "ASP TX3 Source"}, + {"ASPTX4", NULL, "ASP TX4 Source"}, + {"AMP Capture", NULL, "ASPTX1"}, + {"AMP Capture", NULL, "ASPTX2"}, + {"AMP Capture", NULL, "ASPTX3"}, + {"AMP Capture", NULL, "ASPTX4"}, + + {"DSP1", NULL, "VMON"}, + {"DSP1", NULL, "IMON"}, + {"DSP1", NULL, "VPMON"}, + {"DSP1", NULL, "VBSTMON"}, + {"DSP1", NULL, "TEMPMON"}, + + {"VMON ADC", NULL, "VMON"}, + {"IMON ADC", NULL, "IMON"}, + {"VPMON ADC", NULL, "VPMON"}, + {"VBSTMON ADC", NULL, "VBSTMON"}, + {"TEMPMON ADC", NULL, "TEMPMON"}, + + {"VMON ADC", NULL, "VSENSE"}, + {"IMON ADC", NULL, "ISENSE"}, + {"VPMON ADC", NULL, "VP"}, + {"VBSTMON ADC", NULL, "VBST"}, + {"TEMPMON ADC", NULL, "TEMP"}, + + {"DSP1 Preload", NULL, "DSP1 Preloader"}, + {"DSP1", NULL, "DSP1 Preloader"}, + + {"ASPRX1", NULL, "AMP Playback"}, + {"ASPRX2", NULL, "AMP Playback"}, + {"DRE", "Switch", "CLASS H"}, + {"Main AMP", NULL, "CLASS H"}, + {"Main AMP", NULL, "DRE"}, + {"SPK", NULL, "Main AMP"}, + + {"PCM Source", "ASP", "ASPRX1"}, + {"PCM Source", "DSP", "DSP1"}, + {"CLASS H", NULL, "PCM Source"}, +}; + +static int cs35l41_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_n, + unsigned int *tx_slot, unsigned int rx_n, unsigned int *rx_slot) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component); + + return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_n, tx_slot, rx_n, rx_slot); +} + +static int cs35l41_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component); + unsigned int daifmt = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + daifmt |= CS35L41_SCLK_MSTR_MASK | CS35L41_LRCLK_MSTR_MASK; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + dev_warn(cs35l41->dev, "Mixed provider/consumer mode unsupported\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + break; + case SND_SOC_DAIFMT_I2S: + daifmt |= 2 << CS35L41_ASP_FMT_SHIFT; + break; + default: + dev_warn(cs35l41->dev, "Invalid or unsupported DAI format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + daifmt |= CS35L41_LRCLK_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + daifmt |= CS35L41_SCLK_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + daifmt |= CS35L41_LRCLK_INV_MASK | CS35L41_SCLK_INV_MASK; + break; + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_warn(cs35l41->dev, "Invalid DAI clock INV\n"); + return -EINVAL; + } + + return regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT, + CS35L41_SCLK_MSTR_MASK | CS35L41_LRCLK_MSTR_MASK | + CS35L41_ASP_FMT_MASK | CS35L41_LRCLK_INV_MASK | + CS35L41_SCLK_INV_MASK, daifmt); +} + +struct cs35l41_global_fs_config { + int rate; + int fs_cfg; +}; + +static const struct cs35l41_global_fs_config cs35l41_fs_rates[] = { + { 12000, 0x01 }, + { 24000, 0x02 }, + { 48000, 0x03 }, + { 96000, 0x04 }, + { 192000, 0x05 }, + { 11025, 0x09 }, + { 22050, 0x0A }, + { 44100, 0x0B }, + { 88200, 0x0C }, + { 176400, 0x0D }, + { 8000, 0x11 }, + { 16000, 0x12 }, + { 32000, 0x13 }, +}; + +static int cs35l41_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component); + unsigned int rate = params_rate(params); + u8 asp_wl; + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l41_fs_rates); i++) { + if (rate == cs35l41_fs_rates[i].rate) + break; + } + + if (i >= ARRAY_SIZE(cs35l41_fs_rates)) { + dev_err(cs35l41->dev, "Unsupported rate: %u\n", rate); + return -EINVAL; + } + + asp_wl = params_width(params); + + if (i < ARRAY_SIZE(cs35l41_fs_rates)) + regmap_update_bits(cs35l41->regmap, CS35L41_GLOBAL_CLK_CTRL, + CS35L41_GLOBAL_FS_MASK, + cs35l41_fs_rates[i].fs_cfg << CS35L41_GLOBAL_FS_SHIFT); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT, + CS35L41_ASP_WIDTH_RX_MASK, + asp_wl << CS35L41_ASP_WIDTH_RX_SHIFT); + regmap_update_bits(cs35l41->regmap, CS35L41_SP_RX_WL, + CS35L41_ASP_RX_WL_MASK, + asp_wl << CS35L41_ASP_RX_WL_SHIFT); + } else { + regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT, + CS35L41_ASP_WIDTH_TX_MASK, + asp_wl << CS35L41_ASP_WIDTH_TX_SHIFT); + regmap_update_bits(cs35l41->regmap, CS35L41_SP_TX_WL, + CS35L41_ASP_TX_WL_MASK, + asp_wl << CS35L41_ASP_TX_WL_SHIFT); + } + + return 0; +} + +static int cs35l41_get_clk_config(int freq) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l41_pll_sysclk); i++) { + if (cs35l41_pll_sysclk[i].freq == freq) + return cs35l41_pll_sysclk[i].clk_cfg; + } + + return -EINVAL; +} + +static const unsigned int cs35l41_src_rates[] = { + 8000, 12000, 11025, 16000, 22050, 24000, 32000, + 44100, 48000, 88200, 96000, 176400, 192000 +}; + +static const struct snd_pcm_hw_constraint_list cs35l41_constraints = { + .count = ARRAY_SIZE(cs35l41_src_rates), + .list = cs35l41_src_rates, +}; + +static int cs35l41_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (substream->runtime) + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &cs35l41_constraints); + return 0; +} + +static int cs35l41_component_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, + unsigned int freq, int dir) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + int extclk_cfg, clksrc; + + switch (clk_id) { + case CS35L41_CLKID_SCLK: + clksrc = CS35L41_PLLSRC_SCLK; + break; + case CS35L41_CLKID_LRCLK: + clksrc = CS35L41_PLLSRC_LRCLK; + break; + case CS35L41_CLKID_MCLK: + clksrc = CS35L41_PLLSRC_MCLK; + break; + default: + dev_err(cs35l41->dev, "Invalid CLK Config\n"); + return -EINVAL; + } + + extclk_cfg = cs35l41_get_clk_config(freq); + + if (extclk_cfg < 0) { + dev_err(cs35l41->dev, "Invalid CLK Config: %d, freq: %u\n", + extclk_cfg, freq); + return -EINVAL; + } + + regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL, + CS35L41_PLL_OPENLOOP_MASK, + 1 << CS35L41_PLL_OPENLOOP_SHIFT); + regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL, + CS35L41_REFCLK_FREQ_MASK, + extclk_cfg << CS35L41_REFCLK_FREQ_SHIFT); + regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL, + CS35L41_PLL_CLK_EN_MASK, + 0 << CS35L41_PLL_CLK_EN_SHIFT); + regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL, + CS35L41_PLL_CLK_SEL_MASK, clksrc); + regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL, + CS35L41_PLL_OPENLOOP_MASK, + 0 << CS35L41_PLL_OPENLOOP_SHIFT); + regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL, + CS35L41_PLL_CLK_EN_MASK, + 1 << CS35L41_PLL_CLK_EN_SHIFT); + + return 0; +} + +static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component); + unsigned int fs1_val; + unsigned int fs2_val; + unsigned int val; + int fsindex; + + fsindex = cs35l41_get_fs_mon_config_index(freq); + if (fsindex < 0) { + dev_err(cs35l41->dev, "Invalid CLK Config freq: %u\n", freq); + return -EINVAL; + } + + dev_dbg(cs35l41->dev, "Set DAI sysclk %d\n", freq); + + if (freq <= 6144000) { + /* Use the lookup table */ + fs1_val = cs35l41_fs_mon[fsindex].fs1; + fs2_val = cs35l41_fs_mon[fsindex].fs2; + } else { + /* Use hard-coded values */ + fs1_val = 0x10; + fs2_val = 0x24; + } + + val = fs1_val; + val |= (fs2_val << CS35L41_FS2_WINDOW_SHIFT) & CS35L41_FS2_WINDOW_MASK; + regmap_write(cs35l41->regmap, CS35L41_TST_FS_MON0, val); + + return 0; +} + +static int cs35l41_set_pdata(struct cs35l41_private *cs35l41) +{ + struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; + int ret; + + if (!hw_cfg->valid) + return -EINVAL; + + if (hw_cfg->bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) + return -EINVAL; + + /* Required */ + ret = cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, hw_cfg); + if (ret) + return ret; + + /* Optional */ + if (hw_cfg->dout_hiz <= CS35L41_ASP_DOUT_HIZ_MASK && hw_cfg->dout_hiz >= 0) + regmap_update_bits(cs35l41->regmap, CS35L41_SP_HIZ_CTRL, CS35L41_ASP_DOUT_HIZ_MASK, + hw_cfg->dout_hiz); + + return 0; +} + +static const struct snd_soc_dapm_route cs35l41_ext_bst_routes[] = { + {"Main AMP", NULL, "VSPK"}, +}; + +static const struct snd_soc_dapm_widget cs35l41_ext_bst_widget[] = { + SND_SOC_DAPM_SUPPLY("VSPK", CS35L41_GPIO1_CTRL1, CS35L41_GPIO_LVL_SHIFT, 0, NULL, 0), +}; + +static int cs35l41_component_probe(struct snd_soc_component *component) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST) { + ret = snd_soc_dapm_new_controls(dapm, cs35l41_ext_bst_widget, + ARRAY_SIZE(cs35l41_ext_bst_widget)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, cs35l41_ext_bst_routes, + ARRAY_SIZE(cs35l41_ext_bst_routes)); + if (ret) + return ret; + } + + return wm_adsp2_component_probe(&cs35l41->dsp, component); +} + +static void cs35l41_component_remove(struct snd_soc_component *component) +{ + struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(component); + + wm_adsp2_component_remove(&cs35l41->dsp, component); +} + +static const struct snd_soc_dai_ops cs35l41_ops = { + .startup = cs35l41_pcm_startup, + .set_fmt = cs35l41_set_dai_fmt, + .hw_params = cs35l41_pcm_hw_params, + .set_sysclk = cs35l41_dai_set_sysclk, + .set_channel_map = cs35l41_set_channel_map, +}; + +static struct snd_soc_dai_driver cs35l41_dai[] = { + { + .name = "cs35l41-pcm", + .id = 0, + .playback = { + .stream_name = "AMP Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L41_RX_FORMATS, + }, + .capture = { + .stream_name = "AMP Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L41_TX_FORMATS, + }, + .ops = &cs35l41_ops, + .symmetric_rate = 1, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_cs35l41 = { + .name = "cs35l41-codec", + .probe = cs35l41_component_probe, + .remove = cs35l41_component_remove, + + .dapm_widgets = cs35l41_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets), + .dapm_routes = cs35l41_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l41_audio_map), + + .controls = cs35l41_aud_controls, + .num_controls = ARRAY_SIZE(cs35l41_aud_controls), + .set_sysclk = cs35l41_component_set_sysclk, + + .endianness = 1, +}; + +static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cfg) +{ + struct cs35l41_gpio_cfg *gpio1 = &hw_cfg->gpio1; + struct cs35l41_gpio_cfg *gpio2 = &hw_cfg->gpio2; + unsigned int val; + int ret; + + /* Some ACPI systems received the Shared Boost feature before the upstream driver, + * leaving those systems with deprecated _DSD properties. + * To correctly configure those systems add shared-boost-active and shared-boost-passive + * properties mapped to the correct value in boost-type. + * These two are not DT properties and should not be used in new systems designs. + */ + if (device_property_read_bool(dev, "cirrus,shared-boost-active")) { + hw_cfg->bst_type = CS35L41_SHD_BOOST_ACTV; + } else if (device_property_read_bool(dev, "cirrus,shared-boost-passive")) { + hw_cfg->bst_type = CS35L41_SHD_BOOST_PASS; + } else { + ret = device_property_read_u32(dev, "cirrus,boost-type", &val); + if (ret >= 0) + hw_cfg->bst_type = val; + } + + ret = device_property_read_u32(dev, "cirrus,boost-peak-milliamp", &val); + if (ret >= 0) + hw_cfg->bst_ipk = val; + else + hw_cfg->bst_ipk = -1; + + ret = device_property_read_u32(dev, "cirrus,boost-ind-nanohenry", &val); + if (ret >= 0) + hw_cfg->bst_ind = val; + else + hw_cfg->bst_ind = -1; + + ret = device_property_read_u32(dev, "cirrus,boost-cap-microfarad", &val); + if (ret >= 0) + hw_cfg->bst_cap = val; + else + hw_cfg->bst_cap = -1; + + ret = device_property_read_u32(dev, "cirrus,asp-sdout-hiz", &val); + if (ret >= 0) + hw_cfg->dout_hiz = val; + else + hw_cfg->dout_hiz = -1; + + /* GPIO1 Pin Config */ + gpio1->pol_inv = device_property_read_bool(dev, "cirrus,gpio1-polarity-invert"); + gpio1->out_en = device_property_read_bool(dev, "cirrus,gpio1-output-enable"); + ret = device_property_read_u32(dev, "cirrus,gpio1-src-select", &val); + if (ret >= 0) { + gpio1->func = val; + gpio1->valid = true; + } + + /* GPIO2 Pin Config */ + gpio2->pol_inv = device_property_read_bool(dev, "cirrus,gpio2-polarity-invert"); + gpio2->out_en = device_property_read_bool(dev, "cirrus,gpio2-output-enable"); + ret = device_property_read_u32(dev, "cirrus,gpio2-src-select", &val); + if (ret >= 0) { + gpio2->func = val; + gpio2->valid = true; + } + + hw_cfg->valid = true; + + return 0; +} + +static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) +{ + struct wm_adsp *dsp; + int ret; + + dsp = &cs35l41->dsp; + dsp->part = "cs35l41"; + dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */ + dsp->toggle_preload = true; + + cs35l41_configure_cs_dsp(cs35l41->dev, cs35l41->regmap, &dsp->cs_dsp); + + ret = cs35l41_write_fs_errata(cs35l41->dev, cs35l41->regmap); + if (ret < 0) + return ret; + + ret = wm_halo_init(dsp); + if (ret) { + dev_err(cs35l41->dev, "wm_halo_init failed: %d\n", ret); + return ret; + } + + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, + CS35L41_INPUT_SRC_VPMON); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_VPMON failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, + CS35L41_INPUT_SRC_CLASSH); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_CLASSH failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC, + CS35L41_INPUT_SRC_TEMPMON); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_TEMPMON failed: %d\n", ret); + goto err_dsp; + } + ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX8_SRC, + CS35L41_INPUT_SRC_RSVD); + if (ret < 0) { + dev_err(cs35l41->dev, "Write INPUT_SRC_RSVD failed: %d\n", ret); + goto err_dsp; + } + + return 0; + +err_dsp: + wm_adsp2_remove(dsp); + + return ret; +} + +static int cs35l41_acpi_get_name(struct cs35l41_private *cs35l41) +{ + acpi_handle handle = ACPI_HANDLE(cs35l41->dev); + const char *sub; + + /* If there is no ACPI_HANDLE, there is no ACPI for this system, return 0 */ + if (!handle) + return 0; + + sub = acpi_get_subsystem_id(handle); + if (IS_ERR(sub)) { + /* If bad ACPI, return 0 and fallback to legacy firmware path, otherwise fail */ + if (PTR_ERR(sub) == -ENODATA) + return 0; + else + return PTR_ERR(sub); + } + + cs35l41->dsp.system_name = sub; + dev_dbg(cs35l41->dev, "Subsystem ID: %s\n", cs35l41->dsp.system_name); + + return 0; +} + +int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg) +{ + u32 regid, reg_revid, i, mtl_revid, int_status, chipid_match; + int irq_pol = 0; + int ret; + + if (hw_cfg) { + cs35l41->hw_cfg = *hw_cfg; + } else { + ret = cs35l41_handle_pdata(cs35l41->dev, &cs35l41->hw_cfg); + if (ret != 0) + return ret; + } + + for (i = 0; i < CS35L41_NUM_SUPPLIES; i++) + cs35l41->supplies[i].supply = cs35l41_supplies[i]; + + ret = devm_regulator_bulk_get(cs35l41->dev, CS35L41_NUM_SUPPLIES, + cs35l41->supplies); + if (ret != 0) { + dev_err(cs35l41->dev, "Failed to request core supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); + if (ret != 0) { + dev_err(cs35l41->dev, "Failed to enable core supplies: %d\n", ret); + return ret; + } + + /* returning NULL can be an option if in stereo mode */ + cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs35l41->reset_gpio)) { + ret = PTR_ERR(cs35l41->reset_gpio); + cs35l41->reset_gpio = NULL; + if (ret == -EBUSY) { + dev_info(cs35l41->dev, + "Reset line busy, assuming shared reset\n"); + } else { + dev_err(cs35l41->dev, + "Failed to get reset GPIO: %d\n", ret); + goto err; + } + } + if (cs35l41->reset_gpio) { + /* satisfy minimum reset pulse width spec */ + usleep_range(2000, 2100); + gpiod_set_value_cansleep(cs35l41->reset_gpio, 1); + } + + usleep_range(2000, 2100); + + ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4, + int_status, int_status & CS35L41_OTP_BOOT_DONE, + 1000, 100000); + if (ret) { + dev_err(cs35l41->dev, + "Failed waiting for OTP_BOOT_DONE: %d\n", ret); + goto err; + } + + regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_status); + if (int_status & CS35L41_OTP_BOOT_ERR) { + dev_err(cs35l41->dev, "OTP Boot error\n"); + ret = -EINVAL; + goto err; + } + + ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, ®id); + if (ret < 0) { + dev_err(cs35l41->dev, "Get Device ID failed: %d\n", ret); + goto err; + } + + ret = regmap_read(cs35l41->regmap, CS35L41_REVID, ®_revid); + if (ret < 0) { + dev_err(cs35l41->dev, "Get Revision ID failed: %d\n", ret); + goto err; + } + + mtl_revid = reg_revid & CS35L41_MTLREVID_MASK; + + /* CS35L41 will have even MTLREVID + * CS35L41R will have odd MTLREVID + */ + chipid_match = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID; + if (regid != chipid_match) { + dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n", + regid, chipid_match); + ret = -ENODEV; + goto err; + } + + cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); + + ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid); + if (ret) + goto err; + + ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap); + if (ret < 0) { + dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret); + goto err; + } + + cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); + + irq_pol = cs35l41_gpio_config(cs35l41->regmap, &cs35l41->hw_cfg); + + /* Set interrupt masks for critical errors */ + regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, + CS35L41_INT1_MASK_DEFAULT); + if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS || + cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV) + regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK, + 0 << CS35L41_INT3_PLL_LOCK_SHIFT); + + ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq, + IRQF_ONESHOT | IRQF_SHARED | irq_pol, + "cs35l41", cs35l41); + if (ret != 0) { + dev_err(cs35l41->dev, "Failed to request IRQ: %d\n", ret); + goto err; + } + + ret = cs35l41_set_pdata(cs35l41); + if (ret < 0) { + dev_err(cs35l41->dev, "Set pdata failed: %d\n", ret); + goto err; + } + + ret = cs35l41_acpi_get_name(cs35l41); + if (ret < 0) + goto err; + + ret = cs35l41_dsp_init(cs35l41); + if (ret < 0) + goto err; + + pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000); + pm_runtime_use_autosuspend(cs35l41->dev); + pm_runtime_mark_last_busy(cs35l41->dev); + pm_runtime_set_active(cs35l41->dev); + pm_runtime_get_noresume(cs35l41->dev); + pm_runtime_enable(cs35l41->dev); + + ret = devm_snd_soc_register_component(cs35l41->dev, + &soc_component_dev_cs35l41, + cs35l41_dai, ARRAY_SIZE(cs35l41_dai)); + if (ret < 0) { + dev_err(cs35l41->dev, "Register codec failed: %d\n", ret); + goto err_pm; + } + + pm_runtime_put_autosuspend(cs35l41->dev); + + dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", + regid, reg_revid); + + return 0; + +err_pm: + pm_runtime_dont_use_autosuspend(cs35l41->dev); + pm_runtime_disable(cs35l41->dev); + pm_runtime_put_noidle(cs35l41->dev); + + wm_adsp2_remove(&cs35l41->dsp); +err: + cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type); + regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); + gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l41_probe); + +void cs35l41_remove(struct cs35l41_private *cs35l41) +{ + pm_runtime_get_sync(cs35l41->dev); + pm_runtime_dont_use_autosuspend(cs35l41->dev); + pm_runtime_disable(cs35l41->dev); + + regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF); + if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS || + cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV) + regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK, + 1 << CS35L41_INT3_PLL_LOCK_SHIFT); + kfree(cs35l41->dsp.system_name); + wm_adsp2_remove(&cs35l41->dsp); + cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type); + + pm_runtime_put_noidle(cs35l41->dev); + + regulator_bulk_disable(CS35L41_NUM_SUPPLIES, cs35l41->supplies); + gpiod_set_value_cansleep(cs35l41->reset_gpio, 0); +} +EXPORT_SYMBOL_GPL(cs35l41_remove); + +static int __maybe_unused cs35l41_runtime_suspend(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Runtime suspend\n"); + + if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) + return 0; + + cs35l41_enter_hibernate(dev, cs35l41->regmap, cs35l41->hw_cfg.bst_type); + + regcache_cache_only(cs35l41->regmap, true); + regcache_mark_dirty(cs35l41->regmap); + + return 0; +} + +static int __maybe_unused cs35l41_runtime_resume(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + int ret; + + dev_dbg(cs35l41->dev, "Runtime resume\n"); + + if (!cs35l41->dsp.preloaded || !cs35l41->dsp.cs_dsp.running) + return 0; + + regcache_cache_only(cs35l41->regmap, false); + + ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap); + if (ret) + return ret; + + /* Test key needs to be unlocked to allow the OTP settings to re-apply */ + cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap); + ret = regcache_sync(cs35l41->regmap); + cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap); + if (ret) { + dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret); + return ret; + } + cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg); + + return 0; +} + +static int __maybe_unused cs35l41_sys_suspend(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "System suspend, disabling IRQ\n"); + disable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_suspend_noirq(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Late system suspend, reenabling IRQ\n"); + enable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_resume_noirq(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "Early system resume, disabling IRQ\n"); + disable_irq(cs35l41->irq); + + return 0; +} + +static int __maybe_unused cs35l41_sys_resume(struct device *dev) +{ + struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); + + dev_dbg(cs35l41->dev, "System resume, reenabling IRQ\n"); + enable_irq(cs35l41->irq); + + return 0; +} + +const struct dev_pm_ops cs35l41_pm_ops = { + SET_RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL) + + SET_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq) +}; +EXPORT_SYMBOL_GPL(cs35l41_pm_ops); + +MODULE_DESCRIPTION("ASoC CS35L41 driver"); +MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h new file mode 100644 index 0000000000..c85cbc1dd3 --- /dev/null +++ b/sound/soc/codecs/cs35l41.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * cs35l41.h -- CS35L41 ALSA SoC audio driver + * + * Copyright 2017-2021 Cirrus Logic, Inc. + * + * Author: David Rhodes + */ + +#ifndef __CS35L41_H__ +#define __CS35L41_H__ + +#include +#include +#include +#include +#include + +#include "wm_adsp.h" + +#define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +extern const struct dev_pm_ops cs35l41_pm_ops; + +struct cs35l41_private { + struct wm_adsp dsp; /* needs to be first member */ + struct snd_soc_codec *codec; + struct cs35l41_hw_cfg hw_cfg; + struct device *dev; + struct regmap *regmap; + struct regulator_bulk_data supplies[CS35L41_NUM_SUPPLIES]; + int irq; + /* GPIO for /RST */ + struct gpio_desc *reset_gpio; +}; + +int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg); +void cs35l41_remove(struct cs35l41_private *cs35l41); + +#endif /*__CS35L41_H__*/ diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c new file mode 100644 index 0000000000..bc2af1ed0f --- /dev/null +++ b/sound/soc/codecs/cs35l45-i2c.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l45-i2c.c -- CS35L45 I2C driver +// +// Copyright 2019-2022 Cirrus Logic, Inc. +// +// Author: James Schulman + +#include +#include +#include +#include + +#include "cs35l45.h" + +static int cs35l45_i2c_probe(struct i2c_client *client) +{ + struct cs35l45_private *cs35l45; + struct device *dev = &client->dev; + int ret; + + cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL); + if (!cs35l45) + return -ENOMEM; + + i2c_set_clientdata(client, cs35l45); + cs35l45->regmap = devm_regmap_init_i2c(client, &cs35l45_i2c_regmap); + if (IS_ERR(cs35l45->regmap)) { + ret = PTR_ERR(cs35l45->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + cs35l45->dev = dev; + cs35l45->irq = client->irq; + cs35l45->bus_type = CONTROL_BUS_I2C; + cs35l45->i2c_addr = client->addr; + + return cs35l45_probe(cs35l45); +} + +static void cs35l45_i2c_remove(struct i2c_client *client) +{ + struct cs35l45_private *cs35l45 = i2c_get_clientdata(client); + + cs35l45_remove(cs35l45); +} + +static const struct of_device_id cs35l45_of_match[] = { + { .compatible = "cirrus,cs35l45" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l45_of_match); + +static const struct i2c_device_id cs35l45_id_i2c[] = { + { "cs35l45", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs35l45_id_i2c); + +static struct i2c_driver cs35l45_i2c_driver = { + .driver = { + .name = "cs35l45", + .of_match_table = cs35l45_of_match, + .pm = pm_ptr(&cs35l45_pm_ops), + }, + .id_table = cs35l45_id_i2c, + .probe = cs35l45_i2c_probe, + .remove = cs35l45_i2c_remove, +}; +module_i2c_driver(cs35l45_i2c_driver); + +MODULE_DESCRIPTION("I2C CS35L45 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c new file mode 100644 index 0000000000..39e203a5f0 --- /dev/null +++ b/sound/soc/codecs/cs35l45-spi.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l45-spi.c -- CS35L45 SPI driver +// +// Copyright 2019-2022 Cirrus Logic, Inc. +// +// Author: James Schulman + +#include +#include +#include +#include + +#include "cs35l45.h" + +static int cs35l45_spi_probe(struct spi_device *spi) +{ + struct cs35l45_private *cs35l45; + struct device *dev = &spi->dev; + int ret; + + cs35l45 = devm_kzalloc(dev, sizeof(struct cs35l45_private), GFP_KERNEL); + if (cs35l45 == NULL) + return -ENOMEM; + + spi->max_speed_hz = CS35L45_SPI_MAX_FREQ; + spi_setup(spi); + + spi_set_drvdata(spi, cs35l45); + cs35l45->regmap = devm_regmap_init_spi(spi, &cs35l45_spi_regmap); + if (IS_ERR(cs35l45->regmap)) { + ret = PTR_ERR(cs35l45->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + cs35l45->dev = dev; + cs35l45->irq = spi->irq; + cs35l45->bus_type = CONTROL_BUS_SPI; + + return cs35l45_probe(cs35l45); +} + +static void cs35l45_spi_remove(struct spi_device *spi) +{ + struct cs35l45_private *cs35l45 = spi_get_drvdata(spi); + + cs35l45_remove(cs35l45); +} + +static const struct of_device_id cs35l45_of_match[] = { + { .compatible = "cirrus,cs35l45" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l45_of_match); + +static const struct spi_device_id cs35l45_id_spi[] = { + { "cs35l45", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, cs35l45_id_spi); + +static struct spi_driver cs35l45_spi_driver = { + .driver = { + .name = "cs35l45", + .of_match_table = cs35l45_of_match, + .pm = pm_ptr(&cs35l45_pm_ops), + }, + .id_table = cs35l45_id_spi, + .probe = cs35l45_spi_probe, + .remove = cs35l45_spi_remove, +}; +module_spi_driver(cs35l45_spi_driver); + +MODULE_DESCRIPTION("SPI CS35L45 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c new file mode 100644 index 0000000000..621af17859 --- /dev/null +++ b/sound/soc/codecs/cs35l45-tables.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l45-tables.c -- CS35L45 ALSA SoC audio driver +// +// Copyright 2019-2022 Cirrus Logic, Inc. +// +// Author: James Schulman + +#include +#include + +#include "cs35l45.h" + +static const struct reg_sequence cs35l45_patch[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00000044, 0x00000055 }, + { 0x00000044, 0x000000AA }, + { 0x00006480, 0x0830500A }, + { 0x00007C60, 0x1000850B }, + { CS35L45_BOOST_OV_CFG, 0x007000D0 }, + { CS35L45_LDPM_CONFIG, 0x0001B636 }, + { 0x00002C08, 0x00000009 }, + { 0x00006850, 0x0A30FFC4 }, + { 0x00003820, 0x00040100 }, + { 0x00003824, 0x00000000 }, + { 0x00007CFC, 0x62870004 }, + { 0x00007C60, 0x1001850B }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000000 }, + { CS35L45_BOOST_CCM_CFG, 0xF0000003 }, + { CS35L45_BOOST_DCM_CFG, 0x08710220 }, + { CS35L45_ERROR_RELEASE, 0x00200000 }, +}; + +int cs35l45_apply_patch(struct cs35l45_private *cs35l45) +{ + return regmap_register_patch(cs35l45->regmap, cs35l45_patch, + ARRAY_SIZE(cs35l45_patch)); +} +EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45); + +static const struct reg_default cs35l45_defaults[] = { + { CS35L45_BLOCK_ENABLES, 0x00003323 }, + { CS35L45_BLOCK_ENABLES2, 0x00000010 }, + { CS35L45_SYNC_GPIO1, 0x00000007 }, + { CS35L45_INTB_GPIO2_MCLK_REF, 0x00000005 }, + { CS35L45_GPIO3, 0x00000005 }, + { CS35L45_PWRMGT_CTL, 0x00000000 }, + { CS35L45_WAKESRC_CTL, 0x00000008 }, + { CS35L45_WKI2C_CTL, 0x00000030 }, + { CS35L45_REFCLK_INPUT, 0x00000510 }, + { CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 }, + { CS35L45_ASP_ENABLES1, 0x00000000 }, + { CS35L45_ASP_CONTROL1, 0x00000028 }, + { CS35L45_ASP_CONTROL2, 0x18180200 }, + { CS35L45_ASP_CONTROL3, 0x00000002 }, + { CS35L45_ASP_FRAME_CONTROL1, 0x03020100 }, + { CS35L45_ASP_FRAME_CONTROL2, 0x00000004 }, + { CS35L45_ASP_FRAME_CONTROL5, 0x00000100 }, + { CS35L45_ASP_DATA_CONTROL1, 0x00000018 }, + { CS35L45_ASP_DATA_CONTROL5, 0x00000018 }, + { CS35L45_DACPCM1_INPUT, 0x00000008 }, + { CS35L45_ASPTX1_INPUT, 0x00000018 }, + { CS35L45_ASPTX2_INPUT, 0x00000019 }, + { CS35L45_ASPTX3_INPUT, 0x00000020 }, + { CS35L45_ASPTX4_INPUT, 0x00000028 }, + { CS35L45_ASPTX5_INPUT, 0x00000048 }, + { CS35L45_DSP1_RX1_RATE, 0x00000001 }, + { CS35L45_DSP1_RX2_RATE, 0x00000001 }, + { CS35L45_DSP1_RX3_RATE, 0x00000001 }, + { CS35L45_DSP1_RX4_RATE, 0x00000001 }, + { CS35L45_DSP1_RX5_RATE, 0x00000001 }, + { CS35L45_DSP1_RX6_RATE, 0x00000001 }, + { CS35L45_DSP1_RX7_RATE, 0x00000001 }, + { CS35L45_DSP1_RX8_RATE, 0x00000001 }, + { CS35L45_DSP1_TX1_RATE, 0x00000001 }, + { CS35L45_DSP1_TX2_RATE, 0x00000001 }, + { CS35L45_DSP1_TX3_RATE, 0x00000001 }, + { CS35L45_DSP1_TX4_RATE, 0x00000001 }, + { CS35L45_DSP1_TX5_RATE, 0x00000001 }, + { CS35L45_DSP1_TX6_RATE, 0x00000001 }, + { CS35L45_DSP1_TX7_RATE, 0x00000001 }, + { CS35L45_DSP1_TX8_RATE, 0x00000001 }, + { CS35L45_DSP1RX1_INPUT, 0x00000008 }, + { CS35L45_DSP1RX2_INPUT, 0x00000009 }, + { CS35L45_DSP1RX3_INPUT, 0x00000018 }, + { CS35L45_DSP1RX4_INPUT, 0x00000019 }, + { CS35L45_DSP1RX5_INPUT, 0x00000020 }, + { CS35L45_DSP1RX6_INPUT, 0x00000028 }, + { CS35L45_DSP1RX7_INPUT, 0x0000003A }, + { CS35L45_DSP1RX8_INPUT, 0x00000028 }, + { CS35L45_AMP_PCM_CONTROL, 0x00100000 }, + { CS35L45_IRQ1_CFG, 0x00000000 }, + { CS35L45_IRQ1_MASK_1, 0xBFEFFFBF }, + { CS35L45_IRQ1_MASK_2, 0xFFFFFFFF }, + { CS35L45_IRQ1_MASK_3, 0xFFFF87FF }, + { CS35L45_IRQ1_MASK_4, 0xF8FFFFFF }, + { CS35L45_IRQ1_MASK_5, 0x0EF80000 }, + { CS35L45_IRQ1_MASK_6, 0x00000000 }, + { CS35L45_IRQ1_MASK_7, 0xFFFFFF78 }, + { CS35L45_IRQ1_MASK_8, 0x00003FFF }, + { CS35L45_IRQ1_MASK_9, 0x00000000 }, + { CS35L45_IRQ1_MASK_10, 0x00000000 }, + { CS35L45_IRQ1_MASK_11, 0x00000000 }, + { CS35L45_IRQ1_MASK_12, 0x00000000 }, + { CS35L45_IRQ1_MASK_13, 0x00000000 }, + { CS35L45_IRQ1_MASK_14, 0x00000001 }, + { CS35L45_IRQ1_MASK_15, 0x00000000 }, + { CS35L45_IRQ1_MASK_16, 0x00000000 }, + { CS35L45_IRQ1_MASK_17, 0x00000000 }, + { CS35L45_IRQ1_MASK_18, 0x3FE5D0FF }, + { CS35L45_GPIO1_CTRL1, 0x81000001 }, + { CS35L45_GPIO2_CTRL1, 0x81000001 }, + { CS35L45_GPIO3_CTRL1, 0x81000001 }, +}; + +static bool cs35l45_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L45_DEVID ... CS35L45_OTPID: + case CS35L45_SFT_RESET: + case CS35L45_GLOBAL_ENABLES: + case CS35L45_BLOCK_ENABLES: + case CS35L45_BLOCK_ENABLES2: + case CS35L45_ERROR_RELEASE: + case CS35L45_SYNC_GPIO1: + case CS35L45_INTB_GPIO2_MCLK_REF: + case CS35L45_GPIO3: + case CS35L45_PWRMGT_CTL: + case CS35L45_WAKESRC_CTL: + case CS35L45_WKI2C_CTL: + case CS35L45_PWRMGT_STS: + case CS35L45_REFCLK_INPUT: + case CS35L45_GLOBAL_SAMPLE_RATE: + case CS35L45_ASP_ENABLES1: + case CS35L45_ASP_CONTROL1: + case CS35L45_ASP_CONTROL2: + case CS35L45_ASP_CONTROL3: + case CS35L45_ASP_FRAME_CONTROL1: + case CS35L45_ASP_FRAME_CONTROL2: + case CS35L45_ASP_FRAME_CONTROL5: + case CS35L45_ASP_DATA_CONTROL1: + case CS35L45_ASP_DATA_CONTROL5: + case CS35L45_DACPCM1_INPUT: + case CS35L45_ASPTX1_INPUT: + case CS35L45_ASPTX2_INPUT: + case CS35L45_ASPTX3_INPUT: + case CS35L45_ASPTX4_INPUT: + case CS35L45_ASPTX5_INPUT: + case CS35L45_DSP1RX1_INPUT: + case CS35L45_DSP1RX2_INPUT: + case CS35L45_DSP1RX3_INPUT: + case CS35L45_DSP1RX4_INPUT: + case CS35L45_DSP1RX5_INPUT: + case CS35L45_DSP1RX6_INPUT: + case CS35L45_DSP1RX7_INPUT: + case CS35L45_DSP1RX8_INPUT: + case CS35L45_AMP_PCM_CONTROL: + case CS35L45_AMP_PCM_HPF_TST: + case CS35L45_IRQ1_CFG: + case CS35L45_IRQ1_STATUS: + case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18: + case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18: + case CS35L45_IRQ1_MASK_1 ... CS35L45_IRQ1_MASK_18: + case CS35L45_GPIO_STATUS1: + case CS35L45_GPIO1_CTRL1: + case CS35L45_GPIO2_CTRL1: + case CS35L45_GPIO3_CTRL1: + case CS35L45_DSP_MBOX_1: + case CS35L45_DSP_MBOX_2: + case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4: + case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4: + case CS35L45_DSP1_SYS_ID: + case CS35L45_DSP1_CLOCK_FREQ: + case CS35L45_DSP1_RX1_RATE: + case CS35L45_DSP1_RX2_RATE: + case CS35L45_DSP1_RX3_RATE: + case CS35L45_DSP1_RX4_RATE: + case CS35L45_DSP1_RX5_RATE: + case CS35L45_DSP1_RX6_RATE: + case CS35L45_DSP1_RX7_RATE: + case CS35L45_DSP1_RX8_RATE: + case CS35L45_DSP1_TX1_RATE: + case CS35L45_DSP1_TX2_RATE: + case CS35L45_DSP1_TX3_RATE: + case CS35L45_DSP1_TX4_RATE: + case CS35L45_DSP1_TX5_RATE: + case CS35L45_DSP1_TX6_RATE: + case CS35L45_DSP1_TX7_RATE: + case CS35L45_DSP1_TX8_RATE: + case CS35L45_DSP1_SCRATCH1: + case CS35L45_DSP1_SCRATCH2: + case CS35L45_DSP1_SCRATCH3: + case CS35L45_DSP1_SCRATCH4: + case CS35L45_DSP1_CCM_CORE_CONTROL: + case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607: + case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071: + case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143: + case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532: + case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022: + case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043: + case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834: + return true; + default: + return false; + } +} + +static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L45_DEVID ... CS35L45_OTPID: + case CS35L45_SFT_RESET: + case CS35L45_GLOBAL_ENABLES: + case CS35L45_ERROR_RELEASE: + case CS35L45_AMP_PCM_HPF_TST: /* not cachable */ + case CS35L45_PWRMGT_STS: + case CS35L45_IRQ1_STATUS: + case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18: + case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18: + case CS35L45_GPIO_STATUS1: + case CS35L45_DSP_MBOX_1: + case CS35L45_DSP_MBOX_2: + case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4: + case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4: + case CS35L45_DSP1_SYS_ID: + case CS35L45_DSP1_CLOCK_FREQ: + case CS35L45_DSP1_SCRATCH1: + case CS35L45_DSP1_SCRATCH2: + case CS35L45_DSP1_SCRATCH3: + case CS35L45_DSP1_SCRATCH4: + case CS35L45_DSP1_CCM_CORE_CONTROL: + case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607: + case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071: + case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143: + case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532: + case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022: + case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043: + case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834: + return true; + default: + return false; + } +} + +const struct regmap_config cs35l45_i2c_regmap = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L45_LASTREG, + .reg_defaults = cs35l45_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults), + .volatile_reg = cs35l45_volatile_reg, + .readable_reg = cs35l45_readable_reg, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45); + +const struct regmap_config cs35l45_spi_regmap = { + .reg_bits = 32, + .val_bits = 32, + .pad_bits = 16, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L45_LASTREG, + .reg_defaults = cs35l45_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l45_defaults), + .volatile_reg = cs35l45_volatile_reg, + .readable_reg = cs35l45_readable_reg, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45); + +static const struct { + u8 cfg_id; + u32 freq; +} cs35l45_pll_refclk_freq[] = { + { 0x0C, 128000 }, + { 0x0F, 256000 }, + { 0x11, 384000 }, + { 0x12, 512000 }, + { 0x15, 768000 }, + { 0x17, 1024000 }, + { 0x19, 1411200 }, + { 0x1B, 1536000 }, + { 0x1C, 2116800 }, + { 0x1D, 2048000 }, + { 0x1E, 2304000 }, + { 0x1F, 2822400 }, + { 0x21, 3072000 }, + { 0x23, 4233600 }, + { 0x24, 4096000 }, + { 0x25, 4608000 }, + { 0x26, 5644800 }, + { 0x27, 6000000 }, + { 0x28, 6144000 }, + { 0x29, 6350400 }, + { 0x2A, 6912000 }, + { 0x2D, 7526400 }, + { 0x2E, 8467200 }, + { 0x2F, 8192000 }, + { 0x30, 9216000 }, + { 0x31, 11289600 }, + { 0x33, 12288000 }, + { 0x37, 16934400 }, + { 0x38, 18432000 }, + { 0x39, 22579200 }, + { 0x3B, 24576000 }, +}; + +unsigned int cs35l45_get_clk_freq_id(unsigned int freq) +{ + int i; + + if (freq == 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(cs35l45_pll_refclk_freq); ++i) { + if (cs35l45_pll_refclk_freq[i].freq == freq) + return cs35l45_pll_refclk_freq[i].cfg_id; + } + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c new file mode 100644 index 0000000000..9b9fc2d491 --- /dev/null +++ b/sound/soc/codecs/cs35l45.c @@ -0,0 +1,1346 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l45.c - CS35L45 ALSA SoC audio driver +// +// Copyright 2019-2022 Cirrus Logic, Inc. +// +// Author: James Schulman + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l45.h" + +static bool cs35l45_check_cspl_mbox_sts(const enum cs35l45_cspl_mboxcmd cmd, + enum cs35l45_cspl_mboxstate sts) +{ + switch (cmd) { + case CSPL_MBOX_CMD_NONE: + case CSPL_MBOX_CMD_UNKNOWN_CMD: + return true; + case CSPL_MBOX_CMD_PAUSE: + case CSPL_MBOX_CMD_OUT_OF_HIBERNATE: + return (sts == CSPL_MBOX_STS_PAUSED); + case CSPL_MBOX_CMD_RESUME: + return (sts == CSPL_MBOX_STS_RUNNING); + case CSPL_MBOX_CMD_REINIT: + return (sts == CSPL_MBOX_STS_RUNNING); + case CSPL_MBOX_CMD_STOP_PRE_REINIT: + return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT); + case CSPL_MBOX_CMD_HIBERNATE: + return (sts == CSPL_MBOX_STS_HIBERNATE); + default: + return false; + } +} + +static int cs35l45_set_cspl_mbox_cmd(struct cs35l45_private *cs35l45, + struct regmap *regmap, + const enum cs35l45_cspl_mboxcmd cmd) +{ + unsigned int sts = 0, i; + int ret; + + if (!cs35l45->dsp.cs_dsp.running) { + dev_err(cs35l45->dev, "DSP not running\n"); + return -EPERM; + } + + // Set mailbox cmd + ret = regmap_write(regmap, CS35L45_DSP_VIRT1_MBOX_1, cmd); + if (ret < 0) { + if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE) + dev_err(cs35l45->dev, "Failed to write MBOX: %d\n", ret); + return ret; + } + + // Read mailbox status and verify it is appropriate for the given cmd + for (i = 0; i < 5; i++) { + usleep_range(1000, 1100); + + ret = regmap_read(regmap, CS35L45_DSP_MBOX_2, &sts); + if (ret < 0) { + dev_err(cs35l45->dev, "Failed to read MBOX STS: %d\n", ret); + continue; + } + + if (!cs35l45_check_cspl_mbox_sts(cmd, sts)) + dev_dbg(cs35l45->dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts); + else + return 0; + } + + if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE) + dev_err(cs35l45->dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts); + + return -ENOMSG; +} + +static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); + + dev_dbg(cs35l45->dev, "%s event : %x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES, + CS35L45_GLOBAL_EN_MASK); + + usleep_range(CS35L45_POST_GLOBAL_EN_US, CS35L45_POST_GLOBAL_EN_US + 100); + break; + case SND_SOC_DAPM_PRE_PMD: + usleep_range(CS35L45_PRE_GLOBAL_DIS_US, CS35L45_PRE_GLOBAL_DIS_US + 100); + + regmap_write(cs35l45->regmap, CS35L45_GLOBAL_ENABLES, 0); + break; + default: + break; + } + + return 0; +} + +static int cs35l45_dsp_preload_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (cs35l45->dsp.cs_dsp.booted) + return 0; + + return wm_adsp_early_event(w, kcontrol, event); + case SND_SOC_DAPM_POST_PMU: + if (cs35l45->dsp.cs_dsp.running) + return 0; + + regmap_set_bits(cs35l45->regmap, CS35L45_PWRMGT_CTL, + CS35L45_MEM_RDY_MASK); + + return wm_adsp_event(w, kcontrol, event); + case SND_SOC_DAPM_PRE_PMD: + if (cs35l45->dsp.preloaded) + return 0; + + if (cs35l45->dsp.cs_dsp.running) { + ret = wm_adsp_event(w, kcontrol, event); + if (ret) + return ret; + } + + return wm_adsp_early_event(w, kcontrol, event); + default: + return 0; + } +} + +static int cs35l45_dsp_audio_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap, + CSPL_MBOX_CMD_RESUME); + case SND_SOC_DAPM_PRE_PMD: + return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap, + CSPL_MBOX_CMD_PAUSE); + default: + return 0; + } + + return 0; +} + +static const char * const cs35l45_asp_tx_txt[] = { + "Zero", "ASP_RX1", "ASP_RX2", + "VMON", "IMON", "ERR_VOL", + "VDD_BATTMON", "VDD_BSTMON", + "DSP_TX1", "DSP_TX2", + "Interpolator", "IL_TARGET", +}; + +static const unsigned int cs35l45_asp_tx_val[] = { + CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2, + CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL, + CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON, + CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2, + CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET, +}; + +static const struct soc_enum cs35l45_asp_tx_enums[] = { + SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX1_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, + cs35l45_asp_tx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX2_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, + cs35l45_asp_tx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX3_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, + cs35l45_asp_tx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX4_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, + cs35l45_asp_tx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_ASPTX5_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_asp_tx_txt), cs35l45_asp_tx_txt, + cs35l45_asp_tx_val), +}; + +static const char * const cs35l45_dsp_rx_txt[] = { + "Zero", "ASP_RX1", "ASP_RX2", + "VMON", "IMON", "ERR_VOL", + "CLASSH_TGT", "VDD_BATTMON", + "VDD_BSTMON", "TEMPMON", +}; + +static const unsigned int cs35l45_dsp_rx_val[] = { + CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2, + CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL, + CS35L45_PCM_SRC_CLASSH_TGT, CS35L45_PCM_SRC_VDD_BATTMON, + CS35L45_PCM_SRC_VDD_BSTMON, CS35L45_PCM_SRC_TEMPMON, +}; + +static const struct soc_enum cs35l45_dsp_rx_enums[] = { + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX1_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX2_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX3_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX4_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX5_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX6_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX7_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), + SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX8_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt, + cs35l45_dsp_rx_val), +}; + +static const char * const cs35l45_dac_txt[] = { + "Zero", "ASP_RX1", "ASP_RX2", "DSP_TX1", "DSP_TX2" +}; + +static const unsigned int cs35l45_dac_val[] = { + CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2, + CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2 +}; + +static const struct soc_enum cs35l45_dacpcm_enums[] = { + SOC_VALUE_ENUM_SINGLE(CS35L45_DACPCM1_INPUT, 0, CS35L45_PCM_SRC_MASK, + ARRAY_SIZE(cs35l45_dac_txt), cs35l45_dac_txt, + cs35l45_dac_val), +}; + +static const struct snd_kcontrol_new cs35l45_asp_muxes[] = { + SOC_DAPM_ENUM("ASP_TX1 Source", cs35l45_asp_tx_enums[0]), + SOC_DAPM_ENUM("ASP_TX2 Source", cs35l45_asp_tx_enums[1]), + SOC_DAPM_ENUM("ASP_TX3 Source", cs35l45_asp_tx_enums[2]), + SOC_DAPM_ENUM("ASP_TX4 Source", cs35l45_asp_tx_enums[3]), + SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]), +}; + +static const struct snd_kcontrol_new cs35l45_dsp_muxes[] = { + SOC_DAPM_ENUM("DSP_RX1 Source", cs35l45_dsp_rx_enums[0]), + SOC_DAPM_ENUM("DSP_RX2 Source", cs35l45_dsp_rx_enums[1]), + SOC_DAPM_ENUM("DSP_RX3 Source", cs35l45_dsp_rx_enums[2]), + SOC_DAPM_ENUM("DSP_RX4 Source", cs35l45_dsp_rx_enums[3]), + SOC_DAPM_ENUM("DSP_RX5 Source", cs35l45_dsp_rx_enums[4]), + SOC_DAPM_ENUM("DSP_RX6 Source", cs35l45_dsp_rx_enums[5]), + SOC_DAPM_ENUM("DSP_RX7 Source", cs35l45_dsp_rx_enums[6]), + SOC_DAPM_ENUM("DSP_RX8 Source", cs35l45_dsp_rx_enums[7]), +}; + +static const struct snd_kcontrol_new cs35l45_dac_muxes[] = { + SOC_DAPM_ENUM("DACPCM Source", cs35l45_dacpcm_enums[0]), +}; + +static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = { + SND_SOC_DAPM_SPK("DSP1 Preload", NULL), + SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0, + cs35l45_dsp_preload_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, + cs35l45_dsp_audio_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0, + cs35l45_global_en_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("ASP_EN", CS35L45_BLOCK_ENABLES2, CS35L45_ASP_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_SIGGEN("VMON_SRC"), + SND_SOC_DAPM_SIGGEN("IMON_SRC"), + SND_SOC_DAPM_SIGGEN("VDD_BATTMON_SRC"), + SND_SOC_DAPM_SIGGEN("VDD_BSTMON_SRC"), + SND_SOC_DAPM_SIGGEN("ERR_VOL"), + SND_SOC_DAPM_SIGGEN("AMP_INTP"), + SND_SOC_DAPM_SIGGEN("IL_TARGET"), + SND_SOC_DAPM_ADC("VMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_VMON_EN_SHIFT, 0), + SND_SOC_DAPM_ADC("IMON", NULL, CS35L45_BLOCK_ENABLES, CS35L45_IMON_EN_SHIFT, 0), + SND_SOC_DAPM_ADC("VDD_BATTMON", NULL, CS35L45_BLOCK_ENABLES, + CS35L45_VDD_BATTMON_EN_SHIFT, 0), + SND_SOC_DAPM_ADC("VDD_BSTMON", NULL, CS35L45_BLOCK_ENABLES, + CS35L45_VDD_BSTMON_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_IN("ASP_RX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASP_RX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_RX2_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_OUT("ASP_TX1", NULL, 0, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP_TX2", NULL, 1, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP_TX3", NULL, 2, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP_TX4", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX4_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP_TX5", NULL, 3, CS35L45_ASP_ENABLES1, CS35L45_ASP_TX5_EN_SHIFT, 0), + + SND_SOC_DAPM_MUX("ASP_TX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[0]), + SND_SOC_DAPM_MUX("ASP_TX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[1]), + SND_SOC_DAPM_MUX("ASP_TX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[2]), + SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]), + SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]), + + SND_SOC_DAPM_MUX("DSP_RX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[0]), + SND_SOC_DAPM_MUX("DSP_RX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[1]), + SND_SOC_DAPM_MUX("DSP_RX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[2]), + SND_SOC_DAPM_MUX("DSP_RX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[3]), + SND_SOC_DAPM_MUX("DSP_RX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[4]), + SND_SOC_DAPM_MUX("DSP_RX6 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[5]), + SND_SOC_DAPM_MUX("DSP_RX7 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[6]), + SND_SOC_DAPM_MUX("DSP_RX8 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[7]), + + SND_SOC_DAPM_MUX("DACPCM Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]), + + SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("SPK"), +}; + +#define CS35L45_ASP_MUX_ROUTE(name) \ + { name" Source", "ASP_RX1", "ASP_RX1" }, \ + { name" Source", "ASP_RX2", "ASP_RX2" }, \ + { name" Source", "DSP_TX1", "DSP1" }, \ + { name" Source", "DSP_TX2", "DSP1" }, \ + { name" Source", "VMON", "VMON" }, \ + { name" Source", "IMON", "IMON" }, \ + { name" Source", "ERR_VOL", "ERR_VOL" }, \ + { name" Source", "VDD_BATTMON", "VDD_BATTMON" }, \ + { name" Source", "VDD_BSTMON", "VDD_BSTMON" }, \ + { name" Source", "Interpolator", "AMP_INTP" }, \ + { name" Source", "IL_TARGET", "IL_TARGET" } + +#define CS35L45_DSP_MUX_ROUTE(name) \ + { name" Source", "ASP_RX1", "ASP_RX1" }, \ + { name" Source", "ASP_RX2", "ASP_RX2" } + +#define CS35L45_DAC_MUX_ROUTE(name) \ + { name" Source", "ASP_RX1", "ASP_RX1" }, \ + { name" Source", "ASP_RX2", "ASP_RX2" }, \ + { name" Source", "DSP_TX1", "DSP1" }, \ + { name" Source", "DSP_TX2", "DSP1" } + +static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = { + /* Feedback */ + { "VMON", NULL, "VMON_SRC" }, + { "IMON", NULL, "IMON_SRC" }, + { "VDD_BATTMON", NULL, "VDD_BATTMON_SRC" }, + { "VDD_BSTMON", NULL, "VDD_BSTMON_SRC" }, + + { "Capture", NULL, "ASP_TX1"}, + { "Capture", NULL, "ASP_TX2"}, + { "Capture", NULL, "ASP_TX3"}, + { "Capture", NULL, "ASP_TX4"}, + { "Capture", NULL, "ASP_TX5"}, + { "ASP_TX1", NULL, "ASP_TX1 Source"}, + { "ASP_TX2", NULL, "ASP_TX2 Source"}, + { "ASP_TX3", NULL, "ASP_TX3 Source"}, + { "ASP_TX4", NULL, "ASP_TX4 Source"}, + { "ASP_TX5", NULL, "ASP_TX5 Source"}, + + { "ASP_TX1", NULL, "ASP_EN" }, + { "ASP_TX2", NULL, "ASP_EN" }, + { "ASP_TX3", NULL, "ASP_EN" }, + { "ASP_TX4", NULL, "ASP_EN" }, + { "ASP_TX1", NULL, "GLOBAL_EN" }, + { "ASP_TX2", NULL, "GLOBAL_EN" }, + { "ASP_TX3", NULL, "GLOBAL_EN" }, + { "ASP_TX4", NULL, "GLOBAL_EN" }, + { "ASP_TX5", NULL, "GLOBAL_EN" }, + + CS35L45_ASP_MUX_ROUTE("ASP_TX1"), + CS35L45_ASP_MUX_ROUTE("ASP_TX2"), + CS35L45_ASP_MUX_ROUTE("ASP_TX3"), + CS35L45_ASP_MUX_ROUTE("ASP_TX4"), + CS35L45_ASP_MUX_ROUTE("ASP_TX5"), + + /* Playback */ + { "ASP_RX1", NULL, "Playback" }, + { "ASP_RX2", NULL, "Playback" }, + { "ASP_RX1", NULL, "ASP_EN" }, + { "ASP_RX2", NULL, "ASP_EN" }, + + { "AMP", NULL, "DACPCM Source"}, + { "AMP", NULL, "GLOBAL_EN"}, + + CS35L45_DSP_MUX_ROUTE("DSP_RX1"), + CS35L45_DSP_MUX_ROUTE("DSP_RX2"), + CS35L45_DSP_MUX_ROUTE("DSP_RX3"), + CS35L45_DSP_MUX_ROUTE("DSP_RX4"), + CS35L45_DSP_MUX_ROUTE("DSP_RX5"), + CS35L45_DSP_MUX_ROUTE("DSP_RX6"), + CS35L45_DSP_MUX_ROUTE("DSP_RX7"), + CS35L45_DSP_MUX_ROUTE("DSP_RX8"), + + {"DSP1", NULL, "DSP_RX1 Source"}, + {"DSP1", NULL, "DSP_RX2 Source"}, + {"DSP1", NULL, "DSP_RX3 Source"}, + {"DSP1", NULL, "DSP_RX4 Source"}, + {"DSP1", NULL, "DSP_RX5 Source"}, + {"DSP1", NULL, "DSP_RX6 Source"}, + {"DSP1", NULL, "DSP_RX7 Source"}, + {"DSP1", NULL, "DSP_RX8 Source"}, + + {"DSP1 Preload", NULL, "DSP1 Preloader"}, + {"DSP1", NULL, "DSP1 Preloader"}, + + CS35L45_DAC_MUX_ROUTE("DACPCM"), + + { "SPK", NULL, "AMP"}, +}; + +static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true); + +static const struct snd_kcontrol_new cs35l45_controls[] = { + /* Ignore bit 0: it is beyond the resolution of TLV_DB_SCALE */ + SOC_SINGLE_S_TLV("Digital PCM Volume", + CS35L45_AMP_PCM_CONTROL, + CS35L45_AMP_VOL_PCM_SHIFT + 1, + -409, 48, + (CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1, + 0, cs35l45_dig_pcm_vol_tlv), + WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + WM_ADSP_FW_CONTROL("DSP1", 0), +}; + +static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq) +{ + unsigned int val; + int freq_id; + + freq_id = cs35l45_get_clk_freq_id(freq); + if (freq_id < 0) { + dev_err(cs35l45->dev, "Invalid freq: %u\n", freq); + return -EINVAL; + } + + regmap_read(cs35l45->regmap, CS35L45_REFCLK_INPUT, &val); + val = (val & CS35L45_PLL_REFCLK_FREQ_MASK) >> CS35L45_PLL_REFCLK_FREQ_SHIFT; + if (val == freq_id) + return 0; + + regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK); + regmap_update_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, + CS35L45_PLL_REFCLK_FREQ_MASK, + freq_id << CS35L45_PLL_REFCLK_FREQ_SHIFT); + regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK); + regmap_clear_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_OPEN_LOOP_MASK); + regmap_set_bits(cs35l45->regmap, CS35L45_REFCLK_INPUT, CS35L45_PLL_REFCLK_EN_MASK); + + return 0; +} + +static int cs35l45_asp_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(codec_dai->component); + unsigned int asp_fmt, fsync_inv, bclk_inv; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + dev_err(cs35l45->dev, "Invalid DAI clocking\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + asp_fmt = CS35l45_ASP_FMT_DSP_A; + break; + case SND_SOC_DAIFMT_I2S: + asp_fmt = CS35L45_ASP_FMT_I2S; + break; + default: + dev_err(cs35l45->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + fsync_inv = 1; + bclk_inv = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + fsync_inv = 0; + bclk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_IF: + fsync_inv = 1; + bclk_inv = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + fsync_inv = 0; + bclk_inv = 0; + break; + default: + dev_warn(cs35l45->dev, "Invalid DAI clock polarity\n"); + return -EINVAL; + } + + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2, + CS35L45_ASP_FMT_MASK | + CS35L45_ASP_FSYNC_INV_MASK | + CS35L45_ASP_BCLK_INV_MASK, + (asp_fmt << CS35L45_ASP_FMT_SHIFT) | + (fsync_inv << CS35L45_ASP_FSYNC_INV_SHIFT) | + (bclk_inv << CS35L45_ASP_BCLK_INV_SHIFT)); + + return 0; +} + +static int cs35l45_asp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); + unsigned int asp_width, asp_wl, global_fs, slot_multiple, asp_fmt; + int bclk; + + switch (params_rate(params)) { + case 44100: + global_fs = CS35L45_44P100_KHZ; + break; + case 48000: + global_fs = CS35L45_48P0_KHZ; + break; + case 88200: + global_fs = CS35L45_88P200_KHZ; + break; + case 96000: + global_fs = CS35L45_96P0_KHZ; + break; + default: + dev_warn(cs35l45->dev, "Unsupported sample rate (%d)\n", + params_rate(params)); + return -EINVAL; + } + + regmap_update_bits(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE, + CS35L45_GLOBAL_FS_MASK, + global_fs << CS35L45_GLOBAL_FS_SHIFT); + + asp_wl = params_width(params); + + if (cs35l45->slot_width) + asp_width = cs35l45->slot_width; + else + asp_width = params_width(params); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2, + CS35L45_ASP_WIDTH_RX_MASK, + asp_width << CS35L45_ASP_WIDTH_RX_SHIFT); + + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL5, + CS35L45_ASP_WL_MASK, + asp_wl << CS35L45_ASP_WL_SHIFT); + } else { + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL2, + CS35L45_ASP_WIDTH_TX_MASK, + asp_width << CS35L45_ASP_WIDTH_TX_SHIFT); + + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_DATA_CONTROL1, + CS35L45_ASP_WL_MASK, + asp_wl << CS35L45_ASP_WL_SHIFT); + } + + if (cs35l45->sysclk_set) + return 0; + + /* I2S always has an even number of channels */ + regmap_read(cs35l45->regmap, CS35L45_ASP_CONTROL2, &asp_fmt); + asp_fmt = (asp_fmt & CS35L45_ASP_FMT_MASK) >> CS35L45_ASP_FMT_SHIFT; + if (asp_fmt == CS35L45_ASP_FMT_I2S) + slot_multiple = 2; + else + slot_multiple = 1; + + bclk = snd_soc_tdm_params_to_bclk(params, asp_width, + cs35l45->slot_count, slot_multiple); + + return cs35l45_set_pll(cs35l45, bclk); +} + +static int cs35l45_asp_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); + + if (slot_width && ((slot_width < 16) || (slot_width > 128))) + return -EINVAL; + + cs35l45->slot_width = slot_width; + cs35l45->slot_count = slots; + + return 0; +} + +static int cs35l45_asp_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); + int ret; + + if (clk_id != 0) { + dev_err(cs35l45->dev, "Invalid clk_id %d\n", clk_id); + return -EINVAL; + } + + cs35l45->sysclk_set = false; + if (freq == 0) + return 0; + + ret = cs35l45_set_pll(cs35l45, freq); + if (ret < 0) + return -EINVAL; + + cs35l45->sysclk_set = true; + + return 0; +} + +static int cs35l45_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(dai->component); + unsigned int global_fs, val, hpf_tune; + + if (mute) + return 0; + + regmap_read(cs35l45->regmap, CS35L45_GLOBAL_SAMPLE_RATE, &global_fs); + global_fs = (global_fs & CS35L45_GLOBAL_FS_MASK) >> CS35L45_GLOBAL_FS_SHIFT; + switch (global_fs) { + case CS35L45_44P100_KHZ: + hpf_tune = CS35L45_HPF_44P1; + break; + case CS35L45_88P200_KHZ: + hpf_tune = CS35L45_HPF_88P2; + break; + default: + hpf_tune = CS35l45_HPF_DEFAULT; + break; + } + + regmap_read(cs35l45->regmap, CS35L45_AMP_PCM_HPF_TST, &val); + if (val != hpf_tune) { + struct reg_sequence hpf_override_seq[] = { + { 0x00000040, 0x00000055 }, + { 0x00000040, 0x000000AA }, + { 0x00000044, 0x00000055 }, + { 0x00000044, 0x000000AA }, + { CS35L45_AMP_PCM_HPF_TST, hpf_tune }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000000 }, + }; + regmap_multi_reg_write(cs35l45->regmap, hpf_override_seq, + ARRAY_SIZE(hpf_override_seq)); + } + + return 0; +} + +static const struct snd_soc_dai_ops cs35l45_asp_dai_ops = { + .set_fmt = cs35l45_asp_set_fmt, + .hw_params = cs35l45_asp_hw_params, + .set_tdm_slot = cs35l45_asp_set_tdm_slot, + .set_sysclk = cs35l45_asp_set_sysclk, + .mute_stream = cs35l45_mute_stream, +}; + +static struct snd_soc_dai_driver cs35l45_dai[] = { + { + .name = "cs35l45", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS35L45_RATES, + .formats = CS35L45_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 5, + .rates = CS35L45_RATES, + .formats = CS35L45_FORMATS, + }, + .symmetric_rate = true, + .symmetric_sample_bits = true, + .ops = &cs35l45_asp_dai_ops, + }, +}; + +static int cs35l45_component_probe(struct snd_soc_component *component) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); + + return wm_adsp2_component_probe(&cs35l45->dsp, component); +} + +static void cs35l45_component_remove(struct snd_soc_component *component) +{ + struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component); + + wm_adsp2_component_remove(&cs35l45->dsp, component); +} + +static const struct snd_soc_component_driver cs35l45_component = { + .probe = cs35l45_component_probe, + .remove = cs35l45_component_remove, + + .dapm_widgets = cs35l45_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets), + + .dapm_routes = cs35l45_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs35l45_dapm_routes), + + .controls = cs35l45_controls, + .num_controls = ARRAY_SIZE(cs35l45_controls), + + .name = "cs35l45", + + .endianness = 1, +}; + +static void cs35l45_setup_hibernate(struct cs35l45_private *cs35l45) +{ + unsigned int wksrc; + + if (cs35l45->bus_type == CONTROL_BUS_I2C) + wksrc = CS35L45_WKSRC_I2C; + else + wksrc = CS35L45_WKSRC_SPI; + + regmap_update_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL, + CS35L45_WKSRC_EN_MASK, + wksrc << CS35L45_WKSRC_EN_SHIFT); + + regmap_set_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL, + CS35L45_UPDT_WKCTL_MASK); + + regmap_update_bits(cs35l45->regmap, CS35L45_WKI2C_CTL, + CS35L45_WKI2C_ADDR_MASK, cs35l45->i2c_addr); + + regmap_set_bits(cs35l45->regmap, CS35L45_WKI2C_CTL, + CS35L45_UPDT_WKI2C_MASK); +} + +static int cs35l45_enter_hibernate(struct cs35l45_private *cs35l45) +{ + dev_dbg(cs35l45->dev, "Enter hibernate\n"); + + cs35l45_setup_hibernate(cs35l45); + + regmap_set_bits(cs35l45->regmap, CS35L45_IRQ1_MASK_2, CS35L45_DSP_VIRT2_MBOX_MASK); + + // Don't wait for ACK since bus activity would wake the device + regmap_write(cs35l45->regmap, CS35L45_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE); + + return 0; +} + +static int cs35l45_exit_hibernate(struct cs35l45_private *cs35l45) +{ + const int wake_retries = 20; + const int sleep_retries = 5; + int ret, i, j; + + for (i = 0; i < sleep_retries; i++) { + dev_dbg(cs35l45->dev, "Exit hibernate\n"); + + for (j = 0; j < wake_retries; j++) { + ret = cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap, + CSPL_MBOX_CMD_OUT_OF_HIBERNATE); + if (!ret) { + dev_dbg(cs35l45->dev, "Wake success at cycle: %d\n", j); + regmap_clear_bits(cs35l45->regmap, CS35L45_IRQ1_MASK_2, + CS35L45_DSP_VIRT2_MBOX_MASK); + return 0; + } + usleep_range(100, 200); + } + + dev_err(cs35l45->dev, "Wake failed, re-enter hibernate: %d\n", ret); + + cs35l45_setup_hibernate(cs35l45); + } + + dev_err(cs35l45->dev, "Timed out waking device\n"); + + return -ETIMEDOUT; +} + +static int cs35l45_runtime_suspend(struct device *dev) +{ + struct cs35l45_private *cs35l45 = dev_get_drvdata(dev); + + if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running) + return 0; + + cs35l45_enter_hibernate(cs35l45); + + regcache_cache_only(cs35l45->regmap, true); + regcache_mark_dirty(cs35l45->regmap); + + dev_dbg(cs35l45->dev, "Runtime suspended\n"); + + return 0; +} + +static int cs35l45_runtime_resume(struct device *dev) +{ + struct cs35l45_private *cs35l45 = dev_get_drvdata(dev); + int ret; + + if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running) + return 0; + + dev_dbg(cs35l45->dev, "Runtime resume\n"); + + regcache_cache_only(cs35l45->regmap, false); + + ret = cs35l45_exit_hibernate(cs35l45); + if (ret) + return ret; + + ret = regcache_sync(cs35l45->regmap); + if (ret != 0) + dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret); + + /* Clear global error status */ + regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); + regmap_set_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); + regmap_clear_bits(cs35l45->regmap, CS35L45_ERROR_RELEASE, CS35L45_GLOBAL_ERR_RLS_MASK); + return ret; +} + +static int cs35l45_sys_suspend(struct device *dev) +{ + struct cs35l45_private *cs35l45 = dev_get_drvdata(dev); + + dev_dbg(cs35l45->dev, "System suspend, disabling IRQ\n"); + disable_irq(cs35l45->irq); + + return 0; +} + +static int cs35l45_sys_suspend_noirq(struct device *dev) +{ + struct cs35l45_private *cs35l45 = dev_get_drvdata(dev); + + dev_dbg(cs35l45->dev, "Late system suspend, reenabling IRQ\n"); + enable_irq(cs35l45->irq); + + return 0; +} + +static int cs35l45_sys_resume_noirq(struct device *dev) +{ + struct cs35l45_private *cs35l45 = dev_get_drvdata(dev); + + dev_dbg(cs35l45->dev, "Early system resume, disabling IRQ\n"); + disable_irq(cs35l45->irq); + + return 0; +} + +static int cs35l45_sys_resume(struct device *dev) +{ + struct cs35l45_private *cs35l45 = dev_get_drvdata(dev); + + dev_dbg(cs35l45->dev, "System resume, reenabling IRQ\n"); + enable_irq(cs35l45->irq); + + return 0; +} + +static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45) +{ + struct device_node *node = cs35l45->dev->of_node; + unsigned int gpio_regs[] = {CS35L45_GPIO1_CTRL1, CS35L45_GPIO2_CTRL1, + CS35L45_GPIO3_CTRL1}; + unsigned int pad_regs[] = {CS35L45_SYNC_GPIO1, + CS35L45_INTB_GPIO2_MCLK_REF, CS35L45_GPIO3}; + struct device_node *child; + unsigned int val; + char of_name[32]; + int ret, i; + + if (!node) + return 0; + + for (i = 0; i < CS35L45_NUM_GPIOS; i++) { + sprintf(of_name, "cirrus,gpio-ctrl%d", i + 1); + child = of_get_child_by_name(node, of_name); + if (!child) + continue; + + ret = of_property_read_u32(child, "gpio-dir", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, gpio_regs[i], + CS35L45_GPIO_DIR_MASK, + val << CS35L45_GPIO_DIR_SHIFT); + + ret = of_property_read_u32(child, "gpio-lvl", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, gpio_regs[i], + CS35L45_GPIO_LVL_MASK, + val << CS35L45_GPIO_LVL_SHIFT); + + ret = of_property_read_u32(child, "gpio-op-cfg", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, gpio_regs[i], + CS35L45_GPIO_OP_CFG_MASK, + val << CS35L45_GPIO_OP_CFG_SHIFT); + + ret = of_property_read_u32(child, "gpio-pol", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, gpio_regs[i], + CS35L45_GPIO_POL_MASK, + val << CS35L45_GPIO_POL_SHIFT); + + ret = of_property_read_u32(child, "gpio-ctrl", &val); + if (!ret) + regmap_update_bits(cs35l45->regmap, pad_regs[i], + CS35L45_GPIO_CTRL_MASK, + val << CS35L45_GPIO_CTRL_SHIFT); + + ret = of_property_read_u32(child, "gpio-invert", &val); + if (!ret) { + regmap_update_bits(cs35l45->regmap, pad_regs[i], + CS35L45_GPIO_INVERT_MASK, + val << CS35L45_GPIO_INVERT_SHIFT); + if (i == 1) + cs35l45->irq_invert = val; + } + + of_node_put(child); + } + + if (device_property_read_u32(cs35l45->dev, + "cirrus,asp-sdout-hiz-ctrl", &val) == 0) { + regmap_update_bits(cs35l45->regmap, CS35L45_ASP_CONTROL3, + CS35L45_ASP_DOUT_HIZ_CTRL_MASK, + val << CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT); + } + + return 0; +} + +static int cs35l45_dsp_virt2_mbox3_irq_handle(struct cs35l45_private *cs35l45, + const unsigned int cmd, + unsigned int data) +{ + static char *speak_status = "Unknown"; + + switch (cmd) { + case EVENT_SPEAKER_STATUS: + switch (data) { + case 1: + speak_status = "All Clear"; + break; + case 2: + speak_status = "Open Circuit"; + break; + case 4: + speak_status = "Short Circuit"; + break; + } + + dev_info(cs35l45->dev, "MBOX event (SPEAKER_STATUS): %s\n", + speak_status); + break; + case EVENT_BOOT_DONE: + dev_dbg(cs35l45->dev, "MBOX event (BOOT_DONE)\n"); + break; + default: + dev_err(cs35l45->dev, "MBOX event not supported %u\n", cmd); + return -EINVAL; + } + + return 0; +} + +static irqreturn_t cs35l45_dsp_virt2_mbox_cb(int irq, void *data) +{ + struct cs35l45_private *cs35l45 = data; + unsigned int mbox_val; + int ret = 0; + + ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_3, &mbox_val); + if (!ret && mbox_val) + cs35l45_dsp_virt2_mbox3_irq_handle(cs35l45, mbox_val & CS35L45_MBOX3_CMD_MASK, + (mbox_val & CS35L45_MBOX3_DATA_MASK) >> CS35L45_MBOX3_DATA_SHIFT); + + /* Handle DSP trace log IRQ */ + ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_4, &mbox_val); + if (!ret && mbox_val != 0) { + dev_err(cs35l45->dev, "Spurious DSP MBOX4 IRQ\n"); + } + + return IRQ_RETVAL(ret); +} + +static irqreturn_t cs35l45_pll_unlock(int irq, void *data) +{ + struct cs35l45_private *cs35l45 = data; + + dev_dbg(cs35l45->dev, "PLL unlock detected!"); + + return IRQ_HANDLED; +} + +static irqreturn_t cs35l45_pll_lock(int irq, void *data) +{ + struct cs35l45_private *cs35l45 = data; + + dev_dbg(cs35l45->dev, "PLL lock detected!"); + + return IRQ_HANDLED; +} + +static irqreturn_t cs35l45_spk_safe_err(int irq, void *data); + +static const struct cs35l45_irq cs35l45_irqs[] = { + CS35L45_IRQ(AMP_SHORT_ERR, "Amplifier short error", cs35l45_spk_safe_err), + CS35L45_IRQ(UVLO_VDDBATT_ERR, "VDDBATT undervoltage error", cs35l45_spk_safe_err), + CS35L45_IRQ(BST_SHORT_ERR, "Boost inductor error", cs35l45_spk_safe_err), + CS35L45_IRQ(BST_UVP_ERR, "Boost undervoltage error", cs35l45_spk_safe_err), + CS35L45_IRQ(TEMP_ERR, "Overtemperature error", cs35l45_spk_safe_err), + CS35L45_IRQ(AMP_CAL_ERR, "Amplifier calibration error", cs35l45_spk_safe_err), + CS35L45_IRQ(UVLO_VDDLV_ERR, "LV threshold detector error", cs35l45_spk_safe_err), + CS35L45_IRQ(GLOBAL_ERROR, "Global error", cs35l45_spk_safe_err), + CS35L45_IRQ(DSP_WDT_EXPIRE, "DSP Watchdog Timer", cs35l45_spk_safe_err), + CS35L45_IRQ(PLL_UNLOCK_FLAG_RISE, "PLL unlock", cs35l45_pll_unlock), + CS35L45_IRQ(PLL_LOCK_FLAG, "PLL lock", cs35l45_pll_lock), + CS35L45_IRQ(DSP_VIRT2_MBOX, "DSP virtual MBOX 2 write flag", cs35l45_dsp_virt2_mbox_cb), +}; + +static irqreturn_t cs35l45_spk_safe_err(int irq, void *data) +{ + struct cs35l45_private *cs35l45 = data; + int i; + + i = irq - regmap_irq_get_virq(cs35l45->irq_data, 0); + + dev_err(cs35l45->dev, "%s condition detected!\n", cs35l45_irqs[i].name); + + return IRQ_HANDLED; +} + +static const struct regmap_irq cs35l45_reg_irqs[] = { + CS35L45_REG_IRQ(IRQ1_EINT_1, AMP_SHORT_ERR), + CS35L45_REG_IRQ(IRQ1_EINT_1, UVLO_VDDBATT_ERR), + CS35L45_REG_IRQ(IRQ1_EINT_1, BST_SHORT_ERR), + CS35L45_REG_IRQ(IRQ1_EINT_1, BST_UVP_ERR), + CS35L45_REG_IRQ(IRQ1_EINT_1, TEMP_ERR), + CS35L45_REG_IRQ(IRQ1_EINT_3, AMP_CAL_ERR), + CS35L45_REG_IRQ(IRQ1_EINT_18, UVLO_VDDLV_ERR), + CS35L45_REG_IRQ(IRQ1_EINT_18, GLOBAL_ERROR), + CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_WDT_EXPIRE), + CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_UNLOCK_FLAG_RISE), + CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_LOCK_FLAG), + CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_VIRT2_MBOX), +}; + +static const struct regmap_irq_chip cs35l45_regmap_irq_chip = { + .name = "cs35l45 IRQ1 Controller", + .main_status = CS35L45_IRQ1_STATUS, + .status_base = CS35L45_IRQ1_EINT_1, + .mask_base = CS35L45_IRQ1_MASK_1, + .ack_base = CS35L45_IRQ1_EINT_1, + .num_regs = 18, + .irqs = cs35l45_reg_irqs, + .num_irqs = ARRAY_SIZE(cs35l45_reg_irqs), + .runtime_pm = true, +}; + +static int cs35l45_initialize(struct cs35l45_private *cs35l45) +{ + struct device *dev = cs35l45->dev; + unsigned int dev_id[5]; + unsigned int sts; + int ret; + + ret = regmap_read_poll_timeout(cs35l45->regmap, CS35L45_IRQ1_EINT_4, sts, + (sts & CS35L45_OTP_BOOT_DONE_STS_MASK), + 1000, 5000); + if (ret < 0) { + dev_err(cs35l45->dev, "Timeout waiting for OTP boot\n"); + return ret; + } + + ret = regmap_bulk_read(cs35l45->regmap, CS35L45_DEVID, dev_id, ARRAY_SIZE(dev_id)); + if (ret) { + dev_err(cs35l45->dev, "Get Device ID failed: %d\n", ret); + return ret; + } + + switch (dev_id[0]) { + case 0x35A450: + case 0x35A460: + break; + default: + dev_err(cs35l45->dev, "Bad DEVID 0x%x\n", dev_id[0]); + return -ENODEV; + } + + dev_info(cs35l45->dev, "Cirrus Logic CS35L45: REVID %02X OTPID %02X\n", + dev_id[1], dev_id[4]); + + regmap_write(cs35l45->regmap, CS35L45_IRQ1_EINT_4, + CS35L45_OTP_BOOT_DONE_STS_MASK | CS35L45_OTP_BUSY_MASK); + + ret = cs35l45_apply_patch(cs35l45); + if (ret < 0) { + dev_err(dev, "Failed to apply init patch %d\n", ret); + return ret; + } + + ret = cs35l45_apply_property_config(cs35l45); + if (ret < 0) + return ret; + + return 0; +} + +static const struct reg_sequence cs35l45_fs_errata_patch[] = { + {0x02B80080, 0x00000001}, + {0x02B80088, 0x00000001}, + {0x02B80090, 0x00000001}, + {0x02B80098, 0x00000001}, + {0x02B800A0, 0x00000001}, + {0x02B800A8, 0x00000001}, + {0x02B800B0, 0x00000001}, + {0x02B800B8, 0x00000001}, + {0x02B80280, 0x00000001}, + {0x02B80288, 0x00000001}, + {0x02B80290, 0x00000001}, + {0x02B80298, 0x00000001}, + {0x02B802A0, 0x00000001}, + {0x02B802A8, 0x00000001}, + {0x02B802B0, 0x00000001}, + {0x02B802B8, 0x00000001}, +}; + +static const struct cs_dsp_region cs35l45_dsp1_regions[] = { + { .type = WMFW_HALO_PM_PACKED, .base = CS35L45_DSP1_PMEM_0 }, + { .type = WMFW_HALO_XM_PACKED, .base = CS35L45_DSP1_XMEM_PACK_0 }, + { .type = WMFW_HALO_YM_PACKED, .base = CS35L45_DSP1_YMEM_PACK_0 }, + {. type = WMFW_ADSP2_XM, .base = CS35L45_DSP1_XMEM_UNPACK24_0}, + {. type = WMFW_ADSP2_YM, .base = CS35L45_DSP1_YMEM_UNPACK24_0}, +}; + +static int cs35l45_dsp_init(struct cs35l45_private *cs35l45) +{ + struct wm_adsp *dsp = &cs35l45->dsp; + int ret; + + dsp->part = "cs35l45"; + dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */ + dsp->toggle_preload = true; + dsp->cs_dsp.num = 1; + dsp->cs_dsp.type = WMFW_HALO; + dsp->cs_dsp.rev = 0; + dsp->cs_dsp.dev = cs35l45->dev; + dsp->cs_dsp.regmap = cs35l45->regmap; + dsp->cs_dsp.base = CS35L45_DSP1_CLOCK_FREQ; + dsp->cs_dsp.base_sysinfo = CS35L45_DSP1_SYS_ID; + dsp->cs_dsp.mem = cs35l45_dsp1_regions; + dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l45_dsp1_regions); + dsp->cs_dsp.lock_regions = 0xFFFFFFFF; + + ret = wm_halo_init(dsp); + + regmap_multi_reg_write(cs35l45->regmap, cs35l45_fs_errata_patch, + ARRAY_SIZE(cs35l45_fs_errata_patch)); + + return ret; +} + +int cs35l45_probe(struct cs35l45_private *cs35l45) +{ + struct device *dev = cs35l45->dev; + unsigned long irq_pol = IRQF_ONESHOT | IRQF_SHARED; + int ret, i, irq; + + cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt"); + if (IS_ERR(cs35l45->vdd_batt)) + return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_batt), + "Failed to request vdd-batt\n"); + + cs35l45->vdd_a = devm_regulator_get(dev, "vdd-a"); + if (IS_ERR(cs35l45->vdd_a)) + return dev_err_probe(dev, PTR_ERR(cs35l45->vdd_a), + "Failed to request vdd-a\n"); + + /* VDD_BATT must always be enabled before other supplies */ + ret = regulator_enable(cs35l45->vdd_batt); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable vdd-batt\n"); + + ret = regulator_enable(cs35l45->vdd_a); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable vdd-a\n"); + + /* If reset is shared only one instance can claim it */ + cs35l45->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs35l45->reset_gpio)) { + ret = PTR_ERR(cs35l45->reset_gpio); + cs35l45->reset_gpio = NULL; + if (ret == -EBUSY) { + dev_dbg(dev, "Reset line busy, assuming shared reset\n"); + } else { + dev_err_probe(dev, ret, "Failed to get reset GPIO\n"); + goto err; + } + } + + if (cs35l45->reset_gpio) { + usleep_range(CS35L45_RESET_HOLD_US, CS35L45_RESET_HOLD_US + 100); + gpiod_set_value_cansleep(cs35l45->reset_gpio, 1); + } + + usleep_range(CS35L45_RESET_US, CS35L45_RESET_US + 100); + + ret = cs35l45_initialize(cs35l45); + if (ret < 0) + goto err_reset; + + ret = cs35l45_dsp_init(cs35l45); + if (ret < 0) + goto err_reset; + + pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000); + pm_runtime_use_autosuspend(cs35l45->dev); + pm_runtime_mark_last_busy(cs35l45->dev); + pm_runtime_set_active(cs35l45->dev); + pm_runtime_get_noresume(cs35l45->dev); + pm_runtime_enable(cs35l45->dev); + + if (cs35l45->irq) { + if (cs35l45->irq_invert) + irq_pol |= IRQF_TRIGGER_HIGH; + else + irq_pol |= IRQF_TRIGGER_LOW; + + ret = devm_regmap_add_irq_chip(dev, cs35l45->regmap, cs35l45->irq, irq_pol, 0, + &cs35l45_regmap_irq_chip, &cs35l45->irq_data); + if (ret) { + dev_err(dev, "Failed to register IRQ chip: %d\n", ret); + goto err_dsp; + } + + for (i = 0; i < ARRAY_SIZE(cs35l45_irqs); i++) { + irq = regmap_irq_get_virq(cs35l45->irq_data, cs35l45_irqs[i].irq); + if (irq < 0) { + dev_err(dev, "Failed to get %s\n", cs35l45_irqs[i].name); + ret = irq; + goto err_dsp; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, cs35l45_irqs[i].handler, + irq_pol, cs35l45_irqs[i].name, cs35l45); + if (ret) { + dev_err(dev, "Failed to request IRQ %s: %d\n", + cs35l45_irqs[i].name, ret); + goto err_dsp; + } + } + } + + ret = devm_snd_soc_register_component(dev, &cs35l45_component, + cs35l45_dai, + ARRAY_SIZE(cs35l45_dai)); + if (ret < 0) + goto err_dsp; + + pm_runtime_put_autosuspend(cs35l45->dev); + + return 0; + +err_dsp: + pm_runtime_disable(cs35l45->dev); + pm_runtime_put_noidle(cs35l45->dev); + wm_adsp2_remove(&cs35l45->dsp); + +err_reset: + gpiod_set_value_cansleep(cs35l45->reset_gpio, 0); +err: + regulator_disable(cs35l45->vdd_a); + regulator_disable(cs35l45->vdd_batt); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l45_probe, SND_SOC_CS35L45); + +void cs35l45_remove(struct cs35l45_private *cs35l45) +{ + pm_runtime_get_sync(cs35l45->dev); + pm_runtime_disable(cs35l45->dev); + wm_adsp2_remove(&cs35l45->dsp); + + gpiod_set_value_cansleep(cs35l45->reset_gpio, 0); + + pm_runtime_put_noidle(cs35l45->dev); + regulator_disable(cs35l45->vdd_a); + /* VDD_BATT must be the last to power-off */ + regulator_disable(cs35l45->vdd_batt); +} +EXPORT_SYMBOL_NS_GPL(cs35l45_remove, SND_SOC_CS35L45); + +EXPORT_GPL_DEV_PM_OPS(cs35l45_pm_ops) = { + RUNTIME_PM_OPS(cs35l45_runtime_suspend, cs35l45_runtime_resume, NULL) + + SYSTEM_SLEEP_PM_OPS(cs35l45_sys_suspend, cs35l45_sys_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l45_sys_suspend_noirq, cs35l45_sys_resume_noirq) +}; + +MODULE_DESCRIPTION("ASoC CS35L45 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, "); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h new file mode 100644 index 0000000000..61135a316d --- /dev/null +++ b/sound/soc/codecs/cs35l45.h @@ -0,0 +1,482 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cs35l45.h - CS35L45 ALSA SoC audio driver + * + * Copyright 2019-2022 Cirrus Logic, Inc. + * + * Author: James Schulman + * + */ + +#ifndef CS35L45_H +#define CS35L45_H + +#include +#include +#include +#include +#include "wm_adsp.h" + +#define CS35L45_DEVID 0x00000000 +#define CS35L45_REVID 0x00000004 +#define CS35L45_RELID 0x0000000C +#define CS35L45_OTPID 0x00000010 +#define CS35L45_SFT_RESET 0x00000020 +#define CS35L45_GLOBAL_ENABLES 0x00002014 +#define CS35L45_BLOCK_ENABLES 0x00002018 +#define CS35L45_BLOCK_ENABLES2 0x0000201C +#define CS35L45_ERROR_RELEASE 0x00002034 +#define CS35L45_SYNC_GPIO1 0x00002430 +#define CS35L45_INTB_GPIO2_MCLK_REF 0x00002434 +#define CS35L45_GPIO3 0x00002438 +#define CS35L45_PWRMGT_CTL 0x00002900 +#define CS35L45_WAKESRC_CTL 0x00002904 +#define CS35L45_WKI2C_CTL 0x00002908 +#define CS35L45_PWRMGT_STS 0x0000290C +#define CS35L45_REFCLK_INPUT 0x00002C04 +#define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C +#define CS35L45_BOOST_CCM_CFG 0x00003808 +#define CS35L45_BOOST_DCM_CFG 0x0000380C +#define CS35L45_BOOST_OV_CFG 0x0000382C +#define CS35L45_ASP_ENABLES1 0x00004800 +#define CS35L45_ASP_CONTROL1 0x00004804 +#define CS35L45_ASP_CONTROL2 0x00004808 +#define CS35L45_ASP_CONTROL3 0x0000480C +#define CS35L45_ASP_FRAME_CONTROL1 0x00004810 +#define CS35L45_ASP_FRAME_CONTROL2 0x00004814 +#define CS35L45_ASP_FRAME_CONTROL5 0x00004820 +#define CS35L45_ASP_DATA_CONTROL1 0x00004830 +#define CS35L45_ASP_DATA_CONTROL5 0x00004840 +#define CS35L45_DACPCM1_INPUT 0x00004C00 +#define CS35L45_ASPTX1_INPUT 0x00004C20 +#define CS35L45_ASPTX2_INPUT 0x00004C24 +#define CS35L45_ASPTX3_INPUT 0x00004C28 +#define CS35L45_ASPTX4_INPUT 0x00004C2C +#define CS35L45_ASPTX5_INPUT 0x00004C30 +#define CS35L45_DSP1RX1_INPUT 0x00004C40 +#define CS35L45_DSP1RX2_INPUT 0x00004C44 +#define CS35L45_DSP1RX3_INPUT 0x00004C48 +#define CS35L45_DSP1RX4_INPUT 0x00004C4C +#define CS35L45_DSP1RX5_INPUT 0x00004C50 +#define CS35L45_DSP1RX6_INPUT 0x00004C54 +#define CS35L45_DSP1RX7_INPUT 0x00004C58 +#define CS35L45_DSP1RX8_INPUT 0x00004C5C +#define CS35L45_LDPM_CONFIG 0x00006404 +#define CS35L45_AMP_PCM_CONTROL 0x00007000 +#define CS35L45_AMP_PCM_HPF_TST 0x00007004 +#define CS35L45_IRQ1_CFG 0x0000E000 +#define CS35L45_IRQ1_STATUS 0x0000E004 +#define CS35L45_IRQ1_EINT_1 0x0000E010 +#define CS35L45_IRQ1_EINT_2 0x0000E014 +#define CS35L45_IRQ1_EINT_3 0x0000E018 +#define CS35L45_IRQ1_EINT_4 0x0000E01C +#define CS35L45_IRQ1_EINT_5 0x0000E020 +#define CS35L45_IRQ1_EINT_7 0x0000E028 +#define CS35L45_IRQ1_EINT_8 0x0000E02C +#define CS35L45_IRQ1_EINT_18 0x0000E054 +#define CS35L45_IRQ1_STS_1 0x0000E090 +#define CS35L45_IRQ1_STS_2 0x0000E094 +#define CS35L45_IRQ1_STS_3 0x0000E098 +#define CS35L45_IRQ1_STS_4 0x0000E09C +#define CS35L45_IRQ1_STS_5 0x0000E0A0 +#define CS35L45_IRQ1_STS_7 0x0000E0A8 +#define CS35L45_IRQ1_STS_8 0x0000E0AC +#define CS35L45_IRQ1_STS_18 0x0000E0D4 +#define CS35L45_IRQ1_MASK_1 0x0000E110 +#define CS35L45_IRQ1_MASK_2 0x0000E114 +#define CS35L45_IRQ1_MASK_3 0x0000E118 +#define CS35L45_IRQ1_MASK_4 0x0000E11C +#define CS35L45_IRQ1_MASK_5 0x0000E120 +#define CS35L45_IRQ1_MASK_6 0x0000E124 +#define CS35L45_IRQ1_MASK_7 0x0000E128 +#define CS35L45_IRQ1_MASK_8 0x0000E12C +#define CS35L45_IRQ1_MASK_9 0x0000E130 +#define CS35L45_IRQ1_MASK_10 0x0000E134 +#define CS35L45_IRQ1_MASK_11 0x0000E138 +#define CS35L45_IRQ1_MASK_12 0x0000E13C +#define CS35L45_IRQ1_MASK_13 0x0000E140 +#define CS35L45_IRQ1_MASK_14 0x0000E144 +#define CS35L45_IRQ1_MASK_15 0x0000E148 +#define CS35L45_IRQ1_MASK_16 0x0000E14C +#define CS35L45_IRQ1_MASK_17 0x0000E150 +#define CS35L45_IRQ1_MASK_18 0x0000E154 +#define CS35L45_GPIO_STATUS1 0x0000F000 +#define CS35L45_GPIO1_CTRL1 0x0000F008 +#define CS35L45_GPIO2_CTRL1 0x0000F00C +#define CS35L45_GPIO3_CTRL1 0x0000F010 +#define CS35L45_DSP_MBOX_1 0x00011000 +#define CS35L45_DSP_MBOX_2 0x00011004 +#define CS35L45_DSP_VIRT1_MBOX_1 0x00011020 +#define CS35L45_DSP_VIRT1_MBOX_2 0x00011024 +#define CS35L45_DSP_VIRT1_MBOX_3 0x00011028 +#define CS35L45_DSP_VIRT1_MBOX_4 0x0001102C +#define CS35L45_DSP_VIRT2_MBOX_1 0x00011040 +#define CS35L45_DSP_VIRT2_MBOX_2 0x00011044 +#define CS35L45_DSP_VIRT2_MBOX_3 0x00011048 +#define CS35L45_DSP_VIRT2_MBOX_4 0x0001104C +#define CS35L45_DSP1_XMEM_PACK_0 0x02000000 +#define CS35L45_DSP1_XMEM_PACK_4607 0x020047FC +#define CS35L45_DSP1_XMEM_UNPACK32_0 0x02400000 +#define CS35L45_DSP1_XMEM_UNPACK32_3071 0x02402FFC +#define CS35L45_DSP1_SYS_ID 0x025E0000 +#define CS35L45_DSP1_XMEM_UNPACK24_0 0x02800000 +#define CS35L45_DSP1_XMEM_UNPACK24_6143 0x02805FFC +#define CS35L45_DSP1_CLOCK_FREQ 0x02B80000 +#define CS35L45_DSP1_RX1_RATE 0x02B80080 +#define CS35L45_DSP1_RX2_RATE 0x02B80088 +#define CS35L45_DSP1_RX3_RATE 0x02B80090 +#define CS35L45_DSP1_RX4_RATE 0x02B80098 +#define CS35L45_DSP1_RX5_RATE 0x02B800A0 +#define CS35L45_DSP1_RX6_RATE 0x02B800A8 +#define CS35L45_DSP1_RX7_RATE 0x02B800B0 +#define CS35L45_DSP1_RX8_RATE 0x02B800B8 +#define CS35L45_DSP1_TX1_RATE 0x02B80280 +#define CS35L45_DSP1_TX2_RATE 0x02B80288 +#define CS35L45_DSP1_TX3_RATE 0x02B80290 +#define CS35L45_DSP1_TX4_RATE 0x02B80298 +#define CS35L45_DSP1_TX5_RATE 0x02B802A0 +#define CS35L45_DSP1_TX6_RATE 0x02B802A8 +#define CS35L45_DSP1_TX7_RATE 0x02B802B0 +#define CS35L45_DSP1_TX8_RATE 0x02B802B8 +#define CS35L45_DSP1_SCRATCH1 0x02B805C0 +#define CS35L45_DSP1_SCRATCH2 0x02B805C8 +#define CS35L45_DSP1_SCRATCH3 0x02B805D0 +#define CS35L45_DSP1_SCRATCH4 0x02B805D8 +#define CS35L45_DSP1_CCM_CORE_CONTROL 0x02BC1000 +#define CS35L45_DSP1_YMEM_PACK_0 0x02C00000 +#define CS35L45_DSP1_YMEM_PACK_1532 0x02C017F0 +#define CS35L45_DSP1_YMEM_UNPACK32_0 0x03000000 +#define CS35L45_DSP1_YMEM_UNPACK32_1022 0x03000FF8 +#define CS35L45_DSP1_YMEM_UNPACK24_0 0x03400000 +#define CS35L45_DSP1_YMEM_UNPACK24_2043 0x03401FEC +#define CS35L45_DSP1_PMEM_0 0x03800000 +#define CS35L45_DSP1_PMEM_3834 0x03803BE8 +#define CS35L45_LASTREG 0x03C6EFE8 + +/* SFT_RESET */ +#define CS35L45_SOFT_RESET_TRIGGER 0x5A000000 + +/* GLOBAL_ENABLES */ +#define CS35L45_GLOBAL_EN_SHIFT 0 +#define CS35L45_GLOBAL_EN_MASK BIT(0) + +/* BLOCK_ENABLES */ +#define CS35L45_IMON_EN_SHIFT 13 +#define CS35L45_VMON_EN_SHIFT 12 +#define CS35L45_VDD_BSTMON_EN_SHIFT 9 +#define CS35L45_VDD_BATTMON_EN_SHIFT 8 +#define CS35L45_BST_EN_SHIFT 4 +#define CS35L45_BST_EN_MASK GENMASK(5, 4) + +#define CS35L45_BST_DISABLE_FET_ON 0x01 + +/* BLOCK_ENABLES2 */ +#define CS35L45_ASP_EN_SHIFT 27 + +#define CS35L45_MEM_RDY_SHIFT 1 +#define CS35L45_MEM_RDY_MASK BIT(1) + +/* ERROR_RELEASE */ +#define CS35L45_GLOBAL_ERR_RLS_MASK BIT(11) + +/* CCM_CORE */ +#define CS35L45_CCM_CORE_RESET_SHIFT 9 +#define CS35L45_CCM_CORE_RESET_MASK BIT(9) +#define CS35L45_CCM_PM_REMAP_SHIFT 7 +#define CS35L45_CCM_PM_REMAP_MASK BIT(7) +#define CS35L45_CCM_CORE_EN_SHIFT 0 +#define CS35L45_CCM_CORE_EN_MASK BIT(0) + +/* REFCLK_INPUT */ +#define CS35L45_PLL_FORCE_EN_SHIFT 16 +#define CS35L45_PLL_FORCE_EN_MASK BIT(16) +#define CS35L45_PLL_OPEN_LOOP_SHIFT 11 +#define CS35L45_PLL_OPEN_LOOP_MASK BIT(11) +#define CS35L45_PLL_REFCLK_FREQ_SHIFT 5 +#define CS35L45_PLL_REFCLK_FREQ_MASK GENMASK(10, 5) +#define CS35L45_PLL_REFCLK_EN_SHIFT 4 +#define CS35L45_PLL_REFCLK_EN_MASK BIT(4) +#define CS35L45_PLL_REFCLK_SEL_SHIFT 0 +#define CS35L45_PLL_REFCLK_SEL_MASK GENMASK(2, 0) + +#define CS35L45_PLL_REFCLK_SEL_BCLK 0x0 + +/* GLOBAL_SAMPLE_RATE */ +#define CS35L45_GLOBAL_FS_SHIFT 0 +#define CS35L45_GLOBAL_FS_MASK GENMASK(4, 0) + +#define CS35L45_48P0_KHZ 0x03 +#define CS35L45_96P0_KHZ 0x04 +#define CS35L45_44P100_KHZ 0x0B +#define CS35L45_88P200_KHZ 0x0C + +/* ASP_ENABLES_1 */ +#define CS35L45_ASP_RX2_EN_SHIFT 17 +#define CS35L45_ASP_RX1_EN_SHIFT 16 +#define CS35L45_ASP_TX5_EN_SHIFT 4 +#define CS35L45_ASP_TX4_EN_SHIFT 3 +#define CS35L45_ASP_TX3_EN_SHIFT 2 +#define CS35L45_ASP_TX2_EN_SHIFT 1 +#define CS35L45_ASP_TX1_EN_SHIFT 0 + +/* ASP_CONTROL2 */ +#define CS35L45_ASP_WIDTH_RX_SHIFT 24 +#define CS35L45_ASP_WIDTH_RX_MASK GENMASK(31, 24) +#define CS35L45_ASP_WIDTH_TX_SHIFT 16 +#define CS35L45_ASP_WIDTH_TX_MASK GENMASK(23, 16) +#define CS35L45_ASP_FMT_SHIFT 8 +#define CS35L45_ASP_FMT_MASK GENMASK(10, 8) +#define CS35L45_ASP_BCLK_INV_SHIFT 6 +#define CS35L45_ASP_BCLK_INV_MASK BIT(6) +#define CS35L45_ASP_FSYNC_INV_SHIFT 2 +#define CS35L45_ASP_FSYNC_INV_MASK BIT(2) + +#define CS35l45_ASP_FMT_DSP_A 0 +#define CS35L45_ASP_FMT_I2S 2 + +/* ASP_CONTROL3 */ +#define CS35L45_ASP_DOUT_HIZ_CTRL_SHIFT 0 +#define CS35L45_ASP_DOUT_HIZ_CTRL_MASK GENMASK(1, 0) + +/* ASP_FRAME_CONTROL1 */ +#define CS35L45_ASP_TX4_SLOT_SHIFT 24 +#define CS35L45_ASP_TX4_SLOT_MASK GENMASK(29, 24) +#define CS35L45_ASP_TX3_SLOT_SHIFT 16 +#define CS35L45_ASP_TX3_SLOT_MASK GENMASK(21, 16) +#define CS35L45_ASP_TX2_SLOT_SHIFT 8 +#define CS35L45_ASP_TX2_SLOT_MASK GENMASK(13, 8) +#define CS35L45_ASP_TX1_SLOT_SHIFT 0 +#define CS35L45_ASP_TX1_SLOT_MASK GENMASK(5, 0) + +#define CS35L45_ASP_TX_ALL_SLOTS (CS35L45_ASP_TX4_SLOT_MASK | \ + CS35L45_ASP_TX3_SLOT_MASK | \ + CS35L45_ASP_TX2_SLOT_MASK | \ + CS35L45_ASP_TX1_SLOT_MASK) +/* ASP_FRAME_CONTROL5 */ +#define CS35L45_ASP_RX2_SLOT_SHIFT 8 +#define CS35L45_ASP_RX2_SLOT_MASK GENMASK(13, 8) +#define CS35L45_ASP_RX1_SLOT_SHIFT 0 +#define CS35L45_ASP_RX1_SLOT_MASK GENMASK(5, 0) + +#define CS35L45_ASP_RX_ALL_SLOTS (CS35L45_ASP_RX2_SLOT_MASK | \ + CS35L45_ASP_RX1_SLOT_MASK) + +/* ASP_DATA_CONTROL1 */ +/* ASP_DATA_CONTROL5 */ +#define CS35L45_ASP_WL_SHIFT 0 +#define CS35L45_ASP_WL_MASK GENMASK(5, 0) + +/* AMP_PCM_CONTROL */ +#define CS35L45_AMP_VOL_PCM_SHIFT 0 +#define CS35L45_AMP_VOL_PCM_WIDTH 11 + +/* AMP_PCM_HPF_TST */ +#define CS35l45_HPF_DEFAULT 0x00000000 +#define CS35L45_HPF_44P1 0x000108BD +#define CS35L45_HPF_88P2 0x0001045F + +/* IRQ1_EINT_4 */ +#define CS35L45_OTP_BOOT_DONE_STS_MASK BIT(1) +#define CS35L45_OTP_BUSY_MASK BIT(0) + +/* GPIOX_CTRL1 */ +#define CS35L45_GPIO_DIR_SHIFT 31 +#define CS35L45_GPIO_DIR_MASK BIT(31) +#define CS35L45_GPIO_LVL_SHIFT 15 +#define CS35L45_GPIO_LVL_MASK BIT(15) +#define CS35L45_GPIO_OP_CFG_SHIFT 14 +#define CS35L45_GPIO_OP_CFG_MASK BIT(14) +#define CS35L45_GPIO_POL_SHIFT 12 +#define CS35L45_GPIO_POL_MASK BIT(12) + +/* SYNC_GPIO1, INTB_GPIO2_MCLK_REF, GPIO3 */ +#define CS35L45_GPIO_CTRL_SHIFT 20 +#define CS35L45_GPIO_CTRL_MASK GENMASK(22, 20) +#define CS35L45_GPIO_INVERT_SHIFT 19 +#define CS35L45_GPIO_INVERT_MASK BIT(19) + +/* CS35L45_IRQ1_EINT_1 */ +#define CS35L45_BST_UVP_ERR_SHIFT 7 +#define CS35L45_BST_UVP_ERR_MASK BIT(7) +#define CS35L45_BST_SHORT_ERR_SHIFT 8 +#define CS35L45_BST_SHORT_ERR_MASK BIT(8) +#define CS35L45_TEMP_ERR_SHIFT 17 +#define CS35L45_TEMP_ERR_MASK BIT(17) +#define CS35L45_MSM_GLOBAL_EN_ASSERT_SHIFT 22 +#define CS35L45_MSM_GLOBAL_EN_ASSERT_MASK BIT(22) +#define CS35L45_UVLO_VDDBATT_ERR_SHIFT 29 +#define CS35L45_UVLO_VDDBATT_ERR_MASK BIT(29) +#define CS35L45_AMP_SHORT_ERR_SHIFT 31 +#define CS35L45_AMP_SHORT_ERR_MASK BIT(31) + +/* CS35L45_IRQ1_EINT_2 */ +#define CS35L45_DSP_WDT_EXPIRE_SHIFT 4 +#define CS35L45_DSP_WDT_EXPIRE_MASK BIT(4) +#define CS35L45_DSP_VIRT2_MBOX_SHIFT 21 +#define CS35L45_DSP_VIRT2_MBOX_MASK BIT(21) + +/* CS35L45_IRQ1_EINT_3 */ +#define CS35L45_PLL_LOCK_FLAG_SHIFT 1 +#define CS35L45_PLL_LOCK_FLAG_MASK BIT(1) +#define CS35L45_PLL_UNLOCK_FLAG_RISE_SHIFT 4 +#define CS35L45_PLL_UNLOCK_FLAG_RISE_MASK BIT(4) +#define CS35L45_AMP_CAL_ERR_SHIFT 25 +#define CS35L45_AMP_CAL_ERR_MASK BIT(25) + +/* CS35L45_IRQ1_EINT_18 */ +#define CS35L45_GLOBAL_ERROR_SHIFT 15 +#define CS35L45_GLOBAL_ERROR_MASK BIT(15) +#define CS35L45_UVLO_VDDLV_ERR_SHIFT 16 +#define CS35L45_UVLO_VDDLV_ERR_MASK BIT(16) + +/* Mixer sources */ +#define CS35L45_PCM_SRC_MASK 0x7F +#define CS35L45_PCM_SRC_ZERO 0x00 +#define CS35L45_PCM_SRC_ASP_RX1 0x08 +#define CS35L45_PCM_SRC_ASP_RX2 0x09 +#define CS35L45_PCM_SRC_VMON 0x18 +#define CS35L45_PCM_SRC_IMON 0x19 +#define CS35L45_PCM_SRC_ERR_VOL 0x20 +#define CS35L45_PCM_SRC_CLASSH_TGT 0x21 +#define CS35L45_PCM_SRC_VDD_BATTMON 0x28 +#define CS35L45_PCM_SRC_VDD_BSTMON 0x29 +#define CS35L45_PCM_SRC_DSP_TX1 0x32 +#define CS35L45_PCM_SRC_DSP_TX2 0x33 +#define CS35L45_PCM_SRC_TEMPMON 0x3A +#define CS35L45_PCM_SRC_INTERPOLATOR 0x40 +#define CS35L45_PCM_SRC_IL_TARGET 0x48 + +#define CS35L45_RESET_HOLD_US 2000 +#define CS35L45_RESET_US 2000 +#define CS35L45_POST_GLOBAL_EN_US 5000 +#define CS35L45_PRE_GLOBAL_DIS_US 3000 + +/* WAKESRC_CTL */ +#define CS35L45_WKSRC_SYNC_GPIO1 BIT(0) +#define CS35L45_WKSRC_INT_GPIO2 BIT(1) +#define CS35L45_WKSRC_GPIO3 BIT(2) +#define CS35L45_WKSRC_SPI BIT(3) +#define CS35L45_WKSRC_I2C BIT(4) +#define CS35L45_UPDT_WKCTL_SHIFT 15 +#define CS35L45_UPDT_WKCTL_MASK BIT(15) +#define CS35L45_WKSRC_EN_SHIFT 8 +#define CS35L45_WKSRC_EN_MASK GENMASK(12, 8) +#define CS35L45_WKSRC_POL_SHIFT 0 +#define CS35L45_WKSRC_POL_MASK GENMASK(3, 0) + +/* WAKEI2C_CTL */ +#define CS35L45_UPDT_WKI2C_SHIFT 15 +#define CS35L45_UPDT_WKI2C_MASK BIT(15) +#define CS35L45_WKI2C_ADDR_SHIFT 0 +#define CS35L45_WKI2C_ADDR_MASK GENMASK(6, 0) + +#define CS35L45_SPI_MAX_FREQ 4000000 + +enum cs35l45_cspl_mboxstate { + CSPL_MBOX_STS_RUNNING = 0, + CSPL_MBOX_STS_PAUSED = 1, + CSPL_MBOX_STS_RDY_FOR_REINIT = 2, + CSPL_MBOX_STS_HIBERNATE = 3, +}; + +enum cs35l45_cspl_mboxcmd { + CSPL_MBOX_CMD_NONE = 0, + CSPL_MBOX_CMD_PAUSE = 1, + CSPL_MBOX_CMD_RESUME = 2, + CSPL_MBOX_CMD_REINIT = 3, + CSPL_MBOX_CMD_STOP_PRE_REINIT = 4, + CSPL_MBOX_CMD_HIBERNATE = 5, + CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6, + CSPL_MBOX_CMD_UNKNOWN_CMD = -1, + CSPL_MBOX_CMD_INVALID_SEQUENCE = -2, +}; + +enum control_bus_type { + CONTROL_BUS_I2C = 0, + CONTROL_BUS_SPI = 1, +}; + +#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE| \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define CS35L45_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000) + +/* + * IRQs + */ +#define CS35L45_IRQ(_irq, _name, _hand) \ + { \ + .irq = CS35L45_ ## _irq ## _IRQ,\ + .name = _name, \ + .handler = _hand, \ + } + +struct cs35l45_irq { + int irq; + const char *name; + irqreturn_t (*handler)(int irq, void *data); +}; + +#define CS35L45_REG_IRQ(_reg, _irq) \ + [CS35L45_ ## _irq ## _IRQ] = { \ + .reg_offset = (CS35L45_ ## _reg) - CS35L45_IRQ1_EINT_1, \ + .mask = CS35L45_ ## _irq ## _MASK \ + } + +enum cs35l45_irq_list { + CS35L45_AMP_SHORT_ERR_IRQ, + CS35L45_UVLO_VDDBATT_ERR_IRQ, + CS35L45_BST_SHORT_ERR_IRQ, + CS35L45_BST_UVP_ERR_IRQ, + CS35L45_TEMP_ERR_IRQ, + CS35L45_AMP_CAL_ERR_IRQ, + CS35L45_UVLO_VDDLV_ERR_IRQ, + CS35L45_GLOBAL_ERROR_IRQ, + CS35L45_DSP_WDT_EXPIRE_IRQ, + CS35L45_PLL_UNLOCK_FLAG_RISE_IRQ, + CS35L45_PLL_LOCK_FLAG_IRQ, + CS35L45_DSP_VIRT2_MBOX_IRQ, + CS35L45_NUM_IRQ +}; + +#define CS35L45_MBOX3_CMD_MASK 0xFF +#define CS35L45_MBOX3_CMD_SHIFT 0 +#define CS35L45_MBOX3_DATA_MASK 0xFFFFFF00 +#define CS35L45_MBOX3_DATA_SHIFT 8 + +enum mbox3_events { + EVENT_SPEAKER_STATUS = 0x66, + EVENT_BOOT_DONE = 0x67, +}; + +struct cs35l45_private { + struct wm_adsp dsp; /* needs to be first member */ + struct device *dev; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + struct regulator *vdd_batt; + struct regulator *vdd_a; + bool initialized; + bool sysclk_set; + u8 slot_width; + u8 slot_count; + int irq_invert; + int irq; + unsigned int i2c_addr; + enum control_bus_type bus_type; + struct regmap_irq_chip_data *irq_data; +}; + +extern const struct dev_pm_ops cs35l45_pm_ops; +extern const struct regmap_config cs35l45_i2c_regmap; +extern const struct regmap_config cs35l45_spi_regmap; +int cs35l45_apply_patch(struct cs35l45_private *cs35l45); +unsigned int cs35l45_get_clk_freq_id(unsigned int freq); +int cs35l45_probe(struct cs35l45_private *cs35l45); +void cs35l45_remove(struct cs35l45_private *cs35l45); + +#endif /* CS35L45_H */ diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c new file mode 100644 index 0000000000..d10e0e2380 --- /dev/null +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// CS35L56 ALSA SoC audio driver I2C binding +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l56.h" + +static int cs35l56_i2c_probe(struct i2c_client *client) +{ + struct cs35l56_private *cs35l56; + struct device *dev = &client->dev; + const struct regmap_config *regmap_config = &cs35l56_regmap_i2c; + int ret; + + cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL); + if (!cs35l56) + return -ENOMEM; + + cs35l56->base.dev = dev; + + i2c_set_clientdata(client, cs35l56); + cs35l56->base.regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(cs35l56->base.regmap)) { + ret = PTR_ERR(cs35l56->base.regmap); + return dev_err_probe(cs35l56->base.dev, ret, "Failed to allocate register map\n"); + } + + ret = cs35l56_common_probe(cs35l56); + if (ret != 0) + return ret; + + ret = cs35l56_init(cs35l56); + if (ret == 0) + ret = cs35l56_irq_request(&cs35l56->base, client->irq); + if (ret < 0) + cs35l56_remove(cs35l56); + + return ret; +} + +static void cs35l56_i2c_remove(struct i2c_client *client) +{ + struct cs35l56_private *cs35l56 = i2c_get_clientdata(client); + + cs35l56_remove(cs35l56); +} + +static const struct i2c_device_id cs35l56_id_i2c[] = { + { "cs35l56", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id cs35l56_asoc_acpi_match[] = { + { "CSC355C", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match); +#endif + +static struct i2c_driver cs35l56_i2c_driver = { + .driver = { + .name = "cs35l56", + .pm = &cs35l56_pm_ops_i2c_spi, + .acpi_match_table = ACPI_PTR(cs35l56_asoc_acpi_match), + }, + .id_table = cs35l56_id_i2c, + .probe = cs35l56_i2c_probe, + .remove = cs35l56_i2c_remove, +}; + +module_i2c_driver(cs35l56_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L56 I2C driver"); +MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Simon Trimmer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c new file mode 100644 index 0000000000..b433266b78 --- /dev/null +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// CS35L56 ALSA SoC audio driver SoundWire binding +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs35l56.h" + +/* Register addresses are offset when sent over SoundWire */ +#define CS35L56_SDW_ADDR_OFFSET 0x8000 + +static int cs35l56_sdw_read_one(struct sdw_slave *peripheral, unsigned int reg, void *buf) +{ + int ret; + + ret = sdw_nread_no_pm(peripheral, reg, 4, (u8 *)buf); + if (ret != 0) { + dev_err(&peripheral->dev, "Read failed @%#x:%d\n", reg, ret); + return ret; + } + + swab32s((u32 *)buf); + + return 0; +} + +static int cs35l56_sdw_read(void *context, const void *reg_buf, + const size_t reg_size, void *val_buf, + size_t val_size) +{ + struct sdw_slave *peripheral = context; + u8 *buf8 = val_buf; + unsigned int reg, bytes; + int ret; + + reg = le32_to_cpu(*(const __le32 *)reg_buf); + reg += CS35L56_SDW_ADDR_OFFSET; + + if (val_size == 4) + return cs35l56_sdw_read_one(peripheral, reg, val_buf); + + while (val_size) { + bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */ + if (bytes > val_size) + bytes = val_size; + + ret = sdw_nread_no_pm(peripheral, reg, bytes, buf8); + if (ret != 0) { + dev_err(&peripheral->dev, "Read failed @%#x..%#x:%d\n", + reg, reg + bytes - 1, ret); + return ret; + } + + swab32_array((u32 *)buf8, bytes / 4); + val_size -= bytes; + reg += bytes; + buf8 += bytes; + } + + return 0; +} + +static inline void cs35l56_swab_copy(void *dest, const void *src, size_t nbytes) +{ + u32 *dest32 = dest; + const u32 *src32 = src; + + for (; nbytes > 0; nbytes -= 4) + *dest32++ = swab32(*src32++); +} + +static int cs35l56_sdw_write_one(struct sdw_slave *peripheral, unsigned int reg, const void *buf) +{ + u32 val_le = swab32(*(u32 *)buf); + int ret; + + ret = sdw_nwrite_no_pm(peripheral, reg, 4, (u8 *)&val_le); + if (ret != 0) { + dev_err(&peripheral->dev, "Write failed @%#x:%d\n", reg, ret); + return ret; + } + + return 0; +} + +static int cs35l56_sdw_gather_write(void *context, + const void *reg_buf, size_t reg_size, + const void *val_buf, size_t val_size) +{ + struct sdw_slave *peripheral = context; + const u8 *src_be = val_buf; + u32 val_le_buf[64]; /* Define u32 so it is 32-bit aligned */ + unsigned int reg, bytes; + int ret; + + reg = le32_to_cpu(*(const __le32 *)reg_buf); + reg += CS35L56_SDW_ADDR_OFFSET; + + if (val_size == 4) + return cs35l56_sdw_write_one(peripheral, reg, src_be); + + while (val_size) { + bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */ + if (bytes > val_size) + bytes = val_size; + if (bytes > sizeof(val_le_buf)) + bytes = sizeof(val_le_buf); + + cs35l56_swab_copy(val_le_buf, src_be, bytes); + + ret = sdw_nwrite_no_pm(peripheral, reg, bytes, (u8 *)val_le_buf); + if (ret != 0) { + dev_err(&peripheral->dev, "Write failed @%#x..%#x:%d\n", + reg, reg + bytes - 1, ret); + return ret; + } + + val_size -= bytes; + reg += bytes; + src_be += bytes; + } + + return 0; +} + +static int cs35l56_sdw_write(void *context, const void *val_buf, size_t val_size) +{ + const u8 *src_buf = val_buf; + + /* First word of val_buf contains the destination address */ + return cs35l56_sdw_gather_write(context, &src_buf[0], 4, &src_buf[4], val_size - 4); +} + +/* + * Registers are big-endian on I2C and SPI but little-endian on SoundWire. + * Exported firmware controls are big-endian on I2C/SPI but little-endian on + * SoundWire. Firmware files are always big-endian and are opaque blobs. + * Present a big-endian regmap and hide the endianness swap, so that the ALSA + * byte controls always have the same byte order, and firmware file blobs + * can be written verbatim. + */ +static const struct regmap_bus cs35l56_regmap_bus_sdw = { + .read = cs35l56_sdw_read, + .write = cs35l56_sdw_write, + .gather_write = cs35l56_sdw_gather_write, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static void cs35l56_sdw_init(struct sdw_slave *peripheral) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + int ret; + + pm_runtime_get_noresume(cs35l56->base.dev); + + regcache_cache_only(cs35l56->base.regmap, false); + + ret = cs35l56_init(cs35l56); + if (ret < 0) { + regcache_cache_only(cs35l56->base.regmap, true); + goto out; + } + + /* + * cs35l56_init can return with !init_done if it triggered + * a soft reset. + */ + if (cs35l56->base.init_done) { + /* Enable SoundWire interrupts */ + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, + CS35L56_SDW_INT_MASK_CODEC_IRQ); + } + +out: + pm_runtime_mark_last_busy(cs35l56->base.dev); + pm_runtime_put_autosuspend(cs35l56->base.dev); +} + +static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, + struct sdw_slave_intr_status *status) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + + /* SoundWire core holds our pm_runtime when calling this function. */ + + dev_dbg(cs35l56->base.dev, "int control_port=%#x\n", status->control_port); + + if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0) + return 0; + + /* + * Prevent bus manager suspending and possibly issuing a + * bus-reset before the queued work has run. + */ + pm_runtime_get_noresume(cs35l56->base.dev); + + /* + * Mask and clear until it has been handled. The read of GEN_INT_STAT_1 + * is required as per the SoundWire spec for interrupt status bits + * to clear. GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1. + * None of the interrupts are time-critical so use the + * power-efficient queue. + */ + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work); + + return 0; +} + +static void cs35l56_sdw_irq_work(struct work_struct *work) +{ + struct cs35l56_private *cs35l56 = container_of(work, + struct cs35l56_private, + sdw_irq_work); + + cs35l56_irq(-1, &cs35l56->base); + + /* unmask interrupts */ + if (!cs35l56->sdw_irq_no_unmask) + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, + CS35L56_SDW_INT_MASK_CODEC_IRQ); + + pm_runtime_put_autosuspend(cs35l56->base.dev); +} + +static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + struct sdw_slave_prop *prop = &peripheral->prop; + struct sdw_dpn_prop *ports; + + ports = devm_kcalloc(cs35l56->base.dev, 2, sizeof(*ports), GFP_KERNEL); + if (!ports) + return -ENOMEM; + + prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT); + prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT); + prop->paging_support = true; + prop->clk_stop_mode1 = false; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF; + + /* DP1 - playback */ + ports[0].num = CS35L56_SDW1_PLAYBACK_PORT; + ports[0].type = SDW_DPN_FULL; + ports[0].ch_prep_timeout = 10; + prop->sink_dpn_prop = &ports[0]; + + /* DP3 - capture */ + ports[1].num = CS35L56_SDW1_CAPTURE_PORT; + ports[1].type = SDW_DPN_FULL; + ports[1].ch_prep_timeout = 10; + prop->src_dpn_prop = &ports[1]; + + return 0; +} + +static int cs35l56_sdw_update_status(struct sdw_slave *peripheral, + enum sdw_slave_status status) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + + switch (status) { + case SDW_SLAVE_ATTACHED: + dev_dbg(cs35l56->base.dev, "%s: ATTACHED\n", __func__); + if (cs35l56->sdw_attached) + break; + + if (!cs35l56->base.init_done || cs35l56->soft_resetting) + cs35l56_sdw_init(peripheral); + + cs35l56->sdw_attached = true; + break; + case SDW_SLAVE_UNATTACHED: + dev_dbg(cs35l56->base.dev, "%s: UNATTACHED\n", __func__); + cs35l56->sdw_attached = false; + break; + default: + break; + } + + return 0; +} + +static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56, + struct sdw_slave *peripheral) +{ + unsigned int curr_scale_reg, next_scale_reg; + int curr_scale, next_scale, ret; + + if (!cs35l56->base.init_done) + return 0; + + if (peripheral->bus->params.curr_bank) { + curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; + next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; + } else { + curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0; + next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1; + } + + /* + * Current clock scale value must be different to new value. + * Modify current to guarantee this. If next still has the dummy + * value we wrote when it was current, the core code has not set + * a new scale so restore its original good value + */ + curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg); + if (curr_scale < 0) { + dev_err(cs35l56->base.dev, "Failed to read current clock scale: %d\n", curr_scale); + return curr_scale; + } + + next_scale = sdw_read_no_pm(peripheral, next_scale_reg); + if (next_scale < 0) { + dev_err(cs35l56->base.dev, "Failed to read next clock scale: %d\n", next_scale); + return next_scale; + } + + if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) { + next_scale = cs35l56->old_sdw_clock_scale; + ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale); + if (ret < 0) { + dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", + ret); + return ret; + } + } + + cs35l56->old_sdw_clock_scale = curr_scale; + ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE); + if (ret < 0) { + dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", ret); + return ret; + } + + dev_dbg(cs35l56->base.dev, "Next bus scale: %#x\n", next_scale); + + return 0; +} + +static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral, + struct sdw_bus_params *params) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + int sclk; + + sclk = params->curr_dr_freq / 2; + dev_dbg(cs35l56->base.dev, "%s: sclk=%u c=%u r=%u\n", + __func__, sclk, params->col, params->row); + + if (cs35l56->base.rev < 0xb0) + return cs35l56_a1_kick_divider(cs35l56, peripheral); + + return 0; +} + +static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral, + enum sdw_clk_stop_mode mode, + enum sdw_clk_stop_type type) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + + dev_dbg(cs35l56->base.dev, "%s: mode:%d type:%d\n", __func__, mode, type); + + return 0; +} + +static const struct sdw_slave_ops cs35l56_sdw_ops = { + .read_prop = cs35l56_sdw_read_prop, + .interrupt_callback = cs35l56_sdw_interrupt, + .update_status = cs35l56_sdw_update_status, + .bus_config = cs35l56_sdw_bus_config, +#ifdef DEBUG + .clk_stop = cs35l56_sdw_clk_stop, +#endif +}; + +static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs35l56) +{ + struct sdw_slave *peripheral = cs35l56->sdw_peripheral; + + if (peripheral->unattach_request) { + /* Cannot access registers until bus is re-initialized. */ + dev_dbg(cs35l56->base.dev, "Wait for initialization_complete\n"); + if (!wait_for_completion_timeout(&peripheral->initialization_complete, + msecs_to_jiffies(5000))) { + dev_err(cs35l56->base.dev, "initialization_complete timed out\n"); + return -ETIMEDOUT; + } + + peripheral->unattach_request = 0; + + /* + * Don't call regcache_mark_dirty(), we can't be sure that the + * Manager really did issue a Bus Reset. + */ + } + + return 0; +} + +static int __maybe_unused cs35l56_sdw_runtime_suspend(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + if (!cs35l56->base.init_done) + return 0; + + return cs35l56_runtime_suspend_common(&cs35l56->base); +} + +static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "Runtime resume\n"); + + if (!cs35l56->base.init_done) + return 0; + + ret = cs35l56_sdw_handle_unattach(cs35l56); + if (ret < 0) + return ret; + + ret = cs35l56_runtime_resume_common(&cs35l56->base, true); + if (ret) + return ret; + + /* Re-enable SoundWire interrupts */ + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, + CS35L56_SDW_INT_MASK_CODEC_IRQ); + + return 0; +} + +static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + if (!cs35l56->base.init_done) + return 0; + + /* + * Disable SoundWire interrupts. + * Flush - don't cancel because that could leave an unbalanced pm_runtime_get. + */ + cs35l56->sdw_irq_no_unmask = true; + flush_work(&cs35l56->sdw_irq_work); + + /* Mask interrupts and flush in case sdw_irq_work was queued again */ + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + flush_work(&cs35l56->sdw_irq_work); + + return cs35l56_system_suspend(dev); +} + +static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + cs35l56->sdw_irq_no_unmask = false; + /* runtime_resume re-enables the interrupt */ + + return cs35l56_system_resume(dev); +} + +static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) +{ + struct device *dev = &peripheral->dev; + struct cs35l56_private *cs35l56; + int ret; + + cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL); + if (!cs35l56) + return -ENOMEM; + + cs35l56->base.dev = dev; + cs35l56->sdw_peripheral = peripheral; + INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work); + + dev_set_drvdata(dev, cs35l56); + + cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw, + peripheral, &cs35l56_regmap_sdw); + if (IS_ERR(cs35l56->base.regmap)) { + ret = PTR_ERR(cs35l56->base.regmap); + return dev_err_probe(dev, ret, "Failed to allocate register map\n"); + } + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(cs35l56->base.regmap, true); + + ret = cs35l56_common_probe(cs35l56); + if (ret != 0) + return ret; + + return 0; +} + +static int cs35l56_sdw_remove(struct sdw_slave *peripheral) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + + /* Disable SoundWire interrupts */ + cs35l56->sdw_irq_no_unmask = true; + cancel_work_sync(&cs35l56->sdw_irq_work); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + + cs35l56_remove(cs35l56); + + return 0; +} + +static const struct dev_pm_ops cs35l56_sdw_pm = { + SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume) + LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) + /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */ +}; + +static const struct sdw_device_id cs35l56_sdw_id[] = { + SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id); + +static struct sdw_driver cs35l56_sdw_driver = { + .driver = { + .name = "cs35l56", + .pm = &cs35l56_sdw_pm, + }, + .probe = cs35l56_sdw_probe, + .remove = cs35l56_sdw_remove, + .ops = &cs35l56_sdw_ops, + .id_table = cs35l56_sdw_id, +}; + +module_sdw_driver(cs35l56_sdw_driver); + +MODULE_DESCRIPTION("ASoC CS35L56 SoundWire driver"); +MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Simon Trimmer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c new file mode 100644 index 0000000000..98b1e63360 --- /dev/null +++ b/sound/soc/codecs/cs35l56-shared.c @@ -0,0 +1,807 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Components shared between ASoC and HDA CS35L56 drivers +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include + +#include "cs35l56.h" + +static const struct reg_sequence cs35l56_patch[] = { + /* These are not reset by a soft-reset, so patch to defaults. */ + { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 }, + { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 }, + { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 }, +}; + +int cs35l56_set_patch(struct cs35l56_base *cs35l56_base) +{ + return regmap_register_patch(cs35l56_base->regmap, cs35l56_patch, + ARRAY_SIZE(cs35l56_patch)); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, SND_SOC_CS35L56_SHARED); + +static const struct reg_default cs35l56_reg_defaults[] = { + { CS35L56_ASP1_ENABLES1, 0x00000000 }, + { CS35L56_ASP1_CONTROL1, 0x00000028 }, + { CS35L56_ASP1_CONTROL2, 0x18180200 }, + { CS35L56_ASP1_CONTROL3, 0x00000002 }, + { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 }, + { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 }, + { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 }, + { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 }, + { CS35L56_ASP1TX1_INPUT, 0x00000018 }, + { CS35L56_ASP1TX2_INPUT, 0x00000019 }, + { CS35L56_ASP1TX3_INPUT, 0x00000020 }, + { CS35L56_ASP1TX4_INPUT, 0x00000028 }, + { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 }, + { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 }, + { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 }, + { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 }, + { CS35L56_IRQ1_CFG, 0x00000000 }, + { CS35L56_IRQ1_MASK_1, 0x83ffffff }, + { CS35L56_IRQ1_MASK_2, 0xffff7fff }, + { CS35L56_IRQ1_MASK_4, 0xe0ffffff }, + { CS35L56_IRQ1_MASK_8, 0xfc000fff }, + { CS35L56_IRQ1_MASK_18, 0x1f7df0ff }, + { CS35L56_IRQ1_MASK_20, 0x15c00000 }, + { CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 }, + { CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 }, + { CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 }, +}; + +static bool cs35l56_is_dsp_memory(unsigned int reg) +{ + switch (reg) { + case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143: + case CS35L56_DSP1_XMEM_UNPACKED32_0 ... CS35L56_DSP1_XMEM_UNPACKED32_4095: + case CS35L56_DSP1_XMEM_UNPACKED24_0 ... CS35L56_DSP1_XMEM_UNPACKED24_8191: + case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604: + case CS35L56_DSP1_YMEM_UNPACKED32_0 ... CS35L56_DSP1_YMEM_UNPACKED32_3070: + case CS35L56_DSP1_YMEM_UNPACKED24_0 ... CS35L56_DSP1_YMEM_UNPACKED24_6141: + case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114: + return true; + default: + return false; + } +} + +static bool cs35l56_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L56_DEVID: + case CS35L56_REVID: + case CS35L56_RELID: + case CS35L56_OTPID: + case CS35L56_SFT_RESET: + case CS35L56_GLOBAL_ENABLES: + case CS35L56_BLOCK_ENABLES: + case CS35L56_BLOCK_ENABLES2: + case CS35L56_REFCLK_INPUT: + case CS35L56_GLOBAL_SAMPLE_RATE: + case CS35L56_ASP1_ENABLES1: + case CS35L56_ASP1_CONTROL1: + case CS35L56_ASP1_CONTROL2: + case CS35L56_ASP1_CONTROL3: + case CS35L56_ASP1_FRAME_CONTROL1: + case CS35L56_ASP1_FRAME_CONTROL5: + case CS35L56_ASP1_DATA_CONTROL1: + case CS35L56_ASP1_DATA_CONTROL5: + case CS35L56_DACPCM1_INPUT: + case CS35L56_DACPCM2_INPUT: + case CS35L56_ASP1TX1_INPUT: + case CS35L56_ASP1TX2_INPUT: + case CS35L56_ASP1TX3_INPUT: + case CS35L56_ASP1TX4_INPUT: + case CS35L56_DSP1RX1_INPUT: + case CS35L56_DSP1RX2_INPUT: + case CS35L56_SWIRE_DP3_CH1_INPUT: + case CS35L56_SWIRE_DP3_CH2_INPUT: + case CS35L56_SWIRE_DP3_CH3_INPUT: + case CS35L56_SWIRE_DP3_CH4_INPUT: + case CS35L56_IRQ1_CFG: + case CS35L56_IRQ1_STATUS: + case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8: + case CS35L56_IRQ1_EINT_18: + case CS35L56_IRQ1_EINT_20: + case CS35L56_IRQ1_MASK_1: + case CS35L56_IRQ1_MASK_2: + case CS35L56_IRQ1_MASK_4: + case CS35L56_IRQ1_MASK_8: + case CS35L56_IRQ1_MASK_18: + case CS35L56_IRQ1_MASK_20: + case CS35L56_DSP_VIRTUAL1_MBOX_1: + case CS35L56_DSP_VIRTUAL1_MBOX_2: + case CS35L56_DSP_VIRTUAL1_MBOX_3: + case CS35L56_DSP_VIRTUAL1_MBOX_4: + case CS35L56_DSP_VIRTUAL1_MBOX_5: + case CS35L56_DSP_VIRTUAL1_MBOX_6: + case CS35L56_DSP_VIRTUAL1_MBOX_7: + case CS35L56_DSP_VIRTUAL1_MBOX_8: + case CS35L56_DSP_RESTRICT_STS1: + case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END: + case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0: + case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1: + case CS35L56_DSP1_SCRATCH1: + case CS35L56_DSP1_SCRATCH2: + case CS35L56_DSP1_SCRATCH3: + case CS35L56_DSP1_SCRATCH4: + return true; + default: + return cs35l56_is_dsp_memory(reg); + } +} + +static bool cs35l56_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143: + case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604: + case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114: + return true; + default: + return false; + } +} + +static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L56_DEVID: + case CS35L56_REVID: + case CS35L56_RELID: + case CS35L56_OTPID: + case CS35L56_SFT_RESET: + case CS35L56_GLOBAL_ENABLES: /* owned by firmware */ + case CS35L56_BLOCK_ENABLES: /* owned by firmware */ + case CS35L56_BLOCK_ENABLES2: /* owned by firmware */ + case CS35L56_REFCLK_INPUT: /* owned by firmware */ + case CS35L56_GLOBAL_SAMPLE_RATE: /* owned by firmware */ + case CS35L56_DACPCM1_INPUT: /* owned by firmware */ + case CS35L56_DACPCM2_INPUT: /* owned by firmware */ + case CS35L56_DSP1RX1_INPUT: /* owned by firmware */ + case CS35L56_DSP1RX2_INPUT: /* owned by firmware */ + case CS35L56_IRQ1_STATUS: + case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8: + case CS35L56_IRQ1_EINT_18: + case CS35L56_IRQ1_EINT_20: + case CS35L56_DSP_VIRTUAL1_MBOX_1: + case CS35L56_DSP_VIRTUAL1_MBOX_2: + case CS35L56_DSP_VIRTUAL1_MBOX_3: + case CS35L56_DSP_VIRTUAL1_MBOX_4: + case CS35L56_DSP_VIRTUAL1_MBOX_5: + case CS35L56_DSP_VIRTUAL1_MBOX_6: + case CS35L56_DSP_VIRTUAL1_MBOX_7: + case CS35L56_DSP_VIRTUAL1_MBOX_8: + case CS35L56_DSP_RESTRICT_STS1: + case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END: + case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0: + case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1: + case CS35L56_DSP1_SCRATCH1: + case CS35L56_DSP1_SCRATCH2: + case CS35L56_DSP1_SCRATCH3: + case CS35L56_DSP1_SCRATCH4: + return true; + case CS35L56_MAIN_RENDER_USER_MUTE: + case CS35L56_MAIN_RENDER_USER_VOLUME: + case CS35L56_MAIN_POSTURE_NUMBER: + return false; + default: + return cs35l56_is_dsp_memory(reg); + } +} + +int cs35l56_mbox_send(struct cs35l56_base *cs35l56_base, unsigned int command) +{ + unsigned int val; + int ret; + + regmap_write(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command); + ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + val, (val == 0), + CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US); + if (ret) { + dev_warn(cs35l56_base->dev, "MBOX command %#x failed: %d\n", command, ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_mbox_send, SND_SOC_CS35L56_SHARED); + +int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base) +{ + int ret; + unsigned int reg; + unsigned int val; + + ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_SHUTDOWN); + if (ret) + return ret; + + if (cs35l56_base->rev < CS35L56_REVID_B0) + reg = CS35L56_DSP1_PM_CUR_STATE_A1; + else + reg = CS35L56_DSP1_PM_CUR_STATE; + + ret = regmap_read_poll_timeout(cs35l56_base->regmap, reg, + val, (val == CS35L56_HALO_STATE_SHUTDOWN), + CS35L56_HALO_STATE_POLL_US, + CS35L56_HALO_STATE_TIMEOUT_US); + if (ret < 0) + dev_err(cs35l56_base->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n", + val, ret); + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_firmware_shutdown, SND_SOC_CS35L56_SHARED); + +int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base) +{ + unsigned int reg; + unsigned int val; + int read_ret, poll_ret; + + if (cs35l56_base->rev < CS35L56_REVID_B0) + reg = CS35L56_DSP1_HALO_STATE_A1; + else + reg = CS35L56_DSP1_HALO_STATE; + + /* + * This can't be a regmap_read_poll_timeout() because cs35l56 will NAK + * I2C until it has booted which would terminate the poll + */ + poll_ret = read_poll_timeout(regmap_read, read_ret, + (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE), + CS35L56_HALO_STATE_POLL_US, + CS35L56_HALO_STATE_TIMEOUT_US, + false, + cs35l56_base->regmap, reg, &val); + + if (poll_ret) { + dev_err(cs35l56_base->dev, "Firmware boot timed out(%d): HALO_STATE=%#x\n", + read_ret, val); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_wait_for_firmware_boot, SND_SOC_CS35L56_SHARED); + +void cs35l56_wait_control_port_ready(void) +{ + /* Wait for control port to be ready (datasheet tIRS). */ + usleep_range(CS35L56_CONTROL_PORT_READY_US, 2 * CS35L56_CONTROL_PORT_READY_US); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_wait_control_port_ready, SND_SOC_CS35L56_SHARED); + +void cs35l56_wait_min_reset_pulse(void) +{ + /* Satisfy minimum reset pulse width spec */ + usleep_range(CS35L56_RESET_PULSE_MIN_US, 2 * CS35L56_RESET_PULSE_MIN_US); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_wait_min_reset_pulse, SND_SOC_CS35L56_SHARED); + +static const struct reg_sequence cs35l56_system_reset_seq[] = { + REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET), +}; + +void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire) +{ + /* + * Must enter cache-only first so there can't be any more register + * accesses other than the controlled system reset sequence below. + */ + regcache_cache_only(cs35l56_base->regmap, true); + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l56_system_reset_seq, + ARRAY_SIZE(cs35l56_system_reset_seq)); + + /* On SoundWire the registers won't be accessible until it re-enumerates. */ + if (is_soundwire) + return; + + cs35l56_wait_control_port_ready(); + regcache_cache_only(cs35l56_base->regmap, false); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_system_reset, SND_SOC_CS35L56_SHARED); + +int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq) +{ + int ret; + + if (!irq) + return 0; + + ret = devm_request_threaded_irq(cs35l56_base->dev, irq, NULL, cs35l56_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "cs35l56", cs35l56_base); + if (!ret) + cs35l56_base->irq = irq; + else + dev_err(cs35l56_base->dev, "Failed to get IRQ: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, SND_SOC_CS35L56_SHARED); + +irqreturn_t cs35l56_irq(int irq, void *data) +{ + struct cs35l56_base *cs35l56_base = data; + unsigned int status1 = 0, status8 = 0, status20 = 0; + unsigned int mask1, mask8, mask20; + unsigned int val; + int rv; + + irqreturn_t ret = IRQ_NONE; + + if (!cs35l56_base->init_done) + return IRQ_NONE; + + mutex_lock(&cs35l56_base->irq_lock); + + rv = pm_runtime_resume_and_get(cs35l56_base->dev); + if (rv < 0) { + dev_err(cs35l56_base->dev, "irq: failed to get pm_runtime: %d\n", rv); + goto err_unlock; + } + + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val); + if ((val & CS35L56_IRQ1_STS_MASK) == 0) { + dev_dbg(cs35l56_base->dev, "Spurious IRQ: no pending interrupt\n"); + goto err; + } + + /* Ack interrupts */ + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, &status1); + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1, &mask1); + status1 &= ~mask1; + regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_1, status1); + + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, &status8); + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8, &mask8); + status8 &= ~mask8; + regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_8, status8); + + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_20, &status20); + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, &mask20); + status20 &= ~mask20; + /* We don't want EINT20 but they default to unmasked: force mask */ + regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); + + dev_dbg(cs35l56_base->dev, "%s: %#x %#x\n", __func__, status1, status8); + + /* Check to see if unmasked bits are active */ + if (!status1 && !status8 && !status20) + goto err; + + if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK) + dev_crit(cs35l56_base->dev, "Amp short error\n"); + + if (status8 & CS35L56_TEMP_ERR_EINT1_MASK) + dev_crit(cs35l56_base->dev, "Overtemp error\n"); + + ret = IRQ_HANDLED; + +err: + pm_runtime_put(cs35l56_base->dev); +err_unlock: + mutex_unlock(&cs35l56_base->irq_lock); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_irq, SND_SOC_CS35L56_SHARED); + +int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base) +{ + unsigned int val; + int ret; + + /* Nothing to re-patch if we haven't done any patching yet. */ + if (!cs35l56_base->fw_patched) + return false; + + /* + * If we have control of RESET we will have asserted it so the firmware + * will need re-patching. + */ + if (cs35l56_base->reset_gpio) + return true; + + /* + * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so + * can't be used here to test for memory retention. + * Assume that tuning must be re-loaded. + */ + if (cs35l56_base->secured) + return true; + + ret = pm_runtime_resume_and_get(cs35l56_base->dev); + if (ret) { + dev_err(cs35l56_base->dev, "Failed to runtime_get: %d\n", ret); + return ret; + } + + ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &val); + if (ret) + dev_err(cs35l56_base->dev, "Failed to read PROTECTION_STATUS: %d\n", ret); + else + ret = !!(val & CS35L56_FIRMWARE_MISSING); + + pm_runtime_put_autosuspend(cs35l56_base->dev); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_is_fw_reload_needed, SND_SOC_CS35L56_SHARED); + +static const struct reg_sequence cs35l56_hibernate_seq[] = { + /* This must be the last register access */ + REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_HIBERNATE_NOW), +}; + +static const struct reg_sequence cs35l56_hibernate_wake_seq[] = { + REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_WAKEUP), +}; + +int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base) +{ + unsigned int val; + int ret; + + if (!cs35l56_base->init_done) + return 0; + + /* Firmware must have entered a power-save state */ + ret = regmap_read_poll_timeout(cs35l56_base->regmap, + CS35L56_TRANSDUCER_ACTUAL_PS, + val, (val >= CS35L56_PS3), + CS35L56_PS3_POLL_US, + CS35L56_PS3_TIMEOUT_US); + if (ret) + dev_warn(cs35l56_base->dev, "PS3 wait failed: %d\n", ret); + + /* Clear BOOT_DONE so it can be used to detect a reboot */ + regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK); + + if (!cs35l56_base->can_hibernate) { + regcache_cache_only(cs35l56_base->regmap, true); + dev_dbg(cs35l56_base->dev, "Suspended: no hibernate"); + + return 0; + } + + /* + * Enable auto-hibernate. If it is woken by some other wake source + * it will automatically return to hibernate. + */ + cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE); + + /* + * Must enter cache-only first so there can't be any more register + * accesses other than the controlled hibernate sequence below. + */ + regcache_cache_only(cs35l56_base->regmap, true); + + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l56_hibernate_seq, + ARRAY_SIZE(cs35l56_hibernate_seq)); + + dev_dbg(cs35l56_base->dev, "Suspended: hibernate"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend_common, SND_SOC_CS35L56_SHARED); + +int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire) +{ + unsigned int val; + int ret; + + if (!cs35l56_base->init_done) + return 0; + + if (!cs35l56_base->can_hibernate) + goto out_sync; + + if (!is_soundwire) { + /* + * Dummy transaction to trigger I2C/SPI auto-wake. This will NAK on I2C. + * Must be done before releasing cache-only. + */ + regmap_multi_reg_write_bypassed(cs35l56_base->regmap, + cs35l56_hibernate_wake_seq, + ARRAY_SIZE(cs35l56_hibernate_wake_seq)); + + cs35l56_wait_control_port_ready(); + } + +out_sync: + regcache_cache_only(cs35l56_base->regmap, false); + + ret = cs35l56_wait_for_firmware_boot(cs35l56_base); + if (ret) { + dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret); + goto err; + } + + ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); + if (ret) + goto err; + + /* BOOT_DONE will be 1 if the amp reset */ + regmap_read(cs35l56_base->regmap, CS35L56_IRQ1_EINT_4, &val); + if (val & CS35L56_OTP_BOOT_DONE_MASK) { + dev_dbg(cs35l56_base->dev, "Registers reset in suspend\n"); + regcache_mark_dirty(cs35l56_base->regmap); + } + + regcache_sync(cs35l56_base->regmap); + + dev_dbg(cs35l56_base->dev, "Resumed"); + + return 0; + +err: + regmap_write(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + CS35L56_MBOX_CMD_HIBERNATE_NOW); + + regcache_cache_only(cs35l56_base->regmap, true); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_SHARED); + +static const struct cs_dsp_region cs35l56_dsp1_regions[] = { + { .type = WMFW_HALO_PM_PACKED, .base = CS35L56_DSP1_PMEM_0 }, + { .type = WMFW_HALO_XM_PACKED, .base = CS35L56_DSP1_XMEM_PACKED_0 }, + { .type = WMFW_HALO_YM_PACKED, .base = CS35L56_DSP1_YMEM_PACKED_0 }, + { .type = WMFW_ADSP2_XM, .base = CS35L56_DSP1_XMEM_UNPACKED24_0 }, + { .type = WMFW_ADSP2_YM, .base = CS35L56_DSP1_YMEM_UNPACKED24_0 }, +}; + +void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp) +{ + cs_dsp->num = 1; + cs_dsp->type = WMFW_HALO; + cs_dsp->rev = 0; + cs_dsp->dev = cs35l56_base->dev; + cs_dsp->regmap = cs35l56_base->regmap; + cs_dsp->base = CS35L56_DSP1_CORE_BASE; + cs_dsp->base_sysinfo = CS35L56_DSP1_SYS_INFO_ID; + cs_dsp->mem = cs35l56_dsp1_regions; + cs_dsp->num_mems = ARRAY_SIZE(cs35l56_dsp1_regions); + cs_dsp->no_core_startstop = true; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_init_cs_dsp, SND_SOC_CS35L56_SHARED); + +int cs35l56_hw_init(struct cs35l56_base *cs35l56_base) +{ + int ret; + unsigned int devid, revid, otpid, secured; + + /* + * If the system is not using a reset_gpio then issue a + * dummy read to force a wakeup. + */ + if (!cs35l56_base->reset_gpio) + regmap_read(cs35l56_base->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, &devid); + + cs35l56_wait_control_port_ready(); + + /* + * The HALO_STATE register is in different locations on Ax and B0 + * devices so the REVID needs to be determined before waiting for the + * firmware to boot. + */ + ret = regmap_read(cs35l56_base->regmap, CS35L56_REVID, &revid); + if (ret < 0) { + dev_err(cs35l56_base->dev, "Get Revision ID failed\n"); + return ret; + } + cs35l56_base->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK); + + ret = cs35l56_wait_for_firmware_boot(cs35l56_base); + if (ret) + return ret; + + ret = regmap_read(cs35l56_base->regmap, CS35L56_DEVID, &devid); + if (ret < 0) { + dev_err(cs35l56_base->dev, "Get Device ID failed\n"); + return ret; + } + devid &= CS35L56_DEVID_MASK; + + switch (devid) { + case 0x35A56: + break; + default: + dev_err(cs35l56_base->dev, "Unknown device %x\n", devid); + return ret; + } + + ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP_RESTRICT_STS1, &secured); + if (ret) { + dev_err(cs35l56_base->dev, "Get Secure status failed\n"); + return ret; + } + + /* When any bus is restricted treat the device as secured */ + if (secured & CS35L56_RESTRICTED_MASK) + cs35l56_base->secured = true; + + ret = regmap_read(cs35l56_base->regmap, CS35L56_OTPID, &otpid); + if (ret < 0) { + dev_err(cs35l56_base->dev, "Get OTP ID failed\n"); + return ret; + } + + dev_info(cs35l56_base->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n", + cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid); + + /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ + regmap_write(cs35l56_base->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); + regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_1, + CS35L56_AMP_SHORT_ERR_EINT1_MASK, + 0); + regmap_update_bits(cs35l56_base->regmap, CS35L56_IRQ1_MASK_8, + CS35L56_TEMP_ERR_EINT1_MASK, + 0); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_hw_init, SND_SOC_CS35L56_SHARED); + +static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = { + [0x0C] = 128000, + [0x0F] = 256000, + [0x11] = 384000, + [0x12] = 512000, + [0x15] = 768000, + [0x17] = 1024000, + [0x1A] = 1500000, + [0x1B] = 1536000, + [0x1C] = 2000000, + [0x1D] = 2048000, + [0x1E] = 2400000, + [0x20] = 3000000, + [0x21] = 3072000, + [0x23] = 4000000, + [0x24] = 4096000, + [0x25] = 4800000, + [0x27] = 6000000, + [0x28] = 6144000, + [0x29] = 6250000, + [0x2A] = 6400000, + [0x2E] = 8000000, + [0x2F] = 8192000, + [0x30] = 9600000, + [0x32] = 12000000, + [0x33] = 12288000, + [0x37] = 13500000, + [0x38] = 19200000, + [0x39] = 22579200, + [0x3B] = 24576000, +}; + +int cs35l56_get_bclk_freq_id(unsigned int freq) +{ + int i; + + if (freq == 0) + return -EINVAL; + + /* The BCLK frequency must be a valid PLL REFCLK */ + for (i = 0; i < ARRAY_SIZE(cs35l56_bclk_valid_for_pll_freq_table); ++i) { + if (cs35l56_bclk_valid_for_pll_freq_table[i] == freq) + return i; + } + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_get_bclk_freq_id, SND_SOC_CS35L56_SHARED); + +static const char * const cs35l56_supplies[/* auto-sized */] = { + "VDD_P", + "VDD_IO", + "VDD_A", +}; + +void cs35l56_fill_supply_names(struct regulator_bulk_data *data) +{ + int i; + + BUILD_BUG_ON(ARRAY_SIZE(cs35l56_supplies) != CS35L56_NUM_BULK_SUPPLIES); + for (i = 0; i < ARRAY_SIZE(cs35l56_supplies); i++) + data[i].supply = cs35l56_supplies[i]; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_fill_supply_names, SND_SOC_CS35L56_SHARED); + +const char * const cs35l56_tx_input_texts[] = { + "None", "ASP1RX1", "ASP1RX2", "VMON", "IMON", "ERRVOL", "CLASSH", + "VDDBMON", "VBSTMON", "DSP1TX1", "DSP1TX2", "DSP1TX3", "DSP1TX4", + "DSP1TX5", "DSP1TX6", "DSP1TX7", "DSP1TX8", "TEMPMON", + "INTERPOLATOR", "SDW1RX1", "SDW1RX2", +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_texts, SND_SOC_CS35L56_SHARED); + +const unsigned int cs35l56_tx_input_values[] = { + CS35L56_INPUT_SRC_NONE, + CS35L56_INPUT_SRC_ASP1RX1, + CS35L56_INPUT_SRC_ASP1RX2, + CS35L56_INPUT_SRC_VMON, + CS35L56_INPUT_SRC_IMON, + CS35L56_INPUT_SRC_ERR_VOL, + CS35L56_INPUT_SRC_CLASSH, + CS35L56_INPUT_SRC_VDDBMON, + CS35L56_INPUT_SRC_VBSTMON, + CS35L56_INPUT_SRC_DSP1TX1, + CS35L56_INPUT_SRC_DSP1TX2, + CS35L56_INPUT_SRC_DSP1TX3, + CS35L56_INPUT_SRC_DSP1TX4, + CS35L56_INPUT_SRC_DSP1TX5, + CS35L56_INPUT_SRC_DSP1TX6, + CS35L56_INPUT_SRC_DSP1TX7, + CS35L56_INPUT_SRC_DSP1TX8, + CS35L56_INPUT_SRC_TEMPMON, + CS35L56_INPUT_SRC_INTERPOLATOR, + CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL1, + CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL2, +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_values, SND_SOC_CS35L56_SHARED); + +struct regmap_config cs35l56_regmap_i2c = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L56_DSP1_PMEM_5114, + .reg_defaults = cs35l56_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults), + .volatile_reg = cs35l56_volatile_reg, + .readable_reg = cs35l56_readable_reg, + .precious_reg = cs35l56_precious_reg, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_i2c, SND_SOC_CS35L56_SHARED); + +struct regmap_config cs35l56_regmap_spi = { + .reg_bits = 32, + .val_bits = 32, + .pad_bits = 16, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L56_DSP1_PMEM_5114, + .reg_defaults = cs35l56_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults), + .volatile_reg = cs35l56_volatile_reg, + .readable_reg = cs35l56_readable_reg, + .precious_reg = cs35l56_precious_reg, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, SND_SOC_CS35L56_SHARED); + +struct regmap_config cs35l56_regmap_sdw = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS35L56_DSP1_PMEM_5114, + .reg_defaults = cs35l56_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults), + .volatile_reg = cs35l56_volatile_reg, + .readable_reg = cs35l56_readable_reg, + .precious_reg = cs35l56_precious_reg, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, SND_SOC_CS35L56_SHARED); + +MODULE_DESCRIPTION("ASoC CS35L56 Shared"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Simon Trimmer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c new file mode 100644 index 0000000000..9962703915 --- /dev/null +++ b/sound/soc/codecs/cs35l56-spi.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// CS35L56 ALSA SoC audio driver SPI binding +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include + +#include "cs35l56.h" + +static int cs35l56_spi_probe(struct spi_device *spi) +{ + const struct regmap_config *regmap_config = &cs35l56_regmap_spi; + struct cs35l56_private *cs35l56; + int ret; + + cs35l56 = devm_kzalloc(&spi->dev, sizeof(struct cs35l56_private), GFP_KERNEL); + if (!cs35l56) + return -ENOMEM; + + spi_set_drvdata(spi, cs35l56); + cs35l56->base.regmap = devm_regmap_init_spi(spi, regmap_config); + if (IS_ERR(cs35l56->base.regmap)) { + ret = PTR_ERR(cs35l56->base.regmap); + return dev_err_probe(&spi->dev, ret, "Failed to allocate register map\n"); + } + + cs35l56->base.dev = &spi->dev; + + ret = cs35l56_common_probe(cs35l56); + if (ret != 0) + return ret; + + ret = cs35l56_init(cs35l56); + if (ret == 0) + ret = cs35l56_irq_request(&cs35l56->base, spi->irq); + if (ret < 0) + cs35l56_remove(cs35l56); + + return ret; +} + +static void cs35l56_spi_remove(struct spi_device *spi) +{ + struct cs35l56_private *cs35l56 = spi_get_drvdata(spi); + + cs35l56_remove(cs35l56); +} + +static const struct spi_device_id cs35l56_id_spi[] = { + { "cs35l56", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, cs35l56_id_spi); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id cs35l56_asoc_acpi_match[] = { + { "CSC355C", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match); +#endif + +static struct spi_driver cs35l56_spi_driver = { + .driver = { + .name = "cs35l56", + .pm = &cs35l56_pm_ops_i2c_spi, + .acpi_match_table = ACPI_PTR(cs35l56_asoc_acpi_match), + }, + .id_table = cs35l56_id_spi, + .probe = cs35l56_spi_probe, + .remove = cs35l56_spi_remove, +}; + +module_spi_driver(cs35l56_spi_driver); + +MODULE_DESCRIPTION("ASoC CS35L56 SPI driver"); +MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Simon Trimmer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c new file mode 100644 index 0000000000..32d4ab2cd6 --- /dev/null +++ b/sound/soc/codecs/cs35l56.c @@ -0,0 +1,1250 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Driver for Cirrus Logic CS35L56 smart amp +// +// Copyright (C) 2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm_adsp.h" +#include "cs35l56.h" + +static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +static void cs35l56_wait_dsp_ready(struct cs35l56_private *cs35l56) +{ + /* Wait for patching to complete */ + flush_work(&cs35l56->dsp_work); +} + +static int cs35l56_dspwait_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + + cs35l56_wait_dsp_ready(cs35l56); + return snd_soc_get_volsw(kcontrol, ucontrol); +} + +static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + + cs35l56_wait_dsp_ready(cs35l56); + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0); + +static const struct snd_kcontrol_new cs35l56_controls[] = { + SOC_SINGLE_EXT("Speaker Switch", + CS35L56_MAIN_RENDER_USER_MUTE, 0, 1, 1, + cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), + SOC_SINGLE_S_EXT_TLV("Speaker Volume", + CS35L56_MAIN_RENDER_USER_VOLUME, + 6, -400, 400, 9, 0, + cs35l56_dspwait_get_volsw, + cs35l56_dspwait_put_volsw, + vol_tlv), + SOC_SINGLE_EXT("Posture Number", CS35L56_MAIN_POSTURE_NUMBER, + 0, 255, 0, + cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw), +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum, + CS35L56_ASP1TX1_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new asp1_tx1_mux = + SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum, + CS35L56_ASP1TX2_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new asp1_tx2_mux = + SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum, + CS35L56_ASP1TX3_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new asp1_tx3_mux = + SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum, + CS35L56_ASP1TX4_INPUT, + 0, CS35L56_ASP_TXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new asp1_tx4_mux = + SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum, + CS35L56_SWIRE_DP3_CH1_INPUT, + 0, CS35L56_SWIRETXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new sdw1_tx1_mux = + SOC_DAPM_ENUM("SDW1TX1 SRC", cs35l56_sdw1tx1_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx2_enum, + CS35L56_SWIRE_DP3_CH2_INPUT, + 0, CS35L56_SWIRETXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new sdw1_tx2_mux = + SOC_DAPM_ENUM("SDW1TX2 SRC", cs35l56_sdw1tx2_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx3_enum, + CS35L56_SWIRE_DP3_CH3_INPUT, + 0, CS35L56_SWIRETXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new sdw1_tx3_mux = + SOC_DAPM_ENUM("SDW1TX3 SRC", cs35l56_sdw1tx3_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum, + CS35L56_SWIRE_DP3_CH4_INPUT, + 0, CS35L56_SWIRETXn_SRC_MASK, + cs35l56_tx_input_texts, + cs35l56_tx_input_values); + +static const struct snd_kcontrol_new sdw1_tx4_mux = + SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum); + +static int cs35l56_play_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + unsigned int val; + int ret; + + dev_dbg(cs35l56->base.dev, "play: %d\n", event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Don't wait for ACK, we check in POST_PMU that it completed */ + return regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + CS35L56_MBOX_CMD_AUDIO_PLAY); + case SND_SOC_DAPM_POST_PMU: + /* Wait for firmware to enter PS0 power state */ + ret = regmap_read_poll_timeout(cs35l56->base.regmap, + CS35L56_TRANSDUCER_ACTUAL_PS, + val, (val == CS35L56_PS0), + CS35L56_PS0_POLL_US, + CS35L56_PS0_TIMEOUT_US); + if (ret) + dev_err(cs35l56->base.dev, "PS0 wait failed: %d\n", ret); + return ret; + case SND_SOC_DAPM_POST_PMD: + return cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE); + default: + return 0; + } +} + +static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = { + SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0), + + SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPK"), + + SND_SOC_DAPM_PGA_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, cs35l56_dsp_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_RX1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_RX2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_TX1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_TX2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_TX3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS35L56_ASP1_ENABLES1, + CS35L56_ASP_TX4_EN_SHIFT, 0), + + SND_SOC_DAPM_MUX("ASP1 TX1 Source", SND_SOC_NOPM, 0, 0, &asp1_tx1_mux), + SND_SOC_DAPM_MUX("ASP1 TX2 Source", SND_SOC_NOPM, 0, 0, &asp1_tx2_mux), + SND_SOC_DAPM_MUX("ASP1 TX3 Source", SND_SOC_NOPM, 0, 0, &asp1_tx3_mux), + SND_SOC_DAPM_MUX("ASP1 TX4 Source", SND_SOC_NOPM, 0, 0, &asp1_tx4_mux), + + SND_SOC_DAPM_MUX("SDW1 TX1 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx1_mux), + SND_SOC_DAPM_MUX("SDW1 TX2 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx2_mux), + SND_SOC_DAPM_MUX("SDW1 TX3 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx3_mux), + SND_SOC_DAPM_MUX("SDW1 TX4 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx4_mux), + + SND_SOC_DAPM_SIGGEN("VMON ADC"), + SND_SOC_DAPM_SIGGEN("IMON ADC"), + SND_SOC_DAPM_SIGGEN("ERRVOL ADC"), + SND_SOC_DAPM_SIGGEN("CLASSH ADC"), + SND_SOC_DAPM_SIGGEN("VDDBMON ADC"), + SND_SOC_DAPM_SIGGEN("VBSTMON ADC"), + SND_SOC_DAPM_SIGGEN("TEMPMON ADC"), +}; + +#define CS35L56_SRC_ROUTE(name) \ + { name" Source", "ASP1RX1", "ASP1RX1" }, \ + { name" Source", "ASP1RX2", "ASP1RX2" }, \ + { name" Source", "VMON", "VMON ADC" }, \ + { name" Source", "IMON", "IMON ADC" }, \ + { name" Source", "ERRVOL", "ERRVOL ADC" }, \ + { name" Source", "CLASSH", "CLASSH ADC" }, \ + { name" Source", "VDDBMON", "VDDBMON ADC" }, \ + { name" Source", "VBSTMON", "VBSTMON ADC" }, \ + { name" Source", "DSP1TX1", "DSP1" }, \ + { name" Source", "DSP1TX2", "DSP1" }, \ + { name" Source", "DSP1TX3", "DSP1" }, \ + { name" Source", "DSP1TX4", "DSP1" }, \ + { name" Source", "DSP1TX5", "DSP1" }, \ + { name" Source", "DSP1TX6", "DSP1" }, \ + { name" Source", "DSP1TX7", "DSP1" }, \ + { name" Source", "DSP1TX8", "DSP1" }, \ + { name" Source", "TEMPMON", "TEMPMON ADC" }, \ + { name" Source", "INTERPOLATOR", "AMP" }, \ + { name" Source", "SDW1RX1", "SDW1 Playback" }, \ + { name" Source", "SDW1RX2", "SDW1 Playback" }, + +static const struct snd_soc_dapm_route cs35l56_audio_map[] = { + { "AMP", NULL, "VDD_B" }, + { "AMP", NULL, "VDD_AMP" }, + + { "ASP1 Playback", NULL, "PLAY" }, + { "SDW1 Playback", NULL, "PLAY" }, + + { "ASP1RX1", NULL, "ASP1 Playback" }, + { "ASP1RX2", NULL, "ASP1 Playback" }, + { "DSP1", NULL, "ASP1RX1" }, + { "DSP1", NULL, "ASP1RX2" }, + { "DSP1", NULL, "SDW1 Playback" }, + { "AMP", NULL, "DSP1" }, + { "SPK", NULL, "AMP" }, + + CS35L56_SRC_ROUTE("ASP1 TX1") + CS35L56_SRC_ROUTE("ASP1 TX2") + CS35L56_SRC_ROUTE("ASP1 TX3") + CS35L56_SRC_ROUTE("ASP1 TX4") + + { "ASP1TX1", NULL, "ASP1 TX1 Source" }, + { "ASP1TX2", NULL, "ASP1 TX2 Source" }, + { "ASP1TX3", NULL, "ASP1 TX3 Source" }, + { "ASP1TX4", NULL, "ASP1 TX4 Source" }, + { "ASP1 Capture", NULL, "ASP1TX1" }, + { "ASP1 Capture", NULL, "ASP1TX2" }, + { "ASP1 Capture", NULL, "ASP1TX3" }, + { "ASP1 Capture", NULL, "ASP1TX4" }, + + CS35L56_SRC_ROUTE("SDW1 TX1") + CS35L56_SRC_ROUTE("SDW1 TX2") + CS35L56_SRC_ROUTE("SDW1 TX3") + CS35L56_SRC_ROUTE("SDW1 TX4") + { "SDW1 Capture", NULL, "SDW1 TX1 Source" }, + { "SDW1 Capture", NULL, "SDW1 TX2 Source" }, + { "SDW1 Capture", NULL, "SDW1 TX3 Source" }, + { "SDW1 Capture", NULL, "SDW1 TX4 Source" }, +}; + +static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + + dev_dbg(cs35l56->base.dev, "%s: %d\n", __func__, event); + + return wm_adsp_event(w, kcontrol, event); +} + +static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component); + unsigned int val; + + dev_dbg(cs35l56->base.dev, "%s: %#x\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + dev_err(cs35l56->base.dev, "Unsupported clock source mode\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + val = CS35L56_ASP_FMT_DSP_A << CS35L56_ASP_FMT_SHIFT; + cs35l56->tdm_mode = true; + break; + case SND_SOC_DAIFMT_I2S: + val = CS35L56_ASP_FMT_I2S << CS35L56_ASP_FMT_SHIFT; + cs35l56->tdm_mode = false; + break; + default: + dev_err(cs35l56->base.dev, "Unsupported DAI format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + val |= CS35L56_ASP_FSYNC_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + val |= CS35L56_ASP_BCLK_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + val |= CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK; + break; + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_err(cs35l56->base.dev, "Invalid clock invert\n"); + return -EINVAL; + } + + regmap_update_bits(cs35l56->base.regmap, + CS35L56_ASP1_CONTROL2, + CS35L56_ASP_FMT_MASK | + CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK, + val); + + /* Hi-Z DOUT in unused slots and when all TX are disabled */ + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL3, + CS35L56_ASP1_DOUT_HIZ_CTRL_MASK, + CS35L56_ASP_UNUSED_HIZ_OFF_HIZ); + + return 0; +} + +static unsigned int cs35l56_make_tdm_config_word(unsigned int reg_val, unsigned long mask) +{ + unsigned int channel_shift; + int bit_num; + + /* Enable consecutive TX1..TXn for each of the slots set in mask */ + channel_shift = 0; + for_each_set_bit(bit_num, &mask, 32) { + reg_val &= ~(0x3f << channel_shift); + reg_val |= bit_num << channel_shift; + channel_shift += 8; + } + + return reg_val; +} + +static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + + if ((slots == 0) || (slot_width == 0)) { + dev_dbg(cs35l56->base.dev, "tdm config cleared\n"); + cs35l56->asp_slot_width = 0; + cs35l56->asp_slot_count = 0; + return 0; + } + + if (slot_width > (CS35L56_ASP_RX_WIDTH_MASK >> CS35L56_ASP_RX_WIDTH_SHIFT)) { + dev_err(cs35l56->base.dev, "tdm invalid slot width %d\n", slot_width); + return -EINVAL; + } + + /* More than 32 slots would give an unsupportable BCLK frequency */ + if (slots > 32) { + dev_err(cs35l56->base.dev, "tdm invalid slot count %d\n", slots); + return -EINVAL; + } + + cs35l56->asp_slot_width = (u8)slot_width; + cs35l56->asp_slot_count = (u8)slots; + + // Note: rx/tx is from point of view of the CPU end + if (tx_mask == 0) + tx_mask = 0x3; // ASPRX1/RX2 in slots 0 and 1 + + if (rx_mask == 0) + rx_mask = 0xf; // ASPTX1..TX4 in slots 0..3 + + /* Default unused slots to 63 */ + regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL1, + cs35l56_make_tdm_config_word(0x3f3f3f3f, rx_mask)); + regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL5, + cs35l56_make_tdm_config_word(0x3f3f3f, tx_mask)); + + dev_dbg(cs35l56->base.dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n", + cs35l56->asp_slot_width, cs35l56->asp_slot_count, tx_mask, rx_mask); + + return 0; +} + +static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + unsigned int rate = params_rate(params); + u8 asp_width, asp_wl; + + asp_wl = params_width(params); + if (cs35l56->asp_slot_width) + asp_width = cs35l56->asp_slot_width; + else + asp_width = asp_wl; + + dev_dbg(cs35l56->base.dev, "%s: wl=%d, width=%d, rate=%d", + __func__, asp_wl, asp_width, rate); + + if (!cs35l56->sysclk_set) { + unsigned int slots = cs35l56->asp_slot_count; + unsigned int bclk_freq; + int freq_id; + + if (slots == 0) { + slots = params_channels(params); + + /* I2S always has an even number of slots */ + if (!cs35l56->tdm_mode) + slots = round_up(slots, 2); + } + + bclk_freq = asp_width * slots * rate; + freq_id = cs35l56_get_bclk_freq_id(bclk_freq); + if (freq_id < 0) { + dev_err(cs35l56->base.dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq); + return -EINVAL; + } + + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL1, + CS35L56_ASP_BCLK_FREQ_MASK, + freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL2, + CS35L56_ASP_RX_WIDTH_MASK, asp_width << + CS35L56_ASP_RX_WIDTH_SHIFT); + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_DATA_CONTROL5, + CS35L56_ASP_RX_WL_MASK, asp_wl); + } else { + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL2, + CS35L56_ASP_TX_WIDTH_MASK, asp_width << + CS35L56_ASP_TX_WIDTH_SHIFT); + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_DATA_CONTROL1, + CS35L56_ASP_TX_WL_MASK, asp_wl); + } + + return 0; +} + +static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + int freq_id; + + if (freq == 0) { + cs35l56->sysclk_set = false; + return 0; + } + + freq_id = cs35l56_get_bclk_freq_id(freq); + if (freq_id < 0) + return freq_id; + + regmap_update_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL1, + CS35L56_ASP_BCLK_FREQ_MASK, + freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT); + cs35l56->sysclk_set = true; + + return 0; +} + +static const struct snd_soc_dai_ops cs35l56_ops = { + .set_fmt = cs35l56_asp_dai_set_fmt, + .set_tdm_slot = cs35l56_asp_dai_set_tdm_slot, + .hw_params = cs35l56_asp_dai_hw_params, + .set_sysclk = cs35l56_asp_dai_set_sysclk, +}; + +static void cs35l56_sdw_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int cs35l56_sdw_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + + /* rx/tx are from point of view of the CPU end so opposite to our rx/tx */ + cs35l56->rx_mask = tx_mask; + cs35l56->tx_mask = rx_mask; + + return 0; +} + +static int cs35l56_sdw_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct sdw_stream_config sconfig; + struct sdw_port_config pconfig; + int ret; + + dev_dbg(cs35l56->base.dev, "%s: rate %d\n", __func__, params_rate(params)); + + if (!cs35l56->base.init_done) + return -ENODEV; + + if (!sdw_stream) + return -EINVAL; + + memset(&sconfig, 0, sizeof(sconfig)); + memset(&pconfig, 0, sizeof(pconfig)); + + sconfig.frame_rate = params_rate(params); + sconfig.bps = snd_pcm_format_width(params_format(params)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + sconfig.direction = SDW_DATA_DIR_RX; + pconfig.num = CS35L56_SDW1_PLAYBACK_PORT; + pconfig.ch_mask = cs35l56->rx_mask; + } else { + sconfig.direction = SDW_DATA_DIR_TX; + pconfig.num = CS35L56_SDW1_CAPTURE_PORT; + pconfig.ch_mask = cs35l56->tx_mask; + } + + if (pconfig.ch_mask == 0) { + sconfig.ch_count = params_channels(params); + pconfig.ch_mask = GENMASK(sconfig.ch_count - 1, 0); + } else { + sconfig.ch_count = hweight32(pconfig.ch_mask); + } + + ret = sdw_stream_add_slave(cs35l56->sdw_peripheral, &sconfig, &pconfig, + 1, sdw_stream); + if (ret) { + dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret); + return ret; + } + + return 0; +} + +static int cs35l56_sdw_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!cs35l56->sdw_peripheral) + return -EINVAL; + + sdw_stream_remove_slave(cs35l56->sdw_peripheral, sdw_stream); + + return 0; +} + +static int cs35l56_sdw_dai_set_stream(struct snd_soc_dai *dai, + void *sdw_stream, int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static const struct snd_soc_dai_ops cs35l56_sdw_dai_ops = { + .set_tdm_slot = cs35l56_sdw_dai_set_tdm_slot, + .shutdown = cs35l56_sdw_dai_shutdown, + .hw_params = cs35l56_sdw_dai_hw_params, + .hw_free = cs35l56_sdw_dai_hw_free, + .set_stream = cs35l56_sdw_dai_set_stream, +}; + +static struct snd_soc_dai_driver cs35l56_dai[] = { + { + .name = "cs35l56-asp1", + .id = 0, + .playback = { + .stream_name = "ASP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS35L56_RATES, + .formats = CS35L56_RX_FORMATS, + }, + .capture = { + .stream_name = "ASP1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = CS35L56_RATES, + .formats = CS35L56_TX_FORMATS, + }, + .ops = &cs35l56_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs35l56-sdw1", + .id = 1, + .playback = { + .stream_name = "SDW1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS35L56_RATES, + .formats = CS35L56_RX_FORMATS, + }, + .capture = { + .stream_name = "SDW1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = CS35L56_RATES, + .formats = CS35L56_TX_FORMATS, + }, + .symmetric_rate = 1, + .ops = &cs35l56_sdw_dai_ops, + } +}; + +static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) +{ + int ret; + + /* Use wm_adsp to load and apply the firmware patch and coefficient files */ + ret = wm_adsp_power_up(&cs35l56->dsp, true); + if (ret) + dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); + else + cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT); +} + +static void cs35l56_patch(struct cs35l56_private *cs35l56) +{ + unsigned int firmware_missing; + int ret; + + ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing); + if (ret) { + dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret); + return; + } + + firmware_missing &= CS35L56_FIRMWARE_MISSING; + + /* + * Disable SoundWire interrupts to prevent race with IRQ work. + * Setting sdw_irq_no_unmask prevents the handler re-enabling + * the SoundWire interrupt. + */ + if (cs35l56->sdw_peripheral) { + cs35l56->sdw_irq_no_unmask = true; + flush_work(&cs35l56->sdw_irq_work); + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + flush_work(&cs35l56->sdw_irq_work); + } + + ret = cs35l56_firmware_shutdown(&cs35l56->base); + if (ret) + goto err; + + /* + * Use wm_adsp to load and apply the firmware patch and coefficient files, + * but only if firmware is missing. If firmware is already patched just + * power-up wm_adsp without downloading firmware. + */ + ret = wm_adsp_power_up(&cs35l56->dsp, !!firmware_missing); + if (ret) { + dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); + goto err; + } + + mutex_lock(&cs35l56->base.irq_lock); + + reinit_completion(&cs35l56->init_completion); + + cs35l56->soft_resetting = true; + cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral); + + if (cs35l56->sdw_peripheral) { + /* + * The system-reset causes the CS35L56 to detach from the bus. + * Wait for the manager to re-enumerate the CS35L56 and + * cs35l56_init() to run again. + */ + if (!wait_for_completion_timeout(&cs35l56->init_completion, + msecs_to_jiffies(5000))) { + dev_err(cs35l56->base.dev, "%s: init_completion timed out (SDW)\n", + __func__); + goto err_unlock; + } + } else if (cs35l56_init(cs35l56)) { + goto err_unlock; + } + + regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, + CS35L56_FIRMWARE_MISSING); + cs35l56->base.fw_patched = true; + +err_unlock: + mutex_unlock(&cs35l56->base.irq_lock); +err: + /* Re-enable SoundWire interrupts */ + if (cs35l56->sdw_peripheral) { + cs35l56->sdw_irq_no_unmask = false; + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, + CS35L56_SDW_INT_MASK_CODEC_IRQ); + } +} + +static void cs35l56_dsp_work(struct work_struct *work) +{ + struct cs35l56_private *cs35l56 = container_of(work, + struct cs35l56_private, + dsp_work); + + if (!cs35l56->base.init_done) + return; + + pm_runtime_get_sync(cs35l56->base.dev); + + /* + * When the device is running in secure mode the firmware files can + * only contain insecure tunings and therefore we do not need to + * shutdown the firmware to apply them and can use the lower cost + * reinit sequence instead. + */ + if (cs35l56->base.secured) + cs35l56_secure_patch(cs35l56); + else + cs35l56_patch(cs35l56); + + pm_runtime_mark_last_busy(cs35l56->base.dev); + pm_runtime_put_autosuspend(cs35l56->base.dev); +} + +static int cs35l56_component_probe(struct snd_soc_component *component) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + struct dentry *debugfs_root = component->debugfs_root; + unsigned short vendor, device; + + BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values)); + + if (!cs35l56->dsp.system_name && + (snd_soc_card_get_pci_ssid(component->card, &vendor, &device) == 0)) { + cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev, + GFP_KERNEL, + "%04x%04x", + vendor, device); + if (!cs35l56->dsp.system_name) + return -ENOMEM; + } + + if (!wait_for_completion_timeout(&cs35l56->init_completion, + msecs_to_jiffies(5000))) { + dev_err(cs35l56->base.dev, "%s: init_completion timed out\n", __func__); + return -ENODEV; + } + + cs35l56->component = component; + wm_adsp2_component_probe(&cs35l56->dsp, component); + + debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->base.init_done); + debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate); + debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched); + + queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); + + return 0; +} + +static void cs35l56_component_remove(struct snd_soc_component *component) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + + cancel_work_sync(&cs35l56->dsp_work); +} + +static int cs35l56_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_STANDBY: + /* + * Wait for patching to complete when transitioning from + * BIAS_OFF to BIAS_STANDBY + */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + cs35l56_wait_dsp_ready(cs35l56); + + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_cs35l56 = { + .probe = cs35l56_component_probe, + .remove = cs35l56_component_remove, + + .dapm_widgets = cs35l56_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l56_dapm_widgets), + .dapm_routes = cs35l56_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l56_audio_map), + .controls = cs35l56_controls, + .num_controls = ARRAY_SIZE(cs35l56_controls), + + .set_bias_level = cs35l56_set_bias_level, + + .suspend_bias_off = 1, /* see cs35l56_system_resume() */ +}; + +static int __maybe_unused cs35l56_runtime_suspend_i2c_spi(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + return cs35l56_runtime_suspend_common(&cs35l56->base); +} + +static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + return cs35l56_runtime_resume_common(&cs35l56->base, false); +} + +int cs35l56_system_suspend(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + dev_dbg(dev, "system_suspend\n"); + + if (cs35l56->component) + flush_work(&cs35l56->dsp_work); + + /* + * The interrupt line is normally shared, but after we start suspending + * we can't check if our device is the source of an interrupt, and can't + * clear it. Prevent this race by temporarily disabling the parent irq + * until we reach _no_irq. + */ + if (cs35l56->base.irq) + disable_irq(cs35l56->base.irq); + + return pm_runtime_force_suspend(dev); +} +EXPORT_SYMBOL_GPL(cs35l56_system_suspend); + +int cs35l56_system_suspend_late(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + dev_dbg(dev, "system_suspend_late\n"); + + /* + * Assert RESET before removing supplies. + * RESET is usually shared by all amps so it must not be asserted until + * all driver instances have done their suspend() stage. + */ + if (cs35l56->base.reset_gpio) { + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); + cs35l56_wait_min_reset_pulse(); + } + + regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l56_system_suspend_late); + +int cs35l56_system_suspend_no_irq(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + dev_dbg(dev, "system_suspend_no_irq\n"); + + /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */ + if (cs35l56->base.irq) + enable_irq(cs35l56->base.irq); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l56_system_suspend_no_irq); + +int cs35l56_system_resume_no_irq(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + dev_dbg(dev, "system_resume_no_irq\n"); + + /* + * WAKE interrupts unmask if the CS35L56 hibernates, which can cause + * spurious interrupts, and the interrupt line is normally shared. + * We can't check if our device is the source of an interrupt, and can't + * clear it, until it has fully resumed. Prevent this race by temporarily + * disabling the parent irq until we complete resume(). + */ + if (cs35l56->base.irq) + disable_irq(cs35l56->base.irq); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l56_system_resume_no_irq); + +int cs35l56_system_resume_early(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "system_resume_early\n"); + + /* Ensure a spec-compliant RESET pulse. */ + if (cs35l56->base.reset_gpio) { + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); + cs35l56_wait_min_reset_pulse(); + } + + /* Enable supplies before releasing RESET. */ + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); + if (ret) { + dev_err(dev, "system_resume_early failed to enable supplies: %d\n", ret); + return ret; + } + + /* Release shared RESET before drivers start resume(). */ + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l56_system_resume_early); + +int cs35l56_system_resume(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "system_resume\n"); + + /* + * We might have done a hard reset or the CS35L56 was power-cycled + * so wait for control port to be ready. + */ + cs35l56_wait_control_port_ready(); + + /* Undo pm_runtime_force_suspend() before re-enabling the irq */ + ret = pm_runtime_force_resume(dev); + if (cs35l56->base.irq) + enable_irq(cs35l56->base.irq); + + if (ret) + return ret; + + /* Firmware won't have been loaded if the component hasn't probed */ + if (!cs35l56->component) + return 0; + + ret = cs35l56_is_fw_reload_needed(&cs35l56->base); + dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret); + if (ret < 1) + return ret; + + cs35l56->base.fw_patched = false; + wm_adsp_power_down(&cs35l56->dsp); + queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); + + /* + * suspend_bias_off ensures we are now in BIAS_OFF so there will be + * a BIAS_OFF->BIAS_STANDBY transition to complete dsp patching. + */ + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l56_system_resume); + +static int cs35l56_dsp_init(struct cs35l56_private *cs35l56) +{ + struct wm_adsp *dsp; + int ret; + + cs35l56->dsp_wq = create_singlethread_workqueue("cs35l56-dsp"); + if (!cs35l56->dsp_wq) + return -ENOMEM; + + INIT_WORK(&cs35l56->dsp_work, cs35l56_dsp_work); + + dsp = &cs35l56->dsp; + cs35l56_init_cs_dsp(&cs35l56->base, &dsp->cs_dsp); + dsp->part = "cs35l56"; + dsp->fw = 12; + dsp->wmfw_optional = true; + + dev_dbg(cs35l56->base.dev, "DSP system name: '%s'\n", dsp->system_name); + + ret = wm_halo_init(dsp); + if (ret != 0) { + dev_err(cs35l56->base.dev, "wm_halo_init failed\n"); + return ret; + } + + return 0; +} + +static int cs35l56_get_firmware_uid(struct cs35l56_private *cs35l56) +{ + struct device *dev = cs35l56->base.dev; + const char *prop; + int ret; + + ret = device_property_read_string(dev, "cirrus,firmware-uid", &prop); + /* If bad sw node property, return 0 and fallback to legacy firmware path */ + if (ret < 0) + return 0; + + cs35l56->dsp.system_name = devm_kstrdup(dev, prop, GFP_KERNEL); + if (cs35l56->dsp.system_name == NULL) + return -ENOMEM; + + dev_dbg(dev, "Firmware UID: %s\n", cs35l56->dsp.system_name); + + return 0; +} + +int cs35l56_common_probe(struct cs35l56_private *cs35l56) +{ + int ret; + + init_completion(&cs35l56->init_completion); + mutex_init(&cs35l56->base.irq_lock); + + dev_set_drvdata(cs35l56->base.dev, cs35l56); + + cs35l56_fill_supply_names(cs35l56->supplies); + ret = devm_regulator_bulk_get(cs35l56->base.dev, ARRAY_SIZE(cs35l56->supplies), + cs35l56->supplies); + if (ret != 0) + return dev_err_probe(cs35l56->base.dev, ret, "Failed to request supplies\n"); + + /* Reset could be controlled by the BIOS or shared by multiple amps */ + cs35l56->base.reset_gpio = devm_gpiod_get_optional(cs35l56->base.dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs35l56->base.reset_gpio)) { + ret = PTR_ERR(cs35l56->base.reset_gpio); + /* + * If RESET is shared the first amp to probe will grab the reset + * line and reset all the amps + */ + if (ret != -EBUSY) + return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n"); + + dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n"); + cs35l56->base.reset_gpio = NULL; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); + if (ret != 0) + return dev_err_probe(cs35l56->base.dev, ret, "Failed to enable supplies\n"); + + if (cs35l56->base.reset_gpio) { + /* ACPI can override GPIOD_OUT_LOW flag so force it to start low */ + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); + cs35l56_wait_min_reset_pulse(); + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1); + } + + ret = cs35l56_get_firmware_uid(cs35l56); + if (ret != 0) + goto err; + + ret = cs35l56_dsp_init(cs35l56); + if (ret < 0) { + dev_err_probe(cs35l56->base.dev, ret, "DSP init failed\n"); + goto err; + } + + ret = devm_snd_soc_register_component(cs35l56->base.dev, + &soc_component_dev_cs35l56, + cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); + if (ret < 0) { + dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); + goto err; + } + + return 0; + +err: + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, SND_SOC_CS35L56_CORE); + +int cs35l56_init(struct cs35l56_private *cs35l56) +{ + int ret; + + /* + * Check whether the actions associated with soft reset or one time + * init need to be performed. + */ + if (cs35l56->soft_resetting) + goto post_soft_reset; + + if (cs35l56->base.init_done) + return 0; + + pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 100); + pm_runtime_use_autosuspend(cs35l56->base.dev); + pm_runtime_set_active(cs35l56->base.dev); + pm_runtime_enable(cs35l56->base.dev); + + ret = cs35l56_hw_init(&cs35l56->base); + if (ret < 0) + return ret; + + /* Populate the DSP information with the revision and security state */ + cs35l56->dsp.part = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "cs35l56%s-%02x", + cs35l56->base.secured ? "s" : "", cs35l56->base.rev); + if (!cs35l56->dsp.part) + return -ENOMEM; + + if (!cs35l56->base.reset_gpio) { + dev_dbg(cs35l56->base.dev, "No reset gpio: using soft reset\n"); + cs35l56->soft_resetting = true; + cs35l56_system_reset(&cs35l56->base, !!cs35l56->sdw_peripheral); + if (cs35l56->sdw_peripheral) { + /* Keep alive while we wait for re-enumeration */ + pm_runtime_get_noresume(cs35l56->base.dev); + return 0; + } + } + +post_soft_reset: + if (cs35l56->soft_resetting) { + cs35l56->soft_resetting = false; + + /* Done re-enumerating after one-time init so release the keep-alive */ + if (cs35l56->sdw_peripheral && !cs35l56->base.init_done) + pm_runtime_put_noidle(cs35l56->base.dev); + + regcache_mark_dirty(cs35l56->base.regmap); + ret = cs35l56_wait_for_firmware_boot(&cs35l56->base); + if (ret) + return ret; + + dev_dbg(cs35l56->base.dev, "Firmware rebooted after soft reset\n"); + } + + /* Disable auto-hibernate so that runtime_pm has control */ + ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); + if (ret) + return ret; + + ret = cs35l56_set_patch(&cs35l56->base); + if (ret) + return ret; + + /* Registers could be dirty after soft reset or SoundWire enumeration */ + regcache_sync(cs35l56->base.regmap); + + /* Set ASP1 DOUT to high-impedance when it is not transmitting audio data. */ + ret = regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_CONTROL3, + CS35L56_ASP1_DOUT_HIZ_CTRL_MASK); + if (ret) + return dev_err_probe(cs35l56->base.dev, ret, "Failed to write ASP1_CONTROL3\n"); + + cs35l56->base.init_done = true; + complete(&cs35l56->init_completion); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs35l56_init, SND_SOC_CS35L56_CORE); + +void cs35l56_remove(struct cs35l56_private *cs35l56) +{ + cs35l56->base.init_done = false; + + /* + * WAKE IRQs unmask if CS35L56 hibernates so free the handler to + * prevent it racing with remove(). + */ + if (cs35l56->base.irq) + devm_free_irq(cs35l56->base.dev, cs35l56->base.irq, &cs35l56->base); + + flush_workqueue(cs35l56->dsp_wq); + destroy_workqueue(cs35l56->dsp_wq); + + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); + pm_runtime_suspend(cs35l56->base.dev); + pm_runtime_disable(cs35l56->base.dev); + + regcache_cache_only(cs35l56->base.regmap, true); + + gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_remove, SND_SOC_CS35L56_CORE); + +const struct dev_pm_ops cs35l56_pm_ops_i2c_spi = { + SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend_i2c_spi, cs35l56_runtime_resume_i2c_spi, NULL) + SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend, cs35l56_system_resume) + LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) + NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_no_irq, cs35l56_system_resume_no_irq) +}; +EXPORT_SYMBOL_NS_GPL(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE); + +MODULE_DESCRIPTION("ASoC CS35L56 driver"); +MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Simon Trimmer "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h new file mode 100644 index 0000000000..8159c3e217 --- /dev/null +++ b/sound/soc/codecs/cs35l56.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Driver for Cirrus Logic CS35L56 smart amp + * + * Copyright (C) 2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS35L56_H +#define CS35L56_H + +#include +#include +#include +#include +#include +#include "wm_adsp.h" + +#define CS35L56_SDW_GEN_INT_STAT_1 0xc0 +#define CS35L56_SDW_GEN_INT_MASK_1 0xc1 +#define CS35L56_SDW_INT_MASK_CODEC_IRQ BIT(0) + +#define CS35L56_SDW_INVALID_BUS_SCALE 0xf + +#define CS35L56_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#define CS35L56_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +#define CS35L56_RATES (SNDRV_PCM_RATE_48000) + +struct sdw_slave; + +struct cs35l56_private { + struct wm_adsp dsp; /* must be first member */ + struct cs35l56_base base; + struct work_struct dsp_work; + struct workqueue_struct *dsp_wq; + struct snd_soc_component *component; + struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES]; + struct sdw_slave *sdw_peripheral; + struct work_struct sdw_irq_work; + bool sdw_irq_no_unmask; + bool soft_resetting; + bool sdw_attached; + struct completion init_completion; + + u32 rx_mask; + u32 tx_mask; + u8 asp_slot_width; + u8 asp_slot_count; + bool tdm_mode; + bool sysclk_set; + u8 old_sdw_clock_scale; +}; + +extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; + +int cs35l56_system_suspend(struct device *dev); +int cs35l56_system_suspend_late(struct device *dev); +int cs35l56_system_suspend_no_irq(struct device *dev); +int cs35l56_system_resume_no_irq(struct device *dev); +int cs35l56_system_resume_early(struct device *dev); +int cs35l56_system_resume(struct device *dev); +irqreturn_t cs35l56_irq(int irq, void *data); +int cs35l56_irq_request(struct cs35l56_base *cs35l56_base, int irq); +int cs35l56_common_probe(struct cs35l56_private *cs35l56); +int cs35l56_init(struct cs35l56_private *cs35l56); +void cs35l56_remove(struct cs35l56_private *cs35l56); + +#endif /* ifndef CS35L56_H */ diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c new file mode 100644 index 0000000000..69287ba7e9 --- /dev/null +++ b/sound/soc/codecs/cs4234.c @@ -0,0 +1,916 @@ +// SPDX-License-Identifier: GPL-2.0-only +// cs4234.c -- ALSA SoC CS4234 driver +// +// Copyright (C) 2020 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs4234.h" + +struct cs4234 { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data core_supplies[2]; + int num_core_supplies; + struct completion vq_ramp_complete; + struct delayed_work vq_ramp_delay; + struct clk *mclk; + unsigned long mclk_rate; + unsigned long lrclk_rate; + unsigned int format; + struct snd_ratnum rate_dividers[2]; + struct snd_pcm_hw_constraint_ratnums rate_constraint; +}; + +/* -89.92dB to +6.02dB with step of 0.38dB */ +static const DECLARE_TLV_DB_SCALE(dac_tlv, -8992, 38, 0); + +static const char * const cs4234_dac14_delay_text[] = { + "0us", "100us", "150us", "200us", "225us", "250us", "275us", "300us", + "325us", "350us", "375us", "400us", "425us", "450us", "475us", "500us", +}; +static SOC_ENUM_SINGLE_DECL(cs4234_dac14_group_delay, CS4234_TPS_CTRL, + CS4234_GRP_DELAY_SHIFT, cs4234_dac14_delay_text); + +static const char * const cs4234_noise_gate_text[] = { + "72dB", "78dB", "84dB", "90dB", "96dB", "102dB", "138dB", "Disabled", +}; +static SOC_ENUM_SINGLE_DECL(cs4234_ll_noise_gate, CS4234_LOW_LAT_CTRL1, + CS4234_LL_NG_SHIFT, cs4234_noise_gate_text); +static SOC_ENUM_SINGLE_DECL(cs4234_dac14_noise_gate, CS4234_DAC_CTRL1, + CS4234_DAC14_NG_SHIFT, cs4234_noise_gate_text); +static SOC_ENUM_SINGLE_DECL(cs4234_dac5_noise_gate, CS4234_DAC_CTRL2, + CS4234_DAC5_NG_SHIFT, cs4234_noise_gate_text); + +static const char * const cs4234_dac5_config_fltr_sel_text[] = { + "Interpolation Filter", "Sample and Hold" +}; +static SOC_ENUM_SINGLE_DECL(cs4234_dac5_config_fltr_sel, CS4234_DAC_CTRL1, + CS4234_DAC5_CFG_FLTR_SHIFT, + cs4234_dac5_config_fltr_sel_text); + +static const char * const cs4234_mute_delay_text[] = { + "1x", "4x", "16x", "64x", +}; +static SOC_ENUM_SINGLE_DECL(cs4234_mute_delay, CS4234_VOLUME_MODE, + CS4234_MUTE_DELAY_SHIFT, cs4234_mute_delay_text); + +static const char * const cs4234_minmax_delay_text[] = { + "1x", "2x", "4x", "8x", "16x", "32x", "64x", "128x", +}; +static SOC_ENUM_SINGLE_DECL(cs4234_min_delay, CS4234_VOLUME_MODE, + CS4234_MIN_DELAY_SHIFT, cs4234_minmax_delay_text); +static SOC_ENUM_SINGLE_DECL(cs4234_max_delay, CS4234_VOLUME_MODE, + CS4234_MAX_DELAY_SHIFT, cs4234_minmax_delay_text); + +static int cs4234_dac14_grp_delay_put(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *uctrl) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kctrl); + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + unsigned int val = 0; + int ret = 0; + + snd_soc_dapm_mutex_lock(dapm); + + regmap_read(cs4234->regmap, CS4234_ADC_CTRL2, &val); + if ((val & 0x0F) != 0x0F) { // are all the ADCs powerdown + ret = -EBUSY; + dev_err(component->dev, "Can't change group delay while ADC are ON\n"); + goto exit; + } + + regmap_read(cs4234->regmap, CS4234_DAC_CTRL4, &val); + if ((val & 0x1F) != 0x1F) { // are all the DACs powerdown + ret = -EBUSY; + dev_err(component->dev, "Can't change group delay while DAC are ON\n"); + goto exit; + } + + ret = snd_soc_put_enum_double(kctrl, uctrl); +exit: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static void cs4234_vq_ramp_done(struct work_struct *work) +{ + struct delayed_work *dw = to_delayed_work(work); + struct cs4234 *cs4234 = container_of(dw, struct cs4234, vq_ramp_delay); + + complete_all(&cs4234->vq_ramp_complete); +} + +static int cs4234_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + switch (snd_soc_component_get_bias_level(component)) { + case SND_SOC_BIAS_STANDBY: + wait_for_completion(&cs4234->vq_ramp_complete); + break; + default: + break; + } + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget cs4234_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("SDRX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDRX2", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDRX3", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDRX4", NULL, 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDRX5", NULL, 4, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DAC1", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC1_SHIFT, 1), + SND_SOC_DAPM_DAC("DAC2", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC2_SHIFT, 1), + SND_SOC_DAPM_DAC("DAC3", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC3_SHIFT, 1), + SND_SOC_DAPM_DAC("DAC4", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC4_SHIFT, 1), + SND_SOC_DAPM_DAC("DAC5", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC5_SHIFT, 1), + + SND_SOC_DAPM_OUTPUT("AOUT1"), + SND_SOC_DAPM_OUTPUT("AOUT2"), + SND_SOC_DAPM_OUTPUT("AOUT3"), + SND_SOC_DAPM_OUTPUT("AOUT4"), + SND_SOC_DAPM_OUTPUT("AOUT5"), + + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("AIN4"), + + SND_SOC_DAPM_ADC("ADC1", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC1_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC2", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC2_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC3", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC3_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC4", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC4_SHIFT, 1), + + SND_SOC_DAPM_AIF_OUT("SDTX1", NULL, 0, SND_SOC_NOPM, 0, 1), + SND_SOC_DAPM_AIF_OUT("SDTX2", NULL, 1, SND_SOC_NOPM, 0, 1), + SND_SOC_DAPM_AIF_OUT("SDTX3", NULL, 2, SND_SOC_NOPM, 0, 1), + SND_SOC_DAPM_AIF_OUT("SDTX4", NULL, 3, SND_SOC_NOPM, 0, 1), +}; + +static const struct snd_soc_dapm_route cs4234_dapm_routes[] = { + /* Playback */ + { "AOUT1", NULL, "DAC1" }, + { "AOUT2", NULL, "DAC2" }, + { "AOUT3", NULL, "DAC3" }, + { "AOUT4", NULL, "DAC4" }, + { "AOUT5", NULL, "DAC5" }, + + { "DAC1", NULL, "SDRX1" }, + { "DAC2", NULL, "SDRX2" }, + { "DAC3", NULL, "SDRX3" }, + { "DAC4", NULL, "SDRX4" }, + { "DAC5", NULL, "SDRX5" }, + + { "SDRX1", NULL, "Playback" }, + { "SDRX2", NULL, "Playback" }, + { "SDRX3", NULL, "Playback" }, + { "SDRX4", NULL, "Playback" }, + { "SDRX5", NULL, "Playback" }, + + /* Capture */ + { "ADC1", NULL, "AIN1" }, + { "ADC2", NULL, "AIN2" }, + { "ADC3", NULL, "AIN3" }, + { "ADC4", NULL, "AIN4" }, + + { "SDTX1", NULL, "ADC1" }, + { "SDTX2", NULL, "ADC2" }, + { "SDTX3", NULL, "ADC3" }, + { "SDTX4", NULL, "ADC4" }, + + { "Capture", NULL, "SDTX1" }, + { "Capture", NULL, "SDTX2" }, + { "Capture", NULL, "SDTX3" }, + { "Capture", NULL, "SDTX4" }, +}; + +static const struct snd_kcontrol_new cs4234_snd_controls[] = { + SOC_SINGLE_TLV("Master Volume", CS4234_MASTER_VOL, 0, 0xff, 1, dac_tlv), + SOC_SINGLE_TLV("DAC1 Volume", CS4234_DAC1_VOL, 0, 0xff, 1, dac_tlv), + SOC_SINGLE_TLV("DAC2 Volume", CS4234_DAC2_VOL, 0, 0xff, 1, dac_tlv), + SOC_SINGLE_TLV("DAC3 Volume", CS4234_DAC3_VOL, 0, 0xff, 1, dac_tlv), + SOC_SINGLE_TLV("DAC4 Volume", CS4234_DAC4_VOL, 0, 0xff, 1, dac_tlv), + SOC_SINGLE_TLV("DAC5 Volume", CS4234_DAC5_VOL, 0, 0xff, 1, dac_tlv), + + SOC_SINGLE("DAC5 Soft Ramp Switch", CS4234_DAC_CTRL3, CS4234_DAC5_ATT_SHIFT, 1, 1), + SOC_SINGLE("DAC1-4 Soft Ramp Switch", CS4234_DAC_CTRL3, CS4234_DAC14_ATT_SHIFT, 1, 1), + + SOC_SINGLE("ADC HPF Switch", CS4234_ADC_CTRL1, CS4234_ENA_HPF_SHIFT, 1, 0), + + SOC_ENUM_EXT("DAC1-4 Group Delay", cs4234_dac14_group_delay, + snd_soc_get_enum_double, cs4234_dac14_grp_delay_put), + + SOC_SINGLE("ADC1 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC1_SHIFT, 1, 0), + SOC_SINGLE("ADC2 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC2_SHIFT, 1, 0), + SOC_SINGLE("ADC3 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC3_SHIFT, 1, 0), + SOC_SINGLE("ADC4 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC4_SHIFT, 1, 0), + + SOC_SINGLE("DAC1 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC1_SHIFT, 1, 0), + SOC_SINGLE("DAC2 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC2_SHIFT, 1, 0), + SOC_SINGLE("DAC3 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC3_SHIFT, 1, 0), + SOC_SINGLE("DAC4 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC4_SHIFT, 1, 0), + SOC_SINGLE("DAC5 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC5_SHIFT, 1, 0), + + SOC_SINGLE("ADC1 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC1_SHIFT, 1, 1), + SOC_SINGLE("ADC2 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC2_SHIFT, 1, 1), + SOC_SINGLE("ADC3 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC3_SHIFT, 1, 1), + SOC_SINGLE("ADC4 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC4_SHIFT, 1, 1), + + SOC_SINGLE("DAC1 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC1_SHIFT, 1, 1), + SOC_SINGLE("DAC2 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC2_SHIFT, 1, 1), + SOC_SINGLE("DAC3 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC3_SHIFT, 1, 1), + SOC_SINGLE("DAC4 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC4_SHIFT, 1, 1), + SOC_SINGLE("DAC5 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC5_SHIFT, 1, 1), + SOC_SINGLE("Low-latency Switch", CS4234_DAC_CTRL3, CS4234_MUTE_LL_SHIFT, 1, 1), + + SOC_SINGLE("DAC1 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1, + CS4234_INV_LL1_SHIFT, 1, 0), + SOC_SINGLE("DAC2 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1, + CS4234_INV_LL2_SHIFT, 1, 0), + SOC_SINGLE("DAC3 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1, + CS4234_INV_LL3_SHIFT, 1, 0), + SOC_SINGLE("DAC4 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1, + CS4234_INV_LL4_SHIFT, 1, 0), + + SOC_ENUM("Low-latency Noise Gate", cs4234_ll_noise_gate), + SOC_ENUM("DAC1-4 Noise Gate", cs4234_dac14_noise_gate), + SOC_ENUM("DAC5 Noise Gate", cs4234_dac5_noise_gate), + + SOC_SINGLE("DAC1-4 De-emphasis Switch", CS4234_DAC_CTRL1, + CS4234_DAC14_DE_SHIFT, 1, 0), + SOC_SINGLE("DAC5 De-emphasis Switch", CS4234_DAC_CTRL1, + CS4234_DAC5_DE_SHIFT, 1, 0), + + SOC_SINGLE("DAC5 Master Controlled Switch", CS4234_DAC_CTRL1, + CS4234_DAC5_MVC_SHIFT, 1, 0), + + SOC_ENUM("DAC5 Filter", cs4234_dac5_config_fltr_sel), + + SOC_ENUM("Mute Delay", cs4234_mute_delay), + SOC_ENUM("Ramp Minimum Delay", cs4234_min_delay), + SOC_ENUM("Ramp Maximum Delay", cs4234_max_delay), + +}; + +static int cs4234_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component); + unsigned int sp_ctrl = 0; + + cs4234->format = format & SND_SOC_DAIFMT_FORMAT_MASK; + switch (cs4234->format) { + case SND_SOC_DAIFMT_LEFT_J: + sp_ctrl |= CS4234_LEFT_J << CS4234_SP_FORMAT_SHIFT; + break; + case SND_SOC_DAIFMT_I2S: + sp_ctrl |= CS4234_I2S << CS4234_SP_FORMAT_SHIFT; + break; + case SND_SOC_DAIFMT_DSP_A: /* TDM mode in datasheet */ + sp_ctrl |= CS4234_TDM << CS4234_SP_FORMAT_SHIFT; + break; + default: + dev_err(component->dev, "Unsupported dai format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + if (cs4234->format == SND_SOC_DAIFMT_DSP_A) { + dev_err(component->dev, "Unsupported DSP A format in master mode\n"); + return -EINVAL; + } + sp_ctrl |= CS4234_MST_SLV_MASK; + break; + default: + dev_err(component->dev, "Unsupported master/slave mode\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + sp_ctrl |= CS4234_INVT_SCLK_MASK; + break; + default: + dev_err(component->dev, "Unsupported inverted clock setting\n"); + return -EINVAL; + } + + regmap_update_bits(cs4234->regmap, CS4234_SP_CTRL, + CS4234_SP_FORMAT_MASK | CS4234_MST_SLV_MASK | CS4234_INVT_SCLK_MASK, + sp_ctrl); + + return 0; +} + +static int cs4234_dai_hw_params(struct snd_pcm_substream *sub, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component); + unsigned int mclk_mult, double_speed = 0; + int ret = 0, rate_ad, sample_width; + + cs4234->lrclk_rate = params_rate(params); + mclk_mult = cs4234->mclk_rate / cs4234->lrclk_rate; + + if (cs4234->lrclk_rate > 48000) { + double_speed = 1; + mclk_mult *= 2; + } + + switch (mclk_mult) { + case 256: + case 384: + case 512: + regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP, + CS4234_SPEED_MODE_MASK, + double_speed << CS4234_SPEED_MODE_SHIFT); + regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP, + CS4234_MCLK_RATE_MASK, + ((mclk_mult / 128) - 2) << CS4234_MCLK_RATE_SHIFT); + break; + default: + dev_err(component->dev, "Unsupported mclk/lrclk rate\n"); + return -EINVAL; + } + + switch (cs4234->lrclk_rate) { + case 48000: + case 96000: + rate_ad = CS4234_48K; + break; + case 44100: + case 88200: + rate_ad = CS4234_44K1; + break; + case 32000: + case 64000: + rate_ad = CS4234_32K; + break; + default: + dev_err(component->dev, "Unsupported LR clock\n"); + return -EINVAL; + } + regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP, CS4234_BASE_RATE_MASK, + rate_ad << CS4234_BASE_RATE_SHIFT); + + sample_width = params_width(params); + switch (sample_width) { + case 16: + sample_width = 0; + break; + case 18: + sample_width = 1; + break; + case 20: + sample_width = 2; + break; + case 24: + sample_width = 3; + break; + default: + dev_err(component->dev, "Unsupported sample width\n"); + return -EINVAL; + } + if (sub->stream == SNDRV_PCM_STREAM_CAPTURE) + regmap_update_bits(cs4234->regmap, CS4234_SAMPLE_WIDTH, + CS4234_SDOUTX_SW_MASK, + sample_width << CS4234_SDOUTX_SW_SHIFT); + else + regmap_update_bits(cs4234->regmap, CS4234_SAMPLE_WIDTH, + CS4234_INPUT_SW_MASK | CS4234_LOW_LAT_SW_MASK | CS4234_DAC5_SW_MASK, + sample_width << CS4234_INPUT_SW_SHIFT | + sample_width << CS4234_LOW_LAT_SW_SHIFT | + sample_width << CS4234_DAC5_SW_SHIFT); + + return ret; +} + +/* Scale MCLK rate by 64 to avoid overflow in the ratnum calculation */ +#define CS4234_MCLK_SCALE 64 + +static const struct snd_ratnum cs4234_dividers[] = { + { + .num = 0, + .den_min = 256 / CS4234_MCLK_SCALE, + .den_max = 512 / CS4234_MCLK_SCALE, + .den_step = 128 / CS4234_MCLK_SCALE, + }, + { + .num = 0, + .den_min = 128 / CS4234_MCLK_SCALE, + .den_max = 192 / CS4234_MCLK_SCALE, + .den_step = 64 / CS4234_MCLK_SCALE, + }, +}; + +static int cs4234_dai_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) +{ + struct cs4234 *cs4234 = rule->private; + int mclk = cs4234->mclk_rate; + struct snd_interval ranges[] = { + { /* Single Speed Mode */ + .min = mclk / clamp(mclk / 30000, 256, 512), + .max = mclk / clamp(mclk / 50000, 256, 512), + }, + { /* Double Speed Mode */ + .min = mclk / clamp(mclk / 60000, 128, 256), + .max = mclk / clamp(mclk / 100000, 128, 256), + }, + }; + + return snd_interval_ranges(hw_param_interval(params, rule->var), + ARRAY_SIZE(ranges), ranges, 0); +} + +static int cs4234_dai_startup(struct snd_pcm_substream *sub, struct snd_soc_dai *dai) +{ + struct snd_soc_component *comp = dai->component; + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(comp); + int i, ret; + + switch (cs4234->format) { + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_I2S: + cs4234->rate_constraint.nrats = 2; + + /* + * Playback only supports 24-bit samples in these modes. + * Note: SNDRV_PCM_HW_PARAM_SAMPLE_BITS constrains the physical + * width, which we don't care about, so constrain the format. + */ + if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_pcm_hw_constraint_mask64( + sub->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_constraint_minmax(sub->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 1, 4); + if (ret < 0) + return ret; + } + + break; + case SND_SOC_DAIFMT_DSP_A: + cs4234->rate_constraint.nrats = 1; + break; + default: + dev_err(comp->dev, "Startup unsupported DAI format\n"); + return -EINVAL; + } + + for (i = 0; i < cs4234->rate_constraint.nrats; i++) + cs4234->rate_dividers[i].num = cs4234->mclk_rate / CS4234_MCLK_SCALE; + + ret = snd_pcm_hw_constraint_ratnums(sub->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &cs4234->rate_constraint); + if (ret < 0) + return ret; + + /* + * MCLK/rate may be a valid ratio but out-of-spec (e.g. 24576000/64000) + * so this rule limits the range of sample rate for given MCLK. + */ + return snd_pcm_hw_rule_add(sub->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + cs4234_dai_rule_rate, cs4234, -1); +} + +static int cs4234_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component); + unsigned int slot_offset, dac5_slot, dac5_mask_group; + uint8_t dac5_masks[4]; + + if (slot_width != 32) { + dev_err(component->dev, "Unsupported slot width\n"); + return -EINVAL; + } + + /* Either 4 or 5 consecutive bits, DAC5 is optional */ + slot_offset = ffs(tx_mask) - 1; + tx_mask >>= slot_offset; + if ((slot_offset % 4) || ((tx_mask != 0x0F) && (tx_mask != 0x1F))) { + dev_err(component->dev, "Unsupported tx slots allocation\n"); + return -EINVAL; + } + + regmap_update_bits(cs4234->regmap, CS4234_SP_DATA_SEL, CS4234_DAC14_SRC_MASK, + (slot_offset / 4) << CS4234_DAC14_SRC_SHIFT); + regmap_update_bits(cs4234->regmap, CS4234_SP_DATA_SEL, CS4234_LL_SRC_MASK, + (slot_offset / 4) << CS4234_LL_SRC_SHIFT); + + if (tx_mask == 0x1F) { + dac5_slot = slot_offset + 4; + memset(dac5_masks, 0xFF, sizeof(dac5_masks)); + dac5_mask_group = dac5_slot / 8; + dac5_slot %= 8; + dac5_masks[dac5_mask_group] ^= BIT(7 - dac5_slot); + regmap_bulk_write(cs4234->regmap, + CS4234_SDIN1_MASK1, + dac5_masks, + ARRAY_SIZE(dac5_masks)); + } + + return 0; +} + +static const struct snd_soc_dai_ops cs4234_dai_ops = { + .set_fmt = cs4234_dai_set_fmt, + .hw_params = cs4234_dai_hw_params, + .startup = cs4234_dai_startup, + .set_tdm_slot = cs4234_dai_set_tdm_slot, +}; + +static struct snd_soc_dai_driver cs4234_dai[] = { + { + .name = "cs4234-dai", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 5, + .rates = CS4234_PCM_RATES, + .formats = CS4234_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = CS4234_PCM_RATES, + .formats = CS4234_FORMATS, + }, + .ops = &cs4234_dai_ops, + .symmetric_rate = 1, + }, +}; + +static const struct reg_default cs4234_default_reg[] = { + { CS4234_CLOCK_SP, 0x04}, + { CS4234_SAMPLE_WIDTH, 0xFF}, + { CS4234_SP_CTRL, 0x48}, + { CS4234_SP_DATA_SEL, 0x01}, + { CS4234_SDIN1_MASK1, 0xFF}, + { CS4234_SDIN1_MASK2, 0xFF}, + { CS4234_SDIN2_MASK1, 0xFF}, + { CS4234_SDIN2_MASK2, 0xFF}, + { CS4234_TPS_CTRL, 0x00}, + { CS4234_ADC_CTRL1, 0xC0}, + { CS4234_ADC_CTRL2, 0xFF}, + { CS4234_LOW_LAT_CTRL1, 0xE0}, + { CS4234_DAC_CTRL1, 0xE0}, + { CS4234_DAC_CTRL2, 0xE0}, + { CS4234_DAC_CTRL3, 0xBF}, + { CS4234_DAC_CTRL4, 0x1F}, + { CS4234_VOLUME_MODE, 0x87}, + { CS4234_MASTER_VOL, 0x10}, + { CS4234_DAC1_VOL, 0x10}, + { CS4234_DAC2_VOL, 0x10}, + { CS4234_DAC3_VOL, 0x10}, + { CS4234_DAC4_VOL, 0x10}, + { CS4234_DAC5_VOL, 0x10}, + { CS4234_INT_CTRL, 0x40}, + { CS4234_INT_MASK1, 0x10}, + { CS4234_INT_MASK2, 0x20}, +}; + +static bool cs4234_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4234_DEVID_AB ... CS4234_DEVID_EF: + case CS4234_REVID ... CS4234_DAC5_VOL: + case CS4234_INT_CTRL ... CS4234_MAX_REGISTER: + return true; + default: + return false; + } +} + +static bool cs4234_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4234_INT_NOTIFY1: + case CS4234_INT_NOTIFY2: + return true; + default: + return false; + } +} + +static bool cs4234_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4234_DEVID_AB ... CS4234_REVID: + case CS4234_INT_NOTIFY1 ... CS4234_INT_NOTIFY2: + return false; + default: + return true; + } +} + +static const struct snd_soc_component_driver soc_component_cs4234 = { + .dapm_widgets = cs4234_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4234_dapm_widgets), + .dapm_routes = cs4234_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4234_dapm_routes), + .controls = cs4234_snd_controls, + .num_controls = ARRAY_SIZE(cs4234_snd_controls), + .set_bias_level = cs4234_set_bias_level, + .idle_bias_on = 1, + .suspend_bias_off = 1, + .endianness = 1, +}; + +static const struct regmap_config cs4234_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS4234_MAX_REGISTER, + .readable_reg = cs4234_readable_register, + .volatile_reg = cs4234_volatile_reg, + .writeable_reg = cs4234_writeable_register, + .reg_defaults = cs4234_default_reg, + .num_reg_defaults = ARRAY_SIZE(cs4234_default_reg), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const char * const cs4234_core_supplies[] = { + "VA", + "VL", +}; + +static void cs4234_shutdown(struct cs4234 *cs4234) +{ + cancel_delayed_work_sync(&cs4234->vq_ramp_delay); + reinit_completion(&cs4234->vq_ramp_complete); + + regmap_update_bits(cs4234->regmap, CS4234_DAC_CTRL4, CS4234_VQ_RAMP_MASK, + CS4234_VQ_RAMP_MASK); + msleep(50); + regcache_cache_only(cs4234->regmap, true); + /* Clear VQ Ramp Bit in cache for the next PowerUp */ + regmap_update_bits(cs4234->regmap, CS4234_DAC_CTRL4, CS4234_VQ_RAMP_MASK, 0); + gpiod_set_value_cansleep(cs4234->reset_gpio, 0); + regulator_bulk_disable(cs4234->num_core_supplies, cs4234->core_supplies); + clk_disable_unprepare(cs4234->mclk); +} + +static int cs4234_powerup(struct cs4234 *cs4234) +{ + int ret; + + ret = clk_prepare_enable(cs4234->mclk); + if (ret) { + dev_err(cs4234->dev, "Failed to enable mclk: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(cs4234->num_core_supplies, cs4234->core_supplies); + if (ret) { + dev_err(cs4234->dev, "Failed to enable core supplies: %d\n", ret); + clk_disable_unprepare(cs4234->mclk); + return ret; + } + + usleep_range(CS4234_HOLD_RESET_TIME_US, 2 * CS4234_HOLD_RESET_TIME_US); + gpiod_set_value_cansleep(cs4234->reset_gpio, 1); + + /* Make sure hardware reset done 2 ms + (3000/MCLK) */ + usleep_range(CS4234_BOOT_TIME_US, CS4234_BOOT_TIME_US * 2); + + queue_delayed_work(system_power_efficient_wq, + &cs4234->vq_ramp_delay, + msecs_to_jiffies(CS4234_VQ_CHARGE_MS)); + + return 0; +} + +static int cs4234_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs4234 *cs4234; + struct device *dev = &i2c_client->dev; + unsigned int revid; + uint32_t devid; + uint8_t ids[3]; + int ret = 0, i; + + cs4234 = devm_kzalloc(dev, sizeof(*cs4234), GFP_KERNEL); + if (!cs4234) + return -ENOMEM; + i2c_set_clientdata(i2c_client, cs4234); + cs4234->dev = dev; + init_completion(&cs4234->vq_ramp_complete); + INIT_DELAYED_WORK(&cs4234->vq_ramp_delay, cs4234_vq_ramp_done); + + cs4234->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs4234->reset_gpio)) + return PTR_ERR(cs4234->reset_gpio); + + BUILD_BUG_ON(ARRAY_SIZE(cs4234->core_supplies) < ARRAY_SIZE(cs4234_core_supplies)); + + cs4234->num_core_supplies = ARRAY_SIZE(cs4234_core_supplies); + for (i = 0; i < ARRAY_SIZE(cs4234_core_supplies); i++) + cs4234->core_supplies[i].supply = cs4234_core_supplies[i]; + + ret = devm_regulator_bulk_get(dev, cs4234->num_core_supplies, cs4234->core_supplies); + if (ret) { + dev_err(dev, "Failed to request core supplies %d\n", ret); + return ret; + } + + cs4234->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(cs4234->mclk)) { + ret = PTR_ERR(cs4234->mclk); + dev_err(dev, "Failed to get the mclk: %d\n", ret); + return ret; + } + cs4234->mclk_rate = clk_get_rate(cs4234->mclk); + + if (cs4234->mclk_rate < 7680000 || cs4234->mclk_rate > 25600000) { + dev_err(dev, "Invalid Master Clock rate\n"); + return -EINVAL; + } + + cs4234->regmap = devm_regmap_init_i2c(i2c_client, &cs4234_regmap); + if (IS_ERR(cs4234->regmap)) { + ret = PTR_ERR(cs4234->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = cs4234_powerup(cs4234); + if (ret) + return ret; + + ret = regmap_bulk_read(cs4234->regmap, CS4234_DEVID_AB, ids, ARRAY_SIZE(ids)); + if (ret < 0) { + dev_err(dev, "Failed to read DEVID: %d\n", ret); + goto fail_shutdown; + } + + devid = (ids[0] << 16) | (ids[1] << 8) | ids[2]; + if (devid != CS4234_SUPPORTED_ID) { + dev_err(dev, "Unknown device ID: %x\n", devid); + ret = -EINVAL; + goto fail_shutdown; + } + + ret = regmap_read(cs4234->regmap, CS4234_REVID, &revid); + if (ret < 0) { + dev_err(dev, "Failed to read CS4234_REVID: %d\n", ret); + goto fail_shutdown; + } + + dev_info(dev, "Cirrus Logic CS4234, Alpha Rev: %02X, Numeric Rev: %02X\n", + (revid & 0xF0) >> 4, revid & 0x0F); + + ret = regulator_get_voltage(cs4234->core_supplies[CS4234_SUPPLY_VA].consumer); + switch (ret) { + case 3135000 ... 3650000: + regmap_update_bits(cs4234->regmap, CS4234_ADC_CTRL1, + CS4234_VA_SEL_MASK, + CS4234_3V3 << CS4234_VA_SEL_SHIFT); + break; + case 4750000 ... 5250000: + regmap_update_bits(cs4234->regmap, CS4234_ADC_CTRL1, + CS4234_VA_SEL_MASK, + CS4234_5V << CS4234_VA_SEL_SHIFT); + break; + default: + dev_err(dev, "Invalid VA voltage\n"); + ret = -EINVAL; + goto fail_shutdown; + } + + pm_runtime_set_active(&i2c_client->dev); + pm_runtime_enable(&i2c_client->dev); + + memcpy(&cs4234->rate_dividers, &cs4234_dividers, sizeof(cs4234_dividers)); + cs4234->rate_constraint.rats = cs4234->rate_dividers; + + ret = snd_soc_register_component(dev, &soc_component_cs4234, cs4234_dai, + ARRAY_SIZE(cs4234_dai)); + if (ret < 0) { + dev_err(dev, "Failed to register component:%d\n", ret); + pm_runtime_disable(&i2c_client->dev); + goto fail_shutdown; + } + + return ret; + +fail_shutdown: + cs4234_shutdown(cs4234); + + return ret; +} + +static void cs4234_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs4234 *cs4234 = i2c_get_clientdata(i2c_client); + struct device *dev = &i2c_client->dev; + + snd_soc_unregister_component(dev); + pm_runtime_disable(dev); + cs4234_shutdown(cs4234); +} + +static int __maybe_unused cs4234_runtime_resume(struct device *dev) +{ + struct cs4234 *cs4234 = dev_get_drvdata(dev); + int ret; + + ret = cs4234_powerup(cs4234); + if (ret) + return ret; + + regcache_mark_dirty(cs4234->regmap); + regcache_cache_only(cs4234->regmap, false); + ret = regcache_sync(cs4234->regmap); + if (ret) { + dev_err(dev, "Failed to sync regmap: %d\n", ret); + cs4234_shutdown(cs4234); + return ret; + } + + return 0; +} + +static int __maybe_unused cs4234_runtime_suspend(struct device *dev) +{ + struct cs4234 *cs4234 = dev_get_drvdata(dev); + + cs4234_shutdown(cs4234); + + return 0; +} + +static const struct dev_pm_ops cs4234_pm = { + SET_RUNTIME_PM_OPS(cs4234_runtime_suspend, cs4234_runtime_resume, NULL) +}; + +static const struct of_device_id cs4234_of_match[] = { + { .compatible = "cirrus,cs4234", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs4234_of_match); + +static struct i2c_driver cs4234_i2c_driver = { + .driver = { + .name = "cs4234", + .pm = &cs4234_pm, + .of_match_table = cs4234_of_match, + }, + .probe = cs4234_i2c_probe, + .remove = cs4234_i2c_remove, +}; +module_i2c_driver(cs4234_i2c_driver); + +MODULE_DESCRIPTION("ASoC Cirrus Logic CS4234 driver"); +MODULE_AUTHOR("Lucas Tanure "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/cs4234.h b/sound/soc/codecs/cs4234.h new file mode 100644 index 0000000000..76a75afc19 --- /dev/null +++ b/sound/soc/codecs/cs4234.h @@ -0,0 +1,287 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC Audio driver for CS4234 codec + * + * Copyright (C) 2020 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS4234_H +#define CS4234_H + +#define CS4234_DEVID_AB 0x01 +#define CS4234_DEVID_CD 0x02 +#define CS4234_DEVID_EF 0x03 +#define CS4234_REVID 0x05 + +#define CS4234_CLOCK_SP 0x06 +#define CS4234_BASE_RATE_MASK 0xC0 +#define CS4234_BASE_RATE_SHIFT 6 +#define CS4234_SPEED_MODE_MASK 0x30 +#define CS4234_SPEED_MODE_SHIFT 4 +#define CS4234_MCLK_RATE_MASK 0x0E +#define CS4234_MCLK_RATE_SHIFT 1 + +#define CS4234_SAMPLE_WIDTH 0x07 +#define CS4234_SDOUTX_SW_MASK 0xC0 +#define CS4234_SDOUTX_SW_SHIFT 6 +#define CS4234_INPUT_SW_MASK 0x30 +#define CS4234_INPUT_SW_SHIFT 4 +#define CS4234_LOW_LAT_SW_MASK 0x0C +#define CS4234_LOW_LAT_SW_SHIFT 2 +#define CS4234_DAC5_SW_MASK 0x03 +#define CS4234_DAC5_SW_SHIFT 0 + +#define CS4234_SP_CTRL 0x08 +#define CS4234_INVT_SCLK_MASK 0x80 +#define CS4234_INVT_SCLK_SHIFT 7 +#define CS4234_DAC5_SRC_MASK 0x70 +#define CS4234_DAC5_SRC_SHIFT 4 +#define CS4234_SP_FORMAT_MASK 0x0C +#define CS4234_SP_FORMAT_SHIFT 2 +#define CS4234_SDO_CHAIN_MASK 0x02 +#define CS4234_SDO_CHAIN_SHIFT 1 +#define CS4234_MST_SLV_MASK 0x01 +#define CS4234_MST_SLV_SHIFT 0 + +#define CS4234_SP_DATA_SEL 0x09 +#define CS4234_DAC14_SRC_MASK 0x38 +#define CS4234_DAC14_SRC_SHIFT 3 +#define CS4234_LL_SRC_MASK 0x07 +#define CS4234_LL_SRC_SHIFT 0 + +#define CS4234_SDIN1_MASK1 0x0A +#define CS4234_SDIN1_MASK2 0x0B +#define CS4234_SDIN2_MASK1 0x0C +#define CS4234_SDIN2_MASK2 0x0D + +#define CS4234_TPS_CTRL 0x0E +#define CS4234_TPS_MODE_MASK 0x80 +#define CS4234_TPS_MODE_SHIFT 7 +#define CS4234_TPS_OFST_MASK 0x70 +#define CS4234_TPS_OFST_SHIFT 4 +#define CS4234_GRP_DELAY_MASK 0x0F +#define CS4234_GRP_DELAY_SHIFT 0 + +#define CS4234_ADC_CTRL1 0x0F +#define CS4234_VA_SEL_MASK 0x20 +#define CS4234_VA_SEL_SHIFT 5 +#define CS4234_ENA_HPF_MASK 0x10 +#define CS4234_ENA_HPF_SHIFT 4 +#define CS4234_INV_ADC_MASK 0x0F +#define CS4234_INV_ADC4_MASK 0x08 +#define CS4234_INV_ADC4_SHIFT 3 +#define CS4234_INV_ADC3_MASK 0x04 +#define CS4234_INV_ADC3_SHIFT 2 +#define CS4234_INV_ADC2_MASK 0x02 +#define CS4234_INV_ADC2_SHIFT 1 +#define CS4234_INV_ADC1_MASK 0x01 +#define CS4234_INV_ADC1_SHIFT 0 + +#define CS4234_ADC_CTRL2 0x10 +#define CS4234_MUTE_ADC4_MASK 0x80 +#define CS4234_MUTE_ADC4_SHIFT 7 +#define CS4234_MUTE_ADC3_MASK 0x40 +#define CS4234_MUTE_ADC3_SHIFT 6 +#define CS4234_MUTE_ADC2_MASK 0x20 +#define CS4234_MUTE_ADC2_SHIFT 5 +#define CS4234_MUTE_ADC1_MASK 0x10 +#define CS4234_MUTE_ADC1_SHIFT 4 +#define CS4234_PDN_ADC4_MASK 0x08 +#define CS4234_PDN_ADC4_SHIFT 3 +#define CS4234_PDN_ADC3_MASK 0x04 +#define CS4234_PDN_ADC3_SHIFT 2 +#define CS4234_PDN_ADC2_MASK 0x02 +#define CS4234_PDN_ADC2_SHIFT 1 +#define CS4234_PDN_ADC1_MASK 0x01 +#define CS4234_PDN_ADC1_SHIFT 0 + +#define CS4234_LOW_LAT_CTRL1 0x11 +#define CS4234_LL_NG_MASK 0xE0 +#define CS4234_LL_NG_SHIFT 5 +#define CS4234_INV_LL_MASK 0x0F +#define CS4234_INV_LL4_MASK 0x08 +#define CS4234_INV_LL4_SHIFT 3 +#define CS4234_INV_LL3_MASK 0x04 +#define CS4234_INV_LL3_SHIFT 2 +#define CS4234_INV_LL2_MASK 0x02 +#define CS4234_INV_LL2_SHIFT 1 +#define CS4234_INV_LL1_MASK 0x01 +#define CS4234_INV_LL1_SHIFT 0 + +#define CS4234_DAC_CTRL1 0x12 +#define CS4234_DAC14_NG_MASK 0xE0 +#define CS4234_DAC14_NG_SHIFT 5 +#define CS4234_DAC14_DE_MASK 0x10 +#define CS4234_DAC14_DE_SHIFT 4 +#define CS4234_DAC5_DE_MASK 0x08 +#define CS4234_DAC5_DE_SHIFT 3 +#define CS4234_DAC5_MVC_MASK 0x04 +#define CS4234_DAC5_MVC_SHIFT 2 +#define CS4234_DAC5_CFG_FLTR_MASK 0x03 +#define CS4234_DAC5_CFG_FLTR_SHIFT 0 + +#define CS4234_DAC_CTRL2 0x13 +#define CS4234_DAC5_NG_MASK 0xE0 +#define CS4234_DAC5_NG_SHIFT 5 +#define CS4234_INV_DAC_MASK 0x1F +#define CS4234_INV_DAC5_MASK 0x10 +#define CS4234_INV_DAC5_SHIFT 4 +#define CS4234_INV_DAC4_MASK 0x08 +#define CS4234_INV_DAC4_SHIFT 3 +#define CS4234_INV_DAC3_MASK 0x04 +#define CS4234_INV_DAC3_SHIFT 2 +#define CS4234_INV_DAC2_MASK 0x02 +#define CS4234_INV_DAC2_SHIFT 1 +#define CS4234_INV_DAC1_MASK 0x01 +#define CS4234_INV_DAC1_SHIFT 0 + +#define CS4234_DAC_CTRL3 0x14 +#define CS4234_DAC5_ATT_MASK 0x80 +#define CS4234_DAC5_ATT_SHIFT 7 +#define CS4234_DAC14_ATT_MASK 0x40 +#define CS4234_DAC14_ATT_SHIFT 6 +#define CS4234_MUTE_LL_MASK 0x20 +#define CS4234_MUTE_LL_SHIFT 5 +#define CS4234_MUTE_DAC5_MASK 0x10 +#define CS4234_MUTE_DAC5_SHIFT 4 +#define CS4234_MUTE_DAC4_MASK 0x08 +#define CS4234_MUTE_DAC4_SHIFT 3 +#define CS4234_MUTE_DAC3_MASK 0x04 +#define CS4234_MUTE_DAC3_SHIFT 2 +#define CS4234_MUTE_DAC2_MASK 0x02 +#define CS4234_MUTE_DAC2_SHIFT 1 +#define CS4234_MUTE_DAC1_MASK 0x01 +#define CS4234_MUTE_DAC1_SHIFT 0 + +#define CS4234_DAC_CTRL4 0x15 +#define CS4234_VQ_RAMP_MASK 0x80 +#define CS4234_VQ_RAMP_SHIFT 7 +#define CS4234_TPS_GAIN_MASK 0x40 +#define CS4234_TPS_GAIN_SHIFT 6 +#define CS4234_PDN_DAC5_MASK 0x10 +#define CS4234_PDN_DAC5_SHIFT 4 +#define CS4234_PDN_DAC4_MASK 0x08 +#define CS4234_PDN_DAC4_SHIFT 3 +#define CS4234_PDN_DAC3_MASK 0x04 +#define CS4234_PDN_DAC3_SHIFT 2 +#define CS4234_PDN_DAC2_MASK 0x02 +#define CS4234_PDN_DAC2_SHIFT 1 +#define CS4234_PDN_DAC1_MASK 0x01 +#define CS4234_PDN_DAC1_SHIFT 0 + +#define CS4234_VOLUME_MODE 0x16 +#define CS4234_MUTE_DELAY_MASK 0xC0 +#define CS4234_MUTE_DELAY_SHIFT 6 +#define CS4234_MIN_DELAY_MASK 0x38 +#define CS4234_MIN_DELAY_SHIFT 3 +#define CS4234_MAX_DELAY_MASK 0x07 +#define CS4234_MAX_DELAY_SHIFT 0 + +#define CS4234_MASTER_VOL 0x17 +#define CS4234_DAC1_VOL 0x18 +#define CS4234_DAC2_VOL 0x19 +#define CS4234_DAC3_VOL 0x1A +#define CS4234_DAC4_VOL 0x1B +#define CS4234_DAC5_VOL 0x1C + +#define CS4234_INT_CTRL 0x1E +#define CS4234_INT_MODE_MASK 0x80 +#define CS4234_INT_MODE_SHIFT 7 +#define CS4234_INT_PIN_MASK 0x60 +#define CS4234_INT_PIN_SHIFT 5 + +#define CS4234_INT_MASK1 0x1F +#define CS4234_MSK_TST_MODE_MASK 0x80 +#define CS4234_MSK_TST_MODE_ERR_SHIFT 7 +#define CS4234_MSK_SP_ERR_MASK 0x40 +#define CS4234_MSK_SP_ERR_SHIFT 6 +#define CS4234_MSK_CLK_ERR_MASK 0x08 +#define CS4234_MSK_CLK_ERR_SHIFT 5 +#define CS4234_MSK_ADC4_OVFL_MASK 0x08 +#define CS4234_MSK_ADC4_OVFL_SHIFT 3 +#define CS4234_MSK_ADC3_OVFL_MASK 0x04 +#define CS4234_MSK_ADC3_OVFL_SHIFT 2 +#define CS4234_MSK_ADC2_OVFL_MASK 0x02 +#define CS4234_MSK_ADC2_OVFL_SHIFT 1 +#define CS4234_MSK_ADC1_OVFL_MASK 0x01 +#define CS4234_MSK_ADC1_OVFL_SHIFT 0 + +#define CS4234_INT_MASK2 0x20 +#define CS4234_MSK_DAC5_CLIP_MASK 0x10 +#define CS4234_MSK_DAC5_CLIP_SHIFT 4 +#define CS4234_MSK_DAC4_CLIP_MASK 0x08 +#define CS4234_MSK_DAC4_CLIP_SHIFT 3 +#define CS4234_MSK_DAC3_CLIP_MASK 0x04 +#define CS4234_MSK_DAC3_CLIP_SHIFT 2 +#define CS4234_MSK_DAC2_CLIP_MASK 0x02 +#define CS4234_MSK_DAC2_CLIP_SHIFT 1 +#define CS4234_MSK_DAC1_CLIP_MASK 0x01 +#define CS4234_MSK_DAC1_CLIP_SHIFT 0 + +#define CS4234_INT_NOTIFY1 0x21 +#define CS4234_TST_MODE_MASK 0x80 +#define CS4234_TST_MODE_SHIFT 7 +#define CS4234_SP_ERR_MASK 0x40 +#define CS4234_SP_ERR_SHIFT 6 +#define CS4234_CLK_MOD_ERR_MASK 0x08 +#define CS4234_CLK_MOD_ERR_SHIFT 5 +#define CS4234_ADC4_OVFL_MASK 0x08 +#define CS4234_ADC4_OVFL_SHIFT 3 +#define CS4234_ADC3_OVFL_MASK 0x04 +#define CS4234_ADC3_OVFL_SHIFT 2 +#define CS4234_ADC2_OVFL_MASK 0x02 +#define CS4234_ADC2_OVFL_SHIFT 1 +#define CS4234_ADC1_OVFL_MASK 0x01 +#define CS4234_ADC1_OVFL_SHIFT 0 + +#define CS4234_INT_NOTIFY2 0x22 +#define CS4234_DAC5_CLIP_MASK 0x10 +#define CS4234_DAC5_CLIP_SHIFT 4 +#define CS4234_DAC4_CLIP_MASK 0x08 +#define CS4234_DAC4_CLIP_SHIFT 3 +#define CS4234_DAC3_CLIP_MASK 0x04 +#define CS4234_DAC3_CLIP_SHIFT 2 +#define CS4234_DAC2_CLIP_MASK 0x02 +#define CS4234_DAC2_CLIP_SHIFT 1 +#define CS4234_DAC1_CLIP_MASK 0x01 +#define CS4234_DAC1_CLIP_SHIFT 0 + +#define CS4234_MAX_REGISTER CS4234_INT_NOTIFY2 + +#define CS4234_SUPPORTED_ID 0x423400 +#define CS4234_BOOT_TIME_US 3000 +#define CS4234_HOLD_RESET_TIME_US 1000 +#define CS4234_VQ_CHARGE_MS 1000 + +#define CS4234_PCM_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define CS4234_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +enum cs4234_supplies { + CS4234_SUPPLY_VA = 0, + CS4234_SUPPLY_VL, +}; + +enum cs4234_va_sel { + CS4234_3V3 = 0, + CS4234_5V, +}; + +enum cs4234_sp_format { + CS4234_LEFT_J = 0, + CS4234_I2S, + CS4234_TDM, +}; + +enum cs4234_base_rate_advisory { + CS4234_48K = 0, + CS4234_44K1, + CS4234_32K, +}; + +#endif diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c new file mode 100644 index 0000000000..1ed1e60d8e --- /dev/null +++ b/sound/soc/codecs/cs4265.c @@ -0,0 +1,660 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs4265.c -- CS4265 ALSA SoC audio driver + * + * Copyright 2014 Cirrus Logic, Inc. + * + * Author: Paul Handrigan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs4265.h" + +struct cs4265_private { + struct regmap *regmap; + struct gpio_desc *reset_gpio; + u8 format; + u32 sysclk; +}; + +static const struct reg_default cs4265_reg_defaults[] = { + { CS4265_PWRCTL, 0x0F }, + { CS4265_DAC_CTL, 0x08 }, + { CS4265_ADC_CTL, 0x00 }, + { CS4265_MCLK_FREQ, 0x00 }, + { CS4265_SIG_SEL, 0x40 }, + { CS4265_CHB_PGA_CTL, 0x00 }, + { CS4265_CHA_PGA_CTL, 0x00 }, + { CS4265_ADC_CTL2, 0x19 }, + { CS4265_DAC_CHA_VOL, 0x00 }, + { CS4265_DAC_CHB_VOL, 0x00 }, + { CS4265_DAC_CTL2, 0xC0 }, + { CS4265_SPDIF_CTL1, 0x00 }, + { CS4265_SPDIF_CTL2, 0x00 }, + { CS4265_INT_MASK, 0x00 }, + { CS4265_STATUS_MODE_MSB, 0x00 }, + { CS4265_STATUS_MODE_LSB, 0x00 }, +}; + +static bool cs4265_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4265_CHIP_ID ... CS4265_MAX_REGISTER: + return true; + default: + return false; + } +} + +static bool cs4265_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4265_INT_STATUS: + return true; + default: + return false; + } +} + +static DECLARE_TLV_DB_SCALE(pga_tlv, -1200, 50, 0); + +static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 0); + +static const char * const digital_input_mux_text[] = { + "SDIN1", "SDIN2" +}; + +static SOC_ENUM_SINGLE_DECL(digital_input_mux_enum, CS4265_SIG_SEL, 7, + digital_input_mux_text); + +static const struct snd_kcontrol_new digital_input_mux = + SOC_DAPM_ENUM("Digital Input Mux", digital_input_mux_enum); + +static const char * const mic_linein_text[] = { + "MIC", "LINEIN" +}; + +static SOC_ENUM_SINGLE_DECL(mic_linein_enum, CS4265_ADC_CTL2, 0, + mic_linein_text); + +static const char * const cam_mode_text[] = { + "One Byte", "Two Byte" +}; + +static SOC_ENUM_SINGLE_DECL(cam_mode_enum, CS4265_SPDIF_CTL1, 5, + cam_mode_text); + +static const char * const cam_mono_stereo_text[] = { + "Stereo", "Mono" +}; + +static SOC_ENUM_SINGLE_DECL(spdif_mono_stereo_enum, CS4265_SPDIF_CTL2, 2, + cam_mono_stereo_text); + +static const char * const mono_select_text[] = { + "Channel A", "Channel B" +}; + +static SOC_ENUM_SINGLE_DECL(spdif_mono_select_enum, CS4265_SPDIF_CTL2, 0, + mono_select_text); + +static const struct snd_kcontrol_new mic_linein_mux = + SOC_DAPM_ENUM("ADC Input Capture Mux", mic_linein_enum); + +static const struct snd_kcontrol_new loopback_ctl = + SOC_DAPM_SINGLE("Switch", CS4265_SIG_SEL, 1, 1, 0); + +static const struct snd_kcontrol_new spdif_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 0, 0); + +static const struct snd_kcontrol_new dac_switch = + SOC_DAPM_SINGLE("Switch", CS4265_PWRCTL, 1, 1, 0); + +static const struct snd_kcontrol_new cs4265_snd_controls[] = { + + SOC_DOUBLE_R_SX_TLV("PGA Volume", CS4265_CHA_PGA_CTL, + CS4265_CHB_PGA_CTL, 0, 0x28, 0x30, pga_tlv), + SOC_DOUBLE_R_TLV("DAC Volume", CS4265_DAC_CHA_VOL, + CS4265_DAC_CHB_VOL, 0, 0xFF, 1, dac_tlv), + SOC_SINGLE("De-emp 44.1kHz Switch", CS4265_DAC_CTL, 1, + 1, 0), + SOC_SINGLE("DAC INV Switch", CS4265_DAC_CTL2, 5, + 1, 0), + SOC_SINGLE("DAC Zero Cross Switch", CS4265_DAC_CTL2, 6, + 1, 0), + SOC_SINGLE("DAC Soft Ramp Switch", CS4265_DAC_CTL2, 7, + 1, 0), + SOC_SINGLE("ADC HPF Switch", CS4265_ADC_CTL, 1, + 1, 0), + SOC_SINGLE("ADC Zero Cross Switch", CS4265_ADC_CTL2, 3, + 1, 1), + SOC_SINGLE("ADC Soft Ramp Switch", CS4265_ADC_CTL2, 7, + 1, 0), + SOC_SINGLE("E to F Buffer Disable Switch", CS4265_SPDIF_CTL1, + 6, 1, 0), + SOC_ENUM("C Data Access", cam_mode_enum), + SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2, + 3, 1, 0), + SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum), + SOC_SINGLE("MMTLR Data Switch", CS4265_SPDIF_CTL2, 0, 1, 0), + SOC_ENUM("Mono Channel Select", spdif_mono_select_enum), + SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24), +}; + +static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = { + + SND_SOC_DAPM_INPUT("LINEINL"), + SND_SOC_DAPM_INPUT("LINEINR"), + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("MICR"), + + SND_SOC_DAPM_AIF_OUT("DOUT", NULL, 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SPDIFOUT", NULL, 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("ADC Mux", SND_SOC_NOPM, 0, 0, &mic_linein_mux), + + SND_SOC_DAPM_ADC("ADC", NULL, CS4265_PWRCTL, 2, 1), + SND_SOC_DAPM_PGA("Pre-amp MIC", CS4265_PWRCTL, 3, + 1, NULL, 0), + + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, + 0, 0, &digital_input_mux), + + SND_SOC_DAPM_MIXER("SDIN1 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SDIN2 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SPDIF Transmitter", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0, + &loopback_ctl), + SND_SOC_DAPM_SWITCH("SPDIF", CS4265_SPDIF_CTL2, 5, 1, + &spdif_switch), + SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1, + &dac_switch), + + SND_SOC_DAPM_AIF_IN("DIN1", NULL, 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DIN2", NULL, 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("TXIN", NULL, 0, + CS4265_SPDIF_CTL2, 5, 1), + + SND_SOC_DAPM_OUTPUT("LINEOUTL"), + SND_SOC_DAPM_OUTPUT("LINEOUTR"), + +}; + +static const struct snd_soc_dapm_route cs4265_audio_map[] = { + + {"DIN1", NULL, "DAI1 Playback"}, + {"DIN2", NULL, "DAI2 Playback"}, + {"SDIN1 Input Mixer", NULL, "DIN1"}, + {"SDIN2 Input Mixer", NULL, "DIN2"}, + {"Input Mux", "SDIN1", "SDIN1 Input Mixer"}, + {"Input Mux", "SDIN2", "SDIN2 Input Mixer"}, + {"DAC", "Switch", "Input Mux"}, + {"SPDIF", "Switch", "Input Mux"}, + {"LINEOUTL", NULL, "DAC"}, + {"LINEOUTR", NULL, "DAC"}, + {"SPDIFOUT", NULL, "SPDIF"}, + + {"Pre-amp MIC", NULL, "MICL"}, + {"Pre-amp MIC", NULL, "MICR"}, + {"ADC Mux", "MIC", "Pre-amp MIC"}, + {"ADC Mux", "LINEIN", "LINEINL"}, + {"ADC Mux", "LINEIN", "LINEINR"}, + {"ADC", NULL, "ADC Mux"}, + {"DOUT", NULL, "ADC"}, + {"DAI1 Capture", NULL, "DOUT"}, + {"DAI2 Capture", NULL, "DOUT"}, + + /* Loopback */ + {"Loopback", "Switch", "ADC"}, + {"DAC", NULL, "Loopback"}, +}; + +struct cs4265_clk_para { + u32 mclk; + u32 rate; + u8 fm_mode; /* values 1, 2, or 4 */ + u8 mclkdiv; +}; + +static const struct cs4265_clk_para clk_map_table[] = { + /*32k*/ + {8192000, 32000, 0, 0}, + {12288000, 32000, 0, 1}, + {16384000, 32000, 0, 2}, + {24576000, 32000, 0, 3}, + {32768000, 32000, 0, 4}, + + /*44.1k*/ + {11289600, 44100, 0, 0}, + {16934400, 44100, 0, 1}, + {22579200, 44100, 0, 2}, + {33868000, 44100, 0, 3}, + {45158400, 44100, 0, 4}, + + /*48k*/ + {12288000, 48000, 0, 0}, + {18432000, 48000, 0, 1}, + {24576000, 48000, 0, 2}, + {36864000, 48000, 0, 3}, + {49152000, 48000, 0, 4}, + + /*64k*/ + {8192000, 64000, 1, 0}, + {12288000, 64000, 1, 1}, + {16934400, 64000, 1, 2}, + {24576000, 64000, 1, 3}, + {32768000, 64000, 1, 4}, + + /* 88.2k */ + {11289600, 88200, 1, 0}, + {16934400, 88200, 1, 1}, + {22579200, 88200, 1, 2}, + {33868000, 88200, 1, 3}, + {45158400, 88200, 1, 4}, + + /* 96k */ + {12288000, 96000, 1, 0}, + {18432000, 96000, 1, 1}, + {24576000, 96000, 1, 2}, + {36864000, 96000, 1, 3}, + {49152000, 96000, 1, 4}, + + /* 128k */ + {8192000, 128000, 2, 0}, + {12288000, 128000, 2, 1}, + {16934400, 128000, 2, 2}, + {24576000, 128000, 2, 3}, + {32768000, 128000, 2, 4}, + + /* 176.4k */ + {11289600, 176400, 2, 0}, + {16934400, 176400, 2, 1}, + {22579200, 176400, 2, 2}, + {33868000, 176400, 2, 3}, + {49152000, 176400, 2, 4}, + + /* 192k */ + {12288000, 192000, 2, 0}, + {18432000, 192000, 2, 1}, + {24576000, 192000, 2, 2}, + {36864000, 192000, 2, 3}, + {49152000, 192000, 2, 4}, +}; + +static int cs4265_get_clk_index(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { + if (clk_map_table[i].rate == rate && + clk_map_table[i].mclk == mclk) + return i; + } + return -EINVAL; +} + +static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(component); + int i; + + if (clk_id != 0) { + dev_err(component->dev, "Invalid clk_id %d\n", clk_id); + return -EINVAL; + } + for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { + if (clk_map_table[i].mclk == freq) { + cs4265->sysclk = freq; + return 0; + } + } + cs4265->sysclk = 0; + dev_err(component->dev, "Invalid freq parameter %d\n", freq); + return -EINVAL; +} + +static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(component); + u8 iface = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + snd_soc_component_update_bits(component, CS4265_ADC_CTL, + CS4265_ADC_MASTER, + CS4265_ADC_MASTER); + break; + case SND_SOC_DAIFMT_CBS_CFS: + snd_soc_component_update_bits(component, CS4265_ADC_CTL, + CS4265_ADC_MASTER, + 0); + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= SND_SOC_DAIFMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface |= SND_SOC_DAIFMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= SND_SOC_DAIFMT_LEFT_J; + break; + default: + return -EINVAL; + } + + cs4265->format = iface; + return 0; +} + +static int cs4265_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + if (mute) { + snd_soc_component_update_bits(component, CS4265_DAC_CTL, + CS4265_DAC_CTL_MUTE, + CS4265_DAC_CTL_MUTE); + snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, + CS4265_SPDIF_CTL2_MUTE, + CS4265_SPDIF_CTL2_MUTE); + } else { + snd_soc_component_update_bits(component, CS4265_DAC_CTL, + CS4265_DAC_CTL_MUTE, + 0); + snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, + CS4265_SPDIF_CTL2_MUTE, + 0); + } + return 0; +} + +static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs4265_private *cs4265 = snd_soc_component_get_drvdata(component); + int index; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + ((cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) + == SND_SOC_DAIFMT_RIGHT_J)) + return -EINVAL; + + index = cs4265_get_clk_index(cs4265->sysclk, params_rate(params)); + if (index >= 0) { + snd_soc_component_update_bits(component, CS4265_ADC_CTL, + CS4265_ADC_FM, clk_map_table[index].fm_mode << 6); + snd_soc_component_update_bits(component, CS4265_MCLK_FREQ, + CS4265_MCLK_FREQ_MASK, + clk_map_table[index].mclkdiv << 4); + + } else { + dev_err(component->dev, "can't get correct mclk\n"); + return -EINVAL; + } + + switch (cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + snd_soc_component_update_bits(component, CS4265_DAC_CTL, + CS4265_DAC_CTL_DIF, (1 << 4)); + snd_soc_component_update_bits(component, CS4265_ADC_CTL, + CS4265_ADC_DIF, (1 << 4)); + snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, + CS4265_SPDIF_CTL2_DIF, (1 << 6)); + break; + case SND_SOC_DAIFMT_RIGHT_J: + if (params_width(params) == 16) { + snd_soc_component_update_bits(component, CS4265_DAC_CTL, + CS4265_DAC_CTL_DIF, (2 << 4)); + snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, + CS4265_SPDIF_CTL2_DIF, (2 << 6)); + } else { + snd_soc_component_update_bits(component, CS4265_DAC_CTL, + CS4265_DAC_CTL_DIF, (3 << 4)); + snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, + CS4265_SPDIF_CTL2_DIF, (3 << 6)); + } + break; + case SND_SOC_DAIFMT_LEFT_J: + snd_soc_component_update_bits(component, CS4265_DAC_CTL, + CS4265_DAC_CTL_DIF, 0); + snd_soc_component_update_bits(component, CS4265_ADC_CTL, + CS4265_ADC_DIF, 0); + snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2, + CS4265_SPDIF_CTL2_DIF, 0); + + break; + default: + return -EINVAL; + } + return 0; +} + +static int cs4265_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, CS4265_PWRCTL, + CS4265_PWRCTL_PDN, 0); + break; + case SND_SOC_BIAS_STANDBY: + snd_soc_component_update_bits(component, CS4265_PWRCTL, + CS4265_PWRCTL_PDN, + CS4265_PWRCTL_PDN); + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, CS4265_PWRCTL, + CS4265_PWRCTL_PDN, + CS4265_PWRCTL_PDN); + break; + } + return 0; +} + +#define CS4265_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +#define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) + +static const struct snd_soc_dai_ops cs4265_ops = { + .hw_params = cs4265_pcm_hw_params, + .mute_stream = cs4265_mute, + .set_fmt = cs4265_set_fmt, + .set_sysclk = cs4265_set_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver cs4265_dai[] = { + { + .name = "cs4265-dai1", + .playback = { + .stream_name = "DAI1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS4265_RATES, + .formats = CS4265_FORMATS, + }, + .capture = { + .stream_name = "DAI1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS4265_RATES, + .formats = CS4265_FORMATS, + }, + .ops = &cs4265_ops, + }, + { + .name = "cs4265-dai2", + .playback = { + .stream_name = "DAI2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS4265_RATES, + .formats = CS4265_FORMATS, + }, + .capture = { + .stream_name = "DAI2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS4265_RATES, + .formats = CS4265_FORMATS, + }, + .ops = &cs4265_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_cs4265 = { + .set_bias_level = cs4265_set_bias_level, + .controls = cs4265_snd_controls, + .num_controls = ARRAY_SIZE(cs4265_snd_controls), + .dapm_widgets = cs4265_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets), + .dapm_routes = cs4265_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs4265_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config cs4265_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS4265_MAX_REGISTER, + .reg_defaults = cs4265_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults), + .readable_reg = cs4265_readable_register, + .volatile_reg = cs4265_volatile_register, + .cache_type = REGCACHE_MAPLE, +}; + +static int cs4265_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs4265_private *cs4265; + int ret; + unsigned int devid = 0; + unsigned int reg; + + cs4265 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4265_private), + GFP_KERNEL); + if (cs4265 == NULL) + return -ENOMEM; + + cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap); + if (IS_ERR(cs4265->regmap)) { + ret = PTR_ERR(cs4265->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + cs4265->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs4265->reset_gpio)) + return PTR_ERR(cs4265->reset_gpio); + + if (cs4265->reset_gpio) { + mdelay(1); + gpiod_set_value_cansleep(cs4265->reset_gpio, 1); + } + + i2c_set_clientdata(i2c_client, cs4265); + + ret = regmap_read(cs4265->regmap, CS4265_CHIP_ID, ®); + if (ret) { + dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret); + return ret; + } + + devid = reg & CS4265_CHIP_ID_MASK; + if (devid != CS4265_CHIP_ID_VAL) { + ret = -ENODEV; + dev_err(&i2c_client->dev, + "CS4265 Part Number ID: 0x%x Expected: 0x%x\n", + devid >> 4, CS4265_CHIP_ID_VAL >> 4); + return ret; + } + dev_info(&i2c_client->dev, + "CS4265 Version %x\n", + reg & CS4265_REV_ID_MASK); + + regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F); + + return devm_snd_soc_register_component(&i2c_client->dev, + &soc_component_cs4265, cs4265_dai, + ARRAY_SIZE(cs4265_dai)); +} + +static void cs4265_i2c_remove(struct i2c_client *i2c) +{ + struct cs4265_private *cs4265 = i2c_get_clientdata(i2c); + + if (cs4265->reset_gpio) + gpiod_set_value_cansleep(cs4265->reset_gpio, 0); +} + +static const struct of_device_id cs4265_of_match[] = { + { .compatible = "cirrus,cs4265", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs4265_of_match); + +static const struct i2c_device_id cs4265_id[] = { + { "cs4265", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs4265_id); + +static struct i2c_driver cs4265_i2c_driver = { + .driver = { + .name = "cs4265", + .of_match_table = cs4265_of_match, + }, + .id_table = cs4265_id, + .probe = cs4265_i2c_probe, + .remove = cs4265_i2c_remove, +}; + +module_i2c_driver(cs4265_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS4265 driver"); +MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4265.h b/sound/soc/codecs/cs4265.h new file mode 100644 index 0000000000..8bc28c2bf9 --- /dev/null +++ b/sound/soc/codecs/cs4265.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * cs4265.h -- CS4265 ALSA SoC audio driver + * + * Copyright 2014 Cirrus Logic, Inc. + * + * Author: Paul Handrigan + */ + +#ifndef __CS4265_H__ +#define __CS4265_H__ + +#define CS4265_CHIP_ID 0x1 +#define CS4265_CHIP_ID_VAL 0xD0 +#define CS4265_CHIP_ID_MASK 0xF0 +#define CS4265_REV_ID_MASK 0x0F + +#define CS4265_PWRCTL 0x02 +#define CS4265_PWRCTL_PDN 1 + +#define CS4265_DAC_CTL 0x3 +#define CS4265_DAC_CTL_MUTE (1 << 2) +#define CS4265_DAC_CTL_DIF (3 << 4) + +#define CS4265_ADC_CTL 0x4 +#define CS4265_ADC_MASTER 1 +#define CS4265_ADC_DIF (1 << 4) +#define CS4265_ADC_FM (3 << 6) + +#define CS4265_MCLK_FREQ 0x5 +#define CS4265_MCLK_FREQ_MASK (7 << 4) + +#define CS4265_SIG_SEL 0x6 +#define CS4265_SIG_SEL_LOOP (1 << 1) + +#define CS4265_CHB_PGA_CTL 0x7 +#define CS4265_CHA_PGA_CTL 0x8 + +#define CS4265_ADC_CTL2 0x9 + +#define CS4265_DAC_CHA_VOL 0xA +#define CS4265_DAC_CHB_VOL 0xB + +#define CS4265_DAC_CTL2 0xC + +#define CS4265_INT_STATUS 0xD +#define CS4265_INT_MASK 0xE +#define CS4265_STATUS_MODE_MSB 0xF +#define CS4265_STATUS_MODE_LSB 0x10 + +#define CS4265_SPDIF_CTL1 0x11 + +#define CS4265_SPDIF_CTL2 0x12 +#define CS4265_SPDIF_CTL2_MUTE (1 << 4) +#define CS4265_SPDIF_CTL2_DIF (3 << 6) + +#define CS4265_C_DATA_BUFF 0x13 +#define CS4265_MAX_REGISTER 0x2A + +#endif diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c new file mode 100644 index 0000000000..3df5672149 --- /dev/null +++ b/sound/soc/codecs/cs4270.c @@ -0,0 +1,762 @@ +/* + * CS4270 ALSA SoC (ASoC) codec driver + * + * Author: Timur Tabi + * + * Copyright 2007-2009 Freescale Semiconductor, Inc. This file is licensed + * under the terms of the GNU General Public License version 2. This + * program is licensed "as is" without any warranty of any kind, whether + * express or implied. + * + * This is an ASoC device driver for the Cirrus Logic CS4270 codec. + * + * Current features/limitations: + * + * - Software mode is supported. Stand-alone mode is not supported. + * - Only I2C is supported, not SPI + * - Support for master and slave mode + * - The machine driver's 'startup' function must call + * cs4270_set_dai_sysclk() with the value of MCLK. + * - Only I2S and left-justified modes are supported + * - Power management is supported + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) + +/* CS4270 registers addresses */ +#define CS4270_CHIPID 0x01 /* Chip ID */ +#define CS4270_PWRCTL 0x02 /* Power Control */ +#define CS4270_MODE 0x03 /* Mode Control */ +#define CS4270_FORMAT 0x04 /* Serial Format, ADC/DAC Control */ +#define CS4270_TRANS 0x05 /* Transition Control */ +#define CS4270_MUTE 0x06 /* Mute Control */ +#define CS4270_VOLA 0x07 /* DAC Channel A Volume Control */ +#define CS4270_VOLB 0x08 /* DAC Channel B Volume Control */ + +#define CS4270_FIRSTREG 0x01 +#define CS4270_LASTREG 0x08 +#define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1) +#define CS4270_I2C_INCR 0x80 + +/* Bit masks for the CS4270 registers */ +#define CS4270_CHIPID_ID 0xF0 +#define CS4270_CHIPID_REV 0x0F +#define CS4270_PWRCTL_FREEZE 0x80 +#define CS4270_PWRCTL_PDN_ADC 0x20 +#define CS4270_PWRCTL_PDN_DAC 0x02 +#define CS4270_PWRCTL_PDN 0x01 +#define CS4270_PWRCTL_PDN_ALL \ + (CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC | CS4270_PWRCTL_PDN) +#define CS4270_MODE_SPEED_MASK 0x30 +#define CS4270_MODE_1X 0x00 +#define CS4270_MODE_2X 0x10 +#define CS4270_MODE_4X 0x20 +#define CS4270_MODE_SLAVE 0x30 +#define CS4270_MODE_DIV_MASK 0x0E +#define CS4270_MODE_DIV1 0x00 +#define CS4270_MODE_DIV15 0x02 +#define CS4270_MODE_DIV2 0x04 +#define CS4270_MODE_DIV3 0x06 +#define CS4270_MODE_DIV4 0x08 +#define CS4270_MODE_POPGUARD 0x01 +#define CS4270_FORMAT_FREEZE_A 0x80 +#define CS4270_FORMAT_FREEZE_B 0x40 +#define CS4270_FORMAT_LOOPBACK 0x20 +#define CS4270_FORMAT_DAC_MASK 0x18 +#define CS4270_FORMAT_DAC_LJ 0x00 +#define CS4270_FORMAT_DAC_I2S 0x08 +#define CS4270_FORMAT_DAC_RJ16 0x18 +#define CS4270_FORMAT_DAC_RJ24 0x10 +#define CS4270_FORMAT_ADC_MASK 0x01 +#define CS4270_FORMAT_ADC_LJ 0x00 +#define CS4270_FORMAT_ADC_I2S 0x01 +#define CS4270_TRANS_ONE_VOL 0x80 +#define CS4270_TRANS_SOFT 0x40 +#define CS4270_TRANS_ZERO 0x20 +#define CS4270_TRANS_INV_ADC_A 0x08 +#define CS4270_TRANS_INV_ADC_B 0x10 +#define CS4270_TRANS_INV_DAC_A 0x02 +#define CS4270_TRANS_INV_DAC_B 0x04 +#define CS4270_TRANS_DEEMPH 0x01 +#define CS4270_MUTE_AUTO 0x20 +#define CS4270_MUTE_ADC_A 0x08 +#define CS4270_MUTE_ADC_B 0x10 +#define CS4270_MUTE_POLARITY 0x04 +#define CS4270_MUTE_DAC_A 0x01 +#define CS4270_MUTE_DAC_B 0x02 + +/* Power-on default values for the registers + * + * This array contains the power-on default values of the registers, with the + * exception of the "CHIPID" register (01h). The lower four bits of that + * register contain the hardware revision, so it is treated as volatile. + */ +static const struct reg_default cs4270_reg_defaults[] = { + { 2, 0x00 }, + { 3, 0x30 }, + { 4, 0x00 }, + { 5, 0x60 }, + { 6, 0x20 }, + { 7, 0x00 }, + { 8, 0x00 }, +}; + +static const char *supply_names[] = { + "va", "vd", "vlc" +}; + +/* Private data for the CS4270 */ +struct cs4270_private { + struct regmap *regmap; + unsigned int mclk; /* Input frequency of the MCLK pin */ + unsigned int mode; /* The mode (I2S or left-justified) */ + unsigned int slave_mode; + unsigned int manual_mute; + + /* power domain regulators */ + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; + + /* reset gpio */ + struct gpio_desc *reset_gpio; +}; + +static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), + +SND_SOC_DAPM_OUTPUT("AOUTL"), +SND_SOC_DAPM_OUTPUT("AOUTR"), +}; + +static const struct snd_soc_dapm_route cs4270_dapm_routes[] = { + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, + + { "AOUTL", NULL, "Playback" }, + { "AOUTR", NULL, "Playback" }, +}; + +/** + * struct cs4270_mode_ratios - clock ratio tables + * @ratio: the ratio of MCLK to the sample rate + * @speed_mode: the Speed Mode bits to set in the Mode Control register for + * this ratio + * @mclk: the Ratio Select bits to set in the Mode Control register for this + * ratio + * + * The data for this chart is taken from Table 5 of the CS4270 reference + * manual. + * + * This table is used to determine how to program the Mode Control register. + * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling + * rates the CS4270 currently supports. + * + * @speed_mode is the corresponding bit pattern to be written to the + * MODE bits of the Mode Control Register + * + * @mclk is the corresponding bit pattern to be wirten to the MCLK bits of + * the Mode Control Register. + * + * In situations where a single ratio is represented by multiple speed + * modes, we favor the slowest speed. E.g, for a ratio of 128, we pick + * double-speed instead of quad-speed. However, the CS4270 errata states + * that divide-By-1.5 can cause failures, so we avoid that mode where + * possible. + * + * Errata: There is an errata for the CS4270 where divide-by-1.5 does not + * work if Vd is 3.3V. If this effects you, select the + * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will + * never select any sample rates that require divide-by-1.5. + */ +struct cs4270_mode_ratios { + unsigned int ratio; + u8 speed_mode; + u8 mclk; +}; + +static struct cs4270_mode_ratios cs4270_mode_ratios[] = { + {64, CS4270_MODE_4X, CS4270_MODE_DIV1}, +#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA + {96, CS4270_MODE_4X, CS4270_MODE_DIV15}, +#endif + {128, CS4270_MODE_2X, CS4270_MODE_DIV1}, + {192, CS4270_MODE_4X, CS4270_MODE_DIV3}, + {256, CS4270_MODE_1X, CS4270_MODE_DIV1}, + {384, CS4270_MODE_2X, CS4270_MODE_DIV3}, + {512, CS4270_MODE_1X, CS4270_MODE_DIV2}, + {768, CS4270_MODE_1X, CS4270_MODE_DIV3}, + {1024, CS4270_MODE_1X, CS4270_MODE_DIV4} +}; + +/* The number of MCLK/LRCK ratios supported by the CS4270 */ +#define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios) + +static bool cs4270_reg_is_readable(struct device *dev, unsigned int reg) +{ + return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG); +} + +static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg) +{ + /* Unreadable registers are considered volatile */ + if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) + return true; + + return reg == CS4270_CHIPID; +} + +/** + * cs4270_set_dai_sysclk - determine the CS4270 samples rates. + * @codec_dai: the codec DAI + * @clk_id: the clock ID (ignored) + * @freq: the MCLK input frequency + * @dir: the clock direction (ignored) + * + * This function is used to tell the codec driver what the input MCLK + * frequency is. + * + * The value of MCLK is used to determine which sample rates are supported + * by the CS4270. The ratio of MCLK / Fs must be equal to one of nine + * supported values - 64, 96, 128, 192, 256, 384, 512, 768, and 1024. + * + * This function calculates the nine ratios and determines which ones match + * a standard sample rate. If there's a match, then it is added to the list + * of supported sample rates. + * + * This function must be called by the machine driver's 'startup' function, + * otherwise the list of supported sample rates will not be available in + * time for ALSA. + * + * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause + * theoretically possible sample rates to be enabled. Call it again with a + * proper value set one the external clock is set (most probably you would do + * that from a machine's driver 'hw_param' hook. + */ +static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component); + + cs4270->mclk = freq; + return 0; +} + +/** + * cs4270_set_dai_fmt - configure the codec for the selected audio format + * @codec_dai: the codec DAI + * @format: a SND_SOC_DAIFMT_x value indicating the data format + * + * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the + * codec accordingly. + * + * Currently, this function only supports SND_SOC_DAIFMT_I2S and + * SND_SOC_DAIFMT_LEFT_J. The CS4270 codec also supports right-justified + * data for playback only, but ASoC currently does not support different + * formats for playback vs. record. + */ +static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component); + + /* set DAI format */ + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + dev_err(component->dev, "invalid dai format\n"); + return -EINVAL; + } + + /* set master/slave audio interface */ + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs4270->slave_mode = 1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs4270->slave_mode = 0; + break; + default: + /* all other modes are unsupported by the hardware */ + dev_err(component->dev, "Unknown master/slave configuration\n"); + return -EINVAL; + } + + return 0; +} + +/** + * cs4270_hw_params - program the CS4270 with the given hardware parameters. + * @substream: the audio stream + * @params: the hardware parameters to set + * @dai: the SOC DAI (ignored) + * + * This function programs the hardware with the values provided. + * Specifically, the sample rate and the data format. + * + * The .ops functions are used to provide board-specific data, like input + * frequencies, to this driver. This function takes that information, + * combines it with the hardware parameters provided, and programs the + * hardware accordingly. + */ +static int cs4270_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component); + int ret; + unsigned int i; + unsigned int rate; + unsigned int ratio; + int reg; + + /* Figure out which MCLK/LRCK ratio to use */ + + rate = params_rate(params); /* Sampling rate, in Hz */ + ratio = cs4270->mclk / rate; /* MCLK/LRCK ratio */ + + for (i = 0; i < NUM_MCLK_RATIOS; i++) { + if (cs4270_mode_ratios[i].ratio == ratio) + break; + } + + if (i == NUM_MCLK_RATIOS) { + /* We did not find a matching ratio */ + dev_err(component->dev, "could not find matching ratio\n"); + return -EINVAL; + } + + /* Set the sample rate */ + + reg = snd_soc_component_read(component, CS4270_MODE); + reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK); + reg |= cs4270_mode_ratios[i].mclk; + + if (cs4270->slave_mode) + reg |= CS4270_MODE_SLAVE; + else + reg |= cs4270_mode_ratios[i].speed_mode; + + ret = snd_soc_component_write(component, CS4270_MODE, reg); + if (ret < 0) { + dev_err(component->dev, "i2c write failed\n"); + return ret; + } + + /* Set the DAI format */ + + reg = snd_soc_component_read(component, CS4270_FORMAT); + reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK); + + switch (cs4270->mode) { + case SND_SOC_DAIFMT_I2S: + reg |= CS4270_FORMAT_DAC_I2S | CS4270_FORMAT_ADC_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ; + break; + default: + dev_err(component->dev, "unknown dai format\n"); + return -EINVAL; + } + + ret = snd_soc_component_write(component, CS4270_FORMAT, reg); + if (ret < 0) { + dev_err(component->dev, "i2c write failed\n"); + return ret; + } + + return ret; +} + +/** + * cs4270_dai_mute - enable/disable the CS4270 external mute + * @dai: the SOC DAI + * @mute: 0 = disable mute, 1 = enable mute + * @direction: (ignored) + * + * This function toggles the mute bits in the MUTE register. The CS4270's + * mute capability is intended for external muting circuitry, so if the + * board does not have the MUTEA or MUTEB pins connected to such circuitry, + * then this function will do nothing. + */ +static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component); + int reg6; + + reg6 = snd_soc_component_read(component, CS4270_MUTE); + + if (mute) + reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B; + else { + reg6 &= ~(CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B); + reg6 |= cs4270->manual_mute; + } + + return snd_soc_component_write(component, CS4270_MUTE, reg6); +} + +/** + * cs4270_soc_put_mute - put callback for the 'Master Playback switch' + * alsa control. + * @kcontrol: mixer control + * @ucontrol: control element information + * + * This function basically passes the arguments on to the generic + * snd_soc_put_volsw() function and saves the mute information in + * our private data structure. This is because we want to prevent + * cs4270_dai_mute() neglecting the user's decision to manually + * mute the codec's output. + * + * Returns 0 for success. + */ +static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component); + int left = !ucontrol->value.integer.value[0]; + int right = !ucontrol->value.integer.value[1]; + + cs4270->manual_mute = (left ? CS4270_MUTE_DAC_A : 0) | + (right ? CS4270_MUTE_DAC_B : 0); + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +/* A list of non-DAPM controls that the CS4270 supports */ +static const struct snd_kcontrol_new cs4270_snd_controls[] = { + SOC_DOUBLE_R("Master Playback Volume", + CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1), + SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0), + SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0), + SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0), + SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0), + SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1), + SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0), + SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1), + SOC_DOUBLE_EXT("Master Playback Switch", CS4270_MUTE, 0, 1, 1, 1, + snd_soc_get_volsw, cs4270_soc_put_mute), +}; + +static const struct snd_soc_dai_ops cs4270_dai_ops = { + .hw_params = cs4270_hw_params, + .set_sysclk = cs4270_set_dai_sysclk, + .set_fmt = cs4270_set_dai_fmt, + .mute_stream = cs4270_dai_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver cs4270_dai = { + .name = "cs4270-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 4000, + .rate_max = 216000, + .formats = CS4270_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 4000, + .rate_max = 216000, + .formats = CS4270_FORMATS, + }, + .ops = &cs4270_dai_ops, +}; + +/** + * cs4270_probe - ASoC probe function + * @component: ASoC component + * + * This function is called when ASoC has all the pieces it needs to + * instantiate a sound driver. + */ +static int cs4270_probe(struct snd_soc_component *component) +{ + struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component); + int ret; + + /* Disable auto-mute. This feature appears to be buggy. In some + * situations, auto-mute will not deactivate when it should, so we want + * this feature disabled by default. An application (e.g. alsactl) can + * re-enabled it by using the controls. + */ + ret = snd_soc_component_update_bits(component, CS4270_MUTE, CS4270_MUTE_AUTO, 0); + if (ret < 0) { + dev_err(component->dev, "i2c write failed\n"); + return ret; + } + + /* Disable automatic volume control. The hardware enables, and it + * causes volume change commands to be delayed, sometimes until after + * playback has started. An application (e.g. alsactl) can + * re-enabled it by using the controls. + */ + ret = snd_soc_component_update_bits(component, CS4270_TRANS, + CS4270_TRANS_SOFT | CS4270_TRANS_ZERO, 0); + if (ret < 0) { + dev_err(component->dev, "i2c write failed\n"); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), + cs4270->supplies); + + return ret; +} + +/** + * cs4270_remove - ASoC remove function + * @component: ASoC component + * + * This function is the counterpart to cs4270_probe(). + */ +static void cs4270_remove(struct snd_soc_component *component) +{ + struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component); + + regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies); +}; + +#ifdef CONFIG_PM + +/* This suspend/resume implementation can handle both - a simple standby + * where the codec remains powered, and a full suspend, where the voltage + * domain the codec is connected to is teared down and/or any other hardware + * reset condition is asserted. + * + * The codec's own power saving features are enabled in the suspend callback, + * and all registers are written back to the hardware when resuming. + */ + +static int cs4270_soc_suspend(struct snd_soc_component *component) +{ + struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component); + int reg, ret; + + reg = snd_soc_component_read(component, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL; + if (reg < 0) + return reg; + + ret = snd_soc_component_write(component, CS4270_PWRCTL, reg); + if (ret < 0) + return ret; + + regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), + cs4270->supplies); + + return 0; +} + +static int cs4270_soc_resume(struct snd_soc_component *component) +{ + struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component); + int reg, ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), + cs4270->supplies); + if (ret != 0) + return ret; + + /* In case the device was put to hard reset during sleep, we need to + * wait 500ns here before any I2C communication. */ + ndelay(500); + + /* first restore the entire register cache ... */ + regcache_sync(cs4270->regmap); + + /* ... then disable the power-down bits */ + reg = snd_soc_component_read(component, CS4270_PWRCTL); + reg &= ~CS4270_PWRCTL_PDN_ALL; + + return snd_soc_component_write(component, CS4270_PWRCTL, reg); +} +#else +#define cs4270_soc_suspend NULL +#define cs4270_soc_resume NULL +#endif /* CONFIG_PM */ + +/* + * ASoC codec driver structure + */ +static const struct snd_soc_component_driver soc_component_device_cs4270 = { + .probe = cs4270_probe, + .remove = cs4270_remove, + .suspend = cs4270_soc_suspend, + .resume = cs4270_soc_resume, + .controls = cs4270_snd_controls, + .num_controls = ARRAY_SIZE(cs4270_snd_controls), + .dapm_widgets = cs4270_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets), + .dapm_routes = cs4270_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +/* + * cs4270_of_match - the device tree bindings + */ +static const struct of_device_id cs4270_of_match[] = { + { .compatible = "cirrus,cs4270", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs4270_of_match); + +static const struct regmap_config cs4270_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = CS4270_LASTREG, + .reg_defaults = cs4270_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .write_flag_mask = CS4270_I2C_INCR, + + .readable_reg = cs4270_reg_is_readable, + .volatile_reg = cs4270_reg_is_volatile, +}; + +/** + * cs4270_i2c_remove - deinitialize the I2C interface of the CS4270 + * @i2c_client: the I2C client object + * + * This function puts the chip into low power mode when the i2c device + * is removed. + */ +static void cs4270_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs4270_private *cs4270 = i2c_get_clientdata(i2c_client); + + gpiod_set_value_cansleep(cs4270->reset_gpio, 0); +} + +/** + * cs4270_i2c_probe - initialize the I2C interface of the CS4270 + * @i2c_client: the I2C client object + * + * This function is called whenever the I2C subsystem finds a device that + * matches the device ID given via a prior call to i2c_add_driver(). + */ +static int cs4270_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs4270_private *cs4270; + unsigned int val; + int ret, i; + + cs4270 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4270_private), + GFP_KERNEL); + if (!cs4270) + return -ENOMEM; + + /* get the power supply regulators */ + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + cs4270->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c_client->dev, + ARRAY_SIZE(cs4270->supplies), + cs4270->supplies); + if (ret < 0) + return ret; + + /* reset the device */ + cs4270->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs4270->reset_gpio)) { + dev_dbg(&i2c_client->dev, "Error getting CS4270 reset GPIO\n"); + return PTR_ERR(cs4270->reset_gpio); + } + + if (cs4270->reset_gpio) { + dev_dbg(&i2c_client->dev, "Found reset GPIO\n"); + gpiod_set_value_cansleep(cs4270->reset_gpio, 1); + } + + /* Sleep 500ns before i2c communications */ + ndelay(500); + + cs4270->regmap = devm_regmap_init_i2c(i2c_client, &cs4270_regmap); + if (IS_ERR(cs4270->regmap)) + return PTR_ERR(cs4270->regmap); + + /* Verify that we have a CS4270 */ + ret = regmap_read(cs4270->regmap, CS4270_CHIPID, &val); + if (ret < 0) { + dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n", + i2c_client->addr); + return ret; + } + /* The top four bits of the chip ID should be 1100. */ + if ((val & 0xF0) != 0xC0) { + dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n", + i2c_client->addr); + return -ENODEV; + } + + dev_info(&i2c_client->dev, "found device at i2c address %X\n", + i2c_client->addr); + dev_info(&i2c_client->dev, "hardware revision %X\n", val & 0xF); + + i2c_set_clientdata(i2c_client, cs4270); + + ret = devm_snd_soc_register_component(&i2c_client->dev, + &soc_component_device_cs4270, &cs4270_dai, 1); + return ret; +} + +/* + * cs4270_id - I2C device IDs supported by this driver + */ +static const struct i2c_device_id cs4270_id[] = { + {"cs4270", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs4270_id); + +/* + * cs4270_i2c_driver - I2C device identification + * + * This structure tells the I2C subsystem how to identify and support a + * given I2C device type. + */ +static struct i2c_driver cs4270_i2c_driver = { + .driver = { + .name = "cs4270", + .of_match_table = cs4270_of_match, + }, + .id_table = cs4270_id, + .probe = cs4270_i2c_probe, + .remove = cs4270_i2c_remove, +}; + +module_i2c_driver(cs4270_i2c_driver); + +MODULE_AUTHOR("Timur Tabi "); +MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c new file mode 100644 index 0000000000..89fe7d1665 --- /dev/null +++ b/sound/soc/codecs/cs4271-i2c.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * CS4271 I2C audio driver + * + * Copyright (c) 2010 Alexander Sverdlin + */ + +#include +#include +#include +#include +#include "cs4271.h" + +static int cs4271_i2c_probe(struct i2c_client *client) +{ + struct regmap_config config; + + config = cs4271_regmap_config; + config.reg_bits = 8; + + return cs4271_probe(&client->dev, + devm_regmap_init_i2c(client, &config)); +} + +static const struct i2c_device_id cs4271_i2c_id[] = { + { "cs4271", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); + +static struct i2c_driver cs4271_i2c_driver = { + .driver = { + .name = "cs4271", + .of_match_table = of_match_ptr(cs4271_dt_ids), + }, + .probe = cs4271_i2c_probe, + .id_table = cs4271_i2c_id, +}; +module_i2c_driver(cs4271_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS4271 I2C Driver"); +MODULE_AUTHOR("Alexander Sverdlin "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c new file mode 100644 index 0000000000..4feb80436b --- /dev/null +++ b/sound/soc/codecs/cs4271-spi.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * CS4271 SPI audio driver + * + * Copyright (c) 2010 Alexander Sverdlin + */ + +#include +#include +#include +#include +#include "cs4271.h" + +static int cs4271_spi_probe(struct spi_device *spi) +{ + struct regmap_config config; + + config = cs4271_regmap_config; + config.reg_bits = 16; + config.read_flag_mask = 0x21; + config.write_flag_mask = 0x20; + + return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config)); +} + +static struct spi_driver cs4271_spi_driver = { + .driver = { + .name = "cs4271", + .of_match_table = of_match_ptr(cs4271_dt_ids), + }, + .probe = cs4271_spi_probe, +}; +module_spi_driver(cs4271_spi_driver); + +MODULE_DESCRIPTION("ASoC CS4271 SPI Driver"); +MODULE_AUTHOR("Alexander Sverdlin "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c new file mode 100644 index 0000000000..188b8b43c5 --- /dev/null +++ b/sound/soc/codecs/cs4271.c @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * CS4271 ASoC codec driver + * + * Copyright (c) 2010 Alexander Sverdlin + * + * This driver support CS4271 codec being master or slave, working + * in control port mode, connected either via SPI or I2C. + * The data format accepted is I2S or left-justified. + * DAPM support not implemented. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs4271.h" + +#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000 + +/* + * CS4271 registers + */ +#define CS4271_MODE1 0x01 /* Mode Control 1 */ +#define CS4271_DACCTL 0x02 /* DAC Control */ +#define CS4271_DACVOL 0x03 /* DAC Volume & Mixing Control */ +#define CS4271_VOLA 0x04 /* DAC Channel A Volume Control */ +#define CS4271_VOLB 0x05 /* DAC Channel B Volume Control */ +#define CS4271_ADCCTL 0x06 /* ADC Control */ +#define CS4271_MODE2 0x07 /* Mode Control 2 */ +#define CS4271_CHIPID 0x08 /* Chip ID */ + +#define CS4271_FIRSTREG CS4271_MODE1 +#define CS4271_LASTREG CS4271_MODE2 +#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1) + +/* Bit masks for the CS4271 registers */ +#define CS4271_MODE1_MODE_MASK 0xC0 +#define CS4271_MODE1_MODE_1X 0x00 +#define CS4271_MODE1_MODE_2X 0x80 +#define CS4271_MODE1_MODE_4X 0xC0 + +#define CS4271_MODE1_DIV_MASK 0x30 +#define CS4271_MODE1_DIV_1 0x00 +#define CS4271_MODE1_DIV_15 0x10 +#define CS4271_MODE1_DIV_2 0x20 +#define CS4271_MODE1_DIV_3 0x30 + +#define CS4271_MODE1_MASTER 0x08 + +#define CS4271_MODE1_DAC_DIF_MASK 0x07 +#define CS4271_MODE1_DAC_DIF_LJ 0x00 +#define CS4271_MODE1_DAC_DIF_I2S 0x01 +#define CS4271_MODE1_DAC_DIF_RJ16 0x02 +#define CS4271_MODE1_DAC_DIF_RJ24 0x03 +#define CS4271_MODE1_DAC_DIF_RJ20 0x04 +#define CS4271_MODE1_DAC_DIF_RJ18 0x05 + +#define CS4271_DACCTL_AMUTE 0x80 +#define CS4271_DACCTL_IF_SLOW 0x40 + +#define CS4271_DACCTL_DEM_MASK 0x30 +#define CS4271_DACCTL_DEM_DIS 0x00 +#define CS4271_DACCTL_DEM_441 0x10 +#define CS4271_DACCTL_DEM_48 0x20 +#define CS4271_DACCTL_DEM_32 0x30 + +#define CS4271_DACCTL_SVRU 0x08 +#define CS4271_DACCTL_SRD 0x04 +#define CS4271_DACCTL_INVA 0x02 +#define CS4271_DACCTL_INVB 0x01 + +#define CS4271_DACVOL_BEQUA 0x40 +#define CS4271_DACVOL_SOFT 0x20 +#define CS4271_DACVOL_ZEROC 0x10 + +#define CS4271_DACVOL_ATAPI_MASK 0x0F +#define CS4271_DACVOL_ATAPI_M_M 0x00 +#define CS4271_DACVOL_ATAPI_M_BR 0x01 +#define CS4271_DACVOL_ATAPI_M_BL 0x02 +#define CS4271_DACVOL_ATAPI_M_BLR2 0x03 +#define CS4271_DACVOL_ATAPI_AR_M 0x04 +#define CS4271_DACVOL_ATAPI_AR_BR 0x05 +#define CS4271_DACVOL_ATAPI_AR_BL 0x06 +#define CS4271_DACVOL_ATAPI_AR_BLR2 0x07 +#define CS4271_DACVOL_ATAPI_AL_M 0x08 +#define CS4271_DACVOL_ATAPI_AL_BR 0x09 +#define CS4271_DACVOL_ATAPI_AL_BL 0x0A +#define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B +#define CS4271_DACVOL_ATAPI_ALR2_M 0x0C +#define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D +#define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E +#define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F + +#define CS4271_VOLA_MUTE 0x80 +#define CS4271_VOLA_VOL_MASK 0x7F +#define CS4271_VOLB_MUTE 0x80 +#define CS4271_VOLB_VOL_MASK 0x7F + +#define CS4271_ADCCTL_DITHER16 0x20 + +#define CS4271_ADCCTL_ADC_DIF_MASK 0x10 +#define CS4271_ADCCTL_ADC_DIF_LJ 0x00 +#define CS4271_ADCCTL_ADC_DIF_I2S 0x10 + +#define CS4271_ADCCTL_MUTEA 0x08 +#define CS4271_ADCCTL_MUTEB 0x04 +#define CS4271_ADCCTL_HPFDA 0x02 +#define CS4271_ADCCTL_HPFDB 0x01 + +#define CS4271_MODE2_LOOP 0x10 +#define CS4271_MODE2_MUTECAEQUB 0x08 +#define CS4271_MODE2_FREEZE 0x04 +#define CS4271_MODE2_CPEN 0x02 +#define CS4271_MODE2_PDN 0x01 + +#define CS4271_CHIPID_PART_MASK 0xF0 +#define CS4271_CHIPID_REV_MASK 0x0F + +/* + * Default CS4271 power-up configuration + * Array contains non-existing in hw register at address 0 + * Array do not include Chip ID, as codec driver does not use + * registers read operations at all + */ +static const struct reg_default cs4271_reg_defaults[] = { + { CS4271_MODE1, 0, }, + { CS4271_DACCTL, CS4271_DACCTL_AMUTE, }, + { CS4271_DACVOL, CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR, }, + { CS4271_VOLA, 0, }, + { CS4271_VOLB, 0, }, + { CS4271_ADCCTL, 0, }, + { CS4271_MODE2, 0, }, +}; + +static bool cs4271_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == CS4271_CHIPID; +} + +static const char * const supply_names[] = { + "vd", "vl", "va" +}; + +struct cs4271_private { + unsigned int mclk; + bool master; + bool deemph; + struct regmap *regmap; + /* Current sample rate for de-emphasis control */ + int rate; + /* GPIO driving Reset pin, if any */ + int gpio_nreset; + /* GPIO that disable serial bus, if any */ + int gpio_disable; + /* enable soft reset workaround */ + bool enable_soft_reset; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; +}; + +static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINA"), +SND_SOC_DAPM_INPUT("AINB"), + +SND_SOC_DAPM_OUTPUT("AOUTA+"), +SND_SOC_DAPM_OUTPUT("AOUTA-"), +SND_SOC_DAPM_OUTPUT("AOUTB+"), +SND_SOC_DAPM_OUTPUT("AOUTB-"), +}; + +static const struct snd_soc_dapm_route cs4271_dapm_routes[] = { + { "Capture", NULL, "AINA" }, + { "Capture", NULL, "AINB" }, + + { "AOUTA+", NULL, "Playback" }, + { "AOUTA-", NULL, "Playback" }, + { "AOUTB+", NULL, "Playback" }, + { "AOUTB-", NULL, "Playback" }, +}; + +/* + * @freq is the desired MCLK rate + * MCLK rate should (c) be the sample rate, multiplied by one of the + * ratios listed in cs4271_mclk_fs_ratios table + */ +static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + + cs4271->mclk = freq; + return 0; +} + +static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + unsigned int val = 0; + int ret; + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs4271->master = false; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs4271->master = true; + val |= CS4271_MODE1_MASTER; + break; + default: + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + val |= CS4271_MODE1_DAC_DIF_LJ; + ret = regmap_update_bits(cs4271->regmap, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ); + if (ret < 0) + return ret; + break; + case SND_SOC_DAIFMT_I2S: + val |= CS4271_MODE1_DAC_DIF_I2S; + ret = regmap_update_bits(cs4271->regmap, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S); + if (ret < 0) + return ret; + break; + default: + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + ret = regmap_update_bits(cs4271->regmap, CS4271_MODE1, + CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_deemph[] = {0, 44100, 48000, 32000}; + +static int cs4271_set_deemph(struct snd_soc_component *component) +{ + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + int i, ret; + int val = CS4271_DACCTL_DEM_DIS; + + if (cs4271->deemph) { + /* Find closest de-emphasis freq */ + val = 1; + for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++) + if (abs(cs4271_deemph[i] - cs4271->rate) < + abs(cs4271_deemph[val] - cs4271->rate)) + val = i; + val <<= 4; + } + + ret = regmap_update_bits(cs4271->regmap, CS4271_DACCTL, + CS4271_DACCTL_DEM_MASK, val); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = cs4271->deemph; + return 0; +} + +static int cs4271_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + + cs4271->deemph = ucontrol->value.integer.value[0]; + return cs4271_set_deemph(component); +} + +struct cs4271_clk_cfg { + bool master; /* codec mode */ + u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */ + unsigned short ratio; /* MCLK / sample rate */ + u8 ratio_mask; /* ratio bit mask for Master mode */ +}; + +static struct cs4271_clk_cfg cs4271_clk_tab[] = { + {1, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_3}, + {1, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_3}, + {1, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_3}, + {0, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_2X, 512, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2}, +}; + +#define CS4271_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab) + +static int cs4271_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + int i, ret; + unsigned int ratio, val; + + if (cs4271->enable_soft_reset) { + /* + * Put the codec in soft reset and back again in case it's not + * currently streaming data. This way of bringing the codec in + * sync to the current clocks is not explicitly documented in + * the data sheet, but it seems to work fine, and in contrast + * to a read hardware reset, we don't have to sync back all + * registers every time. + */ + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) || + (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + !snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK))) { + ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, + CS4271_MODE2_PDN, + CS4271_MODE2_PDN); + if (ret < 0) + return ret; + + ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, + CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + } + } + + cs4271->rate = params_rate(params); + + /* Configure DAC */ + if (cs4271->rate < 50000) + val = CS4271_MODE1_MODE_1X; + else if (cs4271->rate < 100000) + val = CS4271_MODE1_MODE_2X; + else + val = CS4271_MODE1_MODE_4X; + + ratio = cs4271->mclk / cs4271->rate; + for (i = 0; i < CS4271_NR_RATIOS; i++) + if ((cs4271_clk_tab[i].master == cs4271->master) && + (cs4271_clk_tab[i].speed_mode == val) && + (cs4271_clk_tab[i].ratio == ratio)) + break; + + if (i == CS4271_NR_RATIOS) { + dev_err(component->dev, "Invalid sample rate\n"); + return -EINVAL; + } + + val |= cs4271_clk_tab[i].ratio_mask; + + ret = regmap_update_bits(cs4271->regmap, CS4271_MODE1, + CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val); + if (ret < 0) + return ret; + + return cs4271_set_deemph(component); +} + +static int cs4271_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + int ret; + int val_a = 0; + int val_b = 0; + + if (stream != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + + if (mute) { + val_a = CS4271_VOLA_MUTE; + val_b = CS4271_VOLB_MUTE; + } + + ret = regmap_update_bits(cs4271->regmap, CS4271_VOLA, + CS4271_VOLA_MUTE, val_a); + if (ret < 0) + return ret; + + ret = regmap_update_bits(cs4271->regmap, CS4271_VOLB, + CS4271_VOLB_MUTE, val_b); + if (ret < 0) + return ret; + + return 0; +} + +/* CS4271 controls */ +static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0); + +static const struct snd_kcontrol_new cs4271_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB, + 0, 0x7F, 1, cs4271_dac_tlv), + SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0), + SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0), + SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + cs4271_get_deemph, cs4271_put_deemph), + SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0), + SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0), + SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0), + SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0), + SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0), + SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0), + SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1), + SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0), + SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1), + SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB, + 7, 1, 1), +}; + +static const struct snd_soc_dai_ops cs4271_dai_ops = { + .hw_params = cs4271_hw_params, + .set_sysclk = cs4271_set_dai_sysclk, + .set_fmt = cs4271_set_dai_fmt, + .mute_stream = cs4271_mute_stream, +}; + +static struct snd_soc_dai_driver cs4271_dai = { + .name = "cs4271-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = CS4271_PCM_RATES, + .formats = CS4271_PCM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = CS4271_PCM_RATES, + .formats = CS4271_PCM_FORMATS, + }, + .ops = &cs4271_dai_ops, + .symmetric_rate = 1, +}; + +static int cs4271_reset(struct snd_soc_component *component) +{ + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + + if (gpio_is_valid(cs4271->gpio_nreset)) { + gpio_direction_output(cs4271->gpio_nreset, 0); + mdelay(1); + gpio_set_value(cs4271->gpio_nreset, 1); + mdelay(1); + } + + return 0; +} + +#ifdef CONFIG_PM +static int cs4271_soc_suspend(struct snd_soc_component *component) +{ + int ret; + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + + /* Set power-down bit */ + ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, + CS4271_MODE2_PDN, CS4271_MODE2_PDN); + if (ret < 0) + return ret; + + regcache_mark_dirty(cs4271->regmap); + regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies); + + return 0; +} + +static int cs4271_soc_resume(struct snd_soc_component *component) +{ + int ret; + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + + ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies), + cs4271->supplies); + if (ret < 0) { + dev_err(component->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + /* Do a proper reset after power up */ + cs4271_reset(component); + + /* Restore codec state */ + ret = regcache_sync(cs4271->regmap); + if (ret < 0) + return ret; + + /* then disable the power-down bit */ + ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, + CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + + return 0; +} +#else +#define cs4271_soc_suspend NULL +#define cs4271_soc_resume NULL +#endif /* CONFIG_PM */ + +#ifdef CONFIG_OF +const struct of_device_id cs4271_dt_ids[] = { + { .compatible = "cirrus,cs4271", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs4271_dt_ids); +EXPORT_SYMBOL_GPL(cs4271_dt_ids); +#endif + +static int cs4271_component_probe(struct snd_soc_component *component) +{ + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + struct cs4271_platform_data *cs4271plat = component->dev->platform_data; + int ret; + bool amutec_eq_bmutec = false; + +#ifdef CONFIG_OF + if (of_match_device(cs4271_dt_ids, component->dev)) { + if (of_get_property(component->dev->of_node, + "cirrus,amutec-eq-bmutec", NULL)) + amutec_eq_bmutec = true; + + if (of_get_property(component->dev->of_node, + "cirrus,enable-soft-reset", NULL)) + cs4271->enable_soft_reset = true; + } +#endif + + ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies), + cs4271->supplies); + if (ret < 0) { + dev_err(component->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + if (cs4271plat) { + amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; + cs4271->enable_soft_reset = cs4271plat->enable_soft_reset; + } + + /* Reset codec */ + cs4271_reset(component); + + ret = regcache_sync(cs4271->regmap); + if (ret < 0) + return ret; + + ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, + CS4271_MODE2_PDN | CS4271_MODE2_CPEN, + CS4271_MODE2_PDN | CS4271_MODE2_CPEN); + if (ret < 0) + return ret; + ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, + CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + /* Power-up sequence requires 85 uS */ + udelay(85); + + if (amutec_eq_bmutec) + regmap_update_bits(cs4271->regmap, CS4271_MODE2, + CS4271_MODE2_MUTECAEQUB, + CS4271_MODE2_MUTECAEQUB); + + return 0; +} + +static void cs4271_component_remove(struct snd_soc_component *component) +{ + struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component); + + if (gpio_is_valid(cs4271->gpio_nreset)) + /* Set codec to the reset state */ + gpio_set_value(cs4271->gpio_nreset, 0); + + regcache_mark_dirty(cs4271->regmap); + regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies); +}; + +static const struct snd_soc_component_driver soc_component_dev_cs4271 = { + .probe = cs4271_component_probe, + .remove = cs4271_component_remove, + .suspend = cs4271_soc_suspend, + .resume = cs4271_soc_resume, + .controls = cs4271_snd_controls, + .num_controls = ARRAY_SIZE(cs4271_snd_controls), + .dapm_widgets = cs4271_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets), + .dapm_routes = cs4271_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cs4271_common_probe(struct device *dev, + struct cs4271_private **c) +{ + struct cs4271_platform_data *cs4271plat = dev->platform_data; + struct cs4271_private *cs4271; + int i, ret; + + cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + if (of_match_device(cs4271_dt_ids, dev)) + cs4271->gpio_nreset = + of_get_named_gpio(dev->of_node, "reset-gpio", 0); + + if (cs4271plat) + cs4271->gpio_nreset = cs4271plat->gpio_nreset; + + if (gpio_is_valid(cs4271->gpio_nreset)) { + ret = devm_gpio_request(dev, cs4271->gpio_nreset, + "CS4271 Reset"); + if (ret < 0) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + cs4271->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs4271->supplies), + cs4271->supplies); + + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + *c = cs4271; + return 0; +} + +const struct regmap_config cs4271_regmap_config = { + .max_register = CS4271_LASTREG, + + .reg_defaults = cs4271_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults), + .cache_type = REGCACHE_FLAT, + .val_bits = 8, + .volatile_reg = cs4271_volatile_reg, +}; +EXPORT_SYMBOL_GPL(cs4271_regmap_config); + +int cs4271_probe(struct device *dev, struct regmap *regmap) +{ + struct cs4271_private *cs4271; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = cs4271_common_probe(dev, &cs4271); + if (ret < 0) + return ret; + + dev_set_drvdata(dev, cs4271); + cs4271->regmap = regmap; + + return devm_snd_soc_register_component(dev, &soc_component_dev_cs4271, + &cs4271_dai, 1); +} +EXPORT_SYMBOL_GPL(cs4271_probe); + +MODULE_AUTHOR("Alexander Sverdlin "); +MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h new file mode 100644 index 0000000000..290283a914 --- /dev/null +++ b/sound/soc/codecs/cs4271.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _CS4271_PRIV_H +#define _CS4271_PRIV_H + +#include + +extern const struct of_device_id cs4271_dt_ids[]; +extern const struct regmap_config cs4271_regmap_config; + +int cs4271_probe(struct device *dev, struct regmap *regmap); + +#endif diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c new file mode 100644 index 0000000000..2552a1e6b8 --- /dev/null +++ b/sound/soc/codecs/cs42l42-i2c.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l42-i2c.c -- CS42L42 ALSA SoC audio driver for I2C + * + * Copyright 2016, 2022 Cirrus Logic, Inc. + */ + +#include +#include +#include +#include +#include + +#include "cs42l42.h" + +static int cs42l42_i2c_probe(struct i2c_client *i2c_client) +{ + struct device *dev = &i2c_client->dev; + struct cs42l42_private *cs42l42; + struct regmap *regmap; + int ret; + + cs42l42 = devm_kzalloc(dev, sizeof(*cs42l42), GFP_KERNEL); + if (!cs42l42) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap); + if (IS_ERR(regmap)) + return dev_err_probe(&i2c_client->dev, PTR_ERR(regmap), + "regmap_init() failed\n"); + + cs42l42->devid = CS42L42_CHIP_ID; + cs42l42->dev = dev; + cs42l42->regmap = regmap; + cs42l42->irq = i2c_client->irq; + + ret = cs42l42_common_probe(cs42l42, &cs42l42_soc_component, &cs42l42_dai); + if (ret) + return ret; + + return cs42l42_init(cs42l42); +} + +static void cs42l42_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&i2c_client->dev); + + cs42l42_common_remove(cs42l42); +} + +static int __maybe_unused cs42l42_i2c_resume(struct device *dev) +{ + int ret; + + ret = cs42l42_resume(dev); + if (ret) + return ret; + + cs42l42_resume_restore(dev); + + return 0; +} + +static const struct dev_pm_ops cs42l42_i2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_i2c_resume) +}; + +static const struct of_device_id __maybe_unused cs42l42_of_match[] = { + { .compatible = "cirrus,cs42l42", }, + {} +}; +MODULE_DEVICE_TABLE(of, cs42l42_of_match); + +static const struct acpi_device_id __maybe_unused cs42l42_acpi_match[] = { + {"10134242", 0,}, + {} +}; +MODULE_DEVICE_TABLE(acpi, cs42l42_acpi_match); + +static const struct i2c_device_id cs42l42_id[] = { + {"cs42l42", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs42l42_id); + +static struct i2c_driver cs42l42_i2c_driver = { + .driver = { + .name = "cs42l42", + .pm = &cs42l42_i2c_pm_ops, + .of_match_table = of_match_ptr(cs42l42_of_match), + .acpi_match_table = ACPI_PTR(cs42l42_acpi_match), + }, + .id_table = cs42l42_id, + .probe = cs42l42_i2c_probe, + .remove = cs42l42_i2c_remove, +}; + +module_i2c_driver(cs42l42_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L42 I2C driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_CS42L42_CORE); diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c new file mode 100644 index 0000000000..94a66a3253 --- /dev/null +++ b/sound/soc/codecs/cs42l42-sdw.c @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-2.0-only +// cs42l42-sdw.c -- CS42L42 ALSA SoC audio driver SoundWire driver +// +// Copyright (C) 2022 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs42l42.h" + +#define CS42L42_SDW_CAPTURE_PORT 1 +#define CS42L42_SDW_PLAYBACK_PORT 2 + +/* Register addresses are offset when sent over SoundWire */ +#define CS42L42_SDW_ADDR_OFFSET 0x8000 + +#define CS42L42_SDW_MEM_ACCESS_STATUS 0xd0 +#define CS42L42_SDW_MEM_READ_DATA 0xd8 + +#define CS42L42_SDW_LAST_LATE BIT(3) +#define CS42L42_SDW_CMD_IN_PROGRESS BIT(2) +#define CS42L42_SDW_RDATA_RDY BIT(0) + +#define CS42L42_DELAYED_READ_POLL_US 1 +#define CS42L42_DELAYED_READ_TIMEOUT_US 100 + +static const struct snd_soc_dapm_route cs42l42_sdw_audio_map[] = { + /* Playback Path */ + { "HP", NULL, "MIXER" }, + { "MIXER", NULL, "DACSRC" }, + { "DACSRC", NULL, "Playback" }, + + /* Capture Path */ + { "ADCSRC", NULL, "HS" }, + { "Capture", NULL, "ADCSRC" }, +}; + +static int cs42l42_sdw_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component); + + if (!cs42l42->init_done) + return -ENODEV; + + return 0; +} + +static int cs42l42_sdw_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + int ret; + + if (!sdw_stream) + return -EINVAL; + + /* Needed for PLL configuration when we are notified of new bus config */ + cs42l42->sample_rate = params_rate(params); + + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_config.num = CS42L42_SDW_PLAYBACK_PORT; + else + port_config.num = CS42L42_SDW_CAPTURE_PORT; + + ret = sdw_stream_add_slave(cs42l42->sdw_peripheral, &stream_config, &port_config, 1, + sdw_stream); + if (ret) { + dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret); + return ret; + } + + cs42l42_src_config(dai->component, params_rate(params)); + + return 0; +} + +static int cs42l42_sdw_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component); + + dev_dbg(dai->dev, "dai_prepare: sclk=%u rate=%u\n", cs42l42->sclk, cs42l42->sample_rate); + + if (!cs42l42->sclk || !cs42l42->sample_rate) + return -EINVAL; + + /* + * At this point we know the sample rate from hw_params, and the SWIRE_CLK from bus_config() + * callback. This could only fail if the ACPI or machine driver are misconfigured to allow + * an unsupported SWIRE_CLK and sample_rate combination. + */ + + return cs42l42_pll_config(dai->component, cs42l42->sclk, cs42l42->sample_rate); +} + +static int cs42l42_sdw_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + sdw_stream_remove_slave(cs42l42->sdw_peripheral, sdw_stream); + cs42l42->sample_rate = 0; + + return 0; +} + +static int cs42l42_sdw_port_prep(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops state) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&slave->dev); + unsigned int pdn_mask; + + if (prepare_ch->num == CS42L42_SDW_PLAYBACK_PORT) + pdn_mask = CS42L42_HP_PDN_MASK; + else + pdn_mask = CS42L42_ADC_PDN_MASK; + + if (state == SDW_OPS_PORT_PRE_PREP) { + dev_dbg(cs42l42->dev, "Prep Port pdn_mask:%x\n", pdn_mask); + regmap_clear_bits(cs42l42->regmap, CS42L42_PWR_CTL1, pdn_mask); + usleep_range(CS42L42_HP_ADC_EN_TIME_US, CS42L42_HP_ADC_EN_TIME_US + 1000); + } else if (state == SDW_OPS_PORT_POST_DEPREP) { + dev_dbg(cs42l42->dev, "Deprep Port pdn_mask:%x\n", pdn_mask); + regmap_set_bits(cs42l42->regmap, CS42L42_PWR_CTL1, pdn_mask); + } + + return 0; +} + +static int cs42l42_sdw_dai_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void cs42l42_sdw_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static const struct snd_soc_dai_ops cs42l42_sdw_dai_ops = { + .startup = cs42l42_sdw_dai_startup, + .shutdown = cs42l42_sdw_dai_shutdown, + .hw_params = cs42l42_sdw_dai_hw_params, + .prepare = cs42l42_sdw_dai_prepare, + .hw_free = cs42l42_sdw_dai_hw_free, + .mute_stream = cs42l42_mute_stream, + .set_stream = cs42l42_sdw_dai_set_sdw_stream, +}; + +static struct snd_soc_dai_driver cs42l42_sdw_dai = { + .name = "cs42l42-sdw", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + /* Restrict which rates and formats are supported */ + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + /* Restrict which rates and formats are supported */ + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .symmetric_rate = 1, + .ops = &cs42l42_sdw_dai_ops, +}; + +static int cs42l42_sdw_poll_status(struct sdw_slave *peripheral, u8 mask, u8 match) +{ + int ret, sdwret; + + ret = read_poll_timeout(sdw_read_no_pm, sdwret, + (sdwret < 0) || ((sdwret & mask) == match), + CS42L42_DELAYED_READ_POLL_US, CS42L42_DELAYED_READ_TIMEOUT_US, + false, peripheral, CS42L42_SDW_MEM_ACCESS_STATUS); + if (ret == 0) + ret = sdwret; + + if (ret < 0) + dev_err(&peripheral->dev, "MEM_ACCESS_STATUS & %#x for %#x fail: %d\n", + mask, match, ret); + + return ret; +} + +static int cs42l42_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct sdw_slave *peripheral = context; + u8 data; + int ret; + + reg += CS42L42_SDW_ADDR_OFFSET; + + ret = cs42l42_sdw_poll_status(peripheral, CS42L42_SDW_CMD_IN_PROGRESS, 0); + if (ret < 0) + return ret; + + ret = sdw_read_no_pm(peripheral, reg); + if (ret < 0) { + dev_err(&peripheral->dev, "Failed to issue read @0x%x: %d\n", reg, ret); + return ret; + } + + data = (u8)ret; /* possible non-delayed read value */ + ret = sdw_read_no_pm(peripheral, CS42L42_SDW_MEM_ACCESS_STATUS); + if (ret < 0) { + dev_err(&peripheral->dev, "Failed to read MEM_ACCESS_STATUS: %d\n", ret); + return ret; + } + + /* If read was not delayed we already have the result */ + if ((ret & CS42L42_SDW_LAST_LATE) == 0) { + *val = data; + return 0; + } + + /* Poll for delayed read completion */ + if ((ret & CS42L42_SDW_RDATA_RDY) == 0) { + ret = cs42l42_sdw_poll_status(peripheral, + CS42L42_SDW_RDATA_RDY, CS42L42_SDW_RDATA_RDY); + if (ret < 0) + return ret; + } + + ret = sdw_read_no_pm(peripheral, CS42L42_SDW_MEM_READ_DATA); + if (ret < 0) { + dev_err(&peripheral->dev, "Failed to read READ_DATA: %d\n", ret); + return ret; + } + + *val = (u8)ret; + + return 0; +} + +static int cs42l42_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct sdw_slave *peripheral = context; + int ret; + + ret = cs42l42_sdw_poll_status(peripheral, CS42L42_SDW_CMD_IN_PROGRESS, 0); + if (ret < 0) + return ret; + + return sdw_write_no_pm(peripheral, reg + CS42L42_SDW_ADDR_OFFSET, (u8)val); +} + +/* Initialise cs42l42 using SoundWire - this is only called once, during initialisation */ +static void cs42l42_sdw_init(struct sdw_slave *peripheral) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); + int ret; + + regcache_cache_only(cs42l42->regmap, false); + + ret = cs42l42_init(cs42l42); + if (ret < 0) { + regcache_cache_only(cs42l42->regmap, true); + goto err; + } + + /* Write out any cached changes that happened between probe and attach */ + ret = regcache_sync(cs42l42->regmap); + if (ret < 0) + dev_warn(cs42l42->dev, "Failed to sync cache: %d\n", ret); + + /* Disable internal logic that makes clock-stop conditional */ + regmap_clear_bits(cs42l42->regmap, CS42L42_PWR_CTL3, CS42L42_SW_CLK_STP_STAT_SEL_MASK); + +err: + /* This cancels the pm_runtime_get_noresume() call from cs42l42_sdw_probe(). */ + pm_runtime_put_autosuspend(cs42l42->dev); +} + +static int cs42l42_sdw_read_prop(struct sdw_slave *peripheral) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); + struct sdw_slave_prop *prop = &peripheral->prop; + struct sdw_dpn_prop *ports; + + ports = devm_kcalloc(cs42l42->dev, 2, sizeof(*ports), GFP_KERNEL); + if (!ports) + return -ENOMEM; + + prop->source_ports = BIT(CS42L42_SDW_CAPTURE_PORT); + prop->sink_ports = BIT(CS42L42_SDW_PLAYBACK_PORT); + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + + /* DP1 - capture */ + ports[0].num = CS42L42_SDW_CAPTURE_PORT, + ports[0].type = SDW_DPN_FULL, + ports[0].ch_prep_timeout = 10, + prop->src_dpn_prop = &ports[0]; + + /* DP2 - playback */ + ports[1].num = CS42L42_SDW_PLAYBACK_PORT, + ports[1].type = SDW_DPN_FULL, + ports[1].ch_prep_timeout = 10, + prop->sink_dpn_prop = &ports[1]; + + return 0; +} + +static int cs42l42_sdw_update_status(struct sdw_slave *peripheral, + enum sdw_slave_status status) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); + + switch (status) { + case SDW_SLAVE_ATTACHED: + dev_dbg(cs42l42->dev, "ATTACHED\n"); + + /* + * The SoundWire core can report stale ATTACH notifications + * if we hard-reset CS42L42 in probe() but it had already been + * enumerated. Reject the ATTACH if we haven't yet seen an + * UNATTACH report for the device being in reset. + */ + if (cs42l42->sdw_waiting_first_unattach) + break; + + /* + * Initialise codec, this only needs to be done once. + * When resuming from suspend, resume callback will handle re-init of codec, + * using regcache_sync(). + */ + if (!cs42l42->init_done) + cs42l42_sdw_init(peripheral); + break; + case SDW_SLAVE_UNATTACHED: + dev_dbg(cs42l42->dev, "UNATTACHED\n"); + + if (cs42l42->sdw_waiting_first_unattach) { + /* + * SoundWire core has seen that CS42L42 is not on + * the bus so release RESET and wait for ATTACH. + */ + cs42l42->sdw_waiting_first_unattach = false; + gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); + } + + break; + default: + break; + } + + return 0; +} + +static int cs42l42_sdw_bus_config(struct sdw_slave *peripheral, + struct sdw_bus_params *params) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); + unsigned int new_sclk = params->curr_dr_freq / 2; + + /* The cs42l42 cannot support a glitchless SWIRE_CLK change. */ + if ((new_sclk != cs42l42->sclk) && cs42l42->stream_use) { + dev_warn(cs42l42->dev, "Rejected SCLK change while audio active\n"); + return -EBUSY; + } + + cs42l42->sclk = new_sclk; + + dev_dbg(cs42l42->dev, "bus_config: sclk=%u c=%u r=%u\n", + cs42l42->sclk, params->col, params->row); + + return 0; +} + +static const struct sdw_slave_ops cs42l42_sdw_ops = { +/* No interrupt callback because only hardware INT is supported for Jack Detect in the CS42L42 */ + .read_prop = cs42l42_sdw_read_prop, + .update_status = cs42l42_sdw_update_status, + .bus_config = cs42l42_sdw_bus_config, + .port_prep = cs42l42_sdw_port_prep, +}; + +static int __maybe_unused cs42l42_sdw_runtime_suspend(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + + dev_dbg(dev, "Runtime suspend\n"); + + if (!cs42l42->init_done) + return 0; + + /* The host controller could suspend, which would mean no register access */ + regcache_cache_only(cs42l42->regmap, true); + + return 0; +} + +static const struct reg_sequence __maybe_unused cs42l42_soft_reboot_seq[] = { + REG_SEQ0(CS42L42_SOFT_RESET_REBOOT, 0x1e), +}; + +static int __maybe_unused cs42l42_sdw_handle_unattach(struct cs42l42_private *cs42l42) +{ + struct sdw_slave *peripheral = cs42l42->sdw_peripheral; + + if (!peripheral->unattach_request) + return 0; + + /* Cannot access registers until master re-attaches. */ + dev_dbg(&peripheral->dev, "Wait for initialization_complete\n"); + if (!wait_for_completion_timeout(&peripheral->initialization_complete, + msecs_to_jiffies(5000))) { + dev_err(&peripheral->dev, "initialization_complete timed out\n"); + return -ETIMEDOUT; + } + + peripheral->unattach_request = 0; + + /* + * After a bus reset there must be a reconfiguration reset to + * reinitialize the internal state of CS42L42. + */ + regmap_multi_reg_write_bypassed(cs42l42->regmap, + cs42l42_soft_reboot_seq, + ARRAY_SIZE(cs42l42_soft_reboot_seq)); + usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2); + regcache_mark_dirty(cs42l42->regmap); + + return 0; +} + +static int __maybe_unused cs42l42_sdw_runtime_resume(struct device *dev) +{ + static const unsigned int ts_dbnce_ms[] = { 0, 125, 250, 500, 750, 1000, 1250, 1500}; + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + unsigned int dbnce; + int ret; + + dev_dbg(dev, "Runtime resume\n"); + + if (!cs42l42->init_done) + return 0; + + ret = cs42l42_sdw_handle_unattach(cs42l42); + if (ret < 0) { + return ret; + } else if (ret > 0) { + dbnce = max(cs42l42->ts_dbnc_rise, cs42l42->ts_dbnc_fall); + + if (dbnce > 0) + msleep(ts_dbnce_ms[dbnce]); + } + + regcache_cache_only(cs42l42->regmap, false); + + /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ + regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); + regcache_sync(cs42l42->regmap); + + return 0; +} + +static int __maybe_unused cs42l42_sdw_resume(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "System resume\n"); + + /* Power-up so it can re-enumerate */ + ret = cs42l42_resume(dev); + if (ret) + return ret; + + /* Wait for re-attach */ + ret = cs42l42_sdw_handle_unattach(cs42l42); + if (ret < 0) + return ret; + + cs42l42_resume_restore(dev); + + return 0; +} + +static int cs42l42_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) +{ + struct snd_soc_component_driver *component_drv; + struct device *dev = &peripheral->dev; + struct cs42l42_private *cs42l42; + struct regmap_config *regmap_conf; + struct regmap *regmap; + int irq, ret; + + cs42l42 = devm_kzalloc(dev, sizeof(*cs42l42), GFP_KERNEL); + if (!cs42l42) + return -ENOMEM; + + if (has_acpi_companion(dev)) + irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0); + else + irq = of_irq_get(dev->of_node, 0); + + if (irq == -ENOENT) + irq = 0; + else if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get IRQ\n"); + + regmap_conf = devm_kmemdup(dev, &cs42l42_regmap, sizeof(cs42l42_regmap), GFP_KERNEL); + if (!regmap_conf) + return -ENOMEM; + regmap_conf->reg_bits = 16; + regmap_conf->num_ranges = 0; + regmap_conf->reg_read = cs42l42_sdw_read; + regmap_conf->reg_write = cs42l42_sdw_write; + + regmap = devm_regmap_init(dev, NULL, peripheral, regmap_conf); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(regmap, true); + + component_drv = devm_kmemdup(dev, + &cs42l42_soc_component, + sizeof(cs42l42_soc_component), + GFP_KERNEL); + if (!component_drv) + return -ENOMEM; + + component_drv->dapm_routes = cs42l42_sdw_audio_map; + component_drv->num_dapm_routes = ARRAY_SIZE(cs42l42_sdw_audio_map); + + cs42l42->dev = dev; + cs42l42->regmap = regmap; + cs42l42->sdw_peripheral = peripheral; + cs42l42->irq = irq; + cs42l42->devid = CS42L42_CHIP_ID; + + /* + * pm_runtime is needed to control bus manager suspend, and to + * recover from an unattach_request when the manager suspends. + */ + pm_runtime_set_autosuspend_delay(cs42l42->dev, 3000); + pm_runtime_use_autosuspend(cs42l42->dev); + pm_runtime_mark_last_busy(cs42l42->dev); + pm_runtime_set_active(cs42l42->dev); + pm_runtime_get_noresume(cs42l42->dev); + pm_runtime_enable(cs42l42->dev); + + ret = cs42l42_common_probe(cs42l42, component_drv, &cs42l42_sdw_dai); + if (ret < 0) + return ret; + + return 0; +} + +static int cs42l42_sdw_remove(struct sdw_slave *peripheral) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); + + cs42l42_common_remove(cs42l42); + pm_runtime_disable(cs42l42->dev); + + return 0; +} + +static const struct dev_pm_ops cs42l42_sdw_pm = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_sdw_resume) + SET_RUNTIME_PM_OPS(cs42l42_sdw_runtime_suspend, cs42l42_sdw_runtime_resume, NULL) +}; + +static const struct sdw_device_id cs42l42_sdw_id[] = { + SDW_SLAVE_ENTRY(0x01FA, 0x4242, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, cs42l42_sdw_id); + +static struct sdw_driver cs42l42_sdw_driver = { + .driver = { + .name = "cs42l42-sdw", + .pm = &cs42l42_sdw_pm, + }, + .probe = cs42l42_sdw_probe, + .remove = cs42l42_sdw_remove, + .ops = &cs42l42_sdw_ops, + .id_table = cs42l42_sdw_id, +}; + +module_sdw_driver(cs42l42_sdw_driver); + +MODULE_DESCRIPTION("ASoC CS42L42 SoundWire driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_CS42L42_CORE); diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c new file mode 100644 index 0000000000..2961340f15 --- /dev/null +++ b/sound/soc/codecs/cs42l42.c @@ -0,0 +1,2500 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l42.c -- CS42L42 ALSA SoC audio driver + * + * Copyright 2016 Cirrus Logic, Inc. + * + * Author: James Schulman + * Author: Brian Austin + * Author: Michael White + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs42l42.h" +#include "cirrus_legacy.h" + +static const char * const cs42l42_supply_names[] = { + "VA", + "VP", + "VCP", + "VD_FILT", + "VL", +}; + +static const struct reg_default cs42l42_reg_defaults[] = { + { CS42L42_FRZ_CTL, 0x00 }, + { CS42L42_SRC_CTL, 0x10 }, + { CS42L42_MCLK_CTL, 0x02 }, + { CS42L42_SFTRAMP_RATE, 0xA4 }, + { CS42L42_SLOW_START_ENABLE, 0x70 }, + { CS42L42_I2C_DEBOUNCE, 0x88 }, + { CS42L42_I2C_STRETCH, 0x03 }, + { CS42L42_I2C_TIMEOUT, 0xB7 }, + { CS42L42_PWR_CTL1, 0xFF }, + { CS42L42_PWR_CTL2, 0x84 }, + { CS42L42_PWR_CTL3, 0x20 }, + { CS42L42_RSENSE_CTL1, 0x40 }, + { CS42L42_RSENSE_CTL2, 0x00 }, + { CS42L42_OSC_SWITCH, 0x00 }, + { CS42L42_RSENSE_CTL3, 0x1B }, + { CS42L42_TSENSE_CTL, 0x1B }, + { CS42L42_TSRS_INT_DISABLE, 0x00 }, + { CS42L42_HSDET_CTL1, 0x77 }, + { CS42L42_HSDET_CTL2, 0x00 }, + { CS42L42_HS_SWITCH_CTL, 0xF3 }, + { CS42L42_HS_CLAMP_DISABLE, 0x00 }, + { CS42L42_MCLK_SRC_SEL, 0x00 }, + { CS42L42_SPDIF_CLK_CFG, 0x00 }, + { CS42L42_FSYNC_PW_LOWER, 0x00 }, + { CS42L42_FSYNC_PW_UPPER, 0x00 }, + { CS42L42_FSYNC_P_LOWER, 0xF9 }, + { CS42L42_FSYNC_P_UPPER, 0x00 }, + { CS42L42_ASP_CLK_CFG, 0x00 }, + { CS42L42_ASP_FRM_CFG, 0x10 }, + { CS42L42_FS_RATE_EN, 0x00 }, + { CS42L42_IN_ASRC_CLK, 0x00 }, + { CS42L42_OUT_ASRC_CLK, 0x00 }, + { CS42L42_PLL_DIV_CFG1, 0x00 }, + { CS42L42_ADC_OVFL_INT_MASK, 0x01 }, + { CS42L42_MIXER_INT_MASK, 0x0F }, + { CS42L42_SRC_INT_MASK, 0x0F }, + { CS42L42_ASP_RX_INT_MASK, 0x1F }, + { CS42L42_ASP_TX_INT_MASK, 0x0F }, + { CS42L42_CODEC_INT_MASK, 0x03 }, + { CS42L42_SRCPL_INT_MASK, 0x7F }, + { CS42L42_VPMON_INT_MASK, 0x01 }, + { CS42L42_PLL_LOCK_INT_MASK, 0x01 }, + { CS42L42_TSRS_PLUG_INT_MASK, 0x0F }, + { CS42L42_PLL_CTL1, 0x00 }, + { CS42L42_PLL_DIV_FRAC0, 0x00 }, + { CS42L42_PLL_DIV_FRAC1, 0x00 }, + { CS42L42_PLL_DIV_FRAC2, 0x00 }, + { CS42L42_PLL_DIV_INT, 0x40 }, + { CS42L42_PLL_CTL3, 0x10 }, + { CS42L42_PLL_CAL_RATIO, 0x80 }, + { CS42L42_PLL_CTL4, 0x03 }, + { CS42L42_LOAD_DET_EN, 0x00 }, + { CS42L42_HSBIAS_SC_AUTOCTL, 0x03 }, + { CS42L42_WAKE_CTL, 0xC0 }, + { CS42L42_ADC_DISABLE_MUTE, 0x00 }, + { CS42L42_TIPSENSE_CTL, 0x02 }, + { CS42L42_MISC_DET_CTL, 0x03 }, + { CS42L42_MIC_DET_CTL1, 0x1F }, + { CS42L42_MIC_DET_CTL2, 0x2F }, + { CS42L42_DET_INT1_MASK, 0xE0 }, + { CS42L42_DET_INT2_MASK, 0xFF }, + { CS42L42_HS_BIAS_CTL, 0xC2 }, + { CS42L42_ADC_CTL, 0x00 }, + { CS42L42_ADC_VOLUME, 0x00 }, + { CS42L42_ADC_WNF_HPF_CTL, 0x71 }, + { CS42L42_DAC_CTL1, 0x00 }, + { CS42L42_DAC_CTL2, 0x02 }, + { CS42L42_HP_CTL, 0x0D }, + { CS42L42_CLASSH_CTL, 0x07 }, + { CS42L42_MIXER_CHA_VOL, 0x3F }, + { CS42L42_MIXER_ADC_VOL, 0x3F }, + { CS42L42_MIXER_CHB_VOL, 0x3F }, + { CS42L42_EQ_COEF_IN0, 0x00 }, + { CS42L42_EQ_COEF_IN1, 0x00 }, + { CS42L42_EQ_COEF_IN2, 0x00 }, + { CS42L42_EQ_COEF_IN3, 0x00 }, + { CS42L42_EQ_COEF_RW, 0x00 }, + { CS42L42_EQ_COEF_OUT0, 0x00 }, + { CS42L42_EQ_COEF_OUT1, 0x00 }, + { CS42L42_EQ_COEF_OUT2, 0x00 }, + { CS42L42_EQ_COEF_OUT3, 0x00 }, + { CS42L42_EQ_INIT_STAT, 0x00 }, + { CS42L42_EQ_START_FILT, 0x00 }, + { CS42L42_EQ_MUTE_CTL, 0x00 }, + { CS42L42_SP_RX_CH_SEL, 0x04 }, + { CS42L42_SP_RX_ISOC_CTL, 0x04 }, + { CS42L42_SP_RX_FS, 0x8C }, + { CS42l42_SPDIF_CH_SEL, 0x0E }, + { CS42L42_SP_TX_ISOC_CTL, 0x04 }, + { CS42L42_SP_TX_FS, 0xCC }, + { CS42L42_SPDIF_SW_CTL1, 0x3F }, + { CS42L42_SRC_SDIN_FS, 0x40 }, + { CS42L42_SRC_SDOUT_FS, 0x40 }, + { CS42L42_SPDIF_CTL1, 0x01 }, + { CS42L42_SPDIF_CTL2, 0x00 }, + { CS42L42_SPDIF_CTL3, 0x00 }, + { CS42L42_SPDIF_CTL4, 0x42 }, + { CS42L42_ASP_TX_SZ_EN, 0x00 }, + { CS42L42_ASP_TX_CH_EN, 0x00 }, + { CS42L42_ASP_TX_CH_AP_RES, 0x0F }, + { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_TX_HIZ_DLY_CFG, 0x00 }, + { CS42L42_ASP_TX_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH2_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_EN, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH1_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI1_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 }, +}; + +bool cs42l42_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L42_PAGE_REGISTER: + case CS42L42_DEVID_AB: + case CS42L42_DEVID_CD: + case CS42L42_DEVID_E: + case CS42L42_FABID: + case CS42L42_REVID: + case CS42L42_FRZ_CTL: + case CS42L42_SRC_CTL: + case CS42L42_MCLK_STATUS: + case CS42L42_MCLK_CTL: + case CS42L42_SFTRAMP_RATE: + case CS42L42_SLOW_START_ENABLE: + case CS42L42_I2C_DEBOUNCE: + case CS42L42_I2C_STRETCH: + case CS42L42_I2C_TIMEOUT: + case CS42L42_PWR_CTL1: + case CS42L42_PWR_CTL2: + case CS42L42_PWR_CTL3: + case CS42L42_RSENSE_CTL1: + case CS42L42_RSENSE_CTL2: + case CS42L42_OSC_SWITCH: + case CS42L42_OSC_SWITCH_STATUS: + case CS42L42_RSENSE_CTL3: + case CS42L42_TSENSE_CTL: + case CS42L42_TSRS_INT_DISABLE: + case CS42L42_TRSENSE_STATUS: + case CS42L42_HSDET_CTL1: + case CS42L42_HSDET_CTL2: + case CS42L42_HS_SWITCH_CTL: + case CS42L42_HS_DET_STATUS: + case CS42L42_HS_CLAMP_DISABLE: + case CS42L42_MCLK_SRC_SEL: + case CS42L42_SPDIF_CLK_CFG: + case CS42L42_FSYNC_PW_LOWER: + case CS42L42_FSYNC_PW_UPPER: + case CS42L42_FSYNC_P_LOWER: + case CS42L42_FSYNC_P_UPPER: + case CS42L42_ASP_CLK_CFG: + case CS42L42_ASP_FRM_CFG: + case CS42L42_FS_RATE_EN: + case CS42L42_IN_ASRC_CLK: + case CS42L42_OUT_ASRC_CLK: + case CS42L42_PLL_DIV_CFG1: + case CS42L42_ADC_OVFL_STATUS: + case CS42L42_MIXER_STATUS: + case CS42L42_SRC_STATUS: + case CS42L42_ASP_RX_STATUS: + case CS42L42_ASP_TX_STATUS: + case CS42L42_CODEC_STATUS: + case CS42L42_DET_INT_STATUS1: + case CS42L42_DET_INT_STATUS2: + case CS42L42_SRCPL_INT_STATUS: + case CS42L42_VPMON_STATUS: + case CS42L42_PLL_LOCK_STATUS: + case CS42L42_TSRS_PLUG_STATUS: + case CS42L42_ADC_OVFL_INT_MASK: + case CS42L42_MIXER_INT_MASK: + case CS42L42_SRC_INT_MASK: + case CS42L42_ASP_RX_INT_MASK: + case CS42L42_ASP_TX_INT_MASK: + case CS42L42_CODEC_INT_MASK: + case CS42L42_SRCPL_INT_MASK: + case CS42L42_VPMON_INT_MASK: + case CS42L42_PLL_LOCK_INT_MASK: + case CS42L42_TSRS_PLUG_INT_MASK: + case CS42L42_PLL_CTL1: + case CS42L42_PLL_DIV_FRAC0: + case CS42L42_PLL_DIV_FRAC1: + case CS42L42_PLL_DIV_FRAC2: + case CS42L42_PLL_DIV_INT: + case CS42L42_PLL_CTL3: + case CS42L42_PLL_CAL_RATIO: + case CS42L42_PLL_CTL4: + case CS42L42_LOAD_DET_RCSTAT: + case CS42L42_LOAD_DET_DONE: + case CS42L42_LOAD_DET_EN: + case CS42L42_HSBIAS_SC_AUTOCTL: + case CS42L42_WAKE_CTL: + case CS42L42_ADC_DISABLE_MUTE: + case CS42L42_TIPSENSE_CTL: + case CS42L42_MISC_DET_CTL: + case CS42L42_MIC_DET_CTL1: + case CS42L42_MIC_DET_CTL2: + case CS42L42_DET_STATUS1: + case CS42L42_DET_STATUS2: + case CS42L42_DET_INT1_MASK: + case CS42L42_DET_INT2_MASK: + case CS42L42_HS_BIAS_CTL: + case CS42L42_ADC_CTL: + case CS42L42_ADC_VOLUME: + case CS42L42_ADC_WNF_HPF_CTL: + case CS42L42_DAC_CTL1: + case CS42L42_DAC_CTL2: + case CS42L42_HP_CTL: + case CS42L42_CLASSH_CTL: + case CS42L42_MIXER_CHA_VOL: + case CS42L42_MIXER_ADC_VOL: + case CS42L42_MIXER_CHB_VOL: + case CS42L42_EQ_COEF_IN0: + case CS42L42_EQ_COEF_IN1: + case CS42L42_EQ_COEF_IN2: + case CS42L42_EQ_COEF_IN3: + case CS42L42_EQ_COEF_RW: + case CS42L42_EQ_COEF_OUT0: + case CS42L42_EQ_COEF_OUT1: + case CS42L42_EQ_COEF_OUT2: + case CS42L42_EQ_COEF_OUT3: + case CS42L42_EQ_INIT_STAT: + case CS42L42_EQ_START_FILT: + case CS42L42_EQ_MUTE_CTL: + case CS42L42_SP_RX_CH_SEL: + case CS42L42_SP_RX_ISOC_CTL: + case CS42L42_SP_RX_FS: + case CS42l42_SPDIF_CH_SEL: + case CS42L42_SP_TX_ISOC_CTL: + case CS42L42_SP_TX_FS: + case CS42L42_SPDIF_SW_CTL1: + case CS42L42_SRC_SDIN_FS: + case CS42L42_SRC_SDOUT_FS: + case CS42L42_SOFT_RESET_REBOOT: + case CS42L42_SPDIF_CTL1: + case CS42L42_SPDIF_CTL2: + case CS42L42_SPDIF_CTL3: + case CS42L42_SPDIF_CTL4: + case CS42L42_ASP_TX_SZ_EN: + case CS42L42_ASP_TX_CH_EN: + case CS42L42_ASP_TX_CH_AP_RES: + case CS42L42_ASP_TX_CH1_BIT_MSB: + case CS42L42_ASP_TX_CH1_BIT_LSB: + case CS42L42_ASP_TX_HIZ_DLY_CFG: + case CS42L42_ASP_TX_CH2_BIT_MSB: + case CS42L42_ASP_TX_CH2_BIT_LSB: + case CS42L42_ASP_RX_DAI0_EN: + case CS42L42_ASP_RX_DAI0_CH1_AP_RES: + case CS42L42_ASP_RX_DAI0_CH1_BIT_MSB: + case CS42L42_ASP_RX_DAI0_CH1_BIT_LSB: + case CS42L42_ASP_RX_DAI0_CH2_AP_RES: + case CS42L42_ASP_RX_DAI0_CH2_BIT_MSB: + case CS42L42_ASP_RX_DAI0_CH2_BIT_LSB: + case CS42L42_ASP_RX_DAI0_CH3_AP_RES: + case CS42L42_ASP_RX_DAI0_CH3_BIT_MSB: + case CS42L42_ASP_RX_DAI0_CH3_BIT_LSB: + case CS42L42_ASP_RX_DAI0_CH4_AP_RES: + case CS42L42_ASP_RX_DAI0_CH4_BIT_MSB: + case CS42L42_ASP_RX_DAI0_CH4_BIT_LSB: + case CS42L42_ASP_RX_DAI1_CH1_AP_RES: + case CS42L42_ASP_RX_DAI1_CH1_BIT_MSB: + case CS42L42_ASP_RX_DAI1_CH1_BIT_LSB: + case CS42L42_ASP_RX_DAI1_CH2_AP_RES: + case CS42L42_ASP_RX_DAI1_CH2_BIT_MSB: + case CS42L42_ASP_RX_DAI1_CH2_BIT_LSB: + case CS42L42_SUB_REVID: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_NS_GPL(cs42l42_readable_register, SND_SOC_CS42L42_CORE); + +bool cs42l42_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L42_DEVID_AB: + case CS42L42_DEVID_CD: + case CS42L42_DEVID_E: + case CS42L42_MCLK_STATUS: + case CS42L42_OSC_SWITCH_STATUS: + case CS42L42_TRSENSE_STATUS: + case CS42L42_HS_DET_STATUS: + case CS42L42_ADC_OVFL_STATUS: + case CS42L42_MIXER_STATUS: + case CS42L42_SRC_STATUS: + case CS42L42_ASP_RX_STATUS: + case CS42L42_ASP_TX_STATUS: + case CS42L42_CODEC_STATUS: + case CS42L42_DET_INT_STATUS1: + case CS42L42_DET_INT_STATUS2: + case CS42L42_SRCPL_INT_STATUS: + case CS42L42_VPMON_STATUS: + case CS42L42_PLL_LOCK_STATUS: + case CS42L42_TSRS_PLUG_STATUS: + case CS42L42_LOAD_DET_RCSTAT: + case CS42L42_LOAD_DET_DONE: + case CS42L42_DET_STATUS1: + case CS42L42_DET_STATUS2: + case CS42L42_SOFT_RESET_REBOOT: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_NS_GPL(cs42l42_volatile_register, SND_SOC_CS42L42_CORE); + +const struct regmap_range_cfg cs42l42_page_range = { + .name = "Pages", + .range_min = 0, + .range_max = CS42L42_MAX_REGISTER, + .selector_reg = CS42L42_PAGE_REGISTER, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 256, +}; +EXPORT_SYMBOL_NS_GPL(cs42l42_page_range, SND_SOC_CS42L42_CORE); + +const struct regmap_config cs42l42_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = cs42l42_readable_register, + .volatile_reg = cs42l42_volatile_register, + + .ranges = &cs42l42_page_range, + .num_ranges = 1, + + .max_register = CS42L42_MAX_REGISTER, + .reg_defaults = cs42l42_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs42l42_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .use_single_read = true, + .use_single_write = true, +}; +EXPORT_SYMBOL_NS_GPL(cs42l42_regmap, SND_SOC_CS42L42_CORE); + +static DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 100, true); +static DECLARE_TLV_DB_SCALE(mixer_tlv, -6300, 100, true); + +static int cs42l42_slow_start_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + u8 val; + + /* all bits of SLOW_START_EN must change together */ + switch (ucontrol->value.integer.value[0]) { + case 0: + val = 0; + break; + case 1: + val = CS42L42_SLOW_START_EN_MASK; + break; + default: + return -EINVAL; + } + + return snd_soc_component_update_bits(component, CS42L42_SLOW_START_ENABLE, + CS42L42_SLOW_START_EN_MASK, val); +} + +static const char * const cs42l42_hpf_freq_text[] = { + "1.86Hz", "120Hz", "235Hz", "466Hz" +}; + +static SOC_ENUM_SINGLE_DECL(cs42l42_hpf_freq_enum, CS42L42_ADC_WNF_HPF_CTL, + CS42L42_ADC_HPF_CF_SHIFT, + cs42l42_hpf_freq_text); + +static const char * const cs42l42_wnf3_freq_text[] = { + "160Hz", "180Hz", "200Hz", "220Hz", + "240Hz", "260Hz", "280Hz", "300Hz" +}; + +static SOC_ENUM_SINGLE_DECL(cs42l42_wnf3_freq_enum, CS42L42_ADC_WNF_HPF_CTL, + CS42L42_ADC_WNF_CF_SHIFT, + cs42l42_wnf3_freq_text); + +static const struct snd_kcontrol_new cs42l42_snd_controls[] = { + /* ADC Volume and Filter Controls */ + SOC_SINGLE("ADC Notch Switch", CS42L42_ADC_CTL, + CS42L42_ADC_NOTCH_DIS_SHIFT, true, true), + SOC_SINGLE("ADC Weak Force Switch", CS42L42_ADC_CTL, + CS42L42_ADC_FORCE_WEAK_VCM_SHIFT, true, false), + SOC_SINGLE("ADC Invert Switch", CS42L42_ADC_CTL, + CS42L42_ADC_INV_SHIFT, true, false), + SOC_SINGLE("ADC Boost Switch", CS42L42_ADC_CTL, + CS42L42_ADC_DIG_BOOST_SHIFT, true, false), + SOC_SINGLE_S8_TLV("ADC Volume", CS42L42_ADC_VOLUME, -97, 12, adc_tlv), + SOC_SINGLE("ADC WNF Switch", CS42L42_ADC_WNF_HPF_CTL, + CS42L42_ADC_WNF_EN_SHIFT, true, false), + SOC_SINGLE("ADC HPF Switch", CS42L42_ADC_WNF_HPF_CTL, + CS42L42_ADC_HPF_EN_SHIFT, true, false), + SOC_ENUM("HPF Corner Freq", cs42l42_hpf_freq_enum), + SOC_ENUM("WNF 3dB Freq", cs42l42_wnf3_freq_enum), + + /* DAC Volume and Filter Controls */ + SOC_SINGLE("DACA Invert Switch", CS42L42_DAC_CTL1, + CS42L42_DACA_INV_SHIFT, true, false), + SOC_SINGLE("DACB Invert Switch", CS42L42_DAC_CTL1, + CS42L42_DACB_INV_SHIFT, true, false), + SOC_SINGLE("DAC HPF Switch", CS42L42_DAC_CTL2, + CS42L42_DAC_HPF_EN_SHIFT, true, false), + SOC_DOUBLE_R_TLV("Mixer Volume", CS42L42_MIXER_CHA_VOL, + CS42L42_MIXER_CHB_VOL, CS42L42_MIXER_CH_VOL_SHIFT, + 0x3f, 1, mixer_tlv), + + SOC_SINGLE_EXT("Slow Start Switch", CS42L42_SLOW_START_ENABLE, + CS42L42_SLOW_START_EN_SHIFT, true, false, + snd_soc_get_volsw, cs42l42_slow_start_put), +}; + +static int cs42l42_hp_adc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + cs42l42->hp_adc_up_pending = true; + break; + case SND_SOC_DAPM_POST_PMU: + /* Only need one delay if HP and ADC are both powering-up */ + if (cs42l42->hp_adc_up_pending) { + usleep_range(CS42L42_HP_ADC_EN_TIME_US, + CS42L42_HP_ADC_EN_TIME_US + 1000); + cs42l42->hp_adc_up_pending = false; + } + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = { + /* Playback Path */ + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_DAC_E("DAC", NULL, CS42L42_PWR_CTL1, CS42L42_HP_PDN_SHIFT, 1, + cs42l42_hp_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER("MIXER", CS42L42_PWR_CTL1, CS42L42_MIXER_PDN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, SND_SOC_NOPM, 0, 0), + + /* Playback Requirements */ + SND_SOC_DAPM_SUPPLY("ASP DAI0", CS42L42_PWR_CTL1, CS42L42_ASP_DAI_PDN_SHIFT, 1, NULL, 0), + + /* Capture Path */ + SND_SOC_DAPM_INPUT("HS"), + SND_SOC_DAPM_ADC_E("ADC", NULL, CS42L42_PWR_CTL1, CS42L42_ADC_PDN_SHIFT, 1, + cs42l42_hp_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH1_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("SDOUT2", NULL, 1, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH2_SHIFT, 0), + + /* Capture Requirements */ + SND_SOC_DAPM_SUPPLY("ASP DAO0", CS42L42_PWR_CTL1, CS42L42_ASP_DAO_PDN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASP TX EN", CS42L42_ASP_TX_SZ_EN, CS42L42_ASP_TX_EN_SHIFT, 0, NULL, 0), + + /* Playback/Capture Requirements */ + SND_SOC_DAPM_SUPPLY("SCLK", CS42L42_ASP_CLK_CFG, CS42L42_ASP_SCLK_EN_SHIFT, 0, NULL, 0), + + /* Soundwire SRC power control */ + SND_SOC_DAPM_PGA("DACSRC", CS42L42_PWR_CTL2, CS42L42_DAC_SRC_PDNB_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADCSRC", CS42L42_PWR_CTL2, CS42L42_ADC_SRC_PDNB_SHIFT, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route cs42l42_audio_map[] = { + /* Playback Path */ + {"HP", NULL, "DAC"}, + {"DAC", NULL, "MIXER"}, + {"MIXER", NULL, "SDIN1"}, + {"MIXER", NULL, "SDIN2"}, + {"SDIN1", NULL, "Playback"}, + {"SDIN2", NULL, "Playback"}, + + /* Playback Requirements */ + {"SDIN1", NULL, "ASP DAI0"}, + {"SDIN2", NULL, "ASP DAI0"}, + {"SDIN1", NULL, "SCLK"}, + {"SDIN2", NULL, "SCLK"}, + + /* Capture Path */ + {"ADC", NULL, "HS"}, + { "SDOUT1", NULL, "ADC" }, + { "SDOUT2", NULL, "ADC" }, + { "Capture", NULL, "SDOUT1" }, + { "Capture", NULL, "SDOUT2" }, + + /* Capture Requirements */ + { "SDOUT1", NULL, "ASP DAO0" }, + { "SDOUT2", NULL, "ASP DAO0" }, + { "SDOUT1", NULL, "SCLK" }, + { "SDOUT2", NULL, "SCLK" }, + { "SDOUT1", NULL, "ASP TX EN" }, + { "SDOUT2", NULL, "ASP TX EN" }, +}; + +static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jk, void *d) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + + /* Prevent race with interrupt handler */ + mutex_lock(&cs42l42->irq_lock); + cs42l42->jack = jk; + + if (jk) { + switch (cs42l42->hs_type) { + case CS42L42_PLUG_CTIA: + case CS42L42_PLUG_OMTP: + snd_soc_jack_report(jk, SND_JACK_HEADSET, SND_JACK_HEADSET); + break; + case CS42L42_PLUG_HEADPHONE: + snd_soc_jack_report(jk, SND_JACK_HEADPHONE, SND_JACK_HEADPHONE); + break; + default: + break; + } + } + mutex_unlock(&cs42l42->irq_lock); + + return 0; +} + +const struct snd_soc_component_driver cs42l42_soc_component = { + .set_jack = cs42l42_set_jack, + .dapm_widgets = cs42l42_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets), + .dapm_routes = cs42l42_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l42_audio_map), + .controls = cs42l42_snd_controls, + .num_controls = ARRAY_SIZE(cs42l42_snd_controls), + .endianness = 1, +}; +EXPORT_SYMBOL_NS_GPL(cs42l42_soc_component, SND_SOC_CS42L42_CORE); + +/* Switch to SCLK. Atomic delay after the write to allow the switch to complete. */ +static const struct reg_sequence cs42l42_to_sclk_seq[] = { + { + .reg = CS42L42_OSC_SWITCH, + .def = CS42L42_SCLK_PRESENT_MASK, + .delay_us = CS42L42_CLOCK_SWITCH_DELAY_US, + }, +}; + +/* Switch to OSC. Atomic delay after the write to allow the switch to complete. */ +static const struct reg_sequence cs42l42_to_osc_seq[] = { + { + .reg = CS42L42_OSC_SWITCH, + .def = 0, + .delay_us = CS42L42_CLOCK_SWITCH_DELAY_US, + }, +}; + +struct cs42l42_pll_params { + u32 sclk; + u8 mclk_src_sel; + u8 sclk_prediv; + u8 pll_div_int; + u32 pll_div_frac; + u8 pll_mode; + u8 pll_divout; + u32 mclk_int; + u8 pll_cal_ratio; + u8 n; +}; + +/* + * Common PLL Settings for given SCLK + * Table 4-5 from the Datasheet + */ +static const struct cs42l42_pll_params pll_ratio_table[] = { + { 1411200, 1, 0x00, 0x80, 0x000000, 0x03, 0x10, 11289600, 128, 2}, + { 1536000, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125, 2}, + { 2304000, 1, 0x00, 0x55, 0xC00000, 0x02, 0x10, 12288000, 85, 2}, + { 2400000, 1, 0x00, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 2822400, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, + { 3000000, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, + { 3072000, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, + { 4000000, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1}, + { 4096000, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1}, + { 4800000, 1, 0x01, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 4800000, 1, 0x01, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, + { 5644800, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, + { 6000000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, + { 6144000, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, + { 6144000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1}, + { 9600000, 1, 0x02, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 9600000, 1, 0x02, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, + { 11289600, 0, 0, 0, 0, 0, 0, 11289600, 0, 1}, + { 12000000, 0, 0, 0, 0, 0, 0, 12000000, 0, 1}, + { 12288000, 0, 0, 0, 0, 0, 0, 12288000, 0, 1}, + { 19200000, 1, 0x03, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 19200000, 1, 0x03, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, + { 22579200, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, + { 24000000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, + { 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1} +}; + +int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk, + unsigned int sample_rate) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + int i; + + /* Don't reconfigure if there is an audio stream running */ + if (cs42l42->stream_use) { + if (pll_ratio_table[cs42l42->pll_config].sclk == clk) + return 0; + else + return -EBUSY; + } + + for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { + /* MCLKint must be a multiple of the sample rate */ + if (pll_ratio_table[i].mclk_int % sample_rate) + continue; + + if (pll_ratio_table[i].sclk == clk) { + cs42l42->pll_config = i; + + /* Configure the internal sample rate */ + snd_soc_component_update_bits(component, CS42L42_MCLK_CTL, + CS42L42_INTERNAL_FS_MASK, + ((pll_ratio_table[i].mclk_int != + 12000000) && + (pll_ratio_table[i].mclk_int != + 24000000)) << + CS42L42_INTERNAL_FS_SHIFT); + if (pll_ratio_table[i].mclk_src_sel == 0) { + /* Pass the clock straight through */ + snd_soc_component_update_bits(component, + CS42L42_PLL_CTL1, + CS42L42_PLL_START_MASK, 0); + } else { + /* Configure PLL per table 4-5 */ + snd_soc_component_update_bits(component, + CS42L42_PLL_DIV_CFG1, + CS42L42_SCLK_PREDIV_MASK, + pll_ratio_table[i].sclk_prediv + << CS42L42_SCLK_PREDIV_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_PLL_DIV_INT, + CS42L42_PLL_DIV_INT_MASK, + pll_ratio_table[i].pll_div_int + << CS42L42_PLL_DIV_INT_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_PLL_DIV_FRAC0, + CS42L42_PLL_DIV_FRAC_MASK, + CS42L42_FRAC0_VAL( + pll_ratio_table[i].pll_div_frac) + << CS42L42_PLL_DIV_FRAC_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_PLL_DIV_FRAC1, + CS42L42_PLL_DIV_FRAC_MASK, + CS42L42_FRAC1_VAL( + pll_ratio_table[i].pll_div_frac) + << CS42L42_PLL_DIV_FRAC_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_PLL_DIV_FRAC2, + CS42L42_PLL_DIV_FRAC_MASK, + CS42L42_FRAC2_VAL( + pll_ratio_table[i].pll_div_frac) + << CS42L42_PLL_DIV_FRAC_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_PLL_CTL4, + CS42L42_PLL_MODE_MASK, + pll_ratio_table[i].pll_mode + << CS42L42_PLL_MODE_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_PLL_CTL3, + CS42L42_PLL_DIVOUT_MASK, + (pll_ratio_table[i].pll_divout * pll_ratio_table[i].n) + << CS42L42_PLL_DIVOUT_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_PLL_CAL_RATIO, + CS42L42_PLL_CAL_RATIO_MASK, + pll_ratio_table[i].pll_cal_ratio + << CS42L42_PLL_CAL_RATIO_SHIFT); + } + return 0; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(cs42l42_pll_config, SND_SOC_CS42L42_CORE); + +void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate) +{ + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + unsigned int fs; + + /* Don't reconfigure if there is an audio stream running */ + if (cs42l42->stream_use) + return; + + /* SRC MCLK must be as close as possible to 125 * sample rate */ + if (sample_rate <= 48000) + fs = CS42L42_CLK_IASRC_SEL_6; + else + fs = CS42L42_CLK_IASRC_SEL_12; + + /* Set the sample rates (96k or lower) */ + snd_soc_component_update_bits(component, + CS42L42_FS_RATE_EN, + CS42L42_FS_EN_MASK, + (CS42L42_FS_EN_IASRC_96K | + CS42L42_FS_EN_OASRC_96K) << + CS42L42_FS_EN_SHIFT); + + snd_soc_component_update_bits(component, + CS42L42_IN_ASRC_CLK, + CS42L42_CLK_IASRC_SEL_MASK, + fs << CS42L42_CLK_IASRC_SEL_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_OUT_ASRC_CLK, + CS42L42_CLK_OASRC_SEL_MASK, + fs << CS42L42_CLK_OASRC_SEL_SHIFT); +} +EXPORT_SYMBOL_NS_GPL(cs42l42_src_config, SND_SOC_CS42L42_CORE); + +static int cs42l42_asp_config(struct snd_soc_component *component, + unsigned int sclk, unsigned int sample_rate) +{ + u32 fsync = sclk / sample_rate; + + /* Set up the LRCLK */ + if (((fsync * sample_rate) != sclk) || ((fsync % 2) != 0)) { + dev_err(component->dev, + "Unsupported sclk %d/sample rate %d\n", + sclk, + sample_rate); + return -EINVAL; + } + /* Set the LRCLK period */ + snd_soc_component_update_bits(component, + CS42L42_FSYNC_P_LOWER, + CS42L42_FSYNC_PERIOD_MASK, + CS42L42_FRAC0_VAL(fsync - 1) << + CS42L42_FSYNC_PERIOD_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_FSYNC_P_UPPER, + CS42L42_FSYNC_PERIOD_MASK, + CS42L42_FRAC1_VAL(fsync - 1) << + CS42L42_FSYNC_PERIOD_SHIFT); + /* Set the LRCLK to 50% duty cycle */ + fsync = fsync / 2; + snd_soc_component_update_bits(component, + CS42L42_FSYNC_PW_LOWER, + CS42L42_FSYNC_PULSE_WIDTH_MASK, + CS42L42_FRAC0_VAL(fsync - 1) << + CS42L42_FSYNC_PULSE_WIDTH_SHIFT); + snd_soc_component_update_bits(component, + CS42L42_FSYNC_PW_UPPER, + CS42L42_FSYNC_PULSE_WIDTH_MASK, + CS42L42_FRAC1_VAL(fsync - 1) << + CS42L42_FSYNC_PULSE_WIDTH_SHIFT); + + return 0; +} + +static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u32 asp_cfg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFM: + asp_cfg_val |= CS42L42_ASP_MASTER_MODE << + CS42L42_ASP_MODE_SHIFT; + break; + case SND_SOC_DAIFMT_CBS_CFS: + asp_cfg_val |= CS42L42_ASP_SLAVE_MODE << + CS42L42_ASP_MODE_SHIFT; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* + * 5050 mode, frame starts on falling edge of LRCLK, + * frame delayed by 1.0 SCLKs + */ + snd_soc_component_update_bits(component, + CS42L42_ASP_FRM_CFG, + CS42L42_ASP_STP_MASK | + CS42L42_ASP_5050_MASK | + CS42L42_ASP_FSD_MASK, + CS42L42_ASP_5050_MASK | + (CS42L42_ASP_FSD_1_0 << + CS42L42_ASP_FSD_SHIFT)); + break; + default: + return -EINVAL; + } + + /* Bitclock/frame inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + asp_cfg_val |= CS42L42_ASP_SCPOL_NOR << CS42L42_ASP_SCPOL_SHIFT; + break; + case SND_SOC_DAIFMT_NB_IF: + asp_cfg_val |= CS42L42_ASP_SCPOL_NOR << CS42L42_ASP_SCPOL_SHIFT; + asp_cfg_val |= CS42L42_ASP_LCPOL_INV << CS42L42_ASP_LCPOL_SHIFT; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + asp_cfg_val |= CS42L42_ASP_LCPOL_INV << CS42L42_ASP_LCPOL_SHIFT; + break; + } + + snd_soc_component_update_bits(component, CS42L42_ASP_CLK_CFG, CS42L42_ASP_MODE_MASK | + CS42L42_ASP_SCPOL_MASK | + CS42L42_ASP_LCPOL_MASK, + asp_cfg_val); + + return 0; +} + +static int cs42l42_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + + /* + * Sample rates < 44.1 kHz would produce an out-of-range SCLK with + * a standard I2S frame. If the machine driver sets SCLK it must be + * legal. + */ + if (cs42l42->sclk) + return 0; + + /* Machine driver has not set a SCLK, limit bottom end to 44.1 kHz */ + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + 44100, 96000); +} + +static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + unsigned int channels = params_channels(params); + unsigned int width = (params_width(params) / 8) - 1; + unsigned int sample_rate = params_rate(params); + unsigned int slot_width = 0; + unsigned int val = 0; + unsigned int bclk; + int ret; + + if (cs42l42->bclk_ratio) { + /* machine driver has set the BCLK/samp-rate ratio */ + bclk = cs42l42->bclk_ratio * params_rate(params); + } else if (cs42l42->sclk) { + /* machine driver has set the SCLK */ + bclk = cs42l42->sclk; + } else { + /* + * Assume 24-bit samples are in 32-bit slots, to prevent SCLK being + * more than assumed (which would result in overclocking). + */ + if (params_width(params) == 24) + slot_width = 32; + + /* I2S frame always has multiple of 2 channels */ + bclk = snd_soc_tdm_params_to_bclk(params, slot_width, 0, 2); + } + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + /* channel 2 on high LRCLK */ + val = CS42L42_ASP_TX_CH2_AP_MASK | + (width << CS42L42_ASP_TX_CH2_RES_SHIFT) | + (width << CS42L42_ASP_TX_CH1_RES_SHIFT); + + snd_soc_component_update_bits(component, CS42L42_ASP_TX_CH_AP_RES, + CS42L42_ASP_TX_CH1_AP_MASK | CS42L42_ASP_TX_CH2_AP_MASK | + CS42L42_ASP_TX_CH2_RES_MASK | CS42L42_ASP_TX_CH1_RES_MASK, val); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + val |= width << CS42L42_ASP_RX_CH_RES_SHIFT; + /* channel 1 on low LRCLK */ + snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_CH1_AP_RES, + CS42L42_ASP_RX_CH_AP_MASK | + CS42L42_ASP_RX_CH_RES_MASK, val); + /* Channel 2 on high LRCLK */ + val |= CS42L42_ASP_RX_CH_AP_HI << CS42L42_ASP_RX_CH_AP_SHIFT; + snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_CH2_AP_RES, + CS42L42_ASP_RX_CH_AP_MASK | + CS42L42_ASP_RX_CH_RES_MASK, val); + + /* Channel B comes from the last active channel */ + snd_soc_component_update_bits(component, CS42L42_SP_RX_CH_SEL, + CS42L42_SP_RX_CHB_SEL_MASK, + (channels - 1) << CS42L42_SP_RX_CHB_SEL_SHIFT); + + /* Both LRCLK slots must be enabled */ + snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_EN, + CS42L42_ASP_RX0_CH_EN_MASK, + BIT(CS42L42_ASP_RX0_CH1_SHIFT) | + BIT(CS42L42_ASP_RX0_CH2_SHIFT)); + break; + default: + break; + } + + ret = cs42l42_pll_config(component, bclk, sample_rate); + if (ret) + return ret; + + ret = cs42l42_asp_config(component, bclk, sample_rate); + if (ret) + return ret; + + cs42l42_src_config(component, sample_rate); + + return 0; +} + +static int cs42l42_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + int i; + + if (freq == 0) { + cs42l42->sclk = 0; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { + if (pll_ratio_table[i].sclk == freq) { + cs42l42->sclk = freq; + return 0; + } + } + + dev_err(component->dev, "SCLK %u not supported\n", freq); + + return -EINVAL; +} + +static int cs42l42_set_bclk_ratio(struct snd_soc_dai *dai, + unsigned int bclk_ratio) +{ + struct snd_soc_component *component = dai->component; + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + + cs42l42->bclk_ratio = bclk_ratio; + + return 0; +} + +int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component); + unsigned int regval; + int ret; + + if (mute) { + /* Mute the headphone */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_component_update_bits(component, CS42L42_HP_CTL, + CS42L42_HP_ANA_AMUTE_MASK | + CS42L42_HP_ANA_BMUTE_MASK, + CS42L42_HP_ANA_AMUTE_MASK | + CS42L42_HP_ANA_BMUTE_MASK); + + cs42l42->stream_use &= ~(1 << stream); + if (!cs42l42->stream_use) { + /* + * Switch to the internal oscillator. + * SCLK must remain running until after this clock switch. + * Without a source of clock the I2C bus doesn't work. + */ + regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_osc_seq, + ARRAY_SIZE(cs42l42_to_osc_seq)); + + /* Must disconnect PLL before stopping it */ + snd_soc_component_update_bits(component, + CS42L42_MCLK_SRC_SEL, + CS42L42_MCLK_SRC_SEL_MASK, + 0); + usleep_range(100, 200); + + snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, + CS42L42_PLL_START_MASK, 0); + } + } else { + if (!cs42l42->stream_use) { + /* SCLK must be running before codec unmute. + * + * PLL must not be started with ADC and HP both off + * otherwise the FILT+ supply will not charge properly. + * DAPM widgets power-up before stream unmute so at least + * one of the "DAC" or "ADC" widgets will already have + * powered-up. + */ + if (pll_ratio_table[cs42l42->pll_config].mclk_src_sel) { + snd_soc_component_update_bits(component, CS42L42_PLL_CTL1, + CS42L42_PLL_START_MASK, 1); + + if (pll_ratio_table[cs42l42->pll_config].n > 1) { + usleep_range(CS42L42_PLL_DIVOUT_TIME_US, + CS42L42_PLL_DIVOUT_TIME_US * 2); + regval = pll_ratio_table[cs42l42->pll_config].pll_divout; + snd_soc_component_update_bits(component, CS42L42_PLL_CTL3, + CS42L42_PLL_DIVOUT_MASK, + regval << + CS42L42_PLL_DIVOUT_SHIFT); + } + + ret = regmap_read_poll_timeout(cs42l42->regmap, + CS42L42_PLL_LOCK_STATUS, + regval, + (regval & 1), + CS42L42_PLL_LOCK_POLL_US, + CS42L42_PLL_LOCK_TIMEOUT_US); + if (ret < 0) + dev_warn(component->dev, "PLL failed to lock: %d\n", ret); + + /* PLL must be running to drive glitchless switch logic */ + snd_soc_component_update_bits(component, + CS42L42_MCLK_SRC_SEL, + CS42L42_MCLK_SRC_SEL_MASK, + CS42L42_MCLK_SRC_SEL_MASK); + } + + /* Mark SCLK as present, turn off internal oscillator */ + regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_sclk_seq, + ARRAY_SIZE(cs42l42_to_sclk_seq)); + } + cs42l42->stream_use |= 1 << stream; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* Un-mute the headphone */ + snd_soc_component_update_bits(component, CS42L42_HP_CTL, + CS42L42_HP_ANA_AMUTE_MASK | + CS42L42_HP_ANA_BMUTE_MASK, + 0); + } + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs42l42_mute_stream, SND_SOC_CS42L42_CORE); + +#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops cs42l42_ops = { + .startup = cs42l42_dai_startup, + .hw_params = cs42l42_pcm_hw_params, + .set_fmt = cs42l42_set_dai_fmt, + .set_sysclk = cs42l42_set_sysclk, + .set_bclk_ratio = cs42l42_set_bclk_ratio, + .mute_stream = cs42l42_mute_stream, +}; + +struct snd_soc_dai_driver cs42l42_dai = { + .name = "cs42l42", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS42L42_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS42L42_FORMATS, + }, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + .ops = &cs42l42_ops, +}; +EXPORT_SYMBOL_NS_GPL(cs42l42_dai, SND_SOC_CS42L42_CORE); + +static void cs42l42_manual_hs_type_detect(struct cs42l42_private *cs42l42) +{ + unsigned int hs_det_status; + unsigned int hs_det_comp1; + unsigned int hs_det_comp2; + unsigned int hs_det_sw; + + /* Set hs detect to manual, active mode */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (1 << CS42L42_HSDET_CTRL_SHIFT) | + (0 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); + + /* Configure HS DET comparator reference levels. */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL1, + CS42L42_HSDET_COMP1_LVL_MASK | + CS42L42_HSDET_COMP2_LVL_MASK, + (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) | + (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT)); + + /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */ + regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1); + + msleep(100); + + regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status); + + hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> + CS42L42_HSDET_COMP1_OUT_SHIFT; + hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> + CS42L42_HSDET_COMP2_OUT_SHIFT; + + /* Close the SW_HSB_HS3 switch for a Type 2 headset. */ + regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2); + + msleep(100); + + regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status); + + hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >> + CS42L42_HSDET_COMP1_OUT_SHIFT) << 1; + hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >> + CS42L42_HSDET_COMP2_OUT_SHIFT) << 1; + + /* Use Comparator 1 with 1.25V Threshold. */ + switch (hs_det_comp1) { + case CS42L42_HSDET_COMP_TYPE1: + cs42l42->hs_type = CS42L42_PLUG_CTIA; + hs_det_sw = CS42L42_HSDET_SW_TYPE1; + break; + case CS42L42_HSDET_COMP_TYPE2: + cs42l42->hs_type = CS42L42_PLUG_OMTP; + hs_det_sw = CS42L42_HSDET_SW_TYPE2; + break; + default: + /* Fallback to Comparator 2 with 1.75V Threshold. */ + switch (hs_det_comp2) { + case CS42L42_HSDET_COMP_TYPE1: + cs42l42->hs_type = CS42L42_PLUG_CTIA; + hs_det_sw = CS42L42_HSDET_SW_TYPE1; + break; + case CS42L42_HSDET_COMP_TYPE2: + cs42l42->hs_type = CS42L42_PLUG_OMTP; + hs_det_sw = CS42L42_HSDET_SW_TYPE2; + break; + /* Detect Type 3 and Type 4 Headsets as Headphones */ + default: + cs42l42->hs_type = CS42L42_PLUG_HEADPHONE; + hs_det_sw = CS42L42_HSDET_SW_TYPE3; + break; + } + } + + /* Set Switches */ + regmap_write(cs42l42->regmap, CS42L42_HS_SWITCH_CTL, hs_det_sw); + + /* Set HSDET mode to Manual—Disabled */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (0 << CS42L42_HSDET_CTRL_SHIFT) | + (0 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (0 << CS42L42_HSDET_AUTO_TIME_SHIFT)); + + /* Configure HS DET comparator reference levels. */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL1, + CS42L42_HSDET_COMP1_LVL_MASK | + CS42L42_HSDET_COMP2_LVL_MASK, + (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) | + (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT)); +} + +static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42) +{ + unsigned int hs_det_status; + unsigned int int_status; + + /* Read and save the hs detection result */ + regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status); + + /* Mask the auto detect interrupt */ + regmap_update_bits(cs42l42->regmap, + CS42L42_CODEC_INT_MASK, + CS42L42_PDN_DONE_MASK | + CS42L42_HSDET_AUTO_DONE_MASK, + (1 << CS42L42_PDN_DONE_SHIFT) | + (1 << CS42L42_HSDET_AUTO_DONE_SHIFT)); + + + cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >> + CS42L42_HSDET_TYPE_SHIFT; + + /* Set hs detect to automatic, disabled mode */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (2 << CS42L42_HSDET_CTRL_SHIFT) | + (2 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); + + /* Run Manual detection if auto detect has not found a headset. + * We Re-Run with Manual Detection if the original detection was invalid or headphones, + * to ensure that a headset mic is detected in all cases. + */ + if (cs42l42->hs_type == CS42L42_PLUG_INVALID || + cs42l42->hs_type == CS42L42_PLUG_HEADPHONE) { + dev_dbg(cs42l42->dev, "Running Manual Detection Fallback\n"); + cs42l42_manual_hs_type_detect(cs42l42); + } + + /* Set up button detection */ + if ((cs42l42->hs_type == CS42L42_PLUG_CTIA) || + (cs42l42->hs_type == CS42L42_PLUG_OMTP)) { + /* Set auto HS bias settings to default */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSBIAS_SC_AUTOCTL, + CS42L42_HSBIAS_SENSE_EN_MASK | + CS42L42_AUTO_HSBIAS_HIZ_MASK | + CS42L42_TIP_SENSE_EN_MASK | + CS42L42_HSBIAS_SENSE_TRIP_MASK, + (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) | + (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) | + (0 << CS42L42_TIP_SENSE_EN_SHIFT) | + (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT)); + + /* Set up hs detect level sensitivity */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MIC_DET_CTL1, + CS42L42_LATCH_TO_VP_MASK | + CS42L42_EVENT_STAT_SEL_MASK | + CS42L42_HS_DET_LEVEL_MASK, + (1 << CS42L42_LATCH_TO_VP_SHIFT) | + (0 << CS42L42_EVENT_STAT_SEL_SHIFT) | + (cs42l42->bias_thresholds[0] << + CS42L42_HS_DET_LEVEL_SHIFT)); + + /* Set auto HS bias settings to default */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSBIAS_SC_AUTOCTL, + CS42L42_HSBIAS_SENSE_EN_MASK | + CS42L42_AUTO_HSBIAS_HIZ_MASK | + CS42L42_TIP_SENSE_EN_MASK | + CS42L42_HSBIAS_SENSE_TRIP_MASK, + (cs42l42->hs_bias_sense_en << CS42L42_HSBIAS_SENSE_EN_SHIFT) | + (1 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) | + (0 << CS42L42_TIP_SENSE_EN_SHIFT) | + (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT)); + + /* Turn on level detect circuitry */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MISC_DET_CTL, + CS42L42_HSBIAS_CTL_MASK | + CS42L42_PDN_MIC_LVL_DET_MASK, + (3 << CS42L42_HSBIAS_CTL_SHIFT) | + (0 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); + + msleep(cs42l42->btn_det_init_dbnce); + + /* Clear any button interrupts before unmasking them */ + regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2, + &int_status); + + /* Unmask button detect interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_DET_INT2_MASK, + CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK | + CS42L42_M_SHORT_RLS_MASK | + CS42L42_M_SHORT_DET_MASK, + (0 << CS42L42_M_DETECT_TF_SHIFT) | + (0 << CS42L42_M_DETECT_FT_SHIFT) | + (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) | + (1 << CS42L42_M_SHORT_RLS_SHIFT) | + (1 << CS42L42_M_SHORT_DET_SHIFT)); + } else { + /* Make sure button detect and HS bias circuits are off */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MISC_DET_CTL, + CS42L42_HSBIAS_CTL_MASK | + CS42L42_PDN_MIC_LVL_DET_MASK, + (1 << CS42L42_HSBIAS_CTL_SHIFT) | + (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); + } + + regmap_update_bits(cs42l42->regmap, + CS42L42_DAC_CTL2, + CS42L42_HPOUT_PULLDOWN_MASK | + CS42L42_HPOUT_LOAD_MASK | + CS42L42_HPOUT_CLAMP_MASK | + CS42L42_DAC_HPF_EN_MASK | + CS42L42_DAC_MON_EN_MASK, + (0 << CS42L42_HPOUT_PULLDOWN_SHIFT) | + (0 << CS42L42_HPOUT_LOAD_SHIFT) | + (0 << CS42L42_HPOUT_CLAMP_SHIFT) | + (1 << CS42L42_DAC_HPF_EN_SHIFT) | + (0 << CS42L42_DAC_MON_EN_SHIFT)); + + /* Unmask tip sense interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_TSRS_PLUG_INT_MASK, + CS42L42_TS_PLUG_MASK | + CS42L42_TS_UNPLUG_MASK, + (0 << CS42L42_TS_PLUG_SHIFT) | + (0 << CS42L42_TS_UNPLUG_SHIFT)); +} + +static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42) +{ + /* Mask tip sense interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_TSRS_PLUG_INT_MASK, + CS42L42_TS_PLUG_MASK | + CS42L42_TS_UNPLUG_MASK, + (1 << CS42L42_TS_PLUG_SHIFT) | + (1 << CS42L42_TS_UNPLUG_SHIFT)); + + /* Make sure button detect and HS bias circuits are off */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MISC_DET_CTL, + CS42L42_HSBIAS_CTL_MASK | + CS42L42_PDN_MIC_LVL_DET_MASK, + (1 << CS42L42_HSBIAS_CTL_SHIFT) | + (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); + + /* Set auto HS bias settings to default */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSBIAS_SC_AUTOCTL, + CS42L42_HSBIAS_SENSE_EN_MASK | + CS42L42_AUTO_HSBIAS_HIZ_MASK | + CS42L42_TIP_SENSE_EN_MASK | + CS42L42_HSBIAS_SENSE_TRIP_MASK, + (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) | + (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) | + (0 << CS42L42_TIP_SENSE_EN_SHIFT) | + (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT)); + + /* Set hs detect to manual, disabled mode */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (0 << CS42L42_HSDET_CTRL_SHIFT) | + (2 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); + + regmap_update_bits(cs42l42->regmap, + CS42L42_DAC_CTL2, + CS42L42_HPOUT_PULLDOWN_MASK | + CS42L42_HPOUT_LOAD_MASK | + CS42L42_HPOUT_CLAMP_MASK | + CS42L42_DAC_HPF_EN_MASK | + CS42L42_DAC_MON_EN_MASK, + (8 << CS42L42_HPOUT_PULLDOWN_SHIFT) | + (0 << CS42L42_HPOUT_LOAD_SHIFT) | + (1 << CS42L42_HPOUT_CLAMP_SHIFT) | + (1 << CS42L42_DAC_HPF_EN_SHIFT) | + (1 << CS42L42_DAC_MON_EN_SHIFT)); + + /* Power up HS bias to 2.7V */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MISC_DET_CTL, + CS42L42_HSBIAS_CTL_MASK | + CS42L42_PDN_MIC_LVL_DET_MASK, + (3 << CS42L42_HSBIAS_CTL_SHIFT) | + (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); + + /* Wait for HS bias to ramp up */ + msleep(cs42l42->hs_bias_ramp_time); + + /* Unmask auto detect interrupt */ + regmap_update_bits(cs42l42->regmap, + CS42L42_CODEC_INT_MASK, + CS42L42_PDN_DONE_MASK | + CS42L42_HSDET_AUTO_DONE_MASK, + (1 << CS42L42_PDN_DONE_SHIFT) | + (0 << CS42L42_HSDET_AUTO_DONE_SHIFT)); + + /* Set hs detect to automatic, enabled mode */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (3 << CS42L42_HSDET_CTRL_SHIFT) | + (2 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); +} + +static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42) +{ + /* Mask button detect interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_DET_INT2_MASK, + CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK | + CS42L42_M_SHORT_RLS_MASK | + CS42L42_M_SHORT_DET_MASK, + (1 << CS42L42_M_DETECT_TF_SHIFT) | + (1 << CS42L42_M_DETECT_FT_SHIFT) | + (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) | + (1 << CS42L42_M_SHORT_RLS_SHIFT) | + (1 << CS42L42_M_SHORT_DET_SHIFT)); + + /* Ground HS bias */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MISC_DET_CTL, + CS42L42_HSBIAS_CTL_MASK | + CS42L42_PDN_MIC_LVL_DET_MASK, + (1 << CS42L42_HSBIAS_CTL_SHIFT) | + (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)); + + /* Set auto HS bias settings to default */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSBIAS_SC_AUTOCTL, + CS42L42_HSBIAS_SENSE_EN_MASK | + CS42L42_AUTO_HSBIAS_HIZ_MASK | + CS42L42_TIP_SENSE_EN_MASK | + CS42L42_HSBIAS_SENSE_TRIP_MASK, + (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) | + (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) | + (0 << CS42L42_TIP_SENSE_EN_SHIFT) | + (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT)); + + /* Set hs detect to manual, disabled mode */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HSDET_CTL2, + CS42L42_HSDET_CTRL_MASK | + CS42L42_HSDET_SET_MASK | + CS42L42_HSBIAS_REF_MASK | + CS42L42_HSDET_AUTO_TIME_MASK, + (0 << CS42L42_HSDET_CTRL_SHIFT) | + (2 << CS42L42_HSDET_SET_SHIFT) | + (0 << CS42L42_HSBIAS_REF_SHIFT) | + (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)); +} + +static int cs42l42_handle_button_press(struct cs42l42_private *cs42l42) +{ + int bias_level; + unsigned int detect_status; + + /* Mask button detect interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_DET_INT2_MASK, + CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK | + CS42L42_M_SHORT_RLS_MASK | + CS42L42_M_SHORT_DET_MASK, + (1 << CS42L42_M_DETECT_TF_SHIFT) | + (1 << CS42L42_M_DETECT_FT_SHIFT) | + (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) | + (1 << CS42L42_M_SHORT_RLS_SHIFT) | + (1 << CS42L42_M_SHORT_DET_SHIFT)); + + usleep_range(cs42l42->btn_det_event_dbnce * 1000, + cs42l42->btn_det_event_dbnce * 2000); + + /* Test all 4 level detect biases */ + bias_level = 1; + do { + /* Adjust button detect level sensitivity */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MIC_DET_CTL1, + CS42L42_LATCH_TO_VP_MASK | + CS42L42_EVENT_STAT_SEL_MASK | + CS42L42_HS_DET_LEVEL_MASK, + (1 << CS42L42_LATCH_TO_VP_SHIFT) | + (0 << CS42L42_EVENT_STAT_SEL_SHIFT) | + (cs42l42->bias_thresholds[bias_level] << + CS42L42_HS_DET_LEVEL_SHIFT)); + + regmap_read(cs42l42->regmap, CS42L42_DET_STATUS2, + &detect_status); + } while ((detect_status & CS42L42_HS_TRUE_MASK) && + (++bias_level < CS42L42_NUM_BIASES)); + + switch (bias_level) { + case 1: /* Function C button press */ + bias_level = SND_JACK_BTN_2; + dev_dbg(cs42l42->dev, "Function C button press\n"); + break; + case 2: /* Function B button press */ + bias_level = SND_JACK_BTN_1; + dev_dbg(cs42l42->dev, "Function B button press\n"); + break; + case 3: /* Function D button press */ + bias_level = SND_JACK_BTN_3; + dev_dbg(cs42l42->dev, "Function D button press\n"); + break; + case 4: /* Function A button press */ + bias_level = SND_JACK_BTN_0; + dev_dbg(cs42l42->dev, "Function A button press\n"); + break; + default: + bias_level = 0; + break; + } + + /* Set button detect level sensitivity back to default */ + regmap_update_bits(cs42l42->regmap, + CS42L42_MIC_DET_CTL1, + CS42L42_LATCH_TO_VP_MASK | + CS42L42_EVENT_STAT_SEL_MASK | + CS42L42_HS_DET_LEVEL_MASK, + (1 << CS42L42_LATCH_TO_VP_SHIFT) | + (0 << CS42L42_EVENT_STAT_SEL_SHIFT) | + (cs42l42->bias_thresholds[0] << CS42L42_HS_DET_LEVEL_SHIFT)); + + /* Clear any button interrupts before unmasking them */ + regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2, + &detect_status); + + /* Unmask button detect interrupts */ + regmap_update_bits(cs42l42->regmap, + CS42L42_DET_INT2_MASK, + CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK | + CS42L42_M_SHORT_RLS_MASK | + CS42L42_M_SHORT_DET_MASK, + (0 << CS42L42_M_DETECT_TF_SHIFT) | + (0 << CS42L42_M_DETECT_FT_SHIFT) | + (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) | + (1 << CS42L42_M_SHORT_RLS_SHIFT) | + (1 << CS42L42_M_SHORT_DET_SHIFT)); + + return bias_level; +} + +struct cs42l42_irq_params { + u16 status_addr; + u16 mask_addr; + u8 mask; +}; + +static const struct cs42l42_irq_params irq_params_table[] = { + {CS42L42_ADC_OVFL_STATUS, CS42L42_ADC_OVFL_INT_MASK, + CS42L42_ADC_OVFL_VAL_MASK}, + {CS42L42_MIXER_STATUS, CS42L42_MIXER_INT_MASK, + CS42L42_MIXER_VAL_MASK}, + {CS42L42_SRC_STATUS, CS42L42_SRC_INT_MASK, + CS42L42_SRC_VAL_MASK}, + {CS42L42_ASP_RX_STATUS, CS42L42_ASP_RX_INT_MASK, + CS42L42_ASP_RX_VAL_MASK}, + {CS42L42_ASP_TX_STATUS, CS42L42_ASP_TX_INT_MASK, + CS42L42_ASP_TX_VAL_MASK}, + {CS42L42_CODEC_STATUS, CS42L42_CODEC_INT_MASK, + CS42L42_CODEC_VAL_MASK}, + {CS42L42_DET_INT_STATUS1, CS42L42_DET_INT1_MASK, + CS42L42_DET_INT_VAL1_MASK}, + {CS42L42_DET_INT_STATUS2, CS42L42_DET_INT2_MASK, + CS42L42_DET_INT_VAL2_MASK}, + {CS42L42_SRCPL_INT_STATUS, CS42L42_SRCPL_INT_MASK, + CS42L42_SRCPL_VAL_MASK}, + {CS42L42_VPMON_STATUS, CS42L42_VPMON_INT_MASK, + CS42L42_VPMON_VAL_MASK}, + {CS42L42_PLL_LOCK_STATUS, CS42L42_PLL_LOCK_INT_MASK, + CS42L42_PLL_LOCK_VAL_MASK}, + {CS42L42_TSRS_PLUG_STATUS, CS42L42_TSRS_PLUG_INT_MASK, + CS42L42_TSRS_PLUG_VAL_MASK} +}; + +irqreturn_t cs42l42_irq_thread(int irq, void *data) +{ + struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data; + unsigned int stickies[12]; + unsigned int masks[12]; + unsigned int current_plug_status; + unsigned int current_button_status; + unsigned int i; + + pm_runtime_get_sync(cs42l42->dev); + mutex_lock(&cs42l42->irq_lock); + if (cs42l42->suspended || !cs42l42->init_done) { + mutex_unlock(&cs42l42->irq_lock); + pm_runtime_put_autosuspend(cs42l42->dev); + return IRQ_NONE; + } + + /* Read sticky registers to clear interurpt */ + for (i = 0; i < ARRAY_SIZE(stickies); i++) { + regmap_read(cs42l42->regmap, irq_params_table[i].status_addr, + &(stickies[i])); + regmap_read(cs42l42->regmap, irq_params_table[i].mask_addr, + &(masks[i])); + stickies[i] = stickies[i] & (~masks[i]) & + irq_params_table[i].mask; + } + + /* Read tip sense status before handling type detect */ + current_plug_status = (stickies[11] & + (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) >> + CS42L42_TS_PLUG_SHIFT; + + /* Read button sense status */ + current_button_status = stickies[7] & + (CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK); + + /* + * Check auto-detect status. Don't assume a previous unplug event has + * cleared the flags. If the jack is unplugged and plugged during + * system suspend there won't have been an unplug event. + */ + if ((~masks[5]) & irq_params_table[5].mask) { + if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) { + cs42l42_process_hs_type_detect(cs42l42); + switch (cs42l42->hs_type) { + case CS42L42_PLUG_CTIA: + case CS42L42_PLUG_OMTP: + snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADSET, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + break; + case CS42L42_PLUG_HEADPHONE: + snd_soc_jack_report(cs42l42->jack, SND_JACK_HEADPHONE, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + break; + default: + break; + } + dev_dbg(cs42l42->dev, "Auto detect done (%d)\n", cs42l42->hs_type); + } + } + + /* Check tip sense status */ + if ((~masks[11]) & irq_params_table[11].mask) { + switch (current_plug_status) { + case CS42L42_TS_PLUG: + if (cs42l42->plug_state != CS42L42_TS_PLUG) { + cs42l42->plug_state = CS42L42_TS_PLUG; + cs42l42_init_hs_type_detect(cs42l42); + } + break; + + case CS42L42_TS_UNPLUG: + if (cs42l42->plug_state != CS42L42_TS_UNPLUG) { + cs42l42->plug_state = CS42L42_TS_UNPLUG; + cs42l42_cancel_hs_type_detect(cs42l42); + + snd_soc_jack_report(cs42l42->jack, 0, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + dev_dbg(cs42l42->dev, "Unplug event\n"); + } + break; + + default: + cs42l42->plug_state = CS42L42_TS_TRANS; + } + } + + /* Check button detect status */ + if (cs42l42->plug_state == CS42L42_TS_PLUG && ((~masks[7]) & irq_params_table[7].mask)) { + if (!(current_button_status & + CS42L42_M_HSBIAS_HIZ_MASK)) { + + if (current_button_status & CS42L42_M_DETECT_TF_MASK) { + dev_dbg(cs42l42->dev, "Button released\n"); + snd_soc_jack_report(cs42l42->jack, 0, + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else if (current_button_status & CS42L42_M_DETECT_FT_MASK) { + snd_soc_jack_report(cs42l42->jack, + cs42l42_handle_button_press(cs42l42), + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } + } + } + + mutex_unlock(&cs42l42->irq_lock); + pm_runtime_mark_last_busy(cs42l42->dev); + pm_runtime_put_autosuspend(cs42l42->dev); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, SND_SOC_CS42L42_CORE); + +static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42) +{ + regmap_update_bits(cs42l42->regmap, CS42L42_ADC_OVFL_INT_MASK, + CS42L42_ADC_OVFL_MASK, + (1 << CS42L42_ADC_OVFL_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_MIXER_INT_MASK, + CS42L42_MIX_CHB_OVFL_MASK | + CS42L42_MIX_CHA_OVFL_MASK | + CS42L42_EQ_OVFL_MASK | + CS42L42_EQ_BIQUAD_OVFL_MASK, + (1 << CS42L42_MIX_CHB_OVFL_SHIFT) | + (1 << CS42L42_MIX_CHA_OVFL_SHIFT) | + (1 << CS42L42_EQ_OVFL_SHIFT) | + (1 << CS42L42_EQ_BIQUAD_OVFL_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_SRC_INT_MASK, + CS42L42_SRC_ILK_MASK | + CS42L42_SRC_OLK_MASK | + CS42L42_SRC_IUNLK_MASK | + CS42L42_SRC_OUNLK_MASK, + (1 << CS42L42_SRC_ILK_SHIFT) | + (1 << CS42L42_SRC_OLK_SHIFT) | + (1 << CS42L42_SRC_IUNLK_SHIFT) | + (1 << CS42L42_SRC_OUNLK_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_ASP_RX_INT_MASK, + CS42L42_ASPRX_NOLRCK_MASK | + CS42L42_ASPRX_EARLY_MASK | + CS42L42_ASPRX_LATE_MASK | + CS42L42_ASPRX_ERROR_MASK | + CS42L42_ASPRX_OVLD_MASK, + (1 << CS42L42_ASPRX_NOLRCK_SHIFT) | + (1 << CS42L42_ASPRX_EARLY_SHIFT) | + (1 << CS42L42_ASPRX_LATE_SHIFT) | + (1 << CS42L42_ASPRX_ERROR_SHIFT) | + (1 << CS42L42_ASPRX_OVLD_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_ASP_TX_INT_MASK, + CS42L42_ASPTX_NOLRCK_MASK | + CS42L42_ASPTX_EARLY_MASK | + CS42L42_ASPTX_LATE_MASK | + CS42L42_ASPTX_SMERROR_MASK, + (1 << CS42L42_ASPTX_NOLRCK_SHIFT) | + (1 << CS42L42_ASPTX_EARLY_SHIFT) | + (1 << CS42L42_ASPTX_LATE_SHIFT) | + (1 << CS42L42_ASPTX_SMERROR_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_CODEC_INT_MASK, + CS42L42_PDN_DONE_MASK | + CS42L42_HSDET_AUTO_DONE_MASK, + (1 << CS42L42_PDN_DONE_SHIFT) | + (1 << CS42L42_HSDET_AUTO_DONE_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_SRCPL_INT_MASK, + CS42L42_SRCPL_ADC_LK_MASK | + CS42L42_SRCPL_DAC_LK_MASK | + CS42L42_SRCPL_ADC_UNLK_MASK | + CS42L42_SRCPL_DAC_UNLK_MASK, + (1 << CS42L42_SRCPL_ADC_LK_SHIFT) | + (1 << CS42L42_SRCPL_DAC_LK_SHIFT) | + (1 << CS42L42_SRCPL_ADC_UNLK_SHIFT) | + (1 << CS42L42_SRCPL_DAC_UNLK_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_DET_INT1_MASK, + CS42L42_TIP_SENSE_UNPLUG_MASK | + CS42L42_TIP_SENSE_PLUG_MASK | + CS42L42_HSBIAS_SENSE_MASK, + (1 << CS42L42_TIP_SENSE_UNPLUG_SHIFT) | + (1 << CS42L42_TIP_SENSE_PLUG_SHIFT) | + (1 << CS42L42_HSBIAS_SENSE_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_DET_INT2_MASK, + CS42L42_M_DETECT_TF_MASK | + CS42L42_M_DETECT_FT_MASK | + CS42L42_M_HSBIAS_HIZ_MASK | + CS42L42_M_SHORT_RLS_MASK | + CS42L42_M_SHORT_DET_MASK, + (1 << CS42L42_M_DETECT_TF_SHIFT) | + (1 << CS42L42_M_DETECT_FT_SHIFT) | + (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) | + (1 << CS42L42_M_SHORT_RLS_SHIFT) | + (1 << CS42L42_M_SHORT_DET_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_VPMON_INT_MASK, + CS42L42_VPMON_MASK, + (1 << CS42L42_VPMON_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_PLL_LOCK_INT_MASK, + CS42L42_PLL_LOCK_MASK, + (1 << CS42L42_PLL_LOCK_SHIFT)); + + regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, + CS42L42_RS_PLUG_MASK | + CS42L42_RS_UNPLUG_MASK | + CS42L42_TS_PLUG_MASK | + CS42L42_TS_UNPLUG_MASK, + (1 << CS42L42_RS_PLUG_SHIFT) | + (1 << CS42L42_RS_UNPLUG_SHIFT) | + (0 << CS42L42_TS_PLUG_SHIFT) | + (0 << CS42L42_TS_UNPLUG_SHIFT)); +} + +static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42) +{ + unsigned int reg; + + cs42l42->hs_type = CS42L42_PLUG_INVALID; + + /* + * DETECT_MODE must always be 0 with ADC and HP both off otherwise the + * FILT+ supply will not charge properly. + */ + regmap_update_bits(cs42l42->regmap, CS42L42_MISC_DET_CTL, + CS42L42_DETECT_MODE_MASK, 0); + + /* Latch analog controls to VP power domain */ + regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1, + CS42L42_LATCH_TO_VP_MASK | + CS42L42_EVENT_STAT_SEL_MASK | + CS42L42_HS_DET_LEVEL_MASK, + (1 << CS42L42_LATCH_TO_VP_SHIFT) | + (0 << CS42L42_EVENT_STAT_SEL_SHIFT) | + (cs42l42->bias_thresholds[0] << + CS42L42_HS_DET_LEVEL_SHIFT)); + + /* Remove ground noise-suppression clamps */ + regmap_update_bits(cs42l42->regmap, + CS42L42_HS_CLAMP_DISABLE, + CS42L42_HS_CLAMP_DISABLE_MASK, + (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT)); + + /* Enable the tip sense circuit */ + regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL, + CS42L42_TS_INV_MASK, CS42L42_TS_INV_MASK); + + regmap_update_bits(cs42l42->regmap, CS42L42_TIPSENSE_CTL, + CS42L42_TIP_SENSE_CTRL_MASK | + CS42L42_TIP_SENSE_INV_MASK | + CS42L42_TIP_SENSE_DEBOUNCE_MASK, + (3 << CS42L42_TIP_SENSE_CTRL_SHIFT) | + (!cs42l42->ts_inv << CS42L42_TIP_SENSE_INV_SHIFT) | + (2 << CS42L42_TIP_SENSE_DEBOUNCE_SHIFT)); + + /* Save the initial status of the tip sense */ + regmap_read(cs42l42->regmap, + CS42L42_TSRS_PLUG_STATUS, + ®); + cs42l42->plug_state = (((char) reg) & + (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) >> + CS42L42_TS_PLUG_SHIFT; +} + +static const unsigned int threshold_defaults[] = { + CS42L42_HS_DET_LEVEL_15, + CS42L42_HS_DET_LEVEL_8, + CS42L42_HS_DET_LEVEL_4, + CS42L42_HS_DET_LEVEL_1 +}; + +static int cs42l42_handle_device_data(struct device *dev, + struct cs42l42_private *cs42l42) +{ + unsigned int val; + u32 thresholds[CS42L42_NUM_BIASES]; + int ret; + int i; + + ret = device_property_read_u32(dev, "cirrus,ts-inv", &val); + if (!ret) { + switch (val) { + case CS42L42_TS_INV_EN: + case CS42L42_TS_INV_DIS: + cs42l42->ts_inv = val; + break; + default: + dev_err(dev, + "Wrong cirrus,ts-inv DT value %d\n", + val); + cs42l42->ts_inv = CS42L42_TS_INV_DIS; + } + } else { + cs42l42->ts_inv = CS42L42_TS_INV_DIS; + } + + ret = device_property_read_u32(dev, "cirrus,ts-dbnc-rise", &val); + if (!ret) { + switch (val) { + case CS42L42_TS_DBNCE_0: + case CS42L42_TS_DBNCE_125: + case CS42L42_TS_DBNCE_250: + case CS42L42_TS_DBNCE_500: + case CS42L42_TS_DBNCE_750: + case CS42L42_TS_DBNCE_1000: + case CS42L42_TS_DBNCE_1250: + case CS42L42_TS_DBNCE_1500: + cs42l42->ts_dbnc_rise = val; + break; + default: + dev_err(dev, + "Wrong cirrus,ts-dbnc-rise DT value %d\n", + val); + cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000; + } + } else { + cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000; + } + + regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL, + CS42L42_TS_RISE_DBNCE_TIME_MASK, + (cs42l42->ts_dbnc_rise << + CS42L42_TS_RISE_DBNCE_TIME_SHIFT)); + + ret = device_property_read_u32(dev, "cirrus,ts-dbnc-fall", &val); + if (!ret) { + switch (val) { + case CS42L42_TS_DBNCE_0: + case CS42L42_TS_DBNCE_125: + case CS42L42_TS_DBNCE_250: + case CS42L42_TS_DBNCE_500: + case CS42L42_TS_DBNCE_750: + case CS42L42_TS_DBNCE_1000: + case CS42L42_TS_DBNCE_1250: + case CS42L42_TS_DBNCE_1500: + cs42l42->ts_dbnc_fall = val; + break; + default: + dev_err(dev, + "Wrong cirrus,ts-dbnc-fall DT value %d\n", + val); + cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0; + } + } else { + cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0; + } + + regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL, + CS42L42_TS_FALL_DBNCE_TIME_MASK, + (cs42l42->ts_dbnc_fall << + CS42L42_TS_FALL_DBNCE_TIME_SHIFT)); + + ret = device_property_read_u32(dev, "cirrus,btn-det-init-dbnce", &val); + if (!ret) { + if (val <= CS42L42_BTN_DET_INIT_DBNCE_MAX) + cs42l42->btn_det_init_dbnce = val; + else { + dev_err(dev, + "Wrong cirrus,btn-det-init-dbnce DT value %d\n", + val); + cs42l42->btn_det_init_dbnce = + CS42L42_BTN_DET_INIT_DBNCE_DEFAULT; + } + } else { + cs42l42->btn_det_init_dbnce = + CS42L42_BTN_DET_INIT_DBNCE_DEFAULT; + } + + ret = device_property_read_u32(dev, "cirrus,btn-det-event-dbnce", &val); + if (!ret) { + if (val <= CS42L42_BTN_DET_EVENT_DBNCE_MAX) + cs42l42->btn_det_event_dbnce = val; + else { + dev_err(dev, + "Wrong cirrus,btn-det-event-dbnce DT value %d\n", val); + cs42l42->btn_det_event_dbnce = + CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT; + } + } else { + cs42l42->btn_det_event_dbnce = + CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT; + } + + ret = device_property_read_u32_array(dev, "cirrus,bias-lvls", + thresholds, ARRAY_SIZE(thresholds)); + if (!ret) { + for (i = 0; i < CS42L42_NUM_BIASES; i++) { + if (thresholds[i] <= CS42L42_HS_DET_LEVEL_MAX) + cs42l42->bias_thresholds[i] = thresholds[i]; + else { + dev_err(dev, + "Wrong cirrus,bias-lvls[%d] DT value %d\n", i, + thresholds[i]); + cs42l42->bias_thresholds[i] = threshold_defaults[i]; + } + } + } else { + for (i = 0; i < CS42L42_NUM_BIASES; i++) + cs42l42->bias_thresholds[i] = threshold_defaults[i]; + } + + ret = device_property_read_u32(dev, "cirrus,hs-bias-ramp-rate", &val); + if (!ret) { + switch (val) { + case CS42L42_HSBIAS_RAMP_FAST_RISE_SLOW_FALL: + cs42l42->hs_bias_ramp_rate = val; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME0; + break; + case CS42L42_HSBIAS_RAMP_FAST: + cs42l42->hs_bias_ramp_rate = val; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME1; + break; + case CS42L42_HSBIAS_RAMP_SLOW: + cs42l42->hs_bias_ramp_rate = val; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2; + break; + case CS42L42_HSBIAS_RAMP_SLOWEST: + cs42l42->hs_bias_ramp_rate = val; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME3; + break; + default: + dev_err(dev, + "Wrong cirrus,hs-bias-ramp-rate DT value %d\n", + val); + cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2; + } + } else { + cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW; + cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2; + } + + regmap_update_bits(cs42l42->regmap, CS42L42_HS_BIAS_CTL, + CS42L42_HSBIAS_RAMP_MASK, + (cs42l42->hs_bias_ramp_rate << + CS42L42_HSBIAS_RAMP_SHIFT)); + + if (device_property_read_bool(dev, "cirrus,hs-bias-sense-disable")) + cs42l42->hs_bias_sense_en = 0; + else + cs42l42->hs_bias_sense_en = 1; + + return 0; +} + +/* Datasheet suspend sequence */ +static const struct reg_sequence __maybe_unused cs42l42_shutdown_seq[] = { + REG_SEQ0(CS42L42_MIC_DET_CTL1, 0x9F), + REG_SEQ0(CS42L42_ADC_OVFL_INT_MASK, 0x01), + REG_SEQ0(CS42L42_MIXER_INT_MASK, 0x0F), + REG_SEQ0(CS42L42_SRC_INT_MASK, 0x0F), + REG_SEQ0(CS42L42_ASP_RX_INT_MASK, 0x1F), + REG_SEQ0(CS42L42_ASP_TX_INT_MASK, 0x0F), + REG_SEQ0(CS42L42_CODEC_INT_MASK, 0x03), + REG_SEQ0(CS42L42_SRCPL_INT_MASK, 0x7F), + REG_SEQ0(CS42L42_VPMON_INT_MASK, 0x01), + REG_SEQ0(CS42L42_PLL_LOCK_INT_MASK, 0x01), + REG_SEQ0(CS42L42_TSRS_PLUG_INT_MASK, 0x0F), + REG_SEQ0(CS42L42_WAKE_CTL, 0xE1), + REG_SEQ0(CS42L42_DET_INT1_MASK, 0xE0), + REG_SEQ0(CS42L42_DET_INT2_MASK, 0xFF), + REG_SEQ0(CS42L42_MIXER_CHA_VOL, 0x3F), + REG_SEQ0(CS42L42_MIXER_ADC_VOL, 0x3F), + REG_SEQ0(CS42L42_MIXER_CHB_VOL, 0x3F), + REG_SEQ0(CS42L42_HP_CTL, 0x0F), + REG_SEQ0(CS42L42_ASP_RX_DAI0_EN, 0x00), + REG_SEQ0(CS42L42_ASP_CLK_CFG, 0x00), + REG_SEQ0(CS42L42_HSDET_CTL2, 0x00), + REG_SEQ0(CS42L42_PWR_CTL1, 0xFE), + REG_SEQ0(CS42L42_PWR_CTL2, 0x8C), + REG_SEQ0(CS42L42_DAC_CTL2, 0x02), + REG_SEQ0(CS42L42_HS_CLAMP_DISABLE, 0x00), + REG_SEQ0(CS42L42_MISC_DET_CTL, 0x03), + REG_SEQ0(CS42L42_TIPSENSE_CTL, 0x02), + REG_SEQ0(CS42L42_HSBIAS_SC_AUTOCTL, 0x03), + REG_SEQ0(CS42L42_PWR_CTL1, 0xFF) +}; + +int cs42l42_suspend(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + unsigned int reg; + u8 save_regs[ARRAY_SIZE(cs42l42_shutdown_seq)]; + int i, ret; + + if (!cs42l42->init_done) + return 0; + + /* + * Wait for threaded irq handler to be idle and stop it processing + * future interrupts. This ensures a safe disable if the interrupt + * is shared. + */ + mutex_lock(&cs42l42->irq_lock); + cs42l42->suspended = true; + + /* Save register values that will be overwritten by shutdown sequence */ + for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) { + regmap_read(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, ®); + save_regs[i] = (u8)reg; + } + + /* Shutdown codec */ + regmap_multi_reg_write(cs42l42->regmap, + cs42l42_shutdown_seq, + ARRAY_SIZE(cs42l42_shutdown_seq)); + + /* All interrupt sources are now disabled */ + mutex_unlock(&cs42l42->irq_lock); + + /* Wait for power-down complete */ + msleep(CS42L42_PDN_DONE_TIME_MS); + ret = regmap_read_poll_timeout(cs42l42->regmap, + CS42L42_CODEC_STATUS, reg, + (reg & CS42L42_PDN_DONE_MASK), + CS42L42_PDN_DONE_POLL_US, + CS42L42_PDN_DONE_TIMEOUT_US); + if (ret) + dev_warn(dev, "Failed to get PDN_DONE: %d\n", ret); + + /* Discharge FILT+ */ + regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2, + CS42L42_DISCHARGE_FILT_MASK, CS42L42_DISCHARGE_FILT_MASK); + msleep(CS42L42_FILT_DISCHARGE_TIME_MS); + + regcache_cache_only(cs42l42->regmap, true); + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); + + /* Restore register values to the regmap cache */ + for (i = 0; i < ARRAY_SIZE(cs42l42_shutdown_seq); ++i) + regmap_write(cs42l42->regmap, cs42l42_shutdown_seq[i].reg, save_regs[i]); + + /* The cached address page register value is now stale */ + regcache_drop_region(cs42l42->regmap, CS42L42_PAGE_REGISTER, CS42L42_PAGE_REGISTER); + + dev_dbg(dev, "System suspended\n"); + + return 0; + +} +EXPORT_SYMBOL_NS_GPL(cs42l42_suspend, SND_SOC_CS42L42_CORE); + +int cs42l42_resume(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + int ret; + + if (!cs42l42->init_done) + return 0; + + /* + * If jack was unplugged and re-plugged during suspend it could + * have changed type but the tip-sense state hasn't changed. + * Force a plugged state to be re-evaluated. + */ + if (cs42l42->plug_state != CS42L42_TS_UNPLUG) + cs42l42->plug_state = CS42L42_TS_TRANS; + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); + usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2); + + dev_dbg(dev, "System resume powered up\n"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs42l42_resume, SND_SOC_CS42L42_CORE); + +void cs42l42_resume_restore(struct device *dev) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(dev); + + regcache_cache_only(cs42l42->regmap, false); + regcache_mark_dirty(cs42l42->regmap); + + mutex_lock(&cs42l42->irq_lock); + /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */ + regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1); + regcache_sync(cs42l42->regmap); + + cs42l42->suspended = false; + mutex_unlock(&cs42l42->irq_lock); + + dev_dbg(dev, "System resumed\n"); +} +EXPORT_SYMBOL_NS_GPL(cs42l42_resume_restore, SND_SOC_CS42L42_CORE); + +static int __maybe_unused cs42l42_i2c_resume(struct device *dev) +{ + int ret; + + ret = cs42l42_resume(dev); + if (ret) + return ret; + + cs42l42_resume_restore(dev); + + return 0; +} + +int cs42l42_common_probe(struct cs42l42_private *cs42l42, + const struct snd_soc_component_driver *component_drv, + struct snd_soc_dai_driver *dai) +{ + int ret, i; + + dev_set_drvdata(cs42l42->dev, cs42l42); + mutex_init(&cs42l42->irq_lock); + + BUILD_BUG_ON(ARRAY_SIZE(cs42l42_supply_names) != ARRAY_SIZE(cs42l42->supplies)); + for (i = 0; i < ARRAY_SIZE(cs42l42->supplies); i++) + cs42l42->supplies[i].supply = cs42l42_supply_names[i]; + + ret = devm_regulator_bulk_get(cs42l42->dev, + ARRAY_SIZE(cs42l42->supplies), + cs42l42->supplies); + if (ret != 0) { + dev_err(cs42l42->dev, + "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies), + cs42l42->supplies); + if (ret != 0) { + dev_err(cs42l42->dev, + "Failed to enable supplies: %d\n", ret); + return ret; + } + + /* Reset the Device */ + cs42l42->reset_gpio = devm_gpiod_get_optional(cs42l42->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs42l42->reset_gpio)) { + ret = PTR_ERR(cs42l42->reset_gpio); + goto err_disable_noreset; + } + + if (cs42l42->reset_gpio) { + dev_dbg(cs42l42->dev, "Found reset GPIO\n"); + + /* + * ACPI can override the default GPIO state we requested + * so ensure that we start with RESET low. + */ + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); + + /* Ensure minimum reset pulse width */ + usleep_range(10, 500); + + /* + * On SoundWire keep the chip in reset until we get an UNATTACH + * notification from the SoundWire core. This acts as a + * synchronization point to reject stale ATTACH notifications + * if the chip was already enumerated before we reset it. + */ + if (cs42l42->sdw_peripheral) + cs42l42->sdw_waiting_first_unattach = true; + else + gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); + } + usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2); + + /* Request IRQ if one was specified */ + if (cs42l42->irq) { + ret = request_threaded_irq(cs42l42->irq, + NULL, cs42l42_irq_thread, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs42l42", cs42l42); + if (ret) { + dev_err_probe(cs42l42->dev, ret, + "Failed to request IRQ\n"); + goto err_disable_noirq; + } + } + + /* Register codec now so it can EPROBE_DEFER */ + ret = devm_snd_soc_register_component(cs42l42->dev, component_drv, dai, 1); + if (ret < 0) + goto err; + + return 0; + +err: + if (cs42l42->irq) + free_irq(cs42l42->irq, cs42l42); + +err_disable_noirq: + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); +err_disable_noreset: + regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs42l42_common_probe, SND_SOC_CS42L42_CORE); + +int cs42l42_init(struct cs42l42_private *cs42l42) +{ + unsigned int reg; + int devid, ret; + + /* initialize codec */ + devid = cirrus_read_device_id(cs42l42->regmap, CS42L42_DEVID_AB); + if (devid < 0) { + ret = devid; + dev_err(cs42l42->dev, "Failed to read device ID: %d\n", ret); + goto err_disable; + } + + if (devid != cs42l42->devid) { + ret = -ENODEV; + dev_err(cs42l42->dev, + "CS42L%x Device ID (%X). Expected %X\n", + cs42l42->devid & 0xff, devid, cs42l42->devid); + goto err_disable; + } + + ret = regmap_read(cs42l42->regmap, CS42L42_REVID, ®); + if (ret < 0) { + dev_err(cs42l42->dev, "Get Revision ID failed\n"); + goto err_shutdown; + } + + dev_info(cs42l42->dev, + "Cirrus Logic CS42L%x, Revision: %02X\n", + cs42l42->devid & 0xff, reg & 0xFF); + + /* Power up the codec */ + regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL1, + CS42L42_ASP_DAO_PDN_MASK | + CS42L42_ASP_DAI_PDN_MASK | + CS42L42_MIXER_PDN_MASK | + CS42L42_EQ_PDN_MASK | + CS42L42_HP_PDN_MASK | + CS42L42_ADC_PDN_MASK | + CS42L42_PDN_ALL_MASK, + (1 << CS42L42_ASP_DAO_PDN_SHIFT) | + (1 << CS42L42_ASP_DAI_PDN_SHIFT) | + (1 << CS42L42_MIXER_PDN_SHIFT) | + (1 << CS42L42_EQ_PDN_SHIFT) | + (1 << CS42L42_HP_PDN_SHIFT) | + (1 << CS42L42_ADC_PDN_SHIFT) | + (0 << CS42L42_PDN_ALL_SHIFT)); + + ret = cs42l42_handle_device_data(cs42l42->dev, cs42l42); + if (ret != 0) + goto err_shutdown; + + /* + * SRC power is linked to ASP power so doesn't work in Soundwire mode. + * Override it and use DAPM to control SRC power for Soundwire. + */ + if (cs42l42->sdw_peripheral) { + regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2, + CS42L42_SRC_PDN_OVERRIDE_MASK | + CS42L42_DAC_SRC_PDNB_MASK | + CS42L42_ADC_SRC_PDNB_MASK, + CS42L42_SRC_PDN_OVERRIDE_MASK); + } + + /* Setup headset detection */ + cs42l42_setup_hs_type_detect(cs42l42); + + /* + * Set init_done before unmasking interrupts so any triggered + * immediately will be handled. + */ + cs42l42->init_done = true; + + /* Mask/Unmask Interrupts */ + cs42l42_set_interrupt_masks(cs42l42); + + return 0; + +err_shutdown: + regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff); + regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff); + regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff); + +err_disable: + if (cs42l42->irq) + free_irq(cs42l42->irq, cs42l42); + + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), + cs42l42->supplies); + return ret; +} +EXPORT_SYMBOL_NS_GPL(cs42l42_init, SND_SOC_CS42L42_CORE); + +void cs42l42_common_remove(struct cs42l42_private *cs42l42) +{ + if (cs42l42->irq) + free_irq(cs42l42->irq, cs42l42); + + /* + * The driver might not have control of reset and power supplies, + * so ensure that the chip internals are powered down. + */ + if (cs42l42->init_done) { + regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff); + regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff); + regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff); + } + + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); + regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies); +} +EXPORT_SYMBOL_NS_GPL(cs42l42_common_remove, SND_SOC_CS42L42_CORE); + +MODULE_DESCRIPTION("ASoC CS42L42 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, "); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, "); +MODULE_AUTHOR("Michael White, Cirrus Logic Inc, "); +MODULE_AUTHOR("Lucas Tanure "); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Vitaly Rodionov "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h new file mode 100644 index 0000000000..7785125b73 --- /dev/null +++ b/sound/soc/codecs/cs42l42.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * cs42l42.h -- CS42L42 ALSA SoC audio driver header + * + * Copyright 2016-2022 Cirrus Logic, Inc. + * + * Author: James Schulman + * Author: Brian Austin + * Author: Michael White + */ + +#ifndef __CS42L42_H__ +#define __CS42L42_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cs42l42_private { + struct regmap *regmap; + struct device *dev; + struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES]; + struct gpio_desc *reset_gpio; + struct completion pdn_done; + struct snd_soc_jack *jack; + struct sdw_slave *sdw_peripheral; + struct mutex irq_lock; + int devid; + int irq; + int pll_config; + u32 sclk; + u32 sample_rate; + u32 bclk_ratio; + u8 plug_state; + u8 hs_type; + u8 ts_inv; + u8 ts_dbnc_rise; + u8 ts_dbnc_fall; + u8 btn_det_init_dbnce; + u8 btn_det_event_dbnce; + u8 bias_thresholds[CS42L42_NUM_BIASES]; + u8 hs_bias_ramp_rate; + u8 hs_bias_ramp_time; + u8 hs_bias_sense_en; + u8 stream_use; + bool hp_adc_up_pending; + bool suspended; + bool sdw_waiting_first_unattach; + bool init_done; +}; + +extern const struct regmap_range_cfg cs42l42_page_range; +extern const struct regmap_config cs42l42_regmap; +extern const struct snd_soc_component_driver cs42l42_soc_component; +extern struct snd_soc_dai_driver cs42l42_dai; + +bool cs42l42_readable_register(struct device *dev, unsigned int reg); +bool cs42l42_volatile_register(struct device *dev, unsigned int reg); + +int cs42l42_pll_config(struct snd_soc_component *component, + unsigned int clk, unsigned int sample_rate); +void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate); +int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream); +irqreturn_t cs42l42_irq_thread(int irq, void *data); +int cs42l42_suspend(struct device *dev); +int cs42l42_resume(struct device *dev); +void cs42l42_resume_restore(struct device *dev); +int cs42l42_common_probe(struct cs42l42_private *cs42l42, + const struct snd_soc_component_driver *component_drv, + struct snd_soc_dai_driver *dai); +int cs42l42_init(struct cs42l42_private *cs42l42); +void cs42l42_common_remove(struct cs42l42_private *cs42l42); + +#endif /* __CS42L42_H__ */ diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c new file mode 100644 index 0000000000..9f5f1a9256 --- /dev/null +++ b/sound/soc/codecs/cs42l43-jack.c @@ -0,0 +1,946 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS42L43 CODEC driver jack handling +// +// Copyright (C) 2022-2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs42l43.h" + +static const unsigned int cs42l43_accdet_us[] = { + 20, 100, 1000, 10000, 50000, 75000, 100000, 200000 +}; + +static const unsigned int cs42l43_accdet_db_ms[] = { + 0, 125, 250, 500, 750, 1000, 1250, 1500 +}; + +static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 }; + +static const unsigned int cs42l43_accdet_bias_sense[] = { + 14, 24, 43, 52, 61, 71, 90, 99, 0, +}; + +static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const prop, + unsigned int defval, unsigned int *val, + const unsigned int *values, const int nvalues) +{ + struct cs42l43 *cs42l43 = priv->core; + int i, ret; + + ret = device_property_read_u32(cs42l43->dev, prop, &defval); + if (ret != -EINVAL && ret < 0) { + dev_err(priv->dev, "Property %s malformed: %d\n", prop, ret); + return ret; + } + + if (val) + *val = defval; + + for (i = 0; i < nvalues; i++) + if (defval == values[i]) + return i; + + dev_err(priv->dev, "Invalid value for property %s: %d\n", prop, defval); + return -EINVAL; +} + +int cs42l43_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *d) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + /* This tip sense invert is always set, HW wants an inverted signal */ + unsigned int tip_deb = CS42L43_TIPSENSE_INV_MASK; + unsigned int hs2 = 0x2 << CS42L43_HSDET_MODE_SHIFT; + unsigned int autocontrol = 0, pdncntl = 0; + int ret; + + dev_dbg(priv->dev, "Configure accessory detect\n"); + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret); + return ret; + } + + mutex_lock(&priv->jack_lock); + + priv->jack_hp = jack; + + if (!jack) + goto done; + + ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms"); + if (ret != -EINVAL) { + if (ret < 0) { + dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n", + ret); + goto error; + } + + if (ret > CS42L43_N_BUTTONS) { + ret = -EINVAL; + dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n"); + goto error; + } + + device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms", + priv->buttons, ret); + } else { + priv->buttons[0] = 70; + priv->buttons[1] = 185; + priv->buttons[2] = 355; + priv->buttons[3] = 735; + } + + ret = cs42l43_find_index(priv, "cirrus,detect-us", 10000, &priv->detect_us, + cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us)); + if (ret < 0) + goto error; + + hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT; + + priv->bias_low = device_property_read_bool(cs42l43->dev, "cirrus,bias-low"); + + ret = cs42l43_find_index(priv, "cirrus,bias-ramp-ms", 170, + &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms, + ARRAY_SIZE(cs42l43_accdet_ramp_ms)); + if (ret < 0) + goto error; + + hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,bias-sense-microamp", 0, + &priv->bias_sense_ua, cs42l43_accdet_bias_sense, + ARRAY_SIZE(cs42l43_accdet_bias_sense)); + if (ret < 0) + goto error; + + if (priv->bias_sense_ua) + autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT; + + if (!device_property_read_bool(cs42l43->dev, "cirrus,button-automute")) + autocontrol |= CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK; + + ret = device_property_read_u32(cs42l43->dev, "cirrus,tip-debounce-ms", + &priv->tip_debounce_ms); + if (ret < 0 && ret != -EINVAL) { + dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret); + goto error; + } + + /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */ + if (device_property_read_bool(cs42l43->dev, "cirrus,tip-invert")) + autocontrol |= 0x1 << CS42L43_JACKDET_INV_SHIFT; + + if (device_property_read_bool(cs42l43->dev, "cirrus,tip-disable-pullup")) + autocontrol |= 0x1 << CS42L43_JACKDET_MODE_SHIFT; + else + autocontrol |= 0x3 << CS42L43_JACKDET_MODE_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,tip-fall-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,tip-rise-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT; + + if (device_property_read_bool(cs42l43->dev, "cirrus,use-ring-sense")) { + unsigned int ring_deb = 0; + + priv->use_ring_sense = true; + + /* HW wants an inverted signal, so invert the invert */ + if (!device_property_read_bool(cs42l43->dev, "cirrus,ring-invert")) + ring_deb |= CS42L43_RINGSENSE_INV_MASK; + + if (!device_property_read_bool(cs42l43->dev, + "cirrus,ring-disable-pullup")) + ring_deb |= CS42L43_RINGSENSE_PULLUP_PDNB_MASK; + + ret = cs42l43_find_index(priv, "cirrus,ring-fall-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT; + + ret = cs42l43_find_index(priv, "cirrus,ring-rise-db-ms", 500, + NULL, cs42l43_accdet_db_ms, + ARRAY_SIZE(cs42l43_accdet_db_ms)); + if (ret < 0) + goto error; + + ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT; + pdncntl |= CS42L43_RING_SENSE_EN_MASK; + + regmap_update_bits(cs42l43->regmap, CS42L43_RINGSENSE_DEB_CTRL, + CS42L43_RINGSENSE_INV_MASK | + CS42L43_RINGSENSE_PULLUP_PDNB_MASK | + CS42L43_RINGSENSE_FALLING_DB_TIME_MASK | + CS42L43_RINGSENSE_RISING_DB_TIME_MASK, + ring_deb); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_TIPSENSE_DEB_CTRL, + CS42L43_TIPSENSE_INV_MASK | + CS42L43_TIPSENSE_FALLING_DB_TIME_MASK | + CS42L43_TIPSENSE_RISING_DB_TIME_MASK, tip_deb); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK | + CS42L43_AUTO_HSDET_TIME_MASK, hs2); + +done: + ret = 0; + + regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK | + CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol); + regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL, + CS42L43_RING_SENSE_EN_MASK, pdncntl); + + dev_dbg(priv->dev, "Successfully configured accessory detect\n"); + +error: + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return ret; +} + +static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool force_high) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val = 0x3 << CS42L43_HSBIAS_MODE_SHIFT; + + dev_dbg(priv->dev, "Start headset bias\n"); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK); + + if (!force_high && priv->bias_low) + val = 0x2 << CS42L43_HSBIAS_MODE_SHIFT; + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, val); + + msleep(priv->bias_ramp_ms); +} + +static void cs42l43_stop_hs_bias(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Stop headset bias\n"); + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, 0); +} + +irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + queue_delayed_work(system_wq, &priv->bias_sense_timeout, + msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +#define CS42L43_JACK_PRESENT 0x3 +#define CS42L43_JACK_ABSENT 0x0 + +#define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT) +#define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE) +#define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET) +#define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT) +#define CS42L43_JACK_LINEIN (SND_JACK_MECHANICAL | SND_JACK_LINEIN) +#define CS42L43_JACK_EXTENSION (SND_JACK_MECHANICAL) +#define CS42L43_JACK_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | \ + SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5) + +static inline bool cs42l43_jack_present(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int sts = 0; + + regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts); + + sts = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; + + return sts == CS42L43_JACK_PRESENT; +} + +static void cs42l43_start_button_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val = 0x3 << CS42L43_BUTTON_DETECT_MODE_SHIFT; + + dev_dbg(priv->dev, "Start button detect\n"); + + priv->button_detect_running = true; + + if (priv->bias_low) + val = 0x1 << CS42L43_BUTTON_DETECT_MODE_SHIFT; + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_BUTTON_DETECT_MODE_MASK | + CS42L43_MIC_LVL_DET_DISABLE_MASK, val); + + if (priv->bias_sense_ua) { + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); + } +} + +static void cs42l43_stop_button_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Stop button detect\n"); + + if (priv->bias_sense_ua) { + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_HSBIAS_SENSE_EN_MASK | + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_BUTTON_DETECT_MODE_MASK | + CS42L43_MIC_LVL_DET_DISABLE_MASK, + CS42L43_MIC_LVL_DET_DISABLE_MASK); + + priv->button_detect_running = false; +} + +#define CS42L43_BUTTON_COMB_MAX 512 +#define CS42L43_BUTTON_ROUT 2210 + +void cs42l43_button_press_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + button_press_work.work); + struct cs42l43 *cs42l43 = priv->core; + unsigned int buttons = 0; + unsigned int val = 0; + int i, ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for button press: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + if (!priv->button_detect_running) { + dev_dbg(priv->dev, "Spurious button press IRQ\n"); + goto error; + } + + regmap_read(cs42l43->regmap, CS42L43_DETECT_STATUS_1, &val); + + /* Bail if jack removed, the button is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) { + dev_dbg(priv->dev, "Button ignored due to removal\n"); + goto error; + } + + if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) { + dev_dbg(priv->dev, "Button ignored due to bias sense\n"); + goto error; + } + + val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT; + val = ((CS42L43_BUTTON_COMB_MAX << 20) / (val + 1)) - (1 << 20); + if (val) + val = (CS42L43_BUTTON_ROUT << 20) / val; + else + val = UINT_MAX; + + for (i = 0; i < CS42L43_N_BUTTONS; i++) { + if (val < priv->buttons[i]) { + buttons = SND_JACK_BTN_0 >> i; + dev_dbg(priv->dev, "Detected button %d at %d Ohms\n", i, val); + break; + } + } + + if (!buttons) + dev_dbg(priv->dev, "Unrecognised button: %d Ohms\n", val); + + snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS); + +error: + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +irqreturn_t cs42l43_button_press(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + // Wait for 2 full cycles of comb filter to ensure good reading + queue_delayed_work(system_wq, &priv->button_press_work, + msecs_to_jiffies(10)); + + return IRQ_HANDLED; +} + +void cs42l43_button_release_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + button_release_work); + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for button release: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + if (priv->button_detect_running) { + dev_dbg(priv->dev, "Button release IRQ\n"); + + snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS); + } else { + dev_dbg(priv->dev, "Spurious button release IRQ\n"); + } + + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +irqreturn_t cs42l43_button_release(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + queue_work(system_wq, &priv->button_release_work); + + return IRQ_HANDLED; +} + +void cs42l43_bias_sense_timeout(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + bias_sense_timeout.work); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + if (cs42l43_jack_present(priv) && priv->button_detect_running) { + dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n"); + + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, + CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, + CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK); + } + + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +static void cs42l43_start_load_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Start load detect\n"); + + snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component)); + + priv->load_detect_running = true; + + if (priv->hp_ena) { + unsigned long time_left; + + reinit_completion(&priv->hp_shutdown); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + CS42L43_HP_EN_MASK, 0); + + time_left = wait_for_completion_timeout(&priv->hp_shutdown, + msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS)); + if (!time_left) + dev_err(priv->dev, "Load detect HP power down timed out\n"); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3, + CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2, CS42L43_HP_HPF_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL, + CS42L43_ADPTPWR_MODE_MASK, 0x4 << CS42L43_ADPTPWR_MODE_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL, + CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, 0x6); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1, + CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, 0); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK); + + regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA, + CS42L43_HPLOAD_DET_EN_MASK, + CS42L43_HPLOAD_DET_EN_MASK); + + snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component)); +} + +static void cs42l43_stop_load_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Stop load detect\n"); + + snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component)); + + regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA, + CS42L43_HPLOAD_DET_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HS_CLAMP_DISABLE_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1, + CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, + CS42L43_HP_MSTR_VOL_CTRL_EN_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL, + CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, + 0x4 << CS42L43_HP_DIG_VOL_RAMP_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL, + CS42L43_ADPTPWR_MODE_MASK, 0x7 << CS42L43_ADPTPWR_MODE_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1, + CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2, + CS42L43_HP_HPF_EN_MASK, CS42L43_HP_HPF_EN_MASK); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3, + CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, + priv->adc_ena); + + if (priv->hp_ena) { + unsigned long time_left; + + reinit_completion(&priv->hp_startup); + + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + CS42L43_HP_EN_MASK, priv->hp_ena); + + time_left = wait_for_completion_timeout(&priv->hp_startup, + msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS)); + if (!time_left) + dev_err(priv->dev, "Load detect HP restore timed out\n"); + } + + priv->load_detect_running = false; + + snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component)); +} + +static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val = 0; + unsigned long time_left; + + reinit_completion(&priv->load_detect); + + cs42l43_start_load_detect(priv); + time_left = wait_for_completion_timeout(&priv->load_detect, + msecs_to_jiffies(CS42L43_LOAD_TIMEOUT_MS)); + cs42l43_stop_load_detect(priv); + + if (!time_left) + return -ETIMEDOUT; + + regmap_read(cs42l43->regmap, CS42L43_LOADDETRESULTS, &val); + + dev_dbg(priv->dev, "Headphone load detect: 0x%x\n", val); + + /* Bail if jack removed, the load is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) + return -ENODEV; + + if (mic) { + cs42l43_start_hs_bias(priv, false); + cs42l43_start_button_detect(priv); + + return CS42L43_JACK_HEADSET; + } + + switch (val & CS42L43_AMP3_RES_DET_MASK) { + case 0x0: // low impedance + case 0x1: // high impedance + return CS42L43_JACK_HEADPHONE; + case 0x2: // lineout + case 0x3: // Open circuit + return CS42L43_JACK_LINEOUT; + default: + return -EINVAL; + } +} + +static int cs42l43_run_type_detect(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + int timeout_ms = ((2 * priv->detect_us) / 1000) + 200; + unsigned int type = 0xff; + unsigned long time_left; + + reinit_completion(&priv->type_detect); + + cs42l43_start_hs_bias(priv, true); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK, 0x3 << CS42L43_HSDET_MODE_SHIFT); + + time_left = wait_for_completion_timeout(&priv->type_detect, + msecs_to_jiffies(timeout_ms)); + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK, 0x2 << CS42L43_HSDET_MODE_SHIFT); + cs42l43_stop_hs_bias(priv); + + if (!time_left) + return -ETIMEDOUT; + + regmap_read(cs42l43->regmap, CS42L43_HS_STAT, &type); + + dev_dbg(priv->dev, "Type detect: 0x%x\n", type); + + /* Bail if jack removed, the type is irrelevant and likely invalid */ + if (!cs42l43_jack_present(priv)) + return -ENODEV; + + switch (type & CS42L43_HSDET_TYPE_STS_MASK) { + case 0x0: // CTIA + case 0x1: // OMTP + return cs42l43_run_load_detect(priv, true); + case 0x2: // 3-pole + return cs42l43_run_load_detect(priv, false); + case 0x3: // Open-circuit + return CS42L43_JACK_EXTENSION; + default: + return -EINVAL; + } +} + +static void cs42l43_clear_jack(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + cs42l43_stop_button_detect(priv); + cs42l43_stop_hs_bias(priv); + + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL, + CS42L43_JACK_STEREO_CONFIG_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK, + 0x2 << CS42L43_HSDET_MODE_SHIFT); + + snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF); +} + +void cs42l43_tip_sense_work(struct work_struct *work) +{ + struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec, + tip_sense_work.work); + struct cs42l43 *cs42l43 = priv->core; + unsigned int sts = 0; + unsigned int tip, ring; + int ret, report; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for tip work: %d\n", ret); + return; + } + + mutex_lock(&priv->jack_lock); + + regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts); + + dev_dbg(priv->dev, "Tip sense: 0x%x\n", sts); + + tip = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; + ring = (sts >> CS42L43_RINGSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT; + + if (tip == CS42L43_JACK_PRESENT) { + if (cs42l43->sdw && !priv->jack_present) { + priv->jack_present = true; + pm_runtime_get(priv->dev); + } + + if (priv->use_ring_sense && ring == CS42L43_JACK_ABSENT) { + report = CS42L43_JACK_OPTICAL; + } else { + report = cs42l43_run_type_detect(priv); + if (report < 0) { + dev_err(priv->dev, "Jack detect failed: %d\n", report); + goto error; + } + } + + snd_soc_jack_report(priv->jack_hp, report, report); + } else { + priv->jack_override = 0; + + cs42l43_clear_jack(priv); + + if (cs42l43->sdw && priv->jack_present) { + pm_runtime_put(priv->dev); + priv->jack_present = false; + } + } + +error: + mutex_unlock(&priv->jack_lock); + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); +} + +irqreturn_t cs42l43_tip_sense(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + + cancel_delayed_work(&priv->bias_sense_timeout); + cancel_delayed_work(&priv->tip_sense_work); + cancel_delayed_work(&priv->button_press_work); + cancel_work(&priv->button_release_work); + + queue_delayed_work(system_long_wq, &priv->tip_sense_work, + msecs_to_jiffies(priv->tip_debounce_ms)); + + return IRQ_HANDLED; +} + +enum cs42l43_raw_jack { + CS42L43_JACK_RAW_CTIA = 0, + CS42L43_JACK_RAW_OMTP, + CS42L43_JACK_RAW_HEADPHONE, + CS42L43_JACK_RAW_LINE_OUT, + CS42L43_JACK_RAW_LINE_IN, + CS42L43_JACK_RAW_MICROPHONE, + CS42L43_JACK_RAW_OPTICAL, +}; + +#define CS42L43_JACK_3_POLE_SWITCHES ((0x2 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | \ + CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | \ + CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | \ + CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | \ + CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | \ + CS42L43_HSGND_HS3_SEL_MASK | \ + CS42L43_HSGND_HS4_SEL_MASK) + +static const struct cs42l43_jack_override_mode { + unsigned int hsdet_mode; + unsigned int mic_ctrl; + unsigned int clamp_ctrl; + int report; +} cs42l43_jack_override_modes[] = { + [CS42L43_JACK_RAW_CTIA] = { + .hsdet_mode = CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | + CS42L43_HSBIAS_OUT_HS4_SEL_MASK | + CS42L43_HSGND_HS3_SEL_MASK, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_HEADSET, + }, + [CS42L43_JACK_RAW_OMTP] = { + .hsdet_mode = (0x1 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | + CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_OUT_HS3_SEL_MASK | + CS42L43_HSGND_HS4_SEL_MASK, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_HEADSET, + }, + [CS42L43_JACK_RAW_HEADPHONE] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_HEADPHONE, + }, + [CS42L43_JACK_RAW_LINE_OUT] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_LINEOUT, + }, + [CS42L43_JACK_RAW_LINE_IN] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .mic_ctrl = 0x2 << CS42L43_JACK_STEREO_CONFIG_SHIFT, + .report = CS42L43_JACK_LINEIN, + }, + [CS42L43_JACK_RAW_MICROPHONE] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) | + CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK, + .report = CS42L43_JACK_LINEIN, + }, + [CS42L43_JACK_RAW_OPTICAL] = { + .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, + .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + .report = CS42L43_JACK_OPTICAL, + }, +}; + +static const char * const cs42l43_jack_text[] = { + "None", "CTIA", "OMTP", "Headphone", "Line-Out", + "Line-In", "Microphone", "Optical", +}; + +SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text); + +int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + mutex_lock(&priv->jack_lock); + ucontrol->value.integer.value[0] = priv->jack_override; + mutex_unlock(&priv->jack_lock); + + return 0; +} + +int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int override = ucontrol->value.integer.value[0]; + + BUILD_BUG_ON(ARRAY_SIZE(cs42l43_jack_override_modes) != + ARRAY_SIZE(cs42l43_jack_text) - 1); + + if (override >= e->items) + return -EINVAL; + + mutex_lock(&priv->jack_lock); + + if (!cs42l43_jack_present(priv)) { + mutex_unlock(&priv->jack_lock); + return -EBUSY; + } + + if (override == priv->jack_override) { + mutex_unlock(&priv->jack_lock); + return 0; + } + + priv->jack_override = override; + + cs42l43_clear_jack(priv); + + if (!override) { + queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0); + } else { + override--; + + regmap_update_bits(cs42l43->regmap, CS42L43_HS2, + CS42L43_HSDET_MODE_MASK | + CS42L43_HSDET_MANUAL_MODE_MASK | + CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | + CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | + CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | + CS42L43_HSBIAS_OUT_HS3_SEL_MASK | + CS42L43_HSBIAS_OUT_HS4_SEL_MASK | + CS42L43_HSGND_HS3_SEL_MASK | + CS42L43_HSGND_HS4_SEL_MASK, + cs42l43_jack_override_modes[override].hsdet_mode); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL, + CS42L43_HS2_BIAS_EN_MASK | CS42L43_HS1_BIAS_EN_MASK | + CS42L43_JACK_STEREO_CONFIG_MASK, + cs42l43_jack_override_modes[override].mic_ctrl); + regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL, + CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK, + cs42l43_jack_override_modes[override].clamp_ctrl); + + switch (override) { + case CS42L43_JACK_RAW_CTIA: + case CS42L43_JACK_RAW_OMTP: + cs42l43_start_hs_bias(priv, false); + cs42l43_start_button_detect(priv); + break; + case CS42L43_JACK_RAW_LINE_IN: + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, + CS42L43_PGA_WIDESWING_MODE_EN_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2, + CS42L43_PGA_WIDESWING_MODE_EN_MASK, + CS42L43_PGA_WIDESWING_MODE_EN_MASK); + break; + case CS42L43_JACK_RAW_MICROPHONE: + cs42l43_start_hs_bias(priv, false); + break; + default: + break; + } + + snd_soc_jack_report(priv->jack_hp, + cs42l43_jack_override_modes[override].report, + cs42l43_jack_override_modes[override].report); + } + + mutex_unlock(&priv->jack_lock); + + return 1; +} diff --git a/sound/soc/codecs/cs42l43-sdw.c b/sound/soc/codecs/cs42l43-sdw.c new file mode 100644 index 0000000000..55ac5fe8c3 --- /dev/null +++ b/sound/soc/codecs/cs42l43-sdw.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS42L43 CODEC driver SoundWire handling +// +// Copyright (C) 2022-2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs42l43.h" + +int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent); + struct sdw_stream_config sconfig = {0}; + struct sdw_port_config pconfig = {0}; + int ret; + + if (!sdw_stream) + return -EINVAL; + + snd_sdw_params_to_config(substream, params, &sconfig, &pconfig); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pconfig.num = dai->id; + else + pconfig.num = dai->id; + + ret = sdw_stream_add_slave(sdw, &sconfig, &pconfig, 1, sdw_stream); + if (ret) { + dev_err(priv->dev, "Failed to add sdw stream: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_add_peripheral, SND_SOC_CS42L43); + +int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent); + + if (!sdw_stream) + return -EINVAL; + + return sdw_stream_remove_slave(sdw, sdw_stream); +} +EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_remove_peripheral, SND_SOC_CS42L43); + +int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_set_stream, SND_SOC_CS42L43); + +MODULE_DESCRIPTION("CS42L43 CODEC SoundWire Driver"); +MODULE_AUTHOR("Charles Keepax "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c new file mode 100644 index 0000000000..5643c666d7 --- /dev/null +++ b/sound/soc/codecs/cs42l43.c @@ -0,0 +1,2281 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// CS42L43 CODEC driver +// +// Copyright (C) 2022-2023 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs42l43.h" + +#define CS42L43_DECL_MUX(name, reg) \ +static SOC_VALUE_ENUM_SINGLE_DECL(cs42l43_##name##_enum, reg, \ + 0, CS42L43_MIXER_SRC_MASK, \ + cs42l43_mixer_texts, cs42l43_mixer_values); \ +static const struct snd_kcontrol_new cs42l43_##name##_mux = \ + SOC_DAPM_ENUM("Route", cs42l43_##name##_enum) + +#define CS42L43_DECL_MIXER(name, reg) \ + CS42L43_DECL_MUX(name##_in1, reg); \ + CS42L43_DECL_MUX(name##_in2, reg + 0x4); \ + CS42L43_DECL_MUX(name##_in3, reg + 0x8); \ + CS42L43_DECL_MUX(name##_in4, reg + 0xC) + +#define CS42L43_DAPM_MUX(name_str, name) \ + SND_SOC_DAPM_MUX(name_str " Input", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_mux) + +#define CS42L43_DAPM_MIXER(name_str, name) \ + SND_SOC_DAPM_MUX(name_str " Input 1", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in1_mux), \ + SND_SOC_DAPM_MUX(name_str " Input 2", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in2_mux), \ + SND_SOC_DAPM_MUX(name_str " Input 3", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in3_mux), \ + SND_SOC_DAPM_MUX(name_str " Input 4", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in4_mux), \ + SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0) + +#define CS42L43_BASE_ROUTES(name_str) \ + { name_str, "Tone Generator 1", "Tone 1" }, \ + { name_str, "Tone Generator 2", "Tone 2" }, \ + { name_str, "Decimator 1", "Decimator 1" }, \ + { name_str, "Decimator 2", "Decimator 2" }, \ + { name_str, "Decimator 3", "Decimator 3" }, \ + { name_str, "Decimator 4", "Decimator 4" }, \ + { name_str, "ASPRX1", "ASPRX1" }, \ + { name_str, "ASPRX2", "ASPRX2" }, \ + { name_str, "ASPRX3", "ASPRX3" }, \ + { name_str, "ASPRX4", "ASPRX4" }, \ + { name_str, "ASPRX5", "ASPRX5" }, \ + { name_str, "ASPRX6", "ASPRX6" }, \ + { name_str, "DP5RX1", "DP5RX1" }, \ + { name_str, "DP5RX2", "DP5RX2" }, \ + { name_str, "DP6RX1", "DP6RX1" }, \ + { name_str, "DP6RX2", "DP6RX2" }, \ + { name_str, "DP7RX1", "DP7RX1" }, \ + { name_str, "DP7RX2", "DP7RX2" }, \ + { name_str, "ASRC INT1", "ASRC_INT1" }, \ + { name_str, "ASRC INT2", "ASRC_INT2" }, \ + { name_str, "ASRC INT3", "ASRC_INT3" }, \ + { name_str, "ASRC INT4", "ASRC_INT4" }, \ + { name_str, "ASRC DEC1", "ASRC_DEC1" }, \ + { name_str, "ASRC DEC2", "ASRC_DEC2" }, \ + { name_str, "ASRC DEC3", "ASRC_DEC3" }, \ + { name_str, "ASRC DEC4", "ASRC_DEC4" }, \ + { name_str, "ISRC1 INT1", "ISRC1INT1" }, \ + { name_str, "ISRC1 INT2", "ISRC1INT2" }, \ + { name_str, "ISRC1 DEC1", "ISRC1DEC1" }, \ + { name_str, "ISRC1 DEC2", "ISRC1DEC2" }, \ + { name_str, "ISRC2 INT1", "ISRC2INT1" }, \ + { name_str, "ISRC2 INT2", "ISRC2INT2" }, \ + { name_str, "ISRC2 DEC1", "ISRC2DEC1" }, \ + { name_str, "ISRC2 DEC2", "ISRC2DEC2" }, \ + { name_str, "EQ1", "EQ" }, \ + { name_str, "EQ2", "EQ" } + +#define CS42L43_MUX_ROUTES(name_str, widget) \ + { widget, NULL, name_str " Input" }, \ + { name_str " Input", NULL, "Mixer Core" }, \ + CS42L43_BASE_ROUTES(name_str " Input") + +#define CS42L43_MIXER_ROUTES(name_str, widget) \ + { name_str " Mixer", NULL, name_str " Input 1" }, \ + { name_str " Mixer", NULL, name_str " Input 2" }, \ + { name_str " Mixer", NULL, name_str " Input 3" }, \ + { name_str " Mixer", NULL, name_str " Input 4" }, \ + { widget, NULL, name_str " Mixer" }, \ + { name_str " Mixer", NULL, "Mixer Core" }, \ + CS42L43_BASE_ROUTES(name_str " Input 1"), \ + CS42L43_BASE_ROUTES(name_str " Input 2"), \ + CS42L43_BASE_ROUTES(name_str " Input 3"), \ + CS42L43_BASE_ROUTES(name_str " Input 4") + +#define CS42L43_MIXER_VOLUMES(name_str, base) \ + SOC_SINGLE_RANGE_TLV(name_str " Input 1 Volume", base, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name_str " Input 2 Volume", base + 4, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name_str " Input 3 Volume", base + 8, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name_str " Input 4 Volume", base + 12, \ + CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + cs42l43_mixer_tlv) + +#define CS42L43_IRQ_ERROR(name) \ +static irqreturn_t cs42l43_##name(int irq, void *data) \ +{ \ + struct cs42l43_codec *priv = data; \ + dev_err(priv->dev, "Error " #name " IRQ\n"); \ + return IRQ_HANDLED; \ +} + +CS42L43_IRQ_ERROR(pll_lost_lock) +CS42L43_IRQ_ERROR(spkr_clock_stop) +CS42L43_IRQ_ERROR(spkl_clock_stop) +CS42L43_IRQ_ERROR(spkr_brown_out) +CS42L43_IRQ_ERROR(spkl_brown_out) +CS42L43_IRQ_ERROR(spkr_therm_shutdown) +CS42L43_IRQ_ERROR(spkl_therm_shutdown) +CS42L43_IRQ_ERROR(spkr_therm_warm) +CS42L43_IRQ_ERROR(spkl_therm_warm) +CS42L43_IRQ_ERROR(spkr_sc_detect) +CS42L43_IRQ_ERROR(spkl_sc_detect) +CS42L43_IRQ_ERROR(hp_ilimit) + +#define CS42L43_IRQ_COMPLETE(name) \ +static irqreturn_t cs42l43_##name(int irq, void *data) \ +{ \ + struct cs42l43_codec *priv = data; \ + dev_dbg(priv->dev, #name " completed\n"); \ + complete(&priv->name); \ + return IRQ_HANDLED; \ +} + +CS42L43_IRQ_COMPLETE(pll_ready) +CS42L43_IRQ_COMPLETE(hp_startup) +CS42L43_IRQ_COMPLETE(hp_shutdown) +CS42L43_IRQ_COMPLETE(type_detect) +CS42L43_IRQ_COMPLETE(spkr_shutdown) +CS42L43_IRQ_COMPLETE(spkl_shutdown) +CS42L43_IRQ_COMPLETE(spkr_startup) +CS42L43_IRQ_COMPLETE(spkl_startup) +CS42L43_IRQ_COMPLETE(load_detect) + +static irqreturn_t cs42l43_mic_shutter(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + const char * const controls[] = { + "Decimator 1 Switch", + "Decimator 2 Switch", + "Decimator 3 Switch", + "Decimator 4 Switch", + }; + int i, ret; + + dev_dbg(priv->dev, "Microphone shutter changed\n"); + + if (!priv->component) + return IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(controls); i++) { + ret = snd_soc_component_notify_control(priv->component, + controls[i]); + if (ret) + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static irqreturn_t cs42l43_spk_shutter(int irq, void *data) +{ + struct cs42l43_codec *priv = data; + int ret; + + dev_dbg(priv->dev, "Speaker shutter changed\n"); + + if (!priv->component) + return IRQ_NONE; + + ret = snd_soc_component_notify_control(priv->component, + "Speaker Digital Switch"); + if (ret) + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static const unsigned int cs42l43_sample_rates[] = { + 8000, 16000, 24000, 32000, 44100, 48000, 96000, 192000, +}; + +#define CS42L43_CONSUMER_RATE_MASK 0xFF +#define CS42L43_PROVIDER_RATE_MASK 0xEF // 44.1k only supported as consumer + +static const struct snd_pcm_hw_constraint_list cs42l43_constraint = { + .count = ARRAY_SIZE(cs42l43_sample_rates), + .list = cs42l43_sample_rates, +}; + +static int cs42l43_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int provider = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK); + + if (provider) + priv->constraint.mask = CS42L43_PROVIDER_RATE_MASK; + else + priv->constraint.mask = CS42L43_CONSUMER_RATE_MASK; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &priv->constraint); +} + +static int cs42l43_convert_sample_rate(unsigned int rate) +{ + switch (rate) { + case 8000: + return 0x11; + case 16000: + return 0x12; + case 24000: + return 0x02; + case 32000: + return 0x13; + case 44100: + return 0x0B; + case 48000: + return 0x03; + case 96000: + return 0x04; + case 192000: + return 0x05; + default: + return -EINVAL; + } +} + +static int cs42l43_set_sample_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + ret = cs42l43_convert_sample_rate(params_rate(params)); + if (ret < 0) { + dev_err(priv->dev, "Failed to convert sample rate: %d\n", ret); + return ret; + } + + //FIXME: For now lets just set sample rate 1, this needs expanded in the future + regmap_update_bits(cs42l43->regmap, CS42L43_SAMPLE_RATE1, + CS42L43_SAMPLE_RATE_MASK, ret); + + return 0; +} + +static int cs42l43_asp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component); + struct cs42l43 *cs42l43 = priv->core; + int dsp_mode = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CTRL, + CS42L43_ASP_FSYNC_MODE_MASK); + int provider = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK); + int n_chans = params_channels(params); + int data_width = params_width(params); + int n_slots = n_chans; + int slot_width = data_width; + int frame, bclk_target, i; + unsigned int reg; + int *slots; + + if (priv->n_slots) { + n_slots = priv->n_slots; + slot_width = priv->slot_width; + } + + if (!dsp_mode && (n_slots & 0x1)) { + dev_dbg(priv->dev, "Forcing balanced channels on ASP\n"); + n_slots++; + } + + frame = n_slots * slot_width; + bclk_target = params_rate(params) * frame; + + if (provider) { + unsigned int gcd_nm = gcd(bclk_target, CS42L43_INTERNAL_SYSCLK); + int n = bclk_target / gcd_nm; + int m = CS42L43_INTERNAL_SYSCLK / gcd_nm; + + if (n > (CS42L43_ASP_BCLK_N_MASK >> CS42L43_ASP_BCLK_N_SHIFT) || + m > CS42L43_ASP_BCLK_M_MASK) { + dev_err(priv->dev, "Can't produce %dHz bclk\n", bclk_target); + return -EINVAL; + } + + dev_dbg(priv->dev, "bclk %d/%d = %dHz, with %dx%d frame\n", + n, m, bclk_target, n_slots, slot_width); + + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG1, + CS42L43_ASP_BCLK_N_MASK | CS42L43_ASP_BCLK_M_MASK, + n << CS42L43_ASP_BCLK_N_SHIFT | + m << CS42L43_ASP_BCLK_M_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL1, + CS42L43_ASP_FSYNC_M_MASK, frame); + } + + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL4, + CS42L43_ASP_NUM_BCLKS_PER_FSYNC_MASK, + frame << CS42L43_ASP_NUM_BCLKS_PER_FSYNC_SHIFT); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + reg = CS42L43_ASP_TX_CH1_CTRL; + slots = priv->tx_slots; + } else { + reg = CS42L43_ASP_RX_CH1_CTRL; + slots = priv->rx_slots; + } + + for (i = 0; i < n_chans; i++, reg += 4) { + int slot_phase = dsp_mode | (i & CS42L43_ASP_CH_SLOT_PHASE_MASK); + int slot_pos; + + if (dsp_mode) + slot_pos = slots[i] * slot_width; + else + slot_pos = (slots[i] / 2) * slot_width; + + dev_dbg(priv->dev, "Configure channel %d at slot %d (%d,%d)\n", + i, slots[i], slot_pos, slot_phase); + + regmap_update_bits(cs42l43->regmap, reg, + CS42L43_ASP_CH_WIDTH_MASK | + CS42L43_ASP_CH_SLOT_MASK | + CS42L43_ASP_CH_SLOT_PHASE_MASK, + ((data_width - 1) << CS42L43_ASP_CH_WIDTH_SHIFT) | + (slot_pos << CS42L43_ASP_CH_SLOT_SHIFT) | + slot_phase); + } + + return cs42l43_set_sample_rate(substream, params, dai); +} + +static int cs42l43_asp_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int provider = regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK); + struct snd_soc_dapm_route routes[] = { + { "BCLK", NULL, "FSYNC" }, + }; + unsigned int asp_ctrl = 0; + unsigned int data_ctrl = 0; + unsigned int fsync_ctrl = 0; + unsigned int clk_config = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT; + fallthrough; + case SND_SOC_DAIFMT_DSP_B: + asp_ctrl |= CS42L43_ASP_FSYNC_MODE_MASK; + data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK; + break; + case SND_SOC_DAIFMT_I2S: + data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT; + break; + case SND_SOC_DAIFMT_LEFT_J: + data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK; + break; + default: + dev_err(priv->dev, "Unsupported DAI format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + if (provider) + snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes)); + break; + case SND_SOC_DAIFMT_CBP_CFP: + if (!provider) + snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes)); + clk_config |= CS42L43_ASP_MASTER_MODE_MASK; + break; + default: + dev_err(priv->dev, "Unsupported ASP mode 0x%x\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + clk_config |= CS42L43_ASP_BCLK_INV_MASK; /* Yes BCLK_INV = NB */ + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + clk_config |= CS42L43_ASP_BCLK_INV_MASK; + fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK | + CS42L43_ASP_FSYNC_OUT_INV_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK | + CS42L43_ASP_FSYNC_OUT_INV_MASK; + break; + default: + dev_err(priv->dev, "Unsupported invert mode 0x%x\n", + fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CTRL, + CS42L43_ASP_FSYNC_MODE_MASK, + asp_ctrl); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_DATA_CTRL, + CS42L43_ASP_FSYNC_FRAME_START_DLY_MASK | + CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK, + data_ctrl); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2, + CS42L43_ASP_MASTER_MODE_MASK | + CS42L43_ASP_BCLK_INV_MASK, + clk_config); + regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL3, + CS42L43_ASP_FSYNC_IN_INV_MASK | + CS42L43_ASP_FSYNC_OUT_INV_MASK, + fsync_ctrl); + + return 0; +} + +static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned int mask, int *slots) +{ + int i; + + for (i = 0; i < CS42L43_ASP_MAX_CHANNELS; ++i) { + int slot = ffs(mask) - 1; + + if (slot < 0) + return; + + slots[i] = slot; + + mask &= ~(1 << slot); + } + + if (mask) + dev_warn(priv->dev, "Too many channels in TDM mask\n"); +} + +static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + priv->n_slots = slots; + priv->slot_width = slot_width; + + if (!slots) { + tx_mask = CS42L43_DEFAULT_SLOTS; + rx_mask = CS42L43_DEFAULT_SLOTS; + } + + cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots); + cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots); + + return 0; +} + +static const struct snd_soc_dai_ops cs42l43_asp_ops = { + .startup = cs42l43_startup, + .hw_params = cs42l43_asp_hw_params, + .set_fmt = cs42l43_asp_set_fmt, + .set_tdm_slot = cs42l43_asp_set_tdm_slot, +}; + +static int cs42l43_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int ret; + + ret = cs42l43_sdw_add_peripheral(substream, params, dai); + if (ret) + return ret; + + return cs42l43_set_sample_rate(substream, params, dai); +}; + +static const struct snd_soc_dai_ops cs42l43_sdw_ops = { + .startup = cs42l43_startup, + .set_stream = cs42l43_sdw_set_stream, + .hw_params = cs42l43_sdw_hw_params, + .hw_free = cs42l43_sdw_remove_peripheral, +}; + +#define CS42L43_ASP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define CS42L43_SDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver cs42l43_dais[] = { + { + .name = "cs42l43-asp", + .ops = &cs42l43_asp_ops, + .symmetric_rate = 1, + .capture = { + .stream_name = "ASP Capture", + .channels_min = 1, + .channels_max = CS42L43_ASP_MAX_CHANNELS, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_ASP_FORMATS, + }, + .playback = { + .stream_name = "ASP Playback", + .channels_min = 1, + .channels_max = CS42L43_ASP_MAX_CHANNELS, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_ASP_FORMATS, + }, + }, + { + .name = "cs42l43-dp1", + .id = 1, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp2", + .id = 2, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp3", + .id = 3, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp4", + .id = 4, + .ops = &cs42l43_sdw_ops, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp5", + .id = 5, + .ops = &cs42l43_sdw_ops, + .playback = { + .stream_name = "DP5 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp6", + .id = 6, + .ops = &cs42l43_sdw_ops, + .playback = { + .stream_name = "DP6 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, + { + .name = "cs42l43-dp7", + .id = 7, + .ops = &cs42l43_sdw_ops, + .playback = { + .stream_name = "DP7 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L43_SDW_FORMATS, + }, + }, +}; + +static const DECLARE_TLV_DB_SCALE(cs42l43_mixer_tlv, -3200, 100, 0); + +static const char * const cs42l43_ramp_text[] = { + "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", + "15ms/6dB", "30ms/6dB", +}; + +static const char * const cs42l43_adc1_input_text[] = { "IN1", "IN2" }; + +static SOC_ENUM_SINGLE_DECL(cs42l43_adc1_input, CS42L43_ADC_B_CTRL1, + CS42L43_ADC_AIN_SEL_SHIFT, + cs42l43_adc1_input_text); + +static const struct snd_kcontrol_new cs42l43_adc1_input_ctl = + SOC_DAPM_ENUM("ADC1 Input", cs42l43_adc1_input); + +static const char * const cs42l43_dec_mode_text[] = { "ADC", "PDM" }; + +static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec1_mode, cs42l43_dec_mode_text); +static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec2_mode, cs42l43_dec_mode_text); + +static const struct snd_kcontrol_new cs42l43_dec_mode_ctl[] = { + SOC_DAPM_ENUM("Decimator 1 Mode", cs42l43_dec1_mode), + SOC_DAPM_ENUM("Decimator 2 Mode", cs42l43_dec2_mode), +}; + +static const char * const cs42l43_pdm_clk_text[] = { + "3.072MHz", "1.536MHz", "768kHz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_pdm1_clk, CS42L43_PDM_CONTROL, + CS42L43_PDM1_CLK_DIV_SHIFT, cs42l43_pdm_clk_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_pdm2_clk, CS42L43_PDM_CONTROL, + CS42L43_PDM2_CLK_DIV_SHIFT, cs42l43_pdm_clk_text); + +static DECLARE_TLV_DB_SCALE(cs42l43_adc_tlv, -600, 600, 0); +static DECLARE_TLV_DB_SCALE(cs42l43_dec_tlv, -6400, 50, 0); + +static const char * const cs42l43_wnf_corner_text[] = { + "160Hz", "180Hz", "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text); + +static const char * const cs42l43_hpf_corner_text[] = { + "3Hz", "12Hz", "48Hz", "96Hz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text); + +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VD_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VI_RAMP_SHIFT, cs42l43_ramp_text); +static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VD_RAMP_SHIFT, cs42l43_ramp_text); + +static DECLARE_TLV_DB_SCALE(cs42l43_speaker_tlv, -6400, 50, 0); + +static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_up, CS42L43_AMP1_2_VOL_RAMP, + CS42L43_AMP1_2_VI_RAMP_SHIFT, cs42l43_ramp_text); + +static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_down, CS42L43_AMP1_2_VOL_RAMP, + CS42L43_AMP1_2_VD_RAMP_SHIFT, cs42l43_ramp_text); + +static DECLARE_TLV_DB_SCALE(cs42l43_headphone_tlv, -11450, 50, 1); + +static const char * const cs42l43_headphone_ramp_text[] = { + "1", "2", "4", "6", "8", "11", "12", "16", "22", "24", "33", "36", "44", + "48", "66", "72", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_headphone_ramp, CS42L43_PGAVOL, + CS42L43_HP_PATH_VOL_RAMP_SHIFT, + cs42l43_headphone_ramp_text); + +static const char * const cs42l43_tone_freq_text[] = { + "1kHz", "2kHz", "4kHz", "6kHz", "8kHz", +}; + +static SOC_ENUM_SINGLE_DECL(cs42l43_tone1_freq, CS42L43_TONE_CH1_CTRL, + CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text); + +static SOC_ENUM_SINGLE_DECL(cs42l43_tone2_freq, CS42L43_TONE_CH2_CTRL, + CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text); + +static const char * const cs42l43_mixer_texts[] = { + "None", + "Tone Generator 1", "Tone Generator 2", + "Decimator 1", "Decimator 2", "Decimator 3", "Decimator 4", + "ASPRX1", "ASPRX2", "ASPRX3", "ASPRX4", "ASPRX5", "ASPRX6", + "DP5RX1", "DP5RX2", "DP6RX1", "DP6RX2", "DP7RX1", "DP7RX2", + "ASRC INT1", "ASRC INT2", "ASRC INT3", "ASRC INT4", + "ASRC DEC1", "ASRC DEC2", "ASRC DEC3", "ASRC DEC4", + "ISRC1 INT1", "ISRC1 INT2", + "ISRC1 DEC1", "ISRC1 DEC2", + "ISRC2 INT1", "ISRC2 INT2", + "ISRC2 DEC1", "ISRC2 DEC2", + "EQ1", "EQ2", +}; + +static const unsigned int cs42l43_mixer_values[] = { + 0x00, // None + 0x04, 0x05, // Tone Generator 1, 2 + 0x10, 0x11, 0x12, 0x13, // Decimator 1, 2, 3, 4 + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, // ASPRX1,2,3,4,5,6 + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, // DP5, 6, 7RX1, 2 + 0x40, 0x41, 0x42, 0x43, // ASRC INT1, 2, 3, 4 + 0x44, 0x45, 0x46, 0x47, // ASRC DEC1, 2, 3, 4 + 0x50, 0x51, // ISRC1 INT1, 2 + 0x52, 0x53, // ISRC1 DEC1, 2 + 0x54, 0x55, // ISRC2 INT1, 2 + 0x56, 0x57, // ISRC2 DEC1, 2 + 0x58, 0x59, // EQ1, 2 +}; + +CS42L43_DECL_MUX(asptx1, CS42L43_ASPTX1_INPUT); +CS42L43_DECL_MUX(asptx2, CS42L43_ASPTX2_INPUT); +CS42L43_DECL_MUX(asptx3, CS42L43_ASPTX3_INPUT); +CS42L43_DECL_MUX(asptx4, CS42L43_ASPTX4_INPUT); +CS42L43_DECL_MUX(asptx5, CS42L43_ASPTX5_INPUT); +CS42L43_DECL_MUX(asptx6, CS42L43_ASPTX6_INPUT); + +CS42L43_DECL_MUX(dp1tx1, CS42L43_SWIRE_DP1_CH1_INPUT); +CS42L43_DECL_MUX(dp1tx2, CS42L43_SWIRE_DP1_CH2_INPUT); +CS42L43_DECL_MUX(dp1tx3, CS42L43_SWIRE_DP1_CH3_INPUT); +CS42L43_DECL_MUX(dp1tx4, CS42L43_SWIRE_DP1_CH4_INPUT); +CS42L43_DECL_MUX(dp2tx1, CS42L43_SWIRE_DP2_CH1_INPUT); +CS42L43_DECL_MUX(dp2tx2, CS42L43_SWIRE_DP2_CH2_INPUT); +CS42L43_DECL_MUX(dp3tx1, CS42L43_SWIRE_DP3_CH1_INPUT); +CS42L43_DECL_MUX(dp3tx2, CS42L43_SWIRE_DP3_CH2_INPUT); +CS42L43_DECL_MUX(dp4tx1, CS42L43_SWIRE_DP4_CH1_INPUT); +CS42L43_DECL_MUX(dp4tx2, CS42L43_SWIRE_DP4_CH2_INPUT); + +CS42L43_DECL_MUX(asrcint1, CS42L43_ASRC_INT1_INPUT1); +CS42L43_DECL_MUX(asrcint2, CS42L43_ASRC_INT2_INPUT1); +CS42L43_DECL_MUX(asrcint3, CS42L43_ASRC_INT3_INPUT1); +CS42L43_DECL_MUX(asrcint4, CS42L43_ASRC_INT4_INPUT1); +CS42L43_DECL_MUX(asrcdec1, CS42L43_ASRC_DEC1_INPUT1); +CS42L43_DECL_MUX(asrcdec2, CS42L43_ASRC_DEC2_INPUT1); +CS42L43_DECL_MUX(asrcdec3, CS42L43_ASRC_DEC3_INPUT1); +CS42L43_DECL_MUX(asrcdec4, CS42L43_ASRC_DEC4_INPUT1); + +CS42L43_DECL_MUX(isrc1int1, CS42L43_ISRC1INT1_INPUT1); +CS42L43_DECL_MUX(isrc1int2, CS42L43_ISRC1INT2_INPUT1); +CS42L43_DECL_MUX(isrc1dec1, CS42L43_ISRC1DEC1_INPUT1); +CS42L43_DECL_MUX(isrc1dec2, CS42L43_ISRC1DEC2_INPUT1); +CS42L43_DECL_MUX(isrc2int1, CS42L43_ISRC2INT1_INPUT1); +CS42L43_DECL_MUX(isrc2int2, CS42L43_ISRC2INT2_INPUT1); +CS42L43_DECL_MUX(isrc2dec1, CS42L43_ISRC2DEC1_INPUT1); +CS42L43_DECL_MUX(isrc2dec2, CS42L43_ISRC2DEC2_INPUT1); + +CS42L43_DECL_MUX(spdif1, CS42L43_SPDIF1_INPUT1); +CS42L43_DECL_MUX(spdif2, CS42L43_SPDIF2_INPUT1); + +CS42L43_DECL_MIXER(eq1, CS42L43_EQ1MIX_INPUT1); +CS42L43_DECL_MIXER(eq2, CS42L43_EQ2MIX_INPUT1); + +CS42L43_DECL_MIXER(amp1, CS42L43_AMP1MIX_INPUT1); +CS42L43_DECL_MIXER(amp2, CS42L43_AMP2MIX_INPUT1); + +CS42L43_DECL_MIXER(amp3, CS42L43_AMP3MIX_INPUT1); +CS42L43_DECL_MIXER(amp4, CS42L43_AMP4MIX_INPUT1); + +static int cs42l43_dapm_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_get_volsw(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_dapm_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_put_volsw(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_dapm_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_get_enum_double(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_dapm_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + snd_soc_dapm_mutex_lock(dapm); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int cs42l43_eq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + memcpy(ucontrol->value.integer.value, priv->eq_coeffs, sizeof(priv->eq_coeffs)); + + return 0; +} + +static int cs42l43_eq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + snd_soc_dapm_mutex_lock(dapm); + + memcpy(priv->eq_coeffs, ucontrol->value.integer.value, sizeof(priv->eq_coeffs)); + + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + +static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv) +{ + struct cs42l43 *cs42l43 = priv->core; + + mutex_lock(&priv->spk_vu_lock); + + regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, + CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK); + regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1, + CS42L43_AMP1_2_VU_MASK, 0); + + mutex_unlock(&priv->spk_vu_lock); +} + +static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift) +{ + struct cs42l43 *cs42l43 = priv->core; + unsigned int val; + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret) { + dev_err(priv->dev, "Failed to resume for shutters: %d\n", ret); + return ret; + } + + /* + * SHUTTER_CONTROL is a mix of volatile and non-volatile bits, so must + * be cached for the non-volatiles, so drop it from the cache here so + * we force a read. + */ + ret = regcache_drop_region(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, + CS42L43_SHUTTER_CONTROL); + if (ret) { + dev_err(priv->dev, "Failed to drop shutter from cache: %d\n", ret); + goto error; + } + + ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val); + if (ret) { + dev_err(priv->dev, "Failed to check shutter status: %d\n", ret); + goto error; + } + + ret = !(val & BIT(shift)); + + dev_dbg(priv->dev, "%s shutter is %s\n", + BIT(shift) == CS42L43_STATUS_MIC_SHUTTER_MUTE_MASK ? "Mic" : "Speaker", + ret ? "open" : "closed"); + +error: + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return ret; +} + +static int cs42l43_decim_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = cs42l43_shutter_get(priv, CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT); + if (ret < 0) + return ret; + else if (!ret) + ucontrol->value.integer.value[0] = ret; + else + ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol); + + return ret; +} + +static int cs42l43_spk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = cs42l43_shutter_get(priv, CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT); + if (ret < 0) + return ret; + else if (!ret) + ucontrol->value.integer.value[0] = ret; + else + ret = snd_soc_get_volsw(kcontrol, ucontrol); + + return ret; +} + +static int cs42l43_spk_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret > 0) + cs42l43_spk_vu_sync(priv); + + return ret; +} + +static const struct snd_kcontrol_new cs42l43_controls[] = { + SOC_ENUM_EXT("Jack Override", cs42l43_jack_enum, + cs42l43_jack_get, cs42l43_jack_put), + + SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L43_ADC_B_CTRL1, CS42L43_ADC_B_CTRL2, + CS42L43_ADC_PGA_GAIN_SHIFT, + 0xF, 5, cs42l43_adc_tlv), + + SOC_DOUBLE("PDM1 Invert Switch", CS42L43_DMIC_PDM_CTRL, + CS42L43_PDM1L_INV_SHIFT, CS42L43_PDM1R_INV_SHIFT, 1, 0), + SOC_DOUBLE("PDM2 Invert Switch", CS42L43_DMIC_PDM_CTRL, + CS42L43_PDM2L_INV_SHIFT, CS42L43_PDM2R_INV_SHIFT, 1, 0), + SOC_ENUM("PDM1 Clock", cs42l43_pdm1_clk), + SOC_ENUM("PDM2 Clock", cs42l43_pdm2_clk), + + SOC_SINGLE("Decimator 1 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 2 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 3 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 4 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_WNF_EN_SHIFT, 1, 0), + + SOC_ENUM("Decimator 1 WNF Corner Frequency", cs42l43_dec1_wnf_corner), + SOC_ENUM("Decimator 2 WNF Corner Frequency", cs42l43_dec2_wnf_corner), + SOC_ENUM("Decimator 3 WNF Corner Frequency", cs42l43_dec3_wnf_corner), + SOC_ENUM("Decimator 4 WNF Corner Frequency", cs42l43_dec4_wnf_corner), + + SOC_SINGLE("Decimator 1 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL1, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 2 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL2, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 3 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL3, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + SOC_SINGLE("Decimator 4 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL4, + CS42L43_DECIM_HPF_EN_SHIFT, 1, 0), + + SOC_ENUM("Decimator 1 HPF Corner Frequency", cs42l43_dec1_hpf_corner), + SOC_ENUM("Decimator 2 HPF Corner Frequency", cs42l43_dec2_hpf_corner), + SOC_ENUM("Decimator 3 HPF Corner Frequency", cs42l43_dec3_hpf_corner), + SOC_ENUM("Decimator 4 HPF Corner Frequency", cs42l43_dec4_hpf_corner), + + SOC_SINGLE_TLV("Decimator 1 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 1 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM1_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 2 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 2 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2, + CS42L43_DECIM2_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 3 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 3 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM3_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + SOC_SINGLE_TLV("Decimator 4 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv), + SOC_SINGLE_EXT("Decimator 4 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4, + CS42L43_DECIM4_MUTE_SHIFT, 1, 1, + cs42l43_decim_get, cs42l43_dapm_put_volsw), + + SOC_ENUM_EXT("Decimator 1 Ramp Up", cs42l43_dec1_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 1 Ramp Down", cs42l43_dec1_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 2 Ramp Up", cs42l43_dec2_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 2 Ramp Down", cs42l43_dec2_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 3 Ramp Up", cs42l43_dec3_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 3 Ramp Down", cs42l43_dec3_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 4 Ramp Up", cs42l43_dec4_ramp_up, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + SOC_ENUM_EXT("Decimator 4 Ramp Down", cs42l43_dec4_ramp_down, + cs42l43_dapm_get_enum, cs42l43_dapm_put_enum), + + SOC_DOUBLE_R_EXT("Speaker Digital Switch", + CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2, + CS42L43_AMP_MUTE_SHIFT, 1, 1, + cs42l43_spk_get, cs42l43_spk_put), + + SOC_DOUBLE_R_EXT_TLV("Speaker Digital Volume", + CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2, + CS42L43_AMP_VOL_SHIFT, + 0xBF, 0, snd_soc_get_volsw, cs42l43_spk_put, + cs42l43_speaker_tlv), + + SOC_ENUM("Speaker Ramp Up", cs42l43_speaker_ramp_up), + SOC_ENUM("Speaker Ramp Down", cs42l43_speaker_ramp_down), + + CS42L43_MIXER_VOLUMES("Speaker L", CS42L43_AMP1MIX_INPUT1), + CS42L43_MIXER_VOLUMES("Speaker R", CS42L43_AMP2MIX_INPUT1), + + SOC_DOUBLE_SX_TLV("Headphone Digital Volume", CS42L43_HPPATHVOL, + CS42L43_AMP3_PATH_VOL_SHIFT, CS42L43_AMP4_PATH_VOL_SHIFT, + 0x11B, 229, cs42l43_headphone_tlv), + + SOC_DOUBLE("Headphone Invert Switch", CS42L43_DACCNFG1, + CS42L43_AMP3_INV_SHIFT, CS42L43_AMP4_INV_SHIFT, 1, 0), + + SOC_SINGLE("Headphone Zero Cross Switch", CS42L43_PGAVOL, + CS42L43_HP_PATH_VOL_ZC_SHIFT, 1, 0), + SOC_SINGLE("Headphone Ramp Switch", CS42L43_PGAVOL, + CS42L43_HP_PATH_VOL_SFT_SHIFT, 1, 0), + SOC_ENUM("Headphone Ramp Rate", cs42l43_headphone_ramp), + + CS42L43_MIXER_VOLUMES("Headphone L", CS42L43_AMP3MIX_INPUT1), + CS42L43_MIXER_VOLUMES("Headphone R", CS42L43_AMP4MIX_INPUT1), + + SOC_ENUM("Tone 1 Frequency", cs42l43_tone1_freq), + SOC_ENUM("Tone 2 Frequency", cs42l43_tone2_freq), + + SOC_DOUBLE_EXT("EQ Switch", + CS42L43_MUTE_EQ_IN0, CS42L43_MUTE_EQ_CH1_SHIFT, + CS42L43_MUTE_EQ_CH2_SHIFT, 1, 1, + cs42l43_dapm_get_volsw, cs42l43_dapm_put_volsw), + + SND_SOC_BYTES_E("EQ Coefficients", 0, CS42L43_N_EQ_COEFFS, + cs42l43_eq_get, cs42l43_eq_put), + + CS42L43_MIXER_VOLUMES("EQ1", CS42L43_EQ1MIX_INPUT1), + CS42L43_MIXER_VOLUMES("EQ2", CS42L43_EQ2MIX_INPUT1), +}; + +static int cs42l43_eq_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int val; + int i, ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0, + CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK, + CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK); + + regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0, + CS42L43_WRITE_MODE_MASK, CS42L43_WRITE_MODE_MASK); + + for (i = 0; i < CS42L43_N_EQ_COEFFS; i++) + regmap_write(cs42l43->regmap, CS42L43_COEFF_DATA_IN0, + priv->eq_coeffs[i]); + + regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0, + CS42L43_WRITE_MODE_MASK, 0); + + return 0; + case SND_SOC_DAPM_POST_PMU: + ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_INIT_DONE0, + val, (val & CS42L43_INITIALIZE_DONE_MASK), + 2000, 10000); + if (ret) + dev_err(priv->dev, "Failed to start EQs: %d\n", ret); + + regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0, + CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK, 0); + return ret; + default: + return 0; + } +} + +struct cs42l43_pll_config { + unsigned int freq; + + unsigned int div; + unsigned int mode; + unsigned int cal; +}; + +static const struct cs42l43_pll_config cs42l43_pll_configs[] = { + { 2400000, 0x50000000, 0x1, 0xA4 }, + { 3000000, 0x40000000, 0x1, 0x83 }, + { 3072000, 0x40000000, 0x3, 0x80 }, +}; + +static int cs42l43_set_pll(struct cs42l43_codec *priv, unsigned int src, + unsigned int freq) +{ + struct cs42l43 *cs42l43 = priv->core; + + lockdep_assert_held(&cs42l43->pll_lock); + + if (priv->refclk_src == src && priv->refclk_freq == freq) + return 0; + + if (regmap_test_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK)) { + dev_err(priv->dev, "PLL active, can't change configuration\n"); + return -EBUSY; + } + + switch (src) { + case CS42L43_SYSCLK_MCLK: + case CS42L43_SYSCLK_SDW: + dev_dbg(priv->dev, "Source PLL from %s at %uHz\n", + src ? "SoundWire" : "MCLK", freq); + + priv->refclk_src = src; + priv->refclk_freq = freq; + + return 0; + default: + dev_err(priv->dev, "Invalid PLL source: 0x%x\n", src); + return -EINVAL; + } +} + +static int cs42l43_enable_pll(struct cs42l43_codec *priv) +{ + static const struct reg_sequence enable_seq[] = { + { CS42L43_OSC_DIV_SEL, 0x0, }, + { CS42L43_MCLK_SRC_SEL, CS42L43_OSC_PLL_MCLK_SEL_MASK, 5, }, + }; + struct cs42l43 *cs42l43 = priv->core; + const struct cs42l43_pll_config *config = NULL; + unsigned int div = 0; + unsigned int freq = priv->refclk_freq; + unsigned long time_left; + + lockdep_assert_held(&cs42l43->pll_lock); + + if (priv->refclk_src == CS42L43_SYSCLK_SDW) { + if (!freq) + freq = cs42l43->sdw_freq; + else if (!cs42l43->sdw_freq) + cs42l43->sdw_freq = freq; + } + + dev_dbg(priv->dev, "Enabling PLL at %uHz\n", freq); + + while (freq > cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq) { + div++; + freq /= 2; + } + + if (div <= CS42L43_PLL_REFCLK_DIV_MASK) { + int i; + + for (i = 0; i < ARRAY_SIZE(cs42l43_pll_configs); i++) { + if (freq == cs42l43_pll_configs[i].freq) { + config = &cs42l43_pll_configs[i]; + break; + } + } + } + + if (!config) { + dev_err(priv->dev, "No suitable PLL config: 0x%x, %uHz\n", div, freq); + return -EINVAL; + } + + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_DIV_MASK | CS42L43_PLL_REFCLK_SRC_MASK, + div << CS42L43_PLL_REFCLK_DIV_SHIFT | + priv->refclk_src << CS42L43_PLL_REFCLK_SRC_SHIFT); + regmap_write(cs42l43->regmap, CS42L43_FDIV_FRAC, config->div); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, + CS42L43_PLL_MODE_BYPASS_500_MASK | + CS42L43_PLL_MODE_BYPASS_1029_MASK, + config->mode << CS42L43_PLL_MODE_BYPASS_1029_SHIFT); + regmap_update_bits(cs42l43->regmap, CS42L43_CAL_RATIO, + CS42L43_PLL_CAL_RATIO_MASK, config->cal); + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_EN_MASK, CS42L43_PLL_REFCLK_EN_MASK); + + reinit_completion(&priv->pll_ready); + + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, + CS42L43_PLL_EN_MASK, CS42L43_PLL_EN_MASK); + + time_left = wait_for_completion_timeout(&priv->pll_ready, + msecs_to_jiffies(CS42L43_PLL_TIMEOUT_MS)); + if (!time_left) { + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, + CS42L43_PLL_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_EN_MASK, 0); + + dev_err(priv->dev, "Timeout out waiting for PLL\n"); + return -ETIMEDOUT; + } + + if (priv->refclk_src == CS42L43_SYSCLK_SDW) + cs42l43->sdw_pll_active = true; + + dev_dbg(priv->dev, "PLL locked in %ums\n", 200 - jiffies_to_msecs(time_left)); + + /* + * Reads are not allowed over Soundwire without OSC_DIV2_EN or the PLL, + * but you can not change to PLL with OSC_DIV2_EN set. So ensure the whole + * change over happens under the regmap lock to prevent any reads. + */ + regmap_multi_reg_write(cs42l43->regmap, enable_seq, ARRAY_SIZE(enable_seq)); + + return 0; +} + +static int cs42l43_disable_pll(struct cs42l43_codec *priv) +{ + static const struct reg_sequence disable_seq[] = { + { CS42L43_MCLK_SRC_SEL, 0x0, 5, }, + { CS42L43_OSC_DIV_SEL, CS42L43_OSC_DIV2_EN_MASK, }, + }; + struct cs42l43 *cs42l43 = priv->core; + + dev_dbg(priv->dev, "Disabling PLL\n"); + + lockdep_assert_held(&cs42l43->pll_lock); + + regmap_multi_reg_write(cs42l43->regmap, disable_seq, ARRAY_SIZE(disable_seq)); + regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK, 0); + regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL, + CS42L43_PLL_REFCLK_EN_MASK, 0); + + cs42l43->sdw_pll_active = false; + + return 0; +} + +static int cs42l43_pll_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + mutex_lock(&cs42l43->pll_lock); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (priv->refclk_src == CS42L43_SYSCLK_MCLK) { + ret = clk_prepare_enable(priv->mclk); + if (ret) { + dev_err(priv->dev, "Failed to enable MCLK: %d\n", ret); + break; + } + } + + ret = cs42l43_enable_pll(priv); + break; + case SND_SOC_DAPM_POST_PMD: + ret = cs42l43_disable_pll(priv); + + if (priv->refclk_src == CS42L43_SYSCLK_MCLK) + clk_disable_unprepare(priv->mclk); + break; + default: + ret = 0; + break; + } + + mutex_unlock(&cs42l43->pll_lock); + + return ret; +} + +static int cs42l43_dapm_wait_completion(struct completion *pmu, struct completion *pmd, + int event, int timeout_ms) +{ + unsigned long time_left; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + reinit_completion(pmu); + return 0; + case SND_SOC_DAPM_PRE_PMD: + reinit_completion(pmd); + return 0; + case SND_SOC_DAPM_POST_PMU: + time_left = wait_for_completion_timeout(pmu, msecs_to_jiffies(timeout_ms)); + break; + case SND_SOC_DAPM_POST_PMD: + time_left = wait_for_completion_timeout(pmd, msecs_to_jiffies(timeout_ms)); + break; + default: + return 0; + } + + if (!time_left) + return -ETIMEDOUT; + else + return 0; +} + +static int cs42l43_spkr_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + return cs42l43_dapm_wait_completion(&priv->spkr_startup, + &priv->spkr_shutdown, event, + CS42L43_SPK_TIMEOUT_MS); +} + +static int cs42l43_spkl_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + + return cs42l43_dapm_wait_completion(&priv->spkl_startup, + &priv->spkl_shutdown, event, + CS42L43_SPK_TIMEOUT_MS); +} + +static int cs42l43_hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int mask = 1 << w->shift; + unsigned int val = 0; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + val = mask; + fallthrough; + case SND_SOC_DAPM_PRE_PMD: + priv->hp_ena &= ~mask; + priv->hp_ena |= val; + + ret = cs42l43_dapm_wait_completion(&priv->hp_startup, + &priv->hp_shutdown, event, + CS42L43_HP_TIMEOUT_MS); + if (ret) + return ret; + + if (!priv->load_detect_running) + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8, + mask, val); + break; + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + if (priv->load_detect_running) + break; + + ret = cs42l43_dapm_wait_completion(&priv->hp_startup, + &priv->hp_shutdown, event, + CS42L43_HP_TIMEOUT_MS); + if (ret) + return ret; + break; + default: + break; + } + + return 0; +} + +static int cs42l43_mic_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int reg, ramp, mute; + unsigned int *val; + int ret; + + switch (w->shift) { + case CS42L43_ADC1_EN_SHIFT: + case CS42L43_PDM1_DIN_L_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2; + ramp = CS42L43_DECIM1_VD_RAMP_MASK; + mute = CS42L43_DECIM1_MUTE_MASK; + val = &priv->decim_cache[0]; + break; + case CS42L43_ADC2_EN_SHIFT: + case CS42L43_PDM1_DIN_R_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2; + ramp = CS42L43_DECIM2_VD_RAMP_MASK; + mute = CS42L43_DECIM2_MUTE_MASK; + val = &priv->decim_cache[1]; + break; + case CS42L43_PDM2_DIN_L_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4; + ramp = CS42L43_DECIM3_VD_RAMP_MASK; + mute = CS42L43_DECIM3_MUTE_MASK; + val = &priv->decim_cache[2]; + break; + case CS42L43_PDM2_DIN_R_EN_SHIFT: + reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4; + ramp = CS42L43_DECIM4_VD_RAMP_MASK; + mute = CS42L43_DECIM4_MUTE_MASK; + val = &priv->decim_cache[3]; + break; + default: + dev_err(priv->dev, "Invalid microphone shift: %d\n", w->shift); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regmap_read(cs42l43->regmap, reg, val); + if (ret) { + dev_err(priv->dev, + "Failed to cache decimator settings: %d\n", + ret); + return ret; + } + + regmap_update_bits(cs42l43->regmap, reg, mute | ramp, mute); + break; + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(cs42l43->regmap, reg, mute | ramp, *val); + break; + default: + break; + } + + return 0; +} + +static int cs42l43_adc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + unsigned int mask = 1 << w->shift; + unsigned int val = 0; + int ret; + + ret = cs42l43_mic_ev(w, kcontrol, event); + if (ret) + return ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + val = mask; + fallthrough; + case SND_SOC_DAPM_PRE_PMD: + priv->adc_ena &= ~mask; + priv->adc_ena |= val; + + if (!priv->load_detect_running) + regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3, + mask, val); + fallthrough; + default: + return 0; + } +} + +static const struct snd_soc_dapm_widget cs42l43_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, cs42l43_pll_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("ADC1_IN1_P"), + SND_SOC_DAPM_INPUT("ADC1_IN1_N"), + SND_SOC_DAPM_INPUT("ADC1_IN2_P"), + SND_SOC_DAPM_INPUT("ADC1_IN2_N"), + SND_SOC_DAPM_INPUT("ADC2_IN_P"), + SND_SOC_DAPM_INPUT("ADC2_IN_N"), + + SND_SOC_DAPM_INPUT("PDM1_DIN"), + SND_SOC_DAPM_INPUT("PDM2_DIN"), + + SND_SOC_DAPM_MUX("ADC1 Input", SND_SOC_NOPM, 0, 0, &cs42l43_adc1_input_ctl), + + SND_SOC_DAPM_PGA_E("ADC1", SND_SOC_NOPM, CS42L43_ADC1_EN_SHIFT, 0, NULL, 0, + cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("ADC2", SND_SOC_NOPM, CS42L43_ADC2_EN_SHIFT, 0, NULL, 0, + cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA_E("PDM1L", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_L_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PDM1R", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_R_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PDM2L", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_L_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PDM2R", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_R_EN_SHIFT, + 0, NULL, 0, cs42l43_mic_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("Decimator 1 Mode", SND_SOC_NOPM, 0, 0, + &cs42l43_dec_mode_ctl[0]), + SND_SOC_DAPM_MUX("Decimator 2 Mode", SND_SOC_NOPM, 0, 0, + &cs42l43_dec_mode_ctl[1]), + + SND_SOC_DAPM_PGA("Decimator 1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Decimator 2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Decimator 3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Decimator 4", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("FSYNC", 0, CS42L43_ASP_CTRL, CS42L43_ASP_FSYNC_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("BCLK", 1, CS42L43_ASP_CTRL, CS42L43_ASP_BCLK_EN_SHIFT, + 0, NULL, 0), + + SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH4_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH5_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5, + CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH6_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH1_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 1, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH2_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX3", NULL, 2, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH3_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX4", NULL, 3, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH4_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX5", NULL, 4, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH5_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("ASPRX6", NULL, 5, + CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH6_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_OUT("DP1TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP1TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP1TX3", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP1TX4", NULL, 3, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("DP2TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("DP3TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP3TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("DP4TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("DP5RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP5RX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("DP6RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP6RX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("DP7RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP7RX2", NULL, 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-amp", 0, 0), + + SND_SOC_DAPM_PGA_E("AMP1", CS42L43_BLOCK_EN10, CS42L43_AMP1_EN_SHIFT, 0, NULL, 0, + cs42l43_spkl_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("AMP2", CS42L43_BLOCK_EN10, CS42L43_AMP2_EN_SHIFT, 0, NULL, 0, + cs42l43_spkr_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("AMP1_OUT_P"), + SND_SOC_DAPM_OUTPUT("AMP1_OUT_N"), + SND_SOC_DAPM_OUTPUT("AMP2_OUT_P"), + SND_SOC_DAPM_OUTPUT("AMP2_OUT_N"), + + SND_SOC_DAPM_PGA("SPDIF", CS42L43_BLOCK_EN11, CS42L43_SPDIF_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPDIF_TX"), + + SND_SOC_DAPM_PGA_E("HP", SND_SOC_NOPM, CS42L43_HP_EN_SHIFT, 0, NULL, 0, + cs42l43_hp_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("AMP3_OUT"), + SND_SOC_DAPM_OUTPUT("AMP4_OUT"), + + SND_SOC_DAPM_SIGGEN("Tone"), + SND_SOC_DAPM_SUPPLY("Tone Generator", CS42L43_BLOCK_EN9, CS42L43_TONE_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 1", CS42L43_TONE_CH1_CTRL, + CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 2", CS42L43_TONE_CH2_CTRL, + CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0), + + SND_SOC_DAPM_SUPPLY("ISRC1", CS42L43_BLOCK_EN5, CS42L43_ISRC1_BANK_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ISRC2", CS42L43_BLOCK_EN5, CS42L43_ISRC2_BANK_EN_SHIFT, + 0, NULL, 0), + + SND_SOC_DAPM_PGA("ISRC1INT2", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC1INT1", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC1DEC2", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC1DEC1", CS42L43_ISRC1_CTRL, + CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("ISRC2INT2", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2INT1", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2DEC2", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ISRC2DEC1", CS42L43_ISRC2_CTRL, + CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("ASRC_INT", CS42L43_BLOCK_EN4, + CS42L43_ASRC_INT_BANK_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ASRC_DEC", CS42L43_BLOCK_EN4, + CS42L43_ASRC_DEC_BANK_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("ASRC_INT1", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_INT2", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_INT3", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT3_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_INT4", CS42L43_ASRC_INT_ENABLES, + CS42L43_ASRC_INT4_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC1", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC1_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC2", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC2_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC3", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC3_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("ASRC_DEC4", CS42L43_ASRC_DEC_ENABLES, + CS42L43_ASRC_DEC4_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("EQ Clock", CS42L43_BLOCK_EN7, CS42L43_EQ_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA_E("EQ", CS42L43_START_EQZ0, CS42L43_START_FILTER_SHIFT, + 0, NULL, 0, cs42l43_eq_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("Mixer Core", CS42L43_BLOCK_EN6, CS42L43_MIXER_EN_SHIFT, + 0, NULL, 0), + CS42L43_DAPM_MUX("ASPTX1", asptx1), + CS42L43_DAPM_MUX("ASPTX2", asptx2), + CS42L43_DAPM_MUX("ASPTX3", asptx3), + CS42L43_DAPM_MUX("ASPTX4", asptx4), + CS42L43_DAPM_MUX("ASPTX5", asptx5), + CS42L43_DAPM_MUX("ASPTX6", asptx6), + + CS42L43_DAPM_MUX("DP1TX1", dp1tx1), + CS42L43_DAPM_MUX("DP1TX2", dp1tx2), + CS42L43_DAPM_MUX("DP1TX3", dp1tx3), + CS42L43_DAPM_MUX("DP1TX4", dp1tx4), + CS42L43_DAPM_MUX("DP2TX1", dp2tx1), + CS42L43_DAPM_MUX("DP2TX2", dp2tx2), + CS42L43_DAPM_MUX("DP3TX1", dp3tx1), + CS42L43_DAPM_MUX("DP3TX2", dp3tx2), + CS42L43_DAPM_MUX("DP4TX1", dp4tx1), + CS42L43_DAPM_MUX("DP4TX2", dp4tx2), + + CS42L43_DAPM_MUX("ASRC INT1", asrcint1), + CS42L43_DAPM_MUX("ASRC INT2", asrcint2), + CS42L43_DAPM_MUX("ASRC INT3", asrcint3), + CS42L43_DAPM_MUX("ASRC INT4", asrcint4), + CS42L43_DAPM_MUX("ASRC DEC1", asrcdec1), + CS42L43_DAPM_MUX("ASRC DEC2", asrcdec2), + CS42L43_DAPM_MUX("ASRC DEC3", asrcdec3), + CS42L43_DAPM_MUX("ASRC DEC4", asrcdec4), + + CS42L43_DAPM_MUX("ISRC1INT1", isrc1int1), + CS42L43_DAPM_MUX("ISRC1INT2", isrc1int2), + CS42L43_DAPM_MUX("ISRC1DEC1", isrc1dec1), + CS42L43_DAPM_MUX("ISRC1DEC2", isrc1dec2), + CS42L43_DAPM_MUX("ISRC2INT1", isrc2int1), + CS42L43_DAPM_MUX("ISRC2INT2", isrc2int2), + CS42L43_DAPM_MUX("ISRC2DEC1", isrc2dec1), + CS42L43_DAPM_MUX("ISRC2DEC2", isrc2dec2), + + CS42L43_DAPM_MUX("SPDIF1", spdif1), + CS42L43_DAPM_MUX("SPDIF2", spdif2), + + CS42L43_DAPM_MIXER("EQ1", eq1), + CS42L43_DAPM_MIXER("EQ2", eq2), + + CS42L43_DAPM_MIXER("Speaker L", amp1), + CS42L43_DAPM_MIXER("Speaker R", amp2), + + CS42L43_DAPM_MIXER("Headphone L", amp3), + CS42L43_DAPM_MIXER("Headphone R", amp4), +}; + +static const struct snd_soc_dapm_route cs42l43_routes[] = { + { "ADC1_IN1_P", NULL, "PLL" }, + { "ADC1_IN1_N", NULL, "PLL" }, + { "ADC1_IN2_P", NULL, "PLL" }, + { "ADC1_IN2_N", NULL, "PLL" }, + { "ADC2_IN_P", NULL, "PLL" }, + { "ADC2_IN_N", NULL, "PLL" }, + { "PDM1_DIN", NULL, "PLL" }, + { "PDM2_DIN", NULL, "PLL" }, + { "AMP1_OUT_P", NULL, "PLL" }, + { "AMP1_OUT_N", NULL, "PLL" }, + { "AMP2_OUT_P", NULL, "PLL" }, + { "AMP2_OUT_N", NULL, "PLL" }, + { "SPDIF_TX", NULL, "PLL" }, + { "HP", NULL, "PLL" }, + { "AMP3_OUT", NULL, "PLL" }, + { "AMP4_OUT", NULL, "PLL" }, + { "Tone 1", NULL, "PLL" }, + { "Tone 2", NULL, "PLL" }, + { "ASP Playback", NULL, "PLL" }, + { "ASP Capture", NULL, "PLL" }, + { "DP1 Capture", NULL, "PLL" }, + { "DP2 Capture", NULL, "PLL" }, + { "DP3 Capture", NULL, "PLL" }, + { "DP4 Capture", NULL, "PLL" }, + { "DP5 Playback", NULL, "PLL" }, + { "DP6 Playback", NULL, "PLL" }, + { "DP7 Playback", NULL, "PLL" }, + + { "ADC1 Input", "IN1", "ADC1_IN1_P" }, + { "ADC1 Input", "IN1", "ADC1_IN1_N" }, + { "ADC1 Input", "IN2", "ADC1_IN2_P" }, + { "ADC1 Input", "IN2", "ADC1_IN2_N" }, + + { "ADC1", NULL, "ADC1 Input" }, + { "ADC2", NULL, "ADC2_IN_P" }, + { "ADC2", NULL, "ADC2_IN_N" }, + + { "PDM1L", NULL, "PDM1_DIN" }, + { "PDM1R", NULL, "PDM1_DIN" }, + { "PDM2L", NULL, "PDM2_DIN" }, + { "PDM2R", NULL, "PDM2_DIN" }, + + { "Decimator 1 Mode", "PDM", "PDM1L" }, + { "Decimator 1 Mode", "ADC", "ADC1" }, + { "Decimator 2 Mode", "PDM", "PDM1R" }, + { "Decimator 2 Mode", "ADC", "ADC2" }, + + { "Decimator 1", NULL, "Decimator 1 Mode" }, + { "Decimator 2", NULL, "Decimator 2 Mode" }, + { "Decimator 3", NULL, "PDM2L" }, + { "Decimator 4", NULL, "PDM2R" }, + + { "ASP Capture", NULL, "ASPTX1" }, + { "ASP Capture", NULL, "ASPTX2" }, + { "ASP Capture", NULL, "ASPTX3" }, + { "ASP Capture", NULL, "ASPTX4" }, + { "ASP Capture", NULL, "ASPTX5" }, + { "ASP Capture", NULL, "ASPTX6" }, + { "ASPTX1", NULL, "BCLK" }, + { "ASPTX2", NULL, "BCLK" }, + { "ASPTX3", NULL, "BCLK" }, + { "ASPTX4", NULL, "BCLK" }, + { "ASPTX5", NULL, "BCLK" }, + { "ASPTX6", NULL, "BCLK" }, + + { "ASPRX1", NULL, "ASP Playback" }, + { "ASPRX2", NULL, "ASP Playback" }, + { "ASPRX3", NULL, "ASP Playback" }, + { "ASPRX4", NULL, "ASP Playback" }, + { "ASPRX5", NULL, "ASP Playback" }, + { "ASPRX6", NULL, "ASP Playback" }, + { "ASPRX1", NULL, "BCLK" }, + { "ASPRX2", NULL, "BCLK" }, + { "ASPRX3", NULL, "BCLK" }, + { "ASPRX4", NULL, "BCLK" }, + { "ASPRX5", NULL, "BCLK" }, + { "ASPRX6", NULL, "BCLK" }, + + { "DP1 Capture", NULL, "DP1TX1" }, + { "DP1 Capture", NULL, "DP1TX2" }, + { "DP1 Capture", NULL, "DP1TX3" }, + { "DP1 Capture", NULL, "DP1TX4" }, + + { "DP2 Capture", NULL, "DP2TX1" }, + { "DP2 Capture", NULL, "DP2TX2" }, + + { "DP3 Capture", NULL, "DP3TX1" }, + { "DP3 Capture", NULL, "DP3TX2" }, + + { "DP4 Capture", NULL, "DP4TX1" }, + { "DP4 Capture", NULL, "DP4TX2" }, + + { "DP5RX1", NULL, "DP5 Playback" }, + { "DP5RX2", NULL, "DP5 Playback" }, + + { "DP6RX1", NULL, "DP6 Playback" }, + { "DP6RX2", NULL, "DP6 Playback" }, + + { "DP7RX1", NULL, "DP7 Playback" }, + { "DP7RX2", NULL, "DP7 Playback" }, + + { "AMP1", NULL, "vdd-amp" }, + { "AMP2", NULL, "vdd-amp" }, + + { "AMP1_OUT_P", NULL, "AMP1" }, + { "AMP1_OUT_N", NULL, "AMP1" }, + { "AMP2_OUT_P", NULL, "AMP2" }, + { "AMP2_OUT_N", NULL, "AMP2" }, + + { "SPDIF_TX", NULL, "SPDIF" }, + + { "AMP3_OUT", NULL, "HP" }, + { "AMP4_OUT", NULL, "HP" }, + + { "Tone 1", NULL, "Tone" }, + { "Tone 1", NULL, "Tone Generator" }, + { "Tone 2", NULL, "Tone" }, + { "Tone 2", NULL, "Tone Generator" }, + + { "ISRC1INT2", NULL, "ISRC1" }, + { "ISRC1INT1", NULL, "ISRC1" }, + { "ISRC1DEC2", NULL, "ISRC1" }, + { "ISRC1DEC1", NULL, "ISRC1" }, + + { "ISRC2INT2", NULL, "ISRC2" }, + { "ISRC2INT1", NULL, "ISRC2" }, + { "ISRC2DEC2", NULL, "ISRC2" }, + { "ISRC2DEC1", NULL, "ISRC2" }, + + { "ASRC_INT1", NULL, "ASRC_INT" }, + { "ASRC_INT2", NULL, "ASRC_INT" }, + { "ASRC_INT3", NULL, "ASRC_INT" }, + { "ASRC_INT4", NULL, "ASRC_INT" }, + { "ASRC_DEC1", NULL, "ASRC_DEC" }, + { "ASRC_DEC2", NULL, "ASRC_DEC" }, + { "ASRC_DEC3", NULL, "ASRC_DEC" }, + { "ASRC_DEC4", NULL, "ASRC_DEC" }, + + { "EQ", NULL, "EQ Clock" }, + + CS42L43_MUX_ROUTES("ASPTX1", "ASPTX1"), + CS42L43_MUX_ROUTES("ASPTX2", "ASPTX2"), + CS42L43_MUX_ROUTES("ASPTX3", "ASPTX3"), + CS42L43_MUX_ROUTES("ASPTX4", "ASPTX4"), + CS42L43_MUX_ROUTES("ASPTX5", "ASPTX5"), + CS42L43_MUX_ROUTES("ASPTX6", "ASPTX6"), + + CS42L43_MUX_ROUTES("DP1TX1", "DP1TX1"), + CS42L43_MUX_ROUTES("DP1TX2", "DP1TX2"), + CS42L43_MUX_ROUTES("DP1TX3", "DP1TX3"), + CS42L43_MUX_ROUTES("DP1TX4", "DP1TX4"), + CS42L43_MUX_ROUTES("DP2TX1", "DP2TX1"), + CS42L43_MUX_ROUTES("DP2TX2", "DP2TX2"), + CS42L43_MUX_ROUTES("DP3TX1", "DP3TX1"), + CS42L43_MUX_ROUTES("DP3TX2", "DP3TX2"), + CS42L43_MUX_ROUTES("DP4TX1", "DP4TX1"), + CS42L43_MUX_ROUTES("DP4TX2", "DP4TX2"), + + CS42L43_MUX_ROUTES("ASRC INT1", "ASRC_INT1"), + CS42L43_MUX_ROUTES("ASRC INT2", "ASRC_INT2"), + CS42L43_MUX_ROUTES("ASRC INT3", "ASRC_INT3"), + CS42L43_MUX_ROUTES("ASRC INT4", "ASRC_INT4"), + CS42L43_MUX_ROUTES("ASRC DEC1", "ASRC_DEC1"), + CS42L43_MUX_ROUTES("ASRC DEC2", "ASRC_DEC2"), + CS42L43_MUX_ROUTES("ASRC DEC3", "ASRC_DEC3"), + CS42L43_MUX_ROUTES("ASRC DEC4", "ASRC_DEC4"), + + CS42L43_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + CS42L43_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + CS42L43_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + CS42L43_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + CS42L43_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + CS42L43_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + CS42L43_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + CS42L43_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + CS42L43_MUX_ROUTES("SPDIF1", "SPDIF"), + CS42L43_MUX_ROUTES("SPDIF2", "SPDIF"), + + CS42L43_MIXER_ROUTES("EQ1", "EQ"), + CS42L43_MIXER_ROUTES("EQ2", "EQ"), + + CS42L43_MIXER_ROUTES("Speaker L", "AMP1"), + CS42L43_MIXER_ROUTES("Speaker R", "AMP2"), + + CS42L43_MIXER_ROUTES("Headphone L", "HP"), + CS42L43_MIXER_ROUTES("Headphone R", "HP"), +}; + +static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id, + int src, unsigned int freq, int dir) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + int ret; + + mutex_lock(&cs42l43->pll_lock); + ret = cs42l43_set_pll(priv, src, freq); + mutex_unlock(&cs42l43->pll_lock); + + return ret; +} + +static int cs42l43_component_probe(struct snd_soc_component *component) +{ + struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component); + struct cs42l43 *cs42l43 = priv->core; + + snd_soc_component_init_regmap(component, cs42l43->regmap); + + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots); + cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots); + + priv->component = component; + priv->constraint = cs42l43_constraint; + + return 0; +} + +static const struct snd_soc_component_driver cs42l43_component_drv = { + .name = "cs42l43-codec", + + .probe = cs42l43_component_probe, + .set_sysclk = cs42l43_set_sysclk, + .set_jack = cs42l43_set_jack, + + .endianness = 1, + + .controls = cs42l43_controls, + .num_controls = ARRAY_SIZE(cs42l43_controls), + .dapm_widgets = cs42l43_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l43_widgets), + .dapm_routes = cs42l43_routes, + .num_dapm_routes = ARRAY_SIZE(cs42l43_routes), +}; + +struct cs42l43_irq { + unsigned int irq; + const char *name; + irq_handler_t handler; +}; + +static const struct cs42l43_irq cs42l43_irqs[] = { + { CS42L43_PLL_LOST_LOCK, "pll lost lock", cs42l43_pll_lost_lock }, + { CS42L43_PLL_READY, "pll ready", cs42l43_pll_ready }, + { CS42L43_HP_STARTUP_DONE, "hp startup", cs42l43_hp_startup }, + { CS42L43_HP_SHUTDOWN_DONE, "hp shutdown", cs42l43_hp_shutdown }, + { CS42L43_HSDET_DONE, "type detect", cs42l43_type_detect }, + { CS42L43_TIPSENSE_UNPLUG_PDET, "tip sense unplug", cs42l43_tip_sense }, + { CS42L43_TIPSENSE_PLUG_PDET, "tip sense plug", cs42l43_tip_sense }, + { CS42L43_DC_DETECT1_TRUE, "button press", cs42l43_button_press }, + { CS42L43_DC_DETECT1_FALSE, "button release", cs42l43_button_release }, + { CS42L43_HSBIAS_CLAMPED, "hsbias detect clamp", cs42l43_bias_detect_clamp }, + { CS42L43_AMP2_CLK_STOP_FAULT, "spkr clock stop", cs42l43_spkr_clock_stop }, + { CS42L43_AMP1_CLK_STOP_FAULT, "spkl clock stop", cs42l43_spkl_clock_stop }, + { CS42L43_AMP2_VDDSPK_FAULT, "spkr brown out", cs42l43_spkr_brown_out }, + { CS42L43_AMP1_VDDSPK_FAULT, "spkl brown out", cs42l43_spkl_brown_out }, + { CS42L43_AMP2_SHUTDOWN_DONE, "spkr shutdown", cs42l43_spkr_shutdown }, + { CS42L43_AMP1_SHUTDOWN_DONE, "spkl shutdown", cs42l43_spkl_shutdown }, + { CS42L43_AMP2_STARTUP_DONE, "spkr startup", cs42l43_spkr_startup }, + { CS42L43_AMP1_STARTUP_DONE, "spkl startup", cs42l43_spkl_startup }, + { CS42L43_AMP2_THERM_SHDN, "spkr thermal shutdown", cs42l43_spkr_therm_shutdown }, + { CS42L43_AMP1_THERM_SHDN, "spkl thermal shutdown", cs42l43_spkl_therm_shutdown }, + { CS42L43_AMP2_THERM_WARN, "spkr thermal warning", cs42l43_spkr_therm_warm }, + { CS42L43_AMP1_THERM_WARN, "spkl thermal warning", cs42l43_spkl_therm_warm }, + { CS42L43_AMP2_SCDET, "spkr short circuit", cs42l43_spkr_sc_detect }, + { CS42L43_AMP1_SCDET, "spkl short circuit", cs42l43_spkl_sc_detect }, + { CS42L43_HP_ILIMIT, "hp ilimit", cs42l43_hp_ilimit }, + { CS42L43_HP_LOADDET_DONE, "load detect done", cs42l43_load_detect }, +}; + +static int cs42l43_request_irq(struct cs42l43_codec *priv, + struct irq_domain *dom, const char * const name, + unsigned int irq, irq_handler_t handler, + unsigned long flags) +{ + int ret; + + ret = irq_create_mapping(dom, irq); + if (ret < 0) + return dev_err_probe(priv->dev, ret, "Failed to map IRQ %s\n", name); + + dev_dbg(priv->dev, "Request IRQ %d for %s\n", ret, name); + + ret = devm_request_threaded_irq(priv->dev, ret, NULL, handler, + IRQF_ONESHOT | flags, name, priv); + if (ret) + return dev_err_probe(priv->dev, ret, "Failed to request IRQ %s\n", name); + + return 0; +} + +static int cs42l43_shutter_irq(struct cs42l43_codec *priv, + struct irq_domain *dom, unsigned int shutter, + const char * const open_name, + const char * const close_name, + irq_handler_t handler) +{ + unsigned int open_irq, close_irq; + int ret; + + switch (shutter) { + case 0x1: + dev_warn(priv->dev, "Manual shutters, notifications not available\n"); + return 0; + case 0x2: + open_irq = CS42L43_GPIO1_RISE; + close_irq = CS42L43_GPIO1_FALL; + break; + case 0x4: + open_irq = CS42L43_GPIO2_RISE; + close_irq = CS42L43_GPIO2_FALL; + break; + case 0x8: + open_irq = CS42L43_GPIO3_RISE; + close_irq = CS42L43_GPIO3_FALL; + break; + default: + return 0; + } + + ret = cs42l43_request_irq(priv, dom, close_name, close_irq, handler, IRQF_SHARED); + if (ret) + return ret; + + return cs42l43_request_irq(priv, dom, open_name, open_irq, handler, IRQF_SHARED); +} + +static int cs42l43_codec_probe(struct platform_device *pdev) +{ + struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent); + struct cs42l43_codec *priv; + struct irq_domain *dom; + unsigned int val; + int i, ret; + + dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY); + if (!dom) + return -EPROBE_DEFER; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->core = cs42l43; + + platform_set_drvdata(pdev, priv); + + mutex_init(&priv->jack_lock); + mutex_init(&priv->spk_vu_lock); + + init_completion(&priv->hp_startup); + init_completion(&priv->hp_shutdown); + init_completion(&priv->spkr_shutdown); + init_completion(&priv->spkl_shutdown); + init_completion(&priv->spkr_startup); + init_completion(&priv->spkl_startup); + init_completion(&priv->pll_ready); + init_completion(&priv->type_detect); + init_completion(&priv->load_detect); + + INIT_DELAYED_WORK(&priv->tip_sense_work, cs42l43_tip_sense_work); + INIT_DELAYED_WORK(&priv->bias_sense_timeout, cs42l43_bias_sense_timeout); + INIT_DELAYED_WORK(&priv->button_press_work, cs42l43_button_press_work); + INIT_WORK(&priv->button_release_work, cs42l43_button_release_work); + + pm_runtime_set_autosuspend_delay(priv->dev, 100); + pm_runtime_use_autosuspend(priv->dev); + pm_runtime_set_active(priv->dev); + pm_runtime_get_noresume(priv->dev); + devm_pm_runtime_enable(priv->dev); + + for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) { + ret = cs42l43_request_irq(priv, dom, cs42l43_irqs[i].name, + cs42l43_irqs[i].irq, + cs42l43_irqs[i].handler, 0); + if (ret) + goto err_pm; + } + + ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val); + if (ret) { + dev_err(priv->dev, "Failed to check shutter source: %d\n", ret); + goto err_pm; + } + + ret = cs42l43_shutter_irq(priv, dom, val & CS42L43_MIC_SHUTTER_CFG_MASK, + "mic shutter open", "mic shutter close", + cs42l43_mic_shutter); + if (ret) + goto err_pm; + + ret = cs42l43_shutter_irq(priv, dom, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >> + CS42L43_SPK_SHUTTER_CFG_SHIFT, + "spk shutter open", "spk shutter close", + cs42l43_spk_shutter); + if (ret) + goto err_pm; + + // Don't use devm as we need to get against the MFD device + priv->mclk = clk_get_optional(cs42l43->dev, "mclk"); + if (IS_ERR(priv->mclk)) { + ret = PTR_ERR(priv->mclk); + dev_err_probe(priv->dev, ret, "Failed to get mclk\n"); + goto err_pm; + } + + ret = devm_snd_soc_register_component(priv->dev, &cs42l43_component_drv, + cs42l43_dais, ARRAY_SIZE(cs42l43_dais)); + if (ret) { + dev_err_probe(priv->dev, ret, "Failed to register component\n"); + goto err_clk; + } + + pm_runtime_mark_last_busy(priv->dev); + pm_runtime_put_autosuspend(priv->dev); + + return 0; + +err_clk: + clk_put(priv->mclk); +err_pm: + pm_runtime_put_sync(priv->dev); + + return ret; +} + +static int cs42l43_codec_remove(struct platform_device *pdev) +{ + struct cs42l43_codec *priv = platform_get_drvdata(pdev); + + clk_put(priv->mclk); + + return 0; +} + +static int cs42l43_codec_runtime_resume(struct device *dev) +{ + struct cs42l43_codec *priv = dev_get_drvdata(dev); + + dev_dbg(priv->dev, "Runtime resume\n"); + + // Toggle the speaker volume update incase the speaker volume was synced + cs42l43_spk_vu_sync(priv); + + return 0; +} + +DEFINE_RUNTIME_DEV_PM_OPS(cs42l43_codec_pm_ops, NULL, + cs42l43_codec_runtime_resume, NULL); + +static const struct platform_device_id cs42l43_codec_id_table[] = { + { "cs42l43-codec", }, + {} +}; +MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table); + +static struct platform_driver cs42l43_codec_driver = { + .driver = { + .name = "cs42l43-codec", + .pm = &cs42l43_codec_pm_ops, + }, + + .probe = cs42l43_codec_probe, + .remove = cs42l43_codec_remove, + .id_table = cs42l43_codec_id_table, +}; +module_platform_driver(cs42l43_codec_driver); + +MODULE_IMPORT_NS(SND_SOC_CS42L43); + +MODULE_DESCRIPTION("CS42L43 CODEC Driver"); +MODULE_AUTHOR("Charles Keepax "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h new file mode 100644 index 0000000000..bf4f728eea --- /dev/null +++ b/sound/soc/codecs/cs42l43.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CS42L43 CODEC driver internal data + * + * Copyright (C) 2022-2023 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CS42L43_ASOC_INT_H +#define CS42L43_ASOC_INT_H + +#define CS42L43_INTERNAL_SYSCLK 24576000 +#define CS42L43_DEFAULT_SLOTS 0x3F + +#define CS42L43_PLL_TIMEOUT_MS 200 +#define CS42L43_SPK_TIMEOUT_MS 100 +#define CS42L43_HP_TIMEOUT_MS 2000 +#define CS42L43_LOAD_TIMEOUT_MS 1000 + +#define CS42L43_ASP_MAX_CHANNELS 6 +#define CS42L43_N_EQ_COEFFS 15 + +#define CS42L43_N_BUTTONS 6 + +struct cs42l43_codec { + struct device *dev; + struct cs42l43 *core; + struct snd_soc_component *component; + + struct clk *mclk; + + int n_slots; + int slot_width; + int tx_slots[CS42L43_ASP_MAX_CHANNELS]; + int rx_slots[CS42L43_ASP_MAX_CHANNELS]; + struct snd_pcm_hw_constraint_list constraint; + + u32 eq_coeffs[CS42L43_N_EQ_COEFFS]; + + unsigned int refclk_src; + unsigned int refclk_freq; + struct completion pll_ready; + + unsigned int decim_cache[4]; + unsigned int adc_ena; + unsigned int hp_ena; + + struct completion hp_startup; + struct completion hp_shutdown; + struct completion spkr_shutdown; + struct completion spkl_shutdown; + struct completion spkr_startup; + struct completion spkl_startup; + // Lock to ensure speaker VU updates don't clash + struct mutex spk_vu_lock; + + // Lock for all jack detect operations + struct mutex jack_lock; + struct snd_soc_jack *jack_hp; + + bool use_ring_sense; + unsigned int tip_debounce_ms; + unsigned int bias_low; + unsigned int bias_sense_ua; + unsigned int bias_ramp_ms; + unsigned int detect_us; + unsigned int buttons[CS42L43_N_BUTTONS]; + + struct delayed_work tip_sense_work; + struct delayed_work bias_sense_timeout; + struct delayed_work button_press_work; + struct work_struct button_release_work; + struct completion type_detect; + struct completion load_detect; + + bool load_detect_running; + bool button_detect_running; + bool jack_present; + int jack_override; +}; + +#if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW) + +int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction); + +#else + +static inline int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return -EINVAL; +} + +#define cs42l43_sdw_remove_peripheral NULL +#define cs42l43_sdw_set_stream NULL + +#endif + +int cs42l43_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *d); +void cs42l43_bias_sense_timeout(struct work_struct *work); +void cs42l43_tip_sense_work(struct work_struct *work); +void cs42l43_button_press_work(struct work_struct *work); +void cs42l43_button_release_work(struct work_struct *work); +irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data); +irqreturn_t cs42l43_button_press(int irq, void *data); +irqreturn_t cs42l43_button_release(int irq, void *data); +irqreturn_t cs42l43_tip_sense(int irq, void *data); +int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + +extern const struct soc_enum cs42l43_jack_enum; + +#endif /* CS42L43_ASOC_INT_H */ diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c new file mode 100644 index 0000000000..5ed2ef83dc --- /dev/null +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l56.c -- CS42L51 ALSA SoC I2C audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin + */ + +#include +#include +#include + +#include "cs42l51.h" + +static struct i2c_device_id cs42l51_i2c_id[] = { + {"cs42l51", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id); + +static const struct of_device_id cs42l51_of_match[] = { + { .compatible = "cirrus,cs42l51", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs42l51_of_match); + +static int cs42l51_i2c_probe(struct i2c_client *i2c) +{ + struct regmap_config config; + + config = cs42l51_regmap; + + return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config)); +} + +static void cs42l51_i2c_remove(struct i2c_client *i2c) +{ + cs42l51_remove(&i2c->dev); +} + +static const struct dev_pm_ops cs42l51_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume) +}; + +static struct i2c_driver cs42l51_i2c_driver = { + .driver = { + .name = "cs42l51", + .of_match_table = cs42l51_of_match, + .pm = &cs42l51_pm_ops, + }, + .probe = cs42l51_i2c_probe, + .remove = cs42l51_i2c_remove, + .id_table = cs42l51_i2c_id, +}; + +module_i2c_driver(cs42l51_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L51 I2C Driver"); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c new file mode 100644 index 0000000000..e4827b8c2b --- /dev/null +++ b/sound/soc/codecs/cs42l51.c @@ -0,0 +1,828 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l51.c + * + * ASoC Driver for Cirrus Logic CS42L51 codecs + * + * Copyright (c) 2010 Arnaud Patard + * + * Based on cs4270.c - Copyright (c) Freescale Semiconductor + * + * For now: + * - Only I2C is support. Not SPI + * - master mode *NOT* supported + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs42l51.h" + +enum master_slave_mode { + MODE_SLAVE, + MODE_SLAVE_AUTO, + MODE_MASTER, +}; + +static const char * const cs42l51_supply_names[] = { + "VL", + "VD", + "VA", + "VAHP", +}; + +struct cs42l51_private { + unsigned int mclk; + struct clk *mclk_handle; + unsigned int audio_mode; /* The mode (I2S or left-justified) */ + enum master_slave_mode func; + struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)]; + struct gpio_desc *reset_gpio; + struct regmap *regmap; +}; + +#define CS42L51_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) + +static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned long value = snd_soc_component_read(component, CS42L51_PCM_MIXER)&3; + + switch (value) { + default: + case 0: + ucontrol->value.enumerated.item[0] = 0; + break; + /* same value : (L+R)/2 and (R+L)/2 */ + case 1: + case 2: + ucontrol->value.enumerated.item[0] = 1; + break; + case 3: + ucontrol->value.enumerated.item[0] = 2; + break; + } + + return 0; +} + +#define CHAN_MIX_NORMAL 0x00 +#define CHAN_MIX_BOTH 0x55 +#define CHAN_MIX_SWAP 0xFF + +static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned char val; + + switch (ucontrol->value.enumerated.item[0]) { + default: + case 0: + val = CHAN_MIX_NORMAL; + break; + case 1: + val = CHAN_MIX_BOTH; + break; + case 2: + val = CHAN_MIX_SWAP; + break; + } + + snd_soc_component_write(component, CS42L51_PCM_MIXER, val); + + return 1; +} + +static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0); +static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0); + +static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0); + +static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0); +static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0); +static const char *chan_mix[] = { + "L R", + "L+R", + "R L", +}; + +static const DECLARE_TLV_DB_SCALE(pga_tlv, -300, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_att_tlv, -9600, 100, 0); + +static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix); + +static const struct snd_kcontrol_new cs42l51_snd_controls[] = { + SOC_DOUBLE_R_SX_TLV("PCM Playback Volume", + CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, + 0, 0x19, 0x7F, adc_pcm_tlv), + SOC_DOUBLE_R("PCM Playback Switch", + CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1), + SOC_DOUBLE_R_SX_TLV("Analog Playback Volume", + CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL, + 0, 0x34, 0xE4, aout_tlv), + SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", + CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, + 0, 0x19, 0x7F, adc_pcm_tlv), + SOC_DOUBLE_R("ADC Mixer Switch", + CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1), + SOC_DOUBLE_R_SX_TLV("ADC Attenuator Volume", + CS42L51_ADCA_ATT, CS42L51_ADCB_ATT, + 0, 0xA0, 96, adc_att_tlv), + SOC_DOUBLE_R_SX_TLV("PGA Volume", + CS42L51_ALC_PGA_CTL, CS42L51_ALC_PGB_CTL, + 0, 0x1A, 30, pga_tlv), + SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0), + SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0), + SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0), + SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0), + SOC_DOUBLE_TLV("Mic Boost Volume", + CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv), + SOC_DOUBLE_TLV("ADC Boost Volume", + CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv), + SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv), + SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv), + SOC_ENUM_EXT("PCM channel mixer", + cs42l51_chan_mix, + cs42l51_get_chan_mix, cs42l51_set_chan_mix), +}; + +/* + * to power down, one must: + * 1.) Enable the PDN bit + * 2.) enable power-down for the select channels + * 3.) disable the PDN bit. + */ +static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, CS42L51_POWER_CTL1, + CS42L51_POWER_CTL1_PDN, + CS42L51_POWER_CTL1_PDN); + break; + default: + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, CS42L51_POWER_CTL1, + CS42L51_POWER_CTL1_PDN, 0); + break; + } + + return 0; +} + +static const char *cs42l51_dac_names[] = {"Direct PCM", + "DSP PCM", "ADC"}; +static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum, + CS42L51_DAC_CTL, 6, cs42l51_dac_names); +static const struct snd_kcontrol_new cs42l51_dac_mux_controls = + SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum); + +static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left", + "MIC Left", "MIC+preamp Left"}; +static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum, + CS42L51_ADC_INPUT, 4, cs42l51_adcl_names); +static const struct snd_kcontrol_new cs42l51_adcl_mux_controls = + SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum); + +static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right", + "MIC Right", "MIC+preamp Right"}; +static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum, + CS42L51_ADC_INPUT, 6, cs42l51_adcr_names); +static const struct snd_kcontrol_new cs42l51_adcr_mux_controls = + SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum); + +static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0, + cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0, + cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture", + CS42L51_POWER_CTL1, 1, 1, + cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture", + CS42L51_POWER_CTL1, 2, 1, + cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_DAC_E("Left DAC", NULL, CS42L51_POWER_CTL1, 5, 1, + cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_DAC_E("Right DAC", NULL, CS42L51_POWER_CTL1, 6, 1, + cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), + + /* analog/mic */ + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN2R"), + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("MICR"), + + SND_SOC_DAPM_MIXER("Mic Preamp Left", + CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0), + SND_SOC_DAPM_MIXER("Mic Preamp Right", + CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0), + + /* HP */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + + /* mux */ + SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, + &cs42l51_dac_mux_controls), + SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0, + &cs42l51_adcl_mux_controls), + SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0, + &cs42l51_adcr_mux_controls), +}; + +static int mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(comp); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return clk_prepare_enable(cs42l51->mclk_handle); + case SND_SOC_DAPM_POST_PMD: + /* Delay mclk shutdown to fulfill power-down sequence requirements */ + msleep(20); + clk_disable_unprepare(cs42l51->mclk_handle); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = { + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route cs42l51_routes[] = { + {"HPL", NULL, "Left DAC"}, + {"HPR", NULL, "Right DAC"}, + + {"Right DAC", NULL, "DAC Mux"}, + {"Left DAC", NULL, "DAC Mux"}, + + {"DAC Mux", "Direct PCM", "Playback"}, + {"DAC Mux", "DSP PCM", "Playback"}, + + {"Left ADC", NULL, "Left PGA"}, + {"Right ADC", NULL, "Right PGA"}, + + {"Mic Preamp Left", NULL, "MICL"}, + {"Mic Preamp Right", NULL, "MICR"}, + + {"PGA-ADC Mux Left", "AIN1 Left", "AIN1L" }, + {"PGA-ADC Mux Left", "AIN2 Left", "AIN2L" }, + {"PGA-ADC Mux Left", "MIC Left", "MICL" }, + {"PGA-ADC Mux Left", "MIC+preamp Left", "Mic Preamp Left" }, + {"PGA-ADC Mux Right", "AIN1 Right", "AIN1R" }, + {"PGA-ADC Mux Right", "AIN2 Right", "AIN2R" }, + {"PGA-ADC Mux Right", "MIC Right", "MICR" }, + {"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" }, + + {"Left PGA", NULL, "PGA-ADC Mux Left"}, + {"Right PGA", NULL, "PGA-ADC Mux Right"}, +}; + +static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component); + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + dev_err(component->dev, "invalid DAI format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + cs42l51->func = MODE_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + cs42l51->func = MODE_SLAVE_AUTO; + break; + default: + dev_err(component->dev, "Unknown master/slave configuration\n"); + return -EINVAL; + } + + return 0; +} + +struct cs42l51_ratios { + unsigned int ratio; + unsigned char speed_mode; + unsigned char mclk; +}; + +static struct cs42l51_ratios slave_ratios[] = { + { 512, CS42L51_QSM_MODE, 0 }, { 768, CS42L51_QSM_MODE, 0 }, + { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 }, + { 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 }, + { 256, CS42L51_HSM_MODE, 0 }, { 384, CS42L51_HSM_MODE, 0 }, + { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 }, + { 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 }, + { 128, CS42L51_SSM_MODE, 0 }, { 192, CS42L51_SSM_MODE, 0 }, + { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 }, + { 512, CS42L51_SSM_MODE, 0 }, { 768, CS42L51_SSM_MODE, 0 }, + { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 }, + { 256, CS42L51_DSM_MODE, 0 }, { 384, CS42L51_DSM_MODE, 0 }, +}; + +static struct cs42l51_ratios slave_auto_ratios[] = { + { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 }, + { 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 }, + { 512, CS42L51_HSM_MODE, 0 }, { 768, CS42L51_HSM_MODE, 0 }, + { 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 }, + { 256, CS42L51_SSM_MODE, 0 }, { 384, CS42L51_SSM_MODE, 0 }, + { 512, CS42L51_SSM_MODE, 1 }, { 768, CS42L51_SSM_MODE, 1 }, + { 128, CS42L51_DSM_MODE, 0 }, { 192, CS42L51_DSM_MODE, 0 }, + { 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 }, +}; + +/* + * Master mode mclk/fs ratios. + * Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges + * The table below provides support of following ratios: + * 128: SSM (%128) with div2 disabled + * 256: SSM (%128) with div2 enabled + * In both cases, if sampling rate is above 50kHz, SSM is overridden + * with DSM (%128) configuration + */ +static struct cs42l51_ratios master_ratios[] = { + { 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 }, +}; + +static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component); + + cs42l51->mclk = freq; + return 0; +} + +static int cs42l51_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component); + int ret; + unsigned int i; + unsigned int rate; + unsigned int ratio; + struct cs42l51_ratios *ratios = NULL; + int nr_ratios = 0; + int intf_ctl, power_ctl, fmt, mode; + + switch (cs42l51->func) { + case MODE_MASTER: + ratios = master_ratios; + nr_ratios = ARRAY_SIZE(master_ratios); + break; + case MODE_SLAVE: + ratios = slave_ratios; + nr_ratios = ARRAY_SIZE(slave_ratios); + break; + case MODE_SLAVE_AUTO: + ratios = slave_auto_ratios; + nr_ratios = ARRAY_SIZE(slave_auto_ratios); + break; + } + + /* Figure out which MCLK/LRCK ratio to use */ + rate = params_rate(params); /* Sampling rate, in Hz */ + ratio = cs42l51->mclk / rate; /* MCLK/LRCK ratio */ + for (i = 0; i < nr_ratios; i++) { + if (ratios[i].ratio == ratio) + break; + } + + if (i == nr_ratios) { + /* We did not find a matching ratio */ + dev_err(component->dev, "could not find matching ratio\n"); + return -EINVAL; + } + + intf_ctl = snd_soc_component_read(component, CS42L51_INTF_CTL); + power_ctl = snd_soc_component_read(component, CS42L51_MIC_POWER_CTL); + + intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S + | CS42L51_INTF_CTL_DAC_FORMAT(7)); + power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3) + | CS42L51_MIC_POWER_CTL_MCLK_DIV2); + + switch (cs42l51->func) { + case MODE_MASTER: + intf_ctl |= CS42L51_INTF_CTL_MASTER; + mode = ratios[i].speed_mode; + /* Force DSM mode if sampling rate is above 50kHz */ + if (rate > 50000) + mode = CS42L51_DSM_MODE; + power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode); + /* + * Auto detect mode is not applicable for master mode and has to + * be disabled. Otherwise SPEED[1:0] bits will be ignored. + */ + power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO; + break; + case MODE_SLAVE: + power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); + break; + case MODE_SLAVE_AUTO: + power_ctl |= CS42L51_MIC_POWER_CTL_AUTO; + break; + } + + switch (cs42l51->audio_mode) { + case SND_SOC_DAIFMT_I2S: + intf_ctl |= CS42L51_INTF_CTL_ADC_I2S; + intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S); + break; + case SND_SOC_DAIFMT_LEFT_J: + intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24); + break; + case SND_SOC_DAIFMT_RIGHT_J: + switch (params_width(params)) { + case 16: + fmt = CS42L51_DAC_DIF_RJ16; + break; + case 18: + fmt = CS42L51_DAC_DIF_RJ18; + break; + case 20: + fmt = CS42L51_DAC_DIF_RJ20; + break; + case 24: + fmt = CS42L51_DAC_DIF_RJ24; + break; + default: + dev_err(component->dev, "unknown format\n"); + return -EINVAL; + } + intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt); + break; + default: + dev_err(component->dev, "unknown format\n"); + return -EINVAL; + } + + if (ratios[i].mclk) + power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2; + + ret = snd_soc_component_write(component, CS42L51_INTF_CTL, intf_ctl); + if (ret < 0) + return ret; + + ret = snd_soc_component_write(component, CS42L51_MIC_POWER_CTL, power_ctl); + if (ret < 0) + return ret; + + return 0; +} + +static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + int reg; + int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE; + + reg = snd_soc_component_read(component, CS42L51_DAC_OUT_CTL); + + if (mute) + reg |= mask; + else + reg &= ~mask; + + return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg); +} + +static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + /* return dai id 0, whatever the endpoint index */ + return 0; +} + +static const struct snd_soc_dai_ops cs42l51_dai_ops = { + .hw_params = cs42l51_hw_params, + .set_sysclk = cs42l51_set_dai_sysclk, + .set_fmt = cs42l51_set_dai_fmt, + .mute_stream = cs42l51_dai_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver cs42l51_dai = { + .name = "cs42l51-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS42L51_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS42L51_FORMATS, + }, + .ops = &cs42l51_dai_ops, +}; + +static int cs42l51_component_probe(struct snd_soc_component *component) +{ + int ret, reg; + struct snd_soc_dapm_context *dapm; + struct cs42l51_private *cs42l51; + + cs42l51 = snd_soc_component_get_drvdata(component); + dapm = snd_soc_component_get_dapm(component); + + if (cs42l51->mclk_handle) + snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1); + + /* + * DAC configuration + * - Use signal processor + * - auto mute + * - vol changes immediate + * - no de-emphasize + */ + reg = CS42L51_DAC_CTL_DATA_SEL(1) + | CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0); + ret = snd_soc_component_write(component, CS42L51_DAC_CTL, reg); + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_component_device_cs42l51 = { + .probe = cs42l51_component_probe, + .controls = cs42l51_snd_controls, + .num_controls = ARRAY_SIZE(cs42l51_snd_controls), + .dapm_widgets = cs42l51_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets), + .dapm_routes = cs42l51_routes, + .num_dapm_routes = ARRAY_SIZE(cs42l51_routes), + .of_xlate_dai_id = cs42l51_of_xlate_dai_id, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L51_POWER_CTL1: + case CS42L51_MIC_POWER_CTL: + case CS42L51_INTF_CTL: + case CS42L51_MIC_CTL: + case CS42L51_ADC_CTL: + case CS42L51_ADC_INPUT: + case CS42L51_DAC_OUT_CTL: + case CS42L51_DAC_CTL: + case CS42L51_ALC_PGA_CTL: + case CS42L51_ALC_PGB_CTL: + case CS42L51_ADCA_ATT: + case CS42L51_ADCB_ATT: + case CS42L51_ADCA_VOL: + case CS42L51_ADCB_VOL: + case CS42L51_PCMA_VOL: + case CS42L51_PCMB_VOL: + case CS42L51_BEEP_FREQ: + case CS42L51_BEEP_VOL: + case CS42L51_BEEP_CONF: + case CS42L51_TONE_CTL: + case CS42L51_AOUTA_VOL: + case CS42L51_AOUTB_VOL: + case CS42L51_PCM_MIXER: + case CS42L51_LIMIT_THRES_DIS: + case CS42L51_LIMIT_REL: + case CS42L51_LIMIT_ATT: + case CS42L51_ALC_EN: + case CS42L51_ALC_REL: + case CS42L51_ALC_THRES: + case CS42L51_NOISE_CONF: + case CS42L51_CHARGE_FREQ: + return true; + default: + return false; + } +} + +static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L51_STATUS: + return true; + default: + return false; + } +} + +static bool cs42l51_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L51_CHIP_REV_ID: + case CS42L51_POWER_CTL1: + case CS42L51_MIC_POWER_CTL: + case CS42L51_INTF_CTL: + case CS42L51_MIC_CTL: + case CS42L51_ADC_CTL: + case CS42L51_ADC_INPUT: + case CS42L51_DAC_OUT_CTL: + case CS42L51_DAC_CTL: + case CS42L51_ALC_PGA_CTL: + case CS42L51_ALC_PGB_CTL: + case CS42L51_ADCA_ATT: + case CS42L51_ADCB_ATT: + case CS42L51_ADCA_VOL: + case CS42L51_ADCB_VOL: + case CS42L51_PCMA_VOL: + case CS42L51_PCMB_VOL: + case CS42L51_BEEP_FREQ: + case CS42L51_BEEP_VOL: + case CS42L51_BEEP_CONF: + case CS42L51_TONE_CTL: + case CS42L51_AOUTA_VOL: + case CS42L51_AOUTB_VOL: + case CS42L51_PCM_MIXER: + case CS42L51_LIMIT_THRES_DIS: + case CS42L51_LIMIT_REL: + case CS42L51_LIMIT_ATT: + case CS42L51_ALC_EN: + case CS42L51_ALC_REL: + case CS42L51_ALC_THRES: + case CS42L51_NOISE_CONF: + case CS42L51_STATUS: + case CS42L51_CHARGE_FREQ: + return true; + default: + return false; + } +} + +const struct regmap_config cs42l51_regmap = { + .reg_bits = 8, + .reg_stride = 1, + .val_bits = 8, + .use_single_write = true, + .readable_reg = cs42l51_readable_reg, + .volatile_reg = cs42l51_volatile_reg, + .writeable_reg = cs42l51_writeable_reg, + .max_register = CS42L51_CHARGE_FREQ, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_GPL(cs42l51_regmap); + +int cs42l51_probe(struct device *dev, struct regmap *regmap) +{ + struct cs42l51_private *cs42l51; + unsigned int val; + int ret, i; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + cs42l51 = devm_kzalloc(dev, sizeof(struct cs42l51_private), + GFP_KERNEL); + if (!cs42l51) + return -ENOMEM; + + dev_set_drvdata(dev, cs42l51); + cs42l51->regmap = regmap; + + cs42l51->mclk_handle = devm_clk_get_optional(dev, "MCLK"); + if (IS_ERR(cs42l51->mclk_handle)) + return PTR_ERR(cs42l51->mclk_handle); + + for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++) + cs42l51->supplies[i].supply = cs42l51_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs42l51->reset_gpio)) + return PTR_ERR(cs42l51->reset_gpio); + + if (cs42l51->reset_gpio) { + dev_dbg(dev, "Release reset gpio\n"); + gpiod_set_value_cansleep(cs42l51->reset_gpio, 0); + mdelay(2); + } + + /* Verify that we have a CS42L51 */ + ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val); + if (ret < 0) { + dev_err(dev, "failed to read I2C\n"); + goto error; + } + + if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) && + (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) { + dev_err(dev, "Invalid chip id: %x\n", val); + ret = -ENODEV; + goto error; + } + dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n", + val & CS42L51_CHIP_REV_MASK); + + ret = devm_snd_soc_register_component(dev, + &soc_component_device_cs42l51, &cs42l51_dai, 1); + if (ret < 0) + goto error; + + return 0; + +error: + regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); + return ret; +} +EXPORT_SYMBOL_GPL(cs42l51_probe); + +void cs42l51_remove(struct device *dev) +{ + struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); + int ret; + + gpiod_set_value_cansleep(cs42l51->reset_gpio, 1); + + ret = regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), + cs42l51->supplies); + if (ret) + dev_warn(dev, "Failed to disable all regulators (%pe)\n", + ERR_PTR(ret)); + +} +EXPORT_SYMBOL_GPL(cs42l51_remove); + +int __maybe_unused cs42l51_suspend(struct device *dev) +{ + struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); + + regcache_cache_only(cs42l51->regmap, true); + regcache_mark_dirty(cs42l51->regmap); + + return 0; +} +EXPORT_SYMBOL_GPL(cs42l51_suspend); + +int __maybe_unused cs42l51_resume(struct device *dev) +{ + struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); + + regcache_cache_only(cs42l51->regmap, false); + + return regcache_sync(cs42l51->regmap); +} +EXPORT_SYMBOL_GPL(cs42l51_resume); + +MODULE_AUTHOR("Arnaud Patard "); +MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h new file mode 100644 index 0000000000..125703ede1 --- /dev/null +++ b/sound/soc/codecs/cs42l51.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * cs42l51.h + * + * ASoC Driver for Cirrus Logic CS42L51 codecs + * + * Copyright (c) 2010 Arnaud Patard + */ +#ifndef _CS42L51_H +#define _CS42L51_H + +struct device; + +extern const struct regmap_config cs42l51_regmap; +int cs42l51_probe(struct device *dev, struct regmap *regmap); +void cs42l51_remove(struct device *dev); +int __maybe_unused cs42l51_suspend(struct device *dev); +int __maybe_unused cs42l51_resume(struct device *dev); + +#define CS42L51_CHIP_ID 0x1B +#define CS42L51_CHIP_REV_A 0x00 +#define CS42L51_CHIP_REV_B 0x01 +#define CS42L51_CHIP_REV_MASK 0x07 + +#define CS42L51_CHIP_REV_ID 0x01 +#define CS42L51_MK_CHIP_REV(a, b) ((a)<<3|(b)) + +#define CS42L51_POWER_CTL1 0x02 +#define CS42L51_POWER_CTL1_PDN_DACB (1<<6) +#define CS42L51_POWER_CTL1_PDN_DACA (1<<5) +#define CS42L51_POWER_CTL1_PDN_PGAB (1<<4) +#define CS42L51_POWER_CTL1_PDN_PGAA (1<<3) +#define CS42L51_POWER_CTL1_PDN_ADCB (1<<2) +#define CS42L51_POWER_CTL1_PDN_ADCA (1<<1) +#define CS42L51_POWER_CTL1_PDN (1<<0) + +#define CS42L51_MIC_POWER_CTL 0x03 +#define CS42L51_MIC_POWER_CTL_AUTO (1<<7) +#define CS42L51_MIC_POWER_CTL_SPEED(x) (((x)&3)<<5) +#define CS42L51_QSM_MODE 3 +#define CS42L51_HSM_MODE 2 +#define CS42L51_SSM_MODE 1 +#define CS42L51_DSM_MODE 0 +#define CS42L51_MIC_POWER_CTL_3ST_SP (1<<4) +#define CS42L51_MIC_POWER_CTL_PDN_MICB (1<<3) +#define CS42L51_MIC_POWER_CTL_PDN_MICA (1<<2) +#define CS42L51_MIC_POWER_CTL_PDN_BIAS (1<<1) +#define CS42L51_MIC_POWER_CTL_MCLK_DIV2 (1<<0) + +#define CS42L51_INTF_CTL 0x04 +#define CS42L51_INTF_CTL_LOOPBACK (1<<7) +#define CS42L51_INTF_CTL_MASTER (1<<6) +#define CS42L51_INTF_CTL_DAC_FORMAT(x) (((x)&7)<<3) +#define CS42L51_DAC_DIF_LJ24 0x00 +#define CS42L51_DAC_DIF_I2S 0x01 +#define CS42L51_DAC_DIF_RJ24 0x02 +#define CS42L51_DAC_DIF_RJ20 0x03 +#define CS42L51_DAC_DIF_RJ18 0x04 +#define CS42L51_DAC_DIF_RJ16 0x05 +#define CS42L51_INTF_CTL_ADC_I2S (1<<2) +#define CS42L51_INTF_CTL_DIGMIX (1<<1) +#define CS42L51_INTF_CTL_MICMIX (1<<0) + +#define CS42L51_MIC_CTL 0x05 +#define CS42L51_MIC_CTL_ADC_SNGVOL (1<<7) +#define CS42L51_MIC_CTL_ADCD_DBOOST (1<<6) +#define CS42L51_MIC_CTL_ADCA_DBOOST (1<<5) +#define CS42L51_MIC_CTL_MICBIAS_SEL (1<<4) +#define CS42L51_MIC_CTL_MICBIAS_LVL(x) (((x)&3)<<2) +#define CS42L51_MIC_CTL_MICB_BOOST (1<<1) +#define CS42L51_MIC_CTL_MICA_BOOST (1<<0) + +#define CS42L51_ADC_CTL 0x06 +#define CS42L51_ADC_CTL_ADCB_HPFEN (1<<7) +#define CS42L51_ADC_CTL_ADCB_HPFRZ (1<<6) +#define CS42L51_ADC_CTL_ADCA_HPFEN (1<<5) +#define CS42L51_ADC_CTL_ADCA_HPFRZ (1<<4) +#define CS42L51_ADC_CTL_SOFTB (1<<3) +#define CS42L51_ADC_CTL_ZCROSSB (1<<2) +#define CS42L51_ADC_CTL_SOFTA (1<<1) +#define CS42L51_ADC_CTL_ZCROSSA (1<<0) + +#define CS42L51_ADC_INPUT 0x07 +#define CS42L51_ADC_INPUT_AINB_MUX(x) (((x)&3)<<6) +#define CS42L51_ADC_INPUT_AINA_MUX(x) (((x)&3)<<4) +#define CS42L51_ADC_INPUT_INV_ADCB (1<<3) +#define CS42L51_ADC_INPUT_INV_ADCA (1<<2) +#define CS42L51_ADC_INPUT_ADCB_MUTE (1<<1) +#define CS42L51_ADC_INPUT_ADCA_MUTE (1<<0) + +#define CS42L51_DAC_OUT_CTL 0x08 +#define CS42L51_DAC_OUT_CTL_HP_GAIN(x) (((x)&7)<<5) +#define CS42L51_DAC_OUT_CTL_DAC_SNGVOL (1<<4) +#define CS42L51_DAC_OUT_CTL_INV_PCMB (1<<3) +#define CS42L51_DAC_OUT_CTL_INV_PCMA (1<<2) +#define CS42L51_DAC_OUT_CTL_DACB_MUTE (1<<1) +#define CS42L51_DAC_OUT_CTL_DACA_MUTE (1<<0) + +#define CS42L51_DAC_CTL 0x09 +#define CS42L51_DAC_CTL_DATA_SEL(x) (((x)&3)<<6) +#define CS42L51_DAC_CTL_FREEZE (1<<5) +#define CS42L51_DAC_CTL_DEEMPH (1<<3) +#define CS42L51_DAC_CTL_AMUTE (1<<2) +#define CS42L51_DAC_CTL_DACSZ(x) (((x)&3)<<0) + +#define CS42L51_ALC_PGA_CTL 0x0A +#define CS42L51_ALC_PGB_CTL 0x0B +#define CS42L51_ALC_PGX_ALCX_SRDIS (1<<7) +#define CS42L51_ALC_PGX_ALCX_ZCDIS (1<<6) +#define CS42L51_ALC_PGX_PGX_VOL(x) (((x)&0x1f)<<0) + +#define CS42L51_ADCA_ATT 0x0C +#define CS42L51_ADCB_ATT 0x0D + +#define CS42L51_ADCA_VOL 0x0E +#define CS42L51_ADCB_VOL 0x0F +#define CS42L51_PCMA_VOL 0x10 +#define CS42L51_PCMB_VOL 0x11 +#define CS42L51_MIX_MUTE_ADCMIX (1<<7) +#define CS42L51_MIX_VOLUME(x) (((x)&0x7f)<<0) + +#define CS42L51_BEEP_FREQ 0x12 +#define CS42L51_BEEP_VOL 0x13 +#define CS42L51_BEEP_CONF 0x14 + +#define CS42L51_TONE_CTL 0x15 +#define CS42L51_TONE_CTL_TREB(x) (((x)&0xf)<<4) +#define CS42L51_TONE_CTL_BASS(x) (((x)&0xf)<<0) + +#define CS42L51_AOUTA_VOL 0x16 +#define CS42L51_AOUTB_VOL 0x17 +#define CS42L51_PCM_MIXER 0x18 +#define CS42L51_LIMIT_THRES_DIS 0x19 +#define CS42L51_LIMIT_REL 0x1A +#define CS42L51_LIMIT_ATT 0x1B +#define CS42L51_ALC_EN 0x1C +#define CS42L51_ALC_REL 0x1D +#define CS42L51_ALC_THRES 0x1E +#define CS42L51_NOISE_CONF 0x1F + +#define CS42L51_STATUS 0x20 +#define CS42L51_STATUS_SP_CLKERR (1<<6) +#define CS42L51_STATUS_SPEA_OVFL (1<<5) +#define CS42L51_STATUS_SPEB_OVFL (1<<4) +#define CS42L51_STATUS_PCMA_OVFL (1<<3) +#define CS42L51_STATUS_PCMB_OVFL (1<<2) +#define CS42L51_STATUS_ADCA_OVFL (1<<1) +#define CS42L51_STATUS_ADCB_OVFL (1<<0) + +#define CS42L51_CHARGE_FREQ 0x21 + +#define CS42L51_FIRSTREG 0x01 +/* + * Hack: with register 0x21, it makes 33 registers. Looks like someone in the + * i2c layer doesn't like i2c smbus block read of 33 regs. Workaround by using + * 32 regs + */ +#define CS42L51_LASTREG 0x20 +#define CS42L51_NUMREGS (CS42L51_LASTREG - CS42L51_FIRSTREG + 1) + +#endif diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c new file mode 100644 index 0000000000..4fc8a6ae8d --- /dev/null +++ b/sound/soc/codecs/cs42l52.c @@ -0,0 +1,1237 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l52.c -- CS42L52 ALSA SoC audio driver + * + * Copyright 2012 CirrusLogic, Inc. + * + * Author: Georgi Vlaev + * Author: Brian Austin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs42l52.h" + +struct sp_config { + u8 spc, format, spfs; + u32 srate; +}; + +struct cs42l52_private { + struct regmap *regmap; + struct snd_soc_component *component; + struct device *dev; + struct sp_config config; + struct cs42l52_platform_data pdata; + u32 sysclk; + u8 mclksel; + u32 mclk; + u8 flags; + struct input_dev *beep; + struct work_struct beep_work; + int beep_rate; +}; + +static const struct reg_default cs42l52_reg_defaults[] = { + { CS42L52_PWRCTL1, 0x9F }, /* r02 PWRCTL 1 */ + { CS42L52_PWRCTL2, 0x07 }, /* r03 PWRCTL 2 */ + { CS42L52_PWRCTL3, 0xFF }, /* r04 PWRCTL 3 */ + { CS42L52_CLK_CTL, 0xA0 }, /* r05 Clocking Ctl */ + { CS42L52_IFACE_CTL1, 0x00 }, /* r06 Interface Ctl 1 */ + { CS42L52_ADC_PGA_A, 0x80 }, /* r08 Input A Select */ + { CS42L52_ADC_PGA_B, 0x80 }, /* r09 Input B Select */ + { CS42L52_ANALOG_HPF_CTL, 0xA5 }, /* r0A Analog HPF Ctl */ + { CS42L52_ADC_HPF_FREQ, 0x00 }, /* r0B ADC HPF Corner Freq */ + { CS42L52_ADC_MISC_CTL, 0x00 }, /* r0C Misc. ADC Ctl */ + { CS42L52_PB_CTL1, 0x60 }, /* r0D Playback Ctl 1 */ + { CS42L52_MISC_CTL, 0x02 }, /* r0E Misc. Ctl */ + { CS42L52_PB_CTL2, 0x00 }, /* r0F Playback Ctl 2 */ + { CS42L52_MICA_CTL, 0x00 }, /* r10 MICA Amp Ctl */ + { CS42L52_MICB_CTL, 0x00 }, /* r11 MICB Amp Ctl */ + { CS42L52_PGAA_CTL, 0x00 }, /* r12 PGAA Vol, Misc. */ + { CS42L52_PGAB_CTL, 0x00 }, /* r13 PGAB Vol, Misc. */ + { CS42L52_PASSTHRUA_VOL, 0x00 }, /* r14 Bypass A Vol */ + { CS42L52_PASSTHRUB_VOL, 0x00 }, /* r15 Bypass B Vol */ + { CS42L52_ADCA_VOL, 0x00 }, /* r16 ADCA Volume */ + { CS42L52_ADCB_VOL, 0x00 }, /* r17 ADCB Volume */ + { CS42L52_ADCA_MIXER_VOL, 0x80 }, /* r18 ADCA Mixer Volume */ + { CS42L52_ADCB_MIXER_VOL, 0x80 }, /* r19 ADCB Mixer Volume */ + { CS42L52_PCMA_MIXER_VOL, 0x00 }, /* r1A PCMA Mixer Volume */ + { CS42L52_PCMB_MIXER_VOL, 0x00 }, /* r1B PCMB Mixer Volume */ + { CS42L52_BEEP_FREQ, 0x00 }, /* r1C Beep Freq on Time */ + { CS42L52_BEEP_VOL, 0x00 }, /* r1D Beep Volume off Time */ + { CS42L52_BEEP_TONE_CTL, 0x00 }, /* r1E Beep Tone Cfg. */ + { CS42L52_TONE_CTL, 0x00 }, /* r1F Tone Ctl */ + { CS42L52_MASTERA_VOL, 0x00 }, /* r20 Master A Volume */ + { CS42L52_MASTERB_VOL, 0x00 }, /* r21 Master B Volume */ + { CS42L52_HPA_VOL, 0x00 }, /* r22 Headphone A Volume */ + { CS42L52_HPB_VOL, 0x00 }, /* r23 Headphone B Volume */ + { CS42L52_SPKA_VOL, 0x00 }, /* r24 Speaker A Volume */ + { CS42L52_SPKB_VOL, 0x00 }, /* r25 Speaker B Volume */ + { CS42L52_ADC_PCM_MIXER, 0x00 }, /* r26 Channel Mixer and Swap */ + { CS42L52_LIMITER_CTL1, 0x00 }, /* r27 Limit Ctl 1 Thresholds */ + { CS42L52_LIMITER_CTL2, 0x7F }, /* r28 Limit Ctl 2 Release Rate */ + { CS42L52_LIMITER_AT_RATE, 0xC0 }, /* r29 Limiter Attack Rate */ + { CS42L52_ALC_CTL, 0x00 }, /* r2A ALC Ctl 1 Attack Rate */ + { CS42L52_ALC_RATE, 0x3F }, /* r2B ALC Release Rate */ + { CS42L52_ALC_THRESHOLD, 0x3f }, /* r2C ALC Thresholds */ + { CS42L52_NOISE_GATE_CTL, 0x00 }, /* r2D Noise Gate Ctl */ + { CS42L52_CLK_STATUS, 0x00 }, /* r2E Overflow and Clock Status */ + { CS42L52_BATT_COMPEN, 0x00 }, /* r2F battery Compensation */ + { CS42L52_BATT_LEVEL, 0x00 }, /* r30 VP Battery Level */ + { CS42L52_SPK_STATUS, 0x00 }, /* r31 Speaker Status */ + { CS42L52_TEM_CTL, 0x3B }, /* r32 Temp Ctl */ + { CS42L52_THE_FOLDBACK, 0x00 }, /* r33 Foldback */ +}; + +static bool cs42l52_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L52_CHIP ... CS42L52_CHARGE_PUMP: + return true; + default: + return false; + } +} + +static bool cs42l52_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L52_IFACE_CTL2: + case CS42L52_CLK_STATUS: + case CS42L52_BATT_LEVEL: + case CS42L52_SPK_STATUS: + case CS42L52_CHARGE_PUMP: + return true; + default: + return false; + } +} + +static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0); + +static DECLARE_TLV_DB_SCALE(hpd_tlv, -9600, 50, 1); + +static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0); + +static DECLARE_TLV_DB_SCALE(mic_tlv, 1600, 100, 0); + +static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0); + +static DECLARE_TLV_DB_SCALE(pass_tlv, -6000, 50, 0); + +static DECLARE_TLV_DB_SCALE(mix_tlv, -5150, 50, 0); + +static DECLARE_TLV_DB_SCALE(beep_tlv, -56, 200, 0); + +static const DECLARE_TLV_DB_RANGE(limiter_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), + 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0) +); + +static const char * const cs42l52_adca_text[] = { + "Input1A", "Input2A", "Input3A", "Input4A", "PGA Input Left"}; + +static const char * const cs42l52_adcb_text[] = { + "Input1B", "Input2B", "Input3B", "Input4B", "PGA Input Right"}; + +static SOC_ENUM_SINGLE_DECL(adca_enum, + CS42L52_ADC_PGA_A, 5, cs42l52_adca_text); + +static SOC_ENUM_SINGLE_DECL(adcb_enum, + CS42L52_ADC_PGA_B, 5, cs42l52_adcb_text); + +static const struct snd_kcontrol_new adca_mux = + SOC_DAPM_ENUM("Left ADC Input Capture Mux", adca_enum); + +static const struct snd_kcontrol_new adcb_mux = + SOC_DAPM_ENUM("Right ADC Input Capture Mux", adcb_enum); + +static const char * const mic_bias_level_text[] = { + "0.5 +VA", "0.6 +VA", "0.7 +VA", + "0.8 +VA", "0.83 +VA", "0.91 +VA" +}; + +static SOC_ENUM_SINGLE_DECL(mic_bias_level_enum, + CS42L52_IFACE_CTL2, 0, mic_bias_level_text); + +static const char * const cs42l52_mic_text[] = { "MIC1", "MIC2" }; + +static SOC_ENUM_SINGLE_DECL(mica_enum, + CS42L52_MICA_CTL, 5, cs42l52_mic_text); + +static SOC_ENUM_SINGLE_DECL(micb_enum, + CS42L52_MICB_CTL, 5, cs42l52_mic_text); + +static const char * const digital_output_mux_text[] = {"ADC", "DSP"}; + +static SOC_ENUM_SINGLE_DECL(digital_output_mux_enum, + CS42L52_ADC_MISC_CTL, 6, + digital_output_mux_text); + +static const struct snd_kcontrol_new digital_output_mux = + SOC_DAPM_ENUM("Digital Output Mux", digital_output_mux_enum); + +static const char * const hp_gain_num_text[] = { + "0.3959", "0.4571", "0.5111", "0.6047", + "0.7099", "0.8399", "1.000", "1.1430" +}; + +static SOC_ENUM_SINGLE_DECL(hp_gain_enum, + CS42L52_PB_CTL1, 5, + hp_gain_num_text); + +static const char * const beep_pitch_text[] = { + "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5", + "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7" +}; + +static SOC_ENUM_SINGLE_DECL(beep_pitch_enum, + CS42L52_BEEP_FREQ, 4, + beep_pitch_text); + +static const char * const beep_ontime_text[] = { + "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s", + "1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s", + "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s" +}; + +static SOC_ENUM_SINGLE_DECL(beep_ontime_enum, + CS42L52_BEEP_FREQ, 0, + beep_ontime_text); + +static const char * const beep_offtime_text[] = { + "1.23 s", "2.58 s", "3.90 s", "5.20 s", + "6.60 s", "8.05 s", "9.35 s", "10.80 s" +}; + +static SOC_ENUM_SINGLE_DECL(beep_offtime_enum, + CS42L52_BEEP_VOL, 5, + beep_offtime_text); + +static const char * const beep_config_text[] = { + "Off", "Single", "Multiple", "Continuous" +}; + +static SOC_ENUM_SINGLE_DECL(beep_config_enum, + CS42L52_BEEP_TONE_CTL, 6, + beep_config_text); + +static const char * const beep_bass_text[] = { + "50 Hz", "100 Hz", "200 Hz", "250 Hz" +}; + +static SOC_ENUM_SINGLE_DECL(beep_bass_enum, + CS42L52_BEEP_TONE_CTL, 1, + beep_bass_text); + +static const char * const beep_treble_text[] = { + "5 kHz", "7 kHz", "10 kHz", " 15 kHz" +}; + +static SOC_ENUM_SINGLE_DECL(beep_treble_enum, + CS42L52_BEEP_TONE_CTL, 3, + beep_treble_text); + +static const char * const ng_threshold_text[] = { + "-34dB", "-37dB", "-40dB", "-43dB", + "-46dB", "-52dB", "-58dB", "-64dB" +}; + +static SOC_ENUM_SINGLE_DECL(ng_threshold_enum, + CS42L52_NOISE_GATE_CTL, 2, + ng_threshold_text); + +static const char * const cs42l52_ng_delay_text[] = { + "50ms", "100ms", "150ms", "200ms"}; + +static SOC_ENUM_SINGLE_DECL(ng_delay_enum, + CS42L52_NOISE_GATE_CTL, 0, + cs42l52_ng_delay_text); + +static const char * const cs42l52_ng_type_text[] = { + "Apply Specific", "Apply All" +}; + +static SOC_ENUM_SINGLE_DECL(ng_type_enum, + CS42L52_NOISE_GATE_CTL, 6, + cs42l52_ng_type_text); + +static const char * const left_swap_text[] = { + "Left", "LR 2", "Right"}; + +static const char * const right_swap_text[] = { + "Right", "LR 2", "Left"}; + +static const unsigned int swap_values[] = { 0, 1, 3 }; + +static const struct soc_enum adca_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 2, 3, + ARRAY_SIZE(left_swap_text), + left_swap_text, + swap_values); + +static const struct snd_kcontrol_new adca_mixer = + SOC_DAPM_ENUM("Route", adca_swap_enum); + +static const struct soc_enum pcma_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 6, 3, + ARRAY_SIZE(left_swap_text), + left_swap_text, + swap_values); + +static const struct snd_kcontrol_new pcma_mixer = + SOC_DAPM_ENUM("Route", pcma_swap_enum); + +static const struct soc_enum adcb_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 0, 3, + ARRAY_SIZE(right_swap_text), + right_swap_text, + swap_values); + +static const struct snd_kcontrol_new adcb_mixer = + SOC_DAPM_ENUM("Route", adcb_swap_enum); + +static const struct soc_enum pcmb_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L52_ADC_PCM_MIXER, 4, 3, + ARRAY_SIZE(right_swap_text), + right_swap_text, + swap_values); + +static const struct snd_kcontrol_new pcmb_mixer = + SOC_DAPM_ENUM("Route", pcmb_swap_enum); + + +static const struct snd_kcontrol_new passthrul_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 6, 1, 0); + +static const struct snd_kcontrol_new passthrur_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_MISC_CTL, 7, 1, 0); + +static const struct snd_kcontrol_new spkl_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 0, 1, 1); + +static const struct snd_kcontrol_new spkr_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 2, 1, 1); + +static const struct snd_kcontrol_new hpl_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 4, 1, 1); + +static const struct snd_kcontrol_new hpr_ctl = + SOC_DAPM_SINGLE("Switch", CS42L52_PWRCTL3, 6, 1, 1); + +static const struct snd_kcontrol_new cs42l52_snd_controls[] = { + + SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L52_MASTERA_VOL, + CS42L52_MASTERB_VOL, 0, 0x34, 0xE4, hl_tlv), + + SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L52_HPA_VOL, + CS42L52_HPB_VOL, 0, 0x34, 0xC0, hpd_tlv), + + SOC_ENUM("Headphone Analog Gain", hp_gain_enum), + + SOC_DOUBLE_R_SX_TLV("Speaker Volume", CS42L52_SPKA_VOL, + CS42L52_SPKB_VOL, 0, 0x40, 0xC0, hl_tlv), + + SOC_DOUBLE_R_SX_TLV("Bypass Volume", CS42L52_PASSTHRUA_VOL, + CS42L52_PASSTHRUB_VOL, 0, 0x88, 0x90, pass_tlv), + + SOC_DOUBLE("Bypass Mute", CS42L52_MISC_CTL, 4, 5, 1, 0), + + SOC_DOUBLE_R_TLV("MIC Gain Volume", CS42L52_MICA_CTL, + CS42L52_MICB_CTL, 0, 0x10, 0, mic_tlv), + + SOC_ENUM("MIC Bias Level", mic_bias_level_enum), + + SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L52_ADCA_VOL, + CS42L52_ADCB_VOL, 0, 0xA0, 0x78, ipd_tlv), + SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", + CS42L52_ADCA_MIXER_VOL, CS42L52_ADCB_MIXER_VOL, + 0, 0x19, 0x7F, mix_tlv), + + SOC_DOUBLE("ADC Switch", CS42L52_ADC_MISC_CTL, 0, 1, 1, 0), + + SOC_DOUBLE_R("ADC Mixer Switch", CS42L52_ADCA_MIXER_VOL, + CS42L52_ADCB_MIXER_VOL, 7, 1, 1), + + SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L52_PGAA_CTL, + CS42L52_PGAB_CTL, 0, 0x28, 0x24, pga_tlv), + + SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume", + CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL, + 0, 0x19, 0x7f, mix_tlv), + SOC_DOUBLE_R("PCM Mixer Switch", + CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL, 7, 1, 1), + + SOC_ENUM("Beep Config", beep_config_enum), + SOC_ENUM("Beep Pitch", beep_pitch_enum), + SOC_ENUM("Beep on Time", beep_ontime_enum), + SOC_ENUM("Beep off Time", beep_offtime_enum), + SOC_SINGLE_SX_TLV("Beep Volume", CS42L52_BEEP_VOL, + 0, 0x07, 0x1f, beep_tlv), + SOC_SINGLE("Beep Mixer Switch", CS42L52_BEEP_TONE_CTL, 5, 1, 1), + SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum), + SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum), + + SOC_SINGLE("Tone Control Switch", CS42L52_BEEP_TONE_CTL, 0, 1, 1), + SOC_SINGLE_TLV("Treble Gain Volume", + CS42L52_TONE_CTL, 4, 15, 1, hl_tlv), + SOC_SINGLE_TLV("Bass Gain Volume", + CS42L52_TONE_CTL, 0, 15, 1, hl_tlv), + + /* Limiter */ + SOC_SINGLE_TLV("Limiter Max Threshold Volume", + CS42L52_LIMITER_CTL1, 5, 7, 0, limiter_tlv), + SOC_SINGLE_TLV("Limiter Cushion Threshold Volume", + CS42L52_LIMITER_CTL1, 2, 7, 0, limiter_tlv), + SOC_SINGLE_TLV("Limiter Release Rate Volume", + CS42L52_LIMITER_CTL2, 0, 63, 0, limiter_tlv), + SOC_SINGLE_TLV("Limiter Attack Rate Volume", + CS42L52_LIMITER_AT_RATE, 0, 63, 0, limiter_tlv), + + SOC_SINGLE("Limiter SR Switch", CS42L52_LIMITER_CTL1, 1, 1, 0), + SOC_SINGLE("Limiter ZC Switch", CS42L52_LIMITER_CTL1, 0, 1, 0), + SOC_SINGLE("Limiter Switch", CS42L52_LIMITER_CTL2, 7, 1, 0), + + /* ALC */ + SOC_SINGLE_TLV("ALC Attack Rate Volume", CS42L52_ALC_CTL, + 0, 63, 0, limiter_tlv), + SOC_SINGLE_TLV("ALC Release Rate Volume", CS42L52_ALC_RATE, + 0, 63, 0, limiter_tlv), + SOC_SINGLE_TLV("ALC Max Threshold Volume", CS42L52_ALC_THRESHOLD, + 5, 7, 0, limiter_tlv), + SOC_SINGLE_TLV("ALC Min Threshold Volume", CS42L52_ALC_THRESHOLD, + 2, 7, 0, limiter_tlv), + + SOC_DOUBLE_R("ALC SR Capture Switch", CS42L52_PGAA_CTL, + CS42L52_PGAB_CTL, 7, 1, 1), + SOC_DOUBLE_R("ALC ZC Capture Switch", CS42L52_PGAA_CTL, + CS42L52_PGAB_CTL, 6, 1, 1), + SOC_DOUBLE("ALC Capture Switch", CS42L52_ALC_CTL, 6, 7, 1, 0), + + /* Noise gate */ + SOC_ENUM("NG Type Switch", ng_type_enum), + SOC_SINGLE("NG Enable Switch", CS42L52_NOISE_GATE_CTL, 6, 1, 0), + SOC_SINGLE("NG Boost Switch", CS42L52_NOISE_GATE_CTL, 5, 1, 1), + SOC_ENUM("NG Threshold", ng_threshold_enum), + SOC_ENUM("NG Delay", ng_delay_enum), + + SOC_DOUBLE("HPF Switch", CS42L52_ANALOG_HPF_CTL, 5, 7, 1, 0), + + SOC_DOUBLE("Analog SR Switch", CS42L52_ANALOG_HPF_CTL, 1, 3, 1, 1), + SOC_DOUBLE("Analog ZC Switch", CS42L52_ANALOG_HPF_CTL, 0, 2, 1, 1), + SOC_SINGLE("Digital SR Switch", CS42L52_MISC_CTL, 1, 1, 0), + SOC_SINGLE("Digital ZC Switch", CS42L52_MISC_CTL, 0, 1, 0), + SOC_SINGLE("Deemphasis Switch", CS42L52_MISC_CTL, 2, 1, 0), + + SOC_SINGLE("Batt Compensation Switch", CS42L52_BATT_COMPEN, 7, 1, 0), + SOC_SINGLE("Batt VP Monitor Switch", CS42L52_BATT_COMPEN, 6, 1, 0), + SOC_SINGLE("Batt VP ref", CS42L52_BATT_COMPEN, 0, 0x0f, 0), + + SOC_SINGLE("PGA AIN1L Switch", CS42L52_ADC_PGA_A, 0, 1, 0), + SOC_SINGLE("PGA AIN1R Switch", CS42L52_ADC_PGA_B, 0, 1, 0), + SOC_SINGLE("PGA AIN2L Switch", CS42L52_ADC_PGA_A, 1, 1, 0), + SOC_SINGLE("PGA AIN2R Switch", CS42L52_ADC_PGA_B, 1, 1, 0), + + SOC_SINGLE("PGA AIN3L Switch", CS42L52_ADC_PGA_A, 2, 1, 0), + SOC_SINGLE("PGA AIN3R Switch", CS42L52_ADC_PGA_B, 2, 1, 0), + + SOC_SINGLE("PGA AIN4L Switch", CS42L52_ADC_PGA_A, 3, 1, 0), + SOC_SINGLE("PGA AIN4R Switch", CS42L52_ADC_PGA_B, 3, 1, 0), + + SOC_SINGLE("PGA MICA Switch", CS42L52_ADC_PGA_A, 4, 1, 0), + SOC_SINGLE("PGA MICB Switch", CS42L52_ADC_PGA_B, 4, 1, 0), + +}; + +static const struct snd_kcontrol_new cs42l52_mica_controls[] = { + SOC_ENUM("MICA Select", mica_enum), +}; + +static const struct snd_kcontrol_new cs42l52_micb_controls[] = { + SOC_ENUM("MICB Select", micb_enum), +}; + +static int cs42l52_add_mic_controls(struct snd_soc_component *component) +{ + struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component); + struct cs42l52_platform_data *pdata = &cs42l52->pdata; + + if (!pdata->mica_diff_cfg) + snd_soc_add_component_controls(component, cs42l52_mica_controls, + ARRAY_SIZE(cs42l52_mica_controls)); + + if (!pdata->micb_diff_cfg) + snd_soc_add_component_controls(component, cs42l52_micb_controls, + ARRAY_SIZE(cs42l52_micb_controls)); + + return 0; +} + +static const struct snd_soc_dapm_widget cs42l52_dapm_widgets[] = { + + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN2R"), + SND_SOC_DAPM_INPUT("AIN3L"), + SND_SOC_DAPM_INPUT("AIN3R"), + SND_SOC_DAPM_INPUT("AIN4L"), + SND_SOC_DAPM_INPUT("AIN4R"), + SND_SOC_DAPM_INPUT("MICA"), + SND_SOC_DAPM_INPUT("MICB"), + SND_SOC_DAPM_SIGGEN("Beep"), + + SND_SOC_DAPM_AIF_OUT("AIFOUTL", NULL, 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIFOUTR", NULL, 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L52_PWRCTL1, 1, 1), + SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L52_PWRCTL1, 2, 1), + SND_SOC_DAPM_PGA("PGA Left", CS42L52_PWRCTL1, 3, 1, NULL, 0), + SND_SOC_DAPM_PGA("PGA Right", CS42L52_PWRCTL1, 4, 1, NULL, 0), + + SND_SOC_DAPM_MUX("ADC Left Mux", SND_SOC_NOPM, 0, 0, &adca_mux), + SND_SOC_DAPM_MUX("ADC Right Mux", SND_SOC_NOPM, 0, 0, &adcb_mux), + + SND_SOC_DAPM_MUX("ADC Left Swap", SND_SOC_NOPM, + 0, 0, &adca_mixer), + SND_SOC_DAPM_MUX("ADC Right Swap", SND_SOC_NOPM, + 0, 0, &adcb_mixer), + + SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, + 0, 0, &digital_output_mux), + + SND_SOC_DAPM_PGA("PGA MICA", CS42L52_PWRCTL2, 1, 1, NULL, 0), + SND_SOC_DAPM_PGA("PGA MICB", CS42L52_PWRCTL2, 2, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L52_PWRCTL2, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L52_PWRCTL1, 7, 1, NULL, 0), + + SND_SOC_DAPM_AIF_IN("AIFINL", NULL, 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFINR", NULL, 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DAC Left", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC Right", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SWITCH("Bypass Left", CS42L52_MISC_CTL, + 6, 0, &passthrul_ctl), + SND_SOC_DAPM_SWITCH("Bypass Right", CS42L52_MISC_CTL, + 7, 0, &passthrur_ctl), + + SND_SOC_DAPM_MUX("PCM Left Swap", SND_SOC_NOPM, + 0, 0, &pcma_mixer), + SND_SOC_DAPM_MUX("PCM Right Swap", SND_SOC_NOPM, + 0, 0, &pcmb_mixer), + + SND_SOC_DAPM_SWITCH("HP Left Amp", SND_SOC_NOPM, 0, 0, &hpl_ctl), + SND_SOC_DAPM_SWITCH("HP Right Amp", SND_SOC_NOPM, 0, 0, &hpr_ctl), + + SND_SOC_DAPM_SWITCH("SPK Left Amp", SND_SOC_NOPM, 0, 0, &spkl_ctl), + SND_SOC_DAPM_SWITCH("SPK Right Amp", SND_SOC_NOPM, 0, 0, &spkr_ctl), + + SND_SOC_DAPM_OUTPUT("HPOUTA"), + SND_SOC_DAPM_OUTPUT("HPOUTB"), + SND_SOC_DAPM_OUTPUT("SPKOUTA"), + SND_SOC_DAPM_OUTPUT("SPKOUTB"), + +}; + +static const struct snd_soc_dapm_route cs42l52_audio_map[] = { + + {"Capture", NULL, "AIFOUTL"}, + {"Capture", NULL, "AIFOUTL"}, + + {"AIFOUTL", NULL, "Output Mux"}, + {"AIFOUTR", NULL, "Output Mux"}, + + {"Output Mux", "ADC", "ADC Left"}, + {"Output Mux", "ADC", "ADC Right"}, + + {"ADC Left", NULL, "Charge Pump"}, + {"ADC Right", NULL, "Charge Pump"}, + + {"Charge Pump", NULL, "ADC Left Mux"}, + {"Charge Pump", NULL, "ADC Right Mux"}, + + {"ADC Left Mux", "Input1A", "AIN1L"}, + {"ADC Right Mux", "Input1B", "AIN1R"}, + {"ADC Left Mux", "Input2A", "AIN2L"}, + {"ADC Right Mux", "Input2B", "AIN2R"}, + {"ADC Left Mux", "Input3A", "AIN3L"}, + {"ADC Right Mux", "Input3B", "AIN3R"}, + {"ADC Left Mux", "Input4A", "AIN4L"}, + {"ADC Right Mux", "Input4B", "AIN4R"}, + {"ADC Left Mux", "PGA Input Left", "PGA Left"}, + {"ADC Right Mux", "PGA Input Right" , "PGA Right"}, + + {"PGA Left", "Switch", "AIN1L"}, + {"PGA Right", "Switch", "AIN1R"}, + {"PGA Left", "Switch", "AIN2L"}, + {"PGA Right", "Switch", "AIN2R"}, + {"PGA Left", "Switch", "AIN3L"}, + {"PGA Right", "Switch", "AIN3R"}, + {"PGA Left", "Switch", "AIN4L"}, + {"PGA Right", "Switch", "AIN4R"}, + + {"PGA Left", "Switch", "PGA MICA"}, + {"PGA MICA", NULL, "MICA"}, + + {"PGA Right", "Switch", "PGA MICB"}, + {"PGA MICB", NULL, "MICB"}, + + {"HPOUTA", NULL, "HP Left Amp"}, + {"HPOUTB", NULL, "HP Right Amp"}, + {"HP Left Amp", NULL, "Bypass Left"}, + {"HP Right Amp", NULL, "Bypass Right"}, + {"Bypass Left", "Switch", "PGA Left"}, + {"Bypass Right", "Switch", "PGA Right"}, + {"HP Left Amp", "Switch", "DAC Left"}, + {"HP Right Amp", "Switch", "DAC Right"}, + + {"SPKOUTA", NULL, "SPK Left Amp"}, + {"SPKOUTB", NULL, "SPK Right Amp"}, + + {"SPK Left Amp", NULL, "Beep"}, + {"SPK Right Amp", NULL, "Beep"}, + {"SPK Left Amp", "Switch", "Playback"}, + {"SPK Right Amp", "Switch", "Playback"}, + + {"DAC Left", NULL, "Beep"}, + {"DAC Right", NULL, "Beep"}, + {"DAC Left", NULL, "Playback"}, + {"DAC Right", NULL, "Playback"}, + + {"Output Mux", "DSP", "Playback"}, + {"Output Mux", "DSP", "Playback"}, + + {"AIFINL", NULL, "Playback"}, + {"AIFINR", NULL, "Playback"}, + +}; + +struct cs42l52_clk_para { + u32 mclk; + u32 rate; + u8 speed; + u8 group; + u8 videoclk; + u8 ratio; + u8 mclkdiv2; +}; + +static const struct cs42l52_clk_para clk_map_table[] = { + /*8k*/ + {12288000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {18432000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {12000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0}, + {24000000, 8000, CLK_QS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1}, + {27000000, 8000, CLK_QS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0}, + + /*11.025k*/ + {11289600, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {16934400, 11025, CLK_QS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + + /*16k*/ + {12288000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {18432000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {12000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0}, + {24000000, 16000, CLK_HS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1}, + {27000000, 16000, CLK_HS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 1}, + + /*22.05k*/ + {11289600, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {16934400, 22050, CLK_HS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + + /* 32k */ + {12288000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {18432000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_128, 0}, + {12000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 0}, + {24000000, 32000, CLK_SS_MODE, CLK_32K, CLK_NO_27M, CLK_R_125, 1}, + {27000000, 32000, CLK_SS_MODE, CLK_32K, CLK_27M_MCLK, CLK_R_125, 0}, + + /* 44.1k */ + {11289600, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {16934400, 44100, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + + /* 48k */ + {12288000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {18432000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {12000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0}, + {24000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1}, + {27000000, 48000, CLK_SS_MODE, CLK_NO_32K, CLK_27M_MCLK, CLK_R_125, 1}, + + /* 88.2k */ + {11289600, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {16934400, 88200, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + + /* 96k */ + {12288000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {18432000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_128, 0}, + {12000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 0}, + {24000000, 96000, CLK_DS_MODE, CLK_NO_32K, CLK_NO_27M, CLK_R_125, 1}, +}; + +static int cs42l52_get_clk(int mclk, int rate) +{ + int i, ret = -EINVAL; + u_int mclk1, mclk2 = 0; + + for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { + if (clk_map_table[i].rate == rate) { + mclk1 = clk_map_table[i].mclk; + if (abs(mclk - mclk1) < abs(mclk - mclk2)) { + mclk2 = mclk1; + ret = i; + } + } + } + return ret; +} + +static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component); + + if ((freq >= CS42L52_MIN_CLK) && (freq <= CS42L52_MAX_CLK)) { + cs42l52->sysclk = freq; + } else { + dev_err(component->dev, "Invalid freq parameter\n"); + return -EINVAL; + } + return 0; +} + +static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component); + u8 iface = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = CS42L52_IFACE_CTL1_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface = CS42L52_IFACE_CTL1_SLAVE; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= CS42L52_IFACE_CTL1_ADC_FMT_I2S | + CS42L52_IFACE_CTL1_DAC_FMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface |= CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J | + CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= CS42L52_IFACE_CTL1_DSP_MODE_EN; + break; + case SND_SOC_DAIFMT_DSP_B: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= CS42L52_IFACE_CTL1_INV_SCLK; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= CS42L52_IFACE_CTL1_INV_SCLK; + break; + case SND_SOC_DAIFMT_NB_IF: + break; + default: + return -EINVAL; + } + cs42l52->config.format = iface; + snd_soc_component_write(component, CS42L52_IFACE_CTL1, cs42l52->config.format); + + return 0; +} + +static int cs42l52_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + if (mute) + snd_soc_component_update_bits(component, CS42L52_PB_CTL1, + CS42L52_PB_CTL1_MUTE_MASK, + CS42L52_PB_CTL1_MUTE); + else + snd_soc_component_update_bits(component, CS42L52_PB_CTL1, + CS42L52_PB_CTL1_MUTE_MASK, + CS42L52_PB_CTL1_UNMUTE); + + return 0; +} + +static int cs42l52_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component); + u32 clk = 0; + int index; + + index = cs42l52_get_clk(cs42l52->sysclk, params_rate(params)); + if (index >= 0) { + cs42l52->sysclk = clk_map_table[index].mclk; + + clk |= (clk_map_table[index].speed << CLK_SPEED_SHIFT) | + (clk_map_table[index].group << CLK_32K_SR_SHIFT) | + (clk_map_table[index].videoclk << CLK_27M_MCLK_SHIFT) | + (clk_map_table[index].ratio << CLK_RATIO_SHIFT) | + clk_map_table[index].mclkdiv2; + + snd_soc_component_write(component, CS42L52_CLK_CTL, clk); + } else { + dev_err(component->dev, "can't get correct mclk\n"); + return -EINVAL; + } + + return 0; +} + +static int cs42l52_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, CS42L52_PWRCTL1, + CS42L52_PWRCTL1_PDN_CODEC, 0); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + regcache_cache_only(cs42l52->regmap, false); + regcache_sync(cs42l52->regmap); + } + snd_soc_component_write(component, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL); + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL); + regcache_cache_only(cs42l52->regmap, true); + break; + } + + return 0; +} + +#define CS42L52_RATES (SNDRV_PCM_RATE_8000_96000) + +#define CS42L52_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE) + +static const struct snd_soc_dai_ops cs42l52_ops = { + .hw_params = cs42l52_pcm_hw_params, + .mute_stream = cs42l52_mute, + .set_fmt = cs42l52_set_fmt, + .set_sysclk = cs42l52_set_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver cs42l52_dai = { + .name = "cs42l52", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L52_RATES, + .formats = CS42L52_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L52_RATES, + .formats = CS42L52_FORMATS, + }, + .ops = &cs42l52_ops, +}; + +static int beep_rates[] = { + 261, 522, 585, 667, 706, 774, 889, 1000, + 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182 +}; + +static void cs42l52_beep_work(struct work_struct *work) +{ + struct cs42l52_private *cs42l52 = + container_of(work, struct cs42l52_private, beep_work); + struct snd_soc_component *component = cs42l52->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int i; + int val = 0; + int best = 0; + + if (cs42l52->beep_rate) { + for (i = 0; i < ARRAY_SIZE(beep_rates); i++) { + if (abs(cs42l52->beep_rate - beep_rates[i]) < + abs(cs42l52->beep_rate - beep_rates[best])) + best = i; + } + + dev_dbg(component->dev, "Set beep rate %dHz for requested %dHz\n", + beep_rates[best], cs42l52->beep_rate); + + val = (best << CS42L52_BEEP_RATE_SHIFT); + + snd_soc_dapm_enable_pin(dapm, "Beep"); + } else { + dev_dbg(component->dev, "Disabling beep\n"); + snd_soc_dapm_disable_pin(dapm, "Beep"); + } + + snd_soc_component_update_bits(component, CS42L52_BEEP_FREQ, + CS42L52_BEEP_RATE_MASK, val); + + snd_soc_dapm_sync(dapm); +} + +/* For usability define a way of injecting beep events for the device - + * many systems will not have a keyboard. + */ +static int cs42l52_beep_event(struct input_dev *dev, unsigned int type, + unsigned int code, int hz) +{ + struct snd_soc_component *component = input_get_drvdata(dev); + struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "Beep event %x %x\n", code, hz); + + switch (code) { + case SND_BELL: + if (hz) + hz = 261; + break; + case SND_TONE: + break; + default: + return -1; + } + + /* Kick the beep from a workqueue */ + cs42l52->beep_rate = hz; + schedule_work(&cs42l52->beep_work); + return 0; +} + +static ssize_t beep_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs42l52_private *cs42l52 = dev_get_drvdata(dev); + long int time; + int ret; + + ret = kstrtol(buf, 10, &time); + if (ret != 0) + return ret; + + input_event(cs42l52->beep, EV_SND, SND_TONE, time); + + return count; +} + +static DEVICE_ATTR_WO(beep); + +static void cs42l52_init_beep(struct snd_soc_component *component) +{ + struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component); + int ret; + + cs42l52->beep = devm_input_allocate_device(component->dev); + if (!cs42l52->beep) { + dev_err(component->dev, "Failed to allocate beep device\n"); + return; + } + + INIT_WORK(&cs42l52->beep_work, cs42l52_beep_work); + cs42l52->beep_rate = 0; + + cs42l52->beep->name = "CS42L52 Beep Generator"; + cs42l52->beep->phys = dev_name(component->dev); + cs42l52->beep->id.bustype = BUS_I2C; + + cs42l52->beep->evbit[0] = BIT_MASK(EV_SND); + cs42l52->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + cs42l52->beep->event = cs42l52_beep_event; + cs42l52->beep->dev.parent = component->dev; + input_set_drvdata(cs42l52->beep, component); + + ret = input_register_device(cs42l52->beep); + if (ret != 0) { + cs42l52->beep = NULL; + dev_err(component->dev, "Failed to register beep device\n"); + } + + ret = device_create_file(component->dev, &dev_attr_beep); + if (ret != 0) { + dev_err(component->dev, "Failed to create keyclick file: %d\n", + ret); + } +} + +static void cs42l52_free_beep(struct snd_soc_component *component) +{ + struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component); + + device_remove_file(component->dev, &dev_attr_beep); + cancel_work_sync(&cs42l52->beep_work); + cs42l52->beep = NULL; + + snd_soc_component_update_bits(component, CS42L52_BEEP_TONE_CTL, + CS42L52_BEEP_EN_MASK, 0); +} + +static int cs42l52_probe(struct snd_soc_component *component) +{ + struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(cs42l52->regmap, true); + + cs42l52_add_mic_controls(component); + + cs42l52_init_beep(component); + + cs42l52->sysclk = CS42L52_DEFAULT_CLK; + cs42l52->config.format = CS42L52_DEFAULT_FORMAT; + + return 0; +} + +static void cs42l52_remove(struct snd_soc_component *component) +{ + cs42l52_free_beep(component); +} + +static const struct snd_soc_component_driver soc_component_dev_cs42l52 = { + .probe = cs42l52_probe, + .remove = cs42l52_remove, + .set_bias_level = cs42l52_set_bias_level, + .controls = cs42l52_snd_controls, + .num_controls = ARRAY_SIZE(cs42l52_snd_controls), + .dapm_widgets = cs42l52_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l52_dapm_widgets), + .dapm_routes = cs42l52_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l52_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +/* Current and threshold powerup sequence Pg37 */ +static const struct reg_sequence cs42l52_threshold_patch[] = { + + { 0x00, 0x99 }, + { 0x3E, 0xBA }, + { 0x47, 0x80 }, + { 0x32, 0xBB }, + { 0x32, 0x3B }, + { 0x00, 0x00 }, + +}; + +static const struct regmap_config cs42l52_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS42L52_MAX_REGISTER, + .reg_defaults = cs42l52_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults), + .readable_reg = cs42l52_readable_register, + .volatile_reg = cs42l52_volatile_register, + .cache_type = REGCACHE_MAPLE, +}; + +static int cs42l52_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs42l52_private *cs42l52; + struct cs42l52_platform_data *pdata = dev_get_platdata(&i2c_client->dev); + int ret; + unsigned int devid; + unsigned int reg; + u32 val32; + + cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l52), GFP_KERNEL); + if (cs42l52 == NULL) + return -ENOMEM; + cs42l52->dev = &i2c_client->dev; + + cs42l52->regmap = devm_regmap_init_i2c(i2c_client, &cs42l52_regmap); + if (IS_ERR(cs42l52->regmap)) { + ret = PTR_ERR(cs42l52->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + if (pdata) { + cs42l52->pdata = *pdata; + } else { + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (i2c_client->dev.of_node) { + if (of_property_read_bool(i2c_client->dev.of_node, + "cirrus,mica-differential-cfg")) + pdata->mica_diff_cfg = true; + + if (of_property_read_bool(i2c_client->dev.of_node, + "cirrus,micb-differential-cfg")) + pdata->micb_diff_cfg = true; + + if (of_property_read_u32(i2c_client->dev.of_node, + "cirrus,micbias-lvl", &val32) >= 0) + pdata->micbias_lvl = val32; + + if (of_property_read_u32(i2c_client->dev.of_node, + "cirrus,chgfreq-divisor", &val32) >= 0) + pdata->chgfreq = val32; + + pdata->reset_gpio = + of_get_named_gpio(i2c_client->dev.of_node, + "cirrus,reset-gpio", 0); + } + cs42l52->pdata = *pdata; + } + + if (cs42l52->pdata.reset_gpio) { + ret = devm_gpio_request_one(&i2c_client->dev, + cs42l52->pdata.reset_gpio, + GPIOF_OUT_INIT_HIGH, + "CS42L52 /RST"); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n", + cs42l52->pdata.reset_gpio, ret); + return ret; + } + gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 0); + gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 1); + } + + i2c_set_clientdata(i2c_client, cs42l52); + + ret = regmap_register_patch(cs42l52->regmap, cs42l52_threshold_patch, + ARRAY_SIZE(cs42l52_threshold_patch)); + if (ret != 0) + dev_warn(cs42l52->dev, "Failed to apply regmap patch: %d\n", + ret); + + ret = regmap_read(cs42l52->regmap, CS42L52_CHIP, ®); + if (ret) { + dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret); + return ret; + } + + devid = reg & CS42L52_CHIP_ID_MASK; + if (devid != CS42L52_CHIP_ID) { + ret = -ENODEV; + dev_err(&i2c_client->dev, + "CS42L52 Device ID (%X). Expected %X\n", + devid, CS42L52_CHIP_ID); + return ret; + } + + dev_info(&i2c_client->dev, "Cirrus Logic CS42L52, Revision: %02X\n", + reg & CS42L52_CHIP_REV_MASK); + + /* Set Platform Data */ + if (cs42l52->pdata.mica_diff_cfg) + regmap_update_bits(cs42l52->regmap, CS42L52_MICA_CTL, + CS42L52_MIC_CTL_TYPE_MASK, + cs42l52->pdata.mica_diff_cfg << + CS42L52_MIC_CTL_TYPE_SHIFT); + + if (cs42l52->pdata.micb_diff_cfg) + regmap_update_bits(cs42l52->regmap, CS42L52_MICB_CTL, + CS42L52_MIC_CTL_TYPE_MASK, + cs42l52->pdata.micb_diff_cfg << + CS42L52_MIC_CTL_TYPE_SHIFT); + + if (cs42l52->pdata.chgfreq) + regmap_update_bits(cs42l52->regmap, CS42L52_CHARGE_PUMP, + CS42L52_CHARGE_PUMP_MASK, + cs42l52->pdata.chgfreq << + CS42L52_CHARGE_PUMP_SHIFT); + + if (cs42l52->pdata.micbias_lvl) + regmap_update_bits(cs42l52->regmap, CS42L52_IFACE_CTL2, + CS42L52_IFACE_CTL2_BIAS_LVL, + cs42l52->pdata.micbias_lvl); + + return devm_snd_soc_register_component(&i2c_client->dev, + &soc_component_dev_cs42l52, &cs42l52_dai, 1); +} + +static const struct of_device_id cs42l52_of_match[] = { + { .compatible = "cirrus,cs42l52", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs42l52_of_match); + + +static const struct i2c_device_id cs42l52_id[] = { + { "cs42l52", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs42l52_id); + +static struct i2c_driver cs42l52_i2c_driver = { + .driver = { + .name = "cs42l52", + .of_match_table = cs42l52_of_match, + }, + .id_table = cs42l52_id, + .probe = cs42l52_i2c_probe, +}; + +module_i2c_driver(cs42l52_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L52 driver"); +MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, "); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l52.h b/sound/soc/codecs/cs42l52.h new file mode 100644 index 0000000000..e485670f9a --- /dev/null +++ b/sound/soc/codecs/cs42l52.h @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * cs42l52.h -- CS42L52 ALSA SoC audio driver + * + * Copyright 2012 CirrusLogic, Inc. + * + * Author: Georgi Vlaev + * Author: Brian Austin + */ + +#ifndef __CS42L52_H__ +#define __CS42L52_H__ + +#define CS42L52_NAME "CS42L52" +#define CS42L52_DEFAULT_CLK 12000000 +#define CS42L52_MIN_CLK 11000000 +#define CS42L52_MAX_CLK 27000000 +#define CS42L52_DEFAULT_FORMAT SNDRV_PCM_FMTBIT_S16_LE +#define CS42L52_DEFAULT_MAX_CHANS 2 +#define CS42L52_SYSCLK 1 + +#define CS42L52_CHIP_SWICTH (1 << 17) +#define CS42L52_ALL_IN_ONE (1 << 16) +#define CS42L52_CHIP_ONE 0x00 +#define CS42L52_CHIP_TWO 0x01 +#define CS42L52_CHIP_THR 0x02 +#define CS42L52_CHIP_MASK 0x0f + +#define CS42L52_FIX_BITS_CTL 0x00 +#define CS42L52_CHIP 0x01 +#define CS42L52_CHIP_ID 0xE0 +#define CS42L52_CHIP_ID_MASK 0xF8 +#define CS42L52_CHIP_REV_A0 0x00 +#define CS42L52_CHIP_REV_A1 0x01 +#define CS42L52_CHIP_REV_B0 0x02 +#define CS42L52_CHIP_REV_MASK 0x07 + +#define CS42L52_PWRCTL1 0x02 +#define CS42L52_PWRCTL1_PDN_ALL 0x9F +#define CS42L52_PWRCTL1_PDN_CHRG 0x80 +#define CS42L52_PWRCTL1_PDN_PGAB 0x10 +#define CS42L52_PWRCTL1_PDN_PGAA 0x08 +#define CS42L52_PWRCTL1_PDN_ADCB 0x04 +#define CS42L52_PWRCTL1_PDN_ADCA 0x02 +#define CS42L52_PWRCTL1_PDN_CODEC 0x01 + +#define CS42L52_PWRCTL2 0x03 +#define CS42L52_PWRCTL2_OVRDB (1 << 4) +#define CS42L52_PWRCTL2_OVRDA (1 << 3) +#define CS42L52_PWRCTL2_PDN_MICB (1 << 2) +#define CS42L52_PWRCTL2_PDN_MICB_SHIFT 2 +#define CS42L52_PWRCTL2_PDN_MICA (1 << 1) +#define CS42L52_PWRCTL2_PDN_MICA_SHIFT 1 +#define CS42L52_PWRCTL2_PDN_MICBIAS (1 << 0) +#define CS42L52_PWRCTL2_PDN_MICBIAS_SHIFT 0 + +#define CS42L52_PWRCTL3 0x04 +#define CS42L52_PWRCTL3_HPB_PDN_SHIFT 6 +#define CS42L52_PWRCTL3_HPB_ON_LOW 0x00 +#define CS42L52_PWRCTL3_HPB_ON_HIGH 0x01 +#define CS42L52_PWRCTL3_HPB_ALWAYS_ON 0x02 +#define CS42L52_PWRCTL3_HPB_ALWAYS_OFF 0x03 +#define CS42L52_PWRCTL3_HPA_PDN_SHIFT 4 +#define CS42L52_PWRCTL3_HPA_ON_LOW 0x00 +#define CS42L52_PWRCTL3_HPA_ON_HIGH 0x01 +#define CS42L52_PWRCTL3_HPA_ALWAYS_ON 0x02 +#define CS42L52_PWRCTL3_HPA_ALWAYS_OFF 0x03 +#define CS42L52_PWRCTL3_SPKB_PDN_SHIFT 2 +#define CS42L52_PWRCTL3_SPKB_ON_LOW 0x00 +#define CS42L52_PWRCTL3_SPKB_ON_HIGH 0x01 +#define CS42L52_PWRCTL3_SPKB_ALWAYS_ON 0x02 +#define CS42L52_PWRCTL3_PDN_SPKB (1 << 2) +#define CS42L52_PWRCTL3_PDN_SPKA (1 << 0) +#define CS42L52_PWRCTL3_SPKA_PDN_SHIFT 0 +#define CS42L52_PWRCTL3_SPKA_ON_LOW 0x00 +#define CS42L52_PWRCTL3_SPKA_ON_HIGH 0x01 +#define CS42L52_PWRCTL3_SPKA_ALWAYS_ON 0x02 + +#define CS42L52_DEFAULT_OUTPUT_STATE 0x05 +#define CS42L52_PWRCTL3_CONF_MASK 0x03 + +#define CS42L52_CLK_CTL 0x05 +#define CLK_AUTODECT_ENABLE (1 << 7) +#define CLK_SPEED_SHIFT 5 +#define CLK_DS_MODE 0x00 +#define CLK_SS_MODE 0x01 +#define CLK_HS_MODE 0x02 +#define CLK_QS_MODE 0x03 +#define CLK_32K_SR_SHIFT 4 +#define CLK_32K 0x01 +#define CLK_NO_32K 0x00 +#define CLK_27M_MCLK_SHIFT 3 +#define CLK_27M_MCLK 0x01 +#define CLK_NO_27M 0x00 +#define CLK_RATIO_SHIFT 1 +#define CLK_R_128 0x00 +#define CLK_R_125 0x01 +#define CLK_R_132 0x02 +#define CLK_R_136 0x03 + +#define CS42L52_IFACE_CTL1 0x06 +#define CS42L52_IFACE_CTL1_MASTER (1 << 7) +#define CS42L52_IFACE_CTL1_SLAVE (0 << 7) +#define CS42L52_IFACE_CTL1_INV_SCLK (1 << 6) +#define CS42L52_IFACE_CTL1_ADC_FMT_I2S (1 << 5) +#define CS42L52_IFACE_CTL1_ADC_FMT_LEFT_J (0 << 5) +#define CS42L52_IFACE_CTL1_DSP_MODE_EN (1 << 4) +#define CS42L52_IFACE_CTL1_DAC_FMT_LEFT_J (0 << 2) +#define CS42L52_IFACE_CTL1_DAC_FMT_I2S (1 << 2) +#define CS42L52_IFACE_CTL1_DAC_FMT_RIGHT_J (2 << 2) +#define CS42L52_IFACE_CTL1_WL_32BIT (0x00) +#define CS42L52_IFACE_CTL1_WL_24BIT (0x01) +#define CS42L52_IFACE_CTL1_WL_20BIT (0x02) +#define CS42L52_IFACE_CTL1_WL_16BIT (0x03) +#define CS42L52_IFACE_CTL1_WL_MASK 0xFFFF + +#define CS42L52_IFACE_CTL2 0x07 +#define CS42L52_IFACE_CTL2_SC_MC_EQ (1 << 6) +#define CS42L52_IFACE_CTL2_LOOPBACK (1 << 5) +#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_EN (0 << 4) +#define CS42L52_IFACE_CTL2_S_MODE_OUTPUT_HIZ (1 << 4) +#define CS42L52_IFACE_CTL2_HP_SW_INV (1 << 3) +#define CS42L52_IFACE_CTL2_BIAS_LVL 0x07 + +#define CS42L52_ADC_PGA_A 0x08 +#define CS42L52_ADC_PGA_B 0x09 +#define CS42L52_ADC_SEL_SHIFT 5 +#define CS42L52_ADC_SEL_AIN1 0x00 +#define CS42L52_ADC_SEL_AIN2 0x01 +#define CS42L52_ADC_SEL_AIN3 0x02 +#define CS42L52_ADC_SEL_AIN4 0x03 +#define CS42L52_ADC_SEL_PGA 0x04 + +#define CS42L52_ANALOG_HPF_CTL 0x0A +#define CS42L52_HPF_CTL_ANLGSFTB (1 << 3) +#define CS42L52_HPF_CTL_ANLGSFTA (1 << 0) + +#define CS42L52_ADC_HPF_FREQ 0x0B +#define CS42L52_ADC_MISC_CTL 0x0C +#define CS42L52_ADC_MISC_CTL_SOURCE_DSP (1 << 6) + +#define CS42L52_PB_CTL1 0x0D +#define CS42L52_PB_CTL1_HP_GAIN_SHIFT 5 +#define CS42L52_PB_CTL1_HP_GAIN_03959 0x00 +#define CS42L52_PB_CTL1_HP_GAIN_04571 0x01 +#define CS42L52_PB_CTL1_HP_GAIN_05111 0x02 +#define CS42L52_PB_CTL1_HP_GAIN_06047 0x03 +#define CS42L52_PB_CTL1_HP_GAIN_07099 0x04 +#define CS42L52_PB_CTL1_HP_GAIN_08399 0x05 +#define CS42L52_PB_CTL1_HP_GAIN_10000 0x06 +#define CS42L52_PB_CTL1_HP_GAIN_11430 0x07 +#define CS42L52_PB_CTL1_INV_PCMB (1 << 3) +#define CS42L52_PB_CTL1_INV_PCMA (1 << 2) +#define CS42L52_PB_CTL1_MSTB_MUTE (1 << 1) +#define CS42L52_PB_CTL1_MSTA_MUTE (1 << 0) +#define CS42L52_PB_CTL1_MUTE_MASK 0x03 +#define CS42L52_PB_CTL1_MUTE 3 +#define CS42L52_PB_CTL1_UNMUTE 0 + +#define CS42L52_MISC_CTL 0x0E +#define CS42L52_MISC_CTL_DEEMPH (1 << 2) +#define CS42L52_MISC_CTL_DIGSFT (1 << 1) +#define CS42L52_MISC_CTL_DIGZC (1 << 0) + +#define CS42L52_PB_CTL2 0x0F +#define CS42L52_PB_CTL2_HPB_MUTE (1 << 7) +#define CS42L52_PB_CTL2_HPA_MUTE (1 << 6) +#define CS42L52_PB_CTL2_SPKB_MUTE (1 << 5) +#define CS42L52_PB_CTL2_SPKA_MUTE (1 << 4) +#define CS42L52_PB_CTL2_SPK_SWAP (1 << 2) +#define CS42L52_PB_CTL2_SPK_MONO (1 << 1) +#define CS42L52_PB_CTL2_SPK_MUTE50 (1 << 0) + +#define CS42L52_MICA_CTL 0x10 +#define CS42L52_MICB_CTL 0x11 +#define CS42L52_MIC_CTL_MIC_SEL_MASK 0xBF +#define CS42L52_MIC_CTL_MIC_SEL_SHIFT 6 +#define CS42L52_MIC_CTL_TYPE_MASK 0x20 +#define CS42L52_MIC_CTL_TYPE_SHIFT 5 + + +#define CS42L52_PGAA_CTL 0x12 +#define CS42L52_PGAB_CTL 0x13 +#define CS42L52_PGAX_CTL_VOL_12DB 24 +#define CS42L52_PGAX_CTL_VOL_6DB 12 /*step size 0.5db*/ + +#define CS42L52_PASSTHRUA_VOL 0x14 +#define CS42L52_PASSTHRUB_VOL 0x15 + +#define CS42L52_ADCA_VOL 0x16 +#define CS42L52_ADCB_VOL 0x17 +#define CS42L52_ADCX_VOL_24DB 24 /*step size 1db*/ +#define CS42L52_ADCX_VOL_12DB 12 +#define CS42L52_ADCX_VOL_6DB 6 + +#define CS42L52_ADCA_MIXER_VOL 0x18 +#define CS42L52_ADCB_MIXER_VOL 0x19 +#define CS42L52_ADC_MIXER_VOL_12DB 0x18 + +#define CS42L52_PCMA_MIXER_VOL 0x1A +#define CS42L52_PCMB_MIXER_VOL 0x1B + +#define CS42L52_BEEP_FREQ 0x1C +#define CS42L52_BEEP_VOL 0x1D +#define CS42L52_BEEP_TONE_CTL 0x1E +#define CS42L52_BEEP_RATE_SHIFT 4 +#define CS42L52_BEEP_RATE_MASK 0x0F + +#define CS42L52_TONE_CTL 0x1F +#define CS42L52_BEEP_EN_MASK 0x3F + +#define CS42L52_MASTERA_VOL 0x20 +#define CS42L52_MASTERB_VOL 0x21 + +#define CS42L52_HPA_VOL 0x22 +#define CS42L52_HPB_VOL 0x23 +#define CS42L52_DEFAULT_HP_VOL 0xF0 + +#define CS42L52_SPKA_VOL 0x24 +#define CS42L52_SPKB_VOL 0x25 +#define CS42L52_DEFAULT_SPK_VOL 0xF0 + +#define CS42L52_ADC_PCM_MIXER 0x26 + +#define CS42L52_LIMITER_CTL1 0x27 +#define CS42L52_LIMITER_CTL2 0x28 +#define CS42L52_LIMITER_AT_RATE 0x29 + +#define CS42L52_ALC_CTL 0x2A +#define CS42L52_ALC_CTL_ALCB_ENABLE_SHIFT 7 +#define CS42L52_ALC_CTL_ALCA_ENABLE_SHIFT 6 +#define CS42L52_ALC_CTL_FASTEST_ATTACK 0 + +#define CS42L52_ALC_RATE 0x2B +#define CS42L52_ALC_SLOWEST_RELEASE 0x3F + +#define CS42L52_ALC_THRESHOLD 0x2C +#define CS42L52_ALC_MAX_RATE_SHIFT 5 +#define CS42L52_ALC_MIN_RATE_SHIFT 2 +#define CS42L52_ALC_RATE_0DB 0 +#define CS42L52_ALC_RATE_3DB 1 +#define CS42L52_ALC_RATE_6DB 2 + +#define CS42L52_NOISE_GATE_CTL 0x2D +#define CS42L52_NG_ENABLE_SHIFT 6 +#define CS42L52_NG_THRESHOLD_SHIFT 2 +#define CS42L52_NG_MIN_70DB 2 +#define CS42L52_NG_DELAY_SHIFT 0 +#define CS42L52_NG_DELAY_100MS 1 + +#define CS42L52_CLK_STATUS 0x2E +#define CS42L52_BATT_COMPEN 0x2F + +#define CS42L52_BATT_LEVEL 0x30 +#define CS42L52_SPK_STATUS 0x31 +#define CS42L52_SPK_STATUS_PIN_SHIFT 3 +#define CS42L52_SPK_STATUS_PIN_HIGH 1 + +#define CS42L52_TEM_CTL 0x32 +#define CS42L52_TEM_CTL_SET 0x80 +#define CS42L52_THE_FOLDBACK 0x33 +#define CS42L52_CHARGE_PUMP 0x34 +#define CS42L52_CHARGE_PUMP_MASK 0xF0 +#define CS42L52_CHARGE_PUMP_SHIFT 4 +#define CS42L52_FIX_BITS1 0x3E +#define CS42L52_FIX_BITS2 0x47 + +#define CS42L52_MAX_REGISTER 0x47 + +#endif diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c new file mode 100644 index 0000000000..1714857594 --- /dev/null +++ b/sound/soc/codecs/cs42l56.c @@ -0,0 +1,1352 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l56.c -- CS42L56 ALSA SoC audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs42l56.h" + +#define CS42L56_NUM_SUPPLIES 3 +static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = { + "VA", + "VCP", + "VLDO", +}; + +struct cs42l56_private { + struct regmap *regmap; + struct snd_soc_component *component; + struct device *dev; + struct cs42l56_platform_data pdata; + struct regulator_bulk_data supplies[CS42L56_NUM_SUPPLIES]; + u32 mclk; + u8 mclk_prediv; + u8 mclk_div2; + u8 mclk_ratio; + u8 iface; + u8 iface_fmt; + u8 iface_inv; +#if IS_ENABLED(CONFIG_INPUT) + struct input_dev *beep; + struct work_struct beep_work; + int beep_rate; +#endif +}; + +static const struct reg_default cs42l56_reg_defaults[] = { + { 3, 0x7f }, /* r03 - Power Ctl 1 */ + { 4, 0xff }, /* r04 - Power Ctl 2 */ + { 5, 0x00 }, /* ro5 - Clocking Ctl 1 */ + { 6, 0x0b }, /* r06 - Clocking Ctl 2 */ + { 7, 0x00 }, /* r07 - Serial Format */ + { 8, 0x05 }, /* r08 - Class H Ctl */ + { 9, 0x0c }, /* r09 - Misc Ctl */ + { 10, 0x80 }, /* r0a - INT Status */ + { 11, 0x00 }, /* r0b - Playback Ctl */ + { 12, 0x0c }, /* r0c - DSP Mute Ctl */ + { 13, 0x00 }, /* r0d - ADCA Mixer Volume */ + { 14, 0x00 }, /* r0e - ADCB Mixer Volume */ + { 15, 0x00 }, /* r0f - PCMA Mixer Volume */ + { 16, 0x00 }, /* r10 - PCMB Mixer Volume */ + { 17, 0x00 }, /* r11 - Analog Input Advisory Volume */ + { 18, 0x00 }, /* r12 - Digital Input Advisory Volume */ + { 19, 0x00 }, /* r13 - Master A Volume */ + { 20, 0x00 }, /* r14 - Master B Volume */ + { 21, 0x00 }, /* r15 - Beep Freq / On Time */ + { 22, 0x00 }, /* r16 - Beep Volume / Off Time */ + { 23, 0x00 }, /* r17 - Beep Tone Ctl */ + { 24, 0x88 }, /* r18 - Tone Ctl */ + { 25, 0x00 }, /* r19 - Channel Mixer & Swap */ + { 26, 0x00 }, /* r1a - AIN Ref Config / ADC Mux */ + { 27, 0xa0 }, /* r1b - High-Pass Filter Ctl */ + { 28, 0x00 }, /* r1c - Misc ADC Ctl */ + { 29, 0x00 }, /* r1d - Gain & Bias Ctl */ + { 30, 0x00 }, /* r1e - PGAA Mux & Volume */ + { 31, 0x00 }, /* r1f - PGAB Mux & Volume */ + { 32, 0x00 }, /* r20 - ADCA Attenuator */ + { 33, 0x00 }, /* r21 - ADCB Attenuator */ + { 34, 0x00 }, /* r22 - ALC Enable & Attack Rate */ + { 35, 0xbf }, /* r23 - ALC Release Rate */ + { 36, 0x00 }, /* r24 - ALC Threshold */ + { 37, 0x00 }, /* r25 - Noise Gate Ctl */ + { 38, 0x00 }, /* r26 - ALC, Limiter, SFT, ZeroCross */ + { 39, 0x00 }, /* r27 - Analog Mute, LO & HP Mux */ + { 40, 0x00 }, /* r28 - HP A Volume */ + { 41, 0x00 }, /* r29 - HP B Volume */ + { 42, 0x00 }, /* r2a - LINEOUT A Volume */ + { 43, 0x00 }, /* r2b - LINEOUT B Volume */ + { 44, 0x00 }, /* r2c - Limit Threshold Ctl */ + { 45, 0x7f }, /* r2d - Limiter Ctl & Release Rate */ + { 46, 0x00 }, /* r2e - Limiter Attack Rate */ +}; + +static bool cs42l56_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L56_CHIP_ID_1 ... CS42L56_LIM_ATTACK_RATE: + return true; + default: + return false; + } +} + +static bool cs42l56_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L56_INT_STATUS: + return true; + default: + return false; + } +} + +static DECLARE_TLV_DB_SCALE(beep_tlv, -5000, 200, 0); +static DECLARE_TLV_DB_SCALE(hl_tlv, -6000, 50, 0); +static DECLARE_TLV_DB_SCALE(adv_tlv, -10200, 50, 0); +static DECLARE_TLV_DB_SCALE(adc_tlv, -9600, 100, 0); +static DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0); +static DECLARE_TLV_DB_SCALE(preamp_tlv, 0, 1000, 0); +static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0); + +static const DECLARE_TLV_DB_RANGE(ngnb_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-8200, 600, 0), + 2, 5, TLV_DB_SCALE_ITEM(-7600, 300, 0) +); +static const DECLARE_TLV_DB_RANGE(ngb_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-6400, 600, 0), + 3, 7, TLV_DB_SCALE_ITEM(-4600, 300, 0) +); +static const DECLARE_TLV_DB_RANGE(alc_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), + 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0) +); + +static const char * const beep_config_text[] = { + "Off", "Single", "Multiple", "Continuous" +}; + +static const struct soc_enum beep_config_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 6, + ARRAY_SIZE(beep_config_text), beep_config_text); + +static const char * const beep_pitch_text[] = { + "C4", "C5", "D5", "E5", "F5", "G5", "A5", "B5", + "C6", "D6", "E6", "F6", "G6", "A6", "B6", "C7" +}; + +static const struct soc_enum beep_pitch_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_ONTIME, 4, + ARRAY_SIZE(beep_pitch_text), beep_pitch_text); + +static const char * const beep_ontime_text[] = { + "86 ms", "430 ms", "780 ms", "1.20 s", "1.50 s", + "1.80 s", "2.20 s", "2.50 s", "2.80 s", "3.20 s", + "3.50 s", "3.80 s", "4.20 s", "4.50 s", "4.80 s", "5.20 s" +}; + +static const struct soc_enum beep_ontime_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_ONTIME, 0, + ARRAY_SIZE(beep_ontime_text), beep_ontime_text); + +static const char * const beep_offtime_text[] = { + "1.23 s", "2.58 s", "3.90 s", "5.20 s", + "6.60 s", "8.05 s", "9.35 s", "10.80 s" +}; + +static const struct soc_enum beep_offtime_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_FREQ_OFFTIME, 5, + ARRAY_SIZE(beep_offtime_text), beep_offtime_text); + +static const char * const beep_treble_text[] = { + "5kHz", "7kHz", "10kHz", "15kHz" +}; + +static const struct soc_enum beep_treble_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 3, + ARRAY_SIZE(beep_treble_text), beep_treble_text); + +static const char * const beep_bass_text[] = { + "50Hz", "100Hz", "200Hz", "250Hz" +}; + +static const struct soc_enum beep_bass_enum = + SOC_ENUM_SINGLE(CS42L56_BEEP_TONE_CFG, 1, + ARRAY_SIZE(beep_bass_text), beep_bass_text); + +static const char * const pgaa_mux_text[] = { + "AIN1A", "AIN2A", "AIN3A"}; + +static const struct soc_enum pgaa_mux_enum = + SOC_ENUM_SINGLE(CS42L56_PGAA_MUX_VOLUME, 0, + ARRAY_SIZE(pgaa_mux_text), + pgaa_mux_text); + +static const struct snd_kcontrol_new pgaa_mux = + SOC_DAPM_ENUM("Route", pgaa_mux_enum); + +static const char * const pgab_mux_text[] = { + "AIN1B", "AIN2B", "AIN3B"}; + +static const struct soc_enum pgab_mux_enum = + SOC_ENUM_SINGLE(CS42L56_PGAB_MUX_VOLUME, 0, + ARRAY_SIZE(pgab_mux_text), + pgab_mux_text); + +static const struct snd_kcontrol_new pgab_mux = + SOC_DAPM_ENUM("Route", pgab_mux_enum); + +static const char * const adca_mux_text[] = { + "PGAA", "AIN1A", "AIN2A", "AIN3A"}; + +static const struct soc_enum adca_mux_enum = + SOC_ENUM_SINGLE(CS42L56_AIN_REFCFG_ADC_MUX, 0, + ARRAY_SIZE(adca_mux_text), + adca_mux_text); + +static const struct snd_kcontrol_new adca_mux = + SOC_DAPM_ENUM("Route", adca_mux_enum); + +static const char * const adcb_mux_text[] = { + "PGAB", "AIN1B", "AIN2B", "AIN3B"}; + +static const struct soc_enum adcb_mux_enum = + SOC_ENUM_SINGLE(CS42L56_AIN_REFCFG_ADC_MUX, 2, + ARRAY_SIZE(adcb_mux_text), + adcb_mux_text); + +static const struct snd_kcontrol_new adcb_mux = + SOC_DAPM_ENUM("Route", adcb_mux_enum); + +static const char * const left_swap_text[] = { + "Left", "LR 2", "Right"}; + +static const char * const right_swap_text[] = { + "Right", "LR 2", "Left"}; + +static const unsigned int swap_values[] = { 0, 1, 3 }; + +static const struct soc_enum adca_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 0, 3, + ARRAY_SIZE(left_swap_text), + left_swap_text, + swap_values); +static const struct snd_kcontrol_new adca_swap_mux = + SOC_DAPM_ENUM("Route", adca_swap_enum); + +static const struct soc_enum pcma_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 4, 3, + ARRAY_SIZE(left_swap_text), + left_swap_text, + swap_values); +static const struct snd_kcontrol_new pcma_swap_mux = + SOC_DAPM_ENUM("Route", pcma_swap_enum); + +static const struct soc_enum adcb_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 2, 3, + ARRAY_SIZE(right_swap_text), + right_swap_text, + swap_values); +static const struct snd_kcontrol_new adcb_swap_mux = + SOC_DAPM_ENUM("Route", adcb_swap_enum); + +static const struct soc_enum pcmb_swap_enum = + SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 6, 3, + ARRAY_SIZE(right_swap_text), + right_swap_text, + swap_values); +static const struct snd_kcontrol_new pcmb_swap_mux = + SOC_DAPM_ENUM("Route", pcmb_swap_enum); + +static const struct snd_kcontrol_new hpa_switch = + SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 6, 1, 1); + +static const struct snd_kcontrol_new hpb_switch = + SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 4, 1, 1); + +static const struct snd_kcontrol_new loa_switch = + SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 2, 1, 1); + +static const struct snd_kcontrol_new lob_switch = + SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 0, 1, 1); + +static const char * const hploa_input_text[] = { + "DACA", "PGAA"}; + +static const struct soc_enum lineouta_input_enum = + SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 2, + ARRAY_SIZE(hploa_input_text), + hploa_input_text); + +static const struct snd_kcontrol_new lineouta_input = + SOC_DAPM_ENUM("Route", lineouta_input_enum); + +static const struct soc_enum hpa_input_enum = + SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 0, + ARRAY_SIZE(hploa_input_text), + hploa_input_text); + +static const struct snd_kcontrol_new hpa_input = + SOC_DAPM_ENUM("Route", hpa_input_enum); + +static const char * const hplob_input_text[] = { + "DACB", "PGAB"}; + +static const struct soc_enum lineoutb_input_enum = + SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 3, + ARRAY_SIZE(hplob_input_text), + hplob_input_text); + +static const struct snd_kcontrol_new lineoutb_input = + SOC_DAPM_ENUM("Route", lineoutb_input_enum); + +static const struct soc_enum hpb_input_enum = + SOC_ENUM_SINGLE(CS42L56_AMUTE_HPLO_MUX, 1, + ARRAY_SIZE(hplob_input_text), + hplob_input_text); + +static const struct snd_kcontrol_new hpb_input = + SOC_DAPM_ENUM("Route", hpb_input_enum); + +static const char * const dig_mux_text[] = { + "ADC", "DSP"}; + +static const struct soc_enum dig_mux_enum = + SOC_ENUM_SINGLE(CS42L56_MISC_CTL, 7, + ARRAY_SIZE(dig_mux_text), + dig_mux_text); + +static const struct snd_kcontrol_new dig_mux = + SOC_DAPM_ENUM("Route", dig_mux_enum); + +static const char * const hpf_freq_text[] = { + "1.8Hz", "119Hz", "236Hz", "464Hz" +}; + +static const struct soc_enum hpfa_freq_enum = + SOC_ENUM_SINGLE(CS42L56_HPF_CTL, 0, + ARRAY_SIZE(hpf_freq_text), hpf_freq_text); + +static const struct soc_enum hpfb_freq_enum = + SOC_ENUM_SINGLE(CS42L56_HPF_CTL, 2, + ARRAY_SIZE(hpf_freq_text), hpf_freq_text); + +static const char * const ng_delay_text[] = { + "50ms", "100ms", "150ms", "200ms" +}; + +static const struct soc_enum ng_delay_enum = + SOC_ENUM_SINGLE(CS42L56_NOISE_GATE_CTL, 0, + ARRAY_SIZE(ng_delay_text), ng_delay_text); + +static const struct snd_kcontrol_new cs42l56_snd_controls[] = { + + SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L56_MASTER_A_VOLUME, + CS42L56_MASTER_B_VOLUME, 0, 0x34, 0xE4, adv_tlv), + SOC_DOUBLE("Master Mute Switch", CS42L56_DSP_MUTE_CTL, 0, 1, 1, 1), + + SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", CS42L56_ADCA_MIX_VOLUME, + CS42L56_ADCB_MIX_VOLUME, 0, 0x88, 0x90, hl_tlv), + SOC_DOUBLE("ADC Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 6, 7, 1, 1), + + SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume", CS42L56_PCMA_MIX_VOLUME, + CS42L56_PCMB_MIX_VOLUME, 0, 0x88, 0x90, hl_tlv), + SOC_DOUBLE("PCM Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 4, 5, 1, 1), + + SOC_SINGLE_TLV("Analog Advisory Volume", + CS42L56_ANAINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv), + SOC_SINGLE_TLV("Digital Advisory Volume", + CS42L56_DIGINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv), + + SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L56_PGAA_MUX_VOLUME, + CS42L56_PGAB_MUX_VOLUME, 0, 0x34, 0x24, pga_tlv), + SOC_DOUBLE_R_TLV("ADC Volume", CS42L56_ADCA_ATTENUATOR, + CS42L56_ADCB_ATTENUATOR, 0, 0x00, 1, adc_tlv), + SOC_DOUBLE("ADC Mute Switch", CS42L56_MISC_ADC_CTL, 2, 3, 1, 1), + SOC_DOUBLE("ADC Boost Switch", CS42L56_GAIN_BIAS_CTL, 3, 2, 1, 1), + + SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L56_HPA_VOLUME, + CS42L56_HPB_VOLUME, 0, 0x44, 0x48, hl_tlv), + SOC_DOUBLE_R_SX_TLV("LineOut Volume", CS42L56_LOA_VOLUME, + CS42L56_LOB_VOLUME, 0, 0x44, 0x48, hl_tlv), + + SOC_SINGLE_TLV("Bass Shelving Volume", CS42L56_TONE_CTL, + 0, 0x00, 1, tone_tlv), + SOC_SINGLE_TLV("Treble Shelving Volume", CS42L56_TONE_CTL, + 4, 0x00, 1, tone_tlv), + + SOC_DOUBLE_TLV("PGA Preamp Volume", CS42L56_GAIN_BIAS_CTL, + 4, 6, 0x02, 1, preamp_tlv), + + SOC_SINGLE("DSP Switch", CS42L56_PLAYBACK_CTL, 7, 1, 1), + SOC_SINGLE("Gang Playback Switch", CS42L56_PLAYBACK_CTL, 4, 1, 1), + SOC_SINGLE("Gang ADC Switch", CS42L56_MISC_ADC_CTL, 7, 1, 1), + SOC_SINGLE("Gang PGA Switch", CS42L56_MISC_ADC_CTL, 6, 1, 1), + + SOC_SINGLE("PCMA Invert", CS42L56_PLAYBACK_CTL, 2, 1, 1), + SOC_SINGLE("PCMB Invert", CS42L56_PLAYBACK_CTL, 3, 1, 1), + SOC_SINGLE("ADCA Invert", CS42L56_MISC_ADC_CTL, 2, 1, 1), + SOC_SINGLE("ADCB Invert", CS42L56_MISC_ADC_CTL, 3, 1, 1), + + SOC_DOUBLE("HPF Switch", CS42L56_HPF_CTL, 5, 7, 1, 1), + SOC_DOUBLE("HPF Freeze Switch", CS42L56_HPF_CTL, 4, 6, 1, 1), + SOC_ENUM("HPFA Corner Freq", hpfa_freq_enum), + SOC_ENUM("HPFB Corner Freq", hpfb_freq_enum), + + SOC_SINGLE("Analog Soft Ramp", CS42L56_MISC_CTL, 4, 1, 1), + SOC_DOUBLE("Analog Soft Ramp Disable", CS42L56_ALC_LIM_SFT_ZC, + 7, 5, 1, 1), + SOC_SINGLE("Analog Zero Cross", CS42L56_MISC_CTL, 3, 1, 1), + SOC_DOUBLE("Analog Zero Cross Disable", CS42L56_ALC_LIM_SFT_ZC, + 6, 4, 1, 1), + SOC_SINGLE("Digital Soft Ramp", CS42L56_MISC_CTL, 2, 1, 1), + SOC_SINGLE("Digital Soft Ramp Disable", CS42L56_ALC_LIM_SFT_ZC, + 3, 1, 1), + + SOC_SINGLE("HL Deemphasis", CS42L56_PLAYBACK_CTL, 6, 1, 1), + + SOC_SINGLE("ALC Switch", CS42L56_ALC_EN_ATTACK_RATE, 6, 1, 1), + SOC_SINGLE("ALC Limit All Switch", CS42L56_ALC_RELEASE_RATE, 7, 1, 1), + SOC_SINGLE_RANGE("ALC Attack", CS42L56_ALC_EN_ATTACK_RATE, + 0, 0, 0x3f, 0), + SOC_SINGLE_RANGE("ALC Release", CS42L56_ALC_RELEASE_RATE, + 0, 0x3f, 0, 0), + SOC_SINGLE_TLV("ALC MAX", CS42L56_ALC_THRESHOLD, + 5, 0x07, 1, alc_tlv), + SOC_SINGLE_TLV("ALC MIN", CS42L56_ALC_THRESHOLD, + 2, 0x07, 1, alc_tlv), + + SOC_SINGLE("Limiter Switch", CS42L56_LIM_CTL_RELEASE_RATE, 7, 1, 1), + SOC_SINGLE("Limit All Switch", CS42L56_LIM_CTL_RELEASE_RATE, 6, 1, 1), + SOC_SINGLE_RANGE("Limiter Attack", CS42L56_LIM_ATTACK_RATE, + 0, 0, 0x3f, 0), + SOC_SINGLE_RANGE("Limiter Release", CS42L56_LIM_CTL_RELEASE_RATE, + 0, 0x3f, 0, 0), + SOC_SINGLE_TLV("Limiter MAX", CS42L56_LIM_THRESHOLD_CTL, + 5, 0x07, 1, alc_tlv), + SOC_SINGLE_TLV("Limiter Cushion", CS42L56_ALC_THRESHOLD, + 2, 0x07, 1, alc_tlv), + + SOC_SINGLE("NG Switch", CS42L56_NOISE_GATE_CTL, 6, 1, 1), + SOC_SINGLE("NG All Switch", CS42L56_NOISE_GATE_CTL, 7, 1, 1), + SOC_SINGLE("NG Boost Switch", CS42L56_NOISE_GATE_CTL, 5, 1, 1), + SOC_SINGLE_TLV("NG Unboost Threshold", CS42L56_NOISE_GATE_CTL, + 2, 0x07, 1, ngnb_tlv), + SOC_SINGLE_TLV("NG Boost Threshold", CS42L56_NOISE_GATE_CTL, + 2, 0x07, 1, ngb_tlv), + SOC_ENUM("NG Delay", ng_delay_enum), + + SOC_ENUM("Beep Config", beep_config_enum), + SOC_ENUM("Beep Pitch", beep_pitch_enum), + SOC_ENUM("Beep on Time", beep_ontime_enum), + SOC_ENUM("Beep off Time", beep_offtime_enum), + SOC_SINGLE_SX_TLV("Beep Volume", CS42L56_BEEP_FREQ_OFFTIME, + 0, 0x07, 0x23, beep_tlv), + SOC_SINGLE("Beep Tone Ctl Switch", CS42L56_BEEP_TONE_CFG, 0, 1, 1), + SOC_ENUM("Beep Treble Corner Freq", beep_treble_enum), + SOC_ENUM("Beep Bass Corner Freq", beep_bass_enum), + +}; + +static const struct snd_soc_dapm_widget cs42l56_dapm_widgets[] = { + + SND_SOC_DAPM_SIGGEN("Beep"), + SND_SOC_DAPM_SUPPLY("VBUF", CS42L56_PWRCTL_1, 5, 1, NULL, 0), + SND_SOC_DAPM_MICBIAS("MIC1 Bias", CS42L56_PWRCTL_1, 4, 1), + SND_SOC_DAPM_SUPPLY("Charge Pump", CS42L56_PWRCTL_1, 3, 1, NULL, 0), + + SND_SOC_DAPM_INPUT("AIN1A"), + SND_SOC_DAPM_INPUT("AIN2A"), + SND_SOC_DAPM_INPUT("AIN1B"), + SND_SOC_DAPM_INPUT("AIN2B"), + SND_SOC_DAPM_INPUT("AIN3A"), + SND_SOC_DAPM_INPUT("AIN3B"), + + SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("Digital Output Mux", SND_SOC_NOPM, + 0, 0, &dig_mux), + + SND_SOC_DAPM_PGA("PGAA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGAB", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("PGAA Input Mux", + SND_SOC_NOPM, 0, 0, &pgaa_mux), + SND_SOC_DAPM_MUX("PGAB Input Mux", + SND_SOC_NOPM, 0, 0, &pgab_mux), + + SND_SOC_DAPM_MUX("ADCA Mux", SND_SOC_NOPM, + 0, 0, &adca_mux), + SND_SOC_DAPM_MUX("ADCB Mux", SND_SOC_NOPM, + 0, 0, &adcb_mux), + + SND_SOC_DAPM_ADC("ADCA", NULL, CS42L56_PWRCTL_1, 1, 1), + SND_SOC_DAPM_ADC("ADCB", NULL, CS42L56_PWRCTL_1, 2, 1), + + SND_SOC_DAPM_MUX("ADCA Swap Mux", SND_SOC_NOPM, 0, 0, + &adca_swap_mux), + SND_SOC_DAPM_MUX("ADCB Swap Mux", SND_SOC_NOPM, 0, 0, + &adcb_swap_mux), + + SND_SOC_DAPM_MUX("PCMA Swap Mux", SND_SOC_NOPM, 0, 0, + &pcma_swap_mux), + SND_SOC_DAPM_MUX("PCMB Swap Mux", SND_SOC_NOPM, 0, 0, + &pcmb_swap_mux), + + SND_SOC_DAPM_DAC("DACA", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DACB", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("HPA"), + SND_SOC_DAPM_OUTPUT("LOA"), + SND_SOC_DAPM_OUTPUT("HPB"), + SND_SOC_DAPM_OUTPUT("LOB"), + + SND_SOC_DAPM_SWITCH("Headphone Right", + CS42L56_PWRCTL_2, 4, 1, &hpb_switch), + SND_SOC_DAPM_SWITCH("Headphone Left", + CS42L56_PWRCTL_2, 6, 1, &hpa_switch), + + SND_SOC_DAPM_SWITCH("Lineout Right", + CS42L56_PWRCTL_2, 0, 1, &lob_switch), + SND_SOC_DAPM_SWITCH("Lineout Left", + CS42L56_PWRCTL_2, 2, 1, &loa_switch), + + SND_SOC_DAPM_MUX("LINEOUTA Input Mux", SND_SOC_NOPM, + 0, 0, &lineouta_input), + SND_SOC_DAPM_MUX("LINEOUTB Input Mux", SND_SOC_NOPM, + 0, 0, &lineoutb_input), + SND_SOC_DAPM_MUX("HPA Input Mux", SND_SOC_NOPM, + 0, 0, &hpa_input), + SND_SOC_DAPM_MUX("HPB Input Mux", SND_SOC_NOPM, + 0, 0, &hpb_input), + +}; + +static const struct snd_soc_dapm_route cs42l56_audio_map[] = { + + {"HiFi Capture", "DSP", "Digital Output Mux"}, + {"HiFi Capture", "ADC", "Digital Output Mux"}, + + {"Digital Output Mux", NULL, "ADCA"}, + {"Digital Output Mux", NULL, "ADCB"}, + + {"ADCB", NULL, "ADCB Swap Mux"}, + {"ADCA", NULL, "ADCA Swap Mux"}, + + {"ADCA Swap Mux", NULL, "ADCA"}, + {"ADCB Swap Mux", NULL, "ADCB"}, + + {"DACA", "Left", "ADCA Swap Mux"}, + {"DACA", "LR 2", "ADCA Swap Mux"}, + {"DACA", "Right", "ADCA Swap Mux"}, + + {"DACB", "Left", "ADCB Swap Mux"}, + {"DACB", "LR 2", "ADCB Swap Mux"}, + {"DACB", "Right", "ADCB Swap Mux"}, + + {"ADCA Mux", NULL, "AIN3A"}, + {"ADCA Mux", NULL, "AIN2A"}, + {"ADCA Mux", NULL, "AIN1A"}, + {"ADCA Mux", NULL, "PGAA"}, + {"ADCB Mux", NULL, "AIN3B"}, + {"ADCB Mux", NULL, "AIN2B"}, + {"ADCB Mux", NULL, "AIN1B"}, + {"ADCB Mux", NULL, "PGAB"}, + + {"PGAA", "AIN1A", "PGAA Input Mux"}, + {"PGAA", "AIN2A", "PGAA Input Mux"}, + {"PGAA", "AIN3A", "PGAA Input Mux"}, + {"PGAB", "AIN1B", "PGAB Input Mux"}, + {"PGAB", "AIN2B", "PGAB Input Mux"}, + {"PGAB", "AIN3B", "PGAB Input Mux"}, + + {"PGAA Input Mux", NULL, "AIN1A"}, + {"PGAA Input Mux", NULL, "AIN2A"}, + {"PGAA Input Mux", NULL, "AIN3A"}, + {"PGAB Input Mux", NULL, "AIN1B"}, + {"PGAB Input Mux", NULL, "AIN2B"}, + {"PGAB Input Mux", NULL, "AIN3B"}, + + {"LOB", "Switch", "LINEOUTB Input Mux"}, + {"LOA", "Switch", "LINEOUTA Input Mux"}, + + {"LINEOUTA Input Mux", "PGAA", "PGAA"}, + {"LINEOUTB Input Mux", "PGAB", "PGAB"}, + {"LINEOUTA Input Mux", "DACA", "DACA"}, + {"LINEOUTB Input Mux", "DACB", "DACB"}, + + {"HPA", "Switch", "HPB Input Mux"}, + {"HPB", "Switch", "HPA Input Mux"}, + + {"HPA Input Mux", "PGAA", "PGAA"}, + {"HPB Input Mux", "PGAB", "PGAB"}, + {"HPA Input Mux", "DACA", "DACA"}, + {"HPB Input Mux", "DACB", "DACB"}, + + {"DACA", NULL, "PCMA Swap Mux"}, + {"DACB", NULL, "PCMB Swap Mux"}, + + {"PCMB Swap Mux", "Left", "HiFi Playback"}, + {"PCMB Swap Mux", "LR 2", "HiFi Playback"}, + {"PCMB Swap Mux", "Right", "HiFi Playback"}, + + {"PCMA Swap Mux", "Left", "HiFi Playback"}, + {"PCMA Swap Mux", "LR 2", "HiFi Playback"}, + {"PCMA Swap Mux", "Right", "HiFi Playback"}, + +}; + +struct cs42l56_clk_para { + u32 mclk; + u32 srate; + u8 ratio; +}; + +static const struct cs42l56_clk_para clk_ratio_table[] = { + /* 8k */ + { 6000000, 8000, CS42L56_MCLK_LRCLK_768 }, + { 6144000, 8000, CS42L56_MCLK_LRCLK_750 }, + { 12000000, 8000, CS42L56_MCLK_LRCLK_768 }, + { 12288000, 8000, CS42L56_MCLK_LRCLK_750 }, + { 24000000, 8000, CS42L56_MCLK_LRCLK_768 }, + { 24576000, 8000, CS42L56_MCLK_LRCLK_750 }, + /* 11.025k */ + { 5644800, 11025, CS42L56_MCLK_LRCLK_512}, + { 11289600, 11025, CS42L56_MCLK_LRCLK_512}, + { 22579200, 11025, CS42L56_MCLK_LRCLK_512 }, + /* 11.0294k */ + { 6000000, 110294, CS42L56_MCLK_LRCLK_544 }, + { 12000000, 110294, CS42L56_MCLK_LRCLK_544 }, + { 24000000, 110294, CS42L56_MCLK_LRCLK_544 }, + /* 12k */ + { 6000000, 12000, CS42L56_MCLK_LRCLK_500 }, + { 6144000, 12000, CS42L56_MCLK_LRCLK_512 }, + { 12000000, 12000, CS42L56_MCLK_LRCLK_500 }, + { 12288000, 12000, CS42L56_MCLK_LRCLK_512 }, + { 24000000, 12000, CS42L56_MCLK_LRCLK_500 }, + { 24576000, 12000, CS42L56_MCLK_LRCLK_512 }, + /* 16k */ + { 6000000, 16000, CS42L56_MCLK_LRCLK_375 }, + { 6144000, 16000, CS42L56_MCLK_LRCLK_384 }, + { 12000000, 16000, CS42L56_MCLK_LRCLK_375 }, + { 12288000, 16000, CS42L56_MCLK_LRCLK_384 }, + { 24000000, 16000, CS42L56_MCLK_LRCLK_375 }, + { 24576000, 16000, CS42L56_MCLK_LRCLK_384 }, + /* 22.050k */ + { 5644800, 22050, CS42L56_MCLK_LRCLK_256 }, + { 11289600, 22050, CS42L56_MCLK_LRCLK_256 }, + { 22579200, 22050, CS42L56_MCLK_LRCLK_256 }, + /* 22.0588k */ + { 6000000, 220588, CS42L56_MCLK_LRCLK_272 }, + { 12000000, 220588, CS42L56_MCLK_LRCLK_272 }, + { 24000000, 220588, CS42L56_MCLK_LRCLK_272 }, + /* 24k */ + { 6000000, 24000, CS42L56_MCLK_LRCLK_250 }, + { 6144000, 24000, CS42L56_MCLK_LRCLK_256 }, + { 12000000, 24000, CS42L56_MCLK_LRCLK_250 }, + { 12288000, 24000, CS42L56_MCLK_LRCLK_256 }, + { 24000000, 24000, CS42L56_MCLK_LRCLK_250 }, + { 24576000, 24000, CS42L56_MCLK_LRCLK_256 }, + /* 32k */ + { 6000000, 32000, CS42L56_MCLK_LRCLK_187P5 }, + { 6144000, 32000, CS42L56_MCLK_LRCLK_192 }, + { 12000000, 32000, CS42L56_MCLK_LRCLK_187P5 }, + { 12288000, 32000, CS42L56_MCLK_LRCLK_192 }, + { 24000000, 32000, CS42L56_MCLK_LRCLK_187P5 }, + { 24576000, 32000, CS42L56_MCLK_LRCLK_192 }, + /* 44.118k */ + { 6000000, 44118, CS42L56_MCLK_LRCLK_136 }, + { 12000000, 44118, CS42L56_MCLK_LRCLK_136 }, + { 24000000, 44118, CS42L56_MCLK_LRCLK_136 }, + /* 44.1k */ + { 5644800, 44100, CS42L56_MCLK_LRCLK_128 }, + { 11289600, 44100, CS42L56_MCLK_LRCLK_128 }, + { 22579200, 44100, CS42L56_MCLK_LRCLK_128 }, + /* 48k */ + { 6000000, 48000, CS42L56_MCLK_LRCLK_125 }, + { 6144000, 48000, CS42L56_MCLK_LRCLK_128 }, + { 12000000, 48000, CS42L56_MCLK_LRCLK_125 }, + { 12288000, 48000, CS42L56_MCLK_LRCLK_128 }, + { 24000000, 48000, CS42L56_MCLK_LRCLK_125 }, + { 24576000, 48000, CS42L56_MCLK_LRCLK_128 }, +}; + +static int cs42l56_get_mclk_ratio(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clk_ratio_table); i++) { + if (clk_ratio_table[i].mclk == mclk && + clk_ratio_table[i].srate == rate) + return clk_ratio_table[i].ratio; + } + return -EINVAL; +} + +static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case CS42L56_MCLK_5P6448MHZ: + case CS42L56_MCLK_6MHZ: + case CS42L56_MCLK_6P144MHZ: + cs42l56->mclk_div2 = 0; + cs42l56->mclk_prediv = 0; + break; + case CS42L56_MCLK_11P2896MHZ: + case CS42L56_MCLK_12MHZ: + case CS42L56_MCLK_12P288MHZ: + cs42l56->mclk_div2 = CS42L56_MCLK_DIV2; + cs42l56->mclk_prediv = 0; + break; + case CS42L56_MCLK_22P5792MHZ: + case CS42L56_MCLK_24MHZ: + case CS42L56_MCLK_24P576MHZ: + cs42l56->mclk_div2 = CS42L56_MCLK_DIV2; + cs42l56->mclk_prediv = CS42L56_MCLK_PREDIV; + break; + default: + return -EINVAL; + } + cs42l56->mclk = freq; + + snd_soc_component_update_bits(component, CS42L56_CLKCTL_1, + CS42L56_MCLK_PREDIV_MASK, + cs42l56->mclk_prediv); + snd_soc_component_update_bits(component, CS42L56_CLKCTL_1, + CS42L56_MCLK_DIV2_MASK, + cs42l56->mclk_div2); + + return 0; +} + +static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + cs42l56->iface = CS42L56_MASTER_MODE; + break; + case SND_SOC_DAIFMT_CBS_CFS: + cs42l56->iface = CS42L56_SLAVE_MODE; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + cs42l56->iface_fmt = CS42L56_DIG_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + cs42l56->iface_fmt = CS42L56_DIG_FMT_LEFT_J; + break; + default: + return -EINVAL; + } + + /* sclk inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + cs42l56->iface_inv = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + cs42l56->iface_inv = CS42L56_SCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, CS42L56_CLKCTL_1, + CS42L56_MS_MODE_MASK, cs42l56->iface); + snd_soc_component_update_bits(component, CS42L56_SERIAL_FMT, + CS42L56_DIG_FMT_MASK, cs42l56->iface_fmt); + snd_soc_component_update_bits(component, CS42L56_CLKCTL_1, + CS42L56_SCLK_INV_MASK, cs42l56->iface_inv); + return 0; +} + +static int cs42l56_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + if (mute) { + /* Hit the DSP Mixer first */ + snd_soc_component_update_bits(component, CS42L56_DSP_MUTE_CTL, + CS42L56_ADCAMIX_MUTE_MASK | + CS42L56_ADCBMIX_MUTE_MASK | + CS42L56_PCMAMIX_MUTE_MASK | + CS42L56_PCMBMIX_MUTE_MASK | + CS42L56_MSTB_MUTE_MASK | + CS42L56_MSTA_MUTE_MASK, + CS42L56_MUTE_ALL); + /* Mute ADC's */ + snd_soc_component_update_bits(component, CS42L56_MISC_ADC_CTL, + CS42L56_ADCA_MUTE_MASK | + CS42L56_ADCB_MUTE_MASK, + CS42L56_MUTE_ALL); + /* HP And LO */ + snd_soc_component_update_bits(component, CS42L56_HPA_VOLUME, + CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL); + snd_soc_component_update_bits(component, CS42L56_HPB_VOLUME, + CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL); + snd_soc_component_update_bits(component, CS42L56_LOA_VOLUME, + CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL); + snd_soc_component_update_bits(component, CS42L56_LOB_VOLUME, + CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL); + } else { + snd_soc_component_update_bits(component, CS42L56_DSP_MUTE_CTL, + CS42L56_ADCAMIX_MUTE_MASK | + CS42L56_ADCBMIX_MUTE_MASK | + CS42L56_PCMAMIX_MUTE_MASK | + CS42L56_PCMBMIX_MUTE_MASK | + CS42L56_MSTB_MUTE_MASK | + CS42L56_MSTA_MUTE_MASK, + CS42L56_UNMUTE); + + snd_soc_component_update_bits(component, CS42L56_MISC_ADC_CTL, + CS42L56_ADCA_MUTE_MASK | + CS42L56_ADCB_MUTE_MASK, + CS42L56_UNMUTE); + + snd_soc_component_update_bits(component, CS42L56_HPA_VOLUME, + CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE); + snd_soc_component_update_bits(component, CS42L56_HPB_VOLUME, + CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE); + snd_soc_component_update_bits(component, CS42L56_LOA_VOLUME, + CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE); + snd_soc_component_update_bits(component, CS42L56_LOB_VOLUME, + CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE); + } + return 0; +} + +static int cs42l56_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component); + int ratio; + + ratio = cs42l56_get_mclk_ratio(cs42l56->mclk, params_rate(params)); + if (ratio >= 0) { + snd_soc_component_update_bits(component, CS42L56_CLKCTL_2, + CS42L56_CLK_RATIO_MASK, ratio); + } else { + dev_err(component->dev, "unsupported mclk/sclk/lrclk ratio\n"); + return -EINVAL; + } + + return 0; +} + +static int cs42l56_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, CS42L56_CLKCTL_1, + CS42L56_MCLK_DIS_MASK, 0); + snd_soc_component_update_bits(component, CS42L56_PWRCTL_1, + CS42L56_PDN_ALL_MASK, 0); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + regcache_cache_only(cs42l56->regmap, false); + regcache_sync(cs42l56->regmap); + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + if (ret != 0) { + dev_err(cs42l56->dev, + "Failed to enable regulators: %d\n", + ret); + return ret; + } + } + snd_soc_component_update_bits(component, CS42L56_PWRCTL_1, + CS42L56_PDN_ALL_MASK, 1); + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, CS42L56_PWRCTL_1, + CS42L56_PDN_ALL_MASK, 1); + snd_soc_component_update_bits(component, CS42L56_CLKCTL_1, + CS42L56_MCLK_DIS_MASK, 1); + regcache_cache_only(cs42l56->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + break; + } + + return 0; +} + +#define CS42L56_RATES (SNDRV_PCM_RATE_8000_48000) + +#define CS42L56_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + + +static const struct snd_soc_dai_ops cs42l56_ops = { + .hw_params = cs42l56_pcm_hw_params, + .mute_stream = cs42l56_mute, + .set_fmt = cs42l56_set_dai_fmt, + .set_sysclk = cs42l56_set_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver cs42l56_dai = { + .name = "cs42l56", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L56_RATES, + .formats = CS42L56_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS42L56_RATES, + .formats = CS42L56_FORMATS, + }, + .ops = &cs42l56_ops, +}; + +static int beep_freq[] = { + 261, 522, 585, 667, 706, 774, 889, 1000, + 1043, 1200, 1333, 1412, 1600, 1714, 2000, 2182 +}; + +static void cs42l56_beep_work(struct work_struct *work) +{ + struct cs42l56_private *cs42l56 = + container_of(work, struct cs42l56_private, beep_work); + struct snd_soc_component *component = cs42l56->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int i; + int val = 0; + int best = 0; + + if (cs42l56->beep_rate) { + for (i = 0; i < ARRAY_SIZE(beep_freq); i++) { + if (abs(cs42l56->beep_rate - beep_freq[i]) < + abs(cs42l56->beep_rate - beep_freq[best])) + best = i; + } + + dev_dbg(component->dev, "Set beep rate %dHz for requested %dHz\n", + beep_freq[best], cs42l56->beep_rate); + + val = (best << CS42L56_BEEP_RATE_SHIFT); + + snd_soc_dapm_enable_pin(dapm, "Beep"); + } else { + dev_dbg(component->dev, "Disabling beep\n"); + snd_soc_dapm_disable_pin(dapm, "Beep"); + } + + snd_soc_component_update_bits(component, CS42L56_BEEP_FREQ_ONTIME, + CS42L56_BEEP_FREQ_MASK, val); + + snd_soc_dapm_sync(dapm); +} + +/* For usability define a way of injecting beep events for the device - + * many systems will not have a keyboard. + */ +static int cs42l56_beep_event(struct input_dev *dev, unsigned int type, + unsigned int code, int hz) +{ + struct snd_soc_component *component = input_get_drvdata(dev); + struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "Beep event %x %x\n", code, hz); + + switch (code) { + case SND_BELL: + if (hz) + hz = 261; + break; + case SND_TONE: + break; + default: + return -1; + } + + /* Kick the beep from a workqueue */ + cs42l56->beep_rate = hz; + schedule_work(&cs42l56->beep_work); + return 0; +} + +static ssize_t beep_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs42l56_private *cs42l56 = dev_get_drvdata(dev); + long int time; + int ret; + + ret = kstrtol(buf, 10, &time); + if (ret != 0) + return ret; + + input_event(cs42l56->beep, EV_SND, SND_TONE, time); + + return count; +} + +static DEVICE_ATTR_WO(beep); + +static void cs42l56_init_beep(struct snd_soc_component *component) +{ + struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component); + int ret; + + cs42l56->beep = devm_input_allocate_device(component->dev); + if (!cs42l56->beep) { + dev_err(component->dev, "Failed to allocate beep device\n"); + return; + } + + INIT_WORK(&cs42l56->beep_work, cs42l56_beep_work); + cs42l56->beep_rate = 0; + + cs42l56->beep->name = "CS42L56 Beep Generator"; + cs42l56->beep->phys = dev_name(component->dev); + cs42l56->beep->id.bustype = BUS_I2C; + + cs42l56->beep->evbit[0] = BIT_MASK(EV_SND); + cs42l56->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + cs42l56->beep->event = cs42l56_beep_event; + cs42l56->beep->dev.parent = component->dev; + input_set_drvdata(cs42l56->beep, component); + + ret = input_register_device(cs42l56->beep); + if (ret != 0) { + cs42l56->beep = NULL; + dev_err(component->dev, "Failed to register beep device\n"); + } + + ret = device_create_file(component->dev, &dev_attr_beep); + if (ret != 0) { + dev_err(component->dev, "Failed to create keyclick file: %d\n", + ret); + } +} + +static void cs42l56_free_beep(struct snd_soc_component *component) +{ + struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component); + + device_remove_file(component->dev, &dev_attr_beep); + cancel_work_sync(&cs42l56->beep_work); + cs42l56->beep = NULL; + + snd_soc_component_update_bits(component, CS42L56_BEEP_TONE_CFG, + CS42L56_BEEP_EN_MASK, 0); +} + +static int cs42l56_probe(struct snd_soc_component *component) +{ + cs42l56_init_beep(component); + + return 0; +} + +static void cs42l56_remove(struct snd_soc_component *component) +{ + cs42l56_free_beep(component); +} + +static const struct snd_soc_component_driver soc_component_dev_cs42l56 = { + .probe = cs42l56_probe, + .remove = cs42l56_remove, + .set_bias_level = cs42l56_set_bias_level, + .controls = cs42l56_snd_controls, + .num_controls = ARRAY_SIZE(cs42l56_snd_controls), + .dapm_widgets = cs42l56_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l56_dapm_widgets), + .dapm_routes = cs42l56_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l56_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config cs42l56_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS42L56_MAX_REGISTER, + .reg_defaults = cs42l56_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs42l56_reg_defaults), + .readable_reg = cs42l56_readable_register, + .volatile_reg = cs42l56_volatile_register, + .cache_type = REGCACHE_MAPLE, +}; + +static int cs42l56_handle_of_data(struct i2c_client *i2c_client, + struct cs42l56_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + u32 val32; + + if (of_property_read_bool(np, "cirrus,ain1a-reference-cfg")) + pdata->ain1a_ref_cfg = true; + + if (of_property_read_bool(np, "cirrus,ain2a-reference-cfg")) + pdata->ain2a_ref_cfg = true; + + if (of_property_read_bool(np, "cirrus,ain1b-reference-cfg")) + pdata->ain1b_ref_cfg = true; + + if (of_property_read_bool(np, "cirrus,ain2b-reference-cfg")) + pdata->ain2b_ref_cfg = true; + + if (of_property_read_u32(np, "cirrus,micbias-lvl", &val32) >= 0) + pdata->micbias_lvl = val32; + + if (of_property_read_u32(np, "cirrus,chgfreq-divisor", &val32) >= 0) + pdata->chgfreq = val32; + + if (of_property_read_u32(np, "cirrus,adaptive-pwr-cfg", &val32) >= 0) + pdata->adaptive_pwr = val32; + + if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0) + pdata->hpfa_freq = val32; + + if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0) + pdata->hpfb_freq = val32; + + pdata->gpio_nreset = of_get_named_gpio(np, "cirrus,gpio-nreset", 0); + + return 0; +} + +static int cs42l56_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs42l56_private *cs42l56; + struct cs42l56_platform_data *pdata = + dev_get_platdata(&i2c_client->dev); + int ret, i; + unsigned int devid; + unsigned int alpha_rev, metal_rev; + unsigned int reg; + + cs42l56 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l56), GFP_KERNEL); + if (cs42l56 == NULL) + return -ENOMEM; + cs42l56->dev = &i2c_client->dev; + + cs42l56->regmap = devm_regmap_init_i2c(i2c_client, &cs42l56_regmap); + if (IS_ERR(cs42l56->regmap)) { + ret = PTR_ERR(cs42l56->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + if (pdata) { + cs42l56->pdata = *pdata; + } else { + if (i2c_client->dev.of_node) { + ret = cs42l56_handle_of_data(i2c_client, + &cs42l56->pdata); + if (ret != 0) + return ret; + } + } + + if (cs42l56->pdata.gpio_nreset) { + ret = gpio_request_one(cs42l56->pdata.gpio_nreset, + GPIOF_OUT_INIT_HIGH, "CS42L56 /RST"); + if (ret < 0) { + dev_err(&i2c_client->dev, + "Failed to request /RST %d: %d\n", + cs42l56->pdata.gpio_nreset, ret); + return ret; + } + gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0); + gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1); + } + + + i2c_set_clientdata(i2c_client, cs42l56); + + for (i = 0; i < ARRAY_SIZE(cs42l56->supplies); i++) + cs42l56->supplies[i].supply = cs42l56_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c_client->dev, + ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + if (ret != 0) { + dev_err(&i2c_client->dev, + "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, ®); + if (ret) { + dev_err(&i2c_client->dev, "Failed to read chip ID: %d\n", ret); + goto err_enable; + } + + devid = reg & CS42L56_CHIP_ID_MASK; + if (devid != CS42L56_DEVID) { + dev_err(&i2c_client->dev, + "CS42L56 Device ID (%X). Expected %X\n", + devid, CS42L56_DEVID); + ret = -EINVAL; + goto err_enable; + } + alpha_rev = reg & CS42L56_AREV_MASK; + metal_rev = reg & CS42L56_MTLREV_MASK; + + dev_info(&i2c_client->dev, "Cirrus Logic CS42L56 "); + dev_info(&i2c_client->dev, "Alpha Rev %X Metal Rev %X\n", + alpha_rev, metal_rev); + + if (cs42l56->pdata.ain1a_ref_cfg) + regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, + CS42L56_AIN1A_REF_MASK, + CS42L56_AIN1A_REF_MASK); + + if (cs42l56->pdata.ain1b_ref_cfg) + regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, + CS42L56_AIN1B_REF_MASK, + CS42L56_AIN1B_REF_MASK); + + if (cs42l56->pdata.ain2a_ref_cfg) + regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, + CS42L56_AIN2A_REF_MASK, + CS42L56_AIN2A_REF_MASK); + + if (cs42l56->pdata.ain2b_ref_cfg) + regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX, + CS42L56_AIN2B_REF_MASK, + CS42L56_AIN2B_REF_MASK); + + if (cs42l56->pdata.micbias_lvl) + regmap_update_bits(cs42l56->regmap, CS42L56_GAIN_BIAS_CTL, + CS42L56_MIC_BIAS_MASK, + cs42l56->pdata.micbias_lvl); + + if (cs42l56->pdata.chgfreq) + regmap_update_bits(cs42l56->regmap, CS42L56_CLASSH_CTL, + CS42L56_CHRG_FREQ_MASK, + cs42l56->pdata.chgfreq); + + if (cs42l56->pdata.hpfb_freq) + regmap_update_bits(cs42l56->regmap, CS42L56_HPF_CTL, + CS42L56_HPFB_FREQ_MASK, + cs42l56->pdata.hpfb_freq); + + if (cs42l56->pdata.hpfa_freq) + regmap_update_bits(cs42l56->regmap, CS42L56_HPF_CTL, + CS42L56_HPFA_FREQ_MASK, + cs42l56->pdata.hpfa_freq); + + if (cs42l56->pdata.adaptive_pwr) + regmap_update_bits(cs42l56->regmap, CS42L56_CLASSH_CTL, + CS42L56_ADAPT_PWR_MASK, + cs42l56->pdata.adaptive_pwr); + + ret = devm_snd_soc_register_component(&i2c_client->dev, + &soc_component_dev_cs42l56, &cs42l56_dai, 1); + if (ret < 0) + goto err_enable; + + return 0; + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); + return ret; +} + +static void cs42l56_i2c_remove(struct i2c_client *client) +{ + struct cs42l56_private *cs42l56 = i2c_get_clientdata(client); + + regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies), + cs42l56->supplies); +} + +static const struct of_device_id cs42l56_of_match[] = { + { .compatible = "cirrus,cs42l56", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs42l56_of_match); + + +static const struct i2c_device_id cs42l56_id[] = { + { "cs42l56", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs42l56_id); + +static struct i2c_driver cs42l56_i2c_driver = { + .driver = { + .name = "cs42l56", + .of_match_table = cs42l56_of_match, + }, + .id_table = cs42l56_id, + .probe = cs42l56_i2c_probe, + .remove = cs42l56_i2c_remove, +}; + +module_i2c_driver(cs42l56_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L56 driver"); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l56.h b/sound/soc/codecs/cs42l56.h new file mode 100644 index 0000000000..62a8c3cb1a --- /dev/null +++ b/sound/soc/codecs/cs42l56.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * cs42l52.h -- CS42L56 ALSA SoC audio driver + * + * Copyright 2014 CirrusLogic, Inc. + * + * Author: Brian Austin + */ + +#ifndef __CS42L56_H__ +#define __CS42L56_H__ + +#define CS42L56_CHIP_ID_1 0x01 +#define CS42L56_CHIP_ID_2 0x02 +#define CS42L56_PWRCTL_1 0x03 +#define CS42L56_PWRCTL_2 0x04 +#define CS42L56_CLKCTL_1 0x05 +#define CS42L56_CLKCTL_2 0x06 +#define CS42L56_SERIAL_FMT 0x07 +#define CS42L56_CLASSH_CTL 0x08 +#define CS42L56_MISC_CTL 0x09 +#define CS42L56_INT_STATUS 0x0a +#define CS42L56_PLAYBACK_CTL 0x0b +#define CS42L56_DSP_MUTE_CTL 0x0c +#define CS42L56_ADCA_MIX_VOLUME 0x0d +#define CS42L56_ADCB_MIX_VOLUME 0x0e +#define CS42L56_PCMA_MIX_VOLUME 0x0f +#define CS42L56_PCMB_MIX_VOLUME 0x10 +#define CS42L56_ANAINPUT_ADV_VOLUME 0x11 +#define CS42L56_DIGINPUT_ADV_VOLUME 0x12 +#define CS42L56_MASTER_A_VOLUME 0x13 +#define CS42L56_MASTER_B_VOLUME 0x14 +#define CS42L56_BEEP_FREQ_ONTIME 0x15 +#define CS42L56_BEEP_FREQ_OFFTIME 0x16 +#define CS42L56_BEEP_TONE_CFG 0x17 +#define CS42L56_TONE_CTL 0x18 +#define CS42L56_CHAN_MIX_SWAP 0x19 +#define CS42L56_AIN_REFCFG_ADC_MUX 0x1a +#define CS42L56_HPF_CTL 0x1b +#define CS42L56_MISC_ADC_CTL 0x1c +#define CS42L56_GAIN_BIAS_CTL 0x1d +#define CS42L56_PGAA_MUX_VOLUME 0x1e +#define CS42L56_PGAB_MUX_VOLUME 0x1f +#define CS42L56_ADCA_ATTENUATOR 0x20 +#define CS42L56_ADCB_ATTENUATOR 0x21 +#define CS42L56_ALC_EN_ATTACK_RATE 0x22 +#define CS42L56_ALC_RELEASE_RATE 0x23 +#define CS42L56_ALC_THRESHOLD 0x24 +#define CS42L56_NOISE_GATE_CTL 0x25 +#define CS42L56_ALC_LIM_SFT_ZC 0x26 +#define CS42L56_AMUTE_HPLO_MUX 0x27 +#define CS42L56_HPA_VOLUME 0x28 +#define CS42L56_HPB_VOLUME 0x29 +#define CS42L56_LOA_VOLUME 0x2a +#define CS42L56_LOB_VOLUME 0x2b +#define CS42L56_LIM_THRESHOLD_CTL 0x2c +#define CS42L56_LIM_CTL_RELEASE_RATE 0x2d +#define CS42L56_LIM_ATTACK_RATE 0x2e + +/* Device ID and Rev ID Masks */ +#define CS42L56_DEVID 0x56 +#define CS42L56_CHIP_ID_MASK 0xff +#define CS42L56_AREV_MASK 0x1c +#define CS42L56_MTLREV_MASK 0x03 + +/* Power bit masks */ +#define CS42L56_PDN_ALL_MASK 0x01 +#define CS42L56_PDN_ADCA_MASK 0x02 +#define CS42L56_PDN_ADCB_MASK 0x04 +#define CS42L56_PDN_CHRG_MASK 0x08 +#define CS42L56_PDN_BIAS_MASK 0x10 +#define CS42L56_PDN_VBUF_MASK 0x20 +#define CS42L56_PDN_LOA_MASK 0x03 +#define CS42L56_PDN_LOB_MASK 0x0c +#define CS42L56_PDN_HPA_MASK 0x30 +#define CS42L56_PDN_HPB_MASK 0xc0 + +/* serial port and clk masks */ +#define CS42L56_MASTER_MODE 0x40 +#define CS42L56_SLAVE_MODE 0 +#define CS42L56_MS_MODE_MASK 0x40 +#define CS42L56_SCLK_INV 0x20 +#define CS42L56_SCLK_INV_MASK 0x20 +#define CS42L56_SCLK_MCLK_MASK 0x18 +#define CS42L56_MCLK_PREDIV 0x04 +#define CS42L56_MCLK_PREDIV_MASK 0x04 +#define CS42L56_MCLK_DIV2 0x02 +#define CS42L56_MCLK_DIV2_MASK 0x02 +#define CS42L56_MCLK_DIS_MASK 0x01 +#define CS42L56_CLK_AUTO_MASK 0x20 +#define CS42L56_CLK_RATIO_MASK 0x1f +#define CS42L56_DIG_FMT_I2S 0 +#define CS42L56_DIG_FMT_LEFT_J 0x08 +#define CS42L56_DIG_FMT_MASK 0x08 + +/* Class H and misc ctl masks */ +#define CS42L56_ADAPT_PWR_MASK 0xc0 +#define CS42L56_CHRG_FREQ_MASK 0x0f +#define CS42L56_DIG_MUX_MASK 0x80 +#define CS42L56_ANLGSFT_MASK 0x10 +#define CS42L56_ANLGZC_MASK 0x08 +#define CS42L56_DIGSFT_MASK 0x04 +#define CS42L56_FREEZE_MASK 0x01 +#define CS42L56_MIC_BIAS_MASK 0x03 +#define CS42L56_HPFA_FREQ_MASK 0x03 +#define CS42L56_HPFB_FREQ_MASK 0xc0 +#define CS42L56_AIN1A_REF_MASK 0x10 +#define CS42L56_AIN2A_REF_MASK 0x40 +#define CS42L56_AIN1B_REF_MASK 0x20 +#define CS42L56_AIN2B_REF_MASK 0x80 + +/* Playback Capture ctl masks */ +#define CS42L56_PDN_DSP_MASK 0x80 +#define CS42L56_DEEMPH_MASK 0x40 +#define CS42L56_PLYBCK_GANG_MASK 0x10 +#define CS42L56_PCM_INV_MASK 0x0c +#define CS42L56_MUTE_ALL 0xff +#define CS42L56_UNMUTE 0 +#define CS42L56_ADCAMIX_MUTE_MASK 0x40 +#define CS42L56_ADCBMIX_MUTE_MASK 0x80 +#define CS42L56_PCMAMIX_MUTE_MASK 0x10 +#define CS42L56_PCMBMIX_MUTE_MASK 0x20 +#define CS42L56_MSTB_MUTE_MASK 0x02 +#define CS42L56_MSTA_MUTE_MASK 0x01 +#define CS42L56_ADCA_MUTE_MASK 0x01 +#define CS42L56_ADCB_MUTE_MASK 0x02 +#define CS42L56_HP_MUTE_MASK 0x80 +#define CS42L56_LO_MUTE_MASK 0x80 + +/* Beep masks */ +#define CS42L56_BEEP_FREQ_MASK 0xf0 +#define CS42L56_BEEP_ONTIME_MASK 0x0f +#define CS42L56_BEEP_OFFTIME_MASK 0xe0 +#define CS42L56_BEEP_CFG_MASK 0xc0 +#define CS42L56_BEEP_TREBCF_MASK 0x18 +#define CS42L56_BEEP_BASSCF_MASK 0x06 +#define CS42L56_BEEP_TCEN_MASK 0x01 +#define CS42L56_BEEP_RATE_SHIFT 4 +#define CS42L56_BEEP_EN_MASK 0x3f + + +/* Supported MCLKS */ +#define CS42L56_MCLK_5P6448MHZ 5644800 +#define CS42L56_MCLK_6MHZ 6000000 +#define CS42L56_MCLK_6P144MHZ 6144000 +#define CS42L56_MCLK_11P2896MHZ 11289600 +#define CS42L56_MCLK_12MHZ 12000000 +#define CS42L56_MCLK_12P288MHZ 12288000 +#define CS42L56_MCLK_22P5792MHZ 22579200 +#define CS42L56_MCLK_24MHZ 24000000 +#define CS42L56_MCLK_24P576MHZ 24576000 + +/* Clock ratios */ +#define CS42L56_MCLK_LRCLK_128 0x08 +#define CS42L56_MCLK_LRCLK_125 0x09 +#define CS42L56_MCLK_LRCLK_136 0x0b +#define CS42L56_MCLK_LRCLK_192 0x0c +#define CS42L56_MCLK_LRCLK_187P5 0x0d +#define CS42L56_MCLK_LRCLK_256 0x10 +#define CS42L56_MCLK_LRCLK_250 0x11 +#define CS42L56_MCLK_LRCLK_272 0x13 +#define CS42L56_MCLK_LRCLK_384 0x14 +#define CS42L56_MCLK_LRCLK_375 0x15 +#define CS42L56_MCLK_LRCLK_512 0x18 +#define CS42L56_MCLK_LRCLK_500 0x19 +#define CS42L56_MCLK_LRCLK_544 0x1b +#define CS42L56_MCLK_LRCLK_750 0x1c +#define CS42L56_MCLK_LRCLK_768 0x1d + + +#define CS42L56_MAX_REGISTER 0x34 + +#endif diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c new file mode 100644 index 0000000000..6ab67d196d --- /dev/null +++ b/sound/soc/codecs/cs42l73.c @@ -0,0 +1,1396 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l73.c -- CS42L73 ALSA Soc Audio driver + * + * Copyright 2011 Cirrus Logic, Inc. + * + * Authors: Georgi Vlaev, Nucleus Systems Ltd, + * Brian Austin, Cirrus Logic Inc, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs42l73.h" +#include "cirrus_legacy.h" + +struct sp_config { + u8 spc, mmcc, spfs; + u32 srate; +}; +struct cs42l73_private { + struct cs42l73_platform_data pdata; + struct sp_config config[3]; + struct regmap *regmap; + u32 sysclk; + u8 mclksel; + u32 mclk; + int shutdwn_delay; +}; + +static const struct reg_default cs42l73_reg_defaults[] = { + { 6, 0xF1 }, /* r06 - Power Ctl 1 */ + { 7, 0xDF }, /* r07 - Power Ctl 2 */ + { 8, 0x3F }, /* r08 - Power Ctl 3 */ + { 9, 0x50 }, /* r09 - Charge Pump Freq */ + { 10, 0x53 }, /* r0A - Output Load MicBias Short Detect */ + { 11, 0x00 }, /* r0B - DMIC Master Clock Ctl */ + { 12, 0x00 }, /* r0C - Aux PCM Ctl */ + { 13, 0x15 }, /* r0D - Aux PCM Master Clock Ctl */ + { 14, 0x00 }, /* r0E - Audio PCM Ctl */ + { 15, 0x15 }, /* r0F - Audio PCM Master Clock Ctl */ + { 16, 0x00 }, /* r10 - Voice PCM Ctl */ + { 17, 0x15 }, /* r11 - Voice PCM Master Clock Ctl */ + { 18, 0x00 }, /* r12 - Voice/Aux Sample Rate */ + { 19, 0x06 }, /* r13 - Misc I/O Path Ctl */ + { 20, 0x00 }, /* r14 - ADC Input Path Ctl */ + { 21, 0x00 }, /* r15 - MICA Preamp, PGA Volume */ + { 22, 0x00 }, /* r16 - MICB Preamp, PGA Volume */ + { 23, 0x00 }, /* r17 - Input Path A Digital Volume */ + { 24, 0x00 }, /* r18 - Input Path B Digital Volume */ + { 25, 0x00 }, /* r19 - Playback Digital Ctl */ + { 26, 0x00 }, /* r1A - HP/LO Left Digital Volume */ + { 27, 0x00 }, /* r1B - HP/LO Right Digital Volume */ + { 28, 0x00 }, /* r1C - Speakerphone Digital Volume */ + { 29, 0x00 }, /* r1D - Ear/SPKLO Digital Volume */ + { 30, 0x00 }, /* r1E - HP Left Analog Volume */ + { 31, 0x00 }, /* r1F - HP Right Analog Volume */ + { 32, 0x00 }, /* r20 - LO Left Analog Volume */ + { 33, 0x00 }, /* r21 - LO Right Analog Volume */ + { 34, 0x00 }, /* r22 - Stereo Input Path Advisory Volume */ + { 35, 0x00 }, /* r23 - Aux PCM Input Advisory Volume */ + { 36, 0x00 }, /* r24 - Audio PCM Input Advisory Volume */ + { 37, 0x00 }, /* r25 - Voice PCM Input Advisory Volume */ + { 38, 0x00 }, /* r26 - Limiter Attack Rate HP/LO */ + { 39, 0x7F }, /* r27 - Limter Ctl, Release Rate HP/LO */ + { 40, 0x00 }, /* r28 - Limter Threshold HP/LO */ + { 41, 0x00 }, /* r29 - Limiter Attack Rate Speakerphone */ + { 42, 0x3F }, /* r2A - Limter Ctl, Release Rate Speakerphone */ + { 43, 0x00 }, /* r2B - Limter Threshold Speakerphone */ + { 44, 0x00 }, /* r2C - Limiter Attack Rate Ear/SPKLO */ + { 45, 0x3F }, /* r2D - Limter Ctl, Release Rate Ear/SPKLO */ + { 46, 0x00 }, /* r2E - Limter Threshold Ear/SPKLO */ + { 47, 0x00 }, /* r2F - ALC Enable, Attack Rate Left/Right */ + { 48, 0x3F }, /* r30 - ALC Release Rate Left/Right */ + { 49, 0x00 }, /* r31 - ALC Threshold Left/Right */ + { 50, 0x00 }, /* r32 - Noise Gate Ctl Left/Right */ + { 51, 0x00 }, /* r33 - ALC/NG Misc Ctl */ + { 52, 0x18 }, /* r34 - Mixer Ctl */ + { 53, 0x3F }, /* r35 - HP/LO Left Mixer Input Path Volume */ + { 54, 0x3F }, /* r36 - HP/LO Right Mixer Input Path Volume */ + { 55, 0x3F }, /* r37 - HP/LO Left Mixer Aux PCM Volume */ + { 56, 0x3F }, /* r38 - HP/LO Right Mixer Aux PCM Volume */ + { 57, 0x3F }, /* r39 - HP/LO Left Mixer Audio PCM Volume */ + { 58, 0x3F }, /* r3A - HP/LO Right Mixer Audio PCM Volume */ + { 59, 0x3F }, /* r3B - HP/LO Left Mixer Voice PCM Mono Volume */ + { 60, 0x3F }, /* r3C - HP/LO Right Mixer Voice PCM Mono Volume */ + { 61, 0x3F }, /* r3D - Aux PCM Left Mixer Input Path Volume */ + { 62, 0x3F }, /* r3E - Aux PCM Right Mixer Input Path Volume */ + { 63, 0x3F }, /* r3F - Aux PCM Left Mixer Volume */ + { 64, 0x3F }, /* r40 - Aux PCM Left Mixer Volume */ + { 65, 0x3F }, /* r41 - Aux PCM Left Mixer Audio PCM L Volume */ + { 66, 0x3F }, /* r42 - Aux PCM Right Mixer Audio PCM R Volume */ + { 67, 0x3F }, /* r43 - Aux PCM Left Mixer Voice PCM Volume */ + { 68, 0x3F }, /* r44 - Aux PCM Right Mixer Voice PCM Volume */ + { 69, 0x3F }, /* r45 - Audio PCM Left Input Path Volume */ + { 70, 0x3F }, /* r46 - Audio PCM Right Input Path Volume */ + { 71, 0x3F }, /* r47 - Audio PCM Left Mixer Aux PCM L Volume */ + { 72, 0x3F }, /* r48 - Audio PCM Right Mixer Aux PCM R Volume */ + { 73, 0x3F }, /* r49 - Audio PCM Left Mixer Volume */ + { 74, 0x3F }, /* r4A - Audio PCM Right Mixer Volume */ + { 75, 0x3F }, /* r4B - Audio PCM Left Mixer Voice PCM Volume */ + { 76, 0x3F }, /* r4C - Audio PCM Right Mixer Voice PCM Volume */ + { 77, 0x3F }, /* r4D - Voice PCM Left Input Path Volume */ + { 78, 0x3F }, /* r4E - Voice PCM Right Input Path Volume */ + { 79, 0x3F }, /* r4F - Voice PCM Left Mixer Aux PCM L Volume */ + { 80, 0x3F }, /* r50 - Voice PCM Right Mixer Aux PCM R Volume */ + { 81, 0x3F }, /* r51 - Voice PCM Left Mixer Audio PCM L Volume */ + { 82, 0x3F }, /* r52 - Voice PCM Right Mixer Audio PCM R Volume */ + { 83, 0x3F }, /* r53 - Voice PCM Left Mixer Voice PCM Volume */ + { 84, 0x3F }, /* r54 - Voice PCM Right Mixer Voice PCM Volume */ + { 85, 0xAA }, /* r55 - Mono Mixer Ctl */ + { 86, 0x3F }, /* r56 - SPK Mono Mixer Input Path Volume */ + { 87, 0x3F }, /* r57 - SPK Mono Mixer Aux PCM Mono/L/R Volume */ + { 88, 0x3F }, /* r58 - SPK Mono Mixer Audio PCM Mono/L/R Volume */ + { 89, 0x3F }, /* r59 - SPK Mono Mixer Voice PCM Mono Volume */ + { 90, 0x3F }, /* r5A - SPKLO Mono Mixer Input Path Mono Volume */ + { 91, 0x3F }, /* r5B - SPKLO Mono Mixer Aux Mono/L/R Volume */ + { 92, 0x3F }, /* r5C - SPKLO Mono Mixer Audio Mono/L/R Volume */ + { 93, 0x3F }, /* r5D - SPKLO Mono Mixer Voice Mono Volume */ + { 94, 0x00 }, /* r5E - Interrupt Mask 1 */ + { 95, 0x00 }, /* r5F - Interrupt Mask 2 */ +}; + +static bool cs42l73_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L73_IS1: + case CS42L73_IS2: + return true; + default: + return false; + } +} + +static bool cs42l73_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42L73_DEVID_AB ... CS42L73_DEVID_E: + case CS42L73_REVID ... CS42L73_IM2: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_RANGE(hpaloa_tlv, + 0, 13, TLV_DB_SCALE_ITEM(-7600, 200, 0), + 14, 75, TLV_DB_SCALE_ITEM(-4900, 100, 0) +); + +static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2500, 0); + +static DECLARE_TLV_DB_SCALE(hl_tlv, -10200, 50, 0); + +static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0); + +static DECLARE_TLV_DB_SCALE(micpga_tlv, -600, 50, 0); + +static const DECLARE_TLV_DB_RANGE(limiter_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), + 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0) +); + +static const DECLARE_TLV_DB_SCALE(attn_tlv, -6300, 100, 1); + +static const char * const cs42l73_pgaa_text[] = { "Line A", "Mic 1" }; +static const char * const cs42l73_pgab_text[] = { "Line B", "Mic 2" }; + +static SOC_ENUM_SINGLE_DECL(pgaa_enum, + CS42L73_ADCIPC, 3, + cs42l73_pgaa_text); + +static SOC_ENUM_SINGLE_DECL(pgab_enum, + CS42L73_ADCIPC, 7, + cs42l73_pgab_text); + +static const struct snd_kcontrol_new pgaa_mux = + SOC_DAPM_ENUM("Left Analog Input Capture Mux", pgaa_enum); + +static const struct snd_kcontrol_new pgab_mux = + SOC_DAPM_ENUM("Right Analog Input Capture Mux", pgab_enum); + +static const struct snd_kcontrol_new input_left_mixer[] = { + SOC_DAPM_SINGLE("ADC Left Input", CS42L73_PWRCTL1, + 5, 1, 1), + SOC_DAPM_SINGLE("DMIC Left Input", CS42L73_PWRCTL1, + 4, 1, 1), +}; + +static const struct snd_kcontrol_new input_right_mixer[] = { + SOC_DAPM_SINGLE("ADC Right Input", CS42L73_PWRCTL1, + 7, 1, 1), + SOC_DAPM_SINGLE("DMIC Right Input", CS42L73_PWRCTL1, + 6, 1, 1), +}; + +static const char * const cs42l73_ng_delay_text[] = { + "50ms", "100ms", "150ms", "200ms" }; + +static SOC_ENUM_SINGLE_DECL(ng_delay_enum, + CS42L73_NGCAB, 0, + cs42l73_ng_delay_text); + +static const char * const cs42l73_mono_mix_texts[] = { + "Left", "Right", "Mono Mix"}; + +static const unsigned int cs42l73_mono_mix_values[] = { 0, 1, 2 }; + +static const struct soc_enum spk_asp_enum = + SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 6, 3, + ARRAY_SIZE(cs42l73_mono_mix_texts), + cs42l73_mono_mix_texts, + cs42l73_mono_mix_values); + +static const struct snd_kcontrol_new spk_asp_mixer = + SOC_DAPM_ENUM("Route", spk_asp_enum); + +static const struct soc_enum spk_xsp_enum = + SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 4, 3, + ARRAY_SIZE(cs42l73_mono_mix_texts), + cs42l73_mono_mix_texts, + cs42l73_mono_mix_values); + +static const struct snd_kcontrol_new spk_xsp_mixer = + SOC_DAPM_ENUM("Route", spk_xsp_enum); + +static const struct soc_enum esl_asp_enum = + SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 2, 3, + ARRAY_SIZE(cs42l73_mono_mix_texts), + cs42l73_mono_mix_texts, + cs42l73_mono_mix_values); + +static const struct snd_kcontrol_new esl_asp_mixer = + SOC_DAPM_ENUM("Route", esl_asp_enum); + +static const struct soc_enum esl_xsp_enum = + SOC_VALUE_ENUM_SINGLE(CS42L73_MMIXCTL, 0, 3, + ARRAY_SIZE(cs42l73_mono_mix_texts), + cs42l73_mono_mix_texts, + cs42l73_mono_mix_values); + +static const struct snd_kcontrol_new esl_xsp_mixer = + SOC_DAPM_ENUM("Route", esl_xsp_enum); + +static const char * const cs42l73_ip_swap_text[] = { + "Stereo", "Mono A", "Mono B", "Swap A-B"}; + +static SOC_ENUM_SINGLE_DECL(ip_swap_enum, + CS42L73_MIOPC, 6, + cs42l73_ip_swap_text); + +static const char * const cs42l73_spo_mixer_text[] = {"Mono", "Stereo"}; + +static SOC_ENUM_SINGLE_DECL(vsp_output_mux_enum, + CS42L73_MIXERCTL, 5, + cs42l73_spo_mixer_text); + +static SOC_ENUM_SINGLE_DECL(xsp_output_mux_enum, + CS42L73_MIXERCTL, 4, + cs42l73_spo_mixer_text); + +static const struct snd_kcontrol_new hp_amp_ctl = + SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 0, 1, 1); + +static const struct snd_kcontrol_new lo_amp_ctl = + SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 1, 1, 1); + +static const struct snd_kcontrol_new spk_amp_ctl = + SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 2, 1, 1); + +static const struct snd_kcontrol_new spklo_amp_ctl = + SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 4, 1, 1); + +static const struct snd_kcontrol_new ear_amp_ctl = + SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 3, 1, 1); + +static const struct snd_kcontrol_new cs42l73_snd_controls[] = { + SOC_DOUBLE_R_SX_TLV("Headphone Analog Playback Volume", + CS42L73_HPAAVOL, CS42L73_HPBAVOL, 0, + 0x41, 0x4B, hpaloa_tlv), + + SOC_DOUBLE_R_SX_TLV("LineOut Analog Playback Volume", CS42L73_LOAAVOL, + CS42L73_LOBAVOL, 0, 0x41, 0x4B, hpaloa_tlv), + + SOC_DOUBLE_R_SX_TLV("Input PGA Analog Volume", CS42L73_MICAPREPGAAVOL, + CS42L73_MICBPREPGABVOL, 0, 0x34, + 0x24, micpga_tlv), + + SOC_DOUBLE_R("MIC Preamp Switch", CS42L73_MICAPREPGAAVOL, + CS42L73_MICBPREPGABVOL, 6, 1, 1), + + SOC_DOUBLE_R_SX_TLV("Input Path Digital Volume", CS42L73_IPADVOL, + CS42L73_IPBDVOL, 0, 0xA0, 0x6C, ipd_tlv), + + SOC_DOUBLE_R_SX_TLV("HL Digital Playback Volume", + CS42L73_HLADVOL, CS42L73_HLBDVOL, + 0, 0x34, 0xE4, hl_tlv), + + SOC_SINGLE_TLV("ADC A Boost Volume", + CS42L73_ADCIPC, 2, 0x01, 1, adc_boost_tlv), + + SOC_SINGLE_TLV("ADC B Boost Volume", + CS42L73_ADCIPC, 6, 0x01, 1, adc_boost_tlv), + + SOC_SINGLE_SX_TLV("Speakerphone Digital Volume", + CS42L73_SPKDVOL, 0, 0x34, 0xE4, hl_tlv), + + SOC_SINGLE_SX_TLV("Ear Speaker Digital Volume", + CS42L73_ESLDVOL, 0, 0x34, 0xE4, hl_tlv), + + SOC_DOUBLE_R("Headphone Analog Playback Switch", CS42L73_HPAAVOL, + CS42L73_HPBAVOL, 7, 1, 1), + + SOC_DOUBLE_R("LineOut Analog Playback Switch", CS42L73_LOAAVOL, + CS42L73_LOBAVOL, 7, 1, 1), + SOC_DOUBLE("Input Path Digital Switch", CS42L73_ADCIPC, 0, 4, 1, 1), + SOC_DOUBLE("HL Digital Playback Switch", CS42L73_PBDC, 0, + 1, 1, 1), + SOC_SINGLE("Speakerphone Digital Playback Switch", CS42L73_PBDC, 2, 1, + 1), + SOC_SINGLE("Ear Speaker Digital Playback Switch", CS42L73_PBDC, 3, 1, + 1), + + SOC_SINGLE("PGA Soft-Ramp Switch", CS42L73_MIOPC, 3, 1, 0), + SOC_SINGLE("Analog Zero Cross Switch", CS42L73_MIOPC, 2, 1, 0), + SOC_SINGLE("Digital Soft-Ramp Switch", CS42L73_MIOPC, 1, 1, 0), + SOC_SINGLE("Analog Output Soft-Ramp Switch", CS42L73_MIOPC, 0, 1, 0), + + SOC_DOUBLE("ADC Signal Polarity Switch", CS42L73_ADCIPC, 1, 5, 1, + 0), + + SOC_SINGLE("HL Limiter Attack Rate", CS42L73_LIMARATEHL, 0, 0x3F, + 0), + SOC_SINGLE("HL Limiter Release Rate", CS42L73_LIMRRATEHL, 0, + 0x3F, 0), + + + SOC_SINGLE("HL Limiter Switch", CS42L73_LIMRRATEHL, 7, 1, 0), + SOC_SINGLE("HL Limiter All Channels Switch", CS42L73_LIMRRATEHL, 6, 1, + 0), + + SOC_SINGLE_TLV("HL Limiter Max Threshold Volume", CS42L73_LMAXHL, 5, 7, + 1, limiter_tlv), + + SOC_SINGLE_TLV("HL Limiter Cushion Volume", CS42L73_LMAXHL, 2, 7, 1, + limiter_tlv), + + SOC_SINGLE("SPK Limiter Attack Rate Volume", CS42L73_LIMARATESPK, 0, + 0x3F, 0), + SOC_SINGLE("SPK Limiter Release Rate Volume", CS42L73_LIMRRATESPK, 0, + 0x3F, 0), + SOC_SINGLE("SPK Limiter Switch", CS42L73_LIMRRATESPK, 7, 1, 0), + SOC_SINGLE("SPK Limiter All Channels Switch", CS42L73_LIMRRATESPK, + 6, 1, 0), + SOC_SINGLE_TLV("SPK Limiter Max Threshold Volume", CS42L73_LMAXSPK, 5, + 7, 1, limiter_tlv), + + SOC_SINGLE_TLV("SPK Limiter Cushion Volume", CS42L73_LMAXSPK, 2, 7, 1, + limiter_tlv), + + SOC_SINGLE("ESL Limiter Attack Rate Volume", CS42L73_LIMARATEESL, 0, + 0x3F, 0), + SOC_SINGLE("ESL Limiter Release Rate Volume", CS42L73_LIMRRATEESL, 0, + 0x3F, 0), + SOC_SINGLE("ESL Limiter Switch", CS42L73_LIMRRATEESL, 7, 1, 0), + SOC_SINGLE_TLV("ESL Limiter Max Threshold Volume", CS42L73_LMAXESL, 5, + 7, 1, limiter_tlv), + + SOC_SINGLE_TLV("ESL Limiter Cushion Volume", CS42L73_LMAXESL, 2, 7, 1, + limiter_tlv), + + SOC_SINGLE("ALC Attack Rate Volume", CS42L73_ALCARATE, 0, 0x3F, 0), + SOC_SINGLE("ALC Release Rate Volume", CS42L73_ALCRRATE, 0, 0x3F, 0), + SOC_DOUBLE("ALC Switch", CS42L73_ALCARATE, 6, 7, 1, 0), + SOC_SINGLE_TLV("ALC Max Threshold Volume", CS42L73_ALCMINMAX, 5, 7, 0, + limiter_tlv), + SOC_SINGLE_TLV("ALC Min Threshold Volume", CS42L73_ALCMINMAX, 2, 7, 0, + limiter_tlv), + + SOC_DOUBLE("NG Enable Switch", CS42L73_NGCAB, 6, 7, 1, 0), + SOC_SINGLE("NG Boost Switch", CS42L73_NGCAB, 5, 1, 0), + /* + NG Threshold depends on NG_BOOTSAB, which selects + between two threshold scales in decibels. + Set linear values for now .. + */ + SOC_SINGLE("NG Threshold", CS42L73_NGCAB, 2, 7, 0), + SOC_ENUM("NG Delay", ng_delay_enum), + + SOC_DOUBLE_R_TLV("XSP-IP Volume", + CS42L73_XSPAIPAA, CS42L73_XSPBIPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("XSP-XSP Volume", + CS42L73_XSPAXSPAA, CS42L73_XSPBXSPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("XSP-ASP Volume", + CS42L73_XSPAASPAA, CS42L73_XSPAASPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("XSP-VSP Volume", + CS42L73_XSPAVSPMA, CS42L73_XSPBVSPMA, 0, 0x3F, 1, + attn_tlv), + + SOC_DOUBLE_R_TLV("ASP-IP Volume", + CS42L73_ASPAIPAA, CS42L73_ASPBIPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("ASP-XSP Volume", + CS42L73_ASPAXSPAA, CS42L73_ASPBXSPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("ASP-ASP Volume", + CS42L73_ASPAASPAA, CS42L73_ASPBASPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("ASP-VSP Volume", + CS42L73_ASPAVSPMA, CS42L73_ASPBVSPMA, 0, 0x3F, 1, + attn_tlv), + + SOC_DOUBLE_R_TLV("VSP-IP Volume", + CS42L73_VSPAIPAA, CS42L73_VSPBIPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("VSP-XSP Volume", + CS42L73_VSPAXSPAA, CS42L73_VSPBXSPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("VSP-ASP Volume", + CS42L73_VSPAASPAA, CS42L73_VSPBASPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("VSP-VSP Volume", + CS42L73_VSPAVSPMA, CS42L73_VSPBVSPMA, 0, 0x3F, 1, + attn_tlv), + + SOC_DOUBLE_R_TLV("HL-IP Volume", + CS42L73_HLAIPAA, CS42L73_HLBIPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("HL-XSP Volume", + CS42L73_HLAXSPAA, CS42L73_HLBXSPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("HL-ASP Volume", + CS42L73_HLAASPAA, CS42L73_HLBASPBA, 0, 0x3F, 1, + attn_tlv), + SOC_DOUBLE_R_TLV("HL-VSP Volume", + CS42L73_HLAVSPMA, CS42L73_HLBVSPMA, 0, 0x3F, 1, + attn_tlv), + + SOC_SINGLE_TLV("SPK-IP Mono Volume", + CS42L73_SPKMIPMA, 0, 0x3F, 1, attn_tlv), + SOC_SINGLE_TLV("SPK-XSP Mono Volume", + CS42L73_SPKMXSPA, 0, 0x3F, 1, attn_tlv), + SOC_SINGLE_TLV("SPK-ASP Mono Volume", + CS42L73_SPKMASPA, 0, 0x3F, 1, attn_tlv), + SOC_SINGLE_TLV("SPK-VSP Mono Volume", + CS42L73_SPKMVSPMA, 0, 0x3F, 1, attn_tlv), + + SOC_SINGLE_TLV("ESL-IP Mono Volume", + CS42L73_ESLMIPMA, 0, 0x3F, 1, attn_tlv), + SOC_SINGLE_TLV("ESL-XSP Mono Volume", + CS42L73_ESLMXSPA, 0, 0x3F, 1, attn_tlv), + SOC_SINGLE_TLV("ESL-ASP Mono Volume", + CS42L73_ESLMASPA, 0, 0x3F, 1, attn_tlv), + SOC_SINGLE_TLV("ESL-VSP Mono Volume", + CS42L73_ESLMVSPMA, 0, 0x3F, 1, attn_tlv), + + SOC_ENUM("IP Digital Swap/Mono Select", ip_swap_enum), + + SOC_ENUM("VSPOUT Mono/Stereo Select", vsp_output_mux_enum), + SOC_ENUM("XSPOUT Mono/Stereo Select", xsp_output_mux_enum), +}; + +static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l73_private *priv = snd_soc_component_get_drvdata(component); + switch (event) { + case SND_SOC_DAPM_POST_PMD: + /* 150 ms delay between setting PDN and MCLKDIS */ + priv->shutdwn_delay = 150; + break; + default: + pr_err("Invalid event = 0x%x\n", event); + } + return 0; +} + +static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l73_private *priv = snd_soc_component_get_drvdata(component); + switch (event) { + case SND_SOC_DAPM_POST_PMD: + /* 50 ms delay between setting PDN and MCLKDIS */ + if (priv->shutdwn_delay < 50) + priv->shutdwn_delay = 50; + break; + default: + pr_err("Invalid event = 0x%x\n", event); + } + return 0; +} + + +static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs42l73_private *priv = snd_soc_component_get_drvdata(component); + switch (event) { + case SND_SOC_DAPM_POST_PMD: + /* 30 ms delay between setting PDN and MCLKDIS */ + if (priv->shutdwn_delay < 30) + priv->shutdwn_delay = 30; + break; + default: + pr_err("Invalid event = 0x%x\n", event); + } + return 0; +} + +static const struct snd_soc_dapm_widget cs42l73_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMICA"), + SND_SOC_DAPM_INPUT("DMICB"), + SND_SOC_DAPM_INPUT("LINEINA"), + SND_SOC_DAPM_INPUT("LINEINB"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS42L73_PWRCTL2, 6, 1, NULL, 0), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS42L73_PWRCTL2, 7, 1, NULL, 0), + + SND_SOC_DAPM_AIF_OUT("XSPOUTL", NULL, 0, + CS42L73_PWRCTL2, 1, 1), + SND_SOC_DAPM_AIF_OUT("XSPOUTR", NULL, 0, + CS42L73_PWRCTL2, 1, 1), + SND_SOC_DAPM_AIF_OUT("ASPOUTL", NULL, 0, + CS42L73_PWRCTL2, 3, 1), + SND_SOC_DAPM_AIF_OUT("ASPOUTR", NULL, 0, + CS42L73_PWRCTL2, 3, 1), + SND_SOC_DAPM_AIF_OUT("VSPINOUT", NULL, 0, + CS42L73_PWRCTL2, 4, 1), + + SND_SOC_DAPM_PGA("PGA Left", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA Right", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("PGA Left Mux", SND_SOC_NOPM, 0, 0, &pgaa_mux), + SND_SOC_DAPM_MUX("PGA Right Mux", SND_SOC_NOPM, 0, 0, &pgab_mux), + + SND_SOC_DAPM_ADC("ADC Left", NULL, CS42L73_PWRCTL1, 7, 1), + SND_SOC_DAPM_ADC("ADC Right", NULL, CS42L73_PWRCTL1, 5, 1), + SND_SOC_DAPM_ADC("DMIC Left", NULL, CS42L73_PWRCTL1, 6, 1), + SND_SOC_DAPM_ADC("DMIC Right", NULL, CS42L73_PWRCTL1, 4, 1), + + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Left Capture", SND_SOC_NOPM, + 0, 0, input_left_mixer, + ARRAY_SIZE(input_left_mixer)), + + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Right Capture", SND_SOC_NOPM, + 0, 0, input_right_mixer, + ARRAY_SIZE(input_right_mixer)), + + SND_SOC_DAPM_MIXER("ASPL Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ASPR Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("XSPL Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("XSPR Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("VSP Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_AIF_IN("XSPINL", NULL, 0, + CS42L73_PWRCTL2, 0, 1), + SND_SOC_DAPM_AIF_IN("XSPINR", NULL, 0, + CS42L73_PWRCTL2, 0, 1), + SND_SOC_DAPM_AIF_IN("XSPINM", NULL, 0, + CS42L73_PWRCTL2, 0, 1), + + SND_SOC_DAPM_AIF_IN("ASPINL", NULL, 0, + CS42L73_PWRCTL2, 2, 1), + SND_SOC_DAPM_AIF_IN("ASPINR", NULL, 0, + CS42L73_PWRCTL2, 2, 1), + SND_SOC_DAPM_AIF_IN("ASPINM", NULL, 0, + CS42L73_PWRCTL2, 2, 1), + + SND_SOC_DAPM_AIF_IN("VSPINOUT", NULL, 0, + CS42L73_PWRCTL2, 4, 1), + + SND_SOC_DAPM_MIXER("HL Left Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("HL Right Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SPK Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ESL Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("ESL-XSP Mux", SND_SOC_NOPM, + 0, 0, &esl_xsp_mixer), + + SND_SOC_DAPM_MUX("ESL-ASP Mux", SND_SOC_NOPM, + 0, 0, &esl_asp_mixer), + + SND_SOC_DAPM_MUX("SPK-ASP Mux", SND_SOC_NOPM, + 0, 0, &spk_asp_mixer), + + SND_SOC_DAPM_MUX("SPK-XSP Mux", SND_SOC_NOPM, + 0, 0, &spk_xsp_mixer), + + SND_SOC_DAPM_PGA("HL Left DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HL Right DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPK DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ESL DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH_E("HP Amp", CS42L73_PWRCTL3, 0, 1, + &hp_amp_ctl, cs42l73_hp_amp_event, + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SWITCH("LO Amp", CS42L73_PWRCTL3, 1, 1, + &lo_amp_ctl), + SND_SOC_DAPM_SWITCH_E("SPK Amp", CS42L73_PWRCTL3, 2, 1, + &spk_amp_ctl, cs42l73_spklo_spk_amp_event, + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SWITCH_E("EAR Amp", CS42L73_PWRCTL3, 3, 1, + &ear_amp_ctl, cs42l73_ear_amp_event, + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SWITCH_E("SPKLO Amp", CS42L73_PWRCTL3, 4, 1, + &spklo_amp_ctl, cs42l73_spklo_spk_amp_event, + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("HPOUTA"), + SND_SOC_DAPM_OUTPUT("HPOUTB"), + SND_SOC_DAPM_OUTPUT("LINEOUTA"), + SND_SOC_DAPM_OUTPUT("LINEOUTB"), + SND_SOC_DAPM_OUTPUT("EAROUT"), + SND_SOC_DAPM_OUTPUT("SPKOUT"), + SND_SOC_DAPM_OUTPUT("SPKLINEOUT"), +}; + +static const struct snd_soc_dapm_route cs42l73_audio_map[] = { + + /* SPKLO EARSPK Paths */ + {"EAROUT", NULL, "EAR Amp"}, + {"SPKLINEOUT", NULL, "SPKLO Amp"}, + + {"EAR Amp", "Switch", "ESL DAC"}, + {"SPKLO Amp", "Switch", "ESL DAC"}, + + {"ESL DAC", "ESL-ASP Mono Volume", "ESL Mixer"}, + {"ESL DAC", "ESL-XSP Mono Volume", "ESL Mixer"}, + {"ESL DAC", "ESL-VSP Mono Volume", "VSPINOUT"}, + /* Loopback */ + {"ESL DAC", "ESL-IP Mono Volume", "Input Left Capture"}, + {"ESL DAC", "ESL-IP Mono Volume", "Input Right Capture"}, + + {"ESL Mixer", NULL, "ESL-ASP Mux"}, + {"ESL Mixer", NULL, "ESL-XSP Mux"}, + + {"ESL-ASP Mux", "Left", "ASPINL"}, + {"ESL-ASP Mux", "Right", "ASPINR"}, + {"ESL-ASP Mux", "Mono Mix", "ASPINM"}, + + {"ESL-XSP Mux", "Left", "XSPINL"}, + {"ESL-XSP Mux", "Right", "XSPINR"}, + {"ESL-XSP Mux", "Mono Mix", "XSPINM"}, + + /* Speakerphone Paths */ + {"SPKOUT", NULL, "SPK Amp"}, + {"SPK Amp", "Switch", "SPK DAC"}, + + {"SPK DAC", "SPK-ASP Mono Volume", "SPK Mixer"}, + {"SPK DAC", "SPK-XSP Mono Volume", "SPK Mixer"}, + {"SPK DAC", "SPK-VSP Mono Volume", "VSPINOUT"}, + /* Loopback */ + {"SPK DAC", "SPK-IP Mono Volume", "Input Left Capture"}, + {"SPK DAC", "SPK-IP Mono Volume", "Input Right Capture"}, + + {"SPK Mixer", NULL, "SPK-ASP Mux"}, + {"SPK Mixer", NULL, "SPK-XSP Mux"}, + + {"SPK-ASP Mux", "Left", "ASPINL"}, + {"SPK-ASP Mux", "Mono Mix", "ASPINM"}, + {"SPK-ASP Mux", "Right", "ASPINR"}, + + {"SPK-XSP Mux", "Left", "XSPINL"}, + {"SPK-XSP Mux", "Mono Mix", "XSPINM"}, + {"SPK-XSP Mux", "Right", "XSPINR"}, + + /* HP LineOUT Paths */ + {"HPOUTA", NULL, "HP Amp"}, + {"HPOUTB", NULL, "HP Amp"}, + {"LINEOUTA", NULL, "LO Amp"}, + {"LINEOUTB", NULL, "LO Amp"}, + + {"HP Amp", "Switch", "HL Left DAC"}, + {"HP Amp", "Switch", "HL Right DAC"}, + {"LO Amp", "Switch", "HL Left DAC"}, + {"LO Amp", "Switch", "HL Right DAC"}, + + {"HL Left DAC", "HL-XSP Volume", "HL Left Mixer"}, + {"HL Right DAC", "HL-XSP Volume", "HL Right Mixer"}, + {"HL Left DAC", "HL-ASP Volume", "HL Left Mixer"}, + {"HL Right DAC", "HL-ASP Volume", "HL Right Mixer"}, + {"HL Left DAC", "HL-VSP Volume", "HL Left Mixer"}, + {"HL Right DAC", "HL-VSP Volume", "HL Right Mixer"}, + /* Loopback */ + {"HL Left DAC", "HL-IP Volume", "HL Left Mixer"}, + {"HL Right DAC", "HL-IP Volume", "HL Right Mixer"}, + {"HL Left Mixer", NULL, "Input Left Capture"}, + {"HL Right Mixer", NULL, "Input Right Capture"}, + + {"HL Left Mixer", NULL, "ASPINL"}, + {"HL Right Mixer", NULL, "ASPINR"}, + {"HL Left Mixer", NULL, "XSPINL"}, + {"HL Right Mixer", NULL, "XSPINR"}, + {"HL Left Mixer", NULL, "VSPINOUT"}, + {"HL Right Mixer", NULL, "VSPINOUT"}, + + {"ASPINL", NULL, "ASP Playback"}, + {"ASPINM", NULL, "ASP Playback"}, + {"ASPINR", NULL, "ASP Playback"}, + {"XSPINL", NULL, "XSP Playback"}, + {"XSPINM", NULL, "XSP Playback"}, + {"XSPINR", NULL, "XSP Playback"}, + {"VSPINOUT", NULL, "VSP Playback"}, + + /* Capture Paths */ + {"MIC1", NULL, "MIC1 Bias"}, + {"PGA Left Mux", "Mic 1", "MIC1"}, + {"MIC2", NULL, "MIC2 Bias"}, + {"PGA Right Mux", "Mic 2", "MIC2"}, + + {"PGA Left Mux", "Line A", "LINEINA"}, + {"PGA Right Mux", "Line B", "LINEINB"}, + + {"PGA Left", NULL, "PGA Left Mux"}, + {"PGA Right", NULL, "PGA Right Mux"}, + + {"ADC Left", NULL, "PGA Left"}, + {"ADC Right", NULL, "PGA Right"}, + {"DMIC Left", NULL, "DMICA"}, + {"DMIC Right", NULL, "DMICB"}, + + {"Input Left Capture", "ADC Left Input", "ADC Left"}, + {"Input Right Capture", "ADC Right Input", "ADC Right"}, + {"Input Left Capture", "DMIC Left Input", "DMIC Left"}, + {"Input Right Capture", "DMIC Right Input", "DMIC Right"}, + + /* Audio Capture */ + {"ASPL Output Mixer", NULL, "Input Left Capture"}, + {"ASPR Output Mixer", NULL, "Input Right Capture"}, + + {"ASPOUTL", "ASP-IP Volume", "ASPL Output Mixer"}, + {"ASPOUTR", "ASP-IP Volume", "ASPR Output Mixer"}, + + /* Auxillary Capture */ + {"XSPL Output Mixer", NULL, "Input Left Capture"}, + {"XSPR Output Mixer", NULL, "Input Right Capture"}, + + {"XSPOUTL", "XSP-IP Volume", "XSPL Output Mixer"}, + {"XSPOUTR", "XSP-IP Volume", "XSPR Output Mixer"}, + + {"XSPOUTL", NULL, "XSPL Output Mixer"}, + {"XSPOUTR", NULL, "XSPR Output Mixer"}, + + /* Voice Capture */ + {"VSP Output Mixer", NULL, "Input Left Capture"}, + {"VSP Output Mixer", NULL, "Input Right Capture"}, + + {"VSPINOUT", "VSP-IP Volume", "VSP Output Mixer"}, + + {"VSPINOUT", NULL, "VSP Output Mixer"}, + + {"ASP Capture", NULL, "ASPOUTL"}, + {"ASP Capture", NULL, "ASPOUTR"}, + {"XSP Capture", NULL, "XSPOUTL"}, + {"XSP Capture", NULL, "XSPOUTR"}, + {"VSP Capture", NULL, "VSPINOUT"}, +}; + +struct cs42l73_mclk_div { + u32 mclk; + u32 srate; + u8 mmcc; +}; + +static const struct cs42l73_mclk_div cs42l73_mclk_coeffs[] = { + /* MCLK, Sample Rate, xMMCC[5:0] */ + {5644800, 11025, 0x30}, + {5644800, 22050, 0x20}, + {5644800, 44100, 0x10}, + + {6000000, 8000, 0x39}, + {6000000, 11025, 0x33}, + {6000000, 12000, 0x31}, + {6000000, 16000, 0x29}, + {6000000, 22050, 0x23}, + {6000000, 24000, 0x21}, + {6000000, 32000, 0x19}, + {6000000, 44100, 0x13}, + {6000000, 48000, 0x11}, + + {6144000, 8000, 0x38}, + {6144000, 12000, 0x30}, + {6144000, 16000, 0x28}, + {6144000, 24000, 0x20}, + {6144000, 32000, 0x18}, + {6144000, 48000, 0x10}, + + {6500000, 8000, 0x3C}, + {6500000, 11025, 0x35}, + {6500000, 12000, 0x34}, + {6500000, 16000, 0x2C}, + {6500000, 22050, 0x25}, + {6500000, 24000, 0x24}, + {6500000, 32000, 0x1C}, + {6500000, 44100, 0x15}, + {6500000, 48000, 0x14}, + + {6400000, 8000, 0x3E}, + {6400000, 11025, 0x37}, + {6400000, 12000, 0x36}, + {6400000, 16000, 0x2E}, + {6400000, 22050, 0x27}, + {6400000, 24000, 0x26}, + {6400000, 32000, 0x1E}, + {6400000, 44100, 0x17}, + {6400000, 48000, 0x16}, +}; + +struct cs42l73_mclkx_div { + u32 mclkx; + u8 ratio; + u8 mclkdiv; +}; + +static const struct cs42l73_mclkx_div cs42l73_mclkx_coeffs[] = { + {5644800, 1, 0}, /* 5644800 */ + {6000000, 1, 0}, /* 6000000 */ + {6144000, 1, 0}, /* 6144000 */ + {11289600, 2, 2}, /* 5644800 */ + {12288000, 2, 2}, /* 6144000 */ + {12000000, 2, 2}, /* 6000000 */ + {13000000, 2, 2}, /* 6500000 */ + {19200000, 3, 3}, /* 6400000 */ + {24000000, 4, 4}, /* 6000000 */ + {26000000, 4, 4}, /* 6500000 */ + {38400000, 6, 5} /* 6400000 */ +}; + +static int cs42l73_get_mclkx_coeff(int mclkx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs42l73_mclkx_coeffs); i++) { + if (cs42l73_mclkx_coeffs[i].mclkx == mclkx) + return i; + } + return -EINVAL; +} + +static int cs42l73_get_mclk_coeff(int mclk, int srate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs42l73_mclk_coeffs); i++) { + if (cs42l73_mclk_coeffs[i].mclk == mclk && + cs42l73_mclk_coeffs[i].srate == srate) + return i; + } + return -EINVAL; + +} + +static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq) +{ + struct snd_soc_component *component = dai->component; + struct cs42l73_private *priv = snd_soc_component_get_drvdata(component); + + int mclkx_coeff; + u32 mclk = 0; + u8 dmmcc = 0; + + /* MCLKX -> MCLK */ + mclkx_coeff = cs42l73_get_mclkx_coeff(freq); + if (mclkx_coeff < 0) + return mclkx_coeff; + + mclk = cs42l73_mclkx_coeffs[mclkx_coeff].mclkx / + cs42l73_mclkx_coeffs[mclkx_coeff].ratio; + + dev_dbg(component->dev, "MCLK%u %u <-> internal MCLK %u\n", + priv->mclksel + 1, cs42l73_mclkx_coeffs[mclkx_coeff].mclkx, + mclk); + + dmmcc = (priv->mclksel << 4) | + (cs42l73_mclkx_coeffs[mclkx_coeff].mclkdiv << 1); + + snd_soc_component_write(component, CS42L73_DMMCC, dmmcc); + + priv->sysclk = mclkx_coeff; + priv->mclk = mclk; + + return 0; +} + +static int cs42l73_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs42l73_private *priv = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case CS42L73_CLKID_MCLK1: + break; + case CS42L73_CLKID_MCLK2: + break; + default: + return -EINVAL; + } + + if ((cs42l73_set_mclk(dai, freq)) < 0) { + dev_err(component->dev, "Unable to set MCLK for dai %s\n", + dai->name); + return -EINVAL; + } + + priv->mclksel = clk_id; + + return 0; +} + +static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs42l73_private *priv = snd_soc_component_get_drvdata(component); + u8 id = codec_dai->id; + unsigned int inv, format; + u8 spc, mmcc; + + spc = snd_soc_component_read(component, CS42L73_SPC(id)); + mmcc = snd_soc_component_read(component, CS42L73_MMCC(id)); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + mmcc |= CS42L73_MS_MASTER; + break; + + case SND_SOC_DAIFMT_CBS_CFS: + mmcc &= ~CS42L73_MS_MASTER; + break; + + default: + return -EINVAL; + } + + format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK); + inv = (fmt & SND_SOC_DAIFMT_INV_MASK); + + switch (format) { + case SND_SOC_DAIFMT_I2S: + spc &= ~CS42L73_SPDIF_PCM; + break; + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + if (mmcc & CS42L73_MS_MASTER) { + dev_err(component->dev, + "PCM format in slave mode only\n"); + return -EINVAL; + } + if (id == CS42L73_ASP) { + dev_err(component->dev, + "PCM format is not supported on ASP port\n"); + return -EINVAL; + } + spc |= CS42L73_SPDIF_PCM; + break; + default: + return -EINVAL; + } + + if (spc & CS42L73_SPDIF_PCM) { + /* Clear PCM mode, clear PCM_BIT_ORDER bit for MSB->LSB */ + spc &= ~(CS42L73_PCM_MODE_MASK | CS42L73_PCM_BIT_ORDER); + switch (format) { + case SND_SOC_DAIFMT_DSP_B: + if (inv == SND_SOC_DAIFMT_IB_IF) + spc |= CS42L73_PCM_MODE0; + if (inv == SND_SOC_DAIFMT_IB_NF) + spc |= CS42L73_PCM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_A: + if (inv == SND_SOC_DAIFMT_IB_IF) + spc |= CS42L73_PCM_MODE1; + break; + default: + return -EINVAL; + } + } + + priv->config[id].spc = spc; + priv->config[id].mmcc = mmcc; + + return 0; +} + +static const unsigned int cs42l73_asrc_rates[] = { + 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000 +}; + +static unsigned int cs42l73_get_xspfs_coeff(u32 rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(cs42l73_asrc_rates); i++) { + if (cs42l73_asrc_rates[i] == rate) + return i + 1; + } + return 0; /* 0 = Don't know */ +} + +static void cs42l73_update_asrc(struct snd_soc_component *component, int id, int srate) +{ + u8 spfs = 0; + + if (srate > 0) + spfs = cs42l73_get_xspfs_coeff(srate); + + switch (id) { + case CS42L73_XSP: + snd_soc_component_update_bits(component, CS42L73_VXSPFS, 0x0f, spfs); + break; + case CS42L73_ASP: + snd_soc_component_update_bits(component, CS42L73_ASPC, 0x3c, spfs << 2); + break; + case CS42L73_VSP: + snd_soc_component_update_bits(component, CS42L73_VXSPFS, 0xf0, spfs << 4); + break; + default: + break; + } +} + +static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42l73_private *priv = snd_soc_component_get_drvdata(component); + int id = dai->id; + int mclk_coeff; + int srate = params_rate(params); + + if (priv->config[id].mmcc & CS42L73_MS_MASTER) { + /* CS42L73 Master */ + /* MCLK -> srate */ + mclk_coeff = + cs42l73_get_mclk_coeff(priv->mclk, srate); + + if (mclk_coeff < 0) + return -EINVAL; + + dev_dbg(component->dev, + "DAI[%d]: MCLK %u, srate %u, MMCC[5:0] = %x\n", + id, priv->mclk, srate, + cs42l73_mclk_coeffs[mclk_coeff].mmcc); + + priv->config[id].mmcc &= 0xC0; + priv->config[id].mmcc |= cs42l73_mclk_coeffs[mclk_coeff].mmcc; + priv->config[id].spc &= 0xFC; + /* Use SCLK=64*Fs if internal MCLK >= 6.4MHz */ + if (priv->mclk >= 6400000) + priv->config[id].spc |= CS42L73_MCK_SCLK_64FS; + else + priv->config[id].spc |= CS42L73_MCK_SCLK_MCLK; + } else { + /* CS42L73 Slave */ + priv->config[id].spc &= 0xFC; + priv->config[id].spc |= CS42L73_MCK_SCLK_64FS; + } + /* Update ASRCs */ + priv->config[id].srate = srate; + + snd_soc_component_write(component, CS42L73_SPC(id), priv->config[id].spc); + snd_soc_component_write(component, CS42L73_MMCC(id), priv->config[id].mmcc); + + cs42l73_update_asrc(component, id, srate); + + return 0; +} + +static int cs42l73_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct cs42l73_private *cs42l73 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_update_bits(component, CS42L73_DMMCC, CS42L73_MCLKDIS, 0); + snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 0); + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + regcache_cache_only(cs42l73->regmap, false); + regcache_sync(cs42l73->regmap); + } + snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 1); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 1); + if (cs42l73->shutdwn_delay > 0) { + mdelay(cs42l73->shutdwn_delay); + cs42l73->shutdwn_delay = 0; + } else { + mdelay(15); /* Min amount of time requred to power + * down. + */ + } + snd_soc_component_update_bits(component, CS42L73_DMMCC, CS42L73_MCLKDIS, 1); + break; + } + return 0; +} + +static int cs42l73_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_component *component = dai->component; + int id = dai->id; + + return snd_soc_component_update_bits(component, CS42L73_SPC(id), CS42L73_SP_3ST, + tristate << 7); +} + +static const struct snd_pcm_hw_constraint_list constraints_12_24 = { + .count = ARRAY_SIZE(cs42l73_asrc_rates), + .list = cs42l73_asrc_rates, +}; + +static int cs42l73_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_12_24); + return 0; +} + + +#define CS42L73_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops cs42l73_ops = { + .startup = cs42l73_pcm_startup, + .hw_params = cs42l73_pcm_hw_params, + .set_fmt = cs42l73_set_dai_fmt, + .set_sysclk = cs42l73_set_sysclk, + .set_tristate = cs42l73_set_tristate, +}; + +static struct snd_soc_dai_driver cs42l73_dai[] = { + { + .name = "cs42l73-xsp", + .id = CS42L73_XSP, + .playback = { + .stream_name = "XSP Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L73_FORMATS, + }, + .capture = { + .stream_name = "XSP Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L73_FORMATS, + }, + .ops = &cs42l73_ops, + .symmetric_rate = 1, + }, + { + .name = "cs42l73-asp", + .id = CS42L73_ASP, + .playback = { + .stream_name = "ASP Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L73_FORMATS, + }, + .capture = { + .stream_name = "ASP Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L73_FORMATS, + }, + .ops = &cs42l73_ops, + .symmetric_rate = 1, + }, + { + .name = "cs42l73-vsp", + .id = CS42L73_VSP, + .playback = { + .stream_name = "VSP Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L73_FORMATS, + }, + .capture = { + .stream_name = "VSP Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS42L73_FORMATS, + }, + .ops = &cs42l73_ops, + .symmetric_rate = 1, + } +}; + +static int cs42l73_probe(struct snd_soc_component *component) +{ + struct cs42l73_private *cs42l73 = snd_soc_component_get_drvdata(component); + + /* Set Charge Pump Frequency */ + if (cs42l73->pdata.chgfreq) + snd_soc_component_update_bits(component, CS42L73_CPFCHC, + CS42L73_CHARGEPUMP_MASK, + cs42l73->pdata.chgfreq << 4); + + /* MCLK1 as master clk */ + cs42l73->mclksel = CS42L73_CLKID_MCLK1; + cs42l73->mclk = 0; + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_cs42l73 = { + .probe = cs42l73_probe, + .set_bias_level = cs42l73_set_bias_level, + .controls = cs42l73_snd_controls, + .num_controls = ARRAY_SIZE(cs42l73_snd_controls), + .dapm_widgets = cs42l73_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42l73_dapm_widgets), + .dapm_routes = cs42l73_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs42l73_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config cs42l73_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS42L73_MAX_REGISTER, + .reg_defaults = cs42l73_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs42l73_reg_defaults), + .volatile_reg = cs42l73_volatile_register, + .readable_reg = cs42l73_readable_register, + .cache_type = REGCACHE_MAPLE, + + .use_single_read = true, + .use_single_write = true, +}; + +static int cs42l73_i2c_probe(struct i2c_client *i2c_client) +{ + struct cs42l73_private *cs42l73; + struct cs42l73_platform_data *pdata = dev_get_platdata(&i2c_client->dev); + int ret, devid; + unsigned int reg; + u32 val32; + + cs42l73 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l73), GFP_KERNEL); + if (!cs42l73) + return -ENOMEM; + + cs42l73->regmap = devm_regmap_init_i2c(i2c_client, &cs42l73_regmap); + if (IS_ERR(cs42l73->regmap)) { + ret = PTR_ERR(cs42l73->regmap); + dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + if (pdata) { + cs42l73->pdata = *pdata; + } else { + pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (i2c_client->dev.of_node) { + if (of_property_read_u32(i2c_client->dev.of_node, + "chgfreq", &val32) >= 0) + pdata->chgfreq = val32; + } + pdata->reset_gpio = of_get_named_gpio(i2c_client->dev.of_node, + "reset-gpio", 0); + cs42l73->pdata = *pdata; + } + + i2c_set_clientdata(i2c_client, cs42l73); + + if (cs42l73->pdata.reset_gpio) { + ret = devm_gpio_request_one(&i2c_client->dev, + cs42l73->pdata.reset_gpio, + GPIOF_OUT_INIT_HIGH, + "CS42L73 /RST"); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n", + cs42l73->pdata.reset_gpio, ret); + return ret; + } + gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0); + gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 1); + } + + /* initialize codec */ + devid = cirrus_read_device_id(cs42l73->regmap, CS42L73_DEVID_AB); + if (devid < 0) { + ret = devid; + dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret); + goto err_reset; + } + + if (devid != CS42L73_DEVID) { + ret = -ENODEV; + dev_err(&i2c_client->dev, + "CS42L73 Device ID (%X). Expected %X\n", + devid, CS42L73_DEVID); + goto err_reset; + } + + ret = regmap_read(cs42l73->regmap, CS42L73_REVID, ®); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed\n"); + goto err_reset; + } + + dev_info(&i2c_client->dev, + "Cirrus Logic CS42L73, Revision: %02X\n", reg & 0xFF); + + ret = devm_snd_soc_register_component(&i2c_client->dev, + &soc_component_dev_cs42l73, cs42l73_dai, + ARRAY_SIZE(cs42l73_dai)); + if (ret < 0) + goto err_reset; + + return 0; + +err_reset: + gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0); + + return ret; +} + +static const struct of_device_id cs42l73_of_match[] = { + { .compatible = "cirrus,cs42l73", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cs42l73_of_match); + +static const struct i2c_device_id cs42l73_id[] = { + {"cs42l73", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs42l73_id); + +static struct i2c_driver cs42l73_i2c_driver = { + .driver = { + .name = "cs42l73", + .of_match_table = cs42l73_of_match, + }, + .id_table = cs42l73_id, + .probe = cs42l73_i2c_probe, + +}; + +module_i2c_driver(cs42l73_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L73 driver"); +MODULE_AUTHOR("Georgi Vlaev, Nucleus Systems Ltd, "); +MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42l73.h b/sound/soc/codecs/cs42l73.h new file mode 100644 index 0000000000..e43a355761 --- /dev/null +++ b/sound/soc/codecs/cs42l73.h @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC CS42L73 codec driver + * + * Copyright 2011 Cirrus Logic, Inc. + * + * Author: Georgi Vlaev + * Brian Austin + */ + +#ifndef __CS42L73_H__ +#define __CS42L73_H__ + +/* I2C Registers */ +/* I2C Address: 1001010[R/W] - 10010100 = 0x94(Write); 10010101 = 0x95(Read) */ +#define CS42L73_CHIP_ID 0x4a +#define CS42L73_DEVID_AB 0x01 /* Device ID A & B [RO]. */ +#define CS42L73_DEVID_CD 0x02 /* Device ID C & D [RO]. */ +#define CS42L73_DEVID_E 0x03 /* Device ID E [RO]. */ +#define CS42L73_REVID 0x05 /* Revision ID [RO]. */ +#define CS42L73_PWRCTL1 0x06 /* Power Control 1. */ +#define CS42L73_PWRCTL2 0x07 /* Power Control 2. */ +#define CS42L73_PWRCTL3 0x08 /* Power Control 3. */ +#define CS42L73_CPFCHC 0x09 /* Charge Pump Freq. Class H Ctl. */ +#define CS42L73_OLMBMSDC 0x0A /* Output Load, MIC Bias, MIC2 SDT */ +#define CS42L73_DMMCC 0x0B /* Digital MIC & Master Clock Ctl. */ +#define CS42L73_XSPC 0x0C /* Auxiliary Serial Port (XSP) Ctl. */ +#define CS42L73_XSPMMCC 0x0D /* XSP Master Mode Clocking Control. */ +#define CS42L73_ASPC 0x0E /* Audio Serial Port (ASP) Control. */ +#define CS42L73_ASPMMCC 0x0F /* ASP Master Mode Clocking Control. */ +#define CS42L73_VSPC 0x10 /* Voice Serial Port (VSP) Control. */ +#define CS42L73_VSPMMCC 0x11 /* VSP Master Mode Clocking Control. */ +#define CS42L73_VXSPFS 0x12 /* VSP & XSP Sample Rate. */ +#define CS42L73_MIOPC 0x13 /* Misc. Input & Output Path Control. */ +#define CS42L73_ADCIPC 0x14 /* ADC/IP Control. */ +#define CS42L73_MICAPREPGAAVOL 0x15 /* MIC 1 [A] PreAmp, PGAA Vol. */ +#define CS42L73_MICBPREPGABVOL 0x16 /* MIC 2 [B] PreAmp, PGAB Vol. */ +#define CS42L73_IPADVOL 0x17 /* Input Pat7h A Digital Volume. */ +#define CS42L73_IPBDVOL 0x18 /* Input Path B Digital Volume. */ +#define CS42L73_PBDC 0x19 /* Playback Digital Control. */ +#define CS42L73_HLADVOL 0x1A /* HP/Line A Out Digital Vol. */ +#define CS42L73_HLBDVOL 0x1B /* HP/Line B Out Digital Vol. */ +#define CS42L73_SPKDVOL 0x1C /* Spkphone Out [A] Digital Vol. */ +#define CS42L73_ESLDVOL 0x1D /* Ear/Spkphone LO [B] Digital */ +#define CS42L73_HPAAVOL 0x1E /* HP A Analog Volume. */ +#define CS42L73_HPBAVOL 0x1F /* HP B Analog Volume. */ +#define CS42L73_LOAAVOL 0x20 /* Line Out A Analog Volume. */ +#define CS42L73_LOBAVOL 0x21 /* Line Out B Analog Volume. */ +#define CS42L73_STRINV 0x22 /* Stereo Input Path Adv. Vol. */ +#define CS42L73_XSPINV 0x23 /* Auxiliary Port Input Advisory Vol. */ +#define CS42L73_ASPINV 0x24 /* Audio Port Input Advisory Vol. */ +#define CS42L73_VSPINV 0x25 /* Voice Port Input Advisory Vol. */ +#define CS42L73_LIMARATEHL 0x26 /* Lmtr Attack Rate HP/Line. */ +#define CS42L73_LIMRRATEHL 0x27 /* Lmtr Ctl, Rel.Rate HP/Line. */ +#define CS42L73_LMAXHL 0x28 /* Lmtr Thresholds HP/Line. */ +#define CS42L73_LIMARATESPK 0x29 /* Lmtr Attack Rate Spkphone [A]. */ +#define CS42L73_LIMRRATESPK 0x2A /* Lmtr Ctl,Release Rate Spk. [A]. */ +#define CS42L73_LMAXSPK 0x2B /* Lmtr Thresholds Spkphone [A]. */ +#define CS42L73_LIMARATEESL 0x2C /* Lmtr Attack Rate */ +#define CS42L73_LIMRRATEESL 0x2D /* Lmtr Ctl,Release Rate */ +#define CS42L73_LMAXESL 0x2E /* Lmtr Thresholds */ +#define CS42L73_ALCARATE 0x2F /* ALC Enable, Attack Rate AB. */ +#define CS42L73_ALCRRATE 0x30 /* ALC Release Rate AB. */ +#define CS42L73_ALCMINMAX 0x31 /* ALC Thresholds AB. */ +#define CS42L73_NGCAB 0x32 /* Noise Gate Ctl AB. */ +#define CS42L73_ALCNGMC 0x33 /* ALC & Noise Gate Misc Ctl. */ +#define CS42L73_MIXERCTL 0x34 /* Mixer Control. */ +#define CS42L73_HLAIPAA 0x35 /* HP/LO Left Mixer: L. */ +#define CS42L73_HLBIPBA 0x36 /* HP/LO Right Mixer: R. */ +#define CS42L73_HLAXSPAA 0x37 /* HP/LO Left Mixer: XSP L */ +#define CS42L73_HLBXSPBA 0x38 /* HP/LO Right Mixer: XSP R */ +#define CS42L73_HLAASPAA 0x39 /* HP/LO Left Mixer: ASP L */ +#define CS42L73_HLBASPBA 0x3A /* HP/LO Right Mixer: ASP R */ +#define CS42L73_HLAVSPMA 0x3B /* HP/LO Left Mixer: VSP. */ +#define CS42L73_HLBVSPMA 0x3C /* HP/LO Right Mixer: VSP */ +#define CS42L73_XSPAIPAA 0x3D /* XSP Left Mixer: Left */ +#define CS42L73_XSPBIPBA 0x3E /* XSP Rt. Mixer: Right */ +#define CS42L73_XSPAXSPAA 0x3F /* XSP Left Mixer: XSP L */ +#define CS42L73_XSPBXSPBA 0x40 /* XSP Rt. Mixer: XSP R */ +#define CS42L73_XSPAASPAA 0x41 /* XSP Left Mixer: ASP L */ +#define CS42L73_XSPAASPBA 0x42 /* XSP Rt. Mixer: ASP R */ +#define CS42L73_XSPAVSPMA 0x43 /* XSP Left Mixer: VSP */ +#define CS42L73_XSPBVSPMA 0x44 /* XSP Rt. Mixer: VSP */ +#define CS42L73_ASPAIPAA 0x45 /* ASP Left Mixer: Left */ +#define CS42L73_ASPBIPBA 0x46 /* ASP Rt. Mixer: Right */ +#define CS42L73_ASPAXSPAA 0x47 /* ASP Left Mixer: XSP L */ +#define CS42L73_ASPBXSPBA 0x48 /* ASP Rt. Mixer: XSP R */ +#define CS42L73_ASPAASPAA 0x49 /* ASP Left Mixer: ASP L */ +#define CS42L73_ASPBASPBA 0x4A /* ASP Rt. Mixer: ASP R */ +#define CS42L73_ASPAVSPMA 0x4B /* ASP Left Mixer: VSP */ +#define CS42L73_ASPBVSPMA 0x4C /* ASP Rt. Mixer: VSP */ +#define CS42L73_VSPAIPAA 0x4D /* VSP Left Mixer: Left */ +#define CS42L73_VSPBIPBA 0x4E /* VSP Rt. Mixer: Right */ +#define CS42L73_VSPAXSPAA 0x4F /* VSP Left Mixer: XSP L */ +#define CS42L73_VSPBXSPBA 0x50 /* VSP Rt. Mixer: XSP R */ +#define CS42L73_VSPAASPAA 0x51 /* VSP Left Mixer: ASP Left */ +#define CS42L73_VSPBASPBA 0x52 /* VSP Rt. Mixer: ASP Right */ +#define CS42L73_VSPAVSPMA 0x53 /* VSP Left Mixer: VSP */ +#define CS42L73_VSPBVSPMA 0x54 /* VSP Rt. Mixer: VSP */ +#define CS42L73_MMIXCTL 0x55 /* Mono Mixer Controls. */ +#define CS42L73_SPKMIPMA 0x56 /* SPK Mono Mixer: In. Path */ +#define CS42L73_SPKMXSPA 0x57 /* SPK Mono Mixer: XSP Mono/L/R Att. */ +#define CS42L73_SPKMASPA 0x58 /* SPK Mono Mixer: ASP Mono/L/R Att. */ +#define CS42L73_SPKMVSPMA 0x59 /* SPK Mono Mixer: VSP Mono Atten. */ +#define CS42L73_ESLMIPMA 0x5A /* Ear/SpLO Mono Mixer: */ +#define CS42L73_ESLMXSPA 0x5B /* Ear/SpLO Mono Mixer: XSP */ +#define CS42L73_ESLMASPA 0x5C /* Ear/SpLO Mono Mixer: ASP */ +#define CS42L73_ESLMVSPMA 0x5D /* Ear/SpLO Mono Mixer: VSP */ +#define CS42L73_IM1 0x5E /* Interrupt Mask 1. */ +#define CS42L73_IM2 0x5F /* Interrupt Mask 2. */ +#define CS42L73_IS1 0x60 /* Interrupt Status 1 [RO]. */ +#define CS42L73_IS2 0x61 /* Interrupt Status 2 [RO]. */ +#define CS42L73_MAX_REGISTER 0x61 /* Total Registers */ +/* Bitfield Definitions */ + +/* CS42L73_PWRCTL1 */ +#define CS42L73_PDN_ADCB (1 << 7) +#define CS42L73_PDN_DMICB (1 << 6) +#define CS42L73_PDN_ADCA (1 << 5) +#define CS42L73_PDN_DMICA (1 << 4) +#define CS42L73_PDN_LDO (1 << 2) +#define CS42L73_DISCHG_FILT (1 << 1) +#define CS42L73_PDN (1 << 0) + +/* CS42L73_PWRCTL2 */ +#define CS42L73_PDN_MIC2_BIAS (1 << 7) +#define CS42L73_PDN_MIC1_BIAS (1 << 6) +#define CS42L73_PDN_VSP (1 << 4) +#define CS42L73_PDN_ASP_SDOUT (1 << 3) +#define CS42L73_PDN_ASP_SDIN (1 << 2) +#define CS42L73_PDN_XSP_SDOUT (1 << 1) +#define CS42L73_PDN_XSP_SDIN (1 << 0) + +/* CS42L73_PWRCTL3 */ +#define CS42L73_PDN_THMS (1 << 5) +#define CS42L73_PDN_SPKLO (1 << 4) +#define CS42L73_PDN_EAR (1 << 3) +#define CS42L73_PDN_SPK (1 << 2) +#define CS42L73_PDN_LO (1 << 1) +#define CS42L73_PDN_HP (1 << 0) + +/* Thermal Overload Detect. Requires interrupt ... */ +#define CS42L73_THMOVLD_150C 0 +#define CS42L73_THMOVLD_132C 1 +#define CS42L73_THMOVLD_115C 2 +#define CS42L73_THMOVLD_098C 3 + +#define CS42L73_CHARGEPUMP_MASK (0xF0) + +/* CS42L73_ASPC, CS42L73_XSPC, CS42L73_VSPC */ +#define CS42L73_SP_3ST (1 << 7) +#define CS42L73_SPDIF_I2S (0 << 6) +#define CS42L73_SPDIF_PCM (1 << 6) +#define CS42L73_PCM_MODE0 (0 << 4) +#define CS42L73_PCM_MODE1 (1 << 4) +#define CS42L73_PCM_MODE2 (2 << 4) +#define CS42L73_PCM_MODE_MASK (3 << 4) +#define CS42L73_PCM_BIT_ORDER (1 << 3) +#define CS42L73_MCK_SCLK_64FS (0 << 0) +#define CS42L73_MCK_SCLK_MCLK (2 << 0) +#define CS42L73_MCK_SCLK_PREMCLK (3 << 0) + +/* CS42L73_xSPMMCC */ +#define CS42L73_MS_MASTER (1 << 7) + + +/* CS42L73_DMMCC */ +#define CS42L73_MCLKDIS (1 << 0) +#define CS42L73_MCLKSEL_MCLK2 (1 << 4) +#define CS42L73_MCLKSEL_MCLK1 (0 << 4) + +/* CS42L73 MCLK derived from MCLK1 or MCLK2 */ +#define CS42L73_CLKID_MCLK1 0 +#define CS42L73_CLKID_MCLK2 1 + +#define CS42L73_MCLKXDIV 0 +#define CS42L73_MMCCDIV 1 + +#define CS42L73_XSP 0 +#define CS42L73_ASP 1 +#define CS42L73_VSP 2 + +/* IS1, IM1 */ +#define CS42L73_MIC2_SDET (1 << 6) +#define CS42L73_THMOVLD (1 << 4) +#define CS42L73_DIGMIXOVFL (1 << 3) +#define CS42L73_IPBOVFL (1 << 1) +#define CS42L73_IPAOVFL (1 << 0) + +/* Analog Softramp */ +#define CS42L73_ANLGOSFT (1 << 0) + +/* HP A/B Analog Mute */ +#define CS42L73_HPA_MUTE (1 << 7) +/* LO A/B Analog Mute */ +#define CS42L73_LOA_MUTE (1 << 7) +/* Digital Mute */ +#define CS42L73_HLAD_MUTE (1 << 0) +#define CS42L73_HLBD_MUTE (1 << 1) +#define CS42L73_SPKD_MUTE (1 << 2) +#define CS42L73_ESLD_MUTE (1 << 3) + +/* Misc defines for codec */ +#define CS42L73_DEVID 0x00042A73 +#define CS42L73_MCLKX_MIN 5644800 +#define CS42L73_MCLKX_MAX 38400000 + +#define CS42L73_SPC(id) (CS42L73_XSPC + (id << 1)) +#define CS42L73_MMCC(id) (CS42L73_XSPMMCC + (id << 1)) +#define CS42L73_SPFS(id) ((id == CS42L73_ASP) ? CS42L73_ASPC : CS42L73_VXSPFS) + +#endif /* __CS42L73_H__ */ diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c new file mode 100644 index 0000000000..f482b6a4f5 --- /dev/null +++ b/sound/soc/codecs/cs42l83-i2c.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l83-i2c.c -- CS42L83 ALSA SoC audio driver for I2C + * + * Based on cs42l42-i2c.c: + * Copyright 2016, 2022 Cirrus Logic, Inc. + */ + +#include +#include +#include +#include +#include + +#include "cs42l42.h" + +static const struct reg_default cs42l83_reg_defaults[] = { + { CS42L42_FRZ_CTL, 0x00 }, + { CS42L42_SRC_CTL, 0x10 }, + { CS42L42_MCLK_CTL, 0x00 }, /* <- only deviation from CS42L42 */ + { CS42L42_SFTRAMP_RATE, 0xA4 }, + { CS42L42_SLOW_START_ENABLE, 0x70 }, + { CS42L42_I2C_DEBOUNCE, 0x88 }, + { CS42L42_I2C_STRETCH, 0x03 }, + { CS42L42_I2C_TIMEOUT, 0xB7 }, + { CS42L42_PWR_CTL1, 0xFF }, + { CS42L42_PWR_CTL2, 0x84 }, + { CS42L42_PWR_CTL3, 0x20 }, + { CS42L42_RSENSE_CTL1, 0x40 }, + { CS42L42_RSENSE_CTL2, 0x00 }, + { CS42L42_OSC_SWITCH, 0x00 }, + { CS42L42_RSENSE_CTL3, 0x1B }, + { CS42L42_TSENSE_CTL, 0x1B }, + { CS42L42_TSRS_INT_DISABLE, 0x00 }, + { CS42L42_HSDET_CTL1, 0x77 }, + { CS42L42_HSDET_CTL2, 0x00 }, + { CS42L42_HS_SWITCH_CTL, 0xF3 }, + { CS42L42_HS_CLAMP_DISABLE, 0x00 }, + { CS42L42_MCLK_SRC_SEL, 0x00 }, + { CS42L42_SPDIF_CLK_CFG, 0x00 }, + { CS42L42_FSYNC_PW_LOWER, 0x00 }, + { CS42L42_FSYNC_PW_UPPER, 0x00 }, + { CS42L42_FSYNC_P_LOWER, 0xF9 }, + { CS42L42_FSYNC_P_UPPER, 0x00 }, + { CS42L42_ASP_CLK_CFG, 0x00 }, + { CS42L42_ASP_FRM_CFG, 0x10 }, + { CS42L42_FS_RATE_EN, 0x00 }, + { CS42L42_IN_ASRC_CLK, 0x00 }, + { CS42L42_OUT_ASRC_CLK, 0x00 }, + { CS42L42_PLL_DIV_CFG1, 0x00 }, + { CS42L42_ADC_OVFL_INT_MASK, 0x01 }, + { CS42L42_MIXER_INT_MASK, 0x0F }, + { CS42L42_SRC_INT_MASK, 0x0F }, + { CS42L42_ASP_RX_INT_MASK, 0x1F }, + { CS42L42_ASP_TX_INT_MASK, 0x0F }, + { CS42L42_CODEC_INT_MASK, 0x03 }, + { CS42L42_SRCPL_INT_MASK, 0x7F }, + { CS42L42_VPMON_INT_MASK, 0x01 }, + { CS42L42_PLL_LOCK_INT_MASK, 0x01 }, + { CS42L42_TSRS_PLUG_INT_MASK, 0x0F }, + { CS42L42_PLL_CTL1, 0x00 }, + { CS42L42_PLL_DIV_FRAC0, 0x00 }, + { CS42L42_PLL_DIV_FRAC1, 0x00 }, + { CS42L42_PLL_DIV_FRAC2, 0x00 }, + { CS42L42_PLL_DIV_INT, 0x40 }, + { CS42L42_PLL_CTL3, 0x10 }, + { CS42L42_PLL_CAL_RATIO, 0x80 }, + { CS42L42_PLL_CTL4, 0x03 }, + { CS42L42_LOAD_DET_EN, 0x00 }, + { CS42L42_HSBIAS_SC_AUTOCTL, 0x03 }, + { CS42L42_WAKE_CTL, 0xC0 }, + { CS42L42_ADC_DISABLE_MUTE, 0x00 }, + { CS42L42_TIPSENSE_CTL, 0x02 }, + { CS42L42_MISC_DET_CTL, 0x03 }, + { CS42L42_MIC_DET_CTL1, 0x1F }, + { CS42L42_MIC_DET_CTL2, 0x2F }, + { CS42L42_DET_INT1_MASK, 0xE0 }, + { CS42L42_DET_INT2_MASK, 0xFF }, + { CS42L42_HS_BIAS_CTL, 0xC2 }, + { CS42L42_ADC_CTL, 0x00 }, + { CS42L42_ADC_VOLUME, 0x00 }, + { CS42L42_ADC_WNF_HPF_CTL, 0x71 }, + { CS42L42_DAC_CTL1, 0x00 }, + { CS42L42_DAC_CTL2, 0x02 }, + { CS42L42_HP_CTL, 0x0D }, + { CS42L42_CLASSH_CTL, 0x07 }, + { CS42L42_MIXER_CHA_VOL, 0x3F }, + { CS42L42_MIXER_ADC_VOL, 0x3F }, + { CS42L42_MIXER_CHB_VOL, 0x3F }, + { CS42L42_EQ_COEF_IN0, 0x00 }, + { CS42L42_EQ_COEF_IN1, 0x00 }, + { CS42L42_EQ_COEF_IN2, 0x00 }, + { CS42L42_EQ_COEF_IN3, 0x00 }, + { CS42L42_EQ_COEF_RW, 0x00 }, + { CS42L42_EQ_COEF_OUT0, 0x00 }, + { CS42L42_EQ_COEF_OUT1, 0x00 }, + { CS42L42_EQ_COEF_OUT2, 0x00 }, + { CS42L42_EQ_COEF_OUT3, 0x00 }, + { CS42L42_EQ_INIT_STAT, 0x00 }, + { CS42L42_EQ_START_FILT, 0x00 }, + { CS42L42_EQ_MUTE_CTL, 0x00 }, + { CS42L42_SP_RX_CH_SEL, 0x04 }, + { CS42L42_SP_RX_ISOC_CTL, 0x04 }, + { CS42L42_SP_RX_FS, 0x8C }, + { CS42l42_SPDIF_CH_SEL, 0x0E }, + { CS42L42_SP_TX_ISOC_CTL, 0x04 }, + { CS42L42_SP_TX_FS, 0xCC }, + { CS42L42_SPDIF_SW_CTL1, 0x3F }, + { CS42L42_SRC_SDIN_FS, 0x40 }, + { CS42L42_SRC_SDOUT_FS, 0x40 }, + { CS42L42_SPDIF_CTL1, 0x01 }, + { CS42L42_SPDIF_CTL2, 0x00 }, + { CS42L42_SPDIF_CTL3, 0x00 }, + { CS42L42_SPDIF_CTL4, 0x42 }, + { CS42L42_ASP_TX_SZ_EN, 0x00 }, + { CS42L42_ASP_TX_CH_EN, 0x00 }, + { CS42L42_ASP_TX_CH_AP_RES, 0x0F }, + { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_TX_HIZ_DLY_CFG, 0x00 }, + { CS42L42_ASP_TX_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH2_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_EN, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH1_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI1_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 }, +}; + +/* + * This is all the same as for CS42L42 but we + * replace the on-reset register defaults. + */ +static const struct regmap_config cs42l83_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = cs42l42_readable_register, + .volatile_reg = cs42l42_volatile_register, + + .ranges = &cs42l42_page_range, + .num_ranges = 1, + + .max_register = CS42L42_MAX_REGISTER, + .reg_defaults = cs42l83_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs42l83_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .use_single_read = true, + .use_single_write = true, +}; + +static int cs42l83_i2c_probe(struct i2c_client *i2c_client) +{ + struct device *dev = &i2c_client->dev; + struct cs42l42_private *cs42l83; + struct regmap *regmap; + int ret; + + cs42l83 = devm_kzalloc(dev, sizeof(*cs42l83), GFP_KERNEL); + if (!cs42l83) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(i2c_client, &cs42l83_regmap); + if (IS_ERR(regmap)) + return dev_err_probe(&i2c_client->dev, PTR_ERR(regmap), + "regmap_init() failed\n"); + + cs42l83->devid = CS42L83_CHIP_ID; + cs42l83->dev = dev; + cs42l83->regmap = regmap; + cs42l83->irq = i2c_client->irq; + + ret = cs42l42_common_probe(cs42l83, &cs42l42_soc_component, &cs42l42_dai); + if (ret) + return ret; + + return cs42l42_init(cs42l83); +} + +static void cs42l83_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs42l42_private *cs42l83 = dev_get_drvdata(&i2c_client->dev); + + cs42l42_common_remove(cs42l83); +} + +static int __maybe_unused cs42l83_i2c_resume(struct device *dev) +{ + int ret; + + ret = cs42l42_resume(dev); + if (ret) + return ret; + + cs42l42_resume_restore(dev); + + return 0; +} + +static const struct dev_pm_ops cs42l83_i2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l83_i2c_resume) +}; + +static const struct of_device_id __maybe_unused cs42l83_of_match[] = { + { .compatible = "cirrus,cs42l83", }, + {} +}; +MODULE_DEVICE_TABLE(of, cs42l83_of_match); + +static struct i2c_driver cs42l83_i2c_driver = { + .driver = { + .name = "cs42l83", + .pm = &cs42l83_i2c_pm_ops, + .of_match_table = of_match_ptr(cs42l83_of_match), + }, + .probe = cs42l83_i2c_probe, + .remove = cs42l83_i2c_remove, +}; + +module_i2c_driver(cs42l83_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L83 I2C driver"); +MODULE_AUTHOR("Martin Povišer "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_CS42L42_CORE); diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c new file mode 100644 index 0000000000..a422472820 --- /dev/null +++ b/sound/soc/codecs/cs42xx8-i2c.c @@ -0,0 +1,82 @@ +/* + * Cirrus Logic CS42448/CS42888 Audio CODEC DAI I2C driver + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include "cs42xx8.h" + +static const struct of_device_id cs42xx8_of_match[]; + +static int cs42xx8_i2c_probe(struct i2c_client *i2c) +{ + int ret; + struct cs42xx8_driver_data *drvdata; + const struct of_device_id *of_id; + + of_id = of_match_device(cs42xx8_of_match, &i2c->dev); + if (!of_id) { + dev_err(&i2c->dev, "failed to find driver data\n"); + return -EINVAL; + } + + drvdata = (struct cs42xx8_driver_data *)of_id->data; + + ret = cs42xx8_probe(&i2c->dev, + devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config), drvdata); + if (ret) + return ret; + + pm_runtime_enable(&i2c->dev); + pm_request_idle(&i2c->dev); + + return 0; +} + +static void cs42xx8_i2c_remove(struct i2c_client *i2c) +{ + pm_runtime_disable(&i2c->dev); +} + +static const struct of_device_id cs42xx8_of_match[] = { + { .compatible = "cirrus,cs42448", .data = &cs42448_data, }, + { .compatible = "cirrus,cs42888", .data = &cs42888_data, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cs42xx8_of_match); + +static const struct i2c_device_id cs42xx8_i2c_id[] = { + {"cs42448", (kernel_ulong_t)&cs42448_data}, + {"cs42888", (kernel_ulong_t)&cs42888_data}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id); + +static struct i2c_driver cs42xx8_i2c_driver = { + .driver = { + .name = "cs42xx8", + .pm = &cs42xx8_pm, + .of_match_table = cs42xx8_of_match, + }, + .probe = cs42xx8_i2c_probe, + .remove = cs42xx8_i2c_remove, + .id_table = cs42xx8_i2c_id, +}; + +module_i2c_driver(cs42xx8_i2c_driver); + +MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec I2C Driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c new file mode 100644 index 0000000000..9c44b6283b --- /dev/null +++ b/sound/soc/codecs/cs42xx8.c @@ -0,0 +1,679 @@ +/* + * Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs42xx8.h" + +#define CS42XX8_NUM_SUPPLIES 4 +static const char *const cs42xx8_supply_names[CS42XX8_NUM_SUPPLIES] = { + "VA", + "VD", + "VLS", + "VLC", +}; + +#define CS42XX8_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* codec private data */ +struct cs42xx8_priv { + struct regulator_bulk_data supplies[CS42XX8_NUM_SUPPLIES]; + const struct cs42xx8_driver_data *drvdata; + struct regmap *regmap; + struct clk *clk; + + bool slave_mode; + unsigned long sysclk; + u32 tx_channels; + struct gpio_desc *gpiod_reset; + u32 rate[2]; +}; + +/* -127.5dB to 0dB with step of 0.5dB */ +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); +/* -64dB to 24dB with step of 0.5dB */ +static const DECLARE_TLV_DB_SCALE(adc_tlv, -6400, 50, 0); + +static const char *const cs42xx8_adc_single[] = { "Differential", "Single-Ended" }; +static const char *const cs42xx8_szc[] = { "Immediate Change", "Zero Cross", + "Soft Ramp", "Soft Ramp on Zero Cross" }; + +static const struct soc_enum adc1_single_enum = + SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 4, 2, cs42xx8_adc_single); +static const struct soc_enum adc2_single_enum = + SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 3, 2, cs42xx8_adc_single); +static const struct soc_enum adc3_single_enum = + SOC_ENUM_SINGLE(CS42XX8_ADCCTL, 2, 2, cs42xx8_adc_single); +static const struct soc_enum dac_szc_enum = + SOC_ENUM_SINGLE(CS42XX8_TXCTL, 5, 4, cs42xx8_szc); +static const struct soc_enum adc_szc_enum = + SOC_ENUM_SINGLE(CS42XX8_TXCTL, 0, 4, cs42xx8_szc); + +static const struct snd_kcontrol_new cs42xx8_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC1 Playback Volume", CS42XX8_VOLAOUT1, + CS42XX8_VOLAOUT2, 0, 0xff, 1, dac_tlv), + SOC_DOUBLE_R_TLV("DAC2 Playback Volume", CS42XX8_VOLAOUT3, + CS42XX8_VOLAOUT4, 0, 0xff, 1, dac_tlv), + SOC_DOUBLE_R_TLV("DAC3 Playback Volume", CS42XX8_VOLAOUT5, + CS42XX8_VOLAOUT6, 0, 0xff, 1, dac_tlv), + SOC_DOUBLE_R_TLV("DAC4 Playback Volume", CS42XX8_VOLAOUT7, + CS42XX8_VOLAOUT8, 0, 0xff, 1, dac_tlv), + SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", CS42XX8_VOLAIN1, + CS42XX8_VOLAIN2, 0, -0x80, 0x30, 7, 0, adc_tlv), + SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", CS42XX8_VOLAIN3, + CS42XX8_VOLAIN4, 0, -0x80, 0x30, 7, 0, adc_tlv), + SOC_DOUBLE("DAC1 Invert Switch", CS42XX8_DACINV, 0, 1, 1, 0), + SOC_DOUBLE("DAC2 Invert Switch", CS42XX8_DACINV, 2, 3, 1, 0), + SOC_DOUBLE("DAC3 Invert Switch", CS42XX8_DACINV, 4, 5, 1, 0), + SOC_DOUBLE("DAC4 Invert Switch", CS42XX8_DACINV, 6, 7, 1, 0), + SOC_DOUBLE("ADC1 Invert Switch", CS42XX8_ADCINV, 0, 1, 1, 0), + SOC_DOUBLE("ADC2 Invert Switch", CS42XX8_ADCINV, 2, 3, 1, 0), + SOC_SINGLE("ADC High-Pass Filter Switch", CS42XX8_ADCCTL, 7, 1, 1), + SOC_SINGLE("DAC De-emphasis Switch", CS42XX8_ADCCTL, 5, 1, 0), + SOC_ENUM("ADC1 Single Ended Mode Switch", adc1_single_enum), + SOC_ENUM("ADC2 Single Ended Mode Switch", adc2_single_enum), + SOC_SINGLE("DAC Single Volume Control Switch", CS42XX8_TXCTL, 7, 1, 0), + SOC_ENUM("DAC Soft Ramp & Zero Cross Control Switch", dac_szc_enum), + SOC_SINGLE("DAC Auto Mute Switch", CS42XX8_TXCTL, 4, 1, 0), + SOC_SINGLE("Mute ADC Serial Port Switch", CS42XX8_TXCTL, 3, 1, 0), + SOC_SINGLE("ADC Single Volume Control Switch", CS42XX8_TXCTL, 2, 1, 0), + SOC_ENUM("ADC Soft Ramp & Zero Cross Control Switch", adc_szc_enum), +}; + +static const struct snd_kcontrol_new cs42xx8_adc3_snd_controls[] = { + SOC_DOUBLE_R_S_TLV("ADC3 Capture Volume", CS42XX8_VOLAIN5, + CS42XX8_VOLAIN6, 0, -0x80, 0x30, 7, 0, adc_tlv), + SOC_DOUBLE("ADC3 Invert Switch", CS42XX8_ADCINV, 4, 5, 1, 0), + SOC_ENUM("ADC3 Single Ended Mode Switch", adc3_single_enum), +}; + +static const struct snd_soc_dapm_widget cs42xx8_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC1", "Playback", CS42XX8_PWRCTL, 1, 1), + SND_SOC_DAPM_DAC("DAC2", "Playback", CS42XX8_PWRCTL, 2, 1), + SND_SOC_DAPM_DAC("DAC3", "Playback", CS42XX8_PWRCTL, 3, 1), + SND_SOC_DAPM_DAC("DAC4", "Playback", CS42XX8_PWRCTL, 4, 1), + + SND_SOC_DAPM_OUTPUT("AOUT1L"), + SND_SOC_DAPM_OUTPUT("AOUT1R"), + SND_SOC_DAPM_OUTPUT("AOUT2L"), + SND_SOC_DAPM_OUTPUT("AOUT2R"), + SND_SOC_DAPM_OUTPUT("AOUT3L"), + SND_SOC_DAPM_OUTPUT("AOUT3R"), + SND_SOC_DAPM_OUTPUT("AOUT4L"), + SND_SOC_DAPM_OUTPUT("AOUT4R"), + + SND_SOC_DAPM_ADC("ADC1", "Capture", CS42XX8_PWRCTL, 5, 1), + SND_SOC_DAPM_ADC("ADC2", "Capture", CS42XX8_PWRCTL, 6, 1), + + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN2R"), + + SND_SOC_DAPM_SUPPLY("PWR", CS42XX8_PWRCTL, 0, 1, NULL, 0), +}; + +static const struct snd_soc_dapm_widget cs42xx8_adc3_dapm_widgets[] = { + SND_SOC_DAPM_ADC("ADC3", "Capture", CS42XX8_PWRCTL, 7, 1), + + SND_SOC_DAPM_INPUT("AIN3L"), + SND_SOC_DAPM_INPUT("AIN3R"), +}; + +static const struct snd_soc_dapm_route cs42xx8_dapm_routes[] = { + /* Playback */ + { "AOUT1L", NULL, "DAC1" }, + { "AOUT1R", NULL, "DAC1" }, + { "DAC1", NULL, "PWR" }, + + { "AOUT2L", NULL, "DAC2" }, + { "AOUT2R", NULL, "DAC2" }, + { "DAC2", NULL, "PWR" }, + + { "AOUT3L", NULL, "DAC3" }, + { "AOUT3R", NULL, "DAC3" }, + { "DAC3", NULL, "PWR" }, + + { "AOUT4L", NULL, "DAC4" }, + { "AOUT4R", NULL, "DAC4" }, + { "DAC4", NULL, "PWR" }, + + /* Capture */ + { "ADC1", NULL, "AIN1L" }, + { "ADC1", NULL, "AIN1R" }, + { "ADC1", NULL, "PWR" }, + + { "ADC2", NULL, "AIN2L" }, + { "ADC2", NULL, "AIN2R" }, + { "ADC2", NULL, "PWR" }, +}; + +static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = { + /* Capture */ + { "ADC3", NULL, "AIN3L" }, + { "ADC3", NULL, "AIN3R" }, + { "ADC3", NULL, "PWR" }, +}; + +struct cs42xx8_ratios { + unsigned int mfreq; + unsigned int min_mclk; + unsigned int max_mclk; + unsigned int ratio[3]; +}; + +/* + * According to reference mannual, define the cs42xx8_ratio struct + * MFreq2 | MFreq1 | MFreq0 | Description | SSM | DSM | QSM | + * 0 | 0 | 0 |1.029MHz to 12.8MHz | 256 | 128 | 64 | + * 0 | 0 | 1 |1.536MHz to 19.2MHz | 384 | 192 | 96 | + * 0 | 1 | 0 |2.048MHz to 25.6MHz | 512 | 256 | 128 | + * 0 | 1 | 1 |3.072MHz to 38.4MHz | 768 | 384 | 192 | + * 1 | x | x |4.096MHz to 51.2MHz |1024 | 512 | 256 | + */ +static const struct cs42xx8_ratios cs42xx8_ratios[] = { + { 0, 1029000, 12800000, {256, 128, 64} }, + { 2, 1536000, 19200000, {384, 192, 96} }, + { 4, 2048000, 25600000, {512, 256, 128} }, + { 6, 3072000, 38400000, {768, 384, 192} }, + { 8, 4096000, 51200000, {1024, 512, 256} }, +}; + +static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); + + cs42xx8->sysclk = freq; + + return 0; +} + +static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); + u32 val; + + /* Set DAI format */ + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + val = CS42XX8_INTF_DAC_DIF_LEFTJ | CS42XX8_INTF_ADC_DIF_LEFTJ; + break; + case SND_SOC_DAIFMT_I2S: + val = CS42XX8_INTF_DAC_DIF_I2S | CS42XX8_INTF_ADC_DIF_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + val = CS42XX8_INTF_DAC_DIF_RIGHTJ | CS42XX8_INTF_ADC_DIF_RIGHTJ; + break; + case SND_SOC_DAIFMT_DSP_A: + val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM; + break; + default: + dev_err(component->dev, "unsupported dai format\n"); + return -EINVAL; + } + + regmap_update_bits(cs42xx8->regmap, CS42XX8_INTF, + CS42XX8_INTF_DAC_DIF_MASK | + CS42XX8_INTF_ADC_DIF_MASK, val); + + /* Set master/slave audio interface */ + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs42xx8->slave_mode = true; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs42xx8->slave_mode = false; + break; + default: + dev_err(component->dev, "unsupported master/slave mode\n"); + return -EINVAL; + } + + return 0; +} + +static int cs42xx8_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u32 ratio[2]; + u32 rate[2]; + u32 fm[2]; + u32 i, val, mask; + bool condition1, condition2; + + if (tx) + cs42xx8->tx_channels = params_channels(params); + + rate[tx] = params_rate(params); + rate[!tx] = cs42xx8->rate[!tx]; + + ratio[tx] = rate[tx] > 0 ? cs42xx8->sysclk / rate[tx] : 0; + ratio[!tx] = rate[!tx] > 0 ? cs42xx8->sysclk / rate[!tx] : 0; + + /* Get functional mode for tx and rx according to rate */ + for (i = 0; i < 2; i++) { + if (cs42xx8->slave_mode) { + fm[i] = CS42XX8_FM_AUTO; + } else { + if (rate[i] < 50000) { + fm[i] = CS42XX8_FM_SINGLE; + } else if (rate[i] > 50000 && rate[i] < 100000) { + fm[i] = CS42XX8_FM_DOUBLE; + } else if (rate[i] > 100000 && rate[i] < 200000) { + fm[i] = CS42XX8_FM_QUAD; + } else { + dev_err(component->dev, + "unsupported sample rate\n"); + return -EINVAL; + } + } + } + + for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { + /* Is the ratio[tx] valid ? */ + condition1 = ((fm[tx] == CS42XX8_FM_AUTO) ? + (cs42xx8_ratios[i].ratio[0] == ratio[tx] || + cs42xx8_ratios[i].ratio[1] == ratio[tx] || + cs42xx8_ratios[i].ratio[2] == ratio[tx]) : + (cs42xx8_ratios[i].ratio[fm[tx]] == ratio[tx])) && + cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk && + cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk; + + if (!ratio[tx]) + condition1 = true; + + /* Is the ratio[!tx] valid ? */ + condition2 = ((fm[!tx] == CS42XX8_FM_AUTO) ? + (cs42xx8_ratios[i].ratio[0] == ratio[!tx] || + cs42xx8_ratios[i].ratio[1] == ratio[!tx] || + cs42xx8_ratios[i].ratio[2] == ratio[!tx]) : + (cs42xx8_ratios[i].ratio[fm[!tx]] == ratio[!tx])); + + if (!ratio[!tx]) + condition2 = true; + + /* + * Both ratio[tx] and ratio[!tx] is valid, then we get + * a proper MFreq. + */ + if (condition1 && condition2) + break; + } + + if (i == ARRAY_SIZE(cs42xx8_ratios)) { + dev_err(component->dev, "unsupported sysclk ratio\n"); + return -EINVAL; + } + + cs42xx8->rate[tx] = params_rate(params); + + mask = CS42XX8_FUNCMOD_MFREQ_MASK; + val = cs42xx8_ratios[i].mfreq; + + regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, + CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask, + CS42XX8_FUNCMOD_xC_FM(tx, fm[tx]) | val); + + return 0; +} + +static int cs42xx8_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + /* Clear stored rate */ + cs42xx8->rate[tx] = 0; + + regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD, + CS42XX8_FUNCMOD_xC_FM_MASK(tx), + CS42XX8_FUNCMOD_xC_FM(tx, CS42XX8_FM_AUTO)); + return 0; +} + +static int cs42xx8_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); + u8 dac_unmute = cs42xx8->tx_channels ? + ~((0x1 << cs42xx8->tx_channels) - 1) : 0; + + regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, + mute ? CS42XX8_DACMUTE_ALL : dac_unmute); + + return 0; +} + +static const struct snd_soc_dai_ops cs42xx8_dai_ops = { + .set_fmt = cs42xx8_set_dai_fmt, + .set_sysclk = cs42xx8_set_dai_sysclk, + .hw_params = cs42xx8_hw_params, + .hw_free = cs42xx8_hw_free, + .mute_stream = cs42xx8_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver cs42xx8_dai = { + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = CS42XX8_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = CS42XX8_FORMATS, + }, + .ops = &cs42xx8_dai_ops, +}; + +static const struct reg_default cs42xx8_reg[] = { + { 0x02, 0x00 }, /* Power Control */ + { 0x03, 0xF0 }, /* Functional Mode */ + { 0x04, 0x46 }, /* Interface Formats */ + { 0x05, 0x00 }, /* ADC Control & DAC De-Emphasis */ + { 0x06, 0x10 }, /* Transition Control */ + { 0x07, 0x00 }, /* DAC Channel Mute */ + { 0x08, 0x00 }, /* Volume Control AOUT1 */ + { 0x09, 0x00 }, /* Volume Control AOUT2 */ + { 0x0a, 0x00 }, /* Volume Control AOUT3 */ + { 0x0b, 0x00 }, /* Volume Control AOUT4 */ + { 0x0c, 0x00 }, /* Volume Control AOUT5 */ + { 0x0d, 0x00 }, /* Volume Control AOUT6 */ + { 0x0e, 0x00 }, /* Volume Control AOUT7 */ + { 0x0f, 0x00 }, /* Volume Control AOUT8 */ + { 0x10, 0x00 }, /* DAC Channel Invert */ + { 0x11, 0x00 }, /* Volume Control AIN1 */ + { 0x12, 0x00 }, /* Volume Control AIN2 */ + { 0x13, 0x00 }, /* Volume Control AIN3 */ + { 0x14, 0x00 }, /* Volume Control AIN4 */ + { 0x15, 0x00 }, /* Volume Control AIN5 */ + { 0x16, 0x00 }, /* Volume Control AIN6 */ + { 0x17, 0x00 }, /* ADC Channel Invert */ + { 0x18, 0x00 }, /* Status Control */ + { 0x1a, 0x00 }, /* Status Mask */ + { 0x1b, 0x00 }, /* MUTEC Pin Control */ +}; + +static bool cs42xx8_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42XX8_STATUS: + return true; + default: + return false; + } +} + +static bool cs42xx8_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS42XX8_CHIPID: + case CS42XX8_STATUS: + return false; + default: + return true; + } +} + +const struct regmap_config cs42xx8_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS42XX8_LASTREG, + .reg_defaults = cs42xx8_reg, + .num_reg_defaults = ARRAY_SIZE(cs42xx8_reg), + .volatile_reg = cs42xx8_volatile_register, + .writeable_reg = cs42xx8_writeable_register, + .cache_type = REGCACHE_MAPLE, +}; +EXPORT_SYMBOL_GPL(cs42xx8_regmap_config); + +static int cs42xx8_component_probe(struct snd_soc_component *component) +{ + struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + switch (cs42xx8->drvdata->num_adcs) { + case 3: + snd_soc_add_component_controls(component, cs42xx8_adc3_snd_controls, + ARRAY_SIZE(cs42xx8_adc3_snd_controls)); + snd_soc_dapm_new_controls(dapm, cs42xx8_adc3_dapm_widgets, + ARRAY_SIZE(cs42xx8_adc3_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, cs42xx8_adc3_dapm_routes, + ARRAY_SIZE(cs42xx8_adc3_dapm_routes)); + break; + default: + break; + } + + /* Mute all DAC channels */ + regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE, CS42XX8_DACMUTE_ALL); + + return 0; +} + +static const struct snd_soc_component_driver cs42xx8_driver = { + .probe = cs42xx8_component_probe, + .controls = cs42xx8_snd_controls, + .num_controls = ARRAY_SIZE(cs42xx8_snd_controls), + .dapm_widgets = cs42xx8_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets), + .dapm_routes = cs42xx8_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +const struct cs42xx8_driver_data cs42448_data = { + .name = "cs42448", + .num_adcs = 3, +}; +EXPORT_SYMBOL_GPL(cs42448_data); + +const struct cs42xx8_driver_data cs42888_data = { + .name = "cs42888", + .num_adcs = 2, +}; +EXPORT_SYMBOL_GPL(cs42888_data); + +int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata) +{ + struct cs42xx8_priv *cs42xx8; + int ret, val, i; + + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "failed to allocate regmap: %d\n", ret); + return ret; + } + + cs42xx8 = devm_kzalloc(dev, sizeof(*cs42xx8), GFP_KERNEL); + if (cs42xx8 == NULL) + return -ENOMEM; + + dev_set_drvdata(dev, cs42xx8); + + cs42xx8->regmap = regmap; + + cs42xx8->drvdata = drvdata; + + cs42xx8->gpiod_reset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(cs42xx8->gpiod_reset)) + return PTR_ERR(cs42xx8->gpiod_reset); + + gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0); + + cs42xx8->clk = devm_clk_get(dev, "mclk"); + if (IS_ERR(cs42xx8->clk)) { + dev_err(dev, "failed to get the clock: %ld\n", + PTR_ERR(cs42xx8->clk)); + return -EINVAL; + } + + cs42xx8->sysclk = clk_get_rate(cs42xx8->clk); + + for (i = 0; i < ARRAY_SIZE(cs42xx8->supplies); i++) + cs42xx8->supplies[i].supply = cs42xx8_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, + ARRAY_SIZE(cs42xx8->supplies), cs42xx8->supplies); + if (ret) { + dev_err(dev, "failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies), + cs42xx8->supplies); + if (ret) { + dev_err(dev, "failed to enable supplies: %d\n", ret); + return ret; + } + + /* Make sure hardware reset done */ + msleep(5); + + /* Validate the chip ID */ + ret = regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val); + if (ret < 0) { + dev_err(dev, "failed to get device ID, ret = %d", ret); + goto err_enable; + } + + /* The top four bits of the chip ID should be 0000 */ + if (((val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4) != 0x00) { + dev_err(dev, "unmatched chip ID: %d\n", + (val & CS42XX8_CHIPID_CHIP_ID_MASK) >> 4); + ret = -EINVAL; + goto err_enable; + } + + dev_info(dev, "found device, revision %X\n", + val & CS42XX8_CHIPID_REV_ID_MASK); + + cs42xx8_dai.name = cs42xx8->drvdata->name; + + /* Each adc supports stereo input */ + cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2; + + ret = devm_snd_soc_register_component(dev, &cs42xx8_driver, &cs42xx8_dai, 1); + if (ret) { + dev_err(dev, "failed to register component:%d\n", ret); + goto err_enable; + } + + regcache_cache_only(cs42xx8->regmap, true); + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), + cs42xx8->supplies); + + return ret; +} +EXPORT_SYMBOL_GPL(cs42xx8_probe); + +#ifdef CONFIG_PM +static int cs42xx8_runtime_resume(struct device *dev) +{ + struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(cs42xx8->clk); + if (ret) { + dev_err(dev, "failed to enable mclk: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0); + + ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies), + cs42xx8->supplies); + if (ret) { + dev_err(dev, "failed to enable supplies: %d\n", ret); + goto err_clk; + } + + /* Make sure hardware reset done */ + msleep(5); + + regcache_cache_only(cs42xx8->regmap, false); + regcache_mark_dirty(cs42xx8->regmap); + + ret = regcache_sync(cs42xx8->regmap); + if (ret) { + dev_err(dev, "failed to sync regmap: %d\n", ret); + goto err_bulk; + } + + return 0; + +err_bulk: + regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), + cs42xx8->supplies); +err_clk: + clk_disable_unprepare(cs42xx8->clk); + + return ret; +} + +static int cs42xx8_runtime_suspend(struct device *dev) +{ + struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev); + + regcache_cache_only(cs42xx8->regmap, true); + + regulator_bulk_disable(ARRAY_SIZE(cs42xx8->supplies), + cs42xx8->supplies); + + gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 1); + + clk_disable_unprepare(cs42xx8->clk); + + return 0; +} +#endif + +const struct dev_pm_ops cs42xx8_pm = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(cs42xx8_pm); + +MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec Driver"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42xx8.h b/sound/soc/codecs/cs42xx8.h new file mode 100644 index 0000000000..342389e8b1 --- /dev/null +++ b/sound/soc/codecs/cs42xx8.h @@ -0,0 +1,238 @@ +/* + * cs42xx8.h - Cirrus Logic CS42448/CS42888 Audio CODEC driver header file + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef _CS42XX8_H +#define _CS42XX8_H + +struct cs42xx8_driver_data { + char name[32]; + int num_adcs; +}; + +extern const struct dev_pm_ops cs42xx8_pm; +extern const struct cs42xx8_driver_data cs42448_data; +extern const struct cs42xx8_driver_data cs42888_data; +extern const struct regmap_config cs42xx8_regmap_config; +int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata); + +/* CS42888 register map */ +#define CS42XX8_CHIPID 0x01 /* Chip ID */ +#define CS42XX8_PWRCTL 0x02 /* Power Control */ +#define CS42XX8_FUNCMOD 0x03 /* Functional Mode */ +#define CS42XX8_INTF 0x04 /* Interface Formats */ +#define CS42XX8_ADCCTL 0x05 /* ADC Control */ +#define CS42XX8_TXCTL 0x06 /* Transition Control */ +#define CS42XX8_DACMUTE 0x07 /* DAC Mute Control */ +#define CS42XX8_VOLAOUT1 0x08 /* Volume Control AOUT1 */ +#define CS42XX8_VOLAOUT2 0x09 /* Volume Control AOUT2 */ +#define CS42XX8_VOLAOUT3 0x0A /* Volume Control AOUT3 */ +#define CS42XX8_VOLAOUT4 0x0B /* Volume Control AOUT4 */ +#define CS42XX8_VOLAOUT5 0x0C /* Volume Control AOUT5 */ +#define CS42XX8_VOLAOUT6 0x0D /* Volume Control AOUT6 */ +#define CS42XX8_VOLAOUT7 0x0E /* Volume Control AOUT7 */ +#define CS42XX8_VOLAOUT8 0x0F /* Volume Control AOUT8 */ +#define CS42XX8_DACINV 0x10 /* DAC Channel Invert */ +#define CS42XX8_VOLAIN1 0x11 /* Volume Control AIN1 */ +#define CS42XX8_VOLAIN2 0x12 /* Volume Control AIN2 */ +#define CS42XX8_VOLAIN3 0x13 /* Volume Control AIN3 */ +#define CS42XX8_VOLAIN4 0x14 /* Volume Control AIN4 */ +#define CS42XX8_VOLAIN5 0x15 /* Volume Control AIN5 */ +#define CS42XX8_VOLAIN6 0x16 /* Volume Control AIN6 */ +#define CS42XX8_ADCINV 0x17 /* ADC Channel Invert */ +#define CS42XX8_STATUSCTL 0x18 /* Status Control */ +#define CS42XX8_STATUS 0x19 /* Status */ +#define CS42XX8_STATUSM 0x1A /* Status Mask */ +#define CS42XX8_MUTEC 0x1B /* MUTEC Pin Control */ + +#define CS42XX8_FIRSTREG CS42XX8_CHIPID +#define CS42XX8_LASTREG CS42XX8_MUTEC +#define CS42XX8_NUMREGS (CS42XX8_LASTREG - CS42XX8_FIRSTREG + 1) +#define CS42XX8_I2C_INCR 0x80 + +/* Chip I.D. and Revision Register (Address 01h) */ +#define CS42XX8_CHIPID_CHIP_ID_MASK 0xF0 +#define CS42XX8_CHIPID_REV_ID_MASK 0x0F + +/* Power Control (Address 02h) */ +#define CS42XX8_PWRCTL_PDN_ADC3_SHIFT 7 +#define CS42XX8_PWRCTL_PDN_ADC3_MASK (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT) +#define CS42XX8_PWRCTL_PDN_ADC3 (1 << CS42XX8_PWRCTL_PDN_ADC3_SHIFT) +#define CS42XX8_PWRCTL_PDN_ADC2_SHIFT 6 +#define CS42XX8_PWRCTL_PDN_ADC2_MASK (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT) +#define CS42XX8_PWRCTL_PDN_ADC2 (1 << CS42XX8_PWRCTL_PDN_ADC2_SHIFT) +#define CS42XX8_PWRCTL_PDN_ADC1_SHIFT 5 +#define CS42XX8_PWRCTL_PDN_ADC1_MASK (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT) +#define CS42XX8_PWRCTL_PDN_ADC1 (1 << CS42XX8_PWRCTL_PDN_ADC1_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC4_SHIFT 4 +#define CS42XX8_PWRCTL_PDN_DAC4_MASK (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC4 (1 << CS42XX8_PWRCTL_PDN_DAC4_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC3_SHIFT 3 +#define CS42XX8_PWRCTL_PDN_DAC3_MASK (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC3 (1 << CS42XX8_PWRCTL_PDN_DAC3_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC2_SHIFT 2 +#define CS42XX8_PWRCTL_PDN_DAC2_MASK (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC2 (1 << CS42XX8_PWRCTL_PDN_DAC2_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC1_SHIFT 1 +#define CS42XX8_PWRCTL_PDN_DAC1_MASK (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT) +#define CS42XX8_PWRCTL_PDN_DAC1 (1 << CS42XX8_PWRCTL_PDN_DAC1_SHIFT) +#define CS42XX8_PWRCTL_PDN_SHIFT 0 +#define CS42XX8_PWRCTL_PDN_MASK (1 << CS42XX8_PWRCTL_PDN_SHIFT) +#define CS42XX8_PWRCTL_PDN (1 << CS42XX8_PWRCTL_PDN_SHIFT) + +/* Functional Mode (Address 03h) */ +#define CS42XX8_FUNCMOD_DAC_FM_SHIFT 6 +#define CS42XX8_FUNCMOD_DAC_FM_WIDTH 2 +#define CS42XX8_FUNCMOD_DAC_FM_MASK (((1 << CS42XX8_FUNCMOD_DAC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_DAC_FM_SHIFT) +#define CS42XX8_FUNCMOD_DAC_FM(v) ((v) << CS42XX8_FUNCMOD_DAC_FM_SHIFT) +#define CS42XX8_FUNCMOD_ADC_FM_SHIFT 4 +#define CS42XX8_FUNCMOD_ADC_FM_WIDTH 2 +#define CS42XX8_FUNCMOD_ADC_FM_MASK (((1 << CS42XX8_FUNCMOD_ADC_FM_WIDTH) - 1) << CS42XX8_FUNCMOD_ADC_FM_SHIFT) +#define CS42XX8_FUNCMOD_ADC_FM(v) ((v) << CS42XX8_FUNCMOD_ADC_FM_SHIFT) +#define CS42XX8_FUNCMOD_xC_FM_MASK(x) ((x) ? CS42XX8_FUNCMOD_DAC_FM_MASK : CS42XX8_FUNCMOD_ADC_FM_MASK) +#define CS42XX8_FUNCMOD_xC_FM(x, v) ((x) ? CS42XX8_FUNCMOD_DAC_FM(v) : CS42XX8_FUNCMOD_ADC_FM(v)) +#define CS42XX8_FUNCMOD_MFREQ_SHIFT 1 +#define CS42XX8_FUNCMOD_MFREQ_WIDTH 3 +#define CS42XX8_FUNCMOD_MFREQ_MASK (((1 << CS42XX8_FUNCMOD_MFREQ_WIDTH) - 1) << CS42XX8_FUNCMOD_MFREQ_SHIFT) +#define CS42XX8_FUNCMOD_MFREQ_256(s) ((0 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1)) +#define CS42XX8_FUNCMOD_MFREQ_384(s) ((1 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1)) +#define CS42XX8_FUNCMOD_MFREQ_512(s) ((2 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1)) +#define CS42XX8_FUNCMOD_MFREQ_768(s) ((3 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1)) +#define CS42XX8_FUNCMOD_MFREQ_1024(s) ((4 << CS42XX8_FUNCMOD_MFREQ_SHIFT) >> (s >> 1)) + +#define CS42XX8_FM_SINGLE 0 +#define CS42XX8_FM_DOUBLE 1 +#define CS42XX8_FM_QUAD 2 +#define CS42XX8_FM_AUTO 3 + +/* Interface Formats (Address 04h) */ +#define CS42XX8_INTF_FREEZE_SHIFT 7 +#define CS42XX8_INTF_FREEZE_MASK (1 << CS42XX8_INTF_FREEZE_SHIFT) +#define CS42XX8_INTF_FREEZE (1 << CS42XX8_INTF_FREEZE_SHIFT) +#define CS42XX8_INTF_AUX_DIF_SHIFT 6 +#define CS42XX8_INTF_AUX_DIF_MASK (1 << CS42XX8_INTF_AUX_DIF_SHIFT) +#define CS42XX8_INTF_AUX_DIF (1 << CS42XX8_INTF_AUX_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_SHIFT 3 +#define CS42XX8_INTF_DAC_DIF_WIDTH 3 +#define CS42XX8_INTF_DAC_DIF_MASK (((1 << CS42XX8_INTF_DAC_DIF_WIDTH) - 1) << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_LEFTJ (0 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_I2S (1 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_RIGHTJ (2 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_ONELINE_20 (4 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_ONELINE_24 (5 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_DAC_DIF_TDM (6 << CS42XX8_INTF_DAC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_SHIFT 0 +#define CS42XX8_INTF_ADC_DIF_WIDTH 3 +#define CS42XX8_INTF_ADC_DIF_MASK (((1 << CS42XX8_INTF_ADC_DIF_WIDTH) - 1) << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_LEFTJ (0 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_I2S (1 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_RIGHTJ (2 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_ONELINE_20 (4 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_ONELINE_24 (5 << CS42XX8_INTF_ADC_DIF_SHIFT) +#define CS42XX8_INTF_ADC_DIF_TDM (6 << CS42XX8_INTF_ADC_DIF_SHIFT) + +/* ADC Control & DAC De-Emphasis (Address 05h) */ +#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT 7 +#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_MASK (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT) +#define CS42XX8_ADCCTL_ADC_HPF_FREEZE (1 << CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT) +#define CS42XX8_ADCCTL_DAC_DEM_SHIFT 5 +#define CS42XX8_ADCCTL_DAC_DEM_MASK (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT) +#define CS42XX8_ADCCTL_DAC_DEM (1 << CS42XX8_ADCCTL_DAC_DEM_SHIFT) +#define CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT 4 +#define CS42XX8_ADCCTL_ADC1_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_ADC1_SINGLE (1 << CS42XX8_ADCCTL_ADC1_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT 3 +#define CS42XX8_ADCCTL_ADC2_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_ADC2_SINGLE (1 << CS42XX8_ADCCTL_ADC2_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT 2 +#define CS42XX8_ADCCTL_ADC3_SINGLE_MASK (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_ADC3_SINGLE (1 << CS42XX8_ADCCTL_ADC3_SINGLE_SHIFT) +#define CS42XX8_ADCCTL_AIN5_MUX_SHIFT 1 +#define CS42XX8_ADCCTL_AIN5_MUX_MASK (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT) +#define CS42XX8_ADCCTL_AIN5_MUX (1 << CS42XX8_ADCCTL_AIN5_MUX_SHIFT) +#define CS42XX8_ADCCTL_AIN6_MUX_SHIFT 0 +#define CS42XX8_ADCCTL_AIN6_MUX_MASK (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT) +#define CS42XX8_ADCCTL_AIN6_MUX (1 << CS42XX8_ADCCTL_AIN6_MUX_SHIFT) + +/* Transition Control (Address 06h) */ +#define CS42XX8_TXCTL_DAC_SNGVOL_SHIFT 7 +#define CS42XX8_TXCTL_DAC_SNGVOL_MASK (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT) +#define CS42XX8_TXCTL_DAC_SNGVOL (1 << CS42XX8_TXCTL_DAC_SNGVOL_SHIFT) +#define CS42XX8_TXCTL_DAC_SZC_SHIFT 5 +#define CS42XX8_TXCTL_DAC_SZC_WIDTH 2 +#define CS42XX8_TXCTL_DAC_SZC_MASK (((1 << CS42XX8_TXCTL_DAC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_DAC_SZC_SHIFT) +#define CS42XX8_TXCTL_DAC_SZC_IC (0 << CS42XX8_TXCTL_DAC_SZC_SHIFT) +#define CS42XX8_TXCTL_DAC_SZC_ZC (1 << CS42XX8_TXCTL_DAC_SZC_SHIFT) +#define CS42XX8_TXCTL_DAC_SZC_SR (2 << CS42XX8_TXCTL_DAC_SZC_SHIFT) +#define CS42XX8_TXCTL_DAC_SZC_SRZC (3 << CS42XX8_TXCTL_DAC_SZC_SHIFT) +#define CS42XX8_TXCTL_AMUTE_SHIFT 4 +#define CS42XX8_TXCTL_AMUTE_MASK (1 << CS42XX8_TXCTL_AMUTE_SHIFT) +#define CS42XX8_TXCTL_AMUTE (1 << CS42XX8_TXCTL_AMUTE_SHIFT) +#define CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT 3 +#define CS42XX8_TXCTL_MUTE_ADC_SP_MASK (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT) +#define CS42XX8_TXCTL_MUTE_ADC_SP (1 << CS42XX8_TXCTL_MUTE_ADC_SP_SHIFT) +#define CS42XX8_TXCTL_ADC_SNGVOL_SHIFT 2 +#define CS42XX8_TXCTL_ADC_SNGVOL_MASK (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT) +#define CS42XX8_TXCTL_ADC_SNGVOL (1 << CS42XX8_TXCTL_ADC_SNGVOL_SHIFT) +#define CS42XX8_TXCTL_ADC_SZC_SHIFT 0 +#define CS42XX8_TXCTL_ADC_SZC_MASK (((1 << CS42XX8_TXCTL_ADC_SZC_WIDTH) - 1) << CS42XX8_TXCTL_ADC_SZC_SHIFT) +#define CS42XX8_TXCTL_ADC_SZC_IC (0 << CS42XX8_TXCTL_ADC_SZC_SHIFT) +#define CS42XX8_TXCTL_ADC_SZC_ZC (1 << CS42XX8_TXCTL_ADC_SZC_SHIFT) +#define CS42XX8_TXCTL_ADC_SZC_SR (2 << CS42XX8_TXCTL_ADC_SZC_SHIFT) +#define CS42XX8_TXCTL_ADC_SZC_SRZC (3 << CS42XX8_TXCTL_ADC_SZC_SHIFT) + +/* DAC Channel Mute (Address 07h) */ +#define CS42XX8_DACMUTE_AOUT(n) (0x1 << n) +#define CS42XX8_DACMUTE_ALL 0xff + +/* Status Control (Address 18h)*/ +#define CS42XX8_STATUSCTL_INI_SHIFT 2 +#define CS42XX8_STATUSCTL_INI_WIDTH 2 +#define CS42XX8_STATUSCTL_INI_MASK (((1 << CS42XX8_STATUSCTL_INI_WIDTH) - 1) << CS42XX8_STATUSCTL_INI_SHIFT) +#define CS42XX8_STATUSCTL_INT_ACTIVE_HIGH (0 << CS42XX8_STATUSCTL_INI_SHIFT) +#define CS42XX8_STATUSCTL_INT_ACTIVE_LOW (1 << CS42XX8_STATUSCTL_INI_SHIFT) +#define CS42XX8_STATUSCTL_INT_OPEN_DRAIN (2 << CS42XX8_STATUSCTL_INI_SHIFT) + +/* Status (Address 19h)*/ +#define CS42XX8_STATUS_DAC_CLK_ERR_SHIFT 4 +#define CS42XX8_STATUS_DAC_CLK_ERR_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_SHIFT) +#define CS42XX8_STATUS_ADC_CLK_ERR_SHIFT 3 +#define CS42XX8_STATUS_ADC_CLK_ERR_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_SHIFT) +#define CS42XX8_STATUS_ADC3_OVFL_SHIFT 2 +#define CS42XX8_STATUS_ADC3_OVFL_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_SHIFT) +#define CS42XX8_STATUS_ADC2_OVFL_SHIFT 1 +#define CS42XX8_STATUS_ADC2_OVFL_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_SHIFT) +#define CS42XX8_STATUS_ADC1_OVFL_SHIFT 0 +#define CS42XX8_STATUS_ADC1_OVFL_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_SHIFT) + +/* Status Mask (Address 1Ah) */ +#define CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT 4 +#define CS42XX8_STATUS_DAC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_DAC_CLK_ERR_M_SHIFT) +#define CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT 3 +#define CS42XX8_STATUS_ADC_CLK_ERR_M_MASK (1 << CS42XX8_STATUS_ADC_CLK_ERR_M_SHIFT) +#define CS42XX8_STATUS_ADC3_OVFL_M_SHIFT 2 +#define CS42XX8_STATUS_ADC3_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC3_OVFL_M_SHIFT) +#define CS42XX8_STATUS_ADC2_OVFL_M_SHIFT 1 +#define CS42XX8_STATUS_ADC2_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC2_OVFL_M_SHIFT) +#define CS42XX8_STATUS_ADC1_OVFL_M_SHIFT 0 +#define CS42XX8_STATUS_ADC1_OVFL_M_MASK (1 << CS42XX8_STATUS_ADC1_OVFL_M_SHIFT) + +/* MUTEC Pin Control (Address 1Bh) */ +#define CS42XX8_MUTEC_MCPOLARITY_SHIFT 1 +#define CS42XX8_MUTEC_MCPOLARITY_MASK (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT) +#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_LOW (0 << CS42XX8_MUTEC_MCPOLARITY_SHIFT) +#define CS42XX8_MUTEC_MCPOLARITY_ACTIVE_HIGH (1 << CS42XX8_MUTEC_MCPOLARITY_SHIFT) +#define CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT 0 +#define CS42XX8_MUTEC_MUTEC_ACTIVE_MASK (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT) +#define CS42XX8_MUTEC_MUTEC_ACTIVE (1 << CS42XX8_MUTEC_MUTEC_ACTIVE_SHIFT) +#endif /* _CS42XX8_H */ diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c new file mode 100644 index 0000000000..206008bdec --- /dev/null +++ b/sound/soc/codecs/cs43130.c @@ -0,0 +1,2708 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs43130.c -- CS43130 ALSA Soc Audio driver + * + * Copyright 2017 Cirrus Logic, Inc. + * + * Authors: Li Xu + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs43130.h" +#include "cirrus_legacy.h" + +static const struct reg_default cs43130_reg_defaults[] = { + {CS43130_SYS_CLK_CTL_1, 0x06}, + {CS43130_SP_SRATE, 0x01}, + {CS43130_SP_BITSIZE, 0x05}, + {CS43130_PAD_INT_CFG, 0x03}, + {CS43130_PWDN_CTL, 0xFE}, + {CS43130_CRYSTAL_SET, 0x04}, + {CS43130_PLL_SET_1, 0x00}, + {CS43130_PLL_SET_2, 0x00}, + {CS43130_PLL_SET_3, 0x00}, + {CS43130_PLL_SET_4, 0x00}, + {CS43130_PLL_SET_5, 0x40}, + {CS43130_PLL_SET_6, 0x10}, + {CS43130_PLL_SET_7, 0x80}, + {CS43130_PLL_SET_8, 0x03}, + {CS43130_PLL_SET_9, 0x02}, + {CS43130_PLL_SET_10, 0x02}, + {CS43130_CLKOUT_CTL, 0x00}, + {CS43130_ASP_NUM_1, 0x01}, + {CS43130_ASP_NUM_2, 0x00}, + {CS43130_ASP_DEN_1, 0x08}, + {CS43130_ASP_DEN_2, 0x00}, + {CS43130_ASP_LRCK_HI_TIME_1, 0x1F}, + {CS43130_ASP_LRCK_HI_TIME_2, 0x00}, + {CS43130_ASP_LRCK_PERIOD_1, 0x3F}, + {CS43130_ASP_LRCK_PERIOD_2, 0x00}, + {CS43130_ASP_CLOCK_CONF, 0x0C}, + {CS43130_ASP_FRAME_CONF, 0x0A}, + {CS43130_XSP_NUM_1, 0x01}, + {CS43130_XSP_NUM_2, 0x00}, + {CS43130_XSP_DEN_1, 0x02}, + {CS43130_XSP_DEN_2, 0x00}, + {CS43130_XSP_LRCK_HI_TIME_1, 0x1F}, + {CS43130_XSP_LRCK_HI_TIME_2, 0x00}, + {CS43130_XSP_LRCK_PERIOD_1, 0x3F}, + {CS43130_XSP_LRCK_PERIOD_2, 0x00}, + {CS43130_XSP_CLOCK_CONF, 0x0C}, + {CS43130_XSP_FRAME_CONF, 0x0A}, + {CS43130_ASP_CH_1_LOC, 0x00}, + {CS43130_ASP_CH_2_LOC, 0x00}, + {CS43130_ASP_CH_1_SZ_EN, 0x06}, + {CS43130_ASP_CH_2_SZ_EN, 0x0E}, + {CS43130_XSP_CH_1_LOC, 0x00}, + {CS43130_XSP_CH_2_LOC, 0x00}, + {CS43130_XSP_CH_1_SZ_EN, 0x06}, + {CS43130_XSP_CH_2_SZ_EN, 0x0E}, + {CS43130_DSD_VOL_B, 0x78}, + {CS43130_DSD_VOL_A, 0x78}, + {CS43130_DSD_PATH_CTL_1, 0xA8}, + {CS43130_DSD_INT_CFG, 0x00}, + {CS43130_DSD_PATH_CTL_2, 0x02}, + {CS43130_DSD_PCM_MIX_CTL, 0x00}, + {CS43130_DSD_PATH_CTL_3, 0x40}, + {CS43130_HP_OUT_CTL_1, 0x30}, + {CS43130_PCM_FILT_OPT, 0x02}, + {CS43130_PCM_VOL_B, 0x78}, + {CS43130_PCM_VOL_A, 0x78}, + {CS43130_PCM_PATH_CTL_1, 0xA8}, + {CS43130_PCM_PATH_CTL_2, 0x00}, + {CS43130_CLASS_H_CTL, 0x1E}, + {CS43130_HP_DETECT, 0x04}, + {CS43130_HP_LOAD_1, 0x00}, + {CS43130_HP_MEAS_LOAD_1, 0x00}, + {CS43130_HP_MEAS_LOAD_2, 0x00}, + {CS43130_INT_MASK_1, 0xFF}, + {CS43130_INT_MASK_2, 0xFF}, + {CS43130_INT_MASK_3, 0xFF}, + {CS43130_INT_MASK_4, 0xFF}, + {CS43130_INT_MASK_5, 0xFF}, +}; + +static bool cs43130_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5: + case CS43130_HP_DC_STAT_1 ... CS43130_HP_DC_STAT_2: + case CS43130_HP_AC_STAT_1 ... CS43130_HP_AC_STAT_2: + return true; + default: + return false; + } +} + +static bool cs43130_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS43130_DEVID_AB ... CS43130_SYS_CLK_CTL_1: + case CS43130_SP_SRATE ... CS43130_PAD_INT_CFG: + case CS43130_PWDN_CTL: + case CS43130_CRYSTAL_SET: + case CS43130_PLL_SET_1 ... CS43130_PLL_SET_5: + case CS43130_PLL_SET_6: + case CS43130_PLL_SET_7: + case CS43130_PLL_SET_8: + case CS43130_PLL_SET_9: + case CS43130_PLL_SET_10: + case CS43130_CLKOUT_CTL: + case CS43130_ASP_NUM_1 ... CS43130_ASP_FRAME_CONF: + case CS43130_XSP_NUM_1 ... CS43130_XSP_FRAME_CONF: + case CS43130_ASP_CH_1_LOC: + case CS43130_ASP_CH_2_LOC: + case CS43130_ASP_CH_1_SZ_EN: + case CS43130_ASP_CH_2_SZ_EN: + case CS43130_XSP_CH_1_LOC: + case CS43130_XSP_CH_2_LOC: + case CS43130_XSP_CH_1_SZ_EN: + case CS43130_XSP_CH_2_SZ_EN: + case CS43130_DSD_VOL_B ... CS43130_DSD_PATH_CTL_3: + case CS43130_HP_OUT_CTL_1: + case CS43130_PCM_FILT_OPT ... CS43130_PCM_PATH_CTL_2: + case CS43130_CLASS_H_CTL: + case CS43130_HP_DETECT: + case CS43130_HP_STATUS: + case CS43130_HP_LOAD_1: + case CS43130_HP_MEAS_LOAD_1: + case CS43130_HP_MEAS_LOAD_2: + case CS43130_HP_DC_STAT_1: + case CS43130_HP_DC_STAT_2: + case CS43130_HP_AC_STAT_1: + case CS43130_HP_AC_STAT_2: + case CS43130_HP_LOAD_STAT: + case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5: + case CS43130_INT_MASK_1 ... CS43130_INT_MASK_5: + return true; + default: + return false; + } +} + +static bool cs43130_precious_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS43130_INT_STATUS_1 ... CS43130_INT_STATUS_5: + return true; + default: + return false; + } +} + +struct cs43130_pll_params { + unsigned int pll_in; + u8 sclk_prediv; + u8 pll_div_int; + u32 pll_div_frac; + u8 pll_mode; + u8 pll_divout; + unsigned int pll_out; + u8 pll_cal_ratio; +}; + +static const struct cs43130_pll_params pll_ratio_table[] = { + {9600000, 0x02, 0x49, 0x800000, 0x00, 0x08, 22579200, 151}, + {9600000, 0x02, 0x50, 0x000000, 0x00, 0x08, 24576000, 164}, + + {11289600, 0x02, 0X40, 0, 0x01, 0x08, 22579200, 128}, + {11289600, 0x02, 0x44, 0x06F700, 0x0, 0x08, 24576000, 139}, + + {12000000, 0x02, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120}, + {12000000, 0x02, 0x40, 0x000000, 0x00, 0x08, 24576000, 131}, + + {12288000, 0x02, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118}, + {12288000, 0x02, 0x40, 0x000000, 0x01, 0x08, 24576000, 128}, + + {13000000, 0x02, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111}, + {13000000, 0x02, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121}, + + {19200000, 0x03, 0x49, 0x800000, 0x00, 0x08, 22579200, 151}, + {19200000, 0x03, 0x50, 0x000000, 0x00, 0x08, 24576000, 164}, + + {22579200, 0, 0, 0, 0, 0, 22579200, 0}, + {22579200, 0x03, 0x44, 0x06F700, 0x00, 0x08, 24576000, 139}, + + {24000000, 0x03, 0x49, 0x800000, 0x00, 0x0A, 22579200, 120}, + {24000000, 0x03, 0x40, 0x000000, 0x00, 0x08, 24576000, 131}, + + {24576000, 0x03, 0x49, 0x800000, 0x01, 0x0A, 22579200, 118}, + {24576000, 0, 0, 0, 0, 0, 24576000, 0}, + + {26000000, 0x03, 0x45, 0x797680, 0x01, 0x0A, 22579200, 111}, + {26000000, 0x03, 0x3C, 0x7EA940, 0x01, 0x08, 24576000, 121}, +}; + +static const struct cs43130_pll_params *cs43130_get_pll_table( + unsigned int freq_in, unsigned int freq_out) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) { + if (pll_ratio_table[i].pll_in == freq_in && + pll_ratio_table[i].pll_out == freq_out) + return &pll_ratio_table[i]; + } + + return NULL; +} + +static int cs43130_pll_config(struct snd_soc_component *component) +{ + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + const struct cs43130_pll_params *pll_entry; + + dev_dbg(component->dev, "cs43130->mclk = %u, cs43130->mclk_int = %u\n", + cs43130->mclk, cs43130->mclk_int); + + pll_entry = cs43130_get_pll_table(cs43130->mclk, cs43130->mclk_int); + if (!pll_entry) + return -EINVAL; + + if (pll_entry->pll_cal_ratio == 0) { + regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_1, + CS43130_PLL_START_MASK, 0); + + cs43130->pll_bypass = true; + return 0; + } + + cs43130->pll_bypass = false; + + regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_2, + CS43130_PLL_DIV_DATA_MASK, + pll_entry->pll_div_frac >> + CS43130_PLL_DIV_FRAC_0_DATA_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_3, + CS43130_PLL_DIV_DATA_MASK, + pll_entry->pll_div_frac >> + CS43130_PLL_DIV_FRAC_1_DATA_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_4, + CS43130_PLL_DIV_DATA_MASK, + pll_entry->pll_div_frac >> + CS43130_PLL_DIV_FRAC_2_DATA_SHIFT); + regmap_write(cs43130->regmap, CS43130_PLL_SET_5, + pll_entry->pll_div_int); + regmap_write(cs43130->regmap, CS43130_PLL_SET_6, pll_entry->pll_divout); + regmap_write(cs43130->regmap, CS43130_PLL_SET_7, + pll_entry->pll_cal_ratio); + regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_8, + CS43130_PLL_MODE_MASK, + pll_entry->pll_mode << CS43130_PLL_MODE_SHIFT); + regmap_write(cs43130->regmap, CS43130_PLL_SET_9, + pll_entry->sclk_prediv); + regmap_update_bits(cs43130->regmap, CS43130_PLL_SET_1, + CS43130_PLL_START_MASK, 1); + + return 0; +} + +static int cs43130_set_pll(struct snd_soc_component *component, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + int ret = 0; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + + switch (freq_in) { + case 9600000: + case 11289600: + case 12000000: + case 12288000: + case 13000000: + case 19200000: + case 22579200: + case 24000000: + case 24576000: + case 26000000: + cs43130->mclk = freq_in; + break; + default: + dev_err(component->dev, + "unsupported pll input reference clock:%d\n", freq_in); + return -EINVAL; + } + + switch (freq_out) { + case 22579200: + cs43130->mclk_int = freq_out; + break; + case 24576000: + cs43130->mclk_int = freq_out; + break; + default: + dev_err(component->dev, + "unsupported pll output ref clock: %u\n", freq_out); + return -EINVAL; + } + + ret = cs43130_pll_config(component); + dev_dbg(component->dev, "cs43130->pll_bypass = %d", cs43130->pll_bypass); + return ret; +} + +static int cs43130_change_clksrc(struct snd_soc_component *component, + enum cs43130_mclk_src_sel src) +{ + int ret; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + int mclk_int_decoded; + + if (src == cs43130->mclk_int_src) { + /* clk source has not changed */ + return 0; + } + + switch (cs43130->mclk_int) { + case CS43130_MCLK_22M: + mclk_int_decoded = CS43130_MCLK_22P5; + break; + case CS43130_MCLK_24M: + mclk_int_decoded = CS43130_MCLK_24P5; + break; + default: + dev_err(component->dev, "Invalid MCLK INT freq: %u\n", cs43130->mclk_int); + return -EINVAL; + } + + switch (src) { + case CS43130_MCLK_SRC_EXT: + cs43130->pll_bypass = true; + cs43130->mclk_int_src = CS43130_MCLK_SRC_EXT; + if (cs43130->xtal_ibias == CS43130_XTAL_UNUSED) { + regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, + CS43130_PDN_XTAL_MASK, + 1 << CS43130_PDN_XTAL_SHIFT); + } else { + reinit_completion(&cs43130->xtal_rdy); + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_XTAL_RDY_INT_MASK, 0); + regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, + CS43130_PDN_XTAL_MASK, 0); + ret = wait_for_completion_timeout(&cs43130->xtal_rdy, + msecs_to_jiffies(100)); + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_XTAL_RDY_INT_MASK, + 1 << CS43130_XTAL_RDY_INT_SHIFT); + if (ret == 0) { + dev_err(component->dev, "Timeout waiting for XTAL_READY interrupt\n"); + return -ETIMEDOUT; + } + } + + regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, + CS43130_MCLK_SRC_SEL_MASK, + src << CS43130_MCLK_SRC_SEL_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, + CS43130_MCLK_INT_MASK, + mclk_int_decoded << CS43130_MCLK_INT_SHIFT); + usleep_range(150, 200); + + regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, + CS43130_PDN_PLL_MASK, + 1 << CS43130_PDN_PLL_SHIFT); + break; + case CS43130_MCLK_SRC_PLL: + cs43130->pll_bypass = false; + cs43130->mclk_int_src = CS43130_MCLK_SRC_PLL; + if (cs43130->xtal_ibias == CS43130_XTAL_UNUSED) { + regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, + CS43130_PDN_XTAL_MASK, + 1 << CS43130_PDN_XTAL_SHIFT); + } else { + reinit_completion(&cs43130->xtal_rdy); + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_XTAL_RDY_INT_MASK, 0); + regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, + CS43130_PDN_XTAL_MASK, 0); + ret = wait_for_completion_timeout(&cs43130->xtal_rdy, + msecs_to_jiffies(100)); + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_XTAL_RDY_INT_MASK, + 1 << CS43130_XTAL_RDY_INT_SHIFT); + if (ret == 0) { + dev_err(component->dev, "Timeout waiting for XTAL_READY interrupt\n"); + return -ETIMEDOUT; + } + } + + reinit_completion(&cs43130->pll_rdy); + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_PLL_RDY_INT_MASK, 0); + regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, + CS43130_PDN_PLL_MASK, 0); + ret = wait_for_completion_timeout(&cs43130->pll_rdy, + msecs_to_jiffies(100)); + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_PLL_RDY_INT_MASK, + 1 << CS43130_PLL_RDY_INT_SHIFT); + if (ret == 0) { + dev_err(component->dev, "Timeout waiting for PLL_READY interrupt\n"); + return -ETIMEDOUT; + } + + regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, + CS43130_MCLK_SRC_SEL_MASK, + src << CS43130_MCLK_SRC_SEL_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, + CS43130_MCLK_INT_MASK, + mclk_int_decoded << CS43130_MCLK_INT_SHIFT); + usleep_range(150, 200); + break; + case CS43130_MCLK_SRC_RCO: + cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO; + + regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, + CS43130_MCLK_SRC_SEL_MASK, + src << CS43130_MCLK_SRC_SEL_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_SYS_CLK_CTL_1, + CS43130_MCLK_INT_MASK, + CS43130_MCLK_22P5 << CS43130_MCLK_INT_SHIFT); + usleep_range(150, 200); + + regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, + CS43130_PDN_XTAL_MASK, + 1 << CS43130_PDN_XTAL_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_PWDN_CTL, + CS43130_PDN_PLL_MASK, + 1 << CS43130_PDN_PLL_SHIFT); + break; + default: + dev_err(component->dev, "Invalid MCLK source value\n"); + return -EINVAL; + } + + return 0; +} + +static const struct cs43130_bitwidth_map cs43130_bitwidth_table[] = { + {8, CS43130_SP_BIT_SIZE_8, CS43130_CH_BIT_SIZE_8}, + {16, CS43130_SP_BIT_SIZE_16, CS43130_CH_BIT_SIZE_16}, + {24, CS43130_SP_BIT_SIZE_24, CS43130_CH_BIT_SIZE_24}, + {32, CS43130_SP_BIT_SIZE_32, CS43130_CH_BIT_SIZE_32}, +}; + +static const struct cs43130_bitwidth_map *cs43130_get_bitwidth_table( + unsigned int bitwidth) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs43130_bitwidth_table); i++) { + if (cs43130_bitwidth_table[i].bitwidth == bitwidth) + return &cs43130_bitwidth_table[i]; + } + + return NULL; +} + +static int cs43130_set_bitwidth(int dai_id, unsigned int bitwidth_dai, + struct regmap *regmap) +{ + const struct cs43130_bitwidth_map *bw_map; + + bw_map = cs43130_get_bitwidth_table(bitwidth_dai); + if (!bw_map) + return -EINVAL; + + switch (dai_id) { + case CS43130_ASP_PCM_DAI: + case CS43130_ASP_DOP_DAI: + regmap_update_bits(regmap, CS43130_ASP_CH_1_SZ_EN, + CS43130_CH_BITSIZE_MASK, bw_map->ch_bit); + regmap_update_bits(regmap, CS43130_ASP_CH_2_SZ_EN, + CS43130_CH_BITSIZE_MASK, bw_map->ch_bit); + regmap_update_bits(regmap, CS43130_SP_BITSIZE, + CS43130_ASP_BITSIZE_MASK, bw_map->sp_bit); + break; + case CS43130_XSP_DOP_DAI: + regmap_update_bits(regmap, CS43130_XSP_CH_1_SZ_EN, + CS43130_CH_BITSIZE_MASK, bw_map->ch_bit); + regmap_update_bits(regmap, CS43130_XSP_CH_2_SZ_EN, + CS43130_CH_BITSIZE_MASK, bw_map->ch_bit); + regmap_update_bits(regmap, CS43130_SP_BITSIZE, + CS43130_XSP_BITSIZE_MASK, bw_map->sp_bit << + CS43130_XSP_BITSIZE_SHIFT); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct cs43130_rate_map cs43130_rate_table[] = { + {32000, CS43130_ASP_SPRATE_32K}, + {44100, CS43130_ASP_SPRATE_44_1K}, + {48000, CS43130_ASP_SPRATE_48K}, + {88200, CS43130_ASP_SPRATE_88_2K}, + {96000, CS43130_ASP_SPRATE_96K}, + {176400, CS43130_ASP_SPRATE_176_4K}, + {192000, CS43130_ASP_SPRATE_192K}, + {352800, CS43130_ASP_SPRATE_352_8K}, + {384000, CS43130_ASP_SPRATE_384K}, +}; + +static const struct cs43130_rate_map *cs43130_get_rate_table(int fs) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs43130_rate_table); i++) { + if (cs43130_rate_table[i].fs == fs) + return &cs43130_rate_table[i]; + } + + return NULL; +} + +static const struct cs43130_clk_gen *cs43130_get_clk_gen(int mclk_int, int fs, + const struct cs43130_clk_gen *clk_gen_table, int len_clk_gen_table) +{ + int i; + + for (i = 0; i < len_clk_gen_table; i++) { + if (clk_gen_table[i].mclk_int == mclk_int && + clk_gen_table[i].fs == fs) + return &clk_gen_table[i]; + } + + return NULL; +} + +static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk, + struct snd_pcm_hw_params *params, + struct cs43130_private *cs43130) +{ + u16 frm_size; + u16 hi_size; + u8 frm_delay; + u8 frm_phase; + u8 frm_data; + u8 sclk_edge; + u8 lrck_edge; + u8 clk_data; + u8 loc_ch1; + u8 loc_ch2; + u8 dai_mode_val; + const struct cs43130_clk_gen *clk_gen; + + switch (cs43130->dais[dai_id].dai_format) { + case SND_SOC_DAIFMT_I2S: + hi_size = bitwidth_sclk; + frm_delay = 2; + frm_phase = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + hi_size = bitwidth_sclk; + frm_delay = 0; + frm_phase = 1; + break; + case SND_SOC_DAIFMT_DSP_A: + hi_size = 1; + frm_delay = 2; + frm_phase = 1; + break; + case SND_SOC_DAIFMT_DSP_B: + hi_size = 1; + frm_delay = 0; + frm_phase = 1; + break; + default: + return -EINVAL; + } + + switch (cs43130->dais[dai_id].dai_mode) { + case SND_SOC_DAIFMT_CBS_CFS: + dai_mode_val = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + dai_mode_val = 1; + break; + default: + return -EINVAL; + } + + frm_size = bitwidth_sclk * params_channels(params); + sclk_edge = 1; + lrck_edge = 0; + loc_ch1 = 0; + loc_ch2 = bitwidth_sclk * (params_channels(params) - 1); + + frm_data = frm_delay & CS43130_SP_FSD_MASK; + frm_data |= (frm_phase << CS43130_SP_STP_SHIFT) & CS43130_SP_STP_MASK; + + clk_data = lrck_edge & CS43130_SP_LCPOL_IN_MASK; + clk_data |= (lrck_edge << CS43130_SP_LCPOL_OUT_SHIFT) & + CS43130_SP_LCPOL_OUT_MASK; + clk_data |= (sclk_edge << CS43130_SP_SCPOL_IN_SHIFT) & + CS43130_SP_SCPOL_IN_MASK; + clk_data |= (sclk_edge << CS43130_SP_SCPOL_OUT_SHIFT) & + CS43130_SP_SCPOL_OUT_MASK; + clk_data |= (dai_mode_val << CS43130_SP_MODE_SHIFT) & + CS43130_SP_MODE_MASK; + + switch (dai_id) { + case CS43130_ASP_PCM_DAI: + case CS43130_ASP_DOP_DAI: + regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_1, + CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >> + CS43130_SP_LCPR_LSB_DATA_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_PERIOD_2, + CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >> + CS43130_SP_LCPR_MSB_DATA_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_1, + CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >> + CS43130_SP_LCHI_LSB_DATA_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_ASP_LRCK_HI_TIME_2, + CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >> + CS43130_SP_LCHI_MSB_DATA_SHIFT); + regmap_write(cs43130->regmap, CS43130_ASP_FRAME_CONF, frm_data); + regmap_write(cs43130->regmap, CS43130_ASP_CH_1_LOC, loc_ch1); + regmap_write(cs43130->regmap, CS43130_ASP_CH_2_LOC, loc_ch2); + regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_1_SZ_EN, + CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_ASP_CH_2_SZ_EN, + CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT); + regmap_write(cs43130->regmap, CS43130_ASP_CLOCK_CONF, clk_data); + break; + case CS43130_XSP_DOP_DAI: + regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_PERIOD_1, + CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >> + CS43130_SP_LCPR_LSB_DATA_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_PERIOD_2, + CS43130_SP_LCPR_DATA_MASK, (frm_size - 1) >> + CS43130_SP_LCPR_MSB_DATA_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_HI_TIME_1, + CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >> + CS43130_SP_LCHI_LSB_DATA_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_XSP_LRCK_HI_TIME_2, + CS43130_SP_LCHI_DATA_MASK, (hi_size - 1) >> + CS43130_SP_LCHI_MSB_DATA_SHIFT); + regmap_write(cs43130->regmap, CS43130_XSP_FRAME_CONF, frm_data); + regmap_write(cs43130->regmap, CS43130_XSP_CH_1_LOC, loc_ch1); + regmap_write(cs43130->regmap, CS43130_XSP_CH_2_LOC, loc_ch2); + regmap_update_bits(cs43130->regmap, CS43130_XSP_CH_1_SZ_EN, + CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_XSP_CH_2_SZ_EN, + CS43130_CH_EN_MASK, 1 << CS43130_CH_EN_SHIFT); + regmap_write(cs43130->regmap, CS43130_XSP_CLOCK_CONF, clk_data); + break; + default: + return -EINVAL; + } + + switch (frm_size) { + case 16: + clk_gen = cs43130_get_clk_gen(cs43130->mclk_int, + params_rate(params), + cs43130_16_clk_gen, + ARRAY_SIZE(cs43130_16_clk_gen)); + break; + case 32: + clk_gen = cs43130_get_clk_gen(cs43130->mclk_int, + params_rate(params), + cs43130_32_clk_gen, + ARRAY_SIZE(cs43130_32_clk_gen)); + break; + case 48: + clk_gen = cs43130_get_clk_gen(cs43130->mclk_int, + params_rate(params), + cs43130_48_clk_gen, + ARRAY_SIZE(cs43130_48_clk_gen)); + break; + case 64: + clk_gen = cs43130_get_clk_gen(cs43130->mclk_int, + params_rate(params), + cs43130_64_clk_gen, + ARRAY_SIZE(cs43130_64_clk_gen)); + break; + default: + return -EINVAL; + } + + if (!clk_gen) + return -EINVAL; + + switch (dai_id) { + case CS43130_ASP_PCM_DAI: + case CS43130_ASP_DOP_DAI: + regmap_write(cs43130->regmap, CS43130_ASP_DEN_1, + (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >> + CS43130_SP_M_LSB_DATA_SHIFT); + regmap_write(cs43130->regmap, CS43130_ASP_DEN_2, + (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >> + CS43130_SP_M_MSB_DATA_SHIFT); + regmap_write(cs43130->regmap, CS43130_ASP_NUM_1, + (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >> + CS43130_SP_N_LSB_DATA_SHIFT); + regmap_write(cs43130->regmap, CS43130_ASP_NUM_2, + (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >> + CS43130_SP_N_MSB_DATA_SHIFT); + break; + case CS43130_XSP_DOP_DAI: + regmap_write(cs43130->regmap, CS43130_XSP_DEN_1, + (clk_gen->v.denominator & CS43130_SP_M_LSB_DATA_MASK) >> + CS43130_SP_M_LSB_DATA_SHIFT); + regmap_write(cs43130->regmap, CS43130_XSP_DEN_2, + (clk_gen->v.denominator & CS43130_SP_M_MSB_DATA_MASK) >> + CS43130_SP_M_MSB_DATA_SHIFT); + regmap_write(cs43130->regmap, CS43130_XSP_NUM_1, + (clk_gen->v.numerator & CS43130_SP_N_LSB_DATA_MASK) >> + CS43130_SP_N_LSB_DATA_SHIFT); + regmap_write(cs43130->regmap, CS43130_XSP_NUM_2, + (clk_gen->v.numerator & CS43130_SP_N_MSB_DATA_MASK) >> + CS43130_SP_N_MSB_DATA_SHIFT); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs43130_pcm_dsd_mix(bool en, struct regmap *regmap) +{ + if (en) { + regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL, + CS43130_MIX_PCM_PREP_MASK, + 1 << CS43130_MIX_PCM_PREP_SHIFT); + usleep_range(6000, 6050); + regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL, + CS43130_MIX_PCM_DSD_MASK, + 1 << CS43130_MIX_PCM_DSD_SHIFT); + } else { + regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL, + CS43130_MIX_PCM_DSD_MASK, + 0 << CS43130_MIX_PCM_DSD_SHIFT); + usleep_range(1600, 1650); + regmap_update_bits(regmap, CS43130_DSD_PCM_MIX_CTL, + CS43130_MIX_PCM_PREP_MASK, + 0 << CS43130_MIX_PCM_PREP_SHIFT); + } + + return 0; +} + +static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + unsigned int required_clk; + u8 dsd_speed; + + mutex_lock(&cs43130->clk_mutex); + if (!cs43130->clk_req) { + /* no DAI is currently using clk */ + if (!(CS43130_MCLK_22M % params_rate(params))) + required_clk = CS43130_MCLK_22M; + else + required_clk = CS43130_MCLK_24M; + + cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); + if (cs43130->pll_bypass) + cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); + else + cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + } + + cs43130->clk_req++; + if (cs43130->clk_req == 2) + cs43130_pcm_dsd_mix(true, cs43130->regmap); + mutex_unlock(&cs43130->clk_mutex); + + switch (params_rate(params)) { + case 176400: + dsd_speed = 0; + break; + case 352800: + dsd_speed = 1; + break; + default: + dev_err(component->dev, "Rate(%u) not supported\n", + params_rate(params)); + return -EINVAL; + } + + if (cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM) + regmap_update_bits(cs43130->regmap, CS43130_DSD_INT_CFG, + CS43130_DSD_MASTER, CS43130_DSD_MASTER); + else + regmap_update_bits(cs43130->regmap, CS43130_DSD_INT_CFG, + CS43130_DSD_MASTER, 0); + + regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, + CS43130_DSD_SPEED_MASK, + dsd_speed << CS43130_DSD_SPEED_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, + CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_DSD << + CS43130_DSD_SRC_SHIFT); + + return 0; +} + +static int cs43130_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + const struct cs43130_rate_map *rate_map; + unsigned int sclk = cs43130->dais[dai->id].sclk; + unsigned int bitwidth_sclk; + unsigned int bitwidth_dai = (unsigned int)(params_width(params)); + unsigned int required_clk; + u8 dsd_speed; + + mutex_lock(&cs43130->clk_mutex); + if (!cs43130->clk_req) { + /* no DAI is currently using clk */ + if (!(CS43130_MCLK_22M % params_rate(params))) + required_clk = CS43130_MCLK_22M; + else + required_clk = CS43130_MCLK_24M; + + cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk); + if (cs43130->pll_bypass) + cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); + else + cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + } + + cs43130->clk_req++; + if (cs43130->clk_req == 2) + cs43130_pcm_dsd_mix(true, cs43130->regmap); + mutex_unlock(&cs43130->clk_mutex); + + switch (dai->id) { + case CS43130_ASP_DOP_DAI: + case CS43130_XSP_DOP_DAI: + /* DoP bitwidth is always 24-bit */ + bitwidth_dai = 24; + sclk = params_rate(params) * bitwidth_dai * + params_channels(params); + + switch (params_rate(params)) { + case 176400: + dsd_speed = 0; + break; + case 352800: + dsd_speed = 1; + break; + default: + dev_err(component->dev, "Rate(%u) not supported\n", + params_rate(params)); + return -EINVAL; + } + + regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, + CS43130_DSD_SPEED_MASK, + dsd_speed << CS43130_DSD_SPEED_SHIFT); + break; + case CS43130_ASP_PCM_DAI: + rate_map = cs43130_get_rate_table(params_rate(params)); + if (!rate_map) + return -EINVAL; + + regmap_write(cs43130->regmap, CS43130_SP_SRATE, rate_map->val); + break; + default: + dev_err(component->dev, "Invalid DAI (%d)\n", dai->id); + return -EINVAL; + } + + switch (dai->id) { + case CS43130_ASP_DOP_DAI: + regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, + CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_ASP << + CS43130_DSD_SRC_SHIFT); + break; + case CS43130_XSP_DOP_DAI: + regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, + CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_XSP << + CS43130_DSD_SRC_SHIFT); + break; + } + + if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM) + /* Calculate SCLK in master mode if unassigned */ + sclk = params_rate(params) * bitwidth_dai * + params_channels(params); + + if (!sclk) { + /* at this point, SCLK must be set */ + dev_err(component->dev, "SCLK freq is not set\n"); + return -EINVAL; + } + + bitwidth_sclk = (sclk / params_rate(params)) / params_channels(params); + if (bitwidth_sclk < bitwidth_dai) { + dev_err(component->dev, "Format not supported: SCLK freq is too low\n"); + return -EINVAL; + } + + dev_dbg(component->dev, + "sclk = %u, fs = %d, bitwidth_dai = %u\n", + sclk, params_rate(params), bitwidth_dai); + + dev_dbg(component->dev, + "bitwidth_sclk = %u, num_ch = %u\n", + bitwidth_sclk, params_channels(params)); + + cs43130_set_bitwidth(dai->id, bitwidth_dai, cs43130->regmap); + cs43130_set_sp_fmt(dai->id, bitwidth_sclk, params, cs43130); + + return 0; +} + +static int cs43130_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + + mutex_lock(&cs43130->clk_mutex); + cs43130->clk_req--; + if (!cs43130->clk_req) { + /* no DAI is currently using clk */ + cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO); + cs43130_pcm_dsd_mix(false, cs43130->regmap); + } + mutex_unlock(&cs43130->clk_mutex); + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(pcm_vol_tlv, -12750, 50, 1); + +static const char * const pcm_ch_text[] = { + "Left-Right Ch", + "Left-Left Ch", + "Right-Left Ch", + "Right-Right Ch", +}; + +static const struct reg_sequence pcm_ch_en_seq[] = { + {CS43130_DXD1, 0x99}, + {0x180005, 0x8C}, + {0x180007, 0xAB}, + {0x180015, 0x31}, + {0x180017, 0xB2}, + {0x180025, 0x30}, + {0x180027, 0x84}, + {0x180035, 0x9C}, + {0x180037, 0xAE}, + {0x18000D, 0x24}, + {0x18000F, 0xA3}, + {0x18001D, 0x05}, + {0x18001F, 0xD4}, + {0x18002D, 0x0B}, + {0x18002F, 0xC7}, + {0x18003D, 0x71}, + {0x18003F, 0xE7}, + {CS43130_DXD1, 0}, +}; + +static const struct reg_sequence pcm_ch_dis_seq[] = { + {CS43130_DXD1, 0x99}, + {0x180005, 0x24}, + {0x180007, 0xA3}, + {0x180015, 0x05}, + {0x180017, 0xD4}, + {0x180025, 0x0B}, + {0x180027, 0xC7}, + {0x180035, 0x71}, + {0x180037, 0xE7}, + {0x18000D, 0x8C}, + {0x18000F, 0xAB}, + {0x18001D, 0x31}, + {0x18001F, 0xB2}, + {0x18002D, 0x30}, + {0x18002F, 0x84}, + {0x18003D, 0x9C}, + {0x18003F, 0xAE}, + {CS43130_DXD1, 0}, +}; + +static int cs43130_pcm_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return snd_soc_get_enum_double(kcontrol, ucontrol); +} + +static int cs43130_pcm_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (item[0] >= e->items) + return -EINVAL; + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + switch (cs43130->dev_id) { + case CS43131_CHIP_ID: + case CS43198_CHIP_ID: + if (val >= 2) + regmap_multi_reg_write(cs43130->regmap, pcm_ch_en_seq, + ARRAY_SIZE(pcm_ch_en_seq)); + else + regmap_multi_reg_write(cs43130->regmap, pcm_ch_dis_seq, + ARRAY_SIZE(pcm_ch_dis_seq)); + break; + } + + return snd_soc_put_enum_double(kcontrol, ucontrol); +} + +static SOC_ENUM_SINGLE_DECL(pcm_ch_enum, CS43130_PCM_PATH_CTL_2, 0, + pcm_ch_text); + +static const char * const pcm_spd_texts[] = { + "Fast", + "Slow", +}; + +static SOC_ENUM_SINGLE_DECL(pcm_spd_enum, CS43130_PCM_FILT_OPT, 7, + pcm_spd_texts); + +static const char * const dsd_texts[] = { + "Off", + "BCKA Mode", + "BCKD Mode", +}; + +static const unsigned int dsd_values[] = { + CS43130_DSD_SRC_DSD, + CS43130_DSD_SRC_ASP, + CS43130_DSD_SRC_XSP, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dsd_enum, CS43130_DSD_INT_CFG, 0, 0x03, + dsd_texts, dsd_values); + +static const struct snd_kcontrol_new cs43130_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", + CS43130_PCM_VOL_A, CS43130_PCM_VOL_B, 0, 0xFF, 1, + pcm_vol_tlv), + SOC_DOUBLE_R_TLV("Master DSD Playback Volume", + CS43130_DSD_VOL_A, CS43130_DSD_VOL_B, 0, 0xFF, 1, + pcm_vol_tlv), + SOC_ENUM_EXT("PCM Ch Select", pcm_ch_enum, cs43130_pcm_ch_get, + cs43130_pcm_ch_put), + SOC_ENUM("PCM Filter Speed", pcm_spd_enum), + SOC_SINGLE("PCM Phase Compensation", CS43130_PCM_FILT_OPT, 6, 1, 0), + SOC_SINGLE("PCM Nonoversample Emulate", CS43130_PCM_FILT_OPT, 5, 1, 0), + SOC_SINGLE("PCM High-pass Filter", CS43130_PCM_FILT_OPT, 1, 1, 0), + SOC_SINGLE("PCM De-emphasis Filter", CS43130_PCM_FILT_OPT, 0, 1, 0), + SOC_ENUM("DSD Phase Modulation", dsd_enum), +}; + +static const struct reg_sequence pcm_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD7, 0x01}, + {CS43130_DXD8, 0}, + {CS43130_DXD9, 0x01}, + {CS43130_DXD3, 0x12}, + {CS43130_DXD4, 0}, + {CS43130_DXD10, 0x28}, + {CS43130_DXD11, 0x28}, + {CS43130_DXD1, 0}, +}; + +static const struct reg_sequence dsd_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD7, 0x01}, + {CS43130_DXD8, 0}, + {CS43130_DXD9, 0x01}, + {CS43130_DXD3, 0x12}, + {CS43130_DXD4, 0}, + {CS43130_DXD10, 0x1E}, + {CS43130_DXD11, 0x20}, + {CS43130_DXD1, 0}, +}; + +static const struct reg_sequence pop_free_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD12, 0x0A}, + {CS43130_DXD1, 0}, +}; + +static const struct reg_sequence pop_free_seq2[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD13, 0x20}, + {CS43130_DXD1, 0}, +}; + +static const struct reg_sequence mute_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD3, 0x12}, + {CS43130_DXD5, 0x02}, + {CS43130_DXD4, 0x12}, + {CS43130_DXD1, 0}, +}; + +static const struct reg_sequence unmute_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD3, 0x10}, + {CS43130_DXD5, 0}, + {CS43130_DXD4, 0x16}, + {CS43130_DXD1, 0}, +}; + +static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + case CS4399_CHIP_ID: + regmap_multi_reg_write(cs43130->regmap, dsd_seq, + ARRAY_SIZE(dsd_seq)); + break; + } + break; + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_1, + CS43130_MUTE_MASK, 0); + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + case CS4399_CHIP_ID: + regmap_multi_reg_write(cs43130->regmap, unmute_seq, + ARRAY_SIZE(unmute_seq)); + break; + } + break; + case SND_SOC_DAPM_PRE_PMD: + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + case CS4399_CHIP_ID: + regmap_multi_reg_write(cs43130->regmap, mute_seq, + ARRAY_SIZE(mute_seq)); + regmap_update_bits(cs43130->regmap, + CS43130_DSD_PATH_CTL_1, + CS43130_MUTE_MASK, CS43130_MUTE_EN); + /* + * DSD Power Down Sequence + * According to Design, 130ms is preferred. + */ + msleep(130); + break; + case CS43131_CHIP_ID: + case CS43198_CHIP_ID: + regmap_update_bits(cs43130->regmap, + CS43130_DSD_PATH_CTL_1, + CS43130_MUTE_MASK, CS43130_MUTE_EN); + break; + } + break; + default: + dev_err(component->dev, "Invalid event = 0x%x\n", event); + return -EINVAL; + } + return 0; +} + +static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + case CS4399_CHIP_ID: + regmap_multi_reg_write(cs43130->regmap, pcm_seq, + ARRAY_SIZE(pcm_seq)); + break; + } + break; + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(cs43130->regmap, CS43130_PCM_PATH_CTL_1, + CS43130_MUTE_MASK, 0); + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + case CS4399_CHIP_ID: + regmap_multi_reg_write(cs43130->regmap, unmute_seq, + ARRAY_SIZE(unmute_seq)); + break; + } + break; + case SND_SOC_DAPM_PRE_PMD: + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + case CS4399_CHIP_ID: + regmap_multi_reg_write(cs43130->regmap, mute_seq, + ARRAY_SIZE(mute_seq)); + regmap_update_bits(cs43130->regmap, + CS43130_PCM_PATH_CTL_1, + CS43130_MUTE_MASK, CS43130_MUTE_EN); + /* + * PCM Power Down Sequence + * According to Design, 130ms is preferred. + */ + msleep(130); + break; + case CS43131_CHIP_ID: + case CS43198_CHIP_ID: + regmap_update_bits(cs43130->regmap, + CS43130_PCM_PATH_CTL_1, + CS43130_MUTE_MASK, CS43130_MUTE_EN); + break; + } + break; + default: + dev_err(component->dev, "Invalid event = 0x%x\n", event); + return -EINVAL; + } + return 0; +} + +static const struct reg_sequence dac_postpmu_seq[] = { + {CS43130_DXD9, 0x0C}, + {CS43130_DXD3, 0x10}, + {CS43130_DXD4, 0x20}, +}; + +static const struct reg_sequence dac_postpmd_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD6, 0x01}, + {CS43130_DXD1, 0}, +}; + +static int cs43130_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + case CS4399_CHIP_ID: + regmap_multi_reg_write(cs43130->regmap, pop_free_seq, + ARRAY_SIZE(pop_free_seq)); + break; + case CS43131_CHIP_ID: + case CS43198_CHIP_ID: + regmap_multi_reg_write(cs43130->regmap, pop_free_seq2, + ARRAY_SIZE(pop_free_seq2)); + break; + } + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(10000, 10050); + + regmap_write(cs43130->regmap, CS43130_DXD1, 0x99); + + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + case CS4399_CHIP_ID: + regmap_multi_reg_write(cs43130->regmap, dac_postpmu_seq, + ARRAY_SIZE(dac_postpmu_seq)); + /* + * Per datasheet, Sec. PCM Power-Up Sequence. + * According to Design, CS43130_DXD12 must be 0 to meet + * THDN and Dynamic Range spec. + */ + msleep(1000); + regmap_write(cs43130->regmap, CS43130_DXD12, 0); + break; + case CS43131_CHIP_ID: + case CS43198_CHIP_ID: + usleep_range(12000, 12010); + regmap_write(cs43130->regmap, CS43130_DXD13, 0); + break; + } + + regmap_write(cs43130->regmap, CS43130_DXD1, 0); + break; + case SND_SOC_DAPM_POST_PMD: + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + case CS4399_CHIP_ID: + regmap_multi_reg_write(cs43130->regmap, dac_postpmd_seq, + ARRAY_SIZE(dac_postpmd_seq)); + break; + } + break; + default: + dev_err(component->dev, "Invalid DAC event = 0x%x\n", event); + return -EINVAL; + } + return 0; +} + +static const struct reg_sequence hpin_prepmd_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD15, 0x64}, + {CS43130_DXD14, 0}, + {CS43130_DXD2, 0}, + {CS43130_DXD1, 0}, +}; + +static const struct reg_sequence hpin_postpmu_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD2, 1}, + {CS43130_DXD14, 0xDC}, + {CS43130_DXD15, 0xE4}, + {CS43130_DXD1, 0}, +}; + +static int cs43130_hpin_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + regmap_multi_reg_write(cs43130->regmap, hpin_prepmd_seq, + ARRAY_SIZE(hpin_prepmd_seq)); + break; + case SND_SOC_DAPM_PRE_PMU: + regmap_multi_reg_write(cs43130->regmap, hpin_postpmu_seq, + ARRAY_SIZE(hpin_postpmu_seq)); + break; + default: + dev_err(component->dev, "Invalid HPIN event = 0x%x\n", event); + return -EINVAL; + } + return 0; +} + +static const struct snd_soc_dapm_widget digital_hp_widgets[] = { + SND_SOC_DAPM_OUTPUT("HPOUTA"), + SND_SOC_DAPM_OUTPUT("HPOUTB"), + + SND_SOC_DAPM_AIF_IN_E("ASPIN PCM", NULL, 0, CS43130_PWDN_CTL, + CS43130_PDN_ASP_SHIFT, 1, cs43130_pcm_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD)), + + SND_SOC_DAPM_AIF_IN_E("ASPIN DoP", NULL, 0, CS43130_PWDN_CTL, + CS43130_PDN_ASP_SHIFT, 1, cs43130_dsd_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD)), + + SND_SOC_DAPM_AIF_IN_E("XSPIN DoP", NULL, 0, CS43130_PWDN_CTL, + CS43130_PDN_XSP_SHIFT, 1, cs43130_dsd_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD)), + + SND_SOC_DAPM_AIF_IN_E("XSPIN DSD", NULL, 0, CS43130_PWDN_CTL, + CS43130_PDN_DSDIF_SHIFT, 1, cs43130_dsd_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD)), + + SND_SOC_DAPM_DAC("DSD", NULL, CS43130_DSD_PATH_CTL_2, + CS43130_DSD_EN_SHIFT, 0), + + SND_SOC_DAPM_DAC_E("HiFi DAC", NULL, CS43130_PWDN_CTL, + CS43130_PDN_HP_SHIFT, 1, cs43130_dac_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD)), +}; + +static const struct snd_soc_dapm_widget analog_hp_widgets[] = { + SND_SOC_DAPM_DAC_E("Analog Playback", NULL, CS43130_HP_OUT_CTL_1, + CS43130_HP_IN_EN_SHIFT, 0, cs43130_hpin_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)), +}; + +static struct snd_soc_dapm_widget all_hp_widgets[ + ARRAY_SIZE(digital_hp_widgets) + + ARRAY_SIZE(analog_hp_widgets)]; + +static const struct snd_soc_dapm_route digital_hp_routes[] = { + {"ASPIN PCM", NULL, "ASP PCM Playback"}, + {"ASPIN DoP", NULL, "ASP DoP Playback"}, + {"XSPIN DoP", NULL, "XSP DoP Playback"}, + {"XSPIN DSD", NULL, "XSP DSD Playback"}, + {"DSD", NULL, "ASPIN DoP"}, + {"DSD", NULL, "XSPIN DoP"}, + {"DSD", NULL, "XSPIN DSD"}, + {"HiFi DAC", NULL, "ASPIN PCM"}, + {"HiFi DAC", NULL, "DSD"}, + {"HPOUTA", NULL, "HiFi DAC"}, + {"HPOUTB", NULL, "HiFi DAC"}, +}; + +static const struct snd_soc_dapm_route analog_hp_routes[] = { + {"HPOUTA", NULL, "Analog Playback"}, + {"HPOUTB", NULL, "Analog Playback"}, +}; + +static struct snd_soc_dapm_route all_hp_routes[ + ARRAY_SIZE(digital_hp_routes) + + ARRAY_SIZE(analog_hp_routes)]; + +static const unsigned int cs43130_asp_src_rates[] = { + 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 +}; + +static const struct snd_pcm_hw_constraint_list cs43130_asp_constraints = { + .count = ARRAY_SIZE(cs43130_asp_src_rates), + .list = cs43130_asp_src_rates, +}; + +static int cs43130_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &cs43130_asp_constraints); +} + +static const unsigned int cs43130_dop_src_rates[] = { + 176400, 352800, +}; + +static const struct snd_pcm_hw_constraint_list cs43130_dop_constraints = { + .count = ARRAY_SIZE(cs43130_dop_src_rates), + .list = cs43130_dop_src_rates, +}; + +static int cs43130_dop_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &cs43130_dop_constraints); +} + +static int cs43130_pcm_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBS_CFS; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM; + break; + default: + dev_err(component->dev, "unsupported mode\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_LEFT_J; + break; + case SND_SOC_DAIFMT_DSP_A: + cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + cs43130->dais[codec_dai->id].dai_format = SND_SOC_DAIFMT_DSP_B; + break; + default: + dev_err(component->dev, + "unsupported audio format\n"); + return -EINVAL; + } + + dev_dbg(component->dev, "dai_id = %d, dai_mode = %u, dai_format = %u\n", + codec_dai->id, + cs43130->dais[codec_dai->id].dai_mode, + cs43130->dais[codec_dai->id].dai_format); + + return 0; +} + +static int cs43130_dsd_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBS_CFS; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBM_CFM; + break; + default: + dev_err(component->dev, "Unsupported DAI format.\n"); + return -EINVAL; + } + + dev_dbg(component->dev, "dai_mode = 0x%x\n", + cs43130->dais[codec_dai->id].dai_mode); + + return 0; +} + +static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + + cs43130->dais[codec_dai->id].sclk = freq; + dev_dbg(component->dev, "dai_id = %d, sclk = %u\n", codec_dai->id, + cs43130->dais[codec_dai->id].sclk); + + return 0; +} + +static const struct snd_soc_dai_ops cs43130_pcm_ops = { + .startup = cs43130_pcm_startup, + .hw_params = cs43130_hw_params, + .hw_free = cs43130_hw_free, + .set_sysclk = cs43130_set_sysclk, + .set_fmt = cs43130_pcm_set_fmt, +}; + +static const struct snd_soc_dai_ops cs43130_dop_ops = { + .startup = cs43130_dop_startup, + .hw_params = cs43130_hw_params, + .hw_free = cs43130_hw_free, + .set_sysclk = cs43130_set_sysclk, + .set_fmt = cs43130_pcm_set_fmt, +}; + +static const struct snd_soc_dai_ops cs43130_dsd_ops = { + .startup = cs43130_dop_startup, + .hw_params = cs43130_dsd_hw_params, + .hw_free = cs43130_hw_free, + .set_fmt = cs43130_dsd_set_fmt, +}; + +static struct snd_soc_dai_driver cs43130_dai[] = { + { + .name = "cs43130-asp-pcm", + .id = CS43130_ASP_PCM_DAI, + .playback = { + .stream_name = "ASP PCM Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS43130_PCM_FORMATS, + }, + .ops = &cs43130_pcm_ops, + .symmetric_rate = 1, + }, + { + .name = "cs43130-asp-dop", + .id = CS43130_ASP_DOP_DAI, + .playback = { + .stream_name = "ASP DoP Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS43130_DOP_FORMATS, + }, + .ops = &cs43130_dop_ops, + .symmetric_rate = 1, + }, + { + .name = "cs43130-xsp-dop", + .id = CS43130_XSP_DOP_DAI, + .playback = { + .stream_name = "XSP DoP Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS43130_DOP_FORMATS, + }, + .ops = &cs43130_dop_ops, + .symmetric_rate = 1, + }, + { + .name = "cs43130-xsp-dsd", + .id = CS43130_XSP_DSD_DAI, + .playback = { + .stream_name = "XSP DSD Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS43130_DOP_FORMATS, + }, + .ops = &cs43130_dsd_ops, + }, + +}; + +static int cs43130_component_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, + int dir) +{ + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "clk_id = %d, source = %d, freq = %d, dir = %d\n", + clk_id, source, freq, dir); + + switch (freq) { + case CS43130_MCLK_22M: + case CS43130_MCLK_24M: + cs43130->mclk = freq; + break; + default: + dev_err(component->dev, "Invalid MCLK INT freq: %u\n", freq); + return -EINVAL; + } + + if (source == CS43130_MCLK_SRC_EXT) { + cs43130->pll_bypass = true; + } else { + dev_err(component->dev, "Invalid MCLK source\n"); + return -EINVAL; + } + + return 0; +} + +static inline u16 cs43130_get_ac_reg_val(u16 ac_freq) +{ + /* AC freq is counted in 5.94Hz step. */ + return ac_freq / 6; +} + +static int cs43130_show_dc(struct device *dev, char *buf, u8 ch) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cs43130_private *cs43130 = i2c_get_clientdata(client); + + if (!cs43130->hpload_done) + return sysfs_emit(buf, "NO_HPLOAD\n"); + else + return sysfs_emit(buf, "%u\n", cs43130->hpload_dc[ch]); +} + +static ssize_t hpload_dc_l_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return cs43130_show_dc(dev, buf, HP_LEFT); +} + +static ssize_t hpload_dc_r_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return cs43130_show_dc(dev, buf, HP_RIGHT); +} + +static const u16 cs43130_ac_freq[CS43130_AC_FREQ] = { + 24, + 43, + 93, + 200, + 431, + 928, + 2000, + 4309, + 9283, + 20000, +}; + +static int cs43130_show_ac(struct device *dev, char *buf, u8 ch) +{ + int i, j = 0, tmp; + struct i2c_client *client = to_i2c_client(dev); + struct cs43130_private *cs43130 = i2c_get_clientdata(client); + + if (cs43130->hpload_done && cs43130->ac_meas) { + for (i = 0; i < ARRAY_SIZE(cs43130_ac_freq); i++) { + tmp = sysfs_emit_at(buf, j, "%u\n", + cs43130->hpload_ac[i][ch]); + if (!tmp) + break; + + j += tmp; + } + + return j; + } else { + return sysfs_emit(buf, "NO_HPLOAD\n"); + } +} + +static ssize_t hpload_ac_l_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return cs43130_show_ac(dev, buf, HP_LEFT); +} + +static ssize_t hpload_ac_r_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return cs43130_show_ac(dev, buf, HP_RIGHT); +} + +static DEVICE_ATTR_RO(hpload_dc_l); +static DEVICE_ATTR_RO(hpload_dc_r); +static DEVICE_ATTR_RO(hpload_ac_l); +static DEVICE_ATTR_RO(hpload_ac_r); + +static struct attribute *hpload_attrs[] = { + &dev_attr_hpload_dc_l.attr, + &dev_attr_hpload_dc_r.attr, + &dev_attr_hpload_ac_l.attr, + &dev_attr_hpload_ac_r.attr, +}; +ATTRIBUTE_GROUPS(hpload); + +static struct reg_sequence hp_en_cal_seq[] = { + {CS43130_INT_MASK_4, CS43130_INT_MASK_ALL}, + {CS43130_HP_MEAS_LOAD_1, 0}, + {CS43130_HP_MEAS_LOAD_2, 0}, + {CS43130_INT_MASK_4, 0}, + {CS43130_DXD1, 0x99}, + {CS43130_DXD16, 0xBB}, + {CS43130_DXD12, 0x01}, + {CS43130_DXD19, 0xCB}, + {CS43130_DXD17, 0x95}, + {CS43130_DXD18, 0x0B}, + {CS43130_DXD1, 0}, + {CS43130_HP_LOAD_1, 0x80}, +}; + +static struct reg_sequence hp_en_cal_seq2[] = { + {CS43130_INT_MASK_4, CS43130_INT_MASK_ALL}, + {CS43130_HP_MEAS_LOAD_1, 0}, + {CS43130_HP_MEAS_LOAD_2, 0}, + {CS43130_INT_MASK_4, 0}, + {CS43130_HP_LOAD_1, 0x80}, +}; + +static struct reg_sequence hp_dis_cal_seq[] = { + {CS43130_HP_LOAD_1, 0x80}, + {CS43130_DXD1, 0x99}, + {CS43130_DXD12, 0}, + {CS43130_DXD1, 0}, + {CS43130_HP_LOAD_1, 0}, +}; + +static struct reg_sequence hp_dis_cal_seq2[] = { + {CS43130_HP_LOAD_1, 0x80}, + {CS43130_HP_LOAD_1, 0}, +}; + +static struct reg_sequence hp_dc_ch_l_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD19, 0x0A}, + {CS43130_DXD17, 0x93}, + {CS43130_DXD18, 0x0A}, + {CS43130_DXD1, 0}, + {CS43130_HP_LOAD_1, 0x80}, + {CS43130_HP_LOAD_1, 0x81}, +}; + +static struct reg_sequence hp_dc_ch_l_seq2[] = { + {CS43130_HP_LOAD_1, 0x80}, + {CS43130_HP_LOAD_1, 0x81}, +}; + +static struct reg_sequence hp_dc_ch_r_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD19, 0x8A}, + {CS43130_DXD17, 0x15}, + {CS43130_DXD18, 0x06}, + {CS43130_DXD1, 0}, + {CS43130_HP_LOAD_1, 0x90}, + {CS43130_HP_LOAD_1, 0x91}, +}; + +static struct reg_sequence hp_dc_ch_r_seq2[] = { + {CS43130_HP_LOAD_1, 0x90}, + {CS43130_HP_LOAD_1, 0x91}, +}; + +static struct reg_sequence hp_ac_ch_l_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD19, 0x0A}, + {CS43130_DXD17, 0x93}, + {CS43130_DXD18, 0x0A}, + {CS43130_DXD1, 0}, + {CS43130_HP_LOAD_1, 0x80}, + {CS43130_HP_LOAD_1, 0x82}, +}; + +static struct reg_sequence hp_ac_ch_l_seq2[] = { + {CS43130_HP_LOAD_1, 0x80}, + {CS43130_HP_LOAD_1, 0x82}, +}; + +static struct reg_sequence hp_ac_ch_r_seq[] = { + {CS43130_DXD1, 0x99}, + {CS43130_DXD19, 0x8A}, + {CS43130_DXD17, 0x15}, + {CS43130_DXD18, 0x06}, + {CS43130_DXD1, 0}, + {CS43130_HP_LOAD_1, 0x90}, + {CS43130_HP_LOAD_1, 0x92}, +}; + +static struct reg_sequence hp_ac_ch_r_seq2[] = { + {CS43130_HP_LOAD_1, 0x90}, + {CS43130_HP_LOAD_1, 0x92}, +}; + +static struct reg_sequence hp_cln_seq[] = { + {CS43130_INT_MASK_4, CS43130_INT_MASK_ALL}, + {CS43130_HP_MEAS_LOAD_1, 0}, + {CS43130_HP_MEAS_LOAD_2, 0}, +}; + +struct reg_sequences { + struct reg_sequence *seq; + int size; + unsigned int msk; +}; + +static struct reg_sequences hpload_seq1[] = { + { + .seq = hp_en_cal_seq, + .size = ARRAY_SIZE(hp_en_cal_seq), + .msk = CS43130_HPLOAD_ON_INT, + }, + { + .seq = hp_dc_ch_l_seq, + .size = ARRAY_SIZE(hp_dc_ch_l_seq), + .msk = CS43130_HPLOAD_DC_INT, + }, + { + .seq = hp_ac_ch_l_seq, + .size = ARRAY_SIZE(hp_ac_ch_l_seq), + .msk = CS43130_HPLOAD_AC_INT, + }, + { + .seq = hp_dis_cal_seq, + .size = ARRAY_SIZE(hp_dis_cal_seq), + .msk = CS43130_HPLOAD_OFF_INT, + }, + { + .seq = hp_en_cal_seq, + .size = ARRAY_SIZE(hp_en_cal_seq), + .msk = CS43130_HPLOAD_ON_INT, + }, + { + .seq = hp_dc_ch_r_seq, + .size = ARRAY_SIZE(hp_dc_ch_r_seq), + .msk = CS43130_HPLOAD_DC_INT, + }, + { + .seq = hp_ac_ch_r_seq, + .size = ARRAY_SIZE(hp_ac_ch_r_seq), + .msk = CS43130_HPLOAD_AC_INT, + }, +}; + +static struct reg_sequences hpload_seq2[] = { + { + .seq = hp_en_cal_seq2, + .size = ARRAY_SIZE(hp_en_cal_seq2), + .msk = CS43130_HPLOAD_ON_INT, + }, + { + .seq = hp_dc_ch_l_seq2, + .size = ARRAY_SIZE(hp_dc_ch_l_seq2), + .msk = CS43130_HPLOAD_DC_INT, + }, + { + .seq = hp_ac_ch_l_seq2, + .size = ARRAY_SIZE(hp_ac_ch_l_seq2), + .msk = CS43130_HPLOAD_AC_INT, + }, + { + .seq = hp_dis_cal_seq2, + .size = ARRAY_SIZE(hp_dis_cal_seq2), + .msk = CS43130_HPLOAD_OFF_INT, + }, + { + .seq = hp_en_cal_seq2, + .size = ARRAY_SIZE(hp_en_cal_seq2), + .msk = CS43130_HPLOAD_ON_INT, + }, + { + .seq = hp_dc_ch_r_seq2, + .size = ARRAY_SIZE(hp_dc_ch_r_seq2), + .msk = CS43130_HPLOAD_DC_INT, + }, + { + .seq = hp_ac_ch_r_seq2, + .size = ARRAY_SIZE(hp_ac_ch_r_seq2), + .msk = CS43130_HPLOAD_AC_INT, + }, +}; + +static int cs43130_update_hpload(unsigned int msk, int ac_idx, + struct cs43130_private *cs43130) +{ + bool left_ch = true; + unsigned int reg; + u32 addr; + u16 impedance; + struct snd_soc_component *component = cs43130->component; + + switch (msk) { + case CS43130_HPLOAD_DC_INT: + case CS43130_HPLOAD_AC_INT: + break; + default: + return 0; + } + + regmap_read(cs43130->regmap, CS43130_HP_LOAD_1, ®); + if (reg & CS43130_HPLOAD_CHN_SEL) + left_ch = false; + + if (msk == CS43130_HPLOAD_DC_INT) + addr = CS43130_HP_DC_STAT_1; + else + addr = CS43130_HP_AC_STAT_1; + + regmap_read(cs43130->regmap, addr, ®); + impedance = reg >> 3; + regmap_read(cs43130->regmap, addr + 1, ®); + impedance |= reg << 5; + + if (msk == CS43130_HPLOAD_DC_INT) { + if (left_ch) + cs43130->hpload_dc[HP_LEFT] = impedance; + else + cs43130->hpload_dc[HP_RIGHT] = impedance; + + dev_dbg(component->dev, "HP DC impedance (Ch %u): %u\n", !left_ch, + impedance); + } else { + if (left_ch) + cs43130->hpload_ac[ac_idx][HP_LEFT] = impedance; + else + cs43130->hpload_ac[ac_idx][HP_RIGHT] = impedance; + + dev_dbg(component->dev, "HP AC (%u Hz) impedance (Ch %u): %u\n", + cs43130->ac_freq[ac_idx], !left_ch, impedance); + } + + return 0; +} + +static int cs43130_hpload_proc(struct cs43130_private *cs43130, + struct reg_sequence *seq, int seq_size, + unsigned int rslt_msk, int ac_idx) +{ + int ret; + unsigned int msk; + u16 ac_reg_val; + struct snd_soc_component *component = cs43130->component; + + reinit_completion(&cs43130->hpload_evt); + + if (rslt_msk == CS43130_HPLOAD_AC_INT) { + ac_reg_val = cs43130_get_ac_reg_val(cs43130->ac_freq[ac_idx]); + regmap_update_bits(cs43130->regmap, CS43130_HP_LOAD_1, + CS43130_HPLOAD_AC_START, 0); + regmap_update_bits(cs43130->regmap, CS43130_HP_MEAS_LOAD_1, + CS43130_HP_MEAS_LOAD_MASK, + ac_reg_val >> CS43130_HP_MEAS_LOAD_1_SHIFT); + regmap_update_bits(cs43130->regmap, CS43130_HP_MEAS_LOAD_2, + CS43130_HP_MEAS_LOAD_MASK, + ac_reg_val >> CS43130_HP_MEAS_LOAD_2_SHIFT); + } + + regmap_multi_reg_write(cs43130->regmap, seq, + seq_size); + + ret = wait_for_completion_timeout(&cs43130->hpload_evt, + msecs_to_jiffies(1000)); + regmap_read(cs43130->regmap, CS43130_INT_MASK_4, &msk); + if (!ret) { + dev_err(component->dev, "Timeout waiting for HPLOAD interrupt\n"); + return -1; + } + + dev_dbg(component->dev, "HP load stat: %x, INT_MASK_4: %x\n", + cs43130->hpload_stat, msk); + if ((cs43130->hpload_stat & (CS43130_HPLOAD_NO_DC_INT | + CS43130_HPLOAD_UNPLUG_INT | + CS43130_HPLOAD_OOR_INT)) || + !(cs43130->hpload_stat & rslt_msk)) { + dev_dbg(component->dev, "HP load measure failed\n"); + return -1; + } + + return 0; +} + +static const struct reg_sequence hv_seq[][2] = { + { + {CS43130_CLASS_H_CTL, 0x1C}, + {CS43130_HP_OUT_CTL_1, 0x10}, + }, + { + {CS43130_CLASS_H_CTL, 0x1E}, + {CS43130_HP_OUT_CTL_1, 0x20}, + }, + { + {CS43130_CLASS_H_CTL, 0x1E}, + {CS43130_HP_OUT_CTL_1, 0x30}, + }, +}; + +static int cs43130_set_hv(struct regmap *regmap, u16 hpload_dc, + const u16 *dc_threshold) +{ + int i; + + for (i = 0; i < CS43130_DC_THRESHOLD; i++) { + if (hpload_dc <= dc_threshold[i]) + break; + } + + regmap_multi_reg_write(regmap, hv_seq[i], ARRAY_SIZE(hv_seq[i])); + + return 0; +} + +static void cs43130_imp_meas(struct work_struct *wk) +{ + unsigned int reg, seq_size; + int i, ret, ac_idx; + struct cs43130_private *cs43130; + struct snd_soc_component *component; + struct reg_sequences *hpload_seq; + + cs43130 = container_of(wk, struct cs43130_private, work); + component = cs43130->component; + + if (!cs43130->mclk) + return; + + cs43130->hpload_done = false; + + mutex_lock(&cs43130->clk_mutex); + if (!cs43130->clk_req) { + /* clk not in use */ + cs43130_set_pll(component, 0, 0, cs43130->mclk, CS43130_MCLK_22M); + if (cs43130->pll_bypass) + cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT); + else + cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL); + } + + cs43130->clk_req++; + mutex_unlock(&cs43130->clk_mutex); + + regmap_read(cs43130->regmap, CS43130_INT_STATUS_4, ®); + + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + hpload_seq = hpload_seq1; + seq_size = ARRAY_SIZE(hpload_seq1); + break; + case CS43131_CHIP_ID: + hpload_seq = hpload_seq2; + seq_size = ARRAY_SIZE(hpload_seq2); + break; + default: + WARN(1, "Invalid dev_id for meas: %d", cs43130->dev_id); + return; + } + + i = 0; + ac_idx = 0; + while (i < seq_size) { + ret = cs43130_hpload_proc(cs43130, hpload_seq[i].seq, + hpload_seq[i].size, + hpload_seq[i].msk, ac_idx); + if (ret < 0) + goto exit; + + cs43130_update_hpload(hpload_seq[i].msk, ac_idx, cs43130); + + if (cs43130->ac_meas && + hpload_seq[i].msk == CS43130_HPLOAD_AC_INT && + ac_idx < CS43130_AC_FREQ - 1) { + ac_idx++; + } else { + ac_idx = 0; + i++; + } + } + cs43130->hpload_done = true; + + if (cs43130->hpload_dc[HP_LEFT] >= CS43130_LINEOUT_LOAD) + snd_soc_jack_report(&cs43130->jack, CS43130_JACK_LINEOUT, + CS43130_JACK_MASK); + else + snd_soc_jack_report(&cs43130->jack, CS43130_JACK_HEADPHONE, + CS43130_JACK_MASK); + + dev_dbg(component->dev, "Set HP output control. DC threshold\n"); + for (i = 0; i < CS43130_DC_THRESHOLD; i++) + dev_dbg(component->dev, "DC threshold[%d]: %u.\n", i, + cs43130->dc_threshold[i]); + + cs43130_set_hv(cs43130->regmap, cs43130->hpload_dc[HP_LEFT], + cs43130->dc_threshold); + +exit: + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + cs43130_hpload_proc(cs43130, hp_dis_cal_seq, + ARRAY_SIZE(hp_dis_cal_seq), + CS43130_HPLOAD_OFF_INT, ac_idx); + break; + case CS43131_CHIP_ID: + cs43130_hpload_proc(cs43130, hp_dis_cal_seq2, + ARRAY_SIZE(hp_dis_cal_seq2), + CS43130_HPLOAD_OFF_INT, ac_idx); + break; + } + + regmap_multi_reg_write(cs43130->regmap, hp_cln_seq, + ARRAY_SIZE(hp_cln_seq)); + + mutex_lock(&cs43130->clk_mutex); + cs43130->clk_req--; + /* clk not in use */ + if (!cs43130->clk_req) + cs43130_change_clksrc(component, CS43130_MCLK_SRC_RCO); + mutex_unlock(&cs43130->clk_mutex); +} + +static irqreturn_t cs43130_irq_thread(int irq, void *data) +{ + struct cs43130_private *cs43130 = (struct cs43130_private *)data; + struct snd_soc_component *component = cs43130->component; + unsigned int stickies[CS43130_NUM_INT]; + unsigned int irq_occurrence = 0; + unsigned int masks[CS43130_NUM_INT]; + int i, j; + + for (i = 0; i < ARRAY_SIZE(stickies); i++) { + regmap_read(cs43130->regmap, CS43130_INT_STATUS_1 + i, + &stickies[i]); + regmap_read(cs43130->regmap, CS43130_INT_MASK_1 + i, + &masks[i]); + } + + for (i = 0; i < ARRAY_SIZE(stickies); i++) { + stickies[i] = stickies[i] & (~masks[i]); + for (j = 0; j < 8; j++) + irq_occurrence += (stickies[i] >> j) & 1; + } + dev_dbg(component->dev, "number of interrupts occurred (%u)\n", + irq_occurrence); + + if (!irq_occurrence) + return IRQ_NONE; + + if (stickies[0] & CS43130_XTAL_RDY_INT) { + complete(&cs43130->xtal_rdy); + return IRQ_HANDLED; + } + + if (stickies[0] & CS43130_PLL_RDY_INT) { + complete(&cs43130->pll_rdy); + return IRQ_HANDLED; + } + + if (stickies[3] & CS43130_HPLOAD_NO_DC_INT) { + cs43130->hpload_stat = stickies[3]; + dev_err(component->dev, + "DC load has not completed before AC load (%x)\n", + cs43130->hpload_stat); + complete(&cs43130->hpload_evt); + return IRQ_HANDLED; + } + + if (stickies[3] & CS43130_HPLOAD_UNPLUG_INT) { + cs43130->hpload_stat = stickies[3]; + dev_err(component->dev, "HP unplugged during measurement (%x)\n", + cs43130->hpload_stat); + complete(&cs43130->hpload_evt); + return IRQ_HANDLED; + } + + if (stickies[3] & CS43130_HPLOAD_OOR_INT) { + cs43130->hpload_stat = stickies[3]; + dev_err(component->dev, "HP load out of range (%x)\n", + cs43130->hpload_stat); + complete(&cs43130->hpload_evt); + return IRQ_HANDLED; + } + + if (stickies[3] & CS43130_HPLOAD_AC_INT) { + cs43130->hpload_stat = stickies[3]; + dev_dbg(component->dev, "HP AC load measurement done (%x)\n", + cs43130->hpload_stat); + complete(&cs43130->hpload_evt); + return IRQ_HANDLED; + } + + if (stickies[3] & CS43130_HPLOAD_DC_INT) { + cs43130->hpload_stat = stickies[3]; + dev_dbg(component->dev, "HP DC load measurement done (%x)\n", + cs43130->hpload_stat); + complete(&cs43130->hpload_evt); + return IRQ_HANDLED; + } + + if (stickies[3] & CS43130_HPLOAD_ON_INT) { + cs43130->hpload_stat = stickies[3]; + dev_dbg(component->dev, "HP load state machine on done (%x)\n", + cs43130->hpload_stat); + complete(&cs43130->hpload_evt); + return IRQ_HANDLED; + } + + if (stickies[3] & CS43130_HPLOAD_OFF_INT) { + cs43130->hpload_stat = stickies[3]; + dev_dbg(component->dev, "HP load state machine off done (%x)\n", + cs43130->hpload_stat); + complete(&cs43130->hpload_evt); + return IRQ_HANDLED; + } + + if (stickies[0] & CS43130_XTAL_ERR_INT) { + dev_err(component->dev, "Crystal err: clock is not running\n"); + return IRQ_HANDLED; + } + + if (stickies[0] & CS43130_HP_UNPLUG_INT) { + dev_dbg(component->dev, "HP unplugged\n"); + cs43130->hpload_done = false; + snd_soc_jack_report(&cs43130->jack, 0, CS43130_JACK_MASK); + return IRQ_HANDLED; + } + + if (stickies[0] & CS43130_HP_PLUG_INT) { + if (cs43130->dc_meas && !cs43130->hpload_done && + !work_busy(&cs43130->work)) { + dev_dbg(component->dev, "HP load queue work\n"); + queue_work(cs43130->wq, &cs43130->work); + } + + snd_soc_jack_report(&cs43130->jack, SND_JACK_MECHANICAL, + CS43130_JACK_MASK); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int cs43130_probe(struct snd_soc_component *component) +{ + int ret; + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component); + struct snd_soc_card *card = component->card; + unsigned int reg; + + cs43130->component = component; + + if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED) { + regmap_update_bits(cs43130->regmap, CS43130_CRYSTAL_SET, + CS43130_XTAL_IBIAS_MASK, + cs43130->xtal_ibias); + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_XTAL_ERR_INT, 0); + } + + ret = snd_soc_card_jack_new(card, "Headphone", CS43130_JACK_MASK, + &cs43130->jack); + if (ret < 0) { + dev_err(component->dev, "Cannot create jack\n"); + return ret; + } + + cs43130->hpload_done = false; + if (cs43130->dc_meas) { + ret = sysfs_create_groups(&component->dev->kobj, hpload_groups); + if (ret) + return ret; + + cs43130->wq = create_singlethread_workqueue("cs43130_hp"); + if (!cs43130->wq) { + sysfs_remove_groups(&component->dev->kobj, hpload_groups); + return -ENOMEM; + } + INIT_WORK(&cs43130->work, cs43130_imp_meas); + } + + regmap_read(cs43130->regmap, CS43130_INT_STATUS_1, ®); + regmap_read(cs43130->regmap, CS43130_HP_STATUS, ®); + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT, 0); + regmap_update_bits(cs43130->regmap, CS43130_HP_DETECT, + CS43130_HP_DETECT_CTRL_MASK, 0); + regmap_update_bits(cs43130->regmap, CS43130_HP_DETECT, + CS43130_HP_DETECT_CTRL_MASK, + CS43130_HP_DETECT_CTRL_MASK); + + return 0; +} + +static struct snd_soc_component_driver soc_component_dev_cs43130 = { + .probe = cs43130_probe, + .controls = cs43130_snd_controls, + .num_controls = ARRAY_SIZE(cs43130_snd_controls), + .set_sysclk = cs43130_component_set_sysclk, + .set_pll = cs43130_set_pll, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config cs43130_regmap = { + .reg_bits = 24, + .pad_bits = 8, + .val_bits = 8, + + .max_register = CS43130_LASTREG, + .reg_defaults = cs43130_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs43130_reg_defaults), + .readable_reg = cs43130_readable_register, + .precious_reg = cs43130_precious_register, + .volatile_reg = cs43130_volatile_register, + .cache_type = REGCACHE_MAPLE, + /* needed for regcache_sync */ + .use_single_read = true, + .use_single_write = true, +}; + +static const u16 cs43130_dc_threshold[CS43130_DC_THRESHOLD] = { + 50, + 120, +}; + +static int cs43130_handle_device_data(struct i2c_client *i2c_client, + struct cs43130_private *cs43130) +{ + struct device_node *np = i2c_client->dev.of_node; + unsigned int val; + int i; + + if (of_property_read_u32(np, "cirrus,xtal-ibias", &val) < 0) { + /* Crystal is unused. System clock is used for external MCLK */ + cs43130->xtal_ibias = CS43130_XTAL_UNUSED; + return 0; + } + + switch (val) { + case 1: + cs43130->xtal_ibias = CS43130_XTAL_IBIAS_7_5UA; + break; + case 2: + cs43130->xtal_ibias = CS43130_XTAL_IBIAS_12_5UA; + break; + case 3: + cs43130->xtal_ibias = CS43130_XTAL_IBIAS_15UA; + break; + default: + dev_err(&i2c_client->dev, + "Invalid cirrus,xtal-ibias value: %d\n", val); + return -EINVAL; + } + + cs43130->dc_meas = of_property_read_bool(np, "cirrus,dc-measure"); + cs43130->ac_meas = of_property_read_bool(np, "cirrus,ac-measure"); + + if (of_property_read_u16_array(np, "cirrus,ac-freq", cs43130->ac_freq, + CS43130_AC_FREQ) < 0) { + for (i = 0; i < CS43130_AC_FREQ; i++) + cs43130->ac_freq[i] = cs43130_ac_freq[i]; + } + + if (of_property_read_u16_array(np, "cirrus,dc-threshold", + cs43130->dc_threshold, + CS43130_DC_THRESHOLD) < 0) { + for (i = 0; i < CS43130_DC_THRESHOLD; i++) + cs43130->dc_threshold[i] = cs43130_dc_threshold[i]; + } + + return 0; +} + +static int cs43130_i2c_probe(struct i2c_client *client) +{ + struct cs43130_private *cs43130; + int ret; + unsigned int reg; + int i, devid; + + cs43130 = devm_kzalloc(&client->dev, sizeof(*cs43130), GFP_KERNEL); + if (!cs43130) + return -ENOMEM; + + i2c_set_clientdata(client, cs43130); + + cs43130->regmap = devm_regmap_init_i2c(client, &cs43130_regmap); + if (IS_ERR(cs43130->regmap)) { + ret = PTR_ERR(cs43130->regmap); + return ret; + } + + if (client->dev.of_node) { + ret = cs43130_handle_device_data(client, cs43130); + if (ret != 0) + return ret; + } + for (i = 0; i < ARRAY_SIZE(cs43130->supplies); i++) + cs43130->supplies[i].supply = cs43130_supply_names[i]; + + ret = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(cs43130->supplies), + cs43130->supplies); + if (ret != 0) { + dev_err(&client->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + ret = regulator_bulk_enable(ARRAY_SIZE(cs43130->supplies), + cs43130->supplies); + if (ret != 0) { + dev_err(&client->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + cs43130->reset_gpio = devm_gpiod_get_optional(&client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs43130->reset_gpio)) { + ret = PTR_ERR(cs43130->reset_gpio); + goto err_supplies; + } + + gpiod_set_value_cansleep(cs43130->reset_gpio, 1); + + usleep_range(2000, 2050); + + devid = cirrus_read_device_id(cs43130->regmap, CS43130_DEVID_AB); + if (devid < 0) { + ret = devid; + dev_err(&client->dev, "Failed to read device ID: %d\n", ret); + goto err; + } + + switch (devid) { + case CS43130_CHIP_ID: + case CS4399_CHIP_ID: + case CS43131_CHIP_ID: + case CS43198_CHIP_ID: + break; + default: + dev_err(&client->dev, + "CS43130 Device ID %X. Expected ID %X, %X, %X or %X\n", + devid, CS43130_CHIP_ID, CS4399_CHIP_ID, + CS43131_CHIP_ID, CS43198_CHIP_ID); + ret = -ENODEV; + goto err; + } + + cs43130->dev_id = devid; + ret = regmap_read(cs43130->regmap, CS43130_REV_ID, ®); + if (ret < 0) { + dev_err(&client->dev, "Get Revision ID failed\n"); + goto err; + } + + dev_info(&client->dev, + "Cirrus Logic CS43130 (%x), Revision: %02X\n", devid, + reg & 0xFF); + + mutex_init(&cs43130->clk_mutex); + + init_completion(&cs43130->xtal_rdy); + init_completion(&cs43130->pll_rdy); + init_completion(&cs43130->hpload_evt); + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, cs43130_irq_thread, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "cs43130", cs43130); + if (ret != 0) { + dev_err(&client->dev, "Failed to request IRQ: %d\n", ret); + goto err; + } + + cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO; + + pm_runtime_set_autosuspend_delay(&client->dev, 100); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + + switch (cs43130->dev_id) { + case CS43130_CHIP_ID: + case CS43131_CHIP_ID: + memcpy(all_hp_widgets, digital_hp_widgets, + sizeof(digital_hp_widgets)); + memcpy(all_hp_widgets + ARRAY_SIZE(digital_hp_widgets), + analog_hp_widgets, sizeof(analog_hp_widgets)); + memcpy(all_hp_routes, digital_hp_routes, + sizeof(digital_hp_routes)); + memcpy(all_hp_routes + ARRAY_SIZE(digital_hp_routes), + analog_hp_routes, sizeof(analog_hp_routes)); + + soc_component_dev_cs43130.dapm_widgets = + all_hp_widgets; + soc_component_dev_cs43130.num_dapm_widgets = + ARRAY_SIZE(all_hp_widgets); + soc_component_dev_cs43130.dapm_routes = + all_hp_routes; + soc_component_dev_cs43130.num_dapm_routes = + ARRAY_SIZE(all_hp_routes); + break; + case CS43198_CHIP_ID: + case CS4399_CHIP_ID: + soc_component_dev_cs43130.dapm_widgets = + digital_hp_widgets; + soc_component_dev_cs43130.num_dapm_widgets = + ARRAY_SIZE(digital_hp_widgets); + soc_component_dev_cs43130.dapm_routes = + digital_hp_routes; + soc_component_dev_cs43130.num_dapm_routes = + ARRAY_SIZE(digital_hp_routes); + break; + } + + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_cs43130, + cs43130_dai, ARRAY_SIZE(cs43130_dai)); + if (ret < 0) { + dev_err(&client->dev, + "snd_soc_register_component failed with ret = %d\n", ret); + goto err; + } + + regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG, + CS43130_ASP_3ST_MASK, 0); + regmap_update_bits(cs43130->regmap, CS43130_PAD_INT_CFG, + CS43130_XSP_3ST_MASK, 0); + + return 0; + +err: + gpiod_set_value_cansleep(cs43130->reset_gpio, 0); +err_supplies: + regulator_bulk_disable(ARRAY_SIZE(cs43130->supplies), + cs43130->supplies); + + return ret; +} + +static void cs43130_i2c_remove(struct i2c_client *client) +{ + struct cs43130_private *cs43130 = i2c_get_clientdata(client); + + if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED) + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_XTAL_ERR_INT, + 1 << CS43130_XTAL_ERR_INT_SHIFT); + + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT, + CS43130_HP_PLUG_INT | CS43130_HP_UNPLUG_INT); + + if (cs43130->dc_meas) { + cancel_work_sync(&cs43130->work); + flush_workqueue(cs43130->wq); + + device_remove_file(&client->dev, &dev_attr_hpload_dc_l); + device_remove_file(&client->dev, &dev_attr_hpload_dc_r); + device_remove_file(&client->dev, &dev_attr_hpload_ac_l); + device_remove_file(&client->dev, &dev_attr_hpload_ac_r); + } + + gpiod_set_value_cansleep(cs43130->reset_gpio, 0); + + pm_runtime_disable(&client->dev); + regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies); +} + +static int __maybe_unused cs43130_runtime_suspend(struct device *dev) +{ + struct cs43130_private *cs43130 = dev_get_drvdata(dev); + + if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED) + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_XTAL_ERR_INT, + 1 << CS43130_XTAL_ERR_INT_SHIFT); + + regcache_cache_only(cs43130->regmap, true); + regcache_mark_dirty(cs43130->regmap); + + gpiod_set_value_cansleep(cs43130->reset_gpio, 0); + + regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies); + + return 0; +} + +static int __maybe_unused cs43130_runtime_resume(struct device *dev) +{ + struct cs43130_private *cs43130 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(CS43130_NUM_SUPPLIES, cs43130->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_cache_only(cs43130->regmap, false); + + gpiod_set_value_cansleep(cs43130->reset_gpio, 1); + + usleep_range(2000, 2050); + + ret = regcache_sync(cs43130->regmap); + if (ret != 0) { + dev_err(dev, "Failed to restore register cache\n"); + goto err; + } + + if (cs43130->xtal_ibias != CS43130_XTAL_UNUSED) + regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1, + CS43130_XTAL_ERR_INT, 0); + + return 0; +err: + regcache_cache_only(cs43130->regmap, true); + regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies); + + return ret; +} + +static const struct dev_pm_ops cs43130_runtime_pm = { + SET_RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume, + NULL) +}; + +static const struct of_device_id cs43130_of_match[] = { + {.compatible = "cirrus,cs43130",}, + {.compatible = "cirrus,cs4399",}, + {.compatible = "cirrus,cs43131",}, + {.compatible = "cirrus,cs43198",}, + {}, +}; + +MODULE_DEVICE_TABLE(of, cs43130_of_match); + +static const struct i2c_device_id cs43130_i2c_id[] = { + {"cs43130", 0}, + {"cs4399", 0}, + {"cs43131", 0}, + {"cs43198", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id); + +static struct i2c_driver cs43130_i2c_driver = { + .driver = { + .name = "cs43130", + .of_match_table = cs43130_of_match, + .pm = &cs43130_runtime_pm, + }, + .id_table = cs43130_i2c_id, + .probe = cs43130_i2c_probe, + .remove = cs43130_i2c_remove, +}; + +module_i2c_driver(cs43130_i2c_driver); + +MODULE_AUTHOR("Li Xu "); +MODULE_DESCRIPTION("Cirrus Logic CS43130 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h new file mode 100644 index 0000000000..90e8895275 --- /dev/null +++ b/sound/soc/codecs/cs43130.h @@ -0,0 +1,538 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC CS43130 codec driver + * + * Copyright 2017 Cirrus Logic, Inc. + * + * Author: Li Xu + */ + +#ifndef __CS43130_H__ +#define __CS43130_H__ + +#include + +/* CS43130 registers addresses */ +/* all reg address is shifted by a byte for control byte to be LSB */ +#define CS43130_FIRSTREG 0x010000 +#define CS43130_LASTREG 0x190000 +#define CS43130_CHIP_ID 0x00043130 +#define CS4399_CHIP_ID 0x00043990 +#define CS43131_CHIP_ID 0x00043131 +#define CS43198_CHIP_ID 0x00043198 +#define CS43130_DEVID_AB 0x010000 /* Device ID A & B [RO] */ +#define CS43130_DEVID_CD 0x010001 /* Device ID C & D [RO] */ +#define CS43130_DEVID_E 0x010002 /* Device ID E [RO] */ +#define CS43130_FAB_ID 0x010003 /* Fab ID [RO] */ +#define CS43130_REV_ID 0x010004 /* Revision ID [RO] */ +#define CS43130_SUBREV_ID 0x010005 /* Subrevision ID */ +#define CS43130_SYS_CLK_CTL_1 0x010006 /* System Clocking Ctl 1 */ +#define CS43130_SP_SRATE 0x01000B /* Serial Port Sample Rate */ +#define CS43130_SP_BITSIZE 0x01000C /* Serial Port Bit Size */ +#define CS43130_PAD_INT_CFG 0x01000D /* Pad Interface Config */ +#define CS43130_DXD1 0x010010 /* DXD1 */ +#define CS43130_DXD7 0x010025 /* DXD7 */ +#define CS43130_DXD19 0x010026 /* DXD19 */ +#define CS43130_DXD17 0x010027 /* DXD17 */ +#define CS43130_DXD18 0x010028 /* DXD18 */ +#define CS43130_DXD12 0x01002C /* DXD12 */ +#define CS43130_DXD8 0x01002E /* DXD8 */ +#define CS43130_PWDN_CTL 0x020000 /* Power Down Ctl */ +#define CS43130_DXD2 0x020019 /* DXD2 */ +#define CS43130_CRYSTAL_SET 0x020052 /* Crystal Setting */ +#define CS43130_PLL_SET_1 0x030001 /* PLL Setting 1 */ +#define CS43130_PLL_SET_2 0x030002 /* PLL Setting 2 */ +#define CS43130_PLL_SET_3 0x030003 /* PLL Setting 3 */ +#define CS43130_PLL_SET_4 0x030004 /* PLL Setting 4 */ +#define CS43130_PLL_SET_5 0x030005 /* PLL Setting 5 */ +#define CS43130_PLL_SET_6 0x030008 /* PLL Setting 6 */ +#define CS43130_PLL_SET_7 0x03000A /* PLL Setting 7 */ +#define CS43130_PLL_SET_8 0x03001B /* PLL Setting 8 */ +#define CS43130_PLL_SET_9 0x040002 /* PLL Setting 9 */ +#define CS43130_PLL_SET_10 0x040003 /* PLL Setting 10 */ +#define CS43130_CLKOUT_CTL 0x040004 /* CLKOUT Ctl */ +#define CS43130_ASP_NUM_1 0x040010 /* ASP Numerator 1 */ +#define CS43130_ASP_NUM_2 0x040011 /* ASP Numerator 2 */ +#define CS43130_ASP_DEN_1 0x040012 /* ASP Denominator 1 */ +#define CS43130_ASP_DEN_2 0x040013 /* ASP Denominator 2 */ +#define CS43130_ASP_LRCK_HI_TIME_1 0x040014 /* ASP LRCK High Time 1 */ +#define CS43130_ASP_LRCK_HI_TIME_2 0x040015 /* ASP LRCK High Time 2 */ +#define CS43130_ASP_LRCK_PERIOD_1 0x040016 /* ASP LRCK Period 1 */ +#define CS43130_ASP_LRCK_PERIOD_2 0x040017 /* ASP LRCK Period 2 */ +#define CS43130_ASP_CLOCK_CONF 0x040018 /* ASP Clock Config */ +#define CS43130_ASP_FRAME_CONF 0x040019 /* ASP Frame Config */ +#define CS43130_XSP_NUM_1 0x040020 /* XSP Numerator 1 */ +#define CS43130_XSP_NUM_2 0x040021 /* XSP Numerator 2 */ +#define CS43130_XSP_DEN_1 0x040022 /* XSP Denominator 1 */ +#define CS43130_XSP_DEN_2 0x040023 /* XSP Denominator 2 */ +#define CS43130_XSP_LRCK_HI_TIME_1 0x040024 /* XSP LRCK High Time 1 */ +#define CS43130_XSP_LRCK_HI_TIME_2 0x040025 /* XSP LRCK High Time 2 */ +#define CS43130_XSP_LRCK_PERIOD_1 0x040026 /* XSP LRCK Period 1 */ +#define CS43130_XSP_LRCK_PERIOD_2 0x040027 /* XSP LRCK Period 2 */ +#define CS43130_XSP_CLOCK_CONF 0x040028 /* XSP Clock Config */ +#define CS43130_XSP_FRAME_CONF 0x040029 /* XSP Frame Config */ +#define CS43130_ASP_CH_1_LOC 0x050000 /* ASP Chan 1 Location */ +#define CS43130_ASP_CH_2_LOC 0x050001 /* ASP Chan 2 Location */ +#define CS43130_ASP_CH_1_SZ_EN 0x05000A /* ASP Chan 1 Size, Enable */ +#define CS43130_ASP_CH_2_SZ_EN 0x05000B /* ASP Chan 2 Size, Enable */ +#define CS43130_XSP_CH_1_LOC 0x060000 /* XSP Chan 1 Location */ +#define CS43130_XSP_CH_2_LOC 0x060001 /* XSP Chan 2 Location */ +#define CS43130_XSP_CH_1_SZ_EN 0x06000A /* XSP Chan 1 Size, Enable */ +#define CS43130_XSP_CH_2_SZ_EN 0x06000B /* XSP Chan 2 Size, Enable */ +#define CS43130_DSD_VOL_B 0x070000 /* DSD Volume B */ +#define CS43130_DSD_VOL_A 0x070001 /* DSD Volume A */ +#define CS43130_DSD_PATH_CTL_1 0x070002 /* DSD Proc Path Sig Ctl 1 */ +#define CS43130_DSD_INT_CFG 0x070003 /* DSD Interface Config */ +#define CS43130_DSD_PATH_CTL_2 0x070004 /* DSD Proc Path Sig Ctl 2 */ +#define CS43130_DSD_PCM_MIX_CTL 0x070005 /* DSD and PCM Mixing Ctl */ +#define CS43130_DSD_PATH_CTL_3 0x070006 /* DSD Proc Path Sig Ctl 3 */ +#define CS43130_HP_OUT_CTL_1 0x080000 /* HP Output Ctl 1 */ +#define CS43130_DXD16 0x080024 /* DXD16 */ +#define CS43130_DXD13 0x080032 /* DXD13 */ +#define CS43130_PCM_FILT_OPT 0x090000 /* PCM Filter Option */ +#define CS43130_PCM_VOL_B 0x090001 /* PCM Volume B */ +#define CS43130_PCM_VOL_A 0x090002 /* PCM Volume A */ +#define CS43130_PCM_PATH_CTL_1 0x090003 /* PCM Path Signal Ctl 1 */ +#define CS43130_PCM_PATH_CTL_2 0x090004 /* PCM Path Signal Ctl 2 */ +#define CS43130_DXD6 0x090097 /* DXD6 */ +#define CS43130_CLASS_H_CTL 0x0B0000 /* Class H Ctl */ +#define CS43130_DXD15 0x0B0005 /* DXD15 */ +#define CS43130_DXD14 0x0B0006 /* DXD14 */ +#define CS43130_DXD3 0x0C0002 /* DXD3 */ +#define CS43130_DXD10 0x0C0003 /* DXD10 */ +#define CS43130_DXD11 0x0C0005 /* DXD11 */ +#define CS43130_DXD9 0x0C0006 /* DXD9 */ +#define CS43130_DXD4 0x0C0009 /* DXD4 */ +#define CS43130_DXD5 0x0C000E /* DXD5 */ +#define CS43130_HP_DETECT 0x0D0000 /* HP Detect */ +#define CS43130_HP_STATUS 0x0D0001 /* HP Status [RO] */ +#define CS43130_HP_LOAD_1 0x0E0000 /* HP Load 1 */ +#define CS43130_HP_MEAS_LOAD_1 0x0E0003 /* HP Load Measurement 1 */ +#define CS43130_HP_MEAS_LOAD_2 0x0E0004 /* HP Load Measurement 2 */ +#define CS43130_HP_DC_STAT_1 0x0E000D /* HP DC Load Status 0 [RO] */ +#define CS43130_HP_DC_STAT_2 0x0E000E /* HP DC Load Status 1 [RO] */ +#define CS43130_HP_AC_STAT_1 0x0E0010 /* HP AC Load Status 0 [RO] */ +#define CS43130_HP_AC_STAT_2 0x0E0011 /* HP AC Load Status 1 [RO] */ +#define CS43130_HP_LOAD_STAT 0x0E001A /* HP Load Status [RO] */ +#define CS43130_INT_STATUS_1 0x0F0000 /* Interrupt Status 1 */ +#define CS43130_INT_STATUS_2 0x0F0001 /* Interrupt Status 2 */ +#define CS43130_INT_STATUS_3 0x0F0002 /* Interrupt Status 3 */ +#define CS43130_INT_STATUS_4 0x0F0003 /* Interrupt Status 4 */ +#define CS43130_INT_STATUS_5 0x0F0004 /* Interrupt Status 5 */ +#define CS43130_INT_MASK_1 0x0F0010 /* Interrupt Mask 1 */ +#define CS43130_INT_MASK_2 0x0F0011 /* Interrupt Mask 2 */ +#define CS43130_INT_MASK_3 0x0F0012 /* Interrupt Mask 3 */ +#define CS43130_INT_MASK_4 0x0F0013 /* Interrupt Mask 4 */ +#define CS43130_INT_MASK_5 0x0F0014 /* Interrupt Mask 5 */ + +#define CS43130_MCLK_SRC_SEL_MASK 0x03 +#define CS43130_MCLK_SRC_SEL_SHIFT 0 +#define CS43130_MCLK_INT_MASK 0x04 +#define CS43130_MCLK_INT_SHIFT 2 +#define CS43130_CH_BITSIZE_MASK 0x03 +#define CS43130_CH_EN_MASK 0x04 +#define CS43130_CH_EN_SHIFT 2 +#define CS43130_ASP_BITSIZE_MASK 0x03 +#define CS43130_XSP_BITSIZE_MASK 0x0C +#define CS43130_XSP_BITSIZE_SHIFT 2 +#define CS43130_SP_BITSIZE_ASP_SHIFT 0 +#define CS43130_HP_DETECT_CTRL_SHIFT 6 +#define CS43130_HP_DETECT_CTRL_MASK (0x03 << CS43130_HP_DETECT_CTRL_SHIFT) +#define CS43130_HP_DETECT_INV_SHIFT 5 +#define CS43130_HP_DETECT_INV_MASK (1 << CS43130_HP_DETECT_INV_SHIFT) + +/* CS43130_INT_MASK_1 */ +#define CS43130_HP_PLUG_INT_SHIFT 6 +#define CS43130_HP_PLUG_INT (1 << CS43130_HP_PLUG_INT_SHIFT) +#define CS43130_HP_UNPLUG_INT_SHIFT 5 +#define CS43130_HP_UNPLUG_INT (1 << CS43130_HP_UNPLUG_INT_SHIFT) +#define CS43130_XTAL_RDY_INT_SHIFT 4 +#define CS43130_XTAL_RDY_INT_MASK 0x10 +#define CS43130_XTAL_RDY_INT (1 << CS43130_XTAL_RDY_INT_SHIFT) +#define CS43130_XTAL_ERR_INT_SHIFT 3 +#define CS43130_XTAL_ERR_INT (1 << CS43130_XTAL_ERR_INT_SHIFT) +#define CS43130_PLL_RDY_INT_MASK 0x04 +#define CS43130_PLL_RDY_INT_SHIFT 2 +#define CS43130_PLL_RDY_INT (1 << CS43130_PLL_RDY_INT_SHIFT) + +/* CS43130_INT_MASK_4 */ +#define CS43130_INT_MASK_ALL 0xFF +#define CS43130_HPLOAD_NO_DC_INT_SHIFT 7 +#define CS43130_HPLOAD_NO_DC_INT (1 << CS43130_HPLOAD_NO_DC_INT_SHIFT) +#define CS43130_HPLOAD_UNPLUG_INT_SHIFT 6 +#define CS43130_HPLOAD_UNPLUG_INT (1 << CS43130_HPLOAD_UNPLUG_INT_SHIFT) +#define CS43130_HPLOAD_OOR_INT_SHIFT 4 +#define CS43130_HPLOAD_OOR_INT (1 << CS43130_HPLOAD_OOR_INT_SHIFT) +#define CS43130_HPLOAD_AC_INT_SHIFT 3 +#define CS43130_HPLOAD_AC_INT (1 << CS43130_HPLOAD_AC_INT_SHIFT) +#define CS43130_HPLOAD_DC_INT_SHIFT 2 +#define CS43130_HPLOAD_DC_INT (1 << CS43130_HPLOAD_DC_INT_SHIFT) +#define CS43130_HPLOAD_OFF_INT_SHIFT 1 +#define CS43130_HPLOAD_OFF_INT (1 << CS43130_HPLOAD_OFF_INT_SHIFT) +#define CS43130_HPLOAD_ON_INT 1 + +/* CS43130_HP_LOAD_1 */ +#define CS43130_HPLOAD_EN_SHIFT 7 +#define CS43130_HPLOAD_EN (1 << CS43130_HPLOAD_EN_SHIFT) +#define CS43130_HPLOAD_CHN_SEL_SHIFT 4 +#define CS43130_HPLOAD_CHN_SEL (1 << CS43130_HPLOAD_CHN_SEL_SHIFT) +#define CS43130_HPLOAD_AC_START_SHIFT 1 +#define CS43130_HPLOAD_AC_START (1 << CS43130_HPLOAD_AC_START_SHIFT) +#define CS43130_HPLOAD_DC_START 1 + +/* Reg CS43130_SP_BITSIZE */ +#define CS43130_SP_BIT_SIZE_8 0x03 +#define CS43130_SP_BIT_SIZE_16 0x02 +#define CS43130_SP_BIT_SIZE_24 0x01 +#define CS43130_SP_BIT_SIZE_32 0x00 + +/* Reg CS43130_SP_CH_SZ_EN */ +#define CS43130_CH_BIT_SIZE_8 0x00 +#define CS43130_CH_BIT_SIZE_16 0x01 +#define CS43130_CH_BIT_SIZE_24 0x02 +#define CS43130_CH_BIT_SIZE_32 0x03 + +/* PLL */ +#define CS43130_PLL_START_MASK 0x01 +#define CS43130_PLL_MODE_MASK 0x02 +#define CS43130_PLL_MODE_SHIFT 1 + +#define CS43130_PLL_REF_PREDIV_MASK 0x3 + +#define CS43130_SP_STP_MASK 0x10 +#define CS43130_SP_STP_SHIFT 4 +#define CS43130_SP_5050_MASK 0x08 +#define CS43130_SP_5050_SHIFT 3 +#define CS43130_SP_FSD_MASK 0x07 + +#define CS43130_SP_MODE_MASK 0x10 +#define CS43130_SP_MODE_SHIFT 4 +#define CS43130_SP_SCPOL_OUT_MASK 0x08 +#define CS43130_SP_SCPOL_OUT_SHIFT 3 +#define CS43130_SP_SCPOL_IN_MASK 0x04 +#define CS43130_SP_SCPOL_IN_SHIFT 2 +#define CS43130_SP_LCPOL_OUT_MASK 0x02 +#define CS43130_SP_LCPOL_OUT_SHIFT 1 +#define CS43130_SP_LCPOL_IN_MASK 0x01 +#define CS43130_SP_LCPOL_IN_SHIFT 0 + +/* Reg CS43130_PWDN_CTL */ +#define CS43130_PDN_XSP_MASK 0x80 +#define CS43130_PDN_XSP_SHIFT 7 +#define CS43130_PDN_ASP_MASK 0x40 +#define CS43130_PDN_ASP_SHIFT 6 +#define CS43130_PDN_DSPIF_MASK 0x20 +#define CS43130_PDN_DSDIF_SHIFT 5 +#define CS43130_PDN_HP_MASK 0x10 +#define CS43130_PDN_HP_SHIFT 4 +#define CS43130_PDN_XTAL_MASK 0x08 +#define CS43130_PDN_XTAL_SHIFT 3 +#define CS43130_PDN_PLL_MASK 0x04 +#define CS43130_PDN_PLL_SHIFT 2 +#define CS43130_PDN_CLKOUT_MASK 0x02 +#define CS43130_PDN_CLKOUT_SHIFT 1 + +/* Reg CS43130_HP_OUT_CTL_1 */ +#define CS43130_HP_IN_EN_SHIFT 3 +#define CS43130_HP_IN_EN_MASK 0x08 + +/* Reg CS43130_PAD_INT_CFG */ +#define CS43130_ASP_3ST_MASK 0x01 +#define CS43130_XSP_3ST_MASK 0x02 + +/* Reg CS43130_PLL_SET_2 */ +#define CS43130_PLL_DIV_DATA_MASK 0x000000FF +#define CS43130_PLL_DIV_FRAC_0_DATA_SHIFT 0 + +/* Reg CS43130_PLL_SET_3 */ +#define CS43130_PLL_DIV_FRAC_1_DATA_SHIFT 8 + +/* Reg CS43130_PLL_SET_4 */ +#define CS43130_PLL_DIV_FRAC_2_DATA_SHIFT 16 + +/* Reg CS43130_SP_DEN_1 */ +#define CS43130_SP_M_LSB_DATA_MASK 0x00FF +#define CS43130_SP_M_LSB_DATA_SHIFT 0 + +/* Reg CS43130_SP_DEN_2 */ +#define CS43130_SP_M_MSB_DATA_MASK 0xFF00 +#define CS43130_SP_M_MSB_DATA_SHIFT 8 + +/* Reg CS43130_SP_NUM_1 */ +#define CS43130_SP_N_LSB_DATA_MASK 0x00FF +#define CS43130_SP_N_LSB_DATA_SHIFT 0 + +/* Reg CS43130_SP_NUM_2 */ +#define CS43130_SP_N_MSB_DATA_MASK 0xFF00 +#define CS43130_SP_N_MSB_DATA_SHIFT 8 + +/* Reg CS43130_SP_LRCK_HI_TIME_1 */ +#define CS43130_SP_LCHI_DATA_MASK 0x00FF +#define CS43130_SP_LCHI_LSB_DATA_SHIFT 0 + +/* Reg CS43130_SP_LRCK_HI_TIME_2 */ +#define CS43130_SP_LCHI_MSB_DATA_SHIFT 8 + +/* Reg CS43130_SP_LRCK_PERIOD_1 */ +#define CS43130_SP_LCPR_DATA_MASK 0x00FF +#define CS43130_SP_LCPR_LSB_DATA_SHIFT 0 + +/* Reg CS43130_SP_LRCK_PERIOD_2 */ +#define CS43130_SP_LCPR_MSB_DATA_SHIFT 8 + +#define CS43130_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define CS43130_DOP_FORMATS (SNDRV_PCM_FMTBIT_DSD_U16_LE | \ + SNDRV_PCM_FMTBIT_DSD_U16_BE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/* Reg CS43130_CRYSTAL_SET */ +#define CS43130_XTAL_IBIAS_MASK 0x07 + +/* Reg CS43130_PATH_CTL_1 */ +#define CS43130_MUTE_MASK 0x03 +#define CS43130_MUTE_EN 0x03 + +/* Reg CS43130_DSD_INT_CFG */ +#define CS43130_DSD_MASTER 0x04 + +/* Reg CS43130_DSD_PATH_CTL_2 */ +#define CS43130_DSD_SRC_MASK 0x60 +#define CS43130_DSD_SRC_SHIFT 5 +#define CS43130_DSD_EN_SHIFT 4 +#define CS43130_DSD_SPEED_MASK 0x04 +#define CS43130_DSD_SPEED_SHIFT 2 + +/* Reg CS43130_DSD_PCM_MIX_CTL */ +#define CS43130_MIX_PCM_PREP_SHIFT 1 +#define CS43130_MIX_PCM_PREP_MASK 0x02 + +#define CS43130_MIX_PCM_DSD_SHIFT 0 +#define CS43130_MIX_PCM_DSD_MASK 0x01 + +/* Reg CS43130_HP_MEAS_LOAD */ +#define CS43130_HP_MEAS_LOAD_MASK 0x000000FF +#define CS43130_HP_MEAS_LOAD_1_SHIFT 0 +#define CS43130_HP_MEAS_LOAD_2_SHIFT 8 + +#define CS43130_MCLK_22M 22579200 +#define CS43130_MCLK_24M 24576000 + +#define CS43130_LINEOUT_LOAD 5000 +#define CS43130_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT) +#define CS43130_JACK_HEADPHONE (SND_JACK_MECHANICAL | \ + SND_JACK_HEADPHONE) +#define CS43130_JACK_MASK (SND_JACK_MECHANICAL | \ + SND_JACK_LINEOUT | \ + SND_JACK_HEADPHONE) + +enum cs43130_dsd_src { + CS43130_DSD_SRC_DSD = 0, + CS43130_DSD_SRC_ASP = 2, + CS43130_DSD_SRC_XSP = 3, +}; + +enum cs43130_asp_rate { + CS43130_ASP_SPRATE_32K = 0, + CS43130_ASP_SPRATE_44_1K, + CS43130_ASP_SPRATE_48K, + CS43130_ASP_SPRATE_88_2K, + CS43130_ASP_SPRATE_96K, + CS43130_ASP_SPRATE_176_4K, + CS43130_ASP_SPRATE_192K, + CS43130_ASP_SPRATE_352_8K, + CS43130_ASP_SPRATE_384K, +}; + +enum cs43130_mclk_src_sel { + CS43130_MCLK_SRC_EXT = 0, + CS43130_MCLK_SRC_PLL, + CS43130_MCLK_SRC_RCO +}; + +enum cs43130_mclk_int_freq { + CS43130_MCLK_24P5 = 0, + CS43130_MCLK_22P5, +}; + +enum cs43130_xtal_ibias { + CS43130_XTAL_UNUSED = -1, + CS43130_XTAL_IBIAS_15UA = 2, + CS43130_XTAL_IBIAS_12_5UA = 4, + CS43130_XTAL_IBIAS_7_5UA = 6, +}; + +enum cs43130_dai_id { + CS43130_ASP_PCM_DAI = 0, + CS43130_ASP_DOP_DAI, + CS43130_XSP_DOP_DAI, + CS43130_XSP_DSD_DAI, + CS43130_DAI_ID_MAX, +}; + +struct cs43130_clk_gen { + unsigned int mclk_int; + int fs; + struct u16_fract v; +}; + +/* frm_size = 16 */ +static const struct cs43130_clk_gen cs43130_16_clk_gen[] = { + { 22579200, 32000, .v = { 10, 441, }, }, + { 22579200, 44100, .v = { 1, 32, }, }, + { 22579200, 48000, .v = { 5, 147, }, }, + { 22579200, 88200, .v = { 1, 16, }, }, + { 22579200, 96000, .v = { 10, 147, }, }, + { 22579200, 176400, .v = { 1, 8, }, }, + { 22579200, 192000, .v = { 20, 147, }, }, + { 22579200, 352800, .v = { 1, 4, }, }, + { 22579200, 384000, .v = { 40, 147, }, }, + { 24576000, 32000, .v = { 1, 48, }, }, + { 24576000, 44100, .v = { 147, 5120, }, }, + { 24576000, 48000, .v = { 1, 32, }, }, + { 24576000, 88200, .v = { 147, 2560, }, }, + { 24576000, 96000, .v = { 1, 16, }, }, + { 24576000, 176400, .v = { 147, 1280, }, }, + { 24576000, 192000, .v = { 1, 8, }, }, + { 24576000, 352800, .v = { 147, 640, }, }, + { 24576000, 384000, .v = { 1, 4, }, }, +}; + +/* frm_size = 32 */ +static const struct cs43130_clk_gen cs43130_32_clk_gen[] = { + { 22579200, 32000, .v = { 20, 441, }, }, + { 22579200, 44100, .v = { 1, 16, }, }, + { 22579200, 48000, .v = { 10, 147, }, }, + { 22579200, 88200, .v = { 1, 8, }, }, + { 22579200, 96000, .v = { 20, 147, }, }, + { 22579200, 176400, .v = { 1, 4, }, }, + { 22579200, 192000, .v = { 40, 147, }, }, + { 22579200, 352800, .v = { 1, 2, }, }, + { 22579200, 384000, .v = { 80, 147, }, }, + { 24576000, 32000, .v = { 1, 24, }, }, + { 24576000, 44100, .v = { 147, 2560, }, }, + { 24576000, 48000, .v = { 1, 16, }, }, + { 24576000, 88200, .v = { 147, 1280, }, }, + { 24576000, 96000, .v = { 1, 8, }, }, + { 24576000, 176400, .v = { 147, 640, }, }, + { 24576000, 192000, .v = { 1, 4, }, }, + { 24576000, 352800, .v = { 147, 320, }, }, + { 24576000, 384000, .v = { 1, 2, }, }, +}; + +/* frm_size = 48 */ +static const struct cs43130_clk_gen cs43130_48_clk_gen[] = { + { 22579200, 32000, .v = { 100, 147, }, }, + { 22579200, 44100, .v = { 3, 32, }, }, + { 22579200, 48000, .v = { 5, 49, }, }, + { 22579200, 88200, .v = { 3, 16, }, }, + { 22579200, 96000, .v = { 10, 49, }, }, + { 22579200, 176400, .v = { 3, 8, }, }, + { 22579200, 192000, .v = { 20, 49, }, }, + { 22579200, 352800, .v = { 3, 4, }, }, + { 22579200, 384000, .v = { 40, 49, }, }, + { 24576000, 32000, .v = { 1, 16, }, }, + { 24576000, 44100, .v = { 441, 5120, }, }, + { 24576000, 48000, .v = { 3, 32, }, }, + { 24576000, 88200, .v = { 441, 2560, }, }, + { 24576000, 96000, .v = { 3, 16, }, }, + { 24576000, 176400, .v = { 441, 1280, }, }, + { 24576000, 192000, .v = { 3, 8, }, }, + { 24576000, 352800, .v = { 441, 640, }, }, + { 24576000, 384000, .v = { 3, 4, }, }, +}; + +/* frm_size = 64 */ +static const struct cs43130_clk_gen cs43130_64_clk_gen[] = { + { 22579200, 32000, .v = { 40, 441, }, }, + { 22579200, 44100, .v = { 1, 8, }, }, + { 22579200, 48000, .v = { 20, 147, }, }, + { 22579200, 88200, .v = { 1, 4, }, }, + { 22579200, 96000, .v = { 40, 147, }, }, + { 22579200, 176400, .v = { 1, 2, }, }, + { 22579200, 192000, .v = { 80, 147, }, }, + { 22579200, 352800, .v = { 1, 1, }, }, + { 24576000, 32000, .v = { 1, 12, }, }, + { 24576000, 44100, .v = { 147, 1280, }, }, + { 24576000, 48000, .v = { 1, 8, }, }, + { 24576000, 88200, .v = { 147, 640, }, }, + { 24576000, 96000, .v = { 1, 4, }, }, + { 24576000, 176400, .v = { 147, 320, }, }, + { 24576000, 192000, .v = { 1, 2, }, }, + { 24576000, 352800, .v = { 147, 160, }, }, + { 24576000, 384000, .v = { 1, 1, }, }, +}; + +struct cs43130_bitwidth_map { + unsigned int bitwidth; + u8 sp_bit; + u8 ch_bit; +}; + +struct cs43130_rate_map { + int fs; + int val; +}; + +#define HP_LEFT 0 +#define HP_RIGHT 1 +#define CS43130_AC_FREQ 10 +#define CS43130_DC_THRESHOLD 2 + +#define CS43130_NUM_SUPPLIES 5 +static const char *const cs43130_supply_names[CS43130_NUM_SUPPLIES] = { + "VA", + "VP", + "VCP", + "VD", + "VL", +}; + +#define CS43130_NUM_INT 5 /* number of interrupt status reg */ + +struct cs43130_dai { + unsigned int sclk; + unsigned int dai_format; + unsigned int dai_mode; +}; + +struct cs43130_private { + struct snd_soc_component *component; + struct regmap *regmap; + struct regulator_bulk_data supplies[CS43130_NUM_SUPPLIES]; + struct gpio_desc *reset_gpio; + unsigned int dev_id; /* codec device ID */ + int xtal_ibias; + + /* shared by both DAIs */ + struct mutex clk_mutex; + int clk_req; + bool pll_bypass; + struct completion xtal_rdy; + struct completion pll_rdy; + unsigned int mclk; + unsigned int mclk_int; + int mclk_int_src; + + /* DAI specific */ + struct cs43130_dai dais[CS43130_DAI_ID_MAX]; + + /* HP load specific */ + bool dc_meas; + bool ac_meas; + bool hpload_done; + struct completion hpload_evt; + unsigned int hpload_stat; + u16 hpload_dc[2]; + u16 dc_threshold[CS43130_DC_THRESHOLD]; + u16 ac_freq[CS43130_AC_FREQ]; + u16 hpload_ac[CS43130_AC_FREQ][2]; + struct workqueue_struct *wq; + struct work_struct work; + struct snd_soc_jack jack; +}; + +#endif /* __CS43130_H__ */ diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c new file mode 100644 index 0000000000..2ceca5d0e5 --- /dev/null +++ b/sound/soc/codecs/cs4341.c @@ -0,0 +1,352 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Cirrus Logic CS4341A ALSA SoC Codec Driver + * Author: Alexander Shiyan + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define CS4341_REG_MODE1 0x00 +#define CS4341_REG_MODE2 0x01 +#define CS4341_REG_MIX 0x02 +#define CS4341_REG_VOLA 0x03 +#define CS4341_REG_VOLB 0x04 + +#define CS4341_MODE2_DIF (7 << 4) +#define CS4341_MODE2_DIF_I2S_24 (0 << 4) +#define CS4341_MODE2_DIF_I2S_16 (1 << 4) +#define CS4341_MODE2_DIF_LJ_24 (2 << 4) +#define CS4341_MODE2_DIF_RJ_24 (3 << 4) +#define CS4341_MODE2_DIF_RJ_16 (5 << 4) +#define CS4341_VOLX_MUTE (1 << 7) + +struct cs4341_priv { + unsigned int fmt; + struct regmap *regmap; + struct regmap_config regcfg; +}; + +static const struct reg_default cs4341_reg_defaults[] = { + { CS4341_REG_MODE1, 0x00 }, + { CS4341_REG_MODE2, 0x82 }, + { CS4341_REG_MIX, 0x49 }, + { CS4341_REG_VOLA, 0x80 }, + { CS4341_REG_VOLB, 0x80 }, +}; + +static int cs4341_set_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct snd_soc_component *component = dai->component; + struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component); + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + cs4341->fmt = format & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs4341_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component); + unsigned int mode = 0; + int b24 = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S24_LE: + b24 = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + break; + default: + dev_err(component->dev, "Unsupported PCM format 0x%08x.\n", + params_format(params)); + return -EINVAL; + } + + switch (cs4341->fmt) { + case SND_SOC_DAIFMT_I2S: + mode = b24 ? CS4341_MODE2_DIF_I2S_24 : CS4341_MODE2_DIF_I2S_16; + break; + case SND_SOC_DAIFMT_LEFT_J: + mode = CS4341_MODE2_DIF_LJ_24; + break; + case SND_SOC_DAIFMT_RIGHT_J: + mode = b24 ? CS4341_MODE2_DIF_RJ_24 : CS4341_MODE2_DIF_RJ_16; + break; + default: + dev_err(component->dev, "Unsupported DAI format 0x%08x.\n", + cs4341->fmt); + return -EINVAL; + } + + return snd_soc_component_update_bits(component, CS4341_REG_MODE2, + CS4341_MODE2_DIF, mode); +} + +static int cs4341_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + int ret; + + ret = snd_soc_component_update_bits(component, CS4341_REG_VOLA, + CS4341_VOLX_MUTE, + mute ? CS4341_VOLX_MUTE : 0); + if (ret < 0) + return ret; + + return snd_soc_component_update_bits(component, CS4341_REG_VOLB, + CS4341_VOLX_MUTE, + mute ? CS4341_VOLX_MUTE : 0); +} + +static DECLARE_TLV_DB_SCALE(out_tlv, -9000, 100, 0); + +static const char * const deemph[] = { + "None", "44.1k", "48k", "32k", +}; + +static const struct soc_enum deemph_enum = + SOC_ENUM_SINGLE(CS4341_REG_MODE2, 2, 4, deemph); + +static const char * const srzc[] = { + "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC", +}; + +static const struct soc_enum srzc_enum = + SOC_ENUM_SINGLE(CS4341_REG_MIX, 5, 4, srzc); + + +static const struct snd_soc_dapm_widget cs4341_dapm_widgets[] = { + SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OutA"), + SND_SOC_DAPM_OUTPUT("OutB"), +}; + +static const struct snd_soc_dapm_route cs4341_routes[] = { + { "OutA", NULL, "HiFi DAC" }, + { "OutB", NULL, "HiFi DAC" }, + { "DAC Playback", NULL, "OutA" }, + { "DAC Playback", NULL, "OutB" }, +}; + +static const struct snd_kcontrol_new cs4341_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", + CS4341_REG_VOLA, CS4341_REG_VOLB, 0, 90, 1, out_tlv), + SOC_ENUM("De-Emphasis Control", deemph_enum), + SOC_ENUM("Soft Ramp Zero Cross Control", srzc_enum), + SOC_SINGLE("Auto-Mute Switch", CS4341_REG_MODE2, 7, 1, 0), + SOC_SINGLE("Popguard Transient Switch", CS4341_REG_MODE2, 1, 1, 0), +}; + +static const struct snd_soc_dai_ops cs4341_dai_ops = { + .set_fmt = cs4341_set_fmt, + .hw_params = cs4341_hw_params, + .mute_stream = cs4341_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver cs4341_dai = { + .name = "cs4341a-hifi", + .playback = { + .stream_name = "DAC Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &cs4341_dai_ops, + .symmetric_rate = 1, +}; + +static const struct snd_soc_component_driver soc_component_cs4341 = { + .controls = cs4341_controls, + .num_controls = ARRAY_SIZE(cs4341_controls), + .dapm_widgets = cs4341_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4341_dapm_widgets), + .dapm_routes = cs4341_routes, + .num_dapm_routes = ARRAY_SIZE(cs4341_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id __maybe_unused cs4341_dt_ids[] = { + { .compatible = "cirrus,cs4341a", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs4341_dt_ids); + +static int cs4341_probe(struct device *dev) +{ + struct cs4341_priv *cs4341 = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(cs4341_reg_defaults); i++) + regmap_write(cs4341->regmap, cs4341_reg_defaults[i].reg, + cs4341_reg_defaults[i].def); + + return devm_snd_soc_register_component(dev, &soc_component_cs4341, + &cs4341_dai, 1); +} + +#if IS_ENABLED(CONFIG_I2C) +static int cs4341_i2c_probe(struct i2c_client *i2c) +{ + struct cs4341_priv *cs4341; + + cs4341 = devm_kzalloc(&i2c->dev, sizeof(*cs4341), GFP_KERNEL); + if (!cs4341) + return -ENOMEM; + + i2c_set_clientdata(i2c, cs4341); + + cs4341->regcfg.reg_bits = 8; + cs4341->regcfg.val_bits = 8; + cs4341->regcfg.max_register = CS4341_REG_VOLB; + cs4341->regcfg.cache_type = REGCACHE_FLAT; + cs4341->regcfg.reg_defaults = cs4341_reg_defaults; + cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults); + cs4341->regmap = devm_regmap_init_i2c(i2c, &cs4341->regcfg); + if (IS_ERR(cs4341->regmap)) + return PTR_ERR(cs4341->regmap); + + return cs4341_probe(&i2c->dev); +} + +static const struct i2c_device_id cs4341_i2c_id[] = { + { "cs4341", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id); + +static struct i2c_driver cs4341_i2c_driver = { + .driver = { + .name = "cs4341-i2c", + .of_match_table = of_match_ptr(cs4341_dt_ids), + }, + .probe = cs4341_i2c_probe, + .id_table = cs4341_i2c_id, +}; +#endif + +#if defined(CONFIG_SPI_MASTER) +static bool cs4341_reg_readable(struct device *dev, unsigned int reg) +{ + return false; +} + +static int cs4341_spi_probe(struct spi_device *spi) +{ + struct cs4341_priv *cs4341; + int ret; + + cs4341 = devm_kzalloc(&spi->dev, sizeof(*cs4341), GFP_KERNEL); + if (!cs4341) + return -ENOMEM; + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + if (!spi->max_speed_hz) + spi->max_speed_hz = 6000000; + ret = spi_setup(spi); + if (ret) + return ret; + + spi_set_drvdata(spi, cs4341); + + cs4341->regcfg.reg_bits = 16; + cs4341->regcfg.val_bits = 8; + cs4341->regcfg.write_flag_mask = 0x20; + cs4341->regcfg.max_register = CS4341_REG_VOLB; + cs4341->regcfg.cache_type = REGCACHE_FLAT; + cs4341->regcfg.readable_reg = cs4341_reg_readable; + cs4341->regcfg.reg_defaults = cs4341_reg_defaults; + cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults); + cs4341->regmap = devm_regmap_init_spi(spi, &cs4341->regcfg); + if (IS_ERR(cs4341->regmap)) + return PTR_ERR(cs4341->regmap); + + return cs4341_probe(&spi->dev); +} + +static const struct spi_device_id cs4341_spi_ids[] = { + { "cs4341a" }, + { } +}; +MODULE_DEVICE_TABLE(spi, cs4341_spi_ids); + +static struct spi_driver cs4341_spi_driver = { + .driver = { + .name = "cs4341-spi", + .of_match_table = of_match_ptr(cs4341_dt_ids), + }, + .probe = cs4341_spi_probe, + .id_table = cs4341_spi_ids, +}; +#endif + +static int __init cs4341_init(void) +{ + int ret = 0; + +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&cs4341_i2c_driver); + if (ret) + return ret; +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&cs4341_spi_driver); +#endif + + return ret; +} +module_init(cs4341_init); + +static void __exit cs4341_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&cs4341_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&cs4341_spi_driver); +#endif +} +module_exit(cs4341_exit); + +MODULE_AUTHOR("Alexander Shiyan "); +MODULE_DESCRIPTION("Cirrus Logic CS4341 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c new file mode 100644 index 0000000000..ef08e51901 --- /dev/null +++ b/sound/soc/codecs/cs4349.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs4349.c -- CS4349 ALSA Soc Audio driver + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Authors: Tim Howe + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs4349.h" + + +static const struct reg_default cs4349_reg_defaults[] = { + { 2, 0x00 }, /* r02 - Mode Control */ + { 3, 0x09 }, /* r03 - Volume, Mixing and Inversion Control */ + { 4, 0x81 }, /* r04 - Mute Control */ + { 5, 0x00 }, /* r05 - Channel A Volume Control */ + { 6, 0x00 }, /* r06 - Channel B Volume Control */ + { 7, 0xB1 }, /* r07 - Ramp and Filter Control */ + { 8, 0x1C }, /* r08 - Misc. Control */ +}; + +/* Private data for the CS4349 */ +struct cs4349_private { + struct regmap *regmap; + struct gpio_desc *reset_gpio; + unsigned int mode; + int rate; +}; + +static bool cs4349_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4349_CHIPID ... CS4349_MISC: + return true; + default: + return false; + } +} + +static bool cs4349_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4349_MODE ... CS4349_MISC: + return true; + default: + return false; + } +} + +static int cs4349_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs4349_private *cs4349 = snd_soc_component_get_drvdata(component); + unsigned int fmt; + + fmt = format & SND_SOC_DAIFMT_FORMAT_MASK; + + switch (fmt) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + cs4349->mode = format & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs4349_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs4349_private *cs4349 = snd_soc_component_get_drvdata(component); + int fmt, ret; + + cs4349->rate = params_rate(params); + + switch (cs4349->mode) { + case SND_SOC_DAIFMT_I2S: + fmt = DIF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + fmt = DIF_LEFT_JST; + break; + case SND_SOC_DAIFMT_RIGHT_J: + switch (params_width(params)) { + case 16: + fmt = DIF_RGHT_JST16; + break; + case 24: + fmt = DIF_RGHT_JST24; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, CS4349_MODE, DIF_MASK, + MODE_FORMAT(fmt)); + if (ret < 0) + return ret; + + return 0; +} + +static int cs4349_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + int reg; + + reg = 0; + if (mute) + reg = MUTE_AB_MASK; + + return snd_soc_component_update_bits(component, CS4349_MUTE, MUTE_AB_MASK, reg); +} + +static DECLARE_TLV_DB_SCALE(dig_tlv, -12750, 50, 0); + +static const char * const chan_mix_texts[] = { + "Mute", "MuteA", "MuteA SwapB", "MuteA MonoB", "SwapA MuteB", + "BothR", "Swap", "SwapA MonoB", "MuteB", "Normal", "BothL", + "MonoB", "MonoA MuteB", "MonoA", "MonoA SwapB", "Mono", + /*Normal == Channel A = Left, Channel B = Right*/ +}; + +static const char * const fm_texts[] = { + "Auto", "Single", "Double", "Quad", +}; + +static const char * const deemph_texts[] = { + "None", "44.1k", "48k", "32k", +}; + +static const char * const softr_zeroc_texts[] = { + "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC", +}; + +static int deemph_values[] = { + 0, 4, 8, 12, +}; + +static int softr_zeroc_values[] = { + 0, 64, 128, 192, +}; + +static const struct soc_enum chan_mix_enum = + SOC_ENUM_SINGLE(CS4349_VMI, 0, + ARRAY_SIZE(chan_mix_texts), + chan_mix_texts); + +static const struct soc_enum fm_mode_enum = + SOC_ENUM_SINGLE(CS4349_MODE, 0, + ARRAY_SIZE(fm_texts), + fm_texts); + +static SOC_VALUE_ENUM_SINGLE_DECL(deemph_enum, CS4349_MODE, 0, DEM_MASK, + deemph_texts, deemph_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(softr_zeroc_enum, CS4349_RMPFLT, 0, + SR_ZC_MASK, softr_zeroc_texts, + softr_zeroc_values); + +static const struct snd_kcontrol_new cs4349_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", + CS4349_VOLA, CS4349_VOLB, 0, 0xFF, 1, dig_tlv), + SOC_ENUM("Functional Mode", fm_mode_enum), + SOC_ENUM("De-Emphasis Control", deemph_enum), + SOC_ENUM("Soft Ramp Zero Cross Control", softr_zeroc_enum), + SOC_ENUM("Channel Mixer", chan_mix_enum), + SOC_SINGLE("VolA = VolB Switch", CS4349_VMI, 7, 1, 0), + SOC_SINGLE("InvertA Switch", CS4349_VMI, 6, 1, 0), + SOC_SINGLE("InvertB Switch", CS4349_VMI, 5, 1, 0), + SOC_SINGLE("Auto-Mute Switch", CS4349_MUTE, 7, 1, 0), + SOC_SINGLE("MUTEC A = B Switch", CS4349_MUTE, 5, 1, 0), + SOC_SINGLE("Soft Ramp Up Switch", CS4349_RMPFLT, 5, 1, 0), + SOC_SINGLE("Soft Ramp Down Switch", CS4349_RMPFLT, 4, 1, 0), + SOC_SINGLE("Slow Roll Off Filter Switch", CS4349_RMPFLT, 2, 1, 0), + SOC_SINGLE("Freeze Switch", CS4349_MISC, 5, 1, 0), + SOC_SINGLE("Popguard Switch", CS4349_MISC, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget cs4349_dapm_widgets[] = { + SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("OutputA"), + SND_SOC_DAPM_OUTPUT("OutputB"), +}; + +static const struct snd_soc_dapm_route cs4349_routes[] = { + {"DAC Playback", NULL, "OutputA"}, + {"DAC Playback", NULL, "OutputB"}, + + {"OutputA", NULL, "HiFi DAC"}, + {"OutputB", NULL, "HiFi DAC"}, +}; + +#define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define CS4349_PCM_RATES SNDRV_PCM_RATE_8000_192000 + +static const struct snd_soc_dai_ops cs4349_dai_ops = { + .hw_params = cs4349_pcm_hw_params, + .set_fmt = cs4349_set_dai_fmt, + .mute_stream = cs4349_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver cs4349_dai = { + .name = "cs4349_hifi", + .playback = { + .stream_name = "DAC Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS4349_PCM_RATES, + .formats = CS4349_PCM_FORMATS, + }, + .ops = &cs4349_dai_ops, + .symmetric_rate = 1, +}; + +static const struct snd_soc_component_driver soc_component_dev_cs4349 = { + .controls = cs4349_snd_controls, + .num_controls = ARRAY_SIZE(cs4349_snd_controls), + .dapm_widgets = cs4349_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4349_dapm_widgets), + .dapm_routes = cs4349_routes, + .num_dapm_routes = ARRAY_SIZE(cs4349_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config cs4349_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS4349_MISC, + .reg_defaults = cs4349_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs4349_reg_defaults), + .readable_reg = cs4349_readable_register, + .writeable_reg = cs4349_writeable_register, + .cache_type = REGCACHE_MAPLE, +}; + +static int cs4349_i2c_probe(struct i2c_client *client) +{ + struct cs4349_private *cs4349; + int ret; + + cs4349 = devm_kzalloc(&client->dev, sizeof(*cs4349), GFP_KERNEL); + if (!cs4349) + return -ENOMEM; + + cs4349->regmap = devm_regmap_init_i2c(client, &cs4349_regmap); + if (IS_ERR(cs4349->regmap)) { + ret = PTR_ERR(cs4349->regmap); + dev_err(&client->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + /* Reset the Device */ + cs4349->reset_gpio = devm_gpiod_get_optional(&client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs4349->reset_gpio)) + return PTR_ERR(cs4349->reset_gpio); + + gpiod_set_value_cansleep(cs4349->reset_gpio, 1); + + i2c_set_clientdata(client, cs4349); + + return devm_snd_soc_register_component(&client->dev, + &soc_component_dev_cs4349, + &cs4349_dai, 1); +} + +static void cs4349_i2c_remove(struct i2c_client *client) +{ + struct cs4349_private *cs4349 = i2c_get_clientdata(client); + + /* Hold down reset */ + gpiod_set_value_cansleep(cs4349->reset_gpio, 0); +} + +#ifdef CONFIG_PM +static int cs4349_runtime_suspend(struct device *dev) +{ + struct cs4349_private *cs4349 = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(cs4349->regmap, CS4349_MISC, PWR_DWN, PWR_DWN); + if (ret < 0) + return ret; + + regcache_cache_only(cs4349->regmap, true); + + /* Hold down reset */ + gpiod_set_value_cansleep(cs4349->reset_gpio, 0); + + return 0; +} + +static int cs4349_runtime_resume(struct device *dev) +{ + struct cs4349_private *cs4349 = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(cs4349->regmap, CS4349_MISC, PWR_DWN, 0); + if (ret < 0) + return ret; + + gpiod_set_value_cansleep(cs4349->reset_gpio, 1); + + regcache_cache_only(cs4349->regmap, false); + regcache_sync(cs4349->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops cs4349_runtime_pm = { + SET_RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume, + NULL) +}; + +static const struct of_device_id cs4349_of_match[] = { + { .compatible = "cirrus,cs4349", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, cs4349_of_match); + +static const struct i2c_device_id cs4349_i2c_id[] = { + {"cs4349", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs4349_i2c_id); + +static struct i2c_driver cs4349_i2c_driver = { + .driver = { + .name = "cs4349", + .of_match_table = cs4349_of_match, + .pm = &cs4349_runtime_pm, + }, + .id_table = cs4349_i2c_id, + .probe = cs4349_i2c_probe, + .remove = cs4349_i2c_remove, +}; + +module_i2c_driver(cs4349_i2c_driver); + +MODULE_AUTHOR("Tim Howe "); +MODULE_DESCRIPTION("Cirrus Logic CS4349 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs4349.h b/sound/soc/codecs/cs4349.h new file mode 100644 index 0000000000..bf31405f7f --- /dev/null +++ b/sound/soc/codecs/cs4349.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC CS4349 codec driver + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Tim Howe + */ + +#ifndef __CS4349_H__ +#define __CS4349_H__ + +/* CS4349 registers addresses */ +#define CS4349_CHIPID 0x01 /* Device and Rev ID, Read Only */ +#define CS4349_MODE 0x02 /* Mode Control */ +#define CS4349_VMI 0x03 /* Volume, Mixing, Inversion Control */ +#define CS4349_MUTE 0x04 /* Mute Control */ +#define CS4349_VOLA 0x05 /* DAC Channel A Volume Control */ +#define CS4349_VOLB 0x06 /* DAC Channel B Volume Control */ +#define CS4349_RMPFLT 0x07 /* Ramp and Filter Control */ +#define CS4349_MISC 0x08 /* Power Down,Freeze Control,Pop Stop*/ + +#define CS4349_I2C_INCR 0x80 + + +/* Device and Revision ID */ +#define CS4349_REVA 0xF0 /* Rev A */ +#define CS4349_REVB 0xF1 /* Rev B */ +#define CS4349_REVC2 0xFF /* Rev C2 */ + + +/* PDN_DONE Poll Maximum + * If soft ramp is set it will take much longer to power down + * the system. + */ +#define PDN_POLL_MAX 900 + + +/* Bitfield Definitions */ + +/* CS4349_MODE */ +/* (Digital Interface Format, De-Emphasis Control, Functional Mode */ +#define DIF2 (1 << 6) +#define DIF1 (1 << 5) +#define DIF0 (1 << 4) +#define DEM1 (1 << 3) +#define DEM0 (1 << 2) +#define FM1 (1 << 1) +#define DIF_LEFT_JST 0x00 +#define DIF_I2S 0x01 +#define DIF_RGHT_JST16 0x02 +#define DIF_RGHT_JST24 0x03 +#define DIF_TDM0 0x04 +#define DIF_TDM1 0x05 +#define DIF_TDM2 0x06 +#define DIF_TDM3 0x07 +#define DIF_MASK 0x70 +#define MODE_FORMAT(x) (((x)&7)<<4) +#define DEM_MASK 0x0C +#define NO_DEM 0x00 +#define DEM_441 0x04 +#define DEM_48K 0x08 +#define DEM_32K 0x0C +#define FM_AUTO 0x00 +#define FM_SNGL 0x01 +#define FM_DBL 0x02 +#define FM_QUAD 0x03 +#define FM_SNGL_MIN 30000 +#define FM_SNGL_MAX 54000 +#define FM_DBL_MAX 108000 +#define FM_QUAD_MAX 216000 +#define FM_MASK 0x03 + +/* CS4349_VMI (VMI = Volume, Mixing and Inversion Controls) */ +#define VOLBISA (1 << 7) +#define VOLAISB (1 << 7) +/* INVERT_A only available for Left Jstfd, Right Jstfd16 and Right Jstfd24 */ +#define INVERT_A (1 << 6) +/* INVERT_B only available for Left Jstfd, Right Jstfd16 and Right Jstfd24 */ +#define INVERT_B (1 << 5) +#define ATAPI3 (1 << 3) +#define ATAPI2 (1 << 2) +#define ATAPI1 (1 << 1) +#define ATAPI0 (1 << 0) +#define MUTEAB 0x00 +#define MUTEA_RIGHTB 0x01 +#define MUTEA_LEFTB 0x02 +#define MUTEA_SUMLRDIV2B 0x03 +#define RIGHTA_MUTEB 0x04 +#define RIGHTA_RIGHTB 0x05 +#define RIGHTA_LEFTB 0x06 +#define RIGHTA_SUMLRDIV2B 0x07 +#define LEFTA_MUTEB 0x08 +#define LEFTA_RIGHTB 0x09 /* Default */ +#define LEFTA_LEFTB 0x0A +#define LEFTA_SUMLRDIV2B 0x0B +#define SUMLRDIV2A_MUTEB 0x0C +#define SUMLRDIV2A_RIGHTB 0x0D +#define SUMLRDIV2A_LEFTB 0x0E +#define SUMLRDIV2_AB 0x0F +#define CHMIX_MASK 0x0F + +/* CS4349_MUTE */ +#define AUTOMUTE (1 << 7) +#define MUTEC_AB (1 << 5) +#define MUTE_A (1 << 4) +#define MUTE_B (1 << 3) +#define MUTE_AB_MASK 0x18 + +/* CS4349_RMPFLT (Ramp and Filter Control) */ +#define SCZ1 (1 << 7) +#define SCZ0 (1 << 6) +#define RMP_UP (1 << 5) +#define RMP_DN (1 << 4) +#define FILT_SEL (1 << 2) +#define IMMDT_CHNG 0x31 +#define ZEROCRSS 0x71 +#define SOFT_RMP 0xB1 +#define SFTRMP_ZEROCRSS 0xF1 +#define SR_ZC_MASK 0xC0 + +/* CS4349_MISC */ +#define PWR_DWN (1 << 7) +#define FREEZE (1 << 5) +#define POPG_EN (1 << 4) + +#endif /* __CS4349_H__ */ diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c new file mode 100644 index 0000000000..1245e1a4f2 --- /dev/null +++ b/sound/soc/codecs/cs47l15.c @@ -0,0 +1,1506 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// ALSA SoC Audio driver for CS47L15 codec +// +// Copyright (C) 2016-2019 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "madera.h" +#include "wm_adsp.h" + +#define CS47L15_NUM_ADSP 1 +#define CS47L15_MONO_OUTPUTS 1 + +/* Mid-mode registers */ +#define CS47L15_ADC_INT_BIAS_MASK 0x3800 +#define CS47L15_ADC_INT_BIAS_SHIFT 11 +#define CS47L15_PGA_BIAS_SEL_MASK 0x03 +#define CS47L15_PGA_BIAS_SEL_SHIFT 0 + +#define DRV_NAME "cs47l15-codec" + +struct cs47l15 { + struct madera_priv core; + struct madera_fll fll[2]; + + bool in1_lp_mode; +}; + +static const struct cs_dsp_region cs47l15_dsp1_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x080000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x0a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x0c0000 }, +}; + +static const char * const cs47l15_outdemux_texts[] = { + "HPOUT", + "EPOUT", +}; + +static SOC_ENUM_SINGLE_DECL(cs47l15_outdemux_enum, SND_SOC_NOPM, 0, + cs47l15_outdemux_texts); + +static const struct snd_kcontrol_new cs47l15_outdemux = + SOC_DAPM_ENUM_EXT("HPOUT1 Demux", cs47l15_outdemux_enum, + madera_out1_demux_get, madera_out1_demux_put); + +static int cs47l15_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l15->core; + struct madera *madera = priv->madera; + unsigned int freq; + int ret; + + ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_2, &freq); + if (ret != 0) { + dev_err(madera->dev, + "Failed to read MADERA_DSP_CLOCK_2: %d\n", ret); + return ret; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = madera_set_adsp_clk(&cs47l15->core, w->shift, freq); + if (ret) + return ret; + break; + default: + break; + } + + return wm_adsp_early_event(w, kcontrol, event); +} + +#define CS47L15_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + +static int cs47l15_in1_adc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !!cs47l15->in1_lp_mode; + + return 0; +} + +static int cs47l15_in1_adc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); + + if (!!ucontrol->value.integer.value[0] == cs47l15->in1_lp_mode) + return 0; + + switch (ucontrol->value.integer.value[0]) { + case 0: + /* Set IN1 to normal mode */ + snd_soc_component_update_bits(component, MADERA_DMIC1L_CONTROL, + MADERA_IN1_OSR_MASK, + 5 << MADERA_IN1_OSR_SHIFT); + snd_soc_component_update_bits(component, CS47L15_ADC_INT_BIAS, + CS47L15_ADC_INT_BIAS_MASK, + 4 << CS47L15_ADC_INT_BIAS_SHIFT); + snd_soc_component_update_bits(component, CS47L15_PGA_BIAS_SEL, + CS47L15_PGA_BIAS_SEL_MASK, 0); + cs47l15->in1_lp_mode = false; + break; + default: + /* Set IN1 to LP mode */ + snd_soc_component_update_bits(component, MADERA_DMIC1L_CONTROL, + MADERA_IN1_OSR_MASK, + 4 << MADERA_IN1_OSR_SHIFT); + snd_soc_component_update_bits(component, CS47L15_ADC_INT_BIAS, + CS47L15_ADC_INT_BIAS_MASK, + 1 << CS47L15_ADC_INT_BIAS_SHIFT); + snd_soc_component_update_bits(component, CS47L15_PGA_BIAS_SEL, + CS47L15_PGA_BIAS_SEL_MASK, + 3 << CS47L15_PGA_BIAS_SEL_SHIFT); + cs47l15->in1_lp_mode = true; + break; + } + + return 1; +} + +static const struct snd_kcontrol_new cs47l15_snd_controls[] = { +SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL, + MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL, + MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL, MADERA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL, MADERA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL, MADERA_IN2L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL, MADERA_IN2R_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L, + MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R, + MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L, + MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R, + MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), + +SOC_ENUM("Input Ramp Up", madera_in_vi_ramp), +SOC_ENUM("Input Ramp Down", madera_in_vd_ramp), + +MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE), + +MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2), +SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2), +SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5, + MADERA_DRC1R_ENA | MADERA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5, + MADERA_DRC2R_ENA | MADERA_DRC2L_ENA), + +MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE), + +MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2), +MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2), +MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2), +MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2), + +SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode), + +MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]), +MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]), +MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]), +MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]), + +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + +MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR, + MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv), + +MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKOUTL", MADERA_OUT4LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL, + MADERA_HP1_SC_ENA_SHIFT, 1, 0), + +SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L, + MADERA_OUT5_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Speaker Digital Switch", MADERA_DAC_DIGITAL_VOLUME_4L, + MADERA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("Speaker Digital Volume", MADERA_DAC_DIGITAL_VOLUME_4L, + MADERA_OUT4L_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), + +SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT, + MADERA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_ENUM("Output Ramp Up", madera_out_vi_ramp), +SOC_ENUM("Output Ramp Down", madera_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv), +SOC_ENUM("Noise Gate Hold", madera_ng_hold), + +SOC_SINGLE_BOOL_EXT("IN1 LP Mode Switch", 0, + cs47l15_in1_adc_get, cs47l15_in1_adc_put), + +CS47L15_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L), +CS47L15_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R), +CS47L15_NG_SRC("SPKOUTL", MADERA_NOISE_GATE_SELECT_4L), +CS47L15_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L), +CS47L15_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R), + +MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE), + +MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE), +MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), +}; + +MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKOUTL, MADERA_OUT4LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE); + +static const char * const cs47l15_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "SPKOUTL", "SPKDAT1L", "SPKDAT1R", +}; + +static const unsigned int cs47l15_aec_loopback_values[] = { + 0, 1, 6, 8, 9, +}; + +static const struct soc_enum cs47l15_aec1_loopback = + SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1, + MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l15_aec_loopback_texts), + cs47l15_aec_loopback_texts, + cs47l15_aec_loopback_values); + +static const struct soc_enum cs47l15_aec2_loopback = + SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2, + MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l15_aec_loopback_texts), + cs47l15_aec_loopback_texts, + cs47l15_aec_loopback_values); + +static const struct snd_kcontrol_new cs47l15_aec_loopback_mux[] = { + SOC_DAPM_ENUM("AEC1 Loopback", cs47l15_aec1_loopback), + SOC_DAPM_ENUM("AEC2 Loopback", cs47l15_aec2_loopback), +}; + +static const struct snd_soc_dapm_widget cs47l15_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT, + 0, madera_sysclk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK, + MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT, + 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1C", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1C_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_FX, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_OUT, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_SPD, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF3, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_PWM, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), + +SND_SOC_DAPM_INPUT("IN1ALN"), +SND_SOC_DAPM_INPUT("IN1ALP"), +SND_SOC_DAPM_INPUT("IN1BLN"), +SND_SOC_DAPM_INPUT("IN1BLP"), +SND_SOC_DAPM_INPUT("IN1ARN"), +SND_SOC_DAPM_INPUT("IN1ARP"), +SND_SOC_DAPM_INPUT("IN1BRN"), +SND_SOC_DAPM_INPUT("IN1BRP"), +SND_SOC_DAPM_INPUT("IN2N"), +SND_SOC_DAPM_INPUT("IN2P"), +SND_SOC_DAPM_INPUT("SPKRXDAT"), + +SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]), +SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]), + +SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]), +SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]), + +SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), +SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_OUTPUT("DSP Trigger Out"), + +SND_SOC_DAPM_DEMUX("HPOUT1 Demux", SND_SOC_NOPM, 0, 0, &cs47l15_outdemux), +SND_SOC_DAPM_MUX("HPOUT1 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l15_outdemux), + +SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM, + MADERA_OUT4L_ENA_SHIFT, 0, NULL, 0, madera_spk_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_ENA_SHIFT, 0, NULL, 0), + +/* + * mux_in widgets : arranged in the order of sources + * specified in MADERA_MIXER_INPUT_ROUTES + */ + +SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR, + MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1, + MADERA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1, + MADERA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1, + MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0, + &cs47l15_aec_loopback_mux[0]), +SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2, + MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0, + &cs47l15_aec_loopback_mux[1]), + +SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0), + +WM_ADSP2("DSP1", 0, cs47l15_adsp_power_ev), + +/* end of ordered widget list */ + +MADERA_MIXER_WIDGETS(EQ1, "EQ1"), +MADERA_MIXER_WIDGETS(EQ2, "EQ2"), +MADERA_MIXER_WIDGETS(EQ3, "EQ3"), +MADERA_MIXER_WIDGETS(EQ4, "EQ4"), + +MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"), +MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"), +MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"), +MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0, + &madera_drc_activity_output_mux[0]), +SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0, + &madera_drc_activity_output_mux[1]), + +MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"), +MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"), +MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"), +MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +MADERA_MIXER_WIDGETS(PWM1, "PWM1"), +MADERA_MIXER_WIDGETS(PWM2, "PWM2"), + +MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +MADERA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), + +MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), + +MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), + +MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"), +MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"), + +MADERA_DSP_WIDGETS(DSP1, "DSP1"), + +SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[0]), + +MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), +MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"), +MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"), + +MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), +MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"), +MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("EPOUTP"), +SND_SOC_DAPM_OUTPUT("EPOUTN"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +SND_SOC_DAPM_OUTPUT("SPKOUTP"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), +SND_SOC_DAPM_OUTPUT("SPDIF1"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define MADERA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC1", "AEC1 Loopback" }, \ + { name, "AEC2", "AEC2 Loopback" }, \ + { name, "IN1L", "IN1L" }, \ + { name, "IN1R", "IN1R" }, \ + { name, "IN2L", "IN2L" }, \ + { name, "IN2R", "IN2R" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2DEC3", "ISRC2DEC3" }, \ + { name, "ISRC2DEC4", "ISRC2DEC4" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC2INT3", "ISRC2INT3" }, \ + { name, "ISRC2INT4", "ISRC2INT4" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" } + +static const struct snd_soc_dapm_route cs47l15_dapm_routes[] = { + /* Internal clock domains */ + { "EQ1", NULL, "FXCLK" }, + { "EQ2", NULL, "FXCLK" }, + { "EQ3", NULL, "FXCLK" }, + { "EQ4", NULL, "FXCLK" }, + { "DRC1L", NULL, "FXCLK" }, + { "DRC1R", NULL, "FXCLK" }, + { "DRC2L", NULL, "FXCLK" }, + { "DRC2R", NULL, "FXCLK" }, + { "LHPF1", NULL, "FXCLK" }, + { "LHPF2", NULL, "FXCLK" }, + { "LHPF3", NULL, "FXCLK" }, + { "LHPF4", NULL, "FXCLK" }, + { "PWM1 Mixer", NULL, "PWMCLK" }, + { "PWM2 Mixer", NULL, "PWMCLK" }, + { "OUT1L", NULL, "OUTCLK" }, + { "OUT1R", NULL, "OUTCLK" }, + { "OUT4L", NULL, "OUTCLK" }, + { "OUT5L", NULL, "OUTCLK" }, + { "OUT5R", NULL, "OUTCLK" }, + { "AIF1TX1", NULL, "AIF1TXCLK" }, + { "AIF1TX2", NULL, "AIF1TXCLK" }, + { "AIF1TX3", NULL, "AIF1TXCLK" }, + { "AIF1TX4", NULL, "AIF1TXCLK" }, + { "AIF1TX5", NULL, "AIF1TXCLK" }, + { "AIF1TX6", NULL, "AIF1TXCLK" }, + { "AIF2TX1", NULL, "AIF2TXCLK" }, + { "AIF2TX2", NULL, "AIF2TXCLK" }, + { "AIF2TX3", NULL, "AIF2TXCLK" }, + { "AIF2TX4", NULL, "AIF2TXCLK" }, + { "AIF3TX1", NULL, "AIF3TXCLK" }, + { "AIF3TX2", NULL, "AIF3TXCLK" }, + { "SPD1TX1", NULL, "SPDCLK" }, + { "SPD1TX2", NULL, "SPDCLK" }, + { "DSP1", NULL, "DSP1CLK" }, + { "ISRC1DEC1", NULL, "ISRC1CLK" }, + { "ISRC1DEC2", NULL, "ISRC1CLK" }, + { "ISRC1DEC3", NULL, "ISRC1CLK" }, + { "ISRC1DEC4", NULL, "ISRC1CLK" }, + { "ISRC1INT1", NULL, "ISRC1CLK" }, + { "ISRC1INT2", NULL, "ISRC1CLK" }, + { "ISRC1INT3", NULL, "ISRC1CLK" }, + { "ISRC1INT4", NULL, "ISRC1CLK" }, + { "ISRC2DEC1", NULL, "ISRC2CLK" }, + { "ISRC2DEC2", NULL, "ISRC2CLK" }, + { "ISRC2DEC3", NULL, "ISRC2CLK" }, + { "ISRC2DEC4", NULL, "ISRC2CLK" }, + { "ISRC2INT1", NULL, "ISRC2CLK" }, + { "ISRC2INT2", NULL, "ISRC2CLK" }, + { "ISRC2INT3", NULL, "ISRC2CLK" }, + { "ISRC2INT4", NULL, "ISRC2CLK" }, + + { "OUT1L", NULL, "CPVDD1" }, + { "OUT1R", NULL, "CPVDD1" }, + { "OUT4L", NULL, "SPKVDD" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + + { "SPD1", NULL, "SYSCLK" }, + { "SPD1", NULL, "SPD1TX1" }, + { "SPD1", NULL, "SPD1TX2" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + + { "MICBIAS1A", NULL, "MICBIAS1" }, + { "MICBIAS1B", NULL, "MICBIAS1" }, + { "MICBIAS1C", NULL, "MICBIAS1" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + + { "Audio Trace DSP", NULL, "DSP1" }, + + { "IN1L Analog Mux", "A", "IN1ALN" }, + { "IN1L Analog Mux", "A", "IN1ALP" }, + { "IN1L Analog Mux", "B", "IN1BLN" }, + { "IN1L Analog Mux", "B", "IN1BLP" }, + { "IN1R Analog Mux", "A", "IN1ARN" }, + { "IN1R Analog Mux", "A", "IN1ARP" }, + { "IN1R Analog Mux", "B", "IN1BRN" }, + { "IN1R Analog Mux", "B", "IN1BRP" }, + + { "IN1L Mode", "Analog", "IN1L Analog Mux" }, + { "IN1R Mode", "Analog", "IN1R Analog Mux" }, + + { "IN1L Mode", "Digital", "IN1ALN" }, + { "IN1L Mode", "Digital", "IN1ALP" }, + { "IN1R Mode", "Digital", "IN1ALN" }, + { "IN1R Mode", "Digital", "IN1ALP" }, + + { "IN1L", NULL, "IN1L Mode" }, + { "IN1R", NULL, "IN1R Mode" }, + + { "IN2L Mode", "Analog", "IN2N" }, + { "IN2L Mode", "Analog", "IN2P" }, + + { "IN2L Mode", "Digital", "SPKRXDAT" }, + { "IN2R Mode", "Digital", "SPKRXDAT" }, + + { "IN2L", NULL, "IN2L Mode" }, + { "IN2R", NULL, "IN2R Mode" }, + + MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + MADERA_MIXER_ROUTES("OUT4L", "SPKOUTL"), + MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + + MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + + MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + + MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"), + MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"), + + MADERA_MIXER_ROUTES("EQ1", "EQ1"), + MADERA_MIXER_ROUTES("EQ2", "EQ2"), + MADERA_MIXER_ROUTES("EQ3", "EQ3"), + MADERA_MIXER_ROUTES("EQ4", "EQ4"), + + MADERA_MIXER_ROUTES("DRC1L", "DRC1L"), + MADERA_MIXER_ROUTES("DRC1R", "DRC1R"), + MADERA_MIXER_ROUTES("DRC2L", "DRC2L"), + MADERA_MIXER_ROUTES("DRC2R", "DRC2R"), + + MADERA_MIXER_ROUTES("LHPF1", "LHPF1"), + MADERA_MIXER_ROUTES("LHPF2", "LHPF2"), + MADERA_MIXER_ROUTES("LHPF3", "LHPF3"), + MADERA_MIXER_ROUTES("LHPF4", "LHPF4"), + + MADERA_DSP_ROUTES("DSP1"), + + { "DSP Trigger Out", NULL, "DSP1 Trigger Output" }, + + { "DSP1 Trigger Output", "Switch", "DSP1" }, + + MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"), + MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"), + + MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"), + MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"), + + { "AEC1 Loopback", "HPOUT1L", "OUT1L" }, + { "AEC1 Loopback", "HPOUT1R", "OUT1R" }, + { "AEC2 Loopback", "HPOUT1L", "OUT1L" }, + { "AEC2 Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1 Demux", NULL, "OUT1L" }, + { "HPOUT1 Demux", NULL, "OUT1R" }, + + { "OUT1R", NULL, "HPOUT1 Mono Mux" }, + { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" }, + + { "HPOUTL", "HPOUT", "HPOUT1 Demux" }, + { "HPOUTR", "HPOUT", "HPOUT1 Demux" }, + { "EPOUTP", "EPOUT", "HPOUT1 Demux" }, + { "EPOUTN", "EPOUT", "HPOUT1 Demux" }, + + { "AEC1 Loopback", "SPKOUTL", "OUT4L" }, + { "AEC2 Loopback", "SPKOUTL", "OUT4L" }, + { "SPKOUTN", NULL, "OUT4L" }, + { "SPKOUTP", NULL, "OUT4L" }, + + { "AEC1 Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC1 Loopback", "SPKDAT1R", "OUT5R" }, + { "AEC2 Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC2 Loopback", "SPKDAT1R", "OUT5R" }, + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + { "SPDIF1", NULL, "SPD1" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" }, + { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" }, + { "DRC1 Activity Output", "Switch", "DRC1L" }, + { "DRC1 Activity Output", "Switch", "DRC1R" }, + { "DRC2 Activity Output", "Switch", "DRC2L" }, + { "DRC2 Activity Output", "Switch", "DRC2R" }, +}; + +static int cs47l15_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case MADERA_FLL1_REFCLK: + return madera_set_fll_refclk(&cs47l15->fll[0], source, fref, + fout); + case MADERA_FLLAO_REFCLK: + return madera_set_fll_ao_refclk(&cs47l15->fll[1], source, fref, + fout); + case MADERA_FLL1_SYNCCLK: + return madera_set_fll_syncclk(&cs47l15->fll[0], source, fref, + fout); + default: + return -EINVAL; + } +} + +static const struct snd_soc_dai_ops cs47l15_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + +static struct snd_soc_dai_driver cs47l15_dai[] = { + { + .name = "cs47l15-aif1", + .id = 1, + .base = MADERA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l15-aif2", + .id = 2, + .base = MADERA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l15-aif3", + .id = 3, + .base = MADERA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l15-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &cs47l15_dai_ops, + }, + { + .name = "cs47l15-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + }, +}; + +static int cs47l15_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l15->core; + struct madera *madera = priv->madera; + int n_adsp; + + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l15-dsp-trace") == 0) { + n_adsp = 0; + } else { + dev_err(madera->dev, + "No suitable compressed stream for DAI '%s'\n", + asoc_rtd_to_codec(rtd, 0)->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&priv->adsp[n_adsp], stream); +} + +static irqreturn_t cs47l15_adsp2_irq(int irq, void *data) +{ + struct cs47l15 *cs47l15 = data; + struct madera_priv *priv = &cs47l15->core; + struct madera *madera = priv->madera; + int ret; + + ret = wm_adsp_compr_handle_irq(&priv->adsp[0]); + if (ret == -ENODEV) { + dev_err(madera->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static const struct snd_soc_dapm_route cs47l15_mono_routes[] = { + { "HPOUT1 Mono Mux", "HPOUT", "OUT1L" }, +}; + +static int cs47l15_component_probe(struct snd_soc_component *component) +{ + struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l15->core.madera; + int ret; + + snd_soc_component_init_regmap(component, madera->regmap); + + mutex_lock(&madera->dapm_ptr_lock); + madera->dapm = snd_soc_component_get_dapm(component); + mutex_unlock(&madera->dapm_ptr_lock); + + ret = madera_init_inputs(component); + if (ret) + return ret; + + ret = madera_init_outputs(component, cs47l15_mono_routes, + ARRAY_SIZE(cs47l15_mono_routes), + CS47L15_MONO_OUTPUTS); + if (ret) + return ret; + + snd_soc_component_disable_pin(component, "HAPTICS"); + + ret = snd_soc_add_component_controls(component, + madera_adsp_rate_controls, + CS47L15_NUM_ADSP); + if (ret) + return ret; + + wm_adsp2_component_probe(&cs47l15->core.adsp[0], component); + + return 0; +} + +static void cs47l15_component_remove(struct snd_soc_component *component) +{ + struct cs47l15 *cs47l15 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l15->core.madera; + + mutex_lock(&madera->dapm_ptr_lock); + madera->dapm = NULL; + mutex_unlock(&madera->dapm_ptr_lock); + + wm_adsp2_component_remove(&cs47l15->core.adsp[0], component); +} + +#define CS47L15_DIG_VU 0x0200 + +static unsigned int cs47l15_digital_vu[] = { + MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, + MADERA_DAC_DIGITAL_VOLUME_4L, + MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, +}; + +static const struct snd_compress_ops cs47l15_compress_ops = { + .open = &cs47l15_open, + .free = &wm_adsp_compr_free, + .set_params = &wm_adsp_compr_set_params, + .get_caps = &wm_adsp_compr_get_caps, + .trigger = &wm_adsp_compr_trigger, + .pointer = &wm_adsp_compr_pointer, + .copy = &wm_adsp_compr_copy, +}; + +static const struct snd_soc_component_driver soc_component_dev_cs47l15 = { + .probe = &cs47l15_component_probe, + .remove = &cs47l15_component_remove, + .set_sysclk = &madera_set_sysclk, + .set_pll = &cs47l15_set_fll, + .name = DRV_NAME, + .compress_ops = &cs47l15_compress_ops, + .controls = cs47l15_snd_controls, + .num_controls = ARRAY_SIZE(cs47l15_snd_controls), + .dapm_widgets = cs47l15_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs47l15_dapm_widgets), + .dapm_routes = cs47l15_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs47l15_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cs47l15_probe(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + struct cs47l15 *cs47l15; + int i, ret; + + BUILD_BUG_ON(ARRAY_SIZE(cs47l15_dai) > MADERA_MAX_DAI); + + /* quick exit if Madera irqchip driver hasn't completed probe */ + if (!madera->irq_dev) { + dev_dbg(&pdev->dev, "irqchip driver not ready\n"); + return -EPROBE_DEFER; + } + + cs47l15 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l15), + GFP_KERNEL); + if (!cs47l15) + return -ENOMEM; + + platform_set_drvdata(pdev, cs47l15); + + cs47l15->core.madera = madera; + cs47l15->core.dev = &pdev->dev; + cs47l15->core.num_inputs = 4; + + ret = madera_core_init(&cs47l15->core); + if (ret) + return ret; + + ret = madera_init_overheat(&cs47l15->core); + if (ret) + goto error_core; + + ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", cs47l15_adsp2_irq, + cs47l15); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + goto error_overheat; + } + + ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1); + if (ret) + dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret); + + cs47l15->core.adsp[0].part = "cs47l15"; + cs47l15->core.adsp[0].cs_dsp.num = 1; + cs47l15->core.adsp[0].cs_dsp.type = WMFW_ADSP2; + cs47l15->core.adsp[0].cs_dsp.rev = 2; + cs47l15->core.adsp[0].cs_dsp.dev = madera->dev; + cs47l15->core.adsp[0].cs_dsp.regmap = madera->regmap_32bit; + + cs47l15->core.adsp[0].cs_dsp.base = MADERA_DSP1_CONFIG_1; + cs47l15->core.adsp[0].cs_dsp.mem = cs47l15_dsp1_regions; + cs47l15->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(cs47l15_dsp1_regions); + + cs47l15->core.adsp[0].cs_dsp.lock_regions = + CS_ADSP2_REGION_1 | CS_ADSP2_REGION_2 | CS_ADSP2_REGION_3; + + ret = wm_adsp2_init(&cs47l15->core.adsp[0]); + if (ret != 0) + goto error_dsp_irq; + + ret = madera_init_bus_error_irq(&cs47l15->core, 0, wm_adsp2_bus_error); + if (ret) + goto error_adsp; + + madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1, + &cs47l15->fll[0]); + madera_init_fll(madera, 4, MADERA_FLLAO_CONTROL_1 - 1, + &cs47l15->fll[1]); + + for (i = 0; i < ARRAY_SIZE(cs47l15_dai); i++) + madera_init_dai(&cs47l15->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(cs47l15_digital_vu); i++) + regmap_update_bits(madera->regmap, cs47l15_digital_vu[i], + CS47L15_DIG_VU, CS47L15_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_cs47l15, + cs47l15_dai, + ARRAY_SIZE(cs47l15_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto error_pm_runtime; + } + + return ret; + +error_pm_runtime: + pm_runtime_disable(&pdev->dev); + madera_free_bus_error_irq(&cs47l15->core, 0); +error_adsp: + wm_adsp2_remove(&cs47l15->core.adsp[0]); +error_dsp_irq: + madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0); + madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l15); +error_overheat: + madera_free_overheat(&cs47l15->core); +error_core: + madera_core_free(&cs47l15->core); + + return ret; +} + +static void cs47l15_remove(struct platform_device *pdev) +{ + struct cs47l15 *cs47l15 = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + madera_free_bus_error_irq(&cs47l15->core, 0); + + wm_adsp2_remove(&cs47l15->core.adsp[0]); + + madera_set_irq_wake(cs47l15->core.madera, MADERA_IRQ_DSP_IRQ1, 0); + madera_free_irq(cs47l15->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l15); + madera_free_overheat(&cs47l15->core); + madera_core_free(&cs47l15->core); +} + +static struct platform_driver cs47l15_codec_driver = { + .driver = { + .name = "cs47l15-codec", + }, + .probe = &cs47l15_probe, + .remove_new = cs47l15_remove, +}; + +module_platform_driver(cs47l15_codec_driver); + +MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp"); +MODULE_DESCRIPTION("ASoC CS47L15 driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_AUTHOR("Jaswinder Jassal "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cs47l15-codec"); diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c new file mode 100644 index 0000000000..cfa1d34f6e --- /dev/null +++ b/sound/soc/codecs/cs47l24.c @@ -0,0 +1,1355 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24 + * + * Copyright 2015 Cirrus Logic Inc. + * + * Author: Richard Fitzgerald + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "arizona.h" +#include "wm_adsp.h" +#include "cs47l24.h" + +#define DRV_NAME "cs47l24-codec" + +struct cs47l24_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static const struct cs_dsp_region cs47l24_dsp2_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x200000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x280000 }, + { .type = WMFW_ADSP2_XM, .base = 0x290000 }, + { .type = WMFW_ADSP2_YM, .base = 0x2a8000 }, +}; + +static const struct cs_dsp_region cs47l24_dsp3_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x300000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x380000 }, + { .type = WMFW_ADSP2_XM, .base = 0x390000 }, + { .type = WMFW_ADSP2_YM, .base = 0x3a8000 }, +}; + +static const struct cs_dsp_region *cs47l24_dsp_regions[] = { + cs47l24_dsp2_regions, + cs47l24_dsp3_regions, +}; + +static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + unsigned int v; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); + if (ret != 0) { + dev_err(component->dev, "Failed to read SYSCLK state: %d\n", ret); + return ret; + } + + v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; + + wm_adsp2_set_dspclk(w, v); + + return wm_adsp_early_event(w, kcontrol, event); +} + +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define CS47L24_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0) + +static const struct snd_kcontrol_new cs47l24_snd_controls[] = { +SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), + +SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), + +ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5, + ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2), +ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2), +ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2), +ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), +SOC_ENUM("ISRC3 FSL", arizona_isrc_fsl[2]), +SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]), +SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]), +SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]), +SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1), + +WM_ADSP2_PRELOAD_SWITCH("DSP2", 2), +WM_ADSP2_PRELOAD_SWITCH("DSP3", 3), + +ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP3R", ARIZONA_DSP3RMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv), + +ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL, + ARIZONA_HP1_SC_ENA_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +CS47L24_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +CS47L24_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +CS47L24_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP2", 1), +WM_ADSP_FW_CONTROL("DSP3", 2), +}; + +ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP2L, ARIZONA_DSP2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP2R, ARIZONA_DSP2RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP2, ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP3L, ARIZONA_DSP3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP3R, ARIZONA_DSP3RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP3, ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT3, ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT4, ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC3, ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC4, ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC3INT1, ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT2, ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT3, ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT4, ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC3DEC1, ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC2, ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC3, ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC4, ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE); + +static const char * const cs47l24_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "SPKOUT", +}; + +static const unsigned int cs47l24_aec_loopback_values[] = { + 0, 1, 6, +}; + +static const struct soc_enum cs47l24_aec_loopback = + SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l24_aec_loopback_texts), + cs47l24_aec_loopback_texts, + cs47l24_aec_loopback_values); + +static const struct snd_kcontrol_new cs47l24_aec_loopback_mux = + SOC_DAPM_ENUM("AEC Loopback", cs47l24_aec_loopback); + +static const struct snd_soc_dapm_widget cs47l24_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, + ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"), + +SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0, + &arizona_voice_trigger_switch[2]), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0), + +WM_ADSP2("DSP2", 1, cs47l24_adsp_power_ev), +WM_ADSP2("DSP3", 2, cs47l24_adsp_power_ev), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT3", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT4", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC3", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC4", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3INT1", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT2", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT3", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT4", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3DEC1", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC2", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC3", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC4", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, &cs47l24_aec_loopback_mux), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"), +ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"), + +ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"), +ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"), +ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), + +ARIZONA_DSP_WIDGETS(DSP2, "DSP2"), +ARIZONA_DSP_WIDGETS(DSP3, "DSP3"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), +ARIZONA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"), +ARIZONA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), +ARIZONA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"), +ARIZONA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"), + +ARIZONA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"), +ARIZONA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"), +ARIZONA_MUX_WIDGETS(ISRC3DEC3, "ISRC3DEC3"), +ARIZONA_MUX_WIDGETS(ISRC3DEC4, "ISRC3DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"), +ARIZONA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"), +ARIZONA_MUX_WIDGETS(ISRC3INT3, "ISRC3INT3"), +ARIZONA_MUX_WIDGETS(ISRC3INT4, "ISRC3INT4"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +SND_SOC_DAPM_OUTPUT("SPKOUTP"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2DEC3", "ISRC2DEC3" }, \ + { name, "ISRC2DEC4", "ISRC2DEC4" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC2INT3", "ISRC2INT3" }, \ + { name, "ISRC2INT4", "ISRC2INT4" }, \ + { name, "ISRC3DEC1", "ISRC3DEC1" }, \ + { name, "ISRC3DEC2", "ISRC3DEC2" }, \ + { name, "ISRC3DEC3", "ISRC3DEC3" }, \ + { name, "ISRC3DEC4", "ISRC3DEC4" }, \ + { name, "ISRC3INT1", "ISRC3INT1" }, \ + { name, "ISRC3INT2", "ISRC3INT2" }, \ + { name, "ISRC3INT3", "ISRC3INT3" }, \ + { name, "ISRC3INT4", "ISRC3INT4" }, \ + { name, "DSP2.1", "DSP2" }, \ + { name, "DSP2.2", "DSP2" }, \ + { name, "DSP2.3", "DSP2" }, \ + { name, "DSP2.4", "DSP2" }, \ + { name, "DSP2.5", "DSP2" }, \ + { name, "DSP2.6", "DSP2" }, \ + { name, "DSP3.1", "DSP3" }, \ + { name, "DSP3.2", "DSP3" }, \ + { name, "DSP3.3", "DSP3" }, \ + { name, "DSP3.4", "DSP3" }, \ + { name, "DSP3.5", "DSP3" }, \ + { name, "DSP3.6", "DSP3" } + +static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = { + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDD" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "SYSCLK" }, + { "ASRC1R", NULL, "SYSCLK" }, + { "ASRC2L", NULL, "SYSCLK" }, + { "ASRC2R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "ASYNCCLK" }, + { "ASRC1R", NULL, "ASYNCCLK" }, + { "ASRC2L", NULL, "ASYNCCLK" }, + { "ASRC2R", NULL, "ASYNCCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + + { "Voice Control DSP", NULL, "DSP3" }, + + { "IN1L PGA", NULL, "IN1L" }, + { "IN1R PGA", NULL, "IN1R" }, + + { "IN2L PGA", NULL, "IN2L" }, + { "IN2R PGA", NULL, "IN2R" }, + + { "Audio Trace DSP", NULL, "DSP2" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), + ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), + + ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"), + ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"), + ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + + ARIZONA_DSP_ROUTES("DSP2"), + ARIZONA_DSP_ROUTES("DSP3"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + ARIZONA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"), + ARIZONA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + ARIZONA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"), + ARIZONA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"), + + ARIZONA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"), + ARIZONA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"), + ARIZONA_MUX_ROUTES("ISRC3INT3", "ISRC3INT3"), + ARIZONA_MUX_ROUTES("ISRC3INT4", "ISRC3INT4"), + + ARIZONA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"), + ARIZONA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"), + ARIZONA_MUX_ROUTES("ISRC3DEC3", "ISRC3DEC3"), + ARIZONA_MUX_ROUTES("ISRC3DEC4", "ISRC3DEC4"), + + { "AEC Loopback", "HPOUT1L", "OUT1L" }, + { "AEC Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC Loopback", "SPKOUT", "OUT4L" }, + { "SPKOUTN", NULL, "OUT4L" }, + { "SPKOUTP", NULL, "OUT4L" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "SYSCLK" }, + { "DRC2 Signal Activity", NULL, "SYSCLK" }, + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, + { "DRC2 Signal Activity", NULL, "DRC2L" }, + { "DRC2 Signal Activity", NULL, "DRC2R" }, + + { "DSP Voice Trigger", NULL, "SYSCLK" }, + { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" }, + { "DSP3 Voice Trigger", "Switch", "DSP3" }, +}; + +static int cs47l24_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int Fref, unsigned int Fout) +{ + struct cs47l24_priv *cs47l24 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case CS47L24_FLL1: + return arizona_set_fll(&cs47l24->fll[0], source, Fref, Fout); + case CS47L24_FLL2: + return arizona_set_fll(&cs47l24->fll[1], source, Fref, Fout); + case CS47L24_FLL1_REFCLK: + return arizona_set_fll_refclk(&cs47l24->fll[0], source, Fref, + Fout); + case CS47L24_FLL2_REFCLK: + return arizona_set_fll_refclk(&cs47l24->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +#define CS47L24_RATES SNDRV_PCM_RATE_KNOT + +#define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops cs47l24_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + +static struct snd_soc_dai_driver cs47l24_dai[] = { + { + .name = "cs47l24-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l24-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l24-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l24-cpu-voicectrl", + .capture = { + .stream_name = "Voice Control CPU", + .channels_min = 1, + .channels_max = 1, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &cs47l24_dai_ops, + }, + { + .name = "cs47l24-dsp-voicectrl", + .capture = { + .stream_name = "Voice Control DSP", + .channels_min = 1, + .channels_max = 1, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + }, + { + .name = "cs47l24-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + .ops = &cs47l24_dai_ops, + }, + { + .name = "cs47l24-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 6, + .rates = CS47L24_RATES, + .formats = CS47L24_FORMATS, + }, + }, +}; + +static int cs47l24_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->core.arizona; + int n_adsp; + + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l24-dsp-voicectrl") == 0) { + n_adsp = 2; + } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l24-dsp-trace") == 0) { + n_adsp = 1; + } else { + dev_err(arizona->dev, + "No suitable compressed stream for DAI '%s'\n", + asoc_rtd_to_codec(rtd, 0)->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream); +} + +static irqreturn_t cs47l24_adsp2_irq(int irq, void *data) +{ + struct cs47l24_priv *priv = data; + struct arizona *arizona = priv->core.arizona; + struct arizona_voice_trigger_info info; + int serviced = 0; + int i, ret; + + for (i = 1; i <= 2; ++i) { + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]); + if (ret != -ENODEV) + serviced++; + if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) { + info.core = i; + arizona_call_notifiers(arizona, + ARIZONA_NOTIFY_VOICE_TRIGGER, + &info); + } + } + + if (!serviced) { + dev_err(arizona->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int cs47l24_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->core.arizona; + int ret; + + arizona->dapm = dapm; + snd_soc_component_init_regmap(component, arizona->regmap); + + ret = arizona_init_spk(component); + if (ret < 0) + return ret; + + arizona_init_gpio(component); + arizona_init_mono(component); + + ret = wm_adsp2_component_probe(&priv->core.adsp[1], component); + if (ret) + goto err_adsp2_codec_probe; + + ret = wm_adsp2_component_probe(&priv->core.adsp[2], component); + if (ret) + goto err_adsp2_codec_probe; + + ret = snd_soc_add_component_controls(component, + &arizona_adsp2_rate_controls[1], + 2); + if (ret) + goto err_adsp2_codec_probe; + + snd_soc_component_disable_pin(component, "HAPTICS"); + + return 0; + +err_adsp2_codec_probe: + wm_adsp2_component_remove(&priv->core.adsp[1], component); + wm_adsp2_component_remove(&priv->core.adsp[2], component); + + return ret; +} + +static void cs47l24_component_remove(struct snd_soc_component *component) +{ + struct cs47l24_priv *priv = snd_soc_component_get_drvdata(component); + + wm_adsp2_component_remove(&priv->core.adsp[1], component); + wm_adsp2_component_remove(&priv->core.adsp[2], component); + + priv->core.arizona->dapm = NULL; +} + +#define CS47L24_DIG_VU 0x0200 + +static unsigned int cs47l24_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_4L, +}; + +static const struct snd_compress_ops cs47l24_compress_ops = { + .open = cs47l24_open, + .free = wm_adsp_compr_free, + .set_params = wm_adsp_compr_set_params, + .get_caps = wm_adsp_compr_get_caps, + .trigger = wm_adsp_compr_trigger, + .pointer = wm_adsp_compr_pointer, + .copy = wm_adsp_compr_copy, +}; + +static const struct snd_soc_component_driver soc_component_dev_cs47l24 = { + .probe = cs47l24_component_probe, + .remove = cs47l24_component_remove, + .set_sysclk = arizona_set_sysclk, + .set_pll = cs47l24_set_fll, + .name = DRV_NAME, + .compress_ops = &cs47l24_compress_ops, + .controls = cs47l24_snd_controls, + .num_controls = ARRAY_SIZE(cs47l24_snd_controls), + .dapm_widgets = cs47l24_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs47l24_dapm_widgets), + .dapm_routes = cs47l24_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cs47l24_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct cs47l24_priv *cs47l24; + int i, ret; + + BUILD_BUG_ON(ARRAY_SIZE(cs47l24_dai) > ARIZONA_MAX_DAI); + + cs47l24 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l24_priv), + GFP_KERNEL); + if (!cs47l24) + return -ENOMEM; + + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + + platform_set_drvdata(pdev, cs47l24); + + cs47l24->core.arizona = arizona; + cs47l24->core.num_inputs = 4; + + for (i = 1; i <= 2; i++) { + cs47l24->core.adsp[i].part = "cs47l24"; + cs47l24->core.adsp[i].cs_dsp.num = i + 1; + cs47l24->core.adsp[i].cs_dsp.type = WMFW_ADSP2; + cs47l24->core.adsp[i].cs_dsp.dev = arizona->dev; + cs47l24->core.adsp[i].cs_dsp.regmap = arizona->regmap; + + cs47l24->core.adsp[i].cs_dsp.base = ARIZONA_DSP1_CONTROL_1 + + (0x100 * i); + cs47l24->core.adsp[i].cs_dsp.mem = cs47l24_dsp_regions[i - 1]; + cs47l24->core.adsp[i].cs_dsp.num_mems = + ARRAY_SIZE(cs47l24_dsp2_regions); + + ret = wm_adsp2_init(&cs47l24->core.adsp[i]); + if (ret != 0) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(cs47l24->fll); i++) + cs47l24->fll[i].vco_mult = 3; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &cs47l24->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &cs47l24->fll[1]); + + /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */ + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2, + ARIZONA_SAMPLE_RATE_2_MASK, 0x11); + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3, + ARIZONA_SAMPLE_RATE_3_MASK, 0x12); + + for (i = 0; i < ARRAY_SIZE(cs47l24_dai); i++) + arizona_init_dai(&cs47l24->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(cs47l24_digital_vu); i++) + regmap_update_bits(arizona->regmap, cs47l24_digital_vu[i], + CS47L24_DIG_VU, CS47L24_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", cs47l24_adsp2_irq, + cs47l24); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + return ret; + } + + ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1); + if (ret != 0) + dev_warn(&pdev->dev, + "Failed to set compressed IRQ as a wake source: %d\n", + ret); + + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; + ret = arizona_init_spk_irqs(arizona); + if (ret < 0) + goto err_dsp_irq; + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_cs47l24, + cs47l24_dai, + ARRAY_SIZE(cs47l24_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto err_spk_irqs; + } + + return ret; + +err_spk_irqs: + arizona_free_spk_irqs(arizona); +err_dsp_irq: + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24); + + return ret; +} + +static void cs47l24_remove(struct platform_device *pdev) +{ + struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev); + struct arizona *arizona = cs47l24->core.arizona; + + pm_runtime_disable(&pdev->dev); + + wm_adsp2_remove(&cs47l24->core.adsp[1]); + wm_adsp2_remove(&cs47l24->core.adsp[2]); + + arizona_free_spk_irqs(arizona); + + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24); +} + +static struct platform_driver cs47l24_codec_driver = { + .driver = { + .name = "cs47l24-codec", + }, + .probe = cs47l24_probe, + .remove_new = cs47l24_remove, +}; + +module_platform_driver(cs47l24_codec_driver); + +MODULE_DESCRIPTION("ASoC CS47L24 driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cs47l24-codec"); diff --git a/sound/soc/codecs/cs47l24.h b/sound/soc/codecs/cs47l24.h new file mode 100644 index 0000000000..9fd4b41f1f --- /dev/null +++ b/sound/soc/codecs/cs47l24.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * cs47l24.h -- ALSA SoC Audio driver for Cirrus Logic CS47L24 + * + * Copyright 2015 Cirrus Logic Inc. + * + * Author: Richard Fitzgerald + */ + +#ifndef _CS47L24_H +#define _CS47L24_H + +#include "arizona.h" + +#define CS47L24_FLL1 1 +#define CS47L24_FLL2 2 +#define CS47L24_FLL1_REFCLK 3 +#define CS47L24_FLL2_REFCLK 4 + +#endif diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c new file mode 100644 index 0000000000..a953f2ede1 --- /dev/null +++ b/sound/soc/codecs/cs47l35.c @@ -0,0 +1,1781 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// ALSA SoC Audio driver for CS47L35 codec +// +// Copyright (C) 2015-2019 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "madera.h" +#include "wm_adsp.h" + +#define CS47L35_NUM_ADSP 3 +#define CS47L35_MONO_OUTPUTS 1 + +#define DRV_NAME "cs47l35-codec" + +struct cs47l35 { + struct madera_priv core; + struct madera_fll fll; +}; + +static const struct cs_dsp_region cs47l35_dsp1_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x080000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x0a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x0c0000 }, +}; + +static const struct cs_dsp_region cs47l35_dsp2_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x100000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x160000 }, + { .type = WMFW_ADSP2_XM, .base = 0x120000 }, + { .type = WMFW_ADSP2_YM, .base = 0x140000 }, +}; + +static const struct cs_dsp_region cs47l35_dsp3_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x180000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x1a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x1c0000 }, +}; + +static const struct cs_dsp_region *cs47l35_dsp_regions[] = { + cs47l35_dsp1_regions, + cs47l35_dsp2_regions, + cs47l35_dsp3_regions, +}; + +static const int wm_adsp2_control_bases[] = { + MADERA_DSP1_CONFIG_1, + MADERA_DSP2_CONFIG_1, + MADERA_DSP3_CONFIG_1, +}; + +static const char * const cs47l35_outdemux_texts[] = { + "HPOUT", + "EPOUT", +}; + +static SOC_ENUM_SINGLE_DECL(cs47l35_outdemux_enum, SND_SOC_NOPM, 0, + cs47l35_outdemux_texts); + +static const struct snd_kcontrol_new cs47l35_outdemux = + SOC_DAPM_ENUM_EXT("HPOUT1 Demux", cs47l35_outdemux_enum, + madera_out1_demux_get, madera_out1_demux_put); + +static int cs47l35_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l35->core; + struct madera *madera = priv->madera; + unsigned int freq; + int ret; + + ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_1, &freq); + if (ret != 0) { + dev_err(madera->dev, + "Failed to read MADERA_DSP_CLOCK_1: %d\n", ret); + return ret; + } + + freq &= MADERA_DSP_CLK_FREQ_LEGACY_MASK; + freq >>= MADERA_DSP_CLK_FREQ_LEGACY_SHIFT; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = madera_set_adsp_clk(&cs47l35->core, w->shift, freq); + if (ret) + return ret; + break; + default: + break; + } + + return wm_adsp_early_event(w, kcontrol, event); +} + +#define CS47L35_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + +static void cs47l35_hp_post_enable(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + unsigned int val; + + switch (w->shift) { + case MADERA_OUT1L_ENA_SHIFT: + case MADERA_OUT1R_ENA_SHIFT: + val = snd_soc_component_read(component, MADERA_OUTPUT_ENABLES_1); + val &= (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA); + + if (val != (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA)) + break; + + snd_soc_component_update_bits(component, + MADERA_EDRE_HP_STEREO_CONTROL, + 0x0001, 1); + break; + default: + break; + } +} + +static void cs47l35_hp_post_disable(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case MADERA_OUT1L_ENA_SHIFT: + snd_soc_component_write(component, MADERA_DCS_HP1L_CONTROL, + 0x2006); + break; + case MADERA_OUT1R_ENA_SHIFT: + snd_soc_component_write(component, MADERA_DCS_HP1R_CONTROL, + 0x2006); + break; + default: + return; + } + + /* Only get to here for OUT1L and OUT1R */ + snd_soc_component_update_bits(component, + MADERA_EDRE_HP_STEREO_CONTROL, + 0x0001, 0); +} + +static int cs47l35_hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_PRE_PMD: + return madera_hp_ev(w, kcontrol, event); + case SND_SOC_DAPM_POST_PMU: + ret = madera_hp_ev(w, kcontrol, event); + if (ret < 0) + return ret; + + cs47l35_hp_post_enable(w); + return 0; + case SND_SOC_DAPM_POST_PMD: + ret = madera_hp_ev(w, kcontrol, event); + cs47l35_hp_post_disable(w); + return ret; + default: + return -EINVAL; + } +} + +static const struct snd_kcontrol_new cs47l35_snd_controls[] = { +SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL, + MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL, + MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL, + MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL, + MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL, + MADERA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL, + MADERA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL, + MADERA_IN2L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL, + MADERA_IN2R_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L, + MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R, + MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L, + MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R, + MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), + +SOC_ENUM("Input Ramp Up", madera_in_vi_ramp), +SOC_ENUM("Input Ramp Down", madera_in_vd_ramp), + +MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE), + +MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2), +SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2), +SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5, + MADERA_DRC1R_ENA | MADERA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5, + MADERA_DRC2R_ENA | MADERA_DRC2L_ENA), + +MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE), + +MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2), +MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2), +MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2), +MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2), + +SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode), + +MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]), +MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]), +MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]), +MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]), + +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), +WM_ADSP2_PRELOAD_SWITCH("DSP2", 2), +WM_ADSP2_PRELOAD_SWITCH("DSP3", 3), + +MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP2L", MADERA_DSP2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP2R", MADERA_DSP2RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP3L", MADERA_DSP3LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP3R", MADERA_DSP3RMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR, + MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv), + +MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKOUT", MADERA_OUT4LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL, + MADERA_HP1_SC_ENA_SHIFT, 1, 0), + +SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L, + MADERA_OUT5_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Speaker Digital Switch", MADERA_DAC_DIGITAL_VOLUME_4L, + MADERA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("Speaker Digital Volume", MADERA_DAC_DIGITAL_VOLUME_4L, + MADERA_OUT4L_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), + +SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT, + MADERA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_ENUM("Output Ramp Up", madera_out_vi_ramp), +SOC_ENUM("Output Ramp Down", madera_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv), +SOC_ENUM("Noise Gate Hold", madera_ng_hold), + +CS47L35_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L), +CS47L35_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R), +CS47L35_NG_SRC("SPKOUT", MADERA_NOISE_GATE_SELECT_4L), +CS47L35_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L), +CS47L35_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R), + +MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE), + +MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE), +MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), +WM_ADSP_FW_CONTROL("DSP2", 1), +WM_ADSP_FW_CONTROL("DSP3", 2), +}; + +MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP2L, MADERA_DSP2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP2R, MADERA_DSP2RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP2, MADERA_DSP2AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP3L, MADERA_DSP3LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP3R, MADERA_DSP3RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP3, MADERA_DSP3AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKOUT, MADERA_OUT4LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE); + +static const char * const cs47l35_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "SPKOUT", "SPKDAT1L", "SPKDAT1R", +}; + +static const unsigned int cs47l35_aec_loopback_values[] = { + 0, 1, 6, 8, 9, +}; + +static const struct soc_enum cs47l35_aec1_loopback = + SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1, + MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l35_aec_loopback_texts), + cs47l35_aec_loopback_texts, + cs47l35_aec_loopback_values); + +static const struct soc_enum cs47l35_aec2_loopback = + SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2, + MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l35_aec_loopback_texts), + cs47l35_aec_loopback_texts, + cs47l35_aec_loopback_values); + +static const struct snd_kcontrol_new cs47l35_aec_loopback_mux[] = { + SOC_DAPM_ENUM("AEC1 Loopback", cs47l35_aec1_loopback), + SOC_DAPM_ENUM("AEC2 Loopback", cs47l35_aec2_loopback), +}; + +static const struct snd_soc_dapm_widget cs47l35_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT, + 0, madera_sysclk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK, + MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT, + 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2A", MADERA_MIC_BIAS_CTRL_6, + MADERA_MICB2A_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2B", MADERA_MIC_BIAS_CTRL_6, + MADERA_MICB2B_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_FX, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_OUT, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_SPD, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP3CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP3, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF3, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_SLIMBUS, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_PWM, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), + +SND_SOC_DAPM_INPUT("IN1ALN"), +SND_SOC_DAPM_INPUT("IN1ALP"), +SND_SOC_DAPM_INPUT("IN1BLN"), +SND_SOC_DAPM_INPUT("IN1BLP"), +SND_SOC_DAPM_INPUT("IN1ARN"), +SND_SOC_DAPM_INPUT("IN1ARP"), +SND_SOC_DAPM_INPUT("IN1BRN"), +SND_SOC_DAPM_INPUT("IN1BRP"), +SND_SOC_DAPM_INPUT("IN2LN"), +SND_SOC_DAPM_INPUT("IN2LP"), +SND_SOC_DAPM_INPUT("IN2RN"), +SND_SOC_DAPM_INPUT("IN2RP"), + +SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]), +SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]), + +SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]), +SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]), + +SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), +SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_OUTPUT("DSP Trigger Out"), + +SND_SOC_DAPM_DEMUX("HPOUT1 Demux", SND_SOC_NOPM, 0, 0, &cs47l35_outdemux), +SND_SOC_DAPM_MUX("HPOUT1 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l35_outdemux), + +SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, cs47l35_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, cs47l35_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM, + MADERA_OUT4L_ENA_SHIFT, 0, NULL, 0, madera_spk_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_ENA_SHIFT, 0, NULL, 0), + +/* + * Input mux widgets arranged in order of sources in MADERA_MIXER_INPUT_ROUTES + * to take advantage of cache lookup in DAPM + */ +SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR, + MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1, + MADERA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1, + MADERA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1, + MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0, + &cs47l35_aec_loopback_mux[0]), + +SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2, + MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0, + &cs47l35_aec_loopback_mux[1]), + +SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0), + +WM_ADSP2("DSP1", 0, cs47l35_adsp_power_ev), +WM_ADSP2("DSP2", 1, cs47l35_adsp_power_ev), +WM_ADSP2("DSP3", 2, cs47l35_adsp_power_ev), + +/* End of ordered input mux widgets */ + +MADERA_MIXER_WIDGETS(EQ1, "EQ1"), +MADERA_MIXER_WIDGETS(EQ2, "EQ2"), +MADERA_MIXER_WIDGETS(EQ3, "EQ3"), +MADERA_MIXER_WIDGETS(EQ4, "EQ4"), + +MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"), +MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"), +MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"), +MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0, + &madera_drc_activity_output_mux[0]), +SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0, + &madera_drc_activity_output_mux[1]), + +MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"), +MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"), +MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"), +MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +MADERA_MIXER_WIDGETS(PWM1, "PWM1"), +MADERA_MIXER_WIDGETS(PWM2, "PWM2"), + +MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +MADERA_MIXER_WIDGETS(SPKOUT, "SPKOUT"), +MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), + +MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), + +MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), + +MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), + +MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"), +MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"), + +MADERA_DSP_WIDGETS(DSP1, "DSP1"), +MADERA_DSP_WIDGETS(DSP2, "DSP2"), +MADERA_DSP_WIDGETS(DSP3, "DSP3"), + +SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[0]), +SND_SOC_DAPM_SWITCH("DSP2 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[1]), +SND_SOC_DAPM_SWITCH("DSP3 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[2]), + +MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), +MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"), +MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"), + +MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), +MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"), +MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("EPOUTP"), +SND_SOC_DAPM_OUTPUT("EPOUTN"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +SND_SOC_DAPM_OUTPUT("SPKOUTP"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), +SND_SOC_DAPM_OUTPUT("SPDIF1"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define MADERA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC1", "AEC1 Loopback" }, \ + { name, "AEC2", "AEC2 Loopback" }, \ + { name, "IN1L", "IN1L" }, \ + { name, "IN1R", "IN1R" }, \ + { name, "IN2L", "IN2L" }, \ + { name, "IN2R", "IN2R" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2DEC3", "ISRC2DEC3" }, \ + { name, "ISRC2DEC4", "ISRC2DEC4" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC2INT3", "ISRC2INT3" }, \ + { name, "ISRC2INT4", "ISRC2INT4" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" }, \ + { name, "DSP2.1", "DSP2" }, \ + { name, "DSP2.2", "DSP2" }, \ + { name, "DSP2.3", "DSP2" }, \ + { name, "DSP2.4", "DSP2" }, \ + { name, "DSP2.5", "DSP2" }, \ + { name, "DSP2.6", "DSP2" }, \ + { name, "DSP3.1", "DSP3" }, \ + { name, "DSP3.2", "DSP3" }, \ + { name, "DSP3.3", "DSP3" }, \ + { name, "DSP3.4", "DSP3" }, \ + { name, "DSP3.5", "DSP3" }, \ + { name, "DSP3.6", "DSP3" } + +static const struct snd_soc_dapm_route cs47l35_dapm_routes[] = { + /* Internal clock domains */ + { "EQ1", NULL, "FXCLK" }, + { "EQ2", NULL, "FXCLK" }, + { "EQ3", NULL, "FXCLK" }, + { "EQ4", NULL, "FXCLK" }, + { "DRC1L", NULL, "FXCLK" }, + { "DRC1R", NULL, "FXCLK" }, + { "DRC2L", NULL, "FXCLK" }, + { "DRC2R", NULL, "FXCLK" }, + { "LHPF1", NULL, "FXCLK" }, + { "LHPF2", NULL, "FXCLK" }, + { "LHPF3", NULL, "FXCLK" }, + { "LHPF4", NULL, "FXCLK" }, + { "PWM1 Mixer", NULL, "PWMCLK" }, + { "PWM2 Mixer", NULL, "PWMCLK" }, + { "OUT1L", NULL, "OUTCLK" }, + { "OUT1R", NULL, "OUTCLK" }, + { "OUT4L", NULL, "OUTCLK" }, + { "OUT5L", NULL, "OUTCLK" }, + { "OUT5R", NULL, "OUTCLK" }, + { "AIF1TX1", NULL, "AIF1TXCLK" }, + { "AIF1TX2", NULL, "AIF1TXCLK" }, + { "AIF1TX3", NULL, "AIF1TXCLK" }, + { "AIF1TX4", NULL, "AIF1TXCLK" }, + { "AIF1TX5", NULL, "AIF1TXCLK" }, + { "AIF1TX6", NULL, "AIF1TXCLK" }, + { "AIF2TX1", NULL, "AIF2TXCLK" }, + { "AIF2TX2", NULL, "AIF2TXCLK" }, + { "AIF3TX1", NULL, "AIF3TXCLK" }, + { "AIF3TX2", NULL, "AIF3TXCLK" }, + { "SLIMTX1", NULL, "SLIMBUSCLK" }, + { "SLIMTX2", NULL, "SLIMBUSCLK" }, + { "SLIMTX3", NULL, "SLIMBUSCLK" }, + { "SLIMTX4", NULL, "SLIMBUSCLK" }, + { "SLIMTX5", NULL, "SLIMBUSCLK" }, + { "SLIMTX6", NULL, "SLIMBUSCLK" }, + { "SPD1TX1", NULL, "SPDCLK" }, + { "SPD1TX2", NULL, "SPDCLK" }, + { "DSP1", NULL, "DSP1CLK" }, + { "DSP2", NULL, "DSP2CLK" }, + { "DSP3", NULL, "DSP3CLK" }, + { "ISRC1DEC1", NULL, "ISRC1CLK" }, + { "ISRC1DEC2", NULL, "ISRC1CLK" }, + { "ISRC1DEC3", NULL, "ISRC1CLK" }, + { "ISRC1DEC4", NULL, "ISRC1CLK" }, + { "ISRC1INT1", NULL, "ISRC1CLK" }, + { "ISRC1INT2", NULL, "ISRC1CLK" }, + { "ISRC1INT3", NULL, "ISRC1CLK" }, + { "ISRC1INT4", NULL, "ISRC1CLK" }, + { "ISRC2DEC1", NULL, "ISRC2CLK" }, + { "ISRC2DEC2", NULL, "ISRC2CLK" }, + { "ISRC2DEC3", NULL, "ISRC2CLK" }, + { "ISRC2DEC4", NULL, "ISRC2CLK" }, + { "ISRC2INT1", NULL, "ISRC2CLK" }, + { "ISRC2INT2", NULL, "ISRC2CLK" }, + { "ISRC2INT3", NULL, "ISRC2CLK" }, + { "ISRC2INT4", NULL, "ISRC2CLK" }, + + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD2" }, + { "AIF3 Playback", NULL, "DBVDD2" }, + + { "OUT1L", NULL, "CPVDD1" }, + { "OUT1R", NULL, "CPVDD1" }, + { "OUT1L", NULL, "CPVDD2" }, + { "OUT1R", NULL, "CPVDD2" }, + + { "OUT4L", NULL, "SPKVDD" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + + { "SPD1", NULL, "SYSCLK" }, + { "SPD1", NULL, "SPD1TX1" }, + { "SPD1", NULL, "SPD1TX2" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + + { "MICBIAS1A", NULL, "MICBIAS1" }, + { "MICBIAS1B", NULL, "MICBIAS1" }, + { "MICBIAS2A", NULL, "MICBIAS2" }, + { "MICBIAS2B", NULL, "MICBIAS2" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + + { "Voice Control DSP", NULL, "DSP3" }, + + { "Audio Trace DSP", NULL, "DSP1" }, + + { "IN1L Analog Mux", "A", "IN1ALN" }, + { "IN1L Analog Mux", "A", "IN1ALP" }, + { "IN1L Analog Mux", "B", "IN1BLN" }, + { "IN1L Analog Mux", "B", "IN1BLP" }, + + { "IN1R Analog Mux", "A", "IN1ARN" }, + { "IN1R Analog Mux", "A", "IN1ARP" }, + { "IN1R Analog Mux", "B", "IN1BRN" }, + { "IN1R Analog Mux", "B", "IN1BRP" }, + + { "IN1L Mode", "Analog", "IN1L Analog Mux" }, + { "IN1R Mode", "Analog", "IN1R Analog Mux" }, + + { "IN1L Mode", "Digital", "IN1ALN" }, + { "IN1L Mode", "Digital", "IN1ARN" }, + { "IN1R Mode", "Digital", "IN1ALN" }, + { "IN1R Mode", "Digital", "IN1ARN" }, + + { "IN1L", NULL, "IN1L Mode" }, + { "IN1R", NULL, "IN1R Mode" }, + + { "IN2L Mode", "Analog", "IN2LN" }, + { "IN2L Mode", "Analog", "IN2LP" }, + { "IN2R Mode", "Analog", "IN2RN" }, + { "IN2R Mode", "Analog", "IN2RP" }, + + { "IN2L Mode", "Digital", "IN2LN" }, + { "IN2L Mode", "Digital", "IN2RN" }, + { "IN2R Mode", "Digital", "IN2LN" }, + { "IN2R Mode", "Digital", "IN2RN" }, + + { "IN2L", NULL, "IN2L Mode" }, + { "IN2R", NULL, "IN2R Mode" }, + + MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + + MADERA_MIXER_ROUTES("OUT4L", "SPKOUT"), + + MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + + MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + + MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + + MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + + MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"), + MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"), + + MADERA_MIXER_ROUTES("EQ1", "EQ1"), + MADERA_MIXER_ROUTES("EQ2", "EQ2"), + MADERA_MIXER_ROUTES("EQ3", "EQ3"), + MADERA_MIXER_ROUTES("EQ4", "EQ4"), + + MADERA_MIXER_ROUTES("DRC1L", "DRC1L"), + MADERA_MIXER_ROUTES("DRC1R", "DRC1R"), + MADERA_MIXER_ROUTES("DRC2L", "DRC2L"), + MADERA_MIXER_ROUTES("DRC2R", "DRC2R"), + + MADERA_MIXER_ROUTES("LHPF1", "LHPF1"), + MADERA_MIXER_ROUTES("LHPF2", "LHPF2"), + MADERA_MIXER_ROUTES("LHPF3", "LHPF3"), + MADERA_MIXER_ROUTES("LHPF4", "LHPF4"), + + MADERA_DSP_ROUTES("DSP1"), + MADERA_DSP_ROUTES("DSP2"), + MADERA_DSP_ROUTES("DSP3"), + + { "DSP Trigger Out", NULL, "DSP1 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP2 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP3 Trigger Output" }, + + { "DSP1 Trigger Output", "Switch", "DSP1" }, + { "DSP2 Trigger Output", "Switch", "DSP2" }, + { "DSP3 Trigger Output", "Switch", "DSP3" }, + + MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"), + MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"), + + MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"), + MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"), + + { "AEC1 Loopback", "HPOUT1L", "OUT1L" }, + { "AEC1 Loopback", "HPOUT1R", "OUT1R" }, + { "AEC2 Loopback", "HPOUT1L", "OUT1L" }, + { "AEC2 Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1 Demux", NULL, "OUT1L" }, + { "HPOUT1 Demux", NULL, "OUT1R" }, + + { "AEC1 Loopback", "SPKOUT", "OUT4L" }, + { "AEC2 Loopback", "SPKOUT", "OUT4L" }, + { "SPKOUTN", NULL, "OUT4L" }, + { "SPKOUTP", NULL, "OUT4L" }, + + { "OUT1R", NULL, "HPOUT1 Mono Mux" }, + { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" }, + + { "HPOUTL", "HPOUT", "HPOUT1 Demux" }, + { "HPOUTR", "HPOUT", "HPOUT1 Demux" }, + { "EPOUTP", "EPOUT", "HPOUT1 Demux" }, + { "EPOUTN", "EPOUT", "HPOUT1 Demux" }, + + { "AEC1 Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC1 Loopback", "SPKDAT1R", "OUT5R" }, + { "AEC2 Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC2 Loopback", "SPKDAT1R", "OUT5R" }, + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + { "SPDIF1", NULL, "SPD1" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" }, + { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" }, + { "DRC1 Activity Output", "Switch", "DRC1L" }, + { "DRC1 Activity Output", "Switch", "DRC1R" }, + { "DRC2 Activity Output", "Switch", "DRC2L" }, + { "DRC2 Activity Output", "Switch", "DRC2R" }, +}; + +static int cs47l35_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case MADERA_FLL1_REFCLK: + return madera_set_fll_refclk(&cs47l35->fll, source, fref, + fout); + case MADERA_FLL1_SYNCCLK: + return madera_set_fll_syncclk(&cs47l35->fll, source, fref, + fout); + default: + return -EINVAL; + } +} + +static const struct snd_soc_dai_ops cs47l35_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + +static struct snd_soc_dai_driver cs47l35_dai[] = { + { + .name = "cs47l35-aif1", + .id = 1, + .base = MADERA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l35-aif2", + .id = 2, + .base = MADERA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l35-aif3", + .id = 3, + .base = MADERA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l35-slim1", + .id = 4, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l35-slim2", + .id = 5, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l35-cpu-voicectrl", + .capture = { + .stream_name = "Voice Control CPU", + .channels_min = 1, + .channels_max = 1, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &cs47l35_dai_ops, + }, + { + .name = "cs47l35-dsp-voicectrl", + .capture = { + .stream_name = "Voice Control DSP", + .channels_min = 1, + .channels_max = 1, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + }, + { + .name = "cs47l35-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &cs47l35_dai_ops, + }, + { + .name = "cs47l35-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + }, +}; + +static int cs47l35_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l35->core; + struct madera *madera = priv->madera; + int n_adsp; + + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l35-dsp-voicectrl") == 0) { + n_adsp = 2; + } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l35-dsp-trace") == 0) { + n_adsp = 0; + } else { + dev_err(madera->dev, + "No suitable compressed stream for DAI '%s'\n", + asoc_rtd_to_codec(rtd, 0)->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&priv->adsp[n_adsp], stream); +} + +static irqreturn_t cs47l35_adsp2_irq(int irq, void *data) +{ + struct cs47l35 *cs47l35 = data; + struct madera_priv *priv = &cs47l35->core; + struct madera *madera = priv->madera; + struct madera_voice_trigger_info trig_info; + int serviced = 0; + int i, ret; + + for (i = 0; i < CS47L35_NUM_ADSP; ++i) { + ret = wm_adsp_compr_handle_irq(&priv->adsp[i]); + if (ret != -ENODEV) + serviced++; + if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) { + trig_info.core_num = i + 1; + blocking_notifier_call_chain(&madera->notifier, + MADERA_NOTIFY_VOICE_TRIGGER, + &trig_info); + } + } + + if (!serviced) { + dev_err(madera->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static const struct snd_soc_dapm_route cs47l35_mono_routes[] = { + { "HPOUT1 Mono Mux", "HPOUT", "OUT1L" }, +}; + +static int cs47l35_component_probe(struct snd_soc_component *component) +{ + struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l35->core.madera; + int i, ret; + + snd_soc_component_init_regmap(component, madera->regmap); + + mutex_lock(&madera->dapm_ptr_lock); + madera->dapm = snd_soc_component_get_dapm(component); + mutex_unlock(&madera->dapm_ptr_lock); + + ret = madera_init_inputs(component); + if (ret) + return ret; + + ret = madera_init_outputs(component, cs47l35_mono_routes, + ARRAY_SIZE(cs47l35_mono_routes), + CS47L35_MONO_OUTPUTS); + if (ret) + return ret; + + snd_soc_component_disable_pin(component, "HAPTICS"); + + ret = snd_soc_add_component_controls(component, + madera_adsp_rate_controls, + CS47L35_NUM_ADSP); + if (ret) + return ret; + + for (i = 0; i < CS47L35_NUM_ADSP; i++) + wm_adsp2_component_probe(&cs47l35->core.adsp[i], component); + + return 0; +} + +static void cs47l35_component_remove(struct snd_soc_component *component) +{ + struct cs47l35 *cs47l35 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l35->core.madera; + int i; + + mutex_lock(&madera->dapm_ptr_lock); + madera->dapm = NULL; + mutex_unlock(&madera->dapm_ptr_lock); + + for (i = 0; i < CS47L35_NUM_ADSP; i++) + wm_adsp2_component_remove(&cs47l35->core.adsp[i], component); +} + +#define CS47L35_DIG_VU 0x0200 + +static unsigned int cs47l35_digital_vu[] = { + MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, + MADERA_DAC_DIGITAL_VOLUME_4L, + MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, +}; + +static const struct snd_compress_ops cs47l35_compress_ops = { + .open = &cs47l35_open, + .free = &wm_adsp_compr_free, + .set_params = &wm_adsp_compr_set_params, + .get_caps = &wm_adsp_compr_get_caps, + .trigger = &wm_adsp_compr_trigger, + .pointer = &wm_adsp_compr_pointer, + .copy = &wm_adsp_compr_copy, +}; + +static const struct snd_soc_component_driver soc_component_dev_cs47l35 = { + .probe = &cs47l35_component_probe, + .remove = &cs47l35_component_remove, + .set_sysclk = &madera_set_sysclk, + .set_pll = &cs47l35_set_fll, + .name = DRV_NAME, + .compress_ops = &cs47l35_compress_ops, + .controls = cs47l35_snd_controls, + .num_controls = ARRAY_SIZE(cs47l35_snd_controls), + .dapm_widgets = cs47l35_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs47l35_dapm_widgets), + .dapm_routes = cs47l35_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs47l35_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cs47l35_probe(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + struct cs47l35 *cs47l35; + int i, ret; + + BUILD_BUG_ON(ARRAY_SIZE(cs47l35_dai) > MADERA_MAX_DAI); + + /* quick exit if Madera irqchip driver hasn't completed probe */ + if (!madera->irq_dev) { + dev_dbg(&pdev->dev, "irqchip driver not ready\n"); + return -EPROBE_DEFER; + } + + cs47l35 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l35), GFP_KERNEL); + if (!cs47l35) + return -ENOMEM; + platform_set_drvdata(pdev, cs47l35); + + cs47l35->core.madera = madera; + cs47l35->core.dev = &pdev->dev; + cs47l35->core.num_inputs = 4; + + ret = madera_core_init(&cs47l35->core); + if (ret) + return ret; + + ret = madera_init_overheat(&cs47l35->core); + if (ret) + goto error_core; + + ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", cs47l35_adsp2_irq, + cs47l35); + if (ret) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + goto error_overheat; + } + + ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1); + if (ret) + dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret); + + for (i = 0; i < CS47L35_NUM_ADSP; i++) { + cs47l35->core.adsp[i].part = "cs47l35"; + cs47l35->core.adsp[i].cs_dsp.num = i + 1; + cs47l35->core.adsp[i].cs_dsp.type = WMFW_ADSP2; + cs47l35->core.adsp[i].cs_dsp.rev = 1; + cs47l35->core.adsp[i].cs_dsp.dev = madera->dev; + cs47l35->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit; + + cs47l35->core.adsp[i].cs_dsp.base = wm_adsp2_control_bases[i]; + cs47l35->core.adsp[i].cs_dsp.mem = cs47l35_dsp_regions[i]; + cs47l35->core.adsp[i].cs_dsp.num_mems = + ARRAY_SIZE(cs47l35_dsp1_regions); + + ret = wm_adsp2_init(&cs47l35->core.adsp[i]); + if (ret) { + for (--i; i >= 0; --i) + wm_adsp2_remove(&cs47l35->core.adsp[i]); + goto error_dsp_irq; + } + } + + madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1, &cs47l35->fll); + + for (i = 0; i < ARRAY_SIZE(cs47l35_dai); i++) + madera_init_dai(&cs47l35->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(cs47l35_digital_vu); i++) + regmap_update_bits(madera->regmap, cs47l35_digital_vu[i], + CS47L35_DIG_VU, CS47L35_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_cs47l35, + cs47l35_dai, + ARRAY_SIZE(cs47l35_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto error_pm_runtime; + } + + return ret; + +error_pm_runtime: + pm_runtime_disable(&pdev->dev); + + for (i = 0; i < CS47L35_NUM_ADSP; i++) + wm_adsp2_remove(&cs47l35->core.adsp[i]); +error_dsp_irq: + madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0); + madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l35); +error_overheat: + madera_free_overheat(&cs47l35->core); +error_core: + madera_core_free(&cs47l35->core); + + return ret; +} + +static void cs47l35_remove(struct platform_device *pdev) +{ + struct cs47l35 *cs47l35 = platform_get_drvdata(pdev); + int i; + + pm_runtime_disable(&pdev->dev); + + for (i = 0; i < CS47L35_NUM_ADSP; i++) + wm_adsp2_remove(&cs47l35->core.adsp[i]); + + madera_set_irq_wake(cs47l35->core.madera, MADERA_IRQ_DSP_IRQ1, 0); + madera_free_irq(cs47l35->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l35); + madera_free_overheat(&cs47l35->core); + madera_core_free(&cs47l35->core); +} + +static struct platform_driver cs47l35_codec_driver = { + .driver = { + .name = "cs47l35-codec", + }, + .probe = &cs47l35_probe, + .remove_new = cs47l35_remove, +}; + +module_platform_driver(cs47l35_codec_driver); + +MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp"); +MODULE_DESCRIPTION("ASoC CS47L35 driver"); +MODULE_AUTHOR("Piotr Stankiewicz "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cs47l35-codec"); diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c new file mode 100644 index 0000000000..8276854818 --- /dev/null +++ b/sound/soc/codecs/cs47l85.c @@ -0,0 +1,2732 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// ALSA SoC Audio driver for CS47L85 codec +// +// Copyright (C) 2015-2019 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "madera.h" +#include "wm_adsp.h" + +#define DRV_NAME "cs47l85-codec" + +#define CS47L85_NUM_ADSP 7 +#define CS47L85_MONO_OUTPUTS 4 + +struct cs47l85 { + struct madera_priv core; + struct madera_fll fll[3]; +}; + +static const struct cs_dsp_region cs47l85_dsp1_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x080000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x0a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x0c0000 }, +}; + +static const struct cs_dsp_region cs47l85_dsp2_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x100000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x160000 }, + { .type = WMFW_ADSP2_XM, .base = 0x120000 }, + { .type = WMFW_ADSP2_YM, .base = 0x140000 }, +}; + +static const struct cs_dsp_region cs47l85_dsp3_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x180000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x1a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x1c0000 }, +}; + +static const struct cs_dsp_region cs47l85_dsp4_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x200000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x260000 }, + { .type = WMFW_ADSP2_XM, .base = 0x220000 }, + { .type = WMFW_ADSP2_YM, .base = 0x240000 }, +}; + +static const struct cs_dsp_region cs47l85_dsp5_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x280000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x2e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x2a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x2c0000 }, +}; + +static const struct cs_dsp_region cs47l85_dsp6_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x300000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x360000 }, + { .type = WMFW_ADSP2_XM, .base = 0x320000 }, + { .type = WMFW_ADSP2_YM, .base = 0x340000 }, +}; + +static const struct cs_dsp_region cs47l85_dsp7_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x380000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x3e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x3a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x3c0000 }, +}; + +static const struct cs_dsp_region *cs47l85_dsp_regions[] = { + cs47l85_dsp1_regions, + cs47l85_dsp2_regions, + cs47l85_dsp3_regions, + cs47l85_dsp4_regions, + cs47l85_dsp5_regions, + cs47l85_dsp6_regions, + cs47l85_dsp7_regions, +}; + +static const unsigned int wm_adsp2_control_bases[] = { + MADERA_DSP1_CONFIG_1, + MADERA_DSP2_CONFIG_1, + MADERA_DSP3_CONFIG_1, + MADERA_DSP4_CONFIG_1, + MADERA_DSP5_CONFIG_1, + MADERA_DSP6_CONFIG_1, + MADERA_DSP7_CONFIG_1, +}; + +static int cs47l85_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l85->core; + struct madera *madera = priv->madera; + unsigned int freq; + int ret; + + ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_1, &freq); + if (ret != 0) { + dev_err(madera->dev, + "Failed to read MADERA_DSP_CLOCK_1: %d\n", ret); + return ret; + } + + freq &= MADERA_DSP_CLK_FREQ_LEGACY_MASK; + freq >>= MADERA_DSP_CLK_FREQ_LEGACY_SHIFT; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = madera_set_adsp_clk(&cs47l85->core, w->shift, freq); + if (ret) + return ret; + break; + default: + break; + } + + return wm_adsp_early_event(w, kcontrol, event); +} + +#define CS47L85_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0) + +#define CS47L85_RXANC_INPUT_ROUTES(widget, name) \ + { widget, NULL, name " NG Mux" }, \ + { name " NG Internal", NULL, "RXANC NG Clock" }, \ + { name " NG Internal", NULL, name " Channel" }, \ + { name " NG External", NULL, "RXANC NG External Clock" }, \ + { name " NG External", NULL, name " Channel" }, \ + { name " NG Mux", "None", name " Channel" }, \ + { name " NG Mux", "Internal", name " NG Internal" }, \ + { name " NG Mux", "External", name " NG External" }, \ + { name " Channel", "Left", name " Left Input" }, \ + { name " Channel", "Combine", name " Left Input" }, \ + { name " Channel", "Right", name " Right Input" }, \ + { name " Channel", "Combine", name " Right Input" }, \ + { name " Left Input", "IN1", "IN1L" }, \ + { name " Right Input", "IN1", "IN1R" }, \ + { name " Left Input", "IN2", "IN2L" }, \ + { name " Right Input", "IN2", "IN2R" }, \ + { name " Left Input", "IN3", "IN3L" }, \ + { name " Right Input", "IN3", "IN3R" }, \ + { name " Left Input", "IN4", "IN4L" }, \ + { name " Right Input", "IN4", "IN4R" }, \ + { name " Left Input", "IN5", "IN5L" }, \ + { name " Right Input", "IN5", "IN5R" }, \ + { name " Left Input", "IN6", "IN6L" }, \ + { name " Right Input", "IN6", "IN6R" } + +#define CS47L85_RXANC_OUTPUT_ROUTES(widget, name) \ + { widget, NULL, name " ANC Source" }, \ + { name " ANC Source", "RXANCL", "RXANCL" }, \ + { name " ANC Source", "RXANCR", "RXANCR" } + +static void cs47l85_hp_post_enable(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + unsigned int val; + + switch (w->shift) { + case MADERA_OUT1L_ENA_SHIFT: + case MADERA_OUT1R_ENA_SHIFT: + val = snd_soc_component_read(component, MADERA_OUTPUT_ENABLES_1); + val &= (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA); + + if (val != (MADERA_OUT1L_ENA | MADERA_OUT1R_ENA)) + break; + + snd_soc_component_update_bits(component, + MADERA_EDRE_HP_STEREO_CONTROL, + 0x0001, 1); + break; + default: + break; + } +} + +static void cs47l85_hp_post_disable(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case MADERA_OUT1L_ENA_SHIFT: + snd_soc_component_write(component, MADERA_DCS_HP1L_CONTROL, + 0x2006); + break; + case MADERA_OUT1R_ENA_SHIFT: + snd_soc_component_write(component, MADERA_DCS_HP1R_CONTROL, + 0x2006); + break; + default: + return; + } + + /* Only get to here for OUT1L and OUT1R */ + snd_soc_component_update_bits(component, + MADERA_EDRE_HP_STEREO_CONTROL, + 0x0001, 0); +} + +static int cs47l85_hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_PRE_PMD: + return madera_hp_ev(w, kcontrol, event); + case SND_SOC_DAPM_POST_PMU: + ret = madera_hp_ev(w, kcontrol, event); + if (ret < 0) + return ret; + + cs47l85_hp_post_enable(w); + return 0; + case SND_SOC_DAPM_POST_PMD: + ret = madera_hp_ev(w, kcontrol, event); + cs47l85_hp_post_disable(w); + return ret; + default: + return -EINVAL; + } +} + +static const struct snd_kcontrol_new cs47l85_snd_controls[] = { +SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]), +SOC_ENUM("IN3 OSR", madera_in_dmic_osr[2]), +SOC_ENUM("IN4 OSR", madera_in_dmic_osr[3]), +SOC_ENUM("IN5 OSR", madera_in_dmic_osr[4]), +SOC_ENUM("IN6 OSR", madera_in_dmic_osr[5]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL, + MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL, + MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL, + MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL, + MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3L Volume", MADERA_IN3L_CONTROL, + MADERA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3R Volume", MADERA_IN3R_CONTROL, + MADERA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL, + MADERA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL, + MADERA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL, + MADERA_IN2L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL, + MADERA_IN2R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN3L HPF Switch", MADERA_IN3L_CONTROL, + MADERA_IN3L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN3R HPF Switch", MADERA_IN3R_CONTROL, + MADERA_IN3R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN4L HPF Switch", MADERA_IN4L_CONTROL, + MADERA_IN4L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN4R HPF Switch", MADERA_IN4R_CONTROL, + MADERA_IN4R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN5L HPF Switch", MADERA_IN5L_CONTROL, + MADERA_IN5L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN5R HPF Switch", MADERA_IN5R_CONTROL, + MADERA_IN5R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN6L HPF Switch", MADERA_IN6L_CONTROL, + MADERA_IN6L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN6R HPF Switch", MADERA_IN6R_CONTROL, + MADERA_IN6R_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L, + MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R, + MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L, + MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R, + MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3L, + MADERA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3R, + MADERA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN4L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4L, + MADERA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN4R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4R, + MADERA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN5L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5L, + MADERA_IN5L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN5R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5R, + MADERA_IN5R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN6L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_6L, + MADERA_IN6L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN6R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_6R, + MADERA_IN6R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), + +SOC_ENUM("Input Ramp Up", madera_in_vi_ramp), +SOC_ENUM("Input Ramp Down", madera_in_vd_ramp), + +SND_SOC_BYTES("RXANC Coefficients", MADERA_ANC_COEFF_START, + MADERA_ANC_COEFF_END - MADERA_ANC_COEFF_START + 1), +SND_SOC_BYTES("RXANCL Config", MADERA_FCL_FILTER_CONTROL, 1), +SND_SOC_BYTES("RXANCL Coefficients", MADERA_FCL_COEFF_START, + MADERA_FCL_COEFF_END - MADERA_FCL_COEFF_START + 1), +SND_SOC_BYTES("RXANCR Config", MADERA_FCR_FILTER_CONTROL, 1), +SND_SOC_BYTES("RXANCR Coefficients", MADERA_FCR_COEFF_START, + MADERA_FCR_COEFF_END - MADERA_FCR_COEFF_START + 1), + +MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE), + +MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2), +SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2), +SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5, + MADERA_DRC1R_ENA | MADERA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5, + MADERA_DRC2R_ENA | MADERA_DRC2L_ENA), + +MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE), + +MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2), +MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2), +MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2), +MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2), + +SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode), + +MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]), +MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]), +MADERA_RATE_ENUM("ISRC3 FSL", madera_isrc_fsl[2]), +MADERA_RATE_ENUM("ISRC4 FSL", madera_isrc_fsl[3]), +MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]), +MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]), +MADERA_RATE_ENUM("ISRC3 FSH", madera_isrc_fsh[2]), +MADERA_RATE_ENUM("ISRC4 FSH", madera_isrc_fsh[3]), +MADERA_RATE_ENUM("ASRC1 Rate 1", madera_asrc1_rate[0]), +MADERA_RATE_ENUM("ASRC1 Rate 2", madera_asrc1_rate[1]), +MADERA_RATE_ENUM("ASRC2 Rate 1", madera_asrc2_rate[0]), +MADERA_RATE_ENUM("ASRC2 Rate 2", madera_asrc2_rate[1]), + +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), +WM_ADSP2_PRELOAD_SWITCH("DSP2", 2), +WM_ADSP2_PRELOAD_SWITCH("DSP3", 3), +WM_ADSP2_PRELOAD_SWITCH("DSP4", 4), +WM_ADSP2_PRELOAD_SWITCH("DSP5", 5), +WM_ADSP2_PRELOAD_SWITCH("DSP6", 6), +WM_ADSP2_PRELOAD_SWITCH("DSP7", 7), + +MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP2L", MADERA_DSP2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP2R", MADERA_DSP2RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP3L", MADERA_DSP3LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP3R", MADERA_DSP3RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP4L", MADERA_DSP4LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP4R", MADERA_DSP4RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP5L", MADERA_DSP5LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP5R", MADERA_DSP5RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP6L", MADERA_DSP6LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP6R", MADERA_DSP6RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP7L", MADERA_DSP7LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP7R", MADERA_DSP7RMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR, + MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv), + +MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT2L", MADERA_OUT2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT2R", MADERA_OUT2RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT3L", MADERA_OUT3LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT3R", MADERA_OUT3RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKOUTL", MADERA_OUT4LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKOUTR", MADERA_OUT4RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT2L", MADERA_OUT6LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT2R", MADERA_OUT6RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL, + MADERA_HP1_SC_ENA_SHIFT, 1, 0), +SOC_SINGLE("HPOUT2 SC Protect Switch", MADERA_HP2_SHORT_CIRCUIT_CTRL, + MADERA_HP2_SC_ENA_SHIFT, 1, 0), +SOC_SINGLE("HPOUT3 SC Protect Switch", MADERA_HP3_SHORT_CIRCUIT_CTRL, + MADERA_HP3_SC_ENA_SHIFT, 1, 0), + +SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L, + MADERA_OUT5_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT2 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_6L, + MADERA_OUT6_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_2L, + MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT3 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_3L, + MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("Speaker Digital Switch", MADERA_DAC_DIGITAL_VOLUME_4L, + MADERA_DAC_DIGITAL_VOLUME_4R, MADERA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_6L, + MADERA_DAC_DIGITAL_VOLUME_6R, MADERA_OUT6L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_2L, + MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_3L, + MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("Speaker Digital Volume", MADERA_DAC_DIGITAL_VOLUME_4L, + MADERA_DAC_DIGITAL_VOLUME_4R, MADERA_OUT4L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_6L, + MADERA_DAC_DIGITAL_VOLUME_6R, MADERA_OUT6L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), + +SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT, + MADERA_SPK1R_MUTE_SHIFT, 1, 1), +SOC_DOUBLE("SPKDAT2 Switch", MADERA_PDM_SPK2_CTRL_1, MADERA_SPK2L_MUTE_SHIFT, + MADERA_SPK2R_MUTE_SHIFT, 1, 1), + +SOC_ENUM("Output Ramp Up", madera_out_vi_ramp), +SOC_ENUM("Output Ramp Down", madera_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv), +SOC_ENUM("Noise Gate Hold", madera_ng_hold), + +CS47L85_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L), +CS47L85_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R), +CS47L85_NG_SRC("HPOUT2L", MADERA_NOISE_GATE_SELECT_2L), +CS47L85_NG_SRC("HPOUT2R", MADERA_NOISE_GATE_SELECT_2R), +CS47L85_NG_SRC("HPOUT3L", MADERA_NOISE_GATE_SELECT_3L), +CS47L85_NG_SRC("HPOUT3R", MADERA_NOISE_GATE_SELECT_3R), +CS47L85_NG_SRC("SPKOUTL", MADERA_NOISE_GATE_SELECT_4L), +CS47L85_NG_SRC("SPKOUTR", MADERA_NOISE_GATE_SELECT_4R), +CS47L85_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L), +CS47L85_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R), +CS47L85_NG_SRC("SPKDAT2L", MADERA_NOISE_GATE_SELECT_6L), +CS47L85_NG_SRC("SPKDAT2R", MADERA_NOISE_GATE_SELECT_6R), + +MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX7", MADERA_AIF1TX7MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX8", MADERA_AIF1TX8MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX5", MADERA_AIF2TX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX6", MADERA_AIF2TX6MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX7", MADERA_AIF2TX7MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX8", MADERA_AIF2TX8MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF4TX1", MADERA_AIF4TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF4TX2", MADERA_AIF4TX2MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX7", MADERA_SLIMTX7MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX8", MADERA_SLIMTX8MIX_INPUT_1_SOURCE), + +MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE), +MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), +WM_ADSP_FW_CONTROL("DSP2", 1), +WM_ADSP_FW_CONTROL("DSP3", 2), +WM_ADSP_FW_CONTROL("DSP4", 3), +WM_ADSP_FW_CONTROL("DSP5", 4), +WM_ADSP_FW_CONTROL("DSP6", 5), +WM_ADSP_FW_CONTROL("DSP7", 6), +}; + +MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP2L, MADERA_DSP2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP2R, MADERA_DSP2RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP2, MADERA_DSP2AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP3L, MADERA_DSP3LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP3R, MADERA_DSP3RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP3, MADERA_DSP3AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP4L, MADERA_DSP4LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP4R, MADERA_DSP4RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP4, MADERA_DSP4AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP5L, MADERA_DSP5LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP5R, MADERA_DSP5RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP5, MADERA_DSP5AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP6L, MADERA_DSP6LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP6R, MADERA_DSP6RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP6, MADERA_DSP6AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP7L, MADERA_DSP7LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP7R, MADERA_DSP7RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP7, MADERA_DSP7AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT2L, MADERA_OUT2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT2R, MADERA_OUT2RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT3L, MADERA_OUT3LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT3R, MADERA_OUT3RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKOUTL, MADERA_OUT4LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKOUTR, MADERA_OUT4RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT2L, MADERA_OUT6LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT2R, MADERA_OUT6RMIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX7, MADERA_AIF1TX7MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX8, MADERA_AIF1TX8MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX5, MADERA_AIF2TX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX6, MADERA_AIF2TX6MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX7, MADERA_AIF2TX7MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX8, MADERA_AIF2TX8MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF4TX1, MADERA_AIF4TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF4TX2, MADERA_AIF4TX2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX7, MADERA_SLIMTX7MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX8, MADERA_SLIMTX8MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ASRC1IN1L, MADERA_ASRC1_1LMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC1IN1R, MADERA_ASRC1_1RMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC1IN2L, MADERA_ASRC1_2LMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC1IN2R, MADERA_ASRC1_2RMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC2IN1L, MADERA_ASRC2_1LMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC2IN1R, MADERA_ASRC2_1RMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC2IN2L, MADERA_ASRC2_2LMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC2IN2R, MADERA_ASRC2_2RMIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC3INT1, MADERA_ISRC3INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC3INT2, MADERA_ISRC3INT2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC3DEC1, MADERA_ISRC3DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC3DEC2, MADERA_ISRC3DEC2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC4INT1, MADERA_ISRC4INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC4INT2, MADERA_ISRC4INT2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC4DEC1, MADERA_ISRC4DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC4DEC2, MADERA_ISRC4DEC2MIX_INPUT_1_SOURCE); + +static const char * const cs47l85_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R", + "SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R", "SPKDAT2L", "SPKDAT2R", +}; + +static const unsigned int cs47l85_aec_loopback_values[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +}; + +static const struct soc_enum cs47l85_aec1_loopback = + SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1, + MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l85_aec_loopback_texts), + cs47l85_aec_loopback_texts, + cs47l85_aec_loopback_values); + +static const struct soc_enum cs47l85_aec2_loopback = + SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2, + MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l85_aec_loopback_texts), + cs47l85_aec_loopback_texts, + cs47l85_aec_loopback_values); + +static const struct snd_kcontrol_new cs47l85_aec_loopback_mux[] = { + SOC_DAPM_ENUM("AEC1 Loopback", cs47l85_aec1_loopback), + SOC_DAPM_ENUM("AEC2 Loopback", cs47l85_aec2_loopback), +}; + +static const struct snd_kcontrol_new cs47l85_anc_input_mux[] = { + SOC_DAPM_ENUM("RXANCL Input", madera_anc_input_src[0]), + SOC_DAPM_ENUM("RXANCL Channel", madera_anc_input_src[1]), + SOC_DAPM_ENUM("RXANCR Input", madera_anc_input_src[2]), + SOC_DAPM_ENUM("RXANCR Channel", madera_anc_input_src[3]), +}; + +static const struct snd_kcontrol_new cs47l85_anc_ng_mux = + SOC_DAPM_ENUM("RXANC NG Source", madera_anc_ng_enum); + +static const struct snd_kcontrol_new cs47l85_output_anc_src[] = { + SOC_DAPM_ENUM("HPOUT1L ANC Source", madera_output_anc_src[0]), + SOC_DAPM_ENUM("HPOUT1R ANC Source", madera_output_anc_src[1]), + SOC_DAPM_ENUM("HPOUT2L ANC Source", madera_output_anc_src[2]), + SOC_DAPM_ENUM("HPOUT2R ANC Source", madera_output_anc_src[3]), + SOC_DAPM_ENUM("HPOUT3L ANC Source", madera_output_anc_src[4]), + SOC_DAPM_ENUM("HPOUT3R ANC Source", madera_output_anc_src[5]), + SOC_DAPM_ENUM("SPKOUTL ANC Source", madera_output_anc_src[6]), + SOC_DAPM_ENUM("SPKOUTR ANC Source", madera_output_anc_src[7]), + SOC_DAPM_ENUM("SPKDAT1L ANC Source", madera_output_anc_src[8]), + SOC_DAPM_ENUM("SPKDAT1R ANC Source", madera_output_anc_src[9]), + SOC_DAPM_ENUM("SPKDAT2L ANC Source", madera_output_anc_src[10]), + SOC_DAPM_ENUM("SPKDAT2R ANC Source", madera_output_anc_src[11]), +}; + +static const struct snd_soc_dapm_widget cs47l85_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT, + 0, madera_sysclk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1, + MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK, + MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK, + MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT, + 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD4", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", MADERA_MIC_BIAS_CTRL_3, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS4", MADERA_MIC_BIAS_CTRL_4, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_FX, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASRC1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ASRC1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASRC2CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ASRC2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC3CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC3, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC4CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC4, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_OUT, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_SPD, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP3CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP3, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP4CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP4, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP5CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP5, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP6CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP6, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP7CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP7, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF3, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF4TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF4, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_SLIMBUS, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_PWM, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM, + MADERA_EXT_NG_SEL_SET_SHIFT, 0, madera_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM, + MADERA_CLK_NG_ENA_SET_SHIFT, 0, madera_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), + +SND_SOC_DAPM_INPUT("IN1ALN"), +SND_SOC_DAPM_INPUT("IN1ALP"), +SND_SOC_DAPM_INPUT("IN1BN"), +SND_SOC_DAPM_INPUT("IN1BP"), +SND_SOC_DAPM_INPUT("IN1RN"), +SND_SOC_DAPM_INPUT("IN1RP"), +SND_SOC_DAPM_INPUT("IN2ALN"), +SND_SOC_DAPM_INPUT("IN2ALP"), +SND_SOC_DAPM_INPUT("IN2ARN"), +SND_SOC_DAPM_INPUT("IN2ARP"), +SND_SOC_DAPM_INPUT("IN2BLN"), +SND_SOC_DAPM_INPUT("IN2BLP"), +SND_SOC_DAPM_INPUT("IN2BRN"), +SND_SOC_DAPM_INPUT("IN2BRP"), +SND_SOC_DAPM_INPUT("IN3LN"), +SND_SOC_DAPM_INPUT("IN3LP"), +SND_SOC_DAPM_INPUT("IN3RN"), +SND_SOC_DAPM_INPUT("IN3RP"), +SND_SOC_DAPM_INPUT("DMICCLK4"), +SND_SOC_DAPM_INPUT("DMICDAT4"), +SND_SOC_DAPM_INPUT("DMICCLK5"), +SND_SOC_DAPM_INPUT("DMICDAT5"), +SND_SOC_DAPM_INPUT("DMICCLK6"), +SND_SOC_DAPM_INPUT("DMICDAT6"), + +SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]), +SND_SOC_DAPM_MUX("IN2L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[2]), +SND_SOC_DAPM_MUX("IN2R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[3]), + +SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]), +SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]), + +SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), +SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), + +SND_SOC_DAPM_MUX("IN3L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[2]), +SND_SOC_DAPM_MUX("IN3R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[2]), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_OUTPUT("DSP Trigger Out"), + +SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0, + &cs47l85_anc_input_mux[0]), +SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0, + &cs47l85_anc_input_mux[0]), +SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0, + &cs47l85_anc_input_mux[1]), +SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &cs47l85_anc_ng_mux), +SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0, + &cs47l85_anc_input_mux[2]), +SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0, + &cs47l85_anc_input_mux[2]), +SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0, + &cs47l85_anc_input_mux[3]), +SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &cs47l85_anc_ng_mux), + +SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, MADERA_CLK_L_ENA_SET_SHIFT, + 0, NULL, 0, madera_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, MADERA_CLK_R_ENA_SET_SHIFT, + 0, NULL, 0, madera_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[0]), +SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[1]), +SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[2]), +SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[3]), +SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[4]), +SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[5]), +SND_SOC_DAPM_MUX("SPKOUTL ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[6]), +SND_SOC_DAPM_MUX("SPKOUTR ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[7]), +SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[8]), +SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[9]), +SND_SOC_DAPM_MUX("SPKDAT2L ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[10]), +SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l85_output_anc_src[11]), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX7", NULL, 6, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX8", NULL, 7, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF4TX1", NULL, 0, + MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF4TX2", NULL, 1, + MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, cs47l85_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, cs47l85_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT2L_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT2R_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT3L_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3R", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT3R_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT4L", SND_SOC_NOPM, + MADERA_OUT4L_ENA_SHIFT, 0, NULL, 0, madera_spk_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_E("OUT4R", SND_SOC_NOPM, + MADERA_OUT4R_ENA_SHIFT, 0, NULL, 0, madera_spk_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT6L", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT6L_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT6R", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT6R_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_ENA_SHIFT, 0, NULL, 0), + +/* + * Input mux widgets arranged in order of sources in MADERA_MIXER_INPUT_ROUTES + * to take advantage of cache lookup in DAPM + */ +SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR, + MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1, + MADERA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1, + MADERA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1, + MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0, + &cs47l85_aec_loopback_mux[0]), +SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2, + MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0, + &cs47l85_aec_loopback_mux[1]), + +SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3L", MADERA_INPUT_ENABLES, MADERA_IN3L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3R", MADERA_INPUT_ENABLES, MADERA_IN3R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4L", MADERA_INPUT_ENABLES, MADERA_IN4L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4R", MADERA_INPUT_ENABLES, MADERA_IN4R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN5L", MADERA_INPUT_ENABLES, MADERA_IN5L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN5R", MADERA_INPUT_ENABLES, MADERA_IN5R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN6L", MADERA_INPUT_ENABLES, MADERA_IN6L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN6R", MADERA_INPUT_ENABLES, MADERA_IN6R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX7", NULL, 6, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX8", NULL, 7, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF4RX1", NULL, 0, + MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF4RX2", NULL, 1, + MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, + MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("ASRC1IN1L", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN1L_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC1IN1R", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN1R_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC1IN2L", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN2L_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC1IN2R", MADERA_ASRC1_ENABLE, MADERA_ASRC1_IN2R_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ASRC2IN1L", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN1L_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC2IN1R", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN1R_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC2IN2L", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN2L_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC2IN2R", MADERA_ASRC2_ENABLE, MADERA_ASRC2_IN2R_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3DEC1", MADERA_ISRC_3_CTRL_3, + MADERA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC2", MADERA_ISRC_3_CTRL_3, + MADERA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3INT1", MADERA_ISRC_3_CTRL_3, + MADERA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT2", MADERA_ISRC_3_CTRL_3, + MADERA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC4DEC1", MADERA_ISRC_4_CTRL_3, + MADERA_ISRC4_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC4DEC2", MADERA_ISRC_4_CTRL_3, + MADERA_ISRC4_DEC2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC4INT1", MADERA_ISRC_4_CTRL_3, + MADERA_ISRC4_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC4INT2", MADERA_ISRC_4_CTRL_3, + MADERA_ISRC4_INT2_ENA_SHIFT, 0, NULL, 0), + +WM_ADSP2("DSP1", 0, cs47l85_adsp_power_ev), +WM_ADSP2("DSP2", 1, cs47l85_adsp_power_ev), +WM_ADSP2("DSP3", 2, cs47l85_adsp_power_ev), +WM_ADSP2("DSP4", 3, cs47l85_adsp_power_ev), +WM_ADSP2("DSP5", 4, cs47l85_adsp_power_ev), +WM_ADSP2("DSP6", 5, cs47l85_adsp_power_ev), +WM_ADSP2("DSP7", 6, cs47l85_adsp_power_ev), + +/* End of ordered input mux widgets */ + +MADERA_MIXER_WIDGETS(EQ1, "EQ1"), +MADERA_MIXER_WIDGETS(EQ2, "EQ2"), +MADERA_MIXER_WIDGETS(EQ3, "EQ3"), +MADERA_MIXER_WIDGETS(EQ4, "EQ4"), + +MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"), +MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"), +MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"), +MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0, + &madera_drc_activity_output_mux[0]), +SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0, + &madera_drc_activity_output_mux[1]), + +MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"), +MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"), +MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"), +MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +MADERA_MIXER_WIDGETS(PWM1, "PWM1"), +MADERA_MIXER_WIDGETS(PWM2, "PWM2"), + +MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +MADERA_MIXER_WIDGETS(OUT2L, "HPOUT2L"), +MADERA_MIXER_WIDGETS(OUT2R, "HPOUT2R"), +MADERA_MIXER_WIDGETS(OUT3L, "HPOUT3L"), +MADERA_MIXER_WIDGETS(OUT3R, "HPOUT3R"), +MADERA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +MADERA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"), +MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), +MADERA_MIXER_WIDGETS(SPKDAT2L, "SPKDAT2L"), +MADERA_MIXER_WIDGETS(SPKDAT2R, "SPKDAT2R"), + +MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +MADERA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +MADERA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +MADERA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +MADERA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), +MADERA_MIXER_WIDGETS(AIF2TX7, "AIF2TX7"), +MADERA_MIXER_WIDGETS(AIF2TX8, "AIF2TX8"), + +MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +MADERA_MIXER_WIDGETS(AIF4TX1, "AIF4TX1"), +MADERA_MIXER_WIDGETS(AIF4TX2, "AIF4TX2"), + +MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), +MADERA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"), +MADERA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"), + +MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"), +MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"), + +MADERA_MUX_WIDGETS(ASRC1IN1L, "ASRC1IN1L"), +MADERA_MUX_WIDGETS(ASRC1IN1R, "ASRC1IN1R"), +MADERA_MUX_WIDGETS(ASRC1IN2L, "ASRC1IN2L"), +MADERA_MUX_WIDGETS(ASRC1IN2R, "ASRC1IN2R"), +MADERA_MUX_WIDGETS(ASRC2IN1L, "ASRC2IN1L"), +MADERA_MUX_WIDGETS(ASRC2IN1R, "ASRC2IN1R"), +MADERA_MUX_WIDGETS(ASRC2IN2L, "ASRC2IN2L"), +MADERA_MUX_WIDGETS(ASRC2IN2R, "ASRC2IN2R"), + +MADERA_DSP_WIDGETS(DSP1, "DSP1"), +MADERA_DSP_WIDGETS(DSP2, "DSP2"), +MADERA_DSP_WIDGETS(DSP3, "DSP3"), +MADERA_DSP_WIDGETS(DSP4, "DSP4"), +MADERA_DSP_WIDGETS(DSP5, "DSP5"), +MADERA_DSP_WIDGETS(DSP6, "DSP6"), +MADERA_DSP_WIDGETS(DSP7, "DSP7"), + +SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[0]), +SND_SOC_DAPM_SWITCH("DSP2 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[1]), +SND_SOC_DAPM_SWITCH("DSP3 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[2]), +SND_SOC_DAPM_SWITCH("DSP4 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[3]), +SND_SOC_DAPM_SWITCH("DSP5 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[4]), +SND_SOC_DAPM_SWITCH("DSP6 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[5]), +SND_SOC_DAPM_SWITCH("DSP7 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[6]), + +MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), +MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"), +MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"), + +MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), +MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"), +MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"), + +MADERA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"), +MADERA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"), + +MADERA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"), +MADERA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"), + +MADERA_MUX_WIDGETS(ISRC4DEC1, "ISRC4DEC1"), +MADERA_MUX_WIDGETS(ISRC4DEC2, "ISRC4DEC2"), + +MADERA_MUX_WIDGETS(ISRC4INT1, "ISRC4INT1"), +MADERA_MUX_WIDGETS(ISRC4INT2, "ISRC4INT2"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2L"), +SND_SOC_DAPM_OUTPUT("HPOUT2R"), +SND_SOC_DAPM_OUTPUT("HPOUT3L"), +SND_SOC_DAPM_OUTPUT("HPOUT3R"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), +SND_SOC_DAPM_OUTPUT("SPKDAT2L"), +SND_SOC_DAPM_OUTPUT("SPKDAT2R"), +SND_SOC_DAPM_OUTPUT("SPDIF1"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define MADERA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC1", "AEC1 Loopback" }, \ + { name, "AEC2", "AEC2 Loopback" }, \ + { name, "IN1L", "IN1L" }, \ + { name, "IN1R", "IN1R" }, \ + { name, "IN2L", "IN2L" }, \ + { name, "IN2R", "IN2R" }, \ + { name, "IN3L", "IN3L" }, \ + { name, "IN3R", "IN3R" }, \ + { name, "IN4L", "IN4L" }, \ + { name, "IN4R", "IN4R" }, \ + { name, "IN5L", "IN5L" }, \ + { name, "IN5R", "IN5R" }, \ + { name, "IN6L", "IN6L" }, \ + { name, "IN6R", "IN6R" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF2RX7", "AIF2RX7" }, \ + { name, "AIF2RX8", "AIF2RX8" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "AIF4RX1", "AIF4RX1" }, \ + { name, "AIF4RX2", "AIF4RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "SLIMRX7", "SLIMRX7" }, \ + { name, "SLIMRX8", "SLIMRX8" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1IN1L", "ASRC1IN1L" }, \ + { name, "ASRC1IN1R", "ASRC1IN1R" }, \ + { name, "ASRC1IN2L", "ASRC1IN2L" }, \ + { name, "ASRC1IN2R", "ASRC1IN2R" }, \ + { name, "ASRC2IN1L", "ASRC2IN1L" }, \ + { name, "ASRC2IN1R", "ASRC2IN1R" }, \ + { name, "ASRC2IN2L", "ASRC2IN2L" }, \ + { name, "ASRC2IN2R", "ASRC2IN2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2DEC3", "ISRC2DEC3" }, \ + { name, "ISRC2DEC4", "ISRC2DEC4" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC2INT3", "ISRC2INT3" }, \ + { name, "ISRC2INT4", "ISRC2INT4" }, \ + { name, "ISRC3DEC1", "ISRC3DEC1" }, \ + { name, "ISRC3DEC2", "ISRC3DEC2" }, \ + { name, "ISRC3INT1", "ISRC3INT1" }, \ + { name, "ISRC3INT2", "ISRC3INT2" }, \ + { name, "ISRC4DEC1", "ISRC4DEC1" }, \ + { name, "ISRC4DEC2", "ISRC4DEC2" }, \ + { name, "ISRC4INT1", "ISRC4INT1" }, \ + { name, "ISRC4INT2", "ISRC4INT2" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" }, \ + { name, "DSP2.1", "DSP2" }, \ + { name, "DSP2.2", "DSP2" }, \ + { name, "DSP2.3", "DSP2" }, \ + { name, "DSP2.4", "DSP2" }, \ + { name, "DSP2.5", "DSP2" }, \ + { name, "DSP2.6", "DSP2" }, \ + { name, "DSP3.1", "DSP3" }, \ + { name, "DSP3.2", "DSP3" }, \ + { name, "DSP3.3", "DSP3" }, \ + { name, "DSP3.4", "DSP3" }, \ + { name, "DSP3.5", "DSP3" }, \ + { name, "DSP3.6", "DSP3" }, \ + { name, "DSP4.1", "DSP4" }, \ + { name, "DSP4.2", "DSP4" }, \ + { name, "DSP4.3", "DSP4" }, \ + { name, "DSP4.4", "DSP4" }, \ + { name, "DSP4.5", "DSP4" }, \ + { name, "DSP4.6", "DSP4" }, \ + { name, "DSP5.1", "DSP5" }, \ + { name, "DSP5.2", "DSP5" }, \ + { name, "DSP5.3", "DSP5" }, \ + { name, "DSP5.4", "DSP5" }, \ + { name, "DSP5.5", "DSP5" }, \ + { name, "DSP5.6", "DSP5" }, \ + { name, "DSP6.1", "DSP6" }, \ + { name, "DSP6.2", "DSP6" }, \ + { name, "DSP6.3", "DSP6" }, \ + { name, "DSP6.4", "DSP6" }, \ + { name, "DSP6.5", "DSP6" }, \ + { name, "DSP6.6", "DSP6" }, \ + { name, "DSP7.1", "DSP7" }, \ + { name, "DSP7.2", "DSP7" }, \ + { name, "DSP7.3", "DSP7" }, \ + { name, "DSP7.4", "DSP7" }, \ + { name, "DSP7.5", "DSP7" }, \ + { name, "DSP7.6", "DSP7" } + +static const struct snd_soc_dapm_route cs47l85_dapm_routes[] = { + /* Internal clock domains */ + { "EQ1", NULL, "FXCLK" }, + { "EQ2", NULL, "FXCLK" }, + { "EQ3", NULL, "FXCLK" }, + { "EQ4", NULL, "FXCLK" }, + { "DRC1L", NULL, "FXCLK" }, + { "DRC1R", NULL, "FXCLK" }, + { "DRC2L", NULL, "FXCLK" }, + { "DRC2R", NULL, "FXCLK" }, + { "LHPF1", NULL, "FXCLK" }, + { "LHPF2", NULL, "FXCLK" }, + { "LHPF3", NULL, "FXCLK" }, + { "LHPF4", NULL, "FXCLK" }, + { "PWM1 Mixer", NULL, "PWMCLK" }, + { "PWM2 Mixer", NULL, "PWMCLK" }, + { "OUT1L", NULL, "OUTCLK" }, + { "OUT1R", NULL, "OUTCLK" }, + { "OUT2L", NULL, "OUTCLK" }, + { "OUT2R", NULL, "OUTCLK" }, + { "OUT3L", NULL, "OUTCLK" }, + { "OUT3R", NULL, "OUTCLK" }, + { "OUT4L", NULL, "OUTCLK" }, + { "OUT4R", NULL, "OUTCLK" }, + { "OUT5L", NULL, "OUTCLK" }, + { "OUT5R", NULL, "OUTCLK" }, + { "OUT6L", NULL, "OUTCLK" }, + { "OUT6R", NULL, "OUTCLK" }, + { "AIF1TX1", NULL, "AIF1TXCLK" }, + { "AIF1TX2", NULL, "AIF1TXCLK" }, + { "AIF1TX3", NULL, "AIF1TXCLK" }, + { "AIF1TX4", NULL, "AIF1TXCLK" }, + { "AIF1TX5", NULL, "AIF1TXCLK" }, + { "AIF1TX6", NULL, "AIF1TXCLK" }, + { "AIF1TX7", NULL, "AIF1TXCLK" }, + { "AIF1TX8", NULL, "AIF1TXCLK" }, + { "AIF2TX1", NULL, "AIF2TXCLK" }, + { "AIF2TX2", NULL, "AIF2TXCLK" }, + { "AIF2TX3", NULL, "AIF2TXCLK" }, + { "AIF2TX4", NULL, "AIF2TXCLK" }, + { "AIF2TX5", NULL, "AIF2TXCLK" }, + { "AIF2TX6", NULL, "AIF2TXCLK" }, + { "AIF2TX7", NULL, "AIF2TXCLK" }, + { "AIF2TX8", NULL, "AIF2TXCLK" }, + { "AIF3TX1", NULL, "AIF3TXCLK" }, + { "AIF3TX2", NULL, "AIF3TXCLK" }, + { "AIF4TX1", NULL, "AIF4TXCLK" }, + { "AIF4TX2", NULL, "AIF4TXCLK" }, + { "SLIMTX1", NULL, "SLIMBUSCLK" }, + { "SLIMTX2", NULL, "SLIMBUSCLK" }, + { "SLIMTX3", NULL, "SLIMBUSCLK" }, + { "SLIMTX4", NULL, "SLIMBUSCLK" }, + { "SLIMTX5", NULL, "SLIMBUSCLK" }, + { "SLIMTX6", NULL, "SLIMBUSCLK" }, + { "SLIMTX7", NULL, "SLIMBUSCLK" }, + { "SLIMTX8", NULL, "SLIMBUSCLK" }, + { "SPD1TX1", NULL, "SPDCLK" }, + { "SPD1TX2", NULL, "SPDCLK" }, + { "DSP1", NULL, "DSP1CLK" }, + { "DSP2", NULL, "DSP2CLK" }, + { "DSP3", NULL, "DSP3CLK" }, + { "DSP4", NULL, "DSP4CLK" }, + { "DSP5", NULL, "DSP5CLK" }, + { "DSP6", NULL, "DSP6CLK" }, + { "DSP7", NULL, "DSP7CLK" }, + { "ISRC1DEC1", NULL, "ISRC1CLK" }, + { "ISRC1DEC2", NULL, "ISRC1CLK" }, + { "ISRC1DEC3", NULL, "ISRC1CLK" }, + { "ISRC1DEC4", NULL, "ISRC1CLK" }, + { "ISRC1INT1", NULL, "ISRC1CLK" }, + { "ISRC1INT2", NULL, "ISRC1CLK" }, + { "ISRC1INT3", NULL, "ISRC1CLK" }, + { "ISRC1INT4", NULL, "ISRC1CLK" }, + { "ISRC2DEC1", NULL, "ISRC2CLK" }, + { "ISRC2DEC2", NULL, "ISRC2CLK" }, + { "ISRC2DEC3", NULL, "ISRC2CLK" }, + { "ISRC2DEC4", NULL, "ISRC2CLK" }, + { "ISRC2INT1", NULL, "ISRC2CLK" }, + { "ISRC2INT2", NULL, "ISRC2CLK" }, + { "ISRC2INT3", NULL, "ISRC2CLK" }, + { "ISRC2INT4", NULL, "ISRC2CLK" }, + { "ISRC3DEC1", NULL, "ISRC3CLK" }, + { "ISRC3DEC2", NULL, "ISRC3CLK" }, + { "ISRC3INT1", NULL, "ISRC3CLK" }, + { "ISRC3INT2", NULL, "ISRC3CLK" }, + { "ISRC4DEC1", NULL, "ISRC4CLK" }, + { "ISRC4DEC2", NULL, "ISRC4CLK" }, + { "ISRC4INT1", NULL, "ISRC4CLK" }, + { "ISRC4INT2", NULL, "ISRC4CLK" }, + { "ASRC1IN1L", NULL, "ASRC1CLK" }, + { "ASRC1IN1R", NULL, "ASRC1CLK" }, + { "ASRC1IN2L", NULL, "ASRC1CLK" }, + { "ASRC1IN2R", NULL, "ASRC1CLK" }, + { "ASRC2IN1L", NULL, "ASRC2CLK" }, + { "ASRC2IN1R", NULL, "ASRC2CLK" }, + { "ASRC2IN2L", NULL, "ASRC2CLK" }, + { "ASRC2IN2R", NULL, "ASRC2CLK" }, + + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD3" }, + { "AIF3 Playback", NULL, "DBVDD3" }, + + { "AIF4 Capture", NULL, "DBVDD3" }, + { "AIF4 Playback", NULL, "DBVDD3" }, + + { "OUT1L", NULL, "CPVDD1" }, + { "OUT1L", NULL, "CPVDD2" }, + { "OUT1R", NULL, "CPVDD1" }, + { "OUT1R", NULL, "CPVDD2" }, + { "OUT2L", NULL, "CPVDD1" }, + { "OUT2L", NULL, "CPVDD2" }, + { "OUT2R", NULL, "CPVDD1" }, + { "OUT2R", NULL, "CPVDD2" }, + { "OUT3L", NULL, "CPVDD1" }, + { "OUT3L", NULL, "CPVDD2" }, + { "OUT3R", NULL, "CPVDD1" }, + { "OUT3R", NULL, "CPVDD2" }, + + { "OUT4L", NULL, "SPKVDDL" }, + { "OUT4R", NULL, "SPKVDDR" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT3R", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT4R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + { "OUT6L", NULL, "SYSCLK" }, + { "OUT6R", NULL, "SYSCLK" }, + + { "SPD1", NULL, "SYSCLK" }, + { "SPD1", NULL, "SPD1TX1" }, + { "SPD1", NULL, "SPD1TX2" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + { "IN3L", NULL, "SYSCLK" }, + { "IN3R", NULL, "SYSCLK" }, + { "IN4L", NULL, "SYSCLK" }, + { "IN4R", NULL, "SYSCLK" }, + { "IN5L", NULL, "SYSCLK" }, + { "IN5R", NULL, "SYSCLK" }, + { "IN6L", NULL, "SYSCLK" }, + { "IN6R", NULL, "SYSCLK" }, + + { "IN4L", NULL, "DBVDD4" }, + { "IN4R", NULL, "DBVDD4" }, + { "IN5L", NULL, "DBVDD4" }, + { "IN5R", NULL, "DBVDD4" }, + { "IN6L", NULL, "DBVDD4" }, + { "IN6R", NULL, "DBVDD4" }, + + { "ASRC1IN1L", NULL, "SYSCLK" }, + { "ASRC1IN1R", NULL, "SYSCLK" }, + { "ASRC1IN2L", NULL, "SYSCLK" }, + { "ASRC1IN2R", NULL, "SYSCLK" }, + { "ASRC2IN1L", NULL, "SYSCLK" }, + { "ASRC2IN1R", NULL, "SYSCLK" }, + { "ASRC2IN2L", NULL, "SYSCLK" }, + { "ASRC2IN2R", NULL, "SYSCLK" }, + + { "ASRC1IN1L", NULL, "ASYNCCLK" }, + { "ASRC1IN1R", NULL, "ASYNCCLK" }, + { "ASRC1IN2L", NULL, "ASYNCCLK" }, + { "ASRC1IN2R", NULL, "ASYNCCLK" }, + { "ASRC2IN1L", NULL, "ASYNCCLK" }, + { "ASRC2IN1R", NULL, "ASYNCCLK" }, + { "ASRC2IN2L", NULL, "ASYNCCLK" }, + { "ASRC2IN2R", NULL, "ASYNCCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + { "MICBIAS4", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + { "AIF2 Capture", NULL, "AIF2TX7" }, + { "AIF2 Capture", NULL, "AIF2TX8" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + { "AIF2RX7", NULL, "AIF2 Playback" }, + { "AIF2RX8", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "AIF4 Capture", NULL, "AIF4TX1" }, + { "AIF4 Capture", NULL, "AIF4TX2" }, + + { "AIF4RX1", NULL, "AIF4 Playback" }, + { "AIF4RX2", NULL, "AIF4 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "Slim3 Capture", NULL, "SLIMTX7" }, + { "Slim3 Capture", NULL, "SLIMTX8" }, + + { "SLIMRX7", NULL, "Slim3 Playback" }, + { "SLIMRX8", NULL, "Slim3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + { "AIF4 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + { "Slim3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + { "AIF4 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + { "Slim3 Capture", NULL, "SYSCLK" }, + + { "Voice Control DSP", NULL, "DSP6" }, + + { "Audio Trace DSP", NULL, "DSP1" }, + + { "IN1L Analog Mux", "A", "IN1ALN" }, + { "IN1L Analog Mux", "A", "IN1ALP" }, + { "IN1L Analog Mux", "B", "IN1BN" }, + { "IN1L Analog Mux", "B", "IN1BP" }, + + { "IN1L Mode", "Analog", "IN1L Analog Mux" }, + { "IN1R Mode", "Analog", "IN1RN" }, + { "IN1R Mode", "Analog", "IN1RP" }, + + { "IN1L Mode", "Digital", "IN1ALN" }, + { "IN1L Mode", "Digital", "IN1RN" }, + { "IN1R Mode", "Digital", "IN1ALN" }, + { "IN1R Mode", "Digital", "IN1RN" }, + + { "IN1L", NULL, "IN1L Mode" }, + { "IN1R", NULL, "IN1R Mode" }, + + { "IN2L Analog Mux", "A", "IN2ALN" }, + { "IN2L Analog Mux", "A", "IN2ALP" }, + { "IN2L Analog Mux", "B", "IN2BLN" }, + { "IN2L Analog Mux", "B", "IN2BLP" }, + { "IN2R Analog Mux", "A", "IN2ARN" }, + { "IN2R Analog Mux", "A", "IN2ARP" }, + { "IN2R Analog Mux", "B", "IN2BRN" }, + { "IN2R Analog Mux", "B", "IN2BRP" }, + + { "IN2L Mode", "Analog", "IN2L Analog Mux" }, + { "IN2R Mode", "Analog", "IN2R Analog Mux" }, + + { "IN2L Mode", "Digital", "IN2ALN" }, + { "IN2L Mode", "Digital", "IN2ARN" }, + { "IN2R Mode", "Digital", "IN2ALN" }, + { "IN2R Mode", "Digital", "IN2ARN" }, + + { "IN2L", NULL, "IN2L Mode" }, + { "IN2R", NULL, "IN2R Mode" }, + + { "IN3L Mode", "Analog", "IN3LN" }, + { "IN3L Mode", "Analog", "IN3LP" }, + { "IN3R Mode", "Analog", "IN3RN" }, + { "IN3R Mode", "Analog", "IN3RP" }, + + { "IN3L Mode", "Digital", "IN3LN" }, + { "IN3L Mode", "Digital", "IN3RN" }, + { "IN3R Mode", "Digital", "IN3LN" }, + { "IN3R Mode", "Digital", "IN3RN" }, + + { "IN3L", NULL, "IN3L Mode" }, + { "IN3R", NULL, "IN3R Mode" }, + + { "IN4L", NULL, "DMICCLK4" }, + { "IN4L", NULL, "DMICDAT4" }, + { "IN4R", NULL, "DMICCLK4" }, + { "IN4R", NULL, "DMICDAT4" }, + + { "IN5L", NULL, "DMICCLK5" }, + { "IN5L", NULL, "DMICDAT5" }, + { "IN5R", NULL, "DMICCLK5" }, + { "IN5R", NULL, "DMICDAT5" }, + + { "IN6L", NULL, "DMICCLK6" }, + { "IN6L", NULL, "DMICDAT6" }, + { "IN6R", NULL, "DMICCLK6" }, + { "IN6R", NULL, "DMICDAT6" }, + + MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + MADERA_MIXER_ROUTES("OUT2L", "HPOUT2L"), + MADERA_MIXER_ROUTES("OUT2R", "HPOUT2R"), + MADERA_MIXER_ROUTES("OUT3L", "HPOUT3L"), + MADERA_MIXER_ROUTES("OUT3R", "HPOUT3R"), + + MADERA_MIXER_ROUTES("OUT4L", "SPKOUTL"), + MADERA_MIXER_ROUTES("OUT4R", "SPKOUTR"), + MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + MADERA_MIXER_ROUTES("OUT6L", "SPKDAT2L"), + MADERA_MIXER_ROUTES("OUT6R", "SPKDAT2R"), + + MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + MADERA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + MADERA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + MADERA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + MADERA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + MADERA_MIXER_ROUTES("AIF2TX7", "AIF2TX7"), + MADERA_MIXER_ROUTES("AIF2TX8", "AIF2TX8"), + + MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + MADERA_MIXER_ROUTES("AIF4TX1", "AIF4TX1"), + MADERA_MIXER_ROUTES("AIF4TX2", "AIF4TX2"), + + MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + MADERA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"), + MADERA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"), + + MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"), + MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"), + + MADERA_MIXER_ROUTES("EQ1", "EQ1"), + MADERA_MIXER_ROUTES("EQ2", "EQ2"), + MADERA_MIXER_ROUTES("EQ3", "EQ3"), + MADERA_MIXER_ROUTES("EQ4", "EQ4"), + + MADERA_MIXER_ROUTES("DRC1L", "DRC1L"), + MADERA_MIXER_ROUTES("DRC1R", "DRC1R"), + MADERA_MIXER_ROUTES("DRC2L", "DRC2L"), + MADERA_MIXER_ROUTES("DRC2R", "DRC2R"), + + MADERA_MIXER_ROUTES("LHPF1", "LHPF1"), + MADERA_MIXER_ROUTES("LHPF2", "LHPF2"), + MADERA_MIXER_ROUTES("LHPF3", "LHPF3"), + MADERA_MIXER_ROUTES("LHPF4", "LHPF4"), + + MADERA_MUX_ROUTES("ASRC1IN1L", "ASRC1IN1L"), + MADERA_MUX_ROUTES("ASRC1IN1R", "ASRC1IN1R"), + MADERA_MUX_ROUTES("ASRC1IN2L", "ASRC1IN2L"), + MADERA_MUX_ROUTES("ASRC1IN2R", "ASRC1IN2R"), + MADERA_MUX_ROUTES("ASRC2IN1L", "ASRC2IN1L"), + MADERA_MUX_ROUTES("ASRC2IN1R", "ASRC2IN1R"), + MADERA_MUX_ROUTES("ASRC2IN2L", "ASRC2IN2L"), + MADERA_MUX_ROUTES("ASRC2IN2R", "ASRC2IN2R"), + + MADERA_DSP_ROUTES("DSP1"), + MADERA_DSP_ROUTES("DSP2"), + MADERA_DSP_ROUTES("DSP3"), + MADERA_DSP_ROUTES("DSP4"), + MADERA_DSP_ROUTES("DSP5"), + MADERA_DSP_ROUTES("DSP6"), + MADERA_DSP_ROUTES("DSP7"), + + { "DSP Trigger Out", NULL, "DSP1 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP2 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP3 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP4 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP5 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP6 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP7 Trigger Output" }, + + { "DSP1 Trigger Output", "Switch", "DSP1" }, + { "DSP2 Trigger Output", "Switch", "DSP2" }, + { "DSP3 Trigger Output", "Switch", "DSP3" }, + { "DSP4 Trigger Output", "Switch", "DSP4" }, + { "DSP5 Trigger Output", "Switch", "DSP5" }, + { "DSP6 Trigger Output", "Switch", "DSP6" }, + { "DSP7 Trigger Output", "Switch", "DSP7" }, + + MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"), + MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"), + + MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"), + MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"), + + MADERA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"), + MADERA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"), + + MADERA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"), + MADERA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"), + + MADERA_MUX_ROUTES("ISRC4INT1", "ISRC4INT1"), + MADERA_MUX_ROUTES("ISRC4INT2", "ISRC4INT2"), + + MADERA_MUX_ROUTES("ISRC4DEC1", "ISRC4DEC1"), + MADERA_MUX_ROUTES("ISRC4DEC2", "ISRC4DEC2"), + + { "AEC1 Loopback", "HPOUT1L", "OUT1L" }, + { "AEC1 Loopback", "HPOUT1R", "OUT1R" }, + { "AEC2 Loopback", "HPOUT1L", "OUT1L" }, + { "AEC2 Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC1 Loopback", "HPOUT2L", "OUT2L" }, + { "AEC1 Loopback", "HPOUT2R", "OUT2R" }, + { "AEC2 Loopback", "HPOUT2L", "OUT2L" }, + { "AEC2 Loopback", "HPOUT2R", "OUT2R" }, + { "HPOUT2L", NULL, "OUT2L" }, + { "HPOUT2R", NULL, "OUT2R" }, + + { "AEC1 Loopback", "HPOUT3L", "OUT3L" }, + { "AEC1 Loopback", "HPOUT3R", "OUT3R" }, + { "AEC2 Loopback", "HPOUT3L", "OUT3L" }, + { "AEC2 Loopback", "HPOUT3R", "OUT3R" }, + { "HPOUT3L", NULL, "OUT3L" }, + { "HPOUT3R", NULL, "OUT3R" }, + + { "AEC1 Loopback", "SPKOUTL", "OUT4L" }, + { "AEC2 Loopback", "SPKOUTL", "OUT4L" }, + { "SPKOUTLN", NULL, "OUT4L" }, + { "SPKOUTLP", NULL, "OUT4L" }, + + { "AEC1 Loopback", "SPKOUTR", "OUT4R" }, + { "AEC2 Loopback", "SPKOUTR", "OUT4R" }, + { "SPKOUTRN", NULL, "OUT4R" }, + { "SPKOUTRP", NULL, "OUT4R" }, + + { "AEC1 Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC1 Loopback", "SPKDAT1R", "OUT5R" }, + { "AEC2 Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC2 Loopback", "SPKDAT1R", "OUT5R" }, + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + { "AEC1 Loopback", "SPKDAT2L", "OUT6L" }, + { "AEC1 Loopback", "SPKDAT2R", "OUT6R" }, + { "AEC2 Loopback", "SPKDAT2L", "OUT6L" }, + { "AEC2 Loopback", "SPKDAT2R", "OUT6R" }, + { "SPKDAT2L", NULL, "OUT6L" }, + { "SPKDAT2R", NULL, "OUT6R" }, + + CS47L85_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"), + CS47L85_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"), + + CS47L85_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT4L", "SPKOUTL"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT4R", "SPKOUTR"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT6L", "SPKDAT2L"), + CS47L85_RXANC_OUTPUT_ROUTES("OUT6R", "SPKDAT2R"), + + { "SPDIF1", NULL, "SPD1" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" }, + { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" }, + { "DRC1 Activity Output", "Switch", "DRC1L" }, + { "DRC1 Activity Output", "Switch", "DRC1R" }, + { "DRC2 Activity Output", "Switch", "DRC2L" }, + { "DRC2 Activity Output", "Switch", "DRC2R" }, +}; + +static int cs47l85_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case MADERA_FLL1_REFCLK: + return madera_set_fll_refclk(&cs47l85->fll[0], source, fref, + fout); + case MADERA_FLL2_REFCLK: + return madera_set_fll_refclk(&cs47l85->fll[1], source, fref, + fout); + case MADERA_FLL3_REFCLK: + return madera_set_fll_refclk(&cs47l85->fll[2], source, fref, + fout); + case MADERA_FLL1_SYNCCLK: + return madera_set_fll_syncclk(&cs47l85->fll[0], source, fref, + fout); + case MADERA_FLL2_SYNCCLK: + return madera_set_fll_syncclk(&cs47l85->fll[1], source, fref, + fout); + case MADERA_FLL3_SYNCCLK: + return madera_set_fll_syncclk(&cs47l85->fll[2], source, fref, + fout); + default: + return -EINVAL; + } +} + +static const struct snd_soc_dai_ops cs47l85_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + +static struct snd_soc_dai_driver cs47l85_dai[] = { + { + .name = "cs47l85-aif1", + .id = 1, + .base = MADERA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l85-aif2", + .id = 2, + .base = MADERA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l85-aif3", + .id = 3, + .base = MADERA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l85-aif4", + .id = 4, + .base = MADERA_AIF4_BCLK_CTRL, + .playback = { + .stream_name = "AIF4 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l85-slim1", + .id = 5, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l85-slim2", + .id = 6, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l85-slim3", + .id = 7, + .playback = { + .stream_name = "Slim3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l85-cpu-voicectrl", + .capture = { + .stream_name = "Voice Control CPU", + .channels_min = 1, + .channels_max = 1, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &cs47l85_dai_ops, + }, + { + .name = "cs47l85-dsp-voicectrl", + .capture = { + .stream_name = "Voice Control DSP", + .channels_min = 1, + .channels_max = 1, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + }, + { + .name = "cs47l85-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &cs47l85_dai_ops, + }, + { + .name = "cs47l85-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + }, +}; + +static int cs47l85_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l85->core; + struct madera *madera = priv->madera; + int n_adsp; + + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l85-dsp-voicectrl") == 0) { + n_adsp = 5; + } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l85-dsp-trace") == 0) { + n_adsp = 0; + } else { + dev_err(madera->dev, + "No suitable compressed stream for DAI '%s'\n", + asoc_rtd_to_codec(rtd, 0)->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&priv->adsp[n_adsp], stream); +} + +static irqreturn_t cs47l85_adsp2_irq(int irq, void *data) +{ + struct cs47l85 *cs47l85 = data; + struct madera_priv *priv = &cs47l85->core; + struct madera *madera = priv->madera; + struct madera_voice_trigger_info trig_info; + int serviced = 0; + int i, ret; + + for (i = 0; i < CS47L85_NUM_ADSP; ++i) { + ret = wm_adsp_compr_handle_irq(&priv->adsp[i]); + if (ret != -ENODEV) + serviced++; + if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) { + trig_info.core_num = i + 1; + blocking_notifier_call_chain(&madera->notifier, + MADERA_NOTIFY_VOICE_TRIGGER, + &trig_info); + } + } + + if (!serviced) { + dev_err(madera->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int cs47l85_component_probe(struct snd_soc_component *component) +{ + struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l85->core.madera; + int i, ret; + + snd_soc_component_init_regmap(component, madera->regmap); + + mutex_lock(&madera->dapm_ptr_lock); + madera->dapm = snd_soc_component_get_dapm(component); + mutex_unlock(&madera->dapm_ptr_lock); + + ret = madera_init_inputs(component); + if (ret) + return ret; + + ret = madera_init_outputs(component, NULL, CS47L85_MONO_OUTPUTS, + CS47L85_MONO_OUTPUTS); + if (ret) + return ret; + + snd_soc_component_disable_pin(component, "HAPTICS"); + + ret = snd_soc_add_component_controls(component, + madera_adsp_rate_controls, + CS47L85_NUM_ADSP); + if (ret) + return ret; + + for (i = 0; i < CS47L85_NUM_ADSP; i++) + wm_adsp2_component_probe(&cs47l85->core.adsp[i], component); + + return 0; +} + +static void cs47l85_component_remove(struct snd_soc_component *component) +{ + struct cs47l85 *cs47l85 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l85->core.madera; + int i; + + mutex_lock(&madera->dapm_ptr_lock); + madera->dapm = NULL; + mutex_unlock(&madera->dapm_ptr_lock); + + for (i = 0; i < CS47L85_NUM_ADSP; i++) + wm_adsp2_component_remove(&cs47l85->core.adsp[i], component); +} + +#define MADERA_DIG_VU 0x0200 + +static const unsigned int cs47l85_digital_vu[] = { + MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, + MADERA_DAC_DIGITAL_VOLUME_2L, + MADERA_DAC_DIGITAL_VOLUME_2R, + MADERA_DAC_DIGITAL_VOLUME_3L, + MADERA_DAC_DIGITAL_VOLUME_3R, + MADERA_DAC_DIGITAL_VOLUME_4L, + MADERA_DAC_DIGITAL_VOLUME_4R, + MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, + MADERA_DAC_DIGITAL_VOLUME_6L, + MADERA_DAC_DIGITAL_VOLUME_6R, +}; + +static const struct snd_compress_ops cs47l85_compress_ops = { + .open = &cs47l85_open, + .free = &wm_adsp_compr_free, + .set_params = &wm_adsp_compr_set_params, + .get_caps = &wm_adsp_compr_get_caps, + .trigger = &wm_adsp_compr_trigger, + .pointer = &wm_adsp_compr_pointer, + .copy = &wm_adsp_compr_copy, +}; + +static const struct snd_soc_component_driver soc_component_dev_cs47l85 = { + .probe = &cs47l85_component_probe, + .remove = &cs47l85_component_remove, + .set_sysclk = &madera_set_sysclk, + .set_pll = &cs47l85_set_fll, + .name = DRV_NAME, + .compress_ops = &cs47l85_compress_ops, + .controls = cs47l85_snd_controls, + .num_controls = ARRAY_SIZE(cs47l85_snd_controls), + .dapm_widgets = cs47l85_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs47l85_dapm_widgets), + .dapm_routes = cs47l85_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs47l85_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cs47l85_probe(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + struct cs47l85 *cs47l85; + int i, ret; + + BUILD_BUG_ON(ARRAY_SIZE(cs47l85_dai) > MADERA_MAX_DAI); + + /* quick exit if Madera irqchip driver hasn't completed probe */ + if (!madera->irq_dev) { + dev_dbg(&pdev->dev, "irqchip driver not ready\n"); + return -EPROBE_DEFER; + } + + cs47l85 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l85), + GFP_KERNEL); + if (!cs47l85) + return -ENOMEM; + + platform_set_drvdata(pdev, cs47l85); + + cs47l85->core.madera = madera; + cs47l85->core.dev = &pdev->dev; + cs47l85->core.num_inputs = 12; + + ret = madera_core_init(&cs47l85->core); + if (ret) + return ret; + + ret = madera_init_overheat(&cs47l85->core); + if (ret) + goto error_core; + + ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", cs47l85_adsp2_irq, + cs47l85); + if (ret) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + goto error_overheat; + } + + ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1); + if (ret) + dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret); + + for (i = 0; i < CS47L85_NUM_ADSP; i++) { + cs47l85->core.adsp[i].part = "cs47l85"; + cs47l85->core.adsp[i].cs_dsp.num = i + 1; + cs47l85->core.adsp[i].cs_dsp.type = WMFW_ADSP2; + cs47l85->core.adsp[i].cs_dsp.rev = 1; + cs47l85->core.adsp[i].cs_dsp.dev = madera->dev; + cs47l85->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit; + + cs47l85->core.adsp[i].cs_dsp.base = wm_adsp2_control_bases[i]; + cs47l85->core.adsp[i].cs_dsp.mem = cs47l85_dsp_regions[i]; + cs47l85->core.adsp[i].cs_dsp.num_mems = + ARRAY_SIZE(cs47l85_dsp1_regions); + + ret = wm_adsp2_init(&cs47l85->core.adsp[i]); + if (ret) { + for (--i; i >= 0; --i) + wm_adsp2_remove(&cs47l85->core.adsp[i]); + goto error_dsp_irq; + } + } + + madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1, + &cs47l85->fll[0]); + madera_init_fll(madera, 2, MADERA_FLL2_CONTROL_1 - 1, + &cs47l85->fll[1]); + madera_init_fll(madera, 3, MADERA_FLL3_CONTROL_1 - 1, + &cs47l85->fll[2]); + + for (i = 0; i < ARRAY_SIZE(cs47l85_dai); i++) + madera_init_dai(&cs47l85->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(cs47l85_digital_vu); i++) + regmap_update_bits(madera->regmap, cs47l85_digital_vu[i], + MADERA_DIG_VU, MADERA_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_cs47l85, + cs47l85_dai, + ARRAY_SIZE(cs47l85_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto error_pm_runtime; + } + + return ret; + +error_pm_runtime: + pm_runtime_disable(&pdev->dev); + + for (i = 0; i < CS47L85_NUM_ADSP; i++) + wm_adsp2_remove(&cs47l85->core.adsp[i]); +error_dsp_irq: + madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0); + madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l85); +error_overheat: + madera_free_overheat(&cs47l85->core); +error_core: + madera_core_free(&cs47l85->core); + + return ret; +} + +static void cs47l85_remove(struct platform_device *pdev) +{ + struct cs47l85 *cs47l85 = platform_get_drvdata(pdev); + int i; + + pm_runtime_disable(&pdev->dev); + + for (i = 0; i < CS47L85_NUM_ADSP; i++) + wm_adsp2_remove(&cs47l85->core.adsp[i]); + + madera_set_irq_wake(cs47l85->core.madera, MADERA_IRQ_DSP_IRQ1, 0); + madera_free_irq(cs47l85->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l85); + madera_free_overheat(&cs47l85->core); + madera_core_free(&cs47l85->core); +} + +static struct platform_driver cs47l85_codec_driver = { + .driver = { + .name = "cs47l85-codec", + }, + .probe = &cs47l85_probe, + .remove_new = cs47l85_remove, +}; + +module_platform_driver(cs47l85_codec_driver); + +MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp"); +MODULE_DESCRIPTION("ASoC CS47L85 driver"); +MODULE_AUTHOR("Nariman Poushin "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cs47l85-codec"); diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c new file mode 100644 index 0000000000..2c9a5372cf --- /dev/null +++ b/sound/soc/codecs/cs47l90.c @@ -0,0 +1,2656 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// ALSA SoC Audio driver for CS47L90 codec +// +// Copyright (C) 2015-2019 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "madera.h" +#include "wm_adsp.h" + +#define DRV_NAME "cs47l90-codec" + +#define CS47L90_NUM_ADSP 7 +#define CS47L90_MONO_OUTPUTS 3 + +struct cs47l90 { + struct madera_priv core; + struct madera_fll fll[3]; +}; + +static const struct cs_dsp_region cs47l90_dsp1_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x080000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x0a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x0c0000 }, +}; + +static const struct cs_dsp_region cs47l90_dsp2_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x100000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x160000 }, + { .type = WMFW_ADSP2_XM, .base = 0x120000 }, + { .type = WMFW_ADSP2_YM, .base = 0x140000 }, +}; + +static const struct cs_dsp_region cs47l90_dsp3_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x180000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x1e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x1a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x1c0000 }, +}; + +static const struct cs_dsp_region cs47l90_dsp4_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x200000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x260000 }, + { .type = WMFW_ADSP2_XM, .base = 0x220000 }, + { .type = WMFW_ADSP2_YM, .base = 0x240000 }, +}; + +static const struct cs_dsp_region cs47l90_dsp5_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x280000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x2e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x2a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x2c0000 }, +}; + +static const struct cs_dsp_region cs47l90_dsp6_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x300000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x360000 }, + { .type = WMFW_ADSP2_XM, .base = 0x320000 }, + { .type = WMFW_ADSP2_YM, .base = 0x340000 }, +}; + +static const struct cs_dsp_region cs47l90_dsp7_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x380000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x3e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x3a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x3c0000 }, +}; + +static const struct cs_dsp_region *cs47l90_dsp_regions[] = { + cs47l90_dsp1_regions, + cs47l90_dsp2_regions, + cs47l90_dsp3_regions, + cs47l90_dsp4_regions, + cs47l90_dsp5_regions, + cs47l90_dsp6_regions, + cs47l90_dsp7_regions, +}; + +static const int cs47l90_dsp_control_bases[] = { + MADERA_DSP1_CONFIG_1, + MADERA_DSP2_CONFIG_1, + MADERA_DSP3_CONFIG_1, + MADERA_DSP4_CONFIG_1, + MADERA_DSP5_CONFIG_1, + MADERA_DSP6_CONFIG_1, + MADERA_DSP7_CONFIG_1, +}; + +static int cs47l90_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l90->core; + struct madera *madera = priv->madera; + unsigned int freq; + int ret; + + ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_2, &freq); + if (ret != 0) { + dev_err(madera->dev, + "Failed to read MADERA_DSP_CLOCK_2: %d\n", ret); + return ret; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = madera_set_adsp_clk(&cs47l90->core, w->shift, freq); + if (ret) + return ret; + break; + default: + break; + } + + return wm_adsp_early_event(w, kcontrol, event); +} + +#define CS47L90_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + +#define CS47L90_RXANC_INPUT_ROUTES(widget, name) \ + { widget, NULL, name " NG Mux" }, \ + { name " NG Internal", NULL, "RXANC NG Clock" }, \ + { name " NG Internal", NULL, name " Channel" }, \ + { name " NG External", NULL, "RXANC NG External Clock" }, \ + { name " NG External", NULL, name " Channel" }, \ + { name " NG Mux", "None", name " Channel" }, \ + { name " NG Mux", "Internal", name " NG Internal" }, \ + { name " NG Mux", "External", name " NG External" }, \ + { name " Channel", "Left", name " Left Input" }, \ + { name " Channel", "Combine", name " Left Input" }, \ + { name " Channel", "Right", name " Right Input" }, \ + { name " Channel", "Combine", name " Right Input" }, \ + { name " Left Input", "IN1", "IN1L" }, \ + { name " Right Input", "IN1", "IN1R" }, \ + { name " Left Input", "IN2", "IN2L" }, \ + { name " Right Input", "IN2", "IN2R" }, \ + { name " Left Input", "IN3", "IN3L" }, \ + { name " Right Input", "IN3", "IN3R" }, \ + { name " Left Input", "IN4", "IN4L" }, \ + { name " Right Input", "IN4", "IN4R" }, \ + { name " Left Input", "IN5", "IN5L" }, \ + { name " Right Input", "IN5", "IN5R" } + +#define CS47L90_RXANC_OUTPUT_ROUTES(widget, name) \ + { widget, NULL, name " ANC Source" }, \ + { name " ANC Source", "RXANCL", "RXANCL" }, \ + { name " ANC Source", "RXANCR", "RXANCR" } + +static const struct snd_kcontrol_new cs47l90_snd_controls[] = { +SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]), +SOC_ENUM("IN3 OSR", madera_in_dmic_osr[2]), +SOC_ENUM("IN4 OSR", madera_in_dmic_osr[3]), +SOC_ENUM("IN5 OSR", madera_in_dmic_osr[4]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL, + MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL, + MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL, + MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL, + MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum), + +SOC_SINGLE_EXT("IN1L LP Switch", MADERA_ADC_DIGITAL_VOLUME_1L, + MADERA_IN1L_LP_MODE_SHIFT, 1, 0, + snd_soc_get_volsw, madera_lp_mode_put), +SOC_SINGLE_EXT("IN1R LP Switch", MADERA_ADC_DIGITAL_VOLUME_1R, + MADERA_IN1R_LP_MODE_SHIFT, 1, 0, + snd_soc_get_volsw, madera_lp_mode_put), +SOC_SINGLE_EXT("IN2L LP Switch", MADERA_ADC_DIGITAL_VOLUME_2L, + MADERA_IN2L_LP_MODE_SHIFT, 1, 0, + snd_soc_get_volsw, madera_lp_mode_put), +SOC_SINGLE_EXT("IN2R LP Switch", MADERA_ADC_DIGITAL_VOLUME_2R, + MADERA_IN2R_LP_MODE_SHIFT, 1, 0, + snd_soc_get_volsw, madera_lp_mode_put), + +SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL, + MADERA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL, + MADERA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL, + MADERA_IN2L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL, + MADERA_IN2R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN3L HPF Switch", MADERA_IN3L_CONTROL, + MADERA_IN3L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN3R HPF Switch", MADERA_IN3R_CONTROL, + MADERA_IN3R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN4L HPF Switch", MADERA_IN4L_CONTROL, + MADERA_IN4L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN4R HPF Switch", MADERA_IN4R_CONTROL, + MADERA_IN4R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN5L HPF Switch", MADERA_IN5L_CONTROL, + MADERA_IN5L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN5R HPF Switch", MADERA_IN5R_CONTROL, + MADERA_IN5R_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L, + MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R, + MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L, + MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R, + MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3L, + MADERA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3R, + MADERA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN4L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4L, + MADERA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN4R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4R, + MADERA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN5L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5L, + MADERA_IN5L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN5R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_5R, + MADERA_IN5R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), + +SOC_ENUM("Input Ramp Up", madera_in_vi_ramp), +SOC_ENUM("Input Ramp Down", madera_in_vd_ramp), + +SND_SOC_BYTES("RXANC Coefficients", MADERA_ANC_COEFF_START, + MADERA_ANC_COEFF_END - MADERA_ANC_COEFF_START + 1), +SND_SOC_BYTES("RXANCL Config", MADERA_FCL_FILTER_CONTROL, 1), +SND_SOC_BYTES("RXANCL Coefficients", MADERA_FCL_COEFF_START, + MADERA_FCL_COEFF_END - MADERA_FCL_COEFF_START + 1), +SND_SOC_BYTES("RXANCR Config", MADERA_FCR_FILTER_CONTROL, 1), +SND_SOC_BYTES("RXANCR Coefficients", MADERA_FCR_COEFF_START, + MADERA_FCR_COEFF_END - MADERA_FCR_COEFF_START + 1), + +MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE), + +MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2), +SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2), +SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5, + MADERA_DRC1R_ENA | MADERA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5, + MADERA_DRC2R_ENA | MADERA_DRC2L_ENA), + +MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE), + +MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2), +MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2), +MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2), +MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2), + +SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode), + +MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]), +MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]), +MADERA_RATE_ENUM("ISRC3 FSL", madera_isrc_fsl[2]), +MADERA_RATE_ENUM("ISRC4 FSL", madera_isrc_fsl[3]), +MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]), +MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]), +MADERA_RATE_ENUM("ISRC3 FSH", madera_isrc_fsh[2]), +MADERA_RATE_ENUM("ISRC4 FSH", madera_isrc_fsh[3]), +MADERA_RATE_ENUM("ASRC1 Rate 1", madera_asrc1_rate[0]), +MADERA_RATE_ENUM("ASRC1 Rate 2", madera_asrc1_rate[1]), +MADERA_RATE_ENUM("ASRC2 Rate 1", madera_asrc2_rate[0]), +MADERA_RATE_ENUM("ASRC2 Rate 2", madera_asrc2_rate[1]), + +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), +WM_ADSP2_PRELOAD_SWITCH("DSP2", 2), +WM_ADSP2_PRELOAD_SWITCH("DSP3", 3), +WM_ADSP2_PRELOAD_SWITCH("DSP4", 4), +WM_ADSP2_PRELOAD_SWITCH("DSP5", 5), +WM_ADSP2_PRELOAD_SWITCH("DSP6", 6), +WM_ADSP2_PRELOAD_SWITCH("DSP7", 7), + +MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP2L", MADERA_DSP2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP2R", MADERA_DSP2RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP3L", MADERA_DSP3LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP3R", MADERA_DSP3RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP4L", MADERA_DSP4LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP4R", MADERA_DSP4RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP5L", MADERA_DSP5LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP5R", MADERA_DSP5RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP6L", MADERA_DSP6LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP6R", MADERA_DSP6RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP7L", MADERA_DSP7LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP7R", MADERA_DSP7RMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR, + MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv), + +MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT2L", MADERA_OUT2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT2R", MADERA_OUT2RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT3L", MADERA_OUT3LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT3R", MADERA_OUT3RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL, + MADERA_HP1_SC_ENA_SHIFT, 1, 0), +SOC_SINGLE("HPOUT2 SC Protect Switch", MADERA_HP2_SHORT_CIRCUIT_CTRL, + MADERA_HP2_SC_ENA_SHIFT, 1, 0), +SOC_SINGLE("HPOUT3 SC Protect Switch", MADERA_HP3_SHORT_CIRCUIT_CTRL, + MADERA_HP3_SC_ENA_SHIFT, 1, 0), + +SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L, + MADERA_OUT5_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_2L, + MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT3 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_3L, + MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_2L, + MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_3L, + MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), + +SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT, + MADERA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_ENUM("Output Ramp Up", madera_out_vi_ramp), +SOC_ENUM("Output Ramp Down", madera_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv), +SOC_ENUM("Noise Gate Hold", madera_ng_hold), + +SOC_ENUM_EXT("DFC1RX Width", madera_dfc_width[0], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC1RX Type", madera_dfc_type[0], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC1TX Width", madera_dfc_width[1], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC1TX Type", madera_dfc_type[1], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC2RX Width", madera_dfc_width[2], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC2RX Type", madera_dfc_type[2], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC2TX Width", madera_dfc_width[3], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC2TX Type", madera_dfc_type[3], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC3RX Width", madera_dfc_width[4], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC3RX Type", madera_dfc_type[4], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC3TX Width", madera_dfc_width[5], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC3TX Type", madera_dfc_type[5], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC4RX Width", madera_dfc_width[6], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC4RX Type", madera_dfc_type[6], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC4TX Width", madera_dfc_width[7], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC4TX Type", madera_dfc_type[7], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC5RX Width", madera_dfc_width[8], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC5RX Type", madera_dfc_type[8], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC5TX Width", madera_dfc_width[9], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC5TX Type", madera_dfc_type[9], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC6RX Width", madera_dfc_width[10], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC6RX Type", madera_dfc_type[10], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC6TX Width", madera_dfc_width[11], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC6TX Type", madera_dfc_type[11], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC7RX Width", madera_dfc_width[12], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC7RX Type", madera_dfc_type[12], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC7TX Width", madera_dfc_width[13], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC7TX Type", madera_dfc_type[13], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC8RX Width", madera_dfc_width[14], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC8RX Type", madera_dfc_type[14], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC8TX Width", madera_dfc_width[15], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC8TX Type", madera_dfc_type[15], + snd_soc_get_enum_double, madera_dfc_put), + +CS47L90_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L), +CS47L90_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R), +CS47L90_NG_SRC("HPOUT2L", MADERA_NOISE_GATE_SELECT_2L), +CS47L90_NG_SRC("HPOUT2R", MADERA_NOISE_GATE_SELECT_2R), +CS47L90_NG_SRC("HPOUT3L", MADERA_NOISE_GATE_SELECT_3L), +CS47L90_NG_SRC("HPOUT3R", MADERA_NOISE_GATE_SELECT_3R), +CS47L90_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L), +CS47L90_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R), + +MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX7", MADERA_AIF1TX7MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX8", MADERA_AIF1TX8MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX5", MADERA_AIF2TX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX6", MADERA_AIF2TX6MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX7", MADERA_AIF2TX7MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX8", MADERA_AIF2TX8MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF4TX1", MADERA_AIF4TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF4TX2", MADERA_AIF4TX2MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX7", MADERA_SLIMTX7MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX8", MADERA_SLIMTX8MIX_INPUT_1_SOURCE), + +MADERA_GAINMUX_CONTROLS("SPDIF1TX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE), +MADERA_GAINMUX_CONTROLS("SPDIF1TX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), +WM_ADSP_FW_CONTROL("DSP2", 1), +WM_ADSP_FW_CONTROL("DSP3", 2), +WM_ADSP_FW_CONTROL("DSP4", 3), +WM_ADSP_FW_CONTROL("DSP5", 4), +WM_ADSP_FW_CONTROL("DSP6", 5), +WM_ADSP_FW_CONTROL("DSP7", 6), +}; + +MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP2L, MADERA_DSP2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP2R, MADERA_DSP2RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP2, MADERA_DSP2AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP3L, MADERA_DSP3LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP3R, MADERA_DSP3RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP3, MADERA_DSP3AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP4L, MADERA_DSP4LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP4R, MADERA_DSP4RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP4, MADERA_DSP4AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP5L, MADERA_DSP5LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP5R, MADERA_DSP5RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP5, MADERA_DSP5AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP6L, MADERA_DSP6LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP6R, MADERA_DSP6RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP6, MADERA_DSP6AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP7L, MADERA_DSP7LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP7R, MADERA_DSP7RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP7, MADERA_DSP7AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT2L, MADERA_OUT2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT2R, MADERA_OUT2RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT3L, MADERA_OUT3LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT3R, MADERA_OUT3RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX7, MADERA_AIF1TX7MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX8, MADERA_AIF1TX8MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX5, MADERA_AIF2TX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX6, MADERA_AIF2TX6MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX7, MADERA_AIF2TX7MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX8, MADERA_AIF2TX8MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF4TX1, MADERA_AIF4TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF4TX2, MADERA_AIF4TX2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX7, MADERA_SLIMTX7MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX8, MADERA_SLIMTX8MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ASRC1IN1L, MADERA_ASRC1_1LMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC1IN1R, MADERA_ASRC1_1RMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC1IN2L, MADERA_ASRC1_2LMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC1IN2R, MADERA_ASRC1_2RMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC2IN1L, MADERA_ASRC2_1LMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC2IN1R, MADERA_ASRC2_1RMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC2IN2L, MADERA_ASRC2_2LMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC2IN2R, MADERA_ASRC2_2RMIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT3, MADERA_ISRC1INT3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT4, MADERA_ISRC1INT4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC3, MADERA_ISRC1DEC3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC4, MADERA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT3, MADERA_ISRC2INT3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT4, MADERA_ISRC2INT4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC3, MADERA_ISRC2DEC3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC4, MADERA_ISRC2DEC4MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC3INT1, MADERA_ISRC3INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC3INT2, MADERA_ISRC3INT2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC3DEC1, MADERA_ISRC3DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC3DEC2, MADERA_ISRC3DEC2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC4INT1, MADERA_ISRC4INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC4INT2, MADERA_ISRC4INT2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC4DEC1, MADERA_ISRC4DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC4DEC2, MADERA_ISRC4DEC2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(DFC1, MADERA_DFC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC2, MADERA_DFC2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC3, MADERA_DFC3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC4, MADERA_DFC4MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC5, MADERA_DFC5MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC6, MADERA_DFC6MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC7, MADERA_DFC7MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC8, MADERA_DFC8MIX_INPUT_1_SOURCE); + +static const char * const cs47l90_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R", + "SPKDAT1L", "SPKDAT1R", +}; + +static const unsigned int cs47l90_aec_loopback_values[] = { + 0, 1, 2, 3, 4, 5, 8, 9, +}; + +static const struct soc_enum cs47l90_aec1_loopback = + SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1, + MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l90_aec_loopback_texts), + cs47l90_aec_loopback_texts, + cs47l90_aec_loopback_values); + +static const struct soc_enum cs47l90_aec2_loopback = + SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_2, + MADERA_AEC2_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l90_aec_loopback_texts), + cs47l90_aec_loopback_texts, + cs47l90_aec_loopback_values); + +static const struct snd_kcontrol_new cs47l90_aec_loopback_mux[] = { + SOC_DAPM_ENUM("AEC1 Loopback", cs47l90_aec1_loopback), + SOC_DAPM_ENUM("AEC2 Loopback", cs47l90_aec2_loopback), +}; + +static const struct snd_kcontrol_new cs47l90_anc_input_mux[] = { + SOC_DAPM_ENUM("RXANCL Input", madera_anc_input_src[0]), + SOC_DAPM_ENUM("RXANCL Channel", madera_anc_input_src[1]), + SOC_DAPM_ENUM("RXANCR Input", madera_anc_input_src[2]), + SOC_DAPM_ENUM("RXANCR Channel", madera_anc_input_src[3]), +}; + +static const struct snd_kcontrol_new cs47l90_anc_ng_mux = + SOC_DAPM_ENUM("RXANC NG Source", madera_anc_ng_enum); + +static const struct snd_kcontrol_new cs47l90_output_anc_src[] = { + SOC_DAPM_ENUM("HPOUT1L ANC Source", madera_output_anc_src[0]), + SOC_DAPM_ENUM("HPOUT1R ANC Source", madera_output_anc_src[1]), + SOC_DAPM_ENUM("HPOUT2L ANC Source", madera_output_anc_src[2]), + SOC_DAPM_ENUM("HPOUT2R ANC Source", madera_output_anc_src[3]), + SOC_DAPM_ENUM("HPOUT3L ANC Source", madera_output_anc_src[4]), + SOC_DAPM_ENUM("HPOUT3R ANC Source", madera_output_anc_src[0]), + SOC_DAPM_ENUM("SPKDAT1L ANC Source", madera_output_anc_src[8]), + SOC_DAPM_ENUM("SPKDAT1R ANC Source", madera_output_anc_src[9]), +}; + +static const struct snd_soc_dapm_widget cs47l90_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT, + 0, madera_sysclk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1, + MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK, + MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK, + MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT, + 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD4", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1C", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1C_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1D", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1D_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS2A", MADERA_MIC_BIAS_CTRL_6, + MADERA_MICB2A_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2B", MADERA_MIC_BIAS_CTRL_6, + MADERA_MICB2B_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2C", MADERA_MIC_BIAS_CTRL_6, + MADERA_MICB2C_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2D", MADERA_MIC_BIAS_CTRL_6, + MADERA_MICB2D_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_FX, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASRC1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ASRC1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASRC2CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ASRC2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC3CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC3, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC4CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC4, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_OUT, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_SPD, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP3CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP3, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP4CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP4, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP5CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP5, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP6CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP6, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP7CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP7, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF3, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF4TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF4, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_SLIMBUS, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_PWM, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DFCCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DFC, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), + +SND_SOC_DAPM_INPUT("IN1ALN"), +SND_SOC_DAPM_INPUT("IN1ALP"), +SND_SOC_DAPM_INPUT("IN1BLN"), +SND_SOC_DAPM_INPUT("IN1BLP"), +SND_SOC_DAPM_INPUT("IN1ARN"), +SND_SOC_DAPM_INPUT("IN1ARP"), +SND_SOC_DAPM_INPUT("IN1BRN"), +SND_SOC_DAPM_INPUT("IN1BRP"), +SND_SOC_DAPM_INPUT("IN2ALN"), +SND_SOC_DAPM_INPUT("IN2ALP"), +SND_SOC_DAPM_INPUT("IN2BLN"), +SND_SOC_DAPM_INPUT("IN2BLP"), +SND_SOC_DAPM_INPUT("IN2RN"), +SND_SOC_DAPM_INPUT("IN2RP"), +SND_SOC_DAPM_INPUT("DMICCLK3"), +SND_SOC_DAPM_INPUT("DMICDAT3"), +SND_SOC_DAPM_INPUT("DMICCLK4"), +SND_SOC_DAPM_INPUT("DMICDAT4"), +SND_SOC_DAPM_INPUT("DMICCLK5"), +SND_SOC_DAPM_INPUT("DMICDAT5"), + +SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]), +SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]), +SND_SOC_DAPM_MUX("IN2L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[2]), + +SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]), +SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]), + +SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), +SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_OUTPUT("DSP Trigger Out"), + +SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM, + MADERA_EXT_NG_SEL_SET_SHIFT, 0, madera_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM, + MADERA_CLK_NG_ENA_SET_SHIFT, 0, madera_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0, + &cs47l90_anc_input_mux[0]), +SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0, + &cs47l90_anc_input_mux[0]), +SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0, + &cs47l90_anc_input_mux[1]), +SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &cs47l90_anc_ng_mux), +SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0, + &cs47l90_anc_input_mux[2]), +SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0, + &cs47l90_anc_input_mux[2]), +SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0, + &cs47l90_anc_input_mux[3]), +SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &cs47l90_anc_ng_mux), + +SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, MADERA_CLK_L_ENA_SET_SHIFT, + 0, NULL, 0, madera_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, MADERA_CLK_R_ENA_SET_SHIFT, + 0, NULL, 0, madera_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l90_output_anc_src[0]), +SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l90_output_anc_src[1]), +SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l90_output_anc_src[2]), +SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l90_output_anc_src[3]), +SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l90_output_anc_src[4]), +SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l90_output_anc_src[5]), +SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l90_output_anc_src[6]), +SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0, + &cs47l90_output_anc_src[7]), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX7", NULL, 6, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX8", NULL, 7, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF4TX1", NULL, 0, + MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF4TX2", NULL, 1, + MADERA_AIF4_TX_ENABLES, MADERA_AIF4TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", SND_SOC_NOPM, + MADERA_OUT2L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", SND_SOC_NOPM, + MADERA_OUT2R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", SND_SOC_NOPM, + MADERA_OUT3L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3R", SND_SOC_NOPM, + MADERA_OUT3R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_ENA_SHIFT, 0, NULL, 0), + +/* + * mux_in widgets : arranged in the order of sources + * specified in MADERA_MIXER_INPUT_ROUTES + */ + +SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR, + MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1, + MADERA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1, + MADERA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1, + MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0, + &cs47l90_aec_loopback_mux[0]), +SND_SOC_DAPM_MUX("AEC2 Loopback", MADERA_DAC_AEC_CONTROL_2, + MADERA_AEC2_LOOPBACK_ENA_SHIFT, 0, + &cs47l90_aec_loopback_mux[1]), + +SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3L", MADERA_INPUT_ENABLES, MADERA_IN3L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3R", MADERA_INPUT_ENABLES, MADERA_IN3R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4L", MADERA_INPUT_ENABLES, MADERA_IN4L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4R", MADERA_INPUT_ENABLES, MADERA_IN4R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN5L", MADERA_INPUT_ENABLES, MADERA_IN5L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN5R", MADERA_INPUT_ENABLES, MADERA_IN5R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX7", NULL, 6, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX8", NULL, 7, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF4RX1", NULL, 0, + MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF4RX2", NULL, 1, + MADERA_AIF4_RX_ENABLES, MADERA_AIF4RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("ASRC1IN1L", MADERA_ASRC1_ENABLE, + MADERA_ASRC1_IN1L_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC1IN1R", MADERA_ASRC1_ENABLE, + MADERA_ASRC1_IN1R_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC1IN2L", MADERA_ASRC1_ENABLE, + MADERA_ASRC1_IN2L_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC1IN2R", MADERA_ASRC1_ENABLE, + MADERA_ASRC1_IN2R_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ASRC2IN1L", MADERA_ASRC2_ENABLE, + MADERA_ASRC2_IN1L_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC2IN1R", MADERA_ASRC2_ENABLE, + MADERA_ASRC2_IN1R_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC2IN2L", MADERA_ASRC2_ENABLE, + MADERA_ASRC2_IN2L_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC2IN2R", MADERA_ASRC2_ENABLE, + MADERA_ASRC2_IN2R_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC3", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC4", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT3", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT4", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3DEC1", MADERA_ISRC_3_CTRL_3, + MADERA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC2", MADERA_ISRC_3_CTRL_3, + MADERA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3INT1", MADERA_ISRC_3_CTRL_3, + MADERA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT2", MADERA_ISRC_3_CTRL_3, + MADERA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC4DEC1", MADERA_ISRC_4_CTRL_3, + MADERA_ISRC4_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC4DEC2", MADERA_ISRC_4_CTRL_3, + MADERA_ISRC4_DEC2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC4INT1", MADERA_ISRC_4_CTRL_3, + MADERA_ISRC4_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC4INT2", MADERA_ISRC_4_CTRL_3, + MADERA_ISRC4_INT2_ENA_SHIFT, 0, NULL, 0), + +WM_ADSP2("DSP1", 0, cs47l90_adsp_power_ev), +WM_ADSP2("DSP2", 1, cs47l90_adsp_power_ev), +WM_ADSP2("DSP3", 2, cs47l90_adsp_power_ev), +WM_ADSP2("DSP4", 3, cs47l90_adsp_power_ev), +WM_ADSP2("DSP5", 4, cs47l90_adsp_power_ev), +WM_ADSP2("DSP6", 5, cs47l90_adsp_power_ev), +WM_ADSP2("DSP7", 6, cs47l90_adsp_power_ev), + +/* end of ordered widget list */ + +SND_SOC_DAPM_PGA("DFC1", MADERA_DFC1_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC2", MADERA_DFC2_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC3", MADERA_DFC3_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC4", MADERA_DFC4_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC5", MADERA_DFC5_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC6", MADERA_DFC6_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC7", MADERA_DFC7_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC8", MADERA_DFC8_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), + +MADERA_MIXER_WIDGETS(EQ1, "EQ1"), +MADERA_MIXER_WIDGETS(EQ2, "EQ2"), +MADERA_MIXER_WIDGETS(EQ3, "EQ3"), +MADERA_MIXER_WIDGETS(EQ4, "EQ4"), + +MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"), +MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"), +MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"), +MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0, + &madera_drc_activity_output_mux[0]), +SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0, + &madera_drc_activity_output_mux[1]), + +MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"), +MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"), +MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"), +MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +MADERA_MIXER_WIDGETS(PWM1, "PWM1"), +MADERA_MIXER_WIDGETS(PWM2, "PWM2"), + +MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +MADERA_MIXER_WIDGETS(OUT2L, "HPOUT2L"), +MADERA_MIXER_WIDGETS(OUT2R, "HPOUT2R"), +MADERA_MIXER_WIDGETS(OUT3L, "HPOUT3L"), +MADERA_MIXER_WIDGETS(OUT3R, "HPOUT3R"), +MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), + +MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +MADERA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +MADERA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +MADERA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +MADERA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), +MADERA_MIXER_WIDGETS(AIF2TX7, "AIF2TX7"), +MADERA_MIXER_WIDGETS(AIF2TX8, "AIF2TX8"), + +MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +MADERA_MIXER_WIDGETS(AIF4TX1, "AIF4TX1"), +MADERA_MIXER_WIDGETS(AIF4TX2, "AIF4TX2"), + +MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), +MADERA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"), +MADERA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"), + +MADERA_MUX_WIDGETS(SPD1TX1, "SPDIF1TX1"), +MADERA_MUX_WIDGETS(SPD1TX2, "SPDIF1TX2"), + +MADERA_MUX_WIDGETS(ASRC1IN1L, "ASRC1IN1L"), +MADERA_MUX_WIDGETS(ASRC1IN1R, "ASRC1IN1R"), +MADERA_MUX_WIDGETS(ASRC1IN2L, "ASRC1IN2L"), +MADERA_MUX_WIDGETS(ASRC1IN2R, "ASRC1IN2R"), +MADERA_MUX_WIDGETS(ASRC2IN1L, "ASRC2IN1L"), +MADERA_MUX_WIDGETS(ASRC2IN1R, "ASRC2IN1R"), +MADERA_MUX_WIDGETS(ASRC2IN2L, "ASRC2IN2L"), +MADERA_MUX_WIDGETS(ASRC2IN2R, "ASRC2IN2R"), + +MADERA_DSP_WIDGETS(DSP1, "DSP1"), +MADERA_DSP_WIDGETS(DSP2, "DSP2"), +MADERA_DSP_WIDGETS(DSP3, "DSP3"), +MADERA_DSP_WIDGETS(DSP4, "DSP4"), +MADERA_DSP_WIDGETS(DSP5, "DSP5"), +MADERA_DSP_WIDGETS(DSP6, "DSP6"), +MADERA_DSP_WIDGETS(DSP7, "DSP7"), + +SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[0]), +SND_SOC_DAPM_SWITCH("DSP2 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[1]), +SND_SOC_DAPM_SWITCH("DSP3 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[2]), +SND_SOC_DAPM_SWITCH("DSP4 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[3]), +SND_SOC_DAPM_SWITCH("DSP5 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[4]), +SND_SOC_DAPM_SWITCH("DSP6 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[5]), +SND_SOC_DAPM_SWITCH("DSP7 Trigger Output", SND_SOC_NOPM, 0, 0, + &madera_dsp_trigger_output_mux[6]), + +MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +MADERA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +MADERA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +MADERA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +MADERA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), +MADERA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"), +MADERA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"), + +MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), +MADERA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"), +MADERA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"), + +MADERA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"), +MADERA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"), + +MADERA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"), +MADERA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"), + +MADERA_MUX_WIDGETS(ISRC4DEC1, "ISRC4DEC1"), +MADERA_MUX_WIDGETS(ISRC4DEC2, "ISRC4DEC2"), + +MADERA_MUX_WIDGETS(ISRC4INT1, "ISRC4INT1"), +MADERA_MUX_WIDGETS(ISRC4INT2, "ISRC4INT2"), + +MADERA_MUX_WIDGETS(DFC1, "DFC1"), +MADERA_MUX_WIDGETS(DFC2, "DFC2"), +MADERA_MUX_WIDGETS(DFC3, "DFC3"), +MADERA_MUX_WIDGETS(DFC4, "DFC4"), +MADERA_MUX_WIDGETS(DFC5, "DFC5"), +MADERA_MUX_WIDGETS(DFC6, "DFC6"), +MADERA_MUX_WIDGETS(DFC7, "DFC7"), +MADERA_MUX_WIDGETS(DFC8, "DFC8"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2L"), +SND_SOC_DAPM_OUTPUT("HPOUT2R"), +SND_SOC_DAPM_OUTPUT("HPOUT3L"), +SND_SOC_DAPM_OUTPUT("HPOUT3R"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), +SND_SOC_DAPM_OUTPUT("SPDIF1"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define MADERA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC1", "AEC1 Loopback" }, \ + { name, "AEC2", "AEC2 Loopback" }, \ + { name, "IN1L", "IN1L" }, \ + { name, "IN1R", "IN1R" }, \ + { name, "IN2L", "IN2L" }, \ + { name, "IN2R", "IN2R" }, \ + { name, "IN3L", "IN3L" }, \ + { name, "IN3R", "IN3R" }, \ + { name, "IN4L", "IN4L" }, \ + { name, "IN4R", "IN4R" }, \ + { name, "IN5L", "IN5L" }, \ + { name, "IN5R", "IN5R" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF2RX7", "AIF2RX7" }, \ + { name, "AIF2RX8", "AIF2RX8" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "AIF4RX1", "AIF4RX1" }, \ + { name, "AIF4RX2", "AIF4RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "SLIMRX7", "SLIMRX7" }, \ + { name, "SLIMRX8", "SLIMRX8" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1IN1L", "ASRC1IN1L" }, \ + { name, "ASRC1IN1R", "ASRC1IN1R" }, \ + { name, "ASRC1IN2L", "ASRC1IN2L" }, \ + { name, "ASRC1IN2R", "ASRC1IN2R" }, \ + { name, "ASRC2IN1L", "ASRC2IN1L" }, \ + { name, "ASRC2IN1R", "ASRC2IN1R" }, \ + { name, "ASRC2IN2L", "ASRC2IN2L" }, \ + { name, "ASRC2IN2R", "ASRC2IN2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2DEC3", "ISRC2DEC3" }, \ + { name, "ISRC2DEC4", "ISRC2DEC4" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC2INT3", "ISRC2INT3" }, \ + { name, "ISRC2INT4", "ISRC2INT4" }, \ + { name, "ISRC3DEC1", "ISRC3DEC1" }, \ + { name, "ISRC3DEC2", "ISRC3DEC2" }, \ + { name, "ISRC3INT1", "ISRC3INT1" }, \ + { name, "ISRC3INT2", "ISRC3INT2" }, \ + { name, "ISRC4DEC1", "ISRC4DEC1" }, \ + { name, "ISRC4DEC2", "ISRC4DEC2" }, \ + { name, "ISRC4INT1", "ISRC4INT1" }, \ + { name, "ISRC4INT2", "ISRC4INT2" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" }, \ + { name, "DSP2.1", "DSP2" }, \ + { name, "DSP2.2", "DSP2" }, \ + { name, "DSP2.3", "DSP2" }, \ + { name, "DSP2.4", "DSP2" }, \ + { name, "DSP2.5", "DSP2" }, \ + { name, "DSP2.6", "DSP2" }, \ + { name, "DSP3.1", "DSP3" }, \ + { name, "DSP3.2", "DSP3" }, \ + { name, "DSP3.3", "DSP3" }, \ + { name, "DSP3.4", "DSP3" }, \ + { name, "DSP3.5", "DSP3" }, \ + { name, "DSP3.6", "DSP3" }, \ + { name, "DSP4.1", "DSP4" }, \ + { name, "DSP4.2", "DSP4" }, \ + { name, "DSP4.3", "DSP4" }, \ + { name, "DSP4.4", "DSP4" }, \ + { name, "DSP4.5", "DSP4" }, \ + { name, "DSP4.6", "DSP4" }, \ + { name, "DSP5.1", "DSP5" }, \ + { name, "DSP5.2", "DSP5" }, \ + { name, "DSP5.3", "DSP5" }, \ + { name, "DSP5.4", "DSP5" }, \ + { name, "DSP5.5", "DSP5" }, \ + { name, "DSP5.6", "DSP5" }, \ + { name, "DSP6.1", "DSP6" }, \ + { name, "DSP6.2", "DSP6" }, \ + { name, "DSP6.3", "DSP6" }, \ + { name, "DSP6.4", "DSP6" }, \ + { name, "DSP6.5", "DSP6" }, \ + { name, "DSP6.6", "DSP6" }, \ + { name, "DSP7.1", "DSP7" }, \ + { name, "DSP7.2", "DSP7" }, \ + { name, "DSP7.3", "DSP7" }, \ + { name, "DSP7.4", "DSP7" }, \ + { name, "DSP7.5", "DSP7" }, \ + { name, "DSP7.6", "DSP7" }, \ + { name, "DFC1", "DFC1" }, \ + { name, "DFC2", "DFC2" }, \ + { name, "DFC3", "DFC3" }, \ + { name, "DFC4", "DFC4" }, \ + { name, "DFC5", "DFC5" }, \ + { name, "DFC6", "DFC6" }, \ + { name, "DFC7", "DFC7" }, \ + { name, "DFC8", "DFC8" } + +static const struct snd_soc_dapm_route cs47l90_dapm_routes[] = { + /* Internal clock domains */ + { "EQ1", NULL, "FXCLK" }, + { "EQ2", NULL, "FXCLK" }, + { "EQ3", NULL, "FXCLK" }, + { "EQ4", NULL, "FXCLK" }, + { "DRC1L", NULL, "FXCLK" }, + { "DRC1R", NULL, "FXCLK" }, + { "DRC2L", NULL, "FXCLK" }, + { "DRC2R", NULL, "FXCLK" }, + { "LHPF1", NULL, "FXCLK" }, + { "LHPF2", NULL, "FXCLK" }, + { "LHPF3", NULL, "FXCLK" }, + { "LHPF4", NULL, "FXCLK" }, + { "PWM1 Mixer", NULL, "PWMCLK" }, + { "PWM2 Mixer", NULL, "PWMCLK" }, + { "OUT1L", NULL, "OUTCLK" }, + { "OUT1R", NULL, "OUTCLK" }, + { "OUT2L", NULL, "OUTCLK" }, + { "OUT2R", NULL, "OUTCLK" }, + { "OUT3L", NULL, "OUTCLK" }, + { "OUT3R", NULL, "OUTCLK" }, + { "OUT5L", NULL, "OUTCLK" }, + { "OUT5R", NULL, "OUTCLK" }, + { "AIF1TX1", NULL, "AIF1TXCLK" }, + { "AIF1TX2", NULL, "AIF1TXCLK" }, + { "AIF1TX3", NULL, "AIF1TXCLK" }, + { "AIF1TX4", NULL, "AIF1TXCLK" }, + { "AIF1TX5", NULL, "AIF1TXCLK" }, + { "AIF1TX6", NULL, "AIF1TXCLK" }, + { "AIF1TX7", NULL, "AIF1TXCLK" }, + { "AIF1TX8", NULL, "AIF1TXCLK" }, + { "AIF2TX1", NULL, "AIF2TXCLK" }, + { "AIF2TX2", NULL, "AIF2TXCLK" }, + { "AIF2TX3", NULL, "AIF2TXCLK" }, + { "AIF2TX4", NULL, "AIF2TXCLK" }, + { "AIF2TX5", NULL, "AIF2TXCLK" }, + { "AIF2TX6", NULL, "AIF2TXCLK" }, + { "AIF2TX7", NULL, "AIF2TXCLK" }, + { "AIF2TX8", NULL, "AIF2TXCLK" }, + { "AIF3TX1", NULL, "AIF3TXCLK" }, + { "AIF3TX2", NULL, "AIF3TXCLK" }, + { "AIF4TX1", NULL, "AIF4TXCLK" }, + { "AIF4TX2", NULL, "AIF4TXCLK" }, + { "SLIMTX1", NULL, "SLIMBUSCLK" }, + { "SLIMTX2", NULL, "SLIMBUSCLK" }, + { "SLIMTX3", NULL, "SLIMBUSCLK" }, + { "SLIMTX4", NULL, "SLIMBUSCLK" }, + { "SLIMTX5", NULL, "SLIMBUSCLK" }, + { "SLIMTX6", NULL, "SLIMBUSCLK" }, + { "SLIMTX7", NULL, "SLIMBUSCLK" }, + { "SLIMTX8", NULL, "SLIMBUSCLK" }, + { "SPD1TX1", NULL, "SPDCLK" }, + { "SPD1TX2", NULL, "SPDCLK" }, + { "DSP1", NULL, "DSP1CLK" }, + { "DSP2", NULL, "DSP2CLK" }, + { "DSP3", NULL, "DSP3CLK" }, + { "DSP4", NULL, "DSP4CLK" }, + { "DSP5", NULL, "DSP5CLK" }, + { "DSP6", NULL, "DSP6CLK" }, + { "DSP7", NULL, "DSP7CLK" }, + { "ISRC1DEC1", NULL, "ISRC1CLK" }, + { "ISRC1DEC2", NULL, "ISRC1CLK" }, + { "ISRC1DEC3", NULL, "ISRC1CLK" }, + { "ISRC1DEC4", NULL, "ISRC1CLK" }, + { "ISRC1INT1", NULL, "ISRC1CLK" }, + { "ISRC1INT2", NULL, "ISRC1CLK" }, + { "ISRC1INT3", NULL, "ISRC1CLK" }, + { "ISRC1INT4", NULL, "ISRC1CLK" }, + { "ISRC2DEC1", NULL, "ISRC2CLK" }, + { "ISRC2DEC2", NULL, "ISRC2CLK" }, + { "ISRC2DEC3", NULL, "ISRC2CLK" }, + { "ISRC2DEC4", NULL, "ISRC2CLK" }, + { "ISRC2INT1", NULL, "ISRC2CLK" }, + { "ISRC2INT2", NULL, "ISRC2CLK" }, + { "ISRC2INT3", NULL, "ISRC2CLK" }, + { "ISRC2INT4", NULL, "ISRC2CLK" }, + { "ISRC3DEC1", NULL, "ISRC3CLK" }, + { "ISRC3DEC2", NULL, "ISRC3CLK" }, + { "ISRC3INT1", NULL, "ISRC3CLK" }, + { "ISRC3INT2", NULL, "ISRC3CLK" }, + { "ISRC4DEC1", NULL, "ISRC4CLK" }, + { "ISRC4DEC2", NULL, "ISRC4CLK" }, + { "ISRC4INT1", NULL, "ISRC4CLK" }, + { "ISRC4INT2", NULL, "ISRC4CLK" }, + { "ASRC1IN1L", NULL, "ASRC1CLK" }, + { "ASRC1IN1R", NULL, "ASRC1CLK" }, + { "ASRC1IN2L", NULL, "ASRC1CLK" }, + { "ASRC1IN2R", NULL, "ASRC1CLK" }, + { "ASRC2IN1L", NULL, "ASRC2CLK" }, + { "ASRC2IN1R", NULL, "ASRC2CLK" }, + { "ASRC2IN2L", NULL, "ASRC2CLK" }, + { "ASRC2IN2R", NULL, "ASRC2CLK" }, + { "DFC1", NULL, "DFCCLK" }, + { "DFC2", NULL, "DFCCLK" }, + { "DFC3", NULL, "DFCCLK" }, + { "DFC4", NULL, "DFCCLK" }, + { "DFC5", NULL, "DFCCLK" }, + { "DFC6", NULL, "DFCCLK" }, + { "DFC7", NULL, "DFCCLK" }, + { "DFC8", NULL, "DFCCLK" }, + + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD3" }, + { "AIF3 Playback", NULL, "DBVDD3" }, + + { "AIF4 Capture", NULL, "DBVDD3" }, + { "AIF4 Playback", NULL, "DBVDD3" }, + + { "OUT1L", NULL, "CPVDD1" }, + { "OUT1L", NULL, "CPVDD2" }, + { "OUT1R", NULL, "CPVDD1" }, + { "OUT1R", NULL, "CPVDD2" }, + { "OUT2L", NULL, "CPVDD1" }, + { "OUT2L", NULL, "CPVDD2" }, + { "OUT2R", NULL, "CPVDD1" }, + { "OUT2R", NULL, "CPVDD2" }, + { "OUT3L", NULL, "CPVDD1" }, + { "OUT3L", NULL, "CPVDD2" }, + { "OUT3R", NULL, "CPVDD1" }, + { "OUT3R", NULL, "CPVDD2" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT3R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + + { "SPD1", NULL, "SYSCLK" }, + { "SPD1", NULL, "SPD1TX1" }, + { "SPD1", NULL, "SPD1TX2" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + { "IN3L", NULL, "SYSCLK" }, + { "IN3R", NULL, "SYSCLK" }, + { "IN4L", NULL, "SYSCLK" }, + { "IN4R", NULL, "SYSCLK" }, + { "IN5L", NULL, "SYSCLK" }, + { "IN5R", NULL, "SYSCLK" }, + + { "IN3L", NULL, "DBVDD4" }, + { "IN3R", NULL, "DBVDD4" }, + { "IN4L", NULL, "DBVDD4" }, + { "IN4R", NULL, "DBVDD4" }, + { "IN5L", NULL, "DBVDD4" }, + { "IN5R", NULL, "DBVDD4" }, + + { "ASRC1IN1L", NULL, "SYSCLK" }, + { "ASRC1IN1R", NULL, "SYSCLK" }, + { "ASRC1IN2L", NULL, "SYSCLK" }, + { "ASRC1IN2R", NULL, "SYSCLK" }, + { "ASRC2IN1L", NULL, "SYSCLK" }, + { "ASRC2IN1R", NULL, "SYSCLK" }, + { "ASRC2IN2L", NULL, "SYSCLK" }, + { "ASRC2IN2R", NULL, "SYSCLK" }, + + { "ASRC1IN1L", NULL, "ASYNCCLK" }, + { "ASRC1IN1R", NULL, "ASYNCCLK" }, + { "ASRC1IN2L", NULL, "ASYNCCLK" }, + { "ASRC1IN2R", NULL, "ASYNCCLK" }, + { "ASRC2IN1L", NULL, "ASYNCCLK" }, + { "ASRC2IN1R", NULL, "ASYNCCLK" }, + { "ASRC2IN2L", NULL, "ASYNCCLK" }, + { "ASRC2IN2R", NULL, "ASYNCCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + + { "MICBIAS1A", NULL, "MICBIAS1" }, + { "MICBIAS1B", NULL, "MICBIAS1" }, + { "MICBIAS1C", NULL, "MICBIAS1" }, + { "MICBIAS1D", NULL, "MICBIAS1" }, + + { "MICBIAS2A", NULL, "MICBIAS2" }, + { "MICBIAS2B", NULL, "MICBIAS2" }, + { "MICBIAS2C", NULL, "MICBIAS2" }, + { "MICBIAS2D", NULL, "MICBIAS2" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + { "AIF2 Capture", NULL, "AIF2TX7" }, + { "AIF2 Capture", NULL, "AIF2TX8" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + { "AIF2RX7", NULL, "AIF2 Playback" }, + { "AIF2RX8", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "AIF4 Capture", NULL, "AIF4TX1" }, + { "AIF4 Capture", NULL, "AIF4TX2" }, + + { "AIF4RX1", NULL, "AIF4 Playback" }, + { "AIF4RX2", NULL, "AIF4 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "Slim3 Capture", NULL, "SLIMTX7" }, + { "Slim3 Capture", NULL, "SLIMTX8" }, + + { "SLIMRX7", NULL, "Slim3 Playback" }, + { "SLIMRX8", NULL, "Slim3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + { "AIF4 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + { "Slim3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + { "AIF4 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + { "Slim3 Capture", NULL, "SYSCLK" }, + + { "Voice Control DSP", NULL, "DSP6" }, + + { "Audio Trace DSP", NULL, "DSP1" }, + + { "IN1L Analog Mux", "A", "IN1ALN" }, + { "IN1L Analog Mux", "A", "IN1ALP" }, + { "IN1L Analog Mux", "B", "IN1BLN" }, + { "IN1L Analog Mux", "B", "IN1BLP" }, + { "IN1R Analog Mux", "A", "IN1ARN" }, + { "IN1R Analog Mux", "A", "IN1ARP" }, + { "IN1R Analog Mux", "B", "IN1BRN" }, + { "IN1R Analog Mux", "B", "IN1BRP" }, + + { "IN1L Mode", "Analog", "IN1L Analog Mux" }, + { "IN1R Mode", "Analog", "IN1R Analog Mux" }, + + { "IN1L Mode", "Digital", "IN1ARN" }, + { "IN1L Mode", "Digital", "IN1ARP" }, + { "IN1R Mode", "Digital", "IN1ARN" }, + { "IN1R Mode", "Digital", "IN1ARP" }, + + { "IN1L", NULL, "IN1L Mode" }, + { "IN1R", NULL, "IN1R Mode" }, + + { "IN2L Analog Mux", "A", "IN2ALN" }, + { "IN2L Analog Mux", "A", "IN2ALP" }, + { "IN2L Analog Mux", "B", "IN2BLN" }, + { "IN2L Analog Mux", "B", "IN2BLP" }, + + { "IN2L Mode", "Analog", "IN2L Analog Mux" }, + { "IN2R Mode", "Analog", "IN2RN" }, + { "IN2R Mode", "Analog", "IN2RP" }, + + { "IN2L Mode", "Digital", "IN2ALN" }, + { "IN2L Mode", "Digital", "IN2ALP" }, + { "IN2R Mode", "Digital", "IN2ALN" }, + { "IN2R Mode", "Digital", "IN2ALP" }, + + { "IN2L", NULL, "IN2L Mode" }, + { "IN2R", NULL, "IN2R Mode" }, + + { "IN3L", NULL, "DMICCLK3" }, + { "IN3L", NULL, "DMICDAT3" }, + { "IN3R", NULL, "DMICCLK3" }, + { "IN3R", NULL, "DMICDAT3" }, + + { "IN4L", NULL, "DMICCLK4" }, + { "IN4L", NULL, "DMICDAT4" }, + { "IN4R", NULL, "DMICCLK4" }, + { "IN4R", NULL, "DMICDAT4" }, + + { "IN5L", NULL, "DMICCLK5" }, + { "IN5L", NULL, "DMICDAT5" }, + { "IN5R", NULL, "DMICCLK5" }, + { "IN5R", NULL, "DMICDAT5" }, + + MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + MADERA_MIXER_ROUTES("OUT2L", "HPOUT2L"), + MADERA_MIXER_ROUTES("OUT2R", "HPOUT2R"), + MADERA_MIXER_ROUTES("OUT3L", "HPOUT3L"), + MADERA_MIXER_ROUTES("OUT3R", "HPOUT3R"), + + MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + + MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + MADERA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + MADERA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + MADERA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + MADERA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + MADERA_MIXER_ROUTES("AIF2TX7", "AIF2TX7"), + MADERA_MIXER_ROUTES("AIF2TX8", "AIF2TX8"), + + MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + MADERA_MIXER_ROUTES("AIF4TX1", "AIF4TX1"), + MADERA_MIXER_ROUTES("AIF4TX2", "AIF4TX2"), + + MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + MADERA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"), + MADERA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"), + + MADERA_MUX_ROUTES("SPD1TX1", "SPDIF1TX1"), + MADERA_MUX_ROUTES("SPD1TX2", "SPDIF1TX2"), + + MADERA_MIXER_ROUTES("EQ1", "EQ1"), + MADERA_MIXER_ROUTES("EQ2", "EQ2"), + MADERA_MIXER_ROUTES("EQ3", "EQ3"), + MADERA_MIXER_ROUTES("EQ4", "EQ4"), + + MADERA_MIXER_ROUTES("DRC1L", "DRC1L"), + MADERA_MIXER_ROUTES("DRC1R", "DRC1R"), + MADERA_MIXER_ROUTES("DRC2L", "DRC2L"), + MADERA_MIXER_ROUTES("DRC2R", "DRC2R"), + + MADERA_MIXER_ROUTES("LHPF1", "LHPF1"), + MADERA_MIXER_ROUTES("LHPF2", "LHPF2"), + MADERA_MIXER_ROUTES("LHPF3", "LHPF3"), + MADERA_MIXER_ROUTES("LHPF4", "LHPF4"), + + MADERA_MUX_ROUTES("ASRC1IN1L", "ASRC1IN1L"), + MADERA_MUX_ROUTES("ASRC1IN1R", "ASRC1IN1R"), + MADERA_MUX_ROUTES("ASRC1IN2L", "ASRC1IN2L"), + MADERA_MUX_ROUTES("ASRC1IN2R", "ASRC1IN2R"), + MADERA_MUX_ROUTES("ASRC2IN1L", "ASRC2IN1L"), + MADERA_MUX_ROUTES("ASRC2IN1R", "ASRC2IN1R"), + MADERA_MUX_ROUTES("ASRC2IN2L", "ASRC2IN2L"), + MADERA_MUX_ROUTES("ASRC2IN2R", "ASRC2IN2R"), + + MADERA_DSP_ROUTES("DSP1"), + MADERA_DSP_ROUTES("DSP2"), + MADERA_DSP_ROUTES("DSP3"), + MADERA_DSP_ROUTES("DSP4"), + MADERA_DSP_ROUTES("DSP5"), + MADERA_DSP_ROUTES("DSP6"), + MADERA_DSP_ROUTES("DSP7"), + + { "DSP Trigger Out", NULL, "DSP1 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP2 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP3 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP4 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP5 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP6 Trigger Output" }, + { "DSP Trigger Out", NULL, "DSP7 Trigger Output" }, + + { "DSP1 Trigger Output", "Switch", "DSP1" }, + { "DSP2 Trigger Output", "Switch", "DSP2" }, + { "DSP3 Trigger Output", "Switch", "DSP3" }, + { "DSP4 Trigger Output", "Switch", "DSP4" }, + { "DSP5 Trigger Output", "Switch", "DSP5" }, + { "DSP6 Trigger Output", "Switch", "DSP6" }, + { "DSP7 Trigger Output", "Switch", "DSP7" }, + + MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + MADERA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + MADERA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + MADERA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + MADERA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + MADERA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"), + MADERA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"), + + MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + MADERA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"), + MADERA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"), + + MADERA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"), + MADERA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"), + + MADERA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"), + MADERA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"), + + MADERA_MUX_ROUTES("ISRC4INT1", "ISRC4INT1"), + MADERA_MUX_ROUTES("ISRC4INT2", "ISRC4INT2"), + + MADERA_MUX_ROUTES("ISRC4DEC1", "ISRC4DEC1"), + MADERA_MUX_ROUTES("ISRC4DEC2", "ISRC4DEC2"), + + { "AEC1 Loopback", "HPOUT1L", "OUT1L" }, + { "AEC1 Loopback", "HPOUT1R", "OUT1R" }, + { "AEC2 Loopback", "HPOUT1L", "OUT1L" }, + { "AEC2 Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC1 Loopback", "HPOUT2L", "OUT2L" }, + { "AEC1 Loopback", "HPOUT2R", "OUT2R" }, + { "AEC2 Loopback", "HPOUT2L", "OUT2L" }, + { "AEC2 Loopback", "HPOUT2R", "OUT2R" }, + { "HPOUT2L", NULL, "OUT2L" }, + { "HPOUT2R", NULL, "OUT2R" }, + + { "AEC1 Loopback", "HPOUT3L", "OUT3L" }, + { "AEC1 Loopback", "HPOUT3R", "OUT3R" }, + { "AEC2 Loopback", "HPOUT3L", "OUT3L" }, + { "AEC2 Loopback", "HPOUT3R", "OUT3R" }, + { "HPOUT3L", NULL, "OUT3L" }, + { "HPOUT3R", NULL, "OUT3R" }, + + { "AEC1 Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC1 Loopback", "SPKDAT1R", "OUT5R" }, + { "AEC2 Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC2 Loopback", "SPKDAT1R", "OUT5R" }, + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + CS47L90_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"), + CS47L90_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"), + + CS47L90_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"), + CS47L90_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"), + CS47L90_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"), + CS47L90_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"), + CS47L90_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"), + CS47L90_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"), + CS47L90_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"), + CS47L90_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"), + + { "SPDIF1", NULL, "SPD1" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" }, + { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" }, + { "DRC1 Activity Output", "Switch", "DRC1L" }, + { "DRC1 Activity Output", "Switch", "DRC1R" }, + { "DRC2 Activity Output", "Switch", "DRC2L" }, + { "DRC2 Activity Output", "Switch", "DRC2R" }, + + MADERA_MUX_ROUTES("DFC1", "DFC1"), + MADERA_MUX_ROUTES("DFC2", "DFC2"), + MADERA_MUX_ROUTES("DFC3", "DFC3"), + MADERA_MUX_ROUTES("DFC4", "DFC4"), + MADERA_MUX_ROUTES("DFC5", "DFC5"), + MADERA_MUX_ROUTES("DFC6", "DFC6"), + MADERA_MUX_ROUTES("DFC7", "DFC7"), + MADERA_MUX_ROUTES("DFC8", "DFC8"), +}; + +static int cs47l90_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case MADERA_FLL1_REFCLK: + return madera_set_fll_refclk(&cs47l90->fll[0], source, fref, + fout); + case MADERA_FLL2_REFCLK: + return madera_set_fll_refclk(&cs47l90->fll[1], source, fref, + fout); + case MADERA_FLLAO_REFCLK: + return madera_set_fll_ao_refclk(&cs47l90->fll[2], source, fref, + fout); + case MADERA_FLL1_SYNCCLK: + return madera_set_fll_syncclk(&cs47l90->fll[0], source, fref, + fout); + case MADERA_FLL2_SYNCCLK: + return madera_set_fll_syncclk(&cs47l90->fll[1], source, fref, + fout); + default: + return -EINVAL; + } +} + +static const struct snd_soc_dai_ops cs47l90_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + +static struct snd_soc_dai_driver cs47l90_dai[] = { + { + .name = "cs47l90-aif1", + .id = 1, + .base = MADERA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l90-aif2", + .id = 2, + .base = MADERA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l90-aif3", + .id = 3, + .base = MADERA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l90-aif4", + .id = 4, + .base = MADERA_AIF4_BCLK_CTRL, + .playback = { + .stream_name = "AIF4 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l90-slim1", + .id = 5, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l90-slim2", + .id = 6, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l90-slim3", + .id = 7, + .playback = { + .stream_name = "Slim3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l90-cpu-voicectrl", + .capture = { + .stream_name = "Voice Control CPU", + .channels_min = 1, + .channels_max = 1, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &cs47l90_dai_ops, + }, + { + .name = "cs47l90-dsp-voicectrl", + .capture = { + .stream_name = "Voice Control DSP", + .channels_min = 1, + .channels_max = 1, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + }, + { + .name = "cs47l90-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &cs47l90_dai_ops, + }, + { + .name = "cs47l90-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 6, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + }, +}; + +static int cs47l90_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l90->core; + struct madera *madera = priv->madera; + int n_adsp; + + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l90-dsp-voicectrl") == 0) { + n_adsp = 5; + } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l90-dsp-trace") == 0) { + n_adsp = 0; + } else { + dev_err(madera->dev, + "No suitable compressed stream for DAI '%s'\n", + asoc_rtd_to_codec(rtd, 0)->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&priv->adsp[n_adsp], stream); +} + +static irqreturn_t cs47l90_adsp2_irq(int irq, void *data) +{ + struct cs47l90 *cs47l90 = data; + struct madera_priv *priv = &cs47l90->core; + struct madera *madera = priv->madera; + struct madera_voice_trigger_info trig_info; + int serviced = 0; + int i, ret; + + for (i = 0; i < CS47L90_NUM_ADSP; ++i) { + ret = wm_adsp_compr_handle_irq(&priv->adsp[i]); + if (ret != -ENODEV) + serviced++; + if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) { + trig_info.core_num = i + 1; + blocking_notifier_call_chain(&madera->notifier, + MADERA_NOTIFY_VOICE_TRIGGER, + &trig_info); + } + } + + if (!serviced) { + dev_err(madera->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int cs47l90_component_probe(struct snd_soc_component *component) +{ + struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l90->core.madera; + int ret, i; + + snd_soc_component_init_regmap(component, madera->regmap); + + mutex_lock(&madera->dapm_ptr_lock); + madera->dapm = snd_soc_component_get_dapm(component); + mutex_unlock(&madera->dapm_ptr_lock); + + ret = madera_init_inputs(component); + if (ret) + return ret; + + ret = madera_init_outputs(component, NULL, CS47L90_MONO_OUTPUTS, + CS47L90_MONO_OUTPUTS); + if (ret) + return ret; + + snd_soc_component_disable_pin(component, "HAPTICS"); + + ret = snd_soc_add_component_controls(component, + madera_adsp_rate_controls, + CS47L90_NUM_ADSP); + if (ret) + return ret; + + for (i = 0; i < CS47L90_NUM_ADSP; i++) + wm_adsp2_component_probe(&cs47l90->core.adsp[i], component); + + return 0; +} + +static void cs47l90_component_remove(struct snd_soc_component *component) +{ + struct cs47l90 *cs47l90 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l90->core.madera; + int i; + + mutex_lock(&madera->dapm_ptr_lock); + madera->dapm = NULL; + mutex_unlock(&madera->dapm_ptr_lock); + + for (i = 0; i < CS47L90_NUM_ADSP; i++) + wm_adsp2_component_remove(&cs47l90->core.adsp[i], component); +} + +#define CS47L90_DIG_VU 0x0200 + +static unsigned int cs47l90_digital_vu[] = { + MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, + MADERA_DAC_DIGITAL_VOLUME_2L, + MADERA_DAC_DIGITAL_VOLUME_2R, + MADERA_DAC_DIGITAL_VOLUME_3L, + MADERA_DAC_DIGITAL_VOLUME_3R, + MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, +}; + +static const struct snd_compress_ops cs47l90_compress_ops = { + .open = &cs47l90_open, + .free = &wm_adsp_compr_free, + .set_params = &wm_adsp_compr_set_params, + .get_caps = &wm_adsp_compr_get_caps, + .trigger = &wm_adsp_compr_trigger, + .pointer = &wm_adsp_compr_pointer, + .copy = &wm_adsp_compr_copy, +}; + +static const struct snd_soc_component_driver soc_component_dev_cs47l90 = { + .probe = &cs47l90_component_probe, + .remove = &cs47l90_component_remove, + .set_sysclk = &madera_set_sysclk, + .set_pll = &cs47l90_set_fll, + .name = DRV_NAME, + .compress_ops = &cs47l90_compress_ops, + .controls = cs47l90_snd_controls, + .num_controls = ARRAY_SIZE(cs47l90_snd_controls), + .dapm_widgets = cs47l90_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs47l90_dapm_widgets), + .dapm_routes = cs47l90_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs47l90_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cs47l90_probe(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + struct cs47l90 *cs47l90; + int i, ret; + + BUILD_BUG_ON(ARRAY_SIZE(cs47l90_dai) > MADERA_MAX_DAI); + + /* quick exit if Madera irqchip driver hasn't completed probe */ + if (!madera->irq_dev) { + dev_dbg(&pdev->dev, "irqchip driver not ready\n"); + return -EPROBE_DEFER; + } + + cs47l90 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l90), + GFP_KERNEL); + if (!cs47l90) + return -ENOMEM; + + platform_set_drvdata(pdev, cs47l90); + + cs47l90->core.madera = madera; + cs47l90->core.dev = &pdev->dev; + cs47l90->core.num_inputs = 10; + + ret = madera_core_init(&cs47l90->core); + if (ret) + return ret; + + ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", cs47l90_adsp2_irq, + cs47l90); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + goto error_core; + } + + ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1); + if (ret) + dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret); + + for (i = 0; i < CS47L90_NUM_ADSP; i++) { + cs47l90->core.adsp[i].part = "cs47l90"; + cs47l90->core.adsp[i].cs_dsp.num = i + 1; + cs47l90->core.adsp[i].cs_dsp.type = WMFW_ADSP2; + cs47l90->core.adsp[i].cs_dsp.rev = 2; + cs47l90->core.adsp[i].cs_dsp.dev = madera->dev; + cs47l90->core.adsp[i].cs_dsp.regmap = madera->regmap_32bit; + + cs47l90->core.adsp[i].cs_dsp.base = cs47l90_dsp_control_bases[i]; + cs47l90->core.adsp[i].cs_dsp.mem = cs47l90_dsp_regions[i]; + cs47l90->core.adsp[i].cs_dsp.num_mems = + ARRAY_SIZE(cs47l90_dsp1_regions); + + cs47l90->core.adsp[i].cs_dsp.lock_regions = CS_ADSP2_REGION_1_9; + + ret = wm_adsp2_init(&cs47l90->core.adsp[i]); + + if (ret == 0) { + ret = madera_init_bus_error_irq(&cs47l90->core, i, + wm_adsp2_bus_error); + if (ret != 0) + wm_adsp2_remove(&cs47l90->core.adsp[i]); + } + + if (ret) { + for (--i; i >= 0; --i) { + madera_free_bus_error_irq(&cs47l90->core, i); + wm_adsp2_remove(&cs47l90->core.adsp[i]); + } + goto error_dsp_irq; + } + } + + madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1, + &cs47l90->fll[0]); + madera_init_fll(madera, 2, MADERA_FLL2_CONTROL_1 - 1, + &cs47l90->fll[1]); + madera_init_fll(madera, 4, MADERA_FLLAO_CONTROL_1 - 1, + &cs47l90->fll[2]); + + for (i = 0; i < ARRAY_SIZE(cs47l90_dai); i++) + madera_init_dai(&cs47l90->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(cs47l90_digital_vu); i++) + regmap_update_bits(madera->regmap, cs47l90_digital_vu[i], + CS47L90_DIG_VU, CS47L90_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_cs47l90, + cs47l90_dai, + ARRAY_SIZE(cs47l90_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto error_pm_runtime; + } + + return ret; + +error_pm_runtime: + pm_runtime_disable(&pdev->dev); + + for (i = 0; i < CS47L90_NUM_ADSP; i++) { + madera_free_bus_error_irq(&cs47l90->core, i); + wm_adsp2_remove(&cs47l90->core.adsp[i]); + } +error_dsp_irq: + madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0); + madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l90); +error_core: + madera_core_free(&cs47l90->core); + + return ret; +} + +static void cs47l90_remove(struct platform_device *pdev) +{ + struct cs47l90 *cs47l90 = platform_get_drvdata(pdev); + int i; + + pm_runtime_disable(&pdev->dev); + + for (i = 0; i < CS47L90_NUM_ADSP; i++) { + madera_free_bus_error_irq(&cs47l90->core, i); + wm_adsp2_remove(&cs47l90->core.adsp[i]); + } + + madera_set_irq_wake(cs47l90->core.madera, MADERA_IRQ_DSP_IRQ1, 0); + madera_free_irq(cs47l90->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l90); + madera_core_free(&cs47l90->core); +} + +static struct platform_driver cs47l90_codec_driver = { + .driver = { + .name = "cs47l90-codec", + }, + .probe = &cs47l90_probe, + .remove_new = cs47l90_remove, +}; + +module_platform_driver(cs47l90_codec_driver); + +MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp"); +MODULE_DESCRIPTION("ASoC CS47L90 driver"); +MODULE_AUTHOR("Nikesh Oswal "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cs47l90-codec"); diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c new file mode 100644 index 0000000000..352deeaff1 --- /dev/null +++ b/sound/soc/codecs/cs47l92.c @@ -0,0 +1,2104 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// ALSA SoC Audio driver for CS47L92 codec +// +// Copyright (C) 2016-2019 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "madera.h" +#include "wm_adsp.h" + +#define CS47L92_NUM_ADSP 1 +#define CS47L92_MONO_OUTPUTS 3 + +#define DRV_NAME "cs47l92-codec" + +struct cs47l92 { + struct madera_priv core; + struct madera_fll fll[2]; +}; + +static const struct cs_dsp_region cs47l92_dsp1_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x080000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x0e0000 }, + { .type = WMFW_ADSP2_XM, .base = 0x0a0000 }, + { .type = WMFW_ADSP2_YM, .base = 0x0c0000 }, +}; + +static const char * const cs47l92_outdemux_texts[] = { + "HPOUT3", + "HPOUT4", +}; + +static int cs47l92_put_demux(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l92->core; + struct madera *madera = priv->madera; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int ep_sel, mux, change, cur; + bool out_mono; + int ret; + + if (ucontrol->value.enumerated.item[0] > e->items - 1) + return -EINVAL; + + mux = ucontrol->value.enumerated.item[0]; + + snd_soc_dapm_mutex_lock(dapm); + + ep_sel = mux << e->shift_l; + + change = snd_soc_component_test_bits(component, MADERA_OUTPUT_ENABLES_1, + MADERA_EP_SEL_MASK, + ep_sel); + if (!change) + goto end; + + ret = regmap_read(madera->regmap, MADERA_OUTPUT_ENABLES_1, &cur); + if (ret != 0) + dev_warn(madera->dev, "Failed to read outputs: %d\n", ret); + + /* EP_SEL should not be modified while HPOUT3 or 4 is enabled */ + ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, + MADERA_OUT3L_ENA | MADERA_OUT3R_ENA, 0); + if (ret) + dev_warn(madera->dev, "Failed to disable outputs: %d\n", ret); + + usleep_range(2000, 3000); /* wait for wseq to complete */ + + ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, + MADERA_EP_SEL, ep_sel); + if (ret) { + dev_err(madera->dev, "Failed to set OUT3 demux: %d\n", ret); + } else { + out_mono = madera->pdata.codec.out_mono[2 + mux]; + + ret = madera_set_output_mode(component, 3, out_mono); + if (ret < 0) + dev_warn(madera->dev, + "Failed to set output mode: %d\n", ret); + } + + ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, + MADERA_OUT3L_ENA | MADERA_OUT3R_ENA, cur); + if (ret) { + dev_warn(madera->dev, "Failed to restore outputs: %d\n", ret); + } else { + /* wait for wseq */ + if (cur & (MADERA_OUT3L_ENA | MADERA_OUT3R_ENA)) + msleep(34); /* enable delay */ + else + usleep_range(2000, 3000); /* disable delay */ + } + +end: + snd_soc_dapm_mutex_unlock(dapm); + + ret = snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); + if (ret < 0) { + dev_err(madera->dev, "Failed to update demux power state: %d\n", ret); + return ret; + } + + return change; +} + +static SOC_ENUM_SINGLE_DECL(cs47l92_outdemux_enum, + MADERA_OUTPUT_ENABLES_1, + MADERA_EP_SEL_SHIFT, + cs47l92_outdemux_texts); + +static const struct snd_kcontrol_new cs47l92_outdemux = + SOC_DAPM_ENUM_EXT("OUT3 Demux", cs47l92_outdemux_enum, + snd_soc_dapm_get_enum_double, cs47l92_put_demux); + +static int cs47l92_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l92->core; + struct madera *madera = priv->madera; + unsigned int freq; + int ret; + + ret = regmap_read(madera->regmap, MADERA_DSP_CLOCK_2, &freq); + if (ret != 0) { + dev_err(madera->dev, + "Failed to read MADERA_DSP_CLOCK_2: %d\n", ret); + return ret; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = madera_set_adsp_clk(&cs47l92->core, w->shift, freq); + if (ret) + return ret; + break; + default: + break; + } + + return wm_adsp_early_event(w, kcontrol, event); +} + +static int cs47l92_outclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l92->core; + struct madera *madera = priv->madera; + unsigned int val; + int ret; + + ret = regmap_read(madera->regmap, MADERA_OUTPUT_RATE_1, &val); + if (ret) { + dev_err(madera->dev, "Failed to read OUTCLK source: %d\n", ret); + return ret; + } + + val &= MADERA_OUT_CLK_SRC_MASK; + + switch (val) { + case MADERA_OUTCLK_MCLK1: + case MADERA_OUTCLK_MCLK2: + case MADERA_OUTCLK_MCLK3: + val -= (MADERA_OUTCLK_MCLK1 - MADERA_MCLK1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = clk_prepare_enable(madera->mclk[val].clk); + if (ret) + return ret; + break; + case SND_SOC_DAPM_POST_PMD: + clk_disable_unprepare(madera->mclk[val].clk); + break; + default: + break; + } + break; + default: + break; + } + + return madera_domain_clk_ev(w, kcontrol, event); +} + +#define CS47L92_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + +static const struct snd_kcontrol_new cs47l92_snd_controls[] = { +SOC_ENUM("IN1 OSR", madera_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", madera_in_dmic_osr[1]), +SOC_ENUM("IN3 OSR", madera_in_dmic_osr[2]), +SOC_ENUM("IN4 OSR", madera_in_dmic_osr[3]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", MADERA_IN1L_CONTROL, + MADERA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", MADERA_IN1R_CONTROL, + MADERA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", MADERA_IN2L_CONTROL, + MADERA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", MADERA_IN2R_CONTROL, + MADERA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, madera_ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", madera_in_hpf_cut_enum), + +SOC_SINGLE_EXT("IN1L LP Switch", MADERA_ADC_DIGITAL_VOLUME_1L, + MADERA_IN1L_LP_MODE_SHIFT, 1, 0, + snd_soc_get_volsw, madera_lp_mode_put), +SOC_SINGLE_EXT("IN1R LP Switch", MADERA_ADC_DIGITAL_VOLUME_1R, + MADERA_IN1L_LP_MODE_SHIFT, 1, 0, + snd_soc_get_volsw, madera_lp_mode_put), +SOC_SINGLE_EXT("IN2L LP Switch", MADERA_ADC_DIGITAL_VOLUME_2L, + MADERA_IN1L_LP_MODE_SHIFT, 1, 0, + snd_soc_get_volsw, madera_lp_mode_put), +SOC_SINGLE_EXT("IN2R LP Switch", MADERA_ADC_DIGITAL_VOLUME_2R, + MADERA_IN1L_LP_MODE_SHIFT, 1, 0, + snd_soc_get_volsw, madera_lp_mode_put), + +SOC_SINGLE("IN1L HPF Switch", MADERA_IN1L_CONTROL, + MADERA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", MADERA_IN1R_CONTROL, + MADERA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", MADERA_IN2L_CONTROL, + MADERA_IN2L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", MADERA_IN2R_CONTROL, + MADERA_IN2R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN3L HPF Switch", MADERA_IN3L_CONTROL, + MADERA_IN3L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN3R HPF Switch", MADERA_IN3R_CONTROL, + MADERA_IN3R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN4L HPF Switch", MADERA_IN4L_CONTROL, + MADERA_IN4L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN4R HPF Switch", MADERA_IN4R_CONTROL, + MADERA_IN4R_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1L, + MADERA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_1R, + MADERA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2L, + MADERA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_2R, + MADERA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3L, + MADERA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_3R, + MADERA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN4L Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4L, + MADERA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), +SOC_SINGLE_TLV("IN4R Digital Volume", MADERA_ADC_DIGITAL_VOLUME_4R, + MADERA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, madera_digital_tlv), + +SOC_ENUM("Input Ramp Up", madera_in_vi_ramp), +SOC_ENUM("Input Ramp Down", madera_in_vd_ramp), + +MADERA_MIXER_CONTROLS("EQ1", MADERA_EQ1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ2", MADERA_EQ2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ3", MADERA_EQ3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("EQ4", MADERA_EQ4MIX_INPUT_1_SOURCE), + +MADERA_EQ_CONTROL("EQ1 Coefficients", MADERA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", MADERA_EQ1_1, MADERA_EQ1_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", MADERA_EQ1_1, MADERA_EQ1_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", MADERA_EQ1_1, MADERA_EQ1_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", MADERA_EQ1_2, MADERA_EQ1_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", MADERA_EQ1_2, MADERA_EQ1_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ2 Coefficients", MADERA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", MADERA_EQ2_1, MADERA_EQ2_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", MADERA_EQ2_1, MADERA_EQ2_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", MADERA_EQ2_1, MADERA_EQ2_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", MADERA_EQ2_2, MADERA_EQ2_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", MADERA_EQ2_2, MADERA_EQ2_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ3 Coefficients", MADERA_EQ3_2), +SOC_SINGLE_TLV("EQ3 B1 Volume", MADERA_EQ3_1, MADERA_EQ3_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", MADERA_EQ3_1, MADERA_EQ3_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", MADERA_EQ3_1, MADERA_EQ3_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", MADERA_EQ3_2, MADERA_EQ3_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", MADERA_EQ3_2, MADERA_EQ3_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +MADERA_EQ_CONTROL("EQ4 Coefficients", MADERA_EQ4_2), +SOC_SINGLE_TLV("EQ4 B1 Volume", MADERA_EQ4_1, MADERA_EQ4_B1_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", MADERA_EQ4_1, MADERA_EQ4_B2_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", MADERA_EQ4_1, MADERA_EQ4_B3_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", MADERA_EQ4_2, MADERA_EQ4_B4_GAIN_SHIFT, + 24, 0, madera_eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", MADERA_EQ4_2, MADERA_EQ4_B5_GAIN_SHIFT, + 24, 0, madera_eq_tlv), + +SOC_SINGLE("DAC High Performance Mode Switch", MADERA_OUTPUT_RATE_1, + MADERA_CP_DAC_MODE_SHIFT, 1, 0), + +MADERA_MIXER_CONTROLS("DRC1L", MADERA_DRC1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC1R", MADERA_DRC1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC2L", MADERA_DRC2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DRC2R", MADERA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", MADERA_DRC1_CTRL1, 5, + MADERA_DRC1R_ENA | MADERA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", MADERA_DRC2_CTRL1, 5, + MADERA_DRC2R_ENA | MADERA_DRC2L_ENA), + +MADERA_MIXER_CONTROLS("LHPF1", MADERA_HPLP1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF2", MADERA_HPLP2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF3", MADERA_HPLP3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("LHPF4", MADERA_HPLP4MIX_INPUT_1_SOURCE), + +MADERA_LHPF_CONTROL("LHPF1 Coefficients", MADERA_HPLPF1_2), +MADERA_LHPF_CONTROL("LHPF2 Coefficients", MADERA_HPLPF2_2), +MADERA_LHPF_CONTROL("LHPF3 Coefficients", MADERA_HPLPF3_2), +MADERA_LHPF_CONTROL("LHPF4 Coefficients", MADERA_HPLPF4_2), + +SOC_ENUM("LHPF1 Mode", madera_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", madera_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", madera_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", madera_lhpf4_mode), + +MADERA_RATE_ENUM("ISRC1 FSL", madera_isrc_fsl[0]), +MADERA_RATE_ENUM("ISRC2 FSL", madera_isrc_fsl[1]), +MADERA_RATE_ENUM("ISRC1 FSH", madera_isrc_fsh[0]), +MADERA_RATE_ENUM("ISRC2 FSH", madera_isrc_fsh[1]), +MADERA_RATE_ENUM("ASRC1 Rate 1", madera_asrc1_bidir_rate[0]), +MADERA_RATE_ENUM("ASRC1 Rate 2", madera_asrc1_bidir_rate[1]), + +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + +MADERA_MIXER_CONTROLS("DSP1L", MADERA_DSP1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("DSP1R", MADERA_DSP1RMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", MADERA_COMFORT_NOISE_GENERATOR, + MADERA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, madera_noise_tlv), + +MADERA_MIXER_CONTROLS("HPOUT1L", MADERA_OUT1LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT1R", MADERA_OUT1RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT2L", MADERA_OUT2LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT2R", MADERA_OUT2RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT3L", MADERA_OUT3LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("HPOUT3R", MADERA_OUT3RMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT1L", MADERA_OUT5LMIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SPKDAT1R", MADERA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 SC Protect Switch", MADERA_HP1_SHORT_CIRCUIT_CTRL, + MADERA_HP1_SC_ENA_SHIFT, 1, 0), +SOC_SINGLE("HPOUT2 SC Protect Switch", MADERA_HP2_SHORT_CIRCUIT_CTRL, + MADERA_HP2_SC_ENA_SHIFT, 1, 0), +SOC_SINGLE("HPOUT3 SC Protect Switch", MADERA_HP3_SHORT_CIRCUIT_CTRL, + MADERA_HP3_SC_ENA_SHIFT, 1, 0), + +SOC_SINGLE("SPKDAT1 High Performance Switch", MADERA_OUTPUT_PATH_CONFIG_5L, + MADERA_OUT5_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT2 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_2L, + MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT3 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_3L, + MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, MADERA_OUT1L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_2L, + MADERA_DAC_DIGITAL_VOLUME_2R, MADERA_OUT2L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_3L, + MADERA_DAC_DIGITAL_VOLUME_3R, MADERA_OUT3L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, MADERA_OUT5L_VOL_SHIFT, + 0xbf, 0, madera_digital_tlv), + +SOC_DOUBLE("SPKDAT1 Switch", MADERA_PDM_SPK1_CTRL_1, MADERA_SPK1L_MUTE_SHIFT, + MADERA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_ENUM("Output Ramp Up", madera_out_vi_ramp), +SOC_ENUM("Output Ramp Down", madera_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_THR_SHIFT, 7, 1, madera_ng_tlv), +SOC_ENUM("Noise Gate Hold", madera_ng_hold), + +SOC_ENUM_EXT("DFC1RX Width", madera_dfc_width[0], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC1RX Type", madera_dfc_type[0], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC1TX Width", madera_dfc_width[1], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC1TX Type", madera_dfc_type[1], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC2RX Width", madera_dfc_width[2], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC2RX Type", madera_dfc_type[2], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC2TX Width", madera_dfc_width[3], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC2TX Type", madera_dfc_type[3], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC3RX Width", madera_dfc_width[4], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC3RX Type", madera_dfc_type[4], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC3TX Width", madera_dfc_width[5], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC3TX Type", madera_dfc_type[5], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC4RX Width", madera_dfc_width[6], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC4RX Type", madera_dfc_type[6], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC4TX Width", madera_dfc_width[7], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC4TX Type", madera_dfc_type[7], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC5RX Width", madera_dfc_width[8], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC5RX Type", madera_dfc_type[8], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC5TX Width", madera_dfc_width[9], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC5TX Type", madera_dfc_type[9], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC6RX Width", madera_dfc_width[10], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC6RX Type", madera_dfc_type[10], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC6TX Width", madera_dfc_width[11], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC6TX Type", madera_dfc_type[11], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC7RX Width", madera_dfc_width[12], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC7RX Type", madera_dfc_type[12], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC7TX Width", madera_dfc_width[13], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC7TX Type", madera_dfc_type[13], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC8RX Width", madera_dfc_width[14], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC8RX Type", madera_dfc_type[14], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC8TX Width", madera_dfc_width[15], + snd_soc_get_enum_double, madera_dfc_put), +SOC_ENUM_EXT("DFC8TX Type", madera_dfc_type[15], + snd_soc_get_enum_double, madera_dfc_put), + +CS47L92_NG_SRC("HPOUT1L", MADERA_NOISE_GATE_SELECT_1L), +CS47L92_NG_SRC("HPOUT1R", MADERA_NOISE_GATE_SELECT_1R), +CS47L92_NG_SRC("HPOUT2L", MADERA_NOISE_GATE_SELECT_2L), +CS47L92_NG_SRC("HPOUT2R", MADERA_NOISE_GATE_SELECT_2R), +CS47L92_NG_SRC("HPOUT3L", MADERA_NOISE_GATE_SELECT_3L), +CS47L92_NG_SRC("HPOUT3R", MADERA_NOISE_GATE_SELECT_3R), +CS47L92_NG_SRC("SPKDAT1L", MADERA_NOISE_GATE_SELECT_5L), +CS47L92_NG_SRC("SPKDAT1R", MADERA_NOISE_GATE_SELECT_5R), + +MADERA_MIXER_CONTROLS("AIF1TX1", MADERA_AIF1TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX2", MADERA_AIF1TX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX3", MADERA_AIF1TX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX4", MADERA_AIF1TX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX5", MADERA_AIF1TX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX6", MADERA_AIF1TX6MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX7", MADERA_AIF1TX7MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF1TX8", MADERA_AIF1TX8MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF2TX1", MADERA_AIF2TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX2", MADERA_AIF2TX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX3", MADERA_AIF2TX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX4", MADERA_AIF2TX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX5", MADERA_AIF2TX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX6", MADERA_AIF2TX6MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX7", MADERA_AIF2TX7MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF2TX8", MADERA_AIF2TX8MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("AIF3TX1", MADERA_AIF3TX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF3TX2", MADERA_AIF3TX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF3TX3", MADERA_AIF3TX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("AIF3TX4", MADERA_AIF3TX4MIX_INPUT_1_SOURCE), + +MADERA_MIXER_CONTROLS("SLIMTX1", MADERA_SLIMTX1MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX2", MADERA_SLIMTX2MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX3", MADERA_SLIMTX3MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX4", MADERA_SLIMTX4MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX5", MADERA_SLIMTX5MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX6", MADERA_SLIMTX6MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX7", MADERA_SLIMTX7MIX_INPUT_1_SOURCE), +MADERA_MIXER_CONTROLS("SLIMTX8", MADERA_SLIMTX8MIX_INPUT_1_SOURCE), + +MADERA_GAINMUX_CONTROLS("SPDIFTX1", MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE), +MADERA_GAINMUX_CONTROLS("SPDIFTX2", MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), +}; + +MADERA_MIXER_ENUMS(EQ1, MADERA_EQ1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ2, MADERA_EQ2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ3, MADERA_EQ3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(EQ4, MADERA_EQ4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DRC1L, MADERA_DRC1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC1R, MADERA_DRC1RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC2L, MADERA_DRC2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DRC2R, MADERA_DRC2RMIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(LHPF1, MADERA_HPLP1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF2, MADERA_HPLP2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF3, MADERA_HPLP3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(LHPF4, MADERA_HPLP4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(DSP1L, MADERA_DSP1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(DSP1R, MADERA_DSP1RMIX_INPUT_1_SOURCE); +MADERA_DSP_AUX_ENUMS(DSP1, MADERA_DSP1AUX1MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(PWM1, MADERA_PWM1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(PWM2, MADERA_PWM2MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(OUT1L, MADERA_OUT1LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT1R, MADERA_OUT1RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT2L, MADERA_OUT2LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT2R, MADERA_OUT2RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT3L, MADERA_OUT3LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(OUT3R, MADERA_OUT3RMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT1L, MADERA_OUT5LMIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SPKDAT1R, MADERA_OUT5RMIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF1TX1, MADERA_AIF1TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX2, MADERA_AIF1TX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX3, MADERA_AIF1TX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX4, MADERA_AIF1TX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX5, MADERA_AIF1TX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX6, MADERA_AIF1TX6MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX7, MADERA_AIF1TX7MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF1TX8, MADERA_AIF1TX8MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF2TX1, MADERA_AIF2TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX2, MADERA_AIF2TX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX3, MADERA_AIF2TX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX4, MADERA_AIF2TX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX5, MADERA_AIF2TX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX6, MADERA_AIF2TX6MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX7, MADERA_AIF2TX7MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF2TX8, MADERA_AIF2TX8MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(AIF3TX1, MADERA_AIF3TX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF3TX2, MADERA_AIF3TX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF3TX3, MADERA_AIF3TX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(AIF3TX4, MADERA_AIF3TX4MIX_INPUT_1_SOURCE); + +MADERA_MIXER_ENUMS(SLIMTX1, MADERA_SLIMTX1MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX2, MADERA_SLIMTX2MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX3, MADERA_SLIMTX3MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX4, MADERA_SLIMTX4MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX5, MADERA_SLIMTX5MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX6, MADERA_SLIMTX6MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX7, MADERA_SLIMTX7MIX_INPUT_1_SOURCE); +MADERA_MIXER_ENUMS(SLIMTX8, MADERA_SLIMTX8MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(SPD1TX1, MADERA_SPDIF1TX1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(SPD1TX2, MADERA_SPDIF1TX2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ASRC1IN1L, MADERA_ASRC1_1LMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC1IN1R, MADERA_ASRC1_1RMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC1IN2L, MADERA_ASRC1_2LMIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ASRC1IN2R, MADERA_ASRC1_2RMIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC1INT1, MADERA_ISRC1INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1INT2, MADERA_ISRC1INT2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC1DEC1, MADERA_ISRC1DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC1DEC2, MADERA_ISRC1DEC2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC2INT1, MADERA_ISRC2INT1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2INT2, MADERA_ISRC2INT2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(ISRC2DEC1, MADERA_ISRC2DEC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(ISRC2DEC2, MADERA_ISRC2DEC2MIX_INPUT_1_SOURCE); + +MADERA_MUX_ENUMS(DFC1, MADERA_DFC1MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC2, MADERA_DFC2MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC3, MADERA_DFC3MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC4, MADERA_DFC4MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC5, MADERA_DFC5MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC6, MADERA_DFC6MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC7, MADERA_DFC7MIX_INPUT_1_SOURCE); +MADERA_MUX_ENUMS(DFC8, MADERA_DFC8MIX_INPUT_1_SOURCE); + +static const char * const cs47l92_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R", + "SPKDAT1L", "SPKDAT1R", +}; + +static const unsigned int cs47l92_aec_loopback_values[] = { + 0, 1, 2, 3, 4, 5, 8, 9 +}; + +static const struct soc_enum cs47l92_aec_loopback = + SOC_VALUE_ENUM_SINGLE(MADERA_DAC_AEC_CONTROL_1, + MADERA_AEC1_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(cs47l92_aec_loopback_texts), + cs47l92_aec_loopback_texts, + cs47l92_aec_loopback_values); + +static const struct snd_kcontrol_new cs47l92_aec_loopback_mux = + SOC_DAPM_ENUM("AEC1 Loopback", cs47l92_aec_loopback); + +static const struct snd_soc_dapm_widget cs47l92_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", MADERA_SYSTEM_CLOCK_1, MADERA_SYSCLK_ENA_SHIFT, + 0, madera_sysclk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", MADERA_ASYNC_CLOCK_1, + MADERA_ASYNC_CLK_ENA_SHIFT, 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", MADERA_OUTPUT_SYSTEM_CLOCK, + MADERA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", MADERA_OUTPUT_ASYNC_CLOCK, + MADERA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSPCLK", MADERA_DSP_CLOCK_1, MADERA_DSP_CLK_ENA_SHIFT, + 0, madera_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD1", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD2", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", MADERA_MIC_BIAS_CTRL_1, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", MADERA_MIC_BIAS_CTRL_2, + MADERA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS1A", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1A_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1B", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1B_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1C", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1C_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1D", MADERA_MIC_BIAS_CTRL_5, + MADERA_MICB1D_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS2A", MADERA_MIC_BIAS_CTRL_6, + MADERA_MICB2A_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2B", MADERA_MIC_BIAS_CTRL_6, + MADERA_MICB2B_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("FXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_FX, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASRC1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ASRC1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ISRC2CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_ISRC2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OUTCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_OUT, 0, + cs47l92_outclk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("SPDCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_SPD, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DSP1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF1TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF1, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF2TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF2, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF3TXCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_AIF3, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("SLIMBUSCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_SLIMBUS, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("PWMCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_PWM, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("DFCCLK", SND_SOC_NOPM, + MADERA_DOM_GRP_DFC, 0, + madera_domain_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), + +SND_SOC_DAPM_INPUT("IN1ALN"), +SND_SOC_DAPM_INPUT("IN1ALP"), +SND_SOC_DAPM_INPUT("IN1BLN"), +SND_SOC_DAPM_INPUT("IN1BLP"), +SND_SOC_DAPM_INPUT("IN1ARN"), +SND_SOC_DAPM_INPUT("IN1ARP"), +SND_SOC_DAPM_INPUT("IN1BR"), +SND_SOC_DAPM_INPUT("IN2ALN"), +SND_SOC_DAPM_INPUT("IN2ALP"), +SND_SOC_DAPM_INPUT("IN2BL"), +SND_SOC_DAPM_INPUT("IN2ARN"), +SND_SOC_DAPM_INPUT("IN2ARP"), +SND_SOC_DAPM_INPUT("IN2BR"), + +SND_SOC_DAPM_MUX("IN1L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[0]), +SND_SOC_DAPM_MUX("IN1R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[1]), +SND_SOC_DAPM_MUX("IN2L Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[2]), +SND_SOC_DAPM_MUX("IN2R Analog Mux", SND_SOC_NOPM, 0, 0, &madera_inmux[3]), + +SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]), +SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[0]), + +SND_SOC_DAPM_MUX("IN2L Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), +SND_SOC_DAPM_MUX("IN2R Mode", SND_SOC_NOPM, 0, 0, &madera_inmode[1]), + +SND_SOC_DAPM_DEMUX("OUT3 Demux", SND_SOC_NOPM, 0, 0, &cs47l92_outdemux), +SND_SOC_DAPM_MUX("OUT3 Mono Mux", SND_SOC_NOPM, 0, 0, &cs47l92_outdemux), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_PGA("PWM1 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", MADERA_PWM_DRIVE_1, MADERA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, + MADERA_AIF1_TX_ENABLES, MADERA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX7", NULL, 6, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX8", NULL, 7, + MADERA_AIF2_TX_ENABLES, MADERA_AIF2TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7, + MADERA_SLIMBUS_TX_CHANNEL_ENABLE, + MADERA_SLIMTX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX3", NULL, 2, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX4", NULL, 3, + MADERA_AIF3_TX_ENABLES, MADERA_AIF3TX4_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + MADERA_OUT1L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + MADERA_OUT1R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", SND_SOC_NOPM, + MADERA_OUT2L_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", SND_SOC_NOPM, + MADERA_OUT2R_ENA_SHIFT, 0, NULL, 0, madera_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT3L_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3R", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT3R_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT5L_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", MADERA_OUTPUT_ENABLES_1, + MADERA_OUT5R_ENA_SHIFT, 0, NULL, 0, madera_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("SPD1TX1", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_VAL1_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPD1TX2", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_VAL2_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPD1", MADERA_SPD1_TX_CONTROL, + MADERA_SPD1_ENA_SHIFT, 0, NULL, 0), + +/* + * mux_in widgets : arranged in the order of sources + * specified in MADERA_MIXER_INPUT_ROUTES + */ + +SND_SOC_DAPM_PGA("Noise Generator", MADERA_COMFORT_NOISE_GENERATOR, + MADERA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", MADERA_TONE_GENERATOR_1, + MADERA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", MADERA_TONE_GENERATOR_1, + MADERA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_MUX("AEC1 Loopback", MADERA_DAC_AEC_CONTROL_1, + MADERA_AEC1_LOOPBACK_ENA_SHIFT, 0, + &cs47l92_aec_loopback_mux), + +SND_SOC_DAPM_PGA_E("IN1L", MADERA_INPUT_ENABLES, MADERA_IN1L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R", MADERA_INPUT_ENABLES, MADERA_IN1R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L", MADERA_INPUT_ENABLES, MADERA_IN2L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R", MADERA_INPUT_ENABLES, MADERA_IN2R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3L", MADERA_INPUT_ENABLES, MADERA_IN3L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3R", MADERA_INPUT_ENABLES, MADERA_IN3R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4L", MADERA_INPUT_ENABLES, MADERA_IN4L_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4R", MADERA_INPUT_ENABLES, MADERA_IN4R_ENA_SHIFT, + 0, NULL, 0, madera_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, + MADERA_AIF1_RX_ENABLES, MADERA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX7", NULL, 6, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX8", NULL, 7, + MADERA_AIF2_RX_ENABLES, MADERA_AIF2RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX3", NULL, 2, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX4", NULL, 3, + MADERA_AIF3_RX_ENABLES, MADERA_AIF3RX4_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, MADERA_SLIMBUS_RX_CHANNEL_ENABLE, + MADERA_SLIMRX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA("EQ1", MADERA_EQ1_1, MADERA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", MADERA_EQ2_1, MADERA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", MADERA_EQ3_1, MADERA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", MADERA_EQ4_1, MADERA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", MADERA_DRC1_CTRL1, MADERA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", MADERA_DRC1_CTRL1, MADERA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", MADERA_DRC2_CTRL1, MADERA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", MADERA_DRC2_CTRL1, MADERA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", MADERA_HPLPF1_1, MADERA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", MADERA_HPLPF2_1, MADERA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", MADERA_HPLPF3_1, MADERA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", MADERA_HPLPF4_1, MADERA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("ASRC1IN1L", MADERA_ASRC1_ENABLE, + MADERA_ASRC1_IN1L_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC1IN1R", MADERA_ASRC1_ENABLE, + MADERA_ASRC1_IN1R_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC1IN2L", MADERA_ASRC1_ENABLE, + MADERA_ASRC1_IN2L_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ASRC1IN2R", MADERA_ASRC1_ENABLE, + MADERA_ASRC1_IN2R_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", MADERA_ISRC_1_CTRL_3, + MADERA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", MADERA_ISRC_2_CTRL_3, + MADERA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0), + +WM_ADSP2("DSP1", 0, cs47l92_adsp_power_ev), + +/* end of ordered widget list */ + +SND_SOC_DAPM_PGA("DFC1", MADERA_DFC1_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC2", MADERA_DFC2_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC3", MADERA_DFC3_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC4", MADERA_DFC4_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC5", MADERA_DFC5_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC6", MADERA_DFC6_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC7", MADERA_DFC7_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("DFC8", MADERA_DFC8_CTRL, MADERA_DFC1_ENA_SHIFT, 0, NULL, 0), + +MADERA_MIXER_WIDGETS(EQ1, "EQ1"), +MADERA_MIXER_WIDGETS(EQ2, "EQ2"), +MADERA_MIXER_WIDGETS(EQ3, "EQ3"), +MADERA_MIXER_WIDGETS(EQ4, "EQ4"), + +MADERA_MIXER_WIDGETS(DRC1L, "DRC1L"), +MADERA_MIXER_WIDGETS(DRC1R, "DRC1R"), +MADERA_MIXER_WIDGETS(DRC2L, "DRC2L"), +MADERA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0, + &madera_drc_activity_output_mux[0]), +SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0, + &madera_drc_activity_output_mux[1]), + +MADERA_MIXER_WIDGETS(LHPF1, "LHPF1"), +MADERA_MIXER_WIDGETS(LHPF2, "LHPF2"), +MADERA_MIXER_WIDGETS(LHPF3, "LHPF3"), +MADERA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +MADERA_MIXER_WIDGETS(PWM1, "PWM1"), +MADERA_MIXER_WIDGETS(PWM2, "PWM2"), + +MADERA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +MADERA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +MADERA_MIXER_WIDGETS(OUT2L, "HPOUT2L"), +MADERA_MIXER_WIDGETS(OUT2R, "HPOUT2R"), +MADERA_MIXER_WIDGETS(OUT3L, "HPOUT3L"), +MADERA_MIXER_WIDGETS(OUT3R, "HPOUT3R"), +MADERA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +MADERA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), + +MADERA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +MADERA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +MADERA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +MADERA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +MADERA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +MADERA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +MADERA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +MADERA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +MADERA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +MADERA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +MADERA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +MADERA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +MADERA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +MADERA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), +MADERA_MIXER_WIDGETS(AIF2TX7, "AIF2TX7"), +MADERA_MIXER_WIDGETS(AIF2TX8, "AIF2TX8"), + +MADERA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +MADERA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), +MADERA_MIXER_WIDGETS(AIF3TX3, "AIF3TX3"), +MADERA_MIXER_WIDGETS(AIF3TX4, "AIF3TX4"), + +MADERA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +MADERA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +MADERA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +MADERA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +MADERA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +MADERA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), +MADERA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"), +MADERA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"), + +MADERA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"), +MADERA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"), + +MADERA_MUX_WIDGETS(ASRC1IN1L, "ASRC1IN1L"), +MADERA_MUX_WIDGETS(ASRC1IN1R, "ASRC1IN1R"), +MADERA_MUX_WIDGETS(ASRC1IN2L, "ASRC1IN2L"), +MADERA_MUX_WIDGETS(ASRC1IN2R, "ASRC1IN2R"), + +MADERA_DSP_WIDGETS(DSP1, "DSP1"), + +MADERA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +MADERA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), + +MADERA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +MADERA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), + +MADERA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +MADERA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +MADERA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +MADERA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +MADERA_MUX_WIDGETS(DFC1, "DFC1"), +MADERA_MUX_WIDGETS(DFC2, "DFC2"), +MADERA_MUX_WIDGETS(DFC3, "DFC3"), +MADERA_MUX_WIDGETS(DFC4, "DFC4"), +MADERA_MUX_WIDGETS(DFC5, "DFC5"), +MADERA_MUX_WIDGETS(DFC6, "DFC6"), +MADERA_MUX_WIDGETS(DFC7, "DFC7"), +MADERA_MUX_WIDGETS(DFC8, "DFC8"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2L"), +SND_SOC_DAPM_OUTPUT("HPOUT2R"), +SND_SOC_DAPM_OUTPUT("HPOUT3L"), +SND_SOC_DAPM_OUTPUT("HPOUT3R"), +SND_SOC_DAPM_OUTPUT("HPOUT4L"), +SND_SOC_DAPM_OUTPUT("HPOUT4R"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), +SND_SOC_DAPM_OUTPUT("SPDIF1"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define MADERA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC1", "AEC1 Loopback" }, \ + { name, "IN1L", "IN1L" }, \ + { name, "IN1R", "IN1R" }, \ + { name, "IN2L", "IN2L" }, \ + { name, "IN2R", "IN2R" }, \ + { name, "IN3L", "IN3L" }, \ + { name, "IN3R", "IN3R" }, \ + { name, "IN4L", "IN4L" }, \ + { name, "IN4R", "IN4R" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF2RX7", "AIF2RX7" }, \ + { name, "AIF2RX8", "AIF2RX8" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "AIF3RX3", "AIF3RX3" }, \ + { name, "AIF3RX4", "AIF3RX4" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "SLIMRX7", "SLIMRX7" }, \ + { name, "SLIMRX8", "SLIMRX8" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1IN1L", "ASRC1IN1L" }, \ + { name, "ASRC1IN1R", "ASRC1IN1R" }, \ + { name, "ASRC1IN2L", "ASRC1IN2L" }, \ + { name, "ASRC1IN2R", "ASRC1IN2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" }, \ + { name, "DFC1", "DFC1" }, \ + { name, "DFC2", "DFC2" }, \ + { name, "DFC3", "DFC3" }, \ + { name, "DFC4", "DFC4" }, \ + { name, "DFC5", "DFC5" }, \ + { name, "DFC6", "DFC6" }, \ + { name, "DFC7", "DFC7" }, \ + { name, "DFC8", "DFC8" } + +static const struct snd_soc_dapm_route cs47l92_dapm_routes[] = { + /* Internal clock domains */ + { "EQ1", NULL, "FXCLK" }, + { "EQ2", NULL, "FXCLK" }, + { "EQ3", NULL, "FXCLK" }, + { "EQ4", NULL, "FXCLK" }, + { "DRC1L", NULL, "FXCLK" }, + { "DRC1R", NULL, "FXCLK" }, + { "DRC2L", NULL, "FXCLK" }, + { "DRC2R", NULL, "FXCLK" }, + { "LHPF1", NULL, "FXCLK" }, + { "LHPF2", NULL, "FXCLK" }, + { "LHPF3", NULL, "FXCLK" }, + { "LHPF4", NULL, "FXCLK" }, + { "PWM1 Mixer", NULL, "PWMCLK" }, + { "PWM2 Mixer", NULL, "PWMCLK" }, + { "OUT1L", NULL, "OUTCLK" }, + { "OUT1R", NULL, "OUTCLK" }, + { "OUT2L", NULL, "OUTCLK" }, + { "OUT2R", NULL, "OUTCLK" }, + { "OUT3L", NULL, "OUTCLK" }, + { "OUT3R", NULL, "OUTCLK" }, + { "OUT5L", NULL, "OUTCLK" }, + { "OUT5R", NULL, "OUTCLK" }, + { "AIF1TX1", NULL, "AIF1TXCLK" }, + { "AIF1TX2", NULL, "AIF1TXCLK" }, + { "AIF1TX3", NULL, "AIF1TXCLK" }, + { "AIF1TX4", NULL, "AIF1TXCLK" }, + { "AIF1TX5", NULL, "AIF1TXCLK" }, + { "AIF1TX6", NULL, "AIF1TXCLK" }, + { "AIF1TX7", NULL, "AIF1TXCLK" }, + { "AIF1TX8", NULL, "AIF1TXCLK" }, + { "AIF2TX1", NULL, "AIF2TXCLK" }, + { "AIF2TX2", NULL, "AIF2TXCLK" }, + { "AIF2TX3", NULL, "AIF2TXCLK" }, + { "AIF2TX4", NULL, "AIF2TXCLK" }, + { "AIF2TX5", NULL, "AIF2TXCLK" }, + { "AIF2TX6", NULL, "AIF2TXCLK" }, + { "AIF2TX7", NULL, "AIF2TXCLK" }, + { "AIF2TX8", NULL, "AIF2TXCLK" }, + { "AIF3TX1", NULL, "AIF3TXCLK" }, + { "AIF3TX2", NULL, "AIF3TXCLK" }, + { "AIF3TX3", NULL, "AIF3TXCLK" }, + { "AIF3TX4", NULL, "AIF3TXCLK" }, + { "SLIMTX1", NULL, "SLIMBUSCLK" }, + { "SLIMTX2", NULL, "SLIMBUSCLK" }, + { "SLIMTX3", NULL, "SLIMBUSCLK" }, + { "SLIMTX4", NULL, "SLIMBUSCLK" }, + { "SLIMTX5", NULL, "SLIMBUSCLK" }, + { "SLIMTX6", NULL, "SLIMBUSCLK" }, + { "SLIMTX7", NULL, "SLIMBUSCLK" }, + { "SLIMTX8", NULL, "SLIMBUSCLK" }, + { "SPD1TX1", NULL, "SPDCLK" }, + { "SPD1TX2", NULL, "SPDCLK" }, + { "DSP1", NULL, "DSP1CLK" }, + { "ISRC1DEC1", NULL, "ISRC1CLK" }, + { "ISRC1DEC2", NULL, "ISRC1CLK" }, + { "ISRC1INT1", NULL, "ISRC1CLK" }, + { "ISRC1INT2", NULL, "ISRC1CLK" }, + { "ISRC2DEC1", NULL, "ISRC2CLK" }, + { "ISRC2DEC2", NULL, "ISRC2CLK" }, + { "ISRC2INT1", NULL, "ISRC2CLK" }, + { "ISRC2INT2", NULL, "ISRC2CLK" }, + { "ASRC1IN1L", NULL, "ASRC1CLK" }, + { "ASRC1IN1R", NULL, "ASRC1CLK" }, + { "ASRC1IN2L", NULL, "ASRC1CLK" }, + { "ASRC1IN2R", NULL, "ASRC1CLK" }, + { "DFC1", NULL, "DFCCLK" }, + { "DFC2", NULL, "DFCCLK" }, + { "DFC3", NULL, "DFCCLK" }, + { "DFC4", NULL, "DFCCLK" }, + { "DFC5", NULL, "DFCCLK" }, + { "DFC6", NULL, "DFCCLK" }, + { "DFC7", NULL, "DFCCLK" }, + { "DFC8", NULL, "DFCCLK" }, + + { "OUT1L", NULL, "CPVDD1" }, + { "OUT1L", NULL, "CPVDD2" }, + { "OUT1R", NULL, "CPVDD1" }, + { "OUT1R", NULL, "CPVDD2" }, + { "OUT2L", NULL, "CPVDD1" }, + { "OUT2L", NULL, "CPVDD2" }, + { "OUT2R", NULL, "CPVDD1" }, + { "OUT2R", NULL, "CPVDD2" }, + { "OUT3L", NULL, "CPVDD1" }, + { "OUT3L", NULL, "CPVDD2" }, + { "OUT3R", NULL, "CPVDD1" }, + { "OUT3R", NULL, "CPVDD2" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT3R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + + { "SPD1", NULL, "SYSCLK" }, + { "SPD1", NULL, "SPD1TX1" }, + { "SPD1", NULL, "SPD1TX2" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + { "IN3L", NULL, "SYSCLK" }, + { "IN3R", NULL, "SYSCLK" }, + { "IN4L", NULL, "SYSCLK" }, + { "IN4R", NULL, "SYSCLK" }, + + { "ASRC1IN1L", NULL, "SYSCLK" }, + { "ASRC1IN1R", NULL, "SYSCLK" }, + { "ASRC1IN2L", NULL, "SYSCLK" }, + { "ASRC1IN2R", NULL, "SYSCLK" }, + + { "ASRC1IN1L", NULL, "ASYNCCLK" }, + { "ASRC1IN1R", NULL, "ASYNCCLK" }, + { "ASRC1IN2L", NULL, "ASYNCCLK" }, + { "ASRC1IN2R", NULL, "ASYNCCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + + { "MICBIAS1A", NULL, "MICBIAS1" }, + { "MICBIAS1B", NULL, "MICBIAS1" }, + { "MICBIAS1C", NULL, "MICBIAS1" }, + { "MICBIAS1D", NULL, "MICBIAS1" }, + + { "MICBIAS2A", NULL, "MICBIAS2" }, + { "MICBIAS2B", NULL, "MICBIAS2" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + { "AIF2 Capture", NULL, "AIF2TX7" }, + { "AIF2 Capture", NULL, "AIF2TX8" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + { "AIF2RX7", NULL, "AIF2 Playback" }, + { "AIF2RX8", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + { "AIF3 Capture", NULL, "AIF3TX3" }, + { "AIF3 Capture", NULL, "AIF3TX4" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + { "AIF3RX3", NULL, "AIF3 Playback" }, + { "AIF3RX4", NULL, "AIF3 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "Slim3 Capture", NULL, "SLIMTX7" }, + { "Slim3 Capture", NULL, "SLIMTX8" }, + + { "SLIMRX7", NULL, "Slim3 Playback" }, + { "SLIMRX8", NULL, "Slim3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + { "Slim3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + { "Slim3 Capture", NULL, "SYSCLK" }, + + { "Audio Trace DSP", NULL, "DSP1" }, + + { "IN1L Analog Mux", "A", "IN1ALN" }, + { "IN1L Analog Mux", "A", "IN1ALP" }, + { "IN1L Analog Mux", "B", "IN1BLN" }, + { "IN1L Analog Mux", "B", "IN1BLP" }, + { "IN1R Analog Mux", "A", "IN1ARN" }, + { "IN1R Analog Mux", "A", "IN1ARP" }, + { "IN1R Analog Mux", "B", "IN1BR" }, + { "IN1R Analog Mux", "B", "IN1ALN" }, + + { "IN1L Mode", "Analog", "IN1L Analog Mux" }, + { "IN1R Mode", "Analog", "IN1R Analog Mux" }, + + { "IN1L Mode", "Digital", "IN1ALN" }, + { "IN1L Mode", "Digital", "IN1ALP" }, + { "IN1R Mode", "Digital", "IN1ALN" }, + { "IN1R Mode", "Digital", "IN1ALP" }, + + { "IN1L", NULL, "IN1L Mode" }, + { "IN1R", NULL, "IN1R Mode" }, + + { "IN2L Analog Mux", "A", "IN2ALN" }, + { "IN2L Analog Mux", "A", "IN2ALP" }, + { "IN2L Analog Mux", "B", "IN2ALN" }, + { "IN2L Analog Mux", "B", "IN2BL" }, + { "IN2R Analog Mux", "A", "IN2ARN" }, + { "IN2R Analog Mux", "A", "IN2ARP" }, + { "IN2R Analog Mux", "B", "IN2ARN" }, + { "IN2R Analog Mux", "B", "IN2BR" }, + + { "IN2L Mode", "Analog", "IN2L Analog Mux" }, + { "IN2R Mode", "Analog", "IN2R Analog Mux" }, + + { "IN2L Mode", "Digital", "IN2ALN" }, + { "IN2L Mode", "Digital", "IN2ALP" }, + { "IN2R Mode", "Digital", "IN2ALN" }, + { "IN2R Mode", "Digital", "IN2ALP" }, + + { "IN2L", NULL, "IN2L Mode" }, + { "IN2R", NULL, "IN2R Mode" }, + + { "IN3L", NULL, "IN1ARN" }, + { "IN3L", NULL, "IN1ARP" }, + { "IN3R", NULL, "IN1ARN" }, + { "IN3R", NULL, "IN1ARP" }, + + { "IN4L", NULL, "IN2ARN" }, + { "IN4L", NULL, "IN2ARP" }, + { "IN4R", NULL, "IN2ARN" }, + { "IN4R", NULL, "IN2ARP" }, + + MADERA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + MADERA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + MADERA_MIXER_ROUTES("OUT2L", "HPOUT2L"), + MADERA_MIXER_ROUTES("OUT2R", "HPOUT2R"), + MADERA_MIXER_ROUTES("OUT3L", "HPOUT3L"), + MADERA_MIXER_ROUTES("OUT3R", "HPOUT3R"), + + MADERA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + MADERA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + + MADERA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + MADERA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + MADERA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + MADERA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + MADERA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + MADERA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + MADERA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + MADERA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + MADERA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + MADERA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + MADERA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + MADERA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + MADERA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + MADERA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + MADERA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + MADERA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + MADERA_MIXER_ROUTES("AIF2TX7", "AIF2TX7"), + MADERA_MIXER_ROUTES("AIF2TX8", "AIF2TX8"), + + MADERA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + MADERA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + MADERA_MIXER_ROUTES("AIF3TX3", "AIF3TX3"), + MADERA_MIXER_ROUTES("AIF3TX4", "AIF3TX4"), + + MADERA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + MADERA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + MADERA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + MADERA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + MADERA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + MADERA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + MADERA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"), + MADERA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"), + + MADERA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"), + MADERA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"), + + MADERA_MIXER_ROUTES("EQ1", "EQ1"), + MADERA_MIXER_ROUTES("EQ2", "EQ2"), + MADERA_MIXER_ROUTES("EQ3", "EQ3"), + MADERA_MIXER_ROUTES("EQ4", "EQ4"), + + MADERA_MIXER_ROUTES("DRC1L", "DRC1L"), + MADERA_MIXER_ROUTES("DRC1R", "DRC1R"), + MADERA_MIXER_ROUTES("DRC2L", "DRC2L"), + MADERA_MIXER_ROUTES("DRC2R", "DRC2R"), + + MADERA_MIXER_ROUTES("LHPF1", "LHPF1"), + MADERA_MIXER_ROUTES("LHPF2", "LHPF2"), + MADERA_MIXER_ROUTES("LHPF3", "LHPF3"), + MADERA_MIXER_ROUTES("LHPF4", "LHPF4"), + + MADERA_MUX_ROUTES("ASRC1IN1L", "ASRC1IN1L"), + MADERA_MUX_ROUTES("ASRC1IN1R", "ASRC1IN1R"), + MADERA_MUX_ROUTES("ASRC1IN2L", "ASRC1IN2L"), + MADERA_MUX_ROUTES("ASRC1IN2R", "ASRC1IN2R"), + + MADERA_DSP_ROUTES("DSP1"), + + MADERA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + MADERA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + + MADERA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + MADERA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + + MADERA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + MADERA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + MADERA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + MADERA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + { "AEC1 Loopback", "HPOUT1L", "OUT1L" }, + { "AEC1 Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC1 Loopback", "HPOUT2L", "OUT2L" }, + { "AEC1 Loopback", "HPOUT2R", "OUT2R" }, + { "HPOUT2L", NULL, "OUT2L" }, + { "HPOUT2R", NULL, "OUT2R" }, + + { "AEC1 Loopback", "HPOUT3L", "OUT3L" }, + { "AEC1 Loopback", "HPOUT3R", "OUT3R" }, + { "OUT3 Demux", NULL, "OUT3L" }, + { "OUT3 Demux", NULL, "OUT3R" }, + + { "OUT3R", NULL, "OUT3 Mono Mux" }, + + { "HPOUT3L", "HPOUT3", "OUT3 Demux" }, + { "HPOUT3R", "HPOUT3", "OUT3 Demux" }, + { "HPOUT4L", "HPOUT4", "OUT3 Demux" }, + { "HPOUT4R", "HPOUT4", "OUT3 Demux" }, + + { "AEC1 Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC1 Loopback", "SPKDAT1R", "OUT5R" }, + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + { "SPDIF1", NULL, "SPD1" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" }, + { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" }, + { "DRC1 Activity Output", "Switch", "DRC1L" }, + { "DRC1 Activity Output", "Switch", "DRC1R" }, + { "DRC2 Activity Output", "Switch", "DRC2L" }, + { "DRC2 Activity Output", "Switch", "DRC2R" }, + + MADERA_MUX_ROUTES("DFC1", "DFC1"), + MADERA_MUX_ROUTES("DFC2", "DFC2"), + MADERA_MUX_ROUTES("DFC3", "DFC3"), + MADERA_MUX_ROUTES("DFC4", "DFC4"), + MADERA_MUX_ROUTES("DFC5", "DFC5"), + MADERA_MUX_ROUTES("DFC6", "DFC6"), + MADERA_MUX_ROUTES("DFC7", "DFC7"), + MADERA_MUX_ROUTES("DFC8", "DFC8"), +}; + +static int cs47l92_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case MADERA_FLL1_REFCLK: + return madera_fllhj_set_refclk(&cs47l92->fll[0], source, fref, + fout); + case MADERA_FLL2_REFCLK: + return madera_fllhj_set_refclk(&cs47l92->fll[1], source, fref, + fout); + default: + return -EINVAL; + } +} + +static const struct snd_soc_dai_ops cs47l92_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + +static struct snd_soc_dai_driver cs47l92_dai[] = { + { + .name = "cs47l92-aif1", + .id = 1, + .base = MADERA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l92-aif2", + .id = 2, + .base = MADERA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l92-aif3", + .id = 3, + .base = MADERA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "cs47l92-slim1", + .id = 5, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l92-slim2", + .id = 6, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l92-slim3", + .id = 7, + .playback = { + .stream_name = "Slim3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .capture = { + .stream_name = "Slim3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &madera_simple_dai_ops, + }, + { + .name = "cs47l92-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + .ops = &cs47l92_dai_ops, + }, + { + .name = "cs47l92-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 2, + .rates = MADERA_RATES, + .formats = MADERA_FORMATS, + }, + }, +}; + +static int cs47l92_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); + struct madera_priv *priv = &cs47l92->core; + struct madera *madera = priv->madera; + int n_adsp; + + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "cs47l92-dsp-trace") == 0) { + n_adsp = 0; + } else { + dev_err(madera->dev, + "No suitable compressed stream for DAI '%s'\n", + asoc_rtd_to_codec(rtd, 0)->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&priv->adsp[n_adsp], stream); +} + +static irqreturn_t cs47l92_adsp2_irq(int irq, void *data) +{ + struct cs47l92 *cs47l92 = data; + struct madera_priv *priv = &cs47l92->core; + struct madera *madera = priv->madera; + int ret; + + ret = wm_adsp_compr_handle_irq(&priv->adsp[0]); + if (ret == -ENODEV) { + dev_err(madera->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static const struct snd_soc_dapm_route cs47l92_mono_routes[] = { + { "OUT1R", NULL, "OUT1L" }, + { "OUT2R", NULL, "OUT2L" }, + { "OUT3 Mono Mux", "HPOUT3", "OUT3L" }, + { "OUT3 Mono Mux", "HPOUT4", "OUT3L" }, +}; + +static int cs47l92_component_probe(struct snd_soc_component *component) +{ + struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l92->core.madera; + int ret; + + snd_soc_component_init_regmap(component, madera->regmap); + + mutex_lock(&madera->dapm_ptr_lock); + madera->dapm = snd_soc_component_get_dapm(component); + mutex_unlock(&madera->dapm_ptr_lock); + + ret = madera_init_inputs(component); + if (ret) + return ret; + + ret = madera_init_outputs(component, cs47l92_mono_routes, + ARRAY_SIZE(cs47l92_mono_routes), + CS47L92_MONO_OUTPUTS); + if (ret) + return ret; + + snd_soc_component_disable_pin(component, "HAPTICS"); + + ret = snd_soc_add_component_controls(component, + madera_adsp_rate_controls, + CS47L92_NUM_ADSP); + if (ret) + return ret; + + return wm_adsp2_component_probe(&cs47l92->core.adsp[0], component); +} + +static void cs47l92_component_remove(struct snd_soc_component *component) +{ + struct cs47l92 *cs47l92 = snd_soc_component_get_drvdata(component); + struct madera *madera = cs47l92->core.madera; + + mutex_lock(&madera->dapm_ptr_lock); + madera->dapm = NULL; + mutex_unlock(&madera->dapm_ptr_lock); + + wm_adsp2_component_remove(&cs47l92->core.adsp[0], component); +} + +#define CS47L92_DIG_VU 0x0200 + +static unsigned int cs47l92_digital_vu[] = { + MADERA_DAC_DIGITAL_VOLUME_1L, + MADERA_DAC_DIGITAL_VOLUME_1R, + MADERA_DAC_DIGITAL_VOLUME_2L, + MADERA_DAC_DIGITAL_VOLUME_2R, + MADERA_DAC_DIGITAL_VOLUME_3L, + MADERA_DAC_DIGITAL_VOLUME_3R, + MADERA_DAC_DIGITAL_VOLUME_5L, + MADERA_DAC_DIGITAL_VOLUME_5R, +}; + +static const struct snd_compress_ops cs47l92_compress_ops = { + .open = &cs47l92_open, + .free = &wm_adsp_compr_free, + .set_params = &wm_adsp_compr_set_params, + .get_caps = &wm_adsp_compr_get_caps, + .trigger = &wm_adsp_compr_trigger, + .pointer = &wm_adsp_compr_pointer, + .copy = &wm_adsp_compr_copy, +}; + +static const struct snd_soc_component_driver soc_component_dev_cs47l92 = { + .probe = &cs47l92_component_probe, + .remove = &cs47l92_component_remove, + .set_sysclk = &madera_set_sysclk, + .set_pll = &cs47l92_set_fll, + .name = DRV_NAME, + .compress_ops = &cs47l92_compress_ops, + .controls = cs47l92_snd_controls, + .num_controls = ARRAY_SIZE(cs47l92_snd_controls), + .dapm_widgets = cs47l92_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs47l92_dapm_widgets), + .dapm_routes = cs47l92_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs47l92_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cs47l92_probe(struct platform_device *pdev) +{ + struct madera *madera = dev_get_drvdata(pdev->dev.parent); + struct cs47l92 *cs47l92; + int i, ret; + + BUILD_BUG_ON(ARRAY_SIZE(cs47l92_dai) > MADERA_MAX_DAI); + + /* quick exit if Madera irqchip driver hasn't completed probe */ + if (!madera->irq_dev) { + dev_dbg(&pdev->dev, "irqchip driver not ready\n"); + return -EPROBE_DEFER; + } + + cs47l92 = devm_kzalloc(&pdev->dev, sizeof(struct cs47l92), GFP_KERNEL); + if (!cs47l92) + return -ENOMEM; + + platform_set_drvdata(pdev, cs47l92); + + cs47l92->core.madera = madera; + cs47l92->core.dev = &pdev->dev; + cs47l92->core.num_inputs = 8; + + ret = madera_core_init(&cs47l92->core); + if (ret) + return ret; + + ret = madera_request_irq(madera, MADERA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", cs47l92_adsp2_irq, + cs47l92); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + goto error_core; + } + + ret = madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 1); + if (ret) + dev_warn(&pdev->dev, "Failed to set DSP IRQ wake: %d\n", ret); + + cs47l92->core.adsp[0].part = "cs47l92"; + cs47l92->core.adsp[0].cs_dsp.num = 1; + cs47l92->core.adsp[0].cs_dsp.type = WMFW_ADSP2; + cs47l92->core.adsp[0].cs_dsp.rev = 2; + cs47l92->core.adsp[0].cs_dsp.dev = madera->dev; + cs47l92->core.adsp[0].cs_dsp.regmap = madera->regmap_32bit; + + cs47l92->core.adsp[0].cs_dsp.base = MADERA_DSP1_CONFIG_1; + cs47l92->core.adsp[0].cs_dsp.mem = cs47l92_dsp1_regions; + cs47l92->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(cs47l92_dsp1_regions); + + cs47l92->core.adsp[0].cs_dsp.lock_regions = CS_ADSP2_REGION_1_9; + + ret = wm_adsp2_init(&cs47l92->core.adsp[0]); + if (ret != 0) + goto error_dsp_irq; + + ret = madera_init_bus_error_irq(&cs47l92->core, 0, wm_adsp2_bus_error); + if (ret != 0) + goto error_adsp; + + madera_init_fll(madera, 1, MADERA_FLL1_CONTROL_1 - 1, + &cs47l92->fll[0]); + madera_init_fll(madera, 2, MADERA_FLL2_CONTROL_1 - 1, + &cs47l92->fll[1]); + + for (i = 0; i < ARRAY_SIZE(cs47l92_dai); i++) + madera_init_dai(&cs47l92->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(cs47l92_digital_vu); i++) + regmap_update_bits(madera->regmap, cs47l92_digital_vu[i], + CS47L92_DIG_VU, CS47L92_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_cs47l92, + cs47l92_dai, + ARRAY_SIZE(cs47l92_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto error_pm_runtime; + } + + return ret; + +error_pm_runtime: + pm_runtime_disable(&pdev->dev); + madera_free_bus_error_irq(&cs47l92->core, 0); +error_adsp: + wm_adsp2_remove(&cs47l92->core.adsp[0]); +error_dsp_irq: + madera_set_irq_wake(madera, MADERA_IRQ_DSP_IRQ1, 0); + madera_free_irq(madera, MADERA_IRQ_DSP_IRQ1, cs47l92); +error_core: + madera_core_free(&cs47l92->core); + + return ret; +} + +static void cs47l92_remove(struct platform_device *pdev) +{ + struct cs47l92 *cs47l92 = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + madera_free_bus_error_irq(&cs47l92->core, 0); + wm_adsp2_remove(&cs47l92->core.adsp[0]); + + madera_set_irq_wake(cs47l92->core.madera, MADERA_IRQ_DSP_IRQ1, 0); + madera_free_irq(cs47l92->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l92); + + madera_core_free(&cs47l92->core); +} + +static struct platform_driver cs47l92_codec_driver = { + .driver = { + .name = "cs47l92-codec", + }, + .probe = &cs47l92_probe, + .remove_new = cs47l92_remove, +}; + +module_platform_driver(cs47l92_codec_driver); + +MODULE_SOFTDEP("pre: madera irq-madera arizona-micsupp"); +MODULE_DESCRIPTION("ASoC CS47L92 driver"); +MODULE_AUTHOR("Stuart Henderson "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cs47l92-codec"); diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c new file mode 100644 index 0000000000..f4065555c3 --- /dev/null +++ b/sound/soc/codecs/cs53l30.c @@ -0,0 +1,1128 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs53l30.c -- CS53l30 ALSA Soc Audio driver + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Authors: Paul Handrigan , + * Tim Howe + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs53l30.h" +#include "cirrus_legacy.h" + +#define CS53L30_NUM_SUPPLIES 2 +static const char *const cs53l30_supply_names[CS53L30_NUM_SUPPLIES] = { + "VA", + "VP", +}; + +struct cs53l30_private { + struct regulator_bulk_data supplies[CS53L30_NUM_SUPPLIES]; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + struct gpio_desc *mute_gpio; + struct clk *mclk; + bool use_sdout2; + u32 mclk_rate; +}; + +static const struct reg_default cs53l30_reg_defaults[] = { + { CS53L30_PWRCTL, CS53L30_PWRCTL_DEFAULT }, + { CS53L30_MCLKCTL, CS53L30_MCLKCTL_DEFAULT }, + { CS53L30_INT_SR_CTL, CS53L30_INT_SR_CTL_DEFAULT }, + { CS53L30_MICBIAS_CTL, CS53L30_MICBIAS_CTL_DEFAULT }, + { CS53L30_ASPCFG_CTL, CS53L30_ASPCFG_CTL_DEFAULT }, + { CS53L30_ASP_CTL1, CS53L30_ASP_CTL1_DEFAULT }, + { CS53L30_ASP_TDMTX_CTL1, CS53L30_ASP_TDMTX_CTLx_DEFAULT }, + { CS53L30_ASP_TDMTX_CTL2, CS53L30_ASP_TDMTX_CTLx_DEFAULT }, + { CS53L30_ASP_TDMTX_CTL3, CS53L30_ASP_TDMTX_CTLx_DEFAULT }, + { CS53L30_ASP_TDMTX_CTL4, CS53L30_ASP_TDMTX_CTLx_DEFAULT }, + { CS53L30_ASP_TDMTX_EN1, CS53L30_ASP_TDMTX_ENx_DEFAULT }, + { CS53L30_ASP_TDMTX_EN2, CS53L30_ASP_TDMTX_ENx_DEFAULT }, + { CS53L30_ASP_TDMTX_EN3, CS53L30_ASP_TDMTX_ENx_DEFAULT }, + { CS53L30_ASP_TDMTX_EN4, CS53L30_ASP_TDMTX_ENx_DEFAULT }, + { CS53L30_ASP_TDMTX_EN5, CS53L30_ASP_TDMTX_ENx_DEFAULT }, + { CS53L30_ASP_TDMTX_EN6, CS53L30_ASP_TDMTX_ENx_DEFAULT }, + { CS53L30_ASP_CTL2, CS53L30_ASP_CTL2_DEFAULT }, + { CS53L30_SFT_RAMP, CS53L30_SFT_RMP_DEFAULT }, + { CS53L30_LRCK_CTL1, CS53L30_LRCK_CTLx_DEFAULT }, + { CS53L30_LRCK_CTL2, CS53L30_LRCK_CTLx_DEFAULT }, + { CS53L30_MUTEP_CTL1, CS53L30_MUTEP_CTL1_DEFAULT }, + { CS53L30_MUTEP_CTL2, CS53L30_MUTEP_CTL2_DEFAULT }, + { CS53L30_INBIAS_CTL1, CS53L30_INBIAS_CTL1_DEFAULT }, + { CS53L30_INBIAS_CTL2, CS53L30_INBIAS_CTL2_DEFAULT }, + { CS53L30_DMIC1_STR_CTL, CS53L30_DMIC1_STR_CTL_DEFAULT }, + { CS53L30_DMIC2_STR_CTL, CS53L30_DMIC2_STR_CTL_DEFAULT }, + { CS53L30_ADCDMIC1_CTL1, CS53L30_ADCDMICx_CTL1_DEFAULT }, + { CS53L30_ADCDMIC1_CTL2, CS53L30_ADCDMIC1_CTL2_DEFAULT }, + { CS53L30_ADC1_CTL3, CS53L30_ADCx_CTL3_DEFAULT }, + { CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_CTL_DEFAULT }, + { CS53L30_ADC1A_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT }, + { CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT }, + { CS53L30_ADC1A_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT }, + { CS53L30_ADC1B_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT }, + { CS53L30_ADCDMIC2_CTL1, CS53L30_ADCDMICx_CTL1_DEFAULT }, + { CS53L30_ADCDMIC2_CTL2, CS53L30_ADCDMIC1_CTL2_DEFAULT }, + { CS53L30_ADC2_CTL3, CS53L30_ADCx_CTL3_DEFAULT }, + { CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_CTL_DEFAULT }, + { CS53L30_ADC2A_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT }, + { CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT }, + { CS53L30_ADC2A_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT }, + { CS53L30_ADC2B_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT }, + { CS53L30_INT_MASK, CS53L30_DEVICE_INT_MASK }, +}; + +static bool cs53l30_volatile_register(struct device *dev, unsigned int reg) +{ + if (reg == CS53L30_IS) + return true; + else + return false; +} + +static bool cs53l30_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS53L30_DEVID_AB: + case CS53L30_DEVID_CD: + case CS53L30_DEVID_E: + case CS53L30_REVID: + case CS53L30_IS: + return false; + default: + return true; + } +} + +static bool cs53l30_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS53L30_DEVID_AB: + case CS53L30_DEVID_CD: + case CS53L30_DEVID_E: + case CS53L30_REVID: + case CS53L30_PWRCTL: + case CS53L30_MCLKCTL: + case CS53L30_INT_SR_CTL: + case CS53L30_MICBIAS_CTL: + case CS53L30_ASPCFG_CTL: + case CS53L30_ASP_CTL1: + case CS53L30_ASP_TDMTX_CTL1: + case CS53L30_ASP_TDMTX_CTL2: + case CS53L30_ASP_TDMTX_CTL3: + case CS53L30_ASP_TDMTX_CTL4: + case CS53L30_ASP_TDMTX_EN1: + case CS53L30_ASP_TDMTX_EN2: + case CS53L30_ASP_TDMTX_EN3: + case CS53L30_ASP_TDMTX_EN4: + case CS53L30_ASP_TDMTX_EN5: + case CS53L30_ASP_TDMTX_EN6: + case CS53L30_ASP_CTL2: + case CS53L30_SFT_RAMP: + case CS53L30_LRCK_CTL1: + case CS53L30_LRCK_CTL2: + case CS53L30_MUTEP_CTL1: + case CS53L30_MUTEP_CTL2: + case CS53L30_INBIAS_CTL1: + case CS53L30_INBIAS_CTL2: + case CS53L30_DMIC1_STR_CTL: + case CS53L30_DMIC2_STR_CTL: + case CS53L30_ADCDMIC1_CTL1: + case CS53L30_ADCDMIC1_CTL2: + case CS53L30_ADC1_CTL3: + case CS53L30_ADC1_NG_CTL: + case CS53L30_ADC1A_AFE_CTL: + case CS53L30_ADC1B_AFE_CTL: + case CS53L30_ADC1A_DIG_VOL: + case CS53L30_ADC1B_DIG_VOL: + case CS53L30_ADCDMIC2_CTL1: + case CS53L30_ADCDMIC2_CTL2: + case CS53L30_ADC2_CTL3: + case CS53L30_ADC2_NG_CTL: + case CS53L30_ADC2A_AFE_CTL: + case CS53L30_ADC2B_AFE_CTL: + case CS53L30_ADC2A_DIG_VOL: + case CS53L30_ADC2B_DIG_VOL: + case CS53L30_INT_MASK: + return true; + default: + return false; + } +} + +static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2000, 0); +static DECLARE_TLV_DB_SCALE(adc_ng_boost_tlv, 0, 3000, 0); +static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0); +static DECLARE_TLV_DB_SCALE(dig_tlv, -9600, 100, 1); +static DECLARE_TLV_DB_SCALE(pga_preamp_tlv, 0, 10000, 0); + +static const char * const input1_sel_text[] = { + "DMIC1 On AB In", + "DMIC1 On A In", + "DMIC1 On B In", + "ADC1 On AB In", + "ADC1 On A In", + "ADC1 On B In", + "DMIC1 Off ADC1 Off", +}; + +static unsigned int const input1_sel_values[] = { + CS53L30_CH_TYPE, + CS53L30_ADCxB_PDN | CS53L30_CH_TYPE, + CS53L30_ADCxA_PDN | CS53L30_CH_TYPE, + CS53L30_DMICx_PDN, + CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN, + CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN, + CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN, +}; + +static const char * const input2_sel_text[] = { + "DMIC2 On AB In", + "DMIC2 On A In", + "DMIC2 On B In", + "ADC2 On AB In", + "ADC2 On A In", + "ADC2 On B In", + "DMIC2 Off ADC2 Off", +}; + +static unsigned int const input2_sel_values[] = { + 0x0, + CS53L30_ADCxB_PDN, + CS53L30_ADCxA_PDN, + CS53L30_DMICx_PDN, + CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN, + CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN, + CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN, +}; + +static const char * const input1_route_sel_text[] = { + "ADC1_SEL", "DMIC1_SEL", +}; + +static const struct soc_enum input1_route_sel_enum = + SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, CS53L30_CH_TYPE_SHIFT, + ARRAY_SIZE(input1_route_sel_text), + input1_route_sel_text); + +static SOC_VALUE_ENUM_SINGLE_DECL(input1_sel_enum, CS53L30_ADCDMIC1_CTL1, 0, + CS53L30_ADCDMICx_PDN_MASK, input1_sel_text, + input1_sel_values); + +static const struct snd_kcontrol_new input1_route_sel_mux = + SOC_DAPM_ENUM("Input 1 Route", input1_route_sel_enum); + +static const char * const input2_route_sel_text[] = { + "ADC2_SEL", "DMIC2_SEL", +}; + +/* Note: CS53L30_ADCDMIC1_CTL1 CH_TYPE controls inputs 1 and 2 */ +static const struct soc_enum input2_route_sel_enum = + SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, 0, + ARRAY_SIZE(input2_route_sel_text), + input2_route_sel_text); + +static SOC_VALUE_ENUM_SINGLE_DECL(input2_sel_enum, CS53L30_ADCDMIC2_CTL1, 0, + CS53L30_ADCDMICx_PDN_MASK, input2_sel_text, + input2_sel_values); + +static const struct snd_kcontrol_new input2_route_sel_mux = + SOC_DAPM_ENUM("Input 2 Route", input2_route_sel_enum); + +/* + * TB = 6144*(MCLK(int) scaling factor)/MCLK(internal) + * TB - Time base + * NOTE: If MCLK_INT_SCALE = 0, then TB=1 + */ +static const char * const cs53l30_ng_delay_text[] = { + "TB*50ms", "TB*100ms", "TB*150ms", "TB*200ms", +}; + +static const struct soc_enum adc1_ng_delay_enum = + SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT, + ARRAY_SIZE(cs53l30_ng_delay_text), + cs53l30_ng_delay_text); + +static const struct soc_enum adc2_ng_delay_enum = + SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT, + ARRAY_SIZE(cs53l30_ng_delay_text), + cs53l30_ng_delay_text); + +/* The noise gate threshold selected will depend on NG Boost */ +static const char * const cs53l30_ng_thres_text[] = { + "-64dB/-34dB", "-66dB/-36dB", "-70dB/-40dB", "-73dB/-43dB", + "-76dB/-46dB", "-82dB/-52dB", "-58dB", "-64dB", +}; + +static const struct soc_enum adc1_ng_thres_enum = + SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT, + ARRAY_SIZE(cs53l30_ng_thres_text), + cs53l30_ng_thres_text); + +static const struct soc_enum adc2_ng_thres_enum = + SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT, + ARRAY_SIZE(cs53l30_ng_thres_text), + cs53l30_ng_thres_text); + +/* Corner frequencies are with an Fs of 48kHz. */ +static const char * const hpf_corner_freq_text[] = { + "1.86Hz", "120Hz", "235Hz", "466Hz", +}; + +static const struct soc_enum adc1_hpf_enum = + SOC_ENUM_SINGLE(CS53L30_ADC1_CTL3, CS53L30_ADCx_HPF_CF_SHIFT, + ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text); + +static const struct soc_enum adc2_hpf_enum = + SOC_ENUM_SINGLE(CS53L30_ADC2_CTL3, CS53L30_ADCx_HPF_CF_SHIFT, + ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text); + +static const struct snd_kcontrol_new cs53l30_snd_controls[] = { + SOC_SINGLE("Digital Soft-Ramp Switch", CS53L30_SFT_RAMP, + CS53L30_DIGSFT_SHIFT, 1, 0), + SOC_SINGLE("ADC1 Noise Gate Ganging Switch", CS53L30_ADC1_CTL3, + CS53L30_ADCx_NG_ALL_SHIFT, 1, 0), + SOC_SINGLE("ADC2 Noise Gate Ganging Switch", CS53L30_ADC2_CTL3, + CS53L30_ADCx_NG_ALL_SHIFT, 1, 0), + SOC_SINGLE("ADC1A Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL, + CS53L30_ADCxA_NG_SHIFT, 1, 0), + SOC_SINGLE("ADC1B Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL, + CS53L30_ADCxB_NG_SHIFT, 1, 0), + SOC_SINGLE("ADC2A Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL, + CS53L30_ADCxA_NG_SHIFT, 1, 0), + SOC_SINGLE("ADC2B Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL, + CS53L30_ADCxB_NG_SHIFT, 1, 0), + SOC_SINGLE("ADC1 Notch Filter Switch", CS53L30_ADCDMIC1_CTL2, + CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1), + SOC_SINGLE("ADC2 Notch Filter Switch", CS53L30_ADCDMIC2_CTL2, + CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1), + SOC_SINGLE("ADC1A Invert Switch", CS53L30_ADCDMIC1_CTL2, + CS53L30_ADCxA_INV_SHIFT, 1, 0), + SOC_SINGLE("ADC1B Invert Switch", CS53L30_ADCDMIC1_CTL2, + CS53L30_ADCxB_INV_SHIFT, 1, 0), + SOC_SINGLE("ADC2A Invert Switch", CS53L30_ADCDMIC2_CTL2, + CS53L30_ADCxA_INV_SHIFT, 1, 0), + SOC_SINGLE("ADC2B Invert Switch", CS53L30_ADCDMIC2_CTL2, + CS53L30_ADCxB_INV_SHIFT, 1, 0), + + SOC_SINGLE_TLV("ADC1A Digital Boost Volume", CS53L30_ADCDMIC1_CTL2, + CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv), + SOC_SINGLE_TLV("ADC1B Digital Boost Volume", CS53L30_ADCDMIC1_CTL2, + CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv), + SOC_SINGLE_TLV("ADC2A Digital Boost Volume", CS53L30_ADCDMIC2_CTL2, + CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv), + SOC_SINGLE_TLV("ADC2B Digital Boost Volume", CS53L30_ADCDMIC2_CTL2, + CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv), + SOC_SINGLE_TLV("ADC1 NG Boost Volume", CS53L30_ADC1_NG_CTL, + CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv), + SOC_SINGLE_TLV("ADC2 NG Boost Volume", CS53L30_ADC2_NG_CTL, + CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv), + + SOC_DOUBLE_R_TLV("ADC1 Preamplifier Volume", CS53L30_ADC1A_AFE_CTL, + CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT, + 2, 0, pga_preamp_tlv), + SOC_DOUBLE_R_TLV("ADC2 Preamplifier Volume", CS53L30_ADC2A_AFE_CTL, + CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT, + 2, 0, pga_preamp_tlv), + + SOC_ENUM("Input 1 Channel Select", input1_sel_enum), + SOC_ENUM("Input 2 Channel Select", input2_sel_enum), + + SOC_ENUM("ADC1 HPF Select", adc1_hpf_enum), + SOC_ENUM("ADC2 HPF Select", adc2_hpf_enum), + SOC_ENUM("ADC1 NG Threshold", adc1_ng_thres_enum), + SOC_ENUM("ADC2 NG Threshold", adc2_ng_thres_enum), + SOC_ENUM("ADC1 NG Delay", adc1_ng_delay_enum), + SOC_ENUM("ADC2 NG Delay", adc2_ng_delay_enum), + + SOC_SINGLE_SX_TLV("ADC1A PGA Volume", + CS53L30_ADC1A_AFE_CTL, 0, 0x34, 0x24, pga_tlv), + SOC_SINGLE_SX_TLV("ADC1B PGA Volume", + CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x24, pga_tlv), + SOC_SINGLE_SX_TLV("ADC2A PGA Volume", + CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x24, pga_tlv), + SOC_SINGLE_SX_TLV("ADC2B PGA Volume", + CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x24, pga_tlv), + + SOC_SINGLE_SX_TLV("ADC1A Digital Volume", + CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv), + SOC_SINGLE_SX_TLV("ADC1B Digital Volume", + CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv), + SOC_SINGLE_SX_TLV("ADC2A Digital Volume", + CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv), + SOC_SINGLE_SX_TLV("ADC2B Digital Volume", + CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x6C, dig_tlv), +}; + +static const struct snd_soc_dapm_widget cs53l30_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN1_DMIC1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_INPUT("IN3_DMIC2"), + SND_SOC_DAPM_INPUT("IN4"), + SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS53L30_MICBIAS_CTL, + CS53L30_MIC1_BIAS_PDN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS53L30_MICBIAS_CTL, + CS53L30_MIC2_BIAS_PDN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC3 Bias", CS53L30_MICBIAS_CTL, + CS53L30_MIC3_BIAS_PDN_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC4 Bias", CS53L30_MICBIAS_CTL, + CS53L30_MIC4_BIAS_PDN_SHIFT, 1, NULL, 0), + + SND_SOC_DAPM_AIF_OUT("ASP_SDOUT1", NULL, 0, CS53L30_ASP_CTL1, + CS53L30_ASP_SDOUTx_PDN_SHIFT, 1), + SND_SOC_DAPM_AIF_OUT("ASP_SDOUT2", NULL, 0, CS53L30_ASP_CTL2, + CS53L30_ASP_SDOUTx_PDN_SHIFT, 1), + + SND_SOC_DAPM_MUX("Input Mux 1", SND_SOC_NOPM, 0, 0, + &input1_route_sel_mux), + SND_SOC_DAPM_MUX("Input Mux 2", SND_SOC_NOPM, 0, 0, + &input2_route_sel_mux), + + SND_SOC_DAPM_ADC("ADC1A", NULL, CS53L30_ADCDMIC1_CTL1, + CS53L30_ADCxA_PDN_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC1B", NULL, CS53L30_ADCDMIC1_CTL1, + CS53L30_ADCxB_PDN_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC2A", NULL, CS53L30_ADCDMIC2_CTL1, + CS53L30_ADCxA_PDN_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC2B", NULL, CS53L30_ADCDMIC2_CTL1, + CS53L30_ADCxB_PDN_SHIFT, 1), + SND_SOC_DAPM_ADC("DMIC1", NULL, CS53L30_ADCDMIC1_CTL1, + CS53L30_DMICx_PDN_SHIFT, 1), + SND_SOC_DAPM_ADC("DMIC2", NULL, CS53L30_ADCDMIC2_CTL1, + CS53L30_DMICx_PDN_SHIFT, 1), +}; + +static const struct snd_soc_dapm_route cs53l30_dapm_routes[] = { + /* ADC Input Paths */ + {"ADC1A", NULL, "IN1_DMIC1"}, + {"Input Mux 1", "ADC1_SEL", "ADC1A"}, + {"ADC1B", NULL, "IN2"}, + + {"ADC2A", NULL, "IN3_DMIC2"}, + {"Input Mux 2", "ADC2_SEL", "ADC2A"}, + {"ADC2B", NULL, "IN4"}, + + /* MIC Bias Paths */ + {"ADC1A", NULL, "MIC1 Bias"}, + {"ADC1B", NULL, "MIC2 Bias"}, + {"ADC2A", NULL, "MIC3 Bias"}, + {"ADC2B", NULL, "MIC4 Bias"}, + + /* DMIC Paths */ + {"DMIC1", NULL, "IN1_DMIC1"}, + {"Input Mux 1", "DMIC1_SEL", "DMIC1"}, + + {"DMIC2", NULL, "IN3_DMIC2"}, + {"Input Mux 2", "DMIC2_SEL", "DMIC2"}, +}; + +static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout1[] = { + /* Output Paths when using SDOUT1 only */ + {"ASP_SDOUT1", NULL, "ADC1A" }, + {"ASP_SDOUT1", NULL, "Input Mux 1"}, + {"ASP_SDOUT1", NULL, "ADC1B"}, + + {"ASP_SDOUT1", NULL, "ADC2A"}, + {"ASP_SDOUT1", NULL, "Input Mux 2"}, + {"ASP_SDOUT1", NULL, "ADC2B"}, + + {"Capture", NULL, "ASP_SDOUT1"}, +}; + +static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout2[] = { + /* Output Paths when using both SDOUT1 and SDOUT2 */ + {"ASP_SDOUT1", NULL, "ADC1A" }, + {"ASP_SDOUT1", NULL, "Input Mux 1"}, + {"ASP_SDOUT1", NULL, "ADC1B"}, + + {"ASP_SDOUT2", NULL, "ADC2A"}, + {"ASP_SDOUT2", NULL, "Input Mux 2"}, + {"ASP_SDOUT2", NULL, "ADC2B"}, + + {"Capture", NULL, "ASP_SDOUT1"}, + {"Capture", NULL, "ASP_SDOUT2"}, +}; + +struct cs53l30_mclk_div { + u32 mclk_rate; + u32 srate; + u8 asp_rate; + u8 internal_fs_ratio; + u8 mclk_int_scale; +}; + +static const struct cs53l30_mclk_div cs53l30_mclk_coeffs[] = { + /* NOTE: Enable MCLK_INT_SCALE to save power. */ + + /* MCLK, Sample Rate, asp_rate, internal_fs_ratio, mclk_int_scale */ + {5644800, 11025, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {5644800, 22050, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {5644800, 44100, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + + {6000000, 8000, 0x1, 0, CS53L30_MCLK_INT_SCALE}, + {6000000, 11025, 0x2, 0, CS53L30_MCLK_INT_SCALE}, + {6000000, 12000, 0x4, 0, CS53L30_MCLK_INT_SCALE}, + {6000000, 16000, 0x5, 0, CS53L30_MCLK_INT_SCALE}, + {6000000, 22050, 0x6, 0, CS53L30_MCLK_INT_SCALE}, + {6000000, 24000, 0x8, 0, CS53L30_MCLK_INT_SCALE}, + {6000000, 32000, 0x9, 0, CS53L30_MCLK_INT_SCALE}, + {6000000, 44100, 0xA, 0, CS53L30_MCLK_INT_SCALE}, + {6000000, 48000, 0xC, 0, CS53L30_MCLK_INT_SCALE}, + + {6144000, 8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6144000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6144000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6144000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6144000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6144000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6144000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6144000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6144000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + + {6400000, 8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6400000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6400000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6400000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6400000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6400000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6400000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6400000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, + {6400000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE}, +}; + +struct cs53l30_mclkx_div { + u32 mclkx; + u8 ratio; + u8 mclkdiv; +}; + +static const struct cs53l30_mclkx_div cs53l30_mclkx_coeffs[] = { + {5644800, 1, CS53L30_MCLK_DIV_BY_1}, + {6000000, 1, CS53L30_MCLK_DIV_BY_1}, + {6144000, 1, CS53L30_MCLK_DIV_BY_1}, + {11289600, 2, CS53L30_MCLK_DIV_BY_2}, + {12288000, 2, CS53L30_MCLK_DIV_BY_2}, + {12000000, 2, CS53L30_MCLK_DIV_BY_2}, + {19200000, 3, CS53L30_MCLK_DIV_BY_3}, +}; + +static int cs53l30_get_mclkx_coeff(int mclkx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs53l30_mclkx_coeffs); i++) { + if (cs53l30_mclkx_coeffs[i].mclkx == mclkx) + return i; + } + + return -EINVAL; +} + +static int cs53l30_get_mclk_coeff(int mclk_rate, int srate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs53l30_mclk_coeffs); i++) { + if (cs53l30_mclk_coeffs[i].mclk_rate == mclk_rate && + cs53l30_mclk_coeffs[i].srate == srate) + return i; + } + + return -EINVAL; +} + +static int cs53l30_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component); + int mclkx_coeff; + u32 mclk_rate; + + /* MCLKX -> MCLK */ + mclkx_coeff = cs53l30_get_mclkx_coeff(freq); + if (mclkx_coeff < 0) + return mclkx_coeff; + + mclk_rate = cs53l30_mclkx_coeffs[mclkx_coeff].mclkx / + cs53l30_mclkx_coeffs[mclkx_coeff].ratio; + + regmap_update_bits(priv->regmap, CS53L30_MCLKCTL, + CS53L30_MCLK_DIV_MASK, + cs53l30_mclkx_coeffs[mclkx_coeff].mclkdiv); + + priv->mclk_rate = mclk_rate; + + return 0; +} + +static int cs53l30_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component); + u8 aspcfg = 0, aspctl1 = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aspcfg |= CS53L30_ASP_MS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* DAI mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* Set TDM_PDN to turn off TDM mode -- Reset default */ + aspctl1 |= CS53L30_ASP_TDM_PDN; + break; + case SND_SOC_DAIFMT_DSP_A: + /* + * Clear TDM_PDN to turn on TDM mode; Use ASP_SCLK_INV = 0 + * with SHIFT_LEFT = 1 combination as Figure 4-13 shows in + * the CS53L30 datasheet + */ + aspctl1 |= CS53L30_SHIFT_LEFT; + break; + default: + return -EINVAL; + } + + /* Check to see if the SCLK is inverted */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: + aspcfg ^= CS53L30_ASP_SCLK_INV; + break; + default: + break; + } + + regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL, + CS53L30_ASP_MS | CS53L30_ASP_SCLK_INV, aspcfg); + + regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1, + CS53L30_ASP_TDM_PDN | CS53L30_SHIFT_LEFT, aspctl1); + + return 0; +} + +static int cs53l30_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component); + int srate = params_rate(params); + int mclk_coeff; + + /* MCLK -> srate */ + mclk_coeff = cs53l30_get_mclk_coeff(priv->mclk_rate, srate); + if (mclk_coeff < 0) + return -EINVAL; + + regmap_update_bits(priv->regmap, CS53L30_INT_SR_CTL, + CS53L30_INTRNL_FS_RATIO_MASK, + cs53l30_mclk_coeffs[mclk_coeff].internal_fs_ratio); + + regmap_update_bits(priv->regmap, CS53L30_MCLKCTL, + CS53L30_MCLK_INT_SCALE_MASK, + cs53l30_mclk_coeffs[mclk_coeff].mclk_int_scale); + + regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL, + CS53L30_ASP_RATE_MASK, + cs53l30_mclk_coeffs[mclk_coeff].asp_rate); + + return 0; +} + +static int cs53l30_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct cs53l30_private *priv = snd_soc_component_get_drvdata(component); + unsigned int reg; + int i, inter_max_check, ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) + regmap_update_bits(priv->regmap, CS53L30_PWRCTL, + CS53L30_PDN_LP_MASK, 0); + break; + case SND_SOC_BIAS_STANDBY: + if (dapm->bias_level == SND_SOC_BIAS_OFF) { + ret = clk_prepare_enable(priv->mclk); + if (ret) { + dev_err(component->dev, + "failed to enable MCLK: %d\n", ret); + return ret; + } + regmap_update_bits(priv->regmap, CS53L30_MCLKCTL, + CS53L30_MCLK_DIS_MASK, 0); + regmap_update_bits(priv->regmap, CS53L30_PWRCTL, + CS53L30_PDN_ULP_MASK, 0); + msleep(50); + } else { + regmap_update_bits(priv->regmap, CS53L30_PWRCTL, + CS53L30_PDN_ULP_MASK, + CS53L30_PDN_ULP); + } + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(priv->regmap, CS53L30_INT_MASK, + CS53L30_PDN_DONE, 0); + /* + * If digital softramp is set, the amount of time required + * for power down increases and depends on the digital + * volume setting. + */ + + /* Set the max possible time if digsft is set */ + regmap_read(priv->regmap, CS53L30_SFT_RAMP, ®); + if (reg & CS53L30_DIGSFT_MASK) + inter_max_check = CS53L30_PDN_POLL_MAX; + else + inter_max_check = 10; + + regmap_update_bits(priv->regmap, CS53L30_PWRCTL, + CS53L30_PDN_ULP_MASK, + CS53L30_PDN_ULP); + /* PDN_DONE will take a min of 20ms to be set.*/ + msleep(20); + /* Clr status */ + regmap_read(priv->regmap, CS53L30_IS, ®); + for (i = 0; i < inter_max_check; i++) { + if (inter_max_check < 10) { + usleep_range(1000, 1100); + regmap_read(priv->regmap, CS53L30_IS, ®); + if (reg & CS53L30_PDN_DONE) + break; + } else { + usleep_range(10000, 10100); + regmap_read(priv->regmap, CS53L30_IS, ®); + if (reg & CS53L30_PDN_DONE) + break; + } + } + /* PDN_DONE is set. We now can disable the MCLK */ + regmap_update_bits(priv->regmap, CS53L30_INT_MASK, + CS53L30_PDN_DONE, CS53L30_PDN_DONE); + regmap_update_bits(priv->regmap, CS53L30_MCLKCTL, + CS53L30_MCLK_DIS_MASK, + CS53L30_MCLK_DIS); + clk_disable_unprepare(priv->mclk); + break; + } + + return 0; +} + +static int cs53l30_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component); + u8 val = tristate ? CS53L30_ASP_3ST : 0; + + return regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1, + CS53L30_ASP_3ST_MASK, val); +} + +static unsigned int const cs53l30_src_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 +}; + +static const struct snd_pcm_hw_constraint_list src_constraints = { + .count = ARRAY_SIZE(cs53l30_src_rates), + .list = cs53l30_src_rates, +}; + +static int cs53l30_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &src_constraints); + + return 0; +} + +/* + * Note: CS53L30 counts the slot number per byte while ASoC counts the slot + * number per slot_width. So there is a difference between the slots of ASoC + * and the slots of CS53L30. + */ +static int cs53l30_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component); + unsigned int loc[CS53L30_TDM_SLOT_MAX] = {48, 48, 48, 48}; + unsigned int slot_next, slot_step; + u64 tx_enable = 0; + int i; + + if (!rx_mask) { + dev_err(dai->dev, "rx masks must not be 0\n"); + return -EINVAL; + } + + /* Assuming slot_width is not supposed to be greater than 64 */ + if (slots <= 0 || slot_width <= 0 || slot_width > 64) { + dev_err(dai->dev, "invalid slot number or slot width\n"); + return -EINVAL; + } + + if (slot_width & 0x7) { + dev_err(dai->dev, "slot width must count in byte\n"); + return -EINVAL; + } + + /* How many bytes in each ASoC slot */ + slot_step = slot_width >> 3; + + for (i = 0; rx_mask && i < CS53L30_TDM_SLOT_MAX; i++) { + /* Find the first slot from LSB */ + slot_next = __ffs(rx_mask); + /* Save the slot location by converting to CS53L30 slot */ + loc[i] = slot_next * slot_step; + /* Create the mask of CS53L30 slot */ + tx_enable |= (u64)((u64)(1 << slot_step) - 1) << (u64)loc[i]; + /* Clear this slot from rx_mask */ + rx_mask &= ~(1 << slot_next); + } + + /* Error out to avoid slot shift */ + if (rx_mask && i == CS53L30_TDM_SLOT_MAX) { + dev_err(dai->dev, "rx_mask exceeds max slot number: %d\n", + CS53L30_TDM_SLOT_MAX); + return -EINVAL; + } + + /* Validate the last active CS53L30 slot */ + slot_next = loc[i - 1] + slot_step - 1; + if (slot_next > 47) { + dev_err(dai->dev, "slot selection out of bounds: %u\n", + slot_next); + return -EINVAL; + } + + for (i = 0; i < CS53L30_TDM_SLOT_MAX && loc[i] != 48; i++) { + regmap_update_bits(priv->regmap, CS53L30_ASP_TDMTX_CTL(i), + CS53L30_ASP_CHx_TX_LOC_MASK, loc[i]); + dev_dbg(dai->dev, "loc[%d]=%x\n", i, loc[i]); + } + + for (i = 0; i < CS53L30_ASP_TDMTX_ENx_MAX && tx_enable; i++) { + regmap_write(priv->regmap, CS53L30_ASP_TDMTX_ENx(i), + tx_enable & 0xff); + tx_enable >>= 8; + dev_dbg(dai->dev, "en_reg=%x, tx_enable=%llx\n", + CS53L30_ASP_TDMTX_ENx(i), tx_enable & 0xff); + } + + return 0; +} + +static int cs53l30_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct cs53l30_private *priv = snd_soc_component_get_drvdata(dai->component); + + gpiod_set_value_cansleep(priv->mute_gpio, mute); + + return 0; +} + +/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */ +#define CS53L30_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT) + +#define CS53L30_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops cs53l30_ops = { + .startup = cs53l30_pcm_startup, + .hw_params = cs53l30_pcm_hw_params, + .set_fmt = cs53l30_set_dai_fmt, + .set_sysclk = cs53l30_set_sysclk, + .set_tristate = cs53l30_set_tristate, + .set_tdm_slot = cs53l30_set_dai_tdm_slot, + .mute_stream = cs53l30_mute_stream, +}; + +static struct snd_soc_dai_driver cs53l30_dai = { + .name = "cs53l30", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = CS53L30_RATES, + .formats = CS53L30_FORMATS, + }, + .ops = &cs53l30_ops, + .symmetric_rate = 1, +}; + +static int cs53l30_component_probe(struct snd_soc_component *component) +{ + struct cs53l30_private *priv = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + if (priv->use_sdout2) + snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout2, + ARRAY_SIZE(cs53l30_dapm_routes_sdout2)); + else + snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout1, + ARRAY_SIZE(cs53l30_dapm_routes_sdout1)); + + return 0; +} + +static const struct snd_soc_component_driver cs53l30_driver = { + .probe = cs53l30_component_probe, + .set_bias_level = cs53l30_set_bias_level, + .controls = cs53l30_snd_controls, + .num_controls = ARRAY_SIZE(cs53l30_snd_controls), + .dapm_widgets = cs53l30_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs53l30_dapm_widgets), + .dapm_routes = cs53l30_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs53l30_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct regmap_config cs53l30_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS53L30_MAX_REGISTER, + .reg_defaults = cs53l30_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs53l30_reg_defaults), + .volatile_reg = cs53l30_volatile_register, + .writeable_reg = cs53l30_writeable_register, + .readable_reg = cs53l30_readable_register, + .cache_type = REGCACHE_MAPLE, + + .use_single_read = true, + .use_single_write = true, +}; + +static int cs53l30_i2c_probe(struct i2c_client *client) +{ + const struct device_node *np = client->dev.of_node; + struct device *dev = &client->dev; + struct cs53l30_private *cs53l30; + unsigned int reg; + int ret = 0, i, devid; + u8 val; + + cs53l30 = devm_kzalloc(dev, sizeof(*cs53l30), GFP_KERNEL); + if (!cs53l30) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(cs53l30->supplies); i++) + cs53l30->supplies[i].supply = cs53l30_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs53l30->supplies), + cs53l30->supplies); + if (ret) { + dev_err(dev, "failed to get supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies), + cs53l30->supplies); + if (ret) { + dev_err(dev, "failed to enable supplies: %d\n", ret); + return ret; + } + + /* Reset the Device */ + cs53l30->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs53l30->reset_gpio)) { + ret = PTR_ERR(cs53l30->reset_gpio); + goto error_supplies; + } + + gpiod_set_value_cansleep(cs53l30->reset_gpio, 1); + + i2c_set_clientdata(client, cs53l30); + + cs53l30->mclk_rate = 0; + + cs53l30->regmap = devm_regmap_init_i2c(client, &cs53l30_regmap); + if (IS_ERR(cs53l30->regmap)) { + ret = PTR_ERR(cs53l30->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + goto error; + } + + /* Initialize codec */ + devid = cirrus_read_device_id(cs53l30->regmap, CS53L30_DEVID_AB); + if (devid < 0) { + ret = devid; + dev_err(dev, "Failed to read device ID: %d\n", ret); + goto error; + } + + if (devid != CS53L30_DEVID) { + ret = -ENODEV; + dev_err(dev, "Device ID (%X). Expected %X\n", + devid, CS53L30_DEVID); + goto error; + } + + ret = regmap_read(cs53l30->regmap, CS53L30_REVID, ®); + if (ret < 0) { + dev_err(dev, "failed to get Revision ID: %d\n", ret); + goto error; + } + + /* Check if MCLK provided */ + cs53l30->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(cs53l30->mclk)) { + ret = PTR_ERR(cs53l30->mclk); + goto error; + } + + /* Fetch the MUTE control */ + cs53l30->mute_gpio = devm_gpiod_get_optional(dev, "mute", + GPIOD_OUT_HIGH); + if (IS_ERR(cs53l30->mute_gpio)) { + ret = PTR_ERR(cs53l30->mute_gpio); + goto error; + } + + if (cs53l30->mute_gpio) { + /* Enable MUTE controls via MUTE pin */ + regmap_write(cs53l30->regmap, CS53L30_MUTEP_CTL1, + CS53L30_MUTEP_CTL1_MUTEALL); + /* Flip the polarity of MUTE pin */ + if (gpiod_is_active_low(cs53l30->mute_gpio)) + regmap_update_bits(cs53l30->regmap, CS53L30_MUTEP_CTL2, + CS53L30_MUTE_PIN_POLARITY, 0); + } + + if (!of_property_read_u8(np, "cirrus,micbias-lvl", &val)) + regmap_update_bits(cs53l30->regmap, CS53L30_MICBIAS_CTL, + CS53L30_MIC_BIAS_CTRL_MASK, val); + + if (of_property_read_bool(np, "cirrus,use-sdout2")) + cs53l30->use_sdout2 = true; + + dev_info(dev, "Cirrus Logic CS53L30, Revision: %02X\n", reg & 0xFF); + + ret = devm_snd_soc_register_component(dev, &cs53l30_driver, &cs53l30_dai, 1); + if (ret) { + dev_err(dev, "failed to register component: %d\n", ret); + goto error; + } + + return 0; + +error: + gpiod_set_value_cansleep(cs53l30->reset_gpio, 0); +error_supplies: + regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies), + cs53l30->supplies); + return ret; +} + +static void cs53l30_i2c_remove(struct i2c_client *client) +{ + struct cs53l30_private *cs53l30 = i2c_get_clientdata(client); + + /* Hold down reset */ + gpiod_set_value_cansleep(cs53l30->reset_gpio, 0); + + regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies), + cs53l30->supplies); +} + +#ifdef CONFIG_PM +static int cs53l30_runtime_suspend(struct device *dev) +{ + struct cs53l30_private *cs53l30 = dev_get_drvdata(dev); + + regcache_cache_only(cs53l30->regmap, true); + + /* Hold down reset */ + gpiod_set_value_cansleep(cs53l30->reset_gpio, 0); + + regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies), + cs53l30->supplies); + + return 0; +} + +static int cs53l30_runtime_resume(struct device *dev) +{ + struct cs53l30_private *cs53l30 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies), + cs53l30->supplies); + if (ret) { + dev_err(dev, "failed to enable supplies: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(cs53l30->reset_gpio, 1); + + regcache_cache_only(cs53l30->regmap, false); + ret = regcache_sync(cs53l30->regmap); + if (ret) { + dev_err(dev, "failed to synchronize regcache: %d\n", ret); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops cs53l30_runtime_pm = { + SET_RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume, + NULL) +}; + +static const struct of_device_id cs53l30_of_match[] = { + { .compatible = "cirrus,cs53l30", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, cs53l30_of_match); + +static const struct i2c_device_id cs53l30_id[] = { + { "cs53l30", 0 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs53l30_id); + +static struct i2c_driver cs53l30_i2c_driver = { + .driver = { + .name = "cs53l30", + .of_match_table = cs53l30_of_match, + .pm = &cs53l30_runtime_pm, + }, + .id_table = cs53l30_id, + .probe = cs53l30_i2c_probe, + .remove = cs53l30_i2c_remove, +}; + +module_i2c_driver(cs53l30_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS53L30 driver"); +MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs53l30.h b/sound/soc/codecs/cs53l30.h new file mode 100644 index 0000000000..071547c557 --- /dev/null +++ b/sound/soc/codecs/cs53l30.h @@ -0,0 +1,455 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC CS53L30 codec driver + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Paul Handrigan , + * Tim Howe + */ + +#ifndef __CS53L30_H__ +#define __CS53L30_H__ + +/* I2C Registers */ +#define CS53L30_DEVID_AB 0x01 /* Device ID A & B [RO]. */ +#define CS53L30_DEVID_CD 0x02 /* Device ID C & D [RO]. */ +#define CS53L30_DEVID_E 0x03 /* Device ID E [RO]. */ +#define CS53L30_REVID 0x05 /* Revision ID [RO]. */ +#define CS53L30_PWRCTL 0x06 /* Power Control. */ +#define CS53L30_MCLKCTL 0x07 /* MCLK Control. */ +#define CS53L30_INT_SR_CTL 0x08 /* Internal Sample Rate Control. */ +#define CS53L30_MICBIAS_CTL 0x0A /* Mic Bias Control. */ +#define CS53L30_ASPCFG_CTL 0x0C /* ASP Config Control. */ +#define CS53L30_ASP_CTL1 0x0D /* ASP1 Control. */ +#define CS53L30_ASP_TDMTX_CTL1 0x0E /* ASP1 TDM TX Control 1 */ +#define CS53L30_ASP_TDMTX_CTL2 0x0F /* ASP1 TDM TX Control 2 */ +#define CS53L30_ASP_TDMTX_CTL3 0x10 /* ASP1 TDM TX Control 3 */ +#define CS53L30_ASP_TDMTX_CTL4 0x11 /* ASP1 TDM TX Control 4 */ +#define CS53L30_ASP_TDMTX_EN1 0x12 /* ASP1 TDM TX Enable 1 */ +#define CS53L30_ASP_TDMTX_EN2 0x13 /* ASP1 TDM TX Enable 2 */ +#define CS53L30_ASP_TDMTX_EN3 0x14 /* ASP1 TDM TX Enable 3 */ +#define CS53L30_ASP_TDMTX_EN4 0x15 /* ASP1 TDM TX Enable 4 */ +#define CS53L30_ASP_TDMTX_EN5 0x16 /* ASP1 TDM TX Enable 5 */ +#define CS53L30_ASP_TDMTX_EN6 0x17 /* ASP1 TDM TX Enable 6 */ +#define CS53L30_ASP_CTL2 0x18 /* ASP2 Control. */ +#define CS53L30_SFT_RAMP 0x1A /* Soft Ramp Control. */ +#define CS53L30_LRCK_CTL1 0x1B /* LRCK Control 1. */ +#define CS53L30_LRCK_CTL2 0x1C /* LRCK Control 2. */ +#define CS53L30_MUTEP_CTL1 0x1F /* Mute Pin Control 1. */ +#define CS53L30_MUTEP_CTL2 0x20 /* Mute Pin Control 2. */ +#define CS53L30_INBIAS_CTL1 0x21 /* Input Bias Control 1. */ +#define CS53L30_INBIAS_CTL2 0x22 /* Input Bias Control 2. */ +#define CS53L30_DMIC1_STR_CTL 0x23 /* DMIC1 Stereo Control. */ +#define CS53L30_DMIC2_STR_CTL 0x24 /* DMIC2 Stereo Control. */ +#define CS53L30_ADCDMIC1_CTL1 0x25 /* ADC1/DMIC1 Control 1. */ +#define CS53L30_ADCDMIC1_CTL2 0x26 /* ADC1/DMIC1 Control 2. */ +#define CS53L30_ADC1_CTL3 0x27 /* ADC1 Control 3. */ +#define CS53L30_ADC1_NG_CTL 0x28 /* ADC1 Noise Gate Control. */ +#define CS53L30_ADC1A_AFE_CTL 0x29 /* ADC1A AFE Control. */ +#define CS53L30_ADC1B_AFE_CTL 0x2A /* ADC1B AFE Control. */ +#define CS53L30_ADC1A_DIG_VOL 0x2B /* ADC1A Digital Volume. */ +#define CS53L30_ADC1B_DIG_VOL 0x2C /* ADC1B Digital Volume. */ +#define CS53L30_ADCDMIC2_CTL1 0x2D /* ADC2/DMIC2 Control 1. */ +#define CS53L30_ADCDMIC2_CTL2 0x2E /* ADC2/DMIC2 Control 2. */ +#define CS53L30_ADC2_CTL3 0x2F /* ADC2 Control 3. */ +#define CS53L30_ADC2_NG_CTL 0x30 /* ADC2 Noise Gate Control. */ +#define CS53L30_ADC2A_AFE_CTL 0x31 /* ADC2A AFE Control. */ +#define CS53L30_ADC2B_AFE_CTL 0x32 /* ADC2B AFE Control. */ +#define CS53L30_ADC2A_DIG_VOL 0x33 /* ADC2A Digital Volume. */ +#define CS53L30_ADC2B_DIG_VOL 0x34 /* ADC2B Digital Volume. */ +#define CS53L30_INT_MASK 0x35 /* Interrupt Mask. */ +#define CS53L30_IS 0x36 /* Interrupt Status. */ +#define CS53L30_MAX_REGISTER 0x36 + +#define CS53L30_TDM_SLOT_MAX 4 +#define CS53L30_ASP_TDMTX_CTL(x) (CS53L30_ASP_TDMTX_CTL1 + (x)) +/* x : index for registers; n : index for slot; 8 slots per register */ +#define CS53L30_ASP_TDMTX_ENx(x) (CS53L30_ASP_TDMTX_EN6 - (x)) +#define CS53L30_ASP_TDMTX_ENn(n) CS53L30_ASP_TDMTX_ENx((n) >> 3) +#define CS53L30_ASP_TDMTX_ENx_MAX 6 + +/* Device ID */ +#define CS53L30_DEVID 0x53A30 + +/* PDN_DONE Poll Maximum + * If soft ramp is set it will take much longer to power down + * the system. + */ +#define CS53L30_PDN_POLL_MAX 90 + +/* Bitfield Definitions */ + +/* R6 (0x06) CS53L30_PWRCTL - Power Control */ +#define CS53L30_PDN_ULP_SHIFT 7 +#define CS53L30_PDN_ULP_MASK (1 << CS53L30_PDN_ULP_SHIFT) +#define CS53L30_PDN_ULP (1 << CS53L30_PDN_ULP_SHIFT) +#define CS53L30_PDN_LP_SHIFT 6 +#define CS53L30_PDN_LP_MASK (1 << CS53L30_PDN_LP_SHIFT) +#define CS53L30_PDN_LP (1 << CS53L30_PDN_LP_SHIFT) +#define CS53L30_DISCHARGE_FILT_SHIFT 5 +#define CS53L30_DISCHARGE_FILT_MASK (1 << CS53L30_DISCHARGE_FILT_SHIFT) +#define CS53L30_DISCHARGE_FILT (1 << CS53L30_DISCHARGE_FILT_SHIFT) +#define CS53L30_THMS_PDN_SHIFT 4 +#define CS53L30_THMS_PDN_MASK (1 << CS53L30_THMS_PDN_SHIFT) +#define CS53L30_THMS_PDN (1 << CS53L30_THMS_PDN_SHIFT) + +#define CS53L30_PWRCTL_DEFAULT (CS53L30_THMS_PDN) + +/* R7 (0x07) CS53L30_MCLKCTL - MCLK Control */ +#define CS53L30_MCLK_DIS_SHIFT 7 +#define CS53L30_MCLK_DIS_MASK (1 << CS53L30_MCLK_DIS_SHIFT) +#define CS53L30_MCLK_DIS (1 << CS53L30_MCLK_DIS_SHIFT) +#define CS53L30_MCLK_INT_SCALE_SHIFT 6 +#define CS53L30_MCLK_INT_SCALE_MASK (1 << CS53L30_MCLK_INT_SCALE_SHIFT) +#define CS53L30_MCLK_INT_SCALE (1 << CS53L30_MCLK_INT_SCALE_SHIFT) +#define CS53L30_DMIC_DRIVE_SHIFT 5 +#define CS53L30_DMIC_DRIVE_MASK (1 << CS53L30_DMIC_DRIVE_SHIFT) +#define CS53L30_DMIC_DRIVE (1 << CS53L30_DMIC_DRIVE_SHIFT) +#define CS53L30_MCLK_DIV_SHIFT 2 +#define CS53L30_MCLK_DIV_WIDTH 2 +#define CS53L30_MCLK_DIV_MASK (((1 << CS53L30_MCLK_DIV_WIDTH) - 1) << CS53L30_MCLK_DIV_SHIFT) +#define CS53L30_MCLK_DIV_BY_1 (0x0 << CS53L30_MCLK_DIV_SHIFT) +#define CS53L30_MCLK_DIV_BY_2 (0x1 << CS53L30_MCLK_DIV_SHIFT) +#define CS53L30_MCLK_DIV_BY_3 (0x2 << CS53L30_MCLK_DIV_SHIFT) +#define CS53L30_SYNC_EN_SHIFT 1 +#define CS53L30_SYNC_EN_MASK (1 << CS53L30_SYNC_EN_SHIFT) +#define CS53L30_SYNC_EN (1 << CS53L30_SYNC_EN_SHIFT) + +#define CS53L30_MCLKCTL_DEFAULT (CS53L30_MCLK_DIV_BY_2) + +/* R8 (0x08) CS53L30_INT_SR_CTL - Internal Sample Rate Control */ +#define CS53L30_INTRNL_FS_RATIO_SHIFT 4 +#define CS53L30_INTRNL_FS_RATIO_MASK (1 << CS53L30_INTRNL_FS_RATIO_SHIFT) +#define CS53L30_INTRNL_FS_RATIO (1 << CS53L30_INTRNL_FS_RATIO_SHIFT) +#define CS53L30_MCLK_19MHZ_EN_SHIFT 0 +#define CS53L30_MCLK_19MHZ_EN_MASK (1 << CS53L30_MCLK_19MHZ_EN_SHIFT) +#define CS53L30_MCLK_19MHZ_EN (1 << CS53L30_MCLK_19MHZ_EN_SHIFT) + +/* 0x6 << 1 is reserved bits */ +#define CS53L30_INT_SR_CTL_DEFAULT (CS53L30_INTRNL_FS_RATIO | 0x6 << 1) + +/* R10 (0x0A) CS53L30_MICBIAS_CTL - Mic Bias Control */ +#define CS53L30_MIC4_BIAS_PDN_SHIFT 7 +#define CS53L30_MIC4_BIAS_PDN_MASK (1 << CS53L30_MIC4_BIAS_PDN_SHIFT) +#define CS53L30_MIC4_BIAS_PDN (1 << CS53L30_MIC4_BIAS_PDN_SHIFT) +#define CS53L30_MIC3_BIAS_PDN_SHIFT 6 +#define CS53L30_MIC3_BIAS_PDN_MASK (1 << CS53L30_MIC3_BIAS_PDN_SHIFT) +#define CS53L30_MIC3_BIAS_PDN (1 << CS53L30_MIC3_BIAS_PDN_SHIFT) +#define CS53L30_MIC2_BIAS_PDN_SHIFT 5 +#define CS53L30_MIC2_BIAS_PDN_MASK (1 << CS53L30_MIC2_BIAS_PDN_SHIFT) +#define CS53L30_MIC2_BIAS_PDN (1 << CS53L30_MIC2_BIAS_PDN_SHIFT) +#define CS53L30_MIC1_BIAS_PDN_SHIFT 4 +#define CS53L30_MIC1_BIAS_PDN_MASK (1 << CS53L30_MIC1_BIAS_PDN_SHIFT) +#define CS53L30_MIC1_BIAS_PDN (1 << CS53L30_MIC1_BIAS_PDN_SHIFT) +#define CS53L30_MICx_BIAS_PDN (0xf << CS53L30_MIC1_BIAS_PDN_SHIFT) +#define CS53L30_VP_MIN_SHIFT 2 +#define CS53L30_VP_MIN_MASK (1 << CS53L30_VP_MIN_SHIFT) +#define CS53L30_VP_MIN (1 << CS53L30_VP_MIN_SHIFT) +#define CS53L30_MIC_BIAS_CTRL_SHIFT 0 +#define CS53L30_MIC_BIAS_CTRL_WIDTH 2 +#define CS53L30_MIC_BIAS_CTRL_MASK (((1 << CS53L30_MIC_BIAS_CTRL_WIDTH) - 1) << CS53L30_MIC_BIAS_CTRL_SHIFT) +#define CS53L30_MIC_BIAS_CTRL_HIZ (0 << CS53L30_MIC_BIAS_CTRL_SHIFT) +#define CS53L30_MIC_BIAS_CTRL_1V8 (1 << CS53L30_MIC_BIAS_CTRL_SHIFT) +#define CS53L30_MIC_BIAS_CTRL_2V75 (2 << CS53L30_MIC_BIAS_CTRL_SHIFT) + +#define CS53L30_MICBIAS_CTL_DEFAULT (CS53L30_MICx_BIAS_PDN | CS53L30_VP_MIN) + +/* R12 (0x0C) CS53L30_ASPCFG_CTL - ASP Configuration Control */ +#define CS53L30_ASP_MS_SHIFT 7 +#define CS53L30_ASP_MS_MASK (1 << CS53L30_ASP_MS_SHIFT) +#define CS53L30_ASP_MS (1 << CS53L30_ASP_MS_SHIFT) +#define CS53L30_ASP_SCLK_INV_SHIFT 4 +#define CS53L30_ASP_SCLK_INV_MASK (1 << CS53L30_ASP_SCLK_INV_SHIFT) +#define CS53L30_ASP_SCLK_INV (1 << CS53L30_ASP_SCLK_INV_SHIFT) +#define CS53L30_ASP_RATE_SHIFT 0 +#define CS53L30_ASP_RATE_WIDTH 4 +#define CS53L30_ASP_RATE_MASK (((1 << CS53L30_ASP_RATE_WIDTH) - 1) << CS53L30_ASP_RATE_SHIFT) +#define CS53L30_ASP_RATE_48K (0xc << CS53L30_ASP_RATE_SHIFT) + +#define CS53L30_ASPCFG_CTL_DEFAULT (CS53L30_ASP_RATE_48K) + +/* R13/R24 (0x0D/0x18) CS53L30_ASP_CTL1 & CS53L30_ASP_CTL2 - ASP Control 1~2 */ +#define CS53L30_ASP_TDM_PDN_SHIFT 7 +#define CS53L30_ASP_TDM_PDN_MASK (1 << CS53L30_ASP_TDM_PDN_SHIFT) +#define CS53L30_ASP_TDM_PDN (1 << CS53L30_ASP_TDM_PDN_SHIFT) +#define CS53L30_ASP_SDOUTx_PDN_SHIFT 6 +#define CS53L30_ASP_SDOUTx_PDN_MASK (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT) +#define CS53L30_ASP_SDOUTx_PDN (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT) +#define CS53L30_ASP_3ST_SHIFT 5 +#define CS53L30_ASP_3ST_MASK (1 << CS53L30_ASP_3ST_SHIFT) +#define CS53L30_ASP_3ST (1 << CS53L30_ASP_3ST_SHIFT) +#define CS53L30_SHIFT_LEFT_SHIFT 4 +#define CS53L30_SHIFT_LEFT_MASK (1 << CS53L30_SHIFT_LEFT_SHIFT) +#define CS53L30_SHIFT_LEFT (1 << CS53L30_SHIFT_LEFT_SHIFT) +#define CS53L30_ASP_SDOUTx_DRIVE_SHIFT 0 +#define CS53L30_ASP_SDOUTx_DRIVE_MASK (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT) +#define CS53L30_ASP_SDOUTx_DRIVE (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT) + +#define CS53L30_ASP_CTL1_DEFAULT (CS53L30_ASP_TDM_PDN) +#define CS53L30_ASP_CTL2_DEFAULT (0) + +/* R14 (0x0E) ~ R17 (0x11) CS53L30_ASP_TDMTX_CTLx - ASP TDM TX Control 1~4 */ +#define CS53L30_ASP_CHx_TX_STATE_SHIFT 7 +#define CS53L30_ASP_CHx_TX_STATE_MASK (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT) +#define CS53L30_ASP_CHx_TX_STATE (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT) +#define CS53L30_ASP_CHx_TX_LOC_SHIFT 0 +#define CS53L30_ASP_CHx_TX_LOC_WIDTH 6 +#define CS53L30_ASP_CHx_TX_LOC_MASK (((1 << CS53L30_ASP_CHx_TX_LOC_WIDTH) - 1) << CS53L30_ASP_CHx_TX_LOC_SHIFT) +#define CS53L30_ASP_CHx_TX_LOC_MAX (47 << CS53L30_ASP_CHx_TX_LOC_SHIFT) +#define CS53L30_ASP_CHx_TX_LOC(x) ((x) << CS53L30_ASP_CHx_TX_LOC_SHIFT) + +#define CS53L30_ASP_TDMTX_CTLx_DEFAULT (CS53L30_ASP_CHx_TX_LOC_MAX) + +/* R18 (0x12) ~ R23 (0x17) CS53L30_ASP_TDMTX_ENx - ASP TDM TX Enable 1~6 */ +#define CS53L30_ASP_TDMTX_ENx_DEFAULT (0) + +/* R26 (0x1A) CS53L30_SFT_RAMP - Soft Ramp Control */ +#define CS53L30_DIGSFT_SHIFT 5 +#define CS53L30_DIGSFT_MASK (1 << CS53L30_DIGSFT_SHIFT) +#define CS53L30_DIGSFT (1 << CS53L30_DIGSFT_SHIFT) + +#define CS53L30_SFT_RMP_DEFAULT (0) + +/* R28 (0x1C) CS53L30_LRCK_CTL2 - LRCK Control 2 */ +#define CS53L30_LRCK_50_NPW_SHIFT 3 +#define CS53L30_LRCK_50_NPW_MASK (1 << CS53L30_LRCK_50_NPW_SHIFT) +#define CS53L30_LRCK_50_NPW (1 << CS53L30_LRCK_50_NPW_SHIFT) +#define CS53L30_LRCK_TPWH_SHIFT 0 +#define CS53L30_LRCK_TPWH_WIDTH 3 +#define CS53L30_LRCK_TPWH_MASK (((1 << CS53L30_LRCK_TPWH_WIDTH) - 1) << CS53L30_LRCK_TPWH_SHIFT) +#define CS53L30_LRCK_TPWH(x) (((x) << CS53L30_LRCK_TPWH_SHIFT) & CS53L30_LRCK_TPWH_MASK) + +#define CS53L30_LRCK_CTLx_DEFAULT (0) + +/* R31 (0x1F) CS53L30_MUTEP_CTL1 - MUTE Pin Control 1 */ +#define CS53L30_MUTE_PDN_ULP_SHIFT 7 +#define CS53L30_MUTE_PDN_ULP_MASK (1 << CS53L30_MUTE_PDN_ULP_SHIFT) +#define CS53L30_MUTE_PDN_ULP (1 << CS53L30_MUTE_PDN_ULP_SHIFT) +#define CS53L30_MUTE_PDN_LP_SHIFT 6 +#define CS53L30_MUTE_PDN_LP_MASK (1 << CS53L30_MUTE_PDN_LP_SHIFT) +#define CS53L30_MUTE_PDN_LP (1 << CS53L30_MUTE_PDN_LP_SHIFT) +#define CS53L30_MUTE_M4B_PDN_SHIFT 4 +#define CS53L30_MUTE_M4B_PDN_MASK (1 << CS53L30_MUTE_M4B_PDN_SHIFT) +#define CS53L30_MUTE_M4B_PDN (1 << CS53L30_MUTE_M4B_PDN_SHIFT) +#define CS53L30_MUTE_M3B_PDN_SHIFT 3 +#define CS53L30_MUTE_M3B_PDN_MASK (1 << CS53L30_MUTE_M3B_PDN_SHIFT) +#define CS53L30_MUTE_M3B_PDN (1 << CS53L30_MUTE_M3B_PDN_SHIFT) +#define CS53L30_MUTE_M2B_PDN_SHIFT 2 +#define CS53L30_MUTE_M2B_PDN_MASK (1 << CS53L30_MUTE_M2B_PDN_SHIFT) +#define CS53L30_MUTE_M2B_PDN (1 << CS53L30_MUTE_M2B_PDN_SHIFT) +#define CS53L30_MUTE_M1B_PDN_SHIFT 1 +#define CS53L30_MUTE_M1B_PDN_MASK (1 << CS53L30_MUTE_M1B_PDN_SHIFT) +#define CS53L30_MUTE_M1B_PDN (1 << CS53L30_MUTE_M1B_PDN_SHIFT) +/* Note: be careful - x starts from 0 */ +#define CS53L30_MUTE_MxB_PDN_SHIFT(x) (CS53L30_MUTE_M1B_PDN_SHIFT + (x)) +#define CS53L30_MUTE_MxB_PDN_MASK(x) (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x)) +#define CS53L30_MUTE_MxB_PDN(x) (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x)) +#define CS53L30_MUTE_MB_ALL_PDN_SHIFT 0 +#define CS53L30_MUTE_MB_ALL_PDN_MASK (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT) +#define CS53L30_MUTE_MB_ALL_PDN (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT) + +#define CS53L30_MUTEP_CTL1_MUTEALL (0xdf) +#define CS53L30_MUTEP_CTL1_DEFAULT (0) + +/* R32 (0x20) CS53L30_MUTEP_CTL2 - MUTE Pin Control 2 */ +#define CS53L30_MUTE_PIN_POLARITY_SHIFT 7 +#define CS53L30_MUTE_PIN_POLARITY_MASK (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT) +#define CS53L30_MUTE_PIN_POLARITY (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT) +#define CS53L30_MUTE_ASP_TDM_PDN_SHIFT 6 +#define CS53L30_MUTE_ASP_TDM_PDN_MASK (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT) +#define CS53L30_MUTE_ASP_TDM_PDN (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT) +#define CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT 5 +#define CS53L30_MUTE_ASP_SDOUT2_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT) +#define CS53L30_MUTE_ASP_SDOUT2_PDN (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT) +#define CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT 4 +#define CS53L30_MUTE_ASP_SDOUT1_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT) +#define CS53L30_MUTE_ASP_SDOUT1_PDN (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT) +/* Note: be careful - x starts from 0 */ +#define CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x) ((x) + CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT) +#define CS53L30_MUTE_ASP_SDOUTx_PDN_MASK(x) (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x)) +#define CS53L30_MUTE_ASP_SDOUTx_PDN (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x)) +#define CS53L30_MUTE_ADC2B_PDN_SHIFT 3 +#define CS53L30_MUTE_ADC2B_PDN_MASK (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT) +#define CS53L30_MUTE_ADC2B_PDN (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT) +#define CS53L30_MUTE_ADC2A_PDN_SHIFT 2 +#define CS53L30_MUTE_ADC2A_PDN_MASK (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT) +#define CS53L30_MUTE_ADC2A_PDN (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT) +#define CS53L30_MUTE_ADC1B_PDN_SHIFT 1 +#define CS53L30_MUTE_ADC1B_PDN_MASK (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT) +#define CS53L30_MUTE_ADC1B_PDN (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT) +#define CS53L30_MUTE_ADC1A_PDN_SHIFT 0 +#define CS53L30_MUTE_ADC1A_PDN_MASK (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT) +#define CS53L30_MUTE_ADC1A_PDN (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT) + +#define CS53L30_MUTEP_CTL2_DEFAULT (CS53L30_MUTE_PIN_POLARITY) + +/* R33 (0x21) CS53L30_INBIAS_CTL1 - Input Bias Control 1 */ +#define CS53L30_IN4M_BIAS_SHIFT 6 +#define CS53L30_IN4M_BIAS_WIDTH 2 +#define CS53L30_IN4M_BIAS_MASK (((1 << CS53L30_IN4M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT) +#define CS53L30_IN4M_BIAS_OPEN (0 << CS53L30_IN4M_BIAS_SHIFT) +#define CS53L30_IN4M_BIAS_PULL_DOWN (1 << CS53L30_IN4M_BIAS_SHIFT) +#define CS53L30_IN4M_BIAS_VCM (2 << CS53L30_IN4M_BIAS_SHIFT) +#define CS53L30_IN4P_BIAS_SHIFT 4 +#define CS53L30_IN4P_BIAS_WIDTH 2 +#define CS53L30_IN4P_BIAS_MASK (((1 << CS53L30_IN4P_BIAS_WIDTH) - 1) << CS53L30_IN4P_BIAS_SHIFT) +#define CS53L30_IN4P_BIAS_OPEN (0 << CS53L30_IN4P_BIAS_SHIFT) +#define CS53L30_IN4P_BIAS_PULL_DOWN (1 << CS53L30_IN4P_BIAS_SHIFT) +#define CS53L30_IN4P_BIAS_VCM (2 << CS53L30_IN4P_BIAS_SHIFT) +#define CS53L30_IN3M_BIAS_SHIFT 2 +#define CS53L30_IN3M_BIAS_WIDTH 2 +#define CS53L30_IN3M_BIAS_MASK (((1 << CS53L30_IN3M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT) +#define CS53L30_IN3M_BIAS_OPEN (0 << CS53L30_IN3M_BIAS_SHIFT) +#define CS53L30_IN3M_BIAS_PULL_DOWN (1 << CS53L30_IN3M_BIAS_SHIFT) +#define CS53L30_IN3M_BIAS_VCM (2 << CS53L30_IN3M_BIAS_SHIFT) +#define CS53L30_IN3P_BIAS_SHIFT 0 +#define CS53L30_IN3P_BIAS_WIDTH 2 +#define CS53L30_IN3P_BIAS_MASK (((1 << CS53L30_IN3P_BIAS_WIDTH) - 1) << CS53L30_IN3P_BIAS_SHIFT) +#define CS53L30_IN3P_BIAS_OPEN (0 << CS53L30_IN3P_BIAS_SHIFT) +#define CS53L30_IN3P_BIAS_PULL_DOWN (1 << CS53L30_IN3P_BIAS_SHIFT) +#define CS53L30_IN3P_BIAS_VCM (2 << CS53L30_IN3P_BIAS_SHIFT) + +#define CS53L30_INBIAS_CTL1_DEFAULT (CS53L30_IN4M_BIAS_VCM | CS53L30_IN4P_BIAS_VCM |\ + CS53L30_IN3M_BIAS_VCM | CS53L30_IN3P_BIAS_VCM) + +/* R34 (0x22) CS53L30_INBIAS_CTL2 - Input Bias Control 2 */ +#define CS53L30_IN2M_BIAS_SHIFT 6 +#define CS53L30_IN2M_BIAS_WIDTH 2 +#define CS53L30_IN2M_BIAS_MASK (((1 << CS53L30_IN2M_BIAS_WIDTH) - 1) << CS53L30_IN2M_BIAS_SHIFT) +#define CS53L30_IN2M_BIAS_OPEN (0 << CS53L30_IN2M_BIAS_SHIFT) +#define CS53L30_IN2M_BIAS_PULL_DOWN (1 << CS53L30_IN2M_BIAS_SHIFT) +#define CS53L30_IN2M_BIAS_VCM (2 << CS53L30_IN2M_BIAS_SHIFT) +#define CS53L30_IN2P_BIAS_SHIFT 4 +#define CS53L30_IN2P_BIAS_WIDTH 2 +#define CS53L30_IN2P_BIAS_MASK (((1 << CS53L30_IN2P_BIAS_WIDTH) - 1) << CS53L30_IN2P_BIAS_SHIFT) +#define CS53L30_IN2P_BIAS_OPEN (0 << CS53L30_IN2P_BIAS_SHIFT) +#define CS53L30_IN2P_BIAS_PULL_DOWN (1 << CS53L30_IN2P_BIAS_SHIFT) +#define CS53L30_IN2P_BIAS_VCM (2 << CS53L30_IN2P_BIAS_SHIFT) +#define CS53L30_IN1M_BIAS_SHIFT 2 +#define CS53L30_IN1M_BIAS_WIDTH 2 +#define CS53L30_IN1M_BIAS_MASK (((1 << CS53L30_IN1M_BIAS_WIDTH) - 1) << CS53L30_IN1M_BIAS_SHIFT) +#define CS53L30_IN1M_BIAS_OPEN (0 << CS53L30_IN1M_BIAS_SHIFT) +#define CS53L30_IN1M_BIAS_PULL_DOWN (1 << CS53L30_IN1M_BIAS_SHIFT) +#define CS53L30_IN1M_BIAS_VCM (2 << CS53L30_IN1M_BIAS_SHIFT) +#define CS53L30_IN1P_BIAS_SHIFT 0 +#define CS53L30_IN1P_BIAS_WIDTH 2 +#define CS53L30_IN1P_BIAS_MASK (((1 << CS53L30_IN1P_BIAS_WIDTH) - 1) << CS53L30_IN1P_BIAS_SHIFT) +#define CS53L30_IN1P_BIAS_OPEN (0 << CS53L30_IN1P_BIAS_SHIFT) +#define CS53L30_IN1P_BIAS_PULL_DOWN (1 << CS53L30_IN1P_BIAS_SHIFT) +#define CS53L30_IN1P_BIAS_VCM (2 << CS53L30_IN1P_BIAS_SHIFT) + +#define CS53L30_INBIAS_CTL2_DEFAULT (CS53L30_IN2M_BIAS_VCM | CS53L30_IN2P_BIAS_VCM |\ + CS53L30_IN1M_BIAS_VCM | CS53L30_IN1P_BIAS_VCM) + +/* R35 (0x23) & R36 (0x24) CS53L30_DMICx_STR_CTL - DMIC1 & DMIC2 Stereo Control */ +#define CS53L30_DMICx_STEREO_ENB_SHIFT 5 +#define CS53L30_DMICx_STEREO_ENB_MASK (1 << CS53L30_DMICx_STEREO_ENB_SHIFT) +#define CS53L30_DMICx_STEREO_ENB (1 << CS53L30_DMICx_STEREO_ENB_SHIFT) + +/* 0x88 and 0xCC are reserved bits */ +#define CS53L30_DMIC1_STR_CTL_DEFAULT (CS53L30_DMICx_STEREO_ENB | 0x88) +#define CS53L30_DMIC2_STR_CTL_DEFAULT (CS53L30_DMICx_STEREO_ENB | 0xCC) + +/* R37/R45 (0x25/0x2D) CS53L30_ADCDMICx_CTL1 - ADC1/DMIC1 & ADC2/DMIC2 Control 1 */ +#define CS53L30_ADCxB_PDN_SHIFT 7 +#define CS53L30_ADCxB_PDN_MASK (1 << CS53L30_ADCxB_PDN_SHIFT) +#define CS53L30_ADCxB_PDN (1 << CS53L30_ADCxB_PDN_SHIFT) +#define CS53L30_ADCxA_PDN_SHIFT 6 +#define CS53L30_ADCxA_PDN_MASK (1 << CS53L30_ADCxA_PDN_SHIFT) +#define CS53L30_ADCxA_PDN (1 << CS53L30_ADCxA_PDN_SHIFT) +#define CS53L30_DMICx_PDN_SHIFT 2 +#define CS53L30_DMICx_PDN_MASK (1 << CS53L30_DMICx_PDN_SHIFT) +#define CS53L30_DMICx_PDN (1 << CS53L30_DMICx_PDN_SHIFT) +#define CS53L30_DMICx_SCLK_DIV_SHIFT 1 +#define CS53L30_DMICx_SCLK_DIV_MASK (1 << CS53L30_DMICx_SCLK_DIV_SHIFT) +#define CS53L30_DMICx_SCLK_DIV (1 << CS53L30_DMICx_SCLK_DIV_SHIFT) +#define CS53L30_CH_TYPE_SHIFT 0 +#define CS53L30_CH_TYPE_MASK (1 << CS53L30_CH_TYPE_SHIFT) +#define CS53L30_CH_TYPE (1 << CS53L30_CH_TYPE_SHIFT) + +#define CS53L30_ADCDMICx_PDN_MASK 0xFF +#define CS53L30_ADCDMICx_CTL1_DEFAULT (CS53L30_DMICx_PDN) + +/* R38/R46 (0x26/0x2E) CS53L30_ADCDMICx_CTL2 - ADC1/DMIC1 & ADC2/DMIC2 Control 2 */ +#define CS53L30_ADCx_NOTCH_DIS_SHIFT 7 +#define CS53L30_ADCx_NOTCH_DIS_MASK (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT) +#define CS53L30_ADCx_NOTCH_DIS (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT) +#define CS53L30_ADCxB_INV_SHIFT 5 +#define CS53L30_ADCxB_INV_MASK (1 << CS53L30_ADCxB_INV_SHIFT) +#define CS53L30_ADCxB_INV (1 << CS53L30_ADCxB_INV_SHIFT) +#define CS53L30_ADCxA_INV_SHIFT 4 +#define CS53L30_ADCxA_INV_MASK (1 << CS53L30_ADCxA_INV_SHIFT) +#define CS53L30_ADCxA_INV (1 << CS53L30_ADCxA_INV_SHIFT) +#define CS53L30_ADCxB_DIG_BOOST_SHIFT 1 +#define CS53L30_ADCxB_DIG_BOOST_MASK (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT) +#define CS53L30_ADCxB_DIG_BOOST (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT) +#define CS53L30_ADCxA_DIG_BOOST_SHIFT 0 +#define CS53L30_ADCxA_DIG_BOOST_MASK (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT) +#define CS53L30_ADCxA_DIG_BOOST (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT) + +#define CS53L30_ADCDMIC1_CTL2_DEFAULT (0) + +/* R39/R47 (0x27/0x2F) CS53L30_ADCx_CTL3 - ADC1/ADC2 Control 3 */ +#define CS53L30_ADCx_HPF_EN_SHIFT 3 +#define CS53L30_ADCx_HPF_EN_MASK (1 << CS53L30_ADCx_HPF_EN_SHIFT) +#define CS53L30_ADCx_HPF_EN (1 << CS53L30_ADCx_HPF_EN_SHIFT) +#define CS53L30_ADCx_HPF_CF_SHIFT 1 +#define CS53L30_ADCx_HPF_CF_WIDTH 2 +#define CS53L30_ADCx_HPF_CF_MASK (((1 << CS53L30_ADCx_HPF_CF_WIDTH) - 1) << CS53L30_ADCx_HPF_CF_SHIFT) +#define CS53L30_ADCx_HPF_CF_1HZ86 (0 << CS53L30_ADCx_HPF_CF_SHIFT) +#define CS53L30_ADCx_HPF_CF_120HZ (1 << CS53L30_ADCx_HPF_CF_SHIFT) +#define CS53L30_ADCx_HPF_CF_235HZ (2 << CS53L30_ADCx_HPF_CF_SHIFT) +#define CS53L30_ADCx_HPF_CF_466HZ (3 << CS53L30_ADCx_HPF_CF_SHIFT) +#define CS53L30_ADCx_NG_ALL_SHIFT 0 +#define CS53L30_ADCx_NG_ALL_MASK (1 << CS53L30_ADCx_NG_ALL_SHIFT) +#define CS53L30_ADCx_NG_ALL (1 << CS53L30_ADCx_NG_ALL_SHIFT) + +#define CS53L30_ADCx_CTL3_DEFAULT (CS53L30_ADCx_HPF_EN) + +/* R40/R48 (0x28/0x30) CS53L30_ADCx_NG_CTL - ADC1/ADC2 Noise Gate Control */ +#define CS53L30_ADCxB_NG_SHIFT 7 +#define CS53L30_ADCxB_NG_MASK (1 << CS53L30_ADCxB_NG_SHIFT) +#define CS53L30_ADCxB_NG (1 << CS53L30_ADCxB_NG_SHIFT) +#define CS53L30_ADCxA_NG_SHIFT 6 +#define CS53L30_ADCxA_NG_MASK (1 << CS53L30_ADCxA_NG_SHIFT) +#define CS53L30_ADCxA_NG (1 << CS53L30_ADCxA_NG_SHIFT) +#define CS53L30_ADCx_NG_BOOST_SHIFT 5 +#define CS53L30_ADCx_NG_BOOST_MASK (1 << CS53L30_ADCx_NG_BOOST_SHIFT) +#define CS53L30_ADCx_NG_BOOST (1 << CS53L30_ADCx_NG_BOOST_SHIFT) +#define CS53L30_ADCx_NG_THRESH_SHIFT 2 +#define CS53L30_ADCx_NG_THRESH_WIDTH 3 +#define CS53L30_ADCx_NG_THRESH_MASK (((1 << CS53L30_ADCx_NG_THRESH_WIDTH) - 1) << CS53L30_ADCx_NG_THRESH_SHIFT) +#define CS53L30_ADCx_NG_DELAY_SHIFT 0 +#define CS53L30_ADCx_NG_DELAY_WIDTH 2 +#define CS53L30_ADCx_NG_DELAY_MASK (((1 << CS53L30_ADCx_NG_DELAY_WIDTH) - 1) << CS53L30_ADCx_NG_DELAY_SHIFT) + +#define CS53L30_ADCx_NG_CTL_DEFAULT (0) + +/* R41/R42/R49/R50 (0x29/0x2A/0x31/0x32) CS53L30_ADCxy_AFE_CTL - ADC1A/1B/2A/2B AFE Control */ +#define CS53L30_ADCxy_PREAMP_SHIFT 6 +#define CS53L30_ADCxy_PREAMP_WIDTH 2 +#define CS53L30_ADCxy_PREAMP_MASK (((1 << CS53L30_ADCxy_PREAMP_WIDTH) - 1) << CS53L30_ADCxy_PREAMP_SHIFT) +#define CS53L30_ADCxy_PGA_VOL_SHIFT 0 +#define CS53L30_ADCxy_PGA_VOL_WIDTH 6 +#define CS53L30_ADCxy_PGA_VOL_MASK (((1 << CS53L30_ADCxy_PGA_VOL_WIDTH) - 1) << CS53L30_ADCxy_PGA_VOL_SHIFT) + +#define CS53L30_ADCxy_AFE_CTL_DEFAULT (0) + +/* R43/R44/R51/R52 (0x2B/0x2C/0x33/0x34) CS53L30_ADCxy_DIG_VOL - ADC1A/1B/2A/2B Digital Volume */ +#define CS53L30_ADCxy_VOL_MUTE (0x80) + +#define CS53L30_ADCxy_DIG_VOL_DEFAULT (0x0) + +/* CS53L30_INT */ +#define CS53L30_PDN_DONE (1 << 7) +#define CS53L30_THMS_TRIP (1 << 6) +#define CS53L30_SYNC_DONE (1 << 5) +#define CS53L30_ADC2B_OVFL (1 << 4) +#define CS53L30_ADC2A_OVFL (1 << 3) +#define CS53L30_ADC1B_OVFL (1 << 2) +#define CS53L30_ADC1A_OVFL (1 << 1) +#define CS53L30_MUTE_PIN (1 << 0) +#define CS53L30_DEVICE_INT_MASK 0xFF + +#endif /* __CS53L30_H__ */ diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c new file mode 100644 index 0000000000..9d54141a0c --- /dev/null +++ b/sound/soc/codecs/cx20442.c @@ -0,0 +1,434 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * cx20442.c -- CX20442 ALSA Soc Audio driver + * + * Copyright 2009 Janusz Krzysztofik + * + * Initially based on sound/soc/codecs/wm8400.c + * Copyright 2008, 2009 Wolfson Microelectronics PLC. + * Author: Mark Brown + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "cx20442.h" + + +struct cx20442_priv { + struct tty_struct *tty; + struct regulator *por; + u8 reg_cache; +}; + +#define CX20442_PM 0x0 + +#define CX20442_TELIN 0 +#define CX20442_TELOUT 1 +#define CX20442_MIC 2 +#define CX20442_SPKOUT 3 +#define CX20442_AGC 4 + +static const struct snd_soc_dapm_widget cx20442_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("TELOUT"), + SND_SOC_DAPM_OUTPUT("SPKOUT"), + SND_SOC_DAPM_OUTPUT("AGCOUT"), + + SND_SOC_DAPM_MIXER("SPKOUT Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("TELOUT Amp", CX20442_PM, CX20442_TELOUT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPKOUT Amp", CX20442_PM, CX20442_SPKOUT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPKOUT AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MICBIAS("TELIN Bias", CX20442_PM, CX20442_TELIN, 0), + SND_SOC_DAPM_MICBIAS("MIC Bias", CX20442_PM, CX20442_MIC, 0), + + SND_SOC_DAPM_PGA("MIC AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("TELIN"), + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_INPUT("AGCIN"), +}; + +static const struct snd_soc_dapm_route cx20442_audio_map[] = { + {"TELOUT", NULL, "TELOUT Amp"}, + + {"SPKOUT", NULL, "SPKOUT Mixer"}, + {"SPKOUT Mixer", NULL, "SPKOUT Amp"}, + + {"TELOUT Amp", NULL, "DAC"}, + {"SPKOUT Amp", NULL, "DAC"}, + + {"SPKOUT Mixer", NULL, "SPKOUT AGC"}, + {"SPKOUT AGC", NULL, "AGCIN"}, + + {"AGCOUT", NULL, "MIC AGC"}, + {"MIC AGC", NULL, "MIC"}, + + {"MIC Bias", NULL, "MIC"}, + {"Input Mixer", NULL, "MIC Bias"}, + + {"TELIN Bias", NULL, "TELIN"}, + {"Input Mixer", NULL, "TELIN Bias"}, + + {"ADC", NULL, "Input Mixer"}, +}; + +static unsigned int cx20442_read_reg_cache(struct snd_soc_component *component, + unsigned int reg) +{ + struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component); + + if (reg >= 1) + return -EINVAL; + + return cx20442->reg_cache; +} + +enum v253_vls { + V253_VLS_NONE = 0, + V253_VLS_T, + V253_VLS_L, + V253_VLS_LT, + V253_VLS_S, + V253_VLS_ST, + V253_VLS_M, + V253_VLS_MST, + V253_VLS_S1, + V253_VLS_S1T, + V253_VLS_MS1T, + V253_VLS_M1, + V253_VLS_M1ST, + V253_VLS_M1S1T, + V253_VLS_H, + V253_VLS_HT, + V253_VLS_MS, + V253_VLS_MS1, + V253_VLS_M1S, + V253_VLS_M1S1, + V253_VLS_TEST, +}; + +static int cx20442_pm_to_v253_vls(u8 value) +{ + switch (value & ~(1 << CX20442_AGC)) { + case 0: + return V253_VLS_T; + case (1 << CX20442_SPKOUT): + case (1 << CX20442_MIC): + case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC): + return V253_VLS_M1S1; + case (1 << CX20442_TELOUT): + case (1 << CX20442_TELIN): + case (1 << CX20442_TELOUT) | (1 << CX20442_TELIN): + return V253_VLS_L; + case (1 << CX20442_TELOUT) | (1 << CX20442_MIC): + return V253_VLS_NONE; + } + return -EINVAL; +} +static int cx20442_pm_to_v253_vsp(u8 value) +{ + switch (value & ~(1 << CX20442_AGC)) { + case (1 << CX20442_SPKOUT): + case (1 << CX20442_MIC): + case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC): + return (bool)(value & (1 << CX20442_AGC)); + } + return (value & (1 << CX20442_AGC)) ? -EINVAL : 0; +} + +static int cx20442_write(struct snd_soc_component *component, unsigned int reg, + unsigned int value) +{ + struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component); + int vls, vsp, old, len; + char buf[18]; + + if (reg >= 1) + return -EINVAL; + + /* tty and write pointers required for talking to the modem + * are expected to be set by the line discipline initialization code */ + if (!cx20442->tty || !cx20442->tty->ops->write) + return -EIO; + + old = cx20442->reg_cache; + cx20442->reg_cache = value; + + vls = cx20442_pm_to_v253_vls(value); + if (vls < 0) + return vls; + + vsp = cx20442_pm_to_v253_vsp(value); + if (vsp < 0) + return vsp; + + if ((vls == V253_VLS_T) || + (vls == cx20442_pm_to_v253_vls(old))) { + if (vsp == cx20442_pm_to_v253_vsp(old)) + return 0; + len = snprintf(buf, ARRAY_SIZE(buf), "at+vsp=%d\r", vsp); + } else if (vsp == cx20442_pm_to_v253_vsp(old)) + len = snprintf(buf, ARRAY_SIZE(buf), "at+vls=%d\r", vls); + else + len = snprintf(buf, ARRAY_SIZE(buf), + "at+vls=%d;+vsp=%d\r", vls, vsp); + + if (unlikely(len > (ARRAY_SIZE(buf) - 1))) + return -ENOMEM; + + dev_dbg(component->dev, "%s: %s\n", __func__, buf); + if (cx20442->tty->ops->write(cx20442->tty, buf, len) != len) + return -EIO; + + return 0; +} + +/* + * Line discpline related code + * + * Any of the callback functions below can be used in two ways: + * 1) registerd by a machine driver as one of line discipline operations, + * 2) called from a machine's provided line discipline callback function + * in case when extra machine specific code must be run as well. + */ + +/* Modem init: echo off, digital speaker off, quiet off, voice mode */ +static const char v253_init[] = "ate0m0q0+fclass=8\r"; + +/* Line discipline .open() */ +static int v253_open(struct tty_struct *tty) +{ + int ret, len = strlen(v253_init); + + /* Doesn't make sense without write callback */ + if (!tty->ops->write) + return -EINVAL; + + /* Won't work if no codec pointer has been passed by a card driver */ + if (!tty->disc_data) + return -ENODEV; + + tty->receive_room = 16; + if (tty->ops->write(tty, v253_init, len) != len) { + ret = -EIO; + goto err; + } + /* Actual setup will be performed after the modem responds. */ + return 0; +err: + tty->disc_data = NULL; + return ret; +} + +/* Line discipline .close() */ +static void v253_close(struct tty_struct *tty) +{ + struct snd_soc_component *component = tty->disc_data; + struct cx20442_priv *cx20442; + + tty->disc_data = NULL; + + if (!component) + return; + + cx20442 = snd_soc_component_get_drvdata(component); + + /* Prevent the codec driver from further accessing the modem */ + cx20442->tty = NULL; + component->card->pop_time = 0; +} + +/* Line discipline .hangup() */ +static void v253_hangup(struct tty_struct *tty) +{ + v253_close(tty); +} + +/* Line discipline .receive_buf() */ +static void v253_receive(struct tty_struct *tty, const u8 *cp, const u8 *fp, + size_t count) +{ + struct snd_soc_component *component = tty->disc_data; + struct cx20442_priv *cx20442; + + if (!component) + return; + + cx20442 = snd_soc_component_get_drvdata(component); + + if (!cx20442->tty) { + /* First modem response, complete setup procedure */ + + /* Set up codec driver access to modem controls */ + cx20442->tty = tty; + component->card->pop_time = 1; + } +} + +struct tty_ldisc_ops v253_ops = { + .name = "cx20442", + .owner = THIS_MODULE, + .open = v253_open, + .close = v253_close, + .hangup = v253_hangup, + .receive_buf = v253_receive, +}; +EXPORT_SYMBOL_GPL(v253_ops); + + +/* + * Codec DAI + */ + +static struct snd_soc_dai_driver cx20442_dai = { + .name = "cx20442-voice", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static int cx20442_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component); + int err = 0; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_STANDBY) + break; + if (IS_ERR(cx20442->por)) + err = PTR_ERR(cx20442->por); + else + err = regulator_enable(cx20442->por); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_PREPARE) + break; + if (IS_ERR(cx20442->por)) + err = PTR_ERR(cx20442->por); + else + err = regulator_disable(cx20442->por); + break; + default: + break; + } + + return err; +} + +static int cx20442_component_probe(struct snd_soc_component *component) +{ + struct cx20442_priv *cx20442; + + cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL); + if (cx20442 == NULL) + return -ENOMEM; + + cx20442->por = regulator_get(component->dev, "POR"); + if (IS_ERR(cx20442->por)) { + int err = PTR_ERR(cx20442->por); + + dev_warn(component->dev, "failed to get POR supply (%d)", err); + /* + * When running on a non-dt platform and requested regulator + * is not available, regulator_get() never returns + * -EPROBE_DEFER as it is not able to justify if the regulator + * may still appear later. On the other hand, the board can + * still set full constraints flag at late_initcall in order + * to instruct regulator_get() to return a dummy one if + * sufficient. Hence, if we get -ENODEV here, let's convert + * it to -EPROBE_DEFER and wait for the board to decide or + * let Deferred Probe infrastructure handle this error. + */ + if (err == -ENODEV) + err = -EPROBE_DEFER; + kfree(cx20442); + return err; + } + + cx20442->tty = NULL; + + snd_soc_component_set_drvdata(component, cx20442); + component->card->pop_time = 0; + + return 0; +} + +/* power down chip */ +static void cx20442_component_remove(struct snd_soc_component *component) +{ + struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component); + + if (cx20442->tty) { + struct tty_struct *tty = cx20442->tty; + tty_hangup(tty); + } + + if (!IS_ERR(cx20442->por)) { + /* should be already in STANDBY, hence disabled */ + regulator_put(cx20442->por); + } + + snd_soc_component_set_drvdata(component, NULL); + kfree(cx20442); +} + +static const struct snd_soc_component_driver cx20442_component_dev = { + .probe = cx20442_component_probe, + .remove = cx20442_component_remove, + .set_bias_level = cx20442_set_bias_level, + .read = cx20442_read_reg_cache, + .write = cx20442_write, + .dapm_widgets = cx20442_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets), + .dapm_routes = cx20442_audio_map, + .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int cx20442_platform_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &cx20442_component_dev, &cx20442_dai, 1); +} + +static struct platform_driver cx20442_platform_driver = { + .driver = { + .name = "cx20442-codec", + }, + .probe = cx20442_platform_probe, +}; + +module_platform_driver(cx20442_platform_driver); + +MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver"); +MODULE_AUTHOR("Janusz Krzysztofik"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cx20442-codec"); diff --git a/sound/soc/codecs/cx20442.h b/sound/soc/codecs/cx20442.h new file mode 100644 index 0000000000..bb897bcb24 --- /dev/null +++ b/sound/soc/codecs/cx20442.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * cx20442.h -- audio driver for CX20442 + * + * Copyright 2009 Janusz Krzysztofik + */ + +#ifndef _CX20442_CODEC_H +#define _CX20442_CODEC_H + +extern struct tty_ldisc_ops v253_ops; + +#endif diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c new file mode 100644 index 0000000000..f8b1280840 --- /dev/null +++ b/sound/soc/codecs/cx2072x.c @@ -0,0 +1,1725 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC CX20721/CX20723 codec driver +// +// Copyright: (C) 2017 Conexant Systems, Inc. +// Author: Simon Ho, +// +// TODO: add support for TDM mode. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cx2072x.h" + +#define PLL_OUT_HZ_48 (1024 * 3 * 48000) +#define BITS_PER_SLOT 8 + +/* codec private data */ +struct cx2072x_priv { + struct regmap *regmap; + struct clk *mclk; + unsigned int mclk_rate; + struct device *dev; + struct snd_soc_component *codec; + struct snd_soc_jack_gpio jack_gpio; + struct mutex lock; + unsigned int bclk_ratio; + bool pll_changed; + bool i2spcm_changed; + int sample_size; + int frame_size; + int sample_rate; + unsigned int dai_fmt; + bool en_aec_ref; +}; + +/* + * DAC/ADC Volume + * + * max : 74 : 0 dB + * ( in 1 dB step ) + * min : 0 : -74 dB + */ +static const DECLARE_TLV_DB_SCALE(adc_tlv, -7400, 100, 0); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -7400, 100, 0); +static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 1200, 0); + +struct cx2072x_eq_ctrl { + u8 ch; + u8 band; +}; + +static const DECLARE_TLV_DB_RANGE(hpf_tlv, + 0, 0, TLV_DB_SCALE_ITEM(120, 0, 0), + 1, 63, TLV_DB_SCALE_ITEM(30, 30, 0) +); + +/* Lookup table for PRE_DIV */ +static const struct { + unsigned int mclk; + unsigned int div; +} mclk_pre_div[] = { + { 6144000, 1 }, + { 12288000, 2 }, + { 19200000, 3 }, + { 26000000, 4 }, + { 28224000, 5 }, + { 36864000, 6 }, + { 36864000, 7 }, + { 48000000, 8 }, + { 49152000, 8 }, +}; + +/* + * cx2072x register cache. + */ +static const struct reg_default cx2072x_reg_defaults[] = { + { CX2072X_AFG_POWER_STATE, 0x00000003 }, + { CX2072X_UM_RESPONSE, 0x00000000 }, + { CX2072X_GPIO_DATA, 0x00000000 }, + { CX2072X_GPIO_ENABLE, 0x00000000 }, + { CX2072X_GPIO_DIRECTION, 0x00000000 }, + { CX2072X_GPIO_WAKE, 0x00000000 }, + { CX2072X_GPIO_UM_ENABLE, 0x00000000 }, + { CX2072X_GPIO_STICKY_MASK, 0x00000000 }, + { CX2072X_DAC1_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_DAC1_AMP_GAIN_RIGHT, 0x0000004a }, + { CX2072X_DAC1_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC1_POWER_STATE, 0x00000433 }, + { CX2072X_DAC1_CONVERTER_STREAM_CHANNEL, 0x00000000 }, + { CX2072X_DAC1_EAPD_ENABLE, 0x00000000 }, + { CX2072X_DAC2_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_DAC2_AMP_GAIN_RIGHT, 0x0000004a }, + { CX2072X_DAC2_AMP_GAIN_LEFT, 0x0000004a }, + { CX2072X_DAC2_POWER_STATE, 0x00000433 }, + { CX2072X_DAC2_CONVERTER_STREAM_CHANNEL, 0x00000000 }, + { CX2072X_ADC1_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_1, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_2, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_3, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_3, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_4, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_4, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_5, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_5, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_RIGHT_6, 0x0000004a }, + { CX2072X_ADC1_AMP_GAIN_LEFT_6, 0x0000004a }, + { CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0x00000000 }, + { CX2072X_ADC1_POWER_STATE, 0x00000433 }, + { CX2072X_ADC1_CONVERTER_STREAM_CHANNEL, 0x00000000 }, + { CX2072X_ADC2_CONVERTER_FORMAT, 0x00000031 }, + { CX2072X_ADC2_AMP_GAIN_RIGHT_0, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_RIGHT_1, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_LEFT_1, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_RIGHT_2, 0x0000004a }, + { CX2072X_ADC2_AMP_GAIN_LEFT_2, 0x0000004a }, + { CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0x00000000 }, + { CX2072X_ADC2_POWER_STATE, 0x00000433 }, + { CX2072X_ADC2_CONVERTER_STREAM_CHANNEL, 0x00000000 }, + { CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTA_POWER_STATE, 0x00000433 }, + { CX2072X_PORTA_PIN_CTRL, 0x000000c0 }, + { CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTA_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTA_EAPD_BTL, 0x00000002 }, + { CX2072X_PORTB_POWER_STATE, 0x00000433 }, + { CX2072X_PORTB_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTB_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTB_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTB_EAPD_BTL, 0x00000002 }, + { CX2072X_PORTB_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTB_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTC_POWER_STATE, 0x00000433 }, + { CX2072X_PORTC_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTC_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTC_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTD_POWER_STATE, 0x00000433 }, + { CX2072X_PORTD_PIN_CTRL, 0x00000020 }, + { CX2072X_PORTD_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTD_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTD_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTD_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTE_POWER_STATE, 0x00000433 }, + { CX2072X_PORTE_PIN_CTRL, 0x00000040 }, + { CX2072X_PORTE_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTE_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTE_EAPD_BTL, 0x00000002 }, + { CX2072X_PORTE_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTE_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTF_POWER_STATE, 0x00000433 }, + { CX2072X_PORTF_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTF_UNSOLICITED_RESPONSE, 0x00000000 }, + { CX2072X_PORTF_PIN_SENSE, 0x00000000 }, + { CX2072X_PORTF_GAIN_RIGHT, 0x00000000 }, + { CX2072X_PORTF_GAIN_LEFT, 0x00000000 }, + { CX2072X_PORTG_POWER_STATE, 0x00000433 }, + { CX2072X_PORTG_PIN_CTRL, 0x00000040 }, + { CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTG_EAPD_BTL, 0x00000002 }, + { CX2072X_PORTM_POWER_STATE, 0x00000433 }, + { CX2072X_PORTM_PIN_CTRL, 0x00000000 }, + { CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0x00000000 }, + { CX2072X_PORTM_EAPD_BTL, 0x00000002 }, + { CX2072X_MIXER_POWER_STATE, 0x00000433 }, + { CX2072X_MIXER_GAIN_RIGHT_0, 0x0000004a }, + { CX2072X_MIXER_GAIN_LEFT_0, 0x0000004a }, + { CX2072X_MIXER_GAIN_RIGHT_1, 0x0000004a }, + { CX2072X_MIXER_GAIN_LEFT_1, 0x0000004a }, + { CX2072X_SPKR_DRC_ENABLE_STEP, 0x040065a4 }, + { CX2072X_SPKR_DRC_CONTROL, 0x007b0024 }, + { CX2072X_SPKR_DRC_TEST, 0x00000000 }, + { CX2072X_DIGITAL_BIOS_TEST0, 0x001f008a }, + { CX2072X_DIGITAL_BIOS_TEST2, 0x00990026 }, + { CX2072X_I2SPCM_CONTROL1, 0x00010001 }, + { CX2072X_I2SPCM_CONTROL2, 0x00000000 }, + { CX2072X_I2SPCM_CONTROL3, 0x00000000 }, + { CX2072X_I2SPCM_CONTROL4, 0x00000000 }, + { CX2072X_I2SPCM_CONTROL5, 0x00000000 }, + { CX2072X_I2SPCM_CONTROL6, 0x00000000 }, + { CX2072X_UM_INTERRUPT_CRTL_E, 0x00000000 }, + { CX2072X_CODEC_TEST2, 0x00000000 }, + { CX2072X_CODEC_TEST9, 0x00000004 }, + { CX2072X_CODEC_TEST20, 0x00000600 }, + { CX2072X_CODEC_TEST26, 0x00000208 }, + { CX2072X_ANALOG_TEST4, 0x00000000 }, + { CX2072X_ANALOG_TEST5, 0x00000000 }, + { CX2072X_ANALOG_TEST6, 0x0000059a }, + { CX2072X_ANALOG_TEST7, 0x000000a7 }, + { CX2072X_ANALOG_TEST8, 0x00000017 }, + { CX2072X_ANALOG_TEST9, 0x00000000 }, + { CX2072X_ANALOG_TEST10, 0x00000285 }, + { CX2072X_ANALOG_TEST11, 0x00000000 }, + { CX2072X_ANALOG_TEST12, 0x00000000 }, + { CX2072X_ANALOG_TEST13, 0x00000000 }, + { CX2072X_DIGITAL_TEST1, 0x00000242 }, + { CX2072X_DIGITAL_TEST11, 0x00000000 }, + { CX2072X_DIGITAL_TEST12, 0x00000084 }, + { CX2072X_DIGITAL_TEST15, 0x00000077 }, + { CX2072X_DIGITAL_TEST16, 0x00000021 }, + { CX2072X_DIGITAL_TEST17, 0x00000018 }, + { CX2072X_DIGITAL_TEST18, 0x00000024 }, + { CX2072X_DIGITAL_TEST19, 0x00000001 }, + { CX2072X_DIGITAL_TEST20, 0x00000002 }, +}; + +/* + * register initialization + */ +static const struct reg_sequence cx2072x_reg_init[] = { + { CX2072X_ANALOG_TEST9, 0x080 }, /* DC offset Calibration */ + { CX2072X_CODEC_TEST26, 0x65f }, /* Disable the PA */ + { CX2072X_ANALOG_TEST10, 0x289 }, /* Set the speaker output gain */ + { CX2072X_CODEC_TEST20, 0xf05 }, + { CX2072X_CODEC_TESTXX, 0x380 }, + { CX2072X_CODEC_TEST26, 0xb90 }, + { CX2072X_CODEC_TEST9, 0x001 }, /* Enable 30 Hz High pass filter */ + { CX2072X_ANALOG_TEST3, 0x300 }, /* Disable PCBEEP pad */ + { CX2072X_CODEC_TEST24, 0x100 }, /* Disable SnM mode */ + { CX2072X_PORTD_PIN_CTRL, 0x020 }, /* Enable PortD input */ + { CX2072X_GPIO_ENABLE, 0x040 }, /* Enable GPIO7 pin for button */ + { CX2072X_GPIO_UM_ENABLE, 0x040 }, /* Enable UM for GPIO7 */ + { CX2072X_UM_RESPONSE, 0x080 }, /* Enable button response */ + { CX2072X_DIGITAL_TEST12, 0x0c4 }, /* Enable headset button */ + { CX2072X_DIGITAL_TEST0, 0x415 }, /* Power down class-D during idle */ + { CX2072X_I2SPCM_CONTROL2, 0x00f }, /* Enable I2S TX */ + { CX2072X_I2SPCM_CONTROL3, 0x00f }, /* Enable I2S RX */ +}; + +static unsigned int cx2072x_register_size(unsigned int reg) +{ + switch (reg) { + case CX2072X_VENDOR_ID: + case CX2072X_REVISION_ID: + case CX2072X_PORTA_PIN_SENSE: + case CX2072X_PORTB_PIN_SENSE: + case CX2072X_PORTD_PIN_SENSE: + case CX2072X_PORTE_PIN_SENSE: + case CX2072X_PORTF_PIN_SENSE: + case CX2072X_I2SPCM_CONTROL1: + case CX2072X_I2SPCM_CONTROL2: + case CX2072X_I2SPCM_CONTROL3: + case CX2072X_I2SPCM_CONTROL4: + case CX2072X_I2SPCM_CONTROL5: + case CX2072X_I2SPCM_CONTROL6: + case CX2072X_UM_INTERRUPT_CRTL_E: + case CX2072X_EQ_G_COEFF: + case CX2072X_SPKR_DRC_CONTROL: + case CX2072X_SPKR_DRC_TEST: + case CX2072X_DIGITAL_BIOS_TEST0: + case CX2072X_DIGITAL_BIOS_TEST2: + return 4; + case CX2072X_EQ_ENABLE_BYPASS: + case CX2072X_EQ_B0_COEFF: + case CX2072X_EQ_B1_COEFF: + case CX2072X_EQ_B2_COEFF: + case CX2072X_EQ_A1_COEFF: + case CX2072X_EQ_A2_COEFF: + case CX2072X_DAC1_CONVERTER_FORMAT: + case CX2072X_DAC2_CONVERTER_FORMAT: + case CX2072X_ADC1_CONVERTER_FORMAT: + case CX2072X_ADC2_CONVERTER_FORMAT: + case CX2072X_CODEC_TEST2: + case CX2072X_CODEC_TEST9: + case CX2072X_CODEC_TEST20: + case CX2072X_CODEC_TEST26: + case CX2072X_ANALOG_TEST3: + case CX2072X_ANALOG_TEST4: + case CX2072X_ANALOG_TEST5: + case CX2072X_ANALOG_TEST6: + case CX2072X_ANALOG_TEST7: + case CX2072X_ANALOG_TEST8: + case CX2072X_ANALOG_TEST9: + case CX2072X_ANALOG_TEST10: + case CX2072X_ANALOG_TEST11: + case CX2072X_ANALOG_TEST12: + case CX2072X_ANALOG_TEST13: + case CX2072X_DIGITAL_TEST0: + case CX2072X_DIGITAL_TEST1: + case CX2072X_DIGITAL_TEST11: + case CX2072X_DIGITAL_TEST12: + case CX2072X_DIGITAL_TEST15: + case CX2072X_DIGITAL_TEST16: + case CX2072X_DIGITAL_TEST17: + case CX2072X_DIGITAL_TEST18: + case CX2072X_DIGITAL_TEST19: + case CX2072X_DIGITAL_TEST20: + return 2; + default: + return 1; + } +} + +static bool cx2072x_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CX2072X_VENDOR_ID: + case CX2072X_REVISION_ID: + case CX2072X_CURRENT_BCLK_FREQUENCY: + case CX2072X_AFG_POWER_STATE: + case CX2072X_UM_RESPONSE: + case CX2072X_GPIO_DATA: + case CX2072X_GPIO_ENABLE: + case CX2072X_GPIO_DIRECTION: + case CX2072X_GPIO_WAKE: + case CX2072X_GPIO_UM_ENABLE: + case CX2072X_GPIO_STICKY_MASK: + case CX2072X_DAC1_CONVERTER_FORMAT: + case CX2072X_DAC1_AMP_GAIN_RIGHT: + case CX2072X_DAC1_AMP_GAIN_LEFT: + case CX2072X_DAC1_POWER_STATE: + case CX2072X_DAC1_CONVERTER_STREAM_CHANNEL: + case CX2072X_DAC1_EAPD_ENABLE: + case CX2072X_DAC2_CONVERTER_FORMAT: + case CX2072X_DAC2_AMP_GAIN_RIGHT: + case CX2072X_DAC2_AMP_GAIN_LEFT: + case CX2072X_DAC2_POWER_STATE: + case CX2072X_DAC2_CONVERTER_STREAM_CHANNEL: + case CX2072X_ADC1_CONVERTER_FORMAT: + case CX2072X_ADC1_AMP_GAIN_RIGHT_0: + case CX2072X_ADC1_AMP_GAIN_LEFT_0: + case CX2072X_ADC1_AMP_GAIN_RIGHT_1: + case CX2072X_ADC1_AMP_GAIN_LEFT_1: + case CX2072X_ADC1_AMP_GAIN_RIGHT_2: + case CX2072X_ADC1_AMP_GAIN_LEFT_2: + case CX2072X_ADC1_AMP_GAIN_RIGHT_3: + case CX2072X_ADC1_AMP_GAIN_LEFT_3: + case CX2072X_ADC1_AMP_GAIN_RIGHT_4: + case CX2072X_ADC1_AMP_GAIN_LEFT_4: + case CX2072X_ADC1_AMP_GAIN_RIGHT_5: + case CX2072X_ADC1_AMP_GAIN_LEFT_5: + case CX2072X_ADC1_AMP_GAIN_RIGHT_6: + case CX2072X_ADC1_AMP_GAIN_LEFT_6: + case CX2072X_ADC1_CONNECTION_SELECT_CONTROL: + case CX2072X_ADC1_POWER_STATE: + case CX2072X_ADC1_CONVERTER_STREAM_CHANNEL: + case CX2072X_ADC2_CONVERTER_FORMAT: + case CX2072X_ADC2_AMP_GAIN_RIGHT_0: + case CX2072X_ADC2_AMP_GAIN_LEFT_0: + case CX2072X_ADC2_AMP_GAIN_RIGHT_1: + case CX2072X_ADC2_AMP_GAIN_LEFT_1: + case CX2072X_ADC2_AMP_GAIN_RIGHT_2: + case CX2072X_ADC2_AMP_GAIN_LEFT_2: + case CX2072X_ADC2_CONNECTION_SELECT_CONTROL: + case CX2072X_ADC2_POWER_STATE: + case CX2072X_ADC2_CONVERTER_STREAM_CHANNEL: + case CX2072X_PORTA_CONNECTION_SELECT_CTRL: + case CX2072X_PORTA_POWER_STATE: + case CX2072X_PORTA_PIN_CTRL: + case CX2072X_PORTA_UNSOLICITED_RESPONSE: + case CX2072X_PORTA_PIN_SENSE: + case CX2072X_PORTA_EAPD_BTL: + case CX2072X_PORTB_POWER_STATE: + case CX2072X_PORTB_PIN_CTRL: + case CX2072X_PORTB_UNSOLICITED_RESPONSE: + case CX2072X_PORTB_PIN_SENSE: + case CX2072X_PORTB_EAPD_BTL: + case CX2072X_PORTB_GAIN_RIGHT: + case CX2072X_PORTB_GAIN_LEFT: + case CX2072X_PORTC_POWER_STATE: + case CX2072X_PORTC_PIN_CTRL: + case CX2072X_PORTC_GAIN_RIGHT: + case CX2072X_PORTC_GAIN_LEFT: + case CX2072X_PORTD_POWER_STATE: + case CX2072X_PORTD_PIN_CTRL: + case CX2072X_PORTD_UNSOLICITED_RESPONSE: + case CX2072X_PORTD_PIN_SENSE: + case CX2072X_PORTD_GAIN_RIGHT: + case CX2072X_PORTD_GAIN_LEFT: + case CX2072X_PORTE_CONNECTION_SELECT_CTRL: + case CX2072X_PORTE_POWER_STATE: + case CX2072X_PORTE_PIN_CTRL: + case CX2072X_PORTE_UNSOLICITED_RESPONSE: + case CX2072X_PORTE_PIN_SENSE: + case CX2072X_PORTE_EAPD_BTL: + case CX2072X_PORTE_GAIN_RIGHT: + case CX2072X_PORTE_GAIN_LEFT: + case CX2072X_PORTF_POWER_STATE: + case CX2072X_PORTF_PIN_CTRL: + case CX2072X_PORTF_UNSOLICITED_RESPONSE: + case CX2072X_PORTF_PIN_SENSE: + case CX2072X_PORTF_GAIN_RIGHT: + case CX2072X_PORTF_GAIN_LEFT: + case CX2072X_PORTG_POWER_STATE: + case CX2072X_PORTG_PIN_CTRL: + case CX2072X_PORTG_CONNECTION_SELECT_CTRL: + case CX2072X_PORTG_EAPD_BTL: + case CX2072X_PORTM_POWER_STATE: + case CX2072X_PORTM_PIN_CTRL: + case CX2072X_PORTM_CONNECTION_SELECT_CTRL: + case CX2072X_PORTM_EAPD_BTL: + case CX2072X_MIXER_POWER_STATE: + case CX2072X_MIXER_GAIN_RIGHT_0: + case CX2072X_MIXER_GAIN_LEFT_0: + case CX2072X_MIXER_GAIN_RIGHT_1: + case CX2072X_MIXER_GAIN_LEFT_1: + case CX2072X_EQ_ENABLE_BYPASS: + case CX2072X_EQ_B0_COEFF: + case CX2072X_EQ_B1_COEFF: + case CX2072X_EQ_B2_COEFF: + case CX2072X_EQ_A1_COEFF: + case CX2072X_EQ_A2_COEFF: + case CX2072X_EQ_G_COEFF: + case CX2072X_SPKR_DRC_ENABLE_STEP: + case CX2072X_SPKR_DRC_CONTROL: + case CX2072X_SPKR_DRC_TEST: + case CX2072X_DIGITAL_BIOS_TEST0: + case CX2072X_DIGITAL_BIOS_TEST2: + case CX2072X_I2SPCM_CONTROL1: + case CX2072X_I2SPCM_CONTROL2: + case CX2072X_I2SPCM_CONTROL3: + case CX2072X_I2SPCM_CONTROL4: + case CX2072X_I2SPCM_CONTROL5: + case CX2072X_I2SPCM_CONTROL6: + case CX2072X_UM_INTERRUPT_CRTL_E: + case CX2072X_CODEC_TEST2: + case CX2072X_CODEC_TEST9: + case CX2072X_CODEC_TEST20: + case CX2072X_CODEC_TEST26: + case CX2072X_ANALOG_TEST4: + case CX2072X_ANALOG_TEST5: + case CX2072X_ANALOG_TEST6: + case CX2072X_ANALOG_TEST7: + case CX2072X_ANALOG_TEST8: + case CX2072X_ANALOG_TEST9: + case CX2072X_ANALOG_TEST10: + case CX2072X_ANALOG_TEST11: + case CX2072X_ANALOG_TEST12: + case CX2072X_ANALOG_TEST13: + case CX2072X_DIGITAL_TEST0: + case CX2072X_DIGITAL_TEST1: + case CX2072X_DIGITAL_TEST11: + case CX2072X_DIGITAL_TEST12: + case CX2072X_DIGITAL_TEST15: + case CX2072X_DIGITAL_TEST16: + case CX2072X_DIGITAL_TEST17: + case CX2072X_DIGITAL_TEST18: + case CX2072X_DIGITAL_TEST19: + case CX2072X_DIGITAL_TEST20: + return true; + default: + return false; + } +} + +static bool cx2072x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CX2072X_VENDOR_ID: + case CX2072X_REVISION_ID: + case CX2072X_UM_INTERRUPT_CRTL_E: + case CX2072X_DIGITAL_TEST11: + case CX2072X_PORTA_PIN_SENSE: + case CX2072X_PORTB_PIN_SENSE: + case CX2072X_PORTD_PIN_SENSE: + case CX2072X_PORTE_PIN_SENSE: + case CX2072X_PORTF_PIN_SENSE: + case CX2072X_EQ_G_COEFF: + case CX2072X_EQ_BAND: + return true; + default: + return false; + } +} + +static int cx2072x_reg_raw_write(struct i2c_client *client, + unsigned int reg, + const void *val, size_t val_count) +{ + struct device *dev = &client->dev; + u8 buf[2 + CX2072X_MAX_EQ_COEFF]; + int ret; + + if (WARN_ON(val_count + 2 > sizeof(buf))) + return -EINVAL; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + memcpy(buf + 2, val, val_count); + + ret = i2c_master_send(client, buf, val_count + 2); + if (ret != val_count + 2) { + dev_err(dev, "I2C write failed, ret = %d\n", ret); + return ret < 0 ? ret : -EIO; + } + return 0; +} + +static int cx2072x_reg_write(void *context, unsigned int reg, + unsigned int value) +{ + __le32 raw_value; + unsigned int size; + + size = cx2072x_register_size(reg); + + if (reg == CX2072X_UM_INTERRUPT_CRTL_E) { + /* Update the MSB byte only */ + reg += 3; + size = 1; + value >>= 24; + } + + raw_value = cpu_to_le32(value); + return cx2072x_reg_raw_write(context, reg, &raw_value, size); +} + +static int cx2072x_reg_read(void *context, unsigned int reg, + unsigned int *value) +{ + struct i2c_client *client = context; + struct device *dev = &client->dev; + __le32 recv_buf = 0; + struct i2c_msg msgs[2]; + unsigned int size; + u8 send_buf[2]; + int ret; + + size = cx2072x_register_size(reg); + + send_buf[0] = reg >> 8; + send_buf[1] = reg & 0xff; + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(send_buf); + msgs[0].buf = send_buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = size; + msgs[1].buf = (u8 *)&recv_buf; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(dev, "Failed to read register, ret = %d\n", ret); + return ret < 0 ? ret : -EIO; + } + + *value = le32_to_cpu(recv_buf); + return 0; +} + +/* get suggested pre_div valuce from mclk frequency */ +static unsigned int get_div_from_mclk(unsigned int mclk) +{ + unsigned int div = 8; + int i; + + for (i = 0; i < ARRAY_SIZE(mclk_pre_div); i++) { + if (mclk <= mclk_pre_div[i].mclk) { + div = mclk_pre_div[i].div; + break; + } + } + return div; +} + +static int cx2072x_config_pll(struct cx2072x_priv *cx2072x) +{ + struct device *dev = cx2072x->dev; + unsigned int pre_div; + unsigned int pre_div_val; + unsigned int pll_input; + unsigned int pll_output; + unsigned int int_div; + unsigned int frac_div; + u64 frac_num; + unsigned int frac; + unsigned int sample_rate = cx2072x->sample_rate; + int pt_sample_per_sync = 2; + int pt_clock_per_sample = 96; + + switch (sample_rate) { + case 48000: + case 32000: + case 24000: + case 16000: + break; + + case 96000: + pt_sample_per_sync = 1; + pt_clock_per_sample = 48; + break; + + case 192000: + pt_sample_per_sync = 0; + pt_clock_per_sample = 24; + break; + + default: + dev_err(dev, "Unsupported sample rate %d\n", sample_rate); + return -EINVAL; + } + + /* Configure PLL settings */ + pre_div = get_div_from_mclk(cx2072x->mclk_rate); + pll_input = cx2072x->mclk_rate / pre_div; + pll_output = sample_rate * 3072; + int_div = pll_output / pll_input; + frac_div = pll_output - (int_div * pll_input); + + if (frac_div) { + frac_div *= 1000; + frac_div /= pll_input; + frac_num = (u64)(4000 + frac_div) * ((1 << 20) - 4); + do_div(frac_num, 7); + frac = ((u32)frac_num + 499) / 1000; + } + pre_div_val = (pre_div - 1) * 2; + + regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST4, + 0x40 | (pre_div_val << 8)); + if (frac_div == 0) { + /* Int mode */ + regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7, 0x100); + } else { + /* frac mode */ + regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST6, + frac & 0xfff); + regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST7, + (u8)(frac >> 12)); + } + + int_div--; + regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST8, int_div); + + /* configure PLL tracking */ + if (frac_div == 0) { + /* disable PLL tracking */ + regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST16, 0x00); + } else { + /* configure and enable PLL tracking */ + regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST16, + (pt_sample_per_sync << 4) & 0xf0); + regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST17, + pt_clock_per_sample); + regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST18, + pt_clock_per_sample * 3 / 2); + regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST19, 0x01); + regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST20, 0x02); + regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_TEST16, + 0x01, 0x01); + } + + return 0; +} + +static int cx2072x_config_i2spcm(struct cx2072x_priv *cx2072x) +{ + struct device *dev = cx2072x->dev; + unsigned int bclk_rate = 0; + int is_i2s = 0; + int has_one_bit_delay = 0; + int is_frame_inv = 0; + int is_bclk_inv = 0; + int pulse_len; + int frame_len = cx2072x->frame_size; + int sample_size = cx2072x->sample_size; + int i2s_right_slot; + int i2s_right_pause_interval = 0; + int i2s_right_pause_pos; + int is_big_endian = 1; + u64 div; + unsigned int mod; + union cx2072x_reg_i2spcm_ctrl_reg1 reg1; + union cx2072x_reg_i2spcm_ctrl_reg2 reg2; + union cx2072x_reg_i2spcm_ctrl_reg3 reg3; + union cx2072x_reg_i2spcm_ctrl_reg4 reg4; + union cx2072x_reg_i2spcm_ctrl_reg5 reg5; + union cx2072x_reg_i2spcm_ctrl_reg6 reg6; + union cx2072x_reg_digital_bios_test2 regdbt2; + const unsigned int fmt = cx2072x->dai_fmt; + + if (frame_len <= 0) { + dev_err(dev, "Incorrect frame len %d\n", frame_len); + return -EINVAL; + } + + if (sample_size <= 0) { + dev_err(dev, "Incorrect sample size %d\n", sample_size); + return -EINVAL; + } + + dev_dbg(dev, "config_i2spcm set_dai_fmt- %08x\n", fmt); + + regdbt2.ulval = 0xac; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + reg2.r.tx_master = 1; + reg3.r.rx_master = 1; + break; + + case SND_SOC_DAIFMT_CBC_CFC: + reg2.r.tx_master = 0; + reg3.r.rx_master = 0; + break; + + default: + dev_err(dev, "Unsupported DAI clocking mode\n"); + return -EINVAL; + } + + /* set format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + is_i2s = 1; + has_one_bit_delay = 1; + pulse_len = frame_len / 2; + break; + + case SND_SOC_DAIFMT_RIGHT_J: + is_i2s = 1; + pulse_len = frame_len / 2; + break; + + case SND_SOC_DAIFMT_LEFT_J: + is_i2s = 1; + pulse_len = frame_len / 2; + break; + + default: + dev_err(dev, "Unsupported DAI format\n"); + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + is_frame_inv = is_i2s; + is_bclk_inv = is_i2s; + break; + + case SND_SOC_DAIFMT_IB_IF: + is_frame_inv = !is_i2s; + is_bclk_inv = !is_i2s; + break; + + case SND_SOC_DAIFMT_IB_NF: + is_frame_inv = is_i2s; + is_bclk_inv = !is_i2s; + break; + + case SND_SOC_DAIFMT_NB_IF: + is_frame_inv = !is_i2s; + is_bclk_inv = is_i2s; + break; + + default: + dev_err(dev, "Unsupported DAI clock inversion\n"); + return -EINVAL; + } + + reg1.r.rx_data_one_line = 1; + reg1.r.tx_data_one_line = 1; + + if (is_i2s) { + i2s_right_slot = (frame_len / 2) / BITS_PER_SLOT; + i2s_right_pause_interval = (frame_len / 2) % BITS_PER_SLOT; + i2s_right_pause_pos = i2s_right_slot * BITS_PER_SLOT; + } + + reg1.r.rx_ws_pol = is_frame_inv; + reg1.r.rx_ws_wid = pulse_len - 1; + + reg1.r.rx_frm_len = frame_len / BITS_PER_SLOT - 1; + reg1.r.rx_sa_size = (sample_size / BITS_PER_SLOT) - 1; + + reg1.r.tx_ws_pol = reg1.r.rx_ws_pol; + reg1.r.tx_ws_wid = pulse_len - 1; + reg1.r.tx_frm_len = reg1.r.rx_frm_len; + reg1.r.tx_sa_size = reg1.r.rx_sa_size; + + reg2.r.tx_endian_sel = !is_big_endian; + reg2.r.tx_dstart_dly = has_one_bit_delay; + if (cx2072x->en_aec_ref) + reg2.r.tx_dstart_dly = 0; + + reg3.r.rx_endian_sel = !is_big_endian; + reg3.r.rx_dstart_dly = has_one_bit_delay; + + reg4.ulval = 0; + + if (is_i2s) { + reg2.r.tx_slot_1 = 0; + reg2.r.tx_slot_2 = i2s_right_slot; + reg3.r.rx_slot_1 = 0; + if (cx2072x->en_aec_ref) + reg3.r.rx_slot_2 = 0; + else + reg3.r.rx_slot_2 = i2s_right_slot; + reg6.r.rx_pause_start_pos = i2s_right_pause_pos; + reg6.r.rx_pause_cycles = i2s_right_pause_interval; + reg6.r.tx_pause_start_pos = i2s_right_pause_pos; + reg6.r.tx_pause_cycles = i2s_right_pause_interval; + } else { + dev_err(dev, "TDM mode is not implemented yet\n"); + return -EINVAL; + } + regdbt2.r.i2s_bclk_invert = is_bclk_inv; + + /* Configures the BCLK output */ + bclk_rate = cx2072x->sample_rate * frame_len; + reg5.r.i2s_pcm_clk_div_chan_en = 0; + + /* Disables bclk output before setting new value */ + regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL5, 0); + + if (reg2.r.tx_master) { + /* Configures BCLK rate */ + div = PLL_OUT_HZ_48; + mod = do_div(div, bclk_rate); + if (mod) { + dev_err(dev, "Unsupported BCLK %dHz\n", bclk_rate); + return -EINVAL; + } + dev_dbg(dev, "enables BCLK %dHz output\n", bclk_rate); + reg5.r.i2s_pcm_clk_div = (u32)div - 1; + reg5.r.i2s_pcm_clk_div_chan_en = 1; + } + + regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL1, reg1.ulval); + regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL2, 0xffffffc0, + reg2.ulval); + regmap_update_bits(cx2072x->regmap, CX2072X_I2SPCM_CONTROL3, 0xffffffc0, + reg3.ulval); + regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL4, reg4.ulval); + regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL6, reg6.ulval); + regmap_write(cx2072x->regmap, CX2072X_I2SPCM_CONTROL5, reg5.ulval); + + regmap_write(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2, + regdbt2.ulval); + + return 0; +} + +static int afg_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0, + 0x00, 0x10); + break; + + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST0, + 0x10, 0x10); + break; + } + + return 0; +} + +static const struct snd_kcontrol_new cx2072x_snd_controls[] = { + SOC_DOUBLE_R_TLV("PortD Boost Volume", CX2072X_PORTD_GAIN_LEFT, + CX2072X_PORTD_GAIN_RIGHT, 0, 3, 0, boost_tlv), + SOC_DOUBLE_R_TLV("PortC Boost Volume", CX2072X_PORTC_GAIN_LEFT, + CX2072X_PORTC_GAIN_RIGHT, 0, 3, 0, boost_tlv), + SOC_DOUBLE_R_TLV("PortB Boost Volume", CX2072X_PORTB_GAIN_LEFT, + CX2072X_PORTB_GAIN_RIGHT, 0, 3, 0, boost_tlv), + SOC_DOUBLE_R_TLV("PortD ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_1, + CX2072X_ADC1_AMP_GAIN_RIGHT_1, 0, 0x4a, 0, adc_tlv), + SOC_DOUBLE_R_TLV("PortC ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_2, + CX2072X_ADC1_AMP_GAIN_RIGHT_2, 0, 0x4a, 0, adc_tlv), + SOC_DOUBLE_R_TLV("PortB ADC1 Volume", CX2072X_ADC1_AMP_GAIN_LEFT_0, + CX2072X_ADC1_AMP_GAIN_RIGHT_0, 0, 0x4a, 0, adc_tlv), + SOC_DOUBLE_R_TLV("DAC1 Volume", CX2072X_DAC1_AMP_GAIN_LEFT, + CX2072X_DAC1_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv), + SOC_DOUBLE_R("DAC1 Switch", CX2072X_DAC1_AMP_GAIN_LEFT, + CX2072X_DAC1_AMP_GAIN_RIGHT, 7, 1, 0), + SOC_DOUBLE_R_TLV("DAC2 Volume", CX2072X_DAC2_AMP_GAIN_LEFT, + CX2072X_DAC2_AMP_GAIN_RIGHT, 0, 0x4a, 0, dac_tlv), + SOC_SINGLE_TLV("HPF Freq", CX2072X_CODEC_TEST9, 0, 0x3f, 0, hpf_tlv), + SOC_DOUBLE("HPF Switch", CX2072X_CODEC_TEST9, 8, 9, 1, 1), + SOC_SINGLE("PortA HP Amp Switch", CX2072X_PORTA_PIN_CTRL, 7, 1, 0), +}; + +static int cx2072x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + struct device *dev = codec->dev; + const unsigned int sample_rate = params_rate(params); + int sample_size, frame_size; + + /* Data sizes if not using TDM */ + sample_size = params_width(params); + + if (sample_size < 0) + return sample_size; + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) + return frame_size; + + if (cx2072x->mclk_rate == 0) { + dev_err(dev, "Master clock rate is not configured\n"); + return -EINVAL; + } + + if (cx2072x->bclk_ratio) + frame_size = cx2072x->bclk_ratio; + + switch (sample_rate) { + case 48000: + case 32000: + case 24000: + case 16000: + case 96000: + case 192000: + break; + + default: + dev_err(dev, "Unsupported sample rate %d\n", sample_rate); + return -EINVAL; + } + + dev_dbg(dev, "Sample size %d bits, frame = %d bits, rate = %d Hz\n", + sample_size, frame_size, sample_rate); + + cx2072x->frame_size = frame_size; + cx2072x->sample_size = sample_size; + cx2072x->sample_rate = sample_rate; + + if (dai->id == CX2072X_DAI_DSP) { + cx2072x->en_aec_ref = true; + dev_dbg(cx2072x->dev, "enables aec reference\n"); + regmap_write(cx2072x->regmap, + CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 3); + } + + if (cx2072x->pll_changed) { + cx2072x_config_pll(cx2072x); + cx2072x->pll_changed = false; + } + + if (cx2072x->i2spcm_changed) { + cx2072x_config_i2spcm(cx2072x); + cx2072x->i2spcm_changed = false; + } + + return 0; +} + +static int cx2072x_set_dai_bclk_ratio(struct snd_soc_dai *dai, + unsigned int ratio) +{ + struct snd_soc_component *codec = dai->component; + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + + cx2072x->bclk_ratio = ratio; + return 0; +} + +static int cx2072x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *codec = dai->component; + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + + if (clk_set_rate(cx2072x->mclk, freq)) { + dev_err(codec->dev, "set clk rate failed\n"); + return -EINVAL; + } + + cx2072x->mclk_rate = freq; + return 0; +} + +static int cx2072x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *codec = dai->component; + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + struct device *dev = codec->dev; + + dev_dbg(dev, "set_dai_fmt- %08x\n", fmt); + /* set master/slave */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + case SND_SOC_DAIFMT_CBC_CFC: + break; + + default: + dev_err(dev, "Unsupported DAI master mode\n"); + return -EINVAL; + } + + /* set format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + break; + + default: + dev_err(dev, "Unsupported DAI format\n"); + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_IB_IF: + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_NB_IF: + break; + + default: + dev_err(dev, "Unsupported DAI clock inversion\n"); + return -EINVAL; + } + + cx2072x->dai_fmt = fmt; + return 0; +} + +static const struct snd_kcontrol_new portaouten_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_PORTA_PIN_CTRL, 6, 1, 0); + +static const struct snd_kcontrol_new porteouten_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_PORTE_PIN_CTRL, 6, 1, 0); + +static const struct snd_kcontrol_new portgouten_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_PORTG_PIN_CTRL, 6, 1, 0); + +static const struct snd_kcontrol_new portmouten_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_PORTM_PIN_CTRL, 6, 1, 0); + +static const struct snd_kcontrol_new portbinen_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_PORTB_PIN_CTRL, 5, 1, 0); + +static const struct snd_kcontrol_new portcinen_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_PORTC_PIN_CTRL, 5, 1, 0); + +static const struct snd_kcontrol_new portdinen_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_PORTD_PIN_CTRL, 5, 1, 0); + +static const struct snd_kcontrol_new porteinen_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_PORTE_PIN_CTRL, 5, 1, 0); + +static const struct snd_kcontrol_new i2sadc1l_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 0, 1, 0); + +static const struct snd_kcontrol_new i2sadc1r_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 1, 1, 0); + +static const struct snd_kcontrol_new i2sadc2l_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 2, 1, 0); + +static const struct snd_kcontrol_new i2sadc2r_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL2, 3, 1, 0); + +static const struct snd_kcontrol_new i2sdac1l_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 0, 1, 0); + +static const struct snd_kcontrol_new i2sdac1r_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 1, 1, 0); + +static const struct snd_kcontrol_new i2sdac2l_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 2, 1, 0); + +static const struct snd_kcontrol_new i2sdac2r_ctl = + SOC_DAPM_SINGLE("Switch", CX2072X_I2SPCM_CONTROL3, 3, 1, 0); + +static const char * const dac_enum_text[] = { + "DAC1 Switch", "DAC2 Switch", +}; + +static const struct soc_enum porta_dac_enum = +SOC_ENUM_SINGLE(CX2072X_PORTA_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text); + +static const struct snd_kcontrol_new porta_mux = +SOC_DAPM_ENUM("PortA Mux", porta_dac_enum); + +static const struct soc_enum portg_dac_enum = +SOC_ENUM_SINGLE(CX2072X_PORTG_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text); + +static const struct snd_kcontrol_new portg_mux = +SOC_DAPM_ENUM("PortG Mux", portg_dac_enum); + +static const struct soc_enum porte_dac_enum = +SOC_ENUM_SINGLE(CX2072X_PORTE_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text); + +static const struct snd_kcontrol_new porte_mux = +SOC_DAPM_ENUM("PortE Mux", porte_dac_enum); + +static const struct soc_enum portm_dac_enum = +SOC_ENUM_SINGLE(CX2072X_PORTM_CONNECTION_SELECT_CTRL, 0, 2, dac_enum_text); + +static const struct snd_kcontrol_new portm_mux = +SOC_DAPM_ENUM("PortM Mux", portm_dac_enum); + +static const char * const adc1in_sel_text[] = { + "PortB Switch", "PortD Switch", "PortC Switch", "Widget15 Switch", + "PortE Switch", "PortF Switch", "PortH Switch" +}; + +static const struct soc_enum adc1in_sel_enum = +SOC_ENUM_SINGLE(CX2072X_ADC1_CONNECTION_SELECT_CONTROL, 0, 7, adc1in_sel_text); + +static const struct snd_kcontrol_new adc1_mux = +SOC_DAPM_ENUM("ADC1 Mux", adc1in_sel_enum); + +static const char * const adc2in_sel_text[] = { + "PortC Switch", "Widget15 Switch", "PortH Switch" +}; + +static const struct soc_enum adc2in_sel_enum = +SOC_ENUM_SINGLE(CX2072X_ADC2_CONNECTION_SELECT_CONTROL, 0, 3, adc2in_sel_text); + +static const struct snd_kcontrol_new adc2_mux = +SOC_DAPM_ENUM("ADC2 Mux", adc2in_sel_enum); + +static const struct snd_kcontrol_new wid15_mix[] = { + SOC_DAPM_SINGLE("DAC1L Switch", CX2072X_MIXER_GAIN_LEFT_0, 7, 1, 1), + SOC_DAPM_SINGLE("DAC1R Switch", CX2072X_MIXER_GAIN_RIGHT_0, 7, 1, 1), + SOC_DAPM_SINGLE("DAC2L Switch", CX2072X_MIXER_GAIN_LEFT_1, 7, 1, 1), + SOC_DAPM_SINGLE("DAC2R Switch", CX2072X_MIXER_GAIN_RIGHT_1, 7, 1, 1), +}; + +#define CX2072X_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, wmask, won_val, \ + woff_val, wevent, wflags) \ + {.id = snd_soc_dapm_supply, .name = wname, .kcontrol_news = NULL, \ + .num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \ + .on_val = won_val, .off_val = woff_val, \ + .subseq = wsubseq, .event = wevent, .event_flags = wflags} + +#define CX2072X_DAPM_SWITCH(wname, wreg, wshift, wmask, won_val, woff_val, \ + wevent, wflags) \ + {.id = snd_soc_dapm_switch, .name = wname, .kcontrol_news = NULL, \ + .num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \ + .on_val = won_val, .off_val = woff_val, \ + .event = wevent, .event_flags = wflags} + +#define CX2072X_DAPM_SWITCH(wname, wreg, wshift, wmask, won_val, woff_val, \ + wevent, wflags) \ + {.id = snd_soc_dapm_switch, .name = wname, .kcontrol_news = NULL, \ + .num_kcontrols = 0, .reg = wreg, .shift = wshift, .mask = wmask, \ + .on_val = won_val, .off_val = woff_val, \ + .event = wevent, .event_flags = wflags} + +#define CX2072X_DAPM_REG_E(wid, wname, wreg, wshift, wmask, won_val, woff_val, \ + wevent, wflags) \ + {.id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \ + .reg = wreg, .shift = wshift, .mask = wmask, \ + .on_val = won_val, .off_val = woff_val, \ + .event = wevent, .event_flags = wflags} + +static const struct snd_soc_dapm_widget cx2072x_dapm_widgets[] = { + /*Playback*/ + SND_SOC_DAPM_AIF_IN("In AIF", "Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SWITCH("I2S DAC1L", SND_SOC_NOPM, 0, 0, &i2sdac1l_ctl), + SND_SOC_DAPM_SWITCH("I2S DAC1R", SND_SOC_NOPM, 0, 0, &i2sdac1r_ctl), + SND_SOC_DAPM_SWITCH("I2S DAC2L", SND_SOC_NOPM, 0, 0, &i2sdac2l_ctl), + SND_SOC_DAPM_SWITCH("I2S DAC2R", SND_SOC_NOPM, 0, 0, &i2sdac2r_ctl), + + SND_SOC_DAPM_REG(snd_soc_dapm_dac, "DAC1", CX2072X_DAC1_POWER_STATE, + 0, 0xfff, 0x00, 0x03), + + SND_SOC_DAPM_REG(snd_soc_dapm_dac, "DAC2", CX2072X_DAC2_POWER_STATE, + 0, 0xfff, 0x00, 0x03), + + SND_SOC_DAPM_MUX("PortA Mux", SND_SOC_NOPM, 0, 0, &porta_mux), + SND_SOC_DAPM_MUX("PortG Mux", SND_SOC_NOPM, 0, 0, &portg_mux), + SND_SOC_DAPM_MUX("PortE Mux", SND_SOC_NOPM, 0, 0, &porte_mux), + SND_SOC_DAPM_MUX("PortM Mux", SND_SOC_NOPM, 0, 0, &portm_mux), + + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortA Power", + CX2072X_PORTA_POWER_STATE, 0, 0xfff, 0x00, 0x03), + + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortM Power", + CX2072X_PORTM_POWER_STATE, 0, 0xfff, 0x00, 0x03), + + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortG Power", + CX2072X_PORTG_POWER_STATE, 0, 0xfff, 0x00, 0x03), + + CX2072X_DAPM_SUPPLY_S("AFG Power", 0, CX2072X_AFG_POWER_STATE, + 0, 0xfff, 0x00, 0x03, afg_power_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SWITCH("PortA Out En", SND_SOC_NOPM, 0, 0, + &portaouten_ctl), + SND_SOC_DAPM_SWITCH("PortE Out En", SND_SOC_NOPM, 0, 0, + &porteouten_ctl), + SND_SOC_DAPM_SWITCH("PortG Out En", SND_SOC_NOPM, 0, 0, + &portgouten_ctl), + SND_SOC_DAPM_SWITCH("PortM Out En", SND_SOC_NOPM, 0, 0, + &portmouten_ctl), + + SND_SOC_DAPM_OUTPUT("PORTA"), + SND_SOC_DAPM_OUTPUT("PORTG"), + SND_SOC_DAPM_OUTPUT("PORTE"), + SND_SOC_DAPM_OUTPUT("PORTM"), + SND_SOC_DAPM_OUTPUT("AEC REF"), + + /*Capture*/ + SND_SOC_DAPM_AIF_OUT("Out AIF", "Capture", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SWITCH("I2S ADC1L", SND_SOC_NOPM, 0, 0, &i2sadc1l_ctl), + SND_SOC_DAPM_SWITCH("I2S ADC1R", SND_SOC_NOPM, 0, 0, &i2sadc1r_ctl), + SND_SOC_DAPM_SWITCH("I2S ADC2L", SND_SOC_NOPM, 0, 0, &i2sadc2l_ctl), + SND_SOC_DAPM_SWITCH("I2S ADC2R", SND_SOC_NOPM, 0, 0, &i2sadc2r_ctl), + + SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC1", CX2072X_ADC1_POWER_STATE, + 0, 0xff, 0x00, 0x03), + SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC2", CX2072X_ADC2_POWER_STATE, + 0, 0xff, 0x00, 0x03), + + SND_SOC_DAPM_MUX("ADC1 Mux", SND_SOC_NOPM, 0, 0, &adc1_mux), + SND_SOC_DAPM_MUX("ADC2 Mux", SND_SOC_NOPM, 0, 0, &adc2_mux), + + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortB Power", + CX2072X_PORTB_POWER_STATE, 0, 0xfff, 0x00, 0x03), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortC Power", + CX2072X_PORTC_POWER_STATE, 0, 0xfff, 0x00, 0x03), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortD Power", + CX2072X_PORTD_POWER_STATE, 0, 0xfff, 0x00, 0x03), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "PortE Power", + CX2072X_PORTE_POWER_STATE, 0, 0xfff, 0x00, 0x03), + SND_SOC_DAPM_REG(snd_soc_dapm_supply, "Widget15 Power", + CX2072X_MIXER_POWER_STATE, 0, 0xfff, 0x00, 0x03), + + SND_SOC_DAPM_MIXER("Widget15 Mixer", SND_SOC_NOPM, 0, 0, + wid15_mix, ARRAY_SIZE(wid15_mix)), + SND_SOC_DAPM_SWITCH("PortB In En", SND_SOC_NOPM, 0, 0, &portbinen_ctl), + SND_SOC_DAPM_SWITCH("PortC In En", SND_SOC_NOPM, 0, 0, &portcinen_ctl), + SND_SOC_DAPM_SWITCH("PortD In En", SND_SOC_NOPM, 0, 0, &portdinen_ctl), + SND_SOC_DAPM_SWITCH("PortE In En", SND_SOC_NOPM, 0, 0, &porteinen_ctl), + + SND_SOC_DAPM_MICBIAS("Headset Bias", CX2072X_ANALOG_TEST11, 1, 0), + SND_SOC_DAPM_MICBIAS("PortB Mic Bias", CX2072X_PORTB_PIN_CTRL, 2, 0), + SND_SOC_DAPM_MICBIAS("PortD Mic Bias", CX2072X_PORTD_PIN_CTRL, 2, 0), + SND_SOC_DAPM_MICBIAS("PortE Mic Bias", CX2072X_PORTE_PIN_CTRL, 2, 0), + SND_SOC_DAPM_INPUT("PORTB"), + SND_SOC_DAPM_INPUT("PORTC"), + SND_SOC_DAPM_INPUT("PORTD"), + SND_SOC_DAPM_INPUT("PORTEIN"), + +}; + +static const struct snd_soc_dapm_route cx2072x_intercon[] = { + /* Playback */ + {"In AIF", NULL, "AFG Power"}, + {"I2S DAC1L", "Switch", "In AIF"}, + {"I2S DAC1R", "Switch", "In AIF"}, + {"I2S DAC2L", "Switch", "In AIF"}, + {"I2S DAC2R", "Switch", "In AIF"}, + {"DAC1", NULL, "I2S DAC1L"}, + {"DAC1", NULL, "I2S DAC1R"}, + {"DAC2", NULL, "I2S DAC2L"}, + {"DAC2", NULL, "I2S DAC2R"}, + {"PortA Mux", "DAC1 Switch", "DAC1"}, + {"PortA Mux", "DAC2 Switch", "DAC2"}, + {"PortG Mux", "DAC1 Switch", "DAC1"}, + {"PortG Mux", "DAC2 Switch", "DAC2"}, + {"PortE Mux", "DAC1 Switch", "DAC1"}, + {"PortE Mux", "DAC2 Switch", "DAC2"}, + {"PortM Mux", "DAC1 Switch", "DAC1"}, + {"PortM Mux", "DAC2 Switch", "DAC2"}, + {"Widget15 Mixer", "DAC1L Switch", "DAC1"}, + {"Widget15 Mixer", "DAC1R Switch", "DAC2"}, + {"Widget15 Mixer", "DAC2L Switch", "DAC1"}, + {"Widget15 Mixer", "DAC2R Switch", "DAC2"}, + {"Widget15 Mixer", NULL, "Widget15 Power"}, + {"PortA Out En", "Switch", "PortA Mux"}, + {"PortG Out En", "Switch", "PortG Mux"}, + {"PortE Out En", "Switch", "PortE Mux"}, + {"PortM Out En", "Switch", "PortM Mux"}, + {"PortA Mux", NULL, "PortA Power"}, + {"PortG Mux", NULL, "PortG Power"}, + {"PortE Mux", NULL, "PortE Power"}, + {"PortM Mux", NULL, "PortM Power"}, + {"PortA Out En", NULL, "PortA Power"}, + {"PortG Out En", NULL, "PortG Power"}, + {"PortE Out En", NULL, "PortE Power"}, + {"PortM Out En", NULL, "PortM Power"}, + {"PORTA", NULL, "PortA Out En"}, + {"PORTG", NULL, "PortG Out En"}, + {"PORTE", NULL, "PortE Out En"}, + {"PORTM", NULL, "PortM Out En"}, + + /* Capture */ + {"PORTD", NULL, "Headset Bias"}, + {"PortB In En", "Switch", "PORTB"}, + {"PortC In En", "Switch", "PORTC"}, + {"PortD In En", "Switch", "PORTD"}, + {"PortE In En", "Switch", "PORTEIN"}, + {"ADC1 Mux", "PortB Switch", "PortB In En"}, + {"ADC1 Mux", "PortC Switch", "PortC In En"}, + {"ADC1 Mux", "PortD Switch", "PortD In En"}, + {"ADC1 Mux", "PortE Switch", "PortE In En"}, + {"ADC1 Mux", "Widget15 Switch", "Widget15 Mixer"}, + {"ADC2 Mux", "PortC Switch", "PortC In En"}, + {"ADC2 Mux", "Widget15 Switch", "Widget15 Mixer"}, + {"ADC1", NULL, "ADC1 Mux"}, + {"ADC2", NULL, "ADC2 Mux"}, + {"I2S ADC1L", "Switch", "ADC1"}, + {"I2S ADC1R", "Switch", "ADC1"}, + {"I2S ADC2L", "Switch", "ADC2"}, + {"I2S ADC2R", "Switch", "ADC2"}, + {"Out AIF", NULL, "I2S ADC1L"}, + {"Out AIF", NULL, "I2S ADC1R"}, + {"Out AIF", NULL, "I2S ADC2L"}, + {"Out AIF", NULL, "I2S ADC2R"}, + {"Out AIF", NULL, "AFG Power"}, + {"AEC REF", NULL, "Out AIF"}, + {"PortB In En", NULL, "PortB Power"}, + {"PortC In En", NULL, "PortC Power"}, + {"PortD In En", NULL, "PortD Power"}, + {"PortE In En", NULL, "PortE Power"}, +}; + +static int cx2072x_set_bias_level(struct snd_soc_component *codec, + enum snd_soc_bias_level level) +{ + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + const enum snd_soc_bias_level old_level = + snd_soc_component_get_bias_level(codec); + + if (level == SND_SOC_BIAS_STANDBY && old_level == SND_SOC_BIAS_OFF) + regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 0); + else if (level == SND_SOC_BIAS_OFF && old_level != SND_SOC_BIAS_OFF) + regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 3); + + return 0; +} + +/* + * FIXME: the whole jack detection code below is pretty platform-specific; + * it has lots of implicit assumptions about the pins, etc. + * However, since we have no other code and reference, take this hard-coded + * setup for now. Once when we have different platform implementations, + * this needs to be rewritten in a more generic form, or moving into the + * platform data. + */ +static void cx2072x_enable_jack_detect(struct snd_soc_component *codec) +{ + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + + /* No-sticky input type */ + regmap_write(cx2072x->regmap, CX2072X_GPIO_STICKY_MASK, 0x1f); + + /* Use GPOI0 as interrupt pin */ + regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24); + + /* Enables unsolitited message on PortA */ + regmap_write(cx2072x->regmap, CX2072X_PORTA_UNSOLICITED_RESPONSE, 0x80); + + /* support both nokia and apple headset set. Monitor time = 275 ms */ + regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST15, 0x73); + + /* Disable TIP detection */ + regmap_write(cx2072x->regmap, CX2072X_ANALOG_TEST12, 0x300); + + /* Switch MusicD3Live pin to GPIO */ + regmap_write(cx2072x->regmap, CX2072X_DIGITAL_TEST1, 0); + + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PORTD"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Headset Bias"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PortD Mic Bias"); + + snd_soc_dapm_mutex_unlock(dapm); +} + +static void cx2072x_disable_jack_detect(struct snd_soc_component *codec) +{ + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + + regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0); + regmap_write(cx2072x->regmap, CX2072X_PORTA_UNSOLICITED_RESPONSE, 0); +} + +static int cx2072x_jack_status_check(void *data) +{ + struct snd_soc_component *codec = data; + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + unsigned int jack; + unsigned int type = 0; + int state = 0; + + mutex_lock(&cx2072x->lock); + + regmap_read(cx2072x->regmap, CX2072X_PORTA_PIN_SENSE, &jack); + jack = jack >> 24; + regmap_read(cx2072x->regmap, CX2072X_DIGITAL_TEST11, &type); + + if (jack == 0x80) { + type = type >> 8; + + if (type & 0x8) { + /* Apple headset */ + state |= SND_JACK_HEADSET; + if (type & 0x2) + state |= SND_JACK_BTN_0; + } else { + /* + * Nokia headset (type & 0x4) and + * regular Headphone + */ + state |= SND_JACK_HEADPHONE; + } + } + + /* clear interrupt */ + regmap_write(cx2072x->regmap, CX2072X_UM_INTERRUPT_CRTL_E, 0x12 << 24); + + mutex_unlock(&cx2072x->lock); + + dev_dbg(codec->dev, "CX2072X_HSDETECT type=0x%X,Jack state = %x\n", + type, state); + return state; +} + +static const struct snd_soc_jack_gpio cx2072x_jack_gpio = { + .name = "headset", + .report = SND_JACK_HEADSET | SND_JACK_BTN_0, + .debounce_time = 150, + .wake = true, + .jack_status_check = cx2072x_jack_status_check, +}; + +static int cx2072x_set_jack(struct snd_soc_component *codec, + struct snd_soc_jack *jack, void *data) +{ + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + int err; + + if (!jack) { + cx2072x_disable_jack_detect(codec); + return 0; + } + + if (!cx2072x->jack_gpio.gpiod_dev) { + cx2072x->jack_gpio = cx2072x_jack_gpio; + cx2072x->jack_gpio.gpiod_dev = codec->dev; + cx2072x->jack_gpio.data = codec; + err = snd_soc_jack_add_gpios(jack, 1, &cx2072x->jack_gpio); + if (err) { + cx2072x->jack_gpio.gpiod_dev = NULL; + return err; + } + } + + cx2072x_enable_jack_detect(codec); + return 0; +} + +static int cx2072x_probe(struct snd_soc_component *codec) +{ + struct cx2072x_priv *cx2072x = snd_soc_component_get_drvdata(codec); + + cx2072x->codec = codec; + + /* + * FIXME: below is, again, a very platform-specific init sequence, + * but we keep the code here just for simplicity. It seems that all + * existing hardware implementations require this, so there is no very + * much reason to move this out of the codec driver to the platform + * data. + * But of course it's no "right" thing; if you are a good boy, don't + * read and follow the code like this! + */ + pm_runtime_get_sync(codec->dev); + regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 0); + + regmap_multi_reg_write(cx2072x->regmap, cx2072x_reg_init, + ARRAY_SIZE(cx2072x_reg_init)); + + /* configure PortC as input device */ + regmap_update_bits(cx2072x->regmap, CX2072X_PORTC_PIN_CTRL, + 0x20, 0x20); + + regmap_update_bits(cx2072x->regmap, CX2072X_DIGITAL_BIOS_TEST2, + 0x84, 0xff); + + regmap_write(cx2072x->regmap, CX2072X_AFG_POWER_STATE, 3); + pm_runtime_put(codec->dev); + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_driver_cx2072x = { + .probe = cx2072x_probe, + .set_bias_level = cx2072x_set_bias_level, + .set_jack = cx2072x_set_jack, + .controls = cx2072x_snd_controls, + .num_controls = ARRAY_SIZE(cx2072x_snd_controls), + .dapm_widgets = cx2072x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cx2072x_dapm_widgets), + .dapm_routes = cx2072x_intercon, + .num_dapm_routes = ARRAY_SIZE(cx2072x_intercon), + .endianness = 1, +}; + +/* + * DAI ops + */ +static const struct snd_soc_dai_ops cx2072x_dai_ops = { + .set_sysclk = cx2072x_set_dai_sysclk, + .set_fmt = cx2072x_set_dai_fmt, + .hw_params = cx2072x_hw_params, + .set_bclk_ratio = cx2072x_set_dai_bclk_ratio, +}; + +static int cx2072x_dsp_dai_probe(struct snd_soc_dai *dai) +{ + struct cx2072x_priv *cx2072x = + snd_soc_component_get_drvdata(dai->component); + + cx2072x->en_aec_ref = true; + return 0; +} + +static const struct snd_soc_dai_ops cx2072x_dai_ops2 = { + .probe = cx2072x_dsp_dai_probe, + .set_sysclk = cx2072x_set_dai_sysclk, + .set_fmt = cx2072x_set_dai_fmt, + .hw_params = cx2072x_hw_params, + .set_bclk_ratio = cx2072x_set_dai_bclk_ratio, +}; + +#define CX2072X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = { + { /* playback and capture */ + .name = "cx2072x-hifi", + .id = CX2072X_DAI_HIFI, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = CX2072X_RATES_DSP, + .formats = CX2072X_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = CX2072X_RATES_DSP, + .formats = CX2072X_FORMATS, + }, + .ops = &cx2072x_dai_ops, + .symmetric_rate = 1, + }, + { /* plabayck only, return echo reference to Conexant DSP chip */ + .name = "cx2072x-dsp", + .id = CX2072X_DAI_DSP, + .playback = { + .stream_name = "DSP Playback", + .channels_min = 2, + .channels_max = 2, + .rates = CX2072X_RATES_DSP, + .formats = CX2072X_FORMATS, + }, + .ops = &cx2072x_dai_ops2, + }, + { /* plabayck only, return echo reference through I2S TX */ + .name = "cx2072x-aec", + .id = 3, + .capture = { + .stream_name = "AEC Capture", + .channels_min = 2, + .channels_max = 2, + .rates = CX2072X_RATES_DSP, + .formats = CX2072X_FORMATS, + }, + }, +}; + +static const struct regmap_config cx2072x_regmap = { + .reg_bits = 16, + .val_bits = 32, + .max_register = CX2072X_REG_MAX, + .reg_defaults = cx2072x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cx2072x_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .readable_reg = cx2072x_readable_register, + .volatile_reg = cx2072x_volatile_register, + /* Needs custom read/write functions for various register lengths */ + .reg_read = cx2072x_reg_read, + .reg_write = cx2072x_reg_write, +}; + +static int __maybe_unused cx2072x_runtime_suspend(struct device *dev) +{ + struct cx2072x_priv *cx2072x = dev_get_drvdata(dev); + + clk_disable_unprepare(cx2072x->mclk); + return 0; +} + +static int __maybe_unused cx2072x_runtime_resume(struct device *dev) +{ + struct cx2072x_priv *cx2072x = dev_get_drvdata(dev); + + return clk_prepare_enable(cx2072x->mclk); +} + +static int cx2072x_i2c_probe(struct i2c_client *i2c) +{ + struct cx2072x_priv *cx2072x; + unsigned int ven_id, rev_id; + int ret; + + cx2072x = devm_kzalloc(&i2c->dev, sizeof(struct cx2072x_priv), + GFP_KERNEL); + if (!cx2072x) + return -ENOMEM; + + cx2072x->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, + &cx2072x_regmap); + if (IS_ERR(cx2072x->regmap)) + return PTR_ERR(cx2072x->regmap); + + mutex_init(&cx2072x->lock); + + i2c_set_clientdata(i2c, cx2072x); + + cx2072x->dev = &i2c->dev; + cx2072x->pll_changed = true; + cx2072x->i2spcm_changed = true; + cx2072x->bclk_ratio = 0; + + cx2072x->mclk = devm_clk_get(cx2072x->dev, "mclk"); + if (IS_ERR(cx2072x->mclk)) { + dev_err(cx2072x->dev, "Failed to get MCLK\n"); + return PTR_ERR(cx2072x->mclk); + } + + regmap_read(cx2072x->regmap, CX2072X_VENDOR_ID, &ven_id); + regmap_read(cx2072x->regmap, CX2072X_REVISION_ID, &rev_id); + + dev_info(cx2072x->dev, "codec version: %08x,%08x\n", ven_id, rev_id); + + ret = devm_snd_soc_register_component(cx2072x->dev, + &soc_codec_driver_cx2072x, + soc_codec_cx2072x_dai, + ARRAY_SIZE(soc_codec_cx2072x_dai)); + if (ret < 0) + return ret; + + pm_runtime_use_autosuspend(cx2072x->dev); + pm_runtime_enable(cx2072x->dev); + + return 0; +} + +static void cx2072x_i2c_remove(struct i2c_client *i2c) +{ + pm_runtime_disable(&i2c->dev); +} + +static const struct i2c_device_id cx2072x_i2c_id[] = { + { "cx20721", 0 }, + { "cx20723", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, cx2072x_i2c_id); + +#ifdef CONFIG_ACPI +static struct acpi_device_id cx2072x_acpi_match[] = { + { "14F10720", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, cx2072x_acpi_match); +#endif + +static const struct dev_pm_ops cx2072x_runtime_pm = { + SET_RUNTIME_PM_OPS(cx2072x_runtime_suspend, cx2072x_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct i2c_driver cx2072x_i2c_driver = { + .driver = { + .name = "cx2072x", + .acpi_match_table = ACPI_PTR(cx2072x_acpi_match), + .pm = &cx2072x_runtime_pm, + }, + .probe = cx2072x_i2c_probe, + .remove = cx2072x_i2c_remove, + .id_table = cx2072x_i2c_id, +}; + +module_i2c_driver(cx2072x_i2c_driver); + +MODULE_DESCRIPTION("ASoC cx2072x Codec Driver"); +MODULE_AUTHOR("Simon Ho "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cx2072x.h b/sound/soc/codecs/cx2072x.h new file mode 100644 index 0000000000..09e3a92b18 --- /dev/null +++ b/sound/soc/codecs/cx2072x.h @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ALSA SoC CX20721/CX20723 codec driver + * + * Copyright: (C) 2017 Conexant Systems, Inc. + * Author: Simon Ho, + */ + +#ifndef __CX2072X_H__ +#define __CX2072X_H__ + +#define CX2072X_MCLK_PLL 1 +#define CX2072X_MCLK_EXTERNAL_PLL 1 +#define CX2072X_MCLK_INTERNAL_OSC 2 + +/*#define CX2072X_RATES SNDRV_PCM_RATE_8000_192000*/ +#define CX2072X_RATES_DSP SNDRV_PCM_RATE_48000 + +#define CX2072X_REG_MAX 0x8a3c + +#define CX2072X_VENDOR_ID 0x0200 +#define CX2072X_REVISION_ID 0x0208 +#define CX2072X_CURRENT_BCLK_FREQUENCY 0x00dc +#define CX2072X_AFG_POWER_STATE 0x0414 +#define CX2072X_UM_RESPONSE 0x0420 +#define CX2072X_GPIO_DATA 0x0454 +#define CX2072X_GPIO_ENABLE 0x0458 +#define CX2072X_GPIO_DIRECTION 0x045c +#define CX2072X_GPIO_WAKE 0x0460 +#define CX2072X_GPIO_UM_ENABLE 0x0464 +#define CX2072X_GPIO_STICKY_MASK 0x0468 +#define CX2072X_AFG_FUNCTION_RESET 0x07fc +#define CX2072X_DAC1_CONVERTER_FORMAT 0x43c8 +#define CX2072X_DAC1_AMP_GAIN_RIGHT 0x41c0 +#define CX2072X_DAC1_AMP_GAIN_LEFT 0x41e0 +#define CX2072X_DAC1_POWER_STATE 0x4014 +#define CX2072X_DAC1_CONVERTER_STREAM_CHANNEL 0x4018 +#define CX2072X_DAC1_EAPD_ENABLE 0x4030 +#define CX2072X_DAC2_CONVERTER_FORMAT 0x47c8 +#define CX2072X_DAC2_AMP_GAIN_RIGHT 0x45c0 +#define CX2072X_DAC2_AMP_GAIN_LEFT 0x45e0 +#define CX2072X_DAC2_POWER_STATE 0x4414 +#define CX2072X_DAC2_CONVERTER_STREAM_CHANNEL 0x4418 +#define CX2072X_ADC1_CONVERTER_FORMAT 0x4fc8 +#define CX2072X_ADC1_AMP_GAIN_RIGHT_0 0x4d80 +#define CX2072X_ADC1_AMP_GAIN_LEFT_0 0x4da0 +#define CX2072X_ADC1_AMP_GAIN_RIGHT_1 0x4d84 +#define CX2072X_ADC1_AMP_GAIN_LEFT_1 0x4da4 +#define CX2072X_ADC1_AMP_GAIN_RIGHT_2 0x4d88 +#define CX2072X_ADC1_AMP_GAIN_LEFT_2 0x4da8 +#define CX2072X_ADC1_AMP_GAIN_RIGHT_3 0x4d8c +#define CX2072X_ADC1_AMP_GAIN_LEFT_3 0x4dac +#define CX2072X_ADC1_AMP_GAIN_RIGHT_4 0x4d90 +#define CX2072X_ADC1_AMP_GAIN_LEFT_4 0x4db0 +#define CX2072X_ADC1_AMP_GAIN_RIGHT_5 0x4d94 +#define CX2072X_ADC1_AMP_GAIN_LEFT_5 0x4db4 +#define CX2072X_ADC1_AMP_GAIN_RIGHT_6 0x4d98 +#define CX2072X_ADC1_AMP_GAIN_LEFT_6 0x4db8 +#define CX2072X_ADC1_CONNECTION_SELECT_CONTROL 0x4c04 +#define CX2072X_ADC1_POWER_STATE 0x4c14 +#define CX2072X_ADC1_CONVERTER_STREAM_CHANNEL 0x4c18 +#define CX2072X_ADC2_CONVERTER_FORMAT 0x53c8 +#define CX2072X_ADC2_AMP_GAIN_RIGHT_0 0x5180 +#define CX2072X_ADC2_AMP_GAIN_LEFT_0 0x51a0 +#define CX2072X_ADC2_AMP_GAIN_RIGHT_1 0x5184 +#define CX2072X_ADC2_AMP_GAIN_LEFT_1 0x51a4 +#define CX2072X_ADC2_AMP_GAIN_RIGHT_2 0x5188 +#define CX2072X_ADC2_AMP_GAIN_LEFT_2 0x51a8 +#define CX2072X_ADC2_CONNECTION_SELECT_CONTROL 0x5004 +#define CX2072X_ADC2_POWER_STATE 0x5014 +#define CX2072X_ADC2_CONVERTER_STREAM_CHANNEL 0x5018 +#define CX2072X_PORTA_CONNECTION_SELECT_CTRL 0x5804 +#define CX2072X_PORTA_POWER_STATE 0x5814 +#define CX2072X_PORTA_PIN_CTRL 0x581c +#define CX2072X_PORTA_UNSOLICITED_RESPONSE 0x5820 +#define CX2072X_PORTA_PIN_SENSE 0x5824 +#define CX2072X_PORTA_EAPD_BTL 0x5830 +#define CX2072X_PORTB_POWER_STATE 0x6014 +#define CX2072X_PORTB_PIN_CTRL 0x601c +#define CX2072X_PORTB_UNSOLICITED_RESPONSE 0x6020 +#define CX2072X_PORTB_PIN_SENSE 0x6024 +#define CX2072X_PORTB_EAPD_BTL 0x6030 +#define CX2072X_PORTB_GAIN_RIGHT 0x6180 +#define CX2072X_PORTB_GAIN_LEFT 0x61a0 +#define CX2072X_PORTC_POWER_STATE 0x6814 +#define CX2072X_PORTC_PIN_CTRL 0x681c +#define CX2072X_PORTC_GAIN_RIGHT 0x6980 +#define CX2072X_PORTC_GAIN_LEFT 0x69a0 +#define CX2072X_PORTD_POWER_STATE 0x6414 +#define CX2072X_PORTD_PIN_CTRL 0x641c +#define CX2072X_PORTD_UNSOLICITED_RESPONSE 0x6420 +#define CX2072X_PORTD_PIN_SENSE 0x6424 +#define CX2072X_PORTD_GAIN_RIGHT 0x6580 +#define CX2072X_PORTD_GAIN_LEFT 0x65a0 +#define CX2072X_PORTE_CONNECTION_SELECT_CTRL 0x7404 +#define CX2072X_PORTE_POWER_STATE 0x7414 +#define CX2072X_PORTE_PIN_CTRL 0x741c +#define CX2072X_PORTE_UNSOLICITED_RESPONSE 0x7420 +#define CX2072X_PORTE_PIN_SENSE 0x7424 +#define CX2072X_PORTE_EAPD_BTL 0x7430 +#define CX2072X_PORTE_GAIN_RIGHT 0x7580 +#define CX2072X_PORTE_GAIN_LEFT 0x75a0 +#define CX2072X_PORTF_POWER_STATE 0x7814 +#define CX2072X_PORTF_PIN_CTRL 0x781c +#define CX2072X_PORTF_UNSOLICITED_RESPONSE 0x7820 +#define CX2072X_PORTF_PIN_SENSE 0x7824 +#define CX2072X_PORTF_GAIN_RIGHT 0x7980 +#define CX2072X_PORTF_GAIN_LEFT 0x79a0 +#define CX2072X_PORTG_POWER_STATE 0x5c14 +#define CX2072X_PORTG_PIN_CTRL 0x5c1c +#define CX2072X_PORTG_CONNECTION_SELECT_CTRL 0x5c04 +#define CX2072X_PORTG_EAPD_BTL 0x5c30 +#define CX2072X_PORTM_POWER_STATE 0x8814 +#define CX2072X_PORTM_PIN_CTRL 0x881c +#define CX2072X_PORTM_CONNECTION_SELECT_CTRL 0x8804 +#define CX2072X_PORTM_EAPD_BTL 0x8830 +#define CX2072X_MIXER_POWER_STATE 0x5414 +#define CX2072X_MIXER_GAIN_RIGHT_0 0x5580 +#define CX2072X_MIXER_GAIN_LEFT_0 0x55a0 +#define CX2072X_MIXER_GAIN_RIGHT_1 0x5584 +#define CX2072X_MIXER_GAIN_LEFT_1 0x55a4 +#define CX2072X_EQ_ENABLE_BYPASS 0x6d00 +#define CX2072X_EQ_B0_COEFF 0x6d02 +#define CX2072X_EQ_B1_COEFF 0x6d04 +#define CX2072X_EQ_B2_COEFF 0x6d06 +#define CX2072X_EQ_A1_COEFF 0x6d08 +#define CX2072X_EQ_A2_COEFF 0x6d0a +#define CX2072X_EQ_G_COEFF 0x6d0c +#define CX2072X_EQ_BAND 0x6d0d +#define CX2072X_SPKR_DRC_ENABLE_STEP 0x6d10 +#define CX2072X_SPKR_DRC_CONTROL 0x6d14 +#define CX2072X_SPKR_DRC_TEST 0x6d18 +#define CX2072X_DIGITAL_BIOS_TEST0 0x6d80 +#define CX2072X_DIGITAL_BIOS_TEST2 0x6d84 +#define CX2072X_I2SPCM_CONTROL1 0x6e00 +#define CX2072X_I2SPCM_CONTROL2 0x6e04 +#define CX2072X_I2SPCM_CONTROL3 0x6e08 +#define CX2072X_I2SPCM_CONTROL4 0x6e0c +#define CX2072X_I2SPCM_CONTROL5 0x6e10 +#define CX2072X_I2SPCM_CONTROL6 0x6e18 +#define CX2072X_UM_INTERRUPT_CRTL_E 0x6e14 +#define CX2072X_CODEC_TEST2 0x7108 +#define CX2072X_CODEC_TEST9 0x7124 +#define CX2072X_CODEC_TESTXX 0x7290 +#define CX2072X_CODEC_TEST20 0x7310 +#define CX2072X_CODEC_TEST24 0x731c +#define CX2072X_CODEC_TEST26 0x7328 +#define CX2072X_ANALOG_TEST3 0x718c +#define CX2072X_ANALOG_TEST4 0x7190 +#define CX2072X_ANALOG_TEST5 0x7194 +#define CX2072X_ANALOG_TEST6 0x7198 +#define CX2072X_ANALOG_TEST7 0x719c +#define CX2072X_ANALOG_TEST8 0x71a0 +#define CX2072X_ANALOG_TEST9 0x71a4 +#define CX2072X_ANALOG_TEST10 0x71a8 +#define CX2072X_ANALOG_TEST11 0x71ac +#define CX2072X_ANALOG_TEST12 0x71b0 +#define CX2072X_ANALOG_TEST13 0x71b4 +#define CX2072X_DIGITAL_TEST0 0x7200 +#define CX2072X_DIGITAL_TEST1 0x7204 +#define CX2072X_DIGITAL_TEST11 0x722c +#define CX2072X_DIGITAL_TEST12 0x7230 +#define CX2072X_DIGITAL_TEST15 0x723c +#define CX2072X_DIGITAL_TEST16 0x7080 +#define CX2072X_DIGITAL_TEST17 0x7084 +#define CX2072X_DIGITAL_TEST18 0x7088 +#define CX2072X_DIGITAL_TEST19 0x708c +#define CX2072X_DIGITAL_TEST20 0x7090 + +/* not used in the current code, for future extensions (if any) */ +#define CX2072X_MAX_EQ_BAND 7 +#define CX2072X_MAX_EQ_COEFF 11 +#define CX2072X_MAX_DRC_REGS 9 +#define CX2072X_MIC_EQ_COEFF 10 +#define CX2072X_PLBK_EQ_BAND_NUM 7 +#define CX2072X_PLBK_EQ_COEF_LEN 11 +#define CX2072X_PLBK_DRC_PARM_LEN 9 +#define CX2072X_CLASSD_AMP_LEN 6 + +/* DAI interface type */ +#define CX2072X_DAI_HIFI 1 +#define CX2072X_DAI_DSP 2 +#define CX2072X_DAI_DSP_PWM 3 /* 4 ch, including mic and AEC */ + +enum cx2072x_reg_sample_size { + CX2072X_SAMPLE_SIZE_8_BITS = 0, + CX2072X_SAMPLE_SIZE_16_BITS = 1, + CX2072X_SAMPLE_SIZE_24_BITS = 2, + CX2072X_SAMPLE_SIZE_RESERVED = 3, +}; + +union cx2072x_reg_i2spcm_ctrl_reg1 { + struct { + u32 rx_data_one_line:1; + u32 rx_ws_pol:1; + u32 rx_ws_wid:7; + u32 rx_frm_len:5; + u32 rx_sa_size:2; + u32 tx_data_one_line:1; + u32 tx_ws_pol:1; + u32 tx_ws_wid:7; + u32 tx_frm_len:5; + u32 tx_sa_size:2; + } r; + u32 ulval; +}; + +union cx2072x_reg_i2spcm_ctrl_reg2 { + struct { + u32 tx_en_ch1:1; + u32 tx_en_ch2:1; + u32 tx_en_ch3:1; + u32 tx_en_ch4:1; + u32 tx_en_ch5:1; + u32 tx_en_ch6:1; + u32 tx_slot_1:5; + u32 tx_slot_2:5; + u32 tx_slot_3:5; + u32 tx_slot_4:5; + u32 res:1; + u32 tx_data_neg_bclk:1; + u32 tx_master:1; + u32 tx_tri_n:1; + u32 tx_endian_sel:1; + u32 tx_dstart_dly:1; + } r; + u32 ulval; +}; + +union cx2072x_reg_i2spcm_ctrl_reg3 { + struct { + u32 rx_en_ch1:1; + u32 rx_en_ch2:1; + u32 rx_en_ch3:1; + u32 rx_en_ch4:1; + u32 rx_en_ch5:1; + u32 rx_en_ch6:1; + u32 rx_slot_1:5; + u32 rx_slot_2:5; + u32 rx_slot_3:5; + u32 rx_slot_4:5; + u32 res:1; + u32 rx_data_neg_bclk:1; + u32 rx_master:1; + u32 rx_tri_n:1; + u32 rx_endian_sel:1; + u32 rx_dstart_dly:1; + } r; + u32 ulval; +}; + +union cx2072x_reg_i2spcm_ctrl_reg4 { + struct { + u32 rx_mute:1; + u32 tx_mute:1; + u32 reserved:1; + u32 dac_34_independent:1; + u32 dac_bclk_lrck_share:1; + u32 bclk_lrck_share_en:1; + u32 reserved2:2; + u32 rx_last_dac_ch_en:1; + u32 rx_last_dac_ch:3; + u32 tx_last_adc_ch_en:1; + u32 tx_last_adc_ch:3; + u32 rx_slot_5:5; + u32 rx_slot_6:5; + u32 reserved3:6; + } r; + u32 ulval; +}; + +union cx2072x_reg_i2spcm_ctrl_reg5 { + struct { + u32 tx_slot_5:5; + u32 reserved:3; + u32 tx_slot_6:5; + u32 reserved2:3; + u32 reserved3:8; + u32 i2s_pcm_clk_div:7; + u32 i2s_pcm_clk_div_chan_en:1; + } r; + u32 ulval; +}; + +union cx2072x_reg_i2spcm_ctrl_reg6 { + struct { + u32 reserved:5; + u32 rx_pause_cycles:3; + u32 rx_pause_start_pos:8; + u32 reserved2:5; + u32 tx_pause_cycles:3; + u32 tx_pause_start_pos:8; + } r; + u32 ulval; +}; + +union cx2072x_reg_digital_bios_test2 { + struct { + u32 pull_down_eapd:2; + u32 input_en_eapd_pad:1; + u32 push_pull_mode:1; + u32 eapd_pad_output_driver:2; + u32 pll_source:1; + u32 i2s_bclk_en:1; + u32 i2s_bclk_invert:1; + u32 pll_ref_clock:1; + u32 class_d_shield_clk:1; + u32 audio_pll_bypass_mode:1; + u32 reserved:4; + } r; + u32 ulval; +}; + +#endif /* __CX2072X_H__ */ diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c new file mode 100644 index 0000000000..1e232d0180 --- /dev/null +++ b/sound/soc/codecs/da7210.c @@ -0,0 +1,1364 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// DA7210 ALSA Soc codec driver +// +// Copyright (c) 2009 Dialog Semiconductor +// Written by David Chen +// +// Copyright (C) 2009 Renesas Solutions Corp. +// Cleanups by Kuninori Morimoto +// +// Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DA7210 register space */ +#define DA7210_PAGE_CONTROL 0x00 +#define DA7210_CONTROL 0x01 +#define DA7210_STATUS 0x02 +#define DA7210_STARTUP1 0x03 +#define DA7210_STARTUP2 0x04 +#define DA7210_STARTUP3 0x05 +#define DA7210_MIC_L 0x07 +#define DA7210_MIC_R 0x08 +#define DA7210_AUX1_L 0x09 +#define DA7210_AUX1_R 0x0A +#define DA7210_AUX2 0x0B +#define DA7210_IN_GAIN 0x0C +#define DA7210_INMIX_L 0x0D +#define DA7210_INMIX_R 0x0E +#define DA7210_ADC_HPF 0x0F +#define DA7210_ADC 0x10 +#define DA7210_ADC_EQ1_2 0X11 +#define DA7210_ADC_EQ3_4 0x12 +#define DA7210_ADC_EQ5 0x13 +#define DA7210_DAC_HPF 0x14 +#define DA7210_DAC_L 0x15 +#define DA7210_DAC_R 0x16 +#define DA7210_DAC_SEL 0x17 +#define DA7210_SOFTMUTE 0x18 +#define DA7210_DAC_EQ1_2 0x19 +#define DA7210_DAC_EQ3_4 0x1A +#define DA7210_DAC_EQ5 0x1B +#define DA7210_OUTMIX_L 0x1C +#define DA7210_OUTMIX_R 0x1D +#define DA7210_OUT1_L 0x1E +#define DA7210_OUT1_R 0x1F +#define DA7210_OUT2 0x20 +#define DA7210_HP_L_VOL 0x21 +#define DA7210_HP_R_VOL 0x22 +#define DA7210_HP_CFG 0x23 +#define DA7210_ZERO_CROSS 0x24 +#define DA7210_DAI_SRC_SEL 0x25 +#define DA7210_DAI_CFG1 0x26 +#define DA7210_DAI_CFG3 0x28 +#define DA7210_PLL_DIV1 0x29 +#define DA7210_PLL_DIV2 0x2A +#define DA7210_PLL_DIV3 0x2B +#define DA7210_PLL 0x2C +#define DA7210_ALC_MAX 0x83 +#define DA7210_ALC_MIN 0x84 +#define DA7210_ALC_NOIS 0x85 +#define DA7210_ALC_ATT 0x86 +#define DA7210_ALC_REL 0x87 +#define DA7210_ALC_DEL 0x88 +#define DA7210_A_HID_UNLOCK 0x8A +#define DA7210_A_TEST_UNLOCK 0x8B +#define DA7210_A_PLL1 0x90 +#define DA7210_A_CP_MODE 0xA7 + +/* STARTUP1 bit fields */ +#define DA7210_SC_MST_EN (1 << 0) + +/* MIC_L bit fields */ +#define DA7210_MICBIAS_EN (1 << 6) +#define DA7210_MIC_L_EN (1 << 7) + +/* MIC_R bit fields */ +#define DA7210_MIC_R_EN (1 << 7) + +/* INMIX_L bit fields */ +#define DA7210_IN_L_EN (1 << 7) + +/* INMIX_R bit fields */ +#define DA7210_IN_R_EN (1 << 7) + +/* ADC bit fields */ +#define DA7210_ADC_ALC_EN (1 << 0) +#define DA7210_ADC_L_EN (1 << 3) +#define DA7210_ADC_R_EN (1 << 7) + +/* DAC/ADC HPF fields */ +#define DA7210_VOICE_F0_MASK (0x7 << 4) +#define DA7210_VOICE_F0_25 (1 << 4) +#define DA7210_VOICE_EN (1 << 7) + +/* DAC_SEL bit fields */ +#define DA7210_DAC_L_SRC_DAI_L (4 << 0) +#define DA7210_DAC_L_EN (1 << 3) +#define DA7210_DAC_R_SRC_DAI_R (5 << 4) +#define DA7210_DAC_R_EN (1 << 7) + +/* OUTMIX_L bit fields */ +#define DA7210_OUT_L_EN (1 << 7) + +/* OUTMIX_R bit fields */ +#define DA7210_OUT_R_EN (1 << 7) + +/* HP_CFG bit fields */ +#define DA7210_HP_2CAP_MODE (1 << 1) +#define DA7210_HP_SENSE_EN (1 << 2) +#define DA7210_HP_L_EN (1 << 3) +#define DA7210_HP_MODE (1 << 6) +#define DA7210_HP_R_EN (1 << 7) + +/* DAI_SRC_SEL bit fields */ +#define DA7210_DAI_OUT_L_SRC (6 << 0) +#define DA7210_DAI_OUT_R_SRC (7 << 4) + +/* DAI_CFG1 bit fields */ +#define DA7210_DAI_WORD_S16_LE (0 << 0) +#define DA7210_DAI_WORD_S20_3LE (1 << 0) +#define DA7210_DAI_WORD_S24_LE (2 << 0) +#define DA7210_DAI_WORD_S32_LE (3 << 0) +#define DA7210_DAI_FLEN_64BIT (1 << 2) +#define DA7210_DAI_MODE_SLAVE (0 << 7) +#define DA7210_DAI_MODE_MASTER (1 << 7) + +/* DAI_CFG3 bit fields */ +#define DA7210_DAI_FORMAT_I2SMODE (0 << 0) +#define DA7210_DAI_FORMAT_LEFT_J (1 << 0) +#define DA7210_DAI_FORMAT_RIGHT_J (2 << 0) +#define DA7210_DAI_OE (1 << 3) +#define DA7210_DAI_EN (1 << 7) + +/*PLL_DIV3 bit fields */ +#define DA7210_PLL_DIV_L_MASK (0xF << 0) +#define DA7210_MCLK_RANGE_10_20_MHZ (1 << 4) +#define DA7210_PLL_BYP (1 << 6) + +/* PLL bit fields */ +#define DA7210_PLL_FS_MASK (0xF << 0) +#define DA7210_PLL_FS_8000 (0x1 << 0) +#define DA7210_PLL_FS_11025 (0x2 << 0) +#define DA7210_PLL_FS_12000 (0x3 << 0) +#define DA7210_PLL_FS_16000 (0x5 << 0) +#define DA7210_PLL_FS_22050 (0x6 << 0) +#define DA7210_PLL_FS_24000 (0x7 << 0) +#define DA7210_PLL_FS_32000 (0x9 << 0) +#define DA7210_PLL_FS_44100 (0xA << 0) +#define DA7210_PLL_FS_48000 (0xB << 0) +#define DA7210_PLL_FS_88200 (0xE << 0) +#define DA7210_PLL_FS_96000 (0xF << 0) +#define DA7210_MCLK_DET_EN (0x1 << 5) +#define DA7210_MCLK_SRM_EN (0x1 << 6) +#define DA7210_PLL_EN (0x1 << 7) + +/* SOFTMUTE bit fields */ +#define DA7210_RAMP_EN (1 << 6) + +/* CONTROL bit fields */ +#define DA7210_REG_EN (1 << 0) +#define DA7210_BIAS_EN (1 << 2) +#define DA7210_NOISE_SUP_EN (1 << 3) + +/* IN_GAIN bit fields */ +#define DA7210_INPGA_L_VOL (0x0F << 0) +#define DA7210_INPGA_R_VOL (0xF0 << 0) + +/* ZERO_CROSS bit fields */ +#define DA7210_AUX1_L_ZC (1 << 0) +#define DA7210_AUX1_R_ZC (1 << 1) +#define DA7210_HP_L_ZC (1 << 6) +#define DA7210_HP_R_ZC (1 << 7) + +/* AUX1_L bit fields */ +#define DA7210_AUX1_L_VOL (0x3F << 0) +#define DA7210_AUX1_L_EN (1 << 7) + +/* AUX1_R bit fields */ +#define DA7210_AUX1_R_VOL (0x3F << 0) +#define DA7210_AUX1_R_EN (1 << 7) + +/* AUX2 bit fields */ +#define DA7210_AUX2_EN (1 << 3) + +/* Minimum INPGA and AUX1 volume to enable noise suppression */ +#define DA7210_INPGA_MIN_VOL_NS 0x0A /* 10.5dB */ +#define DA7210_AUX1_MIN_VOL_NS 0x35 /* 6dB */ + +/* OUT1_L bit fields */ +#define DA7210_OUT1_L_EN (1 << 7) + +/* OUT1_R bit fields */ +#define DA7210_OUT1_R_EN (1 << 7) + +/* OUT2 bit fields */ +#define DA7210_OUT2_OUTMIX_R (1 << 5) +#define DA7210_OUT2_OUTMIX_L (1 << 6) +#define DA7210_OUT2_EN (1 << 7) + +struct pll_div { + int fref; + int fout; + u8 div1; + u8 div2; + u8 div3; + u8 mode; /* 0 = slave, 1 = master */ +}; + +/* PLL dividers table */ +static const struct pll_div da7210_pll_div[] = { + /* for MASTER mode, fs = 44.1Khz */ + { 12000000, 2822400, 0xE8, 0x6C, 0x2, 1}, /* MCLK=12Mhz */ + { 13000000, 2822400, 0xDF, 0x28, 0xC, 1}, /* MCLK=13Mhz */ + { 13500000, 2822400, 0xDB, 0x0A, 0xD, 1}, /* MCLK=13.5Mhz */ + { 14400000, 2822400, 0xD4, 0x5A, 0x2, 1}, /* MCLK=14.4Mhz */ + { 19200000, 2822400, 0xBB, 0x43, 0x9, 1}, /* MCLK=19.2Mhz */ + { 19680000, 2822400, 0xB9, 0x6D, 0xA, 1}, /* MCLK=19.68Mhz */ + { 19800000, 2822400, 0xB8, 0xFB, 0xB, 1}, /* MCLK=19.8Mhz */ + /* for MASTER mode, fs = 48Khz */ + { 12000000, 3072000, 0xF3, 0x12, 0x7, 1}, /* MCLK=12Mhz */ + { 13000000, 3072000, 0xE8, 0xFD, 0x5, 1}, /* MCLK=13Mhz */ + { 13500000, 3072000, 0xE4, 0x82, 0x3, 1}, /* MCLK=13.5Mhz */ + { 14400000, 3072000, 0xDD, 0x3A, 0x0, 1}, /* MCLK=14.4Mhz */ + { 19200000, 3072000, 0xC1, 0xEB, 0x8, 1}, /* MCLK=19.2Mhz */ + { 19680000, 3072000, 0xBF, 0xEC, 0x0, 1}, /* MCLK=19.68Mhz */ + { 19800000, 3072000, 0xBF, 0x70, 0x0, 1}, /* MCLK=19.8Mhz */ + /* for SLAVE mode with SRM */ + { 12000000, 2822400, 0xED, 0xBF, 0x5, 0}, /* MCLK=12Mhz */ + { 13000000, 2822400, 0xE4, 0x13, 0x0, 0}, /* MCLK=13Mhz */ + { 13500000, 2822400, 0xDF, 0xC6, 0x8, 0}, /* MCLK=13.5Mhz */ + { 14400000, 2822400, 0xD8, 0xCA, 0x1, 0}, /* MCLK=14.4Mhz */ + { 19200000, 2822400, 0xBE, 0x97, 0x9, 0}, /* MCLK=19.2Mhz */ + { 19680000, 2822400, 0xBC, 0xAC, 0xD, 0}, /* MCLK=19.68Mhz */ + { 19800000, 2822400, 0xBC, 0x35, 0xE, 0}, /* MCLK=19.8Mhz */ +}; + +enum clk_src { + DA7210_CLKSRC_MCLK +}; + +#define DA7210_VERSION "0.0.1" + +/* + * Playback Volume + * + * max : 0x3F (+15.0 dB) + * (1.5 dB step) + * min : 0x11 (-54.0 dB) + * mute : 0x10 + * reserved : 0x00 - 0x0F + * + * Reserved area are considered as "mute". + */ +static const DECLARE_TLV_DB_RANGE(hp_out_tlv, + 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -54 dB to +15 dB */ + 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0) +); + +static const DECLARE_TLV_DB_RANGE(lineout_vol_tlv, + 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -54dB to 15dB */ + 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0) +); + +static const DECLARE_TLV_DB_RANGE(mono_vol_tlv, + 0x0, 0x2, TLV_DB_SCALE_ITEM(-1800, 0, 1), + /* -18dB to 6dB */ + 0x3, 0x7, TLV_DB_SCALE_ITEM(-1800, 600, 0) +); + +static const DECLARE_TLV_DB_RANGE(aux1_vol_tlv, + 0x0, 0x10, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -48dB to 21dB */ + 0x11, 0x3f, TLV_DB_SCALE_ITEM(-4800, 150, 0) +); + +static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_eq_master_gain_tlv, -1800, 600, 1); +static const DECLARE_TLV_DB_SCALE(dac_gain_tlv, -7725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(aux2_vol_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(inpga_gain_tlv, -450, 150, 0); + +/* ADC and DAC high pass filter f0 value */ +static const char * const da7210_hpf_cutoff_txt[] = { + "Fs/8192*pi", "Fs/4096*pi", "Fs/2048*pi", "Fs/1024*pi" +}; + +static SOC_ENUM_SINGLE_DECL(da7210_dac_hpf_cutoff, + DA7210_DAC_HPF, 0, da7210_hpf_cutoff_txt); + +static SOC_ENUM_SINGLE_DECL(da7210_adc_hpf_cutoff, + DA7210_ADC_HPF, 0, da7210_hpf_cutoff_txt); + +/* ADC and DAC voice (8kHz) high pass cutoff value */ +static const char * const da7210_vf_cutoff_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static SOC_ENUM_SINGLE_DECL(da7210_dac_vf_cutoff, + DA7210_DAC_HPF, 4, da7210_vf_cutoff_txt); + +static SOC_ENUM_SINGLE_DECL(da7210_adc_vf_cutoff, + DA7210_ADC_HPF, 4, da7210_vf_cutoff_txt); + +static const char *da7210_hp_mode_txt[] = { + "Class H", "Class G" +}; + +static SOC_ENUM_SINGLE_DECL(da7210_hp_mode_sel, + DA7210_HP_CFG, 0, da7210_hp_mode_txt); + +/* ALC can be enabled only if noise suppression is disabled */ +static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + + if (ucontrol->value.integer.value[0]) { + /* Check if noise suppression is enabled */ + if (snd_soc_component_read(component, DA7210_CONTROL) & DA7210_NOISE_SUP_EN) { + dev_dbg(component->dev, + "Disable noise suppression to enable ALC\n"); + return -EINVAL; + } + } + /* If all conditions are met or we are actually disabling ALC */ + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +/* Noise suppression can be enabled only if following conditions are met + * ALC disabled + * ZC enabled for HP and AUX1 PGA + * INPGA_L_VOL and INPGA_R_VOL >= 10.5 dB + * AUX1_L_VOL and AUX1_R_VOL >= 6 dB + */ +static int da7210_put_noise_sup_sw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + u8 val; + + if (ucontrol->value.integer.value[0]) { + /* Check if ALC is enabled */ + if (snd_soc_component_read(component, DA7210_ADC) & DA7210_ADC_ALC_EN) + goto err; + + /* Check ZC for HP and AUX1 PGA */ + if ((snd_soc_component_read(component, DA7210_ZERO_CROSS) & + (DA7210_AUX1_L_ZC | DA7210_AUX1_R_ZC | DA7210_HP_L_ZC | + DA7210_HP_R_ZC)) != (DA7210_AUX1_L_ZC | + DA7210_AUX1_R_ZC | DA7210_HP_L_ZC | DA7210_HP_R_ZC)) + goto err; + + /* Check INPGA_L_VOL and INPGA_R_VOL */ + val = snd_soc_component_read(component, DA7210_IN_GAIN); + if (((val & DA7210_INPGA_L_VOL) < DA7210_INPGA_MIN_VOL_NS) || + (((val & DA7210_INPGA_R_VOL) >> 4) < + DA7210_INPGA_MIN_VOL_NS)) + goto err; + + /* Check AUX1_L_VOL and AUX1_R_VOL */ + if (((snd_soc_component_read(component, DA7210_AUX1_L) & DA7210_AUX1_L_VOL) < + DA7210_AUX1_MIN_VOL_NS) || + ((snd_soc_component_read(component, DA7210_AUX1_R) & DA7210_AUX1_R_VOL) < + DA7210_AUX1_MIN_VOL_NS)) + goto err; + } + /* If all conditions are met or we are actually disabling Noise sup */ + return snd_soc_put_volsw(kcontrol, ucontrol); + +err: + return -EINVAL; +} + +static const struct snd_kcontrol_new da7210_snd_controls[] = { + + SOC_DOUBLE_R_TLV("HeadPhone Playback Volume", + DA7210_HP_L_VOL, DA7210_HP_R_VOL, + 0, 0x3F, 0, hp_out_tlv), + SOC_DOUBLE_R_TLV("Digital Playback Volume", + DA7210_DAC_L, DA7210_DAC_R, + 0, 0x77, 1, dac_gain_tlv), + SOC_DOUBLE_R_TLV("Lineout Playback Volume", + DA7210_OUT1_L, DA7210_OUT1_R, + 0, 0x3f, 0, lineout_vol_tlv), + SOC_SINGLE_TLV("Mono Playback Volume", DA7210_OUT2, 0, 0x7, 0, + mono_vol_tlv), + + SOC_DOUBLE_R_TLV("Mic Capture Volume", + DA7210_MIC_L, DA7210_MIC_R, + 0, 0x5, 0, mic_vol_tlv), + SOC_DOUBLE_R_TLV("Aux1 Capture Volume", + DA7210_AUX1_L, DA7210_AUX1_R, + 0, 0x3f, 0, aux1_vol_tlv), + SOC_SINGLE_TLV("Aux2 Capture Volume", DA7210_AUX2, 0, 0x3, 0, + aux2_vol_tlv), + SOC_DOUBLE_TLV("In PGA Capture Volume", DA7210_IN_GAIN, 0, 4, 0xF, 0, + inpga_gain_tlv), + + /* DAC Equalizer controls */ + SOC_SINGLE("DAC EQ Switch", DA7210_DAC_EQ5, 7, 1, 0), + SOC_SINGLE_TLV("DAC EQ1 Volume", DA7210_DAC_EQ1_2, 0, 0xf, 1, + eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ2 Volume", DA7210_DAC_EQ1_2, 4, 0xf, 1, + eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ3 Volume", DA7210_DAC_EQ3_4, 0, 0xf, 1, + eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ4 Volume", DA7210_DAC_EQ3_4, 4, 0xf, 1, + eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ5 Volume", DA7210_DAC_EQ5, 0, 0xf, 1, + eq_gain_tlv), + + /* ADC Equalizer controls */ + SOC_SINGLE("ADC EQ Switch", DA7210_ADC_EQ5, 7, 1, 0), + SOC_SINGLE_TLV("ADC EQ Master Volume", DA7210_ADC_EQ5, 4, 0x3, + 1, adc_eq_master_gain_tlv), + SOC_SINGLE_TLV("ADC EQ1 Volume", DA7210_ADC_EQ1_2, 0, 0xf, 1, + eq_gain_tlv), + SOC_SINGLE_TLV("ADC EQ2 Volume", DA7210_ADC_EQ1_2, 4, 0xf, 1, + eq_gain_tlv), + SOC_SINGLE_TLV("ADC EQ3 Volume", DA7210_ADC_EQ3_4, 0, 0xf, 1, + eq_gain_tlv), + SOC_SINGLE_TLV("ADC EQ4 Volume", DA7210_ADC_EQ3_4, 4, 0xf, 1, + eq_gain_tlv), + SOC_SINGLE_TLV("ADC EQ5 Volume", DA7210_ADC_EQ5, 0, 0xf, 1, + eq_gain_tlv), + + SOC_SINGLE("DAC HPF Switch", DA7210_DAC_HPF, 3, 1, 0), + SOC_ENUM("DAC HPF Cutoff", da7210_dac_hpf_cutoff), + SOC_SINGLE("DAC Voice Mode Switch", DA7210_DAC_HPF, 7, 1, 0), + SOC_ENUM("DAC Voice Cutoff", da7210_dac_vf_cutoff), + + SOC_SINGLE("ADC HPF Switch", DA7210_ADC_HPF, 3, 1, 0), + SOC_ENUM("ADC HPF Cutoff", da7210_adc_hpf_cutoff), + SOC_SINGLE("ADC Voice Mode Switch", DA7210_ADC_HPF, 7, 1, 0), + SOC_ENUM("ADC Voice Cutoff", da7210_adc_vf_cutoff), + + /* Mute controls */ + SOC_DOUBLE_R("Mic Capture Switch", DA7210_MIC_L, DA7210_MIC_R, 3, 1, 0), + SOC_SINGLE("Aux2 Capture Switch", DA7210_AUX2, 2, 1, 0), + SOC_DOUBLE("ADC Capture Switch", DA7210_ADC, 2, 6, 1, 0), + SOC_SINGLE("Digital Soft Mute Switch", DA7210_SOFTMUTE, 7, 1, 0), + SOC_SINGLE("Digital Soft Mute Rate", DA7210_SOFTMUTE, 0, 0x7, 0), + + /* Zero cross controls */ + SOC_DOUBLE("Aux1 ZC Switch", DA7210_ZERO_CROSS, 0, 1, 1, 0), + SOC_DOUBLE("In PGA ZC Switch", DA7210_ZERO_CROSS, 2, 3, 1, 0), + SOC_DOUBLE("Lineout ZC Switch", DA7210_ZERO_CROSS, 4, 5, 1, 0), + SOC_DOUBLE("Headphone ZC Switch", DA7210_ZERO_CROSS, 6, 7, 1, 0), + + SOC_ENUM("Headphone Class", da7210_hp_mode_sel), + + /* ALC controls */ + SOC_SINGLE_EXT("ALC Enable Switch", DA7210_ADC, 0, 1, 0, + snd_soc_get_volsw, da7210_put_alc_sw), + SOC_SINGLE("ALC Capture Max Volume", DA7210_ALC_MAX, 0, 0x3F, 0), + SOC_SINGLE("ALC Capture Min Volume", DA7210_ALC_MIN, 0, 0x3F, 0), + SOC_SINGLE("ALC Capture Noise Volume", DA7210_ALC_NOIS, 0, 0x3F, 0), + SOC_SINGLE("ALC Capture Attack Rate", DA7210_ALC_ATT, 0, 0xFF, 0), + SOC_SINGLE("ALC Capture Release Rate", DA7210_ALC_REL, 0, 0xFF, 0), + SOC_SINGLE("ALC Capture Release Delay", DA7210_ALC_DEL, 0, 0xFF, 0), + + SOC_SINGLE_EXT("Noise Suppression Enable Switch", DA7210_CONTROL, 3, 1, + 0, snd_soc_get_volsw, da7210_put_noise_sup_sw), +}; + +/* + * DAPM Controls + * + * Current DAPM implementation covers almost all codec components e.g. IOs, + * mixers, PGAs,ADC and DAC. + */ +/* In Mixer Left */ +static const struct snd_kcontrol_new da7210_dapm_inmixl_controls[] = { + SOC_DAPM_SINGLE("Mic Left Switch", DA7210_INMIX_L, 0, 1, 0), + SOC_DAPM_SINGLE("Mic Right Switch", DA7210_INMIX_L, 1, 1, 0), + SOC_DAPM_SINGLE("Aux1 Left Switch", DA7210_INMIX_L, 2, 1, 0), + SOC_DAPM_SINGLE("Aux2 Switch", DA7210_INMIX_L, 3, 1, 0), + SOC_DAPM_SINGLE("Outmix Left Switch", DA7210_INMIX_L, 4, 1, 0), +}; + +/* In Mixer Right */ +static const struct snd_kcontrol_new da7210_dapm_inmixr_controls[] = { + SOC_DAPM_SINGLE("Mic Right Switch", DA7210_INMIX_R, 0, 1, 0), + SOC_DAPM_SINGLE("Mic Left Switch", DA7210_INMIX_R, 1, 1, 0), + SOC_DAPM_SINGLE("Aux1 Right Switch", DA7210_INMIX_R, 2, 1, 0), + SOC_DAPM_SINGLE("Aux2 Switch", DA7210_INMIX_R, 3, 1, 0), + SOC_DAPM_SINGLE("Outmix Right Switch", DA7210_INMIX_R, 4, 1, 0), +}; + +/* Out Mixer Left */ +static const struct snd_kcontrol_new da7210_dapm_outmixl_controls[] = { + SOC_DAPM_SINGLE("Aux1 Left Switch", DA7210_OUTMIX_L, 0, 1, 0), + SOC_DAPM_SINGLE("Aux2 Switch", DA7210_OUTMIX_L, 1, 1, 0), + SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUTMIX_L, 2, 1, 0), + SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUTMIX_L, 3, 1, 0), + SOC_DAPM_SINGLE("DAC Left Switch", DA7210_OUTMIX_L, 4, 1, 0), +}; + +/* Out Mixer Right */ +static const struct snd_kcontrol_new da7210_dapm_outmixr_controls[] = { + SOC_DAPM_SINGLE("Aux1 Right Switch", DA7210_OUTMIX_R, 0, 1, 0), + SOC_DAPM_SINGLE("Aux2 Switch", DA7210_OUTMIX_R, 1, 1, 0), + SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUTMIX_R, 2, 1, 0), + SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUTMIX_R, 3, 1, 0), + SOC_DAPM_SINGLE("DAC Right Switch", DA7210_OUTMIX_R, 4, 1, 0), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new da7210_dapm_monomix_controls[] = { + SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUT2, 3, 1, 0), + SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUT2, 4, 1, 0), + SOC_DAPM_SINGLE("Outmix Right Switch", DA7210_OUT2, 5, 1, 0), + SOC_DAPM_SINGLE("Outmix Left Switch", DA7210_OUT2, 6, 1, 0), +}; + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget da7210_dapm_widgets[] = { + /* Input Side */ + /* Input Lines */ + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("MICR"), + SND_SOC_DAPM_INPUT("AUX1L"), + SND_SOC_DAPM_INPUT("AUX1R"), + SND_SOC_DAPM_INPUT("AUX2"), + + /* Input PGAs */ + SND_SOC_DAPM_PGA("Mic Left", DA7210_STARTUP3, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("Mic Right", DA7210_STARTUP3, 1, 1, NULL, 0), + SND_SOC_DAPM_PGA("Aux1 Left", DA7210_STARTUP3, 2, 1, NULL, 0), + SND_SOC_DAPM_PGA("Aux1 Right", DA7210_STARTUP3, 3, 1, NULL, 0), + SND_SOC_DAPM_PGA("Aux2 Mono", DA7210_STARTUP3, 4, 1, NULL, 0), + + SND_SOC_DAPM_PGA("INPGA Left", DA7210_INMIX_L, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("INPGA Right", DA7210_INMIX_R, 7, 0, NULL, 0), + + /* MICBIAS */ + SND_SOC_DAPM_SUPPLY("Mic Bias", DA7210_MIC_L, 6, 0, NULL, 0), + + /* Input Mixers */ + SND_SOC_DAPM_MIXER("In Mixer Left", SND_SOC_NOPM, 0, 0, + &da7210_dapm_inmixl_controls[0], + ARRAY_SIZE(da7210_dapm_inmixl_controls)), + + SND_SOC_DAPM_MIXER("In Mixer Right", SND_SOC_NOPM, 0, 0, + &da7210_dapm_inmixr_controls[0], + ARRAY_SIZE(da7210_dapm_inmixr_controls)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC Left", "Capture", DA7210_STARTUP3, 5, 1), + SND_SOC_DAPM_ADC("ADC Right", "Capture", DA7210_STARTUP3, 6, 1), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("DAC Left", "Playback", DA7210_STARTUP2, 5, 1), + SND_SOC_DAPM_DAC("DAC Right", "Playback", DA7210_STARTUP2, 6, 1), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Out Mixer Left", SND_SOC_NOPM, 0, 0, + &da7210_dapm_outmixl_controls[0], + ARRAY_SIZE(da7210_dapm_outmixl_controls)), + + SND_SOC_DAPM_MIXER("Out Mixer Right", SND_SOC_NOPM, 0, 0, + &da7210_dapm_outmixr_controls[0], + ARRAY_SIZE(da7210_dapm_outmixr_controls)), + + SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, + &da7210_dapm_monomix_controls[0], + ARRAY_SIZE(da7210_dapm_monomix_controls)), + + /* Output PGAs */ + SND_SOC_DAPM_PGA("OUTPGA Left Enable", DA7210_OUTMIX_L, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("OUTPGA Right Enable", DA7210_OUTMIX_R, 7, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Out1 Left", DA7210_STARTUP2, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("Out1 Right", DA7210_STARTUP2, 1, 1, NULL, 0), + SND_SOC_DAPM_PGA("Out2 Mono", DA7210_STARTUP2, 2, 1, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Left", DA7210_STARTUP2, 3, 1, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Right", DA7210_STARTUP2, 4, 1, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("OUT1L"), + SND_SOC_DAPM_OUTPUT("OUT1R"), + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("OUT2"), +}; + +/* DAPM audio route definition */ +static const struct snd_soc_dapm_route da7210_audio_map[] = { + /* Dest Connecting Widget source */ + /* Input path */ + {"Mic Left", NULL, "MICL"}, + {"Mic Right", NULL, "MICR"}, + {"Aux1 Left", NULL, "AUX1L"}, + {"Aux1 Right", NULL, "AUX1R"}, + {"Aux2 Mono", NULL, "AUX2"}, + + {"In Mixer Left", "Mic Left Switch", "Mic Left"}, + {"In Mixer Left", "Mic Right Switch", "Mic Right"}, + {"In Mixer Left", "Aux1 Left Switch", "Aux1 Left"}, + {"In Mixer Left", "Aux2 Switch", "Aux2 Mono"}, + {"In Mixer Left", "Outmix Left Switch", "Out Mixer Left"}, + + {"In Mixer Right", "Mic Right Switch", "Mic Right"}, + {"In Mixer Right", "Mic Left Switch", "Mic Left"}, + {"In Mixer Right", "Aux1 Right Switch", "Aux1 Right"}, + {"In Mixer Right", "Aux2 Switch", "Aux2 Mono"}, + {"In Mixer Right", "Outmix Right Switch", "Out Mixer Right"}, + + {"INPGA Left", NULL, "In Mixer Left"}, + {"ADC Left", NULL, "INPGA Left"}, + + {"INPGA Right", NULL, "In Mixer Right"}, + {"ADC Right", NULL, "INPGA Right"}, + + /* Output path */ + {"Out Mixer Left", "Aux1 Left Switch", "Aux1 Left"}, + {"Out Mixer Left", "Aux2 Switch", "Aux2 Mono"}, + {"Out Mixer Left", "INPGA Left Switch", "INPGA Left"}, + {"Out Mixer Left", "INPGA Right Switch", "INPGA Right"}, + {"Out Mixer Left", "DAC Left Switch", "DAC Left"}, + + {"Out Mixer Right", "Aux1 Right Switch", "Aux1 Right"}, + {"Out Mixer Right", "Aux2 Switch", "Aux2 Mono"}, + {"Out Mixer Right", "INPGA Right Switch", "INPGA Right"}, + {"Out Mixer Right", "INPGA Left Switch", "INPGA Left"}, + {"Out Mixer Right", "DAC Right Switch", "DAC Right"}, + + {"Mono Mixer", "INPGA Right Switch", "INPGA Right"}, + {"Mono Mixer", "INPGA Left Switch", "INPGA Left"}, + {"Mono Mixer", "Outmix Right Switch", "Out Mixer Right"}, + {"Mono Mixer", "Outmix Left Switch", "Out Mixer Left"}, + + {"OUTPGA Left Enable", NULL, "Out Mixer Left"}, + {"OUTPGA Right Enable", NULL, "Out Mixer Right"}, + + {"Out1 Left", NULL, "OUTPGA Left Enable"}, + {"OUT1L", NULL, "Out1 Left"}, + + {"Out1 Right", NULL, "OUTPGA Right Enable"}, + {"OUT1R", NULL, "Out1 Right"}, + + {"Headphone Left", NULL, "OUTPGA Left Enable"}, + {"HPL", NULL, "Headphone Left"}, + + {"Headphone Right", NULL, "OUTPGA Right Enable"}, + {"HPR", NULL, "Headphone Right"}, + + {"Out2 Mono", NULL, "Mono Mixer"}, + {"OUT2", NULL, "Out2 Mono"}, +}; + +/* Codec private data */ +struct da7210_priv { + struct regmap *regmap; + unsigned int mclk_rate; + int master; +}; + +static const struct reg_default da7210_reg_defaults[] = { + { 0x00, 0x00 }, + { 0x01, 0x11 }, + { 0x03, 0x00 }, + { 0x04, 0x00 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x00 }, + { 0x0a, 0x00 }, + { 0x0b, 0x00 }, + { 0x0c, 0x00 }, + { 0x0d, 0x00 }, + { 0x0e, 0x00 }, + { 0x0f, 0x08 }, + { 0x10, 0x00 }, + { 0x11, 0x00 }, + { 0x12, 0x00 }, + { 0x13, 0x00 }, + { 0x14, 0x08 }, + { 0x15, 0x10 }, + { 0x16, 0x10 }, + { 0x17, 0x54 }, + { 0x18, 0x40 }, + { 0x19, 0x00 }, + { 0x1a, 0x00 }, + { 0x1b, 0x00 }, + { 0x1c, 0x00 }, + { 0x1d, 0x00 }, + { 0x1e, 0x00 }, + { 0x1f, 0x00 }, + { 0x20, 0x00 }, + { 0x21, 0x00 }, + { 0x22, 0x00 }, + { 0x23, 0x02 }, + { 0x24, 0x00 }, + { 0x25, 0x76 }, + { 0x26, 0x00 }, + { 0x27, 0x00 }, + { 0x28, 0x04 }, + { 0x29, 0x00 }, + { 0x2a, 0x00 }, + { 0x2b, 0x30 }, + { 0x2c, 0x2A }, + { 0x83, 0x00 }, + { 0x84, 0x00 }, + { 0x85, 0x00 }, + { 0x86, 0x00 }, + { 0x87, 0x00 }, + { 0x88, 0x00 }, +}; + +static bool da7210_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA7210_A_HID_UNLOCK: + case DA7210_A_TEST_UNLOCK: + case DA7210_A_PLL1: + case DA7210_A_CP_MODE: + return false; + default: + return true; + } +} + +static bool da7210_volatile_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case DA7210_STATUS: + return true; + default: + return false; + } +} + +/* + * Set PCM DAI word length. + */ +static int da7210_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component); + u32 dai_cfg1; + u32 fs, sysclk; + + /* set DAI source to Left and Right ADC */ + snd_soc_component_write(component, DA7210_DAI_SRC_SEL, + DA7210_DAI_OUT_R_SRC | DA7210_DAI_OUT_L_SRC); + + /* Enable DAI */ + snd_soc_component_write(component, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN); + + dai_cfg1 = 0xFC & snd_soc_component_read(component, DA7210_DAI_CFG1); + + switch (params_width(params)) { + case 16: + dai_cfg1 |= DA7210_DAI_WORD_S16_LE; + break; + case 20: + dai_cfg1 |= DA7210_DAI_WORD_S20_3LE; + break; + case 24: + dai_cfg1 |= DA7210_DAI_WORD_S24_LE; + break; + case 32: + dai_cfg1 |= DA7210_DAI_WORD_S32_LE; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, DA7210_DAI_CFG1, dai_cfg1); + + switch (params_rate(params)) { + case 8000: + fs = DA7210_PLL_FS_8000; + sysclk = 3072000; + break; + case 11025: + fs = DA7210_PLL_FS_11025; + sysclk = 2822400; + break; + case 12000: + fs = DA7210_PLL_FS_12000; + sysclk = 3072000; + break; + case 16000: + fs = DA7210_PLL_FS_16000; + sysclk = 3072000; + break; + case 22050: + fs = DA7210_PLL_FS_22050; + sysclk = 2822400; + break; + case 32000: + fs = DA7210_PLL_FS_32000; + sysclk = 3072000; + break; + case 44100: + fs = DA7210_PLL_FS_44100; + sysclk = 2822400; + break; + case 48000: + fs = DA7210_PLL_FS_48000; + sysclk = 3072000; + break; + case 88200: + fs = DA7210_PLL_FS_88200; + sysclk = 2822400; + break; + case 96000: + fs = DA7210_PLL_FS_96000; + sysclk = 3072000; + break; + default: + return -EINVAL; + } + + /* Disable active mode */ + snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, 0); + + snd_soc_component_update_bits(component, DA7210_PLL, DA7210_PLL_FS_MASK, fs); + + if (da7210->mclk_rate && (da7210->mclk_rate != sysclk)) { + /* PLL mode, disable PLL bypass */ + snd_soc_component_update_bits(component, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0); + + if (!da7210->master) { + /* PLL slave mode, also enable SRM */ + snd_soc_component_update_bits(component, DA7210_PLL, + (DA7210_MCLK_SRM_EN | + DA7210_MCLK_DET_EN), + (DA7210_MCLK_SRM_EN | + DA7210_MCLK_DET_EN)); + } + } else { + /* PLL bypass mode, enable PLL bypass and Auto Detection */ + snd_soc_component_update_bits(component, DA7210_PLL, DA7210_MCLK_DET_EN, + DA7210_MCLK_DET_EN); + snd_soc_component_update_bits(component, DA7210_PLL_DIV3, DA7210_PLL_BYP, + DA7210_PLL_BYP); + } + /* Enable active mode */ + snd_soc_component_update_bits(component, DA7210_STARTUP1, + DA7210_SC_MST_EN, DA7210_SC_MST_EN); + + return 0; +} + +/* + * Set DAI mode and Format + */ +static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component); + u32 dai_cfg1; + u32 dai_cfg3; + + dai_cfg1 = 0x7f & snd_soc_component_read(component, DA7210_DAI_CFG1); + dai_cfg3 = 0xfc & snd_soc_component_read(component, DA7210_DAI_CFG3); + + if ((snd_soc_component_read(component, DA7210_PLL) & DA7210_PLL_EN) && + (!(snd_soc_component_read(component, DA7210_PLL_DIV3) & DA7210_PLL_BYP))) + return -EINVAL; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da7210->master = 1; + dai_cfg1 |= DA7210_DAI_MODE_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + da7210->master = 0; + dai_cfg1 |= DA7210_DAI_MODE_SLAVE; + break; + default: + return -EINVAL; + } + + /* FIXME + * + * It support I2S only now + */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_cfg3 |= DA7210_DAI_FORMAT_I2SMODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_cfg3 |= DA7210_DAI_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dai_cfg3 |= DA7210_DAI_FORMAT_RIGHT_J; + break; + default: + return -EINVAL; + } + + /* FIXME + * + * It support 64bit data transmission only now + */ + dai_cfg1 |= DA7210_DAI_FLEN_64BIT; + + snd_soc_component_write(component, DA7210_DAI_CFG1, dai_cfg1); + snd_soc_component_write(component, DA7210_DAI_CFG3, dai_cfg3); + + return 0; +} + +static int da7210_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u8 mute_reg = snd_soc_component_read(component, DA7210_DAC_HPF) & 0xFB; + + if (mute) + snd_soc_component_write(component, DA7210_DAC_HPF, mute_reg | 0x4); + else + snd_soc_component_write(component, DA7210_DAC_HPF, mute_reg); + return 0; +} + +#define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case DA7210_CLKSRC_MCLK: + switch (freq) { + case 12000000: + case 13000000: + case 13500000: + case 14400000: + case 19200000: + case 19680000: + case 19800000: + da7210->mclk_rate = freq; + return 0; + default: + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } +} + +/** + * da7210_set_dai_pll :Configure the codec PLL + * @codec_dai: pointer to codec DAI + * @pll_id: da7210 has only one pll, so pll_id is always zero + * @source: clock source + * @fref: MCLK frequency, should be < 20MHz + * @fout: FsDM value, Refer page 44 & 45 of datasheet + * + * Note: Supported PLL input frequencies are 12MHz, 13MHz, 13.5MHz, 14.4MHz, + * 19.2MHz, 19.6MHz and 19.8MHz + * + * Return: Zero for success, negative error code for error + */ +static int da7210_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_component *component = codec_dai->component; + struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component); + + u8 pll_div1, pll_div2, pll_div3, cnt; + + /* In slave mode, there is only one set of divisors */ + if (!da7210->master) + fout = 2822400; + + /* Search pll div array for correct divisors */ + for (cnt = 0; cnt < ARRAY_SIZE(da7210_pll_div); cnt++) { + /* check fref, mode and fout */ + if ((fref == da7210_pll_div[cnt].fref) && + (da7210->master == da7210_pll_div[cnt].mode) && + (fout == da7210_pll_div[cnt].fout)) { + /* all match, pick up divisors */ + pll_div1 = da7210_pll_div[cnt].div1; + pll_div2 = da7210_pll_div[cnt].div2; + pll_div3 = da7210_pll_div[cnt].div3; + break; + } + } + if (cnt >= ARRAY_SIZE(da7210_pll_div)) + goto err; + + /* Disable active mode */ + snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, 0); + /* Write PLL dividers */ + snd_soc_component_write(component, DA7210_PLL_DIV1, pll_div1); + snd_soc_component_write(component, DA7210_PLL_DIV2, pll_div2); + snd_soc_component_update_bits(component, DA7210_PLL_DIV3, + DA7210_PLL_DIV_L_MASK, pll_div3); + + /* Enable PLL */ + snd_soc_component_update_bits(component, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN); + + /* Enable active mode */ + snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, + DA7210_SC_MST_EN); + return 0; +err: + dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n", fref); + return -EINVAL; +} + +/* DAI operations */ +static const struct snd_soc_dai_ops da7210_dai_ops = { + .hw_params = da7210_hw_params, + .set_fmt = da7210_set_dai_fmt, + .set_sysclk = da7210_set_dai_sysclk, + .set_pll = da7210_set_dai_pll, + .mute_stream = da7210_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver da7210_dai = { + .name = "da7210-hifi", + /* playback capabilities */ + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7210_FORMATS, + }, + /* capture capabilities */ + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7210_FORMATS, + }, + .ops = &da7210_dai_ops, + .symmetric_rate = 1, +}; + +static int da7210_probe(struct snd_soc_component *component) +{ + struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component); + + dev_info(component->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION); + + da7210->mclk_rate = 0; /* This will be set from set_sysclk() */ + da7210->master = 0; /* This will be set from set_fmt() */ + + /* Enable internal regulator & bias current */ + snd_soc_component_write(component, DA7210_CONTROL, DA7210_REG_EN | DA7210_BIAS_EN); + + /* + * ADC settings + */ + + /* Enable Left & Right MIC PGA and Mic Bias */ + snd_soc_component_write(component, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN); + snd_soc_component_write(component, DA7210_MIC_R, DA7210_MIC_R_EN); + + /* Enable Left and Right input PGA */ + snd_soc_component_write(component, DA7210_INMIX_L, DA7210_IN_L_EN); + snd_soc_component_write(component, DA7210_INMIX_R, DA7210_IN_R_EN); + + /* Enable Left and Right ADC */ + snd_soc_component_write(component, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN); + + /* + * DAC settings + */ + + /* Enable Left and Right DAC */ + snd_soc_component_write(component, DA7210_DAC_SEL, + DA7210_DAC_L_SRC_DAI_L | DA7210_DAC_L_EN | + DA7210_DAC_R_SRC_DAI_R | DA7210_DAC_R_EN); + + /* Enable Left and Right out PGA */ + snd_soc_component_write(component, DA7210_OUTMIX_L, DA7210_OUT_L_EN); + snd_soc_component_write(component, DA7210_OUTMIX_R, DA7210_OUT_R_EN); + + /* Enable Left and Right HeadPhone PGA */ + snd_soc_component_write(component, DA7210_HP_CFG, + DA7210_HP_2CAP_MODE | DA7210_HP_SENSE_EN | + DA7210_HP_L_EN | DA7210_HP_MODE | DA7210_HP_R_EN); + + /* Enable ramp mode for DAC gain update */ + snd_soc_component_write(component, DA7210_SOFTMUTE, DA7210_RAMP_EN); + + /* + * For DA7210 codec, there are two ways to enable/disable analog IOs + * and ADC/DAC, + * (1) Using "Enable Bit" of register associated with that IO + * (or ADC/DAC) + * e.g. Mic Left can be enabled using bit 7 of MIC_L(0x7) reg + * + * (2) Using "Standby Bit" of STARTUP2 or STARTUP3 register + * e.g. Mic left can be put to STANDBY using bit 0 of STARTUP3(0x5) + * + * Out of these two methods, the one using STANDBY bits is preferred + * way to enable/disable individual blocks. This is because STANDBY + * registers are part of system controller which allows system power + * up/down in a controlled, pop-free manner. Also, as per application + * note of DA7210, STANDBY register bits are only effective if a + * particular IO (or ADC/DAC) is already enabled using enable/disable + * register bits. Keeping these things in mind, current DAPM + * implementation manipulates only STANDBY bits. + * + * Overall implementation can be outlined as below, + * + * - "Enable bit" of an IO or ADC/DAC is used to enable it in probe() + * - "STANDBY bit" is controlled by DAPM + */ + + /* Enable Line out amplifiers */ + snd_soc_component_write(component, DA7210_OUT1_L, DA7210_OUT1_L_EN); + snd_soc_component_write(component, DA7210_OUT1_R, DA7210_OUT1_R_EN); + snd_soc_component_write(component, DA7210_OUT2, DA7210_OUT2_EN | + DA7210_OUT2_OUTMIX_L | DA7210_OUT2_OUTMIX_R); + + /* Enable Aux1 */ + snd_soc_component_write(component, DA7210_AUX1_L, DA7210_AUX1_L_EN); + snd_soc_component_write(component, DA7210_AUX1_R, DA7210_AUX1_R_EN); + /* Enable Aux2 */ + snd_soc_component_write(component, DA7210_AUX2, DA7210_AUX2_EN); + + /* Set PLL Master clock range 10-20 MHz, enable PLL bypass */ + snd_soc_component_write(component, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ | + DA7210_PLL_BYP); + + /* Diable PLL and bypass it */ + snd_soc_component_write(component, DA7210_PLL, DA7210_PLL_FS_48000); + + /* Activate all enabled subsystem */ + snd_soc_component_write(component, DA7210_STARTUP1, DA7210_SC_MST_EN); + + dev_info(component->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_da7210 = { + .probe = da7210_probe, + .controls = da7210_snd_controls, + .num_controls = ARRAY_SIZE(da7210_snd_controls), + .dapm_widgets = da7210_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7210_dapm_widgets), + .dapm_routes = da7210_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7210_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +#if IS_ENABLED(CONFIG_I2C) + +static const struct reg_sequence da7210_regmap_i2c_patch[] = { + + /* System controller master disable */ + { DA7210_STARTUP1, 0x00 }, + /* Set PLL Master clock range 10-20 MHz */ + { DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ }, + + /* to unlock */ + { DA7210_A_HID_UNLOCK, 0x8B}, + { DA7210_A_TEST_UNLOCK, 0xB4}, + { DA7210_A_PLL1, 0x01}, + { DA7210_A_CP_MODE, 0x7C}, + /* to re-lock */ + { DA7210_A_HID_UNLOCK, 0x00}, + { DA7210_A_TEST_UNLOCK, 0x00}, +}; + +static const struct regmap_config da7210_regmap_config_i2c = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = da7210_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7210_reg_defaults), + .volatile_reg = da7210_volatile_register, + .readable_reg = da7210_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int da7210_i2c_probe(struct i2c_client *i2c) +{ + struct da7210_priv *da7210; + int ret; + + da7210 = devm_kzalloc(&i2c->dev, sizeof(struct da7210_priv), + GFP_KERNEL); + if (!da7210) + return -ENOMEM; + + i2c_set_clientdata(i2c, da7210); + + da7210->regmap = devm_regmap_init_i2c(i2c, &da7210_regmap_config_i2c); + if (IS_ERR(da7210->regmap)) { + ret = PTR_ERR(da7210->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = regmap_register_patch(da7210->regmap, da7210_regmap_i2c_patch, + ARRAY_SIZE(da7210_regmap_i2c_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_da7210, &da7210_dai, 1); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register component: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id da7210_i2c_id[] = { + { "da7210", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da7210_i2c_id); + +/* I2C codec control layer */ +static struct i2c_driver da7210_i2c_driver = { + .driver = { + .name = "da7210", + }, + .probe = da7210_i2c_probe, + .id_table = da7210_i2c_id, +}; +#endif + +#if defined(CONFIG_SPI_MASTER) + +static const struct reg_sequence da7210_regmap_spi_patch[] = { + /* Dummy read to give two pulses over nCS for SPI */ + { DA7210_AUX2, 0x00 }, + { DA7210_AUX2, 0x00 }, + + /* System controller master disable */ + { DA7210_STARTUP1, 0x00 }, + /* Set PLL Master clock range 10-20 MHz */ + { DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ }, + + /* to set PAGE1 of SPI register space */ + { DA7210_PAGE_CONTROL, 0x80 }, + /* to unlock */ + { DA7210_A_HID_UNLOCK, 0x8B}, + { DA7210_A_TEST_UNLOCK, 0xB4}, + { DA7210_A_PLL1, 0x01}, + { DA7210_A_CP_MODE, 0x7C}, + /* to re-lock */ + { DA7210_A_HID_UNLOCK, 0x00}, + { DA7210_A_TEST_UNLOCK, 0x00}, + /* to set back PAGE0 of SPI register space */ + { DA7210_PAGE_CONTROL, 0x00 }, +}; + +static const struct regmap_config da7210_regmap_config_spi = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0x01, + .write_flag_mask = 0x00, + + .reg_defaults = da7210_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7210_reg_defaults), + .volatile_reg = da7210_volatile_register, + .readable_reg = da7210_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int da7210_spi_probe(struct spi_device *spi) +{ + struct da7210_priv *da7210; + int ret; + + da7210 = devm_kzalloc(&spi->dev, sizeof(struct da7210_priv), + GFP_KERNEL); + if (!da7210) + return -ENOMEM; + + spi_set_drvdata(spi, da7210); + da7210->regmap = devm_regmap_init_spi(spi, &da7210_regmap_config_spi); + if (IS_ERR(da7210->regmap)) { + ret = PTR_ERR(da7210->regmap); + dev_err(&spi->dev, "Failed to register regmap: %d\n", ret); + return ret; + } + + ret = regmap_register_patch(da7210->regmap, da7210_regmap_spi_patch, + ARRAY_SIZE(da7210_regmap_spi_patch)); + if (ret != 0) + dev_warn(&spi->dev, "Failed to apply regmap patch: %d\n", ret); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_da7210, &da7210_dai, 1); + + return ret; +} + +static struct spi_driver da7210_spi_driver = { + .driver = { + .name = "da7210", + }, + .probe = da7210_spi_probe, +}; +#endif + +static int __init da7210_modinit(void) +{ + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&da7210_i2c_driver); + if (ret) + return ret; +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&da7210_spi_driver); + if (ret) { + printk(KERN_ERR "Failed to register da7210 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(da7210_modinit); + +static void __exit da7210_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&da7210_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&da7210_spi_driver); +#endif +} +module_exit(da7210_exit); + +MODULE_DESCRIPTION("ASoC DA7210 driver"); +MODULE_AUTHOR("David Chen, Kuninori Morimoto"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c new file mode 100644 index 0000000000..3a6449c44b --- /dev/null +++ b/sound/soc/codecs/da7213.c @@ -0,0 +1,2081 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DA7213 ALSA SoC Codec Driver + * + * Copyright (c) 2013 Dialog Semiconductor + * + * Author: Adam Thomson + * Based on DA9055 ALSA SoC codec driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "da7213.h" + + +/* Gain and Volume */ +static const DECLARE_TLV_DB_RANGE(aux_vol_tlv, + /* -54dB */ + 0x0, 0x11, TLV_DB_SCALE_ITEM(-5400, 0, 0), + /* -52.5dB to 15dB */ + 0x12, 0x3f, TLV_DB_SCALE_ITEM(-5250, 150, 0) +); + +static const DECLARE_TLV_DB_RANGE(digital_gain_tlv, + 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -78dB to 12dB */ + 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0) +); + +static const DECLARE_TLV_DB_RANGE(alc_analog_gain_tlv, + 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* 0dB to 36dB */ + 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0) +); + +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0); +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0); +static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0); + +/* ADC and DAC voice mode (8kHz) high pass cutoff value */ +static const char * const da7213_voice_hpf_corner_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_dac_voice_hpf_corner, + DA7213_DAC_FILTERS1, + DA7213_VOICE_HPF_CORNER_SHIFT, + da7213_voice_hpf_corner_txt); + +static SOC_ENUM_SINGLE_DECL(da7213_adc_voice_hpf_corner, + DA7213_ADC_FILTERS1, + DA7213_VOICE_HPF_CORNER_SHIFT, + da7213_voice_hpf_corner_txt); + +/* ADC and DAC high pass filter cutoff value */ +static const char * const da7213_audio_hpf_corner_txt[] = { + "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_dac_audio_hpf_corner, + DA7213_DAC_FILTERS1 + , DA7213_AUDIO_HPF_CORNER_SHIFT, + da7213_audio_hpf_corner_txt); + +static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner, + DA7213_ADC_FILTERS1, + DA7213_AUDIO_HPF_CORNER_SHIFT, + da7213_audio_hpf_corner_txt); + +/* Gain ramping rate value */ +static const char * const da7213_gain_ramp_rate_txt[] = { + "nominal rate * 8", "nominal rate * 16", "nominal rate / 16", + "nominal rate / 32" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_gain_ramp_rate, + DA7213_GAIN_RAMP_CTRL, + DA7213_GAIN_RAMP_RATE_SHIFT, + da7213_gain_ramp_rate_txt); + +/* DAC noise gate setup time value */ +static const char * const da7213_dac_ng_setup_time_txt[] = { + "256 samples", "512 samples", "1024 samples", "2048 samples" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_setup_time, + DA7213_DAC_NG_SETUP_TIME, + DA7213_DAC_NG_SETUP_TIME_SHIFT, + da7213_dac_ng_setup_time_txt); + +/* DAC noise gate rampup rate value */ +static const char * const da7213_dac_ng_rampup_txt[] = { + "0.02 ms/dB", "0.16 ms/dB" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_rampup_rate, + DA7213_DAC_NG_SETUP_TIME, + DA7213_DAC_NG_RAMPUP_RATE_SHIFT, + da7213_dac_ng_rampup_txt); + +/* DAC noise gate rampdown rate value */ +static const char * const da7213_dac_ng_rampdown_txt[] = { + "0.64 ms/dB", "20.48 ms/dB" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_dac_ng_rampdown_rate, + DA7213_DAC_NG_SETUP_TIME, + DA7213_DAC_NG_RAMPDN_RATE_SHIFT, + da7213_dac_ng_rampdown_txt); + +/* DAC soft mute rate value */ +static const char * const da7213_dac_soft_mute_rate_txt[] = { + "1", "2", "4", "8", "16", "32", "64" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_dac_soft_mute_rate, + DA7213_DAC_FILTERS5, + DA7213_DAC_SOFTMUTE_RATE_SHIFT, + da7213_dac_soft_mute_rate_txt); + +/* ALC Attack Rate select */ +static const char * const da7213_alc_attack_rate_txt[] = { + "44/fs", "88/fs", "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", + "5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_alc_attack_rate, + DA7213_ALC_CTRL2, + DA7213_ALC_ATTACK_SHIFT, + da7213_alc_attack_rate_txt); + +/* ALC Release Rate select */ +static const char * const da7213_alc_release_rate_txt[] = { + "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", "5632/fs", + "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_alc_release_rate, + DA7213_ALC_CTRL2, + DA7213_ALC_RELEASE_SHIFT, + da7213_alc_release_rate_txt); + +/* ALC Hold Time select */ +static const char * const da7213_alc_hold_time_txt[] = { + "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", + "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", + "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_alc_hold_time, + DA7213_ALC_CTRL3, + DA7213_ALC_HOLD_SHIFT, + da7213_alc_hold_time_txt); + +/* ALC Input Signal Tracking rate select */ +static const char * const da7213_alc_integ_rate_txt[] = { + "1/4", "1/16", "1/256", "1/65536" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_attack_rate, + DA7213_ALC_CTRL3, + DA7213_ALC_INTEG_ATTACK_SHIFT, + da7213_alc_integ_rate_txt); + +static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_release_rate, + DA7213_ALC_CTRL3, + DA7213_ALC_INTEG_RELEASE_SHIFT, + da7213_alc_integ_rate_txt); + + +/* + * Control Functions + */ + +static int da7213_get_alc_data(struct snd_soc_component *component, u8 reg_val) +{ + int mid_data, top_data; + int sum = 0; + u8 iteration; + + for (iteration = 0; iteration < DA7213_ALC_AVG_ITERATIONS; + iteration++) { + /* Select the left or right channel and capture data */ + snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL, reg_val); + + /* Select middle 8 bits for read back from data register */ + snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL, + reg_val | DA7213_ALC_DATA_MIDDLE); + mid_data = snd_soc_component_read(component, DA7213_ALC_CIC_OP_LVL_DATA); + + /* Select top 8 bits for read back from data register */ + snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL, + reg_val | DA7213_ALC_DATA_TOP); + top_data = snd_soc_component_read(component, DA7213_ALC_CIC_OP_LVL_DATA); + + sum += ((mid_data << 8) | (top_data << 16)); + } + + return sum / DA7213_ALC_AVG_ITERATIONS; +} + +static void da7213_alc_calib_man(struct snd_soc_component *component) +{ + u8 reg_val; + int avg_left_data, avg_right_data, offset_l, offset_r; + + /* Calculate average for Left and Right data */ + /* Left Data */ + avg_left_data = da7213_get_alc_data(component, + DA7213_ALC_CIC_OP_CHANNEL_LEFT); + /* Right Data */ + avg_right_data = da7213_get_alc_data(component, + DA7213_ALC_CIC_OP_CHANNEL_RIGHT); + + /* Calculate DC offset */ + offset_l = -avg_left_data; + offset_r = -avg_right_data; + + reg_val = (offset_l & DA7213_ALC_OFFSET_15_8) >> 8; + snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_M_L, reg_val); + reg_val = (offset_l & DA7213_ALC_OFFSET_19_16) >> 16; + snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_U_L, reg_val); + + reg_val = (offset_r & DA7213_ALC_OFFSET_15_8) >> 8; + snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_M_R, reg_val); + reg_val = (offset_r & DA7213_ALC_OFFSET_19_16) >> 16; + snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_U_R, reg_val); + + /* Enable analog/digital gain mode & offset cancellation */ + snd_soc_component_update_bits(component, DA7213_ALC_CTRL1, + DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE, + DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE); +} + +static void da7213_alc_calib_auto(struct snd_soc_component *component) +{ + u8 alc_ctrl1; + + /* Begin auto calibration and wait for completion */ + snd_soc_component_update_bits(component, DA7213_ALC_CTRL1, DA7213_ALC_AUTO_CALIB_EN, + DA7213_ALC_AUTO_CALIB_EN); + do { + alc_ctrl1 = snd_soc_component_read(component, DA7213_ALC_CTRL1); + } while (alc_ctrl1 & DA7213_ALC_AUTO_CALIB_EN); + + /* If auto calibration fails, fall back to digital gain only mode */ + if (alc_ctrl1 & DA7213_ALC_CALIB_OVERFLOW) { + dev_warn(component->dev, + "ALC auto calibration failed with overflow\n"); + snd_soc_component_update_bits(component, DA7213_ALC_CTRL1, + DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE, + 0); + } else { + /* Enable analog/digital gain mode & offset cancellation */ + snd_soc_component_update_bits(component, DA7213_ALC_CTRL1, + DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE, + DA7213_ALC_OFFSET_EN | DA7213_ALC_SYNC_MODE); + } + +} + +static void da7213_alc_calib(struct snd_soc_component *component) +{ + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + u8 adc_l_ctrl, adc_r_ctrl; + u8 mixin_l_sel, mixin_r_sel; + u8 mic_1_ctrl, mic_2_ctrl; + + /* Save current values from ADC control registers */ + adc_l_ctrl = snd_soc_component_read(component, DA7213_ADC_L_CTRL); + adc_r_ctrl = snd_soc_component_read(component, DA7213_ADC_R_CTRL); + + /* Save current values from MIXIN_L/R_SELECT registers */ + mixin_l_sel = snd_soc_component_read(component, DA7213_MIXIN_L_SELECT); + mixin_r_sel = snd_soc_component_read(component, DA7213_MIXIN_R_SELECT); + + /* Save current values from MIC control registers */ + mic_1_ctrl = snd_soc_component_read(component, DA7213_MIC_1_CTRL); + mic_2_ctrl = snd_soc_component_read(component, DA7213_MIC_2_CTRL); + + /* Enable ADC Left and Right */ + snd_soc_component_update_bits(component, DA7213_ADC_L_CTRL, DA7213_ADC_EN, + DA7213_ADC_EN); + snd_soc_component_update_bits(component, DA7213_ADC_R_CTRL, DA7213_ADC_EN, + DA7213_ADC_EN); + + /* Enable MIC paths */ + snd_soc_component_update_bits(component, DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_L_MIX_SELECT_MIC_1 | + DA7213_MIXIN_L_MIX_SELECT_MIC_2, + DA7213_MIXIN_L_MIX_SELECT_MIC_1 | + DA7213_MIXIN_L_MIX_SELECT_MIC_2); + snd_soc_component_update_bits(component, DA7213_MIXIN_R_SELECT, + DA7213_MIXIN_R_MIX_SELECT_MIC_2 | + DA7213_MIXIN_R_MIX_SELECT_MIC_1, + DA7213_MIXIN_R_MIX_SELECT_MIC_2 | + DA7213_MIXIN_R_MIX_SELECT_MIC_1); + + /* Mute MIC PGAs */ + snd_soc_component_update_bits(component, DA7213_MIC_1_CTRL, DA7213_MUTE_EN, + DA7213_MUTE_EN); + snd_soc_component_update_bits(component, DA7213_MIC_2_CTRL, DA7213_MUTE_EN, + DA7213_MUTE_EN); + + /* Perform calibration */ + if (da7213->alc_calib_auto) + da7213_alc_calib_auto(component); + else + da7213_alc_calib_man(component); + + /* Restore MIXIN_L/R_SELECT registers to their original states */ + snd_soc_component_write(component, DA7213_MIXIN_L_SELECT, mixin_l_sel); + snd_soc_component_write(component, DA7213_MIXIN_R_SELECT, mixin_r_sel); + + /* Restore ADC control registers to their original states */ + snd_soc_component_write(component, DA7213_ADC_L_CTRL, adc_l_ctrl); + snd_soc_component_write(component, DA7213_ADC_R_CTRL, adc_r_ctrl); + + /* Restore original values of MIC control registers */ + snd_soc_component_write(component, DA7213_MIC_1_CTRL, mic_1_ctrl); + snd_soc_component_write(component, DA7213_MIC_2_CTRL, mic_2_ctrl); +} + +static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); + + /* If ALC in operation, make sure calibrated offsets are updated */ + if ((!ret) && (da7213->alc_en)) + da7213_alc_calib(component); + + return ret; +} + +static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + + /* Force ALC offset calibration if enabling ALC */ + if (ucontrol->value.integer.value[0] || + ucontrol->value.integer.value[1]) { + if (!da7213->alc_en) { + da7213_alc_calib(component); + da7213->alc_en = true; + } + } else { + da7213->alc_en = false; + } + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + + +/* + * KControls + */ + +static const struct snd_kcontrol_new da7213_snd_controls[] = { + + /* Volume controls */ + SOC_SINGLE_TLV("Mic 1 Volume", DA7213_MIC_1_GAIN, + DA7213_MIC_AMP_GAIN_SHIFT, DA7213_MIC_AMP_GAIN_MAX, + DA7213_NO_INVERT, mic_vol_tlv), + SOC_SINGLE_TLV("Mic 2 Volume", DA7213_MIC_2_GAIN, + DA7213_MIC_AMP_GAIN_SHIFT, DA7213_MIC_AMP_GAIN_MAX, + DA7213_NO_INVERT, mic_vol_tlv), + SOC_DOUBLE_R_TLV("Aux Volume", DA7213_AUX_L_GAIN, DA7213_AUX_R_GAIN, + DA7213_AUX_AMP_GAIN_SHIFT, DA7213_AUX_AMP_GAIN_MAX, + DA7213_NO_INVERT, aux_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("Mixin PGA Volume", DA7213_MIXIN_L_GAIN, + DA7213_MIXIN_R_GAIN, DA7213_MIXIN_AMP_GAIN_SHIFT, + DA7213_MIXIN_AMP_GAIN_MAX, DA7213_NO_INVERT, + snd_soc_get_volsw_2r, da7213_put_mixin_gain, + mixin_gain_tlv), + SOC_DOUBLE_R_TLV("ADC Volume", DA7213_ADC_L_GAIN, DA7213_ADC_R_GAIN, + DA7213_ADC_AMP_GAIN_SHIFT, DA7213_ADC_AMP_GAIN_MAX, + DA7213_NO_INVERT, digital_gain_tlv), + SOC_DOUBLE_R_TLV("DAC Volume", DA7213_DAC_L_GAIN, DA7213_DAC_R_GAIN, + DA7213_DAC_AMP_GAIN_SHIFT, DA7213_DAC_AMP_GAIN_MAX, + DA7213_NO_INVERT, digital_gain_tlv), + SOC_DOUBLE_R_TLV("Headphone Volume", DA7213_HP_L_GAIN, DA7213_HP_R_GAIN, + DA7213_HP_AMP_GAIN_SHIFT, DA7213_HP_AMP_GAIN_MAX, + DA7213_NO_INVERT, hp_vol_tlv), + SOC_SINGLE_TLV("Lineout Volume", DA7213_LINE_GAIN, + DA7213_LINE_AMP_GAIN_SHIFT, DA7213_LINE_AMP_GAIN_MAX, + DA7213_NO_INVERT, lineout_vol_tlv), + + /* DAC Equalizer controls */ + SOC_SINGLE("DAC EQ Switch", DA7213_DAC_FILTERS4, DA7213_DAC_EQ_EN_SHIFT, + DA7213_DAC_EQ_EN_MAX, DA7213_NO_INVERT), + SOC_SINGLE_TLV("DAC EQ1 Volume", DA7213_DAC_FILTERS2, + DA7213_DAC_EQ_BAND1_SHIFT, DA7213_DAC_EQ_BAND_MAX, + DA7213_NO_INVERT, eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ2 Volume", DA7213_DAC_FILTERS2, + DA7213_DAC_EQ_BAND2_SHIFT, DA7213_DAC_EQ_BAND_MAX, + DA7213_NO_INVERT, eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ3 Volume", DA7213_DAC_FILTERS3, + DA7213_DAC_EQ_BAND3_SHIFT, DA7213_DAC_EQ_BAND_MAX, + DA7213_NO_INVERT, eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ4 Volume", DA7213_DAC_FILTERS3, + DA7213_DAC_EQ_BAND4_SHIFT, DA7213_DAC_EQ_BAND_MAX, + DA7213_NO_INVERT, eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ5 Volume", DA7213_DAC_FILTERS4, + DA7213_DAC_EQ_BAND5_SHIFT, DA7213_DAC_EQ_BAND_MAX, + DA7213_NO_INVERT, eq_gain_tlv), + + /* High Pass Filter and Voice Mode controls */ + SOC_SINGLE("ADC HPF Switch", DA7213_ADC_FILTERS1, DA7213_HPF_EN_SHIFT, + DA7213_HPF_EN_MAX, DA7213_NO_INVERT), + SOC_ENUM("ADC HPF Cutoff", da7213_adc_audio_hpf_corner), + SOC_SINGLE("ADC Voice Mode Switch", DA7213_ADC_FILTERS1, + DA7213_VOICE_EN_SHIFT, DA7213_VOICE_EN_MAX, + DA7213_NO_INVERT), + SOC_ENUM("ADC Voice Cutoff", da7213_adc_voice_hpf_corner), + + SOC_SINGLE("DAC HPF Switch", DA7213_DAC_FILTERS1, DA7213_HPF_EN_SHIFT, + DA7213_HPF_EN_MAX, DA7213_NO_INVERT), + SOC_ENUM("DAC HPF Cutoff", da7213_dac_audio_hpf_corner), + SOC_SINGLE("DAC Voice Mode Switch", DA7213_DAC_FILTERS1, + DA7213_VOICE_EN_SHIFT, DA7213_VOICE_EN_MAX, + DA7213_NO_INVERT), + SOC_ENUM("DAC Voice Cutoff", da7213_dac_voice_hpf_corner), + + /* Mute controls */ + SOC_SINGLE("Mic 1 Switch", DA7213_MIC_1_CTRL, DA7213_MUTE_EN_SHIFT, + DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_SINGLE("Mic 2 Switch", DA7213_MIC_2_CTRL, DA7213_MUTE_EN_SHIFT, + DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_DOUBLE_R("Aux Switch", DA7213_AUX_L_CTRL, DA7213_AUX_R_CTRL, + DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_DOUBLE_R("Mixin PGA Switch", DA7213_MIXIN_L_CTRL, + DA7213_MIXIN_R_CTRL, DA7213_MUTE_EN_SHIFT, + DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_DOUBLE_R("ADC Switch", DA7213_ADC_L_CTRL, DA7213_ADC_R_CTRL, + DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_DOUBLE_R("Headphone Switch", DA7213_HP_L_CTRL, DA7213_HP_R_CTRL, + DA7213_MUTE_EN_SHIFT, DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_SINGLE("Lineout Switch", DA7213_LINE_CTRL, DA7213_MUTE_EN_SHIFT, + DA7213_MUTE_EN_MAX, DA7213_INVERT), + SOC_SINGLE("DAC Soft Mute Switch", DA7213_DAC_FILTERS5, + DA7213_DAC_SOFTMUTE_EN_SHIFT, DA7213_DAC_SOFTMUTE_EN_MAX, + DA7213_NO_INVERT), + SOC_ENUM("DAC Soft Mute Rate", da7213_dac_soft_mute_rate), + + /* Zero Cross controls */ + SOC_DOUBLE_R("Aux ZC Switch", DA7213_AUX_L_CTRL, DA7213_AUX_R_CTRL, + DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, DA7213_NO_INVERT), + SOC_DOUBLE_R("Mixin PGA ZC Switch", DA7213_MIXIN_L_CTRL, + DA7213_MIXIN_R_CTRL, DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, + DA7213_NO_INVERT), + SOC_DOUBLE_R("Headphone ZC Switch", DA7213_HP_L_CTRL, DA7213_HP_R_CTRL, + DA7213_ZC_EN_SHIFT, DA7213_ZC_EN_MAX, DA7213_NO_INVERT), + + /* Gain Ramping controls */ + SOC_DOUBLE_R("Aux Gain Ramping Switch", DA7213_AUX_L_CTRL, + DA7213_AUX_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT, + DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT), + SOC_DOUBLE_R("Mixin Gain Ramping Switch", DA7213_MIXIN_L_CTRL, + DA7213_MIXIN_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT, + DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT), + SOC_DOUBLE_R("ADC Gain Ramping Switch", DA7213_ADC_L_CTRL, + DA7213_ADC_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT, + DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT), + SOC_DOUBLE_R("DAC Gain Ramping Switch", DA7213_DAC_L_CTRL, + DA7213_DAC_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT, + DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT), + SOC_DOUBLE_R("Headphone Gain Ramping Switch", DA7213_HP_L_CTRL, + DA7213_HP_R_CTRL, DA7213_GAIN_RAMP_EN_SHIFT, + DA7213_GAIN_RAMP_EN_MAX, DA7213_NO_INVERT), + SOC_SINGLE("Lineout Gain Ramping Switch", DA7213_LINE_CTRL, + DA7213_GAIN_RAMP_EN_SHIFT, DA7213_GAIN_RAMP_EN_MAX, + DA7213_NO_INVERT), + SOC_ENUM("Gain Ramping Rate", da7213_gain_ramp_rate), + + /* DAC Noise Gate controls */ + SOC_SINGLE("DAC NG Switch", DA7213_DAC_NG_CTRL, DA7213_DAC_NG_EN_SHIFT, + DA7213_DAC_NG_EN_MAX, DA7213_NO_INVERT), + SOC_ENUM("DAC NG Setup Time", da7213_dac_ng_setup_time), + SOC_ENUM("DAC NG Rampup Rate", da7213_dac_ng_rampup_rate), + SOC_ENUM("DAC NG Rampdown Rate", da7213_dac_ng_rampdown_rate), + SOC_SINGLE("DAC NG OFF Threshold", DA7213_DAC_NG_OFF_THRESHOLD, + DA7213_DAC_NG_THRESHOLD_SHIFT, DA7213_DAC_NG_THRESHOLD_MAX, + DA7213_NO_INVERT), + SOC_SINGLE("DAC NG ON Threshold", DA7213_DAC_NG_ON_THRESHOLD, + DA7213_DAC_NG_THRESHOLD_SHIFT, DA7213_DAC_NG_THRESHOLD_MAX, + DA7213_NO_INVERT), + + /* DAC Routing & Inversion */ + SOC_DOUBLE("DAC Mono Switch", DA7213_DIG_ROUTING_DAC, + DA7213_DAC_L_MONO_SHIFT, DA7213_DAC_R_MONO_SHIFT, + DA7213_DAC_MONO_MAX, DA7213_NO_INVERT), + SOC_DOUBLE("DAC Invert Switch", DA7213_DIG_CTRL, DA7213_DAC_L_INV_SHIFT, + DA7213_DAC_R_INV_SHIFT, DA7213_DAC_INV_MAX, + DA7213_NO_INVERT), + + /* DMIC controls */ + SOC_DOUBLE_R("DMIC Switch", DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_R_SELECT, DA7213_DMIC_EN_SHIFT, + DA7213_DMIC_EN_MAX, DA7213_NO_INVERT), + + /* ALC Controls */ + SOC_DOUBLE_EXT("ALC Switch", DA7213_ALC_CTRL1, DA7213_ALC_L_EN_SHIFT, + DA7213_ALC_R_EN_SHIFT, DA7213_ALC_EN_MAX, + DA7213_NO_INVERT, snd_soc_get_volsw, da7213_put_alc_sw), + SOC_ENUM("ALC Attack Rate", da7213_alc_attack_rate), + SOC_ENUM("ALC Release Rate", da7213_alc_release_rate), + SOC_ENUM("ALC Hold Time", da7213_alc_hold_time), + /* + * Rate at which input signal envelope is tracked as the signal gets + * larger + */ + SOC_ENUM("ALC Integ Attack Rate", da7213_alc_integ_attack_rate), + /* + * Rate at which input signal envelope is tracked as the signal gets + * smaller + */ + SOC_ENUM("ALC Integ Release Rate", da7213_alc_integ_release_rate), + SOC_SINGLE_TLV("ALC Noise Threshold Volume", DA7213_ALC_NOISE, + DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX, + DA7213_INVERT, alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Min Threshold Volume", DA7213_ALC_TARGET_MIN, + DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX, + DA7213_INVERT, alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Threshold Volume", DA7213_ALC_TARGET_MAX, + DA7213_ALC_THRESHOLD_SHIFT, DA7213_ALC_THRESHOLD_MAX, + DA7213_INVERT, alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Attenuation Volume", DA7213_ALC_GAIN_LIMITS, + DA7213_ALC_ATTEN_MAX_SHIFT, + DA7213_ALC_ATTEN_GAIN_MAX_MAX, DA7213_NO_INVERT, + alc_gain_tlv), + SOC_SINGLE_TLV("ALC Max Gain Volume", DA7213_ALC_GAIN_LIMITS, + DA7213_ALC_GAIN_MAX_SHIFT, DA7213_ALC_ATTEN_GAIN_MAX_MAX, + DA7213_NO_INVERT, alc_gain_tlv), + SOC_SINGLE_TLV("ALC Min Analog Gain Volume", DA7213_ALC_ANA_GAIN_LIMITS, + DA7213_ALC_ANA_GAIN_MIN_SHIFT, DA7213_ALC_ANA_GAIN_MAX, + DA7213_NO_INVERT, alc_analog_gain_tlv), + SOC_SINGLE_TLV("ALC Max Analog Gain Volume", DA7213_ALC_ANA_GAIN_LIMITS, + DA7213_ALC_ANA_GAIN_MAX_SHIFT, DA7213_ALC_ANA_GAIN_MAX, + DA7213_NO_INVERT, alc_analog_gain_tlv), + SOC_SINGLE("ALC Anticlip Mode Switch", DA7213_ALC_ANTICLIP_CTRL, + DA7213_ALC_ANTICLIP_EN_SHIFT, DA7213_ALC_ANTICLIP_EN_MAX, + DA7213_NO_INVERT), + SOC_SINGLE("ALC Anticlip Level", DA7213_ALC_ANTICLIP_LEVEL, + DA7213_ALC_ANTICLIP_LEVEL_SHIFT, + DA7213_ALC_ANTICLIP_LEVEL_MAX, DA7213_NO_INVERT), +}; + + +/* + * DAPM + */ + +/* + * Enums + */ + +/* MIC PGA source select */ +static const char * const da7213_mic_amp_in_sel_txt[] = { + "Differential", "MIC_P", "MIC_N" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_mic_1_amp_in_sel, + DA7213_MIC_1_CTRL, + DA7213_MIC_AMP_IN_SEL_SHIFT, + da7213_mic_amp_in_sel_txt); +static const struct snd_kcontrol_new da7213_mic_1_amp_in_sel_mux = + SOC_DAPM_ENUM("Mic 1 Amp Source MUX", da7213_mic_1_amp_in_sel); + +static SOC_ENUM_SINGLE_DECL(da7213_mic_2_amp_in_sel, + DA7213_MIC_2_CTRL, + DA7213_MIC_AMP_IN_SEL_SHIFT, + da7213_mic_amp_in_sel_txt); +static const struct snd_kcontrol_new da7213_mic_2_amp_in_sel_mux = + SOC_DAPM_ENUM("Mic 2 Amp Source MUX", da7213_mic_2_amp_in_sel); + +/* DAI routing select */ +static const char * const da7213_dai_src_txt[] = { + "ADC Left", "ADC Right", "DAI Input Left", "DAI Input Right" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_dai_l_src, + DA7213_DIG_ROUTING_DAI, + DA7213_DAI_L_SRC_SHIFT, + da7213_dai_src_txt); +static const struct snd_kcontrol_new da7213_dai_l_src_mux = + SOC_DAPM_ENUM("DAI Left Source MUX", da7213_dai_l_src); + +static SOC_ENUM_SINGLE_DECL(da7213_dai_r_src, + DA7213_DIG_ROUTING_DAI, + DA7213_DAI_R_SRC_SHIFT, + da7213_dai_src_txt); +static const struct snd_kcontrol_new da7213_dai_r_src_mux = + SOC_DAPM_ENUM("DAI Right Source MUX", da7213_dai_r_src); + +/* DAC routing select */ +static const char * const da7213_dac_src_txt[] = { + "ADC Output Left", "ADC Output Right", "DAI Input Left", + "DAI Input Right" +}; + +static SOC_ENUM_SINGLE_DECL(da7213_dac_l_src, + DA7213_DIG_ROUTING_DAC, + DA7213_DAC_L_SRC_SHIFT, + da7213_dac_src_txt); +static const struct snd_kcontrol_new da7213_dac_l_src_mux = + SOC_DAPM_ENUM("DAC Left Source MUX", da7213_dac_l_src); + +static SOC_ENUM_SINGLE_DECL(da7213_dac_r_src, + DA7213_DIG_ROUTING_DAC, + DA7213_DAC_R_SRC_SHIFT, + da7213_dac_src_txt); +static const struct snd_kcontrol_new da7213_dac_r_src_mux = + SOC_DAPM_ENUM("DAC Right Source MUX", da7213_dac_r_src); + +/* + * Mixer Controls + */ + +/* Mixin Left */ +static const struct snd_kcontrol_new da7213_dapm_mixinl_controls[] = { + SOC_DAPM_SINGLE("Aux Left Switch", DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_L_MIX_SELECT_AUX_L_SHIFT, + DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mic 1 Switch", DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_L_MIX_SELECT_MIC_1_SHIFT, + DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mic 2 Switch", DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_L_MIX_SELECT_MIC_2_SHIFT, + DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXIN_L_SELECT, + DA7213_MIXIN_L_MIX_SELECT_MIXIN_R_SHIFT, + DA7213_MIXIN_L_MIX_SELECT_MAX, DA7213_NO_INVERT), +}; + +/* Mixin Right */ +static const struct snd_kcontrol_new da7213_dapm_mixinr_controls[] = { + SOC_DAPM_SINGLE("Aux Right Switch", DA7213_MIXIN_R_SELECT, + DA7213_MIXIN_R_MIX_SELECT_AUX_R_SHIFT, + DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mic 2 Switch", DA7213_MIXIN_R_SELECT, + DA7213_MIXIN_R_MIX_SELECT_MIC_2_SHIFT, + DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mic 1 Switch", DA7213_MIXIN_R_SELECT, + DA7213_MIXIN_R_MIX_SELECT_MIC_1_SHIFT, + DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXIN_R_SELECT, + DA7213_MIXIN_R_MIX_SELECT_MIXIN_L_SHIFT, + DA7213_MIXIN_R_MIX_SELECT_MAX, DA7213_NO_INVERT), +}; + +/* Mixout Left */ +static const struct snd_kcontrol_new da7213_dapm_mixoutl_controls[] = { + SOC_DAPM_SINGLE("Aux Left Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_AUX_L_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("DAC Left Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_DAC_L_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Aux Left Invert Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_AUX_L_INVERTED_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_INVERTED_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA7213_MIXOUT_L_SELECT, + DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_INVERTED_SHIFT, + DA7213_MIXOUT_L_MIX_SELECT_MAX, DA7213_NO_INVERT), +}; + +/* Mixout Right */ +static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = { + SOC_DAPM_SINGLE("Aux Right Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_AUX_R_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Right Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Left Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("DAC Right Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_DAC_R_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Aux Right Invert Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_AUX_R_INVERTED_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_INVERTED_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), + SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA7213_MIXOUT_R_SELECT, + DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_INVERTED_SHIFT, + DA7213_MIXOUT_R_MIX_SELECT_MAX, DA7213_NO_INVERT), +}; + + +/* + * DAPM Events + */ + +static int da7213_dai_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + u8 pll_ctrl, pll_status; + int i = 0; + bool srm_lock = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable DAI clks for master mode */ + if (da7213->master) + snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE, + DA7213_DAI_CLK_EN_MASK, + DA7213_DAI_CLK_EN_MASK); + + /* PC synchronised to DAI */ + snd_soc_component_update_bits(component, DA7213_PC_COUNT, + DA7213_PC_FREERUN_MASK, 0); + + /* If SRM not enabled then nothing more to do */ + pll_ctrl = snd_soc_component_read(component, DA7213_PLL_CTRL); + if (!(pll_ctrl & DA7213_PLL_SRM_EN)) + return 0; + + /* Assist 32KHz mode PLL lock */ + if (pll_ctrl & DA7213_PLL_32K_MODE) { + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0xF2, 0x03); + snd_soc_component_write(component, 0xF0, 0x00); + } + + /* Check SRM has locked */ + do { + pll_status = snd_soc_component_read(component, DA7213_PLL_STATUS); + if (pll_status & DA7219_PLL_SRM_LOCK) { + srm_lock = true; + } else { + ++i; + msleep(50); + } + } while ((i < DA7213_SRM_CHECK_RETRIES) && (!srm_lock)); + + if (!srm_lock) + dev_warn(component->dev, "SRM failed to lock\n"); + + return 0; + case SND_SOC_DAPM_POST_PMD: + /* Revert 32KHz PLL lock udpates if applied previously */ + pll_ctrl = snd_soc_component_read(component, DA7213_PLL_CTRL); + if (pll_ctrl & DA7213_PLL_32K_MODE) { + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0xF2, 0x01); + snd_soc_component_write(component, 0xF0, 0x00); + } + + /* PC free-running */ + snd_soc_component_update_bits(component, DA7213_PC_COUNT, + DA7213_PC_FREERUN_MASK, + DA7213_PC_FREERUN_MASK); + + /* Disable DAI clks if in master mode */ + if (da7213->master) + snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE, + DA7213_DAI_CLK_EN_MASK, 0); + return 0; + default: + return -EINVAL; + } +} + + +/* + * DAPM widgets + */ + +static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = { + /* + * Power Supply + */ + SND_SOC_DAPM_REGULATOR_SUPPLY("VDDMIC", 0, 0), + + /* + * Input & Output + */ + + /* Use a supply here as this controls both input & output DAIs */ + SND_SOC_DAPM_SUPPLY("DAI", DA7213_DAI_CTRL, DA7213_DAI_EN_SHIFT, + DA7213_NO_INVERT, da7213_dai_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* + * Input + */ + + /* Input Lines */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("AUXL"), + SND_SOC_DAPM_INPUT("AUXR"), + + /* MUXs for Mic PGA source selection */ + SND_SOC_DAPM_MUX("Mic 1 Amp Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_mic_1_amp_in_sel_mux), + SND_SOC_DAPM_MUX("Mic 2 Amp Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_mic_2_amp_in_sel_mux), + + /* Input PGAs */ + SND_SOC_DAPM_PGA("Mic 1 PGA", DA7213_MIC_1_CTRL, DA7213_AMP_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Mic 2 PGA", DA7213_MIC_2_CTRL, DA7213_AMP_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Aux Left PGA", DA7213_AUX_L_CTRL, DA7213_AMP_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Aux Right PGA", DA7213_AUX_R_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Mixin Left PGA", DA7213_MIXIN_L_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Mixin Right PGA", DA7213_MIXIN_R_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + + /* Mic Biases */ + SND_SOC_DAPM_SUPPLY("Mic Bias 1", DA7213_MICBIAS_CTRL, + DA7213_MICBIAS1_EN_SHIFT, DA7213_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias 2", DA7213_MICBIAS_CTRL, + DA7213_MICBIAS2_EN_SHIFT, DA7213_NO_INVERT, + NULL, 0), + + /* Input Mixers */ + SND_SOC_DAPM_MIXER("Mixin Left", SND_SOC_NOPM, 0, 0, + &da7213_dapm_mixinl_controls[0], + ARRAY_SIZE(da7213_dapm_mixinl_controls)), + SND_SOC_DAPM_MIXER("Mixin Right", SND_SOC_NOPM, 0, 0, + &da7213_dapm_mixinr_controls[0], + ARRAY_SIZE(da7213_dapm_mixinr_controls)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC Left", NULL, DA7213_ADC_L_CTRL, + DA7213_ADC_EN_SHIFT, DA7213_NO_INVERT), + SND_SOC_DAPM_ADC("ADC Right", NULL, DA7213_ADC_R_CTRL, + DA7213_ADC_EN_SHIFT, DA7213_NO_INVERT), + + /* DAI */ + SND_SOC_DAPM_MUX("DAI Left Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_dai_l_src_mux), + SND_SOC_DAPM_MUX("DAI Right Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_dai_r_src_mux), + SND_SOC_DAPM_AIF_OUT("DAIOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DAIOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0), + + /* + * Output + */ + + /* DAI */ + SND_SOC_DAPM_AIF_IN("DAIINL", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DAIINR", "Playback", 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("DAC Left Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_dac_l_src_mux), + SND_SOC_DAPM_MUX("DAC Right Source MUX", SND_SOC_NOPM, 0, 0, + &da7213_dac_r_src_mux), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC Left", NULL, DA7213_DAC_L_CTRL, + DA7213_DAC_EN_SHIFT, DA7213_NO_INVERT), + SND_SOC_DAPM_DAC("DAC Right", NULL, DA7213_DAC_R_CTRL, + DA7213_DAC_EN_SHIFT, DA7213_NO_INVERT), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Mixout Left", SND_SOC_NOPM, 0, 0, + &da7213_dapm_mixoutl_controls[0], + ARRAY_SIZE(da7213_dapm_mixoutl_controls)), + SND_SOC_DAPM_MIXER("Mixout Right", SND_SOC_NOPM, 0, 0, + &da7213_dapm_mixoutr_controls[0], + ARRAY_SIZE(da7213_dapm_mixoutr_controls)), + + /* Output PGAs */ + SND_SOC_DAPM_PGA("Mixout Left PGA", DA7213_MIXOUT_L_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Mixout Right PGA", DA7213_MIXOUT_R_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Lineout PGA", DA7213_LINE_CTRL, DA7213_AMP_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Left PGA", DA7213_HP_L_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Right PGA", DA7213_HP_R_CTRL, + DA7213_AMP_EN_SHIFT, DA7213_NO_INVERT, NULL, 0), + + /* Charge Pump */ + SND_SOC_DAPM_SUPPLY("Charge Pump", DA7213_CP_CTRL, DA7213_CP_EN_SHIFT, + DA7213_NO_INVERT, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LINE"), +}; + + +/* + * DAPM audio route definition + */ + +static const struct snd_soc_dapm_route da7213_audio_map[] = { + /* Dest Connecting Widget source */ + + /* Input path */ + {"Mic Bias 1", NULL, "VDDMIC"}, + {"Mic Bias 2", NULL, "VDDMIC"}, + + {"MIC1", NULL, "Mic Bias 1"}, + {"MIC2", NULL, "Mic Bias 2"}, + + {"Mic 1 Amp Source MUX", "Differential", "MIC1"}, + {"Mic 1 Amp Source MUX", "MIC_P", "MIC1"}, + {"Mic 1 Amp Source MUX", "MIC_N", "MIC1"}, + + {"Mic 2 Amp Source MUX", "Differential", "MIC2"}, + {"Mic 2 Amp Source MUX", "MIC_P", "MIC2"}, + {"Mic 2 Amp Source MUX", "MIC_N", "MIC2"}, + + {"Mic 1 PGA", NULL, "Mic 1 Amp Source MUX"}, + {"Mic 2 PGA", NULL, "Mic 2 Amp Source MUX"}, + + {"Aux Left PGA", NULL, "AUXL"}, + {"Aux Right PGA", NULL, "AUXR"}, + + {"Mixin Left", "Aux Left Switch", "Aux Left PGA"}, + {"Mixin Left", "Mic 1 Switch", "Mic 1 PGA"}, + {"Mixin Left", "Mic 2 Switch", "Mic 2 PGA"}, + {"Mixin Left", "Mixin Right Switch", "Mixin Right PGA"}, + + {"Mixin Right", "Aux Right Switch", "Aux Right PGA"}, + {"Mixin Right", "Mic 2 Switch", "Mic 2 PGA"}, + {"Mixin Right", "Mic 1 Switch", "Mic 1 PGA"}, + {"Mixin Right", "Mixin Left Switch", "Mixin Left PGA"}, + + {"Mixin Left PGA", NULL, "Mixin Left"}, + {"ADC Left", NULL, "Mixin Left PGA"}, + + {"Mixin Right PGA", NULL, "Mixin Right"}, + {"ADC Right", NULL, "Mixin Right PGA"}, + + {"DAI Left Source MUX", "ADC Left", "ADC Left"}, + {"DAI Left Source MUX", "ADC Right", "ADC Right"}, + {"DAI Left Source MUX", "DAI Input Left", "DAIINL"}, + {"DAI Left Source MUX", "DAI Input Right", "DAIINR"}, + + {"DAI Right Source MUX", "ADC Left", "ADC Left"}, + {"DAI Right Source MUX", "ADC Right", "ADC Right"}, + {"DAI Right Source MUX", "DAI Input Left", "DAIINL"}, + {"DAI Right Source MUX", "DAI Input Right", "DAIINR"}, + + {"DAIOUTL", NULL, "DAI Left Source MUX"}, + {"DAIOUTR", NULL, "DAI Right Source MUX"}, + + {"DAIOUTL", NULL, "DAI"}, + {"DAIOUTR", NULL, "DAI"}, + + /* Output path */ + {"DAIINL", NULL, "DAI"}, + {"DAIINR", NULL, "DAI"}, + + {"DAC Left Source MUX", "ADC Output Left", "ADC Left"}, + {"DAC Left Source MUX", "ADC Output Right", "ADC Right"}, + {"DAC Left Source MUX", "DAI Input Left", "DAIINL"}, + {"DAC Left Source MUX", "DAI Input Right", "DAIINR"}, + + {"DAC Right Source MUX", "ADC Output Left", "ADC Left"}, + {"DAC Right Source MUX", "ADC Output Right", "ADC Right"}, + {"DAC Right Source MUX", "DAI Input Left", "DAIINL"}, + {"DAC Right Source MUX", "DAI Input Right", "DAIINR"}, + + {"DAC Left", NULL, "DAC Left Source MUX"}, + {"DAC Right", NULL, "DAC Right Source MUX"}, + + {"Mixout Left", "Aux Left Switch", "Aux Left PGA"}, + {"Mixout Left", "Mixin Left Switch", "Mixin Left PGA"}, + {"Mixout Left", "Mixin Right Switch", "Mixin Right PGA"}, + {"Mixout Left", "DAC Left Switch", "DAC Left"}, + {"Mixout Left", "Aux Left Invert Switch", "Aux Left PGA"}, + {"Mixout Left", "Mixin Left Invert Switch", "Mixin Left PGA"}, + {"Mixout Left", "Mixin Right Invert Switch", "Mixin Right PGA"}, + + {"Mixout Right", "Aux Right Switch", "Aux Right PGA"}, + {"Mixout Right", "Mixin Right Switch", "Mixin Right PGA"}, + {"Mixout Right", "Mixin Left Switch", "Mixin Left PGA"}, + {"Mixout Right", "DAC Right Switch", "DAC Right"}, + {"Mixout Right", "Aux Right Invert Switch", "Aux Right PGA"}, + {"Mixout Right", "Mixin Right Invert Switch", "Mixin Right PGA"}, + {"Mixout Right", "Mixin Left Invert Switch", "Mixin Left PGA"}, + + {"Mixout Left PGA", NULL, "Mixout Left"}, + {"Mixout Right PGA", NULL, "Mixout Right"}, + + {"Headphone Left PGA", NULL, "Mixout Left PGA"}, + {"Headphone Left PGA", NULL, "Charge Pump"}, + {"HPL", NULL, "Headphone Left PGA"}, + + {"Headphone Right PGA", NULL, "Mixout Right PGA"}, + {"Headphone Right PGA", NULL, "Charge Pump"}, + {"HPR", NULL, "Headphone Right PGA"}, + + {"Lineout PGA", NULL, "Mixout Right PGA"}, + {"LINE", NULL, "Lineout PGA"}, +}; + +static const struct reg_default da7213_reg_defaults[] = { + { DA7213_DIG_ROUTING_DAI, 0x10 }, + { DA7213_SR, 0x0A }, + { DA7213_REFERENCES, 0x80 }, + { DA7213_PLL_FRAC_TOP, 0x00 }, + { DA7213_PLL_FRAC_BOT, 0x00 }, + { DA7213_PLL_INTEGER, 0x20 }, + { DA7213_PLL_CTRL, 0x0C }, + { DA7213_DAI_CLK_MODE, 0x01 }, + { DA7213_DAI_CTRL, 0x08 }, + { DA7213_DIG_ROUTING_DAC, 0x32 }, + { DA7213_AUX_L_GAIN, 0x35 }, + { DA7213_AUX_R_GAIN, 0x35 }, + { DA7213_MIXIN_L_SELECT, 0x00 }, + { DA7213_MIXIN_R_SELECT, 0x00 }, + { DA7213_MIXIN_L_GAIN, 0x03 }, + { DA7213_MIXIN_R_GAIN, 0x03 }, + { DA7213_ADC_L_GAIN, 0x6F }, + { DA7213_ADC_R_GAIN, 0x6F }, + { DA7213_ADC_FILTERS1, 0x80 }, + { DA7213_MIC_1_GAIN, 0x01 }, + { DA7213_MIC_2_GAIN, 0x01 }, + { DA7213_DAC_FILTERS5, 0x00 }, + { DA7213_DAC_FILTERS2, 0x88 }, + { DA7213_DAC_FILTERS3, 0x88 }, + { DA7213_DAC_FILTERS4, 0x08 }, + { DA7213_DAC_FILTERS1, 0x80 }, + { DA7213_DAC_L_GAIN, 0x6F }, + { DA7213_DAC_R_GAIN, 0x6F }, + { DA7213_CP_CTRL, 0x61 }, + { DA7213_HP_L_GAIN, 0x39 }, + { DA7213_HP_R_GAIN, 0x39 }, + { DA7213_LINE_GAIN, 0x30 }, + { DA7213_MIXOUT_L_SELECT, 0x00 }, + { DA7213_MIXOUT_R_SELECT, 0x00 }, + { DA7213_SYSTEM_MODES_INPUT, 0x00 }, + { DA7213_SYSTEM_MODES_OUTPUT, 0x00 }, + { DA7213_AUX_L_CTRL, 0x44 }, + { DA7213_AUX_R_CTRL, 0x44 }, + { DA7213_MICBIAS_CTRL, 0x11 }, + { DA7213_MIC_1_CTRL, 0x40 }, + { DA7213_MIC_2_CTRL, 0x40 }, + { DA7213_MIXIN_L_CTRL, 0x40 }, + { DA7213_MIXIN_R_CTRL, 0x40 }, + { DA7213_ADC_L_CTRL, 0x40 }, + { DA7213_ADC_R_CTRL, 0x40 }, + { DA7213_DAC_L_CTRL, 0x48 }, + { DA7213_DAC_R_CTRL, 0x40 }, + { DA7213_HP_L_CTRL, 0x41 }, + { DA7213_HP_R_CTRL, 0x40 }, + { DA7213_LINE_CTRL, 0x40 }, + { DA7213_MIXOUT_L_CTRL, 0x10 }, + { DA7213_MIXOUT_R_CTRL, 0x10 }, + { DA7213_LDO_CTRL, 0x00 }, + { DA7213_IO_CTRL, 0x00 }, + { DA7213_GAIN_RAMP_CTRL, 0x00}, + { DA7213_MIC_CONFIG, 0x00 }, + { DA7213_PC_COUNT, 0x00 }, + { DA7213_CP_VOL_THRESHOLD1, 0x32 }, + { DA7213_CP_DELAY, 0x95 }, + { DA7213_CP_DETECTOR, 0x00 }, + { DA7213_DAI_OFFSET, 0x00 }, + { DA7213_DIG_CTRL, 0x00 }, + { DA7213_ALC_CTRL2, 0x00 }, + { DA7213_ALC_CTRL3, 0x00 }, + { DA7213_ALC_NOISE, 0x3F }, + { DA7213_ALC_TARGET_MIN, 0x3F }, + { DA7213_ALC_TARGET_MAX, 0x00 }, + { DA7213_ALC_GAIN_LIMITS, 0xFF }, + { DA7213_ALC_ANA_GAIN_LIMITS, 0x71 }, + { DA7213_ALC_ANTICLIP_CTRL, 0x00 }, + { DA7213_ALC_ANTICLIP_LEVEL, 0x00 }, + { DA7213_ALC_OFFSET_MAN_M_L, 0x00 }, + { DA7213_ALC_OFFSET_MAN_U_L, 0x00 }, + { DA7213_ALC_OFFSET_MAN_M_R, 0x00 }, + { DA7213_ALC_OFFSET_MAN_U_R, 0x00 }, + { DA7213_ALC_CIC_OP_LVL_CTRL, 0x00 }, + { DA7213_DAC_NG_SETUP_TIME, 0x00 }, + { DA7213_DAC_NG_OFF_THRESHOLD, 0x00 }, + { DA7213_DAC_NG_ON_THRESHOLD, 0x00 }, + { DA7213_DAC_NG_CTRL, 0x00 }, +}; + +static bool da7213_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA7213_STATUS1: + case DA7213_PLL_STATUS: + case DA7213_AUX_L_GAIN_STATUS: + case DA7213_AUX_R_GAIN_STATUS: + case DA7213_MIC_1_GAIN_STATUS: + case DA7213_MIC_2_GAIN_STATUS: + case DA7213_MIXIN_L_GAIN_STATUS: + case DA7213_MIXIN_R_GAIN_STATUS: + case DA7213_ADC_L_GAIN_STATUS: + case DA7213_ADC_R_GAIN_STATUS: + case DA7213_DAC_L_GAIN_STATUS: + case DA7213_DAC_R_GAIN_STATUS: + case DA7213_HP_L_GAIN_STATUS: + case DA7213_HP_R_GAIN_STATUS: + case DA7213_LINE_GAIN_STATUS: + case DA7213_ALC_CTRL1: + case DA7213_ALC_OFFSET_AUTO_M_L: + case DA7213_ALC_OFFSET_AUTO_U_L: + case DA7213_ALC_OFFSET_AUTO_M_R: + case DA7213_ALC_OFFSET_AUTO_U_R: + case DA7213_ALC_CIC_OP_LVL_DATA: + return true; + default: + return false; + } +} + +static int da7213_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + u8 dai_clk_mode = DA7213_DAI_BCLKS_PER_WCLK_64; + u8 dai_ctrl = 0; + u8 fs; + + /* Set channels */ + switch (params_channels(params)) { + case 1: + if (da7213->fmt != DA7213_DAI_FORMAT_DSP) { + dev_err(component->dev, "Mono supported only in DSP mode\n"); + return -EINVAL; + } + dai_ctrl |= DA7213_DAI_MONO_MODE_EN; + break; + case 2: + dai_ctrl &= ~(DA7213_DAI_MONO_MODE_EN); + break; + default: + return -EINVAL; + } + + /* Set DAI format */ + switch (params_width(params)) { + case 16: + dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE; + dai_clk_mode = DA7213_DAI_BCLKS_PER_WCLK_32; /* 32bit for 1ch and 2ch */ + break; + case 20: + dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE; + break; + case 24: + dai_ctrl |= DA7213_DAI_WORD_LENGTH_S24_LE; + break; + case 32: + dai_ctrl |= DA7213_DAI_WORD_LENGTH_S32_LE; + break; + default: + return -EINVAL; + } + + /* Set sampling rate */ + switch (params_rate(params)) { + case 8000: + fs = DA7213_SR_8000; + da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000; + break; + case 11025: + fs = DA7213_SR_11025; + da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800; + break; + case 12000: + fs = DA7213_SR_12000; + da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000; + break; + case 16000: + fs = DA7213_SR_16000; + da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000; + break; + case 22050: + fs = DA7213_SR_22050; + da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800; + break; + case 32000: + fs = DA7213_SR_32000; + da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000; + break; + case 44100: + fs = DA7213_SR_44100; + da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800; + break; + case 48000: + fs = DA7213_SR_48000; + da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000; + break; + case 88200: + fs = DA7213_SR_88200; + da7213->out_rate = DA7213_PLL_FREQ_OUT_90316800; + break; + case 96000: + fs = DA7213_SR_96000; + da7213->out_rate = DA7213_PLL_FREQ_OUT_98304000; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE, + DA7213_DAI_BCLKS_PER_WCLK_MASK, dai_clk_mode); + + snd_soc_component_update_bits(component, DA7213_DAI_CTRL, + DA7213_DAI_WORD_LENGTH_MASK | DA7213_DAI_MONO_MODE_MASK, dai_ctrl); + snd_soc_component_write(component, DA7213_SR, fs); + + return 0; +} + +static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + u8 dai_clk_mode = 0, dai_ctrl = 0; + u8 dai_offset = 0; + + /* Set master/slave mode */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da7213->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + da7213->master = false; + break; + default: + return -EINVAL; + } + + /* Set clock normal/inverted */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + dai_clk_mode |= DA7213_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | + DA7213_DAI_CLK_POL_INV; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAI_FORMAT_DSP_A: + case SND_SOC_DAI_FORMAT_DSP_B: + /* The bclk is inverted wrt ASoC conventions */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + dai_clk_mode |= DA7213_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | + DA7213_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + /* Only I2S is supported */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_ctrl |= DA7213_DAI_FORMAT_I2S_MODE; + da7213->fmt = DA7213_DAI_FORMAT_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_ctrl |= DA7213_DAI_FORMAT_LEFT_J; + da7213->fmt = DA7213_DAI_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dai_ctrl |= DA7213_DAI_FORMAT_RIGHT_J; + da7213->fmt = DA7213_DAI_FORMAT_RIGHT_J; + break; + case SND_SOC_DAI_FORMAT_DSP_A: /* L data MSB after FRM LRC */ + dai_ctrl |= DA7213_DAI_FORMAT_DSP; + dai_offset = 1; + da7213->fmt = DA7213_DAI_FORMAT_DSP; + break; + case SND_SOC_DAI_FORMAT_DSP_B: /* L data MSB during FRM LRC */ + dai_ctrl |= DA7213_DAI_FORMAT_DSP; + da7213->fmt = DA7213_DAI_FORMAT_DSP; + break; + default: + return -EINVAL; + } + + /* By default only 64 BCLK per WCLK is supported */ + dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_64; + + snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE, + DA7213_DAI_BCLKS_PER_WCLK_MASK | + DA7213_DAI_CLK_POL_MASK | DA7213_DAI_WCLK_POL_MASK, + dai_clk_mode); + snd_soc_component_update_bits(component, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK, + dai_ctrl); + snd_soc_component_write(component, DA7213_DAI_OFFSET, dai_offset); + + return 0; +} + +static int da7213_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + if (mute) { + snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL, + DA7213_MUTE_EN, DA7213_MUTE_EN); + snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL, + DA7213_MUTE_EN, DA7213_MUTE_EN); + } else { + snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL, + DA7213_MUTE_EN, 0); + snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL, + DA7213_MUTE_EN, 0); + } + + return 0; +} + +#define DA7213_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static int da7213_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, + unsigned int freq, int dir) +{ + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + int ret = 0; + + if ((da7213->clk_src == clk_id) && (da7213->mclk_rate == freq)) + return 0; + + if (((freq < 5000000) && (freq != 32768)) || (freq > 54000000)) { + dev_err(component->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + + switch (clk_id) { + case DA7213_CLKSRC_MCLK: + snd_soc_component_update_bits(component, DA7213_PLL_CTRL, + DA7213_PLL_MCLK_SQR_EN, 0); + break; + case DA7213_CLKSRC_MCLK_SQR: + snd_soc_component_update_bits(component, DA7213_PLL_CTRL, + DA7213_PLL_MCLK_SQR_EN, + DA7213_PLL_MCLK_SQR_EN); + break; + default: + dev_err(component->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } + + da7213->clk_src = clk_id; + + if (da7213->mclk) { + freq = clk_round_rate(da7213->mclk, freq); + ret = clk_set_rate(da7213->mclk, freq); + if (ret) { + dev_err(component->dev, "Failed to set clock rate %d\n", + freq); + return ret; + } + } + + da7213->mclk_rate = freq; + + return 0; +} + +/* Supported PLL input frequencies are 32KHz, 5MHz - 54MHz. */ +static int _da7213_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, + unsigned int fref, unsigned int fout) +{ + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + + u8 pll_ctrl, indiv_bits, indiv; + u8 pll_frac_top, pll_frac_bot, pll_integer; + u32 freq_ref; + u64 frac_div; + + /* Workout input divider based on MCLK rate */ + if (da7213->mclk_rate == 32768) { + if (!da7213->master) { + dev_err(component->dev, + "32KHz only valid if codec is clock master\n"); + return -EINVAL; + } + + /* 32KHz PLL Mode */ + indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL; + source = DA7213_SYSCLK_PLL_32KHZ; + freq_ref = 3750000; + + } else { + if (da7213->mclk_rate < 5000000) { + dev_err(component->dev, + "PLL input clock %d below valid range\n", + da7213->mclk_rate); + return -EINVAL; + } else if (da7213->mclk_rate <= 9000000) { + indiv_bits = DA7213_PLL_INDIV_5_TO_9_MHZ; + indiv = DA7213_PLL_INDIV_5_TO_9_MHZ_VAL; + } else if (da7213->mclk_rate <= 18000000) { + indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL; + } else if (da7213->mclk_rate <= 36000000) { + indiv_bits = DA7213_PLL_INDIV_18_TO_36_MHZ; + indiv = DA7213_PLL_INDIV_18_TO_36_MHZ_VAL; + } else if (da7213->mclk_rate <= 54000000) { + indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ; + indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL; + } else { + dev_err(component->dev, + "PLL input clock %d above valid range\n", + da7213->mclk_rate); + return -EINVAL; + } + freq_ref = (da7213->mclk_rate / indiv); + } + + pll_ctrl = indiv_bits; + + /* Configure PLL */ + switch (source) { + case DA7213_SYSCLK_MCLK: + snd_soc_component_update_bits(component, DA7213_PLL_CTRL, + DA7213_PLL_INDIV_MASK | + DA7213_PLL_MODE_MASK, pll_ctrl); + return 0; + case DA7213_SYSCLK_PLL: + break; + case DA7213_SYSCLK_PLL_SRM: + pll_ctrl |= DA7213_PLL_SRM_EN; + fout = DA7213_PLL_FREQ_OUT_94310400; + break; + case DA7213_SYSCLK_PLL_32KHZ: + if (da7213->mclk_rate != 32768) { + dev_err(component->dev, + "32KHz mode only valid with 32KHz MCLK\n"); + return -EINVAL; + } + + pll_ctrl |= DA7213_PLL_32K_MODE | DA7213_PLL_SRM_EN; + fout = DA7213_PLL_FREQ_OUT_94310400; + break; + default: + dev_err(component->dev, "Invalid PLL config\n"); + return -EINVAL; + } + + /* Calculate dividers for PLL */ + pll_integer = fout / freq_ref; + frac_div = (u64)(fout % freq_ref) * 8192ULL; + do_div(frac_div, freq_ref); + pll_frac_top = (frac_div >> DA7213_BYTE_SHIFT) & DA7213_BYTE_MASK; + pll_frac_bot = (frac_div) & DA7213_BYTE_MASK; + + /* Write PLL dividers */ + snd_soc_component_write(component, DA7213_PLL_FRAC_TOP, pll_frac_top); + snd_soc_component_write(component, DA7213_PLL_FRAC_BOT, pll_frac_bot); + snd_soc_component_write(component, DA7213_PLL_INTEGER, pll_integer); + + /* Enable PLL */ + pll_ctrl |= DA7213_PLL_EN; + snd_soc_component_update_bits(component, DA7213_PLL_CTRL, + DA7213_PLL_INDIV_MASK | DA7213_PLL_MODE_MASK, + pll_ctrl); + + /* Assist 32KHz mode PLL lock */ + if (source == DA7213_SYSCLK_PLL_32KHZ) { + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0xF1, 0x03); + snd_soc_component_write(component, 0xF1, 0x01); + snd_soc_component_write(component, 0xF0, 0x00); + } + + return 0; +} + +static int da7213_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, + unsigned int fref, unsigned int fout) +{ + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + da7213->fixed_clk_auto_pll = false; + + return _da7213_set_component_pll(component, pll_id, source, fref, fout); +} + +/* DAI operations */ +static const struct snd_soc_dai_ops da7213_dai_ops = { + .hw_params = da7213_hw_params, + .set_fmt = da7213_set_dai_fmt, + .mute_stream = da7213_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver da7213_dai = { + .name = "da7213-hifi", + /* Playback Capabilities */ + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7213_FORMATS, + }, + /* Capture Capabilities */ + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7213_FORMATS, + }, + .ops = &da7213_dai_ops, + .symmetric_rate = 1, +}; + +static int da7213_set_auto_pll(struct snd_soc_component *component, bool enable) +{ + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + int mode; + + if (!da7213->fixed_clk_auto_pll) + return 0; + + da7213->mclk_rate = clk_get_rate(da7213->mclk); + + if (enable) { + /* Slave mode needs SRM for non-harmonic frequencies */ + if (da7213->master) + mode = DA7213_SYSCLK_PLL; + else + mode = DA7213_SYSCLK_PLL_SRM; + + /* PLL is not required for harmonic frequencies */ + switch (da7213->out_rate) { + case DA7213_PLL_FREQ_OUT_90316800: + if (da7213->mclk_rate == 11289600 || + da7213->mclk_rate == 22579200 || + da7213->mclk_rate == 45158400) + mode = DA7213_SYSCLK_MCLK; + break; + case DA7213_PLL_FREQ_OUT_98304000: + if (da7213->mclk_rate == 12288000 || + da7213->mclk_rate == 24576000 || + da7213->mclk_rate == 49152000) + mode = DA7213_SYSCLK_MCLK; + + break; + default: + return -1; + } + } else { + /* Disable PLL in standby */ + mode = DA7213_SYSCLK_MCLK; + } + + return _da7213_set_component_pll(component, 0, mode, + da7213->mclk_rate, da7213->out_rate); +} + +static int da7213_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + /* Enable MCLK for transition to ON state */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) { + if (da7213->mclk) { + ret = clk_prepare_enable(da7213->mclk); + if (ret) { + dev_err(component->dev, + "Failed to enable mclk\n"); + return ret; + } + + da7213_set_auto_pll(component, true); + } + } + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Enable VMID reference & master bias */ + snd_soc_component_update_bits(component, DA7213_REFERENCES, + DA7213_VMID_EN | DA7213_BIAS_EN, + DA7213_VMID_EN | DA7213_BIAS_EN); + } else { + /* Remove MCLK */ + if (da7213->mclk) { + da7213_set_auto_pll(component, false); + clk_disable_unprepare(da7213->mclk); + } + } + break; + case SND_SOC_BIAS_OFF: + /* Disable VMID reference & master bias */ + snd_soc_component_update_bits(component, DA7213_REFERENCES, + DA7213_VMID_EN | DA7213_BIAS_EN, 0); + break; + } + return 0; +} + +#if defined(CONFIG_OF) +/* DT */ +static const struct of_device_id da7213_of_match[] = { + { .compatible = "dlg,da7212", }, + { .compatible = "dlg,da7213", }, + { } +}; +MODULE_DEVICE_TABLE(of, da7213_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id da7213_acpi_match[] = { + { "DLGS7212", 0}, + { "DLGS7213", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, da7213_acpi_match); +#endif + +static enum da7213_micbias_voltage + da7213_of_micbias_lvl(struct snd_soc_component *component, u32 val) +{ + switch (val) { + case 1600: + return DA7213_MICBIAS_1_6V; + case 2200: + return DA7213_MICBIAS_2_2V; + case 2500: + return DA7213_MICBIAS_2_5V; + case 3000: + return DA7213_MICBIAS_3_0V; + default: + dev_warn(component->dev, "Invalid micbias level\n"); + return DA7213_MICBIAS_2_2V; + } +} + +static enum da7213_dmic_data_sel + da7213_of_dmic_data_sel(struct snd_soc_component *component, const char *str) +{ + if (!strcmp(str, "lrise_rfall")) { + return DA7213_DMIC_DATA_LRISE_RFALL; + } else if (!strcmp(str, "lfall_rrise")) { + return DA7213_DMIC_DATA_LFALL_RRISE; + } else { + dev_warn(component->dev, "Invalid DMIC data select type\n"); + return DA7213_DMIC_DATA_LRISE_RFALL; + } +} + +static enum da7213_dmic_samplephase + da7213_of_dmic_samplephase(struct snd_soc_component *component, const char *str) +{ + if (!strcmp(str, "on_clkedge")) { + return DA7213_DMIC_SAMPLE_ON_CLKEDGE; + } else if (!strcmp(str, "between_clkedge")) { + return DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE; + } else { + dev_warn(component->dev, "Invalid DMIC sample phase\n"); + return DA7213_DMIC_SAMPLE_ON_CLKEDGE; + } +} + +static enum da7213_dmic_clk_rate + da7213_of_dmic_clkrate(struct snd_soc_component *component, u32 val) +{ + switch (val) { + case 1500000: + return DA7213_DMIC_CLK_1_5MHZ; + case 3000000: + return DA7213_DMIC_CLK_3_0MHZ; + default: + dev_warn(component->dev, "Invalid DMIC clock rate\n"); + return DA7213_DMIC_CLK_1_5MHZ; + } +} + +static struct da7213_platform_data + *da7213_fw_to_pdata(struct snd_soc_component *component) +{ + struct device *dev = component->dev; + struct da7213_platform_data *pdata; + const char *fw_str; + u32 fw_val32; + + pdata = devm_kzalloc(component->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (device_property_read_u32(dev, "dlg,micbias1-lvl", &fw_val32) >= 0) + pdata->micbias1_lvl = da7213_of_micbias_lvl(component, fw_val32); + else + pdata->micbias1_lvl = DA7213_MICBIAS_2_2V; + + if (device_property_read_u32(dev, "dlg,micbias2-lvl", &fw_val32) >= 0) + pdata->micbias2_lvl = da7213_of_micbias_lvl(component, fw_val32); + else + pdata->micbias2_lvl = DA7213_MICBIAS_2_2V; + + if (!device_property_read_string(dev, "dlg,dmic-data-sel", &fw_str)) + pdata->dmic_data_sel = da7213_of_dmic_data_sel(component, fw_str); + else + pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL; + + if (!device_property_read_string(dev, "dlg,dmic-samplephase", &fw_str)) + pdata->dmic_samplephase = + da7213_of_dmic_samplephase(component, fw_str); + else + pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE; + + if (device_property_read_u32(dev, "dlg,dmic-clkrate", &fw_val32) >= 0) + pdata->dmic_clk_rate = da7213_of_dmic_clkrate(component, fw_val32); + else + pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ; + + return pdata; +} + +static int da7213_probe(struct snd_soc_component *component) +{ + struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component); + + pm_runtime_get_sync(component->dev); + + /* Default to using ALC auto offset calibration mode. */ + snd_soc_component_update_bits(component, DA7213_ALC_CTRL1, + DA7213_ALC_CALIB_MODE_MAN, 0); + da7213->alc_calib_auto = true; + + /* Default PC counter to free-running */ + snd_soc_component_update_bits(component, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK, + DA7213_PC_FREERUN_MASK); + + /* Enable all Gain Ramps */ + snd_soc_component_update_bits(component, DA7213_AUX_L_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_component_update_bits(component, DA7213_AUX_R_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_component_update_bits(component, DA7213_MIXIN_L_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_component_update_bits(component, DA7213_MIXIN_R_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_component_update_bits(component, DA7213_ADC_L_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_component_update_bits(component, DA7213_ADC_R_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_component_update_bits(component, DA7213_HP_L_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_component_update_bits(component, DA7213_HP_R_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + snd_soc_component_update_bits(component, DA7213_LINE_CTRL, + DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN); + + /* + * There are two separate control bits for input and output mixers as + * well as headphone and line outs. + * One to enable corresponding amplifier and other to enable its + * output. As amplifier bits are related to power control, they are + * being managed by DAPM while other (non power related) bits are + * enabled here + */ + snd_soc_component_update_bits(component, DA7213_MIXIN_L_CTRL, + DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN); + snd_soc_component_update_bits(component, DA7213_MIXIN_R_CTRL, + DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN); + + snd_soc_component_update_bits(component, DA7213_MIXOUT_L_CTRL, + DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN); + snd_soc_component_update_bits(component, DA7213_MIXOUT_R_CTRL, + DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN); + + snd_soc_component_update_bits(component, DA7213_HP_L_CTRL, + DA7213_HP_AMP_OE, DA7213_HP_AMP_OE); + snd_soc_component_update_bits(component, DA7213_HP_R_CTRL, + DA7213_HP_AMP_OE, DA7213_HP_AMP_OE); + + snd_soc_component_update_bits(component, DA7213_LINE_CTRL, + DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE); + + /* Handle DT/Platform data */ + da7213->pdata = dev_get_platdata(component->dev); + if (!da7213->pdata) + da7213->pdata = da7213_fw_to_pdata(component); + + /* Set platform data values */ + if (da7213->pdata) { + struct da7213_platform_data *pdata = da7213->pdata; + u8 micbias_lvl = 0, dmic_cfg = 0; + + /* Set Mic Bias voltages */ + switch (pdata->micbias1_lvl) { + case DA7213_MICBIAS_1_6V: + case DA7213_MICBIAS_2_2V: + case DA7213_MICBIAS_2_5V: + case DA7213_MICBIAS_3_0V: + micbias_lvl |= (pdata->micbias1_lvl << + DA7213_MICBIAS1_LEVEL_SHIFT); + break; + } + switch (pdata->micbias2_lvl) { + case DA7213_MICBIAS_1_6V: + case DA7213_MICBIAS_2_2V: + case DA7213_MICBIAS_2_5V: + case DA7213_MICBIAS_3_0V: + micbias_lvl |= (pdata->micbias2_lvl << + DA7213_MICBIAS2_LEVEL_SHIFT); + break; + } + snd_soc_component_update_bits(component, DA7213_MICBIAS_CTRL, + DA7213_MICBIAS1_LEVEL_MASK | + DA7213_MICBIAS2_LEVEL_MASK, micbias_lvl); + + /* Set DMIC configuration */ + switch (pdata->dmic_data_sel) { + case DA7213_DMIC_DATA_LFALL_RRISE: + case DA7213_DMIC_DATA_LRISE_RFALL: + dmic_cfg |= (pdata->dmic_data_sel << + DA7213_DMIC_DATA_SEL_SHIFT); + break; + } + switch (pdata->dmic_samplephase) { + case DA7213_DMIC_SAMPLE_ON_CLKEDGE: + case DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE: + dmic_cfg |= (pdata->dmic_samplephase << + DA7213_DMIC_SAMPLEPHASE_SHIFT); + break; + } + switch (pdata->dmic_clk_rate) { + case DA7213_DMIC_CLK_3_0MHZ: + case DA7213_DMIC_CLK_1_5MHZ: + dmic_cfg |= (pdata->dmic_clk_rate << + DA7213_DMIC_CLK_RATE_SHIFT); + break; + } + snd_soc_component_update_bits(component, DA7213_MIC_CONFIG, + DA7213_DMIC_DATA_SEL_MASK | + DA7213_DMIC_SAMPLEPHASE_MASK | + DA7213_DMIC_CLK_RATE_MASK, dmic_cfg); + } + + pm_runtime_put_sync(component->dev); + + /* Check if MCLK provided */ + da7213->mclk = devm_clk_get(component->dev, "mclk"); + if (IS_ERR(da7213->mclk)) { + if (PTR_ERR(da7213->mclk) != -ENOENT) + return PTR_ERR(da7213->mclk); + else + da7213->mclk = NULL; + } else { + /* Do automatic PLL handling assuming fixed clock until + * set_pll() has been called. This makes the codec usable + * with the simple-audio-card driver. */ + da7213->fixed_clk_auto_pll = true; + } + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_da7213 = { + .probe = da7213_probe, + .set_bias_level = da7213_set_bias_level, + .controls = da7213_snd_controls, + .num_controls = ARRAY_SIZE(da7213_snd_controls), + .dapm_widgets = da7213_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7213_dapm_widgets), + .dapm_routes = da7213_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7213_audio_map), + .set_sysclk = da7213_set_component_sysclk, + .set_pll = da7213_set_component_pll, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config da7213_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = da7213_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7213_reg_defaults), + .volatile_reg = da7213_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static void da7213_power_off(void *data) +{ + struct da7213_priv *da7213 = data; + regulator_bulk_disable(DA7213_NUM_SUPPLIES, da7213->supplies); +} + +static const char *da7213_supply_names[DA7213_NUM_SUPPLIES] = { + [DA7213_SUPPLY_VDDA] = "VDDA", + [DA7213_SUPPLY_VDDIO] = "VDDIO", +}; + +static int da7213_i2c_probe(struct i2c_client *i2c) +{ + struct da7213_priv *da7213; + int i, ret; + + da7213 = devm_kzalloc(&i2c->dev, sizeof(*da7213), GFP_KERNEL); + if (!da7213) + return -ENOMEM; + + i2c_set_clientdata(i2c, da7213); + + /* Get required supplies */ + for (i = 0; i < DA7213_NUM_SUPPLIES; ++i) + da7213->supplies[i].supply = da7213_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, DA7213_NUM_SUPPLIES, + da7213->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to get supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(DA7213_NUM_SUPPLIES, da7213->supplies); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&i2c->dev, da7213_power_off, da7213); + if (ret < 0) + return ret; + + da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config); + if (IS_ERR(da7213->regmap)) { + ret = PTR_ERR(da7213->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + pm_runtime_set_autosuspend_delay(&i2c->dev, 100); + pm_runtime_use_autosuspend(&i2c->dev); + pm_runtime_set_active(&i2c->dev); + pm_runtime_enable(&i2c->dev); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_da7213, &da7213_dai, 1); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register da7213 component: %d\n", + ret); + } + return ret; +} + +static void da7213_i2c_remove(struct i2c_client *i2c) +{ + pm_runtime_disable(&i2c->dev); +} + +static int __maybe_unused da7213_runtime_suspend(struct device *dev) +{ + struct da7213_priv *da7213 = dev_get_drvdata(dev); + + regcache_cache_only(da7213->regmap, true); + regcache_mark_dirty(da7213->regmap); + regulator_bulk_disable(DA7213_NUM_SUPPLIES, da7213->supplies); + + return 0; +} + +static int __maybe_unused da7213_runtime_resume(struct device *dev) +{ + struct da7213_priv *da7213 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(DA7213_NUM_SUPPLIES, da7213->supplies); + if (ret < 0) + return ret; + regcache_cache_only(da7213->regmap, false); + regcache_sync(da7213->regmap); + return 0; +} + +static const struct dev_pm_ops da7213_pm = { + SET_RUNTIME_PM_OPS(da7213_runtime_suspend, da7213_runtime_resume, NULL) +}; + +static const struct i2c_device_id da7213_i2c_id[] = { + { "da7213", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da7213_i2c_id); + +/* I2C codec control layer */ +static struct i2c_driver da7213_i2c_driver = { + .driver = { + .name = "da7213", + .of_match_table = of_match_ptr(da7213_of_match), + .acpi_match_table = ACPI_PTR(da7213_acpi_match), + .pm = &da7213_pm, + }, + .probe = da7213_i2c_probe, + .remove = da7213_i2c_remove, + .id_table = da7213_i2c_id, +}; + +module_i2c_driver(da7213_i2c_driver); + +MODULE_DESCRIPTION("ASoC DA7213 Codec driver"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h new file mode 100644 index 0000000000..4ca9cfdea0 --- /dev/null +++ b/sound/soc/codecs/da7213.h @@ -0,0 +1,550 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * da7213.h - DA7213 ASoC Codec Driver + * + * Copyright (c) 2013 Dialog Semiconductor + * + * Author: Adam Thomson + */ + +#ifndef _DA7213_H +#define _DA7213_H + +#include +#include +#include +#include + +/* + * Registers + */ + +/* Status Registers */ +#define DA7213_STATUS1 0x02 +#define DA7213_PLL_STATUS 0x03 +#define DA7213_AUX_L_GAIN_STATUS 0x04 +#define DA7213_AUX_R_GAIN_STATUS 0x05 +#define DA7213_MIC_1_GAIN_STATUS 0x06 +#define DA7213_MIC_2_GAIN_STATUS 0x07 +#define DA7213_MIXIN_L_GAIN_STATUS 0x08 +#define DA7213_MIXIN_R_GAIN_STATUS 0x09 +#define DA7213_ADC_L_GAIN_STATUS 0x0A +#define DA7213_ADC_R_GAIN_STATUS 0x0B +#define DA7213_DAC_L_GAIN_STATUS 0x0C +#define DA7213_DAC_R_GAIN_STATUS 0x0D +#define DA7213_HP_L_GAIN_STATUS 0x0E +#define DA7213_HP_R_GAIN_STATUS 0x0F +#define DA7213_LINE_GAIN_STATUS 0x10 + +/* System Initialisation Registers */ +#define DA7213_DIG_ROUTING_DAI 0x21 +#define DA7213_SR 0x22 +#define DA7213_REFERENCES 0x23 +#define DA7213_PLL_FRAC_TOP 0x24 +#define DA7213_PLL_FRAC_BOT 0x25 +#define DA7213_PLL_INTEGER 0x26 +#define DA7213_PLL_CTRL 0x27 +#define DA7213_DAI_CLK_MODE 0x28 +#define DA7213_DAI_CTRL 0x29 +#define DA7213_DIG_ROUTING_DAC 0x2A +#define DA7213_ALC_CTRL1 0x2B + +/* Input - Gain, Select and Filter Registers */ +#define DA7213_AUX_L_GAIN 0x30 +#define DA7213_AUX_R_GAIN 0x31 +#define DA7213_MIXIN_L_SELECT 0x32 +#define DA7213_MIXIN_R_SELECT 0x33 +#define DA7213_MIXIN_L_GAIN 0x34 +#define DA7213_MIXIN_R_GAIN 0x35 +#define DA7213_ADC_L_GAIN 0x36 +#define DA7213_ADC_R_GAIN 0x37 +#define DA7213_ADC_FILTERS1 0x38 +#define DA7213_MIC_1_GAIN 0x39 +#define DA7213_MIC_2_GAIN 0x3A + +/* Output - Gain, Select and Filter Registers */ +#define DA7213_DAC_FILTERS5 0x40 +#define DA7213_DAC_FILTERS2 0x41 +#define DA7213_DAC_FILTERS3 0x42 +#define DA7213_DAC_FILTERS4 0x43 +#define DA7213_DAC_FILTERS1 0x44 +#define DA7213_DAC_L_GAIN 0x45 +#define DA7213_DAC_R_GAIN 0x46 +#define DA7213_CP_CTRL 0x47 +#define DA7213_HP_L_GAIN 0x48 +#define DA7213_HP_R_GAIN 0x49 +#define DA7213_LINE_GAIN 0x4A +#define DA7213_MIXOUT_L_SELECT 0x4B +#define DA7213_MIXOUT_R_SELECT 0x4C + +/* System Controller Registers */ +#define DA7213_SYSTEM_MODES_INPUT 0x50 +#define DA7213_SYSTEM_MODES_OUTPUT 0x51 + +/* Control Registers */ +#define DA7213_AUX_L_CTRL 0x60 +#define DA7213_AUX_R_CTRL 0x61 +#define DA7213_MICBIAS_CTRL 0x62 +#define DA7213_MIC_1_CTRL 0x63 +#define DA7213_MIC_2_CTRL 0x64 +#define DA7213_MIXIN_L_CTRL 0x65 +#define DA7213_MIXIN_R_CTRL 0x66 +#define DA7213_ADC_L_CTRL 0x67 +#define DA7213_ADC_R_CTRL 0x68 +#define DA7213_DAC_L_CTRL 0x69 +#define DA7213_DAC_R_CTRL 0x6A +#define DA7213_HP_L_CTRL 0x6B +#define DA7213_HP_R_CTRL 0x6C +#define DA7213_LINE_CTRL 0x6D +#define DA7213_MIXOUT_L_CTRL 0x6E +#define DA7213_MIXOUT_R_CTRL 0x6F + +/* Configuration Registers */ +#define DA7213_LDO_CTRL 0x90 +#define DA7213_IO_CTRL 0x91 +#define DA7213_GAIN_RAMP_CTRL 0x92 +#define DA7213_MIC_CONFIG 0x93 +#define DA7213_PC_COUNT 0x94 +#define DA7213_CP_VOL_THRESHOLD1 0x95 +#define DA7213_CP_DELAY 0x96 +#define DA7213_CP_DETECTOR 0x97 +#define DA7213_DAI_OFFSET 0x98 +#define DA7213_DIG_CTRL 0x99 +#define DA7213_ALC_CTRL2 0x9A +#define DA7213_ALC_CTRL3 0x9B +#define DA7213_ALC_NOISE 0x9C +#define DA7213_ALC_TARGET_MIN 0x9D +#define DA7213_ALC_TARGET_MAX 0x9E +#define DA7213_ALC_GAIN_LIMITS 0x9F +#define DA7213_ALC_ANA_GAIN_LIMITS 0xA0 +#define DA7213_ALC_ANTICLIP_CTRL 0xA1 +#define DA7213_ALC_ANTICLIP_LEVEL 0xA2 + +#define DA7213_ALC_OFFSET_AUTO_M_L 0xA3 +#define DA7213_ALC_OFFSET_AUTO_U_L 0xA4 +#define DA7213_ALC_OFFSET_MAN_M_L 0xA6 +#define DA7213_ALC_OFFSET_MAN_U_L 0xA7 +#define DA7213_ALC_OFFSET_AUTO_M_R 0xA8 +#define DA7213_ALC_OFFSET_AUTO_U_R 0xA9 +#define DA7213_ALC_OFFSET_MAN_M_R 0xAB +#define DA7213_ALC_OFFSET_MAN_U_R 0xAC +#define DA7213_ALC_CIC_OP_LVL_CTRL 0xAD +#define DA7213_ALC_CIC_OP_LVL_DATA 0xAE +#define DA7213_DAC_NG_SETUP_TIME 0xAF +#define DA7213_DAC_NG_OFF_THRESHOLD 0xB0 +#define DA7213_DAC_NG_ON_THRESHOLD 0xB1 +#define DA7213_DAC_NG_CTRL 0xB2 + + +/* + * Bit fields + */ + +/* DA7213_PLL_STATUS = 0x03 */ +#define DA7219_PLL_SRM_LOCK (0x1 << 1) + +/* DA7213_SR = 0x22 */ +#define DA7213_SR_8000 (0x1 << 0) +#define DA7213_SR_11025 (0x2 << 0) +#define DA7213_SR_12000 (0x3 << 0) +#define DA7213_SR_16000 (0x5 << 0) +#define DA7213_SR_22050 (0x6 << 0) +#define DA7213_SR_24000 (0x7 << 0) +#define DA7213_SR_32000 (0x9 << 0) +#define DA7213_SR_44100 (0xA << 0) +#define DA7213_SR_48000 (0xB << 0) +#define DA7213_SR_88200 (0xE << 0) +#define DA7213_SR_96000 (0xF << 0) + +/* DA7213_REFERENCES = 0x23 */ +#define DA7213_BIAS_EN (0x1 << 3) +#define DA7213_VMID_EN (0x1 << 7) + +/* DA7213_PLL_CTRL = 0x27 */ +#define DA7213_PLL_INDIV_5_TO_9_MHZ (0x0 << 2) +#define DA7213_PLL_INDIV_9_TO_18_MHZ (0x1 << 2) +#define DA7213_PLL_INDIV_18_TO_36_MHZ (0x2 << 2) +#define DA7213_PLL_INDIV_36_TO_54_MHZ (0x3 << 2) +#define DA7213_PLL_INDIV_MASK (0x3 << 2) +#define DA7213_PLL_MCLK_SQR_EN (0x1 << 4) +#define DA7213_PLL_32K_MODE (0x1 << 5) +#define DA7213_PLL_SRM_EN (0x1 << 6) +#define DA7213_PLL_EN (0x1 << 7) +#define DA7213_PLL_MODE_MASK (0x7 << 5) + +/* DA7213_DAI_CLK_MODE = 0x28 */ +#define DA7213_DAI_BCLKS_PER_WCLK_32 (0x0 << 0) +#define DA7213_DAI_BCLKS_PER_WCLK_64 (0x1 << 0) +#define DA7213_DAI_BCLKS_PER_WCLK_128 (0x2 << 0) +#define DA7213_DAI_BCLKS_PER_WCLK_256 (0x3 << 0) +#define DA7213_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) +#define DA7213_DAI_CLK_POL_INV (0x1 << 2) +#define DA7213_DAI_CLK_POL_MASK (0x1 << 2) +#define DA7213_DAI_WCLK_POL_INV (0x1 << 3) +#define DA7213_DAI_WCLK_POL_MASK (0x1 << 3) +#define DA7213_DAI_CLK_EN_MASK (0x1 << 7) + +/* DA7213_DAI_CTRL = 0x29 */ +#define DA7213_DAI_FORMAT_I2S_MODE (0x0 << 0) +#define DA7213_DAI_FORMAT_LEFT_J (0x1 << 0) +#define DA7213_DAI_FORMAT_RIGHT_J (0x2 << 0) +#define DA7213_DAI_FORMAT_DSP (0x3 << 0) +#define DA7213_DAI_FORMAT_MASK (0x3 << 0) +#define DA7213_DAI_WORD_LENGTH_S16_LE (0x0 << 2) +#define DA7213_DAI_WORD_LENGTH_S20_LE (0x1 << 2) +#define DA7213_DAI_WORD_LENGTH_S24_LE (0x2 << 2) +#define DA7213_DAI_WORD_LENGTH_S32_LE (0x3 << 2) +#define DA7213_DAI_WORD_LENGTH_MASK (0x3 << 2) +#define DA7213_DAI_MONO_MODE_EN (0x1 << 4) +#define DA7213_DAI_MONO_MODE_MASK (0x1 << 4) +#define DA7213_DAI_EN_SHIFT 7 + +/* DA7213_DIG_ROUTING_DAI = 0x21 */ +#define DA7213_DAI_L_SRC_SHIFT 0 +#define DA7213_DAI_R_SRC_SHIFT 4 +#define DA7213_DAI_SRC_MAX 4 + +/* DA7213_DIG_ROUTING_DAC = 0x2A */ +#define DA7213_DAC_L_SRC_SHIFT 0 +#define DA7213_DAC_L_MONO_SHIFT 3 +#define DA7213_DAC_R_SRC_SHIFT 4 +#define DA7213_DAC_R_MONO_SHIFT 7 +#define DA7213_DAC_SRC_MAX 4 +#define DA7213_DAC_MONO_MAX 0x1 + +/* DA7213_ALC_CTRL1 = 0x2B */ +#define DA7213_ALC_OFFSET_EN_SHIFT 0 +#define DA7213_ALC_OFFSET_EN_MAX 0x1 +#define DA7213_ALC_OFFSET_EN (0x1 << 0) +#define DA7213_ALC_SYNC_MODE (0x1 << 1) +#define DA7213_ALC_CALIB_MODE_MAN (0x1 << 2) +#define DA7213_ALC_L_EN_SHIFT 3 +#define DA7213_ALC_AUTO_CALIB_EN (0x1 << 4) +#define DA7213_ALC_CALIB_OVERFLOW (0x1 << 5) +#define DA7213_ALC_R_EN_SHIFT 7 +#define DA7213_ALC_EN_MAX 0x1 + +/* DA7213_AUX_L/R_GAIN = 0x30/0x31 */ +#define DA7213_AUX_AMP_GAIN_SHIFT 0 +#define DA7213_AUX_AMP_GAIN_MAX 0x3F + +/* DA7213_MIXIN_L/R_SELECT = 0x32/0x33 */ +#define DA7213_DMIC_EN_SHIFT 7 +#define DA7213_DMIC_EN_MAX 0x1 + +/* DA7213_MIXIN_L_SELECT = 0x32 */ +#define DA7213_MIXIN_L_MIX_SELECT_AUX_L_SHIFT 0 +#define DA7213_MIXIN_L_MIX_SELECT_MIC_1_SHIFT 1 +#define DA7213_MIXIN_L_MIX_SELECT_MIC_1 (0x1 << 1) +#define DA7213_MIXIN_L_MIX_SELECT_MIC_2_SHIFT 2 +#define DA7213_MIXIN_L_MIX_SELECT_MIC_2 (0x1 << 2) +#define DA7213_MIXIN_L_MIX_SELECT_MIXIN_R_SHIFT 3 +#define DA7213_MIXIN_L_MIX_SELECT_MAX 0x1 + +/* DA7213_MIXIN_R_SELECT = 0x33 */ +#define DA7213_MIXIN_R_MIX_SELECT_AUX_R_SHIFT 0 +#define DA7213_MIXIN_R_MIX_SELECT_MIC_2_SHIFT 1 +#define DA7213_MIXIN_R_MIX_SELECT_MIC_2 (0x1 << 1) +#define DA7213_MIXIN_R_MIX_SELECT_MIC_1_SHIFT 2 +#define DA7213_MIXIN_R_MIX_SELECT_MIC_1 (0x1 << 2) +#define DA7213_MIXIN_R_MIX_SELECT_MIXIN_L_SHIFT 3 +#define DA7213_MIXIN_R_MIX_SELECT_MAX 0x1 +#define DA7213_MIC_BIAS_OUTPUT_SELECT_2 (0x1 << 6) + +/* DA7213_MIXIN_L/R_GAIN = 0x34/0x35 */ +#define DA7213_MIXIN_AMP_GAIN_SHIFT 0 +#define DA7213_MIXIN_AMP_GAIN_MAX 0xF + +/* DA7213_ADC_L/R_GAIN = 0x36/0x37 */ +#define DA7213_ADC_AMP_GAIN_SHIFT 0 +#define DA7213_ADC_AMP_GAIN_MAX 0x7F + +/* DA7213_ADC/DAC_FILTERS1 = 0x38/0x44 */ +#define DA7213_VOICE_HPF_CORNER_SHIFT 0 +#define DA7213_VOICE_HPF_CORNER_MAX 8 +#define DA7213_VOICE_EN_SHIFT 3 +#define DA7213_VOICE_EN_MAX 0x1 +#define DA7213_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7213_AUDIO_HPF_CORNER_MAX 4 +#define DA7213_HPF_EN_SHIFT 7 +#define DA7213_HPF_EN_MAX 0x1 + +/* DA7213_MIC_1/2_GAIN = 0x39/0x3A */ +#define DA7213_MIC_AMP_GAIN_SHIFT 0 +#define DA7213_MIC_AMP_GAIN_MAX 0x7 + +/* DA7213_DAC_FILTERS5 = 0x40 */ +#define DA7213_DAC_SOFTMUTE_EN_SHIFT 7 +#define DA7213_DAC_SOFTMUTE_EN_MAX 0x1 +#define DA7213_DAC_SOFTMUTE_RATE_SHIFT 4 +#define DA7213_DAC_SOFTMUTE_RATE_MAX 7 + +/* DA7213_DAC_FILTERS2/3/4 = 0x41/0x42/0x43 */ +#define DA7213_DAC_EQ_BAND_MAX 0xF + +/* DA7213_DAC_FILTERS2 = 0x41 */ +#define DA7213_DAC_EQ_BAND1_SHIFT 0 +#define DA7213_DAC_EQ_BAND2_SHIFT 4 + +/* DA7213_DAC_FILTERS2 = 0x42 */ +#define DA7213_DAC_EQ_BAND3_SHIFT 0 +#define DA7213_DAC_EQ_BAND4_SHIFT 4 + +/* DA7213_DAC_FILTERS4 = 0x43 */ +#define DA7213_DAC_EQ_BAND5_SHIFT 0 +#define DA7213_DAC_EQ_EN_SHIFT 7 +#define DA7213_DAC_EQ_EN_MAX 0x1 + +/* DA7213_DAC_L/R_GAIN = 0x45/0x46 */ +#define DA7213_DAC_AMP_GAIN_SHIFT 0 +#define DA7213_DAC_AMP_GAIN_MAX 0x7F + +/* DA7213_HP_L/R_GAIN = 0x45/0x46 */ +#define DA7213_HP_AMP_GAIN_SHIFT 0 +#define DA7213_HP_AMP_GAIN_MAX 0x3F + +/* DA7213_CP_CTRL = 0x47 */ +#define DA7213_CP_EN_SHIFT 7 + +/* DA7213_LINE_GAIN = 0x4A */ +#define DA7213_LINE_AMP_GAIN_SHIFT 0 +#define DA7213_LINE_AMP_GAIN_MAX 0x3F + +/* DA7213_MIXOUT_L_SELECT = 0x4B */ +#define DA7213_MIXOUT_L_MIX_SELECT_AUX_L_SHIFT 0 +#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_SHIFT 1 +#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_SHIFT 2 +#define DA7213_MIXOUT_L_MIX_SELECT_DAC_L_SHIFT 3 +#define DA7213_MIXOUT_L_MIX_SELECT_AUX_L_INVERTED_SHIFT 4 +#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_L_INVERTED_SHIFT 5 +#define DA7213_MIXOUT_L_MIX_SELECT_MIXIN_R_INVERTED_SHIFT 6 +#define DA7213_MIXOUT_L_MIX_SELECT_MAX 0x1 + +/* DA7213_MIXOUT_R_SELECT = 0x4C */ +#define DA7213_MIXOUT_R_MIX_SELECT_AUX_R_SHIFT 0 +#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_SHIFT 1 +#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_SHIFT 2 +#define DA7213_MIXOUT_R_MIX_SELECT_DAC_R_SHIFT 3 +#define DA7213_MIXOUT_R_MIX_SELECT_AUX_R_INVERTED_SHIFT 4 +#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_R_INVERTED_SHIFT 5 +#define DA7213_MIXOUT_R_MIX_SELECT_MIXIN_L_INVERTED_SHIFT 6 +#define DA7213_MIXOUT_R_MIX_SELECT_MAX 0x1 + +/* + * DA7213_AUX_L/R_CTRL = 0x60/0x61, + * DA7213_MIC_1/2_CTRL = 0x63/0x64, + * DA7213_MIXIN_L/R_CTRL = 0x65/0x66, + * DA7213_ADC_L/R_CTRL = 0x65/0x66, + * DA7213_DAC_L/R_CTRL = 0x69/0x6A, + * DA7213_HP_L/R_CTRL = 0x6B/0x6C, + * DA7213_LINE_CTRL = 0x6D + */ +#define DA7213_MUTE_EN_SHIFT 6 +#define DA7213_MUTE_EN_MAX 0x1 +#define DA7213_MUTE_EN (0x1 << 6) + +/* + * DA7213_AUX_L/R_CTRL = 0x60/0x61, + * DA7213_MIXIN_L/R_CTRL = 0x65/0x66, + * DA7213_ADC_L/R_CTRL = 0x65/0x66, + * DA7213_DAC_L/R_CTRL = 0x69/0x6A, + * DA7213_HP_L/R_CTRL = 0x6B/0x6C, + * DA7213_LINE_CTRL = 0x6D + */ +#define DA7213_GAIN_RAMP_EN_SHIFT 5 +#define DA7213_GAIN_RAMP_EN_MAX 0x1 +#define DA7213_GAIN_RAMP_EN (0x1 << 5) + +/* + * DA7213_AUX_L/R_CTRL = 0x60/0x61, + * DA7213_MIXIN_L/R_CTRL = 0x65/0x66, + * DA7213_HP_L/R_CTRL = 0x6B/0x6C, + * DA7213_LINE_CTRL = 0x6D + */ +#define DA7213_ZC_EN_SHIFT 4 +#define DA7213_ZC_EN_MAX 0x1 + +/* + * DA7213_AUX_L/R_CTRL = 0x60/0x61, + * DA7213_MIC_1/2_CTRL = 0x63/0x64, + * DA7213_MIXIN_L/R_CTRL = 0x65/0x66, + * DA7213_HP_L/R_CTRL = 0x6B/0x6C, + * DA7213_MIXOUT_L/R_CTRL = 0x6E/0x6F, + * DA7213_LINE_CTRL = 0x6D + */ +#define DA7213_AMP_EN_SHIFT 7 + +/* DA7213_MIC_1/2_CTRL = 0x63/0x64 */ +#define DA7213_MIC_AMP_IN_SEL_SHIFT 2 +#define DA7213_MIC_AMP_IN_SEL_MAX 3 + +/* DA7213_MICBIAS_CTRL = 0x62 */ +#define DA7213_MICBIAS1_LEVEL_SHIFT 0 +#define DA7213_MICBIAS1_LEVEL_MASK (0x3 << 0) +#define DA7213_MICBIAS1_EN_SHIFT 3 +#define DA7213_MICBIAS2_LEVEL_SHIFT 4 +#define DA7213_MICBIAS2_LEVEL_MASK (0x3 << 4) +#define DA7213_MICBIAS2_EN_SHIFT 7 + +/* DA7213_MIXIN_L/R_CTRL = 0x65/0x66 */ +#define DA7213_MIXIN_MIX_EN (0x1 << 3) + +/* DA7213_ADC_L/R_CTRL = 0x67/0x68 */ +#define DA7213_ADC_EN_SHIFT 7 +#define DA7213_ADC_EN (0x1 << 7) + +/* DA7213_DAC_L/R_CTRL = 0x69/0x6A*/ +#define DA7213_DAC_EN_SHIFT 7 + +/* DA7213_HP_L/R_CTRL = 0x6B/0x6C */ +#define DA7213_HP_AMP_OE (0x1 << 3) + +/* DA7213_LINE_CTRL = 0x6D */ +#define DA7213_LINE_AMP_OE (0x1 << 3) + +/* DA7213_MIXOUT_L/R_CTRL = 0x6E/0x6F */ +#define DA7213_MIXOUT_MIX_EN (0x1 << 3) + +/* DA7213_GAIN_RAMP_CTRL = 0x92 */ +#define DA7213_GAIN_RAMP_RATE_SHIFT 0 +#define DA7213_GAIN_RAMP_RATE_MAX 4 + +/* DA7213_MIC_CONFIG = 0x93 */ +#define DA7213_DMIC_DATA_SEL_SHIFT 0 +#define DA7213_DMIC_DATA_SEL_MASK (0x1 << 0) +#define DA7213_DMIC_SAMPLEPHASE_SHIFT 1 +#define DA7213_DMIC_SAMPLEPHASE_MASK (0x1 << 1) +#define DA7213_DMIC_CLK_RATE_SHIFT 2 +#define DA7213_DMIC_CLK_RATE_MASK (0x1 << 2) + +/* DA7213_PC_COUNT = 0x94 */ +#define DA7213_PC_FREERUN_MASK (0x1 << 0) + +/* DA7213_DIG_CTRL = 0x99 */ +#define DA7213_DAC_L_INV_SHIFT 3 +#define DA7213_DAC_R_INV_SHIFT 7 +#define DA7213_DAC_INV_MAX 0x1 + +/* DA7213_ALC_CTRL2 = 0x9A */ +#define DA7213_ALC_ATTACK_SHIFT 0 +#define DA7213_ALC_ATTACK_MAX 13 +#define DA7213_ALC_RELEASE_SHIFT 4 +#define DA7213_ALC_RELEASE_MAX 11 + +/* DA7213_ALC_CTRL3 = 0x9B */ +#define DA7213_ALC_HOLD_SHIFT 0 +#define DA7213_ALC_HOLD_MAX 16 +#define DA7213_ALC_INTEG_ATTACK_SHIFT 4 +#define DA7213_ALC_INTEG_RELEASE_SHIFT 6 +#define DA7213_ALC_INTEG_MAX 4 + +/* + * DA7213_ALC_NOISE = 0x9C, + * DA7213_ALC_TARGET_MIN/MAX = 0x9D/0x9E + */ +#define DA7213_ALC_THRESHOLD_SHIFT 0 +#define DA7213_ALC_THRESHOLD_MAX 0x3F + +/* DA7213_ALC_GAIN_LIMITS = 0x9F */ +#define DA7213_ALC_ATTEN_MAX_SHIFT 0 +#define DA7213_ALC_GAIN_MAX_SHIFT 4 +#define DA7213_ALC_ATTEN_GAIN_MAX_MAX 0xF + +/* DA7213_ALC_ANA_GAIN_LIMITS = 0xA0 */ +#define DA7213_ALC_ANA_GAIN_MIN_SHIFT 0 +#define DA7213_ALC_ANA_GAIN_MAX_SHIFT 4 +#define DA7213_ALC_ANA_GAIN_MAX 0x7 + +/* DA7213_ALC_ANTICLIP_CTRL = 0xA1 */ +#define DA7213_ALC_ANTICLIP_EN_SHIFT 7 +#define DA7213_ALC_ANTICLIP_EN_MAX 0x1 + +/* DA7213_ALC_ANTICLIP_LEVEL = 0xA2 */ +#define DA7213_ALC_ANTICLIP_LEVEL_SHIFT 0 +#define DA7213_ALC_ANTICLIP_LEVEL_MAX 0x7F + +/* DA7213_ALC_CIC_OP_LVL_CTRL = 0xAD */ +#define DA7213_ALC_DATA_MIDDLE (0x2 << 0) +#define DA7213_ALC_DATA_TOP (0x3 << 0) +#define DA7213_ALC_CIC_OP_CHANNEL_LEFT (0x0 << 7) +#define DA7213_ALC_CIC_OP_CHANNEL_RIGHT (0x1 << 7) + +/* DA7213_DAC_NG_SETUP_TIME = 0xAF */ +#define DA7213_DAC_NG_SETUP_TIME_SHIFT 0 +#define DA7213_DAC_NG_SETUP_TIME_MAX 4 +#define DA7213_DAC_NG_RAMPUP_RATE_SHIFT 2 +#define DA7213_DAC_NG_RAMPDN_RATE_SHIFT 3 +#define DA7213_DAC_NG_RAMP_RATE_MAX 2 + +/* DA7213_DAC_NG_OFF/ON_THRESH = 0xB0/0xB1 */ +#define DA7213_DAC_NG_THRESHOLD_SHIFT 0 +#define DA7213_DAC_NG_THRESHOLD_MAX 0x7 + +/* DA7213_DAC_NG_CTRL = 0xB2 */ +#define DA7213_DAC_NG_EN_SHIFT 7 +#define DA7213_DAC_NG_EN_MAX 0x1 + + +/* + * General defines + */ + +/* Register inversion */ +#define DA7213_NO_INVERT 0 +#define DA7213_INVERT 1 + +/* Byte related defines */ +#define DA7213_BYTE_SHIFT 8 +#define DA7213_BYTE_MASK 0xFF + +/* ALC related */ +#define DA7213_ALC_OFFSET_15_8 0x00FF00 +#define DA7213_ALC_OFFSET_19_16 0x0F0000 +#define DA7213_ALC_AVG_ITERATIONS 5 + +/* PLL related */ +#define DA7213_PLL_FREQ_OUT_90316800 90316800 +#define DA7213_PLL_FREQ_OUT_98304000 98304000 +#define DA7213_PLL_FREQ_OUT_94310400 94310400 +#define DA7213_PLL_INDIV_5_TO_9_MHZ_VAL 2 +#define DA7213_PLL_INDIV_9_TO_18_MHZ_VAL 4 +#define DA7213_PLL_INDIV_18_TO_36_MHZ_VAL 8 +#define DA7213_PLL_INDIV_36_TO_54_MHZ_VAL 16 +#define DA7213_SRM_CHECK_RETRIES 8 + +enum da7213_clk_src { + DA7213_CLKSRC_MCLK = 0, + DA7213_CLKSRC_MCLK_SQR, +}; + +enum da7213_sys_clk { + DA7213_SYSCLK_MCLK = 0, + DA7213_SYSCLK_PLL, + DA7213_SYSCLK_PLL_SRM, + DA7213_SYSCLK_PLL_32KHZ +}; + +/* Regulators */ +enum da7213_supplies { + DA7213_SUPPLY_VDDA = 0, + DA7213_SUPPLY_VDDIO, + DA7213_NUM_SUPPLIES, +}; + +/* Codec private data */ +struct da7213_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[DA7213_NUM_SUPPLIES]; + struct clk *mclk; + unsigned int mclk_rate; + unsigned int out_rate; + int clk_src; + bool master; + bool alc_calib_auto; + bool alc_en; + bool fixed_clk_auto_pll; + struct da7213_platform_data *pdata; + int fmt; +}; + +#endif /* _DA7213_H */ diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c new file mode 100644 index 0000000000..3f456b08b8 --- /dev/null +++ b/sound/soc/codecs/da7218.c @@ -0,0 +1,3328 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * da7218.c - DA7218 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "da7218.h" + + +/* + * TLVs and Enums + */ + +/* Input TLVs */ +static const DECLARE_TLV_DB_SCALE(da7218_mic_gain_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_in_dig_gain_tlv, -8325, 75, 0); +static const DECLARE_TLV_DB_SCALE(da7218_ags_trigger_tlv, -9000, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_ags_att_max_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_alc_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_alc_ana_gain_tlv, 0, 600, 0); + +/* Input/Output TLVs */ +static const DECLARE_TLV_DB_SCALE(da7218_dmix_gain_tlv, -4200, 150, 0); + +/* Output TLVs */ +static const DECLARE_TLV_DB_SCALE(da7218_dgs_trigger_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_dgs_anticlip_tlv, -4200, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_dgs_signal_tlv, -9000, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_out_eq_band_tlv, -1050, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7218_out_dig_gain_tlv, -8325, 75, 0); +static const DECLARE_TLV_DB_SCALE(da7218_dac_ng_threshold_tlv, -10200, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7218_mixout_gain_tlv, -100, 50, 0); +static const DECLARE_TLV_DB_SCALE(da7218_hp_gain_tlv, -5700, 150, 0); + +/* Input Enums */ +static const char * const da7218_alc_attack_rate_txt[] = { + "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs", + "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs", + "30024/fs", +}; + +static const struct soc_enum da7218_alc_attack_rate = + SOC_ENUM_SINGLE(DA7218_ALC_CTRL2, DA7218_ALC_ATTACK_SHIFT, + DA7218_ALC_ATTACK_MAX, da7218_alc_attack_rate_txt); + +static const char * const da7218_alc_release_rate_txt[] = { + "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs", + "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs", +}; + +static const struct soc_enum da7218_alc_release_rate = + SOC_ENUM_SINGLE(DA7218_ALC_CTRL2, DA7218_ALC_RELEASE_SHIFT, + DA7218_ALC_RELEASE_MAX, da7218_alc_release_rate_txt); + +static const char * const da7218_alc_hold_time_txt[] = { + "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", + "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", + "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static const struct soc_enum da7218_alc_hold_time = + SOC_ENUM_SINGLE(DA7218_ALC_CTRL3, DA7218_ALC_HOLD_SHIFT, + DA7218_ALC_HOLD_MAX, da7218_alc_hold_time_txt); + +static const char * const da7218_alc_anticlip_step_txt[] = { + "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs", +}; + +static const struct soc_enum da7218_alc_anticlip_step = + SOC_ENUM_SINGLE(DA7218_ALC_ANTICLIP_CTRL, + DA7218_ALC_ANTICLIP_STEP_SHIFT, + DA7218_ALC_ANTICLIP_STEP_MAX, + da7218_alc_anticlip_step_txt); + +static const char * const da7218_integ_rate_txt[] = { + "1/4", "1/16", "1/256", "1/65536" +}; + +static const struct soc_enum da7218_integ_attack_rate = + SOC_ENUM_SINGLE(DA7218_ENV_TRACK_CTRL, DA7218_INTEG_ATTACK_SHIFT, + DA7218_INTEG_MAX, da7218_integ_rate_txt); + +static const struct soc_enum da7218_integ_release_rate = + SOC_ENUM_SINGLE(DA7218_ENV_TRACK_CTRL, DA7218_INTEG_RELEASE_SHIFT, + DA7218_INTEG_MAX, da7218_integ_rate_txt); + +/* Input/Output Enums */ +static const char * const da7218_gain_ramp_rate_txt[] = { + "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8", + "Nominal Rate / 16", +}; + +static const struct soc_enum da7218_gain_ramp_rate = + SOC_ENUM_SINGLE(DA7218_GAIN_RAMP_CTRL, DA7218_GAIN_RAMP_RATE_SHIFT, + DA7218_GAIN_RAMP_RATE_MAX, da7218_gain_ramp_rate_txt); + +static const char * const da7218_hpf_mode_txt[] = { + "Disabled", "Audio", "Voice", +}; + +static const unsigned int da7218_hpf_mode_val[] = { + DA7218_HPF_DISABLED, DA7218_HPF_AUDIO_EN, DA7218_HPF_VOICE_EN, +}; + +static const struct soc_enum da7218_in1_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK, + DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt, + da7218_hpf_mode_val); + +static const struct soc_enum da7218_in2_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK, + DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt, + da7218_hpf_mode_val); + +static const struct soc_enum da7218_out1_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL, + DA7218_HPF_MODE_SHIFT, DA7218_HPF_MODE_MASK, + DA7218_HPF_MODE_MAX, da7218_hpf_mode_txt, + da7218_hpf_mode_val); + +static const char * const da7218_audio_hpf_corner_txt[] = { + "2Hz", "4Hz", "8Hz", "16Hz", +}; + +static const struct soc_enum da7218_in1_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_IN_1_AUDIO_HPF_CORNER_SHIFT, + DA7218_AUDIO_HPF_CORNER_MAX, + da7218_audio_hpf_corner_txt); + +static const struct soc_enum da7218_in2_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_IN_2_AUDIO_HPF_CORNER_SHIFT, + DA7218_AUDIO_HPF_CORNER_MAX, + da7218_audio_hpf_corner_txt); + +static const struct soc_enum da7218_out1_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL, + DA7218_OUT_1_AUDIO_HPF_CORNER_SHIFT, + DA7218_AUDIO_HPF_CORNER_MAX, + da7218_audio_hpf_corner_txt); + +static const char * const da7218_voice_hpf_corner_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz", +}; + +static const struct soc_enum da7218_in1_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_IN_1_VOICE_HPF_CORNER_SHIFT, + DA7218_VOICE_HPF_CORNER_MAX, + da7218_voice_hpf_corner_txt); + +static const struct soc_enum da7218_in2_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_IN_2_VOICE_HPF_CORNER_SHIFT, + DA7218_VOICE_HPF_CORNER_MAX, + da7218_voice_hpf_corner_txt); + +static const struct soc_enum da7218_out1_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7218_OUT_1_HPF_FILTER_CTRL, + DA7218_OUT_1_VOICE_HPF_CORNER_SHIFT, + DA7218_VOICE_HPF_CORNER_MAX, + da7218_voice_hpf_corner_txt); + +static const char * const da7218_tonegen_dtmf_key_txt[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", + "*", "#" +}; + +static const struct soc_enum da7218_tonegen_dtmf_key = + SOC_ENUM_SINGLE(DA7218_TONE_GEN_CFG1, DA7218_DTMF_REG_SHIFT, + DA7218_DTMF_REG_MAX, da7218_tonegen_dtmf_key_txt); + +static const char * const da7218_tonegen_swg_sel_txt[] = { + "Sum", "SWG1", "SWG2", "SWG1_1-Cos" +}; + +static const struct soc_enum da7218_tonegen_swg_sel = + SOC_ENUM_SINGLE(DA7218_TONE_GEN_CFG2, DA7218_SWG_SEL_SHIFT, + DA7218_SWG_SEL_MAX, da7218_tonegen_swg_sel_txt); + +/* Output Enums */ +static const char * const da7218_dgs_rise_coeff_txt[] = { + "1/1", "1/16", "1/64", "1/256", "1/1024", "1/4096", "1/16384", +}; + +static const struct soc_enum da7218_dgs_rise_coeff = + SOC_ENUM_SINGLE(DA7218_DGS_RISE_FALL, DA7218_DGS_RISE_COEFF_SHIFT, + DA7218_DGS_RISE_COEFF_MAX, da7218_dgs_rise_coeff_txt); + +static const char * const da7218_dgs_fall_coeff_txt[] = { + "1/4", "1/16", "1/64", "1/256", "1/1024", "1/4096", "1/16384", "1/65536", +}; + +static const struct soc_enum da7218_dgs_fall_coeff = + SOC_ENUM_SINGLE(DA7218_DGS_RISE_FALL, DA7218_DGS_FALL_COEFF_SHIFT, + DA7218_DGS_FALL_COEFF_MAX, da7218_dgs_fall_coeff_txt); + +static const char * const da7218_dac_ng_setup_time_txt[] = { + "256 Samples", "512 Samples", "1024 Samples", "2048 Samples" +}; + +static const struct soc_enum da7218_dac_ng_setup_time = + SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME, + DA7218_DAC_NG_SETUP_TIME_SHIFT, + DA7218_DAC_NG_SETUP_TIME_MAX, + da7218_dac_ng_setup_time_txt); + +static const char * const da7218_dac_ng_rampup_txt[] = { + "0.22ms/dB", "0.0138ms/dB" +}; + +static const struct soc_enum da7218_dac_ng_rampup_rate = + SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME, + DA7218_DAC_NG_RAMPUP_RATE_SHIFT, + DA7218_DAC_NG_RAMPUP_RATE_MAX, + da7218_dac_ng_rampup_txt); + +static const char * const da7218_dac_ng_rampdown_txt[] = { + "0.88ms/dB", "14.08ms/dB" +}; + +static const struct soc_enum da7218_dac_ng_rampdown_rate = + SOC_ENUM_SINGLE(DA7218_DAC_NG_SETUP_TIME, + DA7218_DAC_NG_RAMPDN_RATE_SHIFT, + DA7218_DAC_NG_RAMPDN_RATE_MAX, + da7218_dac_ng_rampdown_txt); + +static const char * const da7218_cp_mchange_txt[] = { + "Largest Volume", "DAC Volume", "Signal Magnitude" +}; + +static const unsigned int da7218_cp_mchange_val[] = { + DA7218_CP_MCHANGE_LARGEST_VOL, DA7218_CP_MCHANGE_DAC_VOL, + DA7218_CP_MCHANGE_SIG_MAG +}; + +static const struct soc_enum da7218_cp_mchange = + SOC_VALUE_ENUM_SINGLE(DA7218_CP_CTRL, DA7218_CP_MCHANGE_SHIFT, + DA7218_CP_MCHANGE_REL_MASK, DA7218_CP_MCHANGE_MAX, + da7218_cp_mchange_txt, da7218_cp_mchange_val); + +static const char * const da7218_cp_fcontrol_txt[] = { + "1MHz", "500KHz", "250KHz", "125KHz", "63KHz", "0KHz" +}; + +static const struct soc_enum da7218_cp_fcontrol = + SOC_ENUM_SINGLE(DA7218_CP_DELAY, DA7218_CP_FCONTROL_SHIFT, + DA7218_CP_FCONTROL_MAX, da7218_cp_fcontrol_txt); + +static const char * const da7218_cp_tau_delay_txt[] = { + "0ms", "2ms", "4ms", "16ms", "64ms", "128ms", "256ms", "512ms" +}; + +static const struct soc_enum da7218_cp_tau_delay = + SOC_ENUM_SINGLE(DA7218_CP_DELAY, DA7218_CP_TAU_DELAY_SHIFT, + DA7218_CP_TAU_DELAY_MAX, da7218_cp_tau_delay_txt); + +/* + * Control Functions + */ + +/* ALC */ +static void da7218_alc_calib(struct snd_soc_component *component) +{ + u8 mic_1_ctrl, mic_2_ctrl; + u8 mixin_1_ctrl, mixin_2_ctrl; + u8 in_1l_filt_ctrl, in_1r_filt_ctrl, in_2l_filt_ctrl, in_2r_filt_ctrl; + u8 in_1_hpf_ctrl, in_2_hpf_ctrl; + u8 calib_ctrl; + int i = 0; + bool calibrated = false; + + /* Save current state of MIC control registers */ + mic_1_ctrl = snd_soc_component_read(component, DA7218_MIC_1_CTRL); + mic_2_ctrl = snd_soc_component_read(component, DA7218_MIC_2_CTRL); + + /* Save current state of input mixer control registers */ + mixin_1_ctrl = snd_soc_component_read(component, DA7218_MIXIN_1_CTRL); + mixin_2_ctrl = snd_soc_component_read(component, DA7218_MIXIN_2_CTRL); + + /* Save current state of input filter control registers */ + in_1l_filt_ctrl = snd_soc_component_read(component, DA7218_IN_1L_FILTER_CTRL); + in_1r_filt_ctrl = snd_soc_component_read(component, DA7218_IN_1R_FILTER_CTRL); + in_2l_filt_ctrl = snd_soc_component_read(component, DA7218_IN_2L_FILTER_CTRL); + in_2r_filt_ctrl = snd_soc_component_read(component, DA7218_IN_2R_FILTER_CTRL); + + /* Save current state of input HPF control registers */ + in_1_hpf_ctrl = snd_soc_component_read(component, DA7218_IN_1_HPF_FILTER_CTRL); + in_2_hpf_ctrl = snd_soc_component_read(component, DA7218_IN_2_HPF_FILTER_CTRL); + + /* Enable then Mute MIC PGAs */ + snd_soc_component_update_bits(component, DA7218_MIC_1_CTRL, DA7218_MIC_1_AMP_EN_MASK, + DA7218_MIC_1_AMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_MIC_2_CTRL, DA7218_MIC_2_AMP_EN_MASK, + DA7218_MIC_2_AMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_MIC_1_CTRL, + DA7218_MIC_1_AMP_MUTE_EN_MASK, + DA7218_MIC_1_AMP_MUTE_EN_MASK); + snd_soc_component_update_bits(component, DA7218_MIC_2_CTRL, + DA7218_MIC_2_AMP_MUTE_EN_MASK, + DA7218_MIC_2_AMP_MUTE_EN_MASK); + + /* Enable input mixers unmuted */ + snd_soc_component_update_bits(component, DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_EN_MASK | + DA7218_MIXIN_1_AMP_MUTE_EN_MASK, + DA7218_MIXIN_1_AMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_EN_MASK | + DA7218_MIXIN_2_AMP_MUTE_EN_MASK, + DA7218_MIXIN_2_AMP_EN_MASK); + + /* Enable input filters unmuted */ + snd_soc_component_update_bits(component, DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_FILTER_EN_MASK | + DA7218_IN_1L_MUTE_EN_MASK, + DA7218_IN_1L_FILTER_EN_MASK); + snd_soc_component_update_bits(component, DA7218_IN_1R_FILTER_CTRL, + DA7218_IN_1R_FILTER_EN_MASK | + DA7218_IN_1R_MUTE_EN_MASK, + DA7218_IN_1R_FILTER_EN_MASK); + snd_soc_component_update_bits(component, DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_FILTER_EN_MASK | + DA7218_IN_2L_MUTE_EN_MASK, + DA7218_IN_2L_FILTER_EN_MASK); + snd_soc_component_update_bits(component, DA7218_IN_2R_FILTER_CTRL, + DA7218_IN_2R_FILTER_EN_MASK | + DA7218_IN_2R_MUTE_EN_MASK, + DA7218_IN_2R_FILTER_EN_MASK); + + /* + * Make sure input HPFs voice mode is disabled, otherwise for sampling + * rates above 32KHz the ADC signals will be stopped and will cause + * calibration to lock up. + */ + snd_soc_component_update_bits(component, DA7218_IN_1_HPF_FILTER_CTRL, + DA7218_IN_1_VOICE_EN_MASK, 0); + snd_soc_component_update_bits(component, DA7218_IN_2_HPF_FILTER_CTRL, + DA7218_IN_2_VOICE_EN_MASK, 0); + + /* Perform auto calibration */ + snd_soc_component_update_bits(component, DA7218_CALIB_CTRL, DA7218_CALIB_AUTO_EN_MASK, + DA7218_CALIB_AUTO_EN_MASK); + do { + calib_ctrl = snd_soc_component_read(component, DA7218_CALIB_CTRL); + if (calib_ctrl & DA7218_CALIB_AUTO_EN_MASK) { + ++i; + usleep_range(DA7218_ALC_CALIB_DELAY_MIN, + DA7218_ALC_CALIB_DELAY_MAX); + } else { + calibrated = true; + } + + } while ((i < DA7218_ALC_CALIB_MAX_TRIES) && (!calibrated)); + + /* If auto calibration fails, disable DC offset, hybrid ALC */ + if ((!calibrated) || (calib_ctrl & DA7218_CALIB_OVERFLOW_MASK)) { + dev_warn(component->dev, + "ALC auto calibration failed - %s\n", + (calibrated) ? "overflow" : "timeout"); + snd_soc_component_update_bits(component, DA7218_CALIB_CTRL, + DA7218_CALIB_OFFSET_EN_MASK, 0); + snd_soc_component_update_bits(component, DA7218_ALC_CTRL1, + DA7218_ALC_SYNC_MODE_MASK, 0); + + } else { + /* Enable DC offset cancellation */ + snd_soc_component_update_bits(component, DA7218_CALIB_CTRL, + DA7218_CALIB_OFFSET_EN_MASK, + DA7218_CALIB_OFFSET_EN_MASK); + + /* Enable ALC hybrid mode */ + snd_soc_component_update_bits(component, DA7218_ALC_CTRL1, + DA7218_ALC_SYNC_MODE_MASK, + DA7218_ALC_SYNC_MODE_CH1 | + DA7218_ALC_SYNC_MODE_CH2); + } + + /* Restore input HPF control registers to original states */ + snd_soc_component_write(component, DA7218_IN_1_HPF_FILTER_CTRL, in_1_hpf_ctrl); + snd_soc_component_write(component, DA7218_IN_2_HPF_FILTER_CTRL, in_2_hpf_ctrl); + + /* Restore input filter control registers to original states */ + snd_soc_component_write(component, DA7218_IN_1L_FILTER_CTRL, in_1l_filt_ctrl); + snd_soc_component_write(component, DA7218_IN_1R_FILTER_CTRL, in_1r_filt_ctrl); + snd_soc_component_write(component, DA7218_IN_2L_FILTER_CTRL, in_2l_filt_ctrl); + snd_soc_component_write(component, DA7218_IN_2R_FILTER_CTRL, in_2r_filt_ctrl); + + /* Restore input mixer control registers to original state */ + snd_soc_component_write(component, DA7218_MIXIN_1_CTRL, mixin_1_ctrl); + snd_soc_component_write(component, DA7218_MIXIN_2_CTRL, mixin_2_ctrl); + + /* Restore MIC control registers to original states */ + snd_soc_component_write(component, DA7218_MIC_1_CTRL, mic_1_ctrl); + snd_soc_component_write(component, DA7218_MIC_2_CTRL, mic_2_ctrl); +} + +static int da7218_mixin_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + /* + * If ALC in operation and value of control has been updated, + * make sure calibrated offsets are updated. + */ + if ((ret == 1) && (da7218->alc_en)) + da7218_alc_calib(component); + + return ret; +} + +static int da7218_alc_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *) kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + unsigned int lvalue = ucontrol->value.integer.value[0]; + unsigned int rvalue = ucontrol->value.integer.value[1]; + unsigned int lshift = mc->shift; + unsigned int rshift = mc->rshift; + unsigned int mask = (mc->max << lshift) | (mc->max << rshift); + + /* Force ALC offset calibration if enabling ALC */ + if ((lvalue || rvalue) && (!da7218->alc_en)) + da7218_alc_calib(component); + + /* Update bits to detail which channels are enabled/disabled */ + da7218->alc_en &= ~mask; + da7218->alc_en |= (lvalue << lshift) | (rvalue << rshift); + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +/* ToneGen */ +static int da7218_tonegen_freq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + int ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to host endianness here. + */ + ret = regmap_raw_read(da7218->regmap, reg, &val, 2); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = le16_to_cpu(val); + + return 0; +} + +static int da7218_tonegen_freq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + u16 val; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to little endian here to align with + * HW registers. + */ + val = cpu_to_le16(ucontrol->value.integer.value[0]); + + return regmap_raw_write(da7218->regmap, reg, &val, 2); +} + +static int da7218_mic_lvl_det_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int lvalue = ucontrol->value.integer.value[0]; + unsigned int rvalue = ucontrol->value.integer.value[1]; + unsigned int lshift = mixer_ctrl->shift; + unsigned int rshift = mixer_ctrl->rshift; + unsigned int mask = (mixer_ctrl->max << lshift) | + (mixer_ctrl->max << rshift); + da7218->mic_lvl_det_en &= ~mask; + da7218->mic_lvl_det_en |= (lvalue << lshift) | (rvalue << rshift); + + /* + * Here we only enable the feature on paths which are already + * powered. If a channel is enabled here for level detect, but that path + * isn't powered, then the channel will actually be enabled when we do + * power the path (IN_FILTER widget events). This handling avoids + * unwanted level detect events. + */ + return snd_soc_component_write(component, mixer_ctrl->reg, + (da7218->in_filt_en & da7218->mic_lvl_det_en)); +} + +static int da7218_mic_lvl_det_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int lshift = mixer_ctrl->shift; + unsigned int rshift = mixer_ctrl->rshift; + unsigned int lmask = (mixer_ctrl->max << lshift); + unsigned int rmask = (mixer_ctrl->max << rshift); + + ucontrol->value.integer.value[0] = + (da7218->mic_lvl_det_en & lmask) >> lshift; + ucontrol->value.integer.value[1] = + (da7218->mic_lvl_det_en & rmask) >> rshift; + + return 0; +} + +static int da7218_biquad_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + + /* Determine which BiQuads we're setting based on size of config data */ + switch (bytes_ext->max) { + case DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE: + memcpy(ucontrol->value.bytes.data, da7218->biq_5stage_coeff, + bytes_ext->max); + break; + case DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE: + memcpy(ucontrol->value.bytes.data, da7218->stbiq_3stage_coeff, + bytes_ext->max); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int da7218_biquad_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *) kcontrol->private_value; + u8 reg, out_filt1l; + u8 cfg[DA7218_BIQ_CFG_SIZE]; + int i; + + /* + * Determine which BiQuads we're setting based on size of config data, + * and stored the data for use by get function. + */ + switch (bytes_ext->max) { + case DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE: + reg = DA7218_OUT_1_BIQ_5STAGE_DATA; + memcpy(da7218->biq_5stage_coeff, ucontrol->value.bytes.data, + bytes_ext->max); + break; + case DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE: + reg = DA7218_SIDETONE_BIQ_3STAGE_DATA; + memcpy(da7218->stbiq_3stage_coeff, ucontrol->value.bytes.data, + bytes_ext->max); + break; + default: + return -EINVAL; + } + + /* Make sure at least out filter1 enabled to allow programming */ + out_filt1l = snd_soc_component_read(component, DA7218_OUT_1L_FILTER_CTRL); + snd_soc_component_write(component, DA7218_OUT_1L_FILTER_CTRL, + out_filt1l | DA7218_OUT_1L_FILTER_EN_MASK); + + for (i = 0; i < bytes_ext->max; ++i) { + cfg[DA7218_BIQ_CFG_DATA] = ucontrol->value.bytes.data[i]; + cfg[DA7218_BIQ_CFG_ADDR] = i; + regmap_raw_write(da7218->regmap, reg, cfg, DA7218_BIQ_CFG_SIZE); + } + + /* Restore filter to previous setting */ + snd_soc_component_write(component, DA7218_OUT_1L_FILTER_CTRL, out_filt1l); + + return 0; +} + + +/* + * KControls + */ + +static const struct snd_kcontrol_new da7218_snd_controls[] = { + /* Mics */ + SOC_SINGLE_TLV("Mic1 Volume", DA7218_MIC_1_GAIN, + DA7218_MIC_1_AMP_GAIN_SHIFT, DA7218_MIC_AMP_GAIN_MAX, + DA7218_NO_INVERT, da7218_mic_gain_tlv), + SOC_SINGLE("Mic1 Switch", DA7218_MIC_1_CTRL, + DA7218_MIC_1_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE_TLV("Mic2 Volume", DA7218_MIC_2_GAIN, + DA7218_MIC_2_AMP_GAIN_SHIFT, DA7218_MIC_AMP_GAIN_MAX, + DA7218_NO_INVERT, da7218_mic_gain_tlv), + SOC_SINGLE("Mic2 Switch", DA7218_MIC_2_CTRL, + DA7218_MIC_2_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + + /* Mixer Input */ + SOC_SINGLE_EXT_TLV("Mixin1 Volume", DA7218_MIXIN_1_GAIN, + DA7218_MIXIN_1_AMP_GAIN_SHIFT, + DA7218_MIXIN_AMP_GAIN_MAX, DA7218_NO_INVERT, + snd_soc_get_volsw, da7218_mixin_gain_put, + da7218_mixin_gain_tlv), + SOC_SINGLE("Mixin1 Switch", DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("Mixin1 Gain Ramp Switch", DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("Mixin1 ZC Gain Switch", DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_ZC_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE_EXT_TLV("Mixin2 Volume", DA7218_MIXIN_2_GAIN, + DA7218_MIXIN_2_AMP_GAIN_SHIFT, + DA7218_MIXIN_AMP_GAIN_MAX, DA7218_NO_INVERT, + snd_soc_get_volsw, da7218_mixin_gain_put, + da7218_mixin_gain_tlv), + SOC_SINGLE("Mixin2 Switch", DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("Mixin2 Gain Ramp Switch", DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("Mixin2 ZC Gain Switch", DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_ZC_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + + /* ADCs */ + SOC_SINGLE("ADC1 AAF Switch", DA7218_ADC_1_CTRL, + DA7218_ADC_1_AAF_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("ADC2 AAF Switch", DA7218_ADC_2_CTRL, + DA7218_ADC_2_AAF_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("ADC LP Mode Switch", DA7218_ADC_MODE, + DA7218_ADC_LP_MODE_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + + /* Input Filters */ + SOC_SINGLE_TLV("In Filter1L Volume", DA7218_IN_1L_GAIN, + DA7218_IN_1L_DIGITAL_GAIN_SHIFT, + DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT, + da7218_in_dig_gain_tlv), + SOC_SINGLE("In Filter1L Switch", DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("In Filter1L Gain Ramp Switch", DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE_TLV("In Filter1R Volume", DA7218_IN_1R_GAIN, + DA7218_IN_1R_DIGITAL_GAIN_SHIFT, + DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT, + da7218_in_dig_gain_tlv), + SOC_SINGLE("In Filter1R Switch", DA7218_IN_1R_FILTER_CTRL, + DA7218_IN_1R_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("In Filter1R Gain Ramp Switch", + DA7218_IN_1R_FILTER_CTRL, DA7218_IN_1R_RAMP_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), + SOC_SINGLE_TLV("In Filter2L Volume", DA7218_IN_2L_GAIN, + DA7218_IN_2L_DIGITAL_GAIN_SHIFT, + DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT, + da7218_in_dig_gain_tlv), + SOC_SINGLE("In Filter2L Switch", DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("In Filter2L Gain Ramp Switch", DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE_TLV("In Filter2R Volume", DA7218_IN_2R_GAIN, + DA7218_IN_2R_DIGITAL_GAIN_SHIFT, + DA7218_IN_DIGITAL_GAIN_MAX, DA7218_NO_INVERT, + da7218_in_dig_gain_tlv), + SOC_SINGLE("In Filter2R Switch", DA7218_IN_2R_FILTER_CTRL, + DA7218_IN_2R_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_SINGLE("In Filter2R Gain Ramp Switch", + DA7218_IN_2R_FILTER_CTRL, DA7218_IN_2R_RAMP_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), + + /* AGS */ + SOC_SINGLE_TLV("AGS Trigger", DA7218_AGS_TRIGGER, + DA7218_AGS_TRIGGER_SHIFT, DA7218_AGS_TRIGGER_MAX, + DA7218_INVERT, da7218_ags_trigger_tlv), + SOC_SINGLE_TLV("AGS Max Attenuation", DA7218_AGS_ATT_MAX, + DA7218_AGS_ATT_MAX_SHIFT, DA7218_AGS_ATT_MAX_MAX, + DA7218_NO_INVERT, da7218_ags_att_max_tlv), + SOC_SINGLE("AGS Anticlip Switch", DA7218_AGS_ANTICLIP_CTRL, + DA7218_AGS_ANTICLIP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("AGS Channel1 Switch", DA7218_AGS_ENABLE, + DA7218_AGS_ENABLE_CHAN1_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("AGS Channel2 Switch", DA7218_AGS_ENABLE, + DA7218_AGS_ENABLE_CHAN2_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + + /* ALC */ + SOC_ENUM("ALC Attack Rate", da7218_alc_attack_rate), + SOC_ENUM("ALC Release Rate", da7218_alc_release_rate), + SOC_ENUM("ALC Hold Time", da7218_alc_hold_time), + SOC_SINGLE_TLV("ALC Noise Threshold", DA7218_ALC_NOISE, + DA7218_ALC_NOISE_SHIFT, DA7218_ALC_THRESHOLD_MAX, + DA7218_INVERT, da7218_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Min Threshold", DA7218_ALC_TARGET_MIN, + DA7218_ALC_THRESHOLD_MIN_SHIFT, DA7218_ALC_THRESHOLD_MAX, + DA7218_INVERT, da7218_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Threshold", DA7218_ALC_TARGET_MAX, + DA7218_ALC_THRESHOLD_MAX_SHIFT, DA7218_ALC_THRESHOLD_MAX, + DA7218_INVERT, da7218_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Attenuation", DA7218_ALC_GAIN_LIMITS, + DA7218_ALC_ATTEN_MAX_SHIFT, DA7218_ALC_ATTEN_GAIN_MAX, + DA7218_NO_INVERT, da7218_alc_gain_tlv), + SOC_SINGLE_TLV("ALC Max Gain", DA7218_ALC_GAIN_LIMITS, + DA7218_ALC_GAIN_MAX_SHIFT, DA7218_ALC_ATTEN_GAIN_MAX, + DA7218_NO_INVERT, da7218_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Min Analog Gain", DA7218_ALC_ANA_GAIN_LIMITS, + DA7218_ALC_ANA_GAIN_MIN_SHIFT, + DA7218_ALC_ANA_GAIN_MIN, DA7218_ALC_ANA_GAIN_MAX, + DA7218_NO_INVERT, da7218_alc_ana_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Max Analog Gain", DA7218_ALC_ANA_GAIN_LIMITS, + DA7218_ALC_ANA_GAIN_MAX_SHIFT, + DA7218_ALC_ANA_GAIN_MIN, DA7218_ALC_ANA_GAIN_MAX, + DA7218_NO_INVERT, da7218_alc_ana_gain_tlv), + SOC_ENUM("ALC Anticlip Step", da7218_alc_anticlip_step), + SOC_SINGLE("ALC Anticlip Switch", DA7218_ALC_ANTICLIP_CTRL, + DA7218_ALC_ANTICLIP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_DOUBLE_EXT("ALC Channel1 Switch", DA7218_ALC_CTRL1, + DA7218_ALC_CHAN1_L_EN_SHIFT, DA7218_ALC_CHAN1_R_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT, + snd_soc_get_volsw, da7218_alc_sw_put), + SOC_DOUBLE_EXT("ALC Channel2 Switch", DA7218_ALC_CTRL1, + DA7218_ALC_CHAN2_L_EN_SHIFT, DA7218_ALC_CHAN2_R_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT, + snd_soc_get_volsw, da7218_alc_sw_put), + + /* Envelope Tracking */ + SOC_ENUM("Envelope Tracking Attack Rate", da7218_integ_attack_rate), + SOC_ENUM("Envelope Tracking Release Rate", da7218_integ_release_rate), + + /* Input High-Pass Filters */ + SOC_ENUM("In Filter1 HPF Mode", da7218_in1_hpf_mode), + SOC_ENUM("In Filter1 HPF Corner Audio", da7218_in1_audio_hpf_corner), + SOC_ENUM("In Filter1 HPF Corner Voice", da7218_in1_voice_hpf_corner), + SOC_ENUM("In Filter2 HPF Mode", da7218_in2_hpf_mode), + SOC_ENUM("In Filter2 HPF Corner Audio", da7218_in2_audio_hpf_corner), + SOC_ENUM("In Filter2 HPF Corner Voice", da7218_in2_voice_hpf_corner), + + /* Mic Level Detect */ + SOC_DOUBLE_EXT("Mic Level Detect Channel1 Switch", DA7218_LVL_DET_CTRL, + DA7218_LVL_DET_EN_CHAN1L_SHIFT, + DA7218_LVL_DET_EN_CHAN1R_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT, da7218_mic_lvl_det_sw_get, + da7218_mic_lvl_det_sw_put), + SOC_DOUBLE_EXT("Mic Level Detect Channel2 Switch", DA7218_LVL_DET_CTRL, + DA7218_LVL_DET_EN_CHAN2L_SHIFT, + DA7218_LVL_DET_EN_CHAN2R_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT, da7218_mic_lvl_det_sw_get, + da7218_mic_lvl_det_sw_put), + SOC_SINGLE("Mic Level Detect Level", DA7218_LVL_DET_LEVEL, + DA7218_LVL_DET_LEVEL_SHIFT, DA7218_LVL_DET_LEVEL_MAX, + DA7218_NO_INVERT), + + /* Digital Mixer (Input) */ + SOC_SINGLE_TLV("DMix In Filter1L Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN, + DA7218_OUTDAI_1L_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1L Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN, + DA7218_OUTDAI_1R_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1L Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN, + DA7218_OUTDAI_2L_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1L Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN, + DA7218_OUTDAI_2R_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter1R Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN, + DA7218_OUTDAI_1L_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1R Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN, + DA7218_OUTDAI_1R_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1R Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN, + DA7218_OUTDAI_2L_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1R Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN, + DA7218_OUTDAI_2R_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter2L Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN, + DA7218_OUTDAI_1L_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2L Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN, + DA7218_OUTDAI_1R_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2L Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN, + DA7218_OUTDAI_2L_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2L Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN, + DA7218_OUTDAI_2R_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter2R Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN, + DA7218_OUTDAI_1L_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2R Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN, + DA7218_OUTDAI_1R_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2R Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN, + DA7218_OUTDAI_2L_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2R Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN, + DA7218_OUTDAI_2R_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix ToneGen Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN, + DA7218_OUTDAI_1L_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix ToneGen Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN, + DA7218_OUTDAI_1R_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix ToneGen Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN, + DA7218_OUTDAI_2L_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix ToneGen Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN, + DA7218_OUTDAI_2R_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In DAIL Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN, + DA7218_OUTDAI_1L_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIL Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN, + DA7218_OUTDAI_1R_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIL Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN, + DA7218_OUTDAI_2L_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIL Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN, + DA7218_OUTDAI_2R_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In DAIR Out1 DAIL Volume", + DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN, + DA7218_OUTDAI_1L_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIR Out1 DAIR Volume", + DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN, + DA7218_OUTDAI_1R_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIR Out2 DAIL Volume", + DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN, + DA7218_OUTDAI_2L_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIR Out2 DAIR Volume", + DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN, + DA7218_OUTDAI_2R_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + /* Digital Mixer (Output) */ + SOC_SINGLE_TLV("DMix In Filter1L Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN, + DA7218_OUTFILT_1L_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1L Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN, + DA7218_OUTFILT_1R_INFILT_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter1R Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN, + DA7218_OUTFILT_1L_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter1R Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN, + DA7218_OUTFILT_1R_INFILT_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter2L Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN, + DA7218_OUTFILT_1L_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2L Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN, + DA7218_OUTFILT_1R_INFILT_2L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In Filter2R Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN, + DA7218_OUTFILT_1L_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In Filter2R Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN, + DA7218_OUTFILT_1R_INFILT_2R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix ToneGen Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN, + DA7218_OUTFILT_1L_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix ToneGen Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN, + DA7218_OUTFILT_1R_TONEGEN_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In DAIL Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN, + DA7218_OUTFILT_1L_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIL Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN, + DA7218_OUTFILT_1R_INDAI_1L_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + SOC_SINGLE_TLV("DMix In DAIR Out FilterL Volume", + DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN, + DA7218_OUTFILT_1L_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + SOC_SINGLE_TLV("DMix In DAIR Out FilterR Volume", + DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN, + DA7218_OUTFILT_1R_INDAI_1R_GAIN_SHIFT, + DA7218_DMIX_GAIN_MAX, DA7218_NO_INVERT, + da7218_dmix_gain_tlv), + + /* Sidetone Filter */ + SND_SOC_BYTES_EXT("Sidetone BiQuad Coefficients", + DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE, + da7218_biquad_coeff_get, da7218_biquad_coeff_put), + SOC_SINGLE_TLV("Sidetone Volume", DA7218_SIDETONE_GAIN, + DA7218_SIDETONE_GAIN_SHIFT, DA7218_DMIX_GAIN_MAX, + DA7218_NO_INVERT, da7218_dmix_gain_tlv), + SOC_SINGLE("Sidetone Switch", DA7218_SIDETONE_CTRL, + DA7218_SIDETONE_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + + /* Tone Generator */ + SOC_ENUM("ToneGen DTMF Key", da7218_tonegen_dtmf_key), + SOC_SINGLE("ToneGen DTMF Switch", DA7218_TONE_GEN_CFG1, + DA7218_DTMF_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_ENUM("ToneGen Sinewave Gen Type", da7218_tonegen_swg_sel), + SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7218_TONE_GEN_FREQ1_L, + DA7218_FREQ1_L_SHIFT, DA7218_FREQ_MAX, DA7218_NO_INVERT, + da7218_tonegen_freq_get, da7218_tonegen_freq_put), + SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7218_TONE_GEN_FREQ2_L, + DA7218_FREQ2_L_SHIFT, DA7218_FREQ_MAX, DA7218_NO_INVERT, + da7218_tonegen_freq_get, da7218_tonegen_freq_put), + SOC_SINGLE("ToneGen On Time", DA7218_TONE_GEN_ON_PER, + DA7218_BEEP_ON_PER_SHIFT, DA7218_BEEP_ON_OFF_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("ToneGen Off Time", DA7218_TONE_GEN_OFF_PER, + DA7218_BEEP_OFF_PER_SHIFT, DA7218_BEEP_ON_OFF_MAX, + DA7218_NO_INVERT), + + /* Gain ramping */ + SOC_ENUM("Gain Ramp Rate", da7218_gain_ramp_rate), + + /* DGS */ + SOC_SINGLE_TLV("DGS Trigger", DA7218_DGS_TRIGGER, + DA7218_DGS_TRIGGER_LVL_SHIFT, DA7218_DGS_TRIGGER_MAX, + DA7218_INVERT, da7218_dgs_trigger_tlv), + SOC_ENUM("DGS Rise Coefficient", da7218_dgs_rise_coeff), + SOC_ENUM("DGS Fall Coefficient", da7218_dgs_fall_coeff), + SOC_SINGLE("DGS Sync Delay", DA7218_DGS_SYNC_DELAY, + DA7218_DGS_SYNC_DELAY_SHIFT, DA7218_DGS_SYNC_DELAY_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("DGS Fast SR Sync Delay", DA7218_DGS_SYNC_DELAY2, + DA7218_DGS_SYNC_DELAY2_SHIFT, DA7218_DGS_SYNC_DELAY_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("DGS Voice Filter Sync Delay", DA7218_DGS_SYNC_DELAY3, + DA7218_DGS_SYNC_DELAY3_SHIFT, DA7218_DGS_SYNC_DELAY3_MAX, + DA7218_NO_INVERT), + SOC_SINGLE_TLV("DGS Anticlip Level", DA7218_DGS_LEVELS, + DA7218_DGS_ANTICLIP_LVL_SHIFT, + DA7218_DGS_ANTICLIP_LVL_MAX, DA7218_INVERT, + da7218_dgs_anticlip_tlv), + SOC_SINGLE_TLV("DGS Signal Level", DA7218_DGS_LEVELS, + DA7218_DGS_SIGNAL_LVL_SHIFT, DA7218_DGS_SIGNAL_LVL_MAX, + DA7218_INVERT, da7218_dgs_signal_tlv), + SOC_SINGLE("DGS Gain Subrange Switch", DA7218_DGS_GAIN_CTRL, + DA7218_DGS_SUBR_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("DGS Gain Ramp Switch", DA7218_DGS_GAIN_CTRL, + DA7218_DGS_RAMP_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_SINGLE("DGS Gain Steps", DA7218_DGS_GAIN_CTRL, + DA7218_DGS_STEPS_SHIFT, DA7218_DGS_STEPS_MAX, + DA7218_NO_INVERT), + SOC_DOUBLE("DGS Switch", DA7218_DGS_ENABLE, DA7218_DGS_ENABLE_L_SHIFT, + DA7218_DGS_ENABLE_R_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + + /* Output High-Pass Filter */ + SOC_ENUM("Out Filter HPF Mode", da7218_out1_hpf_mode), + SOC_ENUM("Out Filter HPF Corner Audio", da7218_out1_audio_hpf_corner), + SOC_ENUM("Out Filter HPF Corner Voice", da7218_out1_voice_hpf_corner), + + /* 5-Band Equaliser */ + SOC_SINGLE_TLV("Out EQ Band1 Volume", DA7218_OUT_1_EQ_12_FILTER_CTRL, + DA7218_OUT_1_EQ_BAND1_SHIFT, DA7218_OUT_EQ_BAND_MAX, + DA7218_NO_INVERT, da7218_out_eq_band_tlv), + SOC_SINGLE_TLV("Out EQ Band2 Volume", DA7218_OUT_1_EQ_12_FILTER_CTRL, + DA7218_OUT_1_EQ_BAND2_SHIFT, DA7218_OUT_EQ_BAND_MAX, + DA7218_NO_INVERT, da7218_out_eq_band_tlv), + SOC_SINGLE_TLV("Out EQ Band3 Volume", DA7218_OUT_1_EQ_34_FILTER_CTRL, + DA7218_OUT_1_EQ_BAND3_SHIFT, DA7218_OUT_EQ_BAND_MAX, + DA7218_NO_INVERT, da7218_out_eq_band_tlv), + SOC_SINGLE_TLV("Out EQ Band4 Volume", DA7218_OUT_1_EQ_34_FILTER_CTRL, + DA7218_OUT_1_EQ_BAND4_SHIFT, DA7218_OUT_EQ_BAND_MAX, + DA7218_NO_INVERT, da7218_out_eq_band_tlv), + SOC_SINGLE_TLV("Out EQ Band5 Volume", DA7218_OUT_1_EQ_5_FILTER_CTRL, + DA7218_OUT_1_EQ_BAND5_SHIFT, DA7218_OUT_EQ_BAND_MAX, + DA7218_NO_INVERT, da7218_out_eq_band_tlv), + SOC_SINGLE("Out EQ Switch", DA7218_OUT_1_EQ_5_FILTER_CTRL, + DA7218_OUT_1_EQ_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + + /* BiQuad Filters */ + SND_SOC_BYTES_EXT("BiQuad Coefficients", + DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE, + da7218_biquad_coeff_get, da7218_biquad_coeff_put), + SOC_SINGLE("BiQuad Filter Switch", DA7218_OUT_1_BIQ_5STAGE_CTRL, + DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + + /* Output Filters */ + SOC_DOUBLE_R_RANGE_TLV("Out Filter Volume", DA7218_OUT_1L_GAIN, + DA7218_OUT_1R_GAIN, + DA7218_OUT_1L_DIGITAL_GAIN_SHIFT, + DA7218_OUT_DIGITAL_GAIN_MIN, + DA7218_OUT_DIGITAL_GAIN_MAX, DA7218_NO_INVERT, + da7218_out_dig_gain_tlv), + SOC_DOUBLE_R("Out Filter Switch", DA7218_OUT_1L_FILTER_CTRL, + DA7218_OUT_1R_FILTER_CTRL, DA7218_OUT_1L_MUTE_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_INVERT), + SOC_DOUBLE_R("Out Filter Gain Subrange Switch", + DA7218_OUT_1L_FILTER_CTRL, DA7218_OUT_1R_FILTER_CTRL, + DA7218_OUT_1L_SUBRANGE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_NO_INVERT), + SOC_DOUBLE_R("Out Filter Gain Ramp Switch", DA7218_OUT_1L_FILTER_CTRL, + DA7218_OUT_1R_FILTER_CTRL, DA7218_OUT_1L_RAMP_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), + + /* Mixer Output */ + SOC_DOUBLE_R_RANGE_TLV("Mixout Volume", DA7218_MIXOUT_L_GAIN, + DA7218_MIXOUT_R_GAIN, + DA7218_MIXOUT_L_AMP_GAIN_SHIFT, + DA7218_MIXOUT_AMP_GAIN_MIN, + DA7218_MIXOUT_AMP_GAIN_MAX, DA7218_NO_INVERT, + da7218_mixout_gain_tlv), + + /* DAC Noise Gate */ + SOC_ENUM("DAC NG Setup Time", da7218_dac_ng_setup_time), + SOC_ENUM("DAC NG Rampup Rate", da7218_dac_ng_rampup_rate), + SOC_ENUM("DAC NG Rampdown Rate", da7218_dac_ng_rampdown_rate), + SOC_SINGLE_TLV("DAC NG Off Threshold", DA7218_DAC_NG_OFF_THRESH, + DA7218_DAC_NG_OFF_THRESHOLD_SHIFT, + DA7218_DAC_NG_THRESHOLD_MAX, DA7218_NO_INVERT, + da7218_dac_ng_threshold_tlv), + SOC_SINGLE_TLV("DAC NG On Threshold", DA7218_DAC_NG_ON_THRESH, + DA7218_DAC_NG_ON_THRESHOLD_SHIFT, + DA7218_DAC_NG_THRESHOLD_MAX, DA7218_NO_INVERT, + da7218_dac_ng_threshold_tlv), + SOC_SINGLE("DAC NG Switch", DA7218_DAC_NG_CTRL, DA7218_DAC_NG_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), + + /* CP */ + SOC_ENUM("Charge Pump Track Mode", da7218_cp_mchange), + SOC_ENUM("Charge Pump Frequency", da7218_cp_fcontrol), + SOC_ENUM("Charge Pump Decay Rate", da7218_cp_tau_delay), + SOC_SINGLE("Charge Pump Threshold", DA7218_CP_VOL_THRESHOLD1, + DA7218_CP_THRESH_VDD2_SHIFT, DA7218_CP_THRESH_VDD2_MAX, + DA7218_NO_INVERT), + + /* Headphones */ + SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", DA7218_HP_L_GAIN, + DA7218_HP_R_GAIN, DA7218_HP_L_AMP_GAIN_SHIFT, + DA7218_HP_AMP_GAIN_MIN, DA7218_HP_AMP_GAIN_MAX, + DA7218_NO_INVERT, da7218_hp_gain_tlv), + SOC_DOUBLE_R("Headphone Switch", DA7218_HP_L_CTRL, DA7218_HP_R_CTRL, + DA7218_HP_L_AMP_MUTE_EN_SHIFT, DA7218_SWITCH_EN_MAX, + DA7218_INVERT), + SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7218_HP_L_CTRL, + DA7218_HP_R_CTRL, DA7218_HP_L_AMP_RAMP_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), + SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7218_HP_L_CTRL, + DA7218_HP_R_CTRL, DA7218_HP_L_AMP_ZC_EN_SHIFT, + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), +}; + + +/* + * DAPM Mux Controls + */ + +static const char * const da7218_mic_sel_text[] = { "Analog", "Digital" }; + +static const struct soc_enum da7218_mic1_sel = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(da7218_mic_sel_text), + da7218_mic_sel_text); + +static const struct snd_kcontrol_new da7218_mic1_sel_mux = + SOC_DAPM_ENUM("Mic1 Mux", da7218_mic1_sel); + +static const struct soc_enum da7218_mic2_sel = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(da7218_mic_sel_text), + da7218_mic_sel_text); + +static const struct snd_kcontrol_new da7218_mic2_sel_mux = + SOC_DAPM_ENUM("Mic2 Mux", da7218_mic2_sel); + +static const char * const da7218_sidetone_in_sel_txt[] = { + "In Filter1L", "In Filter1R", "In Filter2L", "In Filter2R" +}; + +static const struct soc_enum da7218_sidetone_in_sel = + SOC_ENUM_SINGLE(DA7218_SIDETONE_IN_SELECT, + DA7218_SIDETONE_IN_SELECT_SHIFT, + DA7218_SIDETONE_IN_SELECT_MAX, + da7218_sidetone_in_sel_txt); + +static const struct snd_kcontrol_new da7218_sidetone_in_sel_mux = + SOC_DAPM_ENUM("Sidetone Mux", da7218_sidetone_in_sel); + +static const char * const da7218_out_filt_biq_sel_txt[] = { + "Bypass", "Enabled" +}; + +static const struct soc_enum da7218_out_filtl_biq_sel = + SOC_ENUM_SINGLE(DA7218_OUT_1L_FILTER_CTRL, + DA7218_OUT_1L_BIQ_5STAGE_SEL_SHIFT, + DA7218_OUT_BIQ_5STAGE_SEL_MAX, + da7218_out_filt_biq_sel_txt); + +static const struct snd_kcontrol_new da7218_out_filtl_biq_sel_mux = + SOC_DAPM_ENUM("Out FilterL BiQuad Mux", da7218_out_filtl_biq_sel); + +static const struct soc_enum da7218_out_filtr_biq_sel = + SOC_ENUM_SINGLE(DA7218_OUT_1R_FILTER_CTRL, + DA7218_OUT_1R_BIQ_5STAGE_SEL_SHIFT, + DA7218_OUT_BIQ_5STAGE_SEL_MAX, + da7218_out_filt_biq_sel_txt); + +static const struct snd_kcontrol_new da7218_out_filtr_biq_sel_mux = + SOC_DAPM_ENUM("Out FilterR BiQuad Mux", da7218_out_filtr_biq_sel); + + +/* + * DAPM Mixer Controls + */ + +#define DA7218_DMIX_CTRLS(reg) \ + SOC_DAPM_SINGLE("In Filter1L Switch", reg, \ + DA7218_DMIX_SRC_INFILT1L, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("In Filter1R Switch", reg, \ + DA7218_DMIX_SRC_INFILT1R, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("In Filter2L Switch", reg, \ + DA7218_DMIX_SRC_INFILT2L, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("In Filter2R Switch", reg, \ + DA7218_DMIX_SRC_INFILT2R, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("ToneGen Switch", reg, \ + DA7218_DMIX_SRC_TONEGEN, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("DAIL Switch", reg, DA7218_DMIX_SRC_DAIL, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("DAIR Switch", reg, DA7218_DMIX_SRC_DAIR, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT) + +static const struct snd_kcontrol_new da7218_out_dai1l_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_1L), +}; + +static const struct snd_kcontrol_new da7218_out_dai1r_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_1R), +}; + +static const struct snd_kcontrol_new da7218_out_dai2l_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_2L), +}; + +static const struct snd_kcontrol_new da7218_out_dai2r_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTDAI_2R), +}; + +static const struct snd_kcontrol_new da7218_out_filtl_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTFILT_1L), +}; + +static const struct snd_kcontrol_new da7218_out_filtr_mix_controls[] = { + DA7218_DMIX_CTRLS(DA7218_DROUTING_OUTFILT_1R), +}; + +#define DA7218_DMIX_ST_CTRLS(reg) \ + SOC_DAPM_SINGLE("Out FilterL Switch", reg, \ + DA7218_DMIX_ST_SRC_OUTFILT1L, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("Out FilterR Switch", reg, \ + DA7218_DMIX_ST_SRC_OUTFILT1R, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT), \ + SOC_DAPM_SINGLE("Sidetone Switch", reg, \ + DA7218_DMIX_ST_SRC_SIDETONE, \ + DA7218_SWITCH_EN_MAX, DA7218_NO_INVERT) \ + +static const struct snd_kcontrol_new da7218_st_out_filtl_mix_controls[] = { + DA7218_DMIX_ST_CTRLS(DA7218_DROUTING_ST_OUTFILT_1L), +}; + +static const struct snd_kcontrol_new da7218_st_out_filtr_mix_controls[] = { + DA7218_DMIX_ST_CTRLS(DA7218_DROUTING_ST_OUTFILT_1R), +}; + + +/* + * DAPM Events + */ + +/* + * We keep track of which input filters are enabled. This is used in the logic + * for controlling the mic level detect feature. + */ +static int da7218_in_filter_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + u8 mask; + + switch (w->reg) { + case DA7218_IN_1L_FILTER_CTRL: + mask = (1 << DA7218_LVL_DET_EN_CHAN1L_SHIFT); + break; + case DA7218_IN_1R_FILTER_CTRL: + mask = (1 << DA7218_LVL_DET_EN_CHAN1R_SHIFT); + break; + case DA7218_IN_2L_FILTER_CTRL: + mask = (1 << DA7218_LVL_DET_EN_CHAN2L_SHIFT); + break; + case DA7218_IN_2R_FILTER_CTRL: + mask = (1 << DA7218_LVL_DET_EN_CHAN2R_SHIFT); + break; + default: + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + da7218->in_filt_en |= mask; + /* + * If we're enabling path for mic level detect, wait for path + * to settle before enabling feature to avoid incorrect and + * unwanted detect events. + */ + if (mask & da7218->mic_lvl_det_en) + msleep(DA7218_MIC_LVL_DET_DELAY); + break; + case SND_SOC_DAPM_PRE_PMD: + da7218->in_filt_en &= ~mask; + break; + default: + return -EINVAL; + } + + /* Enable configured level detection paths */ + snd_soc_component_write(component, DA7218_LVL_DET_CTRL, + (da7218->in_filt_en & da7218->mic_lvl_det_en)); + + return 0; +} + +static int da7218_dai_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + u8 pll_ctrl, pll_status, refosc_cal; + int i; + bool success; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (da7218->master) + /* Enable DAI clks for master mode */ + snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE, + DA7218_DAI_CLK_EN_MASK, + DA7218_DAI_CLK_EN_MASK); + + /* Tune reference oscillator */ + snd_soc_component_write(component, DA7218_PLL_REFOSC_CAL, + DA7218_PLL_REFOSC_CAL_START_MASK); + snd_soc_component_write(component, DA7218_PLL_REFOSC_CAL, + DA7218_PLL_REFOSC_CAL_START_MASK | + DA7218_PLL_REFOSC_CAL_EN_MASK); + + /* Check tuning complete */ + i = 0; + success = false; + do { + refosc_cal = snd_soc_component_read(component, DA7218_PLL_REFOSC_CAL); + if (!(refosc_cal & DA7218_PLL_REFOSC_CAL_START_MASK)) { + success = true; + } else { + ++i; + usleep_range(DA7218_REF_OSC_CHECK_DELAY_MIN, + DA7218_REF_OSC_CHECK_DELAY_MAX); + } + } while ((i < DA7218_REF_OSC_CHECK_TRIES) && (!success)); + + if (!success) + dev_warn(component->dev, + "Reference oscillator failed calibration\n"); + + /* PC synchronised to DAI */ + snd_soc_component_write(component, DA7218_PC_COUNT, + DA7218_PC_RESYNC_AUTO_MASK); + + /* If SRM not enabled, we don't need to check status */ + pll_ctrl = snd_soc_component_read(component, DA7218_PLL_CTRL); + if ((pll_ctrl & DA7218_PLL_MODE_MASK) != DA7218_PLL_MODE_SRM) + return 0; + + /* Check SRM has locked */ + i = 0; + success = false; + do { + pll_status = snd_soc_component_read(component, DA7218_PLL_STATUS); + if (pll_status & DA7218_PLL_SRM_STATUS_SRM_LOCK) { + success = true; + } else { + ++i; + msleep(DA7218_SRM_CHECK_DELAY); + } + } while ((i < DA7218_SRM_CHECK_TRIES) && (!success)); + + if (!success) + dev_warn(component->dev, "SRM failed to lock\n"); + + return 0; + case SND_SOC_DAPM_POST_PMD: + /* PC free-running */ + snd_soc_component_write(component, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK); + + if (da7218->master) + /* Disable DAI clks for master mode */ + snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE, + DA7218_DAI_CLK_EN_MASK, 0); + + return 0; + default: + return -EINVAL; + } +} + +static int da7218_cp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + + /* + * If this is DA7217 and we're using single supply for differential + * output, we really don't want to touch the charge pump. + */ + if (da7218->hp_single_supply) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, DA7218_CP_CTRL, DA7218_CP_EN_MASK, + DA7218_CP_EN_MASK); + return 0; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, DA7218_CP_CTRL, DA7218_CP_EN_MASK, + 0); + return 0; + default: + return -EINVAL; + } +} + +static int da7218_hp_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Enable headphone output */ + snd_soc_component_update_bits(component, w->reg, DA7218_HP_AMP_OE_MASK, + DA7218_HP_AMP_OE_MASK); + return 0; + case SND_SOC_DAPM_PRE_PMD: + /* Headphone output high impedance */ + snd_soc_component_update_bits(component, w->reg, DA7218_HP_AMP_OE_MASK, 0); + return 0; + default: + return -EINVAL; + } +} + + +/* + * DAPM Widgets + */ + +static const struct snd_soc_dapm_widget da7218_dapm_widgets[] = { + /* Input Supplies */ + SND_SOC_DAPM_SUPPLY("Mic Bias1", DA7218_MICBIAS_EN, + DA7218_MICBIAS_1_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias2", DA7218_MICBIAS_EN, + DA7218_MICBIAS_2_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMic1 Left", DA7218_DMIC_1_CTRL, + DA7218_DMIC_1L_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMic1 Right", DA7218_DMIC_1_CTRL, + DA7218_DMIC_1R_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMic2 Left", DA7218_DMIC_2_CTRL, + DA7218_DMIC_2L_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMic2 Right", DA7218_DMIC_2_CTRL, + DA7218_DMIC_2R_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMIC1L"), + SND_SOC_DAPM_INPUT("DMIC1R"), + SND_SOC_DAPM_INPUT("DMIC2L"), + SND_SOC_DAPM_INPUT("DMIC2R"), + + /* Input Mixer Supplies */ + SND_SOC_DAPM_SUPPLY("Mixin1 Supply", DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_MIX_SEL_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Mixin2 Supply", DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_MIX_SEL_SHIFT, DA7218_NO_INVERT, + NULL, 0), + + /* Input PGAs */ + SND_SOC_DAPM_PGA("Mic1 PGA", DA7218_MIC_1_CTRL, + DA7218_MIC_1_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mic2 PGA", DA7218_MIC_2_CTRL, + DA7218_MIC_2_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixin1 PGA", DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixin2 PGA", DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + + /* Mic/DMic Muxes */ + SND_SOC_DAPM_MUX("Mic1 Mux", SND_SOC_NOPM, 0, 0, &da7218_mic1_sel_mux), + SND_SOC_DAPM_MUX("Mic2 Mux", SND_SOC_NOPM, 0, 0, &da7218_mic2_sel_mux), + + /* Input Filters */ + SND_SOC_DAPM_ADC_E("In Filter1L", NULL, DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_FILTER_EN_SHIFT, DA7218_NO_INVERT, + da7218_in_filter_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("In Filter1R", NULL, DA7218_IN_1R_FILTER_CTRL, + DA7218_IN_1R_FILTER_EN_SHIFT, DA7218_NO_INVERT, + da7218_in_filter_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("In Filter2L", NULL, DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_FILTER_EN_SHIFT, DA7218_NO_INVERT, + da7218_in_filter_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("In Filter2R", NULL, DA7218_IN_2R_FILTER_CTRL, + DA7218_IN_2R_FILTER_EN_SHIFT, DA7218_NO_INVERT, + da7218_in_filter_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Tone Generator */ + SND_SOC_DAPM_SIGGEN("TONE"), + SND_SOC_DAPM_PGA("Tone Generator", DA7218_TONE_GEN_CFG1, + DA7218_START_STOPN_SHIFT, DA7218_NO_INVERT, NULL, 0), + + /* Sidetone Input */ + SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, + &da7218_sidetone_in_sel_mux), + SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7218_SIDETONE_CTRL, + DA7218_SIDETONE_FILTER_EN_SHIFT, DA7218_NO_INVERT), + + /* Input Mixers */ + SND_SOC_DAPM_MIXER("Mixer DAI1L", SND_SOC_NOPM, 0, 0, + da7218_out_dai1l_mix_controls, + ARRAY_SIZE(da7218_out_dai1l_mix_controls)), + SND_SOC_DAPM_MIXER("Mixer DAI1R", SND_SOC_NOPM, 0, 0, + da7218_out_dai1r_mix_controls, + ARRAY_SIZE(da7218_out_dai1r_mix_controls)), + SND_SOC_DAPM_MIXER("Mixer DAI2L", SND_SOC_NOPM, 0, 0, + da7218_out_dai2l_mix_controls, + ARRAY_SIZE(da7218_out_dai2l_mix_controls)), + SND_SOC_DAPM_MIXER("Mixer DAI2R", SND_SOC_NOPM, 0, 0, + da7218_out_dai2r_mix_controls, + ARRAY_SIZE(da7218_out_dai2r_mix_controls)), + + /* DAI Supply */ + SND_SOC_DAPM_SUPPLY("DAI", DA7218_DAI_CTRL, DA7218_DAI_EN_SHIFT, + DA7218_NO_INVERT, da7218_dai_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* DAI */ + SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7218_DAI_TDM_CTRL, + DA7218_DAI_OE_SHIFT, DA7218_NO_INVERT), + SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7218_out_filtl_mix_controls, + ARRAY_SIZE(da7218_out_filtl_mix_controls)), + SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0, + da7218_out_filtr_mix_controls, + ARRAY_SIZE(da7218_out_filtr_mix_controls)), + + /* BiQuad Filters */ + SND_SOC_DAPM_MUX("Out FilterL BiQuad Mux", SND_SOC_NOPM, 0, 0, + &da7218_out_filtl_biq_sel_mux), + SND_SOC_DAPM_MUX("Out FilterR BiQuad Mux", SND_SOC_NOPM, 0, 0, + &da7218_out_filtr_biq_sel_mux), + SND_SOC_DAPM_DAC("BiQuad Filter", NULL, DA7218_OUT_1_BIQ_5STAGE_CTRL, + DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_SHIFT, + DA7218_NO_INVERT), + + /* Sidetone Mixers */ + SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7218_st_out_filtl_mix_controls, + ARRAY_SIZE(da7218_st_out_filtl_mix_controls)), + SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0, 0, + da7218_st_out_filtr_mix_controls, + ARRAY_SIZE(da7218_st_out_filtr_mix_controls)), + + /* Output Filters */ + SND_SOC_DAPM_DAC("Out FilterL", NULL, DA7218_OUT_1L_FILTER_CTRL, + DA7218_OUT_1L_FILTER_EN_SHIFT, DA7218_NO_INVERT), + SND_SOC_DAPM_DAC("Out FilterR", NULL, DA7218_OUT_1R_FILTER_CTRL, + DA7218_IN_1R_FILTER_EN_SHIFT, DA7218_NO_INVERT), + + /* Output PGAs */ + SND_SOC_DAPM_PGA("Mixout Left PGA", DA7218_MIXOUT_L_CTRL, + DA7218_MIXOUT_L_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA("Mixout Right PGA", DA7218_MIXOUT_R_CTRL, + DA7218_MIXOUT_R_AMP_EN_SHIFT, DA7218_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_PGA_E("Headphone Left PGA", DA7218_HP_L_CTRL, + DA7218_HP_L_AMP_EN_SHIFT, DA7218_NO_INVERT, NULL, 0, + da7218_hp_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("Headphone Right PGA", DA7218_HP_R_CTRL, + DA7218_HP_R_AMP_EN_SHIFT, DA7218_NO_INVERT, NULL, 0, + da7218_hp_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Output Supplies */ + SND_SOC_DAPM_SUPPLY("Charge Pump", SND_SOC_NOPM, 0, 0, da7218_cp_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), +}; + + +/* + * DAPM Mixer Routes + */ + +#define DA7218_DMIX_ROUTES(name) \ + {name, "In Filter1L Switch", "In Filter1L"}, \ + {name, "In Filter1R Switch", "In Filter1R"}, \ + {name, "In Filter2L Switch", "In Filter2L"}, \ + {name, "In Filter2R Switch", "In Filter2R"}, \ + {name, "ToneGen Switch", "Tone Generator"}, \ + {name, "DAIL Switch", "DAIIN"}, \ + {name, "DAIR Switch", "DAIIN"} + +#define DA7218_DMIX_ST_ROUTES(name) \ + {name, "Out FilterL Switch", "Out FilterL BiQuad Mux"}, \ + {name, "Out FilterR Switch", "Out FilterR BiQuad Mux"}, \ + {name, "Sidetone Switch", "Sidetone Filter"} + + +/* + * DAPM audio route definition + */ + +static const struct snd_soc_dapm_route da7218_audio_map[] = { + /* Input paths */ + {"MIC1", NULL, "Mic Bias1"}, + {"MIC2", NULL, "Mic Bias2"}, + {"DMIC1L", NULL, "Mic Bias1"}, + {"DMIC1L", NULL, "DMic1 Left"}, + {"DMIC1R", NULL, "Mic Bias1"}, + {"DMIC1R", NULL, "DMic1 Right"}, + {"DMIC2L", NULL, "Mic Bias2"}, + {"DMIC2L", NULL, "DMic2 Left"}, + {"DMIC2R", NULL, "Mic Bias2"}, + {"DMIC2R", NULL, "DMic2 Right"}, + + {"Mic1 PGA", NULL, "MIC1"}, + {"Mic2 PGA", NULL, "MIC2"}, + + {"Mixin1 PGA", NULL, "Mixin1 Supply"}, + {"Mixin2 PGA", NULL, "Mixin2 Supply"}, + + {"Mixin1 PGA", NULL, "Mic1 PGA"}, + {"Mixin2 PGA", NULL, "Mic2 PGA"}, + + {"Mic1 Mux", "Analog", "Mixin1 PGA"}, + {"Mic1 Mux", "Digital", "DMIC1L"}, + {"Mic1 Mux", "Digital", "DMIC1R"}, + {"Mic2 Mux", "Analog", "Mixin2 PGA"}, + {"Mic2 Mux", "Digital", "DMIC2L"}, + {"Mic2 Mux", "Digital", "DMIC2R"}, + + {"In Filter1L", NULL, "Mic1 Mux"}, + {"In Filter1R", NULL, "Mic1 Mux"}, + {"In Filter2L", NULL, "Mic2 Mux"}, + {"In Filter2R", NULL, "Mic2 Mux"}, + + {"Tone Generator", NULL, "TONE"}, + + {"Sidetone Mux", "In Filter1L", "In Filter1L"}, + {"Sidetone Mux", "In Filter1R", "In Filter1R"}, + {"Sidetone Mux", "In Filter2L", "In Filter2L"}, + {"Sidetone Mux", "In Filter2R", "In Filter2R"}, + {"Sidetone Filter", NULL, "Sidetone Mux"}, + + DA7218_DMIX_ROUTES("Mixer DAI1L"), + DA7218_DMIX_ROUTES("Mixer DAI1R"), + DA7218_DMIX_ROUTES("Mixer DAI2L"), + DA7218_DMIX_ROUTES("Mixer DAI2R"), + + {"DAIOUT", NULL, "Mixer DAI1L"}, + {"DAIOUT", NULL, "Mixer DAI1R"}, + {"DAIOUT", NULL, "Mixer DAI2L"}, + {"DAIOUT", NULL, "Mixer DAI2R"}, + + {"DAIOUT", NULL, "DAI"}, + + /* Output paths */ + {"DAIIN", NULL, "DAI"}, + + DA7218_DMIX_ROUTES("Mixer Out FilterL"), + DA7218_DMIX_ROUTES("Mixer Out FilterR"), + + {"BiQuad Filter", NULL, "Mixer Out FilterL"}, + {"BiQuad Filter", NULL, "Mixer Out FilterR"}, + + {"Out FilterL BiQuad Mux", "Bypass", "Mixer Out FilterL"}, + {"Out FilterL BiQuad Mux", "Enabled", "BiQuad Filter"}, + {"Out FilterR BiQuad Mux", "Bypass", "Mixer Out FilterR"}, + {"Out FilterR BiQuad Mux", "Enabled", "BiQuad Filter"}, + + DA7218_DMIX_ST_ROUTES("ST Mixer Out FilterL"), + DA7218_DMIX_ST_ROUTES("ST Mixer Out FilterR"), + + {"Out FilterL", NULL, "ST Mixer Out FilterL"}, + {"Out FilterR", NULL, "ST Mixer Out FilterR"}, + + {"Mixout Left PGA", NULL, "Out FilterL"}, + {"Mixout Right PGA", NULL, "Out FilterR"}, + + {"Headphone Left PGA", NULL, "Mixout Left PGA"}, + {"Headphone Right PGA", NULL, "Mixout Right PGA"}, + + {"HPL", NULL, "Headphone Left PGA"}, + {"HPR", NULL, "Headphone Right PGA"}, + + {"HPL", NULL, "Charge Pump"}, + {"HPR", NULL, "Charge Pump"}, +}; + + +/* + * DAI operations + */ + +static int da7218_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + int ret; + + if (da7218->mclk_rate == freq) + return 0; + + if ((freq < 2000000) || (freq > 54000000)) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + + switch (clk_id) { + case DA7218_CLKSRC_MCLK_SQR: + snd_soc_component_update_bits(component, DA7218_PLL_CTRL, + DA7218_PLL_MCLK_SQR_EN_MASK, + DA7218_PLL_MCLK_SQR_EN_MASK); + break; + case DA7218_CLKSRC_MCLK: + snd_soc_component_update_bits(component, DA7218_PLL_CTRL, + DA7218_PLL_MCLK_SQR_EN_MASK, 0); + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } + + if (da7218->mclk) { + freq = clk_round_rate(da7218->mclk, freq); + ret = clk_set_rate(da7218->mclk, freq); + if (ret) { + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", + freq); + return ret; + } + } + + da7218->mclk_rate = freq; + + return 0; +} + +static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_component *component = codec_dai->component; + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + + u8 pll_ctrl, indiv_bits, indiv; + u8 pll_frac_top, pll_frac_bot, pll_integer; + u32 freq_ref; + u64 frac_div; + + /* Verify 2MHz - 54MHz MCLK provided, and set input divider */ + if (da7218->mclk_rate < 2000000) { + dev_err(component->dev, "PLL input clock %d below valid range\n", + da7218->mclk_rate); + return -EINVAL; + } else if (da7218->mclk_rate <= 4500000) { + indiv_bits = DA7218_PLL_INDIV_2_TO_4_5_MHZ; + indiv = DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL; + } else if (da7218->mclk_rate <= 9000000) { + indiv_bits = DA7218_PLL_INDIV_4_5_TO_9_MHZ; + indiv = DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL; + } else if (da7218->mclk_rate <= 18000000) { + indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL; + } else if (da7218->mclk_rate <= 36000000) { + indiv_bits = DA7218_PLL_INDIV_18_TO_36_MHZ; + indiv = DA7218_PLL_INDIV_18_TO_36_MHZ_VAL; + } else if (da7218->mclk_rate <= 54000000) { + indiv_bits = DA7218_PLL_INDIV_36_TO_54_MHZ; + indiv = DA7218_PLL_INDIV_36_TO_54_MHZ_VAL; + } else { + dev_err(component->dev, "PLL input clock %d above valid range\n", + da7218->mclk_rate); + return -EINVAL; + } + freq_ref = (da7218->mclk_rate / indiv); + pll_ctrl = indiv_bits; + + /* Configure PLL */ + switch (source) { + case DA7218_SYSCLK_MCLK: + pll_ctrl |= DA7218_PLL_MODE_BYPASS; + snd_soc_component_update_bits(component, DA7218_PLL_CTRL, + DA7218_PLL_INDIV_MASK | + DA7218_PLL_MODE_MASK, pll_ctrl); + return 0; + case DA7218_SYSCLK_PLL: + pll_ctrl |= DA7218_PLL_MODE_NORMAL; + break; + case DA7218_SYSCLK_PLL_SRM: + pll_ctrl |= DA7218_PLL_MODE_SRM; + break; + default: + dev_err(component->dev, "Invalid PLL config\n"); + return -EINVAL; + } + + /* Calculate dividers for PLL */ + pll_integer = fout / freq_ref; + frac_div = (u64)(fout % freq_ref) * 8192ULL; + do_div(frac_div, freq_ref); + pll_frac_top = (frac_div >> DA7218_BYTE_SHIFT) & DA7218_BYTE_MASK; + pll_frac_bot = (frac_div) & DA7218_BYTE_MASK; + + /* Write PLL config & dividers */ + snd_soc_component_write(component, DA7218_PLL_FRAC_TOP, pll_frac_top); + snd_soc_component_write(component, DA7218_PLL_FRAC_BOT, pll_frac_bot); + snd_soc_component_write(component, DA7218_PLL_INTEGER, pll_integer); + snd_soc_component_update_bits(component, DA7218_PLL_CTRL, + DA7218_PLL_MODE_MASK | DA7218_PLL_INDIV_MASK, + pll_ctrl); + + return 0; +} + +static int da7218_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + u8 dai_clk_mode = 0, dai_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da7218->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + da7218->master = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7218_DAI_WCLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + dai_clk_mode |= DA7218_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7218_DAI_WCLK_POL_INV | + DA7218_DAI_CLK_POL_INV; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_DSP_B: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + dai_clk_mode |= DA7218_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7218_DAI_WCLK_POL_INV | + DA7218_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7218_DAI_WCLK_POL_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_ctrl |= DA7218_DAI_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_ctrl |= DA7218_DAI_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dai_ctrl |= DA7218_DAI_FORMAT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_B: + dai_ctrl |= DA7218_DAI_FORMAT_DSP; + break; + default: + return -EINVAL; + } + + /* By default 64 BCLKs per WCLK is supported */ + dai_clk_mode |= DA7218_DAI_BCLKS_PER_WCLK_64; + + snd_soc_component_write(component, DA7218_DAI_CLK_MODE, dai_clk_mode); + snd_soc_component_update_bits(component, DA7218_DAI_CTRL, DA7218_DAI_FORMAT_MASK, + dai_ctrl); + + return 0; +} + +static int da7218_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + u8 dai_bclks_per_wclk; + u32 frame_size; + + /* No channels enabled so disable TDM, revert to 64-bit frames */ + if (!tx_mask) { + snd_soc_component_update_bits(component, DA7218_DAI_TDM_CTRL, + DA7218_DAI_TDM_CH_EN_MASK | + DA7218_DAI_TDM_MODE_EN_MASK, 0); + snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE, + DA7218_DAI_BCLKS_PER_WCLK_MASK, + DA7218_DAI_BCLKS_PER_WCLK_64); + return 0; + } + + /* Check we have valid slots */ + if (fls(tx_mask) > DA7218_DAI_TDM_MAX_SLOTS) { + dev_err(component->dev, "Invalid number of slots, max = %d\n", + DA7218_DAI_TDM_MAX_SLOTS); + return -EINVAL; + } + + /* Check we have a valid offset given (first 2 bytes of rx_mask) */ + if (rx_mask >> DA7218_2BYTE_SHIFT) { + dev_err(component->dev, "Invalid slot offset, max = %d\n", + DA7218_2BYTE_MASK); + return -EINVAL; + } + + /* Calculate & validate frame size based on slot info provided. */ + frame_size = slots * slot_width; + switch (frame_size) { + case 32: + dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_32; + break; + case 64: + dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_64; + break; + case 128: + dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_128; + break; + case 256: + dai_bclks_per_wclk = DA7218_DAI_BCLKS_PER_WCLK_256; + break; + default: + dev_err(component->dev, "Invalid frame size\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, DA7218_DAI_CLK_MODE, + DA7218_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + snd_soc_component_write(component, DA7218_DAI_OFFSET_LOWER, + (rx_mask & DA7218_BYTE_MASK)); + snd_soc_component_write(component, DA7218_DAI_OFFSET_UPPER, + ((rx_mask >> DA7218_BYTE_SHIFT) & DA7218_BYTE_MASK)); + snd_soc_component_update_bits(component, DA7218_DAI_TDM_CTRL, + DA7218_DAI_TDM_CH_EN_MASK | + DA7218_DAI_TDM_MODE_EN_MASK, + (tx_mask << DA7218_DAI_TDM_CH_EN_SHIFT) | + DA7218_DAI_TDM_MODE_EN_MASK); + + return 0; +} + +static int da7218_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u8 dai_ctrl = 0, fs; + unsigned int channels; + + switch (params_width(params)) { + case 16: + dai_ctrl |= DA7218_DAI_WORD_LENGTH_S16_LE; + break; + case 20: + dai_ctrl |= DA7218_DAI_WORD_LENGTH_S20_LE; + break; + case 24: + dai_ctrl |= DA7218_DAI_WORD_LENGTH_S24_LE; + break; + case 32: + dai_ctrl |= DA7218_DAI_WORD_LENGTH_S32_LE; + break; + default: + return -EINVAL; + } + + channels = params_channels(params); + if ((channels < 1) || (channels > DA7218_DAI_CH_NUM_MAX)) { + dev_err(component->dev, + "Invalid number of channels, only 1 to %d supported\n", + DA7218_DAI_CH_NUM_MAX); + return -EINVAL; + } + dai_ctrl |= channels << DA7218_DAI_CH_NUM_SHIFT; + + switch (params_rate(params)) { + case 8000: + fs = DA7218_SR_8000; + break; + case 11025: + fs = DA7218_SR_11025; + break; + case 12000: + fs = DA7218_SR_12000; + break; + case 16000: + fs = DA7218_SR_16000; + break; + case 22050: + fs = DA7218_SR_22050; + break; + case 24000: + fs = DA7218_SR_24000; + break; + case 32000: + fs = DA7218_SR_32000; + break; + case 44100: + fs = DA7218_SR_44100; + break; + case 48000: + fs = DA7218_SR_48000; + break; + case 88200: + fs = DA7218_SR_88200; + break; + case 96000: + fs = DA7218_SR_96000; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, DA7218_DAI_CTRL, + DA7218_DAI_WORD_LENGTH_MASK | DA7218_DAI_CH_NUM_MASK, + dai_ctrl); + /* SRs tied for ADCs and DACs. */ + snd_soc_component_write(component, DA7218_SR, + (fs << DA7218_SR_DAC_SHIFT) | (fs << DA7218_SR_ADC_SHIFT)); + + return 0; +} + +static const struct snd_soc_dai_ops da7218_dai_ops = { + .hw_params = da7218_hw_params, + .set_sysclk = da7218_set_dai_sysclk, + .set_pll = da7218_set_dai_pll, + .set_fmt = da7218_set_dai_fmt, + .set_tdm_slot = da7218_set_dai_tdm_slot, +}; + +#define DA7218_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver da7218_dai = { + .name = "da7218-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 4, /* Only 2 channels of data */ + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7218_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA7218_FORMATS, + }, + .ops = &da7218_dai_ops, + .symmetric_rate = 1, + .symmetric_channels = 1, + .symmetric_sample_bits = 1, +}; + + +/* + * HP Detect + */ + +int da7218_hpldet(struct snd_soc_component *component, struct snd_soc_jack *jack) +{ + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + + if (da7218->dev_id == DA7217_DEV_ID) + return -EINVAL; + + da7218->jack = jack; + snd_soc_component_update_bits(component, DA7218_HPLDET_JACK, + DA7218_HPLDET_JACK_EN_MASK, + jack ? DA7218_HPLDET_JACK_EN_MASK : 0); + + return 0; +} +EXPORT_SYMBOL_GPL(da7218_hpldet); + +static void da7218_micldet_irq(struct snd_soc_component *component) +{ + char *envp[] = { + "EVENT=MIC_LEVEL_DETECT", + NULL, + }; + + kobject_uevent_env(&component->dev->kobj, KOBJ_CHANGE, envp); +} + +static void da7218_hpldet_irq(struct snd_soc_component *component) +{ + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + u8 jack_status; + int report; + + jack_status = snd_soc_component_read(component, DA7218_EVENT_STATUS); + + if (jack_status & DA7218_HPLDET_JACK_STS_MASK) + report = SND_JACK_HEADPHONE; + else + report = 0; + + snd_soc_jack_report(da7218->jack, report, SND_JACK_HEADPHONE); +} + +/* + * IRQ + */ + +static irqreturn_t da7218_irq_thread(int irq, void *data) +{ + struct snd_soc_component *component = data; + u8 status; + + /* Read IRQ status reg */ + status = snd_soc_component_read(component, DA7218_EVENT); + if (!status) + return IRQ_NONE; + + /* Mic level detect */ + if (status & DA7218_LVL_DET_EVENT_MASK) + da7218_micldet_irq(component); + + /* HP detect */ + if (status & DA7218_HPLDET_JACK_EVENT_MASK) + da7218_hpldet_irq(component); + + /* Clear interrupts */ + snd_soc_component_write(component, DA7218_EVENT, status); + + return IRQ_HANDLED; +} + +/* + * DT + */ + +static const struct of_device_id da7218_of_match[] = { + { .compatible = "dlg,da7217", .data = (void *) DA7217_DEV_ID }, + { .compatible = "dlg,da7218", .data = (void *) DA7218_DEV_ID }, + { } +}; +MODULE_DEVICE_TABLE(of, da7218_of_match); + +static inline int da7218_of_get_id(struct device *dev) +{ + const struct of_device_id *id = of_match_device(da7218_of_match, dev); + + if (id) + return (uintptr_t)id->data; + else + return -EINVAL; +} + +static enum da7218_micbias_voltage + da7218_of_micbias_lvl(struct snd_soc_component *component, u32 val) +{ + switch (val) { + case 1200: + return DA7218_MICBIAS_1_2V; + case 1600: + return DA7218_MICBIAS_1_6V; + case 1800: + return DA7218_MICBIAS_1_8V; + case 2000: + return DA7218_MICBIAS_2_0V; + case 2200: + return DA7218_MICBIAS_2_2V; + case 2400: + return DA7218_MICBIAS_2_4V; + case 2600: + return DA7218_MICBIAS_2_6V; + case 2800: + return DA7218_MICBIAS_2_8V; + case 3000: + return DA7218_MICBIAS_3_0V; + default: + dev_warn(component->dev, "Invalid micbias level"); + return DA7218_MICBIAS_1_6V; + } +} + +static enum da7218_mic_amp_in_sel + da7218_of_mic_amp_in_sel(struct snd_soc_component *component, const char *str) +{ + if (!strcmp(str, "diff")) { + return DA7218_MIC_AMP_IN_SEL_DIFF; + } else if (!strcmp(str, "se_p")) { + return DA7218_MIC_AMP_IN_SEL_SE_P; + } else if (!strcmp(str, "se_n")) { + return DA7218_MIC_AMP_IN_SEL_SE_N; + } else { + dev_warn(component->dev, "Invalid mic input type selection"); + return DA7218_MIC_AMP_IN_SEL_DIFF; + } +} + +static enum da7218_dmic_data_sel + da7218_of_dmic_data_sel(struct snd_soc_component *component, const char *str) +{ + if (!strcmp(str, "lrise_rfall")) { + return DA7218_DMIC_DATA_LRISE_RFALL; + } else if (!strcmp(str, "lfall_rrise")) { + return DA7218_DMIC_DATA_LFALL_RRISE; + } else { + dev_warn(component->dev, "Invalid DMIC data type selection"); + return DA7218_DMIC_DATA_LRISE_RFALL; + } +} + +static enum da7218_dmic_samplephase + da7218_of_dmic_samplephase(struct snd_soc_component *component, const char *str) +{ + if (!strcmp(str, "on_clkedge")) { + return DA7218_DMIC_SAMPLE_ON_CLKEDGE; + } else if (!strcmp(str, "between_clkedge")) { + return DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE; + } else { + dev_warn(component->dev, "Invalid DMIC sample phase"); + return DA7218_DMIC_SAMPLE_ON_CLKEDGE; + } +} + +static enum da7218_dmic_clk_rate + da7218_of_dmic_clkrate(struct snd_soc_component *component, u32 val) +{ + switch (val) { + case 1500000: + return DA7218_DMIC_CLK_1_5MHZ; + case 3000000: + return DA7218_DMIC_CLK_3_0MHZ; + default: + dev_warn(component->dev, "Invalid DMIC clock rate"); + return DA7218_DMIC_CLK_3_0MHZ; + } +} + +static enum da7218_hpldet_jack_rate + da7218_of_jack_rate(struct snd_soc_component *component, u32 val) +{ + switch (val) { + case 5: + return DA7218_HPLDET_JACK_RATE_5US; + case 10: + return DA7218_HPLDET_JACK_RATE_10US; + case 20: + return DA7218_HPLDET_JACK_RATE_20US; + case 40: + return DA7218_HPLDET_JACK_RATE_40US; + case 80: + return DA7218_HPLDET_JACK_RATE_80US; + case 160: + return DA7218_HPLDET_JACK_RATE_160US; + case 320: + return DA7218_HPLDET_JACK_RATE_320US; + case 640: + return DA7218_HPLDET_JACK_RATE_640US; + default: + dev_warn(component->dev, "Invalid jack detect rate"); + return DA7218_HPLDET_JACK_RATE_40US; + } +} + +static enum da7218_hpldet_jack_debounce + da7218_of_jack_debounce(struct snd_soc_component *component, u32 val) +{ + switch (val) { + case 0: + return DA7218_HPLDET_JACK_DEBOUNCE_OFF; + case 2: + return DA7218_HPLDET_JACK_DEBOUNCE_2; + case 3: + return DA7218_HPLDET_JACK_DEBOUNCE_3; + case 4: + return DA7218_HPLDET_JACK_DEBOUNCE_4; + default: + dev_warn(component->dev, "Invalid jack debounce"); + return DA7218_HPLDET_JACK_DEBOUNCE_2; + } +} + +static enum da7218_hpldet_jack_thr + da7218_of_jack_thr(struct snd_soc_component *component, u32 val) +{ + switch (val) { + case 84: + return DA7218_HPLDET_JACK_THR_84PCT; + case 88: + return DA7218_HPLDET_JACK_THR_88PCT; + case 92: + return DA7218_HPLDET_JACK_THR_92PCT; + case 96: + return DA7218_HPLDET_JACK_THR_96PCT; + default: + dev_warn(component->dev, "Invalid jack threshold level"); + return DA7218_HPLDET_JACK_THR_84PCT; + } +} + +static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_component *component) +{ + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + struct device_node *np = component->dev->of_node; + struct device_node *hpldet_np; + struct da7218_pdata *pdata; + struct da7218_hpldet_pdata *hpldet_pdata; + const char *of_str; + u32 of_val32; + + pdata = devm_kzalloc(component->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "dlg,micbias1-lvl-millivolt", &of_val32) >= 0) + pdata->micbias1_lvl = da7218_of_micbias_lvl(component, of_val32); + else + pdata->micbias1_lvl = DA7218_MICBIAS_1_6V; + + if (of_property_read_u32(np, "dlg,micbias2-lvl-millivolt", &of_val32) >= 0) + pdata->micbias2_lvl = da7218_of_micbias_lvl(component, of_val32); + else + pdata->micbias2_lvl = DA7218_MICBIAS_1_6V; + + if (!of_property_read_string(np, "dlg,mic1-amp-in-sel", &of_str)) + pdata->mic1_amp_in_sel = + da7218_of_mic_amp_in_sel(component, of_str); + else + pdata->mic1_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF; + + if (!of_property_read_string(np, "dlg,mic2-amp-in-sel", &of_str)) + pdata->mic2_amp_in_sel = + da7218_of_mic_amp_in_sel(component, of_str); + else + pdata->mic2_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF; + + if (!of_property_read_string(np, "dlg,dmic1-data-sel", &of_str)) + pdata->dmic1_data_sel = da7218_of_dmic_data_sel(component, of_str); + else + pdata->dmic1_data_sel = DA7218_DMIC_DATA_LRISE_RFALL; + + if (!of_property_read_string(np, "dlg,dmic1-samplephase", &of_str)) + pdata->dmic1_samplephase = + da7218_of_dmic_samplephase(component, of_str); + else + pdata->dmic1_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE; + + if (of_property_read_u32(np, "dlg,dmic1-clkrate-hz", &of_val32) >= 0) + pdata->dmic1_clk_rate = da7218_of_dmic_clkrate(component, of_val32); + else + pdata->dmic1_clk_rate = DA7218_DMIC_CLK_3_0MHZ; + + if (!of_property_read_string(np, "dlg,dmic2-data-sel", &of_str)) + pdata->dmic2_data_sel = da7218_of_dmic_data_sel(component, of_str); + else + pdata->dmic2_data_sel = DA7218_DMIC_DATA_LRISE_RFALL; + + if (!of_property_read_string(np, "dlg,dmic2-samplephase", &of_str)) + pdata->dmic2_samplephase = + da7218_of_dmic_samplephase(component, of_str); + else + pdata->dmic2_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE; + + if (of_property_read_u32(np, "dlg,dmic2-clkrate-hz", &of_val32) >= 0) + pdata->dmic2_clk_rate = da7218_of_dmic_clkrate(component, of_val32); + else + pdata->dmic2_clk_rate = DA7218_DMIC_CLK_3_0MHZ; + + if (da7218->dev_id == DA7217_DEV_ID) { + if (of_property_read_bool(np, "dlg,hp-diff-single-supply")) + pdata->hp_diff_single_supply = true; + } + + if (da7218->dev_id == DA7218_DEV_ID) { + hpldet_np = of_get_child_by_name(np, "da7218_hpldet"); + if (!hpldet_np) + return pdata; + + hpldet_pdata = devm_kzalloc(component->dev, sizeof(*hpldet_pdata), + GFP_KERNEL); + if (!hpldet_pdata) { + of_node_put(hpldet_np); + return pdata; + } + pdata->hpldet_pdata = hpldet_pdata; + + if (of_property_read_u32(hpldet_np, "dlg,jack-rate-us", + &of_val32) >= 0) + hpldet_pdata->jack_rate = + da7218_of_jack_rate(component, of_val32); + else + hpldet_pdata->jack_rate = DA7218_HPLDET_JACK_RATE_40US; + + if (of_property_read_u32(hpldet_np, "dlg,jack-debounce", + &of_val32) >= 0) + hpldet_pdata->jack_debounce = + da7218_of_jack_debounce(component, of_val32); + else + hpldet_pdata->jack_debounce = + DA7218_HPLDET_JACK_DEBOUNCE_2; + + if (of_property_read_u32(hpldet_np, "dlg,jack-threshold-pct", + &of_val32) >= 0) + hpldet_pdata->jack_thr = + da7218_of_jack_thr(component, of_val32); + else + hpldet_pdata->jack_thr = DA7218_HPLDET_JACK_THR_84PCT; + + if (of_property_read_bool(hpldet_np, "dlg,comp-inv")) + hpldet_pdata->comp_inv = true; + + if (of_property_read_bool(hpldet_np, "dlg,hyst")) + hpldet_pdata->hyst = true; + + if (of_property_read_bool(hpldet_np, "dlg,discharge")) + hpldet_pdata->discharge = true; + + of_node_put(hpldet_np); + } + + return pdata; +} + + +/* + * Codec driver functions + */ + +static int da7218_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + /* Enable MCLK for transition to ON state */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) { + if (da7218->mclk) { + ret = clk_prepare_enable(da7218->mclk); + if (ret) { + dev_err(component->dev, "Failed to enable mclk\n"); + return ret; + } + } + } + + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Master bias */ + snd_soc_component_update_bits(component, DA7218_REFERENCES, + DA7218_BIAS_EN_MASK, + DA7218_BIAS_EN_MASK); + + /* Internal LDO */ + snd_soc_component_update_bits(component, DA7218_LDO_CTRL, + DA7218_LDO_EN_MASK, + DA7218_LDO_EN_MASK); + } else { + /* Remove MCLK */ + if (da7218->mclk) + clk_disable_unprepare(da7218->mclk); + } + break; + case SND_SOC_BIAS_OFF: + /* Only disable if jack detection disabled */ + if (!da7218->jack) { + /* Internal LDO */ + snd_soc_component_update_bits(component, DA7218_LDO_CTRL, + DA7218_LDO_EN_MASK, 0); + + /* Master bias */ + snd_soc_component_update_bits(component, DA7218_REFERENCES, + DA7218_BIAS_EN_MASK, 0); + } + break; + } + + return 0; +} + +static const char *da7218_supply_names[DA7218_NUM_SUPPLIES] = { + [DA7218_SUPPLY_VDD] = "VDD", + [DA7218_SUPPLY_VDDMIC] = "VDDMIC", + [DA7218_SUPPLY_VDDIO] = "VDDIO", +}; + +static int da7218_handle_supplies(struct snd_soc_component *component) +{ + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + struct regulator *vddio; + u8 io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_2_5V_3_6V; + int i, ret; + + /* Get required supplies */ + for (i = 0; i < DA7218_NUM_SUPPLIES; ++i) + da7218->supplies[i].supply = da7218_supply_names[i]; + + ret = devm_regulator_bulk_get(component->dev, DA7218_NUM_SUPPLIES, + da7218->supplies); + if (ret) { + dev_err(component->dev, "Failed to get supplies\n"); + return ret; + } + + /* Determine VDDIO voltage provided */ + vddio = da7218->supplies[DA7218_SUPPLY_VDDIO].consumer; + ret = regulator_get_voltage(vddio); + if (ret < 1500000) + dev_warn(component->dev, "Invalid VDDIO voltage\n"); + else if (ret < 2500000) + io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_1_5V_2_5V; + + /* Enable main supplies */ + ret = regulator_bulk_enable(DA7218_NUM_SUPPLIES, da7218->supplies); + if (ret) { + dev_err(component->dev, "Failed to enable supplies\n"); + return ret; + } + + /* Ensure device in active mode */ + snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE, DA7218_SYSTEM_ACTIVE_MASK); + + /* Update IO voltage level range */ + snd_soc_component_write(component, DA7218_IO_CTRL, io_voltage_lvl); + + return 0; +} + +static void da7218_handle_pdata(struct snd_soc_component *component) +{ + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + struct da7218_pdata *pdata = da7218->pdata; + + if (pdata) { + u8 micbias_lvl = 0, dmic_cfg = 0; + + /* Mic Bias voltages */ + switch (pdata->micbias1_lvl) { + case DA7218_MICBIAS_1_2V: + micbias_lvl |= DA7218_MICBIAS_1_LP_MODE_MASK; + break; + case DA7218_MICBIAS_1_6V: + case DA7218_MICBIAS_1_8V: + case DA7218_MICBIAS_2_0V: + case DA7218_MICBIAS_2_2V: + case DA7218_MICBIAS_2_4V: + case DA7218_MICBIAS_2_6V: + case DA7218_MICBIAS_2_8V: + case DA7218_MICBIAS_3_0V: + micbias_lvl |= (pdata->micbias1_lvl << + DA7218_MICBIAS_1_LEVEL_SHIFT); + break; + } + + switch (pdata->micbias2_lvl) { + case DA7218_MICBIAS_1_2V: + micbias_lvl |= DA7218_MICBIAS_2_LP_MODE_MASK; + break; + case DA7218_MICBIAS_1_6V: + case DA7218_MICBIAS_1_8V: + case DA7218_MICBIAS_2_0V: + case DA7218_MICBIAS_2_2V: + case DA7218_MICBIAS_2_4V: + case DA7218_MICBIAS_2_6V: + case DA7218_MICBIAS_2_8V: + case DA7218_MICBIAS_3_0V: + micbias_lvl |= (pdata->micbias2_lvl << + DA7218_MICBIAS_2_LEVEL_SHIFT); + break; + } + + snd_soc_component_write(component, DA7218_MICBIAS_CTRL, micbias_lvl); + + /* Mic */ + switch (pdata->mic1_amp_in_sel) { + case DA7218_MIC_AMP_IN_SEL_DIFF: + case DA7218_MIC_AMP_IN_SEL_SE_P: + case DA7218_MIC_AMP_IN_SEL_SE_N: + snd_soc_component_write(component, DA7218_MIC_1_SELECT, + pdata->mic1_amp_in_sel); + break; + } + + switch (pdata->mic2_amp_in_sel) { + case DA7218_MIC_AMP_IN_SEL_DIFF: + case DA7218_MIC_AMP_IN_SEL_SE_P: + case DA7218_MIC_AMP_IN_SEL_SE_N: + snd_soc_component_write(component, DA7218_MIC_2_SELECT, + pdata->mic2_amp_in_sel); + break; + } + + /* DMic */ + switch (pdata->dmic1_data_sel) { + case DA7218_DMIC_DATA_LFALL_RRISE: + case DA7218_DMIC_DATA_LRISE_RFALL: + dmic_cfg |= (pdata->dmic1_data_sel << + DA7218_DMIC_1_DATA_SEL_SHIFT); + break; + } + + switch (pdata->dmic1_samplephase) { + case DA7218_DMIC_SAMPLE_ON_CLKEDGE: + case DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE: + dmic_cfg |= (pdata->dmic1_samplephase << + DA7218_DMIC_1_SAMPLEPHASE_SHIFT); + break; + } + + switch (pdata->dmic1_clk_rate) { + case DA7218_DMIC_CLK_3_0MHZ: + case DA7218_DMIC_CLK_1_5MHZ: + dmic_cfg |= (pdata->dmic1_clk_rate << + DA7218_DMIC_1_CLK_RATE_SHIFT); + break; + } + + snd_soc_component_update_bits(component, DA7218_DMIC_1_CTRL, + DA7218_DMIC_1_DATA_SEL_MASK | + DA7218_DMIC_1_SAMPLEPHASE_MASK | + DA7218_DMIC_1_CLK_RATE_MASK, dmic_cfg); + + dmic_cfg = 0; + switch (pdata->dmic2_data_sel) { + case DA7218_DMIC_DATA_LFALL_RRISE: + case DA7218_DMIC_DATA_LRISE_RFALL: + dmic_cfg |= (pdata->dmic2_data_sel << + DA7218_DMIC_2_DATA_SEL_SHIFT); + break; + } + + switch (pdata->dmic2_samplephase) { + case DA7218_DMIC_SAMPLE_ON_CLKEDGE: + case DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE: + dmic_cfg |= (pdata->dmic2_samplephase << + DA7218_DMIC_2_SAMPLEPHASE_SHIFT); + break; + } + + switch (pdata->dmic2_clk_rate) { + case DA7218_DMIC_CLK_3_0MHZ: + case DA7218_DMIC_CLK_1_5MHZ: + dmic_cfg |= (pdata->dmic2_clk_rate << + DA7218_DMIC_2_CLK_RATE_SHIFT); + break; + } + + snd_soc_component_update_bits(component, DA7218_DMIC_2_CTRL, + DA7218_DMIC_2_DATA_SEL_MASK | + DA7218_DMIC_2_SAMPLEPHASE_MASK | + DA7218_DMIC_2_CLK_RATE_MASK, dmic_cfg); + + /* DA7217 Specific */ + if (da7218->dev_id == DA7217_DEV_ID) { + da7218->hp_single_supply = + pdata->hp_diff_single_supply; + + if (da7218->hp_single_supply) { + snd_soc_component_write(component, DA7218_HP_DIFF_UNLOCK, + DA7218_HP_DIFF_UNLOCK_VAL); + snd_soc_component_update_bits(component, DA7218_HP_DIFF_CTRL, + DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK, + DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK); + } + } + + /* DA7218 Specific */ + if ((da7218->dev_id == DA7218_DEV_ID) && + (pdata->hpldet_pdata)) { + struct da7218_hpldet_pdata *hpldet_pdata = + pdata->hpldet_pdata; + u8 hpldet_cfg = 0; + + switch (hpldet_pdata->jack_rate) { + case DA7218_HPLDET_JACK_RATE_5US: + case DA7218_HPLDET_JACK_RATE_10US: + case DA7218_HPLDET_JACK_RATE_20US: + case DA7218_HPLDET_JACK_RATE_40US: + case DA7218_HPLDET_JACK_RATE_80US: + case DA7218_HPLDET_JACK_RATE_160US: + case DA7218_HPLDET_JACK_RATE_320US: + case DA7218_HPLDET_JACK_RATE_640US: + hpldet_cfg |= + (hpldet_pdata->jack_rate << + DA7218_HPLDET_JACK_RATE_SHIFT); + break; + } + + switch (hpldet_pdata->jack_debounce) { + case DA7218_HPLDET_JACK_DEBOUNCE_OFF: + case DA7218_HPLDET_JACK_DEBOUNCE_2: + case DA7218_HPLDET_JACK_DEBOUNCE_3: + case DA7218_HPLDET_JACK_DEBOUNCE_4: + hpldet_cfg |= + (hpldet_pdata->jack_debounce << + DA7218_HPLDET_JACK_DEBOUNCE_SHIFT); + break; + } + + switch (hpldet_pdata->jack_thr) { + case DA7218_HPLDET_JACK_THR_84PCT: + case DA7218_HPLDET_JACK_THR_88PCT: + case DA7218_HPLDET_JACK_THR_92PCT: + case DA7218_HPLDET_JACK_THR_96PCT: + hpldet_cfg |= + (hpldet_pdata->jack_thr << + DA7218_HPLDET_JACK_THR_SHIFT); + break; + } + snd_soc_component_update_bits(component, DA7218_HPLDET_JACK, + DA7218_HPLDET_JACK_RATE_MASK | + DA7218_HPLDET_JACK_DEBOUNCE_MASK | + DA7218_HPLDET_JACK_THR_MASK, + hpldet_cfg); + + hpldet_cfg = 0; + if (hpldet_pdata->comp_inv) + hpldet_cfg |= DA7218_HPLDET_COMP_INV_MASK; + + if (hpldet_pdata->hyst) + hpldet_cfg |= DA7218_HPLDET_HYST_EN_MASK; + + if (hpldet_pdata->discharge) + hpldet_cfg |= DA7218_HPLDET_DISCHARGE_EN_MASK; + + snd_soc_component_write(component, DA7218_HPLDET_CTRL, hpldet_cfg); + } + } +} + +static int da7218_probe(struct snd_soc_component *component) +{ + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + int ret; + + /* Regulator configuration */ + ret = da7218_handle_supplies(component); + if (ret) + return ret; + + /* Handle DT/Platform data */ + if (component->dev->of_node) + da7218->pdata = da7218_of_to_pdata(component); + else + da7218->pdata = dev_get_platdata(component->dev); + + da7218_handle_pdata(component); + + /* Check if MCLK provided, if not the clock is NULL */ + da7218->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(da7218->mclk)) { + ret = PTR_ERR(da7218->mclk); + goto err_disable_reg; + } + + /* Default PC to free-running */ + snd_soc_component_write(component, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK); + + /* + * Default Output Filter mixers to off otherwise DAPM will power + * Mic to HP passthrough paths by default at startup. + */ + snd_soc_component_write(component, DA7218_DROUTING_OUTFILT_1L, 0); + snd_soc_component_write(component, DA7218_DROUTING_OUTFILT_1R, 0); + + /* Default CP to normal load, power mode */ + snd_soc_component_update_bits(component, DA7218_CP_CTRL, + DA7218_CP_SMALL_SWITCH_FREQ_EN_MASK, 0); + + /* Default gain ramping */ + snd_soc_component_update_bits(component, DA7218_MIXIN_1_CTRL, + DA7218_MIXIN_1_AMP_RAMP_EN_MASK, + DA7218_MIXIN_1_AMP_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_MIXIN_2_CTRL, + DA7218_MIXIN_2_AMP_RAMP_EN_MASK, + DA7218_MIXIN_2_AMP_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_IN_1L_FILTER_CTRL, + DA7218_IN_1L_RAMP_EN_MASK, + DA7218_IN_1L_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_IN_1R_FILTER_CTRL, + DA7218_IN_1R_RAMP_EN_MASK, + DA7218_IN_1R_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_IN_2L_FILTER_CTRL, + DA7218_IN_2L_RAMP_EN_MASK, + DA7218_IN_2L_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_IN_2R_FILTER_CTRL, + DA7218_IN_2R_RAMP_EN_MASK, + DA7218_IN_2R_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_DGS_GAIN_CTRL, + DA7218_DGS_RAMP_EN_MASK, DA7218_DGS_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_OUT_1L_FILTER_CTRL, + DA7218_OUT_1L_RAMP_EN_MASK, + DA7218_OUT_1L_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_OUT_1R_FILTER_CTRL, + DA7218_OUT_1R_RAMP_EN_MASK, + DA7218_OUT_1R_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_HP_L_CTRL, + DA7218_HP_L_AMP_RAMP_EN_MASK, + DA7218_HP_L_AMP_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7218_HP_R_CTRL, + DA7218_HP_R_AMP_RAMP_EN_MASK, + DA7218_HP_R_AMP_RAMP_EN_MASK); + + /* Default infinite tone gen, start/stop by Kcontrol */ + snd_soc_component_write(component, DA7218_TONE_GEN_CYCLES, DA7218_BEEP_CYCLES_MASK); + + /* DA7217 specific config */ + if (da7218->dev_id == DA7217_DEV_ID) { + snd_soc_component_update_bits(component, DA7218_HP_DIFF_CTRL, + DA7218_HP_AMP_DIFF_MODE_EN_MASK, + DA7218_HP_AMP_DIFF_MODE_EN_MASK); + + /* Only DA7218 supports HP detect, mask off for DA7217 */ + snd_soc_component_write(component, DA7218_EVENT_MASK, + DA7218_HPLDET_JACK_EVENT_IRQ_MSK_MASK); + } + + if (da7218->irq) { + ret = devm_request_threaded_irq(component->dev, da7218->irq, NULL, + da7218_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "da7218", component); + if (ret != 0) { + dev_err(component->dev, "Failed to request IRQ %d: %d\n", + da7218->irq, ret); + goto err_disable_reg; + } + + } + + return 0; + +err_disable_reg: + regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies); + + return ret; +} + +static void da7218_remove(struct snd_soc_component *component) +{ + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + + regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies); +} + +#ifdef CONFIG_PM +static int da7218_suspend(struct snd_soc_component *component) +{ + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + + da7218_set_bias_level(component, SND_SOC_BIAS_OFF); + + /* Put device into standby mode if jack detection disabled */ + if (!da7218->jack) + snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE, 0); + + return 0; +} + +static int da7218_resume(struct snd_soc_component *component) +{ + struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component); + + /* Put device into active mode if previously moved to standby */ + if (!da7218->jack) + snd_soc_component_write(component, DA7218_SYSTEM_ACTIVE, + DA7218_SYSTEM_ACTIVE_MASK); + + da7218_set_bias_level(component, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define da7218_suspend NULL +#define da7218_resume NULL +#endif + +static const struct snd_soc_component_driver soc_component_dev_da7218 = { + .probe = da7218_probe, + .remove = da7218_remove, + .suspend = da7218_suspend, + .resume = da7218_resume, + .set_bias_level = da7218_set_bias_level, + .controls = da7218_snd_controls, + .num_controls = ARRAY_SIZE(da7218_snd_controls), + .dapm_widgets = da7218_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7218_dapm_widgets), + .dapm_routes = da7218_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7218_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + + +/* + * Regmap configs + */ + +static struct reg_default da7218_reg_defaults[] = { + { DA7218_SYSTEM_ACTIVE, 0x00 }, + { DA7218_CIF_CTRL, 0x00 }, + { DA7218_SPARE1, 0x00 }, + { DA7218_SR, 0xAA }, + { DA7218_PC_COUNT, 0x02 }, + { DA7218_GAIN_RAMP_CTRL, 0x00 }, + { DA7218_CIF_TIMEOUT_CTRL, 0x01 }, + { DA7218_SYSTEM_MODES_INPUT, 0x00 }, + { DA7218_SYSTEM_MODES_OUTPUT, 0x00 }, + { DA7218_IN_1L_FILTER_CTRL, 0x00 }, + { DA7218_IN_1R_FILTER_CTRL, 0x00 }, + { DA7218_IN_2L_FILTER_CTRL, 0x00 }, + { DA7218_IN_2R_FILTER_CTRL, 0x00 }, + { DA7218_OUT_1L_FILTER_CTRL, 0x40 }, + { DA7218_OUT_1R_FILTER_CTRL, 0x40 }, + { DA7218_OUT_1_HPF_FILTER_CTRL, 0x80 }, + { DA7218_OUT_1_EQ_12_FILTER_CTRL, 0x77 }, + { DA7218_OUT_1_EQ_34_FILTER_CTRL, 0x77 }, + { DA7218_OUT_1_EQ_5_FILTER_CTRL, 0x07 }, + { DA7218_OUT_1_BIQ_5STAGE_CTRL, 0x40 }, + { DA7218_OUT_1_BIQ_5STAGE_DATA, 0x00 }, + { DA7218_OUT_1_BIQ_5STAGE_ADDR, 0x00 }, + { DA7218_MIXIN_1_CTRL, 0x48 }, + { DA7218_MIXIN_1_GAIN, 0x03 }, + { DA7218_MIXIN_2_CTRL, 0x48 }, + { DA7218_MIXIN_2_GAIN, 0x03 }, + { DA7218_ALC_CTRL1, 0x00 }, + { DA7218_ALC_CTRL2, 0x00 }, + { DA7218_ALC_CTRL3, 0x00 }, + { DA7218_ALC_NOISE, 0x3F }, + { DA7218_ALC_TARGET_MIN, 0x3F }, + { DA7218_ALC_TARGET_MAX, 0x00 }, + { DA7218_ALC_GAIN_LIMITS, 0xFF }, + { DA7218_ALC_ANA_GAIN_LIMITS, 0x71 }, + { DA7218_ALC_ANTICLIP_CTRL, 0x00 }, + { DA7218_AGS_ENABLE, 0x00 }, + { DA7218_AGS_TRIGGER, 0x09 }, + { DA7218_AGS_ATT_MAX, 0x00 }, + { DA7218_AGS_TIMEOUT, 0x00 }, + { DA7218_AGS_ANTICLIP_CTRL, 0x00 }, + { DA7218_ENV_TRACK_CTRL, 0x00 }, + { DA7218_LVL_DET_CTRL, 0x00 }, + { DA7218_LVL_DET_LEVEL, 0x7F }, + { DA7218_DGS_TRIGGER, 0x24 }, + { DA7218_DGS_ENABLE, 0x00 }, + { DA7218_DGS_RISE_FALL, 0x50 }, + { DA7218_DGS_SYNC_DELAY, 0xA3 }, + { DA7218_DGS_SYNC_DELAY2, 0x31 }, + { DA7218_DGS_SYNC_DELAY3, 0x11 }, + { DA7218_DGS_LEVELS, 0x01 }, + { DA7218_DGS_GAIN_CTRL, 0x74 }, + { DA7218_DROUTING_OUTDAI_1L, 0x01 }, + { DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN, 0x1C }, + { DA7218_DROUTING_OUTDAI_1R, 0x04 }, + { DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN, 0x1C }, + { DA7218_DROUTING_OUTFILT_1L, 0x01 }, + { DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN, 0x1C }, + { DA7218_DROUTING_OUTFILT_1R, 0x04 }, + { DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN, 0x1C }, + { DA7218_DROUTING_OUTDAI_2L, 0x04 }, + { DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN, 0x1C }, + { DA7218_DROUTING_OUTDAI_2R, 0x08 }, + { DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN, 0x1C }, + { DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN, 0x1C }, + { DA7218_DAI_CTRL, 0x28 }, + { DA7218_DAI_TDM_CTRL, 0x40 }, + { DA7218_DAI_OFFSET_LOWER, 0x00 }, + { DA7218_DAI_OFFSET_UPPER, 0x00 }, + { DA7218_DAI_CLK_MODE, 0x01 }, + { DA7218_PLL_CTRL, 0x04 }, + { DA7218_PLL_FRAC_TOP, 0x00 }, + { DA7218_PLL_FRAC_BOT, 0x00 }, + { DA7218_PLL_INTEGER, 0x20 }, + { DA7218_DAC_NG_CTRL, 0x00 }, + { DA7218_DAC_NG_SETUP_TIME, 0x00 }, + { DA7218_DAC_NG_OFF_THRESH, 0x00 }, + { DA7218_DAC_NG_ON_THRESH, 0x00 }, + { DA7218_TONE_GEN_CFG2, 0x00 }, + { DA7218_TONE_GEN_FREQ1_L, 0x55 }, + { DA7218_TONE_GEN_FREQ1_U, 0x15 }, + { DA7218_TONE_GEN_FREQ2_L, 0x00 }, + { DA7218_TONE_GEN_FREQ2_U, 0x40 }, + { DA7218_TONE_GEN_CYCLES, 0x00 }, + { DA7218_TONE_GEN_ON_PER, 0x02 }, + { DA7218_TONE_GEN_OFF_PER, 0x01 }, + { DA7218_CP_CTRL, 0x60 }, + { DA7218_CP_DELAY, 0x11 }, + { DA7218_CP_VOL_THRESHOLD1, 0x0E }, + { DA7218_MIC_1_CTRL, 0x40 }, + { DA7218_MIC_1_GAIN, 0x01 }, + { DA7218_MIC_1_SELECT, 0x00 }, + { DA7218_MIC_2_CTRL, 0x40 }, + { DA7218_MIC_2_GAIN, 0x01 }, + { DA7218_MIC_2_SELECT, 0x00 }, + { DA7218_IN_1_HPF_FILTER_CTRL, 0x80 }, + { DA7218_IN_2_HPF_FILTER_CTRL, 0x80 }, + { DA7218_ADC_1_CTRL, 0x07 }, + { DA7218_ADC_2_CTRL, 0x07 }, + { DA7218_MIXOUT_L_CTRL, 0x00 }, + { DA7218_MIXOUT_L_GAIN, 0x03 }, + { DA7218_MIXOUT_R_CTRL, 0x00 }, + { DA7218_MIXOUT_R_GAIN, 0x03 }, + { DA7218_HP_L_CTRL, 0x40 }, + { DA7218_HP_L_GAIN, 0x3B }, + { DA7218_HP_R_CTRL, 0x40 }, + { DA7218_HP_R_GAIN, 0x3B }, + { DA7218_HP_DIFF_CTRL, 0x00 }, + { DA7218_HP_DIFF_UNLOCK, 0xC3 }, + { DA7218_HPLDET_JACK, 0x0B }, + { DA7218_HPLDET_CTRL, 0x00 }, + { DA7218_REFERENCES, 0x08 }, + { DA7218_IO_CTRL, 0x00 }, + { DA7218_LDO_CTRL, 0x00 }, + { DA7218_SIDETONE_CTRL, 0x40 }, + { DA7218_SIDETONE_IN_SELECT, 0x00 }, + { DA7218_SIDETONE_GAIN, 0x1C }, + { DA7218_DROUTING_ST_OUTFILT_1L, 0x01 }, + { DA7218_DROUTING_ST_OUTFILT_1R, 0x02 }, + { DA7218_SIDETONE_BIQ_3STAGE_DATA, 0x00 }, + { DA7218_SIDETONE_BIQ_3STAGE_ADDR, 0x00 }, + { DA7218_EVENT_MASK, 0x00 }, + { DA7218_DMIC_1_CTRL, 0x00 }, + { DA7218_DMIC_2_CTRL, 0x00 }, + { DA7218_IN_1L_GAIN, 0x6F }, + { DA7218_IN_1R_GAIN, 0x6F }, + { DA7218_IN_2L_GAIN, 0x6F }, + { DA7218_IN_2R_GAIN, 0x6F }, + { DA7218_OUT_1L_GAIN, 0x6F }, + { DA7218_OUT_1R_GAIN, 0x6F }, + { DA7218_MICBIAS_CTRL, 0x00 }, + { DA7218_MICBIAS_EN, 0x00 }, +}; + +static bool da7218_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA7218_STATUS1: + case DA7218_SOFT_RESET: + case DA7218_SYSTEM_STATUS: + case DA7218_CALIB_CTRL: + case DA7218_CALIB_OFFSET_AUTO_M_1: + case DA7218_CALIB_OFFSET_AUTO_U_1: + case DA7218_CALIB_OFFSET_AUTO_M_2: + case DA7218_CALIB_OFFSET_AUTO_U_2: + case DA7218_PLL_STATUS: + case DA7218_PLL_REFOSC_CAL: + case DA7218_TONE_GEN_CFG1: + case DA7218_ADC_MODE: + case DA7218_HP_SNGL_CTRL: + case DA7218_HPLDET_TEST: + case DA7218_EVENT_STATUS: + case DA7218_EVENT: + return true; + default: + return false; + } +} + +static const struct regmap_config da7218_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DA7218_MICBIAS_EN, + .reg_defaults = da7218_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7218_reg_defaults), + .volatile_reg = da7218_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + + +/* + * I2C layer + */ + +static const struct i2c_device_id da7218_i2c_id[]; + +static inline int da7218_i2c_get_id(struct i2c_client *i2c) +{ + const struct i2c_device_id *id = i2c_match_id(da7218_i2c_id, i2c); + + if (id) + return (uintptr_t)id->driver_data; + else + return -EINVAL; +} + +static int da7218_i2c_probe(struct i2c_client *i2c) +{ + struct da7218_priv *da7218; + int ret; + + da7218 = devm_kzalloc(&i2c->dev, sizeof(*da7218), GFP_KERNEL); + if (!da7218) + return -ENOMEM; + + i2c_set_clientdata(i2c, da7218); + + if (i2c->dev.of_node) + da7218->dev_id = da7218_of_get_id(&i2c->dev); + else + da7218->dev_id = da7218_i2c_get_id(i2c); + + if ((da7218->dev_id != DA7217_DEV_ID) && + (da7218->dev_id != DA7218_DEV_ID)) { + dev_err(&i2c->dev, "Invalid device Id\n"); + return -EINVAL; + } + + da7218->irq = i2c->irq; + + da7218->regmap = devm_regmap_init_i2c(i2c, &da7218_regmap_config); + if (IS_ERR(da7218->regmap)) { + ret = PTR_ERR(da7218->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_da7218, &da7218_dai, 1); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register da7218 component: %d\n", + ret); + } + return ret; +} + +static const struct i2c_device_id da7218_i2c_id[] = { + { "da7217", DA7217_DEV_ID }, + { "da7218", DA7218_DEV_ID }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da7218_i2c_id); + +static struct i2c_driver da7218_i2c_driver = { + .driver = { + .name = "da7218", + .of_match_table = da7218_of_match, + }, + .probe = da7218_i2c_probe, + .id_table = da7218_i2c_id, +}; + +module_i2c_driver(da7218_i2c_driver); + +MODULE_DESCRIPTION("ASoC DA7218 Codec driver"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h new file mode 100644 index 0000000000..9ac2892092 --- /dev/null +++ b/sound/soc/codecs/da7218.h @@ -0,0 +1,1411 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * da7218.h - DA7218 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson + */ + +#ifndef _DA7218_H +#define _DA7218_H + +#include +#include +#include + + +/* + * Registers + */ +#define DA7218_SYSTEM_ACTIVE 0x0 +#define DA7218_CIF_CTRL 0x1 +#define DA7218_CHIP_ID1 0x4 +#define DA7218_CHIP_ID2 0x5 +#define DA7218_CHIP_REVISION 0x6 +#define DA7218_SPARE1 0x7 +#define DA7218_STATUS1 0x8 +#define DA7218_SOFT_RESET 0x9 +#define DA7218_SR 0xB +#define DA7218_PC_COUNT 0xC +#define DA7218_GAIN_RAMP_CTRL 0xD +#define DA7218_CIF_TIMEOUT_CTRL 0x10 +#define DA7218_SYSTEM_MODES_INPUT 0x14 +#define DA7218_SYSTEM_MODES_OUTPUT 0x15 +#define DA7218_SYSTEM_STATUS 0x16 +#define DA7218_IN_1L_FILTER_CTRL 0x18 +#define DA7218_IN_1R_FILTER_CTRL 0x19 +#define DA7218_IN_2L_FILTER_CTRL 0x1A +#define DA7218_IN_2R_FILTER_CTRL 0x1B +#define DA7218_OUT_1L_FILTER_CTRL 0x20 +#define DA7218_OUT_1R_FILTER_CTRL 0x21 +#define DA7218_OUT_1_HPF_FILTER_CTRL 0x24 +#define DA7218_OUT_1_EQ_12_FILTER_CTRL 0x25 +#define DA7218_OUT_1_EQ_34_FILTER_CTRL 0x26 +#define DA7218_OUT_1_EQ_5_FILTER_CTRL 0x27 +#define DA7218_OUT_1_BIQ_5STAGE_CTRL 0x28 +#define DA7218_OUT_1_BIQ_5STAGE_DATA 0x29 +#define DA7218_OUT_1_BIQ_5STAGE_ADDR 0x2A +#define DA7218_MIXIN_1_CTRL 0x2C +#define DA7218_MIXIN_1_GAIN 0x2D +#define DA7218_MIXIN_2_CTRL 0x2E +#define DA7218_MIXIN_2_GAIN 0x2F +#define DA7218_ALC_CTRL1 0x30 +#define DA7218_ALC_CTRL2 0x31 +#define DA7218_ALC_CTRL3 0x32 +#define DA7218_ALC_NOISE 0x33 +#define DA7218_ALC_TARGET_MIN 0x34 +#define DA7218_ALC_TARGET_MAX 0x35 +#define DA7218_ALC_GAIN_LIMITS 0x36 +#define DA7218_ALC_ANA_GAIN_LIMITS 0x37 +#define DA7218_ALC_ANTICLIP_CTRL 0x38 +#define DA7218_AGS_ENABLE 0x3C +#define DA7218_AGS_TRIGGER 0x3D +#define DA7218_AGS_ATT_MAX 0x3E +#define DA7218_AGS_TIMEOUT 0x3F +#define DA7218_AGS_ANTICLIP_CTRL 0x40 +#define DA7218_CALIB_CTRL 0x44 +#define DA7218_CALIB_OFFSET_AUTO_M_1 0x45 +#define DA7218_CALIB_OFFSET_AUTO_U_1 0x46 +#define DA7218_CALIB_OFFSET_AUTO_M_2 0x47 +#define DA7218_CALIB_OFFSET_AUTO_U_2 0x48 +#define DA7218_ENV_TRACK_CTRL 0x4C +#define DA7218_LVL_DET_CTRL 0x50 +#define DA7218_LVL_DET_LEVEL 0x51 +#define DA7218_DGS_TRIGGER 0x54 +#define DA7218_DGS_ENABLE 0x55 +#define DA7218_DGS_RISE_FALL 0x56 +#define DA7218_DGS_SYNC_DELAY 0x57 +#define DA7218_DGS_SYNC_DELAY2 0x58 +#define DA7218_DGS_SYNC_DELAY3 0x59 +#define DA7218_DGS_LEVELS 0x5A +#define DA7218_DGS_GAIN_CTRL 0x5B +#define DA7218_DROUTING_OUTDAI_1L 0x5C +#define DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN 0x5D +#define DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN 0x5E +#define DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN 0x5F +#define DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN 0x60 +#define DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN 0x61 +#define DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN 0x62 +#define DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN 0x63 +#define DA7218_DROUTING_OUTDAI_1R 0x64 +#define DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN 0x65 +#define DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN 0x66 +#define DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN 0x67 +#define DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN 0x68 +#define DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN 0x69 +#define DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN 0x6A +#define DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN 0x6B +#define DA7218_DROUTING_OUTFILT_1L 0x6C +#define DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN 0x6D +#define DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN 0x6E +#define DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN 0x6F +#define DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN 0x70 +#define DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN 0x71 +#define DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN 0x72 +#define DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN 0x73 +#define DA7218_DROUTING_OUTFILT_1R 0x74 +#define DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN 0x75 +#define DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN 0x76 +#define DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN 0x77 +#define DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN 0x78 +#define DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN 0x79 +#define DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN 0x7A +#define DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN 0x7B +#define DA7218_DROUTING_OUTDAI_2L 0x7C +#define DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN 0x7D +#define DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN 0x7E +#define DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN 0x7F +#define DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN 0x80 +#define DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN 0x81 +#define DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN 0x82 +#define DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN 0x83 +#define DA7218_DROUTING_OUTDAI_2R 0x84 +#define DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN 0x85 +#define DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN 0x86 +#define DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN 0x87 +#define DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN 0x88 +#define DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN 0x89 +#define DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN 0x8A +#define DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN 0x8B +#define DA7218_DAI_CTRL 0x8C +#define DA7218_DAI_TDM_CTRL 0x8D +#define DA7218_DAI_OFFSET_LOWER 0x8E +#define DA7218_DAI_OFFSET_UPPER 0x8F +#define DA7218_DAI_CLK_MODE 0x90 +#define DA7218_PLL_CTRL 0x91 +#define DA7218_PLL_FRAC_TOP 0x92 +#define DA7218_PLL_FRAC_BOT 0x93 +#define DA7218_PLL_INTEGER 0x94 +#define DA7218_PLL_STATUS 0x95 +#define DA7218_PLL_REFOSC_CAL 0x98 +#define DA7218_DAC_NG_CTRL 0x9C +#define DA7218_DAC_NG_SETUP_TIME 0x9D +#define DA7218_DAC_NG_OFF_THRESH 0x9E +#define DA7218_DAC_NG_ON_THRESH 0x9F +#define DA7218_TONE_GEN_CFG1 0xA0 +#define DA7218_TONE_GEN_CFG2 0xA1 +#define DA7218_TONE_GEN_FREQ1_L 0xA2 +#define DA7218_TONE_GEN_FREQ1_U 0xA3 +#define DA7218_TONE_GEN_FREQ2_L 0xA4 +#define DA7218_TONE_GEN_FREQ2_U 0xA5 +#define DA7218_TONE_GEN_CYCLES 0xA6 +#define DA7218_TONE_GEN_ON_PER 0xA7 +#define DA7218_TONE_GEN_OFF_PER 0xA8 +#define DA7218_CP_CTRL 0xAC +#define DA7218_CP_DELAY 0xAD +#define DA7218_CP_VOL_THRESHOLD1 0xAE +#define DA7218_MIC_1_CTRL 0xB4 +#define DA7218_MIC_1_GAIN 0xB5 +#define DA7218_MIC_1_SELECT 0xB7 +#define DA7218_MIC_2_CTRL 0xB8 +#define DA7218_MIC_2_GAIN 0xB9 +#define DA7218_MIC_2_SELECT 0xBB +#define DA7218_IN_1_HPF_FILTER_CTRL 0xBC +#define DA7218_IN_2_HPF_FILTER_CTRL 0xBD +#define DA7218_ADC_1_CTRL 0xC0 +#define DA7218_ADC_2_CTRL 0xC1 +#define DA7218_ADC_MODE 0xC2 +#define DA7218_MIXOUT_L_CTRL 0xCC +#define DA7218_MIXOUT_L_GAIN 0xCD +#define DA7218_MIXOUT_R_CTRL 0xCE +#define DA7218_MIXOUT_R_GAIN 0xCF +#define DA7218_HP_L_CTRL 0xD0 +#define DA7218_HP_L_GAIN 0xD1 +#define DA7218_HP_R_CTRL 0xD2 +#define DA7218_HP_R_GAIN 0xD3 +#define DA7218_HP_SNGL_CTRL 0xD4 +#define DA7218_HP_DIFF_CTRL 0xD5 +#define DA7218_HP_DIFF_UNLOCK 0xD7 +#define DA7218_HPLDET_JACK 0xD8 +#define DA7218_HPLDET_CTRL 0xD9 +#define DA7218_HPLDET_TEST 0xDA +#define DA7218_REFERENCES 0xDC +#define DA7218_IO_CTRL 0xE0 +#define DA7218_LDO_CTRL 0xE1 +#define DA7218_SIDETONE_CTRL 0xE4 +#define DA7218_SIDETONE_IN_SELECT 0xE5 +#define DA7218_SIDETONE_GAIN 0xE6 +#define DA7218_DROUTING_ST_OUTFILT_1L 0xE8 +#define DA7218_DROUTING_ST_OUTFILT_1R 0xE9 +#define DA7218_SIDETONE_BIQ_3STAGE_DATA 0xEA +#define DA7218_SIDETONE_BIQ_3STAGE_ADDR 0xEB +#define DA7218_EVENT_STATUS 0xEC +#define DA7218_EVENT 0xED +#define DA7218_EVENT_MASK 0xEE +#define DA7218_DMIC_1_CTRL 0xF0 +#define DA7218_DMIC_2_CTRL 0xF1 +#define DA7218_IN_1L_GAIN 0xF4 +#define DA7218_IN_1R_GAIN 0xF5 +#define DA7218_IN_2L_GAIN 0xF6 +#define DA7218_IN_2R_GAIN 0xF7 +#define DA7218_OUT_1L_GAIN 0xF8 +#define DA7218_OUT_1R_GAIN 0xF9 +#define DA7218_MICBIAS_CTRL 0xFC +#define DA7218_MICBIAS_EN 0xFD + + +/* + * Bit Fields + */ + +#define DA7218_SWITCH_EN_MAX 0x1 + +/* DA7218_SYSTEM_ACTIVE = 0x0 */ +#define DA7218_SYSTEM_ACTIVE_SHIFT 0 +#define DA7218_SYSTEM_ACTIVE_MASK (0x1 << 0) + +/* DA7218_CIF_CTRL = 0x1 */ +#define DA7218_CIF_I2C_WRITE_MODE_SHIFT 0 +#define DA7218_CIF_I2C_WRITE_MODE_MASK (0x1 << 0) + +/* DA7218_CHIP_ID1 = 0x4 */ +#define DA7218_CHIP_ID1_SHIFT 0 +#define DA7218_CHIP_ID1_MASK (0xFF << 0) + +/* DA7218_CHIP_ID2 = 0x5 */ +#define DA7218_CHIP_ID2_SHIFT 0 +#define DA7218_CHIP_ID2_MASK (0xFF << 0) + +/* DA7218_CHIP_REVISION = 0x6 */ +#define DA7218_CHIP_MINOR_SHIFT 0 +#define DA7218_CHIP_MINOR_MASK (0xF << 0) +#define DA7218_CHIP_MAJOR_SHIFT 4 +#define DA7218_CHIP_MAJOR_MASK (0xF << 4) + +/* DA7218_SPARE1 = 0x7 */ +#define DA7218_SPARE1_SHIFT 0 +#define DA7218_SPARE1_MASK (0xFF << 0) + +/* DA7218_STATUS1 = 0x8 */ +#define DA7218_STATUS_SPARE1_SHIFT 0 +#define DA7218_STATUS_SPARE1_MASK (0xFF << 0) + +/* DA7218_SOFT_RESET = 0x9 */ +#define DA7218_CIF_REG_SOFT_RESET_SHIFT 7 +#define DA7218_CIF_REG_SOFT_RESET_MASK (0x1 << 7) + +/* DA7218_SR = 0xB */ +#define DA7218_SR_ADC_SHIFT 0 +#define DA7218_SR_ADC_MASK (0xF << 0) +#define DA7218_SR_DAC_SHIFT 4 +#define DA7218_SR_DAC_MASK (0xF << 4) +#define DA7218_SR_8000 0x01 +#define DA7218_SR_11025 0x02 +#define DA7218_SR_12000 0x03 +#define DA7218_SR_16000 0x05 +#define DA7218_SR_22050 0x06 +#define DA7218_SR_24000 0x07 +#define DA7218_SR_32000 0x09 +#define DA7218_SR_44100 0x0A +#define DA7218_SR_48000 0x0B +#define DA7218_SR_88200 0x0E +#define DA7218_SR_96000 0x0F + +/* DA7218_PC_COUNT = 0xC */ +#define DA7218_PC_FREERUN_SHIFT 0 +#define DA7218_PC_FREERUN_MASK (0x1 << 0) +#define DA7218_PC_RESYNC_AUTO_SHIFT 1 +#define DA7218_PC_RESYNC_AUTO_MASK (0x1 << 1) + +/* DA7218_GAIN_RAMP_CTRL = 0xD */ +#define DA7218_GAIN_RAMP_RATE_SHIFT 0 +#define DA7218_GAIN_RAMP_RATE_MASK (0x3 << 0) +#define DA7218_GAIN_RAMP_RATE_MAX 4 + +/* DA7218_CIF_TIMEOUT_CTRL = 0x10 */ +#define DA7218_I2C_TIMEOUT_EN_SHIFT 0 +#define DA7218_I2C_TIMEOUT_EN_MASK (0x1 << 0) + +/* DA7218_SYSTEM_MODES_INPUT = 0x14 */ +#define DA7218_MODE_SUBMIT_SHIFT 0 +#define DA7218_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7218_ADC_MODE_SHIFT 1 +#define DA7218_ADC_MODE_MASK (0x7F << 1) + +/* DA7218_SYSTEM_MODES_OUTPUT = 0x15 */ +#define DA7218_MODE_SUBMIT_SHIFT 0 +#define DA7218_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7218_DAC_MODE_SHIFT 1 +#define DA7218_DAC_MODE_MASK (0x7F << 1) + +/* DA7218_SYSTEM_STATUS = 0x16 */ +#define DA7218_SC1_BUSY_SHIFT 0 +#define DA7218_SC1_BUSY_MASK (0x1 << 0) +#define DA7218_SC2_BUSY_SHIFT 1 +#define DA7218_SC2_BUSY_MASK (0x1 << 1) + +/* DA7218_IN_1L_FILTER_CTRL = 0x18 */ +#define DA7218_IN_1L_RAMP_EN_SHIFT 5 +#define DA7218_IN_1L_RAMP_EN_MASK (0x1 << 5) +#define DA7218_IN_1L_MUTE_EN_SHIFT 6 +#define DA7218_IN_1L_MUTE_EN_MASK (0x1 << 6) +#define DA7218_IN_1L_FILTER_EN_SHIFT 7 +#define DA7218_IN_1L_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_IN_1R_FILTER_CTRL = 0x19 */ +#define DA7218_IN_1R_RAMP_EN_SHIFT 5 +#define DA7218_IN_1R_RAMP_EN_MASK (0x1 << 5) +#define DA7218_IN_1R_MUTE_EN_SHIFT 6 +#define DA7218_IN_1R_MUTE_EN_MASK (0x1 << 6) +#define DA7218_IN_1R_FILTER_EN_SHIFT 7 +#define DA7218_IN_1R_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_IN_2L_FILTER_CTRL = 0x1A */ +#define DA7218_IN_2L_RAMP_EN_SHIFT 5 +#define DA7218_IN_2L_RAMP_EN_MASK (0x1 << 5) +#define DA7218_IN_2L_MUTE_EN_SHIFT 6 +#define DA7218_IN_2L_MUTE_EN_MASK (0x1 << 6) +#define DA7218_IN_2L_FILTER_EN_SHIFT 7 +#define DA7218_IN_2L_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_IN_2R_FILTER_CTRL = 0x1B */ +#define DA7218_IN_2R_RAMP_EN_SHIFT 5 +#define DA7218_IN_2R_RAMP_EN_MASK (0x1 << 5) +#define DA7218_IN_2R_MUTE_EN_SHIFT 6 +#define DA7218_IN_2R_MUTE_EN_MASK (0x1 << 6) +#define DA7218_IN_2R_FILTER_EN_SHIFT 7 +#define DA7218_IN_2R_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_OUT_1L_FILTER_CTRL = 0x20 */ +#define DA7218_OUT_1L_BIQ_5STAGE_SEL_SHIFT 3 +#define DA7218_OUT_1L_BIQ_5STAGE_SEL_MASK (0x1 << 3) +#define DA7218_OUT_BIQ_5STAGE_SEL_MAX 2 +#define DA7218_OUT_1L_SUBRANGE_EN_SHIFT 4 +#define DA7218_OUT_1L_SUBRANGE_EN_MASK (0x1 << 4) +#define DA7218_OUT_1L_RAMP_EN_SHIFT 5 +#define DA7218_OUT_1L_RAMP_EN_MASK (0x1 << 5) +#define DA7218_OUT_1L_MUTE_EN_SHIFT 6 +#define DA7218_OUT_1L_MUTE_EN_MASK (0x1 << 6) +#define DA7218_OUT_1L_FILTER_EN_SHIFT 7 +#define DA7218_OUT_1L_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_OUT_1R_FILTER_CTRL = 0x21 */ +#define DA7218_OUT_1R_BIQ_5STAGE_SEL_SHIFT 3 +#define DA7218_OUT_1R_BIQ_5STAGE_SEL_MASK (0x1 << 3) +#define DA7218_OUT_1R_SUBRANGE_EN_SHIFT 4 +#define DA7218_OUT_1R_SUBRANGE_EN_MASK (0x1 << 4) +#define DA7218_OUT_1R_RAMP_EN_SHIFT 5 +#define DA7218_OUT_1R_RAMP_EN_MASK (0x1 << 5) +#define DA7218_OUT_1R_MUTE_EN_SHIFT 6 +#define DA7218_OUT_1R_MUTE_EN_MASK (0x1 << 6) +#define DA7218_OUT_1R_FILTER_EN_SHIFT 7 +#define DA7218_OUT_1R_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_OUT_1_HPF_FILTER_CTRL = 0x24 */ +#define DA7218_OUT_1_VOICE_HPF_CORNER_SHIFT 0 +#define DA7218_OUT_1_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7218_VOICE_HPF_CORNER_MAX 8 +#define DA7218_OUT_1_VOICE_EN_SHIFT 3 +#define DA7218_OUT_1_VOICE_EN_MASK (0x1 << 3) +#define DA7218_OUT_1_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7218_OUT_1_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7218_AUDIO_HPF_CORNER_MAX 4 +#define DA7218_OUT_1_HPF_EN_SHIFT 7 +#define DA7218_OUT_1_HPF_EN_MASK (0x1 << 7) +#define DA7218_HPF_MODE_SHIFT 0 +#define DA7218_HPF_DISABLED ((0x0 << 3) | (0x0 << 7)) +#define DA7218_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7)) +#define DA7218_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7)) +#define DA7218_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7)) +#define DA7218_HPF_MODE_MAX 3 + +/* DA7218_OUT_1_EQ_12_FILTER_CTRL = 0x25 */ +#define DA7218_OUT_1_EQ_BAND1_SHIFT 0 +#define DA7218_OUT_1_EQ_BAND1_MASK (0xF << 0) +#define DA7218_OUT_EQ_BAND_MAX 0xF +#define DA7218_OUT_1_EQ_BAND2_SHIFT 4 +#define DA7218_OUT_1_EQ_BAND2_MASK (0xF << 4) + +/* DA7218_OUT_1_EQ_34_FILTER_CTRL = 0x26 */ +#define DA7218_OUT_1_EQ_BAND3_SHIFT 0 +#define DA7218_OUT_1_EQ_BAND3_MASK (0xF << 0) +#define DA7218_OUT_1_EQ_BAND4_SHIFT 4 +#define DA7218_OUT_1_EQ_BAND4_MASK (0xF << 4) + +/* DA7218_OUT_1_EQ_5_FILTER_CTRL = 0x27 */ +#define DA7218_OUT_1_EQ_BAND5_SHIFT 0 +#define DA7218_OUT_1_EQ_BAND5_MASK (0xF << 0) +#define DA7218_OUT_1_EQ_EN_SHIFT 7 +#define DA7218_OUT_1_EQ_EN_MASK (0x1 << 7) + +/* DA7218_OUT_1_BIQ_5STAGE_CTRL = 0x28 */ +#define DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_SHIFT 6 +#define DA7218_OUT_1_BIQ_5STAGE_MUTE_EN_MASK (0x1 << 6) +#define DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_SHIFT 7 +#define DA7218_OUT_1_BIQ_5STAGE_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_OUT_1_BIQ_5STAGE_DATA = 0x29 */ +#define DA7218_OUT_1_BIQ_5STAGE_DATA_SHIFT 0 +#define DA7218_OUT_1_BIQ_5STAGE_DATA_MASK (0xFF << 0) + +/* DA7218_OUT_1_BIQ_5STAGE_ADDR = 0x2A */ +#define DA7218_OUT_1_BIQ_5STAGE_ADDR_SHIFT 0 +#define DA7218_OUT_1_BIQ_5STAGE_ADDR_MASK (0x3F << 0) +#define DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE 50 + +/* DA7218_MIXIN_1_CTRL = 0x2C */ +#define DA7218_MIXIN_1_MIX_SEL_SHIFT 3 +#define DA7218_MIXIN_1_MIX_SEL_MASK (0x1 << 3) +#define DA7218_MIXIN_1_AMP_ZC_EN_SHIFT 4 +#define DA7218_MIXIN_1_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7218_MIXIN_1_AMP_RAMP_EN_SHIFT 5 +#define DA7218_MIXIN_1_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7218_MIXIN_1_AMP_MUTE_EN_SHIFT 6 +#define DA7218_MIXIN_1_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_MIXIN_1_AMP_EN_SHIFT 7 +#define DA7218_MIXIN_1_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIXIN_1_GAIN = 0x2D */ +#define DA7218_MIXIN_1_AMP_GAIN_SHIFT 0 +#define DA7218_MIXIN_1_AMP_GAIN_MASK (0xF << 0) +#define DA7218_MIXIN_AMP_GAIN_MAX 0xF + +/* DA7218_MIXIN_2_CTRL = 0x2E */ +#define DA7218_MIXIN_2_MIX_SEL_SHIFT 3 +#define DA7218_MIXIN_2_MIX_SEL_MASK (0x1 << 3) +#define DA7218_MIXIN_2_AMP_ZC_EN_SHIFT 4 +#define DA7218_MIXIN_2_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7218_MIXIN_2_AMP_RAMP_EN_SHIFT 5 +#define DA7218_MIXIN_2_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7218_MIXIN_2_AMP_MUTE_EN_SHIFT 6 +#define DA7218_MIXIN_2_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_MIXIN_2_AMP_EN_SHIFT 7 +#define DA7218_MIXIN_2_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIXIN_2_GAIN = 0x2F */ +#define DA7218_MIXIN_2_AMP_GAIN_SHIFT 0 +#define DA7218_MIXIN_2_AMP_GAIN_MASK (0xF << 0) + +/* DA7218_ALC_CTRL1 = 0x30 */ +#define DA7218_ALC_EN_SHIFT 0 +#define DA7218_ALC_EN_MASK (0xF << 0) +#define DA7218_ALC_CHAN1_L_EN_SHIFT 0 +#define DA7218_ALC_CHAN1_R_EN_SHIFT 1 +#define DA7218_ALC_CHAN2_L_EN_SHIFT 2 +#define DA7218_ALC_CHAN2_R_EN_SHIFT 3 +#define DA7218_ALC_SYNC_MODE_SHIFT 4 +#define DA7218_ALC_SYNC_MODE_MASK (0xF << 4) +#define DA7218_ALC_SYNC_MODE_CH1 (0x1 << 4) +#define DA7218_ALC_SYNC_MODE_CH2 (0x4 << 4) + +/* DA7218_ALC_CTRL2 = 0x31 */ +#define DA7218_ALC_ATTACK_SHIFT 0 +#define DA7218_ALC_ATTACK_MASK (0xF << 0) +#define DA7218_ALC_ATTACK_MAX 13 +#define DA7218_ALC_RELEASE_SHIFT 4 +#define DA7218_ALC_RELEASE_MASK (0xF << 4) +#define DA7218_ALC_RELEASE_MAX 11 + +/* DA7218_ALC_CTRL3 = 0x32 */ +#define DA7218_ALC_HOLD_SHIFT 0 +#define DA7218_ALC_HOLD_MASK (0xF << 0) +#define DA7218_ALC_HOLD_MAX 16 + +/* DA7218_ALC_NOISE = 0x33 */ +#define DA7218_ALC_NOISE_SHIFT 0 +#define DA7218_ALC_NOISE_MASK (0x3F << 0) +#define DA7218_ALC_THRESHOLD_MAX 0x3F + +/* DA7218_ALC_TARGET_MIN = 0x34 */ +#define DA7218_ALC_THRESHOLD_MIN_SHIFT 0 +#define DA7218_ALC_THRESHOLD_MIN_MASK (0x3F << 0) + +/* DA7218_ALC_TARGET_MAX = 0x35 */ +#define DA7218_ALC_THRESHOLD_MAX_SHIFT 0 +#define DA7218_ALC_THRESHOLD_MAX_MASK (0x3F << 0) + +/* DA7218_ALC_GAIN_LIMITS = 0x36 */ +#define DA7218_ALC_ATTEN_MAX_SHIFT 0 +#define DA7218_ALC_ATTEN_MAX_MASK (0xF << 0) +#define DA7218_ALC_ATTEN_GAIN_MAX 0xF +#define DA7218_ALC_GAIN_MAX_SHIFT 4 +#define DA7218_ALC_GAIN_MAX_MASK (0xF << 4) + +/* DA7218_ALC_ANA_GAIN_LIMITS = 0x37 */ +#define DA7218_ALC_ANA_GAIN_MIN_SHIFT 0 +#define DA7218_ALC_ANA_GAIN_MIN_MASK (0x7 << 0) +#define DA7218_ALC_ANA_GAIN_MIN 0x1 +#define DA7218_ALC_ANA_GAIN_MAX 0x7 +#define DA7218_ALC_ANA_GAIN_MAX_SHIFT 4 +#define DA7218_ALC_ANA_GAIN_MAX_MASK (0x7 << 4) + +/* DA7218_ALC_ANTICLIP_CTRL = 0x38 */ +#define DA7218_ALC_ANTICLIP_STEP_SHIFT 0 +#define DA7218_ALC_ANTICLIP_STEP_MASK (0x3 << 0) +#define DA7218_ALC_ANTICLIP_STEP_MAX 4 +#define DA7218_ALC_ANTICLIP_EN_SHIFT 7 +#define DA7218_ALC_ANTICLIP_EN_MASK (0x1 << 7) + +/* DA7218_AGS_ENABLE = 0x3C */ +#define DA7218_AGS_ENABLE_SHIFT 0 +#define DA7218_AGS_ENABLE_MASK (0x3 << 0) +#define DA7218_AGS_ENABLE_CHAN1_SHIFT 0 +#define DA7218_AGS_ENABLE_CHAN2_SHIFT 1 + +/* DA7218_AGS_TRIGGER = 0x3D */ +#define DA7218_AGS_TRIGGER_SHIFT 0 +#define DA7218_AGS_TRIGGER_MASK (0xF << 0) +#define DA7218_AGS_TRIGGER_MAX 0xF + +/* DA7218_AGS_ATT_MAX = 0x3E */ +#define DA7218_AGS_ATT_MAX_SHIFT 0 +#define DA7218_AGS_ATT_MAX_MASK (0x7 << 0) +#define DA7218_AGS_ATT_MAX_MAX 0x7 + +/* DA7218_AGS_TIMEOUT = 0x3F */ +#define DA7218_AGS_TIMEOUT_EN_SHIFT 0 +#define DA7218_AGS_TIMEOUT_EN_MASK (0x1 << 0) + +/* DA7218_AGS_ANTICLIP_CTRL = 0x40 */ +#define DA7218_AGS_ANTICLIP_EN_SHIFT 7 +#define DA7218_AGS_ANTICLIP_EN_MASK (0x1 << 7) + +/* DA7218_CALIB_CTRL = 0x44 */ +#define DA7218_CALIB_OFFSET_EN_SHIFT 0 +#define DA7218_CALIB_OFFSET_EN_MASK (0x1 << 0) +#define DA7218_CALIB_AUTO_EN_SHIFT 2 +#define DA7218_CALIB_AUTO_EN_MASK (0x1 << 2) +#define DA7218_CALIB_OVERFLOW_SHIFT 3 +#define DA7218_CALIB_OVERFLOW_MASK (0x1 << 3) + +/* DA7218_CALIB_OFFSET_AUTO_M_1 = 0x45 */ +#define DA7218_CALIB_OFFSET_AUTO_M_1_SHIFT 0 +#define DA7218_CALIB_OFFSET_AUTO_M_1_MASK (0xFF << 0) + +/* DA7218_CALIB_OFFSET_AUTO_U_1 = 0x46 */ +#define DA7218_CALIB_OFFSET_AUTO_U_1_SHIFT 0 +#define DA7218_CALIB_OFFSET_AUTO_U_1_MASK (0xF << 0) + +/* DA7218_CALIB_OFFSET_AUTO_M_2 = 0x47 */ +#define DA7218_CALIB_OFFSET_AUTO_M_2_SHIFT 0 +#define DA7218_CALIB_OFFSET_AUTO_M_2_MASK (0xFF << 0) + +/* DA7218_CALIB_OFFSET_AUTO_U_2 = 0x48 */ +#define DA7218_CALIB_OFFSET_AUTO_U_2_SHIFT 0 +#define DA7218_CALIB_OFFSET_AUTO_U_2_MASK (0xF << 0) + +/* DA7218_ENV_TRACK_CTRL = 0x4C */ +#define DA7218_INTEG_ATTACK_SHIFT 0 +#define DA7218_INTEG_ATTACK_MASK (0x3 << 0) +#define DA7218_INTEG_RELEASE_SHIFT 4 +#define DA7218_INTEG_RELEASE_MASK (0x3 << 4) +#define DA7218_INTEG_MAX 4 + +/* DA7218_LVL_DET_CTRL = 0x50 */ +#define DA7218_LVL_DET_EN_SHIFT 0 +#define DA7218_LVL_DET_EN_MASK (0xF << 0) +#define DA7218_LVL_DET_EN_CHAN1L_SHIFT 0 +#define DA7218_LVL_DET_EN_CHAN1R_SHIFT 1 +#define DA7218_LVL_DET_EN_CHAN2L_SHIFT 2 +#define DA7218_LVL_DET_EN_CHAN2R_SHIFT 3 + +/* DA7218_LVL_DET_LEVEL = 0x51 */ +#define DA7218_LVL_DET_LEVEL_SHIFT 0 +#define DA7218_LVL_DET_LEVEL_MASK (0x7F << 0) +#define DA7218_LVL_DET_LEVEL_MAX 0x7F + +/* DA7218_DGS_TRIGGER = 0x54 */ +#define DA7218_DGS_TRIGGER_LVL_SHIFT 0 +#define DA7218_DGS_TRIGGER_LVL_MASK (0x3F << 0) +#define DA7218_DGS_TRIGGER_MAX 0x3F + +/* DA7218_DGS_ENABLE = 0x55 */ +#define DA7218_DGS_ENABLE_SHIFT 0 +#define DA7218_DGS_ENABLE_MASK (0x3 << 0) +#define DA7218_DGS_ENABLE_L_SHIFT 0 +#define DA7218_DGS_ENABLE_R_SHIFT 1 + +/* DA7218_DGS_RISE_FALL = 0x56 */ +#define DA7218_DGS_RISE_COEFF_SHIFT 0 +#define DA7218_DGS_RISE_COEFF_MASK (0x7 << 0) +#define DA7218_DGS_RISE_COEFF_MAX 7 +#define DA7218_DGS_FALL_COEFF_SHIFT 4 +#define DA7218_DGS_FALL_COEFF_MASK (0x7 << 4) +#define DA7218_DGS_FALL_COEFF_MAX 8 + +/* DA7218_DGS_SYNC_DELAY = 0x57 */ +#define DA7218_DGS_SYNC_DELAY_SHIFT 0 +#define DA7218_DGS_SYNC_DELAY_MASK (0xFF << 0) +#define DA7218_DGS_SYNC_DELAY_MAX 0xFF + +/* DA7218_DGS_SYNC_DELAY2 = 0x58 */ +#define DA7218_DGS_SYNC_DELAY2_SHIFT 0 +#define DA7218_DGS_SYNC_DELAY2_MASK (0xFF << 0) + +/* DA7218_DGS_SYNC_DELAY3 = 0x59 */ +#define DA7218_DGS_SYNC_DELAY3_SHIFT 0 +#define DA7218_DGS_SYNC_DELAY3_MASK (0x7F << 0) +#define DA7218_DGS_SYNC_DELAY3_MAX 0x7F + +/* DA7218_DGS_LEVELS = 0x5A */ +#define DA7218_DGS_ANTICLIP_LVL_SHIFT 0 +#define DA7218_DGS_ANTICLIP_LVL_MASK (0x7 << 0) +#define DA7218_DGS_ANTICLIP_LVL_MAX 0x7 +#define DA7218_DGS_SIGNAL_LVL_SHIFT 4 +#define DA7218_DGS_SIGNAL_LVL_MASK (0xF << 4) +#define DA7218_DGS_SIGNAL_LVL_MAX 0xF + +/* DA7218_DGS_GAIN_CTRL = 0x5B */ +#define DA7218_DGS_STEPS_SHIFT 0 +#define DA7218_DGS_STEPS_MASK (0x1F << 0) +#define DA7218_DGS_STEPS_MAX 0x1F +#define DA7218_DGS_RAMP_EN_SHIFT 5 +#define DA7218_DGS_RAMP_EN_MASK (0x1 << 5) +#define DA7218_DGS_SUBR_EN_SHIFT 6 +#define DA7218_DGS_SUBR_EN_MASK (0x1 << 6) + +/* DA7218_DROUTING_OUTDAI_1L = 0x5C */ +#define DA7218_OUTDAI_1L_SRC_SHIFT 0 +#define DA7218_OUTDAI_1L_SRC_MASK (0x7F << 0) +#define DA7218_DMIX_SRC_INFILT1L 0 +#define DA7218_DMIX_SRC_INFILT1R 1 +#define DA7218_DMIX_SRC_INFILT2L 2 +#define DA7218_DMIX_SRC_INFILT2R 3 +#define DA7218_DMIX_SRC_TONEGEN 4 +#define DA7218_DMIX_SRC_DAIL 5 +#define DA7218_DMIX_SRC_DAIR 6 + +/* DA7218_DMIX_OUTDAI_1L_INFILT_1L_GAIN = 0x5D */ +#define DA7218_OUTDAI_1L_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INFILT_1L_GAIN_MASK (0x1F << 0) +#define DA7218_DMIX_GAIN_MAX 0x1F + +/* DA7218_DMIX_OUTDAI_1L_INFILT_1R_GAIN = 0x5E */ +#define DA7218_OUTDAI_1L_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1L_INFILT_2L_GAIN = 0x5F */ +#define DA7218_OUTDAI_1L_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1L_INFILT_2R_GAIN = 0x60 */ +#define DA7218_OUTDAI_1L_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1L_TONEGEN_GAIN = 0x61 */ +#define DA7218_OUTDAI_1L_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1L_INDAI_1L_GAIN = 0x62 */ +#define DA7218_OUTDAI_1L_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1L_INDAI_1R_GAIN = 0x63 */ +#define DA7218_OUTDAI_1L_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1L_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_OUTDAI_1R = 0x64 */ +#define DA7218_OUTDAI_1R_SRC_SHIFT 0 +#define DA7218_OUTDAI_1R_SRC_MASK (0x7F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INFILT_1L_GAIN = 0x65 */ +#define DA7218_OUTDAI_1R_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INFILT_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INFILT_1R_GAIN = 0x66 */ +#define DA7218_OUTDAI_1R_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INFILT_2L_GAIN = 0x67 */ +#define DA7218_OUTDAI_1R_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INFILT_2R_GAIN = 0x68 */ +#define DA7218_OUTDAI_1R_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_TONEGEN_GAIN = 0x69 */ +#define DA7218_OUTDAI_1R_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INDAI_1L_GAIN = 0x6A */ +#define DA7218_OUTDAI_1R_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_1R_INDAI_1R_GAIN = 0x6B */ +#define DA7218_OUTDAI_1R_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_1R_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_OUTFILT_1L = 0x6C */ +#define DA7218_OUTFILT_1L_SRC_SHIFT 0 +#define DA7218_OUTFILT_1L_SRC_MASK (0x7F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INFILT_1L_GAIN = 0x6D */ +#define DA7218_OUTFILT_1L_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INFILT_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INFILT_1R_GAIN = 0x6E */ +#define DA7218_OUTFILT_1L_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INFILT_2L_GAIN = 0x6F */ +#define DA7218_OUTFILT_1L_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INFILT_2R_GAIN = 0x70 */ +#define DA7218_OUTFILT_1L_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_TONEGEN_GAIN = 0x71 */ +#define DA7218_OUTFILT_1L_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INDAI_1L_GAIN = 0x72 */ +#define DA7218_OUTFILT_1L_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1L_INDAI_1R_GAIN = 0x73 */ +#define DA7218_OUTFILT_1L_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1L_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_OUTFILT_1R = 0x74 */ +#define DA7218_OUTFILT_1R_SRC_SHIFT 0 +#define DA7218_OUTFILT_1R_SRC_MASK (0x7F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INFILT_1L_GAIN = 0x75 */ +#define DA7218_OUTFILT_1R_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INFILT_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INFILT_1R_GAIN = 0x76 */ +#define DA7218_OUTFILT_1R_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INFILT_2L_GAIN = 0x77 */ +#define DA7218_OUTFILT_1R_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INFILT_2R_GAIN = 0x78 */ +#define DA7218_OUTFILT_1R_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_TONEGEN_GAIN = 0x79 */ +#define DA7218_OUTFILT_1R_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INDAI_1L_GAIN = 0x7A */ +#define DA7218_OUTFILT_1R_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTFILT_1R_INDAI_1R_GAIN = 0x7B */ +#define DA7218_OUTFILT_1R_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTFILT_1R_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_OUTDAI_2L = 0x7C */ +#define DA7218_OUTDAI_2L_SRC_SHIFT 0 +#define DA7218_OUTDAI_2L_SRC_MASK (0x7F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INFILT_1L_GAIN = 0x7D */ +#define DA7218_OUTDAI_2L_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INFILT_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INFILT_1R_GAIN = 0x7E */ +#define DA7218_OUTDAI_2L_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INFILT_2L_GAIN = 0x7F */ +#define DA7218_OUTDAI_2L_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INFILT_2R_GAIN = 0x80 */ +#define DA7218_OUTDAI_2L_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_TONEGEN_GAIN = 0x81 */ +#define DA7218_OUTDAI_2L_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INDAI_1L_GAIN = 0x82 */ +#define DA7218_OUTDAI_2L_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2L_INDAI_1R_GAIN = 0x83 */ +#define DA7218_OUTDAI_2L_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2L_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_OUTDAI_2R = 0x84 */ +#define DA7218_OUTDAI_2R_SRC_SHIFT 0 +#define DA7218_OUTDAI_2R_SRC_MASK (0x7F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INFILT_1L_GAIN = 0x85 */ +#define DA7218_OUTDAI_2R_INFILT_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INFILT_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INFILT_1R_GAIN = 0x86 */ +#define DA7218_OUTDAI_2R_INFILT_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INFILT_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INFILT_2L_GAIN = 0x87 */ +#define DA7218_OUTDAI_2R_INFILT_2L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INFILT_2L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INFILT_2R_GAIN = 0x88 */ +#define DA7218_OUTDAI_2R_INFILT_2R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INFILT_2R_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_TONEGEN_GAIN = 0x89 */ +#define DA7218_OUTDAI_2R_TONEGEN_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_TONEGEN_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INDAI_1L_GAIN = 0x8A */ +#define DA7218_OUTDAI_2R_INDAI_1L_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INDAI_1L_GAIN_MASK (0x1F << 0) + +/* DA7218_DMIX_OUTDAI_2R_INDAI_1R_GAIN = 0x8B */ +#define DA7218_OUTDAI_2R_INDAI_1R_GAIN_SHIFT 0 +#define DA7218_OUTDAI_2R_INDAI_1R_GAIN_MASK (0x1F << 0) + +/* DA7218_DAI_CTRL = 0x8C */ +#define DA7218_DAI_FORMAT_SHIFT 0 +#define DA7218_DAI_FORMAT_MASK (0x3 << 0) +#define DA7218_DAI_FORMAT_I2S (0x0 << 0) +#define DA7218_DAI_FORMAT_LEFT_J (0x1 << 0) +#define DA7218_DAI_FORMAT_RIGHT_J (0x2 << 0) +#define DA7218_DAI_FORMAT_DSP (0x3 << 0) +#define DA7218_DAI_WORD_LENGTH_SHIFT 2 +#define DA7218_DAI_WORD_LENGTH_MASK (0x3 << 2) +#define DA7218_DAI_WORD_LENGTH_S16_LE (0x0 << 2) +#define DA7218_DAI_WORD_LENGTH_S20_LE (0x1 << 2) +#define DA7218_DAI_WORD_LENGTH_S24_LE (0x2 << 2) +#define DA7218_DAI_WORD_LENGTH_S32_LE (0x3 << 2) +#define DA7218_DAI_CH_NUM_SHIFT 4 +#define DA7218_DAI_CH_NUM_MASK (0x7 << 4) +#define DA7218_DAI_CH_NUM_MAX 4 +#define DA7218_DAI_EN_SHIFT 7 +#define DA7218_DAI_EN_MASK (0x1 << 7) + +/* DA7218_DAI_TDM_CTRL = 0x8D */ +#define DA7218_DAI_TDM_CH_EN_SHIFT 0 +#define DA7218_DAI_TDM_CH_EN_MASK (0xF << 0) +#define DA7218_DAI_TDM_MAX_SLOTS 4 +#define DA7218_DAI_OE_SHIFT 6 +#define DA7218_DAI_OE_MASK (0x1 << 6) +#define DA7218_DAI_TDM_MODE_EN_SHIFT 7 +#define DA7218_DAI_TDM_MODE_EN_MASK (0x1 << 7) + +/* DA7218_DAI_OFFSET_LOWER = 0x8E */ +#define DA7218_DAI_OFFSET_LOWER_SHIFT 0 +#define DA7218_DAI_OFFSET_LOWER_MASK (0xFF << 0) + +/* DA7218_DAI_OFFSET_UPPER = 0x8F */ +#define DA7218_DAI_OFFSET_UPPER_SHIFT 0 +#define DA7218_DAI_OFFSET_UPPER_MASK (0x7 << 0) + +/* DA7218_DAI_CLK_MODE = 0x90 */ +#define DA7218_DAI_BCLKS_PER_WCLK_SHIFT 0 +#define DA7218_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) +#define DA7218_DAI_BCLKS_PER_WCLK_32 (0x0 << 0) +#define DA7218_DAI_BCLKS_PER_WCLK_64 (0x1 << 0) +#define DA7218_DAI_BCLKS_PER_WCLK_128 (0x2 << 0) +#define DA7218_DAI_BCLKS_PER_WCLK_256 (0x3 << 0) +#define DA7218_DAI_CLK_POL_SHIFT 2 +#define DA7218_DAI_CLK_POL_MASK (0x1 << 2) +#define DA7218_DAI_CLK_POL_INV (0x1 << 2) +#define DA7218_DAI_WCLK_POL_SHIFT 3 +#define DA7218_DAI_WCLK_POL_MASK (0x1 << 3) +#define DA7218_DAI_WCLK_POL_INV (0x1 << 3) +#define DA7218_DAI_WCLK_TRI_STATE_SHIFT 4 +#define DA7218_DAI_WCLK_TRI_STATE_MASK (0x1 << 4) +#define DA7218_DAI_CLK_EN_SHIFT 7 +#define DA7218_DAI_CLK_EN_MASK (0x1 << 7) + +/* DA7218_PLL_CTRL = 0x91 */ +#define DA7218_PLL_INDIV_SHIFT 0 +#define DA7218_PLL_INDIV_MASK (0x7 << 0) +#define DA7218_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 0) +#define DA7218_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 0) +#define DA7218_PLL_INDIV_9_TO_18_MHZ (0x2 << 0) +#define DA7218_PLL_INDIV_18_TO_36_MHZ (0x3 << 0) +#define DA7218_PLL_INDIV_36_TO_54_MHZ (0x4 << 0) +#define DA7218_PLL_MCLK_SQR_EN_SHIFT 4 +#define DA7218_PLL_MCLK_SQR_EN_MASK (0x1 << 4) +#define DA7218_PLL_MODE_SHIFT 6 +#define DA7218_PLL_MODE_MASK (0x3 << 6) +#define DA7218_PLL_MODE_BYPASS (0x0 << 6) +#define DA7218_PLL_MODE_NORMAL (0x1 << 6) +#define DA7218_PLL_MODE_SRM (0x2 << 6) + +/* DA7218_PLL_FRAC_TOP = 0x92 */ +#define DA7218_PLL_FBDIV_FRAC_TOP_SHIFT 0 +#define DA7218_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0) + +/* DA7218_PLL_FRAC_BOT = 0x93 */ +#define DA7218_PLL_FBDIV_FRAC_BOT_SHIFT 0 +#define DA7218_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0) + +/* DA7218_PLL_INTEGER = 0x94 */ +#define DA7218_PLL_FBDIV_INTEGER_SHIFT 0 +#define DA7218_PLL_FBDIV_INTEGER_MASK (0x7F << 0) + +/* DA7218_PLL_STATUS = 0x95 */ +#define DA7218_PLL_SRM_STATUS_SHIFT 0 +#define DA7218_PLL_SRM_STATUS_MASK (0xFF << 0) +#define DA7218_PLL_SRM_STATUS_SRM_LOCK (0x1 << 7) + +/* DA7218_PLL_REFOSC_CAL = 0x98 */ +#define DA7218_PLL_REFOSC_CAL_CTRL_SHIFT 0 +#define DA7218_PLL_REFOSC_CAL_CTRL_MASK (0x1F << 0) +#define DA7218_PLL_REFOSC_CAL_START_SHIFT 6 +#define DA7218_PLL_REFOSC_CAL_START_MASK (0x1 << 6) +#define DA7218_PLL_REFOSC_CAL_EN_SHIFT 7 +#define DA7218_PLL_REFOSC_CAL_EN_MASK (0x1 << 7) + +/* DA7218_DAC_NG_CTRL = 0x9C */ +#define DA7218_DAC_NG_EN_SHIFT 7 +#define DA7218_DAC_NG_EN_MASK (0x1 << 7) + +/* DA7218_DAC_NG_SETUP_TIME = 0x9D */ +#define DA7218_DAC_NG_SETUP_TIME_SHIFT 0 +#define DA7218_DAC_NG_SETUP_TIME_MASK (0x3 << 0) +#define DA7218_DAC_NG_SETUP_TIME_MAX 4 +#define DA7218_DAC_NG_RAMPUP_RATE_SHIFT 2 +#define DA7218_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2) +#define DA7218_DAC_NG_RAMPUP_RATE_MAX 2 +#define DA7218_DAC_NG_RAMPDN_RATE_SHIFT 3 +#define DA7218_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3) +#define DA7218_DAC_NG_RAMPDN_RATE_MAX 2 + +/* DA7218_DAC_NG_OFF_THRESH = 0x9E */ +#define DA7218_DAC_NG_OFF_THRESHOLD_SHIFT 0 +#define DA7218_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0) +#define DA7218_DAC_NG_THRESHOLD_MAX 0x7 + +/* DA7218_DAC_NG_ON_THRESH = 0x9F */ +#define DA7218_DAC_NG_ON_THRESHOLD_SHIFT 0 +#define DA7218_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0) + +/* DA7218_TONE_GEN_CFG1 = 0xA0 */ +#define DA7218_DTMF_REG_SHIFT 0 +#define DA7218_DTMF_REG_MASK (0xF << 0) +#define DA7218_DTMF_REG_MAX 16 +#define DA7218_DTMF_EN_SHIFT 4 +#define DA7218_DTMF_EN_MASK (0x1 << 4) +#define DA7218_START_STOPN_SHIFT 7 +#define DA7218_START_STOPN_MASK (0x1 << 7) + +/* DA7218_TONE_GEN_CFG2 = 0xA1 */ +#define DA7218_SWG_SEL_SHIFT 0 +#define DA7218_SWG_SEL_MASK (0x3 << 0) +#define DA7218_SWG_SEL_MAX 4 + +/* DA7218_TONE_GEN_FREQ1_L = 0xA2 */ +#define DA7218_FREQ1_L_SHIFT 0 +#define DA7218_FREQ1_L_MASK (0xFF << 0) +#define DA7218_FREQ_MAX 0xFFFF + +/* DA7218_TONE_GEN_FREQ1_U = 0xA3 */ +#define DA7218_FREQ1_U_SHIFT 0 +#define DA7218_FREQ1_U_MASK (0xFF << 0) + +/* DA7218_TONE_GEN_FREQ2_L = 0xA4 */ +#define DA7218_FREQ2_L_SHIFT 0 +#define DA7218_FREQ2_L_MASK (0xFF << 0) + +/* DA7218_TONE_GEN_FREQ2_U = 0xA5 */ +#define DA7218_FREQ2_U_SHIFT 0 +#define DA7218_FREQ2_U_MASK (0xFF << 0) + +/* DA7218_TONE_GEN_CYCLES = 0xA6 */ +#define DA7218_BEEP_CYCLES_SHIFT 0 +#define DA7218_BEEP_CYCLES_MASK (0x7 << 0) + +/* DA7218_TONE_GEN_ON_PER = 0xA7 */ +#define DA7218_BEEP_ON_PER_SHIFT 0 +#define DA7218_BEEP_ON_PER_MASK (0x3F << 0) + +/* DA7218_TONE_GEN_OFF_PER = 0xA8 */ +#define DA7218_BEEP_OFF_PER_SHIFT 0 +#define DA7218_BEEP_OFF_PER_MASK (0x3F << 0) +#define DA7218_BEEP_ON_OFF_MAX 0x3F + +/* DA7218_CP_CTRL = 0xAC */ +#define DA7218_CP_MOD_SHIFT 2 +#define DA7218_CP_MOD_MASK (0x3 << 2) +#define DA7218_CP_MCHANGE_SHIFT 4 +#define DA7218_CP_MCHANGE_MASK (0x3 << 4) +#define DA7218_CP_MCHANGE_REL_MASK 0x3 +#define DA7218_CP_MCHANGE_MAX 3 +#define DA7218_CP_MCHANGE_LARGEST_VOL 0x1 +#define DA7218_CP_MCHANGE_DAC_VOL 0x2 +#define DA7218_CP_MCHANGE_SIG_MAG 0x3 +#define DA7218_CP_SMALL_SWITCH_FREQ_EN_SHIFT 6 +#define DA7218_CP_SMALL_SWITCH_FREQ_EN_MASK (0x1 << 6) +#define DA7218_CP_EN_SHIFT 7 +#define DA7218_CP_EN_MASK (0x1 << 7) + +/* DA7218_CP_DELAY = 0xAD */ +#define DA7218_CP_FCONTROL_SHIFT 0 +#define DA7218_CP_FCONTROL_MASK (0x7 << 0) +#define DA7218_CP_FCONTROL_MAX 6 +#define DA7218_CP_TAU_DELAY_SHIFT 3 +#define DA7218_CP_TAU_DELAY_MASK (0x7 << 3) +#define DA7218_CP_TAU_DELAY_MAX 8 + +/* DA7218_CP_VOL_THRESHOLD1 = 0xAE */ +#define DA7218_CP_THRESH_VDD2_SHIFT 0 +#define DA7218_CP_THRESH_VDD2_MASK (0x3F << 0) +#define DA7218_CP_THRESH_VDD2_MAX 0x3F + +/* DA7218_MIC_1_CTRL = 0xB4 */ +#define DA7218_MIC_1_AMP_MUTE_EN_SHIFT 6 +#define DA7218_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_MIC_1_AMP_EN_SHIFT 7 +#define DA7218_MIC_1_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIC_1_GAIN = 0xB5 */ +#define DA7218_MIC_1_AMP_GAIN_SHIFT 0 +#define DA7218_MIC_1_AMP_GAIN_MASK (0x7 << 0) +#define DA7218_MIC_AMP_GAIN_MAX 0x7 + +/* DA7218_MIC_1_SELECT = 0xB7 */ +#define DA7218_MIC_1_AMP_IN_SEL_SHIFT 0 +#define DA7218_MIC_1_AMP_IN_SEL_MASK (0x3 << 0) + +/* DA7218_MIC_2_CTRL = 0xB8 */ +#define DA7218_MIC_2_AMP_MUTE_EN_SHIFT 6 +#define DA7218_MIC_2_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_MIC_2_AMP_EN_SHIFT 7 +#define DA7218_MIC_2_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIC_2_GAIN = 0xB9 */ +#define DA7218_MIC_2_AMP_GAIN_SHIFT 0 +#define DA7218_MIC_2_AMP_GAIN_MASK (0x7 << 0) + +/* DA7218_MIC_2_SELECT = 0xBB */ +#define DA7218_MIC_2_AMP_IN_SEL_SHIFT 0 +#define DA7218_MIC_2_AMP_IN_SEL_MASK (0x3 << 0) + +/* DA7218_IN_1_HPF_FILTER_CTRL = 0xBC */ +#define DA7218_IN_1_VOICE_HPF_CORNER_SHIFT 0 +#define DA7218_IN_1_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7218_IN_VOICE_HPF_CORNER_MAX 8 +#define DA7218_IN_1_VOICE_EN_SHIFT 3 +#define DA7218_IN_1_VOICE_EN_MASK (0x1 << 3) +#define DA7218_IN_1_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7218_IN_1_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7218_IN_1_HPF_EN_SHIFT 7 +#define DA7218_IN_1_HPF_EN_MASK (0x1 << 7) + +/* DA7218_IN_2_HPF_FILTER_CTRL = 0xBD */ +#define DA7218_IN_2_VOICE_HPF_CORNER_SHIFT 0 +#define DA7218_IN_2_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7218_IN_2_VOICE_EN_SHIFT 3 +#define DA7218_IN_2_VOICE_EN_MASK (0x1 << 3) +#define DA7218_IN_2_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7218_IN_2_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7218_IN_2_HPF_EN_SHIFT 7 +#define DA7218_IN_2_HPF_EN_MASK (0x1 << 7) + +/* DA7218_ADC_1_CTRL = 0xC0 */ +#define DA7218_ADC_1_AAF_EN_SHIFT 2 +#define DA7218_ADC_1_AAF_EN_MASK (0x1 << 2) + +/* DA7218_ADC_2_CTRL = 0xC1 */ +#define DA7218_ADC_2_AAF_EN_SHIFT 2 +#define DA7218_ADC_2_AAF_EN_MASK (0x1 << 2) + +/* DA7218_ADC_MODE = 0xC2 */ +#define DA7218_ADC_LP_MODE_SHIFT 0 +#define DA7218_ADC_LP_MODE_MASK (0x1 << 0) +#define DA7218_ADC_LVLDET_MODE_SHIFT 1 +#define DA7218_ADC_LVLDET_MODE_MASK (0x1 << 1) +#define DA7218_ADC_LVLDET_AUTO_EXIT_SHIFT 2 +#define DA7218_ADC_LVLDET_AUTO_EXIT_MASK (0x1 << 2) + +/* DA7218_MIXOUT_L_CTRL = 0xCC */ +#define DA7218_MIXOUT_L_AMP_EN_SHIFT 7 +#define DA7218_MIXOUT_L_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIXOUT_L_GAIN = 0xCD */ +#define DA7218_MIXOUT_L_AMP_GAIN_SHIFT 0 +#define DA7218_MIXOUT_L_AMP_GAIN_MASK (0x3 << 0) +#define DA7218_MIXOUT_AMP_GAIN_MIN 0x1 +#define DA7218_MIXOUT_AMP_GAIN_MAX 0x3 + +/* DA7218_MIXOUT_R_CTRL = 0xCE */ +#define DA7218_MIXOUT_R_AMP_EN_SHIFT 7 +#define DA7218_MIXOUT_R_AMP_EN_MASK (0x1 << 7) + +/* DA7218_MIXOUT_R_GAIN = 0xCF */ +#define DA7218_MIXOUT_R_AMP_GAIN_SHIFT 0 +#define DA7218_MIXOUT_R_AMP_GAIN_MASK (0x3 << 0) + +/* DA7218_HP_L_CTRL = 0xD0 */ +#define DA7218_HP_L_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7218_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7218_HP_L_AMP_OE_SHIFT 3 +#define DA7218_HP_L_AMP_OE_MASK (0x1 << 3) +#define DA7218_HP_L_AMP_ZC_EN_SHIFT 4 +#define DA7218_HP_L_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7218_HP_L_AMP_RAMP_EN_SHIFT 5 +#define DA7218_HP_L_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7218_HP_L_AMP_MUTE_EN_SHIFT 6 +#define DA7218_HP_L_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_HP_L_AMP_EN_SHIFT 7 +#define DA7218_HP_L_AMP_EN_MASK (0x1 << 7) +#define DA7218_HP_AMP_OE_MASK (0x1 << 3) + +/* DA7218_HP_L_GAIN = 0xD1 */ +#define DA7218_HP_L_AMP_GAIN_SHIFT 0 +#define DA7218_HP_L_AMP_GAIN_MASK (0x3F << 0) +#define DA7218_HP_AMP_GAIN_MIN 0x15 +#define DA7218_HP_AMP_GAIN_MAX 0x3F + +/* DA7218_HP_R_CTRL = 0xD2 */ +#define DA7218_HP_R_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7218_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7218_HP_R_AMP_OE_SHIFT 3 +#define DA7218_HP_R_AMP_OE_MASK (0x1 << 3) +#define DA7218_HP_R_AMP_ZC_EN_SHIFT 4 +#define DA7218_HP_R_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7218_HP_R_AMP_RAMP_EN_SHIFT 5 +#define DA7218_HP_R_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7218_HP_R_AMP_MUTE_EN_SHIFT 6 +#define DA7218_HP_R_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7218_HP_R_AMP_EN_SHIFT 7 +#define DA7218_HP_R_AMP_EN_MASK (0x1 << 7) + +/* DA7218_HP_R_GAIN = 0xD3 */ +#define DA7218_HP_R_AMP_GAIN_SHIFT 0 +#define DA7218_HP_R_AMP_GAIN_MASK (0x3F << 0) + +/* DA7218_HP_SNGL_CTRL = 0xD4 */ +#define DA7218_HP_AMP_STEREO_DETECT_STATUS_SHIFT 0 +#define DA7218_HP_AMP_STEREO_DETECT_STATUS_MASK (0x1 << 0) +#define DA7218_HPL_AMP_LOAD_DETECT_STATUS_SHIFT 1 +#define DA7218_HPL_AMP_LOAD_DETECT_STATUS_MASK (0x1 << 1) +#define DA7218_HPR_AMP_LOAD_DETECT_STATUS_SHIFT 2 +#define DA7218_HPR_AMP_LOAD_DETECT_STATUS_MASK (0x1 << 2) +#define DA7218_HP_AMP_LOAD_DETECT_EN_SHIFT 6 +#define DA7218_HP_AMP_LOAD_DETECT_EN_MASK (0x1 << 6) +#define DA7218_HP_AMP_STEREO_DETECT_EN_SHIFT 7 +#define DA7218_HP_AMP_STEREO_DETECT_EN_MASK (0x1 << 7) + +/* DA7218_HP_DIFF_CTRL = 0xD5 */ +#define DA7218_HP_AMP_DIFF_MODE_EN_SHIFT 0 +#define DA7218_HP_AMP_DIFF_MODE_EN_MASK (0x1 << 0) +#define DA7218_HP_AMP_SINGLE_SUPPLY_EN_SHIFT 4 +#define DA7218_HP_AMP_SINGLE_SUPPLY_EN_MASK (0x1 << 4) + +/* DA7218_HP_DIFF_UNLOCK = 0xD7 */ +#define DA7218_HP_DIFF_UNLOCK_SHIFT 0 +#define DA7218_HP_DIFF_UNLOCK_MASK (0x1 << 0) +#define DA7218_HP_DIFF_UNLOCK_VAL 0xC3 + +/* DA7218_HPLDET_JACK = 0xD8 */ +#define DA7218_HPLDET_JACK_RATE_SHIFT 0 +#define DA7218_HPLDET_JACK_RATE_MASK (0x7 << 0) +#define DA7218_HPLDET_JACK_DEBOUNCE_SHIFT 3 +#define DA7218_HPLDET_JACK_DEBOUNCE_MASK (0x3 << 3) +#define DA7218_HPLDET_JACK_THR_SHIFT 5 +#define DA7218_HPLDET_JACK_THR_MASK (0x3 << 5) +#define DA7218_HPLDET_JACK_EN_SHIFT 7 +#define DA7218_HPLDET_JACK_EN_MASK (0x1 << 7) + +/* DA7218_HPLDET_CTRL = 0xD9 */ +#define DA7218_HPLDET_COMP_INV_SHIFT 0 +#define DA7218_HPLDET_COMP_INV_MASK (0x1 << 0) +#define DA7218_HPLDET_HYST_EN_SHIFT 1 +#define DA7218_HPLDET_HYST_EN_MASK (0x1 << 1) +#define DA7218_HPLDET_DISCHARGE_EN_SHIFT 7 +#define DA7218_HPLDET_DISCHARGE_EN_MASK (0x1 << 7) + +/* DA7218_HPLDET_TEST = 0xDA */ +#define DA7218_HPLDET_COMP_STS_SHIFT 4 +#define DA7218_HPLDET_COMP_STS_MASK (0x1 << 4) + +/* DA7218_REFERENCES = 0xDC */ +#define DA7218_BIAS_EN_SHIFT 3 +#define DA7218_BIAS_EN_MASK (0x1 << 3) + +/* DA7218_IO_CTRL = 0xE0 */ +#define DA7218_IO_VOLTAGE_LEVEL_SHIFT 0 +#define DA7218_IO_VOLTAGE_LEVEL_MASK (0x1 << 0) +#define DA7218_IO_VOLTAGE_LEVEL_2_5V_3_6V 0 +#define DA7218_IO_VOLTAGE_LEVEL_1_5V_2_5V 1 + +/* DA7218_LDO_CTRL = 0xE1 */ +#define DA7218_LDO_LEVEL_SELECT_SHIFT 4 +#define DA7218_LDO_LEVEL_SELECT_MASK (0x3 << 4) +#define DA7218_LDO_EN_SHIFT 7 +#define DA7218_LDO_EN_MASK (0x1 << 7) + +/* DA7218_SIDETONE_CTRL = 0xE4 */ +#define DA7218_SIDETONE_MUTE_EN_SHIFT 6 +#define DA7218_SIDETONE_MUTE_EN_MASK (0x1 << 6) +#define DA7218_SIDETONE_FILTER_EN_SHIFT 7 +#define DA7218_SIDETONE_FILTER_EN_MASK (0x1 << 7) + +/* DA7218_SIDETONE_IN_SELECT = 0xE5 */ +#define DA7218_SIDETONE_IN_SELECT_SHIFT 0 +#define DA7218_SIDETONE_IN_SELECT_MASK (0x3 << 0) +#define DA7218_SIDETONE_IN_SELECT_MAX 4 + +/* DA7218_SIDETONE_GAIN = 0xE6 */ +#define DA7218_SIDETONE_GAIN_SHIFT 0 +#define DA7218_SIDETONE_GAIN_MASK (0x1F << 0) + +/* DA7218_DROUTING_ST_OUTFILT_1L = 0xE8 */ +#define DA7218_OUTFILT_ST_1L_SRC_SHIFT 0 +#define DA7218_OUTFILT_ST_1L_SRC_MASK (0x7 << 0) +#define DA7218_DMIX_ST_SRC_OUTFILT1L 0 +#define DA7218_DMIX_ST_SRC_OUTFILT1R 1 +#define DA7218_DMIX_ST_SRC_SIDETONE 2 + +/* DA7218_DROUTING_ST_OUTFILT_1R = 0xE9 */ +#define DA7218_OUTFILT_ST_1R_SRC_SHIFT 0 +#define DA7218_OUTFILT_ST_1R_SRC_MASK (0x7 << 0) + +/* DA7218_SIDETONE_BIQ_3STAGE_DATA = 0xEA */ +#define DA7218_SIDETONE_BIQ_3STAGE_DATA_SHIFT 0 +#define DA7218_SIDETONE_BIQ_3STAGE_DATA_MASK (0xFF << 0) + +/* DA7218_SIDETONE_BIQ_3STAGE_ADDR = 0xEB */ +#define DA7218_SIDETONE_BIQ_3STAGE_ADDR_SHIFT 0 +#define DA7218_SIDETONE_BIQ_3STAGE_ADDR_MASK (0x1F << 0) +#define DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE 30 + +/* DA7218_EVENT_STATUS = 0xEC */ +#define DA7218_HPLDET_JACK_STS_SHIFT 7 +#define DA7218_HPLDET_JACK_STS_MASK (0x1 << 7) + +/* DA7218_EVENT = 0xED */ +#define DA7218_LVL_DET_EVENT_SHIFT 0 +#define DA7218_LVL_DET_EVENT_MASK (0x1 << 0) +#define DA7218_HPLDET_JACK_EVENT_SHIFT 7 +#define DA7218_HPLDET_JACK_EVENT_MASK (0x1 << 7) + +/* DA7218_EVENT_MASK = 0xEE */ +#define DA7218_LVL_DET_EVENT_MSK_SHIFT 0 +#define DA7218_LVL_DET_EVENT_MSK_MASK (0x1 << 0) +#define DA7218_HPLDET_JACK_EVENT_IRQ_MSK_SHIFT 7 +#define DA7218_HPLDET_JACK_EVENT_IRQ_MSK_MASK (0x1 << 7) + +/* DA7218_DMIC_1_CTRL = 0xF0 */ +#define DA7218_DMIC_1_DATA_SEL_SHIFT 0 +#define DA7218_DMIC_1_DATA_SEL_MASK (0x1 << 0) +#define DA7218_DMIC_1_SAMPLEPHASE_SHIFT 1 +#define DA7218_DMIC_1_SAMPLEPHASE_MASK (0x1 << 1) +#define DA7218_DMIC_1_CLK_RATE_SHIFT 2 +#define DA7218_DMIC_1_CLK_RATE_MASK (0x1 << 2) +#define DA7218_DMIC_1L_EN_SHIFT 6 +#define DA7218_DMIC_1L_EN_MASK (0x1 << 6) +#define DA7218_DMIC_1R_EN_SHIFT 7 +#define DA7218_DMIC_1R_EN_MASK (0x1 << 7) + +/* DA7218_DMIC_2_CTRL = 0xF1 */ +#define DA7218_DMIC_2_DATA_SEL_SHIFT 0 +#define DA7218_DMIC_2_DATA_SEL_MASK (0x1 << 0) +#define DA7218_DMIC_2_SAMPLEPHASE_SHIFT 1 +#define DA7218_DMIC_2_SAMPLEPHASE_MASK (0x1 << 1) +#define DA7218_DMIC_2_CLK_RATE_SHIFT 2 +#define DA7218_DMIC_2_CLK_RATE_MASK (0x1 << 2) +#define DA7218_DMIC_2L_EN_SHIFT 6 +#define DA7218_DMIC_2L_EN_MASK (0x1 << 6) +#define DA7218_DMIC_2R_EN_SHIFT 7 +#define DA7218_DMIC_2R_EN_MASK (0x1 << 7) + +/* DA7218_IN_1L_GAIN = 0xF4 */ +#define DA7218_IN_1L_DIGITAL_GAIN_SHIFT 0 +#define DA7218_IN_1L_DIGITAL_GAIN_MASK (0x7F << 0) +#define DA7218_IN_DIGITAL_GAIN_MAX 0x7F + +/* DA7218_IN_1R_GAIN = 0xF5 */ +#define DA7218_IN_1R_DIGITAL_GAIN_SHIFT 0 +#define DA7218_IN_1R_DIGITAL_GAIN_MASK (0x7F << 0) + +/* DA7218_IN_2L_GAIN = 0xF6 */ +#define DA7218_IN_2L_DIGITAL_GAIN_SHIFT 0 +#define DA7218_IN_2L_DIGITAL_GAIN_MASK (0x7F << 0) + +/* DA7218_IN_2R_GAIN = 0xF7 */ +#define DA7218_IN_2R_DIGITAL_GAIN_SHIFT 0 +#define DA7218_IN_2R_DIGITAL_GAIN_MASK (0x7F << 0) + +/* DA7218_OUT_1L_GAIN = 0xF8 */ +#define DA7218_OUT_1L_DIGITAL_GAIN_SHIFT 0 +#define DA7218_OUT_1L_DIGITAL_GAIN_MASK (0xFF << 0) +#define DA7218_OUT_DIGITAL_GAIN_MIN 0x0 +#define DA7218_OUT_DIGITAL_GAIN_MAX 0x97 + +/* DA7218_OUT_1R_GAIN = 0xF9 */ +#define DA7218_OUT_1R_DIGITAL_GAIN_SHIFT 0 +#define DA7218_OUT_1R_DIGITAL_GAIN_MASK (0xFF << 0) + +/* DA7218_MICBIAS_CTRL = 0xFC */ +#define DA7218_MICBIAS_1_LEVEL_SHIFT 0 +#define DA7218_MICBIAS_1_LEVEL_MASK (0x7 << 0) +#define DA7218_MICBIAS_1_LP_MODE_SHIFT 3 +#define DA7218_MICBIAS_1_LP_MODE_MASK (0x1 << 3) +#define DA7218_MICBIAS_2_LEVEL_SHIFT 4 +#define DA7218_MICBIAS_2_LEVEL_MASK (0x7 << 4) +#define DA7218_MICBIAS_2_LP_MODE_SHIFT 7 +#define DA7218_MICBIAS_2_LP_MODE_MASK (0x1 << 7) + +/* DA7218_MICBIAS_EN = 0xFD */ +#define DA7218_MICBIAS_1_EN_SHIFT 0 +#define DA7218_MICBIAS_1_EN_MASK (0x1 << 0) +#define DA7218_MICBIAS_2_EN_SHIFT 4 +#define DA7218_MICBIAS_2_EN_MASK (0x1 << 4) + + +/* + * General defines & data + */ + +/* Register inversion */ +#define DA7218_NO_INVERT 0 +#define DA7218_INVERT 1 + +/* Byte related defines */ +#define DA7218_BYTE_SHIFT 8 +#define DA7218_BYTE_MASK 0xFF +#define DA7218_2BYTE_SHIFT 16 +#define DA7218_2BYTE_MASK 0xFFFF + +/* PLL Output Frequencies */ +#define DA7218_PLL_FREQ_OUT_90316 90316800 +#define DA7218_PLL_FREQ_OUT_98304 98304000 + +/* PLL Frequency Dividers */ +#define DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL 1 +#define DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL 2 +#define DA7218_PLL_INDIV_9_TO_18_MHZ_VAL 4 +#define DA7218_PLL_INDIV_18_TO_36_MHZ_VAL 8 +#define DA7218_PLL_INDIV_36_TO_54_MHZ_VAL 16 + +/* ALC Calibration */ +#define DA7218_ALC_CALIB_DELAY_MIN 2500 +#define DA7218_ALC_CALIB_DELAY_MAX 5000 +#define DA7218_ALC_CALIB_MAX_TRIES 5 + +/* Ref Oscillator */ +#define DA7218_REF_OSC_CHECK_DELAY_MIN 5000 +#define DA7218_REF_OSC_CHECK_DELAY_MAX 10000 +#define DA7218_REF_OSC_CHECK_TRIES 4 + +/* SRM */ +#define DA7218_SRM_CHECK_DELAY 50 +#define DA7218_SRM_CHECK_TRIES 8 + +/* Mic Level Detect */ +#define DA7218_MIC_LVL_DET_DELAY 50 + +enum da7218_biq_cfg { + DA7218_BIQ_CFG_DATA = 0, + DA7218_BIQ_CFG_ADDR, + DA7218_BIQ_CFG_SIZE, +}; + +enum da7218_clk_src { + DA7218_CLKSRC_MCLK = 0, + DA7218_CLKSRC_MCLK_SQR, +}; + +enum da7218_sys_clk { + DA7218_SYSCLK_MCLK = 0, + DA7218_SYSCLK_PLL, + DA7218_SYSCLK_PLL_SRM, +}; + +enum da7218_dev_id { + DA7217_DEV_ID = 0, + DA7218_DEV_ID, +}; + +/* Regulators */ +enum da7218_supplies { + DA7218_SUPPLY_VDD = 0, + DA7218_SUPPLY_VDDMIC, + DA7218_SUPPLY_VDDIO, + DA7218_NUM_SUPPLIES, +}; + +/* Private data */ +struct da7218_priv { + struct da7218_pdata *pdata; + + struct regulator_bulk_data supplies[DA7218_NUM_SUPPLIES]; + struct regmap *regmap; + int dev_id; + + struct snd_soc_jack *jack; + int irq; + + struct clk *mclk; + unsigned int mclk_rate; + + bool hp_single_supply; + bool master; + u8 alc_en; + u8 in_filt_en; + u8 mic_lvl_det_en; + + u8 biq_5stage_coeff[DA7218_OUT_1_BIQ_5STAGE_CFG_SIZE]; + u8 stbiq_3stage_coeff[DA7218_SIDETONE_BIQ_3STAGE_CFG_SIZE]; +}; + +/* HP detect control */ +int da7218_hpldet(struct snd_soc_component *component, struct snd_soc_jack *jack); + +#endif /* _DA7218_H */ diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c new file mode 100644 index 0000000000..8537c96307 --- /dev/null +++ b/sound/soc/codecs/da7219-aad.c @@ -0,0 +1,1071 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver + * + * Copyright (c) 2015 Dialog Semiconductor Ltd. + * + * Author: Adam Thomson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "da7219.h" +#include "da7219-aad.h" + + +/* + * Detection control + */ + +void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + da7219->aad->jack = jack; + da7219->aad->jack_inserted = false; + + /* Send an initial empty report */ + snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK); + + /* Enable/Disable jack detection */ + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, + (jack ? DA7219_ACCDET_EN_MASK : 0)); +} + +/* + * Button/HPTest work + */ + +static void da7219_aad_btn_det_work(struct work_struct *work) +{ + struct da7219_aad_priv *da7219_aad = + container_of(work, struct da7219_aad_priv, btn_det_work); + struct snd_soc_component *component = da7219_aad->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + u8 statusa, micbias_ctrl; + bool micbias_up = false; + int retries = 0; + + /* Drive headphones/lineout */ + snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK, + DA7219_HP_L_AMP_OE_MASK); + snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK, + DA7219_HP_R_AMP_OE_MASK); + + /* Make sure mic bias is up */ + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + + do { + statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A); + if (statusa & DA7219_MICBIAS_UP_STS_MASK) + micbias_up = true; + else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES) + msleep(DA7219_AAD_MICBIAS_CHK_DELAY); + } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES)); + + if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES) + dev_warn(component->dev, "Mic bias status check timed out"); + + da7219->micbias_on_event = true; + + /* + * Mic bias pulse required to enable mic, must be done before enabling + * button detection to prevent erroneous button readings. + */ + if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) { + /* Pulse higher level voltage */ + micbias_ctrl = snd_soc_component_read(component, DA7219_MICBIAS_CTRL); + snd_soc_component_update_bits(component, DA7219_MICBIAS_CTRL, + DA7219_MICBIAS1_LEVEL_MASK, + da7219_aad->micbias_pulse_lvl); + msleep(da7219_aad->micbias_pulse_time); + snd_soc_component_write(component, DA7219_MICBIAS_CTRL, micbias_ctrl); + + } + + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, + da7219_aad->btn_cfg); +} + +static void da7219_aad_hptest_work(struct work_struct *work) +{ + struct da7219_aad_priv *da7219_aad = + container_of(work, struct da7219_aad_priv, hptest_work); + struct snd_soc_component *component = da7219_aad->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + __le16 tonegen_freq_hptest; + u8 pll_srm_sts, pll_ctrl, gain_ramp_ctrl, accdet_cfg8; + int report = 0, ret; + + /* Lock DAPM, Kcontrols affected by this test and the PLL */ + snd_soc_dapm_mutex_lock(dapm); + mutex_lock(&da7219->ctrl_lock); + mutex_lock(&da7219->pll_lock); + + /* Ensure MCLK is available for HP test procedure */ + if (da7219->mclk) { + ret = clk_prepare_enable(da7219->mclk); + if (ret) { + dev_err(component->dev, "Failed to enable mclk - %d\n", ret); + mutex_unlock(&da7219->pll_lock); + mutex_unlock(&da7219->ctrl_lock); + snd_soc_dapm_mutex_unlock(dapm); + return; + } + } + + /* + * If MCLK not present, then we're using the internal oscillator and + * require different frequency settings to achieve the same result. + * + * If MCLK is present, but PLL is not enabled then we enable it here to + * ensure a consistent detection procedure. + */ + pll_srm_sts = snd_soc_component_read(component, DA7219_PLL_SRM_STS); + if (pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) { + tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ); + + pll_ctrl = snd_soc_component_read(component, DA7219_PLL_CTRL); + if ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS) + da7219_set_pll(component, DA7219_SYSCLK_PLL, + DA7219_PLL_FREQ_OUT_98304); + } else { + tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC); + } + + /* Ensure gain ramping at fastest rate */ + gain_ramp_ctrl = snd_soc_component_read(component, DA7219_GAIN_RAMP_CTRL); + snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_X8); + + /* Bypass cache so it saves current settings */ + regcache_cache_bypass(da7219->regmap, true); + + /* Make sure Tone Generator is disabled */ + snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, 0); + + /* Enable HPTest block, 1KOhms check */ + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8, + DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK, + DA7219_HPTEST_EN_MASK | + DA7219_HPTEST_RES_SEL_1KOHMS); + + /* Set gains to 0db */ + snd_soc_component_write(component, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB); + snd_soc_component_write(component, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB); + snd_soc_component_write(component, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB); + snd_soc_component_write(component, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB); + + /* Disable DAC filters, EQs and soft mute */ + snd_soc_component_update_bits(component, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK, + 0); + snd_soc_component_update_bits(component, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK, + 0); + snd_soc_component_update_bits(component, DA7219_DAC_FILTERS5, + DA7219_DAC_SOFTMUTE_EN_MASK, 0); + + /* Enable HP left & right paths */ + snd_soc_component_update_bits(component, DA7219_CP_CTRL, DA7219_CP_EN_MASK, + DA7219_CP_EN_MASK); + snd_soc_component_update_bits(component, DA7219_DIG_ROUTING_DAC, + DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK, + DA7219_DAC_L_SRC_TONEGEN | + DA7219_DAC_R_SRC_TONEGEN); + snd_soc_component_update_bits(component, DA7219_DAC_L_CTRL, + DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK, + DA7219_DAC_L_EN_MASK); + snd_soc_component_update_bits(component, DA7219_DAC_R_CTRL, + DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK, + DA7219_DAC_R_EN_MASK); + snd_soc_component_update_bits(component, DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_L_MIX_SELECT_MASK, + DA7219_MIXOUT_L_MIX_SELECT_MASK); + snd_soc_component_update_bits(component, DA7219_MIXOUT_R_SELECT, + DA7219_MIXOUT_R_MIX_SELECT_MASK, + DA7219_MIXOUT_R_MIX_SELECT_MASK); + snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1L, + DA7219_OUTFILT_ST_1L_SRC_MASK, + DA7219_DMIX_ST_SRC_OUTFILT1L); + snd_soc_component_update_bits(component, DA7219_DROUTING_ST_OUTFILT_1R, + DA7219_OUTFILT_ST_1R_SRC_MASK, + DA7219_DMIX_ST_SRC_OUTFILT1R); + snd_soc_component_update_bits(component, DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_L_AMP_EN_MASK, + DA7219_MIXOUT_L_AMP_EN_MASK); + snd_soc_component_update_bits(component, DA7219_MIXOUT_R_CTRL, + DA7219_MIXOUT_R_AMP_EN_MASK, + DA7219_MIXOUT_R_AMP_EN_MASK); + snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK, + DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK); + snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK, + DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK); + msleep(DA7219_SETTLING_DELAY); + snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_MUTE_EN_MASK | + DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, 0); + snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_MUTE_EN_MASK | + DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, 0); + + /* + * If we're running from the internal oscillator then give audio paths + * time to settle before running test. + */ + if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK)) + msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY); + + /* Configure & start Tone Generator */ + snd_soc_component_write(component, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK); + regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L, + &tonegen_freq_hptest, sizeof(tonegen_freq_hptest)); + snd_soc_component_update_bits(component, DA7219_TONE_GEN_CFG2, + DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK, + DA7219_SWG_SEL_SRAMP | + DA7219_TONE_GEN_GAIN_MINUS_15DB); + snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK); + + msleep(DA7219_AAD_HPTEST_PERIOD); + + /* Grab comparator reading */ + accdet_cfg8 = snd_soc_component_read(component, DA7219_ACCDET_CONFIG_8); + if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK) + report |= SND_JACK_HEADPHONE; + else + report |= SND_JACK_LINEOUT; + + /* Stop tone generator */ + snd_soc_component_write(component, DA7219_TONE_GEN_CFG1, 0); + + msleep(DA7219_AAD_HPTEST_PERIOD); + + /* Restore original settings from cache */ + regcache_mark_dirty(da7219->regmap); + regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL); + msleep(DA7219_SETTLING_DELAY); + regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L, + DA7219_DROUTING_ST_OUTFILT_1R); + regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_R_SELECT); + regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC, + DA7219_DIG_ROUTING_DAC); + regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL); + regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5, + DA7219_DAC_FILTERS5); + regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4, + DA7219_DAC_FILTERS1); + regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN, + DA7219_HP_R_GAIN); + regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN, + DA7219_DAC_R_GAIN); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER, + DA7219_TONE_GEN_ON_PER); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L, + DA7219_TONE_GEN_FREQ1_U); + regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1, + DA7219_TONE_GEN_CFG2); + + regcache_cache_bypass(da7219->regmap, false); + + /* Disable HPTest block */ + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_8, + DA7219_HPTEST_EN_MASK, 0); + + /* + * If we're running from the internal oscillator then give audio paths + * time to settle before allowing headphones to be driven as required. + */ + if (!(pll_srm_sts & DA7219_PLL_SRM_STS_MCLK)) + msleep(DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY); + + /* Restore gain ramping rate */ + snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, gain_ramp_ctrl); + + /* Drive Headphones/lineout */ + snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK, + DA7219_HP_L_AMP_OE_MASK); + snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK, + DA7219_HP_R_AMP_OE_MASK); + + /* Restore PLL to previous configuration, if re-configured */ + if ((pll_srm_sts & DA7219_PLL_SRM_STS_MCLK) && + ((pll_ctrl & DA7219_PLL_MODE_MASK) == DA7219_PLL_MODE_BYPASS)) + da7219_set_pll(component, DA7219_SYSCLK_MCLK, 0); + + /* Remove MCLK, if previously enabled */ + if (da7219->mclk) + clk_disable_unprepare(da7219->mclk); + + mutex_unlock(&da7219->pll_lock); + mutex_unlock(&da7219->ctrl_lock); + snd_soc_dapm_mutex_unlock(dapm); + + /* + * Only send report if jack hasn't been removed during process, + * otherwise it's invalid and we drop it. + */ + if (da7219_aad->jack_inserted) + snd_soc_jack_report(da7219_aad->jack, report, + SND_JACK_HEADSET | SND_JACK_LINEOUT); +} + +static void da7219_aad_jack_det_work(struct work_struct *work) +{ + struct da7219_aad_priv *da7219_aad = + container_of(work, struct da7219_aad_priv, jack_det_work.work); + struct snd_soc_component *component = da7219_aad->component; + + /* Enable ground switch */ + snd_soc_component_update_bits(component, 0xFB, 0x01, 0x01); +} + +/* + * IRQ + */ + +static irqreturn_t da7219_aad_irq_thread(int irq, void *data) +{ + struct da7219_aad_priv *da7219_aad = data; + struct snd_soc_component *component = da7219_aad->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + u8 events[DA7219_AAD_IRQ_REG_MAX]; + u8 statusa; + int i, ret, report = 0, mask = 0; + + /* Read current IRQ events */ + ret = regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A, + events, DA7219_AAD_IRQ_REG_MAX); + if (ret) { + dev_warn_ratelimited(component->dev, "Failed to read IRQ events: %d\n", ret); + return IRQ_NONE; + } + + if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B]) + return IRQ_NONE; + + /* Read status register for jack insertion & type status */ + statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A); + + if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_INSERTED_MASK) { + u8 srm_st; + int delay = 0; + + srm_st = snd_soc_component_read(component, + DA7219_PLL_SRM_STS) & DA7219_PLL_SRM_STS_MCLK; + delay = (da7219_aad->gnd_switch_delay * ((srm_st == 0x0) ? 2 : 1) - 2); + queue_delayed_work(da7219_aad->aad_wq, + &da7219_aad->jack_det_work, + msecs_to_jiffies(delay)); + } + + /* Clear events */ + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A, + events, DA7219_AAD_IRQ_REG_MAX); + + dev_dbg(component->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n", + events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B], + statusa); + + if (statusa & DA7219_JACK_INSERTION_STS_MASK) { + /* Jack Insertion */ + if (events[DA7219_AAD_IRQ_REG_A] & + DA7219_E_JACK_INSERTED_MASK) { + report |= SND_JACK_MECHANICAL; + mask |= SND_JACK_MECHANICAL; + da7219_aad->jack_inserted = true; + } + + /* Jack type detection */ + if (events[DA7219_AAD_IRQ_REG_A] & + DA7219_E_JACK_DETECT_COMPLETE_MASK) { + /* + * If 4-pole, then enable button detection, else perform + * HP impedance test to determine output type to report. + * + * We schedule work here as the tasks themselves can + * take time to complete, and in particular for hptest + * we want to be able to check if the jack was removed + * during the procedure as this will invalidate the + * result. By doing this as work, the IRQ thread can + * handle a removal, and we can check at the end of + * hptest if we have a valid result or not. + */ + + cancel_delayed_work_sync(&da7219_aad->jack_det_work); + /* Disable ground switch */ + snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00); + + if (statusa & DA7219_JACK_TYPE_STS_MASK) { + report |= SND_JACK_HEADSET; + mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT; + queue_work(da7219_aad->aad_wq, &da7219_aad->btn_det_work); + } else { + queue_work(da7219_aad->aad_wq, &da7219_aad->hptest_work); + } + } + + /* Button support for 4-pole jack */ + if (statusa & DA7219_JACK_TYPE_STS_MASK) { + for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) { + /* Button Press */ + if (events[DA7219_AAD_IRQ_REG_B] & + (DA7219_E_BUTTON_A_PRESSED_MASK << i)) { + report |= SND_JACK_BTN_0 >> i; + mask |= SND_JACK_BTN_0 >> i; + } + } + snd_soc_jack_report(da7219_aad->jack, report, mask); + + for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) { + /* Button Release */ + if (events[DA7219_AAD_IRQ_REG_B] & + (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) { + report &= ~(SND_JACK_BTN_0 >> i); + mask |= SND_JACK_BTN_0 >> i; + } + } + } + } else { + /* Jack removal */ + if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) { + report = 0; + mask |= DA7219_AAD_REPORT_ALL_MASK; + da7219_aad->jack_inserted = false; + + /* Cancel any pending work */ + cancel_delayed_work_sync(&da7219_aad->jack_det_work); + cancel_work_sync(&da7219_aad->btn_det_work); + cancel_work_sync(&da7219_aad->hptest_work); + + /* Un-drive headphones/lineout */ + snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_OE_MASK, 0); + snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_OE_MASK, 0); + + /* Ensure button detection disabled */ + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, 0); + + da7219->micbias_on_event = false; + + /* Disable mic bias */ + snd_soc_dapm_disable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + + /* Disable ground switch */ + snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00); + } + } + + snd_soc_jack_report(da7219_aad->jack, report, mask); + + return IRQ_HANDLED; +} + +/* + * DT/ACPI to pdata conversion + */ + +static enum da7219_aad_micbias_pulse_lvl + da7219_aad_fw_micbias_pulse_lvl(struct device *dev, u32 val) +{ + switch (val) { + case 2800: + return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V; + case 2900: + return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V; + default: + dev_warn(dev, "Invalid micbias pulse level"); + return DA7219_AAD_MICBIAS_PULSE_LVL_OFF; + } +} + +static enum da7219_aad_btn_cfg + da7219_aad_fw_btn_cfg(struct device *dev, u32 val) +{ + switch (val) { + case 2: + return DA7219_AAD_BTN_CFG_2MS; + case 5: + return DA7219_AAD_BTN_CFG_5MS; + case 10: + return DA7219_AAD_BTN_CFG_10MS; + case 50: + return DA7219_AAD_BTN_CFG_50MS; + case 100: + return DA7219_AAD_BTN_CFG_100MS; + case 200: + return DA7219_AAD_BTN_CFG_200MS; + case 500: + return DA7219_AAD_BTN_CFG_500MS; + default: + dev_warn(dev, "Invalid button config"); + return DA7219_AAD_BTN_CFG_10MS; + } +} + +static enum da7219_aad_mic_det_thr + da7219_aad_fw_mic_det_thr(struct device *dev, u32 val) +{ + switch (val) { + case 200: + return DA7219_AAD_MIC_DET_THR_200_OHMS; + case 500: + return DA7219_AAD_MIC_DET_THR_500_OHMS; + case 750: + return DA7219_AAD_MIC_DET_THR_750_OHMS; + case 1000: + return DA7219_AAD_MIC_DET_THR_1000_OHMS; + default: + dev_warn(dev, "Invalid mic detect threshold"); + return DA7219_AAD_MIC_DET_THR_500_OHMS; + } +} + +static enum da7219_aad_jack_ins_deb + da7219_aad_fw_jack_ins_deb(struct device *dev, u32 val) +{ + switch (val) { + case 5: + return DA7219_AAD_JACK_INS_DEB_5MS; + case 10: + return DA7219_AAD_JACK_INS_DEB_10MS; + case 20: + return DA7219_AAD_JACK_INS_DEB_20MS; + case 50: + return DA7219_AAD_JACK_INS_DEB_50MS; + case 100: + return DA7219_AAD_JACK_INS_DEB_100MS; + case 200: + return DA7219_AAD_JACK_INS_DEB_200MS; + case 500: + return DA7219_AAD_JACK_INS_DEB_500MS; + case 1000: + return DA7219_AAD_JACK_INS_DEB_1S; + default: + dev_warn(dev, "Invalid jack insert debounce"); + return DA7219_AAD_JACK_INS_DEB_20MS; + } +} + +static enum da7219_aad_jack_ins_det_pty + da7219_aad_fw_jack_ins_det_pty(struct device *dev, const char *str) +{ + if (!strcmp(str, "low")) { + return DA7219_AAD_JACK_INS_DET_PTY_LOW; + } else if (!strcmp(str, "high")) { + return DA7219_AAD_JACK_INS_DET_PTY_HIGH; + } else { + dev_warn(dev, "Invalid jack insertion detection polarity"); + return DA7219_AAD_JACK_INS_DET_PTY_LOW; + } +} + +static enum da7219_aad_jack_det_rate + da7219_aad_fw_jack_det_rate(struct device *dev, const char *str) +{ + if (!strcmp(str, "32_64")) { + return DA7219_AAD_JACK_DET_RATE_32_64MS; + } else if (!strcmp(str, "64_128")) { + return DA7219_AAD_JACK_DET_RATE_64_128MS; + } else if (!strcmp(str, "128_256")) { + return DA7219_AAD_JACK_DET_RATE_128_256MS; + } else if (!strcmp(str, "256_512")) { + return DA7219_AAD_JACK_DET_RATE_256_512MS; + } else { + dev_warn(dev, "Invalid jack detect rate"); + return DA7219_AAD_JACK_DET_RATE_256_512MS; + } +} + +static enum da7219_aad_jack_rem_deb + da7219_aad_fw_jack_rem_deb(struct device *dev, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_JACK_REM_DEB_1MS; + case 5: + return DA7219_AAD_JACK_REM_DEB_5MS; + case 10: + return DA7219_AAD_JACK_REM_DEB_10MS; + case 20: + return DA7219_AAD_JACK_REM_DEB_20MS; + default: + dev_warn(dev, "Invalid jack removal debounce"); + return DA7219_AAD_JACK_REM_DEB_1MS; + } +} + +static enum da7219_aad_btn_avg + da7219_aad_fw_btn_avg(struct device *dev, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_BTN_AVG_1; + case 2: + return DA7219_AAD_BTN_AVG_2; + case 4: + return DA7219_AAD_BTN_AVG_4; + case 8: + return DA7219_AAD_BTN_AVG_8; + default: + dev_warn(dev, "Invalid button average value"); + return DA7219_AAD_BTN_AVG_2; + } +} + +static enum da7219_aad_adc_1bit_rpt + da7219_aad_fw_adc_1bit_rpt(struct device *dev, u32 val) +{ + switch (val) { + case 1: + return DA7219_AAD_ADC_1BIT_RPT_1; + case 2: + return DA7219_AAD_ADC_1BIT_RPT_2; + case 4: + return DA7219_AAD_ADC_1BIT_RPT_4; + case 8: + return DA7219_AAD_ADC_1BIT_RPT_8; + default: + dev_warn(dev, "Invalid ADC 1-bit repeat value"); + return DA7219_AAD_ADC_1BIT_RPT_1; + } +} + +static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct fwnode_handle *aad_np; + struct da7219_aad_pdata *aad_pdata; + const char *fw_str; + u32 fw_val32; + + aad_np = device_get_named_child_node(dev, "da7219_aad"); + if (!aad_np) + return NULL; + + aad_pdata = devm_kzalloc(dev, sizeof(*aad_pdata), GFP_KERNEL); + if (!aad_pdata) + return NULL; + + aad_pdata->irq = i2c->irq; + + if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-lvl", + &fw_val32) >= 0) + aad_pdata->micbias_pulse_lvl = + da7219_aad_fw_micbias_pulse_lvl(dev, fw_val32); + else + aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF; + + if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-time", + &fw_val32) >= 0) + aad_pdata->micbias_pulse_time = fw_val32; + + if (fwnode_property_read_u32(aad_np, "dlg,btn-cfg", &fw_val32) >= 0) + aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(dev, fw_val32); + else + aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS; + + if (fwnode_property_read_u32(aad_np, "dlg,mic-det-thr", &fw_val32) >= 0) + aad_pdata->mic_det_thr = + da7219_aad_fw_mic_det_thr(dev, fw_val32); + else + aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_200_OHMS; + + if (fwnode_property_read_u32(aad_np, "dlg,jack-ins-deb", &fw_val32) >= 0) + aad_pdata->jack_ins_deb = + da7219_aad_fw_jack_ins_deb(dev, fw_val32); + else + aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS; + + if (!fwnode_property_read_string(aad_np, "dlg,jack-ins-det-pty", &fw_str)) + aad_pdata->jack_ins_det_pty = + da7219_aad_fw_jack_ins_det_pty(dev, fw_str); + else + aad_pdata->jack_ins_det_pty = DA7219_AAD_JACK_INS_DET_PTY_LOW; + + if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str)) + aad_pdata->jack_det_rate = + da7219_aad_fw_jack_det_rate(dev, fw_str); + else + aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS; + + if (fwnode_property_read_u32(aad_np, "dlg,jack-rem-deb", &fw_val32) >= 0) + aad_pdata->jack_rem_deb = + da7219_aad_fw_jack_rem_deb(dev, fw_val32); + else + aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS; + + if (fwnode_property_read_u32(aad_np, "dlg,a-d-btn-thr", &fw_val32) >= 0) + aad_pdata->a_d_btn_thr = (u8) fw_val32; + else + aad_pdata->a_d_btn_thr = 0xA; + + if (fwnode_property_read_u32(aad_np, "dlg,d-b-btn-thr", &fw_val32) >= 0) + aad_pdata->d_b_btn_thr = (u8) fw_val32; + else + aad_pdata->d_b_btn_thr = 0x16; + + if (fwnode_property_read_u32(aad_np, "dlg,b-c-btn-thr", &fw_val32) >= 0) + aad_pdata->b_c_btn_thr = (u8) fw_val32; + else + aad_pdata->b_c_btn_thr = 0x21; + + if (fwnode_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &fw_val32) >= 0) + aad_pdata->c_mic_btn_thr = (u8) fw_val32; + else + aad_pdata->c_mic_btn_thr = 0x3E; + + if (fwnode_property_read_u32(aad_np, "dlg,btn-avg", &fw_val32) >= 0) + aad_pdata->btn_avg = da7219_aad_fw_btn_avg(dev, fw_val32); + else + aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2; + + if (fwnode_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &fw_val32) >= 0) + aad_pdata->adc_1bit_rpt = + da7219_aad_fw_adc_1bit_rpt(dev, fw_val32); + else + aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1; + + return aad_pdata; +} + +static void da7219_aad_handle_pdata(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct da7219_aad_priv *da7219_aad = da7219->aad; + struct da7219_pdata *pdata = da7219->pdata; + + if ((pdata) && (pdata->aad_pdata)) { + struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata; + u8 cfg, mask; + + da7219_aad->irq = aad_pdata->irq; + + switch (aad_pdata->micbias_pulse_lvl) { + case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V: + case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V: + da7219_aad->micbias_pulse_lvl = + (aad_pdata->micbias_pulse_lvl << + DA7219_MICBIAS1_LEVEL_SHIFT); + break; + default: + break; + } + + da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time; + + switch (aad_pdata->btn_cfg) { + case DA7219_AAD_BTN_CFG_2MS: + case DA7219_AAD_BTN_CFG_5MS: + case DA7219_AAD_BTN_CFG_10MS: + case DA7219_AAD_BTN_CFG_50MS: + case DA7219_AAD_BTN_CFG_100MS: + case DA7219_AAD_BTN_CFG_200MS: + case DA7219_AAD_BTN_CFG_500MS: + da7219_aad->btn_cfg = (aad_pdata->btn_cfg << + DA7219_BUTTON_CONFIG_SHIFT); + } + + cfg = 0; + mask = 0; + switch (aad_pdata->mic_det_thr) { + case DA7219_AAD_MIC_DET_THR_200_OHMS: + case DA7219_AAD_MIC_DET_THR_500_OHMS: + case DA7219_AAD_MIC_DET_THR_750_OHMS: + case DA7219_AAD_MIC_DET_THR_1000_OHMS: + cfg |= (aad_pdata->mic_det_thr << + DA7219_MIC_DET_THRESH_SHIFT); + mask |= DA7219_MIC_DET_THRESH_MASK; + } + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, mask, cfg); + + cfg = 0; + mask = 0; + switch (aad_pdata->jack_ins_deb) { + case DA7219_AAD_JACK_INS_DEB_5MS: + case DA7219_AAD_JACK_INS_DEB_10MS: + case DA7219_AAD_JACK_INS_DEB_20MS: + case DA7219_AAD_JACK_INS_DEB_50MS: + case DA7219_AAD_JACK_INS_DEB_100MS: + case DA7219_AAD_JACK_INS_DEB_200MS: + case DA7219_AAD_JACK_INS_DEB_500MS: + case DA7219_AAD_JACK_INS_DEB_1S: + cfg |= (aad_pdata->jack_ins_deb << + DA7219_JACKDET_DEBOUNCE_SHIFT); + mask |= DA7219_JACKDET_DEBOUNCE_MASK; + } + switch (aad_pdata->jack_det_rate) { + case DA7219_AAD_JACK_DET_RATE_32_64MS: + case DA7219_AAD_JACK_DET_RATE_64_128MS: + case DA7219_AAD_JACK_DET_RATE_128_256MS: + case DA7219_AAD_JACK_DET_RATE_256_512MS: + cfg |= (aad_pdata->jack_det_rate << + DA7219_JACK_DETECT_RATE_SHIFT); + mask |= DA7219_JACK_DETECT_RATE_MASK; + } + switch (aad_pdata->jack_rem_deb) { + case DA7219_AAD_JACK_REM_DEB_1MS: + case DA7219_AAD_JACK_REM_DEB_5MS: + case DA7219_AAD_JACK_REM_DEB_10MS: + case DA7219_AAD_JACK_REM_DEB_20MS: + cfg |= (aad_pdata->jack_rem_deb << + DA7219_JACKDET_REM_DEB_SHIFT); + mask |= DA7219_JACKDET_REM_DEB_MASK; + } + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_2, mask, cfg); + + snd_soc_component_write(component, DA7219_ACCDET_CONFIG_3, + aad_pdata->a_d_btn_thr); + snd_soc_component_write(component, DA7219_ACCDET_CONFIG_4, + aad_pdata->d_b_btn_thr); + snd_soc_component_write(component, DA7219_ACCDET_CONFIG_5, + aad_pdata->b_c_btn_thr); + snd_soc_component_write(component, DA7219_ACCDET_CONFIG_6, + aad_pdata->c_mic_btn_thr); + + cfg = 0; + mask = 0; + switch (aad_pdata->btn_avg) { + case DA7219_AAD_BTN_AVG_1: + case DA7219_AAD_BTN_AVG_2: + case DA7219_AAD_BTN_AVG_4: + case DA7219_AAD_BTN_AVG_8: + cfg |= (aad_pdata->btn_avg << + DA7219_BUTTON_AVERAGE_SHIFT); + mask |= DA7219_BUTTON_AVERAGE_MASK; + } + switch (aad_pdata->adc_1bit_rpt) { + case DA7219_AAD_ADC_1BIT_RPT_1: + case DA7219_AAD_ADC_1BIT_RPT_2: + case DA7219_AAD_ADC_1BIT_RPT_4: + case DA7219_AAD_ADC_1BIT_RPT_8: + cfg |= (aad_pdata->adc_1bit_rpt << + DA7219_ADC_1_BIT_REPEAT_SHIFT); + mask |= DA7219_ADC_1_BIT_REPEAT_MASK; + } + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_7, mask, cfg); + + switch (aad_pdata->jack_ins_det_pty) { + case DA7219_AAD_JACK_INS_DET_PTY_LOW: + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0x75, 0x80); + snd_soc_component_write(component, 0xF0, 0x00); + break; + case DA7219_AAD_JACK_INS_DET_PTY_HIGH: + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0x75, 0x00); + snd_soc_component_write(component, 0xF0, 0x00); + break; + default: + break; + } + } +} + +static void da7219_aad_handle_gnd_switch_time(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct da7219_aad_priv *da7219_aad = da7219->aad; + u8 jack_det; + + jack_det = snd_soc_component_read(component, DA7219_ACCDET_CONFIG_2) + & DA7219_JACK_DETECT_RATE_MASK; + switch (jack_det) { + case 0x00: + da7219_aad->gnd_switch_delay = 32; + break; + case 0x10: + da7219_aad->gnd_switch_delay = 64; + break; + case 0x20: + da7219_aad->gnd_switch_delay = 128; + break; + case 0x30: + da7219_aad->gnd_switch_delay = 256; + break; + default: + da7219_aad->gnd_switch_delay = 32; + break; + } +} + +/* + * Suspend/Resume + */ + +void da7219_aad_suspend(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct da7219_aad_priv *da7219_aad = da7219->aad; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + u8 micbias_ctrl; + + if (da7219_aad->jack) { + /* Disable jack detection during suspend */ + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, 0); + + /* + * If we have a 4-pole jack inserted, then micbias will be + * enabled. We can disable micbias here, and keep a note to + * re-enable it on resume. If jack removal occurred during + * suspend then this will be dealt with through the IRQ handler. + */ + if (da7219_aad->jack_inserted) { + micbias_ctrl = snd_soc_component_read(component, DA7219_MICBIAS_CTRL); + if (micbias_ctrl & DA7219_MICBIAS1_EN_MASK) { + snd_soc_dapm_disable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + da7219_aad->micbias_resume_enable = true; + } + } + } + + synchronize_irq(da7219_aad->irq); +} + +void da7219_aad_resume(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct da7219_aad_priv *da7219_aad = da7219->aad; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + if (da7219_aad->jack) { + /* Re-enable micbias if previously enabled for 4-pole jack */ + if (da7219_aad->jack_inserted && + da7219_aad->micbias_resume_enable) { + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + snd_soc_dapm_sync(dapm); + da7219_aad->micbias_resume_enable = false; + } + + /* Re-enable jack detection */ + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, + DA7219_ACCDET_EN_MASK); + } +} + + +/* + * Init/Exit + */ + +int da7219_aad_init(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct da7219_aad_priv *da7219_aad = da7219->aad; + u8 mask[DA7219_AAD_IRQ_REG_MAX]; + int ret; + + da7219_aad->component = component; + + /* Handle any DT/ACPI/platform data */ + da7219_aad_handle_pdata(component); + + /* Disable button detection */ + snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1, + DA7219_BUTTON_CONFIG_MASK, 0); + + da7219_aad_handle_gnd_switch_time(component); + + da7219_aad->aad_wq = create_singlethread_workqueue("da7219-aad"); + if (!da7219_aad->aad_wq) { + dev_err(component->dev, "Failed to create aad workqueue\n"); + return -ENOMEM; + } + + INIT_DELAYED_WORK(&da7219_aad->jack_det_work, da7219_aad_jack_det_work); + INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work); + INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work); + + ret = request_threaded_irq(da7219_aad->irq, NULL, + da7219_aad_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "da7219-aad", da7219_aad); + if (ret) { + dev_err(component->dev, "Failed to request IRQ: %d\n", ret); + return ret; + } + + /* Unmask AAD IRQs */ + memset(mask, 0, DA7219_AAD_IRQ_REG_MAX); + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A, + &mask, DA7219_AAD_IRQ_REG_MAX); + + return 0; +} + +void da7219_aad_exit(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct da7219_aad_priv *da7219_aad = da7219->aad; + u8 mask[DA7219_AAD_IRQ_REG_MAX]; + + /* Mask off AAD IRQs */ + memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX); + regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A, + mask, DA7219_AAD_IRQ_REG_MAX); + + free_irq(da7219_aad->irq, da7219_aad); + + cancel_delayed_work_sync(&da7219_aad->jack_det_work); + cancel_work_sync(&da7219_aad->btn_det_work); + cancel_work_sync(&da7219_aad->hptest_work); + destroy_workqueue(da7219_aad->aad_wq); +} + +/* + * AAD related I2C probe handling + */ + +int da7219_aad_probe(struct i2c_client *i2c) +{ + struct da7219_priv *da7219 = i2c_get_clientdata(i2c); + struct device *dev = &i2c->dev; + struct da7219_aad_priv *da7219_aad; + + da7219_aad = devm_kzalloc(dev, sizeof(*da7219_aad), GFP_KERNEL); + if (!da7219_aad) + return -ENOMEM; + + da7219->aad = da7219_aad; + + /* Retrieve any DT/ACPI/platform data */ + if (da7219->pdata && !da7219->pdata->aad_pdata) + da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(dev); + + return 0; +} + +MODULE_DESCRIPTION("ASoC DA7219 AAD Driver"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_AUTHOR("David Rau "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h new file mode 100644 index 0000000000..fbfbf3e679 --- /dev/null +++ b/sound/soc/codecs/da7219-aad.h @@ -0,0 +1,222 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * da7219-aad.h - DA7322 ASoC AAD Driver + * + * Copyright (c) 2015 Dialog Semiconductor Ltd. + * + * Author: Adam Thomson + */ + +#ifndef __DA7219_AAD_H +#define __DA7219_AAD_H + +#include +#include +#include +#include +#include + +/* + * Registers + */ + +#define DA7219_ACCDET_STATUS_A 0xC0 +#define DA7219_ACCDET_STATUS_B 0xC1 +#define DA7219_ACCDET_IRQ_EVENT_A 0xC2 +#define DA7219_ACCDET_IRQ_EVENT_B 0xC3 +#define DA7219_ACCDET_IRQ_MASK_A 0xC4 +#define DA7219_ACCDET_IRQ_MASK_B 0xC5 +#define DA7219_ACCDET_CONFIG_1 0xC6 +#define DA7219_ACCDET_CONFIG_2 0xC7 +#define DA7219_ACCDET_CONFIG_3 0xC8 +#define DA7219_ACCDET_CONFIG_4 0xC9 +#define DA7219_ACCDET_CONFIG_5 0xCA +#define DA7219_ACCDET_CONFIG_6 0xCB +#define DA7219_ACCDET_CONFIG_7 0xCC +#define DA7219_ACCDET_CONFIG_8 0xCD + + +/* + * Bit Fields + */ + +/* DA7219_ACCDET_STATUS_A = 0xC0 */ +#define DA7219_JACK_INSERTION_STS_SHIFT 0 +#define DA7219_JACK_INSERTION_STS_MASK (0x1 << 0) +#define DA7219_JACK_TYPE_STS_SHIFT 1 +#define DA7219_JACK_TYPE_STS_MASK (0x1 << 1) +#define DA7219_JACK_PIN_ORDER_STS_SHIFT 2 +#define DA7219_JACK_PIN_ORDER_STS_MASK (0x1 << 2) +#define DA7219_MICBIAS_UP_STS_SHIFT 3 +#define DA7219_MICBIAS_UP_STS_MASK (0x1 << 3) + +/* DA7219_ACCDET_STATUS_B = 0xC1 */ +#define DA7219_BUTTON_TYPE_STS_SHIFT 0 +#define DA7219_BUTTON_TYPE_STS_MASK (0xFF << 0) + +/* DA7219_ACCDET_IRQ_EVENT_A = 0xC2 */ +#define DA7219_E_JACK_INSERTED_SHIFT 0 +#define DA7219_E_JACK_INSERTED_MASK (0x1 << 0) +#define DA7219_E_JACK_REMOVED_SHIFT 1 +#define DA7219_E_JACK_REMOVED_MASK (0x1 << 1) +#define DA7219_E_JACK_DETECT_COMPLETE_SHIFT 2 +#define DA7219_E_JACK_DETECT_COMPLETE_MASK (0x1 << 2) + +/* DA7219_ACCDET_IRQ_EVENT_B = 0xC3 */ +#define DA7219_E_BUTTON_A_PRESSED_SHIFT 0 +#define DA7219_E_BUTTON_A_PRESSED_MASK (0x1 << 0) +#define DA7219_E_BUTTON_B_PRESSED_SHIFT 1 +#define DA7219_E_BUTTON_B_PRESSED_MASK (0x1 << 1) +#define DA7219_E_BUTTON_C_PRESSED_SHIFT 2 +#define DA7219_E_BUTTON_C_PRESSED_MASK (0x1 << 2) +#define DA7219_E_BUTTON_D_PRESSED_SHIFT 3 +#define DA7219_E_BUTTON_D_PRESSED_MASK (0x1 << 3) +#define DA7219_E_BUTTON_D_RELEASED_SHIFT 4 +#define DA7219_E_BUTTON_D_RELEASED_MASK (0x1 << 4) +#define DA7219_E_BUTTON_C_RELEASED_SHIFT 5 +#define DA7219_E_BUTTON_C_RELEASED_MASK (0x1 << 5) +#define DA7219_E_BUTTON_B_RELEASED_SHIFT 6 +#define DA7219_E_BUTTON_B_RELEASED_MASK (0x1 << 6) +#define DA7219_E_BUTTON_A_RELEASED_SHIFT 7 +#define DA7219_E_BUTTON_A_RELEASED_MASK (0x1 << 7) + +/* DA7219_ACCDET_IRQ_MASK_A = 0xC4 */ +#define DA7219_M_JACK_INSERTED_SHIFT 0 +#define DA7219_M_JACK_INSERTED_MASK (0x1 << 0) +#define DA7219_M_JACK_REMOVED_SHIFT 1 +#define DA7219_M_JACK_REMOVED_MASK (0x1 << 1) +#define DA7219_M_JACK_DETECT_COMPLETE_SHIFT 2 +#define DA7219_M_JACK_DETECT_COMPLETE_MASK (0x1 << 2) + +/* DA7219_ACCDET_IRQ_MASK_B = 0xC5 */ +#define DA7219_M_BUTTON_A_PRESSED_SHIFT 0 +#define DA7219_M_BUTTON_A_PRESSED_MASK (0x1 << 0) +#define DA7219_M_BUTTON_B_PRESSED_SHIFT 1 +#define DA7219_M_BUTTON_B_PRESSED_MASK (0x1 << 1) +#define DA7219_M_BUTTON_C_PRESSED_SHIFT 2 +#define DA7219_M_BUTTON_C_PRESSED_MASK (0x1 << 2) +#define DA7219_M_BUTTON_D_PRESSED_SHIFT 3 +#define DA7219_M_BUTTON_D_PRESSED_MASK (0x1 << 3) +#define DA7219_M_BUTTON_D_RELEASED_SHIFT 4 +#define DA7219_M_BUTTON_D_RELEASED_MASK (0x1 << 4) +#define DA7219_M_BUTTON_C_RELEASED_SHIFT 5 +#define DA7219_M_BUTTON_C_RELEASED_MASK (0x1 << 5) +#define DA7219_M_BUTTON_B_RELEASED_SHIFT 6 +#define DA7219_M_BUTTON_B_RELEASED_MASK (0x1 << 6) +#define DA7219_M_BUTTON_A_RELEASED_SHIFT 7 +#define DA7219_M_BUTTON_A_RELEASED_MASK (0x1 << 7) + +/* DA7219_ACCDET_CONFIG_1 = 0xC6 */ +#define DA7219_ACCDET_EN_SHIFT 0 +#define DA7219_ACCDET_EN_MASK (0x1 << 0) +#define DA7219_BUTTON_CONFIG_SHIFT 1 +#define DA7219_BUTTON_CONFIG_MASK (0x7 << 1) +#define DA7219_MIC_DET_THRESH_SHIFT 4 +#define DA7219_MIC_DET_THRESH_MASK (0x3 << 4) +#define DA7219_JACK_TYPE_DET_EN_SHIFT 6 +#define DA7219_JACK_TYPE_DET_EN_MASK (0x1 << 6) +#define DA7219_PIN_ORDER_DET_EN_SHIFT 7 +#define DA7219_PIN_ORDER_DET_EN_MASK (0x1 << 7) + +/* DA7219_ACCDET_CONFIG_2 = 0xC7 */ +#define DA7219_ACCDET_PAUSE_SHIFT 0 +#define DA7219_ACCDET_PAUSE_MASK (0x1 << 0) +#define DA7219_JACKDET_DEBOUNCE_SHIFT 1 +#define DA7219_JACKDET_DEBOUNCE_MASK (0x7 << 1) +#define DA7219_JACK_DETECT_RATE_SHIFT 4 +#define DA7219_JACK_DETECT_RATE_MASK (0x3 << 4) +#define DA7219_JACKDET_REM_DEB_SHIFT 6 +#define DA7219_JACKDET_REM_DEB_MASK (0x3 << 6) + +/* DA7219_ACCDET_CONFIG_3 = 0xC8 */ +#define DA7219_A_D_BUTTON_THRESH_SHIFT 0 +#define DA7219_A_D_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_4 = 0xC9 */ +#define DA7219_D_B_BUTTON_THRESH_SHIFT 0 +#define DA7219_D_B_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_5 = 0xCA */ +#define DA7219_B_C_BUTTON_THRESH_SHIFT 0 +#define DA7219_B_C_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_6 = 0xCB */ +#define DA7219_C_MIC_BUTTON_THRESH_SHIFT 0 +#define DA7219_C_MIC_BUTTON_THRESH_MASK (0xFF << 0) + +/* DA7219_ACCDET_CONFIG_7 = 0xCC */ +#define DA7219_BUTTON_AVERAGE_SHIFT 0 +#define DA7219_BUTTON_AVERAGE_MASK (0x3 << 0) +#define DA7219_ADC_1_BIT_REPEAT_SHIFT 2 +#define DA7219_ADC_1_BIT_REPEAT_MASK (0x3 << 2) +#define DA7219_PIN_ORDER_FORCE_SHIFT 4 +#define DA7219_PIN_ORDER_FORCE_MASK (0x1 << 4) +#define DA7219_JACK_TYPE_FORCE_SHIFT 5 +#define DA7219_JACK_TYPE_FORCE_MASK (0x1 << 5) + +/* DA7219_ACCDET_CONFIG_8 = 0xCD */ +#define DA7219_HPTEST_EN_SHIFT 0 +#define DA7219_HPTEST_EN_MASK (0x1 << 0) +#define DA7219_HPTEST_RES_SEL_SHIFT 1 +#define DA7219_HPTEST_RES_SEL_MASK (0x3 << 1) +#define DA7219_HPTEST_RES_SEL_1KOHMS (0x0 << 1) +#define DA7219_HPTEST_COMP_SHIFT 4 +#define DA7219_HPTEST_COMP_MASK (0x1 << 4) + + +#define DA7219_AAD_MAX_BUTTONS 4 +#define DA7219_AAD_REPORT_ALL_MASK (SND_JACK_MECHANICAL | \ + SND_JACK_HEADSET | SND_JACK_LINEOUT | \ + SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + +#define DA7219_AAD_MICBIAS_CHK_DELAY 10 +#define DA7219_AAD_MICBIAS_CHK_RETRIES 5 + +#define DA7219_AAD_HPTEST_RAMP_FREQ 0x28 +#define DA7219_AAD_HPTEST_RAMP_FREQ_INT_OSC 0x4D +#define DA7219_AAD_HPTEST_PERIOD 65 +#define DA7219_AAD_HPTEST_INT_OSC_PATH_DELAY 20 + +enum da7219_aad_event_regs { + DA7219_AAD_IRQ_REG_A = 0, + DA7219_AAD_IRQ_REG_B, + DA7219_AAD_IRQ_REG_MAX, +}; + +/* Private data */ +struct da7219_aad_priv { + struct snd_soc_component *component; + int irq; + int gnd_switch_delay; + + u8 micbias_pulse_lvl; + u32 micbias_pulse_time; + + u8 btn_cfg; + + struct work_struct btn_det_work; + struct work_struct hptest_work; + struct delayed_work jack_det_work; + struct workqueue_struct *aad_wq; + + struct snd_soc_jack *jack; + bool micbias_resume_enable; + bool jack_inserted; +}; + +/* AAD control */ +void da7219_aad_jack_det(struct snd_soc_component *component, struct snd_soc_jack *jack); + +/* Suspend/Resume */ +void da7219_aad_suspend(struct snd_soc_component *component); +void da7219_aad_resume(struct snd_soc_component *component); + +/* Init/Exit */ +int da7219_aad_init(struct snd_soc_component *component); +void da7219_aad_exit(struct snd_soc_component *component); + +/* I2C Probe */ +int da7219_aad_probe(struct i2c_client *i2c); + +#endif /* __DA7219_AAD_H */ diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c new file mode 100644 index 0000000000..600c2db587 --- /dev/null +++ b/sound/soc/codecs/da7219.c @@ -0,0 +1,2725 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * da7219.c - DA7219 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "da7219.h" +#include "da7219-aad.h" + + +/* + * TLVs and Enums + */ + +/* Input TLVs */ +static const DECLARE_TLV_DB_SCALE(da7219_mic_gain_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7219_adc_dig_gain_tlv, -8325, 75, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_alc_ana_gain_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_sidetone_gain_tlv, -4200, 300, 0); +static const DECLARE_TLV_DB_SCALE(da7219_tonegen_gain_tlv, -4500, 300, 0); + +/* Output TLVs */ +static const DECLARE_TLV_DB_SCALE(da7219_dac_eq_band_tlv, -1050, 150, 0); + +static const DECLARE_TLV_DB_RANGE(da7219_dac_dig_gain_tlv, + 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -77.25dB to 12dB */ + 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7725, 75, 0) +); + +static const DECLARE_TLV_DB_SCALE(da7219_dac_ng_threshold_tlv, -10200, 600, 0); +static const DECLARE_TLV_DB_SCALE(da7219_hp_gain_tlv, -5700, 100, 0); + +/* Input Enums */ +static const char * const da7219_alc_attack_rate_txt[] = { + "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs", + "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs", + "30024/fs" +}; + +static const struct soc_enum da7219_alc_attack_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_ATTACK_SHIFT, + DA7219_ALC_ATTACK_MAX, da7219_alc_attack_rate_txt); + +static const char * const da7219_alc_release_rate_txt[] = { + "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs", + "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs" +}; + +static const struct soc_enum da7219_alc_release_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_RELEASE_SHIFT, + DA7219_ALC_RELEASE_MAX, da7219_alc_release_rate_txt); + +static const char * const da7219_alc_hold_time_txt[] = { + "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", + "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", + "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static const struct soc_enum da7219_alc_hold_time = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_HOLD_SHIFT, + DA7219_ALC_HOLD_MAX, da7219_alc_hold_time_txt); + +static const char * const da7219_alc_env_rate_txt[] = { + "1/4", "1/16", "1/256", "1/65536" +}; + +static const struct soc_enum da7219_alc_env_attack_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_ATTACK_SHIFT, + DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt); + +static const struct soc_enum da7219_alc_env_release_rate = + SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_RELEASE_SHIFT, + DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt); + +static const char * const da7219_alc_anticlip_step_txt[] = { + "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs" +}; + +static const struct soc_enum da7219_alc_anticlip_step = + SOC_ENUM_SINGLE(DA7219_ALC_ANTICLIP_CTRL, + DA7219_ALC_ANTICLIP_STEP_SHIFT, + DA7219_ALC_ANTICLIP_STEP_MAX, + da7219_alc_anticlip_step_txt); + +/* Input/Output Enums */ +static const char * const da7219_gain_ramp_rate_txt[] = { + "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8", + "Nominal Rate / 16" +}; + +static const struct soc_enum da7219_gain_ramp_rate = + SOC_ENUM_SINGLE(DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_SHIFT, + DA7219_GAIN_RAMP_RATE_MAX, da7219_gain_ramp_rate_txt); + +static const char * const da7219_hpf_mode_txt[] = { + "Disabled", "Audio", "Voice" +}; + +static const unsigned int da7219_hpf_mode_val[] = { + DA7219_HPF_DISABLED, DA7219_HPF_AUDIO_EN, DA7219_HPF_VOICE_EN, +}; + +static const struct soc_enum da7219_adc_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_ADC_FILTERS1, DA7219_HPF_MODE_SHIFT, + DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX, + da7219_hpf_mode_txt, da7219_hpf_mode_val); + +static const struct soc_enum da7219_dac_hpf_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_DAC_FILTERS1, DA7219_HPF_MODE_SHIFT, + DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX, + da7219_hpf_mode_txt, da7219_hpf_mode_val); + +static const char * const da7219_audio_hpf_corner_txt[] = { + "2Hz", "4Hz", "8Hz", "16Hz" +}; + +static const struct soc_enum da7219_adc_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1, + DA7219_ADC_AUDIO_HPF_CORNER_SHIFT, + DA7219_AUDIO_HPF_CORNER_MAX, + da7219_audio_hpf_corner_txt); + +static const struct soc_enum da7219_dac_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1, + DA7219_DAC_AUDIO_HPF_CORNER_SHIFT, + DA7219_AUDIO_HPF_CORNER_MAX, + da7219_audio_hpf_corner_txt); + +static const char * const da7219_voice_hpf_corner_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static const struct soc_enum da7219_adc_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1, + DA7219_ADC_VOICE_HPF_CORNER_SHIFT, + DA7219_VOICE_HPF_CORNER_MAX, + da7219_voice_hpf_corner_txt); + +static const struct soc_enum da7219_dac_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1, + DA7219_DAC_VOICE_HPF_CORNER_SHIFT, + DA7219_VOICE_HPF_CORNER_MAX, + da7219_voice_hpf_corner_txt); + +static const char * const da7219_tonegen_dtmf_key_txt[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", + "*", "#" +}; + +static const struct soc_enum da7219_tonegen_dtmf_key = + SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG1, DA7219_DTMF_REG_SHIFT, + DA7219_DTMF_REG_MAX, da7219_tonegen_dtmf_key_txt); + +static const char * const da7219_tonegen_swg_sel_txt[] = { + "Sum", "SWG1", "SWG2", "SWG1_1-Cos" +}; + +static const struct soc_enum da7219_tonegen_swg_sel = + SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG2, DA7219_SWG_SEL_SHIFT, + DA7219_SWG_SEL_MAX, da7219_tonegen_swg_sel_txt); + +/* Output Enums */ +static const char * const da7219_dac_softmute_rate_txt[] = { + "1 Sample", "2 Samples", "4 Samples", "8 Samples", "16 Samples", + "32 Samples", "64 Samples" +}; + +static const struct soc_enum da7219_dac_softmute_rate = + SOC_ENUM_SINGLE(DA7219_DAC_FILTERS5, DA7219_DAC_SOFTMUTE_RATE_SHIFT, + DA7219_DAC_SOFTMUTE_RATE_MAX, + da7219_dac_softmute_rate_txt); + +static const char * const da7219_dac_ng_setup_time_txt[] = { + "256 Samples", "512 Samples", "1024 Samples", "2048 Samples" +}; + +static const struct soc_enum da7219_dac_ng_setup_time = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_SETUP_TIME_SHIFT, + DA7219_DAC_NG_SETUP_TIME_MAX, + da7219_dac_ng_setup_time_txt); + +static const char * const da7219_dac_ng_rampup_txt[] = { + "0.22ms/dB", "0.0138ms/dB" +}; + +static const struct soc_enum da7219_dac_ng_rampup_rate = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_RAMPUP_RATE_SHIFT, + DA7219_DAC_NG_RAMP_RATE_MAX, + da7219_dac_ng_rampup_txt); + +static const char * const da7219_dac_ng_rampdown_txt[] = { + "0.88ms/dB", "14.08ms/dB" +}; + +static const struct soc_enum da7219_dac_ng_rampdown_rate = + SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME, + DA7219_DAC_NG_RAMPDN_RATE_SHIFT, + DA7219_DAC_NG_RAMP_RATE_MAX, + da7219_dac_ng_rampdown_txt); + + +static const char * const da7219_cp_track_mode_txt[] = { + "Largest Volume", "DAC Volume", "Signal Magnitude" +}; + +static const unsigned int da7219_cp_track_mode_val[] = { + DA7219_CP_MCHANGE_LARGEST_VOL, DA7219_CP_MCHANGE_DAC_VOL, + DA7219_CP_MCHANGE_SIG_MAG +}; + +static const struct soc_enum da7219_cp_track_mode = + SOC_VALUE_ENUM_SINGLE(DA7219_CP_CTRL, DA7219_CP_MCHANGE_SHIFT, + DA7219_CP_MCHANGE_REL_MASK, DA7219_CP_MCHANGE_MAX, + da7219_cp_track_mode_txt, + da7219_cp_track_mode_val); + + +/* + * Control Functions + */ + +/* Locked Kcontrol calls */ +static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + int ret; + + mutex_lock(&da7219->ctrl_lock); + ret = snd_soc_get_volsw(kcontrol, ucontrol); + mutex_unlock(&da7219->ctrl_lock); + + return ret; +} + +static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + int ret; + + mutex_lock(&da7219->ctrl_lock); + ret = snd_soc_put_volsw(kcontrol, ucontrol); + mutex_unlock(&da7219->ctrl_lock); + + return ret; +} + +static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + int ret; + + mutex_lock(&da7219->ctrl_lock); + ret = snd_soc_get_enum_double(kcontrol, ucontrol); + mutex_unlock(&da7219->ctrl_lock); + + return ret; +} + +static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + int ret; + + mutex_lock(&da7219->ctrl_lock); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + mutex_unlock(&da7219->ctrl_lock); + + return ret; +} + +/* ALC */ +static void da7219_alc_calib(struct snd_soc_component *component) +{ + u8 mic_ctrl, mixin_ctrl, adc_ctrl, calib_ctrl; + + /* Save current state of mic control register */ + mic_ctrl = snd_soc_component_read(component, DA7219_MIC_1_CTRL); + + /* Save current state of input mixer control register */ + mixin_ctrl = snd_soc_component_read(component, DA7219_MIXIN_L_CTRL); + + /* Save current state of input ADC control register */ + adc_ctrl = snd_soc_component_read(component, DA7219_ADC_L_CTRL); + + /* Enable then Mute MIC PGAs */ + snd_soc_component_update_bits(component, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK, + DA7219_MIC_1_AMP_EN_MASK); + snd_soc_component_update_bits(component, DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_MUTE_EN_MASK, + DA7219_MIC_1_AMP_MUTE_EN_MASK); + + /* Enable input mixers unmuted */ + snd_soc_component_update_bits(component, DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_EN_MASK | + DA7219_MIXIN_L_AMP_MUTE_EN_MASK, + DA7219_MIXIN_L_AMP_EN_MASK); + + /* Enable input filters unmuted */ + snd_soc_component_update_bits(component, DA7219_ADC_L_CTRL, + DA7219_ADC_L_MUTE_EN_MASK | DA7219_ADC_L_EN_MASK, + DA7219_ADC_L_EN_MASK); + + /* Perform auto calibration */ + snd_soc_component_update_bits(component, DA7219_ALC_CTRL1, + DA7219_ALC_AUTO_CALIB_EN_MASK, + DA7219_ALC_AUTO_CALIB_EN_MASK); + do { + calib_ctrl = snd_soc_component_read(component, DA7219_ALC_CTRL1); + } while (calib_ctrl & DA7219_ALC_AUTO_CALIB_EN_MASK); + + /* If auto calibration fails, disable DC offset, hybrid ALC */ + if (calib_ctrl & DA7219_ALC_CALIB_OVERFLOW_MASK) { + dev_warn(component->dev, + "ALC auto calibration failed with overflow\n"); + snd_soc_component_update_bits(component, DA7219_ALC_CTRL1, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK, 0); + } else { + /* Enable DC offset cancellation, hybrid mode */ + snd_soc_component_update_bits(component, DA7219_ALC_CTRL1, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK, + DA7219_ALC_OFFSET_EN_MASK | + DA7219_ALC_SYNC_MODE_MASK); + } + + /* Restore input filter control register to original state */ + snd_soc_component_write(component, DA7219_ADC_L_CTRL, adc_ctrl); + + /* Restore input mixer control registers to original state */ + snd_soc_component_write(component, DA7219_MIXIN_L_CTRL, mixin_ctrl); + + /* Restore MIC control registers to original states */ + snd_soc_component_write(component, DA7219_MIC_1_CTRL, mic_ctrl); +} + +static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + /* + * If ALC in operation and value of control has been updated, + * make sure calibrated offsets are updated. + */ + if ((ret == 1) && (da7219->alc_en)) + da7219_alc_calib(component); + + return ret; +} + +static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + + /* Force ALC offset calibration if enabling ALC */ + if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) { + da7219_alc_calib(component); + da7219->alc_en = true; + } else { + da7219->alc_en = false; + } + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +/* ToneGen */ +static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + __le16 val; + int ret; + + mutex_lock(&da7219->ctrl_lock); + ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val)); + mutex_unlock(&da7219->ctrl_lock); + + if (ret) + return ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to host endianness here. + */ + ucontrol->value.integer.value[0] = le16_to_cpu(val); + + return 0; +} + +static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mixer_ctrl = + (struct soc_mixer_control *) kcontrol->private_value; + unsigned int reg = mixer_ctrl->reg; + __le16 val_new, val_old; + int ret; + + /* + * Frequency value spans two 8-bit registers, lower then upper byte. + * Therefore we need to convert to little endian here to align with + * HW registers. + */ + val_new = cpu_to_le16(ucontrol->value.integer.value[0]); + + mutex_lock(&da7219->ctrl_lock); + ret = regmap_raw_read(da7219->regmap, reg, &val_old, sizeof(val_old)); + if (ret == 0 && (val_old != val_new)) + ret = regmap_raw_write(da7219->regmap, reg, + &val_new, sizeof(val_new)); + mutex_unlock(&da7219->ctrl_lock); + + if (ret < 0) + return ret; + + return val_old != val_new; +} + + +/* + * KControls + */ + +static const struct snd_kcontrol_new da7219_snd_controls[] = { + /* Mics */ + SOC_SINGLE_TLV("Mic Volume", DA7219_MIC_1_GAIN, + DA7219_MIC_1_AMP_GAIN_SHIFT, DA7219_MIC_1_AMP_GAIN_MAX, + DA7219_NO_INVERT, da7219_mic_gain_tlv), + SOC_SINGLE("Mic Switch", DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + + /* Mixer Input */ + SOC_SINGLE_EXT_TLV("Mixin Volume", DA7219_MIXIN_L_GAIN, + DA7219_MIXIN_L_AMP_GAIN_SHIFT, + DA7219_MIXIN_L_AMP_GAIN_MAX, DA7219_NO_INVERT, + snd_soc_get_volsw, da7219_mixin_gain_put, + da7219_mixin_gain_tlv), + SOC_SINGLE("Mixin Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + SOC_SINGLE("Mixin Gain Ramp Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + SOC_SINGLE("Mixin ZC Gain Switch", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_ZC_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + + /* ADC */ + SOC_SINGLE_TLV("Capture Digital Volume", DA7219_ADC_L_GAIN, + DA7219_ADC_L_DIGITAL_GAIN_SHIFT, + DA7219_ADC_L_DIGITAL_GAIN_MAX, DA7219_NO_INVERT, + da7219_adc_dig_gain_tlv), + SOC_SINGLE("Capture Digital Switch", DA7219_ADC_L_CTRL, + DA7219_ADC_L_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + SOC_SINGLE("Capture Digital Gain Ramp Switch", DA7219_ADC_L_CTRL, + DA7219_ADC_L_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + + /* ALC */ + SOC_ENUM("ALC Attack Rate", da7219_alc_attack_rate), + SOC_ENUM("ALC Release Rate", da7219_alc_release_rate), + SOC_ENUM("ALC Hold Time", da7219_alc_hold_time), + SOC_ENUM("ALC Envelope Attack Rate", da7219_alc_env_attack_rate), + SOC_ENUM("ALC Envelope Release Rate", da7219_alc_env_release_rate), + SOC_SINGLE_TLV("ALC Noise Threshold", DA7219_ALC_NOISE, + DA7219_ALC_NOISE_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Min Threshold", DA7219_ALC_TARGET_MIN, + DA7219_ALC_THRESHOLD_MIN_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Threshold", DA7219_ALC_TARGET_MAX, + DA7219_ALC_THRESHOLD_MAX_SHIFT, DA7219_ALC_THRESHOLD_MAX, + DA7219_INVERT, da7219_alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Attenuation", DA7219_ALC_GAIN_LIMITS, + DA7219_ALC_ATTEN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_gain_tlv), + SOC_SINGLE_TLV("ALC Max Volume", DA7219_ALC_GAIN_LIMITS, + DA7219_ALC_GAIN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Min Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS, + DA7219_ALC_ANA_GAIN_MIN_SHIFT, + DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_ana_gain_tlv), + SOC_SINGLE_RANGE_TLV("ALC Max Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS, + DA7219_ALC_ANA_GAIN_MAX_SHIFT, + DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX, + DA7219_NO_INVERT, da7219_alc_ana_gain_tlv), + SOC_ENUM("ALC Anticlip Step", da7219_alc_anticlip_step), + SOC_SINGLE("ALC Anticlip Switch", DA7219_ALC_ANTICLIP_CTRL, + DA7219_ALC_ANTIPCLIP_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT), + SOC_SINGLE_EXT("ALC Switch", DA7219_ALC_CTRL1, DA7219_ALC_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT, + snd_soc_get_volsw, da7219_alc_sw_put), + + /* Input High-Pass Filters */ + SOC_ENUM("ADC HPF Mode", da7219_adc_hpf_mode), + SOC_ENUM("ADC HPF Corner Audio", da7219_adc_audio_hpf_corner), + SOC_ENUM("ADC HPF Corner Voice", da7219_adc_voice_hpf_corner), + + /* Sidetone Filter */ + SOC_SINGLE_TLV("Sidetone Volume", DA7219_SIDETONE_GAIN, + DA7219_SIDETONE_GAIN_SHIFT, DA7219_SIDETONE_GAIN_MAX, + DA7219_NO_INVERT, da7219_sidetone_gain_tlv), + SOC_SINGLE("Sidetone Switch", DA7219_SIDETONE_CTRL, + DA7219_SIDETONE_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT), + + /* Tone Generator */ + SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7219_TONE_GEN_CFG2, + DA7219_TONE_GEN_GAIN_SHIFT, DA7219_TONE_GEN_GAIN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put, da7219_tonegen_gain_tlv), + SOC_ENUM_EXT("ToneGen DTMF Key", da7219_tonegen_dtmf_key, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7219_TONE_GEN_CFG1, + DA7219_DTMF_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7219_tonegen_swg_sel, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7219_TONE_GEN_FREQ1_L, + DA7219_FREQ1_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT, + da7219_tonegen_freq_get, da7219_tonegen_freq_put), + SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7219_TONE_GEN_FREQ2_L, + DA7219_FREQ2_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT, + da7219_tonegen_freq_get, da7219_tonegen_freq_put), + SOC_SINGLE_EXT("ToneGen On Time", DA7219_TONE_GEN_ON_PER, + DA7219_BEEP_ON_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_SINGLE("ToneGen Off Time", DA7219_TONE_GEN_OFF_PER, + DA7219_BEEP_OFF_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX, + DA7219_NO_INVERT), + + /* Gain ramping */ + SOC_ENUM("Gain Ramp Rate", da7219_gain_ramp_rate), + + /* DAC High-Pass Filter */ + SOC_ENUM_EXT("DAC HPF Mode", da7219_dac_hpf_mode, + da7219_enum_locked_get, da7219_enum_locked_put), + SOC_ENUM("DAC HPF Corner Audio", da7219_dac_audio_hpf_corner), + SOC_ENUM("DAC HPF Corner Voice", da7219_dac_voice_hpf_corner), + + /* DAC 5-Band Equaliser */ + SOC_SINGLE_TLV("DAC EQ Band1 Volume", DA7219_DAC_FILTERS2, + DA7219_DAC_EQ_BAND1_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band2 Volume", DA7219_DAC_FILTERS2, + DA7219_DAC_EQ_BAND2_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band3 Volume", DA7219_DAC_FILTERS3, + DA7219_DAC_EQ_BAND3_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band4 Volume", DA7219_DAC_FILTERS3, + DA7219_DAC_EQ_BAND4_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_TLV("DAC EQ Band5 Volume", DA7219_DAC_FILTERS4, + DA7219_DAC_EQ_BAND5_SHIFT, DA7219_DAC_EQ_BAND_MAX, + DA7219_NO_INVERT, da7219_dac_eq_band_tlv), + SOC_SINGLE_EXT("DAC EQ Switch", DA7219_DAC_FILTERS4, + DA7219_DAC_EQ_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + + /* DAC Softmute */ + SOC_ENUM("DAC Soft Mute Rate", da7219_dac_softmute_rate), + SOC_SINGLE_EXT("DAC Soft Mute Switch", DA7219_DAC_FILTERS5, + DA7219_DAC_SOFTMUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_NO_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + + /* DAC Noise Gate */ + SOC_ENUM("DAC NG Setup Time", da7219_dac_ng_setup_time), + SOC_ENUM("DAC NG Rampup Rate", da7219_dac_ng_rampup_rate), + SOC_ENUM("DAC NG Rampdown Rate", da7219_dac_ng_rampdown_rate), + SOC_SINGLE_TLV("DAC NG Off Threshold", DA7219_DAC_NG_OFF_THRESH, + DA7219_DAC_NG_OFF_THRESHOLD_SHIFT, + DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT, + da7219_dac_ng_threshold_tlv), + SOC_SINGLE_TLV("DAC NG On Threshold", DA7219_DAC_NG_ON_THRESH, + DA7219_DAC_NG_ON_THRESHOLD_SHIFT, + DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT, + da7219_dac_ng_threshold_tlv), + SOC_SINGLE("DAC NG Switch", DA7219_DAC_NG_CTRL, DA7219_DAC_NG_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + + /* DACs */ + SOC_DOUBLE_R_EXT_TLV("Playback Digital Volume", DA7219_DAC_L_GAIN, + DA7219_DAC_R_GAIN, DA7219_DAC_L_DIGITAL_GAIN_SHIFT, + DA7219_DAC_DIGITAL_GAIN_MAX, DA7219_NO_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put, + da7219_dac_dig_gain_tlv), + SOC_DOUBLE_R_EXT("Playback Digital Switch", DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL, DA7219_DAC_L_MUTE_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put), + SOC_DOUBLE_R("Playback Digital Gain Ramp Switch", DA7219_DAC_L_CTRL, + DA7219_DAC_R_CTRL, DA7219_DAC_L_RAMP_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + + /* CP */ + SOC_ENUM("Charge Pump Track Mode", da7219_cp_track_mode), + SOC_SINGLE("Charge Pump Threshold", DA7219_CP_VOL_THRESHOLD1, + DA7219_CP_THRESH_VDD2_SHIFT, DA7219_CP_THRESH_VDD2_MAX, + DA7219_NO_INVERT), + + /* Headphones */ + SOC_DOUBLE_R_EXT_TLV("Headphone Volume", DA7219_HP_L_GAIN, + DA7219_HP_R_GAIN, DA7219_HP_L_AMP_GAIN_SHIFT, + DA7219_HP_AMP_GAIN_MAX, DA7219_NO_INVERT, + da7219_volsw_locked_get, da7219_volsw_locked_put, + da7219_hp_gain_tlv), + SOC_DOUBLE_R_EXT("Headphone Switch", DA7219_HP_L_CTRL, DA7219_HP_R_CTRL, + DA7219_HP_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX, + DA7219_INVERT, da7219_volsw_locked_get, + da7219_volsw_locked_put), + SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL, DA7219_HP_L_AMP_RAMP_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), + SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7219_HP_L_CTRL, + DA7219_HP_R_CTRL, DA7219_HP_L_AMP_ZC_EN_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + + +/* + * DAPM Mux Controls + */ + +static const char * const da7219_out_sel_txt[] = { + "ADC", "Tone Generator", "DAIL", "DAIR" +}; + +static const struct soc_enum da7219_out_dail_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI, + DA7219_DAI_L_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dail_sel_mux = + SOC_DAPM_ENUM("Out DAIL Mux", da7219_out_dail_sel); + +static const struct soc_enum da7219_out_dair_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI, + DA7219_DAI_R_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dair_sel_mux = + SOC_DAPM_ENUM("Out DAIR Mux", da7219_out_dair_sel); + +static const struct soc_enum da7219_out_dacl_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC, + DA7219_DAC_L_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dacl_sel_mux = + SOC_DAPM_ENUM("Out DACL Mux", da7219_out_dacl_sel); + +static const struct soc_enum da7219_out_dacr_sel = + SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC, + DA7219_DAC_R_SRC_SHIFT, + DA7219_OUT_SRC_MAX, + da7219_out_sel_txt); + +static const struct snd_kcontrol_new da7219_out_dacr_sel_mux = + SOC_DAPM_ENUM("Out DACR Mux", da7219_out_dacr_sel); + + +/* + * DAPM Mixer Controls + */ + +static const struct snd_kcontrol_new da7219_mixin_controls[] = { + SOC_DAPM_SINGLE("Mic Switch", DA7219_MIXIN_L_SELECT, + DA7219_MIXIN_L_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +static const struct snd_kcontrol_new da7219_mixout_l_controls[] = { + SOC_DAPM_SINGLE("DACL Switch", DA7219_MIXOUT_L_SELECT, + DA7219_MIXOUT_L_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +static const struct snd_kcontrol_new da7219_mixout_r_controls[] = { + SOC_DAPM_SINGLE("DACR Switch", DA7219_MIXOUT_R_SELECT, + DA7219_MIXOUT_R_MIX_SELECT_SHIFT, + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), +}; + +#define DA7219_DMIX_ST_CTRLS(reg) \ + SOC_DAPM_SINGLE("Out FilterL Switch", reg, \ + DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \ + SOC_DAPM_SINGLE("Out FilterR Switch", reg, \ + DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT), \ + SOC_DAPM_SINGLE("Sidetone Switch", reg, \ + DA7219_DMIX_ST_SRC_SIDETONE_SHIFT, \ + DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT) \ + +static const struct snd_kcontrol_new da7219_st_out_filtl_mix_controls[] = { + DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1L), +}; + +static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = { + DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1R), +}; + + +/* + * DAPM Events + */ + +static int da7219_mic_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (da7219->micbias_on_event) { + /* + * Delay only for first capture after bias enabled to + * avoid possible DC offset related noise. + */ + da7219->micbias_on_event = false; + msleep(da7219->mic_pga_delay); + } + break; + default: + break; + } + + return 0; +} + +static int da7219_dai_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX]; + u8 pll_ctrl, pll_status; + int i = 0, ret; + bool srm_lock = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (da7219->master) { + /* Enable DAI clks for master mode */ + if (bclk) { + ret = clk_prepare_enable(bclk); + if (ret) { + dev_err(component->dev, + "Failed to enable DAI clks\n"); + return ret; + } + } else { + snd_soc_component_update_bits(component, + DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_EN_MASK, + DA7219_DAI_CLK_EN_MASK); + } + } + + /* PC synchronised to DAI */ + snd_soc_component_update_bits(component, DA7219_PC_COUNT, + DA7219_PC_FREERUN_MASK, 0); + + /* Slave mode, if SRM not enabled no need for status checks */ + pll_ctrl = snd_soc_component_read(component, DA7219_PLL_CTRL); + if ((pll_ctrl & DA7219_PLL_MODE_MASK) != DA7219_PLL_MODE_SRM) + return 0; + + /* Check SRM has locked */ + do { + pll_status = snd_soc_component_read(component, DA7219_PLL_SRM_STS); + if (pll_status & DA7219_PLL_SRM_STS_SRM_LOCK) { + srm_lock = true; + } else { + ++i; + msleep(50); + } + } while ((i < DA7219_SRM_CHECK_RETRIES) && (!srm_lock)); + + if (!srm_lock) + dev_warn(component->dev, "SRM failed to lock\n"); + + return 0; + case SND_SOC_DAPM_POST_PMD: + /* PC free-running */ + snd_soc_component_update_bits(component, DA7219_PC_COUNT, + DA7219_PC_FREERUN_MASK, + DA7219_PC_FREERUN_MASK); + + /* Disable DAI clks if in master mode */ + if (da7219->master) { + if (bclk) + clk_disable_unprepare(bclk); + else + snd_soc_component_update_bits(component, + DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_EN_MASK, + 0); + } + + return 0; + default: + return -EINVAL; + } +} + +static int da7219_settling_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + msleep(DA7219_SETTLING_DELAY); + break; + default: + break; + } + + return 0; +} + +static int da7219_mixout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u8 hp_ctrl, min_gain_mask; + + switch (w->reg) { + case DA7219_MIXOUT_L_CTRL: + hp_ctrl = DA7219_HP_L_CTRL; + min_gain_mask = DA7219_HP_L_AMP_MIN_GAIN_EN_MASK; + break; + case DA7219_MIXOUT_R_CTRL: + hp_ctrl = DA7219_HP_R_CTRL; + min_gain_mask = DA7219_HP_R_AMP_MIN_GAIN_EN_MASK; + break; + default: + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMD: + /* Enable minimum gain on HP to avoid pops */ + snd_soc_component_update_bits(component, hp_ctrl, min_gain_mask, + min_gain_mask); + + msleep(DA7219_MIN_GAIN_DELAY); + + break; + case SND_SOC_DAPM_POST_PMU: + /* Remove minimum gain on HP */ + snd_soc_component_update_bits(component, hp_ctrl, min_gain_mask, 0); + + break; + } + + return 0; +} + +static int da7219_gain_ramp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_PRE_PMD: + /* Ensure nominal gain ramping for DAPM sequence */ + da7219->gain_ramp_ctrl = + snd_soc_component_read(component, DA7219_GAIN_RAMP_CTRL); + snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, + DA7219_GAIN_RAMP_RATE_NOMINAL); + break; + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + /* Restore previous gain ramp settings */ + snd_soc_component_write(component, DA7219_GAIN_RAMP_CTRL, + da7219->gain_ramp_ctrl); + break; + } + + return 0; +} + + +/* + * DAPM Widgets + */ + +static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = { + /* Input Supplies */ + SND_SOC_DAPM_SUPPLY("Mic Bias", DA7219_MICBIAS_CTRL, + DA7219_MICBIAS1_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("MIC"), + + /* Input PGAs */ + SND_SOC_DAPM_PGA_E("Mic PGA", DA7219_MIC_1_CTRL, + DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0, da7219_mic_pga_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("Mixin PGA", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0, da7219_settling_event, SND_SOC_DAPM_POST_PMU), + + /* Input Filters */ + SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT, + DA7219_NO_INVERT), + + /* Tone Generator */ + SND_SOC_DAPM_SIGGEN("TONE"), + SND_SOC_DAPM_PGA("Tone Generator", DA7219_TONE_GEN_CFG1, + DA7219_START_STOPN_SHIFT, DA7219_NO_INVERT, NULL, 0), + + /* Sidetone Input */ + SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7219_SIDETONE_CTRL, + DA7219_SIDETONE_EN_SHIFT, DA7219_NO_INVERT), + + /* Input Mixer Supply */ + SND_SOC_DAPM_SUPPLY("Mixer In Supply", DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_MIX_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0), + + /* Input Mixer */ + SND_SOC_DAPM_MIXER("Mixer In", SND_SOC_NOPM, 0, 0, + da7219_mixin_controls, + ARRAY_SIZE(da7219_mixin_controls)), + + /* Input Muxes */ + SND_SOC_DAPM_MUX("Out DAIL Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dail_sel_mux), + SND_SOC_DAPM_MUX("Out DAIR Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dair_sel_mux), + + /* DAI Supply */ + SND_SOC_DAPM_SUPPLY("DAI", DA7219_DAI_CTRL, DA7219_DAI_EN_SHIFT, + DA7219_NO_INVERT, da7219_dai_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* DAI */ + SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7219_DAI_TDM_CTRL, + DA7219_DAI_OE_SHIFT, DA7219_NO_INVERT), + SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Muxes */ + SND_SOC_DAPM_MUX("Out DACL Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dacl_sel_mux), + SND_SOC_DAPM_MUX("Out DACR Mux", SND_SOC_NOPM, 0, 0, + &da7219_out_dacr_sel_mux), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7219_mixout_l_controls, + ARRAY_SIZE(da7219_mixout_l_controls)), + SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0, + da7219_mixout_r_controls, + ARRAY_SIZE(da7219_mixout_r_controls)), + + /* Sidetone Mixers */ + SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0, + da7219_st_out_filtl_mix_controls, + ARRAY_SIZE(da7219_st_out_filtl_mix_controls)), + SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0, + 0, da7219_st_out_filtr_mix_controls, + ARRAY_SIZE(da7219_st_out_filtr_mix_controls)), + + /* DACs */ + SND_SOC_DAPM_DAC_E("DACL", NULL, DA7219_DAC_L_CTRL, + DA7219_DAC_L_EN_SHIFT, DA7219_NO_INVERT, + da7219_settling_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DACR", NULL, DA7219_DAC_R_CTRL, + DA7219_DAC_R_EN_SHIFT, DA7219_NO_INVERT, + da7219_settling_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Output PGAs */ + SND_SOC_DAPM_PGA_E("Mixout Left PGA", DA7219_MIXOUT_L_CTRL, + DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0, da7219_mixout_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("Mixout Right PGA", DA7219_MIXOUT_R_CTRL, + DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT, + NULL, 0, da7219_mixout_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY_S("Headphone Left PGA", 1, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, + da7219_settling_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("Headphone Right PGA", 1, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, + da7219_settling_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Output Supplies */ + SND_SOC_DAPM_SUPPLY_S("Charge Pump", 0, DA7219_CP_CTRL, + DA7219_CP_EN_SHIFT, DA7219_NO_INVERT, + da7219_settling_event, + SND_SOC_DAPM_POST_PMU), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + + /* Pre/Post Power */ + SND_SOC_DAPM_PRE("Pre Power Gain Ramp", da7219_gain_ramp_event), + SND_SOC_DAPM_POST("Post Power Gain Ramp", da7219_gain_ramp_event), +}; + + +/* + * DAPM Mux Routes + */ + +#define DA7219_OUT_DAI_MUX_ROUTES(name) \ + {name, "ADC", "Mixer In"}, \ + {name, "Tone Generator", "Tone Generator"}, \ + {name, "DAIL", "DAIOUT"}, \ + {name, "DAIR", "DAIOUT"} + +#define DA7219_OUT_DAC_MUX_ROUTES(name) \ + {name, "ADC", "Mixer In"}, \ + {name, "Tone Generator", "Tone Generator"}, \ + {name, "DAIL", "DAIIN"}, \ + {name, "DAIR", "DAIIN"} + +/* + * DAPM Mixer Routes + */ + +#define DA7219_DMIX_ST_ROUTES(name) \ + {name, "Out FilterL Switch", "Mixer Out FilterL"}, \ + {name, "Out FilterR Switch", "Mixer Out FilterR"}, \ + {name, "Sidetone Switch", "Sidetone Filter"} + + +/* + * DAPM audio route definition + */ + +static const struct snd_soc_dapm_route da7219_audio_map[] = { + /* Input paths */ + {"MIC", NULL, "Mic Bias"}, + {"Mic PGA", NULL, "MIC"}, + {"Mixin PGA", NULL, "Mic PGA"}, + {"ADC", NULL, "Mixin PGA"}, + + {"Mixer In", NULL, "Mixer In Supply"}, + {"Mixer In", "Mic Switch", "ADC"}, + + {"Sidetone Filter", NULL, "Mixer In"}, + + {"Tone Generator", NULL, "TONE"}, + + DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"), + DA7219_OUT_DAI_MUX_ROUTES("Out DAIR Mux"), + + {"DAIOUT", NULL, "Out DAIL Mux"}, + {"DAIOUT", NULL, "Out DAIR Mux"}, + {"DAIOUT", NULL, "DAI"}, + + /* Output paths */ + {"DAIIN", NULL, "DAI"}, + + DA7219_OUT_DAC_MUX_ROUTES("Out DACL Mux"), + DA7219_OUT_DAC_MUX_ROUTES("Out DACR Mux"), + + {"Mixer Out FilterL", "DACL Switch", "Out DACL Mux"}, + {"Mixer Out FilterR", "DACR Switch", "Out DACR Mux"}, + + DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterL"), + DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterR"), + + {"DACL", NULL, "ST Mixer Out FilterL"}, + {"DACR", NULL, "ST Mixer Out FilterR"}, + + {"Mixout Left PGA", NULL, "DACL"}, + {"Mixout Right PGA", NULL, "DACR"}, + + {"HPL", NULL, "Mixout Left PGA"}, + {"HPR", NULL, "Mixout Right PGA"}, + + {"HPL", NULL, "Headphone Left PGA"}, + {"HPR", NULL, "Headphone Right PGA"}, + + {"HPL", NULL, "Charge Pump"}, + {"HPR", NULL, "Charge Pump"}, +}; + + +/* + * DAI operations + */ + +static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + int ret = 0; + + if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) + return 0; + + if ((freq < 2000000) || (freq > 54000000)) { + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + + mutex_lock(&da7219->pll_lock); + + switch (clk_id) { + case DA7219_CLKSRC_MCLK_SQR: + snd_soc_component_update_bits(component, DA7219_PLL_CTRL, + DA7219_PLL_MCLK_SQR_EN_MASK, + DA7219_PLL_MCLK_SQR_EN_MASK); + break; + case DA7219_CLKSRC_MCLK: + snd_soc_component_update_bits(component, DA7219_PLL_CTRL, + DA7219_PLL_MCLK_SQR_EN_MASK, 0); + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + mutex_unlock(&da7219->pll_lock); + return -EINVAL; + } + + da7219->clk_src = clk_id; + + if (da7219->mclk) { + freq = clk_round_rate(da7219->mclk, freq); + ret = clk_set_rate(da7219->mclk, freq); + if (ret) { + dev_err(codec_dai->dev, "Failed to set clock rate %d\n", + freq); + mutex_unlock(&da7219->pll_lock); + return ret; + } + } + + da7219->mclk_rate = freq; + + mutex_unlock(&da7219->pll_lock); + + return 0; +} + +int da7219_set_pll(struct snd_soc_component *component, int source, unsigned int fout) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + u8 pll_ctrl, indiv_bits, indiv; + u8 pll_frac_top, pll_frac_bot, pll_integer; + u32 freq_ref; + u64 frac_div; + + /* Verify 2MHz - 54MHz MCLK provided, and set input divider */ + if (da7219->mclk_rate < 2000000) { + dev_err(component->dev, "PLL input clock %d below valid range\n", + da7219->mclk_rate); + return -EINVAL; + } else if (da7219->mclk_rate <= 4500000) { + indiv_bits = DA7219_PLL_INDIV_2_TO_4_5_MHZ; + indiv = DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL; + } else if (da7219->mclk_rate <= 9000000) { + indiv_bits = DA7219_PLL_INDIV_4_5_TO_9_MHZ; + indiv = DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL; + } else if (da7219->mclk_rate <= 18000000) { + indiv_bits = DA7219_PLL_INDIV_9_TO_18_MHZ; + indiv = DA7219_PLL_INDIV_9_TO_18_MHZ_VAL; + } else if (da7219->mclk_rate <= 36000000) { + indiv_bits = DA7219_PLL_INDIV_18_TO_36_MHZ; + indiv = DA7219_PLL_INDIV_18_TO_36_MHZ_VAL; + } else if (da7219->mclk_rate <= 54000000) { + indiv_bits = DA7219_PLL_INDIV_36_TO_54_MHZ; + indiv = DA7219_PLL_INDIV_36_TO_54_MHZ_VAL; + } else { + dev_err(component->dev, "PLL input clock %d above valid range\n", + da7219->mclk_rate); + return -EINVAL; + } + freq_ref = (da7219->mclk_rate / indiv); + pll_ctrl = indiv_bits; + + /* Configure PLL */ + switch (source) { + case DA7219_SYSCLK_MCLK: + pll_ctrl |= DA7219_PLL_MODE_BYPASS; + snd_soc_component_update_bits(component, DA7219_PLL_CTRL, + DA7219_PLL_INDIV_MASK | + DA7219_PLL_MODE_MASK, pll_ctrl); + return 0; + case DA7219_SYSCLK_PLL: + pll_ctrl |= DA7219_PLL_MODE_NORMAL; + break; + case DA7219_SYSCLK_PLL_SRM: + pll_ctrl |= DA7219_PLL_MODE_SRM; + break; + default: + dev_err(component->dev, "Invalid PLL config\n"); + return -EINVAL; + } + + /* Calculate dividers for PLL */ + pll_integer = fout / freq_ref; + frac_div = (u64)(fout % freq_ref) * 8192ULL; + do_div(frac_div, freq_ref); + pll_frac_top = (frac_div >> DA7219_BYTE_SHIFT) & DA7219_BYTE_MASK; + pll_frac_bot = (frac_div) & DA7219_BYTE_MASK; + + /* Write PLL config & dividers */ + snd_soc_component_write(component, DA7219_PLL_FRAC_TOP, pll_frac_top); + snd_soc_component_write(component, DA7219_PLL_FRAC_BOT, pll_frac_bot); + snd_soc_component_write(component, DA7219_PLL_INTEGER, pll_integer); + snd_soc_component_update_bits(component, DA7219_PLL_CTRL, + DA7219_PLL_INDIV_MASK | DA7219_PLL_MODE_MASK, + pll_ctrl); + + return 0; +} + +static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_component *component = codec_dai->component; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + int ret; + + mutex_lock(&da7219->pll_lock); + ret = da7219_set_pll(component, source, fout); + mutex_unlock(&da7219->pll_lock); + + return ret; +} + +static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + u8 dai_clk_mode = 0, dai_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da7219->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + da7219->master = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7219_DAI_WCLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + dai_clk_mode |= DA7219_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7219_DAI_WCLK_POL_INV | + DA7219_DAI_CLK_POL_INV; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_DSP_B: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + dai_clk_mode |= DA7219_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7219_DAI_WCLK_POL_INV | + DA7219_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7219_DAI_WCLK_POL_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dai_ctrl |= DA7219_DAI_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + dai_ctrl |= DA7219_DAI_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dai_ctrl |= DA7219_DAI_FORMAT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_B: + dai_ctrl |= DA7219_DAI_FORMAT_DSP; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK, + dai_clk_mode); + snd_soc_component_update_bits(component, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK, + dai_ctrl); + + return 0; +} + +static int da7219_set_bclks_per_wclk(struct snd_soc_component *component, + unsigned long factor) +{ + u8 bclks_per_wclk; + + switch (factor) { + case 32: + bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; + break; + case 64: + bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; + break; + case 128: + bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128; + break; + case 256: + bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + bclks_per_wclk); + + return 0; +} + +static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX]; + struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX]; + unsigned int ch_mask; + unsigned long sr, bclk_rate; + u8 slot_offset; + u16 offset; + __le16 dai_offset; + u32 frame_size; + int ret; + + /* No channels enabled so disable TDM */ + if (!tx_mask) { + snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL, + DA7219_DAI_TDM_CH_EN_MASK | + DA7219_DAI_TDM_MODE_EN_MASK, 0); + da7219->tdm_en = false; + return 0; + } + + /* Check we have valid slots */ + slot_offset = ffs(tx_mask) - 1; + ch_mask = (tx_mask >> slot_offset); + if (fls(ch_mask) > DA7219_DAI_TDM_MAX_SLOTS) { + dev_err(component->dev, + "Invalid number of slots, max = %d\n", + DA7219_DAI_TDM_MAX_SLOTS); + return -EINVAL; + } + + /* + * Ensure we have a valid offset into the frame, based on slot width + * and slot offset of first slot we're interested in. + */ + offset = slot_offset * slot_width; + if (offset > DA7219_DAI_OFFSET_MAX) { + dev_err(component->dev, "Invalid frame offset %d\n", offset); + return -EINVAL; + } + + /* + * If we're master, calculate & validate frame size based on slot info + * provided as we have a limited set of rates available. + */ + if (da7219->master) { + frame_size = slots * slot_width; + + if (bclk) { + sr = clk_get_rate(wclk); + bclk_rate = sr * frame_size; + ret = clk_set_rate(bclk, bclk_rate); + if (ret) { + dev_err(component->dev, + "Failed to set TDM BCLK rate %lu: %d\n", + bclk_rate, ret); + return ret; + } + } else { + ret = da7219_set_bclks_per_wclk(component, frame_size); + if (ret) { + dev_err(component->dev, + "Failed to set TDM BCLKs per WCLK %d: %d\n", + frame_size, ret); + return ret; + } + } + } + + dai_offset = cpu_to_le16(offset); + regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER, + &dai_offset, sizeof(dai_offset)); + + snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL, + DA7219_DAI_TDM_CH_EN_MASK | + DA7219_DAI_TDM_MODE_EN_MASK, + (ch_mask << DA7219_DAI_TDM_CH_EN_SHIFT) | + DA7219_DAI_TDM_MODE_EN_MASK); + + da7219->tdm_en = true; + + return 0; +} + +static int da7219_set_sr(struct snd_soc_component *component, + unsigned long rate) +{ + u8 fs; + + switch (rate) { + case 8000: + fs = DA7219_SR_8000; + break; + case 11025: + fs = DA7219_SR_11025; + break; + case 12000: + fs = DA7219_SR_12000; + break; + case 16000: + fs = DA7219_SR_16000; + break; + case 22050: + fs = DA7219_SR_22050; + break; + case 24000: + fs = DA7219_SR_24000; + break; + case 32000: + fs = DA7219_SR_32000; + break; + case 44100: + fs = DA7219_SR_44100; + break; + case 48000: + fs = DA7219_SR_48000; + break; + case 88200: + fs = DA7219_SR_88200; + break; + case 96000: + fs = DA7219_SR_96000; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, DA7219_SR, fs); + + return 0; +} + +static int da7219_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct clk *wclk = da7219->dai_clks[DA7219_DAI_WCLK_IDX]; + struct clk *bclk = da7219->dai_clks[DA7219_DAI_BCLK_IDX]; + u8 dai_ctrl = 0; + unsigned int channels; + unsigned long sr, bclk_rate; + int word_len = params_width(params); + int frame_size, ret; + + switch (word_len) { + case 16: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE; + break; + case 20: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE; + break; + case 24: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE; + break; + case 32: + dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE; + break; + default: + return -EINVAL; + } + + channels = params_channels(params); + if ((channels < 1) || (channels > DA7219_DAI_CH_NUM_MAX)) { + dev_err(component->dev, + "Invalid number of channels, only 1 to %d supported\n", + DA7219_DAI_CH_NUM_MAX); + return -EINVAL; + } + dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT; + + sr = params_rate(params); + if (da7219->master && wclk) { + ret = clk_set_rate(wclk, sr); + if (ret) { + dev_err(component->dev, + "Failed to set WCLK SR %lu: %d\n", sr, ret); + return ret; + } + } else { + ret = da7219_set_sr(component, sr); + if (ret) { + dev_err(component->dev, + "Failed to set SR %lu: %d\n", sr, ret); + return ret; + } + } + + /* + * If we're master, then we have a limited set of BCLK rates we + * support. For slave mode this isn't the case and the codec can detect + * the BCLK rate automatically. + */ + if (da7219->master && !da7219->tdm_en) { + if ((word_len * DA7219_DAI_CH_NUM_MAX) <= 32) + frame_size = 32; + else + frame_size = 64; + + if (bclk) { + bclk_rate = frame_size * sr; + /* + * Rounding the rate here avoids failure trying to set a + * new rate on an already enabled bclk. In that + * instance this will just set the same rate as is + * currently in use, and so should continue without + * problem, as long as the BCLK rate is suitable for the + * desired frame size. + */ + bclk_rate = clk_round_rate(bclk, bclk_rate); + if ((bclk_rate / sr) < frame_size) { + dev_err(component->dev, + "BCLK rate mismatch against frame size"); + return -EINVAL; + } + + ret = clk_set_rate(bclk, bclk_rate); + if (ret) { + dev_err(component->dev, + "Failed to set BCLK rate %lu: %d\n", + bclk_rate, ret); + return ret; + } + } else { + ret = da7219_set_bclks_per_wclk(component, frame_size); + if (ret) { + dev_err(component->dev, + "Failed to set BCLKs per WCLK %d: %d\n", + frame_size, ret); + return ret; + } + } + } + + snd_soc_component_update_bits(component, DA7219_DAI_CTRL, + DA7219_DAI_WORD_LENGTH_MASK | + DA7219_DAI_CH_NUM_MASK, + dai_ctrl); + + return 0; +} + +static const struct snd_soc_dai_ops da7219_dai_ops = { + .hw_params = da7219_hw_params, + .set_sysclk = da7219_set_dai_sysclk, + .set_pll = da7219_set_dai_pll, + .set_fmt = da7219_set_dai_fmt, + .set_tdm_slot = da7219_set_dai_tdm_slot, +}; + +#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define DA7219_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000) + +static struct snd_soc_dai_driver da7219_dai = { + .name = "da7219-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = DA7219_DAI_CH_NUM_MAX, + .rates = DA7219_RATES, + .formats = DA7219_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = DA7219_DAI_CH_NUM_MAX, + .rates = DA7219_RATES, + .formats = DA7219_FORMATS, + }, + .ops = &da7219_dai_ops, + .symmetric_rate = 1, + .symmetric_channels = 1, + .symmetric_sample_bits = 1, +}; + + +/* + * DT/ACPI + */ + +#ifdef CONFIG_OF +static const struct of_device_id da7219_of_match[] = { + { .compatible = "dlg,da7219", }, + { } +}; +MODULE_DEVICE_TABLE(of, da7219_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id da7219_acpi_match[] = { + { .id = "DLGS7219", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, da7219_acpi_match); +#endif + +static enum da7219_micbias_voltage + da7219_fw_micbias_lvl(struct device *dev, u32 val) +{ + switch (val) { + case 1600: + return DA7219_MICBIAS_1_6V; + case 1800: + return DA7219_MICBIAS_1_8V; + case 2000: + return DA7219_MICBIAS_2_0V; + case 2200: + return DA7219_MICBIAS_2_2V; + case 2400: + return DA7219_MICBIAS_2_4V; + case 2600: + return DA7219_MICBIAS_2_6V; + default: + dev_warn(dev, "Invalid micbias level"); + return DA7219_MICBIAS_2_2V; + } +} + +static enum da7219_mic_amp_in_sel + da7219_fw_mic_amp_in_sel(struct device *dev, const char *str) +{ + if (!strcmp(str, "diff")) { + return DA7219_MIC_AMP_IN_SEL_DIFF; + } else if (!strcmp(str, "se_p")) { + return DA7219_MIC_AMP_IN_SEL_SE_P; + } else if (!strcmp(str, "se_n")) { + return DA7219_MIC_AMP_IN_SEL_SE_N; + } else { + dev_warn(dev, "Invalid mic input type selection"); + return DA7219_MIC_AMP_IN_SEL_DIFF; + } +} + +static struct da7219_pdata *da7219_fw_to_pdata(struct device *dev) +{ + struct da7219_pdata *pdata; + const char *of_str; + u32 of_val32; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + pdata->wakeup_source = device_property_read_bool(dev, "wakeup-source"); + + pdata->dai_clk_names[DA7219_DAI_WCLK_IDX] = "da7219-dai-wclk"; + pdata->dai_clk_names[DA7219_DAI_BCLK_IDX] = "da7219-dai-bclk"; + if (device_property_read_string_array(dev, "clock-output-names", + pdata->dai_clk_names, + DA7219_DAI_NUM_CLKS) < 0) + dev_warn(dev, "Using default DAI clk names: %s, %s\n", + pdata->dai_clk_names[DA7219_DAI_WCLK_IDX], + pdata->dai_clk_names[DA7219_DAI_BCLK_IDX]); + + if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0) + pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32); + else + pdata->micbias_lvl = DA7219_MICBIAS_2_2V; + + if (!device_property_read_string(dev, "dlg,mic-amp-in-sel", &of_str)) + pdata->mic_amp_in_sel = da7219_fw_mic_amp_in_sel(dev, of_str); + else + pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF; + + return pdata; +} + + +/* + * Codec driver functions + */ + +static int da7219_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + /* Enable MCLK for transition to ON state */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) { + if (da7219->mclk) { + ret = clk_prepare_enable(da7219->mclk); + if (ret) { + dev_err(component->dev, + "Failed to enable mclk\n"); + return ret; + } + } + } + + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + /* Master bias */ + snd_soc_component_update_bits(component, DA7219_REFERENCES, + DA7219_BIAS_EN_MASK, + DA7219_BIAS_EN_MASK); + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) { + /* Remove MCLK */ + if (da7219->mclk) + clk_disable_unprepare(da7219->mclk); + } + break; + case SND_SOC_BIAS_OFF: + /* Only disable master bias if we're not a wake-up source */ + if (!da7219->wakeup_source) + snd_soc_component_update_bits(component, DA7219_REFERENCES, + DA7219_BIAS_EN_MASK, 0); + + break; + } + + return 0; +} + +static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = { + [DA7219_SUPPLY_VDD] = "VDD", + [DA7219_SUPPLY_VDDMIC] = "VDDMIC", + [DA7219_SUPPLY_VDDIO] = "VDDIO", +}; + +static int da7219_handle_supplies(struct snd_soc_component *component, + u8 *io_voltage_lvl) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct regulator *vddio; + int i, ret; + + /* Get required supplies */ + for (i = 0; i < DA7219_NUM_SUPPLIES; ++i) + da7219->supplies[i].supply = da7219_supply_names[i]; + + ret = regulator_bulk_get(component->dev, DA7219_NUM_SUPPLIES, + da7219->supplies); + if (ret) { + dev_err(component->dev, "Failed to get supplies"); + return ret; + } + + /* Default to upper range */ + *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V; + + /* Determine VDDIO voltage provided */ + vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer; + ret = regulator_get_voltage(vddio); + if (ret < 1200000) + dev_warn(component->dev, "Invalid VDDIO voltage\n"); + else if (ret < 2800000) + *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V; + + /* Enable main supplies */ + ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies); + if (ret) { + dev_err(component->dev, "Failed to enable supplies"); + regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies); + return ret; + } + + return 0; +} + +#ifdef CONFIG_COMMON_CLK +static int da7219_wclk_prepare(struct clk_hw *hw) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, + dai_clks_hw[DA7219_DAI_WCLK_IDX]); + struct snd_soc_component *component = da7219->component; + + if (!da7219->master) + return -EINVAL; + + snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_EN_MASK, + DA7219_DAI_CLK_EN_MASK); + + return 0; +} + +static void da7219_wclk_unprepare(struct clk_hw *hw) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, + dai_clks_hw[DA7219_DAI_WCLK_IDX]); + struct snd_soc_component *component = da7219->component; + + if (!da7219->master) + return; + + snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, + DA7219_DAI_CLK_EN_MASK, 0); +} + +static int da7219_wclk_is_prepared(struct clk_hw *hw) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, + dai_clks_hw[DA7219_DAI_WCLK_IDX]); + struct snd_soc_component *component = da7219->component; + u8 clk_reg; + + if (!da7219->master) + return -EINVAL; + + clk_reg = snd_soc_component_read(component, DA7219_DAI_CLK_MODE); + + return !!(clk_reg & DA7219_DAI_CLK_EN_MASK); +} + +static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, + dai_clks_hw[DA7219_DAI_WCLK_IDX]); + struct snd_soc_component *component = da7219->component; + u8 fs = snd_soc_component_read(component, DA7219_SR); + + switch (fs & DA7219_SR_MASK) { + case DA7219_SR_8000: + return 8000; + case DA7219_SR_11025: + return 11025; + case DA7219_SR_12000: + return 12000; + case DA7219_SR_16000: + return 16000; + case DA7219_SR_22050: + return 22050; + case DA7219_SR_24000: + return 24000; + case DA7219_SR_32000: + return 32000; + case DA7219_SR_44100: + return 44100; + case DA7219_SR_48000: + return 48000; + case DA7219_SR_88200: + return 88200; + case DA7219_SR_96000: + return 96000; + default: + return 0; + } +} + +static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, + dai_clks_hw[DA7219_DAI_WCLK_IDX]); + + if (!da7219->master) + return -EINVAL; + + if (rate < 11025) + return 8000; + else if (rate < 12000) + return 11025; + else if (rate < 16000) + return 12000; + else if (rate < 22050) + return 16000; + else if (rate < 24000) + return 22050; + else if (rate < 32000) + return 24000; + else if (rate < 44100) + return 32000; + else if (rate < 48000) + return 44100; + else if (rate < 88200) + return 48000; + else if (rate < 96000) + return 88200; + else + return 96000; +} + +static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, + dai_clks_hw[DA7219_DAI_WCLK_IDX]); + struct snd_soc_component *component = da7219->component; + + if (!da7219->master) + return -EINVAL; + + return da7219_set_sr(component, rate); +} + +static unsigned long da7219_bclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, + dai_clks_hw[DA7219_DAI_BCLK_IDX]); + struct snd_soc_component *component = da7219->component; + u8 bclks_per_wclk = snd_soc_component_read(component, + DA7219_DAI_CLK_MODE); + + switch (bclks_per_wclk & DA7219_DAI_BCLKS_PER_WCLK_MASK) { + case DA7219_DAI_BCLKS_PER_WCLK_32: + return parent_rate * 32; + case DA7219_DAI_BCLKS_PER_WCLK_64: + return parent_rate * 64; + case DA7219_DAI_BCLKS_PER_WCLK_128: + return parent_rate * 128; + case DA7219_DAI_BCLKS_PER_WCLK_256: + return parent_rate * 256; + default: + return 0; + } +} + +static unsigned long da7219_bclk_get_factor(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long factor; + + factor = rate / parent_rate; + if (factor < 64) + return 32; + else if (factor < 128) + return 64; + else if (factor < 256) + return 128; + else + return 256; +} + +static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, + dai_clks_hw[DA7219_DAI_BCLK_IDX]); + unsigned long factor; + + if (!*parent_rate || !da7219->master) + return -EINVAL; + + /* + * We don't allow changing the parent rate as some BCLK rates can be + * derived from multiple parent WCLK rates (BCLK rates are set as a + * multiplier of WCLK in HW). We just do some rounding down based on the + * parent WCLK rate set and find the appropriate multiplier of BCLK to + * get the rounded down BCLK value. + */ + factor = da7219_bclk_get_factor(rate, *parent_rate); + + return *parent_rate * factor; +} + +static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, + dai_clks_hw[DA7219_DAI_BCLK_IDX]); + struct snd_soc_component *component = da7219->component; + unsigned long factor; + + if (!da7219->master) + return -EINVAL; + + factor = da7219_bclk_get_factor(rate, parent_rate); + + return da7219_set_bclks_per_wclk(component, factor); +} + +static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = { + [DA7219_DAI_WCLK_IDX] = { + .prepare = da7219_wclk_prepare, + .unprepare = da7219_wclk_unprepare, + .is_prepared = da7219_wclk_is_prepared, + .recalc_rate = da7219_wclk_recalc_rate, + .round_rate = da7219_wclk_round_rate, + .set_rate = da7219_wclk_set_rate, + }, + [DA7219_DAI_BCLK_IDX] = { + .recalc_rate = da7219_bclk_recalc_rate, + .round_rate = da7219_bclk_round_rate, + .set_rate = da7219_bclk_set_rate, + }, +}; + +static int da7219_register_dai_clks(struct snd_soc_component *component) +{ + struct device *dev = component->dev; + struct device_node *np = dev->of_node; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct da7219_pdata *pdata = da7219->pdata; + const char *parent_name; + struct clk_hw_onecell_data *clk_data; + int i, ret; + + /* For DT platforms allocate onecell data for clock registration */ + if (np) { + clk_data = kzalloc(struct_size(clk_data, hws, DA7219_DAI_NUM_CLKS), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = DA7219_DAI_NUM_CLKS; + da7219->clk_hw_data = clk_data; + } + + for (i = 0; i < DA7219_DAI_NUM_CLKS; ++i) { + struct clk_init_data init = {}; + struct clk_lookup *dai_clk_lookup; + struct clk_hw *dai_clk_hw = &da7219->dai_clks_hw[i]; + + switch (i) { + case DA7219_DAI_WCLK_IDX: + /* + * If we can, make MCLK the parent of WCLK to ensure + * it's enabled as required. + */ + if (da7219->mclk) { + parent_name = __clk_get_name(da7219->mclk); + init.parent_names = &parent_name; + init.num_parents = 1; + } else { + init.parent_names = NULL; + init.num_parents = 0; + } + break; + case DA7219_DAI_BCLK_IDX: + /* Make WCLK the parent of BCLK */ + parent_name = __clk_get_name(da7219->dai_clks[DA7219_DAI_WCLK_IDX]); + init.parent_names = &parent_name; + init.num_parents = 1; + break; + default: + dev_err(dev, "Invalid clock index\n"); + ret = -EINVAL; + goto err; + } + + init.name = pdata->dai_clk_names[i]; + init.ops = &da7219_dai_clk_ops[i]; + init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; + dai_clk_hw->init = &init; + + ret = clk_hw_register(dev, dai_clk_hw); + if (ret) { + dev_warn(dev, "Failed to register %s: %d\n", init.name, + ret); + goto err; + } + da7219->dai_clks[i] = dai_clk_hw->clk; + + /* For DT setup onecell data, otherwise create lookup */ + if (np) { + da7219->clk_hw_data->hws[i] = dai_clk_hw; + } else { + dai_clk_lookup = clkdev_hw_create(dai_clk_hw, init.name, + "%s", dev_name(dev)); + if (!dai_clk_lookup) { + clk_hw_unregister(dai_clk_hw); + ret = -ENOMEM; + goto err; + } else { + da7219->dai_clks_lookup[i] = dai_clk_lookup; + } + } + } + + /* If we're using DT, then register as provider accordingly */ + if (np) { + ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, + da7219->clk_hw_data); + if (ret) { + dev_err(dev, "Failed to register clock provider\n"); + goto err; + } + } + + return 0; + +err: + while (--i >= 0) { + if (da7219->dai_clks_lookup[i]) + clkdev_drop(da7219->dai_clks_lookup[i]); + + clk_hw_unregister(&da7219->dai_clks_hw[i]); + } + + if (np) + kfree(da7219->clk_hw_data); + + return ret; +} + +static void da7219_free_dai_clks(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct device_node *np = component->dev->of_node; + int i; + + if (np) + of_clk_del_provider(np); + + for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) { + if (da7219->dai_clks_lookup[i]) + clkdev_drop(da7219->dai_clks_lookup[i]); + + clk_hw_unregister(&da7219->dai_clks_hw[i]); + } + + if (np) + kfree(da7219->clk_hw_data); +} +#else +static inline int da7219_register_dai_clks(struct snd_soc_component *component) +{ + return 0; +} + +static void da7219_free_dai_clks(struct snd_soc_component *component) {} +#endif /* CONFIG_COMMON_CLK */ + +static void da7219_handle_pdata(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct da7219_pdata *pdata = da7219->pdata; + + if (pdata) { + u8 micbias_lvl = 0; + + da7219->wakeup_source = pdata->wakeup_source; + + /* Mic Bias voltages */ + switch (pdata->micbias_lvl) { + case DA7219_MICBIAS_1_6V: + case DA7219_MICBIAS_1_8V: + case DA7219_MICBIAS_2_0V: + case DA7219_MICBIAS_2_2V: + case DA7219_MICBIAS_2_4V: + case DA7219_MICBIAS_2_6V: + micbias_lvl |= (pdata->micbias_lvl << + DA7219_MICBIAS1_LEVEL_SHIFT); + break; + } + + snd_soc_component_write(component, DA7219_MICBIAS_CTRL, micbias_lvl); + + /* + * Calculate delay required to compensate for DC offset in + * Mic PGA, based on Mic Bias voltage. + */ + da7219->mic_pga_delay = DA7219_MIC_PGA_BASE_DELAY + + (pdata->micbias_lvl * + DA7219_MIC_PGA_OFFSET_DELAY); + + /* Mic */ + switch (pdata->mic_amp_in_sel) { + case DA7219_MIC_AMP_IN_SEL_DIFF: + case DA7219_MIC_AMP_IN_SEL_SE_P: + case DA7219_MIC_AMP_IN_SEL_SE_N: + snd_soc_component_write(component, DA7219_MIC_1_SELECT, + pdata->mic_amp_in_sel); + break; + } + } +} + + +/* + * Regmap configs + */ + +static struct reg_default da7219_reg_defaults[] = { + { DA7219_MIC_1_SELECT, 0x00 }, + { DA7219_CIF_TIMEOUT_CTRL, 0x01 }, + { DA7219_SR_24_48, 0x00 }, + { DA7219_SR, 0x0A }, + { DA7219_CIF_I2C_ADDR_CFG, 0x02 }, + { DA7219_PLL_CTRL, 0x10 }, + { DA7219_PLL_FRAC_TOP, 0x00 }, + { DA7219_PLL_FRAC_BOT, 0x00 }, + { DA7219_PLL_INTEGER, 0x20 }, + { DA7219_DIG_ROUTING_DAI, 0x10 }, + { DA7219_DAI_CLK_MODE, 0x01 }, + { DA7219_DAI_CTRL, 0x28 }, + { DA7219_DAI_TDM_CTRL, 0x40 }, + { DA7219_DIG_ROUTING_DAC, 0x32 }, + { DA7219_DAI_OFFSET_LOWER, 0x00 }, + { DA7219_DAI_OFFSET_UPPER, 0x00 }, + { DA7219_REFERENCES, 0x08 }, + { DA7219_MIXIN_L_SELECT, 0x00 }, + { DA7219_MIXIN_L_GAIN, 0x03 }, + { DA7219_ADC_L_GAIN, 0x6F }, + { DA7219_ADC_FILTERS1, 0x80 }, + { DA7219_MIC_1_GAIN, 0x01 }, + { DA7219_SIDETONE_CTRL, 0x40 }, + { DA7219_SIDETONE_GAIN, 0x0E }, + { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 }, + { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 }, + { DA7219_DAC_FILTERS5, 0x00 }, + { DA7219_DAC_FILTERS2, 0x88 }, + { DA7219_DAC_FILTERS3, 0x88 }, + { DA7219_DAC_FILTERS4, 0x08 }, + { DA7219_DAC_FILTERS1, 0x80 }, + { DA7219_DAC_L_GAIN, 0x6F }, + { DA7219_DAC_R_GAIN, 0x6F }, + { DA7219_CP_CTRL, 0x20 }, + { DA7219_HP_L_GAIN, 0x39 }, + { DA7219_HP_R_GAIN, 0x39 }, + { DA7219_MIXOUT_L_SELECT, 0x00 }, + { DA7219_MIXOUT_R_SELECT, 0x00 }, + { DA7219_MICBIAS_CTRL, 0x03 }, + { DA7219_MIC_1_CTRL, 0x40 }, + { DA7219_MIXIN_L_CTRL, 0x40 }, + { DA7219_ADC_L_CTRL, 0x40 }, + { DA7219_DAC_L_CTRL, 0x40 }, + { DA7219_DAC_R_CTRL, 0x40 }, + { DA7219_HP_L_CTRL, 0x40 }, + { DA7219_HP_R_CTRL, 0x40 }, + { DA7219_MIXOUT_L_CTRL, 0x10 }, + { DA7219_MIXOUT_R_CTRL, 0x10 }, + { DA7219_CHIP_ID1, 0x23 }, + { DA7219_CHIP_ID2, 0x93 }, + { DA7219_IO_CTRL, 0x00 }, + { DA7219_GAIN_RAMP_CTRL, 0x00 }, + { DA7219_PC_COUNT, 0x02 }, + { DA7219_CP_VOL_THRESHOLD1, 0x0E }, + { DA7219_DIG_CTRL, 0x00 }, + { DA7219_ALC_CTRL2, 0x00 }, + { DA7219_ALC_CTRL3, 0x00 }, + { DA7219_ALC_NOISE, 0x3F }, + { DA7219_ALC_TARGET_MIN, 0x3F }, + { DA7219_ALC_TARGET_MAX, 0x00 }, + { DA7219_ALC_GAIN_LIMITS, 0xFF }, + { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 }, + { DA7219_ALC_ANTICLIP_CTRL, 0x00 }, + { DA7219_ALC_ANTICLIP_LEVEL, 0x00 }, + { DA7219_DAC_NG_SETUP_TIME, 0x00 }, + { DA7219_DAC_NG_OFF_THRESH, 0x00 }, + { DA7219_DAC_NG_ON_THRESH, 0x00 }, + { DA7219_DAC_NG_CTRL, 0x00 }, + { DA7219_TONE_GEN_CFG1, 0x00 }, + { DA7219_TONE_GEN_CFG2, 0x00 }, + { DA7219_TONE_GEN_CYCLES, 0x00 }, + { DA7219_TONE_GEN_FREQ1_L, 0x55 }, + { DA7219_TONE_GEN_FREQ1_U, 0x15 }, + { DA7219_TONE_GEN_FREQ2_L, 0x00 }, + { DA7219_TONE_GEN_FREQ2_U, 0x40 }, + { DA7219_TONE_GEN_ON_PER, 0x02 }, + { DA7219_TONE_GEN_OFF_PER, 0x01 }, + { DA7219_ACCDET_IRQ_MASK_A, 0x00 }, + { DA7219_ACCDET_IRQ_MASK_B, 0x00 }, + { DA7219_ACCDET_CONFIG_1, 0xD6 }, + { DA7219_ACCDET_CONFIG_2, 0x34 }, + { DA7219_ACCDET_CONFIG_3, 0x0A }, + { DA7219_ACCDET_CONFIG_4, 0x16 }, + { DA7219_ACCDET_CONFIG_5, 0x21 }, + { DA7219_ACCDET_CONFIG_6, 0x3E }, + { DA7219_ACCDET_CONFIG_7, 0x01 }, + { DA7219_SYSTEM_ACTIVE, 0x00 }, +}; + +static bool da7219_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA7219_MIC_1_GAIN_STATUS: + case DA7219_MIXIN_L_GAIN_STATUS: + case DA7219_ADC_L_GAIN_STATUS: + case DA7219_DAC_L_GAIN_STATUS: + case DA7219_DAC_R_GAIN_STATUS: + case DA7219_HP_L_GAIN_STATUS: + case DA7219_HP_R_GAIN_STATUS: + case DA7219_CIF_CTRL: + case DA7219_PLL_SRM_STS: + case DA7219_ALC_CTRL1: + case DA7219_SYSTEM_MODES_INPUT: + case DA7219_SYSTEM_MODES_OUTPUT: + case DA7219_ALC_OFFSET_AUTO_M_L: + case DA7219_ALC_OFFSET_AUTO_U_L: + case DA7219_TONE_GEN_CFG1: + case DA7219_ACCDET_STATUS_A: + case DA7219_ACCDET_STATUS_B: + case DA7219_ACCDET_IRQ_EVENT_A: + case DA7219_ACCDET_IRQ_EVENT_B: + case DA7219_ACCDET_CONFIG_8: + case DA7219_SYSTEM_STATUS: + return true; + default: + return false; + } +} + +static const struct regmap_config da7219_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DA7219_SYSTEM_ACTIVE, + .reg_defaults = da7219_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults), + .volatile_reg = da7219_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static struct reg_sequence da7219_rev_aa_patch[] = { + { DA7219_REFERENCES, 0x08 }, +}; + +static int da7219_probe(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + unsigned int system_active, system_status, rev; + u8 io_voltage_lvl; + int i, ret; + + da7219->component = component; + mutex_init(&da7219->ctrl_lock); + mutex_init(&da7219->pll_lock); + + /* Regulator configuration */ + ret = da7219_handle_supplies(component, &io_voltage_lvl); + if (ret) + return ret; + + regcache_cache_bypass(da7219->regmap, true); + + /* Disable audio paths if still active from previous start */ + regmap_read(da7219->regmap, DA7219_SYSTEM_ACTIVE, &system_active); + if (system_active) { + regmap_write(da7219->regmap, DA7219_GAIN_RAMP_CTRL, + DA7219_GAIN_RAMP_RATE_NOMINAL); + regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_INPUT, 0x00); + regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_OUTPUT, 0x01); + + for (i = 0; i < DA7219_SYS_STAT_CHECK_RETRIES; ++i) { + regmap_read(da7219->regmap, DA7219_SYSTEM_STATUS, + &system_status); + if (!system_status) + break; + + msleep(DA7219_SYS_STAT_CHECK_DELAY); + } + } + + /* Soft reset component */ + regmap_write_bits(da7219->regmap, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, 0); + regmap_write_bits(da7219->regmap, DA7219_CIF_CTRL, + DA7219_CIF_REG_SOFT_RESET_MASK, + DA7219_CIF_REG_SOFT_RESET_MASK); + regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE, + DA7219_SYSTEM_ACTIVE_MASK, 0); + regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE, + DA7219_SYSTEM_ACTIVE_MASK, 1); + + regcache_cache_bypass(da7219->regmap, false); + regmap_reinit_cache(da7219->regmap, &da7219_regmap_config); + + /* Update IO voltage level range based on supply level */ + snd_soc_component_write(component, DA7219_IO_CTRL, io_voltage_lvl); + + ret = regmap_read(da7219->regmap, DA7219_CHIP_REVISION, &rev); + if (ret) { + dev_err(component->dev, "Failed to read chip revision: %d\n", ret); + goto err_disable_reg; + } + + switch (rev & DA7219_CHIP_MINOR_MASK) { + case 0: + ret = regmap_register_patch(da7219->regmap, da7219_rev_aa_patch, + ARRAY_SIZE(da7219_rev_aa_patch)); + if (ret) { + dev_err(component->dev, "Failed to register AA patch: %d\n", + ret); + goto err_disable_reg; + } + break; + default: + break; + } + + /* Handle DT/ACPI/Platform data */ + da7219_handle_pdata(component); + + /* Check if MCLK provided */ + da7219->mclk = clk_get(component->dev, "mclk"); + if (IS_ERR(da7219->mclk)) { + if (PTR_ERR(da7219->mclk) != -ENOENT) { + ret = PTR_ERR(da7219->mclk); + goto err_disable_reg; + } else { + da7219->mclk = NULL; + } + } + + /* Register CCF DAI clock control */ + ret = da7219_register_dai_clks(component); + if (ret) + goto err_put_clk; + + /* Default PC counter to free-running */ + snd_soc_component_update_bits(component, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK, + DA7219_PC_FREERUN_MASK); + + /* Default gain ramping */ + snd_soc_component_update_bits(component, DA7219_MIXIN_L_CTRL, + DA7219_MIXIN_L_AMP_RAMP_EN_MASK, + DA7219_MIXIN_L_AMP_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK, + DA7219_ADC_L_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK, + DA7219_DAC_L_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK, + DA7219_DAC_R_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_RAMP_EN_MASK, + DA7219_HP_L_AMP_RAMP_EN_MASK); + snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_RAMP_EN_MASK, + DA7219_HP_R_AMP_RAMP_EN_MASK); + + /* Default minimum gain on HP to avoid pops during DAPM sequencing */ + snd_soc_component_update_bits(component, DA7219_HP_L_CTRL, + DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, + DA7219_HP_L_AMP_MIN_GAIN_EN_MASK); + snd_soc_component_update_bits(component, DA7219_HP_R_CTRL, + DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, + DA7219_HP_R_AMP_MIN_GAIN_EN_MASK); + + /* Default infinite tone gen, start/stop by Kcontrol */ + snd_soc_component_write(component, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK); + + /* Initialise AAD block */ + ret = da7219_aad_init(component); + if (ret) + goto err_free_dai_clks; + + return 0; + +err_free_dai_clks: + da7219_free_dai_clks(component); + +err_put_clk: + clk_put(da7219->mclk); + +err_disable_reg: + regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies); + regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies); + + return ret; +} + +static void da7219_remove(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + da7219_aad_exit(component); + + da7219_free_dai_clks(component); + clk_put(da7219->mclk); + + /* Supplies */ + regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies); + regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies); +} + +#ifdef CONFIG_PM +static int da7219_suspend(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + /* Suspend AAD if we're not a wake-up source */ + if (!da7219->wakeup_source) + da7219_aad_suspend(component); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + return 0; +} + +static int da7219_resume(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + /* Resume AAD if previously suspended */ + if (!da7219->wakeup_source) + da7219_aad_resume(component); + + return 0; +} +#else +#define da7219_suspend NULL +#define da7219_resume NULL +#endif + +static int da7219_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jack, + void *data) +{ + da7219_aad_jack_det(component, jack); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_da7219 = { + .probe = da7219_probe, + .remove = da7219_remove, + .suspend = da7219_suspend, + .resume = da7219_resume, + .set_jack = da7219_set_jack, + .set_bias_level = da7219_set_bias_level, + .controls = da7219_snd_controls, + .num_controls = ARRAY_SIZE(da7219_snd_controls), + .dapm_widgets = da7219_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7219_dapm_widgets), + .dapm_routes = da7219_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7219_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + + +/* + * I2C layer + */ + +static int da7219_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct da7219_priv *da7219; + int ret; + + da7219 = devm_kzalloc(dev, sizeof(struct da7219_priv), + GFP_KERNEL); + if (!da7219) + return -ENOMEM; + + i2c_set_clientdata(i2c, da7219); + + da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config); + if (IS_ERR(da7219->regmap)) { + ret = PTR_ERR(da7219->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + /* Retrieve DT/ACPI/Platform data */ + da7219->pdata = dev_get_platdata(dev); + if (!da7219->pdata) + da7219->pdata = da7219_fw_to_pdata(dev); + + /* AAD */ + ret = da7219_aad_probe(i2c); + if (ret) + return ret; + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_da7219, + &da7219_dai, 1); + if (ret < 0) { + dev_err(dev, "Failed to register da7219 component: %d\n", ret); + } + return ret; +} + +static const struct i2c_device_id da7219_i2c_id[] = { + { "da7219", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da7219_i2c_id); + +static struct i2c_driver da7219_i2c_driver = { + .driver = { + .name = "da7219", + .of_match_table = of_match_ptr(da7219_of_match), + .acpi_match_table = ACPI_PTR(da7219_acpi_match), + }, + .probe = da7219_i2c_probe, + .id_table = da7219_i2c_id, +}; + +module_i2c_driver(da7219_i2c_driver); + +MODULE_DESCRIPTION("ASoC DA7219 Codec Driver"); +MODULE_AUTHOR("Adam Thomson "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h new file mode 100644 index 0000000000..94af88f525 --- /dev/null +++ b/sound/soc/codecs/da7219.h @@ -0,0 +1,839 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * da7219.h - DA7219 ALSA SoC Codec Driver + * + * Copyright (c) 2015 Dialog Semiconductor + * + * Author: Adam Thomson + */ + +#ifndef __DA7219_H +#define __DA7219_H + +#include +#include +#include +#include +#include +#include + +/* + * Registers + */ + +#define DA7219_MIC_1_GAIN_STATUS 0x6 +#define DA7219_MIXIN_L_GAIN_STATUS 0x8 +#define DA7219_ADC_L_GAIN_STATUS 0xA +#define DA7219_DAC_L_GAIN_STATUS 0xC +#define DA7219_DAC_R_GAIN_STATUS 0xD +#define DA7219_HP_L_GAIN_STATUS 0xE +#define DA7219_HP_R_GAIN_STATUS 0xF +#define DA7219_MIC_1_SELECT 0x10 +#define DA7219_CIF_TIMEOUT_CTRL 0x12 +#define DA7219_CIF_CTRL 0x13 +#define DA7219_SR_24_48 0x16 +#define DA7219_SR 0x17 +#define DA7219_CIF_I2C_ADDR_CFG 0x1B +#define DA7219_PLL_CTRL 0x20 +#define DA7219_PLL_FRAC_TOP 0x22 +#define DA7219_PLL_FRAC_BOT 0x23 +#define DA7219_PLL_INTEGER 0x24 +#define DA7219_PLL_SRM_STS 0x25 +#define DA7219_DIG_ROUTING_DAI 0x2A +#define DA7219_DAI_CLK_MODE 0x2B +#define DA7219_DAI_CTRL 0x2C +#define DA7219_DAI_TDM_CTRL 0x2D +#define DA7219_DIG_ROUTING_DAC 0x2E +#define DA7219_ALC_CTRL1 0x2F +#define DA7219_DAI_OFFSET_LOWER 0x30 +#define DA7219_DAI_OFFSET_UPPER 0x31 +#define DA7219_REFERENCES 0x32 +#define DA7219_MIXIN_L_SELECT 0x33 +#define DA7219_MIXIN_L_GAIN 0x34 +#define DA7219_ADC_L_GAIN 0x36 +#define DA7219_ADC_FILTERS1 0x38 +#define DA7219_MIC_1_GAIN 0x39 +#define DA7219_SIDETONE_CTRL 0x3A +#define DA7219_SIDETONE_GAIN 0x3B +#define DA7219_DROUTING_ST_OUTFILT_1L 0x3C +#define DA7219_DROUTING_ST_OUTFILT_1R 0x3D +#define DA7219_DAC_FILTERS5 0x40 +#define DA7219_DAC_FILTERS2 0x41 +#define DA7219_DAC_FILTERS3 0x42 +#define DA7219_DAC_FILTERS4 0x43 +#define DA7219_DAC_FILTERS1 0x44 +#define DA7219_DAC_L_GAIN 0x45 +#define DA7219_DAC_R_GAIN 0x46 +#define DA7219_CP_CTRL 0x47 +#define DA7219_HP_L_GAIN 0x48 +#define DA7219_HP_R_GAIN 0x49 +#define DA7219_MIXOUT_L_SELECT 0x4B +#define DA7219_MIXOUT_R_SELECT 0x4C +#define DA7219_SYSTEM_MODES_INPUT 0x50 +#define DA7219_SYSTEM_MODES_OUTPUT 0x51 +#define DA7219_MICBIAS_CTRL 0x62 +#define DA7219_MIC_1_CTRL 0x63 +#define DA7219_MIXIN_L_CTRL 0x65 +#define DA7219_ADC_L_CTRL 0x67 +#define DA7219_DAC_L_CTRL 0x69 +#define DA7219_DAC_R_CTRL 0x6A +#define DA7219_HP_L_CTRL 0x6B +#define DA7219_HP_R_CTRL 0x6C +#define DA7219_MIXOUT_L_CTRL 0x6E +#define DA7219_MIXOUT_R_CTRL 0x6F +#define DA7219_CHIP_ID1 0x81 +#define DA7219_CHIP_ID2 0x82 +#define DA7219_CHIP_REVISION 0x83 +#define DA7219_IO_CTRL 0x91 +#define DA7219_GAIN_RAMP_CTRL 0x92 +#define DA7219_PC_COUNT 0x94 +#define DA7219_CP_VOL_THRESHOLD1 0x95 +#define DA7219_CP_DELAY 0x96 +#define DA7219_DIG_CTRL 0x99 +#define DA7219_ALC_CTRL2 0x9A +#define DA7219_ALC_CTRL3 0x9B +#define DA7219_ALC_NOISE 0x9C +#define DA7219_ALC_TARGET_MIN 0x9D +#define DA7219_ALC_TARGET_MAX 0x9E +#define DA7219_ALC_GAIN_LIMITS 0x9F +#define DA7219_ALC_ANA_GAIN_LIMITS 0xA0 +#define DA7219_ALC_ANTICLIP_CTRL 0xA1 +#define DA7219_ALC_ANTICLIP_LEVEL 0xA2 +#define DA7219_ALC_OFFSET_AUTO_M_L 0xA3 +#define DA7219_ALC_OFFSET_AUTO_U_L 0xA4 +#define DA7219_DAC_NG_SETUP_TIME 0xAF +#define DA7219_DAC_NG_OFF_THRESH 0xB0 +#define DA7219_DAC_NG_ON_THRESH 0xB1 +#define DA7219_DAC_NG_CTRL 0xB2 +#define DA7219_TONE_GEN_CFG1 0xB4 +#define DA7219_TONE_GEN_CFG2 0xB5 +#define DA7219_TONE_GEN_CYCLES 0xB6 +#define DA7219_TONE_GEN_FREQ1_L 0xB7 +#define DA7219_TONE_GEN_FREQ1_U 0xB8 +#define DA7219_TONE_GEN_FREQ2_L 0xB9 +#define DA7219_TONE_GEN_FREQ2_U 0xBA +#define DA7219_TONE_GEN_ON_PER 0xBB +#define DA7219_TONE_GEN_OFF_PER 0xBC +#define DA7219_SYSTEM_STATUS 0xE0 +#define DA7219_SYSTEM_ACTIVE 0xFD + + +/* + * Bit Fields + */ + +#define DA7219_SWITCH_EN_MAX 0x1 + +/* DA7219_MIC_1_GAIN_STATUS = 0x6 */ +#define DA7219_MIC_1_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_MIC_1_AMP_GAIN_STATUS_MASK (0x7 << 0) +#define DA7219_MIC_1_AMP_GAIN_MAX 0x7 + +/* DA7219_MIXIN_L_GAIN_STATUS = 0x8 */ +#define DA7219_MIXIN_L_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_MIXIN_L_AMP_GAIN_STATUS_MASK (0xF << 0) + +/* DA7219_ADC_L_GAIN_STATUS = 0xA */ +#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_DAC_L_GAIN_STATUS = 0xC */ +#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_DAC_R_GAIN_STATUS = 0xD */ +#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_SHIFT 0 +#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_MASK (0x7F << 0) + +/* DA7219_HP_L_GAIN_STATUS = 0xE */ +#define DA7219_HP_L_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_HP_L_AMP_GAIN_STATUS_MASK (0x3F << 0) + +/* DA7219_HP_R_GAIN_STATUS = 0xF */ +#define DA7219_HP_R_AMP_GAIN_STATUS_SHIFT 0 +#define DA7219_HP_R_AMP_GAIN_STATUS_MASK (0x3F << 0) + +/* DA7219_MIC_1_SELECT = 0x10 */ +#define DA7219_MIC_1_AMP_IN_SEL_SHIFT 0 +#define DA7219_MIC_1_AMP_IN_SEL_MASK (0x3 << 0) + +/* DA7219_CIF_TIMEOUT_CTRL = 0x12 */ +#define DA7219_I2C_TIMEOUT_EN_SHIFT 0 +#define DA7219_I2C_TIMEOUT_EN_MASK (0x1 << 0) + +/* DA7219_CIF_CTRL = 0x13 */ +#define DA7219_CIF_I2C_WRITE_MODE_SHIFT 0 +#define DA7219_CIF_I2C_WRITE_MODE_MASK (0x1 << 0) +#define DA7219_CIF_REG_SOFT_RESET_SHIFT 7 +#define DA7219_CIF_REG_SOFT_RESET_MASK (0x1 << 7) + +/* DA7219_SR_24_48 = 0x16 */ +#define DA7219_SR_24_48_SHIFT 0 +#define DA7219_SR_24_48_MASK (0x1 << 0) + +/* DA7219_SR = 0x17 */ +#define DA7219_SR_SHIFT 0 +#define DA7219_SR_MASK (0xF << 0) +#define DA7219_SR_8000 (0x01 << 0) +#define DA7219_SR_11025 (0x02 << 0) +#define DA7219_SR_12000 (0x03 << 0) +#define DA7219_SR_16000 (0x05 << 0) +#define DA7219_SR_22050 (0x06 << 0) +#define DA7219_SR_24000 (0x07 << 0) +#define DA7219_SR_32000 (0x09 << 0) +#define DA7219_SR_44100 (0x0A << 0) +#define DA7219_SR_48000 (0x0B << 0) +#define DA7219_SR_88200 (0x0E << 0) +#define DA7219_SR_96000 (0x0F << 0) + +/* DA7219_CIF_I2C_ADDR_CFG = 0x1B */ +#define DA7219_CIF_I2C_ADDR_CFG_SHIFT 0 +#define DA7219_CIF_I2C_ADDR_CFG_MASK (0x3 << 0) + +/* DA7219_PLL_CTRL = 0x20 */ +#define DA7219_PLL_INDIV_SHIFT 2 +#define DA7219_PLL_INDIV_MASK (0x7 << 2) +#define DA7219_PLL_INDIV_2_TO_4_5_MHZ (0x0 << 2) +#define DA7219_PLL_INDIV_4_5_TO_9_MHZ (0x1 << 2) +#define DA7219_PLL_INDIV_9_TO_18_MHZ (0x2 << 2) +#define DA7219_PLL_INDIV_18_TO_36_MHZ (0x3 << 2) +#define DA7219_PLL_INDIV_36_TO_54_MHZ (0x4 << 2) +#define DA7219_PLL_MCLK_SQR_EN_SHIFT 5 +#define DA7219_PLL_MCLK_SQR_EN_MASK (0x1 << 5) +#define DA7219_PLL_MODE_SHIFT 6 +#define DA7219_PLL_MODE_MASK (0x3 << 6) +#define DA7219_PLL_MODE_BYPASS (0x0 << 6) +#define DA7219_PLL_MODE_NORMAL (0x1 << 6) +#define DA7219_PLL_MODE_SRM (0x2 << 6) + +/* DA7219_PLL_FRAC_TOP = 0x22 */ +#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT 0 +#define DA7219_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0) + +/* DA7219_PLL_FRAC_BOT = 0x23 */ +#define DA7219_PLL_FBDIV_FRAC_BOT_SHIFT 0 +#define DA7219_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0) + +/* DA7219_PLL_INTEGER = 0x24 */ +#define DA7219_PLL_FBDIV_INTEGER_SHIFT 0 +#define DA7219_PLL_FBDIV_INTEGER_MASK (0x7F << 0) + +/* DA7219_PLL_SRM_STS = 0x25 */ +#define DA7219_PLL_SRM_STATE_SHIFT 0 +#define DA7219_PLL_SRM_STATE_MASK (0xF << 0) +#define DA7219_PLL_SRM_STATUS_SHIFT 4 +#define DA7219_PLL_SRM_STATUS_MASK (0xF << 4) +#define DA7219_PLL_SRM_STS_MCLK (0x1 << 4) +#define DA7219_PLL_SRM_STS_SRM_LOCK (0x1 << 7) + +/* DA7219_DIG_ROUTING_DAI = 0x2A */ +#define DA7219_DAI_L_SRC_SHIFT 0 +#define DA7219_DAI_L_SRC_MASK (0x3 << 0) +#define DA7219_DAI_R_SRC_SHIFT 4 +#define DA7219_DAI_R_SRC_MASK (0x3 << 4) +#define DA7219_OUT_SRC_MAX 4 + +/* DA7219_DAI_CLK_MODE = 0x2B */ +#define DA7219_DAI_BCLKS_PER_WCLK_SHIFT 0 +#define DA7219_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_32 (0x0 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_64 (0x1 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_128 (0x2 << 0) +#define DA7219_DAI_BCLKS_PER_WCLK_256 (0x3 << 0) +#define DA7219_DAI_CLK_POL_SHIFT 2 +#define DA7219_DAI_CLK_POL_MASK (0x1 << 2) +#define DA7219_DAI_CLK_POL_INV (0x1 << 2) +#define DA7219_DAI_WCLK_POL_SHIFT 3 +#define DA7219_DAI_WCLK_POL_MASK (0x1 << 3) +#define DA7219_DAI_WCLK_POL_INV (0x1 << 3) +#define DA7219_DAI_WCLK_TRI_STATE_SHIFT 4 +#define DA7219_DAI_WCLK_TRI_STATE_MASK (0x1 << 4) +#define DA7219_DAI_CLK_EN_SHIFT 7 +#define DA7219_DAI_CLK_EN_MASK (0x1 << 7) + +/* DA7219_DAI_CTRL = 0x2C */ +#define DA7219_DAI_FORMAT_SHIFT 0 +#define DA7219_DAI_FORMAT_MASK (0x3 << 0) +#define DA7219_DAI_FORMAT_I2S (0x0 << 0) +#define DA7219_DAI_FORMAT_LEFT_J (0x1 << 0) +#define DA7219_DAI_FORMAT_RIGHT_J (0x2 << 0) +#define DA7219_DAI_FORMAT_DSP (0x3 << 0) +#define DA7219_DAI_WORD_LENGTH_SHIFT 2 +#define DA7219_DAI_WORD_LENGTH_MASK (0x3 << 2) +#define DA7219_DAI_WORD_LENGTH_S16_LE (0x0 << 2) +#define DA7219_DAI_WORD_LENGTH_S20_LE (0x1 << 2) +#define DA7219_DAI_WORD_LENGTH_S24_LE (0x2 << 2) +#define DA7219_DAI_WORD_LENGTH_S32_LE (0x3 << 2) +#define DA7219_DAI_CH_NUM_SHIFT 4 +#define DA7219_DAI_CH_NUM_MASK (0x3 << 4) +#define DA7219_DAI_CH_NUM_MAX 2 +#define DA7219_DAI_EN_SHIFT 7 +#define DA7219_DAI_EN_MASK (0x1 << 7) + +/* DA7219_DAI_TDM_CTRL = 0x2D */ +#define DA7219_DAI_TDM_CH_EN_SHIFT 0 +#define DA7219_DAI_TDM_CH_EN_MASK (0x3 << 0) +#define DA7219_DAI_OE_SHIFT 6 +#define DA7219_DAI_OE_MASK (0x1 << 6) +#define DA7219_DAI_TDM_MODE_EN_SHIFT 7 +#define DA7219_DAI_TDM_MODE_EN_MASK (0x1 << 7) +#define DA7219_DAI_TDM_MAX_SLOTS 2 + +/* DA7219_DIG_ROUTING_DAC = 0x2E */ +#define DA7219_DAC_L_SRC_SHIFT 0 +#define DA7219_DAC_L_SRC_MASK (0x3 << 0) +#define DA7219_DAC_L_SRC_TONEGEN (0x1 << 0) +#define DA7219_DAC_L_MONO_SHIFT 3 +#define DA7219_DAC_L_MONO_MASK (0x1 << 3) +#define DA7219_DAC_R_SRC_SHIFT 4 +#define DA7219_DAC_R_SRC_MASK (0x3 << 4) +#define DA7219_DAC_R_SRC_TONEGEN (0x1 << 4) +#define DA7219_DAC_R_MONO_SHIFT 7 +#define DA7219_DAC_R_MONO_MASK (0x1 << 7) + +/* DA7219_ALC_CTRL1 = 0x2F */ +#define DA7219_ALC_OFFSET_EN_SHIFT 0 +#define DA7219_ALC_OFFSET_EN_MASK (0x1 << 0) +#define DA7219_ALC_SYNC_MODE_SHIFT 1 +#define DA7219_ALC_SYNC_MODE_MASK (0x1 << 1) +#define DA7219_ALC_EN_SHIFT 3 +#define DA7219_ALC_EN_MASK (0x1 << 3) +#define DA7219_ALC_AUTO_CALIB_EN_SHIFT 4 +#define DA7219_ALC_AUTO_CALIB_EN_MASK (0x1 << 4) +#define DA7219_ALC_CALIB_OVERFLOW_SHIFT 5 +#define DA7219_ALC_CALIB_OVERFLOW_MASK (0x1 << 5) + +/* DA7219_DAI_OFFSET_LOWER = 0x30 */ +#define DA7219_DAI_OFFSET_LOWER_SHIFT 0 +#define DA7219_DAI_OFFSET_LOWER_MASK (0xFF << 0) + +/* DA7219_DAI_OFFSET_UPPER = 0x31 */ +#define DA7219_DAI_OFFSET_UPPER_SHIFT 0 +#define DA7219_DAI_OFFSET_UPPER_MASK (0x7 << 0) +#define DA7219_DAI_OFFSET_MAX 0x2FF + +/* DA7219_REFERENCES = 0x32 */ +#define DA7219_BIAS_EN_SHIFT 3 +#define DA7219_BIAS_EN_MASK (0x1 << 3) +#define DA7219_VMID_FAST_CHARGE_SHIFT 4 +#define DA7219_VMID_FAST_CHARGE_MASK (0x1 << 4) + +/* DA7219_MIXIN_L_SELECT = 0x33 */ +#define DA7219_MIXIN_L_MIX_SELECT_SHIFT 0 +#define DA7219_MIXIN_L_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_MIXIN_L_GAIN = 0x34 */ +#define DA7219_MIXIN_L_AMP_GAIN_SHIFT 0 +#define DA7219_MIXIN_L_AMP_GAIN_MASK (0xF << 0) +#define DA7219_MIXIN_L_AMP_GAIN_MAX 0xF + +/* DA7219_ADC_L_GAIN = 0x36 */ +#define DA7219_ADC_L_DIGITAL_GAIN_SHIFT 0 +#define DA7219_ADC_L_DIGITAL_GAIN_MASK (0x7F << 0) +#define DA7219_ADC_L_DIGITAL_GAIN_MAX 0x7F + +/* DA7219_ADC_FILTERS1 = 0x38 */ +#define DA7219_ADC_VOICE_HPF_CORNER_SHIFT 0 +#define DA7219_ADC_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7219_VOICE_HPF_CORNER_MAX 8 +#define DA7219_ADC_VOICE_EN_SHIFT 3 +#define DA7219_ADC_VOICE_EN_MASK (0x1 << 3) +#define DA7219_ADC_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7219_ADC_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7219_AUDIO_HPF_CORNER_MAX 4 +#define DA7219_ADC_HPF_EN_SHIFT 7 +#define DA7219_ADC_HPF_EN_MASK (0x1 << 7) +#define DA7219_HPF_MODE_SHIFT 0 +#define DA7219_HPF_DISABLED ((0x0 << 3) | (0x0 << 7)) +#define DA7219_HPF_AUDIO_EN ((0x0 << 3) | (0x1 << 7)) +#define DA7219_HPF_VOICE_EN ((0x1 << 3) | (0x1 << 7)) +#define DA7219_HPF_MODE_MASK ((0x1 << 3) | (0x1 << 7)) +#define DA7219_HPF_MODE_MAX 3 + +/* DA7219_MIC_1_GAIN = 0x39 */ +#define DA7219_MIC_1_AMP_GAIN_SHIFT 0 +#define DA7219_MIC_1_AMP_GAIN_MASK (0x7 << 0) + +/* DA7219_SIDETONE_CTRL = 0x3A */ +#define DA7219_SIDETONE_MUTE_EN_SHIFT 6 +#define DA7219_SIDETONE_MUTE_EN_MASK (0x1 << 6) +#define DA7219_SIDETONE_EN_SHIFT 7 +#define DA7219_SIDETONE_EN_MASK (0x1 << 7) + +/* DA7219_SIDETONE_GAIN = 0x3B */ +#define DA7219_SIDETONE_GAIN_SHIFT 0 +#define DA7219_SIDETONE_GAIN_MASK (0xF << 0) +#define DA7219_SIDETONE_GAIN_MAX 0xE + +/* DA7219_DROUTING_ST_OUTFILT_1L = 0x3C */ +#define DA7219_OUTFILT_ST_1L_SRC_SHIFT 0 +#define DA7219_OUTFILT_ST_1L_SRC_MASK (0x7 << 0) +#define DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT 0 +#define DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT 1 +#define DA7219_DMIX_ST_SRC_SIDETONE_SHIFT 2 +#define DA7219_DMIX_ST_SRC_OUTFILT1L (0x1 << 0) +#define DA7219_DMIX_ST_SRC_OUTFILT1R (0x1 << 1) + +/* DA7219_DROUTING_ST_OUTFILT_1R = 0x3D */ +#define DA7219_OUTFILT_ST_1R_SRC_SHIFT 0 +#define DA7219_OUTFILT_ST_1R_SRC_MASK (0x7 << 0) + +/* DA7219_DAC_FILTERS5 = 0x40 */ +#define DA7219_DAC_SOFTMUTE_RATE_SHIFT 4 +#define DA7219_DAC_SOFTMUTE_RATE_MASK (0x7 << 4) +#define DA7219_DAC_SOFTMUTE_RATE_MAX 7 +#define DA7219_DAC_SOFTMUTE_EN_SHIFT 7 +#define DA7219_DAC_SOFTMUTE_EN_MASK (0x1 << 7) + +/* DA7219_DAC_FILTERS2 = 0x41 */ +#define DA7219_DAC_EQ_BAND1_SHIFT 0 +#define DA7219_DAC_EQ_BAND1_MASK (0xF << 0) +#define DA7219_DAC_EQ_BAND2_SHIFT 4 +#define DA7219_DAC_EQ_BAND2_MASK (0xF << 4) +#define DA7219_DAC_EQ_BAND_MAX 0xF + +/* DA7219_DAC_FILTERS3 = 0x42 */ +#define DA7219_DAC_EQ_BAND3_SHIFT 0 +#define DA7219_DAC_EQ_BAND3_MASK (0xF << 0) +#define DA7219_DAC_EQ_BAND4_SHIFT 4 +#define DA7219_DAC_EQ_BAND4_MASK (0xF << 4) + +/* DA7219_DAC_FILTERS4 = 0x43 */ +#define DA7219_DAC_EQ_BAND5_SHIFT 0 +#define DA7219_DAC_EQ_BAND5_MASK (0xF << 0) +#define DA7219_DAC_EQ_EN_SHIFT 7 +#define DA7219_DAC_EQ_EN_MASK (0x1 << 7) + +/* DA7219_DAC_FILTERS1 = 0x44 */ +#define DA7219_DAC_VOICE_HPF_CORNER_SHIFT 0 +#define DA7219_DAC_VOICE_HPF_CORNER_MASK (0x7 << 0) +#define DA7219_DAC_VOICE_EN_SHIFT 3 +#define DA7219_DAC_VOICE_EN_MASK (0x1 << 3) +#define DA7219_DAC_AUDIO_HPF_CORNER_SHIFT 4 +#define DA7219_DAC_AUDIO_HPF_CORNER_MASK (0x3 << 4) +#define DA7219_DAC_HPF_EN_SHIFT 7 +#define DA7219_DAC_HPF_EN_MASK (0x1 << 7) + +/* DA7219_DAC_L_GAIN = 0x45 */ +#define DA7219_DAC_L_DIGITAL_GAIN_SHIFT 0 +#define DA7219_DAC_L_DIGITAL_GAIN_MASK (0x7F << 0) +#define DA7219_DAC_DIGITAL_GAIN_MAX 0x7F +#define DA7219_DAC_DIGITAL_GAIN_0DB (0x6F << 0) + +/* DA7219_DAC_R_GAIN = 0x46 */ +#define DA7219_DAC_R_DIGITAL_GAIN_SHIFT 0 +#define DA7219_DAC_R_DIGITAL_GAIN_MASK (0x7F << 0) + +/* DA7219_CP_CTRL = 0x47 */ +#define DA7219_CP_MCHANGE_SHIFT 4 +#define DA7219_CP_MCHANGE_MASK (0x3 << 4) +#define DA7219_CP_MCHANGE_REL_MASK 0x3 +#define DA7219_CP_MCHANGE_MAX 3 +#define DA7219_CP_MCHANGE_LARGEST_VOL 0x1 +#define DA7219_CP_MCHANGE_DAC_VOL 0x2 +#define DA7219_CP_MCHANGE_SIG_MAG 0x3 +#define DA7219_CP_EN_SHIFT 7 +#define DA7219_CP_EN_MASK (0x1 << 7) + +/* DA7219_HP_L_GAIN = 0x48 */ +#define DA7219_HP_L_AMP_GAIN_SHIFT 0 +#define DA7219_HP_L_AMP_GAIN_MASK (0x3F << 0) +#define DA7219_HP_AMP_GAIN_MAX 0x3F +#define DA7219_HP_AMP_GAIN_0DB (0x39 << 0) + +/* DA7219_HP_R_GAIN = 0x49 */ +#define DA7219_HP_R_AMP_GAIN_SHIFT 0 +#define DA7219_HP_R_AMP_GAIN_MASK (0x3F << 0) + +/* DA7219_MIXOUT_L_SELECT = 0x4B */ +#define DA7219_MIXOUT_L_MIX_SELECT_SHIFT 0 +#define DA7219_MIXOUT_L_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_MIXOUT_R_SELECT = 0x4C */ +#define DA7219_MIXOUT_R_MIX_SELECT_SHIFT 0 +#define DA7219_MIXOUT_R_MIX_SELECT_MASK (0x1 << 0) + +/* DA7219_SYSTEM_MODES_INPUT = 0x50 */ +#define DA7219_MODE_SUBMIT_SHIFT 0 +#define DA7219_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7219_ADC_MODE_SHIFT 1 +#define DA7219_ADC_MODE_MASK (0x7F << 1) + +/* DA7219_SYSTEM_MODES_OUTPUT = 0x51 */ +#define DA7219_MODE_SUBMIT_SHIFT 0 +#define DA7219_MODE_SUBMIT_MASK (0x1 << 0) +#define DA7219_DAC_MODE_SHIFT 1 +#define DA7219_DAC_MODE_MASK (0x7F << 1) + +/* DA7219_MICBIAS_CTRL = 0x62 */ +#define DA7219_MICBIAS1_LEVEL_SHIFT 0 +#define DA7219_MICBIAS1_LEVEL_MASK (0x7 << 0) +#define DA7219_MICBIAS1_EN_SHIFT 3 +#define DA7219_MICBIAS1_EN_MASK (0x1 << 3) + +/* DA7219_MIC_1_CTRL = 0x63 */ +#define DA7219_MIC_1_AMP_RAMP_EN_SHIFT 5 +#define DA7219_MIC_1_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_MIC_1_AMP_MUTE_EN_SHIFT 6 +#define DA7219_MIC_1_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_MIC_1_AMP_EN_SHIFT 7 +#define DA7219_MIC_1_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXIN_L_CTRL = 0x65 */ +#define DA7219_MIXIN_L_MIX_EN_SHIFT 3 +#define DA7219_MIXIN_L_MIX_EN_MASK (0x1 << 3) +#define DA7219_MIXIN_L_AMP_ZC_EN_SHIFT 4 +#define DA7219_MIXIN_L_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT 5 +#define DA7219_MIXIN_L_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT 6 +#define DA7219_MIXIN_L_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_MIXIN_L_AMP_EN_SHIFT 7 +#define DA7219_MIXIN_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_ADC_L_CTRL = 0x67 */ +#define DA7219_ADC_L_BIAS_SHIFT 0 +#define DA7219_ADC_L_BIAS_MASK (0x3 << 0) +#define DA7219_ADC_L_RAMP_EN_SHIFT 5 +#define DA7219_ADC_L_RAMP_EN_MASK (0x1 << 5) +#define DA7219_ADC_L_MUTE_EN_SHIFT 6 +#define DA7219_ADC_L_MUTE_EN_MASK (0x1 << 6) +#define DA7219_ADC_L_EN_SHIFT 7 +#define DA7219_ADC_L_EN_MASK (0x1 << 7) + +/* DA7219_DAC_L_CTRL = 0x69 */ +#define DA7219_DAC_L_RAMP_EN_SHIFT 5 +#define DA7219_DAC_L_RAMP_EN_MASK (0x1 << 5) +#define DA7219_DAC_L_MUTE_EN_SHIFT 6 +#define DA7219_DAC_L_MUTE_EN_MASK (0x1 << 6) +#define DA7219_DAC_L_EN_SHIFT 7 +#define DA7219_DAC_L_EN_MASK (0x1 << 7) + +/* DA7219_DAC_R_CTRL = 0x6A */ +#define DA7219_DAC_R_RAMP_EN_SHIFT 5 +#define DA7219_DAC_R_RAMP_EN_MASK (0x1 << 5) +#define DA7219_DAC_R_MUTE_EN_SHIFT 6 +#define DA7219_DAC_R_MUTE_EN_MASK (0x1 << 6) +#define DA7219_DAC_R_EN_SHIFT 7 +#define DA7219_DAC_R_EN_MASK (0x1 << 7) + +/* DA7219_HP_L_CTRL = 0x6B */ +#define DA7219_HP_L_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7219_HP_L_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7219_HP_L_AMP_OE_SHIFT 3 +#define DA7219_HP_L_AMP_OE_MASK (0x1 << 3) +#define DA7219_HP_L_AMP_ZC_EN_SHIFT 4 +#define DA7219_HP_L_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_HP_L_AMP_RAMP_EN_SHIFT 5 +#define DA7219_HP_L_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_HP_L_AMP_MUTE_EN_SHIFT 6 +#define DA7219_HP_L_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_HP_L_AMP_EN_SHIFT 7 +#define DA7219_HP_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_HP_R_CTRL = 0x6C */ +#define DA7219_HP_R_AMP_MIN_GAIN_EN_SHIFT 2 +#define DA7219_HP_R_AMP_MIN_GAIN_EN_MASK (0x1 << 2) +#define DA7219_HP_R_AMP_OE_SHIFT 3 +#define DA7219_HP_R_AMP_OE_MASK (0x1 << 3) +#define DA7219_HP_R_AMP_ZC_EN_SHIFT 4 +#define DA7219_HP_R_AMP_ZC_EN_MASK (0x1 << 4) +#define DA7219_HP_R_AMP_RAMP_EN_SHIFT 5 +#define DA7219_HP_R_AMP_RAMP_EN_MASK (0x1 << 5) +#define DA7219_HP_R_AMP_MUTE_EN_SHIFT 6 +#define DA7219_HP_R_AMP_MUTE_EN_MASK (0x1 << 6) +#define DA7219_HP_R_AMP_EN_SHIFT 7 +#define DA7219_HP_R_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXOUT_L_CTRL = 0x6E */ +#define DA7219_MIXOUT_L_AMP_EN_SHIFT 7 +#define DA7219_MIXOUT_L_AMP_EN_MASK (0x1 << 7) + +/* DA7219_MIXOUT_R_CTRL = 0x6F */ +#define DA7219_MIXOUT_R_AMP_EN_SHIFT 7 +#define DA7219_MIXOUT_R_AMP_EN_MASK (0x1 << 7) + +/* DA7219_CHIP_ID1 = 0x81 */ +#define DA7219_CHIP_ID1_SHIFT 0 +#define DA7219_CHIP_ID1_MASK (0xFF << 0) + +/* DA7219_CHIP_ID2 = 0x82 */ +#define DA7219_CHIP_ID2_SHIFT 0 +#define DA7219_CHIP_ID2_MASK (0xFF << 0) + +/* DA7219_CHIP_REVISION = 0x83 */ +#define DA7219_CHIP_MINOR_SHIFT 0 +#define DA7219_CHIP_MINOR_MASK (0xF << 0) +#define DA7219_CHIP_MAJOR_SHIFT 4 +#define DA7219_CHIP_MAJOR_MASK (0xF << 4) + +/* DA7219_IO_CTRL = 0x91 */ +#define DA7219_IO_VOLTAGE_LEVEL_SHIFT 0 +#define DA7219_IO_VOLTAGE_LEVEL_MASK (0x1 << 0) +#define DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V 0 +#define DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V 1 + +/* DA7219_GAIN_RAMP_CTRL = 0x92 */ +#define DA7219_GAIN_RAMP_RATE_SHIFT 0 +#define DA7219_GAIN_RAMP_RATE_MASK (0x3 << 0) +#define DA7219_GAIN_RAMP_RATE_X8 (0x0 << 0) +#define DA7219_GAIN_RAMP_RATE_NOMINAL (0x1 << 0) +#define DA7219_GAIN_RAMP_RATE_MAX 4 + +/* DA7219_PC_COUNT = 0x94 */ +#define DA7219_PC_FREERUN_SHIFT 0 +#define DA7219_PC_FREERUN_MASK (0x1 << 0) +#define DA7219_PC_RESYNC_AUTO_SHIFT 1 +#define DA7219_PC_RESYNC_AUTO_MASK (0x1 << 1) + +/* DA7219_CP_VOL_THRESHOLD1 = 0x95 */ +#define DA7219_CP_THRESH_VDD2_SHIFT 0 +#define DA7219_CP_THRESH_VDD2_MASK (0x3F << 0) +#define DA7219_CP_THRESH_VDD2_MAX 0x3F + +/* DA7219_DIG_CTRL = 0x99 */ +#define DA7219_DAC_L_INV_SHIFT 3 +#define DA7219_DAC_L_INV_MASK (0x1 << 3) +#define DA7219_DAC_R_INV_SHIFT 7 +#define DA7219_DAC_R_INV_MASK (0x1 << 7) + +/* DA7219_ALC_CTRL2 = 0x9A */ +#define DA7219_ALC_ATTACK_SHIFT 0 +#define DA7219_ALC_ATTACK_MASK (0xF << 0) +#define DA7219_ALC_ATTACK_MAX 13 +#define DA7219_ALC_RELEASE_SHIFT 4 +#define DA7219_ALC_RELEASE_MASK (0xF << 4) +#define DA7219_ALC_RELEASE_MAX 11 + +/* DA7219_ALC_CTRL3 = 0x9B */ +#define DA7219_ALC_HOLD_SHIFT 0 +#define DA7219_ALC_HOLD_MASK (0xF << 0) +#define DA7219_ALC_HOLD_MAX 16 +#define DA7219_ALC_INTEG_ATTACK_SHIFT 4 +#define DA7219_ALC_INTEG_ATTACK_MASK (0x3 << 4) +#define DA7219_ALC_INTEG_RELEASE_SHIFT 6 +#define DA7219_ALC_INTEG_RELEASE_MASK (0x3 << 6) +#define DA7219_ALC_INTEG_MAX 4 + +/* DA7219_ALC_NOISE = 0x9C */ +#define DA7219_ALC_NOISE_SHIFT 0 +#define DA7219_ALC_NOISE_MASK (0x3F << 0) +#define DA7219_ALC_THRESHOLD_MAX 0x3F + +/* DA7219_ALC_TARGET_MIN = 0x9D */ +#define DA7219_ALC_THRESHOLD_MIN_SHIFT 0 +#define DA7219_ALC_THRESHOLD_MIN_MASK (0x3F << 0) + +/* DA7219_ALC_TARGET_MAX = 0x9E */ +#define DA7219_ALC_THRESHOLD_MAX_SHIFT 0 +#define DA7219_ALC_THRESHOLD_MAX_MASK (0x3F << 0) + +/* DA7219_ALC_GAIN_LIMITS = 0x9F */ +#define DA7219_ALC_ATTEN_MAX_SHIFT 0 +#define DA7219_ALC_ATTEN_MAX_MASK (0xF << 0) +#define DA7219_ALC_GAIN_MAX_SHIFT 4 +#define DA7219_ALC_GAIN_MAX_MASK (0xF << 4) +#define DA7219_ALC_ATTEN_GAIN_MAX 0xF + +/* DA7219_ALC_ANA_GAIN_LIMITS = 0xA0 */ +#define DA7219_ALC_ANA_GAIN_MIN_SHIFT 0 +#define DA7219_ALC_ANA_GAIN_MIN_MASK (0x7 << 0) +#define DA7219_ALC_ANA_GAIN_MIN 0x1 +#define DA7219_ALC_ANA_GAIN_MAX_SHIFT 4 +#define DA7219_ALC_ANA_GAIN_MAX_MASK (0x7 << 4) +#define DA7219_ALC_ANA_GAIN_MAX 0x7 + +/* DA7219_ALC_ANTICLIP_CTRL = 0xA1 */ +#define DA7219_ALC_ANTICLIP_STEP_SHIFT 0 +#define DA7219_ALC_ANTICLIP_STEP_MASK (0x3 << 0) +#define DA7219_ALC_ANTICLIP_STEP_MAX 4 +#define DA7219_ALC_ANTIPCLIP_EN_SHIFT 7 +#define DA7219_ALC_ANTIPCLIP_EN_MASK (0x1 << 7) + +/* DA7219_ALC_ANTICLIP_LEVEL = 0xA2 */ +#define DA7219_ALC_ANTICLIP_LEVEL_SHIFT 0 +#define DA7219_ALC_ANTICLIP_LEVEL_MASK (0x7F << 0) + +/* DA7219_ALC_OFFSET_AUTO_M_L = 0xA3 */ +#define DA7219_ALC_OFFSET_AUTO_M_L_SHIFT 0 +#define DA7219_ALC_OFFSET_AUTO_M_L_MASK (0xFF << 0) + +/* DA7219_ALC_OFFSET_AUTO_U_L = 0xA4 */ +#define DA7219_ALC_OFFSET_AUTO_U_L_SHIFT 0 +#define DA7219_ALC_OFFSET_AUTO_U_L_MASK (0xF << 0) + +/* DA7219_DAC_NG_SETUP_TIME = 0xAF */ +#define DA7219_DAC_NG_SETUP_TIME_SHIFT 0 +#define DA7219_DAC_NG_SETUP_TIME_MASK (0x3 << 0) +#define DA7219_DAC_NG_SETUP_TIME_MAX 4 +#define DA7219_DAC_NG_RAMPUP_RATE_SHIFT 2 +#define DA7219_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2) +#define DA7219_DAC_NG_RAMPDN_RATE_SHIFT 3 +#define DA7219_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3) +#define DA7219_DAC_NG_RAMP_RATE_MAX 2 + +/* DA7219_DAC_NG_OFF_THRESH = 0xB0 */ +#define DA7219_DAC_NG_OFF_THRESHOLD_SHIFT 0 +#define DA7219_DAC_NG_OFF_THRESHOLD_MASK (0x7 << 0) +#define DA7219_DAC_NG_THRESHOLD_MAX 0x7 + +/* DA7219_DAC_NG_ON_THRESH = 0xB1 */ +#define DA7219_DAC_NG_ON_THRESHOLD_SHIFT 0 +#define DA7219_DAC_NG_ON_THRESHOLD_MASK (0x7 << 0) + +/* DA7219_DAC_NG_CTRL = 0xB2 */ +#define DA7219_DAC_NG_EN_SHIFT 7 +#define DA7219_DAC_NG_EN_MASK (0x1 << 7) + +/* DA7219_TONE_GEN_CFG1 = 0xB4 */ +#define DA7219_DTMF_REG_SHIFT 0 +#define DA7219_DTMF_REG_MASK (0xF << 0) +#define DA7219_DTMF_REG_MAX 16 +#define DA7219_DTMF_EN_SHIFT 4 +#define DA7219_DTMF_EN_MASK (0x1 << 4) +#define DA7219_START_STOPN_SHIFT 7 +#define DA7219_START_STOPN_MASK (0x1 << 7) + +/* DA7219_TONE_GEN_CFG2 = 0xB5 */ +#define DA7219_SWG_SEL_SHIFT 0 +#define DA7219_SWG_SEL_MASK (0x3 << 0) +#define DA7219_SWG_SEL_MAX 4 +#define DA7219_SWG_SEL_SRAMP (0x3 << 0) +#define DA7219_TONE_GEN_GAIN_SHIFT 4 +#define DA7219_TONE_GEN_GAIN_MASK (0xF << 4) +#define DA7219_TONE_GEN_GAIN_MAX 0xF +#define DA7219_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4) +#define DA7219_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4) + +/* DA7219_TONE_GEN_CYCLES = 0xB6 */ +#define DA7219_BEEP_CYCLES_SHIFT 0 +#define DA7219_BEEP_CYCLES_MASK (0x7 << 0) + +/* DA7219_TONE_GEN_FREQ1_L = 0xB7 */ +#define DA7219_FREQ1_L_SHIFT 0 +#define DA7219_FREQ1_L_MASK (0xFF << 0) +#define DA7219_FREQ_MAX 0xFFFF + +/* DA7219_TONE_GEN_FREQ1_U = 0xB8 */ +#define DA7219_FREQ1_U_SHIFT 0 +#define DA7219_FREQ1_U_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_FREQ2_L = 0xB9 */ +#define DA7219_FREQ2_L_SHIFT 0 +#define DA7219_FREQ2_L_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_FREQ2_U = 0xBA */ +#define DA7219_FREQ2_U_SHIFT 0 +#define DA7219_FREQ2_U_MASK (0xFF << 0) + +/* DA7219_TONE_GEN_ON_PER = 0xBB */ +#define DA7219_BEEP_ON_PER_SHIFT 0 +#define DA7219_BEEP_ON_PER_MASK (0x3F << 0) +#define DA7219_BEEP_ON_OFF_MAX 0x3F + +/* DA7219_TONE_GEN_OFF_PER = 0xBC */ +#define DA7219_BEEP_OFF_PER_SHIFT 0 +#define DA7219_BEEP_OFF_PER_MASK (0x3F << 0) + +/* DA7219_SYSTEM_STATUS = 0xE0 */ +#define DA7219_SC1_BUSY_SHIFT 0 +#define DA7219_SC1_BUSY_MASK (0x1 << 0) +#define DA7219_SC2_BUSY_SHIFT 1 +#define DA7219_SC2_BUSY_MASK (0x1 << 1) + +/* DA7219_SYSTEM_ACTIVE = 0xFD */ +#define DA7219_SYSTEM_ACTIVE_SHIFT 0 +#define DA7219_SYSTEM_ACTIVE_MASK (0x1 << 0) + + +/* + * General defines & data + */ + +/* Register inversion */ +#define DA7219_NO_INVERT 0 +#define DA7219_INVERT 1 + +/* Byte related defines */ +#define DA7219_BYTE_SHIFT 8 +#define DA7219_BYTE_MASK 0xFF + +/* PLL Output Frequencies */ +#define DA7219_PLL_FREQ_OUT_90316 90316800 +#define DA7219_PLL_FREQ_OUT_98304 98304000 + +/* PLL Frequency Dividers */ +#define DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL 1 +#define DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL 2 +#define DA7219_PLL_INDIV_9_TO_18_MHZ_VAL 4 +#define DA7219_PLL_INDIV_18_TO_36_MHZ_VAL 8 +#define DA7219_PLL_INDIV_36_TO_54_MHZ_VAL 16 + +/* SRM */ +#define DA7219_SRM_CHECK_RETRIES 8 + +/* System Controller */ +#define DA7219_SYS_STAT_CHECK_RETRIES 6 +#define DA7219_SYS_STAT_CHECK_DELAY 50 + +/* Power up/down Delays */ +#define DA7219_SETTLING_DELAY 40 +#define DA7219_MIN_GAIN_DELAY 30 +#define DA7219_MIC_PGA_BASE_DELAY 100 +#define DA7219_MIC_PGA_OFFSET_DELAY 40 + +enum da7219_clk_src { + DA7219_CLKSRC_MCLK = 0, + DA7219_CLKSRC_MCLK_SQR, +}; + +enum da7219_sys_clk { + DA7219_SYSCLK_MCLK = 0, + DA7219_SYSCLK_PLL, + DA7219_SYSCLK_PLL_SRM, +}; + +/* Regulators */ +enum da7219_supplies { + DA7219_SUPPLY_VDD = 0, + DA7219_SUPPLY_VDDMIC, + DA7219_SUPPLY_VDDIO, + DA7219_NUM_SUPPLIES, +}; + +struct da7219_aad_priv; + +/* Private data */ +struct da7219_priv { + struct snd_soc_component *component; + struct da7219_aad_priv *aad; + struct da7219_pdata *pdata; + + bool wakeup_source; + struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES]; + struct regmap *regmap; + struct mutex ctrl_lock; + struct mutex pll_lock; + +#ifdef CONFIG_COMMON_CLK + struct clk_hw dai_clks_hw[DA7219_DAI_NUM_CLKS]; + struct clk_hw_onecell_data *clk_hw_data; +#endif + struct clk_lookup *dai_clks_lookup[DA7219_DAI_NUM_CLKS]; + struct clk *dai_clks[DA7219_DAI_NUM_CLKS]; + + struct clk *mclk; + unsigned int mclk_rate; + int clk_src; + + bool master; + bool tdm_en; + bool alc_en; + bool micbias_on_event; + unsigned int mic_pga_delay; + u8 gain_ramp_ctrl; +}; + +int da7219_set_pll(struct snd_soc_component *component, int source, unsigned int fout); + +#endif /* __DA7219_H */ diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c new file mode 100644 index 0000000000..f8ca1afa8a --- /dev/null +++ b/sound/soc/codecs/da732x.c @@ -0,0 +1,1567 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * da732x.c --- Dialog DA732X ALSA SoC Audio Driver + * + * Copyright (C) 2012 Dialog Semiconductor GmbH + * + * Author: Michal Hajduk + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "da732x.h" +#include "da732x_reg.h" + + +struct da732x_priv { + struct regmap *regmap; + + unsigned int sysclk; + bool pll_en; +}; + +/* + * da732x register cache - default settings + */ +static const struct reg_default da732x_reg_cache[] = { + { DA732X_REG_REF1 , 0x02 }, + { DA732X_REG_BIAS_EN , 0x80 }, + { DA732X_REG_BIAS1 , 0x00 }, + { DA732X_REG_BIAS2 , 0x00 }, + { DA732X_REG_BIAS3 , 0x00 }, + { DA732X_REG_BIAS4 , 0x00 }, + { DA732X_REG_MICBIAS2 , 0x00 }, + { DA732X_REG_MICBIAS1 , 0x00 }, + { DA732X_REG_MICDET , 0x00 }, + { DA732X_REG_MIC1_PRE , 0x01 }, + { DA732X_REG_MIC1 , 0x40 }, + { DA732X_REG_MIC2_PRE , 0x01 }, + { DA732X_REG_MIC2 , 0x40 }, + { DA732X_REG_AUX1L , 0x75 }, + { DA732X_REG_AUX1R , 0x75 }, + { DA732X_REG_MIC3_PRE , 0x01 }, + { DA732X_REG_MIC3 , 0x40 }, + { DA732X_REG_INP_PINBIAS , 0x00 }, + { DA732X_REG_INP_ZC_EN , 0x00 }, + { DA732X_REG_INP_MUX , 0x50 }, + { DA732X_REG_HP_DET , 0x00 }, + { DA732X_REG_HPL_DAC_OFFSET , 0x00 }, + { DA732X_REG_HPL_DAC_OFF_CNTL , 0x00 }, + { DA732X_REG_HPL_OUT_OFFSET , 0x00 }, + { DA732X_REG_HPL , 0x40 }, + { DA732X_REG_HPL_VOL , 0x0F }, + { DA732X_REG_HPR_DAC_OFFSET , 0x00 }, + { DA732X_REG_HPR_DAC_OFF_CNTL , 0x00 }, + { DA732X_REG_HPR_OUT_OFFSET , 0x00 }, + { DA732X_REG_HPR , 0x40 }, + { DA732X_REG_HPR_VOL , 0x0F }, + { DA732X_REG_LIN2 , 0x4F }, + { DA732X_REG_LIN3 , 0x4F }, + { DA732X_REG_LIN4 , 0x4F }, + { DA732X_REG_OUT_ZC_EN , 0x00 }, + { DA732X_REG_HP_LIN1_GNDSEL , 0x00 }, + { DA732X_REG_CP_HP1 , 0x0C }, + { DA732X_REG_CP_HP2 , 0x03 }, + { DA732X_REG_CP_CTRL1 , 0x00 }, + { DA732X_REG_CP_CTRL2 , 0x99 }, + { DA732X_REG_CP_CTRL3 , 0x25 }, + { DA732X_REG_CP_LEVEL_MASK , 0x3F }, + { DA732X_REG_CP_DET , 0x00 }, + { DA732X_REG_CP_STATUS , 0x00 }, + { DA732X_REG_CP_THRESH1 , 0x00 }, + { DA732X_REG_CP_THRESH2 , 0x00 }, + { DA732X_REG_CP_THRESH3 , 0x00 }, + { DA732X_REG_CP_THRESH4 , 0x00 }, + { DA732X_REG_CP_THRESH5 , 0x00 }, + { DA732X_REG_CP_THRESH6 , 0x00 }, + { DA732X_REG_CP_THRESH7 , 0x00 }, + { DA732X_REG_CP_THRESH8 , 0x00 }, + { DA732X_REG_PLL_DIV_LO , 0x00 }, + { DA732X_REG_PLL_DIV_MID , 0x00 }, + { DA732X_REG_PLL_DIV_HI , 0x00 }, + { DA732X_REG_PLL_CTRL , 0x02 }, + { DA732X_REG_CLK_CTRL , 0xaa }, + { DA732X_REG_CLK_DSP , 0x07 }, + { DA732X_REG_CLK_EN1 , 0x00 }, + { DA732X_REG_CLK_EN2 , 0x00 }, + { DA732X_REG_CLK_EN3 , 0x00 }, + { DA732X_REG_CLK_EN4 , 0x00 }, + { DA732X_REG_CLK_EN5 , 0x00 }, + { DA732X_REG_AIF_MCLK , 0x00 }, + { DA732X_REG_AIFA1 , 0x02 }, + { DA732X_REG_AIFA2 , 0x00 }, + { DA732X_REG_AIFA3 , 0x08 }, + { DA732X_REG_AIFB1 , 0x02 }, + { DA732X_REG_AIFB2 , 0x00 }, + { DA732X_REG_AIFB3 , 0x08 }, + { DA732X_REG_PC_CTRL , 0xC0 }, + { DA732X_REG_DATA_ROUTE , 0x00 }, + { DA732X_REG_DSP_CTRL , 0x00 }, + { DA732X_REG_CIF_CTRL2 , 0x00 }, + { DA732X_REG_HANDSHAKE , 0x00 }, + { DA732X_REG_SPARE1_OUT , 0x00 }, + { DA732X_REG_SPARE2_OUT , 0x00 }, + { DA732X_REG_SPARE1_IN , 0x00 }, + { DA732X_REG_ADC1_PD , 0x00 }, + { DA732X_REG_ADC1_HPF , 0x00 }, + { DA732X_REG_ADC1_SEL , 0x00 }, + { DA732X_REG_ADC1_EQ12 , 0x00 }, + { DA732X_REG_ADC1_EQ34 , 0x00 }, + { DA732X_REG_ADC1_EQ5 , 0x00 }, + { DA732X_REG_ADC2_PD , 0x00 }, + { DA732X_REG_ADC2_HPF , 0x00 }, + { DA732X_REG_ADC2_SEL , 0x00 }, + { DA732X_REG_ADC2_EQ12 , 0x00 }, + { DA732X_REG_ADC2_EQ34 , 0x00 }, + { DA732X_REG_ADC2_EQ5 , 0x00 }, + { DA732X_REG_DAC1_HPF , 0x00 }, + { DA732X_REG_DAC1_L_VOL , 0x00 }, + { DA732X_REG_DAC1_R_VOL , 0x00 }, + { DA732X_REG_DAC1_SEL , 0x00 }, + { DA732X_REG_DAC1_SOFTMUTE , 0x00 }, + { DA732X_REG_DAC1_EQ12 , 0x00 }, + { DA732X_REG_DAC1_EQ34 , 0x00 }, + { DA732X_REG_DAC1_EQ5 , 0x00 }, + { DA732X_REG_DAC2_HPF , 0x00 }, + { DA732X_REG_DAC2_L_VOL , 0x00 }, + { DA732X_REG_DAC2_R_VOL , 0x00 }, + { DA732X_REG_DAC2_SEL , 0x00 }, + { DA732X_REG_DAC2_SOFTMUTE , 0x00 }, + { DA732X_REG_DAC2_EQ12 , 0x00 }, + { DA732X_REG_DAC2_EQ34 , 0x00 }, + { DA732X_REG_DAC2_EQ5 , 0x00 }, + { DA732X_REG_DAC3_HPF , 0x00 }, + { DA732X_REG_DAC3_VOL , 0x00 }, + { DA732X_REG_DAC3_SEL , 0x00 }, + { DA732X_REG_DAC3_SOFTMUTE , 0x00 }, + { DA732X_REG_DAC3_EQ12 , 0x00 }, + { DA732X_REG_DAC3_EQ34 , 0x00 }, + { DA732X_REG_DAC3_EQ5 , 0x00 }, + { DA732X_REG_BIQ_BYP , 0x00 }, + { DA732X_REG_DMA_CMD , 0x00 }, + { DA732X_REG_DMA_ADDR0 , 0x00 }, + { DA732X_REG_DMA_ADDR1 , 0x00 }, + { DA732X_REG_DMA_DATA0 , 0x00 }, + { DA732X_REG_DMA_DATA1 , 0x00 }, + { DA732X_REG_DMA_DATA2 , 0x00 }, + { DA732X_REG_DMA_DATA3 , 0x00 }, + { DA732X_REG_UNLOCK , 0x00 }, +}; + +static inline int da732x_get_input_div(struct snd_soc_component *component, int sysclk) +{ + int val; + + if (sysclk < DA732X_MCLK_10MHZ) { + val = DA732X_MCLK_VAL_0_10MHZ; + } else if ((sysclk >= DA732X_MCLK_10MHZ) && + (sysclk < DA732X_MCLK_20MHZ)) { + val = DA732X_MCLK_VAL_10_20MHZ; + } else if ((sysclk >= DA732X_MCLK_20MHZ) && + (sysclk < DA732X_MCLK_40MHZ)) { + val = DA732X_MCLK_VAL_20_40MHZ; + } else if ((sysclk >= DA732X_MCLK_40MHZ) && + (sysclk <= DA732X_MCLK_54MHZ)) { + val = DA732X_MCLK_VAL_40_54MHZ; + } else { + return -EINVAL; + } + + snd_soc_component_write(component, DA732X_REG_PLL_CTRL, val); + + return val; +} + +static void da732x_set_charge_pump(struct snd_soc_component *component, int state) +{ + switch (state) { + case DA732X_ENABLE_CP: + snd_soc_component_write(component, DA732X_REG_CLK_EN2, DA732X_CP_CLK_EN); + snd_soc_component_write(component, DA732X_REG_CP_HP2, DA732X_HP_CP_EN | + DA732X_HP_CP_REG | DA732X_HP_CP_PULSESKIP); + snd_soc_component_write(component, DA732X_REG_CP_CTRL1, DA732X_CP_EN | + DA732X_CP_CTRL_CPVDD1); + snd_soc_component_write(component, DA732X_REG_CP_CTRL2, + DA732X_CP_MANAGE_MAGNITUDE | DA732X_CP_BOOST); + snd_soc_component_write(component, DA732X_REG_CP_CTRL3, DA732X_CP_1MHZ); + break; + case DA732X_DISABLE_CP: + snd_soc_component_write(component, DA732X_REG_CLK_EN2, DA732X_CP_CLK_DIS); + snd_soc_component_write(component, DA732X_REG_CP_HP2, DA732X_HP_CP_DIS); + snd_soc_component_write(component, DA732X_REG_CP_CTRL1, DA723X_CP_DIS); + break; + default: + pr_err("Wrong charge pump state\n"); + break; + } +} + +static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, DA732X_MIC_PRE_VOL_DB_MIN, + DA732X_MIC_PRE_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(mic_pga_tlv, DA732X_MIC_VOL_DB_MIN, + DA732X_MIC_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(aux_pga_tlv, DA732X_AUX_VOL_DB_MIN, + DA732X_AUX_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(hp_pga_tlv, DA732X_HP_VOL_DB_MIN, + DA732X_AUX_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(lin2_pga_tlv, DA732X_LIN2_VOL_DB_MIN, + DA732X_LIN2_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(lin3_pga_tlv, DA732X_LIN3_VOL_DB_MIN, + DA732X_LIN3_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(lin4_pga_tlv, DA732X_LIN4_VOL_DB_MIN, + DA732X_LIN4_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(adc_pga_tlv, DA732X_ADC_VOL_DB_MIN, + DA732X_ADC_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(dac_pga_tlv, DA732X_DAC_VOL_DB_MIN, + DA732X_DAC_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(eq_band_pga_tlv, DA732X_EQ_BAND_VOL_DB_MIN, + DA732X_EQ_BAND_VOL_DB_INC, 0); + +static const DECLARE_TLV_DB_SCALE(eq_overall_tlv, DA732X_EQ_OVERALL_VOL_DB_MIN, + DA732X_EQ_OVERALL_VOL_DB_INC, 0); + +/* High Pass Filter */ +static const char *da732x_hpf_mode[] = { + "Disable", "Music", "Voice", +}; + +static const char *da732x_hpf_music[] = { + "1.8Hz", "3.75Hz", "7.5Hz", "15Hz", +}; + +static const char *da732x_hpf_voice[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", + "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static SOC_ENUM_SINGLE_DECL(da732x_dac1_hpf_mode_enum, + DA732X_REG_DAC1_HPF, DA732X_HPF_MODE_SHIFT, + da732x_hpf_mode); + +static SOC_ENUM_SINGLE_DECL(da732x_dac2_hpf_mode_enum, + DA732X_REG_DAC2_HPF, DA732X_HPF_MODE_SHIFT, + da732x_hpf_mode); + +static SOC_ENUM_SINGLE_DECL(da732x_dac3_hpf_mode_enum, + DA732X_REG_DAC3_HPF, DA732X_HPF_MODE_SHIFT, + da732x_hpf_mode); + +static SOC_ENUM_SINGLE_DECL(da732x_adc1_hpf_mode_enum, + DA732X_REG_ADC1_HPF, DA732X_HPF_MODE_SHIFT, + da732x_hpf_mode); + +static SOC_ENUM_SINGLE_DECL(da732x_adc2_hpf_mode_enum, + DA732X_REG_ADC2_HPF, DA732X_HPF_MODE_SHIFT, + da732x_hpf_mode); + +static SOC_ENUM_SINGLE_DECL(da732x_dac1_hp_filter_enum, + DA732X_REG_DAC1_HPF, DA732X_HPF_MUSIC_SHIFT, + da732x_hpf_music); + +static SOC_ENUM_SINGLE_DECL(da732x_dac2_hp_filter_enum, + DA732X_REG_DAC2_HPF, DA732X_HPF_MUSIC_SHIFT, + da732x_hpf_music); + +static SOC_ENUM_SINGLE_DECL(da732x_dac3_hp_filter_enum, + DA732X_REG_DAC3_HPF, DA732X_HPF_MUSIC_SHIFT, + da732x_hpf_music); + +static SOC_ENUM_SINGLE_DECL(da732x_adc1_hp_filter_enum, + DA732X_REG_ADC1_HPF, DA732X_HPF_MUSIC_SHIFT, + da732x_hpf_music); + +static SOC_ENUM_SINGLE_DECL(da732x_adc2_hp_filter_enum, + DA732X_REG_ADC2_HPF, DA732X_HPF_MUSIC_SHIFT, + da732x_hpf_music); + +static SOC_ENUM_SINGLE_DECL(da732x_dac1_voice_filter_enum, + DA732X_REG_DAC1_HPF, DA732X_HPF_VOICE_SHIFT, + da732x_hpf_voice); + +static SOC_ENUM_SINGLE_DECL(da732x_dac2_voice_filter_enum, + DA732X_REG_DAC2_HPF, DA732X_HPF_VOICE_SHIFT, + da732x_hpf_voice); + +static SOC_ENUM_SINGLE_DECL(da732x_dac3_voice_filter_enum, + DA732X_REG_DAC3_HPF, DA732X_HPF_VOICE_SHIFT, + da732x_hpf_voice); + +static SOC_ENUM_SINGLE_DECL(da732x_adc1_voice_filter_enum, + DA732X_REG_ADC1_HPF, DA732X_HPF_VOICE_SHIFT, + da732x_hpf_voice); + +static SOC_ENUM_SINGLE_DECL(da732x_adc2_voice_filter_enum, + DA732X_REG_ADC2_HPF, DA732X_HPF_VOICE_SHIFT, + da732x_hpf_voice); + +static int da732x_hpf_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value; + unsigned int reg = enum_ctrl->reg; + unsigned int sel = ucontrol->value.enumerated.item[0]; + unsigned int bits; + + switch (sel) { + case DA732X_HPF_DISABLED: + bits = DA732X_HPF_DIS; + break; + case DA732X_HPF_VOICE: + bits = DA732X_HPF_VOICE_EN; + break; + case DA732X_HPF_MUSIC: + bits = DA732X_HPF_MUSIC_EN; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, reg, DA732X_HPF_MASK, bits); + + return 0; +} + +static int da732x_hpf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value; + unsigned int reg = enum_ctrl->reg; + int val; + + val = snd_soc_component_read(component, reg) & DA732X_HPF_MASK; + + switch (val) { + case DA732X_HPF_VOICE_EN: + ucontrol->value.enumerated.item[0] = DA732X_HPF_VOICE; + break; + case DA732X_HPF_MUSIC_EN: + ucontrol->value.enumerated.item[0] = DA732X_HPF_MUSIC; + break; + default: + ucontrol->value.enumerated.item[0] = DA732X_HPF_DISABLED; + break; + } + + return 0; +} + +static const struct snd_kcontrol_new da732x_snd_controls[] = { + /* Input PGAs */ + SOC_SINGLE_RANGE_TLV("MIC1 Boost Volume", DA732X_REG_MIC1_PRE, + DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN, + DA732X_MICBOOST_MAX, 0, mic_boost_tlv), + SOC_SINGLE_RANGE_TLV("MIC2 Boost Volume", DA732X_REG_MIC2_PRE, + DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN, + DA732X_MICBOOST_MAX, 0, mic_boost_tlv), + SOC_SINGLE_RANGE_TLV("MIC3 Boost Volume", DA732X_REG_MIC3_PRE, + DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN, + DA732X_MICBOOST_MAX, 0, mic_boost_tlv), + + /* MICs */ + SOC_SINGLE("MIC1 Switch", DA732X_REG_MIC1, DA732X_MIC_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_RANGE_TLV("MIC1 Volume", DA732X_REG_MIC1, + DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN, + DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv), + SOC_SINGLE("MIC2 Switch", DA732X_REG_MIC2, DA732X_MIC_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_RANGE_TLV("MIC2 Volume", DA732X_REG_MIC2, + DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN, + DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv), + SOC_SINGLE("MIC3 Switch", DA732X_REG_MIC3, DA732X_MIC_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_RANGE_TLV("MIC3 Volume", DA732X_REG_MIC3, + DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN, + DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv), + + /* AUXs */ + SOC_SINGLE("AUX1L Switch", DA732X_REG_AUX1L, DA732X_AUX_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("AUX1L Volume", DA732X_REG_AUX1L, + DA732X_AUX_VOL_SHIFT, DA732X_AUX_VOL_VAL_MAX, + DA732X_NO_INVERT, aux_pga_tlv), + SOC_SINGLE("AUX1R Switch", DA732X_REG_AUX1R, DA732X_AUX_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("AUX1R Volume", DA732X_REG_AUX1R, + DA732X_AUX_VOL_SHIFT, DA732X_AUX_VOL_VAL_MAX, + DA732X_NO_INVERT, aux_pga_tlv), + + /* ADCs */ + SOC_DOUBLE_TLV("ADC1 Volume", DA732X_REG_ADC1_SEL, + DA732X_ADCL_VOL_SHIFT, DA732X_ADCR_VOL_SHIFT, + DA732X_ADC_VOL_VAL_MAX, DA732X_INVERT, adc_pga_tlv), + + SOC_DOUBLE_TLV("ADC2 Volume", DA732X_REG_ADC2_SEL, + DA732X_ADCL_VOL_SHIFT, DA732X_ADCR_VOL_SHIFT, + DA732X_ADC_VOL_VAL_MAX, DA732X_INVERT, adc_pga_tlv), + + /* DACs */ + SOC_DOUBLE("Digital Playback DAC12 Switch", DA732X_REG_DAC1_SEL, + DA732X_DACL_MUTE_SHIFT, DA732X_DACR_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_DOUBLE_R_TLV("Digital Playback DAC12 Volume", DA732X_REG_DAC1_L_VOL, + DA732X_REG_DAC1_R_VOL, DA732X_DAC_VOL_SHIFT, + DA732X_DAC_VOL_VAL_MAX, DA732X_INVERT, dac_pga_tlv), + SOC_SINGLE("Digital Playback DAC3 Switch", DA732X_REG_DAC2_SEL, + DA732X_DACL_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Digital Playback DAC3 Volume", DA732X_REG_DAC2_L_VOL, + DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX, + DA732X_INVERT, dac_pga_tlv), + SOC_SINGLE("Digital Playback DAC4 Switch", DA732X_REG_DAC2_SEL, + DA732X_DACR_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Digital Playback DAC4 Volume", DA732X_REG_DAC2_R_VOL, + DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX, + DA732X_INVERT, dac_pga_tlv), + SOC_SINGLE("Digital Playback DAC5 Switch", DA732X_REG_DAC3_SEL, + DA732X_DACL_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Digital Playback DAC5 Volume", DA732X_REG_DAC3_VOL, + DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX, + DA732X_INVERT, dac_pga_tlv), + + /* High Pass Filters */ + SOC_ENUM_EXT("DAC1 High Pass Filter Mode", + da732x_dac1_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set), + SOC_ENUM("DAC1 High Pass Filter", da732x_dac1_hp_filter_enum), + SOC_ENUM("DAC1 Voice Filter", da732x_dac1_voice_filter_enum), + + SOC_ENUM_EXT("DAC2 High Pass Filter Mode", + da732x_dac2_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set), + SOC_ENUM("DAC2 High Pass Filter", da732x_dac2_hp_filter_enum), + SOC_ENUM("DAC2 Voice Filter", da732x_dac2_voice_filter_enum), + + SOC_ENUM_EXT("DAC3 High Pass Filter Mode", + da732x_dac3_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set), + SOC_ENUM("DAC3 High Pass Filter", da732x_dac3_hp_filter_enum), + SOC_ENUM("DAC3 Filter Mode", da732x_dac3_voice_filter_enum), + + SOC_ENUM_EXT("ADC1 High Pass Filter Mode", + da732x_adc1_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set), + SOC_ENUM("ADC1 High Pass Filter", da732x_adc1_hp_filter_enum), + SOC_ENUM("ADC1 Voice Filter", da732x_adc1_voice_filter_enum), + + SOC_ENUM_EXT("ADC2 High Pass Filter Mode", + da732x_adc2_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set), + SOC_ENUM("ADC2 High Pass Filter", da732x_adc2_hp_filter_enum), + SOC_ENUM("ADC2 Voice Filter", da732x_adc2_voice_filter_enum), + + /* Equalizers */ + SOC_SINGLE("ADC1 EQ Switch", DA732X_REG_ADC1_EQ5, + DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT), + SOC_SINGLE_TLV("ADC1 EQ Band 1 Volume", DA732X_REG_ADC1_EQ12, + DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC1 EQ Band 2 Volume", DA732X_REG_ADC1_EQ12, + DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC1 EQ Band 3 Volume", DA732X_REG_ADC1_EQ34, + DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC1 EQ Band 4 Volume", DA732X_REG_ADC1_EQ34, + DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC1 EQ Band 5 Volume", DA732X_REG_ADC1_EQ5, + DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC1 EQ Overall Volume", DA732X_REG_ADC1_EQ5, + DA732X_EQ_OVERALL_SHIFT, DA732X_EQ_OVERALL_VOL_VAL_MAX, + DA732X_INVERT, eq_overall_tlv), + + SOC_SINGLE("ADC2 EQ Switch", DA732X_REG_ADC2_EQ5, + DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT), + SOC_SINGLE_TLV("ADC2 EQ Band 1 Volume", DA732X_REG_ADC2_EQ12, + DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC2 EQ Band 2 Volume", DA732X_REG_ADC2_EQ12, + DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC2 EQ Band 3 Volume", DA732X_REG_ADC2_EQ34, + DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ACD2 EQ Band 4 Volume", DA732X_REG_ADC2_EQ34, + DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ACD2 EQ Band 5 Volume", DA732X_REG_ADC2_EQ5, + DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("ADC2 EQ Overall Volume", DA732X_REG_ADC1_EQ5, + DA732X_EQ_OVERALL_SHIFT, DA732X_EQ_OVERALL_VOL_VAL_MAX, + DA732X_INVERT, eq_overall_tlv), + + SOC_SINGLE("DAC1 EQ Switch", DA732X_REG_DAC1_EQ5, + DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT), + SOC_SINGLE_TLV("DAC1 EQ Band 1 Volume", DA732X_REG_DAC1_EQ12, + DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC1 EQ Band 2 Volume", DA732X_REG_DAC1_EQ12, + DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC1 EQ Band 3 Volume", DA732X_REG_DAC1_EQ34, + DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC1 EQ Band 4 Volume", DA732X_REG_DAC1_EQ34, + DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC1 EQ Band 5 Volume", DA732X_REG_DAC1_EQ5, + DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + + SOC_SINGLE("DAC2 EQ Switch", DA732X_REG_DAC2_EQ5, + DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT), + SOC_SINGLE_TLV("DAC2 EQ Band 1 Volume", DA732X_REG_DAC2_EQ12, + DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC2 EQ Band 2 Volume", DA732X_REG_DAC2_EQ12, + DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC2 EQ Band 3 Volume", DA732X_REG_DAC2_EQ34, + DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC2 EQ Band 4 Volume", DA732X_REG_DAC2_EQ34, + DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC2 EQ Band 5 Volume", DA732X_REG_DAC2_EQ5, + DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + + SOC_SINGLE("DAC3 EQ Switch", DA732X_REG_DAC3_EQ5, + DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT), + SOC_SINGLE_TLV("DAC3 EQ Band 1 Volume", DA732X_REG_DAC3_EQ12, + DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC3 EQ Band 2 Volume", DA732X_REG_DAC3_EQ12, + DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC3 EQ Band 3 Volume", DA732X_REG_DAC3_EQ34, + DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC3 EQ Band 4 Volume", DA732X_REG_DAC3_EQ34, + DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + SOC_SINGLE_TLV("DAC3 EQ Band 5 Volume", DA732X_REG_DAC3_EQ5, + DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX, + DA732X_INVERT, eq_band_pga_tlv), + + /* Lineout 2 Reciever*/ + SOC_SINGLE("Lineout 2 Switch", DA732X_REG_LIN2, DA732X_LOUT_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Lineout 2 Volume", DA732X_REG_LIN2, + DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX, + DA732X_NO_INVERT, lin2_pga_tlv), + + /* Lineout 3 SPEAKER*/ + SOC_SINGLE("Lineout 3 Switch", DA732X_REG_LIN3, DA732X_LOUT_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Lineout 3 Volume", DA732X_REG_LIN3, + DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX, + DA732X_NO_INVERT, lin3_pga_tlv), + + /* Lineout 4 */ + SOC_SINGLE("Lineout 4 Switch", DA732X_REG_LIN4, DA732X_LOUT_MUTE_SHIFT, + DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_SINGLE_TLV("Lineout 4 Volume", DA732X_REG_LIN4, + DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX, + DA732X_NO_INVERT, lin4_pga_tlv), + + /* Headphones */ + SOC_DOUBLE_R("Headphone Switch", DA732X_REG_HPR, DA732X_REG_HPL, + DA732X_HP_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT), + SOC_DOUBLE_R_TLV("Headphone Volume", DA732X_REG_HPL_VOL, + DA732X_REG_HPR_VOL, DA732X_HP_VOL_SHIFT, + DA732X_HP_VOL_VAL_MAX, DA732X_NO_INVERT, hp_pga_tlv), +}; + +static int da732x_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + switch (w->reg) { + case DA732X_REG_ADC1_PD: + snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3, + DA732X_ADCA_BB_CLK_EN, + DA732X_ADCA_BB_CLK_EN); + break; + case DA732X_REG_ADC2_PD: + snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3, + DA732X_ADCC_BB_CLK_EN, + DA732X_ADCC_BB_CLK_EN); + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, w->reg, DA732X_ADC_RST_MASK, + DA732X_ADC_SET_ACT); + snd_soc_component_update_bits(component, w->reg, DA732X_ADC_PD_MASK, + DA732X_ADC_ON); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, w->reg, DA732X_ADC_PD_MASK, + DA732X_ADC_OFF); + snd_soc_component_update_bits(component, w->reg, DA732X_ADC_RST_MASK, + DA732X_ADC_SET_RST); + + switch (w->reg) { + case DA732X_REG_ADC1_PD: + snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3, + DA732X_ADCA_BB_CLK_EN, 0); + break; + case DA732X_REG_ADC2_PD: + snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3, + DA732X_ADCC_BB_CLK_EN, 0); + break; + default: + return -EINVAL; + } + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int da732x_out_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, w->reg, + (1 << w->shift) | DA732X_OUT_HIZ_EN, + (1 << w->shift) | DA732X_OUT_HIZ_EN); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, w->reg, + (1 << w->shift) | DA732X_OUT_HIZ_EN, + (1 << w->shift) | DA732X_OUT_HIZ_DIS); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const char *adcl_text[] = { + "AUX1L", "MIC1" +}; + +static const char *adcr_text[] = { + "AUX1R", "MIC2", "MIC3" +}; + +static const char *enable_text[] = { + "Disabled", + "Enabled" +}; + +/* ADC1LMUX */ +static SOC_ENUM_SINGLE_DECL(adc1l_enum, + DA732X_REG_INP_MUX, DA732X_ADC1L_MUX_SEL_SHIFT, + adcl_text); +static const struct snd_kcontrol_new adc1l_mux = + SOC_DAPM_ENUM("ADC Route", adc1l_enum); + +/* ADC1RMUX */ +static SOC_ENUM_SINGLE_DECL(adc1r_enum, + DA732X_REG_INP_MUX, DA732X_ADC1R_MUX_SEL_SHIFT, + adcr_text); +static const struct snd_kcontrol_new adc1r_mux = + SOC_DAPM_ENUM("ADC Route", adc1r_enum); + +/* ADC2LMUX */ +static SOC_ENUM_SINGLE_DECL(adc2l_enum, + DA732X_REG_INP_MUX, DA732X_ADC2L_MUX_SEL_SHIFT, + adcl_text); +static const struct snd_kcontrol_new adc2l_mux = + SOC_DAPM_ENUM("ADC Route", adc2l_enum); + +/* ADC2RMUX */ +static SOC_ENUM_SINGLE_DECL(adc2r_enum, + DA732X_REG_INP_MUX, DA732X_ADC2R_MUX_SEL_SHIFT, + adcr_text); + +static const struct snd_kcontrol_new adc2r_mux = + SOC_DAPM_ENUM("ADC Route", adc2r_enum); + +static SOC_ENUM_SINGLE_DECL(da732x_hp_left_output, + DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN_SHIFT, + enable_text); + +static const struct snd_kcontrol_new hpl_mux = + SOC_DAPM_ENUM("HPL Switch", da732x_hp_left_output); + +static SOC_ENUM_SINGLE_DECL(da732x_hp_right_output, + DA732X_REG_HPR, DA732X_HP_OUT_DAC_EN_SHIFT, + enable_text); + +static const struct snd_kcontrol_new hpr_mux = + SOC_DAPM_ENUM("HPR Switch", da732x_hp_right_output); + +static SOC_ENUM_SINGLE_DECL(da732x_speaker_output, + DA732X_REG_LIN3, DA732X_LOUT_DAC_EN_SHIFT, + enable_text); + +static const struct snd_kcontrol_new spk_mux = + SOC_DAPM_ENUM("SPK Switch", da732x_speaker_output); + +static SOC_ENUM_SINGLE_DECL(da732x_lout4_output, + DA732X_REG_LIN4, DA732X_LOUT_DAC_EN_SHIFT, + enable_text); + +static const struct snd_kcontrol_new lout4_mux = + SOC_DAPM_ENUM("LOUT4 Switch", da732x_lout4_output); + +static SOC_ENUM_SINGLE_DECL(da732x_lout2_output, + DA732X_REG_LIN2, DA732X_LOUT_DAC_EN_SHIFT, + enable_text); + +static const struct snd_kcontrol_new lout2_mux = + SOC_DAPM_ENUM("LOUT2 Switch", da732x_lout2_output); + +static const struct snd_soc_dapm_widget da732x_dapm_widgets[] = { + /* Supplies */ + SND_SOC_DAPM_SUPPLY("ADC1 Supply", DA732X_REG_ADC1_PD, 0, + DA732X_NO_INVERT, da732x_adc_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("ADC2 Supply", DA732X_REG_ADC2_PD, 0, + DA732X_NO_INVERT, da732x_adc_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DAC1 CLK", DA732X_REG_CLK_EN4, + DA732X_DACA_BB_CLK_SHIFT, DA732X_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC2 CLK", DA732X_REG_CLK_EN4, + DA732X_DACC_BB_CLK_SHIFT, DA732X_NO_INVERT, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC3 CLK", DA732X_REG_CLK_EN5, + DA732X_DACE_BB_CLK_SHIFT, DA732X_NO_INVERT, + NULL, 0), + + /* Micbias */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", DA732X_REG_MICBIAS1, + DA732X_MICBIAS_EN_SHIFT, + DA732X_NO_INVERT, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", DA732X_REG_MICBIAS2, + DA732X_MICBIAS_EN_SHIFT, + DA732X_NO_INVERT, NULL, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("MIC3"), + SND_SOC_DAPM_INPUT("AUX1L"), + SND_SOC_DAPM_INPUT("AUX1R"), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("ClassD"), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1L", NULL, DA732X_REG_ADC1_SEL, + DA732X_ADCL_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_ADC("ADC1R", NULL, DA732X_REG_ADC1_SEL, + DA732X_ADCR_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_ADC("ADC2L", NULL, DA732X_REG_ADC2_SEL, + DA732X_ADCL_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_ADC("ADC2R", NULL, DA732X_REG_ADC2_SEL, + DA732X_ADCR_EN_SHIFT, DA732X_NO_INVERT), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC1L", NULL, DA732X_REG_DAC1_SEL, + DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_DAC("DAC1R", NULL, DA732X_REG_DAC1_SEL, + DA732X_DACR_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_DAC("DAC2L", NULL, DA732X_REG_DAC2_SEL, + DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_DAC("DAC2R", NULL, DA732X_REG_DAC2_SEL, + DA732X_DACR_EN_SHIFT, DA732X_NO_INVERT), + SND_SOC_DAPM_DAC("DAC3", NULL, DA732X_REG_DAC3_SEL, + DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT), + + /* Input Pgas */ + SND_SOC_DAPM_PGA("MIC1 PGA", DA732X_REG_MIC1, DA732X_MIC_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC2 PGA", DA732X_REG_MIC2, DA732X_MIC_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC3 PGA", DA732X_REG_MIC3, DA732X_MIC_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("AUX1L PGA", DA732X_REG_AUX1L, DA732X_AUX_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("AUX1R PGA", DA732X_REG_AUX1R, DA732X_AUX_EN_SHIFT, + 0, NULL, 0), + + SND_SOC_DAPM_PGA_E("HP Left", DA732X_REG_HPL, DA732X_HP_OUT_EN_SHIFT, + 0, NULL, 0, da732x_out_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HP Right", DA732X_REG_HPR, DA732X_HP_OUT_EN_SHIFT, + 0, NULL, 0, da732x_out_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LIN2", DA732X_REG_LIN2, DA732X_LIN_OUT_EN_SHIFT, + 0, NULL, 0, da732x_out_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LIN3", DA732X_REG_LIN3, DA732X_LIN_OUT_EN_SHIFT, + 0, NULL, 0, da732x_out_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LIN4", DA732X_REG_LIN4, DA732X_LIN_OUT_EN_SHIFT, + 0, NULL, 0, da732x_out_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* MUXs */ + SND_SOC_DAPM_MUX("ADC1 Left MUX", SND_SOC_NOPM, 0, 0, &adc1l_mux), + SND_SOC_DAPM_MUX("ADC1 Right MUX", SND_SOC_NOPM, 0, 0, &adc1r_mux), + SND_SOC_DAPM_MUX("ADC2 Left MUX", SND_SOC_NOPM, 0, 0, &adc2l_mux), + SND_SOC_DAPM_MUX("ADC2 Right MUX", SND_SOC_NOPM, 0, 0, &adc2r_mux), + + SND_SOC_DAPM_MUX("HP Left MUX", SND_SOC_NOPM, 0, 0, &hpl_mux), + SND_SOC_DAPM_MUX("HP Right MUX", SND_SOC_NOPM, 0, 0, &hpr_mux), + SND_SOC_DAPM_MUX("Speaker MUX", SND_SOC_NOPM, 0, 0, &spk_mux), + SND_SOC_DAPM_MUX("LOUT2 MUX", SND_SOC_NOPM, 0, 0, &lout2_mux), + SND_SOC_DAPM_MUX("LOUT4 MUX", SND_SOC_NOPM, 0, 0, &lout4_mux), + + /* AIF interfaces */ + SND_SOC_DAPM_AIF_OUT("AIFA Output", "AIFA Capture", 0, DA732X_REG_AIFA3, + DA732X_AIF_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("AIFA Input", "AIFA Playback", 0, DA732X_REG_AIFA3, + DA732X_AIF_EN_SHIFT, 0), + + SND_SOC_DAPM_AIF_OUT("AIFB Output", "AIFB Capture", 0, DA732X_REG_AIFB3, + DA732X_AIF_EN_SHIFT, 0), + SND_SOC_DAPM_AIF_IN("AIFB Input", "AIFB Playback", 0, DA732X_REG_AIFB3, + DA732X_AIF_EN_SHIFT, 0), +}; + +static const struct snd_soc_dapm_route da732x_dapm_routes[] = { + /* Inputs */ + {"AUX1L PGA", NULL, "AUX1L"}, + {"AUX1R PGA", NULL, "AUX1R"}, + {"MIC1 PGA", NULL, "MIC1"}, + {"MIC2 PGA", NULL, "MIC2"}, + {"MIC3 PGA", NULL, "MIC3"}, + + /* Capture Path */ + {"ADC1 Left MUX", "MIC1", "MIC1 PGA"}, + {"ADC1 Left MUX", "AUX1L", "AUX1L PGA"}, + + {"ADC1 Right MUX", "AUX1R", "AUX1R PGA"}, + {"ADC1 Right MUX", "MIC2", "MIC2 PGA"}, + {"ADC1 Right MUX", "MIC3", "MIC3 PGA"}, + + {"ADC2 Left MUX", "AUX1L", "AUX1L PGA"}, + {"ADC2 Left MUX", "MIC1", "MIC1 PGA"}, + + {"ADC2 Right MUX", "AUX1R", "AUX1R PGA"}, + {"ADC2 Right MUX", "MIC2", "MIC2 PGA"}, + {"ADC2 Right MUX", "MIC3", "MIC3 PGA"}, + + {"ADC1L", NULL, "ADC1 Supply"}, + {"ADC1R", NULL, "ADC1 Supply"}, + {"ADC2L", NULL, "ADC2 Supply"}, + {"ADC2R", NULL, "ADC2 Supply"}, + + {"ADC1L", NULL, "ADC1 Left MUX"}, + {"ADC1R", NULL, "ADC1 Right MUX"}, + {"ADC2L", NULL, "ADC2 Left MUX"}, + {"ADC2R", NULL, "ADC2 Right MUX"}, + + {"AIFA Output", NULL, "ADC1L"}, + {"AIFA Output", NULL, "ADC1R"}, + {"AIFB Output", NULL, "ADC2L"}, + {"AIFB Output", NULL, "ADC2R"}, + + {"HP Left MUX", "Enabled", "AIFA Input"}, + {"HP Right MUX", "Enabled", "AIFA Input"}, + {"Speaker MUX", "Enabled", "AIFB Input"}, + {"LOUT2 MUX", "Enabled", "AIFB Input"}, + {"LOUT4 MUX", "Enabled", "AIFB Input"}, + + {"DAC1L", NULL, "DAC1 CLK"}, + {"DAC1R", NULL, "DAC1 CLK"}, + {"DAC2L", NULL, "DAC2 CLK"}, + {"DAC2R", NULL, "DAC2 CLK"}, + {"DAC3", NULL, "DAC3 CLK"}, + + {"DAC1L", NULL, "HP Left MUX"}, + {"DAC1R", NULL, "HP Right MUX"}, + {"DAC2L", NULL, "Speaker MUX"}, + {"DAC2R", NULL, "LOUT4 MUX"}, + {"DAC3", NULL, "LOUT2 MUX"}, + + /* Output Pgas */ + {"HP Left", NULL, "DAC1L"}, + {"HP Right", NULL, "DAC1R"}, + {"LIN3", NULL, "DAC2L"}, + {"LIN4", NULL, "DAC2R"}, + {"LIN2", NULL, "DAC3"}, + + /* Outputs */ + {"ClassD", NULL, "LIN3"}, + {"LOUTL", NULL, "LIN2"}, + {"LOUTR", NULL, "LIN4"}, + {"HPL", NULL, "HP Left"}, + {"HPR", NULL, "HP Right"}, +}; + +static int da732x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u32 aif = 0; + u32 reg_aif; + u32 fs; + + reg_aif = dai->driver->base; + + switch (params_width(params)) { + case 16: + aif |= DA732X_AIF_WORD_16; + break; + case 20: + aif |= DA732X_AIF_WORD_20; + break; + case 24: + aif |= DA732X_AIF_WORD_24; + break; + case 32: + aif |= DA732X_AIF_WORD_32; + break; + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 8000: + fs = DA732X_SR_8KHZ; + break; + case 11025: + fs = DA732X_SR_11_025KHZ; + break; + case 12000: + fs = DA732X_SR_12KHZ; + break; + case 16000: + fs = DA732X_SR_16KHZ; + break; + case 22050: + fs = DA732X_SR_22_05KHZ; + break; + case 24000: + fs = DA732X_SR_24KHZ; + break; + case 32000: + fs = DA732X_SR_32KHZ; + break; + case 44100: + fs = DA732X_SR_44_1KHZ; + break; + case 48000: + fs = DA732X_SR_48KHZ; + break; + case 88100: + fs = DA732X_SR_88_1KHZ; + break; + case 96000: + fs = DA732X_SR_96KHZ; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, reg_aif, DA732X_AIF_WORD_MASK, aif); + snd_soc_component_update_bits(component, DA732X_REG_CLK_CTRL, DA732X_SR1_MASK, fs); + + return 0; +} + +static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt) +{ + struct snd_soc_component *component = dai->component; + u32 aif_mclk, pc_count; + u32 reg_aif1, aif1; + u32 reg_aif3, aif3; + + switch (dai->id) { + case DA732X_DAI_ID1: + reg_aif1 = DA732X_REG_AIFA1; + reg_aif3 = DA732X_REG_AIFA3; + pc_count = DA732X_PC_PULSE_AIFA | DA732X_PC_RESYNC_NOT_AUT | + DA732X_PC_SAME; + break; + case DA732X_DAI_ID2: + reg_aif1 = DA732X_REG_AIFB1; + reg_aif3 = DA732X_REG_AIFB3; + pc_count = DA732X_PC_PULSE_AIFB | DA732X_PC_RESYNC_NOT_AUT | + DA732X_PC_SAME; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + aif1 = DA732X_AIF_SLAVE; + aif_mclk = DA732X_AIFM_FRAME_64 | DA732X_AIFM_SRC_SEL_AIFA; + break; + case SND_SOC_DAIFMT_CBM_CFM: + aif1 = DA732X_AIF_CLK_FROM_SRC; + aif_mclk = DA732X_CLK_GENERATION_AIF_A; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + aif3 = DA732X_AIF_I2S_MODE; + break; + case SND_SOC_DAIFMT_RIGHT_J: + aif3 = DA732X_AIF_RIGHT_J_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + aif3 = DA732X_AIF_LEFT_J_MODE; + break; + case SND_SOC_DAIFMT_DSP_B: + aif3 = DA732X_AIF_DSP_MODE; + break; + default: + return -EINVAL; + } + + /* Clock inversion */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif3 |= DA732X_AIF_BCLK_INV; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif3 |= DA732X_AIF_BCLK_INV | DA732X_AIF_WCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif3 |= DA732X_AIF_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif3 |= DA732X_AIF_WCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, DA732X_REG_AIF_MCLK, aif_mclk); + snd_soc_component_update_bits(component, reg_aif1, DA732X_AIF1_CLK_MASK, aif1); + snd_soc_component_update_bits(component, reg_aif3, DA732X_AIF_BCLK_INV | + DA732X_AIF_WCLK_INV | DA732X_AIF_MODE_MASK, aif3); + snd_soc_component_write(component, DA732X_REG_PC_CTRL, pc_count); + + return 0; +} + + + +static int da732x_set_dai_pll(struct snd_soc_component *component, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct da732x_priv *da732x = snd_soc_component_get_drvdata(component); + int fref, indiv; + u8 div_lo, div_mid, div_hi; + u64 frac_div; + + /* Disable PLL */ + if (freq_out == 0) { + snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL, + DA732X_PLL_EN, 0); + da732x->pll_en = false; + return 0; + } + + if (da732x->pll_en) + return -EBUSY; + + if (source == DA732X_SRCCLK_MCLK) { + /* Validate Sysclk rate */ + switch (da732x->sysclk) { + case 11290000: + case 12288000: + case 22580000: + case 24576000: + case 45160000: + case 49152000: + snd_soc_component_write(component, DA732X_REG_PLL_CTRL, + DA732X_PLL_BYPASS); + return 0; + default: + dev_err(component->dev, + "Cannot use PLL Bypass, invalid SYSCLK rate\n"); + return -EINVAL; + } + } + + indiv = da732x_get_input_div(component, da732x->sysclk); + if (indiv < 0) + return indiv; + + fref = da732x->sysclk / BIT(indiv); + div_hi = freq_out / fref; + frac_div = (u64)(freq_out % fref) * 8192ULL; + do_div(frac_div, fref); + div_mid = (frac_div >> DA732X_1BYTE_SHIFT) & DA732X_U8_MASK; + div_lo = (frac_div) & DA732X_U8_MASK; + + snd_soc_component_write(component, DA732X_REG_PLL_DIV_LO, div_lo); + snd_soc_component_write(component, DA732X_REG_PLL_DIV_MID, div_mid); + snd_soc_component_write(component, DA732X_REG_PLL_DIV_HI, div_hi); + + snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL, DA732X_PLL_EN, + DA732X_PLL_EN); + + da732x->pll_en = true; + + return 0; +} + +static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct da732x_priv *da732x = snd_soc_component_get_drvdata(component); + + da732x->sysclk = freq; + + return 0; +} + +#define DA732X_RATES SNDRV_PCM_RATE_8000_96000 + +#define DA732X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops da732x_dai_ops = { + .hw_params = da732x_hw_params, + .set_fmt = da732x_set_dai_fmt, + .set_sysclk = da732x_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver da732x_dai[] = { + { + .name = "DA732X_AIFA", + .id = DA732X_DAI_ID1, + .base = DA732X_REG_AIFA1, + .playback = { + .stream_name = "AIFA Playback", + .channels_min = 1, + .channels_max = 2, + .rates = DA732X_RATES, + .formats = DA732X_FORMATS, + }, + .capture = { + .stream_name = "AIFA Capture", + .channels_min = 1, + .channels_max = 2, + .rates = DA732X_RATES, + .formats = DA732X_FORMATS, + }, + .ops = &da732x_dai_ops, + }, + { + .name = "DA732X_AIFB", + .id = DA732X_DAI_ID2, + .base = DA732X_REG_AIFB1, + .playback = { + .stream_name = "AIFB Playback", + .channels_min = 1, + .channels_max = 2, + .rates = DA732X_RATES, + .formats = DA732X_FORMATS, + }, + .capture = { + .stream_name = "AIFB Capture", + .channels_min = 1, + .channels_max = 2, + .rates = DA732X_RATES, + .formats = DA732X_FORMATS, + }, + .ops = &da732x_dai_ops, + }, +}; + +static bool da732x_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA732X_REG_HPL_DAC_OFF_CNTL: + case DA732X_REG_HPR_DAC_OFF_CNTL: + return true; + default: + return false; + } +} + +static const struct regmap_config da732x_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DA732X_MAX_REG, + .volatile_reg = da732x_volatile, + .reg_defaults = da732x_reg_cache, + .num_reg_defaults = ARRAY_SIZE(da732x_reg_cache), + .cache_type = REGCACHE_RBTREE, +}; + + +static void da732x_dac_offset_adjust(struct snd_soc_component *component) +{ + u8 offset[DA732X_HP_DACS]; + u8 sign[DA732X_HP_DACS]; + u8 step = DA732X_DAC_OFFSET_STEP; + + /* Initialize DAC offset calibration circuits and registers */ + snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET, + DA732X_HP_DAC_OFFSET_TRIM_VAL); + snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET, + DA732X_HP_DAC_OFFSET_TRIM_VAL); + snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFF_CNTL, + DA732X_HP_DAC_OFF_CALIBRATION | + DA732X_HP_DAC_OFF_SCALE_STEPS); + snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFF_CNTL, + DA732X_HP_DAC_OFF_CALIBRATION | + DA732X_HP_DAC_OFF_SCALE_STEPS); + + /* Wait for voltage stabilization */ + msleep(DA732X_WAIT_FOR_STABILIZATION); + + /* Check DAC offset sign */ + sign[DA732X_HPL_DAC] = (snd_soc_component_read(component, DA732X_REG_HPL_DAC_OFF_CNTL) & + DA732X_HP_DAC_OFF_CNTL_COMPO); + sign[DA732X_HPR_DAC] = (snd_soc_component_read(component, DA732X_REG_HPR_DAC_OFF_CNTL) & + DA732X_HP_DAC_OFF_CNTL_COMPO); + + /* Binary search DAC offset values (both channels at once) */ + offset[DA732X_HPL_DAC] = sign[DA732X_HPL_DAC] << DA732X_HP_DAC_COMPO_SHIFT; + offset[DA732X_HPR_DAC] = sign[DA732X_HPR_DAC] << DA732X_HP_DAC_COMPO_SHIFT; + + do { + offset[DA732X_HPL_DAC] |= step; + offset[DA732X_HPR_DAC] |= step; + snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET, + ~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK); + snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET, + ~offset[DA732X_HPR_DAC] & DA732X_HP_DAC_OFF_MASK); + + msleep(DA732X_WAIT_FOR_STABILIZATION); + + if ((snd_soc_component_read(component, DA732X_REG_HPL_DAC_OFF_CNTL) & + DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPL_DAC]) + offset[DA732X_HPL_DAC] &= ~step; + if ((snd_soc_component_read(component, DA732X_REG_HPR_DAC_OFF_CNTL) & + DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPR_DAC]) + offset[DA732X_HPR_DAC] &= ~step; + + step >>= 1; + } while (step); + + /* Write final DAC offsets to registers */ + snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET, + ~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK); + snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET, + ~offset[DA732X_HPR_DAC] & DA732X_HP_DAC_OFF_MASK); + + /* End DAC calibration mode */ + snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFF_CNTL, + DA732X_HP_DAC_OFF_SCALE_STEPS); + snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFF_CNTL, + DA732X_HP_DAC_OFF_SCALE_STEPS); +} + +static void da732x_output_offset_adjust(struct snd_soc_component *component) +{ + u8 offset[DA732X_HP_AMPS]; + u8 sign[DA732X_HP_AMPS]; + u8 step = DA732X_OUTPUT_OFFSET_STEP; + + offset[DA732X_HPL_AMP] = DA732X_HP_OUT_TRIM_VAL; + offset[DA732X_HPR_AMP] = DA732X_HP_OUT_TRIM_VAL; + + /* Initialize output offset calibration circuits and registers */ + snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL); + snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL); + snd_soc_component_write(component, DA732X_REG_HPL, + DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN); + snd_soc_component_write(component, DA732X_REG_HPR, + DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN); + + /* Wait for voltage stabilization */ + msleep(DA732X_WAIT_FOR_STABILIZATION); + + /* Check output offset sign */ + sign[DA732X_HPL_AMP] = snd_soc_component_read(component, DA732X_REG_HPL) & + DA732X_HP_OUT_COMPO; + sign[DA732X_HPR_AMP] = snd_soc_component_read(component, DA732X_REG_HPR) & + DA732X_HP_OUT_COMPO; + + snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_OUT_COMP | + (sign[DA732X_HPL_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) | + DA732X_HP_OUT_EN); + snd_soc_component_write(component, DA732X_REG_HPR, DA732X_HP_OUT_COMP | + (sign[DA732X_HPR_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) | + DA732X_HP_OUT_EN); + + /* Binary search output offset values (both channels at once) */ + do { + offset[DA732X_HPL_AMP] |= step; + offset[DA732X_HPR_AMP] |= step; + snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET, + offset[DA732X_HPL_AMP]); + snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET, + offset[DA732X_HPR_AMP]); + + msleep(DA732X_WAIT_FOR_STABILIZATION); + + if ((snd_soc_component_read(component, DA732X_REG_HPL) & + DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPL_AMP]) + offset[DA732X_HPL_AMP] &= ~step; + if ((snd_soc_component_read(component, DA732X_REG_HPR) & + DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPR_AMP]) + offset[DA732X_HPR_AMP] &= ~step; + + step >>= 1; + } while (step); + + /* Write final DAC offsets to registers */ + snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET, offset[DA732X_HPL_AMP]); + snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET, offset[DA732X_HPR_AMP]); +} + +static void da732x_hp_dc_offset_cancellation(struct snd_soc_component *component) +{ + /* Make sure that we have Soft Mute enabled */ + snd_soc_component_write(component, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN | + DA732X_GAIN_RAMPED | DA732X_16_SAMPLES); + snd_soc_component_write(component, DA732X_REG_DAC1_SEL, DA732X_DACL_EN | + DA732X_DACR_EN | DA732X_DACL_SDM | DA732X_DACR_SDM | + DA732X_DACL_MUTE | DA732X_DACR_MUTE); + snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN | + DA732X_HP_OUT_MUTE | DA732X_HP_OUT_EN); + snd_soc_component_write(component, DA732X_REG_HPR, DA732X_HP_OUT_EN | + DA732X_HP_OUT_MUTE | DA732X_HP_OUT_DAC_EN); + + da732x_dac_offset_adjust(component); + da732x_output_offset_adjust(component); + + snd_soc_component_write(component, DA732X_REG_DAC1_SEL, DA732X_DACS_DIS); + snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_DIS); + snd_soc_component_write(component, DA732X_REG_HPR, DA732X_HP_DIS); +} + +static int da732x_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct da732x_priv *da732x = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN, + DA732X_BIAS_BOOST_MASK, + DA732X_BIAS_BOOST_100PC); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Init Codec */ + snd_soc_component_write(component, DA732X_REG_REF1, + DA732X_VMID_FASTCHG); + snd_soc_component_write(component, DA732X_REG_BIAS_EN, + DA732X_BIAS_EN); + + mdelay(DA732X_STARTUP_DELAY); + + /* Disable Fast Charge and enable DAC ref voltage */ + snd_soc_component_write(component, DA732X_REG_REF1, + DA732X_REFBUFX2_EN); + + /* Enable bypass DSP routing */ + snd_soc_component_write(component, DA732X_REG_DATA_ROUTE, + DA732X_BYPASS_DSP); + + /* Enable Digital subsystem */ + snd_soc_component_write(component, DA732X_REG_DSP_CTRL, + DA732X_DIGITAL_EN); + + snd_soc_component_write(component, DA732X_REG_SPARE1_OUT, + DA732X_HP_DRIVER_EN | + DA732X_HP_GATE_LOW | + DA732X_HP_LOOP_GAIN_CTRL); + snd_soc_component_write(component, DA732X_REG_HP_LIN1_GNDSEL, + DA732X_HP_OUT_GNDSEL); + + da732x_set_charge_pump(component, DA732X_ENABLE_CP); + + snd_soc_component_write(component, DA732X_REG_CLK_EN1, + DA732X_SYS3_CLK_EN | DA732X_PC_CLK_EN); + + /* Enable Zero Crossing */ + snd_soc_component_write(component, DA732X_REG_INP_ZC_EN, + DA732X_MIC1_PRE_ZC_EN | + DA732X_MIC1_ZC_EN | + DA732X_MIC2_PRE_ZC_EN | + DA732X_MIC2_ZC_EN | + DA732X_AUXL_ZC_EN | + DA732X_AUXR_ZC_EN | + DA732X_MIC3_PRE_ZC_EN | + DA732X_MIC3_ZC_EN); + snd_soc_component_write(component, DA732X_REG_OUT_ZC_EN, + DA732X_HPL_ZC_EN | DA732X_HPR_ZC_EN | + DA732X_LIN2_ZC_EN | DA732X_LIN3_ZC_EN | + DA732X_LIN4_ZC_EN); + + da732x_hp_dc_offset_cancellation(component); + + regcache_cache_only(da732x->regmap, false); + regcache_sync(da732x->regmap); + } else { + snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN, + DA732X_BIAS_BOOST_MASK, + DA732X_BIAS_BOOST_50PC); + snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL, + DA732X_PLL_EN, 0); + da732x->pll_en = false; + } + break; + case SND_SOC_BIAS_OFF: + regcache_cache_only(da732x->regmap, true); + da732x_set_charge_pump(component, DA732X_DISABLE_CP); + snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN, DA732X_BIAS_EN, + DA732X_BIAS_DIS); + da732x->pll_en = false; + break; + } + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_da732x = { + .set_bias_level = da732x_set_bias_level, + .controls = da732x_snd_controls, + .num_controls = ARRAY_SIZE(da732x_snd_controls), + .dapm_widgets = da732x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da732x_dapm_widgets), + .dapm_routes = da732x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(da732x_dapm_routes), + .set_pll = da732x_set_dai_pll, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int da732x_i2c_probe(struct i2c_client *i2c) +{ + struct da732x_priv *da732x; + unsigned int reg; + int ret; + + da732x = devm_kzalloc(&i2c->dev, sizeof(struct da732x_priv), + GFP_KERNEL); + if (!da732x) + return -ENOMEM; + + i2c_set_clientdata(i2c, da732x); + + da732x->regmap = devm_regmap_init_i2c(i2c, &da732x_regmap); + if (IS_ERR(da732x->regmap)) { + ret = PTR_ERR(da732x->regmap); + dev_err(&i2c->dev, "Failed to initialize regmap\n"); + goto err; + } + + ret = regmap_read(da732x->regmap, DA732X_REG_ID, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret); + goto err; + } + + dev_info(&i2c->dev, "Revision: %d.%d\n", + (reg & DA732X_ID_MAJOR_MASK) >> 4, + (reg & DA732X_ID_MINOR_MASK)); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_da732x, + da732x_dai, ARRAY_SIZE(da732x_dai)); + if (ret != 0) + dev_err(&i2c->dev, "Failed to register component.\n"); + +err: + return ret; +} + +static const struct i2c_device_id da732x_i2c_id[] = { + { "da7320", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, da732x_i2c_id); + +static struct i2c_driver da732x_i2c_driver = { + .driver = { + .name = "da7320", + }, + .probe = da732x_i2c_probe, + .id_table = da732x_i2c_id, +}; + +module_i2c_driver(da732x_i2c_driver); + + +MODULE_DESCRIPTION("ASoC DA732X driver"); +MODULE_AUTHOR("Michal Hajduk "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da732x.h b/sound/soc/codecs/da732x.h new file mode 100644 index 0000000000..c2f784c3f3 --- /dev/null +++ b/sound/soc/codecs/da732x.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * da732x.h -- Dialog DA732X ALSA SoC Audio Driver Header File + * + * Copyright (C) 2012 Dialog Semiconductor GmbH + * + * Author: Michal Hajduk + */ + +#ifndef __DA732X_H_ +#define __DA732X_H_ + +#include + +/* General */ +#define DA732X_U8_MASK 0xFF +#define DA732X_4BYTES 4 +#define DA732X_3BYTES 3 +#define DA732X_2BYTES 2 +#define DA732X_1BYTE 1 +#define DA732X_1BYTE_SHIFT 8 +#define DA732X_2BYTES_SHIFT 16 +#define DA732X_3BYTES_SHIFT 24 +#define DA732X_4BYTES_SHIFT 32 + +#define DA732X_DACS_DIS 0x0 +#define DA732X_HP_DIS 0x0 +#define DA732X_CLEAR_REG 0x0 + +/* Calibration */ +#define DA732X_DAC_OFFSET_STEP 0x20 +#define DA732X_OUTPUT_OFFSET_STEP 0x80 +#define DA732X_HP_OUT_TRIM_VAL 0x0 +#define DA732X_WAIT_FOR_STABILIZATION 1 +#define DA732X_HPL_DAC 0 +#define DA732X_HPR_DAC 1 +#define DA732X_HP_DACS 2 +#define DA732X_HPL_AMP 0 +#define DA732X_HPR_AMP 1 +#define DA732X_HP_AMPS 2 + +/* Clock settings */ +#define DA732X_STARTUP_DELAY 100 +#define DA732X_PLL_OUT_196608 196608000 +#define DA732X_PLL_OUT_180634 180633600 +#define DA732X_PLL_OUT_SRM 188620800 +#define DA732X_MCLK_10MHZ 10000000 +#define DA732X_MCLK_20MHZ 20000000 +#define DA732X_MCLK_40MHZ 40000000 +#define DA732X_MCLK_54MHZ 54000000 +#define DA732X_MCLK_VAL_0_10MHZ 0 +#define DA732X_MCLK_VAL_10_20MHZ 1 +#define DA732X_MCLK_VAL_20_40MHZ 2 +#define DA732X_MCLK_VAL_40_54MHZ 3 +#define DA732X_DAI_ID1 0 +#define DA732X_DAI_ID2 1 +#define DA732X_SRCCLK_PLL 0 +#define DA732X_SRCCLK_MCLK 1 + +#define DA732X_LIN_LP_VOL 0x4F +#define DA732X_LP_VOL 0x40 + +/* Kcontrols */ +#define DA732X_DAC_EN_MAX 2 +#define DA732X_ADCL_MUX_MAX 2 +#define DA732X_ADCR_MUX_MAX 3 +#define DA732X_HPF_MODE_MAX 3 +#define DA732X_HPF_MODE_SHIFT 4 +#define DA732X_HPF_MUSIC_SHIFT 0 +#define DA732X_HPF_MUSIC_MAX 4 +#define DA732X_HPF_VOICE_SHIFT 4 +#define DA732X_HPF_VOICE_MAX 8 +#define DA732X_EQ_EN_MAX 1 +#define DA732X_HPF_VOICE 1 +#define DA732X_HPF_MUSIC 2 +#define DA732X_HPF_DISABLED 0 +#define DA732X_NO_INVERT 0 +#define DA732X_INVERT 1 +#define DA732X_SWITCH_MAX 1 +#define DA732X_ENABLE_CP 1 +#define DA732X_DISABLE_CP 0 +#define DA732X_DISABLE_ALL_CLKS 0 +#define DA732X_RESET_ADCS 0 + +/* dB values */ +#define DA732X_MIC_VOL_DB_MIN 0 +#define DA732X_MIC_VOL_DB_INC 50 +#define DA732X_MIC_PRE_VOL_DB_MIN 0 +#define DA732X_MIC_PRE_VOL_DB_INC 600 +#define DA732X_AUX_VOL_DB_MIN -6000 +#define DA732X_AUX_VOL_DB_INC 150 +#define DA732X_HP_VOL_DB_MIN -2250 +#define DA732X_HP_VOL_DB_INC 150 +#define DA732X_LIN2_VOL_DB_MIN -1650 +#define DA732X_LIN2_VOL_DB_INC 150 +#define DA732X_LIN3_VOL_DB_MIN -1650 +#define DA732X_LIN3_VOL_DB_INC 150 +#define DA732X_LIN4_VOL_DB_MIN -2250 +#define DA732X_LIN4_VOL_DB_INC 150 +#define DA732X_EQ_BAND_VOL_DB_MIN -1050 +#define DA732X_EQ_BAND_VOL_DB_INC 150 +#define DA732X_DAC_VOL_DB_MIN -7725 +#define DA732X_DAC_VOL_DB_INC 75 +#define DA732X_ADC_VOL_DB_MIN 0 +#define DA732X_ADC_VOL_DB_INC -1 +#define DA732X_EQ_OVERALL_VOL_DB_MIN -1800 +#define DA732X_EQ_OVERALL_VOL_DB_INC 600 + +enum da732x_sysctl { + DA732X_SR_8KHZ = 0x1, + DA732X_SR_11_025KHZ = 0x2, + DA732X_SR_12KHZ = 0x3, + DA732X_SR_16KHZ = 0x5, + DA732X_SR_22_05KHZ = 0x6, + DA732X_SR_24KHZ = 0x7, + DA732X_SR_32KHZ = 0x9, + DA732X_SR_44_1KHZ = 0xA, + DA732X_SR_48KHZ = 0xB, + DA732X_SR_88_1KHZ = 0xE, + DA732X_SR_96KHZ = 0xF, +}; + +#endif /* __DA732X_H_ */ diff --git a/sound/soc/codecs/da732x_reg.h b/sound/soc/codecs/da732x_reg.h new file mode 100644 index 0000000000..a493e0b46f --- /dev/null +++ b/sound/soc/codecs/da732x_reg.h @@ -0,0 +1,651 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * da732x_reg.h --- Dialog DA732X ALSA SoC Audio Registers Header File + * + * Copyright (C) 2012 Dialog Semiconductor GmbH + * + * Author: Michal Hajduk + */ + +#ifndef __DA732X_REG_H_ +#define __DA732X_REG_H_ + +/* DA732X registers */ +#define DA732X_REG_STATUS_EXT 0x00 +#define DA732X_REG_STATUS 0x01 +#define DA732X_REG_REF1 0x02 +#define DA732X_REG_BIAS_EN 0x03 +#define DA732X_REG_BIAS1 0x04 +#define DA732X_REG_BIAS2 0x05 +#define DA732X_REG_BIAS3 0x06 +#define DA732X_REG_BIAS4 0x07 +#define DA732X_REG_MICBIAS2 0x0F +#define DA732X_REG_MICBIAS1 0x10 +#define DA732X_REG_MICDET 0x11 +#define DA732X_REG_MIC1_PRE 0x12 +#define DA732X_REG_MIC1 0x13 +#define DA732X_REG_MIC2_PRE 0x14 +#define DA732X_REG_MIC2 0x15 +#define DA732X_REG_AUX1L 0x16 +#define DA732X_REG_AUX1R 0x17 +#define DA732X_REG_MIC3_PRE 0x18 +#define DA732X_REG_MIC3 0x19 +#define DA732X_REG_INP_PINBIAS 0x1A +#define DA732X_REG_INP_ZC_EN 0x1B +#define DA732X_REG_INP_MUX 0x1D +#define DA732X_REG_HP_DET 0x20 +#define DA732X_REG_HPL_DAC_OFFSET 0x21 +#define DA732X_REG_HPL_DAC_OFF_CNTL 0x22 +#define DA732X_REG_HPL_OUT_OFFSET 0x23 +#define DA732X_REG_HPL 0x24 +#define DA732X_REG_HPL_VOL 0x25 +#define DA732X_REG_HPR_DAC_OFFSET 0x26 +#define DA732X_REG_HPR_DAC_OFF_CNTL 0x27 +#define DA732X_REG_HPR_OUT_OFFSET 0x28 +#define DA732X_REG_HPR 0x29 +#define DA732X_REG_HPR_VOL 0x2A +#define DA732X_REG_LIN2 0x2B +#define DA732X_REG_LIN3 0x2C +#define DA732X_REG_LIN4 0x2D +#define DA732X_REG_OUT_ZC_EN 0x2E +#define DA732X_REG_HP_LIN1_GNDSEL 0x37 +#define DA732X_REG_CP_HP1 0x3A +#define DA732X_REG_CP_HP2 0x3B +#define DA732X_REG_CP_CTRL1 0x40 +#define DA732X_REG_CP_CTRL2 0x41 +#define DA732X_REG_CP_CTRL3 0x42 +#define DA732X_REG_CP_LEVEL_MASK 0x43 +#define DA732X_REG_CP_DET 0x44 +#define DA732X_REG_CP_STATUS 0x45 +#define DA732X_REG_CP_THRESH1 0x46 +#define DA732X_REG_CP_THRESH2 0x47 +#define DA732X_REG_CP_THRESH3 0x48 +#define DA732X_REG_CP_THRESH4 0x49 +#define DA732X_REG_CP_THRESH5 0x4A +#define DA732X_REG_CP_THRESH6 0x4B +#define DA732X_REG_CP_THRESH7 0x4C +#define DA732X_REG_CP_THRESH8 0x4D +#define DA732X_REG_PLL_DIV_LO 0x50 +#define DA732X_REG_PLL_DIV_MID 0x51 +#define DA732X_REG_PLL_DIV_HI 0x52 +#define DA732X_REG_PLL_CTRL 0x53 +#define DA732X_REG_CLK_CTRL 0x54 +#define DA732X_REG_CLK_DSP 0x5A +#define DA732X_REG_CLK_EN1 0x5B +#define DA732X_REG_CLK_EN2 0x5C +#define DA732X_REG_CLK_EN3 0x5D +#define DA732X_REG_CLK_EN4 0x5E +#define DA732X_REG_CLK_EN5 0x5F +#define DA732X_REG_AIF_MCLK 0x60 +#define DA732X_REG_AIFA1 0x61 +#define DA732X_REG_AIFA2 0x62 +#define DA732X_REG_AIFA3 0x63 +#define DA732X_REG_AIFB1 0x64 +#define DA732X_REG_AIFB2 0x65 +#define DA732X_REG_AIFB3 0x66 +#define DA732X_REG_PC_CTRL 0x6A +#define DA732X_REG_DATA_ROUTE 0x70 +#define DA732X_REG_DSP_CTRL 0x71 +#define DA732X_REG_CIF_CTRL2 0x74 +#define DA732X_REG_HANDSHAKE 0x75 +#define DA732X_REG_MBOX0 0x76 +#define DA732X_REG_MBOX1 0x77 +#define DA732X_REG_MBOX2 0x78 +#define DA732X_REG_MBOX_STATUS 0x79 +#define DA732X_REG_SPARE1_OUT 0x7D +#define DA732X_REG_SPARE2_OUT 0x7E +#define DA732X_REG_SPARE1_IN 0x7F +#define DA732X_REG_ID 0x81 +#define DA732X_REG_ADC1_PD 0x90 +#define DA732X_REG_ADC1_HPF 0x93 +#define DA732X_REG_ADC1_SEL 0x94 +#define DA732X_REG_ADC1_EQ12 0x95 +#define DA732X_REG_ADC1_EQ34 0x96 +#define DA732X_REG_ADC1_EQ5 0x97 +#define DA732X_REG_ADC2_PD 0x98 +#define DA732X_REG_ADC2_HPF 0x9B +#define DA732X_REG_ADC2_SEL 0x9C +#define DA732X_REG_ADC2_EQ12 0x9D +#define DA732X_REG_ADC2_EQ34 0x9E +#define DA732X_REG_ADC2_EQ5 0x9F +#define DA732X_REG_DAC1_HPF 0xA0 +#define DA732X_REG_DAC1_L_VOL 0xA1 +#define DA732X_REG_DAC1_R_VOL 0xA2 +#define DA732X_REG_DAC1_SEL 0xA3 +#define DA732X_REG_DAC1_SOFTMUTE 0xA4 +#define DA732X_REG_DAC1_EQ12 0xA5 +#define DA732X_REG_DAC1_EQ34 0xA6 +#define DA732X_REG_DAC1_EQ5 0xA7 +#define DA732X_REG_DAC2_HPF 0xB0 +#define DA732X_REG_DAC2_L_VOL 0xB1 +#define DA732X_REG_DAC2_R_VOL 0xB2 +#define DA732X_REG_DAC2_SEL 0xB3 +#define DA732X_REG_DAC2_SOFTMUTE 0xB4 +#define DA732X_REG_DAC2_EQ12 0xB5 +#define DA732X_REG_DAC2_EQ34 0xB6 +#define DA732X_REG_DAC2_EQ5 0xB7 +#define DA732X_REG_DAC3_HPF 0xC0 +#define DA732X_REG_DAC3_VOL 0xC1 +#define DA732X_REG_DAC3_SEL 0xC3 +#define DA732X_REG_DAC3_SOFTMUTE 0xC4 +#define DA732X_REG_DAC3_EQ12 0xC5 +#define DA732X_REG_DAC3_EQ34 0xC6 +#define DA732X_REG_DAC3_EQ5 0xC7 +#define DA732X_REG_BIQ_BYP 0xD2 +#define DA732X_REG_DMA_CMD 0xD3 +#define DA732X_REG_DMA_ADDR0 0xD4 +#define DA732X_REG_DMA_ADDR1 0xD5 +#define DA732X_REG_DMA_DATA0 0xD6 +#define DA732X_REG_DMA_DATA1 0xD7 +#define DA732X_REG_DMA_DATA2 0xD8 +#define DA732X_REG_DMA_DATA3 0xD9 +#define DA732X_REG_DMA_STATUS 0xDA +#define DA732X_REG_BROWNOUT 0xDF +#define DA732X_REG_UNLOCK 0xE0 + +#define DA732X_MAX_REG DA732X_REG_UNLOCK +/* + * Bits + */ + +/* DA732X_REG_STATUS_EXT (addr=0x00) */ +#define DA732X_STATUS_EXT_DSP (1 << 4) +#define DA732X_STATUS_EXT_CLEAR (0 << 0) + +/* DA732X_REG_STATUS (addr=0x01) */ +#define DA732X_STATUS_PLL_LOCK (1 << 0) +#define DA732X_STATUS_PLL_MCLK_DET (1 << 1) +#define DA732X_STATUS_HPDET_OUT (1 << 2) +#define DA732X_STATUS_INP_MIXDET_1 (1 << 3) +#define DA732X_STATUS_INP_MIXDET_2 (1 << 4) +#define DA732X_STATUS_BO_STATUS (1 << 5) + +/* DA732X_REG_REF1 (addr=0x02) */ +#define DA732X_VMID_FASTCHG (1 << 1) +#define DA732X_VMID_FASTDISCHG (1 << 2) +#define DA732X_REFBUFX2_EN (1 << 6) +#define DA732X_REFBUFX2_DIS (0 << 6) + +/* DA732X_REG_BIAS_EN (addr=0x03) */ +#define DA732X_BIAS_BOOST_MASK (3 << 0) +#define DA732X_BIAS_BOOST_100PC (0 << 0) +#define DA732X_BIAS_BOOST_133PC (1 << 0) +#define DA732X_BIAS_BOOST_88PC (2 << 0) +#define DA732X_BIAS_BOOST_50PC (3 << 0) +#define DA732X_BIAS_EN (1 << 7) +#define DA732X_BIAS_DIS (0 << 7) + +/* DA732X_REG_BIAS1 (addr=0x04) */ +#define DA732X_BIAS1_HP_DAC_BIAS_MASK (3 << 0) +#define DA732X_BIAS1_HP_DAC_BIAS_100PC (0 << 0) +#define DA732X_BIAS1_HP_DAC_BIAS_150PC (1 << 0) +#define DA732X_BIAS1_HP_DAC_BIAS_50PC (2 << 0) +#define DA732X_BIAS1_HP_DAC_BIAS_75PC (3 << 0) +#define DA732X_BIAS1_HP_OUT_BIAS_MASK (7 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_100PC (0 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_125PC (1 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_150PC (2 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_175PC (3 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_200PC (4 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_250PC (5 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_300PC (6 << 4) +#define DA732X_BIAS1_HP_OUT_BIAS_350PC (7 << 4) + +/* DA732X_REG_BIAS2 (addr=0x05) */ +#define DA732X_BIAS2_LINE2_DAC_BIAS_MASK (3 << 0) +#define DA732X_BIAS2_LINE2_DAC_BIAS_100PC (0 << 0) +#define DA732X_BIAS2_LINE2_DAC_BIAS_150PC (1 << 0) +#define DA732X_BIAS2_LINE2_DAC_BIAS_50PC (2 << 0) +#define DA732X_BIAS2_LINE2_DAC_BIAS_75PC (3 << 0) +#define DA732X_BIAS2_LINE2_OUT_BIAS_MASK (7 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_100PC (0 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_125PC (1 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_150PC (2 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_175PC (3 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_200PC (4 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_250PC (5 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_300PC (6 << 4) +#define DA732X_BIAS2_LINE2_OUT_BIAS_350PC (7 << 4) + +/* DA732X_REG_BIAS3 (addr=0x06) */ +#define DA732X_BIAS3_LINE3_DAC_BIAS_MASK (3 << 0) +#define DA732X_BIAS3_LINE3_DAC_BIAS_100PC (0 << 0) +#define DA732X_BIAS3_LINE3_DAC_BIAS_150PC (1 << 0) +#define DA732X_BIAS3_LINE3_DAC_BIAS_50PC (2 << 0) +#define DA732X_BIAS3_LINE3_DAC_BIAS_75PC (3 << 0) +#define DA732X_BIAS3_LINE3_OUT_BIAS_MASK (7 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_100PC (0 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_125PC (1 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_150PC (2 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_175PC (3 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_200PC (4 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_250PC (5 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_300PC (6 << 4) +#define DA732X_BIAS3_LINE3_OUT_BIAS_350PC (7 << 4) + +/* DA732X_REG_BIAS4 (addr=0x07) */ +#define DA732X_BIAS4_LINE4_DAC_BIAS_MASK (3 << 0) +#define DA732X_BIAS4_LINE4_DAC_BIAS_100PC (0 << 0) +#define DA732X_BIAS4_LINE4_DAC_BIAS_150PC (1 << 0) +#define DA732X_BIAS4_LINE4_DAC_BIAS_50PC (2 << 0) +#define DA732X_BIAS4_LINE4_DAC_BIAS_75PC (3 << 0) +#define DA732X_BIAS4_LINE4_OUT_BIAS_MASK (7 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_100PC (0 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_125PC (1 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_150PC (2 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_175PC (3 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_200PC (4 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_250PC (5 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_300PC (6 << 4) +#define DA732X_BIAS4_LINE4_OUT_BIAS_350PC (7 << 4) + +/* DA732X_REG_SIF_VDD_SEL (addr=0x08) */ +#define DA732X_SIF_VDD_SEL_AIFA_VDD2 (1 << 0) +#define DA732X_SIF_VDD_SEL_AIFB_VDD2 (1 << 1) +#define DA732X_SIF_VDD_SEL_CIFA_VDD2 (1 << 4) + +/* DA732X_REG_MICBIAS2/1 (addr=0x0F/0x10) */ +#define DA732X_MICBIAS_VOLTAGE_MASK (0x0F << 0) +#define DA732X_MICBIAS_VOLTAGE_2V (0x00 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V05 (0x01 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V1 (0x02 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V15 (0x03 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V2 (0x04 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V25 (0x05 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V3 (0x06 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V35 (0x07 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V4 (0x08 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V45 (0x09 << 0) +#define DA732X_MICBIAS_VOLTAGE_2V5 (0x0A << 0) +#define DA732X_MICBIAS_EN (1 << 7) +#define DA732X_MICBIAS_EN_SHIFT 7 +#define DA732X_MICBIAS_VOLTAGE_SHIFT 0 +#define DA732X_MICBIAS_VOLTAGE_MAX 0x0B + +/* DA732X_REG_MICDET (addr=0x11) */ +#define DA732X_MICDET_INP_MICRES (1 << 0) +#define DA732X_MICDET_INP_MICHOOK (1 << 1) +#define DA732X_MICDET_INP_DEBOUNCE_PRD_8MS (0 << 0) +#define DA732X_MICDET_INP_DEBOUNCE_PRD_16MS (1 << 0) +#define DA732X_MICDET_INP_DEBOUNCE_PRD_32MS (2 << 0) +#define DA732X_MICDET_INP_DEBOUNCE_PRD_64MS (3 << 0) +#define DA732X_MICDET_INP_MICDET_EN (1 << 7) + +/* DA732X_REG_MIC1/2/3_PRE (addr=0x11/0x14/0x18) */ +#define DA732X_MICBOOST_MASK 0x7 +#define DA732X_MICBOOST_SHIFT 0 +#define DA732X_MICBOOST_MIN 0x1 +#define DA732X_MICBOOST_MAX DA732X_MICBOOST_MASK + +/* DA732X_REG_MIC1/2/3 (addr=0x13/0x15/0x19) */ +#define DA732X_MIC_VOL_SHIFT 0 +#define DA732X_MIC_VOL_VAL_MASK 0x1F +#define DA732X_MIC_MUTE_SHIFT 6 +#define DA732X_MIC_EN_SHIFT 7 +#define DA732X_MIC_VOL_VAL_MIN 0x7 +#define DA732X_MIC_VOL_VAL_MAX DA732X_MIC_VOL_VAL_MASK + +/* DA732X_REG_AUX1L/R (addr=0x16/0x17) */ +#define DA732X_AUX_VOL_SHIFT 0 +#define DA732X_AUX_VOL_MASK 0x7 +#define DA732X_AUX_MUTE_SHIFT 6 +#define DA732X_AUX_EN_SHIFT 7 +#define DA732X_AUX_VOL_VAL_MAX DA732X_AUX_VOL_MASK + +/* DA732X_REG_INP_PINBIAS (addr=0x1A) */ +#define DA732X_INP_MICL_PINBIAS_EN (1 << 0) +#define DA732X_INP_MICR_PINBIAS_EN (1 << 1) +#define DA732X_INP_AUX1L_PINBIAS_EN (1 << 2) +#define DA732X_INP_AUX1R_PINBIAS_EN (1 << 3) +#define DA732X_INP_AUX2_PINBIAS_EN (1 << 4) + +/* DA732X_REG_INP_ZC_EN (addr=0x1B) */ +#define DA732X_MIC1_PRE_ZC_EN (1 << 0) +#define DA732X_MIC1_ZC_EN (1 << 1) +#define DA732X_MIC2_PRE_ZC_EN (1 << 2) +#define DA732X_MIC2_ZC_EN (1 << 3) +#define DA732X_AUXL_ZC_EN (1 << 4) +#define DA732X_AUXR_ZC_EN (1 << 5) +#define DA732X_MIC3_PRE_ZC_EN (1 << 6) +#define DA732X_MIC3_ZC_EN (1 << 7) + +/* DA732X_REG_INP_MUX (addr=0x1D) */ +#define DA732X_INP_ADC1L_MUX_SEL_AUX1L (0 << 0) +#define DA732X_INP_ADC1L_MUX_SEL_MIC1 (1 << 0) +#define DA732X_INP_ADC1R_MUX_SEL_MASK (3 << 2) +#define DA732X_INP_ADC1R_MUX_SEL_AUX1R (0 << 2) +#define DA732X_INP_ADC1R_MUX_SEL_MIC2 (1 << 2) +#define DA732X_INP_ADC1R_MUX_SEL_MIC3 (2 << 2) +#define DA732X_INP_ADC2L_MUX_SEL_AUX1L (0 << 4) +#define DA732X_INP_ADC2L_MUX_SEL_MICL (1 << 4) +#define DA732X_INP_ADC2R_MUX_SEL_MASK (3 << 6) +#define DA732X_INP_ADC2R_MUX_SEL_AUX1R (0 << 6) +#define DA732X_INP_ADC2R_MUX_SEL_MICR (1 << 6) +#define DA732X_INP_ADC2R_MUX_SEL_AUX2 (2 << 6) +#define DA732X_ADC1L_MUX_SEL_SHIFT 0 +#define DA732X_ADC1R_MUX_SEL_SHIFT 2 +#define DA732X_ADC2L_MUX_SEL_SHIFT 4 +#define DA732X_ADC2R_MUX_SEL_SHIFT 6 + +/* DA732X_REG_HP_DET (addr=0x20) */ +#define DA732X_HP_DET_AZ (1 << 0) +#define DA732X_HP_DET_SEL1 (1 << 1) +#define DA732X_HP_DET_IS_MASK (3 << 2) +#define DA732X_HP_DET_IS_0_5UA (0 << 2) +#define DA732X_HP_DET_IS_1UA (1 << 2) +#define DA732X_HP_DET_IS_2UA (2 << 2) +#define DA732X_HP_DET_IS_4UA (3 << 2) +#define DA732X_HP_DET_RS_MASK (3 << 4) +#define DA732X_HP_DET_RS_INFINITE (0 << 4) +#define DA732X_HP_DET_RS_100KOHM (1 << 4) +#define DA732X_HP_DET_RS_10KOHM (2 << 4) +#define DA732X_HP_DET_RS_1KOHM (3 << 4) +#define DA732X_HP_DET_EN (1 << 7) + +/* DA732X_REG_HPL_DAC_OFFSET (addr=0x21/0x26) */ +#define DA732X_HP_DAC_OFFSET_TRIM_MASK (0x3F << 0) +#define DA732X_HP_DAC_OFFSET_DAC_SIGN (1 << 6) + +/* DA732X_REG_HPL_DAC_OFF_CNTL (addr=0x22/0x27) */ +#define DA732X_HP_DAC_OFF_CNTL_CONT_MASK (7 << 0) +#define DA732X_HP_DAC_OFF_CNTL_COMPO (1 << 3) +#define DA732X_HP_DAC_OFF_CALIBRATION (1 << 0) +#define DA732X_HP_DAC_OFF_SCALE_STEPS (1 << 1) +#define DA732X_HP_DAC_OFF_MASK 0x7F +#define DA732X_HP_DAC_COMPO_SHIFT 3 + +/* DA732X_REG_HPL_OUT_OFFSET (addr=0x23/0x28) */ +#define DA732X_HP_OUT_OFFSET_MASK (0xFF << 0) +#define DA732X_HP_DAC_OFFSET_TRIM_VAL 0x7F + +/* DA732X_REG_HPL/R (addr=0x24/0x29) */ +#define DA732X_HP_OUT_SIGN (1 << 0) +#define DA732X_HP_OUT_COMP (1 << 1) +#define DA732X_HP_OUT_RESERVED (1 << 2) +#define DA732X_HP_OUT_COMPO (1 << 3) +#define DA732X_HP_OUT_DAC_EN (1 << 4) +#define DA732X_HP_OUT_HIZ_EN (1 << 5) +#define DA732X_HP_OUT_HIZ_DIS (0 << 5) +#define DA732X_HP_OUT_MUTE (1 << 6) +#define DA732X_HP_OUT_EN (1 << 7) +#define DA732X_HP_OUT_COMPO_SHIFT 3 +#define DA732X_HP_OUT_DAC_EN_SHIFT 4 +#define DA732X_HP_HIZ_SHIFT 5 +#define DA732X_HP_MUTE_SHIFT 6 +#define DA732X_HP_OUT_EN_SHIFT 7 + +#define DA732X_OUT_HIZ_EN (1 << 5) +#define DA732X_OUT_HIZ_DIS (0 << 5) + +/* DA732X_REG_HPL/R_VOL (addr=0x25/0x2A) */ +#define DA732X_HP_VOL_VAL_MASK 0xF +#define DA732X_HP_VOL_SHIFT 0 +#define DA732X_HP_VOL_VAL_MAX DA732X_HP_VOL_VAL_MASK + +/* DA732X_REG_LIN2/3/4 (addr=0x2B/0x2C/0x2D) */ +#define DA732X_LOUT_VOL_SHIFT 0 +#define DA732X_LOUT_VOL_MASK 0x0F +#define DA732X_LOUT_DAC_OFF (0 << 4) +#define DA732X_LOUT_DAC_EN (1 << 4) +#define DA732X_LOUT_HIZ_N_DIS (0 << 5) +#define DA732X_LOUT_HIZ_N_EN (1 << 5) +#define DA732X_LOUT_UNMUTED (0 << 6) +#define DA732X_LOUT_MUTED (1 << 6) +#define DA732X_LOUT_EN (0 << 7) +#define DA732X_LOUT_DIS (1 << 7) +#define DA732X_LOUT_DAC_EN_SHIFT 4 +#define DA732X_LOUT_MUTE_SHIFT 6 +#define DA732X_LIN_OUT_EN_SHIFT 7 +#define DA732X_LOUT_VOL_VAL_MAX DA732X_LOUT_VOL_MASK + +/* DA732X_REG_OUT_ZC_EN (addr=0x2E) */ +#define DA732X_HPL_ZC_EN_SHIFT 0 +#define DA732X_HPR_ZC_EN_SHIFT 1 +#define DA732X_HPL_ZC_EN (1 << 0) +#define DA732X_HPL_ZC_DIS (0 << 0) +#define DA732X_HPR_ZC_EN (1 << 1) +#define DA732X_HPR_ZC_DIS (0 << 1) +#define DA732X_LIN2_ZC_EN (1 << 2) +#define DA732X_LIN2_ZC_DIS (0 << 2) +#define DA732X_LIN3_ZC_EN (1 << 3) +#define DA732X_LIN3_ZC_DIS (0 << 3) +#define DA732X_LIN4_ZC_EN (1 << 4) +#define DA732X_LIN4_ZC_DIS (0 << 4) + +/* DA732X_REG_HP_LIN1_GNDSEL (addr=0x37) */ +#define DA732X_HP_OUT_GNDSEL (1 << 0) + +/* DA732X_REG_CP_HP2 (addr=0x3a) */ +#define DA732X_HP_CP_PULSESKIP (1 << 0) +#define DA732X_HP_CP_REG (1 << 1) +#define DA732X_HP_CP_EN (1 << 3) +#define DA732X_HP_CP_DIS (0 << 3) + +/* DA732X_REG_CP_CTRL1 (addr=0x40) */ +#define DA732X_CP_MODE_MASK (7 << 1) +#define DA732X_CP_CTRL_STANDBY (0 << 1) +#define DA732X_CP_CTRL_CPVDD6 (2 << 1) +#define DA732X_CP_CTRL_CPVDD5 (3 << 1) +#define DA732X_CP_CTRL_CPVDD4 (4 << 1) +#define DA732X_CP_CTRL_CPVDD3 (5 << 1) +#define DA732X_CP_CTRL_CPVDD2 (6 << 1) +#define DA732X_CP_CTRL_CPVDD1 (7 << 1) +#define DA723X_CP_DIS (0 << 7) +#define DA732X_CP_EN (1 << 7) + +/* DA732X_REG_CP_CTRL2 (addr=0x41) */ +#define DA732X_CP_BOOST (1 << 0) +#define DA732X_CP_MANAGE_MAGNITUDE (2 << 2) + +/* DA732X_REG_CP_CTRL3 (addr=0x42) */ +#define DA732X_CP_1MHZ (0 << 0) +#define DA732X_CP_500KHZ (1 << 0) +#define DA732X_CP_250KHZ (2 << 0) +#define DA732X_CP_125KHZ (3 << 0) +#define DA732X_CP_63KHZ (4 << 0) +#define DA732X_CP_0KHZ (5 << 0) + +/* DA732X_REG_PLL_CTRL (addr=0x53) */ +#define DA732X_PLL_INDIV_MASK (3 << 0) +#define DA732X_PLL_SRM_EN (1 << 2) +#define DA732X_PLL_EN (1 << 7) +#define DA732X_PLL_BYPASS (0 << 0) + +/* DA732X_REG_CLK_CTRL (addr=0x54) */ +#define DA732X_SR1_MASK (0xF) +#define DA732X_SR2_MASK (0xF0) + +/* DA732X_REG_CLK_DSP (addr=0x5A) */ +#define DA732X_DSP_FREQ_MASK (7 << 0) +#define DA732X_DSP_FREQ_12MHZ (0 << 0) +#define DA732X_DSP_FREQ_24MHZ (1 << 0) +#define DA732X_DSP_FREQ_36MHZ (2 << 0) +#define DA732X_DSP_FREQ_48MHZ (3 << 0) +#define DA732X_DSP_FREQ_60MHZ (4 << 0) +#define DA732X_DSP_FREQ_72MHZ (5 << 0) +#define DA732X_DSP_FREQ_84MHZ (6 << 0) +#define DA732X_DSP_FREQ_96MHZ (7 << 0) + +/* DA732X_REG_CLK_EN1 (addr=0x5B) */ +#define DA732X_DSP_CLK_EN (1 << 0) +#define DA732X_SYS3_CLK_EN (1 << 1) +#define DA732X_DSP12_CLK_EN (1 << 2) +#define DA732X_PC_CLK_EN (1 << 3) +#define DA732X_MCLK_SQR_EN (1 << 7) + +/* DA732X_REG_CLK_EN2 (addr=0x5C) */ +#define DA732X_UART_CLK_EN (1 << 1) +#define DA732X_CP_CLK_EN (1 << 2) +#define DA732X_CP_CLK_DIS (0 << 2) + +/* DA732X_REG_CLK_EN3 (addr=0x5D) */ +#define DA732X_ADCA_BB_CLK_EN (1 << 0) +#define DA732X_ADCC_BB_CLK_EN (1 << 4) + +/* DA732X_REG_CLK_EN4 (addr=0x5E) */ +#define DA732X_DACA_BB_CLK_EN (1 << 0) +#define DA732X_DACC_BB_CLK_EN (1 << 4) +#define DA732X_DACA_BB_CLK_SHIFT 0 +#define DA732X_DACC_BB_CLK_SHIFT 4 + +/* DA732X_REG_CLK_EN5 (addr=0x5F) */ +#define DA732X_DACE_BB_CLK_EN (1 << 0) +#define DA732X_DACE_BB_CLK_SHIFT 0 + +/* DA732X_REG_AIF_MCLK (addr=0x60) */ +#define DA732X_AIFM_FRAME_64 (1 << 2) +#define DA732X_AIFM_SRC_SEL_AIFA (1 << 6) +#define DA732X_CLK_GENERATION_AIF_A (1 << 4) +#define DA732X_NO_CLK_GENERATION 0x0 + +/* DA732X_REG_AIFA1 (addr=0x61) */ +#define DA732X_AIF_WORD_MASK (0x3 << 0) +#define DA732X_AIF_WORD_16 (0 << 0) +#define DA732X_AIF_WORD_20 (1 << 0) +#define DA732X_AIF_WORD_24 (2 << 0) +#define DA732X_AIF_WORD_32 (3 << 0) +#define DA732X_AIF_TDM_MONO_SHIFT (1 << 6) +#define DA732X_AIF1_CLK_MASK (1 << 7) +#define DA732X_AIF_SLAVE (0 << 7) +#define DA732X_AIF_CLK_FROM_SRC (1 << 7) + +/* DA732X_REG_AIFA3 (addr=0x63) */ +#define DA732X_AIF_MODE_SHIFT 0 +#define DA732X_AIF_MODE_MASK 0x3 +#define DA732X_AIF_I2S_MODE (0 << 0) +#define DA732X_AIF_LEFT_J_MODE (1 << 0) +#define DA732X_AIF_RIGHT_J_MODE (2 << 0) +#define DA732X_AIF_DSP_MODE (3 << 0) +#define DA732X_AIF_WCLK_INV (1 << 4) +#define DA732X_AIF_BCLK_INV (1 << 5) +#define DA732X_AIF_EN (1 << 7) +#define DA732X_AIF_EN_SHIFT 7 + +/* DA732X_REG_PC_CTRL (addr=0x6a) */ +#define DA732X_PC_PULSE_AIFA (0 << 0) +#define DA732X_PC_PULSE_AIFB (1 << 0) +#define DA732X_PC_RESYNC_AUT (1 << 6) +#define DA732X_PC_RESYNC_NOT_AUT (0 << 6) +#define DA732X_PC_SAME (1 << 7) + +/* DA732X_REG_DATA_ROUTE (addr=0x70) */ +#define DA732X_ADC1_TO_AIFA (0 << 0) +#define DA732X_DSP_TO_AIFA (1 << 0) +#define DA732X_ADC2_TO_AIFB (0 << 1) +#define DA732X_DSP_TO_AIFB (1 << 1) +#define DA732X_AIFA_TO_DAC1L (0 << 2) +#define DA732X_DSP_TO_DAC1L (1 << 2) +#define DA732X_AIFA_TO_DAC1R (0 << 3) +#define DA732X_DSP_TO_DAC1R (1 << 3) +#define DA732X_AIFB_TO_DAC2L (0 << 4) +#define DA732X_DSP_TO_DAC2L (1 << 4) +#define DA732X_AIFB_TO_DAC2R (0 << 5) +#define DA732X_DSP_TO_DAC2R (1 << 5) +#define DA732X_AIFB_TO_DAC3 (0 << 6) +#define DA732X_DSP_TO_DAC3 (1 << 6) +#define DA732X_BYPASS_DSP (0 << 0) +#define DA732X_ALL_TO_DSP (0x7F << 0) + +/* DA732X_REG_DSP_CTRL (addr=0x71) */ +#define DA732X_DIGITAL_EN (1 << 0) +#define DA732X_DIGITAL_RESET (0 << 0) +#define DA732X_DSP_CORE_EN (1 << 1) +#define DA732X_DSP_CORE_RESET (0 << 1) + +/* DA732X_REG_SPARE1_OUT (addr=0x7D)*/ +#define DA732X_HP_DRIVER_EN (1 << 0) +#define DA732X_HP_GATE_LOW (1 << 2) +#define DA732X_HP_LOOP_GAIN_CTRL (1 << 3) + +/* DA732X_REG_ID (addr=0x81)*/ +#define DA732X_ID_MINOR_MASK (0xF << 0) +#define DA732X_ID_MAJOR_MASK (0xF << 4) + +/* DA732X_REG_ADC1/2_PD (addr=0x90/0x98) */ +#define DA732X_ADC_RST_MASK (0x3 << 0) +#define DA732X_ADC_PD_MASK (0x3 << 2) +#define DA732X_ADC_SET_ACT (0x3 << 0) +#define DA732X_ADC_SET_RST (0x0 << 0) +#define DA732X_ADC_ON (0x3 << 2) +#define DA732X_ADC_OFF (0x0 << 2) + +/* DA732X_REG_ADC1/2_SEL (addr=0x94/0x9C) */ +#define DA732X_ADC_VOL_VAL_MASK 0x7 +#define DA732X_ADCL_VOL_SHIFT 0 +#define DA732X_ADCR_VOL_SHIFT 4 +#define DA732X_ADCL_EN_SHIFT 2 +#define DA732X_ADCR_EN_SHIFT 3 +#define DA732X_ADCL_EN (1 << 2) +#define DA732X_ADCR_EN (1 << 3) +#define DA732X_ADC_VOL_VAL_MAX DA732X_ADC_VOL_VAL_MASK + +/* + * DA732X_REG_ADC1/2_HPF (addr=0x93/0x9b) + * DA732x_REG_DAC1/2/3_HPG (addr=0xA5/0xB5/0xC5) + */ +#define DA732X_HPF_MUSIC_EN (1 << 3) +#define DA732X_HPF_VOICE_EN ((1 << 3) | (1 << 7)) +#define DA732X_HPF_MASK ((1 << 3) | (1 << 7)) +#define DA732X_HPF_DIS ((0 << 3) | (0 << 7)) + +/* DA732X_REG_DAC1/2/3_VOL */ +#define DA732X_DAC_VOL_VAL_MASK 0x7F +#define DA732X_DAC_VOL_SHIFT 0 +#define DA732X_DAC_VOL_VAL_MAX DA732X_DAC_VOL_VAL_MASK + +/* DA732X_REG_DAC1/2/3_SEL (addr=0xA3/0xB3/0xC3) */ +#define DA732X_DACL_EN_SHIFT 3 +#define DA732X_DACR_EN_SHIFT 7 +#define DA732X_DACL_MUTE_SHIFT 2 +#define DA732X_DACR_MUTE_SHIFT 6 +#define DA732X_DACL_EN (1 << 3) +#define DA732X_DACR_EN (1 << 7) +#define DA732X_DACL_SDM (1 << 0) +#define DA732X_DACR_SDM (1 << 4) +#define DA732X_DACL_MUTE (1 << 2) +#define DA732X_DACR_MUTE (1 << 6) + +/* DA732X_REG_DAC_SOFTMUTE (addr=0xA4/0xB4/0xC4) */ +#define DA732X_SOFTMUTE_EN (1 << 7) +#define DA732X_GAIN_RAMPED (1 << 6) +#define DA732X_16_SAMPLES (4 << 0) +#define DA732X_SOFTMUTE_MASK (1 << 7) +#define DA732X_SOFTMUTE_SHIFT 7 + +/* + * DA732x_REG_ADC1/2_EQ12 (addr=0x95/0x9D) + * DA732x_REG_ADC1/2_EQ34 (addr=0x96/0x9E) + * DA732x_REG_ADC1/2_EQ5 (addr=0x97/0x9F) + * DA732x_REG_DAC1/2/3_EQ12 (addr=0xA5/0xB5/0xC5) + * DA732x_REG_DAC1/2/3_EQ34 (addr=0xA6/0xB6/0xC6) + * DA732x_REG_DAC1/2/3_EQ5 (addr=0xA7/0xB7/0xB7) + */ +#define DA732X_EQ_VOL_VAL_MASK 0xF +#define DA732X_EQ_BAND1_SHIFT 0 +#define DA732X_EQ_BAND2_SHIFT 4 +#define DA732X_EQ_BAND3_SHIFT 0 +#define DA732X_EQ_BAND4_SHIFT 4 +#define DA732X_EQ_BAND5_SHIFT 0 +#define DA732X_EQ_OVERALL_SHIFT 4 +#define DA732X_EQ_OVERALL_VOL_VAL_MASK 0x3 +#define DA732X_EQ_DIS (0 << 7) +#define DA732X_EQ_EN (1 << 7) +#define DA732X_EQ_EN_SHIFT 7 +#define DA732X_EQ_VOL_VAL_MAX DA732X_EQ_VOL_VAL_MASK +#define DA732X_EQ_OVERALL_VOL_VAL_MAX DA732X_EQ_OVERALL_VOL_VAL_MASK + +/* DA732X_REG_DMA_CMD (addr=0xD3) */ +#define DA732X_SEL_DSP_DMA_MASK (3 << 0) +#define DA732X_SEL_DSP_DMA_DIS (0 << 0) +#define DA732X_SEL_DSP_DMA_PMEM (1 << 0) +#define DA732X_SEL_DSP_DMA_XMEM (2 << 0) +#define DA732X_SEL_DSP_DMA_YMEM (3 << 0) +#define DA732X_DSP_RW_MASK (1 << 4) +#define DA732X_DSP_DMA_WRITE (0 << 4) +#define DA732X_DSP_DMA_READ (1 << 4) + +/* DA732X_REG_DMA_STATUS (addr=0xDA) */ +#define DA732X_DSP_DMA_FREE (0 << 0) +#define DA732X_DSP_DMA_BUSY (1 << 0) + +#endif /* __DA732X_REG_H_ */ diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c new file mode 100644 index 0000000000..ae20086777 --- /dev/null +++ b/sound/soc/codecs/da9055.c @@ -0,0 +1,1542 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DA9055 ALSA Soc codec driver + * + * Copyright (c) 2012 Dialog Semiconductor + * + * Tested on (Samsung SMDK6410 board + DA9055 EVB) using I2S and I2C + * Written by David Chen and + * Ashish Chavan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DA9055 register space */ + +/* Status Registers */ +#define DA9055_STATUS1 0x02 +#define DA9055_PLL_STATUS 0x03 +#define DA9055_AUX_L_GAIN_STATUS 0x04 +#define DA9055_AUX_R_GAIN_STATUS 0x05 +#define DA9055_MIC_L_GAIN_STATUS 0x06 +#define DA9055_MIC_R_GAIN_STATUS 0x07 +#define DA9055_MIXIN_L_GAIN_STATUS 0x08 +#define DA9055_MIXIN_R_GAIN_STATUS 0x09 +#define DA9055_ADC_L_GAIN_STATUS 0x0A +#define DA9055_ADC_R_GAIN_STATUS 0x0B +#define DA9055_DAC_L_GAIN_STATUS 0x0C +#define DA9055_DAC_R_GAIN_STATUS 0x0D +#define DA9055_HP_L_GAIN_STATUS 0x0E +#define DA9055_HP_R_GAIN_STATUS 0x0F +#define DA9055_LINE_GAIN_STATUS 0x10 + +/* System Initialisation Registers */ +#define DA9055_CIF_CTRL 0x20 +#define DA9055_DIG_ROUTING_AIF 0X21 +#define DA9055_SR 0x22 +#define DA9055_REFERENCES 0x23 +#define DA9055_PLL_FRAC_TOP 0x24 +#define DA9055_PLL_FRAC_BOT 0x25 +#define DA9055_PLL_INTEGER 0x26 +#define DA9055_PLL_CTRL 0x27 +#define DA9055_AIF_CLK_MODE 0x28 +#define DA9055_AIF_CTRL 0x29 +#define DA9055_DIG_ROUTING_DAC 0x2A +#define DA9055_ALC_CTRL1 0x2B + +/* Input - Gain, Select and Filter Registers */ +#define DA9055_AUX_L_GAIN 0x30 +#define DA9055_AUX_R_GAIN 0x31 +#define DA9055_MIXIN_L_SELECT 0x32 +#define DA9055_MIXIN_R_SELECT 0x33 +#define DA9055_MIXIN_L_GAIN 0x34 +#define DA9055_MIXIN_R_GAIN 0x35 +#define DA9055_ADC_L_GAIN 0x36 +#define DA9055_ADC_R_GAIN 0x37 +#define DA9055_ADC_FILTERS1 0x38 +#define DA9055_MIC_L_GAIN 0x39 +#define DA9055_MIC_R_GAIN 0x3A + +/* Output - Gain, Select and Filter Registers */ +#define DA9055_DAC_FILTERS5 0x40 +#define DA9055_DAC_FILTERS2 0x41 +#define DA9055_DAC_FILTERS3 0x42 +#define DA9055_DAC_FILTERS4 0x43 +#define DA9055_DAC_FILTERS1 0x44 +#define DA9055_DAC_L_GAIN 0x45 +#define DA9055_DAC_R_GAIN 0x46 +#define DA9055_CP_CTRL 0x47 +#define DA9055_HP_L_GAIN 0x48 +#define DA9055_HP_R_GAIN 0x49 +#define DA9055_LINE_GAIN 0x4A +#define DA9055_MIXOUT_L_SELECT 0x4B +#define DA9055_MIXOUT_R_SELECT 0x4C + +/* System Controller Registers */ +#define DA9055_SYSTEM_MODES_INPUT 0x50 +#define DA9055_SYSTEM_MODES_OUTPUT 0x51 + +/* Control Registers */ +#define DA9055_AUX_L_CTRL 0x60 +#define DA9055_AUX_R_CTRL 0x61 +#define DA9055_MIC_BIAS_CTRL 0x62 +#define DA9055_MIC_L_CTRL 0x63 +#define DA9055_MIC_R_CTRL 0x64 +#define DA9055_MIXIN_L_CTRL 0x65 +#define DA9055_MIXIN_R_CTRL 0x66 +#define DA9055_ADC_L_CTRL 0x67 +#define DA9055_ADC_R_CTRL 0x68 +#define DA9055_DAC_L_CTRL 0x69 +#define DA9055_DAC_R_CTRL 0x6A +#define DA9055_HP_L_CTRL 0x6B +#define DA9055_HP_R_CTRL 0x6C +#define DA9055_LINE_CTRL 0x6D +#define DA9055_MIXOUT_L_CTRL 0x6E +#define DA9055_MIXOUT_R_CTRL 0x6F + +/* Configuration Registers */ +#define DA9055_LDO_CTRL 0x90 +#define DA9055_IO_CTRL 0x91 +#define DA9055_GAIN_RAMP_CTRL 0x92 +#define DA9055_MIC_CONFIG 0x93 +#define DA9055_PC_COUNT 0x94 +#define DA9055_CP_VOL_THRESHOLD1 0x95 +#define DA9055_CP_DELAY 0x96 +#define DA9055_CP_DETECTOR 0x97 +#define DA9055_AIF_OFFSET 0x98 +#define DA9055_DIG_CTRL 0x99 +#define DA9055_ALC_CTRL2 0x9A +#define DA9055_ALC_CTRL3 0x9B +#define DA9055_ALC_NOISE 0x9C +#define DA9055_ALC_TARGET_MIN 0x9D +#define DA9055_ALC_TARGET_MAX 0x9E +#define DA9055_ALC_GAIN_LIMITS 0x9F +#define DA9055_ALC_ANA_GAIN_LIMITS 0xA0 +#define DA9055_ALC_ANTICLIP_CTRL 0xA1 +#define DA9055_ALC_ANTICLIP_LEVEL 0xA2 +#define DA9055_ALC_OFFSET_OP2M_L 0xA6 +#define DA9055_ALC_OFFSET_OP2U_L 0xA7 +#define DA9055_ALC_OFFSET_OP2M_R 0xAB +#define DA9055_ALC_OFFSET_OP2U_R 0xAC +#define DA9055_ALC_CIC_OP_LVL_CTRL 0xAD +#define DA9055_ALC_CIC_OP_LVL_DATA 0xAE +#define DA9055_DAC_NG_SETUP_TIME 0xAF +#define DA9055_DAC_NG_OFF_THRESHOLD 0xB0 +#define DA9055_DAC_NG_ON_THRESHOLD 0xB1 +#define DA9055_DAC_NG_CTRL 0xB2 + +/* SR bit fields */ +#define DA9055_SR_8000 (0x1 << 0) +#define DA9055_SR_11025 (0x2 << 0) +#define DA9055_SR_12000 (0x3 << 0) +#define DA9055_SR_16000 (0x5 << 0) +#define DA9055_SR_22050 (0x6 << 0) +#define DA9055_SR_24000 (0x7 << 0) +#define DA9055_SR_32000 (0x9 << 0) +#define DA9055_SR_44100 (0xA << 0) +#define DA9055_SR_48000 (0xB << 0) +#define DA9055_SR_88200 (0xE << 0) +#define DA9055_SR_96000 (0xF << 0) + +/* REFERENCES bit fields */ +#define DA9055_BIAS_EN (1 << 3) +#define DA9055_VMID_EN (1 << 7) + +/* PLL_CTRL bit fields */ +#define DA9055_PLL_INDIV_10_20_MHZ (1 << 2) +#define DA9055_PLL_SRM_EN (1 << 6) +#define DA9055_PLL_EN (1 << 7) + +/* AIF_CLK_MODE bit fields */ +#define DA9055_AIF_BCLKS_PER_WCLK_32 (0 << 0) +#define DA9055_AIF_BCLKS_PER_WCLK_64 (1 << 0) +#define DA9055_AIF_BCLKS_PER_WCLK_128 (2 << 0) +#define DA9055_AIF_BCLKS_PER_WCLK_256 (3 << 0) +#define DA9055_AIF_CLK_EN_SLAVE_MODE (0 << 7) +#define DA9055_AIF_CLK_EN_MASTER_MODE (1 << 7) + +/* AIF_CTRL bit fields */ +#define DA9055_AIF_FORMAT_I2S_MODE (0 << 0) +#define DA9055_AIF_FORMAT_LEFT_J (1 << 0) +#define DA9055_AIF_FORMAT_RIGHT_J (2 << 0) +#define DA9055_AIF_FORMAT_DSP (3 << 0) +#define DA9055_AIF_WORD_S16_LE (0 << 2) +#define DA9055_AIF_WORD_S20_3LE (1 << 2) +#define DA9055_AIF_WORD_S24_LE (2 << 2) +#define DA9055_AIF_WORD_S32_LE (3 << 2) + +/* MIC_L_CTRL bit fields */ +#define DA9055_MIC_L_MUTE_EN (1 << 6) + +/* MIC_R_CTRL bit fields */ +#define DA9055_MIC_R_MUTE_EN (1 << 6) + +/* MIXIN_L_CTRL bit fields */ +#define DA9055_MIXIN_L_MIX_EN (1 << 3) + +/* MIXIN_R_CTRL bit fields */ +#define DA9055_MIXIN_R_MIX_EN (1 << 3) + +/* ADC_L_CTRL bit fields */ +#define DA9055_ADC_L_EN (1 << 7) + +/* ADC_R_CTRL bit fields */ +#define DA9055_ADC_R_EN (1 << 7) + +/* DAC_L_CTRL bit fields */ +#define DA9055_DAC_L_MUTE_EN (1 << 6) + +/* DAC_R_CTRL bit fields */ +#define DA9055_DAC_R_MUTE_EN (1 << 6) + +/* HP_L_CTRL bit fields */ +#define DA9055_HP_L_AMP_OE (1 << 3) + +/* HP_R_CTRL bit fields */ +#define DA9055_HP_R_AMP_OE (1 << 3) + +/* LINE_CTRL bit fields */ +#define DA9055_LINE_AMP_OE (1 << 3) + +/* MIXOUT_L_CTRL bit fields */ +#define DA9055_MIXOUT_L_MIX_EN (1 << 3) + +/* MIXOUT_R_CTRL bit fields */ +#define DA9055_MIXOUT_R_MIX_EN (1 << 3) + +/* MIC bias select bit fields */ +#define DA9055_MICBIAS2_EN (1 << 6) + +/* ALC_CIC_OP_LEVEL_CTRL bit fields */ +#define DA9055_ALC_DATA_MIDDLE (2 << 0) +#define DA9055_ALC_DATA_TOP (3 << 0) +#define DA9055_ALC_CIC_OP_CHANNEL_LEFT (0 << 7) +#define DA9055_ALC_CIC_OP_CHANNEL_RIGHT (1 << 7) + +#define DA9055_AIF_BCLK_MASK (3 << 0) +#define DA9055_AIF_CLK_MODE_MASK (1 << 7) +#define DA9055_AIF_FORMAT_MASK (3 << 0) +#define DA9055_AIF_WORD_LENGTH_MASK (3 << 2) +#define DA9055_GAIN_RAMPING_EN (1 << 5) +#define DA9055_MICBIAS_LEVEL_MASK (3 << 4) + +#define DA9055_ALC_OFFSET_15_8 0x00FF00 +#define DA9055_ALC_OFFSET_17_16 0x030000 +#define DA9055_ALC_AVG_ITERATIONS 5 + +struct pll_div { + int fref; + int fout; + u8 frac_top; + u8 frac_bot; + u8 integer; + u8 mode; /* 0 = slave, 1 = master */ +}; + +/* PLL divisor table */ +static const struct pll_div da9055_pll_div[] = { + /* for MASTER mode, fs = 44.1Khz and its harmonics */ + {11289600, 2822400, 0x00, 0x00, 0x20, 1}, /* MCLK=11.2896Mhz */ + {12000000, 2822400, 0x03, 0x61, 0x1E, 1}, /* MCLK=12Mhz */ + {12288000, 2822400, 0x0C, 0xCC, 0x1D, 1}, /* MCLK=12.288Mhz */ + {13000000, 2822400, 0x19, 0x45, 0x1B, 1}, /* MCLK=13Mhz */ + {13500000, 2822400, 0x18, 0x56, 0x1A, 1}, /* MCLK=13.5Mhz */ + {14400000, 2822400, 0x02, 0xD0, 0x19, 1}, /* MCLK=14.4Mhz */ + {19200000, 2822400, 0x1A, 0x1C, 0x12, 1}, /* MCLK=19.2Mhz */ + {19680000, 2822400, 0x0B, 0x6D, 0x12, 1}, /* MCLK=19.68Mhz */ + {19800000, 2822400, 0x07, 0xDD, 0x12, 1}, /* MCLK=19.8Mhz */ + /* for MASTER mode, fs = 48Khz and its harmonics */ + {11289600, 3072000, 0x1A, 0x8E, 0x22, 1}, /* MCLK=11.2896Mhz */ + {12000000, 3072000, 0x18, 0x93, 0x20, 1}, /* MCLK=12Mhz */ + {12288000, 3072000, 0x00, 0x00, 0x20, 1}, /* MCLK=12.288Mhz */ + {13000000, 3072000, 0x07, 0xEA, 0x1E, 1}, /* MCLK=13Mhz */ + {13500000, 3072000, 0x04, 0x11, 0x1D, 1}, /* MCLK=13.5Mhz */ + {14400000, 3072000, 0x09, 0xD0, 0x1B, 1}, /* MCLK=14.4Mhz */ + {19200000, 3072000, 0x0F, 0x5C, 0x14, 1}, /* MCLK=19.2Mhz */ + {19680000, 3072000, 0x1F, 0x60, 0x13, 1}, /* MCLK=19.68Mhz */ + {19800000, 3072000, 0x1B, 0x80, 0x13, 1}, /* MCLK=19.8Mhz */ + /* for SLAVE mode with SRM */ + {11289600, 2822400, 0x0D, 0x47, 0x21, 0}, /* MCLK=11.2896Mhz */ + {12000000, 2822400, 0x0D, 0xFA, 0x1F, 0}, /* MCLK=12Mhz */ + {12288000, 2822400, 0x16, 0x66, 0x1E, 0}, /* MCLK=12.288Mhz */ + {13000000, 2822400, 0x00, 0x98, 0x1D, 0}, /* MCLK=13Mhz */ + {13500000, 2822400, 0x1E, 0x33, 0x1B, 0}, /* MCLK=13.5Mhz */ + {14400000, 2822400, 0x06, 0x50, 0x1A, 0}, /* MCLK=14.4Mhz */ + {19200000, 2822400, 0x14, 0xBC, 0x13, 0}, /* MCLK=19.2Mhz */ + {19680000, 2822400, 0x05, 0x66, 0x13, 0}, /* MCLK=19.68Mhz */ + {19800000, 2822400, 0x01, 0xAE, 0x13, 0}, /* MCLK=19.8Mhz */ +}; + +enum clk_src { + DA9055_CLKSRC_MCLK +}; + +/* Gain and Volume */ + +static const DECLARE_TLV_DB_RANGE(aux_vol_tlv, + 0x0, 0x10, TLV_DB_SCALE_ITEM(-5400, 0, 0), + /* -54dB to 15dB */ + 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0) +); + +static const DECLARE_TLV_DB_RANGE(digital_gain_tlv, + 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -78dB to 12dB */ + 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0) +); + +static const DECLARE_TLV_DB_RANGE(alc_analog_gain_tlv, + 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* 0dB to 36dB */ + 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0) +); + +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0); +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0); +static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0); + +/* ADC and DAC high pass filter cutoff value */ +static const char * const da9055_hpf_cutoff_txt[] = { + "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_dac_hpf_cutoff, + DA9055_DAC_FILTERS1, 4, da9055_hpf_cutoff_txt); + +static SOC_ENUM_SINGLE_DECL(da9055_adc_hpf_cutoff, + DA9055_ADC_FILTERS1, 4, da9055_hpf_cutoff_txt); + +/* ADC and DAC voice mode (8kHz) high pass cutoff value */ +static const char * const da9055_vf_cutoff_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_dac_vf_cutoff, + DA9055_DAC_FILTERS1, 0, da9055_vf_cutoff_txt); + +static SOC_ENUM_SINGLE_DECL(da9055_adc_vf_cutoff, + DA9055_ADC_FILTERS1, 0, da9055_vf_cutoff_txt); + +/* Gain ramping rate value */ +static const char * const da9055_gain_ramping_txt[] = { + "nominal rate", "nominal rate * 4", "nominal rate * 8", + "nominal rate / 8" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_gain_ramping_rate, + DA9055_GAIN_RAMP_CTRL, 0, da9055_gain_ramping_txt); + +/* DAC noise gate setup time value */ +static const char * const da9055_dac_ng_setup_time_txt[] = { + "256 samples", "512 samples", "1024 samples", "2048 samples" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_setup_time, + DA9055_DAC_NG_SETUP_TIME, 0, + da9055_dac_ng_setup_time_txt); + +/* DAC noise gate rampup rate value */ +static const char * const da9055_dac_ng_rampup_txt[] = { + "0.02 ms/dB", "0.16 ms/dB" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_rampup_rate, + DA9055_DAC_NG_SETUP_TIME, 2, + da9055_dac_ng_rampup_txt); + +/* DAC noise gate rampdown rate value */ +static const char * const da9055_dac_ng_rampdown_txt[] = { + "0.64 ms/dB", "20.48 ms/dB" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_dac_ng_rampdown_rate, + DA9055_DAC_NG_SETUP_TIME, 3, + da9055_dac_ng_rampdown_txt); + +/* DAC soft mute rate value */ +static const char * const da9055_dac_soft_mute_rate_txt[] = { + "1", "2", "4", "8", "16", "32", "64" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_dac_soft_mute_rate, + DA9055_DAC_FILTERS5, 4, + da9055_dac_soft_mute_rate_txt); + +/* DAC routing select */ +static const char * const da9055_dac_src_txt[] = { + "ADC output left", "ADC output right", "AIF input left", + "AIF input right" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_dac_l_src, + DA9055_DIG_ROUTING_DAC, 0, da9055_dac_src_txt); + +static SOC_ENUM_SINGLE_DECL(da9055_dac_r_src, + DA9055_DIG_ROUTING_DAC, 4, da9055_dac_src_txt); + +/* MIC PGA Left source select */ +static const char * const da9055_mic_l_src_txt[] = { + "MIC1_P_N", "MIC1_P", "MIC1_N", "MIC2_L" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_mic_l_src, + DA9055_MIXIN_L_SELECT, 4, da9055_mic_l_src_txt); + +/* MIC PGA Right source select */ +static const char * const da9055_mic_r_src_txt[] = { + "MIC2_R_L", "MIC2_R", "MIC2_L" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_mic_r_src, + DA9055_MIXIN_R_SELECT, 4, da9055_mic_r_src_txt); + +/* ALC Input Signal Tracking rate select */ +static const char * const da9055_signal_tracking_rate_txt[] = { + "1/4", "1/16", "1/256", "1/65536" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_integ_attack_rate, + DA9055_ALC_CTRL3, 4, + da9055_signal_tracking_rate_txt); + +static SOC_ENUM_SINGLE_DECL(da9055_integ_release_rate, + DA9055_ALC_CTRL3, 6, + da9055_signal_tracking_rate_txt); + +/* ALC Attack Rate select */ +static const char * const da9055_attack_rate_txt[] = { + "44/fs", "88/fs", "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", + "5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_attack_rate, + DA9055_ALC_CTRL2, 0, da9055_attack_rate_txt); + +/* ALC Release Rate select */ +static const char * const da9055_release_rate_txt[] = { + "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", "5632/fs", + "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_release_rate, + DA9055_ALC_CTRL2, 4, da9055_release_rate_txt); + +/* ALC Hold Time select */ +static const char * const da9055_hold_time_txt[] = { + "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", + "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", + "253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static SOC_ENUM_SINGLE_DECL(da9055_hold_time, + DA9055_ALC_CTRL3, 0, da9055_hold_time_txt); + +static int da9055_get_alc_data(struct snd_soc_component *component, u8 reg_val) +{ + int mid_data, top_data; + int sum = 0; + u8 iteration; + + for (iteration = 0; iteration < DA9055_ALC_AVG_ITERATIONS; + iteration++) { + /* Select the left or right channel and capture data */ + snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL, reg_val); + + /* Select middle 8 bits for read back from data register */ + snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL, + reg_val | DA9055_ALC_DATA_MIDDLE); + mid_data = snd_soc_component_read(component, DA9055_ALC_CIC_OP_LVL_DATA); + + /* Select top 8 bits for read back from data register */ + snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL, + reg_val | DA9055_ALC_DATA_TOP); + top_data = snd_soc_component_read(component, DA9055_ALC_CIC_OP_LVL_DATA); + + sum += ((mid_data << 8) | (top_data << 16)); + } + + return sum / DA9055_ALC_AVG_ITERATIONS; +} + +static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + u8 reg_val, adc_left, adc_right, mic_left, mic_right; + int avg_left_data, avg_right_data, offset_l, offset_r; + + if (ucontrol->value.integer.value[0]) { + /* + * While enabling ALC (or ALC sync mode), calibration of the DC + * offsets must be done first + */ + + /* Save current values from Mic control registers */ + mic_left = snd_soc_component_read(component, DA9055_MIC_L_CTRL); + mic_right = snd_soc_component_read(component, DA9055_MIC_R_CTRL); + + /* Mute Mic PGA Left and Right */ + snd_soc_component_update_bits(component, DA9055_MIC_L_CTRL, + DA9055_MIC_L_MUTE_EN, DA9055_MIC_L_MUTE_EN); + snd_soc_component_update_bits(component, DA9055_MIC_R_CTRL, + DA9055_MIC_R_MUTE_EN, DA9055_MIC_R_MUTE_EN); + + /* Save current values from ADC control registers */ + adc_left = snd_soc_component_read(component, DA9055_ADC_L_CTRL); + adc_right = snd_soc_component_read(component, DA9055_ADC_R_CTRL); + + /* Enable ADC Left and Right */ + snd_soc_component_update_bits(component, DA9055_ADC_L_CTRL, + DA9055_ADC_L_EN, DA9055_ADC_L_EN); + snd_soc_component_update_bits(component, DA9055_ADC_R_CTRL, + DA9055_ADC_R_EN, DA9055_ADC_R_EN); + + /* Calculate average for Left and Right data */ + /* Left Data */ + avg_left_data = da9055_get_alc_data(component, + DA9055_ALC_CIC_OP_CHANNEL_LEFT); + /* Right Data */ + avg_right_data = da9055_get_alc_data(component, + DA9055_ALC_CIC_OP_CHANNEL_RIGHT); + + /* Calculate DC offset */ + offset_l = -avg_left_data; + offset_r = -avg_right_data; + + reg_val = (offset_l & DA9055_ALC_OFFSET_15_8) >> 8; + snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2M_L, reg_val); + reg_val = (offset_l & DA9055_ALC_OFFSET_17_16) >> 16; + snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2U_L, reg_val); + + reg_val = (offset_r & DA9055_ALC_OFFSET_15_8) >> 8; + snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2M_R, reg_val); + reg_val = (offset_r & DA9055_ALC_OFFSET_17_16) >> 16; + snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2U_R, reg_val); + + /* Restore original values of ADC control registers */ + snd_soc_component_write(component, DA9055_ADC_L_CTRL, adc_left); + snd_soc_component_write(component, DA9055_ADC_R_CTRL, adc_right); + + /* Restore original values of Mic control registers */ + snd_soc_component_write(component, DA9055_MIC_L_CTRL, mic_left); + snd_soc_component_write(component, DA9055_MIC_R_CTRL, mic_right); + } + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +static const struct snd_kcontrol_new da9055_snd_controls[] = { + + /* Volume controls */ + SOC_DOUBLE_R_TLV("Mic Volume", + DA9055_MIC_L_GAIN, DA9055_MIC_R_GAIN, + 0, 0x7, 0, mic_vol_tlv), + SOC_DOUBLE_R_TLV("Aux Volume", + DA9055_AUX_L_GAIN, DA9055_AUX_R_GAIN, + 0, 0x3f, 0, aux_vol_tlv), + SOC_DOUBLE_R_TLV("Mixin PGA Volume", + DA9055_MIXIN_L_GAIN, DA9055_MIXIN_R_GAIN, + 0, 0xf, 0, mixin_gain_tlv), + SOC_DOUBLE_R_TLV("ADC Volume", + DA9055_ADC_L_GAIN, DA9055_ADC_R_GAIN, + 0, 0x7f, 0, digital_gain_tlv), + + SOC_DOUBLE_R_TLV("DAC Volume", + DA9055_DAC_L_GAIN, DA9055_DAC_R_GAIN, + 0, 0x7f, 0, digital_gain_tlv), + SOC_DOUBLE_R_TLV("Headphone Volume", + DA9055_HP_L_GAIN, DA9055_HP_R_GAIN, + 0, 0x3f, 0, hp_vol_tlv), + SOC_SINGLE_TLV("Lineout Volume", DA9055_LINE_GAIN, 0, 0x3f, 0, + lineout_vol_tlv), + + /* DAC Equalizer controls */ + SOC_SINGLE("DAC EQ Switch", DA9055_DAC_FILTERS4, 7, 1, 0), + SOC_SINGLE_TLV("DAC EQ1 Volume", DA9055_DAC_FILTERS2, 0, 0xf, 0, + eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ2 Volume", DA9055_DAC_FILTERS2, 4, 0xf, 0, + eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ3 Volume", DA9055_DAC_FILTERS3, 0, 0xf, 0, + eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ4 Volume", DA9055_DAC_FILTERS3, 4, 0xf, 0, + eq_gain_tlv), + SOC_SINGLE_TLV("DAC EQ5 Volume", DA9055_DAC_FILTERS4, 0, 0xf, 0, + eq_gain_tlv), + + /* High Pass Filter and Voice Mode controls */ + SOC_SINGLE("ADC HPF Switch", DA9055_ADC_FILTERS1, 7, 1, 0), + SOC_ENUM("ADC HPF Cutoff", da9055_adc_hpf_cutoff), + SOC_SINGLE("ADC Voice Mode Switch", DA9055_ADC_FILTERS1, 3, 1, 0), + SOC_ENUM("ADC Voice Cutoff", da9055_adc_vf_cutoff), + + SOC_SINGLE("DAC HPF Switch", DA9055_DAC_FILTERS1, 7, 1, 0), + SOC_ENUM("DAC HPF Cutoff", da9055_dac_hpf_cutoff), + SOC_SINGLE("DAC Voice Mode Switch", DA9055_DAC_FILTERS1, 3, 1, 0), + SOC_ENUM("DAC Voice Cutoff", da9055_dac_vf_cutoff), + + /* Mute controls */ + SOC_DOUBLE_R("Mic Switch", DA9055_MIC_L_CTRL, + DA9055_MIC_R_CTRL, 6, 1, 0), + SOC_DOUBLE_R("Aux Switch", DA9055_AUX_L_CTRL, + DA9055_AUX_R_CTRL, 6, 1, 0), + SOC_DOUBLE_R("Mixin PGA Switch", DA9055_MIXIN_L_CTRL, + DA9055_MIXIN_R_CTRL, 6, 1, 0), + SOC_DOUBLE_R("ADC Switch", DA9055_ADC_L_CTRL, + DA9055_ADC_R_CTRL, 6, 1, 0), + SOC_DOUBLE_R("Headphone Switch", DA9055_HP_L_CTRL, + DA9055_HP_R_CTRL, 6, 1, 0), + SOC_SINGLE("Lineout Switch", DA9055_LINE_CTRL, 6, 1, 0), + SOC_SINGLE("DAC Soft Mute Switch", DA9055_DAC_FILTERS5, 7, 1, 0), + SOC_ENUM("DAC Soft Mute Rate", da9055_dac_soft_mute_rate), + + /* Zero Cross controls */ + SOC_DOUBLE_R("Aux ZC Switch", DA9055_AUX_L_CTRL, + DA9055_AUX_R_CTRL, 4, 1, 0), + SOC_DOUBLE_R("Mixin PGA ZC Switch", DA9055_MIXIN_L_CTRL, + DA9055_MIXIN_R_CTRL, 4, 1, 0), + SOC_DOUBLE_R("Headphone ZC Switch", DA9055_HP_L_CTRL, + DA9055_HP_R_CTRL, 4, 1, 0), + SOC_SINGLE("Lineout ZC Switch", DA9055_LINE_CTRL, 4, 1, 0), + + /* Gain Ramping controls */ + SOC_DOUBLE_R("Aux Gain Ramping Switch", DA9055_AUX_L_CTRL, + DA9055_AUX_R_CTRL, 5, 1, 0), + SOC_DOUBLE_R("Mixin Gain Ramping Switch", DA9055_MIXIN_L_CTRL, + DA9055_MIXIN_R_CTRL, 5, 1, 0), + SOC_DOUBLE_R("ADC Gain Ramping Switch", DA9055_ADC_L_CTRL, + DA9055_ADC_R_CTRL, 5, 1, 0), + SOC_DOUBLE_R("DAC Gain Ramping Switch", DA9055_DAC_L_CTRL, + DA9055_DAC_R_CTRL, 5, 1, 0), + SOC_DOUBLE_R("Headphone Gain Ramping Switch", DA9055_HP_L_CTRL, + DA9055_HP_R_CTRL, 5, 1, 0), + SOC_SINGLE("Lineout Gain Ramping Switch", DA9055_LINE_CTRL, 5, 1, 0), + SOC_ENUM("Gain Ramping Rate", da9055_gain_ramping_rate), + + /* DAC Noise Gate controls */ + SOC_SINGLE("DAC NG Switch", DA9055_DAC_NG_CTRL, 7, 1, 0), + SOC_SINGLE("DAC NG ON Threshold", DA9055_DAC_NG_ON_THRESHOLD, + 0, 0x7, 0), + SOC_SINGLE("DAC NG OFF Threshold", DA9055_DAC_NG_OFF_THRESHOLD, + 0, 0x7, 0), + SOC_ENUM("DAC NG Setup Time", da9055_dac_ng_setup_time), + SOC_ENUM("DAC NG Rampup Rate", da9055_dac_ng_rampup_rate), + SOC_ENUM("DAC NG Rampdown Rate", da9055_dac_ng_rampdown_rate), + + /* DAC Invertion control */ + SOC_SINGLE("DAC Left Invert", DA9055_DIG_CTRL, 3, 1, 0), + SOC_SINGLE("DAC Right Invert", DA9055_DIG_CTRL, 7, 1, 0), + + /* DMIC controls */ + SOC_DOUBLE_R("DMIC Switch", DA9055_MIXIN_L_SELECT, + DA9055_MIXIN_R_SELECT, 7, 1, 0), + + /* ALC Controls */ + SOC_DOUBLE_EXT("ALC Switch", DA9055_ALC_CTRL1, 3, 7, 1, 0, + snd_soc_get_volsw, da9055_put_alc_sw), + SOC_SINGLE_EXT("ALC Sync Mode Switch", DA9055_ALC_CTRL1, 1, 1, 0, + snd_soc_get_volsw, da9055_put_alc_sw), + SOC_SINGLE("ALC Offset Switch", DA9055_ALC_CTRL1, 0, 1, 0), + SOC_SINGLE("ALC Anticlip Mode Switch", DA9055_ALC_ANTICLIP_CTRL, + 7, 1, 0), + SOC_SINGLE("ALC Anticlip Level", DA9055_ALC_ANTICLIP_LEVEL, + 0, 0x7f, 0), + SOC_SINGLE_TLV("ALC Min Threshold Volume", DA9055_ALC_TARGET_MIN, + 0, 0x3f, 1, alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Threshold Volume", DA9055_ALC_TARGET_MAX, + 0, 0x3f, 1, alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Noise Threshold Volume", DA9055_ALC_NOISE, + 0, 0x3f, 1, alc_threshold_tlv), + SOC_SINGLE_TLV("ALC Max Gain Volume", DA9055_ALC_GAIN_LIMITS, + 4, 0xf, 0, alc_gain_tlv), + SOC_SINGLE_TLV("ALC Max Attenuation Volume", DA9055_ALC_GAIN_LIMITS, + 0, 0xf, 0, alc_gain_tlv), + SOC_SINGLE_TLV("ALC Min Analog Gain Volume", + DA9055_ALC_ANA_GAIN_LIMITS, + 0, 0x7, 0, alc_analog_gain_tlv), + SOC_SINGLE_TLV("ALC Max Analog Gain Volume", + DA9055_ALC_ANA_GAIN_LIMITS, + 4, 0x7, 0, alc_analog_gain_tlv), + SOC_ENUM("ALC Attack Rate", da9055_attack_rate), + SOC_ENUM("ALC Release Rate", da9055_release_rate), + SOC_ENUM("ALC Hold Time", da9055_hold_time), + /* + * Rate at which input signal envelope is tracked as the signal gets + * larger + */ + SOC_ENUM("ALC Integ Attack Rate", da9055_integ_attack_rate), + /* + * Rate at which input signal envelope is tracked as the signal gets + * smaller + */ + SOC_ENUM("ALC Integ Release Rate", da9055_integ_release_rate), +}; + +/* DAPM Controls */ + +/* Mic PGA Left Source */ +static const struct snd_kcontrol_new da9055_mic_l_mux_controls = +SOC_DAPM_ENUM("Route", da9055_mic_l_src); + +/* Mic PGA Right Source */ +static const struct snd_kcontrol_new da9055_mic_r_mux_controls = +SOC_DAPM_ENUM("Route", da9055_mic_r_src); + +/* In Mixer Left */ +static const struct snd_kcontrol_new da9055_dapm_mixinl_controls[] = { + SOC_DAPM_SINGLE("Aux Left Switch", DA9055_MIXIN_L_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("Mic Left Switch", DA9055_MIXIN_L_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("Mic Right Switch", DA9055_MIXIN_L_SELECT, 2, 1, 0), +}; + +/* In Mixer Right */ +static const struct snd_kcontrol_new da9055_dapm_mixinr_controls[] = { + SOC_DAPM_SINGLE("Aux Right Switch", DA9055_MIXIN_R_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("Mic Right Switch", DA9055_MIXIN_R_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("Mic Left Switch", DA9055_MIXIN_R_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXIN_R_SELECT, 3, 1, 0), +}; + +/* DAC Left Source */ +static const struct snd_kcontrol_new da9055_dac_l_mux_controls = +SOC_DAPM_ENUM("Route", da9055_dac_l_src); + +/* DAC Right Source */ +static const struct snd_kcontrol_new da9055_dac_r_mux_controls = +SOC_DAPM_ENUM("Route", da9055_dac_r_src); + +/* Out Mixer Left */ +static const struct snd_kcontrol_new da9055_dapm_mixoutl_controls[] = { + SOC_DAPM_SINGLE("Aux Left Switch", DA9055_MIXOUT_L_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXOUT_L_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("Mixin Right Switch", DA9055_MIXOUT_L_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("DAC Left Switch", DA9055_MIXOUT_L_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("Aux Left Invert Switch", DA9055_MIXOUT_L_SELECT, + 4, 1, 0), + SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA9055_MIXOUT_L_SELECT, + 5, 1, 0), + SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA9055_MIXOUT_L_SELECT, + 6, 1, 0), +}; + +/* Out Mixer Right */ +static const struct snd_kcontrol_new da9055_dapm_mixoutr_controls[] = { + SOC_DAPM_SINGLE("Aux Right Switch", DA9055_MIXOUT_R_SELECT, 0, 1, 0), + SOC_DAPM_SINGLE("Mixin Right Switch", DA9055_MIXOUT_R_SELECT, 1, 1, 0), + SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXOUT_R_SELECT, 2, 1, 0), + SOC_DAPM_SINGLE("DAC Right Switch", DA9055_MIXOUT_R_SELECT, 3, 1, 0), + SOC_DAPM_SINGLE("Aux Right Invert Switch", DA9055_MIXOUT_R_SELECT, + 4, 1, 0), + SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA9055_MIXOUT_R_SELECT, + 5, 1, 0), + SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA9055_MIXOUT_R_SELECT, + 6, 1, 0), +}; + +/* Headphone Output Enable */ +static const struct snd_kcontrol_new da9055_dapm_hp_l_control = +SOC_DAPM_SINGLE("Switch", DA9055_HP_L_CTRL, 3, 1, 0); + +static const struct snd_kcontrol_new da9055_dapm_hp_r_control = +SOC_DAPM_SINGLE("Switch", DA9055_HP_R_CTRL, 3, 1, 0); + +/* Lineout Output Enable */ +static const struct snd_kcontrol_new da9055_dapm_lineout_control = +SOC_DAPM_SINGLE("Switch", DA9055_LINE_CTRL, 3, 1, 0); + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget da9055_dapm_widgets[] = { + /* Input Side */ + + /* Input Lines */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("AUXL"), + SND_SOC_DAPM_INPUT("AUXR"), + + /* MUXs for Mic PGA source selection */ + SND_SOC_DAPM_MUX("Mic Left Source", SND_SOC_NOPM, 0, 0, + &da9055_mic_l_mux_controls), + SND_SOC_DAPM_MUX("Mic Right Source", SND_SOC_NOPM, 0, 0, + &da9055_mic_r_mux_controls), + + /* Input PGAs */ + SND_SOC_DAPM_PGA("Mic Left", DA9055_MIC_L_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mic Right", DA9055_MIC_R_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Aux Left", DA9055_AUX_L_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Aux Right", DA9055_AUX_R_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIXIN Left", DA9055_MIXIN_L_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIXIN Right", DA9055_MIXIN_R_CTRL, 7, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", DA9055_MIC_BIAS_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF", DA9055_AIF_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Charge Pump", DA9055_CP_CTRL, 7, 0, NULL, 0), + + /* Input Mixers */ + SND_SOC_DAPM_MIXER("In Mixer Left", SND_SOC_NOPM, 0, 0, + &da9055_dapm_mixinl_controls[0], + ARRAY_SIZE(da9055_dapm_mixinl_controls)), + SND_SOC_DAPM_MIXER("In Mixer Right", SND_SOC_NOPM, 0, 0, + &da9055_dapm_mixinr_controls[0], + ARRAY_SIZE(da9055_dapm_mixinr_controls)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC Left", "Capture", DA9055_ADC_L_CTRL, 7, 0), + SND_SOC_DAPM_ADC("ADC Right", "Capture", DA9055_ADC_R_CTRL, 7, 0), + + /* Output Side */ + + /* MUXs for DAC source selection */ + SND_SOC_DAPM_MUX("DAC Left Source", SND_SOC_NOPM, 0, 0, + &da9055_dac_l_mux_controls), + SND_SOC_DAPM_MUX("DAC Right Source", SND_SOC_NOPM, 0, 0, + &da9055_dac_r_mux_controls), + + /* AIF input */ + SND_SOC_DAPM_AIF_IN("AIFIN Left", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFIN Right", "Playback", 0, SND_SOC_NOPM, 0, 0), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC Left", "Playback", DA9055_DAC_L_CTRL, 7, 0), + SND_SOC_DAPM_DAC("DAC Right", "Playback", DA9055_DAC_R_CTRL, 7, 0), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Out Mixer Left", SND_SOC_NOPM, 0, 0, + &da9055_dapm_mixoutl_controls[0], + ARRAY_SIZE(da9055_dapm_mixoutl_controls)), + SND_SOC_DAPM_MIXER("Out Mixer Right", SND_SOC_NOPM, 0, 0, + &da9055_dapm_mixoutr_controls[0], + ARRAY_SIZE(da9055_dapm_mixoutr_controls)), + + /* Output Enable Switches */ + SND_SOC_DAPM_SWITCH("Headphone Left Enable", SND_SOC_NOPM, 0, 0, + &da9055_dapm_hp_l_control), + SND_SOC_DAPM_SWITCH("Headphone Right Enable", SND_SOC_NOPM, 0, 0, + &da9055_dapm_hp_r_control), + SND_SOC_DAPM_SWITCH("Lineout Enable", SND_SOC_NOPM, 0, 0, + &da9055_dapm_lineout_control), + + /* Output PGAs */ + SND_SOC_DAPM_PGA("MIXOUT Left", DA9055_MIXOUT_L_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIXOUT Right", DA9055_MIXOUT_R_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Lineout", DA9055_LINE_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Left", DA9055_HP_L_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Right", DA9055_HP_R_CTRL, 7, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LINE"), +}; + +/* DAPM audio route definition */ +static const struct snd_soc_dapm_route da9055_audio_map[] = { + /* Dest Connecting Widget source */ + + /* Input path */ + {"Mic Left Source", "MIC1_P_N", "MIC1"}, + {"Mic Left Source", "MIC1_P", "MIC1"}, + {"Mic Left Source", "MIC1_N", "MIC1"}, + {"Mic Left Source", "MIC2_L", "MIC2"}, + + {"Mic Right Source", "MIC2_R_L", "MIC2"}, + {"Mic Right Source", "MIC2_R", "MIC2"}, + {"Mic Right Source", "MIC2_L", "MIC2"}, + + {"Mic Left", NULL, "Mic Left Source"}, + {"Mic Right", NULL, "Mic Right Source"}, + + {"Aux Left", NULL, "AUXL"}, + {"Aux Right", NULL, "AUXR"}, + + {"In Mixer Left", "Mic Left Switch", "Mic Left"}, + {"In Mixer Left", "Mic Right Switch", "Mic Right"}, + {"In Mixer Left", "Aux Left Switch", "Aux Left"}, + + {"In Mixer Right", "Mic Right Switch", "Mic Right"}, + {"In Mixer Right", "Mic Left Switch", "Mic Left"}, + {"In Mixer Right", "Aux Right Switch", "Aux Right"}, + {"In Mixer Right", "Mixin Left Switch", "MIXIN Left"}, + + {"MIXIN Left", NULL, "In Mixer Left"}, + {"ADC Left", NULL, "MIXIN Left"}, + + {"MIXIN Right", NULL, "In Mixer Right"}, + {"ADC Right", NULL, "MIXIN Right"}, + + {"ADC Left", NULL, "AIF"}, + {"ADC Right", NULL, "AIF"}, + + /* Output path */ + {"AIFIN Left", NULL, "AIF"}, + {"AIFIN Right", NULL, "AIF"}, + + {"DAC Left Source", "ADC output left", "ADC Left"}, + {"DAC Left Source", "ADC output right", "ADC Right"}, + {"DAC Left Source", "AIF input left", "AIFIN Left"}, + {"DAC Left Source", "AIF input right", "AIFIN Right"}, + + {"DAC Right Source", "ADC output left", "ADC Left"}, + {"DAC Right Source", "ADC output right", "ADC Right"}, + {"DAC Right Source", "AIF input left", "AIFIN Left"}, + {"DAC Right Source", "AIF input right", "AIFIN Right"}, + + {"DAC Left", NULL, "DAC Left Source"}, + {"DAC Right", NULL, "DAC Right Source"}, + + {"Out Mixer Left", "Aux Left Switch", "Aux Left"}, + {"Out Mixer Left", "Mixin Left Switch", "MIXIN Left"}, + {"Out Mixer Left", "Mixin Right Switch", "MIXIN Right"}, + {"Out Mixer Left", "Aux Left Invert Switch", "Aux Left"}, + {"Out Mixer Left", "Mixin Left Invert Switch", "MIXIN Left"}, + {"Out Mixer Left", "Mixin Right Invert Switch", "MIXIN Right"}, + {"Out Mixer Left", "DAC Left Switch", "DAC Left"}, + + {"Out Mixer Right", "Aux Right Switch", "Aux Right"}, + {"Out Mixer Right", "Mixin Right Switch", "MIXIN Right"}, + {"Out Mixer Right", "Mixin Left Switch", "MIXIN Left"}, + {"Out Mixer Right", "Aux Right Invert Switch", "Aux Right"}, + {"Out Mixer Right", "Mixin Right Invert Switch", "MIXIN Right"}, + {"Out Mixer Right", "Mixin Left Invert Switch", "MIXIN Left"}, + {"Out Mixer Right", "DAC Right Switch", "DAC Right"}, + + {"MIXOUT Left", NULL, "Out Mixer Left"}, + {"Headphone Left Enable", "Switch", "MIXOUT Left"}, + {"Headphone Left", NULL, "Headphone Left Enable"}, + {"Headphone Left", NULL, "Charge Pump"}, + {"HPL", NULL, "Headphone Left"}, + + {"MIXOUT Right", NULL, "Out Mixer Right"}, + {"Headphone Right Enable", "Switch", "MIXOUT Right"}, + {"Headphone Right", NULL, "Headphone Right Enable"}, + {"Headphone Right", NULL, "Charge Pump"}, + {"HPR", NULL, "Headphone Right"}, + + {"MIXOUT Right", NULL, "Out Mixer Right"}, + {"Lineout Enable", "Switch", "MIXOUT Right"}, + {"Lineout", NULL, "Lineout Enable"}, + {"LINE", NULL, "Lineout"}, +}; + +/* Codec private data */ +struct da9055_priv { + struct regmap *regmap; + unsigned int mclk_rate; + int master; + struct da9055_platform_data *pdata; +}; + +static const struct reg_default da9055_reg_defaults[] = { + { 0x21, 0x10 }, + { 0x22, 0x0A }, + { 0x23, 0x00 }, + { 0x24, 0x00 }, + { 0x25, 0x00 }, + { 0x26, 0x00 }, + { 0x27, 0x0C }, + { 0x28, 0x01 }, + { 0x29, 0x08 }, + { 0x2A, 0x32 }, + { 0x2B, 0x00 }, + { 0x30, 0x35 }, + { 0x31, 0x35 }, + { 0x32, 0x00 }, + { 0x33, 0x00 }, + { 0x34, 0x03 }, + { 0x35, 0x03 }, + { 0x36, 0x6F }, + { 0x37, 0x6F }, + { 0x38, 0x80 }, + { 0x39, 0x01 }, + { 0x3A, 0x01 }, + { 0x40, 0x00 }, + { 0x41, 0x88 }, + { 0x42, 0x88 }, + { 0x43, 0x08 }, + { 0x44, 0x80 }, + { 0x45, 0x6F }, + { 0x46, 0x6F }, + { 0x47, 0x61 }, + { 0x48, 0x35 }, + { 0x49, 0x35 }, + { 0x4A, 0x35 }, + { 0x4B, 0x00 }, + { 0x4C, 0x00 }, + { 0x60, 0x44 }, + { 0x61, 0x44 }, + { 0x62, 0x00 }, + { 0x63, 0x40 }, + { 0x64, 0x40 }, + { 0x65, 0x40 }, + { 0x66, 0x40 }, + { 0x67, 0x40 }, + { 0x68, 0x40 }, + { 0x69, 0x48 }, + { 0x6A, 0x40 }, + { 0x6B, 0x41 }, + { 0x6C, 0x40 }, + { 0x6D, 0x40 }, + { 0x6E, 0x10 }, + { 0x6F, 0x10 }, + { 0x90, 0x80 }, + { 0x92, 0x02 }, + { 0x93, 0x00 }, + { 0x99, 0x00 }, + { 0x9A, 0x00 }, + { 0x9B, 0x00 }, + { 0x9C, 0x3F }, + { 0x9D, 0x00 }, + { 0x9E, 0x3F }, + { 0x9F, 0xFF }, + { 0xA0, 0x71 }, + { 0xA1, 0x00 }, + { 0xA2, 0x00 }, + { 0xA6, 0x00 }, + { 0xA7, 0x00 }, + { 0xAB, 0x00 }, + { 0xAC, 0x00 }, + { 0xAD, 0x00 }, + { 0xAF, 0x08 }, + { 0xB0, 0x00 }, + { 0xB1, 0x00 }, + { 0xB2, 0x00 }, +}; + +static bool da9055_volatile_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case DA9055_STATUS1: + case DA9055_PLL_STATUS: + case DA9055_AUX_L_GAIN_STATUS: + case DA9055_AUX_R_GAIN_STATUS: + case DA9055_MIC_L_GAIN_STATUS: + case DA9055_MIC_R_GAIN_STATUS: + case DA9055_MIXIN_L_GAIN_STATUS: + case DA9055_MIXIN_R_GAIN_STATUS: + case DA9055_ADC_L_GAIN_STATUS: + case DA9055_ADC_R_GAIN_STATUS: + case DA9055_DAC_L_GAIN_STATUS: + case DA9055_DAC_R_GAIN_STATUS: + case DA9055_HP_L_GAIN_STATUS: + case DA9055_HP_R_GAIN_STATUS: + case DA9055_LINE_GAIN_STATUS: + case DA9055_ALC_CIC_OP_LVL_DATA: + return true; + default: + return false; + } +} + +/* Set DAI word length */ +static int da9055_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component); + u8 aif_ctrl, fs; + u32 sysclk; + + switch (params_width(params)) { + case 16: + aif_ctrl = DA9055_AIF_WORD_S16_LE; + break; + case 20: + aif_ctrl = DA9055_AIF_WORD_S20_3LE; + break; + case 24: + aif_ctrl = DA9055_AIF_WORD_S24_LE; + break; + case 32: + aif_ctrl = DA9055_AIF_WORD_S32_LE; + break; + default: + return -EINVAL; + } + + /* Set AIF format */ + snd_soc_component_update_bits(component, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK, + aif_ctrl); + + switch (params_rate(params)) { + case 8000: + fs = DA9055_SR_8000; + sysclk = 3072000; + break; + case 11025: + fs = DA9055_SR_11025; + sysclk = 2822400; + break; + case 12000: + fs = DA9055_SR_12000; + sysclk = 3072000; + break; + case 16000: + fs = DA9055_SR_16000; + sysclk = 3072000; + break; + case 22050: + fs = DA9055_SR_22050; + sysclk = 2822400; + break; + case 32000: + fs = DA9055_SR_32000; + sysclk = 3072000; + break; + case 44100: + fs = DA9055_SR_44100; + sysclk = 2822400; + break; + case 48000: + fs = DA9055_SR_48000; + sysclk = 3072000; + break; + case 88200: + fs = DA9055_SR_88200; + sysclk = 2822400; + break; + case 96000: + fs = DA9055_SR_96000; + sysclk = 3072000; + break; + default: + return -EINVAL; + } + + if (da9055->mclk_rate) { + /* PLL Mode, Write actual FS */ + snd_soc_component_write(component, DA9055_SR, fs); + } else { + /* + * Non-PLL Mode + * When PLL is bypassed, chip assumes constant MCLK of + * 12.288MHz and uses sample rate value to divide this MCLK + * to derive its sys clk. As sys clk has to be 256 * Fs, we + * need to write constant sample rate i.e. 48KHz. + */ + snd_soc_component_write(component, DA9055_SR, DA9055_SR_48000); + } + + if (da9055->mclk_rate && (da9055->mclk_rate != sysclk)) { + /* PLL Mode */ + if (!da9055->master) { + /* PLL slave mode, enable PLL and also SRM */ + snd_soc_component_update_bits(component, DA9055_PLL_CTRL, + DA9055_PLL_EN | DA9055_PLL_SRM_EN, + DA9055_PLL_EN | DA9055_PLL_SRM_EN); + } else { + /* PLL master mode, only enable PLL */ + snd_soc_component_update_bits(component, DA9055_PLL_CTRL, + DA9055_PLL_EN, DA9055_PLL_EN); + } + } else { + /* Non PLL Mode, disable PLL */ + snd_soc_component_update_bits(component, DA9055_PLL_CTRL, DA9055_PLL_EN, 0); + } + + return 0; +} + +/* Set DAI mode and Format */ +static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component); + u8 aif_clk_mode, aif_ctrl, mode; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + /* DA9055 in I2S Master Mode */ + mode = 1; + aif_clk_mode = DA9055_AIF_CLK_EN_MASTER_MODE; + break; + case SND_SOC_DAIFMT_CBS_CFS: + /* DA9055 in I2S Slave Mode */ + mode = 0; + aif_clk_mode = DA9055_AIF_CLK_EN_SLAVE_MODE; + break; + default: + return -EINVAL; + } + + /* Don't allow change of mode if PLL is enabled */ + if ((snd_soc_component_read(component, DA9055_PLL_CTRL) & DA9055_PLL_EN) && + (da9055->master != mode)) + return -EINVAL; + + da9055->master = mode; + + /* Only I2S is supported */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + aif_ctrl = DA9055_AIF_FORMAT_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + aif_ctrl = DA9055_AIF_FORMAT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + aif_ctrl = DA9055_AIF_FORMAT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_A: + aif_ctrl = DA9055_AIF_FORMAT_DSP; + break; + default: + return -EINVAL; + } + + /* By default only 32 BCLK per WCLK is supported */ + aif_clk_mode |= DA9055_AIF_BCLKS_PER_WCLK_32; + + snd_soc_component_update_bits(component, DA9055_AIF_CLK_MODE, + (DA9055_AIF_CLK_MODE_MASK | DA9055_AIF_BCLK_MASK), + aif_clk_mode); + snd_soc_component_update_bits(component, DA9055_AIF_CTRL, DA9055_AIF_FORMAT_MASK, + aif_ctrl); + return 0; +} + +static int da9055_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + if (mute) { + snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL, + DA9055_DAC_L_MUTE_EN, DA9055_DAC_L_MUTE_EN); + snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL, + DA9055_DAC_R_MUTE_EN, DA9055_DAC_R_MUTE_EN); + } else { + snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL, + DA9055_DAC_L_MUTE_EN, 0); + snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL, + DA9055_DAC_R_MUTE_EN, 0); + } + + return 0; +} + +#define DA9055_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static int da9055_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case DA9055_CLKSRC_MCLK: + switch (freq) { + case 11289600: + case 12000000: + case 12288000: + case 13000000: + case 13500000: + case 14400000: + case 19200000: + case 19680000: + case 19800000: + da9055->mclk_rate = freq; + return 0; + default: + dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", + freq); + return -EINVAL; + } + break; + default: + dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } +} + +/* + * da9055_set_dai_pll : Configure the codec PLL + * @param codec_dai : Pointer to codec DAI + * @param pll_id : da9055 has only one pll, so pll_id is always zero + * @param fref : Input MCLK frequency + * @param fout : FsDM value + * @return int : Zero for success, negative error code for error + * + * Note: Supported PLL input frequencies are 11.2896MHz, 12MHz, 12.288MHz, + * 13MHz, 13.5MHz, 14.4MHz, 19.2MHz, 19.6MHz and 19.8MHz + */ +static int da9055_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int fref, unsigned int fout) +{ + struct snd_soc_component *component = codec_dai->component; + struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component); + + u8 pll_frac_top, pll_frac_bot, pll_integer, cnt; + + /* Disable PLL before setting the divisors */ + snd_soc_component_update_bits(component, DA9055_PLL_CTRL, DA9055_PLL_EN, 0); + + /* In slave mode, there is only one set of divisors */ + if (!da9055->master && (fout != 2822400)) + goto pll_err; + + /* Search pll div array for correct divisors */ + for (cnt = 0; cnt < ARRAY_SIZE(da9055_pll_div); cnt++) { + /* Check fref, mode and fout */ + if ((fref == da9055_pll_div[cnt].fref) && + (da9055->master == da9055_pll_div[cnt].mode) && + (fout == da9055_pll_div[cnt].fout)) { + /* All match, pick up divisors */ + pll_frac_top = da9055_pll_div[cnt].frac_top; + pll_frac_bot = da9055_pll_div[cnt].frac_bot; + pll_integer = da9055_pll_div[cnt].integer; + break; + } + } + if (cnt >= ARRAY_SIZE(da9055_pll_div)) + goto pll_err; + + /* Write PLL dividers */ + snd_soc_component_write(component, DA9055_PLL_FRAC_TOP, pll_frac_top); + snd_soc_component_write(component, DA9055_PLL_FRAC_BOT, pll_frac_bot); + snd_soc_component_write(component, DA9055_PLL_INTEGER, pll_integer); + + return 0; +pll_err: + dev_err(codec_dai->dev, "Error in setting up PLL\n"); + return -EINVAL; +} + +/* DAI operations */ +static const struct snd_soc_dai_ops da9055_dai_ops = { + .hw_params = da9055_hw_params, + .set_fmt = da9055_set_dai_fmt, + .set_sysclk = da9055_set_dai_sysclk, + .set_pll = da9055_set_dai_pll, + .mute_stream = da9055_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver da9055_dai = { + .name = "da9055-hifi", + /* Playback Capabilities */ + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA9055_FORMATS, + }, + /* Capture Capabilities */ + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = DA9055_FORMATS, + }, + .ops = &da9055_dai_ops, + .symmetric_rate = 1, +}; + +static int da9055_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Enable VMID reference & master bias */ + snd_soc_component_update_bits(component, DA9055_REFERENCES, + DA9055_VMID_EN | DA9055_BIAS_EN, + DA9055_VMID_EN | DA9055_BIAS_EN); + } + break; + case SND_SOC_BIAS_OFF: + /* Disable VMID reference & master bias */ + snd_soc_component_update_bits(component, DA9055_REFERENCES, + DA9055_VMID_EN | DA9055_BIAS_EN, 0); + break; + } + return 0; +} + +static int da9055_probe(struct snd_soc_component *component) +{ + struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component); + + /* Enable all Gain Ramps */ + snd_soc_component_update_bits(component, DA9055_AUX_L_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + snd_soc_component_update_bits(component, DA9055_AUX_R_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + snd_soc_component_update_bits(component, DA9055_MIXIN_L_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + snd_soc_component_update_bits(component, DA9055_MIXIN_R_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + snd_soc_component_update_bits(component, DA9055_ADC_L_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + snd_soc_component_update_bits(component, DA9055_ADC_R_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + snd_soc_component_update_bits(component, DA9055_HP_L_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + snd_soc_component_update_bits(component, DA9055_HP_R_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + snd_soc_component_update_bits(component, DA9055_LINE_CTRL, + DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + + /* + * There are two separate control bits for input and output mixers. + * One to enable corresponding amplifier and other to enable its + * output. As amplifier bits are related to power control, they are + * being managed by DAPM while other (non power related) bits are + * enabled here + */ + snd_soc_component_update_bits(component, DA9055_MIXIN_L_CTRL, + DA9055_MIXIN_L_MIX_EN, DA9055_MIXIN_L_MIX_EN); + snd_soc_component_update_bits(component, DA9055_MIXIN_R_CTRL, + DA9055_MIXIN_R_MIX_EN, DA9055_MIXIN_R_MIX_EN); + + snd_soc_component_update_bits(component, DA9055_MIXOUT_L_CTRL, + DA9055_MIXOUT_L_MIX_EN, DA9055_MIXOUT_L_MIX_EN); + snd_soc_component_update_bits(component, DA9055_MIXOUT_R_CTRL, + DA9055_MIXOUT_R_MIX_EN, DA9055_MIXOUT_R_MIX_EN); + + /* Set this as per your system configuration */ + snd_soc_component_write(component, DA9055_PLL_CTRL, DA9055_PLL_INDIV_10_20_MHZ); + + /* Set platform data values */ + if (da9055->pdata) { + /* set mic bias source */ + if (da9055->pdata->micbias_source) { + snd_soc_component_update_bits(component, DA9055_MIXIN_R_SELECT, + DA9055_MICBIAS2_EN, + DA9055_MICBIAS2_EN); + } else { + snd_soc_component_update_bits(component, DA9055_MIXIN_R_SELECT, + DA9055_MICBIAS2_EN, 0); + } + /* set mic bias voltage */ + switch (da9055->pdata->micbias) { + case DA9055_MICBIAS_2_2V: + case DA9055_MICBIAS_2_1V: + case DA9055_MICBIAS_1_8V: + case DA9055_MICBIAS_1_6V: + snd_soc_component_update_bits(component, DA9055_MIC_CONFIG, + DA9055_MICBIAS_LEVEL_MASK, + (da9055->pdata->micbias) << 4); + break; + } + } + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_da9055 = { + .probe = da9055_probe, + .set_bias_level = da9055_set_bias_level, + .controls = da9055_snd_controls, + .num_controls = ARRAY_SIZE(da9055_snd_controls), + .dapm_widgets = da9055_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da9055_dapm_widgets), + .dapm_routes = da9055_audio_map, + .num_dapm_routes = ARRAY_SIZE(da9055_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config da9055_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = da9055_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da9055_reg_defaults), + .volatile_reg = da9055_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int da9055_i2c_probe(struct i2c_client *i2c) +{ + struct da9055_priv *da9055; + struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev); + int ret; + + da9055 = devm_kzalloc(&i2c->dev, sizeof(struct da9055_priv), + GFP_KERNEL); + if (!da9055) + return -ENOMEM; + + if (pdata) + da9055->pdata = pdata; + + i2c_set_clientdata(i2c, da9055); + + da9055->regmap = devm_regmap_init_i2c(i2c, &da9055_regmap_config); + if (IS_ERR(da9055->regmap)) { + ret = PTR_ERR(da9055->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_da9055, &da9055_dai, 1); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register da9055 component: %d\n", + ret); + } + return ret; +} + +/* + * DO NOT change the device Ids. The naming is intentionally specific as both + * the CODEC and PMIC parts of this chip are instantiated separately as I2C + * devices (both have configurable I2C addresses, and are to all intents and + * purposes separate). As a result there are specific DA9055 Ids for CODEC + * and PMIC, which must be different to operate together. + */ +static const struct i2c_device_id da9055_i2c_id[] = { + { "da9055-codec", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, da9055_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id da9055_of_match[] = { + { .compatible = "dlg,da9055-codec", }, + { } +}; +MODULE_DEVICE_TABLE(of, da9055_of_match); +#endif + +/* I2C codec control layer */ +static struct i2c_driver da9055_i2c_driver = { + .driver = { + .name = "da9055-codec", + .of_match_table = of_match_ptr(da9055_of_match), + }, + .probe = da9055_i2c_probe, + .id_table = da9055_i2c_id, +}; + +module_i2c_driver(da9055_i2c_driver); + +MODULE_DESCRIPTION("ASoC DA9055 Codec driver"); +MODULE_AUTHOR("David Chen, Ashish Chavan"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c new file mode 100644 index 0000000000..4fd6f97e5a --- /dev/null +++ b/sound/soc/codecs/dmic.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * dmic.c -- SoC audio for Generic Digital MICs + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MODESWITCH_DELAY 70 +static int modeswitch_delay; +module_param(modeswitch_delay, uint, 0644); + +static int wakeup_delay; +module_param(wakeup_delay, uint, 0644); + +struct dmic { + struct gpio_desc *gpio_en; + int wakeup_delay; + /* Delay after DMIC mode switch */ + int modeswitch_delay; +}; + +static int dmic_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct dmic *dmic = snd_soc_component_get_drvdata(component); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + if (dmic->modeswitch_delay) + mdelay(dmic->modeswitch_delay); + + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops dmic_dai_ops = { + .trigger = dmic_daiops_trigger, +}; + +static int dmic_aif_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct dmic *dmic = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (dmic->gpio_en) + gpiod_set_value_cansleep(dmic->gpio_en, 1); + + if (dmic->wakeup_delay) + msleep(dmic->wakeup_delay); + break; + case SND_SOC_DAPM_POST_PMD: + if (dmic->gpio_en) + gpiod_set_value_cansleep(dmic->gpio_en, 0); + break; + } + + return 0; +} + +static struct snd_soc_dai_driver dmic_dai = { + .name = "dmic-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S32_LE + | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S16_LE + | SNDRV_PCM_FMTBIT_DSD_U8 + | SNDRV_PCM_FMTBIT_DSD_U16_LE + | SNDRV_PCM_FMTBIT_DSD_U32_LE, + }, + .ops = &dmic_dai_ops, +}; + +static int dmic_component_probe(struct snd_soc_component *component) +{ + struct dmic *dmic; + + dmic = devm_kzalloc(component->dev, sizeof(*dmic), GFP_KERNEL); + if (!dmic) + return -ENOMEM; + + dmic->gpio_en = devm_gpiod_get_optional(component->dev, + "dmicen", GPIOD_OUT_LOW); + if (IS_ERR(dmic->gpio_en)) + return PTR_ERR(dmic->gpio_en); + + device_property_read_u32(component->dev, "wakeup-delay-ms", + &dmic->wakeup_delay); + device_property_read_u32(component->dev, "modeswitch-delay-ms", + &dmic->modeswitch_delay); + if (wakeup_delay) + dmic->wakeup_delay = wakeup_delay; + if (modeswitch_delay) + dmic->modeswitch_delay = modeswitch_delay; + + if (dmic->modeswitch_delay > MAX_MODESWITCH_DELAY) + dmic->modeswitch_delay = MAX_MODESWITCH_DELAY; + + snd_soc_component_set_drvdata(component, dmic); + + return 0; +} + +static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT_E("DMIC AIF", "Capture", 0, + SND_SOC_NOPM, 0, 0, dmic_aif_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_INPUT("DMic"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"DMIC AIF", NULL, "DMic"}, +}; + +static const struct snd_soc_component_driver soc_dmic = { + .probe = dmic_component_probe, + .dapm_widgets = dmic_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int dmic_dev_probe(struct platform_device *pdev) +{ + int err; + u32 chans; + struct snd_soc_dai_driver *dai_drv = &dmic_dai; + + if (pdev->dev.of_node) { + err = of_property_read_u32(pdev->dev.of_node, "num-channels", &chans); + if (err && (err != -EINVAL)) + return err; + + if (!err) { + if (chans < 1 || chans > 8) + return -EINVAL; + + dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL); + if (!dai_drv) + return -ENOMEM; + + memcpy(dai_drv, &dmic_dai, sizeof(*dai_drv)); + dai_drv->capture.channels_max = chans; + } + } + + return devm_snd_soc_register_component(&pdev->dev, + &soc_dmic, dai_drv, 1); +} + +MODULE_ALIAS("platform:dmic-codec"); + +static const struct of_device_id dmic_dev_match[] = { + {.compatible = "dmic-codec"}, + {} +}; +MODULE_DEVICE_TABLE(of, dmic_dev_match); + +static struct platform_driver dmic_driver = { + .driver = { + .name = "dmic-codec", + .of_match_table = dmic_dev_match, + }, + .probe = dmic_dev_probe, +}; + +module_platform_driver(dmic_driver); + +MODULE_DESCRIPTION("Generic DMIC driver"); +MODULE_AUTHOR("Liam Girdwood "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c new file mode 100644 index 0000000000..f5150d2f95 --- /dev/null +++ b/sound/soc/codecs/es7134.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017 BayLibre, SAS. + * Author: Jerome Brunet + */ + +#include +#include +#include + +/* + * The everest 7134 is a very simple DA converter with no register + */ + +struct es7134_clock_mode { + unsigned int rate_min; + unsigned int rate_max; + unsigned int *mclk_fs; + unsigned int mclk_fs_num; +}; + +struct es7134_chip { + struct snd_soc_dai_driver *dai_drv; + const struct es7134_clock_mode *modes; + unsigned int mode_num; + const struct snd_soc_dapm_widget *extra_widgets; + unsigned int extra_widget_num; + const struct snd_soc_dapm_route *extra_routes; + unsigned int extra_route_num; +}; + +struct es7134_data { + unsigned int mclk; + const struct es7134_chip *chip; +}; + +static int es7134_check_mclk(struct snd_soc_dai *dai, + struct es7134_data *priv, + unsigned int rate) +{ + unsigned int mfs = priv->mclk / rate; + int i, j; + + for (i = 0; i < priv->chip->mode_num; i++) { + const struct es7134_clock_mode *mode = &priv->chip->modes[i]; + + if (rate < mode->rate_min || rate > mode->rate_max) + continue; + + for (j = 0; j < mode->mclk_fs_num; j++) { + if (mode->mclk_fs[j] == mfs) + return 0; + } + + dev_err(dai->dev, "unsupported mclk_fs %u for rate %u\n", + mfs, rate); + return -EINVAL; + } + + /* should not happen */ + dev_err(dai->dev, "unsupported rate: %u\n", rate); + return -EINVAL; +} + +static int es7134_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct es7134_data *priv = snd_soc_dai_get_drvdata(dai); + + /* mclk has not been provided, assume it is OK */ + if (!priv->mclk) + return 0; + + return es7134_check_mclk(dai, priv, params_rate(params)); +} + +static int es7134_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct es7134_data *priv = snd_soc_dai_get_drvdata(dai); + + if (dir == SND_SOC_CLOCK_IN && clk_id == 0) { + priv->mclk = freq; + return 0; + } + + return -ENOTSUPP; +} + +static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK | + SND_SOC_DAIFMT_MASTER_MASK); + + if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC)) { + dev_err(codec_dai->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + return 0; +} + +static int es7134_component_probe(struct snd_soc_component *c) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(c); + struct es7134_data *priv = snd_soc_component_get_drvdata(c); + const struct es7134_chip *chip = priv->chip; + int ret; + + if (chip->extra_widget_num) { + ret = snd_soc_dapm_new_controls(dapm, chip->extra_widgets, + chip->extra_widget_num); + if (ret) { + dev_err(c->dev, "failed to add extra widgets\n"); + return ret; + } + } + + if (chip->extra_route_num) { + ret = snd_soc_dapm_add_routes(dapm, chip->extra_routes, + chip->extra_route_num); + if (ret) { + dev_err(c->dev, "failed to add extra routes\n"); + return ret; + } + } + + return 0; +} + +static const struct snd_soc_dai_ops es7134_dai_ops = { + .set_fmt = es7134_set_fmt, + .hw_params = es7134_hw_params, + .set_sysclk = es7134_set_sysclk, +}; + +static struct snd_soc_dai_driver es7134_dai = { + .name = "es7134-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S18_3LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &es7134_dai_ops, +}; + +static const struct es7134_clock_mode es7134_modes[] = { + { + /* Single speed mode */ + .rate_min = 8000, + .rate_max = 50000, + .mclk_fs = (unsigned int[]) { 256, 384, 512, 768, 1024 }, + .mclk_fs_num = 5, + }, { + /* Double speed mode */ + .rate_min = 84000, + .rate_max = 100000, + .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512 }, + .mclk_fs_num = 5, + }, { + /* Quad speed mode */ + .rate_min = 167000, + .rate_max = 192000, + .mclk_fs = (unsigned int[]) { 128, 192, 256 }, + .mclk_fs_num = 3, + }, +}; + +/* Digital I/O are also supplied by VDD on the es7134 */ +static const struct snd_soc_dapm_route es7134_extra_routes[] = { + { "Playback", NULL, "VDD", } +}; + +static const struct es7134_chip es7134_chip __maybe_unused = { + .dai_drv = &es7134_dai, + .modes = es7134_modes, + .mode_num = ARRAY_SIZE(es7134_modes), + .extra_routes = es7134_extra_routes, + .extra_route_num = ARRAY_SIZE(es7134_extra_routes), +}; + +static const struct snd_soc_dapm_widget es7134_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("AOUTL"), + SND_SOC_DAPM_OUTPUT("AOUTR"), + SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDD", 0, 0), +}; + +static const struct snd_soc_dapm_route es7134_dapm_routes[] = { + { "AOUTL", NULL, "DAC" }, + { "AOUTR", NULL, "DAC" }, + { "DAC", NULL, "VDD" }, +}; + +static const struct snd_soc_component_driver es7134_component_driver = { + .probe = es7134_component_probe, + .dapm_widgets = es7134_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es7134_dapm_widgets), + .dapm_routes = es7134_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es7134_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct snd_soc_dai_driver es7154_dai = { + .name = "es7154-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = (SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S18_3LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &es7134_dai_ops, +}; + +static const struct es7134_clock_mode es7154_modes[] = { + { + /* Single speed mode */ + .rate_min = 8000, + .rate_max = 50000, + .mclk_fs = (unsigned int[]) { 32, 64, 128, 192, 256, + 384, 512, 768, 1024 }, + .mclk_fs_num = 9, + }, { + /* Double speed mode */ + .rate_min = 84000, + .rate_max = 100000, + .mclk_fs = (unsigned int[]) { 128, 192, 256, 384, 512, + 768, 1024}, + .mclk_fs_num = 7, + } +}; + +/* Es7154 has a separate supply for digital I/O */ +static const struct snd_soc_dapm_widget es7154_extra_widgets[] = { + SND_SOC_DAPM_REGULATOR_SUPPLY("PVDD", 0, 0), +}; + +static const struct snd_soc_dapm_route es7154_extra_routes[] = { + { "Playback", NULL, "PVDD", } +}; + +static const struct es7134_chip es7154_chip __maybe_unused = { + .dai_drv = &es7154_dai, + .modes = es7154_modes, + .mode_num = ARRAY_SIZE(es7154_modes), + .extra_routes = es7154_extra_routes, + .extra_route_num = ARRAY_SIZE(es7154_extra_routes), + .extra_widgets = es7154_extra_widgets, + .extra_widget_num = ARRAY_SIZE(es7154_extra_widgets), +}; + +static int es7134_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct es7134_data *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->chip = of_device_get_match_data(dev); + if (!priv->chip) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + return devm_snd_soc_register_component(&pdev->dev, + &es7134_component_driver, + priv->chip->dai_drv, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id es7134_ids[] = { + { .compatible = "everest,es7134", .data = &es7134_chip }, + { .compatible = "everest,es7144", .data = &es7134_chip }, + { .compatible = "everest,es7154", .data = &es7154_chip }, + { } +}; +MODULE_DEVICE_TABLE(of, es7134_ids); +#endif + +static struct platform_driver es7134_driver = { + .driver = { + .name = "es7134", + .of_match_table = of_match_ptr(es7134_ids), + }, + .probe = es7134_probe, +}; + +module_platform_driver(es7134_driver); + +MODULE_DESCRIPTION("ASoC ES7134 audio codec driver"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c new file mode 100644 index 0000000000..339553cfbb --- /dev/null +++ b/sound/soc/codecs/es7241.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet + +#include +#include +#include +#include + +struct es7241_clock_mode { + unsigned int rate_min; + unsigned int rate_max; + unsigned int *slv_mfs; + unsigned int slv_mfs_num; + unsigned int mst_mfs; + unsigned int mst_m0:1; + unsigned int mst_m1:1; +}; + +struct es7241_chip { + const struct es7241_clock_mode *modes; + unsigned int mode_num; +}; + +struct es7241_data { + struct gpio_desc *reset; + struct gpio_desc *m0; + struct gpio_desc *m1; + unsigned int fmt; + unsigned int mclk; + bool is_consumer; + const struct es7241_chip *chip; +}; + +static void es7241_set_mode(struct es7241_data *priv, int m0, int m1) +{ + /* put the device in reset */ + gpiod_set_value_cansleep(priv->reset, 0); + + /* set the mode */ + gpiod_set_value_cansleep(priv->m0, m0); + gpiod_set_value_cansleep(priv->m1, m1); + + /* take the device out of reset - datasheet does not specify a delay */ + gpiod_set_value_cansleep(priv->reset, 1); +} + +static int es7241_set_consumer_mode(struct es7241_data *priv, + const struct es7241_clock_mode *mode, + unsigned int mfs) +{ + int j; + + if (!mfs) + goto out_ok; + + for (j = 0; j < mode->slv_mfs_num; j++) { + if (mode->slv_mfs[j] == mfs) + goto out_ok; + } + + return -EINVAL; + +out_ok: + es7241_set_mode(priv, 1, 1); + return 0; +} + +static int es7241_set_provider_mode(struct es7241_data *priv, + const struct es7241_clock_mode *mode, + unsigned int mfs) +{ + /* + * We can't really set clock ratio, if the mclk/lrclk is different + * from what we provide, then error out + */ + if (mfs && mfs != mode->mst_mfs) + return -EINVAL; + + es7241_set_mode(priv, mode->mst_m0, mode->mst_m1); + + return 0; +} + +static int es7241_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct es7241_data *priv = snd_soc_dai_get_drvdata(dai); + unsigned int rate = params_rate(params); + unsigned int mfs = priv->mclk / rate; + int i; + + for (i = 0; i < priv->chip->mode_num; i++) { + const struct es7241_clock_mode *mode = &priv->chip->modes[i]; + + if (rate < mode->rate_min || rate >= mode->rate_max) + continue; + + if (priv->is_consumer) + return es7241_set_consumer_mode(priv, mode, mfs); + else + return es7241_set_provider_mode(priv, mode, mfs); + } + + /* should not happen */ + dev_err(dai->dev, "unsupported rate: %u\n", rate); + return -EINVAL; +} + +static int es7241_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct es7241_data *priv = snd_soc_dai_get_drvdata(dai); + + if (dir == SND_SOC_CLOCK_IN && clk_id == 0) { + priv->mclk = freq; + return 0; + } + + return -ENOTSUPP; +} + +static int es7241_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct es7241_data *priv = snd_soc_dai_get_drvdata(dai); + + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) { + dev_err(dai->dev, "Unsupported dai clock inversion\n"); + return -EINVAL; + } + + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != priv->fmt) { + dev_err(dai->dev, "Invalid dai format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + priv->is_consumer = true; + break; + case SND_SOC_DAIFMT_CBP_CFP: + priv->is_consumer = false; + break; + + default: + dev_err(dai->dev, "Unsupported clock configuration\n"); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops es7241_dai_ops = { + .set_fmt = es7241_set_fmt, + .hw_params = es7241_hw_params, + .set_sysclk = es7241_set_sysclk, +}; + +static struct snd_soc_dai_driver es7241_dai = { + .name = "es7241-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + .ops = &es7241_dai_ops, +}; + +static const struct es7241_clock_mode es7241_modes[] = { + { + /* Single speed mode */ + .rate_min = 8000, + .rate_max = 50000, + .slv_mfs = (unsigned int[]) { 256, 384, 512, 768, 1024 }, + .slv_mfs_num = 5, + .mst_mfs = 256, + .mst_m0 = 0, + .mst_m1 = 0, + }, { + /* Double speed mode */ + .rate_min = 50000, + .rate_max = 100000, + .slv_mfs = (unsigned int[]) { 128, 192 }, + .slv_mfs_num = 2, + .mst_mfs = 128, + .mst_m0 = 1, + .mst_m1 = 0, + }, { + /* Quad speed mode */ + .rate_min = 100000, + .rate_max = 200000, + .slv_mfs = (unsigned int[]) { 64 }, + .slv_mfs_num = 1, + .mst_mfs = 64, + .mst_m0 = 0, + .mst_m1 = 1, + }, +}; + +static const struct es7241_chip es7241_chip __maybe_unused = { + .modes = es7241_modes, + .mode_num = ARRAY_SIZE(es7241_modes), +}; + +static const struct snd_soc_dapm_widget es7241_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("AINL"), + SND_SOC_DAPM_INPUT("AINR"), + SND_SOC_DAPM_DAC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDDP", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDDD", 0, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("VDDA", 0, 0), +}; + +static const struct snd_soc_dapm_route es7241_dapm_routes[] = { + { "ADC", NULL, "AINL", }, + { "ADC", NULL, "AINR", }, + { "ADC", NULL, "VDDA", }, + { "Capture", NULL, "VDDP", }, + { "Capture", NULL, "VDDD", }, +}; + +static const struct snd_soc_component_driver es7241_component_driver = { + .dapm_widgets = es7241_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es7241_dapm_widgets), + .dapm_routes = es7241_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es7241_dapm_routes), + .idle_bias_on = 1, + .endianness = 1, +}; + +static void es7241_parse_fmt(struct device *dev, struct es7241_data *priv) +{ + bool is_leftj; + + /* + * The format is given by a pull resistor on the SDOUT pin: + * pull-up for i2s, pull-down for left justified. + */ + is_leftj = of_property_read_bool(dev->of_node, + "everest,sdout-pull-down"); + if (is_leftj) + priv->fmt = SND_SOC_DAIFMT_LEFT_J; + else + priv->fmt = SND_SOC_DAIFMT_I2S; +} + +static int es7241_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct es7241_data *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->chip = of_device_get_match_data(dev); + if (!priv->chip) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + es7241_parse_fmt(dev, priv); + + priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) + return dev_err_probe(dev, PTR_ERR(priv->reset), + "Failed to get 'reset' gpio"); + + priv->m0 = devm_gpiod_get_optional(dev, "m0", GPIOD_OUT_LOW); + if (IS_ERR(priv->m0)) + return dev_err_probe(dev, PTR_ERR(priv->m0), + "Failed to get 'm0' gpio"); + + priv->m1 = devm_gpiod_get_optional(dev, "m1", GPIOD_OUT_LOW); + if (IS_ERR(priv->m1)) + return dev_err_probe(dev, PTR_ERR(priv->m1), + "Failed to get 'm1' gpio"); + + return devm_snd_soc_register_component(&pdev->dev, + &es7241_component_driver, + &es7241_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id es7241_ids[] = { + { .compatible = "everest,es7241", .data = &es7241_chip }, + { } +}; +MODULE_DEVICE_TABLE(of, es7241_ids); +#endif + +static struct platform_driver es7241_driver = { + .driver = { + .name = "es7241", + .of_match_table = of_match_ptr(es7241_ids), + }, + .probe = es7241_probe, +}; + +module_platform_driver(es7241_driver); + +MODULE_DESCRIPTION("ASoC ES7241 audio codec driver"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c new file mode 100644 index 0000000000..a8f347f1af --- /dev/null +++ b/sound/soc/codecs/es8316.c @@ -0,0 +1,903 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * es8316.c -- es8316 ALSA SoC audio driver + * Copyright Everest Semiconductor Co.,Ltd + * + * Authors: David Yang , + * Daniel Drake + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "es8316.h" + +/* In slave mode at single speed, the codec is documented as accepting 5 + * MCLK/LRCK ratios, but we also add ratio 400, which is commonly used on + * Intel Cherry Trail platforms (19.2MHz MCLK, 48kHz LRCK). + */ +#define NR_SUPPORTED_MCLK_LRCK_RATIOS ARRAY_SIZE(supported_mclk_lrck_ratios) +static const unsigned int supported_mclk_lrck_ratios[] = { + 256, 384, 400, 500, 512, 768, 1024 +}; + +struct es8316_priv { + struct mutex lock; + struct clk *mclk; + struct regmap *regmap; + struct snd_soc_component *component; + struct snd_soc_jack *jack; + int irq; + unsigned int sysclk; + unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS]; + struct snd_pcm_hw_constraint_list sysclk_constraints; + bool jd_inverted; +}; + +/* + * ES8316 controls + */ +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9600, 50, 1); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(alc_target_tlv, + 0, 10, TLV_DB_SCALE_ITEM(-1650, 150, 0), + 11, 11, TLV_DB_SCALE_ITEM(-150, 0, 0), +); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpmixer_gain_tlv, + 0, 4, TLV_DB_SCALE_ITEM(-1200, 150, 0), + 8, 11, TLV_DB_SCALE_ITEM(-450, 150, 0), +); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(adc_pga_gain_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-350, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(0, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(250, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0), + 4, 7, TLV_DB_SCALE_ITEM(700, 300, 0), + 8, 10, TLV_DB_SCALE_ITEM(1800, 300, 0), +); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpout_vol_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-4800, 0, 0), + 1, 3, TLV_DB_SCALE_ITEM(-2400, 1200, 0), +); + +static const char * const ng_type_txt[] = + { "Constant PGA Gain", "Mute ADC Output" }; +static const struct soc_enum ng_type = + SOC_ENUM_SINGLE(ES8316_ADC_ALC_NG, 6, 2, ng_type_txt); + +static const char * const adcpol_txt[] = { "Normal", "Invert" }; +static const struct soc_enum adcpol = + SOC_ENUM_SINGLE(ES8316_ADC_MUTE, 1, 2, adcpol_txt); +static const char *const dacpol_txt[] = + { "Normal", "R Invert", "L Invert", "L + R Invert" }; +static const struct soc_enum dacpol = + SOC_ENUM_SINGLE(ES8316_DAC_SET1, 0, 4, dacpol_txt); + +static const struct snd_kcontrol_new es8316_snd_controls[] = { + SOC_DOUBLE_TLV("Headphone Playback Volume", ES8316_CPHP_ICAL_VOL, + 4, 0, 3, 1, hpout_vol_tlv), + SOC_DOUBLE_TLV("Headphone Mixer Volume", ES8316_HPMIX_VOL, + 4, 0, 11, 0, hpmixer_gain_tlv), + + SOC_ENUM("Playback Polarity", dacpol), + SOC_DOUBLE_R_TLV("DAC Playback Volume", ES8316_DAC_VOLL, + ES8316_DAC_VOLR, 0, 0xc0, 1, dac_vol_tlv), + SOC_SINGLE("DAC Soft Ramp Switch", ES8316_DAC_SET1, 4, 1, 1), + SOC_SINGLE("DAC Soft Ramp Rate", ES8316_DAC_SET1, 2, 4, 0), + SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0), + SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0), + SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0), + SOC_SINGLE("DAC Mono Mix Switch", ES8316_DAC_SET3, 3, 1, 0), + + SOC_ENUM("Capture Polarity", adcpol), + SOC_SINGLE("Mic Boost Switch", ES8316_ADC_D2SEPGA, 0, 1, 0), + SOC_SINGLE_TLV("ADC Capture Volume", ES8316_ADC_VOLUME, + 0, 0xc0, 1, adc_vol_tlv), + SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8316_ADC_PGAGAIN, + 4, 10, 0, adc_pga_gain_tlv), + SOC_SINGLE("ADC Soft Ramp Switch", ES8316_ADC_MUTE, 4, 1, 0), + SOC_SINGLE("ADC Double Fs Switch", ES8316_ADC_DMIC, 4, 1, 0), + + SOC_SINGLE("ALC Capture Switch", ES8316_ADC_ALC1, 6, 1, 0), + SOC_SINGLE_TLV("ALC Capture Max Volume", ES8316_ADC_ALC1, 0, 28, 0, + alc_max_gain_tlv), + SOC_SINGLE_TLV("ALC Capture Min Volume", ES8316_ADC_ALC2, 0, 28, 0, + alc_min_gain_tlv), + SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 11, 0, + alc_target_tlv), + SOC_SINGLE("ALC Capture Hold Time", ES8316_ADC_ALC3, 0, 10, 0), + SOC_SINGLE("ALC Capture Decay Time", ES8316_ADC_ALC4, 4, 10, 0), + SOC_SINGLE("ALC Capture Attack Time", ES8316_ADC_ALC4, 0, 10, 0), + SOC_SINGLE("ALC Capture Noise Gate Switch", ES8316_ADC_ALC_NG, + 5, 1, 0), + SOC_SINGLE("ALC Capture Noise Gate Threshold", ES8316_ADC_ALC_NG, + 0, 31, 0), + SOC_ENUM("ALC Capture Noise Gate Type", ng_type), +}; + +/* Analog Input Mux */ +static const char * const es8316_analog_in_txt[] = { + "lin1-rin1", + "lin2-rin2", + "lin1-rin1 with 20db Boost", + "lin2-rin2 with 20db Boost" +}; +static const unsigned int es8316_analog_in_values[] = { 0, 1, 2, 3 }; +static const struct soc_enum es8316_analog_input_enum = + SOC_VALUE_ENUM_SINGLE(ES8316_ADC_PDN_LINSEL, 4, 3, + ARRAY_SIZE(es8316_analog_in_txt), + es8316_analog_in_txt, + es8316_analog_in_values); +static const struct snd_kcontrol_new es8316_analog_in_mux_controls = + SOC_DAPM_ENUM("Route", es8316_analog_input_enum); + +static const char * const es8316_dmic_txt[] = { + "dmic disable", + "dmic data at high level", + "dmic data at low level", +}; +static const unsigned int es8316_dmic_values[] = { 0, 2, 3 }; +static const struct soc_enum es8316_dmic_src_enum = + SOC_VALUE_ENUM_SINGLE(ES8316_ADC_DMIC, 0, 3, + ARRAY_SIZE(es8316_dmic_txt), + es8316_dmic_txt, + es8316_dmic_values); +static const struct snd_kcontrol_new es8316_dmic_src_controls = + SOC_DAPM_ENUM("Route", es8316_dmic_src_enum); + +/* hp mixer mux */ +static const char * const es8316_hpmux_texts[] = { + "lin1-rin1", + "lin2-rin2", + "lin-rin with Boost", + "lin-rin with Boost and PGA" +}; + +static SOC_ENUM_SINGLE_DECL(es8316_left_hpmux_enum, ES8316_HPMIX_SEL, + 4, es8316_hpmux_texts); + +static const struct snd_kcontrol_new es8316_left_hpmux_controls = + SOC_DAPM_ENUM("Route", es8316_left_hpmux_enum); + +static SOC_ENUM_SINGLE_DECL(es8316_right_hpmux_enum, ES8316_HPMIX_SEL, + 0, es8316_hpmux_texts); + +static const struct snd_kcontrol_new es8316_right_hpmux_controls = + SOC_DAPM_ENUM("Route", es8316_right_hpmux_enum); + +/* headphone Output Mixer */ +static const struct snd_kcontrol_new es8316_out_left_mix[] = { + SOC_DAPM_SINGLE("LLIN Switch", ES8316_HPMIX_SWITCH, 6, 1, 0), + SOC_DAPM_SINGLE("Left DAC Switch", ES8316_HPMIX_SWITCH, 7, 1, 0), +}; +static const struct snd_kcontrol_new es8316_out_right_mix[] = { + SOC_DAPM_SINGLE("RLIN Switch", ES8316_HPMIX_SWITCH, 2, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", ES8316_HPMIX_SWITCH, 3, 1, 0), +}; + +/* DAC data source mux */ +static const char * const es8316_dacsrc_texts[] = { + "LDATA TO LDAC, RDATA TO RDAC", + "LDATA TO LDAC, LDATA TO RDAC", + "RDATA TO LDAC, RDATA TO RDAC", + "RDATA TO LDAC, LDATA TO RDAC", +}; + +static SOC_ENUM_SINGLE_DECL(es8316_dacsrc_mux_enum, ES8316_DAC_SET1, + 6, es8316_dacsrc_texts); + +static const struct snd_kcontrol_new es8316_dacsrc_mux_controls = + SOC_DAPM_ENUM("Route", es8316_dacsrc_mux_enum); + +static const struct snd_soc_dapm_widget es8316_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Bias", ES8316_SYS_PDN, 3, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Analog power", ES8316_SYS_PDN, 4, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias", ES8316_SYS_PDN, 5, 1, NULL, 0), + + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + + /* Input Mux */ + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &es8316_analog_in_mux_controls), + + SND_SOC_DAPM_SUPPLY("ADC Vref", ES8316_SYS_PDN, 1, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC bias", ES8316_SYS_PDN, 2, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Clock", ES8316_CLKMGR_CLKSW, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Line input PGA", ES8316_ADC_PDN_LINSEL, + 7, 1, NULL, 0), + SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8316_ADC_PDN_LINSEL, 6, 1), + SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0, + &es8316_dmic_src_controls), + + /* Digital Interface */ + SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 1, + ES8316_SERDATA_ADC, 6, 1), + SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("DAC Source Mux", SND_SOC_NOPM, 0, 0, + &es8316_dacsrc_mux_controls), + + SND_SOC_DAPM_SUPPLY("DAC Vref", ES8316_SYS_PDN, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Clock", ES8316_CLKMGR_CLKSW, 2, 0, NULL, 0), + SND_SOC_DAPM_DAC("Right DAC", NULL, ES8316_DAC_PDN, 0, 1), + SND_SOC_DAPM_DAC("Left DAC", NULL, ES8316_DAC_PDN, 4, 1), + + /* Headphone Output Side */ + SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, + &es8316_left_hpmux_controls), + SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, + &es8316_right_hpmux_controls), + SND_SOC_DAPM_MIXER("Left Headphone Mixer", ES8316_HPMIX_PDN, + 5, 1, &es8316_out_left_mix[0], + ARRAY_SIZE(es8316_out_left_mix)), + SND_SOC_DAPM_MIXER("Right Headphone Mixer", ES8316_HPMIX_PDN, + 1, 1, &es8316_out_right_mix[0], + ARRAY_SIZE(es8316_out_right_mix)), + SND_SOC_DAPM_PGA("Left Headphone Mixer Out", ES8316_HPMIX_PDN, + 4, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right Headphone Mixer Out", ES8316_HPMIX_PDN, + 0, 1, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("Left Headphone Charge Pump", ES8316_CPHP_OUTEN, + 6, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Right Headphone Charge Pump", ES8316_CPHP_OUTEN, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", ES8316_CPHP_PDN2, + 5, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Charge Pump Clock", ES8316_CLKMGR_CLKSW, + 4, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("Left Headphone Driver", ES8316_CPHP_OUTEN, + 5, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Right Headphone Driver", ES8316_CPHP_OUTEN, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Out", ES8316_CPHP_PDN1, 2, 1, NULL, 0), + + /* pdn_Lical and pdn_Rical bits are documented as Reserved, but must + * be explicitly unset in order to enable HP output + */ + SND_SOC_DAPM_SUPPLY("Left Headphone ical", ES8316_CPHP_ICAL_VOL, + 7, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Right Headphone ical", ES8316_CPHP_ICAL_VOL, + 3, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route es8316_dapm_routes[] = { + /* Recording */ + {"MIC1", NULL, "Mic Bias"}, + {"MIC2", NULL, "Mic Bias"}, + {"MIC1", NULL, "Bias"}, + {"MIC2", NULL, "Bias"}, + {"MIC1", NULL, "Analog power"}, + {"MIC2", NULL, "Analog power"}, + + {"Differential Mux", "lin1-rin1", "MIC1"}, + {"Differential Mux", "lin2-rin2", "MIC2"}, + {"Line input PGA", NULL, "Differential Mux"}, + + {"Mono ADC", NULL, "ADC Clock"}, + {"Mono ADC", NULL, "ADC Vref"}, + {"Mono ADC", NULL, "ADC bias"}, + {"Mono ADC", NULL, "Line input PGA"}, + + /* It's not clear why, but to avoid recording only silence, + * the DAC clock must be running for the ADC to work. + */ + {"Mono ADC", NULL, "DAC Clock"}, + + {"Digital Mic Mux", "dmic disable", "Mono ADC"}, + + {"I2S OUT", NULL, "Digital Mic Mux"}, + + /* Playback */ + {"DAC Source Mux", "LDATA TO LDAC, RDATA TO RDAC", "I2S IN"}, + + {"Left DAC", NULL, "DAC Clock"}, + {"Right DAC", NULL, "DAC Clock"}, + + {"Left DAC", NULL, "DAC Vref"}, + {"Right DAC", NULL, "DAC Vref"}, + + {"Left DAC", NULL, "DAC Source Mux"}, + {"Right DAC", NULL, "DAC Source Mux"}, + + {"Left Headphone Mux", "lin-rin with Boost and PGA", "Line input PGA"}, + {"Right Headphone Mux", "lin-rin with Boost and PGA", "Line input PGA"}, + + {"Left Headphone Mixer", "LLIN Switch", "Left Headphone Mux"}, + {"Left Headphone Mixer", "Left DAC Switch", "Left DAC"}, + + {"Right Headphone Mixer", "RLIN Switch", "Right Headphone Mux"}, + {"Right Headphone Mixer", "Right DAC Switch", "Right DAC"}, + + {"Left Headphone Mixer Out", NULL, "Left Headphone Mixer"}, + {"Right Headphone Mixer Out", NULL, "Right Headphone Mixer"}, + + {"Left Headphone Charge Pump", NULL, "Left Headphone Mixer Out"}, + {"Right Headphone Charge Pump", NULL, "Right Headphone Mixer Out"}, + + {"Left Headphone Charge Pump", NULL, "Headphone Charge Pump"}, + {"Right Headphone Charge Pump", NULL, "Headphone Charge Pump"}, + + {"Left Headphone Charge Pump", NULL, "Headphone Charge Pump Clock"}, + {"Right Headphone Charge Pump", NULL, "Headphone Charge Pump Clock"}, + + {"Left Headphone Driver", NULL, "Left Headphone Charge Pump"}, + {"Right Headphone Driver", NULL, "Right Headphone Charge Pump"}, + + {"HPOL", NULL, "Left Headphone Driver"}, + {"HPOR", NULL, "Right Headphone Driver"}, + + {"HPOL", NULL, "Left Headphone ical"}, + {"HPOR", NULL, "Right Headphone ical"}, + + {"Headphone Out", NULL, "Bias"}, + {"Headphone Out", NULL, "Analog power"}, + {"HPOL", NULL, "Headphone Out"}, + {"HPOR", NULL, "Headphone Out"}, +}; + +static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + int i, ret; + int count = 0; + + es8316->sysclk = freq; + es8316->sysclk_constraints.list = NULL; + es8316->sysclk_constraints.count = 0; + + if (freq == 0) + return 0; + + ret = clk_set_rate(es8316->mclk, freq); + if (ret) + return ret; + + /* Limit supported sample rates to ones that can be autodetected + * by the codec running in slave mode. + */ + for (i = 0; i < NR_SUPPORTED_MCLK_LRCK_RATIOS; i++) { + const unsigned int ratio = supported_mclk_lrck_ratios[i]; + + if (freq % ratio == 0) + es8316->allowed_rates[count++] = freq / ratio; + } + + if (count) { + es8316->sysclk_constraints.list = es8316->allowed_rates; + es8316->sysclk_constraints.count = count; + } + + return 0; +} + +static int es8316_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u8 serdata1 = 0; + u8 serdata2 = 0; + u8 clksw; + u8 mask; + + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBP_CFP) + serdata1 |= ES8316_SERDATA1_MASTER; + + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) { + dev_err(component->dev, "Codec driver only supports I2S format\n"); + return -EINVAL; + } + + /* Clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + serdata1 |= ES8316_SERDATA1_BCLK_INV; + serdata2 |= ES8316_SERDATA2_ADCLRP; + break; + case SND_SOC_DAIFMT_IB_NF: + serdata1 |= ES8316_SERDATA1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + serdata2 |= ES8316_SERDATA2_ADCLRP; + break; + default: + return -EINVAL; + } + + mask = ES8316_SERDATA1_MASTER | ES8316_SERDATA1_BCLK_INV; + snd_soc_component_update_bits(component, ES8316_SERDATA1, mask, serdata1); + + mask = ES8316_SERDATA2_FMT_MASK | ES8316_SERDATA2_ADCLRP; + snd_soc_component_update_bits(component, ES8316_SERDATA_ADC, mask, serdata2); + snd_soc_component_update_bits(component, ES8316_SERDATA_DAC, mask, serdata2); + + /* Enable BCLK and MCLK inputs in slave mode */ + clksw = ES8316_CLKMGR_CLKSW_MCLK_ON | ES8316_CLKMGR_CLKSW_BCLK_ON; + snd_soc_component_update_bits(component, ES8316_CLKMGR_CLKSW, clksw, clksw); + + return 0; +} + +static int es8316_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + + if (es8316->sysclk_constraints.list) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &es8316->sysclk_constraints); + + return 0; +} + +static int es8316_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + u8 wordlen = 0; + u8 bclk_divider; + u16 lrck_divider; + int i; + + /* Validate supported sample rates that are autodetected from MCLK */ + for (i = 0; i < NR_SUPPORTED_MCLK_LRCK_RATIOS; i++) { + const unsigned int ratio = supported_mclk_lrck_ratios[i]; + + if (es8316->sysclk % ratio != 0) + continue; + if (es8316->sysclk / ratio == params_rate(params)) + break; + } + if (i == NR_SUPPORTED_MCLK_LRCK_RATIOS) + return -EINVAL; + lrck_divider = es8316->sysclk / params_rate(params); + bclk_divider = lrck_divider / 4; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + wordlen = ES8316_SERDATA2_LEN_16; + bclk_divider /= 16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + wordlen = ES8316_SERDATA2_LEN_20; + bclk_divider /= 20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + wordlen = ES8316_SERDATA2_LEN_24; + bclk_divider /= 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + wordlen = ES8316_SERDATA2_LEN_32; + bclk_divider /= 32; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, ES8316_SERDATA_DAC, + ES8316_SERDATA2_LEN_MASK, wordlen); + snd_soc_component_update_bits(component, ES8316_SERDATA_ADC, + ES8316_SERDATA2_LEN_MASK, wordlen); + snd_soc_component_update_bits(component, ES8316_SERDATA1, 0x1f, bclk_divider); + snd_soc_component_update_bits(component, ES8316_CLKMGR_ADCDIV1, 0x0f, lrck_divider >> 8); + snd_soc_component_update_bits(component, ES8316_CLKMGR_ADCDIV2, 0xff, lrck_divider & 0xff); + snd_soc_component_update_bits(component, ES8316_CLKMGR_DACDIV1, 0x0f, lrck_divider >> 8); + snd_soc_component_update_bits(component, ES8316_CLKMGR_DACDIV2, 0xff, lrck_divider & 0xff); + return 0; +} + +static int es8316_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + snd_soc_component_update_bits(dai->component, ES8316_DAC_SET1, 0x20, + mute ? 0x20 : 0); + return 0; +} + +#define ES8316_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops es8316_ops = { + .startup = es8316_pcm_startup, + .hw_params = es8316_pcm_hw_params, + .set_fmt = es8316_set_dai_fmt, + .set_sysclk = es8316_set_dai_sysclk, + .mute_stream = es8316_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver es8316_dai = { + .name = "ES8316 HiFi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ES8316_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = ES8316_FORMATS, + }, + .ops = &es8316_ops, + .symmetric_rate = 1, +}; + +static void es8316_enable_micbias_for_mic_gnd_short_detect( + struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Bias"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Analog power"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Mic Bias"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); + + msleep(20); +} + +static void es8316_disable_micbias_for_mic_gnd_short_detect( + struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Bias"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Analog power"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Bias"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +static irqreturn_t es8316_irq(int irq, void *data) +{ + struct es8316_priv *es8316 = data; + struct snd_soc_component *comp = es8316->component; + unsigned int flags; + + mutex_lock(&es8316->lock); + + regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); + if (flags == 0x00) + goto out; /* Powered-down / reset */ + + /* Catch spurious IRQ before set_jack is called */ + if (!es8316->jack) + goto out; + + if (es8316->jd_inverted) + flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED; + + dev_dbg(comp->dev, "gpio flags %#04x\n", flags); + if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) { + /* Jack removed, or spurious IRQ? */ + if (es8316->jack->status & SND_JACK_MICROPHONE) + es8316_disable_micbias_for_mic_gnd_short_detect(comp); + + if (es8316->jack->status & SND_JACK_HEADPHONE) { + snd_soc_jack_report(es8316->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + dev_dbg(comp->dev, "jack unplugged\n"); + } + } else if (!(es8316->jack->status & SND_JACK_HEADPHONE)) { + /* Jack inserted, determine type */ + es8316_enable_micbias_for_mic_gnd_short_detect(comp); + regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); + if (es8316->jd_inverted) + flags ^= ES8316_GPIO_FLAG_HP_NOT_INSERTED; + dev_dbg(comp->dev, "gpio flags %#04x\n", flags); + if (flags & ES8316_GPIO_FLAG_HP_NOT_INSERTED) { + /* Jack unplugged underneath us */ + es8316_disable_micbias_for_mic_gnd_short_detect(comp); + } else if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) { + /* Open, headset */ + snd_soc_jack_report(es8316->jack, + SND_JACK_HEADSET, + SND_JACK_HEADSET); + /* Keep mic-gnd-short detection on for button press */ + } else { + /* Shorted, headphones */ + snd_soc_jack_report(es8316->jack, + SND_JACK_HEADPHONE, + SND_JACK_HEADSET); + /* No longer need mic-gnd-short detection */ + es8316_disable_micbias_for_mic_gnd_short_detect(comp); + } + } else if (es8316->jack->status & SND_JACK_MICROPHONE) { + /* Interrupt while jack inserted, report button state */ + if (flags & ES8316_GPIO_FLAG_GM_NOT_SHORTED) { + /* Open, button release */ + snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0); + } else { + /* Short, button press */ + snd_soc_jack_report(es8316->jack, + SND_JACK_BTN_0, + SND_JACK_BTN_0); + } + } + +out: + mutex_unlock(&es8316->lock); + return IRQ_HANDLED; +} + +static void es8316_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + + /* + * Init es8316->jd_inverted here and not in the probe, as we cannot + * guarantee that the bytchr-es8316 driver, which might set this + * property, will probe before us. + */ + es8316->jd_inverted = device_property_read_bool(component->dev, + "everest,jack-detect-inverted"); + + mutex_lock(&es8316->lock); + + es8316->jack = jack; + + if (es8316->jack->status & SND_JACK_MICROPHONE) + es8316_enable_micbias_for_mic_gnd_short_detect(component); + + snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, + ES8316_GPIO_ENABLE_INTERRUPT, + ES8316_GPIO_ENABLE_INTERRUPT); + + mutex_unlock(&es8316->lock); + + /* Enable irq and sync initial jack state */ + enable_irq(es8316->irq); + es8316_irq(es8316->irq, es8316); +} + +static void es8316_disable_jack_detect(struct snd_soc_component *component) +{ + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + + if (!es8316->jack) + return; /* Already disabled (or never enabled) */ + + disable_irq(es8316->irq); + + mutex_lock(&es8316->lock); + + snd_soc_component_update_bits(component, ES8316_GPIO_DEBOUNCE, + ES8316_GPIO_ENABLE_INTERRUPT, 0); + + if (es8316->jack->status & SND_JACK_MICROPHONE) { + es8316_disable_micbias_for_mic_gnd_short_detect(component); + snd_soc_jack_report(es8316->jack, 0, SND_JACK_BTN_0); + } + + es8316->jack = NULL; + + mutex_unlock(&es8316->lock); +} + +static int es8316_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + if (jack) + es8316_enable_jack_detect(component, jack); + else + es8316_disable_jack_detect(component); + + return 0; +} + +static int es8316_probe(struct snd_soc_component *component) +{ + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + int ret; + + es8316->component = component; + + es8316->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(es8316->mclk)) { + dev_err(component->dev, "unable to get mclk\n"); + return PTR_ERR(es8316->mclk); + } + if (!es8316->mclk) + dev_warn(component->dev, "assuming static mclk\n"); + + ret = clk_prepare_enable(es8316->mclk); + if (ret) { + dev_err(component->dev, "unable to enable mclk\n"); + return ret; + } + + /* Reset codec and enable current state machine */ + snd_soc_component_write(component, ES8316_RESET, 0x3f); + usleep_range(5000, 5500); + snd_soc_component_write(component, ES8316_RESET, ES8316_RESET_CSM_ON); + msleep(30); + + /* + * Documentation is unclear, but this value from the vendor driver is + * needed otherwise audio output is silent. + */ + snd_soc_component_write(component, ES8316_SYS_VMIDSEL, 0xff); + + /* + * Documentation for this register is unclear and incomplete, + * but here is a vendor-provided value that improves volume + * and quality for Intel CHT platforms. + */ + snd_soc_component_write(component, ES8316_CLKMGR_ADCOSR, 0x32); + + return 0; +} + +static void es8316_remove(struct snd_soc_component *component) +{ + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + + clk_disable_unprepare(es8316->mclk); +} + +static int es8316_resume(struct snd_soc_component *component) +{ + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(es8316->regmap, false); + regcache_sync(es8316->regmap); + + return 0; +} + +static int es8316_suspend(struct snd_soc_component *component) +{ + struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(es8316->regmap, true); + regcache_mark_dirty(es8316->regmap); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_es8316 = { + .probe = es8316_probe, + .remove = es8316_remove, + .resume = es8316_resume, + .suspend = es8316_suspend, + .set_jack = es8316_set_jack, + .controls = es8316_snd_controls, + .num_controls = ARRAY_SIZE(es8316_snd_controls), + .dapm_widgets = es8316_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8316_dapm_widgets), + .dapm_routes = es8316_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8316_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static bool es8316_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ES8316_GPIO_FLAG: + return true; + default: + return false; + } +} + +static const struct regmap_config es8316_regmap = { + .reg_bits = 8, + .val_bits = 8, + .use_single_read = true, + .use_single_write = true, + .max_register = 0x53, + .volatile_reg = es8316_volatile_reg, + .cache_type = REGCACHE_MAPLE, +}; + +static int es8316_i2c_probe(struct i2c_client *i2c_client) +{ + struct device *dev = &i2c_client->dev; + struct es8316_priv *es8316; + int ret; + + es8316 = devm_kzalloc(&i2c_client->dev, sizeof(struct es8316_priv), + GFP_KERNEL); + if (es8316 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c_client, es8316); + + es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); + if (IS_ERR(es8316->regmap)) + return PTR_ERR(es8316->regmap); + + es8316->irq = i2c_client->irq; + mutex_init(&es8316->lock); + + if (es8316->irq > 0) { + ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_NO_AUTOEN, + "es8316", es8316); + if (ret) { + dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret); + es8316->irq = -ENXIO; + } + } + + return devm_snd_soc_register_component(&i2c_client->dev, + &soc_component_dev_es8316, + &es8316_dai, 1); +} + +static const struct i2c_device_id es8316_i2c_id[] = { + {"es8316", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, es8316_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id es8316_of_match[] = { + { .compatible = "everest,es8316", }, + {}, +}; +MODULE_DEVICE_TABLE(of, es8316_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id es8316_acpi_match[] = { + {"ESSX8316", 0}, + {"ESSX8336", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, es8316_acpi_match); +#endif + +static struct i2c_driver es8316_i2c_driver = { + .driver = { + .name = "es8316", + .acpi_match_table = ACPI_PTR(es8316_acpi_match), + .of_match_table = of_match_ptr(es8316_of_match), + }, + .probe = es8316_i2c_probe, + .id_table = es8316_i2c_id, +}; +module_i2c_driver(es8316_i2c_driver); + +MODULE_DESCRIPTION("Everest Semi ES8316 ALSA SoC Codec Driver"); +MODULE_AUTHOR("David Yang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/es8316.h b/sound/soc/codecs/es8316.h new file mode 100644 index 0000000000..c335138e28 --- /dev/null +++ b/sound/soc/codecs/es8316.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright Everest Semiconductor Co.,Ltd + * + * Author: David Yang + */ + +#ifndef _ES8316_H +#define _ES8316_H + +/* + * ES8316 register space + */ + +/* Reset Control */ +#define ES8316_RESET 0x00 + +/* Clock Management */ +#define ES8316_CLKMGR_CLKSW 0x01 +#define ES8316_CLKMGR_CLKSEL 0x02 +#define ES8316_CLKMGR_ADCOSR 0x03 +#define ES8316_CLKMGR_ADCDIV1 0x04 +#define ES8316_CLKMGR_ADCDIV2 0x05 +#define ES8316_CLKMGR_DACDIV1 0x06 +#define ES8316_CLKMGR_DACDIV2 0x07 +#define ES8316_CLKMGR_CPDIV 0x08 + +/* Serial Data Port Control */ +#define ES8316_SERDATA1 0x09 +#define ES8316_SERDATA_ADC 0x0a +#define ES8316_SERDATA_DAC 0x0b + +/* System Control */ +#define ES8316_SYS_VMIDSEL 0x0c +#define ES8316_SYS_PDN 0x0d +#define ES8316_SYS_LP1 0x0e +#define ES8316_SYS_LP2 0x0f +#define ES8316_SYS_VMIDLOW 0x10 +#define ES8316_SYS_VSEL 0x11 +#define ES8316_SYS_REF 0x12 + +/* Headphone Mixer */ +#define ES8316_HPMIX_SEL 0x13 +#define ES8316_HPMIX_SWITCH 0x14 +#define ES8316_HPMIX_PDN 0x15 +#define ES8316_HPMIX_VOL 0x16 + +/* Charge Pump Headphone driver */ +#define ES8316_CPHP_OUTEN 0x17 +#define ES8316_CPHP_ICAL_VOL 0x18 +#define ES8316_CPHP_PDN1 0x19 +#define ES8316_CPHP_PDN2 0x1a +#define ES8316_CPHP_LDOCTL 0x1b + +/* Calibration */ +#define ES8316_CAL_TYPE 0x1c +#define ES8316_CAL_SET 0x1d +#define ES8316_CAL_HPLIV 0x1e +#define ES8316_CAL_HPRIV 0x1f +#define ES8316_CAL_HPLMV 0x20 +#define ES8316_CAL_HPRMV 0x21 + +/* ADC Control */ +#define ES8316_ADC_PDN_LINSEL 0x22 +#define ES8316_ADC_PGAGAIN 0x23 +#define ES8316_ADC_D2SEPGA 0x24 +#define ES8316_ADC_DMIC 0x25 +#define ES8316_ADC_MUTE 0x26 +#define ES8316_ADC_VOLUME 0x27 +#define ES8316_ADC_ALC1 0x29 +#define ES8316_ADC_ALC2 0x2a +#define ES8316_ADC_ALC3 0x2b +#define ES8316_ADC_ALC4 0x2c +#define ES8316_ADC_ALC5 0x2d +#define ES8316_ADC_ALC_NG 0x2e + +/* DAC Control */ +#define ES8316_DAC_PDN 0x2f +#define ES8316_DAC_SET1 0x30 +#define ES8316_DAC_SET2 0x31 +#define ES8316_DAC_SET3 0x32 +#define ES8316_DAC_VOLL 0x33 +#define ES8316_DAC_VOLR 0x34 + +/* GPIO */ +#define ES8316_GPIO_SEL 0x4d +#define ES8316_GPIO_DEBOUNCE 0x4e +#define ES8316_GPIO_FLAG 0x4f + +/* Test mode */ +#define ES8316_TESTMODE 0x50 +#define ES8316_TEST1 0x51 +#define ES8316_TEST2 0x52 +#define ES8316_TEST3 0x53 + +/* + * Field definitions + */ + +/* ES8316_RESET */ +#define ES8316_RESET_CSM_ON 0x80 + +/* ES8316_CLKMGR_CLKSW */ +#define ES8316_CLKMGR_CLKSW_MCLK_ON 0x40 +#define ES8316_CLKMGR_CLKSW_BCLK_ON 0x20 + +/* ES8316_SERDATA1 */ +#define ES8316_SERDATA1_MASTER 0x80 +#define ES8316_SERDATA1_BCLK_INV 0x20 + +/* ES8316_SERDATA_ADC and _DAC */ +#define ES8316_SERDATA2_FMT_MASK 0x3 +#define ES8316_SERDATA2_FMT_I2S 0x00 +#define ES8316_SERDATA2_FMT_LEFTJ 0x01 +#define ES8316_SERDATA2_FMT_RIGHTJ 0x02 +#define ES8316_SERDATA2_FMT_PCM 0x03 +#define ES8316_SERDATA2_ADCLRP 0x20 +#define ES8316_SERDATA2_LEN_MASK 0x1c +#define ES8316_SERDATA2_LEN_24 0x00 +#define ES8316_SERDATA2_LEN_20 0x04 +#define ES8316_SERDATA2_LEN_18 0x08 +#define ES8316_SERDATA2_LEN_16 0x0c +#define ES8316_SERDATA2_LEN_32 0x10 + +/* ES8316_GPIO_DEBOUNCE */ +#define ES8316_GPIO_ENABLE_INTERRUPT 0x02 + +/* ES8316_GPIO_FLAG */ +#define ES8316_GPIO_FLAG_GM_NOT_SHORTED 0x02 +#define ES8316_GPIO_FLAG_HP_NOT_INSERTED 0x04 + +#endif diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c new file mode 100644 index 0000000000..6c263086c4 --- /dev/null +++ b/sound/soc/codecs/es8326.c @@ -0,0 +1,1061 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// es8326.c -- es8326 ALSA SoC audio driver +// Copyright Everest Semiconductor Co., Ltd +// +// Authors: David Yang +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "es8326.h" + +struct es8326_priv { + struct clk *mclk; + struct i2c_client *i2c; + struct regmap *regmap; + struct snd_soc_component *component; + struct delayed_work jack_detect_work; + struct delayed_work button_press_work; + struct snd_soc_jack *jack; + int irq; + /* The lock protects the situation that an irq is generated + * while enabling or disabling or during an irq. + */ + struct mutex lock; + u8 mic1_src; + u8 mic2_src; + u8 jack_pol; + u8 interrupt_src; + u8 interrupt_clk; + bool jd_inverted; + unsigned int sysclk; + + bool calibrated; + int version; + int hp; + int jack_remove_retry; +}; + +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_pga_tlv, 0, 600, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(softramp_rate, 0, 100, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_target_tlv, -3200, 200, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(drc_recovery_tlv, -125, 250, 0); + +static const char *const winsize[] = { + "0.25db/2 LRCK", + "0.25db/4 LRCK", + "0.25db/8 LRCK", + "0.25db/16 LRCK", + "0.25db/32 LRCK", + "0.25db/64 LRCK", + "0.25db/128 LRCK", + "0.25db/256 LRCK", + "0.25db/512 LRCK", + "0.25db/1024 LRCK", + "0.25db/2048 LRCK", + "0.25db/4096 LRCK", + "0.25db/8192 LRCK", + "0.25db/16384 LRCK", + "0.25db/32768 LRCK", + "0.25db/65536 LRCK", +}; + +static const char *const dacpol_txt[] = { + "Normal", "R Invert", "L Invert", "L + R Invert" }; + +static const struct soc_enum dacpol = + SOC_ENUM_SINGLE(ES8326_DAC_DSM, 4, 4, dacpol_txt); +static const struct soc_enum alc_winsize = + SOC_ENUM_SINGLE(ES8326_ADC_RAMPRATE, 4, 16, winsize); +static const struct soc_enum drc_winsize = + SOC_ENUM_SINGLE(ES8326_DRC_WINSIZE, 4, 16, winsize); + +static const struct snd_kcontrol_new es8326_snd_controls[] = { + SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DAC_VOL, 0, 0xbf, 0, dac_vol_tlv), + SOC_ENUM("Playback Polarity", dacpol), + SOC_SINGLE_TLV("DAC Ramp Rate", ES8326_DAC_RAMPRATE, 0, 0x0f, 0, softramp_rate), + SOC_SINGLE_TLV("DRC Recovery Level", ES8326_DRC_RECOVERY, 0, 4, 0, drc_recovery_tlv), + SOC_ENUM("DRC Winsize", drc_winsize), + SOC_SINGLE_TLV("DRC Target Level", ES8326_DRC_WINSIZE, 0, 0x0f, 0, drc_target_tlv), + + SOC_DOUBLE_R_TLV("ADC Capture Volume", ES8326_ADC1_VOL, ES8326_ADC2_VOL, 0, 0xff, 0, + adc_vol_tlv), + SOC_DOUBLE_TLV("ADC PGA Volume", ES8326_ADC_SCALE, 4, 0, 5, 0, adc_pga_tlv), + SOC_SINGLE_TLV("ADC PGA Gain Volume", ES8326_PGAGAIN, 0, 10, 0, adc_analog_pga_tlv), + SOC_SINGLE_TLV("ADC Ramp Rate", ES8326_ADC_RAMPRATE, 0, 0x0f, 0, softramp_rate), + SOC_SINGLE("ALC Capture Switch", ES8326_ALC_RECOVERY, 3, 1, 0), + SOC_SINGLE_TLV("ALC Capture Recovery Level", ES8326_ALC_LEVEL, + 0, 4, 0, drc_recovery_tlv), + SOC_ENUM("ALC Capture Winsize", alc_winsize), + SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL, + 0, 0x0f, 0, drc_target_tlv), + +}; + +static const struct snd_soc_dapm_widget es8326_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("MIC3"), + SND_SOC_DAPM_INPUT("MIC4"), + + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + + /* Digital Interface */ + SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S IN", "I2S1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* ADC Digital Mute */ + SND_SOC_DAPM_PGA("ADC L1", ES8326_ADC_MUTE, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("ADC R1", ES8326_ADC_MUTE, 1, 1, NULL, 0), + SND_SOC_DAPM_PGA("ADC L2", ES8326_ADC_MUTE, 2, 1, NULL, 0), + SND_SOC_DAPM_PGA("ADC R2", ES8326_ADC_MUTE, 3, 1, NULL, 0), + + /* Analog Power Supply*/ + SND_SOC_DAPM_DAC("Right DAC", NULL, ES8326_ANA_PDN, 0, 1), + SND_SOC_DAPM_DAC("Left DAC", NULL, ES8326_ANA_PDN, 1, 1), + SND_SOC_DAPM_SUPPLY("MICBIAS1", ES8326_ANA_MICBIAS, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", ES8326_ANA_MICBIAS, 3, 0, NULL, 0), + + SND_SOC_DAPM_PGA("LHPMIX", ES8326_DAC2HPMIX, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("RHPMIX", ES8326_DAC2HPMIX, 3, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route es8326_dapm_routes[] = { + {"ADC L1", NULL, "MIC1"}, + {"ADC R1", NULL, "MIC2"}, + {"ADC L2", NULL, "MIC3"}, + {"ADC R2", NULL, "MIC4"}, + + {"ADC L", NULL, "ADC L1"}, + {"ADC R", NULL, "ADC R1"}, + {"ADC L", NULL, "ADC L2"}, + {"ADC R", NULL, "ADC R2"}, + + {"I2S OUT", NULL, "ADC L"}, + {"I2S OUT", NULL, "ADC R"}, + + {"Right DAC", NULL, "I2S IN"}, + {"Left DAC", NULL, "I2S IN"}, + + {"LHPMIX", NULL, "Left DAC"}, + {"RHPMIX", NULL, "Right DAC"}, + + {"HPOL", NULL, "LHPMIX"}, + {"HPOR", NULL, "RHPMIX"}, +}; + +static bool es8326_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ES8326_HPL_OFFSET_INI: + case ES8326_HPR_OFFSET_INI: + case ES8326_HPDET_STA: + case ES8326_CTIA_OMTP_STA: + case ES8326_CSM_MUTE_STA: + return true; + default: + return false; + } +} + +static const struct regmap_config es8326_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + .volatile_reg = es8326_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +struct _coeff_div { + u16 fs; + u32 rate; + u32 mclk; + u8 reg4; + u8 reg5; + u8 reg6; + u8 reg7; + u8 reg8; + u8 reg9; + u8 rega; + u8 regb; +}; + +/* codec hifi mclk clock divider coefficients */ +/* {ratio, LRCK, MCLK, REG04, REG05, REG06, REG07, REG08, REG09, REG10, REG11} */ +static const struct _coeff_div coeff_div[] = { + {32, 8000, 256000, 0x60, 0x00, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, + {32, 16000, 512000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x3F}, + {32, 44100, 1411200, 0x00, 0x00, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {32, 48000, 1536000, 0x00, 0x00, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {36, 8000, 288000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x23, 0x47}, + {36, 16000, 576000, 0x20, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x23, 0x47}, + {48, 8000, 384000, 0x60, 0x02, 0x1F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, + {48, 16000, 768000, 0x20, 0x02, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x3F}, + {48, 48000, 2304000, 0x00, 0x02, 0x0D, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {64, 8000, 512000, 0x60, 0x00, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, + {64, 16000, 1024000, 0x20, 0x00, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x3F}, + + {64, 44100, 2822400, 0x00, 0x00, 0x11, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {64, 48000, 3072000, 0x00, 0x00, 0x11, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {72, 8000, 576000, 0x20, 0x00, 0x13, 0x35, 0x0A, 0x1B, 0x23, 0x47}, + {72, 16000, 1152000, 0x20, 0x00, 0x05, 0x75, 0x0A, 0x1B, 0x23, 0x47}, + {96, 8000, 768000, 0x60, 0x02, 0x1D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, + {96, 16000, 1536000, 0x20, 0x02, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x3F}, + {100, 48000, 4800000, 0x04, 0x04, 0x3F, 0x6D, 0x38, 0x08, 0x4f, 0x1f}, + {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27}, + {128, 8000, 1024000, 0x60, 0x00, 0x13, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + {128, 16000, 2048000, 0x20, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + + {128, 44100, 5644800, 0x00, 0x00, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {128, 48000, 6144000, 0x00, 0x00, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x23, 0x47}, + {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x23, 0x47}, + {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, + {192, 16000, 3072000, 0x20, 0x02, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x3F}, + {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0x0A, 0x0A, 0x27, 0x27}, + {256, 8000, 2048000, 0x60, 0x00, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + + {256, 44100, 11289600, 0x00, 0x00, 0x10, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {256, 48000, 12288000, 0x00, 0x00, 0x30, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x23, 0x47}, + {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x0A, 0x1B, 0x1F, 0x7F}, + {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + {384, 48000, 18432000, 0x00, 0x02, 0x01, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {400, 48000, 19200000, 0x09, 0x04, 0x0f, 0x6d, 0x3a, 0x0A, 0x4F, 0x1F}, + {500, 48000, 24000000, 0x18, 0x04, 0x1F, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + {512, 16000, 8192000, 0x20, 0x00, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + + {512, 44100, 22579200, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {512, 48000, 24576000, 0x00, 0x00, 0x00, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + {800, 48000, 38400000, 0x00, 0x18, 0x13, 0x2D, 0x0A, 0x0A, 0x1F, 0x1F}, + {1024, 8000, 8192000, 0x60, 0x00, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + {1152, 16000, 18432000, 0x20, 0x08, 0x11, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + {1536, 8000, 12288000, 0x60, 0x02, 0x01, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + + {1536, 16000, 24576000, 0x20, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x3F}, + {1625, 8000, 13000000, 0x0C, 0x18, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27}, + {1625, 16000, 26000000, 0x0C, 0x18, 0x1F, 0x2D, 0x0A, 0x0A, 0x27, 0x27}, + {2048, 8000, 16384000, 0x60, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + {2304, 8000, 18432000, 0x40, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x5F}, + {3072, 8000, 24576000, 0x60, 0x02, 0x10, 0x35, 0x0A, 0x1B, 0x1F, 0x7F}, + {3250, 8000, 26000000, 0x0C, 0x18, 0x0F, 0x2D, 0x0A, 0x0A, 0x27, 0x27}, + +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + + return -EINVAL; +} + +static int es8326_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *codec = codec_dai->component; + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(codec); + + es8326->sysclk = freq; + + return 0; +} + +static int es8326_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u8 iface = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFP: + snd_soc_component_update_bits(component, ES8326_RESET, + ES8326_MASTER_MODE_EN, ES8326_MASTER_MODE_EN); + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_RIGHT_J: + dev_err(component->dev, "Codec driver does not support right justified\n"); + return -EINVAL; + case SND_SOC_DAIFMT_LEFT_J: + iface |= ES8326_DAIFMT_LEFT_J; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= ES8326_DAIFMT_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= ES8326_DAIFMT_DSP_B; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, ES8326_FMT, ES8326_DAIFMT_MASK, iface); + + return 0; +} + +static int es8326_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + u8 srate = 0; + int coeff; + + coeff = get_coeff(es8326->sysclk, params_rate(params)); + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + srate |= ES8326_S16_LE; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + srate |= ES8326_S20_3_LE; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + srate |= ES8326_S18_LE; + break; + case SNDRV_PCM_FORMAT_S24_LE: + srate |= ES8326_S24_LE; + break; + case SNDRV_PCM_FORMAT_S32_LE: + srate |= ES8326_S32_LE; + break; + default: + return -EINVAL; + } + + /* set iface & srate */ + snd_soc_component_update_bits(component, ES8326_FMT, ES8326_DATA_LEN_MASK, srate); + + if (coeff >= 0) { + regmap_write(es8326->regmap, ES8326_CLK_DIV1, + coeff_div[coeff].reg4); + regmap_write(es8326->regmap, ES8326_CLK_DIV2, + coeff_div[coeff].reg5); + regmap_write(es8326->regmap, ES8326_CLK_DLL, + coeff_div[coeff].reg6); + regmap_write(es8326->regmap, ES8326_CLK_MUX, + coeff_div[coeff].reg7); + regmap_write(es8326->regmap, ES8326_CLK_ADC_SEL, + coeff_div[coeff].reg8); + regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL, + coeff_div[coeff].reg9); + regmap_write(es8326->regmap, ES8326_CLK_ADC_OSR, + coeff_div[coeff].rega); + regmap_write(es8326->regmap, ES8326_CLK_DAC_OSR, + coeff_div[coeff].regb); + } else { + dev_warn(component->dev, "Clock coefficients do not match"); + } + + return 0; +} + +static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int offset_l, offset_r; + + if (mute) { + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, + ES8326_MUTE_MASK, ES8326_MUTE); + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xf0); + } else { + if (!es8326->calibrated) { + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL); + msleep(30); + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); + regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l); + regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r); + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); + regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l); + regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r); + es8326->calibrated = true; + } + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa0); + regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80); + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON); + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, + ES8326_MUTE_MASK, ~(ES8326_MUTE)); + } + return 0; +} + +static int es8326_set_bias_level(struct snd_soc_component *codec, + enum snd_soc_bias_level level) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + ret = clk_prepare_enable(es8326->mclk); + if (ret) + return ret; + + regmap_write(es8326->regmap, ES8326_RESET, 0x9f); + msleep(20); + regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00); + regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk); + regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, + (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); + regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); + regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40); + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00); + regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20); + regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + break; + case SND_SOC_BIAS_OFF: + clk_disable_unprepare(es8326->mclk); + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); + regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x00); + regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00); + regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT); + break; + } + + return 0; +} + +#define es8326_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops es8326_ops = { + .hw_params = es8326_pcm_hw_params, + .set_fmt = es8326_set_dai_fmt, + .set_sysclk = es8326_set_dai_sysclk, + .mute_stream = es8326_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver es8326_dai = { + .name = "ES8326 HiFi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = es8326_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = es8326_FORMATS, + }, + .ops = &es8326_ops, + .symmetric_rate = 1, +}; + +static void es8326_enable_micbias(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS1"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS2"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +static void es8326_disable_micbias(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS1"); + snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS2"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +/* + * For button detection, set the following in soundcard + * snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + * snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + * snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + */ +static void es8326_jack_button_handler(struct work_struct *work) +{ + struct es8326_priv *es8326 = + container_of(work, struct es8326_priv, button_press_work.work); + struct snd_soc_component *comp = es8326->component; + unsigned int iface; + static int button_to_report, press_count; + static int prev_button, cur_button; + + if (!(es8326->jack->status & SND_JACK_HEADSET)) /* Jack unplugged */ + return; + + mutex_lock(&es8326->lock); + iface = snd_soc_component_read(comp, ES8326_HPDET_STA); + switch (iface) { + case 0x93: + /* pause button detected */ + cur_button = SND_JACK_BTN_0; + break; + case 0x6f: + case 0x4b: + /* button volume up */ + cur_button = SND_JACK_BTN_1; + break; + case 0x27: + /* button volume down */ + cur_button = SND_JACK_BTN_2; + break; + case 0x1e: + case 0xe2: + /* button released or not pressed */ + cur_button = 0; + break; + default: + break; + } + + if ((prev_button == cur_button) && (cur_button != 0)) { + press_count++; + if (press_count > 3) { + /* report a press every 120ms */ + snd_soc_jack_report(es8326->jack, cur_button, + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2); + press_count = 0; + } + button_to_report = cur_button; + queue_delayed_work(system_wq, &es8326->button_press_work, + msecs_to_jiffies(35)); + } else if (prev_button != cur_button) { + /* mismatch, detect again */ + prev_button = cur_button; + queue_delayed_work(system_wq, &es8326->button_press_work, + msecs_to_jiffies(35)); + } else { + /* released or no pressed */ + if (button_to_report != 0) { + snd_soc_jack_report(es8326->jack, button_to_report, + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2); + snd_soc_jack_report(es8326->jack, 0, + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2); + button_to_report = 0; + } + } + mutex_unlock(&es8326->lock); +} + +static void es8326_jack_detect_handler(struct work_struct *work) +{ + struct es8326_priv *es8326 = + container_of(work, struct es8326_priv, jack_detect_work.work); + struct snd_soc_component *comp = es8326->component; + unsigned int iface; + + mutex_lock(&es8326->lock); + iface = snd_soc_component_read(comp, ES8326_HPDET_STA); + dev_dbg(comp->dev, "gpio flag %#04x", iface); + + if (es8326->jack_remove_retry == 1) { + if (iface & ES8326_HPINSERT_FLAG) + es8326->jack_remove_retry = 2; + else + es8326->jack_remove_retry = 0; + + dev_dbg(comp->dev, "remove event check, set HPJACK_POL normal, cnt = %d\n", + es8326->jack_remove_retry); + /* + * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event + */ + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? + ~es8326->jack_pol : es8326->jack_pol)); + goto exit; + } + + if ((iface & ES8326_HPINSERT_FLAG) == 0) { + /* Jack unplugged or spurious IRQ */ + dev_dbg(comp->dev, "No headset detected\n"); + es8326_disable_micbias(es8326->component); + if (es8326->jack->status & SND_JACK_HEADPHONE) { + dev_dbg(comp->dev, "Report hp remove event\n"); + snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); + /* mute adc when mic path switch */ + regmap_write(es8326->regmap, ES8326_ADC_SCALE, 0x33); + regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x44); + regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x66); + es8326->hp = 0; + } + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + /* + * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event + */ + if (es8326->jack_remove_retry == 0) { + es8326->jack_remove_retry = 1; + dev_dbg(comp->dev, "remove event check, invert HPJACK_POL, cnt = %d\n", + es8326->jack_remove_retry); + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ? + es8326->jack_pol : ~es8326->jack_pol)); + + } else { + es8326->jack_remove_retry = 0; + } + } else if ((iface & ES8326_HPINSERT_FLAG) == ES8326_HPINSERT_FLAG) { + es8326->jack_remove_retry = 0; + if (es8326->hp == 0) { + dev_dbg(comp->dev, "First insert, start OMTP/CTIA type check\n"); + /* + * set auto-check mode, then restart jack_detect_work after 100ms. + * Don't report jack status. + */ + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01); + usleep_range(50000, 70000); + regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00); + queue_delayed_work(system_wq, &es8326->jack_detect_work, + msecs_to_jiffies(100)); + es8326->hp = 1; + goto exit; + } + if (es8326->jack->status & SND_JACK_HEADSET) { + /* detect button */ + dev_dbg(comp->dev, "button pressed\n"); + queue_delayed_work(system_wq, &es8326->button_press_work, 10); + goto exit; + } + if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) { + dev_dbg(comp->dev, "Headphone detected\n"); + snd_soc_jack_report(es8326->jack, + SND_JACK_HEADPHONE, SND_JACK_HEADSET); + } else { + dev_dbg(comp->dev, "Headset detected\n"); + snd_soc_jack_report(es8326->jack, + SND_JACK_HEADSET, SND_JACK_HEADSET); + + regmap_write(es8326->regmap, ES8326_ADC_SCALE, 0x33); + regmap_update_bits(es8326->regmap, ES8326_PGA_PDN, + 0x08, 0x08); + regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, + 0x80, 0x80); + regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00); + regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00); + regmap_update_bits(es8326->regmap, ES8326_PGA_PDN, + 0x08, 0x00); + usleep_range(10000, 15000); + } + } +exit: + mutex_unlock(&es8326->lock); +} + +static irqreturn_t es8326_irq(int irq, void *dev_id) +{ + struct es8326_priv *es8326 = dev_id; + struct snd_soc_component *comp = es8326->component; + + if (!es8326->jack) + goto out; + + es8326_enable_micbias(comp); + + if (es8326->jack->status & SND_JACK_HEADSET) + queue_delayed_work(system_wq, &es8326->jack_detect_work, + msecs_to_jiffies(10)); + else + queue_delayed_work(system_wq, &es8326->jack_detect_work, + msecs_to_jiffies(600)); + +out: + return IRQ_HANDLED; +} + +static int es8326_calibrate(struct snd_soc_component *component) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + unsigned int reg; + unsigned int offset_l, offset_r; + + regmap_read(es8326->regmap, ES8326_CHIP_VERSION, ®); + es8326->version = reg; + + if ((es8326->version == ES8326_VERSION_B) && (es8326->calibrated == false)) { + dev_dbg(component->dev, "ES8326_VERSION_B, calibrating\n"); + regmap_write(es8326->regmap, ES8326_CLK_INV, 0xc0); + regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x01); + regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30); + regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed); + regmap_write(es8326->regmap, ES8326_CLK_TRI, 0xc1); + regmap_write(es8326->regmap, ES8326_DAC_MUTE, 0x03); + regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7f); + regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x33); + regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88); + regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80); + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); + regmap_write(es8326->regmap, ES8326_RESET, 0xc0); + usleep_range(15000, 20000); + + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, ES8326_HP_OFF); + regmap_read(es8326->regmap, ES8326_CSM_MUTE_STA, ®); + if ((reg & 0xf0) != 0x40) + msleep(50); + + regmap_write(es8326->regmap, ES8326_HP_CAL, 0xd4); + msleep(200); + regmap_write(es8326->regmap, ES8326_HP_CAL, 0x4d); + msleep(200); + regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF); + regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l); + regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r); + regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c); + regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l); + regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r); + regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00); + + es8326->calibrated = true; + } + + return 0; +} + +static int es8326_resume(struct snd_soc_component *component) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(es8326->regmap, false); + regcache_sync(es8326->regmap); + + /* reset internal clock state */ + regmap_write(es8326->regmap, ES8326_RESET, 0x1f); + regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E); + usleep_range(10000, 15000); + regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88); + /* set headphone default type and detect pin */ + regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x81); + regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05); + + /* set internal oscillator as clock source of headpone cp */ + regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x84); + regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON); + /* clock manager reset release */ + regmap_write(es8326->regmap, ES8326_RESET, 0x17); + /* set headphone detection as half scan mode */ + regmap_write(es8326->regmap, ES8326_HP_MISC, 0x08); + regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00); + + /* enable headphone driver */ + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7); + usleep_range(2000, 5000); + regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xab); + usleep_range(2000, 5000); + regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xbb); + usleep_range(2000, 5000); + regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1); + + regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00); + regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xc4); + regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x81); + regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x00); + /* calibrate for B version */ + es8326_calibrate(component); + /* turn off headphone out */ + regmap_write(es8326->regmap, ES8326_HP_CAL, 0x00); + /* set ADC and DAC in low power mode */ + regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0); + + /* force micbias on */ + regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0x4f); + regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x08); + regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F); + /* select vdda as micbias source */ + regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23); + /* set dac dsmclip = 1 */ + regmap_write(es8326->regmap, ES8326_DAC_DSM, 0x08); + regmap_write(es8326->regmap, ES8326_DAC_VPPSCALE, 0x15); + + regmap_write(es8326->regmap, ES8326_INT_SOURCE, + (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON)); + regmap_write(es8326->regmap, ES8326_INTOUT_IO, + es8326->interrupt_clk); + regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, + (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT)); + regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT); + + regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b); + regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON); + regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK, + ES8326_MIC1_SEL); + + regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK, + ES8326_MUTE); + + regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 | + ((es8326->version == ES8326_VERSION_B) ? + (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) : + (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04))); + + es8326->jack_remove_retry = 0; + es8326->hp = 0; + return 0; +} + +static int es8326_suspend(struct snd_soc_component *component) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&es8326->jack_detect_work); + es8326_disable_micbias(component); + es8326->calibrated = false; + regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_OFF); + regcache_cache_only(es8326->regmap, true); + regcache_mark_dirty(es8326->regmap); + + /* reset register value to default */ + regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01); + usleep_range(1000, 3000); + regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00); + return 0; +} + +static int es8326_probe(struct snd_soc_component *component) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + int ret; + + es8326->component = component; + es8326->jd_inverted = device_property_read_bool(component->dev, + "everest,jack-detect-inverted"); + + ret = device_property_read_u8(component->dev, "everest,mic1-src", &es8326->mic1_src); + if (ret != 0) { + dev_dbg(component->dev, "mic1-src return %d", ret); + es8326->mic1_src = ES8326_ADC_AMIC; + } + dev_dbg(component->dev, "mic1-src %x", es8326->mic1_src); + + ret = device_property_read_u8(component->dev, "everest,mic2-src", &es8326->mic2_src); + if (ret != 0) { + dev_dbg(component->dev, "mic2-src return %d", ret); + es8326->mic2_src = ES8326_ADC_DMIC; + } + dev_dbg(component->dev, "mic2-src %x", es8326->mic2_src); + + ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol); + if (ret != 0) { + dev_dbg(component->dev, "jack-pol return %d", ret); + es8326->jack_pol = ES8326_HP_TYPE_AUTO; + } + dev_dbg(component->dev, "jack-pol %x", es8326->jack_pol); + + ret = device_property_read_u8(component->dev, "everest,interrupt-src", + &es8326->interrupt_src); + if (ret != 0) { + dev_dbg(component->dev, "interrupt-src return %d", ret); + es8326->interrupt_src = ES8326_HP_DET_SRC_PIN9; + } + dev_dbg(component->dev, "interrupt-src %x", es8326->interrupt_src); + + ret = device_property_read_u8(component->dev, "everest,interrupt-clk", + &es8326->interrupt_clk); + if (ret != 0) { + dev_dbg(component->dev, "interrupt-clk return %d", ret); + es8326->interrupt_clk = 0x45; + } + dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk); + + es8326_resume(component); + return 0; +} + +static void es8326_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + mutex_lock(&es8326->lock); + if (es8326->jd_inverted) + snd_soc_component_update_bits(component, ES8326_HPDET_TYPE, + ES8326_HP_DET_JACK_POL, ~es8326->jack_pol); + es8326->jack = jack; + + mutex_unlock(&es8326->lock); + es8326_irq(es8326->irq, es8326); +} + +static void es8326_disable_jack_detect(struct snd_soc_component *component) +{ + struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "Enter into %s\n", __func__); + if (!es8326->jack) + return; /* Already disabled (or never enabled) */ + cancel_delayed_work_sync(&es8326->jack_detect_work); + + mutex_lock(&es8326->lock); + if (es8326->jack->status & SND_JACK_MICROPHONE) { + es8326_disable_micbias(component); + snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET); + } + es8326->jack = NULL; + mutex_unlock(&es8326->lock); +} + +static int es8326_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + if (jack) + es8326_enable_jack_detect(component, jack); + else + es8326_disable_jack_detect(component); + + return 0; +} + +static void es8326_remove(struct snd_soc_component *component) +{ + es8326_disable_jack_detect(component); + es8326_set_bias_level(component, SND_SOC_BIAS_OFF); +} + +static const struct snd_soc_component_driver soc_component_dev_es8326 = { + .probe = es8326_probe, + .remove = es8326_remove, + .resume = es8326_resume, + .suspend = es8326_suspend, + .set_bias_level = es8326_set_bias_level, + .set_jack = es8326_set_jack, + .dapm_widgets = es8326_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8326_dapm_widgets), + .dapm_routes = es8326_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8326_dapm_routes), + .controls = es8326_snd_controls, + .num_controls = ARRAY_SIZE(es8326_snd_controls), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int es8326_i2c_probe(struct i2c_client *i2c) +{ + struct es8326_priv *es8326; + int ret; + + es8326 = devm_kzalloc(&i2c->dev, sizeof(struct es8326_priv), GFP_KERNEL); + if (!es8326) + return -ENOMEM; + + i2c_set_clientdata(i2c, es8326); + es8326->i2c = i2c; + mutex_init(&es8326->lock); + es8326->regmap = devm_regmap_init_i2c(i2c, &es8326_regmap_config); + if (IS_ERR(es8326->regmap)) { + ret = PTR_ERR(es8326->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + es8326->irq = i2c->irq; + INIT_DELAYED_WORK(&es8326->jack_detect_work, + es8326_jack_detect_handler); + INIT_DELAYED_WORK(&es8326->button_press_work, + es8326_jack_button_handler); + /* ES8316 is level-based while ES8326 is edge-based */ + ret = devm_request_threaded_irq(&i2c->dev, es8326->irq, NULL, es8326_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "es8326", es8326); + if (ret) { + dev_warn(&i2c->dev, "Failed to request IRQ: %d: %d\n", + es8326->irq, ret); + es8326->irq = -ENXIO; + } + + es8326->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(es8326->mclk)) { + dev_err(&i2c->dev, "unable to get mclk\n"); + return PTR_ERR(es8326->mclk); + } + if (!es8326->mclk) + dev_warn(&i2c->dev, "assuming static mclk\n"); + + ret = clk_prepare_enable(es8326->mclk); + if (ret) { + dev_err(&i2c->dev, "unable to enable mclk\n"); + return ret; + } + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_es8326, + &es8326_dai, 1); +} + +static const struct i2c_device_id es8326_i2c_id[] = { + {"es8326", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, es8326_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id es8326_of_match[] = { + { .compatible = "everest,es8326", }, + {} +}; +MODULE_DEVICE_TABLE(of, es8326_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id es8326_acpi_match[] = { + {"ESSX8326", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, es8326_acpi_match); +#endif + +static struct i2c_driver es8326_i2c_driver = { + .driver = { + .name = "es8326", + .acpi_match_table = ACPI_PTR(es8326_acpi_match), + .of_match_table = of_match_ptr(es8326_of_match), + }, + .probe = es8326_i2c_probe, + .id_table = es8326_i2c_id, +}; +module_i2c_driver(es8326_i2c_driver); + +MODULE_DESCRIPTION("ASoC es8326 driver"); +MODULE_AUTHOR("David Yang "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h new file mode 100644 index 0000000000..90a08351d6 --- /dev/null +++ b/sound/soc/codecs/es8326.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * es8326.h -- es8326 ALSA SoC audio driver + * Copyright Everest Semiconductor Co.,Ltd + * + * Authors: David Yang + */ + +#ifndef _ES8326_H +#define _ES8326_H + +/* ES8326 register space */ +#define ES8326_RESET 0x00 +#define ES8326_CLK_CTL 0x01 +#define ES8326_CLK_INV 0x02 +#define ES8326_CLK_RESAMPLE 0x03 +#define ES8326_CLK_DIV1 0x04 +#define ES8326_CLK_DIV2 0x05 +#define ES8326_CLK_DLL 0x06 +#define ES8326_CLK_MUX 0x07 +#define ES8326_CLK_ADC_SEL 0x08 +#define ES8326_CLK_DAC_SEL 0x09 +#define ES8326_CLK_ADC_OSR 0x0a +#define ES8326_CLK_DAC_OSR 0x0b +#define ES8326_CLK_DIV_CPC 0x0c +#define ES8326_CLK_DIV_BCLK 0x0d +#define ES8326_CLK_TRI 0x0e +#define ES8326_CLK_DIV_LRCK 0x0f +#define ES8326_CLK_VMIDS1 0x10 +#define ES8326_CLK_VMIDS2 0x11 +#define ES8326_CLK_CAL_TIME 0x12 +#define ES8326_FMT 0x13 + +#define ES8326_DAC_MUTE 0x14 +#define ES8326_ADC_MUTE 0x15 +#define ES8326_ANA_PDN 0x16 +#define ES8326_PGA_PDN 0x17 +#define ES8326_VMIDSEL 0x18 +#define ES8326_ANA_LP 0x19 +#define ES8326_ANA_DMS 0x1a +#define ES8326_ANA_MICBIAS 0x1b +#define ES8326_ANA_VSEL 0x1c +#define ES8326_SYS_BIAS 0x1d +#define ES8326_BIAS_SW1 0x1e +#define ES8326_BIAS_SW2 0x1f +#define ES8326_BIAS_SW3 0x20 +#define ES8326_BIAS_SW4 0x21 +#define ES8326_VMIDLOW 0x22 +#define ES8326_PGAGAIN 0x23 +#define ES8326_HP_DRIVER 0x24 +#define ES8326_DAC2HPMIX 0x25 +#define ES8326_HP_VOL 0x26 +#define ES8326_HP_CAL 0x27 +#define ES8326_HP_DRIVER_REF 0x28 +#define ES8326_ADC_SCALE 0x29 +#define ES8326_ADC1_SRC 0x2a +#define ES8326_ADC2_SRC 0x2b +#define ES8326_ADC1_VOL 0x2c +#define ES8326_ADC2_VOL 0x2d +#define ES8326_ADC_RAMPRATE 0x2e +#define ES8326_ALC_RECOVERY 0x32 +#define ES8326_ALC_LEVEL 0x33 +#define ES8326_ADC_HPFS1 0x34 +#define ES8326_ADC_HPFS2 0x35 +#define ES8326_ADC_EQ 0x36 +#define ES8326_HP_OFFSET_CAL 0x4A +#define ES8326_HPL_OFFSET_INI 0x4B +#define ES8326_HPR_OFFSET_INI 0x4C +#define ES8326_DAC_DSM 0x4D +#define ES8326_DAC_RAMPRATE 0x4E +#define ES8326_DAC_VPPSCALE 0x4F +#define ES8326_DAC_VOL 0x50 +#define ES8326_DRC_RECOVERY 0x53 +#define ES8326_DRC_WINSIZE 0x54 +#define ES8326_HPJACK_TIMER 0x56 +#define ES8326_HPDET_TYPE 0x57 +#define ES8326_INT_SOURCE 0x58 +#define ES8326_INTOUT_IO 0x59 +#define ES8326_SDINOUT1_IO 0x5A +#define ES8326_SDINOUT23_IO 0x5B +#define ES8326_JACK_PULSE 0x5C + +#define ES8326_HP_MISC 0xF7 +#define ES8326_CTIA_OMTP_STA 0xF8 +#define ES8326_PULLUP_CTL 0xF9 +#define ES8326_CSM_I2C_STA 0xFA +#define ES8326_HPDET_STA 0xFB +#define ES8326_CSM_MUTE_STA 0xFC +#define ES8326_CHIP_ID1 0xFD +#define ES8326_CHIP_ID2 0xFE +#define ES8326_CHIP_VERSION 0xFF + +/* ES8326_RESET */ +#define ES8326_CSM_ON (1 << 7) +#define ES8326_MASTER_MODE_EN (1 << 6) +#define ES8326_PWRUP_SEQ_EN (1 << 5) +#define ES8326_CODEC_RESET (0x0f << 0) +#define ES8326_CSM_OFF (0 << 7) +#define ES8326_MUTE_MASK (3 << 0) +#define ES8326_MUTE (3 << 0) + +/* ES8326_CLK_CTL */ +#define ES8326_CLK_ON (0x7f << 0) +#define ES8326_CLK_OFF (0 << 0) + +/* ES8326_CLK_INV */ +#define ES8326_BCLK_AS_MCLK (1 << 3) + +/* ES8326_FMT */ +#define ES8326_S24_LE (0 << 2) +#define ES8326_S20_3_LE (1 << 2) +#define ES8326_S18_LE (2 << 2) +#define ES8326_S16_LE (3 << 2) +#define ES8326_S32_LE (4 << 2) +#define ES8326_DATA_LEN_MASK (7 << 2) + +#define ES8326_DAIFMT_MASK ((1 << 5) | (3 << 0)) +#define ES8326_DAIFMT_I2S 0 +#define ES8326_DAIFMT_LEFT_J (1 << 0) +#define ES8326_DAIFMT_DSP_A (3 << 0) +#define ES8326_DAIFMT_DSP_B ((1 << 5) | (3 << 0)) + +/* ES8326_PGAGAIN */ +#define ES8326_MIC_SEL_MASK (3 << 4) +#define ES8326_MIC1_SEL (1 << 4) +#define ES8326_MIC2_SEL (1 << 5) + +/* ES8326_HP_CAL */ +#define ES8326_HP_OFF 0 +#define ES8326_HP_FORCE_CAL ((1 << 7) | (1 << 3)) +#define ES8326_HP_ON ((7 << 4) | (7 << 0)) + +/* ES8326_ADC1_SRC */ +#define ES8326_ADC1_SHIFT 0 +#define ES8326_ADC2_SHIFT 4 +#define ES8326_ADC_SRC_ANA 0 +#define ES8326_ADC_SRC_ANA_INV_SW0 1 +#define ES8326_ADC_SRC_ANA_INV_SW1 2 +#define ES8326_ADC_SRC_DMIC_MCLK 3 +#define ES8326_ADC_SRC_DMIC_SDIN2 4 +#define ES8326_ADC_SRC_DMIC_SDIN2_INV 5 +#define ES8326_ADC_SRC_DMIC_SDIN3 6 +#define ES8326_ADC_SRC_DMIC_SDIN3_INV 7 + +#define ES8326_ADC_AMIC ((ES8326_ADC_SRC_ANA_INV_SW1 << ES8326_ADC2_SHIFT) \ + | (ES8326_ADC_SRC_ANA_INV_SW1 << ES8326_ADC1_SHIFT)) +#define ES8326_ADC_DMIC ((ES8326_ADC_SRC_DMIC_SDIN2 << ES8326_ADC2_SHIFT) \ + | (ES8326_ADC_SRC_DMIC_SDIN2 << ES8326_ADC1_SHIFT)) +/* ES8326_ADC2_SRC */ +#define ES8326_ADC3_SHIFT 0 +#define ES8326_ADC4_SHIFT 3 + +/* ES8326_HPDET_TYPE */ +#define ES8326_HP_DET_SRC_PIN27 (1 << 5) +#define ES8326_HP_DET_SRC_PIN9 (1 << 4) +#define ES8326_HP_DET_JACK_POL (1 << 3) +#define ES8326_HP_DET_BUTTON_POL (1 << 2) +#define ES8326_HP_TYPE_OMTP (3 << 0) +#define ES8326_HP_TYPE_CTIA (2 << 0) +#define ES8326_HP_TYPE_AUTO (1 << 0) +#define ES8326_HP_TYPE_AUTO_INV (0 << 0) + +/* ES8326_INT_SOURCE */ +#define ES8326_INT_SRC_DAC_MOZ (1 << 0) +#define ES8326_INT_SRC_ADC_MOZ (1 << 1) +#define ES8326_INT_SRC_BUTTON (1 << 2) +#define ES8326_INT_SRC_PIN9 (1 << 3) +#define ES8326_INT_SRC_PIN27 (1 << 4) + +/* ES8326_SDINOUT1_IO */ +#define ES8326_IO_INPUT (0 << 0) +#define ES8326_IO_SDIN_SLOT0 (1 << 0) +#define ES8326_IO_SDIN_SLOT1 (2 << 0) +#define ES8326_IO_SDIN_SLOT2 (3 << 0) +#define ES8326_IO_SDIN_SLOT7 (8 << 0) +#define ES8326_IO_DMIC_CLK (9 << 0) +#define ES8326_IO_DMIC_CLK_INV (0x0a << 0) +#define ES8326_IO_SDOUT2 (0x0b << 0) +#define ES8326_IO_LOW (0x0e << 0) +#define ES8326_IO_HIGH (0x0f << 0) +#define ES8326_ADC2DAC (1 << 3) +#define ES8326_SDINOUT1_SHIFT 4 + +/* ES8326_SDINOUT23_IO */ +#define ES8326_SDINOUT2_SHIFT 4 +#define ES8326_SDINOUT3_SHIFT 0 + +/* ES8326_HPDET_STA */ +#define ES8326_HPINSERT_FLAG (1 << 1) +#define ES8326_HPBUTTON_FLAG (1 << 0) + +/* ES8326_CHIP_VERSION 0xFF */ +#define ES8326_VERSION (1 << 0) +#define ES8326_VERSION_B (3 << 0) + +#endif diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c new file mode 100644 index 0000000000..3c4aaa0032 --- /dev/null +++ b/sound/soc/codecs/es8328-i2c.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * es8328-i2c.c -- ES8328 ALSA SoC I2C Audio driver + * + * Copyright 2014 Sutajio Ko-Usagi PTE LTD + * + * Author: Sean Cross + */ + +#include +#include +#include + +#include + +#include "es8328.h" + +static const struct i2c_device_id es8328_id[] = { + { "es8328", 0 }, + { "es8388", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, es8328_id); + +static const struct of_device_id es8328_of_match[] = { + { .compatible = "everest,es8328", }, + { .compatible = "everest,es8388", }, + { } +}; +MODULE_DEVICE_TABLE(of, es8328_of_match); + +static int es8328_i2c_probe(struct i2c_client *i2c) +{ + return es8328_probe(&i2c->dev, + devm_regmap_init_i2c(i2c, &es8328_regmap_config)); +} + +static struct i2c_driver es8328_i2c_driver = { + .driver = { + .name = "es8328", + .of_match_table = es8328_of_match, + }, + .probe = es8328_i2c_probe, + .id_table = es8328_id, +}; + +module_i2c_driver(es8328_i2c_driver); + +MODULE_DESCRIPTION("ASoC ES8328 audio CODEC I2C driver"); +MODULE_AUTHOR("Sean Cross "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8328-spi.c b/sound/soc/codecs/es8328-spi.c new file mode 100644 index 0000000000..88e353ae52 --- /dev/null +++ b/sound/soc/codecs/es8328-spi.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * es8328.c -- ES8328 ALSA SoC SPI Audio driver + * + * Copyright 2014 Sutajio Ko-Usagi PTE LTD + * + * Author: Sean Cross + */ + +#include +#include +#include +#include +#include "es8328.h" + +static const struct of_device_id es8328_of_match[] = { + { .compatible = "everest,es8328", }, + { } +}; +MODULE_DEVICE_TABLE(of, es8328_of_match); + +static int es8328_spi_probe(struct spi_device *spi) +{ + return es8328_probe(&spi->dev, + devm_regmap_init_spi(spi, &es8328_regmap_config)); +} + +static struct spi_driver es8328_spi_driver = { + .driver = { + .name = "es8328", + .of_match_table = es8328_of_match, + }, + .probe = es8328_spi_probe, +}; + +module_spi_driver(es8328_spi_driver); +MODULE_DESCRIPTION("ASoC ES8328 audio CODEC SPI driver"); +MODULE_AUTHOR("Sean Cross "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c new file mode 100644 index 0000000000..0bd9ba5a11 --- /dev/null +++ b/sound/soc/codecs/es8328.c @@ -0,0 +1,883 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * es8328.c -- ES8328 ALSA SoC Audio driver + * + * Copyright 2014 Sutajio Ko-Usagi PTE LTD + * + * Author: Sean Cross + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "es8328.h" + +static const unsigned int rates_12288[] = { + 8000, 12000, 16000, 24000, 32000, 48000, 96000, +}; + +static const int ratios_12288[] = { + 10, 7, 6, 4, 3, 2, 0, +}; + +static const struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static const unsigned int rates_11289[] = { + 8018, 11025, 22050, 44100, 88200, +}; + +static const int ratios_11289[] = { + 9, 7, 4, 2, 0, +}; + +static const struct snd_pcm_hw_constraint_list constraints_11289 = { + .count = ARRAY_SIZE(rates_11289), + .list = rates_11289, +}; + +/* regulator supplies for sgtl5000, VDDD is an optional external supply */ +enum sgtl5000_regulator_supplies { + DVDD, + AVDD, + PVDD, + HPVDD, + ES8328_SUPPLY_NUM +}; + +/* vddd is optional supply */ +static const char * const supply_names[ES8328_SUPPLY_NUM] = { + "DVDD", + "AVDD", + "PVDD", + "HPVDD", +}; + +#define ES8328_RATES (SNDRV_PCM_RATE_192000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_8000_48000) +#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +struct es8328_priv { + struct regmap *regmap; + struct clk *clk; + int playback_fs; + bool deemph; + int mclkdiv2; + const struct snd_pcm_hw_constraint_list *sysclk_constraints; + const int *mclk_ratios; + bool provider; + struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM]; +}; + +/* + * ES8328 Controls + */ + +static const char * const adcpol_txt[] = {"Normal", "L Invert", "R Invert", + "L + R Invert"}; +static SOC_ENUM_SINGLE_DECL(adcpol, + ES8328_ADCCONTROL6, 6, adcpol_txt); + +static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0); +static const DECLARE_TLV_DB_SCALE(dac_adc_tlv, -9600, 50, 0); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 300, 0); + +static const struct { + int rate; + unsigned int val; +} deemph_settings[] = { + { 0, ES8328_DACCONTROL6_DEEMPH_OFF }, + { 32000, ES8328_DACCONTROL6_DEEMPH_32k }, + { 44100, ES8328_DACCONTROL6_DEEMPH_44_1k }, + { 48000, ES8328_DACCONTROL6_DEEMPH_48k }, +}; + +static int es8328_set_deemph(struct snd_soc_component *component) +{ + struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); + int val, i, best; + + /* + * If we're using deemphasis select the nearest available sample + * rate. + */ + if (es8328->deemph) { + best = 0; + for (i = 1; i < ARRAY_SIZE(deemph_settings); i++) { + if (abs(deemph_settings[i].rate - es8328->playback_fs) < + abs(deemph_settings[best].rate - es8328->playback_fs)) + best = i; + } + + val = deemph_settings[best].val; + } else { + val = ES8328_DACCONTROL6_DEEMPH_OFF; + } + + dev_dbg(component->dev, "Set deemphasis %d\n", val); + + return snd_soc_component_update_bits(component, ES8328_DACCONTROL6, + ES8328_DACCONTROL6_DEEMPH_MASK, val); +} + +static int es8328_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = es8328->deemph; + return 0; +} + +static int es8328_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; + int ret; + + if (deemph > 1) + return -EINVAL; + + if (es8328->deemph == deemph) + return 0; + + ret = es8328_set_deemph(component); + if (ret < 0) + return ret; + + es8328->deemph = deemph; + + return 1; +} + + + +static const struct snd_kcontrol_new es8328_snd_controls[] = { + SOC_DOUBLE_R_TLV("Capture Digital Volume", + ES8328_ADCCONTROL8, ES8328_ADCCONTROL9, + 0, 0xc0, 1, dac_adc_tlv), + SOC_SINGLE("Capture ZC Switch", ES8328_ADCCONTROL7, 6, 1, 0), + + SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, + es8328_get_deemph, es8328_put_deemph), + + SOC_ENUM("Capture Polarity", adcpol), + + SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", + ES8328_DACCONTROL17, 3, 7, 1, bypass_tlv), + SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", + ES8328_DACCONTROL19, 3, 7, 1, bypass_tlv), + SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", + ES8328_DACCONTROL18, 3, 7, 1, bypass_tlv), + SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", + ES8328_DACCONTROL20, 3, 7, 1, bypass_tlv), + + SOC_DOUBLE_R_TLV("PCM Volume", + ES8328_LDACVOL, ES8328_RDACVOL, + 0, ES8328_DACVOL_MAX, 1, dac_adc_tlv), + + SOC_DOUBLE_R_TLV("Output 1 Playback Volume", + ES8328_LOUT1VOL, ES8328_ROUT1VOL, + 0, ES8328_OUT1VOL_MAX, 0, play_tlv), + + SOC_DOUBLE_R_TLV("Output 2 Playback Volume", + ES8328_LOUT2VOL, ES8328_ROUT2VOL, + 0, ES8328_OUT2VOL_MAX, 0, play_tlv), + + SOC_DOUBLE_TLV("Mic PGA Volume", ES8328_ADCCONTROL1, + 4, 0, 8, 0, mic_tlv), +}; + +/* + * DAPM Controls + */ + +static const char * const es8328_line_texts[] = { + "Line 1", "Line 2", "PGA", "Differential"}; + +static const struct soc_enum es8328_lline_enum = + SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 3, + ARRAY_SIZE(es8328_line_texts), + es8328_line_texts); +static const struct snd_kcontrol_new es8328_left_line_controls = + SOC_DAPM_ENUM("Route", es8328_lline_enum); + +static const struct soc_enum es8328_rline_enum = + SOC_ENUM_SINGLE(ES8328_DACCONTROL16, 0, + ARRAY_SIZE(es8328_line_texts), + es8328_line_texts); +static const struct snd_kcontrol_new es8328_right_line_controls = + SOC_DAPM_ENUM("Route", es8328_rline_enum); + +/* Left Mixer */ +static const struct snd_kcontrol_new es8328_left_mixer_controls[] = { + SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL17, 7, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 6, 1, 0), + SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 7, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 6, 1, 0), +}; + +/* Right Mixer */ +static const struct snd_kcontrol_new es8328_right_mixer_controls[] = { + SOC_DAPM_SINGLE("Left Playback Switch", ES8328_DACCONTROL19, 7, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 6, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 7, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0), +}; + +static const char * const es8328_pga_sel[] = { + "Line 1", "Line 2", "Line 3", "Differential"}; + +/* Left PGA Mux */ +static const struct soc_enum es8328_lpga_enum = + SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 6, + ARRAY_SIZE(es8328_pga_sel), + es8328_pga_sel); +static const struct snd_kcontrol_new es8328_left_pga_controls = + SOC_DAPM_ENUM("Route", es8328_lpga_enum); + +/* Right PGA Mux */ +static const struct soc_enum es8328_rpga_enum = + SOC_ENUM_SINGLE(ES8328_ADCCONTROL2, 4, + ARRAY_SIZE(es8328_pga_sel), + es8328_pga_sel); +static const struct snd_kcontrol_new es8328_right_pga_controls = + SOC_DAPM_ENUM("Route", es8328_rpga_enum); + +/* Differential Mux */ +static const char * const es8328_diff_sel[] = {"Line 1", "Line 2"}; +static SOC_ENUM_SINGLE_DECL(diffmux, + ES8328_ADCCONTROL3, 7, es8328_diff_sel); +static const struct snd_kcontrol_new es8328_diffmux_controls = + SOC_DAPM_ENUM("Route", diffmux); + +/* Mono ADC Mux */ +static const char * const es8328_mono_mux[] = {"Stereo", "Mono (Left)", + "Mono (Right)", "Digital Mono"}; +static SOC_ENUM_SINGLE_DECL(monomux, + ES8328_ADCCONTROL3, 3, es8328_mono_mux); +static const struct snd_kcontrol_new es8328_monomux_controls = + SOC_DAPM_ENUM("Route", monomux); + +static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = { + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &es8328_diffmux_controls), + SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, + &es8328_monomux_controls), + SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, + &es8328_monomux_controls), + + SND_SOC_DAPM_MUX("Left PGA Mux", ES8328_ADCPOWER, + ES8328_ADCPOWER_AINL_OFF, 1, + &es8328_left_pga_controls), + SND_SOC_DAPM_MUX("Right PGA Mux", ES8328_ADCPOWER, + ES8328_ADCPOWER_AINR_OFF, 1, + &es8328_right_pga_controls), + + SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, + &es8328_left_line_controls), + SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, + &es8328_right_line_controls), + + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8328_ADCPOWER, + ES8328_ADCPOWER_ADCR_OFF, 1), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8328_ADCPOWER, + ES8328_ADCPOWER_ADCL_OFF, 1), + + SND_SOC_DAPM_SUPPLY("Mic Bias", ES8328_ADCPOWER, + ES8328_ADCPOWER_MIC_BIAS_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias Gen", ES8328_ADCPOWER, + ES8328_ADCPOWER_ADC_BIAS_GEN_OFF, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC STM", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_DACSTM_RESET, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC STM", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_ADCSTM_RESET, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC DIG", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_DACDIG_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC DIG", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_ADCDIG_OFF, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC DLL", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_DACDLL_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC DLL", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_ADCDLL_OFF, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("ADC Vref", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_ADCVREF_OFF, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Vref", ES8328_CHIPPOWER, + ES8328_CHIPPOWER_DACVREF_OFF, 1, NULL, 0), + + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8328_DACPOWER, + ES8328_DACPOWER_RDAC_OFF, 1), + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8328_DACPOWER, + ES8328_DACPOWER_LDAC_OFF, 1), + + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + &es8328_left_mixer_controls[0], + ARRAY_SIZE(es8328_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + &es8328_right_mixer_controls[0], + ARRAY_SIZE(es8328_right_mixer_controls)), + + SND_SOC_DAPM_PGA("Right Out 2", ES8328_DACPOWER, + ES8328_DACPOWER_ROUT2_ON, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 2", ES8328_DACPOWER, + ES8328_DACPOWER_LOUT2_ON, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 1", ES8328_DACPOWER, + ES8328_DACPOWER_ROUT1_ON, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 1", ES8328_DACPOWER, + ES8328_DACPOWER_LOUT1_ON, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + + SND_SOC_DAPM_INPUT("LINPUT1"), + SND_SOC_DAPM_INPUT("LINPUT2"), + SND_SOC_DAPM_INPUT("RINPUT1"), + SND_SOC_DAPM_INPUT("RINPUT2"), +}; + +static const struct snd_soc_dapm_route es8328_dapm_routes[] = { + + { "Left Line Mux", "Line 1", "LINPUT1" }, + { "Left Line Mux", "Line 2", "LINPUT2" }, + { "Left Line Mux", "PGA", "Left PGA Mux" }, + { "Left Line Mux", "Differential", "Differential Mux" }, + + { "Right Line Mux", "Line 1", "RINPUT1" }, + { "Right Line Mux", "Line 2", "RINPUT2" }, + { "Right Line Mux", "PGA", "Right PGA Mux" }, + { "Right Line Mux", "Differential", "Differential Mux" }, + + { "Left PGA Mux", "Line 1", "LINPUT1" }, + { "Left PGA Mux", "Line 2", "LINPUT2" }, + { "Left PGA Mux", "Differential", "Differential Mux" }, + + { "Right PGA Mux", "Line 1", "RINPUT1" }, + { "Right PGA Mux", "Line 2", "RINPUT2" }, + { "Right PGA Mux", "Differential", "Differential Mux" }, + + { "Differential Mux", "Line 1", "LINPUT1" }, + { "Differential Mux", "Line 1", "RINPUT1" }, + { "Differential Mux", "Line 2", "LINPUT2" }, + { "Differential Mux", "Line 2", "RINPUT2" }, + + { "Left ADC Mux", "Stereo", "Left PGA Mux" }, + { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" }, + { "Left ADC Mux", "Digital Mono", "Left PGA Mux" }, + + { "Right ADC Mux", "Stereo", "Right PGA Mux" }, + { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" }, + { "Right ADC Mux", "Digital Mono", "Right PGA Mux" }, + + { "Left ADC", NULL, "Left ADC Mux" }, + { "Right ADC", NULL, "Right ADC Mux" }, + + { "ADC DIG", NULL, "ADC STM" }, + { "ADC DIG", NULL, "ADC Vref" }, + { "ADC DIG", NULL, "ADC DLL" }, + + { "Left ADC", NULL, "ADC DIG" }, + { "Right ADC", NULL, "ADC DIG" }, + + { "Mic Bias", NULL, "Mic Bias Gen" }, + + { "Left Line Mux", "Line 1", "LINPUT1" }, + { "Left Line Mux", "Line 2", "LINPUT2" }, + { "Left Line Mux", "PGA", "Left PGA Mux" }, + { "Left Line Mux", "Differential", "Differential Mux" }, + + { "Right Line Mux", "Line 1", "RINPUT1" }, + { "Right Line Mux", "Line 2", "RINPUT2" }, + { "Right Line Mux", "PGA", "Right PGA Mux" }, + { "Right Line Mux", "Differential", "Differential Mux" }, + + { "Left Out 1", NULL, "Left DAC" }, + { "Right Out 1", NULL, "Right DAC" }, + { "Left Out 2", NULL, "Left DAC" }, + { "Right Out 2", NULL, "Right DAC" }, + + { "Left Mixer", "Playback Switch", "Left DAC" }, + { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, + { "Left Mixer", "Right Playback Switch", "Right DAC" }, + { "Left Mixer", "Right Bypass Switch", "Right Line Mux" }, + + { "Right Mixer", "Left Playback Switch", "Left DAC" }, + { "Right Mixer", "Left Bypass Switch", "Left Line Mux" }, + { "Right Mixer", "Playback Switch", "Right DAC" }, + { "Right Mixer", "Right Bypass Switch", "Right Line Mux" }, + + { "DAC DIG", NULL, "DAC STM" }, + { "DAC DIG", NULL, "DAC Vref" }, + { "DAC DIG", NULL, "DAC DLL" }, + + { "Left DAC", NULL, "DAC DIG" }, + { "Right DAC", NULL, "DAC DIG" }, + + { "Left Out 1", NULL, "Left Mixer" }, + { "LOUT1", NULL, "Left Out 1" }, + { "Right Out 1", NULL, "Right Mixer" }, + { "ROUT1", NULL, "Right Out 1" }, + + { "Left Out 2", NULL, "Left Mixer" }, + { "LOUT2", NULL, "Left Out 2" }, + { "Right Out 2", NULL, "Right Mixer" }, + { "ROUT2", NULL, "Right Out 2" }, +}; + +static int es8328_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + return snd_soc_component_update_bits(dai->component, ES8328_DACCONTROL3, + ES8328_DACCONTROL3_DACMUTE, + mute ? ES8328_DACCONTROL3_DACMUTE : 0); +} + +static int es8328_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); + + if (es8328->provider && es8328->sysclk_constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + es8328->sysclk_constraints); + + return 0; +} + +static int es8328_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); + int i; + int reg; + int wl; + int ratio; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = ES8328_DACCONTROL2; + else + reg = ES8328_ADCCONTROL5; + + if (es8328->provider) { + if (!es8328->sysclk_constraints) { + dev_err(component->dev, "No MCLK configured\n"); + return -EINVAL; + } + + for (i = 0; i < es8328->sysclk_constraints->count; i++) + if (es8328->sysclk_constraints->list[i] == + params_rate(params)) + break; + + if (i == es8328->sysclk_constraints->count) { + dev_err(component->dev, + "LRCLK %d unsupported with current clock\n", + params_rate(params)); + return -EINVAL; + } + ratio = es8328->mclk_ratios[i]; + } else { + ratio = 0; + es8328->mclkdiv2 = 0; + } + + snd_soc_component_update_bits(component, ES8328_MASTERMODE, + ES8328_MASTERMODE_MCLKDIV2, + es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0); + + switch (params_width(params)) { + case 16: + wl = 3; + break; + case 18: + wl = 2; + break; + case 20: + wl = 1; + break; + case 24: + wl = 0; + break; + case 32: + wl = 4; + break; + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_component_update_bits(component, ES8328_DACCONTROL1, + ES8328_DACCONTROL1_DACWL_MASK, + wl << ES8328_DACCONTROL1_DACWL_SHIFT); + + es8328->playback_fs = params_rate(params); + es8328_set_deemph(component); + } else + snd_soc_component_update_bits(component, ES8328_ADCCONTROL4, + ES8328_ADCCONTROL4_ADCWL_MASK, + wl << ES8328_ADCCONTROL4_ADCWL_SHIFT); + + return snd_soc_component_update_bits(component, reg, ES8328_RATEMASK, ratio); +} + +static int es8328_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); + int mclkdiv2 = 0; + + switch (freq) { + case 0: + es8328->sysclk_constraints = NULL; + es8328->mclk_ratios = NULL; + break; + case 22579200: + mclkdiv2 = 1; + fallthrough; + case 11289600: + es8328->sysclk_constraints = &constraints_11289; + es8328->mclk_ratios = ratios_11289; + break; + case 24576000: + mclkdiv2 = 1; + fallthrough; + case 12288000: + es8328->sysclk_constraints = &constraints_12288; + es8328->mclk_ratios = ratios_12288; + break; + default: + return -EINVAL; + } + + es8328->mclkdiv2 = mclkdiv2; + return 0; +} + +static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component); + u8 dac_mode = 0; + u8 adc_mode = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + /* Master serial port mode, with BCLK generated automatically */ + snd_soc_component_update_bits(component, ES8328_MASTERMODE, + ES8328_MASTERMODE_MSC, + ES8328_MASTERMODE_MSC); + es8328->provider = true; + break; + case SND_SOC_DAIFMT_CBC_CFC: + /* Slave serial port mode */ + snd_soc_component_update_bits(component, ES8328_MASTERMODE, + ES8328_MASTERMODE_MSC, 0); + es8328->provider = false; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S; + adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST; + adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST; + break; + case SND_SOC_DAIFMT_LEFT_J: + dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST; + adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) + return -EINVAL; + + snd_soc_component_update_bits(component, ES8328_DACCONTROL1, + ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode); + snd_soc_component_update_bits(component, ES8328_ADCCONTROL4, + ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode); + + return 0; +} + +static int es8328_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VREF, VMID=2x50k, digital enabled */ + snd_soc_component_write(component, ES8328_CHIPPOWER, 0); + snd_soc_component_update_bits(component, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + ES8328_CONTROL1_VMIDSEL_50k | + ES8328_CONTROL1_ENREF); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + snd_soc_component_update_bits(component, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + ES8328_CONTROL1_VMIDSEL_5k | + ES8328_CONTROL1_ENREF); + + /* Charge caps */ + msleep(100); + } + + snd_soc_component_write(component, ES8328_CONTROL2, + ES8328_CONTROL2_OVERCURRENT_ON | + ES8328_CONTROL2_THERMAL_SHUTDOWN_ON); + + /* VREF, VMID=2*500k, digital stopped */ + snd_soc_component_update_bits(component, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + ES8328_CONTROL1_VMIDSEL_500k | + ES8328_CONTROL1_ENREF); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, ES8328_CONTROL1, + ES8328_CONTROL1_VMIDSEL_MASK | + ES8328_CONTROL1_ENREF, + 0); + break; + } + return 0; +} + +static const struct snd_soc_dai_ops es8328_dai_ops = { + .startup = es8328_startup, + .hw_params = es8328_hw_params, + .mute_stream = es8328_mute, + .set_sysclk = es8328_set_sysclk, + .set_fmt = es8328_set_dai_fmt, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver es8328_dai = { + .name = "es8328-hifi-analog", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ES8328_RATES, + .formats = ES8328_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = ES8328_RATES, + .formats = ES8328_FORMATS, + }, + .ops = &es8328_dai_ops, + .symmetric_rate = 1, +}; + +static int es8328_suspend(struct snd_soc_component *component) +{ + struct es8328_priv *es8328; + int ret; + + es8328 = snd_soc_component_get_drvdata(component); + + clk_disable_unprepare(es8328->clk); + + ret = regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), + es8328->supplies); + if (ret) { + dev_err(component->dev, "unable to disable regulators\n"); + return ret; + } + return 0; +} + +static int es8328_resume(struct snd_soc_component *component) +{ + struct regmap *regmap = dev_get_regmap(component->dev, NULL); + struct es8328_priv *es8328; + int ret; + + es8328 = snd_soc_component_get_drvdata(component); + + ret = clk_prepare_enable(es8328->clk); + if (ret) { + dev_err(component->dev, "unable to enable clock\n"); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies), + es8328->supplies); + if (ret) { + dev_err(component->dev, "unable to enable regulators\n"); + return ret; + } + + regcache_mark_dirty(regmap); + ret = regcache_sync(regmap); + if (ret) { + dev_err(component->dev, "unable to sync regcache\n"); + return ret; + } + + return 0; +} + +static int es8328_component_probe(struct snd_soc_component *component) +{ + struct es8328_priv *es8328; + int ret; + + es8328 = snd_soc_component_get_drvdata(component); + + ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies), + es8328->supplies); + if (ret) { + dev_err(component->dev, "unable to enable regulators\n"); + return ret; + } + + /* Setup clocks */ + es8328->clk = devm_clk_get(component->dev, NULL); + if (IS_ERR(es8328->clk)) { + dev_err(component->dev, "codec clock missing or invalid\n"); + ret = PTR_ERR(es8328->clk); + goto clk_fail; + } + + ret = clk_prepare_enable(es8328->clk); + if (ret) { + dev_err(component->dev, "unable to prepare codec clk\n"); + goto clk_fail; + } + + return 0; + +clk_fail: + regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), + es8328->supplies); + return ret; +} + +static void es8328_remove(struct snd_soc_component *component) +{ + struct es8328_priv *es8328; + + es8328 = snd_soc_component_get_drvdata(component); + + clk_disable_unprepare(es8328->clk); + + regulator_bulk_disable(ARRAY_SIZE(es8328->supplies), + es8328->supplies); +} + +const struct regmap_config es8328_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ES8328_REG_MAX, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; +EXPORT_SYMBOL_GPL(es8328_regmap_config); + +static const struct snd_soc_component_driver es8328_component_driver = { + .probe = es8328_component_probe, + .remove = es8328_remove, + .suspend = es8328_suspend, + .resume = es8328_resume, + .set_bias_level = es8328_set_bias_level, + .controls = es8328_snd_controls, + .num_controls = ARRAY_SIZE(es8328_snd_controls), + .dapm_widgets = es8328_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets), + .dapm_routes = es8328_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +int es8328_probe(struct device *dev, struct regmap *regmap) +{ + struct es8328_priv *es8328; + int ret; + int i; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + es8328 = devm_kzalloc(dev, sizeof(*es8328), GFP_KERNEL); + if (es8328 == NULL) + return -ENOMEM; + + es8328->regmap = regmap; + + for (i = 0; i < ARRAY_SIZE(es8328->supplies); i++) + es8328->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8328->supplies), + es8328->supplies); + if (ret) { + dev_err(dev, "unable to get regulators\n"); + return ret; + } + + dev_set_drvdata(dev, es8328); + + return devm_snd_soc_register_component(dev, + &es8328_component_driver, &es8328_dai, 1); +} +EXPORT_SYMBOL_GPL(es8328_probe); + +MODULE_DESCRIPTION("ASoC ES8328 driver"); +MODULE_AUTHOR("Sean Cross "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h new file mode 100644 index 0000000000..9109f6b5b0 --- /dev/null +++ b/sound/soc/codecs/es8328.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * es8328.h -- ES8328 ALSA SoC Audio driver + */ + +#ifndef _ES8328_H +#define _ES8328_H + +#include + +struct device; + +extern const struct regmap_config es8328_regmap_config; +int es8328_probe(struct device *dev, struct regmap *regmap); + +#define ES8328_DACLVOL 46 +#define ES8328_DACRVOL 47 +#define ES8328_DACCTL 28 +#define ES8328_RATEMASK (0x1f << 0) + +#define ES8328_CONTROL1 0x00 +#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0) +#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0) +#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0) +#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0) +#define ES8328_CONTROL1_VMIDSEL_MASK (3 << 0) +#define ES8328_CONTROL1_ENREF (1 << 2) +#define ES8328_CONTROL1_SEQEN (1 << 3) +#define ES8328_CONTROL1_SAMEFS (1 << 4) +#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5) +#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5) +#define ES8328_CONTROL1_LRCM (1 << 6) +#define ES8328_CONTROL1_SCP_RESET (1 << 7) + +#define ES8328_CONTROL2 0x01 +#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0) +#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1) +#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2) +#define ES8328_CONTROL2_ANALOG_OFF (1 << 3) +#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4) +#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5) +#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6) +#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7) + +#define ES8328_CHIPPOWER 0x02 +#define ES8328_CHIPPOWER_DACVREF_OFF 0 +#define ES8328_CHIPPOWER_ADCVREF_OFF 1 +#define ES8328_CHIPPOWER_DACDLL_OFF 2 +#define ES8328_CHIPPOWER_ADCDLL_OFF 3 +#define ES8328_CHIPPOWER_DACSTM_RESET 4 +#define ES8328_CHIPPOWER_ADCSTM_RESET 5 +#define ES8328_CHIPPOWER_DACDIG_OFF 6 +#define ES8328_CHIPPOWER_ADCDIG_OFF 7 + +#define ES8328_ADCPOWER 0x03 +#define ES8328_ADCPOWER_INT1_LOWPOWER 0 +#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER 1 +#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF 2 +#define ES8328_ADCPOWER_MIC_BIAS_OFF 3 +#define ES8328_ADCPOWER_ADCR_OFF 4 +#define ES8328_ADCPOWER_ADCL_OFF 5 +#define ES8328_ADCPOWER_AINR_OFF 6 +#define ES8328_ADCPOWER_AINL_OFF 7 + +#define ES8328_DACPOWER 0x04 +#define ES8328_DACPOWER_OUT3_ON 0 +#define ES8328_DACPOWER_MONO_ON 1 +#define ES8328_DACPOWER_ROUT2_ON 2 +#define ES8328_DACPOWER_LOUT2_ON 3 +#define ES8328_DACPOWER_ROUT1_ON 4 +#define ES8328_DACPOWER_LOUT1_ON 5 +#define ES8328_DACPOWER_RDAC_OFF 6 +#define ES8328_DACPOWER_LDAC_OFF 7 + +#define ES8328_CHIPLOPOW1 0x05 +#define ES8328_CHIPLOPOW2 0x06 +#define ES8328_ANAVOLMANAG 0x07 + +#define ES8328_MASTERMODE 0x08 +#define ES8328_MASTERMODE_BCLKDIV (0 << 0) +#define ES8328_MASTERMODE_BCLK_INV (1 << 5) +#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6) +#define ES8328_MASTERMODE_MSC (1 << 7) + +#define ES8328_ADCCONTROL1 0x09 +#define ES8328_ADCCONTROL2 0x0a +#define ES8328_ADCCONTROL3 0x0b + +#define ES8328_ADCCONTROL4 0x0c +#define ES8328_ADCCONTROL4_ADCFORMAT_MASK (3 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0) +#define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0) +#define ES8328_ADCCONTROL4_ADCWL_SHIFT 2 +#define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2) +#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5) +#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5) +#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5) +#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5) + +#define ES8328_ADCCONTROL5 0x0d +#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0) + +#define ES8328_ADCCONTROL6 0x0e + +#define ES8328_ADCCONTROL7 0x0f +#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2) +#define ES8328_ADCCONTROL7_ADC_LER (1 << 3) +#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4) +#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5) +#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6) +#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6) +#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6) +#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6) + +#define ES8328_ADCCONTROL8 0x10 +#define ES8328_ADCCONTROL9 0x11 +#define ES8328_ADCCONTROL10 0x12 +#define ES8328_ADCCONTROL11 0x13 +#define ES8328_ADCCONTROL12 0x14 +#define ES8328_ADCCONTROL13 0x15 +#define ES8328_ADCCONTROL14 0x16 + +#define ES8328_DACCONTROL1 0x17 +#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1) +#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1) +#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1) +#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1) +#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1) +#define ES8328_DACCONTROL1_DACWL_SHIFT 3 +#define ES8328_DACCONTROL1_DACWL_MASK (7 << 3) +#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6) +#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6) +#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6) +#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (1 << 6) +#define ES8328_DACCONTROL1_LRSWAP (1 << 7) + +#define ES8328_DACCONTROL2 0x18 +#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0) +#define ES8328_DACCONTROL2_DOUBLESPEED (1 << 5) + +#define ES8328_DACCONTROL3 0x19 +#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2) +#define ES8328_DACCONTROL3_DACMUTE (1 << 2) +#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3) +#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4) +#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5) +#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6) + +#define ES8328_LDACVOL 0x1a +#define ES8328_LDACVOL_MASK (0 << 0) +#define ES8328_LDACVOL_MAX (0xc0) + +#define ES8328_RDACVOL 0x1b +#define ES8328_RDACVOL_MASK (0 << 0) +#define ES8328_RDACVOL_MAX (0xc0) + +#define ES8328_DACVOL_MAX (0xc0) + +#define ES8328_DACCONTROL4 0x1a +#define ES8328_DACCONTROL5 0x1b + +#define ES8328_DACCONTROL6 0x1c +#define ES8328_DACCONTROL6_CLICKFREE (1 << 3) +#define ES8328_DACCONTROL6_DAC_INVR (1 << 4) +#define ES8328_DACCONTROL6_DAC_INVL (1 << 5) +#define ES8328_DACCONTROL6_DEEMPH_MASK (3 << 6) +#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6) +#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6) +#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6) +#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6) + +#define ES8328_DACCONTROL7 0x1d +#define ES8328_DACCONTROL7_VPP_SCALE_3p5 (0 << 0) +#define ES8328_DACCONTROL7_VPP_SCALE_4p0 (1 << 0) +#define ES8328_DACCONTROL7_VPP_SCALE_3p0 (2 << 0) +#define ES8328_DACCONTROL7_VPP_SCALE_2p5 (3 << 0) +#define ES8328_DACCONTROL7_SHELVING_STRENGTH (1 << 2) /* In eights */ +#define ES8328_DACCONTROL7_MONO (1 << 5) +#define ES8328_DACCONTROL7_ZEROR (1 << 6) +#define ES8328_DACCONTROL7_ZEROL (1 << 7) + +/* Shelving filter */ +#define ES8328_DACCONTROL8 0x1e +#define ES8328_DACCONTROL9 0x1f +#define ES8328_DACCONTROL10 0x20 +#define ES8328_DACCONTROL11 0x21 +#define ES8328_DACCONTROL12 0x22 +#define ES8328_DACCONTROL13 0x23 +#define ES8328_DACCONTROL14 0x24 +#define ES8328_DACCONTROL15 0x25 + +#define ES8328_DACCONTROL16 0x26 +#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0) +#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0) +#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0) +#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0) +#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3) +#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3) +#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3) +#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3) + +#define ES8328_DACCONTROL17 0x27 +#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3) +#define ES8328_DACCONTROL17_LI2LO (1 << 6) +#define ES8328_DACCONTROL17_LD2LO (1 << 7) + +#define ES8328_DACCONTROL18 0x28 +#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3) +#define ES8328_DACCONTROL18_RI2LO (1 << 6) +#define ES8328_DACCONTROL18_RD2LO (1 << 7) + +#define ES8328_DACCONTROL19 0x29 +#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3) +#define ES8328_DACCONTROL19_LI2RO (1 << 6) +#define ES8328_DACCONTROL19_LD2RO (1 << 7) + +#define ES8328_DACCONTROL20 0x2a +#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3) +#define ES8328_DACCONTROL20_RI2RO (1 << 6) +#define ES8328_DACCONTROL20_RD2RO (1 << 7) + +#define ES8328_DACCONTROL21 0x2b +#define ES8328_DACCONTROL21_LI2MOVOL (7 << 3) +#define ES8328_DACCONTROL21_LI2MO (1 << 6) +#define ES8328_DACCONTROL21_LD2MO (1 << 7) + +#define ES8328_DACCONTROL22 0x2c +#define ES8328_DACCONTROL22_RI2MOVOL (7 << 3) +#define ES8328_DACCONTROL22_RI2MO (1 << 6) +#define ES8328_DACCONTROL22_RD2MO (1 << 7) + +#define ES8328_DACCONTROL23 0x2d +#define ES8328_DACCONTROL23_MOUTINV (1 << 1) +#define ES8328_DACCONTROL23_HPSWPOL (1 << 2) +#define ES8328_DACCONTROL23_HPSWEN (1 << 3) +#define ES8328_DACCONTROL23_VROI_1p5k (0 << 4) +#define ES8328_DACCONTROL23_VROI_40k (1 << 4) +#define ES8328_DACCONTROL23_OUT3_VREF (0 << 5) +#define ES8328_DACCONTROL23_OUT3_ROUT1 (1 << 5) +#define ES8328_DACCONTROL23_OUT3_MONOOUT (2 << 5) +#define ES8328_DACCONTROL23_OUT3_RIGHT_MIXER (3 << 5) +#define ES8328_DACCONTROL23_ROUT2INV (1 << 7) + +/* LOUT1 Amplifier */ +#define ES8328_LOUT1VOL 0x2e +#define ES8328_LOUT1VOL_MASK (0 << 5) +#define ES8328_LOUT1VOL_MAX (0x24) + +/* ROUT1 Amplifier */ +#define ES8328_ROUT1VOL 0x2f +#define ES8328_ROUT1VOL_MASK (0 << 5) +#define ES8328_ROUT1VOL_MAX (0x24) + +#define ES8328_OUT1VOL_MAX (0x24) + +/* LOUT2 Amplifier */ +#define ES8328_LOUT2VOL 0x30 +#define ES8328_LOUT2VOL_MASK (0 << 5) +#define ES8328_LOUT2VOL_MAX (0x24) + +/* ROUT2 Amplifier */ +#define ES8328_ROUT2VOL 0x31 +#define ES8328_ROUT2VOL_MASK (0 << 5) +#define ES8328_ROUT2VOL_MAX (0x24) + +#define ES8328_OUT2VOL_MAX (0x24) + +/* Mono Out Amplifier */ +#define ES8328_MONOOUTVOL 0x32 +#define ES8328_MONOOUTVOL_MASK (0 << 5) +#define ES8328_MONOOUTVOL_MAX (0x24) + +#define ES8328_DACCONTROL29 0x33 +#define ES8328_DACCONTROL30 0x34 + +#define ES8328_SYSCLK 0 + +#define ES8328_REG_MAX 0x35 + +#define ES8328_1536FS 1536 +#define ES8328_1024FS 1024 +#define ES8328_768FS 768 +#define ES8328_512FS 512 +#define ES8328_384FS 384 +#define ES8328_256FS 256 +#define ES8328_128FS 128 + +#endif diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c new file mode 100644 index 0000000000..c6b1e77ffc --- /dev/null +++ b/sound/soc/codecs/gtm601.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This is a simple driver for the GTM601 Voice PCM interface + * + * Copyright (C) 2015 Goldelico GmbH + * + * Author: Marek Belisko + * + * Based on wm8727.c driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct snd_soc_dapm_widget gtm601_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("AOUT"), + SND_SOC_DAPM_INPUT("AIN"), +}; + +static const struct snd_soc_dapm_route gtm601_dapm_routes[] = { + { "AOUT", NULL, "Playback" }, + { "Capture", NULL, "AIN" }, +}; + +static struct snd_soc_dai_driver gtm601_dai = { + .name = "gtm601", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static struct snd_soc_dai_driver bm818_dai = { + .name = "bm818", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_gtm601 = { + .dapm_widgets = gtm601_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(gtm601_dapm_widgets), + .dapm_routes = gtm601_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(gtm601_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int gtm601_platform_probe(struct platform_device *pdev) +{ + const struct snd_soc_dai_driver *dai_driver; + + dai_driver = of_device_get_match_data(&pdev->dev); + + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_gtm601, + (struct snd_soc_dai_driver *)dai_driver, 1); +} + +static const struct of_device_id gtm601_codec_of_match[] __maybe_unused = { + { .compatible = "option,gtm601", .data = (void *)>m601_dai }, + { .compatible = "broadmobi,bm818", .data = (void *)&bm818_dai }, + {}, +}; +MODULE_DEVICE_TABLE(of, gtm601_codec_of_match); + +static struct platform_driver gtm601_codec_driver = { + .driver = { + .name = "gtm601", + .of_match_table = of_match_ptr(gtm601_codec_of_match), + }, + .probe = gtm601_platform_probe, +}; + +module_platform_driver(gtm601_codec_driver); + +MODULE_DESCRIPTION("ASoC gtm601 driver"); +MODULE_AUTHOR("Marek Belisko "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gtm601"); diff --git a/sound/soc/codecs/hda-dai.c b/sound/soc/codecs/hda-dai.c new file mode 100644 index 0000000000..5371ff0862 --- /dev/null +++ b/sound/soc/codecs/hda-dai.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include +#include +#include "hda.h" + +static int hda_codec_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct hda_pcm_stream *stream_info; + struct hda_codec *codec; + struct hda_pcm *pcm; + int ret; + + codec = dev_to_hda_codec(dai->dev); + stream_info = snd_soc_dai_get_dma_data(dai, substream); + pcm = container_of(stream_info, struct hda_pcm, stream[substream->stream]); + + dev_dbg(dai->dev, "open stream codec: %08x, info: %p, pcm: %p %s substream: %p\n", + codec->core.vendor_id, stream_info, pcm, pcm->name, substream); + + snd_hda_codec_pcm_get(pcm); + + ret = stream_info->ops.open(stream_info, codec, substream); + if (ret < 0) { + dev_err(dai->dev, "codec open failed: %d\n", ret); + snd_hda_codec_pcm_put(pcm); + return ret; + } + + return 0; +} + +static void hda_codec_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct hda_pcm_stream *stream_info; + struct hda_codec *codec; + struct hda_pcm *pcm; + int ret; + + codec = dev_to_hda_codec(dai->dev); + stream_info = snd_soc_dai_get_dma_data(dai, substream); + pcm = container_of(stream_info, struct hda_pcm, stream[substream->stream]); + + dev_dbg(dai->dev, "close stream codec: %08x, info: %p, pcm: %p %s substream: %p\n", + codec->core.vendor_id, stream_info, pcm, pcm->name, substream); + + ret = stream_info->ops.close(stream_info, codec, substream); + if (ret < 0) + dev_err(dai->dev, "codec close failed: %d\n", ret); + + snd_hda_codec_pcm_put(pcm); +} + +static int hda_codec_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct hda_pcm_stream *stream_info; + struct hda_codec *codec; + + codec = dev_to_hda_codec(dai->dev); + stream_info = snd_soc_dai_get_dma_data(dai, substream); + + snd_hda_codec_cleanup(codec, stream_info, substream); + + return 0; +} + +static int hda_codec_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct hda_pcm_stream *stream_info; + struct hdac_stream *stream; + struct hda_codec *codec; + unsigned int format; + int ret; + + codec = dev_to_hda_codec(dai->dev); + stream = substream->runtime->private_data; + stream_info = snd_soc_dai_get_dma_data(dai, substream); + format = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, + runtime->sample_bits, 0); + + ret = snd_hda_codec_prepare(codec, stream_info, stream->stream_tag, format, substream); + if (ret < 0) { + dev_err(dai->dev, "codec prepare failed: %d\n", ret); + return ret; + } + + return 0; +} + +const struct snd_soc_dai_ops snd_soc_hda_codec_dai_ops = { + .startup = hda_codec_dai_startup, + .shutdown = hda_codec_dai_shutdown, + .hw_free = hda_codec_dai_hw_free, + .prepare = hda_codec_dai_prepare, +}; +EXPORT_SYMBOL_GPL(snd_soc_hda_codec_dai_ops); diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c new file mode 100644 index 0000000000..d57b043d6b --- /dev/null +++ b/sound/soc/codecs/hda.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include +#include +#include +#include +#include +#include +#include "hda.h" + +static int hda_codec_create_dais(struct hda_codec *codec, int pcm_count, + struct snd_soc_dai_driver **drivers) +{ + struct device *dev = &codec->core.dev; + struct snd_soc_dai_driver *drvs; + struct hda_pcm *pcm; + int i; + + drvs = devm_kcalloc(dev, pcm_count, sizeof(*drvs), GFP_KERNEL); + if (!drvs) + return -ENOMEM; + + pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list); + + for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) { + struct snd_soc_pcm_stream *stream; + int dir; + + dev_info(dev, "creating for %s %d\n", pcm->name, i); + drvs[i].id = i; + drvs[i].name = pcm->name; + drvs[i].ops = &snd_soc_hda_codec_dai_ops; + + dir = SNDRV_PCM_STREAM_PLAYBACK; + stream = &drvs[i].playback; + if (!pcm->stream[dir].substreams) { + dev_info(dev, "skipping playback dai for %s\n", pcm->name); + goto capture_dais; + } + + stream->stream_name = + devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name, + snd_pcm_direction_name(dir)); + if (!stream->stream_name) + return -ENOMEM; + stream->channels_min = pcm->stream[dir].channels_min; + stream->channels_max = pcm->stream[dir].channels_max; + stream->rates = pcm->stream[dir].rates; + stream->formats = pcm->stream[dir].formats; + stream->sig_bits = pcm->stream[dir].maxbps; + +capture_dais: + dir = SNDRV_PCM_STREAM_CAPTURE; + stream = &drvs[i].capture; + if (!pcm->stream[dir].substreams) { + dev_info(dev, "skipping capture dai for %s\n", pcm->name); + continue; + } + + stream->stream_name = + devm_kasprintf(dev, GFP_KERNEL, "%s %s", pcm->name, + snd_pcm_direction_name(dir)); + if (!stream->stream_name) + return -ENOMEM; + stream->channels_min = pcm->stream[dir].channels_min; + stream->channels_max = pcm->stream[dir].channels_max; + stream->rates = pcm->stream[dir].rates; + stream->formats = pcm->stream[dir].formats; + stream->sig_bits = pcm->stream[dir].maxbps; + } + + *drivers = drvs; + return 0; +} + +static int hda_codec_register_dais(struct hda_codec *codec, struct snd_soc_component *component) +{ + struct snd_soc_dai_driver *drvs = NULL; + struct snd_soc_dapm_context *dapm; + struct hda_pcm *pcm; + int ret, pcm_count = 0; + + if (list_empty(&codec->pcm_list_head)) + return -EINVAL; + list_for_each_entry(pcm, &codec->pcm_list_head, list) + pcm_count++; + + ret = hda_codec_create_dais(codec, pcm_count, &drvs); + if (ret < 0) + return ret; + + dapm = snd_soc_component_get_dapm(component); + + list_for_each_entry(pcm, &codec->pcm_list_head, list) { + struct snd_soc_dai *dai; + + dai = snd_soc_register_dai(component, drvs, false); + if (!dai) { + dev_err(component->dev, "register dai for %s failed\n", pcm->name); + return -EINVAL; + } + + ret = snd_soc_dapm_new_dai_widgets(dapm, dai); + if (ret < 0) { + dev_err(component->dev, "create widgets failed: %d\n", ret); + snd_soc_unregister_dai(dai); + return ret; + } + + snd_soc_dai_init_dma_data(dai, &pcm->stream[0], &pcm->stream[1]); + drvs++; + } + + return 0; +} + +static void hda_codec_unregister_dais(struct hda_codec *codec, + struct snd_soc_component *component) +{ + struct snd_soc_dai *dai, *save; + struct hda_pcm *pcm; + + for_each_component_dais_safe(component, dai, save) { + int stream; + + list_for_each_entry(pcm, &codec->pcm_list_head, list) { + if (strcmp(dai->driver->name, pcm->name)) + continue; + + for_each_pcm_streams(stream) + snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream)); + + snd_soc_unregister_dai(dai); + break; + } + } +} + +int hda_codec_probe_complete(struct hda_codec *codec) +{ + struct hdac_device *hdev = &codec->core; + struct hdac_bus *bus = hdev->bus; + int ret; + + ret = snd_hda_codec_build_controls(codec); + if (ret < 0) { + dev_err(&hdev->dev, "unable to create controls %d\n", ret); + goto out; + } + + /* Bus suspended codecs as it does not manage their pm */ + pm_runtime_set_active(&hdev->dev); + /* rpm was forbidden in snd_hda_codec_device_new() */ + snd_hda_codec_set_power_save(codec, 2000); + snd_hda_codec_register(codec); +out: + /* Complement pm_runtime_get_sync(bus) in probe */ + pm_runtime_mark_last_busy(bus->dev); + pm_runtime_put_autosuspend(bus->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(hda_codec_probe_complete); + +/* Expects codec with usage_count=1 and status=suspended */ +static int hda_codec_probe(struct snd_soc_component *component) +{ + struct hda_codec *codec = dev_to_hda_codec(component->dev); + struct hdac_device *hdev = &codec->core; + struct hdac_bus *bus = hdev->bus; + struct hdac_ext_link *hlink; + hda_codec_patch_t patch; + int ret; + +#ifdef CONFIG_PM + WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 || + !pm_runtime_status_suspended(&hdev->dev)); +#endif + + hlink = snd_hdac_ext_bus_get_hlink_by_addr(bus, hdev->addr); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return -EIO; + } + + pm_runtime_get_sync(bus->dev); + if (hda_codec_is_display(codec)) + snd_hdac_display_power(bus, hdev->addr, true); + snd_hdac_ext_bus_link_get(bus, hlink); + + ret = snd_hda_codec_device_new(codec->bus, component->card->snd_card, hdev->addr, codec, + false); + if (ret < 0) { + dev_err(&hdev->dev, "create hda codec failed: %d\n", ret); + goto device_new_err; + } + + ret = snd_hda_codec_set_name(codec, codec->preset->name); + if (ret < 0) { + dev_err(&hdev->dev, "name failed %s\n", codec->preset->name); + goto err; + } + + ret = snd_hdac_regmap_init(&codec->core); + if (ret < 0) { + dev_err(&hdev->dev, "regmap init failed\n"); + goto err; + } + + patch = (hda_codec_patch_t)codec->preset->driver_data; + if (!patch) { + dev_err(&hdev->dev, "no patch specified\n"); + ret = -EINVAL; + goto err; + } + + ret = patch(codec); + if (ret < 0) { + dev_err(&hdev->dev, "patch failed %d\n", ret); + goto err; + } + + ret = snd_hda_codec_parse_pcms(codec); + if (ret < 0) { + dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret); + goto parse_pcms_err; + } + + ret = hda_codec_register_dais(codec, component); + if (ret < 0) { + dev_err(&hdev->dev, "update dais failed: %d\n", ret); + goto parse_pcms_err; + } + + if (!hda_codec_is_display(codec)) { + ret = hda_codec_probe_complete(codec); + if (ret < 0) + goto complete_err; + } + + codec->core.lazy_cache = true; + + return 0; + +complete_err: + hda_codec_unregister_dais(codec, component); +parse_pcms_err: + if (codec->patch_ops.free) + codec->patch_ops.free(codec); +err: + snd_hda_codec_cleanup_for_unbind(codec); +device_new_err: + if (hda_codec_is_display(codec)) + snd_hdac_display_power(bus, hdev->addr, false); + + snd_hdac_ext_bus_link_put(bus, hlink); + + pm_runtime_mark_last_busy(bus->dev); + pm_runtime_put_autosuspend(bus->dev); + return ret; +} + +/* Leaves codec with usage_count=1 and status=suspended */ +static void hda_codec_remove(struct snd_soc_component *component) +{ + struct hda_codec *codec = dev_to_hda_codec(component->dev); + struct hdac_device *hdev = &codec->core; + struct hdac_bus *bus = hdev->bus; + struct hdac_ext_link *hlink; + bool was_registered = codec->core.registered; + + /* Don't allow any more runtime suspends */ + pm_runtime_forbid(&hdev->dev); + + hda_codec_unregister_dais(codec, component); + + if (codec->patch_ops.free) + codec->patch_ops.free(codec); + + snd_hda_codec_cleanup_for_unbind(codec); + pm_runtime_put_noidle(&hdev->dev); + /* snd_hdac_device_exit() is only called on bus remove */ + pm_runtime_set_suspended(&hdev->dev); + + if (hda_codec_is_display(codec)) + snd_hdac_display_power(bus, hdev->addr, false); + + hlink = snd_hdac_ext_bus_get_hlink_by_addr(bus, hdev->addr); + if (hlink) + snd_hdac_ext_bus_link_put(bus, hlink); + /* + * HDMI card's hda_codec_probe_complete() (see late_probe()) may + * not be called due to early error, leaving bus uc unbalanced + */ + if (!was_registered) { + pm_runtime_mark_last_busy(bus->dev); + pm_runtime_put_autosuspend(bus->dev); + } + +#ifdef CONFIG_PM + WARN_ON(atomic_read(&hdev->dev.power.usage_count) != 1 || + !pm_runtime_status_suspended(&hdev->dev)); +#endif +} + +static const struct snd_soc_dapm_route hda_dapm_routes[] = { + {"AIF1TX", NULL, "Codec Input Pin1"}, + {"AIF2TX", NULL, "Codec Input Pin2"}, + {"AIF3TX", NULL, "Codec Input Pin3"}, + + {"Codec Output Pin1", NULL, "AIF1RX"}, + {"Codec Output Pin2", NULL, "AIF2RX"}, + {"Codec Output Pin3", NULL, "AIF3RX"}, +}; + +static const struct snd_soc_dapm_widget hda_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Input Pins */ + SND_SOC_DAPM_INPUT("Codec Input Pin1"), + SND_SOC_DAPM_INPUT("Codec Input Pin2"), + SND_SOC_DAPM_INPUT("Codec Input Pin3"), + + /* Output Pins */ + SND_SOC_DAPM_OUTPUT("Codec Output Pin1"), + SND_SOC_DAPM_OUTPUT("Codec Output Pin2"), + SND_SOC_DAPM_OUTPUT("Codec Output Pin3"), +}; + +static struct snd_soc_dai_driver card_binder_dai = { + .id = -1, + .name = "codec-probing-DAI", +}; + +static int hda_hdev_attach(struct hdac_device *hdev) +{ + struct hda_codec *codec = dev_to_hda_codec(&hdev->dev); + struct snd_soc_component_driver *comp_drv; + + comp_drv = devm_kzalloc(&hdev->dev, sizeof(*comp_drv), GFP_KERNEL); + if (!comp_drv) + return -ENOMEM; + + /* + * It's save to rely on dev_name() rather than a copy as component + * driver's lifetime is directly tied to hda codec one + */ + comp_drv->name = dev_name(&hdev->dev); + comp_drv->probe = hda_codec_probe; + comp_drv->remove = hda_codec_remove; + comp_drv->idle_bias_on = false; + if (!hda_codec_is_display(codec)) { + comp_drv->dapm_widgets = hda_dapm_widgets; + comp_drv->num_dapm_widgets = ARRAY_SIZE(hda_dapm_widgets); + comp_drv->dapm_routes = hda_dapm_routes; + comp_drv->num_dapm_routes = ARRAY_SIZE(hda_dapm_routes); + } + + return snd_soc_register_component(&hdev->dev, comp_drv, &card_binder_dai, 1); +} + +static int hda_hdev_detach(struct hdac_device *hdev) +{ + struct hda_codec *codec = dev_to_hda_codec(&hdev->dev); + + if (codec->core.registered) + cancel_delayed_work_sync(&codec->jackpoll_work); + + snd_soc_unregister_component(&hdev->dev); + + return 0; +} + +const struct hdac_ext_bus_ops soc_hda_ext_bus_ops = { + .hdev_attach = hda_hdev_attach, + .hdev_detach = hda_hdev_detach, +}; +EXPORT_SYMBOL_GPL(soc_hda_ext_bus_ops); + +MODULE_DESCRIPTION("HD-Audio codec driver"); +MODULE_AUTHOR("Cezary Rojewski "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/hda.h b/sound/soc/codecs/hda.h new file mode 100644 index 0000000000..78a2be4945 --- /dev/null +++ b/sound/soc/codecs/hda.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * + * Author: Cezary Rojewski + */ + +#ifndef SND_SOC_CODECS_HDA_H +#define SND_SOC_CODECS_HDA_H + +#define hda_codec_is_display(codec) \ + ((((codec)->core.vendor_id >> 16) & 0xFFFF) == 0x8086) + +extern const struct snd_soc_dai_ops snd_soc_hda_codec_dai_ops; + +extern const struct hdac_ext_bus_ops soc_hda_ext_bus_ops; +int hda_codec_probe_complete(struct hda_codec *codec); + +#endif diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c new file mode 100644 index 0000000000..d59d38ce56 --- /dev/null +++ b/sound/soc/codecs/hdac_hda.c @@ -0,0 +1,647 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2015-18 Intel Corporation. + +/* + * hdac_hda.c - ASoC extensions to reuse the legacy HDA codec drivers + * with ASoC platform drivers. These APIs are called by the legacy HDA + * codec drivers using hdac_ext_bus_ops ops. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hdac_hda.h" + +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +#define STUB_HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +static int hdac_hda_dai_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static void hdac_hda_dai_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); +static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, void *stream, + int direction); +static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, + struct snd_soc_dai *dai); + +static const struct snd_soc_dai_ops hdac_hda_dai_ops = { + .startup = hdac_hda_dai_open, + .shutdown = hdac_hda_dai_close, + .prepare = hdac_hda_dai_prepare, + .hw_params = hdac_hda_dai_hw_params, + .hw_free = hdac_hda_dai_hw_free, + .set_stream = hdac_hda_dai_set_stream, +}; + +static struct snd_soc_dai_driver hdac_hda_dais[] = { +{ + .id = HDAC_ANALOG_DAI_ID, + .name = "Analog Codec DAI", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "Analog Codec Playback", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Analog Codec Capture", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_DIGITAL_DAI_ID, + .name = "Digital Codec DAI", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "Digital Codec Playback", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Digital Codec Capture", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_ALT_ANALOG_DAI_ID, + .name = "Alt Analog Codec DAI", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "Alt Analog Codec Playback", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Alt Analog Codec Capture", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +}; + +static struct snd_soc_dai_driver hdac_hda_hdmi_dais[] = { +{ + .id = HDAC_HDMI_0_DAI_ID, + .name = "intel-hdmi-hifi1", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "hifi1", + .channels_min = 1, + .channels_max = 32, + .rates = STUB_HDMI_RATES, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_HDMI_1_DAI_ID, + .name = "intel-hdmi-hifi2", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "hifi2", + .channels_min = 1, + .channels_max = 32, + .rates = STUB_HDMI_RATES, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_HDMI_2_DAI_ID, + .name = "intel-hdmi-hifi3", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "hifi3", + .channels_min = 1, + .channels_max = 32, + .rates = STUB_HDMI_RATES, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_HDMI_3_DAI_ID, + .name = "intel-hdmi-hifi4", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "hifi4", + .channels_min = 1, + .channels_max = 32, + .rates = STUB_HDMI_RATES, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, + +}; + +static int hdac_hda_dai_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hdac_hda_pcm *pcm; + struct hdac_stream *hstream; + + if (!stream) + return -EINVAL; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = &hda_pvt->pcm[dai->id]; + hstream = (struct hdac_stream *)stream; + + pcm->stream_tag[direction] = hstream->stream_tag; + + return 0; +} + +static int hdac_hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + unsigned int format_val; + unsigned int maxbps; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + maxbps = dai->driver->playback.sig_bits; + else + maxbps = dai->driver->capture.sig_bits; + + hda_pvt = snd_soc_component_get_drvdata(component); + format_val = snd_hdac_calc_stream_format(params_rate(params), + params_channels(params), + params_format(params), + maxbps, + 0); + if (!format_val) { + dev_err(dai->dev, + "invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n", + params_rate(params), params_channels(params), + params_format(params), maxbps); + + return -EINVAL; + } + + hda_pvt->pcm[dai->id].format_val[substream->stream] = format_val; + return 0; +} + +static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hda_pcm_stream *hda_stream; + struct hda_pcm *pcm; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return -EINVAL; + + hda_stream = &pcm->stream[substream->stream]; + snd_hda_codec_cleanup(hda_pvt->codec, hda_stream, substream); + + return 0; +} + +static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hda_pcm_stream *hda_stream; + struct hdac_hda_priv *hda_pvt; + struct hdac_device *hdev; + unsigned int format_val; + struct hda_pcm *pcm; + unsigned int stream; + int ret = 0; + + hda_pvt = snd_soc_component_get_drvdata(component); + hdev = &hda_pvt->codec->core; + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return -EINVAL; + + hda_stream = &pcm->stream[substream->stream]; + + stream = hda_pvt->pcm[dai->id].stream_tag[substream->stream]; + format_val = hda_pvt->pcm[dai->id].format_val[substream->stream]; + + ret = snd_hda_codec_prepare(hda_pvt->codec, hda_stream, + stream, format_val, substream); + if (ret < 0) + dev_err(&hdev->dev, "codec prepare failed %d\n", ret); + + return ret; +} + +static int hdac_hda_dai_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hda_pcm_stream *hda_stream; + struct hda_pcm *pcm; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return -EINVAL; + + snd_hda_codec_pcm_get(pcm); + + hda_stream = &pcm->stream[substream->stream]; + + return hda_stream->ops.open(hda_stream, hda_pvt->codec, substream); +} + +static void hdac_hda_dai_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hda_pcm_stream *hda_stream; + struct hda_pcm *pcm; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return; + + hda_stream = &pcm->stream[substream->stream]; + + hda_stream->ops.close(hda_stream, hda_pvt->codec, substream); + + snd_hda_codec_pcm_put(pcm); +} + +static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, + struct snd_soc_dai *dai) +{ + struct hda_codec *hcodec = hda_pvt->codec; + struct hda_pcm *cpcm; + const char *pcm_name; + + /* + * map DAI ID to the closest matching PCM name, using the naming + * scheme used by hda-codec snd_hda_gen_build_pcms() and for + * HDMI in hda_codec patch_hdmi.c) + */ + + switch (dai->id) { + case HDAC_ANALOG_DAI_ID: + pcm_name = "Analog"; + break; + case HDAC_DIGITAL_DAI_ID: + pcm_name = "Digital"; + break; + case HDAC_ALT_ANALOG_DAI_ID: + pcm_name = "Alt Analog"; + break; + case HDAC_HDMI_0_DAI_ID: + pcm_name = "HDMI 0"; + break; + case HDAC_HDMI_1_DAI_ID: + pcm_name = "HDMI 1"; + break; + case HDAC_HDMI_2_DAI_ID: + pcm_name = "HDMI 2"; + break; + case HDAC_HDMI_3_DAI_ID: + pcm_name = "HDMI 3"; + break; + default: + dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id); + return NULL; + } + + list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { + if (strstr(cpcm->name, pcm_name)) { + if (strcmp(pcm_name, "Analog") == 0) { + if (strstr(cpcm->name, "Alt Analog")) + continue; + } + return cpcm; + } + } + + dev_err(&hcodec->core.dev, "didn't find PCM for DAI %s\n", dai->name); + return NULL; +} + +static bool is_hdmi_codec(struct hda_codec *hcodec) +{ + struct hda_pcm *cpcm; + + list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { + if (cpcm->pcm_type == HDA_PCM_TYPE_HDMI) + return true; + } + + return false; +} + +static int hdac_hda_codec_probe(struct snd_soc_component *component) +{ + struct hdac_hda_priv *hda_pvt = + snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct hdac_device *hdev = &hda_pvt->codec->core; + struct hda_codec *hcodec = hda_pvt->codec; + struct hdac_ext_link *hlink; + hda_codec_patch_t patch; + int ret; + + hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return -EIO; + } + + snd_hdac_ext_bus_link_get(hdev->bus, hlink); + + /* + * Ensure any HDA display is powered at codec probe. + * After snd_hda_codec_device_new(), display power is + * managed by runtime PM. + */ + if (hda_pvt->need_display_power) + snd_hdac_display_power(hdev->bus, + HDA_CODEC_IDX_CONTROLLER, true); + + ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card, + hdev->addr, hcodec, true); + if (ret < 0) { + dev_err(&hdev->dev, "failed to create hda codec %d\n", ret); + goto error_no_pm; + } + /* + * Overwrite type to HDA_DEV_ASOC since it is a ASoC driver + * hda_codec.c will check this flag to determine if unregister + * device is needed. + */ + hdev->type = HDA_DEV_ASOC; + + /* + * snd_hda_codec_device_new decrements the usage count so call get pm + * else the device will be powered off + */ + pm_runtime_get_noresume(&hdev->dev); + + hcodec->bus->card = dapm->card->snd_card; + + ret = snd_hda_codec_set_name(hcodec, hcodec->preset->name); + if (ret < 0) { + dev_err(&hdev->dev, "name failed %s\n", hcodec->preset->name); + goto error_pm; + } + + ret = snd_hdac_regmap_init(&hcodec->core); + if (ret < 0) { + dev_err(&hdev->dev, "regmap init failed\n"); + goto error_pm; + } + + patch = (hda_codec_patch_t)hcodec->preset->driver_data; + if (patch) { + ret = patch(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "patch failed %d\n", ret); + goto error_regmap; + } + } else { + dev_dbg(&hdev->dev, "no patch file found\n"); + } + + ret = snd_hda_codec_parse_pcms(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret); + goto error_patch; + } + + /* HDMI controls need to be created in machine drivers */ + if (!is_hdmi_codec(hcodec)) { + ret = snd_hda_codec_build_controls(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "unable to create controls %d\n", + ret); + goto error_patch; + } + } + + hcodec->core.lazy_cache = true; + + if (hda_pvt->need_display_power) + snd_hdac_display_power(hdev->bus, + HDA_CODEC_IDX_CONTROLLER, false); + + /* match for forbid call in snd_hda_codec_device_new() */ + pm_runtime_allow(&hdev->dev); + + /* + * hdac_device core already sets the state to active and calls + * get_noresume. So enable runtime and set the device to suspend. + * pm_runtime_enable is also called during codec registeration + */ + pm_runtime_put(&hdev->dev); + pm_runtime_suspend(&hdev->dev); + + return 0; + +error_patch: + if (hcodec->patch_ops.free) + hcodec->patch_ops.free(hcodec); +error_regmap: + snd_hdac_regmap_exit(hdev); +error_pm: + pm_runtime_put(&hdev->dev); +error_no_pm: + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + return ret; +} + +static void hdac_hda_codec_remove(struct snd_soc_component *component) +{ + struct hdac_hda_priv *hda_pvt = + snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = &hda_pvt->codec->core; + struct hda_codec *codec = hda_pvt->codec; + struct hdac_ext_link *hlink = NULL; + + hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return; + } + + pm_runtime_disable(&hdev->dev); + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + + if (codec->patch_ops.free) + codec->patch_ops.free(codec); + + snd_hda_codec_cleanup_for_unbind(codec); +} + +static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = { + {"AIF1TX", NULL, "Codec Input Pin1"}, + {"AIF2TX", NULL, "Codec Input Pin2"}, + {"AIF3TX", NULL, "Codec Input Pin3"}, + + {"Codec Output Pin1", NULL, "AIF1RX"}, + {"Codec Output Pin2", NULL, "AIF2RX"}, + {"Codec Output Pin3", NULL, "AIF3RX"}, +}; + +static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0, + SND_SOC_NOPM, 0, 0), + + /* Input Pins */ + SND_SOC_DAPM_INPUT("Codec Input Pin1"), + SND_SOC_DAPM_INPUT("Codec Input Pin2"), + SND_SOC_DAPM_INPUT("Codec Input Pin3"), + + /* Output Pins */ + SND_SOC_DAPM_OUTPUT("Codec Output Pin1"), + SND_SOC_DAPM_OUTPUT("Codec Output Pin2"), + SND_SOC_DAPM_OUTPUT("Codec Output Pin3"), +}; + +static const struct snd_soc_component_driver hdac_hda_codec = { + .probe = hdac_hda_codec_probe, + .remove = hdac_hda_codec_remove, + .dapm_widgets = hdac_hda_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets), + .dapm_routes = hdac_hda_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes), + .idle_bias_on = false, + .endianness = 1, +}; + +static const struct snd_soc_component_driver hdac_hda_hdmi_codec = { + .probe = hdac_hda_codec_probe, + .remove = hdac_hda_codec_remove, + .idle_bias_on = false, + .endianness = 1, +}; + +static int hdac_hda_dev_probe(struct hdac_device *hdev) +{ + struct hdac_hda_priv *hda_pvt = dev_get_drvdata(&hdev->dev); + struct hdac_ext_link *hlink; + int ret; + + /* hold the ref while we probe */ + hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return -EIO; + } + snd_hdac_ext_bus_link_get(hdev->bus, hlink); + + /* ASoC specific initialization */ + if (hda_pvt->need_display_power) + ret = devm_snd_soc_register_component(&hdev->dev, + &hdac_hda_hdmi_codec, hdac_hda_hdmi_dais, + ARRAY_SIZE(hdac_hda_hdmi_dais)); + else + ret = devm_snd_soc_register_component(&hdev->dev, + &hdac_hda_codec, hdac_hda_dais, + ARRAY_SIZE(hdac_hda_dais)); + + if (ret < 0) { + dev_err(&hdev->dev, "failed to register HDA codec %d\n", ret); + return ret; + } + + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + + return ret; +} + +static int hdac_hda_dev_remove(struct hdac_device *hdev) +{ + /* + * Resources are freed in hdac_hda_codec_remove(). This + * function is kept to keep hda_codec_driver_remove() happy. + */ + return 0; +} + +static struct hdac_ext_bus_ops hdac_ops = { + .hdev_attach = hdac_hda_dev_probe, + .hdev_detach = hdac_hda_dev_remove, +}; + +struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void) +{ + return &hdac_ops; +} +EXPORT_SYMBOL_GPL(snd_soc_hdac_hda_get_ops); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Extensions for legacy HDA Drivers"); +MODULE_AUTHOR("Rakesh Ughreja"); diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h new file mode 100644 index 0000000000..b65560981a --- /dev/null +++ b/sound/soc/codecs/hdac_hda.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2015-18 Intel Corporation. + */ + +#ifndef __HDAC_HDA_H__ +#define __HDAC_HDA_H__ + +enum { + HDAC_ANALOG_DAI_ID = 0, + HDAC_DIGITAL_DAI_ID, + HDAC_ALT_ANALOG_DAI_ID, + HDAC_HDMI_0_DAI_ID, + HDAC_HDMI_1_DAI_ID, + HDAC_HDMI_2_DAI_ID, + HDAC_HDMI_3_DAI_ID, + HDAC_DAI_ID_NUM +}; + +struct hdac_hda_pcm { + int stream_tag[2]; + unsigned int format_val[2]; +}; + +struct hdac_hda_priv { + struct hda_codec *codec; + struct hdac_hda_pcm pcm[HDAC_DAI_ID_NUM]; + bool need_display_power; +}; + +struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void); + +#endif /* __HDAC_HDA_H__ */ diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c new file mode 100644 index 0000000000..8b6b760296 --- /dev/null +++ b/sound/soc/codecs/hdac_hdmi.c @@ -0,0 +1,2347 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms + * + * Copyright (C) 2014-2015 Intel Corp + * Author: Samreen Nilofer + * Subhransu S. Prusty + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../hda/local.h" +#include "hdac_hdmi.h" + +#define NAME_SIZE 32 + +#define AMP_OUT_MUTE 0xb080 +#define AMP_OUT_UNMUTE 0xb000 +#define PIN_OUT (AC_PINCTL_OUT_EN) + +#define HDA_MAX_CONNECTIONS 32 + +#define HDA_MAX_CVTS 3 +#define HDA_MAX_PORTS 3 + +#define ELD_MAX_SIZE 256 +#define ELD_FIXED_BYTES 20 + +#define ELD_VER_CEA_861D 2 +#define ELD_VER_PARTIAL 31 +#define ELD_MAX_MNL 16 + +struct hdac_hdmi_cvt_params { + unsigned int channels_min; + unsigned int channels_max; + u32 rates; + u64 formats; + unsigned int maxbps; +}; + +struct hdac_hdmi_cvt { + struct list_head head; + hda_nid_t nid; + const char *name; + struct hdac_hdmi_cvt_params params; +}; + +/* Currently only spk_alloc, more to be added */ +struct hdac_hdmi_parsed_eld { + u8 spk_alloc; +}; + +struct hdac_hdmi_eld { + bool monitor_present; + bool eld_valid; + int eld_size; + char eld_buffer[ELD_MAX_SIZE]; + struct hdac_hdmi_parsed_eld info; +}; + +struct hdac_hdmi_pin { + struct list_head head; + hda_nid_t nid; + bool mst_capable; + struct hdac_hdmi_port *ports; + int num_ports; + struct hdac_device *hdev; +}; + +struct hdac_hdmi_port { + struct list_head head; + int id; + struct hdac_hdmi_pin *pin; + int num_mux_nids; + hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; + struct hdac_hdmi_eld eld; + const char *jack_pin; + bool is_connect; + struct snd_soc_dapm_context *dapm; + const char *output_pin; + struct work_struct dapm_work; +}; + +struct hdac_hdmi_pcm { + struct list_head head; + int pcm_id; + struct list_head port_list; + struct hdac_hdmi_cvt *cvt; + struct snd_soc_jack *jack; + int stream_tag; + int channels; + int format; + bool chmap_set; + unsigned char chmap[8]; /* ALSA API channel-map */ + struct mutex lock; + int jack_event; + struct snd_kcontrol *eld_ctl; +}; + +struct hdac_hdmi_dai_port_map { + int dai_id; + struct hdac_hdmi_port *port; + struct hdac_hdmi_cvt *cvt; +}; + +struct hdac_hdmi_drv_data { + unsigned int vendor_nid; +}; + +struct hdac_hdmi_priv { + struct hdac_device *hdev; + struct snd_soc_component *component; + struct snd_card *card; + struct hdac_hdmi_dai_port_map dai_map[HDA_MAX_CVTS]; + struct list_head pin_list; + struct list_head cvt_list; + struct list_head pcm_list; + int num_pin; + int num_cvt; + int num_ports; + struct mutex pin_mutex; + struct hdac_chmap chmap; + struct hdac_hdmi_drv_data *drv_data; + struct snd_soc_dai_driver *dai_drv; +}; + +#define hdev_to_hdmi_priv(_hdev) dev_get_drvdata(&(_hdev)->dev) + +static struct hdac_hdmi_pcm * +hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi, + struct hdac_hdmi_cvt *cvt) +{ + struct hdac_hdmi_pcm *pcm; + + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->cvt == cvt) + return pcm; + } + + return NULL; +} + +static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm, + struct hdac_hdmi_port *port, bool is_connect) +{ + struct hdac_device *hdev = port->pin->hdev; + + port->is_connect = is_connect; + if (is_connect) { + /* + * Report Jack connect event when a device is connected + * for the first time where same PCM is attached to multiple + * ports. + */ + if (pcm->jack_event == 0) { + dev_dbg(&hdev->dev, + "jack report for pcm=%d\n", + pcm->pcm_id); + snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT, + SND_JACK_AVOUT); + } + pcm->jack_event++; + } else { + /* + * Report Jack disconnect event when a device is disconnected + * is the only last connected device when same PCM is attached + * to multiple ports. + */ + if (pcm->jack_event == 1) + snd_soc_jack_report(pcm->jack, 0, SND_JACK_AVOUT); + if (pcm->jack_event > 0) + pcm->jack_event--; + } +} + +static void hdac_hdmi_port_dapm_update(struct hdac_hdmi_port *port) +{ + if (port->is_connect) + snd_soc_dapm_enable_pin(port->dapm, port->jack_pin); + else + snd_soc_dapm_disable_pin(port->dapm, port->jack_pin); + snd_soc_dapm_sync(port->dapm); +} + +static void hdac_hdmi_jack_dapm_work(struct work_struct *work) +{ + struct hdac_hdmi_port *port; + + port = container_of(work, struct hdac_hdmi_port, dapm_work); + hdac_hdmi_port_dapm_update(port); +} + +static void hdac_hdmi_jack_report_sync(struct hdac_hdmi_pcm *pcm, + struct hdac_hdmi_port *port, bool is_connect) +{ + hdac_hdmi_jack_report(pcm, port, is_connect); + hdac_hdmi_port_dapm_update(port); +} + +/* MST supported verbs */ +/* + * Get the no devices that can be connected to a port on the Pin widget. + */ +static int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid) +{ + unsigned int caps; + unsigned int type, param; + + caps = get_wcaps(hdev, nid); + type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN)) + return 0; + + param = snd_hdac_read_parm_uncached(hdev, nid, AC_PAR_DEVLIST_LEN); + if (param == -1) + return param; + + return param & AC_DEV_LIST_LEN_MASK; +} + +/* + * Get the port entry select on the pin. Return the port entry + * id selected on the pin. Return 0 means the first port entry + * is selected or MST is not supported. + */ +static int hdac_hdmi_port_select_get(struct hdac_device *hdev, + struct hdac_hdmi_port *port) +{ + return snd_hdac_codec_read(hdev, port->pin->nid, + 0, AC_VERB_GET_DEVICE_SEL, 0); +} + +/* + * Sets the selected port entry for the configuring Pin widget verb. + * returns error if port set is not equal to port get otherwise success + */ +static int hdac_hdmi_port_select_set(struct hdac_device *hdev, + struct hdac_hdmi_port *port) +{ + int num_ports; + + if (!port->pin->mst_capable) + return 0; + + /* AC_PAR_DEVLIST_LEN is 0 based. */ + num_ports = hdac_hdmi_get_port_len(hdev, port->pin->nid); + if (num_ports < 0) + return -EIO; + /* + * Device List Length is a 0 based integer value indicating the + * number of sink device that a MST Pin Widget can support. + */ + if (num_ports + 1 < port->id) + return 0; + + snd_hdac_codec_write(hdev, port->pin->nid, 0, + AC_VERB_SET_DEVICE_SEL, port->id); + + if (port->id != hdac_hdmi_port_select_get(hdev, port)) + return -EIO; + + dev_dbg(&hdev->dev, "Selected the port=%d\n", port->id); + + return 0; +} + +static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi, + int pcm_idx) +{ + struct hdac_hdmi_pcm *pcm; + + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->pcm_id == pcm_idx) + return pcm; + } + + return NULL; +} + +static unsigned int sad_format(const u8 *sad) +{ + return ((sad[0] >> 0x3) & 0x1f); +} + +static unsigned int sad_sample_bits_lpcm(const u8 *sad) +{ + return (sad[2] & 7); +} + +static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime, + void *eld) +{ + u64 formats = SNDRV_PCM_FMTBIT_S16; + int i; + const u8 *sad, *eld_buf = eld; + + sad = drm_eld_sad(eld_buf); + if (!sad) + goto format_constraint; + + for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) { + if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */ + + /* + * the controller support 20 and 24 bits in 32 bit + * container so we set S32 + */ + if (sad_sample_bits_lpcm(sad) & 0x6) + formats |= SNDRV_PCM_FMTBIT_S32; + } + } + +format_constraint: + return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, + formats); + +} + +static void +hdac_hdmi_set_dip_index(struct hdac_device *hdev, hda_nid_t pin_nid, + int packet_index, int byte_index) +{ + int val; + + val = (packet_index << 5) | (byte_index & 0x1f); + snd_hdac_codec_write(hdev, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val); +} + +struct dp_audio_infoframe { + u8 type; /* 0x84 */ + u8 len; /* 0x1b */ + u8 ver; /* 0x11 << 2 */ + + u8 CC02_CT47; /* match with HDMI infoframe from this on */ + u8 SS01_SF24; + u8 CXT04; + u8 CA; + u8 LFEPBL01_LSV36_DM_INH7; +}; + +static int hdac_hdmi_setup_audio_infoframe(struct hdac_device *hdev, + struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port) +{ + uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; + struct hdmi_audio_infoframe frame; + struct hdac_hdmi_pin *pin = port->pin; + struct dp_audio_infoframe dp_ai; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_cvt *cvt = pcm->cvt; + u8 *dip; + int ret; + int i; + const u8 *eld_buf; + u8 conn_type; + int channels, ca; + + ca = snd_hdac_channel_allocation(hdev, port->eld.info.spk_alloc, + pcm->channels, pcm->chmap_set, true, pcm->chmap); + + channels = snd_hdac_get_active_channels(ca); + hdmi->chmap.ops.set_channel_count(hdev, cvt->nid, channels); + + snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca, + pcm->channels, pcm->chmap, pcm->chmap_set); + + eld_buf = port->eld.eld_buffer; + conn_type = drm_eld_get_conn_type(eld_buf); + + switch (conn_type) { + case DRM_ELD_CONN_TYPE_HDMI: + hdmi_audio_infoframe_init(&frame); + + frame.channels = channels; + frame.channel_allocation = ca; + + ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + break; + + case DRM_ELD_CONN_TYPE_DP: + memset(&dp_ai, 0, sizeof(dp_ai)); + dp_ai.type = 0x84; + dp_ai.len = 0x1b; + dp_ai.ver = 0x11 << 2; + dp_ai.CC02_CT47 = channels - 1; + dp_ai.CA = ca; + + dip = (u8 *)&dp_ai; + break; + + default: + dev_err(&hdev->dev, "Invalid connection type: %d\n", conn_type); + return -EIO; + } + + /* stop infoframe transmission */ + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); + snd_hdac_codec_write(hdev, pin->nid, 0, + AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE); + + + /* Fill infoframe. Index auto-incremented */ + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); + if (conn_type == DRM_ELD_CONN_TYPE_HDMI) { + for (i = 0; i < sizeof(buffer); i++) + snd_hdac_codec_write(hdev, pin->nid, 0, + AC_VERB_SET_HDMI_DIP_DATA, buffer[i]); + } else { + for (i = 0; i < sizeof(dp_ai); i++) + snd_hdac_codec_write(hdev, pin->nid, 0, + AC_VERB_SET_HDMI_DIP_DATA, dip[i]); + } + + /* Start infoframe */ + hdac_hdmi_set_dip_index(hdev, pin->nid, 0x0, 0x0); + snd_hdac_codec_write(hdev, pin->nid, 0, + AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST); + + return 0; +} + +static int hdac_hdmi_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_device *hdev = hdmi->hdev; + struct hdac_hdmi_dai_port_map *dai_map; + struct hdac_hdmi_pcm *pcm; + struct hdac_stream *hstream; + + if (!stream) + return -EINVAL; + + hstream = (struct hdac_stream *)stream; + + dev_dbg(&hdev->dev, "%s: strm_tag: %d\n", __func__, hstream->stream_tag); + + dai_map = &hdmi->dai_map[dai->id]; + + pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); + + if (pcm) + pcm->stream_tag = (hstream->stream_tag << 4); + + return 0; +} + +static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_hdmi_dai_port_map *dai_map; + struct hdac_hdmi_pcm *pcm; + int format; + + dai_map = &hdmi->dai_map[dai->id]; + + format = snd_hdac_calc_stream_format(params_rate(hparams), + params_channels(hparams), params_format(hparams), + dai->driver->playback.sig_bits, 0); + + pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); + if (!pcm) + return -EIO; + + pcm->format = format; + pcm->channels = params_channels(hparams); + + return 0; +} + +static int hdac_hdmi_query_port_connlist(struct hdac_device *hdev, + struct hdac_hdmi_pin *pin, + struct hdac_hdmi_port *port) +{ + if (!(get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) { + dev_warn(&hdev->dev, + "HDMI: pin %d wcaps %#x does not support connection list\n", + pin->nid, get_wcaps(hdev, pin->nid)); + return -EINVAL; + } + + if (hdac_hdmi_port_select_set(hdev, port) < 0) + return -EIO; + + port->num_mux_nids = snd_hdac_get_connections(hdev, pin->nid, + port->mux_nids, HDA_MAX_CONNECTIONS); + if (port->num_mux_nids == 0) + dev_warn(&hdev->dev, + "No connections found for pin:port %d:%d\n", + pin->nid, port->id); + + dev_dbg(&hdev->dev, "num_mux_nids %d for pin:port %d:%d\n", + port->num_mux_nids, pin->nid, port->id); + + return port->num_mux_nids; +} + +/* + * Query pcm list and return port to which stream is routed. + * + * Also query connection list of the pin, to validate the cvt to port map. + * + * Same stream rendering to multiple ports simultaneously can be done + * possibly, but not supported for now in driver. So return the first port + * connected. + */ +static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( + struct hdac_device *hdev, + struct hdac_hdmi_priv *hdmi, + struct hdac_hdmi_cvt *cvt) +{ + struct hdac_hdmi_pcm *pcm; + struct hdac_hdmi_port *port; + int ret, i; + + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (pcm->cvt == cvt) { + if (list_empty(&pcm->port_list)) + continue; + + list_for_each_entry(port, &pcm->port_list, head) { + mutex_lock(&pcm->lock); + ret = hdac_hdmi_query_port_connlist(hdev, + port->pin, port); + mutex_unlock(&pcm->lock); + if (ret < 0) + continue; + + for (i = 0; i < port->num_mux_nids; i++) { + if (port->mux_nids[i] == cvt->nid && + port->eld.monitor_present && + port->eld.eld_valid) + return port; + } + } + } + } + + return NULL; +} + +/* + * Go through all converters and ensure connection is set to + * the correct pin as set via kcontrols. + */ +static void hdac_hdmi_verify_connect_sel_all_pins(struct hdac_device *hdev) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_port *port; + struct hdac_hdmi_cvt *cvt; + int cvt_idx = 0; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt); + if (port && port->pin) { + snd_hdac_codec_write(hdev, port->pin->nid, 0, + AC_VERB_SET_CONNECT_SEL, cvt_idx); + dev_dbg(&hdev->dev, "%s: %s set connect %d -> %d\n", + __func__, cvt->name, port->pin->nid, cvt_idx); + } + ++cvt_idx; + } +} + +/* + * This tries to get a valid pin and set the HW constraints based on the + * ELD. Even if a valid pin is not found return success so that device open + * doesn't fail. + */ +static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_device *hdev = hdmi->hdev; + struct hdac_hdmi_dai_port_map *dai_map; + struct hdac_hdmi_cvt *cvt; + struct hdac_hdmi_port *port; + int ret; + + dai_map = &hdmi->dai_map[dai->id]; + + cvt = dai_map->cvt; + port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt); + + /* + * To make PA and other userland happy. + * userland scans devices so returning error does not help. + */ + if (!port) + return 0; + if ((!port->eld.monitor_present) || + (!port->eld.eld_valid)) { + + dev_warn(&hdev->dev, + "Failed: present?:%d ELD valid?:%d pin:port: %d:%d\n", + port->eld.monitor_present, port->eld.eld_valid, + port->pin->nid, port->id); + + return 0; + } + + dai_map->port = port; + + ret = hdac_hdmi_eld_limit_formats(substream->runtime, + port->eld.eld_buffer); + if (ret < 0) + return ret; + + return snd_pcm_hw_constraint_eld(substream->runtime, + port->eld.eld_buffer); +} + +static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_dai_get_drvdata(dai); + struct hdac_hdmi_dai_port_map *dai_map; + struct hdac_hdmi_pcm *pcm; + + dai_map = &hdmi->dai_map[dai->id]; + + pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt); + + if (pcm) { + mutex_lock(&pcm->lock); + pcm->chmap_set = false; + memset(pcm->chmap, 0, sizeof(pcm->chmap)); + pcm->channels = 0; + mutex_unlock(&pcm->lock); + } + + if (dai_map->port) + dai_map->port = NULL; +} + +static int +hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt) +{ + unsigned int chans; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + int err; + + chans = get_wcaps(hdev, cvt->nid); + chans = get_wcaps_channels(chans); + + cvt->params.channels_min = 2; + + cvt->params.channels_max = chans; + if (chans > hdmi->chmap.channels_max) + hdmi->chmap.channels_max = chans; + + err = snd_hdac_query_supported_pcm(hdev, cvt->nid, + &cvt->params.rates, + &cvt->params.formats, + &cvt->params.maxbps); + if (err < 0) + dev_err(&hdev->dev, + "Failed to query pcm params for nid %d: %d\n", + cvt->nid, err); + + return err; +} + +static int hdac_hdmi_fill_widget_info(struct device *dev, + struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id, + void *priv, const char *wname, const char *stream, + struct snd_kcontrol_new *wc, int numkc, + int (*event)(struct snd_soc_dapm_widget *, + struct snd_kcontrol *, int), unsigned short event_flags) +{ + w->id = id; + w->name = devm_kstrdup(dev, wname, GFP_KERNEL); + if (!w->name) + return -ENOMEM; + + w->sname = stream; + w->reg = SND_SOC_NOPM; + w->shift = 0; + w->kcontrol_news = wc; + w->num_kcontrols = numkc; + w->priv = priv; + w->event = event; + w->event_flags = event_flags; + + return 0; +} + +static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route, + const char *sink, const char *control, const char *src, + int (*handler)(struct snd_soc_dapm_widget *src, + struct snd_soc_dapm_widget *sink)) +{ + route->sink = sink; + route->source = src; + route->control = control; + route->connected = handler; +} + +static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_device *hdev, + struct hdac_hdmi_port *port) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pcm *pcm; + struct hdac_hdmi_port *p; + + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (list_empty(&pcm->port_list)) + continue; + + list_for_each_entry(p, &pcm->port_list, head) { + if (p->id == port->id && port->pin == p->pin) + return pcm; + } + } + + return NULL; +} + +static void hdac_hdmi_set_power_state(struct hdac_device *hdev, + hda_nid_t nid, unsigned int pwr_state) +{ + int count; + unsigned int state; + + if (get_wcaps(hdev, nid) & AC_WCAP_POWER) { + if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) { + for (count = 0; count < 10; count++) { + snd_hdac_codec_read(hdev, nid, 0, + AC_VERB_SET_POWER_STATE, + pwr_state); + state = snd_hdac_sync_power_state(hdev, + nid, pwr_state); + if (!(state & AC_PWRST_ERROR)) + break; + } + } + } +} + +static void hdac_hdmi_set_amp(struct hdac_device *hdev, + hda_nid_t nid, int val) +{ + if (get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP) + snd_hdac_codec_write(hdev, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); +} + + +static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct hdac_hdmi_port *port = w->priv; + struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); + struct hdac_hdmi_pcm *pcm; + + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", + __func__, w->name, event); + + pcm = hdac_hdmi_get_pcm(hdev, port); + if (!pcm) + return -EIO; + + /* set the device if pin is mst_capable */ + if (hdac_hdmi_port_select_set(hdev, port) < 0) + return -EIO; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D0); + + /* Enable out path for this pin widget */ + snd_hdac_codec_write(hdev, port->pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + + hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_UNMUTE); + + return hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); + + case SND_SOC_DAPM_POST_PMD: + hdac_hdmi_set_amp(hdev, port->pin->nid, AMP_OUT_MUTE); + + /* Disable out path for this pin widget */ + snd_hdac_codec_write(hdev, port->pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + + hdac_hdmi_set_power_state(hdev, port->pin->nid, AC_PWRST_D3); + break; + + } + + return 0; +} + +static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct hdac_hdmi_cvt *cvt = w->priv; + struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pcm *pcm; + + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", + __func__, w->name, event); + + pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt); + if (!pcm) + return -EIO; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D0); + + /* Enable transmission */ + snd_hdac_codec_write(hdev, cvt->nid, 0, + AC_VERB_SET_DIGI_CONVERT_1, 1); + + /* Category Code (CC) to zero */ + snd_hdac_codec_write(hdev, cvt->nid, 0, + AC_VERB_SET_DIGI_CONVERT_2, 0); + + snd_hdac_codec_write(hdev, cvt->nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag); + snd_hdac_codec_write(hdev, cvt->nid, 0, + AC_VERB_SET_STREAM_FORMAT, pcm->format); + + /* + * The connection indices are shared by all converters and + * may interfere with each other. Ensure correct + * routing for all converters at stream start. + */ + hdac_hdmi_verify_connect_sel_all_pins(hdev); + + break; + + case SND_SOC_DAPM_POST_PMD: + snd_hdac_codec_write(hdev, cvt->nid, 0, + AC_VERB_SET_CHANNEL_STREAMID, 0); + snd_hdac_codec_write(hdev, cvt->nid, 0, + AC_VERB_SET_STREAM_FORMAT, 0); + + hdac_hdmi_set_power_state(hdev, cvt->nid, AC_PWRST_D3); + break; + + } + + return 0; +} + +static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct hdac_hdmi_port *port = w->priv; + struct hdac_device *hdev = dev_to_hdac_dev(w->dapm->dev); + int mux_idx; + + dev_dbg(&hdev->dev, "%s: widget: %s event: %x\n", + __func__, w->name, event); + + if (!kc) + kc = w->kcontrols[0]; + + mux_idx = dapm_kcontrol_get_value(kc); + + /* set the device if pin is mst_capable */ + if (hdac_hdmi_port_select_set(hdev, port) < 0) + return -EIO; + + if (mux_idx > 0) { + snd_hdac_codec_write(hdev, port->pin->nid, 0, + AC_VERB_SET_CONNECT_SEL, (mux_idx - 1)); + } + + return 0; +} + +/* + * Based on user selection, map the PINs with the PCMs. + */ +static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + struct hdac_hdmi_port *p, *p_next; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_context *dapm = w->dapm; + struct hdac_hdmi_port *port = w->priv; + struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pcm *pcm; + const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; + + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + if (ret < 0) + return ret; + + if (port == NULL) + return -EINVAL; + + mutex_lock(&hdmi->pin_mutex); + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (list_empty(&pcm->port_list)) + continue; + + list_for_each_entry_safe(p, p_next, &pcm->port_list, head) { + if (p == port && p->id == port->id && + p->pin == port->pin) { + hdac_hdmi_jack_report_sync(pcm, port, false); + list_del(&p->head); + } + } + } + + /* + * Jack status is not reported during device probe as the + * PCMs are not registered by then. So report it here. + */ + list_for_each_entry(pcm, &hdmi->pcm_list, head) { + if (!strcmp(cvt_name, pcm->cvt->name)) { + list_add_tail(&port->head, &pcm->port_list); + if (port->eld.monitor_present && port->eld.eld_valid) { + hdac_hdmi_jack_report_sync(pcm, port, true); + mutex_unlock(&hdmi->pin_mutex); + return ret; + } + } + } + mutex_unlock(&hdmi->pin_mutex); + + return ret; +} + +/* + * Ideally the Mux inputs should be based on the num_muxs enumerated, but + * the display driver seem to be programming the connection list for the pin + * widget runtime. + * + * So programming all the possible inputs for the mux, the user has to take + * care of selecting the right one and leaving all other inputs selected to + * "NONE" + */ +static int hdac_hdmi_create_pin_port_muxs(struct hdac_device *hdev, + struct hdac_hdmi_port *port, + struct snd_soc_dapm_widget *widget, + const char *widget_name) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pin *pin = port->pin; + struct snd_kcontrol_new *kc; + struct hdac_hdmi_cvt *cvt; + struct soc_enum *se; + char kc_name[NAME_SIZE]; + char mux_items[NAME_SIZE]; + /* To hold inputs to the Pin mux */ + char *items[HDA_MAX_CONNECTIONS]; + int i = 0; + int num_items = hdmi->num_cvt + 1; + + kc = devm_kzalloc(&hdev->dev, sizeof(*kc), GFP_KERNEL); + if (!kc) + return -ENOMEM; + + se = devm_kzalloc(&hdev->dev, sizeof(*se), GFP_KERNEL); + if (!se) + return -ENOMEM; + + snprintf(kc_name, NAME_SIZE, "Pin %d port %d Input", + pin->nid, port->id); + kc->name = devm_kstrdup(&hdev->dev, kc_name, GFP_KERNEL); + if (!kc->name) + return -ENOMEM; + + kc->private_value = (long)se; + kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc->access = 0; + kc->info = snd_soc_info_enum_double; + kc->put = hdac_hdmi_set_pin_port_mux; + kc->get = snd_soc_dapm_get_enum_double; + + se->reg = SND_SOC_NOPM; + + /* enum texts: ["NONE", "cvt #", "cvt #", ...] */ + se->items = num_items; + se->mask = roundup_pow_of_two(se->items) - 1; + + sprintf(mux_items, "NONE"); + items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL); + if (!items[i]) + return -ENOMEM; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + i++; + sprintf(mux_items, "cvt %d", cvt->nid); + items[i] = devm_kstrdup(&hdev->dev, mux_items, GFP_KERNEL); + if (!items[i]) + return -ENOMEM; + } + + se->texts = devm_kmemdup(&hdev->dev, items, + (num_items * sizeof(char *)), GFP_KERNEL); + if (!se->texts) + return -ENOMEM; + + return hdac_hdmi_fill_widget_info(&hdev->dev, widget, + snd_soc_dapm_mux, port, widget_name, NULL, kc, 1, + hdac_hdmi_pin_mux_widget_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG); +} + +/* Add cvt <- input <- mux route map */ +static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_device *hdev, + struct snd_soc_dapm_widget *widgets, + struct snd_soc_dapm_route *route, int rindex) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + const struct snd_kcontrol_new *kc; + struct soc_enum *se; + int mux_index = hdmi->num_cvt + hdmi->num_ports; + int i, j; + + for (i = 0; i < hdmi->num_ports; i++) { + kc = widgets[mux_index].kcontrol_news; + se = (struct soc_enum *)kc->private_value; + for (j = 0; j < hdmi->num_cvt; j++) { + hdac_hdmi_fill_route(&route[rindex], + widgets[mux_index].name, + se->texts[j + 1], + widgets[j].name, NULL); + + rindex++; + } + + mux_index++; + } +} + +/* + * Widgets are added in the below sequence + * Converter widgets for num converters enumerated + * Pin-port widgets for num ports for Pins enumerated + * Pin-port mux widgets to represent connenction list of pin widget + * + * For each port, one Mux and One output widget is added + * Total widgets elements = num_cvt + (num_ports * 2); + * + * Routes are added as below: + * pin-port mux -> pin (based on num_ports) + * cvt -> "Input sel control" -> pin-port_mux + * + * Total route elements: + * num_ports + (pin_muxes * num_cvt) + */ +static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm) +{ + struct snd_soc_dapm_widget *widgets; + struct snd_soc_dapm_route *route; + struct hdac_device *hdev = dev_to_hdac_dev(dapm->dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct snd_soc_dai_driver *dai_drv = hdmi->dai_drv; + char widget_name[NAME_SIZE]; + struct hdac_hdmi_cvt *cvt; + struct hdac_hdmi_pin *pin; + int ret, i = 0, num_routes = 0, j; + + if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list)) + return -EINVAL; + + widgets = devm_kzalloc(dapm->dev, (sizeof(*widgets) * + ((2 * hdmi->num_ports) + hdmi->num_cvt)), + GFP_KERNEL); + + if (!widgets) + return -ENOMEM; + + /* DAPM widgets to represent each converter widget */ + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + sprintf(widget_name, "Converter %d", cvt->nid); + ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], + snd_soc_dapm_aif_in, cvt, + widget_name, dai_drv[i].playback.stream_name, NULL, 0, + hdac_hdmi_cvt_output_widget_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD); + if (ret < 0) + return ret; + i++; + } + + list_for_each_entry(pin, &hdmi->pin_list, head) { + for (j = 0; j < pin->num_ports; j++) { + sprintf(widget_name, "hif%d-%d Output", + pin->nid, pin->ports[j].id); + ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], + snd_soc_dapm_output, &pin->ports[j], + widget_name, NULL, NULL, 0, + hdac_hdmi_pin_output_widget_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD); + if (ret < 0) + return ret; + pin->ports[j].output_pin = widgets[i].name; + i++; + } + } + + /* DAPM widgets to represent the connection list to pin widget */ + list_for_each_entry(pin, &hdmi->pin_list, head) { + for (j = 0; j < pin->num_ports; j++) { + sprintf(widget_name, "Pin%d-Port%d Mux", + pin->nid, pin->ports[j].id); + ret = hdac_hdmi_create_pin_port_muxs(hdev, + &pin->ports[j], &widgets[i], + widget_name); + if (ret < 0) + return ret; + i++; + + /* For cvt to pin_mux mapping */ + num_routes += hdmi->num_cvt; + + /* For pin_mux to pin mapping */ + num_routes++; + } + } + + route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes), + GFP_KERNEL); + if (!route) + return -ENOMEM; + + i = 0; + /* Add pin <- NULL <- mux route map */ + list_for_each_entry(pin, &hdmi->pin_list, head) { + for (j = 0; j < pin->num_ports; j++) { + int sink_index = i + hdmi->num_cvt; + int src_index = sink_index + pin->num_ports * + hdmi->num_pin; + + hdac_hdmi_fill_route(&route[i], + widgets[sink_index].name, NULL, + widgets[src_index].name, NULL); + i++; + } + } + + hdac_hdmi_add_pinmux_cvt_route(hdev, widgets, route, i); + + snd_soc_dapm_new_controls(dapm, widgets, + ((2 * hdmi->num_ports) + hdmi->num_cvt)); + + snd_soc_dapm_add_routes(dapm, route, num_routes); + snd_soc_dapm_new_widgets(dapm->card); + + return 0; + +} + +static int hdac_hdmi_init_dai_map(struct hdac_device *hdev) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_dai_port_map *dai_map; + struct hdac_hdmi_cvt *cvt; + int dai_id = 0; + + if (list_empty(&hdmi->cvt_list)) + return -EINVAL; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + dai_map = &hdmi->dai_map[dai_id]; + dai_map->dai_id = dai_id; + dai_map->cvt = cvt; + + dai_id++; + + if (dai_id == HDA_MAX_CVTS) { + dev_warn(&hdev->dev, + "Max dais supported: %d\n", dai_id); + break; + } + } + + return 0; +} + +static int hdac_hdmi_add_cvt(struct hdac_device *hdev, hda_nid_t nid) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_cvt *cvt; + char name[NAME_SIZE]; + + cvt = devm_kzalloc(&hdev->dev, sizeof(*cvt), GFP_KERNEL); + if (!cvt) + return -ENOMEM; + + cvt->nid = nid; + sprintf(name, "cvt %d", cvt->nid); + cvt->name = devm_kstrdup(&hdev->dev, name, GFP_KERNEL); + if (!cvt->name) + return -ENOMEM; + + list_add_tail(&cvt->head, &hdmi->cvt_list); + hdmi->num_cvt++; + + return hdac_hdmi_query_cvt_params(hdev, cvt); +} + +static int hdac_hdmi_parse_eld(struct hdac_device *hdev, + struct hdac_hdmi_port *port) +{ + unsigned int ver, mnl; + + ver = (port->eld.eld_buffer[DRM_ELD_VER] & DRM_ELD_VER_MASK) + >> DRM_ELD_VER_SHIFT; + + if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) { + dev_err(&hdev->dev, "HDMI: Unknown ELD version %d\n", ver); + return -EINVAL; + } + + mnl = (port->eld.eld_buffer[DRM_ELD_CEA_EDID_VER_MNL] & + DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT; + + if (mnl > ELD_MAX_MNL) { + dev_err(&hdev->dev, "HDMI: MNL Invalid %d\n", mnl); + return -EINVAL; + } + + port->eld.info.spk_alloc = port->eld.eld_buffer[DRM_ELD_SPEAKER]; + + return 0; +} + +static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, + struct hdac_hdmi_port *port) +{ + struct hdac_device *hdev = pin->hdev; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pcm *pcm; + int size = 0; + int port_id = -1; + bool eld_valid, eld_changed; + + if (!hdmi) + return; + + /* + * In case of non MST pin, get_eld info API expectes port + * to be -1. + */ + mutex_lock(&hdmi->pin_mutex); + port->eld.monitor_present = false; + + if (pin->mst_capable) + port_id = port->id; + + size = snd_hdac_acomp_get_eld(hdev, pin->nid, port_id, + &port->eld.monitor_present, + port->eld.eld_buffer, + ELD_MAX_SIZE); + + if (size > 0) { + size = min(size, ELD_MAX_SIZE); + if (hdac_hdmi_parse_eld(hdev, port) < 0) + size = -EINVAL; + } + + eld_valid = port->eld.eld_valid; + + if (size > 0) { + port->eld.eld_valid = true; + port->eld.eld_size = size; + } else { + port->eld.eld_valid = false; + port->eld.eld_size = 0; + } + + eld_changed = (eld_valid != port->eld.eld_valid); + + pcm = hdac_hdmi_get_pcm(hdev, port); + + if (!port->eld.monitor_present || !port->eld.eld_valid) { + + dev_err(&hdev->dev, "%s: disconnect for pin:port %d:%d\n", + __func__, pin->nid, port->id); + + /* + * PCMs are not registered during device probe, so don't + * report jack here. It will be done in usermode mux + * control select. + */ + if (pcm) { + hdac_hdmi_jack_report(pcm, port, false); + schedule_work(&port->dapm_work); + } + + mutex_unlock(&hdmi->pin_mutex); + return; + } + + if (port->eld.monitor_present && port->eld.eld_valid) { + if (pcm) { + hdac_hdmi_jack_report(pcm, port, true); + schedule_work(&port->dapm_work); + } + + print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1, + port->eld.eld_buffer, port->eld.eld_size, false); + + } + mutex_unlock(&hdmi->pin_mutex); + + if (eld_changed && pcm) + snd_ctl_notify(hdmi->card, + SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &pcm->eld_ctl->id); +} + +static int hdac_hdmi_add_ports(struct hdac_device *hdev, + struct hdac_hdmi_pin *pin) +{ + struct hdac_hdmi_port *ports; + int max_ports = HDA_MAX_PORTS; + int i; + + /* + * FIXME: max_port may vary for each platform, so pass this as + * as driver data or query from i915 interface when this API is + * implemented. + */ + + ports = devm_kcalloc(&hdev->dev, max_ports, sizeof(*ports), GFP_KERNEL); + if (!ports) + return -ENOMEM; + + for (i = 0; i < max_ports; i++) { + ports[i].id = i; + ports[i].pin = pin; + INIT_WORK(&ports[i].dapm_work, hdac_hdmi_jack_dapm_work); + } + pin->ports = ports; + pin->num_ports = max_ports; + return 0; +} + +static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pin *pin; + int ret; + + pin = devm_kzalloc(&hdev->dev, sizeof(*pin), GFP_KERNEL); + if (!pin) + return -ENOMEM; + + pin->nid = nid; + pin->mst_capable = false; + pin->hdev = hdev; + ret = hdac_hdmi_add_ports(hdev, pin); + if (ret < 0) + return ret; + + list_add_tail(&pin->head, &hdmi->pin_list); + hdmi->num_pin++; + hdmi->num_ports += pin->num_ports; + + return 0; +} + +#define INTEL_VENDOR_NID 0x08 +#define INTEL_GLK_VENDOR_NID 0x0b +#define INTEL_GET_VENDOR_VERB 0xf81 +#define INTEL_SET_VENDOR_VERB 0x781 +#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ +#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ + +static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev) +{ + unsigned int vendor_param; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + unsigned int vendor_nid = hdmi->drv_data->vendor_nid; + + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS) + return; + + vendor_param |= INTEL_EN_ALL_PIN_CVTS; + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, + INTEL_SET_VENDOR_VERB, vendor_param); + if (vendor_param == -1) + return; +} + +static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev) +{ + unsigned int vendor_param; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + unsigned int vendor_nid = hdmi->drv_data->vendor_nid; + + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, + INTEL_GET_VENDOR_VERB, 0); + if (vendor_param == -1 || vendor_param & INTEL_EN_DP12) + return; + + /* enable DP1.2 mode */ + vendor_param |= INTEL_EN_DP12; + vendor_param = snd_hdac_codec_read(hdev, vendor_nid, 0, + INTEL_SET_VENDOR_VERB, vendor_param); + if (vendor_param == -1) + return; + +} + +static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_hdmi_pcm *pcm; + struct hdac_hdmi_port *port; + struct hdac_hdmi_eld *eld; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 0; + + pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); + if (!pcm) { + dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, + kcontrol->id.device); + return 0; + } + + if (list_empty(&pcm->port_list)) { + dev_dbg(component->dev, "%s: empty port list, device %d\n", + __func__, kcontrol->id.device); + return 0; + } + + mutex_lock(&hdmi->pin_mutex); + + list_for_each_entry(port, &pcm->port_list, head) { + eld = &port->eld; + + if (eld->eld_valid) { + uinfo->count = eld->eld_size; + break; + } + } + + mutex_unlock(&hdmi->pin_mutex); + + return 0; +} + +static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_hdmi_pcm *pcm; + struct hdac_hdmi_port *port; + struct hdac_hdmi_eld *eld; + + memset(ucontrol->value.bytes.data, 0, sizeof(ucontrol->value.bytes.data)); + + pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); + if (!pcm) { + dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, + kcontrol->id.device); + return 0; + } + + if (list_empty(&pcm->port_list)) { + dev_dbg(component->dev, "%s: empty port list, device %d\n", + __func__, kcontrol->id.device); + return 0; + } + + mutex_lock(&hdmi->pin_mutex); + + list_for_each_entry(port, &pcm->port_list, head) { + eld = &port->eld; + + if (!eld->eld_valid) + continue; + + if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || + eld->eld_size > ELD_MAX_SIZE) { + mutex_unlock(&hdmi->pin_mutex); + + dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n", + __func__, kcontrol->id.device, eld->eld_size); + snd_BUG(); + return -EINVAL; + } + + memcpy(ucontrol->value.bytes.data, eld->eld_buffer, + eld->eld_size); + break; + } + + mutex_unlock(&hdmi->pin_mutex); + + return 0; +} + +static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm) +{ + struct snd_kcontrol *kctl; + struct snd_kcontrol_new hdmi_eld_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = hdac_hdmi_eld_ctl_info, + .get = hdac_hdmi_eld_ctl_get, + .device = pcm->pcm_id, + }; + + /* add ELD ctl with the device number corresponding to the PCM stream */ + kctl = snd_ctl_new1(&hdmi_eld_ctl, component); + if (!kctl) + return -ENOMEM; + + pcm->eld_ctl = kctl; + + return snd_ctl_add(component->card->snd_card, kctl); +} + +static const struct snd_soc_dai_ops hdmi_dai_ops = { + .startup = hdac_hdmi_pcm_open, + .shutdown = hdac_hdmi_pcm_close, + .hw_params = hdac_hdmi_set_hw_params, + .set_stream = hdac_hdmi_set_stream, +}; + +/* + * Each converter can support a stream independently. So a dai is created + * based on the number of converter queried. + */ +static int hdac_hdmi_create_dais(struct hdac_device *hdev, + struct snd_soc_dai_driver **dais, + struct hdac_hdmi_priv *hdmi, int num_dais) +{ + struct snd_soc_dai_driver *hdmi_dais; + struct hdac_hdmi_cvt *cvt; + char name[NAME_SIZE], dai_name[NAME_SIZE]; + int i = 0; + u32 rates, bps; + unsigned int rate_max = 384000, rate_min = 8000; + u64 formats; + int ret; + + hdmi_dais = devm_kzalloc(&hdev->dev, + (sizeof(*hdmi_dais) * num_dais), + GFP_KERNEL); + if (!hdmi_dais) + return -ENOMEM; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + ret = snd_hdac_query_supported_pcm(hdev, cvt->nid, + &rates, &formats, &bps); + if (ret) + return ret; + + /* Filter out 44.1, 88.2 and 176.4Khz */ + rates &= ~(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_176400); + if (!rates) + return -EINVAL; + + sprintf(dai_name, "intel-hdmi-hifi%d", i+1); + hdmi_dais[i].name = devm_kstrdup(&hdev->dev, + dai_name, GFP_KERNEL); + + if (!hdmi_dais[i].name) + return -ENOMEM; + + snprintf(name, sizeof(name), "hifi%d", i+1); + hdmi_dais[i].playback.stream_name = + devm_kstrdup(&hdev->dev, name, GFP_KERNEL); + if (!hdmi_dais[i].playback.stream_name) + return -ENOMEM; + + /* + * Set caps based on capability queried from the converter. + * It will be constrained runtime based on ELD queried. + */ + hdmi_dais[i].playback.formats = formats; + hdmi_dais[i].playback.rates = rates; + hdmi_dais[i].playback.rate_max = rate_max; + hdmi_dais[i].playback.rate_min = rate_min; + hdmi_dais[i].playback.channels_min = 2; + hdmi_dais[i].playback.channels_max = 2; + hdmi_dais[i].playback.sig_bits = bps; + hdmi_dais[i].ops = &hdmi_dai_ops; + i++; + } + + *dais = hdmi_dais; + hdmi->dai_drv = hdmi_dais; + + return 0; +} + +/* + * Parse all nodes and store the cvt/pin nids in array + * Add one time initialization for pin and cvt widgets + */ +static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev, + struct snd_soc_dai_driver **dais, int *num_dais) +{ + hda_nid_t nid; + int i, num_nodes; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + int ret; + + hdac_hdmi_skl_enable_all_pins(hdev); + hdac_hdmi_skl_enable_dp12(hdev); + + num_nodes = snd_hdac_get_sub_nodes(hdev, hdev->afg, &nid); + if (!nid || num_nodes <= 0) { + dev_warn(&hdev->dev, "HDMI: failed to get afg sub nodes\n"); + return -EINVAL; + } + + for (i = 0; i < num_nodes; i++, nid++) { + unsigned int caps; + unsigned int type; + + caps = get_wcaps(hdev, nid); + type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + switch (type) { + + case AC_WID_AUD_OUT: + ret = hdac_hdmi_add_cvt(hdev, nid); + if (ret < 0) + return ret; + break; + + case AC_WID_PIN: + ret = hdac_hdmi_add_pin(hdev, nid); + if (ret < 0) + return ret; + break; + } + } + + if (!hdmi->num_pin || !hdmi->num_cvt) { + ret = -EIO; + dev_err(&hdev->dev, "Bad pin/cvt setup in %s\n", __func__); + return ret; + } + + ret = hdac_hdmi_create_dais(hdev, dais, hdmi, hdmi->num_cvt); + if (ret) { + dev_err(&hdev->dev, "Failed to create dais with err: %d\n", + ret); + return ret; + } + + *num_dais = hdmi->num_cvt; + ret = hdac_hdmi_init_dai_map(hdev); + if (ret < 0) + dev_err(&hdev->dev, "Failed to init DAI map with err: %d\n", + ret); + return ret; +} + +static int hdac_hdmi_pin2port(void *aptr, int pin) +{ + return pin - 4; /* map NID 0x05 -> port #1 */ +} + +static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) +{ + struct hdac_device *hdev = aptr; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pin *pin; + struct hdac_hdmi_port *hport = NULL; + struct snd_soc_component *component = hdmi->component; + int i; + + /* Don't know how this mapping is derived */ + hda_nid_t pin_nid = port + 0x04; + + dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__, + pin_nid, pipe); + + /* + * skip notification during system suspend (but not in runtime PM); + * the state will be updated at resume. Also since the ELD and + * connection states are updated in anyway at the end of the resume, + * we can skip it when received during PM process. + */ + if (snd_power_get_state(component->card->snd_card) != + SNDRV_CTL_POWER_D0) + return; + + if (atomic_read(&hdev->in_pm)) + return; + + list_for_each_entry(pin, &hdmi->pin_list, head) { + if (pin->nid != pin_nid) + continue; + + /* In case of non MST pin, pipe is -1 */ + if (pipe == -1) { + pin->mst_capable = false; + /* if not MST, default is port[0] */ + hport = &pin->ports[0]; + } else { + for (i = 0; i < pin->num_ports; i++) { + pin->mst_capable = true; + if (pin->ports[i].id == pipe) { + hport = &pin->ports[i]; + break; + } + } + } + + if (hport) + hdac_hdmi_present_sense(pin, hport); + } + +} + +static struct drm_audio_component_audio_ops aops = { + .pin2port = hdac_hdmi_pin2port, + .pin_eld_notify = hdac_hdmi_eld_notify_cb, +}; + +static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card, + int device) +{ + struct snd_soc_pcm_runtime *rtd; + + for_each_card_rtds(card, rtd) { + if (rtd->pcm && (rtd->pcm->device == device)) + return rtd->pcm; + } + + return NULL; +} + +/* create jack pin kcontrols */ +static int create_fill_jack_kcontrols(struct snd_soc_card *card, + struct hdac_device *hdev) +{ + struct hdac_hdmi_pin *pin; + struct snd_kcontrol_new *kc; + char kc_name[NAME_SIZE], xname[NAME_SIZE]; + char *name; + int i = 0, j; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct snd_soc_component *component = hdmi->component; + + kc = devm_kcalloc(component->dev, hdmi->num_ports, + sizeof(*kc), GFP_KERNEL); + + if (!kc) + return -ENOMEM; + + list_for_each_entry(pin, &hdmi->pin_list, head) { + for (j = 0; j < pin->num_ports; j++) { + snprintf(xname, sizeof(xname), "hif%d-%d Jack", + pin->nid, pin->ports[j].id); + name = devm_kstrdup(component->dev, xname, GFP_KERNEL); + if (!name) + return -ENOMEM; + snprintf(kc_name, sizeof(kc_name), "%s Switch", xname); + kc[i].name = devm_kstrdup(component->dev, kc_name, + GFP_KERNEL); + if (!kc[i].name) + return -ENOMEM; + + kc[i].private_value = (unsigned long)name; + kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kc[i].access = 0; + kc[i].info = snd_soc_dapm_info_pin_switch; + kc[i].put = snd_soc_dapm_put_pin_switch; + kc[i].get = snd_soc_dapm_get_pin_switch; + i++; + } + } + + return snd_soc_add_card_controls(card, kc, i); +} + +int hdac_hdmi_jack_port_init(struct snd_soc_component *component, + struct snd_soc_dapm_context *dapm) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; + struct hdac_hdmi_pin *pin; + struct snd_soc_dapm_widget *widgets; + struct snd_soc_dapm_route *route; + char w_name[NAME_SIZE]; + int i = 0, j, ret; + + widgets = devm_kcalloc(dapm->dev, hdmi->num_ports, + sizeof(*widgets), GFP_KERNEL); + + if (!widgets) + return -ENOMEM; + + route = devm_kcalloc(dapm->dev, hdmi->num_ports, + sizeof(*route), GFP_KERNEL); + if (!route) + return -ENOMEM; + + /* create Jack DAPM widget */ + list_for_each_entry(pin, &hdmi->pin_list, head) { + for (j = 0; j < pin->num_ports; j++) { + snprintf(w_name, sizeof(w_name), "hif%d-%d Jack", + pin->nid, pin->ports[j].id); + + ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i], + snd_soc_dapm_spk, NULL, + w_name, NULL, NULL, 0, NULL, 0); + if (ret < 0) + return ret; + + pin->ports[j].jack_pin = widgets[i].name; + pin->ports[j].dapm = dapm; + + /* add to route from Jack widget to output */ + hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin, + NULL, pin->ports[j].output_pin, NULL); + + i++; + } + } + + /* Add Route from Jack widget to the output widget */ + ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports); + if (ret < 0) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports); + if (ret < 0) + return ret; + + ret = snd_soc_dapm_new_widgets(dapm->card); + if (ret < 0) + return ret; + + /* Add Jack Pin switch Kcontrol */ + ret = create_fill_jack_kcontrols(dapm->card, hdev); + + if (ret < 0) + return ret; + + /* default set the Jack Pin switch to OFF */ + list_for_each_entry(pin, &hdmi->pin_list, head) { + for (j = 0; j < pin->num_ports; j++) + snd_soc_dapm_disable_pin(pin->ports[j].dapm, + pin->ports[j].jack_pin); + } + + return 0; +} +EXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init); + +int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, + struct snd_soc_jack *jack) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; + struct hdac_hdmi_pcm *pcm; + struct snd_pcm *snd_pcm; + int err; + + /* + * this is a new PCM device, create new pcm and + * add to the pcm list + */ + pcm = devm_kzalloc(&hdev->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + pcm->pcm_id = device; + pcm->cvt = hdmi->dai_map[dai->id].cvt; + pcm->jack_event = 0; + pcm->jack = jack; + mutex_init(&pcm->lock); + INIT_LIST_HEAD(&pcm->port_list); + snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device); + if (snd_pcm) { + err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap); + if (err < 0) { + dev_err(&hdev->dev, + "chmap control add failed with err: %d for pcm: %d\n", + err, device); + return err; + } + } + + /* add control for ELD Bytes */ + err = hdac_hdmi_create_eld_ctl(component, pcm); + if (err < 0) { + dev_err(&hdev->dev, + "eld control add failed with err: %d for pcm: %d\n", + err, device); + return err; + } + + list_add_tail(&pcm->head, &hdmi->pcm_list); + + return 0; +} +EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init); + +static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev, + struct hdac_hdmi_priv *hdmi, bool detect_pin_caps) +{ + int i; + struct hdac_hdmi_pin *pin; + + list_for_each_entry(pin, &hdmi->pin_list, head) { + if (detect_pin_caps) { + + if (hdac_hdmi_get_port_len(hdev, pin->nid) == 0) + pin->mst_capable = false; + else + pin->mst_capable = true; + } + + for (i = 0; i < pin->num_ports; i++) { + if (!pin->mst_capable && i > 0) + continue; + + hdac_hdmi_present_sense(pin, &pin->ports[i]); + } + } +} + +static int hdmi_codec_probe(struct snd_soc_component *component) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct hdac_ext_link *hlink; + int ret; + + hdmi->component = component; + + /* + * hold the ref while we probe, also no need to drop the ref on + * exit, we call pm_runtime_suspend() so that will do for us + */ + hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return -EIO; + } + + snd_hdac_ext_bus_link_get(hdev->bus, hlink); + + ret = create_fill_widget_route_map(dapm); + if (ret < 0) + return ret; + + aops.audio_ptr = hdev; + ret = snd_hdac_acomp_register_notifier(hdev->bus, &aops); + if (ret < 0) { + dev_err(&hdev->dev, "notifier register failed: err: %d\n", ret); + return ret; + } + + hdac_hdmi_present_sense_all_pins(hdev, hdmi, true); + /* Imp: Store the card pointer in hda_codec */ + hdmi->card = dapm->card->snd_card; + + /* + * Setup a device_link between card device and HDMI codec device. + * The card device is the consumer and the HDMI codec device is + * the supplier. With this setting, we can make sure that the audio + * domain in display power will be always turned on before operating + * on the HDMI audio codec registers. + * Let's use the flag DL_FLAG_AUTOREMOVE_CONSUMER. This can make + * sure the device link is freed when the machine driver is removed. + */ + device_link_add(component->card->dev, &hdev->dev, DL_FLAG_RPM_ACTIVE | + DL_FLAG_AUTOREMOVE_CONSUMER); + /* + * hdac_device core already sets the state to active and calls + * get_noresume. So enable runtime and set the device to suspend. + */ + pm_runtime_enable(&hdev->dev); + pm_runtime_put(&hdev->dev); + pm_runtime_suspend(&hdev->dev); + + return 0; +} + +static void hdmi_codec_remove(struct snd_soc_component *component) +{ + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = hdmi->hdev; + int ret; + + ret = snd_hdac_acomp_register_notifier(hdev->bus, NULL); + if (ret < 0) + dev_err(&hdev->dev, "notifier unregister failed: err: %d\n", + ret); + + pm_runtime_disable(&hdev->dev); +} + +#ifdef CONFIG_PM_SLEEP +static int hdmi_codec_resume(struct device *dev) +{ + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; + /* + * As the ELD notify callback request is not entertained while the + * device is in suspend state. Need to manually check detection of + * all pins here. pin capablity change is not support, so use the + * already set pin caps. + * + * NOTE: this is safe to call even if the codec doesn't actually resume. + * The pin check involves only with DRM audio component hooks, so it + * works even if the HD-audio side is still dreaming peacefully. + */ + hdac_hdmi_present_sense_all_pins(hdev, hdmi, false); + return 0; +} +#else +#define hdmi_codec_resume NULL +#endif + +static const struct snd_soc_component_driver hdmi_hda_codec = { + .probe = hdmi_codec_probe, + .remove = hdmi_codec_remove, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static void hdac_hdmi_get_chmap(struct hdac_device *hdev, int pcm_idx, + unsigned char *chmap) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + + memcpy(chmap, pcm->chmap, ARRAY_SIZE(pcm->chmap)); +} + +static void hdac_hdmi_set_chmap(struct hdac_device *hdev, int pcm_idx, + unsigned char *chmap, int prepared) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + struct hdac_hdmi_port *port; + + if (!pcm) + return; + + if (list_empty(&pcm->port_list)) + return; + + mutex_lock(&pcm->lock); + pcm->chmap_set = true; + memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap)); + list_for_each_entry(port, &pcm->port_list, head) + if (prepared) + hdac_hdmi_setup_audio_infoframe(hdev, pcm, port); + mutex_unlock(&pcm->lock); +} + +static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdev, int pcm_idx) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + + if (!pcm) + return false; + + if (list_empty(&pcm->port_list)) + return false; + + return true; +} + +static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + struct hdac_hdmi_port *port; + + if (!pcm) + return 0; + + if (list_empty(&pcm->port_list)) + return 0; + + port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head); + + if (!port || !port->eld.eld_valid) + return 0; + + return port->eld.info.spk_alloc; +} + +static struct hdac_hdmi_drv_data intel_glk_drv_data = { + .vendor_nid = INTEL_GLK_VENDOR_NID, +}; + +static struct hdac_hdmi_drv_data intel_drv_data = { + .vendor_nid = INTEL_VENDOR_NID, +}; + +static int hdac_hdmi_dev_probe(struct hdac_device *hdev) +{ + struct hdac_hdmi_priv *hdmi_priv; + struct snd_soc_dai_driver *hdmi_dais = NULL; + struct hdac_ext_link *hlink; + int num_dais = 0; + int ret; + struct hdac_driver *hdrv = drv_to_hdac_driver(hdev->dev.driver); + const struct hda_device_id *hdac_id = hdac_get_device_id(hdev, hdrv); + + /* hold the ref while we probe */ + hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return -EIO; + } + + snd_hdac_ext_bus_link_get(hdev->bus, hlink); + + hdmi_priv = devm_kzalloc(&hdev->dev, sizeof(*hdmi_priv), GFP_KERNEL); + if (hdmi_priv == NULL) + return -ENOMEM; + + snd_hdac_register_chmap_ops(hdev, &hdmi_priv->chmap); + hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap; + hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap; + hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached; + hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; + hdmi_priv->hdev = hdev; + + if (!hdac_id) + return -ENODEV; + + if (hdac_id->driver_data) + hdmi_priv->drv_data = + (struct hdac_hdmi_drv_data *)hdac_id->driver_data; + else + hdmi_priv->drv_data = &intel_drv_data; + + dev_set_drvdata(&hdev->dev, hdmi_priv); + + INIT_LIST_HEAD(&hdmi_priv->pin_list); + INIT_LIST_HEAD(&hdmi_priv->cvt_list); + INIT_LIST_HEAD(&hdmi_priv->pcm_list); + mutex_init(&hdmi_priv->pin_mutex); + + /* + * Turned off in the runtime_suspend during the first explicit + * pm_runtime_suspend call. + */ + snd_hdac_display_power(hdev->bus, hdev->addr, true); + + ret = hdac_hdmi_parse_and_map_nid(hdev, &hdmi_dais, &num_dais); + if (ret < 0) { + dev_err(&hdev->dev, + "Failed in parse and map nid with err: %d\n", ret); + return ret; + } + snd_hdac_refresh_widgets(hdev); + + /* ASoC specific initialization */ + ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec, + hdmi_dais, num_dais); + + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + + return ret; +} + +static void clear_dapm_works(struct hdac_device *hdev) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_pin *pin; + int i; + + list_for_each_entry(pin, &hdmi->pin_list, head) + for (i = 0; i < pin->num_ports; i++) + cancel_work_sync(&pin->ports[i].dapm_work); +} + +static int hdac_hdmi_dev_remove(struct hdac_device *hdev) +{ + clear_dapm_works(hdev); + snd_hdac_display_power(hdev->bus, hdev->addr, false); + + return 0; +} + +#ifdef CONFIG_PM +static int hdac_hdmi_runtime_suspend(struct device *dev) +{ + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_bus *bus = hdev->bus; + struct hdac_ext_link *hlink; + + dev_dbg(dev, "Enter: %s\n", __func__); + + /* controller may not have been initialized for the first time */ + if (!bus) + return 0; + + /* + * Power down afg. + * codec_read is preferred over codec_write to set the power state. + * This way verb is send to set the power state and response + * is received. So setting power state is ensured without using loop + * to read the state. + */ + snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, + AC_PWRST_D3); + + hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev)); + if (!hlink) { + dev_err(dev, "hdac link not found\n"); + return -EIO; + } + + snd_hdac_codec_link_down(hdev); + snd_hdac_ext_bus_link_put(bus, hlink); + + snd_hdac_display_power(bus, hdev->addr, false); + + return 0; +} + +static int hdac_hdmi_runtime_resume(struct device *dev) +{ + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_bus *bus = hdev->bus; + struct hdac_ext_link *hlink; + + dev_dbg(dev, "Enter: %s\n", __func__); + + /* controller may not have been initialized for the first time */ + if (!bus) + return 0; + + hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, dev_name(dev)); + if (!hlink) { + dev_err(dev, "hdac link not found\n"); + return -EIO; + } + + snd_hdac_ext_bus_link_get(bus, hlink); + snd_hdac_codec_link_up(hdev); + + snd_hdac_display_power(bus, hdev->addr, true); + + hdac_hdmi_skl_enable_all_pins(hdev); + hdac_hdmi_skl_enable_dp12(hdev); + + /* Power up afg */ + snd_hdac_codec_read(hdev, hdev->afg, 0, AC_VERB_SET_POWER_STATE, + AC_PWRST_D0); + + return 0; +} +#else +#define hdac_hdmi_runtime_suspend NULL +#define hdac_hdmi_runtime_resume NULL +#endif + +static const struct dev_pm_ops hdac_hdmi_pm = { + SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, hdmi_codec_resume) +}; + +static const struct hda_device_id hdmi_list[] = { + HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0), + HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0), + HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0), + HDA_CODEC_EXT_ENTRY(0x8086280c, 0x100000, "Cannonlake HDMI", + &intel_glk_drv_data), + HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI", + &intel_glk_drv_data), + {} +}; + +MODULE_DEVICE_TABLE(hdaudio, hdmi_list); + +static struct hdac_driver hdmi_driver = { + .driver = { + .name = "HDMI HDA Codec", + .pm = &hdac_hdmi_pm, + }, + .id_table = hdmi_list, + .probe = hdac_hdmi_dev_probe, + .remove = hdac_hdmi_dev_remove, +}; + +static int __init hdmi_init(void) +{ + return snd_hda_ext_driver_register(&hdmi_driver); +} + +static void __exit hdmi_exit(void) +{ + snd_hda_ext_driver_unregister(&hdmi_driver); +} + +module_init(hdmi_init); +module_exit(hdmi_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("HDMI HD codec"); +MODULE_AUTHOR("Samreen Nilofer"); +MODULE_AUTHOR("Subhransu S. Prusty"); diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h new file mode 100644 index 0000000000..493fa3b4ef --- /dev/null +++ b/sound/soc/codecs/hdac_hdmi.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __HDAC_HDMI_H__ +#define __HDAC_HDMI_H__ + +int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, + struct snd_soc_jack *jack); + +int hdac_hdmi_jack_port_init(struct snd_soc_component *component, + struct snd_soc_dapm_context *dapm); +#endif /* __HDAC_HDMI_H__ */ diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c new file mode 100644 index 0000000000..0938671700 --- /dev/null +++ b/sound/soc/codecs/hdmi-codec.c @@ -0,0 +1,1108 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC codec for HDMI encoder drivers + * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Jyri Sarha + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* This is only to get MAX_ELD_BYTES */ + +#define HDMI_CODEC_CHMAP_IDX_UNKNOWN -1 + +/* + * CEA speaker placement for HDMI 1.4: + * + * FL FLC FC FRC FR FRW + * + * LFE + * + * RL RLC RC RRC RR + * + * Speaker placement has to be extended to support HDMI 2.0 + */ +enum hdmi_codec_cea_spk_placement { + FL = BIT(0), /* Front Left */ + FC = BIT(1), /* Front Center */ + FR = BIT(2), /* Front Right */ + FLC = BIT(3), /* Front Left Center */ + FRC = BIT(4), /* Front Right Center */ + RL = BIT(5), /* Rear Left */ + RC = BIT(6), /* Rear Center */ + RR = BIT(7), /* Rear Right */ + RLC = BIT(8), /* Rear Left Center */ + RRC = BIT(9), /* Rear Right Center */ + LFE = BIT(10), /* Low Frequency Effect */ +}; + +/* + * cea Speaker allocation structure + */ +struct hdmi_codec_cea_spk_alloc { + const int ca_id; + unsigned int n_ch; + unsigned long mask; +}; + +/* Channel maps stereo HDMI */ +static const struct snd_pcm_chmap_elem hdmi_codec_stereo_chmaps[] = { + { .channels = 2, + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { } +}; + +/* Channel maps for multi-channel playbacks, up to 8 n_ch */ +static const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = { + { .channels = 2, /* CA_ID 0x00 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } }, + { .channels = 4, /* CA_ID 0x01 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA } }, + { .channels = 4, /* CA_ID 0x02 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC } }, + { .channels = 4, /* CA_ID 0x03 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC } }, + { .channels = 6, /* CA_ID 0x04 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x05 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x06 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x07 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 6, /* CA_ID 0x08 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, /* CA_ID 0x09 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, /* CA_ID 0x0A */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 6, /* CA_ID 0x0B */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, + { .channels = 8, /* CA_ID 0x0C */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x0D */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x0E */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x0F */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } }, + { .channels = 8, /* CA_ID 0x10 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x11 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x12 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x13 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR, + SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } }, + { .channels = 8, /* CA_ID 0x14 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x15 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x16 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x17 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x18 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x19 */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1A */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1B */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1C */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1D */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1E */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { .channels = 8, /* CA_ID 0x1F */ + .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE, + SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, + SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } }, + { } +}; + +/* + * hdmi_codec_channel_alloc: speaker configuration available for CEA + * + * This is an ordered list that must match with hdmi_codec_8ch_chmaps struct + * The preceding ones have better chances to be selected by + * hdmi_codec_get_ch_alloc_table_idx(). + */ +static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = { + { .ca_id = 0x00, .n_ch = 2, + .mask = FL | FR}, + /* 2.1 */ + { .ca_id = 0x01, .n_ch = 4, + .mask = FL | FR | LFE}, + /* Dolby Surround */ + { .ca_id = 0x02, .n_ch = 4, + .mask = FL | FR | FC }, + /* surround51 */ + { .ca_id = 0x0b, .n_ch = 6, + .mask = FL | FR | LFE | FC | RL | RR}, + /* surround40 */ + { .ca_id = 0x08, .n_ch = 6, + .mask = FL | FR | RL | RR }, + /* surround41 */ + { .ca_id = 0x09, .n_ch = 6, + .mask = FL | FR | LFE | RL | RR }, + /* surround50 */ + { .ca_id = 0x0a, .n_ch = 6, + .mask = FL | FR | FC | RL | RR }, + /* 6.1 */ + { .ca_id = 0x0f, .n_ch = 8, + .mask = FL | FR | LFE | FC | RL | RR | RC }, + /* surround71 */ + { .ca_id = 0x13, .n_ch = 8, + .mask = FL | FR | LFE | FC | RL | RR | RLC | RRC }, + /* others */ + { .ca_id = 0x03, .n_ch = 8, + .mask = FL | FR | LFE | FC }, + { .ca_id = 0x04, .n_ch = 8, + .mask = FL | FR | RC}, + { .ca_id = 0x05, .n_ch = 8, + .mask = FL | FR | LFE | RC }, + { .ca_id = 0x06, .n_ch = 8, + .mask = FL | FR | FC | RC }, + { .ca_id = 0x07, .n_ch = 8, + .mask = FL | FR | LFE | FC | RC }, + { .ca_id = 0x0c, .n_ch = 8, + .mask = FL | FR | RC | RL | RR }, + { .ca_id = 0x0d, .n_ch = 8, + .mask = FL | FR | LFE | RL | RR | RC }, + { .ca_id = 0x0e, .n_ch = 8, + .mask = FL | FR | FC | RL | RR | RC }, + { .ca_id = 0x10, .n_ch = 8, + .mask = FL | FR | RL | RR | RLC | RRC }, + { .ca_id = 0x11, .n_ch = 8, + .mask = FL | FR | LFE | RL | RR | RLC | RRC }, + { .ca_id = 0x12, .n_ch = 8, + .mask = FL | FR | FC | RL | RR | RLC | RRC }, + { .ca_id = 0x14, .n_ch = 8, + .mask = FL | FR | FLC | FRC }, + { .ca_id = 0x15, .n_ch = 8, + .mask = FL | FR | LFE | FLC | FRC }, + { .ca_id = 0x16, .n_ch = 8, + .mask = FL | FR | FC | FLC | FRC }, + { .ca_id = 0x17, .n_ch = 8, + .mask = FL | FR | LFE | FC | FLC | FRC }, + { .ca_id = 0x18, .n_ch = 8, + .mask = FL | FR | RC | FLC | FRC }, + { .ca_id = 0x19, .n_ch = 8, + .mask = FL | FR | LFE | RC | FLC | FRC }, + { .ca_id = 0x1a, .n_ch = 8, + .mask = FL | FR | RC | FC | FLC | FRC }, + { .ca_id = 0x1b, .n_ch = 8, + .mask = FL | FR | LFE | RC | FC | FLC | FRC }, + { .ca_id = 0x1c, .n_ch = 8, + .mask = FL | FR | RL | RR | FLC | FRC }, + { .ca_id = 0x1d, .n_ch = 8, + .mask = FL | FR | LFE | RL | RR | FLC | FRC }, + { .ca_id = 0x1e, .n_ch = 8, + .mask = FL | FR | FC | RL | RR | FLC | FRC }, + { .ca_id = 0x1f, .n_ch = 8, + .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC }, +}; + +struct hdmi_codec_priv { + struct hdmi_codec_pdata hcd; + uint8_t eld[MAX_ELD_BYTES]; + struct snd_pcm_chmap *chmap_info; + unsigned int chmap_idx; + struct mutex lock; + bool busy; + struct snd_soc_jack *jack; + unsigned int jack_status; + u8 iec_status[AES_IEC958_STATUS_SIZE]; +}; + +static const struct snd_soc_dapm_widget hdmi_widgets[] = { + SND_SOC_DAPM_OUTPUT("TX"), + SND_SOC_DAPM_OUTPUT("RX"), +}; + +enum { + DAI_ID_I2S = 0, + DAI_ID_SPDIF, +}; + +static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof_field(struct hdmi_codec_priv, eld); + + return 0; +} + +static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld)); + + return 0; +} + +static unsigned long hdmi_codec_spk_mask_from_alloc(int spk_alloc) +{ + int i; + static const unsigned long hdmi_codec_eld_spk_alloc_bits[] = { + [0] = FL | FR, [1] = LFE, [2] = FC, [3] = RL | RR, + [4] = RC, [5] = FLC | FRC, [6] = RLC | RRC, + }; + unsigned long spk_mask = 0; + + for (i = 0; i < ARRAY_SIZE(hdmi_codec_eld_spk_alloc_bits); i++) { + if (spk_alloc & (1 << i)) + spk_mask |= hdmi_codec_eld_spk_alloc_bits[i]; + } + + return spk_mask; +} + +static void hdmi_codec_eld_chmap(struct hdmi_codec_priv *hcp) +{ + u8 spk_alloc; + unsigned long spk_mask; + + spk_alloc = drm_eld_get_spk_alloc(hcp->eld); + spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); + + /* Detect if only stereo supported, else return 8 channels mappings */ + if ((spk_mask & ~(FL | FR)) && hcp->chmap_info->max_channels > 2) + hcp->chmap_info->chmap = hdmi_codec_8ch_chmaps; + else + hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps; +} + +static int hdmi_codec_get_ch_alloc_table_idx(struct hdmi_codec_priv *hcp, + unsigned char channels) +{ + int i; + u8 spk_alloc; + unsigned long spk_mask; + const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc; + + spk_alloc = drm_eld_get_spk_alloc(hcp->eld); + spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc); + + for (i = 0; i < ARRAY_SIZE(hdmi_codec_channel_alloc); i++, cap++) { + /* If spk_alloc == 0, HDMI is unplugged return stereo config*/ + if (!spk_alloc && cap->ca_id == 0) + return i; + if (cap->n_ch != channels) + continue; + if (!(cap->mask == (spk_mask & cap->mask))) + continue; + return i; + } + + return -EINVAL; +} +static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned const char *map; + unsigned int i; + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = info->private_data; + + map = info->chmap[hcp->chmap_idx].map; + + for (i = 0; i < info->max_channels; i++) { + if (hcp->chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN) + ucontrol->value.integer.value[i] = 0; + else + ucontrol->value.integer.value[i] = map[i]; + } + + return 0; +} + +static int hdmi_codec_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int hdmi_codec_iec958_default_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + memcpy(ucontrol->value.iec958.status, hcp->iec_status, + sizeof(hcp->iec_status)); + + return 0; +} + +static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + memcpy(hcp->iec_status, ucontrol->value.iec958.status, + sizeof(hcp->iec_status)); + + return 0; +} + +static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + memset(ucontrol->value.iec958.status, 0xff, + sizeof_field(struct hdmi_codec_priv, iec_status)); + + return 0; +} + +static int hdmi_codec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + bool has_capture = !hcp->hcd.no_i2s_capture; + bool has_playback = !hcp->hcd.no_i2s_playback; + int ret = 0; + + if (!((has_playback && tx) || (has_capture && !tx))) + return 0; + + mutex_lock(&hcp->lock); + if (hcp->busy) { + dev_err(dai->dev, "Only one simultaneous stream supported!\n"); + mutex_unlock(&hcp->lock); + return -EINVAL; + } + + if (hcp->hcd.ops->audio_startup) { + ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data); + if (ret) + goto err; + } + + if (tx && hcp->hcd.ops->get_eld) { + ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data, + hcp->eld, sizeof(hcp->eld)); + if (ret) + goto err; + + ret = snd_pcm_hw_constraint_eld(substream->runtime, hcp->eld); + if (ret) + goto err; + + /* Select chmap supported */ + hdmi_codec_eld_chmap(hcp); + } + + hcp->busy = true; + +err: + mutex_unlock(&hcp->lock); + return ret; +} + +static void hdmi_codec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + bool has_capture = !hcp->hcd.no_i2s_capture; + bool has_playback = !hcp->hcd.no_i2s_playback; + + if (!((has_playback && tx) || (has_capture && !tx))) + return; + + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data); + + mutex_lock(&hcp->lock); + hcp->busy = false; + mutex_unlock(&hcp->lock); +} + +static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, + unsigned int sample_width, + unsigned int sample_rate, + unsigned int channels, + struct hdmi_codec_params *hp) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + int idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + u8 ca_id = 0; + bool pcm_audio = !(hcp->iec_status[0] & IEC958_AES0_NONAUDIO); + + if (pcm_audio) { + /* Select a channel allocation that matches with ELD and pcm channels */ + idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels); + + if (idx < 0) { + dev_err(dai->dev, "Not able to map channels to speakers (%d)\n", + idx); + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + return idx; + } + + ca_id = hdmi_codec_channel_alloc[idx].ca_id; + } + + memset(hp, 0, sizeof(*hp)); + + hdmi_audio_infoframe_init(&hp->cea); + + if (pcm_audio) + hp->cea.channels = channels; + else + hp->cea.channels = 0; + + hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; + hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; + hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; + hp->cea.channel_allocation = ca_id; + + hp->sample_width = sample_width; + hp->sample_rate = sample_rate; + hp->channels = channels; + + if (pcm_audio) + hcp->chmap_idx = ca_id; + else + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + + return 0; +} + +static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); + struct hdmi_codec_params hp = { + .iec = { + .status = { 0 }, + .subcode = { 0 }, + .pad = 0, + .dig_subframe = { 0 }, + } + }; + int ret; + + if (!hcp->hcd.ops->hw_params) + return 0; + + dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, + params_width(params), params_rate(params), + params_channels(params)); + + ret = hdmi_codec_fill_codec_params(dai, + params_width(params), + params_rate(params), + params_channels(params), + &hp); + if (ret < 0) + return ret; + + memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status)); + ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status, + sizeof(hp.iec.status)); + if (ret < 0) { + dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", + ret); + return ret; + } + + cf->bit_fmt = params_format(params); + return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data, + cf, &hp); +} + +static int hdmi_codec_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int channels = runtime->channels; + unsigned int width = snd_pcm_format_width(runtime->format); + unsigned int rate = runtime->rate; + struct hdmi_codec_params hp; + int ret; + + if (!hcp->hcd.ops->prepare) + return 0; + + dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__, + width, rate, channels); + + ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp); + if (ret < 0) + return ret; + + memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status)); + ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status, + sizeof(hp.iec.status)); + if (ret < 0) { + dev_err(dai->dev, "Creating IEC958 channel status failed %d\n", + ret); + return ret; + } + + cf->bit_fmt = runtime->format; + return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data, + cf, &hp); +} + +static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); + + /* Reset daifmt */ + memset(cf, 0, sizeof(*cf)); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + cf->bit_clk_provider = 1; + cf->frame_clk_provider = 1; + break; + case SND_SOC_DAIFMT_CBC_CFP: + cf->frame_clk_provider = 1; + break; + case SND_SOC_DAIFMT_CBP_CFC: + cf->bit_clk_provider = 1; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + cf->frame_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + cf->bit_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_IF: + cf->frame_clk_inv = 1; + cf->bit_clk_inv = 1; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + cf->fmt = HDMI_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + cf->fmt = HDMI_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + cf->fmt = HDMI_DSP_B; + break; + case SND_SOC_DAIFMT_RIGHT_J: + cf->fmt = HDMI_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + cf->fmt = HDMI_LEFT_J; + break; + case SND_SOC_DAIFMT_AC97: + cf->fmt = HDMI_AC97; + break; + default: + dev_err(dai->dev, "Invalid DAI interface format\n"); + return -EINVAL; + } + + return 0; +} + +static int hdmi_codec_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + + /* + * ignore if direction was CAPTURE + * and it had .no_capture_mute flag + * see + * snd_soc_dai_digital_mute() + */ + if (hcp->hcd.ops->mute_stream && + (direction == SNDRV_PCM_STREAM_PLAYBACK || + !hcp->hcd.ops->no_capture_mute)) + return hcp->hcd.ops->mute_stream(dai->dev->parent, + hcp->hcd.data, + mute, direction); + + return -ENOTSUPP; +} + +/* + * This driver can select all SND_SOC_DAIFMT_CBx_CFx, + * but need to be selected from Sound Card, not be auto selected. + * Because it might be used from other driver. + * For example, + * ${LINUX}/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c + */ +static u64 hdmi_codec_formats = + SND_SOC_POSSIBLE_DAIFMT_NB_NF | + SND_SOC_POSSIBLE_DAIFMT_NB_IF | + SND_SOC_POSSIBLE_DAIFMT_IB_NF | + SND_SOC_POSSIBLE_DAIFMT_IB_IF | + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B | + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | + SND_SOC_POSSIBLE_DAIFMT_AC97; + +#define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000) + +#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) + +/* + * This list is only for formats allowed on the I2S bus. So there is + * some formats listed that are not supported by HDMI interface. For + * instance allowing the 32-bit formats enables 24-precision with CPU + * DAIs that do not support 24-bit formats. If the extra formats cause + * problems, we should add the video side driver an option to disable + * them. + */ +#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static struct snd_kcontrol_new hdmi_codec_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = hdmi_codec_iec958_info, + .get = hdmi_codec_iec958_mask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = hdmi_codec_iec958_info, + .get = hdmi_codec_iec958_default_get, + .put = hdmi_codec_iec958_default_put, + }, + { + .access = (SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = hdmi_eld_ctl_info, + .get = hdmi_eld_ctl_get, + }, +}; + +static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *dai) +{ + struct snd_soc_dai_driver *drv = dai->driver; + struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); + unsigned int i; + int ret; + + ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, drv->playback.channels_max, 0, + &hcp->chmap_info); + if (ret < 0) + return ret; + + /* override handlers */ + hcp->chmap_info->private_data = hcp; + hcp->chmap_info->kctl->get = hdmi_codec_chmap_ctl_get; + + /* default chmap supported is stereo */ + hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps; + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + + for (i = 0; i < ARRAY_SIZE(hdmi_codec_controls); i++) { + struct snd_kcontrol *kctl; + + /* add ELD ctl with the device number corresponding to the PCM stream */ + kctl = snd_ctl_new1(&hdmi_codec_controls[i], dai->component); + if (!kctl) + return -ENOMEM; + + kctl->id.device = rtd->pcm->device; + ret = snd_ctl_add(rtd->card->snd_card, kctl); + if (ret < 0) + return ret; + } + + return 0; +} + +static int hdmi_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_dapm_context *dapm; + struct hdmi_codec_daifmt *daifmt; + struct snd_soc_dapm_route route[] = { + { + .sink = "TX", + .source = dai->driver->playback.stream_name, + }, + { + .sink = dai->driver->capture.stream_name, + .source = "RX", + }, + }; + int ret, i; + + dapm = snd_soc_component_get_dapm(dai->component); + + /* One of the directions might be omitted for unidirectional DAIs */ + for (i = 0; i < ARRAY_SIZE(route); i++) { + if (!route[i].source || !route[i].sink) + continue; + + ret = snd_soc_dapm_add_routes(dapm, &route[i], 1); + if (ret) + return ret; + } + + daifmt = devm_kzalloc(dai->dev, sizeof(*daifmt), GFP_KERNEL); + if (!daifmt) + return -ENOMEM; + + snd_soc_dai_dma_data_set_playback(dai, daifmt); + + return 0; +} + +static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp, + unsigned int jack_status) +{ + if (jack_status != hcp->jack_status) { + if (hcp->jack) + snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT); + hcp->jack_status = jack_status; + } +} + +static void plugged_cb(struct device *dev, bool plugged) +{ + struct hdmi_codec_priv *hcp = dev_get_drvdata(dev); + + if (plugged) { + if (hcp->hcd.ops->get_eld) { + hcp->hcd.ops->get_eld(dev->parent, hcp->hcd.data, + hcp->eld, sizeof(hcp->eld)); + } + hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT); + } else { + hdmi_codec_jack_report(hcp, 0); + memset(hcp->eld, 0, sizeof(hcp->eld)); + } +} + +static int hdmi_codec_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, + void *data) +{ + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + if (hcp->hcd.ops->hook_plugged_cb) { + hcp->jack = jack; + + /* + * Report the initial jack status which may have been provided + * by the parent hdmi driver while the hpd hook was registered. + */ + snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_LINEOUT); + + return 0; + } + + return -ENOTSUPP; +} + +static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai) +{ + struct hdmi_codec_daifmt *cf; + int ret; + + ret = hdmi_dai_probe(dai); + if (ret) + return ret; + + cf = snd_soc_dai_dma_data_get_playback(dai); + cf->fmt = HDMI_SPDIF; + + return 0; +} + +static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = { + .probe = hdmi_dai_probe, + .startup = hdmi_codec_startup, + .shutdown = hdmi_codec_shutdown, + .hw_params = hdmi_codec_hw_params, + .prepare = hdmi_codec_prepare, + .set_fmt = hdmi_codec_i2s_set_fmt, + .mute_stream = hdmi_codec_mute, + .pcm_new = hdmi_codec_pcm_new, + .auto_selectable_formats = &hdmi_codec_formats, + .num_auto_selectable_formats = 1, +}; + +static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = { + .probe = hdmi_dai_spdif_probe, + .startup = hdmi_codec_startup, + .shutdown = hdmi_codec_shutdown, + .hw_params = hdmi_codec_hw_params, + .mute_stream = hdmi_codec_mute, + .pcm_new = hdmi_codec_pcm_new, +}; + +static const struct snd_soc_dai_driver hdmi_i2s_dai = { + .name = "i2s-hifi", + .id = DAI_ID_I2S, + .playback = { + .stream_name = "I2S Playback", + .channels_min = 2, + .channels_max = 8, + .rates = HDMI_RATES, + .formats = I2S_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = HDMI_RATES, + .formats = I2S_FORMATS, + .sig_bits = 24, + }, + .ops = &hdmi_codec_i2s_dai_ops, +}; + +static const struct snd_soc_dai_driver hdmi_spdif_dai = { + .name = "spdif-hifi", + .id = DAI_ID_SPDIF, + .playback = { + .stream_name = "SPDIF Playback", + .channels_min = 2, + .channels_max = 2, + .rates = HDMI_RATES, + .formats = SPDIF_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = HDMI_RATES, + .formats = SPDIF_FORMATS, + }, + .ops = &hdmi_codec_spdif_dai_ops, +}; + +static int hdmi_of_xlate_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + int ret = -ENOTSUPP; /* see snd_soc_get_dai_id() */ + + if (hcp->hcd.ops->get_dai_id) + ret = hcp->hcd.ops->get_dai_id(component, endpoint); + + return ret; +} + +static int hdmi_probe(struct snd_soc_component *component) +{ + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + int ret = 0; + + if (hcp->hcd.ops->hook_plugged_cb) { + ret = hcp->hcd.ops->hook_plugged_cb(component->dev->parent, + hcp->hcd.data, + plugged_cb, + component->dev); + } + + return ret; +} + +static void hdmi_remove(struct snd_soc_component *component) +{ + struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); + + if (hcp->hcd.ops->hook_plugged_cb) + hcp->hcd.ops->hook_plugged_cb(component->dev->parent, + hcp->hcd.data, NULL, NULL); +} + +static const struct snd_soc_component_driver hdmi_driver = { + .probe = hdmi_probe, + .remove = hdmi_remove, + .dapm_widgets = hdmi_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), + .of_xlate_dai_id = hdmi_of_xlate_dai_id, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .set_jack = hdmi_codec_set_jack, +}; + +static int hdmi_codec_probe(struct platform_device *pdev) +{ + struct hdmi_codec_pdata *hcd = pdev->dev.platform_data; + struct snd_soc_dai_driver *daidrv; + struct device *dev = &pdev->dev; + struct hdmi_codec_priv *hcp; + int dai_count, i = 0; + int ret; + + if (!hcd) { + dev_err(dev, "%s: No platform data\n", __func__); + return -EINVAL; + } + + dai_count = hcd->i2s + hcd->spdif; + if (dai_count < 1 || !hcd->ops || + (!hcd->ops->hw_params && !hcd->ops->prepare) || + !hcd->ops->audio_shutdown) { + dev_err(dev, "%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL); + if (!hcp) + return -ENOMEM; + + hcp->hcd = *hcd; + mutex_init(&hcp->lock); + + ret = snd_pcm_create_iec958_consumer_default(hcp->iec_status, + sizeof(hcp->iec_status)); + if (ret < 0) + return ret; + + daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL); + if (!daidrv) + return -ENOMEM; + + if (hcd->i2s) { + daidrv[i] = hdmi_i2s_dai; + daidrv[i].playback.channels_max = hcd->max_i2s_channels; + if (hcd->no_i2s_playback) + memset(&daidrv[i].playback, 0, + sizeof(daidrv[i].playback)); + if (hcd->no_i2s_capture) + memset(&daidrv[i].capture, 0, + sizeof(daidrv[i].capture)); + i++; + } + + if (hcd->spdif) { + daidrv[i] = hdmi_spdif_dai; + if (hcd->no_spdif_playback) + memset(&daidrv[i].playback, 0, + sizeof(daidrv[i].playback)); + if (hcd->no_spdif_capture) + memset(&daidrv[i].capture, 0, + sizeof(daidrv[i].capture)); + } + + dev_set_drvdata(dev, hcp); + + ret = devm_snd_soc_register_component(dev, &hdmi_driver, daidrv, + dai_count); + if (ret) { + dev_err(dev, "%s: snd_soc_register_component() failed (%d)\n", + __func__, ret); + return ret; + } + return 0; +} + +static struct platform_driver hdmi_codec_driver = { + .driver = { + .name = HDMI_CODEC_DRV_NAME, + }, + .probe = hdmi_codec_probe, +}; + +module_platform_driver(hdmi_codec_driver); + +MODULE_AUTHOR("Jyri Sarha "); +MODULE_DESCRIPTION("HDMI Audio Codec Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME); diff --git a/sound/soc/codecs/ics43432.c b/sound/soc/codecs/ics43432.c new file mode 100644 index 0000000000..58a3822547 --- /dev/null +++ b/sound/soc/codecs/ics43432.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2S MEMS microphone driver for InvenSense ICS-43432 and similar + * MEMS-based microphones. + * + * - Non configurable. + * - I2S interface, 64 BCLs per frame, 32 bits per channel, 24 bit data + * + * Copyright (c) 2015 Axis Communications AB + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ICS43432_RATE_MIN 7190 /* Hz, from data sheet */ +#define ICS43432_RATE_MAX 52800 /* Hz, from data sheet */ + +#define ICS43432_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32) + +static struct snd_soc_dai_driver ics43432_dai = { + .name = "ics43432-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rate_min = ICS43432_RATE_MIN, + .rate_max = ICS43432_RATE_MAX, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = ICS43432_FORMATS, + }, +}; + +static const struct snd_soc_component_driver ics43432_component_driver = { + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int ics43432_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &ics43432_component_driver, + &ics43432_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id ics43432_ids[] = { + { .compatible = "invensense,ics43432", }, + { .compatible = "cui,cmm-4030d-261", }, + { } +}; +MODULE_DEVICE_TABLE(of, ics43432_ids); +#endif + +static struct platform_driver ics43432_driver = { + .driver = { + .name = "ics43432", + .of_match_table = of_match_ptr(ics43432_ids), + }, + .probe = ics43432_probe, +}; + +module_platform_driver(ics43432_driver); + +MODULE_DESCRIPTION("ASoC ICS43432 driver"); +MODULE_AUTHOR("Ricard Wanderlof "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c new file mode 100644 index 0000000000..2cc7b9166e --- /dev/null +++ b/sound/soc/codecs/idt821034.c @@ -0,0 +1,1178 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// IDT821034 ALSA SoC driver +// +// Copyright 2022 CS GROUP France +// +// Author: Herve Codina + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IDT821034_NB_CHANNEL 4 + +struct idt821034_amp { + u16 gain; + bool is_muted; +}; + +struct idt821034 { + struct spi_device *spi; + struct mutex mutex; + u8 spi_tx_buf; /* Cannot use stack area for SPI (dma-safe memory) */ + u8 spi_rx_buf; /* Cannot use stack area for SPI (dma-safe memory) */ + struct { + u8 codec_conf; + struct { + u8 power; + u8 tx_slot; + u8 rx_slot; + u8 slic_conf; + u8 slic_control; + } ch[IDT821034_NB_CHANNEL]; + } cache; + struct { + struct { + struct idt821034_amp amp_out; + struct idt821034_amp amp_in; + } ch[IDT821034_NB_CHANNEL]; + } amps; + int max_ch_playback; + int max_ch_capture; + struct gpio_chip gpio_chip; +}; + +static int idt821034_8bit_write(struct idt821034 *idt821034, u8 val) +{ + struct spi_transfer xfer[] = { + { + .tx_buf = &idt821034->spi_tx_buf, + .len = 1, + }, { + .cs_off = 1, + .tx_buf = &idt821034->spi_tx_buf, + .len = 1, + } + }; + + idt821034->spi_tx_buf = val; + + dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x\n", val); + + return spi_sync_transfer(idt821034->spi, xfer, 2); +} + +static int idt821034_2x8bit_write(struct idt821034 *idt821034, u8 val1, u8 val2) +{ + int ret; + + ret = idt821034_8bit_write(idt821034, val1); + if (ret) + return ret; + return idt821034_8bit_write(idt821034, val2); +} + +static int idt821034_8bit_read(struct idt821034 *idt821034, u8 valw, u8 *valr) +{ + struct spi_transfer xfer[] = { + { + .tx_buf = &idt821034->spi_tx_buf, + .rx_buf = &idt821034->spi_rx_buf, + .len = 1, + }, { + .cs_off = 1, + .tx_buf = &idt821034->spi_tx_buf, + .len = 1, + } + }; + int ret; + + idt821034->spi_tx_buf = valw; + + ret = spi_sync_transfer(idt821034->spi, xfer, 2); + if (ret) + return ret; + + *valr = idt821034->spi_rx_buf; + + dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x, rd 0x%x\n", + valw, *valr); + + return 0; +} + +/* Available mode for the programming sequence */ +#define IDT821034_MODE_CODEC(_ch) (0x80 | ((_ch) << 2)) +#define IDT821034_MODE_SLIC(_ch) (0xD0 | ((_ch) << 2)) +#define IDT821034_MODE_GAIN(_ch) (0xC0 | ((_ch) << 2)) + +/* Power values that can be used in 'power' (can be ORed) */ +#define IDT821034_CONF_PWRUP_TX BIT(1) /* from analog input to PCM */ +#define IDT821034_CONF_PWRUP_RX BIT(0) /* from PCM to analog output */ + +static int idt821034_set_channel_power(struct idt821034 *idt821034, u8 ch, u8 power) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_channel_power(%u, 0x%x)\n", ch, power); + + conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf; + + if (power & IDT821034_CONF_PWRUP_RX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_RX, + idt821034->cache.ch[ch].rx_slot); + if (ret) + return ret; + } + if (power & IDT821034_CONF_PWRUP_TX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_TX, + idt821034->cache.ch[ch].tx_slot); + if (ret) + return ret; + } + if (!(power & (IDT821034_CONF_PWRUP_TX | IDT821034_CONF_PWRUP_RX))) { + ret = idt821034_2x8bit_write(idt821034, conf, 0); + if (ret) + return ret; + } + + idt821034->cache.ch[ch].power = power; + + return 0; +} + +static u8 idt821034_get_channel_power(struct idt821034 *idt821034, u8 ch) +{ + return idt821034->cache.ch[ch].power; +} + +/* Codec configuration values that can be used in 'codec_conf' (can be ORed) */ +#define IDT821034_CONF_ALAW_MODE BIT(5) +#define IDT821034_CONF_DELAY_MODE BIT(4) + +static int idt821034_set_codec_conf(struct idt821034 *idt821034, u8 codec_conf) +{ + u8 conf; + u8 ts; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_codec_conf(0x%x)\n", codec_conf); + + /* codec conf fields are common to all channel. + * Arbitrary use of channel 0 for this configuration. + */ + + /* Set Configuration Register */ + conf = IDT821034_MODE_CODEC(0) | codec_conf; + + /* Update conf value and timeslot register value according + * to cache values + */ + if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_RX) { + conf |= IDT821034_CONF_PWRUP_RX; + ts = idt821034->cache.ch[0].rx_slot; + } else if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_TX) { + conf |= IDT821034_CONF_PWRUP_TX; + ts = idt821034->cache.ch[0].tx_slot; + } else { + ts = 0x00; + } + + /* Write configuration register and time-slot register */ + ret = idt821034_2x8bit_write(idt821034, conf, ts); + if (ret) + return ret; + + idt821034->cache.codec_conf = codec_conf; + return 0; +} + +static u8 idt821034_get_codec_conf(struct idt821034 *idt821034) +{ + return idt821034->cache.codec_conf; +} + +/* Channel direction values that can be used in 'ch_dir' (can be ORed) */ +#define IDT821034_CH_RX BIT(0) /* from PCM to analog output */ +#define IDT821034_CH_TX BIT(1) /* from analog input to PCM */ + +static int idt821034_set_channel_ts(struct idt821034 *idt821034, u8 ch, u8 ch_dir, u8 ts_num) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_channel_ts(%u, 0x%x, %d)\n", ch, ch_dir, ts_num); + + conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf; + + if (ch_dir & IDT821034_CH_RX) { + if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_RX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_RX, + ts_num); + if (ret) + return ret; + } + idt821034->cache.ch[ch].rx_slot = ts_num; + } + if (ch_dir & IDT821034_CH_TX) { + if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_TX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_TX, + ts_num); + if (ret) + return ret; + } + idt821034->cache.ch[ch].tx_slot = ts_num; + } + + return 0; +} + +/* SLIC direction values that can be used in 'slic_dir' (can be ORed) */ +#define IDT821034_SLIC_IO1_IN BIT(1) +#define IDT821034_SLIC_IO0_IN BIT(0) + +static int idt821034_set_slic_conf(struct idt821034 *idt821034, u8 ch, u8 slic_dir) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_slic_conf(%u, 0x%x)\n", ch, slic_dir); + + conf = IDT821034_MODE_SLIC(ch) | slic_dir; + ret = idt821034_2x8bit_write(idt821034, conf, idt821034->cache.ch[ch].slic_control); + if (ret) + return ret; + + idt821034->cache.ch[ch].slic_conf = slic_dir; + + return 0; +} + +static u8 idt821034_get_slic_conf(struct idt821034 *idt821034, u8 ch) +{ + return idt821034->cache.ch[ch].slic_conf; +} + +static int idt821034_write_slic_raw(struct idt821034 *idt821034, u8 ch, u8 slic_raw) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "write_slic_raw(%u, 0x%x)\n", ch, slic_raw); + + /* + * On write, slic_raw is mapped as follow : + * b4: O_4 + * b3: O_3 + * b2: O_2 + * b1: I/O_1 + * b0: I/O_0 + */ + + conf = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf; + ret = idt821034_2x8bit_write(idt821034, conf, slic_raw); + if (ret) + return ret; + + idt821034->cache.ch[ch].slic_control = slic_raw; + return 0; +} + +static u8 idt821034_get_written_slic_raw(struct idt821034 *idt821034, u8 ch) +{ + return idt821034->cache.ch[ch].slic_control; +} + +static int idt821034_read_slic_raw(struct idt821034 *idt821034, u8 ch, u8 *slic_raw) +{ + u8 val; + int ret; + + /* + * On read, slic_raw is mapped as follow : + * b7: I/O_0 + * b6: I/O_1 + * b5: O_2 + * b4: O_3 + * b3: O_4 + * b2: I/O1_0, I/O_0 from channel 1 (no matter ch value) + * b1: I/O2_0, I/O_0 from channel 2 (no matter ch value) + * b2: I/O3_0, I/O_0 from channel 3 (no matter ch value) + */ + + val = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf; + ret = idt821034_8bit_write(idt821034, val); + if (ret) + return ret; + + ret = idt821034_8bit_read(idt821034, idt821034->cache.ch[ch].slic_control, slic_raw); + if (ret) + return ret; + + dev_dbg(&idt821034->spi->dev, "read_slic_raw(%i) 0x%x\n", ch, *slic_raw); + + return 0; +} + +/* Gain type values that can be used in 'gain_type' (cannot be ORed) */ +#define IDT821034_GAIN_RX (0 << 1) /* from PCM to analog output */ +#define IDT821034_GAIN_TX (1 << 1) /* from analog input to PCM */ + +static int idt821034_set_gain_channel(struct idt821034 *idt821034, u8 ch, + u8 gain_type, u16 gain_val) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_gain_channel(%u, 0x%x, 0x%x-%d)\n", + ch, gain_type, gain_val, gain_val); + + /* + * The gain programming coefficients should be calculated as: + * Transmit : Coeff_X = round [ gain_X0dB × gain_X ] + * Receive: Coeff_R = round [ gain_R0dB × gain_R ] + * where: + * gain_X0dB = 1820; + * gain_X is the target gain; + * Coeff_X should be in the range of 0 to 8192. + * gain_R0dB = 2506; + * gain_R is the target gain; + * Coeff_R should be in the range of 0 to 8192. + * + * A gain programming coefficient is 14-bit wide and in binary format. + * The 7 Most Significant Bits of the coefficient is called + * GA_MSB_Transmit for transmit path, or is called GA_MSB_Receive for + * receive path; The 7 Least Significant Bits of the coefficient is + * called GA_LSB_ Transmit for transmit path, or is called + * GA_LSB_Receive for receive path. + * + * An example is given below to clarify the calculation of the + * coefficient. To program a +3 dB gain in transmit path and a -3.5 dB + * gain in receive path: + * + * Linear Code of +3dB = 10^(3/20)= 1.412537545 + * Coeff_X = round (1820 × 1.412537545) = 2571 + * = 0b001010_00001011 + * GA_MSB_Transmit = 0b0010100 + * GA_LSB_Transmit = 0b0001011 + * + * Linear Code of -3.5dB = 10^(-3.5/20) = 0.668343917 + * Coeff_R= round (2506 × 0.668343917) = 1675 + * = 0b0001101_0001011 + * GA_MSB_Receive = 0b0001101 + * GA_LSB_Receive = 0b0001011 + */ + + conf = IDT821034_MODE_GAIN(ch) | gain_type; + + ret = idt821034_2x8bit_write(idt821034, conf | 0x00, gain_val & 0x007F); + if (ret) + return ret; + + ret = idt821034_2x8bit_write(idt821034, conf | 0x01, (gain_val >> 7) & 0x7F); + if (ret) + return ret; + + return 0; +} + +/* Id helpers used in controls and dapm */ +#define IDT821034_DIR_OUT (1 << 3) +#define IDT821034_DIR_IN (0 << 3) +#define IDT821034_ID(_ch, _dir) (((_ch) & 0x03) | (_dir)) +#define IDT821034_ID_OUT(_ch) IDT821034_ID(_ch, IDT821034_DIR_OUT) +#define IDT821034_ID_IN(_ch) IDT821034_ID(_ch, IDT821034_DIR_IN) + +#define IDT821034_ID_GET_CHAN(_id) ((_id) & 0x03) +#define IDT821034_ID_GET_DIR(_id) ((_id) & (1 << 3)) +#define IDT821034_ID_IS_OUT(_id) (IDT821034_ID_GET_DIR(_id) == IDT821034_DIR_OUT) + +static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + int val; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(mc->reg); + + mutex_lock(&idt821034->mutex); + if (IDT821034_ID_IS_OUT(mc->reg)) + val = idt821034->amps.ch[ch].amp_out.gain; + else + val = idt821034->amps.ch[ch].amp_in.gain; + mutex_unlock(&idt821034->mutex); + + ucontrol->value.integer.value[0] = val & mask; + if (invert) + ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; + else + ucontrol->value.integer.value[0] = ucontrol->value.integer.value[0] - min; + + return 0; +} + +static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + struct idt821034_amp *amp; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + int ret; + u8 gain_type; + u8 ch; + + val = ucontrol->value.integer.value[0]; + if (val > max - min) + return -EINVAL; + + if (invert) + val = (max - val) & mask; + else + val = (val + min) & mask; + + ch = IDT821034_ID_GET_CHAN(mc->reg); + + mutex_lock(&idt821034->mutex); + + if (IDT821034_ID_IS_OUT(mc->reg)) { + amp = &idt821034->amps.ch[ch].amp_out; + gain_type = IDT821034_GAIN_RX; + } else { + amp = &idt821034->amps.ch[ch].amp_in; + gain_type = IDT821034_GAIN_TX; + } + + if (amp->gain == val) { + ret = 0; + goto end; + } + + if (!amp->is_muted) { + ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val); + if (ret) + goto end; + } + + amp->gain = val; + ret = 1; /* The value changed */ +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int id = kcontrol->private_value; + bool is_muted; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + + mutex_lock(&idt821034->mutex); + is_muted = IDT821034_ID_IS_OUT(id) ? + idt821034->amps.ch[ch].amp_out.is_muted : + idt821034->amps.ch[ch].amp_in.is_muted; + mutex_unlock(&idt821034->mutex); + + ucontrol->value.integer.value[0] = !is_muted; + + return 0; +} + +static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int id = kcontrol->private_value; + struct idt821034_amp *amp; + bool is_mute; + u8 gain_type; + int ret; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + is_mute = !ucontrol->value.integer.value[0]; + + mutex_lock(&idt821034->mutex); + + if (IDT821034_ID_IS_OUT(id)) { + amp = &idt821034->amps.ch[ch].amp_out; + gain_type = IDT821034_GAIN_RX; + } else { + amp = &idt821034->amps.ch[ch].amp_in; + gain_type = IDT821034_GAIN_TX; + } + + if (amp->is_muted == is_mute) { + ret = 0; + goto end; + } + + ret = idt821034_set_gain_channel(idt821034, ch, gain_type, + is_mute ? 0 : amp->gain); + if (ret) + goto end; + + amp->is_muted = is_mute; + ret = 1; /* The value changed */ +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -6520, 1306); +#define IDT821034_GAIN_IN_MIN_RAW 1 /* -65.20 dB -> 10^(-65.2/20.0) * 1820 = 1 */ +#define IDT821034_GAIN_IN_MAX_RAW 8191 /* 13.06 dB -> 10^(13.06/20.0) * 1820 = 8191 */ +#define IDT821034_GAIN_IN_INIT_RAW 1820 /* 0dB -> 10^(0/20) * 1820 = 1820 */ + +static const DECLARE_TLV_DB_LINEAR(idt821034_gain_out, -6798, 1029); +#define IDT821034_GAIN_OUT_MIN_RAW 1 /* -67.98 dB -> 10^(-67.98/20.0) * 2506 = 1*/ +#define IDT821034_GAIN_OUT_MAX_RAW 8191 /* 10.29 dB -> 10^(10.29/20.0) * 2506 = 8191 */ +#define IDT821034_GAIN_OUT_INIT_RAW 2506 /* 0dB -> 10^(0/20) * 2506 = 2506 */ + +static const struct snd_kcontrol_new idt821034_controls[] = { + /* DAC volume control */ + SOC_SINGLE_RANGE_EXT_TLV("DAC0 Playback Volume", IDT821034_ID_OUT(0), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + SOC_SINGLE_RANGE_EXT_TLV("DAC1 Playback Volume", IDT821034_ID_OUT(1), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + SOC_SINGLE_RANGE_EXT_TLV("DAC2 Playback Volume", IDT821034_ID_OUT(2), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + SOC_SINGLE_RANGE_EXT_TLV("DAC3 Playback Volume", IDT821034_ID_OUT(3), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + + /* DAC mute control */ + SOC_SINGLE_BOOL_EXT("DAC0 Playback Switch", IDT821034_ID_OUT(0), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("DAC1 Playback Switch", IDT821034_ID_OUT(1), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("DAC2 Playback Switch", IDT821034_ID_OUT(2), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("DAC3 Playback Switch", IDT821034_ID_OUT(3), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + + /* ADC volume control */ + SOC_SINGLE_RANGE_EXT_TLV("ADC0 Capture Volume", IDT821034_ID_IN(0), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + SOC_SINGLE_RANGE_EXT_TLV("ADC1 Capture Volume", IDT821034_ID_IN(1), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + SOC_SINGLE_RANGE_EXT_TLV("ADC2 Capture Volume", IDT821034_ID_IN(2), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + SOC_SINGLE_RANGE_EXT_TLV("ADC3 Capture Volume", IDT821034_ID_IN(3), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + + /* ADC mute control */ + SOC_SINGLE_BOOL_EXT("ADC0 Capture Switch", IDT821034_ID_IN(0), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("ADC1 Capture Switch", IDT821034_ID_IN(1), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("ADC2 Capture Switch", IDT821034_ID_IN(2), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("ADC3 Capture Switch", IDT821034_ID_IN(3), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), +}; + +static int idt821034_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + unsigned int id = w->shift; + u8 power, mask; + int ret; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX; + + mutex_lock(&idt821034->mutex); + + power = idt821034_get_channel_power(idt821034, ch); + if (SND_SOC_DAPM_EVENT_ON(event)) + power |= mask; + else + power &= ~mask; + ret = idt821034_set_channel_power(idt821034, ch, power); + + mutex_unlock(&idt821034->mutex); + + return ret; +} + +static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("DAC0", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(0), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC1", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(1), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC2", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(2), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC3", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(3), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("OUT0"), + SND_SOC_DAPM_OUTPUT("OUT1"), + SND_SOC_DAPM_OUTPUT("OUT2"), + SND_SOC_DAPM_OUTPUT("OUT3"), + + SND_SOC_DAPM_DAC_E("ADC0", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(0), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("ADC1", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(1), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("ADC2", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(2), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("ADC3", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(3), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("IN0"), + SND_SOC_DAPM_INPUT("IN1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_INPUT("IN3"), +}; + +static const struct snd_soc_dapm_route idt821034_dapm_routes[] = { + { "OUT0", NULL, "DAC0" }, + { "OUT1", NULL, "DAC1" }, + { "OUT2", NULL, "DAC2" }, + { "OUT3", NULL, "DAC3" }, + + { "ADC0", NULL, "IN0" }, + { "ADC1", NULL, "IN1" }, + { "ADC2", NULL, "IN2" }, + { "ADC3", NULL, "IN3" }, +}; + +static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int width) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + unsigned int mask; + u8 slot; + int ret; + u8 ch; + + switch (width) { + case 0: /* Not set -> default 8 */ + case 8: + break; + default: + dev_err(dai->dev, "tdm slot width %d not supported\n", width); + return -EINVAL; + } + + mask = tx_mask; + slot = 0; + ch = 0; + while (mask && ch < IDT821034_NB_CHANNEL) { + if (mask & 0x1) { + mutex_lock(&idt821034->mutex); + ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot); + mutex_unlock(&idt821034->mutex); + if (ret) { + dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n", + ch, ret); + return ret; + } + ch++; + } + mask >>= 1; + slot++; + } + if (mask) { + dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n", + tx_mask, IDT821034_NB_CHANNEL); + return -EINVAL; + } + idt821034->max_ch_playback = ch; + + mask = rx_mask; + slot = 0; + ch = 0; + while (mask && ch < IDT821034_NB_CHANNEL) { + if (mask & 0x1) { + mutex_lock(&idt821034->mutex); + ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot); + mutex_unlock(&idt821034->mutex); + if (ret) { + dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n", + ch, ret); + return ret; + } + ch++; + } + mask >>= 1; + slot++; + } + if (mask) { + dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n", + rx_mask, IDT821034_NB_CHANNEL); + return -EINVAL; + } + idt821034->max_ch_capture = ch; + + return 0; +} + +static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + u8 conf; + int ret; + + mutex_lock(&idt821034->mutex); + + conf = idt821034_get_codec_conf(idt821034); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + conf |= IDT821034_CONF_DELAY_MODE; + break; + case SND_SOC_DAIFMT_DSP_B: + conf &= ~IDT821034_CONF_DELAY_MODE; + break; + default: + dev_err(dai->dev, "Unsupported DAI format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + ret = -EINVAL; + goto end; + } + ret = idt821034_set_codec_conf(idt821034, conf); +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + u8 conf; + int ret; + + mutex_lock(&idt821034->mutex); + + conf = idt821034_get_codec_conf(idt821034); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_A_LAW: + conf |= IDT821034_CONF_ALAW_MODE; + break; + case SNDRV_PCM_FORMAT_MU_LAW: + conf &= ~IDT821034_CONF_ALAW_MODE; + break; + default: + dev_err(dai->dev, "Unsupported PCM format 0x%x\n", + params_format(params)); + ret = -EINVAL; + goto end; + } + ret = idt821034_set_codec_conf(idt821034, conf); +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static const unsigned int idt821034_sample_bits[] = {8}; + +static struct snd_pcm_hw_constraint_list idt821034_sample_bits_constr = { + .list = idt821034_sample_bits, + .count = ARRAY_SIZE(idt821034_sample_bits), +}; + +static int idt821034_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + unsigned int max_ch = 0; + int ret; + + max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + idt821034->max_ch_playback : idt821034->max_ch_capture; + + /* + * Disable stream support (min = 0, max = 0) if no timeslots were + * configured otherwise, limit the number of channels to those + * configured. + */ + ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, + max_ch ? 1 : 0, max_ch); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &idt821034_sample_bits_constr); + if (ret) + return ret; + + return 0; +} + +static u64 idt821034_dai_formats[] = { + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops idt821034_dai_ops = { + .startup = idt821034_dai_startup, + .hw_params = idt821034_dai_hw_params, + .set_tdm_slot = idt821034_dai_set_tdm_slot, + .set_fmt = idt821034_dai_set_fmt, + .auto_selectable_formats = idt821034_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(idt821034_dai_formats), +}; + +static struct snd_soc_dai_driver idt821034_dai_driver = { + .name = "idt821034", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = IDT821034_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = IDT821034_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + }, + .ops = &idt821034_dai_ops, +}; + +static int idt821034_reset_audio(struct idt821034 *idt821034) +{ + int ret; + u8 i; + + mutex_lock(&idt821034->mutex); + + ret = idt821034_set_codec_conf(idt821034, 0); + if (ret) + goto end; + + for (i = 0; i < IDT821034_NB_CHANNEL; i++) { + idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW; + idt821034->amps.ch[i].amp_out.is_muted = false; + ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX, + idt821034->amps.ch[i].amp_out.gain); + if (ret) + goto end; + + idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW; + idt821034->amps.ch[i].amp_in.is_muted = false; + ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX, + idt821034->amps.ch[i].amp_in.gain); + if (ret) + goto end; + + ret = idt821034_set_channel_power(idt821034, i, 0); + if (ret) + goto end; + } + + ret = 0; +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_component_probe(struct snd_soc_component *component) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int ret; + + /* reset idt821034 audio part*/ + ret = idt821034_reset_audio(idt821034); + if (ret) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver idt821034_component_driver = { + .probe = idt821034_component_probe, + .controls = idt821034_controls, + .num_controls = ARRAY_SIZE(idt821034_controls), + .dapm_widgets = idt821034_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(idt821034_dapm_widgets), + .dapm_routes = idt821034_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(idt821034_dapm_routes), + .endianness = 1, +}; + +#define IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(_offset) (((_offset) / 5) % 4) +#define IDT821034_GPIO_OFFSET_TO_SLIC_MASK(_offset) BIT((_offset) % 5) + +static void idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_raw; + int ret; + + mutex_lock(&idt821034->mutex); + + slic_raw = idt821034_get_written_slic_raw(idt821034, ch); + if (val) + slic_raw |= mask; + else + slic_raw &= ~mask; + ret = idt821034_write_slic_raw(idt821034, ch, slic_raw); + if (ret) { + dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + } + + mutex_unlock(&idt821034->mutex); +} + +static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_raw; + int ret; + + mutex_lock(&idt821034->mutex); + ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); + mutex_unlock(&idt821034->mutex); + if (ret) { + dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + return ret; + } + + /* + * SLIC IOs are read in reverse order compared to write. + * Reverse the read value here in order to have IO0 at lsb (ie same + * order as write) + */ + return !!(bitrev8(slic_raw) & mask); +} + +static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_dir; + + mutex_lock(&idt821034->mutex); + slic_dir = idt821034_get_slic_conf(idt821034, ch); + mutex_unlock(&idt821034->mutex); + + return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; +} + +static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offset) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_conf; + int ret; + + /* Only IO0 and IO1 can be set as input */ + if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN)) + return -EPERM; + + mutex_lock(&idt821034->mutex); + + slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask; + + ret = idt821034_set_slic_conf(idt821034, ch, slic_conf); + if (ret) { + dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + } + + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_conf; + int ret; + + idt821034_chip_gpio_set(c, offset, val); + + mutex_lock(&idt821034->mutex); + + slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask; + + ret = idt821034_set_slic_conf(idt821034, ch, slic_conf); + if (ret) { + dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + } + + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_reset_gpio(struct idt821034 *idt821034) +{ + int ret; + u8 i; + + mutex_lock(&idt821034->mutex); + + /* IO0 and IO1 as input for all channels and output IO set to 0 */ + for (i = 0; i < IDT821034_NB_CHANNEL; i++) { + ret = idt821034_set_slic_conf(idt821034, i, + IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN); + if (ret) + goto end; + + ret = idt821034_write_slic_raw(idt821034, i, 0); + if (ret) + goto end; + + } + ret = 0; +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_gpio_init(struct idt821034 *idt821034) +{ + int ret; + + ret = idt821034_reset_gpio(idt821034); + if (ret) + return ret; + + idt821034->gpio_chip.owner = THIS_MODULE; + idt821034->gpio_chip.label = dev_name(&idt821034->spi->dev); + idt821034->gpio_chip.parent = &idt821034->spi->dev; + idt821034->gpio_chip.base = -1; + idt821034->gpio_chip.ngpio = 5 * 4; /* 5 GPIOs on 4 channels */ + idt821034->gpio_chip.get_direction = idt821034_chip_get_direction; + idt821034->gpio_chip.direction_input = idt821034_chip_direction_input; + idt821034->gpio_chip.direction_output = idt821034_chip_direction_output; + idt821034->gpio_chip.get = idt821034_chip_gpio_get; + idt821034->gpio_chip.set = idt821034_chip_gpio_set; + idt821034->gpio_chip.can_sleep = true; + + return devm_gpiochip_add_data(&idt821034->spi->dev, &idt821034->gpio_chip, + idt821034); +} + +static int idt821034_spi_probe(struct spi_device *spi) +{ + struct idt821034 *idt821034; + int ret; + + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + idt821034 = devm_kzalloc(&spi->dev, sizeof(*idt821034), GFP_KERNEL); + if (!idt821034) + return -ENOMEM; + + idt821034->spi = spi; + + mutex_init(&idt821034->mutex); + + spi_set_drvdata(spi, idt821034); + + ret = devm_snd_soc_register_component(&spi->dev, &idt821034_component_driver, + &idt821034_dai_driver, 1); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_GPIOLIB)) + return idt821034_gpio_init(idt821034); + + return 0; +} + +static const struct of_device_id idt821034_of_match[] = { + { .compatible = "renesas,idt821034", }, + { } +}; +MODULE_DEVICE_TABLE(of, idt821034_of_match); + +static const struct spi_device_id idt821034_id_table[] = { + { "idt821034", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, idt821034_id_table); + +static struct spi_driver idt821034_spi_driver = { + .driver = { + .name = "idt821034", + .of_match_table = idt821034_of_match, + }, + .id_table = idt821034_id_table, + .probe = idt821034_spi_probe, +}; + +module_spi_driver(idt821034_spi_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("IDT821034 ALSA SoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/inno_rk3036.c b/sound/soc/codecs/inno_rk3036.c new file mode 100644 index 0000000000..11320423c6 --- /dev/null +++ b/sound/soc/codecs/inno_rk3036.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver of Inno codec for rk3036 by Rockchip Inc. + * + * Author: Rockchip Inc. + * Author: Zheng ShunQian + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inno_rk3036.h" + +struct rk3036_codec_priv { + void __iomem *base; + struct clk *pclk; + struct regmap *regmap; + struct device *dev; +}; + +static const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0); + +static int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int val, regval; + + regval = snd_soc_component_read(component, INNO_R09); + val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) & + INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON; + ucontrol->value.integer.value[0] = val; + + val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) & + INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON; + ucontrol->value.integer.value[1] = val; + + return 0; +} + +static int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + int val, ret, regmsk; + + val = (ucontrol->value.integer.value[0] ? + INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) << + INNO_R09_HPL_ANITPOP_SHIFT; + val |= (ucontrol->value.integer.value[1] ? + INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) << + INNO_R09_HPR_ANITPOP_SHIFT; + + regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT | + INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT; + + ret = snd_soc_component_update_bits(component, INNO_R09, + regmsk, val); + if (ret < 0) + return ret; + + return 0; +} + +#define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \ + .put = rk3036_codec_antipop_put, } + +static const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = { + SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08, + INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB, + INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv), + SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT, + INNO_R06_VOUTR_CZ_SHIFT, 1, 0), + SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT, + INNO_R09_HPR_MUTE_SHIFT, 1, 0), + SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"), +}; + +static const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = { + SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09, + INNO_R09_DACL_SWITCH_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = { + SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09, + INNO_R09_DACR_SWITCH_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = { + SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05, + INNO_R05_HPL_WORK_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = { + SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05, + INNO_R05_HPR_WORK_SHIFT, 1, 0), +}; + +static const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06, + INNO_R06_DAC_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04, + INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04, + INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06, + INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06, + INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04, + INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04, + INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04, + INNO_R04_DACL_SW_SHIFT, 0), + SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04, + INNO_R04_DACR_SW_SHIFT, 0), + + SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0, + rk3036_codec_hpl_mixer_controls, + ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0, + rk3036_codec_hpr_mixer_controls, + ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)), + + SND_SOC_DAPM_PGA("HP Left Out", INNO_R05, + INNO_R05_HPL_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Right Out", INNO_R05, + INNO_R05_HPR_EN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("HP Left Switch", SND_SOC_NOPM, 0, 0, + rk3036_codec_hpl_switch_controls, + ARRAY_SIZE(rk3036_codec_hpl_switch_controls)), + SND_SOC_DAPM_MIXER("HP Right Switch", SND_SOC_NOPM, 0, 0, + rk3036_codec_hpr_switch_controls, + ARRAY_SIZE(rk3036_codec_hpr_switch_controls)), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), +}; + +static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = { + {"DACL VREF", NULL, "DAC PWR"}, + {"DACR VREF", NULL, "DAC PWR"}, + {"DACL HiLo VREF", NULL, "DAC PWR"}, + {"DACR HiLo VREF", NULL, "DAC PWR"}, + {"DACL CLK", NULL, "DAC PWR"}, + {"DACR CLK", NULL, "DAC PWR"}, + + {"DACL", NULL, "DACL VREF"}, + {"DACL", NULL, "DACL HiLo VREF"}, + {"DACL", NULL, "DACL CLK"}, + {"DACR", NULL, "DACR VREF"}, + {"DACR", NULL, "DACR HiLo VREF"}, + {"DACR", NULL, "DACR CLK"}, + + {"Left Headphone Mixer", "DAC Left Out Switch", "DACL"}, + {"Right Headphone Mixer", "DAC Right Out Switch", "DACR"}, + {"HP Left Out", NULL, "Left Headphone Mixer"}, + {"HP Right Out", NULL, "Right Headphone Mixer"}, + + {"HP Left Switch", "HP Left Out Switch", "HP Left Out"}, + {"HP Right Switch", "HP Right Out Switch", "HP Right Out"}, + + {"HPL", NULL, "HP Left Switch"}, + {"HPR", NULL, "HP Right Switch"}, +}; + +static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + unsigned int reg01_val = 0, reg02_val = 0, reg03_val = 0; + + dev_dbg(component->dev, "rk3036_codec dai set fmt : %08x\n", fmt); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + reg01_val |= INNO_R01_PINDIR_IN_SLAVE | + INNO_R01_I2SMODE_SLAVE; + break; + case SND_SOC_DAIFMT_CBP_CFP: + reg01_val |= INNO_R01_PINDIR_OUT_MASTER | + INNO_R01_I2SMODE_MASTER; + break; + default: + dev_err(component->dev, "invalid fmt\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + reg02_val |= INNO_R02_DACM_PCM; + break; + case SND_SOC_DAIFMT_I2S: + reg02_val |= INNO_R02_DACM_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + reg02_val |= INNO_R02_DACM_RJM; + break; + case SND_SOC_DAIFMT_LEFT_J: + reg02_val |= INNO_R02_DACM_LJM; + break; + default: + dev_err(component->dev, "set dai format failed\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + reg02_val |= INNO_R02_LRCP_NORMAL; + reg03_val |= INNO_R03_BCP_NORMAL; + break; + case SND_SOC_DAIFMT_IB_IF: + reg02_val |= INNO_R02_LRCP_REVERSAL; + reg03_val |= INNO_R03_BCP_REVERSAL; + break; + case SND_SOC_DAIFMT_IB_NF: + reg02_val |= INNO_R02_LRCP_REVERSAL; + reg03_val |= INNO_R03_BCP_NORMAL; + break; + case SND_SOC_DAIFMT_NB_IF: + reg02_val |= INNO_R02_LRCP_NORMAL; + reg03_val |= INNO_R03_BCP_REVERSAL; + break; + default: + dev_err(component->dev, "set dai format failed\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, INNO_R01, INNO_R01_I2SMODE_MSK | + INNO_R01_PINDIR_MSK, reg01_val); + snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK | + INNO_R02_DACM_MSK, reg02_val); + snd_soc_component_update_bits(component, INNO_R03, INNO_R03_BCP_MSK, reg03_val); + + return 0; +} + +static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned int reg02_val = 0, reg03_val = 0; + + switch (params_format(hw_params)) { + case SNDRV_PCM_FORMAT_S16_LE: + reg02_val |= INNO_R02_VWL_16BIT; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + reg02_val |= INNO_R02_VWL_20BIT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + reg02_val |= INNO_R02_VWL_24BIT; + break; + case SNDRV_PCM_FORMAT_S32_LE: + reg02_val |= INNO_R02_VWL_32BIT; + break; + default: + return -EINVAL; + } + + reg02_val |= INNO_R02_LRCP_NORMAL; + reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK; + + snd_soc_component_update_bits(component, INNO_R02, INNO_R02_LRCP_MSK | + INNO_R02_VWL_MSK, reg02_val); + snd_soc_component_update_bits(component, INNO_R03, INNO_R03_DACR_MSK | + INNO_R03_FWL_MSK, reg03_val); + return 0; +} + +#define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000) + +#define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops rk3036_codec_dai_ops = { + .set_fmt = rk3036_codec_dai_set_fmt, + .hw_params = rk3036_codec_dai_hw_params, +}; + +static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = { + { + .name = "rk3036-codec-dai", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RK3036_CODEC_RATES, + .formats = RK3036_CODEC_FMTS, + }, + .ops = &rk3036_codec_dai_ops, + .symmetric_rate = 1, + }, +}; + +static void rk3036_codec_reset(struct snd_soc_component *component) +{ + snd_soc_component_write(component, INNO_R00, + INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET); + snd_soc_component_write(component, INNO_R00, + INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK); +} + +static int rk3036_codec_probe(struct snd_soc_component *component) +{ + rk3036_codec_reset(component); + return 0; +} + +static void rk3036_codec_remove(struct snd_soc_component *component) +{ + rk3036_codec_reset(component); +} + +static int rk3036_codec_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_STANDBY: + /* set a big current for capacitor charging. */ + snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR); + /* start precharge */ + snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_PRECHARGE); + + break; + + case SND_SOC_BIAS_OFF: + /* set a big current for capacitor discharging. */ + snd_soc_component_write(component, INNO_R10, INNO_R10_MAX_CUR); + /* start discharge. */ + snd_soc_component_write(component, INNO_R06, INNO_R06_DAC_DISCHARGE); + + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_component_driver rk3036_codec_driver = { + .probe = rk3036_codec_probe, + .remove = rk3036_codec_remove, + .set_bias_level = rk3036_codec_set_bias_level, + .controls = rk3036_codec_dapm_controls, + .num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls), + .dapm_routes = rk3036_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes), + .dapm_widgets = rk3036_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rk3036_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +}; + +#define GRF_SOC_CON0 0x00140 +#define GRF_ACODEC_SEL (BIT(10) | BIT(16 + 10)) + +static int rk3036_codec_platform_probe(struct platform_device *pdev) +{ + struct rk3036_codec_priv *priv; + struct device_node *of_node = pdev->dev.of_node; + void __iomem *base; + struct regmap *grf; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->base = base; + priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base, + &rk3036_codec_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&pdev->dev, "init regmap failed\n"); + return PTR_ERR(priv->regmap); + } + + grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf"); + if (IS_ERR(grf)) { + dev_err(&pdev->dev, "needs 'rockchip,grf' property\n"); + return PTR_ERR(grf); + } + ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL); + if (ret) { + dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret); + return ret; + } + + priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk"); + if (IS_ERR(priv->pclk)) + return PTR_ERR(priv->pclk); + + ret = clk_prepare_enable(priv->pclk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable clk\n"); + return ret; + } + + priv->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, priv); + + ret = devm_snd_soc_register_component(&pdev->dev, &rk3036_codec_driver, + rk3036_codec_dai_driver, + ARRAY_SIZE(rk3036_codec_dai_driver)); + if (ret) { + clk_disable_unprepare(priv->pclk); + dev_set_drvdata(&pdev->dev, NULL); + } + + return ret; +} + +static void rk3036_codec_platform_remove(struct platform_device *pdev) +{ + struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(priv->pclk); +} + +static const struct of_device_id rk3036_codec_of_match[] __maybe_unused = { + { .compatible = "rockchip,rk3036-codec", }, + {} +}; +MODULE_DEVICE_TABLE(of, rk3036_codec_of_match); + +static struct platform_driver rk3036_codec_platform_driver = { + .driver = { + .name = "rk3036-codec-platform", + .of_match_table = of_match_ptr(rk3036_codec_of_match), + }, + .probe = rk3036_codec_platform_probe, + .remove_new = rk3036_codec_platform_remove, +}; + +module_platform_driver(rk3036_codec_platform_driver); + +MODULE_AUTHOR("Rockchip Inc."); +MODULE_DESCRIPTION("Rockchip rk3036 codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/inno_rk3036.h b/sound/soc/codecs/inno_rk3036.h new file mode 100644 index 0000000000..44bb240419 --- /dev/null +++ b/sound/soc/codecs/inno_rk3036.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver of Inno Codec for rk3036 by Rockchip Inc. + * + * Author: Zheng ShunQian + */ + +#ifndef _INNO_RK3036_CODEC_H +#define _INNO_RK3036_CODEC_H + +/* codec registers */ +#define INNO_R00 0x00 +#define INNO_R01 0x0c +#define INNO_R02 0x10 +#define INNO_R03 0x14 +#define INNO_R04 0x88 +#define INNO_R05 0x8c +#define INNO_R06 0x90 +#define INNO_R07 0x94 +#define INNO_R08 0x98 +#define INNO_R09 0x9c +#define INNO_R10 0xa0 + +/* register bit filed */ +#define INNO_R00_CSR_RESET (0x0 << 0) /*codec system reset*/ +#define INNO_R00_CSR_WORK (0x1 << 0) +#define INNO_R00_CDCR_RESET (0x0 << 1) /*codec digital core reset*/ +#define INNO_R00_CDCR_WORK (0x1 << 1) +#define INNO_R00_PRB_DISABLE (0x0 << 6) /*power reset bypass*/ +#define INNO_R00_PRB_ENABLE (0x1 << 6) + +#define INNO_R01_I2SMODE_MSK (0x1 << 4) +#define INNO_R01_I2SMODE_SLAVE (0x0 << 4) +#define INNO_R01_I2SMODE_MASTER (0x1 << 4) +#define INNO_R01_PINDIR_MSK (0x1 << 5) +#define INNO_R01_PINDIR_IN_SLAVE (0x0 << 5) /*direction of pin*/ +#define INNO_R01_PINDIR_OUT_MASTER (0x1 << 5) + +#define INNO_R02_LRS_MSK (0x1 << 2) +#define INNO_R02_LRS_NORMAL (0x0 << 2) /*DAC Left Right Swap*/ +#define INNO_R02_LRS_SWAP (0x1 << 2) +#define INNO_R02_DACM_MSK (0x3 << 3) +#define INNO_R02_DACM_PCM (0x3 << 3) /*DAC Mode*/ +#define INNO_R02_DACM_I2S (0x2 << 3) +#define INNO_R02_DACM_LJM (0x1 << 3) +#define INNO_R02_DACM_RJM (0x0 << 3) +#define INNO_R02_VWL_MSK (0x3 << 5) +#define INNO_R02_VWL_32BIT (0x3 << 5) /*1/2Frame Valid Word Len*/ +#define INNO_R02_VWL_24BIT (0x2 << 5) +#define INNO_R02_VWL_20BIT (0x1 << 5) +#define INNO_R02_VWL_16BIT (0x0 << 5) +#define INNO_R02_LRCP_MSK (0x1 << 7) +#define INNO_R02_LRCP_NORMAL (0x0 << 7) /*Left Right Polarity*/ +#define INNO_R02_LRCP_REVERSAL (0x1 << 7) + +#define INNO_R03_BCP_MSK (0x1 << 0) +#define INNO_R03_BCP_NORMAL (0x0 << 0) /*DAC bit clock polarity*/ +#define INNO_R03_BCP_REVERSAL (0x1 << 0) +#define INNO_R03_DACR_MSK (0x1 << 1) +#define INNO_R03_DACR_RESET (0x0 << 1) /*DAC Reset*/ +#define INNO_R03_DACR_WORK (0x1 << 1) +#define INNO_R03_FWL_MSK (0x3 << 2) +#define INNO_R03_FWL_32BIT (0x3 << 2) /*1/2Frame Word Length*/ +#define INNO_R03_FWL_24BIT (0x2 << 2) +#define INNO_R03_FWL_20BIT (0x1 << 2) +#define INNO_R03_FWL_16BIT (0x0 << 2) + +#define INNO_R04_DACR_SW_SHIFT 0 +#define INNO_R04_DACL_SW_SHIFT 1 +#define INNO_R04_DACR_CLK_SHIFT 2 +#define INNO_R04_DACL_CLK_SHIFT 3 +#define INNO_R04_DACR_VREF_SHIFT 4 +#define INNO_R04_DACL_VREF_SHIFT 5 + +#define INNO_R05_HPR_EN_SHIFT 0 +#define INNO_R05_HPL_EN_SHIFT 1 +#define INNO_R05_HPR_WORK_SHIFT 2 +#define INNO_R05_HPL_WORK_SHIFT 3 + +#define INNO_R06_VOUTR_CZ_SHIFT 0 +#define INNO_R06_VOUTL_CZ_SHIFT 1 +#define INNO_R06_DACR_HILO_VREF_SHIFT 2 +#define INNO_R06_DACL_HILO_VREF_SHIFT 3 +#define INNO_R06_DAC_EN_SHIFT 5 + +#define INNO_R06_DAC_PRECHARGE (0x0 << 4) /*PreCharge control for DAC*/ +#define INNO_R06_DAC_DISCHARGE (0x1 << 4) + +#define INNO_HP_GAIN_SHIFT 0 +/* Gain of output, 1.5db step: -39db(0x0) ~ 0db(0x1a) ~ 6db(0x1f) */ +#define INNO_HP_GAIN_0DB 0x1a +#define INNO_HP_GAIN_N39DB 0x0 + +#define INNO_R09_HP_ANTIPOP_MSK 0x3 +#define INNO_R09_HP_ANTIPOP_OFF 0x1 +#define INNO_R09_HP_ANTIPOP_ON 0x2 +#define INNO_R09_HPR_ANITPOP_SHIFT 0 +#define INNO_R09_HPL_ANITPOP_SHIFT 2 +#define INNO_R09_HPR_MUTE_SHIFT 4 +#define INNO_R09_HPL_MUTE_SHIFT 5 +#define INNO_R09_DACR_SWITCH_SHIFT 6 +#define INNO_R09_DACL_SWITCH_SHIFT 7 + +#define INNO_R10_CHARGE_SEL_CUR_400I_YES (0x0 << 0) +#define INNO_R10_CHARGE_SEL_CUR_400I_NO (0x1 << 0) +#define INNO_R10_CHARGE_SEL_CUR_260I_YES (0x0 << 1) +#define INNO_R10_CHARGE_SEL_CUR_260I_NO (0x1 << 1) +#define INNO_R10_CHARGE_SEL_CUR_130I_YES (0x0 << 2) +#define INNO_R10_CHARGE_SEL_CUR_130I_NO (0x1 << 2) +#define INNO_R10_CHARGE_SEL_CUR_100I_YES (0x0 << 3) +#define INNO_R10_CHARGE_SEL_CUR_100I_NO (0x1 << 3) +#define INNO_R10_CHARGE_SEL_CUR_050I_YES (0x0 << 4) +#define INNO_R10_CHARGE_SEL_CUR_050I_NO (0x1 << 4) +#define INNO_R10_CHARGE_SEL_CUR_027I_YES (0x0 << 5) +#define INNO_R10_CHARGE_SEL_CUR_027I_NO (0x1 << 5) + +#define INNO_R10_MAX_CUR (INNO_R10_CHARGE_SEL_CUR_400I_YES | \ + INNO_R10_CHARGE_SEL_CUR_260I_YES | \ + INNO_R10_CHARGE_SEL_CUR_130I_YES | \ + INNO_R10_CHARGE_SEL_CUR_100I_YES | \ + INNO_R10_CHARGE_SEL_CUR_050I_YES | \ + INNO_R10_CHARGE_SEL_CUR_027I_YES) + +#endif diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c new file mode 100644 index 0000000000..f9456133a8 --- /dev/null +++ b/sound/soc/codecs/isabelle.c @@ -0,0 +1,1154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * isabelle.c - Low power high fidelity audio codec driver + * + * Copyright (c) 2012 Texas Instruments, Inc + * + * Initially based on sound/soc/codecs/twl6040.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "isabelle.h" + + +/* Register default values for ISABELLE driver. */ +static const struct reg_default isabelle_reg_defs[] = { + { 0, 0x00 }, + { 1, 0x00 }, + { 2, 0x00 }, + { 3, 0x00 }, + { 4, 0x00 }, + { 5, 0x00 }, + { 6, 0x00 }, + { 7, 0x00 }, + { 8, 0x00 }, + { 9, 0x00 }, + { 10, 0x00 }, + { 11, 0x00 }, + { 12, 0x00 }, + { 13, 0x00 }, + { 14, 0x00 }, + { 15, 0x00 }, + { 16, 0x00 }, + { 17, 0x00 }, + { 18, 0x00 }, + { 19, 0x00 }, + { 20, 0x00 }, + { 21, 0x02 }, + { 22, 0x02 }, + { 23, 0x02 }, + { 24, 0x02 }, + { 25, 0x0F }, + { 26, 0x8F }, + { 27, 0x0F }, + { 28, 0x8F }, + { 29, 0x00 }, + { 30, 0x00 }, + { 31, 0x00 }, + { 32, 0x00 }, + { 33, 0x00 }, + { 34, 0x00 }, + { 35, 0x00 }, + { 36, 0x00 }, + { 37, 0x00 }, + { 38, 0x00 }, + { 39, 0x00 }, + { 40, 0x00 }, + { 41, 0x00 }, + { 42, 0x00 }, + { 43, 0x00 }, + { 44, 0x00 }, + { 45, 0x00 }, + { 46, 0x00 }, + { 47, 0x00 }, + { 48, 0x00 }, + { 49, 0x00 }, + { 50, 0x00 }, + { 51, 0x00 }, + { 52, 0x00 }, + { 53, 0x00 }, + { 54, 0x00 }, + { 55, 0x00 }, + { 56, 0x00 }, + { 57, 0x00 }, + { 58, 0x00 }, + { 59, 0x00 }, + { 60, 0x00 }, + { 61, 0x00 }, + { 62, 0x00 }, + { 63, 0x00 }, + { 64, 0x00 }, + { 65, 0x00 }, + { 66, 0x00 }, + { 67, 0x00 }, + { 68, 0x00 }, + { 69, 0x90 }, + { 70, 0x90 }, + { 71, 0x90 }, + { 72, 0x00 }, + { 73, 0x00 }, + { 74, 0x00 }, + { 75, 0x00 }, + { 76, 0x00 }, + { 77, 0x00 }, + { 78, 0x00 }, + { 79, 0x00 }, + { 80, 0x00 }, + { 81, 0x00 }, + { 82, 0x00 }, + { 83, 0x00 }, + { 84, 0x00 }, + { 85, 0x07 }, + { 86, 0x00 }, + { 87, 0x00 }, + { 88, 0x00 }, + { 89, 0x07 }, + { 90, 0x80 }, + { 91, 0x07 }, + { 92, 0x07 }, + { 93, 0x00 }, + { 94, 0x00 }, + { 95, 0x00 }, + { 96, 0x00 }, + { 97, 0x00 }, + { 98, 0x00 }, + { 99, 0x00 }, +}; + +static const char *isabelle_rx1_texts[] = {"VRX1", "ARX1"}; +static const char *isabelle_rx2_texts[] = {"VRX2", "ARX2"}; + +static const struct soc_enum isabelle_rx1_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 3, + ARRAY_SIZE(isabelle_rx1_texts), isabelle_rx1_texts), + SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 5, + ARRAY_SIZE(isabelle_rx1_texts), isabelle_rx1_texts), +}; + +static const struct soc_enum isabelle_rx2_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 2, + ARRAY_SIZE(isabelle_rx2_texts), isabelle_rx2_texts), + SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 4, + ARRAY_SIZE(isabelle_rx2_texts), isabelle_rx2_texts), +}; + +/* Headset DAC playback switches */ +static const struct snd_kcontrol_new rx1_mux_controls = + SOC_DAPM_ENUM("Route", isabelle_rx1_enum); + +static const struct snd_kcontrol_new rx2_mux_controls = + SOC_DAPM_ENUM("Route", isabelle_rx2_enum); + +/* TX input selection */ +static const char *isabelle_atx_texts[] = {"AMIC1", "DMIC"}; +static const char *isabelle_vtx_texts[] = {"AMIC2", "DMIC"}; + +static const struct soc_enum isabelle_atx_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 7, + ARRAY_SIZE(isabelle_atx_texts), isabelle_atx_texts), + SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0, + ARRAY_SIZE(isabelle_atx_texts), isabelle_atx_texts), +}; + +static const struct soc_enum isabelle_vtx_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 6, + ARRAY_SIZE(isabelle_vtx_texts), isabelle_vtx_texts), + SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0, + ARRAY_SIZE(isabelle_vtx_texts), isabelle_vtx_texts), +}; + +static const struct snd_kcontrol_new atx_mux_controls = + SOC_DAPM_ENUM("Route", isabelle_atx_enum); + +static const struct snd_kcontrol_new vtx_mux_controls = + SOC_DAPM_ENUM("Route", isabelle_vtx_enum); + +/* Left analog microphone selection */ +static const char *isabelle_amic1_texts[] = { + "Main Mic", "Headset Mic", "Aux/FM Left"}; + +/* Left analog microphone selection */ +static const char *isabelle_amic2_texts[] = {"Sub Mic", "Aux/FM Right"}; + +static SOC_ENUM_SINGLE_DECL(isabelle_amic1_enum, + ISABELLE_AMIC_CFG_REG, 5, + isabelle_amic1_texts); + +static SOC_ENUM_SINGLE_DECL(isabelle_amic2_enum, + ISABELLE_AMIC_CFG_REG, 4, + isabelle_amic2_texts); + +static const struct snd_kcontrol_new amic1_control = + SOC_DAPM_ENUM("Route", isabelle_amic1_enum); + +static const struct snd_kcontrol_new amic2_control = + SOC_DAPM_ENUM("Route", isabelle_amic2_enum); + +static const char *isabelle_st_audio_texts[] = {"ATX1", "ATX2"}; + +static const char *isabelle_st_voice_texts[] = {"VTX1", "VTX2"}; + +static const struct soc_enum isabelle_st_audio_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA1_CFG_REG, 7, + ARRAY_SIZE(isabelle_st_audio_texts), + isabelle_st_audio_texts), + SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA2_CFG_REG, 7, + ARRAY_SIZE(isabelle_st_audio_texts), + isabelle_st_audio_texts), +}; + +static const struct soc_enum isabelle_st_voice_enum[] = { + SOC_ENUM_SINGLE(ISABELLE_VTX_STPGA1_CFG_REG, 7, + ARRAY_SIZE(isabelle_st_voice_texts), + isabelle_st_voice_texts), + SOC_ENUM_SINGLE(ISABELLE_VTX2_STPGA2_CFG_REG, 7, + ARRAY_SIZE(isabelle_st_voice_texts), + isabelle_st_voice_texts), +}; + +static const struct snd_kcontrol_new st_audio_control = + SOC_DAPM_ENUM("Route", isabelle_st_audio_enum); + +static const struct snd_kcontrol_new st_voice_control = + SOC_DAPM_ENUM("Route", isabelle_st_voice_enum); + +/* Mixer controls */ +static const struct snd_kcontrol_new isabelle_hs_left_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC1L Playback Switch", ISABELLE_HSDRV_CFG1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_HSDRV_CFG1_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_hs_right_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC1R Playback Switch", ISABELLE_HSDRV_CFG1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_HSDRV_CFG1_REG, 4, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_hf_left_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC2L Playback Switch", ISABELLE_HFLPGA_CFG_REG, 7, 1, 0), +SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_HFLPGA_CFG_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_hf_right_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC2R Playback Switch", ISABELLE_HFRPGA_CFG_REG, 7, 1, 0), +SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_HFRPGA_CFG_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_ep_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC2L Playback Switch", ISABELLE_EARDRV_CFG1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_EARDRV_CFG1_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_aux_left_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC3L Playback Switch", ISABELLE_LINEAMP_CFG_REG, 7, 1, 0), +SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_LINEAMP_CFG_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_aux_right_mixer_controls[] = { +SOC_DAPM_SINGLE("DAC3R Playback Switch", ISABELLE_LINEAMP_CFG_REG, 5, 1, 0), +SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_LINEAMP_CFG_REG, 4, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga1_left_mixer_controls[] = { +SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 7, 1, 0), +SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 6, 1, 0), +SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 5, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga1_right_mixer_controls[] = { +SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 3, 1, 0), +SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 2, 1, 0), +SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 1, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga2_left_mixer_controls[] = { +SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 7, 1, 0), +SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 6, 1, 0), +SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 5, 1, 0), +SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 4, 1, 0), +SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 3, 1, 0), +SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 2, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga2_right_mixer_controls[] = { +SOC_DAPM_SINGLE("USNC Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 7, 1, 0), +SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 3, 1, 0), +SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 2, 1, 0), +SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 1, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga3_left_mixer_controls[] = { +SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 7, 1, 0), +SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 6, 1, 0), +SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 5, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_dpga3_right_mixer_controls[] = { +SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 3, 1, 0), +SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 2, 1, 0), +SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 1, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx1_mixer_controls[] = { +SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DL1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx2_mixer_controls[] = { +SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 5, 1, 0), +SOC_DAPM_SINGLE("DL2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 4, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx3_mixer_controls[] = { +SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 3, 1, 0), +SOC_DAPM_SINGLE("DL3 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 2, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx4_mixer_controls[] = { +SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DL4 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 0, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx5_mixer_controls[] = { +SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DL5 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new isabelle_rx6_mixer_controls[] = { +SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("DL6 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 4, 1, 0), +}; + +static const struct snd_kcontrol_new ep_path_enable_control = + SOC_DAPM_SINGLE("Switch", ISABELLE_EARDRV_CFG2_REG, 0, 1, 0); + +/* TLV Declarations */ +static const DECLARE_TLV_DB_SCALE(mic_amp_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(afm_amp_tlv, -3300, 300, 0); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -1200, 200, 0); +static const DECLARE_TLV_DB_SCALE(hf_tlv, -5000, 200, 0); + +/* from -63 to 0 dB in 1 dB steps */ +static const DECLARE_TLV_DB_SCALE(dpga_tlv, -6300, 100, 1); + +/* from -63 to 9 dB in 1 dB steps */ +static const DECLARE_TLV_DB_SCALE(rx_tlv, -6300, 100, 1); + +static const DECLARE_TLV_DB_SCALE(st_tlv, -2700, 300, 1); +static const DECLARE_TLV_DB_SCALE(tx_tlv, -600, 100, 0); + +static const struct snd_kcontrol_new isabelle_snd_controls[] = { + SOC_DOUBLE_TLV("Headset Playback Volume", ISABELLE_HSDRV_GAIN_REG, + 4, 0, 0xF, 0, dac_tlv), + SOC_DOUBLE_R_TLV("Handsfree Playback Volume", + ISABELLE_HFLPGA_CFG_REG, ISABELLE_HFRPGA_CFG_REG, + 0, 0x1F, 0, hf_tlv), + SOC_DOUBLE_TLV("Aux Playback Volume", ISABELLE_LINEAMP_GAIN_REG, + 4, 0, 0xF, 0, dac_tlv), + SOC_SINGLE_TLV("Earpiece Playback Volume", ISABELLE_EARDRV_CFG1_REG, + 0, 0xF, 0, dac_tlv), + + SOC_DOUBLE_TLV("Aux FM Volume", ISABELLE_APGA_GAIN_REG, 4, 0, 0xF, 0, + afm_amp_tlv), + SOC_SINGLE_TLV("Mic1 Capture Volume", ISABELLE_MIC1_GAIN_REG, 3, 0x1F, + 0, mic_amp_tlv), + SOC_SINGLE_TLV("Mic2 Capture Volume", ISABELLE_MIC2_GAIN_REG, 3, 0x1F, + 0, mic_amp_tlv), + + SOC_DOUBLE_R_TLV("DPGA1 Volume", ISABELLE_DPGA1L_GAIN_REG, + ISABELLE_DPGA1R_GAIN_REG, 0, 0x3F, 0, dpga_tlv), + SOC_DOUBLE_R_TLV("DPGA2 Volume", ISABELLE_DPGA2L_GAIN_REG, + ISABELLE_DPGA2R_GAIN_REG, 0, 0x3F, 0, dpga_tlv), + SOC_DOUBLE_R_TLV("DPGA3 Volume", ISABELLE_DPGA3L_GAIN_REG, + ISABELLE_DPGA3R_GAIN_REG, 0, 0x3F, 0, dpga_tlv), + + SOC_SINGLE_TLV("Sidetone Audio TX1 Volume", + ISABELLE_ATX_STPGA1_CFG_REG, 0, 0xF, 0, st_tlv), + SOC_SINGLE_TLV("Sidetone Audio TX2 Volume", + ISABELLE_ATX_STPGA2_CFG_REG, 0, 0xF, 0, st_tlv), + SOC_SINGLE_TLV("Sidetone Voice TX1 Volume", + ISABELLE_VTX_STPGA1_CFG_REG, 0, 0xF, 0, st_tlv), + SOC_SINGLE_TLV("Sidetone Voice TX2 Volume", + ISABELLE_VTX2_STPGA2_CFG_REG, 0, 0xF, 0, st_tlv), + + SOC_SINGLE_TLV("Audio TX1 Volume", ISABELLE_ATX1_DPGA_REG, 4, 0xF, 0, + tx_tlv), + SOC_SINGLE_TLV("Audio TX2 Volume", ISABELLE_ATX2_DPGA_REG, 4, 0xF, 0, + tx_tlv), + SOC_SINGLE_TLV("Voice TX1 Volume", ISABELLE_VTX1_DPGA_REG, 4, 0xF, 0, + tx_tlv), + SOC_SINGLE_TLV("Voice TX2 Volume", ISABELLE_VTX2_DPGA_REG, 4, 0xF, 0, + tx_tlv), + + SOC_SINGLE_TLV("RX1 DPGA Volume", ISABELLE_RX1_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + SOC_SINGLE_TLV("RX2 DPGA Volume", ISABELLE_RX2_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + SOC_SINGLE_TLV("RX3 DPGA Volume", ISABELLE_RX3_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + SOC_SINGLE_TLV("RX4 DPGA Volume", ISABELLE_RX4_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + SOC_SINGLE_TLV("RX5 DPGA Volume", ISABELLE_RX5_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + SOC_SINGLE_TLV("RX6 DPGA Volume", ISABELLE_RX6_DPGA_REG, 0, 0x3F, 0, + rx_tlv), + + SOC_SINGLE("Headset Noise Gate", ISABELLE_HS_NG_CFG1_REG, 7, 1, 0), + SOC_SINGLE("Handsfree Noise Gate", ISABELLE_HF_NG_CFG1_REG, 7, 1, 0), + + SOC_SINGLE("ATX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 7, 1, 0), + SOC_SINGLE("ATX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 6, 1, 0), + SOC_SINGLE("ARX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 5, 1, 0), + SOC_SINGLE("ARX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 4, 1, 0), + SOC_SINGLE("ARX3 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 3, 1, 0), + SOC_SINGLE("ARX4 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 2, 1, 0), + SOC_SINGLE("ARX5 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 1, 1, 0), + SOC_SINGLE("ARX6 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 0, 1, 0), + SOC_SINGLE("VRX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 3, 1, 0), + SOC_SINGLE("VRX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG, + 2, 1, 0), + + SOC_SINGLE("ATX1 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG, + 7, 1, 0), + SOC_SINGLE("ATX2 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG, + 6, 1, 0), + SOC_SINGLE("VTX1 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG, + 5, 1, 0), + SOC_SINGLE("VTX2 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG, + 4, 1, 0), + SOC_SINGLE("RX1 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 5, 1, 0), + SOC_SINGLE("RX2 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 4, 1, 0), + SOC_SINGLE("RX3 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 3, 1, 0), + SOC_SINGLE("RX4 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 2, 1, 0), + SOC_SINGLE("RX5 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 1, 1, 0), + SOC_SINGLE("RX6 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG, + 0, 1, 0), + + SOC_SINGLE("ULATX12 Capture Switch", ISABELLE_ULATX12_INTF_CFG_REG, + 7, 1, 0), + + SOC_SINGLE("DL12 Playback Switch", ISABELLE_DL12_INTF_CFG_REG, + 7, 1, 0), + SOC_SINGLE("DL34 Playback Switch", ISABELLE_DL34_INTF_CFG_REG, + 7, 1, 0), + SOC_SINGLE("DL56 Playback Switch", ISABELLE_DL56_INTF_CFG_REG, + 7, 1, 0), + + /* DMIC Switch */ + SOC_SINGLE("DMIC Switch", ISABELLE_DMIC_CFG_REG, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget isabelle_dapm_widgets[] = { + /* Inputs */ + SND_SOC_DAPM_INPUT("MAINMIC"), + SND_SOC_DAPM_INPUT("HSMIC"), + SND_SOC_DAPM_INPUT("SUBMIC"), + SND_SOC_DAPM_INPUT("LINEIN1"), + SND_SOC_DAPM_INPUT("LINEIN2"), + SND_SOC_DAPM_INPUT("DMICDAT"), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HSOL"), + SND_SOC_DAPM_OUTPUT("HSOR"), + SND_SOC_DAPM_OUTPUT("HFL"), + SND_SOC_DAPM_OUTPUT("HFR"), + SND_SOC_DAPM_OUTPUT("EP"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + + SND_SOC_DAPM_PGA("DL1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DL2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DL3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DL4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DL5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DL6", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Analog input muxes for the capture amplifiers */ + SND_SOC_DAPM_MUX("Analog Left Capture Route", + SND_SOC_NOPM, 0, 0, &amic1_control), + SND_SOC_DAPM_MUX("Analog Right Capture Route", + SND_SOC_NOPM, 0, 0, &amic2_control), + + SND_SOC_DAPM_MUX("Sidetone Audio Playback", SND_SOC_NOPM, 0, 0, + &st_audio_control), + SND_SOC_DAPM_MUX("Sidetone Voice Playback", SND_SOC_NOPM, 0, 0, + &st_voice_control), + + /* AIF */ + SND_SOC_DAPM_AIF_IN("INTF1_SDI", NULL, 0, ISABELLE_INTF_EN_REG, 7, 0), + SND_SOC_DAPM_AIF_IN("INTF2_SDI", NULL, 0, ISABELLE_INTF_EN_REG, 6, 0), + + SND_SOC_DAPM_AIF_OUT("INTF1_SDO", NULL, 0, ISABELLE_INTF_EN_REG, 5, 0), + SND_SOC_DAPM_AIF_OUT("INTF2_SDO", NULL, 0, ISABELLE_INTF_EN_REG, 4, 0), + + SND_SOC_DAPM_OUT_DRV("ULATX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("ULATX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("ULVTX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("ULVTX2", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Analog Capture PGAs */ + SND_SOC_DAPM_PGA("MicAmp1", ISABELLE_AMIC_CFG_REG, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("MicAmp2", ISABELLE_AMIC_CFG_REG, 4, 0, NULL, 0), + + /* Auxiliary FM PGAs */ + SND_SOC_DAPM_PGA("APGA1", ISABELLE_APGA_CFG_REG, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("APGA2", ISABELLE_APGA_CFG_REG, 6, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1", "Left Front Capture", + ISABELLE_AMIC_CFG_REG, 7, 0), + SND_SOC_DAPM_ADC("ADC2", "Right Front Capture", + ISABELLE_AMIC_CFG_REG, 6, 0), + + /* Microphone Bias */ + SND_SOC_DAPM_SUPPLY("Headset Mic Bias", ISABELLE_ABIAS_CFG_REG, + 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Main Mic Bias", ISABELLE_ABIAS_CFG_REG, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Digital Mic1 Bias", + ISABELLE_DBIAS_CFG_REG, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Digital Mic2 Bias", + ISABELLE_DBIAS_CFG_REG, 2, 0, NULL, 0), + + /* Mixers */ + SND_SOC_DAPM_MIXER("Headset Left Mixer", SND_SOC_NOPM, 0, 0, + isabelle_hs_left_mixer_controls, + ARRAY_SIZE(isabelle_hs_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Headset Right Mixer", SND_SOC_NOPM, 0, 0, + isabelle_hs_right_mixer_controls, + ARRAY_SIZE(isabelle_hs_right_mixer_controls)), + SND_SOC_DAPM_MIXER("Handsfree Left Mixer", SND_SOC_NOPM, 0, 0, + isabelle_hf_left_mixer_controls, + ARRAY_SIZE(isabelle_hf_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Handsfree Right Mixer", SND_SOC_NOPM, 0, 0, + isabelle_hf_right_mixer_controls, + ARRAY_SIZE(isabelle_hf_right_mixer_controls)), + SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_aux_left_mixer_controls, + ARRAY_SIZE(isabelle_aux_left_mixer_controls)), + SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_aux_right_mixer_controls, + ARRAY_SIZE(isabelle_aux_right_mixer_controls)), + SND_SOC_DAPM_MIXER("Earphone Mixer", SND_SOC_NOPM, 0, 0, + isabelle_ep_mixer_controls, + ARRAY_SIZE(isabelle_ep_mixer_controls)), + + SND_SOC_DAPM_MIXER("DPGA1L Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga1_left_mixer_controls, + ARRAY_SIZE(isabelle_dpga1_left_mixer_controls)), + SND_SOC_DAPM_MIXER("DPGA1R Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga1_right_mixer_controls, + ARRAY_SIZE(isabelle_dpga1_right_mixer_controls)), + SND_SOC_DAPM_MIXER("DPGA2L Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga2_left_mixer_controls, + ARRAY_SIZE(isabelle_dpga2_left_mixer_controls)), + SND_SOC_DAPM_MIXER("DPGA2R Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga2_right_mixer_controls, + ARRAY_SIZE(isabelle_dpga2_right_mixer_controls)), + SND_SOC_DAPM_MIXER("DPGA3L Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga3_left_mixer_controls, + ARRAY_SIZE(isabelle_dpga3_left_mixer_controls)), + SND_SOC_DAPM_MIXER("DPGA3R Mixer", SND_SOC_NOPM, 0, 0, + isabelle_dpga3_right_mixer_controls, + ARRAY_SIZE(isabelle_dpga3_right_mixer_controls)), + + SND_SOC_DAPM_MIXER("RX1 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx1_mixer_controls, + ARRAY_SIZE(isabelle_rx1_mixer_controls)), + SND_SOC_DAPM_MIXER("RX2 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx2_mixer_controls, + ARRAY_SIZE(isabelle_rx2_mixer_controls)), + SND_SOC_DAPM_MIXER("RX3 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx3_mixer_controls, + ARRAY_SIZE(isabelle_rx3_mixer_controls)), + SND_SOC_DAPM_MIXER("RX4 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx4_mixer_controls, + ARRAY_SIZE(isabelle_rx4_mixer_controls)), + SND_SOC_DAPM_MIXER("RX5 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx5_mixer_controls, + ARRAY_SIZE(isabelle_rx5_mixer_controls)), + SND_SOC_DAPM_MIXER("RX6 Mixer", SND_SOC_NOPM, 0, 0, + isabelle_rx6_mixer_controls, + ARRAY_SIZE(isabelle_rx6_mixer_controls)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC1L", "Headset Playback", ISABELLE_DAC_CFG_REG, + 5, 0), + SND_SOC_DAPM_DAC("DAC1R", "Headset Playback", ISABELLE_DAC_CFG_REG, + 4, 0), + SND_SOC_DAPM_DAC("DAC2L", "Handsfree Playback", ISABELLE_DAC_CFG_REG, + 3, 0), + SND_SOC_DAPM_DAC("DAC2R", "Handsfree Playback", ISABELLE_DAC_CFG_REG, + 2, 0), + SND_SOC_DAPM_DAC("DAC3L", "Lineout Playback", ISABELLE_DAC_CFG_REG, + 1, 0), + SND_SOC_DAPM_DAC("DAC3R", "Lineout Playback", ISABELLE_DAC_CFG_REG, + 0, 0), + + /* Analog Playback PGAs */ + SND_SOC_DAPM_PGA("Sidetone Audio PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Sidetone Voice PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HF Left PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HF Right PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA1L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA1R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA2L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA2R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA3L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DPGA3R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Analog Playback Mux */ + SND_SOC_DAPM_MUX("RX1 Playback", ISABELLE_ALU_RX_EN_REG, 5, 0, + &rx1_mux_controls), + SND_SOC_DAPM_MUX("RX2 Playback", ISABELLE_ALU_RX_EN_REG, 4, 0, + &rx2_mux_controls), + + /* TX Select */ + SND_SOC_DAPM_MUX("ATX Select", ISABELLE_TX_INPUT_CFG_REG, + 7, 0, &atx_mux_controls), + SND_SOC_DAPM_MUX("VTX Select", ISABELLE_TX_INPUT_CFG_REG, + 6, 0, &vtx_mux_controls), + + SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0, + &ep_path_enable_control), + + /* Output Drivers */ + SND_SOC_DAPM_OUT_DRV("HS Left Driver", ISABELLE_HSDRV_CFG2_REG, + 1, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HS Right Driver", ISABELLE_HSDRV_CFG2_REG, + 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("LINEOUT1 Left Driver", ISABELLE_LINEAMP_CFG_REG, + 1, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("LINEOUT2 Right Driver", ISABELLE_LINEAMP_CFG_REG, + 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Earphone Driver", ISABELLE_EARDRV_CFG2_REG, + 1, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("HF Left Driver", ISABELLE_HFDRV_CFG_REG, + 1, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HF Right Driver", ISABELLE_HFDRV_CFG_REG, + 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route isabelle_intercon[] = { + /* Interface mapping */ + { "DL1", "DL12 Playback Switch", "INTF1_SDI" }, + { "DL2", "DL12 Playback Switch", "INTF1_SDI" }, + { "DL3", "DL34 Playback Switch", "INTF1_SDI" }, + { "DL4", "DL34 Playback Switch", "INTF1_SDI" }, + { "DL5", "DL56 Playback Switch", "INTF1_SDI" }, + { "DL6", "DL56 Playback Switch", "INTF1_SDI" }, + + { "DL1", "DL12 Playback Switch", "INTF2_SDI" }, + { "DL2", "DL12 Playback Switch", "INTF2_SDI" }, + { "DL3", "DL34 Playback Switch", "INTF2_SDI" }, + { "DL4", "DL34 Playback Switch", "INTF2_SDI" }, + { "DL5", "DL56 Playback Switch", "INTF2_SDI" }, + { "DL6", "DL56 Playback Switch", "INTF2_SDI" }, + + /* Input side mapping */ + { "Sidetone Audio PGA", NULL, "Sidetone Audio Playback" }, + { "Sidetone Voice PGA", NULL, "Sidetone Voice Playback" }, + + { "RX1 Mixer", "ST1 Playback Switch", "Sidetone Audio PGA" }, + + { "RX1 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" }, + { "RX1 Mixer", "DL1 Playback Switch", "DL1" }, + + { "RX2 Mixer", "ST2 Playback Switch", "Sidetone Audio PGA" }, + + { "RX2 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" }, + { "RX2 Mixer", "DL2 Playback Switch", "DL2" }, + + { "RX3 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" }, + { "RX3 Mixer", "DL3 Playback Switch", "DL3" }, + + { "RX4 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" }, + { "RX4 Mixer", "DL4 Playback Switch", "DL4" }, + + { "RX5 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" }, + { "RX5 Mixer", "DL5 Playback Switch", "DL5" }, + + { "RX6 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" }, + { "RX6 Mixer", "DL6 Playback Switch", "DL6" }, + + /* Capture path */ + { "Analog Left Capture Route", "Headset Mic", "HSMIC" }, + { "Analog Left Capture Route", "Main Mic", "MAINMIC" }, + { "Analog Left Capture Route", "Aux/FM Left", "LINEIN1" }, + + { "Analog Right Capture Route", "Sub Mic", "SUBMIC" }, + { "Analog Right Capture Route", "Aux/FM Right", "LINEIN2" }, + + { "MicAmp1", NULL, "Analog Left Capture Route" }, + { "MicAmp2", NULL, "Analog Right Capture Route" }, + + { "ADC1", NULL, "MicAmp1" }, + { "ADC2", NULL, "MicAmp2" }, + + { "ATX Select", "AMIC1", "ADC1" }, + { "ATX Select", "DMIC", "DMICDAT" }, + { "ATX Select", "AMIC2", "ADC2" }, + + { "VTX Select", "AMIC1", "ADC1" }, + { "VTX Select", "DMIC", "DMICDAT" }, + { "VTX Select", "AMIC2", "ADC2" }, + + { "ULATX1", "ATX1 Filter Enable Switch", "ATX Select" }, + { "ULATX1", "ATX1 Filter Bypass Switch", "ATX Select" }, + { "ULATX2", "ATX2 Filter Enable Switch", "ATX Select" }, + { "ULATX2", "ATX2 Filter Bypass Switch", "ATX Select" }, + + { "ULVTX1", "VTX1 Filter Enable Switch", "VTX Select" }, + { "ULVTX1", "VTX1 Filter Bypass Switch", "VTX Select" }, + { "ULVTX2", "VTX2 Filter Enable Switch", "VTX Select" }, + { "ULVTX2", "VTX2 Filter Bypass Switch", "VTX Select" }, + + { "INTF1_SDO", "ULATX12 Capture Switch", "ULATX1" }, + { "INTF1_SDO", "ULATX12 Capture Switch", "ULATX2" }, + { "INTF2_SDO", "ULATX12 Capture Switch", "ULATX1" }, + { "INTF2_SDO", "ULATX12 Capture Switch", "ULATX2" }, + + { "INTF1_SDO", NULL, "ULVTX1" }, + { "INTF1_SDO", NULL, "ULVTX2" }, + { "INTF2_SDO", NULL, "ULVTX1" }, + { "INTF2_SDO", NULL, "ULVTX2" }, + + /* AFM Path */ + { "APGA1", NULL, "LINEIN1" }, + { "APGA2", NULL, "LINEIN2" }, + + { "RX1 Playback", "VRX1 Filter Bypass Switch", "RX1 Mixer" }, + { "RX1 Playback", "ARX1 Filter Bypass Switch", "RX1 Mixer" }, + { "RX1 Playback", "RX1 Filter Enable Switch", "RX1 Mixer" }, + + { "RX2 Playback", "VRX2 Filter Bypass Switch", "RX2 Mixer" }, + { "RX2 Playback", "ARX2 Filter Bypass Switch", "RX2 Mixer" }, + { "RX2 Playback", "RX2 Filter Enable Switch", "RX2 Mixer" }, + + { "RX3 Playback", "ARX3 Filter Bypass Switch", "RX3 Mixer" }, + { "RX3 Playback", "RX3 Filter Enable Switch", "RX3 Mixer" }, + + { "RX4 Playback", "ARX4 Filter Bypass Switch", "RX4 Mixer" }, + { "RX4 Playback", "RX4 Filter Enable Switch", "RX4 Mixer" }, + + { "RX5 Playback", "ARX5 Filter Bypass Switch", "RX5 Mixer" }, + { "RX5 Playback", "RX5 Filter Enable Switch", "RX5 Mixer" }, + + { "RX6 Playback", "ARX6 Filter Bypass Switch", "RX6 Mixer" }, + { "RX6 Playback", "RX6 Filter Enable Switch", "RX6 Mixer" }, + + { "DPGA1L Mixer", "RX1 Playback Switch", "RX1 Playback" }, + { "DPGA1L Mixer", "RX3 Playback Switch", "RX3 Playback" }, + { "DPGA1L Mixer", "RX5 Playback Switch", "RX5 Playback" }, + + { "DPGA1R Mixer", "RX2 Playback Switch", "RX2 Playback" }, + { "DPGA1R Mixer", "RX4 Playback Switch", "RX4 Playback" }, + { "DPGA1R Mixer", "RX6 Playback Switch", "RX6 Playback" }, + + { "DPGA1L", NULL, "DPGA1L Mixer" }, + { "DPGA1R", NULL, "DPGA1R Mixer" }, + + { "DAC1L", NULL, "DPGA1L" }, + { "DAC1R", NULL, "DPGA1R" }, + + { "DPGA2L Mixer", "RX1 Playback Switch", "RX1 Playback" }, + { "DPGA2L Mixer", "RX2 Playback Switch", "RX2 Playback" }, + { "DPGA2L Mixer", "RX3 Playback Switch", "RX3 Playback" }, + { "DPGA2L Mixer", "RX4 Playback Switch", "RX4 Playback" }, + { "DPGA2L Mixer", "RX5 Playback Switch", "RX5 Playback" }, + { "DPGA2L Mixer", "RX6 Playback Switch", "RX6 Playback" }, + + { "DPGA2R Mixer", "RX2 Playback Switch", "RX2 Playback" }, + { "DPGA2R Mixer", "RX4 Playback Switch", "RX4 Playback" }, + { "DPGA2R Mixer", "RX6 Playback Switch", "RX6 Playback" }, + + { "DPGA2L", NULL, "DPGA2L Mixer" }, + { "DPGA2R", NULL, "DPGA2R Mixer" }, + + { "DAC2L", NULL, "DPGA2L" }, + { "DAC2R", NULL, "DPGA2R" }, + + { "DPGA3L Mixer", "RX1 Playback Switch", "RX1 Playback" }, + { "DPGA3L Mixer", "RX3 Playback Switch", "RX3 Playback" }, + { "DPGA3L Mixer", "RX5 Playback Switch", "RX5 Playback" }, + + { "DPGA3R Mixer", "RX2 Playback Switch", "RX2 Playback" }, + { "DPGA3R Mixer", "RX4 Playback Switch", "RX4 Playback" }, + { "DPGA3R Mixer", "RX6 Playback Switch", "RX6 Playback" }, + + { "DPGA3L", NULL, "DPGA3L Mixer" }, + { "DPGA3R", NULL, "DPGA3R Mixer" }, + + { "DAC3L", NULL, "DPGA3L" }, + { "DAC3R", NULL, "DPGA3R" }, + + { "Headset Left Mixer", "DAC1L Playback Switch", "DAC1L" }, + { "Headset Left Mixer", "APGA1 Playback Switch", "APGA1" }, + + { "Headset Right Mixer", "DAC1R Playback Switch", "DAC1R" }, + { "Headset Right Mixer", "APGA2 Playback Switch", "APGA2" }, + + { "HS Left Driver", NULL, "Headset Left Mixer" }, + { "HS Right Driver", NULL, "Headset Right Mixer" }, + + { "HSOL", NULL, "HS Left Driver" }, + { "HSOR", NULL, "HS Right Driver" }, + + /* Earphone playback path */ + { "Earphone Mixer", "DAC2L Playback Switch", "DAC2L" }, + { "Earphone Mixer", "APGA1 Playback Switch", "APGA1" }, + + { "Earphone Playback", "Switch", "Earphone Mixer" }, + { "Earphone Driver", NULL, "Earphone Playback" }, + { "EP", NULL, "Earphone Driver" }, + + { "Handsfree Left Mixer", "DAC2L Playback Switch", "DAC2L" }, + { "Handsfree Left Mixer", "APGA1 Playback Switch", "APGA1" }, + + { "Handsfree Right Mixer", "DAC2R Playback Switch", "DAC2R" }, + { "Handsfree Right Mixer", "APGA2 Playback Switch", "APGA2" }, + + { "HF Left PGA", NULL, "Handsfree Left Mixer" }, + { "HF Right PGA", NULL, "Handsfree Right Mixer" }, + + { "HF Left Driver", NULL, "HF Left PGA" }, + { "HF Right Driver", NULL, "HF Right PGA" }, + + { "HFL", NULL, "HF Left Driver" }, + { "HFR", NULL, "HF Right Driver" }, + + { "LINEOUT1 Mixer", "DAC3L Playback Switch", "DAC3L" }, + { "LINEOUT1 Mixer", "APGA1 Playback Switch", "APGA1" }, + + { "LINEOUT2 Mixer", "DAC3R Playback Switch", "DAC3R" }, + { "LINEOUT2 Mixer", "APGA2 Playback Switch", "APGA2" }, + + { "LINEOUT1 Driver", NULL, "LINEOUT1 Mixer" }, + { "LINEOUT2 Driver", NULL, "LINEOUT2 Mixer" }, + + { "LINEOUT1", NULL, "LINEOUT1 Driver" }, + { "LINEOUT2", NULL, "LINEOUT2 Driver" }, +}; + +static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + snd_soc_component_update_bits(dai->component, ISABELLE_DAC1_SOFTRAMP_REG, + BIT(4), (mute ? BIT(4) : 0)); + + return 0; +} + +static int isabelle_hf_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + snd_soc_component_update_bits(dai->component, ISABELLE_DAC2_SOFTRAMP_REG, + BIT(4), (mute ? BIT(4) : 0)); + + return 0; +} + +static int isabelle_line_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + snd_soc_component_update_bits(dai->component, ISABELLE_DAC3_SOFTRAMP_REG, + BIT(4), (mute ? BIT(4) : 0)); + + return 0; +} + +static int isabelle_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_component_update_bits(component, ISABELLE_PWR_EN_REG, + ISABELLE_CHIP_EN, BIT(0)); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, ISABELLE_PWR_EN_REG, + ISABELLE_CHIP_EN, 0); + break; + } + + return 0; +} + +static int isabelle_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 aif = 0; + unsigned int fs_val = 0; + + switch (params_rate(params)) { + case 8000: + fs_val = ISABELLE_FS_RATE_8; + break; + case 11025: + fs_val = ISABELLE_FS_RATE_11; + break; + case 12000: + fs_val = ISABELLE_FS_RATE_12; + break; + case 16000: + fs_val = ISABELLE_FS_RATE_16; + break; + case 22050: + fs_val = ISABELLE_FS_RATE_22; + break; + case 24000: + fs_val = ISABELLE_FS_RATE_24; + break; + case 32000: + fs_val = ISABELLE_FS_RATE_32; + break; + case 44100: + fs_val = ISABELLE_FS_RATE_44; + break; + case 48000: + fs_val = ISABELLE_FS_RATE_48; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, ISABELLE_FS_RATE_CFG_REG, + ISABELLE_FS_RATE_MASK, fs_val); + + /* bit size */ + switch (params_width(params)) { + case 20: + aif |= ISABELLE_AIF_LENGTH_20; + break; + case 32: + aif |= ISABELLE_AIF_LENGTH_32; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, ISABELLE_INTF_CFG_REG, + ISABELLE_AIF_LENGTH_MASK, aif); + + return 0; +} + +static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + unsigned int aif_val = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + aif_val &= ~ISABELLE_AIF_MS; + break; + case SND_SOC_DAIFMT_CBP_CFP: + aif_val |= ISABELLE_AIF_MS; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + aif_val |= ISABELLE_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + aif_val |= ISABELLE_LEFT_J_MODE; + break; + case SND_SOC_DAIFMT_PDM: + aif_val |= ISABELLE_PDM_MODE; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, ISABELLE_INTF_CFG_REG, + (ISABELLE_AIF_MS | ISABELLE_AIF_FMT_MASK), aif_val); + + return 0; +} + +/* Rates supported by Isabelle driver */ +#define ISABELLE_RATES SNDRV_PCM_RATE_8000_48000 + +/* Formates supported by Isabelle driver. */ +#define ISABELLE_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops isabelle_hs_dai_ops = { + .hw_params = isabelle_hw_params, + .set_fmt = isabelle_set_dai_fmt, + .mute_stream = isabelle_hs_mute, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops isabelle_hf_dai_ops = { + .hw_params = isabelle_hw_params, + .set_fmt = isabelle_set_dai_fmt, + .mute_stream = isabelle_hf_mute, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops isabelle_line_dai_ops = { + .hw_params = isabelle_hw_params, + .set_fmt = isabelle_set_dai_fmt, + .mute_stream = isabelle_line_mute, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops isabelle_ul_dai_ops = { + .hw_params = isabelle_hw_params, + .set_fmt = isabelle_set_dai_fmt, +}; + +/* ISABELLE dai structure */ +static struct snd_soc_dai_driver isabelle_dai[] = { + { + .name = "isabelle-dl1", + .playback = { + .stream_name = "Headset Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ISABELLE_RATES, + .formats = ISABELLE_FORMATS, + }, + .ops = &isabelle_hs_dai_ops, + }, + { + .name = "isabelle-dl2", + .playback = { + .stream_name = "Handsfree Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ISABELLE_RATES, + .formats = ISABELLE_FORMATS, + }, + .ops = &isabelle_hf_dai_ops, + }, + { + .name = "isabelle-lineout", + .playback = { + .stream_name = "Lineout Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ISABELLE_RATES, + .formats = ISABELLE_FORMATS, + }, + .ops = &isabelle_line_dai_ops, + }, + { + .name = "isabelle-ul", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = ISABELLE_RATES, + .formats = ISABELLE_FORMATS, + }, + .ops = &isabelle_ul_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_isabelle = { + .set_bias_level = isabelle_set_bias_level, + .controls = isabelle_snd_controls, + .num_controls = ARRAY_SIZE(isabelle_snd_controls), + .dapm_widgets = isabelle_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(isabelle_dapm_widgets), + .dapm_routes = isabelle_intercon, + .num_dapm_routes = ARRAY_SIZE(isabelle_intercon), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config isabelle_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = ISABELLE_MAX_REGISTER, + .reg_defaults = isabelle_reg_defs, + .num_reg_defaults = ARRAY_SIZE(isabelle_reg_defs), + .cache_type = REGCACHE_RBTREE, +}; + +static int isabelle_i2c_probe(struct i2c_client *i2c) +{ + struct regmap *isabelle_regmap; + int ret = 0; + + isabelle_regmap = devm_regmap_init_i2c(i2c, &isabelle_regmap_config); + if (IS_ERR(isabelle_regmap)) { + ret = PTR_ERR(isabelle_regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + i2c_set_clientdata(i2c, isabelle_regmap); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_isabelle, isabelle_dai, + ARRAY_SIZE(isabelle_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register component: %d\n", ret); + return ret; + } + + return ret; +} + +static const struct i2c_device_id isabelle_i2c_id[] = { + { "isabelle", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id); + +static struct i2c_driver isabelle_i2c_driver = { + .driver = { + .name = "isabelle", + }, + .probe = isabelle_i2c_probe, + .id_table = isabelle_i2c_id, +}; + +module_i2c_driver(isabelle_i2c_driver); + +MODULE_DESCRIPTION("ASoC ISABELLE driver"); +MODULE_AUTHOR("Vishwas A Deshpande "); +MODULE_AUTHOR("M R Swami Reddy "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/isabelle.h b/sound/soc/codecs/isabelle.h new file mode 100644 index 0000000000..23afc77cdc --- /dev/null +++ b/sound/soc/codecs/isabelle.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * isabelle.h - Low power high fidelity audio codec driver header file + * + * Copyright (c) 2012 Texas Instruments, Inc + */ + +#ifndef _ISABELLE_H +#define _ISABELLE_H + +#include + +/* ISABELLE REGISTERS */ + +#define ISABELLE_PWR_CFG_REG 0x01 +#define ISABELLE_PWR_EN_REG 0x02 +#define ISABELLE_PS_EN1_REG 0x03 +#define ISABELLE_INT1_STATUS_REG 0x04 +#define ISABELLE_INT1_MASK_REG 0x05 +#define ISABELLE_INT2_STATUS_REG 0x06 +#define ISABELLE_INT2_MASK_REG 0x07 +#define ISABELLE_HKCTL1_REG 0x08 +#define ISABELLE_HKCTL2_REG 0x09 +#define ISABELLE_HKCTL3_REG 0x0A +#define ISABELLE_ACCDET_STATUS_REG 0x0B +#define ISABELLE_BUTTON_ID_REG 0x0C +#define ISABELLE_PLL_CFG_REG 0x10 +#define ISABELLE_PLL_EN_REG 0x11 +#define ISABELLE_FS_RATE_CFG_REG 0x12 +#define ISABELLE_INTF_CFG_REG 0x13 +#define ISABELLE_INTF_EN_REG 0x14 +#define ISABELLE_ULATX12_INTF_CFG_REG 0x15 +#define ISABELLE_DL12_INTF_CFG_REG 0x16 +#define ISABELLE_DL34_INTF_CFG_REG 0x17 +#define ISABELLE_DL56_INTF_CFG_REG 0x18 +#define ISABELLE_ATX_STPGA1_CFG_REG 0x19 +#define ISABELLE_ATX_STPGA2_CFG_REG 0x1A +#define ISABELLE_VTX_STPGA1_CFG_REG 0x1B +#define ISABELLE_VTX2_STPGA2_CFG_REG 0x1C +#define ISABELLE_ATX1_DPGA_REG 0x1D +#define ISABELLE_ATX2_DPGA_REG 0x1E +#define ISABELLE_VTX1_DPGA_REG 0x1F +#define ISABELLE_VTX2_DPGA_REG 0x20 +#define ISABELLE_TX_INPUT_CFG_REG 0x21 +#define ISABELLE_RX_INPUT_CFG_REG 0x22 +#define ISABELLE_RX_INPUT_CFG2_REG 0x23 +#define ISABELLE_VOICE_HPF_CFG_REG 0x24 +#define ISABELLE_AUDIO_HPF_CFG_REG 0x25 +#define ISABELLE_RX1_DPGA_REG 0x26 +#define ISABELLE_RX2_DPGA_REG 0x27 +#define ISABELLE_RX3_DPGA_REG 0x28 +#define ISABELLE_RX4_DPGA_REG 0x29 +#define ISABELLE_RX5_DPGA_REG 0x2A +#define ISABELLE_RX6_DPGA_REG 0x2B +#define ISABELLE_ALU_TX_EN_REG 0x2C +#define ISABELLE_ALU_RX_EN_REG 0x2D +#define ISABELLE_IIR_RESYNC_REG 0x2E +#define ISABELLE_ABIAS_CFG_REG 0x30 +#define ISABELLE_DBIAS_CFG_REG 0x31 +#define ISABELLE_MIC1_GAIN_REG 0x32 +#define ISABELLE_MIC2_GAIN_REG 0x33 +#define ISABELLE_AMIC_CFG_REG 0x34 +#define ISABELLE_DMIC_CFG_REG 0x35 +#define ISABELLE_APGA_GAIN_REG 0x36 +#define ISABELLE_APGA_CFG_REG 0x37 +#define ISABELLE_TX_GAIN_DLY_REG 0x38 +#define ISABELLE_RX_GAIN_DLY_REG 0x39 +#define ISABELLE_RX_PWR_CTRL_REG 0x3A +#define ISABELLE_DPGA1LR_IN_SEL_REG 0x3B +#define ISABELLE_DPGA1L_GAIN_REG 0x3C +#define ISABELLE_DPGA1R_GAIN_REG 0x3D +#define ISABELLE_DPGA2L_IN_SEL_REG 0x3E +#define ISABELLE_DPGA2R_IN_SEL_REG 0x3F +#define ISABELLE_DPGA2L_GAIN_REG 0x40 +#define ISABELLE_DPGA2R_GAIN_REG 0x41 +#define ISABELLE_DPGA3LR_IN_SEL_REG 0x42 +#define ISABELLE_DPGA3L_GAIN_REG 0x43 +#define ISABELLE_DPGA3R_GAIN_REG 0x44 +#define ISABELLE_DAC1_SOFTRAMP_REG 0x45 +#define ISABELLE_DAC2_SOFTRAMP_REG 0x46 +#define ISABELLE_DAC3_SOFTRAMP_REG 0x47 +#define ISABELLE_DAC_CFG_REG 0x48 +#define ISABELLE_EARDRV_CFG1_REG 0x49 +#define ISABELLE_EARDRV_CFG2_REG 0x4A +#define ISABELLE_HSDRV_GAIN_REG 0x4B +#define ISABELLE_HSDRV_CFG1_REG 0x4C +#define ISABELLE_HSDRV_CFG2_REG 0x4D +#define ISABELLE_HS_NG_CFG1_REG 0x4E +#define ISABELLE_HS_NG_CFG2_REG 0x4F +#define ISABELLE_LINEAMP_GAIN_REG 0x50 +#define ISABELLE_LINEAMP_CFG_REG 0x51 +#define ISABELLE_HFL_VOL_CTRL_REG 0x52 +#define ISABELLE_HFL_SFTVOL_CTRL_REG 0x53 +#define ISABELLE_HFL_LIM_CTRL_1_REG 0x54 +#define ISABELLE_HFL_LIM_CTRL_2_REG 0x55 +#define ISABELLE_HFR_VOL_CTRL_REG 0x56 +#define ISABELLE_HFR_SFTVOL_CTRL_REG 0x57 +#define ISABELLE_HFR_LIM_CTRL_1_REG 0x58 +#define ISABELLE_HFR_LIM_CTRL_2_REG 0x59 +#define ISABELLE_HF_MODE_REG 0x5A +#define ISABELLE_HFLPGA_CFG_REG 0x5B +#define ISABELLE_HFRPGA_CFG_REG 0x5C +#define ISABELLE_HFDRV_CFG_REG 0x5D +#define ISABELLE_PDMOUT_CFG1_REG 0x5E +#define ISABELLE_PDMOUT_CFG2_REG 0x5F +#define ISABELLE_PDMOUT_L_WM_REG 0x60 +#define ISABELLE_PDMOUT_R_WM_REG 0x61 +#define ISABELLE_HF_NG_CFG1_REG 0x62 +#define ISABELLE_HF_NG_CFG2_REG 0x63 + +/* ISABELLE_PWR_EN_REG (0x02h) */ +#define ISABELLE_CHIP_EN BIT(0) + +/* ISABELLE DAI FORMATS */ +#define ISABELLE_AIF_FMT_MASK 0x70 +#define ISABELLE_I2S_MODE 0x0 +#define ISABELLE_LEFT_J_MODE 0x1 +#define ISABELLE_PDM_MODE 0x2 + +#define ISABELLE_AIF_LENGTH_MASK 0x30 +#define ISABELLE_AIF_LENGTH_20 0x00 +#define ISABELLE_AIF_LENGTH_32 0x10 + +#define ISABELLE_AIF_MS 0x80 + +#define ISABELLE_FS_RATE_MASK 0xF +#define ISABELLE_FS_RATE_8 0x0 +#define ISABELLE_FS_RATE_11 0x1 +#define ISABELLE_FS_RATE_12 0x2 +#define ISABELLE_FS_RATE_16 0x4 +#define ISABELLE_FS_RATE_22 0x5 +#define ISABELLE_FS_RATE_24 0x6 +#define ISABELLE_FS_RATE_32 0x8 +#define ISABELLE_FS_RATE_44 0x9 +#define ISABELLE_FS_RATE_48 0xA + +#define ISABELLE_MAX_REGISTER 0xFF + +#endif diff --git a/sound/soc/codecs/jz4725b.c b/sound/soc/codecs/jz4725b.c new file mode 100644 index 0000000000..39cebaa167 --- /dev/null +++ b/sound/soc/codecs/jz4725b.c @@ -0,0 +1,667 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// JZ4725B CODEC driver +// +// Copyright (C) 2019, Paul Cercueil + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define ICDC_RGADW_OFFSET 0x00 +#define ICDC_RGDATA_OFFSET 0x04 + +/* ICDC internal register access control register(RGADW) */ +#define ICDC_RGADW_RGWR BIT(16) + +#define ICDC_RGADW_RGADDR_OFFSET 8 +#define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET) + +#define ICDC_RGADW_RGDIN_OFFSET 0 +#define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET) + +/* ICDC internal register data output register (RGDATA)*/ +#define ICDC_RGDATA_IRQ BIT(8) + +#define ICDC_RGDATA_RGDOUT_OFFSET 0 +#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET) + +/* JZ internal register space */ +enum { + JZ4725B_CODEC_REG_AICR, + JZ4725B_CODEC_REG_CR1, + JZ4725B_CODEC_REG_CR2, + JZ4725B_CODEC_REG_CCR1, + JZ4725B_CODEC_REG_CCR2, + JZ4725B_CODEC_REG_PMR1, + JZ4725B_CODEC_REG_PMR2, + JZ4725B_CODEC_REG_CRR, + JZ4725B_CODEC_REG_ICR, + JZ4725B_CODEC_REG_IFR, + JZ4725B_CODEC_REG_CGR1, + JZ4725B_CODEC_REG_CGR2, + JZ4725B_CODEC_REG_CGR3, + JZ4725B_CODEC_REG_CGR4, + JZ4725B_CODEC_REG_CGR5, + JZ4725B_CODEC_REG_CGR6, + JZ4725B_CODEC_REG_CGR7, + JZ4725B_CODEC_REG_CGR8, + JZ4725B_CODEC_REG_CGR9, + JZ4725B_CODEC_REG_CGR10, + JZ4725B_CODEC_REG_TR1, + JZ4725B_CODEC_REG_TR2, + JZ4725B_CODEC_REG_CR3, + JZ4725B_CODEC_REG_AGC1, + JZ4725B_CODEC_REG_AGC2, + JZ4725B_CODEC_REG_AGC3, + JZ4725B_CODEC_REG_AGC4, + JZ4725B_CODEC_REG_AGC5, +}; + +#define REG_AICR_CONFIG1_OFFSET 0 +#define REG_AICR_CONFIG1_MASK (0xf << REG_AICR_CONFIG1_OFFSET) + +#define REG_CR1_SB_MICBIAS_OFFSET 7 +#define REG_CR1_MONO_OFFSET 6 +#define REG_CR1_DAC_MUTE_OFFSET 5 +#define REG_CR1_HP_DIS_OFFSET 4 +#define REG_CR1_DACSEL_OFFSET 3 +#define REG_CR1_BYPASS_OFFSET 2 + +#define REG_CR2_DAC_DEEMP_OFFSET 7 +#define REG_CR2_DAC_ADWL_OFFSET 5 +#define REG_CR2_DAC_ADWL_MASK (0x3 << REG_CR2_DAC_ADWL_OFFSET) +#define REG_CR2_ADC_ADWL_OFFSET 3 +#define REG_CR2_ADC_ADWL_MASK (0x3 << REG_CR2_ADC_ADWL_OFFSET) +#define REG_CR2_ADC_HPF_OFFSET 2 + +#define REG_CR3_SB_MIC1_OFFSET 7 +#define REG_CR3_SB_MIC2_OFFSET 6 +#define REG_CR3_SIDETONE1_OFFSET 5 +#define REG_CR3_SIDETONE2_OFFSET 4 +#define REG_CR3_MICDIFF_OFFSET 3 +#define REG_CR3_MICSTEREO_OFFSET 2 +#define REG_CR3_INSEL_OFFSET 0 +#define REG_CR3_INSEL_MASK (0x3 << REG_CR3_INSEL_OFFSET) + +#define REG_CCR1_CONFIG4_OFFSET 0 +#define REG_CCR1_CONFIG4_MASK (0xf << REG_CCR1_CONFIG4_OFFSET) + +#define REG_CCR2_DFREQ_OFFSET 4 +#define REG_CCR2_DFREQ_MASK (0xf << REG_CCR2_DFREQ_OFFSET) +#define REG_CCR2_AFREQ_OFFSET 0 +#define REG_CCR2_AFREQ_MASK (0xf << REG_CCR2_AFREQ_OFFSET) + +#define REG_PMR1_SB_DAC_OFFSET 7 +#define REG_PMR1_SB_OUT_OFFSET 6 +#define REG_PMR1_SB_MIX_OFFSET 5 +#define REG_PMR1_SB_ADC_OFFSET 4 +#define REG_PMR1_SB_LIN_OFFSET 3 +#define REG_PMR1_SB_IND_OFFSET 0 + +#define REG_PMR2_LRGI_OFFSET 7 +#define REG_PMR2_RLGI_OFFSET 6 +#define REG_PMR2_LRGOD_OFFSET 5 +#define REG_PMR2_RLGOD_OFFSET 4 +#define REG_PMR2_GIM_OFFSET 3 +#define REG_PMR2_SB_MC_OFFSET 2 +#define REG_PMR2_SB_OFFSET 1 +#define REG_PMR2_SB_SLEEP_OFFSET 0 + +#define REG_IFR_RAMP_UP_DONE_OFFSET 3 +#define REG_IFR_RAMP_DOWN_DONE_OFFSET 2 + +#define REG_CGR1_GODL_OFFSET 4 +#define REG_CGR1_GODL_MASK (0xf << REG_CGR1_GODL_OFFSET) +#define REG_CGR1_GODR_OFFSET 0 +#define REG_CGR1_GODR_MASK (0xf << REG_CGR1_GODR_OFFSET) + +#define REG_CGR2_GO1R_OFFSET 0 +#define REG_CGR2_GO1R_MASK (0x1f << REG_CGR2_GO1R_OFFSET) + +#define REG_CGR3_GO1L_OFFSET 0 +#define REG_CGR3_GO1L_MASK (0x1f << REG_CGR3_GO1L_OFFSET) + +#define REG_CGR4_GO2R_OFFSET 0 +#define REG_CGR4_GO2R_MASK (0x1f << REG_CGR4_GO2R_OFFSET) + +#define REG_CGR5_GO2L_OFFSET 0 +#define REG_CGR5_GO2L_MASK (0x1f << REG_CGR5_GO2L_OFFSET) + +#define REG_CGR6_GO3R_OFFSET 0 +#define REG_CGR6_GO3R_MASK (0x1f << REG_CGR6_GO3R_OFFSET) + +#define REG_CGR7_GO3L_OFFSET 0 +#define REG_CGR7_GO3L_MASK (0x1f << REG_CGR7_GO3L_OFFSET) + +#define REG_CGR8_GOR_OFFSET 0 +#define REG_CGR8_GOR_MASK (0x1f << REG_CGR8_GOR_OFFSET) + +#define REG_CGR9_GOL_OFFSET 0 +#define REG_CGR9_GOL_MASK (0x1f << REG_CGR9_GOL_OFFSET) + +#define REG_CGR10_GIL_OFFSET 0 +#define REG_CGR10_GIR_OFFSET 4 + +struct jz_icdc { + struct regmap *regmap; + void __iomem *base; + struct clk *clk; +}; + +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_adc_tlv, 0, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_dac_tlv, -2250, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(jz4725b_mix_tlv, + 0, 11, TLV_DB_SCALE_ITEM(-2250, 0, 0), + 12, 31, TLV_DB_SCALE_ITEM(-2250, 150, 0), +); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(jz4725b_out_tlv, + 0, 11, TLV_DB_SCALE_ITEM(-3350, 200, 0), + 12, 23, TLV_DB_SCALE_ITEM(-1050, 100, 0), + 24, 31, TLV_DB_SCALE_ITEM( 100, 50, 0), +); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(jz4725b_mic_boost_tlv, 0, 2000, 0); + +static const char * const jz4725b_mic_mode_texts[] = { + "Single Ended", "Differential", +}; + +static const struct soc_enum jz4725b_mic_mode_enum = + SOC_ENUM_SINGLE(JZ4725B_CODEC_REG_CR3, REG_CR3_MICDIFF_OFFSET, + 2, jz4725b_mic_mode_texts); + +static const struct snd_kcontrol_new jz4725b_codec_controls[] = { + SOC_DOUBLE_TLV("DAC Playback Volume", + JZ4725B_CODEC_REG_CGR1, + REG_CGR1_GODL_OFFSET, + REG_CGR1_GODR_OFFSET, + 0xf, 1, jz4725b_dac_tlv), + SOC_DOUBLE_TLV("Master Capture Volume", + JZ4725B_CODEC_REG_CGR10, + REG_CGR10_GIL_OFFSET, + REG_CGR10_GIR_OFFSET, + 0xf, 0, jz4725b_adc_tlv), + SOC_DOUBLE_R_TLV("Mixer Line In Bypass Playback Volume", + JZ4725B_CODEC_REG_CGR3, + JZ4725B_CODEC_REG_CGR2, + REG_CGR2_GO1R_OFFSET, + 0x1f, 1, jz4725b_mix_tlv), + SOC_DOUBLE_R_TLV("Mixer Mic 1 Bypass Playback Volume", + JZ4725B_CODEC_REG_CGR5, + JZ4725B_CODEC_REG_CGR4, + REG_CGR4_GO2R_OFFSET, + 0x1f, 1, jz4725b_mix_tlv), + SOC_DOUBLE_R_TLV("Mixer Mic 2 Bypass Playback Volume", + JZ4725B_CODEC_REG_CGR7, + JZ4725B_CODEC_REG_CGR6, + REG_CGR6_GO3R_OFFSET, + 0x1f, 1, jz4725b_mix_tlv), + + SOC_DOUBLE_R_TLV("Master Playback Volume", + JZ4725B_CODEC_REG_CGR9, + JZ4725B_CODEC_REG_CGR8, + REG_CGR8_GOR_OFFSET, + 0x1f, 1, jz4725b_out_tlv), + + SOC_SINGLE("DAC Playback Switch", JZ4725B_CODEC_REG_CR1, + REG_CR1_DAC_MUTE_OFFSET, 1, 1), + + SOC_SINGLE("Deemphasize Filter Playback Switch", + JZ4725B_CODEC_REG_CR2, + REG_CR2_DAC_DEEMP_OFFSET, 1, 0), + + SOC_SINGLE("High-Pass Filter Capture Switch", + JZ4725B_CODEC_REG_CR2, + REG_CR2_ADC_HPF_OFFSET, 1, 0), + + SOC_ENUM("Mic Mode Capture Switch", jz4725b_mic_mode_enum), + + SOC_SINGLE_TLV("Mic1 Boost Capture Volume", + JZ4725B_CODEC_REG_PMR2, + REG_PMR2_GIM_OFFSET, + 1, 0, jz4725b_mic_boost_tlv), +}; + +static const char * const jz4725b_codec_adc_src_texts[] = { + "Mic 1", "Mic 2", "Line In", "Mixer", +}; +static const unsigned int jz4725b_codec_adc_src_values[] = { 0, 1, 2, 3, }; +static SOC_VALUE_ENUM_SINGLE_DECL(jz4725b_codec_adc_src_enum, + JZ4725B_CODEC_REG_CR3, + REG_CR3_INSEL_OFFSET, + REG_CR3_INSEL_MASK, + jz4725b_codec_adc_src_texts, + jz4725b_codec_adc_src_values); +static const struct snd_kcontrol_new jz4725b_codec_adc_src_ctrl = + SOC_DAPM_ENUM("ADC Source Capture Route", jz4725b_codec_adc_src_enum); + +static const struct snd_kcontrol_new jz4725b_codec_mixer_controls[] = { + SOC_DAPM_SINGLE("Line In Bypass Playback Switch", JZ4725B_CODEC_REG_CR1, + REG_CR1_BYPASS_OFFSET, 1, 0), + SOC_DAPM_SINGLE("Mic 1 Bypass Playback Switch", JZ4725B_CODEC_REG_CR3, + REG_CR3_SIDETONE1_OFFSET, 1, 0), + SOC_DAPM_SINGLE("Mic 2 Bypass Playback Switch", JZ4725B_CODEC_REG_CR3, + REG_CR3_SIDETONE2_OFFSET, 1, 0), +}; + +static int jz4725b_out_stage_enable(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct jz_icdc *icdc = snd_soc_component_get_drvdata(codec); + struct regmap *map = icdc->regmap; + unsigned int val; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return regmap_clear_bits(map, JZ4725B_CODEC_REG_IFR, + BIT(REG_IFR_RAMP_UP_DONE_OFFSET)); + case SND_SOC_DAPM_POST_PMU: + return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR, + val, val & BIT(REG_IFR_RAMP_UP_DONE_OFFSET), + 100000, 500000); + case SND_SOC_DAPM_PRE_PMD: + return regmap_clear_bits(map, JZ4725B_CODEC_REG_IFR, + BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET)); + case SND_SOC_DAPM_POST_PMD: + return regmap_read_poll_timeout(map, JZ4725B_CODEC_REG_IFR, + val, val & BIT(REG_IFR_RAMP_DOWN_DONE_OFFSET), + 100000, 500000); + default: + return -EINVAL; + } +} + +static const struct snd_soc_dapm_widget jz4725b_codec_dapm_widgets[] = { + /* DAC */ + SND_SOC_DAPM_DAC("DAC", "Playback", + JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_DAC_OFFSET, 1), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC", "Capture", + JZ4725B_CODEC_REG_PMR1, REG_PMR1_SB_ADC_OFFSET, 1), + + SND_SOC_DAPM_MUX("ADC Source Capture Route", SND_SOC_NOPM, 0, 0, + &jz4725b_codec_adc_src_ctrl), + + /* Mixer */ + SND_SOC_DAPM_MIXER("Mixer", JZ4725B_CODEC_REG_PMR1, + REG_PMR1_SB_MIX_OFFSET, 1, + jz4725b_codec_mixer_controls, + ARRAY_SIZE(jz4725b_codec_mixer_controls)), + SND_SOC_DAPM_MIXER("DAC to Mixer", JZ4725B_CODEC_REG_CR1, + REG_CR1_DACSEL_OFFSET, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Line In", JZ4725B_CODEC_REG_PMR1, + REG_PMR1_SB_LIN_OFFSET, 1, NULL, 0), + SND_SOC_DAPM_MIXER("HP Out", JZ4725B_CODEC_REG_CR1, + REG_CR1_HP_DIS_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_MIXER("Mic 1", JZ4725B_CODEC_REG_CR3, + REG_CR3_SB_MIC1_OFFSET, 1, NULL, 0), + SND_SOC_DAPM_MIXER("Mic 2", JZ4725B_CODEC_REG_CR3, + REG_CR3_SB_MIC2_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_MIXER_E("Out Stage", JZ4725B_CODEC_REG_PMR1, + REG_PMR1_SB_OUT_OFFSET, 1, NULL, 0, + jz4725b_out_stage_enable, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("Mixer to ADC", JZ4725B_CODEC_REG_PMR1, + REG_PMR1_SB_IND_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", JZ4725B_CODEC_REG_CR1, + REG_CR1_SB_MICBIAS_OFFSET, 1, NULL, 0), + + /* Pins */ + SND_SOC_DAPM_INPUT("MIC1P"), + SND_SOC_DAPM_INPUT("MIC1N"), + SND_SOC_DAPM_INPUT("MIC2P"), + SND_SOC_DAPM_INPUT("MIC2N"), + + SND_SOC_DAPM_INPUT("LLINEIN"), + SND_SOC_DAPM_INPUT("RLINEIN"), + + SND_SOC_DAPM_OUTPUT("LHPOUT"), + SND_SOC_DAPM_OUTPUT("RHPOUT"), +}; + +static const struct snd_soc_dapm_route jz4725b_codec_dapm_routes[] = { + {"Mic 1", NULL, "MIC1P"}, + {"Mic 1", NULL, "MIC1N"}, + {"Mic 2", NULL, "MIC2P"}, + {"Mic 2", NULL, "MIC2N"}, + + {"Line In", NULL, "LLINEIN"}, + {"Line In", NULL, "RLINEIN"}, + + {"Mixer", "Mic 1 Bypass Playback Switch", "Mic 1"}, + {"Mixer", "Mic 2 Bypass Playback Switch", "Mic 2"}, + {"Mixer", "Line In Bypass Playback Switch", "Line In"}, + {"DAC to Mixer", NULL, "DAC"}, + {"Mixer", NULL, "DAC to Mixer"}, + + {"Mixer to ADC", NULL, "Mixer"}, + {"ADC Source Capture Route", "Mixer", "Mixer to ADC"}, + {"ADC Source Capture Route", "Line In", "Line In"}, + {"ADC Source Capture Route", "Mic 1", "Mic 1"}, + {"ADC Source Capture Route", "Mic 2", "Mic 2"}, + {"ADC", NULL, "ADC Source Capture Route"}, + + {"Out Stage", NULL, "Mixer"}, + {"HP Out", NULL, "Out Stage"}, + {"LHPOUT", NULL, "HP Out"}, + {"RHPOUT", NULL, "HP Out"}, +}; + +static int jz4725b_codec_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); + struct regmap *map = icdc->regmap; + + switch (level) { + case SND_SOC_BIAS_ON: + regmap_clear_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_SLEEP_OFFSET)); + break; + case SND_SOC_BIAS_PREPARE: + /* Enable sound hardware */ + regmap_clear_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_OFFSET)); + msleep(224); + break; + case SND_SOC_BIAS_STANDBY: + regmap_set_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_SLEEP_OFFSET)); + break; + case SND_SOC_BIAS_OFF: + regmap_set_bits(map, JZ4725B_CODEC_REG_PMR2, + BIT(REG_PMR2_SB_OFFSET)); + break; + } + + return 0; +} + +static int jz4725b_codec_dev_probe(struct snd_soc_component *component) +{ + struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); + struct regmap *map = icdc->regmap; + + clk_prepare_enable(icdc->clk); + + /* Write CONFIGn (n=1 to 8) bits. + * The value 0x0f is specified in the datasheet as a requirement. + */ + regmap_write(map, JZ4725B_CODEC_REG_AICR, + 0xf << REG_AICR_CONFIG1_OFFSET); + regmap_write(map, JZ4725B_CODEC_REG_CCR1, + 0x0 << REG_CCR1_CONFIG4_OFFSET); + + return 0; +} + +static void jz4725b_codec_dev_remove(struct snd_soc_component *component) +{ + struct jz_icdc *icdc = snd_soc_component_get_drvdata(component); + + clk_disable_unprepare(icdc->clk); +} + +static const struct snd_soc_component_driver jz4725b_codec = { + .probe = jz4725b_codec_dev_probe, + .remove = jz4725b_codec_dev_remove, + .set_bias_level = jz4725b_codec_set_bias_level, + .controls = jz4725b_codec_controls, + .num_controls = ARRAY_SIZE(jz4725b_codec_controls), + .dapm_widgets = jz4725b_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(jz4725b_codec_dapm_widgets), + .dapm_routes = jz4725b_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(jz4725b_codec_dapm_routes), + .suspend_bias_off = 1, + .use_pmdown_time = 1, +}; + +static const unsigned int jz4725b_codec_sample_rates[] = { + 96000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, + 11025, 9600, 8000, +}; + +static int jz4725b_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct jz_icdc *icdc = snd_soc_component_get_drvdata(dai->component); + unsigned int rate, bit_width; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + bit_width = 0; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + bit_width = 1; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + bit_width = 2; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bit_width = 3; + break; + default: + return -EINVAL; + } + + for (rate = 0; rate < ARRAY_SIZE(jz4725b_codec_sample_rates); rate++) { + if (jz4725b_codec_sample_rates[rate] == params_rate(params)) + break; + } + + if (rate == ARRAY_SIZE(jz4725b_codec_sample_rates)) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(icdc->regmap, + JZ4725B_CODEC_REG_CR2, + REG_CR2_DAC_ADWL_MASK, + bit_width << REG_CR2_DAC_ADWL_OFFSET); + + regmap_update_bits(icdc->regmap, + JZ4725B_CODEC_REG_CCR2, + REG_CCR2_DFREQ_MASK, + rate << REG_CCR2_DFREQ_OFFSET); + } else { + regmap_update_bits(icdc->regmap, + JZ4725B_CODEC_REG_CR2, + REG_CR2_ADC_ADWL_MASK, + bit_width << REG_CR2_ADC_ADWL_OFFSET); + + regmap_update_bits(icdc->regmap, + JZ4725B_CODEC_REG_CCR2, + REG_CCR2_AFREQ_MASK, + rate << REG_CCR2_AFREQ_OFFSET); + } + + return 0; +} + +static const struct snd_soc_dai_ops jz4725b_codec_dai_ops = { + .hw_params = jz4725b_codec_hw_params, +}; + +#define JZ_ICDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE) + +static struct snd_soc_dai_driver jz4725b_codec_dai = { + .name = "jz4725b-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = JZ_ICDC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = JZ_ICDC_FORMATS, + }, + .ops = &jz4725b_codec_dai_ops, +}; + +static bool jz4725b_codec_volatile(struct device *dev, unsigned int reg) +{ + return reg == JZ4725B_CODEC_REG_IFR; +} + +static bool jz4725b_codec_can_access_reg(struct device *dev, unsigned int reg) +{ + return (reg != JZ4725B_CODEC_REG_TR1) && (reg != JZ4725B_CODEC_REG_TR2); +} + +static int jz4725b_codec_io_wait(struct jz_icdc *icdc) +{ + u32 reg; + + return readl_poll_timeout(icdc->base + ICDC_RGADW_OFFSET, reg, + !(reg & ICDC_RGADW_RGWR), 1000, 10000); +} + +static int jz4725b_codec_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct jz_icdc *icdc = context; + unsigned int i; + u32 tmp; + int ret; + + ret = jz4725b_codec_io_wait(icdc); + if (ret) + return ret; + + tmp = readl(icdc->base + ICDC_RGADW_OFFSET); + tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK) + | (reg << ICDC_RGADW_RGADDR_OFFSET); + writel(tmp, icdc->base + ICDC_RGADW_OFFSET); + + /* wait 6+ cycles */ + for (i = 0; i < 6; i++) + *val = readl(icdc->base + ICDC_RGDATA_OFFSET) & + ICDC_RGDATA_RGDOUT_MASK; + + return 0; +} + +static int jz4725b_codec_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct jz_icdc *icdc = context; + int ret; + + ret = jz4725b_codec_io_wait(icdc); + if (ret) + return ret; + + writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val, + icdc->base + ICDC_RGADW_OFFSET); + + ret = jz4725b_codec_io_wait(icdc); + if (ret) + return ret; + + return 0; +} + +static const u8 jz4725b_codec_reg_defaults[] = { + 0x0c, 0xaa, 0x78, 0x00, 0x00, 0xff, 0x03, 0x51, + 0x3f, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0xc0, 0x34, + 0x07, 0x44, 0x1f, 0x00, +}; + +static const struct regmap_config jz4725b_codec_regmap_config = { + .reg_bits = 7, + .val_bits = 8, + + .max_register = JZ4725B_CODEC_REG_AGC5, + .volatile_reg = jz4725b_codec_volatile, + .readable_reg = jz4725b_codec_can_access_reg, + .writeable_reg = jz4725b_codec_can_access_reg, + + .reg_read = jz4725b_codec_reg_read, + .reg_write = jz4725b_codec_reg_write, + + .reg_defaults_raw = jz4725b_codec_reg_defaults, + .num_reg_defaults_raw = ARRAY_SIZE(jz4725b_codec_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static int jz4725b_codec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct jz_icdc *icdc; + int ret; + + icdc = devm_kzalloc(dev, sizeof(*icdc), GFP_KERNEL); + if (!icdc) + return -ENOMEM; + + icdc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(icdc->base)) + return PTR_ERR(icdc->base); + + icdc->regmap = devm_regmap_init(dev, NULL, icdc, + &jz4725b_codec_regmap_config); + if (IS_ERR(icdc->regmap)) + return PTR_ERR(icdc->regmap); + + icdc->clk = devm_clk_get(&pdev->dev, "aic"); + if (IS_ERR(icdc->clk)) + return PTR_ERR(icdc->clk); + + platform_set_drvdata(pdev, icdc); + + ret = devm_snd_soc_register_component(dev, &jz4725b_codec, + &jz4725b_codec_dai, 1); + if (ret) + dev_err(dev, "Failed to register codec\n"); + + return ret; +} + +static const struct of_device_id jz4725b_codec_of_matches[] = { + { .compatible = "ingenic,jz4725b-codec", }, + { } +}; +MODULE_DEVICE_TABLE(of, jz4725b_codec_of_matches); + +static struct platform_driver jz4725b_codec_driver = { + .probe = jz4725b_codec_probe, + .driver = { + .name = "jz4725b-codec", + .of_match_table = jz4725b_codec_of_matches, + }, +}; +module_platform_driver(jz4725b_codec_driver); + +MODULE_DESCRIPTION("JZ4725B SoC internal codec driver"); +MODULE_AUTHOR("Paul Cercueil "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c new file mode 100644 index 0000000000..d1cea93bdb --- /dev/null +++ b/sound/soc/codecs/jz4740.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// JZ4740 CODEC driver +// +// Copyright (C) 2009-2010, Lars-Peter Clausen + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define JZ4740_REG_CODEC_1 0x0 +#define JZ4740_REG_CODEC_2 0x4 + +#define JZ4740_CODEC_1_LINE_ENABLE BIT(29) +#define JZ4740_CODEC_1_MIC_ENABLE BIT(28) +#define JZ4740_CODEC_1_SW1_ENABLE BIT(27) +#define JZ4740_CODEC_1_ADC_ENABLE BIT(26) +#define JZ4740_CODEC_1_SW2_ENABLE BIT(25) +#define JZ4740_CODEC_1_DAC_ENABLE BIT(24) +#define JZ4740_CODEC_1_VREF_DISABLE BIT(20) +#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19) +#define JZ4740_CODEC_1_VREF_PULLDOWN BIT(18) +#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17) +#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16) +#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14) +#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13) +#define JZ4740_CODEC_1_HEADPHONE_CHARGE BIT(12) +#define JZ4740_CODEC_1_HEADPHONE_PULLDOWN (BIT(11) | BIT(10)) +#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M BIT(9) +#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN BIT(8) +#define JZ4740_CODEC_1_SUSPEND BIT(1) +#define JZ4740_CODEC_1_RESET BIT(0) + +#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29 +#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28 +#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27 +#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26 +#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25 +#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24 +#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14 +#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET 8 + +#define JZ4740_CODEC_2_INPUT_VOLUME_MASK 0x1f0000 +#define JZ4740_CODEC_2_SAMPLE_RATE_MASK 0x000f00 +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK 0x000030 +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK 0x000003 + +#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET 16 +#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET 8 +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4 +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0 + +static const struct reg_default jz4740_codec_reg_defaults[] = { + { JZ4740_REG_CODEC_1, 0x021b2302 }, + { JZ4740_REG_CODEC_2, 0x00170803 }, +}; + +struct jz4740_codec { + struct regmap *regmap; +}; + +static const DECLARE_TLV_DB_RANGE(jz4740_mic_tlv, + 0, 2, TLV_DB_SCALE_ITEM(0, 600, 0), + 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0) +); + +static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0); +static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0); + +static const struct snd_kcontrol_new jz4740_codec_controls[] = { + SOC_SINGLE_TLV("Master Playback Volume", JZ4740_REG_CODEC_2, + JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0, + jz4740_out_tlv), + SOC_SINGLE_TLV("Master Capture Volume", JZ4740_REG_CODEC_2, + JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0, + jz4740_in_tlv), + SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1), + SOC_SINGLE_TLV("Mic Capture Volume", JZ4740_REG_CODEC_2, + JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0, + jz4740_mic_tlv), +}; + +static const struct snd_kcontrol_new jz4740_codec_output_controls[] = { + SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0), + SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0), +}; + +static const struct snd_kcontrol_new jz4740_codec_input_controls[] = { + SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0), + SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0), +}; + +static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = { + SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0), + SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0), + + SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET, 1, + jz4740_codec_output_controls, + ARRAY_SIZE(jz4740_codec_output_controls)), + + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0, + jz4740_codec_input_controls, + ARRAY_SIZE(jz4740_codec_input_controls)), + SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), + + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_INPUT("LIN"), + SND_SOC_DAPM_INPUT("RIN"), +}; + +static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = { + {"Line Input", NULL, "LIN"}, + {"Line Input", NULL, "RIN"}, + + {"Input Mixer", "Line Capture Switch", "Line Input"}, + {"Input Mixer", "Mic Capture Switch", "MIC"}, + + {"ADC", NULL, "Input Mixer"}, + + {"Output Mixer", "Bypass Switch", "Input Mixer"}, + {"Output Mixer", "DAC Switch", "DAC"}, + + {"LOUT", NULL, "Output Mixer"}, + {"ROUT", NULL, "Output Mixer"}, +}; + +static int jz4740_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(dai->component); + uint32_t val; + + switch (params_rate(params)) { + case 8000: + val = 0; + break; + case 11025: + val = 1; + break; + case 12000: + val = 2; + break; + case 16000: + val = 3; + break; + case 22050: + val = 4; + break; + case 24000: + val = 5; + break; + case 32000: + val = 6; + break; + case 44100: + val = 7; + break; + case 48000: + val = 8; + break; + default: + return -EINVAL; + } + + val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET; + + regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_2, + JZ4740_CODEC_2_SAMPLE_RATE_MASK, val); + + return 0; +} + +static const struct snd_soc_dai_ops jz4740_codec_dai_ops = { + .hw_params = jz4740_codec_hw_params, +}; + +static struct snd_soc_dai_driver jz4740_codec_dai = { + .name = "jz4740-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8, + }, + .ops = &jz4740_codec_dai_ops, + .symmetric_rate = 1, +}; + +static void jz4740_codec_wakeup(struct regmap *regmap) +{ + regmap_set_bits(regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_RESET); + udelay(2); + + regmap_clear_bits(regmap, JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET); + + regcache_sync(regmap); +} + +static int jz4740_codec_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(component); + struct regmap *regmap = jz4740_codec->regmap; + unsigned int mask; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + mask = JZ4740_CODEC_1_VREF_DISABLE | + JZ4740_CODEC_1_VREF_AMP_DISABLE | + JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; + + regmap_clear_bits(regmap, JZ4740_REG_CODEC_1, mask); + break; + case SND_SOC_BIAS_STANDBY: + /* The only way to clear the suspend flag is to reset the codec */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + jz4740_codec_wakeup(regmap); + + mask = JZ4740_CODEC_1_VREF_DISABLE | + JZ4740_CODEC_1_VREF_AMP_DISABLE | + JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; + + regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask); + break; + case SND_SOC_BIAS_OFF: + mask = JZ4740_CODEC_1_SUSPEND; + regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask); + regcache_mark_dirty(regmap); + break; + default: + break; + } + + return 0; +} + +static int jz4740_codec_dev_probe(struct snd_soc_component *component) +{ + struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(component); + + regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1, + JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE); + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_jz4740_codec = { + .probe = jz4740_codec_dev_probe, + .set_bias_level = jz4740_codec_set_bias_level, + .controls = jz4740_codec_controls, + .num_controls = ARRAY_SIZE(jz4740_codec_controls), + .dapm_widgets = jz4740_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets), + .dapm_routes = jz4740_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config jz4740_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = JZ4740_REG_CODEC_2, + + .reg_defaults = jz4740_codec_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static int jz4740_codec_probe(struct platform_device *pdev) +{ + int ret; + struct jz4740_codec *jz4740_codec; + void __iomem *base; + + jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec), + GFP_KERNEL); + if (!jz4740_codec) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + jz4740_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &jz4740_codec_regmap_config); + if (IS_ERR(jz4740_codec->regmap)) + return PTR_ERR(jz4740_codec->regmap); + + platform_set_drvdata(pdev, jz4740_codec); + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_codec_dev_jz4740_codec, &jz4740_codec_dai, 1); + if (ret) + dev_err(&pdev->dev, "Failed to register codec\n"); + + return ret; +} + +static const struct of_device_id jz4740_codec_of_matches[] = { + { .compatible = "ingenic,jz4740-codec", }, + { } +}; +MODULE_DEVICE_TABLE(of, jz4740_codec_of_matches); + +static struct platform_driver jz4740_codec_driver = { + .probe = jz4740_codec_probe, + .driver = { + .name = "jz4740-codec", + .of_match_table = jz4740_codec_of_matches, + }, +}; + +module_platform_driver(jz4740_codec_driver); + +MODULE_DESCRIPTION("JZ4740 SoC internal codec driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:jz4740-codec"); diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c new file mode 100644 index 0000000000..9df58e23d3 --- /dev/null +++ b/sound/soc/codecs/jz4760.c @@ -0,0 +1,895 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Ingenic JZ4760 CODEC driver +// +// Copyright (C) 2021, Christophe Branchereau +// Copyright (C) 2021, Paul Cercueil + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define ICDC_RGADW_OFFSET 0x00 +#define ICDC_RGDATA_OFFSET 0x04 + +/* ICDC internal register access control register(RGADW) */ +#define ICDC_RGADW_RGWR BIT(16) +#define ICDC_RGADW_RGADDR_MASK GENMASK(14, 8) +#define ICDC_RGADW_RGDIN_MASK GENMASK(7, 0) + +/* ICDC internal register data output register (RGDATA)*/ +#define ICDC_RGDATA_IRQ BIT(8) +#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, 0) + +/* Internal register space, accessed through regmap */ +enum { + JZ4760_CODEC_REG_SR, + JZ4760_CODEC_REG_AICR, + JZ4760_CODEC_REG_CR1, + JZ4760_CODEC_REG_CR2, + JZ4760_CODEC_REG_CR3, + JZ4760_CODEC_REG_CR4, + JZ4760_CODEC_REG_CCR1, + JZ4760_CODEC_REG_CCR2, + JZ4760_CODEC_REG_PMR1, + JZ4760_CODEC_REG_PMR2, + JZ4760_CODEC_REG_ICR, + JZ4760_CODEC_REG_IFR, + JZ4760_CODEC_REG_GCR1, + JZ4760_CODEC_REG_GCR2, + JZ4760_CODEC_REG_GCR3, + JZ4760_CODEC_REG_GCR4, + JZ4760_CODEC_REG_GCR5, + JZ4760_CODEC_REG_GCR6, + JZ4760_CODEC_REG_GCR7, + JZ4760_CODEC_REG_GCR8, + JZ4760_CODEC_REG_GCR9, + JZ4760_CODEC_REG_AGC1, + JZ4760_CODEC_REG_AGC2, + JZ4760_CODEC_REG_AGC3, + JZ4760_CODEC_REG_AGC4, + JZ4760_CODEC_REG_AGC5, + JZ4760_CODEC_REG_MIX1, + JZ4760_CODEC_REG_MIX2, +}; + +#define REG_AICR_DAC_ADWL_MASK GENMASK(7, 6) +#define REG_AICR_DAC_SERIAL BIT(3) +#define REG_AICR_DAC_I2S BIT(1) + +#define REG_AICR_ADC_ADWL_MASK GENMASK(5, 4) + +#define REG_AICR_ADC_SERIAL BIT(2) +#define REG_AICR_ADC_I2S BIT(0) + +#define REG_CR1_HP_LOAD BIT(7) +#define REG_CR1_HP_MUTE BIT(5) +#define REG_CR1_LO_MUTE_OFFSET 4 +#define REG_CR1_BTL_MUTE_OFFSET 3 +#define REG_CR1_OUTSEL_OFFSET 0 +#define REG_CR1_OUTSEL_MASK GENMASK(1, REG_CR1_OUTSEL_OFFSET) + +#define REG_CR2_DAC_MONO BIT(7) +#define REG_CR2_DAC_MUTE BIT(5) +#define REG_CR2_DAC_NOMAD BIT(1) +#define REG_CR2_DAC_RIGHT_ONLY BIT(0) + +#define REG_CR3_ADC_INSEL_OFFSET 2 +#define REG_CR3_ADC_INSEL_MASK GENMASK(3, REG_CR3_ADC_INSEL_OFFSET) +#define REG_CR3_MICSTEREO_OFFSET 1 +#define REG_CR3_MICDIFF_OFFSET 0 + +#define REG_CR4_ADC_HPF_OFFSET 7 +#define REG_CR4_ADC_RIGHT_ONLY BIT(0) + +#define REG_CCR1_CRYSTAL_MASK GENMASK(3, 0) + +#define REG_CCR2_DAC_FREQ_MASK GENMASK(7, 4) +#define REG_CCR2_ADC_FREQ_MASK GENMASK(3, 0) + +#define REG_PMR1_SB BIT(7) +#define REG_PMR1_SB_SLEEP BIT(6) +#define REG_PMR1_SB_AIP_OFFSET 5 +#define REG_PMR1_SB_LINE_OFFSET 4 +#define REG_PMR1_SB_MIC1_OFFSET 3 +#define REG_PMR1_SB_MIC2_OFFSET 2 +#define REG_PMR1_SB_BYPASS_OFFSET 1 +#define REG_PMR1_SB_MICBIAS_OFFSET 0 + +#define REG_PMR2_SB_ADC_OFFSET 4 +#define REG_PMR2_SB_HP_OFFSET 3 +#define REG_PMR2_SB_BTL_OFFSET 2 +#define REG_PMR2_SB_LOUT_OFFSET 1 +#define REG_PMR2_SB_DAC_OFFSET 0 + +#define REG_ICR_INT_FORM_MASK GENMASK(7, 6) +#define REG_ICR_ALL_MASK GENMASK(5, 0) +#define REG_ICR_JACK_MASK BIT(5) +#define REG_ICR_SCMC_MASK BIT(4) +#define REG_ICR_RUP_MASK BIT(3) +#define REG_ICR_RDO_MASK BIT(2) +#define REG_ICR_GUP_MASK BIT(1) +#define REG_ICR_GDO_MASK BIT(0) + +#define REG_IFR_ALL_MASK GENMASK(5, 0) +#define REG_IFR_JACK BIT(6) +#define REG_IFR_JACK_EVENT BIT(5) +#define REG_IFR_SCMC BIT(4) +#define REG_IFR_RUP BIT(3) +#define REG_IFR_RDO BIT(2) +#define REG_IFR_GUP BIT(1) +#define REG_IFR_GDO BIT(0) + +#define REG_GCR_GAIN_OFFSET 0 +#define REG_GCR_GAIN_MAX 0x1f + +#define REG_GCR_RL BIT(7) + +#define REG_GCR_GIM1_MASK GENMASK(5, 3) +#define REG_GCR_GIM2_MASK GENMASK(2, 0) +#define REG_GCR_GIM_GAIN_MAX 7 + +#define REG_AGC1_EN BIT(7) +#define REG_AGC1_TARGET_MASK GENMASK(5, 2) + +#define REG_AGC2_NG_THR_MASK GENMASK(6, 4) +#define REG_AGC2_HOLD_MASK GENMASK(3, 0) + +#define REG_AGC3_ATK_MASK GENMASK(7, 4) +#define REG_AGC3_DCY_MASK GENMASK(3, 0) + +#define REG_AGC4_AGC_MAX_MASK GENMASK(4, 0) + +#define REG_AGC5_AGC_MIN_MASK GENMASK(4, 0) + +#define REG_MIX1_MIX_REC_MASK GENMASK(7, 6) +#define REG_MIX1_GIMIX_MASK GENMASK(4, 0) + +#define REG_MIX2_DAC_MIX_MASK GENMASK(7, 6) +#define REG_MIX2_GOMIX_MASK GENMASK(4, 0) + +/* codec private data */ +struct jz_codec { + struct device *dev; + struct regmap *regmap; + void __iomem *base; + struct clk *clk; +}; + +static int jz4760_codec_set_bias_level(struct snd_soc_component *codec, + enum snd_soc_bias_level level) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + struct regmap *regmap = jz_codec->regmap; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + /* Reset all interrupt flags. */ + regmap_write(regmap, JZ4760_CODEC_REG_IFR, REG_IFR_ALL_MASK); + + regmap_clear_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB); + msleep(250); + regmap_clear_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB_SLEEP); + msleep(400); + break; + case SND_SOC_BIAS_STANDBY: + regmap_set_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB_SLEEP); + regmap_set_bits(regmap, JZ4760_CODEC_REG_PMR1, REG_PMR1_SB); + break; + default: + break; + } + + return 0; +} + +static int jz4760_codec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + int ret = 0; + + /* + * SYSCLK output from the codec to the AIC is required to keep the + * DMA transfer going during playback when all audible outputs have + * been disabled. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = snd_soc_dapm_force_enable_pin(dapm, "SYSCLK"); + return ret; +} + +static void jz4760_codec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_disable_pin(dapm, "SYSCLK"); +} + + +static int jz4760_codec_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_component_force_bias_level(codec, SND_SOC_BIAS_ON); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* do nothing */ + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int jz4760_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *codec = dai->component; + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP; + unsigned int val, reg; + int change, err; + + change = snd_soc_component_update_bits(codec, JZ4760_CODEC_REG_CR2, + REG_CR2_DAC_MUTE, + mute ? REG_CR2_DAC_MUTE : 0); + if (change == 1) { + regmap_read(jz_codec->regmap, JZ4760_CODEC_REG_PMR2, &val); + + if (val & BIT(REG_PMR2_SB_DAC_OFFSET)) + return 1; + + err = regmap_read_poll_timeout(jz_codec->regmap, + JZ4760_CODEC_REG_IFR, + val, val & gain_bit, + 1000, 1 * USEC_PER_SEC); + if (err) { + dev_err(jz_codec->dev, + "Timeout while setting digital mute: %d", err); + return err; + } + + /* clear GUP/GDO flag */ + regmap_write(jz_codec->regmap, JZ4760_CODEC_REG_IFR, gain_bit); + } + + regmap_read(jz_codec->regmap, JZ4760_CODEC_REG_CR2, ®); + + return 0; +} + +/* unit: 0.01dB */ +static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 100); +static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 100); +static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0); +static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0); + +/* Unconditional controls. */ +static const struct snd_kcontrol_new jz4760_codec_snd_controls[] = { + /* record gain control */ + SOC_DOUBLE_R_TLV("PCM Capture Volume", + JZ4760_CODEC_REG_GCR9, JZ4760_CODEC_REG_GCR8, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 0, adc_tlv), + + SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume", + JZ4760_CODEC_REG_GCR4, JZ4760_CODEC_REG_GCR3, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv), + + SOC_SINGLE_TLV("Mixer Capture Volume", + JZ4760_CODEC_REG_MIX1, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), + + SOC_SINGLE_TLV("Mixer Playback Volume", + JZ4760_CODEC_REG_MIX2, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), + + SOC_SINGLE("High-Pass Filter Capture Switch", + JZ4760_CODEC_REG_CR4, + REG_CR4_ADC_HPF_OFFSET, 1, 0), +}; + +static const struct snd_kcontrol_new jz4760_codec_pcm_playback_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Volume", + .info = snd_soc_info_volsw, + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ + | SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = dac_tlv, + .get = snd_soc_dapm_get_volsw, + .put = snd_soc_dapm_put_volsw, + .private_value = SOC_DOUBLE_R_VALUE(JZ4760_CODEC_REG_GCR6, + JZ4760_CODEC_REG_GCR5, + REG_GCR_GAIN_OFFSET, + REG_GCR_GAIN_MAX, 1), + }, +}; + +static const struct snd_kcontrol_new jz4760_codec_hp_playback_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Volume", + .info = snd_soc_info_volsw, + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ + | SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = out_tlv, + .get = snd_soc_dapm_get_volsw, + .put = snd_soc_dapm_put_volsw, + .private_value = SOC_DOUBLE_R_VALUE(JZ4760_CODEC_REG_GCR2, + JZ4760_CODEC_REG_GCR1, + REG_GCR_GAIN_OFFSET, + REG_GCR_GAIN_MAX, 1), + }, +}; + +static int hpout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + unsigned int val; + int err; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* unmute HP */ + regmap_clear_bits(jz_codec->regmap, JZ4760_CODEC_REG_CR1, + REG_CR1_HP_MUTE); + break; + + case SND_SOC_DAPM_POST_PMU: + /* wait for ramp-up complete (RUP) */ + err = regmap_read_poll_timeout(jz_codec->regmap, + JZ4760_CODEC_REG_IFR, + val, val & REG_IFR_RUP, + 1000, 1 * USEC_PER_SEC); + if (err) { + dev_err(jz_codec->dev, "RUP timeout: %d", err); + return err; + } + + /* clear RUP flag */ + regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_IFR, + REG_IFR_RUP); + + break; + + case SND_SOC_DAPM_POST_PMD: + /* mute HP */ + regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_CR1, + REG_CR1_HP_MUTE); + + err = regmap_read_poll_timeout(jz_codec->regmap, + JZ4760_CODEC_REG_IFR, + val, val & REG_IFR_RDO, + 1000, 1 * USEC_PER_SEC); + if (err) { + dev_err(jz_codec->dev, "RDO timeout: %d", err); + return err; + } + + /* clear RDO flag */ + regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_IFR, + REG_IFR_RDO); + + break; + } + + return 0; +} + +static const char * const jz4760_codec_hp_texts[] = { + "PCM", "Line In", "Mic 1", "Mic 2" +}; + +static const unsigned int jz4760_codec_hp_values[] = { 3, 2, 0, 1 }; + +static SOC_VALUE_ENUM_SINGLE_DECL(jz4760_codec_hp_enum, + JZ4760_CODEC_REG_CR1, + REG_CR1_OUTSEL_OFFSET, + REG_CR1_OUTSEL_MASK >> REG_CR1_OUTSEL_OFFSET, + jz4760_codec_hp_texts, + jz4760_codec_hp_values); +static const struct snd_kcontrol_new jz4760_codec_hp_source = + SOC_DAPM_ENUM("Route", jz4760_codec_hp_enum); + +static const char * const jz4760_codec_cap_texts[] = { + "Line In", "Mic 1", "Mic 2" +}; + +static const unsigned int jz4760_codec_cap_values[] = { 2, 0, 1 }; + +static SOC_VALUE_ENUM_SINGLE_DECL(jz4760_codec_cap_enum, + JZ4760_CODEC_REG_CR3, + REG_CR3_ADC_INSEL_OFFSET, + REG_CR3_ADC_INSEL_MASK >> REG_CR3_ADC_INSEL_OFFSET, + jz4760_codec_cap_texts, + jz4760_codec_cap_values); +static const struct snd_kcontrol_new jz4760_codec_cap_source = + SOC_DAPM_ENUM("Route", jz4760_codec_cap_enum); + +static const struct snd_kcontrol_new jz4760_codec_mic_controls[] = { + SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4760_CODEC_REG_CR3, + REG_CR3_MICSTEREO_OFFSET, 1, 0), +}; + +static const struct snd_kcontrol_new jz4760_codec_line_out_switch = + SOC_DAPM_SINGLE("Switch", JZ4760_CODEC_REG_CR1, + REG_CR1_LO_MUTE_OFFSET, 0, 0); +static const struct snd_kcontrol_new jz4760_codec_btl_out_switch = + SOC_DAPM_SINGLE("Switch", JZ4760_CODEC_REG_CR1, + REG_CR1_BTL_MUTE_OFFSET, 0, 0); + +static const struct snd_soc_dapm_widget jz4760_codec_dapm_widgets[] = { + SND_SOC_DAPM_PGA_E("HP Out", JZ4760_CODEC_REG_PMR2, + REG_PMR2_SB_HP_OFFSET, 1, NULL, 0, hpout_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SWITCH("Line Out", JZ4760_CODEC_REG_PMR2, + REG_PMR2_SB_LOUT_OFFSET, 1, + &jz4760_codec_line_out_switch), + + SND_SOC_DAPM_SWITCH("BTL Out", JZ4760_CODEC_REG_PMR2, + REG_PMR2_SB_BTL_OFFSET, 1, + &jz4760_codec_btl_out_switch), + + SND_SOC_DAPM_PGA("Line In", JZ4760_CODEC_REG_PMR1, + REG_PMR1_SB_LINE_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0, + &jz4760_codec_hp_source), + + SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0, + &jz4760_codec_cap_source), + + SND_SOC_DAPM_PGA("Mic 1", JZ4760_CODEC_REG_PMR1, + REG_PMR1_SB_MIC1_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Mic 2", JZ4760_CODEC_REG_PMR1, + REG_PMR1_SB_MIC2_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Mic Diff", JZ4760_CODEC_REG_CR3, + REG_CR3_MICDIFF_OFFSET, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0, + jz4760_codec_mic_controls, + ARRAY_SIZE(jz4760_codec_mic_controls)), + + SND_SOC_DAPM_PGA("Line In Bypass", JZ4760_CODEC_REG_PMR1, + REG_PMR1_SB_BYPASS_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_ADC("ADC", "Capture", JZ4760_CODEC_REG_PMR2, + REG_PMR2_SB_ADC_OFFSET, 1), + + SND_SOC_DAPM_DAC("DAC", "Playback", JZ4760_CODEC_REG_PMR2, + REG_PMR2_SB_DAC_OFFSET, 1), + + SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0, + jz4760_codec_pcm_playback_controls, + ARRAY_SIZE(jz4760_codec_pcm_playback_controls)), + + SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0, + jz4760_codec_hp_playback_controls, + ARRAY_SIZE(jz4760_codec_hp_playback_controls)), + + SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4760_CODEC_REG_PMR1, + REG_PMR1_SB_MICBIAS_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_INPUT("MIC1P"), + SND_SOC_DAPM_INPUT("MIC1N"), + SND_SOC_DAPM_INPUT("MIC2P"), + SND_SOC_DAPM_INPUT("MIC2N"), + + SND_SOC_DAPM_INPUT("LLINEIN"), + SND_SOC_DAPM_INPUT("RLINEIN"), + + SND_SOC_DAPM_OUTPUT("LHPOUT"), + SND_SOC_DAPM_OUTPUT("RHPOUT"), + + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), + + SND_SOC_DAPM_OUTPUT("BTLP"), + SND_SOC_DAPM_OUTPUT("BTLN"), + + SND_SOC_DAPM_OUTPUT("SYSCLK"), +}; + +/* Unconditional routes. */ +static const struct snd_soc_dapm_route jz4760_codec_dapm_routes[] = { + { "Mic 1", NULL, "MIC1P" }, + { "Mic Diff", NULL, "MIC1N" }, + { "Mic 1", NULL, "Mic Diff" }, + { "Mic 2", NULL, "MIC2P" }, + { "Mic Diff", NULL, "MIC2N" }, + { "Mic 2", NULL, "Mic Diff" }, + + { "Line In", NULL, "LLINEIN" }, + { "Line In", NULL, "RLINEIN" }, + + { "Mic", "Stereo Capture Switch", "Mic 1" }, + { "Mic", "Stereo Capture Switch", "Mic 2" }, + { "Headphones Source", "Mic 1", "Mic" }, + { "Headphones Source", "Mic 2", "Mic" }, + { "Capture Source", "Mic 1", "Mic" }, + { "Capture Source", "Mic 2", "Mic" }, + + { "Capture Source", "Line In", "Line In" }, + { "Capture Source", "Mic 1", "Mic 1" }, + { "Capture Source", "Mic 2", "Mic 2" }, + { "ADC", NULL, "Capture Source" }, + + { "Line In Bypass", NULL, "Line In" }, + + { "Headphones Source", "Mic 1", "Mic 1" }, + { "Headphones Source", "Mic 2", "Mic 2" }, + { "Headphones Source", "Line In", "Line In Bypass" }, + { "Headphones Source", "PCM", "Headphones Playback" }, + { "HP Out", NULL, "Headphones Source" }, + + { "LHPOUT", NULL, "HP Out" }, + { "RHPOUT", NULL, "HP Out" }, + { "Line Out", "Switch", "HP Out" }, + + { "LOUT", NULL, "Line Out" }, + { "ROUT", NULL, "Line Out" }, + { "BTL Out", "Switch", "Line Out" }, + + { "BTLP", NULL, "BTL Out"}, + { "BTLN", NULL, "BTL Out"}, + + { "PCM Playback", "Volume", "DAC" }, + { "Headphones Playback", "Volume", "PCM Playback" }, + + { "SYSCLK", NULL, "DAC" }, +}; + +static void jz4760_codec_codec_init_regs(struct snd_soc_component *codec) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + struct regmap *regmap = jz_codec->regmap; + + /* Collect updates for later sending. */ + regcache_cache_only(regmap, true); + + /* default Amp output to PCM */ + regmap_set_bits(regmap, JZ4760_CODEC_REG_CR1, REG_CR1_OUTSEL_MASK); + + /* Disable stereo mic */ + regmap_clear_bits(regmap, JZ4760_CODEC_REG_CR3, + BIT(REG_CR3_MICSTEREO_OFFSET)); + + /* Set mic 1 as default source for ADC */ + regmap_clear_bits(regmap, JZ4760_CODEC_REG_CR3, + REG_CR3_ADC_INSEL_MASK); + + /* ADC/DAC: serial + i2s */ + regmap_set_bits(regmap, JZ4760_CODEC_REG_AICR, + REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S | + REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S); + + /* The generated IRQ is a high level */ + regmap_clear_bits(regmap, JZ4760_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK); + regmap_update_bits(regmap, JZ4760_CODEC_REG_ICR, REG_ICR_ALL_MASK, + REG_ICR_JACK_MASK | REG_ICR_RUP_MASK | + REG_ICR_RDO_MASK | REG_ICR_GUP_MASK | + REG_ICR_GDO_MASK); + + /* 12M oscillator */ + regmap_clear_bits(regmap, JZ4760_CODEC_REG_CCR1, REG_CCR1_CRYSTAL_MASK); + + /* 0: 16ohm/220uF, 1: 10kohm/1uF */ + regmap_clear_bits(regmap, JZ4760_CODEC_REG_CR1, REG_CR1_HP_LOAD); + + /* default to NOMAD */ + regmap_set_bits(jz_codec->regmap, JZ4760_CODEC_REG_CR2, + REG_CR2_DAC_NOMAD); + + /* disable automatic gain */ + regmap_clear_bits(regmap, JZ4760_CODEC_REG_AGC1, REG_AGC1_EN); + + /* Independent L/R DAC gain control */ + regmap_clear_bits(regmap, JZ4760_CODEC_REG_GCR5, + REG_GCR_RL); + + /* Send collected updates. */ + regcache_cache_only(regmap, false); + regcache_sync(regmap); +} + +static int jz4760_codec_codec_probe(struct snd_soc_component *codec) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + + clk_prepare_enable(jz_codec->clk); + + jz4760_codec_codec_init_regs(codec); + + return 0; +} + +static void jz4760_codec_codec_remove(struct snd_soc_component *codec) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + + clk_disable_unprepare(jz_codec->clk); +} + +static const struct snd_soc_component_driver jz4760_codec_soc_codec_dev = { + .probe = jz4760_codec_codec_probe, + .remove = jz4760_codec_codec_remove, + .set_bias_level = jz4760_codec_set_bias_level, + .controls = jz4760_codec_snd_controls, + .num_controls = ARRAY_SIZE(jz4760_codec_snd_controls), + .dapm_widgets = jz4760_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(jz4760_codec_dapm_widgets), + .dapm_routes = jz4760_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(jz4760_codec_dapm_routes), + .suspend_bias_off = 1, + .use_pmdown_time = 1, +}; + +static const unsigned int jz4760_codec_sample_rates[] = { + 96000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, + 11025, 9600, 8000, +}; + +static int jz4760_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component); + unsigned int rate, bit_width; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + bit_width = 0; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + bit_width = 1; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + bit_width = 2; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bit_width = 3; + break; + default: + return -EINVAL; + } + + for (rate = 0; rate < ARRAY_SIZE(jz4760_codec_sample_rates); rate++) { + if (jz4760_codec_sample_rates[rate] == params_rate(params)) + break; + } + + if (rate == ARRAY_SIZE(jz4760_codec_sample_rates)) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_AICR, + REG_AICR_DAC_ADWL_MASK, + FIELD_PREP(REG_AICR_DAC_ADWL_MASK, bit_width)); + regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_CCR2, + REG_CCR2_DAC_FREQ_MASK, + FIELD_PREP(REG_CCR2_DAC_FREQ_MASK, rate)); + } else { + regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_AICR, + REG_AICR_ADC_ADWL_MASK, + FIELD_PREP(REG_AICR_ADC_ADWL_MASK, bit_width)); + regmap_update_bits(codec->regmap, JZ4760_CODEC_REG_CCR2, + REG_CCR2_ADC_FREQ_MASK, + FIELD_PREP(REG_CCR2_ADC_FREQ_MASK, rate)); + } + + return 0; +} + +static const struct snd_soc_dai_ops jz4760_codec_dai_ops = { + .startup = jz4760_codec_startup, + .shutdown = jz4760_codec_shutdown, + .hw_params = jz4760_codec_hw_params, + .trigger = jz4760_codec_pcm_trigger, + .mute_stream = jz4760_codec_mute_stream, + .no_capture_mute = 1, +}; + +#define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +static struct snd_soc_dai_driver jz4760_codec_dai = { + .name = "jz4760-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = JZ_CODEC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = JZ_CODEC_FORMATS, + }, + .ops = &jz4760_codec_dai_ops, +}; + +static bool jz4760_codec_volatile(struct device *dev, unsigned int reg) +{ + return reg == JZ4760_CODEC_REG_SR || reg == JZ4760_CODEC_REG_IFR; +} + +static bool jz4760_codec_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case JZ4760_CODEC_REG_SR: + return false; + default: + return true; + } +} + +static int jz4760_codec_io_wait(struct jz_codec *codec) +{ + u32 reg; + + return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg, + !(reg & ICDC_RGADW_RGWR), + 1000, 1 * USEC_PER_SEC); +} + +static int jz4760_codec_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct jz_codec *codec = context; + unsigned int i; + u32 tmp; + int ret; + + ret = jz4760_codec_io_wait(codec); + if (ret) + return ret; + + tmp = readl(codec->base + ICDC_RGADW_OFFSET); + tmp &= ~ICDC_RGADW_RGADDR_MASK; + tmp |= FIELD_PREP(ICDC_RGADW_RGADDR_MASK, reg); + writel(tmp, codec->base + ICDC_RGADW_OFFSET); + + /* wait 6+ cycles */ + for (i = 0; i < 6; i++) + *val = readl(codec->base + ICDC_RGDATA_OFFSET) & + ICDC_RGDATA_RGDOUT_MASK; + + return 0; +} + +static int jz4760_codec_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct jz_codec *codec = context; + int ret; + + ret = jz4760_codec_io_wait(codec); + if (ret) + return ret; + + writel(ICDC_RGADW_RGWR | FIELD_PREP(ICDC_RGADW_RGADDR_MASK, reg) | val, + codec->base + ICDC_RGADW_OFFSET); + + ret = jz4760_codec_io_wait(codec); + if (ret) + return ret; + + return 0; +} + +static const u8 jz4760_codec_reg_defaults[] = { + 0x00, 0xFC, 0x1B, 0x20, 0x00, 0x80, 0x00, 0x00, + 0xFF, 0x1F, 0x3F, 0x00, 0x06, 0x06, 0x06, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x07, 0x44, + 0x1F, 0x00, 0x00, 0x00 +}; + +static struct regmap_config jz4760_codec_regmap_config = { + .reg_bits = 7, + .val_bits = 8, + + .max_register = JZ4760_CODEC_REG_MIX2, + .volatile_reg = jz4760_codec_volatile, + .writeable_reg = jz4760_codec_writeable, + + .reg_read = jz4760_codec_reg_read, + .reg_write = jz4760_codec_reg_write, + + .reg_defaults_raw = jz4760_codec_reg_defaults, + .num_reg_defaults_raw = ARRAY_SIZE(jz4760_codec_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static int jz4760_codec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct jz_codec *codec; + int ret; + + codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + codec->dev = dev; + + codec->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(codec->base)) + return PTR_ERR(codec->base); + + codec->regmap = devm_regmap_init(dev, NULL, codec, + &jz4760_codec_regmap_config); + if (IS_ERR(codec->regmap)) + return PTR_ERR(codec->regmap); + + codec->clk = devm_clk_get(dev, "aic"); + if (IS_ERR(codec->clk)) + return PTR_ERR(codec->clk); + + platform_set_drvdata(pdev, codec); + + ret = devm_snd_soc_register_component(dev, &jz4760_codec_soc_codec_dev, + &jz4760_codec_dai, 1); + if (ret) { + dev_err(dev, "Failed to register codec: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct of_device_id jz4760_codec_of_matches[] = { + { .compatible = "ingenic,jz4760-codec", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jz4760_codec_of_matches); + +static struct platform_driver jz4760_codec_driver = { + .probe = jz4760_codec_probe, + .driver = { + .name = "jz4760-codec", + .of_match_table = jz4760_codec_of_matches, + }, +}; +module_platform_driver(jz4760_codec_driver); + +MODULE_DESCRIPTION("JZ4760 SoC internal codec driver"); +MODULE_AUTHOR("Christophe Branchereau "); +MODULE_AUTHOR("Paul Cercueil "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c new file mode 100644 index 0000000000..1d0c467ab5 --- /dev/null +++ b/sound/soc/codecs/jz4770.c @@ -0,0 +1,947 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Ingenic JZ4770 CODEC driver +// +// Copyright (C) 2012, Maarten ter Huurne +// Copyright (C) 2019, Paul Cercueil + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define ICDC_RGADW_OFFSET 0x00 +#define ICDC_RGDATA_OFFSET 0x04 + +/* ICDC internal register access control register(RGADW) */ +#define ICDC_RGADW_RGWR BIT(16) + +#define ICDC_RGADW_RGADDR_OFFSET 8 +#define ICDC_RGADW_RGADDR_MASK GENMASK(14, ICDC_RGADW_RGADDR_OFFSET) + +#define ICDC_RGADW_RGDIN_OFFSET 0 +#define ICDC_RGADW_RGDIN_MASK GENMASK(7, ICDC_RGADW_RGDIN_OFFSET) + +/* ICDC internal register data output register (RGDATA)*/ +#define ICDC_RGDATA_IRQ BIT(8) + +#define ICDC_RGDATA_RGDOUT_OFFSET 0 +#define ICDC_RGDATA_RGDOUT_MASK GENMASK(7, ICDC_RGDATA_RGDOUT_OFFSET) + +/* Internal register space, accessed through regmap */ +enum { + JZ4770_CODEC_REG_SR, + JZ4770_CODEC_REG_AICR_DAC, + JZ4770_CODEC_REG_AICR_ADC, + JZ4770_CODEC_REG_CR_LO, + JZ4770_CODEC_REG_CR_HP, + + JZ4770_CODEC_REG_MISSING_REG1, + + JZ4770_CODEC_REG_CR_DAC, + JZ4770_CODEC_REG_CR_MIC, + JZ4770_CODEC_REG_CR_LI, + JZ4770_CODEC_REG_CR_ADC, + JZ4770_CODEC_REG_CR_MIX, + JZ4770_CODEC_REG_CR_VIC, + JZ4770_CODEC_REG_CCR, + JZ4770_CODEC_REG_FCR_DAC, + JZ4770_CODEC_REG_FCR_ADC, + JZ4770_CODEC_REG_ICR, + JZ4770_CODEC_REG_IMR, + JZ4770_CODEC_REG_IFR, + JZ4770_CODEC_REG_GCR_HPL, + JZ4770_CODEC_REG_GCR_HPR, + JZ4770_CODEC_REG_GCR_LIBYL, + JZ4770_CODEC_REG_GCR_LIBYR, + JZ4770_CODEC_REG_GCR_DACL, + JZ4770_CODEC_REG_GCR_DACR, + JZ4770_CODEC_REG_GCR_MIC1, + JZ4770_CODEC_REG_GCR_MIC2, + JZ4770_CODEC_REG_GCR_ADCL, + JZ4770_CODEC_REG_GCR_ADCR, + + JZ4770_CODEC_REG_MISSING_REG2, + + JZ4770_CODEC_REG_GCR_MIXADC, + JZ4770_CODEC_REG_GCR_MIXDAC, + JZ4770_CODEC_REG_AGC1, + JZ4770_CODEC_REG_AGC2, + JZ4770_CODEC_REG_AGC3, + JZ4770_CODEC_REG_AGC4, + JZ4770_CODEC_REG_AGC5, +}; + +#define REG_AICR_DAC_ADWL_OFFSET 6 +#define REG_AICR_DAC_ADWL_MASK (0x3 << REG_AICR_DAC_ADWL_OFFSET) +#define REG_AICR_DAC_SERIAL BIT(1) +#define REG_AICR_DAC_I2S BIT(0) + +#define REG_AICR_ADC_ADWL_OFFSET 6 +#define REG_AICR_ADC_ADWL_MASK (0x3 << REG_AICR_ADC_ADWL_OFFSET) +#define REG_AICR_ADC_SERIAL BIT(1) +#define REG_AICR_ADC_I2S BIT(0) + +#define REG_CR_LO_MUTE_OFFSET 7 +#define REG_CR_LO_SB_OFFSET 4 +#define REG_CR_LO_SEL_OFFSET 0 +#define REG_CR_LO_SEL_MASK (0x3 << REG_CR_LO_SEL_OFFSET) + +#define REG_CR_HP_MUTE BIT(7) +#define REG_CR_HP_LOAD BIT(6) +#define REG_CR_HP_SB_OFFSET 4 +#define REG_CR_HP_SB_HPCM_OFFSET 3 +#define REG_CR_HP_SEL_OFFSET 0 +#define REG_CR_HP_SEL_MASK (0x3 << REG_CR_HP_SEL_OFFSET) + +#define REG_CR_DAC_MUTE BIT(7) +#define REG_CR_DAC_MONO BIT(6) +#define REG_CR_DAC_LEFT_ONLY BIT(5) +#define REG_CR_DAC_SB_OFFSET 4 +#define REG_CR_DAC_LRSWAP BIT(3) + +#define REG_CR_MIC_STEREO_OFFSET 7 +#define REG_CR_MIC_IDIFF_OFFSET 6 +#define REG_CR_MIC_SB_MIC2_OFFSET 5 +#define REG_CR_MIC_SB_MIC1_OFFSET 4 +#define REG_CR_MIC_BIAS_V0_OFFSET 1 +#define REG_CR_MIC_BIAS_SB_OFFSET 0 + +#define REG_CR_LI_LIBY_OFFSET 4 +#define REG_CR_LI_SB_OFFSET 0 + +#define REG_CR_ADC_DMIC_SEL BIT(7) +#define REG_CR_ADC_MONO BIT(6) +#define REG_CR_ADC_LEFT_ONLY BIT(5) +#define REG_CR_ADC_SB_OFFSET 4 +#define REG_CR_ADC_LRSWAP BIT(3) +#define REG_CR_ADC_IN_SEL_OFFSET 0 +#define REG_CR_ADC_IN_SEL_MASK (0x3 << REG_CR_ADC_IN_SEL_OFFSET) + +#define REG_CR_VIC_SB_SLEEP BIT(1) +#define REG_CR_VIC_SB BIT(0) + +#define REG_CCR_CRYSTAL_OFFSET 0 +#define REG_CCR_CRYSTAL_MASK (0xf << REG_CCR_CRYSTAL_OFFSET) + +#define REG_FCR_DAC_FREQ_OFFSET 0 +#define REG_FCR_DAC_FREQ_MASK (0xf << REG_FCR_DAC_FREQ_OFFSET) + +#define REG_FCR_ADC_FREQ_OFFSET 0 +#define REG_FCR_ADC_FREQ_MASK (0xf << REG_FCR_ADC_FREQ_OFFSET) + +#define REG_ICR_INT_FORM_OFFSET 6 +#define REG_ICR_INT_FORM_MASK (0x3 << REG_ICR_INT_FORM_OFFSET) + +#define REG_IMR_ALL_MASK (0x7f) +#define REG_IMR_SCLR_MASK BIT(6) +#define REG_IMR_JACK_MASK BIT(5) +#define REG_IMR_SCMC_MASK BIT(4) +#define REG_IMR_RUP_MASK BIT(3) +#define REG_IMR_RDO_MASK BIT(2) +#define REG_IMR_GUP_MASK BIT(1) +#define REG_IMR_GDO_MASK BIT(0) + +#define REG_IFR_ALL_MASK (0x7f) +#define REG_IFR_SCLR BIT(6) +#define REG_IFR_JACK BIT(5) +#define REG_IFR_SCMC BIT(4) +#define REG_IFR_RUP BIT(3) +#define REG_IFR_RDO BIT(2) +#define REG_IFR_GUP BIT(1) +#define REG_IFR_GDO BIT(0) + +#define REG_GCR_HPL_LRGO BIT(7) + +#define REG_GCR_DACL_RLGOD BIT(7) + +#define REG_GCR_GAIN_OFFSET 0 +#define REG_GCR_GAIN_MAX 0x1f + +#define REG_GCR_MIC_GAIN_OFFSET 0 +#define REG_GCR_MIC_GAIN_MAX 5 + +#define REG_GCR_ADC_GAIN_OFFSET 0 +#define REG_GCR_ADC_GAIN_MAX 23 + +#define REG_AGC1_EN BIT(7) + +/* codec private data */ +struct jz_codec { + struct device *dev; + struct regmap *regmap; + void __iomem *base; + struct clk *clk; +}; + +static int jz4770_codec_set_bias_level(struct snd_soc_component *codec, + enum snd_soc_bias_level level) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + struct regmap *regmap = jz_codec->regmap; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + /* Reset all interrupt flags. */ + regmap_write(regmap, JZ4770_CODEC_REG_IFR, REG_IFR_ALL_MASK); + + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB); + msleep(250); + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB_SLEEP); + msleep(400); + break; + case SND_SOC_BIAS_STANDBY: + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB_SLEEP); + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_VIC, + REG_CR_VIC_SB); + fallthrough; + default: + break; + } + + return 0; +} + +static int jz4770_codec_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + + /* + * SYSCLK output from the codec to the AIC is required to keep the + * DMA transfer going during playback when all audible outputs have + * been disabled. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_force_enable_pin(dapm, "SYSCLK"); + + return 0; +} + +static void jz4770_codec_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dapm_disable_pin(dapm, "SYSCLK"); +} + + +static int jz4770_codec_pcm_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_component_force_bias_level(codec, + SND_SOC_BIAS_ON); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* do nothing */ + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int jz4770_codec_mute_stream(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *codec = dai->component; + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + unsigned int gain_bit = mute ? REG_IFR_GDO : REG_IFR_GUP; + unsigned int val; + int change, err; + + change = snd_soc_component_update_bits(codec, JZ4770_CODEC_REG_CR_DAC, + REG_CR_DAC_MUTE, + mute ? REG_CR_DAC_MUTE : 0); + if (change == 1) { + regmap_read(jz_codec->regmap, JZ4770_CODEC_REG_CR_DAC, &val); + + if (val & BIT(REG_CR_DAC_SB_OFFSET)) + return 1; + + err = regmap_read_poll_timeout(jz_codec->regmap, + JZ4770_CODEC_REG_IFR, + val, val & gain_bit, + 1000, 1 * USEC_PER_SEC); + if (err) { + dev_err(jz_codec->dev, + "Timeout while setting digital mute: %d", err); + return err; + } + + /* clear GUP/GDO flag */ + regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, + gain_bit); + } + + return 0; +} + +/* unit: 0.01dB */ +static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 600); +static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0); +static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0); + +/* Unconditional controls. */ +static const struct snd_kcontrol_new jz4770_codec_snd_controls[] = { + /* record gain control */ + SOC_DOUBLE_R_TLV("PCM Capture Volume", + JZ4770_CODEC_REG_GCR_ADCL, JZ4770_CODEC_REG_GCR_ADCR, + REG_GCR_ADC_GAIN_OFFSET, REG_GCR_ADC_GAIN_MAX, + 0, adc_tlv), + + SOC_DOUBLE_R_TLV("Line In Bypass Playback Volume", + JZ4770_CODEC_REG_GCR_LIBYL, JZ4770_CODEC_REG_GCR_LIBYR, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv), + + SOC_SINGLE_TLV("Mixer Capture Volume", + JZ4770_CODEC_REG_GCR_MIXADC, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), + + SOC_SINGLE_TLV("Mixer Playback Volume", + JZ4770_CODEC_REG_GCR_MIXDAC, + REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv), +}; + +static const struct snd_kcontrol_new jz4770_codec_pcm_playback_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Volume", + .info = snd_soc_info_volsw, + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ + | SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = dac_tlv, + .get = snd_soc_dapm_get_volsw, + .put = snd_soc_dapm_put_volsw, + /* + * NOTE: DACR/DACL are inversed; the gain value written to DACR + * seems to affect the left channel, and the gain value written + * to DACL seems to affect the right channel. + */ + .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_DACR, + JZ4770_CODEC_REG_GCR_DACL, + REG_GCR_GAIN_OFFSET, + REG_GCR_GAIN_MAX, 1), + }, +}; + +static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Volume", + .info = snd_soc_info_volsw, + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ + | SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = out_tlv, + .get = snd_soc_dapm_get_volsw, + .put = snd_soc_dapm_put_volsw, + /* HPR/HPL inversed for the same reason as above */ + .private_value = SOC_DOUBLE_R_VALUE(JZ4770_CODEC_REG_GCR_HPR, + JZ4770_CODEC_REG_GCR_HPL, + REG_GCR_GAIN_OFFSET, + REG_GCR_GAIN_MAX, 1), + }, +}; + +static int hpout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + unsigned int val; + int err; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* unmute HP */ + regmap_clear_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_MUTE); + break; + + case SND_SOC_DAPM_POST_PMU: + /* wait for ramp-up complete (RUP) */ + err = regmap_read_poll_timeout(jz_codec->regmap, + JZ4770_CODEC_REG_IFR, + val, val & REG_IFR_RUP, + 1000, 1 * USEC_PER_SEC); + if (err) { + dev_err(jz_codec->dev, "RUP timeout: %d", err); + return err; + } + + /* clear RUP flag */ + regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, + REG_IFR_RUP); + + break; + + case SND_SOC_DAPM_POST_PMD: + /* mute HP */ + regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_MUTE); + + err = regmap_read_poll_timeout(jz_codec->regmap, + JZ4770_CODEC_REG_IFR, + val, val & REG_IFR_RDO, + 1000, 1 * USEC_PER_SEC); + if (err) { + dev_err(jz_codec->dev, "RDO timeout: %d", err); + return err; + } + + /* clear RDO flag */ + regmap_set_bits(jz_codec->regmap, JZ4770_CODEC_REG_IFR, + REG_IFR_RDO); + + break; + } + + return 0; +} + +static int adc_poweron_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (event == SND_SOC_DAPM_POST_PMU) + msleep(1000); + + return 0; +} + +static const char * const jz4770_codec_hp_texts[] = { + "PCM", "Line In", "Mic 1", "Mic 2" +}; +static const unsigned int jz4770_codec_hp_values[] = { 3, 2, 0, 1 }; +static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_hp_enum, + JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_SEL_OFFSET, + REG_CR_HP_SEL_MASK, + jz4770_codec_hp_texts, + jz4770_codec_hp_values); +static const struct snd_kcontrol_new jz4770_codec_hp_source = + SOC_DAPM_ENUM("Route", jz4770_codec_hp_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_lo_enum, + JZ4770_CODEC_REG_CR_LO, + REG_CR_LO_SEL_OFFSET, + REG_CR_LO_SEL_MASK, + jz4770_codec_hp_texts, + jz4770_codec_hp_values); +static const struct snd_kcontrol_new jz4770_codec_lo_source = + SOC_DAPM_ENUM("Route", jz4770_codec_lo_enum); + +static const char * const jz4770_codec_cap_texts[] = { + "Line In", "Mic 1", "Mic 2" +}; +static const unsigned int jz4770_codec_cap_values[] = { 2, 0, 1 }; +static SOC_VALUE_ENUM_SINGLE_DECL(jz4770_codec_cap_enum, + JZ4770_CODEC_REG_CR_ADC, + REG_CR_ADC_IN_SEL_OFFSET, + REG_CR_ADC_IN_SEL_MASK, + jz4770_codec_cap_texts, + jz4770_codec_cap_values); +static const struct snd_kcontrol_new jz4770_codec_cap_source = + SOC_DAPM_ENUM("Route", jz4770_codec_cap_enum); + +static const struct snd_kcontrol_new jz4770_codec_mic_controls[] = { + SOC_DAPM_SINGLE("Stereo Capture Switch", JZ4770_CODEC_REG_CR_MIC, + REG_CR_MIC_STEREO_OFFSET, 1, 0), +}; + +static const struct snd_soc_dapm_widget jz4770_codec_dapm_widgets[] = { + SND_SOC_DAPM_PGA_E("HP Out", JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_SB_OFFSET, 1, NULL, 0, hpout_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA("Line Out", JZ4770_CODEC_REG_CR_LO, + REG_CR_LO_SB_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Line Out Switch 2", JZ4770_CODEC_REG_CR_LO, + REG_CR_LO_MUTE_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Line In", JZ4770_CODEC_REG_CR_LI, + REG_CR_LI_SB_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_MUX("Headphones Source", SND_SOC_NOPM, 0, 0, + &jz4770_codec_hp_source), + SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0, + &jz4770_codec_cap_source), + SND_SOC_DAPM_MUX("Line Out Source", SND_SOC_NOPM, 0, 0, + &jz4770_codec_lo_source), + + SND_SOC_DAPM_PGA("Mic 1", JZ4770_CODEC_REG_CR_MIC, + REG_CR_MIC_SB_MIC1_OFFSET, 1, NULL, 0), + SND_SOC_DAPM_PGA("Mic 2", JZ4770_CODEC_REG_CR_MIC, + REG_CR_MIC_SB_MIC2_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Mic Diff", JZ4770_CODEC_REG_CR_MIC, + REG_CR_MIC_IDIFF_OFFSET, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Mic", SND_SOC_NOPM, 0, 0, + jz4770_codec_mic_controls, + ARRAY_SIZE(jz4770_codec_mic_controls)), + + SND_SOC_DAPM_PGA("Line In Bypass", JZ4770_CODEC_REG_CR_LI, + REG_CR_LI_LIBY_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_ADC_E("ADC", "HiFi Capture", JZ4770_CODEC_REG_CR_ADC, + REG_CR_ADC_SB_OFFSET, 1, adc_poweron_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_DAC("DAC", "HiFi Playback", JZ4770_CODEC_REG_CR_DAC, + REG_CR_DAC_SB_OFFSET, 1), + + SND_SOC_DAPM_MIXER("PCM Playback", SND_SOC_NOPM, 0, 0, + jz4770_codec_pcm_playback_controls, + ARRAY_SIZE(jz4770_codec_pcm_playback_controls)), + SND_SOC_DAPM_MIXER("Headphones Playback", SND_SOC_NOPM, 0, 0, + jz4770_codec_hp_playback_controls, + ARRAY_SIZE(jz4770_codec_hp_playback_controls)), + + SND_SOC_DAPM_SUPPLY("MICBIAS", JZ4770_CODEC_REG_CR_MIC, + REG_CR_MIC_BIAS_SB_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Cap-less", JZ4770_CODEC_REG_CR_HP, + REG_CR_HP_SB_HPCM_OFFSET, 1, NULL, 0), + + SND_SOC_DAPM_INPUT("MIC1P"), + SND_SOC_DAPM_INPUT("MIC1N"), + SND_SOC_DAPM_INPUT("MIC2P"), + SND_SOC_DAPM_INPUT("MIC2N"), + + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), + + SND_SOC_DAPM_OUTPUT("LHPOUT"), + SND_SOC_DAPM_OUTPUT("RHPOUT"), + + SND_SOC_DAPM_INPUT("LLINEIN"), + SND_SOC_DAPM_INPUT("RLINEIN"), + + SND_SOC_DAPM_OUTPUT("SYSCLK"), +}; + +/* Unconditional routes. */ +static const struct snd_soc_dapm_route jz4770_codec_dapm_routes[] = { + { "Mic 1", NULL, "MIC1P" }, + { "Mic Diff", NULL, "MIC1N" }, + { "Mic 1", NULL, "Mic Diff" }, + { "Mic 2", NULL, "MIC2P" }, + { "Mic Diff", NULL, "MIC2N" }, + { "Mic 2", NULL, "Mic Diff" }, + + { "Line In", NULL, "LLINEIN" }, + { "Line In", NULL, "RLINEIN" }, + + { "Mic", "Stereo Capture Switch", "Mic 1" }, + { "Mic", "Stereo Capture Switch", "Mic 2" }, + { "Headphones Source", "Mic 1", "Mic" }, + { "Headphones Source", "Mic 2", "Mic" }, + { "Capture Source", "Mic 1", "Mic" }, + { "Capture Source", "Mic 2", "Mic" }, + + { "Headphones Source", "Mic 1", "Mic 1" }, + { "Headphones Source", "Mic 2", "Mic 2" }, + { "Headphones Source", "Line In", "Line In Bypass" }, + { "Headphones Source", "PCM", "Headphones Playback" }, + { "HP Out", NULL, "Headphones Source" }, + + { "Capture Source", "Line In", "Line In" }, + { "Capture Source", "Mic 1", "Mic 1" }, + { "Capture Source", "Mic 2", "Mic 2" }, + { "ADC", NULL, "Capture Source" }, + + { "Line In Bypass", NULL, "Line In" }, + { "Line Out Source", "Line In", "Line In Bypass" }, + { "Line Out Source", "PCM", "PCM Playback" }, + + { "LHPOUT", NULL, "HP Out"}, + { "RHPOUT", NULL, "HP Out"}, + + { "Line Out", NULL, "Line Out Source" }, + { "Line Out Switch 2", NULL, "Line Out" }, + + { "LOUT", NULL, "Line Out Switch 2"}, + { "ROUT", NULL, "Line Out Switch 2"}, + + { "PCM Playback", "Volume", "DAC" }, + { "Headphones Playback", "Volume", "PCM Playback" }, + + { "SYSCLK", NULL, "DAC" }, +}; + +static void jz4770_codec_codec_init_regs(struct snd_soc_component *codec) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + struct regmap *regmap = jz_codec->regmap; + + /* Collect updates for later sending. */ + regcache_cache_only(regmap, true); + + /* default HP output to PCM */ + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_SEL_MASK); + + /* default line output to PCM */ + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_LO, REG_CR_LO_SEL_MASK); + + /* Disable stereo mic */ + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_MIC, + BIT(REG_CR_MIC_STEREO_OFFSET)); + + /* Set mic 1 as default source for ADC */ + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_ADC, + REG_CR_ADC_IN_SEL_MASK); + + /* ADC/DAC: serial + i2s */ + regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_ADC, + REG_AICR_ADC_SERIAL | REG_AICR_ADC_I2S); + regmap_set_bits(regmap, JZ4770_CODEC_REG_AICR_DAC, + REG_AICR_DAC_SERIAL | REG_AICR_DAC_I2S); + + /* The generated IRQ is a high level */ + regmap_clear_bits(regmap, JZ4770_CODEC_REG_ICR, REG_ICR_INT_FORM_MASK); + regmap_update_bits(regmap, JZ4770_CODEC_REG_IMR, REG_IMR_ALL_MASK, + REG_IMR_JACK_MASK | REG_IMR_RUP_MASK | + REG_IMR_RDO_MASK | REG_IMR_GUP_MASK | + REG_IMR_GDO_MASK); + + /* 12M oscillator */ + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CCR, REG_CCR_CRYSTAL_MASK); + + /* 0: 16ohm/220uF, 1: 10kohm/1uF */ + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP, REG_CR_HP_LOAD); + + /* disable automatic gain */ + regmap_clear_bits(regmap, JZ4770_CODEC_REG_AGC1, REG_AGC1_EN); + + /* Disable DAC lrswap */ + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_DAC, REG_CR_DAC_LRSWAP); + + /* Independent L/R DAC gain control */ + regmap_clear_bits(regmap, JZ4770_CODEC_REG_GCR_DACL, + REG_GCR_DACL_RLGOD); + + /* Disable ADC lrswap */ + regmap_set_bits(regmap, JZ4770_CODEC_REG_CR_ADC, REG_CR_ADC_LRSWAP); + + /* default to cap-less mode(0) */ + regmap_clear_bits(regmap, JZ4770_CODEC_REG_CR_HP, + BIT(REG_CR_HP_SB_HPCM_OFFSET)); + + /* Send collected updates. */ + regcache_cache_only(regmap, false); + regcache_sync(regmap); +} + +static int jz4770_codec_codec_probe(struct snd_soc_component *codec) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + + clk_prepare_enable(jz_codec->clk); + + jz4770_codec_codec_init_regs(codec); + + return 0; +} + +static void jz4770_codec_codec_remove(struct snd_soc_component *codec) +{ + struct jz_codec *jz_codec = snd_soc_component_get_drvdata(codec); + + clk_disable_unprepare(jz_codec->clk); +} + +static const struct snd_soc_component_driver jz4770_codec_soc_codec_dev = { + .probe = jz4770_codec_codec_probe, + .remove = jz4770_codec_codec_remove, + .set_bias_level = jz4770_codec_set_bias_level, + .controls = jz4770_codec_snd_controls, + .num_controls = ARRAY_SIZE(jz4770_codec_snd_controls), + .dapm_widgets = jz4770_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(jz4770_codec_dapm_widgets), + .dapm_routes = jz4770_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(jz4770_codec_dapm_routes), + .suspend_bias_off = 1, + .use_pmdown_time = 1, +}; + +static const unsigned int jz4770_codec_sample_rates[] = { + 96000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, + 11025, 9600, 8000, +}; + +static int jz4770_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct jz_codec *codec = snd_soc_component_get_drvdata(dai->component); + unsigned int rate, bit_width; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + bit_width = 0; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + bit_width = 1; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + bit_width = 2; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bit_width = 3; + break; + default: + return -EINVAL; + } + + for (rate = 0; rate < ARRAY_SIZE(jz4770_codec_sample_rates); rate++) { + if (jz4770_codec_sample_rates[rate] == params_rate(params)) + break; + } + + if (rate == ARRAY_SIZE(jz4770_codec_sample_rates)) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_DAC, + REG_AICR_DAC_ADWL_MASK, + bit_width << REG_AICR_DAC_ADWL_OFFSET); + regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_DAC, + REG_FCR_DAC_FREQ_MASK, + rate << REG_FCR_DAC_FREQ_OFFSET); + } else { + regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_AICR_ADC, + REG_AICR_ADC_ADWL_MASK, + bit_width << REG_AICR_ADC_ADWL_OFFSET); + regmap_update_bits(codec->regmap, JZ4770_CODEC_REG_FCR_ADC, + REG_FCR_ADC_FREQ_MASK, + rate << REG_FCR_ADC_FREQ_OFFSET); + } + + return 0; +} + +static const struct snd_soc_dai_ops jz4770_codec_dai_ops = { + .startup = jz4770_codec_startup, + .shutdown = jz4770_codec_shutdown, + .hw_params = jz4770_codec_hw_params, + .trigger = jz4770_codec_pcm_trigger, + .mute_stream = jz4770_codec_mute_stream, + .no_capture_mute = 1, +}; + +#define JZ_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +static struct snd_soc_dai_driver jz4770_codec_dai = { + .name = "jz4770-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = JZ_CODEC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = JZ_CODEC_FORMATS, + }, + .ops = &jz4770_codec_dai_ops, +}; + +static bool jz4770_codec_volatile(struct device *dev, unsigned int reg) +{ + return reg == JZ4770_CODEC_REG_SR || reg == JZ4770_CODEC_REG_IFR; +} + +static bool jz4770_codec_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case JZ4770_CODEC_REG_MISSING_REG1: + case JZ4770_CODEC_REG_MISSING_REG2: + return false; + default: + return true; + } +} + +static bool jz4770_codec_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case JZ4770_CODEC_REG_SR: + case JZ4770_CODEC_REG_MISSING_REG1: + case JZ4770_CODEC_REG_MISSING_REG2: + return false; + default: + return true; + } +} + +static int jz4770_codec_io_wait(struct jz_codec *codec) +{ + u32 reg; + + return readl_poll_timeout(codec->base + ICDC_RGADW_OFFSET, reg, + !(reg & ICDC_RGADW_RGWR), + 1000, 1 * USEC_PER_SEC); +} + +static int jz4770_codec_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct jz_codec *codec = context; + unsigned int i; + u32 tmp; + int ret; + + ret = jz4770_codec_io_wait(codec); + if (ret) + return ret; + + tmp = readl(codec->base + ICDC_RGADW_OFFSET); + tmp = (tmp & ~ICDC_RGADW_RGADDR_MASK) + | (reg << ICDC_RGADW_RGADDR_OFFSET); + writel(tmp, codec->base + ICDC_RGADW_OFFSET); + + /* wait 6+ cycles */ + for (i = 0; i < 6; i++) + *val = readl(codec->base + ICDC_RGDATA_OFFSET) & + ICDC_RGDATA_RGDOUT_MASK; + + return 0; +} + +static int jz4770_codec_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct jz_codec *codec = context; + int ret; + + ret = jz4770_codec_io_wait(codec); + if (ret) + return ret; + + writel(ICDC_RGADW_RGWR | (reg << ICDC_RGADW_RGADDR_OFFSET) | val, + codec->base + ICDC_RGADW_OFFSET); + + ret = jz4770_codec_io_wait(codec); + if (ret) + return ret; + + return 0; +} + +static const u8 jz4770_codec_reg_defaults[] = { + 0x00, 0xC3, 0xC3, 0x90, 0x98, 0xFF, 0x90, 0xB1, + 0x11, 0x10, 0x00, 0x03, 0x00, 0x00, 0x40, 0x00, + 0xFF, 0x00, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x34, + 0x07, 0x44, 0x1F, 0x00 +}; + +static struct regmap_config jz4770_codec_regmap_config = { + .reg_bits = 7, + .val_bits = 8, + + .max_register = JZ4770_CODEC_REG_AGC5, + .volatile_reg = jz4770_codec_volatile, + .readable_reg = jz4770_codec_readable, + .writeable_reg = jz4770_codec_writeable, + + .reg_read = jz4770_codec_reg_read, + .reg_write = jz4770_codec_reg_write, + + .reg_defaults_raw = jz4770_codec_reg_defaults, + .num_reg_defaults_raw = ARRAY_SIZE(jz4770_codec_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static int jz4770_codec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct jz_codec *codec; + int ret; + + codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + codec->dev = dev; + + codec->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(codec->base)) + return PTR_ERR(codec->base); + + codec->regmap = devm_regmap_init(dev, NULL, codec, + &jz4770_codec_regmap_config); + if (IS_ERR(codec->regmap)) + return PTR_ERR(codec->regmap); + + codec->clk = devm_clk_get(dev, "aic"); + if (IS_ERR(codec->clk)) + return PTR_ERR(codec->clk); + + platform_set_drvdata(pdev, codec); + + ret = devm_snd_soc_register_component(dev, &jz4770_codec_soc_codec_dev, + &jz4770_codec_dai, 1); + if (ret) { + dev_err(dev, "Failed to register codec: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct of_device_id jz4770_codec_of_matches[] = { + { .compatible = "ingenic,jz4770-codec", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jz4770_codec_of_matches); + +static struct platform_driver jz4770_codec_driver = { + .probe = jz4770_codec_probe, + .driver = { + .name = "jz4770-codec", + .of_match_table = jz4770_codec_of_matches, + }, +}; +module_platform_driver(jz4770_codec_driver); + +MODULE_DESCRIPTION("JZ4770 SoC internal codec driver"); +MODULE_AUTHOR("Maarten ter Huurne "); +MODULE_AUTHOR("Paul Cercueil "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c new file mode 100644 index 0000000000..e7542f7132 --- /dev/null +++ b/sound/soc/codecs/lm4857.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * LM4857 AMP driver + * + * Copyright 2007 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * graeme.gregory@wolfsonmicro.com + * Copyright 2011 Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +static const struct reg_default lm4857_default_regs[] = { + { 0x0, 0x00 }, + { 0x1, 0x00 }, + { 0x2, 0x00 }, + { 0x3, 0x00 }, +}; + +/* The register offsets in the cache array */ +#define LM4857_MVOL 0 +#define LM4857_LVOL 1 +#define LM4857_RVOL 2 +#define LM4857_CTRL 3 + +/* the shifts required to set these bits */ +#define LM4857_3D 5 +#define LM4857_WAKEUP 5 +#define LM4857_EPGAIN 4 + +static const unsigned int lm4857_mode_values[] = { + 0, + 6, + 7, + 8, + 9, +}; + +static const char * const lm4857_mode_texts[] = { + "Off", + "Earpiece", + "Loudspeaker", + "Loudspeaker + Headphone", + "Headphone", +}; + +static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(lm4857_mode_enum, + LM4857_CTRL, 0, 0xf, lm4857_mode_texts, lm4857_mode_values); + +static const struct snd_kcontrol_new lm4857_mode_ctrl = + SOC_DAPM_ENUM("Mode", lm4857_mode_enum); + +static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + + SND_SOC_DAPM_DEMUX("Mode", SND_SOC_NOPM, 0, 0, &lm4857_mode_ctrl), + + SND_SOC_DAPM_OUTPUT("LS"), + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("EP"), +}; + +static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); +static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); + +static const struct snd_kcontrol_new lm4857_controls[] = { + SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0, + stereo_tlv), + SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0, + stereo_tlv), + SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0, + mono_tlv), + SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0), + SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0), + SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL, + LM4857_WAKEUP, 1, 0), + SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL, + LM4857_EPGAIN, 1, 0), +}; + +static const struct snd_soc_dapm_route lm4857_routes[] = { + { "Mode", NULL, "IN" }, + { "LS", "Loudspeaker", "Mode" }, + { "LS", "Loudspeaker + Headphone", "Mode" }, + { "HP", "Headphone", "Mode" }, + { "HP", "Loudspeaker + Headphone", "Mode" }, + { "EP", "Earpiece", "Mode" }, +}; + +static const struct snd_soc_component_driver lm4857_component_driver = { + .controls = lm4857_controls, + .num_controls = ARRAY_SIZE(lm4857_controls), + .dapm_widgets = lm4857_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets), + .dapm_routes = lm4857_routes, + .num_dapm_routes = ARRAY_SIZE(lm4857_routes), +}; + +static const struct regmap_config lm4857_regmap_config = { + .val_bits = 6, + .reg_bits = 2, + + .max_register = LM4857_CTRL, + + .cache_type = REGCACHE_FLAT, + .reg_defaults = lm4857_default_regs, + .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs), +}; + +static int lm4857_i2c_probe(struct i2c_client *i2c) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return devm_snd_soc_register_component(&i2c->dev, + &lm4857_component_driver, NULL, 0); +} + +static const struct i2c_device_id lm4857_i2c_id[] = { + { "lm4857", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id); + +static struct i2c_driver lm4857_i2c_driver = { + .driver = { + .name = "lm4857", + }, + .probe = lm4857_i2c_probe, + .id_table = lm4857_i2c_id, +}; + +module_i2c_driver(lm4857_i2c_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("LM4857 amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c new file mode 100644 index 0000000000..a4094689b3 --- /dev/null +++ b/sound/soc/codecs/lm49453.c @@ -0,0 +1,1462 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * lm49453.c - LM49453 ALSA Soc Audio driver + * + * Copyright (c) 2012 Texas Instruments, Inc + * + * Initially based on sound/soc/codecs/wm8350.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lm49453.h" + +static const struct reg_default lm49453_reg_defs[] = { + { 0, 0x00 }, + { 1, 0x00 }, + { 2, 0x00 }, + { 3, 0x00 }, + { 4, 0x00 }, + { 5, 0x00 }, + { 6, 0x00 }, + { 7, 0x00 }, + { 8, 0x00 }, + { 9, 0x00 }, + { 10, 0x00 }, + { 11, 0x00 }, + { 12, 0x00 }, + { 13, 0x00 }, + { 14, 0x00 }, + { 15, 0x00 }, + { 16, 0x00 }, + { 17, 0x00 }, + { 18, 0x00 }, + { 19, 0x00 }, + { 20, 0x00 }, + { 21, 0x00 }, + { 22, 0x00 }, + { 23, 0x00 }, + { 32, 0x00 }, + { 33, 0x00 }, + { 35, 0x00 }, + { 36, 0x00 }, + { 37, 0x00 }, + { 46, 0x00 }, + { 48, 0x00 }, + { 49, 0x00 }, + { 51, 0x00 }, + { 56, 0x00 }, + { 58, 0x00 }, + { 59, 0x00 }, + { 60, 0x00 }, + { 61, 0x00 }, + { 62, 0x00 }, + { 63, 0x00 }, + { 64, 0x00 }, + { 65, 0x00 }, + { 66, 0x00 }, + { 67, 0x00 }, + { 68, 0x00 }, + { 69, 0x00 }, + { 70, 0x00 }, + { 71, 0x00 }, + { 72, 0x00 }, + { 73, 0x00 }, + { 74, 0x00 }, + { 75, 0x00 }, + { 76, 0x00 }, + { 77, 0x00 }, + { 78, 0x00 }, + { 79, 0x00 }, + { 80, 0x00 }, + { 81, 0x00 }, + { 82, 0x00 }, + { 83, 0x00 }, + { 85, 0x00 }, + { 85, 0x00 }, + { 86, 0x00 }, + { 87, 0x00 }, + { 88, 0x00 }, + { 89, 0x00 }, + { 90, 0x00 }, + { 91, 0x00 }, + { 92, 0x00 }, + { 93, 0x00 }, + { 94, 0x00 }, + { 95, 0x00 }, + { 96, 0x01 }, + { 97, 0x00 }, + { 98, 0x00 }, + { 99, 0x00 }, + { 100, 0x00 }, + { 101, 0x00 }, + { 102, 0x00 }, + { 103, 0x01 }, + { 104, 0x01 }, + { 105, 0x00 }, + { 106, 0x01 }, + { 107, 0x00 }, + { 108, 0x00 }, + { 109, 0x00 }, + { 110, 0x00 }, + { 111, 0x02 }, + { 112, 0x02 }, + { 113, 0x00 }, + { 121, 0x80 }, + { 122, 0xBB }, + { 123, 0x80 }, + { 124, 0xBB }, + { 128, 0x00 }, + { 130, 0x00 }, + { 131, 0x00 }, + { 132, 0x00 }, + { 133, 0x0A }, + { 134, 0x0A }, + { 135, 0x0A }, + { 136, 0x0F }, + { 137, 0x00 }, + { 138, 0x73 }, + { 139, 0x33 }, + { 140, 0x73 }, + { 141, 0x33 }, + { 142, 0x73 }, + { 143, 0x33 }, + { 144, 0x73 }, + { 145, 0x33 }, + { 146, 0x73 }, + { 147, 0x33 }, + { 148, 0x73 }, + { 149, 0x33 }, + { 150, 0x73 }, + { 151, 0x33 }, + { 152, 0x00 }, + { 153, 0x00 }, + { 154, 0x00 }, + { 155, 0x00 }, + { 176, 0x00 }, + { 177, 0x00 }, + { 178, 0x00 }, + { 179, 0x00 }, + { 180, 0x00 }, + { 181, 0x00 }, + { 182, 0x00 }, + { 183, 0x00 }, + { 184, 0x00 }, + { 185, 0x00 }, + { 186, 0x00 }, + { 187, 0x00 }, + { 188, 0x00 }, + { 189, 0x00 }, + { 208, 0x06 }, + { 209, 0x00 }, + { 210, 0x08 }, + { 211, 0x54 }, + { 212, 0x14 }, + { 213, 0x0d }, + { 214, 0x0d }, + { 215, 0x14 }, + { 216, 0x60 }, + { 221, 0x00 }, + { 222, 0x00 }, + { 223, 0x00 }, + { 224, 0x00 }, + { 248, 0x00 }, + { 249, 0x00 }, + { 250, 0x00 }, + { 255, 0x00 }, +}; + +/* codec private data */ +struct lm49453_priv { + struct regmap *regmap; +}; + +/* capture path controls */ + +static const char *lm49453_mic2mode_text[] = {"Single Ended", "Differential"}; + +static SOC_ENUM_SINGLE_DECL(lm49453_mic2mode_enum, LM49453_P0_MICR_REG, 5, + lm49453_mic2mode_text); + +static const char *lm49453_dmic_cfg_text[] = {"DMICDAT1", "DMICDAT2"}; + +static SOC_ENUM_SINGLE_DECL(lm49453_dmic12_cfg_enum, + LM49453_P0_DIGITAL_MIC1_CONFIG_REG, 7, + lm49453_dmic_cfg_text); + +static SOC_ENUM_SINGLE_DECL(lm49453_dmic34_cfg_enum, + LM49453_P0_DIGITAL_MIC2_CONFIG_REG, 7, + lm49453_dmic_cfg_text); + +/* MUX Controls */ +static const char *lm49453_adcl_mux_text[] = { "MIC1", "Aux_L" }; + +static const char *lm49453_adcr_mux_text[] = { "MIC2", "Aux_R" }; + +static SOC_ENUM_SINGLE_DECL(lm49453_adcl_enum, + LM49453_P0_ANALOG_MIXER_ADC_REG, 0, + lm49453_adcl_mux_text); + +static SOC_ENUM_SINGLE_DECL(lm49453_adcr_enum, + LM49453_P0_ANALOG_MIXER_ADC_REG, 1, + lm49453_adcr_mux_text); + +static const struct snd_kcontrol_new lm49453_adcl_mux_control = + SOC_DAPM_ENUM("ADC Left Mux", lm49453_adcl_enum); + +static const struct snd_kcontrol_new lm49453_adcr_mux_control = + SOC_DAPM_ENUM("ADC Right Mux", lm49453_adcr_enum); + +static const struct snd_kcontrol_new lm49453_headset_left_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPL1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPL1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPL1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPL1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPL1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPL1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPL1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPL1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPL2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPL2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPL2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPL2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPL2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPL2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPL2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPL2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 0, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_headset_right_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHPR1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHPR1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHPR1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHPR1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHPR1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHPR1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHPR1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHPR1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHPR2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHPR2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHPR2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHPR2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHPR2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHPR2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHPR2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHPR2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 1, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_speaker_left_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSL1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSL1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSL1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSL1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSL1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSL1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSL1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSL1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSL2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSL2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSL2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSL2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSL2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSL2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSL2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSL2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 2, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_speaker_right_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLSR1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLSR1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLSR1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLSR1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLSR1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLSR1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLSR1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLSR1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLSR2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLSR2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLSR2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLSR2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLSR2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLSR2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLSR2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLSR2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 3, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_haptic_left_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAL1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAL1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAL1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAL1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAL1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAL1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAL1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAL1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAL2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAL2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAL2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAL2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAL2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAL2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAL2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAL2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 4, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_haptic_right_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACHAR1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACHAR1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACHAR1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACHAR1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACHAR1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACHAR1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACHAR1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACHAR1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACHAR2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACHAR2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACHAR2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACHAR2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACHAR2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACHAR2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACHAR2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACHAR2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 5, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_lineout_left_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOL1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOL1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOL1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOL1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOL1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOL1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOL1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOL1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOL2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOL2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOL2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOL2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOL2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOL2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOL2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOL2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 6, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_lineout_right_mixer[] = { +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_DACLOR1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_DACLOR1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_DACLOR1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_DACLOR1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_DACLOR1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_DACLOR1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_DACLOR1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_DACLOR1_REG, 7, 1, 0), +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_DACLOR2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_DACLOR2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_DACLOR2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_DACLOR2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_DACLOR2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_DACLOR2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_DACLOR2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_DACLOR2_REG, 7, 1, 0), +SOC_DAPM_SINGLE("Sidetone Switch", LM49453_P0_STN_SEL_REG, 7, 0, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx1_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT1_TX1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT1_TX1_REG, 7, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx2_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT1_TX2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT1_TX2_REG, 7, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx3_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX3_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX3_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX3_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX3_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX3_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX3_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_3 Switch", LM49453_P0_PORT1_TX3_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx4_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX4_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX4_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX4_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX4_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX4_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX4_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_4 Switch", LM49453_P0_PORT1_TX4_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx5_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX5_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX5_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX5_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX5_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX5_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX5_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_5 Switch", LM49453_P0_PORT1_TX5_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx6_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX6_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX6_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX6_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX6_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX6_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX6_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_6 Switch", LM49453_P0_PORT1_TX6_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx7_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX7_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX7_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX7_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX7_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX7_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX7_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_7 Switch", LM49453_P0_PORT1_TX7_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port1_tx8_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT1_TX8_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT1_TX8_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT1_TX8_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT1_TX8_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT1_TX8_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT1_TX8_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_8 Switch", LM49453_P0_PORT1_TX8_REG, 6, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port2_tx1_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX1_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX1_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX1_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX1_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX1_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX1_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_1 Switch", LM49453_P0_PORT2_TX1_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_1 Switch", LM49453_P0_PORT2_TX1_REG, 7, 1, 0), +}; + +static const struct snd_kcontrol_new lm49453_port2_tx2_mixer[] = { +SOC_DAPM_SINGLE("DMIC1L Switch", LM49453_P0_PORT2_TX2_REG, 0, 1, 0), +SOC_DAPM_SINGLE("DMIC1R Switch", LM49453_P0_PORT2_TX2_REG, 1, 1, 0), +SOC_DAPM_SINGLE("DMIC2L Switch", LM49453_P0_PORT2_TX2_REG, 2, 1, 0), +SOC_DAPM_SINGLE("DMIC2R Switch", LM49453_P0_PORT2_TX2_REG, 3, 1, 0), +SOC_DAPM_SINGLE("ADCL Switch", LM49453_P0_PORT2_TX2_REG, 4, 1, 0), +SOC_DAPM_SINGLE("ADCR Switch", LM49453_P0_PORT2_TX2_REG, 5, 1, 0), +SOC_DAPM_SINGLE("Port1_2 Switch", LM49453_P0_PORT2_TX2_REG, 6, 1, 0), +SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT2_TX2_REG, 7, 1, 0), +}; + +/* TLV Declarations */ +static const DECLARE_TLV_DB_SCALE(adc_dac_tlv, -7650, 150, 1); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 200, 1); +static const DECLARE_TLV_DB_SCALE(port_tlv, -1800, 600, 0); +static const DECLARE_TLV_DB_SCALE(stn_tlv, -7200, 150, 0); + +static const struct snd_kcontrol_new lm49453_sidetone_mixer_controls[] = { +/* Sidetone supports mono only */ +SOC_DAPM_SINGLE_TLV("Sidetone ADCL Volume", LM49453_P0_STN_VOL_ADCL_REG, + 0, 0x3F, 0, stn_tlv), +SOC_DAPM_SINGLE_TLV("Sidetone ADCR Volume", LM49453_P0_STN_VOL_ADCR_REG, + 0, 0x3F, 0, stn_tlv), +SOC_DAPM_SINGLE_TLV("Sidetone DMIC1L Volume", LM49453_P0_STN_VOL_DMIC1L_REG, + 0, 0x3F, 0, stn_tlv), +SOC_DAPM_SINGLE_TLV("Sidetone DMIC1R Volume", LM49453_P0_STN_VOL_DMIC1R_REG, + 0, 0x3F, 0, stn_tlv), +SOC_DAPM_SINGLE_TLV("Sidetone DMIC2L Volume", LM49453_P0_STN_VOL_DMIC2L_REG, + 0, 0x3F, 0, stn_tlv), +SOC_DAPM_SINGLE_TLV("Sidetone DMIC2R Volume", LM49453_P0_STN_VOL_DMIC2R_REG, + 0, 0x3F, 0, stn_tlv), +}; + +static const struct snd_kcontrol_new lm49453_snd_controls[] = { + /* mic1 and mic2 supports mono only */ + SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_MICL_REG, 0, 15, 0, mic_tlv), + SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_MICR_REG, 0, 15, 0, mic_tlv), + + SOC_SINGLE_TLV("ADCL Volume", LM49453_P0_ADC_LEVELL_REG, 0, 63, + 0, adc_dac_tlv), + SOC_SINGLE_TLV("ADCR Volume", LM49453_P0_ADC_LEVELR_REG, 0, 63, + 0, adc_dac_tlv), + + SOC_DOUBLE_R_TLV("DMIC1 Volume", LM49453_P0_DMIC1_LEVELL_REG, + LM49453_P0_DMIC1_LEVELR_REG, 0, 63, 0, adc_dac_tlv), + SOC_DOUBLE_R_TLV("DMIC2 Volume", LM49453_P0_DMIC2_LEVELL_REG, + LM49453_P0_DMIC2_LEVELR_REG, 0, 63, 0, adc_dac_tlv), + + SOC_DAPM_ENUM("Mic2Mode", lm49453_mic2mode_enum), + SOC_DAPM_ENUM("DMIC12 SRC", lm49453_dmic12_cfg_enum), + SOC_DAPM_ENUM("DMIC34 SRC", lm49453_dmic34_cfg_enum), + + /* Capture path filter enable */ + SOC_SINGLE("DMIC1 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG, + 0, 1, 0), + SOC_SINGLE("DMIC2 HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG, + 1, 1, 0), + SOC_SINGLE("ADC HPFilter Switch", LM49453_P0_ADC_FX_ENABLES_REG, + 2, 1, 0), + + SOC_DOUBLE_R_TLV("DAC HP Volume", LM49453_P0_DAC_HP_LEVELL_REG, + LM49453_P0_DAC_HP_LEVELR_REG, 0, 63, 0, adc_dac_tlv), + SOC_DOUBLE_R_TLV("DAC LO Volume", LM49453_P0_DAC_LO_LEVELL_REG, + LM49453_P0_DAC_LO_LEVELR_REG, 0, 63, 0, adc_dac_tlv), + SOC_DOUBLE_R_TLV("DAC LS Volume", LM49453_P0_DAC_LS_LEVELL_REG, + LM49453_P0_DAC_LS_LEVELR_REG, 0, 63, 0, adc_dac_tlv), + SOC_DOUBLE_R_TLV("DAC HA Volume", LM49453_P0_DAC_HA_LEVELL_REG, + LM49453_P0_DAC_HA_LEVELR_REG, 0, 63, 0, adc_dac_tlv), + + SOC_SINGLE_TLV("EP Volume", LM49453_P0_DAC_LS_LEVELL_REG, + 0, 63, 0, adc_dac_tlv), + + SOC_SINGLE_TLV("PORT1_1_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG, + 0, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_2_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG, + 2, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_3_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG, + 4, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_4_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG, + 6, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_5_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG, + 0, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_6_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG, + 2, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_7_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG, + 4, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT1_8_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL2_REG, + 6, 3, 0, port_tlv), + + SOC_SINGLE_TLV("PORT2_1_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG, + 0, 3, 0, port_tlv), + SOC_SINGLE_TLV("PORT2_2_RX_LVL Volume", LM49453_P0_PORT2_RX_LVL_REG, + 2, 3, 0, port_tlv), + + SOC_SINGLE("Port1 Playback Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG, + 1, 1, 0), + SOC_SINGLE("Port2 Playback Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG, + 1, 1, 0), + SOC_SINGLE("Port1 Capture Switch", LM49453_P0_AUDIO_PORT1_BASIC_REG, + 2, 1, 0), + SOC_SINGLE("Port2 Capture Switch", LM49453_P0_AUDIO_PORT2_BASIC_REG, + 2, 1, 0) + +}; + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget lm49453_dapm_widgets[] = { + + /* All end points HP,EP, LS, Lineout and Haptic */ + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), + SND_SOC_DAPM_OUTPUT("EPOUT"), + SND_SOC_DAPM_OUTPUT("LSOUTL"), + SND_SOC_DAPM_OUTPUT("LSOUTR"), + SND_SOC_DAPM_OUTPUT("LOOUTR"), + SND_SOC_DAPM_OUTPUT("LOOUTL"), + SND_SOC_DAPM_OUTPUT("HAOUTL"), + SND_SOC_DAPM_OUTPUT("HAOUTR"), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("DMIC1DAT"), + SND_SOC_DAPM_INPUT("DMIC2DAT"), + SND_SOC_DAPM_INPUT("AUXL"), + SND_SOC_DAPM_INPUT("AUXR"), + + SND_SOC_DAPM_PGA("PORT1_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_3_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_4_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_5_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_6_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_7_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT1_8_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT2_1_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PORT2_2_RX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("AMIC1Bias", LM49453_P0_MICL_REG, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AMIC2Bias", LM49453_P0_MICR_REG, 6, 0, NULL, 0), + + /* playback path driver enables */ + SND_SOC_DAPM_OUT_DRV("Headset Switch", + LM49453_P0_PMC_SETUP_REG, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Earpiece Switch", + LM49453_P0_EP_REG, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Speaker Left Switch", + LM49453_P0_DIS_PKVL_FB_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Speaker Right Switch", + LM49453_P0_DIS_PKVL_FB_REG, 1, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Haptic Left Switch", + LM49453_P0_DIS_PKVL_FB_REG, 2, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Haptic Right Switch", + LM49453_P0_DIS_PKVL_FB_REG, 3, 1, NULL, 0), + + /* DAC */ + SND_SOC_DAPM_DAC("HPL DAC", "Headset", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("HPR DAC", "Headset", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("LSL DAC", "Speaker", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("LSR DAC", "Speaker", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("HAL DAC", "Haptic", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("HAR DAC", "Haptic", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("LOL DAC", "Lineout", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("LOR DAC", "Lineout", SND_SOC_NOPM, 0, 0), + + + SND_SOC_DAPM_PGA("AUXL Input", + LM49453_P0_ANALOG_MIXER_ADC_REG, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("AUXR Input", + LM49453_P0_ANALOG_MIXER_ADC_REG, 3, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Sidetone", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("DMIC1 Left", "Capture", SND_SOC_NOPM, 1, 0), + SND_SOC_DAPM_ADC("DMIC1 Right", "Capture", SND_SOC_NOPM, 1, 0), + SND_SOC_DAPM_ADC("DMIC2 Left", "Capture", SND_SOC_NOPM, 1, 0), + SND_SOC_DAPM_ADC("DMIC2 Right", "Capture", SND_SOC_NOPM, 1, 0), + + SND_SOC_DAPM_ADC("ADC Left", "Capture", SND_SOC_NOPM, 1, 0), + SND_SOC_DAPM_ADC("ADC Right", "Capture", SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("ADCL Mux", SND_SOC_NOPM, 0, 0, + &lm49453_adcl_mux_control), + SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0, + &lm49453_adcr_mux_control), + + SND_SOC_DAPM_MUX("Mic1 Input", + SND_SOC_NOPM, 0, 0, &lm49453_adcl_mux_control), + + SND_SOC_DAPM_MUX("Mic2 Input", + SND_SOC_NOPM, 0, 0, &lm49453_adcr_mux_control), + + /* AIF */ + SND_SOC_DAPM_AIF_IN("PORT1_SDI", NULL, 0, + LM49453_P0_PULL_CONFIG1_REG, 2, 0), + SND_SOC_DAPM_AIF_IN("PORT2_SDI", NULL, 0, + LM49453_P0_PULL_CONFIG1_REG, 6, 0), + + SND_SOC_DAPM_AIF_OUT("PORT1_SDO", NULL, 0, + LM49453_P0_PULL_CONFIG1_REG, 3, 0), + SND_SOC_DAPM_AIF_OUT("PORT2_SDO", NULL, 0, + LM49453_P0_PULL_CONFIG1_REG, 7, 0), + + /* Port1 TX controls */ + SND_SOC_DAPM_OUT_DRV("P1_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_3_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_4_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_5_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_6_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_7_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P1_8_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Port2 TX controls */ + SND_SOC_DAPM_OUT_DRV("P2_1_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("P2_2_TX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Sidetone Mixer */ + SND_SOC_DAPM_MIXER("Sidetone Mixer", SND_SOC_NOPM, 0, 0, + lm49453_sidetone_mixer_controls, + ARRAY_SIZE(lm49453_sidetone_mixer_controls)), + + /* DAC MIXERS */ + SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0, + lm49453_headset_left_mixer, + ARRAY_SIZE(lm49453_headset_left_mixer)), + SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0, + lm49453_headset_right_mixer, + ARRAY_SIZE(lm49453_headset_right_mixer)), + SND_SOC_DAPM_MIXER("LOL Mixer", SND_SOC_NOPM, 0, 0, + lm49453_lineout_left_mixer, + ARRAY_SIZE(lm49453_lineout_left_mixer)), + SND_SOC_DAPM_MIXER("LOR Mixer", SND_SOC_NOPM, 0, 0, + lm49453_lineout_right_mixer, + ARRAY_SIZE(lm49453_lineout_right_mixer)), + SND_SOC_DAPM_MIXER("LSL Mixer", SND_SOC_NOPM, 0, 0, + lm49453_speaker_left_mixer, + ARRAY_SIZE(lm49453_speaker_left_mixer)), + SND_SOC_DAPM_MIXER("LSR Mixer", SND_SOC_NOPM, 0, 0, + lm49453_speaker_right_mixer, + ARRAY_SIZE(lm49453_speaker_right_mixer)), + SND_SOC_DAPM_MIXER("HAL Mixer", SND_SOC_NOPM, 0, 0, + lm49453_haptic_left_mixer, + ARRAY_SIZE(lm49453_haptic_left_mixer)), + SND_SOC_DAPM_MIXER("HAR Mixer", SND_SOC_NOPM, 0, 0, + lm49453_haptic_right_mixer, + ARRAY_SIZE(lm49453_haptic_right_mixer)), + + /* Capture Mixer */ + SND_SOC_DAPM_MIXER("Port1_1 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx1_mixer, + ARRAY_SIZE(lm49453_port1_tx1_mixer)), + SND_SOC_DAPM_MIXER("Port1_2 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx2_mixer, + ARRAY_SIZE(lm49453_port1_tx2_mixer)), + SND_SOC_DAPM_MIXER("Port1_3 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx3_mixer, + ARRAY_SIZE(lm49453_port1_tx3_mixer)), + SND_SOC_DAPM_MIXER("Port1_4 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx4_mixer, + ARRAY_SIZE(lm49453_port1_tx4_mixer)), + SND_SOC_DAPM_MIXER("Port1_5 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx5_mixer, + ARRAY_SIZE(lm49453_port1_tx5_mixer)), + SND_SOC_DAPM_MIXER("Port1_6 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx6_mixer, + ARRAY_SIZE(lm49453_port1_tx6_mixer)), + SND_SOC_DAPM_MIXER("Port1_7 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx7_mixer, + ARRAY_SIZE(lm49453_port1_tx7_mixer)), + SND_SOC_DAPM_MIXER("Port1_8 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port1_tx8_mixer, + ARRAY_SIZE(lm49453_port1_tx8_mixer)), + + SND_SOC_DAPM_MIXER("Port2_1 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port2_tx1_mixer, + ARRAY_SIZE(lm49453_port2_tx1_mixer)), + SND_SOC_DAPM_MIXER("Port2_2 Mixer", SND_SOC_NOPM, 0, 0, + lm49453_port2_tx2_mixer, + ARRAY_SIZE(lm49453_port2_tx2_mixer)), +}; + +static const struct snd_soc_dapm_route lm49453_audio_map[] = { + /* Port SDI mapping */ + { "PORT1_1_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_2_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_3_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_4_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_5_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_6_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_7_RX", "Port1 Playback Switch", "PORT1_SDI" }, + { "PORT1_8_RX", "Port1 Playback Switch", "PORT1_SDI" }, + + { "PORT2_1_RX", "Port2 Playback Switch", "PORT2_SDI" }, + { "PORT2_2_RX", "Port2 Playback Switch", "PORT2_SDI" }, + + /* HP mapping */ + { "HPL Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "HPL Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "HPL Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "HPL Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "HPL Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "HPL Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "HPL Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "HPL Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + { "HPL Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "HPL Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "HPL Mixer", "ADCL Switch", "ADC Left" }, + { "HPL Mixer", "ADCR Switch", "ADC Right" }, + { "HPL Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "HPL Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "HPL Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "HPL Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "HPL Mixer", "Sidetone Switch", "Sidetone" }, + + { "HPL DAC", NULL, "HPL Mixer" }, + + { "HPR Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "HPR Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "HPR Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "HPR Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "HPR Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "HPR Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "HPR Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "HPR Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "HPR Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "HPR Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "HPR Mixer", "ADCL Switch", "ADC Left" }, + { "HPR Mixer", "ADCR Switch", "ADC Right" }, + { "HPR Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "HPR Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "HPR Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "HPR Mixer", "DMIC2L Switch", "DMIC2 Right" }, + { "HPR Mixer", "Sidetone Switch", "Sidetone" }, + + { "HPR DAC", NULL, "HPR Mixer" }, + + { "HPOUTL", "Headset Switch", "HPL DAC"}, + { "HPOUTR", "Headset Switch", "HPR DAC"}, + + /* EP map */ + { "EPOUT", "Earpiece Switch", "HPL DAC" }, + + /* Speaker map */ + { "LSL Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "LSL Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "LSL Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "LSL Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "LSL Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "LSL Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "LSL Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "LSL Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "LSL Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "LSL Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "LSL Mixer", "ADCL Switch", "ADC Left" }, + { "LSL Mixer", "ADCR Switch", "ADC Right" }, + { "LSL Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "LSL Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "LSL Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "LSL Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "LSL Mixer", "Sidetone Switch", "Sidetone" }, + + { "LSL DAC", NULL, "LSL Mixer" }, + + { "LSR Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "LSR Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "LSR Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "LSR Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "LSR Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "LSR Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "LSR Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "LSR Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "LSR Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "LSR Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "LSR Mixer", "ADCL Switch", "ADC Left" }, + { "LSR Mixer", "ADCR Switch", "ADC Right" }, + { "LSR Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "LSR Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "LSR Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "LSR Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "LSR Mixer", "Sidetone Switch", "Sidetone" }, + + { "LSR DAC", NULL, "LSR Mixer" }, + + { "LSOUTL", "Speaker Left Switch", "LSL DAC"}, + { "LSOUTR", "Speaker Left Switch", "LSR DAC"}, + + /* Haptic map */ + { "HAL Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "HAL Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "HAL Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "HAL Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "HAL Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "HAL Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "HAL Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "HAL Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "HAL Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "HAL Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "HAL Mixer", "ADCL Switch", "ADC Left" }, + { "HAL Mixer", "ADCR Switch", "ADC Right" }, + { "HAL Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "HAL Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "HAL Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "HAL Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "HAL Mixer", "Sidetone Switch", "Sidetone" }, + + { "HAL DAC", NULL, "HAL Mixer" }, + + { "HAR Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "HAR Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "HAR Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "HAR Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "HAR Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "HAR Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "HAR Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "HAR Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "HAR Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "HAR Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "HAR Mixer", "ADCL Switch", "ADC Left" }, + { "HAR Mixer", "ADCR Switch", "ADC Right" }, + { "HAR Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "HAR Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "HAR Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "HAR Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "HAR Mixer", "Sideton Switch", "Sidetone" }, + + { "HAR DAC", NULL, "HAR Mixer" }, + + { "HAOUTL", "Haptic Left Switch", "HAL DAC" }, + { "HAOUTR", "Haptic Right Switch", "HAR DAC" }, + + /* Lineout map */ + { "LOL Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "LOL Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "LOL Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "LOL Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "LOL Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "LOL Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "LOL Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "LOL Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "LOL Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "LOL Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "LOL Mixer", "ADCL Switch", "ADC Left" }, + { "LOL Mixer", "ADCR Switch", "ADC Right" }, + { "LOL Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "LOL Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "LOL Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "LOL Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "LOL Mixer", "Sidetone Switch", "Sidetone" }, + + { "LOL DAC", NULL, "LOL Mixer" }, + + { "LOR Mixer", "Port1_1 Switch", "PORT1_1_RX" }, + { "LOR Mixer", "Port1_2 Switch", "PORT1_2_RX" }, + { "LOR Mixer", "Port1_3 Switch", "PORT1_3_RX" }, + { "LOR Mixer", "Port1_4 Switch", "PORT1_4_RX" }, + { "LOR Mixer", "Port1_5 Switch", "PORT1_5_RX" }, + { "LOR Mixer", "Port1_6 Switch", "PORT1_6_RX" }, + { "LOR Mixer", "Port1_7 Switch", "PORT1_7_RX" }, + { "LOR Mixer", "Port1_8 Switch", "PORT1_8_RX" }, + + /* Port 2 */ + { "LOR Mixer", "Port2_1 Switch", "PORT2_1_RX" }, + { "LOR Mixer", "Port2_2 Switch", "PORT2_2_RX" }, + + { "LOR Mixer", "ADCL Switch", "ADC Left" }, + { "LOR Mixer", "ADCR Switch", "ADC Right" }, + { "LOR Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "LOR Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "LOR Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "LOR Mixer", "DMIC2R Switch", "DMIC2 Right" }, + { "LOR Mixer", "Sidetone Switch", "Sidetone" }, + + { "LOR DAC", NULL, "LOR Mixer" }, + + { "LOOUTL", NULL, "LOL DAC" }, + { "LOOUTR", NULL, "LOR DAC" }, + + /* TX map */ + /* Port1 mappings */ + { "Port1_1 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_1 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_1 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_1 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_1 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_1 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_2 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_2 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_2 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_2 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_2 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_2 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_3 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_3 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_3 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_3 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_3 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_3 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_4 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_4 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_4 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_4 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_4 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_4 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_5 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_5 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_5 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_5 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_5 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_5 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_6 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_6 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_6 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_6 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_6 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_6 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_7 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_7 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_7 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_7 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_7 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_7 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port1_8 Mixer", "ADCL Switch", "ADC Left" }, + { "Port1_8 Mixer", "ADCR Switch", "ADC Right" }, + { "Port1_8 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port1_8 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port1_8 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port1_8 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port2_1 Mixer", "ADCL Switch", "ADC Left" }, + { "Port2_1 Mixer", "ADCR Switch", "ADC Right" }, + { "Port2_1 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port2_1 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port2_1 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port2_1 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "Port2_2 Mixer", "ADCL Switch", "ADC Left" }, + { "Port2_2 Mixer", "ADCR Switch", "ADC Right" }, + { "Port2_2 Mixer", "DMIC1L Switch", "DMIC1 Left" }, + { "Port2_2 Mixer", "DMIC1R Switch", "DMIC1 Right" }, + { "Port2_2 Mixer", "DMIC2L Switch", "DMIC2 Left" }, + { "Port2_2 Mixer", "DMIC2R Switch", "DMIC2 Right" }, + + { "P1_1_TX", NULL, "Port1_1 Mixer" }, + { "P1_2_TX", NULL, "Port1_2 Mixer" }, + { "P1_3_TX", NULL, "Port1_3 Mixer" }, + { "P1_4_TX", NULL, "Port1_4 Mixer" }, + { "P1_5_TX", NULL, "Port1_5 Mixer" }, + { "P1_6_TX", NULL, "Port1_6 Mixer" }, + { "P1_7_TX", NULL, "Port1_7 Mixer" }, + { "P1_8_TX", NULL, "Port1_8 Mixer" }, + + { "P2_1_TX", NULL, "Port2_1 Mixer" }, + { "P2_2_TX", NULL, "Port2_2 Mixer" }, + + { "PORT1_SDO", "Port1 Capture Switch", "P1_1_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_2_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_3_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_4_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_5_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_6_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_7_TX"}, + { "PORT1_SDO", "Port1 Capture Switch", "P1_8_TX"}, + + { "PORT2_SDO", "Port2 Capture Switch", "P2_1_TX"}, + { "PORT2_SDO", "Port2 Capture Switch", "P2_2_TX"}, + + { "Mic1 Input", NULL, "AMIC1" }, + { "Mic2 Input", NULL, "AMIC2" }, + + { "AUXL Input", NULL, "AUXL" }, + { "AUXR Input", NULL, "AUXR" }, + + /* AUX connections */ + { "ADCL Mux", "Aux_L", "AUXL Input" }, + { "ADCL Mux", "MIC1", "Mic1 Input" }, + + { "ADCR Mux", "Aux_R", "AUXR Input" }, + { "ADCR Mux", "MIC2", "Mic2 Input" }, + + /* ADC connection */ + { "ADC Left", NULL, "ADCL Mux"}, + { "ADC Right", NULL, "ADCR Mux"}, + + { "DMIC1 Left", NULL, "DMIC1DAT"}, + { "DMIC1 Right", NULL, "DMIC1DAT"}, + { "DMIC2 Left", NULL, "DMIC2DAT"}, + { "DMIC2 Right", NULL, "DMIC2DAT"}, + + /* Sidetone map */ + { "Sidetone Mixer", NULL, "ADC Left" }, + { "Sidetone Mixer", NULL, "ADC Right" }, + { "Sidetone Mixer", NULL, "DMIC1 Left" }, + { "Sidetone Mixer", NULL, "DMIC1 Right" }, + { "Sidetone Mixer", NULL, "DMIC2 Left" }, + { "Sidetone Mixer", NULL, "DMIC2 Right" }, + + { "Sidetone", "Sidetone Switch", "Sidetone Mixer" }, +}; + +static int lm49453_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 clk_div = 0; + + /* Setting DAC clock dividers based on substream sample rate. */ + switch (params_rate(params)) { + case 8000: + case 16000: + case 32000: + case 24000: + case 48000: + clk_div = 256; + break; + case 11025: + case 22050: + case 44100: + clk_div = 216; + break; + case 96000: + clk_div = 127; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, LM49453_P0_ADC_CLK_DIV_REG, clk_div); + snd_soc_component_write(component, LM49453_P0_DAC_HP_CLK_DIV_REG, clk_div); + + return 0; +} + +static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + + u16 aif_val; + int mode = 0; + int clk_phase = 0; + int clk_shift = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + aif_val = 0; + break; + case SND_SOC_DAIFMT_CBC_CFP: + aif_val = LM49453_AUDIO_PORT1_BASIC_SYNC_MS; + break; + case SND_SOC_DAIFMT_CBP_CFC: + aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS; + break; + case SND_SOC_DAIFMT_CBP_CFP: + aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS | + LM49453_AUDIO_PORT1_BASIC_SYNC_MS; + break; + default: + return -EINVAL; + } + + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + mode = 1; + clk_phase = (1 << 5); + clk_shift = 1; + break; + case SND_SOC_DAIFMT_DSP_B: + mode = 1; + clk_phase = (1 << 5); + clk_shift = 0; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, LM49453_P0_AUDIO_PORT1_BASIC_REG, + LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(0)|BIT(5), + (aif_val | mode | clk_phase)); + + snd_soc_component_write(component, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift); + + return 0; +} + +static int lm49453_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + u16 pll_clk = 0; + + switch (freq) { + case 12288000: + case 26000000: + case 19200000: + /* pll clk slection */ + pll_clk = 0; + break; + case 48000: + case 32576: + return 0; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG, BIT(4), pll_clk); + + return 0; +} + +static int lm49453_hp_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(1)|BIT(0), + (mute ? (BIT(1)|BIT(0)) : 0)); + return 0; +} + +static int lm49453_lo_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(3)|BIT(2), + (mute ? (BIT(3)|BIT(2)) : 0)); + return 0; +} + +static int lm49453_ls_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(5)|BIT(4), + (mute ? (BIT(5)|BIT(4)) : 0)); + return 0; +} + +static int lm49453_ep_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(4), + (mute ? BIT(4) : 0)); + return 0; +} + +static int lm49453_ha_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + snd_soc_component_update_bits(dai->component, LM49453_P0_DAC_DSP_REG, BIT(7)|BIT(6), + (mute ? (BIT(7)|BIT(6)) : 0)); + return 0; +} + +static int lm49453_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct lm49453_priv *lm49453 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + regcache_sync(lm49453->regmap); + + snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG, + LM49453_PMC_SETUP_CHIP_EN, LM49453_CHIP_EN); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG, + LM49453_PMC_SETUP_CHIP_EN, 0); + break; + } + + return 0; +} + +/* Formates supported by LM49453 driver. */ +#define LM49453_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops lm49453_headset_dai_ops = { + .hw_params = lm49453_hw_params, + .set_sysclk = lm49453_set_dai_sysclk, + .set_fmt = lm49453_set_dai_fmt, + .mute_stream = lm49453_hp_mute, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops lm49453_speaker_dai_ops = { + .hw_params = lm49453_hw_params, + .set_sysclk = lm49453_set_dai_sysclk, + .set_fmt = lm49453_set_dai_fmt, + .mute_stream = lm49453_ls_mute, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops lm49453_haptic_dai_ops = { + .hw_params = lm49453_hw_params, + .set_sysclk = lm49453_set_dai_sysclk, + .set_fmt = lm49453_set_dai_fmt, + .mute_stream = lm49453_ha_mute, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops lm49453_ep_dai_ops = { + .hw_params = lm49453_hw_params, + .set_sysclk = lm49453_set_dai_sysclk, + .set_fmt = lm49453_set_dai_fmt, + .mute_stream = lm49453_ep_mute, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops lm49453_lineout_dai_ops = { + .hw_params = lm49453_hw_params, + .set_sysclk = lm49453_set_dai_sysclk, + .set_fmt = lm49453_set_dai_fmt, + .mute_stream = lm49453_lo_mute, + .no_capture_mute = 1, +}; + +/* LM49453 dai structure. */ +static struct snd_soc_dai_driver lm49453_dai[] = { + { + .name = "LM49453 Headset", + .playback = { + .stream_name = "Headset", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 5, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .ops = &lm49453_headset_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "LM49453 Speaker", + .playback = { + .stream_name = "Speaker", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .ops = &lm49453_speaker_dai_ops, + }, + { + .name = "LM49453 Haptic", + .playback = { + .stream_name = "Haptic", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .ops = &lm49453_haptic_dai_ops, + }, + { + .name = "LM49453 Earpiece", + .playback = { + .stream_name = "Earpiece", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .ops = &lm49453_ep_dai_ops, + }, + { + .name = "LM49453 line out", + .playback = { + .stream_name = "Lineout", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = LM49453_FORMATS, + }, + .ops = &lm49453_lineout_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_lm49453 = { + .set_bias_level = lm49453_set_bias_level, + .controls = lm49453_snd_controls, + .num_controls = ARRAY_SIZE(lm49453_snd_controls), + .dapm_widgets = lm49453_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(lm49453_dapm_widgets), + .dapm_routes = lm49453_audio_map, + .num_dapm_routes = ARRAY_SIZE(lm49453_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config lm49453_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = LM49453_MAX_REGISTER, + .reg_defaults = lm49453_reg_defs, + .num_reg_defaults = ARRAY_SIZE(lm49453_reg_defs), + .cache_type = REGCACHE_RBTREE, +}; + +static int lm49453_i2c_probe(struct i2c_client *i2c) +{ + struct lm49453_priv *lm49453; + int ret = 0; + + lm49453 = devm_kzalloc(&i2c->dev, sizeof(struct lm49453_priv), + GFP_KERNEL); + + if (lm49453 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, lm49453); + + lm49453->regmap = devm_regmap_init_i2c(i2c, &lm49453_regmap_config); + if (IS_ERR(lm49453->regmap)) { + ret = PTR_ERR(lm49453->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_lm49453, + lm49453_dai, ARRAY_SIZE(lm49453_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register component: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id lm49453_i2c_id[] = { + { "lm49453", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id); + +static struct i2c_driver lm49453_i2c_driver = { + .driver = { + .name = "lm49453", + }, + .probe = lm49453_i2c_probe, + .id_table = lm49453_i2c_id, +}; + +module_i2c_driver(lm49453_i2c_driver); + +MODULE_DESCRIPTION("ASoC LM49453 driver"); +MODULE_AUTHOR("M R Swami Reddy "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/lm49453.h b/sound/soc/codecs/lm49453.h new file mode 100644 index 0000000000..578a773e6f --- /dev/null +++ b/sound/soc/codecs/lm49453.h @@ -0,0 +1,376 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * lm49453.h - LM49453 ALSA Soc Audio drive + * + * Copyright (c) 2012 Texas Instruments, Inc + */ + +#ifndef _LM49453_H +#define _LM49453_H + +#include + +/* LM49453_P0 register space for page0 */ +#define LM49453_P0_PMC_SETUP_REG 0x00 +#define LM49453_P0_PLL_CLK_SEL1_REG 0x01 +#define LM49453_P0_PLL_CLK_SEL2_REG 0x02 +#define LM49453_P0_PMC_CLK_DIV_REG 0x03 +#define LM49453_P0_HSDET_CLK_DIV_REG 0x04 +#define LM49453_P0_DMIC_CLK_DIV_REG 0x05 +#define LM49453_P0_ADC_CLK_DIV_REG 0x06 +#define LM49453_P0_DAC_OT_CLK_DIV_REG 0x07 +#define LM49453_P0_PLL_HF_M_REG 0x08 +#define LM49453_P0_PLL_LF_M_REG 0x09 +#define LM49453_P0_PLL_NL_REG 0x0A +#define LM49453_P0_PLL_N_MODL_REG 0x0B +#define LM49453_P0_PLL_N_MODH_REG 0x0C +#define LM49453_P0_PLL_P1_REG 0x0D +#define LM49453_P0_PLL_P2_REG 0x0E +#define LM49453_P0_FLL_REF_FREQL_REG 0x0F +#define LM49453_P0_FLL_REF_FREQH_REG 0x10 +#define LM49453_P0_VCO_TARGETLL_REG 0x11 +#define LM49453_P0_VCO_TARGETLH_REG 0x12 +#define LM49453_P0_VCO_TARGETHL_REG 0x13 +#define LM49453_P0_VCO_TARGETHH_REG 0x14 +#define LM49453_P0_PLL_CONFIG_REG 0x15 +#define LM49453_P0_DAC_CLK_SEL_REG 0x16 +#define LM49453_P0_DAC_HP_CLK_DIV_REG 0x17 + +/* Analog Mixer Input Stages */ +#define LM49453_P0_MICL_REG 0x20 +#define LM49453_P0_MICR_REG 0x21 +#define LM49453_P0_EP_REG 0x24 +#define LM49453_P0_DIS_PKVL_FB_REG 0x25 + +/* Analog Mixer Output Stages */ +#define LM49453_P0_ANALOG_MIXER_ADC_REG 0x2E + +/*ADC or DAC */ +#define LM49453_P0_ADC_DSP_REG 0x30 +#define LM49453_P0_DAC_DSP_REG 0x31 + +/* EFFECTS ENABLES */ +#define LM49453_P0_ADC_FX_ENABLES_REG 0x33 + +/* GPIO */ +#define LM49453_P0_GPIO1_REG 0x38 +#define LM49453_P0_GPIO2_REG 0x39 +#define LM49453_P0_GPIO3_REG 0x3A +#define LM49453_P0_HAP_CTL_REG 0x3B +#define LM49453_P0_HAP_FREQ_PROG_LEFTL_REG 0x3C +#define LM49453_P0_HAP_FREQ_PROG_LEFTH_REG 0x3D +#define LM49453_P0_HAP_FREQ_PROG_RIGHTL_REG 0x3E +#define LM49453_P0_HAP_FREQ_PROG_RIGHTH_REG 0x3F + +/* DIGITAL MIXER */ +#define LM49453_P0_DMIX_CLK_SEL_REG 0x40 +#define LM49453_P0_PORT1_RX_LVL1_REG 0x41 +#define LM49453_P0_PORT1_RX_LVL2_REG 0x42 +#define LM49453_P0_PORT2_RX_LVL_REG 0x43 +#define LM49453_P0_PORT1_TX1_REG 0x44 +#define LM49453_P0_PORT1_TX2_REG 0x45 +#define LM49453_P0_PORT1_TX3_REG 0x46 +#define LM49453_P0_PORT1_TX4_REG 0x47 +#define LM49453_P0_PORT1_TX5_REG 0x48 +#define LM49453_P0_PORT1_TX6_REG 0x49 +#define LM49453_P0_PORT1_TX7_REG 0x4A +#define LM49453_P0_PORT1_TX8_REG 0x4B +#define LM49453_P0_PORT2_TX1_REG 0x4C +#define LM49453_P0_PORT2_TX2_REG 0x4D +#define LM49453_P0_STN_SEL_REG 0x4F +#define LM49453_P0_DACHPL1_REG 0x50 +#define LM49453_P0_DACHPL2_REG 0x51 +#define LM49453_P0_DACHPR1_REG 0x52 +#define LM49453_P0_DACHPR2_REG 0x53 +#define LM49453_P0_DACLOL1_REG 0x54 +#define LM49453_P0_DACLOL2_REG 0x55 +#define LM49453_P0_DACLOR1_REG 0x56 +#define LM49453_P0_DACLOR2_REG 0x57 +#define LM49453_P0_DACLSL1_REG 0x58 +#define LM49453_P0_DACLSL2_REG 0x59 +#define LM49453_P0_DACLSR1_REG 0x5A +#define LM49453_P0_DACLSR2_REG 0x5B +#define LM49453_P0_DACHAL1_REG 0x5C +#define LM49453_P0_DACHAL2_REG 0x5D +#define LM49453_P0_DACHAR1_REG 0x5E +#define LM49453_P0_DACHAR2_REG 0x5F + +/* AUDIO PORT 1 (TDM) */ +#define LM49453_P0_AUDIO_PORT1_BASIC_REG 0x60 +#define LM49453_P0_AUDIO_PORT1_CLK_GEN1_REG 0x61 +#define LM49453_P0_AUDIO_PORT1_CLK_GEN2_REG 0x62 +#define LM49453_P0_AUDIO_PORT1_CLK_GEN3_REG 0x63 +#define LM49453_P0_AUDIO_PORT1_SYNC_RATE_REG 0x64 +#define LM49453_P0_AUDIO_PORT1_SYNC_SDO_SETUP_REG 0x65 +#define LM49453_P0_AUDIO_PORT1_DATA_WIDTH_REG 0x66 +#define LM49453_P0_AUDIO_PORT1_RX_MSB_REG 0x67 +#define LM49453_P0_AUDIO_PORT1_TX_MSB_REG 0x68 +#define LM49453_P0_AUDIO_PORT1_TDM_CHANNELS_REG 0x69 + +/* AUDIO PORT 2 */ +#define LM49453_P0_AUDIO_PORT2_BASIC_REG 0x6A +#define LM49453_P0_AUDIO_PORT2_CLK_GEN1_REG 0x6B +#define LM49453_P0_AUDIO_PORT2_CLK_GEN2_REG 0x6C +#define LM49453_P0_AUDIO_PORT2_SYNC_GEN_REG 0x6D +#define LM49453_P0_AUDIO_PORT2_DATA_WIDTH_REG 0x6E +#define LM49453_P0_AUDIO_PORT2_RX_MODE_REG 0x6F +#define LM49453_P0_AUDIO_PORT2_TX_MODE_REG 0x70 + +/* SAMPLE RATE */ +#define LM49453_P0_PORT1_SR_LSB_REG 0x79 +#define LM49453_P0_PORT1_SR_MSB_REG 0x7A +#define LM49453_P0_PORT2_SR_LSB_REG 0x7B +#define LM49453_P0_PORT2_SR_MSB_REG 0x7C + +/* EFFECTS - HPFs */ +#define LM49453_P0_HPF_REG 0x80 + +/* EFFECTS ADC ALC */ +#define LM49453_P0_ADC_ALC1_REG 0x82 +#define LM49453_P0_ADC_ALC2_REG 0x83 +#define LM49453_P0_ADC_ALC3_REG 0x84 +#define LM49453_P0_ADC_ALC4_REG 0x85 +#define LM49453_P0_ADC_ALC5_REG 0x86 +#define LM49453_P0_ADC_ALC6_REG 0x87 +#define LM49453_P0_ADC_ALC7_REG 0x88 +#define LM49453_P0_ADC_ALC8_REG 0x89 +#define LM49453_P0_DMIC1_LEVELL_REG 0x8A +#define LM49453_P0_DMIC1_LEVELR_REG 0x8B +#define LM49453_P0_DMIC2_LEVELL_REG 0x8C +#define LM49453_P0_DMIC2_LEVELR_REG 0x8D +#define LM49453_P0_ADC_LEVELL_REG 0x8E +#define LM49453_P0_ADC_LEVELR_REG 0x8F +#define LM49453_P0_DAC_HP_LEVELL_REG 0x90 +#define LM49453_P0_DAC_HP_LEVELR_REG 0x91 +#define LM49453_P0_DAC_LO_LEVELL_REG 0x92 +#define LM49453_P0_DAC_LO_LEVELR_REG 0x93 +#define LM49453_P0_DAC_LS_LEVELL_REG 0x94 +#define LM49453_P0_DAC_LS_LEVELR_REG 0x95 +#define LM49453_P0_DAC_HA_LEVELL_REG 0x96 +#define LM49453_P0_DAC_HA_LEVELR_REG 0x97 +#define LM49453_P0_SOFT_MUTE_REG 0x98 +#define LM49453_P0_DMIC_MUTE_CFG_REG 0x99 +#define LM49453_P0_ADC_MUTE_CFG_REG 0x9A +#define LM49453_P0_DAC_MUTE_CFG_REG 0x9B + +/*DIGITAL MIC1 */ +#define LM49453_P0_DIGITAL_MIC1_CONFIG_REG 0xB0 +#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYL_REG 0xB1 +#define LM49453_P0_DIGITAL_MIC1_DATA_DELAYR_REG 0xB2 + +/*DIGITAL MIC2 */ +#define LM49453_P0_DIGITAL_MIC2_CONFIG_REG 0xB3 +#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYL_REG 0xB4 +#define LM49453_P0_DIGITAL_MIC2_DATA_DELAYR_REG 0xB5 + +/* ADC DECIMATOR */ +#define LM49453_P0_ADC_DECIMATOR_REG 0xB6 + +/* DAC CONFIGURE */ +#define LM49453_P0_DAC_CONFIG_REG 0xB7 + +/* SIDETONE */ +#define LM49453_P0_STN_VOL_ADCL_REG 0xB8 +#define LM49453_P0_STN_VOL_ADCR_REG 0xB9 +#define LM49453_P0_STN_VOL_DMIC1L_REG 0xBA +#define LM49453_P0_STN_VOL_DMIC1R_REG 0xBB +#define LM49453_P0_STN_VOL_DMIC2L_REG 0xBC +#define LM49453_P0_STN_VOL_DMIC2R_REG 0xBD + +/* ADC/DAC CLIPPING MONITORS (Read Only/Write to Clear) */ +#define LM49453_P0_ADC_DEC_CLIP_REG 0xC2 +#define LM49453_P0_ADC_HPF_CLIP_REG 0xC3 +#define LM49453_P0_ADC_LVL_CLIP_REG 0xC4 +#define LM49453_P0_DAC_LVL_CLIP_REG 0xC5 + +/* ADC ALC EFFECT MONITORS (Read Only) */ +#define LM49453_P0_ADC_LVLMONL_REG 0xC8 +#define LM49453_P0_ADC_LVLMONR_REG 0xC9 +#define LM49453_P0_ADC_ALCMONL_REG 0xCA +#define LM49453_P0_ADC_ALCMONR_REG 0xCB +#define LM49453_P0_ADC_MUTED_REG 0xCC +#define LM49453_P0_DAC_MUTED_REG 0xCD + +/* HEADSET DETECT */ +#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITL_REG 0xD0 +#define LM49453_P0_HSD_PPB_LONG_CNT_LIMITR_REG 0xD1 +#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITL_REG 0xD2 +#define LM49453_P0_HSD_PIN3_4_EX_LOOP_CNT_LIMITH_REG 0xD3 +#define LM49453_P0_HSD_TIMEOUT1_REG 0xD4 +#define LM49453_P0_HSD_TIMEOUT2_REG 0xD5 +#define LM49453_P0_HSD_TIMEOUT3_REG 0xD6 +#define LM49453_P0_HSD_PIN3_4_CFG_REG 0xD7 +#define LM49453_P0_HSD_IRQ1_REG 0xD8 +#define LM49453_P0_HSD_IRQ2_REG 0xD9 +#define LM49453_P0_HSD_IRQ3_REG 0xDA +#define LM49453_P0_HSD_IRQ4_REG 0xDB +#define LM49453_P0_HSD_IRQ_MASK1_REG 0xDC +#define LM49453_P0_HSD_IRQ_MASK2_REG 0xDD +#define LM49453_P0_HSD_IRQ_MASK3_REG 0xDE +#define LM49453_P0_HSD_R_HPLL_REG 0xE0 +#define LM49453_P0_HSD_R_HPLH_REG 0xE1 +#define LM49453_P0_HSD_R_HPLU_REG 0xE2 +#define LM49453_P0_HSD_R_HPRL_REG 0xE3 +#define LM49453_P0_HSD_R_HPRH_REG 0xE4 +#define LM49453_P0_HSD_R_HPRU_REG 0xE5 +#define LM49453_P0_HSD_VEL_L_FINALL_REG 0xE6 +#define LM49453_P0_HSD_VEL_L_FINALH_REG 0xE7 +#define LM49453_P0_HSD_VEL_L_FINALU_REG 0xE8 +#define LM49453_P0_HSD_RO_FINALL_REG 0xE9 +#define LM49453_P0_HSD_RO_FINALH_REG 0xEA +#define LM49453_P0_HSD_RO_FINALU_REG 0xEB +#define LM49453_P0_HSD_VMIC_BIAS_FINALL_REG 0xEC +#define LM49453_P0_HSD_VMIC_BIAS_FINALH_REG 0xED +#define LM49453_P0_HSD_VMIC_BIAS_FINALU_REG 0xEE +#define LM49453_P0_HSD_PIN_CONFIG_REG 0xEF +#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS1_REG 0xF1 +#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS2_REG 0xF2 +#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATUS3_REG 0xF3 +#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEL_REG 0xF4 +#define LM49453_P0_HSD_PLUG_DETECT_BB_IRQ_STATEH_REG 0xF5 + +/* I/O PULLDOWN CONFIG */ +#define LM49453_P0_PULL_CONFIG1_REG 0xF8 +#define LM49453_P0_PULL_CONFIG2_REG 0xF9 +#define LM49453_P0_PULL_CONFIG3_REG 0xFA + +/* RESET */ +#define LM49453_P0_RESET_REG 0xFE + +/* PAGE */ +#define LM49453_PAGE_REG 0xFF + +#define LM49453_MAX_REGISTER (0xFF+1) + +/* LM49453_P0_PMC_SETUP_REG (0x00h) */ +#define LM49453_PMC_SETUP_CHIP_EN (BIT(1)|BIT(0)) +#define LM49453_PMC_SETUP_PLL_EN BIT(2) +#define LM49453_PMC_SETUP_PLL_P2_EN BIT(3) +#define LM49453_PMC_SETUP_PLL_FLL BIT(4) +#define LM49453_PMC_SETUP_MCLK_OVER BIT(5) +#define LM49453_PMC_SETUP_RTC_CLK_OVER BIT(6) +#define LM49453_PMC_SETUP_CHIP_ACTIVE BIT(7) + +/* Chip Enable bits */ +#define LM49453_CHIP_EN_SHUTDOWN 0x00 +#define LM49453_CHIP_EN 0x01 +#define LM49453_CHIP_EN_HSD_DETECT 0x02 +#define LM49453_CHIP_EN_INVALID_HSD 0x03 + +/* LM49453_P0_PLL_CLK_SEL1_REG (0x01h) */ +#define LM49453_CLK_SEL1_MCLK_SEL 0x11 +#define LM49453_CLK_SEL1_RTC_SEL 0x11 +#define LM49453_CLK_SEL1_PORT1_SEL 0x10 +#define LM49453_CLK_SEL1_PORT2_SEL 0x11 + +/* LM49453_P0_PLL_CLK_SEL2_REG (0x02h) */ +#define LM49453_CLK_SEL2_ADC_CLK_SEL 0x38 + +/* LM49453_P0_FLL_REF_FREQL_REG (0x0F) */ +#define LM49453_FLL_REF_FREQ_VAL 0x8ca0001 + +/* LM49453_P0_VCO_TARGETLL_REG (0x11) */ +#define LM49453_VCO_TARGET_VAL 0x8ca0001 + +/* LM49453_P0_ADC_DSP_REG (0x30h) */ +#define LM49453_ADC_DSP_ADC_MUTEL BIT(0) +#define LM49453_ADC_DSP_ADC_MUTER BIT(1) +#define LM49453_ADC_DSP_DMIC1_MUTEL BIT(2) +#define LM49453_ADC_DSP_DMIC1_MUTER BIT(3) +#define LM49453_ADC_DSP_DMIC2_MUTEL BIT(4) +#define LM49453_ADC_DSP_DMIC2_MUTER BIT(5) +#define LM49453_ADC_DSP_MUTE_ALL 0x3F + +/* LM49453_P0_DAC_DSP_REG (0x31h) */ +#define LM49453_DAC_DSP_MUTE_ALL 0xFF + +/* LM49453_P0_AUDIO_PORT1_BASIC_REG (0x60h) */ +#define LM49453_AUDIO_PORT1_BASIC_FMT_MASK (BIT(4)|BIT(3)) +#define LM49453_AUDIO_PORT1_BASIC_CLK_MS BIT(3) +#define LM49453_AUDIO_PORT1_BASIC_SYNC_MS BIT(4) + +/* LM49453_P0_RESET_REG (0xFEh) */ +#define LM49453_RESET_REG_RST BIT(0) + +/* Page select register bits (0xFF) */ +#define LM49453_PAGE0_SELECT 0x0 +#define LM49453_PAGE1_SELECT 0x1 + +/* LM49453_P0_HSD_PIN3_4_CFG_REG (Jack Pin config - 0xD7) */ +#define LM49453_JACK_DISABLE 0x00 +#define LM49453_JACK_CONFIG1 0x01 +#define LM49453_JACK_CONFIG2 0x02 +#define LM49453_JACK_CONFIG3 0x03 +#define LM49453_JACK_CONFIG4 0x04 +#define LM49453_JACK_CONFIG5 0x05 + +/* Page 1 REGISTERS */ + +/* SIDETONE */ +#define LM49453_P1_SIDETONE_SA0L_REG 0x80 +#define LM49453_P1_SIDETONE_SA0H_REG 0x81 +#define LM49453_P1_SIDETONE_SAB0U_REG 0x82 +#define LM49453_P1_SIDETONE_SB0L_REG 0x83 +#define LM49453_P1_SIDETONE_SB0H_REG 0x84 +#define LM49453_P1_SIDETONE_SH0L_REG 0x85 +#define LM49453_P1_SIDETONE_SH0H_REG 0x86 +#define LM49453_P1_SIDETONE_SH0U_REG 0x87 +#define LM49453_P1_SIDETONE_SA1L_REG 0x88 +#define LM49453_P1_SIDETONE_SA1H_REG 0x89 +#define LM49453_P1_SIDETONE_SAB1U_REG 0x8A +#define LM49453_P1_SIDETONE_SB1L_REG 0x8B +#define LM49453_P1_SIDETONE_SB1H_REG 0x8C +#define LM49453_P1_SIDETONE_SH1L_REG 0x8D +#define LM49453_P1_SIDETONE_SH1H_REG 0x8E +#define LM49453_P1_SIDETONE_SH1U_REG 0x8F +#define LM49453_P1_SIDETONE_SA2L_REG 0x90 +#define LM49453_P1_SIDETONE_SA2H_REG 0x91 +#define LM49453_P1_SIDETONE_SAB2U_REG 0x92 +#define LM49453_P1_SIDETONE_SB2L_REG 0x93 +#define LM49453_P1_SIDETONE_SB2H_REG 0x94 +#define LM49453_P1_SIDETONE_SH2L_REG 0x95 +#define LM49453_P1_SIDETONE_SH2H_REG 0x96 +#define LM49453_P1_SIDETONE_SH2U_REG 0x97 +#define LM49453_P1_SIDETONE_SA3L_REG 0x98 +#define LM49453_P1_SIDETONE_SA3H_REG 0x99 +#define LM49453_P1_SIDETONE_SAB3U_REG 0x9A +#define LM49453_P1_SIDETONE_SB3L_REG 0x9B +#define LM49453_P1_SIDETONE_SB3H_REG 0x9C +#define LM49453_P1_SIDETONE_SH3L_REG 0x9D +#define LM49453_P1_SIDETONE_SH3H_REG 0x9E +#define LM49453_P1_SIDETONE_SH3U_REG 0x9F +#define LM49453_P1_SIDETONE_SA4L_REG 0xA0 +#define LM49453_P1_SIDETONE_SA4H_REG 0xA1 +#define LM49453_P1_SIDETONE_SAB4U_REG 0xA2 +#define LM49453_P1_SIDETONE_SB4L_REG 0xA3 +#define LM49453_P1_SIDETONE_SB4H_REG 0xA4 +#define LM49453_P1_SIDETONE_SH4L_REG 0xA5 +#define LM49453_P1_SIDETONE_SH4H_REG 0xA6 +#define LM49453_P1_SIDETONE_SH4U_REG 0xA7 +#define LM49453_P1_SIDETONE_SA5L_REG 0xA8 +#define LM49453_P1_SIDETONE_SA5H_REG 0xA9 +#define LM49453_P1_SIDETONE_SAB5U_REG 0xAA +#define LM49453_P1_SIDETONE_SB5L_REG 0xAB +#define LM49453_P1_SIDETONE_SB5H_REG 0xAC +#define LM49453_P1_SIDETONE_SH5L_REG 0xAD +#define LM49453_P1_SIDETONE_SH5H_REG 0xAE +#define LM49453_P1_SIDETONE_SH5U_REG 0xAF + +/* CHARGE PUMP CONFIG */ +#define LM49453_P1_CP_CONFIG1_REG 0xB0 +#define LM49453_P1_CP_CONFIG2_REG 0xB1 +#define LM49453_P1_CP_CONFIG3_REG 0xB2 +#define LM49453_P1_CP_CONFIG4_REG 0xB3 +#define LM49453_P1_CP_LA_VTH1L_REG 0xB4 +#define LM49453_P1_CP_LA_VTH1M_REG 0xB5 +#define LM49453_P1_CP_LA_VTH2L_REG 0xB6 +#define LM49453_P1_CP_LA_VTH2M_REG 0xB7 +#define LM49453_P1_CP_LA_VTH3L_REG 0xB8 +#define LM49453_P1_CP_LA_VTH3H_REG 0xB9 +#define LM49453_P1_CP_CLK_DIV_REG 0xBA + +/* DAC */ +#define LM49453_P1_DAC_CHOP_REG 0xC0 + +#define LM49453_CLK_SRC_MCLK 1 +#endif diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c new file mode 100644 index 0000000000..5e0bd0d24e --- /dev/null +++ b/sound/soc/codecs/lochnagar-sc.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Lochnagar sound card driver +// +// Copyright (c) 2017-2019 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// +// Author: Charles Keepax +// Piotr Stankiewicz + +#include +#include +#include + +#include +#include +#include + +struct lochnagar_sc_priv { + struct clk *mclk; +}; + +static const struct snd_soc_dapm_widget lochnagar_sc_widgets[] = { + SND_SOC_DAPM_LINE("Line Jack", NULL), + SND_SOC_DAPM_LINE("USB Audio", NULL), +}; + +static const struct snd_soc_dapm_route lochnagar_sc_routes[] = { + { "Line Jack", NULL, "AIF1 Playback" }, + { "AIF1 Capture", NULL, "Line Jack" }, + + { "USB Audio", NULL, "USB1 Playback" }, + { "USB Audio", NULL, "USB2 Playback" }, + { "USB1 Capture", NULL, "USB Audio" }, + { "USB2 Capture", NULL, "USB Audio" }, +}; + +static const unsigned int lochnagar_sc_chan_vals[] = { + 4, 8, +}; + +static const struct snd_pcm_hw_constraint_list lochnagar_sc_chan_constraint = { + .count = ARRAY_SIZE(lochnagar_sc_chan_vals), + .list = lochnagar_sc_chan_vals, +}; + +static const unsigned int lochnagar_sc_rate_vals[] = { + 8000, 16000, 24000, 32000, 48000, 96000, 192000, + 22050, 44100, 88200, 176400, +}; + +static const struct snd_pcm_hw_constraint_list lochnagar_sc_rate_constraint = { + .count = ARRAY_SIZE(lochnagar_sc_rate_vals), + .list = lochnagar_sc_rate_vals, +}; + +static int lochnagar_sc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval range = { + .min = 8000, + .max = 24576000 / hw_param_interval(params, rule->deps[0])->max, + }; + + return snd_interval_refine(hw_param_interval(params, rule->var), + &range); +} + +static int lochnagar_sc_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *comp = dai->component; + struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp); + int ret; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &lochnagar_sc_rate_constraint); + if (ret) + return ret; + + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + lochnagar_sc_hw_rule_rate, priv, + SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); +} + +static int lochnagar_sc_line_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *comp = dai->component; + struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp); + int ret; + + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(dai->dev, "Failed to enable MCLK: %d\n", ret); + return ret; + } + + ret = lochnagar_sc_startup(substream, dai); + if (ret) + return ret; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &lochnagar_sc_chan_constraint); +} + +static void lochnagar_sc_line_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *comp = dai->component; + struct lochnagar_sc_priv *priv = snd_soc_component_get_drvdata(comp); + + clk_disable_unprepare(priv->mclk); +} + +static int lochnagar_sc_check_fmt(struct snd_soc_dai *dai, unsigned int fmt, + unsigned int tar) +{ + tar |= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF; + + if ((fmt & ~SND_SOC_DAIFMT_CLOCK_MASK) != tar) + return -EINVAL; + + return 0; +} + +static int lochnagar_sc_set_line_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBS_CFS); +} + +static int lochnagar_sc_set_usb_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + return lochnagar_sc_check_fmt(dai, fmt, SND_SOC_DAIFMT_CBM_CFM); +} + +static const struct snd_soc_dai_ops lochnagar_sc_line_ops = { + .startup = lochnagar_sc_line_startup, + .shutdown = lochnagar_sc_line_shutdown, + .set_fmt = lochnagar_sc_set_line_fmt, +}; + +static const struct snd_soc_dai_ops lochnagar_sc_usb_ops = { + .startup = lochnagar_sc_startup, + .set_fmt = lochnagar_sc_set_usb_fmt, +}; + +static struct snd_soc_dai_driver lochnagar_sc_dai[] = { + { + .name = "lochnagar-line", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 4, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 4, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &lochnagar_sc_line_ops, + .symmetric_rate = true, + .symmetric_sample_bits = true, + }, + { + .name = "lochnagar-usb1", + .playback = { + .stream_name = "USB1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "USB1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &lochnagar_sc_usb_ops, + .symmetric_rate = true, + .symmetric_sample_bits = true, + }, + { + .name = "lochnagar-usb2", + .playback = { + .stream_name = "USB2 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "USB2 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &lochnagar_sc_usb_ops, + .symmetric_rate = true, + .symmetric_sample_bits = true, + }, +}; + +static const struct snd_soc_component_driver lochnagar_sc_driver = { + .dapm_widgets = lochnagar_sc_widgets, + .num_dapm_widgets = ARRAY_SIZE(lochnagar_sc_widgets), + .dapm_routes = lochnagar_sc_routes, + .num_dapm_routes = ARRAY_SIZE(lochnagar_sc_routes), + + .endianness = 1, +}; + +static int lochnagar_sc_probe(struct platform_device *pdev) +{ + struct lochnagar_sc_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(priv->mclk)) { + ret = PTR_ERR(priv->mclk); + dev_err(&pdev->dev, "Failed to get MCLK: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, priv); + + return devm_snd_soc_register_component(&pdev->dev, + &lochnagar_sc_driver, + lochnagar_sc_dai, + ARRAY_SIZE(lochnagar_sc_dai)); +} + +static const struct of_device_id lochnagar_of_match[] = { + { .compatible = "cirrus,lochnagar2-soundcard" }, + {} +}; +MODULE_DEVICE_TABLE(of, lochnagar_of_match); + +static struct platform_driver lochnagar_sc_codec_driver = { + .driver = { + .name = "lochnagar-soundcard", + .of_match_table = lochnagar_of_match, + }, + + .probe = lochnagar_sc_probe, +}; +module_platform_driver(lochnagar_sc_codec_driver); + +MODULE_DESCRIPTION("ASoC Lochnagar Sound Card Driver"); +MODULE_AUTHOR("Piotr Stankiewicz "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:lochnagar-soundcard"); diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c new file mode 100644 index 0000000000..f54baaad54 --- /dev/null +++ b/sound/soc/codecs/lpass-macro-common.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2022, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include + +#include "lpass-macro-common.h" + +struct lpass_macro *lpass_macro_pds_init(struct device *dev) +{ + struct lpass_macro *l_pds; + int ret; + + if (!of_property_present(dev->of_node, "power-domains")) + return NULL; + + l_pds = devm_kzalloc(dev, sizeof(*l_pds), GFP_KERNEL); + if (!l_pds) + return ERR_PTR(-ENOMEM); + + l_pds->macro_pd = dev_pm_domain_attach_by_name(dev, "macro"); + if (IS_ERR_OR_NULL(l_pds->macro_pd)) { + ret = l_pds->macro_pd ? PTR_ERR(l_pds->macro_pd) : -ENODATA; + goto macro_err; + } + + ret = pm_runtime_resume_and_get(l_pds->macro_pd); + if (ret < 0) + goto macro_sync_err; + + l_pds->dcodec_pd = dev_pm_domain_attach_by_name(dev, "dcodec"); + if (IS_ERR_OR_NULL(l_pds->dcodec_pd)) { + ret = l_pds->dcodec_pd ? PTR_ERR(l_pds->dcodec_pd) : -ENODATA; + goto dcodec_err; + } + + ret = pm_runtime_resume_and_get(l_pds->dcodec_pd); + if (ret < 0) + goto dcodec_sync_err; + return l_pds; + +dcodec_sync_err: + dev_pm_domain_detach(l_pds->dcodec_pd, false); +dcodec_err: + pm_runtime_put(l_pds->macro_pd); +macro_sync_err: + dev_pm_domain_detach(l_pds->macro_pd, false); +macro_err: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(lpass_macro_pds_init); + +void lpass_macro_pds_exit(struct lpass_macro *pds) +{ + if (pds) { + pm_runtime_put(pds->macro_pd); + dev_pm_domain_detach(pds->macro_pd, false); + pm_runtime_put(pds->dcodec_pd); + dev_pm_domain_detach(pds->dcodec_pd, false); + } +} +EXPORT_SYMBOL_GPL(lpass_macro_pds_exit); + +MODULE_DESCRIPTION("Common macro driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h new file mode 100644 index 0000000000..4eb886565e --- /dev/null +++ b/sound/soc/codecs/lpass-macro-common.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022, The Linux Foundation. All rights reserved. + */ + +#ifndef __LPASS_MACRO_COMMON_H__ +#define __LPASS_MACRO_COMMON_H__ + +/* NPL clock is expected */ +#define LPASS_MACRO_FLAG_HAS_NPL_CLOCK BIT(0) + +struct lpass_macro { + struct device *macro_pd; + struct device *dcodec_pd; +}; + +struct lpass_macro *lpass_macro_pds_init(struct device *dev); +void lpass_macro_pds_exit(struct lpass_macro *pds); + +#endif /* __LPASS_MACRO_COMMON_H__ */ diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c new file mode 100644 index 0000000000..29197d34ec --- /dev/null +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -0,0 +1,3750 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lpass-macro-common.h" + +#define CDC_RX_TOP_TOP_CFG0 (0x0000) +#define CDC_RX_TOP_SWR_CTRL (0x0008) +#define CDC_RX_TOP_DEBUG (0x000C) +#define CDC_RX_TOP_DEBUG_BUS (0x0010) +#define CDC_RX_TOP_DEBUG_EN0 (0x0014) +#define CDC_RX_TOP_DEBUG_EN1 (0x0018) +#define CDC_RX_TOP_DEBUG_EN2 (0x001C) +#define CDC_RX_TOP_HPHL_COMP_WR_LSB (0x0020) +#define CDC_RX_TOP_HPHL_COMP_WR_MSB (0x0024) +#define CDC_RX_TOP_HPHL_COMP_LUT (0x0028) +#define CDC_RX_TOP_HPH_LUT_BYPASS_MASK BIT(7) +#define CDC_RX_TOP_HPHL_COMP_RD_LSB (0x002C) +#define CDC_RX_TOP_HPHL_COMP_RD_MSB (0x0030) +#define CDC_RX_TOP_HPHR_COMP_WR_LSB (0x0034) +#define CDC_RX_TOP_HPHR_COMP_WR_MSB (0x0038) +#define CDC_RX_TOP_HPHR_COMP_LUT (0x003C) +#define CDC_RX_TOP_HPHR_COMP_RD_LSB (0x0040) +#define CDC_RX_TOP_HPHR_COMP_RD_MSB (0x0044) +#define CDC_RX_TOP_DSD0_DEBUG_CFG0 (0x0070) +#define CDC_RX_TOP_DSD0_DEBUG_CFG1 (0x0074) +#define CDC_RX_TOP_DSD0_DEBUG_CFG2 (0x0078) +#define CDC_RX_TOP_DSD0_DEBUG_CFG3 (0x007C) +#define CDC_RX_TOP_DSD1_DEBUG_CFG0 (0x0080) +#define CDC_RX_TOP_DSD1_DEBUG_CFG1 (0x0084) +#define CDC_RX_TOP_DSD1_DEBUG_CFG2 (0x0088) +#define CDC_RX_TOP_DSD1_DEBUG_CFG3 (0x008C) +#define CDC_RX_TOP_RX_I2S_CTL (0x0090) +#define CDC_RX_TOP_TX_I2S2_CTL (0x0094) +#define CDC_RX_TOP_I2S_CLK (0x0098) +#define CDC_RX_TOP_I2S_RESET (0x009C) +#define CDC_RX_TOP_I2S_MUX (0x00A0) +#define CDC_RX_CLK_RST_CTRL_MCLK_CONTROL (0x0100) +#define CDC_RX_CLK_MCLK_EN_MASK BIT(0) +#define CDC_RX_CLK_MCLK_ENABLE BIT(0) +#define CDC_RX_CLK_MCLK2_EN_MASK BIT(1) +#define CDC_RX_CLK_MCLK2_ENABLE BIT(1) +#define CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL (0x0104) +#define CDC_RX_FS_MCLK_CNT_EN_MASK BIT(0) +#define CDC_RX_FS_MCLK_CNT_ENABLE BIT(0) +#define CDC_RX_FS_MCLK_CNT_CLR_MASK BIT(1) +#define CDC_RX_FS_MCLK_CNT_CLR BIT(1) +#define CDC_RX_CLK_RST_CTRL_SWR_CONTROL (0x0108) +#define CDC_RX_SWR_CLK_EN_MASK BIT(0) +#define CDC_RX_SWR_RESET_MASK BIT(1) +#define CDC_RX_SWR_RESET BIT(1) +#define CDC_RX_CLK_RST_CTRL_DSD_CONTROL (0x010C) +#define CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL (0x0110) +#define CDC_RX_SOFTCLIP_CRC (0x0140) +#define CDC_RX_SOFTCLIP_CLK_EN_MASK BIT(0) +#define CDC_RX_SOFTCLIP_SOFTCLIP_CTRL (0x0144) +#define CDC_RX_SOFTCLIP_EN_MASK BIT(0) +#define CDC_RX_INP_MUX_RX_INT0_CFG0 (0x0180) +#define CDC_RX_INTX_1_MIX_INP0_SEL_MASK GENMASK(3, 0) +#define CDC_RX_INTX_1_MIX_INP1_SEL_MASK GENMASK(7, 4) +#define CDC_RX_INP_MUX_RX_INT0_CFG1 (0x0184) +#define CDC_RX_INTX_2_SEL_MASK GENMASK(3, 0) +#define CDC_RX_INTX_1_MIX_INP2_SEL_MASK GENMASK(7, 4) +#define CDC_RX_INP_MUX_RX_INT1_CFG0 (0x0188) +#define CDC_RX_INP_MUX_RX_INT1_CFG1 (0x018C) +#define CDC_RX_INP_MUX_RX_INT2_CFG0 (0x0190) +#define CDC_RX_INP_MUX_RX_INT2_CFG1 (0x0194) +#define CDC_RX_INP_MUX_RX_MIX_CFG4 (0x0198) +#define CDC_RX_INP_MUX_RX_MIX_CFG5 (0x019C) +#define CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 (0x01A0) +#define CDC_RX_CLSH_CRC (0x0200) +#define CDC_RX_CLSH_CLK_EN_MASK BIT(0) +#define CDC_RX_CLSH_DLY_CTRL (0x0204) +#define CDC_RX_CLSH_DECAY_CTRL (0x0208) +#define CDC_RX_CLSH_DECAY_RATE_MASK GENMASK(2, 0) +#define CDC_RX_CLSH_HPH_V_PA (0x020C) +#define CDC_RX_CLSH_HPH_V_PA_MIN_MASK GENMASK(5, 0) +#define CDC_RX_CLSH_EAR_V_PA (0x0210) +#define CDC_RX_CLSH_HPH_V_HD (0x0214) +#define CDC_RX_CLSH_EAR_V_HD (0x0218) +#define CDC_RX_CLSH_K1_MSB (0x021C) +#define CDC_RX_CLSH_K1_MSB_COEFF_MASK GENMASK(3, 0) +#define CDC_RX_CLSH_K1_LSB (0x0220) +#define CDC_RX_CLSH_K2_MSB (0x0224) +#define CDC_RX_CLSH_K2_LSB (0x0228) +#define CDC_RX_CLSH_IDLE_CTRL (0x022C) +#define CDC_RX_CLSH_IDLE_HPH (0x0230) +#define CDC_RX_CLSH_IDLE_EAR (0x0234) +#define CDC_RX_CLSH_TEST0 (0x0238) +#define CDC_RX_CLSH_TEST1 (0x023C) +#define CDC_RX_CLSH_OVR_VREF (0x0240) +#define CDC_RX_CLSH_CLSG_CTL (0x0244) +#define CDC_RX_CLSH_CLSG_CFG1 (0x0248) +#define CDC_RX_CLSH_CLSG_CFG2 (0x024C) +#define CDC_RX_BCL_VBAT_PATH_CTL (0x0280) +#define CDC_RX_BCL_VBAT_CFG (0x0284) +#define CDC_RX_BCL_VBAT_ADC_CAL1 (0x0288) +#define CDC_RX_BCL_VBAT_ADC_CAL2 (0x028C) +#define CDC_RX_BCL_VBAT_ADC_CAL3 (0x0290) +#define CDC_RX_BCL_VBAT_PK_EST1 (0x0294) +#define CDC_RX_BCL_VBAT_PK_EST2 (0x0298) +#define CDC_RX_BCL_VBAT_PK_EST3 (0x029C) +#define CDC_RX_BCL_VBAT_RF_PROC1 (0x02A0) +#define CDC_RX_BCL_VBAT_RF_PROC2 (0x02A4) +#define CDC_RX_BCL_VBAT_TAC1 (0x02A8) +#define CDC_RX_BCL_VBAT_TAC2 (0x02AC) +#define CDC_RX_BCL_VBAT_TAC3 (0x02B0) +#define CDC_RX_BCL_VBAT_TAC4 (0x02B4) +#define CDC_RX_BCL_VBAT_GAIN_UPD1 (0x02B8) +#define CDC_RX_BCL_VBAT_GAIN_UPD2 (0x02BC) +#define CDC_RX_BCL_VBAT_GAIN_UPD3 (0x02C0) +#define CDC_RX_BCL_VBAT_GAIN_UPD4 (0x02C4) +#define CDC_RX_BCL_VBAT_GAIN_UPD5 (0x02C8) +#define CDC_RX_BCL_VBAT_DEBUG1 (0x02CC) +#define CDC_RX_BCL_VBAT_GAIN_UPD_MON (0x02D0) +#define CDC_RX_BCL_VBAT_GAIN_MON_VAL (0x02D4) +#define CDC_RX_BCL_VBAT_BAN (0x02D8) +#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD1 (0x02DC) +#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD2 (0x02E0) +#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD3 (0x02E4) +#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD4 (0x02E8) +#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD5 (0x02EC) +#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD6 (0x02F0) +#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD7 (0x02F4) +#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD8 (0x02F8) +#define CDC_RX_BCL_VBAT_BCL_GAIN_UPD9 (0x02FC) +#define CDC_RX_BCL_VBAT_ATTN1 (0x0300) +#define CDC_RX_BCL_VBAT_ATTN2 (0x0304) +#define CDC_RX_BCL_VBAT_ATTN3 (0x0308) +#define CDC_RX_BCL_VBAT_DECODE_CTL1 (0x030C) +#define CDC_RX_BCL_VBAT_DECODE_CTL2 (0x0310) +#define CDC_RX_BCL_VBAT_DECODE_CFG1 (0x0314) +#define CDC_RX_BCL_VBAT_DECODE_CFG2 (0x0318) +#define CDC_RX_BCL_VBAT_DECODE_CFG3 (0x031C) +#define CDC_RX_BCL_VBAT_DECODE_CFG4 (0x0320) +#define CDC_RX_BCL_VBAT_DECODE_ST (0x0324) +#define CDC_RX_INTR_CTRL_CFG (0x0340) +#define CDC_RX_INTR_CTRL_CLR_COMMIT (0x0344) +#define CDC_RX_INTR_CTRL_PIN1_MASK0 (0x0360) +#define CDC_RX_INTR_CTRL_PIN1_STATUS0 (0x0368) +#define CDC_RX_INTR_CTRL_PIN1_CLEAR0 (0x0370) +#define CDC_RX_INTR_CTRL_PIN2_MASK0 (0x0380) +#define CDC_RX_INTR_CTRL_PIN2_STATUS0 (0x0388) +#define CDC_RX_INTR_CTRL_PIN2_CLEAR0 (0x0390) +#define CDC_RX_INTR_CTRL_LEVEL0 (0x03C0) +#define CDC_RX_INTR_CTRL_BYPASS0 (0x03C8) +#define CDC_RX_INTR_CTRL_SET0 (0x03D0) +#define CDC_RX_RXn_RX_PATH_CTL(n) (0x0400 + 0x80 * n) +#define CDC_RX_RX0_RX_PATH_CTL (0x0400) +#define CDC_RX_PATH_RESET_EN_MASK BIT(6) +#define CDC_RX_PATH_CLK_EN_MASK BIT(5) +#define CDC_RX_PATH_CLK_ENABLE BIT(5) +#define CDC_RX_PATH_PGA_MUTE_MASK BIT(4) +#define CDC_RX_PATH_PGA_MUTE_ENABLE BIT(4) +#define CDC_RX_PATH_PCM_RATE_MASK GENMASK(3, 0) +#define CDC_RX_RXn_RX_PATH_CFG0(n) (0x0404 + 0x80 * n) +#define CDC_RX_RXn_COMP_EN_MASK BIT(1) +#define CDC_RX_RX0_RX_PATH_CFG0 (0x0404) +#define CDC_RX_RXn_CLSH_EN_MASK BIT(6) +#define CDC_RX_DLY_ZN_EN_MASK BIT(3) +#define CDC_RX_DLY_ZN_ENABLE BIT(3) +#define CDC_RX_RXn_HD2_EN_MASK BIT(2) +#define CDC_RX_RXn_RX_PATH_CFG1(n) (0x0408 + 0x80 * n) +#define CDC_RX_RXn_SIDETONE_EN_MASK BIT(4) +#define CDC_RX_RX0_RX_PATH_CFG1 (0x0408) +#define CDC_RX_RX0_HPH_L_EAR_SEL_MASK BIT(1) +#define CDC_RX_RXn_RX_PATH_CFG2(n) (0x040C + 0x80 * n) +#define CDC_RX_RXn_HPF_CUT_FREQ_MASK GENMASK(1, 0) +#define CDC_RX_RX0_RX_PATH_CFG2 (0x040C) +#define CDC_RX_RXn_RX_PATH_CFG3(n) (0x0410 + 0x80 * n) +#define CDC_RX_RX0_RX_PATH_CFG3 (0x0410) +#define CDC_RX_DC_COEFF_SEL_MASK GENMASK(1, 0) +#define CDC_RX_DC_COEFF_SEL_TWO 0x2 +#define CDC_RX_RXn_RX_VOL_CTL(n) (0x0414 + 0x80 * n) +#define CDC_RX_RX0_RX_VOL_CTL (0x0414) +#define CDC_RX_RXn_RX_PATH_MIX_CTL(n) (0x0418 + 0x80 * n) +#define CDC_RX_RXn_MIX_PCM_RATE_MASK GENMASK(3, 0) +#define CDC_RX_RXn_MIX_RESET_MASK BIT(6) +#define CDC_RX_RXn_MIX_RESET BIT(6) +#define CDC_RX_RXn_MIX_CLK_EN_MASK BIT(5) +#define CDC_RX_RX0_RX_PATH_MIX_CTL (0x0418) +#define CDC_RX_RX0_RX_PATH_MIX_CFG (0x041C) +#define CDC_RX_RXn_RX_VOL_MIX_CTL(n) (0x0420 + 0x80 * n) +#define CDC_RX_RX0_RX_VOL_MIX_CTL (0x0420) +#define CDC_RX_RX0_RX_PATH_SEC1 (0x0424) +#define CDC_RX_RX0_RX_PATH_SEC2 (0x0428) +#define CDC_RX_RX0_RX_PATH_SEC3 (0x042C) +#define CDC_RX_RX0_RX_PATH_SEC4 (0x0430) +#define CDC_RX_RX0_RX_PATH_SEC7 (0x0434) +#define CDC_RX_DSM_OUT_DELAY_SEL_MASK GENMASK(2, 0) +#define CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE 0x2 +#define CDC_RX_RX0_RX_PATH_MIX_SEC0 (0x0438) +#define CDC_RX_RX0_RX_PATH_MIX_SEC1 (0x043C) +#define CDC_RX_RXn_RX_PATH_DSM_CTL(n) (0x0440 + 0x80 * n) +#define CDC_RX_RXn_DSM_CLK_EN_MASK BIT(0) +#define CDC_RX_RX0_RX_PATH_DSM_CTL (0x0440) +#define CDC_RX_RX0_RX_PATH_DSM_DATA1 (0x0444) +#define CDC_RX_RX0_RX_PATH_DSM_DATA2 (0x0448) +#define CDC_RX_RX0_RX_PATH_DSM_DATA3 (0x044C) +#define CDC_RX_RX0_RX_PATH_DSM_DATA4 (0x0450) +#define CDC_RX_RX0_RX_PATH_DSM_DATA5 (0x0454) +#define CDC_RX_RX0_RX_PATH_DSM_DATA6 (0x0458) +#define CDC_RX_RX1_RX_PATH_CTL (0x0480) +#define CDC_RX_RX1_RX_PATH_CFG0 (0x0484) +#define CDC_RX_RX1_RX_PATH_CFG1 (0x0488) +#define CDC_RX_RX1_RX_PATH_CFG2 (0x048C) +#define CDC_RX_RX1_RX_PATH_CFG3 (0x0490) +#define CDC_RX_RX1_RX_VOL_CTL (0x0494) +#define CDC_RX_RX1_RX_PATH_MIX_CTL (0x0498) +#define CDC_RX_RX1_RX_PATH_MIX_CFG (0x049C) +#define CDC_RX_RX1_RX_VOL_MIX_CTL (0x04A0) +#define CDC_RX_RX1_RX_PATH_SEC1 (0x04A4) +#define CDC_RX_RX1_RX_PATH_SEC2 (0x04A8) +#define CDC_RX_RX1_RX_PATH_SEC3 (0x04AC) +#define CDC_RX_RXn_HD2_ALPHA_MASK GENMASK(5, 2) +#define CDC_RX_RX1_RX_PATH_SEC4 (0x04B0) +#define CDC_RX_RX1_RX_PATH_SEC7 (0x04B4) +#define CDC_RX_RX1_RX_PATH_MIX_SEC0 (0x04B8) +#define CDC_RX_RX1_RX_PATH_MIX_SEC1 (0x04BC) +#define CDC_RX_RX1_RX_PATH_DSM_CTL (0x04C0) +#define CDC_RX_RX1_RX_PATH_DSM_DATA1 (0x04C4) +#define CDC_RX_RX1_RX_PATH_DSM_DATA2 (0x04C8) +#define CDC_RX_RX1_RX_PATH_DSM_DATA3 (0x04CC) +#define CDC_RX_RX1_RX_PATH_DSM_DATA4 (0x04D0) +#define CDC_RX_RX1_RX_PATH_DSM_DATA5 (0x04D4) +#define CDC_RX_RX1_RX_PATH_DSM_DATA6 (0x04D8) +#define CDC_RX_RX2_RX_PATH_CTL (0x0500) +#define CDC_RX_RX2_RX_PATH_CFG0 (0x0504) +#define CDC_RX_RX2_CLSH_EN_MASK BIT(4) +#define CDC_RX_RX2_DLY_Z_EN_MASK BIT(3) +#define CDC_RX_RX2_RX_PATH_CFG1 (0x0508) +#define CDC_RX_RX2_RX_PATH_CFG2 (0x050C) +#define CDC_RX_RX2_RX_PATH_CFG3 (0x0510) +#define CDC_RX_RX2_RX_VOL_CTL (0x0514) +#define CDC_RX_RX2_RX_PATH_MIX_CTL (0x0518) +#define CDC_RX_RX2_RX_PATH_MIX_CFG (0x051C) +#define CDC_RX_RX2_RX_VOL_MIX_CTL (0x0520) +#define CDC_RX_RX2_RX_PATH_SEC0 (0x0524) +#define CDC_RX_RX2_RX_PATH_SEC1 (0x0528) +#define CDC_RX_RX2_RX_PATH_SEC2 (0x052C) +#define CDC_RX_RX2_RX_PATH_SEC3 (0x0530) +#define CDC_RX_RX2_RX_PATH_SEC4 (0x0534) +#define CDC_RX_RX2_RX_PATH_SEC5 (0x0538) +#define CDC_RX_RX2_RX_PATH_SEC6 (0x053C) +#define CDC_RX_RX2_RX_PATH_SEC7 (0x0540) +#define CDC_RX_RX2_RX_PATH_MIX_SEC0 (0x0544) +#define CDC_RX_RX2_RX_PATH_MIX_SEC1 (0x0548) +#define CDC_RX_RX2_RX_PATH_DSM_CTL (0x054C) +#define CDC_RX_IDLE_DETECT_PATH_CTL (0x0780) +#define CDC_RX_IDLE_DETECT_CFG0 (0x0784) +#define CDC_RX_IDLE_DETECT_CFG1 (0x0788) +#define CDC_RX_IDLE_DETECT_CFG2 (0x078C) +#define CDC_RX_IDLE_DETECT_CFG3 (0x0790) +#define CDC_RX_COMPANDERn_CTL0(n) (0x0800 + 0x40 * n) +#define CDC_RX_COMPANDERn_CLK_EN_MASK BIT(0) +#define CDC_RX_COMPANDERn_SOFT_RST_MASK BIT(1) +#define CDC_RX_COMPANDERn_HALT_MASK BIT(2) +#define CDC_RX_COMPANDER0_CTL0 (0x0800) +#define CDC_RX_COMPANDER0_CTL1 (0x0804) +#define CDC_RX_COMPANDER0_CTL2 (0x0808) +#define CDC_RX_COMPANDER0_CTL3 (0x080C) +#define CDC_RX_COMPANDER0_CTL4 (0x0810) +#define CDC_RX_COMPANDER0_CTL5 (0x0814) +#define CDC_RX_COMPANDER0_CTL6 (0x0818) +#define CDC_RX_COMPANDER0_CTL7 (0x081C) +#define CDC_RX_COMPANDER1_CTL0 (0x0840) +#define CDC_RX_COMPANDER1_CTL1 (0x0844) +#define CDC_RX_COMPANDER1_CTL2 (0x0848) +#define CDC_RX_COMPANDER1_CTL3 (0x084C) +#define CDC_RX_COMPANDER1_CTL4 (0x0850) +#define CDC_RX_COMPANDER1_CTL5 (0x0854) +#define CDC_RX_COMPANDER1_CTL6 (0x0858) +#define CDC_RX_COMPANDER1_CTL7 (0x085C) +#define CDC_RX_COMPANDER1_HPH_LOW_PWR_MODE_MASK BIT(5) +#define CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL (0x0A00) +#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL (0x0A04) +#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL (0x0A08) +#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL (0x0A0C) +#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL (0x0A10) +#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL (0x0A14) +#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL (0x0A18) +#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL (0x0A1C) +#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL (0x0A20) +#define CDC_RX_SIDETONE_IIR0_IIR_CTL (0x0A24) +#define CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL (0x0A28) +#define CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL (0x0A2C) +#define CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL (0x0A30) +#define CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL (0x0A80) +#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL (0x0A84) +#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL (0x0A88) +#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL (0x0A8C) +#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL (0x0A90) +#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL (0x0A94) +#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL (0x0A98) +#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL (0x0A9C) +#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL (0x0AA0) +#define CDC_RX_SIDETONE_IIR1_IIR_CTL (0x0AA4) +#define CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL (0x0AA8) +#define CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL (0x0AAC) +#define CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL (0x0AB0) +#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0 (0x0B00) +#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1 (0x0B04) +#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2 (0x0B08) +#define CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3 (0x0B0C) +#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0 (0x0B10) +#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1 (0x0B14) +#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2 (0x0B18) +#define CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3 (0x0B1C) +#define CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL (0x0B40) +#define CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1 (0x0B44) +#define CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL (0x0B50) +#define CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1 (0x0B54) +#define CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL (0x0C00) +#define CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0 (0x0C04) +#define CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL (0x0C40) +#define CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0 (0x0C44) +#define CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL (0x0C80) +#define CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0 (0x0C84) +#define CDC_RX_EC_ASRC0_CLK_RST_CTL (0x0D00) +#define CDC_RX_EC_ASRC0_CTL0 (0x0D04) +#define CDC_RX_EC_ASRC0_CTL1 (0x0D08) +#define CDC_RX_EC_ASRC0_FIFO_CTL (0x0D0C) +#define CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB (0x0D10) +#define CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB (0x0D14) +#define CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB (0x0D18) +#define CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB (0x0D1C) +#define CDC_RX_EC_ASRC0_STATUS_FIFO (0x0D20) +#define CDC_RX_EC_ASRC1_CLK_RST_CTL (0x0D40) +#define CDC_RX_EC_ASRC1_CTL0 (0x0D44) +#define CDC_RX_EC_ASRC1_CTL1 (0x0D48) +#define CDC_RX_EC_ASRC1_FIFO_CTL (0x0D4C) +#define CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB (0x0D50) +#define CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB (0x0D54) +#define CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB (0x0D58) +#define CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB (0x0D5C) +#define CDC_RX_EC_ASRC1_STATUS_FIFO (0x0D60) +#define CDC_RX_EC_ASRC2_CLK_RST_CTL (0x0D80) +#define CDC_RX_EC_ASRC2_CTL0 (0x0D84) +#define CDC_RX_EC_ASRC2_CTL1 (0x0D88) +#define CDC_RX_EC_ASRC2_FIFO_CTL (0x0D8C) +#define CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB (0x0D90) +#define CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB (0x0D94) +#define CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB (0x0D98) +#define CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB (0x0D9C) +#define CDC_RX_EC_ASRC2_STATUS_FIFO (0x0DA0) +#define CDC_RX_DSD0_PATH_CTL (0x0F00) +#define CDC_RX_DSD0_CFG0 (0x0F04) +#define CDC_RX_DSD0_CFG1 (0x0F08) +#define CDC_RX_DSD0_CFG2 (0x0F0C) +#define CDC_RX_DSD1_PATH_CTL (0x0F80) +#define CDC_RX_DSD1_CFG0 (0x0F84) +#define CDC_RX_DSD1_CFG1 (0x0F88) +#define CDC_RX_DSD1_CFG2 (0x0F8C) +#define RX_MAX_OFFSET (0x0F8C) + +#define MCLK_FREQ 19200000 + +#define RX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define RX_MACRO_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) + +#define RX_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define RX_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_48000) +#define RX_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define RX_MACRO_MAX_DMA_CH_PER_PORT 2 + +#define RX_MACRO_EC_MIX_TX0_MASK 0xf0 +#define RX_MACRO_EC_MIX_TX1_MASK 0x0f +#define RX_MACRO_EC_MIX_TX2_MASK 0x0f + +#define COMP_MAX_COEFF 25 +#define RX_NUM_CLKS_MAX 5 + +struct comp_coeff_val { + u8 lsb; + u8 msb; +}; + +enum { + HPH_ULP, + HPH_LOHIFI, + HPH_MODE_MAX, +}; + +static const struct comp_coeff_val comp_coeff_table[HPH_MODE_MAX][COMP_MAX_COEFF] = { + { + {0x40, 0x00}, + {0x4C, 0x00}, + {0x5A, 0x00}, + {0x6B, 0x00}, + {0x7F, 0x00}, + {0x97, 0x00}, + {0xB3, 0x00}, + {0xD5, 0x00}, + {0xFD, 0x00}, + {0x2D, 0x01}, + {0x66, 0x01}, + {0xA7, 0x01}, + {0xF8, 0x01}, + {0x57, 0x02}, + {0xC7, 0x02}, + {0x4B, 0x03}, + {0xE9, 0x03}, + {0xA3, 0x04}, + {0x7D, 0x05}, + {0x90, 0x06}, + {0xD1, 0x07}, + {0x49, 0x09}, + {0x00, 0x0B}, + {0x01, 0x0D}, + {0x59, 0x0F}, + }, + { + {0x40, 0x00}, + {0x4C, 0x00}, + {0x5A, 0x00}, + {0x6B, 0x00}, + {0x80, 0x00}, + {0x98, 0x00}, + {0xB4, 0x00}, + {0xD5, 0x00}, + {0xFE, 0x00}, + {0x2E, 0x01}, + {0x66, 0x01}, + {0xA9, 0x01}, + {0xF8, 0x01}, + {0x56, 0x02}, + {0xC4, 0x02}, + {0x4F, 0x03}, + {0xF0, 0x03}, + {0xAE, 0x04}, + {0x8B, 0x05}, + {0x8E, 0x06}, + {0xBC, 0x07}, + {0x56, 0x09}, + {0x0F, 0x0B}, + {0x13, 0x0D}, + {0x6F, 0x0F}, + }, +}; + +struct rx_macro_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +enum { + INTERP_HPHL, + INTERP_HPHR, + INTERP_AUX, + INTERP_MAX +}; + +enum { + RX_MACRO_RX0, + RX_MACRO_RX1, + RX_MACRO_RX2, + RX_MACRO_RX3, + RX_MACRO_RX4, + RX_MACRO_RX5, + RX_MACRO_PORTS_MAX +}; + +enum { + RX_MACRO_COMP1, /* HPH_L */ + RX_MACRO_COMP2, /* HPH_R */ + RX_MACRO_COMP_MAX +}; + +enum { + RX_MACRO_EC0_MUX = 0, + RX_MACRO_EC1_MUX, + RX_MACRO_EC2_MUX, + RX_MACRO_EC_MUX_MAX, +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, + INTn_1_INP_SEL_IIR0, + INTn_1_INP_SEL_IIR1, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_RX4, + INTn_1_INP_SEL_RX5, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, +}; + +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +#define RX_MACRO_IIR_FILTER_SIZE (sizeof(u32) * BAND_MAX) + +#define RX_MACRO_IIR_FILTER_CTL(xname, iidx, bidx) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = rx_macro_iir_filter_info, \ + .get = rx_macro_get_iir_band_audio_mixer, \ + .put = rx_macro_put_iir_band_audio_mixer, \ + .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) { \ + .iir_idx = iidx, \ + .band_idx = bidx, \ + .bytes_ext = {.max = RX_MACRO_IIR_FILTER_SIZE, }, \ + } \ +} + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate sr_val_tbl[] = { + {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5}, + {192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA}, + {176400, 0xB}, {352800, 0xC}, +}; + +enum { + RX_MACRO_AIF_INVALID = 0, + RX_MACRO_AIF1_PB, + RX_MACRO_AIF2_PB, + RX_MACRO_AIF3_PB, + RX_MACRO_AIF4_PB, + RX_MACRO_AIF_ECHO, + RX_MACRO_MAX_DAIS, +}; + +enum { + RX_MACRO_AIF1_CAP = 0, + RX_MACRO_AIF2_CAP, + RX_MACRO_AIF3_CAP, + RX_MACRO_MAX_AIF_CAP_DAIS +}; + +struct rx_macro { + struct device *dev; + int comp_enabled[RX_MACRO_COMP_MAX]; + /* Main path clock users count */ + int main_clk_users[INTERP_MAX]; + int rx_port_value[RX_MACRO_PORTS_MAX]; + u16 prim_int_users[INTERP_MAX]; + int rx_mclk_users; + int clsh_users; + int rx_mclk_cnt; + bool is_ear_mode_on; + bool hph_pwr_mode; + bool hph_hd2_mode; + struct snd_soc_component *component; + unsigned long active_ch_mask[RX_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[RX_MACRO_MAX_DAIS]; + u16 bit_width[RX_MACRO_MAX_DAIS]; + int is_softclip_on; + int is_aux_hpf_on; + int softclip_clk_users; + struct lpass_macro *pds; + struct regmap *regmap; + struct clk *mclk; + struct clk *npl; + struct clk *macro; + struct clk *dcodec; + struct clk *fsgen; + struct clk_hw hw; +}; +#define to_rx_macro(_hw) container_of(_hw, struct rx_macro, hw) + +struct wcd_iir_filter_ctl { + unsigned int iir_idx; + unsigned int band_idx; + struct soc_bytes_ext bytes_ext; +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0", "SRC1", "SRC_SUM" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", + "RX0", "RX1", "RX2", "RX3", "RX4", "RX5" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_1_interp_mux_text[] = { + "ZERO", "RX INT0_1 MIX1", +}; + +static const char * const rx_int1_1_interp_mux_text[] = { + "ZERO", "RX INT1_1 MIX1", +}; + +static const char * const rx_int2_1_interp_mux_text[] = { + "ZERO", "RX INT2_1 MIX1", +}; + +static const char * const rx_int0_2_interp_mux_text[] = { + "ZERO", "RX INT0_2 MUX", +}; + +static const char * const rx_int1_2_interp_mux_text[] = { + "ZERO", "RX INT1_2 MUX", +}; + +static const char * const rx_int2_2_interp_mux_text[] = { + "ZERO", "RX INT2_2 MUX", +}; + +static const char *const rx_macro_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB" +}; + +static const char *const rx_macro_hph_pwr_mode_text[] = { + "ULP", "LOHIFI" +}; + +static const char * const rx_echo_mux_text[] = { + "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2" +}; + +static const struct soc_enum rx_macro_hph_pwr_mode_enum = + SOC_ENUM_SINGLE_EXT(2, rx_macro_hph_pwr_mode_text); +static const struct soc_enum rx_mix_tx2_mux_enum = + SOC_ENUM_SINGLE(CDC_RX_INP_MUX_RX_MIX_CFG5, 0, 4, rx_echo_mux_text); +static const struct soc_enum rx_mix_tx1_mux_enum = + SOC_ENUM_SINGLE(CDC_RX_INP_MUX_RX_MIX_CFG4, 0, 4, rx_echo_mux_text); +static const struct soc_enum rx_mix_tx0_mux_enum = + SOC_ENUM_SINGLE(CDC_RX_INP_MUX_RX_MIX_CFG4, 4, 4, rx_echo_mux_text); + +static SOC_ENUM_SINGLE_DECL(rx_int0_2_enum, CDC_RX_INP_MUX_RX_INT0_CFG1, 0, + rx_int_mix_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_int1_2_enum, CDC_RX_INP_MUX_RX_INT1_CFG1, 0, + rx_int_mix_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_int2_2_enum, CDC_RX_INP_MUX_RX_INT2_CFG1, 0, + rx_int_mix_mux_text); + +static SOC_ENUM_SINGLE_DECL(rx_int0_1_mix_inp0_enum, CDC_RX_INP_MUX_RX_INT0_CFG0, 0, + rx_prim_mix_text); +static SOC_ENUM_SINGLE_DECL(rx_int0_1_mix_inp1_enum, CDC_RX_INP_MUX_RX_INT0_CFG0, 4, + rx_prim_mix_text); +static SOC_ENUM_SINGLE_DECL(rx_int0_1_mix_inp2_enum, CDC_RX_INP_MUX_RX_INT0_CFG1, 4, + rx_prim_mix_text); +static SOC_ENUM_SINGLE_DECL(rx_int1_1_mix_inp0_enum, CDC_RX_INP_MUX_RX_INT1_CFG0, 0, + rx_prim_mix_text); +static SOC_ENUM_SINGLE_DECL(rx_int1_1_mix_inp1_enum, CDC_RX_INP_MUX_RX_INT1_CFG0, 4, + rx_prim_mix_text); +static SOC_ENUM_SINGLE_DECL(rx_int1_1_mix_inp2_enum, CDC_RX_INP_MUX_RX_INT1_CFG1, 4, + rx_prim_mix_text); +static SOC_ENUM_SINGLE_DECL(rx_int2_1_mix_inp0_enum, CDC_RX_INP_MUX_RX_INT2_CFG0, 0, + rx_prim_mix_text); +static SOC_ENUM_SINGLE_DECL(rx_int2_1_mix_inp1_enum, CDC_RX_INP_MUX_RX_INT2_CFG0, 4, + rx_prim_mix_text); +static SOC_ENUM_SINGLE_DECL(rx_int2_1_mix_inp2_enum, CDC_RX_INP_MUX_RX_INT2_CFG1, 4, + rx_prim_mix_text); + +static SOC_ENUM_SINGLE_DECL(rx_int0_mix2_inp_enum, CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, + rx_sidetone_mix_text); +static SOC_ENUM_SINGLE_DECL(rx_int1_mix2_inp_enum, CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, + rx_sidetone_mix_text); +static SOC_ENUM_SINGLE_DECL(rx_int2_mix2_inp_enum, CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, + rx_sidetone_mix_text); +static SOC_ENUM_SINGLE_DECL(iir0_inp0_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0, 0, + iir_inp_mux_text); +static SOC_ENUM_SINGLE_DECL(iir0_inp1_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1, 0, + iir_inp_mux_text); +static SOC_ENUM_SINGLE_DECL(iir0_inp2_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2, 0, + iir_inp_mux_text); +static SOC_ENUM_SINGLE_DECL(iir0_inp3_enum, CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3, 0, + iir_inp_mux_text); +static SOC_ENUM_SINGLE_DECL(iir1_inp0_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0, 0, + iir_inp_mux_text); +static SOC_ENUM_SINGLE_DECL(iir1_inp1_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1, 0, + iir_inp_mux_text); +static SOC_ENUM_SINGLE_DECL(iir1_inp2_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2, 0, + iir_inp_mux_text); +static SOC_ENUM_SINGLE_DECL(iir1_inp3_enum, CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3, 0, + iir_inp_mux_text); + +static SOC_ENUM_SINGLE_DECL(rx_int0_1_interp_enum, SND_SOC_NOPM, 0, + rx_int0_1_interp_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_int1_1_interp_enum, SND_SOC_NOPM, 0, + rx_int1_1_interp_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_int2_1_interp_enum, SND_SOC_NOPM, 0, + rx_int2_1_interp_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_int0_2_interp_enum, SND_SOC_NOPM, 0, + rx_int0_2_interp_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_int1_2_interp_enum, SND_SOC_NOPM, 0, + rx_int1_2_interp_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_int2_2_interp_enum, SND_SOC_NOPM, 0, + rx_int2_2_interp_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_int0_dem_inp_enum, CDC_RX_RX0_RX_PATH_CFG1, 0, + rx_int_dem_inp_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_int1_dem_inp_enum, CDC_RX_RX1_RX_PATH_CFG1, 0, + rx_int_dem_inp_mux_text); + +static SOC_ENUM_SINGLE_DECL(rx_macro_rx0_enum, SND_SOC_NOPM, 0, rx_macro_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_macro_rx1_enum, SND_SOC_NOPM, 0, rx_macro_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_macro_rx2_enum, SND_SOC_NOPM, 0, rx_macro_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_macro_rx3_enum, SND_SOC_NOPM, 0, rx_macro_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_macro_rx4_enum, SND_SOC_NOPM, 0, rx_macro_mux_text); +static SOC_ENUM_SINGLE_DECL(rx_macro_rx5_enum, SND_SOC_NOPM, 0, rx_macro_mux_text); + +static const struct snd_kcontrol_new rx_mix_tx1_mux = + SOC_DAPM_ENUM("RX MIX TX1_MUX Mux", rx_mix_tx1_mux_enum); +static const struct snd_kcontrol_new rx_mix_tx2_mux = + SOC_DAPM_ENUM("RX MIX TX2_MUX Mux", rx_mix_tx2_mux_enum); +static const struct snd_kcontrol_new rx_int0_2_mux = + SOC_DAPM_ENUM("rx_int0_2", rx_int0_2_enum); +static const struct snd_kcontrol_new rx_int1_2_mux = + SOC_DAPM_ENUM("rx_int1_2", rx_int1_2_enum); +static const struct snd_kcontrol_new rx_int2_2_mux = + SOC_DAPM_ENUM("rx_int2_2", rx_int2_2_enum); +static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux = + SOC_DAPM_ENUM("rx_int0_1_mix_inp0", rx_int0_1_mix_inp0_enum); +static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux = + SOC_DAPM_ENUM("rx_int0_1_mix_inp1", rx_int0_1_mix_inp1_enum); +static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux = + SOC_DAPM_ENUM("rx_int0_1_mix_inp2", rx_int0_1_mix_inp2_enum); +static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux = + SOC_DAPM_ENUM("rx_int1_1_mix_inp0", rx_int1_1_mix_inp0_enum); +static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux = + SOC_DAPM_ENUM("rx_int1_1_mix_inp1", rx_int1_1_mix_inp1_enum); +static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux = + SOC_DAPM_ENUM("rx_int1_1_mix_inp2", rx_int1_1_mix_inp2_enum); +static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux = + SOC_DAPM_ENUM("rx_int2_1_mix_inp0", rx_int2_1_mix_inp0_enum); +static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux = + SOC_DAPM_ENUM("rx_int2_1_mix_inp1", rx_int2_1_mix_inp1_enum); +static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux = + SOC_DAPM_ENUM("rx_int2_1_mix_inp2", rx_int2_1_mix_inp2_enum); +static const struct snd_kcontrol_new rx_int0_mix2_inp_mux = + SOC_DAPM_ENUM("rx_int0_mix2_inp", rx_int0_mix2_inp_enum); +static const struct snd_kcontrol_new rx_int1_mix2_inp_mux = + SOC_DAPM_ENUM("rx_int1_mix2_inp", rx_int1_mix2_inp_enum); +static const struct snd_kcontrol_new rx_int2_mix2_inp_mux = + SOC_DAPM_ENUM("rx_int2_mix2_inp", rx_int2_mix2_inp_enum); +static const struct snd_kcontrol_new iir0_inp0_mux = + SOC_DAPM_ENUM("iir0_inp0", iir0_inp0_enum); +static const struct snd_kcontrol_new iir0_inp1_mux = + SOC_DAPM_ENUM("iir0_inp1", iir0_inp1_enum); +static const struct snd_kcontrol_new iir0_inp2_mux = + SOC_DAPM_ENUM("iir0_inp2", iir0_inp2_enum); +static const struct snd_kcontrol_new iir0_inp3_mux = + SOC_DAPM_ENUM("iir0_inp3", iir0_inp3_enum); +static const struct snd_kcontrol_new iir1_inp0_mux = + SOC_DAPM_ENUM("iir1_inp0", iir1_inp0_enum); +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("iir1_inp1", iir1_inp1_enum); +static const struct snd_kcontrol_new iir1_inp2_mux = + SOC_DAPM_ENUM("iir1_inp2", iir1_inp2_enum); +static const struct snd_kcontrol_new iir1_inp3_mux = + SOC_DAPM_ENUM("iir1_inp3", iir1_inp3_enum); +static const struct snd_kcontrol_new rx_int0_1_interp_mux = + SOC_DAPM_ENUM("rx_int0_1_interp", rx_int0_1_interp_enum); +static const struct snd_kcontrol_new rx_int1_1_interp_mux = + SOC_DAPM_ENUM("rx_int1_1_interp", rx_int1_1_interp_enum); +static const struct snd_kcontrol_new rx_int2_1_interp_mux = + SOC_DAPM_ENUM("rx_int2_1_interp", rx_int2_1_interp_enum); +static const struct snd_kcontrol_new rx_int0_2_interp_mux = + SOC_DAPM_ENUM("rx_int0_2_interp", rx_int0_2_interp_enum); +static const struct snd_kcontrol_new rx_int1_2_interp_mux = + SOC_DAPM_ENUM("rx_int1_2_interp", rx_int1_2_interp_enum); +static const struct snd_kcontrol_new rx_int2_2_interp_mux = + SOC_DAPM_ENUM("rx_int2_2_interp", rx_int2_2_interp_enum); +static const struct snd_kcontrol_new rx_mix_tx0_mux = + SOC_DAPM_ENUM("RX MIX TX0_MUX Mux", rx_mix_tx0_mux_enum); + +static const struct reg_default rx_defaults[] = { + /* RX Macro */ + { CDC_RX_TOP_TOP_CFG0, 0x00 }, + { CDC_RX_TOP_SWR_CTRL, 0x00 }, + { CDC_RX_TOP_DEBUG, 0x00 }, + { CDC_RX_TOP_DEBUG_BUS, 0x00 }, + { CDC_RX_TOP_DEBUG_EN0, 0x00 }, + { CDC_RX_TOP_DEBUG_EN1, 0x00 }, + { CDC_RX_TOP_DEBUG_EN2, 0x00 }, + { CDC_RX_TOP_HPHL_COMP_WR_LSB, 0x00 }, + { CDC_RX_TOP_HPHL_COMP_WR_MSB, 0x00 }, + { CDC_RX_TOP_HPHL_COMP_LUT, 0x00 }, + { CDC_RX_TOP_HPHL_COMP_RD_LSB, 0x00 }, + { CDC_RX_TOP_HPHL_COMP_RD_MSB, 0x00 }, + { CDC_RX_TOP_HPHR_COMP_WR_LSB, 0x00 }, + { CDC_RX_TOP_HPHR_COMP_WR_MSB, 0x00 }, + { CDC_RX_TOP_HPHR_COMP_LUT, 0x00 }, + { CDC_RX_TOP_HPHR_COMP_RD_LSB, 0x00 }, + { CDC_RX_TOP_HPHR_COMP_RD_MSB, 0x00 }, + { CDC_RX_TOP_DSD0_DEBUG_CFG0, 0x11 }, + { CDC_RX_TOP_DSD0_DEBUG_CFG1, 0x20 }, + { CDC_RX_TOP_DSD0_DEBUG_CFG2, 0x00 }, + { CDC_RX_TOP_DSD0_DEBUG_CFG3, 0x00 }, + { CDC_RX_TOP_DSD1_DEBUG_CFG0, 0x11 }, + { CDC_RX_TOP_DSD1_DEBUG_CFG1, 0x20 }, + { CDC_RX_TOP_DSD1_DEBUG_CFG2, 0x00 }, + { CDC_RX_TOP_DSD1_DEBUG_CFG3, 0x00 }, + { CDC_RX_TOP_RX_I2S_CTL, 0x0C }, + { CDC_RX_TOP_TX_I2S2_CTL, 0x0C }, + { CDC_RX_TOP_I2S_CLK, 0x0C }, + { CDC_RX_TOP_I2S_RESET, 0x00 }, + { CDC_RX_TOP_I2S_MUX, 0x00 }, + { CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 }, + { CDC_RX_CLK_RST_CTRL_SWR_CONTROL, 0x00 }, + { CDC_RX_CLK_RST_CTRL_DSD_CONTROL, 0x00 }, + { CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL, 0x08 }, + { CDC_RX_SOFTCLIP_CRC, 0x00 }, + { CDC_RX_SOFTCLIP_SOFTCLIP_CTRL, 0x38 }, + { CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00 }, + { CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00 }, + { CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00 }, + { CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00 }, + { CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00 }, + { CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00 }, + { CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00 }, + { CDC_RX_INP_MUX_RX_MIX_CFG5, 0x00 }, + { CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00 }, + { CDC_RX_CLSH_CRC, 0x00 }, + { CDC_RX_CLSH_DLY_CTRL, 0x03 }, + { CDC_RX_CLSH_DECAY_CTRL, 0x02 }, + { CDC_RX_CLSH_HPH_V_PA, 0x1C }, + { CDC_RX_CLSH_EAR_V_PA, 0x39 }, + { CDC_RX_CLSH_HPH_V_HD, 0x0C }, + { CDC_RX_CLSH_EAR_V_HD, 0x0C }, + { CDC_RX_CLSH_K1_MSB, 0x01 }, + { CDC_RX_CLSH_K1_LSB, 0x00 }, + { CDC_RX_CLSH_K2_MSB, 0x00 }, + { CDC_RX_CLSH_K2_LSB, 0x80 }, + { CDC_RX_CLSH_IDLE_CTRL, 0x00 }, + { CDC_RX_CLSH_IDLE_HPH, 0x00 }, + { CDC_RX_CLSH_IDLE_EAR, 0x00 }, + { CDC_RX_CLSH_TEST0, 0x07 }, + { CDC_RX_CLSH_TEST1, 0x00 }, + { CDC_RX_CLSH_OVR_VREF, 0x00 }, + { CDC_RX_CLSH_CLSG_CTL, 0x02 }, + { CDC_RX_CLSH_CLSG_CFG1, 0x9A }, + { CDC_RX_CLSH_CLSG_CFG2, 0x10 }, + { CDC_RX_BCL_VBAT_PATH_CTL, 0x00 }, + { CDC_RX_BCL_VBAT_CFG, 0x10 }, + { CDC_RX_BCL_VBAT_ADC_CAL1, 0x00 }, + { CDC_RX_BCL_VBAT_ADC_CAL2, 0x00 }, + { CDC_RX_BCL_VBAT_ADC_CAL3, 0x04 }, + { CDC_RX_BCL_VBAT_PK_EST1, 0xE0 }, + { CDC_RX_BCL_VBAT_PK_EST2, 0x01 }, + { CDC_RX_BCL_VBAT_PK_EST3, 0x40 }, + { CDC_RX_BCL_VBAT_RF_PROC1, 0x2A }, + { CDC_RX_BCL_VBAT_RF_PROC1, 0x00 }, + { CDC_RX_BCL_VBAT_TAC1, 0x00 }, + { CDC_RX_BCL_VBAT_TAC2, 0x18 }, + { CDC_RX_BCL_VBAT_TAC3, 0x18 }, + { CDC_RX_BCL_VBAT_TAC4, 0x03 }, + { CDC_RX_BCL_VBAT_GAIN_UPD1, 0x01 }, + { CDC_RX_BCL_VBAT_GAIN_UPD2, 0x00 }, + { CDC_RX_BCL_VBAT_GAIN_UPD3, 0x00 }, + { CDC_RX_BCL_VBAT_GAIN_UPD4, 0x64 }, + { CDC_RX_BCL_VBAT_GAIN_UPD5, 0x01 }, + { CDC_RX_BCL_VBAT_DEBUG1, 0x00 }, + { CDC_RX_BCL_VBAT_GAIN_UPD_MON, 0x00 }, + { CDC_RX_BCL_VBAT_GAIN_MON_VAL, 0x00 }, + { CDC_RX_BCL_VBAT_BAN, 0x0C }, + { CDC_RX_BCL_VBAT_BCL_GAIN_UPD1, 0x00 }, + { CDC_RX_BCL_VBAT_BCL_GAIN_UPD2, 0x77 }, + { CDC_RX_BCL_VBAT_BCL_GAIN_UPD3, 0x01 }, + { CDC_RX_BCL_VBAT_BCL_GAIN_UPD4, 0x00 }, + { CDC_RX_BCL_VBAT_BCL_GAIN_UPD5, 0x4B }, + { CDC_RX_BCL_VBAT_BCL_GAIN_UPD6, 0x00 }, + { CDC_RX_BCL_VBAT_BCL_GAIN_UPD7, 0x01 }, + { CDC_RX_BCL_VBAT_BCL_GAIN_UPD8, 0x00 }, + { CDC_RX_BCL_VBAT_BCL_GAIN_UPD9, 0x00 }, + { CDC_RX_BCL_VBAT_ATTN1, 0x04 }, + { CDC_RX_BCL_VBAT_ATTN2, 0x08 }, + { CDC_RX_BCL_VBAT_ATTN3, 0x0C }, + { CDC_RX_BCL_VBAT_DECODE_CTL1, 0xE0 }, + { CDC_RX_BCL_VBAT_DECODE_CTL2, 0x00 }, + { CDC_RX_BCL_VBAT_DECODE_CFG1, 0x00 }, + { CDC_RX_BCL_VBAT_DECODE_CFG2, 0x00 }, + { CDC_RX_BCL_VBAT_DECODE_CFG3, 0x00 }, + { CDC_RX_BCL_VBAT_DECODE_CFG4, 0x00 }, + { CDC_RX_BCL_VBAT_DECODE_ST, 0x00 }, + { CDC_RX_INTR_CTRL_CFG, 0x00 }, + { CDC_RX_INTR_CTRL_CLR_COMMIT, 0x00 }, + { CDC_RX_INTR_CTRL_PIN1_MASK0, 0xFF }, + { CDC_RX_INTR_CTRL_PIN1_STATUS0, 0x00 }, + { CDC_RX_INTR_CTRL_PIN1_CLEAR0, 0x00 }, + { CDC_RX_INTR_CTRL_PIN2_MASK0, 0xFF }, + { CDC_RX_INTR_CTRL_PIN2_STATUS0, 0x00 }, + { CDC_RX_INTR_CTRL_PIN2_CLEAR0, 0x00 }, + { CDC_RX_INTR_CTRL_LEVEL0, 0x00 }, + { CDC_RX_INTR_CTRL_BYPASS0, 0x00 }, + { CDC_RX_INTR_CTRL_SET0, 0x00 }, + { CDC_RX_RX0_RX_PATH_CTL, 0x04 }, + { CDC_RX_RX0_RX_PATH_CFG0, 0x00 }, + { CDC_RX_RX0_RX_PATH_CFG1, 0x64 }, + { CDC_RX_RX0_RX_PATH_CFG2, 0x8F }, + { CDC_RX_RX0_RX_PATH_CFG3, 0x00 }, + { CDC_RX_RX0_RX_VOL_CTL, 0x00 }, + { CDC_RX_RX0_RX_PATH_MIX_CTL, 0x04 }, + { CDC_RX_RX0_RX_PATH_MIX_CFG, 0x7E }, + { CDC_RX_RX0_RX_VOL_MIX_CTL, 0x00 }, + { CDC_RX_RX0_RX_PATH_SEC1, 0x08 }, + { CDC_RX_RX0_RX_PATH_SEC2, 0x00 }, + { CDC_RX_RX0_RX_PATH_SEC3, 0x00 }, + { CDC_RX_RX0_RX_PATH_SEC4, 0x00 }, + { CDC_RX_RX0_RX_PATH_SEC7, 0x00 }, + { CDC_RX_RX0_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_RX_RX0_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_RX_RX0_RX_PATH_DSM_CTL, 0x08 }, + { CDC_RX_RX0_RX_PATH_DSM_DATA1, 0x00 }, + { CDC_RX_RX0_RX_PATH_DSM_DATA2, 0x00 }, + { CDC_RX_RX0_RX_PATH_DSM_DATA3, 0x00 }, + { CDC_RX_RX0_RX_PATH_DSM_DATA4, 0x55 }, + { CDC_RX_RX0_RX_PATH_DSM_DATA5, 0x55 }, + { CDC_RX_RX0_RX_PATH_DSM_DATA6, 0x55 }, + { CDC_RX_RX1_RX_PATH_CTL, 0x04 }, + { CDC_RX_RX1_RX_PATH_CFG0, 0x00 }, + { CDC_RX_RX1_RX_PATH_CFG1, 0x64 }, + { CDC_RX_RX1_RX_PATH_CFG2, 0x8F }, + { CDC_RX_RX1_RX_PATH_CFG3, 0x00 }, + { CDC_RX_RX1_RX_VOL_CTL, 0x00 }, + { CDC_RX_RX1_RX_PATH_MIX_CTL, 0x04 }, + { CDC_RX_RX1_RX_PATH_MIX_CFG, 0x7E }, + { CDC_RX_RX1_RX_VOL_MIX_CTL, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC1, 0x08 }, + { CDC_RX_RX1_RX_PATH_SEC2, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC3, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC4, 0x00 }, + { CDC_RX_RX1_RX_PATH_SEC7, 0x00 }, + { CDC_RX_RX1_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_RX_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_CTL, 0x08 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA1, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA2, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA3, 0x00 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA4, 0x55 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA5, 0x55 }, + { CDC_RX_RX1_RX_PATH_DSM_DATA6, 0x55 }, + { CDC_RX_RX2_RX_PATH_CTL, 0x04 }, + { CDC_RX_RX2_RX_PATH_CFG0, 0x00 }, + { CDC_RX_RX2_RX_PATH_CFG1, 0x64 }, + { CDC_RX_RX2_RX_PATH_CFG2, 0x8F }, + { CDC_RX_RX2_RX_PATH_CFG3, 0x00 }, + { CDC_RX_RX2_RX_VOL_CTL, 0x00 }, + { CDC_RX_RX2_RX_PATH_MIX_CTL, 0x04 }, + { CDC_RX_RX2_RX_PATH_MIX_CFG, 0x7E }, + { CDC_RX_RX2_RX_VOL_MIX_CTL, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC0, 0x04 }, + { CDC_RX_RX2_RX_PATH_SEC1, 0x08 }, + { CDC_RX_RX2_RX_PATH_SEC2, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC3, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC4, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC5, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC6, 0x00 }, + { CDC_RX_RX2_RX_PATH_SEC7, 0x00 }, + { CDC_RX_RX2_RX_PATH_MIX_SEC0, 0x08 }, + { CDC_RX_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { CDC_RX_RX2_RX_PATH_DSM_CTL, 0x00 }, + { CDC_RX_IDLE_DETECT_PATH_CTL, 0x00 }, + { CDC_RX_IDLE_DETECT_CFG0, 0x07 }, + { CDC_RX_IDLE_DETECT_CFG1, 0x3C }, + { CDC_RX_IDLE_DETECT_CFG2, 0x00 }, + { CDC_RX_IDLE_DETECT_CFG3, 0x00 }, + { CDC_RX_COMPANDER0_CTL0, 0x60 }, + { CDC_RX_COMPANDER0_CTL1, 0xDB }, + { CDC_RX_COMPANDER0_CTL2, 0xFF }, + { CDC_RX_COMPANDER0_CTL3, 0x35 }, + { CDC_RX_COMPANDER0_CTL4, 0xFF }, + { CDC_RX_COMPANDER0_CTL5, 0x00 }, + { CDC_RX_COMPANDER0_CTL6, 0x01 }, + { CDC_RX_COMPANDER0_CTL7, 0x28 }, + { CDC_RX_COMPANDER1_CTL0, 0x60 }, + { CDC_RX_COMPANDER1_CTL1, 0xDB }, + { CDC_RX_COMPANDER1_CTL2, 0xFF }, + { CDC_RX_COMPANDER1_CTL3, 0x35 }, + { CDC_RX_COMPANDER1_CTL4, 0xFF }, + { CDC_RX_COMPANDER1_CTL5, 0x00 }, + { CDC_RX_COMPANDER1_CTL6, 0x01 }, + { CDC_RX_COMPANDER1_CTL7, 0x28 }, + { CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_CTL, 0x40 }, + { CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_CTL, 0x40 }, + { CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL, 0x00 }, + { CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL, 0x00 }, + { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00 }, + { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00 }, + { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00 }, + { CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00 }, + { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00 }, + { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00 }, + { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00 }, + { CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00 }, + { CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04 }, + { CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00 }, + { CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL, 0x04 }, + { CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1, 0x00 }, + { CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL, 0x00 }, + { CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0, 0x01 }, + { CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL, 0x00 }, + { CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0, 0x01 }, + { CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL, 0x00 }, + { CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0, 0x01 }, + { CDC_RX_EC_ASRC0_CLK_RST_CTL, 0x00 }, + { CDC_RX_EC_ASRC0_CTL0, 0x00 }, + { CDC_RX_EC_ASRC0_CTL1, 0x00 }, + { CDC_RX_EC_ASRC0_FIFO_CTL, 0xA8 }, + { CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 }, + { CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 }, + { CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 }, + { CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 }, + { CDC_RX_EC_ASRC0_STATUS_FIFO, 0x00 }, + { CDC_RX_EC_ASRC1_CLK_RST_CTL, 0x00 }, + { CDC_RX_EC_ASRC1_CTL0, 0x00 }, + { CDC_RX_EC_ASRC1_CTL1, 0x00 }, + { CDC_RX_EC_ASRC1_FIFO_CTL, 0xA8 }, + { CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 }, + { CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 }, + { CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 }, + { CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 }, + { CDC_RX_EC_ASRC1_STATUS_FIFO, 0x00 }, + { CDC_RX_EC_ASRC2_CLK_RST_CTL, 0x00 }, + { CDC_RX_EC_ASRC2_CTL0, 0x00 }, + { CDC_RX_EC_ASRC2_CTL1, 0x00 }, + { CDC_RX_EC_ASRC2_FIFO_CTL, 0xA8 }, + { CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB, 0x00 }, + { CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB, 0x00 }, + { CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB, 0x00 }, + { CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB, 0x00 }, + { CDC_RX_EC_ASRC2_STATUS_FIFO, 0x00 }, + { CDC_RX_DSD0_PATH_CTL, 0x00 }, + { CDC_RX_DSD0_CFG0, 0x00 }, + { CDC_RX_DSD0_CFG1, 0x62 }, + { CDC_RX_DSD0_CFG2, 0x96 }, + { CDC_RX_DSD1_PATH_CTL, 0x00 }, + { CDC_RX_DSD1_CFG0, 0x00 }, + { CDC_RX_DSD1_CFG1, 0x62 }, + { CDC_RX_DSD1_CFG2, 0x96 }, +}; + +static bool rx_is_wronly_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case CDC_RX_BCL_VBAT_GAIN_UPD_MON: + case CDC_RX_INTR_CTRL_CLR_COMMIT: + case CDC_RX_INTR_CTRL_PIN1_CLEAR0: + case CDC_RX_INTR_CTRL_PIN2_CLEAR0: + return true; + } + + return false; +} + +static bool rx_is_volatile_register(struct device *dev, unsigned int reg) +{ + /* Update volatile list for rx/tx macros */ + switch (reg) { + case CDC_RX_TOP_HPHL_COMP_RD_LSB: + case CDC_RX_TOP_HPHL_COMP_WR_LSB: + case CDC_RX_TOP_HPHL_COMP_RD_MSB: + case CDC_RX_TOP_HPHL_COMP_WR_MSB: + case CDC_RX_TOP_HPHR_COMP_RD_LSB: + case CDC_RX_TOP_HPHR_COMP_WR_LSB: + case CDC_RX_TOP_HPHR_COMP_RD_MSB: + case CDC_RX_TOP_HPHR_COMP_WR_MSB: + case CDC_RX_TOP_DSD0_DEBUG_CFG2: + case CDC_RX_TOP_DSD1_DEBUG_CFG2: + case CDC_RX_BCL_VBAT_GAIN_MON_VAL: + case CDC_RX_BCL_VBAT_DECODE_ST: + case CDC_RX_INTR_CTRL_PIN1_STATUS0: + case CDC_RX_INTR_CTRL_PIN2_STATUS0: + case CDC_RX_COMPANDER0_CTL6: + case CDC_RX_COMPANDER1_CTL6: + case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB: + case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB: + case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB: + case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB: + case CDC_RX_EC_ASRC0_STATUS_FIFO: + case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB: + case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB: + case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB: + case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB: + case CDC_RX_EC_ASRC1_STATUS_FIFO: + case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB: + case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB: + case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB: + case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB: + case CDC_RX_EC_ASRC2_STATUS_FIFO: + return true; + } + return false; +} + +static bool rx_is_rw_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_RX_TOP_TOP_CFG0: + case CDC_RX_TOP_SWR_CTRL: + case CDC_RX_TOP_DEBUG: + case CDC_RX_TOP_DEBUG_BUS: + case CDC_RX_TOP_DEBUG_EN0: + case CDC_RX_TOP_DEBUG_EN1: + case CDC_RX_TOP_DEBUG_EN2: + case CDC_RX_TOP_HPHL_COMP_WR_LSB: + case CDC_RX_TOP_HPHL_COMP_WR_MSB: + case CDC_RX_TOP_HPHL_COMP_LUT: + case CDC_RX_TOP_HPHR_COMP_WR_LSB: + case CDC_RX_TOP_HPHR_COMP_WR_MSB: + case CDC_RX_TOP_HPHR_COMP_LUT: + case CDC_RX_TOP_DSD0_DEBUG_CFG0: + case CDC_RX_TOP_DSD0_DEBUG_CFG1: + case CDC_RX_TOP_DSD0_DEBUG_CFG3: + case CDC_RX_TOP_DSD1_DEBUG_CFG0: + case CDC_RX_TOP_DSD1_DEBUG_CFG1: + case CDC_RX_TOP_DSD1_DEBUG_CFG3: + case CDC_RX_TOP_RX_I2S_CTL: + case CDC_RX_TOP_TX_I2S2_CTL: + case CDC_RX_TOP_I2S_CLK: + case CDC_RX_TOP_I2S_RESET: + case CDC_RX_TOP_I2S_MUX: + case CDC_RX_CLK_RST_CTRL_MCLK_CONTROL: + case CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL: + case CDC_RX_CLK_RST_CTRL_SWR_CONTROL: + case CDC_RX_CLK_RST_CTRL_DSD_CONTROL: + case CDC_RX_CLK_RST_CTRL_ASRC_SHARE_CONTROL: + case CDC_RX_SOFTCLIP_CRC: + case CDC_RX_SOFTCLIP_SOFTCLIP_CTRL: + case CDC_RX_INP_MUX_RX_INT0_CFG0: + case CDC_RX_INP_MUX_RX_INT0_CFG1: + case CDC_RX_INP_MUX_RX_INT1_CFG0: + case CDC_RX_INP_MUX_RX_INT1_CFG1: + case CDC_RX_INP_MUX_RX_INT2_CFG0: + case CDC_RX_INP_MUX_RX_INT2_CFG1: + case CDC_RX_INP_MUX_RX_MIX_CFG4: + case CDC_RX_INP_MUX_RX_MIX_CFG5: + case CDC_RX_INP_MUX_SIDETONE_SRC_CFG0: + case CDC_RX_CLSH_CRC: + case CDC_RX_CLSH_DLY_CTRL: + case CDC_RX_CLSH_DECAY_CTRL: + case CDC_RX_CLSH_HPH_V_PA: + case CDC_RX_CLSH_EAR_V_PA: + case CDC_RX_CLSH_HPH_V_HD: + case CDC_RX_CLSH_EAR_V_HD: + case CDC_RX_CLSH_K1_MSB: + case CDC_RX_CLSH_K1_LSB: + case CDC_RX_CLSH_K2_MSB: + case CDC_RX_CLSH_K2_LSB: + case CDC_RX_CLSH_IDLE_CTRL: + case CDC_RX_CLSH_IDLE_HPH: + case CDC_RX_CLSH_IDLE_EAR: + case CDC_RX_CLSH_TEST0: + case CDC_RX_CLSH_TEST1: + case CDC_RX_CLSH_OVR_VREF: + case CDC_RX_CLSH_CLSG_CTL: + case CDC_RX_CLSH_CLSG_CFG1: + case CDC_RX_CLSH_CLSG_CFG2: + case CDC_RX_BCL_VBAT_PATH_CTL: + case CDC_RX_BCL_VBAT_CFG: + case CDC_RX_BCL_VBAT_ADC_CAL1: + case CDC_RX_BCL_VBAT_ADC_CAL2: + case CDC_RX_BCL_VBAT_ADC_CAL3: + case CDC_RX_BCL_VBAT_PK_EST1: + case CDC_RX_BCL_VBAT_PK_EST2: + case CDC_RX_BCL_VBAT_PK_EST3: + case CDC_RX_BCL_VBAT_RF_PROC1: + case CDC_RX_BCL_VBAT_RF_PROC2: + case CDC_RX_BCL_VBAT_TAC1: + case CDC_RX_BCL_VBAT_TAC2: + case CDC_RX_BCL_VBAT_TAC3: + case CDC_RX_BCL_VBAT_TAC4: + case CDC_RX_BCL_VBAT_GAIN_UPD1: + case CDC_RX_BCL_VBAT_GAIN_UPD2: + case CDC_RX_BCL_VBAT_GAIN_UPD3: + case CDC_RX_BCL_VBAT_GAIN_UPD4: + case CDC_RX_BCL_VBAT_GAIN_UPD5: + case CDC_RX_BCL_VBAT_DEBUG1: + case CDC_RX_BCL_VBAT_BAN: + case CDC_RX_BCL_VBAT_BCL_GAIN_UPD1: + case CDC_RX_BCL_VBAT_BCL_GAIN_UPD2: + case CDC_RX_BCL_VBAT_BCL_GAIN_UPD3: + case CDC_RX_BCL_VBAT_BCL_GAIN_UPD4: + case CDC_RX_BCL_VBAT_BCL_GAIN_UPD5: + case CDC_RX_BCL_VBAT_BCL_GAIN_UPD6: + case CDC_RX_BCL_VBAT_BCL_GAIN_UPD7: + case CDC_RX_BCL_VBAT_BCL_GAIN_UPD8: + case CDC_RX_BCL_VBAT_BCL_GAIN_UPD9: + case CDC_RX_BCL_VBAT_ATTN1: + case CDC_RX_BCL_VBAT_ATTN2: + case CDC_RX_BCL_VBAT_ATTN3: + case CDC_RX_BCL_VBAT_DECODE_CTL1: + case CDC_RX_BCL_VBAT_DECODE_CTL2: + case CDC_RX_BCL_VBAT_DECODE_CFG1: + case CDC_RX_BCL_VBAT_DECODE_CFG2: + case CDC_RX_BCL_VBAT_DECODE_CFG3: + case CDC_RX_BCL_VBAT_DECODE_CFG4: + case CDC_RX_INTR_CTRL_CFG: + case CDC_RX_INTR_CTRL_PIN1_MASK0: + case CDC_RX_INTR_CTRL_PIN2_MASK0: + case CDC_RX_INTR_CTRL_LEVEL0: + case CDC_RX_INTR_CTRL_BYPASS0: + case CDC_RX_INTR_CTRL_SET0: + case CDC_RX_RX0_RX_PATH_CTL: + case CDC_RX_RX0_RX_PATH_CFG0: + case CDC_RX_RX0_RX_PATH_CFG1: + case CDC_RX_RX0_RX_PATH_CFG2: + case CDC_RX_RX0_RX_PATH_CFG3: + case CDC_RX_RX0_RX_VOL_CTL: + case CDC_RX_RX0_RX_PATH_MIX_CTL: + case CDC_RX_RX0_RX_PATH_MIX_CFG: + case CDC_RX_RX0_RX_VOL_MIX_CTL: + case CDC_RX_RX0_RX_PATH_SEC1: + case CDC_RX_RX0_RX_PATH_SEC2: + case CDC_RX_RX0_RX_PATH_SEC3: + case CDC_RX_RX0_RX_PATH_SEC4: + case CDC_RX_RX0_RX_PATH_SEC7: + case CDC_RX_RX0_RX_PATH_MIX_SEC0: + case CDC_RX_RX0_RX_PATH_MIX_SEC1: + case CDC_RX_RX0_RX_PATH_DSM_CTL: + case CDC_RX_RX0_RX_PATH_DSM_DATA1: + case CDC_RX_RX0_RX_PATH_DSM_DATA2: + case CDC_RX_RX0_RX_PATH_DSM_DATA3: + case CDC_RX_RX0_RX_PATH_DSM_DATA4: + case CDC_RX_RX0_RX_PATH_DSM_DATA5: + case CDC_RX_RX0_RX_PATH_DSM_DATA6: + case CDC_RX_RX1_RX_PATH_CTL: + case CDC_RX_RX1_RX_PATH_CFG0: + case CDC_RX_RX1_RX_PATH_CFG1: + case CDC_RX_RX1_RX_PATH_CFG2: + case CDC_RX_RX1_RX_PATH_CFG3: + case CDC_RX_RX1_RX_VOL_CTL: + case CDC_RX_RX1_RX_PATH_MIX_CTL: + case CDC_RX_RX1_RX_PATH_MIX_CFG: + case CDC_RX_RX1_RX_VOL_MIX_CTL: + case CDC_RX_RX1_RX_PATH_SEC1: + case CDC_RX_RX1_RX_PATH_SEC2: + case CDC_RX_RX1_RX_PATH_SEC3: + case CDC_RX_RX1_RX_PATH_SEC4: + case CDC_RX_RX1_RX_PATH_SEC7: + case CDC_RX_RX1_RX_PATH_MIX_SEC0: + case CDC_RX_RX1_RX_PATH_MIX_SEC1: + case CDC_RX_RX1_RX_PATH_DSM_CTL: + case CDC_RX_RX1_RX_PATH_DSM_DATA1: + case CDC_RX_RX1_RX_PATH_DSM_DATA2: + case CDC_RX_RX1_RX_PATH_DSM_DATA3: + case CDC_RX_RX1_RX_PATH_DSM_DATA4: + case CDC_RX_RX1_RX_PATH_DSM_DATA5: + case CDC_RX_RX1_RX_PATH_DSM_DATA6: + case CDC_RX_RX2_RX_PATH_CTL: + case CDC_RX_RX2_RX_PATH_CFG0: + case CDC_RX_RX2_RX_PATH_CFG1: + case CDC_RX_RX2_RX_PATH_CFG2: + case CDC_RX_RX2_RX_PATH_CFG3: + case CDC_RX_RX2_RX_VOL_CTL: + case CDC_RX_RX2_RX_PATH_MIX_CTL: + case CDC_RX_RX2_RX_PATH_MIX_CFG: + case CDC_RX_RX2_RX_VOL_MIX_CTL: + case CDC_RX_RX2_RX_PATH_SEC0: + case CDC_RX_RX2_RX_PATH_SEC1: + case CDC_RX_RX2_RX_PATH_SEC2: + case CDC_RX_RX2_RX_PATH_SEC3: + case CDC_RX_RX2_RX_PATH_SEC4: + case CDC_RX_RX2_RX_PATH_SEC5: + case CDC_RX_RX2_RX_PATH_SEC6: + case CDC_RX_RX2_RX_PATH_SEC7: + case CDC_RX_RX2_RX_PATH_MIX_SEC0: + case CDC_RX_RX2_RX_PATH_MIX_SEC1: + case CDC_RX_RX2_RX_PATH_DSM_CTL: + case CDC_RX_IDLE_DETECT_PATH_CTL: + case CDC_RX_IDLE_DETECT_CFG0: + case CDC_RX_IDLE_DETECT_CFG1: + case CDC_RX_IDLE_DETECT_CFG2: + case CDC_RX_IDLE_DETECT_CFG3: + case CDC_RX_COMPANDER0_CTL0: + case CDC_RX_COMPANDER0_CTL1: + case CDC_RX_COMPANDER0_CTL2: + case CDC_RX_COMPANDER0_CTL3: + case CDC_RX_COMPANDER0_CTL4: + case CDC_RX_COMPANDER0_CTL5: + case CDC_RX_COMPANDER0_CTL7: + case CDC_RX_COMPANDER1_CTL0: + case CDC_RX_COMPANDER1_CTL1: + case CDC_RX_COMPANDER1_CTL2: + case CDC_RX_COMPANDER1_CTL3: + case CDC_RX_COMPANDER1_CTL4: + case CDC_RX_COMPANDER1_CTL5: + case CDC_RX_COMPANDER1_CTL7: + case CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B5_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B6_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B7_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_GAIN_B8_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL: + case CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B5_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B6_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B7_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_GAIN_B8_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_COEF_B1_CTL: + case CDC_RX_SIDETONE_IIR1_IIR_COEF_B2_CTL: + case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG0: + case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG1: + case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG2: + case CDC_RX_IIR_INP_MUX_IIR0_MIX_CFG3: + case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG0: + case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG1: + case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG2: + case CDC_RX_IIR_INP_MUX_IIR1_MIX_CFG3: + case CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL: + case CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CFG1: + case CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL: + case CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CFG1: + case CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL: + case CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0: + case CDC_RX_EC_REF_HQ1_EC_REF_HQ_PATH_CTL: + case CDC_RX_EC_REF_HQ1_EC_REF_HQ_CFG0: + case CDC_RX_EC_REF_HQ2_EC_REF_HQ_PATH_CTL: + case CDC_RX_EC_REF_HQ2_EC_REF_HQ_CFG0: + case CDC_RX_EC_ASRC0_CLK_RST_CTL: + case CDC_RX_EC_ASRC0_CTL0: + case CDC_RX_EC_ASRC0_CTL1: + case CDC_RX_EC_ASRC0_FIFO_CTL: + case CDC_RX_EC_ASRC1_CLK_RST_CTL: + case CDC_RX_EC_ASRC1_CTL0: + case CDC_RX_EC_ASRC1_CTL1: + case CDC_RX_EC_ASRC1_FIFO_CTL: + case CDC_RX_EC_ASRC2_CLK_RST_CTL: + case CDC_RX_EC_ASRC2_CTL0: + case CDC_RX_EC_ASRC2_CTL1: + case CDC_RX_EC_ASRC2_FIFO_CTL: + case CDC_RX_DSD0_PATH_CTL: + case CDC_RX_DSD0_CFG0: + case CDC_RX_DSD0_CFG1: + case CDC_RX_DSD0_CFG2: + case CDC_RX_DSD1_PATH_CTL: + case CDC_RX_DSD1_CFG0: + case CDC_RX_DSD1_CFG1: + case CDC_RX_DSD1_CFG2: + return true; + } + + return false; +} + +static bool rx_is_writeable_register(struct device *dev, unsigned int reg) +{ + bool ret; + + ret = rx_is_rw_register(dev, reg); + if (!ret) + return rx_is_wronly_register(dev, reg); + + return ret; +} + +static bool rx_is_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_RX_TOP_HPHL_COMP_RD_LSB: + case CDC_RX_TOP_HPHL_COMP_RD_MSB: + case CDC_RX_TOP_HPHR_COMP_RD_LSB: + case CDC_RX_TOP_HPHR_COMP_RD_MSB: + case CDC_RX_TOP_DSD0_DEBUG_CFG2: + case CDC_RX_TOP_DSD1_DEBUG_CFG2: + case CDC_RX_BCL_VBAT_GAIN_MON_VAL: + case CDC_RX_BCL_VBAT_DECODE_ST: + case CDC_RX_INTR_CTRL_PIN1_STATUS0: + case CDC_RX_INTR_CTRL_PIN2_STATUS0: + case CDC_RX_COMPANDER0_CTL6: + case CDC_RX_COMPANDER1_CTL6: + case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_LSB: + case CDC_RX_EC_ASRC0_STATUS_FMIN_CNTR_MSB: + case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_LSB: + case CDC_RX_EC_ASRC0_STATUS_FMAX_CNTR_MSB: + case CDC_RX_EC_ASRC0_STATUS_FIFO: + case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_LSB: + case CDC_RX_EC_ASRC1_STATUS_FMIN_CNTR_MSB: + case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_LSB: + case CDC_RX_EC_ASRC1_STATUS_FMAX_CNTR_MSB: + case CDC_RX_EC_ASRC1_STATUS_FIFO: + case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_LSB: + case CDC_RX_EC_ASRC2_STATUS_FMIN_CNTR_MSB: + case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_LSB: + case CDC_RX_EC_ASRC2_STATUS_FMAX_CNTR_MSB: + case CDC_RX_EC_ASRC2_STATUS_FIFO: + return true; + } + + return rx_is_rw_register(dev, reg); +} + +static const struct regmap_config rx_regmap_config = { + .name = "rx_macro", + .reg_bits = 16, + .val_bits = 32, /* 8 but with 32 bit read/write */ + .reg_stride = 4, + .cache_type = REGCACHE_FLAT, + .reg_defaults = rx_defaults, + .num_reg_defaults = ARRAY_SIZE(rx_defaults), + .max_register = RX_MAX_OFFSET, + .writeable_reg = rx_is_writeable_register, + .volatile_reg = rx_is_volatile_register, + .readable_reg = rx_is_readable_register, +}; + +static int rx_macro_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned short look_ahead_dly_reg; + unsigned int val; + + val = ucontrol->value.enumerated.item[0]; + + if (e->reg == CDC_RX_RX0_RX_PATH_CFG1) + look_ahead_dly_reg = CDC_RX_RX0_RX_PATH_CFG0; + else if (e->reg == CDC_RX_RX1_RX_PATH_CFG1) + look_ahead_dly_reg = CDC_RX_RX1_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + if (val) + snd_soc_component_update_bits(component, look_ahead_dly_reg, + CDC_RX_DLY_ZN_EN_MASK, + CDC_RX_DLY_ZN_ENABLE); + else + snd_soc_component_update_bits(component, look_ahead_dly_reg, + CDC_RX_DLY_ZN_EN_MASK, 0); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static const struct snd_kcontrol_new rx_int0_dem_inp_mux = + SOC_DAPM_ENUM_EXT("rx_int0_dem_inp", rx_int0_dem_inp_enum, + snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put); +static const struct snd_kcontrol_new rx_int1_dem_inp_mux = + SOC_DAPM_ENUM_EXT("rx_int1_dem_inp", rx_int1_dem_inp_enum, + snd_soc_dapm_get_enum_double, rx_macro_int_dem_inp_mux_put); + +static int rx_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, + int rate_reg_val, u32 sample_rate) +{ + + u8 int_1_mix1_inp; + u32 j, port; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_component *component = dai->component; + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + for_each_set_bit(port, &rx->active_ch_mask[dai->id], RX_MACRO_PORTS_MAX) { + int_1_mix1_inp = port; + int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0; + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the rx port + * is connected + */ + for (j = 0; j < INTERP_MAX; j++) { + int_mux_cfg1 = int_mux_cfg0 + 4; + + inp0_sel = snd_soc_component_read_field(component, int_mux_cfg0, + CDC_RX_INTX_1_MIX_INP0_SEL_MASK); + inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0, + CDC_RX_INTX_1_MIX_INP1_SEL_MASK); + inp2_sel = snd_soc_component_read_field(component, int_mux_cfg1, + CDC_RX_INTX_1_MIX_INP2_SEL_MASK); + + if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || + (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || + (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) { + int_fs_reg = CDC_RX_RXn_RX_PATH_CTL(j); + /* sample_rate is in Hz */ + snd_soc_component_update_bits(component, int_fs_reg, + CDC_RX_PATH_PCM_RATE_MASK, + rate_reg_val); + } + int_mux_cfg0 += 8; + } + } + + return 0; +} + +static int rx_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, + int rate_reg_val, u32 sample_rate) +{ + + u8 int_2_inp; + u32 j, port; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_component *component = dai->component; + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + for_each_set_bit(port, &rx->active_ch_mask[dai->id], RX_MACRO_PORTS_MAX) { + int_2_inp = port; + + int_mux_cfg1 = CDC_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < INTERP_MAX; j++) { + int_mux_cfg1_val = snd_soc_component_read_field(component, int_mux_cfg1, + CDC_RX_INTX_2_SEL_MASK); + + if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) { + int_fs_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(j); + snd_soc_component_update_bits(component, int_fs_reg, + CDC_RX_RXn_MIX_PCM_RATE_MASK, + rate_reg_val); + } + int_mux_cfg1 += 8; + } + } + return 0; +} + +static int rx_macro_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) + if (sample_rate == sr_val_tbl[i].sample_rate) + rate_val = sr_val_tbl[i].rate_val; + + ret = rx_macro_set_prim_interpolator_rate(dai, rate_val, sample_rate); + if (ret) + return ret; + + ret = rx_macro_set_mix_interpolator_rate(dai, rate_val, sample_rate); + + return ret; +} + +static int rx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + int ret; + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = rx_macro_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(component->dev, "%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + rx->bit_width[dai->id] = params_width(params); + break; + default: + break; + } + return 0; +} + +static int rx_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_component *component = dai->component; + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + u16 val, mask = 0, cnt = 0, temp; + + switch (dai->id) { + case RX_MACRO_AIF1_PB: + case RX_MACRO_AIF2_PB: + case RX_MACRO_AIF3_PB: + case RX_MACRO_AIF4_PB: + for_each_set_bit(temp, &rx->active_ch_mask[dai->id], + RX_MACRO_PORTS_MAX) { + mask |= (1 << temp); + if (++cnt == RX_MACRO_MAX_DMA_CH_PER_PORT) + break; + } + /* + * CDC_DMA_RX_0 port drives RX0/RX1 -- ch_mask 0x1/0x2/0x3 + * CDC_DMA_RX_1 port drives RX2/RX3 -- ch_mask 0x1/0x2/0x3 + * CDC_DMA_RX_2 port drives RX4 -- ch_mask 0x1 + * CDC_DMA_RX_3 port drives RX5 -- ch_mask 0x1 + * AIFn can pair to any CDC_DMA_RX_n port. + * In general, below convention is used:: + * CDC_DMA_RX_0(AIF1)/CDC_DMA_RX_1(AIF2)/ + * CDC_DMA_RX_2(AIF3)/CDC_DMA_RX_3(AIF4) + */ + if (mask & 0x0C) + mask = mask >> 2; + if ((mask & 0x10) || (mask & 0x20)) + mask = 0x1; + *rx_slot = mask; + *rx_num = rx->active_ch_cnt[dai->id]; + break; + case RX_MACRO_AIF_ECHO: + val = snd_soc_component_read(component, CDC_RX_INP_MUX_RX_MIX_CFG4); + if (val & RX_MACRO_EC_MIX_TX0_MASK) { + mask |= 0x1; + cnt++; + } + if (val & RX_MACRO_EC_MIX_TX1_MASK) { + mask |= 0x2; + cnt++; + } + val = snd_soc_component_read(component, + CDC_RX_INP_MUX_RX_MIX_CFG5); + if (val & RX_MACRO_EC_MIX_TX2_MASK) { + mask |= 0x4; + cnt++; + } + *tx_slot = mask; + *tx_num = cnt; + break; + default: + dev_err(component->dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static int rx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + uint16_t j, reg, mix_reg, dsm_reg; + u16 int_mux_cfg0, int_mux_cfg1; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + + switch (dai->id) { + case RX_MACRO_AIF1_PB: + case RX_MACRO_AIF2_PB: + case RX_MACRO_AIF3_PB: + case RX_MACRO_AIF4_PB: + for (j = 0; j < INTERP_MAX; j++) { + reg = CDC_RX_RXn_RX_PATH_CTL(j); + mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(j); + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(j); + + if (mute) { + snd_soc_component_update_bits(component, reg, + CDC_RX_PATH_PGA_MUTE_MASK, + CDC_RX_PATH_PGA_MUTE_ENABLE); + snd_soc_component_update_bits(component, mix_reg, + CDC_RX_PATH_PGA_MUTE_MASK, + CDC_RX_PATH_PGA_MUTE_ENABLE); + } else { + snd_soc_component_update_bits(component, reg, + CDC_RX_PATH_PGA_MUTE_MASK, 0x0); + snd_soc_component_update_bits(component, mix_reg, + CDC_RX_PATH_PGA_MUTE_MASK, 0x0); + } + + if (j == INTERP_AUX) + dsm_reg = CDC_RX_RX2_RX_PATH_DSM_CTL; + + int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0 + j * 8; + int_mux_cfg1 = int_mux_cfg0 + 4; + int_mux_cfg0_val = snd_soc_component_read(component, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_component_read(component, int_mux_cfg1); + + if (snd_soc_component_read(component, dsm_reg) & 0x01) { + if (int_mux_cfg0_val || (int_mux_cfg1_val & 0xF0)) + snd_soc_component_update_bits(component, reg, 0x20, 0x20); + if (int_mux_cfg1_val & 0x0F) { + snd_soc_component_update_bits(component, reg, 0x20, 0x20); + snd_soc_component_update_bits(component, mix_reg, 0x20, + 0x20); + } + } + } + break; + default: + break; + } + return 0; +} + +static const struct snd_soc_dai_ops rx_macro_dai_ops = { + .hw_params = rx_macro_hw_params, + .get_channel_map = rx_macro_get_channel_map, + .mute_stream = rx_macro_digital_mute, +}; + +static struct snd_soc_dai_driver rx_macro_dai[] = { + { + .name = "rx_macro_rx1", + .id = RX_MACRO_AIF1_PB, + .playback = { + .stream_name = "RX_MACRO_AIF1 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_rx2", + .id = RX_MACRO_AIF2_PB, + .playback = { + .stream_name = "RX_MACRO_AIF2 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_rx3", + .id = RX_MACRO_AIF3_PB, + .playback = { + .stream_name = "RX_MACRO_AIF3 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_rx4", + .id = RX_MACRO_AIF4_PB, + .playback = { + .stream_name = "RX_MACRO_AIF4 Playback", + .rates = RX_MACRO_RATES | RX_MACRO_FRAC_RATES, + .formats = RX_MACRO_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rx_macro_dai_ops, + }, + { + .name = "rx_macro_echo", + .id = RX_MACRO_AIF_ECHO, + .capture = { + .stream_name = "RX_AIF_ECHO Capture", + .rates = RX_MACRO_ECHO_RATES, + .formats = RX_MACRO_ECHO_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 3, + }, + .ops = &rx_macro_dai_ops, + }, +}; + +static void rx_macro_mclk_enable(struct rx_macro *rx, bool mclk_enable) +{ + struct regmap *regmap = rx->regmap; + + if (mclk_enable) { + if (rx->rx_mclk_users == 0) { + regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, + CDC_RX_CLK_MCLK_EN_MASK | + CDC_RX_CLK_MCLK2_EN_MASK, + CDC_RX_CLK_MCLK_ENABLE | + CDC_RX_CLK_MCLK2_ENABLE); + regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_RX_FS_MCLK_CNT_CLR_MASK, 0x00); + regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_RX_FS_MCLK_CNT_EN_MASK, + CDC_RX_FS_MCLK_CNT_ENABLE); + regcache_mark_dirty(regmap); + regcache_sync(regmap); + } + rx->rx_mclk_users++; + } else { + if (rx->rx_mclk_users <= 0) { + dev_err(rx->dev, "%s: clock already disabled\n", __func__); + rx->rx_mclk_users = 0; + return; + } + rx->rx_mclk_users--; + if (rx->rx_mclk_users == 0) { + regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_RX_FS_MCLK_CNT_EN_MASK, 0x0); + regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_RX_FS_MCLK_CNT_CLR_MASK, + CDC_RX_FS_MCLK_CNT_CLR); + regmap_update_bits(regmap, CDC_RX_CLK_RST_CTRL_MCLK_CONTROL, + CDC_RX_CLK_MCLK_EN_MASK | + CDC_RX_CLK_MCLK2_EN_MASK, 0x0); + } + } +} + +static int rx_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rx_macro_mclk_enable(rx, true); + break; + case SND_SOC_DAPM_POST_PMD: + rx_macro_mclk_enable(rx, false); + break; + default: + dev_err(component->dev, "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; + } + return ret; +} + +static bool rx_macro_adie_lb(struct snd_soc_component *component, + int interp_idx) +{ + u16 int_mux_cfg0, int_mux_cfg1; + u8 int_n_inp0, int_n_inp1, int_n_inp2; + + int_mux_cfg0 = CDC_RX_INP_MUX_RX_INT0_CFG0 + interp_idx * 8; + int_mux_cfg1 = int_mux_cfg0 + 4; + + int_n_inp0 = snd_soc_component_read_field(component, int_mux_cfg0, + CDC_RX_INTX_1_MIX_INP0_SEL_MASK); + int_n_inp1 = snd_soc_component_read_field(component, int_mux_cfg0, + CDC_RX_INTX_1_MIX_INP1_SEL_MASK); + int_n_inp2 = snd_soc_component_read_field(component, int_mux_cfg1, + CDC_RX_INTX_1_MIX_INP2_SEL_MASK); + + if (int_n_inp0 == INTn_1_INP_SEL_DEC0 || + int_n_inp0 == INTn_1_INP_SEL_DEC1 || + int_n_inp0 == INTn_1_INP_SEL_IIR0 || + int_n_inp0 == INTn_1_INP_SEL_IIR1) + return true; + + if (int_n_inp1 == INTn_1_INP_SEL_DEC0 || + int_n_inp1 == INTn_1_INP_SEL_DEC1 || + int_n_inp1 == INTn_1_INP_SEL_IIR0 || + int_n_inp1 == INTn_1_INP_SEL_IIR1) + return true; + + if (int_n_inp2 == INTn_1_INP_SEL_DEC0 || + int_n_inp2 == INTn_1_INP_SEL_DEC1 || + int_n_inp2 == INTn_1_INP_SEL_IIR0 || + int_n_inp2 == INTn_1_INP_SEL_IIR1) + return true; + + return false; +} + +static int rx_macro_enable_interp_clk(struct snd_soc_component *component, + int event, int interp_idx); +static int rx_macro_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg, reg; + + reg = CDC_RX_RXn_RX_PATH_CTL(w->shift); + gain_reg = CDC_RX_RXn_RX_VOL_CTL(w->shift); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rx_macro_enable_interp_clk(component, event, w->shift); + if (rx_macro_adie_lb(component, w->shift)) + snd_soc_component_update_bits(component, reg, + CDC_RX_PATH_CLK_EN_MASK, + CDC_RX_PATH_CLK_ENABLE); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(component, gain_reg, + snd_soc_component_read(component, gain_reg)); + break; + case SND_SOC_DAPM_POST_PMD: + rx_macro_enable_interp_clk(component, event, w->shift); + break; + } + + return 0; +} + +static int rx_macro_config_compander(struct snd_soc_component *component, + struct rx_macro *rx, + int comp, int event) +{ + u8 pcm_rate, val; + + /* AUX does not have compander */ + if (comp == INTERP_AUX) + return 0; + + pcm_rate = snd_soc_component_read(component, CDC_RX_RXn_RX_PATH_CTL(comp)) & 0x0F; + if (pcm_rate < 0x06) + val = 0x03; + else if (pcm_rate < 0x08) + val = 0x01; + else if (pcm_rate < 0x0B) + val = 0x02; + else + val = 0x00; + + if (SND_SOC_DAPM_EVENT_ON(event)) + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(comp), + CDC_RX_DC_COEFF_SEL_MASK, val); + + if (SND_SOC_DAPM_EVENT_OFF(event)) + snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(comp), + CDC_RX_DC_COEFF_SEL_MASK, 0x3); + if (!rx->comp_enabled[comp]) + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), + CDC_RX_COMPANDERn_CLK_EN_MASK, 0x1); + snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), + CDC_RX_COMPANDERn_SOFT_RST_MASK, 0x1); + snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), + CDC_RX_COMPANDERn_SOFT_RST_MASK, 0x0); + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(comp), + CDC_RX_RXn_COMP_EN_MASK, 0x1); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), + CDC_RX_COMPANDERn_HALT_MASK, 0x1); + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG0(comp), + CDC_RX_RXn_COMP_EN_MASK, 0x0); + snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), + CDC_RX_COMPANDERn_CLK_EN_MASK, 0x0); + snd_soc_component_write_field(component, CDC_RX_COMPANDERn_CTL0(comp), + CDC_RX_COMPANDERn_HALT_MASK, 0x0); + } + + return 0; +} + +static int rx_macro_load_compander_coeff(struct snd_soc_component *component, + struct rx_macro *rx, + int comp, int event) +{ + u16 comp_coeff_lsb_reg, comp_coeff_msb_reg; + int i; + int hph_pwr_mode; + + /* AUX does not have compander */ + if (comp == INTERP_AUX) + return 0; + + if (!rx->comp_enabled[comp]) + return 0; + + if (comp == INTERP_HPHL) { + comp_coeff_lsb_reg = CDC_RX_TOP_HPHL_COMP_WR_LSB; + comp_coeff_msb_reg = CDC_RX_TOP_HPHL_COMP_WR_MSB; + } else if (comp == INTERP_HPHR) { + comp_coeff_lsb_reg = CDC_RX_TOP_HPHR_COMP_WR_LSB; + comp_coeff_msb_reg = CDC_RX_TOP_HPHR_COMP_WR_MSB; + } else { + /* compander coefficients are loaded only for hph path */ + return 0; + } + + hph_pwr_mode = rx->hph_pwr_mode; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Load Compander Coeff */ + for (i = 0; i < COMP_MAX_COEFF; i++) { + snd_soc_component_write(component, comp_coeff_lsb_reg, + comp_coeff_table[hph_pwr_mode][i].lsb); + snd_soc_component_write(component, comp_coeff_msb_reg, + comp_coeff_table[hph_pwr_mode][i].msb); + } + } + + return 0; +} + +static void rx_macro_enable_softclip_clk(struct snd_soc_component *component, + struct rx_macro *rx, bool enable) +{ + if (enable) { + if (rx->softclip_clk_users == 0) + snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_CRC, + CDC_RX_SOFTCLIP_CLK_EN_MASK, 1); + rx->softclip_clk_users++; + } else { + rx->softclip_clk_users--; + if (rx->softclip_clk_users == 0) + snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_CRC, + CDC_RX_SOFTCLIP_CLK_EN_MASK, 0); + } +} + +static int rx_macro_config_softclip(struct snd_soc_component *component, + struct rx_macro *rx, int event) +{ + + if (!rx->is_softclip_on) + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Softclip clock */ + rx_macro_enable_softclip_clk(component, rx, true); + /* Enable Softclip control */ + snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_SOFTCLIP_CTRL, + CDC_RX_SOFTCLIP_EN_MASK, 0x01); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_write_field(component, CDC_RX_SOFTCLIP_SOFTCLIP_CTRL, + CDC_RX_SOFTCLIP_EN_MASK, 0x0); + rx_macro_enable_softclip_clk(component, rx, false); + } + + return 0; +} + +static int rx_macro_config_aux_hpf(struct snd_soc_component *component, + struct rx_macro *rx, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Update Aux HPF control */ + if (!rx->is_aux_hpf_on) + snd_soc_component_update_bits(component, + CDC_RX_RX2_RX_PATH_CFG1, 0x04, 0x00); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + /* Reset to default (HPF=ON) */ + snd_soc_component_update_bits(component, + CDC_RX_RX2_RX_PATH_CFG1, 0x04, 0x04); + } + + return 0; +} + +static inline void rx_macro_enable_clsh_block(struct rx_macro *rx, bool enable) +{ + if ((enable && ++rx->clsh_users == 1) || (!enable && --rx->clsh_users == 0)) + snd_soc_component_update_bits(rx->component, CDC_RX_CLSH_CRC, + CDC_RX_CLSH_CLK_EN_MASK, enable); + if (rx->clsh_users < 0) + rx->clsh_users = 0; +} + +static int rx_macro_config_classh(struct snd_soc_component *component, + struct rx_macro *rx, + int interp_n, int event) +{ + if (SND_SOC_DAPM_EVENT_OFF(event)) { + rx_macro_enable_clsh_block(rx, false); + return 0; + } + + if (!SND_SOC_DAPM_EVENT_ON(event)) + return 0; + + rx_macro_enable_clsh_block(rx, true); + if (interp_n == INTERP_HPHL || + interp_n == INTERP_HPHR) { + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_component_write(component, CDC_RX_CLSH_K1_LSB, 0xc0); + snd_soc_component_write_field(component, CDC_RX_CLSH_K1_MSB, + CDC_RX_CLSH_K1_MSB_COEFF_MASK, 0); + } + switch (interp_n) { + case INTERP_HPHL: + if (rx->is_ear_mode_on) + snd_soc_component_update_bits(component, + CDC_RX_CLSH_HPH_V_PA, + CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x39); + else + snd_soc_component_update_bits(component, + CDC_RX_CLSH_HPH_V_PA, + CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x1c); + snd_soc_component_update_bits(component, + CDC_RX_CLSH_DECAY_CTRL, + CDC_RX_CLSH_DECAY_RATE_MASK, 0x0); + snd_soc_component_write_field(component, + CDC_RX_RX0_RX_PATH_CFG0, + CDC_RX_RXn_CLSH_EN_MASK, 0x1); + break; + case INTERP_HPHR: + if (rx->is_ear_mode_on) + snd_soc_component_update_bits(component, + CDC_RX_CLSH_HPH_V_PA, + CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x39); + else + snd_soc_component_update_bits(component, + CDC_RX_CLSH_HPH_V_PA, + CDC_RX_CLSH_HPH_V_PA_MIN_MASK, 0x1c); + snd_soc_component_update_bits(component, + CDC_RX_CLSH_DECAY_CTRL, + CDC_RX_CLSH_DECAY_RATE_MASK, 0x0); + snd_soc_component_write_field(component, + CDC_RX_RX1_RX_PATH_CFG0, + CDC_RX_RXn_CLSH_EN_MASK, 0x1); + break; + case INTERP_AUX: + snd_soc_component_update_bits(component, + CDC_RX_RX2_RX_PATH_CFG0, + CDC_RX_RX2_DLY_Z_EN_MASK, 1); + snd_soc_component_write_field(component, + CDC_RX_RX2_RX_PATH_CFG0, + CDC_RX_RX2_CLSH_EN_MASK, 1); + break; + } + + return 0; +} + +static void rx_macro_hd2_control(struct snd_soc_component *component, + u16 interp_idx, int event) +{ + u16 hd2_scale_reg, hd2_enable_reg; + + switch (interp_idx) { + case INTERP_HPHL: + hd2_scale_reg = CDC_RX_RX0_RX_PATH_SEC3; + hd2_enable_reg = CDC_RX_RX0_RX_PATH_CFG0; + break; + case INTERP_HPHR: + hd2_scale_reg = CDC_RX_RX1_RX_PATH_SEC3; + hd2_enable_reg = CDC_RX_RX1_RX_PATH_CFG0; + break; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(component, hd2_scale_reg, + CDC_RX_RXn_HD2_ALPHA_MASK, 0x14); + snd_soc_component_write_field(component, hd2_enable_reg, + CDC_RX_RXn_HD2_EN_MASK, 1); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_write_field(component, hd2_enable_reg, + CDC_RX_RXn_HD2_EN_MASK, 0); + snd_soc_component_update_bits(component, hd2_scale_reg, + CDC_RX_RXn_HD2_ALPHA_MASK, 0x0); + } +} + +static int rx_macro_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rx->comp_enabled[comp]; + return 0; +} + +static int rx_macro_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + rx->comp_enabled[comp] = value; + + return 0; +} + +static int rx_macro_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = + rx->rx_port_value[widget->shift]; + return 0; +} + +static int rx_macro_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 rx_port_value = ucontrol->value.enumerated.item[0]; + u32 aif_rst; + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + aif_rst = rx->rx_port_value[widget->shift]; + if (!rx_port_value) { + if (aif_rst == 0) + return 0; + if (aif_rst > RX_MACRO_AIF4_PB) { + dev_err(component->dev, "%s: Invalid AIF reset\n", __func__); + return 0; + } + } + rx->rx_port_value[widget->shift] = rx_port_value; + + switch (rx_port_value) { + case 0: + if (rx->active_ch_cnt[aif_rst]) { + clear_bit(widget->shift, + &rx->active_ch_mask[aif_rst]); + rx->active_ch_cnt[aif_rst]--; + } + break; + case 1: + case 2: + case 3: + case 4: + set_bit(widget->shift, + &rx->active_ch_mask[rx_port_value]); + rx->active_ch_cnt[rx_port_value]++; + break; + default: + dev_err(component->dev, + "%s:Invalid AIF_ID for RX_MACRO MUX %d\n", + __func__, rx_port_value); + goto err; + } + + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + return 0; +err: + return -EINVAL; +} + +static const struct snd_kcontrol_new rx_macro_rx0_mux = + SOC_DAPM_ENUM_EXT("rx_macro_rx0", rx_macro_rx0_enum, + rx_macro_mux_get, rx_macro_mux_put); +static const struct snd_kcontrol_new rx_macro_rx1_mux = + SOC_DAPM_ENUM_EXT("rx_macro_rx1", rx_macro_rx1_enum, + rx_macro_mux_get, rx_macro_mux_put); +static const struct snd_kcontrol_new rx_macro_rx2_mux = + SOC_DAPM_ENUM_EXT("rx_macro_rx2", rx_macro_rx2_enum, + rx_macro_mux_get, rx_macro_mux_put); +static const struct snd_kcontrol_new rx_macro_rx3_mux = + SOC_DAPM_ENUM_EXT("rx_macro_rx3", rx_macro_rx3_enum, + rx_macro_mux_get, rx_macro_mux_put); +static const struct snd_kcontrol_new rx_macro_rx4_mux = + SOC_DAPM_ENUM_EXT("rx_macro_rx4", rx_macro_rx4_enum, + rx_macro_mux_get, rx_macro_mux_put); +static const struct snd_kcontrol_new rx_macro_rx5_mux = + SOC_DAPM_ENUM_EXT("rx_macro_rx5", rx_macro_rx5_enum, + rx_macro_mux_get, rx_macro_mux_put); + +static int rx_macro_get_ear_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rx->is_ear_mode_on; + return 0; +} + +static int rx_macro_put_ear_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + rx->is_ear_mode_on = (!ucontrol->value.integer.value[0] ? false : true); + return 0; +} + +static int rx_macro_get_hph_hd2_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rx->hph_hd2_mode; + return 0; +} + +static int rx_macro_put_hph_hd2_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + rx->hph_hd2_mode = ucontrol->value.integer.value[0]; + return 0; +} + +static int rx_macro_get_hph_pwr_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = rx->hph_pwr_mode; + return 0; +} + +static int rx_macro_put_hph_pwr_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + rx->hph_pwr_mode = ucontrol->value.enumerated.item[0]; + return 0; +} + +static int rx_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rx->is_softclip_on; + + return 0; +} + +static int rx_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + rx->is_softclip_on = ucontrol->value.integer.value[0]; + + return 0; +} + +static int rx_macro_aux_hpf_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rx->is_aux_hpf_on; + + return 0; +} + +static int rx_macro_aux_hpf_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + rx->is_aux_hpf_on = ucontrol->value.integer.value[0]; + + return 0; +} + +static int rx_macro_hphdelay_lutbypass(struct snd_soc_component *component, + struct rx_macro *rx, + u16 interp_idx, int event) +{ + u16 hph_lut_bypass_reg; + u16 hph_comp_ctrl7; + + switch (interp_idx) { + case INTERP_HPHL: + hph_lut_bypass_reg = CDC_RX_TOP_HPHL_COMP_LUT; + hph_comp_ctrl7 = CDC_RX_COMPANDER0_CTL7; + break; + case INTERP_HPHR: + hph_lut_bypass_reg = CDC_RX_TOP_HPHR_COMP_LUT; + hph_comp_ctrl7 = CDC_RX_COMPANDER1_CTL7; + break; + default: + return -EINVAL; + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) { + if (interp_idx == INTERP_HPHL) { + if (rx->is_ear_mode_on) + snd_soc_component_write_field(component, + CDC_RX_RX0_RX_PATH_CFG1, + CDC_RX_RX0_HPH_L_EAR_SEL_MASK, 0x1); + else + snd_soc_component_write_field(component, + hph_lut_bypass_reg, + CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 1); + } else { + snd_soc_component_write_field(component, hph_lut_bypass_reg, + CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 1); + } + if (rx->hph_pwr_mode) + snd_soc_component_write_field(component, hph_comp_ctrl7, + CDC_RX_COMPANDER1_HPH_LOW_PWR_MODE_MASK, 0x0); + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_write_field(component, + CDC_RX_RX0_RX_PATH_CFG1, + CDC_RX_RX0_HPH_L_EAR_SEL_MASK, 0x0); + snd_soc_component_update_bits(component, hph_lut_bypass_reg, + CDC_RX_TOP_HPH_LUT_BYPASS_MASK, 0); + snd_soc_component_write_field(component, hph_comp_ctrl7, + CDC_RX_COMPANDER1_HPH_LOW_PWR_MODE_MASK, 0x1); + } + + return 0; +} + +static int rx_macro_enable_interp_clk(struct snd_soc_component *component, + int event, int interp_idx) +{ + u16 main_reg, dsm_reg, rx_cfg2_reg; + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + main_reg = CDC_RX_RXn_RX_PATH_CTL(interp_idx); + dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(interp_idx); + if (interp_idx == INTERP_AUX) + dsm_reg = CDC_RX_RX2_RX_PATH_DSM_CTL; + rx_cfg2_reg = CDC_RX_RXn_RX_PATH_CFG2(interp_idx); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (rx->main_clk_users[interp_idx] == 0) { + /* Main path PGA mute enable */ + snd_soc_component_write_field(component, main_reg, + CDC_RX_PATH_PGA_MUTE_MASK, 0x1); + snd_soc_component_write_field(component, dsm_reg, + CDC_RX_RXn_DSM_CLK_EN_MASK, 0x1); + snd_soc_component_update_bits(component, rx_cfg2_reg, + CDC_RX_RXn_HPF_CUT_FREQ_MASK, 0x03); + rx_macro_load_compander_coeff(component, rx, interp_idx, event); + if (rx->hph_hd2_mode) + rx_macro_hd2_control(component, interp_idx, event); + rx_macro_hphdelay_lutbypass(component, rx, interp_idx, event); + rx_macro_config_compander(component, rx, interp_idx, event); + if (interp_idx == INTERP_AUX) { + rx_macro_config_softclip(component, rx, event); + rx_macro_config_aux_hpf(component, rx, event); + } + rx_macro_config_classh(component, rx, interp_idx, event); + } + rx->main_clk_users[interp_idx]++; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + rx->main_clk_users[interp_idx]--; + if (rx->main_clk_users[interp_idx] <= 0) { + rx->main_clk_users[interp_idx] = 0; + /* Main path PGA mute enable */ + snd_soc_component_write_field(component, main_reg, + CDC_RX_PATH_PGA_MUTE_MASK, 0x1); + /* Clk Disable */ + snd_soc_component_write_field(component, dsm_reg, + CDC_RX_RXn_DSM_CLK_EN_MASK, 0); + snd_soc_component_write_field(component, main_reg, + CDC_RX_PATH_CLK_EN_MASK, 0); + /* Reset enable and disable */ + snd_soc_component_write_field(component, main_reg, + CDC_RX_PATH_RESET_EN_MASK, 1); + snd_soc_component_write_field(component, main_reg, + CDC_RX_PATH_RESET_EN_MASK, 0); + /* Reset rate to 48K*/ + snd_soc_component_update_bits(component, main_reg, + CDC_RX_PATH_PCM_RATE_MASK, + 0x04); + snd_soc_component_update_bits(component, rx_cfg2_reg, + CDC_RX_RXn_HPF_CUT_FREQ_MASK, 0x00); + rx_macro_config_classh(component, rx, interp_idx, event); + rx_macro_config_compander(component, rx, interp_idx, event); + if (interp_idx == INTERP_AUX) { + rx_macro_config_softclip(component, rx, event); + rx_macro_config_aux_hpf(component, rx, event); + } + rx_macro_hphdelay_lutbypass(component, rx, interp_idx, event); + if (rx->hph_hd2_mode) + rx_macro_hd2_control(component, interp_idx, event); + } + } + + return rx->main_clk_users[interp_idx]; +} + +static int rx_macro_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg, mix_reg; + + gain_reg = CDC_RX_RXn_RX_VOL_MIX_CTL(w->shift); + mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(w->shift); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rx_macro_enable_interp_clk(component, event, w->shift); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(component, gain_reg, + snd_soc_component_read(component, gain_reg)); + break; + case SND_SOC_DAPM_POST_PMD: + /* Clk Disable */ + snd_soc_component_update_bits(component, mix_reg, + CDC_RX_RXn_MIX_CLK_EN_MASK, 0x00); + rx_macro_enable_interp_clk(component, event, w->shift); + /* Reset enable and disable */ + snd_soc_component_update_bits(component, mix_reg, + CDC_RX_RXn_MIX_RESET_MASK, + CDC_RX_RXn_MIX_RESET); + snd_soc_component_update_bits(component, mix_reg, + CDC_RX_RXn_MIX_RESET_MASK, 0x00); + break; + } + + return 0; +} + +static int rx_macro_enable_rx_path_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rx_macro_enable_interp_clk(component, event, w->shift); + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(w->shift), + CDC_RX_RXn_SIDETONE_EN_MASK, 1); + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CTL(w->shift), + CDC_RX_PATH_CLK_EN_MASK, 1); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CFG1(w->shift), + CDC_RX_RXn_SIDETONE_EN_MASK, 0); + rx_macro_enable_interp_clk(component, event, w->shift); + break; + default: + break; + } + return 0; +} + +static int rx_macro_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR0", sizeof("IIR0"))) { + snd_soc_component_write(component, + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_component_read(component, + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_component_write(component, + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_component_read(component, + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_component_write(component, + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_component_read(component, + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_component_write(component, + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_component_read(component, + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } else { + snd_soc_component_write(component, + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + snd_soc_component_read(component, + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL)); + snd_soc_component_write(component, + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, + snd_soc_component_read(component, + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL)); + snd_soc_component_write(component, + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, + snd_soc_component_read(component, + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL)); + snd_soc_component_write(component, + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, + snd_soc_component_read(component, + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL)); + } + break; + } + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_component *component, + int iir_idx, int band_idx, int coeff_idx) +{ + u32 value; + int reg, b2_reg; + + /* Address does not automatically update if reading */ + reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx; + b2_reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx; + + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) * + sizeof(uint32_t)) & 0x7F); + + value = snd_soc_component_read(component, b2_reg); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_component_read(component, b2_reg) << 8); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_component_read(component, b2_reg) << 16); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= (snd_soc_component_read(component, b2_reg) << 24); + return value; +} + +static void set_iir_band_coeff(struct snd_soc_component *component, + int iir_idx, int band_idx, uint32_t value) +{ + int reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B2_CTL + 0x80 * iir_idx; + + snd_soc_component_write(component, reg, (value & 0xFF)); + snd_soc_component_write(component, reg, (value >> 8) & 0xFF); + snd_soc_component_write(component, reg, (value >> 16) & 0xFF); + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_component_write(component, reg, (value >> 24) & 0x3F); +} + +static int rx_macro_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + int iir_idx = ctl->iir_idx; + int band_idx = ctl->band_idx; + u32 coeff[BAND_MAX]; + int reg = CDC_RX_SIDETONE_IIR0_IIR_COEF_B1_CTL + 0x80 * iir_idx; + + memcpy(&coeff[0], ucontrol->value.bytes.data, params->max); + + /* Mask top bit it is reserved */ + /* Updates addr automatically for each B2 write */ + snd_soc_component_write(component, reg, (band_idx * BAND_MAX * + sizeof(uint32_t)) & 0x7F); + + set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]); + + return 0; +} + +static int rx_macro_get_iir_band_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + int iir_idx = ctl->iir_idx; + int band_idx = ctl->band_idx; + u32 coeff[BAND_MAX]; + + coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, 0); + coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, 1); + coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, 2); + coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, 3); + coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, 4); + + memcpy(ucontrol->value.bytes.data, &coeff[0], params->max); + + return 0; +} + +static int rx_macro_iir_filter_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} + +static const struct snd_kcontrol_new rx_macro_snd_controls[] = { + SOC_SINGLE_S8_TLV("RX_RX0 Digital Volume", CDC_RX_RX0_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX1 Digital Volume", CDC_RX_RX1_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX2 Digital Volume", CDC_RX_RX2_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX0 Mix Digital Volume", CDC_RX_RX0_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX1 Mix Digital Volume", CDC_RX_RX1_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX_RX2 Mix Digital Volume", CDC_RX_RX2_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + + SOC_SINGLE_EXT("RX_COMP1 Switch", SND_SOC_NOPM, RX_MACRO_COMP1, 1, 0, + rx_macro_get_compander, rx_macro_set_compander), + SOC_SINGLE_EXT("RX_COMP2 Switch", SND_SOC_NOPM, RX_MACRO_COMP2, 1, 0, + rx_macro_get_compander, rx_macro_set_compander), + + SOC_SINGLE_EXT("RX_EAR Mode Switch", SND_SOC_NOPM, 0, 1, 0, + rx_macro_get_ear_mode, rx_macro_put_ear_mode), + + SOC_SINGLE_EXT("RX_HPH HD2 Mode Switch", SND_SOC_NOPM, 0, 1, 0, + rx_macro_get_hph_hd2_mode, rx_macro_put_hph_hd2_mode), + + SOC_ENUM_EXT("RX_HPH PWR Mode", rx_macro_hph_pwr_mode_enum, + rx_macro_get_hph_pwr_mode, rx_macro_put_hph_pwr_mode), + + SOC_SINGLE_EXT("RX_Softclip Switch", SND_SOC_NOPM, 0, 1, 0, + rx_macro_soft_clip_enable_get, + rx_macro_soft_clip_enable_put), + SOC_SINGLE_EXT("AUX_HPF Switch", SND_SOC_NOPM, 0, 1, 0, + rx_macro_aux_hpf_mode_get, + rx_macro_aux_hpf_mode_put), + + SOC_SINGLE_S8_TLV("IIR0 INP0 Volume", + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B1_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR0 INP1 Volume", + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B2_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR0 INP2 Volume", + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B3_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR0 INP3 Volume", + CDC_RX_SIDETONE_IIR0_IIR_GAIN_B4_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP0 Volume", + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B1_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP1 Volume", + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B2_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP2 Volume", + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B3_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP3 Volume", + CDC_RX_SIDETONE_IIR1_IIR_GAIN_B4_CTL, -84, 40, + digital_gain), + + SOC_SINGLE("IIR1 Band1 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL, + 0, 1, 0), + SOC_SINGLE("IIR1 Band2 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL, + 1, 1, 0), + SOC_SINGLE("IIR1 Band3 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL, + 2, 1, 0), + SOC_SINGLE("IIR1 Band4 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL, + 3, 1, 0), + SOC_SINGLE("IIR1 Band5 Switch", CDC_RX_SIDETONE_IIR0_IIR_CTL, + 4, 1, 0), + SOC_SINGLE("IIR2 Band1 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL, + 0, 1, 0), + SOC_SINGLE("IIR2 Band2 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL, + 1, 1, 0), + SOC_SINGLE("IIR2 Band3 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL, + 2, 1, 0), + SOC_SINGLE("IIR2 Band4 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL, + 3, 1, 0), + SOC_SINGLE("IIR2 Band5 Switch", CDC_RX_SIDETONE_IIR1_IIR_CTL, + 4, 1, 0), + + RX_MACRO_IIR_FILTER_CTL("IIR0 Band1", IIR0, BAND1), + RX_MACRO_IIR_FILTER_CTL("IIR0 Band2", IIR0, BAND2), + RX_MACRO_IIR_FILTER_CTL("IIR0 Band3", IIR0, BAND3), + RX_MACRO_IIR_FILTER_CTL("IIR0 Band4", IIR0, BAND4), + RX_MACRO_IIR_FILTER_CTL("IIR0 Band5", IIR0, BAND5), + + RX_MACRO_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1), + RX_MACRO_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2), + RX_MACRO_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3), + RX_MACRO_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4), + RX_MACRO_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5), + +}; + +static int rx_macro_enable_echo(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 val, ec_hq_reg; + int ec_tx = -1; + + val = snd_soc_component_read(component, + CDC_RX_INP_MUX_RX_MIX_CFG4); + if (!(strcmp(w->name, "RX MIX TX0 MUX"))) + ec_tx = ((val & 0xf0) >> 0x4) - 1; + else if (!(strcmp(w->name, "RX MIX TX1 MUX"))) + ec_tx = (val & 0x0f) - 1; + + val = snd_soc_component_read(component, + CDC_RX_INP_MUX_RX_MIX_CFG5); + if (!(strcmp(w->name, "RX MIX TX2 MUX"))) + ec_tx = (val & 0x0f) - 1; + + if (ec_tx < 0 || (ec_tx >= RX_MACRO_EC_MUX_MAX)) { + dev_err(component->dev, "%s: EC mix control not set correctly\n", + __func__); + return -EINVAL; + } + ec_hq_reg = CDC_RX_EC_REF_HQ0_EC_REF_HQ_PATH_CTL + + 0x40 * ec_tx; + snd_soc_component_update_bits(component, ec_hq_reg, 0x01, 0x01); + ec_hq_reg = CDC_RX_EC_REF_HQ0_EC_REF_HQ_CFG0 + + 0x40 * ec_tx; + /* default set to 48k */ + snd_soc_component_update_bits(component, ec_hq_reg, 0x1E, 0x08); + + return 0; +} + +static const struct snd_soc_dapm_widget rx_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("RX AIF1 PB", "RX_MACRO_AIF1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("RX AIF2 PB", "RX_MACRO_AIF2 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("RX AIF3 PB", "RX_MACRO_AIF3 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("RX AIF4 PB", "RX_MACRO_AIF4 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("RX AIF_ECHO", "RX_AIF_ECHO Capture", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("RX_MACRO RX0 MUX", SND_SOC_NOPM, RX_MACRO_RX0, 0, + &rx_macro_rx0_mux), + SND_SOC_DAPM_MUX("RX_MACRO RX1 MUX", SND_SOC_NOPM, RX_MACRO_RX1, 0, + &rx_macro_rx1_mux), + SND_SOC_DAPM_MUX("RX_MACRO RX2 MUX", SND_SOC_NOPM, RX_MACRO_RX2, 0, + &rx_macro_rx2_mux), + SND_SOC_DAPM_MUX("RX_MACRO RX3 MUX", SND_SOC_NOPM, RX_MACRO_RX3, 0, + &rx_macro_rx3_mux), + SND_SOC_DAPM_MUX("RX_MACRO RX4 MUX", SND_SOC_NOPM, RX_MACRO_RX4, 0, + &rx_macro_rx4_mux), + SND_SOC_DAPM_MUX("RX_MACRO RX5 MUX", SND_SOC_NOPM, RX_MACRO_RX5, 0, + &rx_macro_rx5_mux), + + SND_SOC_DAPM_MIXER("RX_RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX_RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux), + SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux), + SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux), + SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux), + SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux), + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux), + SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux), + + SND_SOC_DAPM_MUX_E("RX MIX TX0 MUX", SND_SOC_NOPM, + RX_MACRO_EC0_MUX, 0, + &rx_mix_tx0_mux, rx_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX MIX TX1 MUX", SND_SOC_NOPM, + RX_MACRO_EC1_MUX, 0, + &rx_mix_tx1_mux, rx_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX MIX TX2 MUX", SND_SOC_NOPM, + RX_MACRO_EC2_MUX, 0, + &rx_mix_tx2_mux, rx_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("IIR0", CDC_RX_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, rx_macro_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("IIR1", CDC_RX_SIDETONE_IIR1_IIR_PATH_CTL, + 4, 0, NULL, 0, rx_macro_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("SRC0", CDC_RX_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", CDC_RX_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + + SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int0_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int1_dem_inp_mux), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int0_2_mux, rx_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int1_2_mux, rx_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_AUX, 0, + &rx_int2_2_mux, rx_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, &rx_int0_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx_int0_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx_int0_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, &rx_int1_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx_int1_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx_int1_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, &rx_int2_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, &rx_int2_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, &rx_int2_1_mix_inp2_mux), + + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int0_1_interp_mux, rx_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int1_1_interp_mux, rx_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_AUX, 0, + &rx_int2_1_interp_mux, rx_macro_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int0_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT1_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int1_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT2_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int2_2_interp_mux), + + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_HPHL, + 0, &rx_int0_mix2_inp_mux, rx_macro_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", SND_SOC_NOPM, INTERP_HPHR, + 0, &rx_int1_mix2_inp_mux, rx_macro_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", SND_SOC_NOPM, INTERP_AUX, + 0, &rx_int2_mix2_inp_mux, rx_macro_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPHL_OUT"), + SND_SOC_DAPM_OUTPUT("HPHR_OUT"), + SND_SOC_DAPM_OUTPUT("AUX_OUT"), + + SND_SOC_DAPM_INPUT("RX_TX DEC0_INP"), + SND_SOC_DAPM_INPUT("RX_TX DEC1_INP"), + SND_SOC_DAPM_INPUT("RX_TX DEC2_INP"), + SND_SOC_DAPM_INPUT("RX_TX DEC3_INP"), + + SND_SOC_DAPM_SUPPLY_S("RX_MCLK", 0, SND_SOC_NOPM, 0, 0, + rx_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route rx_audio_map[] = { + {"RX AIF1 PB", NULL, "RX_MCLK"}, + {"RX AIF2 PB", NULL, "RX_MCLK"}, + {"RX AIF3 PB", NULL, "RX_MCLK"}, + {"RX AIF4 PB", NULL, "RX_MCLK"}, + + {"RX_MACRO RX0 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX1 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX2 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX3 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX4 MUX", "AIF1_PB", "RX AIF1 PB"}, + {"RX_MACRO RX5 MUX", "AIF1_PB", "RX AIF1 PB"}, + + {"RX_MACRO RX0 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX1 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX2 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX3 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX4 MUX", "AIF2_PB", "RX AIF2 PB"}, + {"RX_MACRO RX5 MUX", "AIF2_PB", "RX AIF2 PB"}, + + {"RX_MACRO RX0 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX1 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX2 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX3 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX4 MUX", "AIF3_PB", "RX AIF3 PB"}, + {"RX_MACRO RX5 MUX", "AIF3_PB", "RX AIF3 PB"}, + + {"RX_MACRO RX0 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX1 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX2 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX3 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX4 MUX", "AIF4_PB", "RX AIF4 PB"}, + {"RX_MACRO RX5 MUX", "AIF4_PB", "RX AIF4 PB"}, + + {"RX_RX0", NULL, "RX_MACRO RX0 MUX"}, + {"RX_RX1", NULL, "RX_MACRO RX1 MUX"}, + {"RX_RX2", NULL, "RX_MACRO RX2 MUX"}, + {"RX_RX3", NULL, "RX_MACRO RX3 MUX"}, + {"RX_RX4", NULL, "RX_MACRO RX4 MUX"}, + {"RX_RX5", NULL, "RX_MACRO RX5 MUX"}, + + {"RX INT0_1 MIX1 INP0", "RX0", "RX_RX0"}, + {"RX INT0_1 MIX1 INP0", "RX1", "RX_RX1"}, + {"RX INT0_1 MIX1 INP0", "RX2", "RX_RX2"}, + {"RX INT0_1 MIX1 INP0", "RX3", "RX_RX3"}, + {"RX INT0_1 MIX1 INP0", "RX4", "RX_RX4"}, + {"RX INT0_1 MIX1 INP0", "RX5", "RX_RX5"}, + {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP0", "DEC0", "RX_TX DEC0_INP"}, + {"RX INT0_1 MIX1 INP0", "DEC1", "RX_TX DEC1_INP"}, + {"RX INT0_1 MIX1 INP1", "RX0", "RX_RX0"}, + {"RX INT0_1 MIX1 INP1", "RX1", "RX_RX1"}, + {"RX INT0_1 MIX1 INP1", "RX2", "RX_RX2"}, + {"RX INT0_1 MIX1 INP1", "RX3", "RX_RX3"}, + {"RX INT0_1 MIX1 INP1", "RX4", "RX_RX4"}, + {"RX INT0_1 MIX1 INP1", "RX5", "RX_RX5"}, + {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP1", "DEC0", "RX_TX DEC0_INP"}, + {"RX INT0_1 MIX1 INP1", "DEC1", "RX_TX DEC1_INP"}, + {"RX INT0_1 MIX1 INP2", "RX0", "RX_RX0"}, + {"RX INT0_1 MIX1 INP2", "RX1", "RX_RX1"}, + {"RX INT0_1 MIX1 INP2", "RX2", "RX_RX2"}, + {"RX INT0_1 MIX1 INP2", "RX3", "RX_RX3"}, + {"RX INT0_1 MIX1 INP2", "RX4", "RX_RX4"}, + {"RX INT0_1 MIX1 INP2", "RX5", "RX_RX5"}, + {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP2", "DEC0", "RX_TX DEC0_INP"}, + {"RX INT0_1 MIX1 INP2", "DEC1", "RX_TX DEC1_INP"}, + + {"RX INT1_1 MIX1 INP0", "RX0", "RX_RX0"}, + {"RX INT1_1 MIX1 INP0", "RX1", "RX_RX1"}, + {"RX INT1_1 MIX1 INP0", "RX2", "RX_RX2"}, + {"RX INT1_1 MIX1 INP0", "RX3", "RX_RX3"}, + {"RX INT1_1 MIX1 INP0", "RX4", "RX_RX4"}, + {"RX INT1_1 MIX1 INP0", "RX5", "RX_RX5"}, + {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP0", "DEC0", "RX_TX DEC0_INP"}, + {"RX INT1_1 MIX1 INP0", "DEC1", "RX_TX DEC1_INP"}, + {"RX INT1_1 MIX1 INP1", "RX0", "RX_RX0"}, + {"RX INT1_1 MIX1 INP1", "RX1", "RX_RX1"}, + {"RX INT1_1 MIX1 INP1", "RX2", "RX_RX2"}, + {"RX INT1_1 MIX1 INP1", "RX3", "RX_RX3"}, + {"RX INT1_1 MIX1 INP1", "RX4", "RX_RX4"}, + {"RX INT1_1 MIX1 INP1", "RX5", "RX_RX5"}, + {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP1", "DEC0", "RX_TX DEC0_INP"}, + {"RX INT1_1 MIX1 INP1", "DEC1", "RX_TX DEC1_INP"}, + {"RX INT1_1 MIX1 INP2", "RX0", "RX_RX0"}, + {"RX INT1_1 MIX1 INP2", "RX1", "RX_RX1"}, + {"RX INT1_1 MIX1 INP2", "RX2", "RX_RX2"}, + {"RX INT1_1 MIX1 INP2", "RX3", "RX_RX3"}, + {"RX INT1_1 MIX1 INP2", "RX4", "RX_RX4"}, + {"RX INT1_1 MIX1 INP2", "RX5", "RX_RX5"}, + {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP2", "DEC0", "RX_TX DEC0_INP"}, + {"RX INT1_1 MIX1 INP2", "DEC1", "RX_TX DEC1_INP"}, + + {"RX INT2_1 MIX1 INP0", "RX0", "RX_RX0"}, + {"RX INT2_1 MIX1 INP0", "RX1", "RX_RX1"}, + {"RX INT2_1 MIX1 INP0", "RX2", "RX_RX2"}, + {"RX INT2_1 MIX1 INP0", "RX3", "RX_RX3"}, + {"RX INT2_1 MIX1 INP0", "RX4", "RX_RX4"}, + {"RX INT2_1 MIX1 INP0", "RX5", "RX_RX5"}, + {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP0", "DEC0", "RX_TX DEC0_INP"}, + {"RX INT2_1 MIX1 INP0", "DEC1", "RX_TX DEC1_INP"}, + {"RX INT2_1 MIX1 INP1", "RX0", "RX_RX0"}, + {"RX INT2_1 MIX1 INP1", "RX1", "RX_RX1"}, + {"RX INT2_1 MIX1 INP1", "RX2", "RX_RX2"}, + {"RX INT2_1 MIX1 INP1", "RX3", "RX_RX3"}, + {"RX INT2_1 MIX1 INP1", "RX4", "RX_RX4"}, + {"RX INT2_1 MIX1 INP1", "RX5", "RX_RX5"}, + {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP1", "DEC0", "RX_TX DEC0_INP"}, + {"RX INT2_1 MIX1 INP1", "DEC1", "RX_TX DEC1_INP"}, + {"RX INT2_1 MIX1 INP2", "RX0", "RX_RX0"}, + {"RX INT2_1 MIX1 INP2", "RX1", "RX_RX1"}, + {"RX INT2_1 MIX1 INP2", "RX2", "RX_RX2"}, + {"RX INT2_1 MIX1 INP2", "RX3", "RX_RX3"}, + {"RX INT2_1 MIX1 INP2", "RX4", "RX_RX4"}, + {"RX INT2_1 MIX1 INP2", "RX5", "RX_RX5"}, + {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP2", "DEC0", "RX_TX DEC0_INP"}, + {"RX INT2_1 MIX1 INP2", "DEC1", "RX_TX DEC1_INP"}, + + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"}, + + {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX AIF_ECHO", NULL, "RX MIX TX0 MUX"}, + {"RX AIF_ECHO", NULL, "RX MIX TX1 MUX"}, + {"RX AIF_ECHO", NULL, "RX MIX TX2 MUX"}, + {"RX AIF_ECHO", NULL, "RX_MCLK"}, + + /* Mixing path INT0 */ + {"RX INT0_2 MUX", "RX0", "RX_RX0"}, + {"RX INT0_2 MUX", "RX1", "RX_RX1"}, + {"RX INT0_2 MUX", "RX2", "RX_RX2"}, + {"RX INT0_2 MUX", "RX3", "RX_RX3"}, + {"RX INT0_2 MUX", "RX4", "RX_RX4"}, + {"RX INT0_2 MUX", "RX5", "RX_RX5"}, + {"RX INT0_2 INTERP", NULL, "RX INT0_2 MUX"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_2 INTERP"}, + + /* Mixing path INT1 */ + {"RX INT1_2 MUX", "RX0", "RX_RX0"}, + {"RX INT1_2 MUX", "RX1", "RX_RX1"}, + {"RX INT1_2 MUX", "RX2", "RX_RX2"}, + {"RX INT1_2 MUX", "RX3", "RX_RX3"}, + {"RX INT1_2 MUX", "RX4", "RX_RX4"}, + {"RX INT1_2 MUX", "RX5", "RX_RX5"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"}, + + /* Mixing path INT2 */ + {"RX INT2_2 MUX", "RX0", "RX_RX0"}, + {"RX INT2_2 MUX", "RX1", "RX_RX1"}, + {"RX INT2_2 MUX", "RX2", "RX_RX2"}, + {"RX INT2_2 MUX", "RX3", "RX_RX3"}, + {"RX INT2_2 MUX", "RX4", "RX_RX4"}, + {"RX INT2_2 MUX", "RX5", "RX_RX5"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"}, + + {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"}, + {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, + {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"}, + {"HPHL_OUT", NULL, "RX INT0 DEM MUX"}, + {"HPHL_OUT", NULL, "RX_MCLK"}, + + {"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"}, + {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX2"}, + {"HPHR_OUT", NULL, "RX INT1 DEM MUX"}, + {"HPHR_OUT", NULL, "RX_MCLK"}, + + {"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"}, + + {"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"}, + {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"}, + {"AUX_OUT", NULL, "RX INT2 MIX2"}, + {"AUX_OUT", NULL, "RX_MCLK"}, + + {"IIR0", NULL, "RX_MCLK"}, + {"IIR0", NULL, "IIR0 INP0 MUX"}, + {"IIR0 INP0 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP0 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP0 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP0 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP0 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP0 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP0 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP0 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP0 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP0 MUX", "RX5", "RX_RX5"}, + {"IIR0", NULL, "IIR0 INP1 MUX"}, + {"IIR0 INP1 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP1 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP1 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP1 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP1 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP1 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP1 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP1 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP1 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP1 MUX", "RX5", "RX_RX5"}, + {"IIR0", NULL, "IIR0 INP2 MUX"}, + {"IIR0 INP2 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP2 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP2 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP2 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP2 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP2 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP2 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP2 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP2 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP2 MUX", "RX5", "RX_RX5"}, + {"IIR0", NULL, "IIR0 INP3 MUX"}, + {"IIR0 INP3 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR0 INP3 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR0 INP3 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR0 INP3 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR0 INP3 MUX", "RX0", "RX_RX0"}, + {"IIR0 INP3 MUX", "RX1", "RX_RX1"}, + {"IIR0 INP3 MUX", "RX2", "RX_RX2"}, + {"IIR0 INP3 MUX", "RX3", "RX_RX3"}, + {"IIR0 INP3 MUX", "RX4", "RX_RX4"}, + {"IIR0 INP3 MUX", "RX5", "RX_RX5"}, + + {"IIR1", NULL, "RX_MCLK"}, + {"IIR1", NULL, "IIR1 INP0 MUX"}, + {"IIR1 INP0 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP0 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP0 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP0 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP0 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP0 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP0 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP0 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP0 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP0 MUX", "RX5", "RX_RX5"}, + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP1 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP1 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP1 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP1 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP1 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP1 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP1 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP1 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP1 MUX", "RX5", "RX_RX5"}, + {"IIR1", NULL, "IIR1 INP2 MUX"}, + {"IIR1 INP2 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP2 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP2 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP2 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP2 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP2 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP2 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP2 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP2 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP2 MUX", "RX5", "RX_RX5"}, + {"IIR1", NULL, "IIR1 INP3 MUX"}, + {"IIR1 INP3 MUX", "DEC0", "RX_TX DEC0_INP"}, + {"IIR1 INP3 MUX", "DEC1", "RX_TX DEC1_INP"}, + {"IIR1 INP3 MUX", "DEC2", "RX_TX DEC2_INP"}, + {"IIR1 INP3 MUX", "DEC3", "RX_TX DEC3_INP"}, + {"IIR1 INP3 MUX", "RX0", "RX_RX0"}, + {"IIR1 INP3 MUX", "RX1", "RX_RX1"}, + {"IIR1 INP3 MUX", "RX2", "RX_RX2"}, + {"IIR1 INP3 MUX", "RX3", "RX_RX3"}, + {"IIR1 INP3 MUX", "RX4", "RX_RX4"}, + {"IIR1 INP3 MUX", "RX5", "RX_RX5"}, + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, + {"RX INT0 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT0 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT1 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT1 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT2 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT2 MIX2 INP", "SRC1", "SRC1"}, +}; + +static int rx_macro_component_probe(struct snd_soc_component *component) +{ + struct rx_macro *rx = snd_soc_component_get_drvdata(component); + + snd_soc_component_init_regmap(component, rx->regmap); + + snd_soc_component_update_bits(component, CDC_RX_RX0_RX_PATH_SEC7, + CDC_RX_DSM_OUT_DELAY_SEL_MASK, + CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE); + snd_soc_component_update_bits(component, CDC_RX_RX1_RX_PATH_SEC7, + CDC_RX_DSM_OUT_DELAY_SEL_MASK, + CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE); + snd_soc_component_update_bits(component, CDC_RX_RX2_RX_PATH_SEC7, + CDC_RX_DSM_OUT_DELAY_SEL_MASK, + CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE); + snd_soc_component_update_bits(component, CDC_RX_RX0_RX_PATH_CFG3, + CDC_RX_DC_COEFF_SEL_MASK, + CDC_RX_DC_COEFF_SEL_TWO); + snd_soc_component_update_bits(component, CDC_RX_RX1_RX_PATH_CFG3, + CDC_RX_DC_COEFF_SEL_MASK, + CDC_RX_DC_COEFF_SEL_TWO); + snd_soc_component_update_bits(component, CDC_RX_RX2_RX_PATH_CFG3, + CDC_RX_DC_COEFF_SEL_MASK, + CDC_RX_DC_COEFF_SEL_TWO); + + rx->component = component; + + return 0; +} + +static int swclk_gate_enable(struct clk_hw *hw) +{ + struct rx_macro *rx = to_rx_macro(hw); + int ret; + + ret = clk_prepare_enable(rx->mclk); + if (ret) { + dev_err(rx->dev, "unable to prepare mclk\n"); + return ret; + } + + rx_macro_mclk_enable(rx, true); + + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + CDC_RX_SWR_CLK_EN_MASK, 1); + + return 0; +} + +static void swclk_gate_disable(struct clk_hw *hw) +{ + struct rx_macro *rx = to_rx_macro(hw); + + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + CDC_RX_SWR_CLK_EN_MASK, 0); + + rx_macro_mclk_enable(rx, false); + clk_disable_unprepare(rx->mclk); +} + +static int swclk_gate_is_enabled(struct clk_hw *hw) +{ + struct rx_macro *rx = to_rx_macro(hw); + int ret, val; + + regmap_read(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, &val); + ret = val & BIT(0); + + return ret; +} + +static unsigned long swclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate / 2; +} + +static const struct clk_ops swclk_gate_ops = { + .prepare = swclk_gate_enable, + .unprepare = swclk_gate_disable, + .is_enabled = swclk_gate_is_enabled, + .recalc_rate = swclk_recalc_rate, + +}; + +static int rx_macro_register_mclk_output(struct rx_macro *rx) +{ + struct device *dev = rx->dev; + const char *parent_clk_name = NULL; + const char *clk_name = "lpass-rx-mclk"; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (rx->npl) + parent_clk_name = __clk_get_name(rx->npl); + else + parent_clk_name = __clk_get_name(rx->mclk); + + init.name = clk_name; + init.ops = &swclk_gate_ops; + init.flags = 0; + init.parent_names = &parent_clk_name; + init.num_parents = 1; + rx->hw.init = &init; + hw = &rx->hw; + ret = devm_clk_hw_register(rx->dev, hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); +} + +static const struct snd_soc_component_driver rx_macro_component_drv = { + .name = "RX-MACRO", + .probe = rx_macro_component_probe, + .controls = rx_macro_snd_controls, + .num_controls = ARRAY_SIZE(rx_macro_snd_controls), + .dapm_widgets = rx_macro_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rx_macro_dapm_widgets), + .dapm_routes = rx_audio_map, + .num_dapm_routes = ARRAY_SIZE(rx_audio_map), +}; + +static int rx_macro_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + kernel_ulong_t flags; + struct rx_macro *rx; + void __iomem *base; + int ret; + + flags = (kernel_ulong_t)device_get_match_data(dev); + + rx = devm_kzalloc(dev, sizeof(*rx), GFP_KERNEL); + if (!rx) + return -ENOMEM; + + rx->macro = devm_clk_get_optional(dev, "macro"); + if (IS_ERR(rx->macro)) + return dev_err_probe(dev, PTR_ERR(rx->macro), "unable to get macro clock\n"); + + rx->dcodec = devm_clk_get_optional(dev, "dcodec"); + if (IS_ERR(rx->dcodec)) + return dev_err_probe(dev, PTR_ERR(rx->dcodec), "unable to get dcodec clock\n"); + + rx->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(rx->mclk)) + return dev_err_probe(dev, PTR_ERR(rx->mclk), "unable to get mclk clock\n"); + + if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { + rx->npl = devm_clk_get(dev, "npl"); + if (IS_ERR(rx->npl)) + return dev_err_probe(dev, PTR_ERR(rx->npl), "unable to get npl clock\n"); + } + + rx->fsgen = devm_clk_get(dev, "fsgen"); + if (IS_ERR(rx->fsgen)) + return dev_err_probe(dev, PTR_ERR(rx->fsgen), "unable to get fsgen clock\n"); + + rx->pds = lpass_macro_pds_init(dev); + if (IS_ERR(rx->pds)) + return PTR_ERR(rx->pds); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto err; + } + + rx->regmap = devm_regmap_init_mmio(dev, base, &rx_regmap_config); + if (IS_ERR(rx->regmap)) { + ret = PTR_ERR(rx->regmap); + goto err; + } + + dev_set_drvdata(dev, rx); + + rx->dev = dev; + + /* set MCLK and NPL rates */ + clk_set_rate(rx->mclk, MCLK_FREQ); + clk_set_rate(rx->npl, MCLK_FREQ); + + ret = clk_prepare_enable(rx->macro); + if (ret) + goto err; + + ret = clk_prepare_enable(rx->dcodec); + if (ret) + goto err_dcodec; + + ret = clk_prepare_enable(rx->mclk); + if (ret) + goto err_mclk; + + ret = clk_prepare_enable(rx->npl); + if (ret) + goto err_npl; + + ret = clk_prepare_enable(rx->fsgen); + if (ret) + goto err_fsgen; + + /* reset swr block */ + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + CDC_RX_SWR_RESET_MASK, + CDC_RX_SWR_RESET); + + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + CDC_RX_SWR_CLK_EN_MASK, 1); + + regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL, + CDC_RX_SWR_RESET_MASK, 0); + + ret = devm_snd_soc_register_component(dev, &rx_macro_component_drv, + rx_macro_dai, + ARRAY_SIZE(rx_macro_dai)); + if (ret) + goto err_clkout; + + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = rx_macro_register_mclk_output(rx); + if (ret) + goto err_clkout; + + return 0; + +err_clkout: + clk_disable_unprepare(rx->fsgen); +err_fsgen: + clk_disable_unprepare(rx->npl); +err_npl: + clk_disable_unprepare(rx->mclk); +err_mclk: + clk_disable_unprepare(rx->dcodec); +err_dcodec: + clk_disable_unprepare(rx->macro); +err: + lpass_macro_pds_exit(rx->pds); + + return ret; +} + +static void rx_macro_remove(struct platform_device *pdev) +{ + struct rx_macro *rx = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(rx->mclk); + clk_disable_unprepare(rx->npl); + clk_disable_unprepare(rx->fsgen); + clk_disable_unprepare(rx->macro); + clk_disable_unprepare(rx->dcodec); + + lpass_macro_pds_exit(rx->pds); +} + +static const struct of_device_id rx_macro_dt_match[] = { + { + .compatible = "qcom,sc7280-lpass-rx-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + + }, { + .compatible = "qcom,sm8250-lpass-rx-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, { + .compatible = "qcom,sm8450-lpass-rx-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, { + .compatible = "qcom,sm8550-lpass-rx-macro", + }, { + .compatible = "qcom,sc8280xp-lpass-rx-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, + { } +}; +MODULE_DEVICE_TABLE(of, rx_macro_dt_match); + +static int __maybe_unused rx_macro_runtime_suspend(struct device *dev) +{ + struct rx_macro *rx = dev_get_drvdata(dev); + + regcache_cache_only(rx->regmap, true); + regcache_mark_dirty(rx->regmap); + + clk_disable_unprepare(rx->fsgen); + clk_disable_unprepare(rx->npl); + clk_disable_unprepare(rx->mclk); + + return 0; +} + +static int __maybe_unused rx_macro_runtime_resume(struct device *dev) +{ + struct rx_macro *rx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(rx->mclk); + if (ret) { + dev_err(dev, "unable to prepare mclk\n"); + return ret; + } + + ret = clk_prepare_enable(rx->npl); + if (ret) { + dev_err(dev, "unable to prepare mclkx2\n"); + goto err_npl; + } + + ret = clk_prepare_enable(rx->fsgen); + if (ret) { + dev_err(dev, "unable to prepare fsgen\n"); + goto err_fsgen; + } + regcache_cache_only(rx->regmap, false); + regcache_sync(rx->regmap); + + return 0; +err_fsgen: + clk_disable_unprepare(rx->npl); +err_npl: + clk_disable_unprepare(rx->mclk); + + return ret; +} + +static const struct dev_pm_ops rx_macro_pm_ops = { + SET_RUNTIME_PM_OPS(rx_macro_runtime_suspend, rx_macro_runtime_resume, NULL) +}; + +static struct platform_driver rx_macro_driver = { + .driver = { + .name = "rx_macro", + .of_match_table = rx_macro_dt_match, + .suppress_bind_attrs = true, + .pm = &rx_macro_pm_ops, + }, + .probe = rx_macro_probe, + .remove_new = rx_macro_remove, +}; + +module_platform_driver(rx_macro_driver); + +MODULE_DESCRIPTION("RX macro driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c new file mode 100644 index 0000000000..ebddfa74ce --- /dev/null +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -0,0 +1,2196 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lpass-macro-common.h" + +#define CDC_TX_CLK_RST_CTRL_MCLK_CONTROL (0x0000) +#define CDC_TX_MCLK_EN_MASK BIT(0) +#define CDC_TX_MCLK_ENABLE BIT(0) +#define CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004) +#define CDC_TX_FS_CNT_EN_MASK BIT(0) +#define CDC_TX_FS_CNT_ENABLE BIT(0) +#define CDC_TX_CLK_RST_CTRL_SWR_CONTROL (0x0008) +#define CDC_TX_SWR_RESET_MASK BIT(1) +#define CDC_TX_SWR_RESET_ENABLE BIT(1) +#define CDC_TX_SWR_CLK_EN_MASK BIT(0) +#define CDC_TX_SWR_CLK_ENABLE BIT(0) +#define CDC_TX_TOP_CSR_TOP_CFG0 (0x0080) +#define CDC_TX_TOP_CSR_ANC_CFG (0x0084) +#define CDC_TX_TOP_CSR_SWR_CTRL (0x0088) +#define CDC_TX_TOP_CSR_FREQ_MCLK (0x0090) +#define CDC_TX_TOP_CSR_DEBUG_BUS (0x0094) +#define CDC_TX_TOP_CSR_DEBUG_EN (0x0098) +#define CDC_TX_TOP_CSR_TX_I2S_CTL (0x00A4) +#define CDC_TX_TOP_CSR_I2S_CLK (0x00A8) +#define CDC_TX_TOP_CSR_I2S_RESET (0x00AC) +#define CDC_TX_TOP_CSR_SWR_DMICn_CTL(n) (0x00C0 + n * 0x4) +#define CDC_TX_TOP_CSR_SWR_DMIC0_CTL (0x00C0) +#define CDC_TX_SWR_DMIC_CLK_SEL_MASK GENMASK(3, 1) +#define CDC_TX_TOP_CSR_SWR_DMIC1_CTL (0x00C4) +#define CDC_TX_TOP_CSR_SWR_DMIC2_CTL (0x00C8) +#define CDC_TX_TOP_CSR_SWR_DMIC3_CTL (0x00CC) +#define CDC_TX_TOP_CSR_SWR_AMIC0_CTL (0x00D0) +#define CDC_TX_TOP_CSR_SWR_AMIC1_CTL (0x00D4) +#define CDC_TX_INP_MUX_ADC_MUXn_CFG0(n) (0x0100 + 0x8 * n) +#define CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK GENMASK(3, 0) +#define CDC_TX_MACRO_DMIC_MUX_SEL_MASK GENMASK(7, 4) +#define CDC_TX_INP_MUX_ADC_MUX0_CFG0 (0x0100) +#define CDC_TX_INP_MUX_ADC_MUXn_CFG1(n) (0x0104 + 0x8 * n) +#define CDC_TX_INP_MUX_ADC_MUX0_CFG1 (0x0104) +#define CDC_TX_INP_MUX_ADC_MUX1_CFG0 (0x0108) +#define CDC_TX_INP_MUX_ADC_MUX1_CFG1 (0x010C) +#define CDC_TX_INP_MUX_ADC_MUX2_CFG0 (0x0110) +#define CDC_TX_INP_MUX_ADC_MUX2_CFG1 (0x0114) +#define CDC_TX_INP_MUX_ADC_MUX3_CFG0 (0x0118) +#define CDC_TX_INP_MUX_ADC_MUX3_CFG1 (0x011C) +#define CDC_TX_INP_MUX_ADC_MUX4_CFG0 (0x0120) +#define CDC_TX_INP_MUX_ADC_MUX4_CFG1 (0x0124) +#define CDC_TX_INP_MUX_ADC_MUX5_CFG0 (0x0128) +#define CDC_TX_INP_MUX_ADC_MUX5_CFG1 (0x012C) +#define CDC_TX_INP_MUX_ADC_MUX6_CFG0 (0x0130) +#define CDC_TX_INP_MUX_ADC_MUX6_CFG1 (0x0134) +#define CDC_TX_INP_MUX_ADC_MUX7_CFG0 (0x0138) +#define CDC_TX_INP_MUX_ADC_MUX7_CFG1 (0x013C) +#define CDC_TX_ANC0_CLK_RESET_CTL (0x0200) +#define CDC_TX_ANC0_MODE_1_CTL (0x0204) +#define CDC_TX_ANC0_MODE_2_CTL (0x0208) +#define CDC_TX_ANC0_FF_SHIFT (0x020C) +#define CDC_TX_ANC0_FB_SHIFT (0x0210) +#define CDC_TX_ANC0_LPF_FF_A_CTL (0x0214) +#define CDC_TX_ANC0_LPF_FF_B_CTL (0x0218) +#define CDC_TX_ANC0_LPF_FB_CTL (0x021C) +#define CDC_TX_ANC0_SMLPF_CTL (0x0220) +#define CDC_TX_ANC0_DCFLT_SHIFT_CTL (0x0224) +#define CDC_TX_ANC0_IIR_ADAPT_CTL (0x0228) +#define CDC_TX_ANC0_IIR_COEFF_1_CTL (0x022C) +#define CDC_TX_ANC0_IIR_COEFF_2_CTL (0x0230) +#define CDC_TX_ANC0_FF_A_GAIN_CTL (0x0234) +#define CDC_TX_ANC0_FF_B_GAIN_CTL (0x0238) +#define CDC_TX_ANC0_FB_GAIN_CTL (0x023C) +#define CDC_TXn_TX_PATH_CTL(n) (0x0400 + 0x80 * n) +#define CDC_TXn_PCM_RATE_MASK GENMASK(3, 0) +#define CDC_TXn_PGA_MUTE_MASK BIT(4) +#define CDC_TXn_CLK_EN_MASK BIT(5) +#define CDC_TX0_TX_PATH_CTL (0x0400) +#define CDC_TXn_TX_PATH_CFG0(n) (0x0404 + 0x80 * n) +#define CDC_TX0_TX_PATH_CFG0 (0x0404) +#define CDC_TXn_PH_EN_MASK BIT(0) +#define CDC_TXn_ADC_MODE_MASK GENMASK(2, 1) +#define CDC_TXn_HPF_CUT_FREQ_MASK GENMASK(6, 5) +#define CDC_TXn_ADC_DMIC_SEL_MASK BIT(7) +#define CDC_TX0_TX_PATH_CFG1 (0x0408) +#define CDC_TXn_TX_VOL_CTL(n) (0x040C + 0x80 * n) +#define CDC_TX0_TX_VOL_CTL (0x040C) +#define CDC_TX0_TX_PATH_SEC0 (0x0410) +#define CDC_TX0_TX_PATH_SEC1 (0x0414) +#define CDC_TXn_TX_PATH_SEC2(n) (0x0418 + 0x80 * n) +#define CDC_TXn_HPF_F_CHANGE_MASK BIT(1) +#define CDC_TXn_HPF_ZERO_GATE_MASK BIT(0) +#define CDC_TX0_TX_PATH_SEC2 (0x0418) +#define CDC_TX0_TX_PATH_SEC3 (0x041C) +#define CDC_TX0_TX_PATH_SEC4 (0x0420) +#define CDC_TX0_TX_PATH_SEC5 (0x0424) +#define CDC_TX0_TX_PATH_SEC6 (0x0428) +#define CDC_TX0_TX_PATH_SEC7 (0x042C) +#define CDC_TX0_MBHC_CTL_EN_MASK BIT(6) +#define CDC_TX1_TX_PATH_CTL (0x0480) +#define CDC_TX1_TX_PATH_CFG0 (0x0484) +#define CDC_TX1_TX_PATH_CFG1 (0x0488) +#define CDC_TX1_TX_VOL_CTL (0x048C) +#define CDC_TX1_TX_PATH_SEC0 (0x0490) +#define CDC_TX1_TX_PATH_SEC1 (0x0494) +#define CDC_TX1_TX_PATH_SEC2 (0x0498) +#define CDC_TX1_TX_PATH_SEC3 (0x049C) +#define CDC_TX1_TX_PATH_SEC4 (0x04A0) +#define CDC_TX1_TX_PATH_SEC5 (0x04A4) +#define CDC_TX1_TX_PATH_SEC6 (0x04A8) +#define CDC_TX2_TX_PATH_CTL (0x0500) +#define CDC_TX2_TX_PATH_CFG0 (0x0504) +#define CDC_TX2_TX_PATH_CFG1 (0x0508) +#define CDC_TX2_TX_VOL_CTL (0x050C) +#define CDC_TX2_TX_PATH_SEC0 (0x0510) +#define CDC_TX2_TX_PATH_SEC1 (0x0514) +#define CDC_TX2_TX_PATH_SEC2 (0x0518) +#define CDC_TX2_TX_PATH_SEC3 (0x051C) +#define CDC_TX2_TX_PATH_SEC4 (0x0520) +#define CDC_TX2_TX_PATH_SEC5 (0x0524) +#define CDC_TX2_TX_PATH_SEC6 (0x0528) +#define CDC_TX3_TX_PATH_CTL (0x0580) +#define CDC_TX3_TX_PATH_CFG0 (0x0584) +#define CDC_TX3_TX_PATH_CFG1 (0x0588) +#define CDC_TX3_TX_VOL_CTL (0x058C) +#define CDC_TX3_TX_PATH_SEC0 (0x0590) +#define CDC_TX3_TX_PATH_SEC1 (0x0594) +#define CDC_TX3_TX_PATH_SEC2 (0x0598) +#define CDC_TX3_TX_PATH_SEC3 (0x059C) +#define CDC_TX3_TX_PATH_SEC4 (0x05A0) +#define CDC_TX3_TX_PATH_SEC5 (0x05A4) +#define CDC_TX3_TX_PATH_SEC6 (0x05A8) +#define CDC_TX4_TX_PATH_CTL (0x0600) +#define CDC_TX4_TX_PATH_CFG0 (0x0604) +#define CDC_TX4_TX_PATH_CFG1 (0x0608) +#define CDC_TX4_TX_VOL_CTL (0x060C) +#define CDC_TX4_TX_PATH_SEC0 (0x0610) +#define CDC_TX4_TX_PATH_SEC1 (0x0614) +#define CDC_TX4_TX_PATH_SEC2 (0x0618) +#define CDC_TX4_TX_PATH_SEC3 (0x061C) +#define CDC_TX4_TX_PATH_SEC4 (0x0620) +#define CDC_TX4_TX_PATH_SEC5 (0x0624) +#define CDC_TX4_TX_PATH_SEC6 (0x0628) +#define CDC_TX5_TX_PATH_CTL (0x0680) +#define CDC_TX5_TX_PATH_CFG0 (0x0684) +#define CDC_TX5_TX_PATH_CFG1 (0x0688) +#define CDC_TX5_TX_VOL_CTL (0x068C) +#define CDC_TX5_TX_PATH_SEC0 (0x0690) +#define CDC_TX5_TX_PATH_SEC1 (0x0694) +#define CDC_TX5_TX_PATH_SEC2 (0x0698) +#define CDC_TX5_TX_PATH_SEC3 (0x069C) +#define CDC_TX5_TX_PATH_SEC4 (0x06A0) +#define CDC_TX5_TX_PATH_SEC5 (0x06A4) +#define CDC_TX5_TX_PATH_SEC6 (0x06A8) +#define CDC_TX6_TX_PATH_CTL (0x0700) +#define CDC_TX6_TX_PATH_CFG0 (0x0704) +#define CDC_TX6_TX_PATH_CFG1 (0x0708) +#define CDC_TX6_TX_VOL_CTL (0x070C) +#define CDC_TX6_TX_PATH_SEC0 (0x0710) +#define CDC_TX6_TX_PATH_SEC1 (0x0714) +#define CDC_TX6_TX_PATH_SEC2 (0x0718) +#define CDC_TX6_TX_PATH_SEC3 (0x071C) +#define CDC_TX6_TX_PATH_SEC4 (0x0720) +#define CDC_TX6_TX_PATH_SEC5 (0x0724) +#define CDC_TX6_TX_PATH_SEC6 (0x0728) +#define CDC_TX7_TX_PATH_CTL (0x0780) +#define CDC_TX7_TX_PATH_CFG0 (0x0784) +#define CDC_TX7_TX_PATH_CFG1 (0x0788) +#define CDC_TX7_TX_VOL_CTL (0x078C) +#define CDC_TX7_TX_PATH_SEC0 (0x0790) +#define CDC_TX7_TX_PATH_SEC1 (0x0794) +#define CDC_TX7_TX_PATH_SEC2 (0x0798) +#define CDC_TX7_TX_PATH_SEC3 (0x079C) +#define CDC_TX7_TX_PATH_SEC4 (0x07A0) +#define CDC_TX7_TX_PATH_SEC5 (0x07A4) +#define CDC_TX7_TX_PATH_SEC6 (0x07A8) +#define TX_MAX_OFFSET (0x07A8) + +#define TX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define TX_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 +#define TX_ADC_MAX 5 +#define TX_ADC_TO_DMIC(n) ((n - TX_ADC_MAX)/2) +#define NUM_DECIMATORS 8 +#define TX_NUM_CLKS_MAX 5 +#define TX_MACRO_DMIC_UNMUTE_DELAY_MS 40 +#define TX_MACRO_AMIC_UNMUTE_DELAY_MS 100 +#define TX_MACRO_DMIC_HPF_DELAY_MS 300 +#define TX_MACRO_AMIC_HPF_DELAY_MS 300 +#define MCLK_FREQ 19200000 + +enum { + TX_MACRO_AIF_INVALID = 0, + TX_MACRO_AIF1_CAP, + TX_MACRO_AIF2_CAP, + TX_MACRO_AIF3_CAP, + TX_MACRO_MAX_DAIS +}; + +enum { + TX_MACRO_DEC0, + TX_MACRO_DEC1, + TX_MACRO_DEC2, + TX_MACRO_DEC3, + TX_MACRO_DEC4, + TX_MACRO_DEC5, + TX_MACRO_DEC6, + TX_MACRO_DEC7, + TX_MACRO_DEC_MAX, +}; + +enum { + TX_MACRO_CLK_DIV_2, + TX_MACRO_CLK_DIV_3, + TX_MACRO_CLK_DIV_4, + TX_MACRO_CLK_DIV_6, + TX_MACRO_CLK_DIV_8, + TX_MACRO_CLK_DIV_16, +}; + +enum { + MSM_DMIC, + SWR_MIC, + ANC_FB_TUNE1 +}; + +struct tx_mute_work { + struct tx_macro *tx; + u8 decimator; + struct delayed_work dwork; +}; + +struct hpf_work { + struct tx_macro *tx; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +struct tx_macro { + struct device *dev; + struct snd_soc_component *component; + struct hpf_work tx_hpf_work[NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS]; + unsigned long active_ch_mask[TX_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[TX_MACRO_MAX_DAIS]; + int active_decimator[TX_MACRO_MAX_DAIS]; + struct regmap *regmap; + struct clk *mclk; + struct clk *npl; + struct clk *macro; + struct clk *dcodec; + struct clk *fsgen; + struct clk_hw hw; + bool dec_active[NUM_DECIMATORS]; + int tx_mclk_users; + u16 dmic_clk_div; + bool bcs_enable; + int dec_mode[NUM_DECIMATORS]; + struct lpass_macro *pds; + bool bcs_clk_en; +}; +#define to_tx_macro(_hw) container_of(_hw, struct tx_macro, hw) + +static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); + +static struct reg_default tx_defaults[] = { + /* TX Macro */ + { CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 }, + { CDC_TX_CLK_RST_CTRL_SWR_CONTROL, 0x00}, + { CDC_TX_TOP_CSR_TOP_CFG0, 0x00}, + { CDC_TX_TOP_CSR_ANC_CFG, 0x00}, + { CDC_TX_TOP_CSR_SWR_CTRL, 0x00}, + { CDC_TX_TOP_CSR_FREQ_MCLK, 0x00}, + { CDC_TX_TOP_CSR_DEBUG_BUS, 0x00}, + { CDC_TX_TOP_CSR_DEBUG_EN, 0x00}, + { CDC_TX_TOP_CSR_TX_I2S_CTL, 0x0C}, + { CDC_TX_TOP_CSR_I2S_CLK, 0x00}, + { CDC_TX_TOP_CSR_I2S_RESET, 0x00}, + { CDC_TX_TOP_CSR_SWR_DMIC0_CTL, 0x00}, + { CDC_TX_TOP_CSR_SWR_DMIC1_CTL, 0x00}, + { CDC_TX_TOP_CSR_SWR_DMIC2_CTL, 0x00}, + { CDC_TX_TOP_CSR_SWR_DMIC3_CTL, 0x00}, + { CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0x00}, + { CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX4_CFG1, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX5_CFG1, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX6_CFG1, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00}, + { CDC_TX_INP_MUX_ADC_MUX7_CFG1, 0x00}, + { CDC_TX_ANC0_CLK_RESET_CTL, 0x00}, + { CDC_TX_ANC0_MODE_1_CTL, 0x00}, + { CDC_TX_ANC0_MODE_2_CTL, 0x00}, + { CDC_TX_ANC0_FF_SHIFT, 0x00}, + { CDC_TX_ANC0_FB_SHIFT, 0x00}, + { CDC_TX_ANC0_LPF_FF_A_CTL, 0x00}, + { CDC_TX_ANC0_LPF_FF_B_CTL, 0x00}, + { CDC_TX_ANC0_LPF_FB_CTL, 0x00}, + { CDC_TX_ANC0_SMLPF_CTL, 0x00}, + { CDC_TX_ANC0_DCFLT_SHIFT_CTL, 0x00}, + { CDC_TX_ANC0_IIR_ADAPT_CTL, 0x00}, + { CDC_TX_ANC0_IIR_COEFF_1_CTL, 0x00}, + { CDC_TX_ANC0_IIR_COEFF_2_CTL, 0x00}, + { CDC_TX_ANC0_FF_A_GAIN_CTL, 0x00}, + { CDC_TX_ANC0_FF_B_GAIN_CTL, 0x00}, + { CDC_TX_ANC0_FB_GAIN_CTL, 0x00}, + { CDC_TX0_TX_PATH_CTL, 0x04}, + { CDC_TX0_TX_PATH_CFG0, 0x10}, + { CDC_TX0_TX_PATH_CFG1, 0x0B}, + { CDC_TX0_TX_VOL_CTL, 0x00}, + { CDC_TX0_TX_PATH_SEC0, 0x00}, + { CDC_TX0_TX_PATH_SEC1, 0x00}, + { CDC_TX0_TX_PATH_SEC2, 0x01}, + { CDC_TX0_TX_PATH_SEC3, 0x3C}, + { CDC_TX0_TX_PATH_SEC4, 0x20}, + { CDC_TX0_TX_PATH_SEC5, 0x00}, + { CDC_TX0_TX_PATH_SEC6, 0x00}, + { CDC_TX0_TX_PATH_SEC7, 0x25}, + { CDC_TX1_TX_PATH_CTL, 0x04}, + { CDC_TX1_TX_PATH_CFG0, 0x10}, + { CDC_TX1_TX_PATH_CFG1, 0x0B}, + { CDC_TX1_TX_VOL_CTL, 0x00}, + { CDC_TX1_TX_PATH_SEC0, 0x00}, + { CDC_TX1_TX_PATH_SEC1, 0x00}, + { CDC_TX1_TX_PATH_SEC2, 0x01}, + { CDC_TX1_TX_PATH_SEC3, 0x3C}, + { CDC_TX1_TX_PATH_SEC4, 0x20}, + { CDC_TX1_TX_PATH_SEC5, 0x00}, + { CDC_TX1_TX_PATH_SEC6, 0x00}, + { CDC_TX2_TX_PATH_CTL, 0x04}, + { CDC_TX2_TX_PATH_CFG0, 0x10}, + { CDC_TX2_TX_PATH_CFG1, 0x0B}, + { CDC_TX2_TX_VOL_CTL, 0x00}, + { CDC_TX2_TX_PATH_SEC0, 0x00}, + { CDC_TX2_TX_PATH_SEC1, 0x00}, + { CDC_TX2_TX_PATH_SEC2, 0x01}, + { CDC_TX2_TX_PATH_SEC3, 0x3C}, + { CDC_TX2_TX_PATH_SEC4, 0x20}, + { CDC_TX2_TX_PATH_SEC5, 0x00}, + { CDC_TX2_TX_PATH_SEC6, 0x00}, + { CDC_TX3_TX_PATH_CTL, 0x04}, + { CDC_TX3_TX_PATH_CFG0, 0x10}, + { CDC_TX3_TX_PATH_CFG1, 0x0B}, + { CDC_TX3_TX_VOL_CTL, 0x00}, + { CDC_TX3_TX_PATH_SEC0, 0x00}, + { CDC_TX3_TX_PATH_SEC1, 0x00}, + { CDC_TX3_TX_PATH_SEC2, 0x01}, + { CDC_TX3_TX_PATH_SEC3, 0x3C}, + { CDC_TX3_TX_PATH_SEC4, 0x20}, + { CDC_TX3_TX_PATH_SEC5, 0x00}, + { CDC_TX3_TX_PATH_SEC6, 0x00}, + { CDC_TX4_TX_PATH_CTL, 0x04}, + { CDC_TX4_TX_PATH_CFG0, 0x10}, + { CDC_TX4_TX_PATH_CFG1, 0x0B}, + { CDC_TX4_TX_VOL_CTL, 0x00}, + { CDC_TX4_TX_PATH_SEC0, 0x00}, + { CDC_TX4_TX_PATH_SEC1, 0x00}, + { CDC_TX4_TX_PATH_SEC2, 0x01}, + { CDC_TX4_TX_PATH_SEC3, 0x3C}, + { CDC_TX4_TX_PATH_SEC4, 0x20}, + { CDC_TX4_TX_PATH_SEC5, 0x00}, + { CDC_TX4_TX_PATH_SEC6, 0x00}, + { CDC_TX5_TX_PATH_CTL, 0x04}, + { CDC_TX5_TX_PATH_CFG0, 0x10}, + { CDC_TX5_TX_PATH_CFG1, 0x0B}, + { CDC_TX5_TX_VOL_CTL, 0x00}, + { CDC_TX5_TX_PATH_SEC0, 0x00}, + { CDC_TX5_TX_PATH_SEC1, 0x00}, + { CDC_TX5_TX_PATH_SEC2, 0x01}, + { CDC_TX5_TX_PATH_SEC3, 0x3C}, + { CDC_TX5_TX_PATH_SEC4, 0x20}, + { CDC_TX5_TX_PATH_SEC5, 0x00}, + { CDC_TX5_TX_PATH_SEC6, 0x00}, + { CDC_TX6_TX_PATH_CTL, 0x04}, + { CDC_TX6_TX_PATH_CFG0, 0x10}, + { CDC_TX6_TX_PATH_CFG1, 0x0B}, + { CDC_TX6_TX_VOL_CTL, 0x00}, + { CDC_TX6_TX_PATH_SEC0, 0x00}, + { CDC_TX6_TX_PATH_SEC1, 0x00}, + { CDC_TX6_TX_PATH_SEC2, 0x01}, + { CDC_TX6_TX_PATH_SEC3, 0x3C}, + { CDC_TX6_TX_PATH_SEC4, 0x20}, + { CDC_TX6_TX_PATH_SEC5, 0x00}, + { CDC_TX6_TX_PATH_SEC6, 0x00}, + { CDC_TX7_TX_PATH_CTL, 0x04}, + { CDC_TX7_TX_PATH_CFG0, 0x10}, + { CDC_TX7_TX_PATH_CFG1, 0x0B}, + { CDC_TX7_TX_VOL_CTL, 0x00}, + { CDC_TX7_TX_PATH_SEC0, 0x00}, + { CDC_TX7_TX_PATH_SEC1, 0x00}, + { CDC_TX7_TX_PATH_SEC2, 0x01}, + { CDC_TX7_TX_PATH_SEC3, 0x3C}, + { CDC_TX7_TX_PATH_SEC4, 0x20}, + { CDC_TX7_TX_PATH_SEC5, 0x00}, + { CDC_TX7_TX_PATH_SEC6, 0x00}, +}; + +static bool tx_is_volatile_register(struct device *dev, unsigned int reg) +{ + /* Update volatile list for tx/tx macros */ + switch (reg) { + case CDC_TX_TOP_CSR_SWR_DMIC0_CTL: + case CDC_TX_TOP_CSR_SWR_DMIC1_CTL: + case CDC_TX_TOP_CSR_SWR_DMIC2_CTL: + case CDC_TX_TOP_CSR_SWR_DMIC3_CTL: + return true; + } + return false; +} + +static bool tx_is_rw_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_TX_CLK_RST_CTRL_MCLK_CONTROL: + case CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL: + case CDC_TX_CLK_RST_CTRL_SWR_CONTROL: + case CDC_TX_TOP_CSR_TOP_CFG0: + case CDC_TX_TOP_CSR_ANC_CFG: + case CDC_TX_TOP_CSR_SWR_CTRL: + case CDC_TX_TOP_CSR_FREQ_MCLK: + case CDC_TX_TOP_CSR_DEBUG_BUS: + case CDC_TX_TOP_CSR_DEBUG_EN: + case CDC_TX_TOP_CSR_TX_I2S_CTL: + case CDC_TX_TOP_CSR_I2S_CLK: + case CDC_TX_TOP_CSR_I2S_RESET: + case CDC_TX_TOP_CSR_SWR_DMIC0_CTL: + case CDC_TX_TOP_CSR_SWR_DMIC1_CTL: + case CDC_TX_TOP_CSR_SWR_DMIC2_CTL: + case CDC_TX_TOP_CSR_SWR_DMIC3_CTL: + case CDC_TX_TOP_CSR_SWR_AMIC0_CTL: + case CDC_TX_TOP_CSR_SWR_AMIC1_CTL: + case CDC_TX_ANC0_CLK_RESET_CTL: + case CDC_TX_ANC0_MODE_1_CTL: + case CDC_TX_ANC0_MODE_2_CTL: + case CDC_TX_ANC0_FF_SHIFT: + case CDC_TX_ANC0_FB_SHIFT: + case CDC_TX_ANC0_LPF_FF_A_CTL: + case CDC_TX_ANC0_LPF_FF_B_CTL: + case CDC_TX_ANC0_LPF_FB_CTL: + case CDC_TX_ANC0_SMLPF_CTL: + case CDC_TX_ANC0_DCFLT_SHIFT_CTL: + case CDC_TX_ANC0_IIR_ADAPT_CTL: + case CDC_TX_ANC0_IIR_COEFF_1_CTL: + case CDC_TX_ANC0_IIR_COEFF_2_CTL: + case CDC_TX_ANC0_FF_A_GAIN_CTL: + case CDC_TX_ANC0_FF_B_GAIN_CTL: + case CDC_TX_ANC0_FB_GAIN_CTL: + case CDC_TX_INP_MUX_ADC_MUX0_CFG0: + case CDC_TX_INP_MUX_ADC_MUX0_CFG1: + case CDC_TX_INP_MUX_ADC_MUX1_CFG0: + case CDC_TX_INP_MUX_ADC_MUX1_CFG1: + case CDC_TX_INP_MUX_ADC_MUX2_CFG0: + case CDC_TX_INP_MUX_ADC_MUX2_CFG1: + case CDC_TX_INP_MUX_ADC_MUX3_CFG0: + case CDC_TX_INP_MUX_ADC_MUX3_CFG1: + case CDC_TX_INP_MUX_ADC_MUX4_CFG0: + case CDC_TX_INP_MUX_ADC_MUX4_CFG1: + case CDC_TX_INP_MUX_ADC_MUX5_CFG0: + case CDC_TX_INP_MUX_ADC_MUX5_CFG1: + case CDC_TX_INP_MUX_ADC_MUX6_CFG0: + case CDC_TX_INP_MUX_ADC_MUX6_CFG1: + case CDC_TX_INP_MUX_ADC_MUX7_CFG0: + case CDC_TX_INP_MUX_ADC_MUX7_CFG1: + case CDC_TX0_TX_PATH_CTL: + case CDC_TX0_TX_PATH_CFG0: + case CDC_TX0_TX_PATH_CFG1: + case CDC_TX0_TX_VOL_CTL: + case CDC_TX0_TX_PATH_SEC0: + case CDC_TX0_TX_PATH_SEC1: + case CDC_TX0_TX_PATH_SEC2: + case CDC_TX0_TX_PATH_SEC3: + case CDC_TX0_TX_PATH_SEC4: + case CDC_TX0_TX_PATH_SEC5: + case CDC_TX0_TX_PATH_SEC6: + case CDC_TX0_TX_PATH_SEC7: + case CDC_TX1_TX_PATH_CTL: + case CDC_TX1_TX_PATH_CFG0: + case CDC_TX1_TX_PATH_CFG1: + case CDC_TX1_TX_VOL_CTL: + case CDC_TX1_TX_PATH_SEC0: + case CDC_TX1_TX_PATH_SEC1: + case CDC_TX1_TX_PATH_SEC2: + case CDC_TX1_TX_PATH_SEC3: + case CDC_TX1_TX_PATH_SEC4: + case CDC_TX1_TX_PATH_SEC5: + case CDC_TX1_TX_PATH_SEC6: + case CDC_TX2_TX_PATH_CTL: + case CDC_TX2_TX_PATH_CFG0: + case CDC_TX2_TX_PATH_CFG1: + case CDC_TX2_TX_VOL_CTL: + case CDC_TX2_TX_PATH_SEC0: + case CDC_TX2_TX_PATH_SEC1: + case CDC_TX2_TX_PATH_SEC2: + case CDC_TX2_TX_PATH_SEC3: + case CDC_TX2_TX_PATH_SEC4: + case CDC_TX2_TX_PATH_SEC5: + case CDC_TX2_TX_PATH_SEC6: + case CDC_TX3_TX_PATH_CTL: + case CDC_TX3_TX_PATH_CFG0: + case CDC_TX3_TX_PATH_CFG1: + case CDC_TX3_TX_VOL_CTL: + case CDC_TX3_TX_PATH_SEC0: + case CDC_TX3_TX_PATH_SEC1: + case CDC_TX3_TX_PATH_SEC2: + case CDC_TX3_TX_PATH_SEC3: + case CDC_TX3_TX_PATH_SEC4: + case CDC_TX3_TX_PATH_SEC5: + case CDC_TX3_TX_PATH_SEC6: + case CDC_TX4_TX_PATH_CTL: + case CDC_TX4_TX_PATH_CFG0: + case CDC_TX4_TX_PATH_CFG1: + case CDC_TX4_TX_VOL_CTL: + case CDC_TX4_TX_PATH_SEC0: + case CDC_TX4_TX_PATH_SEC1: + case CDC_TX4_TX_PATH_SEC2: + case CDC_TX4_TX_PATH_SEC3: + case CDC_TX4_TX_PATH_SEC4: + case CDC_TX4_TX_PATH_SEC5: + case CDC_TX4_TX_PATH_SEC6: + case CDC_TX5_TX_PATH_CTL: + case CDC_TX5_TX_PATH_CFG0: + case CDC_TX5_TX_PATH_CFG1: + case CDC_TX5_TX_VOL_CTL: + case CDC_TX5_TX_PATH_SEC0: + case CDC_TX5_TX_PATH_SEC1: + case CDC_TX5_TX_PATH_SEC2: + case CDC_TX5_TX_PATH_SEC3: + case CDC_TX5_TX_PATH_SEC4: + case CDC_TX5_TX_PATH_SEC5: + case CDC_TX5_TX_PATH_SEC6: + case CDC_TX6_TX_PATH_CTL: + case CDC_TX6_TX_PATH_CFG0: + case CDC_TX6_TX_PATH_CFG1: + case CDC_TX6_TX_VOL_CTL: + case CDC_TX6_TX_PATH_SEC0: + case CDC_TX6_TX_PATH_SEC1: + case CDC_TX6_TX_PATH_SEC2: + case CDC_TX6_TX_PATH_SEC3: + case CDC_TX6_TX_PATH_SEC4: + case CDC_TX6_TX_PATH_SEC5: + case CDC_TX6_TX_PATH_SEC6: + case CDC_TX7_TX_PATH_CTL: + case CDC_TX7_TX_PATH_CFG0: + case CDC_TX7_TX_PATH_CFG1: + case CDC_TX7_TX_VOL_CTL: + case CDC_TX7_TX_PATH_SEC0: + case CDC_TX7_TX_PATH_SEC1: + case CDC_TX7_TX_PATH_SEC2: + case CDC_TX7_TX_PATH_SEC3: + case CDC_TX7_TX_PATH_SEC4: + case CDC_TX7_TX_PATH_SEC5: + case CDC_TX7_TX_PATH_SEC6: + return true; + } + + return false; +} + +static const struct regmap_config tx_regmap_config = { + .name = "tx_macro", + .reg_bits = 16, + .val_bits = 32, + .reg_stride = 4, + .cache_type = REGCACHE_FLAT, + .max_register = TX_MAX_OFFSET, + .reg_defaults = tx_defaults, + .num_reg_defaults = ARRAY_SIZE(tx_defaults), + .writeable_reg = tx_is_rw_register, + .volatile_reg = tx_is_volatile_register, + .readable_reg = tx_is_rw_register, +}; + +static int tx_macro_mclk_enable(struct tx_macro *tx, + bool mclk_enable) +{ + struct regmap *regmap = tx->regmap; + + if (mclk_enable) { + if (tx->tx_mclk_users == 0) { + /* 9.6MHz MCLK, set value 0x00 if other frequency */ + regmap_update_bits(regmap, CDC_TX_TOP_CSR_FREQ_MCLK, 0x01, 0x01); + regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, + CDC_TX_MCLK_EN_MASK, + CDC_TX_MCLK_ENABLE); + regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_TX_FS_CNT_EN_MASK, + CDC_TX_FS_CNT_ENABLE); + regcache_mark_dirty(regmap); + regcache_sync(regmap); + } + tx->tx_mclk_users++; + } else { + if (tx->tx_mclk_users <= 0) { + dev_err(tx->dev, "clock already disabled\n"); + tx->tx_mclk_users = 0; + goto exit; + } + tx->tx_mclk_users--; + if (tx->tx_mclk_users == 0) { + regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_TX_FS_CNT_EN_MASK, 0x0); + regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_MCLK_CONTROL, + CDC_TX_MCLK_EN_MASK, 0x0); + } + } +exit: + return 0; +} + +static bool is_amic_enabled(struct snd_soc_component *component, u8 decimator) +{ + u16 adc_mux_reg, adc_reg, adc_n; + + adc_mux_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG1(decimator); + + if (snd_soc_component_read(component, adc_mux_reg) & SWR_MIC) { + adc_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG0(decimator); + adc_n = snd_soc_component_read_field(component, adc_reg, + CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK); + if (adc_n < TX_ADC_MAX) + return true; + } + + return false; +} + +static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct tx_macro *tx; + struct snd_soc_component *component; + u16 dec_cfg_reg, hpf_gate_reg; + u8 hpf_cut_off_freq; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + tx = hpf_work->tx; + component = tx->component; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = CDC_TXn_TX_PATH_CFG0(hpf_work->decimator); + hpf_gate_reg = CDC_TXn_TX_PATH_SEC2(hpf_work->decimator); + + if (is_amic_enabled(component, hpf_work->decimator)) { + snd_soc_component_write_field(component, + dec_cfg_reg, + CDC_TXn_HPF_CUT_FREQ_MASK, + hpf_cut_off_freq); + snd_soc_component_update_bits(component, hpf_gate_reg, + CDC_TXn_HPF_F_CHANGE_MASK | + CDC_TXn_HPF_ZERO_GATE_MASK, + 0x02); + snd_soc_component_update_bits(component, hpf_gate_reg, + CDC_TXn_HPF_F_CHANGE_MASK | + CDC_TXn_HPF_ZERO_GATE_MASK, + 0x01); + } else { + snd_soc_component_write_field(component, dec_cfg_reg, + CDC_TXn_HPF_CUT_FREQ_MASK, + hpf_cut_off_freq); + snd_soc_component_write_field(component, hpf_gate_reg, + CDC_TXn_HPF_F_CHANGE_MASK, 0x1); + /* Minimum 1 clk cycle delay is required as per HW spec */ + usleep_range(1000, 1010); + snd_soc_component_write_field(component, hpf_gate_reg, + CDC_TXn_HPF_F_CHANGE_MASK, 0x0); + } +} + +static void tx_macro_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct snd_soc_component *component; + struct tx_macro *tx; + struct delayed_work *delayed_work; + u8 decimator; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + tx = tx_mute_dwork->tx; + component = tx->component; + decimator = tx_mute_dwork->decimator; + + snd_soc_component_write_field(component, CDC_TXn_TX_PATH_CTL(decimator), + CDC_TXn_PGA_MUTE_MASK, 0x0); +} + +static int tx_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tx_macro_mclk_enable(tx, true); + break; + case SND_SOC_DAPM_POST_PMD: + tx_macro_mclk_enable(tx, false); + break; + default: + break; + } + + return 0; +} + +static int tx_macro_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val, dmic; + u16 mic_sel_reg; + u16 dmic_clk_reg; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + switch (e->reg) { + case CDC_TX_INP_MUX_ADC_MUX0_CFG0: + mic_sel_reg = CDC_TX0_TX_PATH_CFG0; + break; + case CDC_TX_INP_MUX_ADC_MUX1_CFG0: + mic_sel_reg = CDC_TX1_TX_PATH_CFG0; + break; + case CDC_TX_INP_MUX_ADC_MUX2_CFG0: + mic_sel_reg = CDC_TX2_TX_PATH_CFG0; + break; + case CDC_TX_INP_MUX_ADC_MUX3_CFG0: + mic_sel_reg = CDC_TX3_TX_PATH_CFG0; + break; + case CDC_TX_INP_MUX_ADC_MUX4_CFG0: + mic_sel_reg = CDC_TX4_TX_PATH_CFG0; + break; + case CDC_TX_INP_MUX_ADC_MUX5_CFG0: + mic_sel_reg = CDC_TX5_TX_PATH_CFG0; + break; + case CDC_TX_INP_MUX_ADC_MUX6_CFG0: + mic_sel_reg = CDC_TX6_TX_PATH_CFG0; + break; + case CDC_TX_INP_MUX_ADC_MUX7_CFG0: + mic_sel_reg = CDC_TX7_TX_PATH_CFG0; + break; + default: + dev_err(component->dev, "Error in configuration!!\n"); + return -EINVAL; + } + + if (val != 0) { + if (widget->shift) { /* MSM DMIC */ + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 1); + } else if (val < 5) { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 0); + } else { + snd_soc_component_write_field(component, mic_sel_reg, + CDC_TXn_ADC_DMIC_SEL_MASK, 1); + dmic = TX_ADC_TO_DMIC(val); + dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); + snd_soc_component_write_field(component, dmic_clk_reg, + CDC_TX_SWR_DMIC_CLK_SEL_MASK, + tx->dmic_clk_div); + } + } + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tx_macro_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + u32 dai_id = widget->shift; + u32 dec_id = mc->shift; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + + if (test_bit(dec_id, &tx->active_ch_mask[dai_id])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int tx_macro_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct snd_soc_dapm_update *update = NULL; + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + u32 dai_id = widget->shift; + u32 dec_id = mc->shift; + u32 enable = ucontrol->value.integer.value[0]; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + + if (enable) { + if (tx->active_decimator[dai_id] == dec_id) + return 0; + + set_bit(dec_id, &tx->active_ch_mask[dai_id]); + tx->active_ch_cnt[dai_id]++; + tx->active_decimator[dai_id] = dec_id; + } else { + if (tx->active_decimator[dai_id] == -1) + return 0; + + tx->active_ch_cnt[dai_id]--; + clear_bit(dec_id, &tx->active_ch_mask[dai_id]); + tx->active_decimator[dai_id] = -1; + } + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 1; +} + +static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u8 decimator; + u16 tx_vol_ctl_reg, dec_cfg_reg, hpf_gate_reg, tx_gain_ctl_reg; + u8 hpf_cut_off_freq; + int hpf_delay = TX_MACRO_DMIC_HPF_DELAY_MS; + int unmute_delay = TX_MACRO_DMIC_UNMUTE_DELAY_MS; + u16 adc_mux_reg, adc_reg, adc_n, dmic; + u16 dmic_clk_reg; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + + decimator = w->shift; + tx_vol_ctl_reg = CDC_TXn_TX_PATH_CTL(decimator); + hpf_gate_reg = CDC_TXn_TX_PATH_SEC2(decimator); + dec_cfg_reg = CDC_TXn_TX_PATH_CFG0(decimator); + tx_gain_ctl_reg = CDC_TXn_TX_VOL_CTL(decimator); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + adc_mux_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG1(decimator); + if (snd_soc_component_read(component, adc_mux_reg) & SWR_MIC) { + adc_reg = CDC_TX_INP_MUX_ADC_MUXn_CFG0(decimator); + adc_n = snd_soc_component_read(component, adc_reg) & + CDC_TX_MACRO_SWR_MIC_MUX_SEL_MASK; + if (adc_n >= TX_ADC_MAX) { + dmic = TX_ADC_TO_DMIC(adc_n); + dmic_clk_reg = CDC_TX_TOP_CSR_SWR_DMICn_CTL(dmic); + + snd_soc_component_write_field(component, dmic_clk_reg, + CDC_TX_SWR_DMIC_CLK_SEL_MASK, + tx->dmic_clk_div); + } + } + snd_soc_component_write_field(component, dec_cfg_reg, + CDC_TXn_ADC_MODE_MASK, + tx->dec_mode[decimator]); + /* Enable TX PGA Mute */ + snd_soc_component_write_field(component, tx_vol_ctl_reg, + CDC_TXn_PGA_MUTE_MASK, 0x1); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write_field(component, tx_vol_ctl_reg, + CDC_TXn_CLK_EN_MASK, 0x1); + if (!is_amic_enabled(component, decimator)) { + snd_soc_component_update_bits(component, hpf_gate_reg, 0x01, 0x00); + /* Minimum 1 clk cycle delay is required as per HW spec */ + usleep_range(1000, 1010); + } + hpf_cut_off_freq = snd_soc_component_read_field(component, dec_cfg_reg, + CDC_TXn_HPF_CUT_FREQ_MASK); + + tx->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) + snd_soc_component_write_field(component, dec_cfg_reg, + CDC_TXn_HPF_CUT_FREQ_MASK, + CF_MIN_3DB_150HZ); + + if (is_amic_enabled(component, decimator)) { + hpf_delay = TX_MACRO_AMIC_HPF_DELAY_MS; + unmute_delay = TX_MACRO_AMIC_UNMUTE_DELAY_MS; + } + /* schedule work queue to Remove Mute */ + queue_delayed_work(system_freezable_wq, + &tx->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(unmute_delay)); + if (tx->tx_hpf_work[decimator].hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + queue_delayed_work(system_freezable_wq, + &tx->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(hpf_delay)); + snd_soc_component_update_bits(component, hpf_gate_reg, + CDC_TXn_HPF_F_CHANGE_MASK | + CDC_TXn_HPF_ZERO_GATE_MASK, + 0x02); + if (!is_amic_enabled(component, decimator)) + snd_soc_component_update_bits(component, hpf_gate_reg, + CDC_TXn_HPF_F_CHANGE_MASK | + CDC_TXn_HPF_ZERO_GATE_MASK, + 0x00); + snd_soc_component_update_bits(component, hpf_gate_reg, + CDC_TXn_HPF_F_CHANGE_MASK | + CDC_TXn_HPF_ZERO_GATE_MASK, + 0x01); + + /* + * 6ms delay is required as per HW spec + */ + usleep_range(6000, 6010); + } + /* apply gain after decimator is enabled */ + snd_soc_component_write(component, tx_gain_ctl_reg, + snd_soc_component_read(component, + tx_gain_ctl_reg)); + if (tx->bcs_enable) { + snd_soc_component_update_bits(component, dec_cfg_reg, + 0x01, 0x01); + tx->bcs_clk_en = true; + } + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + tx->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_component_write_field(component, tx_vol_ctl_reg, + CDC_TXn_PGA_MUTE_MASK, 0x1); + if (cancel_delayed_work_sync( + &tx->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_write_field( + component, dec_cfg_reg, + CDC_TXn_HPF_CUT_FREQ_MASK, + hpf_cut_off_freq); + if (is_amic_enabled(component, decimator)) + snd_soc_component_update_bits(component, + hpf_gate_reg, + CDC_TXn_HPF_F_CHANGE_MASK | + CDC_TXn_HPF_ZERO_GATE_MASK, + 0x02); + else + snd_soc_component_update_bits(component, + hpf_gate_reg, + CDC_TXn_HPF_F_CHANGE_MASK | + CDC_TXn_HPF_ZERO_GATE_MASK, + 0x03); + + /* + * Minimum 1 clk cycle delay is required + * as per HW spec + */ + usleep_range(1000, 1010); + snd_soc_component_update_bits(component, hpf_gate_reg, + CDC_TXn_HPF_F_CHANGE_MASK | + CDC_TXn_HPF_ZERO_GATE_MASK, + 0x1); + } + } + cancel_delayed_work_sync(&tx->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, tx_vol_ctl_reg, + CDC_TXn_CLK_EN_MASK, 0x0); + snd_soc_component_write_field(component, dec_cfg_reg, + CDC_TXn_ADC_MODE_MASK, 0x0); + snd_soc_component_write_field(component, tx_vol_ctl_reg, + CDC_TXn_PGA_MUTE_MASK, 0x0); + if (tx->bcs_enable) { + snd_soc_component_write_field(component, dec_cfg_reg, + CDC_TXn_PH_EN_MASK, 0x0); + snd_soc_component_write_field(component, + CDC_TX0_TX_PATH_SEC7, + CDC_TX0_MBHC_CTL_EN_MASK, + 0x0); + tx->bcs_clk_en = false; + } + break; + } + return 0; +} + +static int tx_macro_dec_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + ucontrol->value.integer.value[0] = tx->dec_mode[path]; + + return 0; +} + +static int tx_macro_dec_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int value = ucontrol->value.integer.value[0]; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + + if (tx->dec_mode[path] == value) + return 0; + + tx->dec_mode[path] = value; + + return 1; +} + +static int tx_macro_get_bcs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = tx->bcs_enable; + + return 0; +} + +static int tx_macro_set_bcs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int value = ucontrol->value.integer.value[0]; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + + tx->bcs_enable = value; + + return 0; +} + +static int tx_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u32 sample_rate; + u8 decimator; + int tx_fs_rate; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + + sample_rate = params_rate(params); + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(component->dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + } + + for_each_set_bit(decimator, &tx->active_ch_mask[dai->id], TX_MACRO_DEC_MAX) + snd_soc_component_update_bits(component, CDC_TXn_TX_PATH_CTL(decimator), + CDC_TXn_PCM_RATE_MASK, + tx_fs_rate); + return 0; +} + +static int tx_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_component *component = dai->component; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + + switch (dai->id) { + case TX_MACRO_AIF1_CAP: + case TX_MACRO_AIF2_CAP: + case TX_MACRO_AIF3_CAP: + *tx_slot = tx->active_ch_mask[dai->id]; + *tx_num = tx->active_ch_cnt[dai->id]; + break; + default: + break; + } + return 0; +} + +static int tx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct tx_macro *tx = snd_soc_component_get_drvdata(component); + u8 decimator; + + /* active decimator not set yet */ + if (tx->active_decimator[dai->id] == -1) + return 0; + + decimator = tx->active_decimator[dai->id]; + + if (mute) + snd_soc_component_write_field(component, + CDC_TXn_TX_PATH_CTL(decimator), + CDC_TXn_PGA_MUTE_MASK, 0x1); + else + snd_soc_component_update_bits(component, + CDC_TXn_TX_PATH_CTL(decimator), + CDC_TXn_PGA_MUTE_MASK, 0x0); + + return 0; +} + +static const struct snd_soc_dai_ops tx_macro_dai_ops = { + .hw_params = tx_macro_hw_params, + .get_channel_map = tx_macro_get_channel_map, + .mute_stream = tx_macro_digital_mute, +}; + +static struct snd_soc_dai_driver tx_macro_dai[] = { + { + .name = "tx_macro_tx1", + .id = TX_MACRO_AIF1_CAP, + .capture = { + .stream_name = "TX_AIF1 Capture", + .rates = TX_MACRO_RATES, + .formats = TX_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tx_macro_dai_ops, + }, + { + .name = "tx_macro_tx2", + .id = TX_MACRO_AIF2_CAP, + .capture = { + .stream_name = "TX_AIF2 Capture", + .rates = TX_MACRO_RATES, + .formats = TX_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tx_macro_dai_ops, + }, + { + .name = "tx_macro_tx3", + .id = TX_MACRO_AIF3_CAP, + .capture = { + .stream_name = "TX_AIF3 Capture", + .rates = TX_MACRO_RATES, + .formats = TX_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tx_macro_dai_ops, + }, +}; + +static const char * const adc_mux_text[] = { + "MSM_DMIC", "SWR_MIC", "ANC_FB_TUNE1" +}; + +static SOC_ENUM_SINGLE_DECL(tx_dec0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(tx_dec1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(tx_dec2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(tx_dec3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(tx_dec4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(tx_dec5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(tx_dec6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(tx_dec7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG1, + 0, adc_mux_text); + +static const struct snd_kcontrol_new tx_dec0_mux = SOC_DAPM_ENUM("tx_dec0", tx_dec0_enum); +static const struct snd_kcontrol_new tx_dec1_mux = SOC_DAPM_ENUM("tx_dec1", tx_dec1_enum); +static const struct snd_kcontrol_new tx_dec2_mux = SOC_DAPM_ENUM("tx_dec2", tx_dec2_enum); +static const struct snd_kcontrol_new tx_dec3_mux = SOC_DAPM_ENUM("tx_dec3", tx_dec3_enum); +static const struct snd_kcontrol_new tx_dec4_mux = SOC_DAPM_ENUM("tx_dec4", tx_dec4_enum); +static const struct snd_kcontrol_new tx_dec5_mux = SOC_DAPM_ENUM("tx_dec5", tx_dec5_enum); +static const struct snd_kcontrol_new tx_dec6_mux = SOC_DAPM_ENUM("tx_dec6", tx_dec6_enum); +static const struct snd_kcontrol_new tx_dec7_mux = SOC_DAPM_ENUM("tx_dec7", tx_dec7_enum); + +static const char * const smic_mux_text[] = { + "ZERO", "ADC0", "ADC1", "ADC2", "ADC3", "SWR_DMIC0", + "SWR_DMIC1", "SWR_DMIC2", "SWR_DMIC3", "SWR_DMIC4", + "SWR_DMIC5", "SWR_DMIC6", "SWR_DMIC7" +}; + +static SOC_ENUM_SINGLE_DECL(tx_smic0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 0, smic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_smic1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 0, smic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_smic2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 0, smic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_smic3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 0, smic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_smic4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 0, smic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_smic5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 0, smic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_smic6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 0, smic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_smic7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 0, smic_mux_text); + +static const struct snd_kcontrol_new tx_smic0_mux = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic1_mux = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic2_mux = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic3_mux = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic4_mux = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic5_mux = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic6_mux = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_smic7_mux = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "DMIC6", "DMIC7" +}; + +static SOC_ENUM_SINGLE_DECL(tx_dmic0_enum, CDC_TX_INP_MUX_ADC_MUX0_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_dmic1_enum, CDC_TX_INP_MUX_ADC_MUX1_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_dmic2_enum, CDC_TX_INP_MUX_ADC_MUX2_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_dmic3_enum, CDC_TX_INP_MUX_ADC_MUX3_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_dmic4_enum, CDC_TX_INP_MUX_ADC_MUX4_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_dmic5_enum, CDC_TX_INP_MUX_ADC_MUX5_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_dmic6_enum, CDC_TX_INP_MUX_ADC_MUX6_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(tx_dmic7_enum, CDC_TX_INP_MUX_ADC_MUX7_CFG0, + 4, dmic_mux_text); + +static const struct snd_kcontrol_new tx_dmic0_mux = SOC_DAPM_ENUM_EXT("tx_dmic0", tx_dmic0_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_dmic1_mux = SOC_DAPM_ENUM_EXT("tx_dmic1", tx_dmic1_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_dmic2_mux = SOC_DAPM_ENUM_EXT("tx_dmic2", tx_dmic2_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_dmic3_mux = SOC_DAPM_ENUM_EXT("tx_dmic3", tx_dmic3_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_dmic4_mux = SOC_DAPM_ENUM_EXT("tx_dmic4", tx_dmic4_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_dmic5_mux = SOC_DAPM_ENUM_EXT("tx_dmic5", tx_dmic5_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_dmic6_mux = SOC_DAPM_ENUM_EXT("tx_dmic6", tx_dmic6_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); +static const struct snd_kcontrol_new tx_dmic7_mux = SOC_DAPM_ENUM_EXT("tx_dmic7", tx_dmic7_enum, + snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum); + +static const char * const dec_mode_mux_text[] = { + "ADC_DEFAULT", "ADC_LOW_PWR", "ADC_HIGH_PERF", +}; + +static const struct soc_enum dec_mode_mux_enum[] = { + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 4, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 5, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 6, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 7, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), +}; + +static const struct snd_kcontrol_new tx_aif1_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), +}; + +static const struct snd_kcontrol_new tx_aif2_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), +}; + +static const struct snd_kcontrol_new tx_aif3_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, TX_MACRO_DEC1, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, TX_MACRO_DEC2, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, TX_MACRO_DEC3, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, TX_MACRO_DEC4, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, TX_MACRO_DEC5, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, TX_MACRO_DEC6, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, TX_MACRO_DEC7, 1, 0, + tx_macro_tx_mixer_get, tx_macro_tx_mixer_put), +}; + +static const struct snd_soc_dapm_widget tx_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("TX_AIF1 CAP", "TX_AIF1 Capture", 0, + SND_SOC_NOPM, TX_MACRO_AIF1_CAP, 0), + + SND_SOC_DAPM_AIF_OUT("TX_AIF2 CAP", "TX_AIF2 Capture", 0, + SND_SOC_NOPM, TX_MACRO_AIF2_CAP, 0), + + SND_SOC_DAPM_AIF_OUT("TX_AIF3 CAP", "TX_AIF3 Capture", 0, + SND_SOC_NOPM, TX_MACRO_AIF3_CAP, 0), + + SND_SOC_DAPM_MIXER("TX_AIF1_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF1_CAP, 0, + tx_aif1_cap_mixer, ARRAY_SIZE(tx_aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("TX_AIF2_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF2_CAP, 0, + tx_aif2_cap_mixer, ARRAY_SIZE(tx_aif2_cap_mixer)), + + SND_SOC_DAPM_MIXER("TX_AIF3_CAP Mixer", SND_SOC_NOPM, TX_MACRO_AIF3_CAP, 0, + tx_aif3_cap_mixer, ARRAY_SIZE(tx_aif3_cap_mixer)), + + SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux), + SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux), + SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux), + SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux), + SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux), + SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux), + SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux), + SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux), + + SND_SOC_DAPM_MUX("TX DMIC MUX0", SND_SOC_NOPM, 4, 0, &tx_dmic0_mux), + SND_SOC_DAPM_MUX("TX DMIC MUX1", SND_SOC_NOPM, 4, 0, &tx_dmic1_mux), + SND_SOC_DAPM_MUX("TX DMIC MUX2", SND_SOC_NOPM, 4, 0, &tx_dmic2_mux), + SND_SOC_DAPM_MUX("TX DMIC MUX3", SND_SOC_NOPM, 4, 0, &tx_dmic3_mux), + SND_SOC_DAPM_MUX("TX DMIC MUX4", SND_SOC_NOPM, 4, 0, &tx_dmic4_mux), + SND_SOC_DAPM_MUX("TX DMIC MUX5", SND_SOC_NOPM, 4, 0, &tx_dmic5_mux), + SND_SOC_DAPM_MUX("TX DMIC MUX6", SND_SOC_NOPM, 4, 0, &tx_dmic6_mux), + SND_SOC_DAPM_MUX("TX DMIC MUX7", SND_SOC_NOPM, 4, 0, &tx_dmic7_mux), + + SND_SOC_DAPM_INPUT("TX SWR_ADC0"), + SND_SOC_DAPM_INPUT("TX SWR_ADC1"), + SND_SOC_DAPM_INPUT("TX SWR_ADC2"), + SND_SOC_DAPM_INPUT("TX SWR_ADC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC0"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC1"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC2"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC3"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC4"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC5"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC6"), + SND_SOC_DAPM_INPUT("TX SWR_DMIC7"), + SND_SOC_DAPM_INPUT("TX DMIC0"), + SND_SOC_DAPM_INPUT("TX DMIC1"), + SND_SOC_DAPM_INPUT("TX DMIC2"), + SND_SOC_DAPM_INPUT("TX DMIC3"), + SND_SOC_DAPM_INPUT("TX DMIC4"), + SND_SOC_DAPM_INPUT("TX DMIC5"), + SND_SOC_DAPM_INPUT("TX DMIC6"), + SND_SOC_DAPM_INPUT("TX DMIC7"), + + SND_SOC_DAPM_MUX_E("TX DEC0 MUX", SND_SOC_NOPM, + TX_MACRO_DEC0, 0, + &tx_dec0_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC1 MUX", SND_SOC_NOPM, + TX_MACRO_DEC1, 0, + &tx_dec1_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC2 MUX", SND_SOC_NOPM, + TX_MACRO_DEC2, 0, + &tx_dec2_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC3 MUX", SND_SOC_NOPM, + TX_MACRO_DEC3, 0, + &tx_dec3_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC4 MUX", SND_SOC_NOPM, + TX_MACRO_DEC4, 0, + &tx_dec4_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC5 MUX", SND_SOC_NOPM, + TX_MACRO_DEC5, 0, + &tx_dec5_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC6 MUX", SND_SOC_NOPM, + TX_MACRO_DEC6, 0, + &tx_dec6_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("TX DEC7 MUX", SND_SOC_NOPM, + TX_MACRO_DEC7, 0, + &tx_dec7_mux, tx_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("TX_MCLK", 0, SND_SOC_NOPM, 0, 0, + tx_macro_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("TX_SWR_CLK", 0, SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("VA_SWR_CLK", 0, SND_SOC_NOPM, 0, 0, + NULL, 0), +}; + +static const struct snd_soc_dapm_route tx_audio_map[] = { + {"TX_AIF1 CAP", NULL, "TX_MCLK"}, + {"TX_AIF2 CAP", NULL, "TX_MCLK"}, + {"TX_AIF3 CAP", NULL, "TX_MCLK"}, + + {"TX_AIF1 CAP", NULL, "TX_AIF1_CAP Mixer"}, + {"TX_AIF2 CAP", NULL, "TX_AIF2_CAP Mixer"}, + {"TX_AIF3 CAP", NULL, "TX_AIF3_CAP Mixer"}, + + {"TX_AIF1_CAP Mixer", "DEC0", "TX DEC0 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC1", "TX DEC1 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC2", "TX DEC2 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC3", "TX DEC3 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC4", "TX DEC4 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC5", "TX DEC5 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC6", "TX DEC6 MUX"}, + {"TX_AIF1_CAP Mixer", "DEC7", "TX DEC7 MUX"}, + + {"TX_AIF2_CAP Mixer", "DEC0", "TX DEC0 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC1", "TX DEC1 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC2", "TX DEC2 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC3", "TX DEC3 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC4", "TX DEC4 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC5", "TX DEC5 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC6", "TX DEC6 MUX"}, + {"TX_AIF2_CAP Mixer", "DEC7", "TX DEC7 MUX"}, + + {"TX_AIF3_CAP Mixer", "DEC0", "TX DEC0 MUX"}, + {"TX_AIF3_CAP Mixer", "DEC1", "TX DEC1 MUX"}, + {"TX_AIF3_CAP Mixer", "DEC2", "TX DEC2 MUX"}, + {"TX_AIF3_CAP Mixer", "DEC3", "TX DEC3 MUX"}, + {"TX_AIF3_CAP Mixer", "DEC4", "TX DEC4 MUX"}, + {"TX_AIF3_CAP Mixer", "DEC5", "TX DEC5 MUX"}, + {"TX_AIF3_CAP Mixer", "DEC6", "TX DEC6 MUX"}, + {"TX_AIF3_CAP Mixer", "DEC7", "TX DEC7 MUX"}, + + {"TX DEC0 MUX", NULL, "TX_MCLK"}, + {"TX DEC1 MUX", NULL, "TX_MCLK"}, + {"TX DEC2 MUX", NULL, "TX_MCLK"}, + {"TX DEC3 MUX", NULL, "TX_MCLK"}, + {"TX DEC4 MUX", NULL, "TX_MCLK"}, + {"TX DEC5 MUX", NULL, "TX_MCLK"}, + {"TX DEC6 MUX", NULL, "TX_MCLK"}, + {"TX DEC7 MUX", NULL, "TX_MCLK"}, + + {"TX DEC0 MUX", "MSM_DMIC", "TX DMIC MUX0"}, + {"TX DMIC MUX0", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX0", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX0", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX0", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX0", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX0", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX0", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX0", "DMIC7", "TX DMIC7"}, + + {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"}, + {"TX SMIC MUX0", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX0", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX0", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX0", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX0", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX0", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX0", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX0", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX0", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX0", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX0", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX0", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX0", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC1 MUX", "MSM_DMIC", "TX DMIC MUX1"}, + {"TX DMIC MUX1", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX1", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX1", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX1", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX1", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX1", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX1", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX1", "DMIC7", "TX DMIC7"}, + + {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"}, + {"TX SMIC MUX1", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX1", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX1", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX1", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX1", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX1", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX1", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX1", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX1", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX1", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX1", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX1", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX1", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC2 MUX", "MSM_DMIC", "TX DMIC MUX2"}, + {"TX DMIC MUX2", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX2", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX2", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX2", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX2", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX2", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX2", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX2", "DMIC7", "TX DMIC7"}, + + {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"}, + {"TX SMIC MUX2", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX2", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX2", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX2", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX2", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX2", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX2", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX2", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX2", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX2", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX2", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX2", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX2", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC3 MUX", "MSM_DMIC", "TX DMIC MUX3"}, + {"TX DMIC MUX3", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX3", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX3", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX3", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX3", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX3", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX3", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX3", "DMIC7", "TX DMIC7"}, + + {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"}, + {"TX SMIC MUX3", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX3", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX3", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX3", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX3", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX3", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX3", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX3", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX3", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX3", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX3", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX3", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX3", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC4 MUX", "MSM_DMIC", "TX DMIC MUX4"}, + {"TX DMIC MUX4", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX4", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX4", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX4", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX4", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX4", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX4", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX4", "DMIC7", "TX DMIC7"}, + + {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"}, + {"TX SMIC MUX4", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX4", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX4", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX4", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX4", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX4", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX4", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX4", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX4", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX4", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX4", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX4", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX4", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC5 MUX", "MSM_DMIC", "TX DMIC MUX5"}, + {"TX DMIC MUX5", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX5", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX5", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX5", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX5", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX5", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX5", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX5", "DMIC7", "TX DMIC7"}, + + {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"}, + {"TX SMIC MUX5", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX5", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX5", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX5", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX5", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX5", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX5", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX5", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX5", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX5", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX5", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX5", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX5", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC6 MUX", "MSM_DMIC", "TX DMIC MUX6"}, + {"TX DMIC MUX6", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX6", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX6", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX6", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX6", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX6", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX6", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX6", "DMIC7", "TX DMIC7"}, + + {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"}, + {"TX SMIC MUX6", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX6", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX6", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX6", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX6", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX6", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX6", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX6", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX6", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX6", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX6", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX6", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX6", "SWR_DMIC7", "TX SWR_DMIC7"}, + + {"TX DEC7 MUX", "MSM_DMIC", "TX DMIC MUX7"}, + {"TX DMIC MUX7", "DMIC0", "TX DMIC0"}, + {"TX DMIC MUX7", "DMIC1", "TX DMIC1"}, + {"TX DMIC MUX7", "DMIC2", "TX DMIC2"}, + {"TX DMIC MUX7", "DMIC3", "TX DMIC3"}, + {"TX DMIC MUX7", "DMIC4", "TX DMIC4"}, + {"TX DMIC MUX7", "DMIC5", "TX DMIC5"}, + {"TX DMIC MUX7", "DMIC6", "TX DMIC6"}, + {"TX DMIC MUX7", "DMIC7", "TX DMIC7"}, + + {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"}, + {"TX SMIC MUX7", NULL, "TX_SWR_CLK"}, + {"TX SMIC MUX7", "ADC0", "TX SWR_ADC0"}, + {"TX SMIC MUX7", "ADC1", "TX SWR_ADC1"}, + {"TX SMIC MUX7", "ADC2", "TX SWR_ADC2"}, + {"TX SMIC MUX7", "ADC3", "TX SWR_ADC3"}, + {"TX SMIC MUX7", "SWR_DMIC0", "TX SWR_DMIC0"}, + {"TX SMIC MUX7", "SWR_DMIC1", "TX SWR_DMIC1"}, + {"TX SMIC MUX7", "SWR_DMIC2", "TX SWR_DMIC2"}, + {"TX SMIC MUX7", "SWR_DMIC3", "TX SWR_DMIC3"}, + {"TX SMIC MUX7", "SWR_DMIC4", "TX SWR_DMIC4"}, + {"TX SMIC MUX7", "SWR_DMIC5", "TX SWR_DMIC5"}, + {"TX SMIC MUX7", "SWR_DMIC6", "TX SWR_DMIC6"}, + {"TX SMIC MUX7", "SWR_DMIC7", "TX SWR_DMIC7"}, +}; + +static const struct snd_kcontrol_new tx_macro_snd_controls[] = { + SOC_SINGLE_S8_TLV("TX_DEC0 Volume", + CDC_TX0_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("TX_DEC1 Volume", + CDC_TX1_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("TX_DEC2 Volume", + CDC_TX2_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("TX_DEC3 Volume", + CDC_TX3_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("TX_DEC4 Volume", + CDC_TX4_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("TX_DEC5 Volume", + CDC_TX5_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("TX_DEC6 Volume", + CDC_TX6_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("TX_DEC7 Volume", + CDC_TX7_TX_VOL_CTL, + -84, 40, digital_gain), + + SOC_ENUM_EXT("DEC0 MODE", dec_mode_mux_enum[0], + tx_macro_dec_mode_get, tx_macro_dec_mode_put), + + SOC_ENUM_EXT("DEC1 MODE", dec_mode_mux_enum[1], + tx_macro_dec_mode_get, tx_macro_dec_mode_put), + + SOC_ENUM_EXT("DEC2 MODE", dec_mode_mux_enum[2], + tx_macro_dec_mode_get, tx_macro_dec_mode_put), + + SOC_ENUM_EXT("DEC3 MODE", dec_mode_mux_enum[3], + tx_macro_dec_mode_get, tx_macro_dec_mode_put), + + SOC_ENUM_EXT("DEC4 MODE", dec_mode_mux_enum[4], + tx_macro_dec_mode_get, tx_macro_dec_mode_put), + + SOC_ENUM_EXT("DEC5 MODE", dec_mode_mux_enum[5], + tx_macro_dec_mode_get, tx_macro_dec_mode_put), + + SOC_ENUM_EXT("DEC6 MODE", dec_mode_mux_enum[6], + tx_macro_dec_mode_get, tx_macro_dec_mode_put), + + SOC_ENUM_EXT("DEC7 MODE", dec_mode_mux_enum[7], + tx_macro_dec_mode_get, tx_macro_dec_mode_put), + + SOC_SINGLE_EXT("DEC0_BCS Switch", SND_SOC_NOPM, 0, 1, 0, + tx_macro_get_bcs, tx_macro_set_bcs), +}; + +static int tx_macro_component_probe(struct snd_soc_component *comp) +{ + struct tx_macro *tx = snd_soc_component_get_drvdata(comp); + int i; + + snd_soc_component_init_regmap(comp, tx->regmap); + + for (i = 0; i < NUM_DECIMATORS; i++) { + tx->tx_hpf_work[i].tx = tx; + tx->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&tx->tx_hpf_work[i].dwork, + tx_macro_tx_hpf_corner_freq_callback); + } + + for (i = 0; i < NUM_DECIMATORS; i++) { + tx->tx_mute_dwork[i].tx = tx; + tx->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&tx->tx_mute_dwork[i].dwork, + tx_macro_mute_update_callback); + } + tx->component = comp; + + snd_soc_component_update_bits(comp, CDC_TX0_TX_PATH_SEC7, 0x3F, + 0x0A); + /* Enable swr mic0 and mic1 clock */ + snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL, 0xFF, 0x00); + snd_soc_component_update_bits(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL, 0xFF, 0x00); + + return 0; +} + +static int swclk_gate_enable(struct clk_hw *hw) +{ + struct tx_macro *tx = to_tx_macro(hw); + struct regmap *regmap = tx->regmap; + int ret; + + ret = clk_prepare_enable(tx->mclk); + if (ret) { + dev_err(tx->dev, "failed to enable mclk\n"); + return ret; + } + + tx_macro_mclk_enable(tx, true); + + regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + CDC_TX_SWR_CLK_EN_MASK, + CDC_TX_SWR_CLK_ENABLE); + return 0; +} + +static void swclk_gate_disable(struct clk_hw *hw) +{ + struct tx_macro *tx = to_tx_macro(hw); + struct regmap *regmap = tx->regmap; + + regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + CDC_TX_SWR_CLK_EN_MASK, 0x0); + + tx_macro_mclk_enable(tx, false); + clk_disable_unprepare(tx->mclk); +} + +static int swclk_gate_is_enabled(struct clk_hw *hw) +{ + struct tx_macro *tx = to_tx_macro(hw); + int ret, val; + + regmap_read(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, &val); + ret = val & BIT(0); + + return ret; +} + +static unsigned long swclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate / 2; +} + +static const struct clk_ops swclk_gate_ops = { + .prepare = swclk_gate_enable, + .unprepare = swclk_gate_disable, + .is_enabled = swclk_gate_is_enabled, + .recalc_rate = swclk_recalc_rate, + +}; + +static int tx_macro_register_mclk_output(struct tx_macro *tx) +{ + struct device *dev = tx->dev; + const char *parent_clk_name = NULL; + const char *clk_name = "lpass-tx-mclk"; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (tx->npl) + parent_clk_name = __clk_get_name(tx->npl); + else + parent_clk_name = __clk_get_name(tx->mclk); + + init.name = clk_name; + init.ops = &swclk_gate_ops; + init.flags = 0; + init.parent_names = &parent_clk_name; + init.num_parents = 1; + tx->hw.init = &init; + hw = &tx->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); +} + +static const struct snd_soc_component_driver tx_macro_component_drv = { + .name = "RX-MACRO", + .probe = tx_macro_component_probe, + .controls = tx_macro_snd_controls, + .num_controls = ARRAY_SIZE(tx_macro_snd_controls), + .dapm_widgets = tx_macro_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tx_macro_dapm_widgets), + .dapm_routes = tx_audio_map, + .num_dapm_routes = ARRAY_SIZE(tx_audio_map), +}; + +static int tx_macro_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + kernel_ulong_t flags; + struct tx_macro *tx; + void __iomem *base; + int ret, reg; + + flags = (kernel_ulong_t)device_get_match_data(dev); + + tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL); + if (!tx) + return -ENOMEM; + + tx->macro = devm_clk_get_optional(dev, "macro"); + if (IS_ERR(tx->macro)) + return dev_err_probe(dev, PTR_ERR(tx->macro), "unable to get macro clock\n"); + + tx->dcodec = devm_clk_get_optional(dev, "dcodec"); + if (IS_ERR(tx->dcodec)) + return dev_err_probe(dev, PTR_ERR(tx->dcodec), "unable to get dcodec clock\n"); + + tx->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(tx->mclk)) + return dev_err_probe(dev, PTR_ERR(tx->mclk), "unable to get mclk clock\n"); + + if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { + tx->npl = devm_clk_get(dev, "npl"); + if (IS_ERR(tx->npl)) + return dev_err_probe(dev, PTR_ERR(tx->npl), "unable to get npl clock\n"); + } + + tx->fsgen = devm_clk_get(dev, "fsgen"); + if (IS_ERR(tx->fsgen)) + return dev_err_probe(dev, PTR_ERR(tx->fsgen), "unable to get fsgen clock\n"); + + tx->pds = lpass_macro_pds_init(dev); + if (IS_ERR(tx->pds)) + return PTR_ERR(tx->pds); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto err; + } + + /* Update defaults for lpass sc7280 */ + if (of_device_is_compatible(np, "qcom,sc7280-lpass-tx-macro")) { + for (reg = 0; reg < ARRAY_SIZE(tx_defaults); reg++) { + switch (tx_defaults[reg].reg) { + case CDC_TX_TOP_CSR_SWR_AMIC0_CTL: + case CDC_TX_TOP_CSR_SWR_AMIC1_CTL: + tx_defaults[reg].def = 0x0E; + break; + default: + break; + } + } + } + + tx->regmap = devm_regmap_init_mmio(dev, base, &tx_regmap_config); + if (IS_ERR(tx->regmap)) { + ret = PTR_ERR(tx->regmap); + goto err; + } + + dev_set_drvdata(dev, tx); + + tx->dev = dev; + + /* Set active_decimator default value */ + tx->active_decimator[TX_MACRO_AIF1_CAP] = -1; + tx->active_decimator[TX_MACRO_AIF2_CAP] = -1; + tx->active_decimator[TX_MACRO_AIF3_CAP] = -1; + + /* set MCLK and NPL rates */ + clk_set_rate(tx->mclk, MCLK_FREQ); + clk_set_rate(tx->npl, MCLK_FREQ); + + ret = clk_prepare_enable(tx->macro); + if (ret) + goto err; + + ret = clk_prepare_enable(tx->dcodec); + if (ret) + goto err_dcodec; + + ret = clk_prepare_enable(tx->mclk); + if (ret) + goto err_mclk; + + ret = clk_prepare_enable(tx->npl); + if (ret) + goto err_npl; + + ret = clk_prepare_enable(tx->fsgen); + if (ret) + goto err_fsgen; + + /* reset soundwire block */ + regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE); + + regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + CDC_TX_SWR_CLK_EN_MASK, + CDC_TX_SWR_CLK_ENABLE); + regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL, + CDC_TX_SWR_RESET_MASK, 0x0); + + ret = devm_snd_soc_register_component(dev, &tx_macro_component_drv, + tx_macro_dai, + ARRAY_SIZE(tx_macro_dai)); + if (ret) + goto err_clkout; + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = tx_macro_register_mclk_output(tx); + if (ret) + goto err_clkout; + + return 0; + +err_clkout: + clk_disable_unprepare(tx->fsgen); +err_fsgen: + clk_disable_unprepare(tx->npl); +err_npl: + clk_disable_unprepare(tx->mclk); +err_mclk: + clk_disable_unprepare(tx->dcodec); +err_dcodec: + clk_disable_unprepare(tx->macro); +err: + lpass_macro_pds_exit(tx->pds); + + return ret; +} + +static void tx_macro_remove(struct platform_device *pdev) +{ + struct tx_macro *tx = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(tx->macro); + clk_disable_unprepare(tx->dcodec); + clk_disable_unprepare(tx->mclk); + clk_disable_unprepare(tx->npl); + clk_disable_unprepare(tx->fsgen); + + lpass_macro_pds_exit(tx->pds); +} + +static int __maybe_unused tx_macro_runtime_suspend(struct device *dev) +{ + struct tx_macro *tx = dev_get_drvdata(dev); + + regcache_cache_only(tx->regmap, true); + regcache_mark_dirty(tx->regmap); + + clk_disable_unprepare(tx->fsgen); + clk_disable_unprepare(tx->npl); + clk_disable_unprepare(tx->mclk); + + return 0; +} + +static int __maybe_unused tx_macro_runtime_resume(struct device *dev) +{ + struct tx_macro *tx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(tx->mclk); + if (ret) { + dev_err(dev, "unable to prepare mclk\n"); + return ret; + } + + ret = clk_prepare_enable(tx->npl); + if (ret) { + dev_err(dev, "unable to prepare npl\n"); + goto err_npl; + } + + ret = clk_prepare_enable(tx->fsgen); + if (ret) { + dev_err(dev, "unable to prepare fsgen\n"); + goto err_fsgen; + } + + regcache_cache_only(tx->regmap, false); + regcache_sync(tx->regmap); + + return 0; +err_fsgen: + clk_disable_unprepare(tx->npl); +err_npl: + clk_disable_unprepare(tx->mclk); + + return ret; +} + +static const struct dev_pm_ops tx_macro_pm_ops = { + SET_RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL) +}; + +static const struct of_device_id tx_macro_dt_match[] = { + { + .compatible = "qcom,sc7280-lpass-tx-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, { + .compatible = "qcom,sm8250-lpass-tx-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, { + .compatible = "qcom,sm8450-lpass-tx-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, { + .compatible = "qcom,sm8550-lpass-tx-macro", + }, { + .compatible = "qcom,sc8280xp-lpass-tx-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, + { } +}; +MODULE_DEVICE_TABLE(of, tx_macro_dt_match); +static struct platform_driver tx_macro_driver = { + .driver = { + .name = "tx_macro", + .of_match_table = tx_macro_dt_match, + .suppress_bind_attrs = true, + .pm = &tx_macro_pm_ops, + }, + .probe = tx_macro_probe, + .remove_new = tx_macro_remove, +}; + +module_platform_driver(tx_macro_driver); + +MODULE_DESCRIPTION("TX macro driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c new file mode 100644 index 0000000000..b71ef03c4a --- /dev/null +++ b/sound/soc/codecs/lpass-va-macro.c @@ -0,0 +1,1645 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lpass-macro-common.h" + +/* VA macro registers */ +#define CDC_VA_CLK_RST_CTRL_MCLK_CONTROL (0x0000) +#define CDC_VA_MCLK_CONTROL_EN BIT(0) +#define CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004) +#define CDC_VA_FS_CONTROL_EN BIT(0) +#define CDC_VA_FS_COUNTER_CLR BIT(1) +#define CDC_VA_CLK_RST_CTRL_SWR_CONTROL (0x0008) +#define CDC_VA_SWR_RESET_MASK BIT(1) +#define CDC_VA_SWR_RESET_ENABLE BIT(1) +#define CDC_VA_SWR_CLK_EN_MASK BIT(0) +#define CDC_VA_SWR_CLK_ENABLE BIT(0) +#define CDC_VA_TOP_CSR_TOP_CFG0 (0x0080) +#define CDC_VA_FS_BROADCAST_EN BIT(1) +#define CDC_VA_TOP_CSR_DMIC0_CTL (0x0084) +#define CDC_VA_TOP_CSR_DMIC1_CTL (0x0088) +#define CDC_VA_TOP_CSR_DMIC2_CTL (0x008C) +#define CDC_VA_TOP_CSR_DMIC3_CTL (0x0090) +#define CDC_VA_DMIC_EN_MASK BIT(0) +#define CDC_VA_DMIC_ENABLE BIT(0) +#define CDC_VA_DMIC_CLK_SEL_MASK GENMASK(3, 1) +#define CDC_VA_DMIC_CLK_SEL_SHFT 1 +#define CDC_VA_DMIC_CLK_SEL_DIV0 0x0 +#define CDC_VA_DMIC_CLK_SEL_DIV1 0x2 +#define CDC_VA_DMIC_CLK_SEL_DIV2 0x4 +#define CDC_VA_DMIC_CLK_SEL_DIV3 0x6 +#define CDC_VA_DMIC_CLK_SEL_DIV4 0x8 +#define CDC_VA_DMIC_CLK_SEL_DIV5 0xa +#define CDC_VA_TOP_CSR_DMIC_CFG (0x0094) +#define CDC_VA_RESET_ALL_DMICS_MASK BIT(7) +#define CDC_VA_RESET_ALL_DMICS_RESET BIT(7) +#define CDC_VA_RESET_ALL_DMICS_DISABLE 0 +#define CDC_VA_DMIC3_FREQ_CHANGE_MASK BIT(3) +#define CDC_VA_DMIC3_FREQ_CHANGE_EN BIT(3) +#define CDC_VA_DMIC2_FREQ_CHANGE_MASK BIT(2) +#define CDC_VA_DMIC2_FREQ_CHANGE_EN BIT(2) +#define CDC_VA_DMIC1_FREQ_CHANGE_MASK BIT(1) +#define CDC_VA_DMIC1_FREQ_CHANGE_EN BIT(1) +#define CDC_VA_DMIC0_FREQ_CHANGE_MASK BIT(0) +#define CDC_VA_DMIC0_FREQ_CHANGE_EN BIT(0) +#define CDC_VA_DMIC_FREQ_CHANGE_DISABLE 0 +#define CDC_VA_TOP_CSR_DEBUG_BUS (0x009C) +#define CDC_VA_TOP_CSR_DEBUG_EN (0x00A0) +#define CDC_VA_TOP_CSR_TX_I2S_CTL (0x00A4) +#define CDC_VA_TOP_CSR_I2S_CLK (0x00A8) +#define CDC_VA_TOP_CSR_I2S_RESET (0x00AC) +#define CDC_VA_TOP_CSR_CORE_ID_0 (0x00C0) +#define CDC_VA_TOP_CSR_CORE_ID_1 (0x00C4) +#define CDC_VA_TOP_CSR_CORE_ID_2 (0x00C8) +#define CDC_VA_TOP_CSR_CORE_ID_3 (0x00CC) +#define CDC_VA_TOP_CSR_SWR_MIC_CTL0 (0x00D0) +#define CDC_VA_TOP_CSR_SWR_MIC_CTL1 (0x00D4) +#define CDC_VA_TOP_CSR_SWR_MIC_CTL2 (0x00D8) +#define CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK (0xEE) +#define CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1 (0xCC) +#define CDC_VA_TOP_CSR_SWR_CTRL (0x00DC) +#define CDC_VA_INP_MUX_ADC_MUX0_CFG0 (0x0100) +#define CDC_VA_INP_MUX_ADC_MUX0_CFG1 (0x0104) +#define CDC_VA_INP_MUX_ADC_MUX1_CFG0 (0x0108) +#define CDC_VA_INP_MUX_ADC_MUX1_CFG1 (0x010C) +#define CDC_VA_INP_MUX_ADC_MUX2_CFG0 (0x0110) +#define CDC_VA_INP_MUX_ADC_MUX2_CFG1 (0x0114) +#define CDC_VA_INP_MUX_ADC_MUX3_CFG0 (0x0118) +#define CDC_VA_INP_MUX_ADC_MUX3_CFG1 (0x011C) +#define CDC_VA_TX0_TX_PATH_CTL (0x0400) +#define CDC_VA_TX_PATH_CLK_EN_MASK BIT(5) +#define CDC_VA_TX_PATH_CLK_EN BIT(5) +#define CDC_VA_TX_PATH_CLK_DISABLE 0 +#define CDC_VA_TX_PATH_PGA_MUTE_EN_MASK BIT(4) +#define CDC_VA_TX_PATH_PGA_MUTE_EN BIT(4) +#define CDC_VA_TX_PATH_PGA_MUTE_DISABLE 0 +#define CDC_VA_TX0_TX_PATH_CFG0 (0x0404) +#define CDC_VA_ADC_MODE_MASK GENMASK(2, 1) +#define CDC_VA_ADC_MODE_SHIFT 1 +#define TX_HPF_CUT_OFF_FREQ_MASK GENMASK(6, 5) +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 +#define CDC_VA_TX0_TX_PATH_CFG1 (0x0408) +#define CDC_VA_TX0_TX_VOL_CTL (0x040C) +#define CDC_VA_TX0_TX_PATH_SEC0 (0x0410) +#define CDC_VA_TX0_TX_PATH_SEC1 (0x0414) +#define CDC_VA_TX0_TX_PATH_SEC2 (0x0418) +#define CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK BIT(1) +#define CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_REQ BIT(1) +#define CDC_VA_TX_HPF_ZERO_GATE_MASK BIT(0) +#define CDC_VA_TX_HPF_ZERO_NO_GATE BIT(0) +#define CDC_VA_TX_HPF_ZERO_GATE 0 +#define CDC_VA_TX0_TX_PATH_SEC3 (0x041C) +#define CDC_VA_TX0_TX_PATH_SEC4 (0x0420) +#define CDC_VA_TX0_TX_PATH_SEC5 (0x0424) +#define CDC_VA_TX0_TX_PATH_SEC6 (0x0428) +#define CDC_VA_TX0_TX_PATH_SEC7 (0x042C) +#define CDC_VA_TX1_TX_PATH_CTL (0x0480) +#define CDC_VA_TX1_TX_PATH_CFG0 (0x0484) +#define CDC_VA_TX1_TX_PATH_CFG1 (0x0488) +#define CDC_VA_TX1_TX_VOL_CTL (0x048C) +#define CDC_VA_TX1_TX_PATH_SEC0 (0x0490) +#define CDC_VA_TX1_TX_PATH_SEC1 (0x0494) +#define CDC_VA_TX1_TX_PATH_SEC2 (0x0498) +#define CDC_VA_TX1_TX_PATH_SEC3 (0x049C) +#define CDC_VA_TX1_TX_PATH_SEC4 (0x04A0) +#define CDC_VA_TX1_TX_PATH_SEC5 (0x04A4) +#define CDC_VA_TX1_TX_PATH_SEC6 (0x04A8) +#define CDC_VA_TX2_TX_PATH_CTL (0x0500) +#define CDC_VA_TX2_TX_PATH_CFG0 (0x0504) +#define CDC_VA_TX2_TX_PATH_CFG1 (0x0508) +#define CDC_VA_TX2_TX_VOL_CTL (0x050C) +#define CDC_VA_TX2_TX_PATH_SEC0 (0x0510) +#define CDC_VA_TX2_TX_PATH_SEC1 (0x0514) +#define CDC_VA_TX2_TX_PATH_SEC2 (0x0518) +#define CDC_VA_TX2_TX_PATH_SEC3 (0x051C) +#define CDC_VA_TX2_TX_PATH_SEC4 (0x0520) +#define CDC_VA_TX2_TX_PATH_SEC5 (0x0524) +#define CDC_VA_TX2_TX_PATH_SEC6 (0x0528) +#define CDC_VA_TX3_TX_PATH_CTL (0x0580) +#define CDC_VA_TX3_TX_PATH_CFG0 (0x0584) +#define CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK BIT(7) +#define CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC BIT(7) +#define CDC_VA_TX_PATH_ADC_DMIC_SEL_ADC 0 +#define CDC_VA_TX3_TX_PATH_CFG1 (0x0588) +#define CDC_VA_TX3_TX_VOL_CTL (0x058C) +#define CDC_VA_TX3_TX_PATH_SEC0 (0x0590) +#define CDC_VA_TX3_TX_PATH_SEC1 (0x0594) +#define CDC_VA_TX3_TX_PATH_SEC2 (0x0598) +#define CDC_VA_TX3_TX_PATH_SEC3 (0x059C) +#define CDC_VA_TX3_TX_PATH_SEC4 (0x05A0) +#define CDC_VA_TX3_TX_PATH_SEC5 (0x05A4) +#define CDC_VA_TX3_TX_PATH_SEC6 (0x05A8) + +#define VA_MAX_OFFSET (0x07A8) + +#define VA_MACRO_NUM_DECIMATORS 4 +#define VA_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define VA_MACRO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define VA_MACRO_MCLK_FREQ 9600000 +#define VA_MACRO_TX_PATH_OFFSET 0x80 +#define VA_MACRO_SWR_MIC_MUX_SEL_MASK 0xF +#define VA_MACRO_ADC_MUX_CFG_OFFSET 0x8 + +static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); + +enum { + VA_MACRO_AIF_INVALID = 0, + VA_MACRO_AIF1_CAP, + VA_MACRO_AIF2_CAP, + VA_MACRO_AIF3_CAP, + VA_MACRO_MAX_DAIS, +}; + +enum { + VA_MACRO_DEC0, + VA_MACRO_DEC1, + VA_MACRO_DEC2, + VA_MACRO_DEC3, + VA_MACRO_DEC4, + VA_MACRO_DEC5, + VA_MACRO_DEC6, + VA_MACRO_DEC7, + VA_MACRO_DEC_MAX, +}; + +enum { + VA_MACRO_CLK_DIV_2, + VA_MACRO_CLK_DIV_3, + VA_MACRO_CLK_DIV_4, + VA_MACRO_CLK_DIV_6, + VA_MACRO_CLK_DIV_8, + VA_MACRO_CLK_DIV_16, +}; + +#define VA_NUM_CLKS_MAX 3 + +struct va_macro { + struct device *dev; + unsigned long active_ch_mask[VA_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[VA_MACRO_MAX_DAIS]; + u16 dmic_clk_div; + bool has_swr_master; + + int dec_mode[VA_MACRO_NUM_DECIMATORS]; + struct regmap *regmap; + struct clk *mclk; + struct clk *macro; + struct clk *dcodec; + struct clk *fsgen; + struct clk_hw hw; + struct lpass_macro *pds; + + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 dmic_6_7_clk_cnt; + u8 dmic_0_1_clk_div; + u8 dmic_2_3_clk_div; + u8 dmic_4_5_clk_div; + u8 dmic_6_7_clk_div; +}; + +#define to_va_macro(_hw) container_of(_hw, struct va_macro, hw) + +struct va_macro_data { + bool has_swr_master; +}; + +static const struct va_macro_data sm8250_va_data = { + .has_swr_master = false, +}; + +static const struct va_macro_data sm8450_va_data = { + .has_swr_master = true, +}; + +static bool va_is_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_VA_TOP_CSR_CORE_ID_0: + case CDC_VA_TOP_CSR_CORE_ID_1: + case CDC_VA_TOP_CSR_CORE_ID_2: + case CDC_VA_TOP_CSR_CORE_ID_3: + case CDC_VA_TOP_CSR_DMIC0_CTL: + case CDC_VA_TOP_CSR_DMIC1_CTL: + case CDC_VA_TOP_CSR_DMIC2_CTL: + case CDC_VA_TOP_CSR_DMIC3_CTL: + return true; + } + return false; +} + +static const struct reg_default va_defaults[] = { + /* VA macro */ + { CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, 0x00}, + { CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00}, + { CDC_VA_CLK_RST_CTRL_SWR_CONTROL, 0x00}, + { CDC_VA_TOP_CSR_TOP_CFG0, 0x00}, + { CDC_VA_TOP_CSR_DMIC0_CTL, 0x00}, + { CDC_VA_TOP_CSR_DMIC1_CTL, 0x00}, + { CDC_VA_TOP_CSR_DMIC2_CTL, 0x00}, + { CDC_VA_TOP_CSR_DMIC3_CTL, 0x00}, + { CDC_VA_TOP_CSR_DMIC_CFG, 0x80}, + { CDC_VA_TOP_CSR_DEBUG_BUS, 0x00}, + { CDC_VA_TOP_CSR_DEBUG_EN, 0x00}, + { CDC_VA_TOP_CSR_TX_I2S_CTL, 0x0C}, + { CDC_VA_TOP_CSR_I2S_CLK, 0x00}, + { CDC_VA_TOP_CSR_I2S_RESET, 0x00}, + { CDC_VA_TOP_CSR_CORE_ID_0, 0x00}, + { CDC_VA_TOP_CSR_CORE_ID_1, 0x00}, + { CDC_VA_TOP_CSR_CORE_ID_2, 0x00}, + { CDC_VA_TOP_CSR_CORE_ID_3, 0x00}, + { CDC_VA_TOP_CSR_SWR_MIC_CTL0, 0xEE}, + { CDC_VA_TOP_CSR_SWR_MIC_CTL1, 0xEE}, + { CDC_VA_TOP_CSR_SWR_MIC_CTL2, 0xEE}, + { CDC_VA_TOP_CSR_SWR_CTRL, 0x06}, + + /* VA core */ + { CDC_VA_INP_MUX_ADC_MUX0_CFG0, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX0_CFG1, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX1_CFG0, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX1_CFG1, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX2_CFG0, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX2_CFG1, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX3_CFG0, 0x00}, + { CDC_VA_INP_MUX_ADC_MUX3_CFG1, 0x00}, + { CDC_VA_TX0_TX_PATH_CTL, 0x04}, + { CDC_VA_TX0_TX_PATH_CFG0, 0x10}, + { CDC_VA_TX0_TX_PATH_CFG1, 0x0B}, + { CDC_VA_TX0_TX_VOL_CTL, 0x00}, + { CDC_VA_TX0_TX_PATH_SEC0, 0x00}, + { CDC_VA_TX0_TX_PATH_SEC1, 0x00}, + { CDC_VA_TX0_TX_PATH_SEC2, 0x01}, + { CDC_VA_TX0_TX_PATH_SEC3, 0x3C}, + { CDC_VA_TX0_TX_PATH_SEC4, 0x20}, + { CDC_VA_TX0_TX_PATH_SEC5, 0x00}, + { CDC_VA_TX0_TX_PATH_SEC6, 0x00}, + { CDC_VA_TX0_TX_PATH_SEC7, 0x25}, + { CDC_VA_TX1_TX_PATH_CTL, 0x04}, + { CDC_VA_TX1_TX_PATH_CFG0, 0x10}, + { CDC_VA_TX1_TX_PATH_CFG1, 0x0B}, + { CDC_VA_TX1_TX_VOL_CTL, 0x00}, + { CDC_VA_TX1_TX_PATH_SEC0, 0x00}, + { CDC_VA_TX1_TX_PATH_SEC1, 0x00}, + { CDC_VA_TX1_TX_PATH_SEC2, 0x01}, + { CDC_VA_TX1_TX_PATH_SEC3, 0x3C}, + { CDC_VA_TX1_TX_PATH_SEC4, 0x20}, + { CDC_VA_TX1_TX_PATH_SEC5, 0x00}, + { CDC_VA_TX1_TX_PATH_SEC6, 0x00}, + { CDC_VA_TX2_TX_PATH_CTL, 0x04}, + { CDC_VA_TX2_TX_PATH_CFG0, 0x10}, + { CDC_VA_TX2_TX_PATH_CFG1, 0x0B}, + { CDC_VA_TX2_TX_VOL_CTL, 0x00}, + { CDC_VA_TX2_TX_PATH_SEC0, 0x00}, + { CDC_VA_TX2_TX_PATH_SEC1, 0x00}, + { CDC_VA_TX2_TX_PATH_SEC2, 0x01}, + { CDC_VA_TX2_TX_PATH_SEC3, 0x3C}, + { CDC_VA_TX2_TX_PATH_SEC4, 0x20}, + { CDC_VA_TX2_TX_PATH_SEC5, 0x00}, + { CDC_VA_TX2_TX_PATH_SEC6, 0x00}, + { CDC_VA_TX3_TX_PATH_CTL, 0x04}, + { CDC_VA_TX3_TX_PATH_CFG0, 0x10}, + { CDC_VA_TX3_TX_PATH_CFG1, 0x0B}, + { CDC_VA_TX3_TX_VOL_CTL, 0x00}, + { CDC_VA_TX3_TX_PATH_SEC0, 0x00}, + { CDC_VA_TX3_TX_PATH_SEC1, 0x00}, + { CDC_VA_TX3_TX_PATH_SEC2, 0x01}, + { CDC_VA_TX3_TX_PATH_SEC3, 0x3C}, + { CDC_VA_TX3_TX_PATH_SEC4, 0x20}, + { CDC_VA_TX3_TX_PATH_SEC5, 0x00}, + { CDC_VA_TX3_TX_PATH_SEC6, 0x00}, +}; + +static bool va_is_rw_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_VA_CLK_RST_CTRL_MCLK_CONTROL: + case CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL: + case CDC_VA_CLK_RST_CTRL_SWR_CONTROL: + case CDC_VA_TOP_CSR_TOP_CFG0: + case CDC_VA_TOP_CSR_DMIC0_CTL: + case CDC_VA_TOP_CSR_DMIC1_CTL: + case CDC_VA_TOP_CSR_DMIC2_CTL: + case CDC_VA_TOP_CSR_DMIC3_CTL: + case CDC_VA_TOP_CSR_DMIC_CFG: + case CDC_VA_TOP_CSR_SWR_MIC_CTL0: + case CDC_VA_TOP_CSR_SWR_MIC_CTL1: + case CDC_VA_TOP_CSR_SWR_MIC_CTL2: + case CDC_VA_TOP_CSR_DEBUG_BUS: + case CDC_VA_TOP_CSR_DEBUG_EN: + case CDC_VA_TOP_CSR_TX_I2S_CTL: + case CDC_VA_TOP_CSR_I2S_CLK: + case CDC_VA_TOP_CSR_I2S_RESET: + case CDC_VA_INP_MUX_ADC_MUX0_CFG0: + case CDC_VA_INP_MUX_ADC_MUX0_CFG1: + case CDC_VA_INP_MUX_ADC_MUX1_CFG0: + case CDC_VA_INP_MUX_ADC_MUX1_CFG1: + case CDC_VA_INP_MUX_ADC_MUX2_CFG0: + case CDC_VA_INP_MUX_ADC_MUX2_CFG1: + case CDC_VA_INP_MUX_ADC_MUX3_CFG0: + case CDC_VA_INP_MUX_ADC_MUX3_CFG1: + case CDC_VA_TX0_TX_PATH_CTL: + case CDC_VA_TX0_TX_PATH_CFG0: + case CDC_VA_TX0_TX_PATH_CFG1: + case CDC_VA_TX0_TX_VOL_CTL: + case CDC_VA_TX0_TX_PATH_SEC0: + case CDC_VA_TX0_TX_PATH_SEC1: + case CDC_VA_TX0_TX_PATH_SEC2: + case CDC_VA_TX0_TX_PATH_SEC3: + case CDC_VA_TX0_TX_PATH_SEC4: + case CDC_VA_TX0_TX_PATH_SEC5: + case CDC_VA_TX0_TX_PATH_SEC6: + case CDC_VA_TX0_TX_PATH_SEC7: + case CDC_VA_TX1_TX_PATH_CTL: + case CDC_VA_TX1_TX_PATH_CFG0: + case CDC_VA_TX1_TX_PATH_CFG1: + case CDC_VA_TX1_TX_VOL_CTL: + case CDC_VA_TX1_TX_PATH_SEC0: + case CDC_VA_TX1_TX_PATH_SEC1: + case CDC_VA_TX1_TX_PATH_SEC2: + case CDC_VA_TX1_TX_PATH_SEC3: + case CDC_VA_TX1_TX_PATH_SEC4: + case CDC_VA_TX1_TX_PATH_SEC5: + case CDC_VA_TX1_TX_PATH_SEC6: + case CDC_VA_TX2_TX_PATH_CTL: + case CDC_VA_TX2_TX_PATH_CFG0: + case CDC_VA_TX2_TX_PATH_CFG1: + case CDC_VA_TX2_TX_VOL_CTL: + case CDC_VA_TX2_TX_PATH_SEC0: + case CDC_VA_TX2_TX_PATH_SEC1: + case CDC_VA_TX2_TX_PATH_SEC2: + case CDC_VA_TX2_TX_PATH_SEC3: + case CDC_VA_TX2_TX_PATH_SEC4: + case CDC_VA_TX2_TX_PATH_SEC5: + case CDC_VA_TX2_TX_PATH_SEC6: + case CDC_VA_TX3_TX_PATH_CTL: + case CDC_VA_TX3_TX_PATH_CFG0: + case CDC_VA_TX3_TX_PATH_CFG1: + case CDC_VA_TX3_TX_VOL_CTL: + case CDC_VA_TX3_TX_PATH_SEC0: + case CDC_VA_TX3_TX_PATH_SEC1: + case CDC_VA_TX3_TX_PATH_SEC2: + case CDC_VA_TX3_TX_PATH_SEC3: + case CDC_VA_TX3_TX_PATH_SEC4: + case CDC_VA_TX3_TX_PATH_SEC5: + case CDC_VA_TX3_TX_PATH_SEC6: + return true; + } + + return false; +} + +static bool va_is_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_VA_TOP_CSR_CORE_ID_0: + case CDC_VA_TOP_CSR_CORE_ID_1: + case CDC_VA_TOP_CSR_CORE_ID_2: + case CDC_VA_TOP_CSR_CORE_ID_3: + return true; + } + + return va_is_rw_register(dev, reg); +} + +static const struct regmap_config va_regmap_config = { + .name = "va_macro", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .cache_type = REGCACHE_FLAT, + .reg_defaults = va_defaults, + .num_reg_defaults = ARRAY_SIZE(va_defaults), + .max_register = VA_MAX_OFFSET, + .volatile_reg = va_is_volatile_register, + .readable_reg = va_is_readable_register, + .writeable_reg = va_is_rw_register, +}; + +static int va_clk_rsc_fs_gen_request(struct va_macro *va, bool enable) +{ + struct regmap *regmap = va->regmap; + + if (enable) { + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, + CDC_VA_MCLK_CONTROL_EN, + CDC_VA_MCLK_CONTROL_EN); + /* clear the fs counter */ + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_VA_FS_CONTROL_EN | CDC_VA_FS_COUNTER_CLR, + CDC_VA_FS_CONTROL_EN | CDC_VA_FS_COUNTER_CLR); + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_VA_FS_CONTROL_EN | CDC_VA_FS_COUNTER_CLR, + CDC_VA_FS_CONTROL_EN); + + regmap_update_bits(regmap, CDC_VA_TOP_CSR_TOP_CFG0, + CDC_VA_FS_BROADCAST_EN, + CDC_VA_FS_BROADCAST_EN); + } else { + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_MCLK_CONTROL, + CDC_VA_MCLK_CONTROL_EN, 0x0); + + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_VA_FS_CONTROL_EN, 0x0); + + regmap_update_bits(regmap, CDC_VA_TOP_CSR_TOP_CFG0, + CDC_VA_FS_BROADCAST_EN, 0x0); + } + + return 0; +} + +static int va_macro_mclk_enable(struct va_macro *va, bool mclk_enable) +{ + struct regmap *regmap = va->regmap; + + if (mclk_enable) { + va_clk_rsc_fs_gen_request(va, true); + regcache_mark_dirty(regmap); + regcache_sync_region(regmap, 0x0, VA_MAX_OFFSET); + } else { + va_clk_rsc_fs_gen_request(va, false); + } + + return 0; +} + +static int va_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct va_macro *va = snd_soc_component_get_drvdata(comp); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return clk_prepare_enable(va->fsgen); + case SND_SOC_DAPM_POST_PMD: + clk_disable_unprepare(va->fsgen); + } + + return 0; +} + +static int va_macro_put_dec_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg; + + val = ucontrol->value.enumerated.item[0]; + + switch (e->reg) { + case CDC_VA_INP_MUX_ADC_MUX0_CFG0: + mic_sel_reg = CDC_VA_TX0_TX_PATH_CFG0; + break; + case CDC_VA_INP_MUX_ADC_MUX1_CFG0: + mic_sel_reg = CDC_VA_TX1_TX_PATH_CFG0; + break; + case CDC_VA_INP_MUX_ADC_MUX2_CFG0: + mic_sel_reg = CDC_VA_TX2_TX_PATH_CFG0; + break; + case CDC_VA_INP_MUX_ADC_MUX3_CFG0: + mic_sel_reg = CDC_VA_TX3_TX_PATH_CFG0; + break; + default: + dev_err(component->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + if (val != 0) + snd_soc_component_update_bits(component, mic_sel_reg, + CDC_VA_TX_PATH_ADC_DMIC_SEL_MASK, + CDC_VA_TX_PATH_ADC_DMIC_SEL_DMIC); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int va_macro_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + u32 dai_id = widget->shift; + u32 dec_id = mc->shift; + struct va_macro *va = snd_soc_component_get_drvdata(component); + + if (test_bit(dec_id, &va->active_ch_mask[dai_id])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int va_macro_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct snd_soc_dapm_update *update = NULL; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + u32 dai_id = widget->shift; + u32 dec_id = mc->shift; + u32 enable = ucontrol->value.integer.value[0]; + struct va_macro *va = snd_soc_component_get_drvdata(component); + + if (enable) { + set_bit(dec_id, &va->active_ch_mask[dai_id]); + va->active_ch_cnt[dai_id]++; + } else { + clear_bit(dec_id, &va->active_ch_mask[dai_id]); + va->active_ch_cnt[dai_id]--; + } + + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int va_dmic_clk_enable(struct snd_soc_component *component, + u32 dmic, bool enable) +{ + struct va_macro *va = snd_soc_component_get_drvdata(component); + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 *dmic_clk_div; + u8 freq_change_mask; + u8 clk_div; + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(va->dmic_0_1_clk_cnt); + dmic_clk_div = &(va->dmic_0_1_clk_div); + dmic_clk_reg = CDC_VA_TOP_CSR_DMIC0_CTL; + freq_change_mask = CDC_VA_DMIC0_FREQ_CHANGE_MASK; + break; + case 2: + case 3: + dmic_clk_cnt = &(va->dmic_2_3_clk_cnt); + dmic_clk_div = &(va->dmic_2_3_clk_div); + dmic_clk_reg = CDC_VA_TOP_CSR_DMIC1_CTL; + freq_change_mask = CDC_VA_DMIC1_FREQ_CHANGE_MASK; + break; + case 4: + case 5: + dmic_clk_cnt = &(va->dmic_4_5_clk_cnt); + dmic_clk_div = &(va->dmic_4_5_clk_div); + dmic_clk_reg = CDC_VA_TOP_CSR_DMIC2_CTL; + freq_change_mask = CDC_VA_DMIC2_FREQ_CHANGE_MASK; + break; + case 6: + case 7: + dmic_clk_cnt = &(va->dmic_6_7_clk_cnt); + dmic_clk_div = &(va->dmic_6_7_clk_div); + dmic_clk_reg = CDC_VA_TOP_CSR_DMIC3_CTL; + freq_change_mask = CDC_VA_DMIC3_FREQ_CHANGE_MASK; + break; + default: + dev_err(component->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + } + + if (enable) { + clk_div = va->dmic_clk_div; + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_component_update_bits(component, + CDC_VA_TOP_CSR_DMIC_CFG, + CDC_VA_RESET_ALL_DMICS_MASK, + CDC_VA_RESET_ALL_DMICS_DISABLE); + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_CLK_SEL_MASK, + clk_div << CDC_VA_DMIC_CLK_SEL_SHFT); + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_EN_MASK, + CDC_VA_DMIC_ENABLE); + } else { + if (*dmic_clk_div > clk_div) { + snd_soc_component_update_bits(component, + CDC_VA_TOP_CSR_DMIC_CFG, + freq_change_mask, + freq_change_mask); + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_CLK_SEL_MASK, + clk_div << CDC_VA_DMIC_CLK_SEL_SHFT); + snd_soc_component_update_bits(component, + CDC_VA_TOP_CSR_DMIC_CFG, + freq_change_mask, + CDC_VA_DMIC_FREQ_CHANGE_DISABLE); + } else { + clk_div = *dmic_clk_div; + } + } + *dmic_clk_div = clk_div; + } else { + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_EN_MASK, 0); + clk_div = 0; + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_CLK_SEL_MASK, + clk_div << CDC_VA_DMIC_CLK_SEL_SHFT); + } else { + clk_div = va->dmic_clk_div; + if (*dmic_clk_div > clk_div) { + clk_div = va->dmic_clk_div; + snd_soc_component_update_bits(component, + CDC_VA_TOP_CSR_DMIC_CFG, + freq_change_mask, + freq_change_mask); + snd_soc_component_update_bits(component, dmic_clk_reg, + CDC_VA_DMIC_CLK_SEL_MASK, + clk_div << CDC_VA_DMIC_CLK_SEL_SHFT); + snd_soc_component_update_bits(component, + CDC_VA_TOP_CSR_DMIC_CFG, + freq_change_mask, + CDC_VA_DMIC_FREQ_CHANGE_DISABLE); + } else { + clk_div = *dmic_clk_div; + } + } + *dmic_clk_div = clk_div; + } + + return 0; +} + +static int va_macro_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + unsigned int dmic = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + va_dmic_clk_enable(comp, dmic, true); + break; + case SND_SOC_DAPM_POST_PMD: + va_dmic_clk_enable(comp, dmic, false); + break; + } + + return 0; +} + +static int va_macro_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + unsigned int decimator; + u16 tx_vol_ctl_reg, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + u8 hpf_cut_off_freq; + + struct va_macro *va = snd_soc_component_get_drvdata(comp); + + decimator = w->shift; + + tx_vol_ctl_reg = CDC_VA_TX0_TX_PATH_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + hpf_gate_reg = CDC_VA_TX0_TX_PATH_SEC2 + + VA_MACRO_TX_PATH_OFFSET * decimator; + dec_cfg_reg = CDC_VA_TX0_TX_PATH_CFG0 + + VA_MACRO_TX_PATH_OFFSET * decimator; + tx_gain_ctl_reg = CDC_VA_TX0_TX_VOL_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(comp, + dec_cfg_reg, CDC_VA_ADC_MODE_MASK, + va->dec_mode[decimator] << CDC_VA_ADC_MODE_SHIFT); + /* Enable TX PGA Mute */ + break; + case SND_SOC_DAPM_POST_PMU: + /* Enable TX CLK */ + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, + CDC_VA_TX_PATH_CLK_EN_MASK, + CDC_VA_TX_PATH_CLK_EN); + snd_soc_component_update_bits(comp, hpf_gate_reg, + CDC_VA_TX_HPF_ZERO_GATE_MASK, + CDC_VA_TX_HPF_ZERO_GATE); + + usleep_range(1000, 1010); + hpf_cut_off_freq = (snd_soc_component_read(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + + snd_soc_component_update_bits(comp, hpf_gate_reg, + CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK, + CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_REQ); + + /* + * Minimum 1 clk cycle delay is required as per HW spec + */ + usleep_range(1000, 1010); + + snd_soc_component_update_bits(comp, + hpf_gate_reg, + CDC_VA_TX_HPF_CUTOFF_FREQ_CHANGE_MASK, + 0x0); + } + + + usleep_range(1000, 1010); + snd_soc_component_update_bits(comp, hpf_gate_reg, + CDC_VA_TX_HPF_ZERO_GATE_MASK, + CDC_VA_TX_HPF_ZERO_NO_GATE); + /* + * 6ms delay is required as per HW spec + */ + usleep_range(6000, 6010); + /* apply gain after decimator is enabled */ + snd_soc_component_write(comp, tx_gain_ctl_reg, + snd_soc_component_read(comp, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable TX CLK */ + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, + CDC_VA_TX_PATH_CLK_EN_MASK, + CDC_VA_TX_PATH_CLK_DISABLE); + break; + } + return 0; +} + +static int va_macro_dec_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct va_macro *va = snd_soc_component_get_drvdata(comp); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + ucontrol->value.enumerated.item[0] = va->dec_mode[path]; + + return 0; +} + +static int va_macro_dec_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + int value = ucontrol->value.enumerated.item[0]; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + struct va_macro *va = snd_soc_component_get_drvdata(comp); + + va->dec_mode[path] = value; + + return 0; +} + +static int va_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int tx_fs_rate; + struct snd_soc_component *component = dai->component; + u32 decimator, sample_rate; + u16 tx_fs_reg; + struct device *va_dev = component->dev; + struct va_macro *va = snd_soc_component_get_drvdata(component); + + sample_rate = params_rate(params); + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(va_dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + } + + for_each_set_bit(decimator, &va->active_ch_mask[dai->id], + VA_MACRO_DEC_MAX) { + tx_fs_reg = CDC_VA_TX0_TX_PATH_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + snd_soc_component_update_bits(component, tx_fs_reg, 0x0F, + tx_fs_rate); + } + return 0; +} + +static int va_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_component *component = dai->component; + struct device *va_dev = component->dev; + struct va_macro *va = snd_soc_component_get_drvdata(component); + + switch (dai->id) { + case VA_MACRO_AIF1_CAP: + case VA_MACRO_AIF2_CAP: + case VA_MACRO_AIF3_CAP: + *tx_slot = va->active_ch_mask[dai->id]; + *tx_num = va->active_ch_cnt[dai->id]; + break; + default: + dev_err(va_dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static int va_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct va_macro *va = snd_soc_component_get_drvdata(component); + u16 tx_vol_ctl_reg, decimator; + + for_each_set_bit(decimator, &va->active_ch_mask[dai->id], + VA_MACRO_DEC_MAX) { + tx_vol_ctl_reg = CDC_VA_TX0_TX_PATH_CTL + + VA_MACRO_TX_PATH_OFFSET * decimator; + if (mute) + snd_soc_component_update_bits(component, tx_vol_ctl_reg, + CDC_VA_TX_PATH_PGA_MUTE_EN_MASK, + CDC_VA_TX_PATH_PGA_MUTE_EN); + else + snd_soc_component_update_bits(component, tx_vol_ctl_reg, + CDC_VA_TX_PATH_PGA_MUTE_EN_MASK, + CDC_VA_TX_PATH_PGA_MUTE_DISABLE); + } + + return 0; +} + +static const struct snd_soc_dai_ops va_macro_dai_ops = { + .hw_params = va_macro_hw_params, + .get_channel_map = va_macro_get_channel_map, + .mute_stream = va_macro_digital_mute, +}; + +static struct snd_soc_dai_driver va_macro_dais[] = { + { + .name = "va_macro_tx1", + .id = VA_MACRO_AIF1_CAP, + .capture = { + .stream_name = "VA_AIF1 Capture", + .rates = VA_MACRO_RATES, + .formats = VA_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &va_macro_dai_ops, + }, + { + .name = "va_macro_tx2", + .id = VA_MACRO_AIF2_CAP, + .capture = { + .stream_name = "VA_AIF2 Capture", + .rates = VA_MACRO_RATES, + .formats = VA_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &va_macro_dai_ops, + }, + { + .name = "va_macro_tx3", + .id = VA_MACRO_AIF3_CAP, + .capture = { + .stream_name = "VA_AIF3 Capture", + .rates = VA_MACRO_RATES, + .formats = VA_MACRO_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &va_macro_dai_ops, + }, +}; + +static const char * const adc_mux_text[] = { + "VA_DMIC", "SWR_MIC" +}; + +static SOC_ENUM_SINGLE_DECL(va_dec0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(va_dec1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(va_dec2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG1, + 0, adc_mux_text); +static SOC_ENUM_SINGLE_DECL(va_dec3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG1, + 0, adc_mux_text); + +static const struct snd_kcontrol_new va_dec0_mux = SOC_DAPM_ENUM("va_dec0", + va_dec0_enum); +static const struct snd_kcontrol_new va_dec1_mux = SOC_DAPM_ENUM("va_dec1", + va_dec1_enum); +static const struct snd_kcontrol_new va_dec2_mux = SOC_DAPM_ENUM("va_dec2", + va_dec2_enum); +static const struct snd_kcontrol_new va_dec3_mux = SOC_DAPM_ENUM("va_dec3", + va_dec3_enum); + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "DMIC6", "DMIC7" +}; + +static SOC_ENUM_SINGLE_DECL(va_dmic0_enum, CDC_VA_INP_MUX_ADC_MUX0_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(va_dmic1_enum, CDC_VA_INP_MUX_ADC_MUX1_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(va_dmic2_enum, CDC_VA_INP_MUX_ADC_MUX2_CFG0, + 4, dmic_mux_text); + +static SOC_ENUM_SINGLE_DECL(va_dmic3_enum, CDC_VA_INP_MUX_ADC_MUX3_CFG0, + 4, dmic_mux_text); + +static const struct snd_kcontrol_new va_dmic0_mux = SOC_DAPM_ENUM_EXT("va_dmic0", + va_dmic0_enum, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +static const struct snd_kcontrol_new va_dmic1_mux = SOC_DAPM_ENUM_EXT("va_dmic1", + va_dmic1_enum, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +static const struct snd_kcontrol_new va_dmic2_mux = SOC_DAPM_ENUM_EXT("va_dmic2", + va_dmic2_enum, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +static const struct snd_kcontrol_new va_dmic3_mux = SOC_DAPM_ENUM_EXT("va_dmic3", + va_dmic3_enum, snd_soc_dapm_get_enum_double, + va_macro_put_dec_enum); + +static const struct snd_kcontrol_new va_aif1_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), +}; + +static const struct snd_kcontrol_new va_aif2_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), +}; + +static const struct snd_kcontrol_new va_aif3_cap_mixer[] = { + SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, VA_MACRO_DEC0, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC1", SND_SOC_NOPM, VA_MACRO_DEC1, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC2", SND_SOC_NOPM, VA_MACRO_DEC2, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC3", SND_SOC_NOPM, VA_MACRO_DEC3, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC4", SND_SOC_NOPM, VA_MACRO_DEC4, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC5", SND_SOC_NOPM, VA_MACRO_DEC5, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC6", SND_SOC_NOPM, VA_MACRO_DEC6, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), + SOC_SINGLE_EXT("DEC7", SND_SOC_NOPM, VA_MACRO_DEC7, 1, 0, + va_macro_tx_mixer_get, va_macro_tx_mixer_put), +}; + +static const struct snd_soc_dapm_widget va_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("VA_AIF1 CAP", "VA_AIF1 Capture", 0, + SND_SOC_NOPM, VA_MACRO_AIF1_CAP, 0), + + SND_SOC_DAPM_AIF_OUT("VA_AIF2 CAP", "VA_AIF2 Capture", 0, + SND_SOC_NOPM, VA_MACRO_AIF2_CAP, 0), + + SND_SOC_DAPM_AIF_OUT("VA_AIF3 CAP", "VA_AIF3 Capture", 0, + SND_SOC_NOPM, VA_MACRO_AIF3_CAP, 0), + + SND_SOC_DAPM_MIXER("VA_AIF1_CAP Mixer", SND_SOC_NOPM, + VA_MACRO_AIF1_CAP, 0, + va_aif1_cap_mixer, ARRAY_SIZE(va_aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("VA_AIF2_CAP Mixer", SND_SOC_NOPM, + VA_MACRO_AIF2_CAP, 0, + va_aif2_cap_mixer, ARRAY_SIZE(va_aif2_cap_mixer)), + + SND_SOC_DAPM_MIXER("VA_AIF3_CAP Mixer", SND_SOC_NOPM, + VA_MACRO_AIF3_CAP, 0, + va_aif3_cap_mixer, ARRAY_SIZE(va_aif3_cap_mixer)), + + SND_SOC_DAPM_MUX("VA DMIC MUX0", SND_SOC_NOPM, 0, 0, &va_dmic0_mux), + SND_SOC_DAPM_MUX("VA DMIC MUX1", SND_SOC_NOPM, 0, 0, &va_dmic1_mux), + SND_SOC_DAPM_MUX("VA DMIC MUX2", SND_SOC_NOPM, 0, 0, &va_dmic2_mux), + SND_SOC_DAPM_MUX("VA DMIC MUX3", SND_SOC_NOPM, 0, 0, &va_dmic3_mux), + + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micb", 0, 0), + SND_SOC_DAPM_INPUT("DMIC0 Pin"), + SND_SOC_DAPM_INPUT("DMIC1 Pin"), + SND_SOC_DAPM_INPUT("DMIC2 Pin"), + SND_SOC_DAPM_INPUT("DMIC3 Pin"), + SND_SOC_DAPM_INPUT("DMIC4 Pin"), + SND_SOC_DAPM_INPUT("DMIC5 Pin"), + SND_SOC_DAPM_INPUT("DMIC6 Pin"), + SND_SOC_DAPM_INPUT("DMIC7 Pin"), + + SND_SOC_DAPM_ADC_E("VA DMIC0", NULL, SND_SOC_NOPM, 0, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC1", NULL, SND_SOC_NOPM, 1, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC2", NULL, SND_SOC_NOPM, 2, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC3", NULL, SND_SOC_NOPM, 3, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC4", NULL, SND_SOC_NOPM, 4, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC5", NULL, SND_SOC_NOPM, 5, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC6", NULL, SND_SOC_NOPM, 6, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("VA DMIC7", NULL, SND_SOC_NOPM, 7, 0, + va_macro_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("VA SWR_ADC0"), + SND_SOC_DAPM_INPUT("VA SWR_ADC1"), + SND_SOC_DAPM_INPUT("VA SWR_ADC2"), + SND_SOC_DAPM_INPUT("VA SWR_ADC3"), + SND_SOC_DAPM_INPUT("VA SWR_MIC0"), + SND_SOC_DAPM_INPUT("VA SWR_MIC1"), + SND_SOC_DAPM_INPUT("VA SWR_MIC2"), + SND_SOC_DAPM_INPUT("VA SWR_MIC3"), + SND_SOC_DAPM_INPUT("VA SWR_MIC4"), + SND_SOC_DAPM_INPUT("VA SWR_MIC5"), + SND_SOC_DAPM_INPUT("VA SWR_MIC6"), + SND_SOC_DAPM_INPUT("VA SWR_MIC7"), + + SND_SOC_DAPM_MUX_E("VA DEC0 MUX", SND_SOC_NOPM, VA_MACRO_DEC0, 0, + &va_dec0_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC1 MUX", SND_SOC_NOPM, VA_MACRO_DEC1, 0, + &va_dec1_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC2 MUX", SND_SOC_NOPM, VA_MACRO_DEC2, 0, + &va_dec2_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("VA DEC3 MUX", SND_SOC_NOPM, VA_MACRO_DEC3, 0, + &va_dec3_mux, va_macro_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("VA_MCLK", -1, SND_SOC_NOPM, 0, 0, + va_macro_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route va_audio_map[] = { + {"VA_AIF1 CAP", NULL, "VA_MCLK"}, + {"VA_AIF2 CAP", NULL, "VA_MCLK"}, + {"VA_AIF3 CAP", NULL, "VA_MCLK"}, + + {"VA_AIF1 CAP", NULL, "VA_AIF1_CAP Mixer"}, + {"VA_AIF2 CAP", NULL, "VA_AIF2_CAP Mixer"}, + {"VA_AIF3 CAP", NULL, "VA_AIF3_CAP Mixer"}, + + {"VA_AIF1_CAP Mixer", "DEC0", "VA DEC0 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC1", "VA DEC1 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC2", "VA DEC2 MUX"}, + {"VA_AIF1_CAP Mixer", "DEC3", "VA DEC3 MUX"}, + + {"VA_AIF2_CAP Mixer", "DEC0", "VA DEC0 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC1", "VA DEC1 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC2", "VA DEC2 MUX"}, + {"VA_AIF2_CAP Mixer", "DEC3", "VA DEC3 MUX"}, + + {"VA_AIF3_CAP Mixer", "DEC0", "VA DEC0 MUX"}, + {"VA_AIF3_CAP Mixer", "DEC1", "VA DEC1 MUX"}, + {"VA_AIF3_CAP Mixer", "DEC2", "VA DEC2 MUX"}, + {"VA_AIF3_CAP Mixer", "DEC3", "VA DEC3 MUX"}, + + {"VA DEC0 MUX", "VA_DMIC", "VA DMIC MUX0"}, + {"VA DMIC MUX0", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX0", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX0", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX0", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX0", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX0", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX0", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX0", "DMIC7", "VA DMIC7"}, + + {"VA DEC1 MUX", "VA_DMIC", "VA DMIC MUX1"}, + {"VA DMIC MUX1", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX1", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX1", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX1", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX1", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX1", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX1", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX1", "DMIC7", "VA DMIC7"}, + + {"VA DEC2 MUX", "VA_DMIC", "VA DMIC MUX2"}, + {"VA DMIC MUX2", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX2", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX2", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX2", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX2", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX2", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX2", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX2", "DMIC7", "VA DMIC7"}, + + {"VA DEC3 MUX", "VA_DMIC", "VA DMIC MUX3"}, + {"VA DMIC MUX3", "DMIC0", "VA DMIC0"}, + {"VA DMIC MUX3", "DMIC1", "VA DMIC1"}, + {"VA DMIC MUX3", "DMIC2", "VA DMIC2"}, + {"VA DMIC MUX3", "DMIC3", "VA DMIC3"}, + {"VA DMIC MUX3", "DMIC4", "VA DMIC4"}, + {"VA DMIC MUX3", "DMIC5", "VA DMIC5"}, + {"VA DMIC MUX3", "DMIC6", "VA DMIC6"}, + {"VA DMIC MUX3", "DMIC7", "VA DMIC7"}, + + { "VA DMIC0", NULL, "DMIC0 Pin" }, + { "VA DMIC1", NULL, "DMIC1 Pin" }, + { "VA DMIC2", NULL, "DMIC2 Pin" }, + { "VA DMIC3", NULL, "DMIC3 Pin" }, + { "VA DMIC4", NULL, "DMIC4 Pin" }, + { "VA DMIC5", NULL, "DMIC5 Pin" }, + { "VA DMIC6", NULL, "DMIC6 Pin" }, + { "VA DMIC7", NULL, "DMIC7 Pin" }, +}; + +static const char * const dec_mode_mux_text[] = { + "ADC_DEFAULT", "ADC_LOW_PWR", "ADC_HIGH_PERF", +}; + +static const struct soc_enum dec_mode_mux_enum[] = { + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(dec_mode_mux_text), + dec_mode_mux_text), +}; + +static const struct snd_kcontrol_new va_macro_snd_controls[] = { + SOC_SINGLE_S8_TLV("VA_DEC0 Volume", CDC_VA_TX0_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("VA_DEC1 Volume", CDC_VA_TX1_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("VA_DEC2 Volume", CDC_VA_TX2_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("VA_DEC3 Volume", CDC_VA_TX3_TX_VOL_CTL, + -84, 40, digital_gain), + + SOC_ENUM_EXT("VA_DEC0 MODE", dec_mode_mux_enum[0], + va_macro_dec_mode_get, va_macro_dec_mode_put), + SOC_ENUM_EXT("VA_DEC1 MODE", dec_mode_mux_enum[1], + va_macro_dec_mode_get, va_macro_dec_mode_put), + SOC_ENUM_EXT("VA_DEC2 MODE", dec_mode_mux_enum[2], + va_macro_dec_mode_get, va_macro_dec_mode_put), + SOC_ENUM_EXT("VA_DEC3 MODE", dec_mode_mux_enum[3], + va_macro_dec_mode_get, va_macro_dec_mode_put), +}; + +static int va_macro_component_probe(struct snd_soc_component *component) +{ + struct va_macro *va = snd_soc_component_get_drvdata(component); + + snd_soc_component_init_regmap(component, va->regmap); + + return 0; +} + +static const struct snd_soc_component_driver va_macro_component_drv = { + .name = "VA MACRO", + .probe = va_macro_component_probe, + .controls = va_macro_snd_controls, + .num_controls = ARRAY_SIZE(va_macro_snd_controls), + .dapm_widgets = va_macro_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(va_macro_dapm_widgets), + .dapm_routes = va_audio_map, + .num_dapm_routes = ARRAY_SIZE(va_audio_map), +}; + +static int fsgen_gate_enable(struct clk_hw *hw) +{ + struct va_macro *va = to_va_macro(hw); + struct regmap *regmap = va->regmap; + int ret; + + ret = va_macro_mclk_enable(va, true); + if (va->has_swr_master) + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE); + + return ret; +} + +static void fsgen_gate_disable(struct clk_hw *hw) +{ + struct va_macro *va = to_va_macro(hw); + struct regmap *regmap = va->regmap; + + if (va->has_swr_master) + regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_CLK_EN_MASK, 0x0); + + va_macro_mclk_enable(va, false); +} + +static int fsgen_gate_is_enabled(struct clk_hw *hw) +{ + struct va_macro *va = to_va_macro(hw); + int val; + + regmap_read(va->regmap, CDC_VA_TOP_CSR_TOP_CFG0, &val); + + return !!(val & CDC_VA_FS_BROADCAST_EN); +} + +static const struct clk_ops fsgen_gate_ops = { + .prepare = fsgen_gate_enable, + .unprepare = fsgen_gate_disable, + .is_enabled = fsgen_gate_is_enabled, +}; + +static int va_macro_register_fsgen_output(struct va_macro *va) +{ + struct clk *parent = va->mclk; + struct device *dev = va->dev; + struct device_node *np = dev->of_node; + const char *parent_clk_name; + const char *clk_name = "fsgen"; + struct clk_init_data init; + int ret; + + parent_clk_name = __clk_get_name(parent); + + of_property_read_string(np, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = &fsgen_gate_ops; + init.flags = 0; + init.parent_names = &parent_clk_name; + init.num_parents = 1; + va->hw.init = &init; + ret = devm_clk_hw_register(va->dev, &va->hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &va->hw); +} + +static int va_macro_validate_dmic_sample_rate(u32 dmic_sample_rate, + struct va_macro *va) +{ + u32 div_factor; + u32 mclk_rate = VA_MACRO_MCLK_FREQ; + + if (!dmic_sample_rate || mclk_rate % dmic_sample_rate != 0) + goto undefined_rate; + + div_factor = mclk_rate / dmic_sample_rate; + + switch (div_factor) { + case 2: + va->dmic_clk_div = VA_MACRO_CLK_DIV_2; + break; + case 3: + va->dmic_clk_div = VA_MACRO_CLK_DIV_3; + break; + case 4: + va->dmic_clk_div = VA_MACRO_CLK_DIV_4; + break; + case 6: + va->dmic_clk_div = VA_MACRO_CLK_DIV_6; + break; + case 8: + va->dmic_clk_div = VA_MACRO_CLK_DIV_8; + break; + case 16: + va->dmic_clk_div = VA_MACRO_CLK_DIV_16; + break; + default: + /* Any other DIV factor is invalid */ + goto undefined_rate; + } + + return dmic_sample_rate; + +undefined_rate: + dev_err(va->dev, "%s: Invalid rate %d, for mclk %d\n", + __func__, dmic_sample_rate, mclk_rate); + dmic_sample_rate = 0; + + return dmic_sample_rate; +} + +static int va_macro_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct va_macro_data *data; + struct va_macro *va; + void __iomem *base; + u32 sample_rate = 0; + int ret; + + va = devm_kzalloc(dev, sizeof(*va), GFP_KERNEL); + if (!va) + return -ENOMEM; + + va->dev = dev; + + va->macro = devm_clk_get_optional(dev, "macro"); + if (IS_ERR(va->macro)) + return dev_err_probe(dev, PTR_ERR(va->macro), "unable to get macro clock\n"); + + va->dcodec = devm_clk_get_optional(dev, "dcodec"); + if (IS_ERR(va->dcodec)) + return dev_err_probe(dev, PTR_ERR(va->dcodec), "unable to get dcodec clock\n"); + + va->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(va->mclk)) + return dev_err_probe(dev, PTR_ERR(va->mclk), "unable to get mclk clock\n"); + + va->pds = lpass_macro_pds_init(dev); + if (IS_ERR(va->pds)) + return PTR_ERR(va->pds); + + ret = of_property_read_u32(dev->of_node, "qcom,dmic-sample-rate", + &sample_rate); + if (ret) { + dev_err(dev, "qcom,dmic-sample-rate dt entry missing\n"); + va->dmic_clk_div = VA_MACRO_CLK_DIV_2; + } else { + ret = va_macro_validate_dmic_sample_rate(sample_rate, va); + if (!ret) { + ret = -EINVAL; + goto err; + } + } + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto err; + } + + va->regmap = devm_regmap_init_mmio(dev, base, &va_regmap_config); + if (IS_ERR(va->regmap)) { + ret = -EINVAL; + goto err; + } + + dev_set_drvdata(dev, va); + + data = of_device_get_match_data(dev); + va->has_swr_master = data->has_swr_master; + + /* mclk rate */ + clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ); + + ret = clk_prepare_enable(va->macro); + if (ret) + goto err; + + ret = clk_prepare_enable(va->dcodec); + if (ret) + goto err_dcodec; + + ret = clk_prepare_enable(va->mclk); + if (ret) + goto err_mclk; + + if (va->has_swr_master) { + /* Set default CLK div to 1 */ + regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0, + CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK, + CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1); + regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL1, + CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK, + CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1); + regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL2, + CDC_VA_SWR_MIC_CLK_SEL_0_1_MASK, + CDC_VA_SWR_MIC_CLK_SEL_0_1_DIV1); + + } + + if (va->has_swr_master) { + regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_RESET_MASK, CDC_VA_SWR_RESET_ENABLE); + regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE); + regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL, + CDC_VA_SWR_RESET_MASK, 0x0); + } + + ret = devm_snd_soc_register_component(dev, &va_macro_component_drv, + va_macro_dais, + ARRAY_SIZE(va_macro_dais)); + if (ret) + goto err_clkout; + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = va_macro_register_fsgen_output(va); + if (ret) + goto err_clkout; + + va->fsgen = clk_hw_get_clk(&va->hw, "fsgen"); + if (IS_ERR(va->fsgen)) { + ret = PTR_ERR(va->fsgen); + goto err_clkout; + } + + return 0; + +err_clkout: + clk_disable_unprepare(va->mclk); +err_mclk: + clk_disable_unprepare(va->dcodec); +err_dcodec: + clk_disable_unprepare(va->macro); +err: + lpass_macro_pds_exit(va->pds); + + return ret; +} + +static void va_macro_remove(struct platform_device *pdev) +{ + struct va_macro *va = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(va->mclk); + clk_disable_unprepare(va->dcodec); + clk_disable_unprepare(va->macro); + + lpass_macro_pds_exit(va->pds); +} + +static int __maybe_unused va_macro_runtime_suspend(struct device *dev) +{ + struct va_macro *va = dev_get_drvdata(dev); + + regcache_cache_only(va->regmap, true); + regcache_mark_dirty(va->regmap); + + clk_disable_unprepare(va->mclk); + + return 0; +} + +static int __maybe_unused va_macro_runtime_resume(struct device *dev) +{ + struct va_macro *va = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(va->mclk); + if (ret) { + dev_err(va->dev, "unable to prepare mclk\n"); + return ret; + } + + regcache_cache_only(va->regmap, false); + regcache_sync(va->regmap); + + return 0; +} + + +static const struct dev_pm_ops va_macro_pm_ops = { + SET_RUNTIME_PM_OPS(va_macro_runtime_suspend, va_macro_runtime_resume, NULL) +}; + +static const struct of_device_id va_macro_dt_match[] = { + { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data }, + { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data }, + { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data }, + { .compatible = "qcom,sc8280xp-lpass-va-macro", .data = &sm8450_va_data }, + {} +}; +MODULE_DEVICE_TABLE(of, va_macro_dt_match); + +static struct platform_driver va_macro_driver = { + .driver = { + .name = "va_macro", + .of_match_table = va_macro_dt_match, + .suppress_bind_attrs = true, + .pm = &va_macro_pm_ops, + }, + .probe = va_macro_probe, + .remove_new = va_macro_remove, +}; + +module_platform_driver(va_macro_driver); +MODULE_DESCRIPTION("VA macro driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c new file mode 100644 index 0000000000..7e21cec3c2 --- /dev/null +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -0,0 +1,2598 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lpass-macro-common.h" +#include "lpass-wsa-macro.h" + +#define CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL (0x0000) +#define CDC_WSA_MCLK_EN_MASK BIT(0) +#define CDC_WSA_MCLK_ENABLE BIT(0) +#define CDC_WSA_MCLK_DISABLE 0 +#define CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL (0x0004) +#define CDC_WSA_FS_CNT_EN_MASK BIT(0) +#define CDC_WSA_FS_CNT_ENABLE BIT(0) +#define CDC_WSA_FS_CNT_DISABLE 0 +#define CDC_WSA_CLK_RST_CTRL_SWR_CONTROL (0x0008) +#define CDC_WSA_SWR_CLK_EN_MASK BIT(0) +#define CDC_WSA_SWR_CLK_ENABLE BIT(0) +#define CDC_WSA_SWR_RST_EN_MASK BIT(1) +#define CDC_WSA_SWR_RST_ENABLE BIT(1) +#define CDC_WSA_SWR_RST_DISABLE 0 +#define CDC_WSA_TOP_TOP_CFG0 (0x0080) +#define CDC_WSA_TOP_TOP_CFG1 (0x0084) +#define CDC_WSA_TOP_FREQ_MCLK (0x0088) +#define CDC_WSA_TOP_DEBUG_BUS_SEL (0x008C) +#define CDC_WSA_TOP_DEBUG_EN0 (0x0090) +#define CDC_WSA_TOP_DEBUG_EN1 (0x0094) +#define CDC_WSA_TOP_DEBUG_DSM_LB (0x0098) +#define CDC_WSA_TOP_RX_I2S_CTL (0x009C) +#define CDC_WSA_TOP_TX_I2S_CTL (0x00A0) +#define CDC_WSA_TOP_I2S_CLK (0x00A4) +#define CDC_WSA_TOP_I2S_RESET (0x00A8) +#define CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 (0x0100) +#define CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK GENMASK(2, 0) +#define CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK GENMASK(5, 3) +#define CDC_WSA_RX_INP_MUX_RX_INT0_CFG1 (0x0104) +#define CDC_WSA_RX_INTX_2_SEL_MASK GENMASK(2, 0) +#define CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK GENMASK(5, 3) +#define CDC_WSA_RX_INP_MUX_RX_INT1_CFG0 (0x0108) +#define CDC_WSA_RX_INP_MUX_RX_INT1_CFG1 (0x010C) +#define CDC_WSA_RX_INP_MUX_RX_MIX_CFG0 (0x0110) +#define CDC_WSA_RX_MIX_TX1_SEL_MASK GENMASK(5, 3) +#define CDC_WSA_RX_MIX_TX1_SEL_SHFT 3 +#define CDC_WSA_RX_MIX_TX0_SEL_MASK GENMASK(2, 0) +#define CDC_WSA_RX_INP_MUX_RX_EC_CFG0 (0x0114) +#define CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0 (0x0118) +#define CDC_WSA_TX0_SPKR_PROT_PATH_CTL (0x0244) +#define CDC_WSA_TX_SPKR_PROT_RESET_MASK BIT(5) +#define CDC_WSA_TX_SPKR_PROT_RESET BIT(5) +#define CDC_WSA_TX_SPKR_PROT_NO_RESET 0 +#define CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK BIT(4) +#define CDC_WSA_TX_SPKR_PROT_CLK_ENABLE BIT(4) +#define CDC_WSA_TX_SPKR_PROT_CLK_DISABLE 0 +#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK GENMASK(3, 0) +#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K 0 +#define CDC_WSA_TX0_SPKR_PROT_PATH_CFG0 (0x0248) +#define CDC_WSA_TX1_SPKR_PROT_PATH_CTL (0x0264) +#define CDC_WSA_TX1_SPKR_PROT_PATH_CFG0 (0x0268) +#define CDC_WSA_TX2_SPKR_PROT_PATH_CTL (0x0284) +#define CDC_WSA_TX2_SPKR_PROT_PATH_CFG0 (0x0288) +#define CDC_WSA_TX3_SPKR_PROT_PATH_CTL (0x02A4) +#define CDC_WSA_TX3_SPKR_PROT_PATH_CFG0 (0x02A8) +#define CDC_WSA_INTR_CTRL_CFG (0x0340) +#define CDC_WSA_INTR_CTRL_CLR_COMMIT (0x0344) +#define CDC_WSA_INTR_CTRL_PIN1_MASK0 (0x0360) +#define CDC_WSA_INTR_CTRL_PIN1_STATUS0 (0x0368) +#define CDC_WSA_INTR_CTRL_PIN1_CLEAR0 (0x0370) +#define CDC_WSA_INTR_CTRL_PIN2_MASK0 (0x0380) +#define CDC_WSA_INTR_CTRL_PIN2_STATUS0 (0x0388) +#define CDC_WSA_INTR_CTRL_PIN2_CLEAR0 (0x0390) +#define CDC_WSA_INTR_CTRL_LEVEL0 (0x03C0) +#define CDC_WSA_INTR_CTRL_BYPASS0 (0x03C8) +#define CDC_WSA_INTR_CTRL_SET0 (0x03D0) +#define CDC_WSA_RX0_RX_PATH_CTL (0x0400) +#define CDC_WSA_RX_PATH_CLK_EN_MASK BIT(5) +#define CDC_WSA_RX_PATH_CLK_ENABLE BIT(5) +#define CDC_WSA_RX_PATH_CLK_DISABLE 0 +#define CDC_WSA_RX_PATH_PGA_MUTE_EN_MASK BIT(4) +#define CDC_WSA_RX_PATH_PGA_MUTE_ENABLE BIT(4) +#define CDC_WSA_RX_PATH_PGA_MUTE_DISABLE 0 +#define CDC_WSA_RX0_RX_PATH_CFG0 (0x0404) +#define CDC_WSA_RX_PATH_COMP_EN_MASK BIT(1) +#define CDC_WSA_RX_PATH_COMP_ENABLE BIT(1) +#define CDC_WSA_RX_PATH_HD2_EN_MASK BIT(2) +#define CDC_WSA_RX_PATH_HD2_ENABLE BIT(2) +#define CDC_WSA_RX_PATH_SPKR_RATE_MASK BIT(3) +#define CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072 BIT(3) +#define CDC_WSA_RX0_RX_PATH_CFG1 (0x0408) +#define CDC_WSA_RX_PATH_SMART_BST_EN_MASK BIT(0) +#define CDC_WSA_RX_PATH_SMART_BST_ENABLE BIT(0) +#define CDC_WSA_RX_PATH_SMART_BST_DISABLE 0 +#define CDC_WSA_RX0_RX_PATH_CFG2 (0x040C) +#define CDC_WSA_RX0_RX_PATH_CFG3 (0x0410) +#define CDC_WSA_RX_DC_DCOEFF_MASK GENMASK(1, 0) +#define CDC_WSA_RX0_RX_VOL_CTL (0x0414) +#define CDC_WSA_RX0_RX_PATH_MIX_CTL (0x0418) +#define CDC_WSA_RX_PATH_MIX_CLK_EN_MASK BIT(5) +#define CDC_WSA_RX_PATH_MIX_CLK_ENABLE BIT(5) +#define CDC_WSA_RX_PATH_MIX_CLK_DISABLE 0 +#define CDC_WSA_RX0_RX_PATH_MIX_CFG (0x041C) +#define CDC_WSA_RX0_RX_VOL_MIX_CTL (0x0420) +#define CDC_WSA_RX0_RX_PATH_SEC0 (0x0424) +#define CDC_WSA_RX0_RX_PATH_SEC1 (0x0428) +#define CDC_WSA_RX_PGA_HALF_DB_MASK BIT(0) +#define CDC_WSA_RX_PGA_HALF_DB_ENABLE BIT(0) +#define CDC_WSA_RX_PGA_HALF_DB_DISABLE 0 +#define CDC_WSA_RX0_RX_PATH_SEC2 (0x042C) +#define CDC_WSA_RX0_RX_PATH_SEC3 (0x0430) +#define CDC_WSA_RX_PATH_HD2_SCALE_MASK GENMASK(1, 0) +#define CDC_WSA_RX_PATH_HD2_ALPHA_MASK GENMASK(5, 2) +#define CDC_WSA_RX0_RX_PATH_SEC5 (0x0438) +#define CDC_WSA_RX0_RX_PATH_SEC6 (0x043C) +#define CDC_WSA_RX0_RX_PATH_SEC7 (0x0440) +#define CDC_WSA_RX0_RX_PATH_MIX_SEC0 (0x0444) +#define CDC_WSA_RX0_RX_PATH_MIX_SEC1 (0x0448) +#define CDC_WSA_RX0_RX_PATH_DSMDEM_CTL (0x044C) +#define CDC_WSA_RX_DSMDEM_CLK_EN_MASK BIT(0) +#define CDC_WSA_RX_DSMDEM_CLK_ENABLE BIT(0) +#define CDC_WSA_RX1_RX_PATH_CTL (0x0480) +#define CDC_WSA_RX1_RX_PATH_CFG0 (0x0484) +#define CDC_WSA_RX1_RX_PATH_CFG1 (0x0488) +#define CDC_WSA_RX1_RX_PATH_CFG2 (0x048C) +#define CDC_WSA_RX1_RX_PATH_CFG3 (0x0490) +#define CDC_WSA_RX1_RX_VOL_CTL (0x0494) +#define CDC_WSA_RX1_RX_PATH_MIX_CTL (0x0498) +#define CDC_WSA_RX1_RX_PATH_MIX_CFG (0x049C) +#define CDC_WSA_RX1_RX_VOL_MIX_CTL (0x04A0) +#define CDC_WSA_RX1_RX_PATH_SEC0 (0x04A4) +#define CDC_WSA_RX1_RX_PATH_SEC1 (0x04A8) +#define CDC_WSA_RX1_RX_PATH_SEC2 (0x04AC) +#define CDC_WSA_RX1_RX_PATH_SEC3 (0x04B0) +#define CDC_WSA_RX1_RX_PATH_SEC5 (0x04B8) +#define CDC_WSA_RX1_RX_PATH_SEC6 (0x04BC) +#define CDC_WSA_RX1_RX_PATH_SEC7 (0x04C0) +#define CDC_WSA_RX1_RX_PATH_MIX_SEC0 (0x04C4) +#define CDC_WSA_RX1_RX_PATH_MIX_SEC1 (0x04C8) +#define CDC_WSA_RX1_RX_PATH_DSMDEM_CTL (0x04CC) +#define CDC_WSA_BOOST0_BOOST_PATH_CTL (0x0500) +#define CDC_WSA_BOOST_PATH_CLK_EN_MASK BIT(4) +#define CDC_WSA_BOOST_PATH_CLK_ENABLE BIT(4) +#define CDC_WSA_BOOST_PATH_CLK_DISABLE 0 +#define CDC_WSA_BOOST0_BOOST_CTL (0x0504) +#define CDC_WSA_BOOST0_BOOST_CFG1 (0x0508) +#define CDC_WSA_BOOST0_BOOST_CFG2 (0x050C) +#define CDC_WSA_BOOST1_BOOST_PATH_CTL (0x0540) +#define CDC_WSA_BOOST1_BOOST_CTL (0x0544) +#define CDC_WSA_BOOST1_BOOST_CFG1 (0x0548) +#define CDC_WSA_BOOST1_BOOST_CFG2 (0x054C) +#define CDC_WSA_COMPANDER0_CTL0 (0x0580) +#define CDC_WSA_COMPANDER_CLK_EN_MASK BIT(0) +#define CDC_WSA_COMPANDER_CLK_ENABLE BIT(0) +#define CDC_WSA_COMPANDER_SOFT_RST_MASK BIT(1) +#define CDC_WSA_COMPANDER_SOFT_RST_ENABLE BIT(1) +#define CDC_WSA_COMPANDER_HALT_MASK BIT(2) +#define CDC_WSA_COMPANDER_HALT BIT(2) +#define CDC_WSA_COMPANDER0_CTL1 (0x0584) +#define CDC_WSA_COMPANDER0_CTL2 (0x0588) +#define CDC_WSA_COMPANDER0_CTL3 (0x058C) +#define CDC_WSA_COMPANDER0_CTL4 (0x0590) +#define CDC_WSA_COMPANDER0_CTL5 (0x0594) +#define CDC_WSA_COMPANDER0_CTL6 (0x0598) +#define CDC_WSA_COMPANDER0_CTL7 (0x059C) +#define CDC_WSA_COMPANDER1_CTL0 (0x05C0) +#define CDC_WSA_COMPANDER1_CTL1 (0x05C4) +#define CDC_WSA_COMPANDER1_CTL2 (0x05C8) +#define CDC_WSA_COMPANDER1_CTL3 (0x05CC) +#define CDC_WSA_COMPANDER1_CTL4 (0x05D0) +#define CDC_WSA_COMPANDER1_CTL5 (0x05D4) +#define CDC_WSA_COMPANDER1_CTL6 (0x05D8) +#define CDC_WSA_COMPANDER1_CTL7 (0x05DC) +#define CDC_WSA_SOFTCLIP0_CRC (0x0600) +#define CDC_WSA_SOFTCLIP_CLK_EN_MASK BIT(0) +#define CDC_WSA_SOFTCLIP_CLK_ENABLE BIT(0) +#define CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0604) +#define CDC_WSA_SOFTCLIP_EN_MASK BIT(0) +#define CDC_WSA_SOFTCLIP_ENABLE BIT(0) +#define CDC_WSA_SOFTCLIP1_CRC (0x0640) +#define CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0644) +#define CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL (0x0680) +#define CDC_WSA_EC_HQ_EC_CLK_EN_MASK BIT(0) +#define CDC_WSA_EC_HQ_EC_CLK_ENABLE BIT(0) +#define CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 (0x0684) +#define CDC_WSA_EC_HQ_EC_REF_PCM_RATE_MASK GENMASK(4, 1) +#define CDC_WSA_EC_HQ_EC_REF_PCM_RATE_48K BIT(3) +#define CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL (0x06C0) +#define CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0 (0x06C4) +#define CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL (0x0700) +#define CDC_WSA_SPLINE_ASRC0_CTL0 (0x0704) +#define CDC_WSA_SPLINE_ASRC0_CTL1 (0x0708) +#define CDC_WSA_SPLINE_ASRC0_FIFO_CTL (0x070C) +#define CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB (0x0710) +#define CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB (0x0714) +#define CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB (0x0718) +#define CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB (0x071C) +#define CDC_WSA_SPLINE_ASRC0_STATUS_FIFO (0x0720) +#define CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL (0x0740) +#define CDC_WSA_SPLINE_ASRC1_CTL0 (0x0744) +#define CDC_WSA_SPLINE_ASRC1_CTL1 (0x0748) +#define CDC_WSA_SPLINE_ASRC1_FIFO_CTL (0x074C) +#define CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB (0x0750) +#define CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB (0x0754) +#define CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB (0x0758) +#define CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB (0x075C) +#define CDC_WSA_SPLINE_ASRC1_STATUS_FIFO (0x0760) +#define WSA_MAX_OFFSET (0x0760) + +#define WSA_MACRO_RX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define WSA_MACRO_RX_MIX_RATES (SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define WSA_MACRO_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define WSA_MACRO_ECHO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_48000) +#define WSA_MACRO_ECHO_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define NUM_INTERPOLATORS 2 +#define WSA_NUM_CLKS_MAX 5 +#define WSA_MACRO_MCLK_FREQ 19200000 +#define WSA_MACRO_MUX_INP_MASK2 0x38 +#define WSA_MACRO_MUX_CFG_OFFSET 0x8 +#define WSA_MACRO_MUX_CFG1_OFFSET 0x4 +#define WSA_MACRO_RX_COMP_OFFSET 0x40 +#define WSA_MACRO_RX_SOFTCLIP_OFFSET 0x40 +#define WSA_MACRO_RX_PATH_OFFSET 0x80 +#define WSA_MACRO_RX_PATH_CFG3_OFFSET 0x10 +#define WSA_MACRO_RX_PATH_DSMDEM_OFFSET 0x4C +#define WSA_MACRO_FS_RATE_MASK 0x0F +#define WSA_MACRO_EC_MIX_TX0_MASK 0x03 +#define WSA_MACRO_EC_MIX_TX1_MASK 0x18 +#define WSA_MACRO_MAX_DMA_CH_PER_PORT 0x2 + +enum { + WSA_MACRO_GAIN_OFFSET_M1P5_DB, + WSA_MACRO_GAIN_OFFSET_0_DB, +}; +enum { + WSA_MACRO_RX0 = 0, + WSA_MACRO_RX1, + WSA_MACRO_RX_MIX, + WSA_MACRO_RX_MIX0 = WSA_MACRO_RX_MIX, + WSA_MACRO_RX_MIX1, + WSA_MACRO_RX_MAX, +}; + +enum { + WSA_MACRO_TX0 = 0, + WSA_MACRO_TX1, + WSA_MACRO_TX_MAX, +}; + +enum { + WSA_MACRO_EC0_MUX = 0, + WSA_MACRO_EC1_MUX, + WSA_MACRO_EC_MUX_MAX, +}; + +enum { + WSA_MACRO_COMP1, /* SPK_L */ + WSA_MACRO_COMP2, /* SPK_R */ + WSA_MACRO_COMP_MAX +}; + +enum { + WSA_MACRO_SOFTCLIP0, /* RX0 */ + WSA_MACRO_SOFTCLIP1, /* RX1 */ + WSA_MACRO_SOFTCLIP_MAX +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate int_prim_sample_rate_val[] = { + {8000, 0x0}, /* 8K */ + {16000, 0x1}, /* 16K */ + {24000, -EINVAL},/* 24K */ + {32000, 0x3}, /* 32K */ + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ + {384000, 0x7}, /* 384K */ + {44100, 0x8}, /* 44.1K */ +}; + +static struct interp_sample_rate int_mix_sample_rate_val[] = { + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ +}; + +enum { + WSA_MACRO_AIF_INVALID = 0, + WSA_MACRO_AIF1_PB, + WSA_MACRO_AIF_MIX1_PB, + WSA_MACRO_AIF_VI, + WSA_MACRO_AIF_ECHO, + WSA_MACRO_MAX_DAIS, +}; + +struct wsa_macro { + struct device *dev; + int comp_enabled[WSA_MACRO_COMP_MAX]; + int ec_hq[WSA_MACRO_RX1 + 1]; + u16 prim_int_users[WSA_MACRO_RX1 + 1]; + u16 wsa_mclk_users; + unsigned long active_ch_mask[WSA_MACRO_MAX_DAIS]; + unsigned long active_ch_cnt[WSA_MACRO_MAX_DAIS]; + int rx_port_value[WSA_MACRO_RX_MAX]; + int ear_spkr_gain; + int spkr_gain_offset; + int spkr_mode; + int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX]; + int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX]; + struct regmap *regmap; + struct clk *mclk; + struct clk *npl; + struct clk *macro; + struct clk *dcodec; + struct clk *fsgen; + struct clk_hw hw; +}; +#define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw) + +static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); + +static const char *const rx_text[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1" +}; + +static const char *const rx_mix_text[] = { + "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1" +}; + +static const char *const rx_mix_ec_text[] = { + "ZERO", "RX_MIX_TX0", "RX_MIX_TX1" +}; + +static const char *const rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF_MIX1_PB" +}; + +static const char *const rx_sidetone_mix_text[] = { + "ZERO", "SRC0" +}; + +static const char * const wsa_macro_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(wsa_macro_ear_spkr_pa_gain_enum, + wsa_macro_ear_spkr_pa_gain_text); + +/* RX INT0 */ +static const struct soc_enum rx0_prim_inp0_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 0, 7, rx_text); + +static const struct soc_enum rx0_prim_inp1_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, + 3, 7, rx_text); + +static const struct soc_enum rx0_prim_inp2_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 3, 7, rx_text); + +static const struct soc_enum rx0_mix_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, + 0, 5, rx_mix_text); + +static const struct soc_enum rx0_sidetone_mix_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_sidetone_mix_text); + +static const struct snd_kcontrol_new rx0_prim_inp0_mux = + SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum); + +static const struct snd_kcontrol_new rx0_prim_inp1_mux = + SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum); + +static const struct snd_kcontrol_new rx0_prim_inp2_mux = + SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum); + +static const struct snd_kcontrol_new rx0_mix_mux = + SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum); + +static const struct snd_kcontrol_new rx0_sidetone_mix_mux = + SOC_DAPM_ENUM("WSA_RX0 SIDETONE MIX Mux", rx0_sidetone_mix_enum); + +/* RX INT1 */ +static const struct soc_enum rx1_prim_inp0_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 0, 7, rx_text); + +static const struct soc_enum rx1_prim_inp1_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, + 3, 7, rx_text); + +static const struct soc_enum rx1_prim_inp2_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 3, 7, rx_text); + +static const struct soc_enum rx1_mix_chain_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, + 0, 5, rx_mix_text); + +static const struct snd_kcontrol_new rx1_prim_inp0_mux = + SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum); + +static const struct snd_kcontrol_new rx1_prim_inp1_mux = + SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum); + +static const struct snd_kcontrol_new rx1_prim_inp2_mux = + SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum); + +static const struct snd_kcontrol_new rx1_mix_mux = + SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum); + +static const struct soc_enum rx_mix_ec0_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, + 0, 3, rx_mix_ec_text); + +static const struct soc_enum rx_mix_ec1_enum = + SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, + 3, 3, rx_mix_ec_text); + +static const struct snd_kcontrol_new rx_mix_ec0_mux = + SOC_DAPM_ENUM("WSA RX_MIX EC0_Mux", rx_mix_ec0_enum); + +static const struct snd_kcontrol_new rx_mix_ec1_mux = + SOC_DAPM_ENUM("WSA RX_MIX EC1_Mux", rx_mix_ec1_enum); + +static const struct reg_default wsa_defaults[] = { + /* WSA Macro */ + { CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, 0x00}, + { CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00}, + { CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, 0x00}, + { CDC_WSA_TOP_TOP_CFG0, 0x00}, + { CDC_WSA_TOP_TOP_CFG1, 0x00}, + { CDC_WSA_TOP_FREQ_MCLK, 0x00}, + { CDC_WSA_TOP_DEBUG_BUS_SEL, 0x00}, + { CDC_WSA_TOP_DEBUG_EN0, 0x00}, + { CDC_WSA_TOP_DEBUG_EN1, 0x00}, + { CDC_WSA_TOP_DEBUG_DSM_LB, 0x88}, + { CDC_WSA_TOP_RX_I2S_CTL, 0x0C}, + { CDC_WSA_TOP_TX_I2S_CTL, 0x0C}, + { CDC_WSA_TOP_I2S_CLK, 0x02}, + { CDC_WSA_TOP_I2S_RESET, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_INT0_CFG0, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_INT0_CFG1, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_INT1_CFG0, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_INT1_CFG1, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_MIX_CFG0, 0x00}, + { CDC_WSA_RX_INP_MUX_RX_EC_CFG0, 0x00}, + { CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, 0x00}, + { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x02}, + { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x00}, + { CDC_WSA_INTR_CTRL_CFG, 0x00}, + { CDC_WSA_INTR_CTRL_CLR_COMMIT, 0x00}, + { CDC_WSA_INTR_CTRL_PIN1_MASK0, 0xFF}, + { CDC_WSA_INTR_CTRL_PIN1_STATUS0, 0x00}, + { CDC_WSA_INTR_CTRL_PIN1_CLEAR0, 0x00}, + { CDC_WSA_INTR_CTRL_PIN2_MASK0, 0xFF}, + { CDC_WSA_INTR_CTRL_PIN2_STATUS0, 0x00}, + { CDC_WSA_INTR_CTRL_PIN2_CLEAR0, 0x00}, + { CDC_WSA_INTR_CTRL_LEVEL0, 0x00}, + { CDC_WSA_INTR_CTRL_BYPASS0, 0x00}, + { CDC_WSA_INTR_CTRL_SET0, 0x00}, + { CDC_WSA_RX0_RX_PATH_CTL, 0x04}, + { CDC_WSA_RX0_RX_PATH_CFG0, 0x00}, + { CDC_WSA_RX0_RX_PATH_CFG1, 0x64}, + { CDC_WSA_RX0_RX_PATH_CFG2, 0x8F}, + { CDC_WSA_RX0_RX_PATH_CFG3, 0x00}, + { CDC_WSA_RX0_RX_VOL_CTL, 0x00}, + { CDC_WSA_RX0_RX_PATH_MIX_CTL, 0x04}, + { CDC_WSA_RX0_RX_PATH_MIX_CFG, 0x7E}, + { CDC_WSA_RX0_RX_VOL_MIX_CTL, 0x00}, + { CDC_WSA_RX0_RX_PATH_SEC0, 0x04}, + { CDC_WSA_RX0_RX_PATH_SEC1, 0x08}, + { CDC_WSA_RX0_RX_PATH_SEC2, 0x00}, + { CDC_WSA_RX0_RX_PATH_SEC3, 0x00}, + { CDC_WSA_RX0_RX_PATH_SEC5, 0x00}, + { CDC_WSA_RX0_RX_PATH_SEC6, 0x00}, + { CDC_WSA_RX0_RX_PATH_SEC7, 0x00}, + { CDC_WSA_RX0_RX_PATH_MIX_SEC0, 0x08}, + { CDC_WSA_RX0_RX_PATH_MIX_SEC1, 0x00}, + { CDC_WSA_RX0_RX_PATH_DSMDEM_CTL, 0x00}, + { CDC_WSA_RX1_RX_PATH_CFG0, 0x00}, + { CDC_WSA_RX1_RX_PATH_CFG1, 0x64}, + { CDC_WSA_RX1_RX_PATH_CFG2, 0x8F}, + { CDC_WSA_RX1_RX_PATH_CFG3, 0x00}, + { CDC_WSA_RX1_RX_VOL_CTL, 0x00}, + { CDC_WSA_RX1_RX_PATH_MIX_CTL, 0x04}, + { CDC_WSA_RX1_RX_PATH_MIX_CFG, 0x7E}, + { CDC_WSA_RX1_RX_VOL_MIX_CTL, 0x00}, + { CDC_WSA_RX1_RX_PATH_SEC0, 0x04}, + { CDC_WSA_RX1_RX_PATH_SEC1, 0x08}, + { CDC_WSA_RX1_RX_PATH_SEC2, 0x00}, + { CDC_WSA_RX1_RX_PATH_SEC3, 0x00}, + { CDC_WSA_RX1_RX_PATH_SEC5, 0x00}, + { CDC_WSA_RX1_RX_PATH_SEC6, 0x00}, + { CDC_WSA_RX1_RX_PATH_SEC7, 0x00}, + { CDC_WSA_RX1_RX_PATH_MIX_SEC0, 0x08}, + { CDC_WSA_RX1_RX_PATH_MIX_SEC1, 0x00}, + { CDC_WSA_RX1_RX_PATH_DSMDEM_CTL, 0x00}, + { CDC_WSA_BOOST0_BOOST_PATH_CTL, 0x00}, + { CDC_WSA_BOOST0_BOOST_CTL, 0xD0}, + { CDC_WSA_BOOST0_BOOST_CFG1, 0x89}, + { CDC_WSA_BOOST0_BOOST_CFG2, 0x04}, + { CDC_WSA_BOOST1_BOOST_PATH_CTL, 0x00}, + { CDC_WSA_BOOST1_BOOST_CTL, 0xD0}, + { CDC_WSA_BOOST1_BOOST_CFG1, 0x89}, + { CDC_WSA_BOOST1_BOOST_CFG2, 0x04}, + { CDC_WSA_COMPANDER0_CTL0, 0x60}, + { CDC_WSA_COMPANDER0_CTL1, 0xDB}, + { CDC_WSA_COMPANDER0_CTL2, 0xFF}, + { CDC_WSA_COMPANDER0_CTL3, 0x35}, + { CDC_WSA_COMPANDER0_CTL4, 0xFF}, + { CDC_WSA_COMPANDER0_CTL5, 0x00}, + { CDC_WSA_COMPANDER0_CTL6, 0x01}, + { CDC_WSA_COMPANDER0_CTL7, 0x28}, + { CDC_WSA_COMPANDER1_CTL0, 0x60}, + { CDC_WSA_COMPANDER1_CTL1, 0xDB}, + { CDC_WSA_COMPANDER1_CTL2, 0xFF}, + { CDC_WSA_COMPANDER1_CTL3, 0x35}, + { CDC_WSA_COMPANDER1_CTL4, 0xFF}, + { CDC_WSA_COMPANDER1_CTL5, 0x00}, + { CDC_WSA_COMPANDER1_CTL6, 0x01}, + { CDC_WSA_COMPANDER1_CTL7, 0x28}, + { CDC_WSA_SOFTCLIP0_CRC, 0x00}, + { CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38}, + { CDC_WSA_SOFTCLIP1_CRC, 0x00}, + { CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38}, + { CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL, 0x00}, + { CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0, 0x01}, + { CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL, 0x00}, + { CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0, 0x01}, + { CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL, 0x00}, + { CDC_WSA_SPLINE_ASRC0_CTL0, 0x00}, + { CDC_WSA_SPLINE_ASRC0_CTL1, 0x00}, + { CDC_WSA_SPLINE_ASRC0_FIFO_CTL, 0xA8}, + { CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00}, + { CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00}, + { CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00}, + { CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00}, + { CDC_WSA_SPLINE_ASRC0_STATUS_FIFO, 0x00}, + { CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL, 0x00}, + { CDC_WSA_SPLINE_ASRC1_CTL0, 0x00}, + { CDC_WSA_SPLINE_ASRC1_CTL1, 0x00}, + { CDC_WSA_SPLINE_ASRC1_FIFO_CTL, 0xA8}, + { CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00}, + { CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00}, + { CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00}, + { CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00}, + { CDC_WSA_SPLINE_ASRC1_STATUS_FIFO, 0x00}, +}; + +static bool wsa_is_wronly_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case CDC_WSA_INTR_CTRL_CLR_COMMIT: + case CDC_WSA_INTR_CTRL_PIN1_CLEAR0: + case CDC_WSA_INTR_CTRL_PIN2_CLEAR0: + return true; + } + + return false; +} + +static bool wsa_is_rw_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL: + case CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL: + case CDC_WSA_CLK_RST_CTRL_SWR_CONTROL: + case CDC_WSA_TOP_TOP_CFG0: + case CDC_WSA_TOP_TOP_CFG1: + case CDC_WSA_TOP_FREQ_MCLK: + case CDC_WSA_TOP_DEBUG_BUS_SEL: + case CDC_WSA_TOP_DEBUG_EN0: + case CDC_WSA_TOP_DEBUG_EN1: + case CDC_WSA_TOP_DEBUG_DSM_LB: + case CDC_WSA_TOP_RX_I2S_CTL: + case CDC_WSA_TOP_TX_I2S_CTL: + case CDC_WSA_TOP_I2S_CLK: + case CDC_WSA_TOP_I2S_RESET: + case CDC_WSA_RX_INP_MUX_RX_INT0_CFG0: + case CDC_WSA_RX_INP_MUX_RX_INT0_CFG1: + case CDC_WSA_RX_INP_MUX_RX_INT1_CFG0: + case CDC_WSA_RX_INP_MUX_RX_INT1_CFG1: + case CDC_WSA_RX_INP_MUX_RX_MIX_CFG0: + case CDC_WSA_RX_INP_MUX_RX_EC_CFG0: + case CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0: + case CDC_WSA_TX0_SPKR_PROT_PATH_CTL: + case CDC_WSA_TX0_SPKR_PROT_PATH_CFG0: + case CDC_WSA_TX1_SPKR_PROT_PATH_CTL: + case CDC_WSA_TX1_SPKR_PROT_PATH_CFG0: + case CDC_WSA_TX2_SPKR_PROT_PATH_CTL: + case CDC_WSA_TX2_SPKR_PROT_PATH_CFG0: + case CDC_WSA_TX3_SPKR_PROT_PATH_CTL: + case CDC_WSA_TX3_SPKR_PROT_PATH_CFG0: + case CDC_WSA_INTR_CTRL_CFG: + case CDC_WSA_INTR_CTRL_PIN1_MASK0: + case CDC_WSA_INTR_CTRL_PIN2_MASK0: + case CDC_WSA_INTR_CTRL_LEVEL0: + case CDC_WSA_INTR_CTRL_BYPASS0: + case CDC_WSA_INTR_CTRL_SET0: + case CDC_WSA_RX0_RX_PATH_CTL: + case CDC_WSA_RX0_RX_PATH_CFG0: + case CDC_WSA_RX0_RX_PATH_CFG1: + case CDC_WSA_RX0_RX_PATH_CFG2: + case CDC_WSA_RX0_RX_PATH_CFG3: + case CDC_WSA_RX0_RX_VOL_CTL: + case CDC_WSA_RX0_RX_PATH_MIX_CTL: + case CDC_WSA_RX0_RX_PATH_MIX_CFG: + case CDC_WSA_RX0_RX_VOL_MIX_CTL: + case CDC_WSA_RX0_RX_PATH_SEC0: + case CDC_WSA_RX0_RX_PATH_SEC1: + case CDC_WSA_RX0_RX_PATH_SEC2: + case CDC_WSA_RX0_RX_PATH_SEC3: + case CDC_WSA_RX0_RX_PATH_SEC5: + case CDC_WSA_RX0_RX_PATH_SEC6: + case CDC_WSA_RX0_RX_PATH_SEC7: + case CDC_WSA_RX0_RX_PATH_MIX_SEC0: + case CDC_WSA_RX0_RX_PATH_MIX_SEC1: + case CDC_WSA_RX0_RX_PATH_DSMDEM_CTL: + case CDC_WSA_RX1_RX_PATH_CTL: + case CDC_WSA_RX1_RX_PATH_CFG0: + case CDC_WSA_RX1_RX_PATH_CFG1: + case CDC_WSA_RX1_RX_PATH_CFG2: + case CDC_WSA_RX1_RX_PATH_CFG3: + case CDC_WSA_RX1_RX_VOL_CTL: + case CDC_WSA_RX1_RX_PATH_MIX_CTL: + case CDC_WSA_RX1_RX_PATH_MIX_CFG: + case CDC_WSA_RX1_RX_VOL_MIX_CTL: + case CDC_WSA_RX1_RX_PATH_SEC0: + case CDC_WSA_RX1_RX_PATH_SEC1: + case CDC_WSA_RX1_RX_PATH_SEC2: + case CDC_WSA_RX1_RX_PATH_SEC3: + case CDC_WSA_RX1_RX_PATH_SEC5: + case CDC_WSA_RX1_RX_PATH_SEC6: + case CDC_WSA_RX1_RX_PATH_SEC7: + case CDC_WSA_RX1_RX_PATH_MIX_SEC0: + case CDC_WSA_RX1_RX_PATH_MIX_SEC1: + case CDC_WSA_RX1_RX_PATH_DSMDEM_CTL: + case CDC_WSA_BOOST0_BOOST_PATH_CTL: + case CDC_WSA_BOOST0_BOOST_CTL: + case CDC_WSA_BOOST0_BOOST_CFG1: + case CDC_WSA_BOOST0_BOOST_CFG2: + case CDC_WSA_BOOST1_BOOST_PATH_CTL: + case CDC_WSA_BOOST1_BOOST_CTL: + case CDC_WSA_BOOST1_BOOST_CFG1: + case CDC_WSA_BOOST1_BOOST_CFG2: + case CDC_WSA_COMPANDER0_CTL0: + case CDC_WSA_COMPANDER0_CTL1: + case CDC_WSA_COMPANDER0_CTL2: + case CDC_WSA_COMPANDER0_CTL3: + case CDC_WSA_COMPANDER0_CTL4: + case CDC_WSA_COMPANDER0_CTL5: + case CDC_WSA_COMPANDER0_CTL7: + case CDC_WSA_COMPANDER1_CTL0: + case CDC_WSA_COMPANDER1_CTL1: + case CDC_WSA_COMPANDER1_CTL2: + case CDC_WSA_COMPANDER1_CTL3: + case CDC_WSA_COMPANDER1_CTL4: + case CDC_WSA_COMPANDER1_CTL5: + case CDC_WSA_COMPANDER1_CTL7: + case CDC_WSA_SOFTCLIP0_CRC: + case CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL: + case CDC_WSA_SOFTCLIP1_CRC: + case CDC_WSA_SOFTCLIP1_SOFTCLIP_CTRL: + case CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL: + case CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0: + case CDC_WSA_EC_HQ1_EC_REF_HQ_PATH_CTL: + case CDC_WSA_EC_HQ1_EC_REF_HQ_CFG0: + case CDC_WSA_SPLINE_ASRC0_CLK_RST_CTL: + case CDC_WSA_SPLINE_ASRC0_CTL0: + case CDC_WSA_SPLINE_ASRC0_CTL1: + case CDC_WSA_SPLINE_ASRC0_FIFO_CTL: + case CDC_WSA_SPLINE_ASRC1_CLK_RST_CTL: + case CDC_WSA_SPLINE_ASRC1_CTL0: + case CDC_WSA_SPLINE_ASRC1_CTL1: + case CDC_WSA_SPLINE_ASRC1_FIFO_CTL: + return true; + } + + return false; +} + +static bool wsa_is_writeable_register(struct device *dev, unsigned int reg) +{ + bool ret; + + ret = wsa_is_rw_register(dev, reg); + if (!ret) + return wsa_is_wronly_register(dev, reg); + + return ret; +} + +static bool wsa_is_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CDC_WSA_INTR_CTRL_CLR_COMMIT: + case CDC_WSA_INTR_CTRL_PIN1_CLEAR0: + case CDC_WSA_INTR_CTRL_PIN2_CLEAR0: + case CDC_WSA_INTR_CTRL_PIN1_STATUS0: + case CDC_WSA_INTR_CTRL_PIN2_STATUS0: + case CDC_WSA_COMPANDER0_CTL6: + case CDC_WSA_COMPANDER1_CTL6: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FIFO: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO: + return true; + } + + return wsa_is_rw_register(dev, reg); +} + +static bool wsa_is_volatile_register(struct device *dev, unsigned int reg) +{ + /* Update volatile list for rx/tx macros */ + switch (reg) { + case CDC_WSA_INTR_CTRL_PIN1_STATUS0: + case CDC_WSA_INTR_CTRL_PIN2_STATUS0: + case CDC_WSA_COMPANDER0_CTL6: + case CDC_WSA_COMPANDER1_CTL6: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMIN_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FMAX_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC0_STATUS_FIFO: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMIN_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_LSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FMAX_CNTR_MSB: + case CDC_WSA_SPLINE_ASRC1_STATUS_FIFO: + return true; + } + return false; +} + +static const struct regmap_config wsa_regmap_config = { + .name = "wsa_macro", + .reg_bits = 16, + .val_bits = 32, /* 8 but with 32 bit read/write */ + .reg_stride = 4, + .cache_type = REGCACHE_FLAT, + .reg_defaults = wsa_defaults, + .num_reg_defaults = ARRAY_SIZE(wsa_defaults), + .max_register = WSA_MAX_OFFSET, + .writeable_reg = wsa_is_writeable_register, + .volatile_reg = wsa_is_volatile_register, + .readable_reg = wsa_is_readable_register, +}; + +/** + * wsa_macro_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @component: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode) +{ + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + wsa->spkr_mode = mode; + + switch (mode) { + case WSA_MACRO_SPKR_MODE_1: + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x00); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x00); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x00); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x00); + snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x44); + snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x44); + break; + default: + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01); + snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01); + snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x58); + snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x58); + break; + } + return 0; +} +EXPORT_SYMBOL(wsa_macro_set_spkr_mode); + +static int wsa_macro_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 int_prim_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j, port; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_component *component = dai->component; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + for_each_set_bit(port, &wsa->active_ch_mask[dai->id], WSA_MACRO_RX_MAX) { + int_1_mix1_inp = port; + if ((int_1_mix1_inp < WSA_MACRO_RX0) || (int_1_mix1_inp > WSA_MACRO_RX_MIX1)) { + dev_err(component->dev, "%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg0 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the cdc_dma rx port + * is connected + */ + for (j = 0; j < NUM_INTERPOLATORS; j++) { + int_mux_cfg1 = int_mux_cfg0 + WSA_MACRO_MUX_CFG1_OFFSET; + inp0_sel = snd_soc_component_read_field(component, int_mux_cfg0, + CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK); + inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0, + CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK); + inp2_sel = snd_soc_component_read_field(component, int_mux_cfg1, + CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK); + + if ((inp0_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || + (inp1_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0) || + (inp2_sel == int_1_mix1_inp + INTn_1_INP_SEL_RX0)) { + int_fs_reg = CDC_WSA_RX0_RX_PATH_CTL + + WSA_MACRO_RX_PATH_OFFSET * j; + /* sample_rate is in Hz */ + snd_soc_component_update_bits(component, int_fs_reg, + WSA_MACRO_FS_RATE_MASK, + int_prim_fs_rate_reg_val); + } + int_mux_cfg0 += WSA_MACRO_MUX_CFG_OFFSET; + } + } + + return 0; +} + +static int wsa_macro_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 int_mix_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j, port; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_component *component = dai->component; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + for_each_set_bit(port, &wsa->active_ch_mask[dai->id], WSA_MACRO_RX_MAX) { + int_2_inp = port; + if ((int_2_inp < WSA_MACRO_RX0) || (int_2_inp > WSA_MACRO_RX_MIX1)) { + dev_err(component->dev, "%s: Invalid RX port, Dai ID is %d\n", + __func__, dai->id); + return -EINVAL; + } + + int_mux_cfg1 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < NUM_INTERPOLATORS; j++) { + int_mux_cfg1_val = snd_soc_component_read_field(component, int_mux_cfg1, + CDC_WSA_RX_INTX_2_SEL_MASK); + + if (int_mux_cfg1_val == int_2_inp + INTn_2_INP_SEL_RX0) { + int_fs_reg = CDC_WSA_RX0_RX_PATH_MIX_CTL + + WSA_MACRO_RX_PATH_OFFSET * j; + + snd_soc_component_update_bits(component, + int_fs_reg, + WSA_MACRO_FS_RATE_MASK, + int_mix_fs_rate_reg_val); + } + int_mux_cfg1 += WSA_MACRO_MUX_CFG_OFFSET; + } + } + return 0; +} + +static int wsa_macro_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + /* set mixing path rate */ + for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) { + if (sample_rate == int_mix_sample_rate_val[i].sample_rate) { + rate_val = int_mix_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) || (rate_val < 0)) + goto prim_rate; + + ret = wsa_macro_set_mix_interpolator_rate(dai, (u8) rate_val, sample_rate); + if (ret < 0) + return ret; +prim_rate: + /* set primary path sample rate */ + for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) { + if (sample_rate == int_prim_sample_rate_val[i].sample_rate) { + rate_val = int_prim_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) || (rate_val < 0)) + return -EINVAL; + + ret = wsa_macro_set_prim_interpolator_rate(dai, (u8) rate_val, sample_rate); + + return ret; +} + +static int wsa_macro_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int ret; + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = wsa_macro_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(component->dev, + "%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + break; + default: + break; + } + return 0; +} + +static int wsa_macro_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct snd_soc_component *component = dai->component; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u16 val, mask = 0, cnt = 0, temp; + + switch (dai->id) { + case WSA_MACRO_AIF_VI: + *tx_slot = wsa->active_ch_mask[dai->id]; + *tx_num = wsa->active_ch_cnt[dai->id]; + break; + case WSA_MACRO_AIF1_PB: + case WSA_MACRO_AIF_MIX1_PB: + for_each_set_bit(temp, &wsa->active_ch_mask[dai->id], + WSA_MACRO_RX_MAX) { + mask |= (1 << temp); + if (++cnt == WSA_MACRO_MAX_DMA_CH_PER_PORT) + break; + } + if (mask & 0x0C) + mask = mask >> 0x2; + *rx_slot = mask; + *rx_num = cnt; + break; + case WSA_MACRO_AIF_ECHO: + val = snd_soc_component_read(component, CDC_WSA_RX_INP_MUX_RX_MIX_CFG0); + if (val & WSA_MACRO_EC_MIX_TX1_MASK) { + mask |= 0x2; + cnt++; + } + if (val & WSA_MACRO_EC_MIX_TX0_MASK) { + mask |= 0x1; + cnt++; + } + *tx_slot = mask; + *tx_num = cnt; + break; + default: + dev_err(component->dev, "%s: Invalid AIF\n", __func__); + break; + } + return 0; +} + +static const struct snd_soc_dai_ops wsa_macro_dai_ops = { + .hw_params = wsa_macro_hw_params, + .get_channel_map = wsa_macro_get_channel_map, +}; + +static struct snd_soc_dai_driver wsa_macro_dai[] = { + { + .name = "wsa_macro_rx1", + .id = WSA_MACRO_AIF1_PB, + .playback = { + .stream_name = "WSA_AIF1 Playback", + .rates = WSA_MACRO_RX_RATES, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_rx_mix", + .id = WSA_MACRO_AIF_MIX1_PB, + .playback = { + .stream_name = "WSA_AIF_MIX1 Playback", + .rates = WSA_MACRO_RX_MIX_RATES, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 192000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_vifeedback", + .id = WSA_MACRO_AIF_VI, + .capture = { + .stream_name = "WSA_AIF_VI Capture", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = WSA_MACRO_RX_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wsa_macro_dai_ops, + }, + { + .name = "wsa_macro_echo", + .id = WSA_MACRO_AIF_ECHO, + .capture = { + .stream_name = "WSA_AIF_ECHO Capture", + .rates = WSA_MACRO_ECHO_RATES, + .formats = WSA_MACRO_ECHO_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wsa_macro_dai_ops, + }, +}; + +static void wsa_macro_mclk_enable(struct wsa_macro *wsa, bool mclk_enable) +{ + struct regmap *regmap = wsa->regmap; + + if (mclk_enable) { + if (wsa->wsa_mclk_users == 0) { + regcache_mark_dirty(regmap); + regcache_sync(regmap); + /* 9.6MHz MCLK, set value 0x00 if other frequency */ + regmap_update_bits(regmap, CDC_WSA_TOP_FREQ_MCLK, 0x01, 0x01); + regmap_update_bits(regmap, + CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, + CDC_WSA_MCLK_EN_MASK, + CDC_WSA_MCLK_ENABLE); + regmap_update_bits(regmap, + CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_WSA_FS_CNT_EN_MASK, + CDC_WSA_FS_CNT_ENABLE); + } + wsa->wsa_mclk_users++; + } else { + if (wsa->wsa_mclk_users <= 0) { + dev_err(wsa->dev, "clock already disabled\n"); + wsa->wsa_mclk_users = 0; + return; + } + wsa->wsa_mclk_users--; + if (wsa->wsa_mclk_users == 0) { + regmap_update_bits(regmap, + CDC_WSA_CLK_RST_CTRL_FS_CNT_CONTROL, + CDC_WSA_FS_CNT_EN_MASK, + CDC_WSA_FS_CNT_DISABLE); + regmap_update_bits(regmap, + CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL, + CDC_WSA_MCLK_EN_MASK, + CDC_WSA_MCLK_DISABLE); + } + } +} + +static int wsa_macro_mclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + wsa_macro_mclk_enable(wsa, event == SND_SOC_DAPM_PRE_PMU); + return 0; +} + +static int wsa_macro_enable_vi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u32 tx_reg0, tx_reg1; + + if (test_bit(WSA_MACRO_TX0, &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + tx_reg0 = CDC_WSA_TX0_SPKR_PROT_PATH_CTL; + tx_reg1 = CDC_WSA_TX1_SPKR_PROT_PATH_CTL; + } else if (test_bit(WSA_MACRO_TX1, &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + tx_reg0 = CDC_WSA_TX2_SPKR_PROT_PATH_CTL; + tx_reg1 = CDC_WSA_TX3_SPKR_PROT_PATH_CTL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Enable V&I sensing */ + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_RESET); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_RESET); + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK, + CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK, + CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K); + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK, + CDC_WSA_TX_SPKR_PROT_CLK_ENABLE); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK, + CDC_WSA_TX_SPKR_PROT_CLK_ENABLE); + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_NO_RESET); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_NO_RESET); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable V&I sensing */ + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_RESET); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_RESET_MASK, + CDC_WSA_TX_SPKR_PROT_RESET); + snd_soc_component_update_bits(component, tx_reg0, + CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK, + CDC_WSA_TX_SPKR_PROT_CLK_DISABLE); + snd_soc_component_update_bits(component, tx_reg1, + CDC_WSA_TX_SPKR_PROT_CLK_EN_MASK, + CDC_WSA_TX_SPKR_PROT_CLK_DISABLE); + break; + } + + return 0; +} + +static int wsa_macro_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 path_reg, gain_reg; + int val; + + switch (w->shift) { + case WSA_MACRO_RX_MIX0: + path_reg = CDC_WSA_RX0_RX_PATH_MIX_CTL; + gain_reg = CDC_WSA_RX0_RX_VOL_MIX_CTL; + break; + case WSA_MACRO_RX_MIX1: + path_reg = CDC_WSA_RX1_RX_PATH_MIX_CTL; + gain_reg = CDC_WSA_RX1_RX_VOL_MIX_CTL; + break; + default: + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read(component, gain_reg); + snd_soc_component_write(component, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, path_reg, + CDC_WSA_RX_PATH_MIX_CLK_EN_MASK, + CDC_WSA_RX_PATH_MIX_CLK_DISABLE); + break; + } + + return 0; +} + +static void wsa_macro_hd2_control(struct snd_soc_component *component, + u16 reg, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg; + + if (reg == CDC_WSA_RX0_RX_PATH_CTL) { + hd2_scale_reg = CDC_WSA_RX0_RX_PATH_SEC3; + hd2_enable_reg = CDC_WSA_RX0_RX_PATH_CFG0; + } + if (reg == CDC_WSA_RX1_RX_PATH_CTL) { + hd2_scale_reg = CDC_WSA_RX1_RX_PATH_SEC3; + hd2_enable_reg = CDC_WSA_RX1_RX_PATH_CFG0; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(component, hd2_scale_reg, + CDC_WSA_RX_PATH_HD2_ALPHA_MASK, + 0x10); + snd_soc_component_update_bits(component, hd2_scale_reg, + CDC_WSA_RX_PATH_HD2_SCALE_MASK, + 0x1); + snd_soc_component_update_bits(component, hd2_enable_reg, + CDC_WSA_RX_PATH_HD2_EN_MASK, + CDC_WSA_RX_PATH_HD2_ENABLE); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, hd2_enable_reg, + CDC_WSA_RX_PATH_HD2_EN_MASK, 0); + snd_soc_component_update_bits(component, hd2_scale_reg, + CDC_WSA_RX_PATH_HD2_SCALE_MASK, + 0); + snd_soc_component_update_bits(component, hd2_scale_reg, + CDC_WSA_RX_PATH_HD2_ALPHA_MASK, + 0); + } +} + +static int wsa_macro_config_compander(struct snd_soc_component *component, + int comp, int event) +{ + u16 comp_ctl0_reg, rx_path_cfg0_reg; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + if (!wsa->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = CDC_WSA_COMPANDER0_CTL0 + + (comp * WSA_MACRO_RX_COMP_OFFSET); + rx_path_cfg0_reg = CDC_WSA_RX0_RX_PATH_CFG0 + + (comp * WSA_MACRO_RX_PATH_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_CLK_EN_MASK, + CDC_WSA_COMPANDER_CLK_ENABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_SOFT_RST_MASK, + CDC_WSA_COMPANDER_SOFT_RST_ENABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_SOFT_RST_MASK, + 0); + snd_soc_component_update_bits(component, rx_path_cfg0_reg, + CDC_WSA_RX_PATH_COMP_EN_MASK, + CDC_WSA_RX_PATH_COMP_ENABLE); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_HALT_MASK, + CDC_WSA_COMPANDER_HALT); + snd_soc_component_update_bits(component, rx_path_cfg0_reg, + CDC_WSA_RX_PATH_COMP_EN_MASK, 0); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_SOFT_RST_MASK, + CDC_WSA_COMPANDER_SOFT_RST_ENABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_SOFT_RST_MASK, + 0); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_CLK_EN_MASK, 0); + snd_soc_component_update_bits(component, comp_ctl0_reg, + CDC_WSA_COMPANDER_HALT_MASK, 0); + } + + return 0; +} + +static void wsa_macro_enable_softclip_clk(struct snd_soc_component *component, + struct wsa_macro *wsa, + int path, + bool enable) +{ + u16 softclip_clk_reg = CDC_WSA_SOFTCLIP0_CRC + + (path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + u8 softclip_mux_mask = (1 << path); + u8 softclip_mux_value = (1 << path); + + if (enable) { + if (wsa->softclip_clk_users[path] == 0) { + snd_soc_component_update_bits(component, + softclip_clk_reg, + CDC_WSA_SOFTCLIP_CLK_EN_MASK, + CDC_WSA_SOFTCLIP_CLK_ENABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, + softclip_mux_mask, softclip_mux_value); + } + wsa->softclip_clk_users[path]++; + } else { + wsa->softclip_clk_users[path]--; + if (wsa->softclip_clk_users[path] == 0) { + snd_soc_component_update_bits(component, + softclip_clk_reg, + CDC_WSA_SOFTCLIP_CLK_EN_MASK, + 0); + snd_soc_component_update_bits(component, + CDC_WSA_RX_INP_MUX_SOFTCLIP_CFG0, + softclip_mux_mask, 0x00); + } + } +} + +static int wsa_macro_config_softclip(struct snd_soc_component *component, + int path, int event) +{ + u16 softclip_ctrl_reg; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + int softclip_path = 0; + + if (path == WSA_MACRO_COMP1) + softclip_path = WSA_MACRO_SOFTCLIP0; + else if (path == WSA_MACRO_COMP2) + softclip_path = WSA_MACRO_SOFTCLIP1; + + if (!wsa->is_softclip_on[softclip_path]) + return 0; + + softclip_ctrl_reg = CDC_WSA_SOFTCLIP0_SOFTCLIP_CTRL + + (softclip_path * WSA_MACRO_RX_SOFTCLIP_OFFSET); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Softclip clock and mux */ + wsa_macro_enable_softclip_clk(component, wsa, softclip_path, + true); + /* Enable Softclip control */ + snd_soc_component_update_bits(component, softclip_ctrl_reg, + CDC_WSA_SOFTCLIP_EN_MASK, + CDC_WSA_SOFTCLIP_ENABLE); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, softclip_ctrl_reg, + CDC_WSA_SOFTCLIP_EN_MASK, 0); + wsa_macro_enable_softclip_clk(component, wsa, softclip_path, + false); + } + + return 0; +} + +static bool wsa_macro_adie_lb(struct snd_soc_component *component, + int interp_idx) +{ + u16 int_mux_cfg0, int_mux_cfg1; + u8 int_n_inp0, int_n_inp1, int_n_inp2; + + int_mux_cfg0 = CDC_WSA_RX_INP_MUX_RX_INT0_CFG0 + interp_idx * 8; + int_mux_cfg1 = int_mux_cfg0 + 4; + + int_n_inp0 = snd_soc_component_read_field(component, int_mux_cfg0, + CDC_WSA_RX_INTX_1_MIX_INP0_SEL_MASK); + if (int_n_inp0 == INTn_1_INP_SEL_DEC0 || + int_n_inp0 == INTn_1_INP_SEL_DEC1) + return true; + + int_n_inp1 = snd_soc_component_read_field(component, int_mux_cfg0, + CDC_WSA_RX_INTX_1_MIX_INP1_SEL_MASK); + if (int_n_inp1 == INTn_1_INP_SEL_DEC0 || + int_n_inp1 == INTn_1_INP_SEL_DEC1) + return true; + + int_n_inp2 = snd_soc_component_read_field(component, int_mux_cfg1, + CDC_WSA_RX_INTX_1_MIX_INP2_SEL_MASK); + if (int_n_inp2 == INTn_1_INP_SEL_DEC0 || + int_n_inp2 == INTn_1_INP_SEL_DEC1) + return true; + + return false; +} + +static int wsa_macro_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 reg; + + reg = CDC_WSA_RX0_RX_PATH_CTL + WSA_MACRO_RX_PATH_OFFSET * w->shift; + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wsa_macro_adie_lb(component, w->shift)) { + snd_soc_component_update_bits(component, reg, + CDC_WSA_RX_PATH_CLK_EN_MASK, + CDC_WSA_RX_PATH_CLK_ENABLE); + } + break; + default: + break; + } + return 0; +} + +static int wsa_macro_interp_get_primary_reg(u16 reg, u16 *ind) +{ + u16 prim_int_reg = 0; + + switch (reg) { + case CDC_WSA_RX0_RX_PATH_CTL: + case CDC_WSA_RX0_RX_PATH_MIX_CTL: + prim_int_reg = CDC_WSA_RX0_RX_PATH_CTL; + *ind = 0; + break; + case CDC_WSA_RX1_RX_PATH_CTL: + case CDC_WSA_RX1_RX_PATH_MIX_CTL: + prim_int_reg = CDC_WSA_RX1_RX_PATH_CTL; + *ind = 1; + break; + } + + return prim_int_reg; +} + +static int wsa_macro_enable_prim_interpolator(struct snd_soc_component *component, + u16 reg, int event) +{ + u16 prim_int_reg; + u16 ind = 0; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + prim_int_reg = wsa_macro_interp_get_primary_reg(reg, &ind); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wsa->prim_int_users[ind]++; + if (wsa->prim_int_users[ind] == 1) { + snd_soc_component_update_bits(component, + prim_int_reg + WSA_MACRO_RX_PATH_CFG3_OFFSET, + CDC_WSA_RX_DC_DCOEFF_MASK, + 0x3); + snd_soc_component_update_bits(component, prim_int_reg, + CDC_WSA_RX_PATH_PGA_MUTE_EN_MASK, + CDC_WSA_RX_PATH_PGA_MUTE_ENABLE); + wsa_macro_hd2_control(component, prim_int_reg, event); + snd_soc_component_update_bits(component, + prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET, + CDC_WSA_RX_DSMDEM_CLK_EN_MASK, + CDC_WSA_RX_DSMDEM_CLK_ENABLE); + } + if ((reg != prim_int_reg) && + ((snd_soc_component_read( + component, prim_int_reg)) & 0x10)) + snd_soc_component_update_bits(component, reg, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + wsa->prim_int_users[ind]--; + if (wsa->prim_int_users[ind] == 0) { + snd_soc_component_update_bits(component, + prim_int_reg + WSA_MACRO_RX_PATH_DSMDEM_OFFSET, + CDC_WSA_RX_DSMDEM_CLK_EN_MASK, 0); + wsa_macro_hd2_control(component, prim_int_reg, event); + } + break; + } + + return 0; +} + +static int wsa_macro_config_ear_spkr_gain(struct snd_soc_component *component, + struct wsa_macro *wsa, + int event, int gain_reg) +{ + int comp_gain_offset, val; + + switch (wsa->spkr_mode) { + /* Compander gain in WSA_MACRO_SPKR_MODE1 case is 12 dB */ + case WSA_MACRO_SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (wsa->comp_enabled[WSA_MACRO_COMP1] && + (gain_reg == CDC_WSA_RX0_RX_VOL_CTL) && + (wsa->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + wsa->ear_spkr_gain - 1; + snd_soc_component_write(component, gain_reg, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX0 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (wsa->comp_enabled[WSA_MACRO_COMP1] && + (gain_reg == CDC_WSA_RX0_RX_VOL_CTL) && + (wsa->ear_spkr_gain != 0)) { + snd_soc_component_write(component, gain_reg, 0x0); + } + break; + } + + return 0; +} + +static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + if (w->shift == WSA_MACRO_COMP1) { + reg = CDC_WSA_RX0_RX_PATH_CTL; + gain_reg = CDC_WSA_RX0_RX_VOL_CTL; + } else if (w->shift == WSA_MACRO_COMP2) { + reg = CDC_WSA_RX1_RX_PATH_CTL; + gain_reg = CDC_WSA_RX1_RX_VOL_CTL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Reset if needed */ + wsa_macro_enable_prim_interpolator(component, reg, event); + break; + case SND_SOC_DAPM_POST_PMU: + wsa_macro_config_compander(component, w->shift, event); + wsa_macro_config_softclip(component, w->shift, event); + /* apply gain after int clk is enabled */ + if ((wsa->spkr_gain_offset == WSA_MACRO_GAIN_OFFSET_M1P5_DB) && + (wsa->comp_enabled[WSA_MACRO_COMP1] || + wsa->comp_enabled[WSA_MACRO_COMP2])) { + snd_soc_component_update_bits(component, + CDC_WSA_RX0_RX_PATH_SEC1, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_ENABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX0_RX_PATH_MIX_SEC0, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_ENABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX1_RX_PATH_SEC1, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_ENABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX1_RX_PATH_MIX_SEC0, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_ENABLE); + offset_val = -2; + } + val = snd_soc_component_read(component, gain_reg); + val += offset_val; + snd_soc_component_write(component, gain_reg, val); + wsa_macro_config_ear_spkr_gain(component, wsa, + event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + wsa_macro_config_compander(component, w->shift, event); + wsa_macro_config_softclip(component, w->shift, event); + wsa_macro_enable_prim_interpolator(component, reg, event); + if ((wsa->spkr_gain_offset == WSA_MACRO_GAIN_OFFSET_M1P5_DB) && + (wsa->comp_enabled[WSA_MACRO_COMP1] || + wsa->comp_enabled[WSA_MACRO_COMP2])) { + snd_soc_component_update_bits(component, + CDC_WSA_RX0_RX_PATH_SEC1, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_DISABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX0_RX_PATH_MIX_SEC0, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_DISABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX1_RX_PATH_SEC1, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_DISABLE); + snd_soc_component_update_bits(component, + CDC_WSA_RX1_RX_PATH_MIX_SEC0, + CDC_WSA_RX_PGA_HALF_DB_MASK, + CDC_WSA_RX_PGA_HALF_DB_DISABLE); + offset_val = 2; + val = snd_soc_component_read(component, gain_reg); + val += offset_val; + snd_soc_component_write(component, gain_reg, val); + } + wsa_macro_config_ear_spkr_gain(component, wsa, + event, gain_reg); + break; + } + + return 0; +} + +static int wsa_macro_spk_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + if (!snd_soc_dapm_widget_name_cmp(w, "WSA_RX INT0 CHAIN")) { + boost_path_ctl = CDC_WSA_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = CDC_WSA_RX0_RX_PATH_CFG1; + reg = CDC_WSA_RX0_RX_PATH_CTL; + reg_mix = CDC_WSA_RX0_RX_PATH_MIX_CTL; + } else if (!snd_soc_dapm_widget_name_cmp(w, "WSA_RX INT1 CHAIN")) { + boost_path_ctl = CDC_WSA_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = CDC_WSA_RX1_RX_PATH_CFG1; + reg = CDC_WSA_RX1_RX_PATH_CTL; + reg_mix = CDC_WSA_RX1_RX_PATH_MIX_CTL; + } else { + dev_warn(component->dev, "Incorrect widget name in the driver\n"); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, boost_path_cfg1, + CDC_WSA_RX_PATH_SMART_BST_EN_MASK, + CDC_WSA_RX_PATH_SMART_BST_ENABLE); + snd_soc_component_update_bits(component, boost_path_ctl, + CDC_WSA_BOOST_PATH_CLK_EN_MASK, + CDC_WSA_BOOST_PATH_CLK_ENABLE); + if ((snd_soc_component_read(component, reg_mix)) & 0x10) + snd_soc_component_update_bits(component, reg_mix, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, reg, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, boost_path_ctl, + CDC_WSA_BOOST_PATH_CLK_EN_MASK, + CDC_WSA_BOOST_PATH_CLK_DISABLE); + snd_soc_component_update_bits(component, boost_path_cfg1, + CDC_WSA_RX_PATH_SMART_BST_EN_MASK, + CDC_WSA_RX_PATH_SMART_BST_DISABLE); + break; + } + + return 0; +} + +static int wsa_macro_enable_echo(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u16 val, ec_tx, ec_hq_reg; + + val = snd_soc_component_read(component, CDC_WSA_RX_INP_MUX_RX_MIX_CFG0); + + switch (w->shift) { + case WSA_MACRO_EC0_MUX: + val = val & CDC_WSA_RX_MIX_TX0_SEL_MASK; + ec_tx = val - 1; + break; + case WSA_MACRO_EC1_MUX: + val = val & CDC_WSA_RX_MIX_TX1_SEL_MASK; + ec_tx = (val >> CDC_WSA_RX_MIX_TX1_SEL_SHFT) - 1; + break; + default: + dev_err(component->dev, "%s: Invalid shift %u\n", + __func__, w->shift); + return -EINVAL; + } + + if (wsa->ec_hq[ec_tx]) { + ec_hq_reg = CDC_WSA_EC_HQ0_EC_REF_HQ_PATH_CTL + 0x40 * ec_tx; + snd_soc_component_update_bits(component, ec_hq_reg, + CDC_WSA_EC_HQ_EC_CLK_EN_MASK, + CDC_WSA_EC_HQ_EC_CLK_ENABLE); + ec_hq_reg = CDC_WSA_EC_HQ0_EC_REF_HQ_CFG0 + 0x40 * ec_tx; + /* default set to 48k */ + snd_soc_component_update_bits(component, ec_hq_reg, + CDC_WSA_EC_HQ_EC_REF_PCM_RATE_MASK, + CDC_WSA_EC_HQ_EC_REF_PCM_RATE_48K); + } + + return 0; +} + +static int wsa_macro_get_ec_hq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int ec_tx = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wsa->ec_hq[ec_tx]; + + return 0; +} + +static int wsa_macro_set_ec_hq(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int ec_tx = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + wsa->ec_hq[ec_tx] = value; + + return 0; +} + +static int wsa_macro_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wsa->comp_enabled[comp]; + return 0; +} + +static int wsa_macro_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int comp = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + wsa->comp_enabled[comp] = value; + + return 0; +} + +static int wsa_macro_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wsa->ear_spkr_gain; + + return 0; +} + +static int wsa_macro_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + wsa->ear_spkr_gain = ucontrol->value.integer.value[0]; + + return 0; +} + +static int wsa_macro_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = + wsa->rx_port_value[widget->shift]; + return 0; +} + +static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = + snd_soc_dapm_to_component(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 rx_port_value = ucontrol->value.integer.value[0]; + u32 bit_input; + u32 aif_rst; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + + aif_rst = wsa->rx_port_value[widget->shift]; + if (!rx_port_value) { + if (aif_rst == 0) + return 0; + if (aif_rst >= WSA_MACRO_RX_MAX) { + dev_err(component->dev, "%s: Invalid AIF reset\n", __func__); + return 0; + } + } + wsa->rx_port_value[widget->shift] = rx_port_value; + + bit_input = widget->shift; + + switch (rx_port_value) { + case 0: + if (wsa->active_ch_cnt[aif_rst]) { + clear_bit(bit_input, + &wsa->active_ch_mask[aif_rst]); + wsa->active_ch_cnt[aif_rst]--; + } + break; + case 1: + case 2: + set_bit(bit_input, + &wsa->active_ch_mask[rx_port_value]); + wsa->active_ch_cnt[rx_port_value]++; + break; + default: + dev_err(component->dev, + "%s: Invalid AIF_ID for WSA RX MUX %d\n", + __func__, rx_port_value); + return -EINVAL; + } + + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + return 0; +} + +static int wsa_macro_soft_clip_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + int path = ((struct soc_mixer_control *)kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = wsa->is_softclip_on[path]; + + return 0; +} + +static int wsa_macro_soft_clip_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + int path = ((struct soc_mixer_control *) kcontrol->private_value)->shift; + + wsa->is_softclip_on[path] = ucontrol->value.integer.value[0]; + + return 0; +} + +static const struct snd_kcontrol_new wsa_macro_snd_controls[] = { + SOC_ENUM_EXT("EAR SPKR PA Gain", wsa_macro_ear_spkr_pa_gain_enum, + wsa_macro_ear_spkr_pa_gain_get, + wsa_macro_ear_spkr_pa_gain_put), + SOC_SINGLE_EXT("WSA_Softclip0 Enable", SND_SOC_NOPM, + WSA_MACRO_SOFTCLIP0, 1, 0, + wsa_macro_soft_clip_enable_get, + wsa_macro_soft_clip_enable_put), + SOC_SINGLE_EXT("WSA_Softclip1 Enable", SND_SOC_NOPM, + WSA_MACRO_SOFTCLIP1, 1, 0, + wsa_macro_soft_clip_enable_get, + wsa_macro_soft_clip_enable_put), + + SOC_SINGLE_S8_TLV("WSA_RX0 Digital Volume", CDC_WSA_RX0_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("WSA_RX1 Digital Volume", CDC_WSA_RX1_RX_VOL_CTL, + -84, 40, digital_gain), + + SOC_SINGLE("WSA_RX0 Digital Mute", CDC_WSA_RX0_RX_PATH_CTL, 4, 1, 0), + SOC_SINGLE("WSA_RX1 Digital Mute", CDC_WSA_RX1_RX_PATH_CTL, 4, 1, 0), + SOC_SINGLE("WSA_RX0_MIX Digital Mute", CDC_WSA_RX0_RX_PATH_MIX_CTL, 4, + 1, 0), + SOC_SINGLE("WSA_RX1_MIX Digital Mute", CDC_WSA_RX1_RX_PATH_MIX_CTL, 4, + 1, 0), + SOC_SINGLE_EXT("WSA_COMP1 Switch", SND_SOC_NOPM, WSA_MACRO_COMP1, 1, 0, + wsa_macro_get_compander, wsa_macro_set_compander), + SOC_SINGLE_EXT("WSA_COMP2 Switch", SND_SOC_NOPM, WSA_MACRO_COMP2, 1, 0, + wsa_macro_get_compander, wsa_macro_set_compander), + SOC_SINGLE_EXT("WSA_RX0 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX0, 1, 0, + wsa_macro_get_ec_hq, wsa_macro_set_ec_hq), + SOC_SINGLE_EXT("WSA_RX1 EC_HQ Switch", SND_SOC_NOPM, WSA_MACRO_RX1, 1, 0, + wsa_macro_get_ec_hq, wsa_macro_set_ec_hq), +}; + +static const struct soc_enum rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_mux_text), rx_mux_text); + +static const struct snd_kcontrol_new rx_mux[WSA_MACRO_RX_MAX] = { + SOC_DAPM_ENUM_EXT("WSA RX0 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX1 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX_MIX0 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), + SOC_DAPM_ENUM_EXT("WSA RX_MIX1 Mux", rx_mux_enum, + wsa_macro_rx_mux_get, wsa_macro_rx_mux_put), +}; + +static int wsa_macro_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u32 spk_tx_id = mixer->shift; + u32 dai_id = widget->shift; + + if (test_bit(spk_tx_id, &wsa->active_ch_mask[dai_id])) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static int wsa_macro_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + struct wsa_macro *wsa = snd_soc_component_get_drvdata(component); + u32 enable = ucontrol->value.integer.value[0]; + u32 spk_tx_id = mixer->shift; + + if (enable) { + if (spk_tx_id == WSA_MACRO_TX0 && + !test_bit(WSA_MACRO_TX0, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + set_bit(WSA_MACRO_TX0, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa->active_ch_cnt[WSA_MACRO_AIF_VI]++; + } + if (spk_tx_id == WSA_MACRO_TX1 && + !test_bit(WSA_MACRO_TX1, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + set_bit(WSA_MACRO_TX1, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa->active_ch_cnt[WSA_MACRO_AIF_VI]++; + } + } else { + if (spk_tx_id == WSA_MACRO_TX0 && + test_bit(WSA_MACRO_TX0, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + clear_bit(WSA_MACRO_TX0, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa->active_ch_cnt[WSA_MACRO_AIF_VI]--; + } + if (spk_tx_id == WSA_MACRO_TX1 && + test_bit(WSA_MACRO_TX1, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI])) { + clear_bit(WSA_MACRO_TX1, + &wsa->active_ch_mask[WSA_MACRO_AIF_VI]); + wsa->active_ch_cnt[WSA_MACRO_AIF_VI]--; + } + } + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +static const struct snd_kcontrol_new aif_vi_mixer[] = { + SOC_SINGLE_EXT("WSA_SPKR_VI_1", SND_SOC_NOPM, WSA_MACRO_TX0, 1, 0, + wsa_macro_vi_feed_mixer_get, + wsa_macro_vi_feed_mixer_put), + SOC_SINGLE_EXT("WSA_SPKR_VI_2", SND_SOC_NOPM, WSA_MACRO_TX1, 1, 0, + wsa_macro_vi_feed_mixer_get, + wsa_macro_vi_feed_mixer_put), +}; + +static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("WSA AIF1 PB", "WSA_AIF1 Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("WSA AIF_MIX1 PB", "WSA_AIF_MIX1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT_E("WSA AIF_VI", "WSA_AIF_VI Capture", 0, + SND_SOC_NOPM, WSA_MACRO_AIF_VI, 0, + wsa_macro_enable_vi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT("WSA AIF_ECHO", "WSA_AIF_ECHO Capture", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("WSA_AIF_VI Mixer", SND_SOC_NOPM, WSA_MACRO_AIF_VI, + 0, aif_vi_mixer, ARRAY_SIZE(aif_vi_mixer)), + SND_SOC_DAPM_MUX_E("WSA RX_MIX EC0_MUX", SND_SOC_NOPM, + WSA_MACRO_EC0_MUX, 0, + &rx_mix_ec0_mux, wsa_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("WSA RX_MIX EC1_MUX", SND_SOC_NOPM, + WSA_MACRO_EC1_MUX, 0, + &rx_mix_ec1_mux, wsa_macro_enable_echo, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("WSA RX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX0, 0, + &rx_mux[WSA_MACRO_RX0]), + SND_SOC_DAPM_MUX("WSA RX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX1, 0, + &rx_mux[WSA_MACRO_RX1]), + SND_SOC_DAPM_MUX("WSA RX_MIX0 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, 0, + &rx_mux[WSA_MACRO_RX_MIX0]), + SND_SOC_DAPM_MUX("WSA RX_MIX1 MUX", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, 0, + &rx_mux[WSA_MACRO_RX_MIX1]), + + SND_SOC_DAPM_MIXER("WSA RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX_MIX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA RX_MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux), + SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux), + SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux), + SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0, + 0, &rx0_mix_mux, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("WSA_RX1 INP0", SND_SOC_NOPM, 0, 0, &rx1_prim_inp0_mux), + SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux), + SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux), + SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1, + 0, &rx1_mix_mux, wsa_macro_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 MIX", SND_SOC_NOPM, 0, 0, NULL, 0, + wsa_macro_enable_main_path, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 MIX", SND_SOC_NOPM, 1, 0, NULL, 0, + wsa_macro_enable_main_path, SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_MIXER("WSA_RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("WSA_RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("WSA_RX0 INT0 SIDETONE MIX", CDC_WSA_RX0_RX_PATH_CFG1, + 4, 0, &rx0_sidetone_mix_mux), + + SND_SOC_DAPM_INPUT("WSA SRC0_INP"), + SND_SOC_DAPM_INPUT("WSA_TX DEC0_INP"), + SND_SOC_DAPM_INPUT("WSA_TX DEC1_INP"), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 INTERP", SND_SOC_NOPM, + WSA_MACRO_COMP1, 0, NULL, 0, + wsa_macro_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 INTERP", SND_SOC_NOPM, + WSA_MACRO_COMP2, 0, NULL, 0, + wsa_macro_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT0 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, wsa_macro_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("WSA_RX INT1 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, wsa_macro_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("VIINPUT_WSA"), + SND_SOC_DAPM_OUTPUT("WSA_SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("WSA_SPK2 OUT"), + + SND_SOC_DAPM_SUPPLY("WSA_RX0_CLK", CDC_WSA_RX0_RX_PATH_CTL, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("WSA_RX1_CLK", CDC_WSA_RX1_RX_PATH_CTL, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("WSA_RX_MIX0_CLK", CDC_WSA_RX0_RX_PATH_MIX_CTL, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("WSA_RX_MIX1_CLK", CDC_WSA_RX1_RX_PATH_MIX_CTL, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("WSA_MCLK", 0, SND_SOC_NOPM, 0, 0, + wsa_macro_mclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route wsa_audio_map[] = { + /* VI Feedback */ + {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_1", "VIINPUT_WSA"}, + {"WSA_AIF_VI Mixer", "WSA_SPKR_VI_2", "VIINPUT_WSA"}, + {"WSA AIF_VI", NULL, "WSA_AIF_VI Mixer"}, + {"WSA AIF_VI", NULL, "WSA_MCLK"}, + + {"WSA RX_MIX EC0_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"}, + {"WSA RX_MIX EC1_MUX", "RX_MIX_TX0", "WSA_RX INT0 SEC MIX"}, + {"WSA RX_MIX EC0_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"}, + {"WSA RX_MIX EC1_MUX", "RX_MIX_TX1", "WSA_RX INT1 SEC MIX"}, + {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC0_MUX"}, + {"WSA AIF_ECHO", NULL, "WSA RX_MIX EC1_MUX"}, + {"WSA AIF_ECHO", NULL, "WSA_MCLK"}, + + {"WSA AIF1 PB", NULL, "WSA_MCLK"}, + {"WSA AIF_MIX1 PB", NULL, "WSA_MCLK"}, + + {"WSA RX0 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX1 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX_MIX0 MUX", "AIF1_PB", "WSA AIF1 PB"}, + {"WSA RX_MIX1 MUX", "AIF1_PB", "WSA AIF1 PB"}, + + {"WSA RX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX_MIX0 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + {"WSA RX_MIX1 MUX", "AIF_MIX1_PB", "WSA AIF_MIX1 PB"}, + + {"WSA RX0", NULL, "WSA RX0 MUX"}, + {"WSA RX1", NULL, "WSA RX1 MUX"}, + {"WSA RX_MIX0", NULL, "WSA RX_MIX0 MUX"}, + {"WSA RX_MIX1", NULL, "WSA RX_MIX1 MUX"}, + + {"WSA RX0", NULL, "WSA_RX0_CLK"}, + {"WSA RX1", NULL, "WSA_RX1_CLK"}, + {"WSA RX_MIX0", NULL, "WSA_RX_MIX0_CLK"}, + {"WSA RX_MIX1", NULL, "WSA_RX_MIX1_CLK"}, + + {"WSA_RX0 INP0", "RX0", "WSA RX0"}, + {"WSA_RX0 INP0", "RX1", "WSA RX1"}, + {"WSA_RX0 INP0", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP0", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP0", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP0", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP0"}, + + {"WSA_RX0 INP1", "RX0", "WSA RX0"}, + {"WSA_RX0 INP1", "RX1", "WSA RX1"}, + {"WSA_RX0 INP1", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP1", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP1", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP1", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP1"}, + + {"WSA_RX0 INP2", "RX0", "WSA RX0"}, + {"WSA_RX0 INP2", "RX1", "WSA RX1"}, + {"WSA_RX0 INP2", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 INP2", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX0 INP2", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX0 INP2", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT0 MIX", NULL, "WSA_RX0 INP2"}, + + {"WSA_RX0 MIX INP", "RX0", "WSA RX0"}, + {"WSA_RX0 MIX INP", "RX1", "WSA RX1"}, + {"WSA_RX0 MIX INP", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX0 MIX INP", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX0 MIX INP"}, + + {"WSA_RX INT0 SEC MIX", NULL, "WSA_RX INT0 MIX"}, + {"WSA_RX INT0 INTERP", NULL, "WSA_RX INT0 SEC MIX"}, + {"WSA_RX0 INT0 SIDETONE MIX", "SRC0", "WSA SRC0_INP"}, + {"WSA_RX INT0 INTERP", NULL, "WSA_RX0 INT0 SIDETONE MIX"}, + {"WSA_RX INT0 CHAIN", NULL, "WSA_RX INT0 INTERP"}, + + {"WSA_SPK1 OUT", NULL, "WSA_RX INT0 CHAIN"}, + {"WSA_SPK1 OUT", NULL, "WSA_MCLK"}, + + {"WSA_RX1 INP0", "RX0", "WSA RX0"}, + {"WSA_RX1 INP0", "RX1", "WSA RX1"}, + {"WSA_RX1 INP0", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP0", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP0", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP0", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP0"}, + + {"WSA_RX1 INP1", "RX0", "WSA RX0"}, + {"WSA_RX1 INP1", "RX1", "WSA RX1"}, + {"WSA_RX1 INP1", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP1", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP1", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP1", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP1"}, + + {"WSA_RX1 INP2", "RX0", "WSA RX0"}, + {"WSA_RX1 INP2", "RX1", "WSA RX1"}, + {"WSA_RX1 INP2", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 INP2", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX1 INP2", "DEC0", "WSA_TX DEC0_INP"}, + {"WSA_RX1 INP2", "DEC1", "WSA_TX DEC1_INP"}, + {"WSA_RX INT1 MIX", NULL, "WSA_RX1 INP2"}, + + {"WSA_RX1 MIX INP", "RX0", "WSA RX0"}, + {"WSA_RX1 MIX INP", "RX1", "WSA RX1"}, + {"WSA_RX1 MIX INP", "RX_MIX0", "WSA RX_MIX0"}, + {"WSA_RX1 MIX INP", "RX_MIX1", "WSA RX_MIX1"}, + {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX1 MIX INP"}, + + {"WSA_RX INT1 SEC MIX", NULL, "WSA_RX INT1 MIX"}, + {"WSA_RX INT1 INTERP", NULL, "WSA_RX INT1 SEC MIX"}, + + {"WSA_RX INT1 CHAIN", NULL, "WSA_RX INT1 INTERP"}, + {"WSA_SPK2 OUT", NULL, "WSA_RX INT1 CHAIN"}, + {"WSA_SPK2 OUT", NULL, "WSA_MCLK"}, +}; + +static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable) +{ + struct regmap *regmap = wsa->regmap; + + if (enable) { + int ret; + + ret = clk_prepare_enable(wsa->mclk); + if (ret) { + dev_err(wsa->dev, "failed to enable mclk\n"); + return ret; + } + wsa_macro_mclk_enable(wsa, true); + + regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_CLK_EN_MASK, + CDC_WSA_SWR_CLK_ENABLE); + + } else { + regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_CLK_EN_MASK, 0); + wsa_macro_mclk_enable(wsa, false); + clk_disable_unprepare(wsa->mclk); + } + + return 0; +} + +static int wsa_macro_component_probe(struct snd_soc_component *comp) +{ + struct wsa_macro *wsa = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa->regmap); + + wsa->spkr_gain_offset = WSA_MACRO_GAIN_OFFSET_M1P5_DB; + + /* set SPKR rate to FS_2P4_3P072 */ + snd_soc_component_update_bits(comp, CDC_WSA_RX0_RX_PATH_CFG1, + CDC_WSA_RX_PATH_SPKR_RATE_MASK, + CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072); + + snd_soc_component_update_bits(comp, CDC_WSA_RX1_RX_PATH_CFG1, + CDC_WSA_RX_PATH_SPKR_RATE_MASK, + CDC_WSA_RX_PATH_SPKR_RATE_FS_2P4_3P072); + + wsa_macro_set_spkr_mode(comp, WSA_MACRO_SPKR_MODE_1); + + return 0; +} + +static int swclk_gate_enable(struct clk_hw *hw) +{ + return wsa_swrm_clock(to_wsa_macro(hw), true); +} + +static void swclk_gate_disable(struct clk_hw *hw) +{ + wsa_swrm_clock(to_wsa_macro(hw), false); +} + +static int swclk_gate_is_enabled(struct clk_hw *hw) +{ + struct wsa_macro *wsa = to_wsa_macro(hw); + int ret, val; + + regmap_read(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, &val); + ret = val & BIT(0); + + return ret; +} + +static unsigned long swclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate / 2; +} + +static const struct clk_ops swclk_gate_ops = { + .prepare = swclk_gate_enable, + .unprepare = swclk_gate_disable, + .is_enabled = swclk_gate_is_enabled, + .recalc_rate = swclk_recalc_rate, +}; + +static int wsa_macro_register_mclk_output(struct wsa_macro *wsa) +{ + struct device *dev = wsa->dev; + const char *parent_clk_name; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (wsa->npl) + parent_clk_name = __clk_get_name(wsa->npl); + else + parent_clk_name = __clk_get_name(wsa->mclk); + + init.name = "mclk"; + of_property_read_string(dev_of_node(dev), "clock-output-names", + &init.name); + init.ops = &swclk_gate_ops; + init.flags = 0; + init.parent_names = &parent_clk_name; + init.num_parents = 1; + wsa->hw.init = &init; + hw = &wsa->hw; + ret = clk_hw_register(wsa->dev, hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); +} + +static const struct snd_soc_component_driver wsa_macro_component_drv = { + .name = "WSA MACRO", + .probe = wsa_macro_component_probe, + .controls = wsa_macro_snd_controls, + .num_controls = ARRAY_SIZE(wsa_macro_snd_controls), + .dapm_widgets = wsa_macro_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets), + .dapm_routes = wsa_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa_audio_map), +}; + +static int wsa_macro_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wsa_macro *wsa; + kernel_ulong_t flags; + void __iomem *base; + int ret; + + flags = (kernel_ulong_t)device_get_match_data(dev); + + wsa = devm_kzalloc(dev, sizeof(*wsa), GFP_KERNEL); + if (!wsa) + return -ENOMEM; + + wsa->macro = devm_clk_get_optional(dev, "macro"); + if (IS_ERR(wsa->macro)) + return dev_err_probe(dev, PTR_ERR(wsa->macro), "unable to get macro clock\n"); + + wsa->dcodec = devm_clk_get_optional(dev, "dcodec"); + if (IS_ERR(wsa->dcodec)) + return dev_err_probe(dev, PTR_ERR(wsa->dcodec), "unable to get dcodec clock\n"); + + wsa->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(wsa->mclk)) + return dev_err_probe(dev, PTR_ERR(wsa->mclk), "unable to get mclk clock\n"); + + if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { + wsa->npl = devm_clk_get(dev, "npl"); + if (IS_ERR(wsa->npl)) + return dev_err_probe(dev, PTR_ERR(wsa->npl), "unable to get npl clock\n"); + } + + wsa->fsgen = devm_clk_get(dev, "fsgen"); + if (IS_ERR(wsa->fsgen)) + return dev_err_probe(dev, PTR_ERR(wsa->fsgen), "unable to get fsgen clock\n"); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + wsa->regmap = devm_regmap_init_mmio(dev, base, &wsa_regmap_config); + if (IS_ERR(wsa->regmap)) + return PTR_ERR(wsa->regmap); + + dev_set_drvdata(dev, wsa); + + wsa->dev = dev; + + /* set MCLK and NPL rates */ + clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ); + clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ); + + ret = clk_prepare_enable(wsa->macro); + if (ret) + goto err; + + ret = clk_prepare_enable(wsa->dcodec); + if (ret) + goto err_dcodec; + + ret = clk_prepare_enable(wsa->mclk); + if (ret) + goto err_mclk; + + ret = clk_prepare_enable(wsa->npl); + if (ret) + goto err_npl; + + ret = clk_prepare_enable(wsa->fsgen); + if (ret) + goto err_fsgen; + + /* reset swr ip */ + regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_ENABLE); + + regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_CLK_EN_MASK, CDC_WSA_SWR_CLK_ENABLE); + + /* Bring out of reset */ + regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL, + CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_DISABLE); + + ret = devm_snd_soc_register_component(dev, &wsa_macro_component_drv, + wsa_macro_dai, + ARRAY_SIZE(wsa_macro_dai)); + if (ret) + goto err_clkout; + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = wsa_macro_register_mclk_output(wsa); + if (ret) + goto err_clkout; + + return 0; + +err_clkout: + clk_disable_unprepare(wsa->fsgen); +err_fsgen: + clk_disable_unprepare(wsa->npl); +err_npl: + clk_disable_unprepare(wsa->mclk); +err_mclk: + clk_disable_unprepare(wsa->dcodec); +err_dcodec: + clk_disable_unprepare(wsa->macro); +err: + return ret; + +} + +static void wsa_macro_remove(struct platform_device *pdev) +{ + struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(wsa->macro); + clk_disable_unprepare(wsa->dcodec); + clk_disable_unprepare(wsa->mclk); + clk_disable_unprepare(wsa->npl); + clk_disable_unprepare(wsa->fsgen); +} + +static int __maybe_unused wsa_macro_runtime_suspend(struct device *dev) +{ + struct wsa_macro *wsa = dev_get_drvdata(dev); + + regcache_cache_only(wsa->regmap, true); + regcache_mark_dirty(wsa->regmap); + + clk_disable_unprepare(wsa->fsgen); + clk_disable_unprepare(wsa->npl); + clk_disable_unprepare(wsa->mclk); + + return 0; +} + +static int __maybe_unused wsa_macro_runtime_resume(struct device *dev) +{ + struct wsa_macro *wsa = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(wsa->mclk); + if (ret) { + dev_err(dev, "unable to prepare mclk\n"); + return ret; + } + + ret = clk_prepare_enable(wsa->npl); + if (ret) { + dev_err(dev, "unable to prepare mclkx2\n"); + goto err_npl; + } + + ret = clk_prepare_enable(wsa->fsgen); + if (ret) { + dev_err(dev, "unable to prepare fsgen\n"); + goto err_fsgen; + } + + regcache_cache_only(wsa->regmap, false); + regcache_sync(wsa->regmap); + + return 0; +err_fsgen: + clk_disable_unprepare(wsa->npl); +err_npl: + clk_disable_unprepare(wsa->mclk); + + return ret; +} + +static const struct dev_pm_ops wsa_macro_pm_ops = { + SET_RUNTIME_PM_OPS(wsa_macro_runtime_suspend, wsa_macro_runtime_resume, NULL) +}; + +static const struct of_device_id wsa_macro_dt_match[] = { + { + .compatible = "qcom,sc7280-lpass-wsa-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, { + .compatible = "qcom,sm8250-lpass-wsa-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, { + .compatible = "qcom,sm8450-lpass-wsa-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, { + .compatible = "qcom,sm8550-lpass-wsa-macro", + }, { + .compatible = "qcom,sc8280xp-lpass-wsa-macro", + .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK, + }, + {} +}; +MODULE_DEVICE_TABLE(of, wsa_macro_dt_match); + +static struct platform_driver wsa_macro_driver = { + .driver = { + .name = "wsa_macro", + .of_match_table = wsa_macro_dt_match, + .pm = &wsa_macro_pm_ops, + }, + .probe = wsa_macro_probe, + .remove_new = wsa_macro_remove, +}; + +module_platform_driver(wsa_macro_driver); +MODULE_DESCRIPTION("WSA macro driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lpass-wsa-macro.h b/sound/soc/codecs/lpass-wsa-macro.h new file mode 100644 index 0000000000..d3d62b3f65 --- /dev/null +++ b/sound/soc/codecs/lpass-wsa-macro.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __LPASS_WSA_MACRO_H__ +#define __LPASS_WSA_MACRO_H__ + +/* + * Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + WSA_MACRO_SPKR_MODE_DEFAULT, + WSA_MACRO_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode); + +#endif /* __LPASS_WSA_MACRO_H__ */ diff --git a/sound/soc/codecs/madera.c b/sound/soc/codecs/madera.c new file mode 100644 index 0000000000..b9f19fbd29 --- /dev/null +++ b/sound/soc/codecs/madera.c @@ -0,0 +1,4811 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Cirrus Logic Madera class codecs common support +// +// Copyright (C) 2015-2019 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "madera.h" + +#define MADERA_AIF_BCLK_CTRL 0x00 +#define MADERA_AIF_TX_PIN_CTRL 0x01 +#define MADERA_AIF_RX_PIN_CTRL 0x02 +#define MADERA_AIF_RATE_CTRL 0x03 +#define MADERA_AIF_FORMAT 0x04 +#define MADERA_AIF_RX_BCLK_RATE 0x06 +#define MADERA_AIF_FRAME_CTRL_1 0x07 +#define MADERA_AIF_FRAME_CTRL_2 0x08 +#define MADERA_AIF_FRAME_CTRL_3 0x09 +#define MADERA_AIF_FRAME_CTRL_4 0x0A +#define MADERA_AIF_FRAME_CTRL_5 0x0B +#define MADERA_AIF_FRAME_CTRL_6 0x0C +#define MADERA_AIF_FRAME_CTRL_7 0x0D +#define MADERA_AIF_FRAME_CTRL_8 0x0E +#define MADERA_AIF_FRAME_CTRL_9 0x0F +#define MADERA_AIF_FRAME_CTRL_10 0x10 +#define MADERA_AIF_FRAME_CTRL_11 0x11 +#define MADERA_AIF_FRAME_CTRL_12 0x12 +#define MADERA_AIF_FRAME_CTRL_13 0x13 +#define MADERA_AIF_FRAME_CTRL_14 0x14 +#define MADERA_AIF_FRAME_CTRL_15 0x15 +#define MADERA_AIF_FRAME_CTRL_16 0x16 +#define MADERA_AIF_FRAME_CTRL_17 0x17 +#define MADERA_AIF_FRAME_CTRL_18 0x18 +#define MADERA_AIF_TX_ENABLES 0x19 +#define MADERA_AIF_RX_ENABLES 0x1A +#define MADERA_AIF_FORCE_WRITE 0x1B + +#define MADERA_DSP_CONFIG_1_OFFS 0x00 +#define MADERA_DSP_CONFIG_2_OFFS 0x02 + +#define MADERA_DSP_CLK_SEL_MASK 0x70000 +#define MADERA_DSP_CLK_SEL_SHIFT 16 + +#define MADERA_DSP_RATE_MASK 0x7800 +#define MADERA_DSP_RATE_SHIFT 11 + +#define MADERA_SYSCLK_6MHZ 0 +#define MADERA_SYSCLK_12MHZ 1 +#define MADERA_SYSCLK_24MHZ 2 +#define MADERA_SYSCLK_49MHZ 3 +#define MADERA_SYSCLK_98MHZ 4 + +#define MADERA_DSPCLK_9MHZ 0 +#define MADERA_DSPCLK_18MHZ 1 +#define MADERA_DSPCLK_36MHZ 2 +#define MADERA_DSPCLK_73MHZ 3 +#define MADERA_DSPCLK_147MHZ 4 + +#define MADERA_FLL_VCO_CORNER 141900000 +#define MADERA_FLL_MAX_FREF 13500000 +#define MADERA_FLL_MAX_N 1023 +#define MADERA_FLL_MIN_FOUT 90000000 +#define MADERA_FLL_MAX_FOUT 100000000 +#define MADERA_FLL_MAX_FRATIO 16 +#define MADERA_FLL_MAX_REFDIV 8 +#define MADERA_FLL_OUTDIV 3 +#define MADERA_FLL_VCO_MULT 3 +#define MADERA_FLLAO_MAX_FREF 12288000 +#define MADERA_FLLAO_MIN_N 4 +#define MADERA_FLLAO_MAX_N 1023 +#define MADERA_FLLAO_MAX_FBDIV 254 +#define MADERA_FLLHJ_INT_MAX_N 1023 +#define MADERA_FLLHJ_INT_MIN_N 1 +#define MADERA_FLLHJ_FRAC_MAX_N 255 +#define MADERA_FLLHJ_FRAC_MIN_N 4 +#define MADERA_FLLHJ_LOW_THRESH 192000 +#define MADERA_FLLHJ_MID_THRESH 1152000 +#define MADERA_FLLHJ_MAX_THRESH 13000000 +#define MADERA_FLLHJ_LOW_GAINS 0x23f0 +#define MADERA_FLLHJ_MID_GAINS 0x22f2 +#define MADERA_FLLHJ_HIGH_GAINS 0x21f0 + +#define MADERA_FLL_SYNCHRONISER_OFFS 0x10 +#define CS47L35_FLL_SYNCHRONISER_OFFS 0xE +#define MADERA_FLL_CONTROL_1_OFFS 0x1 +#define MADERA_FLL_CONTROL_2_OFFS 0x2 +#define MADERA_FLL_CONTROL_3_OFFS 0x3 +#define MADERA_FLL_CONTROL_4_OFFS 0x4 +#define MADERA_FLL_CONTROL_5_OFFS 0x5 +#define MADERA_FLL_CONTROL_6_OFFS 0x6 +#define MADERA_FLL_GAIN_OFFS 0x8 +#define MADERA_FLL_CONTROL_7_OFFS 0x9 +#define MADERA_FLL_EFS_2_OFFS 0xA +#define MADERA_FLL_SYNCHRONISER_1_OFFS 0x1 +#define MADERA_FLL_SYNCHRONISER_2_OFFS 0x2 +#define MADERA_FLL_SYNCHRONISER_3_OFFS 0x3 +#define MADERA_FLL_SYNCHRONISER_4_OFFS 0x4 +#define MADERA_FLL_SYNCHRONISER_5_OFFS 0x5 +#define MADERA_FLL_SYNCHRONISER_6_OFFS 0x6 +#define MADERA_FLL_SYNCHRONISER_7_OFFS 0x7 +#define MADERA_FLL_SPREAD_SPECTRUM_OFFS 0x9 +#define MADERA_FLL_GPIO_CLOCK_OFFS 0xA +#define MADERA_FLL_CONTROL_10_OFFS 0xA +#define MADERA_FLL_CONTROL_11_OFFS 0xB +#define MADERA_FLL1_DIGITAL_TEST_1_OFFS 0xD + +#define MADERA_FLLAO_CONTROL_1_OFFS 0x1 +#define MADERA_FLLAO_CONTROL_2_OFFS 0x2 +#define MADERA_FLLAO_CONTROL_3_OFFS 0x3 +#define MADERA_FLLAO_CONTROL_4_OFFS 0x4 +#define MADERA_FLLAO_CONTROL_5_OFFS 0x5 +#define MADERA_FLLAO_CONTROL_6_OFFS 0x6 +#define MADERA_FLLAO_CONTROL_7_OFFS 0x8 +#define MADERA_FLLAO_CONTROL_8_OFFS 0xA +#define MADERA_FLLAO_CONTROL_9_OFFS 0xB +#define MADERA_FLLAO_CONTROL_10_OFFS 0xC +#define MADERA_FLLAO_CONTROL_11_OFFS 0xD + +#define MADERA_FMT_DSP_MODE_A 0 +#define MADERA_FMT_DSP_MODE_B 1 +#define MADERA_FMT_I2S_MODE 2 +#define MADERA_FMT_LEFT_JUSTIFIED_MODE 3 + +#define madera_fll_err(_fll, fmt, ...) \ + dev_err(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) +#define madera_fll_warn(_fll, fmt, ...) \ + dev_warn(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) +#define madera_fll_dbg(_fll, fmt, ...) \ + dev_dbg(_fll->madera->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) + +#define madera_aif_err(_dai, fmt, ...) \ + dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) +#define madera_aif_warn(_dai, fmt, ...) \ + dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) +#define madera_aif_dbg(_dai, fmt, ...) \ + dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) + +static const int madera_dsp_bus_error_irqs[MADERA_MAX_ADSP] = { + MADERA_IRQ_DSP1_BUS_ERR, + MADERA_IRQ_DSP2_BUS_ERR, + MADERA_IRQ_DSP3_BUS_ERR, + MADERA_IRQ_DSP4_BUS_ERR, + MADERA_IRQ_DSP5_BUS_ERR, + MADERA_IRQ_DSP6_BUS_ERR, + MADERA_IRQ_DSP7_BUS_ERR, +}; + +int madera_clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + unsigned int val; + int clk_idx; + int ret; + + ret = regmap_read(madera->regmap, w->reg, &val); + if (ret) { + dev_err(madera->dev, "Failed to check clock source: %d\n", ret); + return ret; + } + + switch ((val & MADERA_SYSCLK_SRC_MASK) >> MADERA_SYSCLK_SRC_SHIFT) { + case MADERA_CLK_SRC_MCLK1: + clk_idx = MADERA_MCLK1; + break; + case MADERA_CLK_SRC_MCLK2: + clk_idx = MADERA_MCLK2; + break; + case MADERA_CLK_SRC_MCLK3: + clk_idx = MADERA_MCLK3; + break; + default: + return 0; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return clk_prepare_enable(madera->mclk[clk_idx].clk); + case SND_SOC_DAPM_POST_PMD: + clk_disable_unprepare(madera->mclk[clk_idx].clk); + return 0; + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(madera_clk_ev); + +static void madera_spin_sysclk(struct madera_priv *priv) +{ + struct madera *madera = priv->madera; + unsigned int val; + int ret, i; + + /* Skip this if the chip is down */ + if (pm_runtime_suspended(madera->dev)) + return; + + /* + * Just read a register a few times to ensure the internal + * oscillator sends out a few clocks. + */ + for (i = 0; i < 4; i++) { + ret = regmap_read(madera->regmap, MADERA_SOFTWARE_RESET, &val); + if (ret) + dev_err(madera->dev, + "Failed to read sysclk spin %d: %d\n", i, ret); + } + + udelay(300); +} + +int madera_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMD: + madera_spin_sysclk(priv); + break; + default: + break; + } + + return madera_clk_ev(w, kcontrol, event); +} +EXPORT_SYMBOL_GPL(madera_sysclk_ev); + +static int madera_check_speaker_overheat(struct madera *madera, + bool *warn, bool *shutdown) +{ + unsigned int val; + int ret; + + ret = regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_15, &val); + if (ret) { + dev_err(madera->dev, "Failed to read thermal status: %d\n", + ret); + return ret; + } + + *warn = val & MADERA_SPK_OVERHEAT_WARN_STS1; + *shutdown = val & MADERA_SPK_OVERHEAT_STS1; + + return 0; +} + +int madera_spk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + bool warn, shutdown; + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = madera_check_speaker_overheat(madera, &warn, &shutdown); + if (ret) + return ret; + + if (shutdown) { + dev_crit(madera->dev, + "Speaker not enabled due to temperature\n"); + return -EBUSY; + } + + regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, + 1 << w->shift, 1 << w->shift); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, + 1 << w->shift, 0); + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(madera_spk_ev); + +static irqreturn_t madera_thermal_warn(int irq, void *data) +{ + struct madera *madera = data; + bool warn, shutdown; + int ret; + + ret = madera_check_speaker_overheat(madera, &warn, &shutdown); + if (ret || shutdown) { /* for safety attempt to shutdown on error */ + dev_crit(madera->dev, "Thermal shutdown\n"); + ret = regmap_update_bits(madera->regmap, + MADERA_OUTPUT_ENABLES_1, + MADERA_OUT4L_ENA | + MADERA_OUT4R_ENA, 0); + if (ret != 0) + dev_crit(madera->dev, + "Failed to disable speaker outputs: %d\n", + ret); + } else if (warn) { + dev_alert(madera->dev, "Thermal warning\n"); + } else { + dev_info(madera->dev, "Spurious thermal warning\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +int madera_init_overheat(struct madera_priv *priv) +{ + struct madera *madera = priv->madera; + struct device *dev = madera->dev; + int ret; + + ret = madera_request_irq(madera, MADERA_IRQ_SPK_OVERHEAT_WARN, + "Thermal warning", madera_thermal_warn, + madera); + if (ret) + dev_err(dev, "Failed to get thermal warning IRQ: %d\n", ret); + + ret = madera_request_irq(madera, MADERA_IRQ_SPK_OVERHEAT, + "Thermal shutdown", madera_thermal_warn, + madera); + if (ret) + dev_err(dev, "Failed to get thermal shutdown IRQ: %d\n", ret); + + return 0; +} +EXPORT_SYMBOL_GPL(madera_init_overheat); + +int madera_free_overheat(struct madera_priv *priv) +{ + struct madera *madera = priv->madera; + + madera_free_irq(madera, MADERA_IRQ_SPK_OVERHEAT_WARN, madera); + madera_free_irq(madera, MADERA_IRQ_SPK_OVERHEAT, madera); + + return 0; +} +EXPORT_SYMBOL_GPL(madera_free_overheat); + +static int madera_get_variable_u32_array(struct device *dev, + const char *propname, + u32 *dest, int n_max, + int multiple) +{ + int n, ret; + + n = device_property_count_u32(dev, propname); + if (n < 0) { + if (n == -EINVAL) + return 0; /* missing, ignore */ + + dev_warn(dev, "%s malformed (%d)\n", propname, n); + + return n; + } else if ((n % multiple) != 0) { + dev_warn(dev, "%s not a multiple of %d entries\n", + propname, multiple); + + return -EINVAL; + } + + if (n > n_max) + n = n_max; + + ret = device_property_read_u32_array(dev, propname, dest, n); + if (ret < 0) + return ret; + + return n; +} + +static void madera_prop_get_inmode(struct madera_priv *priv) +{ + struct madera *madera = priv->madera; + struct madera_codec_pdata *pdata = &madera->pdata.codec; + u32 tmp[MADERA_MAX_INPUT * MADERA_MAX_MUXED_CHANNELS]; + int n, i, in_idx, ch_idx; + + BUILD_BUG_ON(ARRAY_SIZE(pdata->inmode) != MADERA_MAX_INPUT); + BUILD_BUG_ON(ARRAY_SIZE(pdata->inmode[0]) != MADERA_MAX_MUXED_CHANNELS); + + n = madera_get_variable_u32_array(madera->dev, "cirrus,inmode", + tmp, ARRAY_SIZE(tmp), + MADERA_MAX_MUXED_CHANNELS); + if (n < 0) + return; + + in_idx = 0; + ch_idx = 0; + for (i = 0; i < n; ++i) { + pdata->inmode[in_idx][ch_idx] = tmp[i]; + + if (++ch_idx == MADERA_MAX_MUXED_CHANNELS) { + ch_idx = 0; + ++in_idx; + } + } +} + +static void madera_prop_get_pdata(struct madera_priv *priv) +{ + struct madera *madera = priv->madera; + struct madera_codec_pdata *pdata = &madera->pdata.codec; + u32 out_mono[ARRAY_SIZE(pdata->out_mono)]; + int i, n; + + madera_prop_get_inmode(priv); + + n = madera_get_variable_u32_array(madera->dev, "cirrus,out-mono", + out_mono, ARRAY_SIZE(out_mono), 1); + if (n > 0) + for (i = 0; i < n; ++i) + pdata->out_mono[i] = !!out_mono[i]; + + madera_get_variable_u32_array(madera->dev, + "cirrus,max-channels-clocked", + pdata->max_channels_clocked, + ARRAY_SIZE(pdata->max_channels_clocked), + 1); + + madera_get_variable_u32_array(madera->dev, "cirrus,pdm-fmt", + pdata->pdm_fmt, + ARRAY_SIZE(pdata->pdm_fmt), 1); + + madera_get_variable_u32_array(madera->dev, "cirrus,pdm-mute", + pdata->pdm_mute, + ARRAY_SIZE(pdata->pdm_mute), 1); + + madera_get_variable_u32_array(madera->dev, "cirrus,dmic-ref", + pdata->dmic_ref, + ARRAY_SIZE(pdata->dmic_ref), 1); +} + +int madera_core_init(struct madera_priv *priv) +{ + int i; + + /* trap undersized array initializers */ + BUILD_BUG_ON(!madera_mixer_texts[MADERA_NUM_MIXER_INPUTS - 1]); + BUILD_BUG_ON(!madera_mixer_values[MADERA_NUM_MIXER_INPUTS - 1]); + + if (!dev_get_platdata(priv->madera->dev)) + madera_prop_get_pdata(priv); + + mutex_init(&priv->rate_lock); + + for (i = 0; i < MADERA_MAX_HP_OUTPUT; i++) + priv->madera->out_clamp[i] = true; + + return 0; +} +EXPORT_SYMBOL_GPL(madera_core_init); + +int madera_core_free(struct madera_priv *priv) +{ + mutex_destroy(&priv->rate_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(madera_core_free); + +static void madera_debug_dump_domain_groups(const struct madera_priv *priv) +{ + struct madera *madera = priv->madera; + int i; + + for (i = 0; i < ARRAY_SIZE(priv->domain_group_ref); ++i) + dev_dbg(madera->dev, "domain_grp_ref[%d]=%d\n", i, + priv->domain_group_ref[i]); +} + +int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + int dom_grp = w->shift; + + if (dom_grp >= ARRAY_SIZE(priv->domain_group_ref)) { + WARN(true, "%s dom_grp exceeds array size\n", __func__); + return -EINVAL; + } + + /* + * We can't rely on the DAPM mutex for locking because we need a lock + * that can safely be called in hw_params + */ + mutex_lock(&priv->rate_lock); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(priv->madera->dev, "Inc ref on domain group %d\n", + dom_grp); + ++priv->domain_group_ref[dom_grp]; + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(priv->madera->dev, "Dec ref on domain group %d\n", + dom_grp); + --priv->domain_group_ref[dom_grp]; + break; + default: + break; + } + + madera_debug_dump_domain_groups(priv); + + mutex_unlock(&priv->rate_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(madera_domain_clk_ev); + +int madera_out1_demux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int ep_sel, mux, change; + bool out_mono; + int ret; + + if (ucontrol->value.enumerated.item[0] > e->items - 1) + return -EINVAL; + + mux = ucontrol->value.enumerated.item[0]; + + snd_soc_dapm_mutex_lock(dapm); + + ep_sel = mux << MADERA_EP_SEL_SHIFT; + + change = snd_soc_component_test_bits(component, MADERA_OUTPUT_ENABLES_1, + MADERA_EP_SEL_MASK, + ep_sel); + if (!change) + goto end; + + /* EP_SEL should not be modified while HP or EP driver is enabled */ + ret = regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, + MADERA_OUT1L_ENA | MADERA_OUT1R_ENA, 0); + if (ret) + dev_warn(madera->dev, "Failed to disable outputs: %d\n", ret); + + usleep_range(2000, 3000); /* wait for wseq to complete */ + + /* change demux setting */ + ret = 0; + if (madera->out_clamp[0]) + ret = regmap_update_bits(madera->regmap, + MADERA_OUTPUT_ENABLES_1, + MADERA_EP_SEL_MASK, ep_sel); + if (ret) { + dev_err(madera->dev, "Failed to set OUT1 demux: %d\n", ret); + } else { + /* apply correct setting for mono mode */ + if (!ep_sel && !madera->pdata.codec.out_mono[0]) + out_mono = false; /* stereo HP */ + else + out_mono = true; /* EP or mono HP */ + + ret = madera_set_output_mode(component, 1, out_mono); + if (ret) + dev_warn(madera->dev, + "Failed to set output mode: %d\n", ret); + } + + /* + * if HPDET has disabled the clamp while switching to HPOUT + * OUT1 should remain disabled + */ + if (ep_sel || + (madera->out_clamp[0] && !madera->out_shorted[0])) { + ret = regmap_update_bits(madera->regmap, + MADERA_OUTPUT_ENABLES_1, + MADERA_OUT1L_ENA | MADERA_OUT1R_ENA, + madera->hp_ena); + if (ret) + dev_warn(madera->dev, + "Failed to restore earpiece outputs: %d\n", + ret); + else if (madera->hp_ena) + msleep(34); /* wait for enable wseq */ + else + usleep_range(2000, 3000); /* wait for disable wseq */ + } + +end: + snd_soc_dapm_mutex_unlock(dapm); + + ret = snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL); + if (ret < 0) { + dev_err(madera->dev, "Failed to update demux power state: %d\n", ret); + return ret; + } + + return change; +} +EXPORT_SYMBOL_GPL(madera_out1_demux_put); + +int madera_out1_demux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + unsigned int val; + + val = snd_soc_component_read(component, MADERA_OUTPUT_ENABLES_1); + val &= MADERA_EP_SEL_MASK; + val >>= MADERA_EP_SEL_SHIFT; + ucontrol->value.enumerated.item[0] = val; + + return 0; +} +EXPORT_SYMBOL_GPL(madera_out1_demux_get); + +static int madera_inmux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + struct regmap *regmap = madera->regmap; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mux, val, mask; + unsigned int inmode; + bool changed; + int ret; + + mux = ucontrol->value.enumerated.item[0]; + if (mux > 1) + return -EINVAL; + + val = mux << e->shift_l; + mask = (e->mask << e->shift_l) | MADERA_IN1L_SRC_SE_MASK; + + switch (e->reg) { + case MADERA_ADC_DIGITAL_VOLUME_1L: + inmode = madera->pdata.codec.inmode[0][2 * mux]; + break; + case MADERA_ADC_DIGITAL_VOLUME_1R: + inmode = madera->pdata.codec.inmode[0][1 + (2 * mux)]; + break; + case MADERA_ADC_DIGITAL_VOLUME_2L: + inmode = madera->pdata.codec.inmode[1][2 * mux]; + break; + case MADERA_ADC_DIGITAL_VOLUME_2R: + inmode = madera->pdata.codec.inmode[1][1 + (2 * mux)]; + break; + default: + return -EINVAL; + } + + if (inmode & MADERA_INMODE_SE) + val |= 1 << MADERA_IN1L_SRC_SE_SHIFT; + + dev_dbg(madera->dev, "mux=%u reg=0x%x inmode=0x%x mask=0x%x val=0x%x\n", + mux, e->reg, inmode, mask, val); + + ret = regmap_update_bits_check(regmap, e->reg, mask, val, &changed); + if (ret < 0) + return ret; + + if (changed) + return snd_soc_dapm_mux_update_power(dapm, kcontrol, + mux, e, NULL); + else + return 0; +} + +static const char * const madera_inmux_texts[] = { + "A", + "B", +}; + +static SOC_ENUM_SINGLE_DECL(madera_in1muxl_enum, + MADERA_ADC_DIGITAL_VOLUME_1L, + MADERA_IN1L_SRC_SHIFT, + madera_inmux_texts); + +static SOC_ENUM_SINGLE_DECL(madera_in1muxr_enum, + MADERA_ADC_DIGITAL_VOLUME_1R, + MADERA_IN1R_SRC_SHIFT, + madera_inmux_texts); + +static SOC_ENUM_SINGLE_DECL(madera_in2muxl_enum, + MADERA_ADC_DIGITAL_VOLUME_2L, + MADERA_IN2L_SRC_SHIFT, + madera_inmux_texts); + +static SOC_ENUM_SINGLE_DECL(madera_in2muxr_enum, + MADERA_ADC_DIGITAL_VOLUME_2R, + MADERA_IN2R_SRC_SHIFT, + madera_inmux_texts); + +const struct snd_kcontrol_new madera_inmux[] = { + SOC_DAPM_ENUM_EXT("IN1L Mux", madera_in1muxl_enum, + snd_soc_dapm_get_enum_double, madera_inmux_put), + SOC_DAPM_ENUM_EXT("IN1R Mux", madera_in1muxr_enum, + snd_soc_dapm_get_enum_double, madera_inmux_put), + SOC_DAPM_ENUM_EXT("IN2L Mux", madera_in2muxl_enum, + snd_soc_dapm_get_enum_double, madera_inmux_put), + SOC_DAPM_ENUM_EXT("IN2R Mux", madera_in2muxr_enum, + snd_soc_dapm_get_enum_double, madera_inmux_put), +}; +EXPORT_SYMBOL_GPL(madera_inmux); + +static const char * const madera_dmode_texts[] = { + "Analog", + "Digital", +}; + +static SOC_ENUM_SINGLE_DECL(madera_in1dmode_enum, + MADERA_IN1L_CONTROL, + MADERA_IN1_MODE_SHIFT, + madera_dmode_texts); + +static SOC_ENUM_SINGLE_DECL(madera_in2dmode_enum, + MADERA_IN2L_CONTROL, + MADERA_IN2_MODE_SHIFT, + madera_dmode_texts); + +static SOC_ENUM_SINGLE_DECL(madera_in3dmode_enum, + MADERA_IN3L_CONTROL, + MADERA_IN3_MODE_SHIFT, + madera_dmode_texts); + +const struct snd_kcontrol_new madera_inmode[] = { + SOC_DAPM_ENUM("IN1 Mode", madera_in1dmode_enum), + SOC_DAPM_ENUM("IN2 Mode", madera_in2dmode_enum), + SOC_DAPM_ENUM("IN3 Mode", madera_in3dmode_enum), +}; +EXPORT_SYMBOL_GPL(madera_inmode); + +static bool madera_can_change_grp_rate(const struct madera_priv *priv, + unsigned int reg) +{ + int count; + + switch (reg) { + case MADERA_FX_CTRL1: + count = priv->domain_group_ref[MADERA_DOM_GRP_FX]; + break; + case MADERA_ASRC1_RATE1: + case MADERA_ASRC1_RATE2: + count = priv->domain_group_ref[MADERA_DOM_GRP_ASRC1]; + break; + case MADERA_ASRC2_RATE1: + case MADERA_ASRC2_RATE2: + count = priv->domain_group_ref[MADERA_DOM_GRP_ASRC2]; + break; + case MADERA_ISRC_1_CTRL_1: + case MADERA_ISRC_1_CTRL_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC1]; + break; + case MADERA_ISRC_2_CTRL_1: + case MADERA_ISRC_2_CTRL_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC2]; + break; + case MADERA_ISRC_3_CTRL_1: + case MADERA_ISRC_3_CTRL_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC3]; + break; + case MADERA_ISRC_4_CTRL_1: + case MADERA_ISRC_4_CTRL_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_ISRC4]; + break; + case MADERA_OUTPUT_RATE_1: + count = priv->domain_group_ref[MADERA_DOM_GRP_OUT]; + break; + case MADERA_SPD1_TX_CONTROL: + count = priv->domain_group_ref[MADERA_DOM_GRP_SPD]; + break; + case MADERA_DSP1_CONFIG_1: + case MADERA_DSP1_CONFIG_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_DSP1]; + break; + case MADERA_DSP2_CONFIG_1: + case MADERA_DSP2_CONFIG_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_DSP2]; + break; + case MADERA_DSP3_CONFIG_1: + case MADERA_DSP3_CONFIG_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_DSP3]; + break; + case MADERA_DSP4_CONFIG_1: + case MADERA_DSP4_CONFIG_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_DSP4]; + break; + case MADERA_DSP5_CONFIG_1: + case MADERA_DSP5_CONFIG_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_DSP5]; + break; + case MADERA_DSP6_CONFIG_1: + case MADERA_DSP6_CONFIG_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_DSP6]; + break; + case MADERA_DSP7_CONFIG_1: + case MADERA_DSP7_CONFIG_2: + count = priv->domain_group_ref[MADERA_DOM_GRP_DSP7]; + break; + case MADERA_AIF1_RATE_CTRL: + count = priv->domain_group_ref[MADERA_DOM_GRP_AIF1]; + break; + case MADERA_AIF2_RATE_CTRL: + count = priv->domain_group_ref[MADERA_DOM_GRP_AIF2]; + break; + case MADERA_AIF3_RATE_CTRL: + count = priv->domain_group_ref[MADERA_DOM_GRP_AIF3]; + break; + case MADERA_AIF4_RATE_CTRL: + count = priv->domain_group_ref[MADERA_DOM_GRP_AIF4]; + break; + case MADERA_SLIMBUS_RATES_1: + case MADERA_SLIMBUS_RATES_2: + case MADERA_SLIMBUS_RATES_3: + case MADERA_SLIMBUS_RATES_4: + case MADERA_SLIMBUS_RATES_5: + case MADERA_SLIMBUS_RATES_6: + case MADERA_SLIMBUS_RATES_7: + case MADERA_SLIMBUS_RATES_8: + count = priv->domain_group_ref[MADERA_DOM_GRP_SLIMBUS]; + break; + case MADERA_PWM_DRIVE_1: + count = priv->domain_group_ref[MADERA_DOM_GRP_PWM]; + break; + default: + return false; + } + + dev_dbg(priv->madera->dev, "Rate reg 0x%x group ref %d\n", reg, count); + + if (count) + return false; + else + return true; +} + +static int madera_adsp_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int cached_rate; + const int adsp_num = e->shift_l; + int item; + + mutex_lock(&priv->rate_lock); + cached_rate = priv->adsp_rate_cache[adsp_num]; + mutex_unlock(&priv->rate_lock); + + item = snd_soc_enum_val_to_item(e, cached_rate); + ucontrol->value.enumerated.item[0] = item; + + return 0; +} + +static int madera_adsp_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + const int adsp_num = e->shift_l; + const unsigned int item = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (item >= e->items) + return -EINVAL; + + /* + * We don't directly write the rate register here but we want to + * maintain consistent behaviour that rate domains cannot be changed + * while in use since this is a hardware requirement + */ + mutex_lock(&priv->rate_lock); + + if (!madera_can_change_grp_rate(priv, priv->adsp[adsp_num].cs_dsp.base)) { + dev_warn(priv->madera->dev, + "Cannot change '%s' while in use by active audio paths\n", + kcontrol->id.name); + ret = -EBUSY; + } else if (priv->adsp_rate_cache[adsp_num] != e->values[item]) { + /* Volatile register so defer until the codec is powered up */ + priv->adsp_rate_cache[adsp_num] = e->values[item]; + ret = 1; + } + + mutex_unlock(&priv->rate_lock); + + return ret; +} + +static const struct soc_enum madera_adsp_rate_enum[] = { + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0, 0xf, MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 1, 0xf, MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 2, 0xf, MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 3, 0xf, MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 4, 0xf, MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 5, 0xf, MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 6, 0xf, MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), +}; + +const struct snd_kcontrol_new madera_adsp_rate_controls[] = { + SOC_ENUM_EXT("DSP1 Rate", madera_adsp_rate_enum[0], + madera_adsp_rate_get, madera_adsp_rate_put), + SOC_ENUM_EXT("DSP2 Rate", madera_adsp_rate_enum[1], + madera_adsp_rate_get, madera_adsp_rate_put), + SOC_ENUM_EXT("DSP3 Rate", madera_adsp_rate_enum[2], + madera_adsp_rate_get, madera_adsp_rate_put), + SOC_ENUM_EXT("DSP4 Rate", madera_adsp_rate_enum[3], + madera_adsp_rate_get, madera_adsp_rate_put), + SOC_ENUM_EXT("DSP5 Rate", madera_adsp_rate_enum[4], + madera_adsp_rate_get, madera_adsp_rate_put), + SOC_ENUM_EXT("DSP6 Rate", madera_adsp_rate_enum[5], + madera_adsp_rate_get, madera_adsp_rate_put), + SOC_ENUM_EXT("DSP7 Rate", madera_adsp_rate_enum[6], + madera_adsp_rate_get, madera_adsp_rate_put), +}; +EXPORT_SYMBOL_GPL(madera_adsp_rate_controls); + +static int madera_write_adsp_clk_setting(struct madera_priv *priv, + struct wm_adsp *dsp, + unsigned int freq) +{ + unsigned int val; + unsigned int mask = MADERA_DSP_RATE_MASK; + int ret; + + val = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT; + + switch (priv->madera->type) { + case CS47L35: + case CS47L85: + case WM1840: + /* use legacy frequency registers */ + mask |= MADERA_DSP_CLK_SEL_MASK; + val |= (freq << MADERA_DSP_CLK_SEL_SHIFT); + break; + default: + /* Configure exact dsp frequency */ + dev_dbg(priv->madera->dev, "Set DSP frequency to 0x%x\n", freq); + + ret = regmap_write(dsp->cs_dsp.regmap, + dsp->cs_dsp.base + MADERA_DSP_CONFIG_2_OFFS, freq); + if (ret) + goto err; + break; + } + + ret = regmap_update_bits(dsp->cs_dsp.regmap, + dsp->cs_dsp.base + MADERA_DSP_CONFIG_1_OFFS, + mask, val); + if (ret) + goto err; + + dev_dbg(priv->madera->dev, "Set DSP clocking to 0x%x\n", val); + + return 0; + +err: + dev_err(dsp->cs_dsp.dev, "Failed to set DSP%d clock: %d\n", dsp->cs_dsp.num, ret); + + return ret; +} + +int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num, + unsigned int freq) +{ + struct wm_adsp *dsp = &priv->adsp[dsp_num]; + struct madera *madera = priv->madera; + unsigned int cur, new; + int ret; + + /* + * This is called at a higher DAPM priority than the mux widgets so + * the muxes are still off at this point and it's safe to change + * the rate domain control. + * Also called at a lower DAPM priority than the domain group widgets + * so locking the reads of adsp_rate_cache is not necessary as we know + * changes are locked out by the domain_group_ref reference count. + */ + + ret = regmap_read(dsp->cs_dsp.regmap, dsp->cs_dsp.base, &cur); + if (ret) { + dev_err(madera->dev, + "Failed to read current DSP rate: %d\n", ret); + return ret; + } + + cur &= MADERA_DSP_RATE_MASK; + + new = priv->adsp_rate_cache[dsp->cs_dsp.num - 1] << MADERA_DSP_RATE_SHIFT; + + if (new == cur) { + dev_dbg(madera->dev, "DSP rate not changed\n"); + return madera_write_adsp_clk_setting(priv, dsp, freq); + } else { + dev_dbg(madera->dev, "DSP rate changed\n"); + + /* The write must be guarded by a number of SYSCLK cycles */ + madera_spin_sysclk(priv); + ret = madera_write_adsp_clk_setting(priv, dsp, freq); + madera_spin_sysclk(priv); + return ret; + } +} +EXPORT_SYMBOL_GPL(madera_set_adsp_clk); + +int madera_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int item = ucontrol->value.enumerated.item[0]; + unsigned int val; + int ret; + + if (item >= e->items) + return -EINVAL; + + /* + * Prevent the domain powering up while we're checking whether it's + * safe to change rate domain + */ + mutex_lock(&priv->rate_lock); + + val = snd_soc_component_read(component, e->reg); + val >>= e->shift_l; + val &= e->mask; + if (snd_soc_enum_item_to_val(e, item) == val) { + ret = 0; + goto out; + } + + if (!madera_can_change_grp_rate(priv, e->reg)) { + dev_warn(priv->madera->dev, + "Cannot change '%s' while in use by active audio paths\n", + kcontrol->id.name); + ret = -EBUSY; + } else { + /* The write must be guarded by a number of SYSCLK cycles */ + madera_spin_sysclk(priv); + ret = snd_soc_put_enum_double(kcontrol, ucontrol); + madera_spin_sysclk(priv); + } +out: + mutex_unlock(&priv->rate_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(madera_rate_put); + +static void madera_configure_input_mode(struct madera *madera) +{ + unsigned int dig_mode, ana_mode_l, ana_mode_r; + int max_analogue_inputs, max_dmic_sup, i; + + switch (madera->type) { + case CS47L15: + max_analogue_inputs = 1; + max_dmic_sup = 2; + break; + case CS47L35: + max_analogue_inputs = 2; + max_dmic_sup = 2; + break; + case CS47L85: + case WM1840: + max_analogue_inputs = 3; + max_dmic_sup = 3; + break; + case CS47L90: + case CS47L91: + max_analogue_inputs = 2; + max_dmic_sup = 2; + break; + default: + max_analogue_inputs = 2; + max_dmic_sup = 4; + break; + } + + /* + * Initialize input modes from the A settings. For muxed inputs the + * B settings will be applied if the mux is changed + */ + for (i = 0; i < max_dmic_sup; i++) { + dev_dbg(madera->dev, "IN%d mode %u:%u:%u:%u\n", i + 1, + madera->pdata.codec.inmode[i][0], + madera->pdata.codec.inmode[i][1], + madera->pdata.codec.inmode[i][2], + madera->pdata.codec.inmode[i][3]); + + dig_mode = madera->pdata.codec.dmic_ref[i] << + MADERA_IN1_DMIC_SUP_SHIFT; + + switch (madera->pdata.codec.inmode[i][0]) { + case MADERA_INMODE_DIFF: + ana_mode_l = 0; + break; + case MADERA_INMODE_SE: + ana_mode_l = 1 << MADERA_IN1L_SRC_SE_SHIFT; + break; + default: + dev_warn(madera->dev, + "IN%dAL Illegal inmode %u ignored\n", + i + 1, madera->pdata.codec.inmode[i][0]); + continue; + } + + switch (madera->pdata.codec.inmode[i][1]) { + case MADERA_INMODE_DIFF: + ana_mode_r = 0; + break; + case MADERA_INMODE_SE: + ana_mode_r = 1 << MADERA_IN1R_SRC_SE_SHIFT; + break; + default: + dev_warn(madera->dev, + "IN%dAR Illegal inmode %u ignored\n", + i + 1, madera->pdata.codec.inmode[i][1]); + continue; + } + + dev_dbg(madera->dev, + "IN%dA DMIC mode=0x%x Analogue mode=0x%x,0x%x\n", + i + 1, dig_mode, ana_mode_l, ana_mode_r); + + regmap_update_bits(madera->regmap, + MADERA_IN1L_CONTROL + (i * 8), + MADERA_IN1_DMIC_SUP_MASK, dig_mode); + + if (i >= max_analogue_inputs) + continue; + + regmap_update_bits(madera->regmap, + MADERA_ADC_DIGITAL_VOLUME_1L + (i * 8), + MADERA_IN1L_SRC_SE_MASK, ana_mode_l); + + regmap_update_bits(madera->regmap, + MADERA_ADC_DIGITAL_VOLUME_1R + (i * 8), + MADERA_IN1R_SRC_SE_MASK, ana_mode_r); + } +} + +int madera_init_inputs(struct snd_soc_component *component) +{ + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + + madera_configure_input_mode(madera); + + return 0; +} +EXPORT_SYMBOL_GPL(madera_init_inputs); + +static const struct snd_soc_dapm_route madera_mono_routes[] = { + { "OUT1R", NULL, "OUT1L" }, + { "OUT2R", NULL, "OUT2L" }, + { "OUT3R", NULL, "OUT3L" }, + { "OUT4R", NULL, "OUT4L" }, + { "OUT5R", NULL, "OUT5L" }, + { "OUT6R", NULL, "OUT6L" }, +}; + +int madera_init_outputs(struct snd_soc_component *component, + const struct snd_soc_dapm_route *routes, + int n_mono_routes, int n_real) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + const struct madera_codec_pdata *pdata = &madera->pdata.codec; + unsigned int val; + int i; + + if (n_mono_routes > MADERA_MAX_OUTPUT) { + dev_warn(madera->dev, + "Requested %d mono outputs, using maximum allowed %d\n", + n_mono_routes, MADERA_MAX_OUTPUT); + n_mono_routes = MADERA_MAX_OUTPUT; + } + + if (!routes) + routes = madera_mono_routes; + + for (i = 0; i < n_mono_routes; i++) { + /* Default is 0 so noop with defaults */ + if (pdata->out_mono[i]) { + val = MADERA_OUT1_MONO; + snd_soc_dapm_add_routes(dapm, &routes[i], 1); + } else { + val = 0; + } + + if (i >= n_real) + continue; + + regmap_update_bits(madera->regmap, + MADERA_OUTPUT_PATH_CONFIG_1L + (i * 8), + MADERA_OUT1_MONO, val); + + dev_dbg(madera->dev, "OUT%d mono=0x%x\n", i + 1, val); + } + + for (i = 0; i < MADERA_MAX_PDM_SPK; i++) { + dev_dbg(madera->dev, "PDM%d fmt=0x%x mute=0x%x\n", i + 1, + pdata->pdm_fmt[i], pdata->pdm_mute[i]); + + if (pdata->pdm_mute[i]) + regmap_update_bits(madera->regmap, + MADERA_PDM_SPK1_CTRL_1 + (i * 2), + MADERA_SPK1_MUTE_ENDIAN_MASK | + MADERA_SPK1_MUTE_SEQ1_MASK, + pdata->pdm_mute[i]); + + if (pdata->pdm_fmt[i]) + regmap_update_bits(madera->regmap, + MADERA_PDM_SPK1_CTRL_2 + (i * 2), + MADERA_SPK1_FMT_MASK, + pdata->pdm_fmt[i]); + } + + return 0; +} +EXPORT_SYMBOL_GPL(madera_init_outputs); + +int madera_init_bus_error_irq(struct madera_priv *priv, int dsp_num, + irq_handler_t handler) +{ + struct madera *madera = priv->madera; + int ret; + + ret = madera_request_irq(madera, + madera_dsp_bus_error_irqs[dsp_num], + "ADSP2 bus error", + handler, + &priv->adsp[dsp_num]); + if (ret) + dev_err(madera->dev, + "Failed to request DSP Lock region IRQ: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(madera_init_bus_error_irq); + +void madera_free_bus_error_irq(struct madera_priv *priv, int dsp_num) +{ + struct madera *madera = priv->madera; + + madera_free_irq(madera, + madera_dsp_bus_error_irqs[dsp_num], + &priv->adsp[dsp_num]); +} +EXPORT_SYMBOL_GPL(madera_free_bus_error_irq); + +const char * const madera_mixer_texts[] = { + "None", + "Tone Generator 1", + "Tone Generator 2", + "Haptics", + "AEC1", + "AEC2", + "Mic Mute Mixer", + "Noise Generator", + "IN1L", + "IN1R", + "IN2L", + "IN2R", + "IN3L", + "IN3R", + "IN4L", + "IN4R", + "IN5L", + "IN5R", + "IN6L", + "IN6R", + "AIF1RX1", + "AIF1RX2", + "AIF1RX3", + "AIF1RX4", + "AIF1RX5", + "AIF1RX6", + "AIF1RX7", + "AIF1RX8", + "AIF2RX1", + "AIF2RX2", + "AIF2RX3", + "AIF2RX4", + "AIF2RX5", + "AIF2RX6", + "AIF2RX7", + "AIF2RX8", + "AIF3RX1", + "AIF3RX2", + "AIF3RX3", + "AIF3RX4", + "AIF4RX1", + "AIF4RX2", + "SLIMRX1", + "SLIMRX2", + "SLIMRX3", + "SLIMRX4", + "SLIMRX5", + "SLIMRX6", + "SLIMRX7", + "SLIMRX8", + "EQ1", + "EQ2", + "EQ3", + "EQ4", + "DRC1L", + "DRC1R", + "DRC2L", + "DRC2R", + "LHPF1", + "LHPF2", + "LHPF3", + "LHPF4", + "DSP1.1", + "DSP1.2", + "DSP1.3", + "DSP1.4", + "DSP1.5", + "DSP1.6", + "DSP2.1", + "DSP2.2", + "DSP2.3", + "DSP2.4", + "DSP2.5", + "DSP2.6", + "DSP3.1", + "DSP3.2", + "DSP3.3", + "DSP3.4", + "DSP3.5", + "DSP3.6", + "DSP4.1", + "DSP4.2", + "DSP4.3", + "DSP4.4", + "DSP4.5", + "DSP4.6", + "DSP5.1", + "DSP5.2", + "DSP5.3", + "DSP5.4", + "DSP5.5", + "DSP5.6", + "DSP6.1", + "DSP6.2", + "DSP6.3", + "DSP6.4", + "DSP6.5", + "DSP6.6", + "DSP7.1", + "DSP7.2", + "DSP7.3", + "DSP7.4", + "DSP7.5", + "DSP7.6", + "ASRC1IN1L", + "ASRC1IN1R", + "ASRC1IN2L", + "ASRC1IN2R", + "ASRC2IN1L", + "ASRC2IN1R", + "ASRC2IN2L", + "ASRC2IN2R", + "ISRC1INT1", + "ISRC1INT2", + "ISRC1INT3", + "ISRC1INT4", + "ISRC1DEC1", + "ISRC1DEC2", + "ISRC1DEC3", + "ISRC1DEC4", + "ISRC2INT1", + "ISRC2INT2", + "ISRC2INT3", + "ISRC2INT4", + "ISRC2DEC1", + "ISRC2DEC2", + "ISRC2DEC3", + "ISRC2DEC4", + "ISRC3INT1", + "ISRC3INT2", + "ISRC3INT3", + "ISRC3INT4", + "ISRC3DEC1", + "ISRC3DEC2", + "ISRC3DEC3", + "ISRC3DEC4", + "ISRC4INT1", + "ISRC4INT2", + "ISRC4DEC1", + "ISRC4DEC2", + "DFC1", + "DFC2", + "DFC3", + "DFC4", + "DFC5", + "DFC6", + "DFC7", + "DFC8", +}; +EXPORT_SYMBOL_GPL(madera_mixer_texts); + +const unsigned int madera_mixer_values[] = { + 0x00, /* None */ + 0x04, /* Tone Generator 1 */ + 0x05, /* Tone Generator 2 */ + 0x06, /* Haptics */ + 0x08, /* AEC */ + 0x09, /* AEC2 */ + 0x0c, /* Noise mixer */ + 0x0d, /* Comfort noise */ + 0x10, /* IN1L */ + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1A, + 0x1B, + 0x20, /* AIF1RX1 */ + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, + 0x26, + 0x27, + 0x28, /* AIF2RX1 */ + 0x29, + 0x2a, + 0x2b, + 0x2c, + 0x2d, + 0x2e, + 0x2f, + 0x30, /* AIF3RX1 */ + 0x31, + 0x32, + 0x33, + 0x34, /* AIF4RX1 */ + 0x35, + 0x38, /* SLIMRX1 */ + 0x39, + 0x3a, + 0x3b, + 0x3c, + 0x3d, + 0x3e, + 0x3f, + 0x50, /* EQ1 */ + 0x51, + 0x52, + 0x53, + 0x58, /* DRC1L */ + 0x59, + 0x5a, + 0x5b, + 0x60, /* LHPF1 */ + 0x61, + 0x62, + 0x63, + 0x68, /* DSP1.1 */ + 0x69, + 0x6a, + 0x6b, + 0x6c, + 0x6d, + 0x70, /* DSP2.1 */ + 0x71, + 0x72, + 0x73, + 0x74, + 0x75, + 0x78, /* DSP3.1 */ + 0x79, + 0x7a, + 0x7b, + 0x7c, + 0x7d, + 0x80, /* DSP4.1 */ + 0x81, + 0x82, + 0x83, + 0x84, + 0x85, + 0x88, /* DSP5.1 */ + 0x89, + 0x8a, + 0x8b, + 0x8c, + 0x8d, + 0xc0, /* DSP6.1 */ + 0xc1, + 0xc2, + 0xc3, + 0xc4, + 0xc5, + 0xc8, /* DSP7.1 */ + 0xc9, + 0xca, + 0xcb, + 0xcc, + 0xcd, + 0x90, /* ASRC1IN1L */ + 0x91, + 0x92, + 0x93, + 0x94, /* ASRC2IN1L */ + 0x95, + 0x96, + 0x97, + 0xa0, /* ISRC1INT1 */ + 0xa1, + 0xa2, + 0xa3, + 0xa4, /* ISRC1DEC1 */ + 0xa5, + 0xa6, + 0xa7, + 0xa8, /* ISRC2DEC1 */ + 0xa9, + 0xaa, + 0xab, + 0xac, /* ISRC2INT1 */ + 0xad, + 0xae, + 0xaf, + 0xb0, /* ISRC3DEC1 */ + 0xb1, + 0xb2, + 0xb3, + 0xb4, /* ISRC3INT1 */ + 0xb5, + 0xb6, + 0xb7, + 0xb8, /* ISRC4INT1 */ + 0xb9, + 0xbc, /* ISRC4DEC1 */ + 0xbd, + 0xf8, /* DFC1 */ + 0xf9, + 0xfa, + 0xfb, + 0xfc, + 0xfd, + 0xfe, + 0xff, /* DFC8 */ +}; +EXPORT_SYMBOL_GPL(madera_mixer_values); + +const DECLARE_TLV_DB_SCALE(madera_ana_tlv, 0, 100, 0); +EXPORT_SYMBOL_GPL(madera_ana_tlv); + +const DECLARE_TLV_DB_SCALE(madera_eq_tlv, -1200, 100, 0); +EXPORT_SYMBOL_GPL(madera_eq_tlv); + +const DECLARE_TLV_DB_SCALE(madera_digital_tlv, -6400, 50, 0); +EXPORT_SYMBOL_GPL(madera_digital_tlv); + +const DECLARE_TLV_DB_SCALE(madera_noise_tlv, -13200, 600, 0); +EXPORT_SYMBOL_GPL(madera_noise_tlv); + +const DECLARE_TLV_DB_SCALE(madera_ng_tlv, -12000, 600, 0); +EXPORT_SYMBOL_GPL(madera_ng_tlv); + +const DECLARE_TLV_DB_SCALE(madera_mixer_tlv, -3200, 100, 0); +EXPORT_SYMBOL_GPL(madera_mixer_tlv); + +const char * const madera_rate_text[MADERA_RATE_ENUM_SIZE] = { + "SYNCCLK rate 1", "SYNCCLK rate 2", "SYNCCLK rate 3", + "ASYNCCLK rate 1", "ASYNCCLK rate 2", +}; +EXPORT_SYMBOL_GPL(madera_rate_text); + +const unsigned int madera_rate_val[MADERA_RATE_ENUM_SIZE] = { + 0x0, 0x1, 0x2, 0x8, 0x9, +}; +EXPORT_SYMBOL_GPL(madera_rate_val); + +static const char * const madera_dfc_width_text[MADERA_DFC_WIDTH_ENUM_SIZE] = { + "8 bit", "16 bit", "20 bit", "24 bit", "32 bit", +}; + +static const unsigned int madera_dfc_width_val[MADERA_DFC_WIDTH_ENUM_SIZE] = { + 7, 15, 19, 23, 31, +}; + +static const char * const madera_dfc_type_text[MADERA_DFC_TYPE_ENUM_SIZE] = { + "Fixed", "Unsigned Fixed", "Single Precision Floating", + "Half Precision Floating", "Arm Alternative Floating", +}; + +static const unsigned int madera_dfc_type_val[MADERA_DFC_TYPE_ENUM_SIZE] = { + 0, 1, 2, 4, 5, +}; + +const struct soc_enum madera_dfc_width[] = { + SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_RX, + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + MADERA_DFC1_RX_DATA_WIDTH_MASK >> + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_TX, + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + MADERA_DFC1_TX_DATA_WIDTH_MASK >> + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_RX, + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + MADERA_DFC1_RX_DATA_WIDTH_MASK >> + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_TX, + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + MADERA_DFC1_TX_DATA_WIDTH_MASK >> + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_RX, + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + MADERA_DFC1_RX_DATA_WIDTH_MASK >> + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_TX, + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + MADERA_DFC1_TX_DATA_WIDTH_MASK >> + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_RX, + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + MADERA_DFC1_RX_DATA_WIDTH_MASK >> + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_TX, + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + MADERA_DFC1_TX_DATA_WIDTH_MASK >> + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_RX, + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + MADERA_DFC1_RX_DATA_WIDTH_MASK >> + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_TX, + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + MADERA_DFC1_TX_DATA_WIDTH_MASK >> + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_RX, + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + MADERA_DFC1_RX_DATA_WIDTH_MASK >> + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_TX, + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + MADERA_DFC1_TX_DATA_WIDTH_MASK >> + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_RX, + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + MADERA_DFC1_RX_DATA_WIDTH_MASK >> + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_TX, + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + MADERA_DFC1_TX_DATA_WIDTH_MASK >> + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_RX, + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + MADERA_DFC1_RX_DATA_WIDTH_MASK >> + MADERA_DFC1_RX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_TX, + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + MADERA_DFC1_TX_DATA_WIDTH_MASK >> + MADERA_DFC1_TX_DATA_WIDTH_SHIFT, + ARRAY_SIZE(madera_dfc_width_text), + madera_dfc_width_text, + madera_dfc_width_val), +}; +EXPORT_SYMBOL_GPL(madera_dfc_width); + +const struct soc_enum madera_dfc_type[] = { + SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_RX, + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + MADERA_DFC1_RX_DATA_TYPE_MASK >> + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC1_TX, + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + MADERA_DFC1_TX_DATA_TYPE_MASK >> + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_RX, + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + MADERA_DFC1_RX_DATA_TYPE_MASK >> + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC2_TX, + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + MADERA_DFC1_TX_DATA_TYPE_MASK >> + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_RX, + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + MADERA_DFC1_RX_DATA_TYPE_MASK >> + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC3_TX, + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + MADERA_DFC1_TX_DATA_TYPE_MASK >> + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_RX, + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + MADERA_DFC1_RX_DATA_TYPE_MASK >> + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC4_TX, + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + MADERA_DFC1_TX_DATA_TYPE_MASK >> + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_RX, + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + MADERA_DFC1_RX_DATA_TYPE_MASK >> + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC5_TX, + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + MADERA_DFC1_TX_DATA_TYPE_MASK >> + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_RX, + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + MADERA_DFC1_RX_DATA_TYPE_MASK >> + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC6_TX, + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + MADERA_DFC1_TX_DATA_TYPE_MASK >> + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_RX, + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + MADERA_DFC1_RX_DATA_TYPE_MASK >> + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC7_TX, + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + MADERA_DFC1_TX_DATA_TYPE_MASK >> + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_RX, + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + MADERA_DFC1_RX_DATA_TYPE_MASK >> + MADERA_DFC1_RX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DFC8_TX, + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + MADERA_DFC1_TX_DATA_TYPE_MASK >> + MADERA_DFC1_TX_DATA_TYPE_SHIFT, + ARRAY_SIZE(madera_dfc_type_text), + madera_dfc_type_text, + madera_dfc_type_val), +}; +EXPORT_SYMBOL_GPL(madera_dfc_type); + +const struct soc_enum madera_isrc_fsh[] = { + SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_1_CTRL_1, + MADERA_ISRC1_FSH_SHIFT, 0xf, + MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_2_CTRL_1, + MADERA_ISRC2_FSH_SHIFT, 0xf, + MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_3_CTRL_1, + MADERA_ISRC3_FSH_SHIFT, 0xf, + MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_4_CTRL_1, + MADERA_ISRC4_FSH_SHIFT, 0xf, + MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), +}; +EXPORT_SYMBOL_GPL(madera_isrc_fsh); + +const struct soc_enum madera_isrc_fsl[] = { + SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_1_CTRL_2, + MADERA_ISRC1_FSL_SHIFT, 0xf, + MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_2_CTRL_2, + MADERA_ISRC2_FSL_SHIFT, 0xf, + MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_3_CTRL_2, + MADERA_ISRC3_FSL_SHIFT, 0xf, + MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(MADERA_ISRC_4_CTRL_2, + MADERA_ISRC4_FSL_SHIFT, 0xf, + MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), +}; +EXPORT_SYMBOL_GPL(madera_isrc_fsl); + +const struct soc_enum madera_asrc1_rate[] = { + SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE1, + MADERA_ASRC1_RATE1_SHIFT, 0xf, + MADERA_SYNC_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE2, + MADERA_ASRC1_RATE1_SHIFT, 0xf, + MADERA_ASYNC_RATE_ENUM_SIZE, + madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE, + madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE), +}; +EXPORT_SYMBOL_GPL(madera_asrc1_rate); + +const struct soc_enum madera_asrc1_bidir_rate[] = { + SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE1, + MADERA_ASRC1_RATE1_SHIFT, 0xf, + MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(MADERA_ASRC1_RATE2, + MADERA_ASRC1_RATE2_SHIFT, 0xf, + MADERA_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), +}; +EXPORT_SYMBOL_GPL(madera_asrc1_bidir_rate); + +const struct soc_enum madera_asrc2_rate[] = { + SOC_VALUE_ENUM_SINGLE(MADERA_ASRC2_RATE1, + MADERA_ASRC2_RATE1_SHIFT, 0xf, + MADERA_SYNC_RATE_ENUM_SIZE, + madera_rate_text, madera_rate_val), + SOC_VALUE_ENUM_SINGLE(MADERA_ASRC2_RATE2, + MADERA_ASRC2_RATE2_SHIFT, 0xf, + MADERA_ASYNC_RATE_ENUM_SIZE, + madera_rate_text + MADERA_SYNC_RATE_ENUM_SIZE, + madera_rate_val + MADERA_SYNC_RATE_ENUM_SIZE), +}; +EXPORT_SYMBOL_GPL(madera_asrc2_rate); + +static const char * const madera_vol_ramp_text[] = { + "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", + "15ms/6dB", "30ms/6dB", +}; + +SOC_ENUM_SINGLE_DECL(madera_in_vd_ramp, + MADERA_INPUT_VOLUME_RAMP, + MADERA_IN_VD_RAMP_SHIFT, + madera_vol_ramp_text); +EXPORT_SYMBOL_GPL(madera_in_vd_ramp); + +SOC_ENUM_SINGLE_DECL(madera_in_vi_ramp, + MADERA_INPUT_VOLUME_RAMP, + MADERA_IN_VI_RAMP_SHIFT, + madera_vol_ramp_text); +EXPORT_SYMBOL_GPL(madera_in_vi_ramp); + +SOC_ENUM_SINGLE_DECL(madera_out_vd_ramp, + MADERA_OUTPUT_VOLUME_RAMP, + MADERA_OUT_VD_RAMP_SHIFT, + madera_vol_ramp_text); +EXPORT_SYMBOL_GPL(madera_out_vd_ramp); + +SOC_ENUM_SINGLE_DECL(madera_out_vi_ramp, + MADERA_OUTPUT_VOLUME_RAMP, + MADERA_OUT_VI_RAMP_SHIFT, + madera_vol_ramp_text); +EXPORT_SYMBOL_GPL(madera_out_vi_ramp); + +static const char * const madera_lhpf_mode_text[] = { + "Low-pass", "High-pass" +}; + +SOC_ENUM_SINGLE_DECL(madera_lhpf1_mode, + MADERA_HPLPF1_1, + MADERA_LHPF1_MODE_SHIFT, + madera_lhpf_mode_text); +EXPORT_SYMBOL_GPL(madera_lhpf1_mode); + +SOC_ENUM_SINGLE_DECL(madera_lhpf2_mode, + MADERA_HPLPF2_1, + MADERA_LHPF2_MODE_SHIFT, + madera_lhpf_mode_text); +EXPORT_SYMBOL_GPL(madera_lhpf2_mode); + +SOC_ENUM_SINGLE_DECL(madera_lhpf3_mode, + MADERA_HPLPF3_1, + MADERA_LHPF3_MODE_SHIFT, + madera_lhpf_mode_text); +EXPORT_SYMBOL_GPL(madera_lhpf3_mode); + +SOC_ENUM_SINGLE_DECL(madera_lhpf4_mode, + MADERA_HPLPF4_1, + MADERA_LHPF4_MODE_SHIFT, + madera_lhpf_mode_text); +EXPORT_SYMBOL_GPL(madera_lhpf4_mode); + +static const char * const madera_ng_hold_text[] = { + "30ms", "120ms", "250ms", "500ms", +}; + +SOC_ENUM_SINGLE_DECL(madera_ng_hold, + MADERA_NOISE_GATE_CONTROL, + MADERA_NGATE_HOLD_SHIFT, + madera_ng_hold_text); +EXPORT_SYMBOL_GPL(madera_ng_hold); + +static const char * const madera_in_hpf_cut_text[] = { + "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz" +}; + +SOC_ENUM_SINGLE_DECL(madera_in_hpf_cut_enum, + MADERA_HPF_CONTROL, + MADERA_IN_HPF_CUT_SHIFT, + madera_in_hpf_cut_text); +EXPORT_SYMBOL_GPL(madera_in_hpf_cut_enum); + +static const char * const madera_in_dmic_osr_text[MADERA_OSR_ENUM_SIZE] = { + "384kHz", "768kHz", "1.536MHz", "3.072MHz", "6.144MHz", +}; + +static const unsigned int madera_in_dmic_osr_val[MADERA_OSR_ENUM_SIZE] = { + 2, 3, 4, 5, 6, +}; + +const struct soc_enum madera_in_dmic_osr[] = { + SOC_VALUE_ENUM_SINGLE(MADERA_DMIC1L_CONTROL, MADERA_IN1_OSR_SHIFT, + 0x7, MADERA_OSR_ENUM_SIZE, + madera_in_dmic_osr_text, madera_in_dmic_osr_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DMIC2L_CONTROL, MADERA_IN2_OSR_SHIFT, + 0x7, MADERA_OSR_ENUM_SIZE, + madera_in_dmic_osr_text, madera_in_dmic_osr_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DMIC3L_CONTROL, MADERA_IN3_OSR_SHIFT, + 0x7, MADERA_OSR_ENUM_SIZE, + madera_in_dmic_osr_text, madera_in_dmic_osr_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DMIC4L_CONTROL, MADERA_IN4_OSR_SHIFT, + 0x7, MADERA_OSR_ENUM_SIZE, + madera_in_dmic_osr_text, madera_in_dmic_osr_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DMIC5L_CONTROL, MADERA_IN5_OSR_SHIFT, + 0x7, MADERA_OSR_ENUM_SIZE, + madera_in_dmic_osr_text, madera_in_dmic_osr_val), + SOC_VALUE_ENUM_SINGLE(MADERA_DMIC6L_CONTROL, MADERA_IN6_OSR_SHIFT, + 0x7, MADERA_OSR_ENUM_SIZE, + madera_in_dmic_osr_text, madera_in_dmic_osr_val), +}; +EXPORT_SYMBOL_GPL(madera_in_dmic_osr); + +static const char * const madera_anc_input_src_text[] = { + "None", "IN1", "IN2", "IN3", "IN4", "IN5", "IN6", +}; + +static const char * const madera_anc_channel_src_text[] = { + "None", "Left", "Right", "Combine", +}; + +const struct soc_enum madera_anc_input_src[] = { + SOC_ENUM_SINGLE(MADERA_ANC_SRC, + MADERA_IN_RXANCL_SEL_SHIFT, + ARRAY_SIZE(madera_anc_input_src_text), + madera_anc_input_src_text), + SOC_ENUM_SINGLE(MADERA_FCL_ADC_REFORMATTER_CONTROL, + MADERA_FCL_MIC_MODE_SEL_SHIFT, + ARRAY_SIZE(madera_anc_channel_src_text), + madera_anc_channel_src_text), + SOC_ENUM_SINGLE(MADERA_ANC_SRC, + MADERA_IN_RXANCR_SEL_SHIFT, + ARRAY_SIZE(madera_anc_input_src_text), + madera_anc_input_src_text), + SOC_ENUM_SINGLE(MADERA_FCR_ADC_REFORMATTER_CONTROL, + MADERA_FCR_MIC_MODE_SEL_SHIFT, + ARRAY_SIZE(madera_anc_channel_src_text), + madera_anc_channel_src_text), +}; +EXPORT_SYMBOL_GPL(madera_anc_input_src); + +static const char * const madera_anc_ng_texts[] = { + "None", "Internal", "External", +}; + +SOC_ENUM_SINGLE_DECL(madera_anc_ng_enum, SND_SOC_NOPM, 0, madera_anc_ng_texts); +EXPORT_SYMBOL_GPL(madera_anc_ng_enum); + +static const char * const madera_out_anc_src_text[] = { + "None", "RXANCL", "RXANCR", +}; + +const struct soc_enum madera_output_anc_src[] = { + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_1L, + MADERA_OUT1L_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_1R, + MADERA_OUT1R_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_2L, + MADERA_OUT2L_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_2R, + MADERA_OUT2R_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_3L, + MADERA_OUT3L_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_3R, + MADERA_OUT3R_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_4L, + MADERA_OUT4L_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_4R, + MADERA_OUT4R_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_5L, + MADERA_OUT5L_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_5R, + MADERA_OUT5R_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_6L, + MADERA_OUT6L_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), + SOC_ENUM_SINGLE(MADERA_OUTPUT_PATH_CONFIG_6R, + MADERA_OUT6R_ANC_SRC_SHIFT, + ARRAY_SIZE(madera_out_anc_src_text), + madera_out_anc_src_text), +}; +EXPORT_SYMBOL_GPL(madera_output_anc_src); + +int madera_dfc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int reg = e->reg; + unsigned int val; + int ret = 0; + + reg = ((reg / 6) * 6) - 2; + + snd_soc_dapm_mutex_lock(dapm); + + val = snd_soc_component_read(component, reg); + if (val & MADERA_DFC1_ENA) { + ret = -EBUSY; + dev_err(component->dev, "Can't change mode on an active DFC\n"); + goto exit; + } + + ret = snd_soc_put_enum_double(kcontrol, ucontrol); +exit: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} +EXPORT_SYMBOL_GPL(madera_dfc_put); + +int madera_lp_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + unsigned int val, mask; + int ret; + + snd_soc_dapm_mutex_lock(dapm); + + /* Cannot change lp mode on an active input */ + val = snd_soc_component_read(component, MADERA_INPUT_ENABLES); + mask = (mc->reg - MADERA_ADC_DIGITAL_VOLUME_1L) / 4; + mask ^= 0x1; /* Flip bottom bit for channel order */ + + if (val & (1 << mask)) { + ret = -EBUSY; + dev_err(component->dev, + "Can't change lp mode on an active input\n"); + goto exit; + } + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + +exit: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} +EXPORT_SYMBOL_GPL(madera_lp_mode_put); + +const struct snd_kcontrol_new madera_dsp_trigger_output_mux[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), +}; +EXPORT_SYMBOL_GPL(madera_dsp_trigger_output_mux); + +const struct snd_kcontrol_new madera_drc_activity_output_mux[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0), +}; +EXPORT_SYMBOL_GPL(madera_drc_activity_output_mux); + +static void madera_in_set_vu(struct madera_priv *priv, bool enable) +{ + unsigned int val; + int i, ret; + + if (enable) + val = MADERA_IN_VU; + else + val = 0; + + for (i = 0; i < priv->num_inputs; i++) { + ret = regmap_update_bits(priv->madera->regmap, + MADERA_ADC_DIGITAL_VOLUME_1L + (i * 4), + MADERA_IN_VU, val); + if (ret) + dev_warn(priv->madera->dev, + "Failed to modify VU bits: %d\n", ret); + } +} + +int madera_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int reg, val; + + if (w->shift % 2) + reg = MADERA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8); + else + reg = MADERA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + priv->in_pending++; + break; + case SND_SOC_DAPM_POST_PMU: + priv->in_pending--; + snd_soc_component_update_bits(component, reg, + MADERA_IN1L_MUTE, 0); + + /* If this is the last input pending then allow VU */ + if (priv->in_pending == 0) { + usleep_range(1000, 3000); + madera_in_set_vu(priv, true); + } + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, reg, + MADERA_IN1L_MUTE | MADERA_IN_VU, + MADERA_IN1L_MUTE | MADERA_IN_VU); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable volume updates if no inputs are enabled */ + val = snd_soc_component_read(component, MADERA_INPUT_ENABLES); + if (!val) + madera_in_set_vu(priv, false); + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(madera_in_ev); + +int madera_out_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + int out_up_delay; + + switch (madera->type) { + case CS47L90: + case CS47L91: + case CS42L92: + case CS47L92: + case CS47L93: + out_up_delay = 6; + break; + default: + out_up_delay = 17; + break; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case MADERA_OUT1L_ENA_SHIFT: + case MADERA_OUT1R_ENA_SHIFT: + case MADERA_OUT2L_ENA_SHIFT: + case MADERA_OUT2R_ENA_SHIFT: + case MADERA_OUT3L_ENA_SHIFT: + case MADERA_OUT3R_ENA_SHIFT: + priv->out_up_pending++; + priv->out_up_delay += out_up_delay; + break; + default: + break; + } + break; + + case SND_SOC_DAPM_POST_PMU: + switch (w->shift) { + case MADERA_OUT1L_ENA_SHIFT: + case MADERA_OUT1R_ENA_SHIFT: + case MADERA_OUT2L_ENA_SHIFT: + case MADERA_OUT2R_ENA_SHIFT: + case MADERA_OUT3L_ENA_SHIFT: + case MADERA_OUT3R_ENA_SHIFT: + priv->out_up_pending--; + if (!priv->out_up_pending) { + msleep(priv->out_up_delay); + priv->out_up_delay = 0; + } + break; + + default: + break; + } + break; + + case SND_SOC_DAPM_PRE_PMD: + switch (w->shift) { + case MADERA_OUT1L_ENA_SHIFT: + case MADERA_OUT1R_ENA_SHIFT: + case MADERA_OUT2L_ENA_SHIFT: + case MADERA_OUT2R_ENA_SHIFT: + case MADERA_OUT3L_ENA_SHIFT: + case MADERA_OUT3R_ENA_SHIFT: + priv->out_down_pending++; + priv->out_down_delay++; + break; + default: + break; + } + break; + + case SND_SOC_DAPM_POST_PMD: + switch (w->shift) { + case MADERA_OUT1L_ENA_SHIFT: + case MADERA_OUT1R_ENA_SHIFT: + case MADERA_OUT2L_ENA_SHIFT: + case MADERA_OUT2R_ENA_SHIFT: + case MADERA_OUT3L_ENA_SHIFT: + case MADERA_OUT3R_ENA_SHIFT: + priv->out_down_pending--; + if (!priv->out_down_pending) { + msleep(priv->out_down_delay); + priv->out_down_delay = 0; + } + break; + default: + break; + } + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(madera_out_ev); + +int madera_hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + unsigned int mask = 1 << w->shift; + unsigned int out_num = w->shift / 2; + unsigned int val; + unsigned int ep_sel = 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = mask; + break; + case SND_SOC_DAPM_PRE_PMD: + val = 0; + break; + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + return madera_out_ev(w, kcontrol, event); + default: + return 0; + } + + /* Store the desired state for the HP outputs */ + madera->hp_ena &= ~mask; + madera->hp_ena |= val; + + switch (madera->type) { + case CS42L92: + case CS47L92: + case CS47L93: + break; + default: + /* if OUT1 is routed to EPOUT, ignore HP clamp and impedance */ + regmap_read(madera->regmap, MADERA_OUTPUT_ENABLES_1, &ep_sel); + ep_sel &= MADERA_EP_SEL_MASK; + break; + } + + /* Force off if HPDET has disabled the clamp for this output */ + if (!ep_sel && + (!madera->out_clamp[out_num] || madera->out_shorted[out_num])) + val = 0; + + regmap_update_bits(madera->regmap, MADERA_OUTPUT_ENABLES_1, mask, val); + + return madera_out_ev(w, kcontrol, event); +} +EXPORT_SYMBOL_GPL(madera_hp_ev); + +int madera_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int val; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = 1 << w->shift; + break; + case SND_SOC_DAPM_PRE_PMD: + val = 1 << (w->shift + 1); + break; + default: + return 0; + } + + snd_soc_component_write(component, MADERA_CLOCK_CONTROL, val); + + return 0; +} +EXPORT_SYMBOL_GPL(madera_anc_ev); + +static const unsigned int madera_opclk_ref_48k_rates[] = { + 6144000, + 12288000, + 24576000, + 49152000, +}; + +static const unsigned int madera_opclk_ref_44k1_rates[] = { + 5644800, + 11289600, + 22579200, + 45158400, +}; + +static int madera_set_opclk(struct snd_soc_component *component, + unsigned int clk, unsigned int freq) +{ + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int mask = MADERA_OPCLK_DIV_MASK | MADERA_OPCLK_SEL_MASK; + unsigned int reg, val; + const unsigned int *rates; + int ref, div, refclk; + + BUILD_BUG_ON(ARRAY_SIZE(madera_opclk_ref_48k_rates) != + ARRAY_SIZE(madera_opclk_ref_44k1_rates)); + + switch (clk) { + case MADERA_CLK_OPCLK: + reg = MADERA_OUTPUT_SYSTEM_CLOCK; + refclk = priv->sysclk; + break; + case MADERA_CLK_ASYNC_OPCLK: + reg = MADERA_OUTPUT_ASYNC_CLOCK; + refclk = priv->asyncclk; + break; + default: + return -EINVAL; + } + + if (refclk % 4000) + rates = madera_opclk_ref_44k1_rates; + else + rates = madera_opclk_ref_48k_rates; + + for (ref = 0; ref < ARRAY_SIZE(madera_opclk_ref_48k_rates); ++ref) { + if (rates[ref] > refclk) + continue; + + div = 2; + while ((rates[ref] / div >= freq) && (div <= 30)) { + if (rates[ref] / div == freq) { + dev_dbg(component->dev, "Configured %dHz OPCLK\n", + freq); + + val = (div << MADERA_OPCLK_DIV_SHIFT) | ref; + + snd_soc_component_update_bits(component, reg, + mask, val); + return 0; + } + div += 2; + } + } + + dev_err(component->dev, "Unable to generate %dHz OPCLK\n", freq); + + return -EINVAL; +} + +static int madera_get_sysclk_setting(unsigned int freq) +{ + switch (freq) { + case 0: + case 5644800: + case 6144000: + return 0; + case 11289600: + case 12288000: + return MADERA_SYSCLK_12MHZ << MADERA_SYSCLK_FREQ_SHIFT; + case 22579200: + case 24576000: + return MADERA_SYSCLK_24MHZ << MADERA_SYSCLK_FREQ_SHIFT; + case 45158400: + case 49152000: + return MADERA_SYSCLK_49MHZ << MADERA_SYSCLK_FREQ_SHIFT; + case 90316800: + case 98304000: + return MADERA_SYSCLK_98MHZ << MADERA_SYSCLK_FREQ_SHIFT; + default: + return -EINVAL; + } +} + +static int madera_get_legacy_dspclk_setting(struct madera *madera, + unsigned int freq) +{ + switch (freq) { + case 0: + return 0; + case 45158400: + case 49152000: + switch (madera->type) { + case CS47L85: + case WM1840: + if (madera->rev < 3) + return -EINVAL; + else + return MADERA_SYSCLK_49MHZ << + MADERA_SYSCLK_FREQ_SHIFT; + default: + return -EINVAL; + } + case 135475200: + case 147456000: + return MADERA_DSPCLK_147MHZ << MADERA_DSP_CLK_FREQ_LEGACY_SHIFT; + default: + return -EINVAL; + } +} + +static int madera_get_dspclk_setting(struct madera *madera, + unsigned int freq, + unsigned int *clock_2_val) +{ + switch (madera->type) { + case CS47L35: + case CS47L85: + case WM1840: + *clock_2_val = 0; /* don't use MADERA_DSP_CLOCK_2 */ + return madera_get_legacy_dspclk_setting(madera, freq); + default: + if (freq > 150000000) + return -EINVAL; + + /* Use new exact frequency control */ + *clock_2_val = freq / 15625; /* freq * (2^6) / (10^6) */ + return 0; + } +} + +static int madera_set_outclk(struct snd_soc_component *component, + unsigned int source, unsigned int freq) +{ + int div, div_inc, rate; + + switch (source) { + case MADERA_OUTCLK_SYSCLK: + dev_dbg(component->dev, "Configured OUTCLK to SYSCLK\n"); + snd_soc_component_update_bits(component, MADERA_OUTPUT_RATE_1, + MADERA_OUT_CLK_SRC_MASK, source); + return 0; + case MADERA_OUTCLK_ASYNCCLK: + dev_dbg(component->dev, "Configured OUTCLK to ASYNCCLK\n"); + snd_soc_component_update_bits(component, MADERA_OUTPUT_RATE_1, + MADERA_OUT_CLK_SRC_MASK, source); + return 0; + case MADERA_OUTCLK_MCLK1: + case MADERA_OUTCLK_MCLK2: + case MADERA_OUTCLK_MCLK3: + break; + default: + return -EINVAL; + } + + if (freq % 4000) + rate = 5644800; + else + rate = 6144000; + + div = 1; + div_inc = 0; + while (div <= 8) { + if (freq / div == rate && !(freq % div)) { + dev_dbg(component->dev, "Configured %dHz OUTCLK\n", rate); + snd_soc_component_update_bits(component, + MADERA_OUTPUT_RATE_1, + MADERA_OUT_EXT_CLK_DIV_MASK | + MADERA_OUT_CLK_SRC_MASK, + (div_inc << MADERA_OUT_EXT_CLK_DIV_SHIFT) | + source); + return 0; + } + div_inc++; + div *= 2; + } + + dev_err(component->dev, + "Unable to generate %dHz OUTCLK from %dHz MCLK\n", + rate, freq); + return -EINVAL; +} + +int madera_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + char *name; + unsigned int reg, clock_2_val = 0; + unsigned int mask = MADERA_SYSCLK_FREQ_MASK | MADERA_SYSCLK_SRC_MASK; + unsigned int val = source << MADERA_SYSCLK_SRC_SHIFT; + int clk_freq_sel, *clk; + int ret = 0; + + switch (clk_id) { + case MADERA_CLK_SYSCLK_1: + name = "SYSCLK"; + reg = MADERA_SYSTEM_CLOCK_1; + clk = &priv->sysclk; + clk_freq_sel = madera_get_sysclk_setting(freq); + mask |= MADERA_SYSCLK_FRAC; + break; + case MADERA_CLK_ASYNCCLK_1: + name = "ASYNCCLK"; + reg = MADERA_ASYNC_CLOCK_1; + clk = &priv->asyncclk; + clk_freq_sel = madera_get_sysclk_setting(freq); + break; + case MADERA_CLK_DSPCLK: + name = "DSPCLK"; + reg = MADERA_DSP_CLOCK_1; + clk = &priv->dspclk; + clk_freq_sel = madera_get_dspclk_setting(madera, freq, + &clock_2_val); + break; + case MADERA_CLK_OPCLK: + case MADERA_CLK_ASYNC_OPCLK: + return madera_set_opclk(component, clk_id, freq); + case MADERA_CLK_OUTCLK: + return madera_set_outclk(component, source, freq); + default: + return -EINVAL; + } + + if (clk_freq_sel < 0) { + dev_err(madera->dev, + "Failed to get clk setting for %dHZ\n", freq); + return clk_freq_sel; + } + + *clk = freq; + + if (freq == 0) { + dev_dbg(madera->dev, "%s cleared\n", name); + return 0; + } + + val |= clk_freq_sel; + + if (clock_2_val) { + ret = regmap_write(madera->regmap, MADERA_DSP_CLOCK_2, + clock_2_val); + if (ret) { + dev_err(madera->dev, + "Failed to write DSP_CONFIG2: %d\n", ret); + return ret; + } + + /* + * We're using the frequency setting in MADERA_DSP_CLOCK_2 so + * don't change the frequency select bits in MADERA_DSP_CLOCK_1 + */ + mask = MADERA_SYSCLK_SRC_MASK; + } + + if (freq % 6144000) + val |= MADERA_SYSCLK_FRAC; + + dev_dbg(madera->dev, "%s set to %uHz\n", name, freq); + + return regmap_update_bits(madera->regmap, reg, mask, val); +} +EXPORT_SYMBOL_GPL(madera_set_sysclk); + +static int madera_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + int lrclk, bclk, mode, base; + + base = dai->driver->base; + + lrclk = 0; + bclk = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + mode = MADERA_FMT_DSP_MODE_A; + break; + case SND_SOC_DAIFMT_DSP_B: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBM_CFM) { + madera_aif_err(dai, "DSP_B not valid in slave mode\n"); + return -EINVAL; + } + mode = MADERA_FMT_DSP_MODE_B; + break; + case SND_SOC_DAIFMT_I2S: + mode = MADERA_FMT_I2S_MODE; + break; + case SND_SOC_DAIFMT_LEFT_J: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBM_CFM) { + madera_aif_err(dai, "LEFT_J not valid in slave mode\n"); + return -EINVAL; + } + mode = MADERA_FMT_LEFT_JUSTIFIED_MODE; + break; + default: + madera_aif_err(dai, "Unsupported DAI format %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBS_CFM: + lrclk |= MADERA_AIF1TX_LRCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + bclk |= MADERA_AIF1_BCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + bclk |= MADERA_AIF1_BCLK_MSTR; + lrclk |= MADERA_AIF1TX_LRCLK_MSTR; + break; + default: + madera_aif_err(dai, "Unsupported master mode %d\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + bclk |= MADERA_AIF1_BCLK_INV; + lrclk |= MADERA_AIF1TX_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + bclk |= MADERA_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + lrclk |= MADERA_AIF1TX_LRCLK_INV; + break; + default: + madera_aif_err(dai, "Unsupported invert mode %d\n", + fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + regmap_update_bits(madera->regmap, base + MADERA_AIF_BCLK_CTRL, + MADERA_AIF1_BCLK_INV | MADERA_AIF1_BCLK_MSTR, + bclk); + regmap_update_bits(madera->regmap, base + MADERA_AIF_TX_PIN_CTRL, + MADERA_AIF1TX_LRCLK_INV | MADERA_AIF1TX_LRCLK_MSTR, + lrclk); + regmap_update_bits(madera->regmap, base + MADERA_AIF_RX_PIN_CTRL, + MADERA_AIF1RX_LRCLK_INV | MADERA_AIF1RX_LRCLK_MSTR, + lrclk); + regmap_update_bits(madera->regmap, base + MADERA_AIF_FORMAT, + MADERA_AIF1_FMT_MASK, mode); + + return 0; +} + +static const int madera_48k_bclk_rates[] = { + -1, + 48000, + 64000, + 96000, + 128000, + 192000, + 256000, + 384000, + 512000, + 768000, + 1024000, + 1536000, + 2048000, + 3072000, + 4096000, + 6144000, + 8192000, + 12288000, + 24576000, +}; + +static const int madera_44k1_bclk_rates[] = { + -1, + 44100, + 58800, + 88200, + 117600, + 177640, + 235200, + 352800, + 470400, + 705600, + 940800, + 1411200, + 1881600, + 2822400, + 3763200, + 5644800, + 7526400, + 11289600, + 22579200, +}; + +static const unsigned int madera_sr_vals[] = { + 0, + 12000, + 24000, + 48000, + 96000, + 192000, + 384000, + 768000, + 0, + 11025, + 22050, + 44100, + 88200, + 176400, + 352800, + 705600, + 4000, + 8000, + 16000, + 32000, + 64000, + 128000, + 256000, + 512000, +}; + +#define MADERA_192K_48K_RATE_MASK 0x0F003E +#define MADERA_192K_44K1_RATE_MASK 0x003E00 +#define MADERA_192K_RATE_MASK (MADERA_192K_48K_RATE_MASK | \ + MADERA_192K_44K1_RATE_MASK) +#define MADERA_384K_48K_RATE_MASK 0x0F007E +#define MADERA_384K_44K1_RATE_MASK 0x007E00 +#define MADERA_384K_RATE_MASK (MADERA_384K_48K_RATE_MASK | \ + MADERA_384K_44K1_RATE_MASK) + +static const struct snd_pcm_hw_constraint_list madera_constraint = { + .count = ARRAY_SIZE(madera_sr_vals), + .list = madera_sr_vals, +}; + +static int madera_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + struct madera *madera = priv->madera; + unsigned int base_rate; + + if (!substream->runtime) + return 0; + + switch (dai_priv->clk) { + case MADERA_CLK_SYSCLK_1: + case MADERA_CLK_SYSCLK_2: + case MADERA_CLK_SYSCLK_3: + base_rate = priv->sysclk; + break; + case MADERA_CLK_ASYNCCLK_1: + case MADERA_CLK_ASYNCCLK_2: + base_rate = priv->asyncclk; + break; + default: + return 0; + } + + switch (madera->type) { + case CS42L92: + case CS47L92: + case CS47L93: + if (base_rate == 0) + dai_priv->constraint.mask = MADERA_384K_RATE_MASK; + else if (base_rate % 4000) + dai_priv->constraint.mask = MADERA_384K_44K1_RATE_MASK; + else + dai_priv->constraint.mask = MADERA_384K_48K_RATE_MASK; + break; + default: + if (base_rate == 0) + dai_priv->constraint.mask = MADERA_192K_RATE_MASK; + else if (base_rate % 4000) + dai_priv->constraint.mask = MADERA_192K_44K1_RATE_MASK; + else + dai_priv->constraint.mask = MADERA_192K_48K_RATE_MASK; + break; + } + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &dai_priv->constraint); +} + +static int madera_hw_params_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + int base = dai->driver->base; + int i, sr_val; + unsigned int reg, cur, tar; + int ret; + + for (i = 0; i < ARRAY_SIZE(madera_sr_vals); i++) + if (madera_sr_vals[i] == params_rate(params)) + break; + + if (i == ARRAY_SIZE(madera_sr_vals)) { + madera_aif_err(dai, "Unsupported sample rate %dHz\n", + params_rate(params)); + return -EINVAL; + } + sr_val = i; + + switch (dai_priv->clk) { + case MADERA_CLK_SYSCLK_1: + reg = MADERA_SAMPLE_RATE_1; + tar = 0 << MADERA_AIF1_RATE_SHIFT; + break; + case MADERA_CLK_SYSCLK_2: + reg = MADERA_SAMPLE_RATE_2; + tar = 1 << MADERA_AIF1_RATE_SHIFT; + break; + case MADERA_CLK_SYSCLK_3: + reg = MADERA_SAMPLE_RATE_3; + tar = 2 << MADERA_AIF1_RATE_SHIFT; + break; + case MADERA_CLK_ASYNCCLK_1: + reg = MADERA_ASYNC_SAMPLE_RATE_1; + tar = 8 << MADERA_AIF1_RATE_SHIFT; + break; + case MADERA_CLK_ASYNCCLK_2: + reg = MADERA_ASYNC_SAMPLE_RATE_2; + tar = 9 << MADERA_AIF1_RATE_SHIFT; + break; + default: + madera_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); + return -EINVAL; + } + + snd_soc_component_update_bits(component, reg, MADERA_SAMPLE_RATE_1_MASK, + sr_val); + + if (!base) + return 0; + + ret = regmap_read(priv->madera->regmap, + base + MADERA_AIF_RATE_CTRL, &cur); + if (ret != 0) { + madera_aif_err(dai, "Failed to check rate: %d\n", ret); + return ret; + } + + if ((cur & MADERA_AIF1_RATE_MASK) == (tar & MADERA_AIF1_RATE_MASK)) + return 0; + + mutex_lock(&priv->rate_lock); + + if (!madera_can_change_grp_rate(priv, base + MADERA_AIF_RATE_CTRL)) { + madera_aif_warn(dai, "Cannot change rate while active\n"); + ret = -EBUSY; + goto out; + } + + /* Guard the rate change with SYSCLK cycles */ + madera_spin_sysclk(priv); + snd_soc_component_update_bits(component, base + MADERA_AIF_RATE_CTRL, + MADERA_AIF1_RATE_MASK, tar); + madera_spin_sysclk(priv); + +out: + mutex_unlock(&priv->rate_lock); + + return ret; +} + +static int madera_aif_cfg_changed(struct snd_soc_component *component, + int base, int bclk, int lrclk, int frame) +{ + unsigned int val; + + val = snd_soc_component_read(component, base + MADERA_AIF_BCLK_CTRL); + if (bclk != (val & MADERA_AIF1_BCLK_FREQ_MASK)) + return 1; + + val = snd_soc_component_read(component, base + MADERA_AIF_RX_BCLK_RATE); + if (lrclk != (val & MADERA_AIF1RX_BCPF_MASK)) + return 1; + + val = snd_soc_component_read(component, base + MADERA_AIF_FRAME_CTRL_1); + if (frame != (val & (MADERA_AIF1TX_WL_MASK | + MADERA_AIF1TX_SLOT_LEN_MASK))) + return 1; + + return 0; +} + +static int madera_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + int base = dai->driver->base; + const int *rates; + int i, ret; + unsigned int val; + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + unsigned int chan_limit = + madera->pdata.codec.max_channels_clocked[dai->id - 1]; + int tdm_width = priv->tdm_width[dai->id - 1]; + int tdm_slots = priv->tdm_slots[dai->id - 1]; + int bclk, lrclk, wl, frame, bclk_target, num_rates; + int reconfig; + unsigned int aif_tx_state = 0, aif_rx_state = 0; + + if (rate % 4000) { + rates = &madera_44k1_bclk_rates[0]; + num_rates = ARRAY_SIZE(madera_44k1_bclk_rates); + } else { + rates = &madera_48k_bclk_rates[0]; + num_rates = ARRAY_SIZE(madera_48k_bclk_rates); + } + + wl = snd_pcm_format_width(params_format(params)); + + if (tdm_slots) { + madera_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n", + tdm_slots, tdm_width); + bclk_target = tdm_slots * tdm_width * rate; + channels = tdm_slots; + } else { + bclk_target = snd_soc_params_to_bclk(params); + tdm_width = wl; + } + + if (chan_limit && chan_limit < channels) { + madera_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); + bclk_target /= channels; + bclk_target *= chan_limit; + } + + /* Force multiple of 2 channels for I2S mode */ + val = snd_soc_component_read(component, base + MADERA_AIF_FORMAT); + val &= MADERA_AIF1_FMT_MASK; + if ((channels & 1) && val == MADERA_FMT_I2S_MODE) { + madera_aif_dbg(dai, "Forcing stereo mode\n"); + bclk_target /= channels; + bclk_target *= channels + 1; + } + + for (i = 0; i < num_rates; i++) { + if (rates[i] >= bclk_target && rates[i] % rate == 0) { + bclk = i; + break; + } + } + + if (i == num_rates) { + madera_aif_err(dai, "Unsupported sample rate %dHz\n", rate); + return -EINVAL; + } + + lrclk = rates[bclk] / rate; + + madera_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", + rates[bclk], rates[bclk] / lrclk); + + frame = wl << MADERA_AIF1TX_WL_SHIFT | tdm_width; + + reconfig = madera_aif_cfg_changed(component, base, bclk, lrclk, frame); + if (reconfig < 0) + return reconfig; + + if (reconfig) { + /* Save AIF TX/RX state */ + regmap_read(madera->regmap, base + MADERA_AIF_TX_ENABLES, + &aif_tx_state); + regmap_read(madera->regmap, base + MADERA_AIF_RX_ENABLES, + &aif_rx_state); + /* Disable AIF TX/RX before reconfiguring it */ + regmap_update_bits(madera->regmap, + base + MADERA_AIF_TX_ENABLES, 0xff, 0x0); + regmap_update_bits(madera->regmap, + base + MADERA_AIF_RX_ENABLES, 0xff, 0x0); + } + + ret = madera_hw_params_rate(substream, params, dai); + if (ret != 0) + goto restore_aif; + + if (reconfig) { + regmap_update_bits(madera->regmap, + base + MADERA_AIF_BCLK_CTRL, + MADERA_AIF1_BCLK_FREQ_MASK, bclk); + regmap_update_bits(madera->regmap, + base + MADERA_AIF_RX_BCLK_RATE, + MADERA_AIF1RX_BCPF_MASK, lrclk); + regmap_update_bits(madera->regmap, + base + MADERA_AIF_FRAME_CTRL_1, + MADERA_AIF1TX_WL_MASK | + MADERA_AIF1TX_SLOT_LEN_MASK, frame); + regmap_update_bits(madera->regmap, + base + MADERA_AIF_FRAME_CTRL_2, + MADERA_AIF1RX_WL_MASK | + MADERA_AIF1RX_SLOT_LEN_MASK, frame); + } + +restore_aif: + if (reconfig) { + /* Restore AIF TX/RX state */ + regmap_update_bits(madera->regmap, + base + MADERA_AIF_TX_ENABLES, + 0xff, aif_tx_state); + regmap_update_bits(madera->regmap, + base + MADERA_AIF_RX_ENABLES, + 0xff, aif_rx_state); + } + + return ret; +} + +static int madera_is_syncclk(int clk_id) +{ + switch (clk_id) { + case MADERA_CLK_SYSCLK_1: + case MADERA_CLK_SYSCLK_2: + case MADERA_CLK_SYSCLK_3: + return 1; + case MADERA_CLK_ASYNCCLK_1: + case MADERA_CLK_ASYNCCLK_2: + return 0; + default: + return -EINVAL; + } +} + +static int madera_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + struct snd_soc_dapm_route routes[2]; + int is_sync; + + is_sync = madera_is_syncclk(clk_id); + if (is_sync < 0) { + dev_err(component->dev, "Illegal DAI clock id %d\n", clk_id); + return is_sync; + } + + if (is_sync == madera_is_syncclk(dai_priv->clk)) + return 0; + + if (snd_soc_dai_active(dai)) { + dev_err(component->dev, "Can't change clock on active DAI %d\n", + dai->id); + return -EBUSY; + } + + dev_dbg(component->dev, "Setting AIF%d to %s\n", dai->id, + is_sync ? "SYSCLK" : "ASYNCCLK"); + + /* + * A connection to SYSCLK is always required, we only add and remove + * a connection to ASYNCCLK + */ + memset(&routes, 0, sizeof(routes)); + routes[0].sink = dai->driver->capture.stream_name; + routes[1].sink = dai->driver->playback.stream_name; + routes[0].source = "ASYNCCLK"; + routes[1].source = "ASYNCCLK"; + + if (is_sync) + snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes)); + else + snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes)); + + dai_priv->clk = clk_id; + + return snd_soc_dapm_sync(dapm); +} + +static int madera_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_component *component = dai->component; + int base = dai->driver->base; + unsigned int reg; + int ret; + + if (tristate) + reg = MADERA_AIF1_TRI; + else + reg = 0; + + ret = snd_soc_component_update_bits(component, + base + MADERA_AIF_RATE_CTRL, + MADERA_AIF1_TRI, reg); + if (ret < 0) + return ret; + else + return 0; +} + +static void madera_set_channels_to_mask(struct snd_soc_dai *dai, + unsigned int base, + int channels, unsigned int mask) +{ + struct snd_soc_component *component = dai->component; + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + int slot, i; + + for (i = 0; i < channels; ++i) { + slot = ffs(mask) - 1; + if (slot < 0) + return; + + regmap_write(madera->regmap, base + i, slot); + + mask &= ~(1 << slot); + } + + if (mask) + madera_aif_warn(dai, "Too many channels in TDM mask\n"); +} + +static int madera_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + int base = dai->driver->base; + int rx_max_chan = dai->driver->playback.channels_max; + int tx_max_chan = dai->driver->capture.channels_max; + + /* Only support TDM for the physical AIFs */ + if (dai->id > MADERA_MAX_AIF) + return -ENOTSUPP; + + if (slots == 0) { + tx_mask = (1 << tx_max_chan) - 1; + rx_mask = (1 << rx_max_chan) - 1; + } + + madera_set_channels_to_mask(dai, base + MADERA_AIF_FRAME_CTRL_3, + tx_max_chan, tx_mask); + madera_set_channels_to_mask(dai, base + MADERA_AIF_FRAME_CTRL_11, + rx_max_chan, rx_mask); + + priv->tdm_width[dai->id - 1] = slot_width; + priv->tdm_slots[dai->id - 1] = slots; + + return 0; +} + +const struct snd_soc_dai_ops madera_dai_ops = { + .startup = &madera_startup, + .set_fmt = &madera_set_fmt, + .set_tdm_slot = &madera_set_tdm_slot, + .hw_params = &madera_hw_params, + .set_sysclk = &madera_dai_set_sysclk, + .set_tristate = &madera_set_tristate, +}; +EXPORT_SYMBOL_GPL(madera_dai_ops); + +const struct snd_soc_dai_ops madera_simple_dai_ops = { + .startup = &madera_startup, + .hw_params = &madera_hw_params_rate, + .set_sysclk = &madera_dai_set_sysclk, +}; +EXPORT_SYMBOL_GPL(madera_simple_dai_ops); + +int madera_init_dai(struct madera_priv *priv, int id) +{ + struct madera_dai_priv *dai_priv = &priv->dai[id]; + + dai_priv->clk = MADERA_CLK_SYSCLK_1; + dai_priv->constraint = madera_constraint; + + return 0; +} +EXPORT_SYMBOL_GPL(madera_init_dai); + +static const struct { + unsigned int min; + unsigned int max; + u16 fratio; + int ratio; +} fll_sync_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +static const unsigned int pseudo_fref_max[MADERA_FLL_MAX_FRATIO] = { + 13500000, + 6144000, + 6144000, + 3072000, + 3072000, + 2822400, + 2822400, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 1536000, + 768000, +}; + +struct madera_fll_gains { + unsigned int min; + unsigned int max; + int gain; /* main gain */ + int alt_gain; /* alternate integer gain */ +}; + +static const struct madera_fll_gains madera_fll_sync_gains[] = { + { 0, 256000, 0, -1 }, + { 256000, 1000000, 2, -1 }, + { 1000000, 13500000, 4, -1 }, +}; + +static const struct madera_fll_gains madera_fll_main_gains[] = { + { 0, 100000, 0, 2 }, + { 100000, 375000, 2, 2 }, + { 375000, 768000, 3, 2 }, + { 768001, 1500000, 3, 3 }, + { 1500000, 6000000, 4, 3 }, + { 6000000, 13500000, 5, 3 }, +}; + +static int madera_find_sync_fratio(unsigned int fref, int *fratio) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fll_sync_fratios); i++) { + if (fll_sync_fratios[i].min <= fref && + fref <= fll_sync_fratios[i].max) { + if (fratio) + *fratio = fll_sync_fratios[i].fratio; + + return fll_sync_fratios[i].ratio; + } + } + + return -EINVAL; +} + +static int madera_find_main_fratio(unsigned int fref, unsigned int fout, + int *fratio) +{ + int ratio = 1; + + while ((fout / (ratio * fref)) > MADERA_FLL_MAX_N) + ratio++; + + if (fratio) + *fratio = ratio - 1; + + return ratio; +} + +static int madera_find_fratio(struct madera_fll *fll, unsigned int fref, + bool sync, int *fratio) +{ + switch (fll->madera->type) { + case CS47L35: + switch (fll->madera->rev) { + case 0: + /* rev A0 uses sync calculation for both loops */ + return madera_find_sync_fratio(fref, fratio); + default: + if (sync) + return madera_find_sync_fratio(fref, fratio); + else + return madera_find_main_fratio(fref, + fll->fout, + fratio); + } + break; + case CS47L85: + case WM1840: + /* these use the same calculation for main and sync loops */ + return madera_find_sync_fratio(fref, fratio); + default: + if (sync) + return madera_find_sync_fratio(fref, fratio); + else + return madera_find_main_fratio(fref, fll->fout, fratio); + } +} + +static int madera_calc_fratio(struct madera_fll *fll, + struct madera_fll_cfg *cfg, + unsigned int fref, bool sync) +{ + int init_ratio, ratio; + int refdiv, div; + + /* fref must be <=13.5MHz, find initial refdiv */ + div = 1; + cfg->refdiv = 0; + while (fref > MADERA_FLL_MAX_FREF) { + div *= 2; + fref /= 2; + cfg->refdiv++; + + if (div > MADERA_FLL_MAX_REFDIV) + return -EINVAL; + } + + /* Find an appropriate FLL_FRATIO */ + init_ratio = madera_find_fratio(fll, fref, sync, &cfg->fratio); + if (init_ratio < 0) { + madera_fll_err(fll, "Unable to find FRATIO for fref=%uHz\n", + fref); + return init_ratio; + } + + if (!sync) + cfg->fratio = init_ratio - 1; + + switch (fll->madera->type) { + case CS47L35: + switch (fll->madera->rev) { + case 0: + if (sync) + return init_ratio; + break; + default: + return init_ratio; + } + break; + case CS47L85: + case WM1840: + if (sync) + return init_ratio; + break; + default: + return init_ratio; + } + + /* + * For CS47L35 rev A0, CS47L85 and WM1840 adjust FRATIO/refdiv to avoid + * integer mode if possible + */ + refdiv = cfg->refdiv; + + while (div <= MADERA_FLL_MAX_REFDIV) { + /* + * start from init_ratio because this may already give a + * fractional N.K + */ + for (ratio = init_ratio; ratio > 0; ratio--) { + if (fll->fout % (ratio * fref)) { + cfg->refdiv = refdiv; + cfg->fratio = ratio - 1; + return ratio; + } + } + + for (ratio = init_ratio + 1; ratio <= MADERA_FLL_MAX_FRATIO; + ratio++) { + if ((MADERA_FLL_VCO_CORNER / 2) / + (MADERA_FLL_VCO_MULT * ratio) < fref) + break; + + if (fref > pseudo_fref_max[ratio - 1]) + break; + + if (fll->fout % (ratio * fref)) { + cfg->refdiv = refdiv; + cfg->fratio = ratio - 1; + return ratio; + } + } + + div *= 2; + fref /= 2; + refdiv++; + init_ratio = madera_find_fratio(fll, fref, sync, NULL); + } + + madera_fll_warn(fll, "Falling back to integer mode operation\n"); + + return cfg->fratio + 1; +} + +static int madera_find_fll_gain(struct madera_fll *fll, + struct madera_fll_cfg *cfg, + unsigned int fref, + const struct madera_fll_gains *gains, + int n_gains) +{ + int i; + + for (i = 0; i < n_gains; i++) { + if (gains[i].min <= fref && fref <= gains[i].max) { + cfg->gain = gains[i].gain; + cfg->alt_gain = gains[i].alt_gain; + return 0; + } + } + + madera_fll_err(fll, "Unable to find gain for fref=%uHz\n", fref); + + return -EINVAL; +} + +static int madera_calc_fll(struct madera_fll *fll, + struct madera_fll_cfg *cfg, + unsigned int fref, bool sync) +{ + unsigned int gcd_fll; + const struct madera_fll_gains *gains; + int n_gains; + int ratio, ret; + + madera_fll_dbg(fll, "fref=%u Fout=%u fvco=%u\n", + fref, fll->fout, fll->fout * MADERA_FLL_VCO_MULT); + + /* Find an appropriate FLL_FRATIO and refdiv */ + ratio = madera_calc_fratio(fll, cfg, fref, sync); + if (ratio < 0) + return ratio; + + /* Apply the division for our remaining calculations */ + fref = fref / (1 << cfg->refdiv); + + cfg->n = fll->fout / (ratio * fref); + + if (fll->fout % (ratio * fref)) { + gcd_fll = gcd(fll->fout, ratio * fref); + madera_fll_dbg(fll, "GCD=%u\n", gcd_fll); + + cfg->theta = (fll->fout - (cfg->n * ratio * fref)) + / gcd_fll; + cfg->lambda = (ratio * fref) / gcd_fll; + } else { + cfg->theta = 0; + cfg->lambda = 0; + } + + /* + * Round down to 16bit range with cost of accuracy lost. + * Denominator must be bigger than numerator so we only + * take care of it. + */ + while (cfg->lambda >= (1 << 16)) { + cfg->theta >>= 1; + cfg->lambda >>= 1; + } + + switch (fll->madera->type) { + case CS47L35: + switch (fll->madera->rev) { + case 0: + /* Rev A0 uses the sync gains for both loops */ + gains = madera_fll_sync_gains; + n_gains = ARRAY_SIZE(madera_fll_sync_gains); + break; + default: + if (sync) { + gains = madera_fll_sync_gains; + n_gains = ARRAY_SIZE(madera_fll_sync_gains); + } else { + gains = madera_fll_main_gains; + n_gains = ARRAY_SIZE(madera_fll_main_gains); + } + break; + } + break; + case CS47L85: + case WM1840: + /* These use the sync gains for both loops */ + gains = madera_fll_sync_gains; + n_gains = ARRAY_SIZE(madera_fll_sync_gains); + break; + default: + if (sync) { + gains = madera_fll_sync_gains; + n_gains = ARRAY_SIZE(madera_fll_sync_gains); + } else { + gains = madera_fll_main_gains; + n_gains = ARRAY_SIZE(madera_fll_main_gains); + } + break; + } + + ret = madera_find_fll_gain(fll, cfg, fref, gains, n_gains); + if (ret) + return ret; + + madera_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n", + cfg->n, cfg->theta, cfg->lambda); + madera_fll_dbg(fll, "FRATIO=0x%x(%d) REFCLK_DIV=0x%x(%d)\n", + cfg->fratio, ratio, cfg->refdiv, 1 << cfg->refdiv); + madera_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain); + + return 0; +} + +static bool madera_write_fll(struct madera *madera, unsigned int base, + struct madera_fll_cfg *cfg, int source, + bool sync, int gain) +{ + bool change, fll_change; + + fll_change = false; + regmap_update_bits_check(madera->regmap, + base + MADERA_FLL_CONTROL_3_OFFS, + MADERA_FLL1_THETA_MASK, + cfg->theta, &change); + fll_change |= change; + regmap_update_bits_check(madera->regmap, + base + MADERA_FLL_CONTROL_4_OFFS, + MADERA_FLL1_LAMBDA_MASK, + cfg->lambda, &change); + fll_change |= change; + regmap_update_bits_check(madera->regmap, + base + MADERA_FLL_CONTROL_5_OFFS, + MADERA_FLL1_FRATIO_MASK, + cfg->fratio << MADERA_FLL1_FRATIO_SHIFT, + &change); + fll_change |= change; + regmap_update_bits_check(madera->regmap, + base + MADERA_FLL_CONTROL_6_OFFS, + MADERA_FLL1_REFCLK_DIV_MASK | + MADERA_FLL1_REFCLK_SRC_MASK, + cfg->refdiv << MADERA_FLL1_REFCLK_DIV_SHIFT | + source << MADERA_FLL1_REFCLK_SRC_SHIFT, + &change); + fll_change |= change; + + if (sync) { + regmap_update_bits_check(madera->regmap, + base + MADERA_FLL_SYNCHRONISER_7_OFFS, + MADERA_FLL1_GAIN_MASK, + gain << MADERA_FLL1_GAIN_SHIFT, + &change); + fll_change |= change; + } else { + regmap_update_bits_check(madera->regmap, + base + MADERA_FLL_CONTROL_7_OFFS, + MADERA_FLL1_GAIN_MASK, + gain << MADERA_FLL1_GAIN_SHIFT, + &change); + fll_change |= change; + } + + regmap_update_bits_check(madera->regmap, + base + MADERA_FLL_CONTROL_2_OFFS, + MADERA_FLL1_CTRL_UPD | MADERA_FLL1_N_MASK, + MADERA_FLL1_CTRL_UPD | cfg->n, &change); + fll_change |= change; + + return fll_change; +} + +static int madera_is_enabled_fll(struct madera_fll *fll, int base) +{ + struct madera *madera = fll->madera; + unsigned int reg; + int ret; + + ret = regmap_read(madera->regmap, + base + MADERA_FLL_CONTROL_1_OFFS, ®); + if (ret != 0) { + madera_fll_err(fll, "Failed to read current state: %d\n", ret); + return ret; + } + + return reg & MADERA_FLL1_ENA; +} + +static int madera_wait_for_fll(struct madera_fll *fll, bool requested) +{ + struct madera *madera = fll->madera; + unsigned int val = 0; + bool status; + int i; + + madera_fll_dbg(fll, "Waiting for FLL...\n"); + + for (i = 0; i < 30; i++) { + regmap_read(madera->regmap, MADERA_IRQ1_RAW_STATUS_2, &val); + status = val & (MADERA_FLL1_LOCK_STS1 << (fll->id - 1)); + if (status == requested) + return 0; + + switch (i) { + case 0 ... 5: + usleep_range(75, 125); + break; + case 11 ... 20: + usleep_range(750, 1250); + break; + default: + msleep(20); + break; + } + } + + madera_fll_warn(fll, "Timed out waiting for lock\n"); + + return -ETIMEDOUT; +} + +static bool madera_set_fll_phase_integrator(struct madera_fll *fll, + struct madera_fll_cfg *ref_cfg, + bool sync) +{ + unsigned int val; + bool reg_change; + + if (!sync && ref_cfg->theta == 0) + val = (1 << MADERA_FLL1_PHASE_ENA_SHIFT) | + (2 << MADERA_FLL1_PHASE_GAIN_SHIFT); + else + val = 2 << MADERA_FLL1_PHASE_GAIN_SHIFT; + + regmap_update_bits_check(fll->madera->regmap, + fll->base + MADERA_FLL_EFS_2_OFFS, + MADERA_FLL1_PHASE_ENA_MASK | + MADERA_FLL1_PHASE_GAIN_MASK, + val, ®_change); + + return reg_change; +} + +static int madera_set_fll_clks_reg(struct madera_fll *fll, bool ena, + unsigned int reg, unsigned int mask, + unsigned int shift) +{ + struct madera *madera = fll->madera; + unsigned int src; + struct clk *clk; + int ret; + + ret = regmap_read(madera->regmap, reg, &src); + if (ret != 0) { + madera_fll_err(fll, "Failed to read current source: %d\n", + ret); + return ret; + } + + src = (src & mask) >> shift; + + switch (src) { + case MADERA_FLL_SRC_MCLK1: + clk = madera->mclk[MADERA_MCLK1].clk; + break; + case MADERA_FLL_SRC_MCLK2: + clk = madera->mclk[MADERA_MCLK2].clk; + break; + case MADERA_FLL_SRC_MCLK3: + clk = madera->mclk[MADERA_MCLK3].clk; + break; + default: + return 0; + } + + if (ena) { + return clk_prepare_enable(clk); + } else { + clk_disable_unprepare(clk); + return 0; + } +} + +static inline int madera_set_fll_clks(struct madera_fll *fll, int base, bool ena) +{ + return madera_set_fll_clks_reg(fll, ena, + base + MADERA_FLL_CONTROL_6_OFFS, + MADERA_FLL1_REFCLK_SRC_MASK, + MADERA_FLL1_REFCLK_DIV_SHIFT); +} + +static inline int madera_set_fllao_clks(struct madera_fll *fll, int base, bool ena) +{ + return madera_set_fll_clks_reg(fll, ena, + base + MADERA_FLLAO_CONTROL_6_OFFS, + MADERA_FLL_AO_REFCLK_SRC_MASK, + MADERA_FLL_AO_REFCLK_SRC_SHIFT); +} + +static inline int madera_set_fllhj_clks(struct madera_fll *fll, int base, bool ena) +{ + return madera_set_fll_clks_reg(fll, ena, + base + MADERA_FLL_CONTROL_1_OFFS, + CS47L92_FLL1_REFCLK_SRC_MASK, + CS47L92_FLL1_REFCLK_SRC_SHIFT); +} + +static void madera_disable_fll(struct madera_fll *fll) +{ + struct madera *madera = fll->madera; + unsigned int sync_base; + bool ref_change, sync_change; + + switch (madera->type) { + case CS47L35: + sync_base = fll->base + CS47L35_FLL_SYNCHRONISER_OFFS; + break; + default: + sync_base = fll->base + MADERA_FLL_SYNCHRONISER_OFFS; + break; + } + + madera_fll_dbg(fll, "Disabling FLL\n"); + + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_FREERUN, MADERA_FLL1_FREERUN); + regmap_update_bits_check(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_ENA, 0, &ref_change); + regmap_update_bits_check(madera->regmap, + sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS, + MADERA_FLL1_SYNC_ENA, 0, &sync_change); + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_FREERUN, 0); + + madera_wait_for_fll(fll, false); + + if (sync_change) + madera_set_fll_clks(fll, sync_base, false); + + if (ref_change) { + madera_set_fll_clks(fll, fll->base, false); + pm_runtime_put_autosuspend(madera->dev); + } +} + +static int madera_enable_fll(struct madera_fll *fll) +{ + struct madera *madera = fll->madera; + bool have_sync = false; + int already_enabled = madera_is_enabled_fll(fll, fll->base); + int sync_enabled; + struct madera_fll_cfg cfg; + unsigned int sync_base; + int gain, ret; + bool fll_change = false; + + if (already_enabled < 0) + return already_enabled; /* error getting current state */ + + if (fll->ref_src < 0 || fll->ref_freq == 0) { + madera_fll_err(fll, "No REFCLK\n"); + ret = -EINVAL; + goto err; + } + + madera_fll_dbg(fll, "Enabling FLL, initially %s\n", + already_enabled ? "enabled" : "disabled"); + + if (fll->fout < MADERA_FLL_MIN_FOUT || + fll->fout > MADERA_FLL_MAX_FOUT) { + madera_fll_err(fll, "invalid fout %uHz\n", fll->fout); + ret = -EINVAL; + goto err; + } + + switch (madera->type) { + case CS47L35: + sync_base = fll->base + CS47L35_FLL_SYNCHRONISER_OFFS; + break; + default: + sync_base = fll->base + MADERA_FLL_SYNCHRONISER_OFFS; + break; + } + + sync_enabled = madera_is_enabled_fll(fll, sync_base); + if (sync_enabled < 0) + return sync_enabled; + + if (already_enabled) { + /* Facilitate smooth refclk across the transition */ + regmap_update_bits(fll->madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_FREERUN, + MADERA_FLL1_FREERUN); + udelay(32); + regmap_update_bits(fll->madera->regmap, + fll->base + MADERA_FLL_CONTROL_7_OFFS, + MADERA_FLL1_GAIN_MASK, 0); + + if (sync_enabled > 0) + madera_set_fll_clks(fll, sync_base, false); + madera_set_fll_clks(fll, fll->base, false); + } + + /* Apply SYNCCLK setting */ + if (fll->sync_src >= 0) { + ret = madera_calc_fll(fll, &cfg, fll->sync_freq, true); + if (ret < 0) + goto err; + + fll_change |= madera_write_fll(madera, sync_base, + &cfg, fll->sync_src, + true, cfg.gain); + have_sync = true; + } + + if (already_enabled && !!sync_enabled != have_sync) + madera_fll_warn(fll, "Synchroniser changed on active FLL\n"); + + /* Apply REFCLK setting */ + ret = madera_calc_fll(fll, &cfg, fll->ref_freq, false); + if (ret < 0) + goto err; + + /* Ref path hardcodes lambda to 65536 when sync is on */ + if (have_sync && cfg.lambda) + cfg.theta = (cfg.theta * (1 << 16)) / cfg.lambda; + + switch (fll->madera->type) { + case CS47L35: + switch (fll->madera->rev) { + case 0: + gain = cfg.gain; + break; + default: + fll_change |= + madera_set_fll_phase_integrator(fll, &cfg, + have_sync); + if (!have_sync && cfg.theta == 0) + gain = cfg.alt_gain; + else + gain = cfg.gain; + break; + } + break; + case CS47L85: + case WM1840: + gain = cfg.gain; + break; + default: + fll_change |= madera_set_fll_phase_integrator(fll, &cfg, + have_sync); + if (!have_sync && cfg.theta == 0) + gain = cfg.alt_gain; + else + gain = cfg.gain; + break; + } + + fll_change |= madera_write_fll(madera, fll->base, + &cfg, fll->ref_src, + false, gain); + + /* + * Increase the bandwidth if we're not using a low frequency + * sync source. + */ + if (have_sync && fll->sync_freq > 100000) + regmap_update_bits(madera->regmap, + sync_base + MADERA_FLL_SYNCHRONISER_7_OFFS, + MADERA_FLL1_SYNC_DFSAT_MASK, 0); + else + regmap_update_bits(madera->regmap, + sync_base + MADERA_FLL_SYNCHRONISER_7_OFFS, + MADERA_FLL1_SYNC_DFSAT_MASK, + MADERA_FLL1_SYNC_DFSAT); + + if (!already_enabled) + pm_runtime_get_sync(madera->dev); + + if (have_sync) { + madera_set_fll_clks(fll, sync_base, true); + regmap_update_bits(madera->regmap, + sync_base + MADERA_FLL_SYNCHRONISER_1_OFFS, + MADERA_FLL1_SYNC_ENA, + MADERA_FLL1_SYNC_ENA); + } + + madera_set_fll_clks(fll, fll->base, true); + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_ENA, MADERA_FLL1_ENA); + + if (already_enabled) + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_FREERUN, 0); + + if (fll_change || !already_enabled) + madera_wait_for_fll(fll, true); + + return 0; + +err: + /* In case of error don't leave the FLL running with an old config */ + madera_disable_fll(fll); + + return ret; +} + +static int madera_apply_fll(struct madera_fll *fll) +{ + if (fll->fout) { + return madera_enable_fll(fll); + } else { + madera_disable_fll(fll); + return 0; + } +} + +int madera_set_fll_syncclk(struct madera_fll *fll, int source, + unsigned int fref, unsigned int fout) +{ + /* + * fout is ignored, since the synchronizer is an optional extra + * constraint on the Fout generated from REFCLK, so the Fout is + * set when configuring REFCLK + */ + + if (fll->sync_src == source && fll->sync_freq == fref) + return 0; + + fll->sync_src = source; + fll->sync_freq = fref; + + return madera_apply_fll(fll); +} +EXPORT_SYMBOL_GPL(madera_set_fll_syncclk); + +int madera_set_fll_refclk(struct madera_fll *fll, int source, + unsigned int fref, unsigned int fout) +{ + int ret; + + if (fll->ref_src == source && + fll->ref_freq == fref && fll->fout == fout) + return 0; + + /* + * Changes of fout on an enabled FLL aren't allowed except when + * setting fout==0 to disable the FLL + */ + if (fout && fout != fll->fout) { + ret = madera_is_enabled_fll(fll, fll->base); + if (ret < 0) + return ret; + + if (ret) { + madera_fll_err(fll, "Can't change Fout on active FLL\n"); + return -EBUSY; + } + } + + fll->ref_src = source; + fll->ref_freq = fref; + fll->fout = fout; + + return madera_apply_fll(fll); +} +EXPORT_SYMBOL_GPL(madera_set_fll_refclk); + +int madera_init_fll(struct madera *madera, int id, int base, + struct madera_fll *fll) +{ + fll->id = id; + fll->base = base; + fll->madera = madera; + fll->ref_src = MADERA_FLL_SRC_NONE; + fll->sync_src = MADERA_FLL_SRC_NONE; + + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_FREERUN, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(madera_init_fll); + +static const struct reg_sequence madera_fll_ao_32K_49M_patch[] = { + { MADERA_FLLAO_CONTROL_2, 0x02EE }, + { MADERA_FLLAO_CONTROL_3, 0x0000 }, + { MADERA_FLLAO_CONTROL_4, 0x0001 }, + { MADERA_FLLAO_CONTROL_5, 0x0002 }, + { MADERA_FLLAO_CONTROL_6, 0x8001 }, + { MADERA_FLLAO_CONTROL_7, 0x0004 }, + { MADERA_FLLAO_CONTROL_8, 0x0077 }, + { MADERA_FLLAO_CONTROL_10, 0x06D8 }, + { MADERA_FLLAO_CONTROL_11, 0x0085 }, + { MADERA_FLLAO_CONTROL_2, 0x82EE }, +}; + +static const struct reg_sequence madera_fll_ao_32K_45M_patch[] = { + { MADERA_FLLAO_CONTROL_2, 0x02B1 }, + { MADERA_FLLAO_CONTROL_3, 0x0001 }, + { MADERA_FLLAO_CONTROL_4, 0x0010 }, + { MADERA_FLLAO_CONTROL_5, 0x0002 }, + { MADERA_FLLAO_CONTROL_6, 0x8001 }, + { MADERA_FLLAO_CONTROL_7, 0x0004 }, + { MADERA_FLLAO_CONTROL_8, 0x0077 }, + { MADERA_FLLAO_CONTROL_10, 0x06D8 }, + { MADERA_FLLAO_CONTROL_11, 0x0005 }, + { MADERA_FLLAO_CONTROL_2, 0x82B1 }, +}; + +struct madera_fllao_patch { + unsigned int fin; + unsigned int fout; + const struct reg_sequence *patch; + unsigned int patch_size; +}; + +static const struct madera_fllao_patch madera_fllao_settings[] = { + { + .fin = 32768, + .fout = 49152000, + .patch = madera_fll_ao_32K_49M_patch, + .patch_size = ARRAY_SIZE(madera_fll_ao_32K_49M_patch), + + }, + { + .fin = 32768, + .fout = 45158400, + .patch = madera_fll_ao_32K_45M_patch, + .patch_size = ARRAY_SIZE(madera_fll_ao_32K_45M_patch), + }, +}; + +static int madera_enable_fll_ao(struct madera_fll *fll, + const struct reg_sequence *patch, + unsigned int patch_size) +{ + struct madera *madera = fll->madera; + int already_enabled = madera_is_enabled_fll(fll, fll->base); + unsigned int val; + int i; + + if (already_enabled < 0) + return already_enabled; + + if (!already_enabled) + pm_runtime_get_sync(madera->dev); + + madera_fll_dbg(fll, "Enabling FLL_AO, initially %s\n", + already_enabled ? "enabled" : "disabled"); + + /* FLL_AO_HOLD must be set before configuring any registers */ + regmap_update_bits(fll->madera->regmap, + fll->base + MADERA_FLLAO_CONTROL_1_OFFS, + MADERA_FLL_AO_HOLD, MADERA_FLL_AO_HOLD); + + if (already_enabled) + madera_set_fllao_clks(fll, fll->base, false); + + for (i = 0; i < patch_size; i++) { + val = patch[i].def; + + /* modify the patch to apply fll->ref_src as input clock */ + if (patch[i].reg == MADERA_FLLAO_CONTROL_6) { + val &= ~MADERA_FLL_AO_REFCLK_SRC_MASK; + val |= (fll->ref_src << MADERA_FLL_AO_REFCLK_SRC_SHIFT) + & MADERA_FLL_AO_REFCLK_SRC_MASK; + } + + regmap_write(madera->regmap, patch[i].reg, val); + } + + madera_set_fllao_clks(fll, fll->base, true); + + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLLAO_CONTROL_1_OFFS, + MADERA_FLL_AO_ENA, MADERA_FLL_AO_ENA); + + /* Release the hold so that fll_ao locks to external frequency */ + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLLAO_CONTROL_1_OFFS, + MADERA_FLL_AO_HOLD, 0); + + if (!already_enabled) + madera_wait_for_fll(fll, true); + + return 0; +} + +static int madera_disable_fll_ao(struct madera_fll *fll) +{ + struct madera *madera = fll->madera; + bool change; + + madera_fll_dbg(fll, "Disabling FLL_AO\n"); + + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLLAO_CONTROL_1_OFFS, + MADERA_FLL_AO_HOLD, MADERA_FLL_AO_HOLD); + regmap_update_bits_check(madera->regmap, + fll->base + MADERA_FLLAO_CONTROL_1_OFFS, + MADERA_FLL_AO_ENA, 0, &change); + + madera_wait_for_fll(fll, false); + + /* + * ctrl_up gates the writes to all fll_ao register, setting it to 0 + * here ensures that after a runtime suspend/resume cycle when one + * enables the fllao then ctrl_up is the last bit that is configured + * by the fllao enable code rather than the cache sync operation which + * would have updated it much earlier before writing out all fllao + * registers + */ + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLLAO_CONTROL_2_OFFS, + MADERA_FLL_AO_CTRL_UPD_MASK, 0); + + if (change) { + madera_set_fllao_clks(fll, fll->base, false); + pm_runtime_put_autosuspend(madera->dev); + } + + return 0; +} + +int madera_set_fll_ao_refclk(struct madera_fll *fll, int source, + unsigned int fin, unsigned int fout) +{ + int ret = 0; + const struct reg_sequence *patch = NULL; + int patch_size = 0; + unsigned int i; + + if (fll->ref_src == source && + fll->ref_freq == fin && fll->fout == fout) + return 0; + + madera_fll_dbg(fll, "Change FLL_AO refclk to fin=%u fout=%u source=%d\n", + fin, fout, source); + + if (fout && (fll->ref_freq != fin || fll->fout != fout)) { + for (i = 0; i < ARRAY_SIZE(madera_fllao_settings); i++) { + if (madera_fllao_settings[i].fin == fin && + madera_fllao_settings[i].fout == fout) + break; + } + + if (i == ARRAY_SIZE(madera_fllao_settings)) { + madera_fll_err(fll, + "No matching configuration for FLL_AO\n"); + return -EINVAL; + } + + patch = madera_fllao_settings[i].patch; + patch_size = madera_fllao_settings[i].patch_size; + } + + fll->ref_src = source; + fll->ref_freq = fin; + fll->fout = fout; + + if (fout) + ret = madera_enable_fll_ao(fll, patch, patch_size); + else + madera_disable_fll_ao(fll); + + return ret; +} +EXPORT_SYMBOL_GPL(madera_set_fll_ao_refclk); + +static int madera_fllhj_disable(struct madera_fll *fll) +{ + struct madera *madera = fll->madera; + bool change; + + madera_fll_dbg(fll, "Disabling FLL\n"); + + /* Disable lockdet, but don't set ctrl_upd update but. This allows the + * lock status bit to clear as normal, but should the FLL be enabled + * again due to a control clock being required, the lock won't re-assert + * as the FLL config registers are automatically applied when the FLL + * enables. + */ + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_11_OFFS, + MADERA_FLL1_LOCKDET_MASK, 0); + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_HOLD_MASK, MADERA_FLL1_HOLD_MASK); + regmap_update_bits_check(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_ENA_MASK, 0, &change); + + madera_wait_for_fll(fll, false); + + /* ctrl_up gates the writes to all the fll's registers, setting it to 0 + * here ensures that after a runtime suspend/resume cycle when one + * enables the fll then ctrl_up is the last bit that is configured + * by the fll enable code rather than the cache sync operation which + * would have updated it much earlier before writing out all fll + * registers + */ + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_2_OFFS, + MADERA_FLL1_CTRL_UPD_MASK, 0); + + if (change) { + madera_set_fllhj_clks(fll, fll->base, false); + pm_runtime_put_autosuspend(madera->dev); + } + + return 0; +} + +static int madera_fllhj_apply(struct madera_fll *fll, int fin) +{ + struct madera *madera = fll->madera; + int refdiv, fref, fout, lockdet_thr, fbdiv, hp, fast_clk, fllgcd; + bool frac = false; + unsigned int fll_n, min_n, max_n, ratio, theta, lambda; + unsigned int gains, val, num; + + madera_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout); + + for (refdiv = 0; refdiv < 4; refdiv++) + if ((fin / (1 << refdiv)) <= MADERA_FLLHJ_MAX_THRESH) + break; + + fref = fin / (1 << refdiv); + + /* Use simple heuristic approach to find a configuration that + * should work for most input clocks. + */ + fast_clk = 0; + fout = fll->fout; + frac = fout % fref; + + if (fref < MADERA_FLLHJ_LOW_THRESH) { + lockdet_thr = 2; + gains = MADERA_FLLHJ_LOW_GAINS; + if (frac) + fbdiv = 256; + else + fbdiv = 4; + } else if (fref < MADERA_FLLHJ_MID_THRESH) { + lockdet_thr = 8; + gains = MADERA_FLLHJ_MID_GAINS; + fbdiv = 1; + } else { + lockdet_thr = 8; + gains = MADERA_FLLHJ_HIGH_GAINS; + fbdiv = 1; + /* For high speed input clocks, enable 300MHz fast oscillator + * when we're in fractional divider mode. + */ + if (frac) { + fast_clk = 0x3; + fout = fll->fout * 6; + } + } + /* Use high performance mode for fractional configurations. */ + if (frac) { + hp = 0x3; + min_n = MADERA_FLLHJ_FRAC_MIN_N; + max_n = MADERA_FLLHJ_FRAC_MAX_N; + } else { + hp = 0x0; + min_n = MADERA_FLLHJ_INT_MIN_N; + max_n = MADERA_FLLHJ_INT_MAX_N; + } + + ratio = fout / fref; + + madera_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n", + refdiv, fref, frac); + + while (ratio / fbdiv < min_n) { + fbdiv /= 2; + if (fbdiv < 1) { + madera_fll_err(fll, "FBDIV (%d) must be >= 1\n", fbdiv); + return -EINVAL; + } + } + while (frac && (ratio / fbdiv > max_n)) { + fbdiv *= 2; + if (fbdiv >= 1024) { + madera_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv); + return -EINVAL; + } + } + + madera_fll_dbg(fll, "lockdet=%d, hp=0x%x, fbdiv:%d\n", + lockdet_thr, hp, fbdiv); + + /* Calculate N.K values */ + fllgcd = gcd(fout, fbdiv * fref); + num = fout / fllgcd; + lambda = (fref * fbdiv) / fllgcd; + fll_n = num / lambda; + theta = num % lambda; + + madera_fll_dbg(fll, "fll_n=%d, gcd=%d, theta=%d, lambda=%d\n", + fll_n, fllgcd, theta, lambda); + + /* Some sanity checks before any registers are written. */ + if (fll_n < min_n || fll_n > max_n) { + madera_fll_err(fll, "N not in valid %s mode range %d-%d: %d\n", + frac ? "fractional" : "integer", min_n, max_n, + fll_n); + return -EINVAL; + } + if (fbdiv < 1 || (frac && fbdiv >= 1024) || (!frac && fbdiv >= 256)) { + madera_fll_err(fll, "Invalid fbdiv for %s mode (%u)\n", + frac ? "fractional" : "integer", fbdiv); + return -EINVAL; + } + + /* clear the ctrl_upd bit to guarantee we write to it later. */ + regmap_write(madera->regmap, + fll->base + MADERA_FLL_CONTROL_2_OFFS, + fll_n << MADERA_FLL1_N_SHIFT); + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_3_OFFS, + MADERA_FLL1_THETA_MASK, + theta << MADERA_FLL1_THETA_SHIFT); + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_4_OFFS, + MADERA_FLL1_LAMBDA_MASK, + lambda << MADERA_FLL1_LAMBDA_SHIFT); + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_5_OFFS, + MADERA_FLL1_FB_DIV_MASK, + fbdiv << MADERA_FLL1_FB_DIV_SHIFT); + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_6_OFFS, + MADERA_FLL1_REFCLK_DIV_MASK, + refdiv << MADERA_FLL1_REFCLK_DIV_SHIFT); + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_GAIN_OFFS, + 0xffff, + gains); + val = hp << MADERA_FLL1_HP_SHIFT; + val |= 1 << MADERA_FLL1_PHASEDET_ENA_SHIFT; + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_10_OFFS, + MADERA_FLL1_HP_MASK | MADERA_FLL1_PHASEDET_ENA_MASK, + val); + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_11_OFFS, + MADERA_FLL1_LOCKDET_THR_MASK, + lockdet_thr << MADERA_FLL1_LOCKDET_THR_SHIFT); + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL1_DIGITAL_TEST_1_OFFS, + MADERA_FLL1_SYNC_EFS_ENA_MASK | + MADERA_FLL1_CLK_VCO_FAST_SRC_MASK, + fast_clk); + + return 0; +} + +static int madera_fllhj_enable(struct madera_fll *fll) +{ + struct madera *madera = fll->madera; + int already_enabled = madera_is_enabled_fll(fll, fll->base); + int ret; + + if (already_enabled < 0) + return already_enabled; + + if (!already_enabled) + pm_runtime_get_sync(madera->dev); + + madera_fll_dbg(fll, "Enabling FLL, initially %s\n", + already_enabled ? "enabled" : "disabled"); + + /* FLLn_HOLD must be set before configuring any registers */ + regmap_update_bits(fll->madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_HOLD_MASK, + MADERA_FLL1_HOLD_MASK); + + if (already_enabled) + madera_set_fllhj_clks(fll, fll->base, false); + + /* Apply refclk */ + ret = madera_fllhj_apply(fll, fll->ref_freq); + if (ret) { + madera_fll_err(fll, "Failed to set FLL: %d\n", ret); + goto out; + } + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + CS47L92_FLL1_REFCLK_SRC_MASK, + fll->ref_src << CS47L92_FLL1_REFCLK_SRC_SHIFT); + + madera_set_fllhj_clks(fll, fll->base, true); + + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_ENA_MASK, + MADERA_FLL1_ENA_MASK); + +out: + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_11_OFFS, + MADERA_FLL1_LOCKDET_MASK, + MADERA_FLL1_LOCKDET_MASK); + + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_2_OFFS, + MADERA_FLL1_CTRL_UPD_MASK, + MADERA_FLL1_CTRL_UPD_MASK); + + /* Release the hold so that flln locks to external frequency */ + regmap_update_bits(madera->regmap, + fll->base + MADERA_FLL_CONTROL_1_OFFS, + MADERA_FLL1_HOLD_MASK, + 0); + + if (!already_enabled) + madera_wait_for_fll(fll, true); + + return 0; +} + +static int madera_fllhj_validate(struct madera_fll *fll, + unsigned int ref_in, + unsigned int fout) +{ + if (fout && !ref_in) { + madera_fll_err(fll, "fllout set without valid input clk\n"); + return -EINVAL; + } + + if (fll->fout && fout != fll->fout) { + madera_fll_err(fll, "Can't change output on active FLL\n"); + return -EINVAL; + } + + if (ref_in / MADERA_FLL_MAX_REFDIV > MADERA_FLLHJ_MAX_THRESH) { + madera_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in); + return -EINVAL; + } + + return 0; +} + +int madera_fllhj_set_refclk(struct madera_fll *fll, int source, + unsigned int fin, unsigned int fout) +{ + int ret = 0; + + /* To remain consistent with previous FLLs, we expect fout to be + * provided in the form of the required sysclk rate, which is + * 2x the calculated fll out. + */ + if (fout) + fout /= 2; + + if (fll->ref_src == source && fll->ref_freq == fin && + fll->fout == fout) + return 0; + + if (fin && fout && madera_fllhj_validate(fll, fin, fout)) + return -EINVAL; + + fll->ref_src = source; + fll->ref_freq = fin; + fll->fout = fout; + + if (fout) + ret = madera_fllhj_enable(fll); + else + madera_fllhj_disable(fll); + + return ret; +} +EXPORT_SYMBOL_GPL(madera_fllhj_set_refclk); + +/** + * madera_set_output_mode - Set the mode of the specified output + * + * @component: Device to configure + * @output: Output number + * @differential: True to set the output to differential mode + * + * Some systems use external analogue switches to connect more + * analogue devices to the CODEC than are supported by the device. In + * some systems this requires changing the switched output from single + * ended to differential mode dynamically at runtime, an operation + * supported using this function. + * + * Most systems have a single static configuration and should use + * platform data instead. + */ +int madera_set_output_mode(struct snd_soc_component *component, int output, + bool differential) +{ + unsigned int reg, val; + int ret; + + if (output < 1 || output > MADERA_MAX_OUTPUT) + return -EINVAL; + + reg = MADERA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8; + + if (differential) + val = MADERA_OUT1_MONO; + else + val = 0; + + ret = snd_soc_component_update_bits(component, reg, MADERA_OUT1_MONO, + val); + if (ret < 0) + return ret; + else + return 0; +} +EXPORT_SYMBOL_GPL(madera_set_output_mode); + +static bool madera_eq_filter_unstable(bool mode, __be16 _a, __be16 _b) +{ + s16 a = be16_to_cpu(_a); + s16 b = be16_to_cpu(_b); + + if (!mode) { + return abs(a) >= 4096; + } else { + if (abs(b) >= 4096) + return true; + + return (abs((a << 16) / (4096 - b)) >= 4096 << 4); + } +} + +int madera_eq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + struct soc_bytes *params = (void *)kcontrol->private_value; + unsigned int val; + __be16 *data; + int len; + int ret; + + len = params->num_regs * regmap_get_val_bytes(madera->regmap); + + data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + data[0] &= cpu_to_be16(MADERA_EQ1_B1_MODE); + + if (madera_eq_filter_unstable(!!data[0], data[1], data[2]) || + madera_eq_filter_unstable(true, data[4], data[5]) || + madera_eq_filter_unstable(true, data[8], data[9]) || + madera_eq_filter_unstable(true, data[12], data[13]) || + madera_eq_filter_unstable(false, data[16], data[17])) { + dev_err(madera->dev, "Rejecting unstable EQ coefficients\n"); + ret = -EINVAL; + goto out; + } + + ret = regmap_read(madera->regmap, params->base, &val); + if (ret != 0) + goto out; + + val &= ~MADERA_EQ1_B1_MODE; + data[0] |= cpu_to_be16(val); + + ret = regmap_raw_write(madera->regmap, params->base, data, len); + +out: + kfree(data); + + return ret; +} +EXPORT_SYMBOL_GPL(madera_eq_coeff_put); + +int madera_lhpf_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + __be16 *data = (__be16 *)ucontrol->value.bytes.data; + s16 val = be16_to_cpu(*data); + + if (abs(val) >= 4096) { + dev_err(madera->dev, "Rejecting unstable LHPF coefficients\n"); + return -EINVAL; + } + + return snd_soc_bytes_put(kcontrol, ucontrol); +} +EXPORT_SYMBOL_GPL(madera_lhpf_coeff_put); + +MODULE_SOFTDEP("pre: madera"); +MODULE_DESCRIPTION("ASoC Cirrus Logic Madera codec support"); +MODULE_AUTHOR("Charles Keepax "); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/madera.h b/sound/soc/codecs/madera.h new file mode 100644 index 0000000000..09ad6e9bce --- /dev/null +++ b/sound/soc/codecs/madera.h @@ -0,0 +1,458 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Cirrus Logic Madera class codecs common support + * + * Copyright (C) 2015-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef ASOC_MADERA_H +#define ASOC_MADERA_H + +#include +#include +#include + +#include "wm_adsp.h" + +#define MADERA_FLL1_REFCLK 1 +#define MADERA_FLL2_REFCLK 2 +#define MADERA_FLL3_REFCLK 3 +#define MADERA_FLLAO_REFCLK 4 +#define MADERA_FLL1_SYNCCLK 5 +#define MADERA_FLL2_SYNCCLK 6 +#define MADERA_FLL3_SYNCCLK 7 +#define MADERA_FLLAO_SYNCCLK 8 + +#define MADERA_FLL_SRC_NONE -1 +#define MADERA_FLL_SRC_MCLK1 0 +#define MADERA_FLL_SRC_MCLK2 1 +#define MADERA_FLL_SRC_MCLK3 2 +#define MADERA_FLL_SRC_SLIMCLK 3 +#define MADERA_FLL_SRC_FLL1 4 +#define MADERA_FLL_SRC_FLL2 5 +#define MADERA_FLL_SRC_AIF1BCLK 8 +#define MADERA_FLL_SRC_AIF2BCLK 9 +#define MADERA_FLL_SRC_AIF3BCLK 10 +#define MADERA_FLL_SRC_AIF4BCLK 11 +#define MADERA_FLL_SRC_AIF1LRCLK 12 +#define MADERA_FLL_SRC_AIF2LRCLK 13 +#define MADERA_FLL_SRC_AIF3LRCLK 14 +#define MADERA_FLL_SRC_AIF4LRCLK 15 + +#define MADERA_CLK_SYSCLK_1 1 +#define MADERA_CLK_ASYNCCLK_1 2 +#define MADERA_CLK_OPCLK 3 +#define MADERA_CLK_ASYNC_OPCLK 4 +#define MADERA_CLK_SYSCLK_2 5 +#define MADERA_CLK_SYSCLK_3 6 +#define MADERA_CLK_ASYNCCLK_2 7 +#define MADERA_CLK_DSPCLK 8 +#define MADERA_CLK_OUTCLK 9 + +#define MADERA_CLK_SRC_MCLK1 0x0 +#define MADERA_CLK_SRC_MCLK2 0x1 +#define MADERA_CLK_SRC_MCLK3 0x2 +#define MADERA_CLK_SRC_FLL1 0x4 +#define MADERA_CLK_SRC_FLL2 0x5 +#define MADERA_CLK_SRC_FLL3 0x6 +#define MADERA_CLK_SRC_FLLAO_HI 0x7 +#define MADERA_CLK_SRC_FLL1_DIV6 0x7 +#define MADERA_CLK_SRC_AIF1BCLK 0x8 +#define MADERA_CLK_SRC_AIF2BCLK 0x9 +#define MADERA_CLK_SRC_AIF3BCLK 0xA +#define MADERA_CLK_SRC_AIF4BCLK 0xB +#define MADERA_CLK_SRC_FLLAO 0xF + +#define MADERA_OUTCLK_SYSCLK 0 +#define MADERA_OUTCLK_ASYNCCLK 1 +#define MADERA_OUTCLK_MCLK1 4 +#define MADERA_OUTCLK_MCLK2 5 +#define MADERA_OUTCLK_MCLK3 6 + +#define MADERA_MIXER_VOL_MASK 0x00FE +#define MADERA_MIXER_VOL_SHIFT 1 +#define MADERA_MIXER_VOL_WIDTH 7 + +#define MADERA_DOM_GRP_FX 0 +#define MADERA_DOM_GRP_ASRC1 1 +#define MADERA_DOM_GRP_ASRC2 2 +#define MADERA_DOM_GRP_ISRC1 3 +#define MADERA_DOM_GRP_ISRC2 4 +#define MADERA_DOM_GRP_ISRC3 5 +#define MADERA_DOM_GRP_ISRC4 6 +#define MADERA_DOM_GRP_OUT 7 +#define MADERA_DOM_GRP_SPD 8 +#define MADERA_DOM_GRP_DSP1 9 +#define MADERA_DOM_GRP_DSP2 10 +#define MADERA_DOM_GRP_DSP3 11 +#define MADERA_DOM_GRP_DSP4 12 +#define MADERA_DOM_GRP_DSP5 13 +#define MADERA_DOM_GRP_DSP6 14 +#define MADERA_DOM_GRP_DSP7 15 +#define MADERA_DOM_GRP_AIF1 16 +#define MADERA_DOM_GRP_AIF2 17 +#define MADERA_DOM_GRP_AIF3 18 +#define MADERA_DOM_GRP_AIF4 19 +#define MADERA_DOM_GRP_SLIMBUS 20 +#define MADERA_DOM_GRP_PWM 21 +#define MADERA_DOM_GRP_DFC 22 +#define MADERA_N_DOM_GRPS 23 + +#define MADERA_MAX_DAI 11 +#define MADERA_MAX_ADSP 7 + +#define MADERA_NUM_MIXER_INPUTS 148 + +struct madera; +struct wm_adsp; + +struct madera_voice_trigger_info { + /** Which core triggered, 1-based (1 = DSP1, ...) */ + int core_num; +}; + +struct madera_dai_priv { + int clk; + struct snd_pcm_hw_constraint_list constraint; +}; + +struct madera_priv { + struct wm_adsp adsp[MADERA_MAX_ADSP]; + struct madera *madera; + struct device *dev; + int sysclk; + int asyncclk; + int dspclk; + struct madera_dai_priv dai[MADERA_MAX_DAI]; + + int num_inputs; + + unsigned int in_pending; + + unsigned int out_up_pending; + unsigned int out_up_delay; + unsigned int out_down_pending; + unsigned int out_down_delay; + + unsigned int adsp_rate_cache[MADERA_MAX_ADSP]; + + struct mutex rate_lock; + + int tdm_width[MADERA_MAX_AIF]; + int tdm_slots[MADERA_MAX_AIF]; + + int domain_group_ref[MADERA_N_DOM_GRPS]; +}; + +struct madera_fll_cfg { + int n; + unsigned int theta; + unsigned int lambda; + int refdiv; + int fratio; + int gain; + int alt_gain; +}; + +struct madera_fll { + struct madera *madera; + int id; + unsigned int base; + + unsigned int fout; + + int sync_src; + unsigned int sync_freq; + + int ref_src; + unsigned int ref_freq; + struct madera_fll_cfg ref_cfg; +}; + +struct madera_enum { + struct soc_enum mixer_enum; + int val; +}; + +extern const unsigned int madera_ana_tlv[]; +extern const unsigned int madera_eq_tlv[]; +extern const unsigned int madera_digital_tlv[]; +extern const unsigned int madera_noise_tlv[]; +extern const unsigned int madera_ng_tlv[]; + +extern const unsigned int madera_mixer_tlv[]; +extern const char * const madera_mixer_texts[MADERA_NUM_MIXER_INPUTS]; +extern const unsigned int madera_mixer_values[MADERA_NUM_MIXER_INPUTS]; + +#define MADERA_GAINMUX_CONTROLS(name, base) \ + SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1, \ + MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + madera_mixer_tlv) + +#define MADERA_MIXER_CONTROLS(name, base) \ + SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1, \ + MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + madera_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 3, \ + MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + madera_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 5, \ + MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + madera_mixer_tlv), \ + SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 7, \ + MADERA_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \ + madera_mixer_tlv) + +#define MADERA_MUX_ENUM_DECL(name, reg) \ + SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL( \ + name, reg, 0, 0xff, madera_mixer_texts, madera_mixer_values) + +#define MADERA_MUX_CTL_DECL(name) \ + const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM("Route", name##_enum) + +#define MADERA_MUX_ENUMS(name, base_reg) \ + static MADERA_MUX_ENUM_DECL(name##_enum, base_reg); \ + static MADERA_MUX_CTL_DECL(name) + +#define MADERA_MIXER_ENUMS(name, base_reg) \ + MADERA_MUX_ENUMS(name##_in1, base_reg); \ + MADERA_MUX_ENUMS(name##_in2, base_reg + 2); \ + MADERA_MUX_ENUMS(name##_in3, base_reg + 4); \ + MADERA_MUX_ENUMS(name##_in4, base_reg + 6) + +#define MADERA_DSP_AUX_ENUMS(name, base_reg) \ + MADERA_MUX_ENUMS(name##_aux1, base_reg); \ + MADERA_MUX_ENUMS(name##_aux2, base_reg + 8); \ + MADERA_MUX_ENUMS(name##_aux3, base_reg + 16); \ + MADERA_MUX_ENUMS(name##_aux4, base_reg + 24); \ + MADERA_MUX_ENUMS(name##_aux5, base_reg + 32); \ + MADERA_MUX_ENUMS(name##_aux6, base_reg + 40) + +#define MADERA_MUX(name, ctrl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl) + +#define MADERA_MUX_WIDGETS(name, name_str) \ + MADERA_MUX(name_str " Input 1", &name##_mux) + +#define MADERA_MIXER_WIDGETS(name, name_str) \ + MADERA_MUX(name_str " Input 1", &name##_in1_mux), \ + MADERA_MUX(name_str " Input 2", &name##_in2_mux), \ + MADERA_MUX(name_str " Input 3", &name##_in3_mux), \ + MADERA_MUX(name_str " Input 4", &name##_in4_mux), \ + SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0) + +#define MADERA_DSP_WIDGETS(name, name_str) \ + MADERA_MIXER_WIDGETS(name##L, name_str "L"), \ + MADERA_MIXER_WIDGETS(name##R, name_str "R"), \ + MADERA_MUX(name_str " Aux 1", &name##_aux1_mux), \ + MADERA_MUX(name_str " Aux 2", &name##_aux2_mux), \ + MADERA_MUX(name_str " Aux 3", &name##_aux3_mux), \ + MADERA_MUX(name_str " Aux 4", &name##_aux4_mux), \ + MADERA_MUX(name_str " Aux 5", &name##_aux5_mux), \ + MADERA_MUX(name_str " Aux 6", &name##_aux6_mux) + +#define MADERA_MUX_ROUTES(widget, name) \ + { widget, NULL, name " Input 1" }, \ + MADERA_MIXER_INPUT_ROUTES(name " Input 1") + +#define MADERA_MIXER_ROUTES(widget, name) \ + { widget, NULL, name " Mixer" }, \ + { name " Mixer", NULL, name " Input 1" }, \ + { name " Mixer", NULL, name " Input 2" }, \ + { name " Mixer", NULL, name " Input 3" }, \ + { name " Mixer", NULL, name " Input 4" }, \ + MADERA_MIXER_INPUT_ROUTES(name " Input 1"), \ + MADERA_MIXER_INPUT_ROUTES(name " Input 2"), \ + MADERA_MIXER_INPUT_ROUTES(name " Input 3"), \ + MADERA_MIXER_INPUT_ROUTES(name " Input 4") + +#define MADERA_DSP_ROUTES(name) \ + { name, NULL, name " Preloader"}, \ + { name " Preload", NULL, name " Preloader"}, \ + { name, NULL, "SYSCLK"}, \ + { name, NULL, "DSPCLK"}, \ + { name, NULL, name " Aux 1" }, \ + { name, NULL, name " Aux 2" }, \ + { name, NULL, name " Aux 3" }, \ + { name, NULL, name " Aux 4" }, \ + { name, NULL, name " Aux 5" }, \ + { name, NULL, name " Aux 6" }, \ + MADERA_MIXER_INPUT_ROUTES(name " Aux 1"), \ + MADERA_MIXER_INPUT_ROUTES(name " Aux 2"), \ + MADERA_MIXER_INPUT_ROUTES(name " Aux 3"), \ + MADERA_MIXER_INPUT_ROUTES(name " Aux 4"), \ + MADERA_MIXER_INPUT_ROUTES(name " Aux 5"), \ + MADERA_MIXER_INPUT_ROUTES(name " Aux 6"), \ + MADERA_MIXER_ROUTES(name, name "L"), \ + MADERA_MIXER_ROUTES(name, name "R") + +#define MADERA_RATE_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_get_enum_double, .put = madera_rate_put, \ + .private_value = (unsigned long)&xenum } + +#define MADERA_EQ_CONTROL(xname, xbase) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \ + .put = madera_eq_coeff_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) { .base = xbase, \ + .num_regs = 20, .mask = ~MADERA_EQ1_B1_MODE }) } + +#define MADERA_LHPF_CONTROL(xname, xbase) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \ + .put = madera_lhpf_coeff_put, .private_value = \ + ((unsigned long)&(struct soc_bytes) { .base = xbase, \ + .num_regs = 1 }) } + +#define MADERA_RATES SNDRV_PCM_RATE_KNOT + +#define MADERA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define MADERA_OSR_ENUM_SIZE 5 +#define MADERA_SYNC_RATE_ENUM_SIZE 3 +#define MADERA_ASYNC_RATE_ENUM_SIZE 2 +#define MADERA_RATE_ENUM_SIZE \ + (MADERA_SYNC_RATE_ENUM_SIZE + MADERA_ASYNC_RATE_ENUM_SIZE) +#define MADERA_SAMPLE_RATE_ENUM_SIZE 16 +#define MADERA_DFC_TYPE_ENUM_SIZE 5 +#define MADERA_DFC_WIDTH_ENUM_SIZE 5 + +extern const struct snd_soc_dai_ops madera_dai_ops; +extern const struct snd_soc_dai_ops madera_simple_dai_ops; + +extern const struct snd_kcontrol_new madera_inmux[]; +extern const struct snd_kcontrol_new madera_inmode[]; + +extern const char * const madera_rate_text[MADERA_RATE_ENUM_SIZE]; +extern const unsigned int madera_rate_val[MADERA_RATE_ENUM_SIZE]; + +extern const struct soc_enum madera_sample_rate[]; +extern const struct soc_enum madera_isrc_fsl[]; +extern const struct soc_enum madera_isrc_fsh[]; +extern const struct soc_enum madera_asrc1_rate[]; +extern const struct soc_enum madera_asrc1_bidir_rate[]; +extern const struct soc_enum madera_asrc2_rate[]; +extern const struct soc_enum madera_dfc_width[]; +extern const struct soc_enum madera_dfc_type[]; + +extern const struct soc_enum madera_in_vi_ramp; +extern const struct soc_enum madera_in_vd_ramp; + +extern const struct soc_enum madera_out_vi_ramp; +extern const struct soc_enum madera_out_vd_ramp; + +extern const struct soc_enum madera_lhpf1_mode; +extern const struct soc_enum madera_lhpf2_mode; +extern const struct soc_enum madera_lhpf3_mode; +extern const struct soc_enum madera_lhpf4_mode; + +extern const struct soc_enum madera_ng_hold; +extern const struct soc_enum madera_in_hpf_cut_enum; +extern const struct soc_enum madera_in_dmic_osr[]; + +extern const struct soc_enum madera_output_anc_src[]; +extern const struct soc_enum madera_anc_input_src[]; +extern const struct soc_enum madera_anc_ng_enum; + +extern const struct snd_kcontrol_new madera_dsp_trigger_output_mux[]; +extern const struct snd_kcontrol_new madera_drc_activity_output_mux[]; + +extern const struct snd_kcontrol_new madera_adsp_rate_controls[]; + +int madera_dfc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int madera_lp_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int madera_out1_demux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int madera_out1_demux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int madera_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int madera_eq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int madera_lhpf_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int madera_clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +int madera_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +int madera_spk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +int madera_in_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +int madera_out_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +int madera_hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +int madera_anc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +int madera_domain_clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event); + +int madera_set_adsp_clk(struct madera_priv *priv, int dsp_num, + unsigned int freq); + +int madera_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir); + +int madera_init_fll(struct madera *madera, int id, int base, + struct madera_fll *fll); +int madera_set_fll_refclk(struct madera_fll *fll, int source, + unsigned int fref, unsigned int fout); +int madera_set_fll_syncclk(struct madera_fll *fll, int source, + unsigned int fref, unsigned int fout); +int madera_set_fll_ao_refclk(struct madera_fll *fll, int source, + unsigned int fin, unsigned int fout); +int madera_fllhj_set_refclk(struct madera_fll *fll, int source, + unsigned int fin, unsigned int fout); + +int madera_core_init(struct madera_priv *priv); +int madera_core_free(struct madera_priv *priv); +int madera_init_overheat(struct madera_priv *priv); +int madera_free_overheat(struct madera_priv *priv); +int madera_init_inputs(struct snd_soc_component *component); +int madera_init_outputs(struct snd_soc_component *component, + const struct snd_soc_dapm_route *routes, + int n_mono_routes, int n_real); +int madera_init_bus_error_irq(struct madera_priv *priv, int dsp_num, + irq_handler_t handler); +void madera_free_bus_error_irq(struct madera_priv *priv, int dsp_num); + +int madera_init_dai(struct madera_priv *priv, int id); + +int madera_set_output_mode(struct snd_soc_component *component, int output, + bool differential); + +/* Following functions are for use by machine drivers */ +static inline int madera_register_notifier(struct snd_soc_component *component, + struct notifier_block *nb) +{ + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + + return blocking_notifier_chain_register(&madera->notifier, nb); +} + +static inline int +madera_unregister_notifier(struct snd_soc_component *component, + struct notifier_block *nb) +{ + struct madera_priv *priv = snd_soc_component_get_drvdata(component); + struct madera *madera = priv->madera; + + return blocking_notifier_chain_unregister(&madera->notifier, nb); +} + +#endif diff --git a/sound/soc/codecs/max9759.c b/sound/soc/codecs/max9759.c new file mode 100644 index 0000000000..bc57d7687f --- /dev/null +++ b/sound/soc/codecs/max9759.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MAX9759 Amplifier Driver + * + * Copyright (c) 2017 BayLibre, SAS. + * Author: Neil Armstrong + */ + +#include +#include +#include +#include +#include + +#define DRV_NAME "max9759" + +struct max9759 { + struct gpio_desc *gpiod_shutdown; + struct gpio_desc *gpiod_mute; + struct gpio_descs *gpiod_gain; + bool is_mute; + unsigned int gain; +}; + +static int pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct max9759 *priv = snd_soc_component_get_drvdata(c); + + if (SND_SOC_DAPM_EVENT_ON(event)) + gpiod_set_value_cansleep(priv->gpiod_shutdown, 0); + else + gpiod_set_value_cansleep(priv->gpiod_shutdown, 1); + + return 0; +} + +/* From 6dB to 24dB in steps of 6dB */ +static const DECLARE_TLV_DB_SCALE(speaker_gain_tlv, 600, 600, 0); + +static int speaker_gain_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct max9759 *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = priv->gain; + + return 0; +} + +static const bool speaker_gain_table[4][2] = { + /* G1, G2 */ + {true, true}, /* +6dB */ + {false, true}, /* +12dB */ + {true, false}, /* +18dB */ + {false, false}, /* +24dB */ +}; + +static int speaker_gain_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct max9759 *priv = snd_soc_component_get_drvdata(c); + + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > 3) + return -EINVAL; + + priv->gain = ucontrol->value.integer.value[0]; + + /* G1 */ + gpiod_set_value_cansleep(priv->gpiod_gain->desc[0], + speaker_gain_table[priv->gain][0]); + /* G2 */ + gpiod_set_value_cansleep(priv->gpiod_gain->desc[1], + speaker_gain_table[priv->gain][1]); + + return 1; +} + +static int speaker_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct max9759 *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = !priv->is_mute; + + return 0; +} + +static int speaker_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct max9759 *priv = snd_soc_component_get_drvdata(c); + + priv->is_mute = !ucontrol->value.integer.value[0]; + + gpiod_set_value_cansleep(priv->gpiod_mute, priv->is_mute); + + return 1; +} + +static const struct snd_kcontrol_new max9759_dapm_controls[] = { + SOC_SINGLE_EXT_TLV("Speaker Gain Volume", 0, 0, 3, 0, + speaker_gain_control_get, speaker_gain_control_put, + speaker_gain_tlv), + SOC_SINGLE_BOOL_EXT("Playback Switch", 0, + speaker_mute_get, speaker_mute_put), +}; + +static const struct snd_soc_dapm_widget max9759_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("INL"), + SND_SOC_DAPM_INPUT("INR"), + SND_SOC_DAPM_PGA_E("PGA", SND_SOC_NOPM, 0, 0, NULL, 0, pga_event, + (SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)), + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static const struct snd_soc_dapm_route max9759_dapm_routes[] = { + { "PGA", NULL, "INL" }, + { "PGA", NULL, "INR" }, + { "OUTL", NULL, "PGA" }, + { "OUTR", NULL, "PGA" }, +}; + +static const struct snd_soc_component_driver max9759_component_driver = { + .controls = max9759_dapm_controls, + .num_controls = ARRAY_SIZE(max9759_dapm_controls), + .dapm_widgets = max9759_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9759_dapm_widgets), + .dapm_routes = max9759_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9759_dapm_routes), +}; + +static int max9759_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct max9759 *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpiod_shutdown)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown), + "Failed to get 'shutdown' gpio"); + + priv->gpiod_mute = devm_gpiod_get(dev, "mute", GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpiod_mute)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_mute), + "Failed to get 'mute' gpio"); + priv->is_mute = true; + + priv->gpiod_gain = devm_gpiod_get_array(dev, "gain", GPIOD_OUT_HIGH); + if (IS_ERR(priv->gpiod_gain)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_gain), + "Failed to get 'gain' gpios"); + priv->gain = 0; + + if (priv->gpiod_gain->ndescs != 2) { + dev_err(dev, "Invalid 'gain' gpios count: %d", + priv->gpiod_gain->ndescs); + return -EINVAL; + } + + return devm_snd_soc_register_component(dev, &max9759_component_driver, + NULL, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id max9759_ids[] = { + { .compatible = "maxim,max9759", }, + { } +}; +MODULE_DEVICE_TABLE(of, max9759_ids); +#endif + +static struct platform_driver max9759_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(max9759_ids), + }, + .probe = max9759_probe, +}; + +module_platform_driver(max9759_driver); + +MODULE_DESCRIPTION("ASoC MAX9759 amplifier driver"); +MODULE_AUTHOR("Neil Armstrong "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c new file mode 100644 index 0000000000..d22b4ba51e --- /dev/null +++ b/sound/soc/codecs/max9768.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MAX9768 AMP driver + * + * Copyright (C) 2011, 2012 by Wolfram Sang, Pengutronix e.K. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* "Registers" */ +#define MAX9768_VOL 0 +#define MAX9768_CTRL 3 + +/* Commands */ +#define MAX9768_CTRL_PWM 0x15 +#define MAX9768_CTRL_FILTERLESS 0x16 + +struct max9768 { + struct regmap *regmap; + int mute_gpio; + int shdn_gpio; + u32 flags; +}; + +static const struct reg_default max9768_default_regs[] = { + { 0, 0 }, + { 3, MAX9768_CTRL_FILTERLESS}, +}; + +static int max9768_get_gpio(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct max9768 *max9768 = snd_soc_component_get_drvdata(c); + int val = gpio_get_value_cansleep(max9768->mute_gpio); + + ucontrol->value.integer.value[0] = !val; + + return 0; +} + +static int max9768_set_gpio(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct max9768 *max9768 = snd_soc_component_get_drvdata(c); + + gpio_set_value_cansleep(max9768->mute_gpio, !ucontrol->value.integer.value[0]); + + return 0; +} + +static const DECLARE_TLV_DB_RANGE(volume_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-16150, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(-9280, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(-9030, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(-8680, 0, 0), + 4, 4, TLV_DB_SCALE_ITEM(-8430, 0, 0), + 5, 5, TLV_DB_SCALE_ITEM(-8080, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(-7830, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(-7470, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(-7220, 0, 0), + 9, 9, TLV_DB_SCALE_ITEM(-6870, 0, 0), + 10, 10, TLV_DB_SCALE_ITEM(-6620, 0, 0), + 11, 11, TLV_DB_SCALE_ITEM(-6270, 0, 0), + 12, 12, TLV_DB_SCALE_ITEM(-6020, 0, 0), + 13, 13, TLV_DB_SCALE_ITEM(-5670, 0, 0), + 14, 14, TLV_DB_SCALE_ITEM(-5420, 0, 0), + 15, 17, TLV_DB_SCALE_ITEM(-5060, 250, 0), + 18, 18, TLV_DB_SCALE_ITEM(-4370, 0, 0), + 19, 19, TLV_DB_SCALE_ITEM(-4210, 0, 0), + 20, 20, TLV_DB_SCALE_ITEM(-3960, 0, 0), + 21, 21, TLV_DB_SCALE_ITEM(-3760, 0, 0), + 22, 22, TLV_DB_SCALE_ITEM(-3600, 0, 0), + 23, 23, TLV_DB_SCALE_ITEM(-3340, 0, 0), + 24, 24, TLV_DB_SCALE_ITEM(-3150, 0, 0), + 25, 25, TLV_DB_SCALE_ITEM(-2980, 0, 0), + 26, 26, TLV_DB_SCALE_ITEM(-2720, 0, 0), + 27, 27, TLV_DB_SCALE_ITEM(-2520, 0, 0), + 28, 30, TLV_DB_SCALE_ITEM(-2350, 190, 0), + 31, 31, TLV_DB_SCALE_ITEM(-1750, 0, 0), + 32, 34, TLV_DB_SCALE_ITEM(-1640, 100, 0), + 35, 37, TLV_DB_SCALE_ITEM(-1310, 110, 0), + 38, 39, TLV_DB_SCALE_ITEM(-990, 100, 0), + 40, 40, TLV_DB_SCALE_ITEM(-710, 0, 0), + 41, 41, TLV_DB_SCALE_ITEM(-600, 0, 0), + 42, 42, TLV_DB_SCALE_ITEM(-500, 0, 0), + 43, 43, TLV_DB_SCALE_ITEM(-340, 0, 0), + 44, 44, TLV_DB_SCALE_ITEM(-190, 0, 0), + 45, 45, TLV_DB_SCALE_ITEM(-50, 0, 0), + 46, 46, TLV_DB_SCALE_ITEM(50, 0, 0), + 47, 50, TLV_DB_SCALE_ITEM(120, 40, 0), + 51, 57, TLV_DB_SCALE_ITEM(290, 50, 0), + 58, 58, TLV_DB_SCALE_ITEM(650, 0, 0), + 59, 62, TLV_DB_SCALE_ITEM(700, 60, 0), + 63, 63, TLV_DB_SCALE_ITEM(950, 0, 0) +); + +static const struct snd_kcontrol_new max9768_volume[] = { + SOC_SINGLE_TLV("Playback Volume", MAX9768_VOL, 0, 63, 0, volume_tlv), +}; + +static const struct snd_kcontrol_new max9768_mute[] = { + SOC_SINGLE_BOOL_EXT("Playback Switch", 0, max9768_get_gpio, max9768_set_gpio), +}; + +static const struct snd_soc_dapm_widget max9768_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN"), + +SND_SOC_DAPM_OUTPUT("OUT+"), +SND_SOC_DAPM_OUTPUT("OUT-"), +}; + +static const struct snd_soc_dapm_route max9768_dapm_routes[] = { + { "OUT+", NULL, "IN" }, + { "OUT-", NULL, "IN" }, +}; + +static int max9768_probe(struct snd_soc_component *component) +{ + struct max9768 *max9768 = snd_soc_component_get_drvdata(component); + int ret; + + if (max9768->flags & MAX9768_FLAG_CLASSIC_PWM) { + ret = regmap_write(max9768->regmap, MAX9768_CTRL, + MAX9768_CTRL_PWM); + if (ret) + return ret; + } + + if (gpio_is_valid(max9768->mute_gpio)) { + ret = snd_soc_add_component_controls(component, max9768_mute, + ARRAY_SIZE(max9768_mute)); + if (ret) + return ret; + } + + return 0; +} + +static const struct snd_soc_component_driver max9768_component_driver = { + .probe = max9768_probe, + .controls = max9768_volume, + .num_controls = ARRAY_SIZE(max9768_volume), + .dapm_widgets = max9768_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9768_dapm_widgets), + .dapm_routes = max9768_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9768_dapm_routes), +}; + +static const struct regmap_config max9768_i2c_regmap_config = { + .reg_bits = 2, + .val_bits = 6, + .max_register = 3, + .reg_defaults = max9768_default_regs, + .num_reg_defaults = ARRAY_SIZE(max9768_default_regs), + .cache_type = REGCACHE_RBTREE, +}; + +static int max9768_i2c_probe(struct i2c_client *client) +{ + struct max9768 *max9768; + struct max9768_pdata *pdata = client->dev.platform_data; + int err; + + max9768 = devm_kzalloc(&client->dev, sizeof(*max9768), GFP_KERNEL); + if (!max9768) + return -ENOMEM; + + if (pdata) { + /* Mute on powerup to avoid clicks */ + err = devm_gpio_request_one(&client->dev, pdata->mute_gpio, + GPIOF_INIT_HIGH, "MAX9768 Mute"); + max9768->mute_gpio = err ?: pdata->mute_gpio; + + /* Activate chip by releasing shutdown, enables I2C */ + err = devm_gpio_request_one(&client->dev, pdata->shdn_gpio, + GPIOF_INIT_HIGH, "MAX9768 Shutdown"); + max9768->shdn_gpio = err ?: pdata->shdn_gpio; + + max9768->flags = pdata->flags; + } else { + max9768->shdn_gpio = -EINVAL; + max9768->mute_gpio = -EINVAL; + } + + i2c_set_clientdata(client, max9768); + + max9768->regmap = devm_regmap_init_i2c(client, &max9768_i2c_regmap_config); + if (IS_ERR(max9768->regmap)) + return PTR_ERR(max9768->regmap); + + return devm_snd_soc_register_component(&client->dev, + &max9768_component_driver, NULL, 0); +} + +static const struct i2c_device_id max9768_i2c_id[] = { + { "max9768", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9768_i2c_id); + +static struct i2c_driver max9768_i2c_driver = { + .driver = { + .name = "max9768", + }, + .probe = max9768_i2c_probe, + .id_table = max9768_i2c_id, +}; +module_i2c_driver(max9768_i2c_driver); + +MODULE_AUTHOR("Wolfram Sang "); +MODULE_DESCRIPTION("ASoC MAX9768 amplifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c new file mode 100644 index 0000000000..8b56ee550c --- /dev/null +++ b/sound/soc/codecs/max98088.c @@ -0,0 +1,1800 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * max98088.c -- MAX98088 ALSA SoC Audio driver + * + * Copyright 2010 Maxim Integrated Products + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98088.h" + +enum max98088_type { + MAX98088, + MAX98089, +}; + +struct max98088_cdata { + unsigned int rate; + unsigned int fmt; + int eq_sel; +}; + +struct max98088_priv { + struct regmap *regmap; + enum max98088_type devtype; + struct max98088_pdata *pdata; + struct clk *mclk; + unsigned char mclk_prescaler; + unsigned int sysclk; + struct max98088_cdata dai[2]; + int eq_textcnt; + const char **eq_texts; + struct soc_enum eq_enum; + u8 ina_state; + u8 inb_state; + unsigned int ex_mode; + unsigned int digmic; + unsigned int mic1pre; + unsigned int mic2pre; + unsigned int extmic_mode; +}; + +static const struct reg_default max98088_reg[] = { + { 0xf, 0x00 }, /* 0F interrupt enable */ + + { 0x10, 0x00 }, /* 10 master clock */ + { 0x11, 0x00 }, /* 11 DAI1 clock mode */ + { 0x12, 0x00 }, /* 12 DAI1 clock control */ + { 0x13, 0x00 }, /* 13 DAI1 clock control */ + { 0x14, 0x00 }, /* 14 DAI1 format */ + { 0x15, 0x00 }, /* 15 DAI1 clock */ + { 0x16, 0x00 }, /* 16 DAI1 config */ + { 0x17, 0x00 }, /* 17 DAI1 TDM */ + { 0x18, 0x00 }, /* 18 DAI1 filters */ + { 0x19, 0x00 }, /* 19 DAI2 clock mode */ + { 0x1a, 0x00 }, /* 1A DAI2 clock control */ + { 0x1b, 0x00 }, /* 1B DAI2 clock control */ + { 0x1c, 0x00 }, /* 1C DAI2 format */ + { 0x1d, 0x00 }, /* 1D DAI2 clock */ + { 0x1e, 0x00 }, /* 1E DAI2 config */ + { 0x1f, 0x00 }, /* 1F DAI2 TDM */ + + { 0x20, 0x00 }, /* 20 DAI2 filters */ + { 0x21, 0x00 }, /* 21 data config */ + { 0x22, 0x00 }, /* 22 DAC mixer */ + { 0x23, 0x00 }, /* 23 left ADC mixer */ + { 0x24, 0x00 }, /* 24 right ADC mixer */ + { 0x25, 0x00 }, /* 25 left HP mixer */ + { 0x26, 0x00 }, /* 26 right HP mixer */ + { 0x27, 0x00 }, /* 27 HP control */ + { 0x28, 0x00 }, /* 28 left REC mixer */ + { 0x29, 0x00 }, /* 29 right REC mixer */ + { 0x2a, 0x00 }, /* 2A REC control */ + { 0x2b, 0x00 }, /* 2B left SPK mixer */ + { 0x2c, 0x00 }, /* 2C right SPK mixer */ + { 0x2d, 0x00 }, /* 2D SPK control */ + { 0x2e, 0x00 }, /* 2E sidetone */ + { 0x2f, 0x00 }, /* 2F DAI1 playback level */ + + { 0x30, 0x00 }, /* 30 DAI1 playback level */ + { 0x31, 0x00 }, /* 31 DAI2 playback level */ + { 0x32, 0x00 }, /* 32 DAI2 playbakc level */ + { 0x33, 0x00 }, /* 33 left ADC level */ + { 0x34, 0x00 }, /* 34 right ADC level */ + { 0x35, 0x00 }, /* 35 MIC1 level */ + { 0x36, 0x00 }, /* 36 MIC2 level */ + { 0x37, 0x00 }, /* 37 INA level */ + { 0x38, 0x00 }, /* 38 INB level */ + { 0x39, 0x00 }, /* 39 left HP volume */ + { 0x3a, 0x00 }, /* 3A right HP volume */ + { 0x3b, 0x00 }, /* 3B left REC volume */ + { 0x3c, 0x00 }, /* 3C right REC volume */ + { 0x3d, 0x00 }, /* 3D left SPK volume */ + { 0x3e, 0x00 }, /* 3E right SPK volume */ + { 0x3f, 0x00 }, /* 3F MIC config */ + + { 0x40, 0x00 }, /* 40 MIC threshold */ + { 0x41, 0x00 }, /* 41 excursion limiter filter */ + { 0x42, 0x00 }, /* 42 excursion limiter threshold */ + { 0x43, 0x00 }, /* 43 ALC */ + { 0x44, 0x00 }, /* 44 power limiter threshold */ + { 0x45, 0x00 }, /* 45 power limiter config */ + { 0x46, 0x00 }, /* 46 distortion limiter config */ + { 0x47, 0x00 }, /* 47 audio input */ + { 0x48, 0x00 }, /* 48 microphone */ + { 0x49, 0x00 }, /* 49 level control */ + { 0x4a, 0x00 }, /* 4A bypass switches */ + { 0x4b, 0x00 }, /* 4B jack detect */ + { 0x4c, 0x00 }, /* 4C input enable */ + { 0x4d, 0x00 }, /* 4D output enable */ + { 0x4e, 0xF0 }, /* 4E bias control */ + { 0x4f, 0x00 }, /* 4F DAC power */ + + { 0x50, 0x0F }, /* 50 DAC power */ + { 0x51, 0x00 }, /* 51 system */ + { 0x52, 0x00 }, /* 52 DAI1 EQ1 */ + { 0x53, 0x00 }, /* 53 DAI1 EQ1 */ + { 0x54, 0x00 }, /* 54 DAI1 EQ1 */ + { 0x55, 0x00 }, /* 55 DAI1 EQ1 */ + { 0x56, 0x00 }, /* 56 DAI1 EQ1 */ + { 0x57, 0x00 }, /* 57 DAI1 EQ1 */ + { 0x58, 0x00 }, /* 58 DAI1 EQ1 */ + { 0x59, 0x00 }, /* 59 DAI1 EQ1 */ + { 0x5a, 0x00 }, /* 5A DAI1 EQ1 */ + { 0x5b, 0x00 }, /* 5B DAI1 EQ1 */ + { 0x5c, 0x00 }, /* 5C DAI1 EQ2 */ + { 0x5d, 0x00 }, /* 5D DAI1 EQ2 */ + { 0x5e, 0x00 }, /* 5E DAI1 EQ2 */ + { 0x5f, 0x00 }, /* 5F DAI1 EQ2 */ + + { 0x60, 0x00 }, /* 60 DAI1 EQ2 */ + { 0x61, 0x00 }, /* 61 DAI1 EQ2 */ + { 0x62, 0x00 }, /* 62 DAI1 EQ2 */ + { 0x63, 0x00 }, /* 63 DAI1 EQ2 */ + { 0x64, 0x00 }, /* 64 DAI1 EQ2 */ + { 0x65, 0x00 }, /* 65 DAI1 EQ2 */ + { 0x66, 0x00 }, /* 66 DAI1 EQ3 */ + { 0x67, 0x00 }, /* 67 DAI1 EQ3 */ + { 0x68, 0x00 }, /* 68 DAI1 EQ3 */ + { 0x69, 0x00 }, /* 69 DAI1 EQ3 */ + { 0x6a, 0x00 }, /* 6A DAI1 EQ3 */ + { 0x6b, 0x00 }, /* 6B DAI1 EQ3 */ + { 0x6c, 0x00 }, /* 6C DAI1 EQ3 */ + { 0x6d, 0x00 }, /* 6D DAI1 EQ3 */ + { 0x6e, 0x00 }, /* 6E DAI1 EQ3 */ + { 0x6f, 0x00 }, /* 6F DAI1 EQ3 */ + + { 0x70, 0x00 }, /* 70 DAI1 EQ4 */ + { 0x71, 0x00 }, /* 71 DAI1 EQ4 */ + { 0x72, 0x00 }, /* 72 DAI1 EQ4 */ + { 0x73, 0x00 }, /* 73 DAI1 EQ4 */ + { 0x74, 0x00 }, /* 74 DAI1 EQ4 */ + { 0x75, 0x00 }, /* 75 DAI1 EQ4 */ + { 0x76, 0x00 }, /* 76 DAI1 EQ4 */ + { 0x77, 0x00 }, /* 77 DAI1 EQ4 */ + { 0x78, 0x00 }, /* 78 DAI1 EQ4 */ + { 0x79, 0x00 }, /* 79 DAI1 EQ4 */ + { 0x7a, 0x00 }, /* 7A DAI1 EQ5 */ + { 0x7b, 0x00 }, /* 7B DAI1 EQ5 */ + { 0x7c, 0x00 }, /* 7C DAI1 EQ5 */ + { 0x7d, 0x00 }, /* 7D DAI1 EQ5 */ + { 0x7e, 0x00 }, /* 7E DAI1 EQ5 */ + { 0x7f, 0x00 }, /* 7F DAI1 EQ5 */ + + { 0x80, 0x00 }, /* 80 DAI1 EQ5 */ + { 0x81, 0x00 }, /* 81 DAI1 EQ5 */ + { 0x82, 0x00 }, /* 82 DAI1 EQ5 */ + { 0x83, 0x00 }, /* 83 DAI1 EQ5 */ + { 0x84, 0x00 }, /* 84 DAI2 EQ1 */ + { 0x85, 0x00 }, /* 85 DAI2 EQ1 */ + { 0x86, 0x00 }, /* 86 DAI2 EQ1 */ + { 0x87, 0x00 }, /* 87 DAI2 EQ1 */ + { 0x88, 0x00 }, /* 88 DAI2 EQ1 */ + { 0x89, 0x00 }, /* 89 DAI2 EQ1 */ + { 0x8a, 0x00 }, /* 8A DAI2 EQ1 */ + { 0x8b, 0x00 }, /* 8B DAI2 EQ1 */ + { 0x8c, 0x00 }, /* 8C DAI2 EQ1 */ + { 0x8d, 0x00 }, /* 8D DAI2 EQ1 */ + { 0x8e, 0x00 }, /* 8E DAI2 EQ2 */ + { 0x8f, 0x00 }, /* 8F DAI2 EQ2 */ + + { 0x90, 0x00 }, /* 90 DAI2 EQ2 */ + { 0x91, 0x00 }, /* 91 DAI2 EQ2 */ + { 0x92, 0x00 }, /* 92 DAI2 EQ2 */ + { 0x93, 0x00 }, /* 93 DAI2 EQ2 */ + { 0x94, 0x00 }, /* 94 DAI2 EQ2 */ + { 0x95, 0x00 }, /* 95 DAI2 EQ2 */ + { 0x96, 0x00 }, /* 96 DAI2 EQ2 */ + { 0x97, 0x00 }, /* 97 DAI2 EQ2 */ + { 0x98, 0x00 }, /* 98 DAI2 EQ3 */ + { 0x99, 0x00 }, /* 99 DAI2 EQ3 */ + { 0x9a, 0x00 }, /* 9A DAI2 EQ3 */ + { 0x9b, 0x00 }, /* 9B DAI2 EQ3 */ + { 0x9c, 0x00 }, /* 9C DAI2 EQ3 */ + { 0x9d, 0x00 }, /* 9D DAI2 EQ3 */ + { 0x9e, 0x00 }, /* 9E DAI2 EQ3 */ + { 0x9f, 0x00 }, /* 9F DAI2 EQ3 */ + + { 0xa0, 0x00 }, /* A0 DAI2 EQ3 */ + { 0xa1, 0x00 }, /* A1 DAI2 EQ3 */ + { 0xa2, 0x00 }, /* A2 DAI2 EQ4 */ + { 0xa3, 0x00 }, /* A3 DAI2 EQ4 */ + { 0xa4, 0x00 }, /* A4 DAI2 EQ4 */ + { 0xa5, 0x00 }, /* A5 DAI2 EQ4 */ + { 0xa6, 0x00 }, /* A6 DAI2 EQ4 */ + { 0xa7, 0x00 }, /* A7 DAI2 EQ4 */ + { 0xa8, 0x00 }, /* A8 DAI2 EQ4 */ + { 0xa9, 0x00 }, /* A9 DAI2 EQ4 */ + { 0xaa, 0x00 }, /* AA DAI2 EQ4 */ + { 0xab, 0x00 }, /* AB DAI2 EQ4 */ + { 0xac, 0x00 }, /* AC DAI2 EQ5 */ + { 0xad, 0x00 }, /* AD DAI2 EQ5 */ + { 0xae, 0x00 }, /* AE DAI2 EQ5 */ + { 0xaf, 0x00 }, /* AF DAI2 EQ5 */ + + { 0xb0, 0x00 }, /* B0 DAI2 EQ5 */ + { 0xb1, 0x00 }, /* B1 DAI2 EQ5 */ + { 0xb2, 0x00 }, /* B2 DAI2 EQ5 */ + { 0xb3, 0x00 }, /* B3 DAI2 EQ5 */ + { 0xb4, 0x00 }, /* B4 DAI2 EQ5 */ + { 0xb5, 0x00 }, /* B5 DAI2 EQ5 */ + { 0xb6, 0x00 }, /* B6 DAI1 biquad */ + { 0xb7, 0x00 }, /* B7 DAI1 biquad */ + { 0xb8 ,0x00 }, /* B8 DAI1 biquad */ + { 0xb9, 0x00 }, /* B9 DAI1 biquad */ + { 0xba, 0x00 }, /* BA DAI1 biquad */ + { 0xbb, 0x00 }, /* BB DAI1 biquad */ + { 0xbc, 0x00 }, /* BC DAI1 biquad */ + { 0xbd, 0x00 }, /* BD DAI1 biquad */ + { 0xbe, 0x00 }, /* BE DAI1 biquad */ + { 0xbf, 0x00 }, /* BF DAI1 biquad */ + + { 0xc0, 0x00 }, /* C0 DAI2 biquad */ + { 0xc1, 0x00 }, /* C1 DAI2 biquad */ + { 0xc2, 0x00 }, /* C2 DAI2 biquad */ + { 0xc3, 0x00 }, /* C3 DAI2 biquad */ + { 0xc4, 0x00 }, /* C4 DAI2 biquad */ + { 0xc5, 0x00 }, /* C5 DAI2 biquad */ + { 0xc6, 0x00 }, /* C6 DAI2 biquad */ + { 0xc7, 0x00 }, /* C7 DAI2 biquad */ + { 0xc8, 0x00 }, /* C8 DAI2 biquad */ + { 0xc9, 0x00 }, /* C9 DAI2 biquad */ +}; + +static bool max98088_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98088_REG_00_IRQ_STATUS ... 0xC9: + case M98088_REG_FF_REV_ID: + return true; + default: + return false; + } +} + +static bool max98088_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98088_REG_03_BATTERY_VOLTAGE ... 0xC9: + return true; + default: + return false; + } +} + +static bool max98088_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98088_REG_00_IRQ_STATUS ... M98088_REG_03_BATTERY_VOLTAGE: + case M98088_REG_FF_REV_ID: + return true; + default: + return false; + } +} + +static const struct regmap_config max98088_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = max98088_readable_register, + .writeable_reg = max98088_writeable_register, + .volatile_reg = max98088_volatile_register, + .max_register = 0xff, + + .reg_defaults = max98088_reg, + .num_reg_defaults = ARRAY_SIZE(max98088_reg), + .cache_type = REGCACHE_RBTREE, +}; + +/* + * Load equalizer DSP coefficient configurations registers + */ +static void m98088_eq_band(struct snd_soc_component *component, unsigned int dai, + unsigned int band, u16 *coefs) +{ + unsigned int eq_reg; + unsigned int i; + + if (WARN_ON(band > 4) || + WARN_ON(dai > 1)) + return; + + /* Load the base register address */ + eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE; + + /* Add the band address offset, note adjustment for word address */ + eq_reg += band * (M98088_COEFS_PER_BAND << 1); + + /* Step through the registers and coefs */ + for (i = 0; i < M98088_COEFS_PER_BAND; i++) { + snd_soc_component_write(component, eq_reg++, M98088_BYTE1(coefs[i])); + snd_soc_component_write(component, eq_reg++, M98088_BYTE0(coefs[i])); + } +} + +/* + * Excursion limiter modes + */ +static const char *max98088_exmode_texts[] = { + "Off", "100Hz", "400Hz", "600Hz", "800Hz", "1000Hz", "200-400Hz", + "400-600Hz", "400-800Hz", +}; + +static const unsigned int max98088_exmode_values[] = { + 0x00, 0x43, 0x10, 0x20, 0x30, 0x40, 0x11, 0x22, 0x32 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(max98088_exmode_enum, + M98088_REG_41_SPKDHP, 0, 127, + max98088_exmode_texts, + max98088_exmode_values); + +static const char *max98088_ex_thresh[] = { /* volts PP */ + "0.6", "1.2", "1.8", "2.4", "3.0", "3.6", "4.2", "4.8"}; +static SOC_ENUM_SINGLE_DECL(max98088_ex_thresh_enum, + M98088_REG_42_SPKDHP_THRESH, 0, + max98088_ex_thresh); + +static const char *max98088_fltr_mode[] = {"Voice", "Music" }; +static SOC_ENUM_SINGLE_DECL(max98088_filter_mode_enum, + M98088_REG_18_DAI1_FILTERS, 7, + max98088_fltr_mode); + +static const char *max98088_extmic_text[] = { "None", "MIC1", "MIC2" }; + +static SOC_ENUM_SINGLE_DECL(max98088_extmic_enum, + M98088_REG_48_CFG_MIC, 0, + max98088_extmic_text); + +static const struct snd_kcontrol_new max98088_extmic_mux = + SOC_DAPM_ENUM("External MIC Mux", max98088_extmic_enum); + +static const char *max98088_dai1_fltr[] = { + "Off", "fc=258/fs=16k", "fc=500/fs=16k", + "fc=258/fs=8k", "fc=500/fs=8k", "fc=200"}; +static SOC_ENUM_SINGLE_DECL(max98088_dai1_dac_filter_enum, + M98088_REG_18_DAI1_FILTERS, 0, + max98088_dai1_fltr); +static SOC_ENUM_SINGLE_DECL(max98088_dai1_adc_filter_enum, + M98088_REG_18_DAI1_FILTERS, 4, + max98088_dai1_fltr); + +static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + + max98088->mic1pre = sel; + snd_soc_component_update_bits(component, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK, + (1+sel)<value.integer.value[0] = max98088->mic1pre; + return 0; +} + +static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + + max98088->mic2pre = sel; + snd_soc_component_update_bits(component, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK, + (1+sel)<value.integer.value[0] = max98088->mic2pre; + return 0; +} + +static const DECLARE_TLV_DB_RANGE(max98088_micboost_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0) +); + +static const DECLARE_TLV_DB_RANGE(max98088_hp_tlv, + 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0) +); + +static const DECLARE_TLV_DB_RANGE(max98088_spk_tlv, + 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0) +); + +static const struct snd_kcontrol_new max98088_snd_controls[] = { + + SOC_DOUBLE_R_TLV("Headphone Volume", M98088_REG_39_LVL_HP_L, + M98088_REG_3A_LVL_HP_R, 0, 31, 0, max98088_hp_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", M98088_REG_3D_LVL_SPK_L, + M98088_REG_3E_LVL_SPK_R, 0, 31, 0, max98088_spk_tlv), + SOC_DOUBLE_R_TLV("Receiver Volume", M98088_REG_3B_LVL_REC_L, + M98088_REG_3C_LVL_REC_R, 0, 31, 0, max98088_spk_tlv), + + SOC_DOUBLE_R("Headphone Switch", M98088_REG_39_LVL_HP_L, + M98088_REG_3A_LVL_HP_R, 7, 1, 1), + SOC_DOUBLE_R("Speaker Switch", M98088_REG_3D_LVL_SPK_L, + M98088_REG_3E_LVL_SPK_R, 7, 1, 1), + SOC_DOUBLE_R("Receiver Switch", M98088_REG_3B_LVL_REC_L, + M98088_REG_3C_LVL_REC_R, 7, 1, 1), + + SOC_SINGLE("MIC1 Volume", M98088_REG_35_LVL_MIC1, 0, 31, 1), + SOC_SINGLE("MIC2 Volume", M98088_REG_36_LVL_MIC2, 0, 31, 1), + + SOC_SINGLE_EXT_TLV("MIC1 Boost Volume", + M98088_REG_35_LVL_MIC1, 5, 2, 0, + max98088_mic1pre_get, max98088_mic1pre_set, + max98088_micboost_tlv), + SOC_SINGLE_EXT_TLV("MIC2 Boost Volume", + M98088_REG_36_LVL_MIC2, 5, 2, 0, + max98088_mic2pre_get, max98088_mic2pre_set, + max98088_micboost_tlv), + + SOC_SINGLE("Noise Gate Threshold", M98088_REG_40_MICAGC_THRESH, + 4, 15, 0), + + SOC_SINGLE("INA Volume", M98088_REG_37_LVL_INA, 0, 7, 1), + SOC_SINGLE("INB Volume", M98088_REG_38_LVL_INB, 0, 7, 1), + + SOC_SINGLE("ADCL Volume", M98088_REG_33_LVL_ADC_L, 0, 15, 0), + SOC_SINGLE("ADCR Volume", M98088_REG_34_LVL_ADC_R, 0, 15, 0), + + SOC_SINGLE("ADCL Boost Volume", M98088_REG_33_LVL_ADC_L, 4, 3, 0), + SOC_SINGLE("ADCR Boost Volume", M98088_REG_34_LVL_ADC_R, 4, 3, 0), + + SOC_SINGLE("EQ1 Switch", M98088_REG_49_CFG_LEVEL, 0, 1, 0), + SOC_SINGLE("EQ2 Switch", M98088_REG_49_CFG_LEVEL, 1, 1, 0), + + SOC_ENUM("EX Limiter Mode", max98088_exmode_enum), + SOC_ENUM("EX Limiter Threshold", max98088_ex_thresh_enum), + + SOC_ENUM("DAI1 Filter Mode", max98088_filter_mode_enum), + SOC_ENUM("DAI1 DAC Filter", max98088_dai1_dac_filter_enum), + SOC_ENUM("DAI1 ADC Filter", max98088_dai1_adc_filter_enum), + SOC_SINGLE("DAI2 DC Block Switch", M98088_REG_20_DAI2_FILTERS, + 0, 1, 0), + + SOC_SINGLE("ALC Switch", M98088_REG_43_SPKALC_COMP, 7, 1, 0), + SOC_SINGLE("ALC Threshold", M98088_REG_43_SPKALC_COMP, 0, 7, 0), + SOC_SINGLE("ALC Multiband", M98088_REG_43_SPKALC_COMP, 3, 1, 0), + SOC_SINGLE("ALC Release Time", M98088_REG_43_SPKALC_COMP, 4, 7, 0), + + SOC_SINGLE("PWR Limiter Threshold", M98088_REG_44_PWRLMT_CFG, + 4, 15, 0), + SOC_SINGLE("PWR Limiter Weight", M98088_REG_44_PWRLMT_CFG, 0, 7, 0), + SOC_SINGLE("PWR Limiter Time1", M98088_REG_45_PWRLMT_TIME, 0, 15, 0), + SOC_SINGLE("PWR Limiter Time2", M98088_REG_45_PWRLMT_TIME, 4, 15, 0), + + SOC_SINGLE("THD Limiter Threshold", M98088_REG_46_THDLMT_CFG, 4, 15, 0), + SOC_SINGLE("THD Limiter Time", M98088_REG_46_THDLMT_CFG, 0, 7, 0), +}; + +/* Left speaker mixer switch */ +static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2B_MIX_SPK_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2B_MIX_SPK_LEFT, 4, 1, 0), +}; + +/* Right speaker mixer switch */ +static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_2C_MIX_SPK_RIGHT, 4, 1, 0), +}; + +/* Left headphone mixer switch */ +static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_25_MIX_HP_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_25_MIX_HP_LEFT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_25_MIX_HP_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_25_MIX_HP_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_25_MIX_HP_LEFT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_25_MIX_HP_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_25_MIX_HP_LEFT, 4, 1, 0), +}; + +/* Right headphone mixer switch */ +static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_26_MIX_HP_RIGHT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_26_MIX_HP_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_26_MIX_HP_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_26_MIX_HP_RIGHT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_26_MIX_HP_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_26_MIX_HP_RIGHT, 4, 1, 0), +}; + +/* Left earpiece/receiver mixer switch */ +static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_28_MIX_REC_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_28_MIX_REC_LEFT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_28_MIX_REC_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_28_MIX_REC_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_28_MIX_REC_LEFT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_28_MIX_REC_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_28_MIX_REC_LEFT, 4, 1, 0), +}; + +/* Right earpiece/receiver mixer switch */ +static const struct snd_kcontrol_new max98088_right_rec_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("Left DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("Right DAC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_29_MIX_REC_RIGHT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_29_MIX_REC_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_29_MIX_REC_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_29_MIX_REC_RIGHT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_29_MIX_REC_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_29_MIX_REC_RIGHT, 4, 1, 0), +}; + +/* Left ADC mixer switch */ +static const struct snd_kcontrol_new max98088_left_ADC_mixer_controls[] = { + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_23_MIX_ADC_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_23_MIX_ADC_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_23_MIX_ADC_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_23_MIX_ADC_LEFT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_23_MIX_ADC_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_23_MIX_ADC_LEFT, 0, 1, 0), +}; + +/* Right ADC mixer switch */ +static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = { + SOC_DAPM_SINGLE("MIC1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("INA1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("INA2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 2, 1, 0), + SOC_DAPM_SINGLE("INB1 Switch", M98088_REG_24_MIX_ADC_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("INB2 Switch", M98088_REG_24_MIX_ADC_RIGHT, 0, 1, 0), +}; + +static int max98088_mic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->reg == M98088_REG_35_LVL_MIC1) { + snd_soc_component_update_bits(component, w->reg, M98088_MICPRE_MASK, + (1+max98088->mic1pre)<reg, M98088_MICPRE_MASK, + (1+max98088->mic2pre)<reg, M98088_MICPRE_MASK, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * The line inputs are 2-channel stereo inputs with the left + * and right channels sharing a common PGA power control signal. + */ +static int max98088_line_pga(struct snd_soc_dapm_widget *w, + int event, int line, u8 channel) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + u8 *state; + + if (WARN_ON(!(channel == 1 || channel == 2))) + return -EINVAL; + + switch (line) { + case LINE_INA: + state = &max98088->ina_state; + break; + case LINE_INB: + state = &max98088->inb_state; + break; + default: + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + *state |= channel; + snd_soc_component_update_bits(component, w->reg, + (1 << w->shift), (1 << w->shift)); + break; + case SND_SOC_DAPM_POST_PMD: + *state &= ~channel; + if (*state == 0) { + snd_soc_component_update_bits(component, w->reg, + (1 << w->shift), 0); + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int max98088_pga_ina1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98088_line_pga(w, event, LINE_INA, 1); +} + +static int max98088_pga_ina2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98088_line_pga(w, event, LINE_INA, 2); +} + +static int max98088_pga_inb1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98088_line_pga(w, event, LINE_INB, 1); +} + +static int max98088_pga_inb2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98088_line_pga(w, event, LINE_INB, 2); +} + +static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = { + + SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 1, 0), + SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98088_REG_4C_PWR_EN_IN, 0, 0), + + SND_SOC_DAPM_DAC("DACL1", "HiFi Playback", + M98088_REG_4D_PWR_EN_OUT, 1, 0), + SND_SOC_DAPM_DAC("DACR1", "HiFi Playback", + M98088_REG_4D_PWR_EN_OUT, 0, 0), + SND_SOC_DAPM_DAC("DACL2", "Aux Playback", + M98088_REG_4D_PWR_EN_OUT, 1, 0), + SND_SOC_DAPM_DAC("DACR2", "Aux Playback", + M98088_REG_4D_PWR_EN_OUT, 0, 0), + + SND_SOC_DAPM_PGA("HP Left Out", M98088_REG_4D_PWR_EN_OUT, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Right Out", M98088_REG_4D_PWR_EN_OUT, + 6, 0, NULL, 0), + + SND_SOC_DAPM_PGA("SPK Left Out", M98088_REG_4D_PWR_EN_OUT, + 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPK Right Out", M98088_REG_4D_PWR_EN_OUT, + 4, 0, NULL, 0), + + SND_SOC_DAPM_PGA("REC Left Out", M98088_REG_4D_PWR_EN_OUT, + 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("REC Right Out", M98088_REG_4D_PWR_EN_OUT, + 2, 0, NULL, 0), + + SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0, + &max98088_extmic_mux), + + SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0, + &max98088_left_hp_mixer_controls[0], + ARRAY_SIZE(max98088_left_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0, + &max98088_right_hp_mixer_controls[0], + ARRAY_SIZE(max98088_right_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left SPK Mixer", SND_SOC_NOPM, 0, 0, + &max98088_left_speaker_mixer_controls[0], + ARRAY_SIZE(max98088_left_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right SPK Mixer", SND_SOC_NOPM, 0, 0, + &max98088_right_speaker_mixer_controls[0], + ARRAY_SIZE(max98088_right_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left REC Mixer", SND_SOC_NOPM, 0, 0, + &max98088_left_rec_mixer_controls[0], + ARRAY_SIZE(max98088_left_rec_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right REC Mixer", SND_SOC_NOPM, 0, 0, + &max98088_right_rec_mixer_controls[0], + ARRAY_SIZE(max98088_right_rec_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98088_left_ADC_mixer_controls[0], + ARRAY_SIZE(max98088_left_ADC_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98088_right_ADC_mixer_controls[0], + ARRAY_SIZE(max98088_right_ADC_mixer_controls)), + + SND_SOC_DAPM_PGA_E("MIC1 Input", M98088_REG_35_LVL_MIC1, + 5, 0, NULL, 0, max98088_mic_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("MIC2 Input", M98088_REG_36_LVL_MIC2, + 5, 0, NULL, 0, max98088_mic_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("INA1 Input", M98088_REG_4C_PWR_EN_IN, + 7, 0, NULL, 0, max98088_pga_ina1_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("INA2 Input", M98088_REG_4C_PWR_EN_IN, + 7, 0, NULL, 0, max98088_pga_ina2_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("INB1 Input", M98088_REG_4C_PWR_EN_IN, + 6, 0, NULL, 0, max98088_pga_inb1_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("INB2 Input", M98088_REG_4C_PWR_EN_IN, + 6, 0, NULL, 0, max98088_pga_inb2_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS("MICBIAS", M98088_REG_4C_PWR_EN_IN, 3, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("RECL"), + SND_SOC_DAPM_OUTPUT("RECR"), + + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("INA1"), + SND_SOC_DAPM_INPUT("INA2"), + SND_SOC_DAPM_INPUT("INB1"), + SND_SOC_DAPM_INPUT("INB2"), +}; + +static const struct snd_soc_dapm_route max98088_audio_map[] = { + /* Left headphone output mixer */ + {"Left HP Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left HP Mixer", "Left DAC2 Switch", "DACL2"}, + {"Left HP Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left HP Mixer", "Right DAC2 Switch", "DACR2"}, + {"Left HP Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left HP Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left HP Mixer", "INA1 Switch", "INA1 Input"}, + {"Left HP Mixer", "INA2 Switch", "INA2 Input"}, + {"Left HP Mixer", "INB1 Switch", "INB1 Input"}, + {"Left HP Mixer", "INB2 Switch", "INB2 Input"}, + + /* Right headphone output mixer */ + {"Right HP Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right HP Mixer", "Left DAC2 Switch", "DACL2" }, + {"Right HP Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right HP Mixer", "Right DAC2 Switch", "DACR2"}, + {"Right HP Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right HP Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right HP Mixer", "INA1 Switch", "INA1 Input"}, + {"Right HP Mixer", "INA2 Switch", "INA2 Input"}, + {"Right HP Mixer", "INB1 Switch", "INB1 Input"}, + {"Right HP Mixer", "INB2 Switch", "INB2 Input"}, + + /* Left speaker output mixer */ + {"Left SPK Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left SPK Mixer", "Left DAC2 Switch", "DACL2"}, + {"Left SPK Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left SPK Mixer", "Right DAC2 Switch", "DACR2"}, + {"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left SPK Mixer", "INA1 Switch", "INA1 Input"}, + {"Left SPK Mixer", "INA2 Switch", "INA2 Input"}, + {"Left SPK Mixer", "INB1 Switch", "INB1 Input"}, + {"Left SPK Mixer", "INB2 Switch", "INB2 Input"}, + + /* Right speaker output mixer */ + {"Right SPK Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right SPK Mixer", "Left DAC2 Switch", "DACL2"}, + {"Right SPK Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right SPK Mixer", "Right DAC2 Switch", "DACR2"}, + {"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right SPK Mixer", "INA1 Switch", "INA1 Input"}, + {"Right SPK Mixer", "INA2 Switch", "INA2 Input"}, + {"Right SPK Mixer", "INB1 Switch", "INB1 Input"}, + {"Right SPK Mixer", "INB2 Switch", "INB2 Input"}, + + /* Earpiece/Receiver output mixer */ + {"Left REC Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left REC Mixer", "Left DAC2 Switch", "DACL2"}, + {"Left REC Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left REC Mixer", "Right DAC2 Switch", "DACR2"}, + {"Left REC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left REC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left REC Mixer", "INA1 Switch", "INA1 Input"}, + {"Left REC Mixer", "INA2 Switch", "INA2 Input"}, + {"Left REC Mixer", "INB1 Switch", "INB1 Input"}, + {"Left REC Mixer", "INB2 Switch", "INB2 Input"}, + + /* Earpiece/Receiver output mixer */ + {"Right REC Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right REC Mixer", "Left DAC2 Switch", "DACL2"}, + {"Right REC Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right REC Mixer", "Right DAC2 Switch", "DACR2"}, + {"Right REC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right REC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right REC Mixer", "INA1 Switch", "INA1 Input"}, + {"Right REC Mixer", "INA2 Switch", "INA2 Input"}, + {"Right REC Mixer", "INB1 Switch", "INB1 Input"}, + {"Right REC Mixer", "INB2 Switch", "INB2 Input"}, + + {"HP Left Out", NULL, "Left HP Mixer"}, + {"HP Right Out", NULL, "Right HP Mixer"}, + {"SPK Left Out", NULL, "Left SPK Mixer"}, + {"SPK Right Out", NULL, "Right SPK Mixer"}, + {"REC Left Out", NULL, "Left REC Mixer"}, + {"REC Right Out", NULL, "Right REC Mixer"}, + + {"HPL", NULL, "HP Left Out"}, + {"HPR", NULL, "HP Right Out"}, + {"SPKL", NULL, "SPK Left Out"}, + {"SPKR", NULL, "SPK Right Out"}, + {"RECL", NULL, "REC Left Out"}, + {"RECR", NULL, "REC Right Out"}, + + /* Left ADC input mixer */ + {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left ADC Mixer", "INA1 Switch", "INA1 Input"}, + {"Left ADC Mixer", "INA2 Switch", "INA2 Input"}, + {"Left ADC Mixer", "INB1 Switch", "INB1 Input"}, + {"Left ADC Mixer", "INB2 Switch", "INB2 Input"}, + + /* Right ADC input mixer */ + {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right ADC Mixer", "INA1 Switch", "INA1 Input"}, + {"Right ADC Mixer", "INA2 Switch", "INA2 Input"}, + {"Right ADC Mixer", "INB1 Switch", "INB1 Input"}, + {"Right ADC Mixer", "INB2 Switch", "INB2 Input"}, + + /* Inputs */ + {"ADCL", NULL, "Left ADC Mixer"}, + {"ADCR", NULL, "Right ADC Mixer"}, + {"INA1 Input", NULL, "INA1"}, + {"INA2 Input", NULL, "INA2"}, + {"INB1 Input", NULL, "INB1"}, + {"INB2 Input", NULL, "INB2"}, + {"MIC1 Input", NULL, "MIC1"}, + {"MIC2 Input", NULL, "MIC2"}, +}; + +/* codec mclk clock divider coefficients */ +static const struct { + u32 rate; + u8 sr; +} rate_table[] = { + {8000, 0x10}, + {11025, 0x20}, + {16000, 0x30}, + {22050, 0x40}, + {24000, 0x50}, + {32000, 0x60}, + {44100, 0x70}, + {48000, 0x80}, + {88200, 0x90}, + {96000, 0xA0}, +}; + +static inline int rate_value(int rate, u8 *value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i].rate >= rate) { + *value = rate_table[i].sr; + return 0; + } + } + *value = rate_table[0].sr; + return -EINVAL; +} + +static int max98088_dai1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + struct max98088_cdata *cdata; + unsigned long long ni; + unsigned int rate; + u8 regval; + + cdata = &max98088->dai[0]; + + rate = params_rate(params); + + switch (params_width(params)) { + case 16: + snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT, + M98088_DAI_WS, 0); + break; + case 24: + snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT, + M98088_DAI_WS, M98088_DAI_WS); + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0); + + if (rate_value(rate, ®val)) + return -EINVAL; + + snd_soc_component_update_bits(component, M98088_REG_11_DAI1_CLKMODE, + M98088_CLKMODE_MASK, regval); + cdata->rate = rate; + + /* Configure NI when operating as master */ + if (snd_soc_component_read(component, M98088_REG_14_DAI1_FORMAT) + & M98088_DAI_MAS) { + unsigned long pclk; + + if (max98088->sysclk == 0) { + dev_err(component->dev, "Invalid system clock frequency\n"); + return -EINVAL; + } + ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)rate; + pclk = DIV_ROUND_CLOSEST(max98088->sysclk, max98088->mclk_prescaler); + ni = DIV_ROUND_CLOSEST_ULL(ni, pclk); + snd_soc_component_write(component, M98088_REG_12_DAI1_CLKCFG_HI, + (ni >> 8) & 0x7F); + snd_soc_component_write(component, M98088_REG_13_DAI1_CLKCFG_LO, + ni & 0xFF); + } + + /* Update sample rate mode */ + if (rate < 50000) + snd_soc_component_update_bits(component, M98088_REG_18_DAI1_FILTERS, + M98088_DAI_DHF, 0); + else + snd_soc_component_update_bits(component, M98088_REG_18_DAI1_FILTERS, + M98088_DAI_DHF, M98088_DAI_DHF); + + snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, + M98088_SHDNRUN); + + return 0; +} + +static int max98088_dai2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + struct max98088_cdata *cdata; + unsigned long long ni; + unsigned int rate; + u8 regval; + + cdata = &max98088->dai[1]; + + rate = params_rate(params); + + switch (params_width(params)) { + case 16: + snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT, + M98088_DAI_WS, 0); + break; + case 24: + snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT, + M98088_DAI_WS, M98088_DAI_WS); + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0); + + if (rate_value(rate, ®val)) + return -EINVAL; + + snd_soc_component_update_bits(component, M98088_REG_19_DAI2_CLKMODE, + M98088_CLKMODE_MASK, regval); + cdata->rate = rate; + + /* Configure NI when operating as master */ + if (snd_soc_component_read(component, M98088_REG_1C_DAI2_FORMAT) + & M98088_DAI_MAS) { + unsigned long pclk; + + if (max98088->sysclk == 0) { + dev_err(component->dev, "Invalid system clock frequency\n"); + return -EINVAL; + } + ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)rate; + pclk = DIV_ROUND_CLOSEST(max98088->sysclk, max98088->mclk_prescaler); + ni = DIV_ROUND_CLOSEST_ULL(ni, pclk); + snd_soc_component_write(component, M98088_REG_1A_DAI2_CLKCFG_HI, + (ni >> 8) & 0x7F); + snd_soc_component_write(component, M98088_REG_1B_DAI2_CLKCFG_LO, + ni & 0xFF); + } + + /* Update sample rate mode */ + if (rate < 50000) + snd_soc_component_update_bits(component, M98088_REG_20_DAI2_FILTERS, + M98088_DAI_DHF, 0); + else + snd_soc_component_update_bits(component, M98088_REG_20_DAI2_FILTERS, + M98088_DAI_DHF, M98088_DAI_DHF); + + snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, + M98088_SHDNRUN); + + return 0; +} + +static int max98088_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + + /* Requested clock frequency is already setup */ + if (freq == max98088->sysclk) + return 0; + + if (!IS_ERR(max98088->mclk)) { + freq = clk_round_rate(max98088->mclk, freq); + clk_set_rate(max98088->mclk, freq); + } + + /* Setup clocks for slave mode, and using the PLL + * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) + * 0x02 (when master clk is 20MHz to 30MHz).. + */ + if ((freq >= 10000000) && (freq < 20000000)) { + snd_soc_component_write(component, M98088_REG_10_SYS_CLK, 0x10); + max98088->mclk_prescaler = 1; + } else if ((freq >= 20000000) && (freq < 30000000)) { + snd_soc_component_write(component, M98088_REG_10_SYS_CLK, 0x20); + max98088->mclk_prescaler = 2; + } else { + dev_err(component->dev, "Invalid master clock frequency\n"); + return -EINVAL; + } + + if (snd_soc_component_read(component, M98088_REG_51_PWR_SYS) & M98088_SHDNRUN) { + snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, + M98088_SHDNRUN, 0); + snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, + M98088_SHDNRUN, M98088_SHDNRUN); + } + + dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq); + + max98088->sysclk = freq; + return 0; +} + +static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + struct max98088_cdata *cdata; + u8 reg15val; + u8 reg14val = 0; + + cdata = &max98088->dai[0]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Consumer mode PLL */ + snd_soc_component_write(component, M98088_REG_12_DAI1_CLKCFG_HI, + 0x80); + snd_soc_component_write(component, M98088_REG_13_DAI1_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ + reg14val |= M98088_DAI_MAS; + break; + default: + dev_err(component->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + reg14val |= M98088_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + reg14val |= M98088_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + reg14val |= M98088_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + reg14val |= M98088_DAI_BCI|M98088_DAI_WCI; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT, + M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI | + M98088_DAI_WCI, reg14val); + + reg15val = M98088_DAI_BSEL64; + if (max98088->digmic) + reg15val |= M98088_DAI_OSR64; + snd_soc_component_write(component, M98088_REG_15_DAI1_CLOCK, reg15val); + } + + return 0; +} + +static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + struct max98088_cdata *cdata; + u8 reg1Cval = 0; + + cdata = &max98088->dai[1]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Consumer mode PLL */ + snd_soc_component_write(component, M98088_REG_1A_DAI2_CLKCFG_HI, + 0x80); + snd_soc_component_write(component, M98088_REG_1B_DAI2_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ + reg1Cval |= M98088_DAI_MAS; + break; + default: + dev_err(component->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + reg1Cval |= M98088_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + reg1Cval |= M98088_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + reg1Cval |= M98088_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + reg1Cval |= M98088_DAI_BCI|M98088_DAI_WCI; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT, + M98088_DAI_MAS | M98088_DAI_DLY | M98088_DAI_BCI | + M98088_DAI_WCI, reg1Cval); + + snd_soc_component_write(component, M98088_REG_1D_DAI2_CLOCK, + M98088_DAI_BSEL64); + } + + return 0; +} + +static int max98088_dai1_mute(struct snd_soc_dai *codec_dai, int mute, + int direction) +{ + struct snd_soc_component *component = codec_dai->component; + int reg; + + if (mute) + reg = M98088_DAI_MUTE; + else + reg = 0; + + snd_soc_component_update_bits(component, M98088_REG_2F_LVL_DAI1_PLAY, + M98088_DAI_MUTE_MASK, reg); + return 0; +} + +static int max98088_dai2_mute(struct snd_soc_dai *codec_dai, int mute, + int direction) +{ + struct snd_soc_component *component = codec_dai->component; + int reg; + + if (mute) + reg = M98088_DAI_MUTE; + else + reg = 0; + + snd_soc_component_update_bits(component, M98088_REG_31_LVL_DAI2_PLAY, + M98088_DAI_MUTE_MASK, reg); + return 0; +} + +static int max98088_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (!IS_ERR(max98088->mclk)) { + if (snd_soc_component_get_bias_level(component) == + SND_SOC_BIAS_ON) + clk_disable_unprepare(max98088->mclk); + else + clk_prepare_enable(max98088->mclk); + } + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + regcache_sync(max98088->regmap); + + snd_soc_component_update_bits(component, M98088_REG_4C_PWR_EN_IN, + M98088_MBEN, M98088_MBEN); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, M98088_REG_4C_PWR_EN_IN, + M98088_MBEN, 0); + regcache_mark_dirty(max98088->regmap); + break; + } + return 0; +} + +#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000 +#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops max98088_dai1_ops = { + .set_sysclk = max98088_dai_set_sysclk, + .set_fmt = max98088_dai1_set_fmt, + .hw_params = max98088_dai1_hw_params, + .mute_stream = max98088_dai1_mute, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops max98088_dai2_ops = { + .set_sysclk = max98088_dai_set_sysclk, + .set_fmt = max98088_dai2_set_fmt, + .hw_params = max98088_dai2_hw_params, + .mute_stream = max98088_dai2_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver max98088_dai[] = { +{ + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98088_RATES, + .formats = MAX98088_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98088_RATES, + .formats = MAX98088_FORMATS, + }, + .ops = &max98088_dai1_ops, +}, +{ + .name = "Aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98088_RATES, + .formats = MAX98088_FORMATS, + }, + .ops = &max98088_dai2_ops, +} +}; + +static const char *eq_mode_name[] = {"EQ1 Mode", "EQ2 Mode"}; + +static int max98088_get_channel(struct snd_soc_component *component, const char *name) +{ + int ret; + + ret = match_string(eq_mode_name, ARRAY_SIZE(eq_mode_name), name); + if (ret < 0) + dev_err(component->dev, "Bad EQ channel name '%s'\n", name); + return ret; +} + +static void max98088_setup_eq1(struct snd_soc_component *component) +{ + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + struct max98088_pdata *pdata = max98088->pdata; + struct max98088_eq_cfg *coef_set; + int best, best_val, save, i, sel, fs; + struct max98088_cdata *cdata; + + cdata = &max98088->dai[0]; + + if (!pdata || !max98088->eq_textcnt) + return; + + /* Find the selected configuration with nearest sample rate */ + fs = cdata->rate; + sel = cdata->eq_sel; + + best = 0; + best_val = INT_MAX; + for (i = 0; i < pdata->eq_cfgcnt; i++) { + if (strcmp(pdata->eq_cfg[i].name, max98088->eq_texts[sel]) == 0 && + abs(pdata->eq_cfg[i].rate - fs) < best_val) { + best = i; + best_val = abs(pdata->eq_cfg[i].rate - fs); + } + } + + dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n", + pdata->eq_cfg[best].name, + pdata->eq_cfg[best].rate, fs); + + /* Disable EQ while configuring, and save current on/off state */ + save = snd_soc_component_read(component, M98088_REG_49_CFG_LEVEL); + snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0); + + coef_set = &pdata->eq_cfg[sel]; + + m98088_eq_band(component, 0, 0, coef_set->band1); + m98088_eq_band(component, 0, 1, coef_set->band2); + m98088_eq_band(component, 0, 2, coef_set->band3); + m98088_eq_band(component, 0, 3, coef_set->band4); + m98088_eq_band(component, 0, 4, coef_set->band5); + + /* Restore the original on/off state */ + snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save); +} + +static void max98088_setup_eq2(struct snd_soc_component *component) +{ + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + struct max98088_pdata *pdata = max98088->pdata; + struct max98088_eq_cfg *coef_set; + int best, best_val, save, i, sel, fs; + struct max98088_cdata *cdata; + + cdata = &max98088->dai[1]; + + if (!pdata || !max98088->eq_textcnt) + return; + + /* Find the selected configuration with nearest sample rate */ + fs = cdata->rate; + + sel = cdata->eq_sel; + best = 0; + best_val = INT_MAX; + for (i = 0; i < pdata->eq_cfgcnt; i++) { + if (strcmp(pdata->eq_cfg[i].name, max98088->eq_texts[sel]) == 0 && + abs(pdata->eq_cfg[i].rate - fs) < best_val) { + best = i; + best_val = abs(pdata->eq_cfg[i].rate - fs); + } + } + + dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n", + pdata->eq_cfg[best].name, + pdata->eq_cfg[best].rate, fs); + + /* Disable EQ while configuring, and save current on/off state */ + save = snd_soc_component_read(component, M98088_REG_49_CFG_LEVEL); + snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0); + + coef_set = &pdata->eq_cfg[sel]; + + m98088_eq_band(component, 1, 0, coef_set->band1); + m98088_eq_band(component, 1, 1, coef_set->band2); + m98088_eq_band(component, 1, 2, coef_set->band3); + m98088_eq_band(component, 1, 3, coef_set->band4); + m98088_eq_band(component, 1, 4, coef_set->band5); + + /* Restore the original on/off state */ + snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, + save); +} + +static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + struct max98088_pdata *pdata = max98088->pdata; + int channel = max98088_get_channel(component, kcontrol->id.name); + struct max98088_cdata *cdata; + int sel = ucontrol->value.enumerated.item[0]; + + if (channel < 0) + return channel; + + cdata = &max98088->dai[channel]; + + if (sel >= pdata->eq_cfgcnt) + return -EINVAL; + + cdata->eq_sel = sel; + + switch (channel) { + case 0: + max98088_setup_eq1(component); + break; + case 1: + max98088_setup_eq2(component); + break; + } + + return 0; +} + +static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + int channel = max98088_get_channel(component, kcontrol->id.name); + struct max98088_cdata *cdata; + + if (channel < 0) + return channel; + + cdata = &max98088->dai[channel]; + ucontrol->value.enumerated.item[0] = cdata->eq_sel; + return 0; +} + +static void max98088_handle_eq_pdata(struct snd_soc_component *component) +{ + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + struct max98088_pdata *pdata = max98088->pdata; + struct max98088_eq_cfg *cfg; + unsigned int cfgcnt; + int i, j; + const char **t; + int ret; + struct snd_kcontrol_new controls[] = { + SOC_ENUM_EXT((char *)eq_mode_name[0], + max98088->eq_enum, + max98088_get_eq_enum, + max98088_put_eq_enum), + SOC_ENUM_EXT((char *)eq_mode_name[1], + max98088->eq_enum, + max98088_get_eq_enum, + max98088_put_eq_enum), + }; + BUILD_BUG_ON(ARRAY_SIZE(controls) != ARRAY_SIZE(eq_mode_name)); + + cfg = pdata->eq_cfg; + cfgcnt = pdata->eq_cfgcnt; + + /* Setup an array of texts for the equalizer enum. + * This is based on Mark Brown's equalizer driver code. + */ + max98088->eq_textcnt = 0; + max98088->eq_texts = NULL; + for (i = 0; i < cfgcnt; i++) { + for (j = 0; j < max98088->eq_textcnt; j++) { + if (strcmp(cfg[i].name, max98088->eq_texts[j]) == 0) + break; + } + + if (j != max98088->eq_textcnt) + continue; + + /* Expand the array */ + t = krealloc(max98088->eq_texts, + sizeof(char *) * (max98088->eq_textcnt + 1), + GFP_KERNEL); + if (t == NULL) + continue; + + /* Store the new entry */ + t[max98088->eq_textcnt] = cfg[i].name; + max98088->eq_textcnt++; + max98088->eq_texts = t; + } + + /* Now point the soc_enum to .texts array items */ + max98088->eq_enum.texts = max98088->eq_texts; + max98088->eq_enum.items = max98088->eq_textcnt; + + ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls)); + if (ret != 0) + dev_err(component->dev, "Failed to add EQ control: %d\n", ret); +} + +static void max98088_handle_pdata(struct snd_soc_component *component) +{ + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + struct max98088_pdata *pdata = max98088->pdata; + u8 regval = 0; + + if (!pdata) { + dev_dbg(component->dev, "No platform data\n"); + return; + } + + /* Configure mic for analog/digital mic mode */ + if (pdata->digmic_left_mode) + regval |= M98088_DIGMIC_L; + + if (pdata->digmic_right_mode) + regval |= M98088_DIGMIC_R; + + max98088->digmic = (regval ? 1 : 0); + + snd_soc_component_write(component, M98088_REG_48_CFG_MIC, regval); + + /* Configure receiver output */ + regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0); + snd_soc_component_update_bits(component, M98088_REG_2A_MIC_REC_CNTL, + M98088_REC_LINEMODE_MASK, regval); + + /* Configure equalizers */ + if (pdata->eq_cfgcnt) + max98088_handle_eq_pdata(component); +} + +static int max98088_probe(struct snd_soc_component *component) +{ + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + struct max98088_cdata *cdata; + int ret = 0; + + regcache_mark_dirty(max98088->regmap); + + /* initialize private data */ + + max98088->sysclk = (unsigned)-1; + max98088->eq_textcnt = 0; + + cdata = &max98088->dai[0]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + cdata->eq_sel = 0; + + cdata = &max98088->dai[1]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + cdata->eq_sel = 0; + + max98088->ina_state = 0; + max98088->inb_state = 0; + max98088->ex_mode = 0; + max98088->digmic = 0; + max98088->mic1pre = 0; + max98088->mic2pre = 0; + + ret = snd_soc_component_read(component, M98088_REG_FF_REV_ID); + if (ret < 0) { + dev_err(component->dev, "Failed to read device revision: %d\n", + ret); + goto err_access; + } + dev_info(component->dev, "revision %c\n", ret - 0x40 + 'A'); + + snd_soc_component_write(component, M98088_REG_51_PWR_SYS, M98088_PWRSV); + + snd_soc_component_write(component, M98088_REG_0F_IRQ_ENABLE, 0x00); + + snd_soc_component_write(component, M98088_REG_22_MIX_DAC, + M98088_DAI1L_TO_DACL|M98088_DAI2L_TO_DACL| + M98088_DAI1R_TO_DACR|M98088_DAI2R_TO_DACR); + + snd_soc_component_write(component, M98088_REG_4E_BIAS_CNTL, 0xF0); + snd_soc_component_write(component, M98088_REG_50_DAC_BIAS2, 0x0F); + + snd_soc_component_write(component, M98088_REG_16_DAI1_IOCFG, + M98088_S1NORMAL|M98088_SDATA); + + snd_soc_component_write(component, M98088_REG_1E_DAI2_IOCFG, + M98088_S2NORMAL|M98088_SDATA); + + max98088_handle_pdata(component); + +err_access: + return ret; +} + +static void max98088_remove(struct snd_soc_component *component) +{ + struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component); + + kfree(max98088->eq_texts); +} + +static const struct snd_soc_component_driver soc_component_dev_max98088 = { + .probe = max98088_probe, + .remove = max98088_remove, + .set_bias_level = max98088_set_bias_level, + .controls = max98088_snd_controls, + .num_controls = ARRAY_SIZE(max98088_snd_controls), + .dapm_widgets = max98088_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets), + .dapm_routes = max98088_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98088_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct i2c_device_id max98088_i2c_id[] = { + { "max98088", MAX98088 }, + { "max98089", MAX98089 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98088_i2c_id); + +static int max98088_i2c_probe(struct i2c_client *i2c) +{ + struct max98088_priv *max98088; + const struct i2c_device_id *id; + + max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv), + GFP_KERNEL); + if (max98088 == NULL) + return -ENOMEM; + + max98088->regmap = devm_regmap_init_i2c(i2c, &max98088_regmap); + if (IS_ERR(max98088->regmap)) + return PTR_ERR(max98088->regmap); + + max98088->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (IS_ERR(max98088->mclk)) + if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER) + return PTR_ERR(max98088->mclk); + + id = i2c_match_id(max98088_i2c_id, i2c); + max98088->devtype = id->driver_data; + + i2c_set_clientdata(i2c, max98088); + max98088->pdata = i2c->dev.platform_data; + + return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_max98088, + &max98088_dai[0], 2); +} + +#if defined(CONFIG_OF) +static const struct of_device_id max98088_of_match[] = { + { .compatible = "maxim,max98088" }, + { .compatible = "maxim,max98089" }, + { } +}; +MODULE_DEVICE_TABLE(of, max98088_of_match); +#endif + +static struct i2c_driver max98088_i2c_driver = { + .driver = { + .name = "max98088", + .of_match_table = of_match_ptr(max98088_of_match), + }, + .probe = max98088_i2c_probe, + .id_table = max98088_i2c_id, +}; + +module_i2c_driver(max98088_i2c_driver); + +MODULE_DESCRIPTION("ALSA SoC MAX98088 driver"); +MODULE_AUTHOR("Peter Hsiang, Jesse Marroquin"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h new file mode 100644 index 0000000000..4190e5ff38 --- /dev/null +++ b/sound/soc/codecs/max98088.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * max98088.h -- MAX98088 ALSA SoC Audio driver + * + * Copyright 2010 Maxim Integrated Products + */ + +#ifndef _MAX98088_H +#define _MAX98088_H + +/* + * MAX98088 Registers Definition + */ +#define M98088_REG_00_IRQ_STATUS 0x00 +#define M98088_REG_01_MIC_STATUS 0x01 +#define M98088_REG_02_JACK_STATUS 0x02 +#define M98088_REG_03_BATTERY_VOLTAGE 0x03 +#define M98088_REG_0F_IRQ_ENABLE 0x0F +#define M98088_REG_10_SYS_CLK 0x10 +#define M98088_REG_11_DAI1_CLKMODE 0x11 +#define M98088_REG_12_DAI1_CLKCFG_HI 0x12 +#define M98088_REG_13_DAI1_CLKCFG_LO 0x13 +#define M98088_REG_14_DAI1_FORMAT 0x14 +#define M98088_REG_15_DAI1_CLOCK 0x15 +#define M98088_REG_16_DAI1_IOCFG 0x16 +#define M98088_REG_17_DAI1_TDM 0x17 +#define M98088_REG_18_DAI1_FILTERS 0x18 +#define M98088_REG_19_DAI2_CLKMODE 0x19 +#define M98088_REG_1A_DAI2_CLKCFG_HI 0x1A +#define M98088_REG_1B_DAI2_CLKCFG_LO 0x1B +#define M98088_REG_1C_DAI2_FORMAT 0x1C +#define M98088_REG_1D_DAI2_CLOCK 0x1D +#define M98088_REG_1E_DAI2_IOCFG 0x1E +#define M98088_REG_1F_DAI2_TDM 0x1F +#define M98088_REG_20_DAI2_FILTERS 0x20 +#define M98088_REG_21_SRC 0x21 +#define M98088_REG_22_MIX_DAC 0x22 +#define M98088_REG_23_MIX_ADC_LEFT 0x23 +#define M98088_REG_24_MIX_ADC_RIGHT 0x24 +#define M98088_REG_25_MIX_HP_LEFT 0x25 +#define M98088_REG_26_MIX_HP_RIGHT 0x26 +#define M98088_REG_27_MIX_HP_CNTL 0x27 +#define M98088_REG_28_MIX_REC_LEFT 0x28 +#define M98088_REG_29_MIX_REC_RIGHT 0x29 +#define M98088_REG_2A_MIC_REC_CNTL 0x2A +#define M98088_REG_2B_MIX_SPK_LEFT 0x2B +#define M98088_REG_2C_MIX_SPK_RIGHT 0x2C +#define M98088_REG_2D_MIX_SPK_CNTL 0x2D +#define M98088_REG_2E_LVL_SIDETONE 0x2E +#define M98088_REG_2F_LVL_DAI1_PLAY 0x2F +#define M98088_REG_30_LVL_DAI1_PLAY_EQ 0x30 +#define M98088_REG_31_LVL_DAI2_PLAY 0x31 +#define M98088_REG_32_LVL_DAI2_PLAY_EQ 0x32 +#define M98088_REG_33_LVL_ADC_L 0x33 +#define M98088_REG_34_LVL_ADC_R 0x34 +#define M98088_REG_35_LVL_MIC1 0x35 +#define M98088_REG_36_LVL_MIC2 0x36 +#define M98088_REG_37_LVL_INA 0x37 +#define M98088_REG_38_LVL_INB 0x38 +#define M98088_REG_39_LVL_HP_L 0x39 +#define M98088_REG_3A_LVL_HP_R 0x3A +#define M98088_REG_3B_LVL_REC_L 0x3B +#define M98088_REG_3C_LVL_REC_R 0x3C +#define M98088_REG_3D_LVL_SPK_L 0x3D +#define M98088_REG_3E_LVL_SPK_R 0x3E +#define M98088_REG_3F_MICAGC_CFG 0x3F +#define M98088_REG_40_MICAGC_THRESH 0x40 +#define M98088_REG_41_SPKDHP 0x41 +#define M98088_REG_42_SPKDHP_THRESH 0x42 +#define M98088_REG_43_SPKALC_COMP 0x43 +#define M98088_REG_44_PWRLMT_CFG 0x44 +#define M98088_REG_45_PWRLMT_TIME 0x45 +#define M98088_REG_46_THDLMT_CFG 0x46 +#define M98088_REG_47_CFG_AUDIO_IN 0x47 +#define M98088_REG_48_CFG_MIC 0x48 +#define M98088_REG_49_CFG_LEVEL 0x49 +#define M98088_REG_4A_CFG_BYPASS 0x4A +#define M98088_REG_4B_CFG_JACKDET 0x4B +#define M98088_REG_4C_PWR_EN_IN 0x4C +#define M98088_REG_4D_PWR_EN_OUT 0x4D +#define M98088_REG_4E_BIAS_CNTL 0x4E +#define M98088_REG_4F_DAC_BIAS1 0x4F +#define M98088_REG_50_DAC_BIAS2 0x50 +#define M98088_REG_51_PWR_SYS 0x51 +#define M98088_REG_52_DAI1_EQ_BASE 0x52 +#define M98088_REG_84_DAI2_EQ_BASE 0x84 +#define M98088_REG_B6_DAI1_BIQUAD_BASE 0xB6 +#define M98088_REG_C0_DAI2_BIQUAD_BASE 0xC0 +#define M98088_REG_FF_REV_ID 0xFF + +#define M98088_REG_CNT (0xFF+1) + +/* MAX98088 Registers Bit Fields */ + +/* M98088_REG_11_DAI1_CLKMODE, M98088_REG_19_DAI2_CLKMODE */ + #define M98088_CLKMODE_MASK 0xFF + +/* M98088_REG_14_DAI1_FORMAT, M98088_REG_1C_DAI2_FORMAT */ + #define M98088_DAI_MAS (1<<7) + #define M98088_DAI_WCI (1<<6) + #define M98088_DAI_BCI (1<<5) + #define M98088_DAI_DLY (1<<4) + #define M98088_DAI_TDM (1<<2) + #define M98088_DAI_FSW (1<<1) + #define M98088_DAI_WS (1<<0) + +/* M98088_REG_15_DAI1_CLOCK, M98088_REG_1D_DAI2_CLOCK */ + #define M98088_DAI_BSEL64 (1<<0) + #define M98088_DAI_OSR64 (1<<6) + +/* M98088_REG_16_DAI1_IOCFG, M98088_REG_1E_DAI2_IOCFG */ + #define M98088_S1NORMAL (1<<6) + #define M98088_S2NORMAL (2<<6) + #define M98088_SDATA (3<<0) + +/* M98088_REG_18_DAI1_FILTERS, M98088_REG_20_DAI2_FILTERS */ + #define M98088_DAI_DHF (1<<3) + +/* M98088_REG_22_MIX_DAC */ + #define M98088_DAI1L_TO_DACL (1<<7) + #define M98088_DAI1R_TO_DACL (1<<6) + #define M98088_DAI2L_TO_DACL (1<<5) + #define M98088_DAI2R_TO_DACL (1<<4) + #define M98088_DAI1L_TO_DACR (1<<3) + #define M98088_DAI1R_TO_DACR (1<<2) + #define M98088_DAI2L_TO_DACR (1<<1) + #define M98088_DAI2R_TO_DACR (1<<0) + +/* M98088_REG_2A_MIC_REC_CNTL */ + #define M98088_REC_LINEMODE (1<<7) + #define M98088_REC_LINEMODE_MASK (1<<7) + +/* M98088_REG_2D_MIX_SPK_CNTL */ + #define M98088_MIX_SPKR_GAIN_MASK (3<<2) + #define M98088_MIX_SPKR_GAIN_SHIFT 2 + #define M98088_MIX_SPKL_GAIN_MASK (3<<0) + #define M98088_MIX_SPKL_GAIN_SHIFT 0 + +/* M98088_REG_2F_LVL_DAI1_PLAY, M98088_REG_31_LVL_DAI2_PLAY */ + #define M98088_DAI_MUTE (1<<7) + #define M98088_DAI_MUTE_MASK (1<<7) + #define M98088_DAI_VOICE_GAIN_MASK (3<<4) + #define M98088_DAI_ATTENUATION_MASK (0xF<<0) + #define M98088_DAI_ATTENUATION_SHIFT 0 + +/* M98088_REG_35_LVL_MIC1, M98088_REG_36_LVL_MIC2 */ + #define M98088_MICPRE_MASK (3<<5) + #define M98088_MICPRE_SHIFT 5 + +/* M98088_REG_3A_LVL_HP_R */ + #define M98088_HP_MUTE (1<<7) + +/* M98088_REG_3C_LVL_REC_R */ + #define M98088_REC_MUTE (1<<7) + +/* M98088_REG_3E_LVL_SPK_R */ + #define M98088_SP_MUTE (1<<7) + +/* M98088_REG_48_CFG_MIC */ + #define M98088_EXTMIC_MASK (3<<0) + #define M98088_DIGMIC_L (1<<5) + #define M98088_DIGMIC_R (1<<4) + +/* M98088_REG_49_CFG_LEVEL */ + #define M98088_VSEN (1<<6) + #define M98088_ZDEN (1<<5) + #define M98088_EQ2EN (1<<1) + #define M98088_EQ1EN (1<<0) + +/* M98088_REG_4C_PWR_EN_IN */ + #define M98088_INAEN (1<<7) + #define M98088_INBEN (1<<6) + #define M98088_MBEN (1<<3) + #define M98088_ADLEN (1<<1) + #define M98088_ADREN (1<<0) + +/* M98088_REG_4D_PWR_EN_OUT */ + #define M98088_HPLEN (1<<7) + #define M98088_HPREN (1<<6) + #define M98088_HPEN ((1<<7)|(1<<6)) + #define M98088_SPLEN (1<<5) + #define M98088_SPREN (1<<4) + #define M98088_RECEN (1<<3) + #define M98088_DALEN (1<<1) + #define M98088_DAREN (1<<0) + +/* M98088_REG_51_PWR_SYS */ + #define M98088_SHDNRUN (1<<7) + #define M98088_PERFMODE (1<<3) + #define M98088_HPPLYBACK (1<<2) + #define M98088_PWRSV8K (1<<1) + #define M98088_PWRSV (1<<0) + +/* Line inputs */ +#define LINE_INA 0 +#define LINE_INB 1 + +#define M98088_COEFS_PER_BAND 5 + +#define M98088_BYTE1(w) ((w >> 8) & 0xff) +#define M98088_BYTE0(w) (w & 0xff) + +#endif diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c new file mode 100644 index 0000000000..2adf744c65 --- /dev/null +++ b/sound/soc/codecs/max98090.c @@ -0,0 +1,2706 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * max98090.c -- MAX98090 ALSA SoC Audio driver + * + * Copyright 2011-2012 Maxim Integrated Products + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98090.h" + +/* Allows for sparsely populated register maps */ +static const struct reg_default max98090_reg[] = { + { 0x00, 0x00 }, /* 00 Software Reset */ + { 0x03, 0x04 }, /* 03 Interrupt Masks */ + { 0x04, 0x00 }, /* 04 System Clock Quick */ + { 0x05, 0x00 }, /* 05 Sample Rate Quick */ + { 0x06, 0x00 }, /* 06 DAI Interface Quick */ + { 0x07, 0x00 }, /* 07 DAC Path Quick */ + { 0x08, 0x00 }, /* 08 Mic/Direct to ADC Quick */ + { 0x09, 0x00 }, /* 09 Line to ADC Quick */ + { 0x0A, 0x00 }, /* 0A Analog Mic Loop Quick */ + { 0x0B, 0x00 }, /* 0B Analog Line Loop Quick */ + { 0x0C, 0x00 }, /* 0C Reserved */ + { 0x0D, 0x00 }, /* 0D Input Config */ + { 0x0E, 0x1B }, /* 0E Line Input Level */ + { 0x0F, 0x00 }, /* 0F Line Config */ + + { 0x10, 0x14 }, /* 10 Mic1 Input Level */ + { 0x11, 0x14 }, /* 11 Mic2 Input Level */ + { 0x12, 0x00 }, /* 12 Mic Bias Voltage */ + { 0x13, 0x00 }, /* 13 Digital Mic Config */ + { 0x14, 0x00 }, /* 14 Digital Mic Mode */ + { 0x15, 0x00 }, /* 15 Left ADC Mixer */ + { 0x16, 0x00 }, /* 16 Right ADC Mixer */ + { 0x17, 0x03 }, /* 17 Left ADC Level */ + { 0x18, 0x03 }, /* 18 Right ADC Level */ + { 0x19, 0x00 }, /* 19 ADC Biquad Level */ + { 0x1A, 0x00 }, /* 1A ADC Sidetone */ + { 0x1B, 0x00 }, /* 1B System Clock */ + { 0x1C, 0x00 }, /* 1C Clock Mode */ + { 0x1D, 0x00 }, /* 1D Any Clock 1 */ + { 0x1E, 0x00 }, /* 1E Any Clock 2 */ + { 0x1F, 0x00 }, /* 1F Any Clock 3 */ + + { 0x20, 0x00 }, /* 20 Any Clock 4 */ + { 0x21, 0x00 }, /* 21 Master Mode */ + { 0x22, 0x00 }, /* 22 Interface Format */ + { 0x23, 0x00 }, /* 23 TDM Format 1*/ + { 0x24, 0x00 }, /* 24 TDM Format 2*/ + { 0x25, 0x00 }, /* 25 I/O Configuration */ + { 0x26, 0x80 }, /* 26 Filter Config */ + { 0x27, 0x00 }, /* 27 DAI Playback Level */ + { 0x28, 0x00 }, /* 28 EQ Playback Level */ + { 0x29, 0x00 }, /* 29 Left HP Mixer */ + { 0x2A, 0x00 }, /* 2A Right HP Mixer */ + { 0x2B, 0x00 }, /* 2B HP Control */ + { 0x2C, 0x1A }, /* 2C Left HP Volume */ + { 0x2D, 0x1A }, /* 2D Right HP Volume */ + { 0x2E, 0x00 }, /* 2E Left Spk Mixer */ + { 0x2F, 0x00 }, /* 2F Right Spk Mixer */ + + { 0x30, 0x00 }, /* 30 Spk Control */ + { 0x31, 0x2C }, /* 31 Left Spk Volume */ + { 0x32, 0x2C }, /* 32 Right Spk Volume */ + { 0x33, 0x00 }, /* 33 ALC Timing */ + { 0x34, 0x00 }, /* 34 ALC Compressor */ + { 0x35, 0x00 }, /* 35 ALC Expander */ + { 0x36, 0x00 }, /* 36 ALC Gain */ + { 0x37, 0x00 }, /* 37 Rcv/Line OutL Mixer */ + { 0x38, 0x00 }, /* 38 Rcv/Line OutL Control */ + { 0x39, 0x15 }, /* 39 Rcv/Line OutL Volume */ + { 0x3A, 0x00 }, /* 3A Line OutR Mixer */ + { 0x3B, 0x00 }, /* 3B Line OutR Control */ + { 0x3C, 0x15 }, /* 3C Line OutR Volume */ + { 0x3D, 0x00 }, /* 3D Jack Detect */ + { 0x3E, 0x00 }, /* 3E Input Enable */ + { 0x3F, 0x00 }, /* 3F Output Enable */ + + { 0x40, 0x00 }, /* 40 Level Control */ + { 0x41, 0x00 }, /* 41 DSP Filter Enable */ + { 0x42, 0x00 }, /* 42 Bias Control */ + { 0x43, 0x00 }, /* 43 DAC Control */ + { 0x44, 0x06 }, /* 44 ADC Control */ + { 0x45, 0x00 }, /* 45 Device Shutdown */ + { 0x46, 0x00 }, /* 46 Equalizer Band 1 Coefficient B0 */ + { 0x47, 0x00 }, /* 47 Equalizer Band 1 Coefficient B0 */ + { 0x48, 0x00 }, /* 48 Equalizer Band 1 Coefficient B0 */ + { 0x49, 0x00 }, /* 49 Equalizer Band 1 Coefficient B1 */ + { 0x4A, 0x00 }, /* 4A Equalizer Band 1 Coefficient B1 */ + { 0x4B, 0x00 }, /* 4B Equalizer Band 1 Coefficient B1 */ + { 0x4C, 0x00 }, /* 4C Equalizer Band 1 Coefficient B2 */ + { 0x4D, 0x00 }, /* 4D Equalizer Band 1 Coefficient B2 */ + { 0x4E, 0x00 }, /* 4E Equalizer Band 1 Coefficient B2 */ + { 0x4F, 0x00 }, /* 4F Equalizer Band 1 Coefficient A1 */ + + { 0x50, 0x00 }, /* 50 Equalizer Band 1 Coefficient A1 */ + { 0x51, 0x00 }, /* 51 Equalizer Band 1 Coefficient A1 */ + { 0x52, 0x00 }, /* 52 Equalizer Band 1 Coefficient A2 */ + { 0x53, 0x00 }, /* 53 Equalizer Band 1 Coefficient A2 */ + { 0x54, 0x00 }, /* 54 Equalizer Band 1 Coefficient A2 */ + { 0x55, 0x00 }, /* 55 Equalizer Band 2 Coefficient B0 */ + { 0x56, 0x00 }, /* 56 Equalizer Band 2 Coefficient B0 */ + { 0x57, 0x00 }, /* 57 Equalizer Band 2 Coefficient B0 */ + { 0x58, 0x00 }, /* 58 Equalizer Band 2 Coefficient B1 */ + { 0x59, 0x00 }, /* 59 Equalizer Band 2 Coefficient B1 */ + { 0x5A, 0x00 }, /* 5A Equalizer Band 2 Coefficient B1 */ + { 0x5B, 0x00 }, /* 5B Equalizer Band 2 Coefficient B2 */ + { 0x5C, 0x00 }, /* 5C Equalizer Band 2 Coefficient B2 */ + { 0x5D, 0x00 }, /* 5D Equalizer Band 2 Coefficient B2 */ + { 0x5E, 0x00 }, /* 5E Equalizer Band 2 Coefficient A1 */ + { 0x5F, 0x00 }, /* 5F Equalizer Band 2 Coefficient A1 */ + + { 0x60, 0x00 }, /* 60 Equalizer Band 2 Coefficient A1 */ + { 0x61, 0x00 }, /* 61 Equalizer Band 2 Coefficient A2 */ + { 0x62, 0x00 }, /* 62 Equalizer Band 2 Coefficient A2 */ + { 0x63, 0x00 }, /* 63 Equalizer Band 2 Coefficient A2 */ + { 0x64, 0x00 }, /* 64 Equalizer Band 3 Coefficient B0 */ + { 0x65, 0x00 }, /* 65 Equalizer Band 3 Coefficient B0 */ + { 0x66, 0x00 }, /* 66 Equalizer Band 3 Coefficient B0 */ + { 0x67, 0x00 }, /* 67 Equalizer Band 3 Coefficient B1 */ + { 0x68, 0x00 }, /* 68 Equalizer Band 3 Coefficient B1 */ + { 0x69, 0x00 }, /* 69 Equalizer Band 3 Coefficient B1 */ + { 0x6A, 0x00 }, /* 6A Equalizer Band 3 Coefficient B2 */ + { 0x6B, 0x00 }, /* 6B Equalizer Band 3 Coefficient B2 */ + { 0x6C, 0x00 }, /* 6C Equalizer Band 3 Coefficient B2 */ + { 0x6D, 0x00 }, /* 6D Equalizer Band 3 Coefficient A1 */ + { 0x6E, 0x00 }, /* 6E Equalizer Band 3 Coefficient A1 */ + { 0x6F, 0x00 }, /* 6F Equalizer Band 3 Coefficient A1 */ + + { 0x70, 0x00 }, /* 70 Equalizer Band 3 Coefficient A2 */ + { 0x71, 0x00 }, /* 71 Equalizer Band 3 Coefficient A2 */ + { 0x72, 0x00 }, /* 72 Equalizer Band 3 Coefficient A2 */ + { 0x73, 0x00 }, /* 73 Equalizer Band 4 Coefficient B0 */ + { 0x74, 0x00 }, /* 74 Equalizer Band 4 Coefficient B0 */ + { 0x75, 0x00 }, /* 75 Equalizer Band 4 Coefficient B0 */ + { 0x76, 0x00 }, /* 76 Equalizer Band 4 Coefficient B1 */ + { 0x77, 0x00 }, /* 77 Equalizer Band 4 Coefficient B1 */ + { 0x78, 0x00 }, /* 78 Equalizer Band 4 Coefficient B1 */ + { 0x79, 0x00 }, /* 79 Equalizer Band 4 Coefficient B2 */ + { 0x7A, 0x00 }, /* 7A Equalizer Band 4 Coefficient B2 */ + { 0x7B, 0x00 }, /* 7B Equalizer Band 4 Coefficient B2 */ + { 0x7C, 0x00 }, /* 7C Equalizer Band 4 Coefficient A1 */ + { 0x7D, 0x00 }, /* 7D Equalizer Band 4 Coefficient A1 */ + { 0x7E, 0x00 }, /* 7E Equalizer Band 4 Coefficient A1 */ + { 0x7F, 0x00 }, /* 7F Equalizer Band 4 Coefficient A2 */ + + { 0x80, 0x00 }, /* 80 Equalizer Band 4 Coefficient A2 */ + { 0x81, 0x00 }, /* 81 Equalizer Band 4 Coefficient A2 */ + { 0x82, 0x00 }, /* 82 Equalizer Band 5 Coefficient B0 */ + { 0x83, 0x00 }, /* 83 Equalizer Band 5 Coefficient B0 */ + { 0x84, 0x00 }, /* 84 Equalizer Band 5 Coefficient B0 */ + { 0x85, 0x00 }, /* 85 Equalizer Band 5 Coefficient B1 */ + { 0x86, 0x00 }, /* 86 Equalizer Band 5 Coefficient B1 */ + { 0x87, 0x00 }, /* 87 Equalizer Band 5 Coefficient B1 */ + { 0x88, 0x00 }, /* 88 Equalizer Band 5 Coefficient B2 */ + { 0x89, 0x00 }, /* 89 Equalizer Band 5 Coefficient B2 */ + { 0x8A, 0x00 }, /* 8A Equalizer Band 5 Coefficient B2 */ + { 0x8B, 0x00 }, /* 8B Equalizer Band 5 Coefficient A1 */ + { 0x8C, 0x00 }, /* 8C Equalizer Band 5 Coefficient A1 */ + { 0x8D, 0x00 }, /* 8D Equalizer Band 5 Coefficient A1 */ + { 0x8E, 0x00 }, /* 8E Equalizer Band 5 Coefficient A2 */ + { 0x8F, 0x00 }, /* 8F Equalizer Band 5 Coefficient A2 */ + + { 0x90, 0x00 }, /* 90 Equalizer Band 5 Coefficient A2 */ + { 0x91, 0x00 }, /* 91 Equalizer Band 6 Coefficient B0 */ + { 0x92, 0x00 }, /* 92 Equalizer Band 6 Coefficient B0 */ + { 0x93, 0x00 }, /* 93 Equalizer Band 6 Coefficient B0 */ + { 0x94, 0x00 }, /* 94 Equalizer Band 6 Coefficient B1 */ + { 0x95, 0x00 }, /* 95 Equalizer Band 6 Coefficient B1 */ + { 0x96, 0x00 }, /* 96 Equalizer Band 6 Coefficient B1 */ + { 0x97, 0x00 }, /* 97 Equalizer Band 6 Coefficient B2 */ + { 0x98, 0x00 }, /* 98 Equalizer Band 6 Coefficient B2 */ + { 0x99, 0x00 }, /* 99 Equalizer Band 6 Coefficient B2 */ + { 0x9A, 0x00 }, /* 9A Equalizer Band 6 Coefficient A1 */ + { 0x9B, 0x00 }, /* 9B Equalizer Band 6 Coefficient A1 */ + { 0x9C, 0x00 }, /* 9C Equalizer Band 6 Coefficient A1 */ + { 0x9D, 0x00 }, /* 9D Equalizer Band 6 Coefficient A2 */ + { 0x9E, 0x00 }, /* 9E Equalizer Band 6 Coefficient A2 */ + { 0x9F, 0x00 }, /* 9F Equalizer Band 6 Coefficient A2 */ + + { 0xA0, 0x00 }, /* A0 Equalizer Band 7 Coefficient B0 */ + { 0xA1, 0x00 }, /* A1 Equalizer Band 7 Coefficient B0 */ + { 0xA2, 0x00 }, /* A2 Equalizer Band 7 Coefficient B0 */ + { 0xA3, 0x00 }, /* A3 Equalizer Band 7 Coefficient B1 */ + { 0xA4, 0x00 }, /* A4 Equalizer Band 7 Coefficient B1 */ + { 0xA5, 0x00 }, /* A5 Equalizer Band 7 Coefficient B1 */ + { 0xA6, 0x00 }, /* A6 Equalizer Band 7 Coefficient B2 */ + { 0xA7, 0x00 }, /* A7 Equalizer Band 7 Coefficient B2 */ + { 0xA8, 0x00 }, /* A8 Equalizer Band 7 Coefficient B2 */ + { 0xA9, 0x00 }, /* A9 Equalizer Band 7 Coefficient A1 */ + { 0xAA, 0x00 }, /* AA Equalizer Band 7 Coefficient A1 */ + { 0xAB, 0x00 }, /* AB Equalizer Band 7 Coefficient A1 */ + { 0xAC, 0x00 }, /* AC Equalizer Band 7 Coefficient A2 */ + { 0xAD, 0x00 }, /* AD Equalizer Band 7 Coefficient A2 */ + { 0xAE, 0x00 }, /* AE Equalizer Band 7 Coefficient A2 */ + { 0xAF, 0x00 }, /* AF ADC Biquad Coefficient B0 */ + + { 0xB0, 0x00 }, /* B0 ADC Biquad Coefficient B0 */ + { 0xB1, 0x00 }, /* B1 ADC Biquad Coefficient B0 */ + { 0xB2, 0x00 }, /* B2 ADC Biquad Coefficient B1 */ + { 0xB3, 0x00 }, /* B3 ADC Biquad Coefficient B1 */ + { 0xB4, 0x00 }, /* B4 ADC Biquad Coefficient B1 */ + { 0xB5, 0x00 }, /* B5 ADC Biquad Coefficient B2 */ + { 0xB6, 0x00 }, /* B6 ADC Biquad Coefficient B2 */ + { 0xB7, 0x00 }, /* B7 ADC Biquad Coefficient B2 */ + { 0xB8, 0x00 }, /* B8 ADC Biquad Coefficient A1 */ + { 0xB9, 0x00 }, /* B9 ADC Biquad Coefficient A1 */ + { 0xBA, 0x00 }, /* BA ADC Biquad Coefficient A1 */ + { 0xBB, 0x00 }, /* BB ADC Biquad Coefficient A2 */ + { 0xBC, 0x00 }, /* BC ADC Biquad Coefficient A2 */ + { 0xBD, 0x00 }, /* BD ADC Biquad Coefficient A2 */ + { 0xBE, 0x00 }, /* BE Digital Mic 3 Volume */ + { 0xBF, 0x00 }, /* BF Digital Mic 4 Volume */ + + { 0xC0, 0x00 }, /* C0 Digital Mic 34 Biquad Pre Atten */ + { 0xC1, 0x00 }, /* C1 Record TDM Slot */ + { 0xC2, 0x00 }, /* C2 Sample Rate */ + { 0xC3, 0x00 }, /* C3 Digital Mic 34 Biquad Coefficient C3 */ + { 0xC4, 0x00 }, /* C4 Digital Mic 34 Biquad Coefficient C4 */ + { 0xC5, 0x00 }, /* C5 Digital Mic 34 Biquad Coefficient C5 */ + { 0xC6, 0x00 }, /* C6 Digital Mic 34 Biquad Coefficient C6 */ + { 0xC7, 0x00 }, /* C7 Digital Mic 34 Biquad Coefficient C7 */ + { 0xC8, 0x00 }, /* C8 Digital Mic 34 Biquad Coefficient C8 */ + { 0xC9, 0x00 }, /* C9 Digital Mic 34 Biquad Coefficient C9 */ + { 0xCA, 0x00 }, /* CA Digital Mic 34 Biquad Coefficient CA */ + { 0xCB, 0x00 }, /* CB Digital Mic 34 Biquad Coefficient CB */ + { 0xCC, 0x00 }, /* CC Digital Mic 34 Biquad Coefficient CC */ + { 0xCD, 0x00 }, /* CD Digital Mic 34 Biquad Coefficient CD */ + { 0xCE, 0x00 }, /* CE Digital Mic 34 Biquad Coefficient CE */ + { 0xCF, 0x00 }, /* CF Digital Mic 34 Biquad Coefficient CF */ + + { 0xD0, 0x00 }, /* D0 Digital Mic 34 Biquad Coefficient D0 */ + { 0xD1, 0x00 }, /* D1 Digital Mic 34 Biquad Coefficient D1 */ +}; + +static bool max98090_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98090_REG_SOFTWARE_RESET: + case M98090_REG_DEVICE_STATUS: + case M98090_REG_JACK_STATUS: + case M98090_REG_REVISION_ID: + return true; + default: + return false; + } +} + +static bool max98090_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98090_REG_DEVICE_STATUS ... M98090_REG_INTERRUPT_S: + case M98090_REG_LINE_INPUT_CONFIG ... 0xD1: + case M98090_REG_REVISION_ID: + return true; + default: + return false; + } +} + +static int max98090_reset(struct max98090_priv *max98090) +{ + int ret; + + /* Reset the codec by writing to this write-only reset register */ + ret = regmap_write(max98090->regmap, M98090_REG_SOFTWARE_RESET, + M98090_SWRESET_MASK); + if (ret < 0) { + dev_err(max98090->component->dev, + "Failed to reset codec: %d\n", ret); + return ret; + } + + msleep(20); + return ret; +} + +static const DECLARE_TLV_DB_RANGE(max98090_micboost_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0) +); + +static const DECLARE_TLV_DB_SCALE(max98090_mic_tlv, 0, 100, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_line_single_ended_tlv, + -600, 600, 0); + +static const DECLARE_TLV_DB_RANGE(max98090_line_tlv, + 0, 3, TLV_DB_SCALE_ITEM(-600, 300, 0), + 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0) +); + +static const DECLARE_TLV_DB_SCALE(max98090_avg_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(max98090_av_tlv, -1200, 100, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_dvg_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(max98090_dv_tlv, -1500, 100, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_alcmakeup_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98090_alccomp_tlv, -3100, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98090_drcexp_tlv, -6600, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98090_sdg_tlv, 50, 200, 0); + +static const DECLARE_TLV_DB_RANGE(max98090_mixout_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-1200, 250, 0), + 2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0) +); + +static const DECLARE_TLV_DB_RANGE(max98090_hp_tlv, + 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0) +); + +static const DECLARE_TLV_DB_RANGE(max98090_spk_tlv, + 0, 4, TLV_DB_SCALE_ITEM(-4800, 400, 0), + 5, 10, TLV_DB_SCALE_ITEM(-2900, 300, 0), + 11, 14, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 15, 29, TLV_DB_SCALE_ITEM(-500, 100, 0), + 30, 39, TLV_DB_SCALE_ITEM(950, 50, 0) +); + +static const DECLARE_TLV_DB_RANGE(max98090_rcv_lout_tlv, + 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0) +); + +static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int val = snd_soc_component_read(component, mc->reg); + unsigned int *select; + + switch (mc->reg) { + case M98090_REG_MIC1_INPUT_LEVEL: + select = &(max98090->pa1en); + break; + case M98090_REG_MIC2_INPUT_LEVEL: + select = &(max98090->pa2en); + break; + case M98090_REG_ADC_SIDETONE: + select = &(max98090->sidetone); + break; + default: + return -EINVAL; + } + + val = (val >> mc->shift) & mask; + + if (val >= 1) { + /* If on, return the volume */ + val = val - 1; + *select = val; + } else { + /* If off, return last stored value */ + val = *select; + } + + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mask = (1 << fls(mc->max)) - 1; + int sel_unchecked = ucontrol->value.integer.value[0]; + unsigned int sel; + unsigned int val = snd_soc_component_read(component, mc->reg); + unsigned int *select; + int change; + + switch (mc->reg) { + case M98090_REG_MIC1_INPUT_LEVEL: + select = &(max98090->pa1en); + break; + case M98090_REG_MIC2_INPUT_LEVEL: + select = &(max98090->pa2en); + break; + case M98090_REG_ADC_SIDETONE: + select = &(max98090->sidetone); + break; + default: + return -EINVAL; + } + + val = (val >> mc->shift) & mask; + + if (sel_unchecked < 0 || sel_unchecked > mc->max) + return -EINVAL; + sel = sel_unchecked; + + change = *select != sel; + *select = sel; + + /* Setting a volume is only valid if it is already On */ + if (val >= 1) { + sel = sel + 1; + } else { + /* Write what was already there */ + sel = val; + } + + snd_soc_component_update_bits(component, mc->reg, + mask << mc->shift, + sel << mc->shift); + + return change; +} + +static const char *max98090_perf_pwr_text[] = + { "High Performance", "Low Power" }; +static const char *max98090_pwr_perf_text[] = + { "Low Power", "High Performance" }; + +static SOC_ENUM_SINGLE_DECL(max98090_vcmbandgap_enum, + M98090_REG_BIAS_CONTROL, + M98090_VCM_MODE_SHIFT, + max98090_pwr_perf_text); + +static const char *max98090_osr128_text[] = { "64*fs", "128*fs" }; + +static SOC_ENUM_SINGLE_DECL(max98090_osr128_enum, + M98090_REG_ADC_CONTROL, + M98090_OSR128_SHIFT, + max98090_osr128_text); + +static const char *max98090_mode_text[] = { "Voice", "Music" }; + +static SOC_ENUM_SINGLE_DECL(max98090_mode_enum, + M98090_REG_FILTER_CONFIG, + M98090_MODE_SHIFT, + max98090_mode_text); + +static SOC_ENUM_SINGLE_DECL(max98090_filter_dmic34mode_enum, + M98090_REG_FILTER_CONFIG, + M98090_FLT_DMIC34MODE_SHIFT, + max98090_mode_text); + +static const char *max98090_drcatk_text[] = + { "0.5ms", "1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "200ms" }; + +static SOC_ENUM_SINGLE_DECL(max98090_drcatk_enum, + M98090_REG_DRC_TIMING, + M98090_DRCATK_SHIFT, + max98090_drcatk_text); + +static const char *max98090_drcrls_text[] = + { "8s", "4s", "2s", "1s", "0.5s", "0.25s", "0.125s", "0.0625s" }; + +static SOC_ENUM_SINGLE_DECL(max98090_drcrls_enum, + M98090_REG_DRC_TIMING, + M98090_DRCRLS_SHIFT, + max98090_drcrls_text); + +static const char *max98090_alccmp_text[] = + { "1:1", "1:1.5", "1:2", "1:4", "1:INF" }; + +static SOC_ENUM_SINGLE_DECL(max98090_alccmp_enum, + M98090_REG_DRC_COMPRESSOR, + M98090_DRCCMP_SHIFT, + max98090_alccmp_text); + +static const char *max98090_drcexp_text[] = { "1:1", "2:1", "3:1" }; + +static SOC_ENUM_SINGLE_DECL(max98090_drcexp_enum, + M98090_REG_DRC_EXPANDER, + M98090_DRCEXP_SHIFT, + max98090_drcexp_text); + +static SOC_ENUM_SINGLE_DECL(max98090_dac_perfmode_enum, + M98090_REG_DAC_CONTROL, + M98090_PERFMODE_SHIFT, + max98090_perf_pwr_text); + +static SOC_ENUM_SINGLE_DECL(max98090_dachp_enum, + M98090_REG_DAC_CONTROL, + M98090_DACHP_SHIFT, + max98090_pwr_perf_text); + +static SOC_ENUM_SINGLE_DECL(max98090_adchp_enum, + M98090_REG_ADC_CONTROL, + M98090_ADCHP_SHIFT, + max98090_pwr_perf_text); + +static const struct snd_kcontrol_new max98090_snd_controls[] = { + SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum), + + SOC_SINGLE("DMIC MIC Comp Filter Config", M98090_REG_DIGITAL_MIC_CONFIG, + M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0), + + SOC_SINGLE_EXT_TLV("MIC1 Boost Volume", + M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT, + M98090_MIC_PA1EN_NUM - 1, 0, max98090_get_enab_tlv, + max98090_put_enab_tlv, max98090_micboost_tlv), + + SOC_SINGLE_EXT_TLV("MIC2 Boost Volume", + M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT, + M98090_MIC_PA2EN_NUM - 1, 0, max98090_get_enab_tlv, + max98090_put_enab_tlv, max98090_micboost_tlv), + + SOC_SINGLE_TLV("MIC1 Volume", M98090_REG_MIC1_INPUT_LEVEL, + M98090_MIC_PGAM1_SHIFT, M98090_MIC_PGAM1_NUM - 1, 1, + max98090_mic_tlv), + + SOC_SINGLE_TLV("MIC2 Volume", M98090_REG_MIC2_INPUT_LEVEL, + M98090_MIC_PGAM2_SHIFT, M98090_MIC_PGAM2_NUM - 1, 1, + max98090_mic_tlv), + + SOC_SINGLE_RANGE_TLV("LINEA Single Ended Volume", + M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG135_SHIFT, 0, + M98090_MIXG135_NUM - 1, 1, max98090_line_single_ended_tlv), + + SOC_SINGLE_RANGE_TLV("LINEB Single Ended Volume", + M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG246_SHIFT, 0, + M98090_MIXG246_NUM - 1, 1, max98090_line_single_ended_tlv), + + SOC_SINGLE_RANGE_TLV("LINEA Volume", M98090_REG_LINE_INPUT_LEVEL, + M98090_LINAPGA_SHIFT, 0, M98090_LINAPGA_NUM - 1, 1, + max98090_line_tlv), + + SOC_SINGLE_RANGE_TLV("LINEB Volume", M98090_REG_LINE_INPUT_LEVEL, + M98090_LINBPGA_SHIFT, 0, M98090_LINBPGA_NUM - 1, 1, + max98090_line_tlv), + + SOC_SINGLE("LINEA Ext Resistor Gain Mode", M98090_REG_INPUT_MODE, + M98090_EXTBUFA_SHIFT, M98090_EXTBUFA_NUM - 1, 0), + SOC_SINGLE("LINEB Ext Resistor Gain Mode", M98090_REG_INPUT_MODE, + M98090_EXTBUFB_SHIFT, M98090_EXTBUFB_NUM - 1, 0), + + SOC_SINGLE_TLV("ADCL Boost Volume", M98090_REG_LEFT_ADC_LEVEL, + M98090_AVLG_SHIFT, M98090_AVLG_NUM - 1, 0, + max98090_avg_tlv), + SOC_SINGLE_TLV("ADCR Boost Volume", M98090_REG_RIGHT_ADC_LEVEL, + M98090_AVRG_SHIFT, M98090_AVLG_NUM - 1, 0, + max98090_avg_tlv), + + SOC_SINGLE_TLV("ADCL Volume", M98090_REG_LEFT_ADC_LEVEL, + M98090_AVL_SHIFT, M98090_AVL_NUM - 1, 1, + max98090_av_tlv), + SOC_SINGLE_TLV("ADCR Volume", M98090_REG_RIGHT_ADC_LEVEL, + M98090_AVR_SHIFT, M98090_AVR_NUM - 1, 1, + max98090_av_tlv), + + SOC_ENUM("ADC Oversampling Rate", max98090_osr128_enum), + SOC_SINGLE("ADC Quantizer Dither", M98090_REG_ADC_CONTROL, + M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0), + SOC_ENUM("ADC High Performance Mode", max98090_adchp_enum), + + SOC_SINGLE("DAC Mono Mode", M98090_REG_IO_CONFIGURATION, + M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0), + SOC_SINGLE("SDIN Mode", M98090_REG_IO_CONFIGURATION, + M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0), + SOC_SINGLE("SDOUT Mode", M98090_REG_IO_CONFIGURATION, + M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0), + SOC_SINGLE("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION, + M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1), + SOC_ENUM("Filter Mode", max98090_mode_enum), + SOC_SINGLE("Record Path DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0), + SOC_SINGLE("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0), + SOC_SINGLE_TLV("Digital BQ Volume", M98090_REG_ADC_BIQUAD_LEVEL, + M98090_AVBQ_SHIFT, M98090_AVBQ_NUM - 1, 1, max98090_dv_tlv), + SOC_SINGLE_EXT_TLV("Digital Sidetone Volume", + M98090_REG_ADC_SIDETONE, M98090_DVST_SHIFT, + M98090_DVST_NUM - 1, 1, max98090_get_enab_tlv, + max98090_put_enab_tlv, max98090_sdg_tlv), + SOC_SINGLE_TLV("Digital Coarse Volume", M98090_REG_DAI_PLAYBACK_LEVEL, + M98090_DVG_SHIFT, M98090_DVG_NUM - 1, 0, + max98090_dvg_tlv), + SOC_SINGLE_TLV("Digital Volume", M98090_REG_DAI_PLAYBACK_LEVEL, + M98090_DV_SHIFT, M98090_DV_NUM - 1, 1, + max98090_dv_tlv), + SND_SOC_BYTES("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105), + SOC_SINGLE("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0), + SOC_SINGLE("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0), + SOC_SINGLE("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0), + SOC_SINGLE("Digital EQ Clipping Detection", M98090_REG_DAI_PLAYBACK_LEVEL_EQ, + M98090_EQCLPN_SHIFT, M98090_EQCLPN_NUM - 1, + 1), + SOC_SINGLE_TLV("Digital EQ Volume", M98090_REG_DAI_PLAYBACK_LEVEL_EQ, + M98090_DVEQ_SHIFT, M98090_DVEQ_NUM - 1, 1, + max98090_dv_tlv), + + SOC_SINGLE("ALC Enable", M98090_REG_DRC_TIMING, + M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0), + SOC_ENUM("ALC Attack Time", max98090_drcatk_enum), + SOC_ENUM("ALC Release Time", max98090_drcrls_enum), + SOC_SINGLE_TLV("ALC Make Up Volume", M98090_REG_DRC_GAIN, + M98090_DRCG_SHIFT, M98090_DRCG_NUM - 1, 0, + max98090_alcmakeup_tlv), + SOC_ENUM("ALC Compression Ratio", max98090_alccmp_enum), + SOC_ENUM("ALC Expansion Ratio", max98090_drcexp_enum), + SOC_SINGLE_TLV("ALC Compression Threshold Volume", + M98090_REG_DRC_COMPRESSOR, M98090_DRCTHC_SHIFT, + M98090_DRCTHC_NUM - 1, 1, max98090_alccomp_tlv), + SOC_SINGLE_TLV("ALC Expansion Threshold Volume", + M98090_REG_DRC_EXPANDER, M98090_DRCTHE_SHIFT, + M98090_DRCTHE_NUM - 1, 1, max98090_drcexp_tlv), + + SOC_ENUM("DAC HP Playback Performance Mode", + max98090_dac_perfmode_enum), + SOC_ENUM("DAC High Performance Mode", max98090_dachp_enum), + + SOC_SINGLE_TLV("Headphone Left Mixer Volume", + M98090_REG_HP_CONTROL, M98090_MIXHPLG_SHIFT, + M98090_MIXHPLG_NUM - 1, 1, max98090_mixout_tlv), + SOC_SINGLE_TLV("Headphone Right Mixer Volume", + M98090_REG_HP_CONTROL, M98090_MIXHPRG_SHIFT, + M98090_MIXHPRG_NUM - 1, 1, max98090_mixout_tlv), + + SOC_SINGLE_TLV("Speaker Left Mixer Volume", + M98090_REG_SPK_CONTROL, M98090_MIXSPLG_SHIFT, + M98090_MIXSPLG_NUM - 1, 1, max98090_mixout_tlv), + SOC_SINGLE_TLV("Speaker Right Mixer Volume", + M98090_REG_SPK_CONTROL, M98090_MIXSPRG_SHIFT, + M98090_MIXSPRG_NUM - 1, 1, max98090_mixout_tlv), + + SOC_SINGLE_TLV("Receiver Left Mixer Volume", + M98090_REG_RCV_LOUTL_CONTROL, M98090_MIXRCVLG_SHIFT, + M98090_MIXRCVLG_NUM - 1, 1, max98090_mixout_tlv), + SOC_SINGLE_TLV("Receiver Right Mixer Volume", + M98090_REG_LOUTR_CONTROL, M98090_MIXRCVRG_SHIFT, + M98090_MIXRCVRG_NUM - 1, 1, max98090_mixout_tlv), + + SOC_DOUBLE_R_TLV("Headphone Volume", M98090_REG_LEFT_HP_VOLUME, + M98090_REG_RIGHT_HP_VOLUME, M98090_HPVOLL_SHIFT, + M98090_HPVOLL_NUM - 1, 0, max98090_hp_tlv), + + SOC_DOUBLE_R_RANGE_TLV("Speaker Volume", + M98090_REG_LEFT_SPK_VOLUME, M98090_REG_RIGHT_SPK_VOLUME, + M98090_SPVOLL_SHIFT, 24, M98090_SPVOLL_NUM - 1 + 24, + 0, max98090_spk_tlv), + + SOC_DOUBLE_R_TLV("Receiver Volume", M98090_REG_RCV_LOUTL_VOLUME, + M98090_REG_LOUTR_VOLUME, M98090_RCVLVOL_SHIFT, + M98090_RCVLVOL_NUM - 1, 0, max98090_rcv_lout_tlv), + + SOC_SINGLE("Headphone Left Switch", M98090_REG_LEFT_HP_VOLUME, + M98090_HPLM_SHIFT, 1, 1), + SOC_SINGLE("Headphone Right Switch", M98090_REG_RIGHT_HP_VOLUME, + M98090_HPRM_SHIFT, 1, 1), + + SOC_SINGLE("Speaker Left Switch", M98090_REG_LEFT_SPK_VOLUME, + M98090_SPLM_SHIFT, 1, 1), + SOC_SINGLE("Speaker Right Switch", M98090_REG_RIGHT_SPK_VOLUME, + M98090_SPRM_SHIFT, 1, 1), + + SOC_SINGLE("Receiver Left Switch", M98090_REG_RCV_LOUTL_VOLUME, + M98090_RCVLM_SHIFT, 1, 1), + SOC_SINGLE("Receiver Right Switch", M98090_REG_LOUTR_VOLUME, + M98090_RCVRM_SHIFT, 1, 1), + + SOC_SINGLE("Zero-Crossing Detection", M98090_REG_LEVEL_CONTROL, + M98090_ZDENN_SHIFT, M98090_ZDENN_NUM - 1, 1), + SOC_SINGLE("Enhanced Vol Smoothing", M98090_REG_LEVEL_CONTROL, + M98090_VS2ENN_SHIFT, M98090_VS2ENN_NUM - 1, 1), + SOC_SINGLE("Volume Adjustment Smoothing", M98090_REG_LEVEL_CONTROL, + M98090_VSENN_SHIFT, M98090_VSENN_NUM - 1, 1), + + SND_SOC_BYTES("Biquad Coefficients", M98090_REG_RECORD_BIQUAD_BASE, 15), + SOC_SINGLE("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0), +}; + +static const struct snd_kcontrol_new max98091_snd_controls[] = { + + SOC_SINGLE("DMIC34 Zeropad", M98090_REG_SAMPLE_RATE, + M98090_DMIC34_ZEROPAD_SHIFT, + M98090_DMIC34_ZEROPAD_NUM - 1, 0), + + SOC_ENUM("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum), + SOC_SINGLE("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_FLT_DMIC34HPF_SHIFT, + M98090_FLT_DMIC34HPF_NUM - 1, 0), + + SOC_SINGLE_TLV("DMIC3 Boost Volume", M98090_REG_DMIC3_VOLUME, + M98090_DMIC_AV3G_SHIFT, M98090_DMIC_AV3G_NUM - 1, 0, + max98090_avg_tlv), + SOC_SINGLE_TLV("DMIC4 Boost Volume", M98090_REG_DMIC4_VOLUME, + M98090_DMIC_AV4G_SHIFT, M98090_DMIC_AV4G_NUM - 1, 0, + max98090_avg_tlv), + + SOC_SINGLE_TLV("DMIC3 Volume", M98090_REG_DMIC3_VOLUME, + M98090_DMIC_AV3_SHIFT, M98090_DMIC_AV3_NUM - 1, 1, + max98090_av_tlv), + SOC_SINGLE_TLV("DMIC4 Volume", M98090_REG_DMIC4_VOLUME, + M98090_DMIC_AV4_SHIFT, M98090_DMIC_AV4_NUM - 1, 1, + max98090_av_tlv), + + SND_SOC_BYTES("DMIC34 Biquad Coefficients", + M98090_REG_DMIC34_BIQUAD_BASE, 15), + SOC_SINGLE("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0), + + SOC_SINGLE_TLV("DMIC34 BQ PreAttenuation Volume", + M98090_REG_DMIC34_BQ_PREATTEN, M98090_AV34BQ_SHIFT, + M98090_AV34BQ_NUM - 1, 1, max98090_dv_tlv), +}; + +static int max98090_micinput_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + + unsigned int val = snd_soc_component_read(component, w->reg); + + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) + val = (val & M98090_MIC_PA1EN_MASK) >> M98090_MIC_PA1EN_SHIFT; + else + val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT; + + if (val >= 1) { + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) { + max98090->pa1en = val - 1; /* Update for volatile */ + } else { + max98090->pa2en = val - 1; /* Update for volatile */ + } + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* If turning on, set to most recently selected volume */ + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) + val = max98090->pa1en + 1; + else + val = max98090->pa2en + 1; + break; + case SND_SOC_DAPM_POST_PMD: + /* If turning off, turn off */ + val = 0; + break; + default: + return -EINVAL; + } + + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) + snd_soc_component_update_bits(component, w->reg, M98090_MIC_PA1EN_MASK, + val << M98090_MIC_PA1EN_SHIFT); + else + snd_soc_component_update_bits(component, w->reg, M98090_MIC_PA2EN_MASK, + val << M98090_MIC_PA2EN_SHIFT); + + return 0; +} + +static int max98090_shdn_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + + if (event & SND_SOC_DAPM_POST_PMU) + max98090->shdn_pending = true; + + return 0; + +} + +static const char *mic1_mux_text[] = { "IN12", "IN56" }; + +static SOC_ENUM_SINGLE_DECL(mic1_mux_enum, + M98090_REG_INPUT_MODE, + M98090_EXTMIC1_SHIFT, + mic1_mux_text); + +static const struct snd_kcontrol_new max98090_mic1_mux = + SOC_DAPM_ENUM("MIC1 Mux", mic1_mux_enum); + +static const char *mic2_mux_text[] = { "IN34", "IN56" }; + +static SOC_ENUM_SINGLE_DECL(mic2_mux_enum, + M98090_REG_INPUT_MODE, + M98090_EXTMIC2_SHIFT, + mic2_mux_text); + +static const struct snd_kcontrol_new max98090_mic2_mux = + SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum); + +static const char *dmic_mux_text[] = { "ADC", "DMIC" }; + +static SOC_ENUM_SINGLE_VIRT_DECL(dmic_mux_enum, dmic_mux_text); + +static const struct snd_kcontrol_new max98090_dmic_mux = + SOC_DAPM_ENUM("DMIC Mux", dmic_mux_enum); + +/* LINEA mixer switch */ +static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN1SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN3 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN3SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN5 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN5SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN34DIFF_SHIFT, 1, 0), +}; + +/* LINEB mixer switch */ +static const struct snd_kcontrol_new max98090_lineb_mixer_controls[] = { + SOC_DAPM_SINGLE("IN2 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN2SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN4 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN4SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN6 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN6SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN56DIFF_SHIFT, 1, 0), +}; + +/* Left ADC mixer switch */ +static const struct snd_kcontrol_new max98090_left_adc_mixer_controls[] = { + SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_IN12DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_IN34DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_IN65DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_MIC2_SHIFT, 1, 0), +}; + +/* Right ADC mixer switch */ +static const struct snd_kcontrol_new max98090_right_adc_mixer_controls[] = { + SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_IN12DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_IN34DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_IN65DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_MIC2_SHIFT, 1, 0), +}; + +static const char *lten_mux_text[] = { "Normal", "Loopthrough" }; + +static SOC_ENUM_SINGLE_DECL(ltenl_mux_enum, + M98090_REG_IO_CONFIGURATION, + M98090_LTEN_SHIFT, + lten_mux_text); + +static SOC_ENUM_SINGLE_DECL(ltenr_mux_enum, + M98090_REG_IO_CONFIGURATION, + M98090_LTEN_SHIFT, + lten_mux_text); + +static const struct snd_kcontrol_new max98090_ltenl_mux = + SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum); + +static const struct snd_kcontrol_new max98090_ltenr_mux = + SOC_DAPM_ENUM("LTENR Mux", ltenr_mux_enum); + +static const char *lben_mux_text[] = { "Normal", "Loopback" }; + +static SOC_ENUM_SINGLE_DECL(lbenl_mux_enum, + M98090_REG_IO_CONFIGURATION, + M98090_LBEN_SHIFT, + lben_mux_text); + +static SOC_ENUM_SINGLE_DECL(lbenr_mux_enum, + M98090_REG_IO_CONFIGURATION, + M98090_LBEN_SHIFT, + lben_mux_text); + +static const struct snd_kcontrol_new max98090_lbenl_mux = + SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum); + +static const struct snd_kcontrol_new max98090_lbenr_mux = + SOC_DAPM_ENUM("LBENR Mux", lbenr_mux_enum); + +static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" }; + +static const char *stenr_mux_text[] = { "Normal", "Sidetone Right" }; + +static SOC_ENUM_SINGLE_DECL(stenl_mux_enum, + M98090_REG_ADC_SIDETONE, + M98090_DSTSL_SHIFT, + stenl_mux_text); + +static SOC_ENUM_SINGLE_DECL(stenr_mux_enum, + M98090_REG_ADC_SIDETONE, + M98090_DSTSR_SHIFT, + stenr_mux_text); + +static const struct snd_kcontrol_new max98090_stenl_mux = + SOC_DAPM_ENUM("STENL Mux", stenl_mux_enum); + +static const struct snd_kcontrol_new max98090_stenr_mux = + SOC_DAPM_ENUM("STENR Mux", stenr_mux_enum); + +/* Left speaker mixer switch */ +static const struct + snd_kcontrol_new max98090_left_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_MIC2_SHIFT, 1, 0), +}; + +/* Right speaker mixer switch */ +static const struct + snd_kcontrol_new max98090_right_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_MIC2_SHIFT, 1, 0), +}; + +/* Left headphone mixer switch */ +static const struct snd_kcontrol_new max98090_left_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_MIC2_SHIFT, 1, 0), +}; + +/* Right headphone mixer switch */ +static const struct snd_kcontrol_new max98090_right_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_MIC2_SHIFT, 1, 0), +}; + +/* Left receiver mixer switch */ +static const struct snd_kcontrol_new max98090_left_rcv_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_MIC2_SHIFT, 1, 0), +}; + +/* Right receiver mixer switch */ +static const struct snd_kcontrol_new max98090_right_rcv_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_MIC2_SHIFT, 1, 0), +}; + +static const char *linmod_mux_text[] = { "Left Only", "Left and Right" }; + +static SOC_ENUM_SINGLE_DECL(linmod_mux_enum, + M98090_REG_LOUTR_MIXER, + M98090_LINMOD_SHIFT, + linmod_mux_text); + +static const struct snd_kcontrol_new max98090_linmod_mux = + SOC_DAPM_ENUM("LINMOD Mux", linmod_mux_enum); + +static const char *mixhpsel_mux_text[] = { "DAC Only", "HP Mixer" }; + +/* + * This is a mux as it selects the HP output, but to DAPM it is a Mixer enable + */ +static SOC_ENUM_SINGLE_DECL(mixhplsel_mux_enum, + M98090_REG_HP_CONTROL, + M98090_MIXHPLSEL_SHIFT, + mixhpsel_mux_text); + +static const struct snd_kcontrol_new max98090_mixhplsel_mux = + SOC_DAPM_ENUM("MIXHPLSEL Mux", mixhplsel_mux_enum); + +static SOC_ENUM_SINGLE_DECL(mixhprsel_mux_enum, + M98090_REG_HP_CONTROL, + M98090_MIXHPRSEL_SHIFT, + mixhpsel_mux_text); + +static const struct snd_kcontrol_new max98090_mixhprsel_mux = + SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum); + +static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMICL"), + SND_SOC_DAPM_INPUT("DMICR"), + SND_SOC_DAPM_INPUT("IN1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_INPUT("IN3"), + SND_SOC_DAPM_INPUT("IN4"), + SND_SOC_DAPM_INPUT("IN5"), + SND_SOC_DAPM_INPUT("IN6"), + SND_SOC_DAPM_INPUT("IN12"), + SND_SOC_DAPM_INPUT("IN34"), + SND_SOC_DAPM_INPUT("IN56"), + + SND_SOC_DAPM_SUPPLY("MICBIAS", M98090_REG_INPUT_ENABLE, + M98090_MBEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SHDN", M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SDIEN", M98090_REG_IO_CONFIGURATION, + M98090_SDIEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION, + M98090_SDOEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMICL_SHIFT, 0, max98090_shdn_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMICR_SHIFT, 0, max98090_shdn_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG, + M98090_AHPF_SHIFT, 0, NULL, 0), + +/* + * Note: Sysclk and misc power supplies are taken care of by SHDN + */ + + SND_SOC_DAPM_MUX("MIC1 Mux", SND_SOC_NOPM, + 0, 0, &max98090_mic1_mux), + + SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM, + 0, 0, &max98090_mic2_mux), + + SND_SOC_DAPM_MUX("DMIC Mux", SND_SOC_NOPM, 0, 0, &max98090_dmic_mux), + + SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL, + M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("MIC2 Input", M98090_REG_MIC2_INPUT_LEVEL, + M98090_MIC_PA2EN_SHIFT, 0, NULL, 0, max98090_micinput_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("LINEA Mixer", SND_SOC_NOPM, 0, 0, + &max98090_linea_mixer_controls[0], + ARRAY_SIZE(max98090_linea_mixer_controls)), + + SND_SOC_DAPM_MIXER("LINEB Mixer", SND_SOC_NOPM, 0, 0, + &max98090_lineb_mixer_controls[0], + ARRAY_SIZE(max98090_lineb_mixer_controls)), + + SND_SOC_DAPM_PGA("LINEA Input", M98090_REG_INPUT_ENABLE, + M98090_LINEAEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("LINEB Input", M98090_REG_INPUT_ENABLE, + M98090_LINEBEN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_adc_mixer_controls[0], + ARRAY_SIZE(max98090_left_adc_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_adc_mixer_controls[0], + ARRAY_SIZE(max98090_right_adc_mixer_controls)), + + SND_SOC_DAPM_ADC_E("ADCL", NULL, M98090_REG_INPUT_ENABLE, + M98090_ADLEN_SHIFT, 0, max98090_shdn_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_ADC_E("ADCR", NULL, M98090_REG_INPUT_ENABLE, + M98090_ADREN_SHIFT, 0, max98090_shdn_event, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIFOUTR", "HiFi Capture", 1, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("LBENL Mux", SND_SOC_NOPM, + 0, 0, &max98090_lbenl_mux), + + SND_SOC_DAPM_MUX("LBENR Mux", SND_SOC_NOPM, + 0, 0, &max98090_lbenr_mux), + + SND_SOC_DAPM_MUX("LTENL Mux", SND_SOC_NOPM, + 0, 0, &max98090_ltenl_mux), + + SND_SOC_DAPM_MUX("LTENR Mux", SND_SOC_NOPM, + 0, 0, &max98090_ltenr_mux), + + SND_SOC_DAPM_MUX("STENL Mux", SND_SOC_NOPM, + 0, 0, &max98090_stenl_mux), + + SND_SOC_DAPM_MUX("STENR Mux", SND_SOC_NOPM, + 0, 0, &max98090_stenr_mux), + + SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DACL", NULL, M98090_REG_OUTPUT_ENABLE, + M98090_DALEN_SHIFT, 0), + SND_SOC_DAPM_DAC("DACR", NULL, M98090_REG_OUTPUT_ENABLE, + M98090_DAREN_SHIFT, 0), + + SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_hp_mixer_controls[0], + ARRAY_SIZE(max98090_left_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_hp_mixer_controls[0], + ARRAY_SIZE(max98090_right_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_speaker_mixer_controls[0], + ARRAY_SIZE(max98090_left_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_speaker_mixer_controls[0], + ARRAY_SIZE(max98090_right_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Receiver Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_rcv_mixer_controls[0], + ARRAY_SIZE(max98090_left_rcv_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Receiver Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_rcv_mixer_controls[0], + ARRAY_SIZE(max98090_right_rcv_mixer_controls)), + + SND_SOC_DAPM_MUX("LINMOD Mux", SND_SOC_NOPM, 0, 0, + &max98090_linmod_mux), + + SND_SOC_DAPM_MUX("MIXHPLSEL Mux", SND_SOC_NOPM, 0, 0, + &max98090_mixhplsel_mux), + + SND_SOC_DAPM_MUX("MIXHPRSEL Mux", SND_SOC_NOPM, 0, 0, + &max98090_mixhprsel_mux), + + SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_HPLEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_HPREN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("SPK Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_SPLEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPK Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_SPREN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("RCV Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_RCVLEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("RCV Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_RCVREN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("RCVL"), + SND_SOC_DAPM_OUTPUT("RCVR"), +}; + +static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + + SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMIC3_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMIC4_SHIFT, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route max98090_dapm_routes[] = { + {"MIC1 Input", NULL, "MIC1"}, + {"MIC2 Input", NULL, "MIC2"}, + + {"DMICL", NULL, "DMICL_ENA"}, + {"DMICL", NULL, "DMICR_ENA"}, + {"DMICR", NULL, "DMICL_ENA"}, + {"DMICR", NULL, "DMICR_ENA"}, + {"DMICL", NULL, "AHPF"}, + {"DMICR", NULL, "AHPF"}, + + /* MIC1 input mux */ + {"MIC1 Mux", "IN12", "IN12"}, + {"MIC1 Mux", "IN56", "IN56"}, + + /* MIC2 input mux */ + {"MIC2 Mux", "IN34", "IN34"}, + {"MIC2 Mux", "IN56", "IN56"}, + + {"MIC1 Input", NULL, "MIC1 Mux"}, + {"MIC2 Input", NULL, "MIC2 Mux"}, + + /* Left ADC input mixer */ + {"Left ADC Mixer", "IN12 Switch", "IN12"}, + {"Left ADC Mixer", "IN34 Switch", "IN34"}, + {"Left ADC Mixer", "IN56 Switch", "IN56"}, + {"Left ADC Mixer", "LINEA Switch", "LINEA Input"}, + {"Left ADC Mixer", "LINEB Switch", "LINEB Input"}, + {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + + /* Right ADC input mixer */ + {"Right ADC Mixer", "IN12 Switch", "IN12"}, + {"Right ADC Mixer", "IN34 Switch", "IN34"}, + {"Right ADC Mixer", "IN56 Switch", "IN56"}, + {"Right ADC Mixer", "LINEA Switch", "LINEA Input"}, + {"Right ADC Mixer", "LINEB Switch", "LINEB Input"}, + {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + + /* Line A input mixer */ + {"LINEA Mixer", "IN1 Switch", "IN1"}, + {"LINEA Mixer", "IN3 Switch", "IN3"}, + {"LINEA Mixer", "IN5 Switch", "IN5"}, + {"LINEA Mixer", "IN34 Switch", "IN34"}, + + /* Line B input mixer */ + {"LINEB Mixer", "IN2 Switch", "IN2"}, + {"LINEB Mixer", "IN4 Switch", "IN4"}, + {"LINEB Mixer", "IN6 Switch", "IN6"}, + {"LINEB Mixer", "IN56 Switch", "IN56"}, + + {"LINEA Input", NULL, "LINEA Mixer"}, + {"LINEB Input", NULL, "LINEB Mixer"}, + + /* Inputs */ + {"ADCL", NULL, "Left ADC Mixer"}, + {"ADCR", NULL, "Right ADC Mixer"}, + {"ADCL", NULL, "SHDN"}, + {"ADCR", NULL, "SHDN"}, + + {"DMIC Mux", "ADC", "ADCL"}, + {"DMIC Mux", "ADC", "ADCR"}, + {"DMIC Mux", "DMIC", "DMICL"}, + {"DMIC Mux", "DMIC", "DMICR"}, + + {"LBENL Mux", "Normal", "DMIC Mux"}, + {"LBENL Mux", "Loopback", "LTENL Mux"}, + {"LBENR Mux", "Normal", "DMIC Mux"}, + {"LBENR Mux", "Loopback", "LTENR Mux"}, + + {"AIFOUTL", NULL, "LBENL Mux"}, + {"AIFOUTR", NULL, "LBENR Mux"}, + {"AIFOUTL", NULL, "SHDN"}, + {"AIFOUTR", NULL, "SHDN"}, + {"AIFOUTL", NULL, "SDOEN"}, + {"AIFOUTR", NULL, "SDOEN"}, + + {"LTENL Mux", "Normal", "AIFINL"}, + {"LTENL Mux", "Loopthrough", "LBENL Mux"}, + {"LTENR Mux", "Normal", "AIFINR"}, + {"LTENR Mux", "Loopthrough", "LBENR Mux"}, + + {"DACL", NULL, "LTENL Mux"}, + {"DACR", NULL, "LTENR Mux"}, + + {"STENL Mux", "Sidetone Left", "ADCL"}, + {"STENL Mux", "Sidetone Left", "DMICL"}, + {"STENR Mux", "Sidetone Right", "ADCR"}, + {"STENR Mux", "Sidetone Right", "DMICR"}, + {"DACL", NULL, "STENL Mux"}, + {"DACR", NULL, "STENR Mux"}, + + {"AIFINL", NULL, "SHDN"}, + {"AIFINR", NULL, "SHDN"}, + {"AIFINL", NULL, "SDIEN"}, + {"AIFINR", NULL, "SDIEN"}, + {"DACL", NULL, "SHDN"}, + {"DACR", NULL, "SHDN"}, + + /* Left headphone output mixer */ + {"Left Headphone Mixer", "Left DAC Switch", "DACL"}, + {"Left Headphone Mixer", "Right DAC Switch", "DACR"}, + {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Headphone Mixer", "LINEA Switch", "LINEA Input"}, + {"Left Headphone Mixer", "LINEB Switch", "LINEB Input"}, + + /* Right headphone output mixer */ + {"Right Headphone Mixer", "Left DAC Switch", "DACL"}, + {"Right Headphone Mixer", "Right DAC Switch", "DACR"}, + {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Headphone Mixer", "LINEA Switch", "LINEA Input"}, + {"Right Headphone Mixer", "LINEB Switch", "LINEB Input"}, + + /* Left speaker output mixer */ + {"Left Speaker Mixer", "Left DAC Switch", "DACL"}, + {"Left Speaker Mixer", "Right DAC Switch", "DACR"}, + {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Speaker Mixer", "LINEA Switch", "LINEA Input"}, + {"Left Speaker Mixer", "LINEB Switch", "LINEB Input"}, + + /* Right speaker output mixer */ + {"Right Speaker Mixer", "Left DAC Switch", "DACL"}, + {"Right Speaker Mixer", "Right DAC Switch", "DACR"}, + {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Speaker Mixer", "LINEA Switch", "LINEA Input"}, + {"Right Speaker Mixer", "LINEB Switch", "LINEB Input"}, + + /* Left Receiver output mixer */ + {"Left Receiver Mixer", "Left DAC Switch", "DACL"}, + {"Left Receiver Mixer", "Right DAC Switch", "DACR"}, + {"Left Receiver Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Receiver Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Receiver Mixer", "LINEA Switch", "LINEA Input"}, + {"Left Receiver Mixer", "LINEB Switch", "LINEB Input"}, + + /* Right Receiver output mixer */ + {"Right Receiver Mixer", "Left DAC Switch", "DACL"}, + {"Right Receiver Mixer", "Right DAC Switch", "DACR"}, + {"Right Receiver Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Receiver Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Receiver Mixer", "LINEA Switch", "LINEA Input"}, + {"Right Receiver Mixer", "LINEB Switch", "LINEB Input"}, + + {"MIXHPLSEL Mux", "HP Mixer", "Left Headphone Mixer"}, + + /* + * Disable this for lowest power if bypassing + * the DAC with an analog signal + */ + {"HP Left Out", NULL, "DACL"}, + {"HP Left Out", NULL, "MIXHPLSEL Mux"}, + + {"MIXHPRSEL Mux", "HP Mixer", "Right Headphone Mixer"}, + + /* + * Disable this for lowest power if bypassing + * the DAC with an analog signal + */ + {"HP Right Out", NULL, "DACR"}, + {"HP Right Out", NULL, "MIXHPRSEL Mux"}, + + {"SPK Left Out", NULL, "Left Speaker Mixer"}, + {"SPK Right Out", NULL, "Right Speaker Mixer"}, + {"RCV Left Out", NULL, "Left Receiver Mixer"}, + + {"LINMOD Mux", "Left and Right", "Right Receiver Mixer"}, + {"LINMOD Mux", "Left Only", "Left Receiver Mixer"}, + {"RCV Right Out", NULL, "LINMOD Mux"}, + + {"HPL", NULL, "HP Left Out"}, + {"HPR", NULL, "HP Right Out"}, + {"SPKL", NULL, "SPK Left Out"}, + {"SPKR", NULL, "SPK Right Out"}, + {"RCVL", NULL, "RCV Left Out"}, + {"RCVR", NULL, "RCV Right Out"}, +}; + +static const struct snd_soc_dapm_route max98091_dapm_routes[] = { + /* DMIC inputs */ + {"DMIC3", NULL, "DMIC3_ENA"}, + {"DMIC4", NULL, "DMIC4_ENA"}, + {"DMIC3", NULL, "AHPF"}, + {"DMIC4", NULL, "AHPF"}, +}; + +static int max98090_add_widgets(struct snd_soc_component *component) +{ + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_add_component_controls(component, max98090_snd_controls, + ARRAY_SIZE(max98090_snd_controls)); + + if (max98090->devtype == MAX98091) { + snd_soc_add_component_controls(component, max98091_snd_controls, + ARRAY_SIZE(max98091_snd_controls)); + } + + snd_soc_dapm_new_controls(dapm, max98090_dapm_widgets, + ARRAY_SIZE(max98090_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, max98090_dapm_routes, + ARRAY_SIZE(max98090_dapm_routes)); + + if (max98090->devtype == MAX98091) { + snd_soc_dapm_new_controls(dapm, max98091_dapm_widgets, + ARRAY_SIZE(max98091_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, max98091_dapm_routes, + ARRAY_SIZE(max98091_dapm_routes)); + } + + return 0; +} + +static const int pclk_rates[] = { + 12000000, 12000000, 13000000, 13000000, + 16000000, 16000000, 19200000, 19200000 +}; + +static const int lrclk_rates[] = { + 8000, 16000, 8000, 16000, + 8000, 16000, 8000, 16000 +}; + +static const int user_pclk_rates[] = { + 13000000, 13000000, 19200000, 19200000, +}; + +static const int user_lrclk_rates[] = { + 44100, 48000, 44100, 48000, +}; + +static const unsigned long long ni_value[] = { + 3528, 768, 441, 8 +}; + +static const unsigned long long mi_value[] = { + 8125, 1625, 1500, 25 +}; + +static void max98090_configure_bclk(struct snd_soc_component *component) +{ + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + unsigned long long ni; + int i; + + if (!max98090->sysclk) { + dev_err(component->dev, "No SYSCLK configured\n"); + return; + } + + if (!max98090->bclk || !max98090->lrclk) { + dev_err(component->dev, "No audio clocks configured\n"); + return; + } + + /* Skip configuration when operating as slave */ + if (!(snd_soc_component_read(component, M98090_REG_MASTER_MODE) & + M98090_MAS_MASK)) { + return; + } + + /* Check for supported PCLK to LRCLK ratios */ + for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) { + if ((pclk_rates[i] == max98090->sysclk) && + (lrclk_rates[i] == max98090->lrclk)) { + dev_dbg(component->dev, + "Found supported PCLK to LRCLK rates 0x%x\n", + i + 0x8); + + snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE, + M98090_FREQ_MASK, + (i + 0x8) << M98090_FREQ_SHIFT); + snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, 0); + return; + } + } + + /* Check for user calculated MI and NI ratios */ + for (i = 0; i < ARRAY_SIZE(user_pclk_rates); i++) { + if ((user_pclk_rates[i] == max98090->sysclk) && + (user_lrclk_rates[i] == max98090->lrclk)) { + dev_dbg(component->dev, + "Found user supported PCLK to LRCLK rates\n"); + dev_dbg(component->dev, "i %d ni %lld mi %lld\n", + i, ni_value[i], mi_value[i]); + + snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE, + M98090_FREQ_MASK, 0); + snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, + 1 << M98090_USE_M1_SHIFT); + + snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_MSB, + (ni_value[i] >> 8) & 0x7F); + snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_LSB, + ni_value[i] & 0xFF); + snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_MI_MSB, + (mi_value[i] >> 8) & 0x7F); + snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_MI_LSB, + mi_value[i] & 0xFF); + + return; + } + } + + /* + * Calculate based on MI = 65536 (not as good as either method above) + */ + snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE, + M98090_FREQ_MASK, 0); + snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, 0); + + /* + * Configure NI when operating as master + * Note: There is a small, but significant audio quality improvement + * by calculating ni and mi. + */ + ni = 65536ULL * (max98090->lrclk < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)max98090->lrclk; + do_div(ni, (unsigned long long int)max98090->sysclk); + dev_info(component->dev, "No better method found\n"); + dev_info(component->dev, "Calculating ni %lld with mi 65536\n", ni); + snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_MSB, + (ni >> 8) & 0x7F); + snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_LSB, ni & 0xFF); +} + +static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + struct max98090_cdata *cdata; + u8 regval, tdm_regval; + + max98090->dai_fmt = fmt; + cdata = &max98090->dai[0]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + regval = 0; + tdm_regval = 0; + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Set to consumer mode PLL - MAS mode off */ + snd_soc_component_write(component, + M98090_REG_CLOCK_RATIO_NI_MSB, 0x00); + snd_soc_component_write(component, + M98090_REG_CLOCK_RATIO_NI_LSB, 0x00); + snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, 0); + max98090->master = false; + break; + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ + if (max98090->tdm_slots == 4) { + /* TDM */ + regval |= M98090_MAS_MASK | + M98090_BSEL_64; + } else if (max98090->tdm_slots == 3) { + /* TDM */ + regval |= M98090_MAS_MASK | + M98090_BSEL_48; + } else { + /* Few TDM slots, or No TDM */ + regval |= M98090_MAS_MASK | + M98090_BSEL_32; + } + max98090->master = true; + break; + default: + dev_err(component->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + snd_soc_component_write(component, M98090_REG_MASTER_MODE, regval); + + regval = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98090_DLY_MASK; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + case SND_SOC_DAIFMT_RIGHT_J: + regval |= M98090_RJ_MASK; + break; + case SND_SOC_DAIFMT_DSP_A: + tdm_regval |= M98090_TDM_MASK; + break; + default: + dev_err(component->dev, "DAI format unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98090_WCI_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98090_BCI_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98090_BCI_MASK|M98090_WCI_MASK; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported"); + return -EINVAL; + } + + /* + * This accommodates an inverted logic in the MAX98090 chip + * for Bit Clock Invert (BCI). The inverted logic is only + * seen for the case of TDM mode. The remaining cases have + * normal logic. + */ + if (tdm_regval) + regval ^= M98090_BCI_MASK; + + snd_soc_component_write(component, + M98090_REG_INTERFACE_FORMAT, regval); + + regval = 0; + if (tdm_regval) + regval = max98090->tdm_lslot << M98090_TDM_SLOTL_SHIFT | + max98090->tdm_rslot << M98090_TDM_SLOTR_SHIFT | + 0 << M98090_TDM_SLOTDLY_SHIFT; + + snd_soc_component_write(component, M98090_REG_TDM_FORMAT, regval); + snd_soc_component_write(component, M98090_REG_TDM_CONTROL, tdm_regval); + } + + return 0; +} + +static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + + if (slots < 0 || slots > 4) + return -EINVAL; + + if (slot_width != 16) + return -EINVAL; + + if (rx_mask != tx_mask) + return -EINVAL; + + if (!rx_mask) + return -EINVAL; + + max98090->tdm_slots = slots; + max98090->tdm_lslot = ffs(rx_mask) - 1; + max98090->tdm_rslot = fls(rx_mask) - 1; + + return 0; +} + +static int max98090_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (IS_ERR(max98090->mclk)) + break; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(max98090->mclk); + } else { + ret = clk_prepare_enable(max98090->mclk); + if (ret) + return ret; + } + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regcache_sync(max98090->regmap); + if (ret != 0) { + dev_err(component->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + break; + + case SND_SOC_BIAS_OFF: + /* Set internal pull-up to lowest power mode */ + snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT, + M98090_JDWK_MASK, M98090_JDWK_MASK); + regcache_mark_dirty(max98090->regmap); + break; + } + return 0; +} + +static const int dmic_divisors[] = { 2, 3, 4, 5, 6, 8 }; + +static const int comp_lrclk_rates[] = { + 8000, 16000, 32000, 44100, 48000, 96000 +}; + +struct dmic_table { + int pclk; + struct { + int freq; + int comp[6]; /* One each for 8, 16, 32, 44.1, 48, and 96 kHz */ + } settings[6]; /* One for each dmic divisor. */ +}; + +static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */ + { + .pclk = 11289600, + .settings = { + { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + }, + }, + { + .pclk = 12000000, + .settings = { + { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + } + }, + { + .pclk = 12288000, + .settings = { + { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + } + }, + { + .pclk = 13000000, + .settings = { + { .freq = 2, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 1, .comp = { 7, 8, 0, 0, 0, 0 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 0, .comp = { 7, 8, 4, 4, 5, 5 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } }, + } + }, + { + .pclk = 19200000, + .settings = { + { .freq = 2, .comp = { 0, 0, 0, 0, 0, 0 } }, + { .freq = 1, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 2, 2, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } }, + } + }, +}; + +static int max98090_find_divisor(int target_freq, int pclk) +{ + int current_diff = INT_MAX; + int test_diff; + int divisor_index = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(dmic_divisors); i++) { + test_diff = abs(target_freq - (pclk / dmic_divisors[i])); + if (test_diff < current_diff) { + current_diff = test_diff; + divisor_index = i; + } + } + + return divisor_index; +} + +static int max98090_find_closest_pclk(int pclk) +{ + int m1; + int m2; + int i; + + for (i = 0; i < ARRAY_SIZE(dmic_table); i++) { + if (pclk == dmic_table[i].pclk) + return i; + if (pclk < dmic_table[i].pclk) { + if (i == 0) + return i; + m1 = pclk - dmic_table[i-1].pclk; + m2 = dmic_table[i].pclk - pclk; + if (m1 < m2) + return i - 1; + else + return i; + } + } + + return -EINVAL; +} + +static int max98090_configure_dmic(struct max98090_priv *max98090, + int target_dmic_clk, int pclk, int fs) +{ + int micclk_index; + int pclk_index; + int dmic_freq; + int dmic_comp; + int i; + + pclk_index = max98090_find_closest_pclk(pclk); + if (pclk_index < 0) + return pclk_index; + + micclk_index = max98090_find_divisor(target_dmic_clk, pclk); + + for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) { + if (fs <= (comp_lrclk_rates[i] + comp_lrclk_rates[i+1]) / 2) + break; + } + + dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq; + dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i]; + + regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE, + M98090_MICCLK_MASK, + micclk_index << M98090_MICCLK_SHIFT); + + regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_CONFIG, + M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK, + dmic_comp << M98090_DMIC_COMP_SHIFT | + dmic_freq << M98090_DMIC_FREQ_SHIFT); + + return 0; +} + +static int max98090_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + unsigned int fmt = max98090->dai_fmt; + + /* Remove 24-bit format support if it is not in right justified mode. */ + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_RIGHT_J) { + substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(substream->runtime, 0, 16, 16); + } + return 0; +} + +static int max98090_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + struct max98090_cdata *cdata; + + cdata = &max98090->dai[0]; + max98090->bclk = snd_soc_params_to_bclk(params); + if (params_channels(params) == 1) + max98090->bclk *= 2; + + max98090->lrclk = params_rate(params); + + switch (params_width(params)) { + case 16: + snd_soc_component_update_bits(component, M98090_REG_INTERFACE_FORMAT, + M98090_WS_MASK, 0); + break; + default: + return -EINVAL; + } + + if (max98090->master) + max98090_configure_bclk(component); + + cdata->rate = max98090->lrclk; + + /* Update filter mode */ + if (max98090->lrclk < 24000) + snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG, + M98090_MODE_MASK, 0); + else + snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG, + M98090_MODE_MASK, M98090_MODE_MASK); + + /* Update sample rate mode */ + if (max98090->lrclk < 50000) + snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG, + M98090_DHF_MASK, 0); + else + snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG, + M98090_DHF_MASK, M98090_DHF_MASK); + + max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk, + max98090->lrclk); + + return 0; +} + +/* + * PLL / Sysclk + */ +static int max98090_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + + /* Requested clock frequency is already setup */ + if (freq == max98090->sysclk) + return 0; + + if (!IS_ERR(max98090->mclk)) { + freq = clk_round_rate(max98090->mclk, freq); + clk_set_rate(max98090->mclk, freq); + } + + /* Setup clocks for slave mode, and using the PLL + * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) + * 0x02 (when master clk is 20MHz to 40MHz).. + * 0x03 (when master clk is 40MHz to 60MHz).. + */ + if ((freq >= 10000000) && (freq <= 20000000)) { + snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV1); + max98090->pclk = freq; + } else if ((freq > 20000000) && (freq <= 40000000)) { + snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV2); + max98090->pclk = freq >> 1; + } else if ((freq > 40000000) && (freq <= 60000000)) { + snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV4); + max98090->pclk = freq >> 2; + } else { + dev_err(component->dev, "Invalid master clock frequency\n"); + return -EINVAL; + } + + max98090->sysclk = freq; + + return 0; +} + +static int max98090_dai_mute(struct snd_soc_dai *codec_dai, int mute, + int direction) +{ + struct snd_soc_component *component = codec_dai->component; + int regval; + + regval = mute ? M98090_DVM_MASK : 0; + snd_soc_component_update_bits(component, M98090_REG_DAI_PLAYBACK_LEVEL, + M98090_DVM_MASK, regval); + + return 0; +} + +static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!max98090->master && snd_soc_dai_active(dai) == 1) + queue_delayed_work(system_power_efficient_wq, + &max98090->pll_det_enable_work, + msecs_to_jiffies(10)); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (!max98090->master && snd_soc_dai_active(dai) == 1) + schedule_work(&max98090->pll_det_disable_work); + break; + default: + break; + } + + return 0; +} + +static void max98090_pll_det_enable_work(struct work_struct *work) +{ + struct max98090_priv *max98090 = + container_of(work, struct max98090_priv, + pll_det_enable_work.work); + struct snd_soc_component *component = max98090->component; + unsigned int status, mask; + + /* + * Clear status register in order to clear possibly already occurred + * PLL unlock. If PLL hasn't still locked, the status will be set + * again and PLL unlock interrupt will occur. + * Note this will clear all status bits + */ + regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &status); + + /* + * Queue jack work in case jack state has just changed but handler + * hasn't run yet + */ + regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask); + status &= mask; + if (status & M98090_JDET_MASK) + queue_delayed_work(system_power_efficient_wq, + &max98090->jack_work, + msecs_to_jiffies(100)); + + /* Enable PLL unlock interrupt */ + snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S, + M98090_IULK_MASK, + 1 << M98090_IULK_SHIFT); +} + +static void max98090_pll_det_disable_work(struct work_struct *work) +{ + struct max98090_priv *max98090 = + container_of(work, struct max98090_priv, pll_det_disable_work); + struct snd_soc_component *component = max98090->component; + + cancel_delayed_work_sync(&max98090->pll_det_enable_work); + + /* Disable PLL unlock interrupt */ + snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S, + M98090_IULK_MASK, 0); +} + +static void max98090_pll_work(struct max98090_priv *max98090) +{ + struct snd_soc_component *component = max98090->component; + unsigned int pll; + int i; + + if (!snd_soc_component_active(component)) + return; + + dev_info_ratelimited(component->dev, "PLL unlocked\n"); + + /* + * As the datasheet suggested, the maximum PLL lock time should be + * 7 msec. The workaround resets the codec softly by toggling SHDN + * off and on if PLL failed to lock for 10 msec. Notably, there is + * no suggested hold time for SHDN off. + */ + + /* Toggle shutdown OFF then ON */ + snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_MASK, 0); + snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_MASK, M98090_SHDNN_MASK); + + for (i = 0; i < 10; ++i) { + /* Give PLL time to lock */ + usleep_range(1000, 1200); + + /* Check lock status */ + pll = snd_soc_component_read( + component, M98090_REG_DEVICE_STATUS); + if (!(pll & M98090_ULK_MASK)) + break; + } +} + +static void max98090_jack_work(struct work_struct *work) +{ + struct max98090_priv *max98090 = container_of(work, + struct max98090_priv, + jack_work.work); + struct snd_soc_component *component = max98090->component; + int status = 0; + int reg; + + /* Read a second time */ + if (max98090->jack_state == M98090_JACK_STATE_NO_HEADSET) { + + /* Strong pull up allows mic detection */ + snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT, + M98090_JDWK_MASK, 0); + + msleep(50); + + snd_soc_component_read(component, M98090_REG_JACK_STATUS); + + /* Weak pull up allows only insertion detection */ + snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT, + M98090_JDWK_MASK, M98090_JDWK_MASK); + } + + reg = snd_soc_component_read(component, M98090_REG_JACK_STATUS); + + switch (reg & (M98090_LSNS_MASK | M98090_JKSNS_MASK)) { + case M98090_LSNS_MASK | M98090_JKSNS_MASK: + dev_dbg(component->dev, "No Headset Detected\n"); + + max98090->jack_state = M98090_JACK_STATE_NO_HEADSET; + + status |= 0; + + break; + + case 0: + if (max98090->jack_state == + M98090_JACK_STATE_HEADSET) { + + dev_dbg(component->dev, + "Headset Button Down Detected\n"); + + /* + * max98090_headset_button_event(codec) + * could be defined, then called here. + */ + + status |= SND_JACK_HEADSET; + status |= SND_JACK_BTN_0; + + break; + } + + /* Line is reported as Headphone */ + /* Nokia Headset is reported as Headphone */ + /* Mono Headphone is reported as Headphone */ + dev_dbg(component->dev, "Headphone Detected\n"); + + max98090->jack_state = M98090_JACK_STATE_HEADPHONE; + + status |= SND_JACK_HEADPHONE; + + break; + + case M98090_JKSNS_MASK: + dev_dbg(component->dev, "Headset Detected\n"); + + max98090->jack_state = M98090_JACK_STATE_HEADSET; + + status |= SND_JACK_HEADSET; + + break; + + default: + dev_dbg(component->dev, "Unrecognized Jack Status\n"); + break; + } + + snd_soc_jack_report(max98090->jack, status, + SND_JACK_HEADSET | SND_JACK_BTN_0); +} + +static irqreturn_t max98090_interrupt(int irq, void *data) +{ + struct max98090_priv *max98090 = data; + struct snd_soc_component *component = max98090->component; + int ret; + unsigned int mask; + unsigned int active; + + /* Treat interrupt before codec is initialized as spurious */ + if (component == NULL) + return IRQ_NONE; + + dev_dbg(component->dev, "***** max98090_interrupt *****\n"); + + ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask); + + if (ret != 0) { + dev_err(component->dev, + "failed to read M98090_REG_INTERRUPT_S: %d\n", + ret); + return IRQ_NONE; + } + + ret = regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &active); + + if (ret != 0) { + dev_err(component->dev, + "failed to read M98090_REG_DEVICE_STATUS: %d\n", + ret); + return IRQ_NONE; + } + + dev_dbg(component->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n", + active, mask, active & mask); + + active &= mask; + + if (!active) + return IRQ_NONE; + + if (active & M98090_CLD_MASK) + dev_err(component->dev, "M98090_CLD_MASK\n"); + + if (active & M98090_SLD_MASK) + dev_dbg(component->dev, "M98090_SLD_MASK\n"); + + if (active & M98090_ULK_MASK) { + dev_dbg(component->dev, "M98090_ULK_MASK\n"); + max98090_pll_work(max98090); + } + + if (active & M98090_JDET_MASK) { + dev_dbg(component->dev, "M98090_JDET_MASK\n"); + + pm_wakeup_event(component->dev, 100); + + queue_delayed_work(system_power_efficient_wq, + &max98090->jack_work, + msecs_to_jiffies(100)); + } + + if (active & M98090_DRCACT_MASK) + dev_dbg(component->dev, "M98090_DRCACT_MASK\n"); + + if (active & M98090_DRCCLP_MASK) + dev_err(component->dev, "M98090_DRCCLP_MASK\n"); + + return IRQ_HANDLED; +} + +/** + * max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ + * + * @component: MAX98090 component + * @jack: jack to report detection events on + * + * Enable microphone detection via IRQ on the MAX98090. If GPIOs are + * being used to bring out signals to the processor then only platform + * data configuration is needed for MAX98090 and processor GPIOs should + * be configured using snd_soc_jack_add_gpios() instead. + * + * If no jack is supplied detection will be disabled. + */ +int max98090_mic_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "max98090_mic_detect\n"); + + max98090->jack = jack; + if (jack) { + snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S, + M98090_IJDET_MASK, + 1 << M98090_IJDET_SHIFT); + } else { + snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S, + M98090_IJDET_MASK, + 0); + } + + /* Send an initial empty report */ + snd_soc_jack_report(max98090->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + + queue_delayed_work(system_power_efficient_wq, + &max98090->jack_work, + msecs_to_jiffies(100)); + + return 0; +} +EXPORT_SYMBOL_GPL(max98090_mic_detect); + +#define MAX98090_RATES SNDRV_PCM_RATE_8000_96000 +#define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops max98090_dai_ops = { + .startup = max98090_dai_startup, + .set_sysclk = max98090_dai_set_sysclk, + .set_fmt = max98090_dai_set_fmt, + .set_tdm_slot = max98090_set_tdm_slot, + .hw_params = max98090_dai_hw_params, + .mute_stream = max98090_dai_mute, + .trigger = max98090_dai_trigger, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver max98090_dai = { + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = MAX98090_RATES, + .formats = MAX98090_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98090_RATES, + .formats = MAX98090_FORMATS, + }, + .ops = &max98090_dai_ops, +}; + +static int max98090_probe(struct snd_soc_component *component) +{ + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + struct max98090_cdata *cdata; + enum max98090_type devtype; + int ret = 0; + int err; + unsigned int micbias; + + dev_dbg(component->dev, "max98090_probe\n"); + + max98090->mclk = devm_clk_get(component->dev, "mclk"); + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + max98090->component = component; + + /* Reset the codec, the DSP core, and disable all interrupts */ + max98090_reset(max98090); + + /* Initialize private data */ + + max98090->sysclk = (unsigned)-1; + max98090->pclk = (unsigned)-1; + max98090->master = false; + + cdata = &max98090->dai[0]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + + max98090->lin_state = 0; + max98090->pa1en = 0; + max98090->pa2en = 0; + + max98090->tdm_lslot = 0; + max98090->tdm_rslot = 1; + + ret = snd_soc_component_read(component, M98090_REG_REVISION_ID); + if (ret < 0) { + dev_err(component->dev, "Failed to read device revision: %d\n", + ret); + goto err_access; + } + + if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) { + devtype = MAX98090; + dev_info(component->dev, "MAX98090 REVID=0x%02x\n", ret); + } else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) { + devtype = MAX98091; + dev_info(component->dev, "MAX98091 REVID=0x%02x\n", ret); + } else { + devtype = MAX98090; + dev_err(component->dev, "Unrecognized revision 0x%02x\n", ret); + } + + if (max98090->devtype != devtype) { + dev_warn(component->dev, "Mismatch in DT specified CODEC type.\n"); + max98090->devtype = devtype; + } + + max98090->jack_state = M98090_JACK_STATE_NO_HEADSET; + + INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work); + INIT_DELAYED_WORK(&max98090->pll_det_enable_work, + max98090_pll_det_enable_work); + INIT_WORK(&max98090->pll_det_disable_work, + max98090_pll_det_disable_work); + + /* Enable jack detection */ + snd_soc_component_write(component, M98090_REG_JACK_DETECT, + M98090_JDETEN_MASK | M98090_JDEB_25MS); + + /* + * Clear any old interrupts. + * An old interrupt ocurring prior to installing the ISR + * can keep a new interrupt from generating a trigger. + */ + snd_soc_component_read(component, M98090_REG_DEVICE_STATUS); + + /* High Performance is default */ + snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL, + M98090_DACHP_MASK, + 1 << M98090_DACHP_SHIFT); + snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL, + M98090_PERFMODE_MASK, + 0 << M98090_PERFMODE_SHIFT); + snd_soc_component_update_bits(component, M98090_REG_ADC_CONTROL, + M98090_ADCHP_MASK, + 1 << M98090_ADCHP_SHIFT); + + /* Turn on VCM bandgap reference */ + snd_soc_component_write(component, M98090_REG_BIAS_CONTROL, + M98090_VCM_MODE_MASK); + + err = device_property_read_u32(component->dev, "maxim,micbias", &micbias); + if (err) { + micbias = M98090_MBVSEL_2V8; + dev_info(component->dev, "use default 2.8v micbias\n"); + } else if (micbias > M98090_MBVSEL_2V8) { + dev_err(component->dev, "micbias out of range 0x%x\n", micbias); + micbias = M98090_MBVSEL_2V8; + } + + snd_soc_component_update_bits(component, M98090_REG_MIC_BIAS_VOLTAGE, + M98090_MBVSEL_MASK, micbias); + + max98090_add_widgets(component); + +err_access: + return ret; +} + +static void max98090_remove(struct snd_soc_component *component) +{ + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&max98090->jack_work); + cancel_delayed_work_sync(&max98090->pll_det_enable_work); + cancel_work_sync(&max98090->pll_det_disable_work); + max98090->component = NULL; +} + +static void max98090_seq_notifier(struct snd_soc_component *component, + enum snd_soc_dapm_type event, int subseq) +{ + struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); + + if (max98090->shdn_pending) { + snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_MASK, 0); + msleep(40); + snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_MASK, M98090_SHDNN_MASK); + max98090->shdn_pending = false; + } +} + +static const struct snd_soc_component_driver soc_component_dev_max98090 = { + .probe = max98090_probe, + .remove = max98090_remove, + .seq_notifier = max98090_seq_notifier, + .set_bias_level = max98090_set_bias_level, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config max98090_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = MAX98090_MAX_REGISTER, + .reg_defaults = max98090_reg, + .num_reg_defaults = ARRAY_SIZE(max98090_reg), + .volatile_reg = max98090_volatile_register, + .readable_reg = max98090_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct i2c_device_id max98090_i2c_id[] = { + { "max98090", MAX98090 }, + { "max98091", MAX98091 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); + +static int max98090_i2c_probe(struct i2c_client *i2c) +{ + struct max98090_priv *max98090; + const struct acpi_device_id *acpi_id; + kernel_ulong_t driver_data = 0; + int ret; + + pr_debug("max98090_i2c_probe\n"); + + max98090 = devm_kzalloc(&i2c->dev, sizeof(struct max98090_priv), + GFP_KERNEL); + if (max98090 == NULL) + return -ENOMEM; + + if (ACPI_HANDLE(&i2c->dev)) { + acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table, + &i2c->dev); + if (!acpi_id) { + dev_err(&i2c->dev, "No driver data\n"); + return -EINVAL; + } + driver_data = acpi_id->driver_data; + } else { + const struct i2c_device_id *i2c_id = + i2c_match_id(max98090_i2c_id, i2c); + driver_data = i2c_id->driver_data; + } + + max98090->devtype = driver_data; + i2c_set_clientdata(i2c, max98090); + max98090->pdata = i2c->dev.platform_data; + + ret = of_property_read_u32(i2c->dev.of_node, "maxim,dmic-freq", + &max98090->dmic_freq); + if (ret < 0) + max98090->dmic_freq = MAX98090_DEFAULT_DMIC_FREQ; + + max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap); + if (IS_ERR(max98090->regmap)) { + ret = PTR_ERR(max98090->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + goto err_enable; + } + + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "max98090_interrupt", max98090); + if (ret < 0) { + dev_err(&i2c->dev, "request_irq failed: %d\n", + ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_max98090, + &max98090_dai, 1); +err_enable: + return ret; +} + +static void max98090_i2c_shutdown(struct i2c_client *i2c) +{ + struct max98090_priv *max98090 = dev_get_drvdata(&i2c->dev); + + /* + * Enable volume smoothing, disable zero cross. This will cause + * a quick 40ms ramp to mute on shutdown. + */ + regmap_write(max98090->regmap, + M98090_REG_LEVEL_CONTROL, M98090_VSENN_MASK); + regmap_write(max98090->regmap, + M98090_REG_DEVICE_SHUTDOWN, 0x00); + msleep(40); +} + +static void max98090_i2c_remove(struct i2c_client *client) +{ + max98090_i2c_shutdown(client); +} + +#ifdef CONFIG_PM +static int max98090_runtime_resume(struct device *dev) +{ + struct max98090_priv *max98090 = dev_get_drvdata(dev); + + regcache_cache_only(max98090->regmap, false); + + max98090_reset(max98090); + + regcache_sync(max98090->regmap); + + return 0; +} + +static int max98090_runtime_suspend(struct device *dev) +{ + struct max98090_priv *max98090 = dev_get_drvdata(dev); + + regcache_cache_only(max98090->regmap, true); + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int max98090_resume(struct device *dev) +{ + struct max98090_priv *max98090 = dev_get_drvdata(dev); + unsigned int status; + + regcache_mark_dirty(max98090->regmap); + + max98090_reset(max98090); + + /* clear IRQ status */ + regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &status); + + regcache_sync(max98090->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops max98090_pm = { + SET_RUNTIME_PM_OPS(max98090_runtime_suspend, + max98090_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume) +}; + +#ifdef CONFIG_OF +static const struct of_device_id max98090_of_match[] = { + { .compatible = "maxim,max98090", }, + { .compatible = "maxim,max98091", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98090_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98090_acpi_match[] = { + { "193C9890", MAX98090 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, max98090_acpi_match); +#endif + +static struct i2c_driver max98090_i2c_driver = { + .driver = { + .name = "max98090", + .pm = &max98090_pm, + .of_match_table = of_match_ptr(max98090_of_match), + .acpi_match_table = ACPI_PTR(max98090_acpi_match), + }, + .probe = max98090_i2c_probe, + .shutdown = max98090_i2c_shutdown, + .remove = max98090_i2c_remove, + .id_table = max98090_i2c_id, +}; + +module_i2c_driver(max98090_i2c_driver); + +MODULE_DESCRIPTION("ALSA SoC MAX98090 driver"); +MODULE_AUTHOR("Peter Hsiang, Jesse Marroqin, Jerry Wong"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h new file mode 100644 index 0000000000..6ce8dd176e --- /dev/null +++ b/sound/soc/codecs/max98090.h @@ -0,0 +1,1549 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * max98090.h -- MAX98090 ALSA SoC Audio driver + * + * Copyright 2011-2012 Maxim Integrated Products + */ + +#ifndef _MAX98090_H +#define _MAX98090_H + +/* + * The default operating frequency for a DMIC attached to the codec. + * This can be overridden by a device tree property. + */ +#define MAX98090_DEFAULT_DMIC_FREQ 2500000 + +/* + * MAX98090 Register Definitions + */ + +#define M98090_REG_SOFTWARE_RESET 0x00 +#define M98090_REG_DEVICE_STATUS 0x01 +#define M98090_REG_JACK_STATUS 0x02 +#define M98090_REG_INTERRUPT_S 0x03 +#define M98090_REG_QUICK_SYSTEM_CLOCK 0x04 +#define M98090_REG_QUICK_SAMPLE_RATE 0x05 +#define M98090_REG_DAI_INTERFACE 0x06 +#define M98090_REG_DAC_PATH 0x07 +#define M98090_REG_MIC_DIRECT_TO_ADC 0x08 +#define M98090_REG_LINE_TO_ADC 0x09 +#define M98090_REG_ANALOG_MIC_LOOP 0x0A +#define M98090_REG_ANALOG_LINE_LOOP 0x0B +#define M98090_REG_RESERVED 0x0C +#define M98090_REG_LINE_INPUT_CONFIG 0x0D +#define M98090_REG_LINE_INPUT_LEVEL 0x0E +#define M98090_REG_INPUT_MODE 0x0F +#define M98090_REG_MIC1_INPUT_LEVEL 0x10 +#define M98090_REG_MIC2_INPUT_LEVEL 0x11 +#define M98090_REG_MIC_BIAS_VOLTAGE 0x12 +#define M98090_REG_DIGITAL_MIC_ENABLE 0x13 +#define M98090_REG_DIGITAL_MIC_CONFIG 0x14 +#define M98090_REG_LEFT_ADC_MIXER 0x15 +#define M98090_REG_RIGHT_ADC_MIXER 0x16 +#define M98090_REG_LEFT_ADC_LEVEL 0x17 +#define M98090_REG_RIGHT_ADC_LEVEL 0x18 +#define M98090_REG_ADC_BIQUAD_LEVEL 0x19 +#define M98090_REG_ADC_SIDETONE 0x1A +#define M98090_REG_SYSTEM_CLOCK 0x1B +#define M98090_REG_CLOCK_MODE 0x1C +#define M98090_REG_CLOCK_RATIO_NI_MSB 0x1D +#define M98090_REG_CLOCK_RATIO_NI_LSB 0x1E +#define M98090_REG_CLOCK_RATIO_MI_MSB 0x1F +#define M98090_REG_CLOCK_RATIO_MI_LSB 0x20 +#define M98090_REG_MASTER_MODE 0x21 +#define M98090_REG_INTERFACE_FORMAT 0x22 +#define M98090_REG_TDM_CONTROL 0x23 +#define M98090_REG_TDM_FORMAT 0x24 +#define M98090_REG_IO_CONFIGURATION 0x25 +#define M98090_REG_FILTER_CONFIG 0x26 +#define M98090_REG_DAI_PLAYBACK_LEVEL 0x27 +#define M98090_REG_DAI_PLAYBACK_LEVEL_EQ 0x28 +#define M98090_REG_LEFT_HP_MIXER 0x29 +#define M98090_REG_RIGHT_HP_MIXER 0x2A +#define M98090_REG_HP_CONTROL 0x2B +#define M98090_REG_LEFT_HP_VOLUME 0x2C +#define M98090_REG_RIGHT_HP_VOLUME 0x2D +#define M98090_REG_LEFT_SPK_MIXER 0x2E +#define M98090_REG_RIGHT_SPK_MIXER 0x2F +#define M98090_REG_SPK_CONTROL 0x30 +#define M98090_REG_LEFT_SPK_VOLUME 0x31 +#define M98090_REG_RIGHT_SPK_VOLUME 0x32 +#define M98090_REG_DRC_TIMING 0x33 +#define M98090_REG_DRC_COMPRESSOR 0x34 +#define M98090_REG_DRC_EXPANDER 0x35 +#define M98090_REG_DRC_GAIN 0x36 +#define M98090_REG_RCV_LOUTL_MIXER 0x37 +#define M98090_REG_RCV_LOUTL_CONTROL 0x38 +#define M98090_REG_RCV_LOUTL_VOLUME 0x39 +#define M98090_REG_LOUTR_MIXER 0x3A +#define M98090_REG_LOUTR_CONTROL 0x3B +#define M98090_REG_LOUTR_VOLUME 0x3C +#define M98090_REG_JACK_DETECT 0x3D +#define M98090_REG_INPUT_ENABLE 0x3E +#define M98090_REG_OUTPUT_ENABLE 0x3F +#define M98090_REG_LEVEL_CONTROL 0x40 +#define M98090_REG_DSP_FILTER_ENABLE 0x41 +#define M98090_REG_BIAS_CONTROL 0x42 +#define M98090_REG_DAC_CONTROL 0x43 +#define M98090_REG_ADC_CONTROL 0x44 +#define M98090_REG_DEVICE_SHUTDOWN 0x45 +#define M98090_REG_EQUALIZER_BASE 0x46 +#define M98090_REG_RECORD_BIQUAD_BASE 0xAF +#define M98090_REG_DMIC3_VOLUME 0xBE +#define M98090_REG_DMIC4_VOLUME 0xBF +#define M98090_REG_DMIC34_BQ_PREATTEN 0xC0 +#define M98090_REG_RECORD_TDM_SLOT 0xC1 +#define M98090_REG_SAMPLE_RATE 0xC2 +#define M98090_REG_DMIC34_BIQUAD_BASE 0xC3 +#define M98090_REG_REVISION_ID 0xFF + +#define M98090_REG_CNT (0xFF+1) +#define MAX98090_MAX_REGISTER 0xFF + +/* MAX98090 Register Bit Fields */ + +/* + * M98090_REG_SOFTWARE_RESET + */ +#define M98090_SWRESET_MASK (1<<7) +#define M98090_SWRESET_SHIFT 7 +#define M98090_SWRESET_WIDTH 1 + +/* + * M98090_REG_DEVICE_STATUS + */ +#define M98090_CLD_MASK (1<<7) +#define M98090_CLD_SHIFT 7 +#define M98090_CLD_WIDTH 1 +#define M98090_SLD_MASK (1<<6) +#define M98090_SLD_SHIFT 6 +#define M98090_SLD_WIDTH 1 +#define M98090_ULK_MASK (1<<5) +#define M98090_ULK_SHIFT 5 +#define M98090_ULK_WIDTH 1 +#define M98090_JDET_MASK (1<<2) +#define M98090_JDET_SHIFT 2 +#define M98090_JDET_WIDTH 1 +#define M98090_DRCACT_MASK (1<<1) +#define M98090_DRCACT_SHIFT 1 +#define M98090_DRCACT_WIDTH 1 +#define M98090_DRCCLP_MASK (1<<0) +#define M98090_DRCCLP_SHIFT 0 +#define M98090_DRCCLP_WIDTH 1 + +/* + * M98090_REG_JACK_STATUS + */ +#define M98090_LSNS_MASK (1<<2) +#define M98090_LSNS_SHIFT 2 +#define M98090_LSNS_WIDTH 1 +#define M98090_JKSNS_MASK (1<<1) +#define M98090_JKSNS_SHIFT 1 +#define M98090_JKSNS_WIDTH 1 + +/* + * M98090_REG_INTERRUPT_S + */ +#define M98090_ICLD_MASK (1<<7) +#define M98090_ICLD_SHIFT 7 +#define M98090_ICLD_WIDTH 1 +#define M98090_ISLD_MASK (1<<6) +#define M98090_ISLD_SHIFT 6 +#define M98090_ISLD_WIDTH 1 +#define M98090_IULK_MASK (1<<5) +#define M98090_IULK_SHIFT 5 +#define M98090_IULK_WIDTH 1 +#define M98090_IJDET_MASK (1<<2) +#define M98090_IJDET_SHIFT 2 +#define M98090_IJDET_WIDTH 1 +#define M98090_IDRCACT_MASK (1<<1) +#define M98090_IDRCACT_SHIFT 1 +#define M98090_IDRCACT_WIDTH 1 +#define M98090_IDRCCLP_MASK (1<<0) +#define M98090_IDRCCLP_SHIFT 0 +#define M98090_IDRCCLP_WIDTH 1 + +/* + * M98090_REG_QUICK_SYSTEM_CLOCK + */ +#define M98090_26M_MASK (1<<7) +#define M98090_26M_SHIFT 7 +#define M98090_26M_WIDTH 1 +#define M98090_19P2M_MASK (1<<6) +#define M98090_19P2M_SHIFT 6 +#define M98090_19P2M_WIDTH 1 +#define M98090_13M_MASK (1<<5) +#define M98090_13M_SHIFT 5 +#define M98090_13M_WIDTH 1 +#define M98090_12P288M_MASK (1<<4) +#define M98090_12P288M_SHIFT 4 +#define M98090_12P288M_WIDTH 1 +#define M98090_12M_MASK (1<<3) +#define M98090_12M_SHIFT 3 +#define M98090_12M_WIDTH 1 +#define M98090_11P2896M_MASK (1<<2) +#define M98090_11P2896M_SHIFT 2 +#define M98090_11P2896M_WIDTH 1 +#define M98090_256FS_MASK (1<<0) +#define M98090_256FS_SHIFT 0 +#define M98090_256FS_WIDTH 1 +#define M98090_CLK_ALL_SHIFT 0 +#define M98090_CLK_ALL_WIDTH 8 +#define M98090_CLK_ALL_NUM (1< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98095.h" + +enum max98095_type { + MAX98095, +}; + +struct max98095_cdata { + unsigned int rate; + unsigned int fmt; + int eq_sel; + int bq_sel; +}; + +struct max98095_priv { + struct regmap *regmap; + enum max98095_type devtype; + struct max98095_pdata *pdata; + struct clk *mclk; + unsigned int sysclk; + struct max98095_cdata dai[3]; + const char **eq_texts; + const char **bq_texts; + struct soc_enum eq_enum; + struct soc_enum bq_enum; + int eq_textcnt; + int bq_textcnt; + u8 lin_state; + unsigned int mic1pre; + unsigned int mic2pre; + struct snd_soc_jack *headphone_jack; + struct snd_soc_jack *mic_jack; + struct mutex lock; +}; + +static const struct reg_default max98095_reg_def[] = { + { 0xf, 0x00 }, /* 0F */ + { 0x10, 0x00 }, /* 10 */ + { 0x11, 0x00 }, /* 11 */ + { 0x12, 0x00 }, /* 12 */ + { 0x13, 0x00 }, /* 13 */ + { 0x14, 0x00 }, /* 14 */ + { 0x15, 0x00 }, /* 15 */ + { 0x16, 0x00 }, /* 16 */ + { 0x17, 0x00 }, /* 17 */ + { 0x18, 0x00 }, /* 18 */ + { 0x19, 0x00 }, /* 19 */ + { 0x1a, 0x00 }, /* 1A */ + { 0x1b, 0x00 }, /* 1B */ + { 0x1c, 0x00 }, /* 1C */ + { 0x1d, 0x00 }, /* 1D */ + { 0x1e, 0x00 }, /* 1E */ + { 0x1f, 0x00 }, /* 1F */ + { 0x20, 0x00 }, /* 20 */ + { 0x21, 0x00 }, /* 21 */ + { 0x22, 0x00 }, /* 22 */ + { 0x23, 0x00 }, /* 23 */ + { 0x24, 0x00 }, /* 24 */ + { 0x25, 0x00 }, /* 25 */ + { 0x26, 0x00 }, /* 26 */ + { 0x27, 0x00 }, /* 27 */ + { 0x28, 0x00 }, /* 28 */ + { 0x29, 0x00 }, /* 29 */ + { 0x2a, 0x00 }, /* 2A */ + { 0x2b, 0x00 }, /* 2B */ + { 0x2c, 0x00 }, /* 2C */ + { 0x2d, 0x00 }, /* 2D */ + { 0x2e, 0x00 }, /* 2E */ + { 0x2f, 0x00 }, /* 2F */ + { 0x30, 0x00 }, /* 30 */ + { 0x31, 0x00 }, /* 31 */ + { 0x32, 0x00 }, /* 32 */ + { 0x33, 0x00 }, /* 33 */ + { 0x34, 0x00 }, /* 34 */ + { 0x35, 0x00 }, /* 35 */ + { 0x36, 0x00 }, /* 36 */ + { 0x37, 0x00 }, /* 37 */ + { 0x38, 0x00 }, /* 38 */ + { 0x39, 0x00 }, /* 39 */ + { 0x3a, 0x00 }, /* 3A */ + { 0x3b, 0x00 }, /* 3B */ + { 0x3c, 0x00 }, /* 3C */ + { 0x3d, 0x00 }, /* 3D */ + { 0x3e, 0x00 }, /* 3E */ + { 0x3f, 0x00 }, /* 3F */ + { 0x40, 0x00 }, /* 40 */ + { 0x41, 0x00 }, /* 41 */ + { 0x42, 0x00 }, /* 42 */ + { 0x43, 0x00 }, /* 43 */ + { 0x44, 0x00 }, /* 44 */ + { 0x45, 0x00 }, /* 45 */ + { 0x46, 0x00 }, /* 46 */ + { 0x47, 0x00 }, /* 47 */ + { 0x48, 0x00 }, /* 48 */ + { 0x49, 0x00 }, /* 49 */ + { 0x4a, 0x00 }, /* 4A */ + { 0x4b, 0x00 }, /* 4B */ + { 0x4c, 0x00 }, /* 4C */ + { 0x4d, 0x00 }, /* 4D */ + { 0x4e, 0x00 }, /* 4E */ + { 0x4f, 0x00 }, /* 4F */ + { 0x50, 0x00 }, /* 50 */ + { 0x51, 0x00 }, /* 51 */ + { 0x52, 0x00 }, /* 52 */ + { 0x53, 0x00 }, /* 53 */ + { 0x54, 0x00 }, /* 54 */ + { 0x55, 0x00 }, /* 55 */ + { 0x56, 0x00 }, /* 56 */ + { 0x57, 0x00 }, /* 57 */ + { 0x58, 0x00 }, /* 58 */ + { 0x59, 0x00 }, /* 59 */ + { 0x5a, 0x00 }, /* 5A */ + { 0x5b, 0x00 }, /* 5B */ + { 0x5c, 0x00 }, /* 5C */ + { 0x5d, 0x00 }, /* 5D */ + { 0x5e, 0x00 }, /* 5E */ + { 0x5f, 0x00 }, /* 5F */ + { 0x60, 0x00 }, /* 60 */ + { 0x61, 0x00 }, /* 61 */ + { 0x62, 0x00 }, /* 62 */ + { 0x63, 0x00 }, /* 63 */ + { 0x64, 0x00 }, /* 64 */ + { 0x65, 0x00 }, /* 65 */ + { 0x66, 0x00 }, /* 66 */ + { 0x67, 0x00 }, /* 67 */ + { 0x68, 0x00 }, /* 68 */ + { 0x69, 0x00 }, /* 69 */ + { 0x6a, 0x00 }, /* 6A */ + { 0x6b, 0x00 }, /* 6B */ + { 0x6c, 0x00 }, /* 6C */ + { 0x6d, 0x00 }, /* 6D */ + { 0x6e, 0x00 }, /* 6E */ + { 0x6f, 0x00 }, /* 6F */ + { 0x70, 0x00 }, /* 70 */ + { 0x71, 0x00 }, /* 71 */ + { 0x72, 0x00 }, /* 72 */ + { 0x73, 0x00 }, /* 73 */ + { 0x74, 0x00 }, /* 74 */ + { 0x75, 0x00 }, /* 75 */ + { 0x76, 0x00 }, /* 76 */ + { 0x77, 0x00 }, /* 77 */ + { 0x78, 0x00 }, /* 78 */ + { 0x79, 0x00 }, /* 79 */ + { 0x7a, 0x00 }, /* 7A */ + { 0x7b, 0x00 }, /* 7B */ + { 0x7c, 0x00 }, /* 7C */ + { 0x7d, 0x00 }, /* 7D */ + { 0x7e, 0x00 }, /* 7E */ + { 0x7f, 0x00 }, /* 7F */ + { 0x80, 0x00 }, /* 80 */ + { 0x81, 0x00 }, /* 81 */ + { 0x82, 0x00 }, /* 82 */ + { 0x83, 0x00 }, /* 83 */ + { 0x84, 0x00 }, /* 84 */ + { 0x85, 0x00 }, /* 85 */ + { 0x86, 0x00 }, /* 86 */ + { 0x87, 0x00 }, /* 87 */ + { 0x88, 0x00 }, /* 88 */ + { 0x89, 0x00 }, /* 89 */ + { 0x8a, 0x00 }, /* 8A */ + { 0x8b, 0x00 }, /* 8B */ + { 0x8c, 0x00 }, /* 8C */ + { 0x8d, 0x00 }, /* 8D */ + { 0x8e, 0x00 }, /* 8E */ + { 0x8f, 0x00 }, /* 8F */ + { 0x90, 0x00 }, /* 90 */ + { 0x91, 0x00 }, /* 91 */ + { 0x92, 0x30 }, /* 92 */ + { 0x93, 0xF0 }, /* 93 */ + { 0x94, 0x00 }, /* 94 */ + { 0x95, 0x00 }, /* 95 */ + { 0x96, 0x3F }, /* 96 */ + { 0x97, 0x00 }, /* 97 */ + { 0xff, 0x00 }, /* FF */ +}; + +static bool max98095_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98095_001_HOST_INT_STS ... M98095_097_PWR_SYS: + case M98095_0FF_REV_ID: + return true; + default: + return false; + } +} + +static bool max98095_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98095_00F_HOST_CFG ... M98095_097_PWR_SYS: + return true; + default: + return false; + } +} + +static bool max98095_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98095_000_HOST_DATA ... M98095_00E_TEMP_SENSOR_STS: + case M98095_REG_MAX_CACHED + 1 ... M98095_0FF_REV_ID: + return true; + default: + return false; + } +} + +static const struct regmap_config max98095_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = max98095_reg_def, + .num_reg_defaults = ARRAY_SIZE(max98095_reg_def), + .max_register = M98095_0FF_REV_ID, + .cache_type = REGCACHE_RBTREE, + + .readable_reg = max98095_readable, + .writeable_reg = max98095_writeable, + .volatile_reg = max98095_volatile, +}; + +/* + * Load equalizer DSP coefficient configurations registers + */ +static void m98095_eq_band(struct snd_soc_component *component, unsigned int dai, + unsigned int band, u16 *coefs) +{ + unsigned int eq_reg; + unsigned int i; + + if (WARN_ON(band > 4) || + WARN_ON(dai > 1)) + return; + + /* Load the base register address */ + eq_reg = dai ? M98095_142_DAI2_EQ_BASE : M98095_110_DAI1_EQ_BASE; + + /* Add the band address offset, note adjustment for word address */ + eq_reg += band * (M98095_COEFS_PER_BAND << 1); + + /* Step through the registers and coefs */ + for (i = 0; i < M98095_COEFS_PER_BAND; i++) { + snd_soc_component_write(component, eq_reg++, M98095_BYTE1(coefs[i])); + snd_soc_component_write(component, eq_reg++, M98095_BYTE0(coefs[i])); + } +} + +/* + * Load biquad filter coefficient configurations registers + */ +static void m98095_biquad_band(struct snd_soc_component *component, unsigned int dai, + unsigned int band, u16 *coefs) +{ + unsigned int bq_reg; + unsigned int i; + + if (WARN_ON(band > 1) || + WARN_ON(dai > 1)) + return; + + /* Load the base register address */ + bq_reg = dai ? M98095_17E_DAI2_BQ_BASE : M98095_174_DAI1_BQ_BASE; + + /* Add the band address offset, note adjustment for word address */ + bq_reg += band * (M98095_COEFS_PER_BAND << 1); + + /* Step through the registers and coefs */ + for (i = 0; i < M98095_COEFS_PER_BAND; i++) { + snd_soc_component_write(component, bq_reg++, M98095_BYTE1(coefs[i])); + snd_soc_component_write(component, bq_reg++, M98095_BYTE0(coefs[i])); + } +} + +static const char * const max98095_fltr_mode[] = { "Voice", "Music" }; +static SOC_ENUM_SINGLE_DECL(max98095_dai1_filter_mode_enum, + M98095_02E_DAI1_FILTERS, 7, + max98095_fltr_mode); +static SOC_ENUM_SINGLE_DECL(max98095_dai2_filter_mode_enum, + M98095_038_DAI2_FILTERS, 7, + max98095_fltr_mode); + +static const char * const max98095_extmic_text[] = { "None", "MIC1", "MIC2" }; + +static SOC_ENUM_SINGLE_DECL(max98095_extmic_enum, + M98095_087_CFG_MIC, 0, + max98095_extmic_text); + +static const struct snd_kcontrol_new max98095_extmic_mux = + SOC_DAPM_ENUM("External MIC Mux", max98095_extmic_enum); + +static const char * const max98095_linein_text[] = { "INA", "INB" }; + +static SOC_ENUM_SINGLE_DECL(max98095_linein_enum, + M98095_086_CFG_LINE, 6, + max98095_linein_text); + +static const struct snd_kcontrol_new max98095_linein_mux = + SOC_DAPM_ENUM("Linein Input Mux", max98095_linein_enum); + +static const char * const max98095_line_mode_text[] = { + "Stereo", "Differential"}; + +static SOC_ENUM_SINGLE_DECL(max98095_linein_mode_enum, + M98095_086_CFG_LINE, 7, + max98095_line_mode_text); + +static SOC_ENUM_SINGLE_DECL(max98095_lineout_mode_enum, + M98095_086_CFG_LINE, 4, + max98095_line_mode_text); + +static const char * const max98095_dai_fltr[] = { + "Off", "Elliptical-HPF-16k", "Butterworth-HPF-16k", + "Elliptical-HPF-8k", "Butterworth-HPF-8k", "Butterworth-HPF-Fs/240"}; +static SOC_ENUM_SINGLE_DECL(max98095_dai1_dac_filter_enum, + M98095_02E_DAI1_FILTERS, 0, + max98095_dai_fltr); +static SOC_ENUM_SINGLE_DECL(max98095_dai2_dac_filter_enum, + M98095_038_DAI2_FILTERS, 0, + max98095_dai_fltr); +static SOC_ENUM_SINGLE_DECL(max98095_dai3_dac_filter_enum, + M98095_042_DAI3_FILTERS, 0, + max98095_dai_fltr); + +static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + + max98095->mic1pre = sel; + snd_soc_component_update_bits(component, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK, + (1+sel)<value.integer.value[0] = max98095->mic1pre; + return 0; +} + +static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + unsigned int sel = ucontrol->value.integer.value[0]; + + max98095->mic2pre = sel; + snd_soc_component_update_bits(component, M98095_060_LVL_MIC2, M98095_MICPRE_MASK, + (1+sel)<value.integer.value[0] = max98095->mic2pre; + return 0; +} + +static const DECLARE_TLV_DB_RANGE(max98095_micboost_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0) +); + +static const DECLARE_TLV_DB_SCALE(max98095_mic_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98095_adc_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98095_adcboost_tlv, 0, 600, 0); + +static const DECLARE_TLV_DB_RANGE(max98095_hp_tlv, + 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0) +); + +static const DECLARE_TLV_DB_RANGE(max98095_spk_tlv, + 0, 10, TLV_DB_SCALE_ITEM(-5900, 400, 0), + 11, 18, TLV_DB_SCALE_ITEM(-1700, 200, 0), + 19, 27, TLV_DB_SCALE_ITEM(-200, 100, 0), + 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0) +); + +static const DECLARE_TLV_DB_RANGE(max98095_rcv_lout_tlv, + 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0) +); + +static const DECLARE_TLV_DB_RANGE(max98095_lin_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-600, 300, 0), + 3, 3, TLV_DB_SCALE_ITEM(300, 1100, 0), + 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0) +); + +static const struct snd_kcontrol_new max98095_snd_controls[] = { + + SOC_DOUBLE_R_TLV("Headphone Volume", M98095_064_LVL_HP_L, + M98095_065_LVL_HP_R, 0, 31, 0, max98095_hp_tlv), + + SOC_DOUBLE_R_TLV("Speaker Volume", M98095_067_LVL_SPK_L, + M98095_068_LVL_SPK_R, 0, 39, 0, max98095_spk_tlv), + + SOC_SINGLE_TLV("Receiver Volume", M98095_066_LVL_RCV, + 0, 31, 0, max98095_rcv_lout_tlv), + + SOC_DOUBLE_R_TLV("Lineout Volume", M98095_062_LVL_LINEOUT1, + M98095_063_LVL_LINEOUT2, 0, 31, 0, max98095_rcv_lout_tlv), + + SOC_DOUBLE_R("Headphone Switch", M98095_064_LVL_HP_L, + M98095_065_LVL_HP_R, 7, 1, 1), + + SOC_DOUBLE_R("Speaker Switch", M98095_067_LVL_SPK_L, + M98095_068_LVL_SPK_R, 7, 1, 1), + + SOC_SINGLE("Receiver Switch", M98095_066_LVL_RCV, 7, 1, 1), + + SOC_DOUBLE_R("Lineout Switch", M98095_062_LVL_LINEOUT1, + M98095_063_LVL_LINEOUT2, 7, 1, 1), + + SOC_SINGLE_TLV("MIC1 Volume", M98095_05F_LVL_MIC1, 0, 20, 1, + max98095_mic_tlv), + + SOC_SINGLE_TLV("MIC2 Volume", M98095_060_LVL_MIC2, 0, 20, 1, + max98095_mic_tlv), + + SOC_SINGLE_EXT_TLV("MIC1 Boost Volume", + M98095_05F_LVL_MIC1, 5, 2, 0, + max98095_mic1pre_get, max98095_mic1pre_set, + max98095_micboost_tlv), + SOC_SINGLE_EXT_TLV("MIC2 Boost Volume", + M98095_060_LVL_MIC2, 5, 2, 0, + max98095_mic2pre_get, max98095_mic2pre_set, + max98095_micboost_tlv), + + SOC_SINGLE_TLV("Linein Volume", M98095_061_LVL_LINEIN, 0, 5, 1, + max98095_lin_tlv), + + SOC_SINGLE_TLV("ADCL Volume", M98095_05D_LVL_ADC_L, 0, 15, 1, + max98095_adc_tlv), + SOC_SINGLE_TLV("ADCR Volume", M98095_05E_LVL_ADC_R, 0, 15, 1, + max98095_adc_tlv), + + SOC_SINGLE_TLV("ADCL Boost Volume", M98095_05D_LVL_ADC_L, 4, 3, 0, + max98095_adcboost_tlv), + SOC_SINGLE_TLV("ADCR Boost Volume", M98095_05E_LVL_ADC_R, 4, 3, 0, + max98095_adcboost_tlv), + + SOC_SINGLE("EQ1 Switch", M98095_088_CFG_LEVEL, 0, 1, 0), + SOC_SINGLE("EQ2 Switch", M98095_088_CFG_LEVEL, 1, 1, 0), + + SOC_SINGLE("Biquad1 Switch", M98095_088_CFG_LEVEL, 2, 1, 0), + SOC_SINGLE("Biquad2 Switch", M98095_088_CFG_LEVEL, 3, 1, 0), + + SOC_ENUM("DAI1 Filter Mode", max98095_dai1_filter_mode_enum), + SOC_ENUM("DAI2 Filter Mode", max98095_dai2_filter_mode_enum), + SOC_ENUM("DAI1 DAC Filter", max98095_dai1_dac_filter_enum), + SOC_ENUM("DAI2 DAC Filter", max98095_dai2_dac_filter_enum), + SOC_ENUM("DAI3 DAC Filter", max98095_dai3_dac_filter_enum), + + SOC_ENUM("Linein Mode", max98095_linein_mode_enum), + SOC_ENUM("Lineout Mode", max98095_lineout_mode_enum), +}; + +/* Left speaker mixer switch */ +static const struct snd_kcontrol_new max98095_left_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_050_MIX_SPK_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_050_MIX_SPK_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_050_MIX_SPK_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_050_MIX_SPK_LEFT, 4, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_050_MIX_SPK_LEFT, 5, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_050_MIX_SPK_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_050_MIX_SPK_LEFT, 2, 1, 0), +}; + +/* Right speaker mixer switch */ +static const struct snd_kcontrol_new max98095_right_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_051_MIX_SPK_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("Mono DAC2 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("Mono DAC3 Switch", M98095_051_MIX_SPK_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_051_MIX_SPK_RIGHT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_051_MIX_SPK_RIGHT, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_051_MIX_SPK_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_051_MIX_SPK_RIGHT, 2, 1, 0), +}; + +/* Left headphone mixer switch */ +static const struct snd_kcontrol_new max98095_left_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04C_MIX_HP_LEFT, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04C_MIX_HP_LEFT, 5, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_04C_MIX_HP_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_04C_MIX_HP_LEFT, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_04C_MIX_HP_LEFT, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_04C_MIX_HP_LEFT, 2, 1, 0), +}; + +/* Right headphone mixer switch */ +static const struct snd_kcontrol_new max98095_right_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 5, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04D_MIX_HP_RIGHT, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_04D_MIX_HP_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_04D_MIX_HP_RIGHT, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_04D_MIX_HP_RIGHT, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_04D_MIX_HP_RIGHT, 2, 1, 0), +}; + +/* Receiver earpiece mixer switch */ +static const struct snd_kcontrol_new max98095_mono_rcv_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_04F_MIX_RCV, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_04F_MIX_RCV, 5, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_04F_MIX_RCV, 3, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_04F_MIX_RCV, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_04F_MIX_RCV, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_04F_MIX_RCV, 2, 1, 0), +}; + +/* Left lineout mixer switch */ +static const struct snd_kcontrol_new max98095_left_lineout_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_053_MIX_LINEOUT1, 5, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_053_MIX_LINEOUT1, 0, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_053_MIX_LINEOUT1, 3, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_053_MIX_LINEOUT1, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_053_MIX_LINEOUT1, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_053_MIX_LINEOUT1, 2, 1, 0), +}; + +/* Right lineout mixer switch */ +static const struct snd_kcontrol_new max98095_right_lineout_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC1 Switch", M98095_054_MIX_LINEOUT2, 0, 1, 0), + SOC_DAPM_SINGLE("Right DAC1 Switch", M98095_054_MIX_LINEOUT2, 5, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98095_054_MIX_LINEOUT2, 3, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_054_MIX_LINEOUT2, 4, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_054_MIX_LINEOUT2, 1, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_054_MIX_LINEOUT2, 2, 1, 0), +}; + +/* Left ADC mixer switch */ +static const struct snd_kcontrol_new max98095_left_ADC_mixer_controls[] = { + SOC_DAPM_SINGLE("MIC1 Switch", M98095_04A_MIX_ADC_LEFT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_04A_MIX_ADC_LEFT, 6, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_04A_MIX_ADC_LEFT, 3, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_04A_MIX_ADC_LEFT, 2, 1, 0), +}; + +/* Right ADC mixer switch */ +static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = { + SOC_DAPM_SINGLE("MIC1 Switch", M98095_04B_MIX_ADC_RIGHT, 7, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98095_04B_MIX_ADC_RIGHT, 6, 1, 0), + SOC_DAPM_SINGLE("IN1 Switch", M98095_04B_MIX_ADC_RIGHT, 3, 1, 0), + SOC_DAPM_SINGLE("IN2 Switch", M98095_04B_MIX_ADC_RIGHT, 2, 1, 0), +}; + +static int max98095_mic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->reg == M98095_05F_LVL_MIC1) { + snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK, + (1+max98095->mic1pre)<reg, M98095_MICPRE_MASK, + (1+max98095->mic2pre)<reg, M98095_MICPRE_MASK, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * The line inputs are stereo inputs with the left and right + * channels sharing a common PGA power control signal. + */ +static int max98095_line_pga(struct snd_soc_dapm_widget *w, + int event, u8 channel) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + u8 *state; + + if (WARN_ON(!(channel == 1 || channel == 2))) + return -EINVAL; + + state = &max98095->lin_state; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + *state |= channel; + snd_soc_component_update_bits(component, w->reg, + (1 << w->shift), (1 << w->shift)); + break; + case SND_SOC_DAPM_POST_PMD: + *state &= ~channel; + if (*state == 0) { + snd_soc_component_update_bits(component, w->reg, + (1 << w->shift), 0); + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int max98095_pga_in1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98095_line_pga(w, event, 1); +} + +static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + return max98095_line_pga(w, event, 2); +} + +/* + * The stereo line out mixer outputs to two stereo line outs. + * The 2nd pair has a separate set of enables. + */ +static int max98095_lineout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, w->reg, + (1 << (w->shift+2)), (1 << (w->shift+2))); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, w->reg, + (1 << (w->shift+2)), 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_widget max98095_dapm_widgets[] = { + + SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", M98095_090_PWR_EN_IN, 0, 0), + SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", M98095_090_PWR_EN_IN, 1, 0), + + SND_SOC_DAPM_DAC("DACL1", "HiFi Playback", + M98095_091_PWR_EN_OUT, 0, 0), + SND_SOC_DAPM_DAC("DACR1", "HiFi Playback", + M98095_091_PWR_EN_OUT, 1, 0), + SND_SOC_DAPM_DAC("DACM2", "Aux Playback", + M98095_091_PWR_EN_OUT, 2, 0), + SND_SOC_DAPM_DAC("DACM3", "Voice Playback", + M98095_091_PWR_EN_OUT, 2, 0), + + SND_SOC_DAPM_PGA("HP Left Out", M98095_091_PWR_EN_OUT, + 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Right Out", M98095_091_PWR_EN_OUT, + 7, 0, NULL, 0), + + SND_SOC_DAPM_PGA("SPK Left Out", M98095_091_PWR_EN_OUT, + 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPK Right Out", M98095_091_PWR_EN_OUT, + 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("RCV Mono Out", M98095_091_PWR_EN_OUT, + 3, 0, NULL, 0), + + SND_SOC_DAPM_PGA_E("LINE Left Out", M98095_092_PWR_EN_OUT, + 0, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("LINE Right Out", M98095_092_PWR_EN_OUT, + 1, 0, NULL, 0, max98095_lineout_event, SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX("External MIC", SND_SOC_NOPM, 0, 0, + &max98095_extmic_mux), + + SND_SOC_DAPM_MUX("Linein Mux", SND_SOC_NOPM, 0, 0, + &max98095_linein_mux), + + SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0, + &max98095_left_hp_mixer_controls[0], + ARRAY_SIZE(max98095_left_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0, + &max98095_right_hp_mixer_controls[0], + ARRAY_SIZE(max98095_right_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0, + &max98095_left_speaker_mixer_controls[0], + ARRAY_SIZE(max98095_left_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0, + &max98095_right_speaker_mixer_controls[0], + ARRAY_SIZE(max98095_right_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Receiver Mixer", SND_SOC_NOPM, 0, 0, + &max98095_mono_rcv_mixer_controls[0], + ARRAY_SIZE(max98095_mono_rcv_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Lineout Mixer", SND_SOC_NOPM, 0, 0, + &max98095_left_lineout_mixer_controls[0], + ARRAY_SIZE(max98095_left_lineout_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Lineout Mixer", SND_SOC_NOPM, 0, 0, + &max98095_right_lineout_mixer_controls[0], + ARRAY_SIZE(max98095_right_lineout_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98095_left_ADC_mixer_controls[0], + ARRAY_SIZE(max98095_left_ADC_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98095_right_ADC_mixer_controls[0], + ARRAY_SIZE(max98095_right_ADC_mixer_controls)), + + SND_SOC_DAPM_PGA_E("MIC1 Input", M98095_05F_LVL_MIC1, + 5, 0, NULL, 0, max98095_mic_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("MIC2 Input", M98095_060_LVL_MIC2, + 5, 0, NULL, 0, max98095_mic_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("IN1 Input", M98095_090_PWR_EN_IN, + 7, 0, NULL, 0, max98095_pga_in1_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("IN2 Input", M98095_090_PWR_EN_IN, + 7, 0, NULL, 0, max98095_pga_in2_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS("MICBIAS1", M98095_090_PWR_EN_IN, 2, 0), + SND_SOC_DAPM_MICBIAS("MICBIAS2", M98095_090_PWR_EN_IN, 3, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("RCV"), + SND_SOC_DAPM_OUTPUT("OUT1"), + SND_SOC_DAPM_OUTPUT("OUT2"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_OUTPUT("OUT4"), + + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("INA1"), + SND_SOC_DAPM_INPUT("INA2"), + SND_SOC_DAPM_INPUT("INB1"), + SND_SOC_DAPM_INPUT("INB2"), +}; + +static const struct snd_soc_dapm_route max98095_audio_map[] = { + /* Left headphone output mixer */ + {"Left Headphone Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left Headphone Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Headphone Mixer", "IN1 Switch", "IN1 Input"}, + {"Left Headphone Mixer", "IN2 Switch", "IN2 Input"}, + + /* Right headphone output mixer */ + {"Right Headphone Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right Headphone Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Headphone Mixer", "IN1 Switch", "IN1 Input"}, + {"Right Headphone Mixer", "IN2 Switch", "IN2 Input"}, + + /* Left speaker output mixer */ + {"Left Speaker Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left Speaker Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left Speaker Mixer", "Mono DAC2 Switch", "DACM2"}, + {"Left Speaker Mixer", "Mono DAC3 Switch", "DACM3"}, + {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Speaker Mixer", "IN1 Switch", "IN1 Input"}, + {"Left Speaker Mixer", "IN2 Switch", "IN2 Input"}, + + /* Right speaker output mixer */ + {"Right Speaker Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right Speaker Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right Speaker Mixer", "Mono DAC2 Switch", "DACM2"}, + {"Right Speaker Mixer", "Mono DAC3 Switch", "DACM3"}, + {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Speaker Mixer", "IN1 Switch", "IN1 Input"}, + {"Right Speaker Mixer", "IN2 Switch", "IN2 Input"}, + + /* Earpiece/Receiver output mixer */ + {"Receiver Mixer", "Left DAC1 Switch", "DACL1"}, + {"Receiver Mixer", "Right DAC1 Switch", "DACR1"}, + {"Receiver Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Receiver Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Receiver Mixer", "IN1 Switch", "IN1 Input"}, + {"Receiver Mixer", "IN2 Switch", "IN2 Input"}, + + /* Left Lineout output mixer */ + {"Left Lineout Mixer", "Left DAC1 Switch", "DACL1"}, + {"Left Lineout Mixer", "Right DAC1 Switch", "DACR1"}, + {"Left Lineout Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Lineout Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Lineout Mixer", "IN1 Switch", "IN1 Input"}, + {"Left Lineout Mixer", "IN2 Switch", "IN2 Input"}, + + /* Right lineout output mixer */ + {"Right Lineout Mixer", "Left DAC1 Switch", "DACL1"}, + {"Right Lineout Mixer", "Right DAC1 Switch", "DACR1"}, + {"Right Lineout Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Lineout Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Lineout Mixer", "IN1 Switch", "IN1 Input"}, + {"Right Lineout Mixer", "IN2 Switch", "IN2 Input"}, + + {"HP Left Out", NULL, "Left Headphone Mixer"}, + {"HP Right Out", NULL, "Right Headphone Mixer"}, + {"SPK Left Out", NULL, "Left Speaker Mixer"}, + {"SPK Right Out", NULL, "Right Speaker Mixer"}, + {"RCV Mono Out", NULL, "Receiver Mixer"}, + {"LINE Left Out", NULL, "Left Lineout Mixer"}, + {"LINE Right Out", NULL, "Right Lineout Mixer"}, + + {"HPL", NULL, "HP Left Out"}, + {"HPR", NULL, "HP Right Out"}, + {"SPKL", NULL, "SPK Left Out"}, + {"SPKR", NULL, "SPK Right Out"}, + {"RCV", NULL, "RCV Mono Out"}, + {"OUT1", NULL, "LINE Left Out"}, + {"OUT2", NULL, "LINE Right Out"}, + {"OUT3", NULL, "LINE Left Out"}, + {"OUT4", NULL, "LINE Right Out"}, + + /* Left ADC input mixer */ + {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left ADC Mixer", "IN1 Switch", "IN1 Input"}, + {"Left ADC Mixer", "IN2 Switch", "IN2 Input"}, + + /* Right ADC input mixer */ + {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right ADC Mixer", "IN1 Switch", "IN1 Input"}, + {"Right ADC Mixer", "IN2 Switch", "IN2 Input"}, + + /* Inputs */ + {"ADCL", NULL, "Left ADC Mixer"}, + {"ADCR", NULL, "Right ADC Mixer"}, + + {"IN1 Input", NULL, "INA1"}, + {"IN2 Input", NULL, "INA2"}, + + {"MIC1 Input", NULL, "MIC1"}, + {"MIC2 Input", NULL, "MIC2"}, +}; + +/* codec mclk clock divider coefficients */ +static const struct { + u32 rate; + u8 sr; +} rate_table[] = { + {8000, 0x01}, + {11025, 0x02}, + {16000, 0x03}, + {22050, 0x04}, + {24000, 0x05}, + {32000, 0x06}, + {44100, 0x07}, + {48000, 0x08}, + {88200, 0x09}, + {96000, 0x0A}, +}; + +static int rate_value(int rate, u8 *value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i].rate >= rate) { + *value = rate_table[i].sr; + return 0; + } + } + *value = rate_table[0].sr; + return -EINVAL; +} + +static int max98095_dai1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_cdata *cdata; + unsigned long long ni; + unsigned int rate; + u8 regval; + + cdata = &max98095->dai[0]; + + rate = params_rate(params); + + switch (params_width(params)) { + case 16: + snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT, + M98095_DAI_WS, 0); + break; + case 24: + snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT, + M98095_DAI_WS, M98095_DAI_WS); + break; + default: + return -EINVAL; + } + + if (rate_value(rate, ®val)) + return -EINVAL; + + snd_soc_component_update_bits(component, M98095_027_DAI1_CLKMODE, + M98095_CLKMODE_MASK, regval); + cdata->rate = rate; + + /* Configure NI when operating as master */ + if (snd_soc_component_read(component, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) { + if (max98095->sysclk == 0) { + dev_err(component->dev, "Invalid system clock frequency\n"); + return -EINVAL; + } + ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)rate; + do_div(ni, (unsigned long long int)max98095->sysclk); + snd_soc_component_write(component, M98095_028_DAI1_CLKCFG_HI, + (ni >> 8) & 0x7F); + snd_soc_component_write(component, M98095_029_DAI1_CLKCFG_LO, + ni & 0xFF); + } + + /* Update sample rate mode */ + if (rate < 50000) + snd_soc_component_update_bits(component, M98095_02E_DAI1_FILTERS, + M98095_DAI_DHF, 0); + else + snd_soc_component_update_bits(component, M98095_02E_DAI1_FILTERS, + M98095_DAI_DHF, M98095_DAI_DHF); + + return 0; +} + +static int max98095_dai2_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_cdata *cdata; + unsigned long long ni; + unsigned int rate; + u8 regval; + + cdata = &max98095->dai[1]; + + rate = params_rate(params); + + switch (params_width(params)) { + case 16: + snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT, + M98095_DAI_WS, 0); + break; + case 24: + snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT, + M98095_DAI_WS, M98095_DAI_WS); + break; + default: + return -EINVAL; + } + + if (rate_value(rate, ®val)) + return -EINVAL; + + snd_soc_component_update_bits(component, M98095_031_DAI2_CLKMODE, + M98095_CLKMODE_MASK, regval); + cdata->rate = rate; + + /* Configure NI when operating as master */ + if (snd_soc_component_read(component, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) { + if (max98095->sysclk == 0) { + dev_err(component->dev, "Invalid system clock frequency\n"); + return -EINVAL; + } + ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)rate; + do_div(ni, (unsigned long long int)max98095->sysclk); + snd_soc_component_write(component, M98095_032_DAI2_CLKCFG_HI, + (ni >> 8) & 0x7F); + snd_soc_component_write(component, M98095_033_DAI2_CLKCFG_LO, + ni & 0xFF); + } + + /* Update sample rate mode */ + if (rate < 50000) + snd_soc_component_update_bits(component, M98095_038_DAI2_FILTERS, + M98095_DAI_DHF, 0); + else + snd_soc_component_update_bits(component, M98095_038_DAI2_FILTERS, + M98095_DAI_DHF, M98095_DAI_DHF); + + return 0; +} + +static int max98095_dai3_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_cdata *cdata; + unsigned long long ni; + unsigned int rate; + u8 regval; + + cdata = &max98095->dai[2]; + + rate = params_rate(params); + + switch (params_width(params)) { + case 16: + snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT, + M98095_DAI_WS, 0); + break; + case 24: + snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT, + M98095_DAI_WS, M98095_DAI_WS); + break; + default: + return -EINVAL; + } + + if (rate_value(rate, ®val)) + return -EINVAL; + + snd_soc_component_update_bits(component, M98095_03B_DAI3_CLKMODE, + M98095_CLKMODE_MASK, regval); + cdata->rate = rate; + + /* Configure NI when operating as master */ + if (snd_soc_component_read(component, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) { + if (max98095->sysclk == 0) { + dev_err(component->dev, "Invalid system clock frequency\n"); + return -EINVAL; + } + ni = 65536ULL * (rate < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)rate; + do_div(ni, (unsigned long long int)max98095->sysclk); + snd_soc_component_write(component, M98095_03C_DAI3_CLKCFG_HI, + (ni >> 8) & 0x7F); + snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO, + ni & 0xFF); + } + + /* Update sample rate mode */ + if (rate < 50000) + snd_soc_component_update_bits(component, M98095_042_DAI3_FILTERS, + M98095_DAI_DHF, 0); + else + snd_soc_component_update_bits(component, M98095_042_DAI3_FILTERS, + M98095_DAI_DHF, M98095_DAI_DHF); + + return 0; +} + +static int max98095_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + + /* Requested clock frequency is already setup */ + if (freq == max98095->sysclk) + return 0; + + if (!IS_ERR(max98095->mclk)) { + freq = clk_round_rate(max98095->mclk, freq); + clk_set_rate(max98095->mclk, freq); + } + + /* Setup clocks for slave mode, and using the PLL + * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) + * 0x02 (when master clk is 20MHz to 40MHz).. + * 0x03 (when master clk is 40MHz to 60MHz).. + */ + if ((freq >= 10000000) && (freq < 20000000)) { + snd_soc_component_write(component, M98095_026_SYS_CLK, 0x10); + } else if ((freq >= 20000000) && (freq < 40000000)) { + snd_soc_component_write(component, M98095_026_SYS_CLK, 0x20); + } else if ((freq >= 40000000) && (freq < 60000000)) { + snd_soc_component_write(component, M98095_026_SYS_CLK, 0x30); + } else { + dev_err(component->dev, "Invalid master clock frequency\n"); + return -EINVAL; + } + + dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq); + + max98095->sysclk = freq; + return 0; +} + +static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_cdata *cdata; + u8 regval = 0; + + cdata = &max98095->dai[0]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Consumer mode PLL */ + snd_soc_component_write(component, M98095_028_DAI1_CLKCFG_HI, + 0x80); + snd_soc_component_write(component, M98095_029_DAI1_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ + regval |= M98095_DAI_MAS; + break; + default: + dev_err(component->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98095_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98095_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98095_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98095_DAI_BCI|M98095_DAI_WCI; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT, + M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI | + M98095_DAI_WCI, regval); + + snd_soc_component_write(component, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64); + } + + return 0; +} + +static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_cdata *cdata; + u8 regval = 0; + + cdata = &max98095->dai[1]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Consumer mode PLL */ + snd_soc_component_write(component, M98095_032_DAI2_CLKCFG_HI, + 0x80); + snd_soc_component_write(component, M98095_033_DAI2_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ + regval |= M98095_DAI_MAS; + break; + default: + dev_err(component->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98095_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98095_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98095_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98095_DAI_BCI|M98095_DAI_WCI; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT, + M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI | + M98095_DAI_WCI, regval); + + snd_soc_component_write(component, M98095_035_DAI2_CLOCK, + M98095_DAI_BSEL64); + } + + return 0; +} + +static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_cdata *cdata; + u8 regval = 0; + + cdata = &max98095->dai[2]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + /* Consumer mode PLL */ + snd_soc_component_write(component, M98095_03C_DAI3_CLKCFG_HI, + 0x80); + snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO, + 0x00); + break; + case SND_SOC_DAIFMT_CBP_CFP: + /* Set to provider mode */ + regval |= M98095_DAI_MAS; + break; + default: + dev_err(component->dev, "Clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98095_DAI_DLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98095_DAI_WCI; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98095_DAI_BCI; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98095_DAI_BCI|M98095_DAI_WCI; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT, + M98095_DAI_MAS | M98095_DAI_DLY | M98095_DAI_BCI | + M98095_DAI_WCI, regval); + + snd_soc_component_write(component, M98095_03F_DAI3_CLOCK, + M98095_DAI_BSEL64); + } + + return 0; +} + +static int max98095_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (IS_ERR(max98095->mclk)) + break; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(max98095->mclk); + } else { + ret = clk_prepare_enable(max98095->mclk); + if (ret) + return ret; + } + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regcache_sync(max98095->regmap); + + if (ret != 0) { + dev_err(component->dev, "Failed to sync cache: %d\n", ret); + return ret; + } + } + + snd_soc_component_update_bits(component, M98095_090_PWR_EN_IN, + M98095_MBEN, M98095_MBEN); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, M98095_090_PWR_EN_IN, + M98095_MBEN, 0); + regcache_mark_dirty(max98095->regmap); + break; + } + return 0; +} + +#define MAX98095_RATES SNDRV_PCM_RATE_8000_96000 +#define MAX98095_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops max98095_dai1_ops = { + .set_sysclk = max98095_dai_set_sysclk, + .set_fmt = max98095_dai1_set_fmt, + .hw_params = max98095_dai1_hw_params, +}; + +static const struct snd_soc_dai_ops max98095_dai2_ops = { + .set_sysclk = max98095_dai_set_sysclk, + .set_fmt = max98095_dai2_set_fmt, + .hw_params = max98095_dai2_hw_params, +}; + +static const struct snd_soc_dai_ops max98095_dai3_ops = { + .set_sysclk = max98095_dai_set_sysclk, + .set_fmt = max98095_dai3_set_fmt, + .hw_params = max98095_dai3_hw_params, +}; + +static struct snd_soc_dai_driver max98095_dai[] = { +{ + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98095_RATES, + .formats = MAX98095_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98095_RATES, + .formats = MAX98095_FORMATS, + }, + .ops = &max98095_dai1_ops, +}, +{ + .name = "Aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 1, + .rates = MAX98095_RATES, + .formats = MAX98095_FORMATS, + }, + .ops = &max98095_dai2_ops, +}, +{ + .name = "Voice", + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = MAX98095_RATES, + .formats = MAX98095_FORMATS, + }, + .ops = &max98095_dai3_ops, +} + +}; + +static int max98095_get_eq_channel(const char *name) +{ + if (strcmp(name, "EQ1 Mode") == 0) + return 0; + if (strcmp(name, "EQ2 Mode") == 0) + return 1; + return -EINVAL; +} + +static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_pdata *pdata = max98095->pdata; + int channel = max98095_get_eq_channel(kcontrol->id.name); + struct max98095_cdata *cdata; + unsigned int sel = ucontrol->value.enumerated.item[0]; + struct max98095_eq_cfg *coef_set; + int fs, best, best_val, i; + int regmask, regsave; + + if (WARN_ON(channel > 1)) + return -EINVAL; + + if (!pdata || !max98095->eq_textcnt) + return 0; + + if (sel >= pdata->eq_cfgcnt) + return -EINVAL; + + cdata = &max98095->dai[channel]; + cdata->eq_sel = sel; + fs = cdata->rate; + + /* Find the selected configuration with nearest sample rate */ + best = 0; + best_val = INT_MAX; + for (i = 0; i < pdata->eq_cfgcnt; i++) { + if (strcmp(pdata->eq_cfg[i].name, max98095->eq_texts[sel]) == 0 && + abs(pdata->eq_cfg[i].rate - fs) < best_val) { + best = i; + best_val = abs(pdata->eq_cfg[i].rate - fs); + } + } + + dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n", + pdata->eq_cfg[best].name, + pdata->eq_cfg[best].rate, fs); + + coef_set = &pdata->eq_cfg[best]; + + regmask = (channel == 0) ? M98095_EQ1EN : M98095_EQ2EN; + + /* Disable filter while configuring, and save current on/off state */ + regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); + + mutex_lock(&max98095->lock); + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); + m98095_eq_band(component, channel, 0, coef_set->band1); + m98095_eq_band(component, channel, 1, coef_set->band2); + m98095_eq_band(component, channel, 2, coef_set->band3); + m98095_eq_band(component, channel, 3, coef_set->band4); + m98095_eq_band(component, channel, 4, coef_set->band5); + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); + mutex_unlock(&max98095->lock); + + /* Restore the original on/off state */ + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); + return 0; +} + +static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + int channel = max98095_get_eq_channel(kcontrol->id.name); + struct max98095_cdata *cdata; + + cdata = &max98095->dai[channel]; + ucontrol->value.enumerated.item[0] = cdata->eq_sel; + + return 0; +} + +static void max98095_handle_eq_pdata(struct snd_soc_component *component) +{ + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_pdata *pdata = max98095->pdata; + struct max98095_eq_cfg *cfg; + unsigned int cfgcnt; + int i, j; + const char **t; + int ret; + + struct snd_kcontrol_new controls[] = { + SOC_ENUM_EXT("EQ1 Mode", + max98095->eq_enum, + max98095_get_eq_enum, + max98095_put_eq_enum), + SOC_ENUM_EXT("EQ2 Mode", + max98095->eq_enum, + max98095_get_eq_enum, + max98095_put_eq_enum), + }; + + cfg = pdata->eq_cfg; + cfgcnt = pdata->eq_cfgcnt; + + /* Setup an array of texts for the equalizer enum. + * This is based on Mark Brown's equalizer driver code. + */ + max98095->eq_textcnt = 0; + max98095->eq_texts = NULL; + for (i = 0; i < cfgcnt; i++) { + for (j = 0; j < max98095->eq_textcnt; j++) { + if (strcmp(cfg[i].name, max98095->eq_texts[j]) == 0) + break; + } + + if (j != max98095->eq_textcnt) + continue; + + /* Expand the array */ + t = krealloc(max98095->eq_texts, + sizeof(char *) * (max98095->eq_textcnt + 1), + GFP_KERNEL); + if (t == NULL) + continue; + + /* Store the new entry */ + t[max98095->eq_textcnt] = cfg[i].name; + max98095->eq_textcnt++; + max98095->eq_texts = t; + } + + /* Now point the soc_enum to .texts array items */ + max98095->eq_enum.texts = max98095->eq_texts; + max98095->eq_enum.items = max98095->eq_textcnt; + + ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls)); + if (ret != 0) + dev_err(component->dev, "Failed to add EQ control: %d\n", ret); +} + +static const char *bq_mode_name[] = {"Biquad1 Mode", "Biquad2 Mode"}; + +static int max98095_get_bq_channel(struct snd_soc_component *component, + const char *name) +{ + int ret; + + ret = match_string(bq_mode_name, ARRAY_SIZE(bq_mode_name), name); + if (ret < 0) + dev_err(component->dev, "Bad biquad channel name '%s'\n", name); + return ret; +} + +static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_pdata *pdata = max98095->pdata; + int channel = max98095_get_bq_channel(component, kcontrol->id.name); + struct max98095_cdata *cdata; + unsigned int sel = ucontrol->value.enumerated.item[0]; + struct max98095_biquad_cfg *coef_set; + int fs, best, best_val, i; + int regmask, regsave; + + if (channel < 0) + return channel; + + if (!pdata || !max98095->bq_textcnt) + return 0; + + if (sel >= pdata->bq_cfgcnt) + return -EINVAL; + + cdata = &max98095->dai[channel]; + cdata->bq_sel = sel; + fs = cdata->rate; + + /* Find the selected configuration with nearest sample rate */ + best = 0; + best_val = INT_MAX; + for (i = 0; i < pdata->bq_cfgcnt; i++) { + if (strcmp(pdata->bq_cfg[i].name, max98095->bq_texts[sel]) == 0 && + abs(pdata->bq_cfg[i].rate - fs) < best_val) { + best = i; + best_val = abs(pdata->bq_cfg[i].rate - fs); + } + } + + dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n", + pdata->bq_cfg[best].name, + pdata->bq_cfg[best].rate, fs); + + coef_set = &pdata->bq_cfg[best]; + + regmask = (channel == 0) ? M98095_BQ1EN : M98095_BQ2EN; + + /* Disable filter while configuring, and save current on/off state */ + regsave = snd_soc_component_read(component, M98095_088_CFG_LEVEL); + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, 0); + + mutex_lock(&max98095->lock); + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG); + m98095_biquad_band(component, channel, 0, coef_set->band1); + m98095_biquad_band(component, channel, 1, coef_set->band2); + snd_soc_component_update_bits(component, M98095_00F_HOST_CFG, M98095_SEG, 0); + mutex_unlock(&max98095->lock); + + /* Restore the original on/off state */ + snd_soc_component_update_bits(component, M98095_088_CFG_LEVEL, regmask, regsave); + return 0; +} + +static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + int channel = max98095_get_bq_channel(component, kcontrol->id.name); + struct max98095_cdata *cdata; + + if (channel < 0) + return channel; + + cdata = &max98095->dai[channel]; + ucontrol->value.enumerated.item[0] = cdata->bq_sel; + + return 0; +} + +static void max98095_handle_bq_pdata(struct snd_soc_component *component) +{ + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_pdata *pdata = max98095->pdata; + struct max98095_biquad_cfg *cfg; + unsigned int cfgcnt; + int i, j; + const char **t; + int ret; + + struct snd_kcontrol_new controls[] = { + SOC_ENUM_EXT((char *)bq_mode_name[0], + max98095->bq_enum, + max98095_get_bq_enum, + max98095_put_bq_enum), + SOC_ENUM_EXT((char *)bq_mode_name[1], + max98095->bq_enum, + max98095_get_bq_enum, + max98095_put_bq_enum), + }; + BUILD_BUG_ON(ARRAY_SIZE(controls) != ARRAY_SIZE(bq_mode_name)); + + cfg = pdata->bq_cfg; + cfgcnt = pdata->bq_cfgcnt; + + /* Setup an array of texts for the biquad enum. + * This is based on Mark Brown's equalizer driver code. + */ + max98095->bq_textcnt = 0; + max98095->bq_texts = NULL; + for (i = 0; i < cfgcnt; i++) { + for (j = 0; j < max98095->bq_textcnt; j++) { + if (strcmp(cfg[i].name, max98095->bq_texts[j]) == 0) + break; + } + + if (j != max98095->bq_textcnt) + continue; + + /* Expand the array */ + t = krealloc(max98095->bq_texts, + sizeof(char *) * (max98095->bq_textcnt + 1), + GFP_KERNEL); + if (t == NULL) + continue; + + /* Store the new entry */ + t[max98095->bq_textcnt] = cfg[i].name; + max98095->bq_textcnt++; + max98095->bq_texts = t; + } + + /* Now point the soc_enum to .texts array items */ + max98095->bq_enum.texts = max98095->bq_texts; + max98095->bq_enum.items = max98095->bq_textcnt; + + ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls)); + if (ret != 0) + dev_err(component->dev, "Failed to add Biquad control: %d\n", ret); +} + +static void max98095_handle_pdata(struct snd_soc_component *component) +{ + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_pdata *pdata = max98095->pdata; + u8 regval = 0; + + if (!pdata) { + dev_dbg(component->dev, "No platform data\n"); + return; + } + + /* Configure mic for analog/digital mic mode */ + if (pdata->digmic_left_mode) + regval |= M98095_DIGMIC_L; + + if (pdata->digmic_right_mode) + regval |= M98095_DIGMIC_R; + + snd_soc_component_write(component, M98095_087_CFG_MIC, regval); + + /* Configure equalizers */ + if (pdata->eq_cfgcnt) + max98095_handle_eq_pdata(component); + + /* Configure bi-quad filters */ + if (pdata->bq_cfgcnt) + max98095_handle_bq_pdata(component); +} + +static irqreturn_t max98095_report_jack(int irq, void *data) +{ + struct snd_soc_component *component = data; + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + unsigned int value; + int hp_report = 0; + int mic_report = 0; + + /* Read the Jack Status Register */ + value = snd_soc_component_read(component, M98095_007_JACK_AUTO_STS); + + /* If ddone is not set, then detection isn't finished yet */ + if ((value & M98095_DDONE) == 0) + return IRQ_NONE; + + /* if hp, check its bit, and if set, clear it */ + if ((value & M98095_HP_IN || value & M98095_LO_IN) && + max98095->headphone_jack) + hp_report |= SND_JACK_HEADPHONE; + + /* if mic, check its bit, and if set, clear it */ + if ((value & M98095_MIC_IN) && max98095->mic_jack) + mic_report |= SND_JACK_MICROPHONE; + + if (max98095->headphone_jack == max98095->mic_jack) { + snd_soc_jack_report(max98095->headphone_jack, + hp_report | mic_report, + SND_JACK_HEADSET); + } else { + if (max98095->headphone_jack) + snd_soc_jack_report(max98095->headphone_jack, + hp_report, SND_JACK_HEADPHONE); + if (max98095->mic_jack) + snd_soc_jack_report(max98095->mic_jack, + mic_report, SND_JACK_MICROPHONE); + } + + return IRQ_HANDLED; +} + +static int max98095_jack_detect_enable(struct snd_soc_component *component) +{ + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + int ret = 0; + int detect_enable = M98095_JDEN; + unsigned int slew = M98095_DEFAULT_SLEW_DELAY; + + if (max98095->pdata->jack_detect_pin5en) + detect_enable |= M98095_PIN5EN; + + if (max98095->pdata->jack_detect_delay) + slew = max98095->pdata->jack_detect_delay; + + ret = snd_soc_component_write(component, M98095_08E_JACK_DC_SLEW, slew); + if (ret < 0) { + dev_err(component->dev, "Failed to cfg auto detect %d\n", ret); + return ret; + } + + /* configure auto detection to be enabled */ + ret = snd_soc_component_write(component, M98095_089_JACK_DET_AUTO, detect_enable); + if (ret < 0) { + dev_err(component->dev, "Failed to cfg auto detect %d\n", ret); + return ret; + } + + return ret; +} + +static int max98095_jack_detect_disable(struct snd_soc_component *component) +{ + int ret = 0; + + /* configure auto detection to be disabled */ + ret = snd_soc_component_write(component, M98095_089_JACK_DET_AUTO, 0x0); + if (ret < 0) { + dev_err(component->dev, "Failed to cfg auto detect %d\n", ret); + return ret; + } + + return ret; +} + +int max98095_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack) +{ + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct i2c_client *client = to_i2c_client(component->dev); + int ret = 0; + + max98095->headphone_jack = hp_jack; + max98095->mic_jack = mic_jack; + + /* only progress if we have at least 1 jack pointer */ + if (!hp_jack && !mic_jack) + return -EINVAL; + + max98095_jack_detect_enable(component); + + /* enable interrupts for headphone jack detection */ + ret = snd_soc_component_update_bits(component, M98095_013_JACK_INT_EN, + M98095_IDDONE, M98095_IDDONE); + if (ret < 0) { + dev_err(component->dev, "Failed to cfg jack irqs %d\n", ret); + return ret; + } + + max98095_report_jack(client->irq, component); + return 0; +} +EXPORT_SYMBOL_GPL(max98095_jack_detect); + +#ifdef CONFIG_PM +static int max98095_suspend(struct snd_soc_component *component) +{ + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + + if (max98095->headphone_jack || max98095->mic_jack) + max98095_jack_detect_disable(component); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + return 0; +} + +static int max98095_resume(struct snd_soc_component *component) +{ + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct i2c_client *client = to_i2c_client(component->dev); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + if (max98095->headphone_jack || max98095->mic_jack) { + max98095_jack_detect_enable(component); + max98095_report_jack(client->irq, component); + } + + return 0; +} +#else +#define max98095_suspend NULL +#define max98095_resume NULL +#endif + +static int max98095_reset(struct snd_soc_component *component) +{ + int i, ret; + + /* Gracefully reset the DSP core and the codec hardware + * in a proper sequence */ + ret = snd_soc_component_write(component, M98095_00F_HOST_CFG, 0); + if (ret < 0) { + dev_err(component->dev, "Failed to reset DSP: %d\n", ret); + return ret; + } + + ret = snd_soc_component_write(component, M98095_097_PWR_SYS, 0); + if (ret < 0) { + dev_err(component->dev, "Failed to reset component: %d\n", ret); + return ret; + } + + /* Reset to hardware default for registers, as there is not + * a soft reset hardware control register */ + for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) { + ret = snd_soc_component_write(component, i, snd_soc_component_read(component, i)); + if (ret < 0) { + dev_err(component->dev, "Failed to reset: %d\n", ret); + return ret; + } + } + + return ret; +} + +static int max98095_probe(struct snd_soc_component *component) +{ + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct max98095_cdata *cdata; + struct i2c_client *client; + int ret = 0; + + max98095->mclk = devm_clk_get(component->dev, "mclk"); + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + /* reset the codec, the DSP core, and disable all interrupts */ + max98095_reset(component); + + client = to_i2c_client(component->dev); + + /* initialize private data */ + + max98095->sysclk = (unsigned)-1; + max98095->eq_textcnt = 0; + max98095->bq_textcnt = 0; + + cdata = &max98095->dai[0]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + cdata->eq_sel = 0; + cdata->bq_sel = 0; + + cdata = &max98095->dai[1]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + cdata->eq_sel = 0; + cdata->bq_sel = 0; + + cdata = &max98095->dai[2]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + cdata->eq_sel = 0; + cdata->bq_sel = 0; + + max98095->lin_state = 0; + max98095->mic1pre = 0; + max98095->mic2pre = 0; + + if (client->irq) { + /* register an audio interrupt */ + ret = request_threaded_irq(client->irq, NULL, + max98095_report_jack, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "max98095", component); + if (ret) { + dev_err(component->dev, "Failed to request IRQ: %d\n", ret); + goto err_access; + } + } + + ret = snd_soc_component_read(component, M98095_0FF_REV_ID); + if (ret < 0) { + dev_err(component->dev, "Failure reading hardware revision: %d\n", + ret); + goto err_irq; + } + dev_info(component->dev, "Hardware revision: %c\n", ret - 0x40 + 'A'); + + snd_soc_component_write(component, M98095_097_PWR_SYS, M98095_PWRSV); + + snd_soc_component_write(component, M98095_048_MIX_DAC_LR, + M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR); + + snd_soc_component_write(component, M98095_049_MIX_DAC_M, + M98095_DAI2M_TO_DACM|M98095_DAI3M_TO_DACM); + + snd_soc_component_write(component, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM); + snd_soc_component_write(component, M98095_045_CFG_DSP, M98095_DSPNORMAL); + snd_soc_component_write(component, M98095_04E_CFG_HP, M98095_HPNORMAL); + + snd_soc_component_write(component, M98095_02C_DAI1_IOCFG, + M98095_S1NORMAL|M98095_SDATA); + + snd_soc_component_write(component, M98095_036_DAI2_IOCFG, + M98095_S2NORMAL|M98095_SDATA); + + snd_soc_component_write(component, M98095_040_DAI3_IOCFG, + M98095_S3NORMAL|M98095_SDATA); + + max98095_handle_pdata(component); + + /* take the codec out of the shut down */ + snd_soc_component_update_bits(component, M98095_097_PWR_SYS, M98095_SHDNRUN, + M98095_SHDNRUN); + + return 0; + +err_irq: + if (client->irq) + free_irq(client->irq, component); +err_access: + return ret; +} + +static void max98095_remove(struct snd_soc_component *component) +{ + struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component); + struct i2c_client *client = to_i2c_client(component->dev); + + if (max98095->headphone_jack || max98095->mic_jack) + max98095_jack_detect_disable(component); + + if (client->irq) + free_irq(client->irq, component); +} + +static const struct snd_soc_component_driver soc_component_dev_max98095 = { + .probe = max98095_probe, + .remove = max98095_remove, + .suspend = max98095_suspend, + .resume = max98095_resume, + .set_bias_level = max98095_set_bias_level, + .controls = max98095_snd_controls, + .num_controls = ARRAY_SIZE(max98095_snd_controls), + .dapm_widgets = max98095_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets), + .dapm_routes = max98095_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98095_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct i2c_device_id max98095_i2c_id[] = { + { "max98095", MAX98095 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98095_i2c_id); + +static int max98095_i2c_probe(struct i2c_client *i2c) +{ + struct max98095_priv *max98095; + int ret; + const struct i2c_device_id *id; + + max98095 = devm_kzalloc(&i2c->dev, sizeof(struct max98095_priv), + GFP_KERNEL); + if (max98095 == NULL) + return -ENOMEM; + + mutex_init(&max98095->lock); + + max98095->regmap = devm_regmap_init_i2c(i2c, &max98095_regmap); + if (IS_ERR(max98095->regmap)) { + ret = PTR_ERR(max98095->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + id = i2c_match_id(max98095_i2c_id, i2c); + max98095->devtype = id->driver_data; + i2c_set_clientdata(i2c, max98095); + max98095->pdata = i2c->dev.platform_data; + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_max98095, + max98095_dai, ARRAY_SIZE(max98095_dai)); + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id max98095_of_match[] = { + { .compatible = "maxim,max98095", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98095_of_match); +#endif + +static struct i2c_driver max98095_i2c_driver = { + .driver = { + .name = "max98095", + .of_match_table = of_match_ptr(max98095_of_match), + }, + .probe = max98095_i2c_probe, + .id_table = max98095_i2c_id, +}; + +module_i2c_driver(max98095_i2c_driver); + +MODULE_DESCRIPTION("ALSA SoC MAX98095 driver"); +MODULE_AUTHOR("Peter Hsiang"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h new file mode 100644 index 0000000000..2af7e77021 --- /dev/null +++ b/sound/soc/codecs/max98095.h @@ -0,0 +1,318 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * max98095.h -- MAX98095 ALSA SoC Audio driver + * + * Copyright 2011 Maxim Integrated Products + */ + +#ifndef _MAX98095_H +#define _MAX98095_H + +/* + * MAX98095 Registers Definition + */ + +#define M98095_000_HOST_DATA 0x00 +#define M98095_001_HOST_INT_STS 0x01 +#define M98095_002_HOST_RSP_STS 0x02 +#define M98095_003_HOST_CMD_STS 0x03 +#define M98095_004_CODEC_STS 0x04 +#define M98095_005_DAI1_ALC_STS 0x05 +#define M98095_006_DAI2_ALC_STS 0x06 +#define M98095_007_JACK_AUTO_STS 0x07 +#define M98095_008_JACK_MANUAL_STS 0x08 +#define M98095_009_JACK_VBAT_STS 0x09 +#define M98095_00A_ACC_ADC_STS 0x0A +#define M98095_00B_MIC_NG_AGC_STS 0x0B +#define M98095_00C_SPK_L_VOLT_STS 0x0C +#define M98095_00D_SPK_R_VOLT_STS 0x0D +#define M98095_00E_TEMP_SENSOR_STS 0x0E +#define M98095_00F_HOST_CFG 0x0F +#define M98095_010_HOST_INT_CFG 0x10 +#define M98095_011_HOST_INT_EN 0x11 +#define M98095_012_CODEC_INT_EN 0x12 +#define M98095_013_JACK_INT_EN 0x13 +#define M98095_014_JACK_INT_EN 0x14 +#define M98095_015_DEC 0x15 +#define M98095_016_RESERVED 0x16 +#define M98095_017_RESERVED 0x17 +#define M98095_018_KEYCODE3 0x18 +#define M98095_019_KEYCODE2 0x19 +#define M98095_01A_KEYCODE1 0x1A +#define M98095_01B_KEYCODE0 0x1B +#define M98095_01C_OEMCODE1 0x1C +#define M98095_01D_OEMCODE0 0x1D +#define M98095_01E_XCFG1 0x1E +#define M98095_01F_XCFG2 0x1F +#define M98095_020_XCFG3 0x20 +#define M98095_021_XCFG4 0x21 +#define M98095_022_XCFG5 0x22 +#define M98095_023_XCFG6 0x23 +#define M98095_024_XGPIO 0x24 +#define M98095_025_XCLKCFG 0x25 +#define M98095_026_SYS_CLK 0x26 +#define M98095_027_DAI1_CLKMODE 0x27 +#define M98095_028_DAI1_CLKCFG_HI 0x28 +#define M98095_029_DAI1_CLKCFG_LO 0x29 +#define M98095_02A_DAI1_FORMAT 0x2A +#define M98095_02B_DAI1_CLOCK 0x2B +#define M98095_02C_DAI1_IOCFG 0x2C +#define M98095_02D_DAI1_TDM 0x2D +#define M98095_02E_DAI1_FILTERS 0x2E +#define M98095_02F_DAI1_LVL1 0x2F +#define M98095_030_DAI1_LVL2 0x30 +#define M98095_031_DAI2_CLKMODE 0x31 +#define M98095_032_DAI2_CLKCFG_HI 0x32 +#define M98095_033_DAI2_CLKCFG_LO 0x33 +#define M98095_034_DAI2_FORMAT 0x34 +#define M98095_035_DAI2_CLOCK 0x35 +#define M98095_036_DAI2_IOCFG 0x36 +#define M98095_037_DAI2_TDM 0x37 +#define M98095_038_DAI2_FILTERS 0x38 +#define M98095_039_DAI2_LVL1 0x39 +#define M98095_03A_DAI2_LVL2 0x3A +#define M98095_03B_DAI3_CLKMODE 0x3B +#define M98095_03C_DAI3_CLKCFG_HI 0x3C +#define M98095_03D_DAI3_CLKCFG_LO 0x3D +#define M98095_03E_DAI3_FORMAT 0x3E +#define M98095_03F_DAI3_CLOCK 0x3F +#define M98095_040_DAI3_IOCFG 0x40 +#define M98095_041_DAI3_TDM 0x41 +#define M98095_042_DAI3_FILTERS 0x42 +#define M98095_043_DAI3_LVL1 0x43 +#define M98095_044_DAI3_LVL2 0x44 +#define M98095_045_CFG_DSP 0x45 +#define M98095_046_DAC_CTRL1 0x46 +#define M98095_047_DAC_CTRL2 0x47 +#define M98095_048_MIX_DAC_LR 0x48 +#define M98095_049_MIX_DAC_M 0x49 +#define M98095_04A_MIX_ADC_LEFT 0x4A +#define M98095_04B_MIX_ADC_RIGHT 0x4B +#define M98095_04C_MIX_HP_LEFT 0x4C +#define M98095_04D_MIX_HP_RIGHT 0x4D +#define M98095_04E_CFG_HP 0x4E +#define M98095_04F_MIX_RCV 0x4F +#define M98095_050_MIX_SPK_LEFT 0x50 +#define M98095_051_MIX_SPK_RIGHT 0x51 +#define M98095_052_MIX_SPK_CFG 0x52 +#define M98095_053_MIX_LINEOUT1 0x53 +#define M98095_054_MIX_LINEOUT2 0x54 +#define M98095_055_MIX_LINEOUT_CFG 0x55 +#define M98095_056_LVL_SIDETONE_DAI12 0x56 +#define M98095_057_LVL_SIDETONE_DAI3 0x57 +#define M98095_058_LVL_DAI1_PLAY 0x58 +#define M98095_059_LVL_DAI1_EQ 0x59 +#define M98095_05A_LVL_DAI2_PLAY 0x5A +#define M98095_05B_LVL_DAI2_EQ 0x5B +#define M98095_05C_LVL_DAI3_PLAY 0x5C +#define M98095_05D_LVL_ADC_L 0x5D +#define M98095_05E_LVL_ADC_R 0x5E +#define M98095_05F_LVL_MIC1 0x5F +#define M98095_060_LVL_MIC2 0x60 +#define M98095_061_LVL_LINEIN 0x61 +#define M98095_062_LVL_LINEOUT1 0x62 +#define M98095_063_LVL_LINEOUT2 0x63 +#define M98095_064_LVL_HP_L 0x64 +#define M98095_065_LVL_HP_R 0x65 +#define M98095_066_LVL_RCV 0x66 +#define M98095_067_LVL_SPK_L 0x67 +#define M98095_068_LVL_SPK_R 0x68 +#define M98095_069_MICAGC_CFG 0x69 +#define M98095_06A_MICAGC_THRESH 0x6A +#define M98095_06B_SPK_NOISEGATE 0x6B +#define M98095_06C_DAI1_ALC1_TIME 0x6C +#define M98095_06D_DAI1_ALC1_COMP 0x6D +#define M98095_06E_DAI1_ALC1_EXPN 0x6E +#define M98095_06F_DAI1_ALC1_GAIN 0x6F +#define M98095_070_DAI1_ALC2_TIME 0x70 +#define M98095_071_DAI1_ALC2_COMP 0x71 +#define M98095_072_DAI1_ALC2_EXPN 0x72 +#define M98095_073_DAI1_ALC2_GAIN 0x73 +#define M98095_074_DAI1_ALC3_TIME 0x74 +#define M98095_075_DAI1_ALC3_COMP 0x75 +#define M98095_076_DAI1_ALC3_EXPN 0x76 +#define M98095_077_DAI1_ALC3_GAIN 0x77 +#define M98095_078_DAI2_ALC1_TIME 0x78 +#define M98095_079_DAI2_ALC1_COMP 0x79 +#define M98095_07A_DAI2_ALC1_EXPN 0x7A +#define M98095_07B_DAI2_ALC1_GAIN 0x7B +#define M98095_07C_DAI2_ALC2_TIME 0x7C +#define M98095_07D_DAI2_ALC2_COMP 0x7D +#define M98095_07E_DAI2_ALC2_EXPN 0x7E +#define M98095_07F_DAI2_ALC2_GAIN 0x7F +#define M98095_080_DAI2_ALC3_TIME 0x80 +#define M98095_081_DAI2_ALC3_COMP 0x81 +#define M98095_082_DAI2_ALC3_EXPN 0x82 +#define M98095_083_DAI2_ALC3_GAIN 0x83 +#define M98095_084_HP_NOISE_GATE 0x84 +#define M98095_085_AUX_ADC 0x85 +#define M98095_086_CFG_LINE 0x86 +#define M98095_087_CFG_MIC 0x87 +#define M98095_088_CFG_LEVEL 0x88 +#define M98095_089_JACK_DET_AUTO 0x89 +#define M98095_08A_JACK_DET_MANUAL 0x8A +#define M98095_08B_JACK_KEYSCAN_DBC 0x8B +#define M98095_08C_JACK_KEYSCAN_DLY 0x8C +#define M98095_08D_JACK_KEY_THRESH 0x8D +#define M98095_08E_JACK_DC_SLEW 0x8E +#define M98095_08F_JACK_TEST_CFG 0x8F +#define M98095_090_PWR_EN_IN 0x90 +#define M98095_091_PWR_EN_OUT 0x91 +#define M98095_092_PWR_EN_OUT 0x92 +#define M98095_093_BIAS_CTRL 0x93 +#define M98095_094_PWR_DAC_21 0x94 +#define M98095_095_PWR_DAC_03 0x95 +#define M98095_096_PWR_DAC_CK 0x96 +#define M98095_097_PWR_SYS 0x97 + +#define M98095_0FF_REV_ID 0xFF + +#define M98095_REG_CNT (0xFF+1) +#define M98095_REG_MAX_CACHED 0X97 + +/* MAX98095 Registers Bit Fields */ + +/* M98095_007_JACK_AUTO_STS */ + #define M98095_MIC_IN (1<<3) + #define M98095_LO_IN (1<<5) + #define M98095_HP_IN (1<<6) + #define M98095_DDONE (1<<7) + +/* M98095_00F_HOST_CFG */ + #define M98095_SEG (1<<0) + #define M98095_XTEN (1<<1) + #define M98095_MDLLEN (1<<2) + +/* M98095_013_JACK_INT_EN */ + #define M98095_IMIC_IN (1<<3) + #define M98095_ILO_IN (1<<5) + #define M98095_IHP_IN (1<<6) + #define M98095_IDDONE (1<<7) + +/* M98095_027_DAI1_CLKMODE, M98095_031_DAI2_CLKMODE, M98095_03B_DAI3_CLKMODE */ + #define M98095_CLKMODE_MASK 0xFF + +/* M98095_02A_DAI1_FORMAT, M98095_034_DAI2_FORMAT, M98095_03E_DAI3_FORMAT */ + #define M98095_DAI_MAS (1<<7) + #define M98095_DAI_WCI (1<<6) + #define M98095_DAI_BCI (1<<5) + #define M98095_DAI_DLY (1<<4) + #define M98095_DAI_TDM (1<<2) + #define M98095_DAI_FSW (1<<1) + #define M98095_DAI_WS (1<<0) + +/* M98095_02B_DAI1_CLOCK, M98095_035_DAI2_CLOCK, M98095_03F_DAI3_CLOCK */ + #define M98095_DAI_BSEL64 (1<<0) + #define M98095_DAI_DOSR_DIV2 (0<<5) + #define M98095_DAI_DOSR_DIV4 (1<<5) + +/* M98095_02C_DAI1_IOCFG, M98095_036_DAI2_IOCFG, M98095_040_DAI3_IOCFG */ + #define M98095_S1NORMAL (1<<6) + #define M98095_S2NORMAL (2<<6) + #define M98095_S3NORMAL (3<<6) + #define M98095_SDATA (3<<0) + +/* M98095_02E_DAI1_FILTERS, M98095_038_DAI2_FILTERS, M98095_042_DAI3_FILTERS */ + #define M98095_DAI_DHF (1<<3) + +/* M98095_045_DSP_CFG */ + #define M98095_DSPNORMAL (5<<4) + +/* M98095_048_MIX_DAC_LR */ + #define M98095_DAI1L_TO_DACR (1<<7) + #define M98095_DAI1R_TO_DACR (1<<6) + #define M98095_DAI2M_TO_DACR (1<<5) + #define M98095_DAI1L_TO_DACL (1<<3) + #define M98095_DAI1R_TO_DACL (1<<2) + #define M98095_DAI2M_TO_DACL (1<<1) + #define M98095_DAI3M_TO_DACL (1<<0) + +/* M98095_049_MIX_DAC_M */ + #define M98095_DAI1L_TO_DACM (1<<3) + #define M98095_DAI1R_TO_DACM (1<<2) + #define M98095_DAI2M_TO_DACM (1<<1) + #define M98095_DAI3M_TO_DACM (1<<0) + +/* M98095_04E_MIX_HP_CFG */ + #define M98095_HPNORMAL (3<<4) + +/* M98095_05F_LVL_MIC1, M98095_060_LVL_MIC2 */ + #define M98095_MICPRE_MASK (3<<5) + #define M98095_MICPRE_SHIFT 5 + +/* M98095_064_LVL_HP_L, M98095_065_LVL_HP_R */ + #define M98095_HP_MUTE (1<<7) + +/* M98095_066_LVL_RCV */ + #define M98095_REC_MUTE (1<<7) + +/* M98095_067_LVL_SPK_L, M98095_068_LVL_SPK_R */ + #define M98095_SP_MUTE (1<<7) + +/* M98095_087_CFG_MIC */ + #define M98095_MICSEL_MASK (3<<0) + #define M98095_DIGMIC_L (1<<2) + #define M98095_DIGMIC_R (1<<3) + #define M98095_DIGMIC2L (1<<4) + #define M98095_DIGMIC2R (1<<5) + +/* M98095_088_CFG_LEVEL */ + #define M98095_VSEN (1<<6) + #define M98095_ZDEN (1<<5) + #define M98095_BQ2EN (1<<3) + #define M98095_BQ1EN (1<<2) + #define M98095_EQ2EN (1<<1) + #define M98095_EQ1EN (1<<0) + +/* M98095_089_JACK_DET_AUTO */ + #define M98095_PIN5EN (1<<2) + #define M98095_JDEN (1<<7) + +/* M98095_090_PWR_EN_IN */ + #define M98095_INEN (1<<7) + #define M98095_MB2EN (1<<3) + #define M98095_MB1EN (1<<2) + #define M98095_MBEN (3<<2) + #define M98095_ADREN (1<<1) + #define M98095_ADLEN (1<<0) + +/* M98095_091_PWR_EN_OUT */ + #define M98095_HPLEN (1<<7) + #define M98095_HPREN (1<<6) + #define M98095_SPLEN (1<<5) + #define M98095_SPREN (1<<4) + #define M98095_RECEN (1<<3) + #define M98095_DALEN (1<<1) + #define M98095_DAREN (1<<0) + +/* M98095_092_PWR_EN_OUT */ + #define M98095_SPK_FIXEDSPECTRUM (0<<4) + #define M98095_SPK_SPREADSPECTRUM (1<<4) + +/* M98095_097_PWR_SYS */ + #define M98095_SHDNRUN (1<<7) + #define M98095_PERFMODE (1<<3) + #define M98095_HPPLYBACK (1<<2) + #define M98095_PWRSV8K (1<<1) + #define M98095_PWRSV (1<<0) + +#define M98095_COEFS_PER_BAND 5 + +#define M98095_BYTE1(w) ((w >> 8) & 0xff) +#define M98095_BYTE0(w) (w & 0xff) + +/* Equalizer filter coefficients */ +#define M98095_110_DAI1_EQ_BASE 0x10 +#define M98095_142_DAI2_EQ_BASE 0x42 + +/* Biquad filter coefficients */ +#define M98095_174_DAI1_BQ_BASE 0x74 +#define M98095_17E_DAI2_BQ_BASE 0x7E + +/* Default Delay used in Slew Rate Calculation for Jack detection */ +#define M98095_DEFAULT_SLEW_DELAY 0x18 + +extern int max98095_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack); + +#endif diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c new file mode 100644 index 0000000000..2a2b286f17 --- /dev/null +++ b/sound/soc/codecs/max98357a.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * + * max98357a.c -- MAX98357A ALSA SoC Codec driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct max98357a_priv { + struct gpio_desc *sdmode; + unsigned int sdmode_delay; + int sdmode_switch; +}; + +static int max98357a_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98357a_priv *max98357a = + snd_soc_component_get_drvdata(component); + + if (!max98357a->sdmode) + return 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + mdelay(max98357a->sdmode_delay); + if (max98357a->sdmode_switch) { + gpiod_set_value(max98357a->sdmode, 1); + dev_dbg(component->dev, "set sdmode to 1"); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + gpiod_set_value(max98357a->sdmode, 0); + dev_dbg(component->dev, "set sdmode to 0"); + break; + } + + return 0; +} + +static int max98357a_sdmode_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct max98357a_priv *max98357a = + snd_soc_component_get_drvdata(component); + + if (event & SND_SOC_DAPM_POST_PMU) + max98357a->sdmode_switch = 1; + else if (event & SND_SOC_DAPM_POST_PMD) + max98357a->sdmode_switch = 0; + + return 0; +} + +static const struct snd_soc_dapm_widget max98357a_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("Speaker"), + SND_SOC_DAPM_OUT_DRV_E("SD_MODE", SND_SOC_NOPM, 0, 0, NULL, 0, + max98357a_sdmode_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route max98357a_dapm_routes[] = { + {"SD_MODE", NULL, "HiFi Playback"}, + {"Speaker", NULL, "SD_MODE"}, +}; + +static const struct snd_soc_component_driver max98357a_component_driver = { + .dapm_widgets = max98357a_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98357a_dapm_widgets), + .dapm_routes = max98357a_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max98357a_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_dai_ops max98357a_dai_ops = { + .trigger = max98357a_daiops_trigger, +}; + +static struct snd_soc_dai_driver max98357a_dai_driver = { + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &max98357a_dai_ops, +}; + +static int max98357a_platform_probe(struct platform_device *pdev) +{ + struct max98357a_priv *max98357a; + int ret; + + max98357a = devm_kzalloc(&pdev->dev, sizeof(*max98357a), GFP_KERNEL); + if (!max98357a) + return -ENOMEM; + + max98357a->sdmode = devm_gpiod_get_optional(&pdev->dev, + "sdmode", GPIOD_OUT_LOW); + if (IS_ERR(max98357a->sdmode)) + return PTR_ERR(max98357a->sdmode); + + ret = device_property_read_u32(&pdev->dev, "sdmode-delay", + &max98357a->sdmode_delay); + if (ret) { + max98357a->sdmode_delay = 0; + dev_dbg(&pdev->dev, + "no optional property 'sdmode-delay' found, " + "default: no delay\n"); + } + + dev_set_drvdata(&pdev->dev, max98357a); + + return devm_snd_soc_register_component(&pdev->dev, + &max98357a_component_driver, + &max98357a_dai_driver, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id max98357a_device_id[] = { + { .compatible = "maxim,max98357a" }, + { .compatible = "maxim,max98360a" }, + {} +}; +MODULE_DEVICE_TABLE(of, max98357a_device_id); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98357a_acpi_match[] = { + { "MX98357A", 0 }, + { "MX98360A", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98357a_acpi_match); +#endif + +static struct platform_driver max98357a_platform_driver = { + .driver = { + .name = "max98357a", + .of_match_table = of_match_ptr(max98357a_device_id), + .acpi_match_table = ACPI_PTR(max98357a_acpi_match), + }, + .probe = max98357a_platform_probe, +}; +module_platform_driver(max98357a_platform_driver); + +MODULE_DESCRIPTION("Maxim MAX98357A Codec Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c new file mode 100644 index 0000000000..a2cca3436c --- /dev/null +++ b/sound/soc/codecs/max98363.c @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2022, Analog Devices Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "max98363.h" + +static struct reg_default max98363_reg[] = { + {MAX98363_R2021_ERR_MON_CTRL, 0x0}, + {MAX98363_R2022_SPK_MON_THRESH, 0x0}, + {MAX98363_R2023_SPK_MON_DURATION, 0x0}, + {MAX98363_R2030_TONE_GEN_CFG, 0x0}, + {MAX98363_R203F_TONE_GEN_EN, 0x0}, + {MAX98363_R2040_AMP_VOL, 0x0}, + {MAX98363_R2041_AMP_GAIN, 0x5}, + {MAX98363_R2042_DSP_CFG, 0x0}, +}; + +static bool max98363_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98363_R2001_INTR_RAW: + case MAX98363_R2003_INTR_STATE: + case MAX98363_R2005_INTR_FALG: + case MAX98363_R2007_INTR_EN: + case MAX98363_R2009_INTR_CLR: + case MAX98363_R2021_ERR_MON_CTRL ... MAX98363_R2023_SPK_MON_DURATION: + case MAX98363_R2030_TONE_GEN_CFG: + case MAX98363_R203F_TONE_GEN_EN: + case MAX98363_R2040_AMP_VOL: + case MAX98363_R2041_AMP_GAIN: + case MAX98363_R2042_DSP_CFG: + case MAX98363_R21FF_REV_ID: + return true; + default: + return false; + } +}; + +static bool max98363_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98363_R2001_INTR_RAW: + case MAX98363_R2003_INTR_STATE: + case MAX98363_R2005_INTR_FALG: + case MAX98363_R2007_INTR_EN: + case MAX98363_R2009_INTR_CLR: + case MAX98363_R21FF_REV_ID: + return true; + default: + return false; + } +} + +static const struct regmap_config max98363_sdw_regmap = { + .reg_bits = 32, + .val_bits = 8, + .max_register = MAX98363_R21FF_REV_ID, + .reg_defaults = max98363_reg, + .num_reg_defaults = ARRAY_SIZE(max98363_reg), + .readable_reg = max98363_readable_register, + .volatile_reg = max98363_volatile_reg, + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static int max98363_suspend(struct device *dev) +{ + struct max98363_priv *max98363 = dev_get_drvdata(dev); + + regcache_cache_only(max98363->regmap, true); + regcache_mark_dirty(max98363->regmap); + + return 0; +} + +#define MAX98363_PROBE_TIMEOUT 5000 + +static int max98363_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct max98363_priv *max98363 = dev_get_drvdata(dev); + unsigned long time; + + if (!max98363->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(MAX98363_PROBE_TIMEOUT)); + if (!time) { + dev_err(dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + +regmap_sync: + + slave->unattach_request = 0; + regcache_cache_only(max98363->regmap, false); + regcache_sync(max98363->regmap); + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(max98363_pm, max98363_suspend, max98363_resume, NULL); + +static int max98363_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + + /* BITMAP: 00000010 Dataport 1 is active */ + prop->sink_ports = BIT(1); + prop->paging_support = true; + prop->clk_stop_timeout = 20; + prop->simple_clk_stop_capable = true; + prop->clock_reg_supported = true; + + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + return 0; +} + +static int max98363_io_init(struct sdw_slave *slave) +{ + struct device *dev = &slave->dev; + struct max98363_priv *max98363 = dev_get_drvdata(dev); + int ret, reg; + + regcache_cache_only(max98363->regmap, false); + if (max98363->first_hw_init) + regcache_cache_bypass(max98363->regmap, true); + + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + if (!max98363->first_hw_init) + /* update count of parent 'active' children */ + pm_runtime_set_active(dev); + + pm_runtime_get_noresume(dev); + + ret = regmap_read(max98363->regmap, MAX98363_R21FF_REV_ID, ®); + if (!ret) + dev_info(dev, "Revision ID: %X\n", reg); + else + goto out; + + if (max98363->first_hw_init) { + regcache_cache_bypass(max98363->regmap, false); + regcache_mark_dirty(max98363->regmap); + } + + max98363->first_hw_init = true; + max98363->hw_init = true; + +out: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +#define MAX98363_RATES SNDRV_PCM_RATE_8000_192000 +#define MAX98363_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static int max98363_sdw_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98363_priv *max98363 = + snd_soc_component_get_drvdata(component); + + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *stream; + struct snd_pcm_runtime *runtime = substream->runtime; + + int ret; + + stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!stream) + return -EINVAL; + + if (!max98363->slave) + return -EINVAL; + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + + direction = SDW_DATA_DIR_RX; + port_config.num = 1; + + stream_config.frame_rate = params_rate(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + stream_config.ch_count = 1; + + if (stream_config.ch_count > runtime->hw.channels_max) { + stream_config.ch_count = runtime->hw.channels_max; + dev_info(dai->dev, "Number of channels: %d (requested: %d)\n", + stream_config.ch_count, params_channels(params)); + } + port_config.ch_mask = GENMASK((int)stream_config.ch_count - 1, 0); + + ret = sdw_stream_add_slave(max98363->slave, &stream_config, + &port_config, 1, stream); + if (ret) { + dev_err(dai->dev, "Unable to configure port\n"); + return ret; + } + + dev_dbg(component->dev, "Format supported %d", params_format(params)); + + return 0; +} + +static int max98363_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98363_priv *max98363 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!max98363->slave) + return -EINVAL; + + sdw_stream_remove_slave(max98363->slave, stream); + + return 0; +} + +static int max98363_set_sdw_stream(struct snd_soc_dai *dai, + void *sdw_stream, int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static const struct snd_soc_dai_ops max98363_dai_sdw_ops = { + .hw_params = max98363_sdw_dai_hw_params, + .hw_free = max98363_pcm_hw_free, + .set_stream = max98363_set_sdw_stream, +}; + +static struct snd_soc_dai_driver max98363_dai[] = { + { + .name = "max98363-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 1, + .rates = MAX98363_RATES, + .formats = MAX98363_FORMATS, + }, + .ops = &max98363_dai_sdw_ops, + } +}; + +static int max98363_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct max98363_priv *max98363 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + max98363->hw_init = false; + + /* + * Perform initialization only if slave status is SDW_SLAVE_ATTACHED + */ + if (max98363->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return max98363_io_init(slave); +} + +static struct sdw_slave_ops max98363_slave_ops = { + .read_prop = max98363_read_prop, + .update_status = max98363_update_status, +}; + +static DECLARE_TLV_DB_SCALE(max98363_digital_tlv, -6350, 50, 1); +static const DECLARE_TLV_DB_RANGE(max98363_spk_tlv, + 0, 5, TLV_DB_SCALE_ITEM(-300, 300, 0), +); + +static const char * const max98363_tone_cfg_text[] = { + "Reserved", "0", "+FS/2", "-FS/2", "1KHz", + "12KHz", "8KHz", "6KHz", "4KHz", "3KHz", + "2KHz", "1.5KHz", "Reserved", "500Hz", "250Hz" +}; + +static SOC_ENUM_SINGLE_DECL(max98363_tone_cfg_enum, + MAX98363_R2030_TONE_GEN_CFG, 0, + max98363_tone_cfg_text); + +static const char * const max98363_spkmon_duration_text[] = { + "8ms", "20ms", "40ms", "60ms", + "80ms", "160ms", "240ms", "320ms", + "400ms", "480ms", "560ms", "640ms", + "720ms", "800ms", "880ms", "960ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98363_spkmon_duration_enum, + MAX98363_R2023_SPK_MON_DURATION, 0, + max98363_spkmon_duration_text); + +static const struct snd_kcontrol_new max98363_snd_controls[] = { + SOC_SINGLE_TLV("Digital Volume", MAX98363_R2040_AMP_VOL, + 0, 0x7F, 1, max98363_digital_tlv), + SOC_SINGLE_TLV("Speaker Volume", MAX98363_R2041_AMP_GAIN, + 0, 10, 0, max98363_spk_tlv), + SOC_SINGLE("Tone Generator Switch", MAX98363_R203F_TONE_GEN_EN, + 0, 1, 0), + SOC_ENUM("Tone Config", max98363_tone_cfg_enum), + SOC_SINGLE("Ramp Switch", MAX98363_R2042_DSP_CFG, + MAX98363_AMP_DSP_CFG_RMP_SHIFT, 1, 0), + SOC_SINGLE("CLK Monitor Switch", MAX98363_R2021_ERR_MON_CTRL, + MAX98363_CLOCK_MON_SHIFT, 1, 0), + SOC_SINGLE("SPKMON Monitor Switch", MAX98363_R2021_ERR_MON_CTRL, + MAX98363_SPKMON_SHIFT, 1, 0), + SOC_SINGLE("SPKMON Thresh", MAX98363_R2022_SPK_MON_THRESH, 0, 0xFF, 0), + SOC_ENUM("SPKMON Duration", max98363_spkmon_duration_enum), +}; + +static const struct snd_soc_dapm_widget max98363_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("BE_OUT"), +}; + +static const struct snd_soc_dapm_route max98363_audio_map[] = { + /* Plabyack */ + {"BE_OUT", NULL, "AIFIN"}, +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98363 = { + .controls = max98363_snd_controls, + .num_controls = ARRAY_SIZE(max98363_snd_controls), + .dapm_widgets = max98363_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98363_dapm_widgets), + .dapm_routes = max98363_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98363_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int max98363_init(struct sdw_slave *slave, struct regmap *regmap) +{ + struct max98363_priv *max98363; + int ret; + struct device *dev = &slave->dev; + + /* Allocate and assign private driver data structure */ + max98363 = devm_kzalloc(dev, sizeof(*max98363), GFP_KERNEL); + if (!max98363) + return -ENOMEM; + + dev_set_drvdata(dev, max98363); + max98363->regmap = regmap; + max98363->slave = slave; + + regcache_cache_only(max98363->regmap, true); + + max98363->hw_init = false; + max98363->first_hw_init = false; + + /* codec registration */ + ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98363, + max98363_dai, + ARRAY_SIZE(max98363_dai)); + if (ret < 0) { + dev_err(dev, "Failed to register codec: %d\n", ret); + return ret; + } + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + return 0; +} + +static int max98363_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &max98363_sdw_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return max98363_init(slave, regmap); +} + +static const struct sdw_device_id max98363_id[] = { + SDW_SLAVE_ENTRY(0x019F, 0x8363, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, max98363_id); + +static struct sdw_driver max98363_sdw_driver = { + .driver = { + .name = "max98363", + .pm = pm_ptr(&max98363_pm), + }, + .probe = max98363_sdw_probe, + .ops = &max98363_slave_ops, + .id_table = max98363_id, +}; + +module_sdw_driver(max98363_sdw_driver); + +MODULE_DESCRIPTION("ASoC MAX98363 driver SDW"); +MODULE_AUTHOR("Ryan Lee "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98363.h b/sound/soc/codecs/max98363.h new file mode 100644 index 0000000000..2b6743d3a2 --- /dev/null +++ b/sound/soc/codecs/max98363.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2022 Analog Devices Inc. */ + +#ifndef _MAX98363_H +#define _MAX98363_H + +#define MAX98363_R2000_SW_RESET 0x2000 +#define MAX98363_R2001_INTR_RAW 0x2001 +#define MAX98363_R2003_INTR_STATE 0x2003 +#define MAX98363_R2005_INTR_FALG 0x2005 +#define MAX98363_R2007_INTR_EN 0x2007 +#define MAX98363_R2009_INTR_CLR 0x2009 +#define MAX98363_R2021_ERR_MON_CTRL 0x2021 +#define MAX98363_R2022_SPK_MON_THRESH 0x2022 +#define MAX98363_R2023_SPK_MON_DURATION 0x2023 +#define MAX98363_R2030_TONE_GEN_CFG 0x2030 +#define MAX98363_R203F_TONE_GEN_EN 0x203F +#define MAX98363_R2040_AMP_VOL 0x2040 +#define MAX98363_R2041_AMP_GAIN 0x2041 +#define MAX98363_R2042_DSP_CFG 0x2042 +#define MAX98363_R21FF_REV_ID 0x21FF + +/* MAX98363_R2021_ERR_MON_CTRL */ +#define MAX98363_SPKMON_SHIFT (3) +#define MAX98363_CLOCK_MON_SHIFT (0) + +/* MAX98363_R2042_DSP_CFG */ +#define MAX98363_AMP_DSP_CFG_RMP_SHIFT (3) + +struct max98363_priv { + struct regmap *regmap; + struct sdw_slave *slave; + bool hw_init; + bool first_hw_init; +}; +#endif diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c new file mode 100644 index 0000000000..f0e49179c3 --- /dev/null +++ b/sound/soc/codecs/max98371.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * max98371.c -- ALSA SoC Stereo MAX98371 driver + * + * Copyright 2015-16 Maxim Integrated Products + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98371.h" + +static const char *const monomix_text[] = { + "Left", "Right", "LeftRightDiv2", +}; + +static const char *const hpf_cutoff_txt[] = { + "Disable", "DC Block", "50Hz", + "100Hz", "200Hz", "400Hz", "800Hz", +}; + +static SOC_ENUM_SINGLE_DECL(max98371_monomix, MAX98371_MONOMIX_CFG, 0, + monomix_text); + +static SOC_ENUM_SINGLE_DECL(max98371_hpf_cutoff, MAX98371_HPF, 0, + hpf_cutoff_txt); + +static const DECLARE_TLV_DB_RANGE(max98371_dht_min_gain, + 0, 1, TLV_DB_SCALE_ITEM(537, 66, 0), + 2, 3, TLV_DB_SCALE_ITEM(677, 82, 0), + 4, 5, TLV_DB_SCALE_ITEM(852, 104, 0), + 6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0), + 8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0), + 10, 11, TLV_DB_SCALE_ITEM(1699, 101, 0), +); + +static const DECLARE_TLV_DB_RANGE(max98371_dht_max_gain, + 0, 1, TLV_DB_SCALE_ITEM(537, 66, 0), + 2, 3, TLV_DB_SCALE_ITEM(677, 82, 0), + 4, 5, TLV_DB_SCALE_ITEM(852, 104, 0), + 6, 7, TLV_DB_SCALE_ITEM(1072, 131, 0), + 8, 9, TLV_DB_SCALE_ITEM(1350, 165, 0), + 10, 11, TLV_DB_SCALE_ITEM(1699, 208, 0), +); + +static const DECLARE_TLV_DB_RANGE(max98371_dht_rot_gain, + 0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0), + 2, 6, TLV_DB_SCALE_ITEM(-100, -100, 0), + 7, 8, TLV_DB_SCALE_ITEM(-800, -200, 0), + 9, 11, TLV_DB_SCALE_ITEM(-1200, -300, 0), + 12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0), + 14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0), +); + +static const struct reg_default max98371_reg[] = { + { 0x01, 0x00 }, + { 0x02, 0x00 }, + { 0x03, 0x00 }, + { 0x04, 0x00 }, + { 0x05, 0x00 }, + { 0x06, 0x00 }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x00 }, + { 0x0A, 0x00 }, + { 0x10, 0x06 }, + { 0x11, 0x08 }, + { 0x14, 0x80 }, + { 0x15, 0x00 }, + { 0x16, 0x00 }, + { 0x18, 0x00 }, + { 0x19, 0x00 }, + { 0x1C, 0x00 }, + { 0x1D, 0x00 }, + { 0x1E, 0x00 }, + { 0x1F, 0x00 }, + { 0x20, 0x00 }, + { 0x21, 0x00 }, + { 0x22, 0x00 }, + { 0x23, 0x00 }, + { 0x24, 0x00 }, + { 0x25, 0x00 }, + { 0x26, 0x00 }, + { 0x27, 0x00 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2A, 0x00 }, + { 0x2B, 0x00 }, + { 0x2C, 0x00 }, + { 0x2D, 0x00 }, + { 0x2E, 0x0B }, + { 0x31, 0x00 }, + { 0x32, 0x18 }, + { 0x33, 0x00 }, + { 0x34, 0x00 }, + { 0x36, 0x00 }, + { 0x37, 0x00 }, + { 0x38, 0x00 }, + { 0x39, 0x00 }, + { 0x3A, 0x00 }, + { 0x3B, 0x00 }, + { 0x3C, 0x00 }, + { 0x3D, 0x00 }, + { 0x3E, 0x00 }, + { 0x3F, 0x00 }, + { 0x40, 0x00 }, + { 0x41, 0x00 }, + { 0x42, 0x00 }, + { 0x43, 0x00 }, + { 0x4A, 0x00 }, + { 0x4B, 0x00 }, + { 0x4C, 0x00 }, + { 0x4D, 0x00 }, + { 0x4E, 0x00 }, + { 0x50, 0x00 }, + { 0x51, 0x00 }, + { 0x55, 0x00 }, + { 0x58, 0x00 }, + { 0x59, 0x00 }, + { 0x5C, 0x00 }, + { 0xFF, 0x43 }, +}; + +static bool max98371_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98371_IRQ_CLEAR1: + case MAX98371_IRQ_CLEAR2: + case MAX98371_IRQ_CLEAR3: + case MAX98371_VERSION: + return true; + default: + return false; + } +} + +static bool max98371_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98371_SOFT_RESET: + return false; + default: + return true; + } +}; + +static const DECLARE_TLV_DB_RANGE(max98371_gain_tlv, + 0, 7, TLV_DB_SCALE_ITEM(0, 50, 0), + 8, 10, TLV_DB_SCALE_ITEM(400, 100, 0) +); + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -6300, 50, 1); + +static const struct snd_kcontrol_new max98371_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", MAX98371_GAIN, + MAX98371_GAIN_SHIFT, (1<component; + struct max98371_priv *max98371 = snd_soc_component_get_drvdata(component); + unsigned int val = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + dev_err(component->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val |= 0; + break; + case SND_SOC_DAIFMT_RIGHT_J: + val |= MAX98371_DAI_RIGHT; + break; + case SND_SOC_DAIFMT_LEFT_J: + val |= MAX98371_DAI_LEFT; + break; + default: + dev_err(component->dev, "DAI wrong mode unsupported"); + return -EINVAL; + } + regmap_update_bits(max98371->regmap, MAX98371_FMT, + MAX98371_FMT_MODE_MASK, val); + return 0; +} + +static int max98371_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98371_priv *max98371 = snd_soc_component_get_drvdata(component); + int blr_clk_ratio, ch_size, channels = params_channels(params); + int rate = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + regmap_update_bits(max98371->regmap, MAX98371_FMT, + MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16); + ch_size = 8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + regmap_update_bits(max98371->regmap, MAX98371_FMT, + MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_16); + ch_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + regmap_update_bits(max98371->regmap, MAX98371_FMT, + MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32); + ch_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + regmap_update_bits(max98371->regmap, MAX98371_FMT, + MAX98371_FMT_MASK, MAX98371_DAI_CHANSZ_32); + ch_size = 32; + break; + default: + return -EINVAL; + } + + /* BCLK/LRCLK ratio calculation */ + blr_clk_ratio = channels * ch_size; + switch (blr_clk_ratio) { + case 32: + regmap_update_bits(max98371->regmap, + MAX98371_DAI_CLK, + MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_32); + break; + case 48: + regmap_update_bits(max98371->regmap, + MAX98371_DAI_CLK, + MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_48); + break; + case 64: + regmap_update_bits(max98371->regmap, + MAX98371_DAI_CLK, + MAX98371_DAI_BSEL_MASK, MAX98371_DAI_BSEL_64); + break; + default: + return -EINVAL; + } + + switch (rate) { + case 32000: + regmap_update_bits(max98371->regmap, + MAX98371_SPK_SR, + MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_32); + break; + case 44100: + regmap_update_bits(max98371->regmap, + MAX98371_SPK_SR, + MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_44); + break; + case 48000: + regmap_update_bits(max98371->regmap, + MAX98371_SPK_SR, + MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_48); + break; + case 88200: + regmap_update_bits(max98371->regmap, + MAX98371_SPK_SR, + MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_88); + break; + case 96000: + regmap_update_bits(max98371->regmap, + MAX98371_SPK_SR, + MAX98371_SPK_SR_MASK, MAX98371_SPK_SR_96); + break; + default: + return -EINVAL; + } + + /* enabling both the RX channels*/ + regmap_update_bits(max98371->regmap, MAX98371_MONOMIX_SRC, + MAX98371_MONOMIX_SRC_MASK, MONOMIX_RX_0_1); + regmap_update_bits(max98371->regmap, MAX98371_DAI_CHANNEL, + MAX98371_CHANNEL_MASK, MAX98371_CHANNEL_MASK); + return 0; +} + +static const struct snd_soc_dapm_widget max98371_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", NULL, MAX98371_SPK_ENABLE, 0, 0), + SND_SOC_DAPM_SUPPLY("Global Enable", MAX98371_GLOBAL_ENABLE, + 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPK_OUT"), +}; + +static const struct snd_soc_dapm_route max98371_audio_map[] = { + {"DAC", NULL, "HiFi Playback"}, + {"SPK_OUT", NULL, "DAC"}, + {"SPK_OUT", NULL, "Global Enable"}, +}; + +#define MAX98371_RATES SNDRV_PCM_RATE_8000_48000 +#define MAX98371_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE) + +static const struct snd_soc_dai_ops max98371_dai_ops = { + .set_fmt = max98371_dai_set_fmt, + .hw_params = max98371_dai_hw_params, +}; + +static struct snd_soc_dai_driver max98371_dai[] = { + { + .name = "max98371-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98371_FORMATS, + }, + .ops = &max98371_dai_ops, + } +}; + +static const struct snd_soc_component_driver max98371_component = { + .controls = max98371_snd_controls, + .num_controls = ARRAY_SIZE(max98371_snd_controls), + .dapm_routes = max98371_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98371_audio_map), + .dapm_widgets = max98371_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98371_dapm_widgets), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config max98371_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX98371_VERSION, + .reg_defaults = max98371_reg, + .num_reg_defaults = ARRAY_SIZE(max98371_reg), + .volatile_reg = max98371_volatile_register, + .readable_reg = max98371_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98371_i2c_probe(struct i2c_client *i2c) +{ + struct max98371_priv *max98371; + int ret, reg; + + max98371 = devm_kzalloc(&i2c->dev, + sizeof(*max98371), GFP_KERNEL); + if (!max98371) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98371); + max98371->regmap = devm_regmap_init_i2c(i2c, &max98371_regmap); + if (IS_ERR(max98371->regmap)) { + ret = PTR_ERR(max98371->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + + ret = regmap_read(max98371->regmap, MAX98371_VERSION, ®); + if (ret < 0) { + dev_info(&i2c->dev, "device error %d\n", ret); + return ret; + } + dev_info(&i2c->dev, "device version %x\n", reg); + + ret = devm_snd_soc_register_component(&i2c->dev, &max98371_component, + max98371_dai, ARRAY_SIZE(max98371_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register component: %d\n", ret); + return ret; + } + return ret; +} + +static const struct i2c_device_id max98371_i2c_id[] = { + { "max98371", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, max98371_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id max98371_of_match[] = { + { .compatible = "maxim,max98371", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98371_of_match); +#endif + +static struct i2c_driver max98371_i2c_driver = { + .driver = { + .name = "max98371", + .of_match_table = of_match_ptr(max98371_of_match), + }, + .probe = max98371_i2c_probe, + .id_table = max98371_i2c_id, +}; + +module_i2c_driver(max98371_i2c_driver); + +MODULE_AUTHOR("anish kumar "); +MODULE_DESCRIPTION("ALSA SoC MAX98371 driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98371.h b/sound/soc/codecs/max98371.h new file mode 100644 index 0000000000..63d9a9de33 --- /dev/null +++ b/sound/soc/codecs/max98371.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * max98371.h -- MAX98371 ALSA SoC Audio driver + * + * Copyright 2011-2012 Maxim Integrated Products + */ + +#ifndef _MAX98371_H +#define _MAX98371_H + +#define MAX98371_IRQ_CLEAR1 0x01 +#define MAX98371_IRQ_CLEAR2 0x02 +#define MAX98371_IRQ_CLEAR3 0x03 +#define MAX98371_DAI_CLK 0x10 +#define MAX98371_DAI_BSEL_MASK 0xF +#define MAX98371_DAI_BSEL_32 2 +#define MAX98371_DAI_BSEL_48 3 +#define MAX98371_DAI_BSEL_64 4 +#define MAX98371_SPK_SR 0x11 +#define MAX98371_SPK_SR_MASK 0xF +#define MAX98371_SPK_SR_32 6 +#define MAX98371_SPK_SR_44 7 +#define MAX98371_SPK_SR_48 8 +#define MAX98371_SPK_SR_88 10 +#define MAX98371_SPK_SR_96 11 +#define MAX98371_DAI_CHANNEL 0x15 +#define MAX98371_CHANNEL_MASK 0x3 +#define MAX98371_MONOMIX_SRC 0x18 +#define MAX98371_MONOMIX_CFG 0x19 +#define MAX98371_HPF 0x1C +#define MAX98371_MONOMIX_SRC_MASK 0xFF +#define MONOMIX_RX_0_1 ((0x1)<<(4)) +#define M98371_DAI_CHANNEL_I2S 0x3 +#define MAX98371_DIGITAL_GAIN 0x2D +#define MAX98371_DIGITAL_GAIN_WIDTH 0x7 +#define MAX98371_GAIN 0x2E +#define MAX98371_GAIN_SHIFT 0x4 +#define MAX98371_GAIN_WIDTH 0x4 +#define MAX98371_DHT_MAX_WIDTH 4 +#define MAX98371_FMT 0x14 +#define MAX98371_CHANSZ_WIDTH 6 +#define MAX98371_FMT_MASK ((0x3)<<(MAX98371_CHANSZ_WIDTH)) +#define MAX98371_FMT_MODE_MASK ((0x7)<<(3)) +#define MAX98371_DAI_LEFT ((0x1)<<(3)) +#define MAX98371_DAI_RIGHT ((0x2)<<(3)) +#define MAX98371_DAI_CHANSZ_16 ((1)<<(MAX98371_CHANSZ_WIDTH)) +#define MAX98371_DAI_CHANSZ_24 ((2)<<(MAX98371_CHANSZ_WIDTH)) +#define MAX98371_DAI_CHANSZ_32 ((3)<<(MAX98371_CHANSZ_WIDTH)) +#define MAX98371_DHT 0x32 +#define MAX98371_DHT_STEP 0x3 +#define MAX98371_DHT_GAIN 0x31 +#define MAX98371_DHT_GAIN_WIDTH 0x4 +#define MAX98371_DHT_ROT_WIDTH 0x4 +#define MAX98371_SPK_ENABLE 0x4A +#define MAX98371_GLOBAL_ENABLE 0x50 +#define MAX98371_SOFT_RESET 0x51 +#define MAX98371_VERSION 0xFF + + +struct max98371_priv { + struct regmap *regmap; +}; +#endif diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c new file mode 100644 index 0000000000..0fa5ceca62 --- /dev/null +++ b/sound/soc/codecs/max98373-i2c.c @@ -0,0 +1,635 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017, Maxim Integrated + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98373.h" + +static const u32 max98373_i2c_cache_reg[] = { + MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, + MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, + MAX98373_R20B6_BDE_CUR_STATE_READBACK, +}; + +static struct reg_default max98373_reg[] = { + {MAX98373_R2000_SW_RESET, 0x00}, + {MAX98373_R2001_INT_RAW1, 0x00}, + {MAX98373_R2002_INT_RAW2, 0x00}, + {MAX98373_R2003_INT_RAW3, 0x00}, + {MAX98373_R2004_INT_STATE1, 0x00}, + {MAX98373_R2005_INT_STATE2, 0x00}, + {MAX98373_R2006_INT_STATE3, 0x00}, + {MAX98373_R2007_INT_FLAG1, 0x00}, + {MAX98373_R2008_INT_FLAG2, 0x00}, + {MAX98373_R2009_INT_FLAG3, 0x00}, + {MAX98373_R200A_INT_EN1, 0x00}, + {MAX98373_R200B_INT_EN2, 0x00}, + {MAX98373_R200C_INT_EN3, 0x00}, + {MAX98373_R200D_INT_FLAG_CLR1, 0x00}, + {MAX98373_R200E_INT_FLAG_CLR2, 0x00}, + {MAX98373_R200F_INT_FLAG_CLR3, 0x00}, + {MAX98373_R2010_IRQ_CTRL, 0x00}, + {MAX98373_R2014_THERM_WARN_THRESH, 0x10}, + {MAX98373_R2015_THERM_SHDN_THRESH, 0x27}, + {MAX98373_R2016_THERM_HYSTERESIS, 0x01}, + {MAX98373_R2017_THERM_FOLDBACK_SET, 0xC0}, + {MAX98373_R2018_THERM_FOLDBACK_EN, 0x00}, + {MAX98373_R201E_PIN_DRIVE_STRENGTH, 0x55}, + {MAX98373_R2020_PCM_TX_HIZ_EN_1, 0xFE}, + {MAX98373_R2021_PCM_TX_HIZ_EN_2, 0xFF}, + {MAX98373_R2022_PCM_TX_SRC_1, 0x00}, + {MAX98373_R2023_PCM_TX_SRC_2, 0x00}, + {MAX98373_R2024_PCM_DATA_FMT_CFG, 0xC0}, + {MAX98373_R2025_AUDIO_IF_MODE, 0x00}, + {MAX98373_R2026_PCM_CLOCK_RATIO, 0x04}, + {MAX98373_R2027_PCM_SR_SETUP_1, 0x08}, + {MAX98373_R2028_PCM_SR_SETUP_2, 0x88}, + {MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, 0x00}, + {MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, 0x00}, + {MAX98373_R202B_PCM_RX_EN, 0x00}, + {MAX98373_R202C_PCM_TX_EN, 0x00}, + {MAX98373_R202E_ICC_RX_CH_EN_1, 0x00}, + {MAX98373_R202F_ICC_RX_CH_EN_2, 0x00}, + {MAX98373_R2030_ICC_TX_HIZ_EN_1, 0xFF}, + {MAX98373_R2031_ICC_TX_HIZ_EN_2, 0xFF}, + {MAX98373_R2032_ICC_LINK_EN_CFG, 0x30}, + {MAX98373_R2034_ICC_TX_CNTL, 0x00}, + {MAX98373_R2035_ICC_TX_EN, 0x00}, + {MAX98373_R2036_SOUNDWIRE_CTRL, 0x05}, + {MAX98373_R203D_AMP_DIG_VOL_CTRL, 0x00}, + {MAX98373_R203E_AMP_PATH_GAIN, 0x08}, + {MAX98373_R203F_AMP_DSP_CFG, 0x02}, + {MAX98373_R2040_TONE_GEN_CFG, 0x00}, + {MAX98373_R2041_AMP_CFG, 0x03}, + {MAX98373_R2042_AMP_EDGE_RATE_CFG, 0x00}, + {MAX98373_R2043_AMP_EN, 0x00}, + {MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, 0x04}, + {MAX98373_R2047_IV_SENSE_ADC_EN, 0x00}, + {MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0x00}, + {MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, 0x00}, + {MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, 0x00}, + {MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0x00}, + {MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0x00}, + {MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0x00}, + {MAX98373_R2090_BDE_LVL_HOLD, 0x00}, + {MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0x00}, + {MAX98373_R2092_BDE_CLIPPER_MODE, 0x00}, + {MAX98373_R2097_BDE_L1_THRESH, 0x00}, + {MAX98373_R2098_BDE_L2_THRESH, 0x00}, + {MAX98373_R2099_BDE_L3_THRESH, 0x00}, + {MAX98373_R209A_BDE_L4_THRESH, 0x00}, + {MAX98373_R209B_BDE_THRESH_HYST, 0x00}, + {MAX98373_R20A8_BDE_L1_CFG_1, 0x00}, + {MAX98373_R20A9_BDE_L1_CFG_2, 0x00}, + {MAX98373_R20AA_BDE_L1_CFG_3, 0x00}, + {MAX98373_R20AB_BDE_L2_CFG_1, 0x00}, + {MAX98373_R20AC_BDE_L2_CFG_2, 0x00}, + {MAX98373_R20AD_BDE_L2_CFG_3, 0x00}, + {MAX98373_R20AE_BDE_L3_CFG_1, 0x00}, + {MAX98373_R20AF_BDE_L3_CFG_2, 0x00}, + {MAX98373_R20B0_BDE_L3_CFG_3, 0x00}, + {MAX98373_R20B1_BDE_L4_CFG_1, 0x00}, + {MAX98373_R20B2_BDE_L4_CFG_2, 0x00}, + {MAX98373_R20B3_BDE_L4_CFG_3, 0x00}, + {MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE, 0x00}, + {MAX98373_R20B5_BDE_EN, 0x00}, + {MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0x00}, + {MAX98373_R20D1_DHT_CFG, 0x01}, + {MAX98373_R20D2_DHT_ATTACK_CFG, 0x02}, + {MAX98373_R20D3_DHT_RELEASE_CFG, 0x03}, + {MAX98373_R20D4_DHT_EN, 0x00}, + {MAX98373_R20E0_LIMITER_THRESH_CFG, 0x00}, + {MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0x00}, + {MAX98373_R20E2_LIMITER_EN, 0x00}, + {MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, 0x00}, + {MAX98373_R20FF_GLOBAL_SHDN, 0x00}, + {MAX98373_R21FF_REV_ID, 0x42}, +}; + +static int max98373_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component); + unsigned int format = 0; + unsigned int invert = 0; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98373->regmap, + MAX98373_R2026_PCM_CLOCK_RATIO, + MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE, + invert); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = MAX98373_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = MAX98373_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98373_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98373_PCM_FORMAT_TDM_MODE0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(max98373->regmap, + MAX98373_R2024_PCM_DATA_FMT_CFG, + MAX98373_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98373_PCM_MODE_CFG_FORMAT_SHIFT); + + return 0; +} + +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, 320, +}; + +static int max98373_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} + +static int max98373_set_clock(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98373->ch_size; + int value; + + if (!max98373->tdm_mode) { + /* BCLK configuration */ + value = max98373_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98373->regmap, + MAX98373_R2026_PCM_CLOCK_RATIO, + MAX98373_PCM_CLK_SETUP_BSEL_MASK, + value); + } + return 0; +} + +static int max98373_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + max98373->ch_size = snd_pcm_format_width(params_format(params)); + + regmap_update_bits(max98373->regmap, + MAX98373_R2024_PCM_DATA_FMT_CFG, + MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + dev_dbg(component->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_8000; + break; + case 11025: + sampling_rate = MAX98373_PCM_SR_SET1_SR_11025; + break; + case 12000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_12000; + break; + case 16000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_16000; + break; + case 22050: + sampling_rate = MAX98373_PCM_SR_SET1_SR_22050; + break; + case 24000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_24000; + break; + case 32000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_32000; + break; + case 44100: + sampling_rate = MAX98373_PCM_SR_SET1_SR_44100; + break; + case 48000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_48000; + break; + case 88200: + sampling_rate = MAX98373_PCM_SR_SET1_SR_88200; + break; + case 96000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_96000; + break; + default: + dev_err(component->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98373->regmap, + MAX98373_R2027_PCM_SR_SETUP_1, + MAX98373_PCM_SR_SET1_SR_MASK, + sampling_rate); + regmap_update_bits(max98373->regmap, + MAX98373_R2028_PCM_SR_SETUP_2, + MAX98373_PCM_SR_SET2_SR_MASK, + sampling_rate << MAX98373_PCM_SR_SET2_SR_SHIFT); + + /* set sampling rate of IV */ + if (max98373->interleave_mode && + sampling_rate > MAX98373_PCM_SR_SET1_SR_16000) + regmap_update_bits(max98373->regmap, + MAX98373_R2028_PCM_SR_SETUP_2, + MAX98373_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate - 3); + else + regmap_update_bits(max98373->regmap, + MAX98373_R2028_PCM_SR_SETUP_2, + MAX98373_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate); + + return max98373_set_clock(component, params); +err: + return -EINVAL; +} + +static int max98373_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component); + int bsel = 0; + unsigned int chan_sz = 0; + unsigned int mask; + int x, slot_found; + + if (!tx_mask && !rx_mask && !slots && !slot_width) + max98373->tdm_mode = false; + else + max98373->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98373_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(component->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98373->regmap, + MAX98373_R2026_PCM_CLOCK_RATIO, + MAX98373_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98373->regmap, + MAX98373_R2024_PCM_DATA_FMT_CFG, + MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + slot_found = 0; + mask = rx_mask; + for (x = 0 ; x < 16 ; x++, mask >>= 1) { + if (mask & 0x1) { + if (slot_found == 0) + regmap_update_bits(max98373->regmap, + MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, + MAX98373_PCM_TO_SPK_CH0_SRC_MASK, x); + else + regmap_write(max98373->regmap, + MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, + x); + slot_found++; + if (slot_found > 1) + break; + } + } + + /* Tx slot Hi-Z configuration */ + regmap_write(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + ~tx_mask & 0xFF); + regmap_write(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + (~tx_mask & 0xFF00) >> 8); + + return 0; +} + +#define MAX98373_RATES SNDRV_PCM_RATE_8000_96000 + +#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98373_dai_ops = { + .set_fmt = max98373_dai_set_fmt, + .hw_params = max98373_dai_hw_params, + .set_tdm_slot = max98373_dai_tdm_slot, +}; + +static bool max98373_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98373_R2000_SW_RESET: + case MAX98373_R2001_INT_RAW1 ... MAX98373_R200C_INT_EN3: + case MAX98373_R2010_IRQ_CTRL: + case MAX98373_R2014_THERM_WARN_THRESH + ... MAX98373_R2018_THERM_FOLDBACK_EN: + case MAX98373_R201E_PIN_DRIVE_STRENGTH + ... MAX98373_R2036_SOUNDWIRE_CTRL: + case MAX98373_R203D_AMP_DIG_VOL_CTRL ... MAX98373_R2043_AMP_EN: + case MAX98373_R2046_IV_SENSE_ADC_DSP_CFG + ... MAX98373_R2047_IV_SENSE_ADC_EN: + case MAX98373_R2051_MEAS_ADC_SAMPLING_RATE + ... MAX98373_R2056_MEAS_ADC_PVDD_CH_EN: + case MAX98373_R2090_BDE_LVL_HOLD ... MAX98373_R2092_BDE_CLIPPER_MODE: + case MAX98373_R2097_BDE_L1_THRESH + ... MAX98373_R209B_BDE_THRESH_HYST: + case MAX98373_R20A8_BDE_L1_CFG_1 ... MAX98373_R20B3_BDE_L4_CFG_3: + case MAX98373_R20B5_BDE_EN ... MAX98373_R20B6_BDE_CUR_STATE_READBACK: + case MAX98373_R20D1_DHT_CFG ... MAX98373_R20D4_DHT_EN: + case MAX98373_R20E0_LIMITER_THRESH_CFG ... MAX98373_R20E2_LIMITER_EN: + case MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG + ... MAX98373_R20FF_GLOBAL_SHDN: + case MAX98373_R21FF_REV_ID: + return true; + default: + return false; + } +}; + +static bool max98373_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98373_R2000_SW_RESET ... MAX98373_R2009_INT_FLAG3: + case MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK: + case MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK: + case MAX98373_R20B6_BDE_CUR_STATE_READBACK: + case MAX98373_R20FF_GLOBAL_SHDN: + case MAX98373_R21FF_REV_ID: + return true; + default: + return false; + } +} + +static struct snd_soc_dai_driver max98373_dai[] = { + { + .name = "max98373-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98373_RATES, + .formats = MAX98373_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98373_RATES, + .formats = MAX98373_FORMATS, + }, + .ops = &max98373_dai_ops, + } +}; + +#ifdef CONFIG_PM_SLEEP +static int max98373_suspend(struct device *dev) +{ + struct max98373_priv *max98373 = dev_get_drvdata(dev); + int i; + + /* cache feedback register values before suspend */ + for (i = 0; i < max98373->cache_num; i++) + regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val); + + regcache_cache_only(max98373->regmap, true); + regcache_mark_dirty(max98373->regmap); + return 0; +} + +static int max98373_resume(struct device *dev) +{ + struct max98373_priv *max98373 = dev_get_drvdata(dev); + + regcache_cache_only(max98373->regmap, false); + max98373_reset(max98373, dev); + regcache_sync(max98373->regmap); + return 0; +} +#endif + +static const struct dev_pm_ops max98373_pm = { + SET_SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume) +}; + +static const struct regmap_config max98373_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98373_R21FF_REV_ID, + .reg_defaults = max98373_reg, + .num_reg_defaults = ARRAY_SIZE(max98373_reg), + .readable_reg = max98373_readable_register, + .volatile_reg = max98373_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98373_i2c_probe(struct i2c_client *i2c) +{ + int ret = 0; + int reg = 0; + int i; + struct max98373_priv *max98373 = NULL; + + max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL); + + if (!max98373) { + ret = -ENOMEM; + return ret; + } + i2c_set_clientdata(i2c, max98373); + + /* update interleave mode info */ + if (device_property_read_bool(&i2c->dev, "maxim,interleave_mode")) + max98373->interleave_mode = true; + else + max98373->interleave_mode = false; + + /* regmap initialization */ + max98373->regmap = devm_regmap_init_i2c(i2c, &max98373_regmap); + if (IS_ERR(max98373->regmap)) { + ret = PTR_ERR(max98373->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + + max98373->cache_num = ARRAY_SIZE(max98373_i2c_cache_reg); + max98373->cache = devm_kcalloc(&i2c->dev, max98373->cache_num, + sizeof(*max98373->cache), + GFP_KERNEL); + if (!max98373->cache) { + ret = -ENOMEM; + return ret; + } + + for (i = 0; i < max98373->cache_num; i++) + max98373->cache[i].reg = max98373_i2c_cache_reg[i]; + + /* voltage/current slot & gpio configuration */ + max98373_slot_config(&i2c->dev, max98373); + + /* Power on device */ + if (gpio_is_valid(max98373->reset_gpio)) { + ret = devm_gpio_request(&i2c->dev, max98373->reset_gpio, + "MAX98373_RESET"); + if (ret) { + dev_err(&i2c->dev, "%s: Failed to request gpio %d\n", + __func__, max98373->reset_gpio); + return -EINVAL; + } + gpio_direction_output(max98373->reset_gpio, 0); + msleep(50); + gpio_direction_output(max98373->reset_gpio, 1); + msleep(20); + } + + /* Check Revision ID */ + ret = regmap_read(max98373->regmap, + MAX98373_R21FF_REV_ID, ®); + if (ret < 0) { + dev_err(&i2c->dev, + "Failed to read: 0x%02X\n", MAX98373_R21FF_REV_ID); + return ret; + } + dev_info(&i2c->dev, "MAX98373 revisionID: 0x%02X\n", reg); + + /* codec registration */ + ret = devm_snd_soc_register_component(&i2c->dev, &soc_codec_dev_max98373, + max98373_dai, ARRAY_SIZE(max98373_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id max98373_i2c_id[] = { + { "max98373", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98373_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id max98373_of_match[] = { + { .compatible = "maxim,max98373", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98373_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98373_acpi_match[] = { + { "MX98373", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98373_acpi_match); +#endif + +static struct i2c_driver max98373_i2c_driver = { + .driver = { + .name = "max98373", + .of_match_table = of_match_ptr(max98373_of_match), + .acpi_match_table = ACPI_PTR(max98373_acpi_match), + .pm = &max98373_pm, + }, + .probe = max98373_i2c_probe, + .id_table = max98373_i2c_id, +}; + +module_i2c_driver(max98373_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98373 driver"); +MODULE_AUTHOR("Ryan Lee "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c new file mode 100644 index 0000000000..b5cb951af5 --- /dev/null +++ b/sound/soc/codecs/max98373-sdw.c @@ -0,0 +1,890 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2020, Maxim Integrated + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98373.h" +#include "max98373-sdw.h" + +static const u32 max98373_sdw_cache_reg[] = { + MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, + MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, + MAX98373_R20B6_BDE_CUR_STATE_READBACK, +}; + +static struct reg_default max98373_reg[] = { + {MAX98373_R0040_SCP_INIT_STAT_1, 0x00}, + {MAX98373_R0041_SCP_INIT_MASK_1, 0x00}, + {MAX98373_R0042_SCP_INIT_STAT_2, 0x00}, + {MAX98373_R0044_SCP_CTRL, 0x00}, + {MAX98373_R0045_SCP_SYSTEM_CTRL, 0x00}, + {MAX98373_R0046_SCP_DEV_NUMBER, 0x00}, + {MAX98373_R0050_SCP_DEV_ID_0, 0x21}, + {MAX98373_R0051_SCP_DEV_ID_1, 0x01}, + {MAX98373_R0052_SCP_DEV_ID_2, 0x9F}, + {MAX98373_R0053_SCP_DEV_ID_3, 0x87}, + {MAX98373_R0054_SCP_DEV_ID_4, 0x08}, + {MAX98373_R0055_SCP_DEV_ID_5, 0x00}, + {MAX98373_R0060_SCP_FRAME_CTLR, 0x00}, + {MAX98373_R0070_SCP_FRAME_CTLR, 0x00}, + {MAX98373_R0100_DP1_INIT_STAT, 0x00}, + {MAX98373_R0101_DP1_INIT_MASK, 0x00}, + {MAX98373_R0102_DP1_PORT_CTRL, 0x00}, + {MAX98373_R0103_DP1_BLOCK_CTRL_1, 0x00}, + {MAX98373_R0104_DP1_PREPARE_STATUS, 0x00}, + {MAX98373_R0105_DP1_PREPARE_CTRL, 0x00}, + {MAX98373_R0120_DP1_CHANNEL_EN, 0x00}, + {MAX98373_R0122_DP1_SAMPLE_CTRL1, 0x00}, + {MAX98373_R0123_DP1_SAMPLE_CTRL2, 0x00}, + {MAX98373_R0124_DP1_OFFSET_CTRL1, 0x00}, + {MAX98373_R0125_DP1_OFFSET_CTRL2, 0x00}, + {MAX98373_R0126_DP1_HCTRL, 0x00}, + {MAX98373_R0127_DP1_BLOCK_CTRL3, 0x00}, + {MAX98373_R0130_DP1_CHANNEL_EN, 0x00}, + {MAX98373_R0132_DP1_SAMPLE_CTRL1, 0x00}, + {MAX98373_R0133_DP1_SAMPLE_CTRL2, 0x00}, + {MAX98373_R0134_DP1_OFFSET_CTRL1, 0x00}, + {MAX98373_R0135_DP1_OFFSET_CTRL2, 0x00}, + {MAX98373_R0136_DP1_HCTRL, 0x0136}, + {MAX98373_R0137_DP1_BLOCK_CTRL3, 0x00}, + {MAX98373_R0300_DP3_INIT_STAT, 0x00}, + {MAX98373_R0301_DP3_INIT_MASK, 0x00}, + {MAX98373_R0302_DP3_PORT_CTRL, 0x00}, + {MAX98373_R0303_DP3_BLOCK_CTRL_1, 0x00}, + {MAX98373_R0304_DP3_PREPARE_STATUS, 0x00}, + {MAX98373_R0305_DP3_PREPARE_CTRL, 0x00}, + {MAX98373_R0320_DP3_CHANNEL_EN, 0x00}, + {MAX98373_R0322_DP3_SAMPLE_CTRL1, 0x00}, + {MAX98373_R0323_DP3_SAMPLE_CTRL2, 0x00}, + {MAX98373_R0324_DP3_OFFSET_CTRL1, 0x00}, + {MAX98373_R0325_DP3_OFFSET_CTRL2, 0x00}, + {MAX98373_R0326_DP3_HCTRL, 0x00}, + {MAX98373_R0327_DP3_BLOCK_CTRL3, 0x00}, + {MAX98373_R0330_DP3_CHANNEL_EN, 0x00}, + {MAX98373_R0332_DP3_SAMPLE_CTRL1, 0x00}, + {MAX98373_R0333_DP3_SAMPLE_CTRL2, 0x00}, + {MAX98373_R0334_DP3_OFFSET_CTRL1, 0x00}, + {MAX98373_R0335_DP3_OFFSET_CTRL2, 0x00}, + {MAX98373_R0336_DP3_HCTRL, 0x00}, + {MAX98373_R0337_DP3_BLOCK_CTRL3, 0x00}, + {MAX98373_R2000_SW_RESET, 0x00}, + {MAX98373_R2001_INT_RAW1, 0x00}, + {MAX98373_R2002_INT_RAW2, 0x00}, + {MAX98373_R2003_INT_RAW3, 0x00}, + {MAX98373_R2004_INT_STATE1, 0x00}, + {MAX98373_R2005_INT_STATE2, 0x00}, + {MAX98373_R2006_INT_STATE3, 0x00}, + {MAX98373_R2007_INT_FLAG1, 0x00}, + {MAX98373_R2008_INT_FLAG2, 0x00}, + {MAX98373_R2009_INT_FLAG3, 0x00}, + {MAX98373_R200A_INT_EN1, 0x00}, + {MAX98373_R200B_INT_EN2, 0x00}, + {MAX98373_R200C_INT_EN3, 0x00}, + {MAX98373_R200D_INT_FLAG_CLR1, 0x00}, + {MAX98373_R200E_INT_FLAG_CLR2, 0x00}, + {MAX98373_R200F_INT_FLAG_CLR3, 0x00}, + {MAX98373_R2010_IRQ_CTRL, 0x00}, + {MAX98373_R2014_THERM_WARN_THRESH, 0x10}, + {MAX98373_R2015_THERM_SHDN_THRESH, 0x27}, + {MAX98373_R2016_THERM_HYSTERESIS, 0x01}, + {MAX98373_R2017_THERM_FOLDBACK_SET, 0xC0}, + {MAX98373_R2018_THERM_FOLDBACK_EN, 0x00}, + {MAX98373_R201E_PIN_DRIVE_STRENGTH, 0x55}, + {MAX98373_R2020_PCM_TX_HIZ_EN_1, 0xFE}, + {MAX98373_R2021_PCM_TX_HIZ_EN_2, 0xFF}, + {MAX98373_R2022_PCM_TX_SRC_1, 0x00}, + {MAX98373_R2023_PCM_TX_SRC_2, 0x00}, + {MAX98373_R2024_PCM_DATA_FMT_CFG, 0xC0}, + {MAX98373_R2025_AUDIO_IF_MODE, 0x00}, + {MAX98373_R2026_PCM_CLOCK_RATIO, 0x04}, + {MAX98373_R2027_PCM_SR_SETUP_1, 0x08}, + {MAX98373_R2028_PCM_SR_SETUP_2, 0x88}, + {MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, 0x00}, + {MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, 0x00}, + {MAX98373_R202B_PCM_RX_EN, 0x00}, + {MAX98373_R202C_PCM_TX_EN, 0x00}, + {MAX98373_R202E_ICC_RX_CH_EN_1, 0x00}, + {MAX98373_R202F_ICC_RX_CH_EN_2, 0x00}, + {MAX98373_R2030_ICC_TX_HIZ_EN_1, 0xFF}, + {MAX98373_R2031_ICC_TX_HIZ_EN_2, 0xFF}, + {MAX98373_R2032_ICC_LINK_EN_CFG, 0x30}, + {MAX98373_R2034_ICC_TX_CNTL, 0x00}, + {MAX98373_R2035_ICC_TX_EN, 0x00}, + {MAX98373_R2036_SOUNDWIRE_CTRL, 0x05}, + {MAX98373_R203D_AMP_DIG_VOL_CTRL, 0x00}, + {MAX98373_R203E_AMP_PATH_GAIN, 0x08}, + {MAX98373_R203F_AMP_DSP_CFG, 0x02}, + {MAX98373_R2040_TONE_GEN_CFG, 0x00}, + {MAX98373_R2041_AMP_CFG, 0x03}, + {MAX98373_R2042_AMP_EDGE_RATE_CFG, 0x00}, + {MAX98373_R2043_AMP_EN, 0x00}, + {MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, 0x04}, + {MAX98373_R2047_IV_SENSE_ADC_EN, 0x00}, + {MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0x00}, + {MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, 0x00}, + {MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, 0x00}, + {MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0x00}, + {MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0x00}, + {MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0x00}, + {MAX98373_R2090_BDE_LVL_HOLD, 0x00}, + {MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0x00}, + {MAX98373_R2092_BDE_CLIPPER_MODE, 0x00}, + {MAX98373_R2097_BDE_L1_THRESH, 0x00}, + {MAX98373_R2098_BDE_L2_THRESH, 0x00}, + {MAX98373_R2099_BDE_L3_THRESH, 0x00}, + {MAX98373_R209A_BDE_L4_THRESH, 0x00}, + {MAX98373_R209B_BDE_THRESH_HYST, 0x00}, + {MAX98373_R20A8_BDE_L1_CFG_1, 0x00}, + {MAX98373_R20A9_BDE_L1_CFG_2, 0x00}, + {MAX98373_R20AA_BDE_L1_CFG_3, 0x00}, + {MAX98373_R20AB_BDE_L2_CFG_1, 0x00}, + {MAX98373_R20AC_BDE_L2_CFG_2, 0x00}, + {MAX98373_R20AD_BDE_L2_CFG_3, 0x00}, + {MAX98373_R20AE_BDE_L3_CFG_1, 0x00}, + {MAX98373_R20AF_BDE_L3_CFG_2, 0x00}, + {MAX98373_R20B0_BDE_L3_CFG_3, 0x00}, + {MAX98373_R20B1_BDE_L4_CFG_1, 0x00}, + {MAX98373_R20B2_BDE_L4_CFG_2, 0x00}, + {MAX98373_R20B3_BDE_L4_CFG_3, 0x00}, + {MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE, 0x00}, + {MAX98373_R20B5_BDE_EN, 0x00}, + {MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0x00}, + {MAX98373_R20D1_DHT_CFG, 0x01}, + {MAX98373_R20D2_DHT_ATTACK_CFG, 0x02}, + {MAX98373_R20D3_DHT_RELEASE_CFG, 0x03}, + {MAX98373_R20D4_DHT_EN, 0x00}, + {MAX98373_R20E0_LIMITER_THRESH_CFG, 0x00}, + {MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0x00}, + {MAX98373_R20E2_LIMITER_EN, 0x00}, + {MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, 0x00}, + {MAX98373_R20FF_GLOBAL_SHDN, 0x00}, + {MAX98373_R21FF_REV_ID, 0x42}, +}; + +static bool max98373_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98373_R21FF_REV_ID: + case MAX98373_R2010_IRQ_CTRL: + /* SoundWire Control Port Registers */ + case MAX98373_R0040_SCP_INIT_STAT_1 ... MAX98373_R0070_SCP_FRAME_CTLR: + /* Soundwire Data Port 1 Registers */ + case MAX98373_R0100_DP1_INIT_STAT ... MAX98373_R0137_DP1_BLOCK_CTRL3: + /* Soundwire Data Port 3 Registers */ + case MAX98373_R0300_DP3_INIT_STAT ... MAX98373_R0337_DP3_BLOCK_CTRL3: + case MAX98373_R2000_SW_RESET ... MAX98373_R200C_INT_EN3: + case MAX98373_R2014_THERM_WARN_THRESH + ... MAX98373_R2018_THERM_FOLDBACK_EN: + case MAX98373_R201E_PIN_DRIVE_STRENGTH + ... MAX98373_R2036_SOUNDWIRE_CTRL: + case MAX98373_R203D_AMP_DIG_VOL_CTRL ... MAX98373_R2043_AMP_EN: + case MAX98373_R2046_IV_SENSE_ADC_DSP_CFG + ... MAX98373_R2047_IV_SENSE_ADC_EN: + case MAX98373_R2051_MEAS_ADC_SAMPLING_RATE + ... MAX98373_R2056_MEAS_ADC_PVDD_CH_EN: + case MAX98373_R2090_BDE_LVL_HOLD ... MAX98373_R2092_BDE_CLIPPER_MODE: + case MAX98373_R2097_BDE_L1_THRESH + ... MAX98373_R209B_BDE_THRESH_HYST: + case MAX98373_R20A8_BDE_L1_CFG_1 ... MAX98373_R20B3_BDE_L4_CFG_3: + case MAX98373_R20B5_BDE_EN ... MAX98373_R20B6_BDE_CUR_STATE_READBACK: + case MAX98373_R20D1_DHT_CFG ... MAX98373_R20D4_DHT_EN: + case MAX98373_R20E0_LIMITER_THRESH_CFG ... MAX98373_R20E2_LIMITER_EN: + case MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG + ... MAX98373_R20FF_GLOBAL_SHDN: + return true; + default: + return false; + } +}; + +static bool max98373_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK: + case MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK: + case MAX98373_R20B6_BDE_CUR_STATE_READBACK: + case MAX98373_R20FF_GLOBAL_SHDN: + case MAX98373_R21FF_REV_ID: + /* SoundWire Control Port Registers */ + case MAX98373_R0040_SCP_INIT_STAT_1 ... MAX98373_R0070_SCP_FRAME_CTLR: + /* Soundwire Data Port 1 Registers */ + case MAX98373_R0100_DP1_INIT_STAT ... MAX98373_R0137_DP1_BLOCK_CTRL3: + /* Soundwire Data Port 3 Registers */ + case MAX98373_R0300_DP3_INIT_STAT ... MAX98373_R0337_DP3_BLOCK_CTRL3: + case MAX98373_R2000_SW_RESET ... MAX98373_R2009_INT_FLAG3: + return true; + default: + return false; + } +} + +static const struct regmap_config max98373_sdw_regmap = { + .reg_bits = 32, + .val_bits = 8, + .max_register = MAX98373_R21FF_REV_ID, + .reg_defaults = max98373_reg, + .num_reg_defaults = ARRAY_SIZE(max98373_reg), + .readable_reg = max98373_readable_register, + .volatile_reg = max98373_volatile_reg, + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +/* Power management functions and structure */ +static __maybe_unused int max98373_suspend(struct device *dev) +{ + struct max98373_priv *max98373 = dev_get_drvdata(dev); + int i; + + /* cache feedback register values before suspend */ + for (i = 0; i < max98373->cache_num; i++) + regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val); + + regcache_cache_only(max98373->regmap, true); + + return 0; +} + +#define MAX98373_PROBE_TIMEOUT 5000 + +static __maybe_unused int max98373_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct max98373_priv *max98373 = dev_get_drvdata(dev); + unsigned long time; + + if (!max98373->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(MAX98373_PROBE_TIMEOUT)); + if (!time) { + dev_err(dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(max98373->regmap, false); + regcache_sync(max98373->regmap); + + return 0; +} + +static const struct dev_pm_ops max98373_pm = { + SET_SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume) + SET_RUNTIME_PM_OPS(max98373_suspend, max98373_resume, NULL) +}; + +static int max98373_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + + /* BITMAP: 00001000 Dataport 3 is active */ + prop->source_ports = BIT(3); + /* BITMAP: 00000010 Dataport 1 is active */ + prop->sink_ports = BIT(1); + prop->paging_support = true; + prop->clk_stop_timeout = 20; + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + return 0; +} + +static int max98373_io_init(struct sdw_slave *slave) +{ + struct device *dev = &slave->dev; + struct max98373_priv *max98373 = dev_get_drvdata(dev); + + regcache_cache_only(max98373->regmap, false); + if (max98373->first_hw_init) + regcache_cache_bypass(max98373->regmap, true); + + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + if (!max98373->first_hw_init) + /* update count of parent 'active' children */ + pm_runtime_set_active(dev); + + pm_runtime_get_noresume(dev); + + /* Software Reset */ + max98373_reset(max98373, dev); + + /* Set soundwire mode */ + regmap_write(max98373->regmap, MAX98373_R2025_AUDIO_IF_MODE, 3); + /* Enable ADC */ + regmap_write(max98373->regmap, MAX98373_R2047_IV_SENSE_ADC_EN, 3); + /* Set default Soundwire clock */ + regmap_write(max98373->regmap, MAX98373_R2036_SOUNDWIRE_CTRL, 5); + /* Set default sampling rate for speaker and IVDAC */ + regmap_write(max98373->regmap, MAX98373_R2028_PCM_SR_SETUP_2, 0x88); + /* IV default slot configuration */ + regmap_write(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + 0xFF); + regmap_write(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + 0xFF); + /* L/R mix configuration */ + regmap_write(max98373->regmap, + MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, + 0x80); + regmap_write(max98373->regmap, + MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, + 0x1); + /* Enable DC blocker */ + regmap_write(max98373->regmap, + MAX98373_R203F_AMP_DSP_CFG, + 0x3); + /* Enable IMON VMON DC blocker */ + regmap_write(max98373->regmap, + MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, + 0x7); + /* voltage, current slot configuration */ + regmap_write(max98373->regmap, + MAX98373_R2022_PCM_TX_SRC_1, + (max98373->i_slot << MAX98373_PCM_TX_CH_SRC_A_I_SHIFT | + max98373->v_slot) & 0xFF); + if (max98373->v_slot < 8) + regmap_update_bits(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + 1 << max98373->v_slot, 0); + else + regmap_update_bits(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + 1 << (max98373->v_slot - 8), 0); + + if (max98373->i_slot < 8) + regmap_update_bits(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + 1 << max98373->i_slot, 0); + else + regmap_update_bits(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + 1 << (max98373->i_slot - 8), 0); + + /* speaker feedback slot configuration */ + regmap_write(max98373->regmap, + MAX98373_R2023_PCM_TX_SRC_2, + max98373->spkfb_slot & 0xFF); + + /* Set interleave mode */ + if (max98373->interleave_mode) + regmap_update_bits(max98373->regmap, + MAX98373_R2024_PCM_DATA_FMT_CFG, + MAX98373_PCM_TX_CH_INTERLEAVE_MASK, + MAX98373_PCM_TX_CH_INTERLEAVE_MASK); + + /* Speaker enable */ + regmap_update_bits(max98373->regmap, + MAX98373_R2043_AMP_EN, + MAX98373_SPK_EN_MASK, 1); + + regmap_write(max98373->regmap, MAX98373_R20B5_BDE_EN, 1); + regmap_write(max98373->regmap, MAX98373_R20E2_LIMITER_EN, 1); + + if (max98373->first_hw_init) { + regcache_cache_bypass(max98373->regmap, false); + regcache_mark_dirty(max98373->regmap); + } + + max98373->first_hw_init = true; + max98373->hw_init = true; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static int max98373_clock_calculate(struct sdw_slave *slave, + unsigned int clk_freq) +{ + int x, y; + static const int max98373_clk_family[] = { + 7680000, 8400000, 9600000, 11289600, + 12000000, 12288000, 13000000 + }; + + for (x = 0; x < 4; x++) + for (y = 0; y < ARRAY_SIZE(max98373_clk_family); y++) + if (clk_freq == (max98373_clk_family[y] >> x)) + return (x << 3) + y; + + /* Set default clock (12.288 Mhz) if the value is not in the list */ + dev_err(&slave->dev, "Requested clock not found. (clk_freq = %d)\n", + clk_freq); + return 0x5; +} + +static int max98373_clock_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct device *dev = &slave->dev; + struct max98373_priv *max98373 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (params->curr_dr_freq >> 1); + + /* + * Select the proper value for the register based on the + * requested clock. If the value is not in the list, + * use reasonable default - 12.288 Mhz + */ + value = max98373_clock_calculate(slave, clk_freq); + + /* SWCLK */ + regmap_write(max98373->regmap, MAX98373_R2036_SOUNDWIRE_CTRL, value); + + /* The default Sampling Rate value for IV is 48KHz*/ + regmap_write(max98373->regmap, MAX98373_R2028_PCM_SR_SETUP_2, 0x88); + + return 0; +} + +#define MAX98373_RATES SNDRV_PCM_RATE_8000_96000 +#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) + +static int max98373_sdw_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98373_priv *max98373 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream; + int ret, chan_sz, sampling_rate; + + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!max98373->slave) + return -EINVAL; + + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + port_config.num = 1; + + if (max98373->slot) { + stream_config.ch_count = max98373->slot; + port_config.ch_mask = max98373->rx_mask; + } + } else { + port_config.num = 3; + + /* only IV are supported by capture */ + stream_config.ch_count = 2; + port_config.ch_mask = GENMASK((int)stream_config.ch_count - 1, 0); + } + + ret = sdw_stream_add_slave(max98373->slave, &stream_config, + &port_config, 1, sdw_stream); + if (ret) { + dev_err(dai->dev, "Unable to configure port\n"); + return ret; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + /* Channel size configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "Channel size unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + max98373->ch_size = snd_pcm_format_width(params_format(params)); + + regmap_update_bits(max98373->regmap, + MAX98373_R2024_PCM_DATA_FMT_CFG, + MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + dev_dbg(component->dev, "Format supported %d", params_format(params)); + + /* Sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_8000; + break; + case 11025: + sampling_rate = MAX98373_PCM_SR_SET1_SR_11025; + break; + case 12000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_12000; + break; + case 16000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_16000; + break; + case 22050: + sampling_rate = MAX98373_PCM_SR_SET1_SR_22050; + break; + case 24000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_24000; + break; + case 32000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_32000; + break; + case 44100: + sampling_rate = MAX98373_PCM_SR_SET1_SR_44100; + break; + case 48000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_48000; + break; + case 88200: + sampling_rate = MAX98373_PCM_SR_SET1_SR_88200; + break; + case 96000: + sampling_rate = MAX98373_PCM_SR_SET1_SR_96000; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set correct sampling frequency */ + regmap_update_bits(max98373->regmap, + MAX98373_R2028_PCM_SR_SETUP_2, + MAX98373_PCM_SR_SET2_SR_MASK, + sampling_rate << MAX98373_PCM_SR_SET2_SR_SHIFT); + + /* set sampling rate of IV */ + regmap_update_bits(max98373->regmap, + MAX98373_R2028_PCM_SR_SETUP_2, + MAX98373_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate); + + return 0; +} + +static int max98373_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98373_priv *max98373 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!max98373->slave) + return -EINVAL; + + sdw_stream_remove_slave(max98373->slave, sdw_stream); + return 0; +} + +static int max98373_set_sdw_stream(struct snd_soc_dai *dai, + void *sdw_stream, int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void max98373_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int max98373_sdw_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct max98373_priv *max98373 = + snd_soc_component_get_drvdata(component); + + /* tx_mask is unused since it's irrelevant for I/V feedback */ + if (tx_mask) + return -EINVAL; + + if (!rx_mask && !slots && !slot_width) + max98373->tdm_mode = false; + else + max98373->tdm_mode = true; + + max98373->rx_mask = rx_mask; + max98373->slot = slots; + + return 0; +} + +static const struct snd_soc_dai_ops max98373_dai_sdw_ops = { + .hw_params = max98373_sdw_dai_hw_params, + .hw_free = max98373_pcm_hw_free, + .set_stream = max98373_set_sdw_stream, + .shutdown = max98373_shutdown, + .set_tdm_slot = max98373_sdw_set_tdm_slot, +}; + +static struct snd_soc_dai_driver max98373_sdw_dai[] = { + { + .name = "max98373-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98373_RATES, + .formats = MAX98373_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98373_RATES, + .formats = MAX98373_FORMATS, + }, + .ops = &max98373_dai_sdw_ops, + } +}; + +static int max98373_init(struct sdw_slave *slave, struct regmap *regmap) +{ + struct max98373_priv *max98373; + int ret; + int i; + struct device *dev = &slave->dev; + + /* Allocate and assign private driver data structure */ + max98373 = devm_kzalloc(dev, sizeof(*max98373), GFP_KERNEL); + if (!max98373) + return -ENOMEM; + + dev_set_drvdata(dev, max98373); + max98373->regmap = regmap; + max98373->slave = slave; + + regcache_cache_only(max98373->regmap, true); + + max98373->cache_num = ARRAY_SIZE(max98373_sdw_cache_reg); + max98373->cache = devm_kcalloc(dev, max98373->cache_num, + sizeof(*max98373->cache), + GFP_KERNEL); + if (!max98373->cache) + return -ENOMEM; + + for (i = 0; i < max98373->cache_num; i++) + max98373->cache[i].reg = max98373_sdw_cache_reg[i]; + + /* Read voltage and slot configuration */ + max98373_slot_config(dev, max98373); + + max98373->hw_init = false; + max98373->first_hw_init = false; + + /* codec registration */ + ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98373_sdw, + max98373_sdw_dai, + ARRAY_SIZE(max98373_sdw_dai)); + if (ret < 0) { + dev_err(dev, "Failed to register codec: %d\n", ret); + return ret; + } + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + return 0; +} + +static int max98373_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct max98373_priv *max98373 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + max98373->hw_init = false; + + /* + * Perform initialization only if slave status is SDW_SLAVE_ATTACHED + */ + if (max98373->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return max98373_io_init(slave); +} + +static int max98373_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + int ret; + + ret = max98373_clock_config(slave, params); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static struct sdw_slave_ops max98373_slave_ops = { + .read_prop = max98373_read_prop, + .update_status = max98373_update_status, + .bus_config = max98373_bus_config, +}; + +static int max98373_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &max98373_sdw_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return max98373_init(slave, regmap); +} + +static int max98373_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id max98373_of_match[] = { + { .compatible = "maxim,max98373", }, + {}, +}; +MODULE_DEVICE_TABLE(of, max98373_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98373_acpi_match[] = { + { "MX98373", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98373_acpi_match); +#endif + +static const struct sdw_device_id max98373_id[] = { + SDW_SLAVE_ENTRY(0x019F, 0x8373, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, max98373_id); + +static struct sdw_driver max98373_sdw_driver = { + .driver = { + .name = "max98373", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max98373_of_match), + .acpi_match_table = ACPI_PTR(max98373_acpi_match), + .pm = &max98373_pm, + }, + .probe = max98373_sdw_probe, + .remove = max98373_sdw_remove, + .ops = &max98373_slave_ops, + .id_table = max98373_id, +}; + +module_sdw_driver(max98373_sdw_driver); + +MODULE_DESCRIPTION("ASoC MAX98373 driver SDW"); +MODULE_AUTHOR("Oleg Sherbakov "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/max98373-sdw.h b/sound/soc/codecs/max98373-sdw.h new file mode 100644 index 0000000000..2d8033515d --- /dev/null +++ b/sound/soc/codecs/max98373-sdw.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2020 Maxim Integrated */ + +#ifndef _MAX98373_SDW_H +#define _MAX98373_SDW_H + +#include "max98373.h" + +/* SoundWire Slave Control Port (SCP) */ +#define MAX98373_R0040_SCP_INIT_STAT_1 0x0040 +#define MAX98373_R0041_SCP_INIT_MASK_1 0x0041 +#define MAX98373_R0042_SCP_INIT_STAT_2 0x0042 +#define MAX98373_R0044_SCP_CTRL 0x0044 +#define MAX98373_R0045_SCP_SYSTEM_CTRL 0x0045 +#define MAX98373_R0046_SCP_DEV_NUMBER 0x0046 +#define MAX98373_R0050_SCP_DEV_ID_0 0x0050 +#define MAX98373_R0051_SCP_DEV_ID_1 0x0051 +#define MAX98373_R0052_SCP_DEV_ID_2 0x0052 +#define MAX98373_R0053_SCP_DEV_ID_3 0x0053 +#define MAX98373_R0054_SCP_DEV_ID_4 0x0054 +#define MAX98373_R0055_SCP_DEV_ID_5 0x0055 +#define MAX98373_R0060_SCP_FRAME_CTLR 0x0060 +#define MAX98373_R0070_SCP_FRAME_CTLR 0x0070 + +/* SoundWire Device Data Port (DP) */ +/* Data Port 1 Registers */ +#define MAX98373_R0100_DP1_INIT_STAT 0x0100 +#define MAX98373_R0101_DP1_INIT_MASK 0x0101 +#define MAX98373_R0102_DP1_PORT_CTRL 0x0102 +#define MAX98373_R0103_DP1_BLOCK_CTRL_1 0x0103 +#define MAX98373_R0104_DP1_PREPARE_STATUS 0x0104 +#define MAX98373_R0105_DP1_PREPARE_CTRL 0x0105 +/* Data Port 1 Bank 0 Registers */ +#define MAX98373_R0120_DP1_CHANNEL_EN 0x0120 +#define MAX98373_R0122_DP1_SAMPLE_CTRL1 0x0122 +#define MAX98373_R0123_DP1_SAMPLE_CTRL2 0x0123 +#define MAX98373_R0124_DP1_OFFSET_CTRL1 0x0124 +#define MAX98373_R0125_DP1_OFFSET_CTRL2 0x0125 +#define MAX98373_R0126_DP1_HCTRL 0x0126 +#define MAX98373_R0127_DP1_BLOCK_CTRL3 0x0127 +/* Data Port 1 Bank 1 Registers */ +#define MAX98373_R0130_DP1_CHANNEL_EN 0x0130 +#define MAX98373_R0132_DP1_SAMPLE_CTRL1 0x0132 +#define MAX98373_R0133_DP1_SAMPLE_CTRL2 0x0133 +#define MAX98373_R0134_DP1_OFFSET_CTRL1 0x0134 +#define MAX98373_R0135_DP1_OFFSET_CTRL2 0x0135 +#define MAX98373_R0136_DP1_HCTRL 0x0136 +#define MAX98373_R0137_DP1_BLOCK_CTRL3 0x0137 +/* Data Port 3 Registers */ +#define MAX98373_R0300_DP3_INIT_STAT 0x0300 +#define MAX98373_R0301_DP3_INIT_MASK 0x0301 +#define MAX98373_R0302_DP3_PORT_CTRL 0x0302 +#define MAX98373_R0303_DP3_BLOCK_CTRL_1 0x0303 +#define MAX98373_R0304_DP3_PREPARE_STATUS 0x0304 +#define MAX98373_R0305_DP3_PREPARE_CTRL 0x0305 +/* Data Port 3 Bank 0 Registers */ +#define MAX98373_R0320_DP3_CHANNEL_EN 0x0320 +#define MAX98373_R0322_DP3_SAMPLE_CTRL1 0x0322 +#define MAX98373_R0323_DP3_SAMPLE_CTRL2 0x0323 +#define MAX98373_R0324_DP3_OFFSET_CTRL1 0x0324 +#define MAX98373_R0325_DP3_OFFSET_CTRL2 0x0325 +#define MAX98373_R0326_DP3_HCTRL 0x0326 +#define MAX98373_R0327_DP3_BLOCK_CTRL3 0x0327 +/* Data Port 3 Bank 1 Registers */ +#define MAX98373_R0330_DP3_CHANNEL_EN 0x0330 +#define MAX98373_R0332_DP3_SAMPLE_CTRL1 0x0332 +#define MAX98373_R0333_DP3_SAMPLE_CTRL2 0x0333 +#define MAX98373_R0334_DP3_OFFSET_CTRL1 0x0334 +#define MAX98373_R0335_DP3_OFFSET_CTRL2 0x0335 +#define MAX98373_R0336_DP3_HCTRL 0x0336 +#define MAX98373_R0337_DP3_BLOCK_CTRL3 0x0337 +#endif diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c new file mode 100644 index 0000000000..fde055c6c8 --- /dev/null +++ b/sound/soc/codecs/max98373.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017, Maxim Integrated + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98373.h" + +static int max98373_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(max98373->regmap, + MAX98373_R20FF_GLOBAL_SHDN, + MAX98373_GLOBAL_EN_MASK, 1); + usleep_range(30000, 31000); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(max98373->regmap, + MAX98373_R20FF_GLOBAL_SHDN, + MAX98373_GLOBAL_EN_MASK, 0); + usleep_range(30000, 31000); + max98373->tdm_mode = false; + break; + default: + return 0; + } + return 0; +} + +static const char * const max98373_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static const struct soc_enum dai_sel_enum = + SOC_ENUM_SINGLE(MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, + MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT, + 3, max98373_switch_text); + +static const struct snd_kcontrol_new max98373_dai_controls = + SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); + +static const struct snd_kcontrol_new max98373_vi_control = + SOC_DAPM_SINGLE("Switch", MAX98373_R202C_PCM_TX_EN, 0, 1, 0); + +static const struct snd_kcontrol_new max98373_spkfb_control = + SOC_DAPM_SINGLE("Switch", MAX98373_R2043_AMP_EN, 1, 1, 0); + +static const struct snd_soc_dapm_widget max98373_dapm_widgets[] = { +SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", + MAX98373_R202B_PCM_RX_EN, 0, 0, max98373_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98373_dai_controls), +SND_SOC_DAPM_OUTPUT("BE_OUT"), +SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, + MAX98373_R2047_IV_SENSE_ADC_EN, 0, 0), +SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, + MAX98373_R2047_IV_SENSE_ADC_EN, 1, 0), +SND_SOC_DAPM_AIF_OUT("Speaker FB Sense", "HiFi Capture", 0, + SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, + &max98373_vi_control), +SND_SOC_DAPM_SWITCH("SpkFB Sense", SND_SOC_NOPM, 0, 0, + &max98373_spkfb_control), +SND_SOC_DAPM_SIGGEN("VMON"), +SND_SOC_DAPM_SIGGEN("IMON"), +SND_SOC_DAPM_SIGGEN("FBMON"), +}; + +static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, -6350, 50, 1); +static const DECLARE_TLV_DB_RANGE(max98373_spk_tlv, + 0, 8, TLV_DB_SCALE_ITEM(0, 50, 0), + 9, 10, TLV_DB_SCALE_ITEM(500, 100, 0), +); +static const DECLARE_TLV_DB_RANGE(max98373_spkgain_max_tlv, + 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0), +); +static const DECLARE_TLV_DB_RANGE(max98373_dht_step_size_tlv, + 0, 1, TLV_DB_SCALE_ITEM(25, 25, 0), + 2, 4, TLV_DB_SCALE_ITEM(100, 100, 0), +); +static const DECLARE_TLV_DB_RANGE(max98373_dht_spkgain_min_tlv, + 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0), +); +static const DECLARE_TLV_DB_RANGE(max98373_dht_rotation_point_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-3000, 500, 0), + 2, 4, TLV_DB_SCALE_ITEM(-2200, 200, 0), + 5, 6, TLV_DB_SCALE_ITEM(-1500, 300, 0), + 7, 9, TLV_DB_SCALE_ITEM(-1000, 200, 0), + 10, 13, TLV_DB_SCALE_ITEM(-500, 100, 0), + 14, 15, TLV_DB_SCALE_ITEM(-100, 50, 0), +); +static const DECLARE_TLV_DB_RANGE(max98373_limiter_thresh_tlv, + 0, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0), +); + +static const DECLARE_TLV_DB_RANGE(max98373_bde_gain_tlv, + 0, 60, TLV_DB_SCALE_ITEM(-1500, 25, 0), +); + +static const char * const max98373_output_voltage_lvl_text[] = { + "5.43V", "6.09V", "6.83V", "7.67V", "8.60V", + "9.65V", "10.83V", "12.15V", "13.63V", "15.29V" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_out_volt_enum, + MAX98373_R203E_AMP_PATH_GAIN, 0, + max98373_output_voltage_lvl_text); + +static const char * const max98373_dht_attack_rate_text[] = { + "17.5us", "35us", "70us", "140us", + "280us", "560us", "1120us", "2240us" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_dht_attack_rate_enum, + MAX98373_R20D2_DHT_ATTACK_CFG, 0, + max98373_dht_attack_rate_text); + +static const char * const max98373_dht_release_rate_text[] = { + "45ms", "225ms", "450ms", "1150ms", + "2250ms", "3100ms", "4500ms", "6750ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_dht_release_rate_enum, + MAX98373_R20D3_DHT_RELEASE_CFG, 0, + max98373_dht_release_rate_text); + +static const char * const max98373_limiter_attack_rate_text[] = { + "10us", "20us", "40us", "80us", + "160us", "320us", "640us", "1.28ms", + "2.56ms", "5.12ms", "10.24ms", "20.48ms", + "40.96ms", "81.92ms", "16.384ms", "32.768ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_limiter_attack_rate_enum, + MAX98373_R20E1_LIMITER_ATK_REL_RATES, 4, + max98373_limiter_attack_rate_text); + +static const char * const max98373_limiter_release_rate_text[] = { + "40us", "80us", "160us", "320us", + "640us", "1.28ms", "2.56ms", "5.120ms", + "10.24ms", "20.48ms", "40.96ms", "81.92ms", + "163.84ms", "327.68ms", "655.36ms", "1310.72ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_limiter_release_rate_enum, + MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0, + max98373_limiter_release_rate_text); + +static const char * const max98373_ADC_samplerate_text[] = { + "333kHz", "192kHz", "64kHz", "48kHz" +}; + +static SOC_ENUM_SINGLE_DECL(max98373_adc_samplerate_enum, + MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0, + max98373_ADC_samplerate_text); + +static int max98373_feedback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component); + int i; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* + * Register values will be cached before suspend. The cached value + * will be a valid value and userspace will happy with that. + */ + for (i = 0; i < max98373->cache_num; i++) { + if (mc->reg == max98373->cache[i].reg) { + ucontrol->value.integer.value[0] = max98373->cache[i].val; + return 0; + } + } + } + + return snd_soc_get_volsw(kcontrol, ucontrol); +} + +static const struct snd_kcontrol_new max98373_snd_controls[] = { +SOC_SINGLE("Digital Vol Sel Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_VOL_SEL_SHIFT, 1, 0), +SOC_SINGLE("Volume Location Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_VOL_SEL_SHIFT, 1, 0), +SOC_SINGLE("Ramp Up Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT, 1, 0), +SOC_SINGLE("Ramp Down Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT, 1, 0), +/* Speaker Amplifier Overcurrent Automatic Restart Enable */ +SOC_SINGLE("OVC Autorestart Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, + MAX98373_OVC_AUTORESTART_SHIFT, 1, 0), +/* Thermal Shutdown Automatic Restart Enable */ +SOC_SINGLE("THERM Autorestart Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, + MAX98373_THERM_AUTORESTART_SHIFT, 1, 0), +/* Clock Monitor Automatic Restart Enable */ +SOC_SINGLE("CMON Autorestart Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, + MAX98373_CMON_AUTORESTART_SHIFT, 1, 0), +SOC_SINGLE("CLK Monitor Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, + MAX98373_CLOCK_MON_SHIFT, 1, 0), +SOC_SINGLE("Dither Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_DSP_CFG_DITH_SHIFT, 1, 0), +SOC_SINGLE("DC Blocker Switch", MAX98373_R203F_AMP_DSP_CFG, + MAX98373_AMP_DSP_CFG_DCBLK_SHIFT, 1, 0), +SOC_SINGLE_TLV("Digital Volume", MAX98373_R203D_AMP_DIG_VOL_CTRL, + 0, 0x7F, 1, max98373_digital_tlv), +SOC_SINGLE_TLV("Speaker Volume", MAX98373_R203E_AMP_PATH_GAIN, + MAX98373_SPK_DIGI_GAIN_SHIFT, 10, 0, max98373_spk_tlv), +SOC_SINGLE_TLV("FS Max Volume", MAX98373_R203E_AMP_PATH_GAIN, + MAX98373_FS_GAIN_MAX_SHIFT, 9, 0, max98373_spkgain_max_tlv), +SOC_ENUM("Output Voltage", max98373_out_volt_enum), +/* Dynamic Headroom Tracking */ +SOC_SINGLE("DHT Switch", MAX98373_R20D4_DHT_EN, + MAX98373_DHT_EN_SHIFT, 1, 0), +SOC_SINGLE_TLV("DHT Min Volume", MAX98373_R20D1_DHT_CFG, + MAX98373_DHT_SPK_GAIN_MIN_SHIFT, 9, 0, max98373_dht_spkgain_min_tlv), +SOC_SINGLE_TLV("DHT Rot Pnt Volume", MAX98373_R20D1_DHT_CFG, + MAX98373_DHT_ROT_PNT_SHIFT, 15, 1, max98373_dht_rotation_point_tlv), +SOC_SINGLE_TLV("DHT Attack Step Volume", MAX98373_R20D2_DHT_ATTACK_CFG, + MAX98373_DHT_ATTACK_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv), +SOC_SINGLE_TLV("DHT Release Step Volume", MAX98373_R20D3_DHT_RELEASE_CFG, + MAX98373_DHT_RELEASE_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv), +SOC_ENUM("DHT Attack Rate", max98373_dht_attack_rate_enum), +SOC_ENUM("DHT Release Rate", max98373_dht_release_rate_enum), +/* ADC configuration */ +SOC_SINGLE("ADC PVDD CH Switch", MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0, 1, 0), +SOC_SINGLE("ADC PVDD FLT Switch", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, + MAX98373_FLT_EN_SHIFT, 1, 0), +SOC_SINGLE("ADC TEMP FLT Switch", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, + MAX98373_FLT_EN_SHIFT, 1, 0), +SOC_SINGLE_EXT("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0, + max98373_feedback_get, NULL), +SOC_SINGLE_EXT("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0, + max98373_feedback_get, NULL), +SOC_SINGLE("ADC PVDD FLT Coeff", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, + 0, 0x3, 0), +SOC_SINGLE("ADC TEMP FLT Coeff", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, + 0, 0x3, 0), +SOC_ENUM("ADC SampleRate", max98373_adc_samplerate_enum), +/* Brownout Detection Engine */ +SOC_SINGLE("BDE Switch", MAX98373_R20B5_BDE_EN, MAX98373_BDE_EN_SHIFT, 1, 0), +SOC_SINGLE("BDE LVL4 Mute Switch", MAX98373_R20B2_BDE_L4_CFG_2, + MAX98373_LVL4_MUTE_EN_SHIFT, 1, 0), +SOC_SINGLE("BDE LVL4 Hold Switch", MAX98373_R20B2_BDE_L4_CFG_2, + MAX98373_LVL4_HOLD_EN_SHIFT, 1, 0), +SOC_SINGLE("BDE LVL1 Thresh", MAX98373_R2097_BDE_L1_THRESH, 0, 0xFF, 0), +SOC_SINGLE("BDE LVL2 Thresh", MAX98373_R2098_BDE_L2_THRESH, 0, 0xFF, 0), +SOC_SINGLE("BDE LVL3 Thresh", MAX98373_R2099_BDE_L3_THRESH, 0, 0xFF, 0), +SOC_SINGLE("BDE LVL4 Thresh", MAX98373_R209A_BDE_L4_THRESH, 0, 0xFF, 0), +SOC_SINGLE_EXT("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0, + max98373_feedback_get, NULL), +SOC_SINGLE("BDE Clip Mode Switch", MAX98373_R2092_BDE_CLIPPER_MODE, 0, 1, 0), +SOC_SINGLE("BDE Thresh Hysteresis", MAX98373_R209B_BDE_THRESH_HYST, 0, 0xFF, 0), +SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0), +SOC_SINGLE("BDE Attack Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 4, 0xF, 0), +SOC_SINGLE("BDE Release Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0, 0xF, 0), +SOC_SINGLE_TLV("BDE LVL1 Clip Thresh Volume", MAX98373_R20A9_BDE_L1_CFG_2, + 0, 0x3C, 1, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL2 Clip Thresh Volume", MAX98373_R20AC_BDE_L2_CFG_2, + 0, 0x3C, 1, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL3 Clip Thresh Volume", MAX98373_R20AF_BDE_L3_CFG_2, + 0, 0x3C, 1, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL4 Clip Thresh Volume", MAX98373_R20B2_BDE_L4_CFG_2, + 0, 0x3C, 1, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL1 Clip Reduction Volume", MAX98373_R20AA_BDE_L1_CFG_3, + 0, 0x3C, 1, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL2 Clip Reduction Volume", MAX98373_R20AD_BDE_L2_CFG_3, + 0, 0x3C, 1, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL3 Clip Reduction Volume", MAX98373_R20B0_BDE_L3_CFG_3, + 0, 0x3C, 1, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL4 Clip Reduction Volume", MAX98373_R20B3_BDE_L4_CFG_3, + 0, 0x3C, 1, max98373_bde_gain_tlv), +SOC_SINGLE_TLV("BDE LVL1 Limiter Thresh Volume", MAX98373_R20A8_BDE_L1_CFG_1, + 0, 0xF, 1, max98373_limiter_thresh_tlv), +SOC_SINGLE_TLV("BDE LVL2 Limiter Thresh Volume", MAX98373_R20AB_BDE_L2_CFG_1, + 0, 0xF, 1, max98373_limiter_thresh_tlv), +SOC_SINGLE_TLV("BDE LVL3 Limiter Thresh Volume", MAX98373_R20AE_BDE_L3_CFG_1, + 0, 0xF, 1, max98373_limiter_thresh_tlv), +SOC_SINGLE_TLV("BDE LVL4 Limiter Thresh Volume", MAX98373_R20B1_BDE_L4_CFG_1, + 0, 0xF, 1, max98373_limiter_thresh_tlv), +/* Limiter */ +SOC_SINGLE("Limiter Switch", MAX98373_R20E2_LIMITER_EN, + MAX98373_LIMITER_EN_SHIFT, 1, 0), +SOC_SINGLE("Limiter Src Switch", MAX98373_R20E0_LIMITER_THRESH_CFG, + MAX98373_LIMITER_THRESH_SRC_SHIFT, 1, 0), +SOC_SINGLE_TLV("Limiter Thresh Volume", MAX98373_R20E0_LIMITER_THRESH_CFG, + MAX98373_LIMITER_THRESH_SHIFT, 15, 0, max98373_limiter_thresh_tlv), +SOC_ENUM("Limiter Attack Rate", max98373_limiter_attack_rate_enum), +SOC_ENUM("Limiter Release Rate", max98373_limiter_release_rate_enum), +}; + +static const struct snd_soc_dapm_route max98373_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, + /* Capture */ + { "VI Sense", "Switch", "VMON" }, + { "VI Sense", "Switch", "IMON" }, + { "SpkFB Sense", "Switch", "FBMON" }, + { "Voltage Sense", NULL, "VI Sense" }, + { "Current Sense", NULL, "VI Sense" }, + { "Speaker FB Sense", NULL, "SpkFB Sense" }, +}; + +void max98373_reset(struct max98373_priv *max98373, struct device *dev) +{ + int ret, reg, count; + + /* Software Reset */ + ret = regmap_update_bits(max98373->regmap, + MAX98373_R2000_SW_RESET, + MAX98373_SOFT_RESET, + MAX98373_SOFT_RESET); + if (ret) + dev_err(dev, "Reset command failed. (ret:%d)\n", ret); + + count = 0; + while (count < 3) { + usleep_range(10000, 11000); + /* Software Reset Verification */ + ret = regmap_read(max98373->regmap, + MAX98373_R21FF_REV_ID, ®); + if (!ret) { + dev_info(dev, "Reset completed (retry:%d)\n", count); + return; + } + count++; + } + dev_err(dev, "Reset failed. (ret:%d)\n", ret); +} +EXPORT_SYMBOL_GPL(max98373_reset); + +static int max98373_probe(struct snd_soc_component *component) +{ + struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component); + + /* Software Reset */ + max98373_reset(max98373, component->dev); + + /* IV default slot configuration */ + regmap_write(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + 0xFF); + regmap_write(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + 0xFF); + /* L/R mix configuration */ + regmap_write(max98373->regmap, + MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, + 0x80); + regmap_write(max98373->regmap, + MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, + 0x1); + /* Enable DC blocker */ + regmap_write(max98373->regmap, + MAX98373_R203F_AMP_DSP_CFG, + 0x3); + /* Enable IMON VMON DC blocker */ + regmap_write(max98373->regmap, + MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, + 0x7); + /* voltage, current slot configuration */ + regmap_write(max98373->regmap, + MAX98373_R2022_PCM_TX_SRC_1, + (max98373->i_slot << MAX98373_PCM_TX_CH_SRC_A_I_SHIFT | + max98373->v_slot) & 0xFF); + if (max98373->v_slot < 8) + regmap_update_bits(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + 1 << max98373->v_slot, 0); + else + regmap_update_bits(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + 1 << (max98373->v_slot - 8), 0); + + if (max98373->i_slot < 8) + regmap_update_bits(max98373->regmap, + MAX98373_R2020_PCM_TX_HIZ_EN_1, + 1 << max98373->i_slot, 0); + else + regmap_update_bits(max98373->regmap, + MAX98373_R2021_PCM_TX_HIZ_EN_2, + 1 << (max98373->i_slot - 8), 0); + + /* enable auto restart function by default */ + regmap_write(max98373->regmap, + MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, + 0xF); + + /* speaker feedback slot configuration */ + regmap_write(max98373->regmap, + MAX98373_R2023_PCM_TX_SRC_2, + max98373->spkfb_slot & 0xFF); + + /* Set interleave mode */ + if (max98373->interleave_mode) + regmap_update_bits(max98373->regmap, + MAX98373_R2024_PCM_DATA_FMT_CFG, + MAX98373_PCM_TX_CH_INTERLEAVE_MASK, + MAX98373_PCM_TX_CH_INTERLEAVE_MASK); + + /* Speaker enable */ + regmap_update_bits(max98373->regmap, + MAX98373_R2043_AMP_EN, + MAX98373_SPK_EN_MASK, 1); + + return 0; +} + +const struct snd_soc_component_driver soc_codec_dev_max98373 = { + .probe = max98373_probe, + .controls = max98373_snd_controls, + .num_controls = ARRAY_SIZE(max98373_snd_controls), + .dapm_widgets = max98373_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98373_dapm_widgets), + .dapm_routes = max98373_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98373_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_max98373); + +static int max98373_sdw_probe(struct snd_soc_component *component) +{ + int ret; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +const struct snd_soc_component_driver soc_codec_dev_max98373_sdw = { + .probe = max98373_sdw_probe, + .controls = max98373_snd_controls, + .num_controls = ARRAY_SIZE(max98373_snd_controls), + .dapm_widgets = max98373_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98373_dapm_widgets), + .dapm_routes = max98373_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98373_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_max98373_sdw); + +void max98373_slot_config(struct device *dev, + struct max98373_priv *max98373) +{ + int value; + + if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value)) + max98373->v_slot = value & 0xF; + else + max98373->v_slot = 0; + + if (!device_property_read_u32(dev, "maxim,imon-slot-no", &value)) + max98373->i_slot = value & 0xF; + else + max98373->i_slot = 1; + if (dev->of_node) { + max98373->reset_gpio = of_get_named_gpio(dev->of_node, + "maxim,reset-gpio", 0); + if (!gpio_is_valid(max98373->reset_gpio)) { + dev_err(dev, "Looking up %s property in node %s failed %d\n", + "maxim,reset-gpio", dev->of_node->full_name, + max98373->reset_gpio); + } else { + dev_dbg(dev, "maxim,reset-gpio=%d", + max98373->reset_gpio); + } + } else { + /* this makes reset_gpio as invalid */ + max98373->reset_gpio = -1; + } + + if (!device_property_read_u32(dev, "maxim,spkfb-slot-no", &value)) + max98373->spkfb_slot = value & 0xF; + else + max98373->spkfb_slot = 2; +} +EXPORT_SYMBOL_GPL(max98373_slot_config); + +MODULE_DESCRIPTION("ALSA SoC MAX98373 driver"); +MODULE_AUTHOR("Ryan Lee "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98373.h b/sound/soc/codecs/max98373.h new file mode 100644 index 0000000000..e1810b3b16 --- /dev/null +++ b/sound/soc/codecs/max98373.h @@ -0,0 +1,240 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2017 Maxim Integrated */ + +#ifndef _MAX98373_H +#define _MAX98373_H + +#define MAX98373_R2000_SW_RESET 0x2000 +#define MAX98373_R2001_INT_RAW1 0x2001 +#define MAX98373_R2002_INT_RAW2 0x2002 +#define MAX98373_R2003_INT_RAW3 0x2003 +#define MAX98373_R2004_INT_STATE1 0x2004 +#define MAX98373_R2005_INT_STATE2 0x2005 +#define MAX98373_R2006_INT_STATE3 0x2006 +#define MAX98373_R2007_INT_FLAG1 0x2007 +#define MAX98373_R2008_INT_FLAG2 0x2008 +#define MAX98373_R2009_INT_FLAG3 0x2009 +#define MAX98373_R200A_INT_EN1 0x200A +#define MAX98373_R200B_INT_EN2 0x200B +#define MAX98373_R200C_INT_EN3 0x200C +#define MAX98373_R200D_INT_FLAG_CLR1 0x200D +#define MAX98373_R200E_INT_FLAG_CLR2 0x200E +#define MAX98373_R200F_INT_FLAG_CLR3 0x200F +#define MAX98373_R2010_IRQ_CTRL 0x2010 +#define MAX98373_R2014_THERM_WARN_THRESH 0x2014 +#define MAX98373_R2015_THERM_SHDN_THRESH 0x2015 +#define MAX98373_R2016_THERM_HYSTERESIS 0x2016 +#define MAX98373_R2017_THERM_FOLDBACK_SET 0x2017 +#define MAX98373_R2018_THERM_FOLDBACK_EN 0x2018 +#define MAX98373_R201E_PIN_DRIVE_STRENGTH 0x201E +#define MAX98373_R2020_PCM_TX_HIZ_EN_1 0x2020 +#define MAX98373_R2021_PCM_TX_HIZ_EN_2 0x2021 +#define MAX98373_R2022_PCM_TX_SRC_1 0x2022 +#define MAX98373_R2023_PCM_TX_SRC_2 0x2023 +#define MAX98373_R2024_PCM_DATA_FMT_CFG 0x2024 +#define MAX98373_R2025_AUDIO_IF_MODE 0x2025 +#define MAX98373_R2026_PCM_CLOCK_RATIO 0x2026 +#define MAX98373_R2027_PCM_SR_SETUP_1 0x2027 +#define MAX98373_R2028_PCM_SR_SETUP_2 0x2028 +#define MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 0x2029 +#define MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2 0x202A +#define MAX98373_R202B_PCM_RX_EN 0x202B +#define MAX98373_R202C_PCM_TX_EN 0x202C +#define MAX98373_R202E_ICC_RX_CH_EN_1 0x202E +#define MAX98373_R202F_ICC_RX_CH_EN_2 0x202F +#define MAX98373_R2030_ICC_TX_HIZ_EN_1 0x2030 +#define MAX98373_R2031_ICC_TX_HIZ_EN_2 0x2031 +#define MAX98373_R2032_ICC_LINK_EN_CFG 0x2032 +#define MAX98373_R2034_ICC_TX_CNTL 0x2034 +#define MAX98373_R2035_ICC_TX_EN 0x2035 +#define MAX98373_R2036_SOUNDWIRE_CTRL 0x2036 +#define MAX98373_R203D_AMP_DIG_VOL_CTRL 0x203D +#define MAX98373_R203E_AMP_PATH_GAIN 0x203E +#define MAX98373_R203F_AMP_DSP_CFG 0x203F +#define MAX98373_R2040_TONE_GEN_CFG 0x2040 +#define MAX98373_R2041_AMP_CFG 0x2041 +#define MAX98373_R2042_AMP_EDGE_RATE_CFG 0x2042 +#define MAX98373_R2043_AMP_EN 0x2043 +#define MAX98373_R2046_IV_SENSE_ADC_DSP_CFG 0x2046 +#define MAX98373_R2047_IV_SENSE_ADC_EN 0x2047 +#define MAX98373_R2051_MEAS_ADC_SAMPLING_RATE 0x2051 +#define MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG 0x2052 +#define MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG 0x2053 +#define MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK 0x2054 +#define MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK 0x2055 +#define MAX98373_R2056_MEAS_ADC_PVDD_CH_EN 0x2056 +#define MAX98373_R2090_BDE_LVL_HOLD 0x2090 +#define MAX98373_R2091_BDE_GAIN_ATK_REL_RATE 0x2091 +#define MAX98373_R2092_BDE_CLIPPER_MODE 0x2092 +#define MAX98373_R2097_BDE_L1_THRESH 0x2097 +#define MAX98373_R2098_BDE_L2_THRESH 0x2098 +#define MAX98373_R2099_BDE_L3_THRESH 0x2099 +#define MAX98373_R209A_BDE_L4_THRESH 0x209A +#define MAX98373_R209B_BDE_THRESH_HYST 0x209B +#define MAX98373_R20A8_BDE_L1_CFG_1 0x20A8 +#define MAX98373_R20A9_BDE_L1_CFG_2 0x20A9 +#define MAX98373_R20AA_BDE_L1_CFG_3 0x20AA +#define MAX98373_R20AB_BDE_L2_CFG_1 0x20AB +#define MAX98373_R20AC_BDE_L2_CFG_2 0x20AC +#define MAX98373_R20AD_BDE_L2_CFG_3 0x20AD +#define MAX98373_R20AE_BDE_L3_CFG_1 0x20AE +#define MAX98373_R20AF_BDE_L3_CFG_2 0x20AF +#define MAX98373_R20B0_BDE_L3_CFG_3 0x20B0 +#define MAX98373_R20B1_BDE_L4_CFG_1 0x20B1 +#define MAX98373_R20B2_BDE_L4_CFG_2 0x20B2 +#define MAX98373_R20B3_BDE_L4_CFG_3 0x20B3 +#define MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE 0x20B4 +#define MAX98373_R20B5_BDE_EN 0x20B5 +#define MAX98373_R20B6_BDE_CUR_STATE_READBACK 0x20B6 +#define MAX98373_R20D1_DHT_CFG 0x20D1 +#define MAX98373_R20D2_DHT_ATTACK_CFG 0x20D2 +#define MAX98373_R20D3_DHT_RELEASE_CFG 0x20D3 +#define MAX98373_R20D4_DHT_EN 0x20D4 +#define MAX98373_R20E0_LIMITER_THRESH_CFG 0x20E0 +#define MAX98373_R20E1_LIMITER_ATK_REL_RATES 0x20E1 +#define MAX98373_R20E2_LIMITER_EN 0x20E2 +#define MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG 0x20FE +#define MAX98373_R20FF_GLOBAL_SHDN 0x20FF +#define MAX98373_R21FF_REV_ID 0x21FF + +/* MAX98373_R2022_PCM_TX_SRC_1 */ +#define MAX98373_PCM_TX_CH_SRC_A_V_SHIFT (0) +#define MAX98373_PCM_TX_CH_SRC_A_I_SHIFT (4) + +/* MAX98373_R2024_PCM_DATA_FMT_CFG */ +#define MAX98373_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98373_PCM_MODE_CFG_FORMAT_SHIFT (3) +#define MAX98373_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2) +#define MAX98373_PCM_FORMAT_I2S (0x0 << 0) +#define MAX98373_PCM_FORMAT_LJ (0x1 << 0) +#define MAX98373_PCM_FORMAT_TDM_MODE0 (0x3 << 0) +#define MAX98373_PCM_FORMAT_TDM_MODE1 (0x4 << 0) +#define MAX98373_PCM_FORMAT_TDM_MODE2 (0x5 << 0) +#define MAX98373_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98373_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98373_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98373_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) + +/* MAX98373_R2026_PCM_CLOCK_RATIO */ +#define MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4) +#define MAX98373_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) + +/* MAX98373_R2027_PCM_SR_SETUP_1 */ +#define MAX98373_PCM_SR_SET1_SR_MASK (0xF << 0) +#define MAX98373_PCM_SR_SET1_SR_8000 (0x0 << 0) +#define MAX98373_PCM_SR_SET1_SR_11025 (0x1 << 0) +#define MAX98373_PCM_SR_SET1_SR_12000 (0x2 << 0) +#define MAX98373_PCM_SR_SET1_SR_16000 (0x3 << 0) +#define MAX98373_PCM_SR_SET1_SR_22050 (0x4 << 0) +#define MAX98373_PCM_SR_SET1_SR_24000 (0x5 << 0) +#define MAX98373_PCM_SR_SET1_SR_32000 (0x6 << 0) +#define MAX98373_PCM_SR_SET1_SR_44100 (0x7 << 0) +#define MAX98373_PCM_SR_SET1_SR_48000 (0x8 << 0) +#define MAX98373_PCM_SR_SET1_SR_88200 (0x9 << 0) +#define MAX98373_PCM_SR_SET1_SR_96000 (0xA << 0) + +/* MAX98373_R2028_PCM_SR_SETUP_2 */ +#define MAX98373_PCM_SR_SET2_SR_MASK (0xF << 4) +#define MAX98373_PCM_SR_SET2_SR_SHIFT (4) +#define MAX98373_PCM_SR_SET2_IVADC_SR_MASK (0xF << 0) + +/* MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 */ +#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6) +#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6) +#define MAX98373_PCM_TO_SPK_CH0_SRC_MASK (0xF << 0) + +/* MAX98373_R203E_AMP_PATH_GAIN */ +#define MAX98373_SPK_DIGI_GAIN_MASK (0xF << 4) +#define MAX98373_SPK_DIGI_GAIN_SHIFT (4) +#define MAX98373_FS_GAIN_MAX_MASK (0xF << 0) +#define MAX98373_FS_GAIN_MAX_SHIFT (0) + +/* MAX98373_R203F_AMP_DSP_CFG */ +#define MAX98373_AMP_DSP_CFG_DCBLK_SHIFT (0) +#define MAX98373_AMP_DSP_CFG_DITH_SHIFT (1) +#define MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT (2) +#define MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT (3) +#define MAX98373_AMP_DSP_CFG_DAC_INV_SHIFT (5) +#define MAX98373_AMP_VOL_SEL_SHIFT (7) + +/* MAX98373_R2043_AMP_EN */ +#define MAX98373_SPKFB_EN_MASK (0x1 << 1) +#define MAX98373_SPK_EN_MASK (0x1 << 0) +#define MAX98373_SPKFB_EN_SHIFT (1) + +/*MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG */ +#define MAX98373_FLT_EN_SHIFT (4) + +/* MAX98373_R20B2_BDE_L4_CFG_2 */ +#define MAX98373_LVL4_MUTE_EN_SHIFT (7) +#define MAX98373_LVL4_HOLD_EN_SHIFT (6) + +/* MAX98373_R20B5_BDE_EN */ +#define MAX98373_BDE_EN_SHIFT (0) + +/* MAX98373_R20D1_DHT_CFG */ +#define MAX98373_DHT_SPK_GAIN_MIN_SHIFT (4) +#define MAX98373_DHT_ROT_PNT_SHIFT (0) + +/* MAX98373_R20D2_DHT_ATTACK_CFG */ +#define MAX98373_DHT_ATTACK_STEP_SHIFT (3) +#define MAX98373_DHT_ATTACK_RATE_SHIFT (0) + +/* MAX98373_R20D3_DHT_RELEASE_CFG */ +#define MAX98373_DHT_RELEASE_STEP_SHIFT (3) +#define MAX98373_DHT_RELEASE_RATE_SHIFT (0) + +/* MAX98373_R20D4_DHT_EN */ +#define MAX98373_DHT_EN_SHIFT (0) + +/* MAX98373_R20E0_LIMITER_THRESH_CFG */ +#define MAX98373_LIMITER_THRESH_SHIFT (2) +#define MAX98373_LIMITER_THRESH_SRC_SHIFT (0) + +/* MAX98373_R20E2_LIMITER_EN */ +#define MAX98373_LIMITER_EN_SHIFT (0) + +/* MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG */ +#define MAX98373_OVC_AUTORESTART_SHIFT (3) +#define MAX98373_THERM_AUTORESTART_SHIFT (2) +#define MAX98373_CMON_AUTORESTART_SHIFT (1) +#define MAX98373_CLOCK_MON_SHIFT (0) + +/* MAX98373_R20FF_GLOBAL_SHDN */ +#define MAX98373_GLOBAL_EN_MASK (0x1 << 0) + +/* MAX98373_R2000_SW_RESET */ +#define MAX98373_SOFT_RESET (0x1 << 0) + +struct max98373_cache { + u32 reg; + u32 val; +}; + +struct max98373_priv { + struct regmap *regmap; + int reset_gpio; + unsigned int v_slot; + unsigned int i_slot; + unsigned int spkfb_slot; + bool interleave_mode; + unsigned int ch_size; + bool tdm_mode; + /* cache for reading a valid fake feedback value */ + struct max98373_cache *cache; + int cache_num; + /* variables to support soundwire */ + struct sdw_slave *slave; + bool hw_init; + bool first_hw_init; + int slot; + unsigned int rx_mask; +}; + +extern const struct snd_soc_component_driver soc_codec_dev_max98373; +extern const struct snd_soc_component_driver soc_codec_dev_max98373_sdw; + +void max98373_reset(struct max98373_priv *max98373, struct device *dev); +void max98373_slot_config(struct device *dev, + struct max98373_priv *max98373); +#endif diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c new file mode 100644 index 0000000000..cde5e85946 --- /dev/null +++ b/sound/soc/codecs/max98388.c @@ -0,0 +1,1013 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, Analog Devices Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98388.h" + +static struct reg_default max98388_reg[] = { + {MAX98388_R2000_SW_RESET, 0x00}, + {MAX98388_R2001_INT_RAW1, 0x00}, + {MAX98388_R2002_INT_RAW2, 0x00}, + {MAX98388_R2004_INT_STATE1, 0x00}, + {MAX98388_R2005_INT_STATE2, 0x00}, + {MAX98388_R2020_THERM_WARN_THRESH, 0x0A}, + {MAX98388_R2031_SPK_MON_THRESH, 0x58}, + {MAX98388_R2032_SPK_MON_LD_SEL, 0x08}, + {MAX98388_R2033_SPK_MON_DURATION, 0x02}, + {MAX98388_R2037_ERR_MON_CTRL, 0x01}, + {MAX98388_R2040_PCM_MODE_CFG, 0xC0}, + {MAX98388_R2041_PCM_CLK_SETUP, 0x04}, + {MAX98388_R2042_PCM_SR_SETUP, 0x88}, + {MAX98388_R2044_PCM_TX_CTRL1, 0x00}, + {MAX98388_R2045_PCM_TX_CTRL2, 0x00}, + {MAX98388_R2050_PCM_TX_HIZ_CTRL1, 0xFF}, + {MAX98388_R2051_PCM_TX_HIZ_CTRL2, 0xFF}, + {MAX98388_R2052_PCM_TX_HIZ_CTRL3, 0xFF}, + {MAX98388_R2053_PCM_TX_HIZ_CTRL4, 0xFF}, + {MAX98388_R2054_PCM_TX_HIZ_CTRL5, 0xFF}, + {MAX98388_R2055_PCM_TX_HIZ_CTRL6, 0xFF}, + {MAX98388_R2056_PCM_TX_HIZ_CTRL7, 0xFF}, + {MAX98388_R2057_PCM_TX_HIZ_CTRL8, 0xFF}, + {MAX98388_R2058_PCM_RX_SRC1, 0x00}, + {MAX98388_R2059_PCM_RX_SRC2, 0x01}, + {MAX98388_R205C_PCM_TX_DRIVE_STRENGTH, 0x00}, + {MAX98388_R205D_PCM_TX_SRC_EN, 0x00}, + {MAX98388_R205E_PCM_RX_EN, 0x00}, + {MAX98388_R205F_PCM_TX_EN, 0x00}, + {MAX98388_R2090_SPK_CH_VOL_CTRL, 0x00}, + {MAX98388_R2091_SPK_CH_CFG, 0x02}, + {MAX98388_R2092_SPK_AMP_OUT_CFG, 0x03}, + {MAX98388_R2093_SPK_AMP_SSM_CFG, 0x01}, + {MAX98388_R2094_SPK_AMP_ER_CTRL, 0x00}, + {MAX98388_R209E_SPK_CH_PINK_NOISE_EN, 0x00}, + {MAX98388_R209F_SPK_CH_AMP_EN, 0x00}, + {MAX98388_R20A0_IV_DATA_DSP_CTRL, 0x10}, + {MAX98388_R20A7_IV_DATA_EN, 0x00}, + {MAX98388_R20E0_BP_ALC_THRESH, 0x04}, + {MAX98388_R20E1_BP_ALC_RATES, 0x20}, + {MAX98388_R20E2_BP_ALC_ATTEN, 0x06}, + {MAX98388_R20E3_BP_ALC_REL, 0x02}, + {MAX98388_R20E4_BP_ALC_MUTE, 0x33}, + {MAX98388_R20EE_BP_INF_HOLD_REL, 0x00}, + {MAX98388_R20EF_BP_ALC_EN, 0x00}, + {MAX98388_R210E_AUTO_RESTART, 0x00}, + {MAX98388_R210F_GLOBAL_EN, 0x00}, + {MAX98388_R22FF_REV_ID, 0x00}, +}; + +static int max98388_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 1); + usleep_range(30000, 31000); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 0); + usleep_range(30000, 31000); + max98388->tdm_mode = false; + break; + default: + return 0; + } + return 0; +} + +static const char * const max98388_monomix_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static const struct soc_enum dai_sel_enum = + SOC_ENUM_SINGLE(MAX98388_R2058_PCM_RX_SRC1, + MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT, + 3, max98388_monomix_switch_text); + +static const struct snd_kcontrol_new max98388_dai_controls = + SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); + +static const struct snd_kcontrol_new max98388_vi_control = + SOC_DAPM_SINGLE("Switch", MAX98388_R205F_PCM_TX_EN, 0, 1, 0); + +static const struct snd_soc_dapm_widget max98388_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", + MAX98388_R205E_PCM_RX_EN, 0, 0, max98388_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98388_dai_controls), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, + MAX98388_R20A7_IV_DATA_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, + MAX98388_R20A7_IV_DATA_EN, 1, 0), + SND_SOC_DAPM_ADC("ADC Voltage", NULL, + MAX98388_R205D_PCM_TX_SRC_EN, 0, 0), + SND_SOC_DAPM_ADC("ADC Current", NULL, + MAX98388_R205D_PCM_TX_SRC_EN, 1, 0), + SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, + &max98388_vi_control), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON"), +}; + +static DECLARE_TLV_DB_SCALE(max98388_digital_tlv, -6350, 50, 1); +static DECLARE_TLV_DB_SCALE(max98388_amp_gain_tlv, -300, 300, 0); + +static const char * const max98388_alc_max_atten_text[] = { + "0dBFS", "-1dBFS", "-2dBFS", "-3dBFS", "-4dBFS", "-5dBFS", + "-6dBFS", "-7dBFS", "-8dBFS", "-9dBFS", "-10dBFS", "-11dBFS", + "-12dBFS", "-13dBFS", "-14dBFS", "-15dBFS" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_max_atten_enum, + MAX98388_R20E2_BP_ALC_ATTEN, + MAX98388_ALC_MAX_ATTEN_SHIFT, + max98388_alc_max_atten_text); + +static const char * const max98388_thermal_warn_text[] = { + "95C", "105C", "115C", "125C" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_thermal_warning_thresh_enum, + MAX98388_R2020_THERM_WARN_THRESH, + MAX98388_THERM_WARN_THRESH_SHIFT, + max98388_thermal_warn_text); + +static const char * const max98388_thermal_shutdown_text[] = { + "135C", "145C", "155C", "165C" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_thermal_shutdown_thresh_enum, + MAX98388_R2020_THERM_WARN_THRESH, + MAX98388_THERM_SHDN_THRESH_SHIFT, + max98388_thermal_shutdown_text); + +static const char * const max98388_alc_thresh_single_text[] = { + "3.625V", "3.550V", "3.475V", "3.400V", "3.325V", "3.250V", + "3.175V", "3.100V", "3.025V", "2.950V", "2.875V", "2.800V", + "2.725V", "2.650V", "2.575V", "2.500V" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_thresh_single_enum, + MAX98388_R20E0_BP_ALC_THRESH, + MAX98388_ALC_THRESH_SHIFT, + max98388_alc_thresh_single_text); + +static const char * const max98388_alc_attack_rate_text[] = { + "0", "10us", "20us", "40us", "80us", "160us", + "320us", "640us", "1.28ms", "2.56ms", "5.12ms", "10.24ms", + "20.48ms", "40.96ms", "81.92ms", "163.84ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_attack_rate_enum, + MAX98388_R20E1_BP_ALC_RATES, + MAX98388_ALC_ATTACK_RATE_SHIFT, + max98388_alc_attack_rate_text); + +static const char * const max98388_alc_release_rate_text[] = { + "20us", "40us", "80us", "160us", "320us", "640us", + "1.28ms", "2.56ms", "5.12ms", "10.24ms", "20.48ms", "40.96ms", + "81.92ms", "163.84ms", "327.68ms", "655.36ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_release_rate_enum, + MAX98388_R20E1_BP_ALC_RATES, + MAX98388_ALC_RELEASE_RATE_SHIFT, + max98388_alc_release_rate_text); + +static const char * const max98388_alc_debounce_text[] = { + "0.01ms", "0.1ms", "1ms", "10ms", "100ms", "250ms", "500ms", "hold" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_debouce_enum, + MAX98388_R20E3_BP_ALC_REL, + MAX98388_ALC_DEBOUNCE_TIME_SHIFT, + max98388_alc_debounce_text); + +static const char * const max98388_alc_mute_delay_text[] = { + "0.01ms", "0.05ms", "0.1ms", "0.5ms", "1ms", "5ms", "25ms", "250ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_mute_delay_enum, + MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_DELAY_SHIFT, + max98388_alc_mute_delay_text); + +static const char * const max98388_spkmon_duration_text[] = { + "10ms", "25ms", "50ms", "75ms", "100ms", "200ms", "300ms", "400ms", + "500ms", "600ms", "700ms", "800ms", "900ms", "1000ms", "1100ms", "1200ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_duration_enum, + MAX98388_R2033_SPK_MON_DURATION, + MAX98388_SPKMON_DURATION_SHIFT, + max98388_spkmon_duration_text); + +static const char * const max98388_spkmon_thresh_text[] = { + "0.03V", "0.06V", "0.09V", "0.12V", "0.15V", "0.18V", "0.20V", "0.23V", + "0.26V", "0.29V", "0.32V", "0.35V", "0.38V", "0.41V", "0.44V", "0.47V", + "0.50V", "0.53V", "0.56V", "0.58V", "0.61V", "0.64V", "0.67V", "0.70V", + "0.73V", "0.76V", "0.79V", "0.82V", "0.85V", "0.88V", "0.91V", "0.94V", + "0.96V", "0.99V", "1.02V", "1.05V", "1.08V", "1.11V", "1.14V", "1.17V", + "1.20V", "1.23V", "1.26V", "1.29V", "1.32V", "1.35V", "1.37V", "1.40V", + "1.43V", "1.46V", "1.49V", "1.52V", "1.55V", "1.58V", "1.61V", "1.64V", + "1.67V", "1.70V", "1.73V", "1.75V", "1.78V", "1.81V", "1.84V", "1.87V", + "1.90V", "1.93V", "1.96V", "1.99V", "2.02V", "2.05V", "2.08V", "2.11V", + "2.13V", "2.16V", "2.19V", "2.22V", "2.25V", "2.28V", "2.31V", "2.34V", + "2.37V", "2.40V", "2.43V", "2.46V", "2.49V", "2.51V", "2.54V", "2.57V", + "2.60V", "2.63V", "2.66V", "2.69V", "2.72V", "2.75V", "2.78V", "2.81V", + "2.84V", "2.87V", "2.89V", "2.92V", "2.95V", "2.98V", "3.01V", "3.04V", + "3.07V", "3.10V", "3.13V", "3.16V", "3.19V", "3.22V", "3.25V", "3.27V", + "3.30V", "3.33V", "3.36V", "3.39V", "3.42V", "3.45V", "3.48V", "3.51V", + "3.54V", "3.57V", "3.60V", "3.63V", "3.66V", "3.68V", "3.71V", "3.74V" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_thresh_enum, + MAX98388_R2031_SPK_MON_THRESH, + MAX98388_SPKMON_THRESH_SHIFT, + max98388_spkmon_thresh_text); + +static const char * const max98388_spkmon_load_text[] = { + "2.00ohm", "2.25ohm", "2.50ohm", "2.75ohm", "3.00ohm", "3.25ohm", + "3.50ohm", "3.75ohm", "4.00ohm", "4.25ohm", "4.50ohm", "4.75ohm", + "5.00ohm", "5.25ohm", "5.50ohm", "5.75ohm", "6.00ohm", "6.25ohm", + "6.50ohm", "6.75ohm", "7.00ohm", "7.25ohm", "7.50ohm", "7.75ohm", + "8.00ohm", "8.25ohm", "8.50ohm", "8.75ohm", "9.00ohm", "9.25ohm", + "9.50ohm", "9.75ohm", "10.00ohm", "10.25ohm", "10.50ohm", "10.75ohm", + "11.00ohm", "11.25ohm", "11.50ohm", "11.75ohm", "12.00ohm", "12.25ohm", + "12.50ohm", "12.75ohm", "13.00ohm", "13.25ohm", "13.50ohm", "13.75ohm", + "14.00ohm", "14.25ohm", "14.50ohm", "14.75ohm", "15.00ohm", "15.25ohm", + "15.50ohm", "15.75ohm", "16.00ohm", "16.25ohm", "16.50ohm", "16.75ohm", + "17.00ohm", "17.25ohm", "17.50ohm", "17.75ohm", "18.00ohm", "18.25ohm", + "18.50ohm", "18.75ohm", "19.00ohm", "19.25ohm", "19.50ohm", "19.75ohm", + "20.00ohm", "20.25ohm", "20.50ohm", "20.75ohm", "21.00ohm", "21.25ohm", + "21.50ohm", "21.75ohm", "22.00ohm", "22.25ohm", "22.50ohm", "22.75ohm", + "23.00ohm", "23.25ohm", "23.50ohm", "23.75ohm", "24.00ohm", "24.25ohm", + "24.50ohm", "24.75ohm", "25.00ohm", "25.25ohm", "25.50ohm", "25.75ohm", + "26.00ohm", "26.25ohm", "26.50ohm", "26.75ohm", "27.00ohm", "27.25ohm", + "27.50ohm", "27.75ohm", "28.00ohm", "28.25ohm", "28.50ohm", "28.75ohm", + "29.00ohm", "29.25ohm", "29.50ohm", "29.75ohm", "30.00ohm", "30.25ohm", + "30.50ohm", "30.75ohm", "31.00ohm", "31.25ohm", "31.50ohm", "31.75ohm", + "32.00ohm", "32.25ohm", "32.50ohm", "32.75ohm", "33.00ohm", "33.25ohm", + "33.50ohm", "33.75ohm" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_load_enum, + MAX98388_R2032_SPK_MON_LD_SEL, + MAX98388_SPKMON_LOAD_SHIFT, + max98388_spkmon_load_text); + +static const char * const max98388_edge_rate_text[] = { + "Normal", "Reduced", "Maximum", "Increased", +}; + +static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_falling_enum, + MAX98388_R2094_SPK_AMP_ER_CTRL, + MAX98388_EDGE_RATE_FALL_SHIFT, + max98388_edge_rate_text); + +static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_rising_enum, + MAX98388_R2094_SPK_AMP_ER_CTRL, + MAX98388_EDGE_RATE_RISE_SHIFT, + max98388_edge_rate_text); + +static const char * const max98388_ssm_mod_text[] = { + "1.5%", "3.0%", "4.5%", "6.0%", +}; + +static SOC_ENUM_SINGLE_DECL(max98388_ssm_mod_enum, + MAX98388_R2093_SPK_AMP_SSM_CFG, + MAX98388_SPK_AMP_SSM_MOD_SHIFT, + max98388_ssm_mod_text); + +static const struct snd_kcontrol_new max98388_snd_controls[] = { + SOC_SINGLE("Ramp Up Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_VOL_RMPUP_SHIFT, 1, 0), + SOC_SINGLE("Ramp Down Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_VOL_RMPDN_SHIFT, 1, 0), + /* Two Cell Mode Enable */ + SOC_SINGLE("OP Mode Switch", MAX98388_R2092_SPK_AMP_OUT_CFG, + MAX98388_SPK_AMP_OUT_MODE_SHIFT, 1, 0), + /* Speaker Amplifier Overcurrent Automatic Restart Enable */ + SOC_SINGLE("OVC Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_OVC_AUTORESTART_SHIFT, 1, 0), + /* Thermal Shutdown Automatic Restart Enable */ + SOC_SINGLE("THERM Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_THERM_AUTORESTART_SHIFT, 1, 0), + /* PVDD UVLO Auto Restart */ + SOC_SINGLE("UVLO Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_PVDD_UVLO_AUTORESTART_SHIFT, 1, 0), + /* Clock Monitor Automatic Restart Enable */ + SOC_SINGLE("CMON Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_CMON_AUTORESTART_SHIFT, 1, 0), + SOC_SINGLE("CLK Monitor Switch", MAX98388_R2037_ERR_MON_CTRL, + MAX98388_CLOCK_MON_SHIFT, 1, 0), + /* Pinknoise Generator Enable */ + SOC_SINGLE("Pinknoise Gen Switch", MAX98388_R209E_SPK_CH_PINK_NOISE_EN, + MAX98388_PINK_NOISE_GEN_SHIFT, 1, 0), + /* Dither Enable */ + SOC_SINGLE("Dither Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_DITH_EN_SHIFT, 1, 0), + SOC_SINGLE("VI Dither Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_DITH_SHIFT, 1, 0), + /* DC Blocker Enable */ + SOC_SINGLE("DC Blocker Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_DCBLK_SHIFT, 1, 0), + SOC_SINGLE("Voltage DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT, 1, 0), + SOC_SINGLE("Current DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT, 1, 0), + /* Digital Volume */ + SOC_SINGLE_TLV("Digital Volume", MAX98388_R2090_SPK_CH_VOL_CTRL, + 0, 0x7F, 1, max98388_digital_tlv), + /* Speaker Volume */ + SOC_SINGLE_TLV("Speaker Volume", MAX98388_R2092_SPK_AMP_OUT_CFG, + 0, 5, 0, max98388_amp_gain_tlv), + SOC_ENUM("Thermal Warn Thresh", max98388_thermal_warning_thresh_enum), + SOC_ENUM("Thermal SHDN Thresh", max98388_thermal_shutdown_thresh_enum), + /* Brownout Protection Automatic Level Control */ + SOC_SINGLE("ALC Switch", MAX98388_R20EF_BP_ALC_EN, 0, 1, 0), + SOC_ENUM("ALC Thresh", max98388_alc_thresh_single_enum), + SOC_ENUM("ALC Attack Rate", max98388_alc_attack_rate_enum), + SOC_ENUM("ALC Release Rate", max98388_alc_release_rate_enum), + SOC_ENUM("ALC Max Atten", max98388_alc_max_atten_enum), + SOC_ENUM("ALC Debounce Time", max98388_alc_debouce_enum), + SOC_SINGLE("ALC Unmute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Mute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_RAMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Mute Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_EN_SHIFT, 1, 0), + SOC_ENUM("ALC Mute Delay", max98388_alc_mute_delay_enum), + /* Speaker Monitor */ + SOC_SINGLE("SPKMON Switch", MAX98388_R2037_ERR_MON_CTRL, + MAX98388_SPK_MON_SHIFT, 1, 0), + SOC_ENUM("SPKMON Thresh", max98388_spkmon_thresh_enum), + SOC_ENUM("SPKMON Load", max98388_spkmon_load_enum), + SOC_ENUM("SPKMON Duration", max98388_spkmon_duration_enum), + /* General Parameters */ + SOC_ENUM("Fall Slew Rate", max98388_edge_rate_falling_enum), + SOC_ENUM("Rise Slew Rate", max98388_edge_rate_rising_enum), + SOC_SINGLE("AMP SSM Switch", MAX98388_R2093_SPK_AMP_SSM_CFG, + MAX98388_SPK_AMP_SSM_EN_SHIFT, 1, 0), + SOC_ENUM("AMP SSM Mod", max98388_ssm_mod_enum), +}; + +static const struct snd_soc_dapm_route max98388_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, + /* Capture */ + { "ADC Voltage", NULL, "VMON"}, + { "ADC Current", NULL, "IMON"}, + { "VI Sense", "Switch", "ADC Voltage"}, + { "VI Sense", "Switch", "ADC Current"}, + { "Voltage Sense", NULL, "VI Sense"}, + { "Current Sense", NULL, "VI Sense"}, +}; + +static void max98388_reset(struct max98388_priv *max98388, struct device *dev) +{ + int ret, reg, count; + + /* Software Reset */ + ret = regmap_update_bits(max98388->regmap, + MAX98388_R2000_SW_RESET, + MAX98388_SOFT_RESET, + MAX98388_SOFT_RESET); + if (ret) + dev_err(dev, "Reset command failed. (ret:%d)\n", ret); + + count = 0; + while (count < 3) { + usleep_range(10000, 11000); + /* Software Reset Verification */ + ret = regmap_read(max98388->regmap, + MAX98388_R22FF_REV_ID, ®); + if (!ret) { + dev_info(dev, "Reset completed (retry:%d)\n", count); + return; + } + count++; + } + dev_err(dev, "Reset failed. (ret:%d)\n", ret); +} + +static int max98388_probe(struct snd_soc_component *component) +{ + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + + /* Software Reset */ + max98388_reset(max98388, component->dev); + + /* General channel source configuration */ + regmap_write(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + 0x10); + + /* Enable DC blocker */ + regmap_write(max98388->regmap, + MAX98388_R2091_SPK_CH_CFG, + 0x1); + /* Enable IMON VMON DC blocker */ + regmap_write(max98388->regmap, + MAX98388_R20A0_IV_DATA_DSP_CTRL, + 0x3); + /* TX slot configuration */ + regmap_write(max98388->regmap, + MAX98388_R2044_PCM_TX_CTRL1, + max98388->v_slot); + + regmap_write(max98388->regmap, + MAX98388_R2045_PCM_TX_CTRL2, + max98388->i_slot); + /* Enable Auto-restart behavior by default */ + regmap_write(max98388->regmap, + MAX98388_R210E_AUTO_RESTART, 0xF); + /* Set interleave mode */ + if (max98388->interleave_mode) + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_TX_CH_INTERLEAVE_MASK, + MAX98388_PCM_TX_CH_INTERLEAVE_MASK); + + /* Speaker Amplifier Channel Enable */ + regmap_update_bits(max98388->regmap, + MAX98388_R209F_SPK_CH_AMP_EN, + MAX98388_SPK_EN_MASK, 1); + + return 0; +} + +static int max98388_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + unsigned int format = 0; + unsigned int invert = 0; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE, + invert); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = MAX98388_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = MAX98388_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98388_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98388_PCM_FORMAT_TDM_MODE0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98388_PCM_MODE_CFG_FORMAT_SHIFT); + + return 0; +} + +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, 320, +}; + +static int max98388_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} + +static int max98388_set_clock(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98388->ch_size; + int value; + + if (!max98388->tdm_mode) { + /* BCLK configuration */ + value = max98388_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_CLK_SETUP_BSEL_MASK, + value); + } + return 0; +} + +static int max98388_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + int ret, reg; + int status = 0; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + max98388->ch_size = snd_pcm_format_width(params_format(params)); + + ret = regmap_read(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, ®); + if (ret < 0) + goto err; + + /* GLOBAL_EN OFF prior to the channel size re-configure */ + if (chan_sz != (reg & MAX98388_PCM_MODE_CFG_CHANSZ_MASK)) { + ret = regmap_read(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, &status); + if (ret < 0) + goto err; + + if (status) { + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 0); + usleep_range(30000, 31000); + } + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + } + dev_dbg(component->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98388_PCM_SR_8000; + break; + case 11025: + sampling_rate = MAX98388_PCM_SR_11025; + break; + case 12000: + sampling_rate = MAX98388_PCM_SR_12000; + break; + case 16000: + sampling_rate = MAX98388_PCM_SR_16000; + break; + case 22050: + sampling_rate = MAX98388_PCM_SR_22050; + break; + case 24000: + sampling_rate = MAX98388_PCM_SR_24000; + break; + case 32000: + sampling_rate = MAX98388_PCM_SR_32000; + break; + case 44100: + sampling_rate = MAX98388_PCM_SR_44100; + break; + case 48000: + sampling_rate = MAX98388_PCM_SR_48000; + break; + case 88200: + sampling_rate = MAX98388_PCM_SR_88200; + break; + case 96000: + sampling_rate = MAX98388_PCM_SR_96000; + break; + default: + dev_err(component->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_MASK, + sampling_rate); + + /* set sampling rate of IV */ + if (max98388->interleave_mode && + sampling_rate > MAX98388_PCM_SR_16000) + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_IV_MASK, + (sampling_rate - 3) << MAX98388_PCM_SR_IV_SHIFT); + else + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_IV_MASK, + sampling_rate << MAX98388_PCM_SR_IV_SHIFT); + + ret = max98388_set_clock(component, params); + + if (status) { + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 1); + usleep_range(30000, 31000); + } + + return ret; + +err: + return -EINVAL; +} + +#define MAX_NUM_SLOTS 16 +#define MAX_NUM_CH 2 + +static int max98388_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + int bsel = 0; + unsigned int chan_sz = 0; + unsigned int mask; + int cnt, slot_found; + int addr, bits; + + if (!tx_mask && !rx_mask && !slots && !slot_width) + max98388->tdm_mode = false; + else + max98388->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98388_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(component->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + slot_found = 0; + mask = rx_mask; + for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) { + if (mask & 0x1) { + if (slot_found == 0) + regmap_update_bits(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + MAX98388_RX_SRC_CH0_SHIFT, + cnt); + else + regmap_update_bits(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + MAX98388_RX_SRC_CH1_SHIFT, + cnt); + slot_found++; + if (slot_found >= MAX_NUM_CH) + break; + } + } + + /* speaker feedback slot configuration */ + slot_found = 0; + mask = tx_mask; + for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) { + if (mask & 0x1) { + addr = MAX98388_R2044_PCM_TX_CTRL1 + (cnt / 8); + bits = cnt % 8; + regmap_update_bits(max98388->regmap, addr, bits, bits); + if (slot_found >= MAX_NUM_CH) + break; + } + } + + return 0; +} + +#define MAX98388_RATES SNDRV_PCM_RATE_8000_96000 + +#define MAX98388_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98388_dai_ops = { + .set_fmt = max98388_dai_set_fmt, + .hw_params = max98388_dai_hw_params, + .set_tdm_slot = max98388_dai_tdm_slot, +}; + +static bool max98388_readable_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case MAX98388_R2001_INT_RAW1 ... MAX98388_R2002_INT_RAW2: + case MAX98388_R2004_INT_STATE1... MAX98388_R2005_INT_STATE2: + case MAX98388_R2020_THERM_WARN_THRESH: + case MAX98388_R2031_SPK_MON_THRESH + ... MAX98388_R2033_SPK_MON_DURATION: + case MAX98388_R2037_ERR_MON_CTRL: + case MAX98388_R2040_PCM_MODE_CFG + ... MAX98388_R2042_PCM_SR_SETUP: + case MAX98388_R2044_PCM_TX_CTRL1 + ... MAX98388_R2045_PCM_TX_CTRL2: + case MAX98388_R2050_PCM_TX_HIZ_CTRL1 + ... MAX98388_R2059_PCM_RX_SRC2: + case MAX98388_R205C_PCM_TX_DRIVE_STRENGTH + ... MAX98388_R205F_PCM_TX_EN: + case MAX98388_R2090_SPK_CH_VOL_CTRL + ... MAX98388_R2094_SPK_AMP_ER_CTRL: + case MAX98388_R209E_SPK_CH_PINK_NOISE_EN + ... MAX98388_R209F_SPK_CH_AMP_EN: + case MAX98388_R20A0_IV_DATA_DSP_CTRL: + case MAX98388_R20A7_IV_DATA_EN: + case MAX98388_R20E0_BP_ALC_THRESH ... MAX98388_R20E4_BP_ALC_MUTE: + case MAX98388_R20EE_BP_INF_HOLD_REL ... MAX98388_R20EF_BP_ALC_EN: + case MAX98388_R210E_AUTO_RESTART: + case MAX98388_R210F_GLOBAL_EN: + case MAX98388_R22FF_REV_ID: + return true; + default: + return false; + } +}; + +static bool max98388_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98388_R2001_INT_RAW1 ... MAX98388_R2005_INT_STATE2: + case MAX98388_R210F_GLOBAL_EN: + case MAX98388_R22FF_REV_ID: + return true; + default: + return false; + } +} + +static struct snd_soc_dai_driver max98388_dai[] = { + { + .name = "max98388-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98388_RATES, + .formats = MAX98388_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98388_RATES, + .formats = MAX98388_FORMATS, + }, + .ops = &max98388_dai_ops, + } +}; + +static int max98388_suspend(struct device *dev) +{ + struct max98388_priv *max98388 = dev_get_drvdata(dev); + + regcache_cache_only(max98388->regmap, true); + regcache_mark_dirty(max98388->regmap); + + return 0; +} + +static int max98388_resume(struct device *dev) +{ + struct max98388_priv *max98388 = dev_get_drvdata(dev); + + regcache_cache_only(max98388->regmap, false); + max98388_reset(max98388, dev); + regcache_sync(max98388->regmap); + + return 0; +} + +static const struct dev_pm_ops max98388_pm = { + SYSTEM_SLEEP_PM_OPS(max98388_suspend, max98388_resume) +}; + +static const struct regmap_config max98388_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98388_R22FF_REV_ID, + .reg_defaults = max98388_reg, + .num_reg_defaults = ARRAY_SIZE(max98388_reg), + .readable_reg = max98388_readable_register, + .volatile_reg = max98388_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98388 = { + .probe = max98388_probe, + .controls = max98388_snd_controls, + .num_controls = ARRAY_SIZE(max98388_snd_controls), + .dapm_widgets = max98388_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98388_dapm_widgets), + .dapm_routes = max98388_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98388_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static void max98388_read_deveice_property(struct device *dev, + struct max98388_priv *max98388) +{ + int value; + + if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value)) + max98388->v_slot = value & 0xF; + else + max98388->v_slot = 0; + + if (!device_property_read_u32(dev, "adi,imon-slot-no", &value)) + max98388->i_slot = value & 0xF; + else + max98388->i_slot = 1; + + if (device_property_read_bool(dev, "adi,interleave-mode")) + max98388->interleave_mode = true; + else + max98388->interleave_mode = false; +} + +static int max98388_i2c_probe(struct i2c_client *i2c) +{ + int ret = 0; + int reg = 0; + + struct max98388_priv *max98388 = NULL; + + max98388 = devm_kzalloc(&i2c->dev, sizeof(*max98388), GFP_KERNEL); + if (!max98388) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98388); + + /* regmap initialization */ + max98388->regmap = devm_regmap_init_i2c(i2c, &max98388_regmap); + if (IS_ERR(max98388->regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(max98388->regmap), + "Failed to allocate register map.\n"); + + /* voltage/current slot & gpio configuration */ + max98388_read_deveice_property(&i2c->dev, max98388); + + /* Device Reset */ + max98388->reset_gpio = devm_gpiod_get_optional(&i2c->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(max98388->reset_gpio)) + return dev_err_probe(&i2c->dev, PTR_ERR(max98388->reset_gpio), + "Unable to request GPIO\n"); + + if (max98388->reset_gpio) { + usleep_range(5000, 6000); + gpiod_set_value_cansleep(max98388->reset_gpio, 0); + /* Wait for the hw reset done */ + usleep_range(5000, 6000); + } + + /* Read Revision ID */ + ret = regmap_read(max98388->regmap, + MAX98388_R22FF_REV_ID, ®); + if (ret < 0) + return dev_err_probe(&i2c->dev, ret, + "Failed to read the revision ID\n"); + + dev_info(&i2c->dev, "MAX98388 revisionID: 0x%02X\n", reg); + + /* codec registration */ + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_max98388, + max98388_dai, + ARRAY_SIZE(max98388_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id max98388_i2c_id[] = { + { "max98388", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98388_i2c_id); + +static const struct of_device_id max98388_of_match[] = { + { .compatible = "adi,max98388", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98388_of_match); + +static const struct acpi_device_id max98388_acpi_match[] = { + { "ADS8388", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98388_acpi_match); + +static struct i2c_driver max98388_i2c_driver = { + .driver = { + .name = "max98388", + .of_match_table = max98388_of_match, + .acpi_match_table = max98388_acpi_match, + .pm = pm_sleep_ptr(&max98388_pm), + }, + .probe = max98388_i2c_probe, + .id_table = max98388_i2c_id, +}; + +module_i2c_driver(max98388_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98388 driver"); +MODULE_AUTHOR("Ryan Lee "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98388.h b/sound/soc/codecs/max98388.h new file mode 100644 index 0000000000..77833d1819 --- /dev/null +++ b/sound/soc/codecs/max98388.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * max98388.h -- MAX98388 ALSA SoC audio driver header + * + * Copyright(c) 2022, Analog Devices Inc. + */ + +#ifndef _MAX98388_H +#define _MAX98388_H + +/* Device Status Registers */ +#define MAX98388_R2000_SW_RESET 0x2000 +#define MAX98388_R2001_INT_RAW1 0x2001 +#define MAX98388_R2002_INT_RAW2 0x2002 +#define MAX98388_R2004_INT_STATE1 0x2004 +#define MAX98388_R2005_INT_STATE2 0x2005 +/* Thermal Protection Registers */ +#define MAX98388_R2020_THERM_WARN_THRESH 0x2020 +/* Error Monitor */ +#define MAX98388_R2031_SPK_MON_THRESH 0x2031 +#define MAX98388_R2032_SPK_MON_LD_SEL 0x2032 +#define MAX98388_R2033_SPK_MON_DURATION 0x2033 +#define MAX98388_R2037_ERR_MON_CTRL 0x2037 +/* PCM Registers */ +#define MAX98388_R2040_PCM_MODE_CFG 0x2040 +#define MAX98388_R2041_PCM_CLK_SETUP 0x2041 +#define MAX98388_R2042_PCM_SR_SETUP 0x2042 +#define MAX98388_R2044_PCM_TX_CTRL1 0x2044 +#define MAX98388_R2045_PCM_TX_CTRL2 0x2045 +#define MAX98388_R2050_PCM_TX_HIZ_CTRL1 0x2050 +#define MAX98388_R2051_PCM_TX_HIZ_CTRL2 0x2051 +#define MAX98388_R2052_PCM_TX_HIZ_CTRL3 0x2052 +#define MAX98388_R2053_PCM_TX_HIZ_CTRL4 0x2053 +#define MAX98388_R2054_PCM_TX_HIZ_CTRL5 0x2054 +#define MAX98388_R2055_PCM_TX_HIZ_CTRL6 0x2055 +#define MAX98388_R2056_PCM_TX_HIZ_CTRL7 0x2056 +#define MAX98388_R2057_PCM_TX_HIZ_CTRL8 0x2057 +#define MAX98388_R2058_PCM_RX_SRC1 0x2058 +#define MAX98388_R2059_PCM_RX_SRC2 0x2059 +#define MAX98388_R205C_PCM_TX_DRIVE_STRENGTH 0x205C +#define MAX98388_R205D_PCM_TX_SRC_EN 0x205D +#define MAX98388_R205E_PCM_RX_EN 0x205E +#define MAX98388_R205F_PCM_TX_EN 0x205F +/* Speaker Channel Control */ +#define MAX98388_R2090_SPK_CH_VOL_CTRL 0x2090 +#define MAX98388_R2091_SPK_CH_CFG 0x2091 +#define MAX98388_R2092_SPK_AMP_OUT_CFG 0x2092 +#define MAX98388_R2093_SPK_AMP_SSM_CFG 0x2093 +#define MAX98388_R2094_SPK_AMP_ER_CTRL 0x2094 +#define MAX98388_R209E_SPK_CH_PINK_NOISE_EN 0x209E +#define MAX98388_R209F_SPK_CH_AMP_EN 0x209F +#define MAX98388_R20A0_IV_DATA_DSP_CTRL 0x20A0 +#define MAX98388_R20A7_IV_DATA_EN 0x20A7 +#define MAX98388_R20E0_BP_ALC_THRESH 0x20E0 +#define MAX98388_R20E1_BP_ALC_RATES 0x20E1 +#define MAX98388_R20E2_BP_ALC_ATTEN 0x20E2 +#define MAX98388_R20E3_BP_ALC_REL 0x20E3 +#define MAX98388_R20E4_BP_ALC_MUTE 0x20E4 +#define MAX98388_R20EE_BP_INF_HOLD_REL 0x20EE +#define MAX98388_R20EF_BP_ALC_EN 0x20EF +#define MAX98388_R210E_AUTO_RESTART 0x210E +#define MAX98388_R210F_GLOBAL_EN 0x210F +#define MAX98388_R22FF_REV_ID 0x22FF + +/* MAX98388_R2000_SW_RESET */ +#define MAX98388_SOFT_RESET (0x1 << 0) + +/* MAX98388_R2020_THERM_WARN_THRESH */ +#define MAX98388_THERM_SHDN_THRESH_SHIFT (0) +#define MAX98388_THERM_WARN_THRESH_SHIFT (2) + +/* MAX98388_R2022_PCM_TX_SRC_1 */ +#define MAX98388_PCM_TX_CH_SRC_A_V_SHIFT (0) +#define MAX98388_PCM_TX_CH_SRC_A_I_SHIFT (4) + +/* MAX98388_R2024_PCM_DATA_FMT_CFG */ +#define MAX98388_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98388_PCM_MODE_CFG_FORMAT_SHIFT (3) +#define MAX98388_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2) +#define MAX98388_PCM_FORMAT_I2S (0x0 << 0) +#define MAX98388_PCM_FORMAT_LJ (0x1 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE0 (0x3 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE1 (0x4 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE2 (0x5 << 0) +#define MAX98388_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) + +/* MAX98388_R2031_SPK_MON_THRESH */ +#define MAX98388_SPKMON_THRESH_SHIFT (0) + +/* MAX98388_R2032_SPK_MON_LD_SEL */ +#define MAX98388_SPKMON_LOAD_SHIFT (0) + +/* MAX98388_R2033_SPK_MON_DURATION */ +#define MAX98388_SPKMON_DURATION_SHIFT (0) + +/* MAX98388_R2037_ERR_MON_CTRL */ +#define MAX98388_CLOCK_MON_SHIFT (0) +#define MAX98388_SPK_MON_SHIFT (1) + +/* MAX98388_R203E_AMP_PATH_GAIN */ +#define MAX98388_SPK_DIGI_GAIN_MASK (0xF << 4) +#define MAX98388_SPK_DIGI_GAIN_SHIFT (4) +#define MAX98388_FS_GAIN_MAX_MASK (0xF << 0) +#define MAX98388_FS_GAIN_MAX_SHIFT (0) + +/* MAX98388_R2041_PCM_CLK_SETUP */ +#define MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4) +#define MAX98388_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) + +/* MAX98388_R2042_PCM_SR_SETUP */ +#define MAX98388_PCM_SR_MASK (0xF << 0) +#define MAX98388_PCM_SR_IV_MASK (0xF << 4) +#define MAX98388_PCM_SR_IV_SHIFT (4) +#define MAX98388_PCM_SR_8000 (0x0 << 0) +#define MAX98388_PCM_SR_11025 (0x1 << 0) +#define MAX98388_PCM_SR_12000 (0x2 << 0) +#define MAX98388_PCM_SR_16000 (0x3 << 0) +#define MAX98388_PCM_SR_22050 (0x4 << 0) +#define MAX98388_PCM_SR_24000 (0x5 << 0) +#define MAX98388_PCM_SR_32000 (0x6 << 0) +#define MAX98388_PCM_SR_44100 (0x7 << 0) +#define MAX98388_PCM_SR_48000 (0x8 << 0) +#define MAX98388_PCM_SR_88200 (0x9 << 0) +#define MAX98388_PCM_SR_96000 (0xA << 0) + +/* MAX98388_R2043_AMP_EN */ +#define MAX98388_SPK_EN_MASK (0x1 << 0) +#define MAX98388_SPKFB_EN_MASK (0x1 << 1) +#define MAX98388_SPKFB_EN_SHIFT (1) + +/* MAX98388_R2052_MEAS_ADC_PVDD_FLT_CFG */ +#define MAX98388_FLT_EN_SHIFT (4) + +/* MAX98388_R2058_PCM_RX_SRC1 */ +#define MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT (0) + +/* MAX98388_R2059_PCM_RX_SRC2 */ +#define MAX98388_RX_SRC_CH0_SHIFT (0) +#define MAX98388_RX_SRC_CH1_SHIFT (4) + +/* MAX98388_R2091_SPK_CH_CFG */ +#define MAX98388_SPK_CFG_DCBLK_SHIFT (0) +#define MAX98388_SPK_CFG_DITH_EN_SHIFT (1) +#define MAX98388_SPK_CFG_INV_SHIFT (2) +#define MAX98388_SPK_CFG_VOL_RMPUP_SHIFT (3) +#define MAX98388_SPK_CFG_VOL_RMPDN_SHIFT (4) + +/* MAX98388_R2092_SPK_AMP_OUT_CFG */ +#define MAX98388_SPK_AMP_OUT_GAIN_SHIFT (0) +#define MAX98388_SPK_AMP_OUT_MODE_SHIFT (3) + +/* MAX98388_R2093_SPK_AMP_SSM_CFG */ +#define MAX98388_SPK_AMP_SSM_EN_SHIFT (0) +#define MAX98388_SPK_AMP_SSM_MOD_SHIFT (1) + +/* MAX98388_R2094_SPK_AMP_ER_CTRL */ +#define MAX98388_EDGE_RATE_RISE_SHIFT (0) +#define MAX98388_EDGE_RATE_FALL_SHIFT (2) + +/* MAX98388_R209E_SPK_CH_PINK_NOISE_EN */ +#define MAX98388_PINK_NOISE_GEN_SHIFT (0) + +/* MAX98388_R20A0_IV_DATA_DSP_CTRL */ +#define MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT (0) +#define MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT (1) +#define MAX98388_AMP_DSP_CTRL_VOL_INV_SHIFT (2) +#define MAX98388_AMP_DSP_CTRL_CUR_INV_SHIFT (3) +#define MAX98388_AMP_DSP_CTRL_DITH_SHIFT (4) + +/* MAX98388_R20B2_BDE_L4_CFG_2 */ +#define MAX98388_LVL4_HOLD_EN_SHIFT (6) +#define MAX98388_LVL4_MUTE_EN_SHIFT (7) + +/* MAX98388_R20B5_BDE_EN */ +#define MAX98388_BDE_EN_SHIFT (0) + +/* MAX98388_R20D1_DHT_CFG */ +#define MAX98388_DHT_ROT_PNT_SHIFT (0) +#define MAX98388_DHT_SPK_GAIN_MIN_SHIFT (4) + +/* MAX98388_R20D2_DHT_ATTACK_CFG */ +#define MAX98388_DHT_ATTACK_RATE_SHIFT (0) +#define MAX98388_DHT_ATTACK_STEP_SHIFT (3) + +/* MAX98388_R20D3_DHT_RELEASE_CFG */ +#define MAX98388_DHT_RELEASE_RATE_SHIFT (0) +#define MAX98388_DHT_RELEASE_STEP_SHIFT (3) + +/* MAX98388_R20D4_DHT_EN */ +#define MAX98388_DHT_EN_SHIFT (0) + +/* MAX98388_R20E0_BP_ALC_THRESH */ +#define MAX98388_ALC_THRESH_SHIFT (0) + +/* MAX98388_R20E1_BP_ALC_RATES */ +#define MAX98388_ALC_RELEASE_RATE_SHIFT (0) +#define MAX98388_ALC_ATTACK_RATE_SHIFT (4) + +/* MAX98388_R20E2_BP_ALC_ATTEN */ +#define MAX98388_ALC_MAX_ATTEN_SHIFT (0) + +/* MAX98388_R20E3_BP_ALC_REL */ +#define MAX98388_ALC_DEBOUNCE_TIME_SHIFT (0) + +/* MAX98388_R20E4_BP_ALC_MUTE */ +#define MAX98388_ALC_MUTE_EN_SHIFT (0) +#define MAX98388_ALC_MUTE_DELAY_SHIFT (1) +#define MAX98388_ALC_MUTE_RAMP_EN_SHIFT (4) +#define MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT (5) + +/* MAX98388_R210E_AUTO_RESTART */ +#define MAX98388_PVDD_UVLO_AUTORESTART_SHIFT (0) +#define MAX98388_THERM_AUTORESTART_SHIFT (1) +#define MAX98388_OVC_AUTORESTART_SHIFT (2) +#define MAX98388_CMON_AUTORESTART_SHIFT (3) + +/* MAX98388_R210F_GLOBAL_EN */ +#define MAX98388_GLOBAL_EN_MASK (0x1 << 0) + +struct max98388_priv { + struct regmap *regmap; + struct gpio_desc *reset_gpio; + unsigned int v_slot; + unsigned int i_slot; + unsigned int spkfb_slot; + bool interleave_mode; + unsigned int ch_size; + bool tdm_mode; +}; + +#endif diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c new file mode 100644 index 0000000000..5b8e78e516 --- /dev/null +++ b/sound/soc/codecs/max98390.c @@ -0,0 +1,1144 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * max98390.c -- MAX98390 ALSA Soc Audio driver + * + * Copyright (C) 2020 Maxim Integrated Products + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "max98390.h" + +static struct reg_default max98390_reg_defaults[] = { + {MAX98390_INT_EN1, 0xf0}, + {MAX98390_INT_EN2, 0x00}, + {MAX98390_INT_EN3, 0x00}, + {MAX98390_INT_FLAG_CLR1, 0x00}, + {MAX98390_INT_FLAG_CLR2, 0x00}, + {MAX98390_INT_FLAG_CLR3, 0x00}, + {MAX98390_IRQ_CTRL, 0x01}, + {MAX98390_CLK_MON, 0x6d}, + {MAX98390_DAT_MON, 0x03}, + {MAX98390_WDOG_CTRL, 0x00}, + {MAX98390_WDOG_RST, 0x00}, + {MAX98390_MEAS_ADC_THERM_WARN_THRESH, 0x75}, + {MAX98390_MEAS_ADC_THERM_SHDN_THRESH, 0x8c}, + {MAX98390_MEAS_ADC_THERM_HYSTERESIS, 0x08}, + {MAX98390_PIN_CFG, 0x55}, + {MAX98390_PCM_RX_EN_A, 0x00}, + {MAX98390_PCM_RX_EN_B, 0x00}, + {MAX98390_PCM_TX_EN_A, 0x00}, + {MAX98390_PCM_TX_EN_B, 0x00}, + {MAX98390_PCM_TX_HIZ_CTRL_A, 0xff}, + {MAX98390_PCM_TX_HIZ_CTRL_B, 0xff}, + {MAX98390_PCM_CH_SRC_1, 0x00}, + {MAX98390_PCM_CH_SRC_2, 0x00}, + {MAX98390_PCM_CH_SRC_3, 0x00}, + {MAX98390_PCM_MODE_CFG, 0xc0}, + {MAX98390_PCM_MASTER_MODE, 0x1c}, + {MAX98390_PCM_CLK_SETUP, 0x44}, + {MAX98390_PCM_SR_SETUP, 0x08}, + {MAX98390_ICC_RX_EN_A, 0x00}, + {MAX98390_ICC_RX_EN_B, 0x00}, + {MAX98390_ICC_TX_EN_A, 0x00}, + {MAX98390_ICC_TX_EN_B, 0x00}, + {MAX98390_ICC_HIZ_MANUAL_MODE, 0x00}, + {MAX98390_ICC_TX_HIZ_EN_A, 0x00}, + {MAX98390_ICC_TX_HIZ_EN_B, 0x00}, + {MAX98390_ICC_LNK_EN, 0x00}, + {MAX98390_R2039_AMP_DSP_CFG, 0x0f}, + {MAX98390_R203A_AMP_EN, 0x81}, + {MAX98390_TONE_GEN_DC_CFG, 0x00}, + {MAX98390_SPK_SRC_SEL, 0x00}, + {MAX98390_SSM_CFG, 0x85}, + {MAX98390_MEAS_EN, 0x03}, + {MAX98390_MEAS_DSP_CFG, 0x0f}, + {MAX98390_BOOST_CTRL0, 0x1c}, + {MAX98390_BOOST_CTRL3, 0x01}, + {MAX98390_BOOST_CTRL1, 0x40}, + {MAX98390_MEAS_ADC_CFG, 0x07}, + {MAX98390_MEAS_ADC_BASE_MSB, 0x00}, + {MAX98390_MEAS_ADC_BASE_LSB, 0x23}, + {MAX98390_ADC_CH0_DIVIDE, 0x00}, + {MAX98390_ADC_CH1_DIVIDE, 0x00}, + {MAX98390_ADC_CH2_DIVIDE, 0x00}, + {MAX98390_ADC_CH0_FILT_CFG, 0x00}, + {MAX98390_ADC_CH1_FILT_CFG, 0x00}, + {MAX98390_ADC_CH2_FILT_CFG, 0x00}, + {MAX98390_PWR_GATE_CTL, 0x2c}, + {MAX98390_BROWNOUT_EN, 0x00}, + {MAX98390_BROWNOUT_INFINITE_HOLD, 0x00}, + {MAX98390_BROWNOUT_INFINITE_HOLD_CLR, 0x00}, + {MAX98390_BROWNOUT_LVL_HOLD, 0x00}, + {MAX98390_BROWNOUT_LVL1_THRESH, 0x00}, + {MAX98390_BROWNOUT_LVL2_THRESH, 0x00}, + {MAX98390_BROWNOUT_LVL3_THRESH, 0x00}, + {MAX98390_BROWNOUT_LVL4_THRESH, 0x00}, + {MAX98390_BROWNOUT_THRESH_HYSTERYSIS, 0x00}, + {MAX98390_BROWNOUT_AMP_LIMITER_ATK_REL, 0x1f}, + {MAX98390_BROWNOUT_AMP_GAIN_ATK_REL, 0x00}, + {MAX98390_BROWNOUT_AMP1_CLIP_MODE, 0x00}, + {MAX98390_BROWNOUT_LVL1_CUR_LIMIT, 0x00}, + {MAX98390_BROWNOUT_LVL1_AMP1_CTRL1, 0x00}, + {MAX98390_BROWNOUT_LVL1_AMP1_CTRL2, 0x00}, + {MAX98390_BROWNOUT_LVL1_AMP1_CTRL3, 0x00}, + {MAX98390_BROWNOUT_LVL2_CUR_LIMIT, 0x00}, + {MAX98390_BROWNOUT_LVL2_AMP1_CTRL1, 0x00}, + {MAX98390_BROWNOUT_LVL2_AMP1_CTRL2, 0x00}, + {MAX98390_BROWNOUT_LVL2_AMP1_CTRL3, 0x00}, + {MAX98390_BROWNOUT_LVL3_CUR_LIMIT, 0x00}, + {MAX98390_BROWNOUT_LVL3_AMP1_CTRL1, 0x00}, + {MAX98390_BROWNOUT_LVL3_AMP1_CTRL2, 0x00}, + {MAX98390_BROWNOUT_LVL3_AMP1_CTRL3, 0x00}, + {MAX98390_BROWNOUT_LVL4_CUR_LIMIT, 0x00}, + {MAX98390_BROWNOUT_LVL4_AMP1_CTRL1, 0x00}, + {MAX98390_BROWNOUT_LVL4_AMP1_CTRL2, 0x00}, + {MAX98390_BROWNOUT_LVL4_AMP1_CTRL3, 0x00}, + {MAX98390_BROWNOUT_ILIM_HLD, 0x00}, + {MAX98390_BROWNOUT_LIM_HLD, 0x00}, + {MAX98390_BROWNOUT_CLIP_HLD, 0x00}, + {MAX98390_BROWNOUT_GAIN_HLD, 0x00}, + {MAX98390_ENV_TRACK_VOUT_HEADROOM, 0x0f}, + {MAX98390_ENV_TRACK_BOOST_VOUT_DELAY, 0x80}, + {MAX98390_ENV_TRACK_REL_RATE, 0x07}, + {MAX98390_ENV_TRACK_HOLD_RATE, 0x07}, + {MAX98390_ENV_TRACK_CTRL, 0x01}, + {MAX98390_BOOST_BYPASS1, 0x49}, + {MAX98390_BOOST_BYPASS2, 0x2b}, + {MAX98390_BOOST_BYPASS3, 0x08}, + {MAX98390_FET_SCALING1, 0x00}, + {MAX98390_FET_SCALING2, 0x03}, + {MAX98390_FET_SCALING3, 0x00}, + {MAX98390_FET_SCALING4, 0x07}, + {MAX98390_SPK_SPEEDUP, 0x00}, + {DSMIG_WB_DRC_RELEASE_TIME_1, 0x00}, + {DSMIG_WB_DRC_RELEASE_TIME_2, 0x00}, + {DSMIG_WB_DRC_ATTACK_TIME_1, 0x00}, + {DSMIG_WB_DRC_ATTACK_TIME_2, 0x00}, + {DSMIG_WB_DRC_COMPRESSION_RATIO, 0x00}, + {DSMIG_WB_DRC_COMPRESSION_THRESHOLD, 0x00}, + {DSMIG_WB_DRC_MAKEUPGAIN, 0x00}, + {DSMIG_WB_DRC_NOISE_GATE_THRESHOLD, 0x00}, + {DSMIG_WBDRC_HPF_ENABLE, 0x00}, + {DSMIG_WB_DRC_TEST_SMOOTHER_OUT_EN, 0x00}, + {DSMIG_PPR_THRESHOLD, 0x00}, + {DSM_STEREO_BASS_CHANNEL_SELECT, 0x00}, + {DSM_TPROT_THRESHOLD_BYTE0, 0x00}, + {DSM_TPROT_THRESHOLD_BYTE1, 0x00}, + {DSM_TPROT_ROOM_TEMPERATURE_BYTE0, 0x00}, + {DSM_TPROT_ROOM_TEMPERATURE_BYTE1, 0x00}, + {DSM_TPROT_RECIP_RDC_ROOM_BYTE0, 0x00}, + {DSM_TPROT_RECIP_RDC_ROOM_BYTE1, 0x00}, + {DSM_TPROT_RECIP_RDC_ROOM_BYTE2, 0x00}, + {DSM_TPROT_RECIP_TCONST_BYTE0, 0x00}, + {DSM_TPROT_RECIP_TCONST_BYTE1, 0x00}, + {DSM_TPROT_RECIP_TCONST_BYTE2, 0x00}, + {DSM_THERMAL_ATTENUATION_SETTINGS, 0x00}, + {DSM_THERMAL_PILOT_TONE_ATTENUATION, 0x00}, + {DSM_TPROT_PG_TEMP_THRESH_BYTE0, 0x00}, + {DSM_TPROT_PG_TEMP_THRESH_BYTE1, 0x00}, + {DSMIG_DEBUZZER_THRESHOLD, 0x00}, + {DSMIG_DEBUZZER_ALPHA_COEF_TEST_ONLY, 0x08}, + {DSM_VOL_ENA, 0x20}, + {DSM_VOL_CTRL, 0xa0}, + {DSMIG_EN, 0x00}, + {MAX98390_R23E1_DSP_GLOBAL_EN, 0x00}, + {MAX98390_R23FF_GLOBAL_EN, 0x00}, +}; + +static int max98390_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + unsigned int mode; + unsigned int format; + unsigned int invert = 0; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + mode = MAX98390_PCM_MASTER_MODE_SLAVE; + break; + case SND_SOC_DAIFMT_CBP_CFP: + max98390->provider = true; + mode = MAX98390_PCM_MASTER_MODE_MASTER; + break; + default: + dev_err(component->dev, "DAI clock mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98390->regmap, + MAX98390_PCM_MASTER_MODE, + MAX98390_PCM_MASTER_MODE_MASK, + mode); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98390_PCM_MODE_CFG_PCM_BCLKEDGE; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98390->regmap, + MAX98390_PCM_MODE_CFG, + MAX98390_PCM_MODE_CFG_PCM_BCLKEDGE, + invert); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = MAX98390_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = MAX98390_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98390_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98390_PCM_FORMAT_TDM_MODE0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(max98390->regmap, + MAX98390_PCM_MODE_CFG, + MAX98390_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98390_PCM_MODE_CFG_FORMAT_SHIFT); + + return 0; +} + +static int max98390_get_bclk_sel(int bclk) +{ + int i; + /* BCLKs per LRCLK */ + static int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 320, 384, 512, + }; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} + +static int max98390_set_clock(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + /* codec MCLK rate in master mode */ + static int rate_table[] = { + 5644800, 6000000, 6144000, 6500000, + 9600000, 11289600, 12000000, 12288000, + 13000000, 19200000, + }; + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) + * snd_pcm_format_width(params_format(params)); + int value; + + if (max98390->provider) { + int i; + /* match rate to closest value */ + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i] >= max98390->sysclk) + break; + } + if (i == ARRAY_SIZE(rate_table)) { + dev_err(component->dev, "failed to find proper clock rate.\n"); + return -EINVAL; + } + + regmap_update_bits(max98390->regmap, + MAX98390_PCM_MASTER_MODE, + MAX98390_PCM_MASTER_MODE_MCLK_MASK, + i << MAX98390_PCM_MASTER_MODE_MCLK_RATE_SHIFT); + } + + if (!max98390->tdm_mode) { + /* BCLK configuration */ + value = max98390_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98390->regmap, + MAX98390_PCM_CLK_SETUP, + MAX98390_PCM_CLK_SETUP_BSEL_MASK, + value); + } + return 0; +} + +static int max98390_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = + dai->component; + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + unsigned int sampling_rate; + unsigned int chan_sz; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + regmap_update_bits(max98390->regmap, + MAX98390_PCM_MODE_CFG, + MAX98390_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + dev_dbg(component->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98390_PCM_SR_SET1_SR_8000; + break; + case 11025: + sampling_rate = MAX98390_PCM_SR_SET1_SR_11025; + break; + case 12000: + sampling_rate = MAX98390_PCM_SR_SET1_SR_12000; + break; + case 16000: + sampling_rate = MAX98390_PCM_SR_SET1_SR_16000; + break; + case 22050: + sampling_rate = MAX98390_PCM_SR_SET1_SR_22050; + break; + case 24000: + sampling_rate = MAX98390_PCM_SR_SET1_SR_24000; + break; + case 32000: + sampling_rate = MAX98390_PCM_SR_SET1_SR_32000; + break; + case 44100: + sampling_rate = MAX98390_PCM_SR_SET1_SR_44100; + break; + case 48000: + sampling_rate = MAX98390_PCM_SR_SET1_SR_48000; + break; + default: + dev_err(component->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98390->regmap, + MAX98390_PCM_SR_SETUP, + MAX98390_PCM_SR_SET1_SR_MASK, + sampling_rate); + + return max98390_set_clock(component, params); +err: + return -EINVAL; +} + +static int max98390_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + int bsel; + unsigned int chan_sz; + + if (!tx_mask && !rx_mask && !slots && !slot_width) + max98390->tdm_mode = false; + else + max98390->tdm_mode = true; + + dev_dbg(component->dev, + "Tdm mode : %d\n", max98390->tdm_mode); + + /* BCLK configuration */ + bsel = max98390_get_bclk_sel(slots * slot_width); + if (!bsel) { + dev_err(component->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98390->regmap, + MAX98390_PCM_CLK_SETUP, + MAX98390_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98390_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98390->regmap, + MAX98390_PCM_MODE_CFG, + MAX98390_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + regmap_write(max98390->regmap, + MAX98390_PCM_RX_EN_A, + rx_mask & 0xFF); + regmap_write(max98390->regmap, + MAX98390_PCM_RX_EN_B, + (rx_mask & 0xFF00) >> 8); + + /* Tx slot Hi-Z configuration */ + regmap_write(max98390->regmap, + MAX98390_PCM_TX_HIZ_CTRL_A, + ~tx_mask & 0xFF); + regmap_write(max98390->regmap, + MAX98390_PCM_TX_HIZ_CTRL_B, + (~tx_mask & 0xFF00) >> 8); + + return 0; +} + +static int max98390_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + max98390->sysclk = freq; + return 0; +} + +static const struct snd_soc_dai_ops max98390_dai_ops = { + .set_sysclk = max98390_dai_set_sysclk, + .set_fmt = max98390_dai_set_fmt, + .hw_params = max98390_dai_hw_params, + .set_tdm_slot = max98390_dai_tdm_slot, +}; + +static int max98390_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(max98390->regmap, + MAX98390_R203A_AMP_EN, + MAX98390_AMP_EN_MASK, 1); + regmap_update_bits(max98390->regmap, + MAX98390_R23FF_GLOBAL_EN, + MAX98390_GLOBAL_EN_MASK, 1); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(max98390->regmap, + MAX98390_R23FF_GLOBAL_EN, + MAX98390_GLOBAL_EN_MASK, 0); + regmap_update_bits(max98390->regmap, + MAX98390_R203A_AMP_EN, + MAX98390_AMP_EN_MASK, 0); + break; + } + return 0; +} + +static const char * const max98390_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static const char * const max98390_boost_voltage_text[] = { + "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V", + "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V", + "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V", + "9.5V", "9.625V", "9.75V", "9.875V", "10V" +}; + +static SOC_ENUM_SINGLE_DECL(max98390_boost_voltage, + MAX98390_BOOST_CTRL0, 0, + max98390_boost_voltage_text); + +static DECLARE_TLV_DB_SCALE(max98390_spk_tlv, 300, 300, 0); +static DECLARE_TLV_DB_SCALE(max98390_digital_tlv, -8000, 50, 0); + +static const char * const max98390_current_limit_text[] = { + "0.00A", "0.50A", "1.00A", "1.05A", "1.10A", "1.15A", "1.20A", "1.25A", + "1.30A", "1.35A", "1.40A", "1.45A", "1.50A", "1.55A", "1.60A", "1.65A", + "1.70A", "1.75A", "1.80A", "1.85A", "1.90A", "1.95A", "2.00A", "2.05A", + "2.10A", "2.15A", "2.20A", "2.25A", "2.30A", "2.35A", "2.40A", "2.45A", + "2.50A", "2.55A", "2.60A", "2.65A", "2.70A", "2.75A", "2.80A", "2.85A", + "2.90A", "2.95A", "3.00A", "3.05A", "3.10A", "3.15A", "3.20A", "3.25A", + "3.30A", "3.35A", "3.40A", "3.45A", "3.50A", "3.55A", "3.60A", "3.65A", + "3.70A", "3.75A", "3.80A", "3.85A", "3.90A", "3.95A", "4.00A", "4.05A", + "4.10A" +}; + +static SOC_ENUM_SINGLE_DECL(max98390_current_limit, + MAX98390_BOOST_CTRL1, 0, + max98390_current_limit_text); + +static int max98390_ref_rdc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + max98390->ref_rdc_value = ucontrol->value.integer.value[0]; + + regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE0, + max98390->ref_rdc_value & 0x000000ff); + regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE1, + (max98390->ref_rdc_value >> 8) & 0x000000ff); + regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE2, + (max98390->ref_rdc_value >> 16) & 0x000000ff); + + return 0; +} + +static int max98390_ref_rdc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = max98390->ref_rdc_value; + + return 0; +} + +static int max98390_ambient_temp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + max98390->ambient_temp_value = ucontrol->value.integer.value[0]; + + regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE1, + (max98390->ambient_temp_value >> 8) & 0x000000ff); + regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE0, + (max98390->ambient_temp_value) & 0x000000ff); + + return 0; +} + +static int max98390_ambient_temp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = max98390->ambient_temp_value; + + return 0; +} + +static int max98390_adaptive_rdc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + + dev_warn(component->dev, "Put adaptive rdc not supported\n"); + + return 0; +} + +static int max98390_adaptive_rdc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rdc, rdc0; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE1, &rdc); + regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE0, &rdc0); + ucontrol->value.integer.value[0] = rdc0 | rdc << 8; + + return 0; +} + +static int max98390_dsm_calib_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* Do nothing */ + return 0; +} + +static int max98390_dsm_calib_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98390_priv *max98390 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + unsigned int rdc, rdc_cal_result, rdc_integer, rdc_factor, temp, val; + + snd_soc_dapm_mutex_lock(dapm); + + regmap_read(max98390->regmap, MAX98390_R23FF_GLOBAL_EN, &val); + if (!val) { + /* Enable the codec for the duration of calibration readout */ + regmap_update_bits(max98390->regmap, MAX98390_R203A_AMP_EN, + MAX98390_AMP_EN_MASK, 1); + regmap_update_bits(max98390->regmap, MAX98390_R23FF_GLOBAL_EN, + MAX98390_GLOBAL_EN_MASK, 1); + } + + regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE1, &rdc); + regmap_read(max98390->regmap, THERMAL_RDC_RD_BACK_BYTE0, &rdc_cal_result); + regmap_read(max98390->regmap, MAX98390_MEAS_ADC_CH2_READ, &temp); + + if (!val) { + /* Disable the codec if it was disabled */ + regmap_update_bits(max98390->regmap, MAX98390_R23FF_GLOBAL_EN, + MAX98390_GLOBAL_EN_MASK, 0); + regmap_update_bits(max98390->regmap, MAX98390_R203A_AMP_EN, + MAX98390_AMP_EN_MASK, 0); + } + + snd_soc_dapm_mutex_unlock(dapm); + + rdc_cal_result |= (rdc << 8) & 0x0000FFFF; + if (rdc_cal_result) + max98390->ref_rdc_value = 268435456U / rdc_cal_result; + + max98390->ambient_temp_value = temp * 52 - 1188; + + rdc_integer = rdc_cal_result * 937 / 65536; + rdc_factor = ((rdc_cal_result * 937 * 100) / 65536) - (rdc_integer * 100); + + dev_info(component->dev, + "rdc resistance about %d.%02d ohm, reg=0x%X temp reg=0x%X\n", + rdc_integer, rdc_factor, rdc_cal_result, temp); + + return 0; +} + +static const struct snd_kcontrol_new max98390_snd_controls[] = { + SOC_SINGLE_TLV("Digital Volume", DSM_VOL_CTRL, + 0, 184, 0, + max98390_digital_tlv), + SOC_SINGLE_TLV("Speaker Volume", MAX98390_R203D_SPK_GAIN, + 0, 6, 0, + max98390_spk_tlv), + SOC_SINGLE("Ramp Up Bypass Switch", MAX98390_R2039_AMP_DSP_CFG, + MAX98390_AMP_DSP_CFG_RMP_UP_SHIFT, 1, 0), + SOC_SINGLE("Ramp Down Bypass Switch", MAX98390_R2039_AMP_DSP_CFG, + MAX98390_AMP_DSP_CFG_RMP_DN_SHIFT, 1, 0), + SOC_SINGLE("Boost Clock Phase", MAX98390_BOOST_CTRL3, + MAX98390_BOOST_CLK_PHASE_CFG_SHIFT, 3, 0), + SOC_ENUM("Boost Output Voltage", max98390_boost_voltage), + SOC_ENUM("Current Limit", max98390_current_limit), + SOC_SINGLE_EXT("DSM Rdc", SND_SOC_NOPM, 0, 0xffffff, 0, + max98390_ref_rdc_get, max98390_ref_rdc_put), + SOC_SINGLE_EXT("DSM Ambient Temp", SND_SOC_NOPM, 0, 0xffff, 0, + max98390_ambient_temp_get, max98390_ambient_temp_put), + SOC_SINGLE_EXT("DSM Adaptive Rdc", SND_SOC_NOPM, 0, 0xffff, 0, + max98390_adaptive_rdc_get, max98390_adaptive_rdc_put), + SOC_SINGLE_EXT("DSM Calibration", SND_SOC_NOPM, 0, 1, 0, + max98390_dsm_calib_get, max98390_dsm_calib_put), +}; + +static const struct soc_enum dai_sel_enum = + SOC_ENUM_SINGLE(MAX98390_PCM_CH_SRC_1, + MAX98390_PCM_RX_CH_SRC_SHIFT, + 3, max98390_switch_text); + +static const struct snd_kcontrol_new max98390_dai_controls = + SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); + +static const struct snd_soc_dapm_widget max98390_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", + SND_SOC_NOPM, 0, 0, max98390_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98390_dai_controls), + SND_SOC_DAPM_OUTPUT("BE_OUT"), +}; + +static const struct snd_soc_dapm_route max98390_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, +}; + +static bool max98390_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98390_SOFTWARE_RESET ... MAX98390_INT_EN3: + case MAX98390_IRQ_CTRL ... MAX98390_WDOG_CTRL: + case MAX98390_MEAS_ADC_THERM_WARN_THRESH + ... MAX98390_BROWNOUT_INFINITE_HOLD: + case MAX98390_BROWNOUT_LVL_HOLD ... DSMIG_DEBUZZER_THRESHOLD: + case DSM_VOL_ENA ... MAX98390_R24FF_REV_ID: + return true; + default: + return false; + } +}; + +static bool max98390_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98390_SOFTWARE_RESET ... MAX98390_INT_EN3: + case MAX98390_MEAS_ADC_CH0_READ ... MAX98390_MEAS_ADC_CH2_READ: + case MAX98390_PWR_GATE_STATUS ... MAX98390_BROWNOUT_STATUS: + case MAX98390_BROWNOUT_LOWEST_STATUS: + case MAX98390_ENV_TRACK_BOOST_VOUT_READ: + case DSM_STBASS_HPF_B0_BYTE0 ... DSM_DEBUZZER_ATTACK_TIME_BYTE2: + case THERMAL_RDC_RD_BACK_BYTE1 ... DSMIG_DEBUZZER_THRESHOLD: + case DSM_THERMAL_GAIN ... DSM_WBDRC_GAIN: + return true; + default: + return false; + } +} + +#define MAX98390_RATES SNDRV_PCM_RATE_8000_48000 + +#define MAX98390_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver max98390_dai[] = { + { + .name = "max98390-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98390_RATES, + .formats = MAX98390_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98390_RATES, + .formats = MAX98390_FORMATS, + }, + .ops = &max98390_dai_ops, + } +}; + +static int max98390_dsm_init(struct snd_soc_component *component) +{ + int ret; + int param_size, param_start_addr; + char filename[128]; + const char *vendor, *product; + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + const struct firmware *fw; + char *dsm_param; + + vendor = dmi_get_system_info(DMI_SYS_VENDOR); + product = dmi_get_system_info(DMI_PRODUCT_NAME); + + if (!strcmp(max98390->dsm_param_name, "default")) { + if (vendor && product) { + snprintf(filename, sizeof(filename), + "dsm_param_%s_%s.bin", vendor, product); + } else { + sprintf(filename, "dsm_param.bin"); + } + } else { + snprintf(filename, sizeof(filename), "%s", + max98390->dsm_param_name); + } + ret = request_firmware(&fw, filename, component->dev); + if (ret) { + ret = request_firmware(&fw, "dsm_param.bin", component->dev); + if (ret) { + ret = request_firmware(&fw, "dsmparam.bin", + component->dev); + if (ret) + goto err; + } + } + + dev_dbg(component->dev, + "max98390: param fw size %zd\n", + fw->size); + if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) { + dev_err(component->dev, + "param fw is invalid.\n"); + ret = -EINVAL; + goto err_alloc; + } + dsm_param = (char *)fw->data; + param_start_addr = (dsm_param[0] & 0xff) | (dsm_param[1] & 0xff) << 8; + param_size = (dsm_param[2] & 0xff) | (dsm_param[3] & 0xff) << 8; + if (param_size > MAX98390_DSM_PARAM_MAX_SIZE || + param_start_addr < MAX98390_IRQ_CTRL || + fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) { + dev_err(component->dev, + "param fw is invalid.\n"); + ret = -EINVAL; + goto err_alloc; + } + regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80); + dsm_param += MAX98390_DSM_PAYLOAD_OFFSET; + regmap_bulk_write(max98390->regmap, param_start_addr, + dsm_param, param_size); + regmap_write(max98390->regmap, MAX98390_R23E1_DSP_GLOBAL_EN, 0x01); + +err_alloc: + release_firmware(fw); +err: + return ret; +} + +static void max98390_init_regs(struct snd_soc_component *component) +{ + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + regmap_write(max98390->regmap, MAX98390_CLK_MON, 0x6f); + regmap_write(max98390->regmap, MAX98390_DAT_MON, 0x00); + regmap_write(max98390->regmap, MAX98390_PWR_GATE_CTL, 0x00); + regmap_write(max98390->regmap, MAX98390_PCM_RX_EN_A, 0x03); + regmap_write(max98390->regmap, MAX98390_ENV_TRACK_VOUT_HEADROOM, 0x0e); + regmap_write(max98390->regmap, MAX98390_BOOST_BYPASS1, 0x46); + regmap_write(max98390->regmap, MAX98390_FET_SCALING3, 0x03); + + /* voltage, current slot configuration */ + regmap_write(max98390->regmap, + MAX98390_PCM_CH_SRC_2, + (max98390->i_l_slot << 4 | + max98390->v_l_slot)&0xFF); + + if (max98390->v_l_slot < 8) { + regmap_update_bits(max98390->regmap, + MAX98390_PCM_TX_HIZ_CTRL_A, + 1 << max98390->v_l_slot, 0); + regmap_update_bits(max98390->regmap, + MAX98390_PCM_TX_EN_A, + 1 << max98390->v_l_slot, + 1 << max98390->v_l_slot); + } else { + regmap_update_bits(max98390->regmap, + MAX98390_PCM_TX_HIZ_CTRL_B, + 1 << (max98390->v_l_slot - 8), 0); + regmap_update_bits(max98390->regmap, + MAX98390_PCM_TX_EN_B, + 1 << (max98390->v_l_slot - 8), + 1 << (max98390->v_l_slot - 8)); + } + + if (max98390->i_l_slot < 8) { + regmap_update_bits(max98390->regmap, + MAX98390_PCM_TX_HIZ_CTRL_A, + 1 << max98390->i_l_slot, 0); + regmap_update_bits(max98390->regmap, + MAX98390_PCM_TX_EN_A, + 1 << max98390->i_l_slot, + 1 << max98390->i_l_slot); + } else { + regmap_update_bits(max98390->regmap, + MAX98390_PCM_TX_HIZ_CTRL_B, + 1 << (max98390->i_l_slot - 8), 0); + regmap_update_bits(max98390->regmap, + MAX98390_PCM_TX_EN_B, + 1 << (max98390->i_l_slot - 8), + 1 << (max98390->i_l_slot - 8)); + } +} + +static int max98390_probe(struct snd_soc_component *component) +{ + struct max98390_priv *max98390 = + snd_soc_component_get_drvdata(component); + + regmap_write(max98390->regmap, MAX98390_SOFTWARE_RESET, 0x01); + /* Sleep reset settle time */ + msleep(20); + + /* Amp init setting */ + max98390_init_regs(component); + /* Update dsm bin param */ + max98390_dsm_init(component); + + /* Dsm Setting */ + if (max98390->ref_rdc_value) { + regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE0, + max98390->ref_rdc_value & 0x000000ff); + regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE1, + (max98390->ref_rdc_value >> 8) & 0x000000ff); + regmap_write(max98390->regmap, DSM_TPROT_RECIP_RDC_ROOM_BYTE2, + (max98390->ref_rdc_value >> 16) & 0x000000ff); + } + if (max98390->ambient_temp_value) { + regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE1, + (max98390->ambient_temp_value >> 8) & 0x000000ff); + regmap_write(max98390->regmap, DSM_TPROT_ROOM_TEMPERATURE_BYTE0, + (max98390->ambient_temp_value) & 0x000000ff); + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max98390_suspend(struct device *dev) +{ + struct max98390_priv *max98390 = dev_get_drvdata(dev); + + dev_dbg(dev, "%s:Enter\n", __func__); + + regcache_cache_only(max98390->regmap, true); + regcache_mark_dirty(max98390->regmap); + + return 0; +} + +static int max98390_resume(struct device *dev) +{ + struct max98390_priv *max98390 = dev_get_drvdata(dev); + + dev_dbg(dev, "%s:Enter\n", __func__); + + regcache_cache_only(max98390->regmap, false); + regcache_sync(max98390->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops max98390_pm = { + SET_SYSTEM_SLEEP_PM_OPS(max98390_suspend, max98390_resume) +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98390 = { + .probe = max98390_probe, + .controls = max98390_snd_controls, + .num_controls = ARRAY_SIZE(max98390_snd_controls), + .dapm_widgets = max98390_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98390_dapm_widgets), + .dapm_routes = max98390_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98390_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config max98390_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98390_R24FF_REV_ID, + .reg_defaults = max98390_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(max98390_reg_defaults), + .readable_reg = max98390_readable_register, + .volatile_reg = max98390_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static void max98390_slot_config(struct i2c_client *i2c, + struct max98390_priv *max98390) +{ + int value; + struct device *dev = &i2c->dev; + + if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value)) + max98390->v_l_slot = value & 0xF; + else + max98390->v_l_slot = 0; + + if (!device_property_read_u32(dev, "maxim,imon-slot-no", &value)) + max98390->i_l_slot = value & 0xF; + else + max98390->i_l_slot = 1; +} + +static int max98390_i2c_probe(struct i2c_client *i2c) +{ + int ret = 0; + int reg = 0; + + struct max98390_priv *max98390 = NULL; + struct i2c_adapter *adapter = i2c->adapter; + struct gpio_desc *reset_gpio; + + ret = i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE + | I2C_FUNC_SMBUS_BYTE_DATA); + if (!ret) { + dev_err(&i2c->dev, "I2C check functionality failed\n"); + return -ENXIO; + } + + max98390 = devm_kzalloc(&i2c->dev, sizeof(*max98390), GFP_KERNEL); + if (!max98390) { + ret = -ENOMEM; + return ret; + } + i2c_set_clientdata(i2c, max98390); + + ret = device_property_read_u32(&i2c->dev, "maxim,temperature_calib", + &max98390->ambient_temp_value); + if (ret) { + dev_info(&i2c->dev, + "no optional property 'temperature_calib' found, default:\n"); + } + ret = device_property_read_u32(&i2c->dev, "maxim,r0_calib", + &max98390->ref_rdc_value); + if (ret) { + dev_info(&i2c->dev, + "no optional property 'r0_calib' found, default:\n"); + } + + dev_info(&i2c->dev, + "%s: r0_calib: 0x%x,temperature_calib: 0x%x", + __func__, max98390->ref_rdc_value, + max98390->ambient_temp_value); + + ret = device_property_read_string(&i2c->dev, "maxim,dsm_param_name", + &max98390->dsm_param_name); + if (ret) + max98390->dsm_param_name = "default"; + + /* voltage/current slot configuration */ + max98390_slot_config(i2c, max98390); + + /* regmap initialization */ + max98390->regmap = devm_regmap_init_i2c(i2c, &max98390_regmap); + if (IS_ERR(max98390->regmap)) { + ret = PTR_ERR(max98390->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + + reset_gpio = devm_gpiod_get_optional(&i2c->dev, + "reset", GPIOD_OUT_HIGH); + + /* Power on device */ + if (reset_gpio) { + usleep_range(1000, 2000); + /* bring out of reset */ + gpiod_set_value_cansleep(reset_gpio, 0); + usleep_range(1000, 2000); + } + + /* Check Revision ID */ + ret = regmap_read(max98390->regmap, + MAX98390_R24FF_REV_ID, ®); + if (ret) { + dev_err(&i2c->dev, + "ret=%d, Failed to read: 0x%02X\n", + ret, MAX98390_R24FF_REV_ID); + return ret; + } + dev_info(&i2c->dev, "MAX98390 revisionID: 0x%02X\n", reg); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_max98390, + max98390_dai, ARRAY_SIZE(max98390_dai)); + + return ret; +} + +static const struct i2c_device_id max98390_i2c_id[] = { + { "max98390", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, max98390_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id max98390_of_match[] = { + { .compatible = "maxim,max98390", }, + {} +}; +MODULE_DEVICE_TABLE(of, max98390_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98390_acpi_match[] = { + { "MX98390", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98390_acpi_match); +#endif + +static struct i2c_driver max98390_i2c_driver = { + .driver = { + .name = "max98390", + .of_match_table = of_match_ptr(max98390_of_match), + .acpi_match_table = ACPI_PTR(max98390_acpi_match), + .pm = &max98390_pm, + }, + .probe = max98390_i2c_probe, + .id_table = max98390_i2c_id, +}; + +module_i2c_driver(max98390_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98390 driver"); +MODULE_AUTHOR("Steve Lee "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98390.h b/sound/soc/codecs/max98390.h new file mode 100644 index 0000000000..f4d6758ab4 --- /dev/null +++ b/sound/soc/codecs/max98390.h @@ -0,0 +1,667 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020, Maxim Integrated. + */ + +#ifndef _MAX98390_H +#define _MAX98390_H + +/* MAX98390 Register Address */ +#define MAX98390_SOFTWARE_RESET 0x2000 +#define MAX98390_INT_RAW1 0x2002 +#define MAX98390_INT_RAW2 0x2003 +#define MAX98390_INT_RAW3 0x2004 +#define MAX98390_INT_STATE1 0x2005 +#define MAX98390_INT_STATE2 0x2006 +#define MAX98390_INT_STATE3 0x2007 +#define MAX98390_INT_FLAG1 0x2008 +#define MAX98390_INT_FLAG2 0x2009 +#define MAX98390_INT_FLAG3 0x200a +#define MAX98390_INT_EN1 0x200b +#define MAX98390_INT_EN2 0x200c +#define MAX98390_INT_EN3 0x200d +#define MAX98390_INT_FLAG_CLR1 0x200e +#define MAX98390_INT_FLAG_CLR2 0x200f +#define MAX98390_INT_FLAG_CLR3 0x2010 +#define MAX98390_IRQ_CTRL 0x2011 +#define MAX98390_CLK_MON 0x2012 +#define MAX98390_DAT_MON 0x2014 +#define MAX98390_WDOG_CTRL 0x2015 +#define MAX98390_WDOG_RST 0x2016 +#define MAX98390_MEAS_ADC_THERM_WARN_THRESH 0x2017 +#define MAX98390_MEAS_ADC_THERM_SHDN_THRESH 0x2018 +#define MAX98390_MEAS_ADC_THERM_HYSTERESIS 0x2019 +#define MAX98390_PIN_CFG 0x201a +#define MAX98390_PCM_RX_EN_A 0x201b +#define MAX98390_PCM_RX_EN_B 0x201c +#define MAX98390_PCM_TX_EN_A 0x201d +#define MAX98390_PCM_TX_EN_B 0x201e +#define MAX98390_PCM_TX_HIZ_CTRL_A 0x201f +#define MAX98390_PCM_TX_HIZ_CTRL_B 0x2020 +#define MAX98390_PCM_CH_SRC_1 0x2021 +#define MAX98390_PCM_CH_SRC_2 0x2022 +#define MAX98390_PCM_CH_SRC_3 0x2023 +#define MAX98390_PCM_MODE_CFG 0x2024 +#define MAX98390_PCM_MASTER_MODE 0x2025 +#define MAX98390_PCM_CLK_SETUP 0x2026 +#define MAX98390_PCM_SR_SETUP 0x2027 +#define MAX98390_ICC_RX_EN_A 0x202c +#define MAX98390_ICC_RX_EN_B 0x202d +#define MAX98390_ICC_TX_EN_A 0x202e +#define MAX98390_ICC_TX_EN_B 0x202f +#define MAX98390_ICC_HIZ_MANUAL_MODE 0x2030 +#define MAX98390_ICC_TX_HIZ_EN_A 0x2031 +#define MAX98390_ICC_TX_HIZ_EN_B 0x2032 +#define MAX98390_ICC_LNK_EN 0x2033 +#define MAX98390_R2039_AMP_DSP_CFG 0x2039 +#define MAX98390_R203A_AMP_EN 0x203a +#define MAX98390_TONE_GEN_DC_CFG 0x203b +#define MAX98390_SPK_SRC_SEL 0x203c +#define MAX98390_R203D_SPK_GAIN 0x203d +#define MAX98390_SSM_CFG 0x203e +#define MAX98390_MEAS_EN 0x203f +#define MAX98390_MEAS_DSP_CFG 0x2040 +#define MAX98390_BOOST_CTRL0 0x2041 +#define MAX98390_BOOST_CTRL3 0x2042 +#define MAX98390_BOOST_CTRL1 0x2043 +#define MAX98390_MEAS_ADC_CFG 0x2044 +#define MAX98390_MEAS_ADC_BASE_MSB 0x2045 +#define MAX98390_MEAS_ADC_BASE_LSB 0x2046 +#define MAX98390_ADC_CH0_DIVIDE 0x2047 +#define MAX98390_ADC_CH1_DIVIDE 0x2048 +#define MAX98390_ADC_CH2_DIVIDE 0x2049 +#define MAX98390_ADC_CH0_FILT_CFG 0x204a +#define MAX98390_ADC_CH1_FILT_CFG 0x204b +#define MAX98390_ADC_CH2_FILT_CFG 0x204c +#define MAX98390_MEAS_ADC_CH0_READ 0x204d +#define MAX98390_MEAS_ADC_CH1_READ 0x204e +#define MAX98390_MEAS_ADC_CH2_READ 0x204f +#define MAX98390_PWR_GATE_CTL 0x2050 +#define MAX98390_PWR_GATE_STATUS 0x2051 +#define MAX98390_VBAT_LOW_STATUS 0x2052 +#define MAX98390_PVDD_LOW_STATUS 0x2053 +#define MAX98390_BROWNOUT_STATUS 0x2054 +#define MAX98390_BROWNOUT_EN 0x2055 +#define MAX98390_BROWNOUT_INFINITE_HOLD 0x2056 +#define MAX98390_BROWNOUT_INFINITE_HOLD_CLR 0x2057 +#define MAX98390_BROWNOUT_LVL_HOLD 0x2058 +#define MAX98390_BROWNOUT_LVL1_THRESH 0x2059 +#define MAX98390_BROWNOUT_LVL2_THRESH 0x205a +#define MAX98390_BROWNOUT_LVL3_THRESH 0x205b +#define MAX98390_BROWNOUT_LVL4_THRESH 0x205c +#define MAX98390_BROWNOUT_THRESH_HYSTERYSIS 0x205d +#define MAX98390_BROWNOUT_AMP_LIMITER_ATK_REL 0x205e +#define MAX98390_BROWNOUT_AMP_GAIN_ATK_REL 0x205f +#define MAX98390_BROWNOUT_AMP1_CLIP_MODE 0x2060 +#define MAX98390_BROWNOUT_LVL1_CUR_LIMIT 0x2061 +#define MAX98390_BROWNOUT_LVL1_AMP1_CTRL1 0x2062 +#define MAX98390_BROWNOUT_LVL1_AMP1_CTRL2 0x2063 +#define MAX98390_BROWNOUT_LVL1_AMP1_CTRL3 0x2064 +#define MAX98390_BROWNOUT_LVL2_CUR_LIMIT 0x2065 +#define MAX98390_BROWNOUT_LVL2_AMP1_CTRL1 0x2066 +#define MAX98390_BROWNOUT_LVL2_AMP1_CTRL2 0x2067 +#define MAX98390_BROWNOUT_LVL2_AMP1_CTRL3 0x2068 +#define MAX98390_BROWNOUT_LVL3_CUR_LIMIT 0x2069 +#define MAX98390_BROWNOUT_LVL3_AMP1_CTRL1 0x206a +#define MAX98390_BROWNOUT_LVL3_AMP1_CTRL2 0x206b +#define MAX98390_BROWNOUT_LVL3_AMP1_CTRL3 0x206c +#define MAX98390_BROWNOUT_LVL4_CUR_LIMIT 0x206d +#define MAX98390_BROWNOUT_LVL4_AMP1_CTRL1 0x206e +#define MAX98390_BROWNOUT_LVL4_AMP1_CTRL2 0x206f +#define MAX98390_BROWNOUT_LVL4_AMP1_CTRL3 0x2070 +#define MAX98390_BROWNOUT_LOWEST_STATUS 0x2071 +#define MAX98390_BROWNOUT_ILIM_HLD 0x2072 +#define MAX98390_BROWNOUT_LIM_HLD 0x2073 +#define MAX98390_BROWNOUT_CLIP_HLD 0x2074 +#define MAX98390_BROWNOUT_GAIN_HLD 0x2075 +#define MAX98390_ENV_TRACK_VOUT_HEADROOM 0x2076 +#define MAX98390_ENV_TRACK_BOOST_VOUT_DELAY 0x2077 +#define MAX98390_ENV_TRACK_REL_RATE 0x2078 +#define MAX98390_ENV_TRACK_HOLD_RATE 0x2079 +#define MAX98390_ENV_TRACK_CTRL 0x207a +#define MAX98390_ENV_TRACK_BOOST_VOUT_READ 0x207b +#define MAX98390_BOOST_BYPASS1 0x207c +#define MAX98390_BOOST_BYPASS2 0x207d +#define MAX98390_BOOST_BYPASS3 0x207e +#define MAX98390_FET_SCALING1 0x207f +#define MAX98390_FET_SCALING2 0x2080 +#define MAX98390_FET_SCALING3 0x2081 +#define MAX98390_FET_SCALING4 0x2082 +#define MAX98390_SPK_SPEEDUP 0x2084 + +#define DSM_STBASS_HPF_B0_BYTE0 0x2101 +#define DSM_STBASS_HPF_B0_BYTE1 0x2102 +#define DSM_STBASS_HPF_B0_BYTE2 0x2103 +#define DSM_STBASS_HPF_B1_BYTE0 0x2105 +#define DSM_STBASS_HPF_B1_BYTE1 0x2106 +#define DSM_STBASS_HPF_B1_BYTE2 0x2107 +#define DSM_STBASS_HPF_B2_BYTE0 0x2109 +#define DSM_STBASS_HPF_B2_BYTE1 0x210a +#define DSM_STBASS_HPF_B2_BYTE2 0x210b +#define DSM_STBASS_HPF_A1_BYTE0 0x210d +#define DSM_STBASS_HPF_A1_BYTE1 0x210e +#define DSM_STBASS_HPF_A1_BYTE2 0x210f +#define DSM_STBASS_HPF_A2_BYTE0 0x2111 +#define DSM_STBASS_HPF_A2_BYTE1 0x2112 +#define DSM_STBASS_HPF_A2_BYTE2 0x2113 +#define DSM_STBASS_LPF_B0_BYTE0 0x2115 +#define DSM_STBASS_LPF_B0_BYTE1 0x2116 +#define DSM_STBASS_LPF_B0_BYTE2 0x2117 +#define DSM_STBASS_LPF_B1_BYTE0 0x2119 +#define DSM_STBASS_LPF_B1_BYTE1 0x211a +#define DSM_STBASS_LPF_B1_BYTE2 0x211b +#define DSM_STBASS_LPF_B2_BYTE0 0x211d +#define DSM_STBASS_LPF_B2_BYTE1 0x211e +#define DSM_STBASS_LPF_B2_BYTE2 0x211f +#define DSM_STBASS_LPF_A1_BYTE0 0x2121 +#define DSM_STBASS_LPF_A1_BYTE1 0x2122 +#define DSM_STBASS_LPF_A1_BYTE2 0x2123 +#define DSM_STBASS_LPF_A2_BYTE0 0x2125 +#define DSM_STBASS_LPF_A2_BYTE1 0x2126 +#define DSM_STBASS_LPF_A2_BYTE2 0x2127 +#define DSM_EQ_BQ1_B0_BYTE0 0x2129 +#define DSM_EQ_BQ1_B0_BYTE1 0x212a +#define DSM_EQ_BQ1_B0_BYTE2 0x212b +#define DSM_EQ_BQ1_B1_BYTE0 0x212d +#define DSM_EQ_BQ1_B1_BYTE1 0x212e +#define DSM_EQ_BQ1_B1_BYTE2 0x212f +#define DSM_EQ_BQ1_B2_BYTE0 0x2131 +#define DSM_EQ_BQ1_B2_BYTE1 0x2132 +#define DSM_EQ_BQ1_B2_BYTE2 0x2133 +#define DSM_EQ_BQ1_A1_BYTE0 0x2135 +#define DSM_EQ_BQ1_A1_BYTE1 0x2136 +#define DSM_EQ_BQ1_A1_BYTE2 0x2137 +#define DSM_EQ_BQ1_A2_BYTE0 0x2139 +#define DSM_EQ_BQ1_A2_BYTE1 0x213a +#define DSM_EQ_BQ1_A2_BYTE2 0x213b +#define DSM_EQ_BQ2_B0_BYTE0 0x213d +#define DSM_EQ_BQ2_B0_BYTE1 0x213e +#define DSM_EQ_BQ2_B0_BYTE2 0x213f +#define DSM_EQ_BQ2_B1_BYTE0 0x2141 +#define DSM_EQ_BQ2_B1_BYTE1 0x2142 +#define DSM_EQ_BQ2_B1_BYTE2 0x2143 +#define DSM_EQ_BQ2_B2_BYTE0 0x2145 +#define DSM_EQ_BQ2_B2_BYTE1 0x2146 +#define DSM_EQ_BQ2_B2_BYTE2 0x2147 +#define DSM_EQ_BQ2_A1_BYTE0 0x2149 +#define DSM_EQ_BQ2_A1_BYTE1 0x214a +#define DSM_EQ_BQ2_A1_BYTE2 0x214b +#define DSM_EQ_BQ2_A2_BYTE0 0x214d +#define DSM_EQ_BQ2_A2_BYTE1 0x214e +#define DSM_EQ_BQ2_A2_BYTE2 0x214f +#define DSM_EQ_BQ3_B0_BYTE0 0x2151 +#define DSM_EQ_BQ3_B0_BYTE1 0x2152 +#define DSM_EQ_BQ3_B0_BYTE2 0x2153 +#define DSM_EQ_BQ3_B1_BYTE0 0x2155 +#define DSM_EQ_BQ3_B1_BYTE1 0x2156 +#define DSM_EQ_BQ3_B1_BYTE2 0x2157 +#define DSM_EQ_BQ3_B2_BYTE0 0x2159 +#define DSM_EQ_BQ3_B2_BYTE1 0x215a +#define DSM_EQ_BQ3_B2_BYTE2 0x215b +#define DSM_EQ_BQ3_A1_BYTE0 0x215d +#define DSM_EQ_BQ3_A1_BYTE1 0x215e +#define DSM_EQ_BQ3_A1_BYTE2 0x215f +#define DSM_EQ_BQ3_A2_BYTE0 0x2161 +#define DSM_EQ_BQ3_A2_BYTE1 0x2162 +#define DSM_EQ_BQ3_A2_BYTE2 0x2163 +#define DSM_EQ_BQ4_B0_BYTE0 0x2165 +#define DSM_EQ_BQ4_B0_BYTE1 0x2166 +#define DSM_EQ_BQ4_B0_BYTE2 0x2167 +#define DSM_EQ_BQ4_B1_BYTE0 0x2169 +#define DSM_EQ_BQ4_B1_BYTE1 0x216a +#define DSM_EQ_BQ4_B1_BYTE2 0x216b +#define DSM_EQ_BQ4_B2_BYTE0 0x216d +#define DSM_EQ_BQ4_B2_BYTE1 0x216e +#define DSM_EQ_BQ4_B2_BYTE2 0x216f +#define DSM_EQ_BQ4_A1_BYTE0 0x2171 +#define DSM_EQ_BQ4_A1_BYTE1 0x2172 +#define DSM_EQ_BQ4_A1_BYTE2 0x2173 +#define DSM_EQ_BQ4_A2_BYTE0 0x2175 +#define DSM_EQ_BQ4_A2_BYTE1 0x2176 +#define DSM_EQ_BQ4_A2_BYTE2 0x2177 +#define DSM_EQ_BQ5_B0_BYTE0 0x2179 +#define DSM_EQ_BQ5_B0_BYTE1 0x217a +#define DSM_EQ_BQ5_B0_BYTE2 0x217b +#define DSM_EQ_BQ5_B1_BYTE0 0x217d +#define DSM_EQ_BQ5_B1_BYTE1 0x217e +#define DSM_EQ_BQ5_B1_BYTE2 0x217f +#define DSM_EQ_BQ5_B2_BYTE0 0x2181 +#define DSM_EQ_BQ5_B2_BYTE1 0x2182 +#define DSM_EQ_BQ5_B2_BYTE2 0x2183 +#define DSM_EQ_BQ5_A1_BYTE0 0x2185 +#define DSM_EQ_BQ5_A1_BYTE1 0x2186 +#define DSM_EQ_BQ5_A1_BYTE2 0x2187 +#define DSM_EQ_BQ5_A2_BYTE0 0x2189 +#define DSM_EQ_BQ5_A2_BYTE1 0x218a +#define DSM_EQ_BQ5_A2_BYTE2 0x218b +#define DSM_EQ_BQ6_B0_BYTE0 0x218d +#define DSM_EQ_BQ6_B0_BYTE1 0x218e +#define DSM_EQ_BQ6_B0_BYTE2 0x218f +#define DSM_EQ_BQ6_B1_BYTE0 0x2191 +#define DSM_EQ_BQ6_B1_BYTE1 0x2192 +#define DSM_EQ_BQ6_B1_BYTE2 0x2193 +#define DSM_EQ_BQ6_B2_BYTE0 0x2195 +#define DSM_EQ_BQ6_B2_BYTE1 0x2196 +#define DSM_EQ_BQ6_B2_BYTE2 0x2197 +#define DSM_EQ_BQ6_A1_BYTE0 0x2199 +#define DSM_EQ_BQ6_A1_BYTE1 0x219a +#define DSM_EQ_BQ6_A1_BYTE2 0x219b +#define DSM_EQ_BQ6_A2_BYTE0 0x219d +#define DSM_EQ_BQ6_A2_BYTE1 0x219e +#define DSM_EQ_BQ6_A2_BYTE2 0x219f +#define DSM_EQ_BQ7_B0_BYTE0 0x21a1 +#define DSM_EQ_BQ7_B0_BYTE1 0x21a2 +#define DSM_EQ_BQ7_B0_BYTE2 0x21a3 +#define DSM_EQ_BQ7_B1_BYTE0 0x21a5 +#define DSM_EQ_BQ7_B1_BYTE1 0x21a6 +#define DSM_EQ_BQ7_B1_BYTE2 0x21a7 +#define DSM_EQ_BQ7_B2_BYTE0 0x21a9 +#define DSM_EQ_BQ7_B2_BYTE1 0x21aa +#define DSM_EQ_BQ7_B2_BYTE2 0x21ab +#define DSM_EQ_BQ7_A1_BYTE0 0x21ad +#define DSM_EQ_BQ7_A1_BYTE1 0x21ae +#define DSM_EQ_BQ7_A1_BYTE2 0x21af +#define DSM_EQ_BQ7_A2_BYTE0 0x21b1 +#define DSM_EQ_BQ7_A2_BYTE1 0x21b2 +#define DSM_EQ_BQ7_A2_BYTE2 0x21b3 +#define DSM_EQ_BQ8_B0_BYTE0 0x21b5 +#define DSM_EQ_BQ8_B0_BYTE1 0x21b6 +#define DSM_EQ_BQ8_B0_BYTE2 0x21b7 +#define DSM_EQ_BQ8_B1_BYTE0 0x21b9 +#define DSM_EQ_BQ8_B1_BYTE1 0x21ba +#define DSM_EQ_BQ8_B1_BYTE2 0x21bb +#define DSM_EQ_BQ8_B2_BYTE0 0x21bd +#define DSM_EQ_BQ8_B2_BYTE1 0x21be +#define DSM_EQ_BQ8_B2_BYTE2 0x21bf +#define DSM_EQ_BQ8_A1_BYTE0 0x21c1 +#define DSM_EQ_BQ8_A1_BYTE1 0x21c2 +#define DSM_EQ_BQ8_A1_BYTE2 0x21c3 +#define DSM_EQ_BQ8_A2_BYTE0 0x21c5 +#define DSM_EQ_BQ8_A2_BYTE1 0x21c6 +#define DSM_EQ_BQ8_A2_BYTE2 0x21c7 +#define DSM_LFX_BQ_B0_BYTE0 0x21c9 +#define DSM_LFX_BQ_B0_BYTE1 0x21ca +#define DSM_LFX_BQ_B0_BYTE2 0x21cb +#define DSM_LFX_BQ_B1_BYTE0 0x21cd +#define DSM_LFX_BQ_B1_BYTE1 0x21ce +#define DSM_LFX_BQ_B1_BYTE2 0x21cf +#define DSM_LFX_BQ_B2_BYTE0 0x21d1 +#define DSM_LFX_BQ_B2_BYTE1 0x21d2 +#define DSM_LFX_BQ_B2_BYTE2 0x21d3 +#define DSM_LFX_BQ_A1_BYTE0 0x21d5 +#define DSM_LFX_BQ_A1_BYTE1 0x21d6 +#define DSM_LFX_BQ_A1_BYTE2 0x21d7 +#define DSM_LFX_BQ_A2_BYTE0 0x21d9 +#define DSM_LFX_BQ_A2_BYTE1 0x21da +#define DSM_LFX_BQ_A2_BYTE2 0x21db +#define DSM_PPR_HPF_B0_BYTE0 0x21dd +#define DSM_PPR_HPF_B0_BYTE1 0x21de +#define DSM_PPR_HPF_B0_BYTE2 0x21df +#define DSM_PPR_HPF_B1_BYTE0 0x21e1 +#define DSM_PPR_HPF_B1_BYTE1 0x21e2 +#define DSM_PPR_HPF_B1_BYTE2 0x21e3 +#define DSM_PPR_HPF_B2_BYTE0 0x21e5 +#define DSM_PPR_HPF_B2_BYTE1 0x21e6 +#define DSM_PPR_HPF_B2_BYTE2 0x21e7 +#define DSM_PPR_HPF_A1_BYTE0 0x21e9 +#define DSM_PPR_HPF_A1_BYTE1 0x21ea +#define DSM_PPR_HPF_A1_BYTE2 0x21eb +#define DSM_PPR_HPF_A2_BYTE0 0x21ed +#define DSM_PPR_HPF_A2_BYTE1 0x21ee +#define DSM_PPR_HPF_A2_BYTE2 0x21ef +#define DSM_PPR_LPF_B0_BYTE0 0x21f1 +#define DSM_PPR_LPF_B0_BYTE1 0x21f2 +#define DSM_PPR_LPF_B0_BYTE2 0x21f3 +#define DSM_PPR_LPF_B1_BYTE0 0x21f5 +#define DSM_PPR_LPF_B1_BYTE1 0x21f6 +#define DSM_PPR_LPF_B1_BYTE2 0x21f7 +#define DSM_PPR_LPF_B2_BYTE0 0x21f9 +#define DSM_PPR_LPF_B2_BYTE1 0x21fa +#define DSM_PPR_LPF_B2_BYTE2 0x21fb +#define DSM_PPR_LPF_A1_BYTE0 0x21fd +#define DSM_PPR_LPF_A1_BYTE1 0x21fe +#define DSM_PPR_LPF_A1_BYTE2 0x21ff +#define DSM_PPR_LPF_A2_BYTE0 0x2201 +#define DSM_PPR_LPF_A2_BYTE1 0x2202 +#define DSM_PPR_LPF_A2_BYTE2 0x2203 +#define DSM_SPL_BQ_B0_BYTE0 0x2205 +#define DSM_SPL_BQ_B0_BYTE1 0x2206 +#define DSM_SPL_BQ_B0_BYTE2 0x2207 +#define DSM_SPL_BQ_B1_BYTE0 0x2209 +#define DSM_SPL_BQ_B1_BYTE1 0x220a +#define DSM_SPL_BQ_B1_BYTE2 0x220b +#define DSM_SPL_BQ_B2_BYTE0 0x220d +#define DSM_SPL_BQ_B2_BYTE1 0x220e +#define DSM_SPL_BQ_B2_BYTE2 0x220f +#define DSM_SPL_BQ_A1_BYTE0 0x2211 +#define DSM_SPL_BQ_A1_BYTE1 0x2212 +#define DSM_SPL_BQ_A1_BYTE2 0x2213 +#define DSM_SPL_BQ_A2_BYTE0 0x2215 +#define DSM_SPL_BQ_A2_BYTE1 0x2216 +#define DSM_SPL_BQ_A2_BYTE2 0x2217 +#define DSM_EXCUR_BQ_B0_BYTE0 0x2219 +#define DSM_EXCUR_BQ_B0_BYTE1 0x221a +#define DSM_EXCUR_BQ_B0_BYTE2 0x221b +#define DSM_EXCUR_BQ_B1_BYTE0 0x221d +#define DSM_EXCUR_BQ_B1_BYTE1 0x221e +#define DSM_EXCUR_BQ_B1_BYTE2 0x221f +#define DSM_EXCUR_BQ_B2_BYTE0 0x2221 +#define DSM_EXCUR_BQ_B2_BYTE1 0x2222 +#define DSM_EXCUR_BQ_B2_BYTE2 0x2223 +#define DSM_EXCUR_BQ_A1_BYTE0 0x2225 +#define DSM_EXCUR_BQ_A1_BYTE1 0x2226 +#define DSM_EXCUR_BQ_A1_BYTE2 0x2227 +#define DSM_EXCUR_BQ_A2_BYTE0 0x2229 +#define DSM_EXCUR_BQ_A2_BYTE1 0x222a +#define DSM_EXCUR_BQ_A2_BYTE2 0x222b +#define DSM_EXCPROT_HPF1_B0_BYTE0 0x222d +#define DSM_EXCPROT_HPF1_B0_BYTE1 0x222e +#define DSM_EXCPROT_HPF1_B0_BYTE2 0x222f +#define DSM_EXCPROT_HPF1_B1_BYTE0 0x2231 +#define DSM_EXCPROT_HPF1_B1_BYTE1 0x2232 +#define DSM_EXCPROT_HPF1_B1_BYTE2 0x2233 +#define DSM_EXCPROT_HPF1_B2_BYTE0 0x2235 +#define DSM_EXCPROT_HPF1_B2_BYTE1 0x2236 +#define DSM_EXCPROT_HPF1_B2_BYTE2 0x2237 +#define DSM_EXCPROT_HPF1_A1_BYTE0 0x2239 +#define DSM_EXCPROT_HPF1_A1_BYTE1 0x223a +#define DSM_EXCPROT_HPF1_A1_BYTE2 0x223b +#define DSM_EXCPROT_HPF1_A2_BYTE0 0x223d +#define DSM_EXCPROT_HPF1_A2_BYTE1 0x223e +#define DSM_EXCPROT_HPF1_A2_BYTE2 0x223f +#define DSM_EXCPROT_HPF2_B0_BYTE0 0x2241 +#define DSM_EXCPROT_HPF2_B0_BYTE1 0x2242 +#define DSM_EXCPROT_HPF2_B0_BYTE2 0x2243 +#define DSM_EXCPROT_HPF2_B1_BYTE0 0x2245 +#define DSM_EXCPROT_HPF2_B1_BYTE1 0x2246 +#define DSM_EXCPROT_HPF2_B1_BYTE2 0x2247 +#define DSM_EXCPROT_HPF2_B2_BYTE0 0x2249 +#define DSM_EXCPROT_HPF2_B2_BYTE1 0x224a +#define DSM_EXCPROT_HPF2_B2_BYTE2 0x224b +#define DSM_EXCPROT_HPF2_A1_BYTE0 0x224d +#define DSM_EXCPROT_HPF2_A1_BYTE1 0x224e +#define DSM_EXCPROT_HPF2_A1_BYTE2 0x224f +#define DSM_EXCPROT_HPF2_A2_BYTE0 0x2251 +#define DSM_EXCPROT_HPF2_A2_BYTE1 0x2252 +#define DSM_EXCPROT_HPF2_A2_BYTE2 0x2253 +#define DSM_EXCPROT_HPF3_B0_BYTE0 0x2255 +#define DSM_EXCPROT_HPF3_B0_BYTE1 0x2256 +#define DSM_EXCPROT_HPF3_B0_BYTE2 0x2257 +#define DSM_EXCPROT_HPF3_B1_BYTE0 0x2259 +#define DSM_EXCPROT_HPF3_B1_BYTE1 0x225a +#define DSM_EXCPROT_HPF3_B1_BYTE2 0x225b +#define DSM_EXCPROT_HPF3_B2_BYTE0 0x225d +#define DSM_EXCPROT_HPF3_B2_BYTE1 0x225e +#define DSM_EXCPROT_HPF3_B2_BYTE2 0x225f +#define DSM_EXCPROT_HPF3_A1_BYTE0 0x2261 +#define DSM_EXCPROT_HPF3_A1_BYTE1 0x2262 +#define DSM_EXCPROT_HPF3_A1_BYTE2 0x2263 +#define DSM_EXCPROT_HPF3_A2_BYTE0 0x2265 +#define DSM_EXCPROT_HPF3_A2_BYTE1 0x2266 +#define DSM_EXCPROT_HPF3_A2_BYTE2 0x2267 +#define DSM_EXCPROT_HPF4_B0_BYTE0 0x2269 +#define DSM_EXCPROT_HPF4_B0_BYTE1 0x226a +#define DSM_EXCPROT_HPF4_B0_BYTE2 0x226b +#define DSM_EXCPROT_HPF4_B1_BYTE0 0x226d +#define DSM_EXCPROT_HPF4_B1_BYTE1 0x226e +#define DSM_EXCPROT_HPF4_B1_BYTE2 0x226f +#define DSM_EXCPROT_HPF4_B2_BYTE0 0x2271 +#define DSM_EXCPROT_HPF4_B2_BYTE1 0x2272 +#define DSM_EXCPROT_HPF4_B2_BYTE2 0x2273 +#define DSM_EXCPROT_HPF4_A1_BYTE0 0x2275 +#define DSM_EXCPROT_HPF4_A1_BYTE1 0x2276 +#define DSM_EXCPROT_HPF4_A1_BYTE2 0x2277 +#define DSM_EXCPROT_HPF4_A2_BYTE0 0x2279 +#define DSM_EXCPROT_HPF4_A2_BYTE1 0x227a +#define DSM_EXCPROT_HPF4_A2_BYTE2 0x227b +#define DSM_EXCPROT_HPF5_B0_BYTE0 0x227d +#define DSM_EXCPROT_HPF5_B0_BYTE1 0x227e +#define DSM_EXCPROT_HPF5_B0_BYTE2 0x227f +#define DSM_EXCPROT_HPF5_B1_BYTE0 0x2281 +#define DSM_EXCPROT_HPF5_B1_BYTE1 0x2282 +#define DSM_EXCPROT_HPF5_B1_BYTE2 0x2283 +#define DSM_EXCPROT_HPF5_B2_BYTE0 0x2285 +#define DSM_EXCPROT_HPF5_B2_BYTE1 0x2286 +#define DSM_EXCPROT_HPF5_B2_BYTE2 0x2287 +#define DSM_EXCPROT_HPF5_A1_BYTE0 0x2289 +#define DSM_EXCPROT_HPF5_A1_BYTE1 0x228a +#define DSM_EXCPROT_HPF5_A1_BYTE2 0x228b +#define DSM_EXCPROT_HPF5_A2_BYTE0 0x228d +#define DSM_EXCPROT_HPF5_A2_BYTE1 0x228e +#define DSM_EXCPROT_HPF5_A2_BYTE2 0x228f +#define DSM_DEBUZZ_BPF_B0_BYTE0 0x2291 +#define DSM_DEBUZZ_BPF_B0_BYTE1 0x2292 +#define DSM_DEBUZZ_BPF_B0_BYTE2 0x2293 +#define DSM_DEBUZZ_BPF_B1_BYTE0 0x2295 +#define DSM_DEBUZZ_BPF_B1_BYTE1 0x2296 +#define DSM_DEBUZZ_BPF_B1_BYTE2 0x2297 +#define DSM_DEBUZZ_BPF_B2_BYTE0 0x2299 +#define DSM_DEBUZZ_BPF_B2_BYTE1 0x229a +#define DSM_DEBUZZ_BPF_B2_BYTE2 0x229b +#define DSM_DEBUZZ_BPF_A1_BYTE0 0x229d +#define DSM_DEBUZZ_BPF_A1_BYTE1 0x229e +#define DSM_DEBUZZ_BPF_A1_BYTE2 0x229f +#define DSM_DEBUZZ_BPF_A2_BYTE0 0x22a1 +#define DSM_DEBUZZ_BPF_A2_BYTE1 0x22a2 +#define DSM_DEBUZZ_BPF_A2_BYTE2 0x22a3 +#define DSM_DEBUZZ_PORT_B0_BYTE0 0x22a5 +#define DSM_DEBUZZ_PORT_B0_BYTE1 0x22a6 +#define DSM_DEBUZZ_PORT_B0_BYTE2 0x22a7 +#define DSM_DEBUZZ_PORT_B1_BYTE0 0x22a9 +#define DSM_DEBUZZ_PORT_B1_BYTE1 0x22aa +#define DSM_DEBUZZ_PORT_B1_BYTE2 0x22ab +#define DSM_DEBUZZ_PORT_B2_BYTE0 0x22ad +#define DSM_DEBUZZ_PORT_B2_BYTE1 0x22ae +#define DSM_DEBUZZ_PORT_B2_BYTE2 0x22af +#define DSM_DEBUZZ_PORT_A1_BYTE0 0x22b1 +#define DSM_DEBUZZ_PORT_A1_BYTE1 0x22b2 +#define DSM_DEBUZZ_PORT_A1_BYTE2 0x22b3 +#define DSM_DEBUZZ_PORT_A2_BYTE0 0x22b5 +#define DSM_DEBUZZ_PORT_A2_BYTE1 0x22b6 +#define DSM_DEBUZZ_PORT_A2_BYTE2 0x22b7 +#define DSM_DEBUZZ_NOTCH_B0_BYTE0 0x22b9 +#define DSM_DEBUZZ_NOTCH_B0_BYTE1 0x22ba +#define DSM_DEBUZZ_NOTCH_B0_BYTE2 0x22bb +#define DSM_DEBUZZ_NOTCH_B1_BYTE0 0x22bd +#define DSM_DEBUZZ_NOTCH_B1_BYTE1 0x22be +#define DSM_DEBUZZ_NOTCH_B1_BYTE2 0x22bf +#define DSM_DEBUZZ_NOTCH_B2_BYTE0 0x22c1 +#define DSM_DEBUZZ_NOTCH_B2_BYTE1 0x22c2 +#define DSM_DEBUZZ_NOTCH_B2_BYTE2 0x22c3 +#define DSM_DEBUZZ_NOTCH_A1_BYTE0 0x22c5 +#define DSM_DEBUZZ_NOTCH_A1_BYTE1 0x22c6 +#define DSM_DEBUZZ_NOTCH_A1_BYTE2 0x22c7 +#define DSM_DEBUZZ_NOTCH_A2_BYTE0 0x22c9 +#define DSM_DEBUZZ_NOTCH_A2_BYTE1 0x22ca +#define DSM_DEBUZZ_NOTCH_A2_BYTE2 0x22cb +#define DSM_THERMAL_BQ_B0_BYTE0 0x22cd +#define DSM_THERMAL_BQ_B0_BYTE1 0x22ce +#define DSM_THERMAL_BQ_B0_BYTE2 0x22cf +#define DSM_THERMAL_BQ_B1_BYTE0 0x22d1 +#define DSM_THERMAL_BQ_B1_BYTE1 0x22d2 +#define DSM_THERMAL_BQ_B1_BYTE2 0x22d3 +#define DSM_THERMAL_BQ_B2_BYTE0 0x22d5 +#define DSM_THERMAL_BQ_B2_BYTE1 0x22d6 +#define DSM_THERMAL_BQ_B2_BYTE2 0x22d7 +#define DSM_THERMAL_BQ_A1_BYTE0 0x22d9 +#define DSM_THERMAL_BQ_A1_BYTE1 0x22da +#define DSM_THERMAL_BQ_A1_BYTE2 0x22db +#define DSM_THERMAL_BQ_A2_BYTE0 0x22dd +#define DSM_THERMAL_BQ_A2_BYTE1 0x22de +#define DSM_THERMAL_BQ_A2_BYTE2 0x22df +#define DSM_WBDRC_FILT1_B0_BYTE0 0x22e1 +#define DSM_WBDRC_FILT1_B0_BYTE1 0x22e2 +#define DSM_WBDRC_FILT1_B0_BYTE2 0x22e3 +#define DSM_WBDRC_FILT1_B1_BYTE0 0x22e5 +#define DSM_WBDRC_FILT1_B1_BYTE1 0x22e6 +#define DSM_WBDRC_FILT1_B1_BYTE2 0x22e7 +#define DSM_WBDRC_FILT1_B2_BYTE0 0x22e9 +#define DSM_WBDRC_FILT1_B2_BYTE1 0x22ea +#define DSM_WBDRC_FILT1_B2_BYTE2 0x22eb +#define DSM_WBDRC_FILT1_A1_BYTE0 0x22ed +#define DSM_WBDRC_FILT1_A1_BYTE1 0x22ee +#define DSM_WBDRC_FILT1_A1_BYTE2 0x22ef +#define DSM_WBDRC_FILT1_A2_BYTE0 0x22f1 +#define DSM_WBDRC_FILT1_A2_BYTE1 0x22f2 +#define DSM_WBDRC_FILT1_A2_BYTE2 0x22f3 +#define DSM_WBDRC_FILT2_B0_BYTE0 0x22f5 +#define DSM_WBDRC_FILT2_B0_BYTE1 0x22f6 +#define DSM_WBDRC_FILT2_B0_BYTE2 0x22f7 +#define DSM_WBDRC_FILT2_B1_BYTE0 0x22f9 +#define DSM_WBDRC_FILT2_B1_BYTE1 0x22fa +#define DSM_WBDRC_FILT2_B1_BYTE2 0x22fb +#define DSM_WBDRC_FILT2_B2_BYTE0 0x22fd +#define DSM_WBDRC_FILT2_B2_BYTE1 0x22fe +#define DSM_WBDRC_FILT2_B2_BYTE2 0x22ff +#define DSM_WBDRC_FILT2_A1_BYTE0 0x2301 +#define DSM_WBDRC_FILT2_A1_BYTE1 0x2302 +#define DSM_WBDRC_FILT2_A1_BYTE2 0x2303 +#define DSM_WBDRC_FILT2_A2_BYTE0 0x2305 +#define DSM_WBDRC_FILT2_A2_BYTE1 0x2306 +#define DSM_WBDRC_FILT2_A2_BYTE2 0x2307 +#define DSM_PPR_RELEASE_TIME_BYTE0 0x2309 +#define DSM_PPR_RELEASE_TIME_BYTE1 0x230a +#define DSM_PPR_RELEASE_TIME_BYTE2 0x230b +#define DSM_PPR_ATTACK_TIME_BYTE0 0x230d +#define DSM_PPR_ATTACK_TIME_BYTE1 0x230e +#define DSM_PPR_ATTACK_TIME_BYTE2 0x230f +#define DSM_DEBUZZER_RELEASE_TIME_BYTE0 0x2311 +#define DSM_DEBUZZER_RELEASE_TIME_BYTE1 0x2312 +#define DSM_DEBUZZER_RELEASE_TIME_BYTE2 0x2313 +#define DSM_DEBUZZER_ATTACK_TIME_BYTE0 0x2315 +#define DSM_DEBUZZER_ATTACK_TIME_BYTE1 0x2316 +#define DSM_DEBUZZER_ATTACK_TIME_BYTE2 0x2317 + +#define DSMIG_WB_DRC_RELEASE_TIME_1 0x2380 +#define DSMIG_WB_DRC_RELEASE_TIME_2 0x2381 +#define DSMIG_WB_DRC_ATTACK_TIME_1 0x2382 +#define DSMIG_WB_DRC_ATTACK_TIME_2 0x2383 +#define DSMIG_WB_DRC_COMPRESSION_RATIO 0x2384 +#define DSMIG_WB_DRC_COMPRESSION_THRESHOLD 0x2385 +#define DSMIG_WB_DRC_MAKEUPGAIN 0x2386 +#define DSMIG_WB_DRC_NOISE_GATE_THRESHOLD 0x2387 +#define DSMIG_WBDRC_HPF_ENABLE 0x2388 +#define DSMIG_WB_DRC_TEST_SMOOTHER_OUT_EN 0x2389 +#define DSMIG_PPR_THRESHOLD 0x238b +#define DSM_STEREO_BASS_CHANNEL_SELECT 0x238d +#define DSM_TPROT_THRESHOLD_BYTE0 0x238e +#define DSM_TPROT_THRESHOLD_BYTE1 0x238f +#define DSM_TPROT_ROOM_TEMPERATURE_BYTE0 0x2390 +#define DSM_TPROT_ROOM_TEMPERATURE_BYTE1 0x2391 +#define DSM_TPROT_RECIP_RDC_ROOM_BYTE0 0x2392 +#define DSM_TPROT_RECIP_RDC_ROOM_BYTE1 0x2393 +#define DSM_TPROT_RECIP_RDC_ROOM_BYTE2 0x2394 +#define DSM_TPROT_RECIP_TCONST_BYTE0 0x2395 +#define DSM_TPROT_RECIP_TCONST_BYTE1 0x2396 +#define DSM_TPROT_RECIP_TCONST_BYTE2 0x2397 +#define DSM_THERMAL_ATTENUATION_SETTINGS 0x2398 +#define DSM_THERMAL_PILOT_TONE_ATTENUATION 0x2399 +#define DSM_TPROT_PG_TEMP_THRESH_BYTE0 0x239a +#define DSM_TPROT_PG_TEMP_THRESH_BYTE1 0x239b + +#define THERMAL_RDC_RD_BACK_BYTE1 0x239c +#define THERMAL_RDC_RD_BACK_BYTE0 0x239d +#define THERMAL_COILTEMP_RD_BACK_BYTE1 0x239e +#define THERMAL_COILTEMP_RD_BACK_BYTE0 0x239f + +#define DSMIG_DEBUZZER_THRESHOLD 0x23b5 +#define DSMIG_DEBUZZER_ALPHA_COEF_TEST_ONLY 0x23b6 +#define DSM_VOL_ENA 0x23b9 +#define DSM_VOL_CTRL 0x23ba + +#define DSMIG_EN 0x23e0 +#define MAX98390_R23E1_DSP_GLOBAL_EN 0x23e1 + +#define DSM_THERMAL_GAIN 0x23f0 +#define DSM_PPR_GAIN 0x23f1 +#define DSM_DBZ_GAIN 0x23f2 +#define DSM_WBDRC_GAIN 0x23f3 + +#define MAX98390_R23FF_GLOBAL_EN 0x23FF +#define MAX98390_R24FF_REV_ID 0x24FF + +/* MAX98390_R2021_PCM_RX_SRC_1 */ +#define MAX98390_PCM_RX_CH_SRC_SHIFT (0) +#define MAX98390_PCM_RX_CH_SRC_BASS_SHIFT (4) + +/* MAX98390_R2022_PCM_TX_SRC_1 */ +#define MAX98390_PCM_TX_CH_SRC_A_V_SHIFT (0) +#define MAX98390_PCM_TX_CH_SRC_A_I_SHIFT (4) + +/* MAX98390_R2024_PCM_DATA_FMT_CFG */ +#define MAX98390_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98390_PCM_MODE_CFG_FORMAT_SHIFT (3) +#define MAX98390_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2) +#define MAX98390_PCM_FORMAT_I2S (0x0 << 0) +#define MAX98390_PCM_FORMAT_LJ (0x1 << 0) +#define MAX98390_PCM_FORMAT_TDM_MODE0 (0x3 << 0) +#define MAX98390_PCM_FORMAT_TDM_MODE1 (0x4 << 0) +#define MAX98390_PCM_FORMAT_TDM_MODE2 (0x5 << 0) +#define MAX98390_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98390_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98390_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98390_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) + +/* MAX98390_R2039_AMP_DSP_CFG */ +#define MAX98390_AMP_DSP_CFG_RMP_UP_SHIFT (4) +#define MAX98390_AMP_DSP_CFG_RMP_DN_SHIFT (5) + +/* MAX98390_R203A_AMP_EN */ +#define MAX98390_R203A_AMP_EN_SHIFT (0) + +/* MAX98390_PCM_MASTER_MODE */ +#define MAX98390_PCM_MASTER_MODE_MASK (0x3 << 0) +#define MAX98390_PCM_MASTER_MODE_SLAVE (0x0 << 0) +#define MAX98390_PCM_MASTER_MODE_MASTER (0x3 << 0) + +#define MAX98390_PCM_MASTER_MODE_MCLK_MASK (0xF << 2) +#define MAX98390_PCM_MASTER_MODE_MCLK_RATE_SHIFT (2) + +/* PCM_CLK_SETUP */ +#define MAX98390_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 2) +#define MAX98390_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) + +/* PCM_SR_SETUP */ +#define MAX98390_PCM_SR_SET1_SR_MASK (0xF << 0) +#define MAX98390_PCM_SR_SET1_SR_8000 (0x0 << 0) +#define MAX98390_PCM_SR_SET1_SR_11025 (0x1 << 0) +#define MAX98390_PCM_SR_SET1_SR_12000 (0x2 << 0) +#define MAX98390_PCM_SR_SET1_SR_16000 (0x3 << 0) +#define MAX98390_PCM_SR_SET1_SR_22050 (0x4 << 0) +#define MAX98390_PCM_SR_SET1_SR_24000 (0x5 << 0) +#define MAX98390_PCM_SR_SET1_SR_32000 (0x6 << 0) +#define MAX98390_PCM_SR_SET1_SR_44100 (0x7 << 0) +#define MAX98390_PCM_SR_SET1_SR_48000 (0x8 << 0) + +/* PCM_TO_SPK_MONO_MIX_1 */ +#define MAX98390_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6) +#define MAX98390_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6) +#define MAX98390_PCM_TO_SPK_CH0_SRC_MASK (0xF << 0) +#define MAX98390_PCM_TO_SPK_CH1_SRC_MASK (0xF << 4) + +/* MAX98390_BOOST_CTRL3 */ +#define MAX98390_BOOST_CLK_PHASE_CFG_SHIFT (2) + +/* SOFT_RESET */ +#define MAX98390_SOFT_RESET_MASK (0x1 << 0) + +#define MAX98390_GLOBAL_EN_MASK (0x1 << 0) +#define MAX98390_AMP_EN_MASK (0x1 << 0) + +/* DSM register offset */ +#define MAX98390_DSM_PAYLOAD_OFFSET 16 +#define MAX98390_DSM_PARAM_MAX_SIZE 1024 +#define MAX98390_DSM_PARAM_MIN_SIZE 670 + +struct max98390_priv { + struct regmap *regmap; + unsigned int sysclk; + unsigned int provider; + unsigned int tdm_mode; + unsigned int v_l_slot; + unsigned int i_l_slot; + unsigned int ref_rdc_value; + unsigned int ambient_temp_value; + const char *dsm_param_name; +}; +#endif diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c new file mode 100644 index 0000000000..3a1d8c211f --- /dev/null +++ b/sound/soc/codecs/max98396.c @@ -0,0 +1,1918 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, Analog Devices Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98396.h" + +static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = { + "avdd", + "dvdd", + "dvddio", +}; + +static struct reg_default max98396_reg[] = { + {MAX98396_R2000_SW_RESET, 0x00}, + {MAX98396_R2001_INT_RAW1, 0x00}, + {MAX98396_R2002_INT_RAW2, 0x00}, + {MAX98396_R2003_INT_RAW3, 0x00}, + {MAX98396_R2004_INT_RAW4, 0x00}, + {MAX98396_R2006_INT_STATE1, 0x00}, + {MAX98396_R2007_INT_STATE2, 0x00}, + {MAX98396_R2008_INT_STATE3, 0x00}, + {MAX98396_R2009_INT_STATE4, 0x00}, + {MAX98396_R200B_INT_FLAG1, 0x00}, + {MAX98396_R200C_INT_FLAG2, 0x00}, + {MAX98396_R200D_INT_FLAG3, 0x00}, + {MAX98396_R200E_INT_FLAG4, 0x00}, + {MAX98396_R2010_INT_EN1, 0x02}, + {MAX98396_R2011_INT_EN2, 0x00}, + {MAX98396_R2012_INT_EN3, 0x00}, + {MAX98396_R2013_INT_EN4, 0x00}, + {MAX98396_R2015_INT_FLAG_CLR1, 0x00}, + {MAX98396_R2016_INT_FLAG_CLR2, 0x00}, + {MAX98396_R2017_INT_FLAG_CLR3, 0x00}, + {MAX98396_R2018_INT_FLAG_CLR4, 0x00}, + {MAX98396_R201F_IRQ_CTRL, 0x00}, + {MAX98396_R2020_THERM_WARN_THRESH, 0x46}, + {MAX98396_R2021_THERM_WARN_THRESH2, 0x46}, + {MAX98396_R2022_THERM_SHDN_THRESH, 0x64}, + {MAX98396_R2023_THERM_HYSTERESIS, 0x02}, + {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5}, + {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01}, + {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32}, + {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00}, + {MAX98396_R2038_CLK_MON_CTRL, 0x00}, + {MAX98396_R2039_DATA_MON_CTRL, 0x00}, + {MAX98396_R203F_ENABLE_CTRLS, 0x0F}, + {MAX98396_R2040_PIN_CFG, 0x55}, + {MAX98396_R2041_PCM_MODE_CFG, 0xC0}, + {MAX98396_R2042_PCM_CLK_SETUP, 0x04}, + {MAX98396_R2043_PCM_SR_SETUP, 0x88}, + {MAX98396_R2044_PCM_TX_CTRL_1, 0x00}, + {MAX98396_R2045_PCM_TX_CTRL_2, 0x00}, + {MAX98396_R2046_PCM_TX_CTRL_3, 0x00}, + {MAX98396_R2047_PCM_TX_CTRL_4, 0x00}, + {MAX98396_R2048_PCM_TX_CTRL_5, 0x00}, + {MAX98396_R2049_PCM_TX_CTRL_6, 0x00}, + {MAX98396_R204A_PCM_TX_CTRL_7, 0x00}, + {MAX98396_R204B_PCM_TX_CTRL_8, 0x00}, + {MAX98396_R204C_PCM_TX_HIZ_CTRL_1, 0xFF}, + {MAX98396_R204D_PCM_TX_HIZ_CTRL_2, 0xFF}, + {MAX98396_R204E_PCM_TX_HIZ_CTRL_3, 0xFF}, + {MAX98396_R204F_PCM_TX_HIZ_CTRL_4, 0xFF}, + {MAX98396_R2050_PCM_TX_HIZ_CTRL_5, 0xFF}, + {MAX98396_R2051_PCM_TX_HIZ_CTRL_6, 0xFF}, + {MAX98396_R2052_PCM_TX_HIZ_CTRL_7, 0xFF}, + {MAX98396_R2053_PCM_TX_HIZ_CTRL_8, 0xFF}, + {MAX98396_R2055_PCM_RX_SRC1, 0x00}, + {MAX98396_R2056_PCM_RX_SRC2, 0x00}, + {MAX98396_R2058_PCM_BYPASS_SRC, 0x00}, + {MAX98396_R205D_PCM_TX_SRC_EN, 0x00}, + {MAX98396_R205E_PCM_RX_EN, 0x00}, + {MAX98396_R205F_PCM_TX_EN, 0x00}, + {MAX98396_R2070_ICC_RX_EN_A, 0x00}, + {MAX98396_R2071_ICC_RX_EN_B, 0x00}, + {MAX98396_R2072_ICC_TX_CTRL, 0x00}, + {MAX98396_R207F_ICC_EN, 0x00}, + {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04}, + {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00}, + {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00}, + {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00}, + {MAX98396_R208F_TONE_GEN_EN, 0x00}, + {MAX98396_R2090_AMP_VOL_CTRL, 0x00}, + {MAX98396_R2091_AMP_PATH_GAIN, 0x0B}, + {MAX98396_R2092_AMP_DSP_CFG, 0x23}, + {MAX98396_R2093_SSM_CFG, 0x0D}, + {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12}, + {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17}, + {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17}, + {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00}, + {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00}, + {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03}, + {MAX98396_R209A_SPK_EDGE_CTRL, 0x00}, + {MAX98396_R209C_SPK_EDGE_CTRL1, 0x0A}, + {MAX98396_R209D_SPK_EDGE_CTRL2, 0xAA}, + {MAX98396_R209E_AMP_CLIP_GAIN, 0x00}, + {MAX98396_R209F_BYPASS_PATH_CFG, 0x00}, + {MAX98396_R20A0_AMP_SUPPLY_CTL, 0x00}, + {MAX98396_R20AF_AMP_EN, 0x00}, + {MAX98396_R20B0_ADC_SR, 0x30}, + {MAX98396_R20B1_ADC_PVDD_CFG, 0x00}, + {MAX98396_R20B2_ADC_VBAT_CFG, 0x00}, + {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00}, + {MAX98396_R20B4_ADC_READBACK_CTRL1, 0x00}, + {MAX98396_R20B5_ADC_READBACK_CTRL2, 0x00}, + {MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0x00}, + {MAX98396_R20B7_ADC_PVDD_READBACK_LSB, 0x00}, + {MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0x00}, + {MAX98396_R20B9_ADC_VBAT_READBACK_LSB, 0x00}, + {MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0x00}, + {MAX98396_R20BB_ADC_TEMP_READBACK_LSB, 0x00}, + {MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB, 0x00}, + {MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB, 0x00}, + {MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB, 0x00}, + {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00}, + {MAX98396_R20C7_ADC_CFG, 0x00}, + {MAX98396_R20D0_DHT_CFG1, 0x00}, + {MAX98396_R20D1_LIMITER_CFG1, 0x08}, + {MAX98396_R20D2_LIMITER_CFG2, 0x00}, + {MAX98396_R20D3_DHT_CFG2, 0x14}, + {MAX98396_R20D4_DHT_CFG3, 0x02}, + {MAX98396_R20D5_DHT_CFG4, 0x04}, + {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07}, + {MAX98396_R20DF_DHT_EN, 0x00}, + {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04}, + {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00}, + {MAX98396_R20E5_BPE_STATE, 0x00}, + {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00}, + {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00}, + {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00}, + {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00}, + {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00}, + {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00}, + {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00}, + {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00}, + {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00}, + {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00}, + {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00}, + {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00}, + {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00}, + {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00}, + {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00}, + {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00}, + {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00}, + {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00}, + {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00}, + {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00}, + {MAX98396_R2109_BPE_LOW_STATE, 0x00}, + {MAX98396_R210A_BPE_LOW_GAIN, 0x00}, + {MAX98396_R210B_BPE_LOW_LIMITER, 0x00}, + {MAX98396_R210D_BPE_EN, 0x00}, + {MAX98396_R210E_AUTO_RESTART, 0x00}, + {MAX98396_R210F_GLOBAL_EN, 0x00}, + {MAX98396_R21FF_REVISION_ID, 0x00}, +}; + +static struct reg_default max98397_reg[] = { + {MAX98396_R2000_SW_RESET, 0x00}, + {MAX98396_R2001_INT_RAW1, 0x00}, + {MAX98396_R2002_INT_RAW2, 0x00}, + {MAX98396_R2003_INT_RAW3, 0x00}, + {MAX98396_R2004_INT_RAW4, 0x00}, + {MAX98396_R2006_INT_STATE1, 0x00}, + {MAX98396_R2007_INT_STATE2, 0x00}, + {MAX98396_R2008_INT_STATE3, 0x00}, + {MAX98396_R2009_INT_STATE4, 0x00}, + {MAX98396_R200B_INT_FLAG1, 0x00}, + {MAX98396_R200C_INT_FLAG2, 0x00}, + {MAX98396_R200D_INT_FLAG3, 0x00}, + {MAX98396_R200E_INT_FLAG4, 0x00}, + {MAX98396_R2010_INT_EN1, 0x02}, + {MAX98396_R2011_INT_EN2, 0x00}, + {MAX98396_R2012_INT_EN3, 0x00}, + {MAX98396_R2013_INT_EN4, 0x00}, + {MAX98396_R2015_INT_FLAG_CLR1, 0x00}, + {MAX98396_R2016_INT_FLAG_CLR2, 0x00}, + {MAX98396_R2017_INT_FLAG_CLR3, 0x00}, + {MAX98396_R2018_INT_FLAG_CLR4, 0x00}, + {MAX98396_R201F_IRQ_CTRL, 0x00}, + {MAX98396_R2020_THERM_WARN_THRESH, 0x46}, + {MAX98396_R2021_THERM_WARN_THRESH2, 0x46}, + {MAX98396_R2022_THERM_SHDN_THRESH, 0x64}, + {MAX98396_R2023_THERM_HYSTERESIS, 0x02}, + {MAX98396_R2024_THERM_FOLDBACK_SET, 0xC5}, + {MAX98396_R2027_THERM_FOLDBACK_EN, 0x01}, + {MAX98396_R2030_NOISEGATE_MODE_CTRL, 0x32}, + {MAX98396_R2033_NOISEGATE_MODE_EN, 0x00}, + {MAX98396_R2038_CLK_MON_CTRL, 0x00}, + {MAX98396_R2039_DATA_MON_CTRL, 0x00}, + {MAX98397_R203A_SPK_MON_THRESH, 0x03}, + {MAX98396_R203F_ENABLE_CTRLS, 0x0F}, + {MAX98396_R2040_PIN_CFG, 0x55}, + {MAX98396_R2041_PCM_MODE_CFG, 0xC0}, + {MAX98396_R2042_PCM_CLK_SETUP, 0x04}, + {MAX98396_R2043_PCM_SR_SETUP, 0x88}, + {MAX98396_R2044_PCM_TX_CTRL_1, 0x00}, + {MAX98396_R2045_PCM_TX_CTRL_2, 0x00}, + {MAX98396_R2046_PCM_TX_CTRL_3, 0x00}, + {MAX98396_R2047_PCM_TX_CTRL_4, 0x00}, + {MAX98396_R2048_PCM_TX_CTRL_5, 0x00}, + {MAX98396_R2049_PCM_TX_CTRL_6, 0x00}, + {MAX98396_R204A_PCM_TX_CTRL_7, 0x00}, + {MAX98396_R204B_PCM_TX_CTRL_8, 0x00}, + {MAX98397_R204C_PCM_TX_CTRL_9, 0x00}, + {MAX98397_R204D_PCM_TX_HIZ_CTRL_1, 0xFF}, + {MAX98397_R204E_PCM_TX_HIZ_CTRL_2, 0xFF}, + {MAX98397_R204F_PCM_TX_HIZ_CTRL_3, 0xFF}, + {MAX98397_R2050_PCM_TX_HIZ_CTRL_4, 0xFF}, + {MAX98397_R2051_PCM_TX_HIZ_CTRL_5, 0xFF}, + {MAX98397_R2052_PCM_TX_HIZ_CTRL_6, 0xFF}, + {MAX98397_R2053_PCM_TX_HIZ_CTRL_7, 0xFF}, + {MAX98397_R2054_PCM_TX_HIZ_CTRL_8, 0xFF}, + {MAX98397_R2056_PCM_RX_SRC1, 0x00}, + {MAX98397_R2057_PCM_RX_SRC2, 0x00}, + {MAX98396_R2058_PCM_BYPASS_SRC, 0x00}, + {MAX98396_R205D_PCM_TX_SRC_EN, 0x00}, + {MAX98396_R205E_PCM_RX_EN, 0x00}, + {MAX98396_R205F_PCM_TX_EN, 0x00}, + {MAX98397_R2060_PCM_TX_SUPPLY_SEL, 0x00}, + {MAX98396_R2070_ICC_RX_EN_A, 0x00}, + {MAX98396_R2071_ICC_RX_EN_B, 0x00}, + {MAX98396_R2072_ICC_TX_CTRL, 0x00}, + {MAX98396_R207F_ICC_EN, 0x00}, + {MAX98396_R2083_TONE_GEN_DC_CFG, 0x04}, + {MAX98396_R2084_TONE_GEN_DC_LVL1, 0x00}, + {MAX98396_R2085_TONE_GEN_DC_LVL2, 0x00}, + {MAX98396_R2086_TONE_GEN_DC_LVL3, 0x00}, + {MAX98396_R208F_TONE_GEN_EN, 0x00}, + {MAX98396_R2090_AMP_VOL_CTRL, 0x00}, + {MAX98396_R2091_AMP_PATH_GAIN, 0x12}, + {MAX98396_R2092_AMP_DSP_CFG, 0x22}, + {MAX98396_R2093_SSM_CFG, 0x08}, + {MAX98396_R2094_SPK_CLS_DG_THRESH, 0x12}, + {MAX98396_R2095_SPK_CLS_DG_HDR, 0x17}, + {MAX98396_R2096_SPK_CLS_DG_HOLD_TIME, 0x17}, + {MAX98396_R2097_SPK_CLS_DG_DELAY, 0x00}, + {MAX98396_R2098_SPK_CLS_DG_MODE, 0x00}, + {MAX98396_R2099_SPK_CLS_DG_VBAT_LVL, 0x03}, + {MAX98396_R209A_SPK_EDGE_CTRL, 0x00}, + {MAX98397_R209B_SPK_PATH_WB_ONLY, 0x00}, + {MAX98396_R209C_SPK_EDGE_CTRL1, 0x03}, + {MAX98396_R209D_SPK_EDGE_CTRL2, 0xFC}, + {MAX98396_R209E_AMP_CLIP_GAIN, 0x00}, + {MAX98396_R209F_BYPASS_PATH_CFG, 0x00}, + {MAX98396_R20AF_AMP_EN, 0x00}, + {MAX98396_R20B0_ADC_SR, 0x30}, + {MAX98396_R20B1_ADC_PVDD_CFG, 0x00}, + {MAX98396_R20B2_ADC_VBAT_CFG, 0x00}, + {MAX98396_R20B3_ADC_THERMAL_CFG, 0x00}, + {MAX98397_R20B4_ADC_VDDH_CFG, 0x00}, + {MAX98397_R20B5_ADC_READBACK_CTRL1, 0x00}, + {MAX98397_R20B6_ADC_READBACK_CTRL2, 0x00}, + {MAX98397_R20B7_ADC_PVDD_READBACK_MSB, 0x00}, + {MAX98397_R20B8_ADC_PVDD_READBACK_LSB, 0x00}, + {MAX98397_R20B9_ADC_VBAT_READBACK_MSB, 0x00}, + {MAX98397_R20BA_ADC_VBAT_READBACK_LSB, 0x00}, + {MAX98397_R20BB_ADC_TEMP_READBACK_MSB, 0x00}, + {MAX98397_R20BC_ADC_TEMP_READBACK_LSB, 0x00}, + {MAX98397_R20BD_ADC_VDDH__READBACK_MSB, 0x00}, + {MAX98397_R20BE_ADC_VDDH_READBACK_LSB, 0x00}, + {MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB, 0x00}, + {MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB, 0x00}, + {MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB, 0x00}, + {MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE, 0x04}, + {MAX98396_R20C7_ADC_CFG, 0x00}, + {MAX98396_R20D0_DHT_CFG1, 0x00}, + {MAX98396_R20D1_LIMITER_CFG1, 0x08}, + {MAX98396_R20D2_LIMITER_CFG2, 0x00}, + {MAX98396_R20D3_DHT_CFG2, 0x14}, + {MAX98396_R20D4_DHT_CFG3, 0x02}, + {MAX98396_R20D5_DHT_CFG4, 0x04}, + {MAX98396_R20D6_DHT_HYSTERESIS_CFG, 0x07}, + {MAX98396_R20DF_DHT_EN, 0x00}, + {MAX98396_R20E0_IV_SENSE_PATH_CFG, 0x04}, + {MAX98396_R20E4_IV_SENSE_PATH_EN, 0x00}, + {MAX98396_R20E5_BPE_STATE, 0x00}, + {MAX98396_R20E6_BPE_L3_THRESH_MSB, 0x00}, + {MAX98396_R20E7_BPE_L3_THRESH_LSB, 0x00}, + {MAX98396_R20E8_BPE_L2_THRESH_MSB, 0x00}, + {MAX98396_R20E9_BPE_L2_THRESH_LSB, 0x00}, + {MAX98396_R20EA_BPE_L1_THRESH_MSB, 0x00}, + {MAX98396_R20EB_BPE_L1_THRESH_LSB, 0x00}, + {MAX98396_R20EC_BPE_L0_THRESH_MSB, 0x00}, + {MAX98396_R20ED_BPE_L0_THRESH_LSB, 0x00}, + {MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME, 0x00}, + {MAX98396_R20F1_BPE_L0_HOLD_TIME, 0x00}, + {MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP, 0x00}, + {MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN, 0x00}, + {MAX98396_R20FA_BPE_L3_ATT_REL_RATE, 0x00}, + {MAX98396_R20FB_BPE_L2_ATT_REL_RATE, 0x00}, + {MAX98396_R20FC_BPE_L1_ATT_REL_RATE, 0x00}, + {MAX98396_R20FD_BPE_L0_ATT_REL_RATE, 0x00}, + {MAX98396_R20FE_BPE_L3_LIMITER_CFG, 0x00}, + {MAX98396_R20FF_BPE_L2_LIMITER_CFG, 0x00}, + {MAX98396_R2100_BPE_L1_LIMITER_CFG, 0x00}, + {MAX98396_R2101_BPE_L0_LIMITER_CFG, 0x00}, + {MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE, 0x00}, + {MAX98396_R2106_BPE_THRESH_HYSTERESIS, 0x00}, + {MAX98396_R2107_BPE_INFINITE_HOLD_CLR, 0x00}, + {MAX98396_R2108_BPE_SUPPLY_SRC, 0x00}, + {MAX98396_R2109_BPE_LOW_STATE, 0x00}, + {MAX98396_R210A_BPE_LOW_GAIN, 0x00}, + {MAX98396_R210B_BPE_LOW_LIMITER, 0x00}, + {MAX98396_R210D_BPE_EN, 0x00}, + {MAX98396_R210E_AUTO_RESTART, 0x00}, + {MAX98396_R210F_GLOBAL_EN, 0x00}, + {MAX98397_R22FF_REVISION_ID, 0x00}, +}; + +static void max98396_global_enable_onoff(struct regmap *regmap, bool onoff) +{ + regmap_write(regmap, MAX98396_R210F_GLOBAL_EN, onoff ? 1 : 0); + usleep_range(11000, 12000); +} + +static int max98396_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + unsigned int format_mask, format = 0; + unsigned int bclk_pol = 0; + int ret, status; + int reg; + bool update = false; + + format_mask = MAX98396_PCM_MODE_CFG_FORMAT_MASK | + MAX98396_PCM_MODE_CFG_LRCLKEDGE; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + format = MAX98396_PCM_MODE_CFG_LRCLKEDGE; + break; + case SND_SOC_DAIFMT_IB_NF: + bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE; + break; + case SND_SOC_DAIFMT_IB_IF: + bclk_pol = MAX98396_PCM_MODE_CFG_BCLKEDGE; + format = MAX98396_PCM_MODE_CFG_LRCLKEDGE; + break; + + default: + dev_err(component->dev, "DAI invert mode %d unsupported\n", + fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format |= MAX98396_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format |= MAX98396_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format |= MAX98396_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format |= MAX98396_PCM_FORMAT_TDM_MODE0; + break; + default: + dev_err(component->dev, "DAI format %d unsupported\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status); + if (ret < 0) + return -EINVAL; + + if (status) { + ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, ®); + if (ret < 0) + return -EINVAL; + if (format != (reg & format_mask)) { + update = true; + } else { + ret = regmap_read(max98396->regmap, + MAX98396_R2042_PCM_CLK_SETUP, ®); + if (ret < 0) + return -EINVAL; + if (bclk_pol != (reg & MAX98396_PCM_MODE_CFG_BCLKEDGE)) + update = true; + } + /* GLOBAL_EN OFF prior to pcm mode, clock configuration change */ + if (update) + max98396_global_enable_onoff(max98396->regmap, false); + } + + regmap_update_bits(max98396->regmap, + MAX98396_R2041_PCM_MODE_CFG, + format_mask, format); + + regmap_update_bits(max98396->regmap, + MAX98396_R2042_PCM_CLK_SETUP, + MAX98396_PCM_MODE_CFG_BCLKEDGE, + bclk_pol); + + if (status && update) + max98396_global_enable_onoff(max98396->regmap, true); + + return 0; +} + +#define MAX98396_BSEL_32 0x2 +#define MAX98396_BSEL_48 0x3 +#define MAX98396_BSEL_64 0x4 +#define MAX98396_BSEL_96 0x5 +#define MAX98396_BSEL_128 0x6 +#define MAX98396_BSEL_192 0x7 +#define MAX98396_BSEL_256 0x8 +#define MAX98396_BSEL_384 0x9 +#define MAX98396_BSEL_512 0xa +#define MAX98396_BSEL_320 0xb +#define MAX98396_BSEL_250 0xc +#define MAX98396_BSEL_125 0xd + +/* Refer to table 5 in the datasheet */ +static const struct max98396_pcm_config { + int in, out, width, bsel, max_sr; +} max98396_pcm_configs[] = { + { .in = 2, .out = 4, .width = 16, .bsel = MAX98396_BSEL_32, .max_sr = 192000 }, + { .in = 2, .out = 6, .width = 24, .bsel = MAX98396_BSEL_48, .max_sr = 192000 }, + { .in = 2, .out = 8, .width = 32, .bsel = MAX98396_BSEL_64, .max_sr = 192000 }, + { .in = 3, .out = 15, .width = 32, .bsel = MAX98396_BSEL_125, .max_sr = 192000 }, + { .in = 4, .out = 8, .width = 16, .bsel = MAX98396_BSEL_64, .max_sr = 192000 }, + { .in = 4, .out = 12, .width = 24, .bsel = MAX98396_BSEL_96, .max_sr = 192000 }, + { .in = 4, .out = 16, .width = 32, .bsel = MAX98396_BSEL_128, .max_sr = 192000 }, + { .in = 5, .out = 15, .width = 24, .bsel = MAX98396_BSEL_125, .max_sr = 192000 }, + { .in = 7, .out = 15, .width = 16, .bsel = MAX98396_BSEL_125, .max_sr = 192000 }, + { .in = 2, .out = 4, .width = 16, .bsel = MAX98396_BSEL_32, .max_sr = 96000 }, + { .in = 2, .out = 6, .width = 24, .bsel = MAX98396_BSEL_48, .max_sr = 96000 }, + { .in = 2, .out = 8, .width = 32, .bsel = MAX98396_BSEL_64, .max_sr = 96000 }, + { .in = 3, .out = 15, .width = 32, .bsel = MAX98396_BSEL_125, .max_sr = 96000 }, + { .in = 4, .out = 8, .width = 16, .bsel = MAX98396_BSEL_64, .max_sr = 96000 }, + { .in = 4, .out = 12, .width = 24, .bsel = MAX98396_BSEL_96, .max_sr = 96000 }, + { .in = 4, .out = 16, .width = 32, .bsel = MAX98396_BSEL_128, .max_sr = 96000 }, + { .in = 5, .out = 15, .width = 24, .bsel = MAX98396_BSEL_125, .max_sr = 96000 }, + { .in = 7, .out = 15, .width = 16, .bsel = MAX98396_BSEL_125, .max_sr = 96000 }, + { .in = 7, .out = 31, .width = 32, .bsel = MAX98396_BSEL_250, .max_sr = 96000 }, + { .in = 8, .out = 16, .width = 16, .bsel = MAX98396_BSEL_128, .max_sr = 96000 }, + { .in = 8, .out = 24, .width = 24, .bsel = MAX98396_BSEL_192, .max_sr = 96000 }, + { .in = 8, .out = 32, .width = 32, .bsel = MAX98396_BSEL_256, .max_sr = 96000 }, + { .in = 10, .out = 31, .width = 24, .bsel = MAX98396_BSEL_250, .max_sr = 96000 }, + { .in = 15, .out = 31, .width = 16, .bsel = MAX98396_BSEL_250, .max_sr = 96000 }, + { .in = 16, .out = 32, .width = 16, .bsel = MAX98396_BSEL_256, .max_sr = 96000 }, + { .in = 7, .out = 31, .width = 32, .bsel = MAX98396_BSEL_250, .max_sr = 48000 }, + { .in = 10, .out = 31, .width = 24, .bsel = MAX98396_BSEL_250, .max_sr = 48000 }, + { .in = 10, .out = 40, .width = 32, .bsel = MAX98396_BSEL_320, .max_sr = 48000 }, + { .in = 15, .out = 31, .width = 16, .bsel = MAX98396_BSEL_250, .max_sr = 48000 }, + { .in = 16, .out = 48, .width = 24, .bsel = MAX98396_BSEL_384, .max_sr = 48000 }, + { .in = 16, .out = 64, .width = 32, .bsel = MAX98396_BSEL_512, .max_sr = 48000 }, +}; + +static int max98396_pcm_config_index(int in_slots, int out_slots, int width) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(max98396_pcm_configs); i++) { + const struct max98396_pcm_config *c = &max98396_pcm_configs[i]; + + if (in_slots == c->in && out_slots <= c->out && width == c->width) + return i; + } + + return -1; +} + +static int max98396_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + int ret, reg, status, bsel = 0; + bool update = false; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + dev_dbg(component->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98396_PCM_SR_8000; + break; + case 11025: + sampling_rate = MAX98396_PCM_SR_11025; + break; + case 12000: + sampling_rate = MAX98396_PCM_SR_12000; + break; + case 16000: + sampling_rate = MAX98396_PCM_SR_16000; + break; + case 22050: + sampling_rate = MAX98396_PCM_SR_22050; + break; + case 24000: + sampling_rate = MAX98396_PCM_SR_24000; + break; + case 32000: + sampling_rate = MAX98396_PCM_SR_32000; + break; + case 44100: + sampling_rate = MAX98396_PCM_SR_44100; + break; + case 48000: + sampling_rate = MAX98396_PCM_SR_48000; + break; + case 88200: + sampling_rate = MAX98396_PCM_SR_88200; + break; + case 96000: + sampling_rate = MAX98396_PCM_SR_96000; + break; + case 192000: + sampling_rate = MAX98396_PCM_SR_192000; + break; + default: + dev_err(component->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + + if (max98396->tdm_mode) { + if (params_rate(params) > max98396->tdm_max_samplerate) { + dev_err(component->dev, "TDM sample rate %d too high", + params_rate(params)); + goto err; + } + } else { + /* BCLK configuration */ + ret = max98396_pcm_config_index(params_channels(params), + params_channels(params), + snd_pcm_format_width(params_format(params))); + if (ret < 0) { + dev_err(component->dev, + "no PCM config for %d channels, format %d\n", + params_channels(params), params_format(params)); + goto err; + } + + bsel = max98396_pcm_configs[ret].bsel; + + if (params_rate(params) > max98396_pcm_configs[ret].max_sr) { + dev_err(component->dev, "sample rate %d too high", + params_rate(params)); + goto err; + } + } + + ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status); + if (ret < 0) + goto err; + + if (status) { + ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, ®); + if (ret < 0) + goto err; + if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK)) { + update = true; + } else { + ret = regmap_read(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP, ®); + if (ret < 0) + goto err; + if (sampling_rate != (reg & MAX98396_PCM_SR_MASK)) + update = true; + } + + /* GLOBAL_EN OFF prior to channel size and sampling rate change */ + if (update) + max98396_global_enable_onoff(max98396->regmap, false); + } + + /* set channel size */ + regmap_update_bits(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, + MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98396->regmap, MAX98396_R2043_PCM_SR_SETUP, + MAX98396_PCM_SR_MASK, sampling_rate); + + /* set sampling rate of IV */ + if (max98396->interleave_mode && + sampling_rate > MAX98396_PCM_SR_16000) + regmap_update_bits(max98396->regmap, + MAX98396_R2043_PCM_SR_SETUP, + MAX98396_IVADC_SR_MASK, + (sampling_rate - 3) + << MAX98396_IVADC_SR_SHIFT); + else + regmap_update_bits(max98396->regmap, + MAX98396_R2043_PCM_SR_SETUP, + MAX98396_IVADC_SR_MASK, + sampling_rate << MAX98396_IVADC_SR_SHIFT); + + if (bsel) + regmap_update_bits(max98396->regmap, + MAX98396_R2042_PCM_CLK_SETUP, + MAX98396_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + if (status && update) + max98396_global_enable_onoff(max98396->regmap, true); + + return 0; + +err: + return -EINVAL; +} + +static int max98396_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct max98396_priv *max98396 = + snd_soc_component_get_drvdata(component); + int bsel; + unsigned int chan_sz = 0; + int ret, status; + int reg; + bool update = false; + + if (!tx_mask && !rx_mask && !slots && !slot_width) + max98396->tdm_mode = false; + else + max98396->tdm_mode = true; + + /* BCLK configuration */ + ret = max98396_pcm_config_index(slots, slots, slot_width); + if (ret < 0) { + dev_err(component->dev, "no TDM config for %d slots %d bits\n", + slots, slot_width); + return -EINVAL; + } + + bsel = max98396_pcm_configs[ret].bsel; + max98396->tdm_max_samplerate = max98396_pcm_configs[ret].max_sr; + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98396_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "slot width %d unsupported\n", + slot_width); + return -EINVAL; + } + + ret = regmap_read(max98396->regmap, MAX98396_R210F_GLOBAL_EN, &status); + if (ret < 0) + return -EINVAL; + + if (status) { + ret = regmap_read(max98396->regmap, MAX98396_R2042_PCM_CLK_SETUP, ®); + if (ret < 0) + return -EINVAL; + if (bsel != (reg & MAX98396_PCM_CLK_SETUP_BSEL_MASK)) { + update = true; + } else { + ret = regmap_read(max98396->regmap, MAX98396_R2041_PCM_MODE_CFG, ®); + if (ret < 0) + return -EINVAL; + if (chan_sz != (reg & MAX98396_PCM_MODE_CFG_CHANSZ_MASK)) + update = true; + } + + /* GLOBAL_EN OFF prior to channel size and BCLK per LRCLK change */ + if (update) + max98396_global_enable_onoff(max98396->regmap, false); + } + + regmap_update_bits(max98396->regmap, + MAX98396_R2042_PCM_CLK_SETUP, + MAX98396_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + regmap_update_bits(max98396->regmap, + MAX98396_R2041_PCM_MODE_CFG, + MAX98396_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + if (max98396->device_id == CODEC_TYPE_MAX98396) { + regmap_update_bits(max98396->regmap, + MAX98396_R2056_PCM_RX_SRC2, + MAX98396_PCM_DMIX_CH0_SRC_MASK, + rx_mask); + regmap_update_bits(max98396->regmap, + MAX98396_R2056_PCM_RX_SRC2, + MAX98396_PCM_DMIX_CH1_SRC_MASK, + rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT); + } else { + regmap_update_bits(max98396->regmap, + MAX98397_R2057_PCM_RX_SRC2, + MAX98396_PCM_DMIX_CH0_SRC_MASK, + rx_mask); + regmap_update_bits(max98396->regmap, + MAX98397_R2057_PCM_RX_SRC2, + MAX98396_PCM_DMIX_CH1_SRC_MASK, + rx_mask << MAX98396_PCM_DMIX_CH1_SHIFT); + } + + /* Tx slot Hi-Z configuration */ + if (max98396->device_id == CODEC_TYPE_MAX98396) { + regmap_write(max98396->regmap, + MAX98396_R2053_PCM_TX_HIZ_CTRL_8, + ~tx_mask & 0xFF); + regmap_write(max98396->regmap, + MAX98396_R2052_PCM_TX_HIZ_CTRL_7, + (~tx_mask & 0xFF00) >> 8); + } else { + regmap_write(max98396->regmap, + MAX98397_R2054_PCM_TX_HIZ_CTRL_8, + ~tx_mask & 0xFF); + regmap_write(max98396->regmap, + MAX98397_R2053_PCM_TX_HIZ_CTRL_7, + (~tx_mask & 0xFF00) >> 8); + } + + if (status && update) + max98396_global_enable_onoff(max98396->regmap, true); + + return 0; +} + +#define MAX98396_RATES SNDRV_PCM_RATE_8000_192000 + +#define MAX98396_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98396_dai_ops = { + .set_fmt = max98396_dai_set_fmt, + .hw_params = max98396_dai_hw_params, + .set_tdm_slot = max98396_dai_tdm_slot, +}; + +static int max98396_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct max98396_priv *max98396 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + max98396_global_enable_onoff(max98396->regmap, true); + break; + case SND_SOC_DAPM_PRE_PMD: + max98396_global_enable_onoff(max98396->regmap, false); + + max98396->tdm_mode = false; + break; + default: + return 0; + } + return 0; +} + +static bool max98396_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4: + case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4: + case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4: + case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4: + case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4: + case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET: + case MAX98396_R2027_THERM_FOLDBACK_EN: + case MAX98396_R2030_NOISEGATE_MODE_CTRL: + case MAX98396_R2033_NOISEGATE_MODE_EN: + case MAX98396_R2038_CLK_MON_CTRL ... MAX98396_R2039_DATA_MON_CTRL: + case MAX98396_R203F_ENABLE_CTRLS ... MAX98396_R2053_PCM_TX_HIZ_CTRL_8: + case MAX98396_R2055_PCM_RX_SRC1 ... MAX98396_R2056_PCM_RX_SRC2: + case MAX98396_R2058_PCM_BYPASS_SRC: + case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98396_R205F_PCM_TX_EN: + case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL: + case MAX98396_R207F_ICC_EN: + case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3: + case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209A_SPK_EDGE_CTRL: + case MAX98396_R209C_SPK_EDGE_CTRL1 ... MAX98396_R20A0_AMP_SUPPLY_CTL: + case MAX98396_R20AF_AMP_EN ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB: + case MAX98396_R20C7_ADC_CFG: + case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG: + case MAX98396_R20DF_DHT_EN: + case MAX98396_R20E0_IV_SENSE_PATH_CFG: + case MAX98396_R20E4_IV_SENSE_PATH_EN + ... MAX98396_R2106_BPE_THRESH_HYSTERESIS: + case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER: + case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN: + case MAX98396_R21FF_REVISION_ID: + return true; + default: + return false; + } +}; + +static bool max98396_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98396_R2000_SW_RESET: + case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4: + case MAX98396_R2041_PCM_MODE_CFG: + case MAX98396_R20B6_ADC_PVDD_READBACK_MSB + ... MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB: + case MAX98396_R20E5_BPE_STATE: + case MAX98396_R2109_BPE_LOW_STATE + ... MAX98396_R210B_BPE_LOW_LIMITER: + case MAX98396_R210F_GLOBAL_EN: + case MAX98396_R21FF_REVISION_ID: + return true; + default: + return false; + } +} + +static bool max98397_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98396_R2001_INT_RAW1 ... MAX98396_R2004_INT_RAW4: + case MAX98396_R2006_INT_STATE1 ... MAX98396_R2009_INT_STATE4: + case MAX98396_R200B_INT_FLAG1 ... MAX98396_R200E_INT_FLAG4: + case MAX98396_R2010_INT_EN1 ... MAX98396_R2013_INT_EN4: + case MAX98396_R2015_INT_FLAG_CLR1 ... MAX98396_R2018_INT_FLAG_CLR4: + case MAX98396_R201F_IRQ_CTRL ... MAX98396_R2024_THERM_FOLDBACK_SET: + case MAX98396_R2027_THERM_FOLDBACK_EN: + case MAX98396_R2030_NOISEGATE_MODE_CTRL: + case MAX98396_R2033_NOISEGATE_MODE_EN: + case MAX98396_R2038_CLK_MON_CTRL ... MAX98397_R203A_SPK_MON_THRESH: + case MAX98396_R203F_ENABLE_CTRLS ... MAX98397_R2054_PCM_TX_HIZ_CTRL_8: + case MAX98397_R2056_PCM_RX_SRC1... MAX98396_R2058_PCM_BYPASS_SRC: + case MAX98396_R205D_PCM_TX_SRC_EN ... MAX98397_R2060_PCM_TX_SUPPLY_SEL: + case MAX98396_R2070_ICC_RX_EN_A... MAX98396_R2072_ICC_TX_CTRL: + case MAX98396_R207F_ICC_EN: + case MAX98396_R2083_TONE_GEN_DC_CFG ... MAX98396_R2086_TONE_GEN_DC_LVL3: + case MAX98396_R208F_TONE_GEN_EN ... MAX98396_R209F_BYPASS_PATH_CFG: + case MAX98396_R20AF_AMP_EN ... MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE: + case MAX98396_R20C7_ADC_CFG: + case MAX98396_R20D0_DHT_CFG1 ... MAX98396_R20D6_DHT_HYSTERESIS_CFG: + case MAX98396_R20DF_DHT_EN: + case MAX98396_R20E0_IV_SENSE_PATH_CFG: + case MAX98396_R20E4_IV_SENSE_PATH_EN + ... MAX98396_R2106_BPE_THRESH_HYSTERESIS: + case MAX98396_R2108_BPE_SUPPLY_SRC ... MAX98396_R210B_BPE_LOW_LIMITER: + case MAX98396_R210D_BPE_EN ... MAX98396_R210F_GLOBAL_EN: + case MAX98397_R22FF_REVISION_ID: + return true; + default: + return false; + } +}; + +static bool max98397_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98396_R2001_INT_RAW1 ... MAX98396_R200E_INT_FLAG4: + case MAX98396_R2041_PCM_MODE_CFG: + case MAX98397_R20B7_ADC_PVDD_READBACK_MSB + ... MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB: + case MAX98396_R20E5_BPE_STATE: + case MAX98396_R2109_BPE_LOW_STATE + ... MAX98396_R210B_BPE_LOW_LIMITER: + case MAX98396_R210F_GLOBAL_EN: + case MAX98397_R22FF_REVISION_ID: + return true; + default: + return false; + } +} + +static const char * const max98396_op_mod_text[] = { + "DG", "PVDD", "VBAT", +}; + +static SOC_ENUM_SINGLE_DECL(max98396_op_mod_enum, + MAX98396_R2098_SPK_CLS_DG_MODE, + 0, max98396_op_mod_text); + +static DECLARE_TLV_DB_SCALE(max98396_digital_tlv, -6350, 50, 1); +static const DECLARE_TLV_DB_RANGE(max98396_spk_tlv, + 0, 0x11, TLV_DB_SCALE_ITEM(400, 100, 0), +); +static DECLARE_TLV_DB_RANGE(max98397_digital_tlv, + 0, 0x4A, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + 0x4B, 0xFF, TLV_DB_SCALE_ITEM(-9000, 50, 0), +); +static const DECLARE_TLV_DB_RANGE(max98397_spk_tlv, + 0, 0x15, TLV_DB_SCALE_ITEM(600, 100, 0), +); + +static int max98396_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + int reg, val; + + if (max98396->device_id == CODEC_TYPE_MAX98396) + reg = MAX98396_R2055_PCM_RX_SRC1; + else + reg = MAX98397_R2056_PCM_RX_SRC1; + + regmap_read(max98396->regmap, reg, &val); + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int max98396_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + int reg, val; + int change; + + if (item[0] >= e->items) + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + if (max98396->device_id == CODEC_TYPE_MAX98396) + reg = MAX98396_R2055_PCM_RX_SRC1; + else + reg = MAX98397_R2056_PCM_RX_SRC1; + + change = snd_soc_component_test_bits(component, reg, + MAX98396_PCM_RX_MASK, val); + + if (change) + regmap_update_bits(max98396->regmap, reg, + MAX98396_PCM_RX_MASK, val); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL); + + return change; +} + +static const char * const max98396_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static SOC_ENUM_SINGLE_DECL(dai_sel_enum, SND_SOC_NOPM, 0, + max98396_switch_text); + +static const struct snd_kcontrol_new max98396_dai_mux = + SOC_DAPM_ENUM_EXT("DAI Sel Mux", dai_sel_enum, + max98396_mux_get, max98396_mux_put); + +static const struct snd_kcontrol_new max98396_vi_control = + SOC_DAPM_SINGLE("Switch", MAX98396_R205F_PCM_TX_EN, 0, 1, 0); + +static const struct snd_soc_dapm_widget max98396_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", + MAX98396_R20AF_AMP_EN, 0, 0, max98396_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98396_dai_mux), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, + MAX98396_R20E4_IV_SENSE_PATH_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, + MAX98396_R20E4_IV_SENSE_PATH_EN, 1, 0), + SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, + &max98396_vi_control), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON"), + SND_SOC_DAPM_SIGGEN("FBMON"), +}; + +static const char * const max98396_thermal_thresh_text[] = { + "50C", "51C", "52C", "53C", "54C", "55C", "56C", "57C", + "58C", "59C", "60C", "61C", "62C", "63C", "64C", "65C", + "66C", "67C", "68C", "69C", "70C", "71C", "72C", "73C", + "74C", "75C", "76C", "77C", "78C", "79C", "80C", "81C", + "82C", "83C", "84C", "85C", "86C", "87C", "88C", "89C", + "90C", "91C", "92C", "93C", "94C", "95C", "96C", "97C", + "98C", "99C", "100C", "101C", "102C", "103C", "104C", "105C", + "106C", "107C", "108C", "109C", "110C", "111C", "112C", "113C", + "114C", "115C", "116C", "117C", "118C", "119C", "120C", "121C", + "122C", "123C", "124C", "125C", "126C", "127C", "128C", "129C", + "130C", "131C", "132C", "133C", "134C", "135C", "136C", "137C", + "138C", "139C", "140C", "141C", "142C", "143C", "144C", "145C", + "146C", "147C", "148C", "149C", "150C" +}; + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh1_enum, + MAX98396_R2020_THERM_WARN_THRESH, 0, + max98396_thermal_thresh_text); + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_warn_thresh2_enum, + MAX98396_R2021_THERM_WARN_THRESH2, 0, + max98396_thermal_thresh_text); + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_shdn_thresh_enum, + MAX98396_R2022_THERM_SHDN_THRESH, 0, + max98396_thermal_thresh_text); + +static const char * const max98396_thermal_hyteresis_text[] = { + "2C", "5C", "7C", "10C" +}; + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_hysteresis_enum, + MAX98396_R2023_THERM_HYSTERESIS, 0, + max98396_thermal_hyteresis_text); + +static const char * const max98396_foldback_slope_text[] = { + "0.25", "0.5", "1.0", "2.0" +}; + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope1_enum, + MAX98396_R2024_THERM_FOLDBACK_SET, + MAX98396_THERM_FB_SLOPE1_SHIFT, + max98396_foldback_slope_text); + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_slope2_enum, + MAX98396_R2024_THERM_FOLDBACK_SET, + MAX98396_THERM_FB_SLOPE2_SHIFT, + max98396_foldback_slope_text); + +static const char * const max98396_foldback_reltime_text[] = { + "3ms", "10ms", "100ms", "300ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_reltime_enum, + MAX98396_R2024_THERM_FOLDBACK_SET, + MAX98396_THERM_FB_REL_SHIFT, + max98396_foldback_reltime_text); + +static const char * const max98396_foldback_holdtime_text[] = { + "0ms", "20ms", "40ms", "80ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98396_thermal_fb_holdtime_enum, + MAX98396_R2024_THERM_FOLDBACK_SET, + MAX98396_THERM_FB_HOLD_SHIFT, + max98396_foldback_holdtime_text); + +static int max98396_adc_value_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct max98396_priv *max98396 = snd_soc_component_get_drvdata(component); + int ret; + u8 val[2]; + int reg = mc->reg; + + /* ADC value is not available if the device is powered down */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + goto exit; + + if (max98396->device_id == CODEC_TYPE_MAX98397) { + switch (mc->reg) { + case MAX98396_R20B6_ADC_PVDD_READBACK_MSB: + reg = MAX98397_R20B7_ADC_PVDD_READBACK_MSB; + break; + case MAX98396_R20B8_ADC_VBAT_READBACK_MSB: + reg = MAX98397_R20B9_ADC_VBAT_READBACK_MSB; + break; + case MAX98396_R20BA_ADC_TEMP_READBACK_MSB: + reg = MAX98397_R20BB_ADC_TEMP_READBACK_MSB; + break; + default: + goto exit; + } + } + + ret = regmap_raw_read(max98396->regmap, reg, &val, 2); + if (ret) + goto exit; + + /* ADC readback bits[8:0] rearrangement */ + ucontrol->value.integer.value[0] = (val[0] << 1) | (val[1] & 1); + return 0; + +exit: + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static const struct snd_kcontrol_new max98396_snd_controls[] = { + /* Volume */ + SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL, + 0, 0x7F, 1, max98396_digital_tlv), + SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN, + 0, 0x11, 0, max98396_spk_tlv), + /* Volume Ramp Up/Down Enable*/ + SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0), + SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0), + /* Clock Monitor Enable */ + SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS, + MAX98396_CTRL_CMON_EN_SHIFT, 1, 0), + /* Dither Enable */ + SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0), + SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0), + /* DC Blocker Enable */ + SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0), + SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0), + /* Speaker Safe Mode Enable */ + SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0), + /* Wideband Filter Enable */ + SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0), + SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0), + /* Dynamic Headroom Tracking */ + SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0), + /* Brownout Protection Engine */ + SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0), + SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0), + /* Bypass Path Enable */ + SOC_SINGLE("Bypass Path Switch", + MAX98396_R205E_PCM_RX_EN, 1, 1, 0), + /* Speaker Operation Mode */ + SOC_ENUM("OP Mode", max98396_op_mod_enum), + /* Auto Restart functions */ + SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL, + MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0), + SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0), + SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0), + SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0), + SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_OVC_RESTART_SHFT, 1, 0), + /* Thermal Threshold */ + SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum), + SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum), + SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum), + SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum), + SOC_SINGLE("THERM Foldback Switch", + MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0), + SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum), + SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum), + SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum), + SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum), + /* ADC */ + SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), + SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), + SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), +}; + +static const struct snd_kcontrol_new max98397_snd_controls[] = { + /* Volume */ + SOC_SINGLE_TLV("Digital Volume", MAX98396_R2090_AMP_VOL_CTRL, + 0, 0xFF, 1, max98397_digital_tlv), + SOC_SINGLE_TLV("Speaker Volume", MAX98396_R2091_AMP_PATH_GAIN, + 0, 0x15, 0, max98397_spk_tlv), + /* Volume Ramp Up/Down Enable*/ + SOC_SINGLE("Ramp Up Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0), + SOC_SINGLE("Ramp Down Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0), + /* Clock Monitor Enable */ + SOC_SINGLE("CLK Monitor Switch", MAX98396_R203F_ENABLE_CTRLS, + MAX98396_CTRL_CMON_EN_SHIFT, 1, 0), + /* Dither Enable */ + SOC_SINGLE("Dither Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_DITH_EN_SHIFT, 1, 0), + SOC_SINGLE("IV Dither Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_DITH_EN_SHIFT, 1, 0), + /* DC Blocker Enable */ + SOC_SINGLE("DC Blocker Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_DCBLK_EN_SHIFT, 1, 0), + SOC_SINGLE("IV DC Blocker Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_DCBLK_EN_SHIFT, 3, 0), + /* Speaker Safe Mode Enable */ + SOC_SINGLE("Safe Mode Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_SAFE_EN_SHIFT, 1, 0), + /* Wideband Filter Enable */ + SOC_SINGLE("WB Filter Switch", MAX98396_R2092_AMP_DSP_CFG, + MAX98396_DSP_SPK_WB_FLT_EN_SHIFT, 1, 0), + SOC_SINGLE("IV WB Filter Switch", MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_WB_FLT_EN_SHIFT, 1, 0), + /* Dynamic Headroom Tracking */ + SOC_SINGLE("DHT Switch", MAX98396_R20DF_DHT_EN, 0, 1, 0), + /* Brownout Protection Engine */ + SOC_SINGLE("BPE Switch", MAX98396_R210D_BPE_EN, 0, 1, 0), + SOC_SINGLE("BPE Limiter Switch", MAX98396_R210D_BPE_EN, 1, 1, 0), + /* Bypass Path Enable */ + SOC_SINGLE("Bypass Path Switch", + MAX98396_R205E_PCM_RX_EN, 1, 1, 0), + /* Speaker Operation Mode */ + SOC_ENUM("OP Mode", max98396_op_mod_enum), + /* Auto Restart functions */ + SOC_SINGLE("CMON Auto Restart Switch", MAX98396_R2038_CLK_MON_CTRL, + MAX98396_CLK_MON_AUTO_RESTART_SHIFT, 1, 0), + SOC_SINGLE("PVDD Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_PVDD_UVLO_RESTART_SHFT, 1, 0), + SOC_SINGLE("VBAT Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_VBAT_UVLO_RESTART_SHFT, 1, 0), + SOC_SINGLE("THERM Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_THEM_SHDN_RESTART_SHFT, 1, 0), + SOC_SINGLE("OVC Auto Restart Switch", MAX98396_R210E_AUTO_RESTART, + MAX98396_OVC_RESTART_SHFT, 1, 0), + /* Thermal Threshold */ + SOC_ENUM("THERM Thresh1", max98396_thermal_warn_thresh1_enum), + SOC_ENUM("THERM Thresh2", max98396_thermal_warn_thresh2_enum), + SOC_ENUM("THERM SHDN Thresh", max98396_thermal_shdn_thresh_enum), + SOC_ENUM("THERM Hysteresis", max98396_thermal_hysteresis_enum), + SOC_SINGLE("THERM Foldback Switch", + MAX98396_R2027_THERM_FOLDBACK_EN, 0, 1, 0), + SOC_ENUM("THERM Slope1", max98396_thermal_fb_slope1_enum), + SOC_ENUM("THERM Slope2", max98396_thermal_fb_slope2_enum), + SOC_ENUM("THERM Release", max98396_thermal_fb_reltime_enum), + SOC_ENUM("THERM Hold", max98396_thermal_fb_holdtime_enum), + /* ADC */ + SOC_SINGLE_EXT("ADC PVDD", MAX98396_R20B6_ADC_PVDD_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), + SOC_SINGLE_EXT("ADC VBAT", MAX98396_R20B8_ADC_VBAT_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), + SOC_SINGLE_EXT("ADC TEMP", MAX98396_R20BA_ADC_TEMP_READBACK_MSB, 0, 0x1FF, 0, + max98396_adc_value_get, NULL), +}; + +static const struct snd_soc_dapm_route max98396_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, + /* Capture */ + { "VI Sense", "Switch", "VMON" }, + { "VI Sense", "Switch", "IMON" }, + { "Voltage Sense", NULL, "VI Sense" }, + { "Current Sense", NULL, "VI Sense" }, +}; + +static struct snd_soc_dai_driver max98396_dai[] = { + { + .name = "max98396-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98396_RATES, + .formats = MAX98396_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98396_RATES, + .formats = MAX98396_FORMATS, + }, + .ops = &max98396_dai_ops, + } +}; + +static struct snd_soc_dai_driver max98397_dai[] = { + { + .name = "max98397-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98396_RATES, + .formats = MAX98396_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98396_RATES, + .formats = MAX98396_FORMATS, + }, + .ops = &max98396_dai_ops, + } +}; + +static void max98396_reset(struct max98396_priv *max98396, struct device *dev) +{ + int ret, reg, count; + + /* Software Reset */ + ret = regmap_write(max98396->regmap, + MAX98396_R2000_SW_RESET, 1); + if (ret) + dev_err(dev, "Reset command failed. (ret:%d)\n", ret); + + count = 0; + while (count < 3) { + usleep_range(5000, 6000); + /* Software Reset Verification */ + ret = regmap_read(max98396->regmap, + GET_REG_ADDR_REV_ID(max98396->device_id), ®); + if (!ret) { + dev_info(dev, "Reset completed (retry:%d)\n", count); + return; + } + count++; + } + dev_err(dev, "Reset failed. (ret:%d)\n", ret); +} + +static int max98396_probe(struct snd_soc_component *component) +{ + struct max98396_priv *max98396 = + snd_soc_component_get_drvdata(component); + + /* Software Reset */ + max98396_reset(max98396, component->dev); + + /* L/R mix configuration */ + if (max98396->device_id == CODEC_TYPE_MAX98396) { + regmap_write(max98396->regmap, + MAX98396_R2055_PCM_RX_SRC1, 0x02); + regmap_write(max98396->regmap, + MAX98396_R2056_PCM_RX_SRC2, 0x10); + } else { + regmap_write(max98396->regmap, + MAX98397_R2056_PCM_RX_SRC1, 0x02); + regmap_write(max98396->regmap, + MAX98397_R2057_PCM_RX_SRC2, 0x10); + } + /* Supply control */ + regmap_update_bits(max98396->regmap, + MAX98396_R20A0_AMP_SUPPLY_CTL, + MAX98396_AMP_SUPPLY_NOVBAT, + (max98396->vbat == NULL) ? + MAX98396_AMP_SUPPLY_NOVBAT : 0); + /* Enable DC blocker */ + regmap_update_bits(max98396->regmap, + MAX98396_R2092_AMP_DSP_CFG, 1, 1); + /* Enable IV Monitor DC blocker */ + regmap_update_bits(max98396->regmap, + MAX98396_R20E0_IV_SENSE_PATH_CFG, + MAX98396_IV_SENSE_DCBLK_EN_MASK, + MAX98396_IV_SENSE_DCBLK_EN_MASK); + /* Configure default data output sources */ + regmap_write(max98396->regmap, + MAX98396_R205D_PCM_TX_SRC_EN, 3); + /* Enable Wideband Filter */ + regmap_update_bits(max98396->regmap, + MAX98396_R2092_AMP_DSP_CFG, 0x40, 0x40); + /* Enable IV Wideband Filter */ + regmap_update_bits(max98396->regmap, + MAX98396_R20E0_IV_SENSE_PATH_CFG, 8, 8); + + /* Enable Bypass Source */ + regmap_write(max98396->regmap, + MAX98396_R2058_PCM_BYPASS_SRC, + max98396->bypass_slot); + /* Voltage, current slot configuration */ + regmap_write(max98396->regmap, + MAX98396_R2044_PCM_TX_CTRL_1, + max98396->v_slot); + regmap_write(max98396->regmap, + MAX98396_R2045_PCM_TX_CTRL_2, + max98396->i_slot); + regmap_write(max98396->regmap, + MAX98396_R204A_PCM_TX_CTRL_7, + max98396->spkfb_slot); + + if (max98396->v_slot < 8) + if (max98396->device_id == CODEC_TYPE_MAX98396) + regmap_update_bits(max98396->regmap, + MAX98396_R2053_PCM_TX_HIZ_CTRL_8, + 1 << max98396->v_slot, 0); + else + regmap_update_bits(max98396->regmap, + MAX98397_R2054_PCM_TX_HIZ_CTRL_8, + 1 << max98396->v_slot, 0); + else + if (max98396->device_id == CODEC_TYPE_MAX98396) + regmap_update_bits(max98396->regmap, + MAX98396_R2052_PCM_TX_HIZ_CTRL_7, + 1 << (max98396->v_slot - 8), 0); + else + regmap_update_bits(max98396->regmap, + MAX98397_R2053_PCM_TX_HIZ_CTRL_7, + 1 << (max98396->v_slot - 8), 0); + + if (max98396->i_slot < 8) + if (max98396->device_id == CODEC_TYPE_MAX98396) + regmap_update_bits(max98396->regmap, + MAX98396_R2053_PCM_TX_HIZ_CTRL_8, + 1 << max98396->i_slot, 0); + else + regmap_update_bits(max98396->regmap, + MAX98397_R2054_PCM_TX_HIZ_CTRL_8, + 1 << max98396->i_slot, 0); + else + if (max98396->device_id == CODEC_TYPE_MAX98396) + regmap_update_bits(max98396->regmap, + MAX98396_R2052_PCM_TX_HIZ_CTRL_7, + 1 << (max98396->i_slot - 8), 0); + else + regmap_update_bits(max98396->regmap, + MAX98397_R2053_PCM_TX_HIZ_CTRL_7, + 1 << (max98396->i_slot - 8), 0); + + /* Set interleave mode */ + if (max98396->interleave_mode) + regmap_update_bits(max98396->regmap, + MAX98396_R2041_PCM_MODE_CFG, + MAX98396_PCM_TX_CH_INTERLEAVE_MASK, + MAX98396_PCM_TX_CH_INTERLEAVE_MASK); + + regmap_update_bits(max98396->regmap, + MAX98396_R2038_CLK_MON_CTRL, + MAX98396_CLK_MON_AUTO_RESTART_MASK, + MAX98396_CLK_MON_AUTO_RESTART_MASK); + + regmap_update_bits(max98396->regmap, + MAX98396_R203F_ENABLE_CTRLS, + MAX98396_CTRL_DMON_STUCK_EN_MASK, + max98396->dmon_stuck_enable ? + MAX98396_CTRL_DMON_STUCK_EN_MASK : 0); + + regmap_update_bits(max98396->regmap, + MAX98396_R203F_ENABLE_CTRLS, + MAX98396_CTRL_DMON_MAG_EN_MASK, + max98396->dmon_mag_enable ? + MAX98396_CTRL_DMON_MAG_EN_MASK : 0); + + switch (max98396->dmon_duration) { + case 64: + regmap_update_bits(max98396->regmap, + MAX98396_R2039_DATA_MON_CTRL, + MAX98396_DMON_DURATION_MASK, 0); + break; + case 256: + regmap_update_bits(max98396->regmap, + MAX98396_R2039_DATA_MON_CTRL, + MAX98396_DMON_DURATION_MASK, 1); + break; + case 1024: + regmap_update_bits(max98396->regmap, + MAX98396_R2039_DATA_MON_CTRL, + MAX98396_DMON_DURATION_MASK, 2); + break; + case 4096: + regmap_update_bits(max98396->regmap, + MAX98396_R2039_DATA_MON_CTRL, + MAX98396_DMON_DURATION_MASK, 3); + break; + default: + dev_err(component->dev, "Invalid DMON duration %d\n", + max98396->dmon_duration); + } + + switch (max98396->dmon_stuck_threshold) { + case 15: + regmap_update_bits(max98396->regmap, + MAX98396_R2039_DATA_MON_CTRL, + MAX98396_DMON_STUCK_THRESH_MASK, + 0 << MAX98396_DMON_STUCK_THRESH_SHIFT); + break; + case 13: + regmap_update_bits(max98396->regmap, + MAX98396_R2039_DATA_MON_CTRL, + MAX98396_DMON_STUCK_THRESH_MASK, + 1 << MAX98396_DMON_STUCK_THRESH_SHIFT); + break; + case 22: + regmap_update_bits(max98396->regmap, + MAX98396_R2039_DATA_MON_CTRL, + MAX98396_DMON_STUCK_THRESH_MASK, + 2 << MAX98396_DMON_STUCK_THRESH_SHIFT); + break; + case 9: + regmap_update_bits(max98396->regmap, + MAX98396_R2039_DATA_MON_CTRL, + MAX98396_DMON_STUCK_THRESH_MASK, + 3 << MAX98396_DMON_STUCK_THRESH_SHIFT); + break; + default: + dev_err(component->dev, "Invalid DMON stuck threshold %d\n", + max98396->dmon_stuck_threshold); + } + + switch (max98396->dmon_mag_threshold) { + case 2 ... 5: + regmap_update_bits(max98396->regmap, + MAX98396_R2039_DATA_MON_CTRL, + MAX98396_DMON_STUCK_THRESH_MASK, + (5 - max98396->dmon_mag_threshold) + << MAX98396_DMON_MAG_THRESH_SHIFT); + break; + default: + dev_err(component->dev, "Invalid DMON magnitude threshold %d\n", + max98396->dmon_mag_threshold); + } + + /* Speaker Amplifier PCM RX Enable by default */ + regmap_update_bits(max98396->regmap, + MAX98396_R205E_PCM_RX_EN, + MAX98396_PCM_RX_EN_MASK, 1); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max98396_suspend(struct device *dev) +{ + struct max98396_priv *max98396 = dev_get_drvdata(dev); + + regcache_cache_only(max98396->regmap, true); + regcache_mark_dirty(max98396->regmap); + regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); + if (max98396->pvdd) + regulator_disable(max98396->pvdd); + + if (max98396->vbat) + regulator_disable(max98396->vbat); + + return 0; +} + +static int max98396_resume(struct device *dev) +{ + struct max98396_priv *max98396 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); + if (ret < 0) + return ret; + + if (max98396->pvdd) { + ret = regulator_enable(max98396->pvdd); + if (ret < 0) + return ret; + } + + if (max98396->vbat) { + ret = regulator_enable(max98396->vbat); + if (ret < 0) + return ret; + } + + regcache_cache_only(max98396->regmap, false); + max98396_reset(max98396, dev); + regcache_sync(max98396->regmap); + return 0; +} +#endif + +static const struct dev_pm_ops max98396_pm = { + SET_SYSTEM_SLEEP_PM_OPS(max98396_suspend, max98396_resume) +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98396 = { + .probe = max98396_probe, + .controls = max98396_snd_controls, + .num_controls = ARRAY_SIZE(max98396_snd_controls), + .dapm_widgets = max98396_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98396_dapm_widgets), + .dapm_routes = max98396_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98396_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98397 = { + .probe = max98396_probe, + .controls = max98397_snd_controls, + .num_controls = ARRAY_SIZE(max98397_snd_controls), + .dapm_widgets = max98396_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98396_dapm_widgets), + .dapm_routes = max98396_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98396_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config max98396_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98396_R21FF_REVISION_ID, + .reg_defaults = max98396_reg, + .num_reg_defaults = ARRAY_SIZE(max98396_reg), + .readable_reg = max98396_readable_register, + .volatile_reg = max98396_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct regmap_config max98397_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98397_R22FF_REVISION_ID, + .reg_defaults = max98397_reg, + .num_reg_defaults = ARRAY_SIZE(max98397_reg), + .readable_reg = max98397_readable_register, + .volatile_reg = max98397_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static void max98396_read_device_property(struct device *dev, + struct max98396_priv *max98396) +{ + int value; + + if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value)) + max98396->v_slot = value & 0xF; + else + max98396->v_slot = 0; + + if (!device_property_read_u32(dev, "adi,imon-slot-no", &value)) + max98396->i_slot = value & 0xF; + else + max98396->i_slot = 1; + + if (!device_property_read_u32(dev, "adi,spkfb-slot-no", &value)) + max98396->spkfb_slot = value & 0xF; + else + max98396->spkfb_slot = 2; + + if (!device_property_read_u32(dev, "adi,bypass-slot-no", &value)) + max98396->bypass_slot = value & 0xF; + else + max98396->bypass_slot = 0; + + max98396->dmon_stuck_enable = + device_property_read_bool(dev, "adi,dmon-stuck-enable"); + + if (!device_property_read_u32(dev, "adi,dmon-stuck-threshold-bits", &value)) + max98396->dmon_stuck_threshold = value; + else + max98396->dmon_stuck_threshold = 15; + + max98396->dmon_mag_enable = + device_property_read_bool(dev, "adi,dmon-magnitude-enable"); + + if (!device_property_read_u32(dev, "adi,dmon-magnitude-threshold-bits", &value)) + max98396->dmon_mag_threshold = value; + else + max98396->dmon_mag_threshold = 5; + + if (!device_property_read_u32(dev, "adi,dmon-duration-ms", &value)) + max98396->dmon_duration = value; + else + max98396->dmon_duration = 64; +} + +static void max98396_core_supplies_disable(void *priv) +{ + struct max98396_priv *max98396 = priv; + + regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); +} + +static void max98396_supply_disable(void *r) +{ + regulator_disable((struct regulator *) r); +} + +static int max98396_i2c_probe(struct i2c_client *i2c) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); + struct max98396_priv *max98396 = NULL; + int i, ret, reg; + + max98396 = devm_kzalloc(&i2c->dev, sizeof(*max98396), GFP_KERNEL); + + if (!max98396) { + ret = -ENOMEM; + return ret; + } + i2c_set_clientdata(i2c, max98396); + + max98396->device_id = id->driver_data; + + /* regmap initialization */ + if (max98396->device_id == CODEC_TYPE_MAX98396) + max98396->regmap = devm_regmap_init_i2c(i2c, &max98396_regmap); + + else + max98396->regmap = devm_regmap_init_i2c(i2c, &max98397_regmap); + + if (IS_ERR(max98396->regmap)) { + ret = PTR_ERR(max98396->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + + /* Obtain regulator supplies */ + for (i = 0; i < MAX98396_NUM_CORE_SUPPLIES; i++) + max98396->core_supplies[i].supply = max98396_core_supplies[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request core supplies: %d\n", ret); + return ret; + } + + max98396->vbat = devm_regulator_get_optional(&i2c->dev, "vbat"); + if (IS_ERR(max98396->vbat)) { + if (PTR_ERR(max98396->vbat) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + max98396->vbat = NULL; + } + + max98396->pvdd = devm_regulator_get_optional(&i2c->dev, "pvdd"); + if (IS_ERR(max98396->pvdd)) { + if (PTR_ERR(max98396->pvdd) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + max98396->pvdd = NULL; + } + + ret = regulator_bulk_enable(MAX98396_NUM_CORE_SUPPLIES, + max98396->core_supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Unable to enable core supplies: %d", ret); + return ret; + } + + ret = devm_add_action_or_reset(&i2c->dev, max98396_core_supplies_disable, + max98396); + if (ret < 0) + return ret; + + if (max98396->pvdd) { + ret = regulator_enable(max98396->pvdd); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&i2c->dev, + max98396_supply_disable, + max98396->pvdd); + if (ret < 0) + return ret; + } + + if (max98396->vbat) { + ret = regulator_enable(max98396->vbat); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&i2c->dev, + max98396_supply_disable, + max98396->vbat); + if (ret < 0) + return ret; + } + + /* update interleave mode info */ + if (device_property_read_bool(&i2c->dev, "adi,interleave_mode")) + max98396->interleave_mode = true; + else + max98396->interleave_mode = false; + + /* voltage/current slot & gpio configuration */ + max98396_read_device_property(&i2c->dev, max98396); + + /* Reset the Device */ + max98396->reset_gpio = devm_gpiod_get_optional(&i2c->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(max98396->reset_gpio)) { + ret = PTR_ERR(max98396->reset_gpio); + dev_err(&i2c->dev, "Unable to request GPIO pin: %d.\n", ret); + return ret; + } + + if (max98396->reset_gpio) { + usleep_range(5000, 6000); + gpiod_set_value_cansleep(max98396->reset_gpio, 0); + /* Wait for the hw reset done */ + usleep_range(5000, 6000); + } + + ret = regmap_read(max98396->regmap, + GET_REG_ADDR_REV_ID(max98396->device_id), ®); + if (ret < 0) { + dev_err(&i2c->dev, "%s: failed to read revision of the device.\n", id->name); + return ret; + } + dev_info(&i2c->dev, "%s revision ID: 0x%02X\n", id->name, reg); + + /* codec registration */ + if (max98396->device_id == CODEC_TYPE_MAX98396) + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_max98396, + max98396_dai, + ARRAY_SIZE(max98396_dai)); + else + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_max98397, + max98397_dai, + ARRAY_SIZE(max98397_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id max98396_i2c_id[] = { + { "max98396", CODEC_TYPE_MAX98396}, + { "max98397", CODEC_TYPE_MAX98397}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98396_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id max98396_of_match[] = { + { .compatible = "adi,max98396", }, + { .compatible = "adi,max98397", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98396_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98396_acpi_match[] = { + { "ADS8396", 0 }, + { "ADS8397", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98396_acpi_match); +#endif + +static struct i2c_driver max98396_i2c_driver = { + .driver = { + .name = "max98396", + .of_match_table = of_match_ptr(max98396_of_match), + .acpi_match_table = ACPI_PTR(max98396_acpi_match), + .pm = &max98396_pm, + }, + .probe = max98396_i2c_probe, + .id_table = max98396_i2c_id, +}; + +module_i2c_driver(max98396_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98396 driver"); +MODULE_AUTHOR("Ryan Lee "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98396.h b/sound/soc/codecs/max98396.h new file mode 100644 index 0000000000..d396aa3e69 --- /dev/null +++ b/sound/soc/codecs/max98396.h @@ -0,0 +1,327 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * max98396.h -- MAX98396 ALSA SoC audio driver header + * + * Copyright(c) 2022, Analog Devices Inc. + */ + +#ifndef _MAX98396_H +#define _MAX98396_H + +#define MAX98396_R2000_SW_RESET 0x2000 +#define MAX98396_R2001_INT_RAW1 0x2001 +#define MAX98396_R2002_INT_RAW2 0x2002 +#define MAX98396_R2003_INT_RAW3 0x2003 +#define MAX98396_R2004_INT_RAW4 0x2004 +#define MAX98396_R2006_INT_STATE1 0x2006 +#define MAX98396_R2007_INT_STATE2 0x2007 +#define MAX98396_R2008_INT_STATE3 0x2008 +#define MAX98396_R2009_INT_STATE4 0x2009 +#define MAX98396_R200B_INT_FLAG1 0x200B +#define MAX98396_R200C_INT_FLAG2 0x200C +#define MAX98396_R200D_INT_FLAG3 0x200D +#define MAX98396_R200E_INT_FLAG4 0x200E +#define MAX98396_R2010_INT_EN1 0x2010 +#define MAX98396_R2011_INT_EN2 0x2011 +#define MAX98396_R2012_INT_EN3 0x2012 +#define MAX98396_R2013_INT_EN4 0x2013 +#define MAX98396_R2015_INT_FLAG_CLR1 0x2015 +#define MAX98396_R2016_INT_FLAG_CLR2 0x2016 +#define MAX98396_R2017_INT_FLAG_CLR3 0x2017 +#define MAX98396_R2018_INT_FLAG_CLR4 0x2018 +#define MAX98396_R201F_IRQ_CTRL 0x201F +#define MAX98396_R2020_THERM_WARN_THRESH 0x2020 +#define MAX98396_R2021_THERM_WARN_THRESH2 0x2021 +#define MAX98396_R2022_THERM_SHDN_THRESH 0x2022 +#define MAX98396_R2023_THERM_HYSTERESIS 0x2023 +#define MAX98396_R2024_THERM_FOLDBACK_SET 0x2024 +#define MAX98396_R2027_THERM_FOLDBACK_EN 0x2027 +#define MAX98396_R2030_NOISEGATE_MODE_CTRL 0x2030 +#define MAX98396_R2033_NOISEGATE_MODE_EN 0x2033 +#define MAX98396_R2038_CLK_MON_CTRL 0x2038 +#define MAX98396_R2039_DATA_MON_CTRL 0x2039 +#define MAX98396_R203F_ENABLE_CTRLS 0x203F +#define MAX98396_R2040_PIN_CFG 0x2040 +#define MAX98396_R2041_PCM_MODE_CFG 0x2041 +#define MAX98396_R2042_PCM_CLK_SETUP 0x2042 +#define MAX98396_R2043_PCM_SR_SETUP 0x2043 +#define MAX98396_R2044_PCM_TX_CTRL_1 0x2044 +#define MAX98396_R2045_PCM_TX_CTRL_2 0x2045 +#define MAX98396_R2046_PCM_TX_CTRL_3 0x2046 +#define MAX98396_R2047_PCM_TX_CTRL_4 0x2047 +#define MAX98396_R2048_PCM_TX_CTRL_5 0x2048 +#define MAX98396_R2049_PCM_TX_CTRL_6 0x2049 +#define MAX98396_R204A_PCM_TX_CTRL_7 0x204A +#define MAX98396_R204B_PCM_TX_CTRL_8 0x204B +#define MAX98396_R204C_PCM_TX_HIZ_CTRL_1 0x204C +#define MAX98396_R204D_PCM_TX_HIZ_CTRL_2 0x204D +#define MAX98396_R204E_PCM_TX_HIZ_CTRL_3 0x204E +#define MAX98396_R204F_PCM_TX_HIZ_CTRL_4 0x204F +#define MAX98396_R2050_PCM_TX_HIZ_CTRL_5 0x2050 +#define MAX98396_R2051_PCM_TX_HIZ_CTRL_6 0x2051 +#define MAX98396_R2052_PCM_TX_HIZ_CTRL_7 0x2052 +#define MAX98396_R2053_PCM_TX_HIZ_CTRL_8 0x2053 +#define MAX98396_R2055_PCM_RX_SRC1 0x2055 +#define MAX98396_R2056_PCM_RX_SRC2 0x2056 +#define MAX98396_R2058_PCM_BYPASS_SRC 0x2058 +#define MAX98396_R205D_PCM_TX_SRC_EN 0x205D +#define MAX98396_R205E_PCM_RX_EN 0x205E +#define MAX98396_R205F_PCM_TX_EN 0x205F +#define MAX98396_R2070_ICC_RX_EN_A 0x2070 +#define MAX98396_R2071_ICC_RX_EN_B 0x2071 +#define MAX98396_R2072_ICC_TX_CTRL 0x2072 +#define MAX98396_R207F_ICC_EN 0x207F +#define MAX98396_R2083_TONE_GEN_DC_CFG 0x2083 +#define MAX98396_R2084_TONE_GEN_DC_LVL1 0x2084 +#define MAX98396_R2085_TONE_GEN_DC_LVL2 0x2085 +#define MAX98396_R2086_TONE_GEN_DC_LVL3 0x2086 +#define MAX98396_R208F_TONE_GEN_EN 0x208F +#define MAX98396_R2090_AMP_VOL_CTRL 0x2090 +#define MAX98396_R2091_AMP_PATH_GAIN 0x2091 +#define MAX98396_R2092_AMP_DSP_CFG 0x2092 +#define MAX98396_R2093_SSM_CFG 0x2093 +#define MAX98396_R2094_SPK_CLS_DG_THRESH 0x2094 +#define MAX98396_R2095_SPK_CLS_DG_HDR 0x2095 +#define MAX98396_R2096_SPK_CLS_DG_HOLD_TIME 0x2096 +#define MAX98396_R2097_SPK_CLS_DG_DELAY 0x2097 +#define MAX98396_R2098_SPK_CLS_DG_MODE 0x2098 +#define MAX98396_R2099_SPK_CLS_DG_VBAT_LVL 0x2099 +#define MAX98396_R209A_SPK_EDGE_CTRL 0x209A +#define MAX98396_R209C_SPK_EDGE_CTRL1 0x209C +#define MAX98396_R209D_SPK_EDGE_CTRL2 0x209D +#define MAX98396_R209E_AMP_CLIP_GAIN 0x209E +#define MAX98396_R209F_BYPASS_PATH_CFG 0x209F +#define MAX98396_R20A0_AMP_SUPPLY_CTL 0x20A0 +#define MAX98396_R20AF_AMP_EN 0x20AF +#define MAX98396_R20B0_ADC_SR 0x20B0 +#define MAX98396_R20B1_ADC_PVDD_CFG 0x20B1 +#define MAX98396_R20B2_ADC_VBAT_CFG 0x20B2 +#define MAX98396_R20B3_ADC_THERMAL_CFG 0x20B3 +#define MAX98396_R20B4_ADC_READBACK_CTRL1 0x20B4 +#define MAX98396_R20B5_ADC_READBACK_CTRL2 0x20B5 +#define MAX98396_R20B6_ADC_PVDD_READBACK_MSB 0x20B6 +#define MAX98396_R20B7_ADC_PVDD_READBACK_LSB 0x20B7 +#define MAX98396_R20B8_ADC_VBAT_READBACK_MSB 0x20B8 +#define MAX98396_R20B9_ADC_VBAT_READBACK_LSB 0x20B9 +#define MAX98396_R20BA_ADC_TEMP_READBACK_MSB 0x20BA +#define MAX98396_R20BB_ADC_TEMP_READBACK_LSB 0x20BB +#define MAX98396_R20BC_ADC_LO_PVDD_READBACK_MSB 0x20BC +#define MAX98396_R20BD_ADC_LO_PVDD_READBACK_LSB 0x20BD +#define MAX98396_R20BE_ADC_LO_VBAT_READBACK_MSB 0x20BE +#define MAX98396_R20BF_ADC_LO_VBAT_READBACK_LSB 0x20BF +#define MAX98396_R20C7_ADC_CFG 0x20C7 +#define MAX98396_R20D0_DHT_CFG1 0x20D0 +#define MAX98396_R20D1_LIMITER_CFG1 0x20D1 +#define MAX98396_R20D2_LIMITER_CFG2 0x20D2 +#define MAX98396_R20D3_DHT_CFG2 0x20D3 +#define MAX98396_R20D4_DHT_CFG3 0x20D4 +#define MAX98396_R20D5_DHT_CFG4 0x20D5 +#define MAX98396_R20D6_DHT_HYSTERESIS_CFG 0x20D6 +#define MAX98396_R20DF_DHT_EN 0x20DF +#define MAX98396_R20E0_IV_SENSE_PATH_CFG 0x20E0 +#define MAX98396_R20E4_IV_SENSE_PATH_EN 0x20E4 +#define MAX98396_R20E5_BPE_STATE 0x20E5 +#define MAX98396_R20E6_BPE_L3_THRESH_MSB 0x20E6 +#define MAX98396_R20E7_BPE_L3_THRESH_LSB 0x20E7 +#define MAX98396_R20E8_BPE_L2_THRESH_MSB 0x20E8 +#define MAX98396_R20E9_BPE_L2_THRESH_LSB 0x20E9 +#define MAX98396_R20EA_BPE_L1_THRESH_MSB 0x20EA +#define MAX98396_R20EB_BPE_L1_THRESH_LSB 0x20EB +#define MAX98396_R20EC_BPE_L0_THRESH_MSB 0x20EC +#define MAX98396_R20ED_BPE_L0_THRESH_LSB 0x20ED +#define MAX98396_R20EE_BPE_L3_DWELL_HOLD_TIME 0x20EE +#define MAX98396_R20EF_BPE_L2_DWELL_HOLD_TIME 0x20EF +#define MAX98396_R20F0_BPE_L1_DWELL_HOLD_TIME 0x20F0 +#define MAX98396_R20F1_BPE_L0_HOLD_TIME 0x20F1 +#define MAX98396_R20F2_BPE_L3_ATTACK_REL_STEP 0x20F2 +#define MAX98396_R20F3_BPE_L2_ATTACK_REL_STEP 0x20F3 +#define MAX98396_R20F4_BPE_L1_ATTACK_REL_STEP 0x20F4 +#define MAX98396_R20F5_BPE_L0_ATTACK_REL_STEP 0x20F5 +#define MAX98396_R20F6_BPE_L3_MAX_GAIN_ATTN 0x20F6 +#define MAX98396_R20F7_BPE_L2_MAX_GAIN_ATTN 0x20F7 +#define MAX98396_R20F8_BPE_L1_MAX_GAIN_ATTN 0x20F8 +#define MAX98396_R20F9_BPE_L0_MAX_GAIN_ATTN 0x20F9 +#define MAX98396_R20FA_BPE_L3_ATT_REL_RATE 0x20FA +#define MAX98396_R20FB_BPE_L2_ATT_REL_RATE 0x20FB +#define MAX98396_R20FC_BPE_L1_ATT_REL_RATE 0x20FC +#define MAX98396_R20FD_BPE_L0_ATT_REL_RATE 0x20FD +#define MAX98396_R20FE_BPE_L3_LIMITER_CFG 0x20FE +#define MAX98396_R20FF_BPE_L2_LIMITER_CFG 0x20FF +#define MAX98396_R2100_BPE_L1_LIMITER_CFG 0x2100 +#define MAX98396_R2101_BPE_L0_LIMITER_CFG 0x2101 +#define MAX98396_R2102_BPE_L3_LIM_ATT_REL_RATE 0x2102 +#define MAX98396_R2103_BPE_L2_LIM_ATT_REL_RATE 0x2103 +#define MAX98396_R2104_BPE_L1_LIM_ATT_REL_RATE 0x2104 +#define MAX98396_R2105_BPE_L0_LIM_ATT_REL_RATE 0x2105 +#define MAX98396_R2106_BPE_THRESH_HYSTERESIS 0x2106 +#define MAX98396_R2107_BPE_INFINITE_HOLD_CLR 0x2107 +#define MAX98396_R2108_BPE_SUPPLY_SRC 0x2108 +#define MAX98396_R2109_BPE_LOW_STATE 0x2109 +#define MAX98396_R210A_BPE_LOW_GAIN 0x210A +#define MAX98396_R210B_BPE_LOW_LIMITER 0x210B +#define MAX98396_R210D_BPE_EN 0x210D +#define MAX98396_R210E_AUTO_RESTART 0x210E +#define MAX98396_R210F_GLOBAL_EN 0x210F +#define MAX98396_R21FF_REVISION_ID 0x21FF + +/* MAX98927 Registers */ +#define MAX98397_R203A_SPK_MON_THRESH 0x203A +#define MAX98397_R204C_PCM_TX_CTRL_9 0x204C +#define MAX98397_R204D_PCM_TX_HIZ_CTRL_1 0x204D +#define MAX98397_R204E_PCM_TX_HIZ_CTRL_2 0x204E +#define MAX98397_R204F_PCM_TX_HIZ_CTRL_3 0x204F +#define MAX98397_R2050_PCM_TX_HIZ_CTRL_4 0x2050 +#define MAX98397_R2051_PCM_TX_HIZ_CTRL_5 0x2051 +#define MAX98397_R2052_PCM_TX_HIZ_CTRL_6 0x2052 +#define MAX98397_R2053_PCM_TX_HIZ_CTRL_7 0x2053 +#define MAX98397_R2054_PCM_TX_HIZ_CTRL_8 0x2054 +#define MAX98397_R2056_PCM_RX_SRC1 0x2056 +#define MAX98397_R2057_PCM_RX_SRC2 0x2057 +#define MAX98397_R2060_PCM_TX_SUPPLY_SEL 0x2060 +#define MAX98397_R209B_SPK_PATH_WB_ONLY 0x209B +#define MAX98397_R20B4_ADC_VDDH_CFG 0x20B4 +#define MAX98397_R20B5_ADC_READBACK_CTRL1 0x20B5 +#define MAX98397_R20B6_ADC_READBACK_CTRL2 0x20B6 +#define MAX98397_R20B7_ADC_PVDD_READBACK_MSB 0x20B7 +#define MAX98397_R20B8_ADC_PVDD_READBACK_LSB 0x20B8 +#define MAX98397_R20B9_ADC_VBAT_READBACK_MSB 0x20B9 +#define MAX98397_R20BA_ADC_VBAT_READBACK_LSB 0x20BA +#define MAX98397_R20BB_ADC_TEMP_READBACK_MSB 0x20BB +#define MAX98397_R20BC_ADC_TEMP_READBACK_LSB 0x20BC +#define MAX98397_R20BD_ADC_VDDH__READBACK_MSB 0x20BD +#define MAX98397_R20BE_ADC_VDDH_READBACK_LSB 0x20BE +#define MAX98397_R20BF_ADC_LO_PVDD_READBACK_MSB 0x20BF +#define MAX98397_R20C0_ADC_LO_PVDD_READBACK_LSB 0x20C0 +#define MAX98397_R20C1_ADC_LO_VBAT_READBACK_MSB 0x20C1 +#define MAX98397_R20C2_ADC_LO_VBAT_READBACK_LSB 0x20C2 +#define MAX98397_R20C3_ADC_LO_VDDH_READBACK_MSB 0x20C3 +#define MAX98397_R20C4_ADC_LO_VDDH_READBACK_LSB 0x20C4 +#define MAX98397_R20C5_MEAS_ADC_OPTIMAL_MODE 0x20C5 +#define MAX98397_R22FF_REVISION_ID 0x22FF + +#define GET_REG_ADDR_REV_ID(x)\ + ((x) > 0 ? MAX98397_R22FF_REVISION_ID : MAX98396_R21FF_REVISION_ID) + +/* MAX98396_R2024_THERM_FOLDBACK_SET */ +#define MAX98396_THERM_FB_SLOPE1_SHIFT (0) +#define MAX98396_THERM_FB_SLOPE2_SHIFT (2) +#define MAX98396_THERM_FB_REL_SHIFT (4) +#define MAX98396_THERM_FB_HOLD_SHIFT (6) + +/* MAX98396_R2038_CLK_MON_CTRL */ +#define MAX98396_CLK_MON_AUTO_RESTART_MASK (0x1 << 0) +#define MAX98396_CLK_MON_AUTO_RESTART_SHIFT (0) + +/* MAX98396_R2039_DATA_MON_CTRL */ +#define MAX98396_DMON_MAG_THRESH_SHIFT (4) +#define MAX98396_DMON_MAG_THRESH_MASK (0x3 << MAX98396_DMON_MAG_THRESH_SHIFT) +#define MAX98396_DMON_STUCK_THRESH_SHIFT (2) +#define MAX98396_DMON_STUCK_THRESH_MASK (0x3 << MAX98396_DMON_STUCK_THRESH_SHIFT) +#define MAX98396_DMON_DURATION_MASK (0x3) + +/* MAX98396_R203F_ENABLE_CTRLS */ +#define MAX98396_CTRL_CMON_EN_SHIFT (0) +#define MAX98396_CTRL_DMON_STUCK_EN_MASK (0x1 << 1) +#define MAX98396_CTRL_DMON_MAG_EN_MASK (0x1 << 2) + +/* MAX98396_R2041_PCM_MODE_CFG */ +#define MAX98396_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98396_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2) +#define MAX98396_PCM_FORMAT_I2S (0x0 << 3) +#define MAX98396_PCM_FORMAT_LJ (0x1 << 3) +#define MAX98396_PCM_FORMAT_TDM_MODE0 (0x3 << 3) +#define MAX98396_PCM_FORMAT_TDM_MODE1 (0x4 << 3) +#define MAX98396_PCM_FORMAT_TDM_MODE2 (0x5 << 3) +#define MAX98396_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98396_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98396_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98396_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) +#define MAX98396_PCM_MODE_CFG_LRCLKEDGE (0x1 << 1) + +/* MAX98396_R2042_PCM_CLK_SETUP */ +#define MAX98396_PCM_MODE_CFG_BCLKEDGE (0x1 << 4) +#define MAX98396_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) +#define MAX98396_PCM_BCLKEDGE_BSEL_MASK (0x1F) + +/* MAX98396_R2043_PCM_SR_SETUP */ +#define MAX98396_PCM_SR_SHIFT (0) +#define MAX98396_IVADC_SR_SHIFT (4) +#define MAX98396_PCM_SR_MASK (0xF << MAX98396_PCM_SR_SHIFT) +#define MAX98396_IVADC_SR_MASK (0xF << MAX98396_IVADC_SR_SHIFT) +#define MAX98396_PCM_SR_8000 (0) +#define MAX98396_PCM_SR_11025 (1) +#define MAX98396_PCM_SR_12000 (2) +#define MAX98396_PCM_SR_16000 (3) +#define MAX98396_PCM_SR_22050 (4) +#define MAX98396_PCM_SR_24000 (5) +#define MAX98396_PCM_SR_32000 (6) +#define MAX98396_PCM_SR_44100 (7) +#define MAX98396_PCM_SR_48000 (8) +#define MAX98396_PCM_SR_88200 (9) +#define MAX98396_PCM_SR_96000 (10) +#define MAX98396_PCM_SR_176400 (11) +#define MAX98396_PCM_SR_192000 (12) + +/* MAX98396_R2055_PCM_RX_SRC1 */ +#define MAX98396_PCM_RX_MASK (0x3 << 0) + +/* MAX98396_R2056_PCM_RX_SRC2 */ +#define MAX98396_PCM_DMIX_CH1_SHIFT (0xF << 0) +#define MAX98396_PCM_DMIX_CH0_SRC_MASK (0xF << 0) +#define MAX98396_PCM_DMIX_CH1_SRC_MASK (0xF << MAX98396_PCM_DMIX_CH1_SHIFT) + +/* MAX98396_R205E_PCM_RX_EN */ +#define MAX98396_PCM_RX_EN_MASK (0x1 << 0) +#define MAX98396_PCM_RX_BYP_EN_MASK (0x1 << 1) + +/* MAX98396_R2092_AMP_DSP_CFG */ +#define MAX98396_DSP_SPK_DCBLK_EN_SHIFT (0) +#define MAX98396_DSP_SPK_DITH_EN_SHIFT (1) +#define MAX98396_DSP_SPK_INVERT_SHIFT (2) +#define MAX98396_DSP_SPK_VOL_RMPUP_SHIFT (3) +#define MAX98396_DSP_SPK_VOL_RMPDN_SHIFT (4) +#define MAX98396_DSP_SPK_SAFE_EN_SHIFT (5) +#define MAX98396_DSP_SPK_WB_FLT_EN_SHIFT (6) + +/* MAX98396_R20A0_AMP_SUPPLY_CTL */ +#define MAX98396_AMP_SUPPLY_NOVBAT (0x1 << 0) + +/* MAX98396_R20E0_IV_SENSE_PATH_CFG */ +#define MAX98396_IV_SENSE_DCBLK_EN_MASK (0x3 << 0) +#define MAX98396_IV_SENSE_DCBLK_EN_SHIFT (0) +#define MAX98396_IV_SENSE_DITH_EN_SHIFT (2) +#define MAX98396_IV_SENSE_WB_FLT_EN_SHIFT (3) + +/* MAX98396_R210E_AUTO_RESTART_BEHAVIOR */ +#define MAX98396_PVDD_UVLO_RESTART_SHFT (0) +#define MAX98396_VBAT_UVLO_RESTART_SHFT (1) +#define MAX98396_THEM_SHDN_RESTART_SHFT (2) +#define MAX98396_OVC_RESTART_SHFT (3) + +enum { + CODEC_TYPE_MAX98396, + CODEC_TYPE_MAX98397, +}; + +#define MAX98396_NUM_CORE_SUPPLIES 3 + +struct max98396_priv { + struct regmap *regmap; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data core_supplies[MAX98396_NUM_CORE_SUPPLIES]; + struct regulator *pvdd, *vbat; + unsigned int v_slot; + unsigned int i_slot; + unsigned int spkfb_slot; + unsigned int bypass_slot; + bool dmon_stuck_enable; + unsigned int dmon_stuck_threshold; + bool dmon_mag_enable; + unsigned int dmon_mag_threshold; + unsigned int dmon_duration; + bool interleave_mode; + bool tdm_mode; + int tdm_max_samplerate; + int device_id; +}; +#endif diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c new file mode 100644 index 0000000000..8b012a8536 --- /dev/null +++ b/sound/soc/codecs/max9850.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * max9850.c -- codec driver for max9850 + * + * Copyright (C) 2011 taskit GmbH + * + * Author: Christian Glindkamp + * + * Initial development of this code was funded by + * MICRONIC Computer Systeme GmbH, https://www.mcsberlin.de/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "max9850.h" + +struct max9850_priv { + struct regmap *regmap; + unsigned int sysclk; +}; + +/* these registers are not used at the moment but provided for the sake of + * completeness */ +static bool max9850_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9850_STATUSA: + case MAX9850_STATUSB: + return true; + default: + return false; + } +} + +static const struct regmap_config max9850_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = MAX9850_DIGITAL_AUDIO, + .volatile_reg = max9850_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static const DECLARE_TLV_DB_RANGE(max9850_tlv, + 0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0), + 0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0), + 0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0), + 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0) +); + +static const struct snd_kcontrol_new max9850_controls[] = { +SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv), +SOC_SINGLE("Headphone Switch", MAX9850_VOLUME, 7, 1, 1), +SOC_SINGLE("Mono Switch", MAX9850_GENERAL_PURPOSE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new max9850_mixer_controls[] = { + SOC_DAPM_SINGLE("Line In Switch", MAX9850_ENABLE, 1, 1, 0), +}; + +static const struct snd_soc_dapm_widget max9850_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("Charge Pump 1", MAX9850_ENABLE, 4, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("Charge Pump 2", MAX9850_ENABLE, 5, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MCLK", MAX9850_ENABLE, 6, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("SHDN", MAX9850_ENABLE, 7, 0, NULL, 0), +SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", MAX9850_ENABLE, 2, 0, + &max9850_mixer_controls[0], + ARRAY_SIZE(max9850_mixer_controls)), +SND_SOC_DAPM_PGA("Headphone Output", MAX9850_ENABLE, 3, 0, NULL, 0), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", MAX9850_ENABLE, 0, 0), +SND_SOC_DAPM_OUTPUT("OUTL"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("OUTR"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_INPUT("INL"), +SND_SOC_DAPM_INPUT("INR"), +}; + +static const struct snd_soc_dapm_route max9850_dapm_routes[] = { + /* output mixer */ + {"Output Mixer", NULL, "DAC"}, + {"Output Mixer", "Line In Switch", "Line Input"}, + + /* outputs */ + {"Headphone Output", NULL, "Output Mixer"}, + {"HPL", NULL, "Headphone Output"}, + {"HPR", NULL, "Headphone Output"}, + {"OUTL", NULL, "Output Mixer"}, + {"OUTR", NULL, "Output Mixer"}, + + /* inputs */ + {"Line Input", NULL, "INL"}, + {"Line Input", NULL, "INR"}, + + /* supplies */ + {"Output Mixer", NULL, "Charge Pump 1"}, + {"Output Mixer", NULL, "Charge Pump 2"}, + {"Output Mixer", NULL, "SHDN"}, + {"DAC", NULL, "MCLK"}, +}; + +static int max9850_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component); + u64 lrclk_div; + u8 sf, da; + + if (!max9850->sysclk) + return -EINVAL; + + /* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */ + sf = (snd_soc_component_read(component, MAX9850_CLOCK) >> 2) + 1; + lrclk_div = (1 << 22); + lrclk_div *= params_rate(params); + lrclk_div *= sf; + do_div(lrclk_div, max9850->sysclk); + + snd_soc_component_write(component, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f); + snd_soc_component_write(component, MAX9850_LRCLK_LSB, lrclk_div & 0xff); + + switch (params_width(params)) { + case 16: + da = 0; + break; + case 20: + da = 0x2; + break; + case 24: + da = 0x3; + break; + default: + return -EINVAL; + } + snd_soc_component_update_bits(component, MAX9850_DIGITAL_AUDIO, 0x3, da); + + return 0; +} + +static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component); + + /* calculate mclk -> iclk divider */ + if (freq <= 13000000) + snd_soc_component_write(component, MAX9850_CLOCK, 0x0); + else if (freq <= 26000000) + snd_soc_component_write(component, MAX9850_CLOCK, 0x4); + else if (freq <= 40000000) + snd_soc_component_write(component, MAX9850_CLOCK, 0x8); + else + return -EINVAL; + + max9850->sysclk = freq; + return 0; +} + +static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u8 da = 0; + + /* set clock provider for audio interface */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + da |= MAX9850_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + da |= MAX9850_DLY; + break; + case SND_SOC_DAIFMT_RIGHT_J: + da |= MAX9850_RTJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + da |= MAX9850_BCINV | MAX9850_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + da |= MAX9850_BCINV; + break; + case SND_SOC_DAIFMT_NB_IF: + da |= MAX9850_INV; + break; + default: + return -EINVAL; + } + + /* set da */ + snd_soc_component_write(component, MAX9850_DIGITAL_AUDIO, da); + + return 0; +} + +static int max9850_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regcache_sync(max9850->regmap); + if (ret) { + dev_err(component->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + break; + case SND_SOC_BIAS_OFF: + break; + } + return 0; +} + +#define MAX9850_RATES SNDRV_PCM_RATE_8000_48000 + +#define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops max9850_dai_ops = { + .hw_params = max9850_hw_params, + .set_sysclk = max9850_set_dai_sysclk, + .set_fmt = max9850_set_dai_fmt, +}; + +static struct snd_soc_dai_driver max9850_dai = { + .name = "max9850-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX9850_RATES, + .formats = MAX9850_FORMATS + }, + .ops = &max9850_dai_ops, +}; + +static int max9850_probe(struct snd_soc_component *component) +{ + /* enable zero-detect */ + snd_soc_component_update_bits(component, MAX9850_GENERAL_PURPOSE, 1, 1); + /* enable slew-rate control */ + snd_soc_component_update_bits(component, MAX9850_VOLUME, 0x40, 0x40); + /* set slew-rate 125ms */ + snd_soc_component_update_bits(component, MAX9850_CHARGE_PUMP, 0xff, 0xc0); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_max9850 = { + .probe = max9850_probe, + .set_bias_level = max9850_set_bias_level, + .controls = max9850_controls, + .num_controls = ARRAY_SIZE(max9850_controls), + .dapm_widgets = max9850_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9850_dapm_widgets), + .dapm_routes = max9850_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9850_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int max9850_i2c_probe(struct i2c_client *i2c) +{ + struct max9850_priv *max9850; + int ret; + + max9850 = devm_kzalloc(&i2c->dev, sizeof(struct max9850_priv), + GFP_KERNEL); + if (max9850 == NULL) + return -ENOMEM; + + max9850->regmap = devm_regmap_init_i2c(i2c, &max9850_regmap); + if (IS_ERR(max9850->regmap)) + return PTR_ERR(max9850->regmap); + + i2c_set_clientdata(i2c, max9850); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_max9850, &max9850_dai, 1); + return ret; +} + +static const struct i2c_device_id max9850_i2c_id[] = { + { "max9850", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9850_i2c_id); + +static struct i2c_driver max9850_i2c_driver = { + .driver = { + .name = "max9850", + }, + .probe = max9850_i2c_probe, + .id_table = max9850_i2c_id, +}; + +module_i2c_driver(max9850_i2c_driver); + +MODULE_AUTHOR("Christian Glindkamp "); +MODULE_DESCRIPTION("ASoC MAX9850 codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9850.h b/sound/soc/codecs/max9850.h new file mode 100644 index 0000000000..da313640b4 --- /dev/null +++ b/sound/soc/codecs/max9850.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * max9850.h -- codec driver for max9850 + * + * Copyright (C) 2011 taskit GmbH + * Author: Christian Glindkamp + */ + +#ifndef _MAX9850_H +#define _MAX9850_H + +#define MAX9850_STATUSA 0x00 +#define MAX9850_STATUSB 0x01 +#define MAX9850_VOLUME 0x02 +#define MAX9850_GENERAL_PURPOSE 0x03 +#define MAX9850_INTERRUPT 0x04 +#define MAX9850_ENABLE 0x05 +#define MAX9850_CLOCK 0x06 +#define MAX9850_CHARGE_PUMP 0x07 +#define MAX9850_LRCLK_MSB 0x08 +#define MAX9850_LRCLK_LSB 0x09 +#define MAX9850_DIGITAL_AUDIO 0x0a + +#define MAX9850_CACHEREGNUM 11 + +/* MAX9850_DIGITAL_AUDIO */ +#define MAX9850_MASTER (1<<7) +#define MAX9850_INV (1<<6) +#define MAX9850_BCINV (1<<5) +#define MAX9850_DLY (1<<3) +#define MAX9850_RTJ (1<<2) + +#endif diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c new file mode 100644 index 0000000000..93412b966b --- /dev/null +++ b/sound/soc/codecs/max98504.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MAX98504 ALSA SoC Audio driver + * + * Copyright 2013 - 2014 Maxim Integrated Products + * Copyright 2016 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "max98504.h" + +static const char * const max98504_supply_names[] = { + "DVDD", + "DIOVDD", + "PVDD", +}; +#define MAX98504_NUM_SUPPLIES ARRAY_SIZE(max98504_supply_names) + +struct max98504_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[MAX98504_NUM_SUPPLIES]; + unsigned int pcm_rx_channels; + bool brownout_enable; + unsigned int brownout_threshold; + unsigned int brownout_attenuation; + unsigned int brownout_attack_hold; + unsigned int brownout_timed_hold; + unsigned int brownout_release_rate; +}; + +static struct reg_default max98504_reg_defaults[] = { + { 0x01, 0}, + { 0x02, 0}, + { 0x03, 0}, + { 0x04, 0}, + { 0x10, 0}, + { 0x11, 0}, + { 0x12, 0}, + { 0x13, 0}, + { 0x14, 0}, + { 0x15, 0}, + { 0x16, 0}, + { 0x17, 0}, + { 0x18, 0}, + { 0x19, 0}, + { 0x1A, 0}, + { 0x20, 0}, + { 0x21, 0}, + { 0x22, 0}, + { 0x23, 0}, + { 0x24, 0}, + { 0x25, 0}, + { 0x26, 0}, + { 0x27, 0}, + { 0x28, 0}, + { 0x30, 0}, + { 0x31, 0}, + { 0x32, 0}, + { 0x33, 0}, + { 0x34, 0}, + { 0x35, 0}, + { 0x36, 0}, + { 0x37, 0}, + { 0x38, 0}, + { 0x39, 0}, + { 0x40, 0}, + { 0x41, 0}, +}; + +static bool max98504_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98504_INTERRUPT_STATUS: + case MAX98504_INTERRUPT_FLAGS: + case MAX98504_INTERRUPT_FLAG_CLEARS: + case MAX98504_WATCHDOG_CLEAR: + case MAX98504_GLOBAL_ENABLE: + case MAX98504_SOFTWARE_RESET: + return true; + default: + return false; + } +} + +static bool max98504_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98504_SOFTWARE_RESET: + case MAX98504_WATCHDOG_CLEAR: + case MAX98504_INTERRUPT_FLAG_CLEARS: + return false; + default: + return true; + } +} + +static int max98504_pcm_rx_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE, + max98504->pcm_rx_channels); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE, 0); + break; + } + + return 0; +} + +static int max98504_component_probe(struct snd_soc_component *c) +{ + struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c); + struct regmap *map = max98504->regmap; + int ret; + + ret = regulator_bulk_enable(MAX98504_NUM_SUPPLIES, max98504->supplies); + if (ret < 0) + return ret; + + regmap_write(map, MAX98504_SOFTWARE_RESET, 0x1); + msleep(20); + + if (!max98504->brownout_enable) + return 0; + + regmap_write(map, MAX98504_PVDD_BROWNOUT_ENABLE, 0x1); + + regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_1, + (max98504->brownout_threshold & 0x1f) << 3 | + (max98504->brownout_attenuation & 0x3)); + + regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_2, + max98504->brownout_attack_hold & 0xff); + + regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_3, + max98504->brownout_timed_hold & 0xff); + + regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_4, + max98504->brownout_release_rate & 0xff); + + return 0; +} + +static void max98504_component_remove(struct snd_soc_component *c) +{ + struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c); + + regulator_bulk_disable(MAX98504_NUM_SUPPLIES, max98504->supplies); +} + +static const char *spk_source_mux_text[] = { + "PCM Monomix", "Analog In", "PDM Left", "PDM Right" +}; + +static const struct soc_enum spk_source_mux_enum = + SOC_ENUM_SINGLE(MAX98504_SPEAKER_SOURCE_SELECT, + 0, ARRAY_SIZE(spk_source_mux_text), + spk_source_mux_text); + +static const struct snd_kcontrol_new spk_source_mux = + SOC_DAPM_ENUM("SPK Source", spk_source_mux_enum); + +static const struct snd_soc_dapm_route max98504_dapm_routes[] = { + { "SPKOUT", NULL, "Global Enable" }, + { "SPK Source", "PCM Monomix", "DAC PCM" }, + { "SPK Source", "Analog In", "AIN" }, + { "SPK Source", "PDM Left", "DAC PDM" }, + { "SPK Source", "PDM Right", "DAC PDM" }, +}; + +static const struct snd_soc_dapm_widget max98504_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Global Enable", MAX98504_GLOBAL_ENABLE, + 0, 0, NULL, 0), + SND_SOC_DAPM_INPUT("AIN"), + SND_SOC_DAPM_AIF_OUT("AIF2OUTL", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2OUTR", "AIF2 Capture", 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC PCM", NULL, SND_SOC_NOPM, 0, 0, + max98504_pcm_rx_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC("DAC PDM", NULL, MAX98504_PDM_RX_ENABLE, 0, 0), + SND_SOC_DAPM_MUX("SPK Source", SND_SOC_NOPM, 0, 0, &spk_source_mux), + SND_SOC_DAPM_REG(snd_soc_dapm_spk, "SPKOUT", + MAX98504_SPEAKER_ENABLE, 0, 1, 1, 0), +}; + +static int max98504_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai); + struct regmap *map = max98504->regmap; + + + switch (dai->id) { + case MAX98504_DAI_ID_PCM: + regmap_write(map, MAX98504_PCM_TX_ENABLE, tx_mask); + max98504->pcm_rx_channels = rx_mask; + break; + + case MAX98504_DAI_ID_PDM: + regmap_write(map, MAX98504_PDM_TX_ENABLE, tx_mask); + break; + default: + WARN_ON(1); + } + + return 0; +} +static int max98504_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai); + struct regmap *map = max98504->regmap; + unsigned int i, sources = 0; + + for (i = 0; i < tx_num; i++) + if (tx_slot[i]) + sources |= (1 << i); + + switch (dai->id) { + case MAX98504_DAI_ID_PCM: + regmap_write(map, MAX98504_PCM_TX_CHANNEL_SOURCES, + sources); + break; + + case MAX98504_DAI_ID_PDM: + regmap_write(map, MAX98504_PDM_TX_CONTROL, sources); + break; + default: + WARN_ON(1); + } + + regmap_write(map, MAX98504_MEASUREMENT_ENABLE, sources ? 0x3 : 0x01); + + return 0; +} + +static const struct snd_soc_dai_ops max98504_dai_ops = { + .set_tdm_slot = max98504_set_tdm_slot, + .set_channel_map = max98504_set_channel_map, +}; + +#define MAX98504_FORMATS (SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|\ + SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE) +#define MAX98504_PDM_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\ + SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|\ + SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_88200|\ + SNDRV_PCM_RATE_96000) + +static struct snd_soc_dai_driver max98504_dai[] = { + /* TODO: Add the PCM interface definitions */ + { + .name = "max98504-aif2", + .id = MAX98504_DAI_ID_PDM, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98504_PDM_RATES, + .formats = MAX98504_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98504_PDM_RATES, + .formats = MAX98504_FORMATS, + }, + .ops = &max98504_dai_ops, + }, +}; + +static const struct snd_soc_component_driver max98504_component_driver = { + .probe = max98504_component_probe, + .remove = max98504_component_remove, + .dapm_widgets = max98504_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98504_dapm_widgets), + .dapm_routes = max98504_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max98504_dapm_routes), + .endianness = 1, +}; + +static const struct regmap_config max98504_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98504_MAX_REGISTER, + .reg_defaults = max98504_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(max98504_reg_defaults), + .volatile_reg = max98504_volatile_register, + .readable_reg = max98504_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98504_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct max98504_priv *max98504; + int i, ret; + + max98504 = devm_kzalloc(dev, sizeof(*max98504), GFP_KERNEL); + if (!max98504) + return -ENOMEM; + + if (node) { + if (!of_property_read_u32(node, "maxim,brownout-threshold", + &max98504->brownout_threshold)) + max98504->brownout_enable = true; + + of_property_read_u32(node, "maxim,brownout-attenuation", + &max98504->brownout_attenuation); + of_property_read_u32(node, "maxim,brownout-attack-hold-ms", + &max98504->brownout_attack_hold); + of_property_read_u32(node, "maxim,brownout-timed-hold-ms", + &max98504->brownout_timed_hold); + of_property_read_u32(node, "maxim,brownout-release-rate-ms", + &max98504->brownout_release_rate); + } + + max98504->regmap = devm_regmap_init_i2c(client, &max98504_regmap); + if (IS_ERR(max98504->regmap)) { + ret = PTR_ERR(max98504->regmap); + dev_err(&client->dev, "regmap initialization failed: %d\n", ret); + return ret; + } + + for (i = 0; i < MAX98504_NUM_SUPPLIES; i++) + max98504->supplies[i].supply = max98504_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, MAX98504_NUM_SUPPLIES, + max98504->supplies); + if (ret < 0) + return ret; + + i2c_set_clientdata(client, max98504); + + return devm_snd_soc_register_component(dev, &max98504_component_driver, + max98504_dai, ARRAY_SIZE(max98504_dai)); +} + +#ifdef CONFIG_OF +static const struct of_device_id max98504_of_match[] = { + { .compatible = "maxim,max98504" }, + { }, +}; +MODULE_DEVICE_TABLE(of, max98504_of_match); +#endif + +static const struct i2c_device_id max98504_i2c_id[] = { + { "max98504" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98504_i2c_id); + +static struct i2c_driver max98504_i2c_driver = { + .driver = { + .name = "max98504", + .of_match_table = of_match_ptr(max98504_of_match), + }, + .probe = max98504_i2c_probe, + .id_table = max98504_i2c_id, +}; +module_i2c_driver(max98504_i2c_driver); + +MODULE_DESCRIPTION("ASoC MAX98504 driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98504.h b/sound/soc/codecs/max98504.h new file mode 100644 index 0000000000..8b2a113b71 --- /dev/null +++ b/sound/soc/codecs/max98504.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * MAX98504 ALSA SoC Audio driver + * + * Copyright 2011 - 2012 Maxim Integrated Products + * Copyright 2016 Samsung Electronics Co., Ltd. + */ +#ifndef MAX98504_H_ +#define MAX98504_H_ + +/* + * MAX98504 Register Definitions + */ +#define MAX98504_INTERRUPT_STATUS 0x01 +#define MAX98504_INTERRUPT_FLAGS 0x02 +#define MAX98504_INTERRUPT_ENABLE 0x03 +#define MAX98504_INTERRUPT_FLAG_CLEARS 0x04 +#define MAX98504_GPIO_ENABLE 0x10 +#define MAX98504_GPIO_CONFIG 0x11 +#define MAX98504_WATCHDOG_ENABLE 0x12 +#define MAX98504_WATCHDOG_CONFIG 0x13 +#define MAX98504_WATCHDOG_CLEAR 0x14 +#define MAX98504_CLOCK_MONITOR_ENABLE 0x15 +#define MAX98504_PVDD_BROWNOUT_ENABLE 0x16 +#define MAX98504_PVDD_BROWNOUT_CONFIG_1 0x17 +#define MAX98504_PVDD_BROWNOUT_CONFIG_2 0x18 +#define MAX98504_PVDD_BROWNOUT_CONFIG_3 0x19 +#define MAX98504_PVDD_BROWNOUT_CONFIG_4 0x1a +#define MAX98504_PCM_RX_ENABLE 0x20 +#define MAX98504_PCM_TX_ENABLE 0x21 +#define MAX98504_PCM_TX_HIZ_CONTROL 0x22 +#define MAX98504_PCM_TX_CHANNEL_SOURCES 0x23 +#define MAX98504_PCM_MODE_CONFIG 0x24 +#define MAX98504_PCM_DSP_CONFIG 0x25 +#define MAX98504_PCM_CLOCK_SETUP 0x26 +#define MAX98504_PCM_SAMPLE_RATE_SETUP 0x27 +#define MAX98504_PCM_TO_SPEAKER_MONOMIX 0x28 +#define MAX98504_PDM_TX_ENABLE 0x30 +#define MAX98504_PDM_TX_HIZ_CONTROL 0x31 +#define MAX98504_PDM_TX_CONTROL 0x32 +#define MAX98504_PDM_RX_ENABLE 0x33 +#define MAX98504_SPEAKER_ENABLE 0x34 +#define MAX98504_SPEAKER_SOURCE_SELECT 0x35 +#define MAX98504_MEASUREMENT_ENABLE 0x36 +#define MAX98504_ANALOGUE_INPUT_GAIN 0x37 +#define MAX98504_TEMPERATURE_LIMIT_CONFIG 0x38 +#define MAX98504_GLOBAL_ENABLE 0x40 +#define MAX98504_SOFTWARE_RESET 0x41 +#define MAX98504_REV_ID 0x7fff + +#define MAX98504_MAX_REGISTER 0x7fff + +#define MAX98504_DAI_ID_PCM 1 +#define MAX98504_DAI_ID_PDM 2 + +#endif /* MAX98504_H_ */ diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c new file mode 100644 index 0000000000..8637fff307 --- /dev/null +++ b/sound/soc/codecs/max98520.c @@ -0,0 +1,768 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2021, Maxim Integrated + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98520.h" + +static struct reg_default max98520_reg[] = { + {MAX98520_R2000_SW_RESET, 0x00}, + {MAX98520_R2001_STATUS_1, 0x00}, + {MAX98520_R2002_STATUS_2, 0x00}, + {MAX98520_R2020_THERM_WARN_THRESH, 0x46}, + {MAX98520_R2021_THERM_SHDN_THRESH, 0x64}, + {MAX98520_R2022_THERM_HYSTERESIS, 0x02}, + {MAX98520_R2023_THERM_FOLDBACK_SET, 0x31}, + {MAX98520_R2027_THERM_FOLDBACK_EN, 0x01}, + {MAX98520_R2030_CLK_MON_CTRL, 0x00}, + {MAX98520_R2037_ERR_MON_CTRL, 0x01}, + {MAX98520_R2040_PCM_MODE_CFG, 0xC0}, + {MAX98520_R2041_PCM_CLK_SETUP, 0x04}, + {MAX98520_R2042_PCM_SR_SETUP, 0x08}, + {MAX98520_R2043_PCM_RX_SRC1, 0x00}, + {MAX98520_R2044_PCM_RX_SRC2, 0x00}, + {MAX98520_R204F_PCM_RX_EN, 0x00}, + {MAX98520_R2090_AMP_VOL_CTRL, 0x00}, + {MAX98520_R2091_AMP_PATH_GAIN, 0x03}, + {MAX98520_R2092_AMP_DSP_CFG, 0x02}, + {MAX98520_R2094_SSM_CFG, 0x01}, + {MAX98520_R2095_AMP_CFG, 0xF0}, + {MAX98520_R209F_AMP_EN, 0x00}, + {MAX98520_R20B0_ADC_SR, 0x00}, + {MAX98520_R20B1_ADC_RESOLUTION, 0x00}, + {MAX98520_R20B2_ADC_PVDD0_CFG, 0x02}, + {MAX98520_R20B3_ADC_THERMAL_CFG, 0x02}, + {MAX98520_R20B4_ADC_READBACK_CTRL, 0x00}, + {MAX98520_R20B5_ADC_READBACK_UPDATE, 0x00}, + {MAX98520_R20B6_ADC_PVDD_READBACK_MSB, 0x00}, + {MAX98520_R20B7_ADC_PVDD_READBACK_LSB, 0x00}, + {MAX98520_R20B8_ADC_TEMP_READBACK_MSB, 0x00}, + {MAX98520_R20B9_ADC_TEMP_READBACK_LSB, 0x00}, + {MAX98520_R20BA_ADC_LOW_PVDD_READBACK_MSB, 0xFF}, + {MAX98520_R20BB_ADC_LOW_READBACK_LSB, 0x01}, + {MAX98520_R20BC_ADC_HIGH_TEMP_READBACK_MSB, 0x00}, + {MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB, 0x00}, + {MAX98520_R20CF_MEAS_ADC_CFG, 0x00}, + {MAX98520_R20D0_DHT_CFG1, 0x00}, + {MAX98520_R20D1_LIMITER_CFG1, 0x08}, + {MAX98520_R20D2_LIMITER_CFG2, 0x00}, + {MAX98520_R20D3_DHT_CFG2, 0x14}, + {MAX98520_R20D4_DHT_CFG3, 0x02}, + {MAX98520_R20D5_DHT_CFG4, 0x04}, + {MAX98520_R20D6_DHT_HYSTERESIS_CFG, 0x07}, + {MAX98520_R20D8_DHT_EN, 0x00}, + {MAX98520_R210E_AUTO_RESTART_BEHAVIOR, 0x00}, + {MAX98520_R210F_GLOBAL_EN, 0x00}, + {MAX98520_R21FF_REVISION_ID, 0x00}, +}; + +static int max98520_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98520_priv *max98520 = + snd_soc_component_get_drvdata(component); + unsigned int format = 0; + unsigned int invert = 0; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98520->regmap, + MAX98520_R2041_PCM_CLK_SETUP, + MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE, + invert); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = MAX98520_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = MAX98520_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98520_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98520_PCM_FORMAT_TDM_MODE0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(max98520->regmap, + MAX98520_R2040_PCM_MODE_CFG, + MAX98520_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98520_PCM_MODE_CFG_FORMAT_SHIFT); + + return 0; +} + +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, 320, +}; + +static int max98520_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} + +static int max98520_set_clock(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct max98520_priv *max98520 = + snd_soc_component_get_drvdata(component); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98520->ch_size; + int value; + + if (!max98520->tdm_mode) { + /* BCLK configuration */ + value = max98520_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98520->regmap, + MAX98520_R2041_PCM_CLK_SETUP, + MAX98520_PCM_CLK_SETUP_BSEL_MASK, + value); + } + dev_dbg(component->dev, "%s tdm_mode:%d out\n", __func__, max98520->tdm_mode); + return 0; +} + +static int max98520_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98520_priv *max98520 = + snd_soc_component_get_drvdata(component); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + max98520->ch_size = snd_pcm_format_width(params_format(params)); + + regmap_update_bits(max98520->regmap, + MAX98520_R2040_PCM_MODE_CFG, + MAX98520_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + dev_dbg(component->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98520_PCM_SR_8000; + break; + case 11025: + sampling_rate = MAX98520_PCM_SR_11025; + break; + case 12000: + sampling_rate = MAX98520_PCM_SR_12000; + break; + case 16000: + sampling_rate = MAX98520_PCM_SR_16000; + break; + case 22050: + sampling_rate = MAX98520_PCM_SR_22050; + break; + case 24000: + sampling_rate = MAX98520_PCM_SR_24000; + break; + case 32000: + sampling_rate = MAX98520_PCM_SR_32000; + break; + case 44100: + sampling_rate = MAX98520_PCM_SR_44100; + break; + case 48000: + sampling_rate = MAX98520_PCM_SR_48000; + break; + case 88200: + sampling_rate = MAX98520_PCM_SR_88200; + break; + case 96000: + sampling_rate = MAX98520_PCM_SR_96000; + break; + case 176400: + sampling_rate = MAX98520_PCM_SR_176400; + break; + case 192000: + sampling_rate = MAX98520_PCM_SR_192000; + break; + default: + dev_err(component->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + + dev_dbg(component->dev, " %s ch_size: %d, sampling rate : %d out\n", __func__, + snd_pcm_format_width(params_format(params)), params_rate(params)); + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98520->regmap, + MAX98520_R2042_PCM_SR_SETUP, + MAX98520_PCM_SR_MASK, + sampling_rate); + + return max98520_set_clock(component, params); +err: + dev_dbg(component->dev, "%s out error", __func__); + return -EINVAL; +} + +static int max98520_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct max98520_priv *max98520 = + snd_soc_component_get_drvdata(component); + int bsel; + unsigned int chan_sz = 0; + + if (!tx_mask && !rx_mask && !slots && !slot_width) + max98520->tdm_mode = false; + else + max98520->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98520_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(component->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98520->regmap, + MAX98520_R2041_PCM_CLK_SETUP, + MAX98520_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98520_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98520->regmap, + MAX98520_R2040_PCM_MODE_CFG, + MAX98520_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + regmap_update_bits(max98520->regmap, + MAX98520_R2044_PCM_RX_SRC2, + MAX98520_PCM_DMIX_CH0_SRC_MASK, + rx_mask); + regmap_update_bits(max98520->regmap, + MAX98520_R2044_PCM_RX_SRC2, + MAX98520_PCM_DMIX_CH1_SRC_MASK, + rx_mask << MAX98520_PCM_DMIX_CH1_SHIFT); + + return 0; +} + +#define MAX98520_RATES SNDRV_PCM_RATE_8000_192000 + +#define MAX98520_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98520_dai_ops = { + .set_fmt = max98520_dai_set_fmt, + .hw_params = max98520_dai_hw_params, + .set_tdm_slot = max98520_dai_tdm_slot, +}; + +static int max98520_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct max98520_priv *max98520 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_dbg(component->dev, " AMP ON\n"); + + regmap_write(max98520->regmap, MAX98520_R209F_AMP_EN, 1); + regmap_write(max98520->regmap, MAX98520_R210F_GLOBAL_EN, 1); + usleep_range(30000, 31000); + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(component->dev, " AMP OFF\n"); + + regmap_write(max98520->regmap, MAX98520_R210F_GLOBAL_EN, 0); + regmap_write(max98520->regmap, MAX98520_R209F_AMP_EN, 0); + usleep_range(30000, 31000); + break; + default: + return 0; + } + return 0; +} + +static const char * const max98520_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static const struct soc_enum dai_sel_enum = + SOC_ENUM_SINGLE(MAX98520_R2043_PCM_RX_SRC1, + 0, 3, max98520_switch_text); + +static const struct snd_kcontrol_new max98520_dai_controls = + SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); + +static const struct snd_kcontrol_new max98520_left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("PCM_INPUT_CH0", MAX98520_R2044_PCM_RX_SRC2, 0, 0x0, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH1", MAX98520_R2044_PCM_RX_SRC2, 0, 0x1, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH2", MAX98520_R2044_PCM_RX_SRC2, 0, 0x2, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH3", MAX98520_R2044_PCM_RX_SRC2, 0, 0x3, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH4", MAX98520_R2044_PCM_RX_SRC2, 0, 0x4, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH5", MAX98520_R2044_PCM_RX_SRC2, 0, 0x5, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH6", MAX98520_R2044_PCM_RX_SRC2, 0, 0x6, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH7", MAX98520_R2044_PCM_RX_SRC2, 0, 0x7, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH8", MAX98520_R2044_PCM_RX_SRC2, 0, 0x8, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH9", MAX98520_R2044_PCM_RX_SRC2, 0, 0x9, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH10", MAX98520_R2044_PCM_RX_SRC2, 0, 0xa, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH11", MAX98520_R2044_PCM_RX_SRC2, 0, 0xb, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH12", MAX98520_R2044_PCM_RX_SRC2, 0, 0xc, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH13", MAX98520_R2044_PCM_RX_SRC2, 0, 0xd, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH14", MAX98520_R2044_PCM_RX_SRC2, 0, 0xe, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH15", MAX98520_R2044_PCM_RX_SRC2, 0, 0xf, 0), +}; + +static const struct snd_kcontrol_new max98520_right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("PCM_INPUT_CH0", MAX98520_R2044_PCM_RX_SRC2, 4, 0x0, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH1", MAX98520_R2044_PCM_RX_SRC2, 4, 0x1, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH2", MAX98520_R2044_PCM_RX_SRC2, 4, 0x2, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH3", MAX98520_R2044_PCM_RX_SRC2, 4, 0x3, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH4", MAX98520_R2044_PCM_RX_SRC2, 4, 0x4, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH5", MAX98520_R2044_PCM_RX_SRC2, 4, 0x5, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH6", MAX98520_R2044_PCM_RX_SRC2, 4, 0x6, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH7", MAX98520_R2044_PCM_RX_SRC2, 4, 0x7, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH8", MAX98520_R2044_PCM_RX_SRC2, 4, 0x8, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH9", MAX98520_R2044_PCM_RX_SRC2, 4, 0x9, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH10", MAX98520_R2044_PCM_RX_SRC2, 4, 0xa, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH11", MAX98520_R2044_PCM_RX_SRC2, 4, 0xb, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH12", MAX98520_R2044_PCM_RX_SRC2, 4, 0xc, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH13", MAX98520_R2044_PCM_RX_SRC2, 4, 0xd, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH14", MAX98520_R2044_PCM_RX_SRC2, 4, 0xe, 0), + SOC_DAPM_SINGLE("PCM_INPUT_CH15", MAX98520_R2044_PCM_RX_SRC2, 4, 0xf, 0), +}; + +static const struct snd_soc_dapm_widget max98520_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", + SND_SOC_NOPM, 0, 0, max98520_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, &max98520_dai_controls), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + /* Left Input Selection */ + SND_SOC_DAPM_MIXER("Left Input Selection", SND_SOC_NOPM, 0, 0, + &max98520_left_input_mixer_controls[0], + ARRAY_SIZE(max98520_left_input_mixer_controls)), + /* Right Input Selection */ + SND_SOC_DAPM_MIXER("Right Input Selection", SND_SOC_NOPM, 0, 0, + &max98520_right_input_mixer_controls[0], + ARRAY_SIZE(max98520_right_input_mixer_controls)), +}; + +static const DECLARE_TLV_DB_SCALE(max98520_digital_tlv, -6300, 50, 1); +static const DECLARE_TLV_DB_SCALE(max98520_spk_tlv, -600, 300, 0); + +static const DECLARE_TLV_DB_RANGE(max98520_dht_lim_thresh_tlv, + 0, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0), +); + +static const DECLARE_TLV_DB_RANGE(max98520_dht_hysteresis_tlv, + 0, 3, TLV_DB_SCALE_ITEM(100, 100, 0), + 4, 7, TLV_DB_SCALE_ITEM(600, 200, 0), +); + +static const DECLARE_TLV_DB_RANGE(max98520_dht_rotation_point_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-1500, 300, 0), + 2, 4, TLV_DB_SCALE_ITEM(-1000, 200, 0), + 5, 10, TLV_DB_SCALE_ITEM(-500, 100, 0), +); + +static const DECLARE_TLV_DB_RANGE(max98520_dht_supply_hr_tlv, + 0, 16, TLV_DB_SCALE_ITEM(-2000, 250, 0), +); + +static const DECLARE_TLV_DB_RANGE(max98520_dht_max_atten_tlv, + 1, 20, TLV_DB_SCALE_ITEM(-2000, 100, 0), +); + +static const char * const max98520_dht_attack_rate_text[] = { + "20us", "40us", "80us", "160us", "320us", "640us", + "1.28ms", "2.56ms", "5.12ms", "10.24ms", "20.48ms", "40.96ms", + "81.92ms", "163.84ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98520_dht_attack_rate_enum, + MAX98520_R20D4_DHT_CFG3, 0, + max98520_dht_attack_rate_text); + +static const char * const max98520_dht_release_rate_text[] = { + "2ms", "4ms", "8ms", "16ms", "32ms", "64ms", "128ms", "256ms", "512ms", + "1.024s", "2.048s", "4.096s", "8.192s", "16.384s" +}; + +static SOC_ENUM_SINGLE_DECL(max98520_dht_release_rate_enum, + MAX98520_R20D5_DHT_CFG4, 0, + max98520_dht_release_rate_text); + +static bool max98520_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98520_R2000_SW_RESET: + case MAX98520_R2027_THERM_FOLDBACK_EN: + case MAX98520_R2030_CLK_MON_CTRL: + case MAX98520_R2037_ERR_MON_CTRL: + case MAX98520_R204F_PCM_RX_EN: + case MAX98520_R209F_AMP_EN: + case MAX98520_R20CF_MEAS_ADC_CFG: + case MAX98520_R20D8_DHT_EN: + case MAX98520_R21FF_REVISION_ID: + case MAX98520_R2001_STATUS_1... MAX98520_R2002_STATUS_2: + case MAX98520_R2020_THERM_WARN_THRESH... MAX98520_R2023_THERM_FOLDBACK_SET: + case MAX98520_R2040_PCM_MODE_CFG... MAX98520_R2044_PCM_RX_SRC2: + case MAX98520_R2090_AMP_VOL_CTRL... MAX98520_R2092_AMP_DSP_CFG: + case MAX98520_R2094_SSM_CFG... MAX98520_R2095_AMP_CFG: + case MAX98520_R20B0_ADC_SR... MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB: + case MAX98520_R20D0_DHT_CFG1... MAX98520_R20D6_DHT_HYSTERESIS_CFG: + case MAX98520_R210E_AUTO_RESTART_BEHAVIOR... MAX98520_R210F_GLOBAL_EN: + case MAX98520_R2161_BOOST_TM1... MAX98520_R2163_BOOST_TM3: + return true; + default: + return false; + } +}; + +static bool max98520_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98520_R210F_GLOBAL_EN: + case MAX98520_R21FF_REVISION_ID: + case MAX98520_R2000_SW_RESET: + case MAX98520_R2001_STATUS_1 ... MAX98520_R2002_STATUS_2: + case MAX98520_R20B4_ADC_READBACK_CTRL + ... MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB: + return true; + default: + return false; + } +} + +static const struct snd_kcontrol_new max98520_snd_controls[] = { +/* Volume */ +SOC_SINGLE_TLV("Digital Volume", MAX98520_R2090_AMP_VOL_CTRL, + 0, 0x7F, 1, max98520_digital_tlv), +SOC_SINGLE_TLV("Speaker Volume", MAX98520_R2091_AMP_PATH_GAIN, + 0, 0x5, 0, max98520_spk_tlv), +/* Volume Ramp Up/Down Enable*/ +SOC_SINGLE("Ramp Up Switch", MAX98520_R2092_AMP_DSP_CFG, + MAX98520_DSP_SPK_VOL_RMPUP_SHIFT, 1, 0), +SOC_SINGLE("Ramp Down Switch", MAX98520_R2092_AMP_DSP_CFG, + MAX98520_DSP_SPK_VOL_RMPDN_SHIFT, 1, 0), +/* Clock Monitor Enable */ +SOC_SINGLE("CLK Monitor Switch", MAX98520_R2037_ERR_MON_CTRL, + MAX98520_CTRL_CMON_EN_SHIFT, 1, 0), +/* Clock Monitor Config */ +SOC_SINGLE("CLKMON Autorestart Switch", MAX98520_R2030_CLK_MON_CTRL, + MAX98520_CMON_AUTORESTART_SHIFT, 1, 0), +/* Dither Enable */ +SOC_SINGLE("Dither Switch", MAX98520_R2092_AMP_DSP_CFG, + MAX98520_DSP_SPK_DITH_EN_SHIFT, 1, 0), +/* DC Blocker Enable */ +SOC_SINGLE("DC Blocker Switch", MAX98520_R2092_AMP_DSP_CFG, + MAX98520_DSP_SPK_DCBLK_EN_SHIFT, 1, 0), +/* Speaker Safe Mode Enable */ +SOC_SINGLE("Speaker Safemode Switch", MAX98520_R2092_AMP_DSP_CFG, + MAX98520_DSP_SPK_SAFE_EN_SHIFT, 1, 0), +/* AMP SSM Enable */ +SOC_SINGLE("CP Bypass Switch", MAX98520_R2094_SSM_CFG, + MAX98520_SSM_RCVR_MODE_SHIFT, 1, 0), +/* Dynamic Headroom Tracking */ +SOC_SINGLE("DHT Switch", MAX98520_R20D8_DHT_EN, 0, 1, 0), +SOC_SINGLE("DHT Limiter Mode", MAX98520_R20D2_LIMITER_CFG2, + MAX98520_DHT_LIMITER_MODE_SHIFT, 1, 0), +SOC_SINGLE("DHT Hysteresis Switch", MAX98520_R20D6_DHT_HYSTERESIS_CFG, + MAX98520_DHT_HYSTERESIS_SWITCH_SHIFT, 1, 0), +SOC_SINGLE_TLV("DHT Rot Pnt", MAX98520_R20D0_DHT_CFG1, + MAX98520_DHT_VROT_PNT_SHIFT, 10, 1, max98520_dht_rotation_point_tlv), +SOC_SINGLE_TLV("DHT Supply Headroom", MAX98520_R20D1_LIMITER_CFG1, + MAX98520_DHT_SUPPLY_HR_SHIFT, 16, 0, max98520_dht_supply_hr_tlv), +SOC_SINGLE_TLV("DHT Limiter Threshold", MAX98520_R20D2_LIMITER_CFG2, + MAX98520_DHT_LIMITER_THRESHOLD_SHIFT, 0xF, 1, max98520_dht_lim_thresh_tlv), +SOC_SINGLE_TLV("DHT Max Attenuation", MAX98520_R20D3_DHT_CFG2, + MAX98520_DHT_MAX_ATTEN_SHIFT, 20, 1, max98520_dht_max_atten_tlv), +SOC_SINGLE_TLV("DHT Hysteresis", MAX98520_R20D6_DHT_HYSTERESIS_CFG, + MAX98520_DHT_HYSTERESIS_SHIFT, 0x7, 0, max98520_dht_hysteresis_tlv), +SOC_ENUM("DHT Attack Rate", max98520_dht_attack_rate_enum), +SOC_ENUM("DHT Release Rate", max98520_dht_release_rate_enum), +/* ADC configuration */ +SOC_SINGLE("ADC PVDD CH Switch", MAX98520_R20CF_MEAS_ADC_CFG, 0, 1, 0), +SOC_SINGLE("ADC PVDD FLT Switch", MAX98520_R20B2_ADC_PVDD0_CFG, MAX98520_FLT_EN_SHIFT, 1, 0), +SOC_SINGLE("ADC TEMP FLT Switch", MAX98520_R20B3_ADC_THERMAL_CFG, MAX98520_FLT_EN_SHIFT, 1, 0), +SOC_SINGLE("ADC PVDD MSB", MAX98520_R20B6_ADC_PVDD_READBACK_MSB, 0, 0xFF, 0), +SOC_SINGLE("ADC PVDD LSB", MAX98520_R20B7_ADC_PVDD_READBACK_LSB, 0, 0x01, 0), +SOC_SINGLE("ADC TEMP MSB", MAX98520_R20B8_ADC_TEMP_READBACK_MSB, 0, 0xFF, 0), +SOC_SINGLE("ADC TEMP LSB", MAX98520_R20B9_ADC_TEMP_READBACK_LSB, 0, 0x01, 0), +}; + +static const struct snd_soc_dapm_route max98520_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, +}; + +static struct snd_soc_dai_driver max98520_dai[] = { + { + .name = "max98520-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98520_RATES, + .formats = MAX98520_FORMATS, + }, + .ops = &max98520_dai_ops, + } + +}; + +static int max98520_probe(struct snd_soc_component *component) +{ + struct max98520_priv *max98520 = + snd_soc_component_get_drvdata(component); + + /* Software Reset */ + regmap_write(max98520->regmap, MAX98520_R2000_SW_RESET, 1); + + /* L/R mono mix configuration : "DAI Sel" for 0x2043 */ + regmap_write(max98520->regmap, MAX98520_R2043_PCM_RX_SRC1, 0x2); + + /* PCM input channles configuration : "Left Input Selection" for 0x2044 */ + /* PCM input channles configuration : "Right Input Selection" for 0x2044 */ + regmap_write(max98520->regmap, MAX98520_R2044_PCM_RX_SRC2, 0x10); + + /* Enable DC blocker */ + regmap_update_bits(max98520->regmap, MAX98520_R2092_AMP_DSP_CFG, 1, 1); + /* Enable Clock Monitor Auto-restart */ + regmap_write(max98520->regmap, MAX98520_R2030_CLK_MON_CTRL, 0x1); + + /* set Rx Enable */ + regmap_update_bits(max98520->regmap, + MAX98520_R204F_PCM_RX_EN, + MAX98520_PCM_RX_EN_MASK, + 1); + + return 0; +} + +static int __maybe_unused max98520_suspend(struct device *dev) +{ + struct max98520_priv *max98520 = dev_get_drvdata(dev); + + regcache_cache_only(max98520->regmap, true); + regcache_mark_dirty(max98520->regmap); + return 0; +} + +static int __maybe_unused max98520_resume(struct device *dev) +{ + struct max98520_priv *max98520 = dev_get_drvdata(dev); + + regcache_cache_only(max98520->regmap, false); + regmap_write(max98520->regmap, MAX98520_R2000_SW_RESET, 1); + regcache_sync(max98520->regmap); + return 0; +} + +static const struct dev_pm_ops max98520_pm = { + SET_SYSTEM_SLEEP_PM_OPS(max98520_suspend, max98520_resume) +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98520 = { + .probe = max98520_probe, + .controls = max98520_snd_controls, + .num_controls = ARRAY_SIZE(max98520_snd_controls), + .dapm_widgets = max98520_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98520_dapm_widgets), + .dapm_routes = max98520_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98520_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config max98520_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98520_R21FF_REVISION_ID, + .reg_defaults = max98520_reg, + .num_reg_defaults = ARRAY_SIZE(max98520_reg), + .readable_reg = max98520_readable_register, + .volatile_reg = max98520_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static void max98520_power_on(struct max98520_priv *max98520, bool poweron) +{ + if (max98520->reset_gpio) + gpiod_set_value_cansleep(max98520->reset_gpio, !poweron); +} + +static int max98520_i2c_probe(struct i2c_client *i2c) +{ + int ret; + int reg = 0; + struct max98520_priv *max98520; + struct i2c_adapter *adapter = to_i2c_adapter(i2c->dev.parent); + + ret = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA); + if (!ret) { + dev_err(&i2c->dev, "I2C check functionality failed\n"); + return -ENXIO; + } + + max98520 = devm_kzalloc(&i2c->dev, sizeof(*max98520), GFP_KERNEL); + + if (!max98520) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98520); + + /* regmap initialization */ + max98520->regmap = devm_regmap_init_i2c(i2c, &max98520_regmap); + if (IS_ERR(max98520->regmap)) { + ret = PTR_ERR(max98520->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + /* Power on device */ + max98520->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH); + if (max98520->reset_gpio) { + if (IS_ERR(max98520->reset_gpio)) { + ret = PTR_ERR(max98520->reset_gpio); + dev_err(&i2c->dev, "Unable to request GPIO pin: %d.\n", ret); + return ret; + } + + max98520_power_on(max98520, 1); + } + + /* Check Revision ID */ + ret = regmap_read(max98520->regmap, MAX98520_R21FF_REVISION_ID, ®); + if (ret < 0) { + dev_err(&i2c->dev, + "Failed to read: 0x%02X\n", MAX98520_R21FF_REVISION_ID); + return ret; + } + dev_info(&i2c->dev, "MAX98520 revisionID: 0x%02X\n", reg); + + /* codec registration */ + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_max98520, + max98520_dai, ARRAY_SIZE(max98520_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id max98520_i2c_id[] = { + { "max98520", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98520_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id max98520_of_match[] = { + { .compatible = "maxim,max98520", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98520_of_match); +#endif + +static struct i2c_driver max98520_i2c_driver = { + .driver = { + .name = "max98520", + .of_match_table = of_match_ptr(max98520_of_match), + .pm = &max98520_pm, + }, + .probe = max98520_i2c_probe, + .id_table = max98520_i2c_id, +}; + +module_i2c_driver(max98520_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98520 driver"); +MODULE_AUTHOR("George Song "); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/codecs/max98520.h b/sound/soc/codecs/max98520.h new file mode 100644 index 0000000000..89a95c25af --- /dev/null +++ b/sound/soc/codecs/max98520.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021, Maxim Integrated. + */ + +#ifndef _MAX98520_H +#define _MAX98520_H + +#define MAX98520_R2000_SW_RESET 0x2000 +#define MAX98520_R2001_STATUS_1 0x2001 +#define MAX98520_R2002_STATUS_2 0x2002 +#define MAX98520_R2020_THERM_WARN_THRESH 0x2020 +#define MAX98520_R2021_THERM_SHDN_THRESH 0x2021 +#define MAX98520_R2022_THERM_HYSTERESIS 0x2022 +#define MAX98520_R2023_THERM_FOLDBACK_SET 0x2023 +#define MAX98520_R2027_THERM_FOLDBACK_EN 0x2027 +#define MAX98520_R2030_CLK_MON_CTRL 0x2030 +#define MAX98520_R2037_ERR_MON_CTRL 0x2037 +#define MAX98520_R2040_PCM_MODE_CFG 0x2040 +#define MAX98520_R2041_PCM_CLK_SETUP 0x2041 +#define MAX98520_R2042_PCM_SR_SETUP 0x2042 +#define MAX98520_R2043_PCM_RX_SRC1 0x2043 +#define MAX98520_R2044_PCM_RX_SRC2 0x2044 +#define MAX98520_R204F_PCM_RX_EN 0x204F +#define MAX98520_R2090_AMP_VOL_CTRL 0x2090 +#define MAX98520_R2091_AMP_PATH_GAIN 0x2091 +#define MAX98520_R2092_AMP_DSP_CFG 0x2092 +#define MAX98520_R2094_SSM_CFG 0x2094 +#define MAX98520_R2095_AMP_CFG 0x2095 +#define MAX98520_R209F_AMP_EN 0x209F +#define MAX98520_R20B0_ADC_SR 0x20B0 +#define MAX98520_R20B1_ADC_RESOLUTION 0x20B1 +#define MAX98520_R20B2_ADC_PVDD0_CFG 0x20B2 +#define MAX98520_R20B3_ADC_THERMAL_CFG 0x20B3 +#define MAX98520_R20B4_ADC_READBACK_CTRL 0x20B4 +#define MAX98520_R20B5_ADC_READBACK_UPDATE 0x20B5 +#define MAX98520_R20B6_ADC_PVDD_READBACK_MSB 0x20B6 +#define MAX98520_R20B7_ADC_PVDD_READBACK_LSB 0x20B7 +#define MAX98520_R20B8_ADC_TEMP_READBACK_MSB 0x20B8 +#define MAX98520_R20B9_ADC_TEMP_READBACK_LSB 0x20B9 +#define MAX98520_R20BA_ADC_LOW_PVDD_READBACK_MSB 0x20BA +#define MAX98520_R20BB_ADC_LOW_READBACK_LSB 0x20BB +#define MAX98520_R20BC_ADC_HIGH_TEMP_READBACK_MSB 0x20BC +#define MAX98520_R20BD_ADC_HIGH_TEMP_READBACK_LSB 0x20BD +#define MAX98520_R20CF_MEAS_ADC_CFG 0x20CF +#define MAX98520_R20D0_DHT_CFG1 0x20D0 +#define MAX98520_R20D1_LIMITER_CFG1 0x20D1 +#define MAX98520_R20D2_LIMITER_CFG2 0x20D2 +#define MAX98520_R20D3_DHT_CFG2 0x20D3 +#define MAX98520_R20D4_DHT_CFG3 0x20D4 +#define MAX98520_R20D5_DHT_CFG4 0x20D5 +#define MAX98520_R20D6_DHT_HYSTERESIS_CFG 0x20D6 +#define MAX98520_R20D8_DHT_EN 0x20D8 +#define MAX98520_R210E_AUTO_RESTART_BEHAVIOR 0x210E +#define MAX98520_R210F_GLOBAL_EN 0x210F +#define MAX98520_R2161_BOOST_TM1 0x2161 +#define MAX98520_R2162_BOOST_TM2 0x2162 +#define MAX98520_R2163_BOOST_TM3 0x2163 +#define MAX98520_R21FF_REVISION_ID 0x21FF + +/* MAX98520_R2030_CLK_MON_CTRL */ +#define MAX98520_CMON_AUTORESTART_SHIFT (0) + +/* MAX98520_R2037_ERR_MON_CTRL */ +#define MAX98520_CTRL_CMON_EN_SHIFT (0) + +/* MAX98520_R2040_PCM_MODE_CFG */ +#define MAX98520_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98520_PCM_MODE_CFG_FORMAT_SHIFT (3) +#define MAX98520_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2) +#define MAX98520_PCM_FORMAT_I2S (0x0 << 3) +#define MAX98520_PCM_FORMAT_LJ (0x1 << 3) +#define MAX98520_PCM_FORMAT_TDM_MODE0 (0x3 << 3) +#define MAX98520_PCM_FORMAT_TDM_MODE1 (0x4 << 3) +#define MAX98520_PCM_FORMAT_TDM_MODE2 (0x5 << 3) +#define MAX98520_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98520_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98520_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98520_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) + +/* MAX98520_R2041_PCM_CLK_SETUP */ +#define MAX98520_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4) +#define MAX98520_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) + +/* MAX98520_R2042_PCM_SR_SETUP */ +#define MAX98520_PCM_SR_SHIFT (0) +#define MAX98520_IVADC_SR_SHIFT (4) +#define MAX98520_PCM_SR_MASK (0xF << MAX98520_PCM_SR_SHIFT) +#define MAX98520_IVADC_SR_MASK (0xF << MAX98520_IVADC_SR_SHIFT) +#define MAX98520_PCM_SR_8000 (0x0) +#define MAX98520_PCM_SR_11025 (0x1) +#define MAX98520_PCM_SR_12000 (0x2) +#define MAX98520_PCM_SR_16000 (0x3) +#define MAX98520_PCM_SR_22050 (0x4) +#define MAX98520_PCM_SR_24000 (0x5) +#define MAX98520_PCM_SR_32000 (0x6) +#define MAX98520_PCM_SR_44100 (0x7) +#define MAX98520_PCM_SR_48000 (0x8) +#define MAX98520_PCM_SR_88200 (0x9) +#define MAX98520_PCM_SR_96000 (0xA) +#define MAX98520_PCM_SR_176400 (0xB) +#define MAX98520_PCM_SR_192000 (0xC) + +/* MAX98520_R2044_PCM_RX_SRC2 */ +#define MAX98520_PCM_DMIX_CH1_SHIFT (0xF << 0) +#define MAX98520_PCM_DMIX_CH0_SRC_MASK (0xF << 0) +#define MAX98520_PCM_DMIX_CH1_SRC_MASK (0xF << MAX98520_PCM_DMIX_CH1_SHIFT) + +/* MAX98520_R204F_PCM_RX_EN */ +#define MAX98520_PCM_RX_EN_MASK (0x1 << 0) +#define MAX98520_PCM_RX_BYP_EN_MASK (0x1 << 1) + +/* MAX98520_R2092_AMP_DSP_CFG */ +#define MAX98520_DSP_SPK_DCBLK_EN_SHIFT (0) +#define MAX98520_DSP_SPK_DITH_EN_SHIFT (1) +#define MAX98520_DSP_SPK_INVERT_SHIFT (2) +#define MAX98520_DSP_SPK_VOL_RMPUP_SHIFT (3) +#define MAX98520_DSP_SPK_VOL_RMPDN_SHIFT (4) +#define MAX98520_DSP_SPK_SAFE_EN_SHIFT (5) + +#define MAX98520_SPK_SAFE_EN_MASK (0x1 << MAX98520_DSP_SPK_SAFE_EN_SHIFT) + +/* MAX98520_R2094_SSM_CFG */ +#define MAX98520_SSM_EN_SHIFT (0) +#define MAX98520_SSM_MOD_SHIFT (1) +#define MAX98520_SSM_RCVR_MODE_SHIFT (3) + +/* MAX98520_R2095_AMP_CFG */ +#define MAX98520_CFG_DYN_MODE_SHIFT (4) +#define MAX98520_CFG_SPK_MODE_SHIFT (3) + +/* MAX98520_R20D0_DHT_CFG1 */ +#define MAX98520_DHT_VROT_PNT_SHIFT (0) + +/* MAX98520_R20D1_LIMITER_CFG1 */ +#define MAX98520_DHT_SUPPLY_HR_SHIFT (0) + +/* MAX98520_R20D2_DHT_CFG2 */ +#define MAX98520_DHT_LIMITER_MODE_SHIFT (0) +#define MAX98520_DHT_LIMITER_THRESHOLD_SHIFT (1) + +/* MAX98520_R20D3_DHT_CFG2 */ +#define MAX98520_DHT_MAX_ATTEN_SHIFT (0) + +/* MAX98520_R20D6_DHT_HYSTERESIS_CFG */ +#define MAX98520_DHT_HYSTERESIS_SWITCH_SHIFT (0) +#define MAX98520_DHT_HYSTERESIS_SHIFT (1) + +/* MAX98520_R20B2_ADC_PVDD0_CFG, MAX98520_R20B3_ADC_THERMAL_CFG */ +#define MAX98520_FLT_EN_SHIFT (4) + +struct max98520_priv { + struct regmap *regmap; + struct gpio_desc *reset_gpio; + unsigned int ch_size; + bool tdm_mode; +}; +#endif + diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c new file mode 100644 index 0000000000..4015ed2c47 --- /dev/null +++ b/sound/soc/codecs/max9860.c @@ -0,0 +1,740 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for the MAX9860 Mono Audio Voice Codec +// +// https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf +// +// The driver does not support sidetone since the DVST register field is +// backwards with the mute near the maximum level instead of the minimum. +// +// Author: Peter Rosin +// Copyright 2016 Axentia Technologies + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "max9860.h" + +struct max9860_priv { + struct regmap *regmap; + struct regulator *dvddio; + struct notifier_block dvddio_nb; + u8 psclk; + unsigned long pclk_rate; + int fmt; +}; + +static int max9860_dvddio_event(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct max9860_priv *max9860 = container_of(nb, struct max9860_priv, + dvddio_nb); + if (event & REGULATOR_EVENT_DISABLE) { + regcache_mark_dirty(max9860->regmap); + regcache_cache_only(max9860->regmap, true); + } + + return 0; +} + +static const struct reg_default max9860_reg_defaults[] = { + { MAX9860_PWRMAN, 0x00 }, + { MAX9860_INTEN, 0x00 }, + { MAX9860_SYSCLK, 0x00 }, + { MAX9860_AUDIOCLKHIGH, 0x00 }, + { MAX9860_AUDIOCLKLOW, 0x00 }, + { MAX9860_IFC1A, 0x00 }, + { MAX9860_IFC1B, 0x00 }, + { MAX9860_VOICEFLTR, 0x00 }, + { MAX9860_DACATTN, 0x00 }, + { MAX9860_ADCLEVEL, 0x00 }, + { MAX9860_DACGAIN, 0x00 }, + { MAX9860_MICGAIN, 0x00 }, + { MAX9860_MICADC, 0x00 }, + { MAX9860_NOISEGATE, 0x00 }, +}; + +static bool max9860_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9860_INTRSTATUS ... MAX9860_MICGAIN: + case MAX9860_MICADC ... MAX9860_PWRMAN: + case MAX9860_REVISION: + return true; + } + + return false; +} + +static bool max9860_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9860_INTEN ... MAX9860_MICGAIN: + case MAX9860_MICADC ... MAX9860_PWRMAN: + return true; + } + + return false; +} + +static bool max9860_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9860_INTRSTATUS: + case MAX9860_MICREADBACK: + return true; + } + + return false; +} + +static bool max9860_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9860_INTRSTATUS: + return true; + } + + return false; +} + +static const struct regmap_config max9860_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = max9860_readable, + .writeable_reg = max9860_writeable, + .volatile_reg = max9860_volatile, + .precious_reg = max9860_precious, + + .max_register = MAX9860_MAX_REGISTER, + .reg_defaults = max9860_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(max9860_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const DECLARE_TLV_DB_SCALE(dva_tlv, -9100, 100, 1); +static const DECLARE_TLV_DB_SCALE(dvg_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_RANGE(pam_tlv, + 0, MAX9860_PAM_MAX - 1, TLV_DB_SCALE_ITEM(-2000, 2000, 1), + MAX9860_PAM_MAX, MAX9860_PAM_MAX, TLV_DB_SCALE_ITEM(3000, 0, 0)); +static const DECLARE_TLV_DB_SCALE(pgam_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(anth_tlv, -7600, 400, 1); +static const DECLARE_TLV_DB_SCALE(agcth_tlv, -1800, 100, 0); + +static const char * const agchld_text[] = { + "AGC Disabled", "50ms", "100ms", "400ms" +}; + +static SOC_ENUM_SINGLE_DECL(agchld_enum, MAX9860_MICADC, + MAX9860_AGCHLD_SHIFT, agchld_text); + +static const char * const agcsrc_text[] = { + "Left ADC", "Left/Right ADC" +}; + +static SOC_ENUM_SINGLE_DECL(agcsrc_enum, MAX9860_MICADC, + MAX9860_AGCSRC_SHIFT, agcsrc_text); + +static const char * const agcatk_text[] = { + "3ms", "12ms", "50ms", "200ms" +}; + +static SOC_ENUM_SINGLE_DECL(agcatk_enum, MAX9860_MICADC, + MAX9860_AGCATK_SHIFT, agcatk_text); + +static const char * const agcrls_text[] = { + "78ms", "156ms", "312ms", "625ms", + "1.25s", "2.5s", "5s", "10s" +}; + +static SOC_ENUM_SINGLE_DECL(agcrls_enum, MAX9860_MICADC, + MAX9860_AGCRLS_SHIFT, agcrls_text); + +static const char * const filter_text[] = { + "Disabled", + "Elliptical HP 217Hz notch (16kHz)", + "Butterworth HP 500Hz (16kHz)", + "Elliptical HP 217Hz notch (8kHz)", + "Butterworth HP 500Hz (8kHz)", + "Butterworth HP 200Hz (48kHz)" +}; + +static SOC_ENUM_SINGLE_DECL(avflt_enum, MAX9860_VOICEFLTR, + MAX9860_AVFLT_SHIFT, filter_text); + +static SOC_ENUM_SINGLE_DECL(dvflt_enum, MAX9860_VOICEFLTR, + MAX9860_DVFLT_SHIFT, filter_text); + +static const struct snd_kcontrol_new max9860_controls[] = { +SOC_SINGLE_TLV("Master Playback Volume", MAX9860_DACATTN, + MAX9860_DVA_SHIFT, MAX9860_DVA_MUTE, 1, dva_tlv), +SOC_SINGLE_TLV("DAC Gain Volume", MAX9860_DACGAIN, + MAX9860_DVG_SHIFT, MAX9860_DVG_MAX, 0, dvg_tlv), +SOC_DOUBLE_TLV("Line Capture Volume", MAX9860_ADCLEVEL, + MAX9860_ADCLL_SHIFT, MAX9860_ADCRL_SHIFT, MAX9860_ADCxL_MIN, 1, + adc_tlv), + +SOC_ENUM("AGC Hold Time", agchld_enum), +SOC_ENUM("AGC/Noise Gate Source", agcsrc_enum), +SOC_ENUM("AGC Attack Time", agcatk_enum), +SOC_ENUM("AGC Release Time", agcrls_enum), + +SOC_SINGLE_TLV("Noise Gate Threshold Volume", MAX9860_NOISEGATE, + MAX9860_ANTH_SHIFT, MAX9860_ANTH_MAX, 0, anth_tlv), +SOC_SINGLE_TLV("AGC Signal Threshold Volume", MAX9860_NOISEGATE, + MAX9860_AGCTH_SHIFT, MAX9860_AGCTH_MIN, 1, agcth_tlv), + +SOC_SINGLE_TLV("Mic PGA Volume", MAX9860_MICGAIN, + MAX9860_PGAM_SHIFT, MAX9860_PGAM_MIN, 1, pgam_tlv), +SOC_SINGLE_TLV("Mic Preamp Volume", MAX9860_MICGAIN, + MAX9860_PAM_SHIFT, MAX9860_PAM_MAX, 0, pam_tlv), + +SOC_ENUM("ADC Filter", avflt_enum), +SOC_ENUM("DAC Filter", dvflt_enum), +}; + +static const struct snd_soc_dapm_widget max9860_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("MICL"), +SND_SOC_DAPM_INPUT("MICR"), + +SND_SOC_DAPM_ADC("ADCL", NULL, MAX9860_PWRMAN, MAX9860_ADCLEN_SHIFT, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, MAX9860_PWRMAN, MAX9860_ADCREN_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_DAC("DAC", NULL, MAX9860_PWRMAN, MAX9860_DACEN_SHIFT, 0), + +SND_SOC_DAPM_OUTPUT("OUT"), + +SND_SOC_DAPM_SUPPLY("Supply", SND_SOC_NOPM, 0, 0, + NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_REGULATOR_SUPPLY("AVDD", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DVDD", 0, 0), +SND_SOC_DAPM_CLOCK_SUPPLY("mclk"), +}; + +static const struct snd_soc_dapm_route max9860_dapm_routes[] = { + { "ADCL", NULL, "MICL" }, + { "ADCR", NULL, "MICR" }, + { "AIFOUTL", NULL, "ADCL" }, + { "AIFOUTR", NULL, "ADCR" }, + + { "DAC", NULL, "AIFINL" }, + { "DAC", NULL, "AIFINR" }, + { "OUT", NULL, "DAC" }, + + { "Supply", NULL, "AVDD" }, + { "Supply", NULL, "DVDD" }, + { "Supply", NULL, "mclk" }, + + { "DAC", NULL, "Supply" }, + { "ADCL", NULL, "Supply" }, + { "ADCR", NULL, "Supply" }, +}; + +static int max9860_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max9860_priv *max9860 = snd_soc_component_get_drvdata(component); + u8 master; + u8 ifc1a = 0; + u8 ifc1b = 0; + u8 sysclk = 0; + unsigned long n; + int ret; + + dev_dbg(component->dev, "hw_params %u Hz, %u channels\n", + params_rate(params), + params_channels(params)); + + if (params_channels(params) == 2) + ifc1b |= MAX9860_ST; + + switch (max9860->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + master = 0; + break; + case SND_SOC_DAIFMT_CBP_CFP: + master = MAX9860_MASTER; + break; + default: + return -EINVAL; + } + ifc1a |= master; + + if (master) { + if (params_width(params) * params_channels(params) > 48) + ifc1b |= MAX9860_BSEL_64X; + else + ifc1b |= MAX9860_BSEL_48X; + } + + switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ifc1a |= MAX9860_DDLY; + ifc1b |= MAX9860_ADLY; + break; + case SND_SOC_DAIFMT_LEFT_J: + ifc1a |= MAX9860_WCI; + break; + case SND_SOC_DAIFMT_DSP_A: + if (params_width(params) != 16) { + dev_err(component->dev, + "DSP_A works for 16 bits per sample only.\n"); + return -EINVAL; + } + ifc1a |= MAX9860_DDLY | MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM; + ifc1b |= MAX9860_ADLY; + break; + case SND_SOC_DAIFMT_DSP_B: + if (params_width(params) != 16) { + dev_err(component->dev, + "DSP_B works for 16 bits per sample only.\n"); + return -EINVAL; + } + ifc1a |= MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM; + break; + default: + return -EINVAL; + } + + switch (max9860->fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + return -EINVAL; + } + ifc1a ^= MAX9860_WCI; + break; + case SND_SOC_DAIFMT_IB_IF: + switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + return -EINVAL; + } + ifc1a ^= MAX9860_WCI; + fallthrough; + case SND_SOC_DAIFMT_IB_NF: + ifc1a ^= MAX9860_DBCI; + ifc1b ^= MAX9860_ABCI; + break; + default: + return -EINVAL; + } + + dev_dbg(component->dev, "IFC1A %02x\n", ifc1a); + ret = regmap_write(max9860->regmap, MAX9860_IFC1A, ifc1a); + if (ret) { + dev_err(component->dev, "Failed to set IFC1A: %d\n", ret); + return ret; + } + dev_dbg(component->dev, "IFC1B %02x\n", ifc1b); + ret = regmap_write(max9860->regmap, MAX9860_IFC1B, ifc1b); + if (ret) { + dev_err(component->dev, "Failed to set IFC1B: %d\n", ret); + return ret; + } + + /* + * Check if Integer Clock Mode is possible, but avoid it in slave mode + * since we then do not know if lrclk is derived from pclk and the + * datasheet mentions that the frequencies have to match exactly in + * order for this to work. + */ + if (params_rate(params) == 8000 || params_rate(params) == 16000) { + if (master) { + switch (max9860->pclk_rate) { + case 12000000: + sysclk = MAX9860_FREQ_12MHZ; + break; + case 13000000: + sysclk = MAX9860_FREQ_13MHZ; + break; + case 19200000: + sysclk = MAX9860_FREQ_19_2MHZ; + break; + default: + /* + * Integer Clock Mode not possible. Leave + * sysclk at zero and fall through to the + * code below for PLL mode. + */ + break; + } + + if (sysclk && params_rate(params) == 16000) + sysclk |= MAX9860_16KHZ; + } + } + + /* + * Largest possible n: + * 65536 * 96 * 48kHz / 10MHz -> 30199 + * Smallest possible n: + * 65536 * 96 * 8kHz / 20MHz -> 2517 + * Both fit nicely in the available 15 bits, no need to apply any mask. + */ + n = DIV_ROUND_CLOSEST_ULL(65536ULL * 96 * params_rate(params), + max9860->pclk_rate); + + if (!sysclk) { + /* PLL mode */ + if (params_rate(params) > 24000) + sysclk |= MAX9860_16KHZ; + + if (!master) + n |= 1; /* trigger rapid pll lock mode */ + } + + sysclk |= max9860->psclk; + dev_dbg(component->dev, "SYSCLK %02x\n", sysclk); + ret = regmap_write(max9860->regmap, + MAX9860_SYSCLK, sysclk); + if (ret) { + dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret); + return ret; + } + dev_dbg(component->dev, "N %lu\n", n); + ret = regmap_write(max9860->regmap, + MAX9860_AUDIOCLKHIGH, n >> 8); + if (ret) { + dev_err(component->dev, "Failed to set NHI: %d\n", ret); + return ret; + } + ret = regmap_write(max9860->regmap, + MAX9860_AUDIOCLKLOW, n & 0xff); + if (ret) { + dev_err(component->dev, "Failed to set NLO: %d\n", ret); + return ret; + } + + if (!master) { + dev_dbg(component->dev, "Enable PLL\n"); + ret = regmap_update_bits(max9860->regmap, MAX9860_AUDIOCLKHIGH, + MAX9860_PLL, MAX9860_PLL); + if (ret) { + dev_err(component->dev, "Failed to enable PLL: %d\n", + ret); + return ret; + } + } + + return 0; +} + +static int max9860_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct max9860_priv *max9860 = snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + case SND_SOC_DAIFMT_CBC_CFC: + max9860->fmt = fmt; + return 0; + + default: + return -EINVAL; + } +} + +static const struct snd_soc_dai_ops max9860_dai_ops = { + .hw_params = max9860_hw_params, + .set_fmt = max9860_set_fmt, +}; + +static struct snd_soc_dai_driver max9860_dai = { + .name = "max9860-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &max9860_dai_ops, + .symmetric_rate = 1, +}; + +static int max9860_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct max9860_priv *max9860 = dev_get_drvdata(component->dev); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN, + MAX9860_SHDN, MAX9860_SHDN); + if (ret) { + dev_err(component->dev, "Failed to remove SHDN: %d\n", + ret); + return ret; + } + break; + + case SND_SOC_BIAS_OFF: + ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN, + MAX9860_SHDN, 0); + if (ret) { + dev_err(component->dev, "Failed to request SHDN: %d\n", + ret); + return ret; + } + break; + } + + return 0; +} + +static const struct snd_soc_component_driver max9860_component_driver = { + .set_bias_level = max9860_set_bias_level, + .controls = max9860_controls, + .num_controls = ARRAY_SIZE(max9860_controls), + .dapm_widgets = max9860_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9860_dapm_widgets), + .dapm_routes = max9860_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9860_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +#ifdef CONFIG_PM +static int max9860_suspend(struct device *dev) +{ + struct max9860_priv *max9860 = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK, + MAX9860_PSCLK, MAX9860_PSCLK_OFF); + if (ret) { + dev_err(dev, "Failed to disable clock: %d\n", ret); + return ret; + } + + regulator_disable(max9860->dvddio); + + return 0; +} + +static int max9860_resume(struct device *dev) +{ + struct max9860_priv *max9860 = dev_get_drvdata(dev); + int ret; + + ret = regulator_enable(max9860->dvddio); + if (ret) { + dev_err(dev, "Failed to enable DVDDIO: %d\n", ret); + return ret; + } + + regcache_cache_only(max9860->regmap, false); + ret = regcache_sync(max9860->regmap); + if (ret) { + dev_err(dev, "Failed to sync cache: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK, + MAX9860_PSCLK, max9860->psclk); + if (ret) { + dev_err(dev, "Failed to enable clock: %d\n", ret); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops max9860_pm_ops = { + SET_RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL) +}; + +static int max9860_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct max9860_priv *max9860; + int ret; + struct clk *mclk; + unsigned long mclk_rate; + int i; + int intr; + + max9860 = devm_kzalloc(dev, sizeof(struct max9860_priv), GFP_KERNEL); + if (!max9860) + return -ENOMEM; + + max9860->dvddio = devm_regulator_get(dev, "DVDDIO"); + if (IS_ERR(max9860->dvddio)) + return dev_err_probe(dev, PTR_ERR(max9860->dvddio), + "Failed to get DVDDIO supply\n"); + + max9860->dvddio_nb.notifier_call = max9860_dvddio_event; + + ret = devm_regulator_register_notifier(max9860->dvddio, + &max9860->dvddio_nb); + if (ret) + dev_err(dev, "Failed to register DVDDIO notifier: %d\n", ret); + + ret = regulator_enable(max9860->dvddio); + if (ret != 0) { + dev_err(dev, "Failed to enable DVDDIO: %d\n", ret); + return ret; + } + + max9860->regmap = devm_regmap_init_i2c(i2c, &max9860_regmap); + if (IS_ERR(max9860->regmap)) { + ret = PTR_ERR(max9860->regmap); + goto err_regulator; + } + + dev_set_drvdata(dev, max9860); + + /* + * mclk has to be in the 10MHz to 60MHz range. + * psclk is used to scale mclk into pclk so that + * pclk is in the 10MHz to 20MHz range. + */ + mclk = clk_get(dev, "mclk"); + + if (IS_ERR(mclk)) { + ret = PTR_ERR(mclk); + dev_err_probe(dev, ret, "Failed to get MCLK\n"); + goto err_regulator; + } + + mclk_rate = clk_get_rate(mclk); + clk_put(mclk); + + if (mclk_rate > 60000000 || mclk_rate < 10000000) { + dev_err(dev, "Bad mclk %luHz (needs 10MHz - 60MHz)\n", + mclk_rate); + ret = -EINVAL; + goto err_regulator; + } + if (mclk_rate >= 40000000) + max9860->psclk = 3; + else if (mclk_rate >= 20000000) + max9860->psclk = 2; + else + max9860->psclk = 1; + max9860->pclk_rate = mclk_rate >> (max9860->psclk - 1); + max9860->psclk <<= MAX9860_PSCLK_SHIFT; + dev_dbg(dev, "mclk %lu pclk %lu\n", mclk_rate, max9860->pclk_rate); + + regcache_cache_bypass(max9860->regmap, true); + for (i = 0; i < max9860_regmap.num_reg_defaults; ++i) { + ret = regmap_write(max9860->regmap, + max9860_regmap.reg_defaults[i].reg, + max9860_regmap.reg_defaults[i].def); + if (ret) { + dev_err(dev, "Failed to initialize register %u: %d\n", + max9860_regmap.reg_defaults[i].reg, ret); + goto err_regulator; + } + } + regcache_cache_bypass(max9860->regmap, false); + + ret = regmap_read(max9860->regmap, MAX9860_INTRSTATUS, &intr); + if (ret) { + dev_err(dev, "Failed to clear INTRSTATUS: %d\n", ret); + goto err_regulator; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + ret = devm_snd_soc_register_component(dev, &max9860_component_driver, + &max9860_dai, 1); + if (ret) { + dev_err(dev, "Failed to register CODEC: %d\n", ret); + goto err_pm; + } + + return 0; + +err_pm: + pm_runtime_disable(dev); +err_regulator: + regulator_disable(max9860->dvddio); + return ret; +} + +static void max9860_remove(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct max9860_priv *max9860 = dev_get_drvdata(dev); + + pm_runtime_disable(dev); + regulator_disable(max9860->dvddio); +} + +static const struct i2c_device_id max9860_i2c_id[] = { + { "max9860", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9860_i2c_id); + +static const struct of_device_id max9860_of_match[] = { + { .compatible = "maxim,max9860", }, + { } +}; +MODULE_DEVICE_TABLE(of, max9860_of_match); + +static struct i2c_driver max9860_i2c_driver = { + .probe = max9860_probe, + .remove = max9860_remove, + .id_table = max9860_i2c_id, + .driver = { + .name = "max9860", + .of_match_table = max9860_of_match, + .pm = &max9860_pm_ops, + }, +}; + +module_i2c_driver(max9860_i2c_driver); + +MODULE_DESCRIPTION("ASoC MAX9860 Mono Audio Voice Codec driver"); +MODULE_AUTHOR("Peter Rosin "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/max9860.h b/sound/soc/codecs/max9860.h new file mode 100644 index 0000000000..e07b905eaf --- /dev/null +++ b/sound/soc/codecs/max9860.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Driver for the MAX9860 Mono Audio Voice Codec + * + * Author: Peter Rosin + * Copyright 2016 Axentia Technologies + */ + +#ifndef _SND_SOC_MAX9860 +#define _SND_SOC_MAX9860 + +#define MAX9860_INTRSTATUS 0x00 +#define MAX9860_MICREADBACK 0x01 +#define MAX9860_INTEN 0x02 +#define MAX9860_SYSCLK 0x03 +#define MAX9860_AUDIOCLKHIGH 0x04 +#define MAX9860_AUDIOCLKLOW 0x05 +#define MAX9860_IFC1A 0x06 +#define MAX9860_IFC1B 0x07 +#define MAX9860_VOICEFLTR 0x08 +#define MAX9860_DACATTN 0x09 +#define MAX9860_ADCLEVEL 0x0a +#define MAX9860_DACGAIN 0x0b +#define MAX9860_MICGAIN 0x0c +#define MAX9860_RESERVED 0x0d +#define MAX9860_MICADC 0x0e +#define MAX9860_NOISEGATE 0x0f +#define MAX9860_PWRMAN 0x10 +#define MAX9860_REVISION 0xff + +#define MAX9860_MAX_REGISTER 0xff + +/* INTRSTATUS */ +#define MAX9860_CLD 0x80 +#define MAX9860_SLD 0x40 +#define MAX9860_ULK 0x20 + +/* MICREADBACK */ +#define MAX9860_NG 0xe0 +#define MAX9860_AGC 0x1f + +/* INTEN */ +#define MAX9860_ICLD 0x80 +#define MAX9860_ISLD 0x40 +#define MAX9860_IULK 0x20 + +/* SYSCLK */ +#define MAX9860_PSCLK 0x30 +#define MAX9860_PSCLK_OFF 0x00 +#define MAX9860_PSCLK_SHIFT 4 +#define MAX9860_FREQ 0x06 +#define MAX9860_FREQ_NORMAL 0x00 +#define MAX9860_FREQ_12MHZ 0x02 +#define MAX9860_FREQ_13MHZ 0x04 +#define MAX9860_FREQ_19_2MHZ 0x06 +#define MAX9860_16KHZ 0x01 + +/* AUDIOCLKHIGH */ +#define MAX9860_PLL 0x80 +#define MAX9860_NHI 0x7f + +/* AUDIOCLKLOW */ +#define MAX9860_NLO 0xff + +/* IFC1A */ +#define MAX9860_MASTER 0x80 +#define MAX9860_WCI 0x40 +#define MAX9860_DBCI 0x20 +#define MAX9860_DDLY 0x10 +#define MAX9860_HIZ 0x08 +#define MAX9860_TDM 0x04 + +/* IFC1B */ +#define MAX9860_ABCI 0x20 +#define MAX9860_ADLY 0x10 +#define MAX9860_ST 0x08 +#define MAX9860_BSEL 0x07 +#define MAX9860_BSEL_OFF 0x00 +#define MAX9860_BSEL_64X 0x01 +#define MAX9860_BSEL_48X 0x02 +#define MAX9860_BSEL_PCLK_2 0x04 +#define MAX9860_BSEL_PCLK_4 0x05 +#define MAX9860_BSEL_PCLK_8 0x06 +#define MAX9860_BSEL_PCLK_16 0x07 + +/* VOICEFLTR */ +#define MAX9860_AVFLT 0xf0 +#define MAX9860_AVFLT_SHIFT 4 +#define MAX9860_AVFLT_COUNT 6 +#define MAX9860_DVFLT 0x0f +#define MAX9860_DVFLT_SHIFT 0 +#define MAX9860_DVFLT_COUNT 6 + +/* DACATTN */ +#define MAX9860_DVA 0xfe +#define MAX9860_DVA_SHIFT 1 +#define MAX9860_DVA_MUTE 0x5e + +/* ADCLEVEL */ +#define MAX9860_ADCRL 0xf0 +#define MAX9860_ADCRL_SHIFT 4 +#define MAX9860_ADCLL 0x0f +#define MAX9860_ADCLL_SHIFT 0 +#define MAX9860_ADCxL_MIN 15 + +/* DACGAIN */ +#define MAX9860_DVG 0x60 +#define MAX9860_DVG_SHIFT 5 +#define MAX9860_DVG_MAX 3 +#define MAX9860_DVST 0x1f +#define MAX9860_DVST_SHIFT 0 +#define MAX9860_DVST_MIN 31 + +/* MICGAIN */ +#define MAX9860_PAM 0x60 +#define MAX9860_PAM_SHIFT 5 +#define MAX9860_PAM_MAX 3 +#define MAX9860_PGAM 0x1f +#define MAX9860_PGAM_SHIFT 0 +#define MAX9860_PGAM_MIN 20 + +/* MICADC */ +#define MAX9860_AGCSRC 0x80 +#define MAX9860_AGCSRC_SHIFT 7 +#define MAX9860_AGCSRC_COUNT 2 +#define MAX9860_AGCRLS 0x70 +#define MAX9860_AGCRLS_SHIFT 4 +#define MAX9860_AGCRLS_COUNT 8 +#define MAX9860_AGCATK 0x0c +#define MAX9860_AGCATK_SHIFT 2 +#define MAX9860_AGCATK_COUNT 4 +#define MAX9860_AGCHLD 0x03 +#define MAX9860_AGCHLD_OFF 0x00 +#define MAX9860_AGCHLD_SHIFT 0 +#define MAX9860_AGCHLD_COUNT 4 + +/* NOISEGATE */ +#define MAX9860_ANTH 0xf0 +#define MAX9860_ANTH_SHIFT 4 +#define MAX9860_ANTH_MAX 15 +#define MAX9860_AGCTH 0x0f +#define MAX9860_AGCTH_SHIFT 0 +#define MAX9860_AGCTH_MIN 15 + +/* PWRMAN */ +#define MAX9860_SHDN 0x80 +#define MAX9860_DACEN 0x08 +#define MAX9860_DACEN_SHIFT 3 +#define MAX9860_ADCLEN 0x02 +#define MAX9860_ADCLEN_SHIFT 1 +#define MAX9860_ADCREN 0x01 +#define MAX9860_ADCREN_SHIFT 0 + +#endif /* _SND_SOC_MAX9860 */ diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c new file mode 100644 index 0000000000..b616ad3985 --- /dev/null +++ b/sound/soc/codecs/max9867.c @@ -0,0 +1,713 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// MAX9867 ALSA SoC codec driver +// +// Copyright 2013-2015 Maxim Integrated Products +// Copyright 2018 Ladislav Michl +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include "max9867.h" + +struct max9867_priv { + struct clk *mclk; + struct regmap *regmap; + const struct snd_pcm_hw_constraint_list *constraints; + unsigned int sysclk, pclk; + bool provider, dsp_a; + unsigned int adc_dac_active; +}; + +static const char *const max9867_spmode[] = { + "Stereo Diff", "Mono Diff", + "Stereo Cap", "Mono Cap", + "Stereo Single", "Mono Single", + "Stereo Single Fast", "Mono Single Fast" +}; +static const char *const max9867_filter_text[] = {"IIR", "FIR"}; + +static const char *const max9867_adc_dac_filter_text[] = { + "Disabled", + "Elliptical/16/256", + "Butterworth/16/500", + "Elliptical/8/256", + "Butterworth/8/500", + "Butterworth/8-24" +}; + +enum max9867_adc_dac { + MAX9867_ADC_LEFT, + MAX9867_ADC_RIGHT, + MAX9867_DAC_LEFT, + MAX9867_DAC_RIGHT, +}; + +static int max9867_adc_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + enum max9867_adc_dac adc_dac; + + if (!strcmp(w->name, "ADCL")) + adc_dac = MAX9867_ADC_LEFT; + else if (!strcmp(w->name, "ADCR")) + adc_dac = MAX9867_ADC_RIGHT; + else if (!strcmp(w->name, "DACL")) + adc_dac = MAX9867_DAC_LEFT; + else if (!strcmp(w->name, "DACR")) + adc_dac = MAX9867_DAC_RIGHT; + else + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) + max9867->adc_dac_active |= BIT(adc_dac); + else if (SND_SOC_DAPM_EVENT_OFF(event)) + max9867->adc_dac_active &= ~BIT(adc_dac); + + return 0; +} + +static int max9867_filter_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + unsigned int reg; + int ret; + + ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, ®); + if (ret) + return -EINVAL; + + if (reg & MAX9867_CODECFLTR_MODE) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + return 0; +} + +static int max9867_filter_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + unsigned int reg, mode = ucontrol->value.enumerated.item[0]; + int ret; + + if (mode > 1) + return -EINVAL; + + /* don't allow change if ADC/DAC active */ + if (max9867->adc_dac_active) + return -EBUSY; + + /* read current filter mode */ + ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, ®); + if (ret) + return -EINVAL; + + if (mode) + mode = MAX9867_CODECFLTR_MODE; + + /* check if change is needed */ + if ((reg & MAX9867_CODECFLTR_MODE) == mode) + return 0; + + /* shutdown codec before switching filter mode */ + regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_PWRMAN_SHDN, 0); + + /* switch filter mode */ + regmap_update_bits(max9867->regmap, MAX9867_CODECFLTR, + MAX9867_CODECFLTR_MODE, mode); + + /* out of shutdown now */ + regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_PWRMAN_SHDN, MAX9867_PWRMAN_SHDN); + + return 0; +} + +static SOC_ENUM_SINGLE_EXT_DECL(max9867_filter, max9867_filter_text); +static SOC_ENUM_SINGLE_DECL(max9867_dac_filter, MAX9867_CODECFLTR, 0, + max9867_adc_dac_filter_text); +static SOC_ENUM_SINGLE_DECL(max9867_adc_filter, MAX9867_CODECFLTR, 4, + max9867_adc_dac_filter_text); +static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0, + max9867_spmode); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_master_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-8600, 200, 1), + 3, 17, TLV_DB_SCALE_ITEM(-7800, 400, 0), + 18, 25, TLV_DB_SCALE_ITEM(-2000, 200, 0), + 26, 34, TLV_DB_SCALE_ITEM( -500, 100, 0), + 35, 40, TLV_DB_SCALE_ITEM( 350, 50, 0), +); +static DECLARE_TLV_DB_SCALE(max9867_mic_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(max9867_line_tlv, -600, 200, 0); +static DECLARE_TLV_DB_SCALE(max9867_adc_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(max9867_dac_tlv, -1500, 100, 0); +static DECLARE_TLV_DB_SCALE(max9867_dacboost_tlv, 0, 600, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-2000, 2000, 1), + 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0), +); + +static const struct snd_kcontrol_new max9867_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL, + MAX9867_RIGHTVOL, 0, 40, 1, max9867_master_tlv), + SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL, + MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv), + SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv), + SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, 5, 3, 0, max9867_micboost_tlv), + SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1), + SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1, + max9867_dac_tlv), + SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0, + max9867_dacboost_tlv), + SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 4, 0, 15, 1, + max9867_adc_tlv), + SOC_ENUM("Speaker Mode", max9867_spkmode), + SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), + SOC_SINGLE("Line ZC Switch", MAX9867_MODECONFIG, 5, 1, 0), + SOC_ENUM_EXT("DSP Filter", max9867_filter, max9867_filter_get, max9867_filter_set), + SOC_ENUM("ADC Filter", max9867_adc_filter), + SOC_ENUM("DAC Filter", max9867_dac_filter), + SOC_SINGLE("Mono Playback Switch", MAX9867_IFC1B, 3, 1, 0), +}; + +/* Input mixer */ +static const struct snd_kcontrol_new max9867_input_mixer_controls[] = { + SOC_DAPM_DOUBLE("Line Capture Switch", MAX9867_INPUTCONFIG, 7, 5, 1, 0), + SOC_DAPM_DOUBLE("Mic Capture Switch", MAX9867_INPUTCONFIG, 6, 4, 1, 0), +}; + +/* Output mixer */ +static const struct snd_kcontrol_new max9867_output_mixer_controls[] = { + SOC_DAPM_DOUBLE_R("Line Bypass Switch", + MAX9867_LEFTLINELVL, MAX9867_RIGHTLINELVL, 6, 1, 1), +}; + +/* Sidetone mixer */ +static const struct snd_kcontrol_new max9867_sidetone_mixer_controls[] = { + SOC_DAPM_DOUBLE("Sidetone Switch", MAX9867_SIDETONE, 6, 7, 1, 0), +}; + +/* Line out switch */ +static const struct snd_kcontrol_new max9867_line_out_control = + SOC_DAPM_DOUBLE_R("Switch", + MAX9867_LEFTVOL, MAX9867_RIGHTVOL, 6, 1, 1); + +/* DMIC mux */ +static const char *const dmic_mux_text[] = { + "ADC", "DMIC" +}; +static SOC_ENUM_SINGLE_DECL(left_dmic_mux_enum, + MAX9867_MICCONFIG, 5, dmic_mux_text); +static SOC_ENUM_SINGLE_DECL(right_dmic_mux_enum, + MAX9867_MICCONFIG, 4, dmic_mux_text); +static const struct snd_kcontrol_new max9867_left_dmic_mux = + SOC_DAPM_ENUM("DMICL Mux", left_dmic_mux_enum); +static const struct snd_kcontrol_new max9867_right_dmic_mux = + SOC_DAPM_ENUM("DMICR Mux", right_dmic_mux_enum); + +static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("MICR"), + SND_SOC_DAPM_INPUT("DMICL"), + SND_SOC_DAPM_INPUT("DMICR"), + SND_SOC_DAPM_INPUT("LINL"), + SND_SOC_DAPM_INPUT("LINR"), + + SND_SOC_DAPM_PGA("Left Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0, + max9867_input_mixer_controls, + ARRAY_SIZE(max9867_input_mixer_controls)), + SND_SOC_DAPM_MUX("DMICL Mux", SND_SOC_NOPM, 0, 0, + &max9867_left_dmic_mux), + SND_SOC_DAPM_MUX("DMICR Mux", SND_SOC_NOPM, 0, 0, + &max9867_right_dmic_mux), + SND_SOC_DAPM_ADC_E("ADCL", "HiFi Capture", SND_SOC_NOPM, 0, 0, + max9867_adc_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADCR", "HiFi Capture", SND_SOC_NOPM, 0, 0, + max9867_adc_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("Digital", SND_SOC_NOPM, 0, 0, + max9867_sidetone_mixer_controls, + ARRAY_SIZE(max9867_sidetone_mixer_controls)), + SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", SND_SOC_NOPM, 0, 0, + max9867_output_mixer_controls, + ARRAY_SIZE(max9867_output_mixer_controls)), + SND_SOC_DAPM_DAC_E("DACL", "HiFi Playback", SND_SOC_NOPM, 0, 0, + max9867_adc_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DACR", "HiFi Playback", SND_SOC_NOPM, 0, 0, + max9867_adc_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SWITCH("Master Playback", SND_SOC_NOPM, 0, 0, + &max9867_line_out_control), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), +}; + +static const struct snd_soc_dapm_route max9867_audio_map[] = { + {"Left Line Input", NULL, "LINL"}, + {"Right Line Input", NULL, "LINR"}, + {"Input Mixer", "Mic Capture Switch", "MICL"}, + {"Input Mixer", "Mic Capture Switch", "MICR"}, + {"Input Mixer", "Line Capture Switch", "Left Line Input"}, + {"Input Mixer", "Line Capture Switch", "Right Line Input"}, + {"DMICL Mux", "DMIC", "DMICL"}, + {"DMICR Mux", "DMIC", "DMICR"}, + {"DMICL Mux", "ADC", "Input Mixer"}, + {"DMICR Mux", "ADC", "Input Mixer"}, + {"ADCL", NULL, "DMICL Mux"}, + {"ADCR", NULL, "DMICR Mux"}, + + {"Digital", "Sidetone Switch", "ADCL"}, + {"Digital", "Sidetone Switch", "ADCR"}, + {"DACL", NULL, "Digital"}, + {"DACR", NULL, "Digital"}, + + {"Output Mixer", "Line Bypass Switch", "Left Line Input"}, + {"Output Mixer", "Line Bypass Switch", "Right Line Input"}, + {"Output Mixer", NULL, "DACL"}, + {"Output Mixer", NULL, "DACR"}, + {"Master Playback", "Switch", "Output Mixer"}, + {"LOUT", NULL, "Master Playback"}, + {"ROUT", NULL, "Master Playback"}, +}; + +static const unsigned int max9867_rates_44k1[] = { + 11025, 22050, 44100, +}; + +static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = { + .list = max9867_rates_44k1, + .count = ARRAY_SIZE(max9867_rates_44k1), +}; + +static const unsigned int max9867_rates_48k[] = { + 8000, 16000, 32000, 48000, +}; + +static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = { + .list = max9867_rates_48k, + .count = ARRAY_SIZE(max9867_rates_48k), +}; + +static int max9867_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct max9867_priv *max9867 = + snd_soc_component_get_drvdata(dai->component); + + if (max9867->constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, max9867->constraints); + + return 0; +} + +static int max9867_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + int value, freq = 0; + unsigned long int rate, ratio; + struct snd_soc_component *component = dai->component; + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params), + max9867->pclk); + + /* set up the ni value */ + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, + MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8); + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, + MAX9867_NI_LOW_MASK, 0x00FF & ni); + if (max9867->provider) { + if (max9867->dsp_a) { + value = MAX9867_IFC1B_48X; + } else { + rate = params_rate(params) * 2 * params_width(params); + ratio = max9867->pclk / rate; + switch (params_width(params)) { + case 8: + case 16: + switch (ratio) { + case 2: + value = MAX9867_IFC1B_PCLK_2; + break; + case 4: + value = MAX9867_IFC1B_PCLK_4; + break; + case 8: + value = MAX9867_IFC1B_PCLK_8; + break; + case 16: + value = MAX9867_IFC1B_PCLK_16; + break; + default: + return -EINVAL; + } + break; + case 24: + value = MAX9867_IFC1B_48X; + break; + case 32: + value = MAX9867_IFC1B_64X; + break; + default: + return -EINVAL; + } + } + regmap_update_bits(max9867->regmap, MAX9867_IFC1B, + MAX9867_IFC1B_BCLK_MASK, value); + + /* Exact integer mode available for 8kHz and 16kHz sample rates + * and certain PCLK (prescaled MCLK) values. + */ + if (params_rate(params) == 8000 || + params_rate(params) == 16000) { + switch (max9867->pclk) { + case 12000000: + freq = 0x08; + break; + case 13000000: + freq = 0x0A; + break; + case 16000000: + freq = 0x0C; + break; + case 19200000: + freq = 0x0E; + break; + } + } + if (freq && params_rate(params) == 16000) + freq++; + + /* If exact integer mode not available, the freq value + * remains zero, i.e. normal mode is used. + */ + regmap_update_bits(max9867->regmap, MAX9867_SYSCLK, + MAX9867_FREQ_MASK, freq); + } else { + /* + * digital pll locks on to any externally supplied LRCLK signal + * and also enable rapid lock mode. + */ + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, + MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, + MAX9867_PLL, MAX9867_PLL); + } + return 0; +} + +static int max9867_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + + return regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, + 1 << 6, !!mute << 6); +} + +static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + int value = 0; + + /* Set the prescaler based on the master clock frequency*/ + if (freq >= 10000000 && freq <= 20000000) { + value |= MAX9867_PSCLK_10_20; + max9867->pclk = freq; + } else if (freq >= 20000000 && freq <= 40000000) { + value |= MAX9867_PSCLK_20_40; + max9867->pclk = freq / 2; + } else if (freq >= 40000000 && freq <= 60000000) { + value |= MAX9867_PSCLK_40_60; + max9867->pclk = freq / 4; + } else { + dev_err(component->dev, + "Invalid clock frequency %uHz (required 10-60MHz)\n", + freq); + return -EINVAL; + } + if (freq % 48000 == 0) + max9867->constraints = &max9867_constraints_48k; + else if (freq % 44100 == 0) + max9867->constraints = &max9867_constraints_44k1; + else + dev_warn(component->dev, + "Unable to set exact rate with %uHz clock frequency\n", + freq); + max9867->sysclk = freq; + value = value << MAX9867_PSCLK_SHIFT; + regmap_update_bits(max9867->regmap, MAX9867_SYSCLK, + MAX9867_PSCLK_MASK, value); + return 0; +} + +static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + u8 iface1A, iface1B; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + max9867->provider = true; + iface1A = MAX9867_MASTER; + iface1B = MAX9867_IFC1B_48X; + break; + case SND_SOC_DAIFMT_CBC_CFC: + max9867->provider = false; + iface1A = iface1B = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + max9867->dsp_a = false; + iface1A |= MAX9867_I2S_DLY; + break; + case SND_SOC_DAIFMT_DSP_A: + max9867->dsp_a = true; + iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ; + break; + default: + return -EINVAL; + } + + /* Clock inversion bits, BCI and WCI */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE; + break; + case SND_SOC_DAIFMT_IB_NF: + iface1A |= MAX9867_BCI_MODE; + break; + case SND_SOC_DAIFMT_NB_IF: + iface1A |= MAX9867_WCI_MODE; + break; + default: + return -EINVAL; + } + + regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); + regmap_update_bits(max9867->regmap, MAX9867_IFC1B, + MAX9867_IFC1B_BCLK_MASK, iface1B); + + return 0; +} + +static const struct snd_soc_dai_ops max9867_dai_ops = { + .set_sysclk = max9867_set_dai_sysclk, + .set_fmt = max9867_dai_set_fmt, + .mute_stream = max9867_mute, + .startup = max9867_startup, + .hw_params = max9867_dai_hw_params, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver max9867_dai[] = { + { + .name = "max9867-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &max9867_dai_ops, + .symmetric_rate = 1, + } +}; + +#ifdef CONFIG_PM +static int max9867_suspend(struct snd_soc_component *component) +{ + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + return 0; +} + +static int max9867_resume(struct snd_soc_component *component) +{ + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define max9867_suspend NULL +#define max9867_resume NULL +#endif + +static int max9867_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int err; + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + err = clk_prepare_enable(max9867->mclk); + if (err) + return err; + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + err = regcache_sync(max9867->regmap); + if (err) + return err; + + err = regmap_write(max9867->regmap, + MAX9867_PWRMAN, 0xff); + if (err) + return err; + } + break; + case SND_SOC_BIAS_OFF: + err = regmap_write(max9867->regmap, MAX9867_PWRMAN, 0); + if (err) + return err; + + regcache_mark_dirty(max9867->regmap); + clk_disable_unprepare(max9867->mclk); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_component_driver max9867_component = { + .controls = max9867_snd_controls, + .num_controls = ARRAY_SIZE(max9867_snd_controls), + .dapm_routes = max9867_audio_map, + .num_dapm_routes = ARRAY_SIZE(max9867_audio_map), + .dapm_widgets = max9867_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets), + .suspend = max9867_suspend, + .resume = max9867_resume, + .set_bias_level = max9867_set_bias_level, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static bool max9867_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX9867_STATUS: + case MAX9867_JACKSTATUS: + case MAX9867_AUXHIGH: + case MAX9867_AUXLOW: + return true; + default: + return false; + } +} + +static const struct regmap_config max9867_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX9867_REVISION, + .volatile_reg = max9867_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max9867_i2c_probe(struct i2c_client *i2c) +{ + struct max9867_priv *max9867; + int ret, reg; + + max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL); + if (!max9867) + return -ENOMEM; + + i2c_set_clientdata(i2c, max9867); + max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap); + if (IS_ERR(max9867->regmap)) { + ret = PTR_ERR(max9867->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + ret = regmap_read(max9867->regmap, MAX9867_REVISION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read: %d\n", ret); + return ret; + } + dev_info(&i2c->dev, "device revision: %x\n", reg); + ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component, + max9867_dai, ARRAY_SIZE(max9867_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register component: %d\n", ret); + return ret; + } + + max9867->mclk = devm_clk_get(&i2c->dev, NULL); + if (IS_ERR(max9867->mclk)) + return PTR_ERR(max9867->mclk); + + return 0; +} + +static const struct i2c_device_id max9867_i2c_id[] = { + { "max9867", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9867_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id max9867_of_match[] = { + { .compatible = "maxim,max9867", }, + { } +}; +MODULE_DEVICE_TABLE(of, max9867_of_match); +#endif + +static struct i2c_driver max9867_i2c_driver = { + .driver = { + .name = "max9867", + .of_match_table = of_match_ptr(max9867_of_match), + }, + .probe = max9867_i2c_probe, + .id_table = max9867_i2c_id, +}; + +module_i2c_driver(max9867_i2c_driver); + +MODULE_AUTHOR("Ladislav Michl "); +MODULE_DESCRIPTION("ASoC MAX9867 driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h new file mode 100644 index 0000000000..b6b880631b --- /dev/null +++ b/sound/soc/codecs/max9867.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * max9867.h -- MAX9867 ALSA SoC Audio driver + * + * Copyright 2013-2015 Maxim Integrated Products + */ + +#ifndef _MAX9867_H +#define _MAX9867_H + +/* MAX9867 register space */ + +#define MAX9867_STATUS 0x00 +#define MAX9867_JACKSTATUS 0x01 +#define MAX9867_AUXHIGH 0x02 +#define MAX9867_AUXLOW 0x03 +#define MAX9867_INTEN 0x04 +#define MAX9867_SYSCLK 0x05 +#define MAX9867_FREQ_MASK 0xF +#define MAX9867_PSCLK_SHIFT 0x4 +#define MAX9867_PSCLK_WIDTH 0x2 +#define MAX9867_PSCLK_MASK (0x03< + */ + +#include +#include +#include +#include +#include +#include + +#include "max9877.h" + +static const struct reg_default max9877_regs[] = { + { 0, 0x40 }, + { 1, 0x00 }, + { 2, 0x00 }, + { 3, 0x00 }, + { 4, 0x49 }, +}; + +static const DECLARE_TLV_DB_RANGE(max9877_pgain_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 900, 0), + 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0) +); + +static const DECLARE_TLV_DB_RANGE(max9877_output_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1), + 8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0), + 16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0), + 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0) +); + +static const char *max9877_out_mode[] = { + "INA -> SPK", + "INA -> HP", + "INA -> SPK and HP", + "INB -> SPK", + "INB -> HP", + "INB -> SPK and HP", + "INA + INB -> SPK", + "INA + INB -> HP", + "INA + INB -> SPK and HP", +}; + +static const char *max9877_osc_mode[] = { + "1176KHz", + "1100KHz", + "700KHz", +}; + +static const struct soc_enum max9877_enum[] = { + SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, 0, ARRAY_SIZE(max9877_out_mode), + max9877_out_mode), + SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, MAX9877_OSC_OFFSET, + ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode), +}; + +static const struct snd_kcontrol_new max9877_controls[] = { + SOC_SINGLE_TLV("MAX9877 PGAINA Playback Volume", + MAX9877_INPUT_MODE, 0, 2, 0, max9877_pgain_tlv), + SOC_SINGLE_TLV("MAX9877 PGAINB Playback Volume", + MAX9877_INPUT_MODE, 2, 2, 0, max9877_pgain_tlv), + SOC_SINGLE_TLV("MAX9877 Amp Speaker Playback Volume", + MAX9877_SPK_VOLUME, 0, 31, 0, max9877_output_tlv), + SOC_DOUBLE_R_TLV("MAX9877 Amp HP Playback Volume", + MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0, + max9877_output_tlv), + SOC_SINGLE("MAX9877 INB Stereo Switch", + MAX9877_INPUT_MODE, 4, 1, 1), + SOC_SINGLE("MAX9877 INA Stereo Switch", + MAX9877_INPUT_MODE, 5, 1, 1), + SOC_SINGLE("MAX9877 Zero-crossing detection Switch", + MAX9877_INPUT_MODE, 6, 1, 0), + SOC_SINGLE("MAX9877 Bypass Mode Switch", + MAX9877_OUTPUT_MODE, 6, 1, 0), + SOC_ENUM("MAX9877 Output Mode", max9877_enum[0]), + SOC_ENUM("MAX9877 Oscillator Mode", max9877_enum[1]), +}; + +static const struct snd_soc_dapm_widget max9877_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("INA1"), +SND_SOC_DAPM_INPUT("INA2"), +SND_SOC_DAPM_INPUT("INB1"), +SND_SOC_DAPM_INPUT("INB2"), +SND_SOC_DAPM_INPUT("RXIN+"), +SND_SOC_DAPM_INPUT("RXIN-"), + +SND_SOC_DAPM_PGA("SHDN", MAX9877_OUTPUT_MODE, 7, 1, NULL, 0), + +SND_SOC_DAPM_OUTPUT("OUT+"), +SND_SOC_DAPM_OUTPUT("OUT-"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +}; + +static const struct snd_soc_dapm_route max9877_dapm_routes[] = { + { "SHDN", NULL, "INA1" }, + { "SHDN", NULL, "INA2" }, + { "SHDN", NULL, "INB1" }, + { "SHDN", NULL, "INB2" }, + + { "OUT+", NULL, "RXIN+" }, + { "OUT+", NULL, "SHDN" }, + + { "OUT-", NULL, "SHDN" }, + { "OUT-", NULL, "RXIN-" }, + + { "HPL", NULL, "SHDN" }, + { "HPR", NULL, "SHDN" }, +}; + +static const struct snd_soc_component_driver max9877_component_driver = { + .controls = max9877_controls, + .num_controls = ARRAY_SIZE(max9877_controls), + + .dapm_widgets = max9877_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9877_dapm_widgets), + .dapm_routes = max9877_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9877_dapm_routes), +}; + +static const struct regmap_config max9877_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = max9877_regs, + .num_reg_defaults = ARRAY_SIZE(max9877_regs), + .cache_type = REGCACHE_RBTREE, +}; + +static int max9877_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + int i; + + regmap = devm_regmap_init_i2c(client, &max9877_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Ensure the device is in reset state */ + for (i = 0; i < ARRAY_SIZE(max9877_regs); i++) + regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def); + + return devm_snd_soc_register_component(&client->dev, + &max9877_component_driver, NULL, 0); +} + +static const struct i2c_device_id max9877_i2c_id[] = { + { "max9877", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9877_i2c_id); + +static struct i2c_driver max9877_i2c_driver = { + .driver = { + .name = "max9877", + }, + .probe = max9877_i2c_probe, + .id_table = max9877_i2c_id, +}; + +module_i2c_driver(max9877_i2c_driver); + +MODULE_DESCRIPTION("ASoC MAX9877 amp driver"); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9877.h b/sound/soc/codecs/max9877.h new file mode 100644 index 0000000000..3c2788175a --- /dev/null +++ b/sound/soc/codecs/max9877.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * max9877.h -- amp driver for max9877 + * + * Copyright (C) 2009 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + */ + +#ifndef _MAX9877_H +#define _MAX9877_H + +#define MAX9877_INPUT_MODE 0x00 +#define MAX9877_SPK_VOLUME 0x01 +#define MAX9877_HPL_VOLUME 0x02 +#define MAX9877_HPR_VOLUME 0x03 +#define MAX9877_OUTPUT_MODE 0x04 + +/* MAX9877_INPUT_MODE */ +#define MAX9877_INB (1 << 4) +#define MAX9877_INA (1 << 5) +#define MAX9877_ZCD (1 << 6) + +/* MAX9877_OUTPUT_MODE */ +#define MAX9877_OUTMODE_MASK (15 << 0) +#define MAX9877_OSC_MASK (3 << 4) +#define MAX9877_OSC_OFFSET 4 +#define MAX9877_BYPASS (1 << 6) +#define MAX9877_SHDN (1 << 7) + +#endif diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c new file mode 100644 index 0000000000..a9c1d85cd0 --- /dev/null +++ b/sound/soc/codecs/max98925.c @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * max98925.c -- ALSA SoC Stereo MAX98925 driver + * Copyright 2013-15 Maxim Integrated Products + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98925.h" + +static const char *const dai_text[] = { + "Left", "Right", "LeftRight", "LeftRightDiv2", +}; + +static const char * const max98925_boost_voltage_text[] = { + "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V", + "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V" +}; + +static SOC_ENUM_SINGLE_DECL(max98925_boost_voltage, + MAX98925_CONFIGURATION, M98925_BST_VOUT_SHIFT, + max98925_boost_voltage_text); + +static const char *const hpf_text[] = { + "Disable", "DC Block", "100Hz", "200Hz", "400Hz", "800Hz", +}; + +static const struct reg_default max98925_reg[] = { + { 0x0B, 0x00 }, /* IRQ Enable0 */ + { 0x0C, 0x00 }, /* IRQ Enable1 */ + { 0x0D, 0x00 }, /* IRQ Enable2 */ + { 0x0E, 0x00 }, /* IRQ Clear0 */ + { 0x0F, 0x00 }, /* IRQ Clear1 */ + { 0x10, 0x00 }, /* IRQ Clear2 */ + { 0x11, 0xC0 }, /* Map0 */ + { 0x12, 0x00 }, /* Map1 */ + { 0x13, 0x00 }, /* Map2 */ + { 0x14, 0xF0 }, /* Map3 */ + { 0x15, 0x00 }, /* Map4 */ + { 0x16, 0xAB }, /* Map5 */ + { 0x17, 0x89 }, /* Map6 */ + { 0x18, 0x00 }, /* Map7 */ + { 0x19, 0x00 }, /* Map8 */ + { 0x1A, 0x06 }, /* DAI Clock Mode 1 */ + { 0x1B, 0xC0 }, /* DAI Clock Mode 2 */ + { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */ + { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */ + { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */ + { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */ + { 0x20, 0x50 }, /* Format */ + { 0x21, 0x00 }, /* TDM Slot Select */ + { 0x22, 0x00 }, /* DOUT Configuration VMON */ + { 0x23, 0x00 }, /* DOUT Configuration IMON */ + { 0x24, 0x00 }, /* DOUT Configuration VBAT */ + { 0x25, 0x00 }, /* DOUT Configuration VBST */ + { 0x26, 0x00 }, /* DOUT Configuration FLAG */ + { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */ + { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */ + { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */ + { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */ + { 0x2B, 0x02 }, /* DOUT Drive Strength */ + { 0x2C, 0x90 }, /* Filters */ + { 0x2D, 0x00 }, /* Gain */ + { 0x2E, 0x02 }, /* Gain Ramping */ + { 0x2F, 0x00 }, /* Speaker Amplifier */ + { 0x30, 0x0A }, /* Threshold */ + { 0x31, 0x00 }, /* ALC Attack */ + { 0x32, 0x80 }, /* ALC Atten and Release */ + { 0x33, 0x00 }, /* ALC Infinite Hold Release */ + { 0x34, 0x92 }, /* ALC Configuration */ + { 0x35, 0x01 }, /* Boost Converter */ + { 0x36, 0x00 }, /* Block Enable */ + { 0x37, 0x00 }, /* Configuration */ + { 0x38, 0x00 }, /* Global Enable */ + { 0x3A, 0x00 }, /* Boost Limiter */ +}; + +static const struct soc_enum max98925_dai_enum = + SOC_ENUM_SINGLE(MAX98925_GAIN, 5, ARRAY_SIZE(dai_text), dai_text); + +static const struct soc_enum max98925_hpf_enum = + SOC_ENUM_SINGLE(MAX98925_FILTERS, 0, ARRAY_SIZE(hpf_text), hpf_text); + +static const struct snd_kcontrol_new max98925_hpf_sel_mux = + SOC_DAPM_ENUM("Rc Filter MUX Mux", max98925_hpf_enum); + +static const struct snd_kcontrol_new max98925_dai_sel_mux = + SOC_DAPM_ENUM("DAI IN MUX Mux", max98925_dai_enum); + +static int max98925_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(max98925->regmap, + MAX98925_BLOCK_ENABLE, + M98925_BST_EN_MASK | + M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK, + M98925_BST_EN_MASK | + M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(max98925->regmap, + MAX98925_BLOCK_ENABLE, M98925_BST_EN_MASK | + M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK, 0); + break; + default: + return 0; + } + return 0; +} + +static const struct snd_soc_dapm_widget max98925_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("DAI IN MUX", SND_SOC_NOPM, 0, 0, + &max98925_dai_sel_mux), + SND_SOC_DAPM_MUX("Rc Filter MUX", SND_SOC_NOPM, 0, 0, + &max98925_hpf_sel_mux), + SND_SOC_DAPM_DAC_E("Amp Enable", NULL, MAX98925_BLOCK_ENABLE, + M98925_SPK_EN_SHIFT, 0, max98925_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("Global Enable", MAX98925_GLOBAL_ENABLE, + M98925_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("BE_OUT"), +}; + +static const struct snd_soc_dapm_route max98925_audio_map[] = { + {"DAI IN MUX", "Left", "DAI_OUT"}, + {"DAI IN MUX", "Right", "DAI_OUT"}, + {"DAI IN MUX", "LeftRight", "DAI_OUT"}, + {"DAI IN MUX", "LeftRightDiv2", "DAI_OUT"}, + {"Rc Filter MUX", "Disable", "DAI IN MUX"}, + {"Rc Filter MUX", "DC Block", "DAI IN MUX"}, + {"Rc Filter MUX", "100Hz", "DAI IN MUX"}, + {"Rc Filter MUX", "200Hz", "DAI IN MUX"}, + {"Rc Filter MUX", "400Hz", "DAI IN MUX"}, + {"Rc Filter MUX", "800Hz", "DAI IN MUX"}, + {"Amp Enable", NULL, "Rc Filter MUX"}, + {"BE_OUT", NULL, "Amp Enable"}, + {"BE_OUT", NULL, "Global Enable"}, +}; + +static bool max98925_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98925_VBAT_DATA: + case MAX98925_VBST_DATA: + case MAX98925_LIVE_STATUS0: + case MAX98925_LIVE_STATUS1: + case MAX98925_LIVE_STATUS2: + case MAX98925_STATE0: + case MAX98925_STATE1: + case MAX98925_STATE2: + case MAX98925_FLAG0: + case MAX98925_FLAG1: + case MAX98925_FLAG2: + case MAX98925_REV_VERSION: + return true; + default: + return false; + } +} + +static bool max98925_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98925_IRQ_CLEAR0: + case MAX98925_IRQ_CLEAR1: + case MAX98925_IRQ_CLEAR2: + case MAX98925_ALC_HOLD_RLS: + return false; + default: + return true; + } +} + +static DECLARE_TLV_DB_SCALE(max98925_spk_tlv, -600, 100, 0); + +static const struct snd_kcontrol_new max98925_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", MAX98925_GAIN, + M98925_SPK_GAIN_SHIFT, (1<= rate) { + *value = rate_table[i].sr; + *n = rate_table[i].divisors[clock][0]; + *m = rate_table[i].divisors[clock][1]; + ret = 0; + break; + } + } + return ret; +} + +static void max98925_set_sense_data(struct max98925_priv *max98925) +{ + /* set VMON slots */ + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_VMON, + M98925_DAI_VMON_EN_MASK, M98925_DAI_VMON_EN_MASK); + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_VMON, + M98925_DAI_VMON_SLOT_MASK, + max98925->v_slot << M98925_DAI_VMON_SLOT_SHIFT); + /* set IMON slots */ + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_IMON, + M98925_DAI_IMON_EN_MASK, M98925_DAI_IMON_EN_MASK); + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_IMON, + M98925_DAI_IMON_SLOT_MASK, + max98925->i_slot << M98925_DAI_IMON_SLOT_SHIFT); +} + +static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component); + unsigned int invert = 0; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_MAS_MASK, 0); + max98925_set_sense_data(max98925); + break; + case SND_SOC_DAIFMT_CBP_CFP: + /* + * set left channel DAI to provider mode, + * right channel always consumer + */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_MAS_MASK, M98925_DAI_MAS_MASK); + break; + default: + dev_err(component->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + invert = M98925_DAI_WCI_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + invert = M98925_DAI_BCI_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + invert = M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported"); + return -EINVAL; + } + + regmap_update_bits(max98925->regmap, MAX98925_FORMAT, + M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK, invert); + return 0; +} + +static int max98925_set_clock(struct max98925_priv *max98925, + struct snd_pcm_hw_params *params) +{ + unsigned int dai_sr = 0, clock, mdll, n, m; + struct snd_soc_component *component = max98925->component; + int rate = params_rate(params); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98925->ch_size; + + switch (blr_clk_ratio) { + case 32: + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_32); + break; + case 48: + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_48); + break; + case 64: + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_64); + break; + default: + return -EINVAL; + } + + switch (max98925->sysclk) { + case 6000000: + clock = 0; + mdll = M98925_MDLL_MULT_MCLKx16; + break; + case 11289600: + clock = 1; + mdll = M98925_MDLL_MULT_MCLKx8; + break; + case 12000000: + clock = 0; + mdll = M98925_MDLL_MULT_MCLKx8; + break; + case 12288000: + clock = 2; + mdll = M98925_MDLL_MULT_MCLKx8; + break; + default: + dev_info(max98925->component->dev, "unsupported sysclk %d\n", + max98925->sysclk); + return -EINVAL; + } + + if (max98925_rate_value(component, rate, clock, &dai_sr, &n, &m)) + return -EINVAL; + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_SR_MASK, dai_sr << M98925_DAI_SR_SHIFT); + /* set DAI m divider */ + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_M_MSBS, m >> 8); + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_M_LSBS, m & 0xFF); + /* set DAI n divider */ + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_N_MSBS, n >> 8); + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_N_LSBS, n & 0xFF); + /* set MDLL */ + regmap_update_bits(max98925->regmap, MAX98925_DAI_CLK_MODE1, + M98925_MDLL_MULT_MASK, mdll << M98925_MDLL_MULT_SHIFT); + return 0; +} + +static int max98925_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component); + + switch (params_width(params)) { + case 16: + regmap_update_bits(max98925->regmap, + MAX98925_FORMAT, + M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_16); + max98925->ch_size = 16; + break; + case 24: + regmap_update_bits(max98925->regmap, + MAX98925_FORMAT, + M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_24); + max98925->ch_size = 24; + break; + case 32: + regmap_update_bits(max98925->regmap, + MAX98925_FORMAT, + M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_32); + max98925->ch_size = 32; + break; + default: + pr_err("%s: format unsupported %d", + __func__, params_format(params)); + return -EINVAL; + } + dev_dbg(component->dev, "%s: format supported %d", + __func__, params_format(params)); + return max98925_set_clock(max98925, params); +} + +static int max98925_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case 0: + /* use MCLK for Left channel, right channel always BCLK */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE1, + M98925_DAI_CLK_SOURCE_MASK, 0); + break; + case 1: + /* configure dai clock source to BCLK instead of MCLK */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE1, + M98925_DAI_CLK_SOURCE_MASK, + M98925_DAI_CLK_SOURCE_MASK); + break; + default: + return -EINVAL; + } + max98925->sysclk = freq; + return 0; +} + +#define MAX98925_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98925_dai_ops = { + .set_sysclk = max98925_dai_set_sysclk, + .set_fmt = max98925_dai_set_fmt, + .hw_params = max98925_dai_hw_params, +}; + +static struct snd_soc_dai_driver max98925_dai[] = { + { + .name = "max98925-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98925_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98925_FORMATS, + }, + .ops = &max98925_dai_ops, + } +}; + +static int max98925_probe(struct snd_soc_component *component) +{ + struct max98925_priv *max98925 = snd_soc_component_get_drvdata(component); + + max98925->component = component; + regmap_write(max98925->regmap, MAX98925_GLOBAL_ENABLE, 0x00); + /* It's not the default but we need to set DAI_DLY */ + regmap_write(max98925->regmap, + MAX98925_FORMAT, M98925_DAI_DLY_MASK); + regmap_write(max98925->regmap, MAX98925_TDM_SLOT_SELECT, 0xC8); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG1, 0xFF); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG2, 0xFF); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG3, 0xFF); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG4, 0xF0); + regmap_write(max98925->regmap, MAX98925_FILTERS, 0xD8); + regmap_write(max98925->regmap, MAX98925_ALC_CONFIGURATION, 0xF8); + regmap_write(max98925->regmap, MAX98925_CONFIGURATION, 0xF0); + /* Disable ALC muting */ + regmap_write(max98925->regmap, MAX98925_BOOST_LIMITER, 0xF8); + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_max98925 = { + .probe = max98925_probe, + .controls = max98925_snd_controls, + .num_controls = ARRAY_SIZE(max98925_snd_controls), + .dapm_routes = max98925_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98925_audio_map), + .dapm_widgets = max98925_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98925_dapm_widgets), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config max98925_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX98925_REV_VERSION, + .reg_defaults = max98925_reg, + .num_reg_defaults = ARRAY_SIZE(max98925_reg), + .volatile_reg = max98925_volatile_register, + .readable_reg = max98925_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98925_i2c_probe(struct i2c_client *i2c) +{ + int ret, reg; + u32 value; + struct max98925_priv *max98925; + + max98925 = devm_kzalloc(&i2c->dev, + sizeof(*max98925), GFP_KERNEL); + if (!max98925) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98925); + max98925->regmap = devm_regmap_init_i2c(i2c, &max98925_regmap); + if (IS_ERR(max98925->regmap)) { + ret = PTR_ERR(max98925->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + + if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { + if (value > M98925_DAI_VMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "vmon slot number is wrong:\n"); + return -EINVAL; + } + max98925->v_slot = value; + } + if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) { + if (value > M98925_DAI_IMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "imon slot number is wrong:\n"); + return -EINVAL; + } + max98925->i_slot = value; + } + + ret = regmap_read(max98925->regmap, MAX98925_REV_VERSION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Read revision failed\n"); + return ret; + } + + if ((reg != MAX98925_VERSION) && (reg != MAX98925_VERSION1)) { + ret = -ENODEV; + dev_err(&i2c->dev, "Invalid revision (%d 0x%02X)\n", + ret, reg); + return ret; + } + + dev_info(&i2c->dev, "device version 0x%02X\n", reg); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_max98925, + max98925_dai, ARRAY_SIZE(max98925_dai)); + if (ret < 0) + dev_err(&i2c->dev, + "Failed to register component: %d\n", ret); + return ret; +} + +static const struct i2c_device_id max98925_i2c_id[] = { + { "max98925", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98925_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id max98925_of_match[] = { + { .compatible = "maxim,max98925", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98925_of_match); +#endif + +static struct i2c_driver max98925_i2c_driver = { + .driver = { + .name = "max98925", + .of_match_table = of_match_ptr(max98925_of_match), + }, + .probe = max98925_i2c_probe, + .id_table = max98925_i2c_id, +}; + +module_i2c_driver(max98925_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98925 driver"); +MODULE_AUTHOR("Ralph Birt , Anish kumar "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98925.h b/sound/soc/codecs/max98925.h new file mode 100644 index 0000000000..6d55ccad27 --- /dev/null +++ b/sound/soc/codecs/max98925.h @@ -0,0 +1,829 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * max98925.h -- MAX98925 ALSA SoC Audio driver + * + * Copyright 2013-2015 Maxim Integrated Products + */ + +#ifndef _MAX98925_H +#define _MAX98925_H + +#define MAX98925_VERSION 0x51 +#define MAX98925_VERSION1 0x80 +#define MAX98925_VBAT_DATA 0x00 +#define MAX98925_VBST_DATA 0x01 +#define MAX98925_LIVE_STATUS0 0x02 +#define MAX98925_LIVE_STATUS1 0x03 +#define MAX98925_LIVE_STATUS2 0x04 +#define MAX98925_STATE0 0x05 +#define MAX98925_STATE1 0x06 +#define MAX98925_STATE2 0x07 +#define MAX98925_FLAG0 0x08 +#define MAX98925_FLAG1 0x09 +#define MAX98925_FLAG2 0x0A +#define MAX98925_IRQ_ENABLE0 0x0B +#define MAX98925_IRQ_ENABLE1 0x0C +#define MAX98925_IRQ_ENABLE2 0x0D +#define MAX98925_IRQ_CLEAR0 0x0E +#define MAX98925_IRQ_CLEAR1 0x0F +#define MAX98925_IRQ_CLEAR2 0x10 +#define MAX98925_MAP0 0x11 +#define MAX98925_MAP1 0x12 +#define MAX98925_MAP2 0x13 +#define MAX98925_MAP3 0x14 +#define MAX98925_MAP4 0x15 +#define MAX98925_MAP5 0x16 +#define MAX98925_MAP6 0x17 +#define MAX98925_MAP7 0x18 +#define MAX98925_MAP8 0x19 +#define MAX98925_DAI_CLK_MODE1 0x1A +#define MAX98925_DAI_CLK_MODE2 0x1B +#define MAX98925_DAI_CLK_DIV_M_MSBS 0x1C +#define MAX98925_DAI_CLK_DIV_M_LSBS 0x1D +#define MAX98925_DAI_CLK_DIV_N_MSBS 0x1E +#define MAX98925_DAI_CLK_DIV_N_LSBS 0x1F +#define MAX98925_FORMAT 0x20 +#define MAX98925_TDM_SLOT_SELECT 0x21 +#define MAX98925_DOUT_CFG_VMON 0x22 +#define MAX98925_DOUT_CFG_IMON 0x23 +#define MAX98925_DOUT_CFG_VBAT 0x24 +#define MAX98925_DOUT_CFG_VBST 0x25 +#define MAX98925_DOUT_CFG_FLAG 0x26 +#define MAX98925_DOUT_HIZ_CFG1 0x27 +#define MAX98925_DOUT_HIZ_CFG2 0x28 +#define MAX98925_DOUT_HIZ_CFG3 0x29 +#define MAX98925_DOUT_HIZ_CFG4 0x2A +#define MAX98925_DOUT_DRV_STRENGTH 0x2B +#define MAX98925_FILTERS 0x2C +#define MAX98925_GAIN 0x2D +#define MAX98925_GAIN_RAMPING 0x2E +#define MAX98925_SPK_AMP 0x2F +#define MAX98925_THRESHOLD 0x30 +#define MAX98925_ALC_ATTACK 0x31 +#define MAX98925_ALC_ATTEN_RLS 0x32 +#define MAX98925_ALC_HOLD_RLS 0x33 +#define MAX98925_ALC_CONFIGURATION 0x34 +#define MAX98925_BOOST_CONVERTER 0x35 +#define MAX98925_BLOCK_ENABLE 0x36 +#define MAX98925_CONFIGURATION 0x37 +#define MAX98925_GLOBAL_ENABLE 0x38 +#define MAX98925_BOOST_LIMITER 0x3A +#define MAX98925_REV_VERSION 0xFF + +#define MAX98925_REG_CNT (MAX98925_R03A_BOOST_LIMITER+1) + +/* MAX98925 Register Bit Fields */ + +/* MAX98925_R002_LIVE_STATUS0 */ +#define M98925_THERMWARN_STATUS_MASK (1<<3) +#define M98925_THERMWARN_STATUS_SHIFT 3 +#define M98925_THERMWARN_STATUS_WIDTH 1 +#define M98925_THERMSHDN_STATUS_MASK (1<<1) +#define M98925_THERMSHDN_STATUS_SHIFT 1 +#define M98925_THERMSHDN_STATUS_WIDTH 1 + +/* MAX98925_R003_LIVE_STATUS1 */ +#define M98925_SPKCURNT_STATUS_MASK (1<<5) +#define M98925_SPKCURNT_STATUS_SHIFT 5 +#define M98925_SPKCURNT_STATUS_WIDTH 1 +#define M98925_WATCHFAIL_STATUS_MASK (1<<4) +#define M98925_WATCHFAIL_STATUS_SHIFT 4 +#define M98925_WATCHFAIL_STATUS_WIDTH 1 +#define M98925_ALCINFH_STATUS_MASK (1<<3) +#define M98925_ALCINFH_STATUS_SHIFT 3 +#define M98925_ALCINFH_STATUS_WIDTH 1 +#define M98925_ALCACT_STATUS_MASK (1<<2) +#define M98925_ALCACT_STATUS_SHIFT 2 +#define M98925_ALCACT_STATUS_WIDTH 1 +#define M98925_ALCMUT_STATUS_MASK (1<<1) +#define M98925_ALCMUT_STATUS_SHIFT 1 +#define M98925_ALCMUT_STATUS_WIDTH 1 +#define M98925_ACLP_STATUS_MASK (1<<0) +#define M98925_ACLP_STATUS_SHIFT 0 +#define M98925_ACLP_STATUS_WIDTH 1 + +/* MAX98925_R004_LIVE_STATUS2 */ +#define M98925_SLOTOVRN_STATUS_MASK (1<<6) +#define M98925_SLOTOVRN_STATUS_SHIFT 6 +#define M98925_SLOTOVRN_STATUS_WIDTH 1 +#define M98925_INVALSLOT_STATUS_MASK (1<<5) +#define M98925_INVALSLOT_STATUS_SHIFT 5 +#define M98925_INVALSLOT_STATUS_WIDTH 1 +#define M98925_SLOTCNFLT_STATUS_MASK (1<<4) +#define M98925_SLOTCNFLT_STATUS_SHIFT 4 +#define M98925_SLOTCNFLT_STATUS_WIDTH 1 +#define M98925_VBSTOVFL_STATUS_MASK (1<<3) +#define M98925_VBSTOVFL_STATUS_SHIFT 3 +#define M98925_VBSTOVFL_STATUS_WIDTH 1 +#define M98925_VBATOVFL_STATUS_MASK (1<<2) +#define M98925_VBATOVFL_STATUS_SHIFT 2 +#define M98925_VBATOVFL_STATUS_WIDTH 1 +#define M98925_IMONOVFL_STATUS_MASK (1<<1) +#define M98925_IMONOVFL_STATUS_SHIFT 1 +#define M98925_IMONOVFL_STATUS_WIDTH 1 +#define M98925_VMONOVFL_STATUS_MASK (1<<0) +#define M98925_VMONOVFL_STATUS_SHIFT 0 +#define M98925_VMONOVFL_STATUS_WIDTH 1 + +/* MAX98925_R005_STATE0 */ +#define M98925_THERMWARN_END_STATE_MASK (1<<3) +#define M98925_THERMWARN_END_STATE_SHIFT 3 +#define M98925_THERMWARN_END_STATE_WIDTH 1 +#define M98925_THERMWARN_BGN_STATE_MASK (1<<2) +#define M98925_THERMWARN_BGN_STATE_SHIFT 1 +#define M98925_THERMWARN_BGN_STATE_WIDTH 1 +#define M98925_THERMSHDN_END_STATE_MASK (1<<1) +#define M98925_THERMSHDN_END_STATE_SHIFT 1 +#define M98925_THERMSHDN_END_STATE_WIDTH 1 +#define M98925_THERMSHDN_BGN_STATE_MASK (1<<0) +#define M98925_THERMSHDN_BGN_STATE_SHIFT 0 +#define M98925_THERMSHDN_BGN_STATE_WIDTH 1 + +/* MAX98925_R006_STATE1 */ +#define M98925_SPRCURNT_STATE_MASK (1<<5) +#define M98925_SPRCURNT_STATE_SHIFT 5 +#define M98925_SPRCURNT_STATE_WIDTH 1 +#define M98925_WATCHFAIL_STATE_MASK (1<<4) +#define M98925_WATCHFAIL_STATE_SHIFT 4 +#define M98925_WATCHFAIL_STATE_WIDTH 1 +#define M98925_ALCINFH_STATE_MASK (1<<3) +#define M98925_ALCINFH_STATE_SHIFT 3 +#define M98925_ALCINFH_STATE_WIDTH 1 +#define M98925_ALCACT_STATE_MASK (1<<2) +#define M98925_ALCACT_STATE_SHIFT 2 +#define M98925_ALCACT_STATE_WIDTH 1 +#define M98925_ALCMUT_STATE_MASK (1<<1) +#define M98925_ALCMUT_STATE_SHIFT 1 +#define M98925_ALCMUT_STATE_WIDTH 1 +#define M98925_ALCP_STATE_MASK (1<<0) +#define M98925_ALCP_STATE_SHIFT 0 +#define M98925_ALCP_STATE_WIDTH 1 + +/* MAX98925_R007_STATE2 */ +#define M98925_SLOTOVRN_STATE_MASK (1<<6) +#define M98925_SLOTOVRN_STATE_SHIFT 6 +#define M98925_SLOTOVRN_STATE_WIDTH 1 +#define M98925_INVALSLOT_STATE_MASK (1<<5) +#define M98925_INVALSLOT_STATE_SHIFT 5 +#define M98925_INVALSLOT_STATE_WIDTH 1 +#define M98925_SLOTCNFLT_STATE_MASK (1<<4) +#define M98925_SLOTCNFLT_STATE_SHIFT 4 +#define M98925_SLOTCNFLT_STATE_WIDTH 1 +#define M98925_VBSTOVFL_STATE_MASK (1<<3) +#define M98925_VBSTOVFL_STATE_SHIFT 3 +#define M98925_VBSTOVFL_STATE_WIDTH 1 +#define M98925_VBATOVFL_STATE_MASK (1<<2) +#define M98925_VBATOVFL_STATE_SHIFT 2 +#define M98925_VBATOVFL_STATE_WIDTH 1 +#define M98925_IMONOVFL_STATE_MASK (1<<1) +#define M98925_IMONOVFL_STATE_SHIFT 1 +#define M98925_IMONOVFL_STATE_WIDTH 1 +#define M98925_VMONOVFL_STATE_MASK (1<<0) +#define M98925_VMONOVFL_STATE_SHIFT 0 +#define M98925_VMONOVFL_STATE_WIDTH 1 + +/* MAX98925_R008_FLAG0 */ +#define M98925_THERMWARN_END_FLAG_MASK (1<<3) +#define M98925_THERMWARN_END_FLAG_SHIFT 3 +#define M98925_THERMWARN_END_FLAG_WIDTH 1 +#define M98925_THERMWARN_BGN_FLAG_MASK (1<<2) +#define M98925_THERMWARN_BGN_FLAG_SHIFT 2 +#define M98925_THERMWARN_BGN_FLAG_WIDTH 1 +#define M98925_THERMSHDN_END_FLAG_MASK (1<<1) +#define M98925_THERMSHDN_END_FLAG_SHIFT 1 +#define M98925_THERMSHDN_END_FLAG_WIDTH 1 +#define M98925_THERMSHDN_BGN_FLAG_MASK (1<<0) +#define M98925_THERMSHDN_BGN_FLAG_SHIFT 0 +#define M98925_THERMSHDN_BGN_FLAG_WIDTH 1 + +/* MAX98925_R009_FLAG1 */ +#define M98925_SPKCURNT_FLAG_MASK (1<<5) +#define M98925_SPKCURNT_FLAG_SHIFT 5 +#define M98925_SPKCURNT_FLAG_WIDTH 1 +#define M98925_WATCHFAIL_FLAG_MASK (1<<4) +#define M98925_WATCHFAIL_FLAG_SHIFT 4 +#define M98925_WATCHFAIL_FLAG_WIDTH 1 +#define M98925_ALCINFH_FLAG_MASK (1<<3) +#define M98925_ALCINFH_FLAG_SHIFT 3 +#define M98925_ALCINFH_FLAG_WIDTH 1 +#define M98925_ALCACT_FLAG_MASK (1<<2) +#define M98925_ALCACT_FLAG_SHIFT 2 +#define M98925_ALCACT_FLAG_WIDTH 1 +#define M98925_ALCMUT_FLAG_MASK (1<<1) +#define M98925_ALCMUT_FLAG_SHIFT 1 +#define M98925_ALCMUT_FLAG_WIDTH 1 +#define M98925_ALCP_FLAG_MASK (1<<0) +#define M98925_ALCP_FLAG_SHIFT 0 +#define M98925_ALCP_FLAG_WIDTH 1 + +/* MAX98925_R00A_FLAG2 */ +#define M98925_SLOTOVRN_FLAG_MASK (1<<6) +#define M98925_SLOTOVRN_FLAG_SHIFT 6 +#define M98925_SLOTOVRN_FLAG_WIDTH 1 +#define M98925_INVALSLOT_FLAG_MASK (1<<5) +#define M98925_INVALSLOT_FLAG_SHIFT 5 +#define M98925_INVALSLOT_FLAG_WIDTH 1 +#define M98925_SLOTCNFLT_FLAG_MASK (1<<4) +#define M98925_SLOTCNFLT_FLAG_SHIFT 4 +#define M98925_SLOTCNFLT_FLAG_WIDTH 1 +#define M98925_VBSTOVFL_FLAG_MASK (1<<3) +#define M98925_VBSTOVFL_FLAG_SHIFT 3 +#define M98925_VBSTOVFL_FLAG_WIDTH 1 +#define M98925_VBATOVFL_FLAG_MASK (1<<2) +#define M98925_VBATOVFL_FLAG_SHIFT 2 +#define M98925_VBATOVFL_FLAG_WIDTH 1 +#define M98925_IMONOVFL_FLAG_MASK (1<<1) +#define M98925_IMONOVFL_FLAG_SHIFT 1 +#define M98925_IMONOVFL_FLAG_WIDTH 1 +#define M98925_VMONOVFL_FLAG_MASK (1<<0) +#define M98925_VMONOVFL_FLAG_SHIFT 0 +#define M98925_VMONOVFL_FLAG_WIDTH 1 + +/* MAX98925_R00B_IRQ_ENABLE0 */ +#define M98925_THERMWARN_END_EN_MASK (1<<3) +#define M98925_THERMWARN_END_EN_SHIFT 3 +#define M98925_THERMWARN_END_EN_WIDTH 1 +#define M98925_THERMWARN_BGN_EN_MASK (1<<2) +#define M98925_THERMWARN_BGN_EN_SHIFT 2 +#define M98925_THERMWARN_BGN_EN_WIDTH 1 +#define M98925_THERMSHDN_END_EN_MASK (1<<1) +#define M98925_THERMSHDN_END_EN_SHIFT 1 +#define M98925_THERMSHDN_END_EN_WIDTH 1 +#define M98925_THERMSHDN_BGN_EN_MASK (1<<0) +#define M98925_THERMSHDN_BGN_EN_SHIFT 0 +#define M98925_THERMSHDN_BGN_EN_WIDTH 1 + +/* MAX98925_R00C_IRQ_ENABLE1 */ +#define M98925_SPKCURNT_EN_MASK (1<<5) +#define M98925_SPKCURNT_EN_SHIFT 5 +#define M98925_SPKCURNT_EN_WIDTH 1 +#define M98925_WATCHFAIL_EN_MASK (1<<4) +#define M98925_WATCHFAIL_EN_SHIFT 4 +#define M98925_WATCHFAIL_EN_WIDTH 1 +#define M98925_ALCINFH_EN_MASK (1<<3) +#define M98925_ALCINFH_EN_SHIFT 3 +#define M98925_ALCINFH_EN_WIDTH 1 +#define M98925_ALCACT_EN_MASK (1<<2) +#define M98925_ALCACT_EN_SHIFT 2 +#define M98925_ALCACT_EN_WIDTH 1 +#define M98925_ALCMUT_EN_MASK (1<<1) +#define M98925_ALCMUT_EN_SHIFT 1 +#define M98925_ALCMUT_EN_WIDTH 1 +#define M98925_ALCP_EN_MASK (1<<0) +#define M98925_ALCP_EN_SHIFT 0 +#define M98925_ALCP_EN_WIDTH 1 + +/* MAX98925_R00D_IRQ_ENABLE2 */ +#define M98925_SLOTOVRN_EN_MASK (1<<6) +#define M98925_SLOTOVRN_EN_SHIFT 6 +#define M98925_SLOTOVRN_EN_WIDTH 1 +#define M98925_INVALSLOT_EN_MASK (1<<5) +#define M98925_INVALSLOT_EN_SHIFT 5 +#define M98925_INVALSLOT_EN_WIDTH 1 +#define M98925_SLOTCNFLT_EN_MASK (1<<4) +#define M98925_SLOTCNFLT_EN_SHIFT 4 +#define M98925_SLOTCNFLT_EN_WIDTH 1 +#define M98925_VBSTOVFL_EN_MASK (1<<3) +#define M98925_VBSTOVFL_EN_SHIFT 3 +#define M98925_VBSTOVFL_EN_WIDTH 1 +#define M98925_VBATOVFL_EN_MASK (1<<2) +#define M98925_VBATOVFL_EN_SHIFT 2 +#define M98925_VBATOVFL_EN_WIDTH 1 +#define M98925_IMONOVFL_EN_MASK (1<<1) +#define M98925_IMONOVFL_EN_SHIFT 1 +#define M98925_IMONOVFL_EN_WIDTH 1 +#define M98925_VMONOVFL_EN_MASK (1<<0) +#define M98925_VMONOVFL_EN_SHIFT 0 +#define M98925_VMONOVFL_EN_WIDTH 1 + +/* MAX98925_R00E_IRQ_CLEAR0 */ +#define M98925_THERMWARN_END_CLR_MASK (1<<3) +#define M98925_THERMWARN_END_CLR_SHIFT 3 +#define M98925_THERMWARN_END_CLR_WIDTH 1 +#define M98925_THERMWARN_BGN_CLR_MASK (1<<2) +#define M98925_THERMWARN_BGN_CLR_SHIFT 2 +#define M98925_THERMWARN_BGN_CLR_WIDTH 1 +#define M98925_THERMSHDN_END_CLR_MASK (1<<1) +#define M98925_THERMSHDN_END_CLR_SHIFT 1 +#define M98925_THERMSHDN_END_CLR_WIDTH 1 +#define M98925_THERMSHDN_BGN_CLR_MASK (1<<0) +#define M98925_THERMSHDN_BGN_CLR_SHIFT 0 +#define M98925_THERMSHDN_BGN_CLR_WIDTH 1 + +/* MAX98925_R00F_IRQ_CLEAR1 */ +#define M98925_SPKCURNT_CLR_MASK (1<<5) +#define M98925_SPKCURNT_CLR_SHIFT 5 +#define M98925_SPKCURNT_CLR_WIDTH 1 +#define M98925_WATCHFAIL_CLR_MASK (1<<4) +#define M98925_WATCHFAIL_CLR_SHIFT 4 +#define M98925_WATCHFAIL_CLR_WIDTH 1 +#define M98925_ALCINFH_CLR_MASK (1<<3) +#define M98925_ALCINFH_CLR_SHIFT 3 +#define M98925_ALCINFH_CLR_WIDTH 1 +#define M98925_ALCACT_CLR_MASK (1<<2) +#define M98925_ALCACT_CLR_SHIFT 2 +#define M98925_ALCACT_CLR_WIDTH 1 +#define M98925_ALCMUT_CLR_MASK (1<<1) +#define M98925_ALCMUT_CLR_SHIFT 1 +#define M98925_ALCMUT_CLR_WIDTH 1 +#define M98925_ALCP_CLR_MASK (1<<0) +#define M98925_ALCP_CLR_SHIFT 0 +#define M98925_ALCP_CLR_WIDTH 1 + +/* MAX98925_R010_IRQ_CLEAR2 */ +#define M98925_SLOTOVRN_CLR_MASK (1<<6) +#define M98925_SLOTOVRN_CLR_SHIFT 6 +#define M98925_SLOTOVRN_CLR_WIDTH 1 +#define M98925_INVALSLOT_CLR_MASK (1<<5) +#define M98925_INVALSLOT_CLR_SHIFT 5 +#define M98925_INVALSLOT_CLR_WIDTH 1 +#define M98925_SLOTCNFLT_CLR_MASK (1<<4) +#define M98925_SLOTCNFLT_CLR_SHIFT 4 +#define M98925_SLOTCNFLT_CLR_WIDTH 1 +#define M98925_VBSTOVFL_CLR_MASK (1<<3) +#define M98925_VBSTOVFL_CLR_SHIFT 3 +#define M98925_VBSTOVFL_CLR_WIDTH 1 +#define M98925_VBATOVFL_CLR_MASK (1<<2) +#define M98925_VBATOVFL_CLR_SHIFT 2 +#define M98925_VBATOVFL_CLR_WIDTH 1 +#define M98925_IMONOVFL_CLR_MASK (1<<1) +#define M98925_IMONOVFL_CLR_SHIFT 1 +#define M98925_IMONOVFL_CLR_WIDTH 1 +#define M98925_VMONOVFL_CLR_MASK (1<<0) +#define M98925_VMONOVFL_CLR_SHIFT 0 +#define M98925_VMONOVFL_CLR_WIDTH 1 + +/* MAX98925_R011_MAP0 */ +#define M98925_ER_THERMWARN_EN_MASK (1<<7) +#define M98925_ER_THERMWARN_EN_SHIFT 7 +#define M98925_ER_THERMWARN_EN_WIDTH 1 +#define M98925_ER_THERMWARN_MAP_MASK (0x07<<4) +#define M98925_ER_THERMWARN_MAP_SHIFT 4 +#define M98925_ER_THERMWARN_MAP_WIDTH 3 + +/* MAX98925_R012_MAP1 */ +#define M98925_ER_ALCMUT_EN_MASK (1<<7) +#define M98925_ER_ALCMUT_EN_SHIFT 7 +#define M98925_ER_ALCMUT_EN_WIDTH 1 +#define M98925_ER_ALCMUT_MAP_MASK (0x07<<4) +#define M98925_ER_ALCMUT_MAP_SHIFT 4 +#define M98925_ER_ALCMUT_MAP_WIDTH 3 +#define M98925_ER_ALCP_EN_MASK (1<<3) +#define M98925_ER_ALCP_EN_SHIFT 3 +#define M98925_ER_ALCP_EN_WIDTH 1 +#define M98925_ER_ALCP_MAP_MASK (0x07<<0) +#define M98925_ER_ALCP_MAP_SHIFT 0 +#define M98925_ER_ALCP_MAP_WIDTH 3 + +/* MAX98925_R013_MAP2 */ +#define M98925_ER_ALCINFH_EN_MASK (1<<7) +#define M98925_ER_ALCINFH_EN_SHIFT 7 +#define M98925_ER_ALCINFH_EN_WIDTH 1 +#define M98925_ER_ALCINFH_MAP_MASK (0x07<<4) +#define M98925_ER_ALCINFH_MAP_SHIFT 4 +#define M98925_ER_ALCINFH_MAP_WIDTH 3 +#define M98925_ER_ALCACT_EN_MASK (1<<3) +#define M98925_ER_ALCACT_EN_SHIFT 3 +#define M98925_ER_ALCACT_EN_WIDTH 1 +#define M98925_ER_ALCACT_MAP_MASK (0x07<<0) +#define M98925_ER_ALCACT_MAP_SHIFT 0 +#define M98925_ER_ALCACT_MAP_WIDTH 3 + +/* MAX98925_R014_MAP3 */ +#define M98925_ER_SPKCURNT_EN_MASK (1<<7) +#define M98925_ER_SPKCURNT_EN_SHIFT 7 +#define M98925_ER_SPKCURNT_EN_WIDTH 1 +#define M98925_ER_SPKCURNT_MAP_MASK (0x07<<4) +#define M98925_ER_SPKCURNT_MAP_SHIFT 4 +#define M98925_ER_SPKCURNT_MAP_WIDTH 3 + +/* MAX98925_R015_MAP4 */ +/* RESERVED */ + +/* MAX98925_R016_MAP5 */ +#define M98925_ER_IMONOVFL_EN_MASK (1<<7) +#define M98925_ER_IMONOVFL_EN_SHIFT 7 +#define M98925_ER_IMONOVFL_EN_WIDTH 1 +#define M98925_ER_IMONOVFL_MAP_MASK (0x07<<4) +#define M98925_ER_IMONOVFL_MAP_SHIFT 4 +#define M98925_ER_IMONOVFL_MAP_WIDTH 3 +#define M98925_ER_VMONOVFL_EN_MASK (1<<3) +#define M98925_ER_VMONOVFL_EN_SHIFT 3 +#define M98925_ER_VMONOVFL_EN_WIDTH 1 +#define M98925_ER_VMONOVFL_MAP_MASK (0x07<<0) +#define M98925_ER_VMONOVFL_MAP_SHIFT 0 +#define M98925_ER_VMONOVFL_MAP_WIDTH 3 + +/* MAX98925_R017_MAP6 */ +#define M98925_ER_VBSTOVFL_EN_MASK (1<<7) +#define M98925_ER_VBSTOVFL_EN_SHIFT 7 +#define M98925_ER_VBSTOVFL_EN_WIDTH 1 +#define M98925_ER_VBSTOVFL_MAP_MASK (0x07<<4) +#define M98925_ER_VBSTOVFL_MAP_SHIFT 4 +#define M98925_ER_VBSTOVFL_MAP_WIDTH 3 +#define M98925_ER_VBATOVFL_EN_MASK (1<<3) +#define M98925_ER_VBATOVFL_EN_SHIFT 3 +#define M98925_ER_VBATOVFL_EN_WIDTH 1 +#define M98925_ER_VBATOVFL_MAP_MASK (0x07<<0) +#define M98925_ER_VBATOVFL_MAP_SHIFT 0 +#define M98925_ER_VBATOVFL_MAP_WIDTH 3 + +/* MAX98925_R018_MAP7 */ +#define M98925_ER_INVALSLOT_EN_MASK (1<<7) +#define M98925_ER_INVALSLOT_EN_SHIFT 7 +#define M98925_ER_INVALSLOT_EN_WIDTH 1 +#define M98925_ER_INVALSLOT_MAP_MASK (0x07<<4) +#define M98925_ER_INVALSLOT_MAP_SHIFT 4 +#define M98925_ER_INVALSLOT_MAP_WIDTH 3 +#define M98925_ER_SLOTCNFLT_EN_MASK (1<<3) +#define M98925_ER_SLOTCNFLT_EN_SHIFT 3 +#define M98925_ER_SLOTCNFLT_EN_WIDTH 1 +#define M98925_ER_SLOTCNFLT_MAP_MASK (0x07<<0) +#define M98925_ER_SLOTCNFLT_MAP_SHIFT 0 +#define M98925_ER_SLOTCNFLT_MAP_WIDTH 3 + +/* MAX98925_R019_MAP8 */ +#define M98925_ER_SLOTOVRN_EN_MASK (1<<3) +#define M98925_ER_SLOTOVRN_EN_SHIFT 3 +#define M98925_ER_SLOTOVRN_EN_WIDTH 1 +#define M98925_ER_SLOTOVRN_MAP_MASK (0x07<<0) +#define M98925_ER_SLOTOVRN_MAP_SHIFT 0 +#define M98925_ER_SLOTOVRN_MAP_WIDTH 3 + +/* MAX98925_R01A_DAI_CLK_MODE1 */ +#define M98925_DAI_CLK_SOURCE_MASK (1<<6) +#define M98925_DAI_CLK_SOURCE_SHIFT 6 +#define M98925_DAI_CLK_SOURCE_WIDTH 1 +#define M98925_MDLL_MULT_MASK (0x0F<<0) +#define M98925_MDLL_MULT_SHIFT 0 +#define M98925_MDLL_MULT_WIDTH 4 + +#define M98925_MDLL_MULT_MCLKx8 6 +#define M98925_MDLL_MULT_MCLKx16 8 + +/* MAX98925_R01B_DAI_CLK_MODE2 */ +#define M98925_DAI_SR_MASK (0x0F<<4) +#define M98925_DAI_SR_SHIFT 4 +#define M98925_DAI_SR_WIDTH 4 +#define M98925_DAI_MAS_MASK (1<<3) +#define M98925_DAI_MAS_SHIFT 3 +#define M98925_DAI_MAS_WIDTH 1 +#define M98925_DAI_BSEL_MASK (0x07<<0) +#define M98925_DAI_BSEL_SHIFT 0 +#define M98925_DAI_BSEL_WIDTH 3 + +#define M98925_DAI_BSEL_32 (0 << M98925_DAI_BSEL_SHIFT) +#define M98925_DAI_BSEL_48 (1 << M98925_DAI_BSEL_SHIFT) +#define M98925_DAI_BSEL_64 (2 << M98925_DAI_BSEL_SHIFT) +#define M98925_DAI_BSEL_256 (6 << M98925_DAI_BSEL_SHIFT) + +/* MAX98925_R01C_DAI_CLK_DIV_M_MSBS */ +#define M98925_DAI_M_MSBS_MASK (0xFF<<0) +#define M98925_DAI_M_MSBS_SHIFT 0 +#define M98925_DAI_M_MSBS_WIDTH 8 + +/* MAX98925_R01D_DAI_CLK_DIV_M_LSBS */ +#define M98925_DAI_M_LSBS_MASK (0xFF<<0) +#define M98925_DAI_M_LSBS_SHIFT 0 +#define M98925_DAI_M_LSBS_WIDTH 8 + +/* MAX98925_R01E_DAI_CLK_DIV_N_MSBS */ +#define M98925_DAI_N_MSBS_MASK (0x7F<<0) +#define M98925_DAI_N_MSBS_SHIFT 0 +#define M98925_DAI_N_MSBS_WIDTH 7 + +/* MAX98925_R01F_DAI_CLK_DIV_N_LSBS */ +#define M98925_DAI_N_LSBS_MASK (0xFF<<0) +#define M98925_DAI_N_LSBS_SHIFT 0 +#define M98925_DAI_N_LSBS_WIDTH 8 + +/* MAX98925_R020_FORMAT */ +#define M98925_DAI_CHANSZ_MASK (0x03<<6) +#define M98925_DAI_CHANSZ_SHIFT 6 +#define M98925_DAI_CHANSZ_WIDTH 2 +#define M98925_DAI_EXTBCLK_HIZ_MASK (1<<4) +#define M98925_DAI_EXTBCLK_HIZ_SHIFT 4 +#define M98925_DAI_EXTBCLK_HIZ_WIDTH 1 +#define M98925_DAI_WCI_MASK (1<<3) +#define M98925_DAI_WCI_SHIFT 3 +#define M98925_DAI_WCI_WIDTH 1 +#define M98925_DAI_BCI_MASK (1<<2) +#define M98925_DAI_BCI_SHIFT 2 +#define M98925_DAI_BCI_WIDTH 1 +#define M98925_DAI_DLY_MASK (1<<1) +#define M98925_DAI_DLY_SHIFT 1 +#define M98925_DAI_DLY_WIDTH 1 +#define M98925_DAI_TDM_MASK (1<<0) +#define M98925_DAI_TDM_SHIFT 0 +#define M98925_DAI_TDM_WIDTH 1 + +#define M98925_DAI_CHANSZ_16 (1 << M98925_DAI_CHANSZ_SHIFT) +#define M98925_DAI_CHANSZ_24 (2 << M98925_DAI_CHANSZ_SHIFT) +#define M98925_DAI_CHANSZ_32 (3 << M98925_DAI_CHANSZ_SHIFT) + +/* MAX98925_R021_TDM_SLOT_SELECT */ +#define M98925_DAI_DO_EN_MASK (1<<7) +#define M98925_DAI_DO_EN_SHIFT 7 +#define M98925_DAI_DO_EN_WIDTH 1 +#define M98925_DAI_DIN_EN_MASK (1<<6) +#define M98925_DAI_DIN_EN_SHIFT 6 +#define M98925_DAI_DIN_EN_WIDTH 1 +#define M98925_DAI_INR_SOURCE_MASK (0x07<<3) +#define M98925_DAI_INR_SOURCE_SHIFT 3 +#define M98925_DAI_INR_SOURCE_WIDTH 3 +#define M98925_DAI_INL_SOURCE_MASK (0x07<<0) +#define M98925_DAI_INL_SOURCE_SHIFT 0 +#define M98925_DAI_INL_SOURCE_WIDTH 3 + +/* MAX98925_R022_DOUT_CFG_VMON */ +#define M98925_DAI_VMON_EN_MASK (1<<5) +#define M98925_DAI_VMON_EN_SHIFT 5 +#define M98925_DAI_VMON_EN_WIDTH 1 +#define M98925_DAI_VMON_SLOT_MASK (0x1F<<0) +#define M98925_DAI_VMON_SLOT_SHIFT 0 +#define M98925_DAI_VMON_SLOT_WIDTH 5 + +#define M98925_DAI_VMON_SLOT_00_01 (0 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_01_02 (1 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_02_03 (2 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_03_04 (3 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_04_05 (4 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_05_06 (5 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_06_07 (6 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_07_08 (7 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_08_09 (8 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_09_0A (9 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0A_0B (10 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0B_0C (11 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0C_0D (12 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0D_0E (13 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0E_0F (14 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0F_10 (15 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_10_11 (16 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_11_12 (17 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_12_13 (18 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_13_14 (19 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_14_15 (20 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_15_16 (21 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_16_17 (22 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_17_18 (23 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_18_19 (24 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_19_1A (25 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1A_1B (26 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1B_1C (27 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1C_1D (28 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1D_1E (29 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1E_1F (30 << M98925_DAI_VMON_SLOT_SHIFT) + +/* MAX98925_R023_DOUT_CFG_IMON */ +#define M98925_DAI_IMON_EN_MASK (1<<5) +#define M98925_DAI_IMON_EN_SHIFT 5 +#define M98925_DAI_IMON_EN_WIDTH 1 +#define M98925_DAI_IMON_SLOT_MASK (0x1F<<0) +#define M98925_DAI_IMON_SLOT_SHIFT 0 +#define M98925_DAI_IMON_SLOT_WIDTH 5 + +#define M98925_DAI_IMON_SLOT_00_01 (0 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_01_02 (1 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_02_03 (2 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_03_04 (3 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_04_05 (4 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_05_06 (5 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_06_07 (6 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_07_08 (7 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_08_09 (8 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_09_0A (9 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0A_0B (10 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0B_0C (11 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0C_0D (12 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0D_0E (13 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0E_0F (14 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0F_10 (15 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_10_11 (16 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_11_12 (17 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_12_13 (18 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_13_14 (19 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_14_15 (20 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_15_16 (21 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_16_17 (22 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_17_18 (23 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_18_19 (24 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_19_1A (25 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1A_1B (26 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1B_1C (27 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1C_1D (28 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1D_1E (29 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1E_1F (30 << M98925_DAI_IMON_SLOT_SHIFT) + +/* MAX98925_R024_DOUT_CFG_VBAT */ +#define M98925_DAI_VBAT_EN_MASK (1<<5) +#define M98925_DAI_VBAT_EN_SHIFT 5 +#define M98925_DAI_VBAT_EN_WIDTH 1 +#define M98925_DAI_VBAT_SLOT_MASK (0x1F<<0) +#define M98925_DAI_VBAT_SLOT_SHIFT 0 +#define M98925_DAI_VBAT_SLOT_WIDTH 5 + +/* MAX98925_R025_DOUT_CFG_VBST */ +#define M98925_DAI_VBST_EN_MASK (1<<5) +#define M98925_DAI_VBST_EN_SHIFT 5 +#define M98925_DAI_VBST_EN_WIDTH 1 +#define M98925_DAI_VBST_SLOT_MASK (0x1F<<0) +#define M98925_DAI_VBST_SLOT_SHIFT 0 +#define M98925_DAI_VBST_SLOT_WIDTH 5 + +/* MAX98925_R026_DOUT_CFG_FLAG */ +#define M98925_DAI_FLAG_EN_MASK (1<<5) +#define M98925_DAI_FLAG_EN_SHIFT 5 +#define M98925_DAI_FLAG_EN_WIDTH 1 +#define M98925_DAI_FLAG_SLOT_MASK (0x1F<<0) +#define M98925_DAI_FLAG_SLOT_SHIFT 0 +#define M98925_DAI_FLAG_SLOT_WIDTH 5 + +/* MAX98925_R027_DOUT_HIZ_CFG1 */ +#define M98925_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG1_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG1_WIDTH 8 + +/* MAX98925_R028_DOUT_HIZ_CFG2 */ +#define M98925_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG2_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG2_WIDTH 8 + +/* MAX98925_R029_DOUT_HIZ_CFG3 */ +#define M98925_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG3_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG3_WIDTH 8 + +/* MAX98925_R02A_DOUT_HIZ_CFG4 */ +#define M98925_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG4_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG4_WIDTH 8 + +/* MAX98925_R02B_DOUT_DRV_STRENGTH */ +#define M98925_DAI_OUT_DRIVE_MASK (0x03<<0) +#define M98925_DAI_OUT_DRIVE_SHIFT 0 +#define M98925_DAI_OUT_DRIVE_WIDTH 2 + +/* MAX98925_R02C_FILTERS */ +#define M98925_ADC_DITHER_EN_MASK (1<<7) +#define M98925_ADC_DITHER_EN_SHIFT 7 +#define M98925_ADC_DITHER_EN_WIDTH 1 +#define M98925_IV_DCB_EN_MASK (1<<6) +#define M98925_IV_DCB_EN_SHIFT 6 +#define M98925_IV_DCB_EN_WIDTH 1 +#define M98925_DAC_DITHER_EN_MASK (1<<4) +#define M98925_DAC_DITHER_EN_SHIFT 4 +#define M98925_DAC_DITHER_EN_WIDTH 1 +#define M98925_DAC_FILTER_MODE_MASK (1<<3) +#define M98925_DAC_FILTER_MODE_SHIFT 3 +#define M98925_DAC_FILTER_MODE_WIDTH 1 +#define M98925_DAC_HPF_MASK (0x07<<0) +#define M98925_DAC_HPF_SHIFT 0 +#define M98925_DAC_HPF_WIDTH 3 +#define M98925_DAC_HPF_DISABLE (0 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_DC_BLOCK (1 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_100 (2 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_200 (3 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_400 (4 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_800 (5 << M98925_DAC_HPF_SHIFT) + +/* MAX98925_R02D_GAIN */ +#define M98925_DAC_IN_SEL_MASK (0x03<<5) +#define M98925_DAC_IN_SEL_SHIFT 5 +#define M98925_DAC_IN_SEL_WIDTH 2 +#define M98925_SPK_GAIN_MASK (0x1F<<0) +#define M98925_SPK_GAIN_SHIFT 0 +#define M98925_SPK_GAIN_WIDTH 5 + +#define M98925_DAC_IN_SEL_LEFT_DAI (0 << M98925_DAC_IN_SEL_SHIFT) +#define M98925_DAC_IN_SEL_RIGHT_DAI (1 << M98925_DAC_IN_SEL_SHIFT) +#define M98925_DAC_IN_SEL_SUMMED_DAI (2 << M98925_DAC_IN_SEL_SHIFT) +#define M98925_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << M98925_DAC_IN_SEL_SHIFT) + +/* MAX98925_R02E_GAIN_RAMPING */ +#define M98925_SPK_RMP_EN_MASK (1<<1) +#define M98925_SPK_RMP_EN_SHIFT 1 +#define M98925_SPK_RMP_EN_WIDTH 1 +#define M98925_SPK_ZCD_EN_MASK (1<<0) +#define M98925_SPK_ZCD_EN_SHIFT 0 +#define M98925_SPK_ZCD_EN_WIDTH 1 + +/* MAX98925_R02F_SPK_AMP */ +#define M98925_SPK_MODE_MASK (1<<0) +#define M98925_SPK_MODE_SHIFT 0 +#define M98925_SPK_MODE_WIDTH 1 + +/* MAX98925_R030_THRESHOLD */ +#define M98925_ALC_EN_MASK (1<<5) +#define M98925_ALC_EN_SHIFT 5 +#define M98925_ALC_EN_WIDTH 1 +#define M98925_ALC_TH_MASK (0x1F<<0) +#define M98925_ALC_TH_SHIFT 0 +#define M98925_ALC_TH_WIDTH 5 + +/* MAX98925_R031_ALC_ATTACK */ +#define M98925_ALC_ATK_STEP_MASK (0x0F<<4) +#define M98925_ALC_ATK_STEP_SHIFT 4 +#define M98925_ALC_ATK_STEP_WIDTH 4 +#define M98925_ALC_ATK_RATE_MASK (0x7<<0) +#define M98925_ALC_ATK_RATE_SHIFT 0 +#define M98925_ALC_ATK_RATE_WIDTH 3 + +/* MAX98925_R032_ALC_ATTEN_RLS */ +#define M98925_ALC_MAX_ATTEN_MASK (0x0F<<4) +#define M98925_ALC_MAX_ATTEN_SHIFT 4 +#define M98925_ALC_MAX_ATTEN_WIDTH 4 +#define M98925_ALC_RLS_RATE_MASK (0x7<<0) +#define M98925_ALC_RLS_RATE_SHIFT 0 +#define M98925_ALC_RLS_RATE_WIDTH 3 + +/* MAX98925_R033_ALC_HOLD_RLS */ +#define M98925_ALC_RLS_TGR_MASK (1<<0) +#define M98925_ALC_RLS_TGR_SHIFT 0 +#define M98925_ALC_RLS_TGR_WIDTH 1 + +/* MAX98925_R034_ALC_CONFIGURATION */ +#define M98925_ALC_MUTE_EN_MASK (1<<7) +#define M98925_ALC_MUTE_EN_SHIFT 7 +#define M98925_ALC_MUTE_EN_WIDTH 1 +#define M98925_ALC_MUTE_DLY_MASK (0x07<<4) +#define M98925_ALC_MUTE_DLY_SHIFT 4 +#define M98925_ALC_MUTE_DLY_WIDTH 3 +#define M98925_ALC_RLS_DBT_MASK (0x07<<0) +#define M98925_ALC_RLS_DBT_SHIFT 0 +#define M98925_ALC_RLS_DBT_WIDTH 3 + +/* MAX98925_R035_BOOST_CONVERTER */ +#define M98925_BST_SYNC_MASK (1<<7) +#define M98925_BST_SYNC_SHIFT 7 +#define M98925_BST_SYNC_WIDTH 1 +#define M98925_BST_PHASE_MASK (0x03<<4) +#define M98925_BST_PHASE_SHIFT 4 +#define M98925_BST_PHASE_WIDTH 2 +#define M98925_BST_SKIP_MODE_MASK (0x03<<0) +#define M98925_BST_SKIP_MODE_SHIFT 0 +#define M98925_BST_SKIP_MODE_WIDTH 2 + +/* MAX98925_R036_BLOCK_ENABLE */ +#define M98925_BST_EN_MASK (1<<7) +#define M98925_BST_EN_SHIFT 7 +#define M98925_BST_EN_WIDTH 1 +#define M98925_WATCH_EN_MASK (1<<6) +#define M98925_WATCH_EN_SHIFT 6 +#define M98925_WATCH_EN_WIDTH 1 +#define M98925_CLKMON_EN_MASK (1<<5) +#define M98925_CLKMON_EN_SHIFT 5 +#define M98925_CLKMON_EN_WIDTH 1 +#define M98925_SPK_EN_MASK (1<<4) +#define M98925_SPK_EN_SHIFT 4 +#define M98925_SPK_EN_WIDTH 1 +#define M98925_ADC_VBST_EN_MASK (1<<3) +#define M98925_ADC_VBST_EN_SHIFT 3 +#define M98925_ADC_VBST_EN_WIDTH 1 +#define M98925_ADC_VBAT_EN_MASK (1<<2) +#define M98925_ADC_VBAT_EN_SHIFT 2 +#define M98925_ADC_VBAT_EN_WIDTH 1 +#define M98925_ADC_IMON_EN_MASK (1<<1) +#define M98925_ADC_IMON_EN_SHIFT 1 +#define M98925_ADC_IMON_EN_WIDTH 1 +#define M98925_ADC_VMON_EN_MASK (1<<0) +#define M98925_ADC_VMON_EN_SHIFT 0 +#define M98925_ADC_VMON_EN_WIDTH 1 + +/* MAX98925_R037_CONFIGURATION */ +#define M98925_BST_VOUT_MASK (0x0F<<4) +#define M98925_BST_VOUT_SHIFT 4 +#define M98925_BST_VOUT_WIDTH 4 +#define M98925_THERMWARN_LEVEL_MASK (0x03<<2) +#define M98925_THERMWARN_LEVEL_SHIFT 2 +#define M98925_THERMWARN_LEVEL_WIDTH 2 +#define M98925_WATCH_TIME_MASK (0x03<<0) +#define M98925_WATCH_TIME_SHIFT 0 +#define M98925_WATCH_TIME_WIDTH 2 + +/* MAX98925_R038_GLOBAL_ENABLE */ +#define M98925_EN_MASK (1<<7) +#define M98925_EN_SHIFT 7 +#define M98925_EN_WIDTH 1 + +/* MAX98925_R03A_BOOST_LIMITER */ +#define M98925_BST_ILIM_MASK (0x1F<<3) +#define M98925_BST_ILIM_SHIFT 3 +#define M98925_BST_ILIM_WIDTH 5 + +/* MAX98925_R0FF_VERSION */ +#define M98925_REV_ID_MASK (0xFF<<0) +#define M98925_REV_ID_SHIFT 0 +#define M98925_REV_ID_WIDTH 8 + +struct max98925_priv { + struct regmap *regmap; + struct snd_soc_component *component; + struct max98925_pdata *pdata; + unsigned int sysclk; + unsigned int v_slot; + unsigned int i_slot; + unsigned int spk_gain; + unsigned int ch_size; +}; +#endif diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c new file mode 100644 index 0000000000..922ce0dc4e --- /dev/null +++ b/sound/soc/codecs/max98926.c @@ -0,0 +1,593 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * max98926.c -- ALSA SoC MAX98926 driver + * Copyright 2013-15 Maxim Integrated Products + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98926.h" + +static const char * const max98926_boost_voltage_txt[] = { + "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V", + "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V" +}; + +static const char *const max98926_pdm_ch_text[] = { + "Current", "Voltage", +}; + +static const char *const max98926_hpf_cutoff_txt[] = { + "Disable", "DC Block", "100Hz", + "200Hz", "400Hz", "800Hz", +}; + +static const struct reg_default max98926_reg[] = { + { 0x0B, 0x00 }, /* IRQ Enable0 */ + { 0x0C, 0x00 }, /* IRQ Enable1 */ + { 0x0D, 0x00 }, /* IRQ Enable2 */ + { 0x0E, 0x00 }, /* IRQ Clear0 */ + { 0x0F, 0x00 }, /* IRQ Clear1 */ + { 0x10, 0x00 }, /* IRQ Clear2 */ + { 0x11, 0xC0 }, /* Map0 */ + { 0x12, 0x00 }, /* Map1 */ + { 0x13, 0x00 }, /* Map2 */ + { 0x14, 0xF0 }, /* Map3 */ + { 0x15, 0x00 }, /* Map4 */ + { 0x16, 0xAB }, /* Map5 */ + { 0x17, 0x89 }, /* Map6 */ + { 0x18, 0x00 }, /* Map7 */ + { 0x19, 0x00 }, /* Map8 */ + { 0x1A, 0x04 }, /* DAI Clock Mode 1 */ + { 0x1B, 0x00 }, /* DAI Clock Mode 2 */ + { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */ + { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */ + { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */ + { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */ + { 0x20, 0x50 }, /* Format */ + { 0x21, 0x00 }, /* TDM Slot Select */ + { 0x22, 0x00 }, /* DOUT Configuration VMON */ + { 0x23, 0x00 }, /* DOUT Configuration IMON */ + { 0x24, 0x00 }, /* DOUT Configuration VBAT */ + { 0x25, 0x00 }, /* DOUT Configuration VBST */ + { 0x26, 0x00 }, /* DOUT Configuration FLAG */ + { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */ + { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */ + { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */ + { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */ + { 0x2B, 0x02 }, /* DOUT Drive Strength */ + { 0x2C, 0x90 }, /* Filters */ + { 0x2D, 0x00 }, /* Gain */ + { 0x2E, 0x02 }, /* Gain Ramping */ + { 0x2F, 0x00 }, /* Speaker Amplifier */ + { 0x30, 0x0A }, /* Threshold */ + { 0x31, 0x00 }, /* ALC Attack */ + { 0x32, 0x80 }, /* ALC Atten and Release */ + { 0x33, 0x00 }, /* ALC Infinite Hold Release */ + { 0x34, 0x92 }, /* ALC Configuration */ + { 0x35, 0x01 }, /* Boost Converter */ + { 0x36, 0x00 }, /* Block Enable */ + { 0x37, 0x00 }, /* Configuration */ + { 0x38, 0x00 }, /* Global Enable */ + { 0x3A, 0x00 }, /* Boost Limiter */ +}; + +static const struct soc_enum max98926_voltage_enum[] = { + SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 0, + ARRAY_SIZE(max98926_pdm_ch_text), + max98926_pdm_ch_text), +}; + +static const struct snd_kcontrol_new max98926_voltage_control = + SOC_DAPM_ENUM("Route", max98926_voltage_enum); + +static const struct soc_enum max98926_current_enum[] = { + SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_SOURCE_1_SHIFT, + ARRAY_SIZE(max98926_pdm_ch_text), + max98926_pdm_ch_text), +}; + +static const struct snd_kcontrol_new max98926_current_control = + SOC_DAPM_ENUM("Route", max98926_current_enum); + +static const struct snd_kcontrol_new max98926_mixer_controls[] = { + SOC_DAPM_SINGLE("PCM Single Switch", MAX98926_SPK_AMP, + MAX98926_INSELECT_MODE_SHIFT, 0, 0), + SOC_DAPM_SINGLE("PDM Single Switch", MAX98926_SPK_AMP, + MAX98926_INSELECT_MODE_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new max98926_dai_controls[] = { + SOC_DAPM_SINGLE("Left", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 0, 0), + SOC_DAPM_SINGLE("Right", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LeftRight", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 2, 0), + SOC_DAPM_SINGLE("(Left+Right)/2 Switch", MAX98926_GAIN, + MAX98926_DAC_IN_SEL_SHIFT, 3, 0), +}; + +static const struct snd_soc_dapm_widget max98926_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("Amp Enable", NULL, MAX98926_BLOCK_ENABLE, + MAX98926_SPK_EN_SHIFT, 0), + SND_SOC_DAPM_SUPPLY("Global Enable", MAX98926_GLOBAL_ENABLE, + MAX98926_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VI Enable", MAX98926_BLOCK_ENABLE, + MAX98926_ADC_IMON_EN_WIDTH | + MAX98926_ADC_VMON_EN_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("BST Enable", MAX98926_BLOCK_ENABLE, + MAX98926_BST_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_MIXER("PCM Sel", MAX98926_SPK_AMP, + MAX98926_INSELECT_MODE_SHIFT, 0, + &max98926_mixer_controls[0], + ARRAY_SIZE(max98926_mixer_controls)), + SND_SOC_DAPM_MIXER("DAI Sel", + MAX98926_GAIN, MAX98926_DAC_IN_SEL_SHIFT, 0, + &max98926_dai_controls[0], + ARRAY_SIZE(max98926_dai_controls)), + SND_SOC_DAPM_MUX("PDM CH1 Source", + MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_CURRENT_SHIFT, + 0, &max98926_current_control), + SND_SOC_DAPM_MUX("PDM CH0 Source", + MAX98926_DAI_CLK_DIV_N_LSBS, + MAX98926_PDM_VOLTAGE_SHIFT, + 0, &max98926_voltage_control), +}; + +static const struct snd_soc_dapm_route max98926_audio_map[] = { + {"VI Enable", NULL, "DAI_OUT"}, + {"DAI Sel", "Left", "VI Enable"}, + {"DAI Sel", "Right", "VI Enable"}, + {"DAI Sel", "LeftRight", "VI Enable"}, + {"DAI Sel", "LeftRightDiv2", "VI Enable"}, + {"PCM Sel", "PCM", "DAI Sel"}, + + {"PDM CH1 Source", "Current", "DAI_OUT"}, + {"PDM CH1 Source", "Voltage", "DAI_OUT"}, + {"PDM CH0 Source", "Current", "DAI_OUT"}, + {"PDM CH0 Source", "Voltage", "DAI_OUT"}, + {"PCM Sel", "Analog", "PDM CH1 Source"}, + {"PCM Sel", "Analog", "PDM CH0 Source"}, + {"Amp Enable", NULL, "PCM Sel"}, + + {"BST Enable", NULL, "Amp Enable"}, + {"BE_OUT", NULL, "BST Enable"}, +}; + +static bool max98926_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98926_VBAT_DATA: + case MAX98926_VBST_DATA: + case MAX98926_LIVE_STATUS0: + case MAX98926_LIVE_STATUS1: + case MAX98926_LIVE_STATUS2: + case MAX98926_STATE0: + case MAX98926_STATE1: + case MAX98926_STATE2: + case MAX98926_FLAG0: + case MAX98926_FLAG1: + case MAX98926_FLAG2: + case MAX98926_VERSION: + return true; + default: + return false; + } +} + +static bool max98926_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98926_IRQ_CLEAR0: + case MAX98926_IRQ_CLEAR1: + case MAX98926_IRQ_CLEAR2: + case MAX98926_ALC_HOLD_RLS: + return false; + default: + return true; + } +}; + +static DECLARE_TLV_DB_SCALE(max98926_spk_tlv, -600, 100, 0); +static DECLARE_TLV_DB_RANGE(max98926_current_tlv, + 0, 11, TLV_DB_SCALE_ITEM(20, 20, 0), + 12, 15, TLV_DB_SCALE_ITEM(320, 40, 0), +); + +static SOC_ENUM_SINGLE_DECL(max98926_dac_hpf_cutoff, + MAX98926_FILTERS, MAX98926_DAC_HPF_SHIFT, + max98926_hpf_cutoff_txt); + +static SOC_ENUM_SINGLE_DECL(max98926_boost_voltage, + MAX98926_CONFIGURATION, MAX98926_BST_VOUT_SHIFT, + max98926_boost_voltage_txt); + +static const struct snd_kcontrol_new max98926_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", MAX98926_GAIN, + MAX98926_SPK_GAIN_SHIFT, + (1<regmap, + MAX98926_DOUT_CFG_VMON, + MAX98926_DAI_VMON_EN_MASK, + MAX98926_DAI_VMON_EN_MASK); + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_IMON, + MAX98926_DAI_IMON_EN_MASK, + MAX98926_DAI_IMON_EN_MASK); + + if (!max98926->interleave_mode) { + /* set VMON slots */ + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_VMON, + MAX98926_DAI_VMON_SLOT_MASK, + max98926->v_slot); + /* set IMON slots */ + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_IMON, + MAX98926_DAI_IMON_SLOT_MASK, + max98926->i_slot); + } else { + /* enable interleave mode */ + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_INTERLEAVE_MASK, + MAX98926_DAI_INTERLEAVE_MASK); + /* set interleave slots */ + regmap_update_bits(max98926->regmap, + MAX98926_DOUT_CFG_VBAT, + MAX98926_DAI_INTERLEAVE_SLOT_MASK, + max98926->v_slot); + } +} + +static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component); + unsigned int invert = 0; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + max98926_set_sense_data(max98926); + break; + default: + dev_err(component->dev, "DAI clock mode unsupported\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + invert = MAX98926_DAI_WCI_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98926_DAI_BCI_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + regmap_write(max98926->regmap, + MAX98926_FORMAT, MAX98926_DAI_DLY_MASK); + regmap_update_bits(max98926->regmap, MAX98926_FORMAT, + MAX98926_DAI_BCI_MASK, invert); + return 0; +} + +static int max98926_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int dai_sr = -EINVAL; + int rate = params_rate(params), i; + struct snd_soc_component *component = dai->component; + struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component); + int blr_clk_ratio; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_CHANSZ_MASK, + MAX98926_DAI_CHANSZ_16); + max98926->ch_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_CHANSZ_MASK, + MAX98926_DAI_CHANSZ_24); + max98926->ch_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + regmap_update_bits(max98926->regmap, + MAX98926_FORMAT, + MAX98926_DAI_CHANSZ_MASK, + MAX98926_DAI_CHANSZ_32); + max98926->ch_size = 32; + break; + default: + dev_dbg(component->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + /* BCLK/LRCLK ratio calculation */ + blr_clk_ratio = params_channels(params) * max98926->ch_size; + + switch (blr_clk_ratio) { + case 32: + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_BSEL_MASK, + MAX98926_DAI_BSEL_32); + break; + case 48: + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_BSEL_MASK, + MAX98926_DAI_BSEL_48); + break; + case 64: + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_BSEL_MASK, + MAX98926_DAI_BSEL_64); + break; + default: + return -EINVAL; + } + + /* find the closest rate */ + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i].rate >= rate) { + dai_sr = rate_table[i].sr; + break; + } + } + if (dai_sr < 0) + return -EINVAL; + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98926->regmap, + MAX98926_DAI_CLK_MODE2, + MAX98926_DAI_SR_MASK, dai_sr << MAX98926_DAI_SR_SHIFT); + return 0; +} + +#define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98926_dai_ops = { + .set_fmt = max98926_dai_set_fmt, + .hw_params = max98926_dai_hw_params, +}; + +static struct snd_soc_dai_driver max98926_dai[] = { +{ + .name = "max98926-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98926_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98926_FORMATS, + }, + .ops = &max98926_dai_ops, +} +}; + +static int max98926_probe(struct snd_soc_component *component) +{ + struct max98926_priv *max98926 = snd_soc_component_get_drvdata(component); + + max98926->component = component; + + /* Hi-Z all the slots */ + regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0); + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_max98926 = { + .probe = max98926_probe, + .controls = max98926_snd_controls, + .num_controls = ARRAY_SIZE(max98926_snd_controls), + .dapm_routes = max98926_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98926_audio_map), + .dapm_widgets = max98926_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config max98926_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX98926_VERSION, + .reg_defaults = max98926_reg, + .num_reg_defaults = ARRAY_SIZE(max98926_reg), + .volatile_reg = max98926_volatile_register, + .readable_reg = max98926_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98926_i2c_probe(struct i2c_client *i2c) +{ + int ret, reg; + u32 value; + struct max98926_priv *max98926; + + max98926 = devm_kzalloc(&i2c->dev, + sizeof(*max98926), GFP_KERNEL); + if (!max98926) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98926); + max98926->regmap = devm_regmap_init_i2c(i2c, &max98926_regmap); + if (IS_ERR(max98926->regmap)) { + ret = PTR_ERR(max98926->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + goto err_out; + } + if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode") || + of_property_read_bool(i2c->dev.of_node, "interleave-mode")) + max98926->interleave_mode = true; + + if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { + if (value > MAX98926_DAI_VMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "vmon slot number is wrong:\n"); + return -EINVAL; + } + max98926->v_slot = value; + } + if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) { + if (value > MAX98926_DAI_IMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "imon slot number is wrong:\n"); + return -EINVAL; + } + max98926->i_slot = value; + } + ret = regmap_read(max98926->regmap, + MAX98926_VERSION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read: %x\n", reg); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_max98926, + max98926_dai, ARRAY_SIZE(max98926_dai)); + if (ret < 0) + dev_err(&i2c->dev, + "Failed to register component: %d\n", ret); + dev_info(&i2c->dev, "device version: %x\n", reg); +err_out: + return ret; +} + +static const struct i2c_device_id max98926_i2c_id[] = { + { "max98926", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98926_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id max98926_of_match[] = { + { .compatible = "maxim,max98926", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98926_of_match); +#endif + +static struct i2c_driver max98926_i2c_driver = { + .driver = { + .name = "max98926", + .of_match_table = of_match_ptr(max98926_of_match), + }, + .probe = max98926_i2c_probe, + .id_table = max98926_i2c_id, +}; + +module_i2c_driver(max98926_i2c_driver) +MODULE_DESCRIPTION("ALSA SoC MAX98926 driver"); +MODULE_AUTHOR("Anish kumar "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98926.h b/sound/soc/codecs/max98926.h new file mode 100644 index 0000000000..d622d5f438 --- /dev/null +++ b/sound/soc/codecs/max98926.h @@ -0,0 +1,846 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * max98926.h -- MAX98926 ALSA SoC Audio driver + * Copyright 2013-2015 Maxim Integrated Products + */ + +#ifndef _MAX98926_H +#define _MAX98926_H + +#define MAX98926_CHIP_VERSION 0x40 +#define MAX98926_CHIP_VERSION1 0x50 + +#define MAX98926_VBAT_DATA 0x00 +#define MAX98926_VBST_DATA 0x01 +#define MAX98926_LIVE_STATUS0 0x02 +#define MAX98926_LIVE_STATUS1 0x03 +#define MAX98926_LIVE_STATUS2 0x04 +#define MAX98926_STATE0 0x05 +#define MAX98926_STATE1 0x06 +#define MAX98926_STATE2 0x07 +#define MAX98926_FLAG0 0x08 +#define MAX98926_FLAG1 0x09 +#define MAX98926_FLAG2 0x0A +#define MAX98926_IRQ_ENABLE0 0x0B +#define MAX98926_IRQ_ENABLE1 0x0C +#define MAX98926_IRQ_ENABLE2 0x0D +#define MAX98926_IRQ_CLEAR0 0x0E +#define MAX98926_IRQ_CLEAR1 0x0F +#define MAX98926_IRQ_CLEAR2 0x10 +#define MAX98926_MAP0 0x11 +#define MAX98926_MAP1 0x12 +#define MAX98926_MAP2 0x13 +#define MAX98926_MAP3 0x14 +#define MAX98926_MAP4 0x15 +#define MAX98926_MAP5 0x16 +#define MAX98926_MAP6 0x17 +#define MAX98926_MAP7 0x18 +#define MAX98926_MAP8 0x19 +#define MAX98926_DAI_CLK_MODE1 0x1A +#define MAX98926_DAI_CLK_MODE2 0x1B +#define MAX98926_DAI_CLK_DIV_M_MSBS 0x1C +#define MAX98926_DAI_CLK_DIV_M_LSBS 0x1D +#define MAX98926_DAI_CLK_DIV_N_MSBS 0x1E +#define MAX98926_DAI_CLK_DIV_N_LSBS 0x1F +#define MAX98926_FORMAT 0x20 +#define MAX98926_TDM_SLOT_SELECT 0x21 +#define MAX98926_DOUT_CFG_VMON 0x22 +#define MAX98926_DOUT_CFG_IMON 0x23 +#define MAX98926_DOUT_CFG_VBAT 0x24 +#define MAX98926_DOUT_CFG_VBST 0x25 +#define MAX98926_DOUT_CFG_FLAG 0x26 +#define MAX98926_DOUT_HIZ_CFG1 0x27 +#define MAX98926_DOUT_HIZ_CFG2 0x28 +#define MAX98926_DOUT_HIZ_CFG3 0x29 +#define MAX98926_DOUT_HIZ_CFG4 0x2A +#define MAX98926_DOUT_DRV_STRENGTH 0x2B +#define MAX98926_FILTERS 0x2C +#define MAX98926_GAIN 0x2D +#define MAX98926_GAIN_RAMPING 0x2E +#define MAX98926_SPK_AMP 0x2F +#define MAX98926_THRESHOLD 0x30 +#define MAX98926_ALC_ATTACK 0x31 +#define MAX98926_ALC_ATTEN_RLS 0x32 +#define MAX98926_ALC_HOLD_RLS 0x33 +#define MAX98926_ALC_CONFIGURATION 0x34 +#define MAX98926_BOOST_CONVERTER 0x35 +#define MAX98926_BLOCK_ENABLE 0x36 +#define MAX98926_CONFIGURATION 0x37 +#define MAX98926_GLOBAL_ENABLE 0x38 +#define MAX98926_BOOST_LIMITER 0x3A +#define MAX98926_VERSION 0xFF + +#define MAX98926_REG_CNT (MAX98926_R03A_BOOST_LIMITER+1) + +#define MAX98926_PDM_CURRENT_MASK (1<<7) +#define MAX98926_PDM_CURRENT_SHIFT 7 +#define MAX98926_PDM_VOLTAGE_MASK (1<<3) +#define MAX98926_PDM_VOLTAGE_SHIFT 3 +#define MAX98926_PDM_CHANNEL_0_MASK (1<<2) +#define MAX98926_PDM_CHANNEL_0_SHIFT 2 +#define MAX98926_PDM_CHANNEL_1_MASK (1<<6) +#define MAX98926_PDM_CHANNEL_1_SHIFT 6 +#define MAX98926_PDM_CHANNEL_1_HIZ 5 +#define MAX98926_PDM_CHANNEL_0_HIZ 1 +#define MAX98926_PDM_SOURCE_0_SHIFT 0 +#define MAX98926_PDM_SOURCE_0_MASK (1<<0) +#define MAX98926_PDM_SOURCE_1_MASK (1<<4) +#define MAX98926_PDM_SOURCE_1_SHIFT 4 + +/* MAX98926 Register Bit Fields */ + +/* MAX98926_R002_LIVE_STATUS0 */ +#define MAX98926_THERMWARN_STATUS_MASK (1<<3) +#define MAX98926_THERMWARN_STATUS_SHIFT 3 +#define MAX98926_THERMWARN_STATUS_WIDTH 1 +#define MAX98926_THERMSHDN_STATUS_MASK (1<<1) +#define MAX98926_THERMSHDN_STATUS_SHIFT 1 +#define MAX98926_THERMSHDN_STATUS_WIDTH 1 + +/* MAX98926_R003_LIVE_STATUS1 */ +#define MAX98926_SPKCURNT_STATUS_MASK (1<<5) +#define MAX98926_SPKCURNT_STATUS_SHIFT 5 +#define MAX98926_SPKCURNT_STATUS_WIDTH 1 +#define MAX98926_WATCHFAIL_STATUS_MASK (1<<4) +#define MAX98926_WATCHFAIL_STATUS_SHIFT 4 +#define MAX98926_WATCHFAIL_STATUS_WIDTH 1 +#define MAX98926_ALCINFH_STATUS_MASK (1<<3) +#define MAX98926_ALCINFH_STATUS_SHIFT 3 +#define MAX98926_ALCINFH_STATUS_WIDTH 1 +#define MAX98926_ALCACT_STATUS_MASK (1<<2) +#define MAX98926_ALCACT_STATUS_SHIFT 2 +#define MAX98926_ALCACT_STATUS_WIDTH 1 +#define MAX98926_ALCMUT_STATUS_MASK (1<<1) +#define MAX98926_ALCMUT_STATUS_SHIFT 1 +#define MAX98926_ALCMUT_STATUS_WIDTH 1 +#define MAX98926_ACLP_STATUS_MASK (1<<0) +#define MAX98926_ACLP_STATUS_SHIFT 0 +#define MAX98926_ACLP_STATUS_WIDTH 1 + +/* MAX98926_R004_LIVE_STATUS2 */ +#define MAX98926_SLOTOVRN_STATUS_MASK (1<<6) +#define MAX98926_SLOTOVRN_STATUS_SHIFT 6 +#define MAX98926_SLOTOVRN_STATUS_WIDTH 1 +#define MAX98926_INVALSLOT_STATUS_MASK (1<<5) +#define MAX98926_INVALSLOT_STATUS_SHIFT 5 +#define MAX98926_INVALSLOT_STATUS_WIDTH 1 +#define MAX98926_SLOTCNFLT_STATUS_MASK (1<<4) +#define MAX98926_SLOTCNFLT_STATUS_SHIFT 4 +#define MAX98926_SLOTCNFLT_STATUS_WIDTH 1 +#define MAX98926_VBSTOVFL_STATUS_MASK (1<<3) +#define MAX98926_VBSTOVFL_STATUS_SHIFT 3 +#define MAX98926_VBSTOVFL_STATUS_WIDTH 1 +#define MAX98926_VBATOVFL_STATUS_MASK (1<<2) +#define MAX98926_VBATOVFL_STATUS_SHIFT 2 +#define MAX98926_VBATOVFL_STATUS_WIDTH 1 +#define MAX98926_IMONOVFL_STATUS_MASK (1<<1) +#define MAX98926_IMONOVFL_STATUS_SHIFT 1 +#define MAX98926_IMONOVFL_STATUS_WIDTH 1 +#define MAX98926_VMONOVFL_STATUS_MASK (1<<0) +#define MAX98926_VMONOVFL_STATUS_SHIFT 0 +#define MAX98926_VMONOVFL_STATUS_WIDTH 1 + +/* MAX98926_R005_STATE0 */ +#define MAX98926_THERMWARN_END_STATE_MASK (1<<3) +#define MAX98926_THERMWARN_END_STATE_SHIFT 3 +#define MAX98926_THERMWARN_END_STATE_WIDTH 1 +#define MAX98926_THERMWARN_BGN_STATE_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_STATE_SHIFT 1 +#define MAX98926_THERMWARN_BGN_STATE_WIDTH 1 +#define MAX98926_THERMSHDN_END_STATE_MASK (1<<1) +#define MAX98926_THERMSHDN_END_STATE_SHIFT 1 +#define MAX98926_THERMSHDN_END_STATE_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_STATE_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_STATE_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_STATE_WIDTH 1 + +/* MAX98926_R006_STATE1 */ +#define MAX98926_SPRCURNT_STATE_MASK (1<<5) +#define MAX98926_SPRCURNT_STATE_SHIFT 5 +#define MAX98926_SPRCURNT_STATE_WIDTH 1 +#define MAX98926_WATCHFAIL_STATE_MASK (1<<4) +#define MAX98926_WATCHFAIL_STATE_SHIFT 4 +#define MAX98926_WATCHFAIL_STATE_WIDTH 1 +#define MAX98926_ALCINFH_STATE_MASK (1<<3) +#define MAX98926_ALCINFH_STATE_SHIFT 3 +#define MAX98926_ALCINFH_STATE_WIDTH 1 +#define MAX98926_ALCACT_STATE_MASK (1<<2) +#define MAX98926_ALCACT_STATE_SHIFT 2 +#define MAX98926_ALCACT_STATE_WIDTH 1 +#define MAX98926_ALCMUT_STATE_MASK (1<<1) +#define MAX98926_ALCMUT_STATE_SHIFT 1 +#define MAX98926_ALCMUT_STATE_WIDTH 1 +#define MAX98926_ALCP_STATE_MASK (1<<0) +#define MAX98926_ALCP_STATE_SHIFT 0 +#define MAX98926_ALCP_STATE_WIDTH 1 + +/* MAX98926_R007_STATE2 */ +#define MAX98926_SLOTOVRN_STATE_MASK (1<<6) +#define MAX98926_SLOTOVRN_STATE_SHIFT 6 +#define MAX98926_SLOTOVRN_STATE_WIDTH 1 +#define MAX98926_INVALSLOT_STATE_MASK (1<<5) +#define MAX98926_INVALSLOT_STATE_SHIFT 5 +#define MAX98926_INVALSLOT_STATE_WIDTH 1 +#define MAX98926_SLOTCNFLT_STATE_MASK (1<<4) +#define MAX98926_SLOTCNFLT_STATE_SHIFT 4 +#define MAX98926_SLOTCNFLT_STATE_WIDTH 1 +#define MAX98926_VBSTOVFL_STATE_MASK (1<<3) +#define MAX98926_VBSTOVFL_STATE_SHIFT 3 +#define MAX98926_VBSTOVFL_STATE_WIDTH 1 +#define MAX98926_VBATOVFL_STATE_MASK (1<<2) +#define MAX98926_VBATOVFL_STATE_SHIFT 2 +#define MAX98926_VBATOVFL_STATE_WIDTH 1 +#define MAX98926_IMONOVFL_STATE_MASK (1<<1) +#define MAX98926_IMONOVFL_STATE_SHIFT 1 +#define MAX98926_IMONOVFL_STATE_WIDTH 1 +#define MAX98926_VMONOVFL_STATE_MASK (1<<0) +#define MAX98926_VMONOVFL_STATE_SHIFT 0 +#define MAX98926_VMONOVFL_STATE_WIDTH 1 + +/* MAX98926_R008_FLAG0 */ +#define MAX98926_THERMWARN_END_FLAG_MASK (1<<3) +#define MAX98926_THERMWARN_END_FLAG_SHIFT 3 +#define MAX98926_THERMWARN_END_FLAG_WIDTH 1 +#define MAX98926_THERMWARN_BGN_FLAG_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_FLAG_SHIFT 2 +#define MAX98926_THERMWARN_BGN_FLAG_WIDTH 1 +#define MAX98926_THERMSHDN_END_FLAG_MASK (1<<1) +#define MAX98926_THERMSHDN_END_FLAG_SHIFT 1 +#define MAX98926_THERMSHDN_END_FLAG_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_FLAG_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_FLAG_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_FLAG_WIDTH 1 + +/* MAX98926_R009_FLAG1 */ +#define MAX98926_SPKCURNT_FLAG_MASK (1<<5) +#define MAX98926_SPKCURNT_FLAG_SHIFT 5 +#define MAX98926_SPKCURNT_FLAG_WIDTH 1 +#define MAX98926_WATCHFAIL_FLAG_MASK (1<<4) +#define MAX98926_WATCHFAIL_FLAG_SHIFT 4 +#define MAX98926_WATCHFAIL_FLAG_WIDTH 1 +#define MAX98926_ALCINFH_FLAG_MASK (1<<3) +#define MAX98926_ALCINFH_FLAG_SHIFT 3 +#define MAX98926_ALCINFH_FLAG_WIDTH 1 +#define MAX98926_ALCACT_FLAG_MASK (1<<2) +#define MAX98926_ALCACT_FLAG_SHIFT 2 +#define MAX98926_ALCACT_FLAG_WIDTH 1 +#define MAX98926_ALCMUT_FLAG_MASK (1<<1) +#define MAX98926_ALCMUT_FLAG_SHIFT 1 +#define MAX98926_ALCMUT_FLAG_WIDTH 1 +#define MAX98926_ALCP_FLAG_MASK (1<<0) +#define MAX98926_ALCP_FLAG_SHIFT 0 +#define MAX98926_ALCP_FLAG_WIDTH 1 + +/* MAX98926_R00A_FLAG2 */ +#define MAX98926_SLOTOVRN_FLAG_MASK (1<<6) +#define MAX98926_SLOTOVRN_FLAG_SHIFT 6 +#define MAX98926_SLOTOVRN_FLAG_WIDTH 1 +#define MAX98926_INVALSLOT_FLAG_MASK (1<<5) +#define MAX98926_INVALSLOT_FLAG_SHIFT 5 +#define MAX98926_INVALSLOT_FLAG_WIDTH 1 +#define MAX98926_SLOTCNFLT_FLAG_MASK (1<<4) +#define MAX98926_SLOTCNFLT_FLAG_SHIFT 4 +#define MAX98926_SLOTCNFLT_FLAG_WIDTH 1 +#define MAX98926_VBSTOVFL_FLAG_MASK (1<<3) +#define MAX98926_VBSTOVFL_FLAG_SHIFT 3 +#define MAX98926_VBSTOVFL_FLAG_WIDTH 1 +#define MAX98926_VBATOVFL_FLAG_MASK (1<<2) +#define MAX98926_VBATOVFL_FLAG_SHIFT 2 +#define MAX98926_VBATOVFL_FLAG_WIDTH 1 +#define MAX98926_IMONOVFL_FLAG_MASK (1<<1) +#define MAX98926_IMONOVFL_FLAG_SHIFT 1 +#define MAX98926_IMONOVFL_FLAG_WIDTH 1 +#define MAX98926_VMONOVFL_FLAG_MASK (1<<0) +#define MAX98926_VMONOVFL_FLAG_SHIFT 0 +#define MAX98926_VMONOVFL_FLAG_WIDTH 1 + +/* MAX98926_R00B_IRQ_ENABLE0 */ +#define MAX98926_THERMWARN_END_EN_MASK (1<<3) +#define MAX98926_THERMWARN_END_EN_SHIFT 3 +#define MAX98926_THERMWARN_END_EN_WIDTH 1 +#define MAX98926_THERMWARN_BGN_EN_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_EN_SHIFT 2 +#define MAX98926_THERMWARN_BGN_EN_WIDTH 1 +#define MAX98926_THERMSHDN_END_EN_MASK (1<<1) +#define MAX98926_THERMSHDN_END_EN_SHIFT 1 +#define MAX98926_THERMSHDN_END_EN_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_EN_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_EN_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_EN_WIDTH 1 + +/* MAX98926_R00C_IRQ_ENABLE1 */ +#define MAX98926_SPKCURNT_EN_MASK (1<<5) +#define MAX98926_SPKCURNT_EN_SHIFT 5 +#define MAX98926_SPKCURNT_EN_WIDTH 1 +#define MAX98926_WATCHFAIL_EN_MASK (1<<4) +#define MAX98926_WATCHFAIL_EN_SHIFT 4 +#define MAX98926_WATCHFAIL_EN_WIDTH 1 +#define MAX98926_ALCINFH_EN_MASK (1<<3) +#define MAX98926_ALCINFH_EN_SHIFT 3 +#define MAX98926_ALCINFH_EN_WIDTH 1 +#define MAX98926_ALCACT_EN_MASK (1<<2) +#define MAX98926_ALCACT_EN_SHIFT 2 +#define MAX98926_ALCACT_EN_WIDTH 1 +#define MAX98926_ALCMUT_EN_MASK (1<<1) +#define MAX98926_ALCMUT_EN_SHIFT 1 +#define MAX98926_ALCMUT_EN_WIDTH 1 +#define MAX98926_ALCP_EN_MASK (1<<0) +#define MAX98926_ALCP_EN_SHIFT 0 +#define MAX98926_ALCP_EN_WIDTH 1 + +/* MAX98926_R00D_IRQ_ENABLE2 */ +#define MAX98926_SLOTOVRN_EN_MASK (1<<6) +#define MAX98926_SLOTOVRN_EN_SHIFT 6 +#define MAX98926_SLOTOVRN_EN_WIDTH 1 +#define MAX98926_INVALSLOT_EN_MASK (1<<5) +#define MAX98926_INVALSLOT_EN_SHIFT 5 +#define MAX98926_INVALSLOT_EN_WIDTH 1 +#define MAX98926_SLOTCNFLT_EN_MASK (1<<4) +#define MAX98926_SLOTCNFLT_EN_SHIFT 4 +#define MAX98926_SLOTCNFLT_EN_WIDTH 1 +#define MAX98926_VBSTOVFL_EN_MASK (1<<3) +#define MAX98926_VBSTOVFL_EN_SHIFT 3 +#define MAX98926_VBSTOVFL_EN_WIDTH 1 +#define MAX98926_VBATOVFL_EN_MASK (1<<2) +#define MAX98926_VBATOVFL_EN_SHIFT 2 +#define MAX98926_VBATOVFL_EN_WIDTH 1 +#define MAX98926_IMONOVFL_EN_MASK (1<<1) +#define MAX98926_IMONOVFL_EN_SHIFT 1 +#define MAX98926_IMONOVFL_EN_WIDTH 1 +#define MAX98926_VMONOVFL_EN_MASK (1<<0) +#define MAX98926_VMONOVFL_EN_SHIFT 0 +#define MAX98926_VMONOVFL_EN_WIDTH 1 + +/* MAX98926_R00E_IRQ_CLEAR0 */ +#define MAX98926_THERMWARN_END_CLR_MASK (1<<3) +#define MAX98926_THERMWARN_END_CLR_SHIFT 3 +#define MAX98926_THERMWARN_END_CLR_WIDTH 1 +#define MAX98926_THERMWARN_BGN_CLR_MASK (1<<2) +#define MAX98926_THERMWARN_BGN_CLR_SHIFT 2 +#define MAX98926_THERMWARN_BGN_CLR_WIDTH 1 +#define MAX98926_THERMSHDN_END_CLR_MASK (1<<1) +#define MAX98926_THERMSHDN_END_CLR_SHIFT 1 +#define MAX98926_THERMSHDN_END_CLR_WIDTH 1 +#define MAX98926_THERMSHDN_BGN_CLR_MASK (1<<0) +#define MAX98926_THERMSHDN_BGN_CLR_SHIFT 0 +#define MAX98926_THERMSHDN_BGN_CLR_WIDTH 1 + +/* MAX98926_R00F_IRQ_CLEAR1 */ +#define MAX98926_SPKCURNT_CLR_MASK (1<<5) +#define MAX98926_SPKCURNT_CLR_SHIFT 5 +#define MAX98926_SPKCURNT_CLR_WIDTH 1 +#define MAX98926_WATCHFAIL_CLR_MASK (1<<4) +#define MAX98926_WATCHFAIL_CLR_SHIFT 4 +#define MAX98926_WATCHFAIL_CLR_WIDTH 1 +#define MAX98926_ALCINFH_CLR_MASK (1<<3) +#define MAX98926_ALCINFH_CLR_SHIFT 3 +#define MAX98926_ALCINFH_CLR_WIDTH 1 +#define MAX98926_ALCACT_CLR_MASK (1<<2) +#define MAX98926_ALCACT_CLR_SHIFT 2 +#define MAX98926_ALCACT_CLR_WIDTH 1 +#define MAX98926_ALCMUT_CLR_MASK (1<<1) +#define MAX98926_ALCMUT_CLR_SHIFT 1 +#define MAX98926_ALCMUT_CLR_WIDTH 1 +#define MAX98926_ALCP_CLR_MASK (1<<0) +#define MAX98926_ALCP_CLR_SHIFT 0 +#define MAX98926_ALCP_CLR_WIDTH 1 + +/* MAX98926_R010_IRQ_CLEAR2 */ +#define MAX98926_SLOTOVRN_CLR_MASK (1<<6) +#define MAX98926_SLOTOVRN_CLR_SHIFT 6 +#define MAX98926_SLOTOVRN_CLR_WIDTH 1 +#define MAX98926_INVALSLOT_CLR_MASK (1<<5) +#define MAX98926_INVALSLOT_CLR_SHIFT 5 +#define MAX98926_INVALSLOT_CLR_WIDTH 1 +#define MAX98926_SLOTCNFLT_CLR_MASK (1<<4) +#define MAX98926_SLOTCNFLT_CLR_SHIFT 4 +#define MAX98926_SLOTCNFLT_CLR_WIDTH 1 +#define MAX98926_VBSTOVFL_CLR_MASK (1<<3) +#define MAX98926_VBSTOVFL_CLR_SHIFT 3 +#define MAX98926_VBSTOVFL_CLR_WIDTH 1 +#define MAX98926_VBATOVFL_CLR_MASK (1<<2) +#define MAX98926_VBATOVFL_CLR_SHIFT 2 +#define MAX98926_VBATOVFL_CLR_WIDTH 1 +#define MAX98926_IMONOVFL_CLR_MASK (1<<1) +#define MAX98926_IMONOVFL_CLR_SHIFT 1 +#define MAX98926_IMONOVFL_CLR_WIDTH 1 +#define MAX98926_VMONOVFL_CLR_MASK (1<<0) +#define MAX98926_VMONOVFL_CLR_SHIFT 0 +#define MAX98926_VMONOVFL_CLR_WIDTH 1 + +/* MAX98926_R011_MAP0 */ +#define MAX98926_ER_THERMWARN_EN_MASK (1<<7) +#define MAX98926_ER_THERMWARN_EN_SHIFT 7 +#define MAX98926_ER_THERMWARN_EN_WIDTH 1 +#define MAX98926_ER_THERMWARN_MAP_MASK (0x07<<4) +#define MAX98926_ER_THERMWARN_MAP_SHIFT 4 +#define MAX98926_ER_THERMWARN_MAP_WIDTH 3 + +/* MAX98926_R012_MAP1 */ +#define MAX98926_ER_ALCMUT_EN_MASK (1<<7) +#define MAX98926_ER_ALCMUT_EN_SHIFT 7 +#define MAX98926_ER_ALCMUT_EN_WIDTH 1 +#define MAX98926_ER_ALCMUT_MAP_MASK (0x07<<4) +#define MAX98926_ER_ALCMUT_MAP_SHIFT 4 +#define MAX98926_ER_ALCMUT_MAP_WIDTH 3 +#define MAX98926_ER_ALCP_EN_MASK (1<<3) +#define MAX98926_ER_ALCP_EN_SHIFT 3 +#define MAX98926_ER_ALCP_EN_WIDTH 1 +#define MAX98926_ER_ALCP_MAP_MASK (0x07<<0) +#define MAX98926_ER_ALCP_MAP_SHIFT 0 +#define MAX98926_ER_ALCP_MAP_WIDTH 3 + +/* MAX98926_R013_MAP2 */ +#define MAX98926_ER_ALCINFH_EN_MASK (1<<7) +#define MAX98926_ER_ALCINFH_EN_SHIFT 7 +#define MAX98926_ER_ALCINFH_EN_WIDTH 1 +#define MAX98926_ER_ALCINFH_MAP_MASK (0x07<<4) +#define MAX98926_ER_ALCINFH_MAP_SHIFT 4 +#define MAX98926_ER_ALCINFH_MAP_WIDTH 3 +#define MAX98926_ER_ALCACT_EN_MASK (1<<3) +#define MAX98926_ER_ALCACT_EN_SHIFT 3 +#define MAX98926_ER_ALCACT_EN_WIDTH 1 +#define MAX98926_ER_ALCACT_MAP_MASK (0x07<<0) +#define MAX98926_ER_ALCACT_MAP_SHIFT 0 +#define MAX98926_ER_ALCACT_MAP_WIDTH 3 + +/* MAX98926_R014_MAP3 */ +#define MAX98926_ER_SPKCURNT_EN_MASK (1<<7) +#define MAX98926_ER_SPKCURNT_EN_SHIFT 7 +#define MAX98926_ER_SPKCURNT_EN_WIDTH 1 +#define MAX98926_ER_SPKCURNT_MAP_MASK (0x07<<4) +#define MAX98926_ER_SPKCURNT_MAP_SHIFT 4 +#define MAX98926_ER_SPKCURNT_MAP_WIDTH 3 + +/* MAX98926_R015_MAP4 */ +/* RESERVED */ + +/* MAX98926_R016_MAP5 */ +#define MAX98926_ER_IMONOVFL_EN_MASK (1<<7) +#define MAX98926_ER_IMONOVFL_EN_SHIFT 7 +#define MAX98926_ER_IMONOVFL_EN_WIDTH 1 +#define MAX98926_ER_IMONOVFL_MAP_MASK (0x07<<4) +#define MAX98926_ER_IMONOVFL_MAP_SHIFT 4 +#define MAX98926_ER_IMONOVFL_MAP_WIDTH 3 +#define MAX98926_ER_VMONOVFL_EN_MASK (1<<3) +#define MAX98926_ER_VMONOVFL_EN_SHIFT 3 +#define MAX98926_ER_VMONOVFL_EN_WIDTH 1 +#define MAX98926_ER_VMONOVFL_MAP_MASK (0x07<<0) +#define MAX98926_ER_VMONOVFL_MAP_SHIFT 0 +#define MAX98926_ER_VMONOVFL_MAP_WIDTH 3 + +/* MAX98926_R017_MAP6 */ +#define MAX98926_ER_VBSTOVFL_EN_MASK (1<<7) +#define MAX98926_ER_VBSTOVFL_EN_SHIFT 7 +#define MAX98926_ER_VBSTOVFL_EN_WIDTH 1 +#define MAX98926_ER_VBSTOVFL_MAP_MASK (0x07<<4) +#define MAX98926_ER_VBSTOVFL_MAP_SHIFT 4 +#define MAX98926_ER_VBSTOVFL_MAP_WIDTH 3 +#define MAX98926_ER_VBATOVFL_EN_MASK (1<<3) +#define MAX98926_ER_VBATOVFL_EN_SHIFT 3 +#define MAX98926_ER_VBATOVFL_EN_WIDTH 1 +#define MAX98926_ER_VBATOVFL_MAP_MASK (0x07<<0) +#define MAX98926_ER_VBATOVFL_MAP_SHIFT 0 +#define MAX98926_ER_VBATOVFL_MAP_WIDTH 3 + +/* MAX98926_R018_MAP7 */ +#define MAX98926_ER_INVALSLOT_EN_MASK (1<<7) +#define MAX98926_ER_INVALSLOT_EN_SHIFT 7 +#define MAX98926_ER_INVALSLOT_EN_WIDTH 1 +#define MAX98926_ER_INVALSLOT_MAP_MASK (0x07<<4) +#define MAX98926_ER_INVALSLOT_MAP_SHIFT 4 +#define MAX98926_ER_INVALSLOT_MAP_WIDTH 3 +#define MAX98926_ER_SLOTCNFLT_EN_MASK (1<<3) +#define MAX98926_ER_SLOTCNFLT_EN_SHIFT 3 +#define MAX98926_ER_SLOTCNFLT_EN_WIDTH 1 +#define MAX98926_ER_SLOTCNFLT_MAP_MASK (0x07<<0) +#define MAX98926_ER_SLOTCNFLT_MAP_SHIFT 0 +#define MAX98926_ER_SLOTCNFLT_MAP_WIDTH 3 + +/* MAX98926_R019_MAP8 */ +#define MAX98926_ER_SLOTOVRN_EN_MASK (1<<3) +#define MAX98926_ER_SLOTOVRN_EN_SHIFT 3 +#define MAX98926_ER_SLOTOVRN_EN_WIDTH 1 +#define MAX98926_ER_SLOTOVRN_MAP_MASK (0x07<<0) +#define MAX98926_ER_SLOTOVRN_MAP_SHIFT 0 +#define MAX98926_ER_SLOTOVRN_MAP_WIDTH 3 + +/* MAX98926_R01A_DAI_CLK_MODE1 */ +#define MAX98926_DAI_CLK_SOURCE_MASK (1<<6) +#define MAX98926_DAI_CLK_SOURCE_SHIFT 6 +#define MAX98926_DAI_CLK_SOURCE_WIDTH 1 +#define MAX98926_MDLL_MULT_MASK (0x0F<<0) +#define MAX98926_MDLL_MULT_SHIFT 0 +#define MAX98926_MDLL_MULT_WIDTH 4 + +#define MAX98926_MDLL_MULT_MCLKx8 6 +#define MAX98926_MDLL_MULT_MCLKx16 8 + +/* MAX98926_R01B_DAI_CLK_MODE2 */ +#define MAX98926_DAI_SR_MASK (0x0F<<4) +#define MAX98926_DAI_SR_SHIFT 4 +#define MAX98926_DAI_SR_WIDTH 4 +#define MAX98926_DAI_MAS_MASK (1<<3) +#define MAX98926_DAI_MAS_SHIFT 3 +#define MAX98926_DAI_MAS_WIDTH 1 +#define MAX98926_DAI_BSEL_MASK (0x07<<0) +#define MAX98926_DAI_BSEL_SHIFT 0 +#define MAX98926_DAI_BSEL_WIDTH 3 + +#define MAX98926_DAI_BSEL_32 (0 << MAX98926_DAI_BSEL_SHIFT) +#define MAX98926_DAI_BSEL_48 (1 << MAX98926_DAI_BSEL_SHIFT) +#define MAX98926_DAI_BSEL_64 (2 << MAX98926_DAI_BSEL_SHIFT) +#define MAX98926_DAI_BSEL_256 (6 << MAX98926_DAI_BSEL_SHIFT) + +/* MAX98926_R01C_DAI_CLK_DIV_M_MSBS */ +#define MAX98926_DAI_M_MSBS_MASK (0xFF<<0) +#define MAX98926_DAI_M_MSBS_SHIFT 0 +#define MAX98926_DAI_M_MSBS_WIDTH 8 + +/* MAX98926_R01D_DAI_CLK_DIV_M_LSBS */ +#define MAX98926_DAI_M_LSBS_MASK (0xFF<<0) +#define MAX98926_DAI_M_LSBS_SHIFT 0 +#define MAX98926_DAI_M_LSBS_WIDTH 8 + +/* MAX98926_R01E_DAI_CLK_DIV_N_MSBS */ +#define MAX98926_DAI_N_MSBS_MASK (0x7F<<0) +#define MAX98926_DAI_N_MSBS_SHIFT 0 +#define MAX98926_DAI_N_MSBS_WIDTH 7 + +/* MAX98926_R01F_DAI_CLK_DIV_N_LSBS */ +#define MAX98926_DAI_N_LSBS_MASK (0xFF<<0) +#define MAX98926_DAI_N_LSBS_SHIFT 0 +#define MAX98926_DAI_N_LSBS_WIDTH 8 + +/* MAX98926_R020_FORMAT */ +#define MAX98926_DAI_CHANSZ_MASK (0x03<<6) +#define MAX98926_DAI_CHANSZ_SHIFT 6 +#define MAX98926_DAI_CHANSZ_WIDTH 2 +#define MAX98926_DAI_INTERLEAVE_MASK (1<<5) +#define MAX98926_DAI_INTERLEAVE_SHIFT 5 +#define MAX98926_DAI_INTERLEAVE_WIDTH 1 +#define MAX98926_DAI_EXTBCLK_HIZ_MASK (1<<4) +#define MAX98926_DAI_EXTBCLK_HIZ_SHIFT 4 +#define MAX98926_DAI_EXTBCLK_HIZ_WIDTH 1 +#define MAX98926_DAI_WCI_MASK (1<<3) +#define MAX98926_DAI_WCI_SHIFT 3 +#define MAX98926_DAI_WCI_WIDTH 1 +#define MAX98926_DAI_BCI_MASK (1<<2) +#define MAX98926_DAI_BCI_SHIFT 2 +#define MAX98926_DAI_BCI_WIDTH 1 +#define MAX98926_DAI_DLY_MASK (1<<1) +#define MAX98926_DAI_DLY_SHIFT 1 +#define MAX98926_DAI_DLY_WIDTH 1 +#define MAX98926_DAI_TDM_MASK (1<<0) +#define MAX98926_DAI_TDM_SHIFT 0 +#define MAX98926_DAI_TDM_WIDTH 1 + +#define MAX98926_DAI_CHANSZ_16 (1 << MAX98926_DAI_CHANSZ_SHIFT) +#define MAX98926_DAI_CHANSZ_24 (2 << MAX98926_DAI_CHANSZ_SHIFT) +#define MAX98926_DAI_CHANSZ_32 (3 << MAX98926_DAI_CHANSZ_SHIFT) + +/* MAX98926_R021_TDM_SLOT_SELECT */ +#define MAX98926_DAI_DO_EN_MASK (1<<7) +#define MAX98926_DAI_DO_EN_SHIFT 7 +#define MAX98926_DAI_DO_EN_WIDTH 1 +#define MAX98926_DAI_DIN_EN_MASK (1<<6) +#define MAX98926_DAI_DIN_EN_SHIFT 6 +#define MAX98926_DAI_DIN_EN_WIDTH 1 +#define MAX98926_DAI_INR_SOURCE_MASK (0x07<<3) +#define MAX98926_DAI_INR_SOURCE_SHIFT 3 +#define MAX98926_DAI_INR_SOURCE_WIDTH 3 +#define MAX98926_DAI_INL_SOURCE_MASK (0x07<<0) +#define MAX98926_DAI_INL_SOURCE_SHIFT 0 +#define MAX98926_DAI_INL_SOURCE_WIDTH 3 + +/* MAX98926_R022_DOUT_CFG_VMON */ +#define MAX98926_DAI_VMON_EN_MASK (1<<5) +#define MAX98926_DAI_VMON_EN_SHIFT 5 +#define MAX98926_DAI_VMON_EN_WIDTH 1 +#define MAX98926_DAI_VMON_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_VMON_SLOT_SHIFT 0 +#define MAX98926_DAI_VMON_SLOT_WIDTH 5 + +#define MAX98926_DAI_VMON_SLOT_00_01 (0 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_01_02 (1 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_02_03 (2 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_03_04 (3 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_04_05 (4 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_05_06 (5 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_06_07 (6 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_07_08 (7 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_08_09 (8 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_09_0A (9 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0A_0B (10 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0B_0C (11 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0C_0D (12 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0D_0E (13 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0E_0F (14 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_0F_10 (15 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_10_11 (16 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_11_12 (17 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_12_13 (18 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_13_14 (19 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_14_15 (20 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_15_16 (21 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_16_17 (22 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_17_18 (23 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_18_19 (24 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_19_1A (25 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1A_1B (26 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1B_1C (27 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1C_1D (28 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1D_1E (29 << MAX98926_DAI_VMON_SLOT_SHIFT) +#define MAX98926_DAI_VMON_SLOT_1E_1F (30 << MAX98926_DAI_VMON_SLOT_SHIFT) + +/* MAX98926_R023_DOUT_CFG_IMON */ +#define MAX98926_DAI_IMON_EN_MASK (1<<5) +#define MAX98926_DAI_IMON_EN_SHIFT 5 +#define MAX98926_DAI_IMON_EN_WIDTH 1 +#define MAX98926_DAI_IMON_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_IMON_SLOT_SHIFT 0 +#define MAX98926_DAI_IMON_SLOT_WIDTH 5 + +#define MAX98926_DAI_IMON_SLOT_00_01 (0 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_01_02 (1 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_02_03 (2 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_03_04 (3 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_04_05 (4 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_05_06 (5 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_06_07 (6 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_07_08 (7 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_08_09 (8 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_09_0A (9 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0A_0B (10 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0B_0C (11 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0C_0D (12 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0D_0E (13 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0E_0F (14 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_0F_10 (15 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_10_11 (16 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_11_12 (17 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_12_13 (18 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_13_14 (19 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_14_15 (20 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_15_16 (21 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_16_17 (22 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_17_18 (23 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_18_19 (24 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_19_1A (25 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1A_1B (26 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1B_1C (27 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1C_1D (28 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1D_1E (29 << MAX98926_DAI_IMON_SLOT_SHIFT) +#define MAX98926_DAI_IMON_SLOT_1E_1F (30 << MAX98926_DAI_IMON_SLOT_SHIFT) + +/* MAX98926_R024_DOUT_CFG_VBAT */ +#define MAX98926_DAI_INTERLEAVE_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_INTERLEAVE_SLOT_SHIFT 0 +#define MAX98926_DAI_INTERLEAVE_SLOT_WIDTH 5 + +/* MAX98926_R025_DOUT_CFG_VBST */ +#define MAX98926_DAI_VBST_EN_MASK (1<<5) +#define MAX98926_DAI_VBST_EN_SHIFT 5 +#define MAX98926_DAI_VBST_EN_WIDTH 1 +#define MAX98926_DAI_VBST_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_VBST_SLOT_SHIFT 0 +#define MAX98926_DAI_VBST_SLOT_WIDTH 5 + +/* MAX98926_R026_DOUT_CFG_FLAG */ +#define MAX98926_DAI_FLAG_EN_MASK (1<<5) +#define MAX98926_DAI_FLAG_EN_SHIFT 5 +#define MAX98926_DAI_FLAG_EN_WIDTH 1 +#define MAX98926_DAI_FLAG_SLOT_MASK (0x1F<<0) +#define MAX98926_DAI_FLAG_SLOT_SHIFT 0 +#define MAX98926_DAI_FLAG_SLOT_WIDTH 5 + +/* MAX98926_R027_DOUT_HIZ_CFG1 */ +#define MAX98926_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG1_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG1_WIDTH 8 + +/* MAX98926_R028_DOUT_HIZ_CFG2 */ +#define MAX98926_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG2_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG2_WIDTH 8 + +/* MAX98926_R029_DOUT_HIZ_CFG3 */ +#define MAX98926_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG3_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG3_WIDTH 8 + +/* MAX98926_R02A_DOUT_HIZ_CFG4 */ +#define MAX98926_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0) +#define MAX98926_DAI_SLOT_HIZ_CFG4_SHIFT 0 +#define MAX98926_DAI_SLOT_HIZ_CFG4_WIDTH 8 + +/* MAX98926_R02B_DOUT_DRV_STRENGTH */ +#define MAX98926_DAI_OUT_DRIVE_MASK (0x03<<0) +#define MAX98926_DAI_OUT_DRIVE_SHIFT 0 +#define MAX98926_DAI_OUT_DRIVE_WIDTH 2 + +/* MAX98926_R02C_FILTERS */ +#define MAX98926_ADC_DITHER_EN_MASK (1<<7) +#define MAX98926_ADC_DITHER_EN_SHIFT 7 +#define MAX98926_ADC_DITHER_EN_WIDTH 1 +#define MAX98926_IV_DCB_EN_MASK (1<<6) +#define MAX98926_IV_DCB_EN_SHIFT 6 +#define MAX98926_IV_DCB_EN_WIDTH 1 +#define MAX98926_DAC_DITHER_EN_MASK (1<<4) +#define MAX98926_DAC_DITHER_EN_SHIFT 4 +#define MAX98926_DAC_DITHER_EN_WIDTH 1 +#define MAX98926_DAC_FILTER_MODE_MASK (1<<3) +#define MAX98926_DAC_FILTER_MODE_SHIFT 3 +#define MAX98926_DAC_FILTER_MODE_WIDTH 1 +#define MAX98926_DAC_HPF_MASK (0x07<<0) +#define MAX98926_DAC_HPF_SHIFT 0 +#define MAX98926_DAC_HPF_WIDTH 3 +#define MAX98926_DAC_HPF_DISABLE (0 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_DC_BLOCK (1 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_100 (2 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_200 (3 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_400 (4 << MAX98926_DAC_HPF_SHIFT) +#define MAX98926_DAC_HPF_EN_800 (5 << MAX98926_DAC_HPF_SHIFT) + +/* MAX98926_R02D_GAIN */ +#define MAX98926_DAC_IN_SEL_MASK (0x03<<5) +#define MAX98926_DAC_IN_SEL_SHIFT 5 +#define MAX98926_DAC_IN_SEL_WIDTH 2 +#define MAX98926_SPK_GAIN_MASK (0x1F<<0) +#define MAX98926_SPK_GAIN_SHIFT 0 +#define MAX98926_SPK_GAIN_WIDTH 5 + +#define MAX98926_DAC_IN_SEL_LEFT_DAI (0 << MAX98926_DAC_IN_SEL_SHIFT) +#define MAX98926_DAC_IN_SEL_RIGHT_DAI (1 << MAX98926_DAC_IN_SEL_SHIFT) +#define MAX98926_DAC_IN_SEL_SUMMED_DAI (2 << MAX98926_DAC_IN_SEL_SHIFT) +#define MAX98926_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << MAX98926_DAC_IN_SEL_SHIFT) + +/* MAX98926_R02E_GAIN_RAMPING */ +#define MAX98926_SPK_RMP_EN_MASK (1<<1) +#define MAX98926_SPK_RMP_EN_SHIFT 1 +#define MAX98926_SPK_RMP_EN_WIDTH 1 +#define MAX98926_SPK_ZCD_EN_MASK (1<<0) +#define MAX98926_SPK_ZCD_EN_SHIFT 0 +#define MAX98926_SPK_ZCD_EN_WIDTH 1 + +/* MAX98926_R02F_SPK_AMP */ +#define MAX98926_SPK_MODE_MASK (1<<0) +#define MAX98926_SPK_MODE_SHIFT 0 +#define MAX98926_SPK_MODE_WIDTH 1 +#define MAX98926_INSELECT_MODE_MASK (1<<1) +#define MAX98926_INSELECT_MODE_SHIFT 1 +#define MAX98926_INSELECT_MODE_WIDTH 1 + +/* MAX98926_R030_THRESHOLD */ +#define MAX98926_ALC_EN_MASK (1<<5) +#define MAX98926_ALC_EN_SHIFT 5 +#define MAX98926_ALC_EN_WIDTH 1 +#define MAX98926_ALC_TH_MASK (0x1F<<0) +#define MAX98926_ALC_TH_SHIFT 0 +#define MAX98926_ALC_TH_WIDTH 5 + +/* MAX98926_R031_ALC_ATTACK */ +#define MAX98926_ALC_ATK_STEP_MASK (0x0F<<4) +#define MAX98926_ALC_ATK_STEP_SHIFT 4 +#define MAX98926_ALC_ATK_STEP_WIDTH 4 +#define MAX98926_ALC_ATK_RATE_MASK (0x7<<0) +#define MAX98926_ALC_ATK_RATE_SHIFT 0 +#define MAX98926_ALC_ATK_RATE_WIDTH 3 + +/* MAX98926_R032_ALC_ATTEN_RLS */ +#define MAX98926_ALC_MAX_ATTEN_MASK (0x0F<<4) +#define MAX98926_ALC_MAX_ATTEN_SHIFT 4 +#define MAX98926_ALC_MAX_ATTEN_WIDTH 4 +#define MAX98926_ALC_RLS_RATE_MASK (0x7<<0) +#define MAX98926_ALC_RLS_RATE_SHIFT 0 +#define MAX98926_ALC_RLS_RATE_WIDTH 3 + +/* MAX98926_R033_ALC_HOLD_RLS */ +#define MAX98926_ALC_RLS_TGR_MASK (1<<0) +#define MAX98926_ALC_RLS_TGR_SHIFT 0 +#define MAX98926_ALC_RLS_TGR_WIDTH 1 + +/* MAX98926_R034_ALC_CONFIGURATION */ +#define MAX98926_ALC_MUTE_EN_MASK (1<<7) +#define MAX98926_ALC_MUTE_EN_SHIFT 7 +#define MAX98926_ALC_MUTE_EN_WIDTH 1 +#define MAX98926_ALC_MUTE_DLY_MASK (0x07<<4) +#define MAX98926_ALC_MUTE_DLY_SHIFT 4 +#define MAX98926_ALC_MUTE_DLY_WIDTH 3 +#define MAX98926_ALC_RLS_DBT_MASK (0x07<<0) +#define MAX98926_ALC_RLS_DBT_SHIFT 0 +#define MAX98926_ALC_RLS_DBT_WIDTH 3 + +/* MAX98926_R035_BOOST_CONVERTER */ +#define MAX98926_BST_SYNC_MASK (1<<7) +#define MAX98926_BST_SYNC_SHIFT 7 +#define MAX98926_BST_SYNC_WIDTH 1 +#define MAX98926_BST_PHASE_MASK (0x03<<4) +#define MAX98926_BST_PHASE_SHIFT 4 +#define MAX98926_BST_PHASE_WIDTH 2 +#define MAX98926_BST_SKIP_MODE_MASK (0x03<<0) +#define MAX98926_BST_SKIP_MODE_SHIFT 0 +#define MAX98926_BST_SKIP_MODE_WIDTH 2 + +/* MAX98926_R036_BLOCK_ENABLE */ +#define MAX98926_BST_EN_MASK (1<<7) +#define MAX98926_BST_EN_SHIFT 7 +#define MAX98926_BST_EN_WIDTH 1 +#define MAX98926_WATCH_EN_MASK (1<<6) +#define MAX98926_WATCH_EN_SHIFT 6 +#define MAX98926_WATCH_EN_WIDTH 1 +#define MAX98926_CLKMON_EN_MASK (1<<5) +#define MAX98926_CLKMON_EN_SHIFT 5 +#define MAX98926_CLKMON_EN_WIDTH 1 +#define MAX98926_SPK_EN_MASK (1<<4) +#define MAX98926_SPK_EN_SHIFT 4 +#define MAX98926_SPK_EN_WIDTH 1 +#define MAX98926_ADC_VBST_EN_MASK (1<<3) +#define MAX98926_ADC_VBST_EN_SHIFT 3 +#define MAX98926_ADC_VBST_EN_WIDTH 1 +#define MAX98926_ADC_VBAT_EN_MASK (1<<2) +#define MAX98926_ADC_VBAT_EN_SHIFT 2 +#define MAX98926_ADC_VBAT_EN_WIDTH 1 +#define MAX98926_ADC_IMON_EN_MASK (1<<1) +#define MAX98926_ADC_IMON_EN_SHIFT 1 +#define MAX98926_ADC_IMON_EN_WIDTH 1 +#define MAX98926_ADC_VMON_EN_MASK (1<<0) +#define MAX98926_ADC_VMON_EN_SHIFT 0 +#define MAX98926_ADC_VMON_EN_WIDTH 1 + +/* MAX98926_R037_CONFIGURATION */ +#define MAX98926_BST_VOUT_MASK (0x0F<<4) +#define MAX98926_BST_VOUT_SHIFT 4 +#define MAX98926_BST_VOUT_WIDTH 4 +#define MAX98926_THERMWARN_LEVEL_MASK (0x03<<2) +#define MAX98926_THERMWARN_LEVEL_SHIFT 2 +#define MAX98926_THERMWARN_LEVEL_WIDTH 2 +#define MAX98926_WATCH_TIME_MASK (0x03<<0) +#define MAX98926_WATCH_TIME_SHIFT 0 +#define MAX98926_WATCH_TIME_WIDTH 2 + +/* MAX98926_R038_GLOBAL_ENABLE */ +#define MAX98926_EN_MASK (1<<7) +#define MAX98926_EN_SHIFT 7 +#define MAX98926_EN_WIDTH 1 + +/* MAX98926_R03A_BOOST_LIMITER */ +#define MAX98926_BST_ILIM_MASK (0xF<<4) +#define MAX98926_BST_ILIM_SHIFT 4 +#define MAX98926_BST_ILIM_WIDTH 4 + +/* MAX98926_R0FF_VERSION */ +#define MAX98926_REV_ID_MASK (0xFF<<0) +#define MAX98926_REV_ID_SHIFT 0 +#define MAX98926_REV_ID_WIDTH 8 + +struct max98926_priv { + struct regmap *regmap; + struct snd_soc_component *component; + unsigned int sysclk; + unsigned int v_slot; + unsigned int i_slot; + unsigned int ch_size; + unsigned int interleave_mode; +}; +#endif diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c new file mode 100644 index 0000000000..776f23d38a --- /dev/null +++ b/sound/soc/codecs/max98927.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * max98927.c -- MAX98927 ALSA Soc Audio driver + * + * Copyright (C) 2016-2017 Maxim Integrated Products + * Author: Ryan Lee + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98927.h" + +static struct reg_default max98927_reg[] = { + {MAX98927_R0001_INT_RAW1, 0x00}, + {MAX98927_R0002_INT_RAW2, 0x00}, + {MAX98927_R0003_INT_RAW3, 0x00}, + {MAX98927_R0004_INT_STATE1, 0x00}, + {MAX98927_R0005_INT_STATE2, 0x00}, + {MAX98927_R0006_INT_STATE3, 0x00}, + {MAX98927_R0007_INT_FLAG1, 0x00}, + {MAX98927_R0008_INT_FLAG2, 0x00}, + {MAX98927_R0009_INT_FLAG3, 0x00}, + {MAX98927_R000A_INT_EN1, 0x00}, + {MAX98927_R000B_INT_EN2, 0x00}, + {MAX98927_R000C_INT_EN3, 0x00}, + {MAX98927_R000D_INT_FLAG_CLR1, 0x00}, + {MAX98927_R000E_INT_FLAG_CLR2, 0x00}, + {MAX98927_R000F_INT_FLAG_CLR3, 0x00}, + {MAX98927_R0010_IRQ_CTRL, 0x00}, + {MAX98927_R0011_CLK_MON, 0x00}, + {MAX98927_R0012_WDOG_CTRL, 0x00}, + {MAX98927_R0013_WDOG_RST, 0x00}, + {MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH, 0x75}, + {MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH, 0x8c}, + {MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS, 0x08}, + {MAX98927_R0017_PIN_CFG, 0x55}, + {MAX98927_R0018_PCM_RX_EN_A, 0x00}, + {MAX98927_R0019_PCM_RX_EN_B, 0x00}, + {MAX98927_R001A_PCM_TX_EN_A, 0x00}, + {MAX98927_R001B_PCM_TX_EN_B, 0x00}, + {MAX98927_R001C_PCM_TX_HIZ_CTRL_A, 0x00}, + {MAX98927_R001D_PCM_TX_HIZ_CTRL_B, 0x00}, + {MAX98927_R001E_PCM_TX_CH_SRC_A, 0x00}, + {MAX98927_R001F_PCM_TX_CH_SRC_B, 0x00}, + {MAX98927_R0020_PCM_MODE_CFG, 0x40}, + {MAX98927_R0021_PCM_MASTER_MODE, 0x00}, + {MAX98927_R0022_PCM_CLK_SETUP, 0x22}, + {MAX98927_R0023_PCM_SR_SETUP1, 0x00}, + {MAX98927_R0024_PCM_SR_SETUP2, 0x00}, + {MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, 0x00}, + {MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, 0x00}, + {MAX98927_R0027_ICC_RX_EN_A, 0x00}, + {MAX98927_R0028_ICC_RX_EN_B, 0x00}, + {MAX98927_R002B_ICC_TX_EN_A, 0x00}, + {MAX98927_R002C_ICC_TX_EN_B, 0x00}, + {MAX98927_R002E_ICC_HIZ_MANUAL_MODE, 0x00}, + {MAX98927_R002F_ICC_TX_HIZ_EN_A, 0x00}, + {MAX98927_R0030_ICC_TX_HIZ_EN_B, 0x00}, + {MAX98927_R0031_ICC_LNK_EN, 0x00}, + {MAX98927_R0032_PDM_TX_EN, 0x00}, + {MAX98927_R0033_PDM_TX_HIZ_CTRL, 0x00}, + {MAX98927_R0034_PDM_TX_CTRL, 0x00}, + {MAX98927_R0035_PDM_RX_CTRL, 0x00}, + {MAX98927_R0036_AMP_VOL_CTRL, 0x00}, + {MAX98927_R0037_AMP_DSP_CFG, 0x02}, + {MAX98927_R0038_TONE_GEN_DC_CFG, 0x00}, + {MAX98927_R0039_DRE_CTRL, 0x01}, + {MAX98927_R003A_AMP_EN, 0x00}, + {MAX98927_R003B_SPK_SRC_SEL, 0x00}, + {MAX98927_R003C_SPK_GAIN, 0x00}, + {MAX98927_R003D_SSM_CFG, 0x04}, + {MAX98927_R003E_MEAS_EN, 0x00}, + {MAX98927_R003F_MEAS_DSP_CFG, 0x04}, + {MAX98927_R0040_BOOST_CTRL0, 0x00}, + {MAX98927_R0041_BOOST_CTRL3, 0x00}, + {MAX98927_R0042_BOOST_CTRL1, 0x00}, + {MAX98927_R0043_MEAS_ADC_CFG, 0x00}, + {MAX98927_R0044_MEAS_ADC_BASE_MSB, 0x01}, + {MAX98927_R0045_MEAS_ADC_BASE_LSB, 0x00}, + {MAX98927_R0046_ADC_CH0_DIVIDE, 0x00}, + {MAX98927_R0047_ADC_CH1_DIVIDE, 0x00}, + {MAX98927_R0048_ADC_CH2_DIVIDE, 0x00}, + {MAX98927_R0049_ADC_CH0_FILT_CFG, 0x00}, + {MAX98927_R004A_ADC_CH1_FILT_CFG, 0x00}, + {MAX98927_R004B_ADC_CH2_FILT_CFG, 0x00}, + {MAX98927_R004C_MEAS_ADC_CH0_READ, 0x00}, + {MAX98927_R004D_MEAS_ADC_CH1_READ, 0x00}, + {MAX98927_R004E_MEAS_ADC_CH2_READ, 0x00}, + {MAX98927_R0051_BROWNOUT_STATUS, 0x00}, + {MAX98927_R0052_BROWNOUT_EN, 0x00}, + {MAX98927_R0053_BROWNOUT_INFINITE_HOLD, 0x00}, + {MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR, 0x00}, + {MAX98927_R0055_BROWNOUT_LVL_HOLD, 0x00}, + {MAX98927_R005A_BROWNOUT_LVL1_THRESH, 0x00}, + {MAX98927_R005B_BROWNOUT_LVL2_THRESH, 0x00}, + {MAX98927_R005C_BROWNOUT_LVL3_THRESH, 0x00}, + {MAX98927_R005D_BROWNOUT_LVL4_THRESH, 0x00}, + {MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS, 0x00}, + {MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL, 0x00}, + {MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL, 0x00}, + {MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE, 0x00}, + {MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT, 0x00}, + {MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1, 0x00}, + {MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2, 0x00}, + {MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3, 0x00}, + {MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT, 0x00}, + {MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1, 0x00}, + {MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2, 0x00}, + {MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3, 0x00}, + {MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT, 0x00}, + {MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1, 0x00}, + {MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2, 0x00}, + {MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3, 0x00}, + {MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT, 0x00}, + {MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, 0x00}, + {MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2, 0x00}, + {MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3, 0x00}, + {MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, 0x00}, + {MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY, 0x00}, + {MAX98927_R0084_ENV_TRACK_REL_RATE, 0x00}, + {MAX98927_R0085_ENV_TRACK_HOLD_RATE, 0x00}, + {MAX98927_R0086_ENV_TRACK_CTRL, 0x00}, + {MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, 0x00}, + {MAX98927_R00FF_GLOBAL_SHDN, 0x00}, + {MAX98927_R0100_SOFT_RESET, 0x00}, + {MAX98927_R01FF_REV_ID, 0x40}, +}; + +static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + unsigned int mode = 0; + unsigned int format = 0; + bool use_pdm = false; + unsigned int invert = 0; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + max98927->provider = false; + mode = MAX98927_PCM_MASTER_MODE_SLAVE; + break; + case SND_SOC_DAIFMT_CBP_CFP: + max98927->provider = true; + mode = MAX98927_PCM_MASTER_MODE_MASTER; + break; + default: + dev_err(component->dev, "DAI clock mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, MAX98927_R0021_PCM_MASTER_MODE, + MAX98927_PCM_MASTER_MODE_MASK, mode); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE, invert); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = MAX98927_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = MAX98927_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98927_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98927_PCM_FORMAT_TDM_MODE0; + break; + case SND_SOC_DAIFMT_PDM: + use_pdm = true; + break; + default: + return -EINVAL; + } + max98927->iface = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + if (!use_pdm) { + /* pcm channel configuration */ + regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN); + + regmap_update_bits(max98927->regmap, + MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT); + + regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL, + MAX98927_SPK_SRC_MASK, 0); + + regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL, + MAX98927_PDM_RX_EN_MASK, 0); + } else { + /* pdm channel configuration */ + regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL, + MAX98927_PDM_RX_EN_MASK, 1); + + regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL, + MAX98927_SPK_SRC_MASK, 3); + + regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, + 0); + } + return 0; +} + +/* codec MCLK rate in master mode */ +static const int rate_table[] = { + 5644800, 6000000, 6144000, 6500000, + 9600000, 11289600, 12000000, 12288000, + 13000000, 19200000, +}; + +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, +}; + +static int max98927_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} +static int max98927_set_clock(struct max98927_priv *max98927, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_component *component = max98927->component; + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98927->ch_size; + int value; + + if (max98927->provider) { + int i; + /* match rate to closest value */ + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i] >= max98927->sysclk) + break; + } + if (i == ARRAY_SIZE(rate_table)) { + dev_err(component->dev, "failed to find proper clock rate.\n"); + return -EINVAL; + } + regmap_update_bits(max98927->regmap, + MAX98927_R0021_PCM_MASTER_MODE, + MAX98927_PCM_MASTER_MODE_MCLK_MASK, + i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT); + } + + if (!max98927->tdm_mode) { + /* BCLK configuration */ + value = max98927_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, + MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, value); + } + return 0; +} + +static int max98927_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + max98927->ch_size = snd_pcm_format_width(params_format(params)); + + regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + dev_dbg(component->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_8000; + break; + case 11025: + sampling_rate = MAX98927_PCM_SR_SET1_SR_11025; + break; + case 12000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_12000; + break; + case 16000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_16000; + break; + case 22050: + sampling_rate = MAX98927_PCM_SR_SET1_SR_22050; + break; + case 24000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_24000; + break; + case 32000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_32000; + break; + case 44100: + sampling_rate = MAX98927_PCM_SR_SET1_SR_44100; + break; + case 48000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_48000; + break; + default: + dev_err(component->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98927->regmap, MAX98927_R0023_PCM_SR_SETUP1, + MAX98927_PCM_SR_SET1_SR_MASK, sampling_rate); + regmap_update_bits(max98927->regmap, MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_SR_MASK, + sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT); + + /* set sampling rate of IV */ + if (max98927->interleave_mode && + sampling_rate > MAX98927_PCM_SR_SET1_SR_16000) + regmap_update_bits(max98927->regmap, + MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate - 3); + else + regmap_update_bits(max98927->regmap, + MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate); + return max98927_set_clock(max98927, params); +err: + return -EINVAL; +} + +static int max98927_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + int bsel = 0; + unsigned int chan_sz = 0; + + max98927->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98927_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(component->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + regmap_write(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, + rx_mask & 0xFF); + regmap_write(max98927->regmap, MAX98927_R0019_PCM_RX_EN_B, + (rx_mask & 0xFF00) >> 8); + + /* Tx slot configuration */ + regmap_write(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A, + tx_mask & 0xFF); + regmap_write(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B, + (tx_mask & 0xFF00) >> 8); + + /* Tx slot Hi-Z configuration */ + regmap_write(max98927->regmap, MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + ~tx_mask & 0xFF); + regmap_write(max98927->regmap, MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + (~tx_mask & 0xFF00) >> 8); + + return 0; +} + +#define MAX98927_RATES SNDRV_PCM_RATE_8000_48000 + +#define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static int max98927_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + + max98927->sysclk = freq; + return 0; +} + +static const struct snd_soc_dai_ops max98927_dai_ops = { + .set_sysclk = max98927_dai_set_sysclk, + .set_fmt = max98927_dai_set_fmt, + .hw_params = max98927_dai_hw_params, + .set_tdm_slot = max98927_dai_tdm_slot, +}; + +static int max98927_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + max98927->tdm_mode = false; + break; + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN, + MAX98927_AMP_EN_MASK, 1); + regmap_update_bits(max98927->regmap, MAX98927_R00FF_GLOBAL_SHDN, + MAX98927_GLOBAL_EN_MASK, 1); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(max98927->regmap, MAX98927_R00FF_GLOBAL_SHDN, + MAX98927_GLOBAL_EN_MASK, 0); + regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN, + MAX98927_AMP_EN_MASK, 0); + break; + default: + return 0; + } + return 0; +} + +static const char * const max98927_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static const struct soc_enum dai_sel_enum = + SOC_ENUM_SINGLE(MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, + MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT, 3, + max98927_switch_text); + +static const struct snd_kcontrol_new max98927_dai_controls = + SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); + +static const struct snd_kcontrol_new max98927_vi_control = + SOC_DAPM_SINGLE("Switch", MAX98927_R003F_MEAS_DSP_CFG, 2, 1, 0); + +static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_R003A_AMP_EN, + 0, 0, max98927_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98927_dai_controls), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, + MAX98927_R003E_MEAS_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, + MAX98927_R003E_MEAS_EN, 1, 0), + SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, + &max98927_vi_control), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON"), +}; + +static DECLARE_TLV_DB_SCALE(max98927_spk_tlv, 300, 300, 0); +static DECLARE_TLV_DB_SCALE(max98927_digital_tlv, -1600, 25, 0); + +static bool max98927_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98927_R0001_INT_RAW1 ... MAX98927_R0028_ICC_RX_EN_B: + case MAX98927_R002B_ICC_TX_EN_A ... MAX98927_R002C_ICC_TX_EN_B: + case MAX98927_R002E_ICC_HIZ_MANUAL_MODE + ... MAX98927_R004E_MEAS_ADC_CH2_READ: + case MAX98927_R0051_BROWNOUT_STATUS + ... MAX98927_R0055_BROWNOUT_LVL_HOLD: + case MAX98927_R005A_BROWNOUT_LVL1_THRESH + ... MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE: + case MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT + ... MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ: + case MAX98927_R00FF_GLOBAL_SHDN: + case MAX98927_R0100_SOFT_RESET: + case MAX98927_R01FF_REV_ID: + return true; + default: + return false; + } +}; + +static bool max98927_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98927_R0001_INT_RAW1 ... MAX98927_R0009_INT_FLAG3: + case MAX98927_R004C_MEAS_ADC_CH0_READ: + case MAX98927_R004D_MEAS_ADC_CH1_READ: + case MAX98927_R004E_MEAS_ADC_CH2_READ: + case MAX98927_R0051_BROWNOUT_STATUS: + case MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ: + case MAX98927_R01FF_REV_ID: + case MAX98927_R0100_SOFT_RESET: + return true; + default: + return false; + } +} + +static const char * const max98927_boost_voltage_text[] = { + "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V", + "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V", + "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V", + "9.5V", "9.625V", "9.75V", "9.875V", "10V" +}; + +static SOC_ENUM_SINGLE_DECL(max98927_boost_voltage, + MAX98927_R0040_BOOST_CTRL0, 0, + max98927_boost_voltage_text); + +static const char * const max98927_current_limit_text[] = { + "1.00A", "1.10A", "1.20A", "1.30A", "1.40A", "1.50A", "1.60A", "1.70A", + "1.80A", "1.90A", "2.00A", "2.10A", "2.20A", "2.30A", "2.40A", "2.50A", + "2.60A", "2.70A", "2.80A", "2.90A", "3.00A", "3.10A", "3.20A", "3.30A", + "3.40A", "3.50A", "3.60A", "3.70A", "3.80A", "3.90A", "4.00A", "4.10A" +}; + +static SOC_ENUM_SINGLE_DECL(max98927_current_limit, + MAX98927_R0042_BOOST_CTRL1, 1, + max98927_current_limit_text); + +static const struct snd_kcontrol_new max98927_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN, 0, 6, 0, + max98927_spk_tlv), + SOC_SINGLE_TLV("Digital Volume", MAX98927_R0036_AMP_VOL_CTRL, + 0, (1 << MAX98927_AMP_VOL_WIDTH) - 1, 0, + max98927_digital_tlv), + SOC_SINGLE("Amp DSP Switch", MAX98927_R0052_BROWNOUT_EN, + MAX98927_BROWNOUT_DSP_SHIFT, 1, 0), + SOC_SINGLE("Ramp Switch", MAX98927_R0037_AMP_DSP_CFG, + MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0), + SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL, MAX98927_DRE_EN_SHIFT, + 1, 0), + SOC_SINGLE("Volume Location Switch", MAX98927_R0036_AMP_VOL_CTRL, + MAX98927_AMP_VOL_SEL_SHIFT, 1, 0), + SOC_ENUM("Boost Output Voltage", max98927_boost_voltage), + SOC_ENUM("Current Limit", max98927_current_limit), +}; + +static const struct snd_soc_dapm_route max98927_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, + /* Capture */ + { "VI Sense", "Switch", "VMON" }, + { "VI Sense", "Switch", "IMON" }, + { "Voltage Sense", NULL, "VI Sense" }, + { "Current Sense", NULL, "VI Sense" }, +}; + +static struct snd_soc_dai_driver max98927_dai[] = { + { + .name = "max98927-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98927_RATES, + .formats = MAX98927_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98927_RATES, + .formats = MAX98927_FORMATS, + }, + .ops = &max98927_dai_ops, + } +}; + +static int max98927_probe(struct snd_soc_component *component) +{ + struct max98927_priv *max98927 = snd_soc_component_get_drvdata(component); + + max98927->component = component; + + /* Software Reset */ + regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, + MAX98927_SOFT_RESET); + + /* IV default slot configuration */ + regmap_write(max98927->regmap, MAX98927_R001C_PCM_TX_HIZ_CTRL_A, 0xFF); + regmap_write(max98927->regmap, MAX98927_R001D_PCM_TX_HIZ_CTRL_B, 0xFF); + regmap_write(max98927->regmap, MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, + 0x80); + regmap_write(max98927->regmap, MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, + 0x1); + /* Set inital volume (+13dB) */ + regmap_write(max98927->regmap, MAX98927_R0036_AMP_VOL_CTRL, 0x38); + regmap_write(max98927->regmap, MAX98927_R003C_SPK_GAIN, 0x05); + /* Enable DC blocker */ + regmap_write(max98927->regmap, MAX98927_R0037_AMP_DSP_CFG, 0x03); + /* Enable IMON VMON DC blocker */ + regmap_write(max98927->regmap, MAX98927_R003F_MEAS_DSP_CFG, 0xF7); + /* Boost Output Voltage & Current limit */ + regmap_write(max98927->regmap, MAX98927_R0040_BOOST_CTRL0, 0x1C); + regmap_write(max98927->regmap, MAX98927_R0042_BOOST_CTRL1, 0x3E); + /* Measurement ADC config */ + regmap_write(max98927->regmap, MAX98927_R0043_MEAS_ADC_CFG, 0x04); + regmap_write(max98927->regmap, MAX98927_R0044_MEAS_ADC_BASE_MSB, 0x00); + regmap_write(max98927->regmap, MAX98927_R0045_MEAS_ADC_BASE_LSB, 0x24); + /* Brownout Level */ + regmap_write(max98927->regmap, MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, + 0x06); + /* Envelope Tracking configuration */ + regmap_write(max98927->regmap, MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, + 0x08); + regmap_write(max98927->regmap, MAX98927_R0086_ENV_TRACK_CTRL, 0x01); + regmap_write(max98927->regmap, MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, + 0x10); + + /* voltage, current slot configuration */ + regmap_write(max98927->regmap, MAX98927_R001E_PCM_TX_CH_SRC_A, + (max98927->i_l_slot << MAX98927_PCM_TX_CH_SRC_A_I_SHIFT | max98927->v_l_slot) & 0xFF); + + if (max98927->v_l_slot < 8) { + regmap_update_bits(max98927->regmap, + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + 1 << max98927->v_l_slot, 0); + regmap_update_bits(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A, + 1 << max98927->v_l_slot, + 1 << max98927->v_l_slot); + } else { + regmap_update_bits(max98927->regmap, + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + 1 << (max98927->v_l_slot - 8), 0); + regmap_update_bits(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B, + 1 << (max98927->v_l_slot - 8), + 1 << (max98927->v_l_slot - 8)); + } + + if (max98927->i_l_slot < 8) { + regmap_update_bits(max98927->regmap, + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + 1 << max98927->i_l_slot, 0); + regmap_update_bits(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A, + 1 << max98927->i_l_slot, + 1 << max98927->i_l_slot); + } else { + regmap_update_bits(max98927->regmap, + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + 1 << (max98927->i_l_slot - 8), 0); + regmap_update_bits(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B, + 1 << (max98927->i_l_slot - 8), + 1 << (max98927->i_l_slot - 8)); + } + + /* Set interleave mode */ + if (max98927->interleave_mode) + regmap_update_bits(max98927->regmap, + MAX98927_R001F_PCM_TX_CH_SRC_B, + MAX98927_PCM_TX_CH_INTERLEAVE_MASK, + MAX98927_PCM_TX_CH_INTERLEAVE_MASK); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max98927_suspend(struct device *dev) +{ + struct max98927_priv *max98927 = dev_get_drvdata(dev); + + regcache_cache_only(max98927->regmap, true); + regcache_mark_dirty(max98927->regmap); + return 0; +} +static int max98927_resume(struct device *dev) +{ + struct max98927_priv *max98927 = dev_get_drvdata(dev); + + regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET, + MAX98927_SOFT_RESET); + regcache_cache_only(max98927->regmap, false); + regcache_sync(max98927->regmap); + return 0; +} +#endif + +static const struct dev_pm_ops max98927_pm = { + SET_SYSTEM_SLEEP_PM_OPS(max98927_suspend, max98927_resume) +}; + +static const struct snd_soc_component_driver soc_component_dev_max98927 = { + .probe = max98927_probe, + .controls = max98927_snd_controls, + .num_controls = ARRAY_SIZE(max98927_snd_controls), + .dapm_widgets = max98927_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98927_dapm_widgets), + .dapm_routes = max98927_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98927_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config max98927_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98927_R01FF_REV_ID, + .reg_defaults = max98927_reg, + .num_reg_defaults = ARRAY_SIZE(max98927_reg), + .readable_reg = max98927_readable_register, + .volatile_reg = max98927_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static void max98927_slot_config(struct i2c_client *i2c, + struct max98927_priv *max98927) +{ + int value; + struct device *dev = &i2c->dev; + + if (!device_property_read_u32(dev, "vmon-slot-no", &value)) + max98927->v_l_slot = value & 0xF; + else + max98927->v_l_slot = 0; + + if (!device_property_read_u32(dev, "imon-slot-no", &value)) + max98927->i_l_slot = value & 0xF; + else + max98927->i_l_slot = 1; +} + +static int max98927_i2c_probe(struct i2c_client *i2c) +{ + + int ret = 0, value; + int reg = 0; + struct max98927_priv *max98927 = NULL; + + max98927 = devm_kzalloc(&i2c->dev, sizeof(*max98927), GFP_KERNEL); + if (!max98927) { + ret = -ENOMEM; + return ret; + } + i2c_set_clientdata(i2c, max98927); + + /* update interleave mode info */ + if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode")) { + max98927->interleave_mode = true; + } else { + if (!of_property_read_u32(i2c->dev.of_node, "interleave_mode", + &value)) + if (value > 0) + max98927->interleave_mode = true; + } + + /* regmap initialization */ + max98927->regmap + = devm_regmap_init_i2c(i2c, &max98927_regmap); + if (IS_ERR(max98927->regmap)) { + ret = PTR_ERR(max98927->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + + max98927->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(max98927->reset_gpio)) { + ret = PTR_ERR(max98927->reset_gpio); + return dev_err_probe(&i2c->dev, ret, "failed to request GPIO reset pin"); + } + + if (max98927->reset_gpio) { + gpiod_set_value_cansleep(max98927->reset_gpio, 0); + /* Wait for i2c port to be ready */ + usleep_range(5000, 6000); + } + + /* Check Revision ID */ + ret = regmap_read(max98927->regmap, MAX98927_R01FF_REV_ID, ®); + if (ret < 0) { + dev_err(&i2c->dev, + "Failed to read: 0x%02X\n", MAX98927_R01FF_REV_ID); + return ret; + } + dev_info(&i2c->dev, "MAX98927 revisionID: 0x%02X\n", reg); + + /* voltage/current slot configuration */ + max98927_slot_config(i2c, max98927); + + /* codec registeration */ + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_max98927, + max98927_dai, ARRAY_SIZE(max98927_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register component: %d\n", ret); + + return ret; +} + +static void max98927_i2c_remove(struct i2c_client *i2c) +{ + struct max98927_priv *max98927 = i2c_get_clientdata(i2c); + + if (max98927->reset_gpio) + gpiod_set_value_cansleep(max98927->reset_gpio, 1); +} + +static const struct i2c_device_id max98927_i2c_id[] = { + { "max98927", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98927_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id max98927_of_match[] = { + { .compatible = "maxim,max98927", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98927_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98927_acpi_match[] = { + { "MX98927", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98927_acpi_match); +#endif + +static struct i2c_driver max98927_i2c_driver = { + .driver = { + .name = "max98927", + .of_match_table = of_match_ptr(max98927_of_match), + .acpi_match_table = ACPI_PTR(max98927_acpi_match), + .pm = &max98927_pm, + }, + .probe = max98927_i2c_probe, + .remove = max98927_i2c_remove, + .id_table = max98927_i2c_id, +}; + +module_i2c_driver(max98927_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98927 driver"); +MODULE_AUTHOR("Ryan Lee "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h new file mode 100644 index 0000000000..2353910f5f --- /dev/null +++ b/sound/soc/codecs/max98927.h @@ -0,0 +1,271 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * max98927.h -- MAX98927 ALSA Soc Audio driver + * + * Copyright (C) 2016-2017 Maxim Integrated Products + * Author: Ryan Lee + */ +#ifndef _MAX98927_H +#define _MAX98927_H + +/* Register Values */ +#define MAX98927_R0001_INT_RAW1 0x0001 +#define MAX98927_R0002_INT_RAW2 0x0002 +#define MAX98927_R0003_INT_RAW3 0x0003 +#define MAX98927_R0004_INT_STATE1 0x0004 +#define MAX98927_R0005_INT_STATE2 0x0005 +#define MAX98927_R0006_INT_STATE3 0x0006 +#define MAX98927_R0007_INT_FLAG1 0x0007 +#define MAX98927_R0008_INT_FLAG2 0x0008 +#define MAX98927_R0009_INT_FLAG3 0x0009 +#define MAX98927_R000A_INT_EN1 0x000A +#define MAX98927_R000B_INT_EN2 0x000B +#define MAX98927_R000C_INT_EN3 0x000C +#define MAX98927_R000D_INT_FLAG_CLR1 0x000D +#define MAX98927_R000E_INT_FLAG_CLR2 0x000E +#define MAX98927_R000F_INT_FLAG_CLR3 0x000F +#define MAX98927_R0010_IRQ_CTRL 0x0010 +#define MAX98927_R0011_CLK_MON 0x0011 +#define MAX98927_R0012_WDOG_CTRL 0x0012 +#define MAX98927_R0013_WDOG_RST 0x0013 +#define MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH 0x0014 +#define MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH 0x0015 +#define MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS 0x0016 +#define MAX98927_R0017_PIN_CFG 0x0017 +#define MAX98927_R0018_PCM_RX_EN_A 0x0018 +#define MAX98927_R0019_PCM_RX_EN_B 0x0019 +#define MAX98927_R001A_PCM_TX_EN_A 0x001A +#define MAX98927_R001B_PCM_TX_EN_B 0x001B +#define MAX98927_R001C_PCM_TX_HIZ_CTRL_A 0x001C +#define MAX98927_R001D_PCM_TX_HIZ_CTRL_B 0x001D +#define MAX98927_R001E_PCM_TX_CH_SRC_A 0x001E +#define MAX98927_R001F_PCM_TX_CH_SRC_B 0x001F +#define MAX98927_R0020_PCM_MODE_CFG 0x0020 +#define MAX98927_R0021_PCM_MASTER_MODE 0x0021 +#define MAX98927_R0022_PCM_CLK_SETUP 0x0022 +#define MAX98927_R0023_PCM_SR_SETUP1 0x0023 +#define MAX98927_R0024_PCM_SR_SETUP2 0x0024 +#define MAX98927_R0025_PCM_TO_SPK_MONOMIX_A 0x0025 +#define MAX98927_R0026_PCM_TO_SPK_MONOMIX_B 0x0026 +#define MAX98927_R0027_ICC_RX_EN_A 0x0027 +#define MAX98927_R0028_ICC_RX_EN_B 0x0028 +#define MAX98927_R002B_ICC_TX_EN_A 0x002B +#define MAX98927_R002C_ICC_TX_EN_B 0x002C +#define MAX98927_R002E_ICC_HIZ_MANUAL_MODE 0x002E +#define MAX98927_R002F_ICC_TX_HIZ_EN_A 0x002F +#define MAX98927_R0030_ICC_TX_HIZ_EN_B 0x0030 +#define MAX98927_R0031_ICC_LNK_EN 0x0031 +#define MAX98927_R0032_PDM_TX_EN 0x0032 +#define MAX98927_R0033_PDM_TX_HIZ_CTRL 0x0033 +#define MAX98927_R0034_PDM_TX_CTRL 0x0034 +#define MAX98927_R0035_PDM_RX_CTRL 0x0035 +#define MAX98927_R0036_AMP_VOL_CTRL 0x0036 +#define MAX98927_R0037_AMP_DSP_CFG 0x0037 +#define MAX98927_R0038_TONE_GEN_DC_CFG 0x0038 +#define MAX98927_R0039_DRE_CTRL 0x0039 +#define MAX98927_R003A_AMP_EN 0x003A +#define MAX98927_R003B_SPK_SRC_SEL 0x003B +#define MAX98927_R003C_SPK_GAIN 0x003C +#define MAX98927_R003D_SSM_CFG 0x003D +#define MAX98927_R003E_MEAS_EN 0x003E +#define MAX98927_R003F_MEAS_DSP_CFG 0x003F +#define MAX98927_R0040_BOOST_CTRL0 0x0040 +#define MAX98927_R0041_BOOST_CTRL3 0x0041 +#define MAX98927_R0042_BOOST_CTRL1 0x0042 +#define MAX98927_R0043_MEAS_ADC_CFG 0x0043 +#define MAX98927_R0044_MEAS_ADC_BASE_MSB 0x0044 +#define MAX98927_R0045_MEAS_ADC_BASE_LSB 0x0045 +#define MAX98927_R0046_ADC_CH0_DIVIDE 0x0046 +#define MAX98927_R0047_ADC_CH1_DIVIDE 0x0047 +#define MAX98927_R0048_ADC_CH2_DIVIDE 0x0048 +#define MAX98927_R0049_ADC_CH0_FILT_CFG 0x0049 +#define MAX98927_R004A_ADC_CH1_FILT_CFG 0x004A +#define MAX98927_R004B_ADC_CH2_FILT_CFG 0x004B +#define MAX98927_R004C_MEAS_ADC_CH0_READ 0x004C +#define MAX98927_R004D_MEAS_ADC_CH1_READ 0x004D +#define MAX98927_R004E_MEAS_ADC_CH2_READ 0x004E +#define MAX98927_R0051_BROWNOUT_STATUS 0x0051 +#define MAX98927_R0052_BROWNOUT_EN 0x0052 +#define MAX98927_R0053_BROWNOUT_INFINITE_HOLD 0x0053 +#define MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR 0x0054 +#define MAX98927_R0055_BROWNOUT_LVL_HOLD 0x0055 +#define MAX98927_R005A_BROWNOUT_LVL1_THRESH 0x005A +#define MAX98927_R005B_BROWNOUT_LVL2_THRESH 0x005B +#define MAX98927_R005C_BROWNOUT_LVL3_THRESH 0x005C +#define MAX98927_R005D_BROWNOUT_LVL4_THRESH 0x005D +#define MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS 0x005E +#define MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL 0x005F +#define MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL 0x0060 +#define MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE 0x0061 +#define MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT 0x0072 +#define MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1 0x0073 +#define MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2 0x0074 +#define MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3 0x0075 +#define MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT 0x0076 +#define MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1 0x0077 +#define MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2 0x0078 +#define MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3 0x0079 +#define MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT 0x007A +#define MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1 0x007B +#define MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2 0x007C +#define MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3 0x007D +#define MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT 0x007E +#define MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1 0x007F +#define MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2 0x0080 +#define MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3 0x0081 +#define MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM 0x0082 +#define MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY 0x0083 +#define MAX98927_R0084_ENV_TRACK_REL_RATE 0x0084 +#define MAX98927_R0085_ENV_TRACK_HOLD_RATE 0x0085 +#define MAX98927_R0086_ENV_TRACK_CTRL 0x0086 +#define MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ 0x0087 +#define MAX98927_R00FF_GLOBAL_SHDN 0x00FF +#define MAX98927_R0100_SOFT_RESET 0x0100 +#define MAX98927_R01FF_REV_ID 0x01FF + +/* MAX98927_R0018_PCM_RX_EN_A */ +#define MAX98927_PCM_RX_CH0_EN (0x1 << 0) +#define MAX98927_PCM_RX_CH1_EN (0x1 << 1) +#define MAX98927_PCM_RX_CH2_EN (0x1 << 2) +#define MAX98927_PCM_RX_CH3_EN (0x1 << 3) +#define MAX98927_PCM_RX_CH4_EN (0x1 << 4) +#define MAX98927_PCM_RX_CH5_EN (0x1 << 5) +#define MAX98927_PCM_RX_CH6_EN (0x1 << 6) +#define MAX98927_PCM_RX_CH7_EN (0x1 << 7) + +/* MAX98927_R001A_PCM_TX_EN_A */ +#define MAX98927_PCM_TX_CH0_EN (0x1 << 0) +#define MAX98927_PCM_TX_CH1_EN (0x1 << 1) +#define MAX98927_PCM_TX_CH2_EN (0x1 << 2) +#define MAX98927_PCM_TX_CH3_EN (0x1 << 3) +#define MAX98927_PCM_TX_CH4_EN (0x1 << 4) +#define MAX98927_PCM_TX_CH5_EN (0x1 << 5) +#define MAX98927_PCM_TX_CH6_EN (0x1 << 6) +#define MAX98927_PCM_TX_CH7_EN (0x1 << 7) + +/* MAX98927_R001E_PCM_TX_CH_SRC_A */ +#define MAX98927_PCM_TX_CH_SRC_A_V_SHIFT (0) +#define MAX98927_PCM_TX_CH_SRC_A_I_SHIFT (4) + +/* MAX98927_R001F_PCM_TX_CH_SRC_B */ +#define MAX98927_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 5) + +/* MAX98927_R0020_PCM_MODE_CFG */ +#define MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 2) +#define MAX98927_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98927_PCM_MODE_CFG_FORMAT_SHIFT (3) +#define MAX98927_PCM_FORMAT_I2S (0x0 << 0) +#define MAX98927_PCM_FORMAT_LJ (0x1 << 0) +#define MAX98927_PCM_FORMAT_TDM_MODE0 (0x3 << 0) +#define MAX98927_PCM_FORMAT_TDM_MODE1 (0x4 << 0) +#define MAX98927_PCM_FORMAT_TDM_MODE2 (0x5 << 0) +#define MAX98927_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98927_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98927_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98927_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) + +/* MAX98927_R0021_PCM_MASTER_MODE */ +#define MAX98927_PCM_MASTER_MODE_MASK (0x3 << 0) +#define MAX98927_PCM_MASTER_MODE_SLAVE (0x0 << 0) +#define MAX98927_PCM_MASTER_MODE_MASTER (0x3 << 0) + +#define MAX98927_PCM_MASTER_MODE_MCLK_MASK (0xF << 2) +#define MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT (2) + +/* MAX98927_R0022_PCM_CLK_SETUP */ +#define MAX98927_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) + +/* MAX98927_R0023_PCM_SR_SETUP1 */ +#define MAX98927_PCM_SR_SET1_SR_MASK (0xF << 0) + +#define MAX98927_PCM_SR_SET1_SR_8000 (0x0 << 0) +#define MAX98927_PCM_SR_SET1_SR_11025 (0x1 << 0) +#define MAX98927_PCM_SR_SET1_SR_12000 (0x2 << 0) +#define MAX98927_PCM_SR_SET1_SR_16000 (0x3 << 0) +#define MAX98927_PCM_SR_SET1_SR_22050 (0x4 << 0) +#define MAX98927_PCM_SR_SET1_SR_24000 (0x5 << 0) +#define MAX98927_PCM_SR_SET1_SR_32000 (0x6 << 0) +#define MAX98927_PCM_SR_SET1_SR_44100 (0x7 << 0) +#define MAX98927_PCM_SR_SET1_SR_48000 (0x8 << 0) + +/* MAX98927_R0024_PCM_SR_SETUP2 */ +#define MAX98927_PCM_SR_SET2_SR_MASK (0xF << 4) +#define MAX98927_PCM_SR_SET2_SR_SHIFT (4) +#define MAX98927_PCM_SR_SET2_IVADC_SR_MASK (0xf << 0) + +/* MAX98927_R0025_PCM_TO_SPK_MONOMIX_A */ +#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6) +#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6) + +/* MAX98927_R0035_PDM_RX_CTRL */ +#define MAX98927_PDM_RX_EN_MASK (0x1 << 0) + +/* MAX98927_R0036_AMP_VOL_CTRL */ +#define MAX98927_AMP_VOL_SEL (0x1 << 7) +#define MAX98927_AMP_VOL_SEL_WIDTH (1) +#define MAX98927_AMP_VOL_SEL_SHIFT (7) +#define MAX98927_AMP_VOL_MASK (0x7f << 0) +#define MAX98927_AMP_VOL_WIDTH (7) +#define MAX98927_AMP_VOL_SHIFT (0) + +/* MAX98927_R0037_AMP_DSP_CFG */ +#define MAX98927_AMP_DSP_CFG_DCBLK_EN (0x1 << 0) +#define MAX98927_AMP_DSP_CFG_DITH_EN (0x1 << 1) +#define MAX98927_AMP_DSP_CFG_RMP_BYPASS (0x1 << 4) +#define MAX98927_AMP_DSP_CFG_DAC_INV (0x1 << 5) +#define MAX98927_AMP_DSP_CFG_RMP_SHIFT (4) + +/* MAX98927_R0039_DRE_CTRL */ +#define MAX98927_DRE_CTRL_DRE_EN (0x1 << 0) +#define MAX98927_DRE_EN_SHIFT 0x1 + +/* MAX98927_R003A_AMP_EN */ +#define MAX98927_AMP_EN_MASK (0x1 << 0) + +/* MAX98927_R003B_SPK_SRC_SEL */ +#define MAX98927_SPK_SRC_MASK (0x3 << 0) + +/* MAX98927_R003C_SPK_GAIN */ +#define MAX98927_SPK_PCM_GAIN_MASK (0x7 << 0) +#define MAX98927_SPK_PDM_GAIN_MASK (0x7 << 4) +#define MAX98927_SPK_GAIN_WIDTH (3) + +/* MAX98927_R003E_MEAS_EN */ +#define MAX98927_MEAS_V_EN (0x1 << 0) +#define MAX98927_MEAS_I_EN (0x1 << 1) + +/* MAX98927_R0040_BOOST_CTRL0 */ +#define MAX98927_BOOST_CTRL0_VOUT_MASK (0x1f << 0) +#define MAX98927_BOOST_CTRL0_PVDD_MASK (0x1 << 7) +#define MAX98927_BOOST_CTRL0_PVDD_EN_SHIFT (7) + +/* MAX98927_R0052_BROWNOUT_EN */ +#define MAX98927_BROWNOUT_BDE_EN (0x1 << 0) +#define MAX98927_BROWNOUT_AMP_EN (0x1 << 1) +#define MAX98927_BROWNOUT_DSP_EN (0x1 << 2) +#define MAX98927_BROWNOUT_DSP_SHIFT (2) + +/* MAX98927_R0100_SOFT_RESET */ +#define MAX98927_SOFT_RESET (0x1 << 0) + +/* MAX98927_R00FF_GLOBAL_SHDN */ +#define MAX98927_GLOBAL_EN_MASK (0x1 << 0) + +struct max98927_priv { + struct regmap *regmap; + struct snd_soc_component *component; + struct max98927_pdata *pdata; + struct gpio_desc *reset_gpio; + unsigned int spk_gain; + unsigned int sysclk; + unsigned int v_l_slot; + unsigned int i_l_slot; + bool interleave_mode; + unsigned int ch_size; + unsigned int rate; + unsigned int iface; + unsigned int provider; + unsigned int digital_gain; + bool tdm_mode; +}; +#endif diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c new file mode 100644 index 0000000000..086ac97e83 --- /dev/null +++ b/sound/soc/codecs/mc13783.c @@ -0,0 +1,789 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + * Copyright 2009 Sascha Hauer, s.hauer@pengutronix.de + * Copyright 2012 Philippe Retornaz, philippe.retornaz@epfl.ch + * + * Initial development of this code was funded by + * Phytec Messtechnik GmbH, https://www.phytec.de + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mc13783.h" + +#define AUDIO_RX0_ALSPEN (1 << 5) +#define AUDIO_RX0_ALSPSEL (1 << 7) +#define AUDIO_RX0_ADDCDC (1 << 21) +#define AUDIO_RX0_ADDSTDC (1 << 22) +#define AUDIO_RX0_ADDRXIN (1 << 23) + +#define AUDIO_RX1_PGARXEN (1 << 0); +#define AUDIO_RX1_PGASTEN (1 << 5) +#define AUDIO_RX1_ARXINEN (1 << 10) + +#define AUDIO_TX_AMC1REN (1 << 5) +#define AUDIO_TX_AMC1LEN (1 << 7) +#define AUDIO_TX_AMC2EN (1 << 9) +#define AUDIO_TX_ATXINEN (1 << 11) +#define AUDIO_TX_RXINREC (1 << 13) + +#define SSI_NETWORK_CDCTXRXSLOT(x) (((x) & 0x3) << 2) +#define SSI_NETWORK_CDCTXSECSLOT(x) (((x) & 0x3) << 4) +#define SSI_NETWORK_CDCRXSECSLOT(x) (((x) & 0x3) << 6) +#define SSI_NETWORK_CDCRXSECGAIN(x) (((x) & 0x3) << 8) +#define SSI_NETWORK_CDCSUMGAIN(x) (1 << 10) +#define SSI_NETWORK_CDCFSDLY(x) (1 << 11) +#define SSI_NETWORK_DAC_SLOTS_8 (1 << 12) +#define SSI_NETWORK_DAC_SLOTS_4 (2 << 12) +#define SSI_NETWORK_DAC_SLOTS_2 (3 << 12) +#define SSI_NETWORK_DAC_SLOT_MASK (3 << 12) +#define SSI_NETWORK_DAC_RXSLOT_0_1 (0 << 14) +#define SSI_NETWORK_DAC_RXSLOT_2_3 (1 << 14) +#define SSI_NETWORK_DAC_RXSLOT_4_5 (2 << 14) +#define SSI_NETWORK_DAC_RXSLOT_6_7 (3 << 14) +#define SSI_NETWORK_DAC_RXSLOT_MASK (3 << 14) +#define SSI_NETWORK_STDCRXSECSLOT(x) (((x) & 0x3) << 16) +#define SSI_NETWORK_STDCRXSECGAIN(x) (((x) & 0x3) << 18) +#define SSI_NETWORK_STDCSUMGAIN (1 << 20) + +/* + * MC13783_AUDIO_CODEC and MC13783_AUDIO_DAC mostly share the same + * register layout + */ +#define AUDIO_SSI_SEL (1 << 0) +#define AUDIO_CLK_SEL (1 << 1) +#define AUDIO_CSM (1 << 2) +#define AUDIO_BCL_INV (1 << 3) +#define AUDIO_CFS_INV (1 << 4) +#define AUDIO_CFS(x) (((x) & 0x3) << 5) +#define AUDIO_CLK(x) (((x) & 0x7) << 7) +#define AUDIO_C_EN (1 << 11) +#define AUDIO_C_CLK_EN (1 << 12) +#define AUDIO_C_RESET (1 << 15) + +#define AUDIO_CODEC_CDCFS8K16K (1 << 10) +#define AUDIO_DAC_CFS_DLY_B (1 << 10) + +struct mc13783_priv { + struct mc13xxx *mc13xxx; + struct regmap *regmap; + + enum mc13783_ssi_port adc_ssi_port; + enum mc13783_ssi_port dac_ssi_port; +}; + +/* Mapping between sample rates and register value */ +static unsigned int mc13783_rates[] = { + 8000, 11025, 12000, 16000, + 22050, 24000, 32000, 44100, + 48000, 64000, 96000 +}; + +static int mc13783_pcm_hw_params_dac(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned int rate = params_rate(params); + int i; + + for (i = 0; i < ARRAY_SIZE(mc13783_rates); i++) { + if (rate == mc13783_rates[i]) { + snd_soc_component_update_bits(component, MC13783_AUDIO_DAC, + 0xf << 17, i << 17); + return 0; + } + } + + return -EINVAL; +} + +static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned int rate = params_rate(params); + unsigned int val; + + switch (rate) { + case 8000: + val = 0; + break; + case 16000: + val = AUDIO_CODEC_CDCFS8K16K; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K, + val); + + return 0; +} + +static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return mc13783_pcm_hw_params_dac(substream, params, dai); + else + return mc13783_pcm_hw_params_codec(substream, params, dai); +} + +static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt, + unsigned int reg) +{ + struct snd_soc_component *component = dai->component; + unsigned int val = 0; + unsigned int mask = AUDIO_CFS(3) | AUDIO_BCL_INV | AUDIO_CFS_INV | + AUDIO_CSM | AUDIO_C_CLK_EN | AUDIO_C_RESET; + + + /* DAI mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val |= AUDIO_CFS(2); + break; + case SND_SOC_DAIFMT_DSP_A: + val |= AUDIO_CFS(1); + break; + default: + return -EINVAL; + } + + /* DAI clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val |= AUDIO_BCL_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + val |= AUDIO_BCL_INV | AUDIO_CFS_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + val |= AUDIO_CFS_INV; + break; + } + + /* DAI clock master masks */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + val |= AUDIO_C_CLK_EN; + break; + case SND_SOC_DAIFMT_CBC_CFC: + val |= AUDIO_CSM; + break; + default: + return -EINVAL; + } + + val |= AUDIO_C_RESET; + + snd_soc_component_update_bits(component, reg, mask, val); + + return 0; +} + +static int mc13783_set_fmt_async(struct snd_soc_dai *dai, unsigned int fmt) +{ + if (dai->id == MC13783_ID_STEREO_DAC) + return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC); + else + return mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC); +} + +static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt) +{ + int ret; + + ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_DAC); + if (ret) + return ret; + + /* + * In synchronous mode force the voice codec into consumer mode + * so that the clock / framesync from the stereo DAC is used + */ + fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; + fmt |= SND_SOC_DAIFMT_CBC_CFC; + ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC); + + return ret; +} + +static int mc13783_sysclk[] = { + 13000000, + 15360000, + 16800000, + -1, + 26000000, + -1, /* 12000000, invalid for voice codec */ + -1, /* 3686400, invalid for voice codec */ + 33600000, +}; + +static int mc13783_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir, + unsigned int reg) +{ + struct snd_soc_component *component = dai->component; + int clk; + unsigned int val = 0; + unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL; + + for (clk = 0; clk < ARRAY_SIZE(mc13783_sysclk); clk++) { + if (mc13783_sysclk[clk] < 0) + continue; + if (mc13783_sysclk[clk] == freq) + break; + } + + if (clk == ARRAY_SIZE(mc13783_sysclk)) + return -EINVAL; + + if (clk_id == MC13783_CLK_CLIB) + val |= AUDIO_CLK_SEL; + + val |= AUDIO_CLK(clk); + + snd_soc_component_update_bits(component, reg, mask, val); + + return 0; +} + +static int mc13783_set_sysclk_dac(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC); +} + +static int mc13783_set_sysclk_codec(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC); +} + +static int mc13783_set_sysclk_sync(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + int ret; + + ret = mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_DAC); + if (ret) + return ret; + + return mc13783_set_sysclk(dai, clk_id, freq, dir, MC13783_AUDIO_CODEC); +} + +static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, + int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int val = 0; + unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK | + SSI_NETWORK_DAC_RXSLOT_MASK; + + switch (slots) { + case 2: + val |= SSI_NETWORK_DAC_SLOTS_2; + break; + case 4: + val |= SSI_NETWORK_DAC_SLOTS_4; + break; + case 8: + val |= SSI_NETWORK_DAC_SLOTS_8; + break; + default: + return -EINVAL; + } + + switch (rx_mask) { + case 0x03: + val |= SSI_NETWORK_DAC_RXSLOT_0_1; + break; + case 0x0c: + val |= SSI_NETWORK_DAC_RXSLOT_2_3; + break; + case 0x30: + val |= SSI_NETWORK_DAC_RXSLOT_4_5; + break; + case 0xc0: + val |= SSI_NETWORK_DAC_RXSLOT_6_7; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val); + + return 0; +} + +static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, + int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int val = 0; + unsigned int mask = 0x3f; + + if (slots != 4) + return -EINVAL; + + if (tx_mask != 0x3) + return -EINVAL; + + val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */ + val |= (0x01 << 4); /* secondary timeslot TX is 1 */ + + snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val); + + return 0; +} + +static int mc13783_set_tdm_slot_sync(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, + int slot_width) +{ + int ret; + + ret = mc13783_set_tdm_slot_dac(dai, tx_mask, rx_mask, slots, + slot_width); + if (ret) + return ret; + + ret = mc13783_set_tdm_slot_codec(dai, tx_mask, rx_mask, slots, + slot_width); + + return ret; +} + +static const struct snd_kcontrol_new mc1l_amp_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 7, 1, 0); + +static const struct snd_kcontrol_new mc1r_amp_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 5, 1, 0); + +static const struct snd_kcontrol_new mc2_amp_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 9, 1, 0); + +static const struct snd_kcontrol_new atx_amp_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_TX, 11, 1, 0); + + +/* Virtual mux. The chip does the input selection automatically + * as soon as we enable one input. */ +static const char * const adcl_enum_text[] = { + "MC1L", "RXINL", +}; + +static SOC_ENUM_SINGLE_VIRT_DECL(adcl_enum, adcl_enum_text); + +static const struct snd_kcontrol_new left_input_mux = + SOC_DAPM_ENUM("Route", adcl_enum); + +static const char * const adcr_enum_text[] = { + "MC1R", "MC2", "RXINR", "TXIN", +}; + +static SOC_ENUM_SINGLE_VIRT_DECL(adcr_enum, adcr_enum_text); + +static const struct snd_kcontrol_new right_input_mux = + SOC_DAPM_ENUM("Route", adcr_enum); + +static const struct snd_kcontrol_new samp_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 3, 1, 0); + +static const char * const speaker_amp_source_text[] = { + "CODEC", "Right" +}; +static SOC_ENUM_SINGLE_DECL(speaker_amp_source, MC13783_AUDIO_RX0, 4, + speaker_amp_source_text); +static const struct snd_kcontrol_new speaker_amp_source_mux = + SOC_DAPM_ENUM("Speaker Amp Source MUX", speaker_amp_source); + +static const char * const headset_amp_source_text[] = { + "CODEC", "Mixer" +}; + +static SOC_ENUM_SINGLE_DECL(headset_amp_source, MC13783_AUDIO_RX0, 11, + headset_amp_source_text); +static const struct snd_kcontrol_new headset_amp_source_mux = + SOC_DAPM_ENUM("Headset Amp Source MUX", headset_amp_source); + +static const struct snd_kcontrol_new cdcout_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 18, 1, 0); + +static const struct snd_kcontrol_new adc_bypass_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_CODEC, 16, 1, 0); + +static const struct snd_kcontrol_new lamp_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 5, 1, 0); + +static const struct snd_kcontrol_new hlamp_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 10, 1, 0); + +static const struct snd_kcontrol_new hramp_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 9, 1, 0); + +static const struct snd_kcontrol_new llamp_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 16, 1, 0); + +static const struct snd_kcontrol_new lramp_ctl = + SOC_DAPM_SINGLE("Switch", MC13783_AUDIO_RX0, 15, 1, 0); + +static const struct snd_soc_dapm_widget mc13783_dapm_widgets[] = { +/* Input */ + SND_SOC_DAPM_INPUT("MC1LIN"), + SND_SOC_DAPM_INPUT("MC1RIN"), + SND_SOC_DAPM_INPUT("MC2IN"), + SND_SOC_DAPM_INPUT("RXINR"), + SND_SOC_DAPM_INPUT("RXINL"), + SND_SOC_DAPM_INPUT("TXIN"), + + SND_SOC_DAPM_SUPPLY("MC1 Bias", MC13783_AUDIO_TX, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MC2 Bias", MC13783_AUDIO_TX, 1, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("MC1L Amp", MC13783_AUDIO_TX, 7, 0, &mc1l_amp_ctl), + SND_SOC_DAPM_SWITCH("MC1R Amp", MC13783_AUDIO_TX, 5, 0, &mc1r_amp_ctl), + SND_SOC_DAPM_SWITCH("MC2 Amp", MC13783_AUDIO_TX, 9, 0, &mc2_amp_ctl), + SND_SOC_DAPM_SWITCH("TXIN Amp", MC13783_AUDIO_TX, 11, 0, &atx_amp_ctl), + + SND_SOC_DAPM_MUX("PGA Left Input Mux", SND_SOC_NOPM, 0, 0, + &left_input_mux), + SND_SOC_DAPM_MUX("PGA Right Input Mux", SND_SOC_NOPM, 0, 0, + &right_input_mux), + + SND_SOC_DAPM_MUX("Speaker Amp Source MUX", SND_SOC_NOPM, 0, 0, + &speaker_amp_source_mux), + + SND_SOC_DAPM_MUX("Headset Amp Source MUX", SND_SOC_NOPM, 0, 0, + &headset_amp_source_mux), + + SND_SOC_DAPM_PGA("PGA Left Input", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA Right Input", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_ADC("ADC", "Capture", MC13783_AUDIO_CODEC, 11, 0), + SND_SOC_DAPM_SUPPLY("ADC_Reset", MC13783_AUDIO_CODEC, 15, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Voice CODEC PGA", MC13783_AUDIO_RX1, 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("Voice CODEC Bypass", MC13783_AUDIO_CODEC, 16, 0, + &adc_bypass_ctl), + +/* Output */ + SND_SOC_DAPM_SUPPLY("DAC_E", MC13783_AUDIO_DAC, 11, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC_Reset", MC13783_AUDIO_DAC, 15, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("RXOUTL"), + SND_SOC_DAPM_OUTPUT("RXOUTR"), + SND_SOC_DAPM_OUTPUT("HSL"), + SND_SOC_DAPM_OUTPUT("HSR"), + SND_SOC_DAPM_OUTPUT("LSPL"), + SND_SOC_DAPM_OUTPUT("LSP"), + SND_SOC_DAPM_OUTPUT("SP"), + SND_SOC_DAPM_OUTPUT("CDCOUT"), + + SND_SOC_DAPM_SWITCH("CDCOUT Switch", MC13783_AUDIO_RX0, 18, 0, + &cdcout_ctl), + SND_SOC_DAPM_SWITCH("Speaker Amp Switch", MC13783_AUDIO_RX0, 3, 0, + &samp_ctl), + SND_SOC_DAPM_SWITCH("Loudspeaker Amp", SND_SOC_NOPM, 0, 0, &lamp_ctl), + SND_SOC_DAPM_SWITCH("Headset Amp Left", MC13783_AUDIO_RX0, 10, 0, + &hlamp_ctl), + SND_SOC_DAPM_SWITCH("Headset Amp Right", MC13783_AUDIO_RX0, 9, 0, + &hramp_ctl), + SND_SOC_DAPM_SWITCH("Line out Amp Left", MC13783_AUDIO_RX0, 16, 0, + &llamp_ctl), + SND_SOC_DAPM_SWITCH("Line out Amp Right", MC13783_AUDIO_RX0, 15, 0, + &lramp_ctl), + SND_SOC_DAPM_DAC("DAC", "Playback", MC13783_AUDIO_RX0, 22, 0), + SND_SOC_DAPM_PGA("DAC PGA", MC13783_AUDIO_RX1, 5, 0, NULL, 0), +}; + +static struct snd_soc_dapm_route mc13783_routes[] = { +/* Input */ + { "MC1L Amp", NULL, "MC1LIN"}, + { "MC1R Amp", NULL, "MC1RIN" }, + { "MC2 Amp", NULL, "MC2IN" }, + { "TXIN Amp", NULL, "TXIN"}, + + { "PGA Left Input Mux", "MC1L", "MC1L Amp" }, + { "PGA Left Input Mux", "RXINL", "RXINL"}, + { "PGA Right Input Mux", "MC1R", "MC1R Amp" }, + { "PGA Right Input Mux", "MC2", "MC2 Amp"}, + { "PGA Right Input Mux", "TXIN", "TXIN Amp"}, + { "PGA Right Input Mux", "RXINR", "RXINR"}, + + { "PGA Left Input", NULL, "PGA Left Input Mux"}, + { "PGA Right Input", NULL, "PGA Right Input Mux"}, + + { "ADC", NULL, "PGA Left Input"}, + { "ADC", NULL, "PGA Right Input"}, + { "ADC", NULL, "ADC_Reset"}, + + { "Voice CODEC PGA", "Voice CODEC Bypass", "ADC" }, + + { "Speaker Amp Source MUX", "CODEC", "Voice CODEC PGA"}, + { "Speaker Amp Source MUX", "Right", "DAC PGA"}, + + { "Headset Amp Source MUX", "CODEC", "Voice CODEC PGA"}, + { "Headset Amp Source MUX", "Mixer", "DAC PGA"}, + +/* Output */ + { "HSL", NULL, "Headset Amp Left" }, + { "HSR", NULL, "Headset Amp Right"}, + { "RXOUTL", NULL, "Line out Amp Left"}, + { "RXOUTR", NULL, "Line out Amp Right"}, + { "SP", "Speaker Amp Switch", "Speaker Amp Source MUX"}, + { "LSP", "Loudspeaker Amp", "Speaker Amp Source MUX"}, + { "HSL", "Headset Amp Left", "Headset Amp Source MUX"}, + { "HSR", "Headset Amp Right", "Headset Amp Source MUX"}, + { "Line out Amp Left", NULL, "DAC PGA"}, + { "Line out Amp Right", NULL, "DAC PGA"}, + { "DAC PGA", NULL, "DAC"}, + { "DAC", NULL, "DAC_E"}, + { "CDCOUT", "CDCOUT Switch", "Voice CODEC PGA"}, +}; + +static const char * const mc13783_3d_mixer[] = {"Stereo", "Phase Mix", + "Mono", "Mono Mix"}; + +static SOC_ENUM_SINGLE_DECL(mc13783_enum_3d_mixer, + MC13783_AUDIO_RX1, 16, + mc13783_3d_mixer); + +static struct snd_kcontrol_new mc13783_control_list[] = { + SOC_SINGLE("Loudspeaker enable", MC13783_AUDIO_RX0, 5, 1, 0), + SOC_SINGLE("PCM Playback Volume", MC13783_AUDIO_RX1, 6, 15, 0), + SOC_SINGLE("PCM Playback Switch", MC13783_AUDIO_RX1, 5, 1, 0), + SOC_DOUBLE("PCM Capture Volume", MC13783_AUDIO_TX, 19, 14, 31, 0), + SOC_ENUM("3D Control", mc13783_enum_3d_mixer), + + SOC_SINGLE("CDCOUT Switch", MC13783_AUDIO_RX0, 18, 1, 0), + SOC_SINGLE("Earpiece Amp Switch", MC13783_AUDIO_RX0, 3, 1, 0), + SOC_DOUBLE("Headset Amp Switch", MC13783_AUDIO_RX0, 10, 9, 1, 0), + SOC_DOUBLE("Line out Amp Switch", MC13783_AUDIO_RX0, 16, 15, 1, 0), + + SOC_SINGLE("PCM Capture Mixin Switch", MC13783_AUDIO_RX0, 22, 1, 0), + SOC_SINGLE("Line in Capture Mixin Switch", MC13783_AUDIO_RX0, 23, 1, 0), + + SOC_SINGLE("CODEC Capture Volume", MC13783_AUDIO_RX1, 1, 15, 0), + SOC_SINGLE("CODEC Capture Mixin Switch", MC13783_AUDIO_RX0, 21, 1, 0), + + SOC_SINGLE("Line in Capture Volume", MC13783_AUDIO_RX1, 12, 15, 0), + SOC_SINGLE("Line in Capture Switch", MC13783_AUDIO_RX1, 10, 1, 0), + + SOC_SINGLE("MC1 Capture Bias Switch", MC13783_AUDIO_TX, 0, 1, 0), + SOC_SINGLE("MC2 Capture Bias Switch", MC13783_AUDIO_TX, 1, 1, 0), +}; + +static int mc13783_probe(struct snd_soc_component *component) +{ + struct mc13783_priv *priv = snd_soc_component_get_drvdata(component); + + snd_soc_component_init_regmap(component, + dev_get_regmap(component->dev->parent, NULL)); + + /* these are the reset values */ + mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX0, 0x25893); + mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_RX1, 0x00d35A); + mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_TX, 0x420000); + mc13xxx_reg_write(priv->mc13xxx, MC13783_SSI_NETWORK, 0x013060); + mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_CODEC, 0x180027); + mc13xxx_reg_write(priv->mc13xxx, MC13783_AUDIO_DAC, 0x0e0004); + + if (priv->adc_ssi_port == MC13783_SSI1_PORT) + mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC, + AUDIO_SSI_SEL, 0); + else + mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC, + AUDIO_SSI_SEL, AUDIO_SSI_SEL); + + if (priv->dac_ssi_port == MC13783_SSI1_PORT) + mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC, + AUDIO_SSI_SEL, 0); + else + mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_DAC, + AUDIO_SSI_SEL, AUDIO_SSI_SEL); + + return 0; +} + +static void mc13783_remove(struct snd_soc_component *component) +{ + struct mc13783_priv *priv = snd_soc_component_get_drvdata(component); + + /* Make sure VAUDIOON is off */ + mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_RX0, 0x3, 0); +} + +#define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000) + +#define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops mc13783_ops_dac = { + .hw_params = mc13783_pcm_hw_params_dac, + .set_fmt = mc13783_set_fmt_async, + .set_sysclk = mc13783_set_sysclk_dac, + .set_tdm_slot = mc13783_set_tdm_slot_dac, +}; + +static const struct snd_soc_dai_ops mc13783_ops_codec = { + .hw_params = mc13783_pcm_hw_params_codec, + .set_fmt = mc13783_set_fmt_async, + .set_sysclk = mc13783_set_sysclk_codec, + .set_tdm_slot = mc13783_set_tdm_slot_codec, +}; + +/* + * The mc13783 has two SSI ports, both of them can be routed either + * to the voice codec or the stereo DAC. When two different SSI ports + * are used for the voice codec and the stereo DAC we can do different + * formats and sysclock settings for playback and capture + * (mc13783-hifi-playback and mc13783-hifi-capture). Using the same port + * forces us to use symmetric rates (mc13783-hifi). + */ +static struct snd_soc_dai_driver mc13783_dai_async[] = { + { + .name = "mc13783-hifi-playback", + .id = MC13783_ID_STEREO_DAC, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = MC13783_FORMATS, + }, + .ops = &mc13783_ops_dac, + }, { + .name = "mc13783-hifi-capture", + .id = MC13783_ID_STEREO_CODEC, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = MC13783_RATES_RECORD, + .formats = MC13783_FORMATS, + }, + .ops = &mc13783_ops_codec, + }, +}; + +static const struct snd_soc_dai_ops mc13783_ops_sync = { + .hw_params = mc13783_pcm_hw_params_sync, + .set_fmt = mc13783_set_fmt_sync, + .set_sysclk = mc13783_set_sysclk_sync, + .set_tdm_slot = mc13783_set_tdm_slot_sync, +}; + +static struct snd_soc_dai_driver mc13783_dai_sync[] = { + { + .name = "mc13783-hifi", + .id = MC13783_ID_SYNC, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = MC13783_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = MC13783_RATES_RECORD, + .formats = MC13783_FORMATS, + }, + .ops = &mc13783_ops_sync, + .symmetric_rate = 1, + } +}; + +static const struct snd_soc_component_driver soc_component_dev_mc13783 = { + .probe = mc13783_probe, + .remove = mc13783_remove, + .controls = mc13783_control_list, + .num_controls = ARRAY_SIZE(mc13783_control_list), + .dapm_widgets = mc13783_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mc13783_dapm_widgets), + .dapm_routes = mc13783_routes, + .num_dapm_routes = ARRAY_SIZE(mc13783_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int __init mc13783_codec_probe(struct platform_device *pdev) +{ + struct mc13783_priv *priv; + struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data; + struct device_node *np; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (pdata) { + priv->adc_ssi_port = pdata->adc_ssi_port; + priv->dac_ssi_port = pdata->dac_ssi_port; + } else { + np = of_get_child_by_name(pdev->dev.parent->of_node, "codec"); + if (!np) + return -ENOSYS; + + ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port); + if (ret) { + of_node_put(np); + return ret; + } + + ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port); + if (ret) { + of_node_put(np); + return ret; + } + + of_node_put(np); + } + + dev_set_drvdata(&pdev->dev, priv); + priv->mc13xxx = dev_get_drvdata(pdev->dev.parent); + + if (priv->adc_ssi_port == priv->dac_ssi_port) + ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783, + mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync)); + else + ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783, + mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async)); + + return ret; +} + +static struct platform_driver mc13783_codec_driver = { + .driver = { + .name = "mc13783-codec", + }, +}; +module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe); + +MODULE_DESCRIPTION("ASoC MC13783 driver"); +MODULE_AUTHOR("Sascha Hauer, Pengutronix "); +MODULE_AUTHOR("Philippe Retornaz "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/mc13783.h b/sound/soc/codecs/mc13783.h new file mode 100644 index 0000000000..8992d3ab57 --- /dev/null +++ b/sound/soc/codecs/mc13783.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + */ + +#ifndef MC13783_MIXER_H +#define MC13783_MIXER_H + +#define MC13783_CLK_CLIA 1 +#define MC13783_CLK_CLIB 2 + +#define MC13783_ID_STEREO_DAC 1 +#define MC13783_ID_STEREO_CODEC 2 +#define MC13783_ID_SYNC 3 + +#endif /* MC13783_MIXER_H */ diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c new file mode 100644 index 0000000000..a45ef9d657 --- /dev/null +++ b/sound/soc/codecs/ml26124.c @@ -0,0 +1,592 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ml26124.h" + +#define DVOL_CTL_DVMUTE_ON BIT(4) /* Digital volume MUTE On */ +#define DVOL_CTL_DVMUTE_OFF 0 /* Digital volume MUTE Off */ +#define ML26124_SAI_NO_DELAY BIT(1) +#define ML26124_SAI_FRAME_SYNC (BIT(5) | BIT(0)) /* For mono (Telecodec) */ +#define ML26134_CACHESIZE 212 +#define ML26124_VMID BIT(1) +#define ML26124_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 |\ + SNDRV_PCM_RATE_48000) +#define ML26124_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) +#define ML26124_NUM_REGISTER ML26134_CACHESIZE + +struct ml26124_priv { + u32 mclk; + u32 rate; + struct regmap *regmap; + int clk_in; + struct snd_pcm_substream *substream; +}; + +struct clk_coeff { + u32 mclk; + u32 rate; + u8 pllnl; + u8 pllnh; + u8 pllml; + u8 pllmh; + u8 plldiv; +}; + +/* ML26124 configuration */ +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7150, 50, 0); + +static const DECLARE_TLV_DB_SCALE(alclvl, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(mingain, -1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(maxgain, -675, 600, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol, -1200, 75, 0); + +static const char * const ml26124_companding[] = {"16bit PCM", "u-law", + "A-law"}; + +static SOC_ENUM_SINGLE_DECL(ml26124_adc_companding_enum, + ML26124_SAI_TRANS_CTL, 6, ml26124_companding); + +static SOC_ENUM_SINGLE_DECL(ml26124_dac_companding_enum, + ML26124_SAI_RCV_CTL, 6, ml26124_companding); + +static const struct snd_kcontrol_new ml26124_snd_controls[] = { + SOC_SINGLE_TLV("Capture Digital Volume", ML26124_RECORD_DIG_VOL, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("Playback Digital Volume", ML26124_PLBAK_DIG_VOL, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("Digital Boost Volume", ML26124_DIGI_BOOST_VOL, 0, + 0x3f, 0, boost_vol), + SOC_SINGLE_TLV("EQ Band0 Volume", ML26124_EQ_GAIN_BRAND0, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("EQ Band1 Volume", ML26124_EQ_GAIN_BRAND1, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("EQ Band2 Volume", ML26124_EQ_GAIN_BRAND2, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("EQ Band3 Volume", ML26124_EQ_GAIN_BRAND3, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("EQ Band4 Volume", ML26124_EQ_GAIN_BRAND4, 0, + 0xff, 1, digital_tlv), + SOC_SINGLE_TLV("ALC Target Level", ML26124_ALC_TARGET_LEV, 0, + 0xf, 1, alclvl), + SOC_SINGLE_TLV("ALC Min Input Volume", ML26124_ALC_MAXMIN_GAIN, 0, + 7, 0, mingain), + SOC_SINGLE_TLV("ALC Max Input Volume", ML26124_ALC_MAXMIN_GAIN, 4, + 7, 1, maxgain), + SOC_SINGLE_TLV("Playback Limiter Min Input Volume", + ML26124_PL_MAXMIN_GAIN, 0, 7, 0, mingain), + SOC_SINGLE_TLV("Playback Limiter Max Input Volume", + ML26124_PL_MAXMIN_GAIN, 4, 7, 1, maxgain), + SOC_SINGLE_TLV("Playback Boost Volume", ML26124_PLYBAK_BOST_VOL, 0, + 0x3f, 0, boost_vol), + SOC_SINGLE("DC High Pass Filter Switch", ML26124_FILTER_EN, 0, 1, 0), + SOC_SINGLE("Noise High Pass Filter Switch", ML26124_FILTER_EN, 1, 1, 0), + SOC_SINGLE("ZC Switch", ML26124_PW_ZCCMP_PW_MNG, 1, + 1, 0), + SOC_SINGLE("EQ Band0 Switch", ML26124_FILTER_EN, 2, 1, 0), + SOC_SINGLE("EQ Band1 Switch", ML26124_FILTER_EN, 3, 1, 0), + SOC_SINGLE("EQ Band2 Switch", ML26124_FILTER_EN, 4, 1, 0), + SOC_SINGLE("EQ Band3 Switch", ML26124_FILTER_EN, 5, 1, 0), + SOC_SINGLE("EQ Band4 Switch", ML26124_FILTER_EN, 6, 1, 0), + SOC_SINGLE("Play Limiter", ML26124_DVOL_CTL, 0, 1, 0), + SOC_SINGLE("Capture Limiter", ML26124_DVOL_CTL, 1, 1, 0), + SOC_SINGLE("Digital Volume Fade Switch", ML26124_DVOL_CTL, 3, 1, 0), + SOC_SINGLE("Digital Switch", ML26124_DVOL_CTL, 4, 1, 0), + SOC_ENUM("DAC Companding", ml26124_dac_companding_enum), + SOC_ENUM("ADC Companding", ml26124_adc_companding_enum), +}; + +static const struct snd_kcontrol_new ml26124_output_mixer_controls[] = { + SOC_DAPM_SINGLE("DAC Switch", ML26124_SPK_AMP_OUT, 1, 1, 0), + SOC_DAPM_SINGLE("Line in loopback Switch", ML26124_SPK_AMP_OUT, 3, 1, + 0), + SOC_DAPM_SINGLE("PGA Switch", ML26124_SPK_AMP_OUT, 5, 1, 0), +}; + +/* Input mux */ +static const char * const ml26124_input_select[] = {"Analog MIC SingleEnded in", + "Digital MIC in", "Analog MIC Differential in"}; + +static SOC_ENUM_SINGLE_DECL(ml26124_insel_enum, + ML26124_MIC_IF_CTL, 0, ml26124_input_select); + +static const struct snd_kcontrol_new ml26124_input_mux_controls = + SOC_DAPM_ENUM("Input Select", ml26124_insel_enum); + +static const struct snd_kcontrol_new ml26124_line_control = + SOC_DAPM_SINGLE("Switch", ML26124_PW_LOUT_PW_MNG, 1, 1, 0); + +static const struct snd_soc_dapm_widget ml26124_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("MCLKEN", ML26124_CLK_EN, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLLEN", ML26124_CLK_EN, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLLOE", ML26124_CLK_EN, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS", ML26124_PW_REF_PW_MNG, 2, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, + &ml26124_output_mixer_controls[0], + ARRAY_SIZE(ml26124_output_mixer_controls)), + SND_SOC_DAPM_DAC("DAC", "Playback", ML26124_PW_DAC_PW_MNG, 1, 0), + SND_SOC_DAPM_ADC("ADC", "Capture", ML26124_PW_IN_PW_MNG, 1, 0), + SND_SOC_DAPM_PGA("PGA", ML26124_PW_IN_PW_MNG, 3, 0, NULL, 0), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, + &ml26124_input_mux_controls), + SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0, + &ml26124_line_control), + SND_SOC_DAPM_INPUT("MDIN"), + SND_SOC_DAPM_INPUT("MIN"), + SND_SOC_DAPM_INPUT("LIN"), + SND_SOC_DAPM_OUTPUT("SPOUT"), + SND_SOC_DAPM_OUTPUT("LOUT"), +}; + +static const struct snd_soc_dapm_route ml26124_intercon[] = { + /* Supply */ + {"DAC", NULL, "MCLKEN"}, + {"ADC", NULL, "MCLKEN"}, + {"DAC", NULL, "PLLEN"}, + {"ADC", NULL, "PLLEN"}, + {"DAC", NULL, "PLLOE"}, + {"ADC", NULL, "PLLOE"}, + + /* output mixer */ + {"Output Mixer", "DAC Switch", "DAC"}, + {"Output Mixer", "Line in loopback Switch", "LIN"}, + + /* outputs */ + {"LOUT", NULL, "Output Mixer"}, + {"SPOUT", NULL, "Output Mixer"}, + {"Line Out Enable", NULL, "LOUT"}, + + /* input */ + {"ADC", NULL, "Input Mux"}, + {"Input Mux", "Analog MIC SingleEnded in", "PGA"}, + {"Input Mux", "Analog MIC Differential in", "PGA"}, + {"PGA", NULL, "MIN"}, +}; + +/* PLLOutputFreq(Hz) = InputMclkFreq(Hz) * PLLM / (PLLN * PLLDIV) */ +static const struct clk_coeff coeff_div[] = { + {12288000, 16000, 0xc, 0x0, 0x20, 0x0, 0x4}, + {12288000, 32000, 0xc, 0x0, 0x20, 0x0, 0x4}, + {12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4}, +}; + +static const struct reg_default ml26124_reg[] = { + /* CLOCK control Register */ + {0x00, 0x00 }, /* Sampling Rate */ + {0x02, 0x00}, /* PLL NL */ + {0x04, 0x00}, /* PLLNH */ + {0x06, 0x00}, /* PLLML */ + {0x08, 0x00}, /* MLLMH */ + {0x0a, 0x00}, /* PLLDIV */ + {0x0c, 0x00}, /* Clock Enable */ + {0x0e, 0x00}, /* CLK Input/Output Control */ + + /* System Control Register */ + {0x10, 0x00}, /* Software RESET */ + {0x12, 0x00}, /* Record/Playback Run */ + {0x14, 0x00}, /* Mic Input/Output control */ + + /* Power Management Register */ + {0x20, 0x00}, /* Reference Power Management */ + {0x22, 0x00}, /* Input Power Management */ + {0x24, 0x00}, /* DAC Power Management */ + {0x26, 0x00}, /* SP-AMP Power Management */ + {0x28, 0x00}, /* LINEOUT Power Management */ + {0x2a, 0x00}, /* VIDEO Power Management */ + {0x2e, 0x00}, /* AC-CMP Power Management */ + + /* Analog reference Control Register */ + {0x30, 0x04}, /* MICBIAS Voltage Control */ + + /* Input/Output Amplifier Control Register */ + {0x32, 0x10}, /* MIC Input Volume */ + {0x38, 0x00}, /* Mic Boost Volume */ + {0x3a, 0x33}, /* Speaker AMP Volume */ + {0x48, 0x00}, /* AMP Volume Control Function Enable */ + {0x4a, 0x00}, /* Amplifier Volume Fader Control */ + + /* Analog Path Control Register */ + {0x54, 0x00}, /* Speaker AMP Output Control */ + {0x5a, 0x00}, /* Mic IF Control */ + {0xe8, 0x01}, /* Mic Select Control */ + + /* Audio Interface Control Register */ + {0x60, 0x00}, /* SAI-Trans Control */ + {0x62, 0x00}, /* SAI-Receive Control */ + {0x64, 0x00}, /* SAI Mode select */ + + /* DSP Control Register */ + {0x66, 0x01}, /* Filter Func Enable */ + {0x68, 0x00}, /* Volume Control Func Enable */ + {0x6A, 0x00}, /* Mixer & Volume Control*/ + {0x6C, 0xff}, /* Record Digital Volume */ + {0x70, 0xff}, /* Playback Digital Volume */ + {0x72, 0x10}, /* Digital Boost Volume */ + {0x74, 0xe7}, /* EQ gain Band0 */ + {0x76, 0xe7}, /* EQ gain Band1 */ + {0x78, 0xe7}, /* EQ gain Band2 */ + {0x7A, 0xe7}, /* EQ gain Band3 */ + {0x7C, 0xe7}, /* EQ gain Band4 */ + {0x7E, 0x00}, /* HPF2 CutOff*/ + {0x80, 0x00}, /* EQ Band0 Coef0L */ + {0x82, 0x00}, /* EQ Band0 Coef0H */ + {0x84, 0x00}, /* EQ Band0 Coef0L */ + {0x86, 0x00}, /* EQ Band0 Coef0H */ + {0x88, 0x00}, /* EQ Band1 Coef0L */ + {0x8A, 0x00}, /* EQ Band1 Coef0H */ + {0x8C, 0x00}, /* EQ Band1 Coef0L */ + {0x8E, 0x00}, /* EQ Band1 Coef0H */ + {0x90, 0x00}, /* EQ Band2 Coef0L */ + {0x92, 0x00}, /* EQ Band2 Coef0H */ + {0x94, 0x00}, /* EQ Band2 Coef0L */ + {0x96, 0x00}, /* EQ Band2 Coef0H */ + {0x98, 0x00}, /* EQ Band3 Coef0L */ + {0x9A, 0x00}, /* EQ Band3 Coef0H */ + {0x9C, 0x00}, /* EQ Band3 Coef0L */ + {0x9E, 0x00}, /* EQ Band3 Coef0H */ + {0xA0, 0x00}, /* EQ Band4 Coef0L */ + {0xA2, 0x00}, /* EQ Band4 Coef0H */ + {0xA4, 0x00}, /* EQ Band4 Coef0L */ + {0xA6, 0x00}, /* EQ Band4 Coef0H */ + + /* ALC Control Register */ + {0xb0, 0x00}, /* ALC Mode */ + {0xb2, 0x02}, /* ALC Attack Time */ + {0xb4, 0x03}, /* ALC Decay Time */ + {0xb6, 0x00}, /* ALC Hold Time */ + {0xb8, 0x0b}, /* ALC Target Level */ + {0xba, 0x70}, /* ALC Max/Min Gain */ + {0xbc, 0x00}, /* Noise Gate Threshold */ + {0xbe, 0x00}, /* ALC ZeroCross TimeOut */ + + /* Playback Limiter Control Register */ + {0xc0, 0x04}, /* PL Attack Time */ + {0xc2, 0x05}, /* PL Decay Time */ + {0xc4, 0x0d}, /* PL Target Level */ + {0xc6, 0x70}, /* PL Max/Min Gain */ + {0xc8, 0x10}, /* Playback Boost Volume */ + {0xca, 0x00}, /* PL ZeroCross TimeOut */ + + /* Video Amplifier Control Register */ + {0xd0, 0x01}, /* VIDEO AMP Gain Control */ + {0xd2, 0x01}, /* VIDEO AMP Setup 1 */ + {0xd4, 0x01}, /* VIDEO AMP Control2 */ +}; + +/* Get sampling rate value of sampling rate setting register (0x0) */ +static inline int get_srate(int rate) +{ + int srate; + + switch (rate) { + case 16000: + srate = 3; + break; + case 32000: + srate = 6; + break; + case 48000: + srate = 8; + break; + default: + return -EINVAL; + } + return srate; +} + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return -EINVAL; +} + +static int ml26124_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ml26124_priv *priv = snd_soc_component_get_drvdata(component); + int i = get_coeff(priv->mclk, params_rate(hw_params)); + int srate; + + if (i < 0) + return i; + priv->substream = substream; + priv->rate = params_rate(hw_params); + + if (priv->clk_in) { + switch (priv->mclk / params_rate(hw_params)) { + case 256: + snd_soc_component_update_bits(component, ML26124_CLK_CTL, + BIT(0) | BIT(1), 1); + break; + case 512: + snd_soc_component_update_bits(component, ML26124_CLK_CTL, + BIT(0) | BIT(1), 2); + break; + case 1024: + snd_soc_component_update_bits(component, ML26124_CLK_CTL, + BIT(0) | BIT(1), 3); + break; + default: + dev_err(component->dev, "Unsupported MCLKI\n"); + break; + } + } else { + snd_soc_component_update_bits(component, ML26124_CLK_CTL, + BIT(0) | BIT(1), 0); + } + + srate = get_srate(params_rate(hw_params)); + if (srate < 0) + return srate; + + snd_soc_component_update_bits(component, ML26124_SMPLING_RATE, 0xf, srate); + snd_soc_component_update_bits(component, ML26124_PLLNL, 0xff, coeff_div[i].pllnl); + snd_soc_component_update_bits(component, ML26124_PLLNH, 0x1, coeff_div[i].pllnh); + snd_soc_component_update_bits(component, ML26124_PLLML, 0xff, coeff_div[i].pllml); + snd_soc_component_update_bits(component, ML26124_PLLMH, 0x3f, coeff_div[i].pllmh); + snd_soc_component_update_bits(component, ML26124_PLLDIV, 0x1f, coeff_div[i].plldiv); + + return 0; +} + +static int ml26124_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct ml26124_priv *priv = snd_soc_component_get_drvdata(component); + + switch (priv->substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + snd_soc_component_update_bits(component, ML26124_REC_PLYBAK_RUN, BIT(0), 1); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + snd_soc_component_update_bits(component, ML26124_REC_PLYBAK_RUN, BIT(1), 2); + break; + } + + if (mute) + snd_soc_component_update_bits(component, ML26124_DVOL_CTL, BIT(4), + DVOL_CTL_DVMUTE_ON); + else + snd_soc_component_update_bits(component, ML26124_DVOL_CTL, BIT(4), + DVOL_CTL_DVMUTE_OFF); + + return 0; +} + +static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + unsigned char mode; + struct snd_soc_component *component = codec_dai->component; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + mode = 1; + break; + case SND_SOC_DAIFMT_CBC_CFC: + mode = 0; + break; + default: + return -EINVAL; + } + snd_soc_component_update_bits(component, ML26124_SAI_MODE_SEL, BIT(0), mode); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct ml26124_priv *priv = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case ML26124_USE_PLLOUT: + priv->clk_in = ML26124_USE_PLLOUT; + break; + case ML26124_USE_MCLKI: + priv->clk_in = ML26124_USE_MCLKI; + break; + default: + return -EINVAL; + } + + priv->mclk = freq; + + return 0; +} + +static int ml26124_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct ml26124_priv *priv = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_update_bits(component, ML26124_PW_SPAMP_PW_MNG, + ML26124_R26_MASK, ML26124_BLT_PREAMP_ON); + msleep(100); + snd_soc_component_update_bits(component, ML26124_PW_SPAMP_PW_MNG, + ML26124_R26_MASK, + ML26124_MICBEN_ON | ML26124_BLT_ALL_ON); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* VMID ON */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + snd_soc_component_update_bits(component, ML26124_PW_REF_PW_MNG, + ML26124_VMID, ML26124_VMID); + msleep(500); + regcache_sync(priv->regmap); + } + break; + case SND_SOC_BIAS_OFF: + /* VMID OFF */ + snd_soc_component_update_bits(component, ML26124_PW_REF_PW_MNG, + ML26124_VMID, 0); + break; + } + return 0; +} + +static const struct snd_soc_dai_ops ml26124_dai_ops = { + .hw_params = ml26124_hw_params, + .mute_stream = ml26124_mute, + .set_fmt = ml26124_set_dai_fmt, + .set_sysclk = ml26124_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver ml26124_dai = { + .name = "ml26124-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = ML26124_RATES, + .formats = ML26124_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = ML26124_RATES, + .formats = ML26124_FORMATS,}, + .ops = &ml26124_dai_ops, + .symmetric_rate = 1, +}; + +static int ml26124_probe(struct snd_soc_component *component) +{ + /* Software Reset */ + snd_soc_component_update_bits(component, ML26124_SW_RST, 0x01, 1); + snd_soc_component_update_bits(component, ML26124_SW_RST, 0x01, 0); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_ml26124 = { + .probe = ml26124_probe, + .set_bias_level = ml26124_set_bias_level, + .controls = ml26124_snd_controls, + .num_controls = ARRAY_SIZE(ml26124_snd_controls), + .dapm_widgets = ml26124_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets), + .dapm_routes = ml26124_intercon, + .num_dapm_routes = ARRAY_SIZE(ml26124_intercon), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config ml26124_i2c_regmap = { + .val_bits = 8, + .reg_bits = 8, + .max_register = ML26124_NUM_REGISTER, + .reg_defaults = ml26124_reg, + .num_reg_defaults = ARRAY_SIZE(ml26124_reg), + .cache_type = REGCACHE_RBTREE, + .write_flag_mask = 0x01, +}; + +static int ml26124_i2c_probe(struct i2c_client *i2c) +{ + struct ml26124_priv *priv; + int ret; + + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_clientdata(i2c, priv); + + priv->regmap = devm_regmap_init_i2c(i2c, &ml26124_i2c_regmap); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret); + return ret; + } + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_ml26124, &ml26124_dai, 1); +} + +static const struct i2c_device_id ml26124_i2c_id[] = { + { "ml26124", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id); + +static struct i2c_driver ml26124_i2c_driver = { + .driver = { + .name = "ml26124", + }, + .probe = ml26124_i2c_probe, + .id_table = ml26124_i2c_id, +}; + +module_i2c_driver(ml26124_i2c_driver); + +MODULE_AUTHOR("Tomoya MORINAGA "); +MODULE_DESCRIPTION("LAPIS Semiconductor ML26124 ALSA SoC codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ml26124.h b/sound/soc/codecs/ml26124.h new file mode 100644 index 0000000000..080a6232f4 --- /dev/null +++ b/sound/soc/codecs/ml26124.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. + */ + +#ifndef ML26124_H +#define ML26124_H + +/* Clock Control Register */ +#define ML26124_SMPLING_RATE 0x00 +#define ML26124_PLLNL 0x02 +#define ML26124_PLLNH 0x04 +#define ML26124_PLLML 0x06 +#define ML26124_PLLMH 0x08 +#define ML26124_PLLDIV 0x0a +#define ML26124_CLK_EN 0x0c +#define ML26124_CLK_CTL 0x0e + +/* System Control Register */ +#define ML26124_SW_RST 0x10 +#define ML26124_REC_PLYBAK_RUN 0x12 +#define ML26124_MIC_TIM 0x14 + +/* Power Mnagement Register */ +#define ML26124_PW_REF_PW_MNG 0x20 +#define ML26124_PW_IN_PW_MNG 0x22 +#define ML26124_PW_DAC_PW_MNG 0x24 +#define ML26124_PW_SPAMP_PW_MNG 0x26 +#define ML26124_PW_LOUT_PW_MNG 0x28 +#define ML26124_PW_VOUT_PW_MNG 0x2a +#define ML26124_PW_ZCCMP_PW_MNG 0x2e + +/* Analog Reference Control Register */ +#define ML26124_PW_MICBIAS_VOL 0x30 + +/* Input/Output Amplifier Control Register */ +#define ML26124_PW_MIC_IN_VOL 0x32 +#define ML26124_PW_MIC_BOST_VOL 0x38 +#define ML26124_PW_SPK_AMP_VOL 0x3a +#define ML26124_PW_AMP_VOL_FUNC 0x48 +#define ML26124_PW_AMP_VOL_FADE 0x4a + +/* Analog Path Control Register */ +#define ML26124_SPK_AMP_OUT 0x54 +#define ML26124_MIC_IF_CTL 0x5a +#define ML26124_MIC_SELECT 0xe8 + +/* Audio Interface Control Register */ +#define ML26124_SAI_TRANS_CTL 0x60 +#define ML26124_SAI_RCV_CTL 0x62 +#define ML26124_SAI_MODE_SEL 0x64 + +/* DSP Control Register */ +#define ML26124_FILTER_EN 0x66 +#define ML26124_DVOL_CTL 0x68 +#define ML26124_MIXER_VOL_CTL 0x6a +#define ML26124_RECORD_DIG_VOL 0x6c +#define ML26124_PLBAK_DIG_VOL 0x70 +#define ML26124_DIGI_BOOST_VOL 0x72 +#define ML26124_EQ_GAIN_BRAND0 0x74 +#define ML26124_EQ_GAIN_BRAND1 0x76 +#define ML26124_EQ_GAIN_BRAND2 0x78 +#define ML26124_EQ_GAIN_BRAND3 0x7a +#define ML26124_EQ_GAIN_BRAND4 0x7c +#define ML26124_HPF2_CUTOFF 0x7e +#define ML26124_EQBRAND0_F0L 0x80 +#define ML26124_EQBRAND0_F0H 0x82 +#define ML26124_EQBRAND0_F1L 0x84 +#define ML26124_EQBRAND0_F1H 0x86 +#define ML26124_EQBRAND1_F0L 0x88 +#define ML26124_EQBRAND1_F0H 0x8a +#define ML26124_EQBRAND1_F1L 0x8c +#define ML26124_EQBRAND1_F1H 0x8e +#define ML26124_EQBRAND2_F0L 0x90 +#define ML26124_EQBRAND2_F0H 0x92 +#define ML26124_EQBRAND2_F1L 0x94 +#define ML26124_EQBRAND2_F1H 0x96 +#define ML26124_EQBRAND3_F0L 0x98 +#define ML26124_EQBRAND3_F0H 0x9a +#define ML26124_EQBRAND3_F1L 0x9c +#define ML26124_EQBRAND3_F1H 0x9e +#define ML26124_EQBRAND4_F0L 0xa0 +#define ML26124_EQBRAND4_F0H 0xa2 +#define ML26124_EQBRAND4_F1L 0xa4 +#define ML26124_EQBRAND4_F1H 0xa6 + +/* ALC Control Register */ +#define ML26124_ALC_MODE 0xb0 +#define ML26124_ALC_ATTACK_TIM 0xb2 +#define ML26124_ALC_DECAY_TIM 0xb4 +#define ML26124_ALC_HOLD_TIM 0xb6 +#define ML26124_ALC_TARGET_LEV 0xb8 +#define ML26124_ALC_MAXMIN_GAIN 0xba +#define ML26124_NOIS_GATE_THRSH 0xbc +#define ML26124_ALC_ZERO_TIMOUT 0xbe + +/* Playback Limiter Control Register */ +#define ML26124_PL_ATTACKTIME 0xc0 +#define ML26124_PL_DECAYTIME 0xc2 +#define ML26124_PL_TARGETTIME 0xc4 +#define ML26124_PL_MAXMIN_GAIN 0xc6 +#define ML26124_PLYBAK_BOST_VOL 0xc8 +#define ML26124_PL_0CROSS_TIMOUT 0xca + +/* Video Amplifer Control Register */ +#define ML26124_VIDEO_AMP_GAIN_CTL 0xd0 +#define ML26124_VIDEO_AMP_SETUP1 0xd2 +#define ML26124_VIDEO_AMP_CTL2 0xd4 + +/* Clock select for machine driver */ +#define ML26124_USE_PLL 0 +#define ML26124_USE_MCLKI_256FS 1 +#define ML26124_USE_MCLKI_512FS 2 +#define ML26124_USE_MCLKI_1024FS 3 + +/* Register Mask */ +#define ML26124_R0_MASK 0xf +#define ML26124_R2_MASK 0xff +#define ML26124_R4_MASK 0x1 +#define ML26124_R6_MASK 0xf +#define ML26124_R8_MASK 0x3f +#define ML26124_Ra_MASK 0x1f +#define ML26124_Rc_MASK 0x1f +#define ML26124_Re_MASK 0x7 +#define ML26124_R10_MASK 0x1 +#define ML26124_R12_MASK 0x17 +#define ML26124_R14_MASK 0x3f +#define ML26124_R20_MASK 0x47 +#define ML26124_R22_MASK 0xa +#define ML26124_R24_MASK 0x2 +#define ML26124_R26_MASK 0x1f +#define ML26124_R28_MASK 0x2 +#define ML26124_R2a_MASK 0x2 +#define ML26124_R2e_MASK 0x2 +#define ML26124_R30_MASK 0x7 +#define ML26124_R32_MASK 0x3f +#define ML26124_R38_MASK 0x38 +#define ML26124_R3a_MASK 0x3f +#define ML26124_R48_MASK 0x3 +#define ML26124_R4a_MASK 0x7 +#define ML26124_R54_MASK 0x2a +#define ML26124_R5a_MASK 0x3 +#define ML26124_Re8_MASK 0x3 +#define ML26124_R60_MASK 0xff +#define ML26124_R62_MASK 0xff +#define ML26124_R64_MASK 0x1 +#define ML26124_R66_MASK 0xff +#define ML26124_R68_MASK 0x3b +#define ML26124_R6a_MASK 0xf3 +#define ML26124_R6c_MASK 0xff +#define ML26124_R70_MASK 0xff + +#define ML26124_MCLKEN BIT(0) +#define ML26124_PLLEN BIT(1) +#define ML26124_PLLOE BIT(2) +#define ML26124_MCLKOE BIT(3) + +#define ML26124_BLT_ALL_ON 0x1f +#define ML26124_BLT_PREAMP_ON 0x13 + +#define ML26124_MICBEN_ON BIT(2) + +enum ml26124_regs { + ML26124_MCLK = 0, +}; + +enum ml26124_clk_in { + ML26124_USE_PLLOUT = 0, + ML26124_USE_MCLKI, +}; + +#endif diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c new file mode 100644 index 0000000000..9ca3818129 --- /dev/null +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -0,0 +1,1280 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CDC_D_REVISION1 (0xf000) +#define CDC_D_PERPH_SUBTYPE (0xf005) +#define CDC_D_INT_EN_SET (0xf015) +#define CDC_D_INT_EN_CLR (0xf016) +#define MBHC_SWITCH_INT BIT(7) +#define MBHC_MIC_ELECTRICAL_INS_REM_DET BIT(6) +#define MBHC_BUTTON_PRESS_DET BIT(5) +#define MBHC_BUTTON_RELEASE_DET BIT(4) +#define CDC_D_CDC_RST_CTL (0xf046) +#define RST_CTL_DIG_SW_RST_N_MASK BIT(7) +#define RST_CTL_DIG_SW_RST_N_RESET 0 +#define RST_CTL_DIG_SW_RST_N_REMOVE_RESET BIT(7) + +#define CDC_D_CDC_TOP_CLK_CTL (0xf048) +#define TOP_CLK_CTL_A_MCLK_MCLK2_EN_MASK (BIT(2) | BIT(3)) +#define TOP_CLK_CTL_A_MCLK_EN_ENABLE BIT(2) +#define TOP_CLK_CTL_A_MCLK2_EN_ENABLE BIT(3) + +#define CDC_D_CDC_ANA_CLK_CTL (0xf049) +#define ANA_CLK_CTL_EAR_HPHR_CLK_EN_MASK BIT(0) +#define ANA_CLK_CTL_EAR_HPHR_CLK_EN BIT(0) +#define ANA_CLK_CTL_EAR_HPHL_CLK_EN BIT(1) +#define ANA_CLK_CTL_SPKR_CLK_EN_MASK BIT(4) +#define ANA_CLK_CTL_SPKR_CLK_EN BIT(4) +#define ANA_CLK_CTL_TXA_CLK25_EN BIT(5) + +#define CDC_D_CDC_DIG_CLK_CTL (0xf04A) +#define DIG_CLK_CTL_RXD1_CLK_EN BIT(0) +#define DIG_CLK_CTL_RXD2_CLK_EN BIT(1) +#define DIG_CLK_CTL_RXD3_CLK_EN BIT(2) +#define DIG_CLK_CTL_D_MBHC_CLK_EN_MASK BIT(3) +#define DIG_CLK_CTL_D_MBHC_CLK_EN BIT(3) +#define DIG_CLK_CTL_TXD_CLK_EN BIT(4) +#define DIG_CLK_CTL_NCP_CLK_EN_MASK BIT(6) +#define DIG_CLK_CTL_NCP_CLK_EN BIT(6) +#define DIG_CLK_CTL_RXD_PDM_CLK_EN_MASK BIT(7) +#define DIG_CLK_CTL_RXD_PDM_CLK_EN BIT(7) + +#define CDC_D_CDC_CONN_TX1_CTL (0xf050) +#define CONN_TX1_SERIAL_TX1_MUX GENMASK(1, 0) +#define CONN_TX1_SERIAL_TX1_ADC_1 0x0 +#define CONN_TX1_SERIAL_TX1_RX_PDM_LB 0x1 +#define CONN_TX1_SERIAL_TX1_ZERO 0x2 + +#define CDC_D_CDC_CONN_TX2_CTL (0xf051) +#define CONN_TX2_SERIAL_TX2_MUX GENMASK(1, 0) +#define CONN_TX2_SERIAL_TX2_ADC_2 0x0 +#define CONN_TX2_SERIAL_TX2_RX_PDM_LB 0x1 +#define CONN_TX2_SERIAL_TX2_ZERO 0x2 +#define CDC_D_CDC_CONN_HPHR_DAC_CTL (0xf052) +#define CDC_D_CDC_CONN_RX1_CTL (0xf053) +#define CDC_D_CDC_CONN_RX2_CTL (0xf054) +#define CDC_D_CDC_CONN_RX3_CTL (0xf055) +#define CDC_D_CDC_CONN_RX_LB_CTL (0xf056) +#define CDC_D_SEC_ACCESS (0xf0D0) +#define CDC_D_PERPH_RESET_CTL3 (0xf0DA) +#define CDC_D_PERPH_RESET_CTL4 (0xf0DB) +#define CDC_A_REVISION1 (0xf100) +#define CDC_A_REVISION2 (0xf101) +#define CDC_A_REVISION3 (0xf102) +#define CDC_A_REVISION4 (0xf103) +#define CDC_A_PERPH_TYPE (0xf104) +#define CDC_A_PERPH_SUBTYPE (0xf105) +#define CDC_A_INT_RT_STS (0xf110) +#define CDC_A_INT_SET_TYPE (0xf111) +#define CDC_A_INT_POLARITY_HIGH (0xf112) +#define CDC_A_INT_POLARITY_LOW (0xf113) +#define CDC_A_INT_LATCHED_CLR (0xf114) +#define CDC_A_INT_EN_SET (0xf115) +#define CDC_A_INT_EN_CLR (0xf116) +#define CDC_A_INT_LATCHED_STS (0xf118) +#define CDC_A_INT_PENDING_STS (0xf119) +#define CDC_A_INT_MID_SEL (0xf11A) +#define CDC_A_INT_PRIORITY (0xf11B) +#define CDC_A_MICB_1_EN (0xf140) +#define MICB_1_EN_MICB_ENABLE BIT(7) +#define MICB_1_EN_BYP_CAP_MASK BIT(6) +#define MICB_1_EN_NO_EXT_BYP_CAP BIT(6) +#define MICB_1_EN_EXT_BYP_CAP 0 +#define MICB_1_EN_PULL_DOWN_EN_MASK BIT(5) +#define MICB_1_EN_PULL_DOWN_EN_ENABLE BIT(5) +#define MICB_1_EN_OPA_STG2_TAIL_CURR_MASK GENMASK(3, 1) +#define MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA (0x4) +#define MICB_1_EN_PULL_UP_EN_MASK BIT(4) +#define MICB_1_EN_TX3_GND_SEL_MASK BIT(0) +#define MICB_1_EN_TX3_GND_SEL_TX_GND 0 + +#define CDC_A_MICB_1_VAL (0xf141) +#define MICB_MIN_VAL 1600 +#define MICB_STEP_SIZE 50 +#define MICB_VOLTAGE_REGVAL(v) (((v - MICB_MIN_VAL)/MICB_STEP_SIZE) << 3) +#define MICB_1_VAL_MICB_OUT_VAL_MASK GENMASK(7, 3) +#define MICB_1_VAL_MICB_OUT_VAL_V2P70V ((0x16) << 3) +#define MICB_1_VAL_MICB_OUT_VAL_V1P80V ((0x4) << 3) +#define CDC_A_MICB_1_CTL (0xf142) + +#define MICB_1_CTL_CFILT_REF_SEL_MASK BIT(1) +#define MICB_1_CTL_CFILT_REF_SEL_HPF_REF BIT(1) +#define MICB_1_CTL_EXT_PRECHARG_EN_MASK BIT(5) +#define MICB_1_CTL_EXT_PRECHARG_EN_ENABLE BIT(5) +#define MICB_1_CTL_INT_PRECHARG_BYP_MASK BIT(6) +#define MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL BIT(6) + +#define CDC_A_MICB_1_INT_RBIAS (0xf143) +#define MICB_1_INT_TX1_INT_RBIAS_EN_MASK BIT(7) +#define MICB_1_INT_TX1_INT_RBIAS_EN_ENABLE BIT(7) +#define MICB_1_INT_TX1_INT_RBIAS_EN_DISABLE 0 + +#define MICB_1_INT_TX1_INT_PULLUP_EN_MASK BIT(6) +#define MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(6) +#define MICB_1_INT_TX1_INT_PULLUP_EN_TX1N_TO_GND 0 + +#define MICB_1_INT_TX2_INT_RBIAS_EN_MASK BIT(4) +#define MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE BIT(4) +#define MICB_1_INT_TX2_INT_RBIAS_EN_DISABLE 0 +#define MICB_1_INT_TX2_INT_PULLUP_EN_MASK BIT(3) +#define MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(3) +#define MICB_1_INT_TX2_INT_PULLUP_EN_TX1N_TO_GND 0 + +#define MICB_1_INT_TX3_INT_RBIAS_EN_MASK BIT(1) +#define MICB_1_INT_TX3_INT_RBIAS_EN_ENABLE BIT(1) +#define MICB_1_INT_TX3_INT_RBIAS_EN_DISABLE 0 +#define MICB_1_INT_TX3_INT_PULLUP_EN_MASK BIT(0) +#define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_MICBIAS BIT(0) +#define MICB_1_INT_TX3_INT_PULLUP_EN_TX1N_TO_GND 0 + +#define CDC_A_MICB_2_EN (0xf144) +#define CDC_A_MICB_2_EN_ENABLE BIT(7) +#define CDC_A_MICB_2_PULL_DOWN_EN_MASK BIT(5) +#define CDC_A_MICB_2_PULL_DOWN_EN BIT(5) +#define CDC_A_TX_1_2_ATEST_CTL_2 (0xf145) +#define CDC_A_MASTER_BIAS_CTL (0xf146) +#define CDC_A_MBHC_DET_CTL_1 (0xf147) +#define CDC_A_MBHC_DET_CTL_L_DET_EN BIT(7) +#define CDC_A_MBHC_DET_CTL_GND_DET_EN BIT(6) +#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION BIT(5) +#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_REMOVAL (0) +#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK BIT(5) +#define CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT (5) +#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO BIT(4) +#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MANUAL BIT(3) +#define CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_MASK GENMASK(4, 3) +#define CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN BIT(2) +#define CDC_A_MBHC_DET_CTL_2 (0xf150) +#define CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 (BIT(7) | BIT(6)) +#define CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD BIT(5) +#define CDC_A_PLUG_TYPE_MASK GENMASK(4, 3) +#define CDC_A_HPHL_PLUG_TYPE_NO BIT(4) +#define CDC_A_GND_PLUG_TYPE_NO BIT(3) +#define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN_MASK BIT(0) +#define CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN BIT(0) +#define CDC_A_MBHC_FSM_CTL (0xf151) +#define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN BIT(7) +#define CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK BIT(7) +#define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA (0x3 << 4) +#define CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK GENMASK(6, 4) +#define CDC_A_MBHC_DBNC_TIMER (0xf152) +#define CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS BIT(3) +#define CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS (0x9 << 4) +#define CDC_A_MBHC_BTN0_ZDET_CTL_0 (0xf153) +#define CDC_A_MBHC_BTN1_ZDET_CTL_1 (0xf154) +#define CDC_A_MBHC_BTN2_ZDET_CTL_2 (0xf155) +#define CDC_A_MBHC_BTN3_CTL (0xf156) +#define CDC_A_MBHC_BTN4_CTL (0xf157) +#define CDC_A_MBHC_BTN_VREF_FINE_SHIFT (2) +#define CDC_A_MBHC_BTN_VREF_FINE_MASK GENMASK(4, 2) +#define CDC_A_MBHC_BTN_VREF_COARSE_MASK GENMASK(7, 5) +#define CDC_A_MBHC_BTN_VREF_COARSE_SHIFT (5) +#define CDC_A_MBHC_BTN_VREF_MASK (CDC_A_MBHC_BTN_VREF_COARSE_MASK | \ + CDC_A_MBHC_BTN_VREF_FINE_MASK) +#define CDC_A_MBHC_RESULT_1 (0xf158) +#define CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK GENMASK(4, 0) +#define CDC_A_TX_1_EN (0xf160) +#define CDC_A_TX_2_EN (0xf161) +#define CDC_A_TX_1_2_TEST_CTL_1 (0xf162) +#define CDC_A_TX_1_2_TEST_CTL_2 (0xf163) +#define CDC_A_TX_1_2_ATEST_CTL (0xf164) +#define CDC_A_TX_1_2_OPAMP_BIAS (0xf165) +#define CDC_A_TX_3_EN (0xf167) +#define CDC_A_NCP_EN (0xf180) +#define CDC_A_NCP_CLK (0xf181) +#define CDC_A_NCP_FBCTRL (0xf183) +#define CDC_A_NCP_FBCTRL_FB_CLK_INV_MASK BIT(5) +#define CDC_A_NCP_FBCTRL_FB_CLK_INV BIT(5) +#define CDC_A_NCP_BIAS (0xf184) +#define CDC_A_NCP_VCTRL (0xf185) +#define CDC_A_NCP_TEST (0xf186) +#define CDC_A_NCP_CLIM_ADDR (0xf187) +#define CDC_A_RX_CLOCK_DIVIDER (0xf190) +#define CDC_A_RX_COM_OCP_CTL (0xf191) +#define CDC_A_RX_COM_OCP_COUNT (0xf192) +#define CDC_A_RX_COM_BIAS_DAC (0xf193) +#define RX_COM_BIAS_DAC_RX_BIAS_EN_MASK BIT(7) +#define RX_COM_BIAS_DAC_RX_BIAS_EN_ENABLE BIT(7) +#define RX_COM_BIAS_DAC_DAC_REF_EN_MASK BIT(0) +#define RX_COM_BIAS_DAC_DAC_REF_EN_ENABLE BIT(0) + +#define CDC_A_RX_HPH_BIAS_PA (0xf194) +#define CDC_A_RX_HPH_BIAS_LDO_OCP (0xf195) +#define CDC_A_RX_HPH_BIAS_CNP (0xf196) +#define CDC_A_RX_HPH_CNP_EN (0xf197) +#define CDC_A_RX_HPH_L_PA_DAC_CTL (0xf19B) +#define RX_HPA_L_PA_DAC_CTL_DATA_RESET_MASK BIT(1) +#define RX_HPA_L_PA_DAC_CTL_DATA_RESET_RESET BIT(1) +#define CDC_A_RX_HPH_R_PA_DAC_CTL (0xf19D) +#define RX_HPH_R_PA_DAC_CTL_DATA_RESET BIT(1) +#define RX_HPH_R_PA_DAC_CTL_DATA_RESET_MASK BIT(1) + +#define CDC_A_RX_EAR_CTL (0xf19E) +#define RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK BIT(0) +#define RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE BIT(0) +#define RX_EAR_CTL_PA_EAR_PA_EN_MASK BIT(6) +#define RX_EAR_CTL_PA_EAR_PA_EN_ENABLE BIT(6) +#define RX_EAR_CTL_PA_SEL_MASK BIT(7) +#define RX_EAR_CTL_PA_SEL BIT(7) + +#define CDC_A_SPKR_DAC_CTL (0xf1B0) +#define SPKR_DAC_CTL_DAC_RESET_MASK BIT(4) +#define SPKR_DAC_CTL_DAC_RESET_NORMAL 0 + +#define CDC_A_SPKR_DRV_CTL (0xf1B2) +#define SPKR_DRV_CTL_DEF_MASK 0xEF +#define SPKR_DRV_CLASSD_PA_EN_MASK BIT(7) +#define SPKR_DRV_CLASSD_PA_EN_ENABLE BIT(7) +#define SPKR_DRV_CAL_EN BIT(6) +#define SPKR_DRV_SETTLE_EN BIT(5) +#define SPKR_DRV_FW_EN BIT(3) +#define SPKR_DRV_BOOST_SET BIT(2) +#define SPKR_DRV_CMFB_SET BIT(1) +#define SPKR_DRV_GAIN_SET BIT(0) +#define SPKR_DRV_CTL_DEF_VAL (SPKR_DRV_CLASSD_PA_EN_ENABLE | \ + SPKR_DRV_CAL_EN | SPKR_DRV_SETTLE_EN | \ + SPKR_DRV_FW_EN | SPKR_DRV_BOOST_SET | \ + SPKR_DRV_CMFB_SET | SPKR_DRV_GAIN_SET) +#define CDC_A_SPKR_OCP_CTL (0xf1B4) +#define CDC_A_SPKR_PWRSTG_CTL (0xf1B5) +#define SPKR_PWRSTG_CTL_DAC_EN_MASK BIT(0) +#define SPKR_PWRSTG_CTL_DAC_EN BIT(0) +#define SPKR_PWRSTG_CTL_MASK 0xE0 +#define SPKR_PWRSTG_CTL_BBM_MASK BIT(7) +#define SPKR_PWRSTG_CTL_BBM_EN BIT(7) +#define SPKR_PWRSTG_CTL_HBRDGE_EN_MASK BIT(6) +#define SPKR_PWRSTG_CTL_HBRDGE_EN BIT(6) +#define SPKR_PWRSTG_CTL_CLAMP_EN_MASK BIT(5) +#define SPKR_PWRSTG_CTL_CLAMP_EN BIT(5) + +#define CDC_A_SPKR_DRV_DBG (0xf1B7) +#define CDC_A_CURRENT_LIMIT (0xf1C0) +#define CDC_A_BOOST_EN_CTL (0xf1C3) +#define CDC_A_SLOPE_COMP_IP_ZERO (0xf1C4) +#define CDC_A_SEC_ACCESS (0xf1D0) +#define CDC_A_PERPH_RESET_CTL3 (0xf1DA) +#define CDC_A_PERPH_RESET_CTL4 (0xf1DB) + +#define MSM8916_WCD_ANALOG_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) +#define MSM8916_WCD_ANALOG_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static int btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4; +static int hs_jack_mask = SND_JACK_HEADPHONE | SND_JACK_HEADSET; + +static const char * const supply_names[] = { + "vdd-cdc-io", + "vdd-cdc-tx-rx-cx", +}; + +#define MBHC_MAX_BUTTONS (5) + +struct pm8916_wcd_analog_priv { + u16 pmic_rev; + u16 codec_version; + bool mbhc_btn_enabled; + /* special event to detect accessory type */ + int mbhc_btn0_released; + bool detect_accessory_type; + struct clk *mclk; + struct snd_soc_component *component; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; + struct snd_soc_jack *jack; + bool hphl_jack_type_normally_open; + bool gnd_jack_type_normally_open; + /* Voltage threshold when internal current source of 100uA is used */ + u32 vref_btn_cs[MBHC_MAX_BUTTONS]; + /* Voltage threshold when microphone bias is ON */ + u32 vref_btn_micb[MBHC_MAX_BUTTONS]; + unsigned int micbias1_cap_mode; + unsigned int micbias2_cap_mode; + unsigned int micbias_mv; +}; + +static const char *const adc2_mux_text[] = { "ZERO", "INP2", "INP3" }; +static const char *const rdac2_mux_text[] = { "RX1", "RX2" }; +static const char *const hph_text[] = { "ZERO", "Switch", }; + +static const struct soc_enum hph_enum = SOC_ENUM_SINGLE_VIRT( + ARRAY_SIZE(hph_text), hph_text); + +static const struct snd_kcontrol_new ear_mux = SOC_DAPM_ENUM("EAR_S", hph_enum); +static const struct snd_kcontrol_new hphl_mux = SOC_DAPM_ENUM("HPHL", hph_enum); +static const struct snd_kcontrol_new hphr_mux = SOC_DAPM_ENUM("HPHR", hph_enum); + +/* ADC2 MUX */ +static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE_VIRT( + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +/* RDAC2 MUX */ +static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE( + CDC_D_CDC_CONN_HPHR_DAC_CTL, 0, 2, rdac2_mux_text); + +static const struct snd_kcontrol_new spkr_switch[] = { + SOC_DAPM_SINGLE("Switch", CDC_A_SPKR_DAC_CTL, 7, 1, 0) +}; + +static const struct snd_kcontrol_new rdac2_mux = SOC_DAPM_ENUM( + "RDAC2 MUX Mux", rdac2_mux_enum); +static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM( + "ADC2 MUX Mux", adc2_enum); + +/* Analog Gain control 0 dB to +24 dB in 6 dB steps */ +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 600, 0); + +static const struct snd_kcontrol_new pm8916_wcd_analog_snd_controls[] = { + SOC_SINGLE_TLV("ADC1 Volume", CDC_A_TX_1_EN, 3, 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", CDC_A_TX_2_EN, 3, 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", CDC_A_TX_3_EN, 3, 8, 0, analog_gain), +}; + +static void pm8916_wcd_analog_micbias_enable(struct snd_soc_component *component) +{ + struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL, + MICB_1_CTL_EXT_PRECHARG_EN_MASK | + MICB_1_CTL_INT_PRECHARG_BYP_MASK, + MICB_1_CTL_INT_PRECHARG_BYP_EXT_PRECHRG_SEL + | MICB_1_CTL_EXT_PRECHARG_EN_ENABLE); + + if (wcd->micbias_mv) { + snd_soc_component_update_bits(component, CDC_A_MICB_1_VAL, + MICB_1_VAL_MICB_OUT_VAL_MASK, + MICB_VOLTAGE_REGVAL(wcd->micbias_mv)); + /* + * Special headset needs MICBIAS as 2.7V so wait for + * 50 msec for the MICBIAS to reach 2.7 volts. + */ + if (wcd->micbias_mv >= 2700) + msleep(50); + } + + snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL, + MICB_1_CTL_EXT_PRECHARG_EN_MASK | + MICB_1_CTL_INT_PRECHARG_BYP_MASK, 0); + +} + +static int pm8916_wcd_analog_enable_micbias(struct snd_soc_component *component, + int event, unsigned int cap_mode) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + pm8916_wcd_analog_micbias_enable(component); + snd_soc_component_update_bits(component, CDC_A_MICB_1_EN, + MICB_1_EN_BYP_CAP_MASK, cap_mode); + break; + } + + return 0; +} + +static int pm8916_wcd_analog_enable_micbias_int(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, CDC_A_MICB_1_EN, + MICB_1_EN_OPA_STG2_TAIL_CURR_MASK, + MICB_1_EN_OPA_STG2_TAIL_CURR_1_60UA); + break; + } + + return 0; +} + +static int pm8916_wcd_analog_enable_micbias1(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component); + + return pm8916_wcd_analog_enable_micbias(component, event, + wcd->micbias1_cap_mode); +} + +static int pm8916_wcd_analog_enable_micbias2(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component); + + return pm8916_wcd_analog_enable_micbias(component, event, + wcd->micbias2_cap_mode); + +} + +static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv, + bool micbias2_enabled) +{ + struct snd_soc_component *component = priv->component; + u32 coarse, fine, reg_val, reg_addr; + int *vrefs, i; + + if (!micbias2_enabled) { /* use internal 100uA Current source */ + /* Enable internal 2.2k Internal Rbias Resistor */ + snd_soc_component_update_bits(component, CDC_A_MICB_1_INT_RBIAS, + MICB_1_INT_TX2_INT_RBIAS_EN_MASK, + MICB_1_INT_TX2_INT_RBIAS_EN_ENABLE); + /* Remove pull down on MIC BIAS2 */ + snd_soc_component_update_bits(component, CDC_A_MICB_2_EN, + CDC_A_MICB_2_PULL_DOWN_EN_MASK, + 0); + /* enable 100uA internal current source */ + snd_soc_component_update_bits(component, CDC_A_MBHC_FSM_CTL, + CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_MASK, + CDC_A_MBHC_FSM_CTL_BTN_ISRC_CTRL_I_100UA); + } + snd_soc_component_update_bits(component, CDC_A_MBHC_FSM_CTL, + CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN_MASK, + CDC_A_MBHC_FSM_CTL_MBHC_FSM_EN); + + if (micbias2_enabled) + vrefs = &priv->vref_btn_micb[0]; + else + vrefs = &priv->vref_btn_cs[0]; + + /* program vref ranges for all the buttons */ + reg_addr = CDC_A_MBHC_BTN0_ZDET_CTL_0; + for (i = 0; i < MBHC_MAX_BUTTONS; i++) { + /* split mv in to coarse parts of 100mv & fine parts of 12mv */ + coarse = (vrefs[i] / 100); + fine = ((vrefs[i] % 100) / 12); + reg_val = (coarse << CDC_A_MBHC_BTN_VREF_COARSE_SHIFT) | + (fine << CDC_A_MBHC_BTN_VREF_FINE_SHIFT); + snd_soc_component_update_bits(component, reg_addr, + CDC_A_MBHC_BTN_VREF_MASK, + reg_val); + reg_addr++; + } + + return 0; +} + +static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd) +{ + struct snd_soc_component *component = wcd->component; + bool micbias_enabled = false; + u32 plug_type = 0; + u32 int_en_mask; + + snd_soc_component_write(component, CDC_A_MBHC_DET_CTL_1, + CDC_A_MBHC_DET_CTL_L_DET_EN | + CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION | + CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO | + CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN); + + if (wcd->hphl_jack_type_normally_open) + plug_type |= CDC_A_HPHL_PLUG_TYPE_NO; + + if (wcd->gnd_jack_type_normally_open) + plug_type |= CDC_A_GND_PLUG_TYPE_NO; + + snd_soc_component_write(component, CDC_A_MBHC_DET_CTL_2, + CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 | + CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD | + plug_type | + CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN); + + + snd_soc_component_write(component, CDC_A_MBHC_DBNC_TIMER, + CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS | + CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS); + + /* enable MBHC clock */ + snd_soc_component_update_bits(component, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_D_MBHC_CLK_EN_MASK, + DIG_CLK_CTL_D_MBHC_CLK_EN); + + if (snd_soc_component_read(component, CDC_A_MICB_2_EN) & CDC_A_MICB_2_EN_ENABLE) + micbias_enabled = true; + + pm8916_mbhc_configure_bias(wcd, micbias_enabled); + + int_en_mask = MBHC_SWITCH_INT; + if (wcd->mbhc_btn_enabled) + int_en_mask |= MBHC_BUTTON_PRESS_DET | MBHC_BUTTON_RELEASE_DET; + + snd_soc_component_update_bits(component, CDC_D_INT_EN_CLR, int_en_mask, 0); + snd_soc_component_update_bits(component, CDC_D_INT_EN_SET, int_en_mask, int_en_mask); + wcd->mbhc_btn0_released = false; + wcd->detect_accessory_type = true; +} + +static int pm8916_wcd_analog_enable_micbias_int2(struct + snd_soc_dapm_widget + *w, struct snd_kcontrol + *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, CDC_A_MICB_2_EN, + CDC_A_MICB_2_PULL_DOWN_EN_MASK, 0); + break; + case SND_SOC_DAPM_POST_PMU: + pm8916_mbhc_configure_bias(wcd, true); + break; + case SND_SOC_DAPM_POST_PMD: + pm8916_mbhc_configure_bias(wcd, false); + break; + } + + return pm8916_wcd_analog_enable_micbias_int(w, kcontrol, event); +} + +static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 adc_reg = CDC_A_TX_1_2_TEST_CTL_2; + u8 init_bit_shift; + + if (w->reg == CDC_A_TX_1_EN) + init_bit_shift = 5; + else + init_bit_shift = 4; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (w->reg == CDC_A_TX_2_EN) + snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL, + MICB_1_CTL_CFILT_REF_SEL_MASK, + MICB_1_CTL_CFILT_REF_SEL_HPF_REF); + /* + * Add delay of 10 ms to give sufficient time for the voltage + * to shoot up and settle so that the txfe init does not + * happen when the input voltage is changing too much. + */ + usleep_range(10000, 10010); + snd_soc_component_update_bits(component, adc_reg, 1 << init_bit_shift, + 1 << init_bit_shift); + switch (w->reg) { + case CDC_A_TX_1_EN: + snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX1_CTL, + CONN_TX1_SERIAL_TX1_MUX, + CONN_TX1_SERIAL_TX1_ADC_1); + break; + case CDC_A_TX_2_EN: + case CDC_A_TX_3_EN: + snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX2_CTL, + CONN_TX2_SERIAL_TX2_MUX, + CONN_TX2_SERIAL_TX2_ADC_2); + break; + } + break; + case SND_SOC_DAPM_POST_PMU: + /* + * Add delay of 12 ms before deasserting the init + * to reduce the tx pop + */ + usleep_range(12000, 12010); + snd_soc_component_update_bits(component, adc_reg, 1 << init_bit_shift, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + switch (w->reg) { + case CDC_A_TX_1_EN: + snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX1_CTL, + CONN_TX1_SERIAL_TX1_MUX, + CONN_TX1_SERIAL_TX1_ZERO); + break; + case CDC_A_TX_2_EN: + snd_soc_component_update_bits(component, CDC_A_MICB_1_CTL, + MICB_1_CTL_CFILT_REF_SEL_MASK, 0); + fallthrough; + case CDC_A_TX_3_EN: + snd_soc_component_update_bits(component, CDC_D_CDC_CONN_TX2_CTL, + CONN_TX2_SERIAL_TX2_MUX, + CONN_TX2_SERIAL_TX2_ZERO); + break; + } + + + break; + } + return 0; +} + +static int pm8916_wcd_analog_enable_spk_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, CDC_A_SPKR_PWRSTG_CTL, + SPKR_PWRSTG_CTL_DAC_EN_MASK | + SPKR_PWRSTG_CTL_BBM_MASK | + SPKR_PWRSTG_CTL_HBRDGE_EN_MASK | + SPKR_PWRSTG_CTL_CLAMP_EN_MASK, + SPKR_PWRSTG_CTL_DAC_EN| + SPKR_PWRSTG_CTL_BBM_EN | + SPKR_PWRSTG_CTL_HBRDGE_EN | + SPKR_PWRSTG_CTL_CLAMP_EN); + + snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK, + RX_EAR_CTL_SPK_VBAT_LDO_EN_ENABLE); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, CDC_A_SPKR_DRV_CTL, + SPKR_DRV_CTL_DEF_MASK, + SPKR_DRV_CTL_DEF_VAL); + snd_soc_component_update_bits(component, w->reg, + SPKR_DRV_CLASSD_PA_EN_MASK, + SPKR_DRV_CLASSD_PA_EN_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, CDC_A_SPKR_PWRSTG_CTL, + SPKR_PWRSTG_CTL_DAC_EN_MASK| + SPKR_PWRSTG_CTL_BBM_MASK | + SPKR_PWRSTG_CTL_HBRDGE_EN_MASK | + SPKR_PWRSTG_CTL_CLAMP_EN_MASK, 0); + + snd_soc_component_update_bits(component, CDC_A_SPKR_DAC_CTL, + SPKR_DAC_CTL_DAC_RESET_MASK, + SPKR_DAC_CTL_DAC_RESET_NORMAL); + snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_SPK_VBAT_LDO_EN_MASK, 0); + break; + } + return 0; +} + +static int pm8916_wcd_analog_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_PA_SEL_MASK, RX_EAR_CTL_PA_SEL); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_PA_EAR_PA_EN_MASK, + RX_EAR_CTL_PA_EAR_PA_EN_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_PA_EAR_PA_EN_MASK, 0); + /* Delay to reduce ear turn off pop */ + usleep_range(7000, 7100); + snd_soc_component_update_bits(component, CDC_A_RX_EAR_CTL, + RX_EAR_CTL_PA_SEL_MASK, 0); + break; + } + return 0; +} + +static const struct reg_default wcd_reg_defaults_2_0[] = { + {CDC_A_RX_COM_OCP_CTL, 0xD1}, + {CDC_A_RX_COM_OCP_COUNT, 0xFF}, + {CDC_D_SEC_ACCESS, 0xA5}, + {CDC_D_PERPH_RESET_CTL3, 0x0F}, + {CDC_A_TX_1_2_OPAMP_BIAS, 0x4F}, + {CDC_A_NCP_FBCTRL, 0x28}, + {CDC_A_SPKR_DRV_CTL, 0x69}, + {CDC_A_SPKR_DRV_DBG, 0x01}, + {CDC_A_BOOST_EN_CTL, 0x5F}, + {CDC_A_SLOPE_COMP_IP_ZERO, 0x88}, + {CDC_A_SEC_ACCESS, 0xA5}, + {CDC_A_PERPH_RESET_CTL3, 0x0F}, + {CDC_A_CURRENT_LIMIT, 0x82}, + {CDC_A_SPKR_DAC_CTL, 0x03}, + {CDC_A_SPKR_OCP_CTL, 0xE1}, + {CDC_A_MASTER_BIAS_CTL, 0x30}, +}; + +static int pm8916_wcd_analog_probe(struct snd_soc_component *component) +{ + struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); + int err, reg; + + err = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); + if (err != 0) { + dev_err(component->dev, "failed to enable regulators (%d)\n", err); + return err; + } + + snd_soc_component_init_regmap(component, + dev_get_regmap(component->dev->parent, NULL)); + snd_soc_component_set_drvdata(component, priv); + priv->pmic_rev = snd_soc_component_read(component, CDC_D_REVISION1); + priv->codec_version = snd_soc_component_read(component, CDC_D_PERPH_SUBTYPE); + + dev_info(component->dev, "PMIC REV: %d\t CODEC Version: %d\n", + priv->pmic_rev, priv->codec_version); + + snd_soc_component_write(component, CDC_D_PERPH_RESET_CTL4, 0x01); + snd_soc_component_write(component, CDC_A_PERPH_RESET_CTL4, 0x01); + + for (reg = 0; reg < ARRAY_SIZE(wcd_reg_defaults_2_0); reg++) + snd_soc_component_write(component, wcd_reg_defaults_2_0[reg].reg, + wcd_reg_defaults_2_0[reg].def); + + priv->component = component; + + snd_soc_component_update_bits(component, CDC_D_CDC_RST_CTL, + RST_CTL_DIG_SW_RST_N_MASK, + RST_CTL_DIG_SW_RST_N_REMOVE_RESET); + + pm8916_wcd_setup_mbhc(priv); + + return 0; +} + +static void pm8916_wcd_analog_remove(struct snd_soc_component *component) +{ + struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(component->dev); + + snd_soc_component_update_bits(component, CDC_D_CDC_RST_CTL, + RST_CTL_DIG_SW_RST_N_MASK, 0); + + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), + priv->supplies); +} + +static const struct snd_soc_dapm_route pm8916_wcd_analog_audio_map[] = { + + {"PDM_RX1", NULL, "PDM Playback"}, + {"PDM_RX2", NULL, "PDM Playback"}, + {"PDM_RX3", NULL, "PDM Playback"}, + {"PDM Capture", NULL, "PDM_TX"}, + + /* ADC Connections */ + {"PDM_TX", NULL, "ADC2"}, + {"PDM_TX", NULL, "ADC3"}, + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC3", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "INP2", "ADC2_INP2"}, + {"ADC2 MUX", "INP3", "ADC2_INP3"}, + + {"PDM_TX", NULL, "ADC1"}, + {"ADC1", NULL, "AMIC1"}, + {"ADC2_INP2", NULL, "AMIC2"}, + {"ADC2_INP3", NULL, "AMIC3"}, + + /* RDAC Connections */ + {"HPHR DAC", NULL, "RDAC2 MUX"}, + {"RDAC2 MUX", "RX1", "PDM_RX1"}, + {"RDAC2 MUX", "RX2", "PDM_RX2"}, + {"HPHL DAC", NULL, "PDM_RX1"}, + {"PDM_RX1", NULL, "RXD1_CLK"}, + {"PDM_RX2", NULL, "RXD2_CLK"}, + {"PDM_RX3", NULL, "RXD3_CLK"}, + + {"PDM_RX1", NULL, "RXD_PDM_CLK"}, + {"PDM_RX2", NULL, "RXD_PDM_CLK"}, + {"PDM_RX3", NULL, "RXD_PDM_CLK"}, + + {"ADC1", NULL, "TXD_CLK"}, + {"ADC2", NULL, "TXD_CLK"}, + {"ADC3", NULL, "TXD_CLK"}, + + {"ADC1", NULL, "TXA_CLK25"}, + {"ADC2", NULL, "TXA_CLK25"}, + {"ADC3", NULL, "TXA_CLK25"}, + + {"PDM_RX1", NULL, "A_MCLK2"}, + {"PDM_RX2", NULL, "A_MCLK2"}, + {"PDM_RX3", NULL, "A_MCLK2"}, + + {"PDM_TX", NULL, "A_MCLK2"}, + {"A_MCLK2", NULL, "A_MCLK"}, + + /* Earpiece (RX MIX1) */ + {"EAR", NULL, "EAR_S"}, + {"EAR_S", "Switch", "EAR PA"}, + {"EAR PA", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "HPHL DAC"}, + {"EAR PA", NULL, "HPHR DAC"}, + {"EAR PA", NULL, "EAR CP"}, + + /* Headset (RX MIX1 and RX MIX2) */ + {"HPH_L", NULL, "HPHL PA"}, + {"HPH_R", NULL, "HPHR PA"}, + + {"HPHL DAC", NULL, "EAR_HPHL_CLK"}, + {"HPHR DAC", NULL, "EAR_HPHR_CLK"}, + + {"CP", NULL, "NCP_CLK"}, + + {"HPHL PA", NULL, "HPHL"}, + {"HPHR PA", NULL, "HPHR"}, + {"HPHL PA", NULL, "CP"}, + {"HPHL PA", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "CP"}, + {"HPHR PA", NULL, "RX_BIAS"}, + {"HPHL", "Switch", "HPHL DAC"}, + {"HPHR", "Switch", "HPHR DAC"}, + + {"RX_BIAS", NULL, "DAC_REF"}, + + {"SPK_OUT", NULL, "SPK PA"}, + {"SPK PA", NULL, "RX_BIAS"}, + {"SPK PA", NULL, "SPKR_CLK"}, + {"SPK PA", NULL, "SPK DAC"}, + {"SPK DAC", "Switch", "PDM_RX3"}, + + {"MIC_BIAS1", NULL, "INT_LDO_H"}, + {"MIC_BIAS2", NULL, "INT_LDO_H"}, + {"MIC_BIAS1", NULL, "vdd-micbias"}, + {"MIC_BIAS2", NULL, "vdd-micbias"}, + + {"MIC BIAS External1", NULL, "MIC_BIAS1"}, + {"MIC BIAS Internal1", NULL, "MIC_BIAS1"}, + {"MIC BIAS External2", NULL, "MIC_BIAS2"}, + {"MIC BIAS Internal2", NULL, "MIC_BIAS2"}, + {"MIC BIAS Internal3", NULL, "MIC_BIAS1"}, +}; + +static const struct snd_soc_dapm_widget pm8916_wcd_analog_dapm_widgets[] = { + + SND_SOC_DAPM_AIF_IN("PDM_RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("PDM_RX2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("PDM_RX3", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("PDM_TX", NULL, 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("HPH_L"), + SND_SOC_DAPM_OUTPUT("HPH_R"), + + /* RX stuff */ + SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), + + SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM, + 0, 0, NULL, 0, + pm8916_wcd_analog_enable_ear_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, &ear_mux), + SND_SOC_DAPM_SUPPLY("EAR CP", CDC_A_NCP_EN, 4, 0, NULL, 0), + + SND_SOC_DAPM_PGA("HPHL PA", CDC_A_RX_HPH_CNP_EN, 5, 0, NULL, 0), + SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, &hphl_mux), + SND_SOC_DAPM_MIXER("HPHL DAC", CDC_A_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, + 0), + SND_SOC_DAPM_PGA("HPHR PA", CDC_A_RX_HPH_CNP_EN, 4, 0, NULL, 0), + SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, &hphr_mux), + SND_SOC_DAPM_MIXER("HPHR DAC", CDC_A_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, + 0), + SND_SOC_DAPM_MIXER("SPK DAC", SND_SOC_NOPM, 0, 0, + spkr_switch, ARRAY_SIZE(spkr_switch)), + + /* Speaker */ + SND_SOC_DAPM_OUTPUT("SPK_OUT"), + SND_SOC_DAPM_PGA_E("SPK PA", CDC_A_SPKR_DRV_CTL, + 6, 0, NULL, 0, + pm8916_wcd_analog_enable_spk_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-micbias", 0, 0), + SND_SOC_DAPM_SUPPLY("CP", CDC_A_NCP_EN, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DAC_REF", CDC_A_RX_COM_BIAS_DAC, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX_BIAS", CDC_A_RX_COM_BIAS_DAC, 7, 0, NULL, 0), + + /* TX */ + SND_SOC_DAPM_SUPPLY("MIC_BIAS1", CDC_A_MICB_1_EN, 7, 0, + pm8916_wcd_analog_enable_micbias1, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("MIC_BIAS2", CDC_A_MICB_2_EN, 7, 0, + pm8916_wcd_analog_enable_micbias2, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("MIC BIAS External1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC BIAS External2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal1", CDC_A_MICB_1_INT_RBIAS, 7, 0, + pm8916_wcd_analog_enable_micbias_int, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal2", CDC_A_MICB_1_INT_RBIAS, 4, 0, + pm8916_wcd_analog_enable_micbias_int2, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS Internal3", CDC_A_MICB_1_INT_RBIAS, 1, 0, + pm8916_wcd_analog_enable_micbias_int, + SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, CDC_A_TX_1_EN, 7, 0, + pm8916_wcd_analog_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP2", NULL, CDC_A_TX_2_EN, 7, 0, + pm8916_wcd_analog_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP3", NULL, CDC_A_TX_3_EN, 7, 0, + pm8916_wcd_analog_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux), + + /* Analog path clocks */ + SND_SOC_DAPM_SUPPLY("EAR_HPHR_CLK", CDC_D_CDC_ANA_CLK_CTL, 0, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY("EAR_HPHL_CLK", CDC_D_CDC_ANA_CLK_CTL, 1, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY("SPKR_CLK", CDC_D_CDC_ANA_CLK_CTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TXA_CLK25", CDC_D_CDC_ANA_CLK_CTL, 5, 0, NULL, 0), + + /* Digital path clocks */ + + SND_SOC_DAPM_SUPPLY("RXD1_CLK", CDC_D_CDC_DIG_CLK_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RXD2_CLK", CDC_D_CDC_DIG_CLK_CTL, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RXD3_CLK", CDC_D_CDC_DIG_CLK_CTL, 2, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("TXD_CLK", CDC_D_CDC_DIG_CLK_CTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("NCP_CLK", CDC_D_CDC_DIG_CLK_CTL, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RXD_PDM_CLK", CDC_D_CDC_DIG_CLK_CTL, 7, 0, NULL, + 0), + + /* System Clock source */ + SND_SOC_DAPM_SUPPLY("A_MCLK", CDC_D_CDC_TOP_CLK_CTL, 2, 0, NULL, 0), + /* TX ADC and RX DAC Clock source. */ + SND_SOC_DAPM_SUPPLY("A_MCLK2", CDC_D_CDC_TOP_CLK_CTL, 3, 0, NULL, 0), +}; + +static int pm8916_wcd_analog_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, + void *data) +{ + struct pm8916_wcd_analog_priv *wcd = snd_soc_component_get_drvdata(component); + + wcd->jack = jack; + + return 0; +} + +static irqreturn_t mbhc_btn_release_irq_handler(int irq, void *arg) +{ + struct pm8916_wcd_analog_priv *priv = arg; + + if (priv->detect_accessory_type) { + struct snd_soc_component *component = priv->component; + u32 val = snd_soc_component_read(component, CDC_A_MBHC_RESULT_1); + + /* check if its BTN0 thats released */ + if ((val != -1) && !(val & CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK)) + priv->mbhc_btn0_released = true; + + } else { + snd_soc_jack_report(priv->jack, 0, btn_mask); + } + + return IRQ_HANDLED; +} + +static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg) +{ + struct pm8916_wcd_analog_priv *priv = arg; + struct snd_soc_component *component = priv->component; + u32 btn_result; + + btn_result = snd_soc_component_read(component, CDC_A_MBHC_RESULT_1) & + CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK; + + switch (btn_result) { + case 0xf: + snd_soc_jack_report(priv->jack, SND_JACK_BTN_4, btn_mask); + break; + case 0x7: + snd_soc_jack_report(priv->jack, SND_JACK_BTN_3, btn_mask); + break; + case 0x3: + snd_soc_jack_report(priv->jack, SND_JACK_BTN_2, btn_mask); + break; + case 0x1: + snd_soc_jack_report(priv->jack, SND_JACK_BTN_1, btn_mask); + break; + case 0x0: + /* handle BTN_0 specially for type detection */ + if (!priv->detect_accessory_type) + snd_soc_jack_report(priv->jack, + SND_JACK_BTN_0, btn_mask); + break; + default: + dev_err(component->dev, + "Unexpected button press result (%x)", btn_result); + break; + } + + return IRQ_HANDLED; +} + +static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg) +{ + struct pm8916_wcd_analog_priv *priv = arg; + struct snd_soc_component *component = priv->component; + bool ins = false; + + if (snd_soc_component_read(component, CDC_A_MBHC_DET_CTL_1) & + CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK) + ins = true; + + /* Set the detection type appropriately */ + snd_soc_component_update_bits(component, CDC_A_MBHC_DET_CTL_1, + CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_MASK, + (!ins << CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_SHIFT)); + + + if (ins) { /* hs insertion */ + bool micbias_enabled = false; + + if (snd_soc_component_read(component, CDC_A_MICB_2_EN) & + CDC_A_MICB_2_EN_ENABLE) + micbias_enabled = true; + + pm8916_mbhc_configure_bias(priv, micbias_enabled); + + /* + * if only a btn0 press event is receive just before + * insert event then its a 3 pole headphone else if + * both press and release event received then its + * a headset. + */ + if (priv->mbhc_btn0_released) + snd_soc_jack_report(priv->jack, + SND_JACK_HEADSET, hs_jack_mask); + else + snd_soc_jack_report(priv->jack, + SND_JACK_HEADPHONE, hs_jack_mask); + + priv->detect_accessory_type = false; + + } else { /* removal */ + snd_soc_jack_report(priv->jack, 0, hs_jack_mask); + priv->detect_accessory_type = true; + priv->mbhc_btn0_released = false; + } + + return IRQ_HANDLED; +} + +static struct snd_soc_dai_driver pm8916_wcd_analog_dai[] = { + [0] = { + .name = "pm8916_wcd_analog_pdm_rx", + .id = 0, + .playback = { + .stream_name = "PDM Playback", + .rates = MSM8916_WCD_ANALOG_RATES, + .formats = MSM8916_WCD_ANALOG_FORMATS, + .channels_min = 1, + .channels_max = 3, + }, + }, + [1] = { + .name = "pm8916_wcd_analog_pdm_tx", + .id = 1, + .capture = { + .stream_name = "PDM Capture", + .rates = MSM8916_WCD_ANALOG_RATES, + .formats = MSM8916_WCD_ANALOG_FORMATS, + .channels_min = 1, + .channels_max = 4, + }, + }, +}; + +static const struct snd_soc_component_driver pm8916_wcd_analog = { + .probe = pm8916_wcd_analog_probe, + .remove = pm8916_wcd_analog_remove, + .set_jack = pm8916_wcd_analog_set_jack, + .controls = pm8916_wcd_analog_snd_controls, + .num_controls = ARRAY_SIZE(pm8916_wcd_analog_snd_controls), + .dapm_widgets = pm8916_wcd_analog_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pm8916_wcd_analog_dapm_widgets), + .dapm_routes = pm8916_wcd_analog_audio_map, + .num_dapm_routes = ARRAY_SIZE(pm8916_wcd_analog_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int pm8916_wcd_analog_parse_dt(struct device *dev, + struct pm8916_wcd_analog_priv *priv) +{ + int rval; + + if (of_property_read_bool(dev->of_node, "qcom,micbias1-ext-cap")) + priv->micbias1_cap_mode = MICB_1_EN_EXT_BYP_CAP; + else + priv->micbias1_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP; + + if (of_property_read_bool(dev->of_node, "qcom,micbias2-ext-cap")) + priv->micbias2_cap_mode = MICB_1_EN_EXT_BYP_CAP; + else + priv->micbias2_cap_mode = MICB_1_EN_NO_EXT_BYP_CAP; + + of_property_read_u32(dev->of_node, "qcom,micbias-lvl", + &priv->micbias_mv); + + if (of_property_read_bool(dev->of_node, + "qcom,hphl-jack-type-normally-open")) + priv->hphl_jack_type_normally_open = true; + else + priv->hphl_jack_type_normally_open = false; + + if (of_property_read_bool(dev->of_node, + "qcom,gnd-jack-type-normally-open")) + priv->gnd_jack_type_normally_open = true; + else + priv->gnd_jack_type_normally_open = false; + + priv->mbhc_btn_enabled = true; + rval = of_property_read_u32_array(dev->of_node, + "qcom,mbhc-vthreshold-low", + &priv->vref_btn_cs[0], + MBHC_MAX_BUTTONS); + if (rval < 0) { + priv->mbhc_btn_enabled = false; + } else { + rval = of_property_read_u32_array(dev->of_node, + "qcom,mbhc-vthreshold-high", + &priv->vref_btn_micb[0], + MBHC_MAX_BUTTONS); + if (rval < 0) + priv->mbhc_btn_enabled = false; + } + + if (!priv->mbhc_btn_enabled) + dev_err(dev, + "DT property missing, MBHC btn detection disabled\n"); + + + return 0; +} + +static int pm8916_wcd_analog_spmi_probe(struct platform_device *pdev) +{ + struct pm8916_wcd_analog_priv *priv; + struct device *dev = &pdev->dev; + int ret, i, irq; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = pm8916_wcd_analog_parse_dt(dev, priv); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + priv->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) { + dev_err(dev, "Failed to get regulator supplies %d\n", ret); + return ret; + } + + irq = platform_get_irq_byname(pdev, "mbhc_switch_int"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + pm8916_mbhc_switch_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "mbhc switch irq", priv); + if (ret) { + dev_err(dev, "cannot request mbhc switch irq\n"); + return ret; + } + + if (priv->mbhc_btn_enabled) { + irq = platform_get_irq_byname(pdev, "mbhc_but_press_det"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + mbhc_btn_press_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "mbhc btn press irq", priv); + if (ret) { + dev_err(dev, "cannot request mbhc button press irq\n"); + return ret; + } + + irq = platform_get_irq_byname(pdev, "mbhc_but_rel_det"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + mbhc_btn_release_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "mbhc btn release irq", priv); + if (ret) { + dev_err(dev, "cannot request mbhc button release irq\n"); + return ret; + } + } + + dev_set_drvdata(dev, priv); + + return devm_snd_soc_register_component(dev, &pm8916_wcd_analog, + pm8916_wcd_analog_dai, + ARRAY_SIZE(pm8916_wcd_analog_dai)); +} + +static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { + { .compatible = "qcom,pm8916-wcd-analog-codec", }, + { } +}; + +MODULE_DEVICE_TABLE(of, pm8916_wcd_analog_spmi_match_table); + +static struct platform_driver pm8916_wcd_analog_spmi_driver = { + .driver = { + .name = "qcom,pm8916-wcd-spmi-codec", + .of_match_table = pm8916_wcd_analog_spmi_match_table, + }, + .probe = pm8916_wcd_analog_spmi_probe, +}; + +module_platform_driver(pm8916_wcd_analog_spmi_driver); + +MODULE_AUTHOR("Srinivas Kandagatla "); +MODULE_DESCRIPTION("PMIC PM8916 WCD Analog Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c new file mode 100644 index 0000000000..978c4d056e --- /dev/null +++ b/sound/soc/codecs/msm8916-wcd-digital.c @@ -0,0 +1,1251 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2016, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LPASS_CDC_CLK_RX_RESET_CTL (0x000) +#define LPASS_CDC_CLK_TX_RESET_B1_CTL (0x004) +#define CLK_RX_RESET_B1_CTL_TX1_RESET_MASK BIT(0) +#define CLK_RX_RESET_B1_CTL_TX2_RESET_MASK BIT(1) +#define LPASS_CDC_CLK_DMIC_B1_CTL (0x008) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_MASK GENMASK(3, 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV2 (0x0 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3 (0x1 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV4 (0x2 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV6 (0x3 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_SEL_DIV16 (0x4 << 1) +#define DMIC_B1_CTL_DMIC0_CLK_EN_MASK BIT(0) +#define DMIC_B1_CTL_DMIC0_CLK_EN_ENABLE BIT(0) + +#define LPASS_CDC_CLK_RX_I2S_CTL (0x00C) +#define RX_I2S_CTL_RX_I2S_MODE_MASK BIT(5) +#define RX_I2S_CTL_RX_I2S_MODE_16 BIT(5) +#define RX_I2S_CTL_RX_I2S_MODE_32 0 +#define RX_I2S_CTL_RX_I2S_FS_RATE_MASK GENMASK(2, 0) +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ 0x0 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ 0x1 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ 0x2 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ 0x3 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_96_KHZ 0x4 +#define RX_I2S_CTL_RX_I2S_FS_RATE_F_192_KHZ 0x5 +#define LPASS_CDC_CLK_TX_I2S_CTL (0x010) +#define TX_I2S_CTL_TX_I2S_MODE_MASK BIT(5) +#define TX_I2S_CTL_TX_I2S_MODE_16 BIT(5) +#define TX_I2S_CTL_TX_I2S_MODE_32 0 +#define TX_I2S_CTL_TX_I2S_FS_RATE_MASK GENMASK(2, 0) +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ 0x0 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ 0x1 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ 0x2 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ 0x3 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_96_KHZ 0x4 +#define TX_I2S_CTL_TX_I2S_FS_RATE_F_192_KHZ 0x5 + +#define LPASS_CDC_CLK_OTHR_RESET_B1_CTL (0x014) +#define LPASS_CDC_CLK_TX_CLK_EN_B1_CTL (0x018) +#define LPASS_CDC_CLK_OTHR_CTL (0x01C) +#define LPASS_CDC_CLK_RX_B1_CTL (0x020) +#define LPASS_CDC_CLK_MCLK_CTL (0x024) +#define MCLK_CTL_MCLK_EN_MASK BIT(0) +#define MCLK_CTL_MCLK_EN_ENABLE BIT(0) +#define MCLK_CTL_MCLK_EN_DISABLE 0 +#define LPASS_CDC_CLK_PDM_CTL (0x028) +#define LPASS_CDC_CLK_PDM_CTL_PDM_EN_MASK BIT(0) +#define LPASS_CDC_CLK_PDM_CTL_PDM_EN BIT(0) +#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK BIT(1) +#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB BIT(1) +#define LPASS_CDC_CLK_PDM_CTL_PDM_CLK_PDM_CLK 0 + +#define LPASS_CDC_CLK_SD_CTL (0x02C) +#define LPASS_CDC_RX1_B1_CTL (0x040) +#define LPASS_CDC_RX2_B1_CTL (0x060) +#define LPASS_CDC_RX3_B1_CTL (0x080) +#define LPASS_CDC_RX1_B2_CTL (0x044) +#define LPASS_CDC_RX2_B2_CTL (0x064) +#define LPASS_CDC_RX3_B2_CTL (0x084) +#define LPASS_CDC_RX1_B3_CTL (0x048) +#define LPASS_CDC_RX2_B3_CTL (0x068) +#define LPASS_CDC_RX3_B3_CTL (0x088) +#define LPASS_CDC_RX1_B4_CTL (0x04C) +#define LPASS_CDC_RX2_B4_CTL (0x06C) +#define LPASS_CDC_RX3_B4_CTL (0x08C) +#define LPASS_CDC_RX1_B5_CTL (0x050) +#define LPASS_CDC_RX2_B5_CTL (0x070) +#define LPASS_CDC_RX3_B5_CTL (0x090) +#define LPASS_CDC_RX1_B6_CTL (0x054) +#define RXn_B6_CTL_MUTE_MASK BIT(0) +#define RXn_B6_CTL_MUTE_ENABLE BIT(0) +#define RXn_B6_CTL_MUTE_DISABLE 0 +#define LPASS_CDC_RX2_B6_CTL (0x074) +#define LPASS_CDC_RX3_B6_CTL (0x094) +#define LPASS_CDC_RX1_VOL_CTL_B1_CTL (0x058) +#define LPASS_CDC_RX2_VOL_CTL_B1_CTL (0x078) +#define LPASS_CDC_RX3_VOL_CTL_B1_CTL (0x098) +#define LPASS_CDC_RX1_VOL_CTL_B2_CTL (0x05C) +#define LPASS_CDC_RX2_VOL_CTL_B2_CTL (0x07C) +#define LPASS_CDC_RX3_VOL_CTL_B2_CTL (0x09C) +#define LPASS_CDC_TOP_GAIN_UPDATE (0x0A0) +#define LPASS_CDC_TOP_CTL (0x0A4) +#define TOP_CTL_DIG_MCLK_FREQ_MASK BIT(0) +#define TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ 0 +#define TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ BIT(0) + +#define LPASS_CDC_DEBUG_DESER1_CTL (0x0E0) +#define LPASS_CDC_DEBUG_DESER2_CTL (0x0E4) +#define LPASS_CDC_DEBUG_B1_CTL_CFG (0x0E8) +#define LPASS_CDC_DEBUG_B2_CTL_CFG (0x0EC) +#define LPASS_CDC_DEBUG_B3_CTL_CFG (0x0F0) +#define LPASS_CDC_IIR1_GAIN_B1_CTL (0x100) +#define LPASS_CDC_IIR2_GAIN_B1_CTL (0x140) +#define LPASS_CDC_IIR1_GAIN_B2_CTL (0x104) +#define LPASS_CDC_IIR2_GAIN_B2_CTL (0x144) +#define LPASS_CDC_IIR1_GAIN_B3_CTL (0x108) +#define LPASS_CDC_IIR2_GAIN_B3_CTL (0x148) +#define LPASS_CDC_IIR1_GAIN_B4_CTL (0x10C) +#define LPASS_CDC_IIR2_GAIN_B4_CTL (0x14C) +#define LPASS_CDC_IIR1_GAIN_B5_CTL (0x110) +#define LPASS_CDC_IIR2_GAIN_B5_CTL (0x150) +#define LPASS_CDC_IIR1_GAIN_B6_CTL (0x114) +#define LPASS_CDC_IIR2_GAIN_B6_CTL (0x154) +#define LPASS_CDC_IIR1_GAIN_B7_CTL (0x118) +#define LPASS_CDC_IIR2_GAIN_B7_CTL (0x158) +#define LPASS_CDC_IIR1_GAIN_B8_CTL (0x11C) +#define LPASS_CDC_IIR2_GAIN_B8_CTL (0x15C) +#define LPASS_CDC_IIR1_CTL (0x120) +#define LPASS_CDC_IIR2_CTL (0x160) +#define LPASS_CDC_IIR1_GAIN_TIMER_CTL (0x124) +#define LPASS_CDC_IIR2_GAIN_TIMER_CTL (0x164) +#define LPASS_CDC_IIR1_COEF_B1_CTL (0x128) +#define LPASS_CDC_IIR2_COEF_B1_CTL (0x168) +#define LPASS_CDC_IIR1_COEF_B2_CTL (0x12C) +#define LPASS_CDC_IIR2_COEF_B2_CTL (0x16C) +#define LPASS_CDC_CONN_RX1_B1_CTL (0x180) +#define LPASS_CDC_CONN_RX1_B2_CTL (0x184) +#define LPASS_CDC_CONN_RX1_B3_CTL (0x188) +#define LPASS_CDC_CONN_RX2_B1_CTL (0x18C) +#define LPASS_CDC_CONN_RX2_B2_CTL (0x190) +#define LPASS_CDC_CONN_RX2_B3_CTL (0x194) +#define LPASS_CDC_CONN_RX3_B1_CTL (0x198) +#define LPASS_CDC_CONN_RX3_B2_CTL (0x19C) +#define LPASS_CDC_CONN_TX_B1_CTL (0x1A0) +#define LPASS_CDC_CONN_EQ1_B1_CTL (0x1A8) +#define LPASS_CDC_CONN_EQ1_B2_CTL (0x1AC) +#define LPASS_CDC_CONN_EQ1_B3_CTL (0x1B0) +#define LPASS_CDC_CONN_EQ1_B4_CTL (0x1B4) +#define LPASS_CDC_CONN_EQ2_B1_CTL (0x1B8) +#define LPASS_CDC_CONN_EQ2_B2_CTL (0x1BC) +#define LPASS_CDC_CONN_EQ2_B3_CTL (0x1C0) +#define LPASS_CDC_CONN_EQ2_B4_CTL (0x1C4) +#define LPASS_CDC_CONN_TX_I2S_SD1_CTL (0x1C8) +#define LPASS_CDC_TX1_VOL_CTL_TIMER (0x280) +#define LPASS_CDC_TX2_VOL_CTL_TIMER (0x2A0) +#define LPASS_CDC_TX1_VOL_CTL_GAIN (0x284) +#define LPASS_CDC_TX2_VOL_CTL_GAIN (0x2A4) +#define LPASS_CDC_TX1_VOL_CTL_CFG (0x288) +#define TX_VOL_CTL_CFG_MUTE_EN_MASK BIT(0) +#define TX_VOL_CTL_CFG_MUTE_EN_ENABLE BIT(0) + +#define LPASS_CDC_TX2_VOL_CTL_CFG (0x2A8) +#define LPASS_CDC_TX1_MUX_CTL (0x28C) +#define TX_MUX_CTL_CUT_OFF_FREQ_MASK GENMASK(5, 4) +#define TX_MUX_CTL_CUT_OFF_FREQ_SHIFT 4 +#define TX_MUX_CTL_CF_NEG_3DB_4HZ (0x0 << 4) +#define TX_MUX_CTL_CF_NEG_3DB_75HZ (0x1 << 4) +#define TX_MUX_CTL_CF_NEG_3DB_150HZ (0x2 << 4) +#define TX_MUX_CTL_HPF_BP_SEL_MASK BIT(3) +#define TX_MUX_CTL_HPF_BP_SEL_BYPASS BIT(3) +#define TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS 0 + +#define LPASS_CDC_TX2_MUX_CTL (0x2AC) +#define LPASS_CDC_TX1_CLK_FS_CTL (0x290) +#define LPASS_CDC_TX2_CLK_FS_CTL (0x2B0) +#define LPASS_CDC_TX1_DMIC_CTL (0x294) +#define LPASS_CDC_TX2_DMIC_CTL (0x2B4) +#define TXN_DMIC_CTL_CLK_SEL_MASK GENMASK(2, 0) +#define TXN_DMIC_CTL_CLK_SEL_DIV2 0x0 +#define TXN_DMIC_CTL_CLK_SEL_DIV3 0x1 +#define TXN_DMIC_CTL_CLK_SEL_DIV4 0x2 +#define TXN_DMIC_CTL_CLK_SEL_DIV6 0x3 +#define TXN_DMIC_CTL_CLK_SEL_DIV16 0x4 + +#define MSM8916_WCD_DIGITAL_RATES (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_48000) +#define MSM8916_WCD_DIGITAL_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +/* Codec supports 2 IIR filters */ +enum { + IIR1 = 0, + IIR2, + IIR_MAX, +}; + +/* Codec supports 5 bands */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +#define WCD_IIR_FILTER_SIZE (sizeof(u32)*BAND_MAX) + +#define WCD_IIR_FILTER_CTL(xname, iidx, bidx) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = wcd_iir_filter_info, \ + .get = msm8x16_wcd_get_iir_band_audio_mixer, \ + .put = msm8x16_wcd_put_iir_band_audio_mixer, \ + .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) { \ + .iir_idx = iidx, \ + .band_idx = bidx, \ + .bytes_ext = {.max = WCD_IIR_FILTER_SIZE, }, \ + } \ +} + +struct wcd_iir_filter_ctl { + unsigned int iir_idx; + unsigned int band_idx; + struct soc_bytes_ext bytes_ext; +}; + +struct msm8916_wcd_digital_priv { + struct clk *ahbclk, *mclk; +}; + +static const unsigned long rx_gain_reg[] = { + LPASS_CDC_RX1_VOL_CTL_B2_CTL, + LPASS_CDC_RX2_VOL_CTL_B2_CTL, + LPASS_CDC_RX3_VOL_CTL_B2_CTL, +}; + +static const unsigned long tx_gain_reg[] = { + LPASS_CDC_TX1_VOL_CTL_GAIN, + LPASS_CDC_TX2_VOL_CTL_GAIN, +}; + +static const char *const rx_mix1_text[] = { + "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3" +}; + +static const char * const rx_mix2_text[] = { + "ZERO", "IIR1", "IIR2" +}; + +static const char *const dec_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2" +}; + +static const char *const cic_mux_text[] = { "AMIC", "DMIC" }; + +/* RX1 MIX1 */ +static const struct soc_enum rx_mix1_inp_enum[] = { + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B1_CTL, 3, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B2_CTL, 0, 6, rx_mix1_text), +}; + +/* RX2 MIX1 */ +static const struct soc_enum rx2_mix1_inp_enum[] = { + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 3, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B2_CTL, 0, 6, rx_mix1_text), +}; + +/* RX3 MIX1 */ +static const struct soc_enum rx3_mix1_inp_enum[] = { + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 3, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B2_CTL, 0, 6, rx_mix1_text), +}; + +/* RX1 MIX2 */ +static const struct soc_enum rx_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX1_B3_CTL, + 0, 3, rx_mix2_text); + +/* RX2 MIX2 */ +static const struct soc_enum rx2_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B3_CTL, + 0, 3, rx_mix2_text); + +/* DEC */ +static const struct soc_enum dec1_mux_enum = SOC_ENUM_SINGLE( + LPASS_CDC_CONN_TX_B1_CTL, 0, 6, dec_mux_text); +static const struct soc_enum dec2_mux_enum = SOC_ENUM_SINGLE( + LPASS_CDC_CONN_TX_B1_CTL, 3, 6, dec_mux_text); + +/* CIC */ +static const struct soc_enum cic1_mux_enum = SOC_ENUM_SINGLE( + LPASS_CDC_TX1_MUX_CTL, 0, 2, cic_mux_text); +static const struct soc_enum cic2_mux_enum = SOC_ENUM_SINGLE( + LPASS_CDC_TX2_MUX_CTL, 0, 2, cic_mux_text); + +/* RDAC2 MUX */ +static const struct snd_kcontrol_new dec1_mux = SOC_DAPM_ENUM( + "DEC1 MUX Mux", dec1_mux_enum); +static const struct snd_kcontrol_new dec2_mux = SOC_DAPM_ENUM( + "DEC2 MUX Mux", dec2_mux_enum); +static const struct snd_kcontrol_new cic1_mux = SOC_DAPM_ENUM( + "CIC1 MUX Mux", cic1_mux_enum); +static const struct snd_kcontrol_new cic2_mux = SOC_DAPM_ENUM( + "CIC2 MUX Mux", cic2_mux_enum); +static const struct snd_kcontrol_new rx_mix1_inp1_mux = SOC_DAPM_ENUM( + "RX1 MIX1 INP1 Mux", rx_mix1_inp_enum[0]); +static const struct snd_kcontrol_new rx_mix1_inp2_mux = SOC_DAPM_ENUM( + "RX1 MIX1 INP2 Mux", rx_mix1_inp_enum[1]); +static const struct snd_kcontrol_new rx_mix1_inp3_mux = SOC_DAPM_ENUM( + "RX1 MIX1 INP3 Mux", rx_mix1_inp_enum[2]); +static const struct snd_kcontrol_new rx2_mix1_inp1_mux = SOC_DAPM_ENUM( + "RX2 MIX1 INP1 Mux", rx2_mix1_inp_enum[0]); +static const struct snd_kcontrol_new rx2_mix1_inp2_mux = SOC_DAPM_ENUM( + "RX2 MIX1 INP2 Mux", rx2_mix1_inp_enum[1]); +static const struct snd_kcontrol_new rx2_mix1_inp3_mux = SOC_DAPM_ENUM( + "RX2 MIX1 INP3 Mux", rx2_mix1_inp_enum[2]); +static const struct snd_kcontrol_new rx3_mix1_inp1_mux = SOC_DAPM_ENUM( + "RX3 MIX1 INP1 Mux", rx3_mix1_inp_enum[0]); +static const struct snd_kcontrol_new rx3_mix1_inp2_mux = SOC_DAPM_ENUM( + "RX3 MIX1 INP2 Mux", rx3_mix1_inp_enum[1]); +static const struct snd_kcontrol_new rx3_mix1_inp3_mux = SOC_DAPM_ENUM( + "RX3 MIX1 INP3 Mux", rx3_mix1_inp_enum[2]); +static const struct snd_kcontrol_new rx1_mix2_inp1_mux = SOC_DAPM_ENUM( + "RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum); +static const struct snd_kcontrol_new rx2_mix2_inp1_mux = SOC_DAPM_ENUM( + "RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum); + +/* Digital Gain control -84 dB to +40 dB in 1 dB steps */ +static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); + +/* Cutoff Freq for High Pass Filter at -3dB */ +static const char * const hpf_cutoff_text[] = { + "4Hz", "75Hz", "150Hz", +}; + +static SOC_ENUM_SINGLE_DECL(tx1_hpf_cutoff_enum, LPASS_CDC_TX1_MUX_CTL, 4, + hpf_cutoff_text); +static SOC_ENUM_SINGLE_DECL(tx2_hpf_cutoff_enum, LPASS_CDC_TX2_MUX_CTL, 4, + hpf_cutoff_text); + +/* cut off for dc blocker inside rx chain */ +static const char * const dc_blocker_cutoff_text[] = { + "4Hz", "75Hz", "150Hz", +}; + +static SOC_ENUM_SINGLE_DECL(rx1_dcb_cutoff_enum, LPASS_CDC_RX1_B4_CTL, 0, + dc_blocker_cutoff_text); +static SOC_ENUM_SINGLE_DECL(rx2_dcb_cutoff_enum, LPASS_CDC_RX2_B4_CTL, 0, + dc_blocker_cutoff_text); +static SOC_ENUM_SINGLE_DECL(rx3_dcb_cutoff_enum, LPASS_CDC_RX3_B4_CTL, 0, + dc_blocker_cutoff_text); + +static int msm8x16_wcd_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + int value = 0, reg = 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->shift == 0) + reg = LPASS_CDC_IIR1_GAIN_B1_CTL; + else if (w->shift == 1) + reg = LPASS_CDC_IIR2_GAIN_B1_CTL; + value = snd_soc_component_read(component, reg); + snd_soc_component_write(component, reg, value); + break; + default: + break; + } + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_component *component, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_component_write(component, + (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_component_read(component, + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)); + + snd_soc_component_write(component, + (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_component_read(component, + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 8); + + snd_soc_component_write(component, + (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_component_read(component, + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 16); + + snd_soc_component_write(component, + (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_component_read(component, + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx)) & 0x3f) << 24); + return value; + +} + +static int msm8x16_wcd_get_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + int iir_idx = ctl->iir_idx; + int band_idx = ctl->band_idx; + u32 coeff[BAND_MAX]; + + coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, 0); + coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, 1); + coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, 2); + coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, 3); + coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, 4); + + memcpy(ucontrol->value.bytes.data, &coeff[0], params->max); + + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_component *component, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_component_write(component, + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value & 0xFF)); + + snd_soc_component_write(component, + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_component_write(component, + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_component_write(component, + (LPASS_CDC_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 24) & 0x3F); +} + +static int msm8x16_wcd_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + int iir_idx = ctl->iir_idx; + int band_idx = ctl->band_idx; + u32 coeff[BAND_MAX]; + + memcpy(&coeff[0], ucontrol->value.bytes.data, params->max); + + /* Mask top bit it is reserved */ + /* Updates addr automatically for each B2 write */ + snd_soc_component_write(component, + (LPASS_CDC_IIR1_COEF_B1_CTL + 64 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]); + + return 0; +} + +static int wcd_iir_filter_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} + +static const struct snd_kcontrol_new msm8916_wcd_digital_snd_controls[] = { + SOC_SINGLE_S8_TLV("RX1 Digital Volume", LPASS_CDC_RX1_VOL_CTL_B2_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX2 Digital Volume", LPASS_CDC_RX2_VOL_CTL_B2_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX3 Digital Volume", LPASS_CDC_RX3_VOL_CTL_B2_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("TX1 Digital Volume", LPASS_CDC_TX1_VOL_CTL_GAIN, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("TX2 Digital Volume", LPASS_CDC_TX2_VOL_CTL_GAIN, + -84, 40, digital_gain), + SOC_ENUM("TX1 HPF Cutoff", tx1_hpf_cutoff_enum), + SOC_ENUM("TX2 HPF Cutoff", tx2_hpf_cutoff_enum), + SOC_SINGLE("TX1 HPF Switch", LPASS_CDC_TX1_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX2 HPF Switch", LPASS_CDC_TX2_MUX_CTL, 3, 1, 0), + SOC_ENUM("RX1 DCB Cutoff", rx1_dcb_cutoff_enum), + SOC_ENUM("RX2 DCB Cutoff", rx2_dcb_cutoff_enum), + SOC_ENUM("RX3 DCB Cutoff", rx3_dcb_cutoff_enum), + SOC_SINGLE("RX1 DCB Switch", LPASS_CDC_RX1_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX2 DCB Switch", LPASS_CDC_RX2_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX3 DCB Switch", LPASS_CDC_RX3_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX1 Mute Switch", LPASS_CDC_RX1_B6_CTL, 0, 1, 0), + SOC_SINGLE("RX2 Mute Switch", LPASS_CDC_RX2_B6_CTL, 0, 1, 0), + SOC_SINGLE("RX3 Mute Switch", LPASS_CDC_RX3_B6_CTL, 0, 1, 0), + + SOC_SINGLE("IIR1 Band1 Switch", LPASS_CDC_IIR1_CTL, 0, 1, 0), + SOC_SINGLE("IIR1 Band2 Switch", LPASS_CDC_IIR1_CTL, 1, 1, 0), + SOC_SINGLE("IIR1 Band3 Switch", LPASS_CDC_IIR1_CTL, 2, 1, 0), + SOC_SINGLE("IIR1 Band4 Switch", LPASS_CDC_IIR1_CTL, 3, 1, 0), + SOC_SINGLE("IIR1 Band5 Switch", LPASS_CDC_IIR1_CTL, 4, 1, 0), + SOC_SINGLE("IIR2 Band1 Switch", LPASS_CDC_IIR2_CTL, 0, 1, 0), + SOC_SINGLE("IIR2 Band2 Switch", LPASS_CDC_IIR2_CTL, 1, 1, 0), + SOC_SINGLE("IIR2 Band3 Switch", LPASS_CDC_IIR2_CTL, 2, 1, 0), + SOC_SINGLE("IIR2 Band4 Switch", LPASS_CDC_IIR2_CTL, 3, 1, 0), + SOC_SINGLE("IIR2 Band5 Switch", LPASS_CDC_IIR2_CTL, 4, 1, 0), + WCD_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1), + WCD_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2), + WCD_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3), + WCD_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4), + WCD_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5), + WCD_IIR_FILTER_CTL("IIR2 Band1", IIR2, BAND1), + WCD_IIR_FILTER_CTL("IIR2 Band2", IIR2, BAND2), + WCD_IIR_FILTER_CTL("IIR2 Band3", IIR2, BAND3), + WCD_IIR_FILTER_CTL("IIR2 Band4", IIR2, BAND4), + WCD_IIR_FILTER_CTL("IIR2 Band5", IIR2, BAND5), + SOC_SINGLE_S8_TLV("IIR1 INP1 Volume", LPASS_CDC_IIR1_GAIN_B1_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP2 Volume", LPASS_CDC_IIR1_GAIN_B2_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP3 Volume", LPASS_CDC_IIR1_GAIN_B3_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP4 Volume", LPASS_CDC_IIR1_GAIN_B4_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("IIR2 INP1 Volume", LPASS_CDC_IIR2_GAIN_B1_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("IIR2 INP2 Volume", LPASS_CDC_IIR2_GAIN_B2_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("IIR2 INP3 Volume", LPASS_CDC_IIR2_GAIN_B3_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("IIR2 INP4 Volume", LPASS_CDC_IIR2_GAIN_B4_CTL, + -84, 40, digital_gain), + +}; + +static int msm8916_wcd_digital_enable_interpolator( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* apply the digital gain after the interpolator is enabled */ + usleep_range(10000, 10100); + snd_soc_component_write(component, rx_gain_reg[w->shift], + snd_soc_component_read(component, rx_gain_reg[w->shift])); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, LPASS_CDC_CLK_RX_RESET_CTL, + 1 << w->shift, 1 << w->shift); + snd_soc_component_update_bits(component, LPASS_CDC_CLK_RX_RESET_CTL, + 1 << w->shift, 0x0); + break; + } + return 0; +} + +static int msm8916_wcd_digital_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int decimator = w->shift + 1; + u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; + u8 dec_hpf_cut_of_freq; + + dec_reset_reg = LPASS_CDC_CLK_TX_RESET_B1_CTL; + tx_vol_ctl_reg = LPASS_CDC_TX1_VOL_CTL_CFG + 32 * (decimator - 1); + tx_mux_ctl_reg = LPASS_CDC_TX1_MUX_CTL + 32 * (decimator - 1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable TX digital mute */ + snd_soc_component_update_bits(component, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, + TX_VOL_CTL_CFG_MUTE_EN_ENABLE); + dec_hpf_cut_of_freq = snd_soc_component_read(component, tx_mux_ctl_reg) & + TX_MUX_CTL_CUT_OFF_FREQ_MASK; + dec_hpf_cut_of_freq >>= TX_MUX_CTL_CUT_OFF_FREQ_SHIFT; + if (dec_hpf_cut_of_freq != TX_MUX_CTL_CF_NEG_3DB_150HZ) { + /* set cut of freq to CF_MIN_3DB_150HZ (0x1) */ + snd_soc_component_update_bits(component, tx_mux_ctl_reg, + TX_MUX_CTL_CUT_OFF_FREQ_MASK, + TX_MUX_CTL_CF_NEG_3DB_150HZ); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* enable HPF */ + snd_soc_component_update_bits(component, tx_mux_ctl_reg, + TX_MUX_CTL_HPF_BP_SEL_MASK, + TX_MUX_CTL_HPF_BP_SEL_NO_BYPASS); + /* apply the digital gain after the decimator is enabled */ + snd_soc_component_write(component, tx_gain_reg[w->shift], + snd_soc_component_read(component, tx_gain_reg[w->shift])); + snd_soc_component_update_bits(component, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, 0); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, + TX_VOL_CTL_CFG_MUTE_EN_ENABLE); + snd_soc_component_update_bits(component, tx_mux_ctl_reg, + TX_MUX_CTL_HPF_BP_SEL_MASK, + TX_MUX_CTL_HPF_BP_SEL_BYPASS); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, dec_reset_reg, 1 << w->shift, + 1 << w->shift); + snd_soc_component_update_bits(component, dec_reset_reg, 1 << w->shift, 0x0); + snd_soc_component_update_bits(component, tx_mux_ctl_reg, + TX_MUX_CTL_HPF_BP_SEL_MASK, + TX_MUX_CTL_HPF_BP_SEL_BYPASS); + snd_soc_component_update_bits(component, tx_vol_ctl_reg, + TX_VOL_CTL_CFG_MUTE_EN_MASK, 0); + break; + } + + return 0; +} + +static int msm8916_wcd_digital_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int dmic; + int ret; + /* get dmic number out of widget name */ + char *dmic_num = strpbrk(w->name, "12"); + + if (dmic_num == NULL) { + dev_err(component->dev, "Invalid DMIC\n"); + return -EINVAL; + } + ret = kstrtouint(dmic_num, 10, &dmic); + if (ret < 0 || dmic > 2) { + dev_err(component->dev, "Invalid DMIC line on the component\n"); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, LPASS_CDC_CLK_DMIC_B1_CTL, + DMIC_B1_CTL_DMIC0_CLK_SEL_MASK, + DMIC_B1_CTL_DMIC0_CLK_SEL_DIV3); + switch (dmic) { + case 1: + snd_soc_component_update_bits(component, LPASS_CDC_TX1_DMIC_CTL, + TXN_DMIC_CTL_CLK_SEL_MASK, + TXN_DMIC_CTL_CLK_SEL_DIV3); + break; + case 2: + snd_soc_component_update_bits(component, LPASS_CDC_TX2_DMIC_CTL, + TXN_DMIC_CTL_CLK_SEL_MASK, + TXN_DMIC_CTL_CLK_SEL_DIV3); + break; + } + break; + } + + return 0; +} + +static const char * const iir_inp1_text[] = { + "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3" +}; + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(LPASS_CDC_CONN_EQ1_B1_CTL, + 0, 6, iir_inp1_text); + +static const struct soc_enum iir2_inp1_mux_enum = + SOC_ENUM_SINGLE(LPASS_CDC_CONN_EQ2_B1_CTL, + 0, 6, iir_inp1_text); + +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); + +static const struct snd_kcontrol_new iir2_inp1_mux = + SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum); + +static const struct snd_soc_dapm_widget msm8916_wcd_digital_dapm_widgets[] = { + /*RX stuff */ + SND_SOC_DAPM_AIF_IN("I2S RX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX3", NULL, 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("PDM_RX1"), + SND_SOC_DAPM_OUTPUT("PDM_RX2"), + SND_SOC_DAPM_OUTPUT("PDM_RX3"), + + SND_SOC_DAPM_INPUT("LPASS_PDM_TX"), + + SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX3 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Interpolator */ + SND_SOC_DAPM_MIXER_E("RX1 INT", LPASS_CDC_CLK_RX_B1_CTL, 0, 0, NULL, + 0, msm8916_wcd_digital_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2 INT", LPASS_CDC_CLK_RX_B1_CTL, 1, 0, NULL, + 0, msm8916_wcd_digital_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3 INT", LPASS_CDC_CLK_RX_B1_CTL, 2, 0, NULL, + 0, msm8916_wcd_digital_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp3_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp3_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp3_mux), + SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx1_mix2_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix2_inp1_mux), + + SND_SOC_DAPM_MUX("CIC1 MUX", SND_SOC_NOPM, 0, 0, &cic1_mux), + SND_SOC_DAPM_MUX("CIC2 MUX", SND_SOC_NOPM, 0, 0, &cic2_mux), + /* TX */ + SND_SOC_DAPM_MIXER("ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("DEC1 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0, + &dec1_mux, msm8916_wcd_digital_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("DEC2 MUX", LPASS_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0, + &dec2_mux, msm8916_wcd_digital_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT("I2S TX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX2", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX3", NULL, 0, SND_SOC_NOPM, 0, 0), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + msm8916_wcd_digital_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + msm8916_wcd_digital_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DMIC_CLK", LPASS_CDC_CLK_DMIC_B1_CTL, 0, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", LPASS_CDC_CLK_RX_I2S_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", LPASS_CDC_CLK_TX_I2S_CTL, 4, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PDM_CLK", LPASS_CDC_CLK_PDM_CTL, 0, 0, NULL, 0), + /* Connectivity Clock */ + SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, LPASS_CDC_CLK_OTHR_CTL, 2, 0, + NULL, 0), + SND_SOC_DAPM_MIC("Digital Mic1", NULL), + SND_SOC_DAPM_MIC("Digital Mic2", NULL), + + /* Sidetone */ + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_PGA_E("IIR1", LPASS_CDC_CLK_SD_CTL, 0, 0, NULL, 0, + msm8x16_wcd_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux), + SND_SOC_DAPM_PGA_E("IIR2", LPASS_CDC_CLK_SD_CTL, 1, 0, NULL, 0, + msm8x16_wcd_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU), + +}; + +static int msm8916_wcd_digital_get_clks(struct platform_device *pdev, + struct msm8916_wcd_digital_priv *priv) +{ + struct device *dev = &pdev->dev; + + priv->ahbclk = devm_clk_get(dev, "ahbix-clk"); + if (IS_ERR(priv->ahbclk)) { + dev_err(dev, "failed to get ahbix clk\n"); + return PTR_ERR(priv->ahbclk); + } + + priv->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(priv->mclk)) { + dev_err(dev, "failed to get mclk\n"); + return PTR_ERR(priv->mclk); + } + + return 0; +} + +static int msm8916_wcd_digital_component_probe(struct snd_soc_component *component) +{ + struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(component->dev); + + snd_soc_component_set_drvdata(component, priv); + + return 0; +} + +static int msm8916_wcd_digital_component_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, + unsigned int freq, int dir) +{ + struct msm8916_wcd_digital_priv *p = dev_get_drvdata(component->dev); + + return clk_set_rate(p->mclk, freq); +} + +static int msm8916_wcd_digital_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u8 tx_fs_rate; + u8 rx_fs_rate; + + switch (params_rate(params)) { + case 8000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_8_KHZ; + rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_8_KHZ; + break; + case 16000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_16_KHZ; + rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_16_KHZ; + break; + case 32000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_32_KHZ; + rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_32_KHZ; + break; + case 48000: + tx_fs_rate = TX_I2S_CTL_TX_I2S_FS_RATE_F_48_KHZ; + rx_fs_rate = RX_I2S_CTL_RX_I2S_FS_RATE_F_48_KHZ; + break; + default: + dev_err(dai->component->dev, "Invalid sampling rate %d\n", + params_rate(params)); + return -EINVAL; + } + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_TX_I2S_CTL, + TX_I2S_CTL_TX_I2S_FS_RATE_MASK, tx_fs_rate); + break; + case SNDRV_PCM_STREAM_PLAYBACK: + snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_RX_I2S_CTL, + RX_I2S_CTL_RX_I2S_FS_RATE_MASK, rx_fs_rate); + break; + default: + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_TX_I2S_CTL, + TX_I2S_CTL_TX_I2S_MODE_MASK, + TX_I2S_CTL_TX_I2S_MODE_16); + snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_RX_I2S_CTL, + RX_I2S_CTL_RX_I2S_MODE_MASK, + RX_I2S_CTL_RX_I2S_MODE_16); + break; + + case SNDRV_PCM_FORMAT_S32_LE: + snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_TX_I2S_CTL, + TX_I2S_CTL_TX_I2S_MODE_MASK, + TX_I2S_CTL_TX_I2S_MODE_32); + snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_RX_I2S_CTL, + RX_I2S_CTL_RX_I2S_MODE_MASK, + RX_I2S_CTL_RX_I2S_MODE_32); + break; + default: + dev_err(dai->dev, "%s: wrong format selected\n", __func__); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_route msm8916_wcd_digital_audio_map[] = { + + {"I2S RX1", NULL, "AIF1 Playback"}, + {"I2S RX2", NULL, "AIF1 Playback"}, + {"I2S RX3", NULL, "AIF1 Playback"}, + + {"AIF1 Capture", NULL, "I2S TX1"}, + {"AIF1 Capture", NULL, "I2S TX2"}, + {"AIF1 Capture", NULL, "I2S TX3"}, + + {"CIC1 MUX", "DMIC", "DEC1 MUX"}, + {"CIC1 MUX", "AMIC", "DEC1 MUX"}, + {"CIC2 MUX", "DMIC", "DEC2 MUX"}, + {"CIC2 MUX", "AMIC", "DEC2 MUX"}, + + /* Decimator Inputs */ + {"DEC1 MUX", "DMIC1", "DMIC1"}, + {"DEC1 MUX", "DMIC2", "DMIC2"}, + {"DEC1 MUX", "ADC1", "ADC1"}, + {"DEC1 MUX", "ADC2", "ADC2"}, + {"DEC1 MUX", "ADC3", "ADC3"}, + {"DEC1 MUX", NULL, "CDC_CONN"}, + + {"DEC2 MUX", "DMIC1", "DMIC1"}, + {"DEC2 MUX", "DMIC2", "DMIC2"}, + {"DEC2 MUX", "ADC1", "ADC1"}, + {"DEC2 MUX", "ADC2", "ADC2"}, + {"DEC2 MUX", "ADC3", "ADC3"}, + {"DEC2 MUX", NULL, "CDC_CONN"}, + + {"DMIC1", NULL, "DMIC_CLK"}, + {"DMIC2", NULL, "DMIC_CLK"}, + + {"I2S TX1", NULL, "CIC1 MUX"}, + {"I2S TX2", NULL, "CIC2 MUX"}, + + {"I2S TX1", NULL, "TX_I2S_CLK"}, + {"I2S TX2", NULL, "TX_I2S_CLK"}, + + {"TX_I2S_CLK", NULL, "MCLK"}, + {"TX_I2S_CLK", NULL, "PDM_CLK"}, + + {"ADC1", NULL, "LPASS_PDM_TX"}, + {"ADC2", NULL, "LPASS_PDM_TX"}, + {"ADC3", NULL, "LPASS_PDM_TX"}, + + {"I2S RX1", NULL, "RX_I2S_CLK"}, + {"I2S RX2", NULL, "RX_I2S_CLK"}, + {"I2S RX3", NULL, "RX_I2S_CLK"}, + + {"RX_I2S_CLK", NULL, "PDM_CLK"}, + {"RX_I2S_CLK", NULL, "MCLK"}, + {"RX_I2S_CLK", NULL, "CDC_CONN"}, + + /* RX1 PATH.. */ + {"PDM_RX1", NULL, "RX1 INT"}, + {"RX1 INT", NULL, "RX1 MIX1"}, + + {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, + + {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX1 MIX1 INP1", "IIR2", "IIR2"}, + + {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX1 MIX1 INP2", "IIR2", "IIR2"}, + + {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, + + /* RX2 PATH */ + {"PDM_RX2", NULL, "RX2 INT"}, + {"RX2 INT", NULL, "RX2 MIX1"}, + + {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP3"}, + + {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP1", "IIR1", "IIR1"}, + {"RX2 MIX1 INP1", "IIR2", "IIR2"}, + + {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP1", "IIR1", "IIR1"}, + {"RX2 MIX1 INP1", "IIR2", "IIR2"}, + + {"RX2 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP3", "RX3", "I2S RX3"}, + + /* RX3 PATH */ + {"PDM_RX3", NULL, "RX3 INT"}, + {"RX3 INT", NULL, "RX3 MIX1"}, + + {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP3"}, + + {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP1", "IIR1", "IIR1"}, + {"RX3 MIX1 INP1", "IIR2", "IIR2"}, + + {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP2", "IIR1", "IIR1"}, + {"RX3 MIX1 INP2", "IIR2", "IIR2"}, + + {"RX1 MIX2 INP1", "IIR1", "IIR1"}, + {"RX2 MIX2 INP1", "IIR1", "IIR1"}, + {"RX1 MIX2 INP1", "IIR2", "IIR2"}, + {"RX2 MIX2 INP1", "IIR2", "IIR2"}, + + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"}, + + {"IIR2", NULL, "IIR2 INP1 MUX"}, + {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"}, + + {"RX3 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP3", "RX3", "I2S RX3"}, + +}; + +static int msm8916_wcd_digital_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct msm8916_wcd_digital_priv *msm8916_wcd; + unsigned long mclk_rate; + + msm8916_wcd = snd_soc_component_get_drvdata(component); + snd_soc_component_update_bits(component, LPASS_CDC_CLK_MCLK_CTL, + MCLK_CTL_MCLK_EN_MASK, + MCLK_CTL_MCLK_EN_ENABLE); + snd_soc_component_update_bits(component, LPASS_CDC_CLK_PDM_CTL, + LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, + LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_FB); + + mclk_rate = clk_get_rate(msm8916_wcd->mclk); + switch (mclk_rate) { + case 12288000: + snd_soc_component_update_bits(component, LPASS_CDC_TOP_CTL, + TOP_CTL_DIG_MCLK_FREQ_MASK, + TOP_CTL_DIG_MCLK_FREQ_F_12_288MHZ); + break; + case 9600000: + snd_soc_component_update_bits(component, LPASS_CDC_TOP_CTL, + TOP_CTL_DIG_MCLK_FREQ_MASK, + TOP_CTL_DIG_MCLK_FREQ_F_9_6MHZ); + break; + default: + dev_err(component->dev, "Invalid mclk rate %ld\n", mclk_rate); + break; + } + return 0; +} + +static void msm8916_wcd_digital_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_component_update_bits(dai->component, LPASS_CDC_CLK_PDM_CTL, + LPASS_CDC_CLK_PDM_CTL_PDM_CLK_SEL_MASK, 0); +} + +static const struct snd_soc_dai_ops msm8916_wcd_digital_dai_ops = { + .startup = msm8916_wcd_digital_startup, + .shutdown = msm8916_wcd_digital_shutdown, + .hw_params = msm8916_wcd_digital_hw_params, +}; + +static struct snd_soc_dai_driver msm8916_wcd_digital_dai[] = { + [0] = { + .name = "msm8916_wcd_digital_i2s_rx1", + .id = 0, + .playback = { + .stream_name = "AIF1 Playback", + .rates = MSM8916_WCD_DIGITAL_RATES, + .formats = MSM8916_WCD_DIGITAL_FORMATS, + .channels_min = 1, + .channels_max = 3, + }, + .ops = &msm8916_wcd_digital_dai_ops, + }, + [1] = { + .name = "msm8916_wcd_digital_i2s_tx1", + .id = 1, + .capture = { + .stream_name = "AIF1 Capture", + .rates = MSM8916_WCD_DIGITAL_RATES, + .formats = MSM8916_WCD_DIGITAL_FORMATS, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &msm8916_wcd_digital_dai_ops, + }, +}; + +static const struct snd_soc_component_driver msm8916_wcd_digital = { + .probe = msm8916_wcd_digital_component_probe, + .set_sysclk = msm8916_wcd_digital_component_set_sysclk, + .controls = msm8916_wcd_digital_snd_controls, + .num_controls = ARRAY_SIZE(msm8916_wcd_digital_snd_controls), + .dapm_widgets = msm8916_wcd_digital_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm8916_wcd_digital_dapm_widgets), + .dapm_routes = msm8916_wcd_digital_audio_map, + .num_dapm_routes = ARRAY_SIZE(msm8916_wcd_digital_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config msm8916_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = LPASS_CDC_TX2_DMIC_CTL, + .cache_type = REGCACHE_FLAT, +}; + +static int msm8916_wcd_digital_probe(struct platform_device *pdev) +{ + struct msm8916_wcd_digital_priv *priv; + struct device *dev = &pdev->dev; + void __iomem *base; + struct regmap *digital_map; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + digital_map = + devm_regmap_init_mmio(&pdev->dev, base, + &msm8916_codec_regmap_config); + if (IS_ERR(digital_map)) + return PTR_ERR(digital_map); + + ret = msm8916_wcd_digital_get_clks(pdev, priv); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(priv->ahbclk); + if (ret < 0) { + dev_err(dev, "failed to enable ahbclk %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(dev, "failed to enable mclk %d\n", ret); + goto err_clk; + } + + dev_set_drvdata(dev, priv); + + ret = devm_snd_soc_register_component(dev, &msm8916_wcd_digital, + msm8916_wcd_digital_dai, + ARRAY_SIZE(msm8916_wcd_digital_dai)); + if (ret) + goto err_mclk; + + return 0; + +err_mclk: + clk_disable_unprepare(priv->mclk); +err_clk: + clk_disable_unprepare(priv->ahbclk); + return ret; +} + +static void msm8916_wcd_digital_remove(struct platform_device *pdev) +{ + struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(priv->mclk); + clk_disable_unprepare(priv->ahbclk); +} + +static const struct of_device_id msm8916_wcd_digital_match_table[] = { + { .compatible = "qcom,msm8916-wcd-digital-codec" }, + { } +}; + +MODULE_DEVICE_TABLE(of, msm8916_wcd_digital_match_table); + +static struct platform_driver msm8916_wcd_digital_driver = { + .driver = { + .name = "msm8916-wcd-digital-codec", + .of_match_table = msm8916_wcd_digital_match_table, + }, + .probe = msm8916_wcd_digital_probe, + .remove_new = msm8916_wcd_digital_remove, +}; + +module_platform_driver(msm8916_wcd_digital_driver); + +MODULE_AUTHOR("Srinivas Kandagatla "); +MODULE_DESCRIPTION("MSM8916 WCD Digital Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c new file mode 100644 index 0000000000..d2cf4847ee --- /dev/null +++ b/sound/soc/codecs/mt6351.c @@ -0,0 +1,1496 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt6351.c -- mt6351 ALSA SoC audio codec driver +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "mt6351.h" + +/* MT6351_TOP_CLKSQ */ +#define RG_CLKSQ_EN_AUD_BIT (0) + +/* MT6351_TOP_CKPDN_CON0 */ +#define RG_AUDNCP_CK_PDN_BIT (12) +#define RG_AUDIF_CK_PDN_BIT (13) +#define RG_AUD_CK_PDN_BIT (14) +#define RG_ZCD13M_CK_PDN_BIT (15) + +/* MT6351_AUDDEC_ANA_CON0 */ +#define RG_AUDDACLPWRUP_VAUDP32_BIT (0) +#define RG_AUDDACRPWRUP_VAUDP32_BIT (1) +#define RG_AUD_DAC_PWR_UP_VA32_BIT (2) +#define RG_AUD_DAC_PWL_UP_VA32_BIT (3) + +#define RG_AUDHSPWRUP_VAUDP32_BIT (4) + +#define RG_AUDHPLPWRUP_VAUDP32_BIT (5) +#define RG_AUDHPRPWRUP_VAUDP32_BIT (6) + +#define RG_AUDHSMUXINPUTSEL_VAUDP32_SFT (7) +#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK (0x3) + +#define RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT (9) +#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK (0x3) + +#define RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT (11) +#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK (0x3) + +#define RG_AUDHSSCDISABLE_VAUDP32 (13) +#define RG_AUDHPLSCDISABLE_VAUDP32_BIT (14) +#define RG_AUDHPRSCDISABLE_VAUDP32_BIT (15) + +/* MT6351_AUDDEC_ANA_CON1 */ +#define RG_HSOUTPUTSTBENH_VAUDP32_BIT (8) + +/* MT6351_AUDDEC_ANA_CON3 */ +#define RG_AUDLOLPWRUP_VAUDP32_BIT (2) + +#define RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT (3) +#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK (0x3) + +#define RG_AUDLOLSCDISABLE_VAUDP32_BIT (5) +#define RG_LOOUTPUTSTBENH_VAUDP32_BIT (9) + +/* MT6351_AUDDEC_ANA_CON6 */ +#define RG_ABIDEC_RSVD0_VAUDP32_HPL_BIT (8) +#define RG_ABIDEC_RSVD0_VAUDP32_HPR_BIT (9) +#define RG_ABIDEC_RSVD0_VAUDP32_HS_BIT (10) +#define RG_ABIDEC_RSVD0_VAUDP32_LOL_BIT (11) + +/* MT6351_AUDDEC_ANA_CON9 */ +#define RG_AUDIBIASPWRDN_VAUDP32_BIT (8) +#define RG_RSTB_DECODER_VA32_BIT (9) +#define RG_AUDGLB_PWRDN_VA32_BIT (12) + +#define RG_LCLDO_DEC_EN_VA32_BIT (13) +#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_BIT (15) +/* MT6351_AUDDEC_ANA_CON10 */ +#define RG_NVREG_EN_VAUDP32_BIT (8) + +#define RG_AUDGLB_LP2_VOW_EN_VA32 10 + +/* MT6351_AFE_UL_DL_CON0 */ +#define RG_AFE_ON_BIT (0) + +/* MT6351_AFE_DL_SRC2_CON0_L */ +#define RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT (0) + +/* MT6351_AFE_UL_SRC_CON0_L */ +#define UL_SRC_ON_TMP_CTL (0) + +/* MT6351_AFE_TOP_CON0 */ +#define RG_DL_SINE_ON_SFT (0) +#define RG_DL_SINE_ON_MASK (0x1) + +#define RG_UL_SINE_ON_SFT (1) +#define RG_UL_SINE_ON_MASK (0x1) + +/* MT6351_AUDIO_TOP_CON0 */ +#define AUD_TOP_PDN_RESERVED_BIT 0 +#define AUD_TOP_PWR_CLK_DIS_CTL_BIT 2 +#define AUD_TOP_PDN_ADC_CTL_BIT 5 +#define AUD_TOP_PDN_DAC_CTL_BIT 6 +#define AUD_TOP_PDN_AFE_CTL_BIT 7 + +/* MT6351_AFE_SGEN_CFG0 */ +#define SGEN_C_MUTE_SW_CTL_BIT 6 +#define SGEN_C_DAC_EN_CTL_BIT 7 + +/* MT6351_AFE_NCP_CFG0 */ +#define RG_NCP_ON_BIT 0 + +/* MT6351_LDO_VUSB33_CON0 */ +#define RG_VUSB33_EN 1 +#define RG_VUSB33_ON_CTRL 3 + +/* MT6351_LDO_VA18_CON0 */ +#define RG_VA18_EN 1 +#define RG_VA18_ON_CTRL 3 + +/* MT6351_AUDENC_ANA_CON0 */ +#define RG_AUDPREAMPLON 0 +#define RG_AUDPREAMPLDCCEN 1 +#define RG_AUDPREAMPLDCPRECHARGE 2 + +#define RG_AUDPREAMPLINPUTSEL_SFT (4) +#define RG_AUDPREAMPLINPUTSEL_MASK (0x3) + +#define RG_AUDADCLPWRUP 12 + +#define RG_AUDADCLINPUTSEL_SFT (13) +#define RG_AUDADCLINPUTSEL_MASK (0x3) + +/* MT6351_AUDENC_ANA_CON1 */ +#define RG_AUDPREAMPRON 0 +#define RG_AUDPREAMPRDCCEN 1 +#define RG_AUDPREAMPRDCPRECHARGE 2 + +#define RG_AUDPREAMPRINPUTSEL_SFT (4) +#define RG_AUDPREAMPRINPUTSEL_MASK (0x3) + +#define RG_AUDADCRPWRUP 12 + +#define RG_AUDADCRINPUTSEL_SFT (13) +#define RG_AUDADCRINPUTSEL_MASK (0x3) + +/* MT6351_AUDENC_ANA_CON3 */ +#define RG_AUDADCCLKRSTB 6 + +/* MT6351_AUDENC_ANA_CON9 */ +#define RG_AUDPWDBMICBIAS0 0 +#define RG_AUDMICBIAS0VREF 4 +#define RG_AUDMICBIAS0LOWPEN 7 + +#define RG_AUDPWDBMICBIAS2 8 +#define RG_AUDMICBIAS2VREF 12 +#define RG_AUDMICBIAS2LOWPEN 15 + +/* MT6351_AUDENC_ANA_CON10 */ +#define RG_AUDPWDBMICBIAS1 0 +#define RG_AUDMICBIAS1DCSW1NEN 2 +#define RG_AUDMICBIAS1VREF 4 +#define RG_AUDMICBIAS1LOWPEN 7 + +enum { + AUDIO_ANALOG_VOLUME_HSOUTL, + AUDIO_ANALOG_VOLUME_HSOUTR, + AUDIO_ANALOG_VOLUME_HPOUTL, + AUDIO_ANALOG_VOLUME_HPOUTR, + AUDIO_ANALOG_VOLUME_LINEOUTL, + AUDIO_ANALOG_VOLUME_LINEOUTR, + AUDIO_ANALOG_VOLUME_MICAMP1, + AUDIO_ANALOG_VOLUME_MICAMP2, + AUDIO_ANALOG_VOLUME_TYPE_MAX +}; + +/* Supply subseq */ +enum { + SUPPLY_SUBSEQ_SETTING, + SUPPLY_SUBSEQ_ENABLE, + SUPPLY_SUBSEQ_MICBIAS, +}; + +#define REG_STRIDE 2 + +struct mt6351_priv { + struct device *dev; + struct regmap *regmap; + + unsigned int dl_rate; + unsigned int ul_rate; + + int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX]; + + int hp_en_counter; +}; + +static void set_hp_gain_zero(struct snd_soc_component *cmpnt) +{ + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON2, + 0x1f << 7, 0x8 << 7); + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON2, + 0x1f << 0, 0x8 << 0); +} + +static unsigned int get_cap_reg_val(struct snd_soc_component *cmpnt, + unsigned int rate) +{ + switch (rate) { + case 8000: + return 0; + case 16000: + return 1; + case 32000: + return 2; + case 48000: + return 3; + case 96000: + return 4; + case 192000: + return 5; + default: + dev_warn(cmpnt->dev, "%s(), error rate %d, return 3", + __func__, rate); + return 3; + } +} + +static unsigned int get_play_reg_val(struct snd_soc_component *cmpnt, + unsigned int rate) +{ + switch (rate) { + case 8000: + return 0; + case 11025: + return 1; + case 12000: + return 2; + case 16000: + return 3; + case 22050: + return 4; + case 24000: + return 5; + case 32000: + return 6; + case 44100: + return 7; + case 48000: + case 96000: + case 192000: + return 8; + default: + dev_warn(cmpnt->dev, "%s(), error rate %d, return 8", + __func__, rate); + return 8; + } +} + +static int mt6351_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *cmpnt = dai->component; + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int rate = params_rate(params); + + dev_dbg(priv->dev, "%s(), substream->stream %d, rate %d\n", + __func__, substream->stream, rate); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + priv->dl_rate = rate; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + priv->ul_rate = rate; + + return 0; +} + +static const struct snd_soc_dai_ops mt6351_codec_dai_ops = { + .hw_params = mt6351_codec_dai_hw_params, +}; + +#define MT6351_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) + +static struct snd_soc_dai_driver mt6351_dai_driver[] = { + { + .name = "mt6351-snd-codec-aif1", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6351_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6351_FORMATS, + }, + .ops = &mt6351_codec_dai_ops, + }, +}; + +enum { + HP_GAIN_SET_ZERO, + HP_GAIN_RESTORE, +}; + +static void hp_gain_ramp_set(struct snd_soc_component *cmpnt, int hp_gain_ctl) +{ + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int idx, old_idx, offset, reg_idx; + + if (hp_gain_ctl == HP_GAIN_SET_ZERO) { + idx = 8; /* 0dB */ + old_idx = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + } else { + idx = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + old_idx = 8; /* 0dB */ + } + dev_dbg(priv->dev, "%s(), idx %d, old_idx %d\n", + __func__, idx, old_idx); + + if (idx > old_idx) + offset = idx - old_idx; + else + offset = old_idx - idx; + + reg_idx = old_idx; + + while (offset > 0) { + reg_idx = idx > old_idx ? reg_idx + 1 : reg_idx - 1; + + /* check valid range, and set value */ + if ((reg_idx >= 0 && reg_idx <= 0x12) || reg_idx == 0x1f) { + regmap_update_bits(cmpnt->regmap, + MT6351_ZCD_CON2, + 0xf9f, + (reg_idx << 7) | reg_idx); + usleep_range(100, 120); + } + offset--; + } +} + +static void hp_zcd_enable(struct snd_soc_component *cmpnt) +{ + /* Enable ZCD, for minimize pop noise */ + /* when adjust gain during HP buffer on */ + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x7 << 8, 0x1 << 8); + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 7, 0x0 << 7); + + /* timeout, 1=5ms, 0=30ms */ + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 6, 0x1 << 6); + + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x3 << 4, 0x0 << 4); + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x7 << 1, 0x5 << 1); + regmap_update_bits(cmpnt->regmap, MT6351_ZCD_CON0, 0x1 << 0, 0x1 << 0); +} + +static void hp_zcd_disable(struct snd_soc_component *cmpnt) +{ + regmap_write(cmpnt->regmap, MT6351_ZCD_CON0, 0x0000); +} + +static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); +static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new mt6351_snd_controls[] = { + /* dl pga gain */ + SOC_DOUBLE_TLV("Headphone Volume", + MT6351_ZCD_CON2, 0, 7, 0x12, 1, + playback_tlv), + SOC_DOUBLE_TLV("Lineout Volume", + MT6351_ZCD_CON1, 0, 7, 0x12, 1, + playback_tlv), + SOC_SINGLE_TLV("Handset Volume", + MT6351_ZCD_CON3, 0, 0x12, 1, + playback_tlv), + /* ul pga gain */ + SOC_DOUBLE_R_TLV("PGA Volume", + MT6351_AUDENC_ANA_CON0, MT6351_AUDENC_ANA_CON1, + 8, 4, 0, + pga_tlv), +}; + +/* MUX */ + +/* LOL MUX */ +static const char *const lo_in_mux_map[] = { + "Open", "Mute", "Playback", "Test Mode", +}; + +static int lo_in_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(lo_in_mux_map_enum, + MT6351_AUDDEC_ANA_CON3, + RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT, + RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK, + lo_in_mux_map, + lo_in_mux_map_value); + +static const struct snd_kcontrol_new lo_in_mux_control = + SOC_DAPM_ENUM("In Select", lo_in_mux_map_enum); + +/*HP MUX */ +static const char *const hp_in_mux_map[] = { + "Open", "LoudSPK Playback", "Audio Playback", "Test Mode", +}; + +static int hp_in_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(hpl_in_mux_map_enum, + MT6351_AUDDEC_ANA_CON0, + RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT, + RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK, + hp_in_mux_map, + hp_in_mux_map_value); + +static const struct snd_kcontrol_new hpl_in_mux_control = + SOC_DAPM_ENUM("HPL Select", hpl_in_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hpr_in_mux_map_enum, + MT6351_AUDDEC_ANA_CON0, + RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT, + RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK, + hp_in_mux_map, + hp_in_mux_map_value); + +static const struct snd_kcontrol_new hpr_in_mux_control = + SOC_DAPM_ENUM("HPR Select", hpr_in_mux_map_enum); + +/* RCV MUX */ +static const char *const rcv_in_mux_map[] = { + "Open", "Mute", "Voice Playback", "Test Mode", +}; + +static int rcv_in_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rcv_in_mux_map_enum, + MT6351_AUDDEC_ANA_CON0, + RG_AUDHSMUXINPUTSEL_VAUDP32_SFT, + RG_AUDHSMUXINPUTSEL_VAUDP32_MASK, + rcv_in_mux_map, + rcv_in_mux_map_value); + +static const struct snd_kcontrol_new rcv_in_mux_control = + SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum); + +/* DAC In MUX */ +static const char *const dac_in_mux_map[] = { + "Normal Path", "Sgen", +}; + +static int dac_in_mux_map_value[] = { + 0x0, 0x1, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum, + MT6351_AFE_TOP_CON0, + RG_DL_SINE_ON_SFT, + RG_DL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new dac_in_mux_control = + SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum); + +/* AIF Out MUX */ +static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum, + MT6351_AFE_TOP_CON0, + RG_UL_SINE_ON_SFT, + RG_UL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new aif_out_mux_control = + SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum); + +/* ADC L MUX */ +static const char *const adc_left_mux_map[] = { + "Idle", "AIN0", "Left Preamplifier", "Idle_1", +}; + +static int adc_left_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum, + MT6351_AUDENC_ANA_CON0, + RG_AUDADCLINPUTSEL_SFT, + RG_AUDADCLINPUTSEL_MASK, + adc_left_mux_map, + adc_left_mux_map_value); + +static const struct snd_kcontrol_new adc_left_mux_control = + SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum); + +/* ADC R MUX */ +static const char *const adc_right_mux_map[] = { + "Idle", "AIN0", "Right Preamplifier", "Idle_1", +}; + +static int adc_right_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum, + MT6351_AUDENC_ANA_CON1, + RG_AUDADCRINPUTSEL_SFT, + RG_AUDADCRINPUTSEL_MASK, + adc_right_mux_map, + adc_right_mux_map_value); + +static const struct snd_kcontrol_new adc_right_mux_control = + SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum); + +/* PGA L MUX */ +static const char *const pga_left_mux_map[] = { + "None", "AIN0", "AIN1", "AIN2", +}; + +static int pga_left_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum, + MT6351_AUDENC_ANA_CON0, + RG_AUDPREAMPLINPUTSEL_SFT, + RG_AUDPREAMPLINPUTSEL_MASK, + pga_left_mux_map, + pga_left_mux_map_value); + +static const struct snd_kcontrol_new pga_left_mux_control = + SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum); + +/* PGA R MUX */ +static const char *const pga_right_mux_map[] = { + "None", "AIN0", "AIN3", "AIN2", +}; + +static int pga_right_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum, + MT6351_AUDENC_ANA_CON1, + RG_AUDPREAMPRINPUTSEL_SFT, + RG_AUDPREAMPRINPUTSEL_MASK, + pga_right_mux_map, + pga_right_mux_map_value); + +static const struct snd_kcontrol_new pga_right_mux_control = + SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum); + +static int mt_reg_set_clr_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->on_val) { + /* SET REG */ + regmap_update_bits(cmpnt->regmap, + w->reg + REG_STRIDE, + 0x1 << w->shift, + 0x1 << w->shift); + } else { + /* CLR REG */ + regmap_update_bits(cmpnt->regmap, + w->reg + REG_STRIDE * 2, + 0x1 << w->shift, + 0x1 << w->shift); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (w->off_val) { + /* SET REG */ + regmap_update_bits(cmpnt->regmap, + w->reg + REG_STRIDE, + 0x1 << w->shift, + 0x1 << w->shift); + } else { + /* CLR REG */ + regmap_update_bits(cmpnt->regmap, + w->reg + REG_STRIDE * 2, + 0x1 << w->shift, + 0x1 << w->shift); + } + break; + default: + break; + } + + return 0; +} + +static int mt_ncp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(cmpnt->regmap, MT6351_AFE_NCP_CFG1, + 0xffff, 0x1515); + /* NCP: ck1 and ck2 clock frequecy adjust configure */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_NCP_CFG0, + 0xfffe, 0x8C00); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(250, 270); + break; + default: + break; + } + + return 0; +} + +static int mt_sgen_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(cmpnt->regmap, MT6351_AFE_SGEN_CFG0, + 0xffef, 0x0008); + regmap_update_bits(cmpnt->regmap, MT6351_AFE_SGEN_CFG1, + 0xffff, 0x0101); + break; + default: + break; + } + + return 0; +} + +static int mt_aif_in_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n", + __func__, event, priv->dl_rate); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* sdm audio fifo clock power on */ + regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2, + 0xffff, 0x0006); + /* scrambler clock on enable */ + regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON0, + 0xffff, 0xC3A1); + /* sdm power on */ + regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2, + 0xffff, 0x0003); + /* sdm fifo enable */ + regmap_update_bits(cmpnt->regmap, MT6351_AFUNC_AUD_CON2, + 0xffff, 0x000B); + /* set attenuation gain */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_DL_SDM_CON1, + 0xffff, 0x001E); + + regmap_write(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG0, + (get_play_reg_val(cmpnt, priv->dl_rate) << 12) | + 0x330); + regmap_write(cmpnt->regmap, MT6351_AFE_DL_SRC2_CON0_H, + (get_play_reg_val(cmpnt, priv->dl_rate) << 12) | + 0x300); + + regmap_update_bits(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG2, + 0x8000, 0x8000); + break; + default: + break; + } + + return 0; +} + +static int mt_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int reg; + + dev_dbg(priv->dev, "%s(), event 0x%x, hp_en_counter %d\n", + __func__, event, priv->hp_en_counter); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + priv->hp_en_counter++; + if (priv->hp_en_counter > 1) + break; /* already enabled, do nothing */ + else if (priv->hp_en_counter <= 0) + dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n", + __func__, + priv->hp_en_counter); + + hp_zcd_disable(cmpnt); + + /* from yoyo HQA script */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON6, + 0x0700, 0x0700); + + /* save target gain to restore after hardware open complete */ + regmap_read(cmpnt->regmap, MT6351_ZCD_CON2, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] = reg & 0x1f; + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = (reg >> 7) & 0x1f; + + /* Set HPR/HPL gain as minimum (~ -40dB) */ + regmap_update_bits(cmpnt->regmap, + MT6351_ZCD_CON2, 0xffff, 0x0F9F); + /* Set HS gain as minimum (~ -40dB) */ + regmap_update_bits(cmpnt->regmap, + MT6351_ZCD_CON3, 0xffff, 0x001F); + /* De_OSC of HP */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON2, + 0x0001, 0x0001); + /* enable output STBENH */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1, + 0xffff, 0x2000); + /* De_OSC of voice, enable output STBENH */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1, + 0xffff, 0x2100); + /* Enable voice driver */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0, + 0x0010, 0xE090); + /* Enable pre-charge buffer */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1, + 0xffff, 0x2140); + + usleep_range(50, 60); + + /* Apply digital DC compensation value to DAC */ + set_hp_gain_zero(cmpnt); + + /* Enable HPR/HPL */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1, + 0xffff, 0x2100); + /* Disable pre-charge buffer */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON1, + 0xffff, 0x2000); + /* Disable De_OSC of voice */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0, + 0x0010, 0xF4EF); + /* Disable voice buffer */ + + /* from yoyo HQ */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON6, + 0x0700, 0x0300); + + /* Enable ZCD, for minimize pop noise */ + /* when adjust gain during HP buffer on */ + hp_zcd_enable(cmpnt); + + /* apply volume setting */ + hp_gain_ramp_set(cmpnt, HP_GAIN_RESTORE); + + break; + case SND_SOC_DAPM_PRE_PMD: + priv->hp_en_counter--; + if (priv->hp_en_counter > 0) + break; /* still being used, don't close */ + else if (priv->hp_en_counter < 0) + dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n", + __func__, + priv->hp_en_counter); + + /* Disable AUD_ZCD */ + hp_zcd_disable(cmpnt); + + /* Set HPR/HPL gain as -1dB, step by step */ + hp_gain_ramp_set(cmpnt, HP_GAIN_SET_ZERO); + + set_hp_gain_zero(cmpnt); + break; + case SND_SOC_DAPM_POST_PMD: + if (priv->hp_en_counter > 0) + break; /* still being used, don't close */ + else if (priv->hp_en_counter < 0) + dev_err(priv->dev, "%s(), hp_en_counter %d <= 0\n", + __func__, + priv->hp_en_counter); + + /* reset*/ + regmap_update_bits(cmpnt->regmap, + MT6351_AUDDEC_ANA_CON6, + 0x0700, + 0x0000); + /* De_OSC of HP */ + regmap_update_bits(cmpnt->regmap, + MT6351_AUDDEC_ANA_CON2, + 0x0001, + 0x0000); + + /* apply volume setting */ + hp_gain_ramp_set(cmpnt, HP_GAIN_RESTORE); + break; + default: + break; + } + + return 0; +} + +static int mt_aif_out_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n", + __func__, event, priv->ul_rate); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* dcclk_div=11'b00100000011, dcclk_ref_ck_sel=2'b00 */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0, + 0xffff, 0x2062); + /* dcclk_pdn=1'b0 */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0, + 0xffff, 0x2060); + /* dcclk_gen_on=1'b1 */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_DCCLK_CFG0, + 0xffff, 0x2061); + + /* UL sample rate and mode configure */ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_UL_SRC_CON0_H, + 0x000E, + get_cap_reg_val(cmpnt, priv->ul_rate) << 1); + + /* fixed 260k path for 8/16/32/48 */ + if (priv->ul_rate <= 48000) { + /* anc ul path src on */ + regmap_update_bits(cmpnt->regmap, + MT6351_AFE_HPANC_CFG0, + 0x1 << 1, + 0x1 << 1); + /* ANC clk pdn release */ + regmap_update_bits(cmpnt->regmap, + MT6351_AFE_HPANC_CFG0, + 0x1 << 0, + 0x0 << 0); + } + break; + case SND_SOC_DAPM_PRE_PMD: + /* fixed 260k path for 8/16/32/48 */ + if (priv->ul_rate <= 48000) { + /* anc ul path src on */ + regmap_update_bits(cmpnt->regmap, + MT6351_AFE_HPANC_CFG0, + 0x1 << 1, + 0x0 << 1); + /* ANC clk pdn release */ + regmap_update_bits(cmpnt->regmap, + MT6351_AFE_HPANC_CFG0, + 0x1 << 0, + 0x1 << 0); + } + break; + default: + break; + } + + return 0; +} + +static int mt_adc_clkgen_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Audio ADC clock gen. mode: 00_divided by 2 (Normal) */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON3, + 0x3 << 4, 0x0); + break; + case SND_SOC_DAPM_POST_PMU: + /* ADC CLK from: 00_13MHz from CLKSQ (Default) */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON3, + 0x3 << 2, 0x0); + break; + default: + break; + } + return 0; +} + +static int mt_pga_left_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Audio L PGA precharge on */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0, + 0x3 << RG_AUDPREAMPLDCPRECHARGE, + 0x1 << RG_AUDPREAMPLDCPRECHARGE); + /* Audio L PGA mode: 1_DCC */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0, + 0x3 << RG_AUDPREAMPLDCCEN, + 0x1 << RG_AUDPREAMPLDCCEN); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(100, 120); + /* Audio L PGA precharge off */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON0, + 0x3 << RG_AUDPREAMPLDCPRECHARGE, + 0x0 << RG_AUDPREAMPLDCPRECHARGE); + break; + default: + break; + } + return 0; +} + +static int mt_pga_right_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Audio R PGA precharge on */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1, + 0x3 << RG_AUDPREAMPRDCPRECHARGE, + 0x1 << RG_AUDPREAMPRDCPRECHARGE); + /* Audio R PGA mode: 1_DCC */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1, + 0x3 << RG_AUDPREAMPRDCCEN, + 0x1 << RG_AUDPREAMPRDCCEN); + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(100, 120); + /* Audio R PGA precharge off */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON1, + 0x3 << RG_AUDPREAMPRDCPRECHARGE, + 0x0 << RG_AUDPREAMPRDCPRECHARGE); + break; + default: + break; + } + return 0; +} + +static int mt_mic_bias_0_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* MIC Bias 0 LowPower: 0_Normal */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x3 << RG_AUDMICBIAS0LOWPEN, 0x0); + /* MISBIAS0 = 1P9V */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x7 << RG_AUDMICBIAS0VREF, + 0x2 << RG_AUDMICBIAS0VREF); + break; + case SND_SOC_DAPM_POST_PMD: + /* MISBIAS0 = 1P97 */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x7 << RG_AUDMICBIAS0VREF, + 0x0 << RG_AUDMICBIAS0VREF); + break; + default: + break; + } + return 0; +} + +static int mt_mic_bias_1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* MIC Bias 1 LowPower: 0_Normal */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10, + 0x3 << RG_AUDMICBIAS1LOWPEN, 0x0); + /* MISBIAS1 = 2P7V */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10, + 0x7 << RG_AUDMICBIAS1VREF, + 0x7 << RG_AUDMICBIAS1VREF); + break; + case SND_SOC_DAPM_POST_PMD: + /* MISBIAS1 = 1P7V */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON10, + 0x7 << RG_AUDMICBIAS1VREF, + 0x0 << RG_AUDMICBIAS1VREF); + break; + default: + break; + } + return 0; +} + +static int mt_mic_bias_2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* MIC Bias 2 LowPower: 0_Normal */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x3 << RG_AUDMICBIAS2LOWPEN, 0x0); + /* MISBIAS2 = 1P9V */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x7 << RG_AUDMICBIAS2VREF, + 0x2 << RG_AUDMICBIAS2VREF); + break; + case SND_SOC_DAPM_POST_PMD: + /* MISBIAS2 = 1P97 */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDENC_ANA_CON9, + 0x7 << RG_AUDMICBIAS2VREF, + 0x0 << RG_AUDMICBIAS2VREF); + break; + default: + break; + } + return 0; +} + +/* DAPM Widgets */ +static const struct snd_soc_dapm_widget mt6351_dapm_widgets[] = { + /* Digital Clock */ + SND_SOC_DAPM_SUPPLY("AUDIO_TOP_AFE_CTL", MT6351_AUDIO_TOP_CON0, + AUD_TOP_PDN_AFE_CTL_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("AUDIO_TOP_DAC_CTL", MT6351_AUDIO_TOP_CON0, + AUD_TOP_PDN_DAC_CTL_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("AUDIO_TOP_ADC_CTL", MT6351_AUDIO_TOP_CON0, + AUD_TOP_PDN_ADC_CTL_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("AUDIO_TOP_PWR_CLK", MT6351_AUDIO_TOP_CON0, + AUD_TOP_PWR_CLK_DIS_CTL_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("AUDIO_TOP_PDN_RESERVED", MT6351_AUDIO_TOP_CON0, + AUD_TOP_PDN_RESERVED_BIT, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("NCP", MT6351_AFE_NCP_CFG0, + RG_NCP_ON_BIT, 0, + mt_ncp_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* Global Supply*/ + SND_SOC_DAPM_SUPPLY("AUDGLB", MT6351_AUDDEC_ANA_CON9, + RG_AUDGLB_PWRDN_VA32_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKSQ Audio", MT6351_TOP_CLKSQ, + RG_CLKSQ_EN_AUD_BIT, 0, + mt_reg_set_clr_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("ZCD13M_CK", MT6351_TOP_CKPDN_CON0, + RG_ZCD13M_CK_PDN_BIT, 1, + mt_reg_set_clr_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("AUD_CK", MT6351_TOP_CKPDN_CON0, + RG_AUD_CK_PDN_BIT, 1, + mt_reg_set_clr_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("AUDIF_CK", MT6351_TOP_CKPDN_CON0, + RG_AUDIF_CK_PDN_BIT, 1, + mt_reg_set_clr_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("AUDNCP_CK", MT6351_TOP_CKPDN_CON0, + RG_AUDNCP_CK_PDN_BIT, 1, + mt_reg_set_clr_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("AFE_ON", MT6351_AFE_UL_DL_CON0, RG_AFE_ON_BIT, 0, + NULL, 0), + + /* AIF Rx*/ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "AIF1 Playback", 0, + MT6351_AFE_DL_SRC2_CON0_L, + RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT, 0, + mt_aif_in_event, SND_SOC_DAPM_PRE_PMU), + + /* DL Supply */ + SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("NV Regulator", MT6351_AUDDEC_ANA_CON10, + RG_NVREG_EN_VAUDP32_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AUD_CLK", MT6351_AUDDEC_ANA_CON9, + RG_RSTB_DECODER_VA32_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("IBIST", MT6351_AUDDEC_ANA_CON9, + RG_AUDIBIASPWRDN_VAUDP32_BIT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO", MT6351_AUDDEC_ANA_CON9, + RG_LCLDO_DEC_EN_VA32_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO_REMOTE_SENSE", MT6351_AUDDEC_ANA_CON9, + RG_LCLDO_DEC_REMOTE_SENSE_VA18_BIT, 0, NULL, 0), + + /* DAC */ + SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control), + + SND_SOC_DAPM_DAC("DACL", NULL, MT6351_AUDDEC_ANA_CON0, + RG_AUDDACLPWRUP_VAUDP32_BIT, 0), + SND_SOC_DAPM_SUPPLY("DACL_BIASGEN", MT6351_AUDDEC_ANA_CON0, + RG_AUD_DAC_PWL_UP_VA32_BIT, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DACR", NULL, MT6351_AUDDEC_ANA_CON0, + RG_AUDDACRPWRUP_VAUDP32_BIT, 0), + SND_SOC_DAPM_SUPPLY("DACR_BIASGEN", MT6351_AUDDEC_ANA_CON0, + RG_AUD_DAC_PWR_UP_VA32_BIT, 0, NULL, 0), + /* LOL */ + SND_SOC_DAPM_MUX("LOL Mux", SND_SOC_NOPM, 0, 0, &lo_in_mux_control), + + SND_SOC_DAPM_SUPPLY("LO Stability Enh", MT6351_AUDDEC_ANA_CON3, + RG_LOOUTPUTSTBENH_VAUDP32_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LOL Bias Gen", MT6351_AUDDEC_ANA_CON6, + RG_ABIDEC_RSVD0_VAUDP32_LOL_BIT, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("LOL Buffer", MT6351_AUDDEC_ANA_CON3, + RG_AUDLOLPWRUP_VAUDP32_BIT, 0, NULL, 0), + + /* Headphone */ + SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0, &hpl_in_mux_control), + SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0, &hpr_in_mux_control), + + SND_SOC_DAPM_OUT_DRV_E("HPL Power", MT6351_AUDDEC_ANA_CON0, + RG_AUDHPLPWRUP_VAUDP32_BIT, 0, NULL, 0, + mt_hp_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUT_DRV_E("HPR Power", MT6351_AUDDEC_ANA_CON0, + RG_AUDHPRPWRUP_VAUDP32_BIT, 0, NULL, 0, + mt_hp_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + + /* Receiver */ + SND_SOC_DAPM_MUX("RCV Mux", SND_SOC_NOPM, 0, 0, &rcv_in_mux_control), + + SND_SOC_DAPM_SUPPLY("RCV Stability Enh", MT6351_AUDDEC_ANA_CON1, + RG_HSOUTPUTSTBENH_VAUDP32_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RCV Bias Gen", MT6351_AUDDEC_ANA_CON6, + RG_ABIDEC_RSVD0_VAUDP32_HS_BIT, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("RCV Buffer", MT6351_AUDDEC_ANA_CON0, + RG_AUDHSPWRUP_VAUDP32_BIT, 0, NULL, 0), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("Receiver"), + SND_SOC_DAPM_OUTPUT("Headphone L"), + SND_SOC_DAPM_OUTPUT("Headphone R"), + SND_SOC_DAPM_OUTPUT("LINEOUT L"), + + /* SGEN */ + SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6351_AFE_SGEN_CFG0, + SGEN_C_DAC_EN_CTL_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6351_AFE_SGEN_CFG0, + SGEN_C_MUTE_SW_CTL_BIT, 1, + mt_sgen_event, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6351_AFE_DL_SRC2_CON0_L, + RG_DL_2_SRC_ON_TMP_CTL_PRE_BIT, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("SGEN DL"), + + /* Uplinks */ + SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "AIF1 Capture", 0, + MT6351_AFE_UL_SRC_CON0_L, + UL_SRC_ON_TMP_CTL, 0, + mt_aif_out_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY_S("VUSB33_LDO", SUPPLY_SUBSEQ_ENABLE, + MT6351_LDO_VUSB33_CON0, RG_VUSB33_EN, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("VUSB33_LDO_CTRL", SUPPLY_SUBSEQ_SETTING, + MT6351_LDO_VUSB33_CON0, RG_VUSB33_ON_CTRL, 1, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("VA18_LDO", SUPPLY_SUBSEQ_ENABLE, + MT6351_LDO_VA18_CON0, RG_VA18_EN, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("VA18_LDO_CTRL", SUPPLY_SUBSEQ_SETTING, + MT6351_LDO_VA18_CON0, RG_VA18_ON_CTRL, 1, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("ADC CLKGEN", SUPPLY_SUBSEQ_ENABLE, + MT6351_AUDENC_ANA_CON3, RG_AUDADCCLKRSTB, 0, + mt_adc_clkgen_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* Uplinks MUX */ + SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0, + &aif_out_mux_control), + + SND_SOC_DAPM_MUX("ADC L Mux", SND_SOC_NOPM, 0, 0, + &adc_left_mux_control), + SND_SOC_DAPM_MUX("ADC R Mux", SND_SOC_NOPM, 0, 0, + &adc_right_mux_control), + + SND_SOC_DAPM_ADC("ADC L", NULL, + MT6351_AUDENC_ANA_CON0, RG_AUDADCLPWRUP, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, + MT6351_AUDENC_ANA_CON1, RG_AUDADCRPWRUP, 0), + + SND_SOC_DAPM_MUX("PGA L Mux", SND_SOC_NOPM, 0, 0, + &pga_left_mux_control), + SND_SOC_DAPM_MUX("PGA R Mux", SND_SOC_NOPM, 0, 0, + &pga_right_mux_control), + + SND_SOC_DAPM_PGA_E("PGA L", MT6351_AUDENC_ANA_CON0, RG_AUDPREAMPLON, 0, + NULL, 0, + mt_pga_left_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("PGA R", MT6351_AUDENC_ANA_CON1, RG_AUDPREAMPRON, 0, + NULL, 0, + mt_pga_right_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* main mic mic bias */ + SND_SOC_DAPM_SUPPLY_S("Mic Bias 0", SUPPLY_SUBSEQ_MICBIAS, + MT6351_AUDENC_ANA_CON9, RG_AUDPWDBMICBIAS0, 0, + mt_mic_bias_0_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /* ref mic mic bias */ + SND_SOC_DAPM_SUPPLY_S("Mic Bias 2", SUPPLY_SUBSEQ_MICBIAS, + MT6351_AUDENC_ANA_CON9, RG_AUDPWDBMICBIAS2, 0, + mt_mic_bias_2_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /* headset mic1/2 mic bias */ + SND_SOC_DAPM_SUPPLY_S("Mic Bias 1", SUPPLY_SUBSEQ_MICBIAS, + MT6351_AUDENC_ANA_CON10, RG_AUDPWDBMICBIAS1, 0, + mt_mic_bias_1_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("Mic Bias 1 DCC pull high", SUPPLY_SUBSEQ_MICBIAS, + MT6351_AUDENC_ANA_CON10, + RG_AUDMICBIAS1DCSW1NEN, 0, + NULL, 0), + + /* UL input */ + SND_SOC_DAPM_INPUT("AIN0"), + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), +}; + +static const struct snd_soc_dapm_route mt6351_dapm_routes[] = { + /* Capture */ + {"AIF1TX", NULL, "AIF Out Mux"}, + {"AIF1TX", NULL, "VUSB33_LDO"}, + {"VUSB33_LDO", NULL, "VUSB33_LDO_CTRL"}, + {"AIF1TX", NULL, "VA18_LDO"}, + {"VA18_LDO", NULL, "VA18_LDO_CTRL"}, + + {"AIF1TX", NULL, "AUDGLB"}, + {"AIF1TX", NULL, "CLKSQ Audio"}, + + {"AIF1TX", NULL, "AFE_ON"}, + + {"AIF1TX", NULL, "AUDIO_TOP_AFE_CTL"}, + {"AIF1TX", NULL, "AUDIO_TOP_ADC_CTL"}, + {"AIF1TX", NULL, "AUDIO_TOP_PWR_CLK"}, + {"AIF1TX", NULL, "AUDIO_TOP_PDN_RESERVED"}, + + {"AIF Out Mux", "Normal Path", "ADC L"}, + {"AIF Out Mux", "Normal Path", "ADC R"}, + + {"ADC L", NULL, "ADC L Mux"}, + {"ADC L", NULL, "AUD_CK"}, + {"ADC L", NULL, "AUDIF_CK"}, + {"ADC L", NULL, "ADC CLKGEN"}, + {"ADC R", NULL, "ADC R Mux"}, + {"ADC R", NULL, "AUD_CK"}, + {"ADC R", NULL, "AUDIF_CK"}, + {"ADC R", NULL, "ADC CLKGEN"}, + + {"ADC L Mux", "AIN0", "AIN0"}, + {"ADC L Mux", "Left Preamplifier", "PGA L"}, + + {"ADC R Mux", "AIN0", "AIN0"}, + {"ADC R Mux", "Right Preamplifier", "PGA R"}, + + {"PGA L", NULL, "PGA L Mux"}, + {"PGA R", NULL, "PGA R Mux"}, + + {"PGA L Mux", "AIN0", "AIN0"}, + {"PGA L Mux", "AIN1", "AIN1"}, + {"PGA L Mux", "AIN2", "AIN2"}, + + {"PGA R Mux", "AIN0", "AIN0"}, + {"PGA R Mux", "AIN3", "AIN3"}, + {"PGA R Mux", "AIN2", "AIN2"}, + + {"AIN0", NULL, "Mic Bias 0"}, + {"AIN2", NULL, "Mic Bias 2"}, + + {"AIN1", NULL, "Mic Bias 1"}, + {"AIN1", NULL, "Mic Bias 1 DCC pull high"}, + + /* DL Supply */ + {"DL Power Supply", NULL, "AUDGLB"}, + {"DL Power Supply", NULL, "CLKSQ Audio"}, + {"DL Power Supply", NULL, "ZCD13M_CK"}, + {"DL Power Supply", NULL, "AUD_CK"}, + {"DL Power Supply", NULL, "AUDIF_CK"}, + {"DL Power Supply", NULL, "AUDNCP_CK"}, + + {"DL Power Supply", NULL, "NV Regulator"}, + {"DL Power Supply", NULL, "AUD_CLK"}, + {"DL Power Supply", NULL, "IBIST"}, + {"DL Power Supply", NULL, "LDO"}, + {"LDO", NULL, "LDO_REMOTE_SENSE"}, + + /* DL Digital Supply */ + {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_PDN_RESERVED"}, + {"DL Digital Clock", NULL, "NCP"}, + {"DL Digital Clock", NULL, "AFE_ON"}, + + {"AIF_RX", NULL, "DL Digital Clock"}, + + /* DL Path */ + {"DAC In Mux", "Normal Path", "AIF_RX"}, + + {"DAC In Mux", "Sgen", "SGEN DL"}, + {"SGEN DL", NULL, "SGEN DL SRC"}, + {"SGEN DL", NULL, "SGEN MUTE"}, + {"SGEN DL", NULL, "SGEN DL Enable"}, + {"SGEN DL", NULL, "DL Digital Clock"}, + + {"DACL", NULL, "DAC In Mux"}, + {"DACL", NULL, "DL Power Supply"}, + {"DACL", NULL, "DACL_BIASGEN"}, + + {"DACR", NULL, "DAC In Mux"}, + {"DACR", NULL, "DL Power Supply"}, + {"DACR", NULL, "DACR_BIASGEN"}, + + {"LOL Mux", "Playback", "DACL"}, + + {"LOL Buffer", NULL, "LOL Mux"}, + {"LOL Buffer", NULL, "LO Stability Enh"}, + {"LOL Buffer", NULL, "LOL Bias Gen"}, + + {"LINEOUT L", NULL, "LOL Buffer"}, + + /* Headphone Path */ + {"HPL Mux", "Audio Playback", "DACL"}, + {"HPR Mux", "Audio Playback", "DACR"}, + + {"HPL Mux", "LoudSPK Playback", "DACL"}, + {"HPR Mux", "LoudSPK Playback", "DACR"}, + + {"HPL Power", NULL, "HPL Mux"}, + {"HPR Power", NULL, "HPR Mux"}, + + {"Headphone L", NULL, "HPL Power"}, + {"Headphone R", NULL, "HPR Power"}, + + /* Receiver Path */ + {"RCV Mux", "Voice Playback", "DACL"}, + + {"RCV Buffer", NULL, "RCV Mux"}, + {"RCV Buffer", NULL, "RCV Stability Enh"}, + {"RCV Buffer", NULL, "RCV Bias Gen"}, + + {"Receiver", NULL, "RCV Buffer"}, +}; + +static int mt6351_codec_init_reg(struct snd_soc_component *cmpnt) +{ + /* Disable CLKSQ 26MHz */ + regmap_update_bits(cmpnt->regmap, MT6351_TOP_CLKSQ, 0x0001, 0x0); + /* disable AUDGLB */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON9, + 0x1000, 0x1000); + /* Turn off AUDNCP_CLKDIV engine clock,Turn off AUD 26M */ + regmap_update_bits(cmpnt->regmap, MT6351_TOP_CKPDN_CON0_SET, + 0x3800, 0x3800); + /* Disable HeadphoneL/HeadphoneR/voice short circuit protection */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON0, + 0xe000, 0xe000); + /* [5] = 1, disable LO buffer left short circuit protection */ + regmap_update_bits(cmpnt->regmap, MT6351_AUDDEC_ANA_CON3, + 0x20, 0x20); + /* Reverse the PMIC clock*/ + regmap_update_bits(cmpnt->regmap, MT6351_AFE_PMIC_NEWIF_CFG2, + 0x8000, 0x8000); + return 0; +} + +static int mt6351_codec_probe(struct snd_soc_component *cmpnt) +{ + struct mt6351_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + snd_soc_component_init_regmap(cmpnt, priv->regmap); + + mt6351_codec_init_reg(cmpnt); + return 0; +} + +static const struct snd_soc_component_driver mt6351_soc_component_driver = { + .probe = mt6351_codec_probe, + .controls = mt6351_snd_controls, + .num_controls = ARRAY_SIZE(mt6351_snd_controls), + .dapm_widgets = mt6351_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt6351_dapm_widgets), + .dapm_routes = mt6351_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mt6351_dapm_routes), + .endianness = 1, +}; + +static int mt6351_codec_driver_probe(struct platform_device *pdev) +{ + struct mt6351_priv *priv; + + priv = devm_kzalloc(&pdev->dev, + sizeof(struct mt6351_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, priv); + + priv->dev = &pdev->dev; + + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->regmap) + return -ENODEV; + + dev_dbg(priv->dev, "%s(), dev name %s\n", + __func__, dev_name(&pdev->dev)); + + return devm_snd_soc_register_component(&pdev->dev, + &mt6351_soc_component_driver, + mt6351_dai_driver, + ARRAY_SIZE(mt6351_dai_driver)); +} + +static const struct of_device_id mt6351_of_match[] = { + {.compatible = "mediatek,mt6351-sound",}, + {} +}; + +static struct platform_driver mt6351_codec_driver = { + .driver = { + .name = "mt6351-sound", + .of_match_table = mt6351_of_match, + }, + .probe = mt6351_codec_driver_probe, +}; + +module_platform_driver(mt6351_codec_driver) + +/* Module information */ +MODULE_DESCRIPTION("MT6351 ALSA SoC codec driver"); +MODULE_AUTHOR("KaiChieh Chuang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/mt6351.h b/sound/soc/codecs/mt6351.h new file mode 100644 index 0000000000..04b2ab694e --- /dev/null +++ b/sound/soc/codecs/mt6351.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt6351.h -- mt6351 ALSA SoC audio codec driver + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang + */ + +#ifndef __MT6351_H__ +#define __MT6351_H__ + +#define MT6351_AFE_UL_DL_CON0 (0x2000 + 0x0000) +#define MT6351_AFE_DL_SRC2_CON0_H (0x2000 + 0x0002) +#define MT6351_AFE_DL_SRC2_CON0_L (0x2000 + 0x0004) +#define MT6351_AFE_DL_SDM_CON0 (0x2000 + 0x0006) +#define MT6351_AFE_DL_SDM_CON1 (0x2000 + 0x0008) +#define MT6351_AFE_UL_SRC_CON0_H (0x2000 + 0x000a) +#define MT6351_AFE_UL_SRC_CON0_L (0x2000 + 0x000c) +#define MT6351_AFE_UL_SRC_CON1_H (0x2000 + 0x000e) +#define MT6351_AFE_UL_SRC_CON1_L (0x2000 + 0x0010) +#define MT6351_AFE_TOP_CON0 (0x2000 + 0x0012) +#define MT6351_AUDIO_TOP_CON0 (0x2000 + 0x0014) +#define MT6351_AFE_DL_SRC_MON0 (0x2000 + 0x0016) +#define MT6351_AFE_DL_SDM_TEST0 (0x2000 + 0x0018) +#define MT6351_AFE_MON_DEBUG0 (0x2000 + 0x001a) +#define MT6351_AFUNC_AUD_CON0 (0x2000 + 0x001c) +#define MT6351_AFUNC_AUD_CON1 (0x2000 + 0x001e) +#define MT6351_AFUNC_AUD_CON2 (0x2000 + 0x0020) +#define MT6351_AFUNC_AUD_CON3 (0x2000 + 0x0022) +#define MT6351_AFUNC_AUD_CON4 (0x2000 + 0x0024) +#define MT6351_AFUNC_AUD_MON0 (0x2000 + 0x0026) +#define MT6351_AFUNC_AUD_MON1 (0x2000 + 0x0028) +#define MT6351_AFE_UP8X_FIFO_CFG0 (0x2000 + 0x002c) +#define MT6351_AFE_UP8X_FIFO_LOG_MON0 (0x2000 + 0x002e) +#define MT6351_AFE_UP8X_FIFO_LOG_MON1 (0x2000 + 0x0030) +#define MT6351_AFE_DL_DC_COMP_CFG0 (0x2000 + 0x0032) +#define MT6351_AFE_DL_DC_COMP_CFG1 (0x2000 + 0x0034) +#define MT6351_AFE_DL_DC_COMP_CFG2 (0x2000 + 0x0036) +#define MT6351_AFE_PMIC_NEWIF_CFG0 (0x2000 + 0x0038) +#define MT6351_AFE_PMIC_NEWIF_CFG1 (0x2000 + 0x003a) +#define MT6351_AFE_PMIC_NEWIF_CFG2 (0x2000 + 0x003c) +#define MT6351_AFE_PMIC_NEWIF_CFG3 (0x2000 + 0x003e) +#define MT6351_AFE_SGEN_CFG0 (0x2000 + 0x0040) +#define MT6351_AFE_SGEN_CFG1 (0x2000 + 0x0042) +#define MT6351_AFE_ADDA2_UP8X_FIFO_LOG_MON0 (0x2000 + 0x004c) +#define MT6351_AFE_ADDA2_UP8X_FIFO_LOG_MON1 (0x2000 + 0x004e) +#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG0 (0x2000 + 0x0050) +#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG1 (0x2000 + 0x0052) +#define MT6351_AFE_ADDA2_PMIC_NEWIF_CFG2 (0x2000 + 0x0054) +#define MT6351_AFE_DCCLK_CFG0 (0x2000 + 0x0090) +#define MT6351_AFE_DCCLK_CFG1 (0x2000 + 0x0092) +#define MT6351_AFE_HPANC_CFG0 (0x2000 + 0x0094) +#define MT6351_AFE_NCP_CFG0 (0x2000 + 0x0096) +#define MT6351_AFE_NCP_CFG1 (0x2000 + 0x0098) + +#define MT6351_TOP_CKPDN_CON0 0x023A +#define MT6351_TOP_CKPDN_CON0_SET 0x023C +#define MT6351_TOP_CKPDN_CON0_CLR 0x023E + +#define MT6351_TOP_CLKSQ 0x029A +#define MT6351_TOP_CLKSQ_SET 0x029C +#define MT6351_TOP_CLKSQ_CLR 0x029E + +#define MT6351_ZCD_CON0 0x0800 +#define MT6351_ZCD_CON1 0x0802 +#define MT6351_ZCD_CON2 0x0804 +#define MT6351_ZCD_CON3 0x0806 +#define MT6351_ZCD_CON4 0x0808 +#define MT6351_ZCD_CON5 0x080A + +#define MT6351_LDO_VA18_CON0 0x0A00 +#define MT6351_LDO_VA18_CON1 0x0A02 +#define MT6351_LDO_VUSB33_CON0 0x0A16 +#define MT6351_LDO_VUSB33_CON1 0x0A18 + +#define MT6351_AUDDEC_ANA_CON0 0x0CF2 +#define MT6351_AUDDEC_ANA_CON1 0x0CF4 +#define MT6351_AUDDEC_ANA_CON2 0x0CF6 +#define MT6351_AUDDEC_ANA_CON3 0x0CF8 +#define MT6351_AUDDEC_ANA_CON4 0x0CFA +#define MT6351_AUDDEC_ANA_CON5 0x0CFC +#define MT6351_AUDDEC_ANA_CON6 0x0CFE +#define MT6351_AUDDEC_ANA_CON7 0x0D00 +#define MT6351_AUDDEC_ANA_CON8 0x0D02 +#define MT6351_AUDDEC_ANA_CON9 0x0D04 +#define MT6351_AUDDEC_ANA_CON10 0x0D06 + +#define MT6351_AUDENC_ANA_CON0 0x0D08 +#define MT6351_AUDENC_ANA_CON1 0x0D0A +#define MT6351_AUDENC_ANA_CON2 0x0D0C +#define MT6351_AUDENC_ANA_CON3 0x0D0E +#define MT6351_AUDENC_ANA_CON4 0x0D10 +#define MT6351_AUDENC_ANA_CON5 0x0D12 +#define MT6351_AUDENC_ANA_CON6 0x0D14 +#define MT6351_AUDENC_ANA_CON7 0x0D16 +#define MT6351_AUDENC_ANA_CON8 0x0D18 +#define MT6351_AUDENC_ANA_CON9 0x0D1A +#define MT6351_AUDENC_ANA_CON10 0x0D1C +#define MT6351_AUDENC_ANA_CON11 0x0D1E +#define MT6351_AUDENC_ANA_CON12 0x0D20 +#define MT6351_AUDENC_ANA_CON13 0x0D22 +#define MT6351_AUDENC_ANA_CON14 0x0D24 +#define MT6351_AUDENC_ANA_CON15 0x0D26 +#define MT6351_AUDENC_ANA_CON16 0x0D28 +#endif diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c new file mode 100644 index 0000000000..d7b157ddc9 --- /dev/null +++ b/sound/soc/codecs/mt6358.c @@ -0,0 +1,2499 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt6358.c -- mt6358 ALSA SoC audio codec driver +// +// Copyright (c) 2018 MediaTek Inc. +// Author: KaiChieh Chuang + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mt6358.h" + +enum { + AUDIO_ANALOG_VOLUME_HSOUTL, + AUDIO_ANALOG_VOLUME_HSOUTR, + AUDIO_ANALOG_VOLUME_HPOUTL, + AUDIO_ANALOG_VOLUME_HPOUTR, + AUDIO_ANALOG_VOLUME_LINEOUTL, + AUDIO_ANALOG_VOLUME_LINEOUTR, + AUDIO_ANALOG_VOLUME_MICAMP1, + AUDIO_ANALOG_VOLUME_MICAMP2, + AUDIO_ANALOG_VOLUME_TYPE_MAX +}; + +enum { + MUX_ADC_L, + MUX_ADC_R, + MUX_PGA_L, + MUX_PGA_R, + MUX_MIC_TYPE, + MUX_HP_L, + MUX_HP_R, + MUX_NUM, +}; + +enum { + DEVICE_HP, + DEVICE_LO, + DEVICE_RCV, + DEVICE_MIC1, + DEVICE_MIC2, + DEVICE_NUM +}; + +/* Supply widget subseq */ +enum { + /* common */ + SUPPLY_SEQ_CLK_BUF, + SUPPLY_SEQ_AUD_GLB, + SUPPLY_SEQ_CLKSQ, + SUPPLY_SEQ_VOW_AUD_LPW, + SUPPLY_SEQ_AUD_VOW, + SUPPLY_SEQ_VOW_CLK, + SUPPLY_SEQ_VOW_LDO, + SUPPLY_SEQ_TOP_CK, + SUPPLY_SEQ_TOP_CK_LAST, + SUPPLY_SEQ_AUD_TOP, + SUPPLY_SEQ_AUD_TOP_LAST, + SUPPLY_SEQ_AFE, + /* capture */ + SUPPLY_SEQ_ADC_SUPPLY, +}; + +enum { + CH_L = 0, + CH_R, + NUM_CH, +}; + +#define REG_STRIDE 2 + +struct mt6358_priv { + struct device *dev; + struct regmap *regmap; + + unsigned int dl_rate; + unsigned int ul_rate; + + int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX]; + unsigned int mux_select[MUX_NUM]; + + int dev_counter[DEVICE_NUM]; + + int mtkaif_protocol; + + struct regulator *avdd_reg; + + int wov_enabled; + + unsigned int dmic_one_wire_mode; +}; + +int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt, + int mtkaif_protocol) +{ + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + priv->mtkaif_protocol = mtkaif_protocol; + return 0; +} +EXPORT_SYMBOL_GPL(mt6358_set_mtkaif_protocol); + +static void playback_gpio_set(struct mt6358_priv *priv) +{ + /* set gpio mosi mode */ + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_CLR, + 0x01f8, 0x01f8); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_SET, + 0xffff, 0x0249); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2, + 0xffff, 0x0249); +} + +static void playback_gpio_reset(struct mt6358_priv *priv) +{ + /* set pad_aud_*_mosi to GPIO mode and dir input + * reason: + * pad_aud_dat_mosi*, because the pin is used as boot strap + * don't clean clk/sync, for mtkaif protocol 2 + */ + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2_CLR, + 0x01f8, 0x01f8); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE2, + 0x01f8, 0x0000); + regmap_update_bits(priv->regmap, MT6358_GPIO_DIR0, + 0xf << 8, 0x0); +} + +static void capture_gpio_set(struct mt6358_priv *priv) +{ + /* set gpio miso mode */ + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_CLR, + 0xffff, 0xffff); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_SET, + 0xffff, 0x0249); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, + 0xffff, 0x0249); +} + +static void capture_gpio_reset(struct mt6358_priv *priv) +{ + /* set pad_aud_*_miso to GPIO mode and dir input + * reason: + * pad_aud_clk_miso, because when playback only the miso_clk + * will also have 26m, so will have power leak + * pad_aud_dat_miso*, because the pin is used as boot strap + */ + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3_CLR, + 0xffff, 0xffff); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_GPIO_DIR0, + 0xf << 12, 0x0); +} + +/* use only when not govern by DAPM */ +static int mt6358_set_dcxo(struct mt6358_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, + 0x1 << RG_XO_AUDIO_EN_M_SFT, + (enable ? 1 : 0) << RG_XO_AUDIO_EN_M_SFT); + return 0; +} + +/* use only when not govern by DAPM */ +static int mt6358_set_clksq(struct mt6358_priv *priv, bool enable) +{ + /* audio clk source from internal dcxo */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6, + RG_CLKSQ_IN_SEL_TEST_MASK_SFT, + 0x0); + + /* Enable/disable CLKSQ 26MHz */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6, + RG_CLKSQ_EN_MASK_SFT, + (enable ? 1 : 0) << RG_CLKSQ_EN_SFT); + return 0; +} + +/* use only when not govern by DAPM */ +static int mt6358_set_aud_global_bias(struct mt6358_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + RG_AUDGLB_PWRDN_VA28_MASK_SFT, + (enable ? 0 : 1) << RG_AUDGLB_PWRDN_VA28_SFT); + return 0; +} + +/* use only when not govern by DAPM */ +static int mt6358_set_topck(struct mt6358_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0, + 0x0066, enable ? 0x0 : 0x66); + return 0; +} + +static int mt6358_mtkaif_tx_enable(struct mt6358_priv *priv) +{ + switch (priv->mtkaif_protocol) { + case MT6358_MTKAIF_PROTOCOL_2_CLK_P2: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6358_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0010); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6358_AFE_AUD_PAD_TOP, + 0xff00, 0x3800); + regmap_update_bits(priv->regmap, + MT6358_AFE_AUD_PAD_TOP, + 0xff00, 0x3900); + break; + case MT6358_MTKAIF_PROTOCOL_2: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6358_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0010); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6358_AFE_AUD_PAD_TOP, + 0xff00, 0x3100); + break; + case MT6358_MTKAIF_PROTOCOL_1: + default: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6358_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0000); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6358_AFE_AUD_PAD_TOP, + 0xff00, 0x3100); + break; + } + return 0; +} + +static int mt6358_mtkaif_tx_disable(struct mt6358_priv *priv) +{ + /* disable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, MT6358_AFE_AUD_PAD_TOP, + 0xff00, 0x3000); + return 0; +} + +int mt6358_mtkaif_calibration_enable(struct snd_soc_component *cmpnt) +{ + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + playback_gpio_set(priv); + capture_gpio_set(priv); + mt6358_mtkaif_tx_enable(priv); + + mt6358_set_dcxo(priv, true); + mt6358_set_aud_global_bias(priv, true); + mt6358_set_clksq(priv, true); + mt6358_set_topck(priv, true); + + /* set dat_miso_loopback on */ + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT, + 1 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT, + 1 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT); + return 0; +} +EXPORT_SYMBOL_GPL(mt6358_mtkaif_calibration_enable); + +int mt6358_mtkaif_calibration_disable(struct snd_soc_component *cmpnt) +{ + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + /* set dat_miso_loopback off */ + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT, + 0 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT, + 0 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT); + + mt6358_set_topck(priv, false); + mt6358_set_clksq(priv, false); + mt6358_set_aud_global_bias(priv, false); + mt6358_set_dcxo(priv, false); + + mt6358_mtkaif_tx_disable(priv); + playback_gpio_reset(priv); + capture_gpio_reset(priv); + return 0; +} +EXPORT_SYMBOL_GPL(mt6358_mtkaif_calibration_disable); + +int mt6358_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt, + int phase_1, int phase_2) +{ + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT, + phase_1 << RG_AUD_PAD_TOP_PHASE_MODE_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT, + phase_2 << RG_AUD_PAD_TOP_PHASE_MODE2_SFT); + return 0; +} +EXPORT_SYMBOL_GPL(mt6358_set_mtkaif_calibration_phase); + +/* dl pga gain */ +enum { + DL_GAIN_8DB = 0, + DL_GAIN_0DB = 8, + DL_GAIN_N_1DB = 9, + DL_GAIN_N_10DB = 18, + DL_GAIN_N_40DB = 0x1f, +}; + +#define DL_GAIN_N_10DB_REG (DL_GAIN_N_10DB << 7 | DL_GAIN_N_10DB) +#define DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB) +#define DL_GAIN_REG_MASK 0x0f9f + +static void hp_zcd_disable(struct mt6358_priv *priv) +{ + regmap_write(priv->regmap, MT6358_ZCD_CON0, 0x0000); +} + +static void hp_main_output_ramp(struct mt6358_priv *priv, bool up) +{ + int i, stage; + int target = 7; + + /* Enable/Reduce HPL/R main output stage step by step */ + for (i = 0; i <= target; i++) { + stage = up ? i : target - i; + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, + 0x7 << 8, stage << 8); + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, + 0x7 << 11, stage << 11); + usleep_range(100, 150); + } +} + +static void hp_aux_feedback_loop_gain_ramp(struct mt6358_priv *priv, bool up) +{ + int i, stage; + + /* Reduce HP aux feedback loop gain step by step */ + for (i = 0; i <= 0xf; i++) { + stage = up ? i : 0xf - i; + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9, + 0xf << 12, stage << 12); + usleep_range(100, 150); + } +} + +static void hp_pull_down(struct mt6358_priv *priv, bool enable) +{ + int i; + + if (enable) { + for (i = 0x0; i <= 0x6; i++) { + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x7, i); + usleep_range(600, 700); + } + } else { + for (i = 0x6; i >= 0x1; i--) { + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x7, i); + usleep_range(600, 700); + } + } +} + +static bool is_valid_hp_pga_idx(int reg_idx) +{ + return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_10DB) || + reg_idx == DL_GAIN_N_40DB; +} + +static void headset_volume_ramp(struct mt6358_priv *priv, int from, int to) +{ + int offset = 0, count = 0, reg_idx; + + if (!is_valid_hp_pga_idx(from) || !is_valid_hp_pga_idx(to)) + dev_warn(priv->dev, "%s(), volume index is not valid, from %d, to %d\n", + __func__, from, to); + + dev_info(priv->dev, "%s(), from %d, to %d\n", + __func__, from, to); + + if (to > from) + offset = to - from; + else + offset = from - to; + + while (offset >= 0) { + if (to > from) + reg_idx = from + count; + else + reg_idx = from - count; + + if (is_valid_hp_pga_idx(reg_idx)) { + regmap_update_bits(priv->regmap, + MT6358_ZCD_CON2, + DL_GAIN_REG_MASK, + (reg_idx << 7) | reg_idx); + usleep_range(200, 300); + } + offset--; + count++; + } +} + +static int mt6358_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = 0; + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + switch (mc->reg) { + case MT6358_ZCD_CON2: + regmap_read(priv->regmap, MT6358_ZCD_CON2, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] = + (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK; + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = + (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK; + break; + case MT6358_ZCD_CON1: + regmap_read(priv->regmap, MT6358_ZCD_CON1, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] = + (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK; + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] = + (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK; + break; + case MT6358_ZCD_CON3: + regmap_read(priv->regmap, MT6358_ZCD_CON3, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] = + (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK; + priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTR] = + (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK; + break; + case MT6358_AUDENC_ANA_CON0: + case MT6358_AUDENC_ANA_CON1: + regmap_read(priv->regmap, MT6358_AUDENC_ANA_CON0, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] = + (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK; + regmap_read(priv->regmap, MT6358_AUDENC_ANA_CON1, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] = + (reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK; + break; + } + + return ret; +} + +static void mt6358_restore_pga(struct mt6358_priv *priv); + +static int mt6358_enable_wov_phase2(struct mt6358_priv *priv) +{ + /* analog */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xffff, 0x0800); + mt6358_restore_pga(priv); + + regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9929); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xffff, 0x0025); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8, + 0xffff, 0x0005); + + /* digital */ + regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x0120); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0xffff); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0200); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2424); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xdbac); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x029e); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0, + 0xffff, 0x0451); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0x68d1); + + return 0; +} + +static int mt6358_disable_wov_phase2(struct mt6358_priv *priv) +{ + /* digital */ + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_TOP, 0xffff, 0xc000); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_HPF_CFG0, + 0xffff, 0x0450); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_POSDIV_CFG0, + 0xffff, 0x0c00); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG5, 0xffff, 0x0100); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG4, 0xffff, 0x006c); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG3, 0xffff, 0xa879); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG2, 0xffff, 0x2323); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG1, 0xffff, 0x0400); + regmap_update_bits(priv->regmap, MT6358_AFE_VOW_CFG0, 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_GPIO_MODE3, 0xffff, 0x02d8); + regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0, + 0xffff, 0x0000); + + /* analog */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON8, + 0xffff, 0x0004); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xffff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_DCXO_CW13, 0xffff, 0x9829); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xffff, 0x0000); + mt6358_restore_pga(priv); + regmap_update_bits(priv->regmap, MT6358_DCXO_CW14, 0xffff, 0xa2b5); + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0xffff, 0x0010); + + return 0; +} + +static int mt6358_get_wov(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = priv->wov_enabled; + return 0; +} + +static int mt6358_put_wov(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(c); + int enabled = ucontrol->value.integer.value[0]; + + if (enabled < 0 || enabled > 1) + return -EINVAL; + + if (priv->wov_enabled != enabled) { + if (enabled) + mt6358_enable_wov_phase2(priv); + else + mt6358_disable_wov_phase2(priv); + + priv->wov_enabled = enabled; + + return 1; + } + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); +static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new mt6358_snd_controls[] = { + /* dl pga gain */ + SOC_DOUBLE_EXT_TLV("Headphone Volume", + MT6358_ZCD_CON2, 0, 7, 0x12, 1, + snd_soc_get_volsw, mt6358_put_volsw, playback_tlv), + SOC_DOUBLE_EXT_TLV("Lineout Volume", + MT6358_ZCD_CON1, 0, 7, 0x12, 1, + snd_soc_get_volsw, mt6358_put_volsw, playback_tlv), + SOC_SINGLE_EXT_TLV("Handset Volume", + MT6358_ZCD_CON3, 0, 0x12, 1, + snd_soc_get_volsw, mt6358_put_volsw, playback_tlv), + /* ul pga gain */ + SOC_DOUBLE_R_EXT_TLV("PGA Volume", + MT6358_AUDENC_ANA_CON0, MT6358_AUDENC_ANA_CON1, + 8, 4, 0, + snd_soc_get_volsw, mt6358_put_volsw, pga_tlv), + + SOC_SINGLE_BOOL_EXT("Wake-on-Voice Phase2 Switch", 0, + mt6358_get_wov, mt6358_put_wov), +}; + +/* MUX */ +/* LOL MUX */ +static const char * const lo_in_mux_map[] = { + "Open", "Mute", "Playback", "Test Mode" +}; + +static int lo_in_mux_map_value[] = { + 0x0, 0x1, 0x2, 0x3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(lo_in_mux_map_enum, + MT6358_AUDDEC_ANA_CON7, + RG_AUDLOLMUXINPUTSEL_VAUDP15_SFT, + RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK, + lo_in_mux_map, + lo_in_mux_map_value); + +static const struct snd_kcontrol_new lo_in_mux_control = + SOC_DAPM_ENUM("In Select", lo_in_mux_map_enum); + +/*HP MUX */ +enum { + HP_MUX_OPEN = 0, + HP_MUX_HPSPK, + HP_MUX_HP, + HP_MUX_TEST_MODE, + HP_MUX_HP_IMPEDANCE, + HP_MUX_MASK = 0x7, +}; + +static const char * const hp_in_mux_map[] = { + "Open", + "LoudSPK Playback", + "Audio Playback", + "Test Mode", + "HP Impedance", +}; + +static int hp_in_mux_map_value[] = { + HP_MUX_OPEN, + HP_MUX_HPSPK, + HP_MUX_HP, + HP_MUX_TEST_MODE, + HP_MUX_HP_IMPEDANCE, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(hpl_in_mux_map_enum, + SND_SOC_NOPM, + 0, + HP_MUX_MASK, + hp_in_mux_map, + hp_in_mux_map_value); + +static const struct snd_kcontrol_new hpl_in_mux_control = + SOC_DAPM_ENUM("HPL Select", hpl_in_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(hpr_in_mux_map_enum, + SND_SOC_NOPM, + 0, + HP_MUX_MASK, + hp_in_mux_map, + hp_in_mux_map_value); + +static const struct snd_kcontrol_new hpr_in_mux_control = + SOC_DAPM_ENUM("HPR Select", hpr_in_mux_map_enum); + +/* RCV MUX */ +enum { + RCV_MUX_OPEN = 0, + RCV_MUX_MUTE, + RCV_MUX_VOICE_PLAYBACK, + RCV_MUX_TEST_MODE, + RCV_MUX_MASK = 0x3, +}; + +static const char * const rcv_in_mux_map[] = { + "Open", "Mute", "Voice Playback", "Test Mode" +}; + +static int rcv_in_mux_map_value[] = { + RCV_MUX_OPEN, + RCV_MUX_MUTE, + RCV_MUX_VOICE_PLAYBACK, + RCV_MUX_TEST_MODE, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rcv_in_mux_map_enum, + SND_SOC_NOPM, + 0, + RCV_MUX_MASK, + rcv_in_mux_map, + rcv_in_mux_map_value); + +static const struct snd_kcontrol_new rcv_in_mux_control = + SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum); + +/* DAC In MUX */ +static const char * const dac_in_mux_map[] = { + "Normal Path", "Sgen" +}; + +static int dac_in_mux_map_value[] = { + 0x0, 0x1, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum, + MT6358_AFE_TOP_CON0, + DL_SINE_ON_SFT, + DL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new dac_in_mux_control = + SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum); + +/* AIF Out MUX */ +static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum, + MT6358_AFE_TOP_CON0, + UL_SINE_ON_SFT, + UL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new aif_out_mux_control = + SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum); + +/* Mic Type MUX */ +enum { + MIC_TYPE_MUX_IDLE = 0, + MIC_TYPE_MUX_ACC, + MIC_TYPE_MUX_DMIC, + MIC_TYPE_MUX_DCC, + MIC_TYPE_MUX_DCC_ECM_DIFF, + MIC_TYPE_MUX_DCC_ECM_SINGLE, + MIC_TYPE_MUX_MASK = 0x7, +}; + +#define IS_DCC_BASE(type) ((type) == MIC_TYPE_MUX_DCC || \ + (type) == MIC_TYPE_MUX_DCC_ECM_DIFF || \ + (type) == MIC_TYPE_MUX_DCC_ECM_SINGLE) + +static const char * const mic_type_mux_map[] = { + "Idle", + "ACC", + "DMIC", + "DCC", + "DCC_ECM_DIFF", + "DCC_ECM_SINGLE", +}; + +static int mic_type_mux_map_value[] = { + MIC_TYPE_MUX_IDLE, + MIC_TYPE_MUX_ACC, + MIC_TYPE_MUX_DMIC, + MIC_TYPE_MUX_DCC, + MIC_TYPE_MUX_DCC_ECM_DIFF, + MIC_TYPE_MUX_DCC_ECM_SINGLE, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(mic_type_mux_map_enum, + SND_SOC_NOPM, + 0, + MIC_TYPE_MUX_MASK, + mic_type_mux_map, + mic_type_mux_map_value); + +static const struct snd_kcontrol_new mic_type_mux_control = + SOC_DAPM_ENUM("Mic Type Select", mic_type_mux_map_enum); + +/* ADC L MUX */ +enum { + ADC_MUX_IDLE = 0, + ADC_MUX_AIN0, + ADC_MUX_PREAMPLIFIER, + ADC_MUX_IDLE1, + ADC_MUX_MASK = 0x3, +}; + +static const char * const adc_left_mux_map[] = { + "Idle", "AIN0", "Left Preamplifier", "Idle_1" +}; + +static int adc_mux_map_value[] = { + ADC_MUX_IDLE, + ADC_MUX_AIN0, + ADC_MUX_PREAMPLIFIER, + ADC_MUX_IDLE1, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum, + SND_SOC_NOPM, + 0, + ADC_MUX_MASK, + adc_left_mux_map, + adc_mux_map_value); + +static const struct snd_kcontrol_new adc_left_mux_control = + SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum); + +/* ADC R MUX */ +static const char * const adc_right_mux_map[] = { + "Idle", "AIN0", "Right Preamplifier", "Idle_1" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum, + SND_SOC_NOPM, + 0, + ADC_MUX_MASK, + adc_right_mux_map, + adc_mux_map_value); + +static const struct snd_kcontrol_new adc_right_mux_control = + SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum); + +/* PGA L MUX */ +enum { + PGA_MUX_NONE = 0, + PGA_MUX_AIN0, + PGA_MUX_AIN1, + PGA_MUX_AIN2, + PGA_MUX_MASK = 0x3, +}; + +static const char * const pga_mux_map[] = { + "None", "AIN0", "AIN1", "AIN2" +}; + +static int pga_mux_map_value[] = { + PGA_MUX_NONE, + PGA_MUX_AIN0, + PGA_MUX_AIN1, + PGA_MUX_AIN2, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum, + SND_SOC_NOPM, + 0, + PGA_MUX_MASK, + pga_mux_map, + pga_mux_map_value); + +static const struct snd_kcontrol_new pga_left_mux_control = + SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum); + +/* PGA R MUX */ +static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum, + SND_SOC_NOPM, + 0, + PGA_MUX_MASK, + pga_mux_map, + pga_mux_map_value); + +static const struct snd_kcontrol_new pga_right_mux_control = + SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum); + +static int mt_clksq_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* audio clk source from internal dcxo */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6, + RG_CLKSQ_IN_SEL_TEST_MASK_SFT, + 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_sgen_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* sdm audio fifo clock power on */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0006); + /* scrambler clock on enable */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xCBA1); + /* sdm power on */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0003); + /* sdm fifo enable */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x000B); + + regmap_update_bits(priv->regmap, MT6358_AFE_SGEN_CFG0, + 0xff3f, + 0x0000); + regmap_update_bits(priv->regmap, MT6358_AFE_SGEN_CFG1, + 0xffff, + 0x0001); + break; + case SND_SOC_DAPM_POST_PMD: + /* DL scrambler disabling sequence */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0000); + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xcba0); + break; + default: + break; + } + + return 0; +} + +static int mt_aif_in_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_info(priv->dev, "%s(), event 0x%x, rate %d\n", + __func__, event, priv->dl_rate); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + playback_gpio_set(priv); + + /* sdm audio fifo clock power on */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0006); + /* scrambler clock on enable */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xCBA1); + /* sdm power on */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0003); + /* sdm fifo enable */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x000B); + break; + case SND_SOC_DAPM_POST_PMD: + /* DL scrambler disabling sequence */ + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON2, 0x0000); + regmap_write(priv->regmap, MT6358_AFUNC_AUD_CON0, 0xcba0); + + playback_gpio_reset(priv); + break; + default: + break; + } + + return 0; +} + +static int mtk_hp_enable(struct mt6358_priv *priv) +{ + /* Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, true); + /* release HP CMFB gate rstb */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x1 << 6, 0x1 << 6); + + /* Reduce ESD resistance of AU_REFN */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000); + + /* Set HPR/HPL gain as minimum (~ -40dB) */ + regmap_write(priv->regmap, MT6358_ZCD_CON2, DL_GAIN_N_40DB_REG); + + /* Turn on DA_600K_NCP_VA18 */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001); + /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c); + /* Toggle RG_DIVCKS_CHG */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001); + /* Set NCP soft start mode as default mode: 100us */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003); + /* Enable NCP */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000); + usleep_range(250, 270); + + /* Enable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x1055, 0x1055); + /* Enable NV regulator (-1.2V) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001); + usleep_range(100, 120); + + /* Disable AUD_ZCD */ + hp_zcd_disable(priv); + + /* Disable headphone short-circuit protection */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3000); + + /* Enable IBIST */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + + /* Set HP DR bias current optimization, 010: 6uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900); + /* Set HP & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + /* Set HPP/N STB enhance circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4033); + + /* Enable HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x000c); + /* Enable HP aux feedback loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x003c); + /* Enable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0c00); + /* Enable HP driver bias circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30c0); + /* Enable HP driver core circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f0); + /* Short HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x00fc); + + /* Enable HP main CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0e00); + /* Disable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0200); + + /* Select CMFB resistor bulk to AC mode */ + /* Selec HS/LO cap size (6.5pF default) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000); + + /* Enable HP main output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x00ff); + /* Enable HPR/L main output stage step by step */ + hp_main_output_ramp(priv, true); + + /* Reduce HP aux feedback loop gain */ + hp_aux_feedback_loop_gain_ramp(priv, true); + /* Disable HP aux feedback loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf); + + /* apply volume setting */ + headset_volume_ramp(priv, + DL_GAIN_N_10DB, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]); + + /* Disable HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3); + /* Unshort HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3f03); + usleep_range(100, 120); + + /* Enable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x1); + /* Enable Audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30ff); + /* Enable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0xf201); + usleep_range(100, 120); + + /* Switch HPL MUX to audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x32ff); + /* Switch HPR MUX to audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3aff); + + /* Disable Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, false); + + return 0; +} + +static int mtk_hp_disable(struct mt6358_priv *priv) +{ + /* Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, true); + + /* HPR/HPL mux to open */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x0f00, 0x0000); + + /* Disable low-noise mode of DAC */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9, + 0x0001, 0x0000); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x0); + + /* Short HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3); + /* Enable HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf); + + /* decrease HPL/R gain to normal gain step by step */ + headset_volume_ramp(priv, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL], + DL_GAIN_N_40DB); + + /* Enable HP aux feedback loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fff); + + /* Reduce HP aux feedback loop gain */ + hp_aux_feedback_loop_gain_ramp(priv, false); + + /* decrease HPR/L main output stage step by step */ + hp_main_output_ramp(priv, false); + + /* Disable HP main output stage */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3, 0x0); + + /* Enable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0e00); + + /* Disable HP main CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0c00); + + /* Unshort HP main output to HP aux output stage */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, + 0x3 << 6, 0x0); + + /* Disable HP driver core circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x3 << 4, 0x0); + + /* Disable HP driver bias circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x3 << 6, 0x0); + + /* Disable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0000); + + /* Disable HP aux feedback loop */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, + 0x3 << 4, 0x0); + + /* Disable HP aux output stage */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, + 0x3 << 2, 0x0); + + /* Disable IBIST */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12, + 0x1 << 8, 0x1 << 8); + + /* Disable NV regulator (-1.2V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x1, 0x0); + /* Disable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x1055, 0x0); + /* Disable NCP */ + regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, + 0x1, 0x1); + + /* Increase ESD resistance of AU_REFN */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON2, + 0x1 << 14, 0x0); + + /* Set HP CMFB gate rstb */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x1 << 6, 0x0); + /* disable Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, false); + + return 0; +} + +static int mtk_hp_spk_enable(struct mt6358_priv *priv) +{ + /* Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, true); + /* release HP CMFB gate rstb */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x1 << 6, 0x1 << 6); + + /* Reduce ESD resistance of AU_REFN */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000); + + /* Set HPR/HPL gain to -10dB */ + regmap_write(priv->regmap, MT6358_ZCD_CON2, DL_GAIN_N_10DB_REG); + + /* Turn on DA_600K_NCP_VA18 */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001); + /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c); + /* Toggle RG_DIVCKS_CHG */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001); + /* Set NCP soft start mode as default mode: 100us */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003); + /* Enable NCP */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000); + usleep_range(250, 270); + + /* Enable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x1055, 0x1055); + /* Enable NV regulator (-1.2V) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001); + usleep_range(100, 120); + + /* Disable AUD_ZCD */ + hp_zcd_disable(priv); + + /* Disable headphone short-circuit protection */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x3000); + + /* Enable IBIST */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + + /* Set HP DR bias current optimization, 010: 6uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900); + /* Set HP & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + /* Set HPP/N STB enhance circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4033); + + /* Disable Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, false); + + /* Enable HP driver bias circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30c0); + /* Enable HP driver core circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f0); + /* Enable HP main CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0200); + + /* Select CMFB resistor bulk to AC mode */ + /* Selec HS/LO cap size (6.5pF default) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000); + + /* Enable HP main output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x0003); + /* Enable HPR/L main output stage step by step */ + hp_main_output_ramp(priv, true); + + /* Set LO gain as minimum (~ -40dB) */ + regmap_write(priv->regmap, MT6358_ZCD_CON1, DL_GAIN_N_40DB_REG); + /* apply volume setting */ + headset_volume_ramp(priv, + DL_GAIN_N_10DB, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]); + + /* Set LO STB enhance circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0110); + /* Enable LO driver bias circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0112); + /* Enable LO driver core circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x0113); + + /* Set LOL gain to normal gain step by step */ + regmap_update_bits(priv->regmap, MT6358_ZCD_CON1, + RG_AUDLOLGAIN_MASK_SFT, + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] << + RG_AUDLOLGAIN_SFT); + regmap_update_bits(priv->regmap, MT6358_ZCD_CON1, + RG_AUDLORGAIN_MASK_SFT, + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] << + RG_AUDLORGAIN_SFT); + + /* Enable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x1); + /* Enable Audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x30f9); + /* Enable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0201); + /* Switch LOL MUX to audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON7, 0x011b); + /* Switch HPL/R MUX to Line-out */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x35f9); + + return 0; +} + +static int mtk_hp_spk_disable(struct mt6358_priv *priv) +{ + /* HPR/HPL mux to open */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x0f00, 0x0000); + /* LOL mux to open */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7, + 0x3 << 2, 0x0000); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, 0x1, 0x0); + + /* decrease HPL/R gain to normal gain step by step */ + headset_volume_ramp(priv, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL], + DL_GAIN_N_40DB); + + /* decrease LOL gain to minimum gain step by step */ + regmap_update_bits(priv->regmap, MT6358_ZCD_CON1, + DL_GAIN_REG_MASK, DL_GAIN_N_40DB_REG); + + /* decrease HPR/L main output stage step by step */ + hp_main_output_ramp(priv, false); + + /* Disable HP main output stage */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3, 0x0); + + /* Short HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fc3); + /* Enable HP aux output stage */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fcf); + + /* Enable HP aux feedback loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON1, 0x3fff); + + /* Reduce HP aux feedback loop gain */ + hp_aux_feedback_loop_gain_ramp(priv, false); + + /* Disable HP driver core circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x3 << 4, 0x0); + /* Disable LO driver core circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7, + 0x1, 0x0); + + /* Disable HP driver bias circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x3 << 6, 0x0); + /* Disable LO driver bias circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7, + 0x1 << 1, 0x0); + + /* Disable HP aux CMFB loop */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9, + 0xff << 8, 0x0000); + + /* Disable IBIST */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12, + 0x1 << 8, 0x1 << 8); + /* Disable NV regulator (-1.2V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x1, 0x0); + /* Disable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, 0x1055, 0x0); + /* Disable NCP */ + regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x1, 0x1); + + /* Set HP CMFB gate rstb */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON4, + 0x1 << 6, 0x0); + /* disable Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, false); + + return 0; +} + +static int mt_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + int device = DEVICE_HP; + + dev_info(priv->dev, "%s(), event 0x%x, dev_counter[DEV_HP] %d, mux %u\n", + __func__, + event, + priv->dev_counter[device], + mux); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + priv->dev_counter[device]++; + if (priv->dev_counter[device] > 1) + break; /* already enabled, do nothing */ + else if (priv->dev_counter[device] <= 0) + dev_warn(priv->dev, "%s(), dev_counter[DEV_HP] %d <= 0\n", + __func__, + priv->dev_counter[device]); + + priv->mux_select[MUX_HP_L] = mux; + + if (mux == HP_MUX_HP) + mtk_hp_enable(priv); + else if (mux == HP_MUX_HPSPK) + mtk_hp_spk_enable(priv); + break; + case SND_SOC_DAPM_PRE_PMD: + priv->dev_counter[device]--; + if (priv->dev_counter[device] > 0) { + break; /* still being used, don't close */ + } else if (priv->dev_counter[device] < 0) { + dev_warn(priv->dev, "%s(), dev_counter[DEV_HP] %d < 0\n", + __func__, + priv->dev_counter[device]); + priv->dev_counter[device] = 0; + break; + } + + if (priv->mux_select[MUX_HP_L] == HP_MUX_HP) + mtk_hp_disable(priv); + else if (priv->mux_select[MUX_HP_L] == HP_MUX_HPSPK) + mtk_hp_spk_disable(priv); + + priv->mux_select[MUX_HP_L] = mux; + break; + default: + break; + } + + return 0; +} + +static int mt_rcv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_info(priv->dev, "%s(), event 0x%x, mux %u\n", + __func__, + event, + dapm_kcontrol_get_value(w->kcontrols[0])); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Reduce ESD resistance of AU_REFN */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON2, 0x4000); + + /* Turn on DA_600K_NCP_VA18 */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON1, 0x0001); + /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON2, 0x002c); + /* Toggle RG_DIVCKS_CHG */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON0, 0x0001); + /* Set NCP soft start mode as default mode: 100us */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON4, 0x0003); + /* Enable NCP */ + regmap_write(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, 0x0000); + usleep_range(250, 270); + + /* Enable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x1055, 0x1055); + /* Enable NV regulator (-1.2V) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON15, 0x0001); + usleep_range(100, 120); + + /* Disable AUD_ZCD */ + hp_zcd_disable(priv); + + /* Disable handset short-circuit protection */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0010); + + /* Enable IBIST */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + /* Set HP DR bias current optimization, 010: 6uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON11, 0x4900); + /* Set HP & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON12, 0x0055); + /* Set HS STB enhance circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0090); + + /* Disable HP main CMFB loop */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0000); + /* Select CMFB resistor bulk to AC mode */ + /* Selec HS/LO cap size (6.5pF default) */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON10, 0x0000); + + /* Enable HS driver bias circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0092); + /* Enable HS driver core circuits */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x0093); + + /* Enable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0x1, 0x1); + + /* Enable Audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON0, 0x0009); + /* Enable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON9, 0x0001); + /* Switch HS MUX to audio DAC */ + regmap_write(priv->regmap, MT6358_AUDDEC_ANA_CON6, 0x009b); + break; + case SND_SOC_DAPM_PRE_PMD: + /* HS mux to open */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6, + RG_AUDHSMUXINPUTSEL_VAUDP15_MASK_SFT, + RCV_MUX_OPEN); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0x1, 0x0); + + /* decrease HS gain to minimum gain step by step */ + regmap_write(priv->regmap, MT6358_ZCD_CON3, DL_GAIN_N_40DB); + + /* Disable HS driver core circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6, + 0x1, 0x0); + + /* Disable HS driver bias circuits */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6, + 0x1 << 1, 0x0000); + + /* Disable HP aux CMFB loop */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9, + 0xff << 8, 0x0); + + /* Enable HP main CMFB Switch */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON9, + 0xff << 8, 0x2 << 8); + + /* Disable IBIST */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON12, + 0x1 << 8, 0x1 << 8); + + /* Disable NV regulator (-1.2V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON15, + 0x1, 0x0); + /* Disable cap-less LDOs (1.5V) */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x1055, 0x0); + /* Disable NCP */ + regmap_update_bits(priv->regmap, MT6358_AUDNCP_CLKDIV_CON3, + 0x1, 0x1); + break; + default: + break; + } + + return 0; +} + +static int mt_aif_out_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x, rate %d\n", + __func__, event, priv->ul_rate); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + capture_gpio_set(priv); + break; + case SND_SOC_DAPM_POST_PMD: + capture_gpio_reset(priv); + break; + default: + break; + } + + return 0; +} + +static int mt_adc_supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x\n", + __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable audio ADC CLKGEN */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0x1 << 5, 0x1 << 5); + /* ADC CLK from CLKGEN (13MHz) */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON3, + 0x0000); + /* Enable LCLDO_ENC 1P8V */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x2500, 0x0100); + /* LCLDO_ENC remote sense */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x2500, 0x2500); + break; + case SND_SOC_DAPM_POST_PMD: + /* LCLDO_ENC remote sense off */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x2500, 0x0100); + /* disable LCLDO_ENC 1P8V */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON14, + 0x2500, 0x0000); + + /* ADC CLK from CLKGEN (13MHz) */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON3, 0x0000); + /* disable audio ADC CLKGEN */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13, + 0x1 << 5, 0x0 << 5); + break; + default: + break; + } + + return 0; +} + +static int mt6358_amic_enable(struct mt6358_priv *priv) +{ + unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE]; + unsigned int mux_pga_l = priv->mux_select[MUX_PGA_L]; + unsigned int mux_pga_r = priv->mux_select[MUX_PGA_R]; + + dev_info(priv->dev, "%s(), mux, mic %u, pga l %u, pga r %u\n", + __func__, mic_type, mux_pga_l, mux_pga_r); + + if (IS_DCC_BASE(mic_type)) { + /* DCC 50k CLK (from 26M) */ + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062); + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062); + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2060); + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2061); + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG1, 0x0100); + } + + /* mic bias 0 */ + if (mux_pga_l == PGA_MUX_AIN0 || mux_pga_l == PGA_MUX_AIN2 || + mux_pga_r == PGA_MUX_AIN0 || mux_pga_r == PGA_MUX_AIN2) { + switch (mic_type) { + case MIC_TYPE_MUX_DCC_ECM_DIFF: + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xff00, 0x7700); + break; + case MIC_TYPE_MUX_DCC_ECM_SINGLE: + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xff00, 0x1100); + break; + default: + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xff00, 0x0000); + break; + } + /* Enable MICBIAS0, MISBIAS0 = 1P9V */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON9, + 0xff, 0x21); + } + + /* mic bias 1 */ + if (mux_pga_l == PGA_MUX_AIN1 || mux_pga_r == PGA_MUX_AIN1) { + /* Enable MICBIAS1, MISBIAS1 = 2P6V */ + if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE) + regmap_write(priv->regmap, + MT6358_AUDENC_ANA_CON10, 0x0161); + else + regmap_write(priv->regmap, + MT6358_AUDENC_ANA_CON10, 0x0061); + } + + if (IS_DCC_BASE(mic_type)) { + /* Audio L/R preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0xf8ff, 0x0004); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xf8ff, 0x0004); + } else { + /* reset reg */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0xf8ff, 0x0000); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xf8ff, 0x0000); + } + + if (mux_pga_l != PGA_MUX_NONE) { + /* L preamplifier input sel */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDPREAMPLINPUTSEL_MASK_SFT, + mux_pga_l << RG_AUDPREAMPLINPUTSEL_SFT); + + /* L preamplifier enable */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDPREAMPLON_MASK_SFT, + 0x1 << RG_AUDPREAMPLON_SFT); + + if (IS_DCC_BASE(mic_type)) { + /* L preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCCEN_MASK_SFT, + 0x1 << RG_AUDPREAMPLDCCEN_SFT); + } + + /* L ADC input sel : L PGA. Enable audio L ADC */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDADCLINPUTSEL_MASK_SFT, + ADC_MUX_PREAMPLIFIER << + RG_AUDADCLINPUTSEL_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDADCLPWRUP_MASK_SFT, + 0x1 << RG_AUDADCLPWRUP_SFT); + } + + if (mux_pga_r != PGA_MUX_NONE) { + /* R preamplifier input sel */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDPREAMPRINPUTSEL_MASK_SFT, + mux_pga_r << RG_AUDPREAMPRINPUTSEL_SFT); + + /* R preamplifier enable */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDPREAMPRON_MASK_SFT, + 0x1 << RG_AUDPREAMPRON_SFT); + + if (IS_DCC_BASE(mic_type)) { + /* R preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCCEN_MASK_SFT, + 0x1 << RG_AUDPREAMPRDCCEN_SFT); + } + + /* R ADC input sel : R PGA. Enable audio R ADC */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDADCRINPUTSEL_MASK_SFT, + ADC_MUX_PREAMPLIFIER << + RG_AUDADCRINPUTSEL_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDADCRPWRUP_MASK_SFT, + 0x1 << RG_AUDADCRPWRUP_SFT); + } + + if (IS_DCC_BASE(mic_type)) { + usleep_range(100, 150); + /* Audio L preamplifier DCC precharge off */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCPRECHARGE_MASK_SFT, 0x0); + /* Audio R preamplifier DCC precharge off */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCPRECHARGE_MASK_SFT, 0x0); + + /* Short body to ground in PGA */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON3, + 0x1 << 12, 0x0); + } + + /* here to set digital part */ + mt6358_mtkaif_tx_enable(priv); + + /* UL dmic setting off */ + regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0000); + + /* UL turn on */ + regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, 0x0001); + + return 0; +} + +static void mt6358_amic_disable(struct mt6358_priv *priv) +{ + unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE]; + unsigned int mux_pga_l = priv->mux_select[MUX_PGA_L]; + unsigned int mux_pga_r = priv->mux_select[MUX_PGA_R]; + + dev_info(priv->dev, "%s(), mux, mic %u, pga l %u, pga r %u\n", + __func__, mic_type, mux_pga_l, mux_pga_r); + + /* UL turn off */ + regmap_update_bits(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, + 0x0001, 0x0000); + + /* disable aud_pad TX fifos */ + mt6358_mtkaif_tx_disable(priv); + + /* L ADC input sel : off, disable L ADC */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0xf000, 0x0000); + /* L preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0x1 << 1, 0x0); + /* L preamplifier input sel : off, L PGA 0 dB gain */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0xfffb, 0x0000); + + /* disable L preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + 0x1 << 2, 0x0); + + /* R ADC input sel : off, disable R ADC */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0xf000, 0x0000); + /* R preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0x1 << 1, 0x0); + /* R preamplifier input sel : off, R PGA 0 dB gain */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0x0ffb, 0x0000); + + /* disable R preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + 0x1 << 2, 0x0); + + /* mic bias */ + /* Disable MICBIAS0, MISBIAS0 = 1P7V */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0000); + + /* Disable MICBIAS1 */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10, + 0x0001, 0x0000); + + if (IS_DCC_BASE(mic_type)) { + /* dcclk_gen_on=1'b0 */ + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2060); + /* dcclk_pdn=1'b1 */ + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062); + /* dcclk_ref_ck_sel=2'b00 */ + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062); + /* dcclk_div=11'b00100000011 */ + regmap_write(priv->regmap, MT6358_AFE_DCCLK_CFG0, 0x2062); + } +} + +static int mt6358_dmic_enable(struct mt6358_priv *priv) +{ + dev_info(priv->dev, "%s()\n", __func__); + + /* mic bias */ + /* Enable MICBIAS0, MISBIAS0 = 1P9V */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0021); + + /* RG_BANDGAPGEN=1'b0 */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10, + 0x1 << 12, 0x0); + + /* DMIC enable */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON8, 0x0005); + + /* here to set digital part */ + mt6358_mtkaif_tx_enable(priv); + + /* UL dmic setting */ + if (priv->dmic_one_wire_mode) + regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0400); + else + regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_H, 0x0080); + + /* UL turn on */ + regmap_write(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, 0x0003); + + /* Prevent pop noise form dmic hw */ + msleep(100); + + return 0; +} + +static void mt6358_dmic_disable(struct mt6358_priv *priv) +{ + dev_info(priv->dev, "%s()\n", __func__); + + /* UL turn off */ + regmap_update_bits(priv->regmap, MT6358_AFE_UL_SRC_CON0_L, + 0x0003, 0x0000); + + /* disable aud_pad TX fifos */ + mt6358_mtkaif_tx_disable(priv); + + /* DMIC disable */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON8, 0x0000); + + /* mic bias */ + /* MISBIAS0 = 1P7V */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0001); + + /* RG_BANDGAPGEN=1'b0 */ + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON10, + 0x1 << 12, 0x0); + + /* MICBIA0 disable */ + regmap_write(priv->regmap, MT6358_AUDENC_ANA_CON9, 0x0000); +} + +static void mt6358_restore_pga(struct mt6358_priv *priv) +{ + unsigned int gain_l, gain_r; + + gain_l = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; + gain_r = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; + + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON0, + RG_AUDPREAMPLGAIN_MASK_SFT, + gain_l << RG_AUDPREAMPLGAIN_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON1, + RG_AUDPREAMPRGAIN_MASK_SFT, + gain_r << RG_AUDPREAMPRGAIN_SFT); +} + +static int mt_mic_type_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n", + __func__, event, mux); + + switch (event) { + case SND_SOC_DAPM_WILL_PMU: + priv->mux_select[MUX_MIC_TYPE] = mux; + break; + case SND_SOC_DAPM_PRE_PMU: + switch (mux) { + case MIC_TYPE_MUX_DMIC: + mt6358_dmic_enable(priv); + break; + default: + mt6358_amic_enable(priv); + break; + } + mt6358_restore_pga(priv); + + break; + case SND_SOC_DAPM_POST_PMD: + switch (priv->mux_select[MUX_MIC_TYPE]) { + case MIC_TYPE_MUX_DMIC: + mt6358_dmic_disable(priv); + break; + default: + mt6358_amic_disable(priv); + break; + } + + priv->mux_select[MUX_MIC_TYPE] = mux; + break; + default: + break; + } + + return 0; +} + +static int mt_adc_l_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n", + __func__, event, mux); + + priv->mux_select[MUX_ADC_L] = mux; + + return 0; +} + +static int mt_adc_r_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n", + __func__, event, mux); + + priv->mux_select[MUX_ADC_R] = mux; + + return 0; +} + +static int mt_pga_left_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n", + __func__, event, mux); + + priv->mux_select[MUX_PGA_L] = mux; + + return 0; +} + +static int mt_pga_right_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event = 0x%x, mux %u\n", + __func__, event, mux); + + priv->mux_select[MUX_PGA_R] = mux; + + return 0; +} + +static int mt_delay_250_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(250, 270); + break; + case SND_SOC_DAPM_PRE_PMD: + usleep_range(250, 270); + break; + default: + break; + } + + return 0; +} + +/* DAPM Widgets */ +static const struct snd_soc_dapm_widget mt6358_dapm_widgets[] = { + /* Global Supply*/ + SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF, + MT6358_DCXO_CW14, + RG_XO_AUDIO_EN_M_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB, + MT6358_AUDDEC_ANA_CON13, + RG_AUDGLB_PWRDN_VA28_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ, + MT6358_AUDENC_ANA_CON6, + RG_CLKSQ_EN_SFT, 0, + mt_clksq_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK, + MT6358_AUD_TOP_CKPDN_CON0, + RG_AUDNCP_CK_PDN_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK, + MT6358_AUD_TOP_CKPDN_CON0, + RG_ZCD13M_CK_PDN_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST, + MT6358_AUD_TOP_CKPDN_CON0, + RG_AUD_CK_PDN_SFT, 1, + mt_delay_250_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY_S("AUDIF_CK", SUPPLY_SEQ_TOP_CK, + MT6358_AUD_TOP_CKPDN_CON0, + RG_AUDIF_CK_PDN_SFT, 1, NULL, 0), + + /* Digital Clock */ + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST, + MT6358_AUDIO_TOP_CON0, + PDN_AFE_CTL_SFT, 1, + mt_delay_250_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_DAC_CTL", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PDN_DAC_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PDN_ADC_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PDN_I2S_DL_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PWR_CLK_DIS_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP, + MT6358_AUDIO_TOP_CON0, + PDN_RESERVED_SFT, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* AFE ON */ + SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE, + MT6358_AFE_UL_DL_CON0, AFE_ON_SFT, 0, + NULL, 0), + + /* AIF Rx*/ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "AIF1 Playback", 0, + MT6358_AFE_DL_SRC2_CON0_L, + DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, + mt_aif_in_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* DL Supply */ + SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* DAC */ + SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control), + + SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + + /* LOL */ + SND_SOC_DAPM_MUX("LOL Mux", SND_SOC_NOPM, 0, 0, &lo_in_mux_control), + + SND_SOC_DAPM_SUPPLY("LO Stability Enh", MT6358_AUDDEC_ANA_CON7, + RG_LOOUTPUTSTBENH_VAUDP15_SFT, 0, NULL, 0), + + SND_SOC_DAPM_OUT_DRV("LOL Buffer", MT6358_AUDDEC_ANA_CON7, + RG_AUDLOLPWRUP_VAUDP15_SFT, 0, NULL, 0), + + /* Headphone */ + SND_SOC_DAPM_MUX_E("HPL Mux", SND_SOC_NOPM, 0, 0, + &hpl_in_mux_control, + mt_hp_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX_E("HPR Mux", SND_SOC_NOPM, 0, 0, + &hpr_in_mux_control, + mt_hp_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD), + + /* Receiver */ + SND_SOC_DAPM_MUX_E("RCV Mux", SND_SOC_NOPM, 0, 0, + &rcv_in_mux_control, + mt_rcv_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_PRE_PMD), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("Receiver"), + SND_SOC_DAPM_OUTPUT("Headphone L"), + SND_SOC_DAPM_OUTPUT("Headphone R"), + SND_SOC_DAPM_OUTPUT("Headphone L Ext Spk Amp"), + SND_SOC_DAPM_OUTPUT("Headphone R Ext Spk Amp"), + SND_SOC_DAPM_OUTPUT("LINEOUT L"), + SND_SOC_DAPM_OUTPUT("LINEOUT L HSSPK"), + + /* SGEN */ + SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6358_AFE_SGEN_CFG0, + SGEN_DAC_EN_CTL_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6358_AFE_SGEN_CFG0, + SGEN_MUTE_SW_CTL_SFT, 1, + mt_sgen_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6358_AFE_DL_SRC2_CON0_L, + DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("SGEN DL"), + + /* Uplinks */ + SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "AIF1 Capture", 0, + SND_SOC_NOPM, 0, 0, + mt_aif_out_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("ADC Supply", SUPPLY_SEQ_ADC_SUPPLY, + SND_SOC_NOPM, 0, 0, + mt_adc_supply_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Uplinks MUX */ + SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0, + &aif_out_mux_control), + + SND_SOC_DAPM_MUX_E("Mic Type Mux", SND_SOC_NOPM, 0, 0, + &mic_type_mux_control, + mt_mic_type_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_WILL_PMU), + + SND_SOC_DAPM_MUX_E("ADC L Mux", SND_SOC_NOPM, 0, 0, + &adc_left_mux_control, + mt_adc_l_event, + SND_SOC_DAPM_WILL_PMU), + SND_SOC_DAPM_MUX_E("ADC R Mux", SND_SOC_NOPM, 0, 0, + &adc_right_mux_control, + mt_adc_r_event, + SND_SOC_DAPM_WILL_PMU), + + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX_E("PGA L Mux", SND_SOC_NOPM, 0, 0, + &pga_left_mux_control, + mt_pga_left_event, + SND_SOC_DAPM_WILL_PMU), + SND_SOC_DAPM_MUX_E("PGA R Mux", SND_SOC_NOPM, 0, 0, + &pga_right_mux_control, + mt_pga_right_event, + SND_SOC_DAPM_WILL_PMU), + + SND_SOC_DAPM_PGA("PGA L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* UL input */ + SND_SOC_DAPM_INPUT("AIN0"), + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), +}; + +static const struct snd_soc_dapm_route mt6358_dapm_routes[] = { + /* Capture */ + {"AIF1TX", NULL, "AIF Out Mux"}, + {"AIF1TX", NULL, "CLK_BUF"}, + {"AIF1TX", NULL, "AUDGLB"}, + {"AIF1TX", NULL, "CLKSQ Audio"}, + + {"AIF1TX", NULL, "AUD_CK"}, + {"AIF1TX", NULL, "AUDIF_CK"}, + + {"AIF1TX", NULL, "AUDIO_TOP_AFE_CTL"}, + {"AIF1TX", NULL, "AUDIO_TOP_ADC_CTL"}, + {"AIF1TX", NULL, "AUDIO_TOP_PWR_CLK"}, + {"AIF1TX", NULL, "AUDIO_TOP_PDN_RESERVED"}, + {"AIF1TX", NULL, "AUDIO_TOP_I2S_DL"}, + + {"AIF1TX", NULL, "AFE_ON"}, + + {"AIF Out Mux", NULL, "Mic Type Mux"}, + + {"Mic Type Mux", "ACC", "ADC L"}, + {"Mic Type Mux", "ACC", "ADC R"}, + {"Mic Type Mux", "DCC", "ADC L"}, + {"Mic Type Mux", "DCC", "ADC R"}, + {"Mic Type Mux", "DCC_ECM_DIFF", "ADC L"}, + {"Mic Type Mux", "DCC_ECM_DIFF", "ADC R"}, + {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC L"}, + {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC R"}, + {"Mic Type Mux", "DMIC", "AIN0"}, + {"Mic Type Mux", "DMIC", "AIN2"}, + + {"ADC L", NULL, "ADC L Mux"}, + {"ADC L", NULL, "ADC Supply"}, + {"ADC R", NULL, "ADC R Mux"}, + {"ADC R", NULL, "ADC Supply"}, + + {"ADC L Mux", "Left Preamplifier", "PGA L"}, + + {"ADC R Mux", "Right Preamplifier", "PGA R"}, + + {"PGA L", NULL, "PGA L Mux"}, + {"PGA R", NULL, "PGA R Mux"}, + + {"PGA L Mux", "AIN0", "AIN0"}, + {"PGA L Mux", "AIN1", "AIN1"}, + {"PGA L Mux", "AIN2", "AIN2"}, + + {"PGA R Mux", "AIN0", "AIN0"}, + {"PGA R Mux", "AIN1", "AIN1"}, + {"PGA R Mux", "AIN2", "AIN2"}, + + /* DL Supply */ + {"DL Power Supply", NULL, "CLK_BUF"}, + {"DL Power Supply", NULL, "AUDGLB"}, + {"DL Power Supply", NULL, "CLKSQ Audio"}, + + {"DL Power Supply", NULL, "AUDNCP_CK"}, + {"DL Power Supply", NULL, "ZCD13M_CK"}, + {"DL Power Supply", NULL, "AUD_CK"}, + {"DL Power Supply", NULL, "AUDIF_CK"}, + + /* DL Digital Supply */ + {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"}, + + {"DL Digital Clock", NULL, "AFE_ON"}, + + {"AIF_RX", NULL, "DL Digital Clock"}, + + /* DL Path */ + {"DAC In Mux", "Normal Path", "AIF_RX"}, + + {"DAC In Mux", "Sgen", "SGEN DL"}, + {"SGEN DL", NULL, "SGEN DL SRC"}, + {"SGEN DL", NULL, "SGEN MUTE"}, + {"SGEN DL", NULL, "SGEN DL Enable"}, + {"SGEN DL", NULL, "DL Digital Clock"}, + {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"}, + + {"DACL", NULL, "DAC In Mux"}, + {"DACL", NULL, "DL Power Supply"}, + + {"DACR", NULL, "DAC In Mux"}, + {"DACR", NULL, "DL Power Supply"}, + + /* Lineout Path */ + {"LOL Mux", "Playback", "DACL"}, + + {"LOL Buffer", NULL, "LOL Mux"}, + {"LOL Buffer", NULL, "LO Stability Enh"}, + + {"LINEOUT L", NULL, "LOL Buffer"}, + + /* Headphone Path */ + {"HPL Mux", "Audio Playback", "DACL"}, + {"HPR Mux", "Audio Playback", "DACR"}, + {"HPL Mux", "HP Impedance", "DACL"}, + {"HPR Mux", "HP Impedance", "DACR"}, + {"HPL Mux", "LoudSPK Playback", "DACL"}, + {"HPR Mux", "LoudSPK Playback", "DACR"}, + + {"Headphone L", NULL, "HPL Mux"}, + {"Headphone R", NULL, "HPR Mux"}, + {"Headphone L Ext Spk Amp", NULL, "HPL Mux"}, + {"Headphone R Ext Spk Amp", NULL, "HPR Mux"}, + {"LINEOUT L HSSPK", NULL, "HPL Mux"}, + + /* Receiver Path */ + {"RCV Mux", "Voice Playback", "DACL"}, + {"Receiver", NULL, "RCV Mux"}, +}; + +static int mt6358_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *cmpnt = dai->component; + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int rate = params_rate(params); + + dev_info(priv->dev, "%s(), substream->stream %d, rate %d, number %d\n", + __func__, + substream->stream, + rate, + substream->number); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + priv->dl_rate = rate; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + priv->ul_rate = rate; + + return 0; +} + +static const struct snd_soc_dai_ops mt6358_codec_dai_ops = { + .hw_params = mt6358_codec_dai_hw_params, +}; + +#define MT6358_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) + +static struct snd_soc_dai_driver mt6358_dai_driver[] = { + { + .name = "mt6358-snd-codec-aif1", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6358_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000, + .formats = MT6358_FORMATS, + }, + .ops = &mt6358_codec_dai_ops, + }, +}; + +static void mt6358_codec_init_reg(struct mt6358_priv *priv) +{ + /* Disable HeadphoneL/HeadphoneR short circuit protection */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + RG_AUDHPLSCDISABLE_VAUDP15_MASK_SFT, + 0x1 << RG_AUDHPLSCDISABLE_VAUDP15_SFT); + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON0, + RG_AUDHPRSCDISABLE_VAUDP15_MASK_SFT, + 0x1 << RG_AUDHPRSCDISABLE_VAUDP15_SFT); + /* Disable voice short circuit protection */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON6, + RG_AUDHSSCDISABLE_VAUDP15_MASK_SFT, + 0x1 << RG_AUDHSSCDISABLE_VAUDP15_SFT); + /* disable LO buffer left short circuit protection */ + regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON7, + RG_AUDLOLSCDISABLE_VAUDP15_MASK_SFT, + 0x1 << RG_AUDLOLSCDISABLE_VAUDP15_SFT); + + /* accdet s/w enable */ + regmap_update_bits(priv->regmap, MT6358_ACCDET_CON13, + 0xFFFF, 0x700E); + + /* gpio miso driving set to 4mA */ + regmap_write(priv->regmap, MT6358_DRV_CON3, 0x8888); + + /* set gpio */ + playback_gpio_reset(priv); + capture_gpio_reset(priv); +} + +static int mt6358_codec_probe(struct snd_soc_component *cmpnt) +{ + struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int ret; + + snd_soc_component_init_regmap(cmpnt, priv->regmap); + + mt6358_codec_init_reg(priv); + + priv->avdd_reg = devm_regulator_get(priv->dev, "Avdd"); + if (IS_ERR(priv->avdd_reg)) { + dev_err(priv->dev, "%s() have no Avdd supply", __func__); + return PTR_ERR(priv->avdd_reg); + } + + ret = regulator_enable(priv->avdd_reg); + if (ret) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver mt6358_soc_component_driver = { + .probe = mt6358_codec_probe, + .controls = mt6358_snd_controls, + .num_controls = ARRAY_SIZE(mt6358_snd_controls), + .dapm_widgets = mt6358_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt6358_dapm_widgets), + .dapm_routes = mt6358_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mt6358_dapm_routes), + .endianness = 1, +}; + +static void mt6358_parse_dt(struct mt6358_priv *priv) +{ + int ret; + struct device *dev = priv->dev; + + ret = of_property_read_u32(dev->of_node, "mediatek,dmic-mode", + &priv->dmic_one_wire_mode); + if (ret) { + dev_warn(priv->dev, "%s() failed to read dmic-mode\n", + __func__); + priv->dmic_one_wire_mode = 0; + } +} + +static int mt6358_platform_driver_probe(struct platform_device *pdev) +{ + struct mt6358_priv *priv; + struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); + + priv = devm_kzalloc(&pdev->dev, + sizeof(struct mt6358_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, priv); + + priv->dev = &pdev->dev; + + priv->regmap = mt6397->regmap; + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + mt6358_parse_dt(priv); + + dev_info(priv->dev, "%s(), dev name %s\n", + __func__, dev_name(&pdev->dev)); + + return devm_snd_soc_register_component(&pdev->dev, + &mt6358_soc_component_driver, + mt6358_dai_driver, + ARRAY_SIZE(mt6358_dai_driver)); +} + +static const struct of_device_id mt6358_of_match[] = { + {.compatible = "mediatek,mt6358-sound",}, + {.compatible = "mediatek,mt6366-sound",}, + {} +}; +MODULE_DEVICE_TABLE(of, mt6358_of_match); + +static struct platform_driver mt6358_platform_driver = { + .driver = { + .name = "mt6358-sound", + .of_match_table = mt6358_of_match, + }, + .probe = mt6358_platform_driver_probe, +}; + +module_platform_driver(mt6358_platform_driver) + +/* Module information */ +MODULE_DESCRIPTION("MT6358 ALSA SoC codec driver"); +MODULE_AUTHOR("KaiChieh Chuang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/mt6358.h b/sound/soc/codecs/mt6358.h new file mode 100644 index 0000000000..a5953315ea --- /dev/null +++ b/sound/soc/codecs/mt6358.h @@ -0,0 +1,2314 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * mt6358.h -- mt6358 ALSA SoC audio codec driver + * + * Copyright (c) 2018 MediaTek Inc. + * Author: KaiChieh Chuang + */ + +#ifndef __MT6358_H__ +#define __MT6358_H__ + +/* Reg bit define */ +/* MT6358_DCXO_CW14 */ +#define RG_XO_AUDIO_EN_M_SFT 13 + +/* MT6358_DCXO_CW13 */ +#define RG_XO_VOW_EN_SFT 8 + +/* MT6358_AUD_TOP_CKPDN_CON0 */ +#define RG_VOW13M_CK_PDN_SFT 13 +#define RG_VOW13M_CK_PDN_MASK 0x1 +#define RG_VOW13M_CK_PDN_MASK_SFT (0x1 << 13) +#define RG_VOW32K_CK_PDN_SFT 12 +#define RG_VOW32K_CK_PDN_MASK 0x1 +#define RG_VOW32K_CK_PDN_MASK_SFT (0x1 << 12) +#define RG_AUD_INTRP_CK_PDN_SFT 8 +#define RG_AUD_INTRP_CK_PDN_MASK 0x1 +#define RG_AUD_INTRP_CK_PDN_MASK_SFT (0x1 << 8) +#define RG_PAD_AUD_CLK_MISO_CK_PDN_SFT 7 +#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK 0x1 +#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK_SFT (0x1 << 7) +#define RG_AUDNCP_CK_PDN_SFT 6 +#define RG_AUDNCP_CK_PDN_MASK 0x1 +#define RG_AUDNCP_CK_PDN_MASK_SFT (0x1 << 6) +#define RG_ZCD13M_CK_PDN_SFT 5 +#define RG_ZCD13M_CK_PDN_MASK 0x1 +#define RG_ZCD13M_CK_PDN_MASK_SFT (0x1 << 5) +#define RG_AUDIF_CK_PDN_SFT 2 +#define RG_AUDIF_CK_PDN_MASK 0x1 +#define RG_AUDIF_CK_PDN_MASK_SFT (0x1 << 2) +#define RG_AUD_CK_PDN_SFT 1 +#define RG_AUD_CK_PDN_MASK 0x1 +#define RG_AUD_CK_PDN_MASK_SFT (0x1 << 1) +#define RG_ACCDET_CK_PDN_SFT 0 +#define RG_ACCDET_CK_PDN_MASK 0x1 +#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0) + +/* MT6358_AUD_TOP_CKPDN_CON0_SET */ +#define RG_AUD_TOP_CKPDN_CON0_SET_SFT 0 +#define RG_AUD_TOP_CKPDN_CON0_SET_MASK 0x3fff +#define RG_AUD_TOP_CKPDN_CON0_SET_MASK_SFT (0x3fff << 0) + +/* MT6358_AUD_TOP_CKPDN_CON0_CLR */ +#define RG_AUD_TOP_CKPDN_CON0_CLR_SFT 0 +#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK 0x3fff +#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK_SFT (0x3fff << 0) + +/* MT6358_AUD_TOP_CKSEL_CON0 */ +#define RG_AUDIF_CK_CKSEL_SFT 3 +#define RG_AUDIF_CK_CKSEL_MASK 0x1 +#define RG_AUDIF_CK_CKSEL_MASK_SFT (0x1 << 3) +#define RG_AUD_CK_CKSEL_SFT 2 +#define RG_AUD_CK_CKSEL_MASK 0x1 +#define RG_AUD_CK_CKSEL_MASK_SFT (0x1 << 2) + +/* MT6358_AUD_TOP_CKSEL_CON0_SET */ +#define RG_AUD_TOP_CKSEL_CON0_SET_SFT 0 +#define RG_AUD_TOP_CKSEL_CON0_SET_MASK 0xf +#define RG_AUD_TOP_CKSEL_CON0_SET_MASK_SFT (0xf << 0) + +/* MT6358_AUD_TOP_CKSEL_CON0_CLR */ +#define RG_AUD_TOP_CKSEL_CON0_CLR_SFT 0 +#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK 0xf +#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK_SFT (0xf << 0) + +/* MT6358_AUD_TOP_CKTST_CON0 */ +#define RG_VOW13M_CK_TSTSEL_SFT 9 +#define RG_VOW13M_CK_TSTSEL_MASK 0x1 +#define RG_VOW13M_CK_TSTSEL_MASK_SFT (0x1 << 9) +#define RG_VOW13M_CK_TST_DIS_SFT 8 +#define RG_VOW13M_CK_TST_DIS_MASK 0x1 +#define RG_VOW13M_CK_TST_DIS_MASK_SFT (0x1 << 8) +#define RG_AUD26M_CK_TSTSEL_SFT 4 +#define RG_AUD26M_CK_TSTSEL_MASK 0x1 +#define RG_AUD26M_CK_TSTSEL_MASK_SFT (0x1 << 4) +#define RG_AUDIF_CK_TSTSEL_SFT 3 +#define RG_AUDIF_CK_TSTSEL_MASK 0x1 +#define RG_AUDIF_CK_TSTSEL_MASK_SFT (0x1 << 3) +#define RG_AUD_CK_TSTSEL_SFT 2 +#define RG_AUD_CK_TSTSEL_MASK 0x1 +#define RG_AUD_CK_TSTSEL_MASK_SFT (0x1 << 2) +#define RG_AUD26M_CK_TST_DIS_SFT 0 +#define RG_AUD26M_CK_TST_DIS_MASK 0x1 +#define RG_AUD26M_CK_TST_DIS_MASK_SFT (0x1 << 0) + +/* MT6358_AUD_TOP_CLK_HWEN_CON0 */ +#define RG_AUD_INTRP_CK_PDN_HWEN_SFT 0 +#define RG_AUD_INTRP_CK_PDN_HWEN_MASK 0x1 +#define RG_AUD_INTRP_CK_PDN_HWEN_MASK_SFT (0x1 << 0) + +/* MT6358_AUD_TOP_CLK_HWEN_CON0_SET */ +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_SFT 0 +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK 0xffff +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK_SFT (0xffff << 0) + +/* MT6358_AUD_TOP_CLK_HWEN_CON0_CLR */ +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_SFT 0 +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK 0xffff +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK_SFT (0xffff << 0) + +/* MT6358_AUD_TOP_RST_CON0 */ +#define RG_AUDNCP_RST_SFT 3 +#define RG_AUDNCP_RST_MASK 0x1 +#define RG_AUDNCP_RST_MASK_SFT (0x1 << 3) +#define RG_ZCD_RST_SFT 2 +#define RG_ZCD_RST_MASK 0x1 +#define RG_ZCD_RST_MASK_SFT (0x1 << 2) +#define RG_ACCDET_RST_SFT 1 +#define RG_ACCDET_RST_MASK 0x1 +#define RG_ACCDET_RST_MASK_SFT (0x1 << 1) +#define RG_AUDIO_RST_SFT 0 +#define RG_AUDIO_RST_MASK 0x1 +#define RG_AUDIO_RST_MASK_SFT (0x1 << 0) + +/* MT6358_AUD_TOP_RST_CON0_SET */ +#define RG_AUD_TOP_RST_CON0_SET_SFT 0 +#define RG_AUD_TOP_RST_CON0_SET_MASK 0xf +#define RG_AUD_TOP_RST_CON0_SET_MASK_SFT (0xf << 0) + +/* MT6358_AUD_TOP_RST_CON0_CLR */ +#define RG_AUD_TOP_RST_CON0_CLR_SFT 0 +#define RG_AUD_TOP_RST_CON0_CLR_MASK 0xf +#define RG_AUD_TOP_RST_CON0_CLR_MASK_SFT (0xf << 0) + +/* MT6358_AUD_TOP_RST_BANK_CON0 */ +#define BANK_AUDZCD_SWRST_SFT 2 +#define BANK_AUDZCD_SWRST_MASK 0x1 +#define BANK_AUDZCD_SWRST_MASK_SFT (0x1 << 2) +#define BANK_AUDIO_SWRST_SFT 1 +#define BANK_AUDIO_SWRST_MASK 0x1 +#define BANK_AUDIO_SWRST_MASK_SFT (0x1 << 1) +#define BANK_ACCDET_SWRST_SFT 0 +#define BANK_ACCDET_SWRST_MASK 0x1 +#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0) + +/* MT6358_AUD_TOP_INT_CON0 */ +#define RG_INT_EN_AUDIO_SFT 0 +#define RG_INT_EN_AUDIO_MASK 0x1 +#define RG_INT_EN_AUDIO_MASK_SFT (0x1 << 0) +#define RG_INT_EN_ACCDET_SFT 5 +#define RG_INT_EN_ACCDET_MASK 0x1 +#define RG_INT_EN_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_EN_ACCDET_EINT0_SFT 6 +#define RG_INT_EN_ACCDET_EINT0_MASK 0x1 +#define RG_INT_EN_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_EN_ACCDET_EINT1_SFT 7 +#define RG_INT_EN_ACCDET_EINT1_MASK 0x1 +#define RG_INT_EN_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +/* MT6358_AUD_TOP_INT_CON0_SET */ +#define RG_AUD_INT_CON0_SET_SFT 0 +#define RG_AUD_INT_CON0_SET_MASK 0xffff +#define RG_AUD_INT_CON0_SET_MASK_SFT (0xffff << 0) + +/* MT6358_AUD_TOP_INT_CON0_CLR */ +#define RG_AUD_INT_CON0_CLR_SFT 0 +#define RG_AUD_INT_CON0_CLR_MASK 0xffff +#define RG_AUD_INT_CON0_CLR_MASK_SFT (0xffff << 0) + +/* MT6358_AUD_TOP_INT_MASK_CON0 */ +#define RG_INT_MASK_AUDIO_SFT 0 +#define RG_INT_MASK_AUDIO_MASK 0x1 +#define RG_INT_MASK_AUDIO_MASK_SFT (0x1 << 0) +#define RG_INT_MASK_ACCDET_SFT 5 +#define RG_INT_MASK_ACCDET_MASK 0x1 +#define RG_INT_MASK_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_MASK_ACCDET_EINT0_SFT 6 +#define RG_INT_MASK_ACCDET_EINT0_MASK 0x1 +#define RG_INT_MASK_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_MASK_ACCDET_EINT1_SFT 7 +#define RG_INT_MASK_ACCDET_EINT1_MASK 0x1 +#define RG_INT_MASK_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +/* MT6358_AUD_TOP_INT_MASK_CON0_SET */ +#define RG_AUD_INT_MASK_CON0_SET_SFT 0 +#define RG_AUD_INT_MASK_CON0_SET_MASK 0xff +#define RG_AUD_INT_MASK_CON0_SET_MASK_SFT (0xff << 0) + +/* MT6358_AUD_TOP_INT_MASK_CON0_CLR */ +#define RG_AUD_INT_MASK_CON0_CLR_SFT 0 +#define RG_AUD_INT_MASK_CON0_CLR_MASK 0xff +#define RG_AUD_INT_MASK_CON0_CLR_MASK_SFT (0xff << 0) + +/* MT6358_AUD_TOP_INT_STATUS0 */ +#define RG_INT_STATUS_AUDIO_SFT 0 +#define RG_INT_STATUS_AUDIO_MASK 0x1 +#define RG_INT_STATUS_AUDIO_MASK_SFT (0x1 << 0) +#define RG_INT_STATUS_ACCDET_SFT 5 +#define RG_INT_STATUS_ACCDET_MASK 0x1 +#define RG_INT_STATUS_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_STATUS_ACCDET_EINT0_SFT 6 +#define RG_INT_STATUS_ACCDET_EINT0_MASK 0x1 +#define RG_INT_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_STATUS_ACCDET_EINT1_SFT 7 +#define RG_INT_STATUS_ACCDET_EINT1_MASK 0x1 +#define RG_INT_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +/* MT6358_AUD_TOP_INT_RAW_STATUS0 */ +#define RG_INT_RAW_STATUS_AUDIO_SFT 0 +#define RG_INT_RAW_STATUS_AUDIO_MASK 0x1 +#define RG_INT_RAW_STATUS_AUDIO_MASK_SFT (0x1 << 0) +#define RG_INT_RAW_STATUS_ACCDET_SFT 5 +#define RG_INT_RAW_STATUS_ACCDET_MASK 0x1 +#define RG_INT_RAW_STATUS_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_RAW_STATUS_ACCDET_EINT0_SFT 6 +#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK 0x1 +#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_RAW_STATUS_ACCDET_EINT1_SFT 7 +#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK 0x1 +#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +/* MT6358_AUD_TOP_INT_MISC_CON0 */ +#define RG_AUD_TOP_INT_POLARITY_SFT 0 +#define RG_AUD_TOP_INT_POLARITY_MASK 0x1 +#define RG_AUD_TOP_INT_POLARITY_MASK_SFT (0x1 << 0) + +/* MT6358_AUDNCP_CLKDIV_CON0 */ +#define RG_DIVCKS_CHG_SFT 0 +#define RG_DIVCKS_CHG_MASK 0x1 +#define RG_DIVCKS_CHG_MASK_SFT (0x1 << 0) + +/* MT6358_AUDNCP_CLKDIV_CON1 */ +#define RG_DIVCKS_ON_SFT 0 +#define RG_DIVCKS_ON_MASK 0x1 +#define RG_DIVCKS_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AUDNCP_CLKDIV_CON2 */ +#define RG_DIVCKS_PRG_SFT 0 +#define RG_DIVCKS_PRG_MASK 0x1ff +#define RG_DIVCKS_PRG_MASK_SFT (0x1ff << 0) + +/* MT6358_AUDNCP_CLKDIV_CON3 */ +#define RG_DIVCKS_PWD_NCP_SFT 0 +#define RG_DIVCKS_PWD_NCP_MASK 0x1 +#define RG_DIVCKS_PWD_NCP_MASK_SFT (0x1 << 0) + +/* MT6358_AUDNCP_CLKDIV_CON4 */ +#define RG_DIVCKS_PWD_NCP_ST_SEL_SFT 0 +#define RG_DIVCKS_PWD_NCP_ST_SEL_MASK 0x3 +#define RG_DIVCKS_PWD_NCP_ST_SEL_MASK_SFT (0x3 << 0) + +/* MT6358_AUD_TOP_MON_CON0 */ +#define RG_AUD_TOP_MON_SEL_SFT 0 +#define RG_AUD_TOP_MON_SEL_MASK 0x7 +#define RG_AUD_TOP_MON_SEL_MASK_SFT (0x7 << 0) +#define RG_AUD_CLK_INT_MON_FLAG_SEL_SFT 3 +#define RG_AUD_CLK_INT_MON_FLAG_SEL_MASK 0xff +#define RG_AUD_CLK_INT_MON_FLAG_SEL_MASK_SFT (0xff << 3) +#define RG_AUD_CLK_INT_MON_FLAG_EN_SFT 11 +#define RG_AUD_CLK_INT_MON_FLAG_EN_MASK 0x1 +#define RG_AUD_CLK_INT_MON_FLAG_EN_MASK_SFT (0x1 << 11) + +/* MT6358_AUDIO_DIG_DSN_ID */ +#define AUDIO_DIG_ANA_ID_SFT 0 +#define AUDIO_DIG_ANA_ID_MASK 0xff +#define AUDIO_DIG_ANA_ID_MASK_SFT (0xff << 0) +#define AUDIO_DIG_DIG_ID_SFT 8 +#define AUDIO_DIG_DIG_ID_MASK 0xff +#define AUDIO_DIG_DIG_ID_MASK_SFT (0xff << 8) + +/* MT6358_AUDIO_DIG_DSN_REV0 */ +#define AUDIO_DIG_ANA_MINOR_REV_SFT 0 +#define AUDIO_DIG_ANA_MINOR_REV_MASK 0xf +#define AUDIO_DIG_ANA_MINOR_REV_MASK_SFT (0xf << 0) +#define AUDIO_DIG_ANA_MAJOR_REV_SFT 4 +#define AUDIO_DIG_ANA_MAJOR_REV_MASK 0xf +#define AUDIO_DIG_ANA_MAJOR_REV_MASK_SFT (0xf << 4) +#define AUDIO_DIG_DIG_MINOR_REV_SFT 8 +#define AUDIO_DIG_DIG_MINOR_REV_MASK 0xf +#define AUDIO_DIG_DIG_MINOR_REV_MASK_SFT (0xf << 8) +#define AUDIO_DIG_DIG_MAJOR_REV_SFT 12 +#define AUDIO_DIG_DIG_MAJOR_REV_MASK 0xf +#define AUDIO_DIG_DIG_MAJOR_REV_MASK_SFT (0xf << 12) + +/* MT6358_AUDIO_DIG_DSN_DBI */ +#define AUDIO_DIG_DSN_CBS_SFT 0 +#define AUDIO_DIG_DSN_CBS_MASK 0x3 +#define AUDIO_DIG_DSN_CBS_MASK_SFT (0x3 << 0) +#define AUDIO_DIG_DSN_BIX_SFT 2 +#define AUDIO_DIG_DSN_BIX_MASK 0x3 +#define AUDIO_DIG_DSN_BIX_MASK_SFT (0x3 << 2) +#define AUDIO_DIG_ESP_SFT 8 +#define AUDIO_DIG_ESP_MASK 0xff +#define AUDIO_DIG_ESP_MASK_SFT (0xff << 8) + +/* MT6358_AUDIO_DIG_DSN_DXI */ +#define AUDIO_DIG_DSN_FPI_SFT 0 +#define AUDIO_DIG_DSN_FPI_MASK 0xff +#define AUDIO_DIG_DSN_FPI_MASK_SFT (0xff << 0) + +/* MT6358_AFE_UL_DL_CON0 */ +#define AFE_UL_LR_SWAP_SFT 15 +#define AFE_UL_LR_SWAP_MASK 0x1 +#define AFE_UL_LR_SWAP_MASK_SFT (0x1 << 15) +#define AFE_DL_LR_SWAP_SFT 14 +#define AFE_DL_LR_SWAP_MASK 0x1 +#define AFE_DL_LR_SWAP_MASK_SFT (0x1 << 14) +#define AFE_ON_SFT 0 +#define AFE_ON_MASK 0x1 +#define AFE_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_DL_SRC2_CON0_L */ +#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_UL_SRC_CON0_H */ +#define C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11 +#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7 +#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11) +#define C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8 +#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7 +#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8) +#define C_TWO_DIGITAL_MIC_CTL_SFT 7 +#define C_TWO_DIGITAL_MIC_CTL_MASK 0x1 +#define C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7) + +/* MT6358_AFE_UL_SRC_CON0_L */ +#define DMIC_LOW_POWER_MODE_CTL_SFT 14 +#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3 +#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14) +#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5) +#define UL_LOOP_BACK_MODE_CTL_SFT 2 +#define UL_LOOP_BACK_MODE_CTL_MASK 0x1 +#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2) +#define UL_SDM_3_LEVEL_CTL_SFT 1 +#define UL_SDM_3_LEVEL_CTL_MASK 0x1 +#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1) +#define UL_SRC_ON_TMP_CTL_SFT 0 +#define UL_SRC_ON_TMP_CTL_MASK 0x1 +#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_TOP_CON0 */ +#define MTKAIF_SINE_ON_SFT 2 +#define MTKAIF_SINE_ON_MASK 0x1 +#define MTKAIF_SINE_ON_MASK_SFT (0x1 << 2) +#define UL_SINE_ON_SFT 1 +#define UL_SINE_ON_MASK 0x1 +#define UL_SINE_ON_MASK_SFT (0x1 << 1) +#define DL_SINE_ON_SFT 0 +#define DL_SINE_ON_MASK 0x1 +#define DL_SINE_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AUDIO_TOP_CON0 */ +#define PDN_AFE_CTL_SFT 7 +#define PDN_AFE_CTL_MASK 0x1 +#define PDN_AFE_CTL_MASK_SFT (0x1 << 7) +#define PDN_DAC_CTL_SFT 6 +#define PDN_DAC_CTL_MASK 0x1 +#define PDN_DAC_CTL_MASK_SFT (0x1 << 6) +#define PDN_ADC_CTL_SFT 5 +#define PDN_ADC_CTL_MASK 0x1 +#define PDN_ADC_CTL_MASK_SFT (0x1 << 5) +#define PDN_I2S_DL_CTL_SFT 3 +#define PDN_I2S_DL_CTL_MASK 0x1 +#define PDN_I2S_DL_CTL_MASK_SFT (0x1 << 3) +#define PWR_CLK_DIS_CTL_SFT 2 +#define PWR_CLK_DIS_CTL_MASK 0x1 +#define PWR_CLK_DIS_CTL_MASK_SFT (0x1 << 2) +#define PDN_AFE_TESTMODEL_CTL_SFT 1 +#define PDN_AFE_TESTMODEL_CTL_MASK 0x1 +#define PDN_AFE_TESTMODEL_CTL_MASK_SFT (0x1 << 1) +#define PDN_RESERVED_SFT 0 +#define PDN_RESERVED_MASK 0x1 +#define PDN_RESERVED_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_MON_DEBUG0 */ +#define AUDIO_SYS_TOP_MON_SWAP_SFT 14 +#define AUDIO_SYS_TOP_MON_SWAP_MASK 0x3 +#define AUDIO_SYS_TOP_MON_SWAP_MASK_SFT (0x3 << 14) +#define AUDIO_SYS_TOP_MON_SEL_SFT 8 +#define AUDIO_SYS_TOP_MON_SEL_MASK 0x1f +#define AUDIO_SYS_TOP_MON_SEL_MASK_SFT (0x1f << 8) +#define AFE_MON_SEL_SFT 0 +#define AFE_MON_SEL_MASK 0xff +#define AFE_MON_SEL_MASK_SFT (0xff << 0) + +/* MT6358_AFUNC_AUD_CON0 */ +#define CCI_AUD_ANACK_SEL_SFT 15 +#define CCI_AUD_ANACK_SEL_MASK 0x1 +#define CCI_AUD_ANACK_SEL_MASK_SFT (0x1 << 15) +#define CCI_AUDIO_FIFO_WPTR_SFT 12 +#define CCI_AUDIO_FIFO_WPTR_MASK 0x7 +#define CCI_AUDIO_FIFO_WPTR_MASK_SFT (0x7 << 12) +#define CCI_SCRAMBLER_CG_EN_SFT 11 +#define CCI_SCRAMBLER_CG_EN_MASK 0x1 +#define CCI_SCRAMBLER_CG_EN_MASK_SFT (0x1 << 11) +#define CCI_LCH_INV_SFT 10 +#define CCI_LCH_INV_MASK 0x1 +#define CCI_LCH_INV_MASK_SFT (0x1 << 10) +#define CCI_RAND_EN_SFT 9 +#define CCI_RAND_EN_MASK 0x1 +#define CCI_RAND_EN_MASK_SFT (0x1 << 9) +#define CCI_SPLT_SCRMB_CLK_ON_SFT 8 +#define CCI_SPLT_SCRMB_CLK_ON_MASK 0x1 +#define CCI_SPLT_SCRMB_CLK_ON_MASK_SFT (0x1 << 8) +#define CCI_SPLT_SCRMB_ON_SFT 7 +#define CCI_SPLT_SCRMB_ON_MASK 0x1 +#define CCI_SPLT_SCRMB_ON_MASK_SFT (0x1 << 7) +#define CCI_AUD_IDAC_TEST_EN_SFT 6 +#define CCI_AUD_IDAC_TEST_EN_MASK 0x1 +#define CCI_AUD_IDAC_TEST_EN_MASK_SFT (0x1 << 6) +#define CCI_ZERO_PAD_DISABLE_SFT 5 +#define CCI_ZERO_PAD_DISABLE_MASK 0x1 +#define CCI_ZERO_PAD_DISABLE_MASK_SFT (0x1 << 5) +#define CCI_AUD_SPLIT_TEST_EN_SFT 4 +#define CCI_AUD_SPLIT_TEST_EN_MASK 0x1 +#define CCI_AUD_SPLIT_TEST_EN_MASK_SFT (0x1 << 4) +#define CCI_AUD_SDM_MUTEL_SFT 3 +#define CCI_AUD_SDM_MUTEL_MASK 0x1 +#define CCI_AUD_SDM_MUTEL_MASK_SFT (0x1 << 3) +#define CCI_AUD_SDM_MUTER_SFT 2 +#define CCI_AUD_SDM_MUTER_MASK 0x1 +#define CCI_AUD_SDM_MUTER_MASK_SFT (0x1 << 2) +#define CCI_AUD_SDM_7BIT_SEL_SFT 1 +#define CCI_AUD_SDM_7BIT_SEL_MASK 0x1 +#define CCI_AUD_SDM_7BIT_SEL_MASK_SFT (0x1 << 1) +#define CCI_SCRAMBLER_EN_SFT 0 +#define CCI_SCRAMBLER_EN_MASK 0x1 +#define CCI_SCRAMBLER_EN_MASK_SFT (0x1 << 0) + +/* MT6358_AFUNC_AUD_CON1 */ +#define AUD_SDM_TEST_L_SFT 8 +#define AUD_SDM_TEST_L_MASK 0xff +#define AUD_SDM_TEST_L_MASK_SFT (0xff << 8) +#define AUD_SDM_TEST_R_SFT 0 +#define AUD_SDM_TEST_R_MASK 0xff +#define AUD_SDM_TEST_R_MASK_SFT (0xff << 0) + +/* MT6358_AFUNC_AUD_CON2 */ +#define CCI_AUD_DAC_ANA_MUTE_SFT 7 +#define CCI_AUD_DAC_ANA_MUTE_MASK 0x1 +#define CCI_AUD_DAC_ANA_MUTE_MASK_SFT (0x1 << 7) +#define CCI_AUD_DAC_ANA_RSTB_SEL_SFT 6 +#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK 0x1 +#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK_SFT (0x1 << 6) +#define CCI_AUDIO_FIFO_CLKIN_INV_SFT 4 +#define CCI_AUDIO_FIFO_CLKIN_INV_MASK 0x1 +#define CCI_AUDIO_FIFO_CLKIN_INV_MASK_SFT (0x1 << 4) +#define CCI_AUDIO_FIFO_ENABLE_SFT 3 +#define CCI_AUDIO_FIFO_ENABLE_MASK 0x1 +#define CCI_AUDIO_FIFO_ENABLE_MASK_SFT (0x1 << 3) +#define CCI_ACD_MODE_SFT 2 +#define CCI_ACD_MODE_MASK 0x1 +#define CCI_ACD_MODE_MASK_SFT (0x1 << 2) +#define CCI_AFIFO_CLK_PWDB_SFT 1 +#define CCI_AFIFO_CLK_PWDB_MASK 0x1 +#define CCI_AFIFO_CLK_PWDB_MASK_SFT (0x1 << 1) +#define CCI_ACD_FUNC_RSTB_SFT 0 +#define CCI_ACD_FUNC_RSTB_MASK 0x1 +#define CCI_ACD_FUNC_RSTB_MASK_SFT (0x1 << 0) + +/* MT6358_AFUNC_AUD_CON3 */ +#define SDM_ANA13M_TESTCK_SEL_SFT 15 +#define SDM_ANA13M_TESTCK_SEL_MASK 0x1 +#define SDM_ANA13M_TESTCK_SEL_MASK_SFT (0x1 << 15) +#define SDM_ANA13M_TESTCK_SRC_SEL_SFT 12 +#define SDM_ANA13M_TESTCK_SRC_SEL_MASK 0x7 +#define SDM_ANA13M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12) +#define SDM_TESTCK_SRC_SEL_SFT 8 +#define SDM_TESTCK_SRC_SEL_MASK 0x7 +#define SDM_TESTCK_SRC_SEL_MASK_SFT (0x7 << 8) +#define DIGMIC_TESTCK_SRC_SEL_SFT 4 +#define DIGMIC_TESTCK_SRC_SEL_MASK 0x7 +#define DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 4) +#define DIGMIC_TESTCK_SEL_SFT 0 +#define DIGMIC_TESTCK_SEL_MASK 0x1 +#define DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 0) + +/* MT6358_AFUNC_AUD_CON4 */ +#define UL_FIFO_WCLK_INV_SFT 8 +#define UL_FIFO_WCLK_INV_MASK 0x1 +#define UL_FIFO_WCLK_INV_MASK_SFT (0x1 << 8) +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6 +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6) +#define UL_FIFO_WDATA_TESTEN_SFT 5 +#define UL_FIFO_WDATA_TESTEN_MASK 0x1 +#define UL_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5) +#define UL_FIFO_WDATA_TESTSRC_SEL_SFT 4 +#define UL_FIFO_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4) +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3 +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1 +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3) +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0 +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7 +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0) + +/* MT6358_AFUNC_AUD_CON5 */ +#define R_AUD_DAC_POS_LARGE_MONO_SFT 8 +#define R_AUD_DAC_POS_LARGE_MONO_MASK 0xff +#define R_AUD_DAC_POS_LARGE_MONO_MASK_SFT (0xff << 8) +#define R_AUD_DAC_NEG_LARGE_MONO_SFT 0 +#define R_AUD_DAC_NEG_LARGE_MONO_MASK 0xff +#define R_AUD_DAC_NEG_LARGE_MONO_MASK_SFT (0xff << 0) + +/* MT6358_AFUNC_AUD_CON6 */ +#define R_AUD_DAC_POS_SMALL_MONO_SFT 12 +#define R_AUD_DAC_POS_SMALL_MONO_MASK 0xf +#define R_AUD_DAC_POS_SMALL_MONO_MASK_SFT (0xf << 12) +#define R_AUD_DAC_NEG_SMALL_MONO_SFT 8 +#define R_AUD_DAC_NEG_SMALL_MONO_MASK 0xf +#define R_AUD_DAC_NEG_SMALL_MONO_MASK_SFT (0xf << 8) +#define R_AUD_DAC_POS_TINY_MONO_SFT 6 +#define R_AUD_DAC_POS_TINY_MONO_MASK 0x3 +#define R_AUD_DAC_POS_TINY_MONO_MASK_SFT (0x3 << 6) +#define R_AUD_DAC_NEG_TINY_MONO_SFT 4 +#define R_AUD_DAC_NEG_TINY_MONO_MASK 0x3 +#define R_AUD_DAC_NEG_TINY_MONO_MASK_SFT (0x3 << 4) +#define R_AUD_DAC_MONO_SEL_SFT 3 +#define R_AUD_DAC_MONO_SEL_MASK 0x1 +#define R_AUD_DAC_MONO_SEL_MASK_SFT (0x1 << 3) +#define R_AUD_DAC_SW_RSTB_SFT 0 +#define R_AUD_DAC_SW_RSTB_MASK 0x1 +#define R_AUD_DAC_SW_RSTB_MASK_SFT (0x1 << 0) + +/* MT6358_AFUNC_AUD_MON0 */ +#define AUD_SCR_OUT_L_SFT 8 +#define AUD_SCR_OUT_L_MASK 0xff +#define AUD_SCR_OUT_L_MASK_SFT (0xff << 8) +#define AUD_SCR_OUT_R_SFT 0 +#define AUD_SCR_OUT_R_MASK 0xff +#define AUD_SCR_OUT_R_MASK_SFT (0xff << 0) + +/* MT6358_AUDRC_TUNE_MON0 */ +#define ASYNC_TEST_OUT_BCK_SFT 15 +#define ASYNC_TEST_OUT_BCK_MASK 0x1 +#define ASYNC_TEST_OUT_BCK_MASK_SFT (0x1 << 15) +#define RGS_AUDRCTUNE1READ_SFT 8 +#define RGS_AUDRCTUNE1READ_MASK 0x1f +#define RGS_AUDRCTUNE1READ_MASK_SFT (0x1f << 8) +#define RGS_AUDRCTUNE0READ_SFT 0 +#define RGS_AUDRCTUNE0READ_MASK 0x1f +#define RGS_AUDRCTUNE0READ_MASK_SFT (0x1f << 0) + +/* MT6358_AFE_ADDA_MTKAIF_FIFO_CFG0 */ +#define AFE_RESERVED_SFT 1 +#define AFE_RESERVED_MASK 0x7fff +#define AFE_RESERVED_MASK_SFT (0x7fff << 1) +#define RG_MTKAIF_RXIF_FIFO_INTEN_SFT 0 +#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK 0x1 +#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 */ +#define MTKAIF_RXIF_WR_FULL_STATUS_SFT 1 +#define MTKAIF_RXIF_WR_FULL_STATUS_MASK 0x1 +#define MTKAIF_RXIF_WR_FULL_STATUS_MASK_SFT (0x1 << 1) +#define MTKAIF_RXIF_RD_EMPTY_STATUS_SFT 0 +#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK 0x1 +#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_ADDA_MTKAIF_MON0 */ +#define MTKAIFTX_V3_SYNC_OUT_SFT 14 +#define MTKAIFTX_V3_SYNC_OUT_MASK 0x1 +#define MTKAIFTX_V3_SYNC_OUT_MASK_SFT (0x1 << 14) +#define MTKAIFTX_V3_SDATA_OUT2_SFT 13 +#define MTKAIFTX_V3_SDATA_OUT2_MASK 0x1 +#define MTKAIFTX_V3_SDATA_OUT2_MASK_SFT (0x1 << 13) +#define MTKAIFTX_V3_SDATA_OUT1_SFT 12 +#define MTKAIFTX_V3_SDATA_OUT1_MASK 0x1 +#define MTKAIFTX_V3_SDATA_OUT1_MASK_SFT (0x1 << 12) +#define MTKAIF_RXIF_FIFO_STATUS_SFT 0 +#define MTKAIF_RXIF_FIFO_STATUS_MASK 0xfff +#define MTKAIF_RXIF_FIFO_STATUS_MASK_SFT (0xfff << 0) + +/* MT6358_AFE_ADDA_MTKAIF_MON1 */ +#define MTKAIFRX_V3_SYNC_IN_SFT 14 +#define MTKAIFRX_V3_SYNC_IN_MASK 0x1 +#define MTKAIFRX_V3_SYNC_IN_MASK_SFT (0x1 << 14) +#define MTKAIFRX_V3_SDATA_IN2_SFT 13 +#define MTKAIFRX_V3_SDATA_IN2_MASK 0x1 +#define MTKAIFRX_V3_SDATA_IN2_MASK_SFT (0x1 << 13) +#define MTKAIFRX_V3_SDATA_IN1_SFT 12 +#define MTKAIFRX_V3_SDATA_IN1_MASK 0x1 +#define MTKAIFRX_V3_SDATA_IN1_MASK_SFT (0x1 << 12) +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_SFT 11 +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK 0x1 +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK_SFT (0x1 << 11) +#define MTKAIF_RXIF_INVALID_FLAG_SFT 8 +#define MTKAIF_RXIF_INVALID_FLAG_MASK 0x1 +#define MTKAIF_RXIF_INVALID_FLAG_MASK_SFT (0x1 << 8) +#define MTKAIF_RXIF_INVALID_CYCLE_SFT 0 +#define MTKAIF_RXIF_INVALID_CYCLE_MASK 0xff +#define MTKAIF_RXIF_INVALID_CYCLE_MASK_SFT (0xff << 0) + +/* MT6358_AFE_ADDA_MTKAIF_MON2 */ +#define MTKAIF_TXIF_IN_CH2_SFT 8 +#define MTKAIF_TXIF_IN_CH2_MASK 0xff +#define MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8) +#define MTKAIF_TXIF_IN_CH1_SFT 0 +#define MTKAIF_TXIF_IN_CH1_MASK 0xff +#define MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0) + +/* MT6358_AFE_ADDA_MTKAIF_MON3 */ +#define MTKAIF_RXIF_OUT_CH2_SFT 8 +#define MTKAIF_RXIF_OUT_CH2_MASK 0xff +#define MTKAIF_RXIF_OUT_CH2_MASK_SFT (0xff << 8) +#define MTKAIF_RXIF_OUT_CH1_SFT 0 +#define MTKAIF_RXIF_OUT_CH1_MASK 0xff +#define MTKAIF_RXIF_OUT_CH1_MASK_SFT (0xff << 0) + +/* MT6358_AFE_ADDA_MTKAIF_CFG0 */ +#define RG_MTKAIF_RXIF_CLKINV_SFT 15 +#define RG_MTKAIF_RXIF_CLKINV_MASK 0x1 +#define RG_MTKAIF_RXIF_CLKINV_MASK_SFT (0x1 << 15) +#define RG_MTKAIF_RXIF_PROTOCOL2_SFT 8 +#define RG_MTKAIF_RXIF_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 8) +#define RG_MTKAIF_BYPASS_SRC_MODE_SFT 6 +#define RG_MTKAIF_BYPASS_SRC_MODE_MASK 0x3 +#define RG_MTKAIF_BYPASS_SRC_MODE_MASK_SFT (0x3 << 6) +#define RG_MTKAIF_BYPASS_SRC_TEST_SFT 5 +#define RG_MTKAIF_BYPASS_SRC_TEST_MASK 0x1 +#define RG_MTKAIF_BYPASS_SRC_TEST_MASK_SFT (0x1 << 5) +#define RG_MTKAIF_TXIF_PROTOCOL2_SFT 4 +#define RG_MTKAIF_TXIF_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4) +#define RG_MTKAIF_PMIC_TXIF_8TO5_SFT 2 +#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1 +#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 2) +#define RG_MTKAIF_LOOPBACK_TEST2_SFT 1 +#define RG_MTKAIF_LOOPBACK_TEST2_MASK 0x1 +#define RG_MTKAIF_LOOPBACK_TEST2_MASK_SFT (0x1 << 1) +#define RG_MTKAIF_LOOPBACK_TEST1_SFT 0 +#define RG_MTKAIF_LOOPBACK_TEST1_MASK 0x1 +#define RG_MTKAIF_LOOPBACK_TEST1_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_ADDA_MTKAIF_RX_CFG0 */ +#define RG_MTKAIF_RXIF_VOICE_MODE_SFT 12 +#define RG_MTKAIF_RXIF_VOICE_MODE_MASK 0xf +#define RG_MTKAIF_RXIF_VOICE_MODE_MASK_SFT (0xf << 12) +#define RG_MTKAIF_RXIF_DATA_BIT_SFT 8 +#define RG_MTKAIF_RXIF_DATA_BIT_MASK 0x7 +#define RG_MTKAIF_RXIF_DATA_BIT_MASK_SFT (0x7 << 8) +#define RG_MTKAIF_RXIF_FIFO_RSP_SFT 4 +#define RG_MTKAIF_RXIF_FIFO_RSP_MASK 0x7 +#define RG_MTKAIF_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_RXIF_DETECT_ON_SFT 3 +#define RG_MTKAIF_RXIF_DETECT_ON_MASK 0x1 +#define RG_MTKAIF_RXIF_DETECT_ON_MASK_SFT (0x1 << 3) +#define RG_MTKAIF_RXIF_DATA_MODE_SFT 0 +#define RG_MTKAIF_RXIF_DATA_MODE_MASK 0x1 +#define RG_MTKAIF_RXIF_DATA_MODE_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_ADDA_MTKAIF_RX_CFG1 */ +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_SFT 12 +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK 0xf +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0xf << 12) +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_SFT 8 +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK 0xf +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8) +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_SFT 4 +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK 0xf +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4) +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_SFT 0 +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK 0xf +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK_SFT (0xf << 0) + +/* MT6358_AFE_ADDA_MTKAIF_RX_CFG2 */ +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_SFT 12 +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK 0x1 +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK_SFT (0x1 << 12) +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_SFT 0 +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK 0xfff +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 0) + +/* MT6358_AFE_ADDA_MTKAIF_RX_CFG3 */ +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_SFT 7 +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK 0x1 +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK_SFT (0x1 << 7) +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4 +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7 +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 3 +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 3) + +/* MT6358_AFE_ADDA_MTKAIF_TX_CFG1 */ +#define RG_MTKAIF_SYNC_WORD2_SFT 4 +#define RG_MTKAIF_SYNC_WORD2_MASK 0x7 +#define RG_MTKAIF_SYNC_WORD2_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_SYNC_WORD1_SFT 0 +#define RG_MTKAIF_SYNC_WORD1_MASK 0x7 +#define RG_MTKAIF_SYNC_WORD1_MASK_SFT (0x7 << 0) + +/* MT6358_AFE_SGEN_CFG0 */ +#define SGEN_AMP_DIV_CH1_CTL_SFT 12 +#define SGEN_AMP_DIV_CH1_CTL_MASK 0xf +#define SGEN_AMP_DIV_CH1_CTL_MASK_SFT (0xf << 12) +#define SGEN_DAC_EN_CTL_SFT 7 +#define SGEN_DAC_EN_CTL_MASK 0x1 +#define SGEN_DAC_EN_CTL_MASK_SFT (0x1 << 7) +#define SGEN_MUTE_SW_CTL_SFT 6 +#define SGEN_MUTE_SW_CTL_MASK 0x1 +#define SGEN_MUTE_SW_CTL_MASK_SFT (0x1 << 6) +#define R_AUD_SDM_MUTE_L_SFT 5 +#define R_AUD_SDM_MUTE_L_MASK 0x1 +#define R_AUD_SDM_MUTE_L_MASK_SFT (0x1 << 5) +#define R_AUD_SDM_MUTE_R_SFT 4 +#define R_AUD_SDM_MUTE_R_MASK 0x1 +#define R_AUD_SDM_MUTE_R_MASK_SFT (0x1 << 4) + +/* MT6358_AFE_SGEN_CFG1 */ +#define C_SGEN_RCH_INV_5BIT_SFT 15 +#define C_SGEN_RCH_INV_5BIT_MASK 0x1 +#define C_SGEN_RCH_INV_5BIT_MASK_SFT (0x1 << 15) +#define C_SGEN_RCH_INV_8BIT_SFT 14 +#define C_SGEN_RCH_INV_8BIT_MASK 0x1 +#define C_SGEN_RCH_INV_8BIT_MASK_SFT (0x1 << 14) +#define SGEN_FREQ_DIV_CH1_CTL_SFT 0 +#define SGEN_FREQ_DIV_CH1_CTL_MASK 0x1f +#define SGEN_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 0) + +/* MT6358_AFE_ADC_ASYNC_FIFO_CFG */ +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_SFT 5 +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1 +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5) +#define RG_UL_ASYNC_FIFO_SOFT_RST_SFT 4 +#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK 0x1 +#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4) +#define RG_AMIC_UL_ADC_CLK_SEL_SFT 1 +#define RG_AMIC_UL_ADC_CLK_SEL_MASK 0x1 +#define RG_AMIC_UL_ADC_CLK_SEL_MASK_SFT (0x1 << 1) + +/* MT6358_AFE_DCCLK_CFG0 */ +#define DCCLK_DIV_SFT 5 +#define DCCLK_DIV_MASK 0x7ff +#define DCCLK_DIV_MASK_SFT (0x7ff << 5) +#define DCCLK_INV_SFT 4 +#define DCCLK_INV_MASK 0x1 +#define DCCLK_INV_MASK_SFT (0x1 << 4) +#define DCCLK_PDN_SFT 1 +#define DCCLK_PDN_MASK 0x1 +#define DCCLK_PDN_MASK_SFT (0x1 << 1) +#define DCCLK_GEN_ON_SFT 0 +#define DCCLK_GEN_ON_MASK 0x1 +#define DCCLK_GEN_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_DCCLK_CFG1 */ +#define RESYNC_SRC_SEL_SFT 10 +#define RESYNC_SRC_SEL_MASK 0x3 +#define RESYNC_SRC_SEL_MASK_SFT (0x3 << 10) +#define RESYNC_SRC_CK_INV_SFT 9 +#define RESYNC_SRC_CK_INV_MASK 0x1 +#define RESYNC_SRC_CK_INV_MASK_SFT (0x1 << 9) +#define DCCLK_RESYNC_BYPASS_SFT 8 +#define DCCLK_RESYNC_BYPASS_MASK 0x1 +#define DCCLK_RESYNC_BYPASS_MASK_SFT (0x1 << 8) +#define DCCLK_PHASE_SEL_SFT 4 +#define DCCLK_PHASE_SEL_MASK 0xf +#define DCCLK_PHASE_SEL_MASK_SFT (0xf << 4) + +/* MT6358_AUDIO_DIG_CFG */ +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT 15 +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK 0x1 +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT (0x1 << 15) +#define RG_AUD_PAD_TOP_PHASE_MODE2_SFT 8 +#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK 0x7f +#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT (0x7f << 8) +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT 7 +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK 0x1 +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT (0x1 << 7) +#define RG_AUD_PAD_TOP_PHASE_MODE_SFT 0 +#define RG_AUD_PAD_TOP_PHASE_MODE_MASK 0x7f +#define RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT (0x7f << 0) + +/* MT6358_AFE_AUD_PAD_TOP */ +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_SFT 12 +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK 0x7 +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK_SFT (0x7 << 12) +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_SFT 11 +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK 0x1 +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK_SFT (0x1 << 11) +#define RG_AUD_PAD_TOP_TX_FIFO_ON_SFT 8 +#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK 0x1 +#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK_SFT (0x1 << 8) + +/* MT6358_AFE_AUD_PAD_TOP_MON */ +#define ADDA_AUD_PAD_TOP_MON_SFT 0 +#define ADDA_AUD_PAD_TOP_MON_MASK 0xffff +#define ADDA_AUD_PAD_TOP_MON_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_AUD_PAD_TOP_MON1 */ +#define ADDA_AUD_PAD_TOP_MON1_SFT 0 +#define ADDA_AUD_PAD_TOP_MON1_MASK 0xffff +#define ADDA_AUD_PAD_TOP_MON1_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_DL_NLE_CFG */ +#define NLE_RCH_HPGAIN_SEL_SFT 10 +#define NLE_RCH_HPGAIN_SEL_MASK 0x1 +#define NLE_RCH_HPGAIN_SEL_MASK_SFT (0x1 << 10) +#define NLE_RCH_CH_SEL_SFT 9 +#define NLE_RCH_CH_SEL_MASK 0x1 +#define NLE_RCH_CH_SEL_MASK_SFT (0x1 << 9) +#define NLE_RCH_ON_SFT 8 +#define NLE_RCH_ON_MASK 0x1 +#define NLE_RCH_ON_MASK_SFT (0x1 << 8) +#define NLE_LCH_HPGAIN_SEL_SFT 2 +#define NLE_LCH_HPGAIN_SEL_MASK 0x1 +#define NLE_LCH_HPGAIN_SEL_MASK_SFT (0x1 << 2) +#define NLE_LCH_CH_SEL_SFT 1 +#define NLE_LCH_CH_SEL_MASK 0x1 +#define NLE_LCH_CH_SEL_MASK_SFT (0x1 << 1) +#define NLE_LCH_ON_SFT 0 +#define NLE_LCH_ON_MASK 0x1 +#define NLE_LCH_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_DL_NLE_MON */ +#define NLE_MONITOR_SFT 0 +#define NLE_MONITOR_MASK 0x3fff +#define NLE_MONITOR_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_CG_EN_MON */ +#define CK_CG_EN_MON_SFT 0 +#define CK_CG_EN_MON_MASK 0x3f +#define CK_CG_EN_MON_MASK_SFT (0x3f << 0) + +/* MT6358_AFE_VOW_TOP */ +#define PDN_VOW_SFT 15 +#define PDN_VOW_MASK 0x1 +#define PDN_VOW_MASK_SFT (0x1 << 15) +#define VOW_1P6M_800K_SEL_SFT 14 +#define VOW_1P6M_800K_SEL_MASK 0x1 +#define VOW_1P6M_800K_SEL_MASK_SFT (0x1 << 14) +#define VOW_DIGMIC_ON_SFT 13 +#define VOW_DIGMIC_ON_MASK 0x1 +#define VOW_DIGMIC_ON_MASK_SFT (0x1 << 13) +#define VOW_CK_DIV_RST_SFT 12 +#define VOW_CK_DIV_RST_MASK 0x1 +#define VOW_CK_DIV_RST_MASK_SFT (0x1 << 12) +#define VOW_ON_SFT 11 +#define VOW_ON_MASK 0x1 +#define VOW_ON_MASK_SFT (0x1 << 11) +#define VOW_DIGMIC_CK_PHASE_SEL_SFT 8 +#define VOW_DIGMIC_CK_PHASE_SEL_MASK 0x7 +#define VOW_DIGMIC_CK_PHASE_SEL_MASK_SFT (0x7 << 8) +#define MAIN_DMIC_CK_VOW_SEL_SFT 7 +#define MAIN_DMIC_CK_VOW_SEL_MASK 0x1 +#define MAIN_DMIC_CK_VOW_SEL_MASK_SFT (0x1 << 7) +#define VOW_SDM_3_LEVEL_SFT 6 +#define VOW_SDM_3_LEVEL_MASK 0x1 +#define VOW_SDM_3_LEVEL_MASK_SFT (0x1 << 6) +#define VOW_LOOP_BACK_MODE_SFT 5 +#define VOW_LOOP_BACK_MODE_MASK 0x1 +#define VOW_LOOP_BACK_MODE_MASK_SFT (0x1 << 5) +#define VOW_INTR_SOURCE_SEL_SFT 4 +#define VOW_INTR_SOURCE_SEL_MASK 0x1 +#define VOW_INTR_SOURCE_SEL_MASK_SFT (0x1 << 4) +#define VOW_INTR_CLR_SFT 3 +#define VOW_INTR_CLR_MASK 0x1 +#define VOW_INTR_CLR_MASK_SFT (0x1 << 3) +#define S_N_VALUE_RST_SFT 2 +#define S_N_VALUE_RST_MASK 0x1 +#define S_N_VALUE_RST_MASK_SFT (0x1 << 2) +#define SAMPLE_BASE_MODE_SFT 1 +#define SAMPLE_BASE_MODE_MASK 0x1 +#define SAMPLE_BASE_MODE_MASK_SFT (0x1 << 1) +#define VOW_INTR_FLAG_SFT 0 +#define VOW_INTR_FLAG_MASK 0x1 +#define VOW_INTR_FLAG_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_VOW_CFG0 */ +#define AMPREF_SFT 0 +#define AMPREF_MASK 0xffff +#define AMPREF_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_CFG1 */ +#define TIMERINI_SFT 0 +#define TIMERINI_MASK 0xffff +#define TIMERINI_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_CFG2 */ +#define B_DEFAULT_SFT 12 +#define B_DEFAULT_MASK 0x7 +#define B_DEFAULT_MASK_SFT (0x7 << 12) +#define A_DEFAULT_SFT 8 +#define A_DEFAULT_MASK 0x7 +#define A_DEFAULT_MASK_SFT (0x7 << 8) +#define B_INI_SFT 4 +#define B_INI_MASK 0x7 +#define B_INI_MASK_SFT (0x7 << 4) +#define A_INI_SFT 0 +#define A_INI_MASK 0x7 +#define A_INI_MASK_SFT (0x7 << 0) + +/* MT6358_AFE_VOW_CFG3 */ +#define K_BETA_RISE_SFT 12 +#define K_BETA_RISE_MASK 0xf +#define K_BETA_RISE_MASK_SFT (0xf << 12) +#define K_BETA_FALL_SFT 8 +#define K_BETA_FALL_MASK 0xf +#define K_BETA_FALL_MASK_SFT (0xf << 8) +#define K_ALPHA_RISE_SFT 4 +#define K_ALPHA_RISE_MASK 0xf +#define K_ALPHA_RISE_MASK_SFT (0xf << 4) +#define K_ALPHA_FALL_SFT 0 +#define K_ALPHA_FALL_MASK 0xf +#define K_ALPHA_FALL_MASK_SFT (0xf << 0) + +/* MT6358_AFE_VOW_CFG4 */ +#define VOW_TXIF_SCK_INV_SFT 15 +#define VOW_TXIF_SCK_INV_MASK 0x1 +#define VOW_TXIF_SCK_INV_MASK_SFT (0x1 << 15) +#define VOW_ADC_TESTCK_SRC_SEL_SFT 12 +#define VOW_ADC_TESTCK_SRC_SEL_MASK 0x7 +#define VOW_ADC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12) +#define VOW_ADC_TESTCK_SEL_SFT 11 +#define VOW_ADC_TESTCK_SEL_MASK 0x1 +#define VOW_ADC_TESTCK_SEL_MASK_SFT (0x1 << 11) +#define VOW_ADC_CLK_INV_SFT 10 +#define VOW_ADC_CLK_INV_MASK 0x1 +#define VOW_ADC_CLK_INV_MASK_SFT (0x1 << 10) +#define VOW_TXIF_MONO_SFT 9 +#define VOW_TXIF_MONO_MASK 0x1 +#define VOW_TXIF_MONO_MASK_SFT (0x1 << 9) +#define VOW_TXIF_SCK_DIV_SFT 4 +#define VOW_TXIF_SCK_DIV_MASK 0x1f +#define VOW_TXIF_SCK_DIV_MASK_SFT (0x1f << 4) +#define K_GAMMA_SFT 0 +#define K_GAMMA_MASK 0xf +#define K_GAMMA_MASK_SFT (0xf << 0) + +/* MT6358_AFE_VOW_CFG5 */ +#define N_MIN_SFT 0 +#define N_MIN_MASK 0xffff +#define N_MIN_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_CFG6 */ +#define RG_WINDOW_SIZE_SEL_SFT 12 +#define RG_WINDOW_SIZE_SEL_MASK 0x1 +#define RG_WINDOW_SIZE_SEL_MASK_SFT (0x1 << 12) +#define RG_FLR_BYPASS_SFT 11 +#define RG_FLR_BYPASS_MASK 0x1 +#define RG_FLR_BYPASS_MASK_SFT (0x1 << 11) +#define RG_FLR_RATIO_SFT 8 +#define RG_FLR_RATIO_MASK 0x7 +#define RG_FLR_RATIO_MASK_SFT (0x7 << 8) +#define RG_BUCK_DVFS_DONE_SW_CTL_SFT 7 +#define RG_BUCK_DVFS_DONE_SW_CTL_MASK 0x1 +#define RG_BUCK_DVFS_DONE_SW_CTL_MASK_SFT (0x1 << 7) +#define RG_BUCK_DVFS_DONE_HW_MODE_SFT 6 +#define RG_BUCK_DVFS_DONE_HW_MODE_MASK 0x1 +#define RG_BUCK_DVFS_DONE_HW_MODE_MASK_SFT (0x1 << 6) +#define RG_BUCK_DVFS_HW_CNT_THR_SFT 0 +#define RG_BUCK_DVFS_HW_CNT_THR_MASK 0x3f +#define RG_BUCK_DVFS_HW_CNT_THR_MASK_SFT (0x3f << 0) + +/* MT6358_AFE_VOW_MON0 */ +#define VOW_DOWNCNT_SFT 0 +#define VOW_DOWNCNT_MASK 0xffff +#define VOW_DOWNCNT_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_MON1 */ +#define K_TMP_MON_SFT 10 +#define K_TMP_MON_MASK 0xf +#define K_TMP_MON_MASK_SFT (0xf << 10) +#define SLT_COUNTER_MON_SFT 7 +#define SLT_COUNTER_MON_MASK 0x7 +#define SLT_COUNTER_MON_MASK_SFT (0x7 << 7) +#define VOW_B_SFT 4 +#define VOW_B_MASK 0x7 +#define VOW_B_MASK_SFT (0x7 << 4) +#define VOW_A_SFT 1 +#define VOW_A_MASK 0x7 +#define VOW_A_MASK_SFT (0x7 << 1) +#define SECOND_CNT_START_SFT 0 +#define SECOND_CNT_START_MASK 0x1 +#define SECOND_CNT_START_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_VOW_MON2 */ +#define VOW_S_L_SFT 0 +#define VOW_S_L_MASK 0xffff +#define VOW_S_L_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_MON3 */ +#define VOW_S_H_SFT 0 +#define VOW_S_H_MASK 0xffff +#define VOW_S_H_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_MON4 */ +#define VOW_N_L_SFT 0 +#define VOW_N_L_MASK 0xffff +#define VOW_N_L_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_MON5 */ +#define VOW_N_H_SFT 0 +#define VOW_N_H_MASK 0xffff +#define VOW_N_H_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_SN_INI_CFG */ +#define VOW_SN_INI_CFG_EN_SFT 15 +#define VOW_SN_INI_CFG_EN_MASK 0x1 +#define VOW_SN_INI_CFG_EN_MASK_SFT (0x1 << 15) +#define VOW_SN_INI_CFG_VAL_SFT 0 +#define VOW_SN_INI_CFG_VAL_MASK 0x7fff +#define VOW_SN_INI_CFG_VAL_MASK_SFT (0x7fff << 0) + +/* MT6358_AFE_VOW_TGEN_CFG0 */ +#define VOW_TGEN_EN_SFT 15 +#define VOW_TGEN_EN_MASK 0x1 +#define VOW_TGEN_EN_MASK_SFT (0x1 << 15) +#define VOW_TGEN_MUTE_SW_SFT 14 +#define VOW_TGEN_MUTE_SW_MASK 0x1 +#define VOW_TGEN_MUTE_SW_MASK_SFT (0x1 << 14) +#define VOW_TGEN_FREQ_DIV_SFT 0 +#define VOW_TGEN_FREQ_DIV_MASK 0x3fff +#define VOW_TGEN_FREQ_DIV_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_POSDIV_CFG0 */ +#define BUCK_DVFS_DONE_SFT 15 +#define BUCK_DVFS_DONE_MASK 0x1 +#define BUCK_DVFS_DONE_MASK_SFT (0x1 << 15) +#define VOW_32K_MODE_SFT 13 +#define VOW_32K_MODE_MASK 0x1 +#define VOW_32K_MODE_MASK_SFT (0x1 << 13) +#define RG_BUCK_CLK_DIV_SFT 8 +#define RG_BUCK_CLK_DIV_MASK 0x1f +#define RG_BUCK_CLK_DIV_MASK_SFT (0x1f << 8) +#define RG_A1P6M_EN_SEL_SFT 7 +#define RG_A1P6M_EN_SEL_MASK 0x1 +#define RG_A1P6M_EN_SEL_MASK_SFT (0x1 << 7) +#define VOW_CLK_SEL_SFT 6 +#define VOW_CLK_SEL_MASK 0x1 +#define VOW_CLK_SEL_MASK_SFT (0x1 << 6) +#define VOW_INTR_SW_MODE_SFT 5 +#define VOW_INTR_SW_MODE_MASK 0x1 +#define VOW_INTR_SW_MODE_MASK_SFT (0x1 << 5) +#define VOW_INTR_SW_VAL_SFT 4 +#define VOW_INTR_SW_VAL_MASK 0x1 +#define VOW_INTR_SW_VAL_MASK_SFT (0x1 << 4) +#define VOW_CIC_MODE_SEL_SFT 2 +#define VOW_CIC_MODE_SEL_MASK 0x3 +#define VOW_CIC_MODE_SEL_MASK_SFT (0x3 << 2) +#define RG_VOW_POSDIV_SFT 0 +#define RG_VOW_POSDIV_MASK 0x3 +#define RG_VOW_POSDIV_MASK_SFT (0x3 << 0) + +/* MT6358_AFE_VOW_HPF_CFG0 */ +#define VOW_HPF_DC_TEST_SFT 12 +#define VOW_HPF_DC_TEST_MASK 0xf +#define VOW_HPF_DC_TEST_MASK_SFT (0xf << 12) +#define VOW_IRQ_LATCH_SNR_EN_SFT 10 +#define VOW_IRQ_LATCH_SNR_EN_MASK 0x1 +#define VOW_IRQ_LATCH_SNR_EN_MASK_SFT (0x1 << 10) +#define VOW_DMICCLK_PDN_SFT 9 +#define VOW_DMICCLK_PDN_MASK 0x1 +#define VOW_DMICCLK_PDN_MASK_SFT (0x1 << 9) +#define VOW_POSDIVCLK_PDN_SFT 8 +#define VOW_POSDIVCLK_PDN_MASK 0x1 +#define VOW_POSDIVCLK_PDN_MASK_SFT (0x1 << 8) +#define RG_BASELINE_ALPHA_ORDER_SFT 4 +#define RG_BASELINE_ALPHA_ORDER_MASK 0xf +#define RG_BASELINE_ALPHA_ORDER_MASK_SFT (0xf << 4) +#define RG_MTKAIF_HPF_BYPASS_SFT 2 +#define RG_MTKAIF_HPF_BYPASS_MASK 0x1 +#define RG_MTKAIF_HPF_BYPASS_MASK_SFT (0x1 << 2) +#define RG_SNRDET_HPF_BYPASS_SFT 1 +#define RG_SNRDET_HPF_BYPASS_MASK 0x1 +#define RG_SNRDET_HPF_BYPASS_MASK_SFT (0x1 << 1) +#define RG_HPF_ON_SFT 0 +#define RG_HPF_ON_MASK 0x1 +#define RG_HPF_ON_MASK_SFT (0x1 << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG0 */ +#define RG_PERIODIC_EN_SFT 15 +#define RG_PERIODIC_EN_MASK 0x1 +#define RG_PERIODIC_EN_MASK_SFT (0x1 << 15) +#define RG_PERIODIC_CNT_CLR_SFT 14 +#define RG_PERIODIC_CNT_CLR_MASK 0x1 +#define RG_PERIODIC_CNT_CLR_MASK_SFT (0x1 << 14) +#define RG_PERIODIC_CNT_PERIOD_SFT 0 +#define RG_PERIODIC_CNT_PERIOD_MASK 0x3fff +#define RG_PERIODIC_CNT_PERIOD_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG1 */ +#define RG_PERIODIC_CNT_SET_SFT 15 +#define RG_PERIODIC_CNT_SET_MASK 0x1 +#define RG_PERIODIC_CNT_SET_MASK_SFT (0x1 << 15) +#define RG_PERIODIC_CNT_PAUSE_SFT 14 +#define RG_PERIODIC_CNT_PAUSE_MASK 0x1 +#define RG_PERIODIC_CNT_PAUSE_MASK_SFT (0x1 << 14) +#define RG_PERIODIC_CNT_SET_VALUE_SFT 0 +#define RG_PERIODIC_CNT_SET_VALUE_MASK 0x3fff +#define RG_PERIODIC_CNT_SET_VALUE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG2 */ +#define AUDPREAMPLON_PERIODIC_MODE_SFT 15 +#define AUDPREAMPLON_PERIODIC_MODE_MASK 0x1 +#define AUDPREAMPLON_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDPREAMPLON_PERIODIC_INVERSE_SFT 14 +#define AUDPREAMPLON_PERIODIC_INVERSE_MASK 0x1 +#define AUDPREAMPLON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDPREAMPLON_PERIODIC_ON_CYCLE_SFT 0 +#define AUDPREAMPLON_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDPREAMPLON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG3 */ +#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_SFT 15 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_MASK 0x1 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_SFT 14 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_MASK 0x1 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_SFT 0 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDPREAMPLDCPRECHARGE_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG4 */ +#define AUDADCLPWRUP_PERIODIC_MODE_SFT 15 +#define AUDADCLPWRUP_PERIODIC_MODE_MASK 0x1 +#define AUDADCLPWRUP_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDADCLPWRUP_PERIODIC_INVERSE_SFT 14 +#define AUDADCLPWRUP_PERIODIC_INVERSE_MASK 0x1 +#define AUDADCLPWRUP_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_SFT 0 +#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDADCLPWRUP_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG5 */ +#define AUDGLBVOWLPWEN_PERIODIC_MODE_SFT 15 +#define AUDGLBVOWLPWEN_PERIODIC_MODE_MASK 0x1 +#define AUDGLBVOWLPWEN_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_SFT 14 +#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_MASK 0x1 +#define AUDGLBVOWLPWEN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_SFT 0 +#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDGLBVOWLPWEN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG6 */ +#define AUDDIGMICEN_PERIODIC_MODE_SFT 15 +#define AUDDIGMICEN_PERIODIC_MODE_MASK 0x1 +#define AUDDIGMICEN_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDDIGMICEN_PERIODIC_INVERSE_SFT 14 +#define AUDDIGMICEN_PERIODIC_INVERSE_MASK 0x1 +#define AUDDIGMICEN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDDIGMICEN_PERIODIC_ON_CYCLE_SFT 0 +#define AUDDIGMICEN_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDDIGMICEN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG7 */ +#define AUDPWDBMICBIAS0_PERIODIC_MODE_SFT 15 +#define AUDPWDBMICBIAS0_PERIODIC_MODE_MASK 0x1 +#define AUDPWDBMICBIAS0_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_SFT 14 +#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_MASK 0x1 +#define AUDPWDBMICBIAS0_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_SFT 0 +#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDPWDBMICBIAS0_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG8 */ +#define AUDPWDBMICBIAS1_PERIODIC_MODE_SFT 15 +#define AUDPWDBMICBIAS1_PERIODIC_MODE_MASK 0x1 +#define AUDPWDBMICBIAS1_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_SFT 14 +#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_MASK 0x1 +#define AUDPWDBMICBIAS1_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_SFT 0 +#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDPWDBMICBIAS1_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG9 */ +#define XO_VOW_CK_EN_PERIODIC_MODE_SFT 15 +#define XO_VOW_CK_EN_PERIODIC_MODE_MASK 0x1 +#define XO_VOW_CK_EN_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define XO_VOW_CK_EN_PERIODIC_INVERSE_SFT 14 +#define XO_VOW_CK_EN_PERIODIC_INVERSE_MASK 0x1 +#define XO_VOW_CK_EN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_SFT 0 +#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_MASK 0x3fff +#define XO_VOW_CK_EN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG10 */ +#define AUDGLB_PWRDN_PERIODIC_MODE_SFT 15 +#define AUDGLB_PWRDN_PERIODIC_MODE_MASK 0x1 +#define AUDGLB_PWRDN_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define AUDGLB_PWRDN_PERIODIC_INVERSE_SFT 14 +#define AUDGLB_PWRDN_PERIODIC_INVERSE_MASK 0x1 +#define AUDGLB_PWRDN_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_SFT 0 +#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_MASK 0x3fff +#define AUDGLB_PWRDN_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG11 */ +#define VOW_ON_PERIODIC_MODE_SFT 15 +#define VOW_ON_PERIODIC_MODE_MASK 0x1 +#define VOW_ON_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define VOW_ON_PERIODIC_INVERSE_SFT 14 +#define VOW_ON_PERIODIC_INVERSE_MASK 0x1 +#define VOW_ON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define VOW_ON_PERIODIC_ON_CYCLE_SFT 0 +#define VOW_ON_PERIODIC_ON_CYCLE_MASK 0x3fff +#define VOW_ON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG12 */ +#define DMIC_ON_PERIODIC_MODE_SFT 15 +#define DMIC_ON_PERIODIC_MODE_MASK 0x1 +#define DMIC_ON_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define DMIC_ON_PERIODIC_INVERSE_SFT 14 +#define DMIC_ON_PERIODIC_INVERSE_MASK 0x1 +#define DMIC_ON_PERIODIC_INVERSE_MASK_SFT (0x1 << 14) +#define DMIC_ON_PERIODIC_ON_CYCLE_SFT 0 +#define DMIC_ON_PERIODIC_ON_CYCLE_MASK 0x3fff +#define DMIC_ON_PERIODIC_ON_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG13 */ +#define PDN_VOW_F32K_CK_SFT 15 +#define PDN_VOW_F32K_CK_MASK 0x1 +#define PDN_VOW_F32K_CK_MASK_SFT (0x1 << 15) +#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDPREAMPLON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG14 */ +#define VOW_SNRDET_PERIODIC_CFG_SFT 15 +#define VOW_SNRDET_PERIODIC_CFG_MASK 0x1 +#define VOW_SNRDET_PERIODIC_CFG_MASK_SFT (0x1 << 15) +#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDPREAMPLDCPRECHARGE_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG15 */ +#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDADCLPWRUP_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG16 */ +#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDGLBVOWLPWEN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG17 */ +#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDDIGMICEN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG18 */ +#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDPWDBMICBIAS0_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG19 */ +#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDPWDBMICBIAS1_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG20 */ +#define CLKSQ_EN_VOW_PERIODIC_MODE_SFT 15 +#define CLKSQ_EN_VOW_PERIODIC_MODE_MASK 0x1 +#define CLKSQ_EN_VOW_PERIODIC_MODE_MASK_SFT (0x1 << 15) +#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_SFT 0 +#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define XO_VOW_CK_EN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG21 */ +#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_SFT 0 +#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define AUDGLB_PWRDN_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG22 */ +#define VOW_ON_PERIODIC_OFF_CYCLE_SFT 0 +#define VOW_ON_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define VOW_ON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_CFG23 */ +#define DMIC_ON_PERIODIC_OFF_CYCLE_SFT 0 +#define DMIC_ON_PERIODIC_OFF_CYCLE_MASK 0x3fff +#define DMIC_ON_PERIODIC_OFF_CYCLE_MASK_SFT (0x3fff << 0) + +/* MT6358_AFE_VOW_PERIODIC_MON0 */ +#define VOW_PERIODIC_MON_SFT 0 +#define VOW_PERIODIC_MON_MASK 0xffff +#define VOW_PERIODIC_MON_MASK_SFT (0xffff << 0) + +/* MT6358_AFE_VOW_PERIODIC_MON1 */ +#define VOW_PERIODIC_COUNT_MON_SFT 0 +#define VOW_PERIODIC_COUNT_MON_MASK 0xffff +#define VOW_PERIODIC_COUNT_MON_MASK_SFT (0xffff << 0) + +/* MT6358_AUDENC_DSN_ID */ +#define AUDENC_ANA_ID_SFT 0 +#define AUDENC_ANA_ID_MASK 0xff +#define AUDENC_ANA_ID_MASK_SFT (0xff << 0) +#define AUDENC_DIG_ID_SFT 8 +#define AUDENC_DIG_ID_MASK 0xff +#define AUDENC_DIG_ID_MASK_SFT (0xff << 8) + +/* MT6358_AUDENC_DSN_REV0 */ +#define AUDENC_ANA_MINOR_REV_SFT 0 +#define AUDENC_ANA_MINOR_REV_MASK 0xf +#define AUDENC_ANA_MINOR_REV_MASK_SFT (0xf << 0) +#define AUDENC_ANA_MAJOR_REV_SFT 4 +#define AUDENC_ANA_MAJOR_REV_MASK 0xf +#define AUDENC_ANA_MAJOR_REV_MASK_SFT (0xf << 4) +#define AUDENC_DIG_MINOR_REV_SFT 8 +#define AUDENC_DIG_MINOR_REV_MASK 0xf +#define AUDENC_DIG_MINOR_REV_MASK_SFT (0xf << 8) +#define AUDENC_DIG_MAJOR_REV_SFT 12 +#define AUDENC_DIG_MAJOR_REV_MASK 0xf +#define AUDENC_DIG_MAJOR_REV_MASK_SFT (0xf << 12) + +/* MT6358_AUDENC_DSN_DBI */ +#define AUDENC_DSN_CBS_SFT 0 +#define AUDENC_DSN_CBS_MASK 0x3 +#define AUDENC_DSN_CBS_MASK_SFT (0x3 << 0) +#define AUDENC_DSN_BIX_SFT 2 +#define AUDENC_DSN_BIX_MASK 0x3 +#define AUDENC_DSN_BIX_MASK_SFT (0x3 << 2) +#define AUDENC_DSN_ESP_SFT 8 +#define AUDENC_DSN_ESP_MASK 0xff +#define AUDENC_DSN_ESP_MASK_SFT (0xff << 8) + +/* MT6358_AUDENC_DSN_FPI */ +#define AUDENC_DSN_FPI_SFT 0 +#define AUDENC_DSN_FPI_MASK 0xff +#define AUDENC_DSN_FPI_MASK_SFT (0xff << 0) + +/* MT6358_AUDENC_ANA_CON0 */ +#define RG_AUDPREAMPLON_SFT 0 +#define RG_AUDPREAMPLON_MASK 0x1 +#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMPLDCCEN_SFT 1 +#define RG_AUDPREAMPLDCCEN_MASK 0x1 +#define RG_AUDPREAMPLDCCEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPLDCPRECHARGE_SFT 2 +#define RG_AUDPREAMPLDCPRECHARGE_MASK 0x1 +#define RG_AUDPREAMPLDCPRECHARGE_MASK_SFT (0x1 << 2) +#define RG_AUDPREAMPLPGATEST_SFT 3 +#define RG_AUDPREAMPLPGATEST_MASK 0x1 +#define RG_AUDPREAMPLPGATEST_MASK_SFT (0x1 << 3) +#define RG_AUDPREAMPLVSCALE_SFT 4 +#define RG_AUDPREAMPLVSCALE_MASK 0x3 +#define RG_AUDPREAMPLVSCALE_MASK_SFT (0x3 << 4) +#define RG_AUDPREAMPLINPUTSEL_SFT 6 +#define RG_AUDPREAMPLINPUTSEL_MASK 0x3 +#define RG_AUDPREAMPLINPUTSEL_MASK_SFT (0x3 << 6) +#define RG_AUDPREAMPLGAIN_SFT 8 +#define RG_AUDPREAMPLGAIN_MASK 0x7 +#define RG_AUDPREAMPLGAIN_MASK_SFT (0x7 << 8) +#define RG_AUDADCLPWRUP_SFT 12 +#define RG_AUDADCLPWRUP_MASK 0x1 +#define RG_AUDADCLPWRUP_MASK_SFT (0x1 << 12) +#define RG_AUDADCLINPUTSEL_SFT 13 +#define RG_AUDADCLINPUTSEL_MASK 0x3 +#define RG_AUDADCLINPUTSEL_MASK_SFT (0x3 << 13) + +/* MT6358_AUDENC_ANA_CON1 */ +#define RG_AUDPREAMPRON_SFT 0 +#define RG_AUDPREAMPRON_MASK 0x1 +#define RG_AUDPREAMPRON_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMPRDCCEN_SFT 1 +#define RG_AUDPREAMPRDCCEN_MASK 0x1 +#define RG_AUDPREAMPRDCCEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPRDCPRECHARGE_SFT 2 +#define RG_AUDPREAMPRDCPRECHARGE_MASK 0x1 +#define RG_AUDPREAMPRDCPRECHARGE_MASK_SFT (0x1 << 2) +#define RG_AUDPREAMPRPGATEST_SFT 3 +#define RG_AUDPREAMPRPGATEST_MASK 0x1 +#define RG_AUDPREAMPRPGATEST_MASK_SFT (0x1 << 3) +#define RG_AUDPREAMPRVSCALE_SFT 4 +#define RG_AUDPREAMPRVSCALE_MASK 0x3 +#define RG_AUDPREAMPRVSCALE_MASK_SFT (0x3 << 4) +#define RG_AUDPREAMPRINPUTSEL_SFT 6 +#define RG_AUDPREAMPRINPUTSEL_MASK 0x3 +#define RG_AUDPREAMPRINPUTSEL_MASK_SFT (0x3 << 6) +#define RG_AUDPREAMPRGAIN_SFT 8 +#define RG_AUDPREAMPRGAIN_MASK 0x7 +#define RG_AUDPREAMPRGAIN_MASK_SFT (0x7 << 8) +#define RG_AUDIO_VOW_EN_SFT 11 +#define RG_AUDIO_VOW_EN_MASK 0x1 +#define RG_AUDIO_VOW_EN_MASK_SFT (0x1 << 11) +#define RG_AUDADCRPWRUP_SFT 12 +#define RG_AUDADCRPWRUP_MASK 0x1 +#define RG_AUDADCRPWRUP_MASK_SFT (0x1 << 12) +#define RG_AUDADCRINPUTSEL_SFT 13 +#define RG_AUDADCRINPUTSEL_MASK 0x3 +#define RG_AUDADCRINPUTSEL_MASK_SFT (0x3 << 13) +#define RG_CLKSQ_EN_VOW_SFT 15 +#define RG_CLKSQ_EN_VOW_MASK 0x1 +#define RG_CLKSQ_EN_VOW_MASK_SFT (0x1 << 15) + +/* MT6358_AUDENC_ANA_CON2 */ +#define RG_AUDULHALFBIAS_SFT 0 +#define RG_AUDULHALFBIAS_MASK 0x1 +#define RG_AUDULHALFBIAS_MASK_SFT (0x1 << 0) +#define RG_AUDGLBVOWLPWEN_SFT 1 +#define RG_AUDGLBVOWLPWEN_MASK 0x1 +#define RG_AUDGLBVOWLPWEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPLPEN_SFT 2 +#define RG_AUDPREAMPLPEN_MASK 0x1 +#define RG_AUDPREAMPLPEN_MASK_SFT (0x1 << 2) +#define RG_AUDADC1STSTAGELPEN_SFT 3 +#define RG_AUDADC1STSTAGELPEN_MASK 0x1 +#define RG_AUDADC1STSTAGELPEN_MASK_SFT (0x1 << 3) +#define RG_AUDADC2NDSTAGELPEN_SFT 4 +#define RG_AUDADC2NDSTAGELPEN_MASK 0x1 +#define RG_AUDADC2NDSTAGELPEN_MASK_SFT (0x1 << 4) +#define RG_AUDADCFLASHLPEN_SFT 5 +#define RG_AUDADCFLASHLPEN_MASK 0x1 +#define RG_AUDADCFLASHLPEN_MASK_SFT (0x1 << 5) +#define RG_AUDPREAMPIDDTEST_SFT 6 +#define RG_AUDPREAMPIDDTEST_MASK 0x3 +#define RG_AUDPREAMPIDDTEST_MASK_SFT (0x3 << 6) +#define RG_AUDADC1STSTAGEIDDTEST_SFT 8 +#define RG_AUDADC1STSTAGEIDDTEST_MASK 0x3 +#define RG_AUDADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8) +#define RG_AUDADC2NDSTAGEIDDTEST_SFT 10 +#define RG_AUDADC2NDSTAGEIDDTEST_MASK 0x3 +#define RG_AUDADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10) +#define RG_AUDADCREFBUFIDDTEST_SFT 12 +#define RG_AUDADCREFBUFIDDTEST_MASK 0x3 +#define RG_AUDADCREFBUFIDDTEST_MASK_SFT (0x3 << 12) +#define RG_AUDADCFLASHIDDTEST_SFT 14 +#define RG_AUDADCFLASHIDDTEST_MASK 0x3 +#define RG_AUDADCFLASHIDDTEST_MASK_SFT (0x3 << 14) + +/* MT6358_AUDENC_ANA_CON3 */ +#define RG_AUDADCDAC0P25FS_SFT 0 +#define RG_AUDADCDAC0P25FS_MASK 0x1 +#define RG_AUDADCDAC0P25FS_MASK_SFT (0x1 << 0) +#define RG_AUDADCCLKSEL_SFT 1 +#define RG_AUDADCCLKSEL_MASK 0x1 +#define RG_AUDADCCLKSEL_MASK_SFT (0x1 << 1) +#define RG_AUDADCCLKSOURCE_SFT 2 +#define RG_AUDADCCLKSOURCE_MASK 0x3 +#define RG_AUDADCCLKSOURCE_MASK_SFT (0x3 << 2) +#define RG_AUDPREAMPAAFEN_SFT 8 +#define RG_AUDPREAMPAAFEN_MASK 0x1 +#define RG_AUDPREAMPAAFEN_MASK_SFT (0x1 << 8) +#define RG_DCCVCMBUFLPMODSEL_SFT 9 +#define RG_DCCVCMBUFLPMODSEL_MASK 0x1 +#define RG_DCCVCMBUFLPMODSEL_MASK_SFT (0x1 << 9) +#define RG_DCCVCMBUFLPSWEN_SFT 10 +#define RG_DCCVCMBUFLPSWEN_MASK 0x1 +#define RG_DCCVCMBUFLPSWEN_MASK_SFT (0x1 << 10) +#define RG_CMSTBENH_SFT 11 +#define RG_CMSTBENH_MASK 0x1 +#define RG_CMSTBENH_MASK_SFT (0x1 << 11) +#define RG_PGABODYSW_SFT 12 +#define RG_PGABODYSW_MASK 0x1 +#define RG_PGABODYSW_MASK_SFT (0x1 << 12) + +/* MT6358_AUDENC_ANA_CON4 */ +#define RG_AUDADC1STSTAGESDENB_SFT 0 +#define RG_AUDADC1STSTAGESDENB_MASK 0x1 +#define RG_AUDADC1STSTAGESDENB_MASK_SFT (0x1 << 0) +#define RG_AUDADC2NDSTAGERESET_SFT 1 +#define RG_AUDADC2NDSTAGERESET_MASK 0x1 +#define RG_AUDADC2NDSTAGERESET_MASK_SFT (0x1 << 1) +#define RG_AUDADC3RDSTAGERESET_SFT 2 +#define RG_AUDADC3RDSTAGERESET_MASK 0x1 +#define RG_AUDADC3RDSTAGERESET_MASK_SFT (0x1 << 2) +#define RG_AUDADCFSRESET_SFT 3 +#define RG_AUDADCFSRESET_MASK 0x1 +#define RG_AUDADCFSRESET_MASK_SFT (0x1 << 3) +#define RG_AUDADCWIDECM_SFT 4 +#define RG_AUDADCWIDECM_MASK 0x1 +#define RG_AUDADCWIDECM_MASK_SFT (0x1 << 4) +#define RG_AUDADCNOPATEST_SFT 5 +#define RG_AUDADCNOPATEST_MASK 0x1 +#define RG_AUDADCNOPATEST_MASK_SFT (0x1 << 5) +#define RG_AUDADCBYPASS_SFT 6 +#define RG_AUDADCBYPASS_MASK 0x1 +#define RG_AUDADCBYPASS_MASK_SFT (0x1 << 6) +#define RG_AUDADCFFBYPASS_SFT 7 +#define RG_AUDADCFFBYPASS_MASK 0x1 +#define RG_AUDADCFFBYPASS_MASK_SFT (0x1 << 7) +#define RG_AUDADCDACFBCURRENT_SFT 8 +#define RG_AUDADCDACFBCURRENT_MASK 0x1 +#define RG_AUDADCDACFBCURRENT_MASK_SFT (0x1 << 8) +#define RG_AUDADCDACIDDTEST_SFT 9 +#define RG_AUDADCDACIDDTEST_MASK 0x3 +#define RG_AUDADCDACIDDTEST_MASK_SFT (0x3 << 9) +#define RG_AUDADCDACNRZ_SFT 11 +#define RG_AUDADCDACNRZ_MASK 0x1 +#define RG_AUDADCDACNRZ_MASK_SFT (0x1 << 11) +#define RG_AUDADCNODEM_SFT 12 +#define RG_AUDADCNODEM_MASK 0x1 +#define RG_AUDADCNODEM_MASK_SFT (0x1 << 12) +#define RG_AUDADCDACTEST_SFT 13 +#define RG_AUDADCDACTEST_MASK 0x1 +#define RG_AUDADCDACTEST_MASK_SFT (0x1 << 13) + +/* MT6358_AUDENC_ANA_CON5 */ +#define RG_AUDRCTUNEL_SFT 0 +#define RG_AUDRCTUNEL_MASK 0x1f +#define RG_AUDRCTUNEL_MASK_SFT (0x1f << 0) +#define RG_AUDRCTUNELSEL_SFT 5 +#define RG_AUDRCTUNELSEL_MASK 0x1 +#define RG_AUDRCTUNELSEL_MASK_SFT (0x1 << 5) +#define RG_AUDRCTUNER_SFT 8 +#define RG_AUDRCTUNER_MASK 0x1f +#define RG_AUDRCTUNER_MASK_SFT (0x1f << 8) +#define RG_AUDRCTUNERSEL_SFT 13 +#define RG_AUDRCTUNERSEL_MASK 0x1 +#define RG_AUDRCTUNERSEL_MASK_SFT (0x1 << 13) + +/* MT6358_AUDENC_ANA_CON6 */ +#define RG_CLKSQ_EN_SFT 0 +#define RG_CLKSQ_EN_MASK 0x1 +#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0) +#define RG_CLKSQ_IN_SEL_TEST_SFT 1 +#define RG_CLKSQ_IN_SEL_TEST_MASK 0x1 +#define RG_CLKSQ_IN_SEL_TEST_MASK_SFT (0x1 << 1) +#define RG_CM_REFGENSEL_SFT 2 +#define RG_CM_REFGENSEL_MASK 0x1 +#define RG_CM_REFGENSEL_MASK_SFT (0x1 << 2) +#define RG_AUDSPARE_SFT 4 +#define RG_AUDSPARE_MASK 0xf +#define RG_AUDSPARE_MASK_SFT (0xf << 4) +#define RG_AUDENCSPARE_SFT 8 +#define RG_AUDENCSPARE_MASK 0x3f +#define RG_AUDENCSPARE_MASK_SFT (0x3f << 8) + +/* MT6358_AUDENC_ANA_CON7 */ +#define RG_AUDENCSPARE2_SFT 0 +#define RG_AUDENCSPARE2_MASK 0xff +#define RG_AUDENCSPARE2_MASK_SFT (0xff << 0) + +/* MT6358_AUDENC_ANA_CON8 */ +#define RG_AUDDIGMICEN_SFT 0 +#define RG_AUDDIGMICEN_MASK 0x1 +#define RG_AUDDIGMICEN_MASK_SFT (0x1 << 0) +#define RG_AUDDIGMICBIAS_SFT 1 +#define RG_AUDDIGMICBIAS_MASK 0x3 +#define RG_AUDDIGMICBIAS_MASK_SFT (0x3 << 1) +#define RG_DMICHPCLKEN_SFT 3 +#define RG_DMICHPCLKEN_MASK 0x1 +#define RG_DMICHPCLKEN_MASK_SFT (0x1 << 3) +#define RG_AUDDIGMICPDUTY_SFT 4 +#define RG_AUDDIGMICPDUTY_MASK 0x3 +#define RG_AUDDIGMICPDUTY_MASK_SFT (0x3 << 4) +#define RG_AUDDIGMICNDUTY_SFT 6 +#define RG_AUDDIGMICNDUTY_MASK 0x3 +#define RG_AUDDIGMICNDUTY_MASK_SFT (0x3 << 6) +#define RG_DMICMONEN_SFT 8 +#define RG_DMICMONEN_MASK 0x1 +#define RG_DMICMONEN_MASK_SFT (0x1 << 8) +#define RG_DMICMONSEL_SFT 9 +#define RG_DMICMONSEL_MASK 0x7 +#define RG_DMICMONSEL_MASK_SFT (0x7 << 9) +#define RG_AUDSPAREVMIC_SFT 12 +#define RG_AUDSPAREVMIC_MASK 0xf +#define RG_AUDSPAREVMIC_MASK_SFT (0xf << 12) + +/* MT6358_AUDENC_ANA_CON9 */ +#define RG_AUDPWDBMICBIAS0_SFT 0 +#define RG_AUDPWDBMICBIAS0_MASK 0x1 +#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS0BYPASSEN_SFT 1 +#define RG_AUDMICBIAS0BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS0BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS0LOWPEN_SFT 2 +#define RG_AUDMICBIAS0LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS0LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDMICBIAS0VREF_SFT 4 +#define RG_AUDMICBIAS0VREF_MASK 0x7 +#define RG_AUDMICBIAS0VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS0DCSW0P1EN_SFT 8 +#define RG_AUDMICBIAS0DCSW0P1EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0P1EN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS0DCSW0P2EN_SFT 9 +#define RG_AUDMICBIAS0DCSW0P2EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0P2EN_MASK_SFT (0x1 << 9) +#define RG_AUDMICBIAS0DCSW0NEN_SFT 10 +#define RG_AUDMICBIAS0DCSW0NEN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0NEN_MASK_SFT (0x1 << 10) +#define RG_AUDMICBIAS0DCSW2P1EN_SFT 12 +#define RG_AUDMICBIAS0DCSW2P1EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2P1EN_MASK_SFT (0x1 << 12) +#define RG_AUDMICBIAS0DCSW2P2EN_SFT 13 +#define RG_AUDMICBIAS0DCSW2P2EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2P2EN_MASK_SFT (0x1 << 13) +#define RG_AUDMICBIAS0DCSW2NEN_SFT 14 +#define RG_AUDMICBIAS0DCSW2NEN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2NEN_MASK_SFT (0x1 << 14) + +/* MT6358_AUDENC_ANA_CON10 */ +#define RG_AUDPWDBMICBIAS1_SFT 0 +#define RG_AUDPWDBMICBIAS1_MASK 0x1 +#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS1BYPASSEN_SFT 1 +#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS1LOWPEN_SFT 2 +#define RG_AUDMICBIAS1LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDMICBIAS1VREF_SFT 4 +#define RG_AUDMICBIAS1VREF_MASK 0x7 +#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS1DCSW1PEN_SFT 8 +#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1 +#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS1DCSW1NEN_SFT 9 +#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1 +#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9) +#define RG_BANDGAPGEN_SFT 12 +#define RG_BANDGAPGEN_MASK 0x1 +#define RG_BANDGAPGEN_MASK_SFT (0x1 << 12) +#define RG_MTEST_EN_SFT 13 +#define RG_MTEST_EN_MASK 0x1 +#define RG_MTEST_EN_MASK_SFT (0x1 << 13) +#define RG_MTEST_SEL_SFT 14 +#define RG_MTEST_SEL_MASK 0x1 +#define RG_MTEST_SEL_MASK_SFT (0x1 << 14) +#define RG_MTEST_CURRENT_SFT 15 +#define RG_MTEST_CURRENT_MASK 0x1 +#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 15) + +/* MT6358_AUDENC_ANA_CON11 */ +#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0 +#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0) +#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1 +#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1) +#define RG_AUDACCDETVIN1PULLLOW_SFT 2 +#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1 +#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 2) +#define RG_AUDACCDETVTHACAL_SFT 4 +#define RG_AUDACCDETVTHACAL_MASK 0x1 +#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4) +#define RG_AUDACCDETVTHBCAL_SFT 5 +#define RG_AUDACCDETVTHBCAL_MASK 0x1 +#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5) +#define RG_AUDACCDETTVDET_SFT 6 +#define RG_AUDACCDETTVDET_MASK 0x1 +#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6) +#define RG_ACCDETSEL_SFT 7 +#define RG_ACCDETSEL_MASK 0x1 +#define RG_ACCDETSEL_MASK_SFT (0x1 << 7) +#define RG_SWBUFMODSEL_SFT 8 +#define RG_SWBUFMODSEL_MASK 0x1 +#define RG_SWBUFMODSEL_MASK_SFT (0x1 << 8) +#define RG_SWBUFSWEN_SFT 9 +#define RG_SWBUFSWEN_MASK 0x1 +#define RG_SWBUFSWEN_MASK_SFT (0x1 << 9) +#define RG_EINTCOMPVTH_SFT 10 +#define RG_EINTCOMPVTH_MASK 0x1 +#define RG_EINTCOMPVTH_MASK_SFT (0x1 << 10) +#define RG_EINTCONFIGACCDET_SFT 11 +#define RG_EINTCONFIGACCDET_MASK 0x1 +#define RG_EINTCONFIGACCDET_MASK_SFT (0x1 << 11) +#define RG_EINTHIRENB_SFT 12 +#define RG_EINTHIRENB_MASK 0x1 +#define RG_EINTHIRENB_MASK_SFT (0x1 << 12) +#define RG_ACCDET2AUXRESBYPASS_SFT 13 +#define RG_ACCDET2AUXRESBYPASS_MASK 0x1 +#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13) +#define RG_ACCDET2AUXBUFFERBYPASS_SFT 14 +#define RG_ACCDET2AUXBUFFERBYPASS_MASK 0x1 +#define RG_ACCDET2AUXBUFFERBYPASS_MASK_SFT (0x1 << 14) +#define RG_ACCDET2AUXSWEN_SFT 15 +#define RG_ACCDET2AUXSWEN_MASK 0x1 +#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 15) + +/* MT6358_AUDENC_ANA_CON12 */ +#define RGS_AUDRCTUNELREAD_SFT 0 +#define RGS_AUDRCTUNELREAD_MASK 0x1f +#define RGS_AUDRCTUNELREAD_MASK_SFT (0x1f << 0) +#define RGS_AUDRCTUNERREAD_SFT 8 +#define RGS_AUDRCTUNERREAD_MASK 0x1f +#define RGS_AUDRCTUNERREAD_MASK_SFT (0x1f << 8) + +/* MT6358_AUDDEC_DSN_ID */ +#define AUDDEC_ANA_ID_SFT 0 +#define AUDDEC_ANA_ID_MASK 0xff +#define AUDDEC_ANA_ID_MASK_SFT (0xff << 0) +#define AUDDEC_DIG_ID_SFT 8 +#define AUDDEC_DIG_ID_MASK 0xff +#define AUDDEC_DIG_ID_MASK_SFT (0xff << 8) + +/* MT6358_AUDDEC_DSN_REV0 */ +#define AUDDEC_ANA_MINOR_REV_SFT 0 +#define AUDDEC_ANA_MINOR_REV_MASK 0xf +#define AUDDEC_ANA_MINOR_REV_MASK_SFT (0xf << 0) +#define AUDDEC_ANA_MAJOR_REV_SFT 4 +#define AUDDEC_ANA_MAJOR_REV_MASK 0xf +#define AUDDEC_ANA_MAJOR_REV_MASK_SFT (0xf << 4) +#define AUDDEC_DIG_MINOR_REV_SFT 8 +#define AUDDEC_DIG_MINOR_REV_MASK 0xf +#define AUDDEC_DIG_MINOR_REV_MASK_SFT (0xf << 8) +#define AUDDEC_DIG_MAJOR_REV_SFT 12 +#define AUDDEC_DIG_MAJOR_REV_MASK 0xf +#define AUDDEC_DIG_MAJOR_REV_MASK_SFT (0xf << 12) + +/* MT6358_AUDDEC_DSN_DBI */ +#define AUDDEC_DSN_CBS_SFT 0 +#define AUDDEC_DSN_CBS_MASK 0x3 +#define AUDDEC_DSN_CBS_MASK_SFT (0x3 << 0) +#define AUDDEC_DSN_BIX_SFT 2 +#define AUDDEC_DSN_BIX_MASK 0x3 +#define AUDDEC_DSN_BIX_MASK_SFT (0x3 << 2) +#define AUDDEC_DSN_ESP_SFT 8 +#define AUDDEC_DSN_ESP_MASK 0xff +#define AUDDEC_DSN_ESP_MASK_SFT (0xff << 8) + +/* MT6358_AUDDEC_DSN_FPI */ +#define AUDDEC_DSN_FPI_SFT 0 +#define AUDDEC_DSN_FPI_MASK 0xff +#define AUDDEC_DSN_FPI_MASK_SFT (0xff << 0) + +/* MT6358_AUDDEC_ANA_CON0 */ +#define RG_AUDDACLPWRUP_VAUDP15_SFT 0 +#define RG_AUDDACLPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDDACLPWRUP_VAUDP15_MASK_SFT (0x1 << 0) +#define RG_AUDDACRPWRUP_VAUDP15_SFT 1 +#define RG_AUDDACRPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDDACRPWRUP_VAUDP15_MASK_SFT (0x1 << 1) +#define RG_AUD_DAC_PWR_UP_VA28_SFT 2 +#define RG_AUD_DAC_PWR_UP_VA28_MASK 0x1 +#define RG_AUD_DAC_PWR_UP_VA28_MASK_SFT (0x1 << 2) +#define RG_AUD_DAC_PWL_UP_VA28_SFT 3 +#define RG_AUD_DAC_PWL_UP_VA28_MASK 0x1 +#define RG_AUD_DAC_PWL_UP_VA28_MASK_SFT (0x1 << 3) +#define RG_AUDHPLPWRUP_VAUDP15_SFT 4 +#define RG_AUDHPLPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPLPWRUP_VAUDP15_MASK_SFT (0x1 << 4) +#define RG_AUDHPRPWRUP_VAUDP15_SFT 5 +#define RG_AUDHPRPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPRPWRUP_VAUDP15_MASK_SFT (0x1 << 5) +#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_SFT 6 +#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_MASK 0x1 +#define RG_AUDHPLPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 6) +#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_SFT 7 +#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_MASK 0x1 +#define RG_AUDHPRPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 7) +#define RG_AUDHPLMUXINPUTSEL_VAUDP15_SFT 8 +#define RG_AUDHPLMUXINPUTSEL_VAUDP15_MASK 0x3 +#define RG_AUDHPLMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 8) +#define RG_AUDHPRMUXINPUTSEL_VAUDP15_SFT 10 +#define RG_AUDHPRMUXINPUTSEL_VAUDP15_MASK 0x3 +#define RG_AUDHPRMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 10) +#define RG_AUDHPLSCDISABLE_VAUDP15_SFT 12 +#define RG_AUDHPLSCDISABLE_VAUDP15_MASK 0x1 +#define RG_AUDHPLSCDISABLE_VAUDP15_MASK_SFT (0x1 << 12) +#define RG_AUDHPRSCDISABLE_VAUDP15_SFT 13 +#define RG_AUDHPRSCDISABLE_VAUDP15_MASK 0x1 +#define RG_AUDHPRSCDISABLE_VAUDP15_MASK_SFT (0x1 << 13) +#define RG_AUDHPLBSCCURRENT_VAUDP15_SFT 14 +#define RG_AUDHPLBSCCURRENT_VAUDP15_MASK 0x1 +#define RG_AUDHPLBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 14) +#define RG_AUDHPRBSCCURRENT_VAUDP15_SFT 15 +#define RG_AUDHPRBSCCURRENT_VAUDP15_MASK 0x1 +#define RG_AUDHPRBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 15) + +/* MT6358_AUDDEC_ANA_CON1 */ +#define RG_AUDHPLOUTPWRUP_VAUDP15_SFT 0 +#define RG_AUDHPLOUTPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPLOUTPWRUP_VAUDP15_MASK_SFT (0x1 << 0) +#define RG_AUDHPROUTPWRUP_VAUDP15_SFT 1 +#define RG_AUDHPROUTPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPROUTPWRUP_VAUDP15_MASK_SFT (0x1 << 1) +#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_SFT 2 +#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPLOUTAUXPWRUP_VAUDP15_MASK_SFT (0x1 << 2) +#define RG_AUDHPROUTAUXPWRUP_VAUDP15_SFT 3 +#define RG_AUDHPROUTAUXPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHPROUTAUXPWRUP_VAUDP15_MASK_SFT (0x1 << 3) +#define RG_HPLAUXFBRSW_EN_VAUDP15_SFT 4 +#define RG_HPLAUXFBRSW_EN_VAUDP15_MASK 0x1 +#define RG_HPLAUXFBRSW_EN_VAUDP15_MASK_SFT (0x1 << 4) +#define RG_HPRAUXFBRSW_EN_VAUDP15_SFT 5 +#define RG_HPRAUXFBRSW_EN_VAUDP15_MASK 0x1 +#define RG_HPRAUXFBRSW_EN_VAUDP15_MASK_SFT (0x1 << 5) +#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_SFT 6 +#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_MASK 0x1 +#define RG_HPLSHORT2HPLAUX_EN_VAUDP15_MASK_SFT (0x1 << 6) +#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_SFT 7 +#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_MASK 0x1 +#define RG_HPRSHORT2HPRAUX_EN_VAUDP15_MASK_SFT (0x1 << 7) +#define RG_HPLOUTSTGCTRL_VAUDP15_SFT 8 +#define RG_HPLOUTSTGCTRL_VAUDP15_MASK 0x7 +#define RG_HPLOUTSTGCTRL_VAUDP15_MASK_SFT (0x7 << 8) +#define RG_HPROUTSTGCTRL_VAUDP15_SFT 11 +#define RG_HPROUTSTGCTRL_VAUDP15_MASK 0x7 +#define RG_HPROUTSTGCTRL_VAUDP15_MASK_SFT (0x7 << 11) + +/* MT6358_AUDDEC_ANA_CON2 */ +#define RG_HPLOUTPUTSTBENH_VAUDP15_SFT 0 +#define RG_HPLOUTPUTSTBENH_VAUDP15_MASK 0x7 +#define RG_HPLOUTPUTSTBENH_VAUDP15_MASK_SFT (0x7 << 0) +#define RG_HPROUTPUTSTBENH_VAUDP15_SFT 4 +#define RG_HPROUTPUTSTBENH_VAUDP15_MASK 0x7 +#define RG_HPROUTPUTSTBENH_VAUDP15_MASK_SFT (0x7 << 4) +#define RG_AUDHPSTARTUP_VAUDP15_SFT 13 +#define RG_AUDHPSTARTUP_VAUDP15_MASK 0x1 +#define RG_AUDHPSTARTUP_VAUDP15_MASK_SFT (0x1 << 13) +#define RG_AUDREFN_DERES_EN_VAUDP15_SFT 14 +#define RG_AUDREFN_DERES_EN_VAUDP15_MASK 0x1 +#define RG_AUDREFN_DERES_EN_VAUDP15_MASK_SFT (0x1 << 14) +#define RG_HPPSHORT2VCM_VAUDP15_SFT 15 +#define RG_HPPSHORT2VCM_VAUDP15_MASK 0x1 +#define RG_HPPSHORT2VCM_VAUDP15_MASK_SFT (0x1 << 15) + +/* MT6358_AUDDEC_ANA_CON3 */ +#define RG_HPINPUTSTBENH_VAUDP15_SFT 13 +#define RG_HPINPUTSTBENH_VAUDP15_MASK 0x1 +#define RG_HPINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 13) +#define RG_HPINPUTRESET0_VAUDP15_SFT 14 +#define RG_HPINPUTRESET0_VAUDP15_MASK 0x1 +#define RG_HPINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 14) +#define RG_HPOUTPUTRESET0_VAUDP15_SFT 15 +#define RG_HPOUTPUTRESET0_VAUDP15_MASK 0x1 +#define RG_HPOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 15) + +/* MT6358_AUDDEC_ANA_CON4 */ +#define RG_ABIDEC_RSVD0_VAUDP28_SFT 0 +#define RG_ABIDEC_RSVD0_VAUDP28_MASK 0xff +#define RG_ABIDEC_RSVD0_VAUDP28_MASK_SFT (0xff << 0) + +/* MT6358_AUDDEC_ANA_CON5 */ +#define RG_AUDHPDECMGAINADJ_VAUDP15_SFT 0 +#define RG_AUDHPDECMGAINADJ_VAUDP15_MASK 0x7 +#define RG_AUDHPDECMGAINADJ_VAUDP15_MASK_SFT (0x7 << 0) +#define RG_AUDHPDEDMGAINADJ_VAUDP15_SFT 4 +#define RG_AUDHPDEDMGAINADJ_VAUDP15_MASK 0x7 +#define RG_AUDHPDEDMGAINADJ_VAUDP15_MASK_SFT (0x7 << 4) + +/* MT6358_AUDDEC_ANA_CON6 */ +#define RG_AUDHSPWRUP_VAUDP15_SFT 0 +#define RG_AUDHSPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDHSPWRUP_VAUDP15_MASK_SFT (0x1 << 0) +#define RG_AUDHSPWRUP_IBIAS_VAUDP15_SFT 1 +#define RG_AUDHSPWRUP_IBIAS_VAUDP15_MASK 0x1 +#define RG_AUDHSPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 1) +#define RG_AUDHSMUXINPUTSEL_VAUDP15_SFT 2 +#define RG_AUDHSMUXINPUTSEL_VAUDP15_MASK 0x3 +#define RG_AUDHSMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 2) +#define RG_AUDHSSCDISABLE_VAUDP15_SFT 4 +#define RG_AUDHSSCDISABLE_VAUDP15_MASK 0x1 +#define RG_AUDHSSCDISABLE_VAUDP15_MASK_SFT (0x1 << 4) +#define RG_AUDHSBSCCURRENT_VAUDP15_SFT 5 +#define RG_AUDHSBSCCURRENT_VAUDP15_MASK 0x1 +#define RG_AUDHSBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 5) +#define RG_AUDHSSTARTUP_VAUDP15_SFT 6 +#define RG_AUDHSSTARTUP_VAUDP15_MASK 0x1 +#define RG_AUDHSSTARTUP_VAUDP15_MASK_SFT (0x1 << 6) +#define RG_HSOUTPUTSTBENH_VAUDP15_SFT 7 +#define RG_HSOUTPUTSTBENH_VAUDP15_MASK 0x1 +#define RG_HSOUTPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 7) +#define RG_HSINPUTSTBENH_VAUDP15_SFT 8 +#define RG_HSINPUTSTBENH_VAUDP15_MASK 0x1 +#define RG_HSINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 8) +#define RG_HSINPUTRESET0_VAUDP15_SFT 9 +#define RG_HSINPUTRESET0_VAUDP15_MASK 0x1 +#define RG_HSINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 9) +#define RG_HSOUTPUTRESET0_VAUDP15_SFT 10 +#define RG_HSOUTPUTRESET0_VAUDP15_MASK 0x1 +#define RG_HSOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 10) +#define RG_HSOUT_SHORTVCM_VAUDP15_SFT 11 +#define RG_HSOUT_SHORTVCM_VAUDP15_MASK 0x1 +#define RG_HSOUT_SHORTVCM_VAUDP15_MASK_SFT (0x1 << 11) + +/* MT6358_AUDDEC_ANA_CON7 */ +#define RG_AUDLOLPWRUP_VAUDP15_SFT 0 +#define RG_AUDLOLPWRUP_VAUDP15_MASK 0x1 +#define RG_AUDLOLPWRUP_VAUDP15_MASK_SFT (0x1 << 0) +#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_SFT 1 +#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_MASK 0x1 +#define RG_AUDLOLPWRUP_IBIAS_VAUDP15_MASK_SFT (0x1 << 1) +#define RG_AUDLOLMUXINPUTSEL_VAUDP15_SFT 2 +#define RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK 0x3 +#define RG_AUDLOLMUXINPUTSEL_VAUDP15_MASK_SFT (0x3 << 2) +#define RG_AUDLOLSCDISABLE_VAUDP15_SFT 4 +#define RG_AUDLOLSCDISABLE_VAUDP15_MASK 0x1 +#define RG_AUDLOLSCDISABLE_VAUDP15_MASK_SFT (0x1 << 4) +#define RG_AUDLOLBSCCURRENT_VAUDP15_SFT 5 +#define RG_AUDLOLBSCCURRENT_VAUDP15_MASK 0x1 +#define RG_AUDLOLBSCCURRENT_VAUDP15_MASK_SFT (0x1 << 5) +#define RG_AUDLOSTARTUP_VAUDP15_SFT 6 +#define RG_AUDLOSTARTUP_VAUDP15_MASK 0x1 +#define RG_AUDLOSTARTUP_VAUDP15_MASK_SFT (0x1 << 6) +#define RG_LOINPUTSTBENH_VAUDP15_SFT 7 +#define RG_LOINPUTSTBENH_VAUDP15_MASK 0x1 +#define RG_LOINPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 7) +#define RG_LOOUTPUTSTBENH_VAUDP15_SFT 8 +#define RG_LOOUTPUTSTBENH_VAUDP15_MASK 0x1 +#define RG_LOOUTPUTSTBENH_VAUDP15_MASK_SFT (0x1 << 8) +#define RG_LOINPUTRESET0_VAUDP15_SFT 9 +#define RG_LOINPUTRESET0_VAUDP15_MASK 0x1 +#define RG_LOINPUTRESET0_VAUDP15_MASK_SFT (0x1 << 9) +#define RG_LOOUTPUTRESET0_VAUDP15_SFT 10 +#define RG_LOOUTPUTRESET0_VAUDP15_MASK 0x1 +#define RG_LOOUTPUTRESET0_VAUDP15_MASK_SFT (0x1 << 10) +#define RG_LOOUT_SHORTVCM_VAUDP15_SFT 11 +#define RG_LOOUT_SHORTVCM_VAUDP15_MASK 0x1 +#define RG_LOOUT_SHORTVCM_VAUDP15_MASK_SFT (0x1 << 11) + +/* MT6358_AUDDEC_ANA_CON8 */ +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_SFT 0 +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_MASK 0xf +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP15_MASK_SFT (0xf << 0) +#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_SFT 4 +#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_MASK 0x3 +#define RG_AUDTRIMBUF_GAINSEL_VAUDP15_MASK_SFT (0x3 << 4) +#define RG_AUDTRIMBUF_EN_VAUDP15_SFT 6 +#define RG_AUDTRIMBUF_EN_VAUDP15_MASK 0x1 +#define RG_AUDTRIMBUF_EN_VAUDP15_MASK_SFT (0x1 << 6) +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_SFT 8 +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_MASK 0x3 +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP15_MASK_SFT (0x3 << 8) +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_SFT 10 +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_MASK 0x3 +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP15_MASK_SFT (0x3 << 10) +#define RG_AUDHPSPKDET_EN_VAUDP15_SFT 12 +#define RG_AUDHPSPKDET_EN_VAUDP15_MASK 0x1 +#define RG_AUDHPSPKDET_EN_VAUDP15_MASK_SFT (0x1 << 12) + +/* MT6358_AUDDEC_ANA_CON9 */ +#define RG_ABIDEC_RSVD0_VA28_SFT 0 +#define RG_ABIDEC_RSVD0_VA28_MASK 0xff +#define RG_ABIDEC_RSVD0_VA28_MASK_SFT (0xff << 0) +#define RG_ABIDEC_RSVD0_VAUDP15_SFT 8 +#define RG_ABIDEC_RSVD0_VAUDP15_MASK 0xff +#define RG_ABIDEC_RSVD0_VAUDP15_MASK_SFT (0xff << 8) + +/* MT6358_AUDDEC_ANA_CON10 */ +#define RG_ABIDEC_RSVD1_VAUDP15_SFT 0 +#define RG_ABIDEC_RSVD1_VAUDP15_MASK 0xff +#define RG_ABIDEC_RSVD1_VAUDP15_MASK_SFT (0xff << 0) +#define RG_ABIDEC_RSVD2_VAUDP15_SFT 8 +#define RG_ABIDEC_RSVD2_VAUDP15_MASK 0xff +#define RG_ABIDEC_RSVD2_VAUDP15_MASK_SFT (0xff << 8) + +/* MT6358_AUDDEC_ANA_CON11 */ +#define RG_AUDZCDMUXSEL_VAUDP15_SFT 0 +#define RG_AUDZCDMUXSEL_VAUDP15_MASK 0x7 +#define RG_AUDZCDMUXSEL_VAUDP15_MASK_SFT (0x7 << 0) +#define RG_AUDZCDCLKSEL_VAUDP15_SFT 3 +#define RG_AUDZCDCLKSEL_VAUDP15_MASK 0x1 +#define RG_AUDZCDCLKSEL_VAUDP15_MASK_SFT (0x1 << 3) +#define RG_AUDBIASADJ_0_VAUDP15_SFT 7 +#define RG_AUDBIASADJ_0_VAUDP15_MASK 0x1ff +#define RG_AUDBIASADJ_0_VAUDP15_MASK_SFT (0x1ff << 7) + +/* MT6358_AUDDEC_ANA_CON12 */ +#define RG_AUDBIASADJ_1_VAUDP15_SFT 0 +#define RG_AUDBIASADJ_1_VAUDP15_MASK 0xff +#define RG_AUDBIASADJ_1_VAUDP15_MASK_SFT (0xff << 0) +#define RG_AUDIBIASPWRDN_VAUDP15_SFT 8 +#define RG_AUDIBIASPWRDN_VAUDP15_MASK 0x1 +#define RG_AUDIBIASPWRDN_VAUDP15_MASK_SFT (0x1 << 8) + +/* MT6358_AUDDEC_ANA_CON13 */ +#define RG_RSTB_DECODER_VA28_SFT 0 +#define RG_RSTB_DECODER_VA28_MASK 0x1 +#define RG_RSTB_DECODER_VA28_MASK_SFT (0x1 << 0) +#define RG_SEL_DECODER_96K_VA28_SFT 1 +#define RG_SEL_DECODER_96K_VA28_MASK 0x1 +#define RG_SEL_DECODER_96K_VA28_MASK_SFT (0x1 << 1) +#define RG_SEL_DELAY_VCORE_SFT 2 +#define RG_SEL_DELAY_VCORE_MASK 0x1 +#define RG_SEL_DELAY_VCORE_MASK_SFT (0x1 << 2) +#define RG_AUDGLB_PWRDN_VA28_SFT 4 +#define RG_AUDGLB_PWRDN_VA28_MASK 0x1 +#define RG_AUDGLB_PWRDN_VA28_MASK_SFT (0x1 << 4) +#define RG_RSTB_ENCODER_VA28_SFT 5 +#define RG_RSTB_ENCODER_VA28_MASK 0x1 +#define RG_RSTB_ENCODER_VA28_MASK_SFT (0x1 << 5) +#define RG_SEL_ENCODER_96K_VA28_SFT 6 +#define RG_SEL_ENCODER_96K_VA28_MASK 0x1 +#define RG_SEL_ENCODER_96K_VA28_MASK_SFT (0x1 << 6) + +/* MT6358_AUDDEC_ANA_CON14 */ +#define RG_HCLDO_EN_VA18_SFT 0 +#define RG_HCLDO_EN_VA18_MASK 0x1 +#define RG_HCLDO_EN_VA18_MASK_SFT (0x1 << 0) +#define RG_HCLDO_PDDIS_EN_VA18_SFT 1 +#define RG_HCLDO_PDDIS_EN_VA18_MASK 0x1 +#define RG_HCLDO_PDDIS_EN_VA18_MASK_SFT (0x1 << 1) +#define RG_HCLDO_REMOTE_SENSE_VA18_SFT 2 +#define RG_HCLDO_REMOTE_SENSE_VA18_MASK 0x1 +#define RG_HCLDO_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 2) +#define RG_LCLDO_EN_VA18_SFT 4 +#define RG_LCLDO_EN_VA18_MASK 0x1 +#define RG_LCLDO_EN_VA18_MASK_SFT (0x1 << 4) +#define RG_LCLDO_PDDIS_EN_VA18_SFT 5 +#define RG_LCLDO_PDDIS_EN_VA18_MASK 0x1 +#define RG_LCLDO_PDDIS_EN_VA18_MASK_SFT (0x1 << 5) +#define RG_LCLDO_REMOTE_SENSE_VA18_SFT 6 +#define RG_LCLDO_REMOTE_SENSE_VA18_MASK 0x1 +#define RG_LCLDO_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 6) +#define RG_LCLDO_ENC_EN_VA28_SFT 8 +#define RG_LCLDO_ENC_EN_VA28_MASK 0x1 +#define RG_LCLDO_ENC_EN_VA28_MASK_SFT (0x1 << 8) +#define RG_LCLDO_ENC_PDDIS_EN_VA28_SFT 9 +#define RG_LCLDO_ENC_PDDIS_EN_VA28_MASK 0x1 +#define RG_LCLDO_ENC_PDDIS_EN_VA28_MASK_SFT (0x1 << 9) +#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_SFT 10 +#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_MASK 0x1 +#define RG_LCLDO_ENC_REMOTE_SENSE_VA28_MASK_SFT (0x1 << 10) +#define RG_VA33REFGEN_EN_VA18_SFT 12 +#define RG_VA33REFGEN_EN_VA18_MASK 0x1 +#define RG_VA33REFGEN_EN_VA18_MASK_SFT (0x1 << 12) +#define RG_VA28REFGEN_EN_VA28_SFT 13 +#define RG_VA28REFGEN_EN_VA28_MASK 0x1 +#define RG_VA28REFGEN_EN_VA28_MASK_SFT (0x1 << 13) +#define RG_HCLDO_VOSEL_VA18_SFT 14 +#define RG_HCLDO_VOSEL_VA18_MASK 0x1 +#define RG_HCLDO_VOSEL_VA18_MASK_SFT (0x1 << 14) +#define RG_LCLDO_VOSEL_VA18_SFT 15 +#define RG_LCLDO_VOSEL_VA18_MASK 0x1 +#define RG_LCLDO_VOSEL_VA18_MASK_SFT (0x1 << 15) + +/* MT6358_AUDDEC_ANA_CON15 */ +#define RG_NVREG_EN_VAUDP15_SFT 0 +#define RG_NVREG_EN_VAUDP15_MASK 0x1 +#define RG_NVREG_EN_VAUDP15_MASK_SFT (0x1 << 0) +#define RG_NVREG_PULL0V_VAUDP15_SFT 1 +#define RG_NVREG_PULL0V_VAUDP15_MASK 0x1 +#define RG_NVREG_PULL0V_VAUDP15_MASK_SFT (0x1 << 1) +#define RG_AUDPMU_RSD0_VAUDP15_SFT 4 +#define RG_AUDPMU_RSD0_VAUDP15_MASK 0xf +#define RG_AUDPMU_RSD0_VAUDP15_MASK_SFT (0xf << 4) +#define RG_AUDPMU_RSD0_VA18_SFT 8 +#define RG_AUDPMU_RSD0_VA18_MASK 0xf +#define RG_AUDPMU_RSD0_VA18_MASK_SFT (0xf << 8) +#define RG_AUDPMU_RSD0_VA28_SFT 12 +#define RG_AUDPMU_RSD0_VA28_MASK 0xf +#define RG_AUDPMU_RSD0_VA28_MASK_SFT (0xf << 12) + +/* MT6358_ZCD_CON0 */ +#define RG_AUDZCDENABLE_SFT 0 +#define RG_AUDZCDENABLE_MASK 0x1 +#define RG_AUDZCDENABLE_MASK_SFT (0x1 << 0) +#define RG_AUDZCDGAINSTEPTIME_SFT 1 +#define RG_AUDZCDGAINSTEPTIME_MASK 0x7 +#define RG_AUDZCDGAINSTEPTIME_MASK_SFT (0x7 << 1) +#define RG_AUDZCDGAINSTEPSIZE_SFT 4 +#define RG_AUDZCDGAINSTEPSIZE_MASK 0x3 +#define RG_AUDZCDGAINSTEPSIZE_MASK_SFT (0x3 << 4) +#define RG_AUDZCDTIMEOUTMODESEL_SFT 6 +#define RG_AUDZCDTIMEOUTMODESEL_MASK 0x1 +#define RG_AUDZCDTIMEOUTMODESEL_MASK_SFT (0x1 << 6) + +/* MT6358_ZCD_CON1 */ +#define RG_AUDLOLGAIN_SFT 0 +#define RG_AUDLOLGAIN_MASK 0x1f +#define RG_AUDLOLGAIN_MASK_SFT (0x1f << 0) +#define RG_AUDLORGAIN_SFT 7 +#define RG_AUDLORGAIN_MASK 0x1f +#define RG_AUDLORGAIN_MASK_SFT (0x1f << 7) + +/* MT6358_ZCD_CON2 */ +#define RG_AUDHPLGAIN_SFT 0 +#define RG_AUDHPLGAIN_MASK 0x1f +#define RG_AUDHPLGAIN_MASK_SFT (0x1f << 0) +#define RG_AUDHPRGAIN_SFT 7 +#define RG_AUDHPRGAIN_MASK 0x1f +#define RG_AUDHPRGAIN_MASK_SFT (0x1f << 7) + +/* MT6358_ZCD_CON3 */ +#define RG_AUDHSGAIN_SFT 0 +#define RG_AUDHSGAIN_MASK 0x1f +#define RG_AUDHSGAIN_MASK_SFT (0x1f << 0) + +/* MT6358_ZCD_CON4 */ +#define RG_AUDIVLGAIN_SFT 0 +#define RG_AUDIVLGAIN_MASK 0x7 +#define RG_AUDIVLGAIN_MASK_SFT (0x7 << 0) +#define RG_AUDIVRGAIN_SFT 8 +#define RG_AUDIVRGAIN_MASK 0x7 +#define RG_AUDIVRGAIN_MASK_SFT (0x7 << 8) + +/* MT6358_ZCD_CON5 */ +#define RG_AUDINTGAIN1_SFT 0 +#define RG_AUDINTGAIN1_MASK 0x3f +#define RG_AUDINTGAIN1_MASK_SFT (0x3f << 0) +#define RG_AUDINTGAIN2_SFT 8 +#define RG_AUDINTGAIN2_MASK 0x3f +#define RG_AUDINTGAIN2_MASK_SFT (0x3f << 8) + +/* audio register */ +#define MT6358_DRV_CON3 0x3c +#define MT6358_GPIO_DIR0 0x88 + +#define MT6358_GPIO_MODE2 0xd8 /* mosi */ +#define MT6358_GPIO_MODE2_SET 0xda +#define MT6358_GPIO_MODE2_CLR 0xdc + +#define MT6358_GPIO_MODE3 0xde /* miso */ +#define MT6358_GPIO_MODE3_SET 0xe0 +#define MT6358_GPIO_MODE3_CLR 0xe2 + +#define MT6358_TOP_CKPDN_CON0 0x10c +#define MT6358_TOP_CKPDN_CON0_SET 0x10e +#define MT6358_TOP_CKPDN_CON0_CLR 0x110 + +#define MT6358_TOP_CKHWEN_CON0 0x12a +#define MT6358_TOP_CKHWEN_CON0_SET 0x12c +#define MT6358_TOP_CKHWEN_CON0_CLR 0x12e + +#define MT6358_OTP_CON0 0x38a +#define MT6358_OTP_CON8 0x39a +#define MT6358_OTP_CON11 0x3a0 +#define MT6358_OTP_CON12 0x3a2 +#define MT6358_OTP_CON13 0x3a4 + +#define MT6358_DCXO_CW13 0x7aa +#define MT6358_DCXO_CW14 0x7ac + +#define MT6358_AUXADC_CON10 0x11a0 + +/* audio register */ +#define MT6358_AUD_TOP_ID 0x2200 +#define MT6358_AUD_TOP_REV0 0x2202 +#define MT6358_AUD_TOP_DBI 0x2204 +#define MT6358_AUD_TOP_DXI 0x2206 +#define MT6358_AUD_TOP_CKPDN_TPM0 0x2208 +#define MT6358_AUD_TOP_CKPDN_TPM1 0x220a +#define MT6358_AUD_TOP_CKPDN_CON0 0x220c +#define MT6358_AUD_TOP_CKPDN_CON0_SET 0x220e +#define MT6358_AUD_TOP_CKPDN_CON0_CLR 0x2210 +#define MT6358_AUD_TOP_CKSEL_CON0 0x2212 +#define MT6358_AUD_TOP_CKSEL_CON0_SET 0x2214 +#define MT6358_AUD_TOP_CKSEL_CON0_CLR 0x2216 +#define MT6358_AUD_TOP_CKTST_CON0 0x2218 +#define MT6358_AUD_TOP_CLK_HWEN_CON0 0x221a +#define MT6358_AUD_TOP_CLK_HWEN_CON0_SET 0x221c +#define MT6358_AUD_TOP_CLK_HWEN_CON0_CLR 0x221e +#define MT6358_AUD_TOP_RST_CON0 0x2220 +#define MT6358_AUD_TOP_RST_CON0_SET 0x2222 +#define MT6358_AUD_TOP_RST_CON0_CLR 0x2224 +#define MT6358_AUD_TOP_RST_BANK_CON0 0x2226 +#define MT6358_AUD_TOP_INT_CON0 0x2228 +#define MT6358_AUD_TOP_INT_CON0_SET 0x222a +#define MT6358_AUD_TOP_INT_CON0_CLR 0x222c +#define MT6358_AUD_TOP_INT_MASK_CON0 0x222e +#define MT6358_AUD_TOP_INT_MASK_CON0_SET 0x2230 +#define MT6358_AUD_TOP_INT_MASK_CON0_CLR 0x2232 +#define MT6358_AUD_TOP_INT_STATUS0 0x2234 +#define MT6358_AUD_TOP_INT_RAW_STATUS0 0x2236 +#define MT6358_AUD_TOP_INT_MISC_CON0 0x2238 +#define MT6358_AUDNCP_CLKDIV_CON0 0x223a +#define MT6358_AUDNCP_CLKDIV_CON1 0x223c +#define MT6358_AUDNCP_CLKDIV_CON2 0x223e +#define MT6358_AUDNCP_CLKDIV_CON3 0x2240 +#define MT6358_AUDNCP_CLKDIV_CON4 0x2242 +#define MT6358_AUD_TOP_MON_CON0 0x2244 +#define MT6358_AUDIO_DIG_DSN_ID 0x2280 +#define MT6358_AUDIO_DIG_DSN_REV0 0x2282 +#define MT6358_AUDIO_DIG_DSN_DBI 0x2284 +#define MT6358_AUDIO_DIG_DSN_DXI 0x2286 +#define MT6358_AFE_UL_DL_CON0 0x2288 +#define MT6358_AFE_DL_SRC2_CON0_L 0x228a +#define MT6358_AFE_UL_SRC_CON0_H 0x228c +#define MT6358_AFE_UL_SRC_CON0_L 0x228e +#define MT6358_AFE_TOP_CON0 0x2290 +#define MT6358_AUDIO_TOP_CON0 0x2292 +#define MT6358_AFE_MON_DEBUG0 0x2294 +#define MT6358_AFUNC_AUD_CON0 0x2296 +#define MT6358_AFUNC_AUD_CON1 0x2298 +#define MT6358_AFUNC_AUD_CON2 0x229a +#define MT6358_AFUNC_AUD_CON3 0x229c +#define MT6358_AFUNC_AUD_CON4 0x229e +#define MT6358_AFUNC_AUD_CON5 0x22a0 +#define MT6358_AFUNC_AUD_CON6 0x22a2 +#define MT6358_AFUNC_AUD_MON0 0x22a4 +#define MT6358_AUDRC_TUNE_MON0 0x22a6 +#define MT6358_AFE_ADDA_MTKAIF_FIFO_CFG0 0x22a8 +#define MT6358_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 0x22aa +#define MT6358_AFE_ADDA_MTKAIF_MON0 0x22ac +#define MT6358_AFE_ADDA_MTKAIF_MON1 0x22ae +#define MT6358_AFE_ADDA_MTKAIF_MON2 0x22b0 +#define MT6358_AFE_ADDA_MTKAIF_MON3 0x22b2 +#define MT6358_AFE_ADDA_MTKAIF_CFG0 0x22b4 +#define MT6358_AFE_ADDA_MTKAIF_RX_CFG0 0x22b6 +#define MT6358_AFE_ADDA_MTKAIF_RX_CFG1 0x22b8 +#define MT6358_AFE_ADDA_MTKAIF_RX_CFG2 0x22ba +#define MT6358_AFE_ADDA_MTKAIF_RX_CFG3 0x22bc +#define MT6358_AFE_ADDA_MTKAIF_TX_CFG1 0x22be +#define MT6358_AFE_SGEN_CFG0 0x22c0 +#define MT6358_AFE_SGEN_CFG1 0x22c2 +#define MT6358_AFE_ADC_ASYNC_FIFO_CFG 0x22c4 +#define MT6358_AFE_DCCLK_CFG0 0x22c6 +#define MT6358_AFE_DCCLK_CFG1 0x22c8 +#define MT6358_AUDIO_DIG_CFG 0x22ca +#define MT6358_AFE_AUD_PAD_TOP 0x22cc +#define MT6358_AFE_AUD_PAD_TOP_MON 0x22ce +#define MT6358_AFE_AUD_PAD_TOP_MON1 0x22d0 +#define MT6358_AFE_DL_NLE_CFG 0x22d2 +#define MT6358_AFE_DL_NLE_MON 0x22d4 +#define MT6358_AFE_CG_EN_MON 0x22d6 +#define MT6358_AUDIO_DIG_2ND_DSN_ID 0x2300 +#define MT6358_AUDIO_DIG_2ND_DSN_REV0 0x2302 +#define MT6358_AUDIO_DIG_2ND_DSN_DBI 0x2304 +#define MT6358_AUDIO_DIG_2ND_DSN_DXI 0x2306 +#define MT6358_AFE_PMIC_NEWIF_CFG3 0x2308 +#define MT6358_AFE_VOW_TOP 0x230a +#define MT6358_AFE_VOW_CFG0 0x230c +#define MT6358_AFE_VOW_CFG1 0x230e +#define MT6358_AFE_VOW_CFG2 0x2310 +#define MT6358_AFE_VOW_CFG3 0x2312 +#define MT6358_AFE_VOW_CFG4 0x2314 +#define MT6358_AFE_VOW_CFG5 0x2316 +#define MT6358_AFE_VOW_CFG6 0x2318 +#define MT6358_AFE_VOW_MON0 0x231a +#define MT6358_AFE_VOW_MON1 0x231c +#define MT6358_AFE_VOW_MON2 0x231e +#define MT6358_AFE_VOW_MON3 0x2320 +#define MT6358_AFE_VOW_MON4 0x2322 +#define MT6358_AFE_VOW_MON5 0x2324 +#define MT6358_AFE_VOW_SN_INI_CFG 0x2326 +#define MT6358_AFE_VOW_TGEN_CFG0 0x2328 +#define MT6358_AFE_VOW_POSDIV_CFG0 0x232a +#define MT6358_AFE_VOW_HPF_CFG0 0x232c +#define MT6358_AFE_VOW_PERIODIC_CFG0 0x232e +#define MT6358_AFE_VOW_PERIODIC_CFG1 0x2330 +#define MT6358_AFE_VOW_PERIODIC_CFG2 0x2332 +#define MT6358_AFE_VOW_PERIODIC_CFG3 0x2334 +#define MT6358_AFE_VOW_PERIODIC_CFG4 0x2336 +#define MT6358_AFE_VOW_PERIODIC_CFG5 0x2338 +#define MT6358_AFE_VOW_PERIODIC_CFG6 0x233a +#define MT6358_AFE_VOW_PERIODIC_CFG7 0x233c +#define MT6358_AFE_VOW_PERIODIC_CFG8 0x233e +#define MT6358_AFE_VOW_PERIODIC_CFG9 0x2340 +#define MT6358_AFE_VOW_PERIODIC_CFG10 0x2342 +#define MT6358_AFE_VOW_PERIODIC_CFG11 0x2344 +#define MT6358_AFE_VOW_PERIODIC_CFG12 0x2346 +#define MT6358_AFE_VOW_PERIODIC_CFG13 0x2348 +#define MT6358_AFE_VOW_PERIODIC_CFG14 0x234a +#define MT6358_AFE_VOW_PERIODIC_CFG15 0x234c +#define MT6358_AFE_VOW_PERIODIC_CFG16 0x234e +#define MT6358_AFE_VOW_PERIODIC_CFG17 0x2350 +#define MT6358_AFE_VOW_PERIODIC_CFG18 0x2352 +#define MT6358_AFE_VOW_PERIODIC_CFG19 0x2354 +#define MT6358_AFE_VOW_PERIODIC_CFG20 0x2356 +#define MT6358_AFE_VOW_PERIODIC_CFG21 0x2358 +#define MT6358_AFE_VOW_PERIODIC_CFG22 0x235a +#define MT6358_AFE_VOW_PERIODIC_CFG23 0x235c +#define MT6358_AFE_VOW_PERIODIC_MON0 0x235e +#define MT6358_AFE_VOW_PERIODIC_MON1 0x2360 +#define MT6358_AUDENC_DSN_ID 0x2380 +#define MT6358_AUDENC_DSN_REV0 0x2382 +#define MT6358_AUDENC_DSN_DBI 0x2384 +#define MT6358_AUDENC_DSN_FPI 0x2386 +#define MT6358_AUDENC_ANA_CON0 0x2388 +#define MT6358_AUDENC_ANA_CON1 0x238a +#define MT6358_AUDENC_ANA_CON2 0x238c +#define MT6358_AUDENC_ANA_CON3 0x238e +#define MT6358_AUDENC_ANA_CON4 0x2390 +#define MT6358_AUDENC_ANA_CON5 0x2392 +#define MT6358_AUDENC_ANA_CON6 0x2394 +#define MT6358_AUDENC_ANA_CON7 0x2396 +#define MT6358_AUDENC_ANA_CON8 0x2398 +#define MT6358_AUDENC_ANA_CON9 0x239a +#define MT6358_AUDENC_ANA_CON10 0x239c +#define MT6358_AUDENC_ANA_CON11 0x239e +#define MT6358_AUDENC_ANA_CON12 0x23a0 +#define MT6358_AUDDEC_DSN_ID 0x2400 +#define MT6358_AUDDEC_DSN_REV0 0x2402 +#define MT6358_AUDDEC_DSN_DBI 0x2404 +#define MT6358_AUDDEC_DSN_FPI 0x2406 +#define MT6358_AUDDEC_ANA_CON0 0x2408 +#define MT6358_AUDDEC_ANA_CON1 0x240a +#define MT6358_AUDDEC_ANA_CON2 0x240c +#define MT6358_AUDDEC_ANA_CON3 0x240e +#define MT6358_AUDDEC_ANA_CON4 0x2410 +#define MT6358_AUDDEC_ANA_CON5 0x2412 +#define MT6358_AUDDEC_ANA_CON6 0x2414 +#define MT6358_AUDDEC_ANA_CON7 0x2416 +#define MT6358_AUDDEC_ANA_CON8 0x2418 +#define MT6358_AUDDEC_ANA_CON9 0x241a +#define MT6358_AUDDEC_ANA_CON10 0x241c +#define MT6358_AUDDEC_ANA_CON11 0x241e +#define MT6358_AUDDEC_ANA_CON12 0x2420 +#define MT6358_AUDDEC_ANA_CON13 0x2422 +#define MT6358_AUDDEC_ANA_CON14 0x2424 +#define MT6358_AUDDEC_ANA_CON15 0x2426 +#define MT6358_AUDDEC_ELR_NUM 0x2428 +#define MT6358_AUDDEC_ELR_0 0x242a +#define MT6358_AUDZCD_DSN_ID 0x2480 +#define MT6358_AUDZCD_DSN_REV0 0x2482 +#define MT6358_AUDZCD_DSN_DBI 0x2484 +#define MT6358_AUDZCD_DSN_FPI 0x2486 +#define MT6358_ZCD_CON0 0x2488 +#define MT6358_ZCD_CON1 0x248a +#define MT6358_ZCD_CON2 0x248c +#define MT6358_ZCD_CON3 0x248e +#define MT6358_ZCD_CON4 0x2490 +#define MT6358_ZCD_CON5 0x2492 +#define MT6358_ACCDET_CON13 0x2522 + +#define MT6358_MAX_REGISTER MT6358_ZCD_CON5 + +enum { + MT6358_MTKAIF_PROTOCOL_1 = 0, + MT6358_MTKAIF_PROTOCOL_2, + MT6358_MTKAIF_PROTOCOL_2_CLK_P2, +}; + +/* set only during init */ +int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt, + int mtkaif_protocol); +int mt6358_mtkaif_calibration_enable(struct snd_soc_component *cmpnt); +int mt6358_mtkaif_calibration_disable(struct snd_soc_component *cmpnt); +int mt6358_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt, + int phase_1, int phase_2); +#endif /* __MT6358_H__ */ diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c new file mode 100644 index 0000000000..7f62485494 --- /dev/null +++ b/sound/soc/codecs/mt6359-accdet.c @@ -0,0 +1,1066 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt6359-accdet.c -- ALSA SoC mt6359 accdet driver +// +// Copyright (C) 2021 MediaTek Inc. +// Author: Argus Lin +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt6359-accdet.h" +#include "mt6359.h" + +/* global variable definitions */ +#define REGISTER_VAL(x) ((x) - 1) + +/* mt6359 accdet capability */ +#define ACCDET_PMIC_EINT_IRQ BIT(0) +#define ACCDET_AP_GPIO_EINT BIT(1) + +#define ACCDET_PMIC_EINT0 BIT(2) +#define ACCDET_PMIC_EINT1 BIT(3) +#define ACCDET_PMIC_BI_EINT BIT(4) + +#define ACCDET_PMIC_GPIO_TRIG_EINT BIT(5) +#define ACCDET_PMIC_INVERTER_TRIG_EINT BIT(6) +#define ACCDET_PMIC_RSV_EINT BIT(7) + +#define ACCDET_THREE_KEY BIT(8) +#define ACCDET_FOUR_KEY BIT(9) +#define ACCDET_TRI_KEY_CDD BIT(10) +#define ACCDET_RSV_KEY BIT(11) + +#define ACCDET_ANALOG_FASTDISCHARGE BIT(12) +#define ACCDET_DIGITAL_FASTDISCHARGE BIT(13) +#define ACCDET_AD_FASTDISCHRAGE BIT(14) + +static struct platform_driver mt6359_accdet_driver; +static const struct snd_soc_component_driver mt6359_accdet_soc_driver; + +/* local function declaration */ +static void accdet_set_debounce(struct mt6359_accdet *priv, int state, + unsigned int debounce); +static unsigned int adjust_eint_analog_setting(struct mt6359_accdet *priv); +static void config_digital_init_by_mode(struct mt6359_accdet *priv); +static void config_eint_init_by_mode(struct mt6359_accdet *priv); +static inline void mt6359_accdet_init(struct mt6359_accdet *priv); +static unsigned int mt6359_accdet_jd_setting(struct mt6359_accdet *priv); +static void mt6359_accdet_recover_jd_setting(struct mt6359_accdet *priv); +static void mt6359_accdet_jack_report(struct mt6359_accdet *priv); +static void recover_eint_analog_setting(struct mt6359_accdet *priv); +static void recover_eint_digital_setting(struct mt6359_accdet *priv); +static void recover_eint_setting(struct mt6359_accdet *priv); + +static unsigned int adjust_eint_analog_setting(struct mt6359_accdet *priv) +{ + if (priv->data->eint_detect_mode == 0x3 || + priv->data->eint_detect_mode == 0x4) { + /* ESD switches off */ + regmap_update_bits(priv->regmap, + RG_ACCDETSPARE_ADDR, 1 << 8, 0); + } + if (priv->data->eint_detect_mode == 0x4) { + if (priv->caps & ACCDET_PMIC_EINT0) { + /* enable RG_EINT0CONFIGACCDET */ + regmap_update_bits(priv->regmap, + RG_EINT0CONFIGACCDET_ADDR, + RG_EINT0CONFIGACCDET_MASK_SFT, + BIT(RG_EINT0CONFIGACCDET_SFT)); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + /* enable RG_EINT1CONFIGACCDET */ + regmap_update_bits(priv->regmap, + RG_EINT1CONFIGACCDET_ADDR, + RG_EINT1CONFIGACCDET_MASK_SFT, + BIT(RG_EINT1CONFIGACCDET_SFT)); + } + if (priv->data->eint_use_ext_res == 0x3 || + priv->data->eint_use_ext_res == 0x4) { + /*select 500k, use internal resistor */ + regmap_update_bits(priv->regmap, + RG_EINT0HIRENB_ADDR, + RG_EINT0HIRENB_MASK_SFT, + BIT(RG_EINT0HIRENB_SFT)); + } + } + return 0; +} + +static unsigned int adjust_eint_digital_setting(struct mt6359_accdet *priv) +{ + if (priv->caps & ACCDET_PMIC_EINT0) { + /* disable inverter */ + regmap_update_bits(priv->regmap, + ACCDET_EINT0_INVERTER_SW_EN_ADDR, + ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT, 0); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + /* disable inverter */ + regmap_update_bits(priv->regmap, + ACCDET_EINT1_INVERTER_SW_EN_ADDR, + ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT, 0); + } + + if (priv->data->eint_detect_mode == 0x4) { + if (priv->caps & ACCDET_PMIC_EINT0) { + /* set DA stable signal */ + regmap_update_bits(priv->regmap, + ACCDET_DA_STABLE_ADDR, + ACCDET_EINT0_CEN_STABLE_MASK_SFT, 0); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + /* set DA stable signal */ + regmap_update_bits(priv->regmap, + ACCDET_DA_STABLE_ADDR, + ACCDET_EINT1_CEN_STABLE_MASK_SFT, 0); + } + } + return 0; +} + +static unsigned int mt6359_accdet_jd_setting(struct mt6359_accdet *priv) +{ + if (priv->jd_sts == M_PLUG_IN) { + /* adjust digital setting */ + adjust_eint_digital_setting(priv); + /* adjust analog setting */ + adjust_eint_analog_setting(priv); + } else if (priv->jd_sts == M_PLUG_OUT) { + /* set debounce to 1ms */ + accdet_set_debounce(priv, eint_state000, + priv->data->pwm_deb->eint_debounce0); + } else { + dev_dbg(priv->dev, "should not be here %s()\n", __func__); + } + + return 0; +} + +static void recover_eint_analog_setting(struct mt6359_accdet *priv) +{ + if (priv->data->eint_detect_mode == 0x3 || + priv->data->eint_detect_mode == 0x4) { + /* ESD switches on */ + regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR, + 1 << 8, 1 << 8); + } + if (priv->data->eint_detect_mode == 0x4) { + if (priv->caps & ACCDET_PMIC_EINT0) { + /* disable RG_EINT0CONFIGACCDET */ + regmap_update_bits(priv->regmap, + RG_EINT0CONFIGACCDET_ADDR, + RG_EINT0CONFIGACCDET_MASK_SFT, 0); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + /* disable RG_EINT1CONFIGACCDET */ + regmap_update_bits(priv->regmap, + RG_EINT1CONFIGACCDET_ADDR, + RG_EINT1CONFIGACCDET_MASK_SFT, 0); + } + regmap_update_bits(priv->regmap, RG_EINT0HIRENB_ADDR, + RG_EINT0HIRENB_MASK_SFT, 0); + } +} + +static void recover_eint_digital_setting(struct mt6359_accdet *priv) +{ + if (priv->caps & ACCDET_PMIC_EINT0) { + regmap_update_bits(priv->regmap, + ACCDET_EINT0_M_SW_EN_ADDR, + ACCDET_EINT0_M_SW_EN_MASK_SFT, 0); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + regmap_update_bits(priv->regmap, + ACCDET_EINT1_M_SW_EN_ADDR, + ACCDET_EINT1_M_SW_EN_MASK_SFT, 0); + } + if (priv->data->eint_detect_mode == 0x4) { + /* enable eint0cen */ + if (priv->caps & ACCDET_PMIC_EINT0) { + /* enable eint0cen */ + regmap_update_bits(priv->regmap, + ACCDET_DA_STABLE_ADDR, + ACCDET_EINT0_CEN_STABLE_MASK_SFT, + BIT(ACCDET_EINT0_CEN_STABLE_SFT)); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + /* enable eint1cen */ + regmap_update_bits(priv->regmap, + ACCDET_DA_STABLE_ADDR, + ACCDET_EINT1_CEN_STABLE_MASK_SFT, + BIT(ACCDET_EINT1_CEN_STABLE_SFT)); + } + } + + if (priv->data->eint_detect_mode != 0x1) { + if (priv->caps & ACCDET_PMIC_EINT0) { + /* enable inverter */ + regmap_update_bits(priv->regmap, + ACCDET_EINT0_INVERTER_SW_EN_ADDR, + ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT, + BIT(ACCDET_EINT0_INVERTER_SW_EN_SFT)); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + /* enable inverter */ + regmap_update_bits(priv->regmap, + ACCDET_EINT1_INVERTER_SW_EN_ADDR, + ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT, + BIT(ACCDET_EINT1_INVERTER_SW_EN_SFT)); + } + } +} + +static void recover_eint_setting(struct mt6359_accdet *priv) +{ + if (priv->jd_sts == M_PLUG_OUT) { + recover_eint_analog_setting(priv); + recover_eint_digital_setting(priv); + } +} + +static void mt6359_accdet_recover_jd_setting(struct mt6359_accdet *priv) +{ + int ret; + unsigned int value = 0; + + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, + ACCDET_IRQ_CLR_MASK_SFT, BIT(ACCDET_IRQ_CLR_SFT)); + usleep_range(200, 300); + ret = regmap_read_poll_timeout(priv->regmap, + ACCDET_IRQ_ADDR, + value, + (value & ACCDET_IRQ_MASK_SFT) == 0, + 0, + 1000); + if (ret) + dev_warn(priv->dev, "%s(), ret %d\n", __func__, ret); + /* clear accdet int, modify for fix interrupt trigger twice error */ + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, + ACCDET_IRQ_CLR_MASK_SFT, 0); + regmap_update_bits(priv->regmap, RG_INT_STATUS_ACCDET_ADDR, + RG_INT_STATUS_ACCDET_MASK_SFT, + BIT(RG_INT_STATUS_ACCDET_SFT)); + + /* recover accdet debounce0,3 */ + accdet_set_debounce(priv, accdet_state000, + priv->data->pwm_deb->debounce0); + accdet_set_debounce(priv, accdet_state001, + priv->data->pwm_deb->debounce1); + accdet_set_debounce(priv, accdet_state011, + priv->data->pwm_deb->debounce3); + + priv->jack_type = 0; + priv->btn_type = 0; + priv->accdet_status = 0x3; + mt6359_accdet_jack_report(priv); +} + +static void accdet_set_debounce(struct mt6359_accdet *priv, int state, + unsigned int debounce) +{ + switch (state) { + case accdet_state000: + regmap_write(priv->regmap, ACCDET_DEBOUNCE0_ADDR, debounce); + break; + case accdet_state001: + regmap_write(priv->regmap, ACCDET_DEBOUNCE1_ADDR, debounce); + break; + case accdet_state010: + regmap_write(priv->regmap, ACCDET_DEBOUNCE2_ADDR, debounce); + break; + case accdet_state011: + regmap_write(priv->regmap, ACCDET_DEBOUNCE3_ADDR, debounce); + break; + case accdet_auxadc: + regmap_write(priv->regmap, + ACCDET_CONNECT_AUXADC_TIME_DIG_ADDR, debounce); + break; + case eint_state000: + regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE0_ADDR, + 0xF << ACCDET_EINT_DEBOUNCE0_SFT, + debounce << ACCDET_EINT_DEBOUNCE0_SFT); + break; + case eint_state001: + regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE1_ADDR, + 0xF << ACCDET_EINT_DEBOUNCE1_SFT, + debounce << ACCDET_EINT_DEBOUNCE1_SFT); + break; + case eint_state010: + regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE2_ADDR, + 0xF << ACCDET_EINT_DEBOUNCE2_SFT, + debounce << ACCDET_EINT_DEBOUNCE2_SFT); + break; + case eint_state011: + regmap_update_bits(priv->regmap, ACCDET_EINT_DEBOUNCE3_ADDR, + 0xF << ACCDET_EINT_DEBOUNCE3_SFT, + debounce << ACCDET_EINT_DEBOUNCE3_SFT); + break; + case eint_inverter_state000: + regmap_write(priv->regmap, ACCDET_EINT_INVERTER_DEBOUNCE_ADDR, + debounce); + break; + default: + dev_warn(priv->dev, "Error: %s error state (%d)\n", __func__, + state); + break; + } +} + +static void mt6359_accdet_jack_report(struct mt6359_accdet *priv) +{ + int report = 0; + + if (!priv->jack) + return; + + report = priv->jack_type | priv->btn_type; + snd_soc_jack_report(priv->jack, report, MT6359_ACCDET_JACK_MASK); +} + +static unsigned int check_button(struct mt6359_accdet *priv, unsigned int v) +{ + if (priv->caps & ACCDET_FOUR_KEY) { + if (v < priv->data->four_key.down && + v >= priv->data->four_key.up) + priv->btn_type = SND_JACK_BTN_1; + if (v < priv->data->four_key.up && + v >= priv->data->four_key.voice) + priv->btn_type = SND_JACK_BTN_2; + if (v < priv->data->four_key.voice && + v >= priv->data->four_key.mid) + priv->btn_type = SND_JACK_BTN_3; + if (v < priv->data->four_key.mid) + priv->btn_type = SND_JACK_BTN_0; + } else { + if (v < priv->data->three_key.down && + v >= priv->data->three_key.up) + priv->btn_type = SND_JACK_BTN_1; + if (v < priv->data->three_key.up && + v >= priv->data->three_key.mid) + priv->btn_type = SND_JACK_BTN_2; + if (v < priv->data->three_key.mid) + priv->btn_type = SND_JACK_BTN_0; + } + return 0; +} + +static void is_key_pressed(struct mt6359_accdet *priv, bool pressed) +{ + priv->btn_type = priv->jack_type & ~MT6359_ACCDET_BTN_MASK; + + if (pressed) + check_button(priv, priv->cali_voltage); +} + +static inline void check_jack_btn_type(struct mt6359_accdet *priv) +{ + unsigned int val = 0; + + regmap_read(priv->regmap, ACCDET_MEM_IN_ADDR, &val); + + priv->accdet_status = + (val >> ACCDET_STATE_MEM_IN_OFFSET) & ACCDET_STATE_AB_MASK; + + switch (priv->accdet_status) { + case 0: + if (priv->jack_type == SND_JACK_HEADSET) + is_key_pressed(priv, true); + else + priv->jack_type = SND_JACK_HEADPHONE; + break; + case 1: + if (priv->jack_type == SND_JACK_HEADSET) { + is_key_pressed(priv, false); + } else { + priv->jack_type = SND_JACK_HEADSET; + accdet_set_debounce(priv, eint_state011, 0x1); + } + break; + case 3: + default: + priv->jack_type = 0; + break; + } +} + +static void mt6359_accdet_work(struct work_struct *work) +{ + struct mt6359_accdet *priv = + container_of(work, struct mt6359_accdet, accdet_work); + + mutex_lock(&priv->res_lock); + priv->pre_accdet_status = priv->accdet_status; + check_jack_btn_type(priv); + + if (priv->jack_plugged && + priv->pre_accdet_status != priv->accdet_status) + mt6359_accdet_jack_report(priv); + mutex_unlock(&priv->res_lock); +} + +static void mt6359_accdet_jd_work(struct work_struct *work) +{ + int ret; + unsigned int value = 0; + + struct mt6359_accdet *priv = + container_of(work, struct mt6359_accdet, jd_work); + + mutex_lock(&priv->res_lock); + if (priv->jd_sts == M_PLUG_IN) { + priv->jack_plugged = true; + + /* set and clear initial bit every eint interrupt */ + regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR, + ACCDET_SEQ_INIT_MASK_SFT, + BIT(ACCDET_SEQ_INIT_SFT)); + regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR, + ACCDET_SEQ_INIT_MASK_SFT, 0); + ret = regmap_read_poll_timeout(priv->regmap, + ACCDET_SEQ_INIT_ADDR, + value, + (value & ACCDET_SEQ_INIT_MASK_SFT) == 0, + 0, + 1000); + if (ret) + dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); + + /* enable ACCDET unit */ + regmap_update_bits(priv->regmap, ACCDET_SW_EN_ADDR, + ACCDET_SW_EN_MASK_SFT, BIT(ACCDET_SW_EN_SFT)); + } else if (priv->jd_sts == M_PLUG_OUT) { + priv->jack_plugged = false; + + accdet_set_debounce(priv, accdet_state011, + priv->data->pwm_deb->debounce3); + regmap_update_bits(priv->regmap, ACCDET_SW_EN_ADDR, + ACCDET_SW_EN_MASK_SFT, 0); + mt6359_accdet_recover_jd_setting(priv); + } + + if (priv->caps & ACCDET_PMIC_EINT_IRQ) + recover_eint_setting(priv); + mutex_unlock(&priv->res_lock); +} + +static irqreturn_t mt6359_accdet_irq(int irq, void *data) +{ + struct mt6359_accdet *priv = data; + unsigned int irq_val = 0, val = 0, value = 0; + int ret; + + mutex_lock(&priv->res_lock); + regmap_read(priv->regmap, ACCDET_IRQ_ADDR, &irq_val); + + if (irq_val & ACCDET_IRQ_MASK_SFT) { + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, + ACCDET_IRQ_CLR_MASK_SFT, + BIT(ACCDET_IRQ_CLR_SFT)); + ret = regmap_read_poll_timeout(priv->regmap, + ACCDET_IRQ_ADDR, + value, + (value & ACCDET_IRQ_MASK_SFT) == 0, + 0, + 1000); + if (ret) { + dev_err(priv->dev, "%s(), ret %d\n", __func__, ret); + mutex_unlock(&priv->res_lock); + return IRQ_NONE; + } + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, + ACCDET_IRQ_CLR_MASK_SFT, 0); + regmap_update_bits(priv->regmap, RG_INT_STATUS_ACCDET_ADDR, + RG_INT_STATUS_ACCDET_MASK_SFT, + BIT(RG_INT_STATUS_ACCDET_SFT)); + + queue_work(priv->accdet_workqueue, &priv->accdet_work); + } else { + if (irq_val & ACCDET_EINT0_IRQ_MASK_SFT) { + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, + ACCDET_EINT0_IRQ_CLR_MASK_SFT, + BIT(ACCDET_EINT0_IRQ_CLR_SFT)); + ret = regmap_read_poll_timeout(priv->regmap, + ACCDET_IRQ_ADDR, + value, + (value & ACCDET_EINT0_IRQ_MASK_SFT) == 0, + 0, + 1000); + if (ret) { + dev_err(priv->dev, "%s(), ret %d\n", __func__, + ret); + mutex_unlock(&priv->res_lock); + return IRQ_NONE; + } + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, + ACCDET_EINT0_IRQ_CLR_MASK_SFT, 0); + regmap_update_bits(priv->regmap, + RG_INT_STATUS_ACCDET_ADDR, + RG_INT_STATUS_ACCDET_EINT0_MASK_SFT, + BIT(RG_INT_STATUS_ACCDET_EINT0_SFT)); + } + if (irq_val & ACCDET_EINT1_IRQ_MASK_SFT) { + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, + ACCDET_EINT1_IRQ_CLR_MASK_SFT, + BIT(ACCDET_EINT1_IRQ_CLR_SFT)); + ret = regmap_read_poll_timeout(priv->regmap, + ACCDET_IRQ_ADDR, + value, + (value & ACCDET_EINT1_IRQ_MASK_SFT) == 0, + 0, + 1000); + if (ret) { + dev_err(priv->dev, "%s(), ret %d\n", __func__, + ret); + mutex_unlock(&priv->res_lock); + return IRQ_NONE; + } + regmap_update_bits(priv->regmap, ACCDET_IRQ_ADDR, + ACCDET_EINT1_IRQ_CLR_MASK_SFT, 0); + regmap_update_bits(priv->regmap, + RG_INT_STATUS_ACCDET_ADDR, + RG_INT_STATUS_ACCDET_EINT1_MASK_SFT, + BIT(RG_INT_STATUS_ACCDET_EINT1_SFT)); + } + /* get jack detection status */ + regmap_read(priv->regmap, ACCDET_EINT0_MEM_IN_ADDR, &val); + priv->jd_sts = ((val >> ACCDET_EINT0_MEM_IN_SFT) & + ACCDET_EINT0_MEM_IN_MASK); + /* adjust eint digital/analog setting */ + mt6359_accdet_jd_setting(priv); + + queue_work(priv->jd_workqueue, &priv->jd_work); + } + mutex_unlock(&priv->res_lock); + + return IRQ_HANDLED; +} + +static int mt6359_accdet_parse_dt(struct mt6359_accdet *priv) +{ + int ret; + struct device *dev = priv->dev; + struct device_node *node = NULL; + int pwm_deb[15] = {0}; + unsigned int tmp = 0; + + node = of_get_child_by_name(dev->parent->of_node, "accdet"); + if (!node) + return -EINVAL; + + ret = of_property_read_u32(node, "mediatek,mic-vol", + &priv->data->mic_vol); + if (ret) + priv->data->mic_vol = 8; + + ret = of_property_read_u32(node, "mediatek,plugout-debounce", + &priv->data->plugout_deb); + if (ret) + priv->data->plugout_deb = 1; + + ret = of_property_read_u32(node, "mediatek,mic-mode", + &priv->data->mic_mode); + if (ret) + priv->data->mic_mode = 2; + + ret = of_property_read_u32_array(node, "mediatek,pwm-deb-setting", + pwm_deb, ARRAY_SIZE(pwm_deb)); + /* debounce8(auxadc debounce) is default, needn't get from dts */ + if (!ret) + memcpy(priv->data->pwm_deb, pwm_deb, sizeof(pwm_deb)); + + ret = of_property_read_u32(node, "mediatek,eint-level-pol", + &priv->data->eint_pol); + if (ret) + priv->data->eint_pol = 8; + + ret = of_property_read_u32(node, "mediatek,eint-use-ap", &tmp); + if (ret) + tmp = 0; + if (tmp == 0) + priv->caps |= ACCDET_PMIC_EINT_IRQ; + else if (tmp == 1) + priv->caps |= ACCDET_AP_GPIO_EINT; + + ret = of_property_read_u32(node, "mediatek,eint-detect-mode", + &priv->data->eint_detect_mode); + if (ret) { + /* eint detection mode equals to EINT HW Mode */ + priv->data->eint_detect_mode = 0x4; + } + + ret = of_property_read_u32(node, "mediatek,eint-num", &tmp); + if (ret) + tmp = 0; + if (tmp == 0) + priv->caps |= ACCDET_PMIC_EINT0; + else if (tmp == 1) + priv->caps |= ACCDET_PMIC_EINT1; + else if (tmp == 2) + priv->caps |= ACCDET_PMIC_BI_EINT; + + ret = of_property_read_u32(node, "mediatek,eint-trig-mode", + &tmp); + if (ret) + tmp = 0; + if (tmp == 0) + priv->caps |= ACCDET_PMIC_GPIO_TRIG_EINT; + else if (tmp == 1) + priv->caps |= ACCDET_PMIC_INVERTER_TRIG_EINT; + + ret = of_property_read_u32(node, "mediatek,eint-use-ext-res", + &priv->data->eint_use_ext_res); + if (ret) { + /* eint use internal resister */ + priv->data->eint_use_ext_res = 0x0; + } + + ret = of_property_read_u32(node, "mediatek,eint-comp-vth", + &priv->data->eint_comp_vth); + if (ret) + priv->data->eint_comp_vth = 0x0; + + ret = of_property_read_u32(node, "mediatek,key-mode", &tmp); + if (ret) + tmp = 0; + if (tmp == 0) { + int three_key[4]; + + priv->caps |= ACCDET_THREE_KEY; + ret = of_property_read_u32_array(node, + "mediatek,three-key-thr", + three_key, + ARRAY_SIZE(three_key)); + if (!ret) + memcpy(&priv->data->three_key, three_key + 1, + sizeof(struct three_key_threshold)); + } else if (tmp == 1) { + int four_key[5]; + + priv->caps |= ACCDET_FOUR_KEY; + ret = of_property_read_u32_array(node, + "mediatek,four-key-thr", + four_key, + ARRAY_SIZE(four_key)); + if (!ret) { + memcpy(&priv->data->four_key, four_key + 1, + sizeof(struct four_key_threshold)); + } else { + dev_warn(priv->dev, + "accdet no 4-key-thrsh dts, use efuse\n"); + } + } else if (tmp == 2) { + int three_key[4]; + + priv->caps |= ACCDET_TRI_KEY_CDD; + ret = of_property_read_u32_array(node, + "mediatek,tri-key-cdd-thr", + three_key, + ARRAY_SIZE(three_key)); + if (!ret) + memcpy(&priv->data->three_key, three_key + 1, + sizeof(struct three_key_threshold)); + } + + of_node_put(node); + dev_warn(priv->dev, "accdet caps=%x\n", priv->caps); + + return 0; +} + +static void config_digital_init_by_mode(struct mt6359_accdet *priv) +{ + /* enable eint cmpmem pwm */ + regmap_write(priv->regmap, ACCDET_EINT_CMPMEN_PWM_THRESH_ADDR, + (priv->data->pwm_deb->eint_pwm_width << 4 | + priv->data->pwm_deb->eint_pwm_thresh)); + /* DA signal stable */ + if (priv->caps & ACCDET_PMIC_EINT0) { + regmap_write(priv->regmap, ACCDET_DA_STABLE_ADDR, + ACCDET_EINT0_STABLE_VAL); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + regmap_write(priv->regmap, ACCDET_DA_STABLE_ADDR, + ACCDET_EINT1_STABLE_VAL); + } + /* after receive n+1 number, interrupt issued. */ + regmap_update_bits(priv->regmap, ACCDET_EINT_M_PLUG_IN_NUM_ADDR, + ACCDET_EINT_M_PLUG_IN_NUM_MASK_SFT, + BIT(ACCDET_EINT_M_PLUG_IN_NUM_SFT)); + /* setting HW mode, enable digital fast discharge + * if use EINT0 & EINT1 detection, please modify + * ACCDET_HWMODE_EN_ADDR[2:1] + */ + regmap_write(priv->regmap, ACCDET_HWMODE_EN_ADDR, 0x100); + + regmap_update_bits(priv->regmap, ACCDET_EINT_M_DETECT_EN_ADDR, + ACCDET_EINT_M_DETECT_EN_MASK_SFT, 0); + + /* enable PWM */ + regmap_write(priv->regmap, ACCDET_CMP_PWM_EN_ADDR, 0x67); + /* enable inverter detection */ + if (priv->data->eint_detect_mode == 0x1) { + /* disable inverter detection */ + if (priv->caps & ACCDET_PMIC_EINT0) { + regmap_update_bits(priv->regmap, + ACCDET_EINT0_INVERTER_SW_EN_ADDR, + ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT, + 0); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + regmap_update_bits(priv->regmap, + ACCDET_EINT1_INVERTER_SW_EN_ADDR, + ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT, + 0); + } + } else { + if (priv->caps & ACCDET_PMIC_EINT0) { + regmap_update_bits(priv->regmap, + ACCDET_EINT0_INVERTER_SW_EN_ADDR, + ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT, + BIT(ACCDET_EINT0_INVERTER_SW_EN_SFT)); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + regmap_update_bits(priv->regmap, + ACCDET_EINT1_INVERTER_SW_EN_ADDR, + ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT, + BIT(ACCDET_EINT1_INVERTER_SW_EN_SFT)); + } + } +} + +static void config_eint_init_by_mode(struct mt6359_accdet *priv) +{ + unsigned int val = 0; + + if (priv->caps & ACCDET_PMIC_EINT0) { + regmap_update_bits(priv->regmap, RG_EINT0EN_ADDR, + RG_EINT0EN_MASK_SFT, BIT(RG_EINT0EN_SFT)); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + regmap_update_bits(priv->regmap, RG_EINT1EN_ADDR, + RG_EINT1EN_MASK_SFT, BIT(RG_EINT1EN_SFT)); + } + /* ESD switches on */ + regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR, + 1 << 8, 1 << 8); + /* before playback, set NCP pull low before nagative voltage */ + regmap_update_bits(priv->regmap, RG_NCP_PDDIS_EN_ADDR, + RG_NCP_PDDIS_EN_MASK_SFT, BIT(RG_NCP_PDDIS_EN_SFT)); + + if (priv->data->eint_detect_mode == 0x1 || + priv->data->eint_detect_mode == 0x2 || + priv->data->eint_detect_mode == 0x3) { + if (priv->data->eint_use_ext_res == 0x1) { + if (priv->caps & ACCDET_PMIC_EINT0) { + regmap_update_bits(priv->regmap, + RG_EINT0CONFIGACCDET_ADDR, + RG_EINT0CONFIGACCDET_MASK_SFT, + 0); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + regmap_update_bits(priv->regmap, + RG_EINT1CONFIGACCDET_ADDR, + RG_EINT1CONFIGACCDET_MASK_SFT, + 0); + } + } else { + if (priv->caps & ACCDET_PMIC_EINT0) { + regmap_update_bits(priv->regmap, + RG_EINT0CONFIGACCDET_ADDR, + RG_EINT0CONFIGACCDET_MASK_SFT, + BIT(RG_EINT0CONFIGACCDET_SFT)); + } else if (priv->caps & ACCDET_PMIC_EINT1) { + regmap_update_bits(priv->regmap, + RG_EINT1CONFIGACCDET_ADDR, + RG_EINT1CONFIGACCDET_MASK_SFT, + BIT(RG_EINT1CONFIGACCDET_SFT)); + } + } + } + + if (priv->data->eint_detect_mode != 0x1) { + /* current detect set 0.25uA */ + regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR, + 0x3 << RG_ACCDETSPARE_SFT, + 0x3 << RG_ACCDETSPARE_SFT); + } + regmap_write(priv->regmap, RG_EINTCOMPVTH_ADDR, + val | priv->data->eint_comp_vth << RG_EINTCOMPVTH_SFT); +} + +static void mt6359_accdet_init(struct mt6359_accdet *priv) +{ + unsigned int reg = 0; + + regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR, + ACCDET_SEQ_INIT_MASK_SFT, BIT(ACCDET_SEQ_INIT_SFT)); + mdelay(2); + regmap_update_bits(priv->regmap, ACCDET_SEQ_INIT_ADDR, + ACCDET_SEQ_INIT_MASK_SFT, 0); + mdelay(1); + /* init the debounce time (debounce/32768)sec */ + accdet_set_debounce(priv, accdet_state000, + priv->data->pwm_deb->debounce0); + accdet_set_debounce(priv, accdet_state001, + priv->data->pwm_deb->debounce1); + accdet_set_debounce(priv, accdet_state011, + priv->data->pwm_deb->debounce3); + accdet_set_debounce(priv, accdet_auxadc, + priv->data->pwm_deb->debounce4); + + accdet_set_debounce(priv, eint_state000, + priv->data->pwm_deb->eint_debounce0); + accdet_set_debounce(priv, eint_state001, + priv->data->pwm_deb->eint_debounce1); + accdet_set_debounce(priv, eint_state011, + priv->data->pwm_deb->eint_debounce3); + accdet_set_debounce(priv, eint_inverter_state000, + priv->data->pwm_deb->eint_inverter_debounce); + + regmap_update_bits(priv->regmap, RG_ACCDET_RST_ADDR, + RG_ACCDET_RST_MASK_SFT, BIT(RG_ACCDET_RST_SFT)); + regmap_update_bits(priv->regmap, RG_ACCDET_RST_ADDR, + RG_ACCDET_RST_MASK_SFT, 0); + + /* clear high micbias1 voltage setting */ + regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR, + 0x3 << RG_AUDMICBIAS1HVEN_SFT, 0); + regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR, + 0x7 << RG_AUDMICBIAS1VREF_SFT, 0); + + /* init pwm frequency, duty & rise/falling delay */ + regmap_write(priv->regmap, ACCDET_PWM_WIDTH_ADDR, + REGISTER_VAL(priv->data->pwm_deb->pwm_width)); + regmap_write(priv->regmap, ACCDET_PWM_THRESH_ADDR, + REGISTER_VAL(priv->data->pwm_deb->pwm_thresh)); + regmap_write(priv->regmap, ACCDET_RISE_DELAY_ADDR, + (priv->data->pwm_deb->fall_delay << 15 | + priv->data->pwm_deb->rise_delay)); + + regmap_read(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR, ®); + if (priv->data->mic_vol <= 7) { + /* micbias1 <= 2.7V */ + regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR, + reg | (priv->data->mic_vol << RG_AUDMICBIAS1VREF_SFT) | + RG_AUDMICBIAS1LOWPEN_MASK_SFT); + } else if (priv->data->mic_vol == 8) { + /* micbias1 = 2.8v */ + regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR, + reg | (3 << RG_AUDMICBIAS1HVEN_SFT) | + RG_AUDMICBIAS1LOWPEN_MASK_SFT); + } else if (priv->data->mic_vol == 9) { + /* micbias1 = 2.85v */ + regmap_write(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR, + reg | (1 << RG_AUDMICBIAS1HVEN_SFT) | + RG_AUDMICBIAS1LOWPEN_MASK_SFT); + } + /* mic mode setting */ + regmap_read(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR, ®); + if (priv->data->mic_mode == HEADSET_MODE_1) { + /* ACC mode*/ + regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR, + reg | RG_ACCDET_MODE_ANA11_MODE1); + /* enable analog fast discharge */ + regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR, + RG_ANALOGFDEN_MASK_SFT, + BIT(RG_ANALOGFDEN_SFT)); + regmap_update_bits(priv->regmap, RG_ACCDETSPARE_ADDR, + 0x3 << 11, 0x3 << 11); + } else if (priv->data->mic_mode == HEADSET_MODE_2) { + /* DCC mode Low cost mode without internal bias */ + regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR, + reg | RG_ACCDET_MODE_ANA11_MODE2); + /* enable analog fast discharge */ + regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR, + 0x3 << RG_ANALOGFDEN_SFT, + 0x3 << RG_ANALOGFDEN_SFT); + } else if (priv->data->mic_mode == HEADSET_MODE_6) { + /* DCC mode Low cost mode with internal bias, + * bit8 = 1 to use internal bias + */ + regmap_write(priv->regmap, RG_AUDACCDETMICBIAS0PULLLOW_ADDR, + reg | RG_ACCDET_MODE_ANA11_MODE6); + regmap_update_bits(priv->regmap, RG_AUDPWDBMICBIAS1_ADDR, + RG_AUDMICBIAS1DCSW1PEN_MASK_SFT, + BIT(RG_AUDMICBIAS1DCSW1PEN_SFT)); + /* enable analog fast discharge */ + regmap_update_bits(priv->regmap, RG_ANALOGFDEN_ADDR, + 0x3 << RG_ANALOGFDEN_SFT, + 0x3 << RG_ANALOGFDEN_SFT); + } + + if (priv->caps & ACCDET_PMIC_EINT_IRQ) { + config_eint_init_by_mode(priv); + config_digital_init_by_mode(priv); + } +} + +int mt6359_accdet_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct mt6359_accdet *priv = + snd_soc_component_get_drvdata(component); + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); + + priv->jack = jack; + + mt6359_accdet_jack_report(priv); + + return 0; +} +EXPORT_SYMBOL_GPL(mt6359_accdet_enable_jack_detect); + +static int mt6359_accdet_probe(struct platform_device *pdev) +{ + struct mt6359_accdet *priv; + struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); + int ret; + + dev_dbg(&pdev->dev, "%s(), dev name %s\n", + __func__, dev_name(&pdev->dev)); + + priv = devm_kzalloc(&pdev->dev, sizeof(struct mt6359_accdet), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->data = devm_kzalloc(&pdev->dev, sizeof(struct dts_data), + GFP_KERNEL); + if (!priv->data) + return -ENOMEM; + + priv->data->pwm_deb = devm_kzalloc(&pdev->dev, + sizeof(struct pwm_deb_settings), + GFP_KERNEL); + if (!priv->data->pwm_deb) + return -ENOMEM; + + priv->regmap = mt6397->regmap; + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&pdev->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + priv->dev = &pdev->dev; + + ret = mt6359_accdet_parse_dt(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to parse dts\n"); + return ret; + } + mutex_init(&priv->res_lock); + + priv->accdet_irq = platform_get_irq(pdev, 0); + if (priv->accdet_irq >= 0) { + ret = devm_request_threaded_irq(&pdev->dev, priv->accdet_irq, + NULL, mt6359_accdet_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "ACCDET_IRQ", priv); + if (ret) { + dev_err(&pdev->dev, + "Failed to request IRQ: (%d)\n", ret); + return ret; + } + } + + if (priv->caps & ACCDET_PMIC_EINT0) { + priv->accdet_eint0 = platform_get_irq(pdev, 1); + if (priv->accdet_eint0 >= 0) { + ret = devm_request_threaded_irq(&pdev->dev, + priv->accdet_eint0, + NULL, mt6359_accdet_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "ACCDET_EINT0", priv); + if (ret) { + dev_err(&pdev->dev, + "Failed to request eint0 IRQ (%d)\n", + ret); + return ret; + } + } + } else if (priv->caps & ACCDET_PMIC_EINT1) { + priv->accdet_eint1 = platform_get_irq(pdev, 2); + if (priv->accdet_eint1 >= 0) { + ret = devm_request_threaded_irq(&pdev->dev, + priv->accdet_eint1, + NULL, mt6359_accdet_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "ACCDET_EINT1", priv); + if (ret) { + dev_err(&pdev->dev, + "Failed to request eint1 IRQ (%d)\n", + ret); + return ret; + } + } + } + + priv->accdet_workqueue = create_singlethread_workqueue("accdet"); + INIT_WORK(&priv->accdet_work, mt6359_accdet_work); + if (!priv->accdet_workqueue) { + dev_err(&pdev->dev, "Failed to create accdet workqueue\n"); + ret = -1; + goto err_accdet_wq; + } + + priv->jd_workqueue = create_singlethread_workqueue("mt6359_accdet_jd"); + INIT_WORK(&priv->jd_work, mt6359_accdet_jd_work); + if (!priv->jd_workqueue) { + dev_err(&pdev->dev, "Failed to create jack detect workqueue\n"); + ret = -1; + goto err_eint_wq; + } + + platform_set_drvdata(pdev, priv); + ret = devm_snd_soc_register_component(&pdev->dev, + &mt6359_accdet_soc_driver, + NULL, 0); + if (ret) { + dev_err(&pdev->dev, "Failed to register component\n"); + return ret; + } + + priv->jd_sts = M_PLUG_OUT; + priv->jack_type = 0; + priv->btn_type = 0; + priv->accdet_status = 0x3; + mt6359_accdet_init(priv); + + mt6359_accdet_jack_report(priv); + + return 0; + +err_eint_wq: + destroy_workqueue(priv->accdet_workqueue); +err_accdet_wq: + dev_err(&pdev->dev, "%s error. now exit.!\n", __func__); + return ret; +} + +static struct platform_driver mt6359_accdet_driver = { + .driver = { + .name = "pmic-codec-accdet", + }, + .probe = mt6359_accdet_probe, +}; + +module_platform_driver(mt6359_accdet_driver) + +/* Module information */ +MODULE_DESCRIPTION("MT6359 ALSA SoC codec jack driver"); +MODULE_AUTHOR("Argus Lin "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/mt6359-accdet.h b/sound/soc/codecs/mt6359-accdet.h new file mode 100644 index 0000000000..c234f2f427 --- /dev/null +++ b/sound/soc/codecs/mt6359-accdet.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 MediaTek Inc. + * Author: Argus Lin + */ + +#ifndef _ACCDET_H_ +#define _ACCDET_H_ + +#include +#include + +#define ACCDET_DEVNAME "accdet" + +#define HEADSET_MODE_1 (1) +#define HEADSET_MODE_2 (2) +#define HEADSET_MODE_6 (6) + +#define MT6359_ACCDET_NUM_BUTTONS 4 +#define MT6359_ACCDET_JACK_MASK (SND_JACK_HEADPHONE | \ + SND_JACK_HEADSET | \ + SND_JACK_BTN_0 | \ + SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | \ + SND_JACK_BTN_3) +#define MT6359_ACCDET_BTN_MASK (SND_JACK_BTN_0 | \ + SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | \ + SND_JACK_BTN_3) + +enum eint_moisture_status { + M_PLUG_IN = 0, + M_WATER_IN = 1, + M_HP_PLUG_IN = 2, + M_PLUG_OUT = 3, + M_NO_ACT = 4, + M_UNKNOWN = 5, +}; + +enum { + accdet_state000 = 0, + accdet_state001, + accdet_state010, + accdet_state011, + accdet_auxadc, + eint_state000, + eint_state001, + eint_state010, + eint_state011, + eint_inverter_state000, +}; + +struct three_key_threshold { + unsigned int mid; + unsigned int up; + unsigned int down; +}; + +struct four_key_threshold { + unsigned int mid; + unsigned int voice; + unsigned int up; + unsigned int down; +}; + +struct pwm_deb_settings { + unsigned int pwm_width; + unsigned int pwm_thresh; + unsigned int fall_delay; + unsigned int rise_delay; + unsigned int debounce0; + unsigned int debounce1; + unsigned int debounce3; + unsigned int debounce4; + unsigned int eint_pwm_width; + unsigned int eint_pwm_thresh; + unsigned int eint_debounce0; + unsigned int eint_debounce1; + unsigned int eint_debounce2; + unsigned int eint_debounce3; + unsigned int eint_inverter_debounce; + +}; + +struct dts_data { + unsigned int mic_vol; + unsigned int mic_mode; + unsigned int plugout_deb; + unsigned int eint_pol; + struct pwm_deb_settings *pwm_deb; + struct three_key_threshold three_key; + struct four_key_threshold four_key; + unsigned int moisture_detect_enable; + unsigned int eint_detect_mode; + unsigned int eint_use_ext_res; + unsigned int eint_comp_vth; + unsigned int moisture_detect_mode; + unsigned int moisture_comp_vth; + unsigned int moisture_comp_vref2; + unsigned int moisture_use_ext_res; +}; + +struct mt6359_accdet { + struct snd_soc_jack *jack; + struct device *dev; + struct regmap *regmap; + struct dts_data *data; + unsigned int caps; + int accdet_irq; + int accdet_eint0; + int accdet_eint1; + struct mutex res_lock; /* lock protection */ + bool jack_plugged; + unsigned int jack_type; + unsigned int btn_type; + unsigned int accdet_status; + unsigned int pre_accdet_status; + unsigned int cali_voltage; + unsigned int jd_sts; + struct work_struct accdet_work; + struct workqueue_struct *accdet_workqueue; + struct work_struct jd_work; + struct workqueue_struct *jd_workqueue; +}; + +int mt6359_accdet_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack); +#endif diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c new file mode 100644 index 0000000000..30690479ec --- /dev/null +++ b/sound/soc/codecs/mt6359.c @@ -0,0 +1,2959 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt6359.c -- mt6359 ALSA SoC audio codec driver +// +// Copyright (c) 2020 MediaTek Inc. +// Author: KaiChieh Chuang + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt6359.h" + +static void mt6359_set_gpio_smt(struct mt6359_priv *priv) +{ + /* set gpio SMT mode */ + regmap_update_bits(priv->regmap, MT6359_SMT_CON1, 0x3ff0, 0x3ff0); +} + +static void mt6359_set_gpio_driving(struct mt6359_priv *priv) +{ + /* 8:4mA(default), a:8mA, c:12mA, e:16mA */ + regmap_update_bits(priv->regmap, MT6359_DRV_CON2, 0xffff, 0x8888); + regmap_update_bits(priv->regmap, MT6359_DRV_CON3, 0xffff, 0x8888); + regmap_update_bits(priv->regmap, MT6359_DRV_CON4, 0x00ff, 0x88); +} + +static void mt6359_set_playback_gpio(struct mt6359_priv *priv) +{ + /* set gpio mosi mode, clk / data mosi */ + regmap_write(priv->regmap, MT6359_GPIO_MODE2_CLR, 0x0ffe); + regmap_write(priv->regmap, MT6359_GPIO_MODE2_SET, 0x0249); + + /* sync mosi */ + regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x6); + regmap_write(priv->regmap, MT6359_GPIO_MODE3_SET, 0x1); +} + +static void mt6359_reset_playback_gpio(struct mt6359_priv *priv) +{ + /* set pad_aud_*_mosi to GPIO mode and dir input + * reason: + * pad_aud_dat_mosi*, because the pin is used as boot strap + * don't clean clk/sync, for mtkaif protocol 2 + */ + regmap_write(priv->regmap, MT6359_GPIO_MODE2_CLR, 0x0ff8); + regmap_update_bits(priv->regmap, MT6359_GPIO_DIR0, 0x7 << 9, 0x0); +} + +static void mt6359_set_capture_gpio(struct mt6359_priv *priv) +{ + /* set gpio miso mode */ + regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x0e00); + regmap_write(priv->regmap, MT6359_GPIO_MODE3_SET, 0x0200); + + regmap_write(priv->regmap, MT6359_GPIO_MODE4_CLR, 0x003f); + regmap_write(priv->regmap, MT6359_GPIO_MODE4_SET, 0x0009); +} + +static void mt6359_reset_capture_gpio(struct mt6359_priv *priv) +{ + /* set pad_aud_*_miso to GPIO mode and dir input + * reason: + * pad_aud_clk_miso, because when playback only the miso_clk + * will also have 26m, so will have power leak + * pad_aud_dat_miso*, because the pin is used as boot strap + */ + regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x0e00); + + regmap_write(priv->regmap, MT6359_GPIO_MODE4_CLR, 0x003f); + + regmap_update_bits(priv->regmap, MT6359_GPIO_DIR0, + 0x7 << 13, 0x0); + regmap_update_bits(priv->regmap, MT6359_GPIO_DIR1, + 0x3 << 0, 0x0); +} + +/* use only when doing mtkaif calibraiton at the boot time */ +static void mt6359_set_dcxo(struct mt6359_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6359_DCXO_CW12, + 0x1 << RG_XO_AUDIO_EN_M_SFT, + (enable ? 1 : 0) << RG_XO_AUDIO_EN_M_SFT); +} + +/* use only when doing mtkaif calibraiton at the boot time */ +static void mt6359_set_clksq(struct mt6359_priv *priv, bool enable) +{ + /* Enable/disable CLKSQ 26MHz */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON23, + RG_CLKSQ_EN_MASK_SFT, + (enable ? 1 : 0) << RG_CLKSQ_EN_SFT); +} + +/* use only when doing mtkaif calibraiton at the boot time */ +static void mt6359_set_aud_global_bias(struct mt6359_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON13, + RG_AUDGLB_PWRDN_VA32_MASK_SFT, + (enable ? 0 : 1) << RG_AUDGLB_PWRDN_VA32_SFT); +} + +/* use only when doing mtkaif calibraiton at the boot time */ +static void mt6359_set_topck(struct mt6359_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6359_AUD_TOP_CKPDN_CON0, + 0x0066, enable ? 0x0 : 0x66); +} + +static void mt6359_set_decoder_clk(struct mt6359_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON13, + RG_RSTB_DECODER_VA32_MASK_SFT, + (enable ? 1 : 0) << RG_RSTB_DECODER_VA32_SFT); +} + +static void mt6359_mtkaif_tx_enable(struct mt6359_priv *priv) +{ + switch (priv->mtkaif_protocol) { + case MT6359_MTKAIF_PROTOCOL_2_CLK_P2: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6359_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0210); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6359_AFE_AUD_PAD_TOP, + 0xff00, 0x3800); + regmap_update_bits(priv->regmap, + MT6359_AFE_AUD_PAD_TOP, + 0xff00, 0x3900); + break; + case MT6359_MTKAIF_PROTOCOL_2: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6359_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0210); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6359_AFE_AUD_PAD_TOP, + 0xff00, 0x3100); + break; + case MT6359_MTKAIF_PROTOCOL_1: + default: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6359_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0000); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6359_AFE_AUD_PAD_TOP, + 0xff00, 0x3100); + break; + } +} + +static void mt6359_mtkaif_tx_disable(struct mt6359_priv *priv) +{ + /* disable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, MT6359_AFE_AUD_PAD_TOP, + 0xff00, 0x3000); +} + +void mt6359_set_mtkaif_protocol(struct snd_soc_component *cmpnt, + int mtkaif_protocol) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + priv->mtkaif_protocol = mtkaif_protocol; +} +EXPORT_SYMBOL_GPL(mt6359_set_mtkaif_protocol); + +void mt6359_mtkaif_calibration_enable(struct snd_soc_component *cmpnt) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + mt6359_set_playback_gpio(priv); + mt6359_set_capture_gpio(priv); + mt6359_mtkaif_tx_enable(priv); + + mt6359_set_dcxo(priv, true); + mt6359_set_aud_global_bias(priv, true); + mt6359_set_clksq(priv, true); + mt6359_set_topck(priv, true); + + /* set dat_miso_loopback on */ + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT, + 1 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT, + 1 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1, + RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT, + 1 << RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT); +} +EXPORT_SYMBOL_GPL(mt6359_mtkaif_calibration_enable); + +void mt6359_mtkaif_calibration_disable(struct snd_soc_component *cmpnt) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + /* set dat_miso_loopback off */ + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT, + 0 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT, + 0 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1, + RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT, + 0 << RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT); + + mt6359_set_topck(priv, false); + mt6359_set_clksq(priv, false); + mt6359_set_aud_global_bias(priv, false); + mt6359_set_dcxo(priv, false); + + mt6359_mtkaif_tx_disable(priv); + mt6359_reset_playback_gpio(priv); + mt6359_reset_capture_gpio(priv); +} +EXPORT_SYMBOL_GPL(mt6359_mtkaif_calibration_disable); + +void mt6359_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt, + int phase_1, int phase_2, int phase_3) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT, + phase_1 << RG_AUD_PAD_TOP_PHASE_MODE_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG, + RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT, + phase_2 << RG_AUD_PAD_TOP_PHASE_MODE2_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDIO_DIG_CFG1, + RG_AUD_PAD_TOP_PHASE_MODE3_MASK_SFT, + phase_3 << RG_AUD_PAD_TOP_PHASE_MODE3_SFT); +} +EXPORT_SYMBOL_GPL(mt6359_set_mtkaif_calibration_phase); + +static void zcd_disable(struct mt6359_priv *priv) +{ + regmap_write(priv->regmap, MT6359_ZCD_CON0, 0x0000); +} + +static void hp_main_output_ramp(struct mt6359_priv *priv, bool up) +{ + int i, stage; + int target = 7; + + /* Enable/Reduce HPL/R main output stage step by step */ + for (i = 0; i <= target; i++) { + stage = up ? i : target - i; + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, + RG_HPLOUTSTGCTRL_VAUDP32_MASK_SFT, + stage << RG_HPLOUTSTGCTRL_VAUDP32_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, + RG_HPROUTSTGCTRL_VAUDP32_MASK_SFT, + stage << RG_HPROUTSTGCTRL_VAUDP32_SFT); + usleep_range(600, 650); + } +} + +static void hp_aux_feedback_loop_gain_ramp(struct mt6359_priv *priv, bool up) +{ + int i, stage; + int target = 0xf; + + /* Enable/Reduce HP aux feedback loop gain step by step */ + for (i = 0; i <= target; i++) { + stage = up ? i : target - i; + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON9, + 0xf << 12, stage << 12); + usleep_range(600, 650); + } +} + +static void hp_in_pair_current(struct mt6359_priv *priv, bool increase) +{ + int i, stage; + int target = 0x3; + + /* Set input diff pair bias select (Hi-Fi mode) */ + if (priv->hp_hifi_mode) { + /* Reduce HP aux feedback loop gain step by step */ + for (i = 0; i <= target; i++) { + stage = increase ? i : target - i; + regmap_update_bits(priv->regmap, + MT6359_AUDDEC_ANA_CON10, + 0x3 << 3, stage << 3); + usleep_range(100, 150); + } + } +} + +static void hp_pull_down(struct mt6359_priv *priv, bool enable) +{ + int i; + + if (enable) { + for (i = 0x0; i <= 0x7; i++) { + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2, + RG_HPPSHORT2VCM_VAUDP32_MASK_SFT, + i << RG_HPPSHORT2VCM_VAUDP32_SFT); + usleep_range(100, 150); + } + } else { + for (i = 0x7; i >= 0x0; i--) { + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2, + RG_HPPSHORT2VCM_VAUDP32_MASK_SFT, + i << RG_HPPSHORT2VCM_VAUDP32_SFT); + usleep_range(100, 150); + } + } +} + +static bool is_valid_hp_pga_idx(int reg_idx) +{ + return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_22DB) || + reg_idx == DL_GAIN_N_40DB; +} + +static void headset_volume_ramp(struct mt6359_priv *priv, + int from, int to) +{ + int offset = 0, count = 1, reg_idx; + + if (!is_valid_hp_pga_idx(from) || !is_valid_hp_pga_idx(to)) { + dev_warn(priv->dev, "%s(), volume index is not valid, from %d, to %d\n", + __func__, from, to); + return; + } + + dev_dbg(priv->dev, "%s(), from %d, to %d\n", __func__, from, to); + + if (to > from) + offset = to - from; + else + offset = from - to; + + while (offset > 0) { + if (to > from) + reg_idx = from + count; + else + reg_idx = from - count; + + if (is_valid_hp_pga_idx(reg_idx)) { + regmap_update_bits(priv->regmap, + MT6359_ZCD_CON2, + DL_GAIN_REG_MASK, + (reg_idx << 7) | reg_idx); + usleep_range(600, 650); + } + offset--; + count++; + } +} + +static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = 0; + int index = ucontrol->value.integer.value[0]; + int orig_gain[2], new_gain[2]; + int ret; + + switch (mc->reg) { + case MT6359_ZCD_CON2: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; + break; + case MT6359_ZCD_CON1: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; + break; + case MT6359_ZCD_CON3: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; + break; + case MT6359_AUDENC_ANA_CON0: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; + break; + case MT6359_AUDENC_ANA_CON1: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; + break; + case MT6359_AUDENC_ANA_CON2: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3]; + break; + default: + return -EINVAL; + } + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + switch (mc->reg) { + case MT6359_ZCD_CON2: + regmap_read(priv->regmap, MT6359_ZCD_CON2, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] = + (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK; + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = + (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; + break; + case MT6359_ZCD_CON1: + regmap_read(priv->regmap, MT6359_ZCD_CON1, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] = + (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK; + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] = + (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; + break; + case MT6359_ZCD_CON3: + regmap_read(priv->regmap, MT6359_ZCD_CON3, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] = + (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; + break; + case MT6359_AUDENC_ANA_CON0: + regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON0, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] = + (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; + break; + case MT6359_AUDENC_ANA_CON1: + regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON1, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] = + (reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; + break; + case MT6359_AUDENC_ANA_CON2: + regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON2, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3] = + (reg >> RG_AUDPREAMP3GAIN_SFT) & RG_AUDPREAMP3GAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3]; + break; + } + + ret = 0; + if (orig_gain[0] != new_gain[0]) { + ret = 1; + } else if (snd_soc_volsw_is_stereo(mc)) { + if (orig_gain[1] != new_gain[1]) + ret = 1; + } + + dev_dbg(priv->dev, "%s(), name %s, reg(0x%x) = 0x%x, set index = %x\n", + __func__, kcontrol->id.name, mc->reg, reg, index); + + return ret; +} + +static int mt6359_get_playback_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + switch (mc->reg) { + case MT6359_ZCD_CON2: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + ucontrol->value.integer.value[1] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; + break; + case MT6359_ZCD_CON1: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + ucontrol->value.integer.value[1] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; + break; + case MT6359_ZCD_CON3: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* MUX */ + +/* LOL MUX */ +static const char * const lo_in_mux_map[] = { + "Open", "Playback_L_DAC", "Playback", "Test Mode" +}; + +static SOC_ENUM_SINGLE_DECL(lo_in_mux_map_enum, SND_SOC_NOPM, 0, lo_in_mux_map); + +static const struct snd_kcontrol_new lo_in_mux_control = + SOC_DAPM_ENUM("LO Select", lo_in_mux_map_enum); + +/*HP MUX */ +static const char * const hp_in_mux_map[] = { + "Open", + "LoudSPK Playback", + "Audio Playback", + "Test Mode", + "HP Impedance", +}; + +static SOC_ENUM_SINGLE_DECL(hp_in_mux_map_enum, + SND_SOC_NOPM, + 0, + hp_in_mux_map); + +static const struct snd_kcontrol_new hp_in_mux_control = + SOC_DAPM_ENUM("HP Select", hp_in_mux_map_enum); + +/* RCV MUX */ +static const char * const rcv_in_mux_map[] = { + "Open", "Mute", "Voice Playback", "Test Mode" +}; + +static SOC_ENUM_SINGLE_DECL(rcv_in_mux_map_enum, + SND_SOC_NOPM, + 0, + rcv_in_mux_map); + +static const struct snd_kcontrol_new rcv_in_mux_control = + SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum); + +/* DAC In MUX */ +static const char * const dac_in_mux_map[] = { + "Normal Path", "Sgen" +}; + +static int dac_in_mux_map_value[] = { + 0x0, 0x1, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum, + MT6359_AFE_TOP_CON0, + DL_SINE_ON_SFT, + DL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new dac_in_mux_control = + SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum); + +/* AIF Out MUX */ +static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum, + MT6359_AFE_TOP_CON0, + UL_SINE_ON_SFT, + UL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new aif_out_mux_control = + SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(aif2_out_mux_map_enum, + MT6359_AFE_TOP_CON0, + ADDA6_UL_SINE_ON_SFT, + ADDA6_UL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new aif2_out_mux_control = + SOC_DAPM_ENUM("AIF Out Select", aif2_out_mux_map_enum); + +static const char * const ul_src_mux_map[] = { + "AMIC", + "DMIC", +}; + +static int ul_src_mux_map_value[] = { + UL_SRC_MUX_AMIC, + UL_SRC_MUX_DMIC, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(ul_src_mux_map_enum, + MT6359_AFE_UL_SRC_CON0_L, + UL_SDM_3_LEVEL_CTL_SFT, + UL_SDM_3_LEVEL_CTL_MASK, + ul_src_mux_map, + ul_src_mux_map_value); + +static const struct snd_kcontrol_new ul_src_mux_control = + SOC_DAPM_ENUM("UL_SRC_MUX Select", ul_src_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(ul2_src_mux_map_enum, + MT6359_AFE_ADDA6_UL_SRC_CON0_L, + ADDA6_UL_SDM_3_LEVEL_CTL_SFT, + ADDA6_UL_SDM_3_LEVEL_CTL_MASK, + ul_src_mux_map, + ul_src_mux_map_value); + +static const struct snd_kcontrol_new ul2_src_mux_control = + SOC_DAPM_ENUM("UL_SRC_MUX Select", ul2_src_mux_map_enum); + +static const char * const miso_mux_map[] = { + "UL1_CH1", + "UL1_CH2", + "UL2_CH1", + "UL2_CH2", +}; + +static int miso_mux_map_value[] = { + MISO_MUX_UL1_CH1, + MISO_MUX_UL1_CH2, + MISO_MUX_UL2_CH1, + MISO_MUX_UL2_CH2, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(miso0_mux_map_enum, + MT6359_AFE_MTKAIF_MUX_CFG, + RG_ADDA_CH1_SEL_SFT, + RG_ADDA_CH1_SEL_MASK, + miso_mux_map, + miso_mux_map_value); + +static const struct snd_kcontrol_new miso0_mux_control = + SOC_DAPM_ENUM("MISO_MUX Select", miso0_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(miso1_mux_map_enum, + MT6359_AFE_MTKAIF_MUX_CFG, + RG_ADDA_CH2_SEL_SFT, + RG_ADDA_CH2_SEL_MASK, + miso_mux_map, + miso_mux_map_value); + +static const struct snd_kcontrol_new miso1_mux_control = + SOC_DAPM_ENUM("MISO_MUX Select", miso1_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(miso2_mux_map_enum, + MT6359_AFE_MTKAIF_MUX_CFG, + RG_ADDA6_CH1_SEL_SFT, + RG_ADDA6_CH1_SEL_MASK, + miso_mux_map, + miso_mux_map_value); + +static const struct snd_kcontrol_new miso2_mux_control = + SOC_DAPM_ENUM("MISO_MUX Select", miso2_mux_map_enum); + +static const char * const dmic_mux_map[] = { + "DMIC_DATA0", + "DMIC_DATA1_L", + "DMIC_DATA1_L_1", + "DMIC_DATA1_R", +}; + +static int dmic_mux_map_value[] = { + DMIC_MUX_DMIC_DATA0, + DMIC_MUX_DMIC_DATA1_L, + DMIC_MUX_DMIC_DATA1_L_1, + DMIC_MUX_DMIC_DATA1_R, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dmic0_mux_map_enum, + MT6359_AFE_MIC_ARRAY_CFG, + RG_DMIC_ADC1_SOURCE_SEL_SFT, + RG_DMIC_ADC1_SOURCE_SEL_MASK, + dmic_mux_map, + dmic_mux_map_value); + +static const struct snd_kcontrol_new dmic0_mux_control = + SOC_DAPM_ENUM("DMIC_MUX Select", dmic0_mux_map_enum); + +/* ul1 ch2 use RG_DMIC_ADC3_SOURCE_SEL */ +static SOC_VALUE_ENUM_SINGLE_DECL(dmic1_mux_map_enum, + MT6359_AFE_MIC_ARRAY_CFG, + RG_DMIC_ADC3_SOURCE_SEL_SFT, + RG_DMIC_ADC3_SOURCE_SEL_MASK, + dmic_mux_map, + dmic_mux_map_value); + +static const struct snd_kcontrol_new dmic1_mux_control = + SOC_DAPM_ENUM("DMIC_MUX Select", dmic1_mux_map_enum); + +/* ul2 ch1 use RG_DMIC_ADC2_SOURCE_SEL */ +static SOC_VALUE_ENUM_SINGLE_DECL(dmic2_mux_map_enum, + MT6359_AFE_MIC_ARRAY_CFG, + RG_DMIC_ADC2_SOURCE_SEL_SFT, + RG_DMIC_ADC2_SOURCE_SEL_MASK, + dmic_mux_map, + dmic_mux_map_value); + +static const struct snd_kcontrol_new dmic2_mux_control = + SOC_DAPM_ENUM("DMIC_MUX Select", dmic2_mux_map_enum); + +/* ADC L MUX */ +static const char * const adc_left_mux_map[] = { + "Idle", "AIN0", "Left Preamplifier", "Idle_1" +}; + +static int adc_mux_map_value[] = { + ADC_MUX_IDLE, + ADC_MUX_AIN0, + ADC_MUX_PREAMPLIFIER, + ADC_MUX_IDLE1, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum, + MT6359_AUDENC_ANA_CON0, + RG_AUDADCLINPUTSEL_SFT, + RG_AUDADCLINPUTSEL_MASK, + adc_left_mux_map, + adc_mux_map_value); + +static const struct snd_kcontrol_new adc_left_mux_control = + SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum); + +/* ADC R MUX */ +static const char * const adc_right_mux_map[] = { + "Idle", "AIN0", "Right Preamplifier", "Idle_1" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum, + MT6359_AUDENC_ANA_CON1, + RG_AUDADCRINPUTSEL_SFT, + RG_AUDADCRINPUTSEL_MASK, + adc_right_mux_map, + adc_mux_map_value); + +static const struct snd_kcontrol_new adc_right_mux_control = + SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum); + +/* ADC 3 MUX */ +static const char * const adc_3_mux_map[] = { + "Idle", "AIN0", "Preamplifier", "Idle_1" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_3_mux_map_enum, + MT6359_AUDENC_ANA_CON2, + RG_AUDADC3INPUTSEL_SFT, + RG_AUDADC3INPUTSEL_MASK, + adc_3_mux_map, + adc_mux_map_value); + +static const struct snd_kcontrol_new adc_3_mux_control = + SOC_DAPM_ENUM("ADC 3 Select", adc_3_mux_map_enum); + +static const char * const pga_l_mux_map[] = { + "None", "AIN0", "AIN1" +}; + +static int pga_l_mux_map_value[] = { + PGA_L_MUX_NONE, + PGA_L_MUX_AIN0, + PGA_L_MUX_AIN1 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum, + MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLINPUTSEL_SFT, + RG_AUDPREAMPLINPUTSEL_MASK, + pga_l_mux_map, + pga_l_mux_map_value); + +static const struct snd_kcontrol_new pga_left_mux_control = + SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum); + +static const char * const pga_r_mux_map[] = { + "None", "AIN2", "AIN3", "AIN0" +}; + +static int pga_r_mux_map_value[] = { + PGA_R_MUX_NONE, + PGA_R_MUX_AIN2, + PGA_R_MUX_AIN3, + PGA_R_MUX_AIN0 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum, + MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRINPUTSEL_SFT, + RG_AUDPREAMPRINPUTSEL_MASK, + pga_r_mux_map, + pga_r_mux_map_value); + +static const struct snd_kcontrol_new pga_right_mux_control = + SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum); + +static const char * const pga_3_mux_map[] = { + "None", "AIN3", "AIN2" +}; + +static int pga_3_mux_map_value[] = { + PGA_3_MUX_NONE, + PGA_3_MUX_AIN3, + PGA_3_MUX_AIN2 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_3_mux_map_enum, + MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3INPUTSEL_SFT, + RG_AUDPREAMP3INPUTSEL_MASK, + pga_3_mux_map, + pga_3_mux_map_value); + +static const struct snd_kcontrol_new pga_3_mux_control = + SOC_DAPM_ENUM("PGA 3 Select", pga_3_mux_map_enum); + +static int mt_sgen_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* sdm audio fifo clock power on */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0006); + /* scrambler clock on enable */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba1); + /* sdm power on */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0003); + /* sdm fifo enable */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x000b); + + regmap_update_bits(priv->regmap, MT6359_AFE_SGEN_CFG0, + 0xff3f, + 0x0000); + regmap_update_bits(priv->regmap, MT6359_AFE_SGEN_CFG1, + 0xffff, + 0x0001); + break; + case SND_SOC_DAPM_POST_PMD: + /* DL scrambler disabling sequence */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0000); + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba0); + break; + default: + break; + } + + return 0; +} + +static void mtk_hp_enable(struct mt6359_priv *priv) +{ + if (priv->hp_hifi_mode) { + /* Set HP DR bias current optimization, 010: 6uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11, + DRBIAS_HP_MASK_SFT, + DRBIAS_6UA << DRBIAS_HP_SFT); + /* Set HP & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_ZCD_MASK_SFT, + IBIAS_ZCD_4UA << IBIAS_ZCD_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_HP_MASK_SFT, + IBIAS_5UA << IBIAS_HP_SFT); + } else { + /* Set HP DR bias current optimization, 001: 5uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11, + DRBIAS_HP_MASK_SFT, + DRBIAS_5UA << DRBIAS_HP_SFT); + /* Set HP & ZCD bias current optimization */ + /* 00: ZCD: 3uA, HP/HS/LO: 4uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_ZCD_MASK_SFT, + IBIAS_ZCD_3UA << IBIAS_ZCD_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_HP_MASK_SFT, + IBIAS_4UA << IBIAS_HP_SFT); + } + + /* HP damp circuit enable */ + /* Enable HPRN/HPLN output 4K to VCM */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x0087); + + /* HP Feedback Cap select 2'b00: 15pF */ + /* for >= 96KHz sampling rate: 2'b01: 10.5pF */ + if (priv->dl_rate[MT6359_AIF_1] >= 96000) + regmap_update_bits(priv->regmap, + MT6359_AUDDEC_ANA_CON4, + RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK_SFT, + 0x1 << RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_SFT); + else + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON4, 0x0000); + + /* Set HPP/N STB enhance circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON2, 0xf133); + + /* Enable HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x000c); + /* Enable HP aux feedback loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x003c); + /* Enable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0c00); + /* Enable HP driver bias circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30c0); + /* Enable HP driver core circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30f0); + /* Short HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x00fc); + + /* Increase HP input pair current to HPM step by step */ + hp_in_pair_current(priv, true); + + /* Enable HP main CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0e00); + /* Disable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0200); + + /* Enable HP main output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x00ff); + /* Enable HPR/L main output stage step by step */ + hp_main_output_ramp(priv, true); + + /* Reduce HP aux feedback loop gain */ + hp_aux_feedback_loop_gain_ramp(priv, true); + /* Disable HP aux feedback loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77cf); + + /* apply volume setting */ + headset_volume_ramp(priv, + DL_GAIN_N_22DB, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]); + + /* Disable HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77c3); + /* Unshort HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x7703); + usleep_range(100, 120); + + /* Enable AUD_CLK */ + mt6359_set_decoder_clk(priv, true); + + /* Enable Audio DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30ff); + if (priv->hp_hifi_mode) { + /* Enable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf201); + } else { + /* Disable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf200); + } + usleep_range(100, 120); + + /* Switch HPL MUX to audio DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x32ff); + /* Switch HPR MUX to audio DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x3aff); + + /* Disable Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, false); +} + +static void mtk_hp_disable(struct mt6359_priv *priv) +{ + /* Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, true); + + /* HPR/HPL mux to open */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x0f00, 0x0000); + + /* Disable low-noise mode of DAC */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON9, + 0x0001, 0x0000); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + mt6359_set_decoder_clk(priv, false); + + /* Short HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77c3); + /* Enable HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77cf); + + /* decrease HPL/R gain to normal gain step by step */ + headset_volume_ramp(priv, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL], + DL_GAIN_N_22DB); + + /* Enable HP aux feedback loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77ff); + + /* Reduce HP aux feedback loop gain */ + hp_aux_feedback_loop_gain_ramp(priv, false); + + /* decrease HPR/L main output stage step by step */ + hp_main_output_ramp(priv, false); + + /* Disable HP main output stage */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x3, 0x0); + + /* Enable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0e01); + + /* Disable HP main CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0c01); + + /* Decrease HP input pair current to 2'b00 step by step */ + hp_in_pair_current(priv, false); + + /* Unshort HP main output to HP aux output stage */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, + 0x3 << 6, 0x0); + + /* Disable HP driver core circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 4, 0x0); + + /* Disable HP driver bias circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 6, 0x0); + + /* Disable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x201); + + /* Disable HP aux feedback loop */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, + 0x3 << 4, 0x0); + + /* Disable HP aux output stage */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, + 0x3 << 2, 0x0); +} + +static int mt_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + int device = DEVICE_HP; + + dev_dbg(priv->dev, "%s(), event 0x%x, dev_counter[DEV_HP] %d, mux %u\n", + __func__, event, priv->dev_counter[device], mux); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + priv->dev_counter[device]++; + if (mux == HP_MUX_HP) + mtk_hp_enable(priv); + break; + case SND_SOC_DAPM_PRE_PMD: + priv->dev_counter[device]--; + if (mux == HP_MUX_HP) + mtk_hp_disable(priv); + break; + default: + break; + } + + return 0; +} + +static int mt_rcv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n", + __func__, event, dapm_kcontrol_get_value(w->kcontrols[0])); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disable handset short-circuit protection */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0010); + + /* Set RCV DR bias current optimization, 010: 6uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11, + DRBIAS_HS_MASK_SFT, + DRBIAS_6UA << DRBIAS_HS_SFT); + /* Set RCV & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_ZCD_MASK_SFT, + IBIAS_ZCD_4UA << IBIAS_ZCD_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_HS_MASK_SFT, + IBIAS_5UA << IBIAS_HS_SFT); + + /* Set HS STB enhance circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0090); + + /* Set HS output stage (3'b111 = 8x) */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x7000); + + /* Enable HS driver bias circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0092); + /* Enable HS driver core circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0093); + + /* Set HS gain to normal gain step by step */ + regmap_write(priv->regmap, MT6359_ZCD_CON3, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]); + + /* Enable AUD_CLK */ + mt6359_set_decoder_clk(priv, true); + + /* Enable Audio DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x0009); + /* Enable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0001); + /* Switch HS MUX to audio DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x009b); + break; + case SND_SOC_DAPM_PRE_PMD: + /* HS mux to open */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6, + RG_AUDHSMUXINPUTSEL_VAUDP32_MASK_SFT, + RCV_MUX_OPEN); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + mt6359_set_decoder_clk(priv, false); + + /* decrease HS gain to minimum gain step by step */ + regmap_write(priv->regmap, MT6359_ZCD_CON3, DL_GAIN_N_40DB); + + /* Disable HS driver core circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6, + RG_AUDHSPWRUP_VAUDP32_MASK_SFT, 0x0); + + /* Disable HS driver bias circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6, + RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK_SFT, 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_lo_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n", + __func__, event, mux); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disable handset short-circuit protection */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0010); + + /* Set LO DR bias current optimization, 010: 6uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11, + DRBIAS_LO_MASK_SFT, + DRBIAS_6UA << DRBIAS_LO_SFT); + /* Set LO & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + if (priv->dev_counter[DEVICE_HP] == 0) + regmap_update_bits(priv->regmap, + MT6359_AUDDEC_ANA_CON12, + IBIAS_ZCD_MASK_SFT, + IBIAS_ZCD_4UA << IBIAS_ZCD_SFT); + + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_LO_MASK_SFT, + IBIAS_5UA << IBIAS_LO_SFT); + + /* Set LO STB enhance circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0110); + + /* Enable LO driver bias circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0112); + /* Enable LO driver core circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0113); + + /* Set LO gain to normal gain step by step */ + regmap_write(priv->regmap, MT6359_ZCD_CON1, + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]); + + /* Enable AUD_CLK */ + mt6359_set_decoder_clk(priv, true); + + /* Switch LOL MUX to audio DAC */ + if (mux == LO_MUX_L_DAC) { + if (priv->dev_counter[DEVICE_HP] > 0) { + dev_info(priv->dev, "%s(), can not enable DAC, hp count %d\n", + __func__, priv->dev_counter[DEVICE_HP]); + break; + } + /* Enable DACL and switch HP MUX to open*/ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x3009); + /* Disable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf200); + usleep_range(100, 120); + /* Switch LOL MUX to DACL */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0117); + } else if (mux == LO_MUX_3RD_DAC) { + /* Enable Audio DAC (3rd DAC) */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113); + /* Enable low-noise mode of DAC */ + if (priv->dev_counter[DEVICE_HP] == 0) + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0001); + /* Switch LOL MUX to audio 3rd DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b); + } + break; + case SND_SOC_DAPM_PRE_PMD: + /* Switch LOL MUX to open */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7, + RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK_SFT, + LO_MUX_OPEN); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + if (mux == LO_MUX_L_DAC) { + /* Disable HP driver core circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 4, 0x0); + /* Disable HP driver bias circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 6, 0x0); + } + + /* Disable AUD_CLK */ + mt6359_set_decoder_clk(priv, false); + + /* decrease LO gain to minimum gain step by step */ + regmap_write(priv->regmap, MT6359_ZCD_CON1, DL_GAIN_N_40DB); + + /* Disable LO driver core circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7, + RG_AUDLOLPWRUP_VAUDP32_MASK_SFT, 0x0); + + /* Disable LO driver bias circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7, + RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK_SFT, 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_adc_clk_gen_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* ADC CLK from CLKGEN (6.5MHz) */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKRSTB_MASK_SFT, + 0x1 << RG_AUDADCCLKRSTB_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKSOURCE_MASK_SFT, 0x0); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKSEL_MASK_SFT, 0x0); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKGENMODE_MASK_SFT, + 0x1 << RG_AUDADCCLKGENMODE_SFT); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKSOURCE_MASK_SFT, 0x0); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKSEL_MASK_SFT, 0x0); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKGENMODE_MASK_SFT, 0x0); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKRSTB_MASK_SFT, 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_dcc_clk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* DCC 50k CLK (from 26M) */ + /* MT6359_AFE_DCCLK_CFG0, bit 3 for dm ck swap */ + regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0, + 0xfff7, 0x2062); + regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0, + 0xfff7, 0x2060); + regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0, + 0xfff7, 0x2061); + + regmap_write(priv->regmap, MT6359_AFE_DCCLK_CFG1, 0x0100); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0, + 0xfff7, 0x2060); + regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0, + 0xfff7, 0x2062); + break; + default: + break; + } + + return 0; +} + +static int mt_mic_bias_0_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_0]; + + dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n", + __func__, event, mic_type); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (mic_type) { + case MIC_TYPE_MUX_DCC_ECM_DIFF: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON15, + 0xff00, 0x7700); + break; + case MIC_TYPE_MUX_DCC_ECM_SINGLE: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON15, + 0xff00, 0x1100); + break; + default: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON15, + 0xff00, 0x0000); + break; + } + + /* DMIC enable */ + regmap_write(priv->regmap, + MT6359_AUDENC_ANA_CON14, 0x0004); + /* MISBIAS0 = 1P9V */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON15, + RG_AUDMICBIAS0VREF_MASK_SFT, + MIC_BIAS_1P9 << RG_AUDMICBIAS0VREF_SFT); + /* normal power select */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON15, + RG_AUDMICBIAS0LOWPEN_MASK_SFT, + 0 << RG_AUDMICBIAS0LOWPEN_SFT); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable MICBIAS0, MISBIAS0 = 1P7V */ + regmap_write(priv->regmap, MT6359_AUDENC_ANA_CON15, 0x0000); + break; + default: + break; + } + + return 0; +} + +static int mt_mic_bias_1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_1]; + + dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n", + __func__, event, mic_type); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* MISBIAS1 = 2P6V */ + if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE) + regmap_write(priv->regmap, + MT6359_AUDENC_ANA_CON16, 0x0160); + else + regmap_write(priv->regmap, + MT6359_AUDENC_ANA_CON16, 0x0060); + + /* normal power select */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON16, + RG_AUDMICBIAS1LOWPEN_MASK_SFT, + 0 << RG_AUDMICBIAS1LOWPEN_SFT); + break; + default: + break; + } + + return 0; +} + +static int mt_mic_bias_2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_2]; + + dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n", + __func__, event, mic_type); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (mic_type) { + case MIC_TYPE_MUX_DCC_ECM_DIFF: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON17, + 0xff00, 0x7700); + break; + case MIC_TYPE_MUX_DCC_ECM_SINGLE: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON17, + 0xff00, 0x1100); + break; + default: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON17, + 0xff00, 0x0000); + break; + } + + /* MISBIAS2 = 1P9V */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON17, + RG_AUDMICBIAS2VREF_MASK_SFT, + MIC_BIAS_1P9 << RG_AUDMICBIAS2VREF_SFT); + /* normal power select */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON17, + RG_AUDMICBIAS2LOWPEN_MASK_SFT, + 0 << RG_AUDMICBIAS2LOWPEN_SFT); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable MICBIAS2, MISBIAS0 = 1P7V */ + regmap_write(priv->regmap, MT6359_AUDENC_ANA_CON17, 0x0000); + break; + default: + break; + } + + return 0; +} + +static int mt_mtkaif_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mt6359_mtkaif_tx_enable(priv); + break; + case SND_SOC_DAPM_POST_PMD: + mt6359_mtkaif_tx_disable(priv); + break; + default: + break; + } + + return 0; +} + +static int mt_ul_src_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* UL dmic setting */ + if (priv->dmic_one_wire_mode) + regmap_write(priv->regmap, MT6359_AFE_UL_SRC_CON0_H, + 0x0400); + else + regmap_write(priv->regmap, MT6359_AFE_UL_SRC_CON0_H, + 0x0080); + /* default one wire, 3.25M */ + regmap_update_bits(priv->regmap, MT6359_AFE_UL_SRC_CON0_L, + 0xfffc, 0x0000); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_write(priv->regmap, + MT6359_AFE_UL_SRC_CON0_H, 0x0000); + break; + default: + break; + } + + return 0; +} + +static int mt_ul_src_34_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* default two wire, 3.25M */ + regmap_write(priv->regmap, + MT6359_AFE_ADDA6_L_SRC_CON0_H, 0x0080); + regmap_update_bits(priv->regmap, MT6359_AFE_ADDA6_UL_SRC_CON0_L, + 0xfffc, 0x0000); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_write(priv->regmap, + MT6359_AFE_ADDA6_L_SRC_CON0_H, 0x0000); + break; + default: + break; + } + + return 0; +} + +static int mt_adc_l_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(100, 120); + /* Audio L preamplifier DCC precharge off */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCPRECHARGE_MASK_SFT, + 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_adc_r_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(100, 120); + /* Audio R preamplifier DCC precharge off */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCPRECHARGE_MASK_SFT, + 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_adc_3_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(100, 120); + /* Audio R preamplifier DCC precharge off */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3DCPRECHARGE_MASK_SFT, + 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_pga_l_mux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux); + priv->mux_select[MUX_PGA_L] = mux >> RG_AUDPREAMPLINPUTSEL_SFT; + return 0; +} + +static int mt_pga_r_mux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux); + priv->mux_select[MUX_PGA_R] = mux >> RG_AUDPREAMPRINPUTSEL_SFT; + return 0; +} + +static int mt_pga_3_mux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux); + priv->mux_select[MUX_PGA_3] = mux >> RG_AUDPREAMP3INPUTSEL_SFT; + return 0; +} + +static int mt_pga_l_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int mic_gain_l = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; + unsigned int mux_pga = priv->mux_select[MUX_PGA_L]; + unsigned int mic_type; + + switch (mux_pga) { + case PGA_L_MUX_AIN0: + mic_type = priv->mux_select[MUX_MIC_TYPE_0]; + break; + case PGA_L_MUX_AIN1: + mic_type = priv->mux_select[MUX_MIC_TYPE_1]; + break; + default: + dev_err(priv->dev, "%s(), invalid pga mux %d\n", + __func__, mux_pga); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (IS_DCC_BASE(mic_type)) { + /* Audio L preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCPRECHARGE_MASK_SFT, + 0x1 << RG_AUDPREAMPLDCPRECHARGE_SFT); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* set mic pga gain */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLGAIN_MASK_SFT, + mic_gain_l << RG_AUDPREAMPLGAIN_SFT); + + if (IS_DCC_BASE(mic_type)) { + /* L preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCCEN_MASK_SFT, + 0x1 << RG_AUDPREAMPLDCCEN_SFT); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* L preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCCEN_MASK_SFT, + 0x0 << RG_AUDPREAMPLDCCEN_SFT); + break; + default: + break; + } + + return 0; +} + +static int mt_pga_r_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int mic_gain_r = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; + unsigned int mux_pga = priv->mux_select[MUX_PGA_R]; + unsigned int mic_type; + + switch (mux_pga) { + case PGA_R_MUX_AIN0: + mic_type = priv->mux_select[MUX_MIC_TYPE_0]; + break; + case PGA_R_MUX_AIN2: + case PGA_R_MUX_AIN3: + mic_type = priv->mux_select[MUX_MIC_TYPE_2]; + break; + default: + dev_err(priv->dev, "%s(), invalid pga mux %d\n", + __func__, mux_pga); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (IS_DCC_BASE(mic_type)) { + /* Audio R preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCPRECHARGE_MASK_SFT, + 0x1 << RG_AUDPREAMPRDCPRECHARGE_SFT); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* set mic pga gain */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRGAIN_MASK_SFT, + mic_gain_r << RG_AUDPREAMPRGAIN_SFT); + + if (IS_DCC_BASE(mic_type)) { + /* R preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCCEN_MASK_SFT, + 0x1 << RG_AUDPREAMPRDCCEN_SFT); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* R preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCCEN_MASK_SFT, + 0x0 << RG_AUDPREAMPRDCCEN_SFT); + break; + default: + break; + } + + return 0; +} + +static int mt_pga_3_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int mic_gain_3 = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3]; + unsigned int mux_pga = priv->mux_select[MUX_PGA_3]; + unsigned int mic_type; + + switch (mux_pga) { + case PGA_3_MUX_AIN2: + case PGA_3_MUX_AIN3: + mic_type = priv->mux_select[MUX_MIC_TYPE_2]; + break; + default: + dev_err(priv->dev, "%s(), invalid pga mux %d\n", + __func__, mux_pga); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (IS_DCC_BASE(mic_type)) { + /* Audio 3 preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3DCPRECHARGE_MASK_SFT, + 0x1 << RG_AUDPREAMP3DCPRECHARGE_SFT); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* set mic pga gain */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3GAIN_MASK_SFT, + mic_gain_3 << RG_AUDPREAMP3GAIN_SFT); + + if (IS_DCC_BASE(mic_type)) { + /* 3 preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3DCCEN_MASK_SFT, + 0x1 << RG_AUDPREAMP3DCCEN_SFT); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* 3 preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3DCCEN_MASK_SFT, + 0x0 << RG_AUDPREAMP3DCCEN_SFT); + break; + default: + break; + } + + return 0; +} + +/* It is based on hw's control sequenece to add some delay when PMU/PMD */ +static int mt_delay_250_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMD: + usleep_range(250, 270); + break; + default: + break; + } + + return 0; +} + +static int mt_delay_100_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMD: + usleep_range(100, 120); + break; + default: + break; + } + + return 0; +} + +static int mt_hp_pull_down_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hp_pull_down(priv, true); + break; + case SND_SOC_DAPM_POST_PMD: + hp_pull_down(priv, false); + break; + default: + break; + } + + return 0; +} + +static int mt_hp_mute_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Set HPR/HPL gain to -22dB */ + regmap_write(priv->regmap, MT6359_ZCD_CON2, DL_GAIN_N_22DB_REG); + break; + case SND_SOC_DAPM_POST_PMD: + /* Set HPL/HPR gain to mute */ + regmap_write(priv->regmap, MT6359_ZCD_CON2, DL_GAIN_N_40DB_REG); + break; + default: + break; + } + + return 0; +} + +static int mt_hp_damp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + /* Disable HP damping circuit & HPN 4K load */ + /* reset CMFB PW level */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x0000); + break; + default: + break; + } + + return 0; +} + +static int mt_esd_resist_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Reduce ESD resistance of AU_REFN */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2, + RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT, + 0x1 << RG_AUDREFN_DERES_EN_VAUDP32_SFT); + usleep_range(250, 270); + break; + case SND_SOC_DAPM_POST_PMD: + /* Increase ESD resistance of AU_REFN */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2, + RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT, 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_sdm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* sdm audio fifo clock power on */ + regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2, + 0xfffd, 0x0006); + /* scrambler clock on enable */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba1); + /* sdm power on */ + regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2, + 0xfffd, 0x0003); + /* sdm fifo enable */ + regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2, + 0xfffd, 0x000B); + break; + case SND_SOC_DAPM_POST_PMD: + /* DL scrambler disabling sequence */ + regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2, + 0xfffd, 0x0000); + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba0); + break; + default: + break; + } + + return 0; +} + +static int mt_sdm_3rd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* sdm audio fifo clock power on */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0006); + /* scrambler clock on enable */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON9, 0xcba1); + /* sdm power on */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0003); + /* sdm fifo enable */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x000b); + break; + case SND_SOC_DAPM_POST_PMD: + /* DL scrambler disabling sequence */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0000); + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON9, 0xcba0); + break; + default: + break; + } + + return 0; +} + +static int mt_ncp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_write(priv->regmap, MT6359_AFE_NCP_CFG0, 0xc800); + break; + default: + break; + } + + return 0; +} + +/* DAPM Widgets */ +static const struct snd_soc_dapm_widget mt6359_dapm_widgets[] = { + /* Global Supply*/ + SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF, + MT6359_DCXO_CW12, + RG_XO_AUDIO_EN_M_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB, + MT6359_AUDDEC_ANA_CON13, + RG_AUDGLB_PWRDN_VA32_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ, + MT6359_AUDENC_ANA_CON23, + RG_CLKSQ_EN_SFT, 0, NULL, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK, + MT6359_AUD_TOP_CKPDN_CON0, + RG_AUDNCP_CK_PDN_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK, + MT6359_AUD_TOP_CKPDN_CON0, + RG_ZCD13M_CK_PDN_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST, + MT6359_AUD_TOP_CKPDN_CON0, + RG_AUD_CK_PDN_SFT, 1, mt_delay_250_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY_S("AUDIF_CK", SUPPLY_SEQ_TOP_CK, + MT6359_AUD_TOP_CKPDN_CON0, + RG_AUDIF_CK_PDN_SFT, 1, NULL, 0), + SND_SOC_DAPM_REGULATOR_SUPPLY("vaud18", 0, 0), + + /* Digital Clock */ + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST, + MT6359_AUDIO_TOP_CON0, + PDN_AFE_CTL_SFT, 1, + mt_delay_250_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_DAC_CTL", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_DAC_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_ADC_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADDA6_ADC_CTL", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_ADDA6_ADC_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_I2S_DL_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PWR_CLK_DIS_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_RESERVED_SFT, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("SDM", SUPPLY_SEQ_DL_SDM, + SND_SOC_NOPM, 0, 0, + mt_sdm_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("SDM_3RD", SUPPLY_SEQ_DL_SDM, + SND_SOC_NOPM, 0, 0, + mt_sdm_3rd_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* ch123 share SDM FIFO CLK */ + SND_SOC_DAPM_SUPPLY_S("SDM_FIFO_CLK", SUPPLY_SEQ_DL_SDM_FIFO_CLK, + MT6359_AFUNC_AUD_CON2, + CCI_AFIFO_CLK_PWDB_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("NCP", SUPPLY_SEQ_DL_NCP, + MT6359_AFE_NCP_CFG0, + RG_NCP_ON_SFT, 0, + mt_ncp_event, + SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DL Digital Clock CH_1_2", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DL Digital Clock CH_3", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* AFE ON */ + SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE, + MT6359_AFE_UL_DL_CON0, AFE_ON_SFT, 0, + NULL, 0), + + /* AIF Rx*/ + SND_SOC_DAPM_AIF_IN("AIF_RX", "AIF1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("AIF2_RX", "AIF2 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY_S("AFE_DL_SRC", SUPPLY_SEQ_DL_SRC, + MT6359_AFE_DL_SRC2_CON0_L, + DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, + NULL, 0), + + /* DL Supply */ + SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM, + 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("ESD_RESIST", SUPPLY_SEQ_DL_ESD_RESIST, + SND_SOC_NOPM, + 0, 0, + mt_esd_resist_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("LDO", SUPPLY_SEQ_DL_LDO, + MT6359_AUDDEC_ANA_CON14, + RG_LCLDO_DEC_EN_VA32_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO_REMOTE", SUPPLY_SEQ_DL_LDO_REMOTE_SENSE, + MT6359_AUDDEC_ANA_CON14, + RG_LCLDO_DEC_REMOTE_SENSE_VA18_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("NV_REGULATOR", SUPPLY_SEQ_DL_NV, + MT6359_AUDDEC_ANA_CON14, + RG_NVREG_EN_VAUDP32_SFT, 0, + mt_delay_100_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY_S("IBIST", SUPPLY_SEQ_DL_IBIST, + MT6359_AUDDEC_ANA_CON12, + RG_AUDIBIASPWRDN_VAUDP32_SFT, 1, + NULL, 0), + + /* DAC */ + SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control), + + SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DAC_3RD", NULL, SND_SOC_NOPM, 0, 0), + + /* Headphone */ + SND_SOC_DAPM_MUX_E("HP Mux", SND_SOC_NOPM, 0, 0, + &hp_in_mux_control, + mt_hp_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("HP_Supply", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("HP_PULL_DOWN", SUPPLY_SEQ_HP_PULL_DOWN, + SND_SOC_NOPM, + 0, 0, + mt_hp_pull_down_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("HP_MUTE", SUPPLY_SEQ_HP_MUTE, + SND_SOC_NOPM, + 0, 0, + mt_hp_mute_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("HP_DAMP", SUPPLY_SEQ_HP_DAMPING_OFF_RESET_CMFB, + SND_SOC_NOPM, + 0, 0, + mt_hp_damp_event, + SND_SOC_DAPM_POST_PMD), + + /* Receiver */ + SND_SOC_DAPM_MUX_E("RCV Mux", SND_SOC_NOPM, 0, 0, + &rcv_in_mux_control, + mt_rcv_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + /* LOL */ + SND_SOC_DAPM_MUX_E("LOL Mux", SND_SOC_NOPM, 0, 0, + &lo_in_mux_control, + mt_lo_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("Receiver"), + SND_SOC_DAPM_OUTPUT("Headphone L"), + SND_SOC_DAPM_OUTPUT("Headphone R"), + SND_SOC_DAPM_OUTPUT("Headphone L Ext Spk Amp"), + SND_SOC_DAPM_OUTPUT("Headphone R Ext Spk Amp"), + SND_SOC_DAPM_OUTPUT("LINEOUT L"), + + /* SGEN */ + SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6359_AFE_SGEN_CFG0, + SGEN_DAC_EN_CTL_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6359_AFE_SGEN_CFG0, + SGEN_MUTE_SW_CTL_SFT, 1, + mt_sgen_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6359_AFE_DL_SRC2_CON0_L, + DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("SGEN DL"), + + /* Uplinks */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY_S("ADC_CLKGEN", SUPPLY_SEQ_ADC_CLKGEN, + SND_SOC_NOPM, 0, 0, + mt_adc_clk_gen_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY_S("DCC_CLK", SUPPLY_SEQ_DCC_CLK, + SND_SOC_NOPM, 0, 0, + mt_dcc_clk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Uplinks MUX */ + SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0, + &aif_out_mux_control), + + SND_SOC_DAPM_MUX("AIF2 Out Mux", SND_SOC_NOPM, 0, 0, + &aif2_out_mux_control), + + SND_SOC_DAPM_SUPPLY("AIFTX_Supply", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("MTKAIF_TX", SUPPLY_SEQ_UL_MTKAIF, + SND_SOC_NOPM, 0, 0, + mt_mtkaif_tx_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("UL_SRC", SUPPLY_SEQ_UL_SRC, + MT6359_AFE_UL_SRC_CON0_L, + UL_SRC_ON_TMP_CTL_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("UL_SRC_DMIC", SUPPLY_SEQ_UL_SRC_DMIC, + SND_SOC_NOPM, 0, 0, + mt_ul_src_dmic_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("UL_SRC_34", SUPPLY_SEQ_UL_SRC, + MT6359_AFE_ADDA6_UL_SRC_CON0_L, + ADDA6_UL_SRC_ON_TMP_CTL_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("UL_SRC_34_DMIC", SUPPLY_SEQ_UL_SRC_DMIC, + SND_SOC_NOPM, 0, 0, + mt_ul_src_34_dmic_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("MISO0_MUX", SND_SOC_NOPM, 0, 0, &miso0_mux_control), + SND_SOC_DAPM_MUX("MISO1_MUX", SND_SOC_NOPM, 0, 0, &miso1_mux_control), + SND_SOC_DAPM_MUX("MISO2_MUX", SND_SOC_NOPM, 0, 0, &miso2_mux_control), + + SND_SOC_DAPM_MUX("UL_SRC_MUX", SND_SOC_NOPM, 0, 0, + &ul_src_mux_control), + SND_SOC_DAPM_MUX("UL2_SRC_MUX", SND_SOC_NOPM, 0, 0, + &ul2_src_mux_control), + + SND_SOC_DAPM_MUX("DMIC0_MUX", SND_SOC_NOPM, 0, 0, &dmic0_mux_control), + SND_SOC_DAPM_MUX("DMIC1_MUX", SND_SOC_NOPM, 0, 0, &dmic1_mux_control), + SND_SOC_DAPM_MUX("DMIC2_MUX", SND_SOC_NOPM, 0, 0, &dmic2_mux_control), + + SND_SOC_DAPM_MUX_E("ADC_L_Mux", SND_SOC_NOPM, 0, 0, + &adc_left_mux_control, NULL, 0), + SND_SOC_DAPM_MUX_E("ADC_R_Mux", SND_SOC_NOPM, 0, 0, + &adc_right_mux_control, NULL, 0), + SND_SOC_DAPM_MUX_E("ADC_3_Mux", SND_SOC_NOPM, 0, 0, + &adc_3_mux_control, NULL, 0), + + SND_SOC_DAPM_ADC("ADC_L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC_R", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC_3", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY_S("ADC_L_EN", SUPPLY_SEQ_UL_ADC, + MT6359_AUDENC_ANA_CON0, + RG_AUDADCLPWRUP_SFT, 0, + mt_adc_l_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY_S("ADC_R_EN", SUPPLY_SEQ_UL_ADC, + MT6359_AUDENC_ANA_CON1, + RG_AUDADCRPWRUP_SFT, 0, + mt_adc_r_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY_S("ADC_3_EN", SUPPLY_SEQ_UL_ADC, + MT6359_AUDENC_ANA_CON2, + RG_AUDADC3PWRUP_SFT, 0, + mt_adc_3_event, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("PGA_L_Mux", SND_SOC_NOPM, 0, 0, + &pga_left_mux_control, + mt_pga_l_mux_event, + SND_SOC_DAPM_WILL_PMU), + SND_SOC_DAPM_MUX_E("PGA_R_Mux", SND_SOC_NOPM, 0, 0, + &pga_right_mux_control, + mt_pga_r_mux_event, + SND_SOC_DAPM_WILL_PMU), + SND_SOC_DAPM_MUX_E("PGA_3_Mux", SND_SOC_NOPM, 0, 0, + &pga_3_mux_control, + mt_pga_3_mux_event, + SND_SOC_DAPM_WILL_PMU), + + SND_SOC_DAPM_PGA("PGA_L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA_R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA_3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("PGA_L_EN", SUPPLY_SEQ_UL_PGA, + MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLON_SFT, 0, + mt_pga_l_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("PGA_R_EN", SUPPLY_SEQ_UL_PGA, + MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRON_SFT, 0, + mt_pga_r_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("PGA_3_EN", SUPPLY_SEQ_UL_PGA, + MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3ON_SFT, 0, + mt_pga_3_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* UL input */ + SND_SOC_DAPM_INPUT("AIN0"), + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + + SND_SOC_DAPM_INPUT("AIN0_DMIC"), + SND_SOC_DAPM_INPUT("AIN2_DMIC"), + SND_SOC_DAPM_INPUT("AIN3_DMIC"), + + /* mic bias */ + SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_0", SUPPLY_SEQ_MIC_BIAS, + MT6359_AUDENC_ANA_CON15, + RG_AUDPWDBMICBIAS0_SFT, 0, + mt_mic_bias_0_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_1", SUPPLY_SEQ_MIC_BIAS, + MT6359_AUDENC_ANA_CON16, + RG_AUDPWDBMICBIAS1_SFT, 0, + mt_mic_bias_1_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_2", SUPPLY_SEQ_MIC_BIAS, + MT6359_AUDENC_ANA_CON17, + RG_AUDPWDBMICBIAS2_SFT, 0, + mt_mic_bias_2_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* dmic */ + SND_SOC_DAPM_SUPPLY_S("DMIC_0", SUPPLY_SEQ_DMIC, + MT6359_AUDENC_ANA_CON13, + RG_AUDDIGMICEN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC_1", SUPPLY_SEQ_DMIC, + MT6359_AUDENC_ANA_CON14, + RG_AUDDIGMIC1EN_SFT, 0, + NULL, 0), +}; + +static int mt_dcc_clk_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + if (IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_0]) || + IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_1]) || + IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_2])) + return 1; + else + return 0; +} + +static const struct snd_soc_dapm_route mt6359_dapm_routes[] = { + /* Capture */ + {"AIFTX_Supply", NULL, "CLK_BUF"}, + {"AIFTX_Supply", NULL, "vaud18"}, + {"AIFTX_Supply", NULL, "AUDGLB"}, + {"AIFTX_Supply", NULL, "CLKSQ Audio"}, + {"AIFTX_Supply", NULL, "AUD_CK"}, + {"AIFTX_Supply", NULL, "AUDIF_CK"}, + {"AIFTX_Supply", NULL, "AUDIO_TOP_AFE_CTL"}, + {"AIFTX_Supply", NULL, "AUDIO_TOP_PWR_CLK"}, + {"AIFTX_Supply", NULL, "AUDIO_TOP_PDN_RESERVED"}, + {"AIFTX_Supply", NULL, "AUDIO_TOP_I2S_DL"}, + /* + * *_ADC_CTL should enable only if UL_SRC in use, + * but dm ck may be needed even UL_SRC_x not in use + */ + {"AIFTX_Supply", NULL, "AUDIO_TOP_ADC_CTL"}, + {"AIFTX_Supply", NULL, "AUDIO_TOP_ADDA6_ADC_CTL"}, + {"AIFTX_Supply", NULL, "AFE_ON"}, + + /* ul ch 12 */ + {"AIF1TX", NULL, "AIF Out Mux"}, + {"AIF1TX", NULL, "AIFTX_Supply"}, + {"AIF1TX", NULL, "MTKAIF_TX"}, + + {"AIF2TX", NULL, "AIF2 Out Mux"}, + {"AIF2TX", NULL, "AIFTX_Supply"}, + {"AIF2TX", NULL, "MTKAIF_TX"}, + + {"AIF Out Mux", "Normal Path", "MISO0_MUX"}, + {"AIF Out Mux", "Normal Path", "MISO1_MUX"}, + {"AIF2 Out Mux", "Normal Path", "MISO2_MUX"}, + + {"MISO0_MUX", "UL1_CH1", "UL_SRC_MUX"}, + {"MISO0_MUX", "UL1_CH2", "UL_SRC_MUX"}, + {"MISO0_MUX", "UL2_CH1", "UL2_SRC_MUX"}, + {"MISO0_MUX", "UL2_CH2", "UL2_SRC_MUX"}, + + {"MISO1_MUX", "UL1_CH1", "UL_SRC_MUX"}, + {"MISO1_MUX", "UL1_CH2", "UL_SRC_MUX"}, + {"MISO1_MUX", "UL2_CH1", "UL2_SRC_MUX"}, + {"MISO1_MUX", "UL2_CH2", "UL2_SRC_MUX"}, + + {"MISO2_MUX", "UL1_CH1", "UL_SRC_MUX"}, + {"MISO2_MUX", "UL1_CH2", "UL_SRC_MUX"}, + {"MISO2_MUX", "UL2_CH1", "UL2_SRC_MUX"}, + {"MISO2_MUX", "UL2_CH2", "UL2_SRC_MUX"}, + + {"MISO0_MUX", NULL, "UL_SRC"}, + {"MISO1_MUX", NULL, "UL_SRC"}, + {"MISO2_MUX", NULL, "UL_SRC_34"}, + + {"UL_SRC_MUX", "AMIC", "ADC_L"}, + {"UL_SRC_MUX", "AMIC", "ADC_R"}, + {"UL_SRC_MUX", "DMIC", "DMIC0_MUX"}, + {"UL_SRC_MUX", "DMIC", "DMIC1_MUX"}, + {"UL_SRC_MUX", NULL, "UL_SRC"}, + + {"UL2_SRC_MUX", "AMIC", "ADC_3"}, + {"UL2_SRC_MUX", "DMIC", "DMIC2_MUX"}, + {"UL2_SRC_MUX", NULL, "UL_SRC_34"}, + + {"DMIC0_MUX", "DMIC_DATA0", "AIN0_DMIC"}, + {"DMIC0_MUX", "DMIC_DATA1_L", "AIN2_DMIC"}, + {"DMIC0_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"}, + {"DMIC0_MUX", "DMIC_DATA1_R", "AIN3_DMIC"}, + {"DMIC1_MUX", "DMIC_DATA0", "AIN0_DMIC"}, + {"DMIC1_MUX", "DMIC_DATA1_L", "AIN2_DMIC"}, + {"DMIC1_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"}, + {"DMIC1_MUX", "DMIC_DATA1_R", "AIN3_DMIC"}, + {"DMIC2_MUX", "DMIC_DATA0", "AIN0_DMIC"}, + {"DMIC2_MUX", "DMIC_DATA1_L", "AIN2_DMIC"}, + {"DMIC2_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"}, + {"DMIC2_MUX", "DMIC_DATA1_R", "AIN3_DMIC"}, + + {"DMIC0_MUX", NULL, "UL_SRC_DMIC"}, + {"DMIC1_MUX", NULL, "UL_SRC_DMIC"}, + {"DMIC2_MUX", NULL, "UL_SRC_34_DMIC"}, + + {"AIN0_DMIC", NULL, "DMIC_0"}, + {"AIN2_DMIC", NULL, "DMIC_1"}, + {"AIN3_DMIC", NULL, "DMIC_1"}, + {"AIN0_DMIC", NULL, "MIC_BIAS_0"}, + {"AIN2_DMIC", NULL, "MIC_BIAS_2"}, + {"AIN3_DMIC", NULL, "MIC_BIAS_2"}, + + /* adc */ + {"ADC_L", NULL, "ADC_L_Mux"}, + {"ADC_L", NULL, "ADC_CLKGEN"}, + {"ADC_L", NULL, "ADC_L_EN"}, + {"ADC_R", NULL, "ADC_R_Mux"}, + {"ADC_R", NULL, "ADC_CLKGEN"}, + {"ADC_R", NULL, "ADC_R_EN"}, + /* + * amic fifo ch1/2 clk from ADC_L, + * enable ADC_L even use ADC_R only + */ + {"ADC_R", NULL, "ADC_L_EN"}, + {"ADC_3", NULL, "ADC_3_Mux"}, + {"ADC_3", NULL, "ADC_CLKGEN"}, + {"ADC_3", NULL, "ADC_3_EN"}, + + {"ADC_L_Mux", "Left Preamplifier", "PGA_L"}, + {"ADC_R_Mux", "Right Preamplifier", "PGA_R"}, + {"ADC_3_Mux", "Preamplifier", "PGA_3"}, + + {"PGA_L", NULL, "PGA_L_Mux"}, + {"PGA_L", NULL, "PGA_L_EN"}, + {"PGA_R", NULL, "PGA_R_Mux"}, + {"PGA_R", NULL, "PGA_R_EN"}, + {"PGA_3", NULL, "PGA_3_Mux"}, + {"PGA_3", NULL, "PGA_3_EN"}, + + {"PGA_L", NULL, "DCC_CLK", mt_dcc_clk_connect}, + {"PGA_R", NULL, "DCC_CLK", mt_dcc_clk_connect}, + {"PGA_3", NULL, "DCC_CLK", mt_dcc_clk_connect}, + + {"PGA_L_Mux", "AIN0", "AIN0"}, + {"PGA_L_Mux", "AIN1", "AIN1"}, + + {"PGA_R_Mux", "AIN0", "AIN0"}, + {"PGA_R_Mux", "AIN2", "AIN2"}, + {"PGA_R_Mux", "AIN3", "AIN3"}, + + {"PGA_3_Mux", "AIN2", "AIN2"}, + {"PGA_3_Mux", "AIN3", "AIN3"}, + + {"AIN0", NULL, "MIC_BIAS_0"}, + {"AIN1", NULL, "MIC_BIAS_1"}, + {"AIN2", NULL, "MIC_BIAS_0"}, + {"AIN2", NULL, "MIC_BIAS_2"}, + {"AIN3", NULL, "MIC_BIAS_2"}, + + /* DL Supply */ + {"DL Power Supply", NULL, "CLK_BUF"}, + {"DL Power Supply", NULL, "vaud18"}, + {"DL Power Supply", NULL, "AUDGLB"}, + {"DL Power Supply", NULL, "CLKSQ Audio"}, + {"DL Power Supply", NULL, "AUDNCP_CK"}, + {"DL Power Supply", NULL, "ZCD13M_CK"}, + {"DL Power Supply", NULL, "AUD_CK"}, + {"DL Power Supply", NULL, "AUDIF_CK"}, + {"DL Power Supply", NULL, "ESD_RESIST"}, + {"DL Power Supply", NULL, "LDO"}, + {"DL Power Supply", NULL, "LDO_REMOTE"}, + {"DL Power Supply", NULL, "NV_REGULATOR"}, + {"DL Power Supply", NULL, "IBIST"}, + + /* DL Digital Supply */ + {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_PDN_RESERVED"}, + {"DL Digital Clock", NULL, "SDM_FIFO_CLK"}, + {"DL Digital Clock", NULL, "NCP"}, + {"DL Digital Clock", NULL, "AFE_ON"}, + {"DL Digital Clock", NULL, "AFE_DL_SRC"}, + + {"DL Digital Clock CH_1_2", NULL, "DL Digital Clock"}, + {"DL Digital Clock CH_1_2", NULL, "SDM"}, + + {"DL Digital Clock CH_3", NULL, "DL Digital Clock"}, + {"DL Digital Clock CH_3", NULL, "SDM_3RD"}, + + {"AIF_RX", NULL, "DL Digital Clock CH_1_2"}, + + {"AIF2_RX", NULL, "DL Digital Clock CH_3"}, + + /* DL Path */ + {"DAC In Mux", "Normal Path", "AIF_RX"}, + {"DAC In Mux", "Sgen", "SGEN DL"}, + {"SGEN DL", NULL, "SGEN DL SRC"}, + {"SGEN DL", NULL, "SGEN MUTE"}, + {"SGEN DL", NULL, "SGEN DL Enable"}, + {"SGEN DL", NULL, "DL Digital Clock CH_1_2"}, + {"SGEN DL", NULL, "DL Digital Clock CH_3"}, + {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"}, + + {"DACL", NULL, "DAC In Mux"}, + {"DACL", NULL, "DL Power Supply"}, + + {"DACR", NULL, "DAC In Mux"}, + {"DACR", NULL, "DL Power Supply"}, + + /* DAC 3RD */ + {"DAC In Mux", "Normal Path", "AIF2_RX"}, + {"DAC_3RD", NULL, "DAC In Mux"}, + {"DAC_3RD", NULL, "DL Power Supply"}, + + /* Lineout Path */ + {"LOL Mux", "Playback", "DAC_3RD"}, + {"LOL Mux", "Playback_L_DAC", "DACL"}, + {"LINEOUT L", NULL, "LOL Mux"}, + + /* Headphone Path */ + {"HP_Supply", NULL, "HP_PULL_DOWN"}, + {"HP_Supply", NULL, "HP_MUTE"}, + {"HP_Supply", NULL, "HP_DAMP"}, + {"HP Mux", NULL, "HP_Supply"}, + + {"HP Mux", "Audio Playback", "DACL"}, + {"HP Mux", "Audio Playback", "DACR"}, + {"HP Mux", "HP Impedance", "DACL"}, + {"HP Mux", "HP Impedance", "DACR"}, + {"HP Mux", "LoudSPK Playback", "DACL"}, + {"HP Mux", "LoudSPK Playback", "DACR"}, + + {"Headphone L", NULL, "HP Mux"}, + {"Headphone R", NULL, "HP Mux"}, + {"Headphone L Ext Spk Amp", NULL, "HP Mux"}, + {"Headphone R Ext Spk Amp", NULL, "HP Mux"}, + + /* Receiver Path */ + {"RCV Mux", "Voice Playback", "DACL"}, + {"Receiver", NULL, "RCV Mux"}, +}; + +static int mt6359_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *cmpnt = dai->component; + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int rate = params_rate(params); + int id = dai->id; + + dev_dbg(priv->dev, "%s(), id %d, substream->stream %d, rate %d, number %d\n", + __func__, id, substream->stream, rate, substream->number); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + priv->dl_rate[id] = rate; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + priv->ul_rate[id] = rate; + + return 0; +} + +static int mt6359_codec_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *cmpnt = dai->component; + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s stream %d\n", __func__, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mt6359_set_playback_gpio(priv); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + mt6359_set_capture_gpio(priv); + + return 0; +} + +static void mt6359_codec_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *cmpnt = dai->component; + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s stream %d\n", __func__, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mt6359_reset_playback_gpio(priv); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + mt6359_reset_capture_gpio(priv); +} + +static const struct snd_soc_dai_ops mt6359_codec_dai_ops = { + .hw_params = mt6359_codec_dai_hw_params, + .startup = mt6359_codec_dai_startup, + .shutdown = mt6359_codec_dai_shutdown, +}; + +#define MT6359_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) + +static struct snd_soc_dai_driver mt6359_dai_driver[] = { + { + .id = MT6359_AIF_1, + .name = "mt6359-snd-codec-aif1", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6359_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6359_FORMATS, + }, + .ops = &mt6359_codec_dai_ops, + }, + { + .id = MT6359_AIF_2, + .name = "mt6359-snd-codec-aif2", + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6359_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000, + .formats = MT6359_FORMATS, + }, + .ops = &mt6359_codec_dai_ops, + }, +}; + +static int mt6359_codec_init_reg(struct snd_soc_component *cmpnt) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + /* enable clk buf */ + regmap_update_bits(priv->regmap, MT6359_DCXO_CW12, + 0x1 << RG_XO_AUDIO_EN_M_SFT, + 0x1 << RG_XO_AUDIO_EN_M_SFT); + + /* set those not controlled by dapm widget */ + + /* audio clk source from internal dcxo */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON23, + RG_CLKSQ_IN_SEL_TEST_MASK_SFT, + 0x0); + + /* Disable HeadphoneL/HeadphoneR short circuit protection */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + RG_AUDHPLSCDISABLE_VAUDP32_MASK_SFT, + 0x1 << RG_AUDHPLSCDISABLE_VAUDP32_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + RG_AUDHPRSCDISABLE_VAUDP32_MASK_SFT, + 0x1 << RG_AUDHPRSCDISABLE_VAUDP32_SFT); + /* Disable voice short circuit protection */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6, + RG_AUDHSSCDISABLE_VAUDP32_MASK_SFT, + 0x1 << RG_AUDHSSCDISABLE_VAUDP32_SFT); + /* disable LO buffer left short circuit protection */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7, + RG_AUDLOLSCDISABLE_VAUDP32_MASK_SFT, + 0x1 << RG_AUDLOLSCDISABLE_VAUDP32_SFT); + + /* set gpio */ + mt6359_set_gpio_smt(priv); + mt6359_set_gpio_driving(priv); + mt6359_reset_playback_gpio(priv); + mt6359_reset_capture_gpio(priv); + + /* hp hifi mode, default normal mode */ + priv->hp_hifi_mode = 0; + + /* Disable AUD_ZCD */ + zcd_disable(priv); + + /* disable clk buf */ + regmap_update_bits(priv->regmap, MT6359_DCXO_CW12, + 0x1 << RG_XO_AUDIO_EN_M_SFT, + 0x0 << RG_XO_AUDIO_EN_M_SFT); + + return 0; +} + +static int mt6359_codec_probe(struct snd_soc_component *cmpnt) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + snd_soc_component_init_regmap(cmpnt, priv->regmap); + + return mt6359_codec_init_reg(cmpnt); +} + +static void mt6359_codec_remove(struct snd_soc_component *cmpnt) +{ + cmpnt->regmap = NULL; +} + +static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); +static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new mt6359_snd_controls[] = { + /* dl pga gain */ + SOC_DOUBLE_EXT_TLV("Headset Volume", + MT6359_ZCD_CON2, 0, 7, 0x12, 0, + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), + SOC_DOUBLE_EXT_TLV("Lineout Volume", + MT6359_ZCD_CON1, 0, 7, 0x12, 0, + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), + SOC_SINGLE_EXT_TLV("Handset Volume", + MT6359_ZCD_CON3, 0, 0x12, 0, + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), + + /* ul pga gain */ + SOC_SINGLE_EXT_TLV("PGA1 Volume", + MT6359_AUDENC_ANA_CON0, RG_AUDPREAMPLGAIN_SFT, 4, 0, + snd_soc_get_volsw, mt6359_put_volsw, capture_tlv), + SOC_SINGLE_EXT_TLV("PGA2 Volume", + MT6359_AUDENC_ANA_CON1, RG_AUDPREAMPRGAIN_SFT, 4, 0, + snd_soc_get_volsw, mt6359_put_volsw, capture_tlv), + SOC_SINGLE_EXT_TLV("PGA3 Volume", + MT6359_AUDENC_ANA_CON2, RG_AUDPREAMP3GAIN_SFT, 4, 0, + snd_soc_get_volsw, mt6359_put_volsw, capture_tlv), +}; + +static const struct snd_soc_component_driver mt6359_soc_component_driver = { + .name = CODEC_MT6359_NAME, + .probe = mt6359_codec_probe, + .remove = mt6359_codec_remove, + .controls = mt6359_snd_controls, + .num_controls = ARRAY_SIZE(mt6359_snd_controls), + .dapm_widgets = mt6359_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt6359_dapm_widgets), + .dapm_routes = mt6359_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mt6359_dapm_routes), + .endianness = 1, +}; + +static int mt6359_parse_dt(struct mt6359_priv *priv) +{ + int ret; + struct device *dev = priv->dev; + struct device_node *np; + + np = of_get_child_by_name(dev->parent->of_node, "mt6359codec"); + if (!np) + return -EINVAL; + + ret = of_property_read_u32(np, "mediatek,dmic-mode", + &priv->dmic_one_wire_mode); + if (ret) { + dev_info(priv->dev, + "%s() failed to read dmic-mode, use default (0)\n", + __func__); + priv->dmic_one_wire_mode = 0; + } + + ret = of_property_read_u32(np, "mediatek,mic-type-0", + &priv->mux_select[MUX_MIC_TYPE_0]); + if (ret) { + dev_info(priv->dev, + "%s() failed to read mic-type-0, use default (%d)\n", + __func__, MIC_TYPE_MUX_IDLE); + priv->mux_select[MUX_MIC_TYPE_0] = MIC_TYPE_MUX_IDLE; + } + + ret = of_property_read_u32(np, "mediatek,mic-type-1", + &priv->mux_select[MUX_MIC_TYPE_1]); + if (ret) { + dev_info(priv->dev, + "%s() failed to read mic-type-1, use default (%d)\n", + __func__, MIC_TYPE_MUX_IDLE); + priv->mux_select[MUX_MIC_TYPE_1] = MIC_TYPE_MUX_IDLE; + } + + ret = of_property_read_u32(np, "mediatek,mic-type-2", + &priv->mux_select[MUX_MIC_TYPE_2]); + of_node_put(np); + if (ret) { + dev_info(priv->dev, + "%s() failed to read mic-type-2, use default (%d)\n", + __func__, MIC_TYPE_MUX_IDLE); + priv->mux_select[MUX_MIC_TYPE_2] = MIC_TYPE_MUX_IDLE; + } + + return 0; +} + +static int mt6359_platform_driver_probe(struct platform_device *pdev) +{ + struct mt6359_priv *priv; + int ret; + struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); + + dev_dbg(&pdev->dev, "%s(), dev name %s\n", + __func__, dev_name(&pdev->dev)); + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = mt6397->regmap; + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + dev_set_drvdata(&pdev->dev, priv); + priv->dev = &pdev->dev; + + ret = mt6359_parse_dt(priv); + if (ret) { + dev_warn(&pdev->dev, "%s() failed to parse dts\n", __func__); + return ret; + } + + return devm_snd_soc_register_component(&pdev->dev, + &mt6359_soc_component_driver, + mt6359_dai_driver, + ARRAY_SIZE(mt6359_dai_driver)); +} + +static struct platform_driver mt6359_platform_driver = { + .driver = { + .name = "mt6359-sound", + }, + .probe = mt6359_platform_driver_probe, +}; + +module_platform_driver(mt6359_platform_driver) + +/* Module information */ +MODULE_DESCRIPTION("MT6359 ALSA SoC codec driver"); +MODULE_AUTHOR("KaiChieh Chuang "); +MODULE_AUTHOR("Eason Yen "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/mt6359.h b/sound/soc/codecs/mt6359.h new file mode 100644 index 0000000000..296ffa7f50 --- /dev/null +++ b/sound/soc/codecs/mt6359.h @@ -0,0 +1,4289 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. + * Author: Argus Lin + */ + +#ifndef _MT6359_H_ +#define _MT6359_H_ + +/*************Register Bit Define*************/ +#define MT6359_TOP0_ID 0x0 +#define MT6359_SMT_CON1 0x32 +#define MT6359_DRV_CON2 0x3c +#define MT6359_DRV_CON3 0x3e +#define MT6359_DRV_CON4 0x40 +#define MT6359_TOP_CKPDN_CON0 0x10c +#define MT6359_TOP_CKPDN_CON0_SET 0x10e +#define MT6359_TOP_CKPDN_CON0_CLR 0x110 +#define MT6359_AUXADC_RQST0 0x1108 +#define MT6359_AUXADC_CON10 0x11a0 +#define MT6359_AUXADC_ACCDET 0x11ba +#define MT6359_LDO_VUSB_OP_EN 0x1d0c +#define MT6359_LDO_VUSB_OP_EN_SET 0x1d0e +#define MT6359_LDO_VUSB_OP_EN_CLR 0x1d10 +#define MT6359_AUD_TOP_CKPDN_CON0 0x230c +#define MT6359_AUD_TOP_CKPDN_CON0_SET 0x230e +#define MT6359_AUD_TOP_CKPDN_CON0_CLR 0x2310 +#define MT6359_AUD_TOP_RST_CON0 0x2320 +#define MT6359_AUD_TOP_RST_CON0_SET 0x2322 +#define MT6359_AUD_TOP_RST_CON0_CLR 0x2324 +#define MT6359_AUD_TOP_INT_CON0 0x2328 +#define MT6359_AUD_TOP_INT_CON0_SET 0x232a +#define MT6359_AUD_TOP_INT_CON0_CLR 0x232c +#define MT6359_AUD_TOP_INT_MASK_CON0 0x232e +#define MT6359_AUD_TOP_INT_MASK_CON0_SET 0x2330 +#define MT6359_AUD_TOP_INT_MASK_CON0_CLR 0x2332 +#define MT6359_AUD_TOP_INT_STATUS0 0x2334 +#define MT6359_AFE_NCP_CFG2 0x24e2 +#define MT6359_AUDENC_DSN_ID 0x2500 +#define MT6359_AUDENC_DSN_REV0 0x2502 +#define MT6359_AUDENC_DSN_DBI 0x2504 +#define MT6359_AUDENC_DSN_FPI 0x2506 +#define MT6359_AUDENC_ANA_CON0 0x2508 +#define MT6359_AUDENC_ANA_CON1 0x250a +#define MT6359_AUDENC_ANA_CON2 0x250c +#define MT6359_AUDENC_ANA_CON3 0x250e +#define MT6359_AUDENC_ANA_CON4 0x2510 +#define MT6359_AUDENC_ANA_CON5 0x2512 +#define MT6359_AUDENC_ANA_CON6 0x2514 +#define MT6359_AUDENC_ANA_CON7 0x2516 +#define MT6359_AUDENC_ANA_CON8 0x2518 +#define MT6359_AUDENC_ANA_CON9 0x251a +#define MT6359_AUDENC_ANA_CON10 0x251c +#define MT6359_AUDENC_ANA_CON11 0x251e +#define MT6359_AUDENC_ANA_CON12 0x2520 +#define MT6359_AUDENC_ANA_CON13 0x2522 +#define MT6359_AUDENC_ANA_CON14 0x2524 +#define MT6359_AUDENC_ANA_CON15 0x2526 +#define MT6359_AUDENC_ANA_CON16 0x2528 +#define MT6359_AUDENC_ANA_CON17 0x252a +#define MT6359_AUDENC_ANA_CON18 0x252c +#define MT6359_AUDENC_ANA_CON19 0x252e +#define MT6359_AUDENC_ANA_CON20 0x2530 +#define MT6359_AUDENC_ANA_CON21 0x2532 +#define MT6359_AUDENC_ANA_CON22 0x2534 +#define MT6359_AUDENC_ANA_CON23 0x2536 +#define MT6359_AUDDEC_DSN_ID 0x2580 +#define MT6359_AUDDEC_DSN_REV0 0x2582 +#define MT6359_AUDDEC_DSN_DBI 0x2584 +#define MT6359_AUDDEC_DSN_FPI 0x2586 +#define MT6359_AUDDEC_ANA_CON0 0x2588 +#define MT6359_AUDDEC_ANA_CON1 0x258a +#define MT6359_AUDDEC_ANA_CON2 0x258c +#define MT6359_AUDDEC_ANA_CON3 0x258e +#define MT6359_AUDDEC_ANA_CON4 0x2590 +#define MT6359_AUDDEC_ANA_CON5 0x2592 +#define MT6359_AUDDEC_ANA_CON6 0x2594 +#define MT6359_AUDDEC_ANA_CON7 0x2596 +#define MT6359_AUDDEC_ANA_CON8 0x2598 +#define MT6359_AUDDEC_ANA_CON9 0x259a +#define MT6359_AUDDEC_ANA_CON10 0x259c +#define MT6359_AUDDEC_ANA_CON11 0x259e +#define MT6359_AUDDEC_ANA_CON12 0x25a0 +#define MT6359_AUDDEC_ANA_CON13 0x25a2 +#define MT6359_AUDDEC_ANA_CON14 0x25a4 +#define MT6359_ACCDET_DSN_DIG_ID 0x2680 +#define MT6359_ACCDET_DSN_DIG_REV0 0x2682 +#define MT6359_ACCDET_DSN_DBI 0x2684 +#define MT6359_ACCDET_DSN_FPI 0x2686 +#define MT6359_ACCDET_CON0 0x2688 +#define MT6359_ACCDET_CON1 0x268a +#define MT6359_ACCDET_CON2 0x268c +#define MT6359_ACCDET_CON3 0x268e +#define MT6359_ACCDET_CON4 0x2690 +#define MT6359_ACCDET_CON5 0x2692 +#define MT6359_ACCDET_CON6 0x2694 +#define MT6359_ACCDET_CON7 0x2696 +#define MT6359_ACCDET_CON8 0x2698 +#define MT6359_ACCDET_CON9 0x269a +#define MT6359_ACCDET_CON10 0x269c +#define MT6359_ACCDET_CON11 0x269e +#define MT6359_ACCDET_CON12 0x26a0 +#define MT6359_ACCDET_CON13 0x26a2 +#define MT6359_ACCDET_CON14 0x26a4 +#define MT6359_ACCDET_CON15 0x26a6 +#define MT6359_ACCDET_CON16 0x26a8 +#define MT6359_ACCDET_CON17 0x26aa +#define MT6359_ACCDET_CON18 0x26ac +#define MT6359_ACCDET_CON19 0x26ae +#define MT6359_ACCDET_CON20 0x26b0 +#define MT6359_ACCDET_CON21 0x26b2 +#define MT6359_ACCDET_CON22 0x26b4 +#define MT6359_ACCDET_CON23 0x26b6 +#define MT6359_ACCDET_CON24 0x26b8 +#define MT6359_ACCDET_CON25 0x26ba +#define MT6359_ACCDET_CON26 0x26bc +#define MT6359_ACCDET_CON27 0x26be +#define MT6359_ACCDET_CON28 0x26c0 +#define MT6359_ACCDET_CON29 0x26c2 +#define MT6359_ACCDET_CON30 0x26c4 +#define MT6359_ACCDET_CON31 0x26c6 +#define MT6359_ACCDET_CON32 0x26c8 +#define MT6359_ACCDET_CON33 0x26ca +#define MT6359_ACCDET_CON34 0x26cc +#define MT6359_ACCDET_CON35 0x26ce +#define MT6359_ACCDET_CON36 0x26d0 +#define MT6359_ACCDET_CON37 0x26d2 +#define MT6359_ACCDET_CON38 0x26d4 +#define MT6359_ACCDET_CON39 0x26d6 +#define MT6359_ACCDET_CON40 0x26d8 + +#define TOP0_ANA_ID_ADDR \ + MT6359_TOP0_ID +#define TOP0_ANA_ID_SFT 0 +#define TOP0_ANA_ID_MASK 0xFF +#define TOP0_ANA_ID_MASK_SFT (0xFF << 0) +#define AUXADC_RQST_CH0_ADDR \ + MT6359_AUXADC_RQST0 +#define AUXADC_RQST_CH0_SFT 0 +#define AUXADC_RQST_CH0_MASK 0x1 +#define AUXADC_RQST_CH0_MASK_SFT (0x1 << 0) +#define AUXADC_ACCDET_ANASWCTRL_EN_ADDR \ + MT6359_AUXADC_CON15 +#define AUXADC_ACCDET_ANASWCTRL_EN_SFT 6 +#define AUXADC_ACCDET_ANASWCTRL_EN_MASK 0x1 +#define AUXADC_ACCDET_ANASWCTRL_EN_MASK_SFT (0x1 << 6) + +#define AUXADC_ACCDET_AUTO_SPL_ADDR \ + MT6359_AUXADC_ACCDET +#define AUXADC_ACCDET_AUTO_SPL_SFT 0 +#define AUXADC_ACCDET_AUTO_SPL_MASK 0x1 +#define AUXADC_ACCDET_AUTO_SPL_MASK_SFT (0x1 << 0) +#define AUXADC_ACCDET_AUTO_RQST_CLR_ADDR \ + MT6359_AUXADC_ACCDET +#define AUXADC_ACCDET_AUTO_RQST_CLR_SFT 1 +#define AUXADC_ACCDET_AUTO_RQST_CLR_MASK 0x1 +#define AUXADC_ACCDET_AUTO_RQST_CLR_MASK_SFT (0x1 << 1) +#define AUXADC_ACCDET_DIG1_RSV0_ADDR \ + MT6359_AUXADC_ACCDET +#define AUXADC_ACCDET_DIG1_RSV0_SFT 2 +#define AUXADC_ACCDET_DIG1_RSV0_MASK 0x3F +#define AUXADC_ACCDET_DIG1_RSV0_MASK_SFT (0x3F << 2) +#define AUXADC_ACCDET_DIG0_RSV0_ADDR \ + MT6359_AUXADC_ACCDET +#define AUXADC_ACCDET_DIG0_RSV0_SFT 8 +#define AUXADC_ACCDET_DIG0_RSV0_MASK 0xFF +#define AUXADC_ACCDET_DIG0_RSV0_MASK_SFT (0xFF << 8) + +#define RG_ACCDET_CK_PDN_ADDR \ + MT6359_AUD_TOP_CKPDN_CON0 +#define RG_ACCDET_CK_PDN_SFT 0 +#define RG_ACCDET_CK_PDN_MASK 0x1 +#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0) + +#define RG_ACCDET_RST_ADDR \ + MT6359_AUD_TOP_RST_CON0 +#define RG_ACCDET_RST_SFT 1 +#define RG_ACCDET_RST_MASK 0x1 +#define RG_ACCDET_RST_MASK_SFT (0x1 << 1) +#define BANK_ACCDET_SWRST_ADDR \ + MT6359_AUD_TOP_RST_BANK_CON0 +#define BANK_ACCDET_SWRST_SFT 0 +#define BANK_ACCDET_SWRST_MASK 0x1 +#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0) + +#define RG_INT_EN_ACCDET_ADDR \ + MT6359_AUD_TOP_INT_CON0 +#define RG_INT_EN_ACCDET_SFT 5 +#define RG_INT_EN_ACCDET_MASK 0x1 +#define RG_INT_EN_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_EN_ACCDET_EINT0_ADDR \ + MT6359_AUD_TOP_INT_CON0 +#define RG_INT_EN_ACCDET_EINT0_SFT 6 +#define RG_INT_EN_ACCDET_EINT0_MASK 0x1 +#define RG_INT_EN_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_EN_ACCDET_EINT1_ADDR \ + MT6359_AUD_TOP_INT_CON0 +#define RG_INT_EN_ACCDET_EINT1_SFT 7 +#define RG_INT_EN_ACCDET_EINT1_MASK 0x1 +#define RG_INT_EN_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +#define RG_INT_MASK_ACCDET_ADDR \ + MT6359_AUD_TOP_INT_MASK_CON0 +#define RG_INT_MASK_ACCDET_SFT 5 +#define RG_INT_MASK_ACCDET_MASK 0x1 +#define RG_INT_MASK_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_MASK_ACCDET_EINT0_ADDR \ + MT6359_AUD_TOP_INT_MASK_CON0 +#define RG_INT_MASK_ACCDET_EINT0_SFT 6 +#define RG_INT_MASK_ACCDET_EINT0_MASK 0x1 +#define RG_INT_MASK_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_MASK_ACCDET_EINT1_ADDR \ + MT6359_AUD_TOP_INT_MASK_CON0 +#define RG_INT_MASK_ACCDET_EINT1_SFT 7 +#define RG_INT_MASK_ACCDET_EINT1_MASK 0x1 +#define RG_INT_MASK_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +#define RG_INT_STATUS_ACCDET_ADDR \ + MT6359_AUD_TOP_INT_STATUS0 +#define RG_INT_STATUS_ACCDET_SFT 5 +#define RG_INT_STATUS_ACCDET_MASK 0x1 +#define RG_INT_STATUS_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_STATUS_ACCDET_EINT0_ADDR \ + MT6359_AUD_TOP_INT_STATUS0 +#define RG_INT_STATUS_ACCDET_EINT0_SFT 6 +#define RG_INT_STATUS_ACCDET_EINT0_MASK 0x1 +#define RG_INT_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_STATUS_ACCDET_EINT1_ADDR \ + MT6359_AUD_TOP_INT_STATUS0 +#define RG_INT_STATUS_ACCDET_EINT1_SFT 7 +#define RG_INT_STATUS_ACCDET_EINT1_MASK 0x1 +#define RG_INT_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +#define RG_INT_RAW_STATUS_ACCDET_ADDR \ + MT6359_AUD_TOP_INT_RAW_STATUS0 +#define RG_INT_RAW_STATUS_ACCDET_SFT 5 +#define RG_INT_RAW_STATUS_ACCDET_MASK 0x1 +#define RG_INT_RAW_STATUS_ACCDET_MASK_SFT (0x1 << 5) +#define RG_INT_RAW_STATUS_ACCDET_EINT0_ADDR \ + MT6359_AUD_TOP_INT_RAW_STATUS0 +#define RG_INT_RAW_STATUS_ACCDET_EINT0_SFT 6 +#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK 0x1 +#define RG_INT_RAW_STATUS_ACCDET_EINT0_MASK_SFT (0x1 << 6) +#define RG_INT_RAW_STATUS_ACCDET_EINT1_ADDR \ + MT6359_AUD_TOP_INT_RAW_STATUS0 +#define RG_INT_RAW_STATUS_ACCDET_EINT1_SFT 7 +#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK 0x1 +#define RG_INT_RAW_STATUS_ACCDET_EINT1_MASK_SFT (0x1 << 7) + +#define RG_AUDACCDETMICBIAS0PULLLOW_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0 +#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0) +#define RG_AUDACCDETMICBIAS1PULLLOW_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1 +#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1) +#define RG_AUDACCDETMICBIAS2PULLLOW_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_AUDACCDETMICBIAS2PULLLOW_SFT 2 +#define RG_AUDACCDETMICBIAS2PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS2PULLLOW_MASK_SFT (0x1 << 2) +#define RG_AUDACCDETVIN1PULLLOW_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_AUDACCDETVIN1PULLLOW_SFT 3 +#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1 +#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 3) +#define RG_AUDACCDETVTHACAL_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_AUDACCDETVTHACAL_SFT 4 +#define RG_AUDACCDETVTHACAL_MASK 0x1 +#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4) +#define RG_AUDACCDETVTHBCAL_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_AUDACCDETVTHBCAL_SFT 5 +#define RG_AUDACCDETVTHBCAL_MASK 0x1 +#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5) +#define RG_AUDACCDETTVDET_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_AUDACCDETTVDET_SFT 6 +#define RG_AUDACCDETTVDET_MASK 0x1 +#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6) +#define RG_ACCDETSEL_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_ACCDETSEL_SFT 7 +#define RG_ACCDETSEL_MASK 0x1 +#define RG_ACCDETSEL_MASK_SFT (0x1 << 7) + +#define RG_AUDPWDBMICBIAS1_ADDR \ + MT6359_AUDENC_ANA_CON16 +#define RG_AUDPWDBMICBIAS1_SFT 0 +#define RG_AUDPWDBMICBIAS1_MASK 0x1 +#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS1BYPASSEN_ADDR \ + MT6359_AUDENC_ANA_CON16 +#define RG_AUDMICBIAS1BYPASSEN_SFT 1 +#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS1LOWPEN_ADDR \ + MT6359_AUDENC_ANA_CON16 +#define RG_AUDMICBIAS1LOWPEN_SFT 2 +#define RG_AUDMICBIAS1LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDMICBIAS1VREF_ADDR \ + MT6359_AUDENC_ANA_CON16 +#define RG_AUDMICBIAS1VREF_SFT 4 +#define RG_AUDMICBIAS1VREF_MASK 0x7 +#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS1DCSW1PEN_ADDR \ + MT6359_AUDENC_ANA_CON16 +#define RG_AUDMICBIAS1DCSW1PEN_SFT 8 +#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1 +#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS1DCSW1NEN_ADDR \ + MT6359_AUDENC_ANA_CON16 +#define RG_AUDMICBIAS1DCSW1NEN_SFT 9 +#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1 +#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9) +#define RG_BANDGAPGEN_ADDR \ + MT6359_AUDENC_ANA_CON16 +#define RG_BANDGAPGEN_SFT 10 +#define RG_BANDGAPGEN_MASK 0x1 +#define RG_BANDGAPGEN_MASK_SFT (0x1 << 10) +#define RG_AUDMICBIAS1HVEN_ADDR \ + MT6359_AUDENC_ANA_CON16 +#define RG_AUDMICBIAS1HVEN_SFT 12 +#define RG_AUDMICBIAS1HVEN_MASK 0x1 +#define RG_AUDMICBIAS1HVEN_MASK_SFT (0x1 << 12) +#define RG_AUDMICBIAS1HVVREF_ADDR \ + MT6359_AUDENC_ANA_CON16 +#define RG_AUDMICBIAS1HVVREF_SFT 13 +#define RG_AUDMICBIAS1HVVREF_MASK 0x1 +#define RG_AUDMICBIAS1HVVREF_MASK_SFT (0x1 << 13) + +#define RG_EINT0NOHYS_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_EINT0NOHYS_SFT 10 +#define RG_EINT0NOHYS_MASK 0x1 +#define RG_EINT0NOHYS_MASK_SFT (0x1 << 10) +#define RG_EINT0CONFIGACCDET_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_EINT0CONFIGACCDET_SFT 11 +#define RG_EINT0CONFIGACCDET_MASK 0x1 +#define RG_EINT0CONFIGACCDET_MASK_SFT (0x1 << 11) +#define RG_EINT0HIRENB_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_EINT0HIRENB_SFT 12 +#define RG_EINT0HIRENB_MASK 0x1 +#define RG_EINT0HIRENB_MASK_SFT (0x1 << 12) +#define RG_ACCDET2AUXRESBYPASS_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_ACCDET2AUXRESBYPASS_SFT 13 +#define RG_ACCDET2AUXRESBYPASS_MASK 0x1 +#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13) +#define RG_ACCDET2AUXSWEN_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_ACCDET2AUXSWEN_SFT 14 +#define RG_ACCDET2AUXSWEN_MASK 0x1 +#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 14) +#define RG_AUDACCDETMICBIAS3PULLLOW_ADDR \ + MT6359_AUDENC_ANA_CON18 +#define RG_AUDACCDETMICBIAS3PULLLOW_SFT 15 +#define RG_AUDACCDETMICBIAS3PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS3PULLLOW_MASK_SFT (0x1 << 15) +#define RG_EINT1CONFIGACCDET_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_EINT1CONFIGACCDET_SFT 0 +#define RG_EINT1CONFIGACCDET_MASK 0x1 +#define RG_EINT1CONFIGACCDET_MASK_SFT (0x1 << 0) +#define RG_EINT1HIRENB_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_EINT1HIRENB_SFT 1 +#define RG_EINT1HIRENB_MASK 0x1 +#define RG_EINT1HIRENB_MASK_SFT (0x1 << 1) +#define RG_EINT1NOHYS_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_EINT1NOHYS_SFT 2 +#define RG_EINT1NOHYS_MASK 0x1 +#define RG_EINT1NOHYS_MASK_SFT (0x1 << 2) +#define RG_EINTCOMPVTH_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_MTEST_EN_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_MTEST_EN_SFT 8 +#define RG_MTEST_EN_MASK 0x1 +#define RG_MTEST_EN_MASK_SFT (0x1 << 8) +#define RG_MTEST_SEL_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_MTEST_SEL_SFT 9 +#define RG_MTEST_SEL_MASK 0x1 +#define RG_MTEST_SEL_MASK_SFT (0x1 << 9) +#define RG_MTEST_CURRENT_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_MTEST_CURRENT_SFT 10 +#define RG_MTEST_CURRENT_MASK 0x1 +#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 10) +#define RG_ANALOGFDEN_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_ANALOGFDEN_SFT 12 +#define RG_ANALOGFDEN_MASK 0x1 +#define RG_ANALOGFDEN_MASK_SFT (0x1 << 12) +#define RG_FDVIN1PPULLLOW_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_FDVIN1PPULLLOW_SFT 13 +#define RG_FDVIN1PPULLLOW_MASK 0x1 +#define RG_FDVIN1PPULLLOW_MASK_SFT (0x1 << 13) +#define RG_FDEINT0TYPE_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_FDEINT0TYPE_SFT 14 +#define RG_FDEINT0TYPE_MASK 0x1 +#define RG_FDEINT0TYPE_MASK_SFT (0x1 << 14) +#define RG_FDEINT1TYPE_ADDR \ + MT6359_AUDENC_ANA_CON19 +#define RG_FDEINT1TYPE_SFT 15 +#define RG_FDEINT1TYPE_MASK 0x1 +#define RG_FDEINT1TYPE_MASK_SFT (0x1 << 15) +#define RG_EINT0CMPEN_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT0CMPEN_SFT 0 +#define RG_EINT0CMPEN_MASK 0x1 +#define RG_EINT0CMPEN_MASK_SFT (0x1 << 0) +#define RG_EINT0CMPMEN_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT0CMPMEN_SFT 1 +#define RG_EINT0CMPMEN_MASK 0x1 +#define RG_EINT0CMPMEN_MASK_SFT (0x1 << 1) +#define RG_EINT0EN_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT0EN_SFT 2 +#define RG_EINT0EN_MASK 0x1 +#define RG_EINT0EN_MASK_SFT (0x1 << 2) +#define RG_EINT0CEN_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT0CEN_SFT 3 +#define RG_EINT0CEN_MASK 0x1 +#define RG_EINT0CEN_MASK_SFT (0x1 << 3) +#define RG_EINT0INVEN_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT0INVEN_SFT 4 +#define RG_EINT0INVEN_MASK 0x1 +#define RG_EINT0INVEN_MASK_SFT (0x1 << 4) +#define RG_EINT0CTURBO_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT0CTURBO_SFT 5 +#define RG_EINT0CTURBO_MASK 0x7 +#define RG_EINT0CTURBO_MASK_SFT (0x7 << 5) +#define RG_EINT1CMPEN_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT1CMPEN_SFT 8 +#define RG_EINT1CMPEN_MASK 0x1 +#define RG_EINT1CMPEN_MASK_SFT (0x1 << 8) +#define RG_EINT1CMPMEN_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT1CMPMEN_SFT 9 +#define RG_EINT1CMPMEN_MASK 0x1 +#define RG_EINT1CMPMEN_MASK_SFT (0x1 << 9) +#define RG_EINT1EN_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT1EN_SFT 10 +#define RG_EINT1EN_MASK 0x1 +#define RG_EINT1EN_MASK_SFT (0x1 << 10) +#define RG_EINT1CEN_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT1CEN_SFT 11 +#define RG_EINT1CEN_MASK 0x1 +#define RG_EINT1CEN_MASK_SFT (0x1 << 11) +#define RG_EINT1INVEN_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT1INVEN_SFT 12 +#define RG_EINT1INVEN_MASK 0x1 +#define RG_EINT1INVEN_MASK_SFT (0x1 << 12) +#define RG_EINT1CTURBO_ADDR \ + MT6359_AUDENC_ANA_CON20 +#define RG_EINT1CTURBO_SFT 13 +#define RG_EINT1CTURBO_MASK 0x7 +#define RG_EINT1CTURBO_MASK_SFT (0x7 << 13) +#define RG_ACCDETSPARE_ADDR \ + MT6359_AUDENC_ANA_CON21 + +#define ACCDET_ANA_ID_ADDR \ + MT6359_ACCDET_DSN_DIG_ID +#define ACCDET_ANA_ID_SFT 0 +#define ACCDET_ANA_ID_MASK 0xFF +#define ACCDET_ANA_ID_MASK_SFT (0xFF << 0) +#define ACCDET_DIG_ID_ADDR \ + MT6359_ACCDET_DSN_DIG_ID +#define ACCDET_DIG_ID_SFT 8 +#define ACCDET_DIG_ID_MASK 0xFF +#define ACCDET_DIG_ID_MASK_SFT (0xFF << 8) +#define ACCDET_ANA_MINOR_REV_ADDR \ + MT6359_ACCDET_DSN_DIG_REV0 +#define ACCDET_ANA_MINOR_REV_SFT 0 +#define ACCDET_ANA_MINOR_REV_MASK 0xF +#define ACCDET_ANA_MINOR_REV_MASK_SFT (0xF << 0) +#define ACCDET_ANA_MAJOR_REV_ADDR \ + MT6359_ACCDET_DSN_DIG_REV0 +#define ACCDET_ANA_MAJOR_REV_SFT 4 +#define ACCDET_ANA_MAJOR_REV_MASK 0xF +#define ACCDET_ANA_MAJOR_REV_MASK_SFT (0xF << 4) +#define ACCDET_DIG_MINOR_REV_ADDR \ + MT6359_ACCDET_DSN_DIG_REV0 +#define ACCDET_DIG_MINOR_REV_SFT 8 +#define ACCDET_DIG_MINOR_REV_MASK 0xF +#define ACCDET_DIG_MINOR_REV_MASK_SFT (0xF << 8) +#define ACCDET_DIG_MAJOR_REV_ADDR \ + MT6359_ACCDET_DSN_DIG_REV0 +#define ACCDET_DIG_MAJOR_REV_SFT 12 +#define ACCDET_DIG_MAJOR_REV_MASK 0xF +#define ACCDET_DIG_MAJOR_REV_MASK_SFT (0xF << 12) +#define ACCDET_DSN_CBS_ADDR \ + MT6359_ACCDET_DSN_DBI +#define ACCDET_DSN_CBS_SFT 0 +#define ACCDET_DSN_CBS_MASK 0x3 +#define ACCDET_DSN_CBS_MASK_SFT (0x3 << 0) +#define ACCDET_DSN_BIX_ADDR \ + MT6359_ACCDET_DSN_DBI +#define ACCDET_DSN_BIX_SFT 2 +#define ACCDET_DSN_BIX_MASK 0x3 +#define ACCDET_DSN_BIX_MASK_SFT (0x3 << 2) +#define ACCDET_ESP_ADDR \ + MT6359_ACCDET_DSN_DBI +#define ACCDET_ESP_SFT 8 +#define ACCDET_ESP_MASK 0xFF +#define ACCDET_ESP_MASK_SFT (0xFF << 8) +#define ACCDET_DSN_FPI_ADDR \ + MT6359_ACCDET_DSN_FPI +#define ACCDET_DSN_FPI_SFT 0 +#define ACCDET_DSN_FPI_MASK 0xFF +#define ACCDET_DSN_FPI_MASK_SFT (0xFF << 0) +#define ACCDET_AUXADC_SEL_ADDR \ + MT6359_ACCDET_CON0 +#define ACCDET_AUXADC_SEL_SFT 0 +#define ACCDET_AUXADC_SEL_MASK 0x1 +#define ACCDET_AUXADC_SEL_MASK_SFT (0x1 << 0) +#define ACCDET_AUXADC_SW_ADDR \ + MT6359_ACCDET_CON0 +#define ACCDET_AUXADC_SW_SFT 1 +#define ACCDET_AUXADC_SW_MASK 0x1 +#define ACCDET_AUXADC_SW_MASK_SFT (0x1 << 1) +#define ACCDET_TEST_AUXADC_ADDR \ + MT6359_ACCDET_CON0 +#define ACCDET_TEST_AUXADC_SFT 2 +#define ACCDET_TEST_AUXADC_MASK 0x1 +#define ACCDET_TEST_AUXADC_MASK_SFT (0x1 << 2) +#define ACCDET_AUXADC_ANASWCTRL_SEL_ADDR \ + MT6359_ACCDET_CON0 +#define ACCDET_AUXADC_ANASWCTRL_SEL_SFT 8 +#define ACCDET_AUXADC_ANASWCTRL_SEL_MASK 0x1 +#define ACCDET_AUXADC_ANASWCTRL_SEL_MASK_SFT (0x1 << 8) +#define AUDACCDETAUXADCSWCTRL_SEL_ADDR \ + MT6359_ACCDET_CON0 +#define AUDACCDETAUXADCSWCTRL_SEL_SFT 9 +#define AUDACCDETAUXADCSWCTRL_SEL_MASK 0x1 +#define AUDACCDETAUXADCSWCTRL_SEL_MASK_SFT (0x1 << 9) +#define AUDACCDETAUXADCSWCTRL_SW_ADDR \ + MT6359_ACCDET_CON0 +#define AUDACCDETAUXADCSWCTRL_SW_SFT 10 +#define AUDACCDETAUXADCSWCTRL_SW_MASK 0x1 +#define AUDACCDETAUXADCSWCTRL_SW_MASK_SFT (0x1 << 10) +#define ACCDET_TEST_ANA_ADDR \ + MT6359_ACCDET_CON0 +#define ACCDET_TEST_ANA_SFT 11 +#define ACCDET_TEST_ANA_MASK 0x1 +#define ACCDET_TEST_ANA_MASK_SFT (0x1 << 11) +#define RG_AUDACCDETRSV_ADDR \ + MT6359_ACCDET_CON0 +#define RG_AUDACCDETRSV_SFT 13 +#define RG_AUDACCDETRSV_MASK 0x3 +#define RG_AUDACCDETRSV_MASK_SFT (0x3 << 13) +#define ACCDET_SW_EN_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_SW_EN_SFT 0 +#define ACCDET_SW_EN_MASK 0x1 +#define ACCDET_SW_EN_MASK_SFT (0x1 << 0) +#define ACCDET_SEQ_INIT_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_SEQ_INIT_SFT 1 +#define ACCDET_SEQ_INIT_MASK 0x1 +#define ACCDET_SEQ_INIT_MASK_SFT (0x1 << 1) +#define ACCDET_EINT0_SW_EN_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT0_SW_EN_SFT 2 +#define ACCDET_EINT0_SW_EN_MASK 0x1 +#define ACCDET_EINT0_SW_EN_MASK_SFT (0x1 << 2) +#define ACCDET_EINT0_SEQ_INIT_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT0_SEQ_INIT_SFT 3 +#define ACCDET_EINT0_SEQ_INIT_MASK 0x1 +#define ACCDET_EINT0_SEQ_INIT_MASK_SFT (0x1 << 3) +#define ACCDET_EINT1_SW_EN_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT1_SW_EN_SFT 4 +#define ACCDET_EINT1_SW_EN_MASK 0x1 +#define ACCDET_EINT1_SW_EN_MASK_SFT (0x1 << 4) +#define ACCDET_EINT1_SEQ_INIT_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT1_SEQ_INIT_SFT 5 +#define ACCDET_EINT1_SEQ_INIT_MASK 0x1 +#define ACCDET_EINT1_SEQ_INIT_MASK_SFT (0x1 << 5) +#define ACCDET_EINT0_INVERTER_SW_EN_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT0_INVERTER_SW_EN_SFT 6 +#define ACCDET_EINT0_INVERTER_SW_EN_MASK 0x1 +#define ACCDET_EINT0_INVERTER_SW_EN_MASK_SFT (0x1 << 6) +#define ACCDET_EINT0_INVERTER_SEQ_INIT_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT0_INVERTER_SEQ_INIT_SFT 7 +#define ACCDET_EINT0_INVERTER_SEQ_INIT_MASK 0x1 +#define ACCDET_EINT0_INVERTER_SEQ_INIT_MASK_SFT (0x1 << 7) +#define ACCDET_EINT1_INVERTER_SW_EN_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT1_INVERTER_SW_EN_SFT 8 +#define ACCDET_EINT1_INVERTER_SW_EN_MASK 0x1 +#define ACCDET_EINT1_INVERTER_SW_EN_MASK_SFT (0x1 << 8) +#define ACCDET_EINT1_INVERTER_SEQ_INIT_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT1_INVERTER_SEQ_INIT_SFT 9 +#define ACCDET_EINT1_INVERTER_SEQ_INIT_MASK 0x1 +#define ACCDET_EINT1_INVERTER_SEQ_INIT_MASK_SFT (0x1 << 9) +#define ACCDET_EINT0_M_SW_EN_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT0_M_SW_EN_SFT 10 +#define ACCDET_EINT0_M_SW_EN_MASK 0x1 +#define ACCDET_EINT0_M_SW_EN_MASK_SFT (0x1 << 10) +#define ACCDET_EINT1_M_SW_EN_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT1_M_SW_EN_SFT 11 +#define ACCDET_EINT1_M_SW_EN_MASK 0x1 +#define ACCDET_EINT1_M_SW_EN_MASK_SFT (0x1 << 11) +#define ACCDET_EINT_M_DETECT_EN_ADDR \ + MT6359_ACCDET_CON1 +#define ACCDET_EINT_M_DETECT_EN_SFT 12 +#define ACCDET_EINT_M_DETECT_EN_MASK 0x1 +#define ACCDET_EINT_M_DETECT_EN_MASK_SFT (0x1 << 12) +#define ACCDET_CMP_PWM_EN_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_CMP_PWM_EN_SFT 0 +#define ACCDET_CMP_PWM_EN_MASK 0x1 +#define ACCDET_CMP_PWM_EN_MASK_SFT (0x1 << 0) +#define ACCDET_VTH_PWM_EN_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_VTH_PWM_EN_SFT 1 +#define ACCDET_VTH_PWM_EN_MASK 0x1 +#define ACCDET_VTH_PWM_EN_MASK_SFT (0x1 << 1) +#define ACCDET_MBIAS_PWM_EN_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_MBIAS_PWM_EN_SFT 2 +#define ACCDET_MBIAS_PWM_EN_MASK 0x1 +#define ACCDET_MBIAS_PWM_EN_MASK_SFT (0x1 << 2) +#define ACCDET_EINT_EN_PWM_EN_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_EINT_EN_PWM_EN_SFT 3 +#define ACCDET_EINT_EN_PWM_EN_MASK 0x1 +#define ACCDET_EINT_EN_PWM_EN_MASK_SFT (0x1 << 3) +#define ACCDET_EINT_CMPEN_PWM_EN_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_EINT_CMPEN_PWM_EN_SFT 4 +#define ACCDET_EINT_CMPEN_PWM_EN_MASK 0x1 +#define ACCDET_EINT_CMPEN_PWM_EN_MASK_SFT (0x1 << 4) +#define ACCDET_EINT_CMPMEN_PWM_EN_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_EINT_CMPMEN_PWM_EN_SFT 5 +#define ACCDET_EINT_CMPMEN_PWM_EN_MASK 0x1 +#define ACCDET_EINT_CMPMEN_PWM_EN_MASK_SFT (0x1 << 5) +#define ACCDET_EINT_CTURBO_PWM_EN_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_EINT_CTURBO_PWM_EN_SFT 6 +#define ACCDET_EINT_CTURBO_PWM_EN_MASK 0x1 +#define ACCDET_EINT_CTURBO_PWM_EN_MASK_SFT (0x1 << 6) +#define ACCDET_CMP_PWM_IDLE_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_CMP_PWM_IDLE_SFT 8 +#define ACCDET_CMP_PWM_IDLE_MASK 0x1 +#define ACCDET_CMP_PWM_IDLE_MASK_SFT (0x1 << 8) +#define ACCDET_VTH_PWM_IDLE_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_VTH_PWM_IDLE_SFT 9 +#define ACCDET_VTH_PWM_IDLE_MASK 0x1 +#define ACCDET_VTH_PWM_IDLE_MASK_SFT (0x1 << 9) +#define ACCDET_MBIAS_PWM_IDLE_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_MBIAS_PWM_IDLE_SFT 10 +#define ACCDET_MBIAS_PWM_IDLE_MASK 0x1 +#define ACCDET_MBIAS_PWM_IDLE_MASK_SFT (0x1 << 10) +#define ACCDET_EINT0_CMPEN_PWM_IDLE_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_EINT0_CMPEN_PWM_IDLE_SFT 11 +#define ACCDET_EINT0_CMPEN_PWM_IDLE_MASK 0x1 +#define ACCDET_EINT0_CMPEN_PWM_IDLE_MASK_SFT (0x1 << 11) +#define ACCDET_EINT1_CMPEN_PWM_IDLE_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_EINT1_CMPEN_PWM_IDLE_SFT 12 +#define ACCDET_EINT1_CMPEN_PWM_IDLE_MASK 0x1 +#define ACCDET_EINT1_CMPEN_PWM_IDLE_MASK_SFT (0x1 << 12) +#define ACCDET_PWM_EN_SW_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_PWM_EN_SW_SFT 13 +#define ACCDET_PWM_EN_SW_MASK 0x1 +#define ACCDET_PWM_EN_SW_MASK_SFT (0x1 << 13) +#define ACCDET_PWM_EN_SEL_ADDR \ + MT6359_ACCDET_CON2 +#define ACCDET_PWM_EN_SEL_SFT 14 +#define ACCDET_PWM_EN_SEL_MASK 0x3 +#define ACCDET_PWM_EN_SEL_MASK_SFT (0x3 << 14) +#define ACCDET_PWM_WIDTH_ADDR \ + MT6359_ACCDET_CON3 +#define ACCDET_PWM_WIDTH_SFT 0 +#define ACCDET_PWM_WIDTH_MASK 0xFFFF +#define ACCDET_PWM_WIDTH_MASK_SFT (0xFFFF << 0) +#define ACCDET_PWM_THRESH_ADDR \ + MT6359_ACCDET_CON4 +#define ACCDET_PWM_THRESH_SFT 0 +#define ACCDET_PWM_THRESH_MASK 0xFFFF +#define ACCDET_PWM_THRESH_MASK_SFT (0xFFFF << 0) +#define ACCDET_RISE_DELAY_ADDR \ + MT6359_ACCDET_CON5 +#define ACCDET_RISE_DELAY_SFT 0 +#define ACCDET_RISE_DELAY_MASK 0x7FFF +#define ACCDET_RISE_DELAY_MASK_SFT (0x7FFF << 0) +#define ACCDET_FALL_DELAY_ADDR \ + MT6359_ACCDET_CON5 +#define ACCDET_FALL_DELAY_SFT 15 +#define ACCDET_FALL_DELAY_MASK 0x1 +#define ACCDET_FALL_DELAY_MASK_SFT (0x1 << 15) +#define ACCDET_EINT_CMPMEN_PWM_THRESH_ADDR \ + MT6359_ACCDET_CON6 +#define ACCDET_EINT_CMPMEN_PWM_THRESH_SFT 0 +#define ACCDET_EINT_CMPMEN_PWM_THRESH_MASK 0x7 +#define ACCDET_EINT_CMPMEN_PWM_THRESH_MASK_SFT (0x7 << 0) +#define ACCDET_EINT_CMPMEN_PWM_WIDTH_ADDR \ + MT6359_ACCDET_CON6 +#define ACCDET_EINT_CMPMEN_PWM_WIDTH_SFT 4 +#define ACCDET_EINT_CMPMEN_PWM_WIDTH_MASK 0x7 +#define ACCDET_EINT_CMPMEN_PWM_WIDTH_MASK_SFT (0x7 << 4) +#define ACCDET_EINT_EN_PWM_THRESH_ADDR \ + MT6359_ACCDET_CON7 +#define ACCDET_EINT_EN_PWM_THRESH_SFT 0 +#define ACCDET_EINT_EN_PWM_THRESH_MASK 0x7 +#define ACCDET_EINT_EN_PWM_THRESH_MASK_SFT (0x7 << 0) +#define ACCDET_EINT_EN_PWM_WIDTH_ADDR \ + MT6359_ACCDET_CON7 +#define ACCDET_EINT_EN_PWM_WIDTH_SFT 4 +#define ACCDET_EINT_EN_PWM_WIDTH_MASK 0x3 +#define ACCDET_EINT_EN_PWM_WIDTH_MASK_SFT (0x3 << 4) +#define ACCDET_EINT_CMPEN_PWM_THRESH_ADDR \ + MT6359_ACCDET_CON7 +#define ACCDET_EINT_CMPEN_PWM_THRESH_SFT 8 +#define ACCDET_EINT_CMPEN_PWM_THRESH_MASK 0x7 +#define ACCDET_EINT_CMPEN_PWM_THRESH_MASK_SFT (0x7 << 8) +#define ACCDET_EINT_CMPEN_PWM_WIDTH_ADDR \ + MT6359_ACCDET_CON7 +#define ACCDET_EINT_CMPEN_PWM_WIDTH_SFT 12 +#define ACCDET_EINT_CMPEN_PWM_WIDTH_MASK 0x3 +#define ACCDET_EINT_CMPEN_PWM_WIDTH_MASK_SFT (0x3 << 12) +#define ACCDET_DEBOUNCE0_ADDR \ + MT6359_ACCDET_CON8 +#define ACCDET_DEBOUNCE0_SFT 0 +#define ACCDET_DEBOUNCE0_MASK 0xFFFF +#define ACCDET_DEBOUNCE0_MASK_SFT (0xFFFF << 0) +#define ACCDET_DEBOUNCE1_ADDR \ + MT6359_ACCDET_CON9 +#define ACCDET_DEBOUNCE1_SFT 0 +#define ACCDET_DEBOUNCE1_MASK 0xFFFF +#define ACCDET_DEBOUNCE1_MASK_SFT (0xFFFF << 0) +#define ACCDET_DEBOUNCE2_ADDR \ + MT6359_ACCDET_CON10 +#define ACCDET_DEBOUNCE2_SFT 0 +#define ACCDET_DEBOUNCE2_MASK 0xFFFF +#define ACCDET_DEBOUNCE2_MASK_SFT (0xFFFF << 0) +#define ACCDET_DEBOUNCE3_ADDR \ + MT6359_ACCDET_CON11 +#define ACCDET_DEBOUNCE3_SFT 0 +#define ACCDET_DEBOUNCE3_MASK 0xFFFF +#define ACCDET_DEBOUNCE3_MASK_SFT (0xFFFF << 0) +#define ACCDET_CONNECT_AUXADC_TIME_DIG_ADDR \ + MT6359_ACCDET_CON12 +#define ACCDET_CONNECT_AUXADC_TIME_DIG_SFT 0 +#define ACCDET_CONNECT_AUXADC_TIME_DIG_MASK 0xFFFF +#define ACCDET_CONNECT_AUXADC_TIME_DIG_MASK_SFT (0xFFFF << 0) +#define ACCDET_CONNECT_AUXADC_TIME_ANA_ADDR \ + MT6359_ACCDET_CON13 +#define ACCDET_CONNECT_AUXADC_TIME_ANA_SFT 0 +#define ACCDET_CONNECT_AUXADC_TIME_ANA_MASK 0xFFFF +#define ACCDET_CONNECT_AUXADC_TIME_ANA_MASK_SFT (0xFFFF << 0) +#define ACCDET_EINT_DEBOUNCE0_ADDR \ + MT6359_ACCDET_CON14 +#define ACCDET_EINT_DEBOUNCE0_SFT 0 +#define ACCDET_EINT_DEBOUNCE0_MASK 0xF +#define ACCDET_EINT_DEBOUNCE0_MASK_SFT (0xF << 0) +#define ACCDET_EINT_DEBOUNCE1_ADDR \ + MT6359_ACCDET_CON14 +#define ACCDET_EINT_DEBOUNCE1_SFT 4 +#define ACCDET_EINT_DEBOUNCE1_MASK 0xF +#define ACCDET_EINT_DEBOUNCE1_MASK_SFT (0xF << 4) +#define ACCDET_EINT_DEBOUNCE2_ADDR \ + MT6359_ACCDET_CON14 +#define ACCDET_EINT_DEBOUNCE2_SFT 8 +#define ACCDET_EINT_DEBOUNCE2_MASK 0xF +#define ACCDET_EINT_DEBOUNCE2_MASK_SFT (0xF << 8) +#define ACCDET_EINT_DEBOUNCE3_ADDR \ + MT6359_ACCDET_CON14 +#define ACCDET_EINT_DEBOUNCE3_SFT 12 +#define ACCDET_EINT_DEBOUNCE3_MASK 0xF +#define ACCDET_EINT_DEBOUNCE3_MASK_SFT (0xF << 12) +#define ACCDET_EINT_INVERTER_DEBOUNCE_ADDR \ + MT6359_ACCDET_CON15 +#define ACCDET_EINT_INVERTER_DEBOUNCE_SFT 0 +#define ACCDET_EINT_INVERTER_DEBOUNCE_MASK 0xF +#define ACCDET_EINT_INVERTER_DEBOUNCE_MASK_SFT (0xF << 0) +#define ACCDET_IVAL_CUR_IN_ADDR \ + MT6359_ACCDET_CON16 +#define ACCDET_IVAL_CUR_IN_SFT 0 +#define ACCDET_IVAL_CUR_IN_MASK 0x3 +#define ACCDET_IVAL_CUR_IN_MASK_SFT (0x3 << 0) +#define ACCDET_IVAL_SAM_IN_ADDR \ + MT6359_ACCDET_CON16 +#define ACCDET_IVAL_SAM_IN_SFT 2 +#define ACCDET_IVAL_SAM_IN_MASK 0x3 +#define ACCDET_IVAL_SAM_IN_MASK_SFT (0x3 << 2) +#define ACCDET_IVAL_MEM_IN_ADDR \ + MT6359_ACCDET_CON16 +#define ACCDET_IVAL_MEM_IN_SFT 4 +#define ACCDET_IVAL_MEM_IN_MASK 0x3 +#define ACCDET_IVAL_MEM_IN_MASK_SFT (0x3 << 4) +#define ACCDET_EINT_IVAL_CUR_IN_ADDR \ + MT6359_ACCDET_CON16 +#define ACCDET_EINT_IVAL_CUR_IN_SFT 6 +#define ACCDET_EINT_IVAL_CUR_IN_MASK 0x3 +#define ACCDET_EINT_IVAL_CUR_IN_MASK_SFT (0x3 << 6) +#define ACCDET_EINT_IVAL_SAM_IN_ADDR \ + MT6359_ACCDET_CON16 +#define ACCDET_EINT_IVAL_SAM_IN_SFT 8 +#define ACCDET_EINT_IVAL_SAM_IN_MASK 0x3 +#define ACCDET_EINT_IVAL_SAM_IN_MASK_SFT (0x3 << 8) +#define ACCDET_EINT_IVAL_MEM_IN_ADDR \ + MT6359_ACCDET_CON16 +#define ACCDET_EINT_IVAL_MEM_IN_SFT 10 +#define ACCDET_EINT_IVAL_MEM_IN_MASK 0x3 +#define ACCDET_EINT_IVAL_MEM_IN_MASK_SFT (0x3 << 10) +#define ACCDET_IVAL_SEL_ADDR \ + MT6359_ACCDET_CON16 +#define ACCDET_IVAL_SEL_SFT 12 +#define ACCDET_IVAL_SEL_MASK 0x1 +#define ACCDET_IVAL_SEL_MASK_SFT (0x1 << 12) +#define ACCDET_EINT_IVAL_SEL_ADDR \ + MT6359_ACCDET_CON16 +#define ACCDET_EINT_IVAL_SEL_SFT 13 +#define ACCDET_EINT_IVAL_SEL_MASK 0x1 +#define ACCDET_EINT_IVAL_SEL_MASK_SFT (0x1 << 13) +#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_ADDR \ + MT6359_ACCDET_CON17 +#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_SFT 0 +#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_MASK 0x1 +#define ACCDET_EINT_INVERTER_IVAL_CUR_IN_MASK_SFT (0x1 << 0) +#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_ADDR \ + MT6359_ACCDET_CON17 +#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_SFT 1 +#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_MASK 0x1 +#define ACCDET_EINT_INVERTER_IVAL_SAM_IN_MASK_SFT (0x1 << 1) +#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_ADDR \ + MT6359_ACCDET_CON17 +#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_SFT 2 +#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_MASK 0x1 +#define ACCDET_EINT_INVERTER_IVAL_MEM_IN_MASK_SFT (0x1 << 2) +#define ACCDET_EINT_INVERTER_IVAL_SEL_ADDR \ + MT6359_ACCDET_CON17 +#define ACCDET_EINT_INVERTER_IVAL_SEL_SFT 3 +#define ACCDET_EINT_INVERTER_IVAL_SEL_MASK 0x1 +#define ACCDET_EINT_INVERTER_IVAL_SEL_MASK_SFT (0x1 << 3) +#define ACCDET_IRQ_ADDR \ + MT6359_ACCDET_CON18 +#define ACCDET_IRQ_SFT 0 +#define ACCDET_IRQ_MASK 0x1 +#define ACCDET_IRQ_MASK_SFT (0x1 << 0) +#define ACCDET_EINT0_IRQ_ADDR \ + MT6359_ACCDET_CON18 +#define ACCDET_EINT0_IRQ_SFT 2 +#define ACCDET_EINT0_IRQ_MASK 0x1 +#define ACCDET_EINT0_IRQ_MASK_SFT (0x1 << 2) +#define ACCDET_EINT1_IRQ_ADDR \ + MT6359_ACCDET_CON18 +#define ACCDET_EINT1_IRQ_SFT 3 +#define ACCDET_EINT1_IRQ_MASK 0x1 +#define ACCDET_EINT1_IRQ_MASK_SFT (0x1 << 3) +#define ACCDET_EINT_IN_INVERSE_ADDR \ + MT6359_ACCDET_CON18 +#define ACCDET_EINT_IN_INVERSE_SFT 4 +#define ACCDET_EINT_IN_INVERSE_MASK 0x1 +#define ACCDET_EINT_IN_INVERSE_MASK_SFT (0x1 << 4) +#define ACCDET_IRQ_CLR_ADDR \ + MT6359_ACCDET_CON18 +#define ACCDET_IRQ_CLR_SFT 8 +#define ACCDET_IRQ_CLR_MASK 0x1 +#define ACCDET_IRQ_CLR_MASK_SFT (0x1 << 8) +#define ACCDET_EINT0_IRQ_CLR_ADDR \ + MT6359_ACCDET_CON18 +#define ACCDET_EINT0_IRQ_CLR_SFT 10 +#define ACCDET_EINT0_IRQ_CLR_MASK 0x1 +#define ACCDET_EINT0_IRQ_CLR_MASK_SFT (0x1 << 10) +#define ACCDET_EINT1_IRQ_CLR_ADDR \ + MT6359_ACCDET_CON18 +#define ACCDET_EINT1_IRQ_CLR_SFT 11 +#define ACCDET_EINT1_IRQ_CLR_MASK 0x1 +#define ACCDET_EINT1_IRQ_CLR_MASK_SFT (0x1 << 11) +#define ACCDET_EINT_M_PLUG_IN_NUM_ADDR \ + MT6359_ACCDET_CON18 +#define ACCDET_EINT_M_PLUG_IN_NUM_SFT 12 +#define ACCDET_EINT_M_PLUG_IN_NUM_MASK 0x7 +#define ACCDET_EINT_M_PLUG_IN_NUM_MASK_SFT (0x7 << 12) +#define ACCDET_DA_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_DA_STABLE_SFT 0 +#define ACCDET_DA_STABLE_MASK 0x1 +#define ACCDET_DA_STABLE_MASK_SFT (0x1 << 0) +#define ACCDET_EINT0_EN_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_EINT0_EN_STABLE_SFT 1 +#define ACCDET_EINT0_EN_STABLE_MASK 0x1 +#define ACCDET_EINT0_EN_STABLE_MASK_SFT (0x1 << 1) +#define ACCDET_EINT0_CMPEN_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_EINT0_CMPEN_STABLE_SFT 2 +#define ACCDET_EINT0_CMPEN_STABLE_MASK 0x1 +#define ACCDET_EINT0_CMPEN_STABLE_MASK_SFT (0x1 << 2) +#define ACCDET_EINT0_CMPMEN_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_EINT0_CMPMEN_STABLE_SFT 3 +#define ACCDET_EINT0_CMPMEN_STABLE_MASK 0x1 +#define ACCDET_EINT0_CMPMEN_STABLE_MASK_SFT (0x1 << 3) +#define ACCDET_EINT0_CTURBO_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_EINT0_CTURBO_STABLE_SFT 4 +#define ACCDET_EINT0_CTURBO_STABLE_MASK 0x1 +#define ACCDET_EINT0_CTURBO_STABLE_MASK_SFT (0x1 << 4) +#define ACCDET_EINT0_CEN_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_EINT0_CEN_STABLE_SFT 5 +#define ACCDET_EINT0_CEN_STABLE_MASK 0x1 +#define ACCDET_EINT0_CEN_STABLE_MASK_SFT (0x1 << 5) +#define ACCDET_EINT1_EN_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_EINT1_EN_STABLE_SFT 6 +#define ACCDET_EINT1_EN_STABLE_MASK 0x1 +#define ACCDET_EINT1_EN_STABLE_MASK_SFT (0x1 << 6) +#define ACCDET_EINT1_CMPEN_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_EINT1_CMPEN_STABLE_SFT 7 +#define ACCDET_EINT1_CMPEN_STABLE_MASK 0x1 +#define ACCDET_EINT1_CMPEN_STABLE_MASK_SFT (0x1 << 7) +#define ACCDET_EINT1_CMPMEN_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_EINT1_CMPMEN_STABLE_SFT 8 +#define ACCDET_EINT1_CMPMEN_STABLE_MASK 0x1 +#define ACCDET_EINT1_CMPMEN_STABLE_MASK_SFT (0x1 << 8) +#define ACCDET_EINT1_CTURBO_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_EINT1_CTURBO_STABLE_SFT 9 +#define ACCDET_EINT1_CTURBO_STABLE_MASK 0x1 +#define ACCDET_EINT1_CTURBO_STABLE_MASK_SFT (0x1 << 9) +#define ACCDET_EINT1_CEN_STABLE_ADDR \ + MT6359_ACCDET_CON19 +#define ACCDET_EINT1_CEN_STABLE_SFT 10 +#define ACCDET_EINT1_CEN_STABLE_MASK 0x1 +#define ACCDET_EINT1_CEN_STABLE_MASK_SFT (0x1 << 10) +#define ACCDET_HWMODE_EN_ADDR \ + MT6359_ACCDET_CON20 +#define ACCDET_HWMODE_EN_SFT 0 +#define ACCDET_HWMODE_EN_MASK 0x1 +#define ACCDET_HWMODE_EN_MASK_SFT (0x1 << 0) +#define ACCDET_HWMODE_SEL_ADDR \ + MT6359_ACCDET_CON20 +#define ACCDET_HWMODE_SEL_SFT 1 +#define ACCDET_HWMODE_SEL_MASK 0x3 +#define ACCDET_HWMODE_SEL_MASK_SFT (0x3 << 1) +#define ACCDET_PLUG_OUT_DETECT_ADDR \ + MT6359_ACCDET_CON20 +#define ACCDET_PLUG_OUT_DETECT_SFT 3 +#define ACCDET_PLUG_OUT_DETECT_MASK 0x1 +#define ACCDET_PLUG_OUT_DETECT_MASK_SFT (0x1 << 3) +#define ACCDET_EINT0_REVERSE_ADDR \ + MT6359_ACCDET_CON20 +#define ACCDET_EINT0_REVERSE_SFT 4 +#define ACCDET_EINT0_REVERSE_MASK 0x1 +#define ACCDET_EINT0_REVERSE_MASK_SFT (0x1 << 4) +#define ACCDET_EINT1_REVERSE_ADDR \ + MT6359_ACCDET_CON20 +#define ACCDET_EINT1_REVERSE_SFT 5 +#define ACCDET_EINT1_REVERSE_MASK 0x1 +#define ACCDET_EINT1_REVERSE_MASK_SFT (0x1 << 5) +#define ACCDET_EINT_HWMODE_EN_ADDR \ + MT6359_ACCDET_CON20 +#define ACCDET_EINT_HWMODE_EN_SFT 8 +#define ACCDET_EINT_HWMODE_EN_MASK 0x1 +#define ACCDET_EINT_HWMODE_EN_MASK_SFT (0x1 << 8) +#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_ADDR \ + MT6359_ACCDET_CON20 +#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_SFT 9 +#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_MASK 0x1 +#define ACCDET_EINT_PLUG_OUT_BYPASS_DEB_MASK_SFT (0x1 << 9) +#define ACCDET_EINT_M_PLUG_IN_EN_ADDR \ + MT6359_ACCDET_CON20 +#define ACCDET_EINT_M_PLUG_IN_EN_SFT 10 +#define ACCDET_EINT_M_PLUG_IN_EN_MASK 0x1 +#define ACCDET_EINT_M_PLUG_IN_EN_MASK_SFT (0x1 << 10) +#define ACCDET_EINT_M_HWMODE_EN_ADDR \ + MT6359_ACCDET_CON20 +#define ACCDET_EINT_M_HWMODE_EN_SFT 11 +#define ACCDET_EINT_M_HWMODE_EN_MASK 0x1 +#define ACCDET_EINT_M_HWMODE_EN_MASK_SFT (0x1 << 11) +#define ACCDET_TEST_CMPEN_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_TEST_CMPEN_SFT 0 +#define ACCDET_TEST_CMPEN_MASK 0x1 +#define ACCDET_TEST_CMPEN_MASK_SFT (0x1 << 0) +#define ACCDET_TEST_VTHEN_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_TEST_VTHEN_SFT 1 +#define ACCDET_TEST_VTHEN_MASK 0x1 +#define ACCDET_TEST_VTHEN_MASK_SFT (0x1 << 1) +#define ACCDET_TEST_MBIASEN_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_TEST_MBIASEN_SFT 2 +#define ACCDET_TEST_MBIASEN_MASK 0x1 +#define ACCDET_TEST_MBIASEN_MASK_SFT (0x1 << 2) +#define ACCDET_EINT_TEST_EN_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_EINT_TEST_EN_SFT 3 +#define ACCDET_EINT_TEST_EN_MASK 0x1 +#define ACCDET_EINT_TEST_EN_MASK_SFT (0x1 << 3) +#define ACCDET_EINT_TEST_INVEN_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_EINT_TEST_INVEN_SFT 4 +#define ACCDET_EINT_TEST_INVEN_MASK 0x1 +#define ACCDET_EINT_TEST_INVEN_MASK_SFT (0x1 << 4) +#define ACCDET_EINT_TEST_CMPEN_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_EINT_TEST_CMPEN_SFT 5 +#define ACCDET_EINT_TEST_CMPEN_MASK 0x1 +#define ACCDET_EINT_TEST_CMPEN_MASK_SFT (0x1 << 5) +#define ACCDET_EINT_TEST_CMPMEN_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_EINT_TEST_CMPMEN_SFT 6 +#define ACCDET_EINT_TEST_CMPMEN_MASK 0x1 +#define ACCDET_EINT_TEST_CMPMEN_MASK_SFT (0x1 << 6) +#define ACCDET_EINT_TEST_CTURBO_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_EINT_TEST_CTURBO_SFT 7 +#define ACCDET_EINT_TEST_CTURBO_MASK 0x1 +#define ACCDET_EINT_TEST_CTURBO_MASK_SFT (0x1 << 7) +#define ACCDET_EINT_TEST_CEN_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_EINT_TEST_CEN_SFT 8 +#define ACCDET_EINT_TEST_CEN_MASK 0x1 +#define ACCDET_EINT_TEST_CEN_MASK_SFT (0x1 << 8) +#define ACCDET_TEST_B_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_TEST_B_SFT 9 +#define ACCDET_TEST_B_MASK 0x1 +#define ACCDET_TEST_B_MASK_SFT (0x1 << 9) +#define ACCDET_TEST_A_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_TEST_A_SFT 10 +#define ACCDET_TEST_A_MASK 0x1 +#define ACCDET_TEST_A_MASK_SFT (0x1 << 10) +#define ACCDET_EINT_TEST_CMPOUT_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_EINT_TEST_CMPOUT_SFT 11 +#define ACCDET_EINT_TEST_CMPOUT_MASK 0x1 +#define ACCDET_EINT_TEST_CMPOUT_MASK_SFT (0x1 << 11) +#define ACCDET_EINT_TEST_CMPMOUT_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_EINT_TEST_CMPMOUT_SFT 12 +#define ACCDET_EINT_TEST_CMPMOUT_MASK 0x1 +#define ACCDET_EINT_TEST_CMPMOUT_MASK_SFT (0x1 << 12) +#define ACCDET_EINT_TEST_INVOUT_ADDR \ + MT6359_ACCDET_CON21 +#define ACCDET_EINT_TEST_INVOUT_SFT 13 +#define ACCDET_EINT_TEST_INVOUT_MASK 0x1 +#define ACCDET_EINT_TEST_INVOUT_MASK_SFT (0x1 << 13) +#define ACCDET_CMPEN_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_CMPEN_SEL_SFT 0 +#define ACCDET_CMPEN_SEL_MASK 0x1 +#define ACCDET_CMPEN_SEL_MASK_SFT (0x1 << 0) +#define ACCDET_VTHEN_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_VTHEN_SEL_SFT 1 +#define ACCDET_VTHEN_SEL_MASK 0x1 +#define ACCDET_VTHEN_SEL_MASK_SFT (0x1 << 1) +#define ACCDET_MBIASEN_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_MBIASEN_SEL_SFT 2 +#define ACCDET_MBIASEN_SEL_MASK 0x1 +#define ACCDET_MBIASEN_SEL_MASK_SFT (0x1 << 2) +#define ACCDET_EINT_EN_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_EINT_EN_SEL_SFT 3 +#define ACCDET_EINT_EN_SEL_MASK 0x1 +#define ACCDET_EINT_EN_SEL_MASK_SFT (0x1 << 3) +#define ACCDET_EINT_INVEN_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_EINT_INVEN_SEL_SFT 4 +#define ACCDET_EINT_INVEN_SEL_MASK 0x1 +#define ACCDET_EINT_INVEN_SEL_MASK_SFT (0x1 << 4) +#define ACCDET_EINT_CMPEN_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_EINT_CMPEN_SEL_SFT 5 +#define ACCDET_EINT_CMPEN_SEL_MASK 0x1 +#define ACCDET_EINT_CMPEN_SEL_MASK_SFT (0x1 << 5) +#define ACCDET_EINT_CMPMEN_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_EINT_CMPMEN_SEL_SFT 6 +#define ACCDET_EINT_CMPMEN_SEL_MASK 0x1 +#define ACCDET_EINT_CMPMEN_SEL_MASK_SFT (0x1 << 6) +#define ACCDET_EINT_CTURBO_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_EINT_CTURBO_SEL_SFT 7 +#define ACCDET_EINT_CTURBO_SEL_MASK 0x1 +#define ACCDET_EINT_CTURBO_SEL_MASK_SFT (0x1 << 7) +#define ACCDET_B_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_B_SEL_SFT 9 +#define ACCDET_B_SEL_MASK 0x1 +#define ACCDET_B_SEL_MASK_SFT (0x1 << 9) +#define ACCDET_A_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_A_SEL_SFT 10 +#define ACCDET_A_SEL_MASK 0x1 +#define ACCDET_A_SEL_MASK_SFT (0x1 << 10) +#define ACCDET_EINT_CMPOUT_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_EINT_CMPOUT_SEL_SFT 11 +#define ACCDET_EINT_CMPOUT_SEL_MASK 0x1 +#define ACCDET_EINT_CMPOUT_SEL_MASK_SFT (0x1 << 11) +#define ACCDET_EINT_CMPMOUT_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_EINT_CMPMOUT_SEL_SFT 12 +#define ACCDET_EINT_CMPMOUT_SEL_MASK 0x1 +#define ACCDET_EINT_CMPMOUT_SEL_MASK_SFT (0x1 << 12) +#define ACCDET_EINT_INVOUT_SEL_ADDR \ + MT6359_ACCDET_CON22 +#define ACCDET_EINT_INVOUT_SEL_SFT 13 +#define ACCDET_EINT_INVOUT_SEL_MASK 0x1 +#define ACCDET_EINT_INVOUT_SEL_MASK_SFT (0x1 << 13) +#define ACCDET_CMPEN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_CMPEN_SW_SFT 0 +#define ACCDET_CMPEN_SW_MASK 0x1 +#define ACCDET_CMPEN_SW_MASK_SFT (0x1 << 0) +#define ACCDET_VTHEN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_VTHEN_SW_SFT 1 +#define ACCDET_VTHEN_SW_MASK 0x1 +#define ACCDET_VTHEN_SW_MASK_SFT (0x1 << 1) +#define ACCDET_MBIASEN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_MBIASEN_SW_SFT 2 +#define ACCDET_MBIASEN_SW_MASK 0x1 +#define ACCDET_MBIASEN_SW_MASK_SFT (0x1 << 2) +#define ACCDET_EINT0_EN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_EINT0_EN_SW_SFT 3 +#define ACCDET_EINT0_EN_SW_MASK 0x1 +#define ACCDET_EINT0_EN_SW_MASK_SFT (0x1 << 3) +#define ACCDET_EINT0_INVEN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_EINT0_INVEN_SW_SFT 4 +#define ACCDET_EINT0_INVEN_SW_MASK 0x1 +#define ACCDET_EINT0_INVEN_SW_MASK_SFT (0x1 << 4) +#define ACCDET_EINT0_CMPEN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_EINT0_CMPEN_SW_SFT 5 +#define ACCDET_EINT0_CMPEN_SW_MASK 0x1 +#define ACCDET_EINT0_CMPEN_SW_MASK_SFT (0x1 << 5) +#define ACCDET_EINT0_CMPMEN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_EINT0_CMPMEN_SW_SFT 6 +#define ACCDET_EINT0_CMPMEN_SW_MASK 0x1 +#define ACCDET_EINT0_CMPMEN_SW_MASK_SFT (0x1 << 6) +#define ACCDET_EINT0_CTURBO_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_EINT0_CTURBO_SW_SFT 7 +#define ACCDET_EINT0_CTURBO_SW_MASK 0x1 +#define ACCDET_EINT0_CTURBO_SW_MASK_SFT (0x1 << 7) +#define ACCDET_EINT1_EN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_EINT1_EN_SW_SFT 8 +#define ACCDET_EINT1_EN_SW_MASK 0x1 +#define ACCDET_EINT1_EN_SW_MASK_SFT (0x1 << 8) +#define ACCDET_EINT1_INVEN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_EINT1_INVEN_SW_SFT 9 +#define ACCDET_EINT1_INVEN_SW_MASK 0x1 +#define ACCDET_EINT1_INVEN_SW_MASK_SFT (0x1 << 9) +#define ACCDET_EINT1_CMPEN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_EINT1_CMPEN_SW_SFT 10 +#define ACCDET_EINT1_CMPEN_SW_MASK 0x1 +#define ACCDET_EINT1_CMPEN_SW_MASK_SFT (0x1 << 10) +#define ACCDET_EINT1_CMPMEN_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_EINT1_CMPMEN_SW_SFT 11 +#define ACCDET_EINT1_CMPMEN_SW_MASK 0x1 +#define ACCDET_EINT1_CMPMEN_SW_MASK_SFT (0x1 << 11) +#define ACCDET_EINT1_CTURBO_SW_ADDR \ + MT6359_ACCDET_CON23 +#define ACCDET_EINT1_CTURBO_SW_SFT 12 +#define ACCDET_EINT1_CTURBO_SW_MASK 0x1 +#define ACCDET_EINT1_CTURBO_SW_MASK_SFT (0x1 << 12) +#define ACCDET_B_SW_ADDR \ + MT6359_ACCDET_CON24 +#define ACCDET_B_SW_SFT 0 +#define ACCDET_B_SW_MASK 0x1 +#define ACCDET_B_SW_MASK_SFT (0x1 << 0) +#define ACCDET_A_SW_ADDR \ + MT6359_ACCDET_CON24 +#define ACCDET_A_SW_SFT 1 +#define ACCDET_A_SW_MASK 0x1 +#define ACCDET_A_SW_MASK_SFT (0x1 << 1) +#define ACCDET_EINT0_CMPOUT_SW_ADDR \ + MT6359_ACCDET_CON24 +#define ACCDET_EINT0_CMPOUT_SW_SFT 2 +#define ACCDET_EINT0_CMPOUT_SW_MASK 0x1 +#define ACCDET_EINT0_CMPOUT_SW_MASK_SFT (0x1 << 2) +#define ACCDET_EINT0_CMPMOUT_SW_ADDR \ + MT6359_ACCDET_CON24 +#define ACCDET_EINT0_CMPMOUT_SW_SFT 3 +#define ACCDET_EINT0_CMPMOUT_SW_MASK 0x1 +#define ACCDET_EINT0_CMPMOUT_SW_MASK_SFT (0x1 << 3) +#define ACCDET_EINT0_INVOUT_SW_ADDR \ + MT6359_ACCDET_CON24 +#define ACCDET_EINT0_INVOUT_SW_SFT 4 +#define ACCDET_EINT0_INVOUT_SW_MASK 0x1 +#define ACCDET_EINT0_INVOUT_SW_MASK_SFT (0x1 << 4) +#define ACCDET_EINT1_CMPOUT_SW_ADDR \ + MT6359_ACCDET_CON24 +#define ACCDET_EINT1_CMPOUT_SW_SFT 5 +#define ACCDET_EINT1_CMPOUT_SW_MASK 0x1 +#define ACCDET_EINT1_CMPOUT_SW_MASK_SFT (0x1 << 5) +#define ACCDET_EINT1_CMPMOUT_SW_ADDR \ + MT6359_ACCDET_CON24 +#define ACCDET_EINT1_CMPMOUT_SW_SFT 6 +#define ACCDET_EINT1_CMPMOUT_SW_MASK 0x1 +#define ACCDET_EINT1_CMPMOUT_SW_MASK_SFT (0x1 << 6) +#define ACCDET_EINT1_INVOUT_SW_ADDR \ + MT6359_ACCDET_CON24 +#define ACCDET_EINT1_INVOUT_SW_SFT 7 +#define ACCDET_EINT1_INVOUT_SW_MASK 0x1 +#define ACCDET_EINT1_INVOUT_SW_MASK_SFT (0x1 << 7) +#define AD_AUDACCDETCMPOB_ADDR \ + MT6359_ACCDET_CON25 +#define AD_AUDACCDETCMPOB_SFT 0 +#define AD_AUDACCDETCMPOB_MASK 0x1 +#define AD_AUDACCDETCMPOB_MASK_SFT (0x1 << 0) +#define AD_AUDACCDETCMPOA_ADDR \ + MT6359_ACCDET_CON25 +#define AD_AUDACCDETCMPOA_SFT 1 +#define AD_AUDACCDETCMPOA_MASK 0x1 +#define AD_AUDACCDETCMPOA_MASK_SFT (0x1 << 1) +#define ACCDET_CUR_IN_ADDR \ + MT6359_ACCDET_CON25 +#define ACCDET_CUR_IN_SFT 2 +#define ACCDET_CUR_IN_MASK 0x3 +#define ACCDET_CUR_IN_MASK_SFT (0x3 << 2) +#define ACCDET_SAM_IN_ADDR \ + MT6359_ACCDET_CON25 +#define ACCDET_SAM_IN_SFT 4 +#define ACCDET_SAM_IN_MASK 0x3 +#define ACCDET_SAM_IN_MASK_SFT (0x3 << 4) +#define ACCDET_MEM_IN_ADDR \ + MT6359_ACCDET_CON25 +#define ACCDET_MEM_IN_SFT 6 +#define ACCDET_MEM_IN_MASK 0x3 +#define ACCDET_MEM_IN_MASK_SFT (0x3 << 6) +#define ACCDET_STATE_ADDR \ + MT6359_ACCDET_CON25 +#define ACCDET_STATE_SFT 8 +#define ACCDET_STATE_MASK 0x7 +#define ACCDET_STATE_MASK_SFT (0x7 << 8) +#define DA_AUDACCDETMBIASCLK_ADDR \ + MT6359_ACCDET_CON25 +#define DA_AUDACCDETMBIASCLK_SFT 12 +#define DA_AUDACCDETMBIASCLK_MASK 0x1 +#define DA_AUDACCDETMBIASCLK_MASK_SFT (0x1 << 12) +#define DA_AUDACCDETVTHCLK_ADDR \ + MT6359_ACCDET_CON25 +#define DA_AUDACCDETVTHCLK_SFT 13 +#define DA_AUDACCDETVTHCLK_MASK 0x1 +#define DA_AUDACCDETVTHCLK_MASK_SFT (0x1 << 13) +#define DA_AUDACCDETCMPCLK_ADDR \ + MT6359_ACCDET_CON25 +#define DA_AUDACCDETCMPCLK_SFT 14 +#define DA_AUDACCDETCMPCLK_MASK 0x1 +#define DA_AUDACCDETCMPCLK_MASK_SFT (0x1 << 14) +#define DA_AUDACCDETAUXADCSWCTRL_ADDR \ + MT6359_ACCDET_CON25 +#define DA_AUDACCDETAUXADCSWCTRL_SFT 15 +#define DA_AUDACCDETAUXADCSWCTRL_MASK 0x1 +#define DA_AUDACCDETAUXADCSWCTRL_MASK_SFT (0x1 << 15) +#define AD_EINT0CMPMOUT_ADDR \ + MT6359_ACCDET_CON26 +#define AD_EINT0CMPMOUT_SFT 0 +#define AD_EINT0CMPMOUT_MASK 0x1 +#define AD_EINT0CMPMOUT_MASK_SFT (0x1 << 0) +#define AD_EINT0CMPOUT_ADDR \ + MT6359_ACCDET_CON26 +#define AD_EINT0CMPOUT_SFT 1 +#define AD_EINT0CMPOUT_MASK 0x1 +#define AD_EINT0CMPOUT_MASK_SFT (0x1 << 1) +#define ACCDET_EINT0_CUR_IN_ADDR \ + MT6359_ACCDET_CON26 +#define ACCDET_EINT0_CUR_IN_SFT 2 +#define ACCDET_EINT0_CUR_IN_MASK 0x3 +#define ACCDET_EINT0_CUR_IN_MASK_SFT (0x3 << 2) +#define ACCDET_EINT0_SAM_IN_ADDR \ + MT6359_ACCDET_CON26 +#define ACCDET_EINT0_SAM_IN_SFT 4 +#define ACCDET_EINT0_SAM_IN_MASK 0x3 +#define ACCDET_EINT0_SAM_IN_MASK_SFT (0x3 << 4) +#define ACCDET_EINT0_MEM_IN_ADDR \ + MT6359_ACCDET_CON26 +#define ACCDET_EINT0_MEM_IN_SFT 6 +#define ACCDET_EINT0_MEM_IN_MASK 0x3 +#define ACCDET_EINT0_MEM_IN_MASK_SFT (0x3 << 6) +#define ACCDET_EINT0_STATE_ADDR \ + MT6359_ACCDET_CON26 +#define ACCDET_EINT0_STATE_SFT 8 +#define ACCDET_EINT0_STATE_MASK 0x7 +#define ACCDET_EINT0_STATE_MASK_SFT (0x7 << 8) +#define DA_EINT0CMPEN_ADDR \ + MT6359_ACCDET_CON26 +#define DA_EINT0CMPEN_SFT 13 +#define DA_EINT0CMPEN_MASK 0x1 +#define DA_EINT0CMPEN_MASK_SFT (0x1 << 13) +#define DA_EINT0CMPMEN_ADDR \ + MT6359_ACCDET_CON26 +#define DA_EINT0CMPMEN_SFT 14 +#define DA_EINT0CMPMEN_MASK 0x1 +#define DA_EINT0CMPMEN_MASK_SFT (0x1 << 14) +#define DA_EINT0CTURBO_ADDR \ + MT6359_ACCDET_CON26 +#define DA_EINT0CTURBO_SFT 15 +#define DA_EINT0CTURBO_MASK 0x1 +#define DA_EINT0CTURBO_MASK_SFT (0x1 << 15) +#define AD_EINT1CMPMOUT_ADDR \ + MT6359_ACCDET_CON27 +#define AD_EINT1CMPMOUT_SFT 0 +#define AD_EINT1CMPMOUT_MASK 0x1 +#define AD_EINT1CMPMOUT_MASK_SFT (0x1 << 0) +#define AD_EINT1CMPOUT_ADDR \ + MT6359_ACCDET_CON27 +#define AD_EINT1CMPOUT_SFT 1 +#define AD_EINT1CMPOUT_MASK 0x1 +#define AD_EINT1CMPOUT_MASK_SFT (0x1 << 1) +#define ACCDET_EINT1_CUR_IN_ADDR \ + MT6359_ACCDET_CON27 +#define ACCDET_EINT1_CUR_IN_SFT 2 +#define ACCDET_EINT1_CUR_IN_MASK 0x3 +#define ACCDET_EINT1_CUR_IN_MASK_SFT (0x3 << 2) +#define ACCDET_EINT1_SAM_IN_ADDR \ + MT6359_ACCDET_CON27 +#define ACCDET_EINT1_SAM_IN_SFT 4 +#define ACCDET_EINT1_SAM_IN_MASK 0x3 +#define ACCDET_EINT1_SAM_IN_MASK_SFT (0x3 << 4) +#define ACCDET_EINT1_MEM_IN_ADDR \ + MT6359_ACCDET_CON27 +#define ACCDET_EINT1_MEM_IN_SFT 6 +#define ACCDET_EINT1_MEM_IN_MASK 0x3 +#define ACCDET_EINT1_MEM_IN_MASK_SFT (0x3 << 6) +#define ACCDET_EINT1_STATE_ADDR \ + MT6359_ACCDET_CON27 +#define ACCDET_EINT1_STATE_SFT 8 +#define ACCDET_EINT1_STATE_MASK 0x7 +#define ACCDET_EINT1_STATE_MASK_SFT (0x7 << 8) +#define DA_EINT1CMPEN_ADDR \ + MT6359_ACCDET_CON27 +#define DA_EINT1CMPEN_SFT 13 +#define DA_EINT1CMPEN_MASK 0x1 +#define DA_EINT1CMPEN_MASK_SFT (0x1 << 13) +#define DA_EINT1CMPMEN_ADDR \ + MT6359_ACCDET_CON27 +#define DA_EINT1CMPMEN_SFT 14 +#define DA_EINT1CMPMEN_MASK 0x1 +#define DA_EINT1CMPMEN_MASK_SFT (0x1 << 14) +#define DA_EINT1CTURBO_ADDR \ + MT6359_ACCDET_CON27 +#define DA_EINT1CTURBO_SFT 15 +#define DA_EINT1CTURBO_MASK 0x1 +#define DA_EINT1CTURBO_MASK_SFT (0x1 << 15) +#define AD_EINT0INVOUT_ADDR \ + MT6359_ACCDET_CON28 +#define AD_EINT0INVOUT_SFT 0 +#define AD_EINT0INVOUT_MASK 0x1 +#define AD_EINT0INVOUT_MASK_SFT (0x1 << 0) +#define ACCDET_EINT0_INVERTER_CUR_IN_ADDR \ + MT6359_ACCDET_CON28 +#define ACCDET_EINT0_INVERTER_CUR_IN_SFT 1 +#define ACCDET_EINT0_INVERTER_CUR_IN_MASK 0x1 +#define ACCDET_EINT0_INVERTER_CUR_IN_MASK_SFT (0x1 << 1) +#define ACCDET_EINT0_INVERTER_SAM_IN_ADDR \ + MT6359_ACCDET_CON28 +#define ACCDET_EINT0_INVERTER_SAM_IN_SFT 2 +#define ACCDET_EINT0_INVERTER_SAM_IN_MASK 0x1 +#define ACCDET_EINT0_INVERTER_SAM_IN_MASK_SFT (0x1 << 2) +#define ACCDET_EINT0_INVERTER_MEM_IN_ADDR \ + MT6359_ACCDET_CON28 +#define ACCDET_EINT0_INVERTER_MEM_IN_SFT 3 +#define ACCDET_EINT0_INVERTER_MEM_IN_MASK 0x1 +#define ACCDET_EINT0_INVERTER_MEM_IN_MASK_SFT (0x1 << 3) +#define ACCDET_EINT0_INVERTER_STATE_ADDR \ + MT6359_ACCDET_CON28 +#define ACCDET_EINT0_INVERTER_STATE_SFT 8 +#define ACCDET_EINT0_INVERTER_STATE_MASK 0x7 +#define ACCDET_EINT0_INVERTER_STATE_MASK_SFT (0x7 << 8) +#define DA_EINT0EN_ADDR \ + MT6359_ACCDET_CON28 +#define DA_EINT0EN_SFT 12 +#define DA_EINT0EN_MASK 0x1 +#define DA_EINT0EN_MASK_SFT (0x1 << 12) +#define DA_EINT0INVEN_ADDR \ + MT6359_ACCDET_CON28 +#define DA_EINT0INVEN_SFT 13 +#define DA_EINT0INVEN_MASK 0x1 +#define DA_EINT0INVEN_MASK_SFT (0x1 << 13) +#define DA_EINT0CEN_ADDR \ + MT6359_ACCDET_CON28 +#define DA_EINT0CEN_SFT 14 +#define DA_EINT0CEN_MASK 0x1 +#define DA_EINT0CEN_MASK_SFT (0x1 << 14) +#define AD_EINT1INVOUT_ADDR \ + MT6359_ACCDET_CON29 +#define AD_EINT1INVOUT_SFT 0 +#define AD_EINT1INVOUT_MASK 0x1 +#define AD_EINT1INVOUT_MASK_SFT (0x1 << 0) +#define ACCDET_EINT1_INVERTER_CUR_IN_ADDR \ + MT6359_ACCDET_CON29 +#define ACCDET_EINT1_INVERTER_CUR_IN_SFT 1 +#define ACCDET_EINT1_INVERTER_CUR_IN_MASK 0x1 +#define ACCDET_EINT1_INVERTER_CUR_IN_MASK_SFT (0x1 << 1) +#define ACCDET_EINT1_INVERTER_SAM_IN_ADDR \ + MT6359_ACCDET_CON29 +#define ACCDET_EINT1_INVERTER_SAM_IN_SFT 2 +#define ACCDET_EINT1_INVERTER_SAM_IN_MASK 0x1 +#define ACCDET_EINT1_INVERTER_SAM_IN_MASK_SFT (0x1 << 2) +#define ACCDET_EINT1_INVERTER_MEM_IN_ADDR \ + MT6359_ACCDET_CON29 +#define ACCDET_EINT1_INVERTER_MEM_IN_SFT 3 +#define ACCDET_EINT1_INVERTER_MEM_IN_MASK 0x1 +#define ACCDET_EINT1_INVERTER_MEM_IN_MASK_SFT (0x1 << 3) +#define ACCDET_EINT1_INVERTER_STATE_ADDR \ + MT6359_ACCDET_CON29 +#define ACCDET_EINT1_INVERTER_STATE_SFT 8 +#define ACCDET_EINT1_INVERTER_STATE_MASK 0x7 +#define ACCDET_EINT1_INVERTER_STATE_MASK_SFT (0x7 << 8) +#define DA_EINT1EN_ADDR \ + MT6359_ACCDET_CON29 +#define DA_EINT1EN_SFT 12 +#define DA_EINT1EN_MASK 0x1 +#define DA_EINT1EN_MASK_SFT (0x1 << 12) +#define DA_EINT1INVEN_ADDR \ + MT6359_ACCDET_CON29 +#define DA_EINT1INVEN_SFT 13 +#define DA_EINT1INVEN_MASK 0x1 +#define DA_EINT1INVEN_MASK_SFT (0x1 << 13) +#define DA_EINT1CEN_ADDR \ + MT6359_ACCDET_CON29 +#define DA_EINT1CEN_SFT 14 +#define DA_EINT1CEN_MASK 0x1 +#define DA_EINT1CEN_MASK_SFT (0x1 << 14) +#define ACCDET_EN_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EN_SFT 0 +#define ACCDET_EN_MASK 0x1 +#define ACCDET_EN_MASK_SFT (0x1 << 0) +#define ACCDET_EINT0_EN_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EINT0_EN_SFT 1 +#define ACCDET_EINT0_EN_MASK 0x1 +#define ACCDET_EINT0_EN_MASK_SFT (0x1 << 1) +#define ACCDET_EINT1_EN_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EINT1_EN_SFT 2 +#define ACCDET_EINT1_EN_MASK 0x1 +#define ACCDET_EINT1_EN_MASK_SFT (0x1 << 2) +#define ACCDET_EINT0_M_EN_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EINT0_M_EN_SFT 3 +#define ACCDET_EINT0_M_EN_MASK 0x1 +#define ACCDET_EINT0_M_EN_MASK_SFT (0x1 << 3) +#define ACCDET_EINT0_DETECT_MOISTURE_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EINT0_DETECT_MOISTURE_SFT 4 +#define ACCDET_EINT0_DETECT_MOISTURE_MASK 0x1 +#define ACCDET_EINT0_DETECT_MOISTURE_MASK_SFT (0x1 << 4) +#define ACCDET_EINT0_PLUG_IN_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EINT0_PLUG_IN_SFT 5 +#define ACCDET_EINT0_PLUG_IN_MASK 0x1 +#define ACCDET_EINT0_PLUG_IN_MASK_SFT (0x1 << 5) +#define ACCDET_EINT0_M_PLUG_IN_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EINT0_M_PLUG_IN_SFT 6 +#define ACCDET_EINT0_M_PLUG_IN_MASK 0x1 +#define ACCDET_EINT0_M_PLUG_IN_MASK_SFT (0x1 << 6) +#define ACCDET_EINT1_M_EN_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EINT1_M_EN_SFT 7 +#define ACCDET_EINT1_M_EN_MASK 0x1 +#define ACCDET_EINT1_M_EN_MASK_SFT (0x1 << 7) +#define ACCDET_EINT1_DETECT_MOISTURE_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EINT1_DETECT_MOISTURE_SFT 8 +#define ACCDET_EINT1_DETECT_MOISTURE_MASK 0x1 +#define ACCDET_EINT1_DETECT_MOISTURE_MASK_SFT (0x1 << 8) +#define ACCDET_EINT1_PLUG_IN_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EINT1_PLUG_IN_SFT 9 +#define ACCDET_EINT1_PLUG_IN_MASK 0x1 +#define ACCDET_EINT1_PLUG_IN_MASK_SFT (0x1 << 9) +#define ACCDET_EINT1_M_PLUG_IN_ADDR \ + MT6359_ACCDET_CON30 +#define ACCDET_EINT1_M_PLUG_IN_SFT 10 +#define ACCDET_EINT1_M_PLUG_IN_MASK 0x1 +#define ACCDET_EINT1_M_PLUG_IN_MASK_SFT (0x1 << 10) +#define ACCDET_CUR_DEB_ADDR \ + MT6359_ACCDET_CON31 +#define ACCDET_CUR_DEB_SFT 0 +#define ACCDET_CUR_DEB_MASK 0xFFFF +#define ACCDET_CUR_DEB_MASK_SFT (0xFFFF << 0) +#define ACCDET_EINT0_CUR_DEB_ADDR \ + MT6359_ACCDET_CON32 +#define ACCDET_EINT0_CUR_DEB_SFT 0 +#define ACCDET_EINT0_CUR_DEB_MASK 0x7FFF +#define ACCDET_EINT0_CUR_DEB_MASK_SFT (0x7FFF << 0) +#define ACCDET_EINT1_CUR_DEB_ADDR \ + MT6359_ACCDET_CON33 +#define ACCDET_EINT1_CUR_DEB_SFT 0 +#define ACCDET_EINT1_CUR_DEB_MASK 0x7FFF +#define ACCDET_EINT1_CUR_DEB_MASK_SFT (0x7FFF << 0) +#define ACCDET_EINT0_INVERTER_CUR_DEB_ADDR \ + MT6359_ACCDET_CON34 +#define ACCDET_EINT0_INVERTER_CUR_DEB_SFT 0 +#define ACCDET_EINT0_INVERTER_CUR_DEB_MASK 0x7FFF +#define ACCDET_EINT0_INVERTER_CUR_DEB_MASK_SFT (0x7FFF << 0) +#define ACCDET_EINT1_INVERTER_CUR_DEB_ADDR \ + MT6359_ACCDET_CON35 +#define ACCDET_EINT1_INVERTER_CUR_DEB_SFT 0 +#define ACCDET_EINT1_INVERTER_CUR_DEB_MASK 0x7FFF +#define ACCDET_EINT1_INVERTER_CUR_DEB_MASK_SFT (0x7FFF << 0) +#define AD_AUDACCDETCMPOB_MON_ADDR \ + MT6359_ACCDET_CON36 +#define AD_AUDACCDETCMPOB_MON_SFT 0 +#define AD_AUDACCDETCMPOB_MON_MASK 0x1 +#define AD_AUDACCDETCMPOB_MON_MASK_SFT (0x1 << 0) +#define AD_AUDACCDETCMPOA_MON_ADDR \ + MT6359_ACCDET_CON36 +#define AD_AUDACCDETCMPOA_MON_SFT 1 +#define AD_AUDACCDETCMPOA_MON_MASK 0x1 +#define AD_AUDACCDETCMPOA_MON_MASK_SFT (0x1 << 1) +#define AD_EINT0CMPMOUT_MON_ADDR \ + MT6359_ACCDET_CON36 +#define AD_EINT0CMPMOUT_MON_SFT 2 +#define AD_EINT0CMPMOUT_MON_MASK 0x1 +#define AD_EINT0CMPMOUT_MON_MASK_SFT (0x1 << 2) +#define AD_EINT0CMPOUT_MON_ADDR \ + MT6359_ACCDET_CON36 +#define AD_EINT0CMPOUT_MON_SFT 3 +#define AD_EINT0CMPOUT_MON_MASK 0x1 +#define AD_EINT0CMPOUT_MON_MASK_SFT (0x1 << 3) +#define AD_EINT0INVOUT_MON_ADDR \ + MT6359_ACCDET_CON36 +#define AD_EINT0INVOUT_MON_SFT 4 +#define AD_EINT0INVOUT_MON_MASK 0x1 +#define AD_EINT0INVOUT_MON_MASK_SFT (0x1 << 4) +#define AD_EINT1CMPMOUT_MON_ADDR \ + MT6359_ACCDET_CON36 +#define AD_EINT1CMPMOUT_MON_SFT 5 +#define AD_EINT1CMPMOUT_MON_MASK 0x1 +#define AD_EINT1CMPMOUT_MON_MASK_SFT (0x1 << 5) +#define AD_EINT1CMPOUT_MON_ADDR \ + MT6359_ACCDET_CON36 +#define AD_EINT1CMPOUT_MON_SFT 6 +#define AD_EINT1CMPOUT_MON_MASK 0x1 +#define AD_EINT1CMPOUT_MON_MASK_SFT (0x1 << 6) +#define AD_EINT1INVOUT_MON_ADDR \ + MT6359_ACCDET_CON36 +#define AD_EINT1INVOUT_MON_SFT 7 +#define AD_EINT1INVOUT_MON_MASK 0x1 +#define AD_EINT1INVOUT_MON_MASK_SFT (0x1 << 7) +#define DA_AUDACCDETCMPCLK_MON_ADDR \ + MT6359_ACCDET_CON37 +#define DA_AUDACCDETCMPCLK_MON_SFT 0 +#define DA_AUDACCDETCMPCLK_MON_MASK 0x1 +#define DA_AUDACCDETCMPCLK_MON_MASK_SFT (0x1 << 0) +#define DA_AUDACCDETVTHCLK_MON_ADDR \ + MT6359_ACCDET_CON37 +#define DA_AUDACCDETVTHCLK_MON_SFT 1 +#define DA_AUDACCDETVTHCLK_MON_MASK 0x1 +#define DA_AUDACCDETVTHCLK_MON_MASK_SFT (0x1 << 1) +#define DA_AUDACCDETMBIASCLK_MON_ADDR \ + MT6359_ACCDET_CON37 +#define DA_AUDACCDETMBIASCLK_MON_SFT 2 +#define DA_AUDACCDETMBIASCLK_MON_MASK 0x1 +#define DA_AUDACCDETMBIASCLK_MON_MASK_SFT (0x1 << 2) +#define DA_AUDACCDETAUXADCSWCTRL_MON_ADDR \ + MT6359_ACCDET_CON37 +#define DA_AUDACCDETAUXADCSWCTRL_MON_SFT 3 +#define DA_AUDACCDETAUXADCSWCTRL_MON_MASK 0x1 +#define DA_AUDACCDETAUXADCSWCTRL_MON_MASK_SFT (0x1 << 3) +#define DA_EINT0CTURBO_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT0CTURBO_MON_SFT 0 +#define DA_EINT0CTURBO_MON_MASK 0x1 +#define DA_EINT0CTURBO_MON_MASK_SFT (0x1 << 0) +#define DA_EINT0CMPMEN_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT0CMPMEN_MON_SFT 1 +#define DA_EINT0CMPMEN_MON_MASK 0x1 +#define DA_EINT0CMPMEN_MON_MASK_SFT (0x1 << 1) +#define DA_EINT0CMPEN_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT0CMPEN_MON_SFT 2 +#define DA_EINT0CMPEN_MON_MASK 0x1 +#define DA_EINT0CMPEN_MON_MASK_SFT (0x1 << 2) +#define DA_EINT0INVEN_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT0INVEN_MON_SFT 3 +#define DA_EINT0INVEN_MON_MASK 0x1 +#define DA_EINT0INVEN_MON_MASK_SFT (0x1 << 3) +#define DA_EINT0CEN_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT0CEN_MON_SFT 4 +#define DA_EINT0CEN_MON_MASK 0x1 +#define DA_EINT0CEN_MON_MASK_SFT (0x1 << 4) +#define DA_EINT0EN_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT0EN_MON_SFT 5 +#define DA_EINT0EN_MON_MASK 0x1 +#define DA_EINT0EN_MON_MASK_SFT (0x1 << 5) +#define DA_EINT1CTURBO_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT1CTURBO_MON_SFT 8 +#define DA_EINT1CTURBO_MON_MASK 0x1 +#define DA_EINT1CTURBO_MON_MASK_SFT (0x1 << 8) +#define DA_EINT1CMPMEN_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT1CMPMEN_MON_SFT 9 +#define DA_EINT1CMPMEN_MON_MASK 0x1 +#define DA_EINT1CMPMEN_MON_MASK_SFT (0x1 << 9) +#define DA_EINT1CMPEN_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT1CMPEN_MON_SFT 10 +#define DA_EINT1CMPEN_MON_MASK 0x1 +#define DA_EINT1CMPEN_MON_MASK_SFT (0x1 << 10) +#define DA_EINT1INVEN_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT1INVEN_MON_SFT 11 +#define DA_EINT1INVEN_MON_MASK 0x1 +#define DA_EINT1INVEN_MON_MASK_SFT (0x1 << 11) +#define DA_EINT1CEN_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT1CEN_MON_SFT 12 +#define DA_EINT1CEN_MON_MASK 0x1 +#define DA_EINT1CEN_MON_MASK_SFT (0x1 << 12) +#define DA_EINT1EN_MON_ADDR \ + MT6359_ACCDET_CON38 +#define DA_EINT1EN_MON_SFT 13 +#define DA_EINT1EN_MON_MASK 0x1 +#define DA_EINT1EN_MON_MASK_SFT (0x1 << 13) +#define ACCDET_EINT0_M_PLUG_IN_COUNT_ADDR \ + MT6359_ACCDET_CON39 +#define ACCDET_EINT0_M_PLUG_IN_COUNT_SFT 0 +#define ACCDET_EINT0_M_PLUG_IN_COUNT_MASK 0x7 +#define ACCDET_EINT0_M_PLUG_IN_COUNT_MASK_SFT (0x7 << 0) +#define ACCDET_EINT1_M_PLUG_IN_COUNT_ADDR \ + MT6359_ACCDET_CON39 +#define ACCDET_EINT1_M_PLUG_IN_COUNT_SFT 4 +#define ACCDET_EINT1_M_PLUG_IN_COUNT_MASK 0x7 +#define ACCDET_EINT1_M_PLUG_IN_COUNT_MASK_SFT (0x7 << 4) +#define ACCDET_MON_FLAG_EN_ADDR \ + MT6359_ACCDET_CON40 +#define ACCDET_MON_FLAG_EN_SFT 0 +#define ACCDET_MON_FLAG_EN_MASK 0x1 +#define ACCDET_MON_FLAG_EN_MASK_SFT (0x1 << 0) +#define ACCDET_MON_FLAG_SEL_ADDR \ + MT6359_ACCDET_CON40 +#define ACCDET_MON_FLAG_SEL_SFT 4 +#define ACCDET_MON_FLAG_SEL_MASK 0xF +#define ACCDET_MON_FLAG_SEL_MASK_SFT (0xF << 4) + +#define RG_AUDPWDBMICBIAS0_ADDR \ + MT6359_AUDENC_ANA_CON15 +#define RG_AUDPWDBMICBIAS0_SFT 0 +#define RG_AUDPWDBMICBIAS0_MASK 0x1 +#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMPLON_ADDR \ + MT6359_AUDENC_ANA_CON0 +#define RG_AUDPREAMPLON_SFT 0 +#define RG_AUDPREAMPLON_MASK 0x1 +#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0) +#define RG_CLKSQ_EN_ADDR \ + MT6359_AUDENC_ANA_CON23 +#define RG_CLKSQ_EN_SFT 0 +#define RG_CLKSQ_EN_MASK 0x1 +#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0) +#define RG_RTC32K_CK_PDN_ADDR \ + MT6359_TOP_CKPDN_CON0 +#define RG_RTC32K_CK_PDN_SFT 15 +#define RG_RTC32K_CK_PDN_MASK 0x1 +#define RG_RTC32K_CK_PDN_MASK_SFT (0x1 << 15) +#define RG_HPLOUTPUTSTBENH_VAUDP32_ADDR \ + MT6359_AUDDEC_ANA_CON2 +#define RG_HPLOUTPUTSTBENH_VAUDP32_SFT 0 +#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK 0x7 +#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 0) +#define AUXADC_RQST_CH5_ADDR \ + MT6359_AUXADC_RQST0 +#define AUXADC_RQST_CH5_SFT 5 +#define AUXADC_RQST_CH5_MASK 0x1 +#define AUXADC_RQST_CH5_MASK_SFT (0x1 << 5) +#define RG_LDO_VUSB_HW0_OP_EN_ADDR \ + MT6359_LDO_VUSB_OP_EN +#define RG_LDO_VUSB_HW0_OP_EN_SFT 0 +#define RG_LDO_VUSB_HW0_OP_EN_MASK 0x1 +#define RG_LDO_VUSB_HW0_OP_EN_MASK_SFT (0x1 << 0) +#define RG_HPROUTPUTSTBENH_VAUDP32_ADDR \ + MT6359_AUDDEC_ANA_CON2 +#define RG_HPROUTPUTSTBENH_VAUDP32_SFT 4 +#define RG_HPROUTPUTSTBENH_VAUDP32_MASK 0x7 +#define RG_HPROUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 4) +#define RG_NCP_PDDIS_EN_ADDR \ + MT6359_AFE_NCP_CFG2 +#define RG_NCP_PDDIS_EN_SFT 0 +#define RG_NCP_PDDIS_EN_MASK 0x1 +#define RG_NCP_PDDIS_EN_MASK_SFT (0x1 << 0) +#define RG_SCK32K_CK_PDN_ADDR \ + MT6359_TOP_CKPDN_CON0 +#define RG_SCK32K_CK_PDN_SFT 0 +#define RG_SCK32K_CK_PDN_MASK 0x1 +#define RG_SCK32K_CK_PDN_MASK_SFT (0x1 << 0) +/* AUDENC_ANA_CON18: */ +#define RG_ACCDET_MODE_ANA11_MODE1 (0x000F) +#define RG_ACCDET_MODE_ANA11_MODE2 (0x008F) +#define RG_ACCDET_MODE_ANA11_MODE6 (0x008F) + +/* AUXADC_ADC5: Auxadc CH5 read data */ +#define AUXADC_DATA_RDY_CH5 BIT(15) +#define AUXADC_DATA_PROCEED_CH5 BIT(15) +#define AUXADC_DATA_MASK (0x0FFF) + +/* AUXADC_RQST0_SET: Auxadc CH5 request, relevant 0x07EC */ +#define AUXADC_RQST_CH5_SET BIT(5) +/* AUXADC_RQST0_CLR: Auxadc CH5 request, relevant 0x07EC */ +#define AUXADC_RQST_CH5_CLR BIT(5) + +#define ACCDET_CALI_MASK0 (0xFF) +#define ACCDET_CALI_MASK1 (0xFF << 8) +#define ACCDET_CALI_MASK2 (0xFF) +#define ACCDET_CALI_MASK3 (0xFF << 8) +#define ACCDET_CALI_MASK4 (0xFF) + +#define ACCDET_EINT_IRQ_B2_B3 (0x03 << ACCDET_EINT0_IRQ_SFT) + +/* ACCDET_CON25: RO, accdet FSM state,etc.*/ +#define ACCDET_STATE_MEM_IN_OFFSET (ACCDET_MEM_IN_SFT) +#define ACCDET_STATE_AB_MASK (0x03) +#define ACCDET_STATE_AB_00 (0x00) +#define ACCDET_STATE_AB_01 (0x01) +#define ACCDET_STATE_AB_10 (0x02) +#define ACCDET_STATE_AB_11 (0x03) + +/* ACCDET_CON19 */ +#define ACCDET_EINT0_STABLE_VAL ((ACCDET_DA_STABLE_MASK_SFT) | \ + (ACCDET_EINT0_EN_STABLE_MASK_SFT) | \ + (ACCDET_EINT0_CMPEN_STABLE_MASK_SFT) | \ + (ACCDET_EINT0_CEN_STABLE_MASK_SFT)) + +#define ACCDET_EINT1_STABLE_VAL ((ACCDET_DA_STABLE_MASK_SFT) | \ + (ACCDET_EINT1_EN_STABLE_MASK_SFT) | \ + (ACCDET_EINT1_CMPEN_STABLE_MASK_SFT) | \ + (ACCDET_EINT1_CEN_STABLE_MASK_SFT)) +/* The following are used for mt6359.c */ +/* MT6359_DCXO_CW12 */ +#define RG_XO_AUDIO_EN_M_SFT 13 + +/* AUD_TOP_CKPDN_CON0 */ +#define RG_VOW13M_CK_PDN_SFT 13 +#define RG_VOW13M_CK_PDN_MASK 0x1 +#define RG_VOW13M_CK_PDN_MASK_SFT (0x1 << 13) +#define RG_VOW32K_CK_PDN_SFT 12 +#define RG_VOW32K_CK_PDN_MASK 0x1 +#define RG_VOW32K_CK_PDN_MASK_SFT (0x1 << 12) +#define RG_AUD_INTRP_CK_PDN_SFT 8 +#define RG_AUD_INTRP_CK_PDN_MASK 0x1 +#define RG_AUD_INTRP_CK_PDN_MASK_SFT (0x1 << 8) +#define RG_PAD_AUD_CLK_MISO_CK_PDN_SFT 7 +#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK 0x1 +#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK_SFT (0x1 << 7) +#define RG_AUDNCP_CK_PDN_SFT 6 +#define RG_AUDNCP_CK_PDN_MASK 0x1 +#define RG_AUDNCP_CK_PDN_MASK_SFT (0x1 << 6) +#define RG_ZCD13M_CK_PDN_SFT 5 +#define RG_ZCD13M_CK_PDN_MASK 0x1 +#define RG_ZCD13M_CK_PDN_MASK_SFT (0x1 << 5) +#define RG_AUDIF_CK_PDN_SFT 2 +#define RG_AUDIF_CK_PDN_MASK 0x1 +#define RG_AUDIF_CK_PDN_MASK_SFT (0x1 << 2) +#define RG_AUD_CK_PDN_SFT 1 +#define RG_AUD_CK_PDN_MASK 0x1 +#define RG_AUD_CK_PDN_MASK_SFT (0x1 << 1) +#define RG_ACCDET_CK_PDN_SFT 0 +#define RG_ACCDET_CK_PDN_MASK 0x1 +#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0) + +/* AUD_TOP_CKPDN_CON0_SET */ +#define RG_AUD_TOP_CKPDN_CON0_SET_SFT 0 +#define RG_AUD_TOP_CKPDN_CON0_SET_MASK 0x3fff +#define RG_AUD_TOP_CKPDN_CON0_SET_MASK_SFT (0x3fff << 0) + +/* AUD_TOP_CKPDN_CON0_CLR */ +#define RG_AUD_TOP_CKPDN_CON0_CLR_SFT 0 +#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK 0x3fff +#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK_SFT (0x3fff << 0) + +/* AUD_TOP_CKSEL_CON0 */ +#define RG_AUDIF_CK_CKSEL_SFT 3 +#define RG_AUDIF_CK_CKSEL_MASK 0x1 +#define RG_AUDIF_CK_CKSEL_MASK_SFT (0x1 << 3) +#define RG_AUD_CK_CKSEL_SFT 2 +#define RG_AUD_CK_CKSEL_MASK 0x1 +#define RG_AUD_CK_CKSEL_MASK_SFT (0x1 << 2) + +/* AUD_TOP_CKSEL_CON0_SET */ +#define RG_AUD_TOP_CKSEL_CON0_SET_SFT 0 +#define RG_AUD_TOP_CKSEL_CON0_SET_MASK 0xf +#define RG_AUD_TOP_CKSEL_CON0_SET_MASK_SFT (0xf << 0) + +/* AUD_TOP_CKSEL_CON0_CLR */ +#define RG_AUD_TOP_CKSEL_CON0_CLR_SFT 0 +#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK 0xf +#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK_SFT (0xf << 0) + +/* AUD_TOP_CKTST_CON0 */ +#define RG_VOW13M_CK_TSTSEL_SFT 9 +#define RG_VOW13M_CK_TSTSEL_MASK 0x1 +#define RG_VOW13M_CK_TSTSEL_MASK_SFT (0x1 << 9) +#define RG_VOW13M_CK_TST_DIS_SFT 8 +#define RG_VOW13M_CK_TST_DIS_MASK 0x1 +#define RG_VOW13M_CK_TST_DIS_MASK_SFT (0x1 << 8) +#define RG_AUD26M_CK_TSTSEL_SFT 4 +#define RG_AUD26M_CK_TSTSEL_MASK 0x1 +#define RG_AUD26M_CK_TSTSEL_MASK_SFT (0x1 << 4) +#define RG_AUDIF_CK_TSTSEL_SFT 3 +#define RG_AUDIF_CK_TSTSEL_MASK 0x1 +#define RG_AUDIF_CK_TSTSEL_MASK_SFT (0x1 << 3) +#define RG_AUD_CK_TSTSEL_SFT 2 +#define RG_AUD_CK_TSTSEL_MASK 0x1 +#define RG_AUD_CK_TSTSEL_MASK_SFT (0x1 << 2) +#define RG_AUD26M_CK_TST_DIS_SFT 0 +#define RG_AUD26M_CK_TST_DIS_MASK 0x1 +#define RG_AUD26M_CK_TST_DIS_MASK_SFT (0x1 << 0) + +/* AUD_TOP_CLK_HWEN_CON0 */ +#define RG_AUD_INTRP_CK_PDN_HWEN_SFT 0 +#define RG_AUD_INTRP_CK_PDN_HWEN_MASK 0x1 +#define RG_AUD_INTRP_CK_PDN_HWEN_MASK_SFT (0x1 << 0) + +/* AUD_TOP_CLK_HWEN_CON0_SET */ +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_SFT 0 +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK 0xffff +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK_SFT (0xffff << 0) + +/* AUD_TOP_CLK_HWEN_CON0_CLR */ +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_SFT 0 +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK 0xffff +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK_SFT (0xffff << 0) + +/* AUD_TOP_RST_CON0 */ +#define RG_AUDNCP_RST_SFT 3 +#define RG_AUDNCP_RST_MASK 0x1 +#define RG_AUDNCP_RST_MASK_SFT (0x1 << 3) +#define RG_ZCD_RST_SFT 2 +#define RG_ZCD_RST_MASK 0x1 +#define RG_ZCD_RST_MASK_SFT (0x1 << 2) +#define RG_ACCDET_RST_SFT 1 +#define RG_ACCDET_RST_MASK 0x1 +#define RG_ACCDET_RST_MASK_SFT (0x1 << 1) +#define RG_AUDIO_RST_SFT 0 +#define RG_AUDIO_RST_MASK 0x1 +#define RG_AUDIO_RST_MASK_SFT (0x1 << 0) + +/* AUD_TOP_RST_CON0_SET */ +#define RG_AUD_TOP_RST_CON0_SET_SFT 0 +#define RG_AUD_TOP_RST_CON0_SET_MASK 0xf +#define RG_AUD_TOP_RST_CON0_SET_MASK_SFT (0xf << 0) + +/* AUD_TOP_RST_CON0_CLR */ +#define RG_AUD_TOP_RST_CON0_CLR_SFT 0 +#define RG_AUD_TOP_RST_CON0_CLR_MASK 0xf +#define RG_AUD_TOP_RST_CON0_CLR_MASK_SFT (0xf << 0) + +/* AUD_TOP_RST_BANK_CON0 */ +#define BANK_AUDZCD_SWRST_SFT 2 +#define BANK_AUDZCD_SWRST_MASK 0x1 +#define BANK_AUDZCD_SWRST_MASK_SFT (0x1 << 2) +#define BANK_AUDIO_SWRST_SFT 1 +#define BANK_AUDIO_SWRST_MASK 0x1 +#define BANK_AUDIO_SWRST_MASK_SFT (0x1 << 1) +#define BANK_ACCDET_SWRST_SFT 0 +#define BANK_ACCDET_SWRST_MASK 0x1 +#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0) + +/* AFE_UL_DL_CON0 */ +#define AFE_UL_LR_SWAP_SFT 15 +#define AFE_UL_LR_SWAP_MASK 0x1 +#define AFE_UL_LR_SWAP_MASK_SFT (0x1 << 15) +#define AFE_DL_LR_SWAP_SFT 14 +#define AFE_DL_LR_SWAP_MASK 0x1 +#define AFE_DL_LR_SWAP_MASK_SFT (0x1 << 14) +#define AFE_ON_SFT 0 +#define AFE_ON_MASK 0x1 +#define AFE_ON_MASK_SFT (0x1 << 0) + +/* AFE_DL_SRC2_CON0_L */ +#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0) + +/* AFE_UL_SRC_CON0_H */ +#define C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11 +#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7 +#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11) +#define C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8 +#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7 +#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8) +#define C_TWO_DIGITAL_MIC_CTL_SFT 7 +#define C_TWO_DIGITAL_MIC_CTL_MASK 0x1 +#define C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7) + +/* AFE_UL_SRC_CON0_L */ +#define DMIC_LOW_POWER_MODE_CTL_SFT 14 +#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3 +#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14) +#define DIGMIC_4P33M_SEL_CTL_SFT 6 +#define DIGMIC_4P33M_SEL_CTL_MASK 0x1 +#define DIGMIC_4P33M_SEL_CTL_MASK_SFT (0x1 << 6) +#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5) +#define UL_LOOP_BACK_MODE_CTL_SFT 2 +#define UL_LOOP_BACK_MODE_CTL_MASK 0x1 +#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2) +#define UL_SDM_3_LEVEL_CTL_SFT 1 +#define UL_SDM_3_LEVEL_CTL_MASK 0x1 +#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1) +#define UL_SRC_ON_TMP_CTL_SFT 0 +#define UL_SRC_ON_TMP_CTL_MASK 0x1 +#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0) + +/* AFE_ADDA6_L_SRC_CON0_H */ +#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11 +#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7 +#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11) +#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8 +#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7 +#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8) +#define ADDA6_C_TWO_DIGITAL_MIC_CTL_SFT 7 +#define ADDA6_C_TWO_DIGITAL_MIC_CTL_MASK 0x1 +#define ADDA6_C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7) + +/* AFE_ADDA6_UL_SRC_CON0_L */ +#define ADDA6_DMIC_LOW_POWER_MODE_CTL_SFT 14 +#define ADDA6_DMIC_LOW_POWER_MODE_CTL_MASK 0x3 +#define ADDA6_DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14) +#define ADDA6_DIGMIC_4P33M_SEL_CTL_SFT 6 +#define ADDA6_DIGMIC_4P33M_SEL_CTL_MASK 0x1 +#define ADDA6_DIGMIC_4P33M_SEL_CTL_MASK_SFT (0x1 << 6) +#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5 +#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1 +#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5) +#define ADDA6_UL_LOOP_BACK_MODE_CTL_SFT 2 +#define ADDA6_UL_LOOP_BACK_MODE_CTL_MASK 0x1 +#define ADDA6_UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2) +#define ADDA6_UL_SDM_3_LEVEL_CTL_SFT 1 +#define ADDA6_UL_SDM_3_LEVEL_CTL_MASK 0x1 +#define ADDA6_UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1) +#define ADDA6_UL_SRC_ON_TMP_CTL_SFT 0 +#define ADDA6_UL_SRC_ON_TMP_CTL_MASK 0x1 +#define ADDA6_UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0) + +/* AFE_TOP_CON0 */ +#define ADDA6_MTKAIF_SINE_ON_SFT 4 +#define ADDA6_MTKAIF_SINE_ON_MASK 0x1 +#define ADDA6_MTKAIF_SINE_ON_MASK_SFT (0x1 << 4) +#define ADDA6_UL_SINE_ON_SFT 3 +#define ADDA6_UL_SINE_ON_MASK 0x1 +#define ADDA6_UL_SINE_ON_MASK_SFT (0x1 << 3) +#define MTKAIF_SINE_ON_SFT 2 +#define MTKAIF_SINE_ON_MASK 0x1 +#define MTKAIF_SINE_ON_MASK_SFT (0x1 << 2) +#define UL_SINE_ON_SFT 1 +#define UL_SINE_ON_MASK 0x1 +#define UL_SINE_ON_MASK_SFT (0x1 << 1) +#define DL_SINE_ON_SFT 0 +#define DL_SINE_ON_MASK 0x1 +#define DL_SINE_ON_MASK_SFT (0x1 << 0) + +/* AUDIO_TOP_CON0 */ +#define PDN_AFE_CTL_SFT 7 +#define PDN_AFE_CTL_MASK 0x1 +#define PDN_AFE_CTL_MASK_SFT (0x1 << 7) +#define PDN_DAC_CTL_SFT 6 +#define PDN_DAC_CTL_MASK 0x1 +#define PDN_DAC_CTL_MASK_SFT (0x1 << 6) +#define PDN_ADC_CTL_SFT 5 +#define PDN_ADC_CTL_MASK 0x1 +#define PDN_ADC_CTL_MASK_SFT (0x1 << 5) +#define PDN_ADDA6_ADC_CTL_SFT 4 +#define PDN_ADDA6_ADC_CTL_MASK 0x1 +#define PDN_ADDA6_ADC_CTL_MASK_SFT (0x1 << 4) +#define PDN_I2S_DL_CTL_SFT 3 +#define PDN_I2S_DL_CTL_MASK 0x1 +#define PDN_I2S_DL_CTL_MASK_SFT (0x1 << 3) +#define PWR_CLK_DIS_CTL_SFT 2 +#define PWR_CLK_DIS_CTL_MASK 0x1 +#define PWR_CLK_DIS_CTL_MASK_SFT (0x1 << 2) +#define PDN_AFE_TESTMODEL_CTL_SFT 1 +#define PDN_AFE_TESTMODEL_CTL_MASK 0x1 +#define PDN_AFE_TESTMODEL_CTL_MASK_SFT (0x1 << 1) +#define PDN_RESERVED_SFT 0 +#define PDN_RESERVED_MASK 0x1 +#define PDN_RESERVED_MASK_SFT (0x1 << 0) + +/* AFE_MON_DEBUG0 */ +#define AUDIO_SYS_TOP_MON_SWAP_SFT 14 +#define AUDIO_SYS_TOP_MON_SWAP_MASK 0x3 +#define AUDIO_SYS_TOP_MON_SWAP_MASK_SFT (0x3 << 14) +#define AUDIO_SYS_TOP_MON_SEL_SFT 8 +#define AUDIO_SYS_TOP_MON_SEL_MASK 0x1f +#define AUDIO_SYS_TOP_MON_SEL_MASK_SFT (0x1f << 8) +#define AFE_MON_SEL_SFT 0 +#define AFE_MON_SEL_MASK 0xff +#define AFE_MON_SEL_MASK_SFT (0xff << 0) + +/* AFUNC_AUD_CON0 */ +#define CCI_AUD_ANACK_SEL_SFT 15 +#define CCI_AUD_ANACK_SEL_MASK 0x1 +#define CCI_AUD_ANACK_SEL_MASK_SFT (0x1 << 15) +#define CCI_AUDIO_FIFO_WPTR_SFT 12 +#define CCI_AUDIO_FIFO_WPTR_MASK 0x7 +#define CCI_AUDIO_FIFO_WPTR_MASK_SFT (0x7 << 12) +#define CCI_SCRAMBLER_CG_EN_SFT 11 +#define CCI_SCRAMBLER_CG_EN_MASK 0x1 +#define CCI_SCRAMBLER_CG_EN_MASK_SFT (0x1 << 11) +#define CCI_LCH_INV_SFT 10 +#define CCI_LCH_INV_MASK 0x1 +#define CCI_LCH_INV_MASK_SFT (0x1 << 10) +#define CCI_RAND_EN_SFT 9 +#define CCI_RAND_EN_MASK 0x1 +#define CCI_RAND_EN_MASK_SFT (0x1 << 9) +#define CCI_SPLT_SCRMB_CLK_ON_SFT 8 +#define CCI_SPLT_SCRMB_CLK_ON_MASK 0x1 +#define CCI_SPLT_SCRMB_CLK_ON_MASK_SFT (0x1 << 8) +#define CCI_SPLT_SCRMB_ON_SFT 7 +#define CCI_SPLT_SCRMB_ON_MASK 0x1 +#define CCI_SPLT_SCRMB_ON_MASK_SFT (0x1 << 7) +#define CCI_AUD_IDAC_TEST_EN_SFT 6 +#define CCI_AUD_IDAC_TEST_EN_MASK 0x1 +#define CCI_AUD_IDAC_TEST_EN_MASK_SFT (0x1 << 6) +#define CCI_ZERO_PAD_DISABLE_SFT 5 +#define CCI_ZERO_PAD_DISABLE_MASK 0x1 +#define CCI_ZERO_PAD_DISABLE_MASK_SFT (0x1 << 5) +#define CCI_AUD_SPLIT_TEST_EN_SFT 4 +#define CCI_AUD_SPLIT_TEST_EN_MASK 0x1 +#define CCI_AUD_SPLIT_TEST_EN_MASK_SFT (0x1 << 4) +#define CCI_AUD_SDM_MUTEL_SFT 3 +#define CCI_AUD_SDM_MUTEL_MASK 0x1 +#define CCI_AUD_SDM_MUTEL_MASK_SFT (0x1 << 3) +#define CCI_AUD_SDM_MUTER_SFT 2 +#define CCI_AUD_SDM_MUTER_MASK 0x1 +#define CCI_AUD_SDM_MUTER_MASK_SFT (0x1 << 2) +#define CCI_AUD_SDM_7BIT_SEL_SFT 1 +#define CCI_AUD_SDM_7BIT_SEL_MASK 0x1 +#define CCI_AUD_SDM_7BIT_SEL_MASK_SFT (0x1 << 1) +#define CCI_SCRAMBLER_EN_SFT 0 +#define CCI_SCRAMBLER_EN_MASK 0x1 +#define CCI_SCRAMBLER_EN_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON1 */ +#define AUD_SDM_TEST_L_SFT 8 +#define AUD_SDM_TEST_L_MASK 0xff +#define AUD_SDM_TEST_L_MASK_SFT (0xff << 8) +#define AUD_SDM_TEST_R_SFT 0 +#define AUD_SDM_TEST_R_MASK 0xff +#define AUD_SDM_TEST_R_MASK_SFT (0xff << 0) + +/* AFUNC_AUD_CON2 */ +#define CCI_AUD_DAC_ANA_MUTE_SFT 7 +#define CCI_AUD_DAC_ANA_MUTE_MASK 0x1 +#define CCI_AUD_DAC_ANA_MUTE_MASK_SFT (0x1 << 7) +#define CCI_AUD_DAC_ANA_RSTB_SEL_SFT 6 +#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK 0x1 +#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK_SFT (0x1 << 6) +#define CCI_AUDIO_FIFO_CLKIN_INV_SFT 4 +#define CCI_AUDIO_FIFO_CLKIN_INV_MASK 0x1 +#define CCI_AUDIO_FIFO_CLKIN_INV_MASK_SFT (0x1 << 4) +#define CCI_AUDIO_FIFO_ENABLE_SFT 3 +#define CCI_AUDIO_FIFO_ENABLE_MASK 0x1 +#define CCI_AUDIO_FIFO_ENABLE_MASK_SFT (0x1 << 3) +#define CCI_ACD_MODE_SFT 2 +#define CCI_ACD_MODE_MASK 0x1 +#define CCI_ACD_MODE_MASK_SFT (0x1 << 2) +#define CCI_AFIFO_CLK_PWDB_SFT 1 +#define CCI_AFIFO_CLK_PWDB_MASK 0x1 +#define CCI_AFIFO_CLK_PWDB_MASK_SFT (0x1 << 1) +#define CCI_ACD_FUNC_RSTB_SFT 0 +#define CCI_ACD_FUNC_RSTB_MASK 0x1 +#define CCI_ACD_FUNC_RSTB_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON3 */ +#define SDM_ANA13M_TESTCK_SEL_SFT 15 +#define SDM_ANA13M_TESTCK_SEL_MASK 0x1 +#define SDM_ANA13M_TESTCK_SEL_MASK_SFT (0x1 << 15) +#define SDM_ANA13M_TESTCK_SRC_SEL_SFT 12 +#define SDM_ANA13M_TESTCK_SRC_SEL_MASK 0x7 +#define SDM_ANA13M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12) +#define SDM_TESTCK_SRC_SEL_SFT 8 +#define SDM_TESTCK_SRC_SEL_MASK 0x7 +#define SDM_TESTCK_SRC_SEL_MASK_SFT (0x7 << 8) +#define DIGMIC_TESTCK_SRC_SEL_SFT 4 +#define DIGMIC_TESTCK_SRC_SEL_MASK 0x7 +#define DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 4) +#define DIGMIC_TESTCK_SEL_SFT 0 +#define DIGMIC_TESTCK_SEL_MASK 0x1 +#define DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON4 */ +#define UL_FIFO_WCLK_INV_SFT 8 +#define UL_FIFO_WCLK_INV_MASK 0x1 +#define UL_FIFO_WCLK_INV_MASK_SFT (0x1 << 8) +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6 +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6) +#define UL_FIFO_WDATA_TESTEN_SFT 5 +#define UL_FIFO_WDATA_TESTEN_MASK 0x1 +#define UL_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5) +#define UL_FIFO_WDATA_TESTSRC_SEL_SFT 4 +#define UL_FIFO_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4) +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3 +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1 +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3) +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0 +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7 +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0) + +/* AFUNC_AUD_CON5 */ +#define R_AUD_DAC_POS_LARGE_MONO_SFT 8 +#define R_AUD_DAC_POS_LARGE_MONO_MASK 0xff +#define R_AUD_DAC_POS_LARGE_MONO_MASK_SFT (0xff << 8) +#define R_AUD_DAC_NEG_LARGE_MONO_SFT 0 +#define R_AUD_DAC_NEG_LARGE_MONO_MASK 0xff +#define R_AUD_DAC_NEG_LARGE_MONO_MASK_SFT (0xff << 0) + +/* AFUNC_AUD_CON6 */ +#define R_AUD_DAC_POS_SMALL_MONO_SFT 12 +#define R_AUD_DAC_POS_SMALL_MONO_MASK 0xf +#define R_AUD_DAC_POS_SMALL_MONO_MASK_SFT (0xf << 12) +#define R_AUD_DAC_NEG_SMALL_MONO_SFT 8 +#define R_AUD_DAC_NEG_SMALL_MONO_MASK 0xf +#define R_AUD_DAC_NEG_SMALL_MONO_MASK_SFT (0xf << 8) +#define R_AUD_DAC_POS_TINY_MONO_SFT 6 +#define R_AUD_DAC_POS_TINY_MONO_MASK 0x3 +#define R_AUD_DAC_POS_TINY_MONO_MASK_SFT (0x3 << 6) +#define R_AUD_DAC_NEG_TINY_MONO_SFT 4 +#define R_AUD_DAC_NEG_TINY_MONO_MASK 0x3 +#define R_AUD_DAC_NEG_TINY_MONO_MASK_SFT (0x3 << 4) +#define R_AUD_DAC_MONO_SEL_SFT 3 +#define R_AUD_DAC_MONO_SEL_MASK 0x1 +#define R_AUD_DAC_MONO_SEL_MASK_SFT (0x1 << 3) +#define R_AUD_DAC_3TH_SEL_SFT 1 +#define R_AUD_DAC_3TH_SEL_MASK 0x1 +#define R_AUD_DAC_3TH_SEL_MASK_SFT (0x1 << 1) +#define R_AUD_DAC_SW_RSTB_SFT 0 +#define R_AUD_DAC_SW_RSTB_MASK 0x1 +#define R_AUD_DAC_SW_RSTB_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON7 */ +#define UL2_DIGMIC_TESTCK_SRC_SEL_SFT 10 +#define UL2_DIGMIC_TESTCK_SRC_SEL_MASK 0x7 +#define UL2_DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 10) +#define UL2_DIGMIC_TESTCK_SEL_SFT 9 +#define UL2_DIGMIC_TESTCK_SEL_MASK 0x1 +#define UL2_DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 9) +#define UL2_FIFO_WCLK_INV_SFT 8 +#define UL2_FIFO_WCLK_INV_MASK 0x1 +#define UL2_FIFO_WCLK_INV_MASK_SFT (0x1 << 8) +#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6 +#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6) +#define UL2_FIFO_WDATA_TESTEN_SFT 5 +#define UL2_FIFO_WDATA_TESTEN_MASK 0x1 +#define UL2_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5) +#define UL2_FIFO_WDATA_TESTSRC_SEL_SFT 4 +#define UL2_FIFO_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL2_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4) +#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3 +#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1 +#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3) +#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0 +#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7 +#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0) + +/* AFUNC_AUD_CON8 */ +#define SPLITTER2_DITHER_EN_SFT 9 +#define SPLITTER2_DITHER_EN_MASK 0x1 +#define SPLITTER2_DITHER_EN_MASK_SFT (0x1 << 9) +#define SPLITTER1_DITHER_EN_SFT 8 +#define SPLITTER1_DITHER_EN_MASK 0x1 +#define SPLITTER1_DITHER_EN_MASK_SFT (0x1 << 8) +#define SPLITTER2_DITHER_GAIN_SFT 4 +#define SPLITTER2_DITHER_GAIN_MASK 0xf +#define SPLITTER2_DITHER_GAIN_MASK_SFT (0xf << 4) +#define SPLITTER1_DITHER_GAIN_SFT 0 +#define SPLITTER1_DITHER_GAIN_MASK 0xf +#define SPLITTER1_DITHER_GAIN_MASK_SFT (0xf << 0) + +/* AFUNC_AUD_CON9 */ +#define CCI_AUD_ANACK_SEL_2ND_SFT 15 +#define CCI_AUD_ANACK_SEL_2ND_MASK 0x1 +#define CCI_AUD_ANACK_SEL_2ND_MASK_SFT (0x1 << 15) +#define CCI_AUDIO_FIFO_WPTR_2ND_SFT 12 +#define CCI_AUDIO_FIFO_WPTR_2ND_MASK 0x7 +#define CCI_AUDIO_FIFO_WPTR_2ND_MASK_SFT (0x7 << 12) +#define CCI_SCRAMBLER_CG_EN_2ND_SFT 11 +#define CCI_SCRAMBLER_CG_EN_2ND_MASK 0x1 +#define CCI_SCRAMBLER_CG_EN_2ND_MASK_SFT (0x1 << 11) +#define CCI_LCH_INV_2ND_SFT 10 +#define CCI_LCH_INV_2ND_MASK 0x1 +#define CCI_LCH_INV_2ND_MASK_SFT (0x1 << 10) +#define CCI_RAND_EN_2ND_SFT 9 +#define CCI_RAND_EN_2ND_MASK 0x1 +#define CCI_RAND_EN_2ND_MASK_SFT (0x1 << 9) +#define CCI_SPLT_SCRMB_CLK_ON_2ND_SFT 8 +#define CCI_SPLT_SCRMB_CLK_ON_2ND_MASK 0x1 +#define CCI_SPLT_SCRMB_CLK_ON_2ND_MASK_SFT (0x1 << 8) +#define CCI_SPLT_SCRMB_ON_2ND_SFT 7 +#define CCI_SPLT_SCRMB_ON_2ND_MASK 0x1 +#define CCI_SPLT_SCRMB_ON_2ND_MASK_SFT (0x1 << 7) +#define CCI_AUD_IDAC_TEST_EN_2ND_SFT 6 +#define CCI_AUD_IDAC_TEST_EN_2ND_MASK 0x1 +#define CCI_AUD_IDAC_TEST_EN_2ND_MASK_SFT (0x1 << 6) +#define CCI_ZERO_PAD_DISABLE_2ND_SFT 5 +#define CCI_ZERO_PAD_DISABLE_2ND_MASK 0x1 +#define CCI_ZERO_PAD_DISABLE_2ND_MASK_SFT (0x1 << 5) +#define CCI_AUD_SPLIT_TEST_EN_2ND_SFT 4 +#define CCI_AUD_SPLIT_TEST_EN_2ND_MASK 0x1 +#define CCI_AUD_SPLIT_TEST_EN_2ND_MASK_SFT (0x1 << 4) +#define CCI_AUD_SDM_MUTEL_2ND_SFT 3 +#define CCI_AUD_SDM_MUTEL_2ND_MASK 0x1 +#define CCI_AUD_SDM_MUTEL_2ND_MASK_SFT (0x1 << 3) +#define CCI_AUD_SDM_MUTER_2ND_SFT 2 +#define CCI_AUD_SDM_MUTER_2ND_MASK 0x1 +#define CCI_AUD_SDM_MUTER_2ND_MASK_SFT (0x1 << 2) +#define CCI_AUD_SDM_7BIT_SEL_2ND_SFT 1 +#define CCI_AUD_SDM_7BIT_SEL_2ND_MASK 0x1 +#define CCI_AUD_SDM_7BIT_SEL_2ND_MASK_SFT (0x1 << 1) +#define CCI_SCRAMBLER_EN_2ND_SFT 0 +#define CCI_SCRAMBLER_EN_2ND_MASK 0x1 +#define CCI_SCRAMBLER_EN_2ND_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON10 */ +#define AUD_SDM_TEST_L_2ND_SFT 8 +#define AUD_SDM_TEST_L_2ND_MASK 0xff +#define AUD_SDM_TEST_L_2ND_MASK_SFT (0xff << 8) +#define AUD_SDM_TEST_R_2ND_SFT 0 +#define AUD_SDM_TEST_R_2ND_MASK 0xff +#define AUD_SDM_TEST_R_2ND_MASK_SFT (0xff << 0) + +/* AFUNC_AUD_CON11 */ +#define CCI_AUD_DAC_ANA_MUTE_2ND_SFT 7 +#define CCI_AUD_DAC_ANA_MUTE_2ND_MASK 0x1 +#define CCI_AUD_DAC_ANA_MUTE_2ND_MASK_SFT (0x1 << 7) +#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_SFT 6 +#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_MASK 0x1 +#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_MASK_SFT (0x1 << 6) +#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_SFT 4 +#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_MASK 0x1 +#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_MASK_SFT (0x1 << 4) +#define CCI_AUDIO_FIFO_ENABLE_2ND_SFT 3 +#define CCI_AUDIO_FIFO_ENABLE_2ND_MASK 0x1 +#define CCI_AUDIO_FIFO_ENABLE_2ND_MASK_SFT (0x1 << 3) +#define CCI_ACD_MODE_2ND_SFT 2 +#define CCI_ACD_MODE_2ND_MASK 0x1 +#define CCI_ACD_MODE_2ND_MASK_SFT (0x1 << 2) +#define CCI_AFIFO_CLK_PWDB_2ND_SFT 1 +#define CCI_AFIFO_CLK_PWDB_2ND_MASK 0x1 +#define CCI_AFIFO_CLK_PWDB_2ND_MASK_SFT (0x1 << 1) +#define CCI_ACD_FUNC_RSTB_2ND_SFT 0 +#define CCI_ACD_FUNC_RSTB_2ND_MASK 0x1 +#define CCI_ACD_FUNC_RSTB_2ND_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON12 */ +#define SPLITTER2_DITHER_EN_2ND_SFT 9 +#define SPLITTER2_DITHER_EN_2ND_MASK 0x1 +#define SPLITTER2_DITHER_EN_2ND_MASK_SFT (0x1 << 9) +#define SPLITTER1_DITHER_EN_2ND_SFT 8 +#define SPLITTER1_DITHER_EN_2ND_MASK 0x1 +#define SPLITTER1_DITHER_EN_2ND_MASK_SFT (0x1 << 8) +#define SPLITTER2_DITHER_GAIN_2ND_SFT 4 +#define SPLITTER2_DITHER_GAIN_2ND_MASK 0xf +#define SPLITTER2_DITHER_GAIN_2ND_MASK_SFT (0xf << 4) +#define SPLITTER1_DITHER_GAIN_2ND_SFT 0 +#define SPLITTER1_DITHER_GAIN_2ND_MASK 0xf +#define SPLITTER1_DITHER_GAIN_2ND_MASK_SFT (0xf << 0) + +/* AFUNC_AUD_MON0 */ +#define AUD_SCR_OUT_L_SFT 8 +#define AUD_SCR_OUT_L_MASK 0xff +#define AUD_SCR_OUT_L_MASK_SFT (0xff << 8) +#define AUD_SCR_OUT_R_SFT 0 +#define AUD_SCR_OUT_R_MASK 0xff +#define AUD_SCR_OUT_R_MASK_SFT (0xff << 0) + +/* AFUNC_AUD_MON1 */ +#define AUD_SCR_OUT_L_2ND_SFT 8 +#define AUD_SCR_OUT_L_2ND_MASK 0xff +#define AUD_SCR_OUT_L_2ND_MASK_SFT (0xff << 8) +#define AUD_SCR_OUT_R_2ND_SFT 0 +#define AUD_SCR_OUT_R_2ND_MASK 0xff +#define AUD_SCR_OUT_R_2ND_MASK_SFT (0xff << 0) + +/* AUDRC_TUNE_MON0 */ +#define ASYNC_TEST_OUT_BCK_SFT 15 +#define ASYNC_TEST_OUT_BCK_MASK 0x1 +#define ASYNC_TEST_OUT_BCK_MASK_SFT (0x1 << 15) +#define RGS_AUDRCTUNE1READ_SFT 8 +#define RGS_AUDRCTUNE1READ_MASK 0x1f +#define RGS_AUDRCTUNE1READ_MASK_SFT (0x1f << 8) +#define RGS_AUDRCTUNE0READ_SFT 0 +#define RGS_AUDRCTUNE0READ_MASK 0x1f +#define RGS_AUDRCTUNE0READ_MASK_SFT (0x1f << 0) + +/* AFE_ADDA_MTKAIF_FIFO_CFG0 */ +#define AFE_RESERVED_SFT 1 +#define AFE_RESERVED_MASK 0x7fff +#define AFE_RESERVED_MASK_SFT (0x7fff << 1) +#define RG_MTKAIF_RXIF_FIFO_INTEN_SFT 0 +#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK 0x1 +#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_MTKAIF_FIFO_LOG_MON1 */ +#define MTKAIF_RXIF_WR_FULL_STATUS_SFT 1 +#define MTKAIF_RXIF_WR_FULL_STATUS_MASK 0x1 +#define MTKAIF_RXIF_WR_FULL_STATUS_MASK_SFT (0x1 << 1) +#define MTKAIF_RXIF_RD_EMPTY_STATUS_SFT 0 +#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK 0x1 +#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_MTKAIF_MON0 */ +#define MTKAIFTX_V3_SYNC_OUT_SFT 15 +#define MTKAIFTX_V3_SYNC_OUT_MASK 0x1 +#define MTKAIFTX_V3_SYNC_OUT_MASK_SFT (0x1 << 15) +#define MTKAIFTX_V3_SDATA_OUT3_SFT 14 +#define MTKAIFTX_V3_SDATA_OUT3_MASK 0x1 +#define MTKAIFTX_V3_SDATA_OUT3_MASK_SFT (0x1 << 14) +#define MTKAIFTX_V3_SDATA_OUT2_SFT 13 +#define MTKAIFTX_V3_SDATA_OUT2_MASK 0x1 +#define MTKAIFTX_V3_SDATA_OUT2_MASK_SFT (0x1 << 13) +#define MTKAIFTX_V3_SDATA_OUT1_SFT 12 +#define MTKAIFTX_V3_SDATA_OUT1_MASK 0x1 +#define MTKAIFTX_V3_SDATA_OUT1_MASK_SFT (0x1 << 12) +#define MTKAIF_RXIF_FIFO_STATUS_SFT 0 +#define MTKAIF_RXIF_FIFO_STATUS_MASK 0xfff +#define MTKAIF_RXIF_FIFO_STATUS_MASK_SFT (0xfff << 0) + +/* AFE_ADDA_MTKAIF_MON1 */ +#define MTKAIFRX_V3_SYNC_IN_SFT 15 +#define MTKAIFRX_V3_SYNC_IN_MASK 0x1 +#define MTKAIFRX_V3_SYNC_IN_MASK_SFT (0x1 << 15) +#define MTKAIFRX_V3_SDATA_IN3_SFT 14 +#define MTKAIFRX_V3_SDATA_IN3_MASK 0x1 +#define MTKAIFRX_V3_SDATA_IN3_MASK_SFT (0x1 << 14) +#define MTKAIFRX_V3_SDATA_IN2_SFT 13 +#define MTKAIFRX_V3_SDATA_IN2_MASK 0x1 +#define MTKAIFRX_V3_SDATA_IN2_MASK_SFT (0x1 << 13) +#define MTKAIFRX_V3_SDATA_IN1_SFT 12 +#define MTKAIFRX_V3_SDATA_IN1_MASK 0x1 +#define MTKAIFRX_V3_SDATA_IN1_MASK_SFT (0x1 << 12) +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_SFT 11 +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK 0x1 +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK_SFT (0x1 << 11) +#define MTKAIF_RXIF_INVALID_FLAG_SFT 8 +#define MTKAIF_RXIF_INVALID_FLAG_MASK 0x1 +#define MTKAIF_RXIF_INVALID_FLAG_MASK_SFT (0x1 << 8) +#define MTKAIF_RXIF_INVALID_CYCLE_SFT 0 +#define MTKAIF_RXIF_INVALID_CYCLE_MASK 0xff +#define MTKAIF_RXIF_INVALID_CYCLE_MASK_SFT (0xff << 0) + +/* AFE_ADDA_MTKAIF_MON2 */ +#define MTKAIF_TXIF_IN_CH2_SFT 8 +#define MTKAIF_TXIF_IN_CH2_MASK 0xff +#define MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8) +#define MTKAIF_TXIF_IN_CH1_SFT 0 +#define MTKAIF_TXIF_IN_CH1_MASK 0xff +#define MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0) + +/* AFE_ADDA6_MTKAIF_MON3 */ +#define ADDA6_MTKAIF_TXIF_IN_CH2_SFT 8 +#define ADDA6_MTKAIF_TXIF_IN_CH2_MASK 0xff +#define ADDA6_MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8) +#define ADDA6_MTKAIF_TXIF_IN_CH1_SFT 0 +#define ADDA6_MTKAIF_TXIF_IN_CH1_MASK 0xff +#define ADDA6_MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0) + +/* AFE_ADDA_MTKAIF_MON4 */ +#define MTKAIF_RXIF_OUT_CH2_SFT 8 +#define MTKAIF_RXIF_OUT_CH2_MASK 0xff +#define MTKAIF_RXIF_OUT_CH2_MASK_SFT (0xff << 8) +#define MTKAIF_RXIF_OUT_CH1_SFT 0 +#define MTKAIF_RXIF_OUT_CH1_MASK 0xff +#define MTKAIF_RXIF_OUT_CH1_MASK_SFT (0xff << 0) + +/* AFE_ADDA_MTKAIF_MON5 */ +#define MTKAIF_RXIF_OUT_CH3_SFT 0 +#define MTKAIF_RXIF_OUT_CH3_MASK 0xff +#define MTKAIF_RXIF_OUT_CH3_MASK_SFT (0xff << 0) + +/* AFE_ADDA_MTKAIF_CFG0 */ +#define RG_MTKAIF_RXIF_CLKINV_SFT 15 +#define RG_MTKAIF_RXIF_CLKINV_MASK 0x1 +#define RG_MTKAIF_RXIF_CLKINV_MASK_SFT (0x1 << 15) +#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_SFT 9 +#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_MASK 0x1 +#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 9) +#define RG_MTKAIF_RXIF_PROTOCOL2_SFT 8 +#define RG_MTKAIF_RXIF_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 8) +#define RG_MTKAIF_BYPASS_SRC_MODE_SFT 6 +#define RG_MTKAIF_BYPASS_SRC_MODE_MASK 0x3 +#define RG_MTKAIF_BYPASS_SRC_MODE_MASK_SFT (0x3 << 6) +#define RG_MTKAIF_BYPASS_SRC_TEST_SFT 5 +#define RG_MTKAIF_BYPASS_SRC_TEST_MASK 0x1 +#define RG_MTKAIF_BYPASS_SRC_TEST_MASK_SFT (0x1 << 5) +#define RG_MTKAIF_TXIF_PROTOCOL2_SFT 4 +#define RG_MTKAIF_TXIF_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4) +#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_SFT 3 +#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1 +#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 3) +#define RG_MTKAIF_PMIC_TXIF_8TO5_SFT 2 +#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1 +#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 2) +#define RG_MTKAIF_LOOPBACK_TEST2_SFT 1 +#define RG_MTKAIF_LOOPBACK_TEST2_MASK 0x1 +#define RG_MTKAIF_LOOPBACK_TEST2_MASK_SFT (0x1 << 1) +#define RG_MTKAIF_LOOPBACK_TEST1_SFT 0 +#define RG_MTKAIF_LOOPBACK_TEST1_MASK 0x1 +#define RG_MTKAIF_LOOPBACK_TEST1_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_MTKAIF_RX_CFG0 */ +#define RG_MTKAIF_RXIF_VOICE_MODE_SFT 12 +#define RG_MTKAIF_RXIF_VOICE_MODE_MASK 0xf +#define RG_MTKAIF_RXIF_VOICE_MODE_MASK_SFT (0xf << 12) +#define RG_MTKAIF_RXIF_DATA_BIT_SFT 8 +#define RG_MTKAIF_RXIF_DATA_BIT_MASK 0x7 +#define RG_MTKAIF_RXIF_DATA_BIT_MASK_SFT (0x7 << 8) +#define RG_MTKAIF_RXIF_FIFO_RSP_SFT 4 +#define RG_MTKAIF_RXIF_FIFO_RSP_MASK 0x7 +#define RG_MTKAIF_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_RXIF_DETECT_ON_SFT 3 +#define RG_MTKAIF_RXIF_DETECT_ON_MASK 0x1 +#define RG_MTKAIF_RXIF_DETECT_ON_MASK_SFT (0x1 << 3) +#define RG_MTKAIF_RXIF_DATA_MODE_SFT 0 +#define RG_MTKAIF_RXIF_DATA_MODE_MASK 0x1 +#define RG_MTKAIF_RXIF_DATA_MODE_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_MTKAIF_RX_CFG1 */ +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_SFT 12 +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK 0xf +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0xf << 12) +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_SFT 8 +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK 0xf +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8) +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_SFT 4 +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK 0xf +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4) +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_SFT 0 +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK 0xf +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK_SFT (0xf << 0) + +/* AFE_ADDA_MTKAIF_RX_CFG2 */ +#define RG_MTKAIF_RXIF_P2_INPUT_SEL_SFT 15 +#define RG_MTKAIF_RXIF_P2_INPUT_SEL_MASK 0x1 +#define RG_MTKAIF_RXIF_P2_INPUT_SEL_MASK_SFT (0x1 << 15) +#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_SFT 14 +#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_MASK 0x1 +#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_MASK_SFT (0x1 << 14) +#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_SFT 13 +#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_MASK 0x1 +#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_MASK_SFT (0x1 << 13) +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_SFT 12 +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK 0x1 +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK_SFT (0x1 << 12) +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_SFT 0 +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK 0xfff +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 0) + +/* AFE_ADDA_MTKAIF_RX_CFG3 */ +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_SFT 7 +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK 0x1 +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK_SFT (0x1 << 7) +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4 +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7 +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 3 +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 3) + +/* AFE_ADDA_MTKAIF_SYNCWORD_CFG0 */ +#define RG_MTKAIF_RX_SYNC_WORD2_SFT 4 +#define RG_MTKAIF_RX_SYNC_WORD2_MASK 0x7 +#define RG_MTKAIF_RX_SYNC_WORD2_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_RX_SYNC_WORD1_SFT 0 +#define RG_MTKAIF_RX_SYNC_WORD1_MASK 0x7 +#define RG_MTKAIF_RX_SYNC_WORD1_MASK_SFT (0x7 << 0) + +/* AFE_ADDA_MTKAIF_SYNCWORD_CFG1 */ +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_SFT 12 +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_MASK 0x7 +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_MASK_SFT (0x7 << 12) +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_SFT 8 +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_MASK 0x7 +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_MASK_SFT (0x7 << 8) +#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_SFT 4 +#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_MASK 0x7 +#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_MASK_SFT (0x7 << 4) +#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_SFT 0 +#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_MASK 0x7 +#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_MASK_SFT (0x7 << 0) + +/* AFE_SGEN_CFG0 */ +#define SGEN_AMP_DIV_CH1_CTL_SFT 12 +#define SGEN_AMP_DIV_CH1_CTL_MASK 0xf +#define SGEN_AMP_DIV_CH1_CTL_MASK_SFT (0xf << 12) +#define SGEN_DAC_EN_CTL_SFT 7 +#define SGEN_DAC_EN_CTL_MASK 0x1 +#define SGEN_DAC_EN_CTL_MASK_SFT (0x1 << 7) +#define SGEN_MUTE_SW_CTL_SFT 6 +#define SGEN_MUTE_SW_CTL_MASK 0x1 +#define SGEN_MUTE_SW_CTL_MASK_SFT (0x1 << 6) +#define R_AUD_SDM_MUTE_L_SFT 5 +#define R_AUD_SDM_MUTE_L_MASK 0x1 +#define R_AUD_SDM_MUTE_L_MASK_SFT (0x1 << 5) +#define R_AUD_SDM_MUTE_R_SFT 4 +#define R_AUD_SDM_MUTE_R_MASK 0x1 +#define R_AUD_SDM_MUTE_R_MASK_SFT (0x1 << 4) +#define R_AUD_SDM_MUTE_L_2ND_SFT 3 +#define R_AUD_SDM_MUTE_L_2ND_MASK 0x1 +#define R_AUD_SDM_MUTE_L_2ND_MASK_SFT (0x1 << 3) +#define R_AUD_SDM_MUTE_R_2ND_SFT 2 +#define R_AUD_SDM_MUTE_R_2ND_MASK 0x1 +#define R_AUD_SDM_MUTE_R_2ND_MASK_SFT (0x1 << 2) + +/* AFE_SGEN_CFG1 */ +#define C_SGEN_RCH_INV_5BIT_SFT 15 +#define C_SGEN_RCH_INV_5BIT_MASK 0x1 +#define C_SGEN_RCH_INV_5BIT_MASK_SFT (0x1 << 15) +#define C_SGEN_RCH_INV_8BIT_SFT 14 +#define C_SGEN_RCH_INV_8BIT_MASK 0x1 +#define C_SGEN_RCH_INV_8BIT_MASK_SFT (0x1 << 14) +#define SGEN_FREQ_DIV_CH1_CTL_SFT 0 +#define SGEN_FREQ_DIV_CH1_CTL_MASK 0x1f +#define SGEN_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 0) + +/* AFE_ADC_ASYNC_FIFO_CFG */ +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_SFT 5 +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1 +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5) +#define RG_UL_ASYNC_FIFO_SOFT_RST_SFT 4 +#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK 0x1 +#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4) +#define RG_AMIC_UL_ADC_CLK_SEL_SFT 1 +#define RG_AMIC_UL_ADC_CLK_SEL_MASK 0x1 +#define RG_AMIC_UL_ADC_CLK_SEL_MASK_SFT (0x1 << 1) + +/* AFE_ADC_ASYNC_FIFO_CFG1 */ +#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_SFT 5 +#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1 +#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5) +#define RG_UL2_ASYNC_FIFO_SOFT_RST_SFT 4 +#define RG_UL2_ASYNC_FIFO_SOFT_RST_MASK 0x1 +#define RG_UL2_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4) + +/* AFE_DCCLK_CFG0 */ +#define DCCLK_DIV_SFT 5 +#define DCCLK_DIV_MASK 0x7ff +#define DCCLK_DIV_MASK_SFT (0x7ff << 5) +#define DCCLK_INV_SFT 4 +#define DCCLK_INV_MASK 0x1 +#define DCCLK_INV_MASK_SFT (0x1 << 4) +#define DCCLK_REF_CK_SEL_SFT 2 +#define DCCLK_REF_CK_SEL_MASK 0x3 +#define DCCLK_REF_CK_SEL_MASK_SFT (0x3 << 2) +#define DCCLK_PDN_SFT 1 +#define DCCLK_PDN_MASK 0x1 +#define DCCLK_PDN_MASK_SFT (0x1 << 1) +#define DCCLK_GEN_ON_SFT 0 +#define DCCLK_GEN_ON_MASK 0x1 +#define DCCLK_GEN_ON_MASK_SFT (0x1 << 0) + +/* AFE_DCCLK_CFG1 */ +#define RESYNC_SRC_SEL_SFT 10 +#define RESYNC_SRC_SEL_MASK 0x3 +#define RESYNC_SRC_SEL_MASK_SFT (0x3 << 10) +#define RESYNC_SRC_CK_INV_SFT 9 +#define RESYNC_SRC_CK_INV_MASK 0x1 +#define RESYNC_SRC_CK_INV_MASK_SFT (0x1 << 9) +#define DCCLK_RESYNC_BYPASS_SFT 8 +#define DCCLK_RESYNC_BYPASS_MASK 0x1 +#define DCCLK_RESYNC_BYPASS_MASK_SFT (0x1 << 8) +#define DCCLK_PHASE_SEL_SFT 4 +#define DCCLK_PHASE_SEL_MASK 0xf +#define DCCLK_PHASE_SEL_MASK_SFT (0xf << 4) + +/* AUDIO_DIG_CFG */ +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT 15 +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK 0x1 +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT (0x1 << 15) +#define RG_AUD_PAD_TOP_PHASE_MODE2_SFT 8 +#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK 0x7f +#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT (0x7f << 8) +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT 7 +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK 0x1 +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT (0x1 << 7) +#define RG_AUD_PAD_TOP_PHASE_MODE_SFT 0 +#define RG_AUD_PAD_TOP_PHASE_MODE_MASK 0x7f +#define RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT (0x7f << 0) + +/* AUDIO_DIG_CFG1 */ +#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT 7 +#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK 0x1 +#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT (0x1 << 7) +#define RG_AUD_PAD_TOP_PHASE_MODE3_SFT 0 +#define RG_AUD_PAD_TOP_PHASE_MODE3_MASK 0x7f +#define RG_AUD_PAD_TOP_PHASE_MODE3_MASK_SFT (0x7f << 0) + +/* AFE_AUD_PAD_TOP */ +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_SFT 12 +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK 0x7 +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK_SFT (0x7 << 12) +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_SFT 11 +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK 0x1 +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK_SFT (0x1 << 11) +#define RG_AUD_PAD_TOP_TX_FIFO_ON_SFT 8 +#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK 0x1 +#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK_SFT (0x1 << 8) + +/* AFE_AUD_PAD_TOP_MON */ +#define ADDA_AUD_PAD_TOP_MON_SFT 0 +#define ADDA_AUD_PAD_TOP_MON_MASK 0xffff +#define ADDA_AUD_PAD_TOP_MON_MASK_SFT (0xffff << 0) + +/* AFE_AUD_PAD_TOP_MON1 */ +#define ADDA_AUD_PAD_TOP_MON1_SFT 0 +#define ADDA_AUD_PAD_TOP_MON1_MASK 0xffff +#define ADDA_AUD_PAD_TOP_MON1_MASK_SFT (0xffff << 0) + +/* AFE_AUD_PAD_TOP_MON2 */ +#define ADDA_AUD_PAD_TOP_MON2_SFT 0 +#define ADDA_AUD_PAD_TOP_MON2_MASK 0xffff +#define ADDA_AUD_PAD_TOP_MON2_MASK_SFT (0xffff << 0) + +/* AFE_DL_NLE_CFG */ +#define NLE_RCH_HPGAIN_SEL_SFT 10 +#define NLE_RCH_HPGAIN_SEL_MASK 0x1 +#define NLE_RCH_HPGAIN_SEL_MASK_SFT (0x1 << 10) +#define NLE_RCH_CH_SEL_SFT 9 +#define NLE_RCH_CH_SEL_MASK 0x1 +#define NLE_RCH_CH_SEL_MASK_SFT (0x1 << 9) +#define NLE_RCH_ON_SFT 8 +#define NLE_RCH_ON_MASK 0x1 +#define NLE_RCH_ON_MASK_SFT (0x1 << 8) +#define NLE_LCH_HPGAIN_SEL_SFT 2 +#define NLE_LCH_HPGAIN_SEL_MASK 0x1 +#define NLE_LCH_HPGAIN_SEL_MASK_SFT (0x1 << 2) +#define NLE_LCH_CH_SEL_SFT 1 +#define NLE_LCH_CH_SEL_MASK 0x1 +#define NLE_LCH_CH_SEL_MASK_SFT (0x1 << 1) +#define NLE_LCH_ON_SFT 0 +#define NLE_LCH_ON_MASK 0x1 +#define NLE_LCH_ON_MASK_SFT (0x1 << 0) + +/* AFE_DL_NLE_MON */ +#define NLE_MONITOR_SFT 0 +#define NLE_MONITOR_MASK 0x3fff +#define NLE_MONITOR_MASK_SFT (0x3fff << 0) + +/* AFE_CG_EN_MON */ +#define CK_CG_EN_MON_SFT 0 +#define CK_CG_EN_MON_MASK 0x3f +#define CK_CG_EN_MON_MASK_SFT (0x3f << 0) + +/* AFE_MIC_ARRAY_CFG */ +#define RG_AMIC_ADC1_SOURCE_SEL_SFT 10 +#define RG_AMIC_ADC1_SOURCE_SEL_MASK 0x3 +#define RG_AMIC_ADC1_SOURCE_SEL_MASK_SFT (0x3 << 10) +#define RG_AMIC_ADC2_SOURCE_SEL_SFT 8 +#define RG_AMIC_ADC2_SOURCE_SEL_MASK 0x3 +#define RG_AMIC_ADC2_SOURCE_SEL_MASK_SFT (0x3 << 8) +#define RG_AMIC_ADC3_SOURCE_SEL_SFT 6 +#define RG_AMIC_ADC3_SOURCE_SEL_MASK 0x3 +#define RG_AMIC_ADC3_SOURCE_SEL_MASK_SFT (0x3 << 6) +#define RG_DMIC_ADC1_SOURCE_SEL_SFT 4 +#define RG_DMIC_ADC1_SOURCE_SEL_MASK 0x3 +#define RG_DMIC_ADC1_SOURCE_SEL_MASK_SFT (0x3 << 4) +#define RG_DMIC_ADC2_SOURCE_SEL_SFT 2 +#define RG_DMIC_ADC2_SOURCE_SEL_MASK 0x3 +#define RG_DMIC_ADC2_SOURCE_SEL_MASK_SFT (0x3 << 2) +#define RG_DMIC_ADC3_SOURCE_SEL_SFT 0 +#define RG_DMIC_ADC3_SOURCE_SEL_MASK 0x3 +#define RG_DMIC_ADC3_SOURCE_SEL_MASK_SFT (0x3 << 0) + +/* AFE_CHOP_CFG0 */ +#define RG_CHOP_DIV_SEL_SFT 4 +#define RG_CHOP_DIV_SEL_MASK 0x1f +#define RG_CHOP_DIV_SEL_MASK_SFT (0x1f << 4) +#define RG_CHOP_DIV_EN_SFT 0 +#define RG_CHOP_DIV_EN_MASK 0x1 +#define RG_CHOP_DIV_EN_MASK_SFT (0x1 << 0) + +/* AFE_MTKAIF_MUX_CFG */ +#define RG_ADDA6_EN_SEL_SFT 12 +#define RG_ADDA6_EN_SEL_MASK 0x1 +#define RG_ADDA6_EN_SEL_MASK_SFT (0x1 << 12) +#define RG_ADDA6_CH2_SEL_SFT 10 +#define RG_ADDA6_CH2_SEL_MASK 0x3 +#define RG_ADDA6_CH2_SEL_MASK_SFT (0x3 << 10) +#define RG_ADDA6_CH1_SEL_SFT 8 +#define RG_ADDA6_CH1_SEL_MASK 0x3 +#define RG_ADDA6_CH1_SEL_MASK_SFT (0x3 << 8) +#define RG_ADDA_EN_SEL_SFT 4 +#define RG_ADDA_EN_SEL_MASK 0x1 +#define RG_ADDA_EN_SEL_MASK_SFT (0x1 << 4) +#define RG_ADDA_CH2_SEL_SFT 2 +#define RG_ADDA_CH2_SEL_MASK 0x3 +#define RG_ADDA_CH2_SEL_MASK_SFT (0x3 << 2) +#define RG_ADDA_CH1_SEL_SFT 0 +#define RG_ADDA_CH1_SEL_MASK 0x3 +#define RG_ADDA_CH1_SEL_MASK_SFT (0x3 << 0) + +/* AFE_PMIC_NEWIF_CFG3 */ +#define RG_UP8X_SYNC_WORD_SFT 0 +#define RG_UP8X_SYNC_WORD_MASK 0xffff +#define RG_UP8X_SYNC_WORD_MASK_SFT (0xffff << 0) + +/* AFE_NCP_CFG0 */ +#define RG_NCP_CK1_VALID_CNT_SFT 9 +#define RG_NCP_CK1_VALID_CNT_MASK 0x7f +#define RG_NCP_CK1_VALID_CNT_MASK_SFT (0x7f << 9) +#define RG_NCP_ADITH_SFT 8 +#define RG_NCP_ADITH_MASK 0x1 +#define RG_NCP_ADITH_MASK_SFT (0x1 << 8) +#define RG_NCP_DITHER_EN_SFT 7 +#define RG_NCP_DITHER_EN_MASK 0x1 +#define RG_NCP_DITHER_EN_MASK_SFT (0x1 << 7) +#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_SFT 4 +#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_MASK 0x7 +#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_MASK_SFT (0x7 << 4) +#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_SFT 1 +#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_MASK 0x7 +#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_MASK_SFT (0x7 << 1) +#define RG_NCP_ON_SFT 0 +#define RG_NCP_ON_MASK 0x1 +#define RG_NCP_ON_MASK_SFT (0x1 << 0) + +/* AFE_NCP_CFG1 */ +#define RG_XY_VAL_CFG_EN_SFT 15 +#define RG_XY_VAL_CFG_EN_MASK 0x1 +#define RG_XY_VAL_CFG_EN_MASK_SFT (0x1 << 15) +#define RG_X_VAL_CFG_SFT 8 +#define RG_X_VAL_CFG_MASK 0x7f +#define RG_X_VAL_CFG_MASK_SFT (0x7f << 8) +#define RG_Y_VAL_CFG_SFT 0 +#define RG_Y_VAL_CFG_MASK 0x7f +#define RG_Y_VAL_CFG_MASK_SFT (0x7f << 0) + +/* AFE_NCP_CFG2 */ +#define RG_NCP_NONCLK_SET_SFT 1 +#define RG_NCP_NONCLK_SET_MASK 0x1 +#define RG_NCP_NONCLK_SET_MASK_SFT (0x1 << 1) +#define RG_NCP_PDDIS_EN_SFT 0 +#define RG_NCP_PDDIS_EN_MASK 0x1 +#define RG_NCP_PDDIS_EN_MASK_SFT (0x1 << 0) + +/* AUDENC_ANA_CON0 */ +#define RG_AUDPREAMPLON_SFT 0 +#define RG_AUDPREAMPLON_MASK 0x1 +#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMPLDCCEN_SFT 1 +#define RG_AUDPREAMPLDCCEN_MASK 0x1 +#define RG_AUDPREAMPLDCCEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPLDCPRECHARGE_SFT 2 +#define RG_AUDPREAMPLDCPRECHARGE_MASK 0x1 +#define RG_AUDPREAMPLDCPRECHARGE_MASK_SFT (0x1 << 2) +#define RG_AUDPREAMPLPGATEST_SFT 3 +#define RG_AUDPREAMPLPGATEST_MASK 0x1 +#define RG_AUDPREAMPLPGATEST_MASK_SFT (0x1 << 3) +#define RG_AUDPREAMPLVSCALE_SFT 4 +#define RG_AUDPREAMPLVSCALE_MASK 0x3 +#define RG_AUDPREAMPLVSCALE_MASK_SFT (0x3 << 4) +#define RG_AUDPREAMPLINPUTSEL_SFT 6 +#define RG_AUDPREAMPLINPUTSEL_MASK 0x3 +#define RG_AUDPREAMPLINPUTSEL_MASK_SFT (0x3 << 6) +#define RG_AUDPREAMPLGAIN_SFT 8 +#define RG_AUDPREAMPLGAIN_MASK 0x7 +#define RG_AUDPREAMPLGAIN_MASK_SFT (0x7 << 8) +#define RG_BULKL_VCM_EN_SFT 11 +#define RG_BULKL_VCM_EN_MASK 0x1 +#define RG_BULKL_VCM_EN_MASK_SFT (0x1 << 11) +#define RG_AUDADCLPWRUP_SFT 12 +#define RG_AUDADCLPWRUP_MASK 0x1 +#define RG_AUDADCLPWRUP_MASK_SFT (0x1 << 12) +#define RG_AUDADCLINPUTSEL_SFT 13 +#define RG_AUDADCLINPUTSEL_MASK 0x3 +#define RG_AUDADCLINPUTSEL_MASK_SFT (0x3 << 13) + +/* AUDENC_ANA_CON1 */ +#define RG_AUDPREAMPRON_SFT 0 +#define RG_AUDPREAMPRON_MASK 0x1 +#define RG_AUDPREAMPRON_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMPRDCCEN_SFT 1 +#define RG_AUDPREAMPRDCCEN_MASK 0x1 +#define RG_AUDPREAMPRDCCEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPRDCPRECHARGE_SFT 2 +#define RG_AUDPREAMPRDCPRECHARGE_MASK 0x1 +#define RG_AUDPREAMPRDCPRECHARGE_MASK_SFT (0x1 << 2) +#define RG_AUDPREAMPRPGATEST_SFT 3 +#define RG_AUDPREAMPRPGATEST_MASK 0x1 +#define RG_AUDPREAMPRPGATEST_MASK_SFT (0x1 << 3) +#define RG_AUDPREAMPRVSCALE_SFT 4 +#define RG_AUDPREAMPRVSCALE_MASK 0x3 +#define RG_AUDPREAMPRVSCALE_MASK_SFT (0x3 << 4) +#define RG_AUDPREAMPRINPUTSEL_SFT 6 +#define RG_AUDPREAMPRINPUTSEL_MASK 0x3 +#define RG_AUDPREAMPRINPUTSEL_MASK_SFT (0x3 << 6) +#define RG_AUDPREAMPRGAIN_SFT 8 +#define RG_AUDPREAMPRGAIN_MASK 0x7 +#define RG_AUDPREAMPRGAIN_MASK_SFT (0x7 << 8) +#define RG_BULKR_VCM_EN_SFT 11 +#define RG_BULKR_VCM_EN_MASK 0x1 +#define RG_BULKR_VCM_EN_MASK_SFT (0x1 << 11) +#define RG_AUDADCRPWRUP_SFT 12 +#define RG_AUDADCRPWRUP_MASK 0x1 +#define RG_AUDADCRPWRUP_MASK_SFT (0x1 << 12) +#define RG_AUDADCRINPUTSEL_SFT 13 +#define RG_AUDADCRINPUTSEL_MASK 0x3 +#define RG_AUDADCRINPUTSEL_MASK_SFT (0x3 << 13) + +/* AUDENC_ANA_CON2 */ +#define RG_AUDPREAMP3ON_SFT 0 +#define RG_AUDPREAMP3ON_MASK 0x1 +#define RG_AUDPREAMP3ON_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMP3DCCEN_SFT 1 +#define RG_AUDPREAMP3DCCEN_MASK 0x1 +#define RG_AUDPREAMP3DCCEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMP3DCPRECHARGE_SFT 2 +#define RG_AUDPREAMP3DCPRECHARGE_MASK 0x1 +#define RG_AUDPREAMP3DCPRECHARGE_MASK_SFT (0x1 << 2) +#define RG_AUDPREAMP3PGATEST_SFT 3 +#define RG_AUDPREAMP3PGATEST_MASK 0x1 +#define RG_AUDPREAMP3PGATEST_MASK_SFT (0x1 << 3) +#define RG_AUDPREAMP3VSCALE_SFT 4 +#define RG_AUDPREAMP3VSCALE_MASK 0x3 +#define RG_AUDPREAMP3VSCALE_MASK_SFT (0x3 << 4) +#define RG_AUDPREAMP3INPUTSEL_SFT 6 +#define RG_AUDPREAMP3INPUTSEL_MASK 0x3 +#define RG_AUDPREAMP3INPUTSEL_MASK_SFT (0x3 << 6) +#define RG_AUDPREAMP3GAIN_SFT 8 +#define RG_AUDPREAMP3GAIN_MASK 0x7 +#define RG_AUDPREAMP3GAIN_MASK_SFT (0x7 << 8) +#define RG_BULK3_VCM_EN_SFT 11 +#define RG_BULK3_VCM_EN_MASK 0x1 +#define RG_BULK3_VCM_EN_MASK_SFT (0x1 << 11) +#define RG_AUDADC3PWRUP_SFT 12 +#define RG_AUDADC3PWRUP_MASK 0x1 +#define RG_AUDADC3PWRUP_MASK_SFT (0x1 << 12) +#define RG_AUDADC3INPUTSEL_SFT 13 +#define RG_AUDADC3INPUTSEL_MASK 0x3 +#define RG_AUDADC3INPUTSEL_MASK_SFT (0x3 << 13) + +/* AUDENC_ANA_CON3 */ +#define RG_AUDULHALFBIAS_SFT 0 +#define RG_AUDULHALFBIAS_MASK 0x1 +#define RG_AUDULHALFBIAS_MASK_SFT (0x1 << 0) +#define RG_AUDGLBVOWLPWEN_SFT 1 +#define RG_AUDGLBVOWLPWEN_MASK 0x1 +#define RG_AUDGLBVOWLPWEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPLPEN_SFT 2 +#define RG_AUDPREAMPLPEN_MASK 0x1 +#define RG_AUDPREAMPLPEN_MASK_SFT (0x1 << 2) +#define RG_AUDADC1STSTAGELPEN_SFT 3 +#define RG_AUDADC1STSTAGELPEN_MASK 0x1 +#define RG_AUDADC1STSTAGELPEN_MASK_SFT (0x1 << 3) +#define RG_AUDADC2NDSTAGELPEN_SFT 4 +#define RG_AUDADC2NDSTAGELPEN_MASK 0x1 +#define RG_AUDADC2NDSTAGELPEN_MASK_SFT (0x1 << 4) +#define RG_AUDADCFLASHLPEN_SFT 5 +#define RG_AUDADCFLASHLPEN_MASK 0x1 +#define RG_AUDADCFLASHLPEN_MASK_SFT (0x1 << 5) +#define RG_AUDPREAMPIDDTEST_SFT 6 +#define RG_AUDPREAMPIDDTEST_MASK 0x3 +#define RG_AUDPREAMPIDDTEST_MASK_SFT (0x3 << 6) +#define RG_AUDADC1STSTAGEIDDTEST_SFT 8 +#define RG_AUDADC1STSTAGEIDDTEST_MASK 0x3 +#define RG_AUDADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8) +#define RG_AUDADC2NDSTAGEIDDTEST_SFT 10 +#define RG_AUDADC2NDSTAGEIDDTEST_MASK 0x3 +#define RG_AUDADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10) +#define RG_AUDADCREFBUFIDDTEST_SFT 12 +#define RG_AUDADCREFBUFIDDTEST_MASK 0x3 +#define RG_AUDADCREFBUFIDDTEST_MASK_SFT (0x3 << 12) +#define RG_AUDADCFLASHIDDTEST_SFT 14 +#define RG_AUDADCFLASHIDDTEST_MASK 0x3 +#define RG_AUDADCFLASHIDDTEST_MASK_SFT (0x3 << 14) + +/* AUDENC_ANA_CON4 */ +#define RG_AUDRULHALFBIAS_SFT 0 +#define RG_AUDRULHALFBIAS_MASK 0x1 +#define RG_AUDRULHALFBIAS_MASK_SFT (0x1 << 0) +#define RG_AUDGLBRVOWLPWEN_SFT 1 +#define RG_AUDGLBRVOWLPWEN_MASK 0x1 +#define RG_AUDGLBRVOWLPWEN_MASK_SFT (0x1 << 1) +#define RG_AUDRPREAMPLPEN_SFT 2 +#define RG_AUDRPREAMPLPEN_MASK 0x1 +#define RG_AUDRPREAMPLPEN_MASK_SFT (0x1 << 2) +#define RG_AUDRADC1STSTAGELPEN_SFT 3 +#define RG_AUDRADC1STSTAGELPEN_MASK 0x1 +#define RG_AUDRADC1STSTAGELPEN_MASK_SFT (0x1 << 3) +#define RG_AUDRADC2NDSTAGELPEN_SFT 4 +#define RG_AUDRADC2NDSTAGELPEN_MASK 0x1 +#define RG_AUDRADC2NDSTAGELPEN_MASK_SFT (0x1 << 4) +#define RG_AUDRADCFLASHLPEN_SFT 5 +#define RG_AUDRADCFLASHLPEN_MASK 0x1 +#define RG_AUDRADCFLASHLPEN_MASK_SFT (0x1 << 5) +#define RG_AUDRPREAMPIDDTEST_SFT 6 +#define RG_AUDRPREAMPIDDTEST_MASK 0x3 +#define RG_AUDRPREAMPIDDTEST_MASK_SFT (0x3 << 6) +#define RG_AUDRADC1STSTAGEIDDTEST_SFT 8 +#define RG_AUDRADC1STSTAGEIDDTEST_MASK 0x3 +#define RG_AUDRADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8) +#define RG_AUDRADC2NDSTAGEIDDTEST_SFT 10 +#define RG_AUDRADC2NDSTAGEIDDTEST_MASK 0x3 +#define RG_AUDRADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10) +#define RG_AUDRADCREFBUFIDDTEST_SFT 12 +#define RG_AUDRADCREFBUFIDDTEST_MASK 0x3 +#define RG_AUDRADCREFBUFIDDTEST_MASK_SFT (0x3 << 12) +#define RG_AUDRADCFLASHIDDTEST_SFT 14 +#define RG_AUDRADCFLASHIDDTEST_MASK 0x3 +#define RG_AUDRADCFLASHIDDTEST_MASK_SFT (0x3 << 14) + +/* AUDENC_ANA_CON5 */ +#define RG_AUDADCCLKRSTB_SFT 0 +#define RG_AUDADCCLKRSTB_MASK 0x1 +#define RG_AUDADCCLKRSTB_MASK_SFT (0x1 << 0) +#define RG_AUDADCCLKSEL_SFT 1 +#define RG_AUDADCCLKSEL_MASK 0x3 +#define RG_AUDADCCLKSEL_MASK_SFT (0x3 << 1) +#define RG_AUDADCCLKSOURCE_SFT 3 +#define RG_AUDADCCLKSOURCE_MASK 0x3 +#define RG_AUDADCCLKSOURCE_MASK_SFT (0x3 << 3) +#define RG_AUDADCCLKGENMODE_SFT 5 +#define RG_AUDADCCLKGENMODE_MASK 0x3 +#define RG_AUDADCCLKGENMODE_MASK_SFT (0x3 << 5) +#define RG_AUDPREAMP_ACCFS_SFT 7 +#define RG_AUDPREAMP_ACCFS_MASK 0x1 +#define RG_AUDPREAMP_ACCFS_MASK_SFT (0x1 << 7) +#define RG_AUDPREAMPAAFEN_SFT 8 +#define RG_AUDPREAMPAAFEN_MASK 0x1 +#define RG_AUDPREAMPAAFEN_MASK_SFT (0x1 << 8) +#define RG_DCCVCMBUFLPMODSEL_SFT 9 +#define RG_DCCVCMBUFLPMODSEL_MASK 0x1 +#define RG_DCCVCMBUFLPMODSEL_MASK_SFT (0x1 << 9) +#define RG_DCCVCMBUFLPSWEN_SFT 10 +#define RG_DCCVCMBUFLPSWEN_MASK 0x1 +#define RG_DCCVCMBUFLPSWEN_MASK_SFT (0x1 << 10) +#define RG_AUDSPAREPGA_SFT 11 +#define RG_AUDSPAREPGA_MASK 0x1f +#define RG_AUDSPAREPGA_MASK_SFT (0x1f << 11) + +/* AUDENC_ANA_CON6 */ +#define RG_AUDADC1STSTAGESDENB_SFT 0 +#define RG_AUDADC1STSTAGESDENB_MASK 0x1 +#define RG_AUDADC1STSTAGESDENB_MASK_SFT (0x1 << 0) +#define RG_AUDADC2NDSTAGERESET_SFT 1 +#define RG_AUDADC2NDSTAGERESET_MASK 0x1 +#define RG_AUDADC2NDSTAGERESET_MASK_SFT (0x1 << 1) +#define RG_AUDADC3RDSTAGERESET_SFT 2 +#define RG_AUDADC3RDSTAGERESET_MASK 0x1 +#define RG_AUDADC3RDSTAGERESET_MASK_SFT (0x1 << 2) +#define RG_AUDADCFSRESET_SFT 3 +#define RG_AUDADCFSRESET_MASK 0x1 +#define RG_AUDADCFSRESET_MASK_SFT (0x1 << 3) +#define RG_AUDADCWIDECM_SFT 4 +#define RG_AUDADCWIDECM_MASK 0x1 +#define RG_AUDADCWIDECM_MASK_SFT (0x1 << 4) +#define RG_AUDADCNOPATEST_SFT 5 +#define RG_AUDADCNOPATEST_MASK 0x1 +#define RG_AUDADCNOPATEST_MASK_SFT (0x1 << 5) +#define RG_AUDADCBYPASS_SFT 6 +#define RG_AUDADCBYPASS_MASK 0x1 +#define RG_AUDADCBYPASS_MASK_SFT (0x1 << 6) +#define RG_AUDADCFFBYPASS_SFT 7 +#define RG_AUDADCFFBYPASS_MASK 0x1 +#define RG_AUDADCFFBYPASS_MASK_SFT (0x1 << 7) +#define RG_AUDADCDACFBCURRENT_SFT 8 +#define RG_AUDADCDACFBCURRENT_MASK 0x1 +#define RG_AUDADCDACFBCURRENT_MASK_SFT (0x1 << 8) +#define RG_AUDADCDACIDDTEST_SFT 9 +#define RG_AUDADCDACIDDTEST_MASK 0x3 +#define RG_AUDADCDACIDDTEST_MASK_SFT (0x3 << 9) +#define RG_AUDADCDACNRZ_SFT 11 +#define RG_AUDADCDACNRZ_MASK 0x1 +#define RG_AUDADCDACNRZ_MASK_SFT (0x1 << 11) +#define RG_AUDADCNODEM_SFT 12 +#define RG_AUDADCNODEM_MASK 0x1 +#define RG_AUDADCNODEM_MASK_SFT (0x1 << 12) +#define RG_AUDADCDACTEST_SFT 13 +#define RG_AUDADCDACTEST_MASK 0x1 +#define RG_AUDADCDACTEST_MASK_SFT (0x1 << 13) +#define RG_AUDADCDAC0P25FS_SFT 14 +#define RG_AUDADCDAC0P25FS_MASK 0x1 +#define RG_AUDADCDAC0P25FS_MASK_SFT (0x1 << 14) +#define RG_AUDADCRDAC0P25FS_SFT 15 +#define RG_AUDADCRDAC0P25FS_MASK 0x1 +#define RG_AUDADCRDAC0P25FS_MASK_SFT (0x1 << 15) + +/* AUDENC_ANA_CON7 */ +#define RG_AUDADCTESTDATA_SFT 0 +#define RG_AUDADCTESTDATA_MASK 0xffff +#define RG_AUDADCTESTDATA_MASK_SFT (0xffff << 0) + +/* AUDENC_ANA_CON8 */ +#define RG_AUDRCTUNEL_SFT 0 +#define RG_AUDRCTUNEL_MASK 0x1f +#define RG_AUDRCTUNEL_MASK_SFT (0x1f << 0) +#define RG_AUDRCTUNELSEL_SFT 5 +#define RG_AUDRCTUNELSEL_MASK 0x1 +#define RG_AUDRCTUNELSEL_MASK_SFT (0x1 << 5) +#define RG_AUDRCTUNER_SFT 8 +#define RG_AUDRCTUNER_MASK 0x1f +#define RG_AUDRCTUNER_MASK_SFT (0x1f << 8) +#define RG_AUDRCTUNERSEL_SFT 13 +#define RG_AUDRCTUNERSEL_MASK 0x1 +#define RG_AUDRCTUNERSEL_MASK_SFT (0x1 << 13) + +/* AUDENC_ANA_CON9 */ +#define RG_AUD3CTUNEL_SFT 0 +#define RG_AUD3CTUNEL_MASK 0x1f +#define RG_AUD3CTUNEL_MASK_SFT (0x1f << 0) +#define RG_AUD3CTUNELSEL_SFT 5 +#define RG_AUD3CTUNELSEL_MASK 0x1 +#define RG_AUD3CTUNELSEL_MASK_SFT (0x1 << 5) +#define RGS_AUDRCTUNE3READ_SFT 6 +#define RGS_AUDRCTUNE3READ_MASK 0x1f +#define RGS_AUDRCTUNE3READ_MASK_SFT (0x1f << 6) +#define RG_AUD3SPARE_SFT 11 +#define RG_AUD3SPARE_MASK 0x1f +#define RG_AUD3SPARE_MASK_SFT (0x1f << 11) + +/* AUDENC_ANA_CON10 */ +#define RGS_AUDRCTUNELREAD_SFT 0 +#define RGS_AUDRCTUNELREAD_MASK 0x1f +#define RGS_AUDRCTUNELREAD_MASK_SFT (0x1f << 0) +#define RGS_AUDRCTUNERREAD_SFT 8 +#define RGS_AUDRCTUNERREAD_MASK 0x1f +#define RGS_AUDRCTUNERREAD_MASK_SFT (0x1f << 8) + +/* AUDENC_ANA_CON11 */ +#define RG_AUDSPAREVA30_SFT 0 +#define RG_AUDSPAREVA30_MASK 0xff +#define RG_AUDSPAREVA30_MASK_SFT (0xff << 0) +#define RG_AUDSPAREVA18_SFT 8 +#define RG_AUDSPAREVA18_MASK 0xff +#define RG_AUDSPAREVA18_MASK_SFT (0xff << 8) + +/* AUDENC_ANA_CON12 */ +#define RG_AUDPGA_DECAP_SFT 0 +#define RG_AUDPGA_DECAP_MASK 0x1 +#define RG_AUDPGA_DECAP_MASK_SFT (0x1 << 0) +#define RG_AUDPGA_CAPRA_SFT 1 +#define RG_AUDPGA_CAPRA_MASK 0x1 +#define RG_AUDPGA_CAPRA_MASK_SFT (0x1 << 1) +#define RG_AUDPGA_ACCCMP_SFT 2 +#define RG_AUDPGA_ACCCMP_MASK 0x1 +#define RG_AUDPGA_ACCCMP_MASK_SFT (0x1 << 2) +#define RG_AUDENC_SPARE2_SFT 3 +#define RG_AUDENC_SPARE2_MASK 0x1fff +#define RG_AUDENC_SPARE2_MASK_SFT (0x1fff << 3) + +/* AUDENC_ANA_CON13 */ +#define RG_AUDDIGMICEN_SFT 0 +#define RG_AUDDIGMICEN_MASK 0x1 +#define RG_AUDDIGMICEN_MASK_SFT (0x1 << 0) +#define RG_AUDDIGMICBIAS_SFT 1 +#define RG_AUDDIGMICBIAS_MASK 0x3 +#define RG_AUDDIGMICBIAS_MASK_SFT (0x3 << 1) +#define RG_DMICHPCLKEN_SFT 3 +#define RG_DMICHPCLKEN_MASK 0x1 +#define RG_DMICHPCLKEN_MASK_SFT (0x1 << 3) +#define RG_AUDDIGMICPDUTY_SFT 4 +#define RG_AUDDIGMICPDUTY_MASK 0x3 +#define RG_AUDDIGMICPDUTY_MASK_SFT (0x3 << 4) +#define RG_AUDDIGMICNDUTY_SFT 6 +#define RG_AUDDIGMICNDUTY_MASK 0x3 +#define RG_AUDDIGMICNDUTY_MASK_SFT (0x3 << 6) +#define RG_DMICMONEN_SFT 8 +#define RG_DMICMONEN_MASK 0x1 +#define RG_DMICMONEN_MASK_SFT (0x1 << 8) +#define RG_DMICMONSEL_SFT 9 +#define RG_DMICMONSEL_MASK 0x7 +#define RG_DMICMONSEL_MASK_SFT (0x7 << 9) + +/* AUDENC_ANA_CON14 */ +#define RG_AUDDIGMIC1EN_SFT 0 +#define RG_AUDDIGMIC1EN_MASK 0x1 +#define RG_AUDDIGMIC1EN_MASK_SFT (0x1 << 0) +#define RG_AUDDIGMICBIAS1_SFT 1 +#define RG_AUDDIGMICBIAS1_MASK 0x3 +#define RG_AUDDIGMICBIAS1_MASK_SFT (0x3 << 1) +#define RG_DMIC1HPCLKEN_SFT 3 +#define RG_DMIC1HPCLKEN_MASK 0x1 +#define RG_DMIC1HPCLKEN_MASK_SFT (0x1 << 3) +#define RG_AUDDIGMIC1PDUTY_SFT 4 +#define RG_AUDDIGMIC1PDUTY_MASK 0x3 +#define RG_AUDDIGMIC1PDUTY_MASK_SFT (0x3 << 4) +#define RG_AUDDIGMIC1NDUTY_SFT 6 +#define RG_AUDDIGMIC1NDUTY_MASK 0x3 +#define RG_AUDDIGMIC1NDUTY_MASK_SFT (0x3 << 6) +#define RG_DMIC1MONEN_SFT 8 +#define RG_DMIC1MONEN_MASK 0x1 +#define RG_DMIC1MONEN_MASK_SFT (0x1 << 8) +#define RG_DMIC1MONSEL_SFT 9 +#define RG_DMIC1MONSEL_MASK 0x7 +#define RG_DMIC1MONSEL_MASK_SFT (0x7 << 9) +#define RG_AUDSPAREVMIC_SFT 12 +#define RG_AUDSPAREVMIC_MASK 0xf +#define RG_AUDSPAREVMIC_MASK_SFT (0xf << 12) + +/* AUDENC_ANA_CON15 */ +#define RG_AUDPWDBMICBIAS0_SFT 0 +#define RG_AUDPWDBMICBIAS0_MASK 0x1 +#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS0BYPASSEN_SFT 1 +#define RG_AUDMICBIAS0BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS0BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS0LOWPEN_SFT 2 +#define RG_AUDMICBIAS0LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS0LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDPWDBMICBIAS3_SFT 3 +#define RG_AUDPWDBMICBIAS3_MASK 0x1 +#define RG_AUDPWDBMICBIAS3_MASK_SFT (0x1 << 3) +#define RG_AUDMICBIAS0VREF_SFT 4 +#define RG_AUDMICBIAS0VREF_MASK 0x7 +#define RG_AUDMICBIAS0VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS0DCSW0P1EN_SFT 8 +#define RG_AUDMICBIAS0DCSW0P1EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0P1EN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS0DCSW0P2EN_SFT 9 +#define RG_AUDMICBIAS0DCSW0P2EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0P2EN_MASK_SFT (0x1 << 9) +#define RG_AUDMICBIAS0DCSW0NEN_SFT 10 +#define RG_AUDMICBIAS0DCSW0NEN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0NEN_MASK_SFT (0x1 << 10) +#define RG_AUDMICBIAS0DCSW2P1EN_SFT 12 +#define RG_AUDMICBIAS0DCSW2P1EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2P1EN_MASK_SFT (0x1 << 12) +#define RG_AUDMICBIAS0DCSW2P2EN_SFT 13 +#define RG_AUDMICBIAS0DCSW2P2EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2P2EN_MASK_SFT (0x1 << 13) +#define RG_AUDMICBIAS0DCSW2NEN_SFT 14 +#define RG_AUDMICBIAS0DCSW2NEN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2NEN_MASK_SFT (0x1 << 14) + +/* AUDENC_ANA_CON16 */ +#define RG_AUDPWDBMICBIAS1_SFT 0 +#define RG_AUDPWDBMICBIAS1_MASK 0x1 +#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS1BYPASSEN_SFT 1 +#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS1LOWPEN_SFT 2 +#define RG_AUDMICBIAS1LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDMICBIAS1VREF_SFT 4 +#define RG_AUDMICBIAS1VREF_MASK 0x7 +#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS1DCSW1PEN_SFT 8 +#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1 +#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS1DCSW1NEN_SFT 9 +#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1 +#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9) +#define RG_BANDGAPGEN_SFT 10 +#define RG_BANDGAPGEN_MASK 0x1 +#define RG_BANDGAPGEN_MASK_SFT (0x1 << 10) +#define RG_AUDMICBIAS1HVEN_SFT 12 +#define RG_AUDMICBIAS1HVEN_MASK 0x1 +#define RG_AUDMICBIAS1HVEN_MASK_SFT (0x1 << 12) +#define RG_AUDMICBIAS1HVVREF_SFT 13 +#define RG_AUDMICBIAS1HVVREF_MASK 0x1 +#define RG_AUDMICBIAS1HVVREF_MASK_SFT (0x1 << 13) + +/* AUDENC_ANA_CON17 */ +#define RG_AUDPWDBMICBIAS2_SFT 0 +#define RG_AUDPWDBMICBIAS2_MASK 0x1 +#define RG_AUDPWDBMICBIAS2_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS2BYPASSEN_SFT 1 +#define RG_AUDMICBIAS2BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS2BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS2LOWPEN_SFT 2 +#define RG_AUDMICBIAS2LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS2LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDMICBIAS2VREF_SFT 4 +#define RG_AUDMICBIAS2VREF_MASK 0x7 +#define RG_AUDMICBIAS2VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS2DCSW3P1EN_SFT 8 +#define RG_AUDMICBIAS2DCSW3P1EN_MASK 0x1 +#define RG_AUDMICBIAS2DCSW3P1EN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS2DCSW3P2EN_SFT 9 +#define RG_AUDMICBIAS2DCSW3P2EN_MASK 0x1 +#define RG_AUDMICBIAS2DCSW3P2EN_MASK_SFT (0x1 << 9) +#define RG_AUDMICBIAS2DCSW3NEN_SFT 10 +#define RG_AUDMICBIAS2DCSW3NEN_MASK 0x1 +#define RG_AUDMICBIAS2DCSW3NEN_MASK_SFT (0x1 << 10) +#define RG_AUDMICBIASSPARE_SFT 12 +#define RG_AUDMICBIASSPARE_MASK 0xf +#define RG_AUDMICBIASSPARE_MASK_SFT (0xf << 12) + +/* AUDENC_ANA_CON18 */ +#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0 +#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0) +#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1 +#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1) +#define RG_AUDACCDETMICBIAS2PULLLOW_SFT 2 +#define RG_AUDACCDETMICBIAS2PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS2PULLLOW_MASK_SFT (0x1 << 2) +#define RG_AUDACCDETVIN1PULLLOW_SFT 3 +#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1 +#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 3) +#define RG_AUDACCDETVTHACAL_SFT 4 +#define RG_AUDACCDETVTHACAL_MASK 0x1 +#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4) +#define RG_AUDACCDETVTHBCAL_SFT 5 +#define RG_AUDACCDETVTHBCAL_MASK 0x1 +#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5) +#define RG_AUDACCDETTVDET_SFT 6 +#define RG_AUDACCDETTVDET_MASK 0x1 +#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6) +#define RG_ACCDETSEL_SFT 7 +#define RG_ACCDETSEL_MASK 0x1 +#define RG_ACCDETSEL_MASK_SFT (0x1 << 7) +#define RG_SWBUFMODSEL_SFT 8 +#define RG_SWBUFMODSEL_MASK 0x1 +#define RG_SWBUFMODSEL_MASK_SFT (0x1 << 8) +#define RG_SWBUFSWEN_SFT 9 +#define RG_SWBUFSWEN_MASK 0x1 +#define RG_SWBUFSWEN_MASK_SFT (0x1 << 9) +#define RG_EINT0NOHYS_SFT 10 +#define RG_EINT0NOHYS_MASK 0x1 +#define RG_EINT0NOHYS_MASK_SFT (0x1 << 10) +#define RG_EINT0CONFIGACCDET_SFT 11 +#define RG_EINT0CONFIGACCDET_MASK 0x1 +#define RG_EINT0CONFIGACCDET_MASK_SFT (0x1 << 11) +#define RG_EINT0HIRENB_SFT 12 +#define RG_EINT0HIRENB_MASK 0x1 +#define RG_EINT0HIRENB_MASK_SFT (0x1 << 12) +#define RG_ACCDET2AUXRESBYPASS_SFT 13 +#define RG_ACCDET2AUXRESBYPASS_MASK 0x1 +#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13) +#define RG_ACCDET2AUXSWEN_SFT 14 +#define RG_ACCDET2AUXSWEN_MASK 0x1 +#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 14) +#define RG_AUDACCDETMICBIAS3PULLLOW_SFT 15 +#define RG_AUDACCDETMICBIAS3PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS3PULLLOW_MASK_SFT (0x1 << 15) + +/* AUDENC_ANA_CON19 */ +#define RG_EINT1CONFIGACCDET_SFT 0 +#define RG_EINT1CONFIGACCDET_MASK 0x1 +#define RG_EINT1CONFIGACCDET_MASK_SFT (0x1 << 0) +#define RG_EINT1HIRENB_SFT 1 +#define RG_EINT1HIRENB_MASK 0x1 +#define RG_EINT1HIRENB_MASK_SFT (0x1 << 1) +#define RG_EINT1NOHYS_SFT 2 +#define RG_EINT1NOHYS_MASK 0x1 +#define RG_EINT1NOHYS_MASK_SFT (0x1 << 2) +#define RG_EINTCOMPVTH_SFT 4 +#define RG_EINTCOMPVTH_MASK 0xf +#define RG_EINTCOMPVTH_MASK_SFT (0xf << 4) +#define RG_MTEST_EN_SFT 8 +#define RG_MTEST_EN_MASK 0x1 +#define RG_MTEST_EN_MASK_SFT (0x1 << 8) +#define RG_MTEST_SEL_SFT 9 +#define RG_MTEST_SEL_MASK 0x1 +#define RG_MTEST_SEL_MASK_SFT (0x1 << 9) +#define RG_MTEST_CURRENT_SFT 10 +#define RG_MTEST_CURRENT_MASK 0x1 +#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 10) +#define RG_ANALOGFDEN_SFT 12 +#define RG_ANALOGFDEN_MASK 0x1 +#define RG_ANALOGFDEN_MASK_SFT (0x1 << 12) +#define RG_FDVIN1PPULLLOW_SFT 13 +#define RG_FDVIN1PPULLLOW_MASK 0x1 +#define RG_FDVIN1PPULLLOW_MASK_SFT (0x1 << 13) +#define RG_FDEINT0TYPE_SFT 14 +#define RG_FDEINT0TYPE_MASK 0x1 +#define RG_FDEINT0TYPE_MASK_SFT (0x1 << 14) +#define RG_FDEINT1TYPE_SFT 15 +#define RG_FDEINT1TYPE_MASK 0x1 +#define RG_FDEINT1TYPE_MASK_SFT (0x1 << 15) + +/* AUDENC_ANA_CON20 */ +#define RG_EINT0CMPEN_SFT 0 +#define RG_EINT0CMPEN_MASK 0x1 +#define RG_EINT0CMPEN_MASK_SFT (0x1 << 0) +#define RG_EINT0CMPMEN_SFT 1 +#define RG_EINT0CMPMEN_MASK 0x1 +#define RG_EINT0CMPMEN_MASK_SFT (0x1 << 1) +#define RG_EINT0EN_SFT 2 +#define RG_EINT0EN_MASK 0x1 +#define RG_EINT0EN_MASK_SFT (0x1 << 2) +#define RG_EINT0CEN_SFT 3 +#define RG_EINT0CEN_MASK 0x1 +#define RG_EINT0CEN_MASK_SFT (0x1 << 3) +#define RG_EINT0INVEN_SFT 4 +#define RG_EINT0INVEN_MASK 0x1 +#define RG_EINT0INVEN_MASK_SFT (0x1 << 4) +#define RG_EINT0CTURBO_SFT 5 +#define RG_EINT0CTURBO_MASK 0x7 +#define RG_EINT0CTURBO_MASK_SFT (0x7 << 5) +#define RG_EINT1CMPEN_SFT 8 +#define RG_EINT1CMPEN_MASK 0x1 +#define RG_EINT1CMPEN_MASK_SFT (0x1 << 8) +#define RG_EINT1CMPMEN_SFT 9 +#define RG_EINT1CMPMEN_MASK 0x1 +#define RG_EINT1CMPMEN_MASK_SFT (0x1 << 9) +#define RG_EINT1EN_SFT 10 +#define RG_EINT1EN_MASK 0x1 +#define RG_EINT1EN_MASK_SFT (0x1 << 10) +#define RG_EINT1CEN_SFT 11 +#define RG_EINT1CEN_MASK 0x1 +#define RG_EINT1CEN_MASK_SFT (0x1 << 11) +#define RG_EINT1INVEN_SFT 12 +#define RG_EINT1INVEN_MASK 0x1 +#define RG_EINT1INVEN_MASK_SFT (0x1 << 12) +#define RG_EINT1CTURBO_SFT 13 +#define RG_EINT1CTURBO_MASK 0x7 +#define RG_EINT1CTURBO_MASK_SFT (0x7 << 13) + +/* AUDENC_ANA_CON21 */ +#define RG_ACCDETSPARE_SFT 0 +#define RG_ACCDETSPARE_MASK 0xffff +#define RG_ACCDETSPARE_MASK_SFT (0xffff << 0) + +/* AUDENC_ANA_CON22 */ +#define RG_AUDENCSPAREVA30_SFT 0 +#define RG_AUDENCSPAREVA30_MASK 0xff +#define RG_AUDENCSPAREVA30_MASK_SFT (0xff << 0) +#define RG_AUDENCSPAREVA18_SFT 8 +#define RG_AUDENCSPAREVA18_MASK 0xff +#define RG_AUDENCSPAREVA18_MASK_SFT (0xff << 8) + +/* AUDENC_ANA_CON23 */ +#define RG_CLKSQ_EN_SFT 0 +#define RG_CLKSQ_EN_MASK 0x1 +#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0) +#define RG_CLKSQ_IN_SEL_TEST_SFT 1 +#define RG_CLKSQ_IN_SEL_TEST_MASK 0x1 +#define RG_CLKSQ_IN_SEL_TEST_MASK_SFT (0x1 << 1) +#define RG_CM_REFGENSEL_SFT 2 +#define RG_CM_REFGENSEL_MASK 0x1 +#define RG_CM_REFGENSEL_MASK_SFT (0x1 << 2) +#define RG_AUDIO_VOW_EN_SFT 3 +#define RG_AUDIO_VOW_EN_MASK 0x1 +#define RG_AUDIO_VOW_EN_MASK_SFT (0x1 << 3) +#define RG_CLKSQ_EN_VOW_SFT 4 +#define RG_CLKSQ_EN_VOW_MASK 0x1 +#define RG_CLKSQ_EN_VOW_MASK_SFT (0x1 << 4) +#define RG_CLKAND_EN_VOW_SFT 5 +#define RG_CLKAND_EN_VOW_MASK 0x1 +#define RG_CLKAND_EN_VOW_MASK_SFT (0x1 << 5) +#define RG_VOWCLK_SEL_EN_VOW_SFT 6 +#define RG_VOWCLK_SEL_EN_VOW_MASK 0x1 +#define RG_VOWCLK_SEL_EN_VOW_MASK_SFT (0x1 << 6) +#define RG_SPARE_VOW_SFT 7 +#define RG_SPARE_VOW_MASK 0x7 +#define RG_SPARE_VOW_MASK_SFT (0x7 << 7) + +/* AUDDEC_ANA_CON0 */ +#define RG_AUDDACLPWRUP_VAUDP32_SFT 0 +#define RG_AUDDACLPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDDACLPWRUP_VAUDP32_MASK_SFT (0x1 << 0) +#define RG_AUDDACRPWRUP_VAUDP32_SFT 1 +#define RG_AUDDACRPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDDACRPWRUP_VAUDP32_MASK_SFT (0x1 << 1) +#define RG_AUD_DAC_PWR_UP_VA32_SFT 2 +#define RG_AUD_DAC_PWR_UP_VA32_MASK 0x1 +#define RG_AUD_DAC_PWR_UP_VA32_MASK_SFT (0x1 << 2) +#define RG_AUD_DAC_PWL_UP_VA32_SFT 3 +#define RG_AUD_DAC_PWL_UP_VA32_MASK 0x1 +#define RG_AUD_DAC_PWL_UP_VA32_MASK_SFT (0x1 << 3) +#define RG_AUDHPLPWRUP_VAUDP32_SFT 4 +#define RG_AUDHPLPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPLPWRUP_VAUDP32_MASK_SFT (0x1 << 4) +#define RG_AUDHPRPWRUP_VAUDP32_SFT 5 +#define RG_AUDHPRPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPRPWRUP_VAUDP32_MASK_SFT (0x1 << 5) +#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_SFT 6 +#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_MASK 0x1 +#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 6) +#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_SFT 7 +#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_MASK 0x1 +#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 7) +#define RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT 8 +#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK 0x3 +#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 8) +#define RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT 10 +#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK 0x3 +#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 10) +#define RG_AUDHPLSCDISABLE_VAUDP32_SFT 12 +#define RG_AUDHPLSCDISABLE_VAUDP32_MASK 0x1 +#define RG_AUDHPLSCDISABLE_VAUDP32_MASK_SFT (0x1 << 12) +#define RG_AUDHPRSCDISABLE_VAUDP32_SFT 13 +#define RG_AUDHPRSCDISABLE_VAUDP32_MASK 0x1 +#define RG_AUDHPRSCDISABLE_VAUDP32_MASK_SFT (0x1 << 13) +#define RG_AUDHPLBSCCURRENT_VAUDP32_SFT 14 +#define RG_AUDHPLBSCCURRENT_VAUDP32_MASK 0x1 +#define RG_AUDHPLBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 14) +#define RG_AUDHPRBSCCURRENT_VAUDP32_SFT 15 +#define RG_AUDHPRBSCCURRENT_VAUDP32_MASK 0x1 +#define RG_AUDHPRBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 15) + +/* AUDDEC_ANA_CON1 */ +#define RG_AUDHPLOUTPWRUP_VAUDP32_SFT 0 +#define RG_AUDHPLOUTPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPLOUTPWRUP_VAUDP32_MASK_SFT (0x1 << 0) +#define RG_AUDHPROUTPWRUP_VAUDP32_SFT 1 +#define RG_AUDHPROUTPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPROUTPWRUP_VAUDP32_MASK_SFT (0x1 << 1) +#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_SFT 2 +#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_MASK_SFT (0x1 << 2) +#define RG_AUDHPROUTAUXPWRUP_VAUDP32_SFT 3 +#define RG_AUDHPROUTAUXPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPROUTAUXPWRUP_VAUDP32_MASK_SFT (0x1 << 3) +#define RG_HPLAUXFBRSW_EN_VAUDP32_SFT 4 +#define RG_HPLAUXFBRSW_EN_VAUDP32_MASK 0x1 +#define RG_HPLAUXFBRSW_EN_VAUDP32_MASK_SFT (0x1 << 4) +#define RG_HPRAUXFBRSW_EN_VAUDP32_SFT 5 +#define RG_HPRAUXFBRSW_EN_VAUDP32_MASK 0x1 +#define RG_HPRAUXFBRSW_EN_VAUDP32_MASK_SFT (0x1 << 5) +#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_SFT 6 +#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_MASK 0x1 +#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_MASK_SFT (0x1 << 6) +#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_SFT 7 +#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_MASK 0x1 +#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_MASK_SFT (0x1 << 7) +#define RG_HPLOUTSTGCTRL_VAUDP32_SFT 8 +#define RG_HPLOUTSTGCTRL_VAUDP32_MASK 0x7 +#define RG_HPLOUTSTGCTRL_VAUDP32_MASK_SFT (0x7 << 8) +#define RG_HPROUTSTGCTRL_VAUDP32_SFT 12 +#define RG_HPROUTSTGCTRL_VAUDP32_MASK 0x7 +#define RG_HPROUTSTGCTRL_VAUDP32_MASK_SFT (0x7 << 12) + +/* AUDDEC_ANA_CON2 */ +#define RG_HPLOUTPUTSTBENH_VAUDP32_SFT 0 +#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK 0x7 +#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 0) +#define RG_HPROUTPUTSTBENH_VAUDP32_SFT 4 +#define RG_HPROUTPUTSTBENH_VAUDP32_MASK 0x7 +#define RG_HPROUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 4) +#define RG_AUDHPSTARTUP_VAUDP32_SFT 7 +#define RG_AUDHPSTARTUP_VAUDP32_MASK 0x1 +#define RG_AUDHPSTARTUP_VAUDP32_MASK_SFT (0x1 << 7) +#define RG_AUDREFN_DERES_EN_VAUDP32_SFT 8 +#define RG_AUDREFN_DERES_EN_VAUDP32_MASK 0x1 +#define RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT (0x1 << 8) +#define RG_HPINPUTSTBENH_VAUDP32_SFT 9 +#define RG_HPINPUTSTBENH_VAUDP32_MASK 0x1 +#define RG_HPINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 9) +#define RG_HPINPUTRESET0_VAUDP32_SFT 10 +#define RG_HPINPUTRESET0_VAUDP32_MASK 0x1 +#define RG_HPINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10) +#define RG_HPOUTPUTRESET0_VAUDP32_SFT 11 +#define RG_HPOUTPUTRESET0_VAUDP32_MASK 0x1 +#define RG_HPOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 11) +#define RG_HPPSHORT2VCM_VAUDP32_SFT 12 +#define RG_HPPSHORT2VCM_VAUDP32_MASK 0x7 +#define RG_HPPSHORT2VCM_VAUDP32_MASK_SFT (0x7 << 12) +#define RG_AUDHPTRIM_EN_VAUDP32_SFT 15 +#define RG_AUDHPTRIM_EN_VAUDP32_MASK 0x1 +#define RG_AUDHPTRIM_EN_VAUDP32_MASK_SFT (0x1 << 15) + +/* AUDDEC_ANA_CON3 */ +#define RG_AUDHPLTRIM_VAUDP32_SFT 0 +#define RG_AUDHPLTRIM_VAUDP32_MASK 0x1f +#define RG_AUDHPLTRIM_VAUDP32_MASK_SFT (0x1f << 0) +#define RG_AUDHPLFINETRIM_VAUDP32_SFT 5 +#define RG_AUDHPLFINETRIM_VAUDP32_MASK 0x7 +#define RG_AUDHPLFINETRIM_VAUDP32_MASK_SFT (0x7 << 5) +#define RG_AUDHPRTRIM_VAUDP32_SFT 8 +#define RG_AUDHPRTRIM_VAUDP32_MASK 0x1f +#define RG_AUDHPRTRIM_VAUDP32_MASK_SFT (0x1f << 8) +#define RG_AUDHPRFINETRIM_VAUDP32_SFT 13 +#define RG_AUDHPRFINETRIM_VAUDP32_MASK 0x7 +#define RG_AUDHPRFINETRIM_VAUDP32_MASK_SFT (0x7 << 13) + +/* AUDDEC_ANA_CON4 */ +#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_SFT 0 +#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_MASK 0x7 +#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_MASK_SFT (0x7 << 0) +#define RG_AUDHPLFCOMPRESSEL_VAUDP32_SFT 4 +#define RG_AUDHPLFCOMPRESSEL_VAUDP32_MASK 0x7 +#define RG_AUDHPLFCOMPRESSEL_VAUDP32_MASK_SFT (0x7 << 4) +#define RG_AUDHPHFCOMPRESSEL_VAUDP32_SFT 8 +#define RG_AUDHPHFCOMPRESSEL_VAUDP32_MASK 0x7 +#define RG_AUDHPHFCOMPRESSEL_VAUDP32_MASK_SFT (0x7 << 8) +#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_SFT 12 +#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK 0x3 +#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK_SFT (0x3 << 12) +#define RG_AUDHPCOMP_EN_VAUDP32_SFT 15 +#define RG_AUDHPCOMP_EN_VAUDP32_MASK 0x1 +#define RG_AUDHPCOMP_EN_VAUDP32_MASK_SFT (0x1 << 15) + +/* AUDDEC_ANA_CON5 */ +#define RG_AUDHPDECMGAINADJ_VAUDP32_SFT 0 +#define RG_AUDHPDECMGAINADJ_VAUDP32_MASK 0x7 +#define RG_AUDHPDECMGAINADJ_VAUDP32_MASK_SFT (0x7 << 0) +#define RG_AUDHPDEDMGAINADJ_VAUDP32_SFT 4 +#define RG_AUDHPDEDMGAINADJ_VAUDP32_MASK 0x7 +#define RG_AUDHPDEDMGAINADJ_VAUDP32_MASK_SFT (0x7 << 4) + +/* AUDDEC_ANA_CON6 */ +#define RG_AUDHSPWRUP_VAUDP32_SFT 0 +#define RG_AUDHSPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHSPWRUP_VAUDP32_MASK_SFT (0x1 << 0) +#define RG_AUDHSPWRUP_IBIAS_VAUDP32_SFT 1 +#define RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK 0x1 +#define RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 1) +#define RG_AUDHSMUXINPUTSEL_VAUDP32_SFT 2 +#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK 0x3 +#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 2) +#define RG_AUDHSSCDISABLE_VAUDP32_SFT 4 +#define RG_AUDHSSCDISABLE_VAUDP32_MASK 0x1 +#define RG_AUDHSSCDISABLE_VAUDP32_MASK_SFT (0x1 << 4) +#define RG_AUDHSBSCCURRENT_VAUDP32_SFT 5 +#define RG_AUDHSBSCCURRENT_VAUDP32_MASK 0x1 +#define RG_AUDHSBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 5) +#define RG_AUDHSSTARTUP_VAUDP32_SFT 6 +#define RG_AUDHSSTARTUP_VAUDP32_MASK 0x1 +#define RG_AUDHSSTARTUP_VAUDP32_MASK_SFT (0x1 << 6) +#define RG_HSOUTPUTSTBENH_VAUDP32_SFT 7 +#define RG_HSOUTPUTSTBENH_VAUDP32_MASK 0x1 +#define RG_HSOUTPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 7) +#define RG_HSINPUTSTBENH_VAUDP32_SFT 8 +#define RG_HSINPUTSTBENH_VAUDP32_MASK 0x1 +#define RG_HSINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 8) +#define RG_HSINPUTRESET0_VAUDP32_SFT 9 +#define RG_HSINPUTRESET0_VAUDP32_MASK 0x1 +#define RG_HSINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 9) +#define RG_HSOUTPUTRESET0_VAUDP32_SFT 10 +#define RG_HSOUTPUTRESET0_VAUDP32_MASK 0x1 +#define RG_HSOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10) +#define RG_HSOUT_SHORTVCM_VAUDP32_SFT 11 +#define RG_HSOUT_SHORTVCM_VAUDP32_MASK 0x1 +#define RG_HSOUT_SHORTVCM_VAUDP32_MASK_SFT (0x1 << 11) + +/* AUDDEC_ANA_CON7 */ +#define RG_AUDLOLPWRUP_VAUDP32_SFT 0 +#define RG_AUDLOLPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDLOLPWRUP_VAUDP32_MASK_SFT (0x1 << 0) +#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_SFT 1 +#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK 0x1 +#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 1) +#define RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT 2 +#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK 0x3 +#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 2) +#define RG_AUDLOLSCDISABLE_VAUDP32_SFT 4 +#define RG_AUDLOLSCDISABLE_VAUDP32_MASK 0x1 +#define RG_AUDLOLSCDISABLE_VAUDP32_MASK_SFT (0x1 << 4) +#define RG_AUDLOLBSCCURRENT_VAUDP32_SFT 5 +#define RG_AUDLOLBSCCURRENT_VAUDP32_MASK 0x1 +#define RG_AUDLOLBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 5) +#define RG_AUDLOSTARTUP_VAUDP32_SFT 6 +#define RG_AUDLOSTARTUP_VAUDP32_MASK 0x1 +#define RG_AUDLOSTARTUP_VAUDP32_MASK_SFT (0x1 << 6) +#define RG_LOINPUTSTBENH_VAUDP32_SFT 7 +#define RG_LOINPUTSTBENH_VAUDP32_MASK 0x1 +#define RG_LOINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 7) +#define RG_LOOUTPUTSTBENH_VAUDP32_SFT 8 +#define RG_LOOUTPUTSTBENH_VAUDP32_MASK 0x1 +#define RG_LOOUTPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 8) +#define RG_LOINPUTRESET0_VAUDP32_SFT 9 +#define RG_LOINPUTRESET0_VAUDP32_MASK 0x1 +#define RG_LOINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 9) +#define RG_LOOUTPUTRESET0_VAUDP32_SFT 10 +#define RG_LOOUTPUTRESET0_VAUDP32_MASK 0x1 +#define RG_LOOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10) +#define RG_LOOUT_SHORTVCM_VAUDP32_SFT 11 +#define RG_LOOUT_SHORTVCM_VAUDP32_MASK 0x1 +#define RG_LOOUT_SHORTVCM_VAUDP32_MASK_SFT (0x1 << 11) +#define RG_AUDDACTPWRUP_VAUDP32_SFT 12 +#define RG_AUDDACTPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDDACTPWRUP_VAUDP32_MASK_SFT (0x1 << 12) +#define RG_AUD_DAC_PWT_UP_VA32_SFT 13 +#define RG_AUD_DAC_PWT_UP_VA32_MASK 0x1 +#define RG_AUD_DAC_PWT_UP_VA32_MASK_SFT (0x1 << 13) + +/* AUDDEC_ANA_CON8 */ +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_SFT 0 +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_MASK 0xf +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_MASK_SFT (0xf << 0) +#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_SFT 4 +#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_MASK 0x3 +#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_MASK_SFT (0x3 << 4) +#define RG_AUDTRIMBUF_EN_VAUDP32_SFT 6 +#define RG_AUDTRIMBUF_EN_VAUDP32_MASK 0x1 +#define RG_AUDTRIMBUF_EN_VAUDP32_MASK_SFT (0x1 << 6) +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_SFT 8 +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_MASK 0x3 +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_MASK_SFT (0x3 << 8) +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_SFT 10 +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_MASK 0x3 +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_MASK_SFT (0x3 << 10) +#define RG_AUDHPSPKDET_EN_VAUDP32_SFT 12 +#define RG_AUDHPSPKDET_EN_VAUDP32_MASK 0x1 +#define RG_AUDHPSPKDET_EN_VAUDP32_MASK_SFT (0x1 << 12) + +/* AUDDEC_ANA_CON9 */ +#define RG_ABIDEC_RSVD0_VA32_SFT 0 +#define RG_ABIDEC_RSVD0_VA32_MASK 0xff +#define RG_ABIDEC_RSVD0_VA32_MASK_SFT (0xff << 0) +#define RG_ABIDEC_RSVD0_VAUDP32_SFT 8 +#define RG_ABIDEC_RSVD0_VAUDP32_MASK 0xff +#define RG_ABIDEC_RSVD0_VAUDP32_MASK_SFT (0xff << 8) + +/* AUDDEC_ANA_CON10 */ +#define RG_ABIDEC_RSVD1_VAUDP32_SFT 0 +#define RG_ABIDEC_RSVD1_VAUDP32_MASK 0xff +#define RG_ABIDEC_RSVD1_VAUDP32_MASK_SFT (0xff << 0) +#define RG_ABIDEC_RSVD2_VAUDP32_SFT 8 +#define RG_ABIDEC_RSVD2_VAUDP32_MASK 0xff +#define RG_ABIDEC_RSVD2_VAUDP32_MASK_SFT (0xff << 8) + +/* AUDDEC_ANA_CON11 */ +#define RG_AUDZCDMUXSEL_VAUDP32_SFT 0 +#define RG_AUDZCDMUXSEL_VAUDP32_MASK 0x7 +#define RG_AUDZCDMUXSEL_VAUDP32_MASK_SFT (0x7 << 0) +#define RG_AUDZCDCLKSEL_VAUDP32_SFT 3 +#define RG_AUDZCDCLKSEL_VAUDP32_MASK 0x1 +#define RG_AUDZCDCLKSEL_VAUDP32_MASK_SFT (0x1 << 3) +#define RG_AUDBIASADJ_0_VAUDP32_SFT 7 +#define RG_AUDBIASADJ_0_VAUDP32_MASK 0x1ff +#define RG_AUDBIASADJ_0_VAUDP32_MASK_SFT (0x1ff << 7) + +/* AUDDEC_ANA_CON12 */ +#define RG_AUDBIASADJ_1_VAUDP32_SFT 0 +#define RG_AUDBIASADJ_1_VAUDP32_MASK 0xff +#define RG_AUDBIASADJ_1_VAUDP32_MASK_SFT (0xff << 0) +#define RG_AUDIBIASPWRDN_VAUDP32_SFT 8 +#define RG_AUDIBIASPWRDN_VAUDP32_MASK 0x1 +#define RG_AUDIBIASPWRDN_VAUDP32_MASK_SFT (0x1 << 8) + +/* AUDDEC_ANA_CON13 */ +#define RG_RSTB_DECODER_VA32_SFT 0 +#define RG_RSTB_DECODER_VA32_MASK 0x1 +#define RG_RSTB_DECODER_VA32_MASK_SFT (0x1 << 0) +#define RG_SEL_DECODER_96K_VA32_SFT 1 +#define RG_SEL_DECODER_96K_VA32_MASK 0x1 +#define RG_SEL_DECODER_96K_VA32_MASK_SFT (0x1 << 1) +#define RG_SEL_DELAY_VCORE_SFT 2 +#define RG_SEL_DELAY_VCORE_MASK 0x1 +#define RG_SEL_DELAY_VCORE_MASK_SFT (0x1 << 2) +#define RG_AUDGLB_PWRDN_VA32_SFT 4 +#define RG_AUDGLB_PWRDN_VA32_MASK 0x1 +#define RG_AUDGLB_PWRDN_VA32_MASK_SFT (0x1 << 4) +#define RG_AUDGLB_LP_VOW_EN_VA32_SFT 5 +#define RG_AUDGLB_LP_VOW_EN_VA32_MASK 0x1 +#define RG_AUDGLB_LP_VOW_EN_VA32_MASK_SFT (0x1 << 5) +#define RG_AUDGLB_LP2_VOW_EN_VA32_SFT 6 +#define RG_AUDGLB_LP2_VOW_EN_VA32_MASK 0x1 +#define RG_AUDGLB_LP2_VOW_EN_VA32_MASK_SFT (0x1 << 6) + +/* AUDDEC_ANA_CON14 */ +#define RG_LCLDO_DEC_EN_VA32_SFT 0 +#define RG_LCLDO_DEC_EN_VA32_MASK 0x1 +#define RG_LCLDO_DEC_EN_VA32_MASK_SFT (0x1 << 0) +#define RG_LCLDO_DEC_PDDIS_EN_VA18_SFT 1 +#define RG_LCLDO_DEC_PDDIS_EN_VA18_MASK 0x1 +#define RG_LCLDO_DEC_PDDIS_EN_VA18_MASK_SFT (0x1 << 1) +#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_SFT 2 +#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_MASK 0x1 +#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 2) +#define RG_NVREG_EN_VAUDP32_SFT 4 +#define RG_NVREG_EN_VAUDP32_MASK 0x1 +#define RG_NVREG_EN_VAUDP32_MASK_SFT (0x1 << 4) +#define RG_NVREG_PULL0V_VAUDP32_SFT 5 +#define RG_NVREG_PULL0V_VAUDP32_MASK 0x1 +#define RG_NVREG_PULL0V_VAUDP32_MASK_SFT (0x1 << 5) +#define RG_AUDPMU_RSVD_VA18_SFT 8 +#define RG_AUDPMU_RSVD_VA18_MASK 0xff +#define RG_AUDPMU_RSVD_VA18_MASK_SFT (0xff << 8) + +/* MT6359_ZCD_CON0 */ +#define RG_AUDZCDENABLE_SFT 0 +#define RG_AUDZCDENABLE_MASK 0x1 +#define RG_AUDZCDENABLE_MASK_SFT (0x1 << 0) +#define RG_AUDZCDGAINSTEPTIME_SFT 1 +#define RG_AUDZCDGAINSTEPTIME_MASK 0x7 +#define RG_AUDZCDGAINSTEPTIME_MASK_SFT (0x7 << 1) +#define RG_AUDZCDGAINSTEPSIZE_SFT 4 +#define RG_AUDZCDGAINSTEPSIZE_MASK 0x3 +#define RG_AUDZCDGAINSTEPSIZE_MASK_SFT (0x3 << 4) +#define RG_AUDZCDTIMEOUTMODESEL_SFT 6 +#define RG_AUDZCDTIMEOUTMODESEL_MASK 0x1 +#define RG_AUDZCDTIMEOUTMODESEL_MASK_SFT (0x1 << 6) + +/* MT6359_ZCD_CON1 */ +#define RG_AUDLOLGAIN_SFT 0 +#define RG_AUDLOLGAIN_MASK 0x1f +#define RG_AUDLOLGAIN_MASK_SFT (0x1f << 0) +#define RG_AUDLORGAIN_SFT 7 +#define RG_AUDLORGAIN_MASK 0x1f +#define RG_AUDLORGAIN_MASK_SFT (0x1f << 7) + +/* MT6359_ZCD_CON2 */ +#define RG_AUDHPLGAIN_SFT 0 +#define RG_AUDHPLGAIN_MASK 0x1f +#define RG_AUDHPLGAIN_MASK_SFT (0x1f << 0) +#define RG_AUDHPRGAIN_SFT 7 +#define RG_AUDHPRGAIN_MASK 0x1f +#define RG_AUDHPRGAIN_MASK_SFT (0x1f << 7) + +/* MT6359_ZCD_CON3 */ +#define RG_AUDHSGAIN_SFT 0 +#define RG_AUDHSGAIN_MASK 0x1f +#define RG_AUDHSGAIN_MASK_SFT (0x1f << 0) + +/* MT6359_ZCD_CON4 */ +#define RG_AUDIVLGAIN_SFT 0 +#define RG_AUDIVLGAIN_MASK 0x7 +#define RG_AUDIVLGAIN_MASK_SFT (0x7 << 0) +#define RG_AUDIVRGAIN_SFT 8 +#define RG_AUDIVRGAIN_MASK 0x7 +#define RG_AUDIVRGAIN_MASK_SFT (0x7 << 8) + +/* MT6359_ZCD_CON5 */ +#define RG_AUDINTGAIN1_SFT 0 +#define RG_AUDINTGAIN1_MASK 0x3f +#define RG_AUDINTGAIN1_MASK_SFT (0x3f << 0) +#define RG_AUDINTGAIN2_SFT 8 +#define RG_AUDINTGAIN2_MASK 0x3f +#define RG_AUDINTGAIN2_MASK_SFT (0x3f << 8) + +/* audio register */ +#define MT6359_GPIO_DIR0 0x88 +#define MT6359_GPIO_DIR0_SET 0x8a +#define MT6359_GPIO_DIR0_CLR 0x8c +#define MT6359_GPIO_DIR1 0x8e +#define MT6359_GPIO_DIR1_SET 0x90 +#define MT6359_GPIO_DIR1_CLR 0x92 + +#define MT6359_DCXO_CW11 0x7a6 +#define MT6359_DCXO_CW12 0x7a8 + +#define MT6359_GPIO_MODE0 0xcc +#define MT6359_GPIO_MODE0_SET 0xce +#define MT6359_GPIO_MODE0_CLR 0xd0 +#define MT6359_GPIO_MODE1 0xd2 +#define MT6359_GPIO_MODE1_SET 0xd4 +#define MT6359_GPIO_MODE1_CLR 0xd6 +#define MT6359_GPIO_MODE2 0xd8 +#define MT6359_GPIO_MODE2_SET 0xda +#define MT6359_GPIO_MODE2_CLR 0xdc +#define MT6359_GPIO_MODE3 0xde +#define MT6359_GPIO_MODE3_SET 0xe0 +#define MT6359_GPIO_MODE3_CLR 0xe2 +#define MT6359_GPIO_MODE4 0xe4 +#define MT6359_GPIO_MODE4_SET 0xe6 +#define MT6359_GPIO_MODE4_CLR 0xe8 + +#define MT6359_AUD_TOP_ID 0x2300 +#define MT6359_AUD_TOP_REV0 0x2302 +#define MT6359_AUD_TOP_DBI 0x2304 +#define MT6359_AUD_TOP_DXI 0x2306 +#define MT6359_AUD_TOP_CKPDN_TPM0 0x2308 +#define MT6359_AUD_TOP_CKPDN_TPM1 0x230a +#define MT6359_AUD_TOP_CKPDN_CON0 0x230c +#define MT6359_AUD_TOP_CKPDN_CON0_SET 0x230e +#define MT6359_AUD_TOP_CKPDN_CON0_CLR 0x2310 +#define MT6359_AUD_TOP_CKSEL_CON0 0x2312 +#define MT6359_AUD_TOP_CKSEL_CON0_SET 0x2314 +#define MT6359_AUD_TOP_CKSEL_CON0_CLR 0x2316 +#define MT6359_AUD_TOP_CKTST_CON0 0x2318 +#define MT6359_AUD_TOP_CLK_HWEN_CON0 0x231a +#define MT6359_AUD_TOP_CLK_HWEN_CON0_SET 0x231c +#define MT6359_AUD_TOP_CLK_HWEN_CON0_CLR 0x231e +#define MT6359_AUD_TOP_RST_CON0 0x2320 +#define MT6359_AUD_TOP_RST_CON0_SET 0x2322 +#define MT6359_AUD_TOP_RST_CON0_CLR 0x2324 +#define MT6359_AUD_TOP_RST_BANK_CON0 0x2326 +#define MT6359_AUD_TOP_INT_CON0 0x2328 +#define MT6359_AUD_TOP_INT_CON0_SET 0x232a +#define MT6359_AUD_TOP_INT_CON0_CLR 0x232c +#define MT6359_AUD_TOP_INT_MASK_CON0 0x232e +#define MT6359_AUD_TOP_INT_MASK_CON0_SET 0x2330 +#define MT6359_AUD_TOP_INT_MASK_CON0_CLR 0x2332 +#define MT6359_AUD_TOP_INT_STATUS0 0x2334 +#define MT6359_AUD_TOP_INT_RAW_STATUS0 0x2336 +#define MT6359_AUD_TOP_INT_MISC_CON0 0x2338 +#define MT6359_AUD_TOP_MON_CON0 0x233a +#define MT6359_AUDIO_DIG_DSN_ID 0x2380 +#define MT6359_AUDIO_DIG_DSN_REV0 0x2382 +#define MT6359_AUDIO_DIG_DSN_DBI 0x2384 +#define MT6359_AUDIO_DIG_DSN_DXI 0x2386 +#define MT6359_AFE_UL_DL_CON0 0x2388 +#define MT6359_AFE_DL_SRC2_CON0_L 0x238a +#define MT6359_AFE_UL_SRC_CON0_H 0x238c +#define MT6359_AFE_UL_SRC_CON0_L 0x238e +#define MT6359_AFE_ADDA6_L_SRC_CON0_H 0x2390 +#define MT6359_AFE_ADDA6_UL_SRC_CON0_L 0x2392 +#define MT6359_AFE_TOP_CON0 0x2394 +#define MT6359_AUDIO_TOP_CON0 0x2396 +#define MT6359_AFE_MON_DEBUG0 0x2398 +#define MT6359_AFUNC_AUD_CON0 0x239a +#define MT6359_AFUNC_AUD_CON1 0x239c +#define MT6359_AFUNC_AUD_CON2 0x239e +#define MT6359_AFUNC_AUD_CON3 0x23a0 +#define MT6359_AFUNC_AUD_CON4 0x23a2 +#define MT6359_AFUNC_AUD_CON5 0x23a4 +#define MT6359_AFUNC_AUD_CON6 0x23a6 +#define MT6359_AFUNC_AUD_CON7 0x23a8 +#define MT6359_AFUNC_AUD_CON8 0x23aa +#define MT6359_AFUNC_AUD_CON9 0x23ac +#define MT6359_AFUNC_AUD_CON10 0x23ae +#define MT6359_AFUNC_AUD_CON11 0x23b0 +#define MT6359_AFUNC_AUD_CON12 0x23b2 +#define MT6359_AFUNC_AUD_MON0 0x23b4 +#define MT6359_AFUNC_AUD_MON1 0x23b6 +#define MT6359_AUDRC_TUNE_MON0 0x23b8 +#define MT6359_AFE_ADDA_MTKAIF_FIFO_CFG0 0x23ba +#define MT6359_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 0x23bc +#define MT6359_AFE_ADDA_MTKAIF_MON0 0x23be +#define MT6359_AFE_ADDA_MTKAIF_MON1 0x23c0 +#define MT6359_AFE_ADDA_MTKAIF_MON2 0x23c2 +#define MT6359_AFE_ADDA6_MTKAIF_MON3 0x23c4 +#define MT6359_AFE_ADDA_MTKAIF_MON4 0x23c6 +#define MT6359_AFE_ADDA_MTKAIF_MON5 0x23c8 +#define MT6359_AFE_ADDA_MTKAIF_CFG0 0x23ca +#define MT6359_AFE_ADDA_MTKAIF_RX_CFG0 0x23cc +#define MT6359_AFE_ADDA_MTKAIF_RX_CFG1 0x23ce +#define MT6359_AFE_ADDA_MTKAIF_RX_CFG2 0x23d0 +#define MT6359_AFE_ADDA_MTKAIF_RX_CFG3 0x23d2 +#define MT6359_AFE_ADDA_MTKAIF_SYNCWORD_CFG0 0x23d4 +#define MT6359_AFE_ADDA_MTKAIF_SYNCWORD_CFG1 0x23d6 +#define MT6359_AFE_SGEN_CFG0 0x23d8 +#define MT6359_AFE_SGEN_CFG1 0x23da +#define MT6359_AFE_ADC_ASYNC_FIFO_CFG 0x23dc +#define MT6359_AFE_ADC_ASYNC_FIFO_CFG1 0x23de +#define MT6359_AFE_DCCLK_CFG0 0x23e0 +#define MT6359_AFE_DCCLK_CFG1 0x23e2 +#define MT6359_AUDIO_DIG_CFG 0x23e4 +#define MT6359_AUDIO_DIG_CFG1 0x23e6 +#define MT6359_AFE_AUD_PAD_TOP 0x23e8 +#define MT6359_AFE_AUD_PAD_TOP_MON 0x23ea +#define MT6359_AFE_AUD_PAD_TOP_MON1 0x23ec +#define MT6359_AFE_AUD_PAD_TOP_MON2 0x23ee +#define MT6359_AFE_DL_NLE_CFG 0x23f0 +#define MT6359_AFE_DL_NLE_MON 0x23f2 +#define MT6359_AFE_CG_EN_MON 0x23f4 +#define MT6359_AFE_MIC_ARRAY_CFG 0x23f6 +#define MT6359_AFE_CHOP_CFG0 0x23f8 +#define MT6359_AFE_MTKAIF_MUX_CFG 0x23fa +#define MT6359_AUDIO_DIG_2ND_DSN_ID 0x2400 +#define MT6359_AUDIO_DIG_2ND_DSN_REV0 0x2402 +#define MT6359_AUDIO_DIG_2ND_DSN_DBI 0x2404 +#define MT6359_AUDIO_DIG_2ND_DSN_DXI 0x2406 +#define MT6359_AFE_PMIC_NEWIF_CFG3 0x2408 +#define MT6359_AUDIO_DIG_3RD_DSN_ID 0x2480 +#define MT6359_AUDIO_DIG_3RD_DSN_REV0 0x2482 +#define MT6359_AUDIO_DIG_3RD_DSN_DBI 0x2484 +#define MT6359_AUDIO_DIG_3RD_DSN_DXI 0x2486 +#define MT6359_AFE_NCP_CFG0 0x24de +#define MT6359_AFE_NCP_CFG1 0x24e0 +#define MT6359_AFE_NCP_CFG2 0x24e2 +#define MT6359_AUDENC_DSN_ID 0x2500 +#define MT6359_AUDENC_DSN_REV0 0x2502 +#define MT6359_AUDENC_DSN_DBI 0x2504 +#define MT6359_AUDENC_DSN_FPI 0x2506 +#define MT6359_AUDENC_ANA_CON0 0x2508 +#define MT6359_AUDENC_ANA_CON1 0x250a +#define MT6359_AUDENC_ANA_CON2 0x250c +#define MT6359_AUDENC_ANA_CON3 0x250e +#define MT6359_AUDENC_ANA_CON4 0x2510 +#define MT6359_AUDENC_ANA_CON5 0x2512 +#define MT6359_AUDENC_ANA_CON6 0x2514 +#define MT6359_AUDENC_ANA_CON7 0x2516 +#define MT6359_AUDENC_ANA_CON8 0x2518 +#define MT6359_AUDENC_ANA_CON9 0x251a +#define MT6359_AUDENC_ANA_CON10 0x251c +#define MT6359_AUDENC_ANA_CON11 0x251e +#define MT6359_AUDENC_ANA_CON12 0x2520 +#define MT6359_AUDENC_ANA_CON13 0x2522 +#define MT6359_AUDENC_ANA_CON14 0x2524 +#define MT6359_AUDENC_ANA_CON15 0x2526 +#define MT6359_AUDENC_ANA_CON16 0x2528 +#define MT6359_AUDENC_ANA_CON17 0x252a +#define MT6359_AUDENC_ANA_CON18 0x252c +#define MT6359_AUDENC_ANA_CON19 0x252e +#define MT6359_AUDENC_ANA_CON20 0x2530 +#define MT6359_AUDENC_ANA_CON21 0x2532 +#define MT6359_AUDENC_ANA_CON22 0x2534 +#define MT6359_AUDENC_ANA_CON23 0x2536 +#define MT6359_AUDDEC_DSN_ID 0x2580 +#define MT6359_AUDDEC_DSN_REV0 0x2582 +#define MT6359_AUDDEC_DSN_DBI 0x2584 +#define MT6359_AUDDEC_DSN_FPI 0x2586 +#define MT6359_AUDDEC_ANA_CON0 0x2588 +#define MT6359_AUDDEC_ANA_CON1 0x258a +#define MT6359_AUDDEC_ANA_CON2 0x258c +#define MT6359_AUDDEC_ANA_CON3 0x258e +#define MT6359_AUDDEC_ANA_CON4 0x2590 +#define MT6359_AUDDEC_ANA_CON5 0x2592 +#define MT6359_AUDDEC_ANA_CON6 0x2594 +#define MT6359_AUDDEC_ANA_CON7 0x2596 +#define MT6359_AUDDEC_ANA_CON8 0x2598 +#define MT6359_AUDDEC_ANA_CON9 0x259a +#define MT6359_AUDDEC_ANA_CON10 0x259c +#define MT6359_AUDDEC_ANA_CON11 0x259e +#define MT6359_AUDDEC_ANA_CON12 0x25a0 +#define MT6359_AUDDEC_ANA_CON13 0x25a2 +#define MT6359_AUDDEC_ANA_CON14 0x25a4 +#define MT6359_AUDZCD_DSN_ID 0x2600 +#define MT6359_AUDZCD_DSN_REV0 0x2602 +#define MT6359_AUDZCD_DSN_DBI 0x2604 +#define MT6359_AUDZCD_DSN_FPI 0x2606 +#define MT6359_ZCD_CON0 0x2608 +#define MT6359_ZCD_CON1 0x260a +#define MT6359_ZCD_CON2 0x260c +#define MT6359_ZCD_CON3 0x260e +#define MT6359_ZCD_CON4 0x2610 +#define MT6359_ZCD_CON5 0x2612 +#define MT6359_ACCDET_DSN_DIG_ID 0x2680 +#define MT6359_ACCDET_DSN_DIG_REV0 0x2682 +#define MT6359_ACCDET_DSN_DBI 0x2684 +#define MT6359_ACCDET_DSN_FPI 0x2686 +#define MT6359_ACCDET_CON0 0x2688 +#define MT6359_ACCDET_CON1 0x268a +#define MT6359_ACCDET_CON2 0x268c +#define MT6359_ACCDET_CON3 0x268e +#define MT6359_ACCDET_CON4 0x2690 +#define MT6359_ACCDET_CON5 0x2692 +#define MT6359_ACCDET_CON6 0x2694 +#define MT6359_ACCDET_CON7 0x2696 +#define MT6359_ACCDET_CON8 0x2698 +#define MT6359_ACCDET_CON9 0x269a +#define MT6359_ACCDET_CON10 0x269c +#define MT6359_ACCDET_CON11 0x269e +#define MT6359_ACCDET_CON12 0x26a0 +#define MT6359_ACCDET_CON13 0x26a2 +#define MT6359_ACCDET_CON14 0x26a4 +#define MT6359_ACCDET_CON15 0x26a6 +#define MT6359_ACCDET_CON16 0x26a8 +#define MT6359_ACCDET_CON17 0x26aa +#define MT6359_ACCDET_CON18 0x26ac +#define MT6359_ACCDET_CON19 0x26ae +#define MT6359_ACCDET_CON20 0x26b0 +#define MT6359_ACCDET_CON21 0x26b2 +#define MT6359_ACCDET_CON22 0x26b4 +#define MT6359_ACCDET_CON23 0x26b6 +#define MT6359_ACCDET_CON24 0x26b8 +#define MT6359_ACCDET_CON25 0x26ba +#define MT6359_ACCDET_CON26 0x26bc +#define MT6359_ACCDET_CON27 0x26be +#define MT6359_ACCDET_CON28 0x26c0 +#define MT6359_ACCDET_CON29 0x26c2 +#define MT6359_ACCDET_CON30 0x26c4 +#define MT6359_ACCDET_CON31 0x26c6 +#define MT6359_ACCDET_CON32 0x26c8 +#define MT6359_ACCDET_CON33 0x26ca +#define MT6359_ACCDET_CON34 0x26cc +#define MT6359_ACCDET_CON35 0x26ce +#define MT6359_ACCDET_CON36 0x26d0 +#define MT6359_ACCDET_CON37 0x26d2 +#define MT6359_ACCDET_CON38 0x26d4 +#define MT6359_ACCDET_CON39 0x26d6 +#define MT6359_ACCDET_CON40 0x26d8 +#define MT6359_MAX_REGISTER MT6359_ZCD_CON5 + +/* dl bias */ +#define DRBIAS_MASK 0x7 +#define DRBIAS_HP_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 0) +#define DRBIAS_HP_MASK_SFT (DRBIAS_MASK << DRBIAS_HP_SFT) +#define DRBIAS_HS_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 3) +#define DRBIAS_HS_MASK_SFT (DRBIAS_MASK << DRBIAS_HS_SFT) +#define DRBIAS_LO_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 6) +#define DRBIAS_LO_MASK_SFT (DRBIAS_MASK << DRBIAS_LO_SFT) +#define IBIAS_MASK 0x3 +#define IBIAS_HP_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 0) +#define IBIAS_HP_MASK_SFT (IBIAS_MASK << IBIAS_HP_SFT) +#define IBIAS_HS_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 2) +#define IBIAS_HS_MASK_SFT (IBIAS_MASK << IBIAS_HS_SFT) +#define IBIAS_LO_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 4) +#define IBIAS_LO_MASK_SFT (IBIAS_MASK << IBIAS_LO_SFT) +#define IBIAS_ZCD_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 6) +#define IBIAS_ZCD_MASK_SFT (IBIAS_MASK << IBIAS_ZCD_SFT) + +/* dl gain */ +#define DL_GAIN_N_10DB_REG (DL_GAIN_N_10DB << 7 | DL_GAIN_N_10DB) +#define DL_GAIN_N_22DB_REG (DL_GAIN_N_22DB << 7 | DL_GAIN_N_22DB) +#define DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB) +#define DL_GAIN_REG_MASK 0x0f9f + +/* mic type mux */ +#define MT_SOC_ENUM_EXT_ID(xname, xenum, xhandler_get, xhandler_put, id) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .device = id,\ + .info = snd_soc_info_enum_double, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(xenum) } + +enum { + MT6359_MTKAIF_PROTOCOL_1 = 0, + MT6359_MTKAIF_PROTOCOL_2, + MT6359_MTKAIF_PROTOCOL_2_CLK_P2, +}; + +enum { + MT6359_AIF_1 = 0, /* dl: hp, rcv, hp+lo */ + MT6359_AIF_2, /* dl: lo only */ + MT6359_AIF_NUM, +}; + +enum { + AUDIO_ANALOG_VOLUME_HSOUTL, + AUDIO_ANALOG_VOLUME_HSOUTR, + AUDIO_ANALOG_VOLUME_HPOUTL, + AUDIO_ANALOG_VOLUME_HPOUTR, + AUDIO_ANALOG_VOLUME_LINEOUTL, + AUDIO_ANALOG_VOLUME_LINEOUTR, + AUDIO_ANALOG_VOLUME_MICAMP1, + AUDIO_ANALOG_VOLUME_MICAMP2, + AUDIO_ANALOG_VOLUME_MICAMP3, + AUDIO_ANALOG_VOLUME_TYPE_MAX +}; + +enum { + MUX_MIC_TYPE_0, /* ain0, micbias 0 */ + MUX_MIC_TYPE_1, /* ain1, micbias 1 */ + MUX_MIC_TYPE_2, /* ain2/3, micbias 2 */ + MUX_PGA_L, + MUX_PGA_R, + MUX_PGA_3, + MUX_HP, + MUX_NUM, +}; + +enum { + DEVICE_HP, + DEVICE_LO, + DEVICE_RCV, + DEVICE_MIC1, + DEVICE_MIC2, + DEVICE_NUM +}; + +enum { + HP_GAIN_CTL_ZCD = 0, + HP_GAIN_CTL_NLE, + HP_GAIN_CTL_NUM, +}; + +enum { + HP_MUX_OPEN = 0, + HP_MUX_HPSPK, + HP_MUX_HP, + HP_MUX_TEST_MODE, + HP_MUX_HP_IMPEDANCE, + HP_MUX_MASK = 0x7, +}; + +enum { + RCV_MUX_OPEN = 0, + RCV_MUX_MUTE, + RCV_MUX_VOICE_PLAYBACK, + RCV_MUX_TEST_MODE, + RCV_MUX_MASK = 0x3, +}; + +enum { + LO_MUX_OPEN = 0, + LO_MUX_L_DAC, + LO_MUX_3RD_DAC, + LO_MUX_TEST_MODE, + LO_MUX_MASK = 0x3, +}; + +/* Supply widget subseq */ +enum { + /* common */ + SUPPLY_SEQ_CLK_BUF, + SUPPLY_SEQ_AUD_GLB, + SUPPLY_SEQ_HP_PULL_DOWN, + SUPPLY_SEQ_CLKSQ, + SUPPLY_SEQ_ADC_CLKGEN, + SUPPLY_SEQ_TOP_CK, + SUPPLY_SEQ_TOP_CK_LAST, + SUPPLY_SEQ_DCC_CLK, + SUPPLY_SEQ_MIC_BIAS, + SUPPLY_SEQ_DMIC, + SUPPLY_SEQ_AUD_TOP, + SUPPLY_SEQ_AUD_TOP_LAST, + SUPPLY_SEQ_DL_SDM_FIFO_CLK, + SUPPLY_SEQ_DL_SDM, + SUPPLY_SEQ_DL_NCP, + SUPPLY_SEQ_AFE, + /* playback */ + SUPPLY_SEQ_DL_SRC, + SUPPLY_SEQ_DL_ESD_RESIST, + SUPPLY_SEQ_HP_DAMPING_OFF_RESET_CMFB, + SUPPLY_SEQ_HP_MUTE, + SUPPLY_SEQ_DL_LDO_REMOTE_SENSE, + SUPPLY_SEQ_DL_LDO, + SUPPLY_SEQ_DL_NV, + SUPPLY_SEQ_HP_ANA_TRIM, + SUPPLY_SEQ_DL_IBIST, + /* capture */ + SUPPLY_SEQ_UL_PGA, + SUPPLY_SEQ_UL_ADC, + SUPPLY_SEQ_UL_MTKAIF, + SUPPLY_SEQ_UL_SRC_DMIC, + SUPPLY_SEQ_UL_SRC, +}; + +enum { + CH_L = 0, + CH_R, + NUM_CH, +}; + +enum { + DRBIAS_4UA = 0, + DRBIAS_5UA, + DRBIAS_6UA, + DRBIAS_7UA, + DRBIAS_8UA, + DRBIAS_9UA, + DRBIAS_10UA, + DRBIAS_11UA, +}; + +enum { + IBIAS_4UA = 0, + IBIAS_5UA, + IBIAS_6UA, + IBIAS_7UA, +}; + +enum { + IBIAS_ZCD_3UA = 0, + IBIAS_ZCD_4UA, + IBIAS_ZCD_5UA, + IBIAS_ZCD_6UA, +}; + +enum { + MIC_BIAS_1P7 = 0, + MIC_BIAS_1P8, + MIC_BIAS_1P9, + MIC_BIAS_2P0, + MIC_BIAS_2P1, + MIC_BIAS_2P5, + MIC_BIAS_2P6, + MIC_BIAS_2P7, +}; + +/* dl pga gain */ +enum { + DL_GAIN_8DB = 0, + DL_GAIN_0DB = 8, + DL_GAIN_N_1DB = 9, + DL_GAIN_N_10DB = 18, + DL_GAIN_N_22DB = 30, + DL_GAIN_N_40DB = 0x1f, +}; + +/* Mic Type MUX */ +enum { + MIC_TYPE_MUX_IDLE = 0, + MIC_TYPE_MUX_ACC, + MIC_TYPE_MUX_DMIC, + MIC_TYPE_MUX_DCC, + MIC_TYPE_MUX_DCC_ECM_DIFF, + MIC_TYPE_MUX_DCC_ECM_SINGLE, +}; + +/* UL SRC MUX */ +enum { + UL_SRC_MUX_AMIC = 0, + UL_SRC_MUX_DMIC, +}; + +/* MISO MUX */ +enum { + MISO_MUX_UL1_CH1 = 0, + MISO_MUX_UL1_CH2, + MISO_MUX_UL2_CH1, + MISO_MUX_UL2_CH2, +}; + +/* DMIC MUX */ +enum { + DMIC_MUX_DMIC_DATA0 = 0, + DMIC_MUX_DMIC_DATA1_L, + DMIC_MUX_DMIC_DATA1_L_1, + DMIC_MUX_DMIC_DATA1_R, +}; + +/* ADC L MUX */ +enum { + ADC_MUX_IDLE = 0, + ADC_MUX_AIN0, + ADC_MUX_PREAMPLIFIER, + ADC_MUX_IDLE1, +}; + +/* PGA L MUX */ +enum { + PGA_L_MUX_NONE = 0, + PGA_L_MUX_AIN0, + PGA_L_MUX_AIN1, +}; + +/* PGA R MUX */ +enum { + PGA_R_MUX_NONE = 0, + PGA_R_MUX_AIN2, + PGA_R_MUX_AIN3, + PGA_R_MUX_AIN0, +}; + +/* PGA 3 MUX */ +enum { + PGA_3_MUX_NONE = 0, + PGA_3_MUX_AIN3, + PGA_3_MUX_AIN2, +}; + +struct mt6359_priv { + struct device *dev; + struct regmap *regmap; + unsigned int dl_rate[MT6359_AIF_NUM]; + unsigned int ul_rate[MT6359_AIF_NUM]; + int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX]; + unsigned int mux_select[MUX_NUM]; + unsigned int dmic_one_wire_mode; + int dev_counter[DEVICE_NUM]; + int hp_gain_ctl; + int hp_hifi_mode; + int mtkaif_protocol; +}; + +#define CODEC_MT6359_NAME "mtk-codec-mt6359" +#define IS_DCC_BASE(type) ((type) == MIC_TYPE_MUX_DCC || \ + (type) == MIC_TYPE_MUX_DCC_ECM_DIFF || \ + (type) == MIC_TYPE_MUX_DCC_ECM_SINGLE) + +void mt6359_set_mtkaif_protocol(struct snd_soc_component *cmpnt, + int mtkaif_protocol); +void mt6359_mtkaif_calibration_enable(struct snd_soc_component *cmpnt); +void mt6359_mtkaif_calibration_disable(struct snd_soc_component *cmpnt); +void mt6359_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt, + int phase_1, int phase_2, int phase_3); + +#endif/* end _MT6359_H_ */ diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c new file mode 100644 index 0000000000..5c50c7de26 --- /dev/null +++ b/sound/soc/codecs/mt6660.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (c) 2019 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt6660.h" + +struct reg_size_table { + u32 addr; + u8 size; +}; + +static const struct reg_size_table mt6660_reg_size_table[] = { + { MT6660_REG_HPF1_COEF, 4 }, + { MT6660_REG_HPF2_COEF, 4 }, + { MT6660_REG_TDM_CFG3, 2 }, + { MT6660_REG_RESV17, 2 }, + { MT6660_REG_RESV23, 2 }, + { MT6660_REG_SIGMAX, 2 }, + { MT6660_REG_DEVID, 2 }, + { MT6660_REG_HCLIP_CTRL, 2 }, + { MT6660_REG_DA_GAIN, 2 }, +}; + +static int mt6660_get_reg_size(uint32_t addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt6660_reg_size_table); i++) { + if (mt6660_reg_size_table[i].addr == addr) + return mt6660_reg_size_table[i].size; + } + return 1; +} + +static int mt6660_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct mt6660_chip *chip = context; + int size = mt6660_get_reg_size(reg); + u8 reg_data[4]; + int i; + + for (i = 0; i < size; i++) + reg_data[size - i - 1] = (val >> (8 * i)) & 0xff; + + return i2c_smbus_write_i2c_block_data(chip->i2c, reg, size, reg_data); +} + +static int mt6660_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct mt6660_chip *chip = context; + int size = mt6660_get_reg_size(reg); + int i, ret; + u8 data[4]; + u32 reg_data = 0; + + ret = i2c_smbus_read_i2c_block_data(chip->i2c, reg, size, data); + if (ret < 0) + return ret; + for (i = 0; i < size; i++) { + reg_data <<= 8; + reg_data |= data[i]; + } + *val = reg_data; + return 0; +} + +static const struct regmap_config mt6660_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .reg_write = mt6660_reg_write, + .reg_read = mt6660_reg_read, +}; + +static int mt6660_codec_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (event == SND_SOC_DAPM_POST_PMU) + usleep_range(1000, 1100); + return 0; +} + +static int mt6660_codec_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(component->dev, + "%s: before classd turn on\n", __func__); + /* config to adaptive mode */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_BST_CTRL, 0x03, 0x03); + if (ret < 0) { + dev_err(component->dev, "config mode adaptive fail\n"); + return ret; + } + break; + case SND_SOC_DAPM_POST_PMU: + /* voltage sensing enable */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_RESV7, 0x04, 0x04); + if (ret < 0) { + dev_err(component->dev, + "enable voltage sensing fail\n"); + return ret; + } + dev_dbg(component->dev, "Amp on\n"); + break; + case SND_SOC_DAPM_PRE_PMD: + dev_dbg(component->dev, "Amp off\n"); + /* voltage sensing disable */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_RESV7, 0x04, 0x00); + if (ret < 0) { + dev_err(component->dev, + "disable voltage sensing fail\n"); + return ret; + } + /* pop-noise improvement 1 */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_RESV10, 0x10, 0x10); + if (ret < 0) { + dev_err(component->dev, + "pop-noise improvement 1 fail\n"); + return ret; + } + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(component->dev, + "%s: after classd turn off\n", __func__); + /* pop-noise improvement 2 */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_RESV10, 0x10, 0x00); + if (ret < 0) { + dev_err(component->dev, + "pop-noise improvement 2 fail\n"); + return ret; + } + /* config to off mode */ + ret = snd_soc_component_update_bits(component, + MT6660_REG_BST_CTRL, 0x03, 0x00); + if (ret < 0) { + dev_err(component->dev, "config mode off fail\n"); + return ret; + } + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget mt6660_component_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("DAC", NULL, MT6660_REG_PLL_CFG1, + 0, 1, mt6660_codec_dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_ADC("VI ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV_E("ClassD", MT6660_REG_SYSTEM_CTRL, 2, 0, + NULL, 0, mt6660_codec_classd_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("OUTP"), + SND_SOC_DAPM_OUTPUT("OUTN"), +}; + +static const struct snd_soc_dapm_route mt6660_component_dapm_routes[] = { + { "DAC", NULL, "aif_playback" }, + { "PGA", NULL, "DAC" }, + { "ClassD", NULL, "PGA" }, + { "OUTP", NULL, "ClassD" }, + { "OUTN", NULL, "ClassD" }, + { "VI ADC", NULL, "ClassD" }, + { "aif_capture", NULL, "VI ADC" }, +}; + +static int mt6660_component_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mt6660_chip *chip = (struct mt6660_chip *) + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = chip->chip_rev & 0x0f; + return 0; +} + +static const DECLARE_TLV_DB_SCALE(vol_ctl_tlv, -1155, 5, 0); + +static const struct snd_kcontrol_new mt6660_component_snd_controls[] = { + SOC_SINGLE_TLV("Digital Volume", MT6660_REG_VOL_CTRL, 0, 255, + 1, vol_ctl_tlv), + SOC_SINGLE("Hard Clip Switch", MT6660_REG_HCLIP_CTRL, 8, 1, 0), + SOC_SINGLE("Clip Switch", MT6660_REG_SPS_CTRL, 0, 1, 0), + SOC_SINGLE("Boost Mode", MT6660_REG_BST_CTRL, 0, 3, 0), + SOC_SINGLE("DRE Switch", MT6660_REG_DRE_CTRL, 0, 1, 0), + SOC_SINGLE("DC Protect Switch", MT6660_REG_DC_PROTECT_CTRL, 3, 1, 0), + SOC_SINGLE("Data Output Left Channel Selection", + MT6660_REG_DATAO_SEL, 3, 7, 0), + SOC_SINGLE("Data Output Right Channel Selection", + MT6660_REG_DATAO_SEL, 0, 7, 0), + SOC_SINGLE_EXT("T0 SEL", MT6660_REG_CALI_T0, 0, 7, 0, + snd_soc_get_volsw, NULL), + SOC_SINGLE_EXT("Chip Rev", MT6660_REG_DEVID, 8, 15, 0, + mt6660_component_get_volsw, NULL), +}; + +static int _mt6660_chip_power_on(struct mt6660_chip *chip, int on_off) +{ + return regmap_write_bits(chip->regmap, MT6660_REG_SYSTEM_CTRL, + 0x01, on_off ? 0x00 : 0x01); +} + +struct reg_table { + uint32_t addr; + uint32_t mask; + uint32_t val; +}; + +static const struct reg_table mt6660_setting_table[] = { + { 0x20, 0x80, 0x00 }, + { 0x30, 0x01, 0x00 }, + { 0x50, 0x1c, 0x04 }, + { 0xB1, 0x0c, 0x00 }, + { 0xD3, 0x03, 0x03 }, + { 0xE0, 0x01, 0x00 }, + { 0x98, 0x44, 0x04 }, + { 0xB9, 0xff, 0x82 }, + { 0xB7, 0x7777, 0x7273 }, + { 0xB6, 0x07, 0x03 }, + { 0x6B, 0xe0, 0x20 }, + { 0x07, 0xff, 0x70 }, + { 0xBB, 0xff, 0x20 }, + { 0x69, 0xff, 0x40 }, + { 0xBD, 0xffff, 0x17f8 }, + { 0x70, 0xff, 0x15 }, + { 0x7C, 0xff, 0x00 }, + { 0x46, 0xff, 0x1d }, + { 0x1A, 0xffffffff, 0x7fdb7ffe }, + { 0x1B, 0xffffffff, 0x7fdb7ffe }, + { 0x51, 0xff, 0x58 }, + { 0xA2, 0xff, 0xce }, + { 0x33, 0xffff, 0x7fff }, + { 0x4C, 0xffff, 0x0116 }, + { 0x16, 0x1800, 0x0800 }, + { 0x68, 0x1f, 0x07 }, +}; + +static int mt6660_component_setting(struct snd_soc_component *component) +{ + struct mt6660_chip *chip = snd_soc_component_get_drvdata(component); + int ret = 0; + size_t i = 0; + + ret = _mt6660_chip_power_on(chip, 1); + if (ret < 0) { + dev_err(component->dev, "%s chip power on failed\n", __func__); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(mt6660_setting_table); i++) { + ret = snd_soc_component_update_bits(component, + mt6660_setting_table[i].addr, + mt6660_setting_table[i].mask, + mt6660_setting_table[i].val); + if (ret < 0) { + dev_err(component->dev, "%s update 0x%02x failed\n", + __func__, mt6660_setting_table[i].addr); + return ret; + } + } + + ret = _mt6660_chip_power_on(chip, 0); + if (ret < 0) { + dev_err(component->dev, "%s chip power off failed\n", __func__); + return ret; + } + + return 0; +} + +static int mt6660_component_probe(struct snd_soc_component *component) +{ + struct mt6660_chip *chip = snd_soc_component_get_drvdata(component); + int ret; + + dev_dbg(component->dev, "%s\n", __func__); + snd_soc_component_init_regmap(component, chip->regmap); + + ret = mt6660_component_setting(component); + if (ret < 0) + dev_err(chip->dev, "mt6660 component setting failed\n"); + + return ret; +} + +static void mt6660_component_remove(struct snd_soc_component *component) +{ + dev_dbg(component->dev, "%s\n", __func__); + snd_soc_component_exit_regmap(component); +} + +static const struct snd_soc_component_driver mt6660_component_driver = { + .probe = mt6660_component_probe, + .remove = mt6660_component_remove, + + .controls = mt6660_component_snd_controls, + .num_controls = ARRAY_SIZE(mt6660_component_snd_controls), + .dapm_widgets = mt6660_component_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt6660_component_dapm_widgets), + .dapm_routes = mt6660_component_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mt6660_component_dapm_routes), + + .idle_bias_on = false, /* idle_bias_off = true */ + .endianness = 1, +}; + +static int mt6660_component_aif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) +{ + int word_len = params_physical_width(hw_params); + int aud_bit = params_width(hw_params); + u16 reg_data = 0; + int ret; + + dev_dbg(dai->dev, "%s: ++\n", __func__); + dev_dbg(dai->dev, "format: 0x%08x\n", params_format(hw_params)); + dev_dbg(dai->dev, "rate: 0x%08x\n", params_rate(hw_params)); + dev_dbg(dai->dev, "word_len: %d, aud_bit: %d\n", word_len, aud_bit); + if (word_len > 32 || word_len < 16) { + dev_err(dai->dev, "not supported word length\n"); + return -ENOTSUPP; + } + switch (aud_bit) { + case 16: + reg_data = 3; + break; + case 18: + reg_data = 2; + break; + case 20: + reg_data = 1; + break; + case 24: + case 32: + reg_data = 0; + break; + default: + return -ENOTSUPP; + } + ret = snd_soc_component_update_bits(dai->component, + MT6660_REG_SERIAL_CFG1, 0xc0, (reg_data << 6)); + if (ret < 0) { + dev_err(dai->dev, "config aud bit fail\n"); + return ret; + } + ret = snd_soc_component_update_bits(dai->component, + MT6660_REG_TDM_CFG3, 0x3f0, word_len << 4); + if (ret < 0) { + dev_err(dai->dev, "config word len fail\n"); + return ret; + } + dev_dbg(dai->dev, "%s: --\n", __func__); + return 0; +} + +static const struct snd_soc_dai_ops mt6660_component_aif_ops = { + .hw_params = mt6660_component_aif_hw_params, +}; + +#define STUB_RATES SNDRV_PCM_RATE_8000_192000 +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE) + +static struct snd_soc_dai_driver mt6660_codec_dai = { + .name = "mt6660-aif", + .playback = { + .stream_name = "aif_playback", + .channels_min = 1, + .channels_max = 2, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, + .capture = { + .stream_name = "aif_capture", + .channels_min = 1, + .channels_max = 2, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, + /* dai properties */ + .symmetric_rate = 1, + .symmetric_channels = 1, + .symmetric_sample_bits = 1, + /* dai operations */ + .ops = &mt6660_component_aif_ops, +}; + +static int _mt6660_chip_id_check(struct mt6660_chip *chip) +{ + int ret; + unsigned int val; + + ret = regmap_read(chip->regmap, MT6660_REG_DEVID, &val); + if (ret < 0) + return ret; + val &= 0x0ff0; + if (val != 0x00e0 && val != 0x01e0) { + dev_err(chip->dev, "%s id(%x) not match\n", __func__, val); + return -ENODEV; + } + return 0; +} + +static int _mt6660_chip_sw_reset(struct mt6660_chip *chip) +{ + int ret; + + /* turn on main pll first, then trigger reset */ + ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x00); + if (ret < 0) + return ret; + ret = regmap_write(chip->regmap, MT6660_REG_SYSTEM_CTRL, 0x80); + if (ret < 0) + return ret; + msleep(30); + return 0; +} + +static int _mt6660_read_chip_revision(struct mt6660_chip *chip) +{ + int ret; + unsigned int val; + + ret = regmap_read(chip->regmap, MT6660_REG_DEVID, &val); + if (ret < 0) { + dev_err(chip->dev, "get chip revision fail\n"); + return ret; + } + chip->chip_rev = val&0xff; + dev_info(chip->dev, "%s chip_rev = %x\n", __func__, chip->chip_rev); + return 0; +} + +static int mt6660_i2c_probe(struct i2c_client *client) +{ + struct mt6660_chip *chip = NULL; + int ret; + + dev_dbg(&client->dev, "%s\n", __func__); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + chip->i2c = client; + chip->dev = &client->dev; + mutex_init(&chip->io_lock); + i2c_set_clientdata(client, chip); + + chip->regmap = devm_regmap_init(&client->dev, + NULL, chip, &mt6660_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(&client->dev, "failed to initialise regmap: %d\n", ret); + return ret; + } + + /* chip reset first */ + ret = _mt6660_chip_sw_reset(chip); + if (ret < 0) { + dev_err(chip->dev, "chip reset fail\n"); + goto probe_fail; + } + /* chip power on */ + ret = _mt6660_chip_power_on(chip, 1); + if (ret < 0) { + dev_err(chip->dev, "chip power on 2 fail\n"); + goto probe_fail; + } + /* chip devid check */ + ret = _mt6660_chip_id_check(chip); + if (ret < 0) { + dev_err(chip->dev, "chip id check fail\n"); + goto probe_fail; + } + /* chip revision get */ + ret = _mt6660_read_chip_revision(chip); + if (ret < 0) { + dev_err(chip->dev, "read chip revision fail\n"); + goto probe_fail; + } + pm_runtime_set_active(chip->dev); + pm_runtime_enable(chip->dev); + + ret = devm_snd_soc_register_component(chip->dev, + &mt6660_component_driver, + &mt6660_codec_dai, 1); + if (ret) + pm_runtime_disable(chip->dev); + + return ret; + +probe_fail: + _mt6660_chip_power_on(chip, 0); + mutex_destroy(&chip->io_lock); + return ret; +} + +static void mt6660_i2c_remove(struct i2c_client *client) +{ + struct mt6660_chip *chip = i2c_get_clientdata(client); + + pm_runtime_disable(chip->dev); + pm_runtime_set_suspended(chip->dev); + mutex_destroy(&chip->io_lock); +} + +static int __maybe_unused mt6660_i2c_runtime_suspend(struct device *dev) +{ + struct mt6660_chip *chip = dev_get_drvdata(dev); + + dev_dbg(dev, "enter low power mode\n"); + return regmap_update_bits(chip->regmap, + MT6660_REG_SYSTEM_CTRL, 0x01, 0x01); +} + +static int __maybe_unused mt6660_i2c_runtime_resume(struct device *dev) +{ + struct mt6660_chip *chip = dev_get_drvdata(dev); + + dev_dbg(dev, "exit low power mode\n"); + return regmap_update_bits(chip->regmap, + MT6660_REG_SYSTEM_CTRL, 0x01, 0x00); +} + +static const struct dev_pm_ops mt6660_dev_pm_ops = { + SET_RUNTIME_PM_OPS(mt6660_i2c_runtime_suspend, + mt6660_i2c_runtime_resume, NULL) +}; + +static const struct of_device_id __maybe_unused mt6660_of_id[] = { + { .compatible = "mediatek,mt6660",}, + {}, +}; +MODULE_DEVICE_TABLE(of, mt6660_of_id); + +static const struct i2c_device_id mt6660_i2c_id[] = { + {"mt6660", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mt6660_i2c_id); + +static struct i2c_driver mt6660_i2c_driver = { + .driver = { + .name = "mt6660", + .of_match_table = of_match_ptr(mt6660_of_id), + .pm = &mt6660_dev_pm_ops, + }, + .probe = mt6660_i2c_probe, + .remove = mt6660_i2c_remove, + .id_table = mt6660_i2c_id, +}; +module_i2c_driver(mt6660_i2c_driver); + +MODULE_AUTHOR("Jeff Chang "); +MODULE_DESCRIPTION("MT6660 SPKAMP Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0.8_G"); diff --git a/sound/soc/codecs/mt6660.h b/sound/soc/codecs/mt6660.h new file mode 100644 index 0000000000..054a3c56ec --- /dev/null +++ b/sound/soc/codecs/mt6660.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 MediaTek Inc. + */ + +#ifndef __SND_SOC_MT6660_H +#define __SND_SOC_MT6660_H + +#include +#include + +#pragma pack(push, 1) +struct mt6660_platform_data { + u8 init_setting_num; + u32 *init_setting_addr; + u32 *init_setting_mask; + u32 *init_setting_val; +}; + +struct mt6660_chip { + struct i2c_client *i2c; + struct device *dev; + struct platform_device *param_dev; + struct mt6660_platform_data plat_data; + struct mutex io_lock; + struct regmap *regmap; + u16 chip_rev; +}; +#pragma pack(pop) + +#define MT6660_REG_DEVID (0x00) +#define MT6660_REG_SYSTEM_CTRL (0x03) +#define MT6660_REG_IRQ_STATUS1 (0x05) +#define MT6660_REG_ADDA_CLOCK (0x07) +#define MT6660_REG_SERIAL_CFG1 (0x10) +#define MT6660_REG_DATAO_SEL (0x12) +#define MT6660_REG_TDM_CFG3 (0x15) +#define MT6660_REG_HPF_CTRL (0x18) +#define MT6660_REG_HPF1_COEF (0x1A) +#define MT6660_REG_HPF2_COEF (0x1B) +#define MT6660_REG_PATH_BYPASS (0x1E) +#define MT6660_REG_WDT_CTRL (0x20) +#define MT6660_REG_HCLIP_CTRL (0x24) +#define MT6660_REG_VOL_CTRL (0x29) +#define MT6660_REG_SPS_CTRL (0x30) +#define MT6660_REG_SIGMAX (0x33) +#define MT6660_REG_CALI_T0 (0x3F) +#define MT6660_REG_BST_CTRL (0x40) +#define MT6660_REG_PROTECTION_CFG (0x46) +#define MT6660_REG_DA_GAIN (0x4c) +#define MT6660_REG_AUDIO_IN2_SEL (0x50) +#define MT6660_REG_SIG_GAIN (0x51) +#define MT6660_REG_PLL_CFG1 (0x60) +#define MT6660_REG_DRE_CTRL (0x68) +#define MT6660_REG_DRE_THDMODE (0x69) +#define MT6660_REG_DRE_CORASE (0x6B) +#define MT6660_REG_PWM_CTRL (0x70) +#define MT6660_REG_DC_PROTECT_CTRL (0x74) +#define MT6660_REG_ADC_USB_MODE (0x7c) +#define MT6660_REG_INTERNAL_CFG (0x88) +#define MT6660_REG_RESV0 (0x98) +#define MT6660_REG_RESV1 (0x99) +#define MT6660_REG_RESV2 (0x9A) +#define MT6660_REG_RESV3 (0x9B) +#define MT6660_REG_RESV6 (0xA2) +#define MT6660_REG_RESV7 (0xA3) +#define MT6660_REG_RESV10 (0xB0) +#define MT6660_REG_RESV11 (0xB1) +#define MT6660_REG_RESV16 (0xB6) +#define MT6660_REG_RESV17 (0xB7) +#define MT6660_REG_RESV19 (0xB9) +#define MT6660_REG_RESV21 (0xBB) +#define MT6660_REG_RESV23 (0xBD) +#define MT6660_REG_RESV31 (0xD3) +#define MT6660_REG_RESV40 (0xE0) + +#endif /* __SND_SOC_MT6660_H */ diff --git a/sound/soc/codecs/nau8315.c b/sound/soc/codecs/nau8315.c new file mode 100644 index 0000000000..125742601f --- /dev/null +++ b/sound/soc/codecs/nau8315.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// nau8315.c -- NAU8315 ALSA SoC Audio Amplifier Driver +// +// Copyright 2020 Nuvoton Technology Crop. +// +// Author: David Lin +// +// Based on MAX98357A.c + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nau8315_priv { + struct gpio_desc *enable; + int enpin_switch; +}; + +static int nau8315_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8315_priv *nau8315 = + snd_soc_component_get_drvdata(component); + + if (!nau8315->enable) + return 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (nau8315->enpin_switch) { + gpiod_set_value(nau8315->enable, 1); + dev_dbg(component->dev, "set enable to 1"); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + gpiod_set_value(nau8315->enable, 0); + dev_dbg(component->dev, "set enable to 0"); + break; + } + + return 0; +} + +static int nau8315_enpin_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct nau8315_priv *nau8315 = + snd_soc_component_get_drvdata(component); + + if (event & SND_SOC_DAPM_PRE_PMU) + nau8315->enpin_switch = 1; + else if (event & SND_SOC_DAPM_POST_PMD) + nau8315->enpin_switch = 0; + + return 0; +} + +static const struct snd_soc_dapm_widget nau8315_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("Speaker"), + SND_SOC_DAPM_OUT_DRV_E("EN_Pin", SND_SOC_NOPM, 0, 0, NULL, 0, + nau8315_enpin_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route nau8315_dapm_routes[] = { + {"EN_Pin", NULL, "HiFi Playback"}, + {"Speaker", NULL, "EN_Pin"}, +}; + +static const struct snd_soc_component_driver nau8315_component_driver = { + .dapm_widgets = nau8315_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8315_dapm_widgets), + .dapm_routes = nau8315_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8315_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_dai_ops nau8315_dai_ops = { + .trigger = nau8315_daiops_trigger, +}; + +#define NAU8315_RATES SNDRV_PCM_RATE_8000_96000 +#define NAU8315_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE) + +static struct snd_soc_dai_driver nau8315_dai_driver = { + .name = "nau8315-hifi", + .playback = { + .stream_name = "HiFi Playback", + .formats = NAU8315_FORMATS, + .rates = NAU8315_RATES, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &nau8315_dai_ops, +}; + +static int nau8315_platform_probe(struct platform_device *pdev) +{ + struct nau8315_priv *nau8315; + + nau8315 = devm_kzalloc(&pdev->dev, sizeof(*nau8315), GFP_KERNEL); + if (!nau8315) + return -ENOMEM; + + nau8315->enable = devm_gpiod_get_optional(&pdev->dev, + "enable", GPIOD_OUT_LOW); + if (IS_ERR(nau8315->enable)) + return PTR_ERR(nau8315->enable); + + dev_set_drvdata(&pdev->dev, nau8315); + + return devm_snd_soc_register_component(&pdev->dev, + &nau8315_component_driver, + &nau8315_dai_driver, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id nau8315_device_id[] = { + { .compatible = "nuvoton,nau8315" }, + { .compatible = "nuvoton,nau8318" }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8315_device_id); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id nau8315_acpi_match[] = { + { "NVTN2010", 0 }, + { "NVTN2012", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, nau8315_acpi_match); +#endif + +static struct platform_driver nau8315_platform_driver = { + .driver = { + .name = "nau8315", + .of_match_table = of_match_ptr(nau8315_device_id), + .acpi_match_table = ACPI_PTR(nau8315_acpi_match), + }, + .probe = nau8315_platform_probe, +}; +module_platform_driver(nau8315_platform_driver); + +MODULE_DESCRIPTION("ASoC NAU8315 Mono Class-D Amplifier Driver"); +MODULE_AUTHOR("David Lin "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c new file mode 100644 index 0000000000..2174a89772 --- /dev/null +++ b/sound/soc/codecs/nau8540.c @@ -0,0 +1,900 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NAU85L40 ALSA SoC audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * Author: John Hsu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nau8540.h" + + +#define NAU_FREF_MAX 13500000 +#define NAU_FVCO_MAX 100000000 +#define NAU_FVCO_MIN 90000000 + +/* the maximum frequency of CLK_ADC */ +#define CLK_ADC_MAX 6144000 + +/* scaling for mclk from sysclk_src output */ +static const struct nau8540_fll_attr mclk_src_scaling[] = { + { 1, 0x0 }, + { 2, 0x2 }, + { 4, 0x3 }, + { 8, 0x4 }, + { 16, 0x5 }, + { 32, 0x6 }, + { 3, 0x7 }, + { 6, 0xa }, + { 12, 0xb }, + { 24, 0xc }, +}; + +/* ratio for input clk freq */ +static const struct nau8540_fll_attr fll_ratio[] = { + { 512000, 0x01 }, + { 256000, 0x02 }, + { 128000, 0x04 }, + { 64000, 0x08 }, + { 32000, 0x10 }, + { 8000, 0x20 }, + { 4000, 0x40 }, +}; + +static const struct nau8540_fll_attr fll_pre_scalar[] = { + { 1, 0x0 }, + { 2, 0x1 }, + { 4, 0x2 }, + { 8, 0x3 }, +}; + +/* over sampling rate */ +static const struct nau8540_osr_attr osr_adc_sel[] = { + { 32, 3 }, /* OSR 32, SRC 1/8 */ + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 256, 0 }, /* OSR 256, SRC 1 */ +}; + +static const struct reg_default nau8540_reg_defaults[] = { + {NAU8540_REG_POWER_MANAGEMENT, 0x0000}, + {NAU8540_REG_CLOCK_CTRL, 0x0000}, + {NAU8540_REG_CLOCK_SRC, 0x0000}, + {NAU8540_REG_FLL1, 0x0001}, + {NAU8540_REG_FLL2, 0x3126}, + {NAU8540_REG_FLL3, 0x0008}, + {NAU8540_REG_FLL4, 0x0010}, + {NAU8540_REG_FLL5, 0xC000}, + {NAU8540_REG_FLL6, 0x6000}, + {NAU8540_REG_FLL_VCO_RSV, 0xF13C}, + {NAU8540_REG_PCM_CTRL0, 0x000B}, + {NAU8540_REG_PCM_CTRL1, 0x3010}, + {NAU8540_REG_PCM_CTRL2, 0x0800}, + {NAU8540_REG_PCM_CTRL3, 0x0000}, + {NAU8540_REG_PCM_CTRL4, 0x000F}, + {NAU8540_REG_ALC_CONTROL_1, 0x0000}, + {NAU8540_REG_ALC_CONTROL_2, 0x700B}, + {NAU8540_REG_ALC_CONTROL_3, 0x0022}, + {NAU8540_REG_ALC_CONTROL_4, 0x1010}, + {NAU8540_REG_ALC_CONTROL_5, 0x1010}, + {NAU8540_REG_NOTCH_FIL1_CH1, 0x0000}, + {NAU8540_REG_NOTCH_FIL2_CH1, 0x0000}, + {NAU8540_REG_NOTCH_FIL1_CH2, 0x0000}, + {NAU8540_REG_NOTCH_FIL2_CH2, 0x0000}, + {NAU8540_REG_NOTCH_FIL1_CH3, 0x0000}, + {NAU8540_REG_NOTCH_FIL2_CH3, 0x0000}, + {NAU8540_REG_NOTCH_FIL1_CH4, 0x0000}, + {NAU8540_REG_NOTCH_FIL2_CH4, 0x0000}, + {NAU8540_REG_HPF_FILTER_CH12, 0x0000}, + {NAU8540_REG_HPF_FILTER_CH34, 0x0000}, + {NAU8540_REG_ADC_SAMPLE_RATE, 0x0002}, + {NAU8540_REG_DIGITAL_GAIN_CH1, 0x0400}, + {NAU8540_REG_DIGITAL_GAIN_CH2, 0x0400}, + {NAU8540_REG_DIGITAL_GAIN_CH3, 0x0400}, + {NAU8540_REG_DIGITAL_GAIN_CH4, 0x0400}, + {NAU8540_REG_DIGITAL_MUX, 0x00E4}, + {NAU8540_REG_GPIO_CTRL, 0x0000}, + {NAU8540_REG_MISC_CTRL, 0x0000}, + {NAU8540_REG_I2C_CTRL, 0xEFFF}, + {NAU8540_REG_VMID_CTRL, 0x0000}, + {NAU8540_REG_MUTE, 0x0000}, + {NAU8540_REG_ANALOG_ADC1, 0x0011}, + {NAU8540_REG_ANALOG_ADC2, 0x0020}, + {NAU8540_REG_ANALOG_PWR, 0x0000}, + {NAU8540_REG_MIC_BIAS, 0x0004}, + {NAU8540_REG_REFERENCE, 0x0000}, + {NAU8540_REG_FEPGA1, 0x0000}, + {NAU8540_REG_FEPGA2, 0x0000}, + {NAU8540_REG_FEPGA3, 0x0101}, + {NAU8540_REG_FEPGA4, 0x0101}, + {NAU8540_REG_PWR, 0x0000}, +}; + +static bool nau8540_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8540_REG_POWER_MANAGEMENT ... NAU8540_REG_FLL_VCO_RSV: + case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4: + case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5: + case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ADC_SAMPLE_RATE: + case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX: + case NAU8540_REG_P2P_CH1 ... NAU8540_REG_I2C_CTRL: + case NAU8540_REG_I2C_DEVICE_ID: + case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE: + case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR: + return true; + default: + return false; + } + +} + +static bool nau8540_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8540_REG_SW_RESET ... NAU8540_REG_FLL_VCO_RSV: + case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4: + case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5: + case NAU8540_REG_NOTCH_FIL1_CH1 ... NAU8540_REG_ADC_SAMPLE_RATE: + case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX: + case NAU8540_REG_GPIO_CTRL ... NAU8540_REG_I2C_CTRL: + case NAU8540_REG_RST: + case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE: + case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR: + return true; + default: + return false; + } +} + +static bool nau8540_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8540_REG_SW_RESET: + case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ALC_STATUS: + case NAU8540_REG_P2P_CH1 ... NAU8540_REG_PEAK_CH4: + case NAU8540_REG_I2C_DEVICE_ID: + case NAU8540_REG_RST: + return true; + default: + return false; + } +} + + +static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -12800, 3600); +static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600); + +static const struct snd_kcontrol_new nau8540_snd_controls[] = { + SOC_SINGLE_TLV("Mic1 Volume", NAU8540_REG_DIGITAL_GAIN_CH1, + 0, 0x520, 0, adc_vol_tlv), + SOC_SINGLE_TLV("Mic2 Volume", NAU8540_REG_DIGITAL_GAIN_CH2, + 0, 0x520, 0, adc_vol_tlv), + SOC_SINGLE_TLV("Mic3 Volume", NAU8540_REG_DIGITAL_GAIN_CH3, + 0, 0x520, 0, adc_vol_tlv), + SOC_SINGLE_TLV("Mic4 Volume", NAU8540_REG_DIGITAL_GAIN_CH4, + 0, 0x520, 0, adc_vol_tlv), + + SOC_SINGLE_TLV("Frontend PGA1 Volume", NAU8540_REG_FEPGA3, + 0, 0x25, 0, fepga_gain_tlv), + SOC_SINGLE_TLV("Frontend PGA2 Volume", NAU8540_REG_FEPGA3, + 8, 0x25, 0, fepga_gain_tlv), + SOC_SINGLE_TLV("Frontend PGA3 Volume", NAU8540_REG_FEPGA4, + 0, 0x25, 0, fepga_gain_tlv), + SOC_SINGLE_TLV("Frontend PGA4 Volume", NAU8540_REG_FEPGA4, + 8, 0x25, 0, fepga_gain_tlv), +}; + +static const char * const adc_channel[] = { + "ADC channel 1", "ADC channel 2", "ADC channel 3", "ADC channel 4" +}; +static SOC_ENUM_SINGLE_DECL( + digital_ch4_enum, NAU8540_REG_DIGITAL_MUX, 6, adc_channel); + +static const struct snd_kcontrol_new digital_ch4_mux = + SOC_DAPM_ENUM("Digital CH4 Select", digital_ch4_enum); + +static SOC_ENUM_SINGLE_DECL( + digital_ch3_enum, NAU8540_REG_DIGITAL_MUX, 4, adc_channel); + +static const struct snd_kcontrol_new digital_ch3_mux = + SOC_DAPM_ENUM("Digital CH3 Select", digital_ch3_enum); + +static SOC_ENUM_SINGLE_DECL( + digital_ch2_enum, NAU8540_REG_DIGITAL_MUX, 2, adc_channel); + +static const struct snd_kcontrol_new digital_ch2_mux = + SOC_DAPM_ENUM("Digital CH2 Select", digital_ch2_enum); + +static SOC_ENUM_SINGLE_DECL( + digital_ch1_enum, NAU8540_REG_DIGITAL_MUX, 0, adc_channel); + +static const struct snd_kcontrol_new digital_ch1_mux = + SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum); + +static int adc_power_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + msleep(300); + /* DO12 and DO34 pad output enable */ + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, + NAU8540_I2S_DO12_TRI, 0); + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, + NAU8540_I2S_DO34_TRI, 0); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, + NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI); + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, + NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI); + } + return 0; +} + +static int aiftx_power_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + regmap_write(nau8540->regmap, NAU8540_REG_RST, 0x0001); + regmap_write(nau8540->regmap, NAU8540_REG_RST, 0x0000); + } + return 0; +} + +static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("MICBIAS2", NAU8540_REG_MIC_BIAS, 11, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS1", NAU8540_REG_MIC_BIAS, 10, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("MIC3"), + SND_SOC_DAPM_INPUT("MIC4"), + + SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0), + SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0), + SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0), + SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, + NAU8540_REG_POWER_MANAGEMENT, 0, 0, adc_power_control, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC2", NULL, + NAU8540_REG_POWER_MANAGEMENT, 1, 0, adc_power_control, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC3", NULL, + NAU8540_REG_POWER_MANAGEMENT, 2, 0, adc_power_control, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC4", NULL, + NAU8540_REG_POWER_MANAGEMENT, 3, 0, adc_power_control, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Digital CH4 Mux", + SND_SOC_NOPM, 0, 0, &digital_ch4_mux), + SND_SOC_DAPM_MUX("Digital CH3 Mux", + SND_SOC_NOPM, 0, 0, &digital_ch3_mux), + SND_SOC_DAPM_MUX("Digital CH2 Mux", + SND_SOC_NOPM, 0, 0, &digital_ch2_mux), + SND_SOC_DAPM_MUX("Digital CH1 Mux", + SND_SOC_NOPM, 0, 0, &digital_ch1_mux), + + SND_SOC_DAPM_AIF_OUT_E("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0, + aiftx_power_control, SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route nau8540_dapm_routes[] = { + {"Frontend PGA1", NULL, "MIC1"}, + {"Frontend PGA2", NULL, "MIC2"}, + {"Frontend PGA3", NULL, "MIC3"}, + {"Frontend PGA4", NULL, "MIC4"}, + + {"ADC1", NULL, "Frontend PGA1"}, + {"ADC2", NULL, "Frontend PGA2"}, + {"ADC3", NULL, "Frontend PGA3"}, + {"ADC4", NULL, "Frontend PGA4"}, + + {"ADC CH1", NULL, "ADC1"}, + {"ADC CH2", NULL, "ADC2"}, + {"ADC CH3", NULL, "ADC3"}, + {"ADC CH4", NULL, "ADC4"}, + + {"ADC1", NULL, "MICBIAS1"}, + {"ADC2", NULL, "MICBIAS1"}, + {"ADC3", NULL, "MICBIAS2"}, + {"ADC4", NULL, "MICBIAS2"}, + + {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"}, + {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"}, + {"Digital CH1 Mux", "ADC channel 3", "ADC CH3"}, + {"Digital CH1 Mux", "ADC channel 4", "ADC CH4"}, + + {"Digital CH2 Mux", "ADC channel 1", "ADC CH1"}, + {"Digital CH2 Mux", "ADC channel 2", "ADC CH2"}, + {"Digital CH2 Mux", "ADC channel 3", "ADC CH3"}, + {"Digital CH2 Mux", "ADC channel 4", "ADC CH4"}, + + {"Digital CH3 Mux", "ADC channel 1", "ADC CH1"}, + {"Digital CH3 Mux", "ADC channel 2", "ADC CH2"}, + {"Digital CH3 Mux", "ADC channel 3", "ADC CH3"}, + {"Digital CH3 Mux", "ADC channel 4", "ADC CH4"}, + + {"Digital CH4 Mux", "ADC channel 1", "ADC CH1"}, + {"Digital CH4 Mux", "ADC channel 2", "ADC CH2"}, + {"Digital CH4 Mux", "ADC channel 3", "ADC CH3"}, + {"Digital CH4 Mux", "ADC channel 4", "ADC CH4"}, + + {"AIFTX", NULL, "Digital CH1 Mux"}, + {"AIFTX", NULL, "Digital CH2 Mux"}, + {"AIFTX", NULL, "Digital CH3 Mux"}, + {"AIFTX", NULL, "Digital CH4 Mux"}, +}; + +static const struct nau8540_osr_attr * +nau8540_get_osr(struct nau8540 *nau8540) +{ + unsigned int osr; + + regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr); + osr &= NAU8540_ADC_OSR_MASK; + if (osr >= ARRAY_SIZE(osr_adc_sel)) + return NULL; + return &osr_adc_sel[osr]; +} + +static int nau8540_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + const struct nau8540_osr_attr *osr; + + osr = nau8540_get_osr(nau8540); + if (!osr || !osr->osr) + return -EINVAL; + + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + 0, CLK_ADC_MAX / osr->osr); +} + +static int nau8540_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0; + const struct nau8540_osr_attr *osr; + + /* CLK_ADC = OSR * FS + * ADC clock frequency is defined as Over Sampling Rate (OSR) + * multiplied by the audio sample rate (Fs). Note that the OSR and Fs + * values must be selected such that the maximum frequency is less + * than 6.144 MHz. + */ + osr = nau8540_get_osr(nau8540); + if (!osr || !osr->osr) + return -EINVAL; + if (params_rate(params) * osr->osr > CLK_ADC_MAX) + return -EINVAL; + regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC, + NAU8540_CLK_ADC_SRC_MASK, + osr->clk_src << NAU8540_CLK_ADC_SRC_SFT); + + switch (params_width(params)) { + case 16: + val_len |= NAU8540_I2S_DL_16; + break; + case 20: + val_len |= NAU8540_I2S_DL_20; + break; + case 24: + val_len |= NAU8540_I2S_DL_24; + break; + case 32: + val_len |= NAU8540_I2S_DL_32; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0, + NAU8540_I2S_DL_MASK, val_len); + + return 0; +} + +static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + unsigned int ctrl1_val = 0, ctrl2_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= NAU8540_I2S_MS_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8540_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8540_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8540_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1_val |= NAU8540_I2S_DF_RIGTH; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8540_I2S_DF_PCM_AB; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1_val |= NAU8540_I2S_DF_PCM_AB; + ctrl1_val |= NAU8540_I2S_PCMB_EN; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0, + NAU8540_I2S_DL_MASK | NAU8540_I2S_DF_MASK | + NAU8540_I2S_BP_INV | NAU8540_I2S_PCMB_EN, ctrl1_val); + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, + NAU8540_I2S_MS_MASK | NAU8540_I2S_DO12_OE, ctrl2_val); + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, + NAU8540_I2S_DO34_OE, 0); + + return 0; +} + +/** + * nau8540_set_tdm_slot - configure DAI TX TDM. + * @dai: DAI + * @tx_mask: bitmask representing active TX slots. Ex. + * 0xf for normal 4 channel TDM. + * 0xf0 for shifted 4 channel TDM + * @rx_mask: no used. + * @slots: Number of slots in use. + * @slot_width: Width in bits for each slot. + * + * Configures a DAI for TDM operation. Only support 4 slots TDM. + */ +static int nau8540_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + unsigned int ctrl2_val = 0, ctrl4_val = 0; + + if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf))) + return -EINVAL; + + ctrl4_val |= (NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN); + if (tx_mask & 0xf0) { + ctrl2_val = 4 * slot_width; + ctrl4_val |= (tx_mask >> 4); + } else { + ctrl4_val |= tx_mask; + } + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL4, + NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN | + NAU8540_TDM_TX_MASK, ctrl4_val); + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1, + NAU8540_I2S_DO12_OE, NAU8540_I2S_DO12_OE); + regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2, + NAU8540_I2S_DO34_OE | NAU8540_I2S_TSLOT_L_MASK, + NAU8540_I2S_DO34_OE | ctrl2_val); + + return 0; +} + + +static const struct snd_soc_dai_ops nau8540_dai_ops = { + .startup = nau8540_dai_startup, + .hw_params = nau8540_hw_params, + .set_fmt = nau8540_set_fmt, + .set_tdm_slot = nau8540_set_tdm_slot, +}; + +#define NAU8540_RATES SNDRV_PCM_RATE_8000_48000 +#define NAU8540_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver nau8540_dai = { + .name = "nau8540-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = NAU8540_RATES, + .formats = NAU8540_FORMATS, + }, + .ops = &nau8540_dai_ops, +}; + +/** + * nau8540_calc_fll_param - Calculate FLL parameters. + * @fll_in: external clock provided to codec. + * @fs: sampling rate. + * @fll_param: Pointer to structure of FLL parameters. + * + * Calculate FLL parameters to configure codec. + * + * Returns 0 for success or negative error code. + */ +static int nau8540_calc_fll_param(unsigned int fll_in, + unsigned int fs, struct nau8540_fll *fll_param) +{ + u64 fvco, fvco_max; + unsigned int fref, i, fvco_sel; + + /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing + * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. + * FREF = freq_in / NAU8540_FLL_REF_DIV_MASK + */ + for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) { + fref = fll_in / fll_pre_scalar[i].param; + if (fref <= NAU_FREF_MAX) + break; + } + if (i == ARRAY_SIZE(fll_pre_scalar)) + return -EINVAL; + fll_param->clk_ref_div = fll_pre_scalar[i].val; + + /* Choose the FLL ratio based on FREF */ + for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) { + if (fref >= fll_ratio[i].param) + break; + } + if (i == ARRAY_SIZE(fll_ratio)) + return -EINVAL; + fll_param->ratio = fll_ratio[i].val; + + /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. + * FDCO must be within the 90MHz - 124MHz or the FFL cannot be + * guaranteed across the full range of operation. + * FDCO = freq_out * 2 * mclk_src_scaling + */ + fvco_max = 0; + fvco_sel = ARRAY_SIZE(mclk_src_scaling); + for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { + fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; + if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && + fvco_max < fvco) { + fvco_max = fvco; + fvco_sel = i; + } + } + if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel) + return -EINVAL; + fll_param->mclk_src = mclk_src_scaling[fvco_sel].val; + + /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional + * input based on FDCO, FREF and FLL ratio. + */ + fvco = div_u64(fvco_max << 16, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> 16) & 0x3FF; + fll_param->fll_frac = fvco & 0xFFFF; + return 0; +} + +static void nau8540_fll_apply(struct regmap *regmap, + struct nau8540_fll *fll_param) +{ + regmap_update_bits(regmap, NAU8540_REG_CLOCK_SRC, + NAU8540_CLK_SRC_MASK | NAU8540_CLK_MCLK_SRC_MASK, + NAU8540_CLK_SRC_MCLK | fll_param->mclk_src); + regmap_update_bits(regmap, NAU8540_REG_FLL1, + NAU8540_FLL_RATIO_MASK | NAU8540_ICTRL_LATCH_MASK, + fll_param->ratio | (0x6 << NAU8540_ICTRL_LATCH_SFT)); + /* FLL 16-bit fractional input */ + regmap_write(regmap, NAU8540_REG_FLL2, fll_param->fll_frac); + /* FLL 10-bit integer input */ + regmap_update_bits(regmap, NAU8540_REG_FLL3, + NAU8540_FLL_INTEGER_MASK, fll_param->fll_int); + /* FLL pre-scaler */ + regmap_update_bits(regmap, NAU8540_REG_FLL4, + NAU8540_FLL_REF_DIV_MASK, + fll_param->clk_ref_div << NAU8540_FLL_REF_DIV_SFT); + regmap_update_bits(regmap, NAU8540_REG_FLL5, + NAU8540_FLL_CLK_SW_MASK, NAU8540_FLL_CLK_SW_REF); + regmap_update_bits(regmap, + NAU8540_REG_FLL6, NAU8540_DCO_EN, 0); + if (fll_param->fll_frac) { + regmap_update_bits(regmap, NAU8540_REG_FLL5, + NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN | + NAU8540_FLL_FTR_SW_MASK, + NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN | + NAU8540_FLL_FTR_SW_FILTER); + regmap_update_bits(regmap, NAU8540_REG_FLL6, + NAU8540_SDM_EN | NAU8540_CUTOFF500, + NAU8540_SDM_EN | NAU8540_CUTOFF500); + } else { + regmap_update_bits(regmap, NAU8540_REG_FLL5, + NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN | + NAU8540_FLL_FTR_SW_MASK, NAU8540_FLL_FTR_SW_ACCU); + regmap_update_bits(regmap, NAU8540_REG_FLL6, + NAU8540_SDM_EN | NAU8540_CUTOFF500, 0); + } +} + +/* freq_out must be 256*Fs in order to achieve the best performance */ +static int nau8540_set_pll(struct snd_soc_component *component, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + struct nau8540_fll fll_param; + int ret, fs; + + switch (pll_id) { + case NAU8540_CLK_FLL_MCLK: + regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3, + NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK, + NAU8540_FLL_CLK_SRC_MCLK | 0); + break; + + case NAU8540_CLK_FLL_BLK: + regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3, + NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK, + NAU8540_FLL_CLK_SRC_BLK | + (0xf << NAU8540_GAIN_ERR_SFT)); + break; + + case NAU8540_CLK_FLL_FS: + regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3, + NAU8540_FLL_CLK_SRC_MASK | NAU8540_GAIN_ERR_MASK, + NAU8540_FLL_CLK_SRC_FS | + (0xf << NAU8540_GAIN_ERR_SFT)); + break; + + default: + dev_err(nau8540->dev, "Invalid clock id (%d)\n", pll_id); + return -EINVAL; + } + dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n", + freq_out, pll_id); + + fs = freq_out / 256; + ret = nau8540_calc_fll_param(freq_in, fs, &fll_param); + if (ret < 0) { + dev_err(nau8540->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + dev_dbg(nau8540->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n", + fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac, + fll_param.fll_int, fll_param.clk_ref_div); + + nau8540_fll_apply(nau8540->regmap, &fll_param); + mdelay(2); + regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC, + NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO); + + return 0; +} + +static int nau8540_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case NAU8540_CLK_DIS: + case NAU8540_CLK_MCLK: + regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC, + NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_MCLK); + regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6, + NAU8540_DCO_EN, 0); + break; + + case NAU8540_CLK_INTERNAL: + regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6, + NAU8540_DCO_EN, NAU8540_DCO_EN); + regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC, + NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO); + break; + + default: + dev_err(nau8540->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static void nau8540_reset_chip(struct regmap *regmap) +{ + regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00); + regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00); +} + +static void nau8540_init_regs(struct nau8540 *nau8540) +{ + struct regmap *regmap = nau8540->regmap; + + /* Enable Bias/VMID/VMID Tieoff */ + regmap_update_bits(regmap, NAU8540_REG_VMID_CTRL, + NAU8540_VMID_EN | NAU8540_VMID_SEL_MASK, + NAU8540_VMID_EN | (0x2 << NAU8540_VMID_SEL_SFT)); + regmap_update_bits(regmap, NAU8540_REG_REFERENCE, + NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN, + NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN); + mdelay(2); + regmap_update_bits(regmap, NAU8540_REG_MIC_BIAS, + NAU8540_PU_PRE, NAU8540_PU_PRE); + regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL, + NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN, + NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN); + /* ADC OSR selection, CLK_ADC = Fs * OSR; + * Channel time alignment enable. + */ + regmap_update_bits(regmap, NAU8540_REG_ADC_SAMPLE_RATE, + NAU8540_CH_SYNC | NAU8540_ADC_OSR_MASK, + NAU8540_CH_SYNC | NAU8540_ADC_OSR_64); + /* PGA input mode selection */ + regmap_update_bits(regmap, NAU8540_REG_FEPGA1, + NAU8540_FEPGA1_MODCH2_SHT | NAU8540_FEPGA1_MODCH1_SHT, + NAU8540_FEPGA1_MODCH2_SHT | NAU8540_FEPGA1_MODCH1_SHT); + regmap_update_bits(regmap, NAU8540_REG_FEPGA2, + NAU8540_FEPGA2_MODCH4_SHT | NAU8540_FEPGA2_MODCH3_SHT, + NAU8540_FEPGA2_MODCH4_SHT | NAU8540_FEPGA2_MODCH3_SHT); + /* DO12 and DO34 pad output disable */ + regmap_update_bits(regmap, NAU8540_REG_PCM_CTRL1, + NAU8540_I2S_DO12_TRI, NAU8540_I2S_DO12_TRI); + regmap_update_bits(regmap, NAU8540_REG_PCM_CTRL2, + NAU8540_I2S_DO34_TRI, NAU8540_I2S_DO34_TRI); +} + +static int __maybe_unused nau8540_suspend(struct snd_soc_component *component) +{ + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(nau8540->regmap, true); + regcache_mark_dirty(nau8540->regmap); + + return 0; +} + +static int __maybe_unused nau8540_resume(struct snd_soc_component *component) +{ + struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(nau8540->regmap, false); + regcache_sync(nau8540->regmap); + + return 0; +} + +static const struct snd_soc_component_driver nau8540_component_driver = { + .set_sysclk = nau8540_set_sysclk, + .set_pll = nau8540_set_pll, + .suspend = nau8540_suspend, + .resume = nau8540_resume, + .controls = nau8540_snd_controls, + .num_controls = ARRAY_SIZE(nau8540_snd_controls), + .dapm_widgets = nau8540_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8540_dapm_widgets), + .dapm_routes = nau8540_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8540_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config nau8540_regmap_config = { + .val_bits = 16, + .reg_bits = 16, + + .max_register = NAU8540_REG_MAX, + .readable_reg = nau8540_readable_reg, + .writeable_reg = nau8540_writeable_reg, + .volatile_reg = nau8540_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8540_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults), +}; + +static int nau8540_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct nau8540 *nau8540 = dev_get_platdata(dev); + int ret, value; + + if (!nau8540) { + nau8540 = devm_kzalloc(dev, sizeof(*nau8540), GFP_KERNEL); + if (!nau8540) + return -ENOMEM; + } + i2c_set_clientdata(i2c, nau8540); + + nau8540->regmap = devm_regmap_init_i2c(i2c, &nau8540_regmap_config); + if (IS_ERR(nau8540->regmap)) + return PTR_ERR(nau8540->regmap); + ret = regmap_read(nau8540->regmap, NAU8540_REG_I2C_DEVICE_ID, &value); + if (ret < 0) { + dev_err(dev, "Failed to read device id from the NAU85L40: %d\n", + ret); + return ret; + } + + nau8540->dev = dev; + nau8540_reset_chip(nau8540->regmap); + nau8540_init_regs(nau8540); + + return devm_snd_soc_register_component(dev, + &nau8540_component_driver, &nau8540_dai, 1); +} + +static const struct i2c_device_id nau8540_i2c_ids[] = { + { "nau8540", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau8540_i2c_ids); + +#ifdef CONFIG_OF +static const struct of_device_id nau8540_of_ids[] = { + { .compatible = "nuvoton,nau8540", }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8540_of_ids); +#endif + +static struct i2c_driver nau8540_i2c_driver = { + .driver = { + .name = "nau8540", + .of_match_table = of_match_ptr(nau8540_of_ids), + }, + .probe = nau8540_i2c_probe, + .id_table = nau8540_i2c_ids, +}; +module_i2c_driver(nau8540_i2c_driver); + +MODULE_DESCRIPTION("ASoC NAU85L40 driver"); +MODULE_AUTHOR("John Hsu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h new file mode 100644 index 0000000000..305ea9207c --- /dev/null +++ b/sound/soc/codecs/nau8540.h @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NAU85L40 ALSA SoC audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * Author: John Hsu + */ + +#ifndef __NAU8540_H__ +#define __NAU8540_H__ + +#define NAU8540_REG_SW_RESET 0x00 +#define NAU8540_REG_POWER_MANAGEMENT 0x01 +#define NAU8540_REG_CLOCK_CTRL 0x02 +#define NAU8540_REG_CLOCK_SRC 0x03 +#define NAU8540_REG_FLL1 0x04 +#define NAU8540_REG_FLL2 0x05 +#define NAU8540_REG_FLL3 0x06 +#define NAU8540_REG_FLL4 0x07 +#define NAU8540_REG_FLL5 0x08 +#define NAU8540_REG_FLL6 0x09 +#define NAU8540_REG_FLL_VCO_RSV 0x0A +#define NAU8540_REG_PCM_CTRL0 0x10 +#define NAU8540_REG_PCM_CTRL1 0x11 +#define NAU8540_REG_PCM_CTRL2 0x12 +#define NAU8540_REG_PCM_CTRL3 0x13 +#define NAU8540_REG_PCM_CTRL4 0x14 +#define NAU8540_REG_ALC_CONTROL_1 0x20 +#define NAU8540_REG_ALC_CONTROL_2 0x21 +#define NAU8540_REG_ALC_CONTROL_3 0x22 +#define NAU8540_REG_ALC_CONTROL_4 0x23 +#define NAU8540_REG_ALC_CONTROL_5 0x24 +#define NAU8540_REG_ALC_GAIN_CH12 0x2D +#define NAU8540_REG_ALC_GAIN_CH34 0x2E +#define NAU8540_REG_ALC_STATUS 0x2F +#define NAU8540_REG_NOTCH_FIL1_CH1 0x30 +#define NAU8540_REG_NOTCH_FIL2_CH1 0x31 +#define NAU8540_REG_NOTCH_FIL1_CH2 0x32 +#define NAU8540_REG_NOTCH_FIL2_CH2 0x33 +#define NAU8540_REG_NOTCH_FIL1_CH3 0x34 +#define NAU8540_REG_NOTCH_FIL2_CH3 0x35 +#define NAU8540_REG_NOTCH_FIL1_CH4 0x36 +#define NAU8540_REG_NOTCH_FIL2_CH4 0x37 +#define NAU8540_REG_HPF_FILTER_CH12 0x38 +#define NAU8540_REG_HPF_FILTER_CH34 0x39 +#define NAU8540_REG_ADC_SAMPLE_RATE 0x3A +#define NAU8540_REG_DIGITAL_GAIN_CH1 0x40 +#define NAU8540_REG_DIGITAL_GAIN_CH2 0x41 +#define NAU8540_REG_DIGITAL_GAIN_CH3 0x42 +#define NAU8540_REG_DIGITAL_GAIN_CH4 0x43 +#define NAU8540_REG_DIGITAL_MUX 0x44 +#define NAU8540_REG_P2P_CH1 0x48 +#define NAU8540_REG_P2P_CH2 0x49 +#define NAU8540_REG_P2P_CH3 0x4A +#define NAU8540_REG_P2P_CH4 0x4B +#define NAU8540_REG_PEAK_CH1 0x4C +#define NAU8540_REG_PEAK_CH2 0x4D +#define NAU8540_REG_PEAK_CH3 0x4E +#define NAU8540_REG_PEAK_CH4 0x4F +#define NAU8540_REG_GPIO_CTRL 0x50 +#define NAU8540_REG_MISC_CTRL 0x51 +#define NAU8540_REG_I2C_CTRL 0x52 +#define NAU8540_REG_I2C_DEVICE_ID 0x58 +#define NAU8540_REG_RST 0x5A +#define NAU8540_REG_VMID_CTRL 0x60 +#define NAU8540_REG_MUTE 0x61 +#define NAU8540_REG_ANALOG_ADC1 0x64 +#define NAU8540_REG_ANALOG_ADC2 0x65 +#define NAU8540_REG_ANALOG_PWR 0x66 +#define NAU8540_REG_MIC_BIAS 0x67 +#define NAU8540_REG_REFERENCE 0x68 +#define NAU8540_REG_FEPGA1 0x69 +#define NAU8540_REG_FEPGA2 0x6A +#define NAU8540_REG_FEPGA3 0x6B +#define NAU8540_REG_FEPGA4 0x6C +#define NAU8540_REG_PWR 0x6D +#define NAU8540_REG_MAX NAU8540_REG_PWR + + +/* POWER_MANAGEMENT (0x01) */ +#define NAU8540_ADC4_EN (0x1 << 3) +#define NAU8540_ADC3_EN (0x1 << 2) +#define NAU8540_ADC2_EN (0x1 << 1) +#define NAU8540_ADC1_EN 0x1 + +/* CLOCK_CTRL (0x02) */ +#define NAU8540_CLK_ADC_EN (0x1 << 15) +#define NAU8540_CLK_I2S_EN (0x1 << 1) + +/* CLOCK_SRC (0x03) */ +#define NAU8540_CLK_SRC_SFT 15 +#define NAU8540_CLK_SRC_MASK (1 << NAU8540_CLK_SRC_SFT) +#define NAU8540_CLK_SRC_VCO (1 << NAU8540_CLK_SRC_SFT) +#define NAU8540_CLK_SRC_MCLK (0 << NAU8540_CLK_SRC_SFT) +#define NAU8540_CLK_ADC_SRC_SFT 6 +#define NAU8540_CLK_ADC_SRC_MASK (0x3 << NAU8540_CLK_ADC_SRC_SFT) +#define NAU8540_CLK_MCLK_SRC_MASK 0xf + +/* FLL1 (0x04) */ +#define NAU8540_ICTRL_LATCH_SFT 10 +#define NAU8540_ICTRL_LATCH_MASK (0x7 << NAU8540_ICTRL_LATCH_SFT) +#define NAU8540_FLL_RATIO_MASK 0x7f + +/* FLL3 (0x06) */ +#define NAU8540_GAIN_ERR_SFT 12 +#define NAU8540_GAIN_ERR_MASK (0xf << NAU8540_GAIN_ERR_SFT) +#define NAU8540_FLL_CLK_SRC_SFT 10 +#define NAU8540_FLL_CLK_SRC_MASK (0x3 << NAU8540_FLL_CLK_SRC_SFT) +#define NAU8540_FLL_CLK_SRC_MCLK (0 << NAU8540_FLL_CLK_SRC_SFT) +#define NAU8540_FLL_CLK_SRC_BLK (0x2 << NAU8540_FLL_CLK_SRC_SFT) +#define NAU8540_FLL_CLK_SRC_FS (0x3 << NAU8540_FLL_CLK_SRC_SFT) +#define NAU8540_FLL_INTEGER_MASK 0x3ff + +/* FLL4 (0x07) */ +#define NAU8540_FLL_REF_DIV_SFT 10 +#define NAU8540_FLL_REF_DIV_MASK (0x3 << NAU8540_FLL_REF_DIV_SFT) + +/* FLL5 (0x08) */ +#define NAU8540_FLL_PDB_DAC_EN (0x1 << 15) +#define NAU8540_FLL_LOOP_FTR_EN (0x1 << 14) +#define NAU8540_FLL_CLK_SW_MASK (0x1 << 13) +#define NAU8540_FLL_CLK_SW_N2 (0x1 << 13) +#define NAU8540_FLL_CLK_SW_REF (0x0 << 13) +#define NAU8540_FLL_FTR_SW_MASK (0x1 << 12) +#define NAU8540_FLL_FTR_SW_ACCU (0x1 << 12) +#define NAU8540_FLL_FTR_SW_FILTER (0x0 << 12) + +/* FLL6 (0x9) */ +#define NAU8540_DCO_EN (0x1 << 15) +#define NAU8540_SDM_EN (0x1 << 14) +#define NAU8540_CUTOFF500 (0x1 << 13) + +/* PCM_CTRL0 (0x10) */ +#define NAU8540_I2S_BP_SFT 7 +#define NAU8540_I2S_BP_INV (0x1 << NAU8540_I2S_BP_SFT) +#define NAU8540_I2S_PCMB_SFT 6 +#define NAU8540_I2S_PCMB_EN (0x1 << NAU8540_I2S_PCMB_SFT) +#define NAU8540_I2S_DL_SFT 2 +#define NAU8540_I2S_DL_MASK (0x3 << NAU8540_I2S_DL_SFT) +#define NAU8540_I2S_DL_16 (0 << NAU8540_I2S_DL_SFT) +#define NAU8540_I2S_DL_20 (0x1 << NAU8540_I2S_DL_SFT) +#define NAU8540_I2S_DL_24 (0x2 << NAU8540_I2S_DL_SFT) +#define NAU8540_I2S_DL_32 (0x3 << NAU8540_I2S_DL_SFT) +#define NAU8540_I2S_DF_MASK 0x3 +#define NAU8540_I2S_DF_RIGTH 0 +#define NAU8540_I2S_DF_LEFT 0x1 +#define NAU8540_I2S_DF_I2S 0x2 +#define NAU8540_I2S_DF_PCM_AB 0x3 + +/* PCM_CTRL1 (0x11) */ +#define NAU8540_I2S_DO12_TRI (0x1 << 15) +#define NAU8540_I2S_LRC_DIV_SFT 12 +#define NAU8540_I2S_LRC_DIV_MASK (0x3 << NAU8540_I2S_LRC_DIV_SFT) +#define NAU8540_I2S_DO12_OE (0x1 << 4) +#define NAU8540_I2S_MS_SFT 3 +#define NAU8540_I2S_MS_MASK (0x1 << NAU8540_I2S_MS_SFT) +#define NAU8540_I2S_MS_MASTER (0x1 << NAU8540_I2S_MS_SFT) +#define NAU8540_I2S_MS_SLAVE (0x0 << NAU8540_I2S_MS_SFT) +#define NAU8540_I2S_BLK_DIV_MASK 0x7 + +/* PCM_CTRL1 (0x12) */ +#define NAU8540_I2S_DO34_TRI (0x1 << 15) +#define NAU8540_I2S_DO34_OE (0x1 << 11) +#define NAU8540_I2S_TSLOT_L_MASK 0x3ff + +/* PCM_CTRL4 (0x14) */ +#define NAU8540_TDM_MODE (0x1 << 15) +#define NAU8540_TDM_OFFSET_EN (0x1 << 14) +#define NAU8540_TDM_TX_MASK 0xf + +/* ADC_SAMPLE_RATE (0x3A) */ +#define NAU8540_CH_SYNC (0x1 << 14) +#define NAU8540_ADC_OSR_MASK 0x3 +#define NAU8540_ADC_OSR_256 0x3 +#define NAU8540_ADC_OSR_128 0x2 +#define NAU8540_ADC_OSR_64 0x1 +#define NAU8540_ADC_OSR_32 0x0 + +/* VMID_CTRL (0x60) */ +#define NAU8540_VMID_EN (1 << 6) +#define NAU8540_VMID_SEL_SFT 4 +#define NAU8540_VMID_SEL_MASK (0x3 << NAU8540_VMID_SEL_SFT) + +/* MIC_BIAS (0x67) */ +#define NAU8540_PU_PRE (0x1 << 8) + +/* REFERENCE (0x68) */ +#define NAU8540_PRECHARGE_DIS (0x1 << 13) +#define NAU8540_GLOBAL_BIAS_EN (0x1 << 12) + +/* FEPGA1 (0x69) */ +#define NAU8540_FEPGA1_MODCH2_SHT_SFT 7 +#define NAU8540_FEPGA1_MODCH2_SHT (0x1 << NAU8540_FEPGA1_MODCH2_SHT_SFT) +#define NAU8540_FEPGA1_MODCH1_SHT_SFT 3 +#define NAU8540_FEPGA1_MODCH1_SHT (0x1 << NAU8540_FEPGA1_MODCH1_SHT_SFT) + +/* FEPGA2 (0x6A) */ +#define NAU8540_FEPGA2_MODCH4_SHT_SFT 7 +#define NAU8540_FEPGA2_MODCH4_SHT (0x1 << NAU8540_FEPGA2_MODCH4_SHT_SFT) +#define NAU8540_FEPGA2_MODCH3_SHT_SFT 3 +#define NAU8540_FEPGA2_MODCH3_SHT (0x1 << NAU8540_FEPGA2_MODCH3_SHT_SFT) + + +/* System Clock Source */ +enum { + NAU8540_CLK_DIS, + NAU8540_CLK_MCLK, + NAU8540_CLK_INTERNAL, + NAU8540_CLK_FLL_MCLK, + NAU8540_CLK_FLL_BLK, + NAU8540_CLK_FLL_FS, +}; + +struct nau8540 { + struct device *dev; + struct regmap *regmap; +}; + +struct nau8540_fll { + int mclk_src; + int ratio; + int fll_frac; + int fll_int; + int clk_ref_div; +}; + +struct nau8540_fll_attr { + unsigned int param; + unsigned int val; +}; + +/* over sampling rate */ +struct nau8540_osr_attr { + unsigned int osr; + unsigned int clk_src; +}; + + +#endif /* __NAU8540_H__ */ diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c new file mode 100644 index 0000000000..47f000cd4d --- /dev/null +++ b/sound/soc/codecs/nau8810.c @@ -0,0 +1,925 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * nau8810.c -- NAU8810 ALSA Soc Audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * + * Author: David Lin + * + * Based on WM8974.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nau8810.h" + +#define NAU_PLL_FREQ_MAX 100000000 +#define NAU_PLL_FREQ_MIN 90000000 +#define NAU_PLL_REF_MAX 33000000 +#define NAU_PLL_REF_MIN 8000000 +#define NAU_PLL_OPTOP_MIN 6 + + +static const int nau8810_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 }; + +static const struct reg_default nau8810_reg_defaults[] = { + { NAU8810_REG_POWER1, 0x0000 }, + { NAU8810_REG_POWER2, 0x0000 }, + { NAU8810_REG_POWER3, 0x0000 }, + { NAU8810_REG_IFACE, 0x0050 }, + { NAU8810_REG_COMP, 0x0000 }, + { NAU8810_REG_CLOCK, 0x0140 }, + { NAU8810_REG_SMPLR, 0x0000 }, + { NAU8810_REG_DAC, 0x0000 }, + { NAU8810_REG_DACGAIN, 0x00FF }, + { NAU8810_REG_ADC, 0x0100 }, + { NAU8810_REG_ADCGAIN, 0x00FF }, + { NAU8810_REG_EQ1, 0x012C }, + { NAU8810_REG_EQ2, 0x002C }, + { NAU8810_REG_EQ3, 0x002C }, + { NAU8810_REG_EQ4, 0x002C }, + { NAU8810_REG_EQ5, 0x002C }, + { NAU8810_REG_DACLIM1, 0x0032 }, + { NAU8810_REG_DACLIM2, 0x0000 }, + { NAU8810_REG_NOTCH1, 0x0000 }, + { NAU8810_REG_NOTCH2, 0x0000 }, + { NAU8810_REG_NOTCH3, 0x0000 }, + { NAU8810_REG_NOTCH4, 0x0000 }, + { NAU8810_REG_ALC1, 0x0038 }, + { NAU8810_REG_ALC2, 0x000B }, + { NAU8810_REG_ALC3, 0x0032 }, + { NAU8810_REG_NOISEGATE, 0x0000 }, + { NAU8810_REG_PLLN, 0x0008 }, + { NAU8810_REG_PLLK1, 0x000C }, + { NAU8810_REG_PLLK2, 0x0093 }, + { NAU8810_REG_PLLK3, 0x00E9 }, + { NAU8810_REG_ATTEN, 0x0000 }, + { NAU8810_REG_INPUT_SIGNAL, 0x0003 }, + { NAU8810_REG_PGAGAIN, 0x0010 }, + { NAU8810_REG_ADCBOOST, 0x0100 }, + { NAU8810_REG_OUTPUT, 0x0002 }, + { NAU8810_REG_SPKMIX, 0x0001 }, + { NAU8810_REG_SPKGAIN, 0x0039 }, + { NAU8810_REG_MONOMIX, 0x0001 }, + { NAU8810_REG_POWER4, 0x0000 }, + { NAU8810_REG_TSLOTCTL1, 0x0000 }, + { NAU8810_REG_TSLOTCTL2, 0x0020 }, + { NAU8810_REG_DEVICE_REVID, 0x0000 }, + { NAU8810_REG_I2C_DEVICEID, 0x001A }, + { NAU8810_REG_ADDITIONID, 0x00CA }, + { NAU8810_REG_RESERVE, 0x0124 }, + { NAU8810_REG_OUTCTL, 0x0001 }, + { NAU8810_REG_ALC1ENHAN1, 0x0010 }, + { NAU8810_REG_ALC1ENHAN2, 0x0000 }, + { NAU8810_REG_MISCCTL, 0x0000 }, + { NAU8810_REG_OUTTIEOFF, 0x0000 }, + { NAU8810_REG_AGCP2POUT, 0x0000 }, + { NAU8810_REG_AGCPOUT, 0x0000 }, + { NAU8810_REG_AMTCTL, 0x0000 }, + { NAU8810_REG_OUTTIEOFFMAN, 0x0000 }, +}; + +static bool nau8810_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8810_REG_RESET ... NAU8810_REG_SMPLR: + case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN: + case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN: + case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5: + case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2: + case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4: + case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN: + case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN: + case NAU8810_REG_ADCBOOST: + case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX: + case NAU8810_REG_SPKGAIN: + case NAU8810_REG_MONOMIX: + case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2: + case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE: + case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2: + case NAU8810_REG_MISCCTL: + case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN: + return true; + default: + return false; + } +} + +static bool nau8810_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8810_REG_RESET ... NAU8810_REG_SMPLR: + case NAU8810_REG_DAC ... NAU8810_REG_DACGAIN: + case NAU8810_REG_ADC ... NAU8810_REG_ADCGAIN: + case NAU8810_REG_EQ1 ... NAU8810_REG_EQ5: + case NAU8810_REG_DACLIM1 ... NAU8810_REG_DACLIM2: + case NAU8810_REG_NOTCH1 ... NAU8810_REG_NOTCH4: + case NAU8810_REG_ALC1 ... NAU8810_REG_ATTEN: + case NAU8810_REG_INPUT_SIGNAL ... NAU8810_REG_PGAGAIN: + case NAU8810_REG_ADCBOOST: + case NAU8810_REG_OUTPUT ... NAU8810_REG_SPKMIX: + case NAU8810_REG_SPKGAIN: + case NAU8810_REG_MONOMIX: + case NAU8810_REG_POWER4 ... NAU8810_REG_TSLOTCTL2: + case NAU8810_REG_OUTCTL ... NAU8810_REG_ALC1ENHAN2: + case NAU8810_REG_MISCCTL: + case NAU8810_REG_OUTTIEOFF ... NAU8810_REG_OUTTIEOFFMAN: + return true; + default: + return false; + } +} + +static bool nau8810_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8810_REG_RESET: + case NAU8810_REG_DEVICE_REVID ... NAU8810_REG_RESERVE: + return true; + default: + return false; + } +} + +/* The EQ parameters get function is to get the 5 band equalizer control. + * The regmap raw read can't work here because regmap doesn't provide + * value format for value width of 9 bits. Therefore, the driver reads data + * from cache and makes value format according to the endianness of + * bytes type control element. + */ +static int nau8810_eq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + int i, reg, reg_val; + u16 *val; + + val = (u16 *)ucontrol->value.bytes.data; + reg = NAU8810_REG_EQ1; + for (i = 0; i < params->max / sizeof(u16); i++) { + regmap_read(nau8810->regmap, reg + i, ®_val); + /* conversion of 16-bit integers between native CPU format + * and big endian format + */ + reg_val = cpu_to_be16(reg_val); + memcpy(val + i, ®_val, sizeof(reg_val)); + } + + return 0; +} + +/* The EQ parameters put function is to make configuration of 5 band equalizer + * control. These configuration includes central frequency, equalizer gain, + * cut-off frequency, bandwidth control, and equalizer path. + * The regmap raw write can't work here because regmap doesn't provide + * register and value format for register with address 7 bits and value 9 bits. + * Therefore, the driver makes value format according to the endianness of + * bytes type control element and writes data to codec. + */ +static int nau8810_eq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + void *data; + u16 *val, value; + int i, reg, ret; + + data = kmemdup(ucontrol->value.bytes.data, + params->max, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + val = (u16 *)data; + reg = NAU8810_REG_EQ1; + for (i = 0; i < params->max / sizeof(u16); i++) { + /* conversion of 16-bit integers between native CPU format + * and big endian format + */ + value = be16_to_cpu(*(val + i)); + ret = regmap_write(nau8810->regmap, reg + i, value); + if (ret) { + dev_err(component->dev, "EQ configuration fail, register: %x ret: %d\n", + reg + i, ret); + kfree(data); + return ret; + } + } + kfree(data); + + return 0; +} + +static const char * const nau8810_companding[] = { + "Off", "NC", "u-law", "A-law" }; + +static const struct soc_enum nau8810_companding_adc_enum = + SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_ADCCM_SFT, + ARRAY_SIZE(nau8810_companding), nau8810_companding); + +static const struct soc_enum nau8810_companding_dac_enum = + SOC_ENUM_SINGLE(NAU8810_REG_COMP, NAU8810_DACCM_SFT, + ARRAY_SIZE(nau8810_companding), nau8810_companding); + +static const char * const nau8810_deemp[] = { + "None", "32kHz", "44.1kHz", "48kHz" }; + +static const struct soc_enum nau8810_deemp_enum = + SOC_ENUM_SINGLE(NAU8810_REG_DAC, NAU8810_DEEMP_SFT, + ARRAY_SIZE(nau8810_deemp), nau8810_deemp); + +static const char * const nau8810_eqmode[] = {"Capture", "Playback" }; + +static const struct soc_enum nau8810_eqmode_enum = + SOC_ENUM_SINGLE(NAU8810_REG_EQ1, NAU8810_EQM_SFT, + ARRAY_SIZE(nau8810_eqmode), nau8810_eqmode); + +static const char * const nau8810_alc[] = {"Normal", "Limiter" }; + +static const struct soc_enum nau8810_alc_enum = + SOC_ENUM_SINGLE(NAU8810_REG_ALC3, NAU8810_ALCM_SFT, + ARRAY_SIZE(nau8810_alc), nau8810_alc); + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); + +static const struct snd_kcontrol_new nau8810_snd_controls[] = { + SOC_ENUM("ADC Companding", nau8810_companding_adc_enum), + SOC_ENUM("DAC Companding", nau8810_companding_dac_enum), + SOC_ENUM("DAC De-emphasis", nau8810_deemp_enum), + + SOC_ENUM("EQ Function", nau8810_eqmode_enum), + SND_SOC_BYTES_EXT("EQ Parameters", 10, + nau8810_eq_get, nau8810_eq_put), + + SOC_SINGLE("DAC Inversion Switch", NAU8810_REG_DAC, + NAU8810_DACPL_SFT, 1, 0), + SOC_SINGLE_TLV("Playback Volume", NAU8810_REG_DACGAIN, + NAU8810_DACGAIN_SFT, 0xff, 0, digital_tlv), + + SOC_SINGLE("High Pass Filter Switch", NAU8810_REG_ADC, + NAU8810_HPFEN_SFT, 1, 0), + SOC_SINGLE("High Pass Cut Off", NAU8810_REG_ADC, + NAU8810_HPF_SFT, 0x7, 0), + + SOC_SINGLE("ADC Inversion Switch", NAU8810_REG_ADC, + NAU8810_ADCPL_SFT, 1, 0), + SOC_SINGLE_TLV("Capture Volume", NAU8810_REG_ADCGAIN, + NAU8810_ADCGAIN_SFT, 0xff, 0, digital_tlv), + + SOC_SINGLE_TLV("EQ1 Volume", NAU8810_REG_EQ1, + NAU8810_EQ1GC_SFT, 0x18, 1, eq_tlv), + SOC_SINGLE_TLV("EQ2 Volume", NAU8810_REG_EQ2, + NAU8810_EQ2GC_SFT, 0x18, 1, eq_tlv), + SOC_SINGLE_TLV("EQ3 Volume", NAU8810_REG_EQ3, + NAU8810_EQ3GC_SFT, 0x18, 1, eq_tlv), + SOC_SINGLE_TLV("EQ4 Volume", NAU8810_REG_EQ4, + NAU8810_EQ4GC_SFT, 0x18, 1, eq_tlv), + SOC_SINGLE_TLV("EQ5 Volume", NAU8810_REG_EQ5, + NAU8810_EQ5GC_SFT, 0x18, 1, eq_tlv), + + SOC_SINGLE("DAC Limiter Switch", NAU8810_REG_DACLIM1, + NAU8810_DACLIMEN_SFT, 1, 0), + SOC_SINGLE("DAC Limiter Decay", NAU8810_REG_DACLIM1, + NAU8810_DACLIMDCY_SFT, 0xf, 0), + SOC_SINGLE("DAC Limiter Attack", NAU8810_REG_DACLIM1, + NAU8810_DACLIMATK_SFT, 0xf, 0), + SOC_SINGLE("DAC Limiter Threshold", NAU8810_REG_DACLIM2, + NAU8810_DACLIMTHL_SFT, 0x7, 0), + SOC_SINGLE("DAC Limiter Boost", NAU8810_REG_DACLIM2, + NAU8810_DACLIMBST_SFT, 0xf, 0), + + SOC_ENUM("ALC Mode", nau8810_alc_enum), + SOC_SINGLE("ALC Enable Switch", NAU8810_REG_ALC1, + NAU8810_ALCEN_SFT, 1, 0), + SOC_SINGLE("ALC Max Volume", NAU8810_REG_ALC1, + NAU8810_ALCMXGAIN_SFT, 0x7, 0), + SOC_SINGLE("ALC Min Volume", NAU8810_REG_ALC1, + NAU8810_ALCMINGAIN_SFT, 0x7, 0), + SOC_SINGLE("ALC ZC Switch", NAU8810_REG_ALC2, + NAU8810_ALCZC_SFT, 1, 0), + SOC_SINGLE("ALC Hold", NAU8810_REG_ALC2, + NAU8810_ALCHT_SFT, 0xf, 0), + SOC_SINGLE("ALC Target", NAU8810_REG_ALC2, + NAU8810_ALCSL_SFT, 0xf, 0), + SOC_SINGLE("ALC Decay", NAU8810_REG_ALC3, + NAU8810_ALCDCY_SFT, 0xf, 0), + SOC_SINGLE("ALC Attack", NAU8810_REG_ALC3, + NAU8810_ALCATK_SFT, 0xf, 0), + SOC_SINGLE("ALC Noise Gate Switch", NAU8810_REG_NOISEGATE, + NAU8810_ALCNEN_SFT, 1, 0), + SOC_SINGLE("ALC Noise Gate Threshold", NAU8810_REG_NOISEGATE, + NAU8810_ALCNTH_SFT, 0x7, 0), + + SOC_SINGLE("PGA ZC Switch", NAU8810_REG_PGAGAIN, + NAU8810_PGAZC_SFT, 1, 0), + SOC_SINGLE_TLV("PGA Volume", NAU8810_REG_PGAGAIN, + NAU8810_PGAGAIN_SFT, 0x3f, 0, inpga_tlv), + + SOC_SINGLE("Speaker ZC Switch", NAU8810_REG_SPKGAIN, + NAU8810_SPKZC_SFT, 1, 0), + SOC_SINGLE("Speaker Mute Switch", NAU8810_REG_SPKGAIN, + NAU8810_SPKMT_SFT, 1, 0), + SOC_SINGLE_TLV("Speaker Volume", NAU8810_REG_SPKGAIN, + NAU8810_SPKGAIN_SFT, 0x3f, 0, spk_tlv), + + SOC_SINGLE("Capture Boost(+20dB)", NAU8810_REG_ADCBOOST, + NAU8810_PGABST_SFT, 1, 0), + SOC_SINGLE("Mono Mute Switch", NAU8810_REG_MONOMIX, + NAU8810_MOUTMXMT_SFT, 1, 0), + + SOC_SINGLE("DAC Oversampling Rate(128x) Switch", NAU8810_REG_DAC, + NAU8810_DACOS_SFT, 1, 0), + SOC_SINGLE("ADC Oversampling Rate(128x) Switch", NAU8810_REG_ADC, + NAU8810_ADCOS_SFT, 1, 0), +}; + +/* Speaker Output Mixer */ +static const struct snd_kcontrol_new nau8810_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("AUX Bypass Switch", NAU8810_REG_SPKMIX, + NAU8810_AUXSPK_SFT, 1, 0), + SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_SPKMIX, + NAU8810_BYPSPK_SFT, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_SPKMIX, + NAU8810_DACSPK_SFT, 1, 0), +}; + +/* Mono Output Mixer */ +static const struct snd_kcontrol_new nau8810_mono_mixer_controls[] = { + SOC_DAPM_SINGLE("AUX Bypass Switch", NAU8810_REG_MONOMIX, + NAU8810_AUXMOUT_SFT, 1, 0), + SOC_DAPM_SINGLE("Line Bypass Switch", NAU8810_REG_MONOMIX, + NAU8810_BYPMOUT_SFT, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", NAU8810_REG_MONOMIX, + NAU8810_DACMOUT_SFT, 1, 0), +}; + +/* PGA Mute */ +static const struct snd_kcontrol_new nau8810_pgaboost_mixer_controls[] = { + SOC_DAPM_SINGLE("AUX PGA Switch", NAU8810_REG_ADCBOOST, + NAU8810_AUXBSTGAIN_SFT, 0x7, 0), + SOC_DAPM_SINGLE("PGA Mute Switch", NAU8810_REG_PGAGAIN, + NAU8810_PGAMT_SFT, 1, 1), + SOC_DAPM_SINGLE("PMIC PGA Switch", NAU8810_REG_ADCBOOST, + NAU8810_PMICBSTGAIN_SFT, 0x7, 0), +}; + +/* Input PGA */ +static const struct snd_kcontrol_new nau8810_inpga[] = { + SOC_DAPM_SINGLE("AUX Switch", NAU8810_REG_INPUT_SIGNAL, + NAU8810_AUXPGA_SFT, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", NAU8810_REG_INPUT_SIGNAL, + NAU8810_NMICPGA_SFT, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", NAU8810_REG_INPUT_SIGNAL, + NAU8810_PMICPGA_SFT, 1, 0), +}; + +/* Loopback Switch */ +static const struct snd_kcontrol_new nau8810_loopback = + SOC_DAPM_SINGLE("Switch", NAU8810_REG_COMP, + NAU8810_ADDAP_SFT, 1, 0); + +static int check_mclk_select_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); + unsigned int value; + + regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &value); + return (value & NAU8810_CLKM_MASK); +} + +static int check_mic_enabled(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); + unsigned int value; + + regmap_read(nau8810->regmap, NAU8810_REG_INPUT_SIGNAL, &value); + if (value & NAU8810_PMICPGA_EN || value & NAU8810_NMICPGA_EN) + return 1; + regmap_read(nau8810->regmap, NAU8810_REG_ADCBOOST, &value); + if (value & NAU8810_PMICBSTGAIN_MASK) + return 1; + return 0; +} + +static const struct snd_soc_dapm_widget nau8810_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Speaker Mixer", NAU8810_REG_POWER3, + NAU8810_SPKMX_EN_SFT, 0, &nau8810_speaker_mixer_controls[0], + ARRAY_SIZE(nau8810_speaker_mixer_controls)), + SND_SOC_DAPM_MIXER("Mono Mixer", NAU8810_REG_POWER3, + NAU8810_MOUTMX_EN_SFT, 0, &nau8810_mono_mixer_controls[0], + ARRAY_SIZE(nau8810_mono_mixer_controls)), + SND_SOC_DAPM_DAC("DAC", "Playback", NAU8810_REG_POWER3, + NAU8810_DAC_EN_SFT, 0), + SND_SOC_DAPM_ADC("ADC", "Capture", NAU8810_REG_POWER2, + NAU8810_ADC_EN_SFT, 0), + SND_SOC_DAPM_PGA("SpkN Out", NAU8810_REG_POWER3, + NAU8810_NSPK_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SpkP Out", NAU8810_REG_POWER3, + NAU8810_PSPK_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono Out", NAU8810_REG_POWER3, + NAU8810_MOUT_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Input PGA", NAU8810_REG_POWER2, + NAU8810_PGA_EN_SFT, 0, nau8810_inpga, + ARRAY_SIZE(nau8810_inpga)), + SND_SOC_DAPM_MIXER("Input Boost Stage", NAU8810_REG_POWER2, + NAU8810_BST_EN_SFT, 0, nau8810_pgaboost_mixer_controls, + ARRAY_SIZE(nau8810_pgaboost_mixer_controls)), + SND_SOC_DAPM_PGA("AUX Input", NAU8810_REG_POWER1, + NAU8810_AUX_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", NAU8810_REG_POWER1, + NAU8810_MICBIAS_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL", NAU8810_REG_POWER1, + NAU8810_PLL_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0, + &nau8810_loopback), + + SND_SOC_DAPM_INPUT("AUX"), + SND_SOC_DAPM_INPUT("MICN"), + SND_SOC_DAPM_INPUT("MICP"), + SND_SOC_DAPM_OUTPUT("MONOOUT"), + SND_SOC_DAPM_OUTPUT("SPKOUTP"), + SND_SOC_DAPM_OUTPUT("SPKOUTN"), +}; + +static const struct snd_soc_dapm_route nau8810_dapm_routes[] = { + {"DAC", NULL, "PLL", check_mclk_select_pll}, + + /* Mono output mixer */ + {"Mono Mixer", "AUX Bypass Switch", "AUX Input"}, + {"Mono Mixer", "PCM Playback Switch", "DAC"}, + {"Mono Mixer", "Line Bypass Switch", "Input Boost Stage"}, + + /* Speaker output mixer */ + {"Speaker Mixer", "AUX Bypass Switch", "AUX Input"}, + {"Speaker Mixer", "PCM Playback Switch", "DAC"}, + {"Speaker Mixer", "Line Bypass Switch", "Input Boost Stage"}, + + /* Outputs */ + {"Mono Out", NULL, "Mono Mixer"}, + {"MONOOUT", NULL, "Mono Out"}, + {"SpkN Out", NULL, "Speaker Mixer"}, + {"SpkP Out", NULL, "Speaker Mixer"}, + {"SPKOUTN", NULL, "SpkN Out"}, + {"SPKOUTP", NULL, "SpkP Out"}, + + /* Input Boost Stage */ + {"ADC", NULL, "Input Boost Stage"}, + {"ADC", NULL, "PLL", check_mclk_select_pll}, + {"Input Boost Stage", "AUX PGA Switch", "AUX Input"}, + {"Input Boost Stage", "PGA Mute Switch", "Input PGA"}, + {"Input Boost Stage", "PMIC PGA Switch", "MICP"}, + + /* Input PGA */ + {"Input PGA", NULL, "Mic Bias", check_mic_enabled}, + {"Input PGA", "AUX Switch", "AUX Input"}, + {"Input PGA", "MicN Switch", "MICN"}, + {"Input PGA", "MicP Switch", "MICP"}, + {"AUX Input", NULL, "AUX"}, + + /* Digital Looptack */ + {"Digital Loopback", "Switch", "ADC"}, + {"DAC", NULL, "Digital Loopback"}, +}; + +static int nau8810_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); + + nau8810->clk_id = clk_id; + nau8810->sysclk = freq; + dev_dbg(nau8810->dev, "master sysclk %dHz, source %s\n", + freq, clk_id == NAU8810_SCLK_PLL ? "PLL" : "MCLK"); + + return 0; +} + +static int nau8810_calc_pll(unsigned int pll_in, + unsigned int fs, struct nau8810_pll *pll_param) +{ + u64 f2, f2_max, pll_ratio; + int i, scal_sel; + + if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN) + return -EINVAL; + + f2_max = 0; + scal_sel = ARRAY_SIZE(nau8810_mclk_scaler); + for (i = 0; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) { + f2 = 256ULL * fs * 4 * nau8810_mclk_scaler[i]; + f2 = div_u64(f2, 10); + if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX && + f2_max < f2) { + f2_max = f2; + scal_sel = i; + } + } + if (ARRAY_SIZE(nau8810_mclk_scaler) == scal_sel) + return -EINVAL; + pll_param->mclk_scaler = scal_sel; + f2 = f2_max; + + /* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional + * input; round up the 24+4bit. + */ + pll_ratio = div_u64(f2 << 28, pll_in); + pll_param->pre_factor = 0; + if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) { + pll_ratio <<= 1; + pll_param->pre_factor = 1; + } + pll_param->pll_int = (pll_ratio >> 28) & 0xF; + pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4); + + return 0; +} + +static int nau8810_set_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = codec_dai->component; + struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); + struct regmap *map = nau8810->regmap; + struct nau8810_pll *pll_param = &nau8810->pll; + int ret, fs; + + fs = freq_out / 256; + ret = nau8810_calc_pll(freq_in, fs, pll_param); + if (ret < 0) { + dev_err(nau8810->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + dev_info(nau8810->dev, "pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n", + pll_param->pll_int, pll_param->pll_frac, pll_param->mclk_scaler, + pll_param->pre_factor); + + regmap_update_bits(map, NAU8810_REG_PLLN, + NAU8810_PLLMCLK_DIV2 | NAU8810_PLLN_MASK, + (pll_param->pre_factor ? NAU8810_PLLMCLK_DIV2 : 0) | + pll_param->pll_int); + regmap_write(map, NAU8810_REG_PLLK1, + (pll_param->pll_frac >> NAU8810_PLLK1_SFT) & + NAU8810_PLLK1_MASK); + regmap_write(map, NAU8810_REG_PLLK2, + (pll_param->pll_frac >> NAU8810_PLLK2_SFT) & + NAU8810_PLLK2_MASK); + regmap_write(map, NAU8810_REG_PLLK3, + pll_param->pll_frac & NAU8810_PLLK3_MASK); + regmap_update_bits(map, NAU8810_REG_CLOCK, NAU8810_MCLKSEL_MASK, + pll_param->mclk_scaler << NAU8810_MCLKSEL_SFT); + regmap_update_bits(map, NAU8810_REG_CLOCK, + NAU8810_CLKM_MASK, NAU8810_CLKM_PLL); + + return 0; +} + +static int nau8810_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); + u16 ctrl1_val = 0, ctrl2_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= NAU8810_CLKIO_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8810_AIFMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8810_AIFMT_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8810_AIFMT_PCM_A; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl1_val |= NAU8810_BCLKP_IB | NAU8810_FSP_IF; + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8810_BCLKP_IB; + break; + case SND_SOC_DAIFMT_NB_IF: + ctrl1_val |= NAU8810_FSP_IF; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8810->regmap, NAU8810_REG_IFACE, + NAU8810_AIFMT_MASK | NAU8810_FSP_IF | + NAU8810_BCLKP_IB, ctrl1_val); + regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK, + NAU8810_CLKIO_MASK, ctrl2_val); + + return 0; +} + +static int nau8810_mclk_clkdiv(struct nau8810 *nau8810, int rate) +{ + int i, sclk, imclk = rate * 256, div = 0; + + if (!nau8810->sysclk) { + dev_err(nau8810->dev, "Make mclk div configuration fail because of invalid system clock\n"); + return -EINVAL; + } + + /* Configure the master clock prescaler div to make system + * clock to approximate the internal master clock (IMCLK); + * and large or equal to IMCLK. + */ + for (i = 1; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) { + sclk = (nau8810->sysclk * 10) / + nau8810_mclk_scaler[i]; + if (sclk < imclk) + break; + div = i; + } + dev_dbg(nau8810->dev, + "master clock prescaler %x for fs %d\n", div, rate); + + /* master clock from MCLK and disable PLL */ + regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK, + NAU8810_MCLKSEL_MASK, (div << NAU8810_MCLKSEL_SFT)); + regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK, + NAU8810_CLKM_MASK, NAU8810_CLKM_MCLK); + + return 0; +} + +static int nau8810_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); + int val_len = 0, val_rate = 0, ret = 0; + unsigned int ctrl_val, bclk_fs, bclk_div; + + /* Select BCLK configuration if the codec as master. */ + regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &ctrl_val); + if (ctrl_val & NAU8810_CLKIO_MASTER) { + /* get the bclk and fs ratio */ + bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params); + if (bclk_fs <= 32) + bclk_div = NAU8810_BCLKDIV_8; + else if (bclk_fs <= 64) + bclk_div = NAU8810_BCLKDIV_4; + else if (bclk_fs <= 128) + bclk_div = NAU8810_BCLKDIV_2; + else + return -EINVAL; + regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK, + NAU8810_BCLKSEL_MASK, bclk_div); + } + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= NAU8810_WLEN_20; + break; + case 24: + val_len |= NAU8810_WLEN_24; + break; + case 32: + val_len |= NAU8810_WLEN_32; + break; + } + + switch (params_rate(params)) { + case 8000: + val_rate |= NAU8810_SMPLR_8K; + break; + case 11025: + val_rate |= NAU8810_SMPLR_12K; + break; + case 16000: + val_rate |= NAU8810_SMPLR_16K; + break; + case 22050: + val_rate |= NAU8810_SMPLR_24K; + break; + case 32000: + val_rate |= NAU8810_SMPLR_32K; + break; + case 44100: + case 48000: + break; + } + + regmap_update_bits(nau8810->regmap, NAU8810_REG_IFACE, + NAU8810_WLEN_MASK, val_len); + regmap_update_bits(nau8810->regmap, NAU8810_REG_SMPLR, + NAU8810_SMPLR_MASK, val_rate); + + /* If the master clock is from MCLK, provide the runtime FS for driver + * to get the master clock prescaler configuration. + */ + if (nau8810->clk_id == NAU8810_SCLK_MCLK) { + ret = nau8810_mclk_clkdiv(nau8810, params_rate(params)); + if (ret < 0) + dev_err(nau8810->dev, "MCLK div configuration fail\n"); + } + + return ret; +} + +static int nau8810_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct nau8810 *nau8810 = snd_soc_component_get_drvdata(component); + struct regmap *map = nau8810->regmap; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(map, NAU8810_REG_POWER1, + NAU8810_REFIMP_MASK, NAU8810_REFIMP_80K); + break; + + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(map, NAU8810_REG_POWER1, + NAU8810_IOBUF_EN | NAU8810_ABIAS_EN, + NAU8810_IOBUF_EN | NAU8810_ABIAS_EN); + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + regcache_sync(map); + regmap_update_bits(map, NAU8810_REG_POWER1, + NAU8810_REFIMP_MASK, NAU8810_REFIMP_3K); + mdelay(100); + } + regmap_update_bits(map, NAU8810_REG_POWER1, + NAU8810_REFIMP_MASK, NAU8810_REFIMP_300K); + break; + + case SND_SOC_BIAS_OFF: + regmap_write(map, NAU8810_REG_POWER1, 0); + regmap_write(map, NAU8810_REG_POWER2, 0); + regmap_write(map, NAU8810_REG_POWER3, 0); + break; + } + + return 0; +} + + +#define NAU8810_RATES (SNDRV_PCM_RATE_8000_48000) + +#define NAU8810_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops nau8810_ops = { + .hw_params = nau8810_pcm_hw_params, + .set_fmt = nau8810_set_dai_fmt, + .set_sysclk = nau8810_set_sysclk, + .set_pll = nau8810_set_pll, +}; + +static struct snd_soc_dai_driver nau8810_dai = { + .name = "nau8810-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, /* Only 1 channel of data */ + .rates = NAU8810_RATES, + .formats = NAU8810_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, /* Only 1 channel of data */ + .rates = NAU8810_RATES, + .formats = NAU8810_FORMATS, + }, + .ops = &nau8810_ops, + .symmetric_rate = 1, +}; + +static const struct regmap_config nau8810_regmap_config = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = NAU8810_REG_MAX, + .readable_reg = nau8810_readable_reg, + .writeable_reg = nau8810_writeable_reg, + .volatile_reg = nau8810_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8810_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8810_reg_defaults), +}; + +static const struct snd_soc_component_driver nau8810_component_driver = { + .set_bias_level = nau8810_set_bias_level, + .controls = nau8810_snd_controls, + .num_controls = ARRAY_SIZE(nau8810_snd_controls), + .dapm_widgets = nau8810_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8810_dapm_widgets), + .dapm_routes = nau8810_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8810_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int nau8810_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct nau8810 *nau8810 = dev_get_platdata(dev); + + if (!nau8810) { + nau8810 = devm_kzalloc(dev, sizeof(*nau8810), GFP_KERNEL); + if (!nau8810) + return -ENOMEM; + } + i2c_set_clientdata(i2c, nau8810); + + nau8810->regmap = devm_regmap_init_i2c(i2c, &nau8810_regmap_config); + if (IS_ERR(nau8810->regmap)) + return PTR_ERR(nau8810->regmap); + nau8810->dev = dev; + + regmap_write(nau8810->regmap, NAU8810_REG_RESET, 0x00); + + return devm_snd_soc_register_component(dev, + &nau8810_component_driver, &nau8810_dai, 1); +} + +static const struct i2c_device_id nau8810_i2c_id[] = { + { "nau8810", 0 }, + { "nau8812", 0 }, + { "nau8814", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id nau8810_of_match[] = { + { .compatible = "nuvoton,nau8810", }, + { .compatible = "nuvoton,nau8812", }, + { .compatible = "nuvoton,nau8814", }, + { } +}; +MODULE_DEVICE_TABLE(of, nau8810_of_match); +#endif + +static struct i2c_driver nau8810_i2c_driver = { + .driver = { + .name = "nau8810", + .of_match_table = of_match_ptr(nau8810_of_match), + }, + .probe = nau8810_i2c_probe, + .id_table = nau8810_i2c_id, +}; + +module_i2c_driver(nau8810_i2c_driver); + +MODULE_DESCRIPTION("ASoC NAU8810 driver"); +MODULE_AUTHOR("David Lin "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/nau8810.h b/sound/soc/codecs/nau8810.h new file mode 100644 index 0000000000..6a7cacbe04 --- /dev/null +++ b/sound/soc/codecs/nau8810.h @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NAU8810 ALSA SoC audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * Author: David Lin + */ + +#ifndef __NAU8810_H__ +#define __NAU8810_H__ + +#define NAU8810_REG_RESET 0x00 +#define NAU8810_REG_POWER1 0x01 +#define NAU8810_REG_POWER2 0x02 +#define NAU8810_REG_POWER3 0x03 +#define NAU8810_REG_IFACE 0x04 +#define NAU8810_REG_COMP 0x05 +#define NAU8810_REG_CLOCK 0x06 +#define NAU8810_REG_SMPLR 0x07 +#define NAU8810_REG_DAC 0x0A +#define NAU8810_REG_DACGAIN 0x0B +#define NAU8810_REG_ADC 0x0E +#define NAU8810_REG_ADCGAIN 0x0F +#define NAU8810_REG_EQ1 0x12 +#define NAU8810_REG_EQ2 0x13 +#define NAU8810_REG_EQ3 0x14 +#define NAU8810_REG_EQ4 0x15 +#define NAU8810_REG_EQ5 0x16 +#define NAU8810_REG_DACLIM1 0x18 +#define NAU8810_REG_DACLIM2 0x19 +#define NAU8810_REG_NOTCH1 0x1B +#define NAU8810_REG_NOTCH2 0x1C +#define NAU8810_REG_NOTCH3 0x1D +#define NAU8810_REG_NOTCH4 0x1E +#define NAU8810_REG_ALC1 0x20 +#define NAU8810_REG_ALC2 0x21 +#define NAU8810_REG_ALC3 0x22 +#define NAU8810_REG_NOISEGATE 0x23 +#define NAU8810_REG_PLLN 0x24 +#define NAU8810_REG_PLLK1 0x25 +#define NAU8810_REG_PLLK2 0x26 +#define NAU8810_REG_PLLK3 0x27 +#define NAU8810_REG_ATTEN 0x28 +#define NAU8810_REG_INPUT_SIGNAL 0x2C +#define NAU8810_REG_PGAGAIN 0x2D +#define NAU8810_REG_ADCBOOST 0x2F +#define NAU8810_REG_OUTPUT 0x31 +#define NAU8810_REG_SPKMIX 0x32 +#define NAU8810_REG_SPKGAIN 0x36 +#define NAU8810_REG_MONOMIX 0x38 +#define NAU8810_REG_POWER4 0x3A +#define NAU8810_REG_TSLOTCTL1 0x3B +#define NAU8810_REG_TSLOTCTL2 0x3C +#define NAU8810_REG_DEVICE_REVID 0x3E +#define NAU8810_REG_I2C_DEVICEID 0x3F +#define NAU8810_REG_ADDITIONID 0x40 +#define NAU8810_REG_RESERVE 0x41 +#define NAU8810_REG_OUTCTL 0x45 +#define NAU8810_REG_ALC1ENHAN1 0x46 +#define NAU8810_REG_ALC1ENHAN2 0x47 +#define NAU8810_REG_MISCCTL 0x49 +#define NAU8810_REG_OUTTIEOFF 0x4B +#define NAU8810_REG_AGCP2POUT 0x4C +#define NAU8810_REG_AGCPOUT 0x4D +#define NAU8810_REG_AMTCTL 0x4E +#define NAU8810_REG_OUTTIEOFFMAN 0x4F +#define NAU8810_REG_MAX NAU8810_REG_OUTTIEOFFMAN + + +/* NAU8810_REG_POWER1 (0x1) */ +#define NAU8810_DCBUF_EN (0x1 << 8) +#define NAU8810_AUX_EN_SFT 6 +#define NAU8810_PLL_EN_SFT 5 +#define NAU8810_MICBIAS_EN_SFT 4 +#define NAU8810_ABIAS_EN (0x1 << 3) +#define NAU8810_IOBUF_EN (0x1 << 2) +#define NAU8810_REFIMP_MASK 0x3 +#define NAU8810_REFIMP_DIS 0x0 +#define NAU8810_REFIMP_80K 0x1 +#define NAU8810_REFIMP_300K 0x2 +#define NAU8810_REFIMP_3K 0x3 + +/* NAU8810_REG_POWER2 (0x2) */ +#define NAU8810_BST_EN_SFT 4 +#define NAU8810_PGA_EN_SFT 2 +#define NAU8810_ADC_EN_SFT 0 + +/* NAU8810_REG_POWER3 (0x3) */ +#define NAU8810_DAC_EN_SFT 0 +#define NAU8810_SPKMX_EN_SFT 2 +#define NAU8810_MOUTMX_EN_SFT 3 +#define NAU8810_PSPK_EN_SFT 5 +#define NAU8810_NSPK_EN_SFT 6 +#define NAU8810_MOUT_EN_SFT 7 + +/* NAU8810_REG_IFACE (0x4) */ +#define NAU8810_AIFMT_SFT 3 +#define NAU8810_AIFMT_MASK (0x3 << NAU8810_AIFMT_SFT) +#define NAU8810_AIFMT_RIGHT (0x0 << NAU8810_AIFMT_SFT) +#define NAU8810_AIFMT_LEFT (0x1 << NAU8810_AIFMT_SFT) +#define NAU8810_AIFMT_I2S (0x2 << NAU8810_AIFMT_SFT) +#define NAU8810_AIFMT_PCM_A (0x3 << NAU8810_AIFMT_SFT) +#define NAU8810_WLEN_SFT 5 +#define NAU8810_WLEN_MASK (0x3 << NAU8810_WLEN_SFT) +#define NAU8810_WLEN_16 (0x0 << NAU8810_WLEN_SFT) +#define NAU8810_WLEN_20 (0x1 << NAU8810_WLEN_SFT) +#define NAU8810_WLEN_24 (0x2 << NAU8810_WLEN_SFT) +#define NAU8810_WLEN_32 (0x3 << NAU8810_WLEN_SFT) +#define NAU8810_FSP_IF (0x1 << 7) +#define NAU8810_BCLKP_IB (0x1 << 8) + +/* NAU8810_REG_COMP (0x5) */ +#define NAU8810_ADDAP_SFT 0 +#define NAU8810_ADCCM_SFT 1 +#define NAU8810_DACCM_SFT 3 + +/* NAU8810_REG_CLOCK (0x6) */ +#define NAU8810_CLKIO_MASK 0x1 +#define NAU8810_CLKIO_SLAVE 0x0 +#define NAU8810_CLKIO_MASTER 0x1 +#define NAU8810_BCLKSEL_SFT 2 +#define NAU8810_BCLKSEL_MASK (0x7 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_1 (0x0 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_2 (0x1 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_4 (0x2 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_8 (0x3 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_16 (0x4 << NAU8810_BCLKSEL_SFT) +#define NAU8810_BCLKDIV_32 (0x5 << NAU8810_BCLKSEL_SFT) +#define NAU8810_MCLKSEL_SFT 5 +#define NAU8810_MCLKSEL_MASK (0x7 << NAU8810_MCLKSEL_SFT) +#define NAU8810_CLKM_SFT 8 +#define NAU8810_CLKM_MASK (0x1 << NAU8810_CLKM_SFT) +#define NAU8810_CLKM_MCLK (0x0 << NAU8810_CLKM_SFT) +#define NAU8810_CLKM_PLL (0x1 << NAU8810_CLKM_SFT) + +/* NAU8810_REG_SMPLR (0x7) */ +#define NAU8810_SMPLR_SFT 1 +#define NAU8810_SMPLR_MASK (0x7 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_48K (0x0 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_32K (0x1 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_24K (0x2 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_16K (0x3 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_12K (0x4 << NAU8810_SMPLR_SFT) +#define NAU8810_SMPLR_8K (0x5 << NAU8810_SMPLR_SFT) + +/* NAU8810_REG_DAC (0xA) */ +#define NAU8810_DACPL_SFT 0 +#define NAU8810_DACOS_SFT 3 +#define NAU8810_DEEMP_SFT 4 + +/* NAU8810_REG_DACGAIN (0xB) */ +#define NAU8810_DACGAIN_SFT 0 + +/* NAU8810_REG_ADC (0xE) */ +#define NAU8810_ADCPL_SFT 0 +#define NAU8810_ADCOS_SFT 3 +#define NAU8810_HPF_SFT 4 +#define NAU8810_HPFEN_SFT 8 + +/* NAU8810_REG_ADCGAIN (0xF) */ +#define NAU8810_ADCGAIN_SFT 0 + +/* NAU8810_REG_EQ1 (0x12) */ +#define NAU8810_EQ1GC_SFT 0 +#define NAU8810_EQ1CF_SFT 5 +#define NAU8810_EQM_SFT 8 + +/* NAU8810_REG_EQ2 (0x13) */ +#define NAU8810_EQ2GC_SFT 0 +#define NAU8810_EQ2CF_SFT 5 +#define NAU8810_EQ2BW_SFT 8 + +/* NAU8810_REG_EQ3 (0x14) */ +#define NAU8810_EQ3GC_SFT 0 +#define NAU8810_EQ3CF_SFT 5 +#define NAU8810_EQ3BW_SFT 8 + +/* NAU8810_REG_EQ4 (0x15) */ +#define NAU8810_EQ4GC_SFT 0 +#define NAU8810_EQ4CF_SFT 5 +#define NAU8810_EQ4BW_SFT 8 + +/* NAU8810_REG_EQ5 (0x16) */ +#define NAU8810_EQ5GC_SFT 0 +#define NAU8810_EQ5CF_SFT 5 + +/* NAU8810_REG_DACLIM1 (0x18) */ +#define NAU8810_DACLIMATK_SFT 0 +#define NAU8810_DACLIMDCY_SFT 4 +#define NAU8810_DACLIMEN_SFT 8 + +/* NAU8810_REG_DACLIM2 (0x19) */ +#define NAU8810_DACLIMBST_SFT 0 +#define NAU8810_DACLIMTHL_SFT 4 + +/* NAU8810_REG_ALC1 (0x20) */ +#define NAU8810_ALCMINGAIN_SFT 0 +#define NAU8810_ALCMXGAIN_SFT 3 +#define NAU8810_ALCEN_SFT 8 + +/* NAU8810_REG_ALC2 (0x21) */ +#define NAU8810_ALCSL_SFT 0 +#define NAU8810_ALCHT_SFT 4 +#define NAU8810_ALCZC_SFT 8 + +/* NAU8810_REG_ALC3 (0x22) */ +#define NAU8810_ALCATK_SFT 0 +#define NAU8810_ALCDCY_SFT 4 +#define NAU8810_ALCM_SFT 8 + +/* NAU8810_REG_NOISEGATE (0x23) */ +#define NAU8810_ALCNTH_SFT 0 +#define NAU8810_ALCNEN_SFT 3 + +/* NAU8810_REG_PLLN (0x24) */ +#define NAU8810_PLLN_MASK 0xF +#define NAU8810_PLLMCLK_DIV2 (0x1 << 4) + +/* NAU8810_REG_PLLK1 (0x25) */ +#define NAU8810_PLLK1_SFT 18 +#define NAU8810_PLLK1_MASK 0x3F + +/* NAU8810_REG_PLLK2 (0x26) */ +#define NAU8810_PLLK2_SFT 9 +#define NAU8810_PLLK2_MASK 0x1FF + +/* NAU8810_REG_PLLK3 (0x27) */ +#define NAU8810_PLLK3_MASK 0x1FF + +/* NAU8810_REG_INPUT_SIGNAL (0x2C) */ +#define NAU8810_PMICPGA_SFT 0 +#define NAU8810_PMICPGA_EN (0x1 << NAU8810_PMICPGA_SFT) +#define NAU8810_NMICPGA_SFT 1 +#define NAU8810_NMICPGA_EN (0x1 << NAU8810_NMICPGA_SFT) +#define NAU8810_AUXPGA_SFT 2 + +/* NAU8810_REG_PGAGAIN (0x2D) */ +#define NAU8810_PGAGAIN_SFT 0 +#define NAU8810_PGAMT_SFT 6 +#define NAU8810_PGAZC_SFT 7 + +/* NAU8810_REG_ADCBOOST (0x2F) */ +#define NAU8810_AUXBSTGAIN_SFT 0 +#define NAU8810_PMICBSTGAIN_SFT 4 +#define NAU8810_PMICBSTGAIN_MASK (0x7 << NAU8810_PMICBSTGAIN_SFT) +#define NAU8810_PGABST_SFT 8 + +/* NAU8810_REG_SPKMIX (0x32) */ +#define NAU8810_DACSPK_SFT 0 +#define NAU8810_BYPSPK_SFT 1 +#define NAU8810_AUXSPK_SFT 5 + +/* NAU8810_REG_SPKGAIN (0x36) */ +#define NAU8810_SPKGAIN_SFT 0 +#define NAU8810_SPKMT_SFT 6 +#define NAU8810_SPKZC_SFT 7 + +/* NAU8810_REG_MONOMIX (0x38) */ +#define NAU8810_DACMOUT_SFT 0 +#define NAU8810_BYPMOUT_SFT 1 +#define NAU8810_AUXMOUT_SFT 2 +#define NAU8810_MOUTMXMT_SFT 6 + + +/* System Clock Source */ +enum { + NAU8810_SCLK_MCLK, + NAU8810_SCLK_PLL, +}; + +struct nau8810_pll { + int pre_factor; + int mclk_scaler; + int pll_frac; + int pll_int; +}; + +struct nau8810 { + struct device *dev; + struct regmap *regmap; + struct nau8810_pll pll; + int sysclk; + int clk_id; +}; + +#endif diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c new file mode 100644 index 0000000000..f307374834 --- /dev/null +++ b/sound/soc/codecs/nau8821.c @@ -0,0 +1,1951 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// nau8821.c -- Nuvoton NAU88L21 audio codec driver +// +// Copyright 2021 Nuvoton Technology Corp. +// Author: John Hsu +// Co-author: Seven Lee +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nau8821.h" + +#define NAU8821_JD_ACTIVE_HIGH BIT(0) + +static int nau8821_quirk; +static int quirk_override = -1; +module_param_named(quirk, quirk_override, uint, 0444); +MODULE_PARM_DESC(quirk, "Board-specific quirk override"); + +#define NAU_FREF_MAX 13500000 +#define NAU_FVCO_MAX 100000000 +#define NAU_FVCO_MIN 90000000 + +#define NAU8821_BUTTON SND_JACK_BTN_0 + +/* the maximum frequency of CLK_ADC and CLK_DAC */ +#define CLK_DA_AD_MAX 6144000 + +static int nau8821_configure_sysclk(struct nau8821 *nau8821, + int clk_id, unsigned int freq); +static bool nau8821_is_jack_inserted(struct regmap *regmap); + +struct nau8821_fll { + int mclk_src; + int ratio; + int fll_frac; + int fll_int; + int clk_ref_div; +}; + +struct nau8821_fll_attr { + unsigned int param; + unsigned int val; +}; + +/* scaling for mclk from sysclk_src output */ +static const struct nau8821_fll_attr mclk_src_scaling[] = { + { 1, 0x0 }, + { 2, 0x2 }, + { 4, 0x3 }, + { 8, 0x4 }, + { 16, 0x5 }, + { 32, 0x6 }, + { 3, 0x7 }, + { 6, 0xa }, + { 12, 0xb }, + { 24, 0xc }, + { 48, 0xd }, + { 96, 0xe }, + { 5, 0xf }, +}; + +/* ratio for input clk freq */ +static const struct nau8821_fll_attr fll_ratio[] = { + { 512000, 0x01 }, + { 256000, 0x02 }, + { 128000, 0x04 }, + { 64000, 0x08 }, + { 32000, 0x10 }, + { 8000, 0x20 }, + { 4000, 0x40 }, +}; + +static const struct nau8821_fll_attr fll_pre_scalar[] = { + { 0, 0x0 }, + { 1, 0x1 }, + { 2, 0x2 }, + { 3, 0x3 }, +}; + +/* over sampling rate */ +struct nau8821_osr_attr { + unsigned int osr; + unsigned int clk_src; +}; + +static const struct nau8821_osr_attr osr_dac_sel[] = { + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 256, 0 }, /* OSR 256, SRC 1 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 0, 0 }, + { 32, 3 }, /* OSR 32, SRC 1/8 */ +}; + +static const struct nau8821_osr_attr osr_adc_sel[] = { + { 32, 3 }, /* OSR 32, SRC 1/8 */ + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 256, 0 }, /* OSR 256, SRC 1 */ +}; + +struct nau8821_dmic_speed { + unsigned int param; + unsigned int val; +}; + +static const struct nau8821_dmic_speed dmic_speed_sel[] = { + { 0, 0x0 }, /*SPEED 1, SRC 1 */ + { 1, 0x1 }, /*SPEED 2, SRC 1/2 */ + { 2, 0x2 }, /*SPEED 4, SRC 1/4 */ + { 3, 0x3 }, /*SPEED 8, SRC 1/8 */ +}; + +static const struct reg_default nau8821_reg_defaults[] = { + { NAU8821_R01_ENA_CTRL, 0x00ff }, + { NAU8821_R03_CLK_DIVIDER, 0x0050 }, + { NAU8821_R04_FLL1, 0x0 }, + { NAU8821_R05_FLL2, 0x00bc }, + { NAU8821_R06_FLL3, 0x0008 }, + { NAU8821_R07_FLL4, 0x0010 }, + { NAU8821_R08_FLL5, 0x4000 }, + { NAU8821_R09_FLL6, 0x6900 }, + { NAU8821_R0A_FLL7, 0x0031 }, + { NAU8821_R0B_FLL8, 0x26e9 }, + { NAU8821_R0D_JACK_DET_CTRL, 0x0 }, + { NAU8821_R0F_INTERRUPT_MASK, 0x0 }, + { NAU8821_R12_INTERRUPT_DIS_CTRL, 0xffff }, + { NAU8821_R13_DMIC_CTRL, 0x0 }, + { NAU8821_R1A_GPIO12_CTRL, 0x0 }, + { NAU8821_R1B_TDM_CTRL, 0x0 }, + { NAU8821_R1C_I2S_PCM_CTRL1, 0x000a }, + { NAU8821_R1D_I2S_PCM_CTRL2, 0x8010 }, + { NAU8821_R1E_LEFT_TIME_SLOT, 0x0 }, + { NAU8821_R1F_RIGHT_TIME_SLOT, 0x0 }, + { NAU8821_R21_BIQ0_COF1, 0x0 }, + { NAU8821_R22_BIQ0_COF2, 0x0 }, + { NAU8821_R23_BIQ0_COF3, 0x0 }, + { NAU8821_R24_BIQ0_COF4, 0x0 }, + { NAU8821_R25_BIQ0_COF5, 0x0 }, + { NAU8821_R26_BIQ0_COF6, 0x0 }, + { NAU8821_R27_BIQ0_COF7, 0x0 }, + { NAU8821_R28_BIQ0_COF8, 0x0 }, + { NAU8821_R29_BIQ0_COF9, 0x0 }, + { NAU8821_R2A_BIQ0_COF10, 0x0 }, + { NAU8821_R2B_ADC_RATE, 0x0002 }, + { NAU8821_R2C_DAC_CTRL1, 0x0082 }, + { NAU8821_R2D_DAC_CTRL2, 0x0 }, + { NAU8821_R2F_DAC_DGAIN_CTRL, 0x0 }, + { NAU8821_R30_ADC_DGAIN_CTRL, 0x0 }, + { NAU8821_R31_MUTE_CTRL, 0x0 }, + { NAU8821_R32_HSVOL_CTRL, 0x0 }, + { NAU8821_R34_DACR_CTRL, 0xcfcf }, + { NAU8821_R35_ADC_DGAIN_CTRL1, 0xcfcf }, + { NAU8821_R36_ADC_DRC_KNEE_IP12, 0x1486 }, + { NAU8821_R37_ADC_DRC_KNEE_IP34, 0x0f12 }, + { NAU8821_R38_ADC_DRC_SLOPES, 0x25ff }, + { NAU8821_R39_ADC_DRC_ATKDCY, 0x3457 }, + { NAU8821_R3A_DAC_DRC_KNEE_IP12, 0x1486 }, + { NAU8821_R3B_DAC_DRC_KNEE_IP34, 0x0f12 }, + { NAU8821_R3C_DAC_DRC_SLOPES, 0x25f9 }, + { NAU8821_R3D_DAC_DRC_ATKDCY, 0x3457 }, + { NAU8821_R41_BIQ1_COF1, 0x0 }, + { NAU8821_R42_BIQ1_COF2, 0x0 }, + { NAU8821_R43_BIQ1_COF3, 0x0 }, + { NAU8821_R44_BIQ1_COF4, 0x0 }, + { NAU8821_R45_BIQ1_COF5, 0x0 }, + { NAU8821_R46_BIQ1_COF6, 0x0 }, + { NAU8821_R47_BIQ1_COF7, 0x0 }, + { NAU8821_R48_BIQ1_COF8, 0x0 }, + { NAU8821_R49_BIQ1_COF9, 0x0 }, + { NAU8821_R4A_BIQ1_COF10, 0x0 }, + { NAU8821_R4B_CLASSG_CTRL, 0x0 }, + { NAU8821_R4C_IMM_MODE_CTRL, 0x0 }, + { NAU8821_R4D_IMM_RMS_L, 0x0 }, + { NAU8821_R53_OTPDOUT_1, 0xaad8 }, + { NAU8821_R54_OTPDOUT_2, 0x0002 }, + { NAU8821_R55_MISC_CTRL, 0x0 }, + { NAU8821_R66_BIAS_ADJ, 0x0 }, + { NAU8821_R68_TRIM_SETTINGS, 0x0 }, + { NAU8821_R69_ANALOG_CONTROL_1, 0x0 }, + { NAU8821_R6A_ANALOG_CONTROL_2, 0x0 }, + { NAU8821_R6B_PGA_MUTE, 0x0 }, + { NAU8821_R71_ANALOG_ADC_1, 0x0011 }, + { NAU8821_R72_ANALOG_ADC_2, 0x0020 }, + { NAU8821_R73_RDAC, 0x0008 }, + { NAU8821_R74_MIC_BIAS, 0x0006 }, + { NAU8821_R76_BOOST, 0x0 }, + { NAU8821_R77_FEPGA, 0x0 }, + { NAU8821_R7E_PGA_GAIN, 0x0 }, + { NAU8821_R7F_POWER_UP_CONTROL, 0x0 }, + { NAU8821_R80_CHARGE_PUMP, 0x0 }, +}; + +static bool nau8821_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8821_R00_RESET ... NAU8821_R01_ENA_CTRL: + case NAU8821_R03_CLK_DIVIDER ... NAU8821_R0B_FLL8: + case NAU8821_R0D_JACK_DET_CTRL: + case NAU8821_R0F_INTERRUPT_MASK ... NAU8821_R13_DMIC_CTRL: + case NAU8821_R1A_GPIO12_CTRL ... NAU8821_R1F_RIGHT_TIME_SLOT: + case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2D_DAC_CTRL2: + case NAU8821_R2F_DAC_DGAIN_CTRL ... NAU8821_R32_HSVOL_CTRL: + case NAU8821_R34_DACR_CTRL ... NAU8821_R3D_DAC_DRC_ATKDCY: + case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4F_FUSE_CTRL3: + case NAU8821_R51_FUSE_CTRL1: + case NAU8821_R53_OTPDOUT_1 ... NAU8821_R55_MISC_CTRL: + case NAU8821_R58_I2C_DEVICE_ID ... NAU8821_R5A_SOFTWARE_RST: + case NAU8821_R66_BIAS_ADJ: + case NAU8821_R68_TRIM_SETTINGS ... NAU8821_R6B_PGA_MUTE: + case NAU8821_R71_ANALOG_ADC_1 ... NAU8821_R74_MIC_BIAS: + case NAU8821_R76_BOOST ... NAU8821_R77_FEPGA: + case NAU8821_R7E_PGA_GAIN ... NAU8821_R82_GENERAL_STATUS: + return true; + default: + return false; + } +} + +static bool nau8821_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8821_R00_RESET ... NAU8821_R01_ENA_CTRL: + case NAU8821_R03_CLK_DIVIDER ... NAU8821_R0B_FLL8: + case NAU8821_R0D_JACK_DET_CTRL: + case NAU8821_R0F_INTERRUPT_MASK: + case NAU8821_R11_INT_CLR_KEY_STATUS ... NAU8821_R13_DMIC_CTRL: + case NAU8821_R1A_GPIO12_CTRL ... NAU8821_R1F_RIGHT_TIME_SLOT: + case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2D_DAC_CTRL2: + case NAU8821_R2F_DAC_DGAIN_CTRL ... NAU8821_R32_HSVOL_CTRL: + case NAU8821_R34_DACR_CTRL ... NAU8821_R3D_DAC_DRC_ATKDCY: + case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4C_IMM_MODE_CTRL: + case NAU8821_R4E_FUSE_CTRL2 ... NAU8821_R4F_FUSE_CTRL3: + case NAU8821_R51_FUSE_CTRL1: + case NAU8821_R55_MISC_CTRL: + case NAU8821_R5A_SOFTWARE_RST: + case NAU8821_R66_BIAS_ADJ: + case NAU8821_R68_TRIM_SETTINGS ... NAU8821_R6B_PGA_MUTE: + case NAU8821_R71_ANALOG_ADC_1 ... NAU8821_R74_MIC_BIAS: + case NAU8821_R76_BOOST ... NAU8821_R77_FEPGA: + case NAU8821_R7E_PGA_GAIN ... NAU8821_R80_CHARGE_PUMP: + return true; + default: + return false; + } +} + +static bool nau8821_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8821_R00_RESET: + case NAU8821_R10_IRQ_STATUS ... NAU8821_R11_INT_CLR_KEY_STATUS: + case NAU8821_R21_BIQ0_COF1 ... NAU8821_R2A_BIQ0_COF10: + case NAU8821_R41_BIQ1_COF1 ... NAU8821_R4A_BIQ1_COF10: + case NAU8821_R4D_IMM_RMS_L: + case NAU8821_R53_OTPDOUT_1 ... NAU8821_R54_OTPDOUT_2: + case NAU8821_R58_I2C_DEVICE_ID ... NAU8821_R5A_SOFTWARE_RST: + case NAU8821_R81_CHARGE_PUMP_INPUT_READ ... NAU8821_R82_GENERAL_STATUS: + return true; + default: + return false; + } +} + +static int nau8821_biq_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + + if (!component->regmap) + return -EINVAL; + + regmap_raw_read(component->regmap, NAU8821_R21_BIQ0_COF1, + ucontrol->value.bytes.data, params->max); + + return 0; +} + +static int nau8821_biq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + void *data; + + if (!component->regmap) + return -EINVAL; + + data = kmemdup(ucontrol->value.bytes.data, + params->max, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + regmap_raw_write(component->regmap, NAU8821_R21_BIQ0_COF1, + data, params->max); + + kfree(data); + + return 0; +} + +static const char * const nau8821_adc_decimation[] = { + "32", "64", "128", "256" }; + +static const struct soc_enum nau8821_adc_decimation_enum = + SOC_ENUM_SINGLE(NAU8821_R2B_ADC_RATE, NAU8821_ADC_SYNC_DOWN_SFT, + ARRAY_SIZE(nau8821_adc_decimation), nau8821_adc_decimation); + +static const char * const nau8821_dac_oversampl[] = { + "64", "256", "128", "", "32" }; + +static const struct soc_enum nau8821_dac_oversampl_enum = + SOC_ENUM_SINGLE(NAU8821_R2C_DAC_CTRL1, NAU8821_DAC_OVERSAMPLE_SFT, + ARRAY_SIZE(nau8821_dac_oversampl), nau8821_dac_oversampl); + +static const char * const nau8821_adc_drc_noise_gate[] = { + "1:1", "2:1", "4:1", "8:1" }; + +static const struct soc_enum nau8821_adc_drc_noise_gate_enum = + SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, NAU8821_DRC_NG_SLP_ADC_SFT, + ARRAY_SIZE(nau8821_adc_drc_noise_gate), + nau8821_adc_drc_noise_gate); + +static const char * const nau8821_adc_drc_expansion_slope[] = { + "1:1", "2:1", "4:1" }; + +static const struct soc_enum nau8821_adc_drc_expansion_slope_enum = + SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, NAU8821_DRC_EXP_SLP_ADC_SFT, + ARRAY_SIZE(nau8821_adc_drc_expansion_slope), + nau8821_adc_drc_expansion_slope); + +static const char * const nau8821_adc_drc_lower_region[] = { + "0", "1:2", "1:4", "1:8", "1:16", "", "", "1:1" }; + +static const struct soc_enum nau8821_adc_drc_lower_region_enum = + SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, + NAU8821_DRC_CMP2_SLP_ADC_SFT, + ARRAY_SIZE(nau8821_adc_drc_lower_region), + nau8821_adc_drc_lower_region); + +static const char * const nau8821_higher_region[] = { + "0", "1:2", "1:4", "1:8", "1:16", "", "", "1:1" }; + +static const struct soc_enum nau8821_higher_region_enum = + SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, + NAU8821_DRC_CMP1_SLP_ADC_SFT, + ARRAY_SIZE(nau8821_higher_region), + nau8821_higher_region); + +static const char * const nau8821_limiter_slope[] = { + "0", "1:2", "1:4", "1:8", "1:16", "1:32", "1:64", "1:1" }; + +static const struct soc_enum nau8821_limiter_slope_enum = + SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, + NAU8821_DRC_LMT_SLP_ADC_SFT, ARRAY_SIZE(nau8821_limiter_slope), + nau8821_limiter_slope); + +static const char * const nau8821_detection_attack_time[] = { + "Ts", "3Ts", "7Ts", "15Ts", "31Ts", "63Ts", "127Ts", "255Ts", + "", "511Ts" }; + +static const struct soc_enum nau8821_detection_attack_time_enum = + SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, + NAU8821_DRC_PK_COEF1_ADC_SFT, + ARRAY_SIZE(nau8821_detection_attack_time), + nau8821_detection_attack_time); + +static const char * const nau8821_detection_release_time[] = { + "63Ts", "127Ts", "255Ts", "511Ts", "1023Ts", "2047Ts", "4095Ts", + "8191Ts", "", "16383Ts" }; + +static const struct soc_enum nau8821_detection_release_time_enum = + SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, + NAU8821_DRC_PK_COEF2_ADC_SFT, + ARRAY_SIZE(nau8821_detection_release_time), + nau8821_detection_release_time); + +static const char * const nau8821_attack_time[] = { + "Ts", "3Ts", "7Ts", "15Ts", "31Ts", "63Ts", "127Ts", "255Ts", + "511Ts", "1023Ts", "2047Ts", "4095Ts", "8191Ts" }; + +static const struct soc_enum nau8821_attack_time_enum = + SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, NAU8821_DRC_ATK_ADC_SFT, + ARRAY_SIZE(nau8821_attack_time), nau8821_attack_time); + +static const char * const nau8821_decay_time[] = { + "63Ts", "127Ts", "255Ts", "511Ts", "1023Ts", "2047Ts", "4095Ts", + "8191Ts", "16383Ts", "32757Ts", "65535Ts" }; + +static const struct soc_enum nau8821_decay_time_enum = + SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, NAU8821_DRC_DCY_ADC_SFT, + ARRAY_SIZE(nau8821_decay_time), nau8821_decay_time); + +static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -6600, 2400); +static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0); +static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -900, 0); +static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -6600, 50, 1); +static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600); +static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -7000, 2400); +static const DECLARE_TLV_DB_MINMAX(drc_knee4_tlv, -9800, -3500); +static const DECLARE_TLV_DB_MINMAX(drc_knee3_tlv, -8100, -1800); + +static const struct snd_kcontrol_new nau8821_controls[] = { + SOC_DOUBLE_TLV("Mic Volume", NAU8821_R35_ADC_DGAIN_CTRL1, + NAU8821_ADCL_CH_VOL_SFT, NAU8821_ADCR_CH_VOL_SFT, + 0xff, 0, adc_vol_tlv), + SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8821_R30_ADC_DGAIN_CTRL, + 12, 8, 0x0f, 0, sidetone_vol_tlv), + SOC_DOUBLE_TLV("Headphone Volume", NAU8821_R32_HSVOL_CTRL, + NAU8821_HPL_VOL_SFT, NAU8821_HPR_VOL_SFT, 0x3, 1, hp_vol_tlv), + SOC_DOUBLE_TLV("Digital Playback Volume", NAU8821_R34_DACR_CTRL, + NAU8821_DACL_CH_VOL_SFT, NAU8821_DACR_CH_VOL_SFT, + 0xcf, 0, playback_vol_tlv), + SOC_DOUBLE_TLV("Frontend PGA Volume", NAU8821_R7E_PGA_GAIN, + NAU8821_PGA_GAIN_L_SFT, NAU8821_PGA_GAIN_R_SFT, + 37, 0, fepga_gain_tlv), + SOC_DOUBLE_TLV("Headphone Crosstalk Volume", + NAU8821_R2F_DAC_DGAIN_CTRL, + 0, 8, 0xff, 0, crosstalk_vol_tlv), + SOC_SINGLE_TLV("ADC DRC KNEE4", NAU8821_R37_ADC_DRC_KNEE_IP34, + NAU8821_DRC_KNEE4_IP_ADC_SFT, 0x3f, 1, drc_knee4_tlv), + SOC_SINGLE_TLV("ADC DRC KNEE3", NAU8821_R37_ADC_DRC_KNEE_IP34, + NAU8821_DRC_KNEE3_IP_ADC_SFT, 0x3f, 1, drc_knee3_tlv), + + SOC_ENUM("ADC DRC Noise Gate", nau8821_adc_drc_noise_gate_enum), + SOC_ENUM("ADC DRC Expansion Slope", nau8821_adc_drc_expansion_slope_enum), + SOC_ENUM("ADC DRC Lower Region", nau8821_adc_drc_lower_region_enum), + SOC_ENUM("ADC DRC Higher Region", nau8821_higher_region_enum), + SOC_ENUM("ADC DRC Limiter Slope", nau8821_limiter_slope_enum), + SOC_ENUM("ADC DRC Peak Detection Attack Time", nau8821_detection_attack_time_enum), + SOC_ENUM("ADC DRC Peak Detection Release Time", nau8821_detection_release_time_enum), + SOC_ENUM("ADC DRC Attack Time", nau8821_attack_time_enum), + SOC_ENUM("ADC DRC Decay Time", nau8821_decay_time_enum), + SOC_SINGLE("DRC Enable Switch", NAU8821_R36_ADC_DRC_KNEE_IP12, + NAU8821_DRC_ENA_ADC_SFT, 1, 0), + + SOC_ENUM("ADC Decimation Rate", nau8821_adc_decimation_enum), + SOC_ENUM("DAC Oversampling Rate", nau8821_dac_oversampl_enum), + SND_SOC_BYTES_EXT("BIQ Coefficients", 20, + nau8821_biq_coeff_get, nau8821_biq_coeff_put), + SOC_SINGLE("ADC Phase Switch", NAU8821_R1B_TDM_CTRL, + NAU8821_ADCPHS_SFT, 1, 0), +}; + +static const struct snd_kcontrol_new nau8821_dmic_mode_switch = + SOC_DAPM_SINGLE("Switch", NAU8821_R13_DMIC_CTRL, + NAU8821_DMIC_EN_SFT, 1, 0); + +static int dmic_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + int i, speed_selection = -1, clk_adc_src, clk_adc; + unsigned int clk_divider_r03; + + /* The DMIC clock is gotten from adc clock divided by + * CLK_DMIC_SRC (1, 2, 4, 8). The clock has to be equal or + * less than nau8821->dmic_clk_threshold. + */ + regmap_read(nau8821->regmap, NAU8821_R03_CLK_DIVIDER, + &clk_divider_r03); + clk_adc_src = (clk_divider_r03 & NAU8821_CLK_ADC_SRC_MASK) + >> NAU8821_CLK_ADC_SRC_SFT; + clk_adc = (nau8821->fs * 256) >> clk_adc_src; + + for (i = 0 ; i < 4 ; i++) + if ((clk_adc >> dmic_speed_sel[i].param) <= + nau8821->dmic_clk_threshold) { + speed_selection = dmic_speed_sel[i].val; + break; + } + if (i == 4) + return -EINVAL; + + dev_dbg(nau8821->dev, + "clk_adc=%d, dmic_clk_threshold = %d, param=%d, val = %d\n", + clk_adc, nau8821->dmic_clk_threshold, + dmic_speed_sel[i].param, dmic_speed_sel[i].val); + regmap_update_bits(nau8821->regmap, NAU8821_R13_DMIC_CTRL, + NAU8821_DMIC_SRC_MASK, + (speed_selection << NAU8821_DMIC_SRC_SFT)); + + return 0; +} + +static int nau8821_left_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(125); + regmap_update_bits(nau8821->regmap, NAU8821_R01_ENA_CTRL, + NAU8821_EN_ADCL, NAU8821_EN_ADCL); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8821->regmap, + NAU8821_R01_ENA_CTRL, NAU8821_EN_ADCL, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8821_right_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(125); + regmap_update_bits(nau8821->regmap, NAU8821_R01_ENA_CTRL, + NAU8821_EN_ADCR, NAU8821_EN_ADCR); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8821->regmap, + NAU8821_R01_ENA_CTRL, NAU8821_EN_ADCR, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8821_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct nau8821 *nau8821 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Prevent startup click by letting charge pump to ramp up */ + msleep(20); + regmap_update_bits(nau8821->regmap, NAU8821_R80_CHARGE_PUMP, + NAU8821_JAMNODCLOW, NAU8821_JAMNODCLOW); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(nau8821->regmap, NAU8821_R80_CHARGE_PUMP, + NAU8821_JAMNODCLOW, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8821_output_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disables the TESTDAC to let DAC signal pass through. */ + regmap_update_bits(nau8821->regmap, NAU8821_R66_BIAS_ADJ, + NAU8821_BIAS_TESTDAC_EN, 0); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8821->regmap, NAU8821_R66_BIAS_ADJ, + NAU8821_BIAS_TESTDAC_EN, NAU8821_BIAS_TESTDAC_EN); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int system_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + dev_dbg(nau8821->dev, "system clock control : POWER OFF\n"); + /* Set clock source to disable or internal clock before the + * playback or capture end. Codec needs clock for Jack + * detection and button press if jack inserted; otherwise, + * the clock should be closed. + */ + if (nau8821_is_jack_inserted(nau8821->regmap)) { + nau8821_configure_sysclk(nau8821, + NAU8821_CLK_INTERNAL, 0); + } else { + nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0); + } + } + return 0; +} + +static int nau8821_left_fepga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + if (!nau8821->left_input_single_end) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA, + NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK, + NAU8821_ACDC_VREF_MICN | NAU8821_FEPGA_MODEL_AAF); + regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST, + NAU8821_HP_BOOST_DISCHRG_EN, NAU8821_HP_BOOST_DISCHRG_EN); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA, + NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK, 0); + regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST, + NAU8821_HP_BOOST_DISCHRG_EN, 0); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget nau8821_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0, + system_clock_control, SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MICBIAS", NAU8821_R74_MIC_BIAS, + NAU8821_MICBIAS_POWERUP_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC Clock", SND_SOC_NOPM, 0, 0, + dmic_clock_control, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_ADC("ADCL Power", NULL, NAU8821_R72_ANALOG_ADC_2, + NAU8821_POWERUP_ADCL_SFT, 0), + SND_SOC_DAPM_ADC("ADCR Power", NULL, NAU8821_R72_ANALOG_ADC_2, + NAU8821_POWERUP_ADCR_SFT, 0), + /* single-ended design only on the left */ + SND_SOC_DAPM_PGA_S("Frontend PGA L", 1, NAU8821_R7F_POWER_UP_CONTROL, + NAU8821_PUP_PGA_L_SFT, 0, nau8821_left_fepga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("Frontend PGA R", 1, NAU8821_R7F_POWER_UP_CONTROL, + NAU8821_PUP_PGA_R_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADCL Digital path", 0, NAU8821_R01_ENA_CTRL, + NAU8821_EN_ADCL_SFT, 0, nau8821_left_adc_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("ADCR Digital path", 0, NAU8821_R01_ENA_CTRL, + NAU8821_EN_ADCR_SFT, 0, nau8821_right_adc_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SWITCH("DMIC Enable", SND_SOC_NOPM, + 0, 0, &nau8821_dmic_mode_switch), + SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8821_R1D_I2S_PCM_CTRL2, + NAU8821_I2S_TRISTATE_SFT, 1), + SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8821_R73_RDAC, + NAU8821_DACL_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8821_R73_RDAC, + NAU8821_DACR_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8821_R73_RDAC, + NAU8821_DACL_CLK_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8821_R73_RDAC, + NAU8821_DACR_CLK_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DDACR", NULL, NAU8821_R01_ENA_CTRL, + NAU8821_EN_DACR_SFT, 0), + SND_SOC_DAPM_DAC("DDACL", NULL, NAU8821_R01_ENA_CTRL, + NAU8821_EN_DACL_SFT, 0), + SND_SOC_DAPM_PGA_S("HP amp L", 0, NAU8821_R4B_CLASSG_CTRL, + NAU8821_CLASSG_LDAC_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("HP amp R", 0, NAU8821_R4B_CLASSG_CTRL, + NAU8821_CLASSG_RDAC_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8821_R80_CHARGE_PUMP, + NAU8821_CHANRGE_PUMP_EN_SFT, 0, nau8821_pump_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4, + NAU8821_R7F_POWER_UP_CONTROL, + NAU8821_PUP_INTEG_R_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4, + NAU8821_R7F_POWER_UP_CONTROL, + NAU8821_PUP_INTEG_L_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5, + NAU8821_R7F_POWER_UP_CONTROL, + NAU8821_PUP_DRV_INSTG_R_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5, + NAU8821_R7F_POWER_UP_CONTROL, + NAU8821_PUP_DRV_INSTG_L_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6, + NAU8821_R7F_POWER_UP_CONTROL, + NAU8821_PUP_MAIN_DRV_R_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6, + NAU8821_R7F_POWER_UP_CONTROL, + NAU8821_PUP_MAIN_DRV_L_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output DACL", 7, + NAU8821_R80_CHARGE_PUMP, NAU8821_POWER_DOWN_DACL_SFT, + 0, nau8821_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("Output DACR", 7, + NAU8821_R80_CHARGE_PUMP, NAU8821_POWER_DOWN_DACR_SFT, + 0, nau8821_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */ + SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8, + NAU8821_R0D_JACK_DET_CTRL, + NAU8821_SPKR_DWN1L_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8, + NAU8821_R0D_JACK_DET_CTRL, + NAU8821_SPKR_DWN1R_SFT, 0, NULL, 0), + + /* High current HPOL/R boost driver */ + SND_SOC_DAPM_PGA_S("HP Boost Driver", 9, + NAU8821_R76_BOOST, NAU8821_HP_BOOST_DIS_SFT, 1, NULL, 0), + SND_SOC_DAPM_PGA("Class G", NAU8821_R4B_CLASSG_CTRL, + NAU8821_CLASSG_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("MICR"), + SND_SOC_DAPM_INPUT("DMIC"), + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route nau8821_dapm_routes[] = { + {"DMIC Enable", "Switch", "DMIC"}, + {"DMIC Enable", NULL, "DMIC Clock"}, + + {"Frontend PGA L", NULL, "MICL"}, + {"Frontend PGA R", NULL, "MICR"}, + {"Frontend PGA L", NULL, "MICBIAS"}, + {"Frontend PGA R", NULL, "MICBIAS"}, + + {"ADCL Power", NULL, "Frontend PGA L"}, + {"ADCR Power", NULL, "Frontend PGA R"}, + + {"ADCL Digital path", NULL, "ADCL Power"}, + {"ADCR Digital path", NULL, "ADCR Power"}, + {"ADCL Digital path", NULL, "DMIC Enable"}, + {"ADCR Digital path", NULL, "DMIC Enable"}, + + {"AIFTX", NULL, "ADCL Digital path"}, + {"AIFTX", NULL, "ADCR Digital path"}, + + {"AIFTX", NULL, "System Clock"}, + {"AIFRX", NULL, "System Clock"}, + + {"DDACL", NULL, "AIFRX"}, + {"DDACR", NULL, "AIFRX"}, + + {"HP amp L", NULL, "DDACL"}, + {"HP amp R", NULL, "DDACR"}, + + {"Charge Pump", NULL, "HP amp L"}, + {"Charge Pump", NULL, "HP amp R"}, + + {"ADACL", NULL, "Charge Pump"}, + {"ADACR", NULL, "Charge Pump"}, + {"ADACL Clock", NULL, "ADACL"}, + {"ADACR Clock", NULL, "ADACR"}, + + {"Output Driver L Stage 1", NULL, "ADACL Clock"}, + {"Output Driver R Stage 1", NULL, "ADACR Clock"}, + {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"}, + {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"}, + {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"}, + {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"}, + {"Output DACL", NULL, "Output Driver L Stage 3"}, + {"Output DACR", NULL, "Output Driver R Stage 3"}, + + {"HPOL Pulldown", NULL, "Output DACL"}, + {"HPOR Pulldown", NULL, "Output DACR"}, + {"HP Boost Driver", NULL, "HPOL Pulldown"}, + {"HP Boost Driver", NULL, "HPOR Pulldown"}, + + {"Class G", NULL, "HP Boost Driver"}, + {"HPOL", NULL, "Class G"}, + {"HPOR", NULL, "Class G"}, +}; + +static const struct nau8821_osr_attr * +nau8821_get_osr(struct nau8821 *nau8821, int stream) +{ + unsigned int osr; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_read(nau8821->regmap, NAU8821_R2C_DAC_CTRL1, &osr); + osr &= NAU8821_DAC_OVERSAMPLE_MASK; + if (osr >= ARRAY_SIZE(osr_dac_sel)) + return NULL; + return &osr_dac_sel[osr]; + } else { + regmap_read(nau8821->regmap, NAU8821_R2B_ADC_RATE, &osr); + osr &= NAU8821_ADC_SYNC_DOWN_MASK; + if (osr >= ARRAY_SIZE(osr_adc_sel)) + return NULL; + return &osr_adc_sel[osr]; + } +} + +static int nau8821_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + const struct nau8821_osr_attr *osr; + + osr = nau8821_get_osr(nau8821, substream->stream); + if (!osr || !osr->osr) + return -EINVAL; + + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + 0, CLK_DA_AD_MAX / osr->osr); +} + +static int nau8821_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, ctrl_val, bclk_fs, clk_div; + const struct nau8821_osr_attr *osr; + + nau8821->fs = params_rate(params); + /* CLK_DAC or CLK_ADC = OSR * FS + * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR) + * multiplied by the audio sample rate (Fs). Note that the OSR and Fs + * values must be selected such that the maximum frequency is less + * than 6.144 MHz. + */ + osr = nau8821_get_osr(nau8821, substream->stream); + if (!osr || !osr->osr) + return -EINVAL; + if (nau8821->fs * osr->osr > CLK_DA_AD_MAX) + return -EINVAL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER, + NAU8821_CLK_DAC_SRC_MASK, + osr->clk_src << NAU8821_CLK_DAC_SRC_SFT); + else + regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER, + NAU8821_CLK_ADC_SRC_MASK, + osr->clk_src << NAU8821_CLK_ADC_SRC_SFT); + + /* make BCLK and LRC divde configuration if the codec as master. */ + regmap_read(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2, &ctrl_val); + if (ctrl_val & NAU8821_I2S_MS_MASTER) { + /* get the bclk and fs ratio */ + bclk_fs = snd_soc_params_to_bclk(params) / nau8821->fs; + if (bclk_fs <= 32) + clk_div = 3; + else if (bclk_fs <= 64) + clk_div = 2; + else if (bclk_fs <= 128) + clk_div = 1; + else { + return -EINVAL; + } + regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2, + NAU8821_I2S_LRC_DIV_MASK | NAU8821_I2S_BLK_DIV_MASK, + (clk_div << NAU8821_I2S_LRC_DIV_SFT) | clk_div); + } + + switch (params_width(params)) { + case 16: + val_len |= NAU8821_I2S_DL_16; + break; + case 20: + val_len |= NAU8821_I2S_DL_20; + break; + case 24: + val_len |= NAU8821_I2S_DL_24; + break; + case 32: + val_len |= NAU8821_I2S_DL_32; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8821->regmap, NAU8821_R1C_I2S_PCM_CTRL1, + NAU8821_I2S_DL_MASK, val_len); + + return 0; +} + +static int nau8821_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + unsigned int ctrl1_val = 0, ctrl2_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + ctrl2_val |= NAU8821_I2S_MS_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8821_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8821_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8821_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1_val |= NAU8821_I2S_DF_RIGTH; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8821_I2S_DF_PCM_AB; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1_val |= NAU8821_I2S_DF_PCM_AB; + ctrl1_val |= NAU8821_I2S_PCMB_EN; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8821->regmap, NAU8821_R1C_I2S_PCM_CTRL1, + NAU8821_I2S_DL_MASK | NAU8821_I2S_DF_MASK | + NAU8821_I2S_BP_MASK | NAU8821_I2S_PCMB_MASK, ctrl1_val); + regmap_update_bits(nau8821->regmap, NAU8821_R1D_I2S_PCM_CTRL2, + NAU8821_I2S_MS_MASK, ctrl2_val); + + return 0; +} + +static int nau8821_digital_mute(struct snd_soc_dai *dai, int mute, + int direction) +{ + struct snd_soc_component *component = dai->component; + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + unsigned int val = 0; + + if (mute) + val = NAU8821_DAC_SOFT_MUTE; + + return regmap_update_bits(nau8821->regmap, + NAU8821_R31_MUTE_CTRL, NAU8821_DAC_SOFT_MUTE, val); +} + +static const struct snd_soc_dai_ops nau8821_dai_ops = { + .startup = nau8821_dai_startup, + .hw_params = nau8821_hw_params, + .set_fmt = nau8821_set_dai_fmt, + .mute_stream = nau8821_digital_mute, + .no_capture_mute = 1, +}; + +#define NAU8821_RATES SNDRV_PCM_RATE_8000_192000 +#define NAU8821_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver nau8821_dai = { + .name = NUVOTON_CODEC_DAI, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8821_RATES, + .formats = NAU8821_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8821_RATES, + .formats = NAU8821_FORMATS, + }, + .ops = &nau8821_dai_ops, +}; + + +static bool nau8821_is_jack_inserted(struct regmap *regmap) +{ + bool active_high, is_high; + int status, jkdet; + + regmap_read(regmap, NAU8821_R0D_JACK_DET_CTRL, &jkdet); + active_high = jkdet & NAU8821_JACK_POLARITY; + regmap_read(regmap, NAU8821_R82_GENERAL_STATUS, &status); + is_high = status & NAU8821_GPIO2_IN; + /* return jack connection status according to jack insertion logic + * active high or active low. + */ + return active_high == is_high; +} + +static void nau8821_int_status_clear_all(struct regmap *regmap) +{ + int active_irq, clear_irq, i; + + /* Reset the intrruption status from rightmost bit if the corres- + * ponding irq event occurs. + */ + regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq); + for (i = 0; i < NAU8821_REG_DATA_LEN; i++) { + clear_irq = (0x1 << i); + if (active_irq & clear_irq) + regmap_write(regmap, + NAU8821_R11_INT_CLR_KEY_STATUS, clear_irq); + } +} + +static void nau8821_eject_jack(struct nau8821 *nau8821) +{ + struct snd_soc_dapm_context *dapm = nau8821->dapm; + struct regmap *regmap = nau8821->regmap; + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + + /* Detach 2kOhm Resistors from MICBIAS to MICGND */ + regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS, + NAU8821_MICBIAS_JKR2, 0); + /* HPL/HPR short to ground */ + regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL, + NAU8821_SPKR_DWN1R | NAU8821_SPKR_DWN1L, 0); + snd_soc_component_disable_pin(component, "MICBIAS"); + snd_soc_dapm_sync(dapm); + + /* Clear all interruption status */ + nau8821_int_status_clear_all(regmap); + + /* Enable the insertion interruption, disable the ejection inter- + * ruption, and then bypass de-bounce circuit. + */ + regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL, + NAU8821_IRQ_EJECT_DIS | NAU8821_IRQ_INSERT_DIS, + NAU8821_IRQ_EJECT_DIS); + /* Mask unneeded IRQs: 1 - disable, 0 - enable */ + regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, + NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN, + NAU8821_IRQ_EJECT_EN); + + regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL, + NAU8821_JACK_DET_DB_BYPASS, NAU8821_JACK_DET_DB_BYPASS); + + /* Close clock for jack type detection at manual mode */ + if (dapm->bias_level < SND_SOC_BIAS_PREPARE) + nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0); + + /* Recover to normal channel input */ + regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE, + NAU8821_ADC_R_SRC_EN, 0); + if (nau8821->key_enable) { + regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, + NAU8821_IRQ_KEY_RELEASE_EN | + NAU8821_IRQ_KEY_PRESS_EN, + NAU8821_IRQ_KEY_RELEASE_EN | + NAU8821_IRQ_KEY_PRESS_EN); + regmap_update_bits(regmap, + NAU8821_R12_INTERRUPT_DIS_CTRL, + NAU8821_IRQ_KEY_RELEASE_DIS | + NAU8821_IRQ_KEY_PRESS_DIS, + NAU8821_IRQ_KEY_RELEASE_DIS | + NAU8821_IRQ_KEY_PRESS_DIS); + } + +} + +static void nau8821_jdet_work(struct work_struct *work) +{ + struct nau8821 *nau8821 = + container_of(work, struct nau8821, jdet_work); + struct snd_soc_dapm_context *dapm = nau8821->dapm; + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + struct regmap *regmap = nau8821->regmap; + int jack_status_reg, mic_detected, event = 0, event_mask = 0; + + snd_soc_component_force_enable_pin(component, "MICBIAS"); + snd_soc_dapm_sync(dapm); + msleep(20); + + regmap_read(regmap, NAU8821_R58_I2C_DEVICE_ID, &jack_status_reg); + mic_detected = !(jack_status_reg & NAU8821_KEYDET); + if (mic_detected) { + dev_dbg(nau8821->dev, "Headset connected\n"); + event |= SND_JACK_HEADSET; + + /* 2kOhm Resistor from MICBIAS to MICGND1 */ + regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS, + NAU8821_MICBIAS_JKR2, NAU8821_MICBIAS_JKR2); + /* Latch Right Channel Analog data + * input into the Right Channel Filter + */ + regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE, + NAU8821_ADC_R_SRC_EN, NAU8821_ADC_R_SRC_EN); + if (nau8821->key_enable) { + regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, + NAU8821_IRQ_KEY_RELEASE_EN | + NAU8821_IRQ_KEY_PRESS_EN, 0); + regmap_update_bits(regmap, + NAU8821_R12_INTERRUPT_DIS_CTRL, + NAU8821_IRQ_KEY_RELEASE_DIS | + NAU8821_IRQ_KEY_PRESS_DIS, 0); + } + } else { + dev_dbg(nau8821->dev, "Headphone connected\n"); + event |= SND_JACK_HEADPHONE; + snd_soc_component_disable_pin(component, "MICBIAS"); + snd_soc_dapm_sync(dapm); + } + event_mask |= SND_JACK_HEADSET; + snd_soc_jack_report(nau8821->jack, event, event_mask); +} + +/* Enable interruptions with internal clock. */ +static void nau8821_setup_inserted_irq(struct nau8821 *nau8821) +{ + struct regmap *regmap = nau8821->regmap; + + /* Enable internal VCO needed for interruptions */ + if (nau8821->dapm->bias_level < SND_SOC_BIAS_PREPARE) + nau8821_configure_sysclk(nau8821, NAU8821_CLK_INTERNAL, 0); + + /* Chip needs one FSCLK cycle in order to generate interruptions, + * as we cannot guarantee one will be provided by the system. Turning + * master mode on then off enables us to generate that FSCLK cycle + * with a minimum of contention on the clock bus. + */ + regmap_update_bits(regmap, NAU8821_R1D_I2S_PCM_CTRL2, + NAU8821_I2S_MS_MASK, NAU8821_I2S_MS_MASTER); + regmap_update_bits(regmap, NAU8821_R1D_I2S_PCM_CTRL2, + NAU8821_I2S_MS_MASK, NAU8821_I2S_MS_SLAVE); + + /* Not bypass de-bounce circuit */ + regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL, + NAU8821_JACK_DET_DB_BYPASS, 0); + + regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, + NAU8821_IRQ_EJECT_EN, 0); + regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL, + NAU8821_IRQ_EJECT_DIS, 0); +} + +static irqreturn_t nau8821_interrupt(int irq, void *data) +{ + struct nau8821 *nau8821 = (struct nau8821 *)data; + struct regmap *regmap = nau8821->regmap; + int active_irq, clear_irq = 0, event = 0, event_mask = 0; + + if (regmap_read(regmap, NAU8821_R10_IRQ_STATUS, &active_irq)) { + dev_err(nau8821->dev, "failed to read irq status\n"); + return IRQ_NONE; + } + + dev_dbg(nau8821->dev, "IRQ %d\n", active_irq); + + if ((active_irq & NAU8821_JACK_EJECT_IRQ_MASK) == + NAU8821_JACK_EJECT_DETECTED) { + regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1, + NAU8821_MICDET_MASK, NAU8821_MICDET_DIS); + nau8821_eject_jack(nau8821); + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8821_JACK_EJECT_IRQ_MASK; + } else if (active_irq & NAU8821_KEY_SHORT_PRESS_IRQ) { + event |= NAU8821_BUTTON; + event_mask |= NAU8821_BUTTON; + clear_irq = NAU8821_KEY_SHORT_PRESS_IRQ; + } else if (active_irq & NAU8821_KEY_RELEASE_IRQ) { + event_mask = NAU8821_BUTTON; + clear_irq = NAU8821_KEY_RELEASE_IRQ; + } else if ((active_irq & NAU8821_JACK_INSERT_IRQ_MASK) == + NAU8821_JACK_INSERT_DETECTED) { + regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1, + NAU8821_MICDET_MASK, NAU8821_MICDET_EN); + if (nau8821_is_jack_inserted(regmap)) { + /* detect microphone and jack type */ + cancel_work_sync(&nau8821->jdet_work); + schedule_work(&nau8821->jdet_work); + /* Turn off insertion interruption at manual mode */ + regmap_update_bits(regmap, + NAU8821_R12_INTERRUPT_DIS_CTRL, + NAU8821_IRQ_INSERT_DIS, + NAU8821_IRQ_INSERT_DIS); + regmap_update_bits(regmap, + NAU8821_R0F_INTERRUPT_MASK, + NAU8821_IRQ_INSERT_EN, + NAU8821_IRQ_INSERT_EN); + nau8821_setup_inserted_irq(nau8821); + } else { + dev_warn(nau8821->dev, + "Inserted IRQ fired but not connected\n"); + nau8821_eject_jack(nau8821); + } + } + + if (!clear_irq) + clear_irq = active_irq; + /* clears the rightmost interruption */ + regmap_write(regmap, NAU8821_R11_INT_CLR_KEY_STATUS, clear_irq); + + if (event_mask) + snd_soc_jack_report(nau8821->jack, event, event_mask); + + return IRQ_HANDLED; +} + +static const struct regmap_config nau8821_regmap_config = { + .val_bits = NAU8821_REG_DATA_LEN, + .reg_bits = NAU8821_REG_ADDR_LEN, + + .max_register = NAU8821_REG_MAX, + .readable_reg = nau8821_readable_reg, + .writeable_reg = nau8821_writeable_reg, + .volatile_reg = nau8821_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8821_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8821_reg_defaults), +}; + +static int nau8821_component_probe(struct snd_soc_component *component) +{ + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + + nau8821->dapm = dapm; + + return 0; +} + +/** + * nau8821_calc_fll_param - Calculate FLL parameters. + * @fll_in: external clock provided to codec. + * @fs: sampling rate. + * @fll_param: Pointer to structure of FLL parameters. + * + * Calculate FLL parameters to configure codec. + * + * Returns 0 for success or negative error code. + */ +static int nau8821_calc_fll_param(unsigned int fll_in, + unsigned int fs, struct nau8821_fll *fll_param) +{ + u64 fvco, fvco_max; + unsigned int fref, i, fvco_sel; + + /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by + * dividing freq_in by 1, 2, 4, or 8 using FLL pre-scalar. + * FREF = freq_in / NAU8821_FLL_REF_DIV_MASK + */ + for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) { + fref = fll_in >> fll_pre_scalar[i].param; + if (fref <= NAU_FREF_MAX) + break; + } + if (i == ARRAY_SIZE(fll_pre_scalar)) + return -EINVAL; + fll_param->clk_ref_div = fll_pre_scalar[i].val; + + /* Choose the FLL ratio based on FREF */ + for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) { + if (fref >= fll_ratio[i].param) + break; + } + if (i == ARRAY_SIZE(fll_ratio)) + return -EINVAL; + fll_param->ratio = fll_ratio[i].val; + + /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. + * FDCO must be within the 90MHz - 100MHz or the FFL cannot be + * guaranteed across the full range of operation. + * FDCO = freq_out * 2 * mclk_src_scaling + */ + fvco_max = 0; + fvco_sel = ARRAY_SIZE(mclk_src_scaling); + for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { + fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; + if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && + fvco_max < fvco) { + fvco_max = fvco; + fvco_sel = i; + } + } + if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel) + return -EINVAL; + fll_param->mclk_src = mclk_src_scaling[fvco_sel].val; + + /* Calculate the FLL 10-bit integer input and the FLL 24-bit fractional + * input based on FDCO, FREF and FLL ratio. + */ + fvco = div_u64(fvco_max << 24, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> 24) & 0x3ff; + fll_param->fll_frac = fvco & 0xffffff; + + return 0; +} + +static void nau8821_fll_apply(struct nau8821 *nau8821, + struct nau8821_fll *fll_param) +{ + struct regmap *regmap = nau8821->regmap; + + regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER, + NAU8821_CLK_SRC_MASK | NAU8821_CLK_MCLK_SRC_MASK, + NAU8821_CLK_SRC_MCLK | fll_param->mclk_src); + /* Make DSP operate at high speed for better performance. */ + regmap_update_bits(regmap, NAU8821_R04_FLL1, + NAU8821_FLL_RATIO_MASK | NAU8821_ICTRL_LATCH_MASK, + fll_param->ratio | (0x6 << NAU8821_ICTRL_LATCH_SFT)); + /* FLL 24-bit fractional input */ + regmap_write(regmap, NAU8821_R0A_FLL7, + (fll_param->fll_frac >> 16) & 0xff); + regmap_write(regmap, NAU8821_R0B_FLL8, fll_param->fll_frac & 0xffff); + /* FLL 10-bit integer input */ + regmap_update_bits(regmap, NAU8821_R06_FLL3, + NAU8821_FLL_INTEGER_MASK, fll_param->fll_int); + /* FLL pre-scaler */ + regmap_update_bits(regmap, NAU8821_R07_FLL4, + NAU8821_HIGHBW_EN | NAU8821_FLL_REF_DIV_MASK, + NAU8821_HIGHBW_EN | + (fll_param->clk_ref_div << NAU8821_FLL_REF_DIV_SFT)); + /* select divided VCO input */ + regmap_update_bits(regmap, NAU8821_R08_FLL5, + NAU8821_FLL_CLK_SW_MASK, NAU8821_FLL_CLK_SW_REF); + /* Disable free-running mode */ + regmap_update_bits(regmap, + NAU8821_R09_FLL6, NAU8821_DCO_EN, 0); + if (fll_param->fll_frac) { + /* set FLL loop filter enable and cutoff frequency at 500Khz */ + regmap_update_bits(regmap, NAU8821_R08_FLL5, + NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN | + NAU8821_FLL_FTR_SW_MASK, + NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN | + NAU8821_FLL_FTR_SW_FILTER); + regmap_update_bits(regmap, NAU8821_R09_FLL6, + NAU8821_SDM_EN | NAU8821_CUTOFF500, + NAU8821_SDM_EN | NAU8821_CUTOFF500); + } else { + /* disable FLL loop filter and cutoff frequency */ + regmap_update_bits(regmap, NAU8821_R08_FLL5, + NAU8821_FLL_PDB_DAC_EN | NAU8821_FLL_LOOP_FTR_EN | + NAU8821_FLL_FTR_SW_MASK, NAU8821_FLL_FTR_SW_ACCU); + regmap_update_bits(regmap, NAU8821_R09_FLL6, + NAU8821_SDM_EN | NAU8821_CUTOFF500, 0); + } +} + +/** + * nau8821_set_fll - FLL configuration of nau8821 + * @component: codec component + * @pll_id: PLL requested + * @source: clock source + * @freq_in: frequency of input clock source + * @freq_out: must be 256*Fs in order to achieve the best performance + * + * The FLL function can select BCLK or MCLK as the input clock source. + * + * Returns 0 if the parameters have been applied successfully + * or negative error code. + */ +static int nau8821_set_fll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, unsigned int freq_out) +{ + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + struct nau8821_fll fll_set_param, *fll_param = &fll_set_param; + int ret, fs; + + fs = freq_out >> 8; + ret = nau8821_calc_fll_param(freq_in, fs, fll_param); + if (ret) { + dev_err(nau8821->dev, + "Unsupported input clock %d to output clock %d\n", + freq_in, freq_out); + return ret; + } + dev_dbg(nau8821->dev, + "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n", + fll_param->mclk_src, fll_param->ratio, fll_param->fll_frac, + fll_param->fll_int, fll_param->clk_ref_div); + + nau8821_fll_apply(nau8821, fll_param); + mdelay(2); + regmap_update_bits(nau8821->regmap, NAU8821_R03_CLK_DIVIDER, + NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_VCO); + + return 0; +} + +static void nau8821_configure_mclk_as_sysclk(struct regmap *regmap) +{ + regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER, + NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_MCLK); + regmap_update_bits(regmap, NAU8821_R09_FLL6, + NAU8821_DCO_EN, 0); + /* Make DSP operate as default setting for power saving. */ + regmap_update_bits(regmap, NAU8821_R04_FLL1, + NAU8821_ICTRL_LATCH_MASK, 0); +} + +static int nau8821_configure_sysclk(struct nau8821 *nau8821, + int clk_id, unsigned int freq) +{ + struct regmap *regmap = nau8821->regmap; + + switch (clk_id) { + case NAU8821_CLK_DIS: + /* Clock provided externally and disable internal VCO clock */ + nau8821_configure_mclk_as_sysclk(regmap); + break; + case NAU8821_CLK_MCLK: + nau8821_configure_mclk_as_sysclk(regmap); + /* MCLK not changed by clock tree */ + regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER, + NAU8821_CLK_MCLK_SRC_MASK, 0); + break; + case NAU8821_CLK_INTERNAL: + if (nau8821_is_jack_inserted(regmap)) { + regmap_update_bits(regmap, NAU8821_R09_FLL6, + NAU8821_DCO_EN, NAU8821_DCO_EN); + regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER, + NAU8821_CLK_SRC_MASK, NAU8821_CLK_SRC_VCO); + /* Decrease the VCO frequency and make DSP operate + * as default setting for power saving. + */ + regmap_update_bits(regmap, NAU8821_R03_CLK_DIVIDER, + NAU8821_CLK_MCLK_SRC_MASK, 0xf); + regmap_update_bits(regmap, NAU8821_R04_FLL1, + NAU8821_ICTRL_LATCH_MASK | + NAU8821_FLL_RATIO_MASK, 0x10); + regmap_update_bits(regmap, NAU8821_R09_FLL6, + NAU8821_SDM_EN, NAU8821_SDM_EN); + } + break; + case NAU8821_CLK_FLL_MCLK: + /* Higher FLL reference input frequency can only set lower + * gain error, such as 0000 for input reference from MCLK + * 12.288Mhz. + */ + regmap_update_bits(regmap, NAU8821_R06_FLL3, + NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK, + NAU8821_FLL_CLK_SRC_MCLK | 0); + break; + case NAU8821_CLK_FLL_BLK: + /* If FLL reference input is from low frequency source, + * higher error gain can apply such as 0xf which has + * the most sensitive gain error correction threshold, + * Therefore, FLL has the most accurate DCO to + * target frequency. + */ + regmap_update_bits(regmap, NAU8821_R06_FLL3, + NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK, + NAU8821_FLL_CLK_SRC_BLK | + (0xf << NAU8821_GAIN_ERR_SFT)); + break; + case NAU8821_CLK_FLL_FS: + /* If FLL reference input is from low frequency source, + * higher error gain can apply such as 0xf which has + * the most sensitive gain error correction threshold, + * Therefore, FLL has the most accurate DCO to + * target frequency. + */ + regmap_update_bits(regmap, NAU8821_R06_FLL3, + NAU8821_FLL_CLK_SRC_MASK | NAU8821_GAIN_ERR_MASK, + NAU8821_FLL_CLK_SRC_FS | + (0xf << NAU8821_GAIN_ERR_SFT)); + break; + default: + dev_err(nau8821->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + nau8821->clk_id = clk_id; + dev_dbg(nau8821->dev, "Sysclk is %dHz and clock id is %d\n", freq, + nau8821->clk_id); + + return 0; +} + +static int nau8821_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + return nau8821_configure_sysclk(nau8821, clk_id, freq); +} + +static int nau8821_resume_setup(struct nau8821 *nau8821) +{ + struct regmap *regmap = nau8821->regmap; + + /* Close clock when jack type detection at manual mode */ + nau8821_configure_sysclk(nau8821, NAU8821_CLK_DIS, 0); + if (nau8821->irq) { + /* Clear all interruption status */ + nau8821_int_status_clear_all(regmap); + + /* Enable both insertion and ejection interruptions, and then + * bypass de-bounce circuit. + */ + regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, + NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN, 0); + regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL, + NAU8821_JACK_DET_DB_BYPASS, + NAU8821_JACK_DET_DB_BYPASS); + regmap_update_bits(regmap, NAU8821_R12_INTERRUPT_DIS_CTRL, + NAU8821_IRQ_INSERT_DIS | NAU8821_IRQ_EJECT_DIS, 0); + } + + return 0; +} + +static int nau8821_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + struct regmap *regmap = nau8821->regmap; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + /* Setup codec configuration after resume */ + if (snd_soc_component_get_bias_level(component) == + SND_SOC_BIAS_OFF) + nau8821_resume_setup(nau8821); + break; + + case SND_SOC_BIAS_OFF: + /* HPL/HPR short to ground */ + regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL, + NAU8821_SPKR_DWN1R | NAU8821_SPKR_DWN1L, 0); + if (nau8821->irq) { + /* Reset the configuration of jack type for detection. + * Detach 2kOhm Resistors from MICBIAS to MICGND1/2. + */ + regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS, + NAU8821_MICBIAS_JKR2, 0); + /* Turn off all interruptions before system shutdown. + * Keep theinterruption quiet before resume + * setup completes. + */ + regmap_write(regmap, + NAU8821_R12_INTERRUPT_DIS_CTRL, 0xffff); + regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, + NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN, + NAU8821_IRQ_EJECT_EN | NAU8821_IRQ_INSERT_EN); + } + break; + default: + break; + } + + return 0; +} + +static int __maybe_unused nau8821_suspend(struct snd_soc_component *component) +{ + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + if (nau8821->irq) + disable_irq(nau8821->irq); + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + /* Power down codec power; don't support button wakeup */ + snd_soc_component_disable_pin(component, "MICBIAS"); + snd_soc_dapm_sync(nau8821->dapm); + regcache_cache_only(nau8821->regmap, true); + regcache_mark_dirty(nau8821->regmap); + + return 0; +} + +static int __maybe_unused nau8821_resume(struct snd_soc_component *component) +{ + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(nau8821->regmap, false); + regcache_sync(nau8821->regmap); + if (nau8821->irq) + enable_irq(nau8821->irq); + + return 0; +} + +static const struct snd_soc_component_driver nau8821_component_driver = { + .probe = nau8821_component_probe, + .set_sysclk = nau8821_set_sysclk, + .set_pll = nau8821_set_fll, + .set_bias_level = nau8821_set_bias_level, + .suspend = nau8821_suspend, + .resume = nau8821_resume, + .controls = nau8821_controls, + .num_controls = ARRAY_SIZE(nau8821_controls), + .dapm_widgets = nau8821_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8821_dapm_widgets), + .dapm_routes = nau8821_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8821_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +/** + * nau8821_enable_jack_detect - Specify a jack for event reporting + * + * @component: component to register the jack with + * @jack: jack to use to report headset and button events on + * + * After this function has been called the headset insert/remove and button + * events will be routed to the given jack. Jack can be null to stop + * reporting. + */ +int nau8821_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component); + int ret; + + nau8821->jack = jack; + /* Initiate jack detection work queue */ + INIT_WORK(&nau8821->jdet_work, nau8821_jdet_work); + ret = devm_request_threaded_irq(nau8821->dev, nau8821->irq, NULL, + nau8821_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "nau8821", nau8821); + if (ret) { + dev_err(nau8821->dev, "Cannot request irq %d (%d)\n", + nau8821->irq, ret); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(nau8821_enable_jack_detect); + +static void nau8821_reset_chip(struct regmap *regmap) +{ + regmap_write(regmap, NAU8821_R00_RESET, 0xffff); + regmap_write(regmap, NAU8821_R00_RESET, 0xffff); +} + +static void nau8821_print_device_properties(struct nau8821 *nau8821) +{ + struct device *dev = nau8821->dev; + + dev_dbg(dev, "jkdet-enable: %d\n", nau8821->jkdet_enable); + dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8821->jkdet_pull_enable); + dev_dbg(dev, "jkdet-pull-up: %d\n", nau8821->jkdet_pull_up); + dev_dbg(dev, "jkdet-polarity: %d\n", nau8821->jkdet_polarity); + dev_dbg(dev, "micbias-voltage: %d\n", nau8821->micbias_voltage); + dev_dbg(dev, "vref-impedance: %d\n", nau8821->vref_impedance); + dev_dbg(dev, "jack-insert-debounce: %d\n", + nau8821->jack_insert_debounce); + dev_dbg(dev, "jack-eject-debounce: %d\n", + nau8821->jack_eject_debounce); + dev_dbg(dev, "dmic-clk-threshold: %d\n", + nau8821->dmic_clk_threshold); + dev_dbg(dev, "key_enable: %d\n", nau8821->key_enable); +} + +static int nau8821_read_device_properties(struct device *dev, + struct nau8821 *nau8821) +{ + int ret; + + nau8821->jkdet_enable = device_property_read_bool(dev, + "nuvoton,jkdet-enable"); + nau8821->jkdet_pull_enable = device_property_read_bool(dev, + "nuvoton,jkdet-pull-enable"); + nau8821->jkdet_pull_up = device_property_read_bool(dev, + "nuvoton,jkdet-pull-up"); + nau8821->key_enable = device_property_read_bool(dev, + "nuvoton,key-enable"); + nau8821->left_input_single_end = device_property_read_bool(dev, + "nuvoton,left-input-single-end"); + ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity", + &nau8821->jkdet_polarity); + if (ret) + nau8821->jkdet_polarity = 1; + ret = device_property_read_u32(dev, "nuvoton,micbias-voltage", + &nau8821->micbias_voltage); + if (ret) + nau8821->micbias_voltage = 6; + ret = device_property_read_u32(dev, "nuvoton,vref-impedance", + &nau8821->vref_impedance); + if (ret) + nau8821->vref_impedance = 2; + ret = device_property_read_u32(dev, "nuvoton,jack-insert-debounce", + &nau8821->jack_insert_debounce); + if (ret) + nau8821->jack_insert_debounce = 7; + ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce", + &nau8821->jack_eject_debounce); + if (ret) + nau8821->jack_eject_debounce = 0; + ret = device_property_read_u32(dev, "nuvoton,dmic-clk-threshold", + &nau8821->dmic_clk_threshold); + if (ret) + nau8821->dmic_clk_threshold = 3072000; + + return 0; +} + +static void nau8821_init_regs(struct nau8821 *nau8821) +{ + struct regmap *regmap = nau8821->regmap; + + /* Enable Bias/Vmid */ + regmap_update_bits(regmap, NAU8821_R66_BIAS_ADJ, + NAU8821_BIAS_VMID, NAU8821_BIAS_VMID); + regmap_update_bits(regmap, NAU8821_R76_BOOST, + NAU8821_GLOBAL_BIAS_EN, NAU8821_GLOBAL_BIAS_EN); + /* VMID Tieoff setting and enable TESTDAC. + * This sets the analog DAC inputs to a '0' input signal to avoid + * any glitches due to power up transients in both the analog and + * digital DAC circuit. + */ + regmap_update_bits(regmap, NAU8821_R66_BIAS_ADJ, + NAU8821_BIAS_VMID_SEL_MASK | NAU8821_BIAS_TESTDAC_EN, + (nau8821->vref_impedance << NAU8821_BIAS_VMID_SEL_SFT) | + NAU8821_BIAS_TESTDAC_EN); + /* Disable short Frame Sync detection logic */ + regmap_update_bits(regmap, NAU8821_R1E_LEFT_TIME_SLOT, + NAU8821_DIS_FS_SHORT_DET, NAU8821_DIS_FS_SHORT_DET); + /* Disable Boost Driver, Automatic Short circuit protection enable */ + regmap_update_bits(regmap, NAU8821_R76_BOOST, + NAU8821_PRECHARGE_DIS | NAU8821_HP_BOOST_DIS | + NAU8821_HP_BOOST_G_DIS | NAU8821_SHORT_SHUTDOWN_EN, + NAU8821_PRECHARGE_DIS | NAU8821_HP_BOOST_DIS | + NAU8821_HP_BOOST_G_DIS | NAU8821_SHORT_SHUTDOWN_EN); + /* Class G timer 64ms */ + regmap_update_bits(regmap, NAU8821_R4B_CLASSG_CTRL, + NAU8821_CLASSG_TIMER_MASK, + 0x20 << NAU8821_CLASSG_TIMER_SFT); + /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */ + regmap_update_bits(regmap, NAU8821_R6A_ANALOG_CONTROL_2, + NAU8821_HP_NON_CLASSG_CURRENT_2xADJ | + NAU8821_DAC_CAPACITOR_MSB | NAU8821_DAC_CAPACITOR_LSB, + NAU8821_HP_NON_CLASSG_CURRENT_2xADJ | + NAU8821_DAC_CAPACITOR_MSB | NAU8821_DAC_CAPACITOR_LSB); + /* Disable DACR/L power */ + regmap_update_bits(regmap, NAU8821_R80_CHARGE_PUMP, + NAU8821_POWER_DOWN_DACR | NAU8821_POWER_DOWN_DACL, 0); + /* DAC clock delay 2ns, VREF */ + regmap_update_bits(regmap, NAU8821_R73_RDAC, + NAU8821_DAC_CLK_DELAY_MASK | NAU8821_DAC_VREF_MASK, + (0x2 << NAU8821_DAC_CLK_DELAY_SFT) | + (0x3 << NAU8821_DAC_VREF_SFT)); + + regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS, + NAU8821_MICBIAS_VOLTAGE_MASK, nau8821->micbias_voltage); + /* Default oversampling/decimations settings are unusable + * (audible hiss). Set it to something better. + */ + regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE, + NAU8821_ADC_SYNC_DOWN_MASK, NAU8821_ADC_SYNC_DOWN_64); + regmap_update_bits(regmap, NAU8821_R2C_DAC_CTRL1, + NAU8821_DAC_OVERSAMPLE_MASK, NAU8821_DAC_OVERSAMPLE_64); + if (nau8821->left_input_single_end) { + regmap_update_bits(regmap, NAU8821_R6B_PGA_MUTE, + NAU8821_MUTE_MICNL_EN, NAU8821_MUTE_MICNL_EN); + regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS, + NAU8821_MICBIAS_LOWNOISE_EN, NAU8821_MICBIAS_LOWNOISE_EN); + } +} + +static int nau8821_setup_irq(struct nau8821 *nau8821) +{ + struct regmap *regmap = nau8821->regmap; + + /* Jack detection */ + regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL, + NAU8821_JKDET_OUTPUT_EN, + nau8821->jkdet_enable ? 0 : NAU8821_JKDET_OUTPUT_EN); + regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL, + NAU8821_JKDET_PULL_EN, + nau8821->jkdet_pull_enable ? 0 : NAU8821_JKDET_PULL_EN); + regmap_update_bits(regmap, NAU8821_R1A_GPIO12_CTRL, + NAU8821_JKDET_PULL_UP, + nau8821->jkdet_pull_up ? NAU8821_JKDET_PULL_UP : 0); + regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL, + NAU8821_JACK_POLARITY, + /* jkdet_polarity - 1 is for active-low */ + nau8821->jkdet_polarity ? 0 : NAU8821_JACK_POLARITY); + regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL, + NAU8821_JACK_INSERT_DEBOUNCE_MASK, + nau8821->jack_insert_debounce << + NAU8821_JACK_INSERT_DEBOUNCE_SFT); + regmap_update_bits(regmap, NAU8821_R0D_JACK_DET_CTRL, + NAU8821_JACK_EJECT_DEBOUNCE_MASK, + nau8821->jack_eject_debounce << + NAU8821_JACK_EJECT_DEBOUNCE_SFT); + /* Pull up IRQ pin */ + regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, + NAU8821_IRQ_PIN_PULL_UP | NAU8821_IRQ_PIN_PULL_EN | + NAU8821_IRQ_OUTPUT_EN, NAU8821_IRQ_PIN_PULL_UP | + NAU8821_IRQ_PIN_PULL_EN | NAU8821_IRQ_OUTPUT_EN); + /* Disable interruption before codec initiation done */ + /* Mask unneeded IRQs: 1 - disable, 0 - enable */ + regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK, 0x3f5, 0x3f5); + + return 0; +} + +/* Please keep this list alphabetically sorted */ +static const struct dmi_system_id nau8821_quirk_table[] = { + { + /* Positivo CW14Q01P-V2 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_BOARD_NAME, "CW14Q01P-V2"), + }, + .driver_data = (void *)(NAU8821_JD_ACTIVE_HIGH), + }, + {} +}; + +static void nau8821_check_quirks(void) +{ + const struct dmi_system_id *dmi_id; + + if (quirk_override != -1) { + nau8821_quirk = quirk_override; + return; + } + + dmi_id = dmi_first_match(nau8821_quirk_table); + if (dmi_id) + nau8821_quirk = (unsigned long)dmi_id->driver_data; +} + +static int nau8821_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct nau8821 *nau8821 = dev_get_platdata(&i2c->dev); + int ret, value; + + if (!nau8821) { + nau8821 = devm_kzalloc(dev, sizeof(*nau8821), GFP_KERNEL); + if (!nau8821) + return -ENOMEM; + nau8821_read_device_properties(dev, nau8821); + } + i2c_set_clientdata(i2c, nau8821); + + nau8821->regmap = devm_regmap_init_i2c(i2c, &nau8821_regmap_config); + if (IS_ERR(nau8821->regmap)) + return PTR_ERR(nau8821->regmap); + + nau8821->dev = dev; + nau8821->irq = i2c->irq; + + nau8821_check_quirks(); + + if (nau8821_quirk & NAU8821_JD_ACTIVE_HIGH) + nau8821->jkdet_polarity = 0; + + nau8821_print_device_properties(nau8821); + + nau8821_reset_chip(nau8821->regmap); + ret = regmap_read(nau8821->regmap, NAU8821_R58_I2C_DEVICE_ID, &value); + if (ret) { + dev_err(dev, "Failed to read device id (%d)\n", ret); + return ret; + } + nau8821_init_regs(nau8821); + + if (i2c->irq) + nau8821_setup_irq(nau8821); + + ret = devm_snd_soc_register_component(&i2c->dev, + &nau8821_component_driver, &nau8821_dai, 1); + + return ret; +} + +static const struct i2c_device_id nau8821_i2c_ids[] = { + { "nau8821", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau8821_i2c_ids); + +#ifdef CONFIG_OF +static const struct of_device_id nau8821_of_ids[] = { + { .compatible = "nuvoton,nau8821", }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8821_of_ids); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id nau8821_acpi_match[] = { + { "NVTN2020", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, nau8821_acpi_match); +#endif + +static struct i2c_driver nau8821_driver = { + .driver = { + .name = "nau8821", + .of_match_table = of_match_ptr(nau8821_of_ids), + .acpi_match_table = ACPI_PTR(nau8821_acpi_match), + }, + .probe = nau8821_i2c_probe, + .id_table = nau8821_i2c_ids, +}; +module_i2c_driver(nau8821_driver); + +MODULE_DESCRIPTION("ASoC nau8821 driver"); +MODULE_AUTHOR("John Hsu "); +MODULE_AUTHOR("Seven Lee "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h new file mode 100644 index 0000000000..00a888ed07 --- /dev/null +++ b/sound/soc/codecs/nau8821.h @@ -0,0 +1,582 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NAU88L21 ALSA SoC audio driver + * + * Copyright 2021 Nuvoton Technology Corp. + * Author: John Hsu + * Co-author: Seven Lee + */ + +#ifndef __NAU8821_H__ +#define __NAU8821_H__ + +#define NAU8821_R00_RESET 0x00 +#define NAU8821_R01_ENA_CTRL 0x01 +#define NAU8821_R03_CLK_DIVIDER 0x03 +#define NAU8821_R04_FLL1 0x04 +#define NAU8821_R05_FLL2 0x05 +#define NAU8821_R06_FLL3 0x06 +#define NAU8821_R07_FLL4 0x07 +#define NAU8821_R08_FLL5 0x08 +#define NAU8821_R09_FLL6 0x09 +#define NAU8821_R0A_FLL7 0x0a +#define NAU8821_R0B_FLL8 0x0b +#define NAU8821_R0D_JACK_DET_CTRL 0x0d +#define NAU8821_R0F_INTERRUPT_MASK 0x0f +#define NAU8821_R10_IRQ_STATUS 0x10 +#define NAU8821_R11_INT_CLR_KEY_STATUS 0x11 +#define NAU8821_R12_INTERRUPT_DIS_CTRL 0x12 +#define NAU8821_R13_DMIC_CTRL 0x13 +#define NAU8821_R1A_GPIO12_CTRL 0x1a +#define NAU8821_R1B_TDM_CTRL 0x1b +#define NAU8821_R1C_I2S_PCM_CTRL1 0x1c +#define NAU8821_R1D_I2S_PCM_CTRL2 0x1d +#define NAU8821_R1E_LEFT_TIME_SLOT 0x1e +#define NAU8821_R1F_RIGHT_TIME_SLOT 0x1f +#define NAU8821_R21_BIQ0_COF1 0x21 +#define NAU8821_R22_BIQ0_COF2 0x22 +#define NAU8821_R23_BIQ0_COF3 0x23 +#define NAU8821_R24_BIQ0_COF4 0x24 +#define NAU8821_R25_BIQ0_COF5 0x25 +#define NAU8821_R26_BIQ0_COF6 0x26 +#define NAU8821_R27_BIQ0_COF7 0x27 +#define NAU8821_R28_BIQ0_COF8 0x28 +#define NAU8821_R29_BIQ0_COF9 0x29 +#define NAU8821_R2A_BIQ0_COF10 0x2a +#define NAU8821_R2B_ADC_RATE 0x2b +#define NAU8821_R2C_DAC_CTRL1 0x2c +#define NAU8821_R2D_DAC_CTRL2 0x2d +#define NAU8821_R2F_DAC_DGAIN_CTRL 0x2f +#define NAU8821_R30_ADC_DGAIN_CTRL 0x30 +#define NAU8821_R31_MUTE_CTRL 0x31 +#define NAU8821_R32_HSVOL_CTRL 0x32 +#define NAU8821_R34_DACR_CTRL 0x34 +#define NAU8821_R35_ADC_DGAIN_CTRL1 0x35 +#define NAU8821_R36_ADC_DRC_KNEE_IP12 0x36 +#define NAU8821_R37_ADC_DRC_KNEE_IP34 0x37 +#define NAU8821_R38_ADC_DRC_SLOPES 0x38 +#define NAU8821_R39_ADC_DRC_ATKDCY 0x39 +#define NAU8821_R3A_DAC_DRC_KNEE_IP12 0x3a +#define NAU8821_R3B_DAC_DRC_KNEE_IP34 0x3b +#define NAU8821_R3C_DAC_DRC_SLOPES 0x3c +#define NAU8821_R3D_DAC_DRC_ATKDCY 0x3d +#define NAU8821_R41_BIQ1_COF1 0x41 +#define NAU8821_R42_BIQ1_COF2 0x42 +#define NAU8821_R43_BIQ1_COF3 0x43 +#define NAU8821_R44_BIQ1_COF4 0x44 +#define NAU8821_R45_BIQ1_COF5 0x45 +#define NAU8821_R46_BIQ1_COF6 0x46 +#define NAU8821_R47_BIQ1_COF7 0x47 +#define NAU8821_R48_BIQ1_COF8 0x48 +#define NAU8821_R49_BIQ1_COF9 0x49 +#define NAU8821_R4A_BIQ1_COF10 0x4a +#define NAU8821_R4B_CLASSG_CTRL 0x4b +#define NAU8821_R4C_IMM_MODE_CTRL 0x4c +#define NAU8821_R4D_IMM_RMS_L 0x4d +#define NAU8821_R4E_FUSE_CTRL2 0x4e +#define NAU8821_R4F_FUSE_CTRL3 0x4f +#define NAU8821_R51_FUSE_CTRL1 0x51 +#define NAU8821_R53_OTPDOUT_1 0x53 +#define NAU8821_R54_OTPDOUT_2 0x54 +#define NAU8821_R55_MISC_CTRL 0x55 +#define NAU8821_R58_I2C_DEVICE_ID 0x58 +#define NAU8821_R59_SARDOUT_RAM_STATUS 0x59 +#define NAU8821_R5A_SOFTWARE_RST 0x5a +#define NAU8821_R66_BIAS_ADJ 0x66 +#define NAU8821_R68_TRIM_SETTINGS 0x68 +#define NAU8821_R69_ANALOG_CONTROL_1 0x69 +#define NAU8821_R6A_ANALOG_CONTROL_2 0x6a +#define NAU8821_R6B_PGA_MUTE 0x6b +#define NAU8821_R71_ANALOG_ADC_1 0x71 +#define NAU8821_R72_ANALOG_ADC_2 0x72 +#define NAU8821_R73_RDAC 0x73 +#define NAU8821_R74_MIC_BIAS 0x74 +#define NAU8821_R76_BOOST 0x76 +#define NAU8821_R77_FEPGA 0x77 +#define NAU8821_R7E_PGA_GAIN 0x7e +#define NAU8821_R7F_POWER_UP_CONTROL 0x7f +#define NAU8821_R80_CHARGE_PUMP 0x80 +#define NAU8821_R81_CHARGE_PUMP_INPUT_READ 0x81 +#define NAU8821_R82_GENERAL_STATUS 0x82 +#define NAU8821_REG_MAX NAU8821_R82_GENERAL_STATUS +/* 16-bit control register address, and 16-bits control register data */ +#define NAU8821_REG_ADDR_LEN 16 +#define NAU8821_REG_DATA_LEN 16 + +/* ENA_CTRL (0x01) */ +#define NAU8821_CLK_DAC_INV_SFT 14 +#define NAU8821_CLK_DAC_INV (0x1 << NAU8821_CLK_DAC_INV) +#define NAU8821_EN_DACR_SFT 11 +#define NAU8821_EN_DACR (0x1 << NAU8821_EN_DACR_SFT) +#define NAU8821_EN_DACL_SFT 10 +#define NAU8821_EN_DACL (0x1 << NAU8821_EN_DACL_SFT) +#define NAU8821_EN_ADCR_SFT 9 +#define NAU8821_EN_ADCR (0x1 << NAU8821_EN_ADCR_SFT) +#define NAU8821_EN_ADCL_SFT 8 +#define NAU8821_EN_ADCL (0x1 << NAU8821_EN_ADCL_SFT) +#define NAU8821_EN_ADC_CLK_SFT 7 +#define NAU8821_EN_ADC_CLK (0x1 << NAU8821_EN_ADC_CLK_SFT) +#define NAU8821_EN_DAC_CLK_SFT 6 +#define NAU8821_EN_DAC_CLK (0x1 << NAU8821_EN_DAC_CLK_SFT) +#define NAU8821_EN_I2S_CLK_SFT 4 +#define NAU8821_EN_I2S_CLK (0x1 << NAU8821_EN_I2S_CLK_SFT) +#define NAU8821_EN_DRC_CLK_SFT 0 +#define NAU8821_EN_DRC_CLK (0x1 << NAU8821_EN_DRC_CLK_SFT) + +/* CLK_DIVIDER (0x03) */ +#define NAU8821_CLK_SRC_SFT 15 +#define NAU8821_CLK_SRC_MASK (0x1 << NAU8821_CLK_SRC_SFT) +#define NAU8821_CLK_SRC_VCO (0x1 << NAU8821_CLK_SRC_SFT) +#define NAU8821_CLK_SRC_MCLK (0x0 << NAU8821_CLK_SRC_SFT) +#define NAU8821_CLK_CODEC_SRC_SFT 13 +#define NAU8821_CLK_CODEC_SRC_MASK (0x1 << NAU8821_CLK_CODEC_SRC_SFT) +#define NAU8821_CLK_CODEC_SRC_VCO (0x1 << NAU8821_CLK_CODEC_SRC_SFT) +#define NAU8821_CLK_CODEC_SRC_MCLK (0x0 << NAU8821_CLK_CODEC_SRC_SFT) +#define NAU8821_CLK_ADC_SRC_SFT 6 +#define NAU8821_CLK_ADC_SRC_MASK (0x3 << NAU8821_CLK_ADC_SRC_SFT) +#define NAU8821_CLK_DAC_SRC_SFT 4 +#define NAU8821_CLK_DAC_SRC_MASK (0x3 << NAU8821_CLK_DAC_SRC_SFT) +#define NAU8821_CLK_MCLK_SRC_MASK 0xf + +/* FLL1 (0x04) */ +#define NAU8821_ICTRL_LATCH_SFT 10 +#define NAU8821_ICTRL_LATCH_MASK (0x7 << NAU8821_ICTRL_LATCH_SFT) +#define NAU8821_FLL_RATIO_MASK 0x7f + +/* FLL3 (0x06) */ +#define NAU8821_GAIN_ERR_SFT 12 +#define NAU8821_GAIN_ERR_MASK (0xf << NAU8821_GAIN_ERR_SFT) +#define NAU8821_FLL_CLK_SRC_SFT 10 +#define NAU8821_FLL_CLK_SRC_MASK (0x3 << NAU8821_FLL_CLK_SRC_SFT) +#define NAU8821_FLL_CLK_SRC_FS (0x3 << NAU8821_FLL_CLK_SRC_SFT) +#define NAU8821_FLL_CLK_SRC_BLK (0x2 << NAU8821_FLL_CLK_SRC_SFT) +#define NAU8821_FLL_CLK_SRC_MCLK (0x0 << NAU8821_FLL_CLK_SRC_SFT) +#define NAU8821_FLL_INTEGER_MASK 0x3ff + +/* FLL4 (0x07) */ +#define NAU8821_HIGHBW_EN_SFT 15 +#define NAU8821_HIGHBW_EN (0x1 << NAU8821_HIGHBW_EN_SFT) +#define NAU8821_FLL_REF_DIV_SFT 10 +#define NAU8821_FLL_REF_DIV_MASK (0x3 << NAU8821_FLL_REF_DIV_SFT) + +/* FLL5 (0x08) */ +#define NAU8821_FLL_PDB_DAC_EN (0x1 << 15) +#define NAU8821_FLL_LOOP_FTR_EN (0x1 << 14) +#define NAU8821_FLL_CLK_SW_SFT 13 +#define NAU8821_FLL_CLK_SW_MASK (0x1 << NAU8821_FLL_CLK_SW_SFT) +#define NAU8821_FLL_CLK_SW_N2 (0x1 << NAU8821_FLL_CLK_SW_SFT) +#define NAU8821_FLL_CLK_SW_REF (0x0 << NAU8821_FLL_CLK_SW_SFT) +#define NAU8821_FLL_FTR_SW_SFT 12 +#define NAU8821_FLL_FTR_SW_MASK (0x1 << NAU8821_FLL_FTR_SW_SFT) +#define NAU8821_FLL_FTR_SW_ACCU (0x1 << NAU8821_FLL_FTR_SW_SFT) +#define NAU8821_FLL_FTR_SW_FILTER (0x0 << NAU8821_FLL_FTR_SW_SFT) + +/* FLL6 (0x09) */ +#define NAU8821_DCO_EN (0x1 << 15) +#define NAU8821_SDM_EN (0x1 << 14) +#define NAU8821_CUTOFF500 (0x1 << 13) + +/* FLL7 (0x0a) */ +#define NAU8821_FLL_FRACH_MASK 0xff + +/* FLL8 (0x0b) */ +#define NAU8821_FLL_FRACL_MASK 0xffff + +/* JACK_DET_CTRL (0x0d) */ +/* 0 - open, 1 - short to GND */ +#define NAU8821_SPKR_DWN1R_SFT 15 +#define NAU8821_SPKR_DWN1R (0x1 << NAU8821_SPKR_DWN1R_SFT) +#define NAU8821_SPKR_DWN1L_SFT 14 +#define NAU8821_SPKR_DWN1L (0x1 << NAU8821_SPKR_DWN1L_SFT) +#define NAU8821_JACK_DET_RESTART (0x1 << 9) +#define NAU8821_JACK_DET_DB_BYPASS (0x1 << 8) +#define NAU8821_JACK_INSERT_DEBOUNCE_SFT 5 +#define NAU8821_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8821_JACK_INSERT_DEBOUNCE_SFT) +#define NAU8821_JACK_EJECT_DEBOUNCE_SFT 2 +#define NAU8821_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8821_JACK_EJECT_DEBOUNCE_SFT) +#define NAU8821_JACK_POLARITY (0x1 << 1) /* 0 - active low, 1 - active high */ + +/* INTERRUPT_MASK (0x0f) */ +#define NAU8821_IRQ_PIN_PULL_UP (0x1 << 14) +#define NAU8821_IRQ_PIN_PULL_EN (0x1 << 13) +#define NAU8821_IRQ_OUTPUT_EN (0x1 << 11) +#define NAU8821_IRQ_RMS_EN (0x1 << 8) +#define NAU8821_IRQ_KEY_RELEASE_EN (0x1 << 7) +#define NAU8821_IRQ_KEY_PRESS_EN (0x1 << 6) +#define NAU8821_IRQ_MIC_DET_EN (0x1 << 4) +#define NAU8821_IRQ_EJECT_EN (0x1 << 2) +#define NAU8821_IRQ_INSERT_EN 0x1 + +/* IRQ_STATUS (0x10) */ +#define NAU8821_SHORT_CIRCUIT_IRQ (0x1 << 9) +#define NAU8821_IMPEDANCE_MEAS_IRQ (0x1 << 8) +#define NAU8821_KEY_IRQ_SFT 6 +#define NAU8821_KEY_IRQ_MASK (0x3 << NAU8821_KEY_IRQ_SFT) +#define NAU8821_KEY_RELEASE_IRQ (0x2 << NAU8821_KEY_IRQ_SFT) +#define NAU8821_KEY_SHORT_PRESS_IRQ (0x1 << NAU8821_KEY_IRQ_SFT) +#define NAU8821_MIC_DETECT_IRQ (0x1 << 4) +#define NAU8821_JACK_EJECT_IRQ_MASK (0x3 << 2) +#define NAU8821_JACK_EJECT_DETECTED (0x1 << 2) +#define NAU8821_JACK_INSERT_IRQ_MASK 0x3 +#define NAU8821_JACK_INSERT_DETECTED 0x1 + +/* INTERRUPT_DIS_CTRL (0x12) */ +#define NAU8821_IRQ_KEY_RELEASE_DIS (0x1 << 7) +#define NAU8821_IRQ_KEY_PRESS_DIS (0x1 << 6) +#define NAU8821_IRQ_MIC_DIS (0x1 << 4) +#define NAU8821_IRQ_EJECT_DIS (0x1 << 2) +#define NAU8821_IRQ_INSERT_DIS 0x1 + +/* DMIC_CTRL (0x13) */ +#define NAU8821_DMIC_DS_SFT 7 +#define NAU8821_DMIC_DS_MASK (0x1 << NAU8821_DMIC_DS_SFT) +#define NAU8821_DMIC_DS_HIGH (0x1 << NAU8821_DMIC_DS_SFT) +#define NAU8821_DMIC_DS_LOW (0x0 << NAU8821_DMIC_DS_SFT) +#define NAU8821_DMIC_SRC_SFT 1 +#define NAU8821_DMIC_SRC_MASK (0x3 << NAU8821_DMIC_SRC_SFT) +#define NAU8821_CLK_DMIC_SRC (0x2 << NAU8821_DMIC_SRC_SFT) +#define NAU8821_DMIC_EN_SFT 0 + +/* GPIO12_CTRL (0x1a) */ +#define NAU8821_JKDET_PULL_UP (0x1 << 11) /* 0 - pull down, 1 - pull up */ +#define NAU8821_JKDET_PULL_EN (0x1 << 9) /* 0 - enable pull, 1 - disable */ +#define NAU8821_JKDET_OUTPUT_EN (0x1 << 8) /* 0 - enable input, 1 - enable output */ + +/* TDM_CTRL (0x1b) */ +#define NAU8821_TDM_EN_SFT 15 +#define NAU8821_TDM_EN (0x1 << NAU8821_TDM_EN_SFT) +#define NAU8821_ADCPHS_SFT 13 +#define NAU8821_DACL_CH_SFT 7 +#define NAU8821_DACL_CH_MASK (0x7 << NAU8821_DACL_CH_SFT) +#define NAU8821_DACR_CH_SFT 4 +#define NAU8821_DACR_CH_MASK (0x7 << NAU8821_DACR_CH_SFT) +#define NAU8821_ADCL_CH_SFT 2 +#define NAU8821_ADCL_CH_MASK (0x3 << NAU8821_ADCL_CH_SFT) +#define NAU8821_ADCR_CH_SFT 0 +#define NAU8821_ADCR_CH_MASK 0x3 + +/* I2S_PCM_CTRL1 (0x1c) */ +#define NAU8821_I2S_BP_SFT 7 +#define NAU8821_I2S_BP_MASK (0x1 << NAU8821_I2S_BP_SFT) +#define NAU8821_I2S_BP_INV (0x1 << NAU8821_I2S_BP_SFT) +#define NAU8821_I2S_PCMB_SFT 6 +#define NAU8821_I2S_PCMB_MASK (0x1 << NAU8821_I2S_PCMB_SFT) +#define NAU8821_I2S_PCMB_EN (0x1 << NAU8821_I2S_PCMB_SFT) +#define NAU8821_I2S_DL_SFT 2 +#define NAU8821_I2S_DL_MASK (0x3 << NAU8821_I2S_DL_SFT) +#define NAU8821_I2S_DL_32 (0x3 << NAU8821_I2S_DL_SFT) +#define NAU8821_I2S_DL_24 (0x2 << NAU8821_I2S_DL_SFT) +#define NAU8821_I2S_DL_20 (0x1 << NAU8821_I2S_DL_SFT) +#define NAU8821_I2S_DL_16 (0x0 << NAU8821_I2S_DL_SFT) +#define NAU8821_I2S_DF_MASK 0x3 +#define NAU8821_I2S_DF_PCM_AB 0x3 +#define NAU8821_I2S_DF_I2S 0x2 +#define NAU8821_I2S_DF_LEFT 0x1 +#define NAU8821_I2S_DF_RIGTH 0x0 + +/* I2S_PCM_CTRL2 (0x1d) */ +#define NAU8821_I2S_TRISTATE_SFT 15 +#define NAU8821_I2S_TRISTATE (0x1 << NAU8821_I2S_TRISTATE_SFT) +#define NAU8821_I2S_LRC_DIV_SFT 12 +#define NAU8821_I2S_LRC_DIV_MASK (0x3 << NAU8821_I2S_LRC_DIV_SFT) +#define NAU8821_I2S_MS_SFT 3 +#define NAU8821_I2S_MS_MASK (0x1 << NAU8821_I2S_MS_SFT) +#define NAU8821_I2S_MS_MASTER (0x1 << NAU8821_I2S_MS_SFT) +#define NAU8821_I2S_MS_SLAVE (0x0 << NAU8821_I2S_MS_SFT) +#define NAU8821_I2S_BLK_DIV_MASK 0x7 + +/* LEFT_TIME_SLOT (0x1e) */ +#define NAU8821_TSLOT_L_OFFSET_MASK 0x3ff +#define NAU8821_DIS_FS_SHORT_DET (0x1 << 13) + +/* RIGHT_TIME_SLOT (0x1f) */ +#define NAU8821_TSLOT_R_OFFSET_MASK 0x3ff + +/* BIQ0_COF10 (0x2a) */ +#define NAU8821_BIQ0_ADC_EN_SFT 3 +#define NAU8821_BIQ0_ADC_EN_EN (0x1 << NAU8821_BIQ0_ADC_EN_SFT) + +/* ADC_RATE (0x2b) */ +#define NAU8821_ADC_SYNC_DOWN_SFT 0 +#define NAU8821_ADC_SYNC_DOWN_MASK 0x3 +#define NAU8821_ADC_SYNC_DOWN_256 0x3 +#define NAU8821_ADC_SYNC_DOWN_128 0x2 +#define NAU8821_ADC_SYNC_DOWN_64 0x1 +#define NAU8821_ADC_SYNC_DOWN_32 0x0 +#define NAU8821_ADC_L_SRC_SFT 15 +#define NAU8821_ADC_L_SRC_EN (0x1 << NAU8821_ADC_L_SRC_SFT) +#define NAU8821_ADC_R_SRC_SFT 14 +#define NAU8821_ADC_R_SRC_EN (0x1 << NAU8821_ADC_R_SRC_SFT) + +/* DAC_CTRL1 (0x2c) */ +#define NAU8821_DAC_OVERSAMPLE_SFT 0 +#define NAU8821_DAC_OVERSAMPLE_MASK 0x7 +#define NAU8821_DAC_OVERSAMPLE_32 0x4 +#define NAU8821_DAC_OVERSAMPLE_128 0x2 +#define NAU8821_DAC_OVERSAMPLE_256 0x1 +#define NAU8821_DAC_OVERSAMPLE_64 0x0 + +/* DAC_DGAIN_CTRL (0x2f) */ +#define NAU8821_DAC1_TO_DAC0_ST_SFT 8 +#define NAU8821_DAC1_TO_DAC0_ST_MASK (0xff << NAU8821_DAC1_TO_DAC0_ST_SFT) +#define NAU8821_DAC0_TO_DAC1_ST_SFT 0 +#define NAU8821_DAC0_TO_DAC1_ST_MASK 0xff + +/* MUTE_CTRL (0x31) */ +#define NAU8821_DAC_ZC_EN (0x1 << 12) +#define NAU8821_DAC_SOFT_MUTE (0x1 << 9) +#define NAU8821_ADC_ZC_EN (0x1 << 2) +#define NAU8821_ADC_SOFT_MUTE (0x1 << 1) + +/* HSVOL_CTRL (0x32) */ +#define NAU8821_HP_MUTE (0x1 << 15) +#define NAU8821_HP_MUTE_AUTO (0x1 << 14) +#define NAU8821_HPL_MUTE (0x1 << 13) +#define NAU8821_HPR_MUTE (0x1 << 12) +#define NAU8821_HPL_VOL_SFT 4 +#define NAU8821_HPL_VOL_MASK (0x3 << NAU8821_HPL_VOL_SFT) +#define NAU8821_HPR_VOL_SFT 0 +#define NAU8821_HPR_VOL_MASK (0x3 << NAU8821_HPR_VOL_SFT) + +/* DACR_CTRL (0x34) */ +#define NAU8821_DACR_CH_VOL_SFT 8 +#define NAU8821_DACR_CH_VOL_MASK (0xff << NAU8821_DACR_CH_VOL_SFT) +#define NAU8821_DACL_CH_VOL_SFT 0 +#define NAU8821_DACL_CH_VOL_MASK 0xff + +/* ADC_DGAIN_CTRL1 (0x35) */ +#define NAU8821_ADCR_CH_VOL_SFT 8 +#define NAU8821_ADCR_CH_VOL_MASK (0xff << NAU8821_ADCR_CH_VOL_SFT) +#define NAU8821_ADCL_CH_VOL_SFT 0 +#define NAU8821_ADCL_CH_VOL_MASK 0xff + +/* ADC_DRC_KNEE_IP12 (0x36) */ +#define NAU8821_DRC_ENA_ADC_SFT 15 +#define NAU8821_DRC_ENA_ADC_EN (0x1 << NAU8821_DRC_ENA_ADC_SFT) + +/* ADC_DRC_KNEE_IP34 (0x37) */ +#define NAU8821_DRC_KNEE4_IP_ADC_SFT 8 +#define NAU8821_DRC_KNEE4_IP_ADC_MASK (0xff << NAU8821_DRC_KNEE4_IP_ADC_SFT) +#define NAU8821_DRC_KNEE3_IP_ADC_SFT 0 +#define NAU8821_DRC_KNEE3_IP_ADC_MASK 0xff + +/* ADC_DRC_SLOPES (0x38) */ +#define NAU8821_DRC_NG_SLP_ADC_SFT 12 +#define NAU8821_DRC_EXP_SLP_ADC_SFT 9 +#define NAU8821_DRC_CMP2_SLP_ADC_SFT 6 +#define NAU8821_DRC_CMP1_SLP_ADC_SFT 3 +#define NAU8821_DRC_LMT_SLP_ADC_SFT 0 + +/* ADC_DRC_ATKDCY (0x39) */ +#define NAU8821_DRC_PK_COEF1_ADC_SFT 12 +#define NAU8821_DRC_PK_COEF2_ADC_SFT 8 +#define NAU8821_DRC_ATK_ADC_SFT 4 +#define NAU8821_DRC_DCY_ADC_SFT 0 + +/* BIQ1_COF10 (0x4a) */ +#define NAU8821_BIQ1_DAC_EN_SFT 3 +#define NAU8821_BIQ1_DAC_EN_EN (0x1 << NAU8821_BIQ1_DAC_EN_SFT) + +/* CLASSG_CTRL (0x4b) */ +#define NAU8821_CLASSG_TIMER_SFT 8 +#define NAU8821_CLASSG_TIMER_MASK (0x3f << NAU8821_CLASSG_TIMER_SFT) +#define NAU8821_CLASSG_TIMER_64MS (0x20 << NAU8821_CLASSG_TIMER_SFT) +#define NAU8821_CLASSG_TIMER_32MS (0x10 << NAU8821_CLASSG_TIMER_SFT) +#define NAU8821_CLASSG_TIMER_16MS (0x8 << NAU8821_CLASSG_TIMER_SFT) +#define NAU8821_CLASSG_TIMER_8MS (0x4 << NAU8821_CLASSG_TIMER_SFT) +#define NAU8821_CLASSG_TIMER_2MS (0x2 << NAU8821_CLASSG_TIMER_SFT) +#define NAU8821_CLASSG_TIMER_1MS (0x1 << NAU8821_CLASSG_TIMER_SFT) +#define NAU8821_CLASSG_RDAC_EN_SFT 2 +#define NAU8821_CLASSG_RDAC_EN (0x1 << NAU8821_CLASSG_RDAC_EN_SFT) +#define NAU8821_CLASSG_LDAC_EN_SFT 1 +#define NAU8821_CLASSG_LDAC_EN (0x1 << NAU8821_CLASSG_LDAC_EN_SFT) +#define NAU8821_CLASSG_EN_SFT 0 +#define NAU8821_CLASSG_EN 0x1 + +/* IMM_MODE_CTRL (0x4c) */ +#define NAU8821_IMM_THD_SFT 8 +#define NAU8821_IMM_THD_MASK (0x3f << NAU8821_IMM_THD_SFT) +#define NAU8821_IMM_GEN_VOL_SFT 6 +#define NAU8821_IMM_GEN_VOL_MASK (0x3 << NAU8821_IMM_GEN_VOL_SFT) +#define NAU8821_IMM_CYC_SFT 4 +#define NAU8821_IMM_CYC_MASK (0x3 << NAU8821_IMM_CYC_SFT) +#define NAU8821_IMM_EN (0x1 << 3) +#define NAU8821_IMM_DAC_SRC_MASK 0x3 + +/* I2C_DEVICE_ID (0x58) */ +#define NAU8821_KEYDET (0x1 << 7) +#define NAU8821_MICDET (0x1 << 6) +#define NAU8821_SOFTWARE_ID_MASK 0x3 + +/* BIAS_ADJ (0x66) */ +#define NAU8821_BIAS_HP_IMP (0x1 << 15) +#define NAU8821_BIAS_TESTDAC_SFT 8 +#define NAU8821_BIAS_TESTDAC_EN (0x3 << NAU8821_BIAS_TESTDAC_SFT) +#define NAU8821_BIAS_TESTDACR_EN (0x2 << NAU8821_BIAS_TESTDAC_SFT) +#define NAU8821_BIAS_TESTDACL_EN (0x1 << NAU8821_BIAS_TESTDAC_SFT) +#define NAU8821_BIAS_VMID (0x1 << 6) +#define NAU8821_BIAS_VMID_SEL_SFT 4 +#define NAU8821_BIAS_VMID_SEL_MASK (0x3 << NAU8821_BIAS_VMID_SEL_SFT) + +/* ANALOG_CONTROL_1 (0x69) */ +#define NAU8821_JD_POL_SFT 2 +#define NAU8821_JD_POL_MASK (0x1 << NAU8821_JD_POL_SFT) +#define NAU8821_JD_POL_INV (0x1 << NAU8821_JD_POL_SFT) +#define NAU8821_JD_OUT_POL_SFT 1 +#define NAU8821_JD_OUT_POL_MASK (0x1 << NAU8821_JD_OUT_POL_SFT) +#define NAU8821_JD_OUT_POL_INV (0x1 << NAU8821_JD_OUT_POL_SFT) +#define NAU8821_JD_EN_SFT 0 +#define NAU8821_JD_EN 0x1 + +/* ANALOG_CONTROL_2 (0x6a) */ +#define NAU8821_HP_NON_CLASSG_CURRENT_2xADJ (0x1 << 12) +#define NAU8821_DAC_CAPACITOR_MSB (0x1 << 1) +#define NAU8821_DAC_CAPACITOR_LSB 0x1 + +/* MUTE_MIC_L_N (0x6b) */ +#define NAU8821_MUTE_MICNL_SFT 5 +#define NAU8821_MUTE_MICNL_EN (0x1 << NAU8821_MUTE_MICNL_SFT) +#define NAU8821_MUTE_MICNR_SFT 4 +#define NAU8821_MUTE_MICNR_EN (0x1 << NAU8821_MUTE_MICNR_SFT) +#define NAU8821_MUTE_MICRP_SFT 2 +#define NAU8821_MUTE_MICRP_EN (0x1 << NAU8821_MUTE_MICRP_SFT) + +/* ANALOG_ADC_1 (0x71) */ +#define NAU8821_MICDET_EN_SFT 0 +#define NAU8821_MICDET_MASK 0x1 +#define NAU8821_MICDET_DIS 0x1 +#define NAU8821_MICDET_EN 0x0 + +/* ANALOG_ADC_2 (0x72) */ +#define NAU8821_ADC_VREFSEL_SFT 8 +#define NAU8821_ADC_VREFSEL_MASK (0x3 << NAU8821_ADC_VREFSEL_SFT) +#define NAU8821_POWERUP_ADCL_SFT 6 +#define NAU8821_POWERUP_ADCL (0x1 << NAU8821_POWERUP_ADCL_SFT) +#define NAU8821_POWERUP_ADCR_SFT 4 +#define NAU8821_POWERUP_ADCR (0x1 << NAU8821_POWERUP_ADCR_SFT) + +/* RDAC (0x73) */ +#define NAU8821_DACR_EN_SFT 13 +#define NAU8821_DACR_EN (0x3 << NAU8821_DACR_EN_SFT) +#define NAU8821_DACL_EN_SFT 12 +#define NAU8821_DACL_EN (0x3 << NAU8821_DACL_EN_SFT) +#define NAU8821_DACR_CLK_EN_SFT 9 +#define NAU8821_DACR_CLK_EN (0x3 << NAU8821_DACR_CLK_EN_SFT) +#define NAU8821_DACL_CLK_EN_SFT 8 +#define NAU8821_DACL_CLK_EN (0x3 << NAU8821_DACL_CLK_EN_SFT) +#define NAU8821_DAC_CLK_DELAY_SFT 4 +#define NAU8821_DAC_CLK_DELAY_MASK (0x7 << NAU8821_DAC_CLK_DELAY_SFT) +#define NAU8821_DAC_VREF_SFT 2 +#define NAU8821_DAC_VREF_MASK (0x3 << NAU8821_DAC_VREF_SFT) + +/* MIC_BIAS (0x74) */ +#define NAU8821_MICBIAS_JKR2 (0x1 << 12) +#define NAU8821_MICBIAS_LOWNOISE_SFT 10 +#define NAU8821_MICBIAS_LOWNOISE_EN (0x1 << NAU8821_MICBIAS_LOWNOISE_SFT) +#define NAU8821_MICBIAS_POWERUP_SFT 8 +#define NAU8821_MICBIAS_POWERUP_EN (0x1 << NAU8821_MICBIAS_POWERUP_SFT) +#define NAU8821_MICBIAS_VOLTAGE_SFT 0 +#define NAU8821_MICBIAS_VOLTAGE_MASK 0x7 + +/* BOOST (0x76) */ +#define NAU8821_PRECHARGE_DIS (0x1 << 13) +#define NAU8821_GLOBAL_BIAS_EN (0x1 << 12) +#define NAU8821_HP_BOOST_DISCHRG_SFT 11 +#define NAU8821_HP_BOOST_DISCHRG_EN (0x1 << NAU8821_HP_BOOST_DISCHRG_SFT) +#define NAU8821_HP_BOOST_DIS_SFT 9 +#define NAU8821_HP_BOOST_DIS (0x1 << NAU8821_HP_BOOST_DIS_SFT) +#define NAU8821_HP_BOOST_G_DIS (0x1 << 8) +#define NAU8821_SHORT_SHUTDOWN_EN (0x1 << 6) + +/* FEPGA (0x77) */ +#define NAU8821_ACDC_CTRL_SFT 14 +#define NAU8821_ACDC_CTRL_MASK (0x3 << NAU8821_ACDC_CTRL_SFT) +#define NAU8821_ACDC_VREF_MICP (0x1 << NAU8821_ACDC_CTRL_SFT) +#define NAU8821_ACDC_VREF_MICN (0x2 << NAU8821_ACDC_CTRL_SFT) +#define NAU8821_FEPGA_MODEL_SFT 4 +#define NAU8821_FEPGA_MODEL_MASK (0xf << NAU8821_FEPGA_MODEL_SFT) +#define NAU8821_FEPGA_MODEL_AAF (0x1 << NAU8821_FEPGA_MODEL_SFT) +#define NAU8821_FEPGA_MODEL_DIS (0x2 << NAU8821_FEPGA_MODEL_SFT) +#define NAU8821_FEPGA_MODEL_IMP12K (0x8 << NAU8821_FEPGA_MODEL_SFT) +#define NAU8821_FEPGA_MODER_SFT 0 +#define NAU8821_FEPGA_MODER_MASK 0xf +#define NAU8821_FEPGA_MODER_AAF 0x1 +#define NAU8821_FEPGA_MODER_DIS 0x2 +#define NAU8821_FEPGA_MODER_IMP12K 0x8 + + +/* PGA_GAIN (0x7e) */ +#define NAU8821_PGA_GAIN_L_SFT 8 +#define NAU8821_PGA_GAIN_L_MASK (0x3f << NAU8821_PGA_GAIN_L_SFT) +#define NAU8821_PGA_GAIN_R_SFT 0 +#define NAU8821_PGA_GAIN_R_MASK 0x3f + +/* POWER_UP_CONTROL (0x7f) */ +#define NAU8821_PUP_PGA_L_SFT 15 +#define NAU8821_PUP_PGA_L (0x1 << NAU8821_PUP_PGA_L_SFT) +#define NAU8821_PUP_PGA_R_SFT 14 +#define NAU8821_PUP_PGA_R (0x1 << NAU8821_PUP_PGA_R_SFT) +#define NAU8821_PUP_INTEG_R_SFT 5 +#define NAU8821_PUP_INTEG_R (0x1 << NAU8821_PUP_INTEG_R_SFT) +#define NAU8821_PUP_INTEG_L_SFT 4 +#define NAU8821_PUP_INTEG_L (0x1 << NAU8821_PUP_INTEG_L_SFT) +#define NAU8821_PUP_DRV_INSTG_R_SFT 3 +#define NAU8821_PUP_DRV_INSTG_R (0x1 << NAU8821_PUP_DRV_INSTG_R_SFT) +#define NAU8821_PUP_DRV_INSTG_L_SFT 2 +#define NAU8821_PUP_DRV_INSTG_L (0x1 << NAU8821_PUP_DRV_INSTG_L_SFT) +#define NAU8821_PUP_MAIN_DRV_R_SFT 1 +#define NAU8821_PUP_MAIN_DRV_R (0x1 << NAU8821_PUP_MAIN_DRV_R_SFT) +#define NAU8821_PUP_MAIN_DRV_L_SFT 0 +#define NAU8821_PUP_MAIN_DRV_L 0x1 + +/* CHARGE_PUMP (0x80) */ +#define NAU8821_JAMNODCLOW (0x1 << 10) +#define NAU8821_POWER_DOWN_DACR_SFT 9 +#define NAU8821_POWER_DOWN_DACR (0x1 << NAU8821_POWER_DOWN_DACR_SFT) +#define NAU8821_POWER_DOWN_DACL_SFT 8 +#define NAU8821_POWER_DOWN_DACL (0x1 << NAU8821_POWER_DOWN_DACL_SFT) +#define NAU8821_CHANRGE_PUMP_EN_SFT 5 +#define NAU8821_CHANRGE_PUMP_EN (0x1 << NAU8821_CHANRGE_PUMP_EN_SFT) + +/* GENERAL_STATUS (0x82) */ +#define NAU8821_GPIO2_IN_SFT 1 +#define NAU8821_GPIO2_IN (0x1 << NAU8821_GPIO2_IN_SFT) + +#define NUVOTON_CODEC_DAI "nau8821-hifi" + +/* System Clock Source */ +enum { + NAU8821_CLK_DIS, + NAU8821_CLK_MCLK, + NAU8821_CLK_INTERNAL, + NAU8821_CLK_FLL_MCLK, + NAU8821_CLK_FLL_BLK, + NAU8821_CLK_FLL_FS, +}; + +struct nau8821 { + struct device *dev; + struct regmap *regmap; + struct snd_soc_dapm_context *dapm; + struct snd_soc_jack *jack; + struct work_struct jdet_work; + int irq; + int clk_id; + int micbias_voltage; + int vref_impedance; + bool jkdet_enable; + bool jkdet_pull_enable; + bool jkdet_pull_up; + bool left_input_single_end; + int jkdet_polarity; + int jack_insert_debounce; + int jack_eject_debounce; + int fs; + int dmic_clk_threshold; + int key_enable; +}; + +int nau8821_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack); + +#endif /* __NAU8821_H__ */ diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c new file mode 100644 index 0000000000..7199d734c7 --- /dev/null +++ b/sound/soc/codecs/nau8822.c @@ -0,0 +1,1179 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// nau8822.c -- NAU8822 ALSA Soc Audio driver +// +// Copyright 2017 Nuvoton Technology Crop. +// +// Author: David Lin +// Co-author: John Hsu +// Co-author: Seven Li +// +// Based on WM8974.c + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nau8822.h" + +#define NAU_PLL_FREQ_MAX 100000000 +#define NAU_PLL_FREQ_MIN 90000000 +#define NAU_PLL_REF_MAX 33000000 +#define NAU_PLL_REF_MIN 8000000 +#define NAU_PLL_OPTOP_MIN 6 + +static const int nau8822_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 }; + +static const struct reg_default nau8822_reg_defaults[] = { + { NAU8822_REG_POWER_MANAGEMENT_1, 0x0000 }, + { NAU8822_REG_POWER_MANAGEMENT_2, 0x0000 }, + { NAU8822_REG_POWER_MANAGEMENT_3, 0x0000 }, + { NAU8822_REG_AUDIO_INTERFACE, 0x0050 }, + { NAU8822_REG_COMPANDING_CONTROL, 0x0000 }, + { NAU8822_REG_CLOCKING, 0x0140 }, + { NAU8822_REG_ADDITIONAL_CONTROL, 0x0000 }, + { NAU8822_REG_GPIO_CONTROL, 0x0000 }, + { NAU8822_REG_JACK_DETECT_CONTROL_1, 0x0000 }, + { NAU8822_REG_DAC_CONTROL, 0x0000 }, + { NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME, 0x00ff }, + { NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, 0x00ff }, + { NAU8822_REG_JACK_DETECT_CONTROL_2, 0x0000 }, + { NAU8822_REG_ADC_CONTROL, 0x0100 }, + { NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME, 0x00ff }, + { NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, 0x00ff }, + { NAU8822_REG_EQ1, 0x012c }, + { NAU8822_REG_EQ2, 0x002c }, + { NAU8822_REG_EQ3, 0x002c }, + { NAU8822_REG_EQ4, 0x002c }, + { NAU8822_REG_EQ5, 0x002c }, + { NAU8822_REG_DAC_LIMITER_1, 0x0032 }, + { NAU8822_REG_DAC_LIMITER_2, 0x0000 }, + { NAU8822_REG_NOTCH_FILTER_1, 0x0000 }, + { NAU8822_REG_NOTCH_FILTER_2, 0x0000 }, + { NAU8822_REG_NOTCH_FILTER_3, 0x0000 }, + { NAU8822_REG_NOTCH_FILTER_4, 0x0000 }, + { NAU8822_REG_ALC_CONTROL_1, 0x0038 }, + { NAU8822_REG_ALC_CONTROL_2, 0x000b }, + { NAU8822_REG_ALC_CONTROL_3, 0x0032 }, + { NAU8822_REG_NOISE_GATE, 0x0010 }, + { NAU8822_REG_PLL_N, 0x0008 }, + { NAU8822_REG_PLL_K1, 0x000c }, + { NAU8822_REG_PLL_K2, 0x0093 }, + { NAU8822_REG_PLL_K3, 0x00e9 }, + { NAU8822_REG_3D_CONTROL, 0x0000 }, + { NAU8822_REG_RIGHT_SPEAKER_CONTROL, 0x0000 }, + { NAU8822_REG_INPUT_CONTROL, 0x0033 }, + { NAU8822_REG_LEFT_INP_PGA_CONTROL, 0x0010 }, + { NAU8822_REG_RIGHT_INP_PGA_CONTROL, 0x0010 }, + { NAU8822_REG_LEFT_ADC_BOOST_CONTROL, 0x0100 }, + { NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0x0100 }, + { NAU8822_REG_OUTPUT_CONTROL, 0x0002 }, + { NAU8822_REG_LEFT_MIXER_CONTROL, 0x0001 }, + { NAU8822_REG_RIGHT_MIXER_CONTROL, 0x0001 }, + { NAU8822_REG_LHP_VOLUME, 0x0039 }, + { NAU8822_REG_RHP_VOLUME, 0x0039 }, + { NAU8822_REG_LSPKOUT_VOLUME, 0x0039 }, + { NAU8822_REG_RSPKOUT_VOLUME, 0x0039 }, + { NAU8822_REG_AUX2_MIXER, 0x0001 }, + { NAU8822_REG_AUX1_MIXER, 0x0001 }, + { NAU8822_REG_POWER_MANAGEMENT_4, 0x0000 }, + { NAU8822_REG_LEFT_TIME_SLOT, 0x0000 }, + { NAU8822_REG_MISC, 0x0020 }, + { NAU8822_REG_RIGHT_TIME_SLOT, 0x0000 }, + { NAU8822_REG_DEVICE_REVISION, 0x007f }, + { NAU8822_REG_DEVICE_ID, 0x001a }, + { NAU8822_REG_DAC_DITHER, 0x0114 }, + { NAU8822_REG_ALC_ENHANCE_1, 0x0000 }, + { NAU8822_REG_ALC_ENHANCE_2, 0x0000 }, + { NAU8822_REG_192KHZ_SAMPLING, 0x0008 }, + { NAU8822_REG_MISC_CONTROL, 0x0000 }, + { NAU8822_REG_INPUT_TIEOFF, 0x0000 }, + { NAU8822_REG_POWER_REDUCTION, 0x0000 }, + { NAU8822_REG_AGC_PEAK2PEAK, 0x0000 }, + { NAU8822_REG_AGC_PEAK_DETECT, 0x0000 }, + { NAU8822_REG_AUTOMUTE_CONTROL, 0x0000 }, + { NAU8822_REG_OUTPUT_TIEOFF, 0x0000 }, +}; + +static bool nau8822_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8822_REG_RESET ... NAU8822_REG_JACK_DETECT_CONTROL_1: + case NAU8822_REG_DAC_CONTROL ... NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME: + case NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME: + case NAU8822_REG_EQ1 ... NAU8822_REG_EQ5: + case NAU8822_REG_DAC_LIMITER_1 ... NAU8822_REG_DAC_LIMITER_2: + case NAU8822_REG_NOTCH_FILTER_1 ... NAU8822_REG_NOTCH_FILTER_4: + case NAU8822_REG_ALC_CONTROL_1 ...NAU8822_REG_PLL_K3: + case NAU8822_REG_3D_CONTROL: + case NAU8822_REG_RIGHT_SPEAKER_CONTROL: + case NAU8822_REG_INPUT_CONTROL ... NAU8822_REG_LEFT_ADC_BOOST_CONTROL: + case NAU8822_REG_RIGHT_ADC_BOOST_CONTROL ... NAU8822_REG_AUX1_MIXER: + case NAU8822_REG_POWER_MANAGEMENT_4 ... NAU8822_REG_DEVICE_ID: + case NAU8822_REG_DAC_DITHER: + case NAU8822_REG_ALC_ENHANCE_1 ... NAU8822_REG_MISC_CONTROL: + case NAU8822_REG_INPUT_TIEOFF ... NAU8822_REG_OUTPUT_TIEOFF: + return true; + default: + return false; + } +} + +static bool nau8822_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8822_REG_RESET ... NAU8822_REG_JACK_DETECT_CONTROL_1: + case NAU8822_REG_DAC_CONTROL ... NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME: + case NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME: + case NAU8822_REG_EQ1 ... NAU8822_REG_EQ5: + case NAU8822_REG_DAC_LIMITER_1 ... NAU8822_REG_DAC_LIMITER_2: + case NAU8822_REG_NOTCH_FILTER_1 ... NAU8822_REG_NOTCH_FILTER_4: + case NAU8822_REG_ALC_CONTROL_1 ...NAU8822_REG_PLL_K3: + case NAU8822_REG_3D_CONTROL: + case NAU8822_REG_RIGHT_SPEAKER_CONTROL: + case NAU8822_REG_INPUT_CONTROL ... NAU8822_REG_LEFT_ADC_BOOST_CONTROL: + case NAU8822_REG_RIGHT_ADC_BOOST_CONTROL ... NAU8822_REG_AUX1_MIXER: + case NAU8822_REG_POWER_MANAGEMENT_4 ... NAU8822_REG_DEVICE_ID: + case NAU8822_REG_DAC_DITHER: + case NAU8822_REG_ALC_ENHANCE_1 ... NAU8822_REG_MISC_CONTROL: + case NAU8822_REG_INPUT_TIEOFF ... NAU8822_REG_OUTPUT_TIEOFF: + return true; + default: + return false; + } +} + +static bool nau8822_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8822_REG_RESET: + case NAU8822_REG_DEVICE_REVISION: + case NAU8822_REG_DEVICE_ID: + case NAU8822_REG_AGC_PEAK2PEAK: + case NAU8822_REG_AGC_PEAK_DETECT: + case NAU8822_REG_AUTOMUTE_CONTROL: + return true; + default: + return false; + } +} + +/* The EQ parameters get function is to get the 5 band equalizer control. + * The regmap raw read can't work here because regmap doesn't provide + * value format for value width of 9 bits. Therefore, the driver reads data + * from cache and makes value format according to the endianness of + * bytes type control element. + */ +static int nau8822_eq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + int i, reg; + u16 reg_val, *val; + __be16 tmp; + + val = (u16 *)ucontrol->value.bytes.data; + reg = NAU8822_REG_EQ1; + for (i = 0; i < params->max / sizeof(u16); i++) { + reg_val = snd_soc_component_read(component, reg + i); + /* conversion of 16-bit integers between native CPU format + * and big endian format + */ + tmp = cpu_to_be16(reg_val); + memcpy(val + i, &tmp, sizeof(tmp)); + } + + return 0; +} + +/* The EQ parameters put function is to make configuration of 5 band equalizer + * control. These configuration includes central frequency, equalizer gain, + * cut-off frequency, bandwidth control, and equalizer path. + * The regmap raw write can't work here because regmap doesn't provide + * register and value format for register with address 7 bits and value 9 bits. + * Therefore, the driver makes value format according to the endianness of + * bytes type control element and writes data to codec. + */ +static int nau8822_eq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + void *data; + u16 *val, value; + int i, reg, ret; + __be16 *tmp; + + data = kmemdup(ucontrol->value.bytes.data, + params->max, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + val = (u16 *)data; + reg = NAU8822_REG_EQ1; + for (i = 0; i < params->max / sizeof(u16); i++) { + /* conversion of 16-bit integers between native CPU format + * and big endian format + */ + tmp = (__be16 *)(val + i); + value = be16_to_cpup(tmp); + ret = snd_soc_component_write(component, reg + i, value); + if (ret) { + dev_err(component->dev, + "EQ configuration fail, register: %x ret: %d\n", + reg + i, ret); + kfree(data); + return ret; + } + } + kfree(data); + + return 0; +} + +static const char * const nau8822_companding[] = { + "Off", "NC", "u-law", "A-law"}; + +static const struct soc_enum nau8822_companding_adc_enum = + SOC_ENUM_SINGLE(NAU8822_REG_COMPANDING_CONTROL, NAU8822_ADCCM_SFT, + ARRAY_SIZE(nau8822_companding), nau8822_companding); + +static const struct soc_enum nau8822_companding_dac_enum = + SOC_ENUM_SINGLE(NAU8822_REG_COMPANDING_CONTROL, NAU8822_DACCM_SFT, + ARRAY_SIZE(nau8822_companding), nau8822_companding); + +static const char * const nau8822_eqmode[] = {"Capture", "Playback"}; + +static const struct soc_enum nau8822_eqmode_enum = + SOC_ENUM_SINGLE(NAU8822_REG_EQ1, NAU8822_EQM_SFT, + ARRAY_SIZE(nau8822_eqmode), nau8822_eqmode); + +static const char * const nau8822_alc1[] = {"Off", "Right", "Left", "Both"}; +static const char * const nau8822_alc3[] = {"Normal", "Limiter"}; + +static const struct soc_enum nau8822_alc_enable_enum = + SOC_ENUM_SINGLE(NAU8822_REG_ALC_CONTROL_1, NAU8822_ALCEN_SFT, + ARRAY_SIZE(nau8822_alc1), nau8822_alc1); + +static const struct soc_enum nau8822_alc_mode_enum = + SOC_ENUM_SINGLE(NAU8822_REG_ALC_CONTROL_3, NAU8822_ALCM_SFT, + ARRAY_SIZE(nau8822_alc3), nau8822_alc3); + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0); +static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1); +static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0); + +static const struct snd_kcontrol_new nau8822_snd_controls[] = { + SOC_ENUM("ADC Companding", nau8822_companding_adc_enum), + SOC_ENUM("DAC Companding", nau8822_companding_dac_enum), + + SOC_ENUM("EQ Function", nau8822_eqmode_enum), + SND_SOC_BYTES_EXT("EQ Parameters", 10, + nau8822_eq_get, nau8822_eq_put), + + SOC_DOUBLE("DAC Inversion Switch", + NAU8822_REG_DAC_CONTROL, 0, 1, 1, 0), + SOC_DOUBLE_R_TLV("PCM Volume", + NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME, + NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, 0, 255, 0, digital_tlv), + + SOC_SINGLE("High Pass Filter Switch", + NAU8822_REG_ADC_CONTROL, 8, 1, 0), + SOC_SINGLE("High Pass Cut Off", + NAU8822_REG_ADC_CONTROL, 4, 7, 0), + + SOC_DOUBLE("ADC Inversion Switch", + NAU8822_REG_ADC_CONTROL, 0, 1, 1, 0), + SOC_DOUBLE_R_TLV("ADC Volume", + NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME, + NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, 0, 255, 0, digital_tlv), + + SOC_SINGLE("DAC Limiter Switch", + NAU8822_REG_DAC_LIMITER_1, 8, 1, 0), + SOC_SINGLE("DAC Limiter Decay", + NAU8822_REG_DAC_LIMITER_1, 4, 15, 0), + SOC_SINGLE("DAC Limiter Attack", + NAU8822_REG_DAC_LIMITER_1, 0, 15, 0), + SOC_SINGLE("DAC Limiter Threshold", + NAU8822_REG_DAC_LIMITER_2, 4, 7, 0), + SOC_SINGLE_TLV("DAC Limiter Volume", + NAU8822_REG_DAC_LIMITER_2, 0, 12, 0, limiter_tlv), + + SOC_ENUM("ALC Mode", nau8822_alc_mode_enum), + SOC_ENUM("ALC Enable Switch", nau8822_alc_enable_enum), + SOC_SINGLE("ALC Min Gain", + NAU8822_REG_ALC_CONTROL_1, 0, 7, 0), + SOC_SINGLE("ALC Max Gain", + NAU8822_REG_ALC_CONTROL_1, 3, 7, 0), + SOC_SINGLE("ALC Hold", + NAU8822_REG_ALC_CONTROL_2, 4, 10, 0), + SOC_SINGLE("ALC Target", + NAU8822_REG_ALC_CONTROL_2, 0, 15, 0), + SOC_SINGLE("ALC Decay", + NAU8822_REG_ALC_CONTROL_3, 4, 10, 0), + SOC_SINGLE("ALC Attack", + NAU8822_REG_ALC_CONTROL_3, 0, 10, 0), + SOC_SINGLE("ALC Noise Gate Switch", + NAU8822_REG_NOISE_GATE, 3, 1, 0), + SOC_SINGLE("ALC Noise Gate Threshold", + NAU8822_REG_NOISE_GATE, 0, 7, 0), + + SOC_DOUBLE_R("PGA ZC Switch", + NAU8822_REG_LEFT_INP_PGA_CONTROL, + NAU8822_REG_RIGHT_INP_PGA_CONTROL, + 7, 1, 0), + SOC_DOUBLE_R_TLV("PGA Volume", + NAU8822_REG_LEFT_INP_PGA_CONTROL, + NAU8822_REG_RIGHT_INP_PGA_CONTROL, 0, 63, 0, inpga_tlv), + + SOC_DOUBLE_R("Headphone ZC Switch", + NAU8822_REG_LHP_VOLUME, + NAU8822_REG_RHP_VOLUME, 7, 1, 0), + SOC_DOUBLE_R("Headphone Playback Switch", + NAU8822_REG_LHP_VOLUME, + NAU8822_REG_RHP_VOLUME, 6, 1, 1), + SOC_DOUBLE_R_TLV("Headphone Volume", + NAU8822_REG_LHP_VOLUME, + NAU8822_REG_RHP_VOLUME, 0, 63, 0, spk_tlv), + + SOC_DOUBLE_R("Speaker ZC Switch", + NAU8822_REG_LSPKOUT_VOLUME, + NAU8822_REG_RSPKOUT_VOLUME, 7, 1, 0), + SOC_DOUBLE_R("Speaker Playback Switch", + NAU8822_REG_LSPKOUT_VOLUME, + NAU8822_REG_RSPKOUT_VOLUME, 6, 1, 1), + SOC_DOUBLE_R_TLV("Speaker Volume", + NAU8822_REG_LSPKOUT_VOLUME, + NAU8822_REG_RSPKOUT_VOLUME, 0, 63, 0, spk_tlv), + + SOC_DOUBLE_R("AUXOUT Playback Switch", + NAU8822_REG_AUX2_MIXER, + NAU8822_REG_AUX1_MIXER, 6, 1, 1), + + SOC_DOUBLE_R_TLV("PGA Boost Volume", + NAU8822_REG_LEFT_ADC_BOOST_CONTROL, + NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 8, 1, 0, pga_boost_tlv), + SOC_DOUBLE_R_TLV("L2/R2 Boost Volume", + NAU8822_REG_LEFT_ADC_BOOST_CONTROL, + NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 4, 7, 0, boost_tlv), + SOC_DOUBLE_R_TLV("Aux Boost Volume", + NAU8822_REG_LEFT_ADC_BOOST_CONTROL, + NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0, 7, 0, boost_tlv), + + SOC_SINGLE("DAC 128x Oversampling Switch", + NAU8822_REG_DAC_CONTROL, 5, 1, 0), + SOC_SINGLE("ADC 128x Oversampling Switch", + NAU8822_REG_ADC_CONTROL, 5, 1, 0), +}; + +/* LMAIN and RMAIN Mixer */ +static const struct snd_kcontrol_new nau8822_left_out_mixer[] = { + SOC_DAPM_SINGLE("LINMIX Switch", + NAU8822_REG_LEFT_MIXER_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("LAUX Switch", + NAU8822_REG_LEFT_MIXER_CONTROL, 5, 1, 0), + SOC_DAPM_SINGLE("LDAC Switch", + NAU8822_REG_LEFT_MIXER_CONTROL, 0, 1, 0), + SOC_DAPM_SINGLE("RDAC Switch", + NAU8822_REG_OUTPUT_CONTROL, 5, 1, 0), +}; + +static const struct snd_kcontrol_new nau8822_right_out_mixer[] = { + SOC_DAPM_SINGLE("RINMIX Switch", + NAU8822_REG_RIGHT_MIXER_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("RAUX Switch", + NAU8822_REG_RIGHT_MIXER_CONTROL, 5, 1, 0), + SOC_DAPM_SINGLE("RDAC Switch", + NAU8822_REG_RIGHT_MIXER_CONTROL, 0, 1, 0), + SOC_DAPM_SINGLE("LDAC Switch", + NAU8822_REG_OUTPUT_CONTROL, 6, 1, 0), +}; + +/* AUX1 and AUX2 Mixer */ +static const struct snd_kcontrol_new nau8822_auxout1_mixer[] = { + SOC_DAPM_SINGLE("RDAC Switch", NAU8822_REG_AUX1_MIXER, 0, 1, 0), + SOC_DAPM_SINGLE("RMIX Switch", NAU8822_REG_AUX1_MIXER, 1, 1, 0), + SOC_DAPM_SINGLE("RINMIX Switch", NAU8822_REG_AUX1_MIXER, 2, 1, 0), + SOC_DAPM_SINGLE("LDAC Switch", NAU8822_REG_AUX1_MIXER, 3, 1, 0), + SOC_DAPM_SINGLE("LMIX Switch", NAU8822_REG_AUX1_MIXER, 4, 1, 0), +}; + +static const struct snd_kcontrol_new nau8822_auxout2_mixer[] = { + SOC_DAPM_SINGLE("LDAC Switch", NAU8822_REG_AUX2_MIXER, 0, 1, 0), + SOC_DAPM_SINGLE("LMIX Switch", NAU8822_REG_AUX2_MIXER, 1, 1, 0), + SOC_DAPM_SINGLE("LINMIX Switch", NAU8822_REG_AUX2_MIXER, 2, 1, 0), + SOC_DAPM_SINGLE("AUX1MIX Output Switch", + NAU8822_REG_AUX2_MIXER, 3, 1, 0), +}; + +/* Input PGA */ +static const struct snd_kcontrol_new nau8822_left_input_mixer[] = { + SOC_DAPM_SINGLE("L2 Switch", NAU8822_REG_INPUT_CONTROL, 2, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 0, 1, 0), +}; +static const struct snd_kcontrol_new nau8822_right_input_mixer[] = { + SOC_DAPM_SINGLE("R2 Switch", NAU8822_REG_INPUT_CONTROL, 6, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 5, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 4, 1, 0), +}; + +/* Loopback Switch */ +static const struct snd_kcontrol_new nau8822_loopback = + SOC_DAPM_SINGLE("Switch", NAU8822_REG_COMPANDING_CONTROL, + NAU8822_ADDAP_SFT, 1, 0); + +static int check_mclk_select_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + unsigned int value; + + value = snd_soc_component_read(component, NAU8822_REG_CLOCKING); + + return (value & NAU8822_CLKM_MASK); +} + +static const struct snd_soc_dapm_widget nau8822_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", + NAU8822_REG_POWER_MANAGEMENT_3, 0, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", + NAU8822_REG_POWER_MANAGEMENT_3, 1, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", + NAU8822_REG_POWER_MANAGEMENT_2, 0, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", + NAU8822_REG_POWER_MANAGEMENT_2, 1, 0), + + SOC_MIXER_ARRAY("Left Output Mixer", + NAU8822_REG_POWER_MANAGEMENT_3, 2, 0, nau8822_left_out_mixer), + SOC_MIXER_ARRAY("Right Output Mixer", + NAU8822_REG_POWER_MANAGEMENT_3, 3, 0, nau8822_right_out_mixer), + SOC_MIXER_ARRAY("AUX1 Output Mixer", + NAU8822_REG_POWER_MANAGEMENT_1, 7, 0, nau8822_auxout1_mixer), + SOC_MIXER_ARRAY("AUX2 Output Mixer", + NAU8822_REG_POWER_MANAGEMENT_1, 6, 0, nau8822_auxout2_mixer), + + SOC_MIXER_ARRAY("Left Input Mixer", + NAU8822_REG_POWER_MANAGEMENT_2, + 2, 0, nau8822_left_input_mixer), + SOC_MIXER_ARRAY("Right Input Mixer", + NAU8822_REG_POWER_MANAGEMENT_2, + 3, 0, nau8822_right_input_mixer), + + SND_SOC_DAPM_PGA("Left Boost Mixer", + NAU8822_REG_POWER_MANAGEMENT_2, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Boost Mixer", + NAU8822_REG_POWER_MANAGEMENT_2, 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Left Capture PGA", + NAU8822_REG_LEFT_INP_PGA_CONTROL, 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right Capture PGA", + NAU8822_REG_RIGHT_INP_PGA_CONTROL, 6, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Left Headphone Out", + NAU8822_REG_POWER_MANAGEMENT_2, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Headphone Out", + NAU8822_REG_POWER_MANAGEMENT_2, 8, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Left Speaker Out", + NAU8822_REG_POWER_MANAGEMENT_3, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Speaker Out", + NAU8822_REG_POWER_MANAGEMENT_3, 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("AUX1 Out", + NAU8822_REG_POWER_MANAGEMENT_3, 8, 0, NULL, 0), + SND_SOC_DAPM_PGA("AUX2 Out", + NAU8822_REG_POWER_MANAGEMENT_3, 7, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", + NAU8822_REG_POWER_MANAGEMENT_1, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL", + NAU8822_REG_POWER_MANAGEMENT_1, 5, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0, + &nau8822_loopback), + + SND_SOC_DAPM_INPUT("LMICN"), + SND_SOC_DAPM_INPUT("LMICP"), + SND_SOC_DAPM_INPUT("RMICN"), + SND_SOC_DAPM_INPUT("RMICP"), + SND_SOC_DAPM_INPUT("LAUX"), + SND_SOC_DAPM_INPUT("RAUX"), + SND_SOC_DAPM_INPUT("L2"), + SND_SOC_DAPM_INPUT("R2"), + SND_SOC_DAPM_OUTPUT("LHP"), + SND_SOC_DAPM_OUTPUT("RHP"), + SND_SOC_DAPM_OUTPUT("LSPK"), + SND_SOC_DAPM_OUTPUT("RSPK"), + SND_SOC_DAPM_OUTPUT("AUXOUT1"), + SND_SOC_DAPM_OUTPUT("AUXOUT2"), +}; + +static const struct snd_soc_dapm_route nau8822_dapm_routes[] = { + {"Right DAC", NULL, "PLL", check_mclk_select_pll}, + {"Left DAC", NULL, "PLL", check_mclk_select_pll}, + + /* LMAIN and RMAIN Mixer */ + {"Right Output Mixer", "LDAC Switch", "Left DAC"}, + {"Right Output Mixer", "RDAC Switch", "Right DAC"}, + {"Right Output Mixer", "RAUX Switch", "RAUX"}, + {"Right Output Mixer", "RINMIX Switch", "Right Boost Mixer"}, + + {"Left Output Mixer", "LDAC Switch", "Left DAC"}, + {"Left Output Mixer", "RDAC Switch", "Right DAC"}, + {"Left Output Mixer", "LAUX Switch", "LAUX"}, + {"Left Output Mixer", "LINMIX Switch", "Left Boost Mixer"}, + + /* AUX1 and AUX2 Mixer */ + {"AUX1 Output Mixer", "RDAC Switch", "Right DAC"}, + {"AUX1 Output Mixer", "RMIX Switch", "Right Output Mixer"}, + {"AUX1 Output Mixer", "RINMIX Switch", "Right Boost Mixer"}, + {"AUX1 Output Mixer", "LDAC Switch", "Left DAC"}, + {"AUX1 Output Mixer", "LMIX Switch", "Left Output Mixer"}, + + {"AUX2 Output Mixer", "LDAC Switch", "Left DAC"}, + {"AUX2 Output Mixer", "LMIX Switch", "Left Output Mixer"}, + {"AUX2 Output Mixer", "LINMIX Switch", "Left Boost Mixer"}, + {"AUX2 Output Mixer", "AUX1MIX Output Switch", "AUX1 Output Mixer"}, + + /* Outputs */ + {"Right Headphone Out", NULL, "Right Output Mixer"}, + {"RHP", NULL, "Right Headphone Out"}, + + {"Left Headphone Out", NULL, "Left Output Mixer"}, + {"LHP", NULL, "Left Headphone Out"}, + + {"Right Speaker Out", NULL, "Right Output Mixer"}, + {"RSPK", NULL, "Right Speaker Out"}, + + {"Left Speaker Out", NULL, "Left Output Mixer"}, + {"LSPK", NULL, "Left Speaker Out"}, + + {"AUX1 Out", NULL, "AUX1 Output Mixer"}, + {"AUX2 Out", NULL, "AUX2 Output Mixer"}, + {"AUXOUT1", NULL, "AUX1 Out"}, + {"AUXOUT2", NULL, "AUX2 Out"}, + + /* Boost Mixer */ + {"Right ADC", NULL, "PLL", check_mclk_select_pll}, + {"Left ADC", NULL, "PLL", check_mclk_select_pll}, + + {"Right ADC", NULL, "Right Boost Mixer"}, + + {"Right Boost Mixer", NULL, "RAUX"}, + {"Right Boost Mixer", NULL, "Right Capture PGA"}, + {"Right Boost Mixer", NULL, "R2"}, + + {"Left ADC", NULL, "Left Boost Mixer"}, + + {"Left Boost Mixer", NULL, "LAUX"}, + {"Left Boost Mixer", NULL, "Left Capture PGA"}, + {"Left Boost Mixer", NULL, "L2"}, + + /* Input PGA */ + {"Right Capture PGA", NULL, "Right Input Mixer"}, + {"Left Capture PGA", NULL, "Left Input Mixer"}, + + /* Enable Microphone Power */ + {"Right Capture PGA", NULL, "Mic Bias"}, + {"Left Capture PGA", NULL, "Mic Bias"}, + + {"Right Input Mixer", "R2 Switch", "R2"}, + {"Right Input Mixer", "MicN Switch", "RMICN"}, + {"Right Input Mixer", "MicP Switch", "RMICP"}, + + {"Left Input Mixer", "L2 Switch", "L2"}, + {"Left Input Mixer", "MicN Switch", "LMICN"}, + {"Left Input Mixer", "MicP Switch", "LMICP"}, + + /* Digital Loopback */ + {"Digital Loopback", "Switch", "Left ADC"}, + {"Digital Loopback", "Switch", "Right ADC"}, + {"Left DAC", NULL, "Digital Loopback"}, + {"Right DAC", NULL, "Digital Loopback"}, +}; + +static int nau8822_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + + nau8822->div_id = clk_id; + nau8822->sysclk = freq; + dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq, + clk_id == NAU8822_CLK_PLL ? "PLL" : "MCLK"); + + return 0; +} + +static int nau8822_calc_pll(unsigned int pll_in, unsigned int fs, + struct nau8822_pll *pll_param) +{ + u64 f2, f2_max, pll_ratio; + int i, scal_sel; + + if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN) + return -EINVAL; + f2_max = 0; + scal_sel = ARRAY_SIZE(nau8822_mclk_scaler); + + for (i = 0; i < scal_sel; i++) { + f2 = 256 * fs * 4 * nau8822_mclk_scaler[i] / 10; + if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX && + f2_max < f2) { + f2_max = f2; + scal_sel = i; + } + } + + if (ARRAY_SIZE(nau8822_mclk_scaler) == scal_sel) + return -EINVAL; + pll_param->mclk_scaler = scal_sel; + f2 = f2_max; + + /* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional + * input; round up the 24+4bit. + */ + pll_ratio = div_u64(f2 << 28, pll_in); + pll_param->pre_factor = 0; + if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) { + pll_ratio <<= 1; + pll_param->pre_factor = 1; + } + pll_param->pll_int = (pll_ratio >> 28) & 0xF; + pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4); + + return 0; +} + +static int nau8822_config_clkdiv(struct snd_soc_dai *dai, int div, int rate) +{ + struct snd_soc_component *component = dai->component; + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + struct nau8822_pll *pll = &nau8822->pll; + int i, sclk, imclk; + + switch (nau8822->div_id) { + case NAU8822_CLK_MCLK: + /* Configure the master clock prescaler div to make system + * clock to approximate the internal master clock (IMCLK); + * and large or equal to IMCLK. + */ + div = 0; + imclk = rate * 256; + for (i = 1; i < ARRAY_SIZE(nau8822_mclk_scaler); i++) { + sclk = (nau8822->sysclk * 10) / nau8822_mclk_scaler[i]; + if (sclk < imclk) + break; + div = i; + } + dev_dbg(component->dev, "master clock prescaler %x for fs %d\n", + div, rate); + + /* master clock from MCLK and disable PLL */ + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK, + (div << NAU8822_MCLKSEL_SFT)); + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK, + NAU8822_CLKM_MCLK); + break; + + case NAU8822_CLK_PLL: + /* master clock from PLL and enable PLL */ + if (pll->mclk_scaler != div) { + dev_err(component->dev, + "master clock prescaler not meet PLL parameters\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK, + (div << NAU8822_MCLKSEL_SFT)); + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK, + NAU8822_CLKM_PLL); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int nau8822_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + struct nau8822_pll *pll_param = &nau8822->pll; + int ret, fs; + + if (freq_in == pll_param->freq_in && + freq_out == pll_param->freq_out) + return 0; + + if (freq_out == 0) { + dev_dbg(component->dev, "PLL disabled\n"); + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_PLL_EN_MASK, NAU8822_PLL_OFF); + return 0; + } + + fs = freq_out / 256; + + ret = nau8822_calc_pll(freq_in, fs, pll_param); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", + freq_in); + return ret; + } + + dev_info(component->dev, + "pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n", + pll_param->pll_int, pll_param->pll_frac, + pll_param->mclk_scaler, pll_param->pre_factor); + + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_PLL_EN_MASK, NAU8822_PLL_OFF); + snd_soc_component_update_bits(component, + NAU8822_REG_PLL_N, NAU8822_PLLMCLK_DIV2 | NAU8822_PLLN_MASK, + (pll_param->pre_factor ? NAU8822_PLLMCLK_DIV2 : 0) | + pll_param->pll_int); + snd_soc_component_write(component, + NAU8822_REG_PLL_K1, (pll_param->pll_frac >> NAU8822_PLLK1_SFT) & + NAU8822_PLLK1_MASK); + snd_soc_component_write(component, + NAU8822_REG_PLL_K2, (pll_param->pll_frac >> NAU8822_PLLK2_SFT) & + NAU8822_PLLK2_MASK); + snd_soc_component_write(component, + NAU8822_REG_PLL_K3, pll_param->pll_frac & NAU8822_PLLK3_MASK); + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK, + pll_param->mclk_scaler << NAU8822_MCLKSEL_SFT); + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK, NAU8822_CLKM_PLL); + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, NAU8822_PLL_EN_MASK, NAU8822_PLL_ON); + + pll_param->freq_in = freq_in; + pll_param->freq_out = freq_out; + + return 0; +} + +static int nau8822_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u16 ctrl1_val = 0, ctrl2_val = 0; + + dev_dbg(component->dev, "%s\n", __func__); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + ctrl2_val &= ~1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= 0x10; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= 0x8; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= 0x18; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl1_val |= 0x180; + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= 0x100; + break; + case SND_SOC_DAIFMT_NB_IF: + ctrl1_val |= 0x80; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, + NAU8822_REG_AUDIO_INTERFACE, + NAU8822_AIFMT_MASK | NAU8822_LRP_MASK | NAU8822_BCLKP_MASK, + ctrl1_val); + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_CLKIOEN_MASK, ctrl2_val); + + return 0; +} + +static int nau8822_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + int val_len = 0, val_rate = 0; + unsigned int ctrl_val, bclk_fs, bclk_div; + + /* make BCLK and LRC divide configuration if the codec as master. */ + ctrl_val = snd_soc_component_read(component, NAU8822_REG_CLOCKING); + if (ctrl_val & NAU8822_CLK_MASTER) { + /* get the bclk and fs ratio */ + bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params); + if (bclk_fs <= 32) + bclk_div = NAU8822_BCLKDIV_8; + else if (bclk_fs <= 64) + bclk_div = NAU8822_BCLKDIV_4; + else if (bclk_fs <= 128) + bclk_div = NAU8822_BCLKDIV_2; + else + return -EINVAL; + snd_soc_component_update_bits(component, NAU8822_REG_CLOCKING, + NAU8822_BCLKSEL_MASK, bclk_div); + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val_len |= NAU8822_WLEN_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val_len |= NAU8822_WLEN_24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + val_len |= NAU8822_WLEN_32; + break; + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 8000: + val_rate |= NAU8822_SMPLR_8K; + break; + case 11025: + val_rate |= NAU8822_SMPLR_12K; + break; + case 16000: + val_rate |= NAU8822_SMPLR_16K; + break; + case 22050: + val_rate |= NAU8822_SMPLR_24K; + break; + case 32000: + val_rate |= NAU8822_SMPLR_32K; + break; + case 44100: + case 48000: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, + NAU8822_REG_AUDIO_INTERFACE, NAU8822_WLEN_MASK, val_len); + snd_soc_component_update_bits(component, + NAU8822_REG_ADDITIONAL_CONTROL, NAU8822_SMPLR_MASK, val_rate); + + /* If the master clock is from MCLK, provide the runtime FS for driver + * to get the master clock prescaler configuration. + */ + if (nau8822->div_id == NAU8822_CLK_MCLK) + nau8822_config_clkdiv(dai, 0, params_rate(params)); + + return 0; +} + +static int nau8822_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + dev_dbg(component->dev, "%s: %d\n", __func__, mute); + + if (mute) + snd_soc_component_update_bits(component, + NAU8822_REG_DAC_CONTROL, 0x40, 0x40); + else + snd_soc_component_update_bits(component, + NAU8822_REG_DAC_CONTROL, 0x40, 0); + + return 0; +} + +static int nau8822_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, + NAU8822_REFIMP_MASK, NAU8822_REFIMP_80K); + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, + NAU8822_IOBUF_EN | NAU8822_ABIAS_EN, + NAU8822_IOBUF_EN | NAU8822_ABIAS_EN); + + if (snd_soc_component_get_bias_level(component) == + SND_SOC_BIAS_OFF) { + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, + NAU8822_REFIMP_MASK, NAU8822_REFIMP_3K); + mdelay(100); + } + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, + NAU8822_REFIMP_MASK, NAU8822_REFIMP_300K); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, + NAU8822_REG_POWER_MANAGEMENT_1, 0); + snd_soc_component_write(component, + NAU8822_REG_POWER_MANAGEMENT_2, 0); + snd_soc_component_write(component, + NAU8822_REG_POWER_MANAGEMENT_3, 0); + break; + } + + dev_dbg(component->dev, "%s: %d\n", __func__, level); + + return 0; +} + +#define NAU8822_RATES (SNDRV_PCM_RATE_8000_48000) + +#define NAU8822_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops nau8822_dai_ops = { + .hw_params = nau8822_hw_params, + .mute_stream = nau8822_mute, + .set_fmt = nau8822_set_dai_fmt, + .set_sysclk = nau8822_set_dai_sysclk, + .set_pll = nau8822_set_pll, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver nau8822_dai = { + .name = "nau8822-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8822_RATES, + .formats = NAU8822_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8822_RATES, + .formats = NAU8822_FORMATS, + }, + .ops = &nau8822_dai_ops, + .symmetric_rate = 1, +}; + +static int nau8822_suspend(struct snd_soc_component *component) +{ + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + regcache_mark_dirty(nau8822->regmap); + + return 0; +} + +static int nau8822_resume(struct snd_soc_component *component) +{ + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + + regcache_sync(nau8822->regmap); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + return 0; +} + +/* + * These registers contain an "update" bit - bit 8. This means, for example, + * that one can write new DAC digital volume for both channels, but only when + * the update bit is set, will also the volume be updated - simultaneously for + * both channels. + */ +static const int update_reg[] = { + NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME, + NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, + NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME, + NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, + NAU8822_REG_LEFT_INP_PGA_CONTROL, + NAU8822_REG_RIGHT_INP_PGA_CONTROL, + NAU8822_REG_LHP_VOLUME, + NAU8822_REG_RHP_VOLUME, + NAU8822_REG_LSPKOUT_VOLUME, + NAU8822_REG_RSPKOUT_VOLUME, +}; + +static int nau8822_probe(struct snd_soc_component *component) +{ + int i; + struct device_node *of_node = component->dev->of_node; + + /* + * Set the update bit in all registers, that have one. This way all + * writes to those registers will also cause the update bit to be + * written. + */ + for (i = 0; i < ARRAY_SIZE(update_reg); i++) + snd_soc_component_update_bits(component, + update_reg[i], 0x100, 0x100); + + /* Check property to configure the two loudspeaker outputs as + * a single Bridge Tied Load output + */ + if (of_property_read_bool(of_node, "nuvoton,spk-btl")) + snd_soc_component_update_bits(component, + NAU8822_REG_RIGHT_SPEAKER_CONTROL, + NAU8822_RSUBBYP, NAU8822_RSUBBYP); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_nau8822 = { + .probe = nau8822_probe, + .suspend = nau8822_suspend, + .resume = nau8822_resume, + .set_bias_level = nau8822_set_bias_level, + .controls = nau8822_snd_controls, + .num_controls = ARRAY_SIZE(nau8822_snd_controls), + .dapm_widgets = nau8822_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8822_dapm_widgets), + .dapm_routes = nau8822_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8822_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config nau8822_regmap_config = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = NAU8822_REG_MAX_REGISTER, + .volatile_reg = nau8822_volatile, + + .readable_reg = nau8822_readable_reg, + .writeable_reg = nau8822_writeable_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8822_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8822_reg_defaults), +}; + +static int nau8822_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct nau8822 *nau8822 = dev_get_platdata(dev); + int ret; + + if (!nau8822) { + nau8822 = devm_kzalloc(dev, sizeof(*nau8822), GFP_KERNEL); + if (nau8822 == NULL) + return -ENOMEM; + } + i2c_set_clientdata(i2c, nau8822); + + nau8822->regmap = devm_regmap_init_i2c(i2c, &nau8822_regmap_config); + if (IS_ERR(nau8822->regmap)) { + ret = PTR_ERR(nau8822->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + nau8822->dev = dev; + + /* Reset the codec */ + ret = regmap_write(nau8822->regmap, NAU8822_REG_RESET, 0x00); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_nau8822, + &nau8822_dai, 1); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id nau8822_i2c_id[] = { + { "nau8822", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau8822_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id nau8822_of_match[] = { + { .compatible = "nuvoton,nau8822", }, + { } +}; +MODULE_DEVICE_TABLE(of, nau8822_of_match); +#endif + +static struct i2c_driver nau8822_i2c_driver = { + .driver = { + .name = "nau8822", + .of_match_table = of_match_ptr(nau8822_of_match), + }, + .probe = nau8822_i2c_probe, + .id_table = nau8822_i2c_id, +}; +module_i2c_driver(nau8822_i2c_driver); + +MODULE_DESCRIPTION("ASoC NAU8822 codec driver"); +MODULE_AUTHOR("David Lin "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h new file mode 100644 index 0000000000..646f6bb64b --- /dev/null +++ b/sound/soc/codecs/nau8822.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * nau8822.h -- NAU8822 ALSA SoC Audio driver + * + * Copyright 2017 Nuvoton Technology Crop. + * + * Author: David Lin + * Co-author: John Hsu + * Co-author: Seven Li + */ + +#ifndef __NAU8822_H__ +#define __NAU8822_H__ + +#define NAU8822_REG_RESET 0x00 +#define NAU8822_REG_POWER_MANAGEMENT_1 0x01 +#define NAU8822_REG_POWER_MANAGEMENT_2 0x02 +#define NAU8822_REG_POWER_MANAGEMENT_3 0x03 +#define NAU8822_REG_AUDIO_INTERFACE 0x04 +#define NAU8822_REG_COMPANDING_CONTROL 0x05 +#define NAU8822_REG_CLOCKING 0x06 +#define NAU8822_REG_ADDITIONAL_CONTROL 0x07 +#define NAU8822_REG_GPIO_CONTROL 0x08 +#define NAU8822_REG_JACK_DETECT_CONTROL_1 0x09 +#define NAU8822_REG_DAC_CONTROL 0x0A +#define NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME 0x0B +#define NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME 0x0C +#define NAU8822_REG_JACK_DETECT_CONTROL_2 0x0D +#define NAU8822_REG_ADC_CONTROL 0x0E +#define NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME 0x0F +#define NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME 0x10 +#define NAU8822_REG_EQ1 0x12 +#define NAU8822_REG_EQ2 0x13 +#define NAU8822_REG_EQ3 0x14 +#define NAU8822_REG_EQ4 0x15 +#define NAU8822_REG_EQ5 0x16 +#define NAU8822_REG_DAC_LIMITER_1 0x18 +#define NAU8822_REG_DAC_LIMITER_2 0x19 +#define NAU8822_REG_NOTCH_FILTER_1 0x1B +#define NAU8822_REG_NOTCH_FILTER_2 0x1C +#define NAU8822_REG_NOTCH_FILTER_3 0x1D +#define NAU8822_REG_NOTCH_FILTER_4 0x1E +#define NAU8822_REG_ALC_CONTROL_1 0x20 +#define NAU8822_REG_ALC_CONTROL_2 0x21 +#define NAU8822_REG_ALC_CONTROL_3 0x22 +#define NAU8822_REG_NOISE_GATE 0x23 +#define NAU8822_REG_PLL_N 0x24 +#define NAU8822_REG_PLL_K1 0x25 +#define NAU8822_REG_PLL_K2 0x26 +#define NAU8822_REG_PLL_K3 0x27 +#define NAU8822_REG_3D_CONTROL 0x29 +#define NAU8822_REG_RIGHT_SPEAKER_CONTROL 0x2B +#define NAU8822_REG_INPUT_CONTROL 0x2C +#define NAU8822_REG_LEFT_INP_PGA_CONTROL 0x2D +#define NAU8822_REG_RIGHT_INP_PGA_CONTROL 0x2E +#define NAU8822_REG_LEFT_ADC_BOOST_CONTROL 0x2F +#define NAU8822_REG_RIGHT_ADC_BOOST_CONTROL 0x30 +#define NAU8822_REG_OUTPUT_CONTROL 0x31 +#define NAU8822_REG_LEFT_MIXER_CONTROL 0x32 +#define NAU8822_REG_RIGHT_MIXER_CONTROL 0x33 +#define NAU8822_REG_LHP_VOLUME 0x34 +#define NAU8822_REG_RHP_VOLUME 0x35 +#define NAU8822_REG_LSPKOUT_VOLUME 0x36 +#define NAU8822_REG_RSPKOUT_VOLUME 0x37 +#define NAU8822_REG_AUX2_MIXER 0x38 +#define NAU8822_REG_AUX1_MIXER 0x39 +#define NAU8822_REG_POWER_MANAGEMENT_4 0x3A +#define NAU8822_REG_LEFT_TIME_SLOT 0x3B +#define NAU8822_REG_MISC 0x3C +#define NAU8822_REG_RIGHT_TIME_SLOT 0x3D +#define NAU8822_REG_DEVICE_REVISION 0x3E +#define NAU8822_REG_DEVICE_ID 0x3F +#define NAU8822_REG_DAC_DITHER 0x41 +#define NAU8822_REG_ALC_ENHANCE_1 0x46 +#define NAU8822_REG_ALC_ENHANCE_2 0x47 +#define NAU8822_REG_192KHZ_SAMPLING 0x48 +#define NAU8822_REG_MISC_CONTROL 0x49 +#define NAU8822_REG_INPUT_TIEOFF 0x4A +#define NAU8822_REG_POWER_REDUCTION 0x4B +#define NAU8822_REG_AGC_PEAK2PEAK 0x4C +#define NAU8822_REG_AGC_PEAK_DETECT 0x4D +#define NAU8822_REG_AUTOMUTE_CONTROL 0x4E +#define NAU8822_REG_OUTPUT_TIEOFF 0x4F +#define NAU8822_REG_MAX_REGISTER NAU8822_REG_OUTPUT_TIEOFF + +/* NAU8822_REG_POWER_MANAGEMENT_1 (0x1) */ +#define NAU8822_REFIMP_MASK 0x3 +#define NAU8822_REFIMP_80K 0x1 +#define NAU8822_REFIMP_300K 0x2 +#define NAU8822_REFIMP_3K 0x3 +#define NAU8822_IOBUF_EN (0x1 << 2) +#define NAU8822_ABIAS_EN (0x1 << 3) +#define NAU8822_PLL_EN_MASK (0x1 << 5) +#define NAU8822_PLL_ON (0x1 << 5) +#define NAU8822_PLL_OFF (0x0 << 5) + +/* NAU8822_REG_AUDIO_INTERFACE (0x4) */ +#define NAU8822_AIFMT_MASK (0x3 << 3) +#define NAU8822_WLEN_MASK (0x3 << 5) +#define NAU8822_WLEN_20 (0x1 << 5) +#define NAU8822_WLEN_24 (0x2 << 5) +#define NAU8822_WLEN_32 (0x3 << 5) +#define NAU8822_LRP_MASK (0x1 << 7) +#define NAU8822_BCLKP_MASK (0x1 << 8) + +/* NAU8822_REG_COMPANDING_CONTROL (0x5) */ +#define NAU8822_ADDAP_SFT 0 +#define NAU8822_ADCCM_SFT 1 +#define NAU8822_DACCM_SFT 3 + +/* NAU8822_REG_CLOCKING (0x6) */ +#define NAU8822_CLKIOEN_MASK 0x1 +#define NAU8822_CLK_MASTER 0x1 +#define NAU8822_CLK_SLAVE 0x0 +#define NAU8822_MCLKSEL_SFT 5 +#define NAU8822_MCLKSEL_MASK (0x7 << 5) +#define NAU8822_BCLKSEL_SFT 2 +#define NAU8822_BCLKSEL_MASK (0x7 << 2) +#define NAU8822_BCLKDIV_1 (0x0 << 2) +#define NAU8822_BCLKDIV_2 (0x1 << 2) +#define NAU8822_BCLKDIV_4 (0x2 << 2) +#define NAU8822_BCLKDIV_8 (0x3 << 2) +#define NAU8822_BCLKDIV_16 (0x4 << 2) +#define NAU8822_CLKM_MASK (0x1 << 8) +#define NAU8822_CLKM_MCLK (0x0 << 8) +#define NAU8822_CLKM_PLL (0x1 << 8) + +/* NAU8822_REG_ADDITIONAL_CONTROL (0x08) */ +#define NAU8822_SMPLR_SFT 1 +#define NAU8822_SMPLR_MASK (0x7 << 1) +#define NAU8822_SMPLR_48K (0x0 << 1) +#define NAU8822_SMPLR_32K (0x1 << 1) +#define NAU8822_SMPLR_24K (0x2 << 1) +#define NAU8822_SMPLR_16K (0x3 << 1) +#define NAU8822_SMPLR_12K (0x4 << 1) +#define NAU8822_SMPLR_8K (0x5 << 1) + +/* NAU8822_REG_EQ1 (0x12) */ +#define NAU8822_EQ1GC_SFT 0 +#define NAU8822_EQ1CF_SFT 5 +#define NAU8822_EQM_SFT 8 + +/* NAU8822_REG_EQ2 (0x13) */ +#define NAU8822_EQ2GC_SFT 0 +#define NAU8822_EQ2CF_SFT 5 +#define NAU8822_EQ2BW_SFT 8 + +/* NAU8822_REG_EQ3 (0x14) */ +#define NAU8822_EQ3GC_SFT 0 +#define NAU8822_EQ3CF_SFT 5 +#define NAU8822_EQ3BW_SFT 8 + +/* NAU8822_REG_EQ4 (0x15) */ +#define NAU8822_EQ4GC_SFT 0 +#define NAU8822_EQ4CF_SFT 5 +#define NAU8822_EQ4BW_SFT 8 + +/* NAU8822_REG_EQ5 (0x16) */ +#define NAU8822_EQ5GC_SFT 0 +#define NAU8822_EQ5CF_SFT 5 + +/* NAU8822_REG_ALC_CONTROL_1 (0x20) */ +#define NAU8822_ALCMINGAIN_SFT 0 +#define NAU8822_ALCMXGAIN_SFT 3 +#define NAU8822_ALCEN_SFT 7 + +/* NAU8822_REG_ALC_CONTROL_2 (0x21) */ +#define NAU8822_ALCSL_SFT 0 +#define NAU8822_ALCHT_SFT 4 + +/* NAU8822_REG_ALC_CONTROL_3 (0x22) */ +#define NAU8822_ALCATK_SFT 0 +#define NAU8822_ALCDCY_SFT 4 +#define NAU8822_ALCM_SFT 8 + +/* NAU8822_REG_PLL_N (0x24) */ +#define NAU8822_PLLMCLK_DIV2 (0x1 << 4) +#define NAU8822_PLLN_MASK 0xF + +#define NAU8822_PLLK1_SFT 18 +#define NAU8822_PLLK1_MASK 0x3F + +/* NAU8822_REG_PLL_K2 (0x26) */ +#define NAU8822_PLLK2_SFT 9 +#define NAU8822_PLLK2_MASK 0x1FF + +/* NAU8822_REG_PLL_K3 (0x27) */ +#define NAU8822_PLLK3_MASK 0x1FF + +/* NAU8822_REG_RIGHT_SPEAKER_CONTROL (0x2B) */ +#define NAU8822_RMIXMUT 0x20 +#define NAU8822_RSUBBYP 0x10 + +#define NAU8822_RAUXRSUBG_SFT 1 +#define NAU8822_RAUXRSUBG_MASK 0x0E + +#define NAU8822_RAUXSMUT 0x01 + +/* System Clock Source */ +enum { + NAU8822_CLK_MCLK, + NAU8822_CLK_PLL, +}; + +struct nau8822_pll { + int pre_factor; + int mclk_scaler; + int pll_frac; + int pll_int; + int freq_in; + int freq_out; +}; + +/* Codec Private Data */ +struct nau8822 { + struct device *dev; + struct regmap *regmap; + int mclk_idx; + struct nau8822_pll pll; + int sysclk; + int div_id; +}; + +#endif /* __NAU8822_H__ */ diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c new file mode 100644 index 0000000000..704af1cf8c --- /dev/null +++ b/sound/soc/codecs/nau8824.c @@ -0,0 +1,2041 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NAU88L24 ALSA SoC audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * Author: John Hsu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "nau8824.h" + +#define NAU8824_JD_ACTIVE_HIGH BIT(0) +#define NAU8824_MONO_SPEAKER BIT(1) + +static int nau8824_quirk; +static int quirk_override = -1; +module_param_named(quirk, quirk_override, uint, 0444); +MODULE_PARM_DESC(quirk, "Board-specific quirk override"); + +static int nau8824_config_sysclk(struct nau8824 *nau8824, + int clk_id, unsigned int freq); +static bool nau8824_is_jack_inserted(struct nau8824 *nau8824); + +/* the ADC threshold of headset */ +#define DMIC_CLK 3072000 + +/* the ADC threshold of headset */ +#define HEADSET_SARADC_THD 0x80 + +/* the parameter threshold of FLL */ +#define NAU_FREF_MAX 13500000 +#define NAU_FVCO_MAX 100000000 +#define NAU_FVCO_MIN 90000000 + +/* scaling for mclk from sysclk_src output */ +static const struct nau8824_fll_attr mclk_src_scaling[] = { + { 1, 0x0 }, + { 2, 0x2 }, + { 4, 0x3 }, + { 8, 0x4 }, + { 16, 0x5 }, + { 32, 0x6 }, + { 3, 0x7 }, + { 6, 0xa }, + { 12, 0xb }, + { 24, 0xc }, +}; + +/* ratio for input clk freq */ +static const struct nau8824_fll_attr fll_ratio[] = { + { 512000, 0x01 }, + { 256000, 0x02 }, + { 128000, 0x04 }, + { 64000, 0x08 }, + { 32000, 0x10 }, + { 8000, 0x20 }, + { 4000, 0x40 }, +}; + +static const struct nau8824_fll_attr fll_pre_scalar[] = { + { 1, 0x0 }, + { 2, 0x1 }, + { 4, 0x2 }, + { 8, 0x3 }, +}; + +/* the maximum frequency of CLK_ADC and CLK_DAC */ +#define CLK_DA_AD_MAX 6144000 + +/* over sampling rate */ +static const struct nau8824_osr_attr osr_dac_sel[] = { + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 256, 0 }, /* OSR 256, SRC 1 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 0, 0 }, + { 32, 3 }, /* OSR 32, SRC 1/8 */ +}; + +static const struct nau8824_osr_attr osr_adc_sel[] = { + { 32, 3 }, /* OSR 32, SRC 1/8 */ + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 256, 0 }, /* OSR 256, SRC 1 */ +}; + +static const struct reg_default nau8824_reg_defaults[] = { + { NAU8824_REG_ENA_CTRL, 0x0000 }, + { NAU8824_REG_CLK_GATING_ENA, 0x0000 }, + { NAU8824_REG_CLK_DIVIDER, 0x0000 }, + { NAU8824_REG_FLL1, 0x0000 }, + { NAU8824_REG_FLL2, 0x3126 }, + { NAU8824_REG_FLL3, 0x0008 }, + { NAU8824_REG_FLL4, 0x0010 }, + { NAU8824_REG_FLL5, 0xC000 }, + { NAU8824_REG_FLL6, 0x6000 }, + { NAU8824_REG_FLL_VCO_RSV, 0xF13C }, + { NAU8824_REG_JACK_DET_CTRL, 0x0000 }, + { NAU8824_REG_INTERRUPT_SETTING_1, 0x0000 }, + { NAU8824_REG_IRQ, 0x0000 }, + { NAU8824_REG_CLEAR_INT_REG, 0x0000 }, + { NAU8824_REG_INTERRUPT_SETTING, 0x1000 }, + { NAU8824_REG_SAR_ADC, 0x0015 }, + { NAU8824_REG_VDET_COEFFICIENT, 0x0110 }, + { NAU8824_REG_VDET_THRESHOLD_1, 0x0000 }, + { NAU8824_REG_VDET_THRESHOLD_2, 0x0000 }, + { NAU8824_REG_VDET_THRESHOLD_3, 0x0000 }, + { NAU8824_REG_VDET_THRESHOLD_4, 0x0000 }, + { NAU8824_REG_GPIO_SEL, 0x0000 }, + { NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 0x000B }, + { NAU8824_REG_PORT0_I2S_PCM_CTRL_2, 0x0010 }, + { NAU8824_REG_PORT0_LEFT_TIME_SLOT, 0x0000 }, + { NAU8824_REG_PORT0_RIGHT_TIME_SLOT, 0x0000 }, + { NAU8824_REG_TDM_CTRL, 0x0000 }, + { NAU8824_REG_ADC_HPF_FILTER, 0x0000 }, + { NAU8824_REG_ADC_FILTER_CTRL, 0x0002 }, + { NAU8824_REG_DAC_FILTER_CTRL_1, 0x0000 }, + { NAU8824_REG_DAC_FILTER_CTRL_2, 0x0000 }, + { NAU8824_REG_NOTCH_FILTER_1, 0x0000 }, + { NAU8824_REG_NOTCH_FILTER_2, 0x0000 }, + { NAU8824_REG_EQ1_LOW, 0x112C }, + { NAU8824_REG_EQ2_EQ3, 0x2C2C }, + { NAU8824_REG_EQ4_EQ5, 0x2C2C }, + { NAU8824_REG_ADC_CH0_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_ADC_CH1_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_ADC_CH2_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_ADC_CH3_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_DAC_MUTE_CTRL, 0x0000 }, + { NAU8824_REG_DAC_CH0_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_DAC_CH1_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_ADC_TO_DAC_ST, 0x0000 }, + { NAU8824_REG_DRC_KNEE_IP12_ADC_CH01, 0x1486 }, + { NAU8824_REG_DRC_KNEE_IP34_ADC_CH01, 0x0F12 }, + { NAU8824_REG_DRC_SLOPE_ADC_CH01, 0x25FF }, + { NAU8824_REG_DRC_ATKDCY_ADC_CH01, 0x3457 }, + { NAU8824_REG_DRC_KNEE_IP12_ADC_CH23, 0x1486 }, + { NAU8824_REG_DRC_KNEE_IP34_ADC_CH23, 0x0F12 }, + { NAU8824_REG_DRC_SLOPE_ADC_CH23, 0x25FF }, + { NAU8824_REG_DRC_ATKDCY_ADC_CH23, 0x3457 }, + { NAU8824_REG_DRC_GAINL_ADC0, 0x0200 }, + { NAU8824_REG_DRC_GAINL_ADC1, 0x0200 }, + { NAU8824_REG_DRC_GAINL_ADC2, 0x0200 }, + { NAU8824_REG_DRC_GAINL_ADC3, 0x0200 }, + { NAU8824_REG_DRC_KNEE_IP12_DAC, 0x1486 }, + { NAU8824_REG_DRC_KNEE_IP34_DAC, 0x0F12 }, + { NAU8824_REG_DRC_SLOPE_DAC, 0x25F9 }, + { NAU8824_REG_DRC_ATKDCY_DAC, 0x3457 }, + { NAU8824_REG_DRC_GAIN_DAC_CH0, 0x0200 }, + { NAU8824_REG_DRC_GAIN_DAC_CH1, 0x0200 }, + { NAU8824_REG_MODE, 0x0000 }, + { NAU8824_REG_MODE1, 0x0000 }, + { NAU8824_REG_MODE2, 0x0000 }, + { NAU8824_REG_CLASSG, 0x0000 }, + { NAU8824_REG_OTP_EFUSE, 0x0000 }, + { NAU8824_REG_OTPDOUT_1, 0x0000 }, + { NAU8824_REG_OTPDOUT_2, 0x0000 }, + { NAU8824_REG_MISC_CTRL, 0x0000 }, + { NAU8824_REG_I2C_TIMEOUT, 0xEFFF }, + { NAU8824_REG_TEST_MODE, 0x0000 }, + { NAU8824_REG_I2C_DEVICE_ID, 0x1AF1 }, + { NAU8824_REG_SAR_ADC_DATA_OUT, 0x00FF }, + { NAU8824_REG_BIAS_ADJ, 0x0000 }, + { NAU8824_REG_PGA_GAIN, 0x0000 }, + { NAU8824_REG_TRIM_SETTINGS, 0x0000 }, + { NAU8824_REG_ANALOG_CONTROL_1, 0x0000 }, + { NAU8824_REG_ANALOG_CONTROL_2, 0x0000 }, + { NAU8824_REG_ENABLE_LO, 0x0000 }, + { NAU8824_REG_GAIN_LO, 0x0000 }, + { NAU8824_REG_CLASSD_GAIN_1, 0x0000 }, + { NAU8824_REG_CLASSD_GAIN_2, 0x0000 }, + { NAU8824_REG_ANALOG_ADC_1, 0x0011 }, + { NAU8824_REG_ANALOG_ADC_2, 0x0020 }, + { NAU8824_REG_RDAC, 0x0008 }, + { NAU8824_REG_MIC_BIAS, 0x0006 }, + { NAU8824_REG_HS_VOLUME_CONTROL, 0x0000 }, + { NAU8824_REG_BOOST, 0x0000 }, + { NAU8824_REG_FEPGA, 0x0000 }, + { NAU8824_REG_FEPGA_II, 0x0000 }, + { NAU8824_REG_FEPGA_SE, 0x0000 }, + { NAU8824_REG_FEPGA_ATTENUATION, 0x0000 }, + { NAU8824_REG_ATT_PORT0, 0x0000 }, + { NAU8824_REG_ATT_PORT1, 0x0000 }, + { NAU8824_REG_POWER_UP_CONTROL, 0x0000 }, + { NAU8824_REG_CHARGE_PUMP_CONTROL, 0x0300 }, + { NAU8824_REG_CHARGE_PUMP_INPUT, 0x0013 }, +}; + +static int nau8824_sema_acquire(struct nau8824 *nau8824, long timeout) +{ + int ret; + + if (timeout) { + ret = down_timeout(&nau8824->jd_sem, timeout); + if (ret < 0) + dev_warn(nau8824->dev, "Acquire semaphore timeout\n"); + } else { + ret = down_interruptible(&nau8824->jd_sem); + if (ret < 0) + dev_warn(nau8824->dev, "Acquire semaphore fail\n"); + } + + return ret; +} + +static inline void nau8824_sema_release(struct nau8824 *nau8824) +{ + up(&nau8824->jd_sem); +} + +static bool nau8824_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8824_REG_ENA_CTRL ... NAU8824_REG_FLL_VCO_RSV: + case NAU8824_REG_JACK_DET_CTRL: + case NAU8824_REG_INTERRUPT_SETTING_1: + case NAU8824_REG_IRQ: + case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4: + case NAU8824_REG_GPIO_SEL: + case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL: + case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5: + case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST: + case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 ... NAU8824_REG_DRC_GAINL_ADC3: + case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_GAIN_DAC_CH1: + case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE: + case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2: + case NAU8824_REG_I2C_TIMEOUT: + case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT: + case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2: + case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1: + case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_INPUT: + return true; + default: + return false; + } + +} + +static bool nau8824_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8824_REG_RESET ... NAU8824_REG_FLL_VCO_RSV: + case NAU8824_REG_JACK_DET_CTRL: + case NAU8824_REG_INTERRUPT_SETTING_1: + case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4: + case NAU8824_REG_GPIO_SEL: + case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL: + case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5: + case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST: + case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01: + case NAU8824_REG_DRC_KNEE_IP34_ADC_CH01: + case NAU8824_REG_DRC_SLOPE_ADC_CH01: + case NAU8824_REG_DRC_ATKDCY_ADC_CH01: + case NAU8824_REG_DRC_KNEE_IP12_ADC_CH23: + case NAU8824_REG_DRC_KNEE_IP34_ADC_CH23: + case NAU8824_REG_DRC_SLOPE_ADC_CH23: + case NAU8824_REG_DRC_ATKDCY_ADC_CH23: + case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_ATKDCY_DAC: + case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE: + case NAU8824_REG_I2C_TIMEOUT: + case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2: + case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1: + case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_CONTROL: + return true; + default: + return false; + } +} + +static bool nau8824_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8824_REG_RESET: + case NAU8824_REG_IRQ ... NAU8824_REG_CLEAR_INT_REG: + case NAU8824_REG_DRC_GAINL_ADC0 ... NAU8824_REG_DRC_GAINL_ADC3: + case NAU8824_REG_DRC_GAIN_DAC_CH0 ... NAU8824_REG_DRC_GAIN_DAC_CH1: + case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2: + case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT: + case NAU8824_REG_CHARGE_PUMP_INPUT: + return true; + default: + return false; + } +} + +static const char * const nau8824_companding[] = { + "Off", "NC", "u-law", "A-law" }; + +static const struct soc_enum nau8824_companding_adc_enum = + SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 12, + ARRAY_SIZE(nau8824_companding), nau8824_companding); + +static const struct soc_enum nau8824_companding_dac_enum = + SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 14, + ARRAY_SIZE(nau8824_companding), nau8824_companding); + +static const char * const nau8824_adc_decimation[] = { + "32", "64", "128", "256" }; + +static const struct soc_enum nau8824_adc_decimation_enum = + SOC_ENUM_SINGLE(NAU8824_REG_ADC_FILTER_CTRL, 0, + ARRAY_SIZE(nau8824_adc_decimation), nau8824_adc_decimation); + +static const char * const nau8824_dac_oversampl[] = { + "64", "256", "128", "", "32" }; + +static const struct soc_enum nau8824_dac_oversampl_enum = + SOC_ENUM_SINGLE(NAU8824_REG_DAC_FILTER_CTRL_1, 0, + ARRAY_SIZE(nau8824_dac_oversampl), nau8824_dac_oversampl); + +static const char * const nau8824_input_channel[] = { + "Input CH0", "Input CH1", "Input CH2", "Input CH3" }; + +static const struct soc_enum nau8824_adc_ch0_enum = + SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH0_DGAIN_CTRL, 9, + ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel); + +static const struct soc_enum nau8824_adc_ch1_enum = + SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH1_DGAIN_CTRL, 9, + ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel); + +static const struct soc_enum nau8824_adc_ch2_enum = + SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH2_DGAIN_CTRL, 9, + ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel); + +static const struct soc_enum nau8824_adc_ch3_enum = + SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH3_DGAIN_CTRL, 9, + ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel); + +static const char * const nau8824_tdm_slot[] = { + "Slot 0", "Slot 1", "Slot 2", "Slot 3" }; + +static const struct soc_enum nau8824_dac_left_sel_enum = + SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 6, + ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot); + +static const struct soc_enum nau8824_dac_right_sel_enum = + SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 4, + ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot); + +static const DECLARE_TLV_DB_MINMAX_MUTE(spk_vol_tlv, 0, 2400); +static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -3000, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 200, 0); +static const DECLARE_TLV_DB_SCALE(dmic_vol_tlv, -12800, 50, 0); + +static const struct snd_kcontrol_new nau8824_snd_controls[] = { + SOC_ENUM("ADC Companding", nau8824_companding_adc_enum), + SOC_ENUM("DAC Companding", nau8824_companding_dac_enum), + + SOC_ENUM("ADC Decimation Rate", nau8824_adc_decimation_enum), + SOC_ENUM("DAC Oversampling Rate", nau8824_dac_oversampl_enum), + + SOC_SINGLE_TLV("Speaker Right DACR Volume", + NAU8824_REG_CLASSD_GAIN_1, 8, 0x1f, 0, spk_vol_tlv), + SOC_SINGLE_TLV("Speaker Left DACL Volume", + NAU8824_REG_CLASSD_GAIN_2, 0, 0x1f, 0, spk_vol_tlv), + SOC_SINGLE_TLV("Speaker Left DACR Volume", + NAU8824_REG_CLASSD_GAIN_1, 0, 0x1f, 0, spk_vol_tlv), + SOC_SINGLE_TLV("Speaker Right DACL Volume", + NAU8824_REG_CLASSD_GAIN_2, 8, 0x1f, 0, spk_vol_tlv), + + SOC_SINGLE_TLV("Headphone Right DACR Volume", + NAU8824_REG_ATT_PORT0, 8, 0x1f, 0, hp_vol_tlv), + SOC_SINGLE_TLV("Headphone Left DACL Volume", + NAU8824_REG_ATT_PORT0, 0, 0x1f, 0, hp_vol_tlv), + SOC_SINGLE_TLV("Headphone Right DACL Volume", + NAU8824_REG_ATT_PORT1, 8, 0x1f, 0, hp_vol_tlv), + SOC_SINGLE_TLV("Headphone Left DACR Volume", + NAU8824_REG_ATT_PORT1, 0, 0x1f, 0, hp_vol_tlv), + + SOC_SINGLE_TLV("MIC1 Volume", NAU8824_REG_FEPGA_II, + NAU8824_FEPGA_GAINL_SFT, 0x12, 0, mic_vol_tlv), + SOC_SINGLE_TLV("MIC2 Volume", NAU8824_REG_FEPGA_II, + NAU8824_FEPGA_GAINR_SFT, 0x12, 0, mic_vol_tlv), + + SOC_SINGLE_TLV("DMIC1 Volume", NAU8824_REG_ADC_CH0_DGAIN_CTRL, + 0, 0x164, 0, dmic_vol_tlv), + SOC_SINGLE_TLV("DMIC2 Volume", NAU8824_REG_ADC_CH1_DGAIN_CTRL, + 0, 0x164, 0, dmic_vol_tlv), + SOC_SINGLE_TLV("DMIC3 Volume", NAU8824_REG_ADC_CH2_DGAIN_CTRL, + 0, 0x164, 0, dmic_vol_tlv), + SOC_SINGLE_TLV("DMIC4 Volume", NAU8824_REG_ADC_CH3_DGAIN_CTRL, + 0, 0x164, 0, dmic_vol_tlv), + + SOC_ENUM("ADC CH0 Select", nau8824_adc_ch0_enum), + SOC_ENUM("ADC CH1 Select", nau8824_adc_ch1_enum), + SOC_ENUM("ADC CH2 Select", nau8824_adc_ch2_enum), + SOC_ENUM("ADC CH3 Select", nau8824_adc_ch3_enum), + + SOC_SINGLE("ADC CH0 TX Switch", NAU8824_REG_TDM_CTRL, 0, 1, 0), + SOC_SINGLE("ADC CH1 TX Switch", NAU8824_REG_TDM_CTRL, 1, 1, 0), + SOC_SINGLE("ADC CH2 TX Switch", NAU8824_REG_TDM_CTRL, 2, 1, 0), + SOC_SINGLE("ADC CH3 TX Switch", NAU8824_REG_TDM_CTRL, 3, 1, 0), + + SOC_ENUM("DACL Channel Source", nau8824_dac_left_sel_enum), + SOC_ENUM("DACR Channel Source", nau8824_dac_right_sel_enum), + + SOC_SINGLE("DACL LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 0, 1, 0), + SOC_SINGLE("DACR LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 1, 1, 0), + + SOC_SINGLE("THD for key media", + NAU8824_REG_VDET_THRESHOLD_1, 8, 0xff, 0), + SOC_SINGLE("THD for key voice command", + NAU8824_REG_VDET_THRESHOLD_1, 0, 0xff, 0), + SOC_SINGLE("THD for key volume up", + NAU8824_REG_VDET_THRESHOLD_2, 8, 0xff, 0), + SOC_SINGLE("THD for key volume down", + NAU8824_REG_VDET_THRESHOLD_2, 0, 0xff, 0), +}; + +static int nau8824_output_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disables the TESTDAC to let DAC signal pass through. */ + regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO, + NAU8824_TEST_DAC_EN, 0); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO, + NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8824_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(nau8824->regmap, + NAU8824_REG_ANALOG_CONTROL_2, + NAU8824_CLASSD_CLAMP_DIS, NAU8824_CLASSD_CLAMP_DIS); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8824->regmap, + NAU8824_REG_ANALOG_CONTROL_2, + NAU8824_CLASSD_CLAMP_DIS, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8824_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Prevent startup click by letting charge pump to ramp up */ + msleep(10); + regmap_update_bits(nau8824->regmap, + NAU8824_REG_CHARGE_PUMP_CONTROL, + NAU8824_JAMNODCLOW, NAU8824_JAMNODCLOW); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(nau8824->regmap, + NAU8824_REG_CHARGE_PUMP_CONTROL, + NAU8824_JAMNODCLOW, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int system_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + struct regmap *regmap = nau8824->regmap; + unsigned int value; + bool clk_fll, error; + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + dev_dbg(nau8824->dev, "system clock control : POWER OFF\n"); + /* Set clock source to disable or internal clock before the + * playback or capture end. Codec needs clock for Jack + * detection and button press if jack inserted; otherwise, + * the clock should be closed. + */ + if (nau8824_is_jack_inserted(nau8824)) { + nau8824_config_sysclk(nau8824, + NAU8824_CLK_INTERNAL, 0); + } else { + nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0); + } + } else { + dev_dbg(nau8824->dev, "system clock control : POWER ON\n"); + /* Check the clock source setting is proper or not + * no matter the source is from FLL or MCLK. + */ + regmap_read(regmap, NAU8824_REG_FLL1, &value); + clk_fll = value & NAU8824_FLL_RATIO_MASK; + /* It's error to use internal clock when playback */ + regmap_read(regmap, NAU8824_REG_FLL6, &value); + error = value & NAU8824_DCO_EN; + if (!error) { + /* Check error depending on source is FLL or MCLK. */ + regmap_read(regmap, NAU8824_REG_CLK_DIVIDER, &value); + if (clk_fll) + error = !(value & NAU8824_CLK_SRC_VCO); + else + error = value & NAU8824_CLK_SRC_VCO; + } + /* Recover the clock source setting if error. */ + if (error) { + if (clk_fll) { + regmap_update_bits(regmap, + NAU8824_REG_FLL6, NAU8824_DCO_EN, 0); + regmap_update_bits(regmap, + NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK, + NAU8824_CLK_SRC_VCO); + } else { + nau8824_config_sysclk(nau8824, + NAU8824_CLK_MCLK, 0); + } + } + } + + return 0; +} + +static int dmic_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + int src; + + /* The DMIC clock is gotten from system clock (256fs) divided by + * DMIC_SRC (1, 2, 4, 8, 16, 32). The clock has to be equal or + * less than 3.072 MHz. + */ + for (src = 0; src < 5; src++) { + if ((0x1 << (8 - src)) * nau8824->fs <= DMIC_CLK) + break; + } + dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, nau8824->fs * 256); + regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_DMIC_SRC_MASK, (src << NAU8824_CLK_DMIC_SRC_SFT)); + + return 0; +} + +static const struct snd_kcontrol_new nau8824_adc_ch0_dmic = + SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL, + NAU8824_ADC_CH0_DMIC_SFT, 1, 0); + +static const struct snd_kcontrol_new nau8824_adc_ch1_dmic = + SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL, + NAU8824_ADC_CH1_DMIC_SFT, 1, 0); + +static const struct snd_kcontrol_new nau8824_adc_ch2_dmic = + SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL, + NAU8824_ADC_CH2_DMIC_SFT, 1, 0); + +static const struct snd_kcontrol_new nau8824_adc_ch3_dmic = + SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL, + NAU8824_ADC_CH3_DMIC_SFT, 1, 0); + +static const struct snd_kcontrol_new nau8824_adc_left_mixer[] = { + SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA, + NAU8824_FEPGA_MODEL_MIC1_SFT, 1, 0), + SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA, + NAU8824_FEPGA_MODEL_HSMIC_SFT, 1, 0), +}; + +static const struct snd_kcontrol_new nau8824_adc_right_mixer[] = { + SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA, + NAU8824_FEPGA_MODER_MIC2_SFT, 1, 0), + SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA, + NAU8824_FEPGA_MODER_HSMIC_SFT, 1, 0), +}; + +static const struct snd_kcontrol_new nau8824_hp_left_mixer[] = { + SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO, + NAU8824_DACR_HPL_EN_SFT, 1, 0), + SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO, + NAU8824_DACL_HPL_EN_SFT, 1, 0), +}; + +static const struct snd_kcontrol_new nau8824_hp_right_mixer[] = { + SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO, + NAU8824_DACL_HPR_EN_SFT, 1, 0), + SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO, + NAU8824_DACR_HPR_EN_SFT, 1, 0), +}; + +static const char * const nau8824_dac_src[] = { "DACL", "DACR" }; + +static SOC_ENUM_SINGLE_DECL( + nau8824_dacl_enum, NAU8824_REG_DAC_CH0_DGAIN_CTRL, + NAU8824_DAC_CH0_SEL_SFT, nau8824_dac_src); + +static SOC_ENUM_SINGLE_DECL( + nau8824_dacr_enum, NAU8824_REG_DAC_CH1_DGAIN_CTRL, + NAU8824_DAC_CH1_SEL_SFT, nau8824_dac_src); + +static const struct snd_kcontrol_new nau8824_dacl_mux = + SOC_DAPM_ENUM("DACL Source", nau8824_dacl_enum); + +static const struct snd_kcontrol_new nau8824_dacr_mux = + SOC_DAPM_ENUM("DACR Source", nau8824_dacr_enum); + + +static const struct snd_soc_dapm_widget nau8824_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0, + system_clock_control, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_INPUT("HSMIC1"), + SND_SOC_DAPM_INPUT("HSMIC2"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + + SND_SOC_DAPM_SUPPLY("SAR", NAU8824_REG_SAR_ADC, + NAU8824_SAR_ADC_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS", NAU8824_REG_MIC_BIAS, + NAU8824_MICBIAS_POWERUP_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC12 Power", NAU8824_REG_BIAS_ADJ, + NAU8824_DMIC1_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC34 Power", NAU8824_REG_BIAS_ADJ, + NAU8824_DMIC2_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC Clock", SND_SOC_NOPM, 0, 0, + dmic_clock_control, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SWITCH("DMIC1 Enable", SND_SOC_NOPM, + 0, 0, &nau8824_adc_ch0_dmic), + SND_SOC_DAPM_SWITCH("DMIC2 Enable", SND_SOC_NOPM, + 0, 0, &nau8824_adc_ch1_dmic), + SND_SOC_DAPM_SWITCH("DMIC3 Enable", SND_SOC_NOPM, + 0, 0, &nau8824_adc_ch2_dmic), + SND_SOC_DAPM_SWITCH("DMIC4 Enable", SND_SOC_NOPM, + 0, 0, &nau8824_adc_ch3_dmic), + + SND_SOC_DAPM_MIXER("Left ADC", NAU8824_REG_POWER_UP_CONTROL, + 12, 0, nau8824_adc_left_mixer, + ARRAY_SIZE(nau8824_adc_left_mixer)), + SND_SOC_DAPM_MIXER("Right ADC", NAU8824_REG_POWER_UP_CONTROL, + 13, 0, nau8824_adc_right_mixer, + ARRAY_SIZE(nau8824_adc_right_mixer)), + + SND_SOC_DAPM_ADC("ADCL", NULL, NAU8824_REG_ANALOG_ADC_2, + NAU8824_ADCL_EN_SFT, 0), + SND_SOC_DAPM_ADC("ADCR", NULL, NAU8824_REG_ANALOG_ADC_2, + NAU8824_ADCR_EN_SFT, 0), + + SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DACL", NULL, NAU8824_REG_RDAC, + NAU8824_DACL_EN_SFT, 0), + SND_SOC_DAPM_SUPPLY("DACL Clock", NAU8824_REG_RDAC, + NAU8824_DACL_CLK_SFT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DACR", NULL, NAU8824_REG_RDAC, + NAU8824_DACR_EN_SFT, 0), + SND_SOC_DAPM_SUPPLY("DACR Clock", NAU8824_REG_RDAC, + NAU8824_DACR_CLK_SFT, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacl_mux), + SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacr_mux), + + SND_SOC_DAPM_PGA_S("Output DACL", 0, NAU8824_REG_CHARGE_PUMP_CONTROL, + 8, 1, nau8824_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("Output DACR", 0, NAU8824_REG_CHARGE_PUMP_CONTROL, + 9, 1, nau8824_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_S("ClassD", 0, NAU8824_REG_CLASSD_GAIN_1, + NAU8824_CLASSD_EN_SFT, 0, nau8824_spk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("Left Headphone", NAU8824_REG_CLASSG, + NAU8824_CLASSG_LDAC_EN_SFT, 0, nau8824_hp_left_mixer, + ARRAY_SIZE(nau8824_hp_left_mixer)), + SND_SOC_DAPM_MIXER("Right Headphone", NAU8824_REG_CLASSG, + NAU8824_CLASSG_RDAC_EN_SFT, 0, nau8824_hp_right_mixer, + ARRAY_SIZE(nau8824_hp_right_mixer)), + SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8824_REG_CHARGE_PUMP_CONTROL, + NAU8824_CHARGE_PUMP_EN_SFT, 0, nau8824_pump_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA("Output Driver L", + NAU8824_REG_POWER_UP_CONTROL, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver R", + NAU8824_REG_POWER_UP_CONTROL, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("Main Driver L", + NAU8824_REG_POWER_UP_CONTROL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("Main Driver R", + NAU8824_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Boost Driver", NAU8824_REG_BOOST, + NAU8824_HP_BOOST_DIS_SFT, 1, NULL, 0), + SND_SOC_DAPM_PGA("Class G", NAU8824_REG_CLASSG, + NAU8824_CLASSG_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("SPKOUTL"), + SND_SOC_DAPM_OUTPUT("SPKOUTR"), + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route nau8824_dapm_routes[] = { + {"DMIC1 Enable", "Switch", "DMIC1"}, + {"DMIC2 Enable", "Switch", "DMIC2"}, + {"DMIC3 Enable", "Switch", "DMIC3"}, + {"DMIC4 Enable", "Switch", "DMIC4"}, + + {"DMIC1", NULL, "DMIC12 Power"}, + {"DMIC2", NULL, "DMIC12 Power"}, + {"DMIC3", NULL, "DMIC34 Power"}, + {"DMIC4", NULL, "DMIC34 Power"}, + {"DMIC12 Power", NULL, "DMIC Clock"}, + {"DMIC34 Power", NULL, "DMIC Clock"}, + + {"Left ADC", "MIC Switch", "MIC1"}, + {"Left ADC", "HSMIC Switch", "HSMIC1"}, + {"Right ADC", "MIC Switch", "MIC2"}, + {"Right ADC", "HSMIC Switch", "HSMIC2"}, + + {"ADCL", NULL, "Left ADC"}, + {"ADCR", NULL, "Right ADC"}, + + {"AIFTX", NULL, "MICBIAS"}, + {"AIFTX", NULL, "ADCL"}, + {"AIFTX", NULL, "ADCR"}, + {"AIFTX", NULL, "DMIC1 Enable"}, + {"AIFTX", NULL, "DMIC2 Enable"}, + {"AIFTX", NULL, "DMIC3 Enable"}, + {"AIFTX", NULL, "DMIC4 Enable"}, + + {"AIFTX", NULL, "System Clock"}, + {"AIFRX", NULL, "System Clock"}, + + {"DACL", NULL, "AIFRX"}, + {"DACL", NULL, "DACL Clock"}, + {"DACR", NULL, "AIFRX"}, + {"DACR", NULL, "DACR Clock"}, + + {"DACL Mux", "DACL", "DACL"}, + {"DACL Mux", "DACR", "DACR"}, + {"DACR Mux", "DACL", "DACL"}, + {"DACR Mux", "DACR", "DACR"}, + + {"Output DACL", NULL, "DACL Mux"}, + {"Output DACR", NULL, "DACR Mux"}, + + {"ClassD", NULL, "Output DACL"}, + {"ClassD", NULL, "Output DACR"}, + + {"Left Headphone", "DAC Left Switch", "Output DACL"}, + {"Left Headphone", "DAC Right Switch", "Output DACR"}, + {"Right Headphone", "DAC Left Switch", "Output DACL"}, + {"Right Headphone", "DAC Right Switch", "Output DACR"}, + + {"Charge Pump", NULL, "Left Headphone"}, + {"Charge Pump", NULL, "Right Headphone"}, + {"Output Driver L", NULL, "Charge Pump"}, + {"Output Driver R", NULL, "Charge Pump"}, + {"Main Driver L", NULL, "Output Driver L"}, + {"Main Driver R", NULL, "Output Driver R"}, + {"Class G", NULL, "Main Driver L"}, + {"Class G", NULL, "Main Driver R"}, + {"HP Boost Driver", NULL, "Class G"}, + + {"SPKOUTL", NULL, "ClassD"}, + {"SPKOUTR", NULL, "ClassD"}, + {"HPOL", NULL, "HP Boost Driver"}, + {"HPOR", NULL, "HP Boost Driver"}, +}; + +static bool nau8824_is_jack_inserted(struct nau8824 *nau8824) +{ + struct snd_soc_jack *jack = nau8824->jack; + bool insert = false; + + if (nau8824->irq && jack) + insert = jack->status & SND_JACK_HEADPHONE; + + return insert; +} + +static void nau8824_int_status_clear_all(struct regmap *regmap) +{ + int active_irq, clear_irq, i; + + /* Reset the intrruption status from rightmost bit if the corres- + * ponding irq event occurs. + */ + regmap_read(regmap, NAU8824_REG_IRQ, &active_irq); + for (i = 0; i < NAU8824_REG_DATA_LEN; i++) { + clear_irq = (0x1 << i); + if (active_irq & clear_irq) + regmap_write(regmap, + NAU8824_REG_CLEAR_INT_REG, clear_irq); + } +} + +static void nau8824_eject_jack(struct nau8824 *nau8824) +{ + struct snd_soc_dapm_context *dapm = nau8824->dapm; + struct regmap *regmap = nau8824->regmap; + + /* Clear all interruption status */ + nau8824_int_status_clear_all(regmap); + + snd_soc_dapm_disable_pin(dapm, "SAR"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + snd_soc_dapm_sync(dapm); + + /* Enable the insertion interruption, disable the ejection + * interruption, and then bypass de-bounce circuit. + */ + regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING, + NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS | + NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS, + NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS | + NAU8824_IRQ_EJECT_DIS); + regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN, + NAU8824_IRQ_INSERT_EN); + regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL, + NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE); + + /* Close clock for jack type detection at manual mode */ + if (dapm->bias_level < SND_SOC_BIAS_PREPARE) + nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0); +} + +static void nau8824_jdet_work(struct work_struct *work) +{ + struct nau8824 *nau8824 = container_of( + work, struct nau8824, jdet_work); + struct snd_soc_dapm_context *dapm = nau8824->dapm; + struct regmap *regmap = nau8824->regmap; + int adc_value, event = 0, event_mask = 0; + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + + msleep(100); + + regmap_read(regmap, NAU8824_REG_SAR_ADC_DATA_OUT, &adc_value); + adc_value = adc_value & NAU8824_SAR_ADC_DATA_MASK; + dev_dbg(nau8824->dev, "SAR ADC data 0x%02x\n", adc_value); + if (adc_value < HEADSET_SARADC_THD) { + event |= SND_JACK_HEADPHONE; + + snd_soc_dapm_disable_pin(dapm, "SAR"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + snd_soc_dapm_sync(dapm); + } else { + event |= SND_JACK_HEADSET; + } + event_mask |= SND_JACK_HEADSET; + snd_soc_jack_report(nau8824->jack, event, event_mask); + + /* Enable short key press and release interruption. */ + regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING, + NAU8824_IRQ_KEY_RELEASE_DIS | + NAU8824_IRQ_KEY_SHORT_PRESS_DIS, 0); + + if (nau8824->resume_lock) { + nau8824_sema_release(nau8824); + nau8824->resume_lock = false; + } +} + +static void nau8824_setup_auto_irq(struct nau8824 *nau8824) +{ + struct regmap *regmap = nau8824->regmap; + + /* Enable jack ejection interruption. */ + regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN, + NAU8824_IRQ_EJECT_EN); + regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING, + NAU8824_IRQ_EJECT_DIS, 0); + /* Enable internal VCO needed for interruptions */ + if (nau8824->dapm->bias_level < SND_SOC_BIAS_PREPARE) + nau8824_config_sysclk(nau8824, NAU8824_CLK_INTERNAL, 0); + regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL, + NAU8824_JD_SLEEP_MODE, 0); +} + +static int nau8824_button_decode(int value) +{ + int buttons = 0; + + /* The chip supports up to 8 buttons, but ALSA defines + * only 6 buttons. + */ + if (value & BIT(0)) + buttons |= SND_JACK_BTN_0; + if (value & BIT(1)) + buttons |= SND_JACK_BTN_1; + if (value & BIT(2)) + buttons |= SND_JACK_BTN_2; + if (value & BIT(3)) + buttons |= SND_JACK_BTN_3; + if (value & BIT(4)) + buttons |= SND_JACK_BTN_4; + if (value & BIT(5)) + buttons |= SND_JACK_BTN_5; + + return buttons; +} + +#define NAU8824_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + +static irqreturn_t nau8824_interrupt(int irq, void *data) +{ + struct nau8824 *nau8824 = (struct nau8824 *)data; + struct regmap *regmap = nau8824->regmap; + int active_irq, clear_irq = 0, event = 0, event_mask = 0; + + if (regmap_read(regmap, NAU8824_REG_IRQ, &active_irq)) { + dev_err(nau8824->dev, "failed to read irq status\n"); + return IRQ_NONE; + } + dev_dbg(nau8824->dev, "IRQ %x\n", active_irq); + + if (active_irq & NAU8824_JACK_EJECTION_DETECTED) { + nau8824_eject_jack(nau8824); + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8824_JACK_EJECTION_DETECTED; + /* release semaphore held after resume, + * and cancel jack detection + */ + if (nau8824->resume_lock) { + nau8824_sema_release(nau8824); + nau8824->resume_lock = false; + } + cancel_work_sync(&nau8824->jdet_work); + } else if (active_irq & NAU8824_KEY_SHORT_PRESS_IRQ) { + int key_status, button_pressed; + + regmap_read(regmap, NAU8824_REG_CLEAR_INT_REG, + &key_status); + + /* lower 8 bits of the register are for pressed keys */ + button_pressed = nau8824_button_decode(key_status); + + event |= button_pressed; + dev_dbg(nau8824->dev, "button %x pressed\n", event); + event_mask |= NAU8824_BUTTONS; + clear_irq = NAU8824_KEY_SHORT_PRESS_IRQ; + } else if (active_irq & NAU8824_KEY_RELEASE_IRQ) { + event_mask = NAU8824_BUTTONS; + clear_irq = NAU8824_KEY_RELEASE_IRQ; + } else if (active_irq & NAU8824_JACK_INSERTION_DETECTED) { + /* Turn off insertion interruption at manual mode */ + regmap_update_bits(regmap, + NAU8824_REG_INTERRUPT_SETTING, + NAU8824_IRQ_INSERT_DIS, + NAU8824_IRQ_INSERT_DIS); + regmap_update_bits(regmap, + NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_INSERT_EN, 0); + /* detect microphone and jack type */ + cancel_work_sync(&nau8824->jdet_work); + schedule_work(&nau8824->jdet_work); + + /* Enable interruption for jack type detection at audo + * mode which can detect microphone and jack type. + */ + nau8824_setup_auto_irq(nau8824); + } + + if (!clear_irq) + clear_irq = active_irq; + /* clears the rightmost interruption */ + regmap_write(regmap, NAU8824_REG_CLEAR_INT_REG, clear_irq); + + if (event_mask) + snd_soc_jack_report(nau8824->jack, event, event_mask); + + return IRQ_HANDLED; +} + +static const struct nau8824_osr_attr * +nau8824_get_osr(struct nau8824 *nau8824, int stream) +{ + unsigned int osr; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_read(nau8824->regmap, + NAU8824_REG_DAC_FILTER_CTRL_1, &osr); + osr &= NAU8824_DAC_OVERSAMPLE_MASK; + if (osr >= ARRAY_SIZE(osr_dac_sel)) + return NULL; + return &osr_dac_sel[osr]; + } else { + regmap_read(nau8824->regmap, + NAU8824_REG_ADC_FILTER_CTRL, &osr); + osr &= NAU8824_ADC_SYNC_DOWN_MASK; + if (osr >= ARRAY_SIZE(osr_adc_sel)) + return NULL; + return &osr_adc_sel[osr]; + } +} + +static int nau8824_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + const struct nau8824_osr_attr *osr; + + osr = nau8824_get_osr(nau8824, substream->stream); + if (!osr || !osr->osr) + return -EINVAL; + + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + 0, CLK_DA_AD_MAX / osr->osr); +} + +static int nau8824_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, ctrl_val, bclk_fs, bclk_div; + const struct nau8824_osr_attr *osr; + int err = -EINVAL; + + nau8824_sema_acquire(nau8824, HZ); + + /* CLK_DAC or CLK_ADC = OSR * FS + * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR) + * multiplied by the audio sample rate (Fs). Note that the OSR and Fs + * values must be selected such that the maximum frequency is less + * than 6.144 MHz. + */ + nau8824->fs = params_rate(params); + osr = nau8824_get_osr(nau8824, substream->stream); + if (!osr || !osr->osr) + goto error; + if (nau8824->fs * osr->osr > CLK_DA_AD_MAX) + goto error; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_DAC_SRC_MASK, + osr->clk_src << NAU8824_CLK_DAC_SRC_SFT); + else + regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_ADC_SRC_MASK, + osr->clk_src << NAU8824_CLK_ADC_SRC_SFT); + + /* make BCLK and LRC divde configuration if the codec as master. */ + regmap_read(nau8824->regmap, + NAU8824_REG_PORT0_I2S_PCM_CTRL_2, &ctrl_val); + if (ctrl_val & NAU8824_I2S_MS_MASTER) { + /* get the bclk and fs ratio */ + bclk_fs = snd_soc_params_to_bclk(params) / nau8824->fs; + if (bclk_fs <= 32) + bclk_div = 0x3; + else if (bclk_fs <= 64) + bclk_div = 0x2; + else if (bclk_fs <= 128) + bclk_div = 0x1; + else if (bclk_fs <= 256) + bclk_div = 0; + else + goto error; + regmap_update_bits(nau8824->regmap, + NAU8824_REG_PORT0_I2S_PCM_CTRL_2, + NAU8824_I2S_LRC_DIV_MASK | NAU8824_I2S_BLK_DIV_MASK, + (bclk_div << NAU8824_I2S_LRC_DIV_SFT) | bclk_div); + } + + switch (params_width(params)) { + case 16: + val_len |= NAU8824_I2S_DL_16; + break; + case 20: + val_len |= NAU8824_I2S_DL_20; + break; + case 24: + val_len |= NAU8824_I2S_DL_24; + break; + case 32: + val_len |= NAU8824_I2S_DL_32; + break; + default: + goto error; + } + + regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1, + NAU8824_I2S_DL_MASK, val_len); + err = 0; + + error: + nau8824_sema_release(nau8824); + + return err; +} + +static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + unsigned int ctrl1_val = 0, ctrl2_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= NAU8824_I2S_MS_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8824_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8824_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8824_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1_val |= NAU8824_I2S_DF_RIGTH; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8824_I2S_DF_PCM_AB; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1_val |= NAU8824_I2S_DF_PCM_AB; + ctrl1_val |= NAU8824_I2S_PCMB_EN; + break; + default: + return -EINVAL; + } + + nau8824_sema_acquire(nau8824, HZ); + + regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1, + NAU8824_I2S_DF_MASK | NAU8824_I2S_BP_MASK | + NAU8824_I2S_PCMB_EN, ctrl1_val); + regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_2, + NAU8824_I2S_MS_MASK, ctrl2_val); + + nau8824_sema_release(nau8824); + + return 0; +} + +/** + * nau8824_set_tdm_slot - configure DAI TDM. + * @dai: DAI + * @tx_mask: Bitmask representing active TX slots. Ex. + * 0xf for normal 4 channel TDM. + * 0xf0 for shifted 4 channel TDM + * @rx_mask: Bitmask [0:1] representing active DACR RX slots. + * Bitmask [2:3] representing active DACL RX slots. + * 00=CH0,01=CH1,10=CH2,11=CH3. Ex. + * 0xf for DACL/R selecting TDM CH3. + * 0xf0 for DACL/R selecting shifted TDM CH3. + * @slots: Number of slots in use. + * @slot_width: Width in bits for each slot. + * + * Configures a DAI for TDM operation. Only support 4 slots TDM. + */ +static int nau8824_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + unsigned int tslot_l = 0, ctrl_val = 0; + + if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)) || + ((rx_mask & 0xf0) && (rx_mask & 0xf)) || + ((rx_mask & 0xf0) && (tx_mask & 0xf)) || + ((rx_mask & 0xf) && (tx_mask & 0xf0))) + return -EINVAL; + + ctrl_val |= (NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN); + if (tx_mask & 0xf0) { + tslot_l = 4 * slot_width; + ctrl_val |= (tx_mask >> 4); + } else { + ctrl_val |= tx_mask; + } + if (rx_mask & 0xf0) + ctrl_val |= ((rx_mask >> 4) << NAU8824_TDM_DACR_RX_SFT); + else + ctrl_val |= (rx_mask << NAU8824_TDM_DACR_RX_SFT); + + regmap_update_bits(nau8824->regmap, NAU8824_REG_TDM_CTRL, + NAU8824_TDM_MODE | NAU8824_TDM_OFFSET_EN | + NAU8824_TDM_DACL_RX_MASK | NAU8824_TDM_DACR_RX_MASK | + NAU8824_TDM_TX_MASK, ctrl_val); + regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_LEFT_TIME_SLOT, + NAU8824_TSLOT_L_MASK, tslot_l); + + return 0; +} + +/** + * nau8824_calc_fll_param - Calculate FLL parameters. + * @fll_in: external clock provided to codec. + * @fs: sampling rate. + * @fll_param: Pointer to structure of FLL parameters. + * + * Calculate FLL parameters to configure codec. + * + * Returns 0 for success or negative error code. + */ +static int nau8824_calc_fll_param(unsigned int fll_in, + unsigned int fs, struct nau8824_fll *fll_param) +{ + u64 fvco, fvco_max; + unsigned int fref, i, fvco_sel; + + /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing + * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. + * FREF = freq_in / NAU8824_FLL_REF_DIV_MASK + */ + for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) { + fref = fll_in / fll_pre_scalar[i].param; + if (fref <= NAU_FREF_MAX) + break; + } + if (i == ARRAY_SIZE(fll_pre_scalar)) + return -EINVAL; + fll_param->clk_ref_div = fll_pre_scalar[i].val; + + /* Choose the FLL ratio based on FREF */ + for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) { + if (fref >= fll_ratio[i].param) + break; + } + if (i == ARRAY_SIZE(fll_ratio)) + return -EINVAL; + fll_param->ratio = fll_ratio[i].val; + + /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. + * FDCO must be within the 90MHz - 124MHz or the FFL cannot be + * guaranteed across the full range of operation. + * FDCO = freq_out * 2 * mclk_src_scaling + */ + fvco_max = 0; + fvco_sel = ARRAY_SIZE(mclk_src_scaling); + for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { + fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; + if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && + fvco_max < fvco) { + fvco_max = fvco; + fvco_sel = i; + } + } + if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel) + return -EINVAL; + fll_param->mclk_src = mclk_src_scaling[fvco_sel].val; + + /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional + * input based on FDCO, FREF and FLL ratio. + */ + fvco = div_u64(fvco_max << 16, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> 16) & 0x3FF; + fll_param->fll_frac = fvco & 0xFFFF; + return 0; +} + +static void nau8824_fll_apply(struct regmap *regmap, + struct nau8824_fll *fll_param) +{ + regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK | NAU8824_CLK_MCLK_SRC_MASK, + NAU8824_CLK_SRC_MCLK | fll_param->mclk_src); + regmap_update_bits(regmap, NAU8824_REG_FLL1, + NAU8824_FLL_RATIO_MASK, fll_param->ratio); + /* FLL 16-bit fractional input */ + regmap_write(regmap, NAU8824_REG_FLL2, fll_param->fll_frac); + /* FLL 10-bit integer input */ + regmap_update_bits(regmap, NAU8824_REG_FLL3, + NAU8824_FLL_INTEGER_MASK, fll_param->fll_int); + /* FLL pre-scaler */ + regmap_update_bits(regmap, NAU8824_REG_FLL4, + NAU8824_FLL_REF_DIV_MASK, + fll_param->clk_ref_div << NAU8824_FLL_REF_DIV_SFT); + /* select divided VCO input */ + regmap_update_bits(regmap, NAU8824_REG_FLL5, + NAU8824_FLL_CLK_SW_MASK, NAU8824_FLL_CLK_SW_REF); + /* Disable free-running mode */ + regmap_update_bits(regmap, + NAU8824_REG_FLL6, NAU8824_DCO_EN, 0); + if (fll_param->fll_frac) { + regmap_update_bits(regmap, NAU8824_REG_FLL5, + NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN | + NAU8824_FLL_FTR_SW_MASK, + NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN | + NAU8824_FLL_FTR_SW_FILTER); + regmap_update_bits(regmap, NAU8824_REG_FLL6, + NAU8824_SDM_EN, NAU8824_SDM_EN); + } else { + regmap_update_bits(regmap, NAU8824_REG_FLL5, + NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN | + NAU8824_FLL_FTR_SW_MASK, NAU8824_FLL_FTR_SW_ACCU); + regmap_update_bits(regmap, + NAU8824_REG_FLL6, NAU8824_SDM_EN, 0); + } +} + +/* freq_out must be 256*Fs in order to achieve the best performance */ +static int nau8824_set_pll(struct snd_soc_component *component, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + struct nau8824_fll fll_param; + int ret, fs; + + fs = freq_out / 256; + ret = nau8824_calc_fll_param(freq_in, fs, &fll_param); + if (ret < 0) { + dev_err(nau8824->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + dev_dbg(nau8824->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n", + fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac, + fll_param.fll_int, fll_param.clk_ref_div); + + nau8824_fll_apply(nau8824->regmap, &fll_param); + mdelay(2); + regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO); + + return 0; +} + +static int nau8824_config_sysclk(struct nau8824 *nau8824, + int clk_id, unsigned int freq) +{ + struct regmap *regmap = nau8824->regmap; + + switch (clk_id) { + case NAU8824_CLK_DIS: + regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK); + regmap_update_bits(regmap, NAU8824_REG_FLL6, + NAU8824_DCO_EN, 0); + break; + + case NAU8824_CLK_MCLK: + nau8824_sema_acquire(nau8824, HZ); + regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK); + regmap_update_bits(regmap, NAU8824_REG_FLL6, + NAU8824_DCO_EN, 0); + nau8824_sema_release(nau8824); + break; + + case NAU8824_CLK_INTERNAL: + regmap_update_bits(regmap, NAU8824_REG_FLL6, + NAU8824_DCO_EN, NAU8824_DCO_EN); + regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO); + break; + + case NAU8824_CLK_FLL_MCLK: + nau8824_sema_acquire(nau8824, HZ); + regmap_update_bits(regmap, NAU8824_REG_FLL3, + NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_MCLK); + nau8824_sema_release(nau8824); + break; + + case NAU8824_CLK_FLL_BLK: + nau8824_sema_acquire(nau8824, HZ); + regmap_update_bits(regmap, NAU8824_REG_FLL3, + NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_BLK); + nau8824_sema_release(nau8824); + break; + + case NAU8824_CLK_FLL_FS: + nau8824_sema_acquire(nau8824, HZ); + regmap_update_bits(regmap, NAU8824_REG_FLL3, + NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_FS); + nau8824_sema_release(nau8824); + break; + + default: + dev_err(nau8824->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + dev_dbg(nau8824->dev, "Sysclk is %dHz and clock id is %d\n", freq, + clk_id); + + return 0; +} + +static int nau8824_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + + return nau8824_config_sysclk(nau8824, clk_id, freq); +} + +static void nau8824_resume_setup(struct nau8824 *nau8824) +{ + nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0); + if (nau8824->irq) { + /* Clear all interruption status */ + nau8824_int_status_clear_all(nau8824->regmap); + /* Enable jack detection at sleep mode, insertion detection, + * and ejection detection. + */ + regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL, + NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE); + regmap_update_bits(nau8824->regmap, + NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, + NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN); + regmap_update_bits(nau8824->regmap, + NAU8824_REG_INTERRUPT_SETTING, + NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS, 0); + } +} + +static int nau8824_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Setup codec configuration after resume */ + nau8824_resume_setup(nau8824); + } + break; + + case SND_SOC_BIAS_OFF: + regmap_update_bits(nau8824->regmap, + NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff); + regmap_update_bits(nau8824->regmap, + NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0); + break; + } + + return 0; +} + +static int nau8824_component_probe(struct snd_soc_component *component) +{ + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + nau8824->dapm = dapm; + + return 0; +} + +static int __maybe_unused nau8824_suspend(struct snd_soc_component *component) +{ + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + + if (nau8824->irq) { + disable_irq(nau8824->irq); + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + } + regcache_cache_only(nau8824->regmap, true); + regcache_mark_dirty(nau8824->regmap); + + return 0; +} + +static int __maybe_unused nau8824_resume(struct snd_soc_component *component) +{ + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + int ret; + + regcache_cache_only(nau8824->regmap, false); + regcache_sync(nau8824->regmap); + if (nau8824->irq) { + /* Hold semaphore to postpone playback happening + * until jack detection done. + */ + nau8824->resume_lock = true; + ret = nau8824_sema_acquire(nau8824, 0); + if (ret) + nau8824->resume_lock = false; + enable_irq(nau8824->irq); + } + + return 0; +} + +static const struct snd_soc_component_driver nau8824_component_driver = { + .probe = nau8824_component_probe, + .set_sysclk = nau8824_set_sysclk, + .set_pll = nau8824_set_pll, + .set_bias_level = nau8824_set_bias_level, + .suspend = nau8824_suspend, + .resume = nau8824_resume, + .controls = nau8824_snd_controls, + .num_controls = ARRAY_SIZE(nau8824_snd_controls), + .dapm_widgets = nau8824_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8824_dapm_widgets), + .dapm_routes = nau8824_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8824_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_dai_ops nau8824_dai_ops = { + .startup = nau8824_dai_startup, + .hw_params = nau8824_hw_params, + .set_fmt = nau8824_set_fmt, + .set_tdm_slot = nau8824_set_tdm_slot, +}; + +#define NAU8824_RATES SNDRV_PCM_RATE_8000_192000 +#define NAU8824_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver nau8824_dai = { + .name = NAU8824_CODEC_DAI, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8824_RATES, + .formats = NAU8824_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8824_RATES, + .formats = NAU8824_FORMATS, + }, + .ops = &nau8824_dai_ops, +}; + +static const struct regmap_config nau8824_regmap_config = { + .val_bits = NAU8824_REG_ADDR_LEN, + .reg_bits = NAU8824_REG_DATA_LEN, + + .max_register = NAU8824_REG_MAX, + .readable_reg = nau8824_readable_reg, + .writeable_reg = nau8824_writeable_reg, + .volatile_reg = nau8824_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8824_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8824_reg_defaults), +}; + +/** + * nau8824_enable_jack_detect - Specify a jack for event reporting + * + * @component: component to register the jack with + * @jack: jack to use to report headset and button events on + * + * After this function has been called the headset insert/remove and button + * events will be routed to the given jack. Jack can be null to stop + * reporting. + */ +int nau8824_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct nau8824 *nau8824 = snd_soc_component_get_drvdata(component); + int ret; + + nau8824->jack = jack; + /* Initiate jack detection work queue */ + INIT_WORK(&nau8824->jdet_work, nau8824_jdet_work); + ret = devm_request_threaded_irq(nau8824->dev, nau8824->irq, NULL, + nau8824_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "nau8824", nau8824); + if (ret) { + dev_err(nau8824->dev, "Cannot request irq %d (%d)\n", + nau8824->irq, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(nau8824_enable_jack_detect); + +static void nau8824_reset_chip(struct regmap *regmap) +{ + regmap_write(regmap, NAU8824_REG_RESET, 0x00); + regmap_write(regmap, NAU8824_REG_RESET, 0x00); +} + +static void nau8824_setup_buttons(struct nau8824 *nau8824) +{ + struct regmap *regmap = nau8824->regmap; + + regmap_update_bits(regmap, NAU8824_REG_SAR_ADC, + NAU8824_SAR_TRACKING_GAIN_MASK, + nau8824->sar_voltage << NAU8824_SAR_TRACKING_GAIN_SFT); + regmap_update_bits(regmap, NAU8824_REG_SAR_ADC, + NAU8824_SAR_COMPARE_TIME_MASK, + nau8824->sar_compare_time << NAU8824_SAR_COMPARE_TIME_SFT); + regmap_update_bits(regmap, NAU8824_REG_SAR_ADC, + NAU8824_SAR_SAMPLING_TIME_MASK, + nau8824->sar_sampling_time << NAU8824_SAR_SAMPLING_TIME_SFT); + + regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT, + NAU8824_LEVELS_NR_MASK, + (nau8824->sar_threshold_num - 1) << NAU8824_LEVELS_NR_SFT); + regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT, + NAU8824_HYSTERESIS_MASK, + nau8824->sar_hysteresis << NAU8824_HYSTERESIS_SFT); + regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT, + NAU8824_SHORTKEY_DEBOUNCE_MASK, + nau8824->key_debounce << NAU8824_SHORTKEY_DEBOUNCE_SFT); + + regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_1, + (nau8824->sar_threshold[0] << 8) | nau8824->sar_threshold[1]); + regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_2, + (nau8824->sar_threshold[2] << 8) | nau8824->sar_threshold[3]); + regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_3, + (nau8824->sar_threshold[4] << 8) | nau8824->sar_threshold[5]); + regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_4, + (nau8824->sar_threshold[6] << 8) | nau8824->sar_threshold[7]); +} + +static void nau8824_init_regs(struct nau8824 *nau8824) +{ + struct regmap *regmap = nau8824->regmap; + + /* Enable Bias/VMID/VMID Tieoff */ + regmap_update_bits(regmap, NAU8824_REG_BIAS_ADJ, + NAU8824_VMID | NAU8824_VMID_SEL_MASK, NAU8824_VMID | + (nau8824->vref_impedance << NAU8824_VMID_SEL_SFT)); + regmap_update_bits(regmap, NAU8824_REG_BOOST, + NAU8824_GLOBAL_BIAS_EN, NAU8824_GLOBAL_BIAS_EN); + mdelay(2); + regmap_update_bits(regmap, NAU8824_REG_MIC_BIAS, + NAU8824_MICBIAS_VOLTAGE_MASK, nau8824->micbias_voltage); + /* Disable Boost Driver, Automatic Short circuit protection enable */ + regmap_update_bits(regmap, NAU8824_REG_BOOST, + NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS | + NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN, + NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS | + NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN); + /* Scaling for ADC and DAC clock */ + regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_ADC_SRC_MASK | NAU8824_CLK_DAC_SRC_MASK, + (0x1 << NAU8824_CLK_ADC_SRC_SFT) | + (0x1 << NAU8824_CLK_DAC_SRC_SFT)); + regmap_update_bits(regmap, NAU8824_REG_DAC_MUTE_CTRL, + NAU8824_DAC_ZC_EN, NAU8824_DAC_ZC_EN); + regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL, + NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN | + NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN | + NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN, + NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN | + NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN | + NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN); + regmap_update_bits(regmap, NAU8824_REG_CLK_GATING_ENA, + NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN | + NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN | + NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN | + NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN, + NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN | + NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN | + NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN | + NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN); + /* Class G timer 64ms */ + regmap_update_bits(regmap, NAU8824_REG_CLASSG, + NAU8824_CLASSG_TIMER_MASK, + 0x20 << NAU8824_CLASSG_TIMER_SFT); + regmap_update_bits(regmap, NAU8824_REG_TRIM_SETTINGS, + NAU8824_DRV_CURR_INC, NAU8824_DRV_CURR_INC); + /* Disable DACR/L power */ + regmap_update_bits(regmap, NAU8824_REG_CHARGE_PUMP_CONTROL, + NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN | + NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL, + NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN | + NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL); + /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input + * signal to avoid any glitches due to power up transients in both + * the analog and digital DAC circuit. + */ + regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO, + NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN); + /* Config L/R channel */ + regmap_update_bits(regmap, NAU8824_REG_DAC_CH0_DGAIN_CTRL, + NAU8824_DAC_CH0_SEL_MASK, NAU8824_DAC_CH0_SEL_I2S0); + regmap_update_bits(regmap, NAU8824_REG_DAC_CH1_DGAIN_CTRL, + NAU8824_DAC_CH1_SEL_MASK, NAU8824_DAC_CH1_SEL_I2S1); + regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO, + NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN, + NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN); + /* Default oversampling/decimations settings are unusable + * (audible hiss). Set it to something better. + */ + regmap_update_bits(regmap, NAU8824_REG_ADC_FILTER_CTRL, + NAU8824_ADC_SYNC_DOWN_MASK, NAU8824_ADC_SYNC_DOWN_64); + regmap_update_bits(regmap, NAU8824_REG_DAC_FILTER_CTRL_1, + NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_MASK, + NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_64); + /* DAC clock delay 2ns, VREF */ + regmap_update_bits(regmap, NAU8824_REG_RDAC, + NAU8824_RDAC_CLK_DELAY_MASK | NAU8824_RDAC_VREF_MASK, + (0x2 << NAU8824_RDAC_CLK_DELAY_SFT) | + (0x3 << NAU8824_RDAC_VREF_SFT)); + /* PGA input mode selection */ + regmap_update_bits(regmap, NAU8824_REG_FEPGA, + NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN, + NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN); + /* Digital microphone control */ + regmap_update_bits(regmap, NAU8824_REG_ANALOG_CONTROL_1, + NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST, + NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST); + regmap_update_bits(regmap, NAU8824_REG_JACK_DET_CTRL, + NAU8824_JACK_LOGIC, + /* jkdet_polarity - 1 is for active-low */ + nau8824->jkdet_polarity ? 0 : NAU8824_JACK_LOGIC); + regmap_update_bits(regmap, + NAU8824_REG_JACK_DET_CTRL, NAU8824_JACK_EJECT_DT_MASK, + (nau8824->jack_eject_debounce << NAU8824_JACK_EJECT_DT_SFT)); + if (nau8824->sar_threshold_num) + nau8824_setup_buttons(nau8824); +} + +static int nau8824_setup_irq(struct nau8824 *nau8824) +{ + /* Disable interruption before codec initiation done */ + regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL, + NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE); + regmap_update_bits(nau8824->regmap, + NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff); + regmap_update_bits(nau8824->regmap, NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0); + + return 0; +} + +static void nau8824_print_device_properties(struct nau8824 *nau8824) +{ + struct device *dev = nau8824->dev; + int i; + + dev_dbg(dev, "jkdet-polarity: %d\n", nau8824->jkdet_polarity); + dev_dbg(dev, "micbias-voltage: %d\n", nau8824->micbias_voltage); + dev_dbg(dev, "vref-impedance: %d\n", nau8824->vref_impedance); + + dev_dbg(dev, "sar-threshold-num: %d\n", nau8824->sar_threshold_num); + for (i = 0; i < nau8824->sar_threshold_num; i++) + dev_dbg(dev, "sar-threshold[%d]=%x\n", i, + nau8824->sar_threshold[i]); + + dev_dbg(dev, "sar-hysteresis: %d\n", nau8824->sar_hysteresis); + dev_dbg(dev, "sar-voltage: %d\n", nau8824->sar_voltage); + dev_dbg(dev, "sar-compare-time: %d\n", nau8824->sar_compare_time); + dev_dbg(dev, "sar-sampling-time: %d\n", nau8824->sar_sampling_time); + dev_dbg(dev, "short-key-debounce: %d\n", nau8824->key_debounce); + dev_dbg(dev, "jack-eject-debounce: %d\n", + nau8824->jack_eject_debounce); +} + +static int nau8824_read_device_properties(struct device *dev, + struct nau8824 *nau8824) { + int ret; + + ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity", + &nau8824->jkdet_polarity); + if (ret) + nau8824->jkdet_polarity = 1; + ret = device_property_read_u32(dev, "nuvoton,micbias-voltage", + &nau8824->micbias_voltage); + if (ret) + nau8824->micbias_voltage = 6; + ret = device_property_read_u32(dev, "nuvoton,vref-impedance", + &nau8824->vref_impedance); + if (ret) + nau8824->vref_impedance = 2; + ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num", + &nau8824->sar_threshold_num); + if (ret) + nau8824->sar_threshold_num = 4; + ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold", + nau8824->sar_threshold, nau8824->sar_threshold_num); + if (ret) { + nau8824->sar_threshold[0] = 0x0a; + nau8824->sar_threshold[1] = 0x14; + nau8824->sar_threshold[2] = 0x26; + nau8824->sar_threshold[3] = 0x73; + } + ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis", + &nau8824->sar_hysteresis); + if (ret) + nau8824->sar_hysteresis = 0; + ret = device_property_read_u32(dev, "nuvoton,sar-voltage", + &nau8824->sar_voltage); + if (ret) + nau8824->sar_voltage = 6; + ret = device_property_read_u32(dev, "nuvoton,sar-compare-time", + &nau8824->sar_compare_time); + if (ret) + nau8824->sar_compare_time = 1; + ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time", + &nau8824->sar_sampling_time); + if (ret) + nau8824->sar_sampling_time = 1; + ret = device_property_read_u32(dev, "nuvoton,short-key-debounce", + &nau8824->key_debounce); + if (ret) + nau8824->key_debounce = 0; + ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce", + &nau8824->jack_eject_debounce); + if (ret) + nau8824->jack_eject_debounce = 1; + + return 0; +} + +/* Please keep this list alphabetically sorted */ +static const struct dmi_system_id nau8824_quirk_table[] = { + { + /* Cyberbook T116 rugged tablet */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "20170531"), + }, + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH | + NAU8824_MONO_SPEAKER), + }, + { + /* CUBE iwork8 Air */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "cube"), + DMI_MATCH(DMI_PRODUCT_NAME, "i1-TF"), + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + }, + .driver_data = (void *)(NAU8824_MONO_SPEAKER), + }, + { + /* Pipo W2S */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PIPO"), + DMI_MATCH(DMI_PRODUCT_NAME, "W2S"), + }, + .driver_data = (void *)(NAU8824_MONO_SPEAKER), + }, + { + /* Positivo CW14Q01P */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_BOARD_NAME, "CW14Q01P"), + }, + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH), + }, + { + /* Positivo K1424G */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_BOARD_NAME, "K1424G"), + }, + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH), + }, + { + /* Positivo N14ZP74G */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_BOARD_NAME, "N14ZP74G"), + }, + .driver_data = (void *)(NAU8824_JD_ACTIVE_HIGH), + }, + {} +}; + +static void nau8824_check_quirks(void) +{ + const struct dmi_system_id *dmi_id; + + if (quirk_override != -1) { + nau8824_quirk = quirk_override; + return; + } + + dmi_id = dmi_first_match(nau8824_quirk_table); + if (dmi_id) + nau8824_quirk = (unsigned long)dmi_id->driver_data; +} + +const char *nau8824_components(void) +{ + nau8824_check_quirks(); + + if (nau8824_quirk & NAU8824_MONO_SPEAKER) + return "cfg-spk:1"; + else + return "cfg-spk:2"; +} +EXPORT_SYMBOL_GPL(nau8824_components); + +static int nau8824_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct nau8824 *nau8824 = dev_get_platdata(dev); + int ret, value; + + if (!nau8824) { + nau8824 = devm_kzalloc(dev, sizeof(*nau8824), GFP_KERNEL); + if (!nau8824) + return -ENOMEM; + ret = nau8824_read_device_properties(dev, nau8824); + if (ret) + return ret; + } + i2c_set_clientdata(i2c, nau8824); + + nau8824->regmap = devm_regmap_init_i2c(i2c, &nau8824_regmap_config); + if (IS_ERR(nau8824->regmap)) + return PTR_ERR(nau8824->regmap); + nau8824->resume_lock = false; + nau8824->dev = dev; + nau8824->irq = i2c->irq; + sema_init(&nau8824->jd_sem, 1); + + nau8824_check_quirks(); + + if (nau8824_quirk & NAU8824_JD_ACTIVE_HIGH) + nau8824->jkdet_polarity = 0; + + nau8824_print_device_properties(nau8824); + + ret = regmap_read(nau8824->regmap, NAU8824_REG_I2C_DEVICE_ID, &value); + if (ret < 0) { + dev_err(dev, "Failed to read device id from the NAU8824: %d\n", + ret); + return ret; + } + nau8824_reset_chip(nau8824->regmap); + nau8824_init_regs(nau8824); + + if (i2c->irq) + nau8824_setup_irq(nau8824); + + return devm_snd_soc_register_component(dev, + &nau8824_component_driver, &nau8824_dai, 1); +} + +static const struct i2c_device_id nau8824_i2c_ids[] = { + { "nau8824", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau8824_i2c_ids); + +#ifdef CONFIG_OF +static const struct of_device_id nau8824_of_ids[] = { + { .compatible = "nuvoton,nau8824", }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8824_of_ids); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id nau8824_acpi_match[] = { + { "10508824", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, nau8824_acpi_match); +#endif + +static struct i2c_driver nau8824_i2c_driver = { + .driver = { + .name = "nau8824", + .of_match_table = of_match_ptr(nau8824_of_ids), + .acpi_match_table = ACPI_PTR(nau8824_acpi_match), + }, + .probe = nau8824_i2c_probe, + .id_table = nau8824_i2c_ids, +}; +module_i2c_driver(nau8824_i2c_driver); + + +MODULE_DESCRIPTION("ASoC NAU88L24 driver"); +MODULE_AUTHOR("John Hsu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h new file mode 100644 index 0000000000..5fcfc43dfc --- /dev/null +++ b/sound/soc/codecs/nau8824.h @@ -0,0 +1,477 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NAU88L24 ALSA SoC audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * Author: John Hsu + */ + +#ifndef __NAU8824_H__ +#define __NAU8824_H__ + +#define NAU8824_REG_RESET 0x00 +#define NAU8824_REG_ENA_CTRL 0x01 +#define NAU8824_REG_CLK_GATING_ENA 0x02 +#define NAU8824_REG_CLK_DIVIDER 0x03 +#define NAU8824_REG_FLL1 0x04 +#define NAU8824_REG_FLL2 0x05 +#define NAU8824_REG_FLL3 0x06 +#define NAU8824_REG_FLL4 0x07 +#define NAU8824_REG_FLL5 0x08 +#define NAU8824_REG_FLL6 0x09 +#define NAU8824_REG_FLL_VCO_RSV 0x0A +#define NAU8824_REG_JACK_DET_CTRL 0x0D +#define NAU8824_REG_INTERRUPT_SETTING_1 0x0F +#define NAU8824_REG_IRQ 0x10 +#define NAU8824_REG_CLEAR_INT_REG 0x11 +#define NAU8824_REG_INTERRUPT_SETTING 0x12 +#define NAU8824_REG_SAR_ADC 0x13 +#define NAU8824_REG_VDET_COEFFICIENT 0x14 +#define NAU8824_REG_VDET_THRESHOLD_1 0x15 +#define NAU8824_REG_VDET_THRESHOLD_2 0x16 +#define NAU8824_REG_VDET_THRESHOLD_3 0x17 +#define NAU8824_REG_VDET_THRESHOLD_4 0x18 +#define NAU8824_REG_GPIO_SEL 0x1A +#define NAU8824_REG_PORT0_I2S_PCM_CTRL_1 0x1C +#define NAU8824_REG_PORT0_I2S_PCM_CTRL_2 0x1D +#define NAU8824_REG_PORT0_LEFT_TIME_SLOT 0x1E +#define NAU8824_REG_PORT0_RIGHT_TIME_SLOT 0x1F +#define NAU8824_REG_TDM_CTRL 0x20 +#define NAU8824_REG_ADC_HPF_FILTER 0x23 +#define NAU8824_REG_ADC_FILTER_CTRL 0x24 +#define NAU8824_REG_DAC_FILTER_CTRL_1 0x25 +#define NAU8824_REG_DAC_FILTER_CTRL_2 0x26 +#define NAU8824_REG_NOTCH_FILTER_1 0x27 +#define NAU8824_REG_NOTCH_FILTER_2 0x28 +#define NAU8824_REG_EQ1_LOW 0x29 +#define NAU8824_REG_EQ2_EQ3 0x2A +#define NAU8824_REG_EQ4_EQ5 0x2B +#define NAU8824_REG_ADC_CH0_DGAIN_CTRL 0x2D +#define NAU8824_REG_ADC_CH1_DGAIN_CTRL 0x2E +#define NAU8824_REG_ADC_CH2_DGAIN_CTRL 0x2F +#define NAU8824_REG_ADC_CH3_DGAIN_CTRL 0x30 +#define NAU8824_REG_DAC_MUTE_CTRL 0x31 +#define NAU8824_REG_DAC_CH0_DGAIN_CTRL 0x32 +#define NAU8824_REG_DAC_CH1_DGAIN_CTRL 0x33 +#define NAU8824_REG_ADC_TO_DAC_ST 0x34 +#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 0x38 +#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH01 0x39 +#define NAU8824_REG_DRC_SLOPE_ADC_CH01 0x3A +#define NAU8824_REG_DRC_ATKDCY_ADC_CH01 0x3B +#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH23 0x3C +#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH23 0x3D +#define NAU8824_REG_DRC_SLOPE_ADC_CH23 0x3E +#define NAU8824_REG_DRC_ATKDCY_ADC_CH23 0x3F +#define NAU8824_REG_DRC_GAINL_ADC0 0x40 +#define NAU8824_REG_DRC_GAINL_ADC1 0x41 +#define NAU8824_REG_DRC_GAINL_ADC2 0x42 +#define NAU8824_REG_DRC_GAINL_ADC3 0x43 +#define NAU8824_REG_DRC_KNEE_IP12_DAC 0x45 +#define NAU8824_REG_DRC_KNEE_IP34_DAC 0x46 +#define NAU8824_REG_DRC_SLOPE_DAC 0x47 +#define NAU8824_REG_DRC_ATKDCY_DAC 0x48 +#define NAU8824_REG_DRC_GAIN_DAC_CH0 0x49 +#define NAU8824_REG_DRC_GAIN_DAC_CH1 0x4A +#define NAU8824_REG_MODE 0x4C +#define NAU8824_REG_MODE1 0x4D +#define NAU8824_REG_MODE2 0x4E +#define NAU8824_REG_CLASSG 0x50 +#define NAU8824_REG_OTP_EFUSE 0x51 +#define NAU8824_REG_OTPDOUT_1 0x53 +#define NAU8824_REG_OTPDOUT_2 0x54 +#define NAU8824_REG_MISC_CTRL 0x55 +#define NAU8824_REG_I2C_TIMEOUT 0x56 +#define NAU8824_REG_TEST_MODE 0x57 +#define NAU8824_REG_I2C_DEVICE_ID 0x58 +#define NAU8824_REG_SAR_ADC_DATA_OUT 0x59 +#define NAU8824_REG_BIAS_ADJ 0x66 +#define NAU8824_REG_PGA_GAIN 0x67 +#define NAU8824_REG_TRIM_SETTINGS 0x68 +#define NAU8824_REG_ANALOG_CONTROL_1 0x69 +#define NAU8824_REG_ANALOG_CONTROL_2 0x6A +#define NAU8824_REG_ENABLE_LO 0x6B +#define NAU8824_REG_GAIN_LO 0x6C +#define NAU8824_REG_CLASSD_GAIN_1 0x6D +#define NAU8824_REG_CLASSD_GAIN_2 0x6E +#define NAU8824_REG_ANALOG_ADC_1 0x71 +#define NAU8824_REG_ANALOG_ADC_2 0x72 +#define NAU8824_REG_RDAC 0x73 +#define NAU8824_REG_MIC_BIAS 0x74 +#define NAU8824_REG_HS_VOLUME_CONTROL 0x75 +#define NAU8824_REG_BOOST 0x76 +#define NAU8824_REG_FEPGA 0x77 +#define NAU8824_REG_FEPGA_II 0x78 +#define NAU8824_REG_FEPGA_SE 0x79 +#define NAU8824_REG_FEPGA_ATTENUATION 0x7A +#define NAU8824_REG_ATT_PORT0 0x7B +#define NAU8824_REG_ATT_PORT1 0x7C +#define NAU8824_REG_POWER_UP_CONTROL 0x7F +#define NAU8824_REG_CHARGE_PUMP_CONTROL 0x80 +#define NAU8824_REG_CHARGE_PUMP_INPUT 0x81 +#define NAU8824_REG_MAX NAU8824_REG_CHARGE_PUMP_INPUT +/* 16-bit control register address, and 16-bits control register data */ +#define NAU8824_REG_ADDR_LEN 16 +#define NAU8824_REG_DATA_LEN 16 + + +/* ENA_CTRL (0x1) */ +#define NAU8824_DMIC_LCH_EDGE_CH23 (0x1 << 12) +#define NAU8824_DMIC_LCH_EDGE_CH01 (0x1 << 11) +#define NAU8824_JD_SLEEP_MODE (0x1 << 10) +#define NAU8824_ADC_CH3_DMIC_SFT 9 +#define NAU8824_ADC_CH3_DMIC_EN (0x1 << NAU8824_ADC_CH3_DMIC_SFT) +#define NAU8824_ADC_CH2_DMIC_SFT 8 +#define NAU8824_ADC_CH2_DMIC_EN (0x1 << NAU8824_ADC_CH2_DMIC_SFT) +#define NAU8824_ADC_CH1_DMIC_SFT 7 +#define NAU8824_ADC_CH1_DMIC_EN (0x1 << NAU8824_ADC_CH1_DMIC_SFT) +#define NAU8824_ADC_CH0_DMIC_SFT 6 +#define NAU8824_ADC_CH0_DMIC_EN (0x1 << NAU8824_ADC_CH0_DMIC_SFT) +#define NAU8824_DAC_CH1_EN (0x1 << 5) +#define NAU8824_DAC_CH0_EN (0x1 << 4) +#define NAU8824_ADC_CH3_EN (0x1 << 3) +#define NAU8824_ADC_CH2_EN (0x1 << 2) +#define NAU8824_ADC_CH1_EN (0x1 << 1) +#define NAU8824_ADC_CH0_EN 0x1 + +/* CLK_GATING_ENA (0x02) */ +#define NAU8824_CLK_ADC_CH23_EN (0x1 << 15) +#define NAU8824_CLK_ADC_CH01_EN (0x1 << 14) +#define NAU8824_CLK_DAC_CH1_EN (0x1 << 13) +#define NAU8824_CLK_DAC_CH0_EN (0x1 << 12) +#define NAU8824_CLK_I2S_EN (0x1 << 7) +#define NAU8824_CLK_GAIN_EN (0x1 << 5) +#define NAU8824_CLK_SAR_EN (0x1 << 3) +#define NAU8824_CLK_DMIC_CH23_EN (0x1 << 1) + +/* CLK_DIVIDER (0x3) */ +#define NAU8824_CLK_SRC_SFT 15 +#define NAU8824_CLK_SRC_MASK (1 << NAU8824_CLK_SRC_SFT) +#define NAU8824_CLK_SRC_VCO (1 << NAU8824_CLK_SRC_SFT) +#define NAU8824_CLK_SRC_MCLK (0 << NAU8824_CLK_SRC_SFT) +#define NAU8824_CLK_MCLK_SRC_MASK (0xf << 0) +#define NAU8824_CLK_DMIC_SRC_SFT 10 +#define NAU8824_CLK_DMIC_SRC_MASK (0x7 << NAU8824_CLK_DMIC_SRC_SFT) +#define NAU8824_CLK_ADC_SRC_SFT 6 +#define NAU8824_CLK_ADC_SRC_MASK (0x3 << NAU8824_CLK_ADC_SRC_SFT) +#define NAU8824_CLK_DAC_SRC_SFT 4 +#define NAU8824_CLK_DAC_SRC_MASK (0x3 << NAU8824_CLK_DAC_SRC_SFT) + +/* FLL1 (0x04) */ +#define NAU8824_FLL_RATIO_MASK (0x7f << 0) + +/* FLL3 (0x06) */ +#define NAU8824_FLL_INTEGER_MASK (0x3ff << 0) +#define NAU8824_FLL_CLK_SRC_SFT 10 +#define NAU8824_FLL_CLK_SRC_MASK (0x3 << NAU8824_FLL_CLK_SRC_SFT) +#define NAU8824_FLL_CLK_SRC_MCLK (0 << NAU8824_FLL_CLK_SRC_SFT) +#define NAU8824_FLL_CLK_SRC_BLK (0x2 << NAU8824_FLL_CLK_SRC_SFT) +#define NAU8824_FLL_CLK_SRC_FS (0x3 << NAU8824_FLL_CLK_SRC_SFT) + +/* FLL4 (0x07) */ +#define NAU8824_FLL_REF_DIV_SFT 10 +#define NAU8824_FLL_REF_DIV_MASK (0x3 << NAU8824_FLL_REF_DIV_SFT) + +/* FLL5 (0x08) */ +#define NAU8824_FLL_PDB_DAC_EN (0x1 << 15) +#define NAU8824_FLL_LOOP_FTR_EN (0x1 << 14) +#define NAU8824_FLL_CLK_SW_MASK (0x1 << 13) +#define NAU8824_FLL_CLK_SW_N2 (0x1 << 13) +#define NAU8824_FLL_CLK_SW_REF (0x0 << 13) +#define NAU8824_FLL_FTR_SW_MASK (0x1 << 12) +#define NAU8824_FLL_FTR_SW_ACCU (0x1 << 12) +#define NAU8824_FLL_FTR_SW_FILTER (0x0 << 12) + +/* FLL6 (0x9) */ +#define NAU8824_DCO_EN (0x1 << 15) +#define NAU8824_SDM_EN (0x1 << 14) + +/* IRQ (0x10) */ +#define NAU8824_SHORT_CIRCUIT_IRQ (0x1 << 7) +#define NAU8824_IMPEDANCE_MEAS_IRQ (0x1 << 6) +#define NAU8824_KEY_RELEASE_IRQ (0x1 << 5) +#define NAU8824_KEY_LONG_PRESS_IRQ (0x1 << 4) +#define NAU8824_KEY_SHORT_PRESS_IRQ (0x1 << 3) +#define NAU8824_JACK_EJECTION_DETECTED (0x1 << 1) +#define NAU8824_JACK_INSERTION_DETECTED 0x1 + +/* JACK_DET_CTRL (0x0D) */ +#define NAU8824_JACK_EJECT_DT_SFT 2 +#define NAU8824_JACK_EJECT_DT_MASK (0x3 << NAU8824_JACK_EJECT_DT_SFT) +#define NAU8824_JACK_LOGIC (0x1 << 1) + + +/* INTERRUPT_SETTING_1 (0x0F) */ +#define NAU8824_IRQ_EJECT_EN (0x1 << 9) +#define NAU8824_IRQ_INSERT_EN (0x1 << 8) + +/* INTERRUPT_SETTING (0x12) */ +#define NAU8824_IRQ_KEY_RELEASE_DIS (0x1 << 5) +#define NAU8824_IRQ_KEY_SHORT_PRESS_DIS (0x1 << 3) +#define NAU8824_IRQ_EJECT_DIS (0x1 << 1) +#define NAU8824_IRQ_INSERT_DIS 0x1 + +/* SAR_ADC (0x13) */ +#define NAU8824_SAR_ADC_EN_SFT 12 +#define NAU8824_SAR_TRACKING_GAIN_SFT 8 +#define NAU8824_SAR_TRACKING_GAIN_MASK (0x7 << NAU8824_SAR_TRACKING_GAIN_SFT) +#define NAU8824_SAR_COMPARE_TIME_SFT 2 +#define NAU8824_SAR_COMPARE_TIME_MASK (3 << 2) +#define NAU8824_SAR_SAMPLING_TIME_SFT 0 +#define NAU8824_SAR_SAMPLING_TIME_MASK (3 << 0) + +/* VDET_COEFFICIENT (0x14) */ +#define NAU8824_SHORTKEY_DEBOUNCE_SFT 12 +#define NAU8824_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8824_SHORTKEY_DEBOUNCE_SFT) +#define NAU8824_LEVELS_NR_SFT 8 +#define NAU8824_LEVELS_NR_MASK (0x7 << 8) +#define NAU8824_HYSTERESIS_SFT 0 +#define NAU8824_HYSTERESIS_MASK 0xf + +/* PORT0_I2S_PCM_CTRL_1 (0x1C) */ +#define NAU8824_I2S_BP_SFT 7 +#define NAU8824_I2S_BP_MASK (1 << NAU8824_I2S_BP_SFT) +#define NAU8824_I2S_BP_INV (1 << NAU8824_I2S_BP_SFT) +#define NAU8824_I2S_PCMB_SFT 6 +#define NAU8824_I2S_PCMB_EN (1 << NAU8824_I2S_PCMB_SFT) +#define NAU8824_I2S_DL_SFT 2 +#define NAU8824_I2S_DL_MASK (0x3 << NAU8824_I2S_DL_SFT) +#define NAU8824_I2S_DL_16 (0 << NAU8824_I2S_DL_SFT) +#define NAU8824_I2S_DL_20 (1 << NAU8824_I2S_DL_SFT) +#define NAU8824_I2S_DL_24 (2 << NAU8824_I2S_DL_SFT) +#define NAU8824_I2S_DL_32 (3 << NAU8824_I2S_DL_SFT) +#define NAU8824_I2S_DF_MASK 0x3 +#define NAU8824_I2S_DF_RIGTH 0 +#define NAU8824_I2S_DF_LEFT 1 +#define NAU8824_I2S_DF_I2S 2 +#define NAU8824_I2S_DF_PCM_AB 3 + + +/* PORT0_I2S_PCM_CTRL_2 (0x1D) */ +#define NAU8824_I2S_LRC_DIV_SFT 12 +#define NAU8824_I2S_LRC_DIV_MASK (0x3 << NAU8824_I2S_LRC_DIV_SFT) +#define NAU8824_I2S_MS_SFT 3 +#define NAU8824_I2S_MS_MASK (1 << NAU8824_I2S_MS_SFT) +#define NAU8824_I2S_MS_MASTER (1 << NAU8824_I2S_MS_SFT) +#define NAU8824_I2S_MS_SLAVE (0 << NAU8824_I2S_MS_SFT) +#define NAU8824_I2S_BLK_DIV_MASK 0x7 + +/* PORT0_LEFT_TIME_SLOT (0x1E) */ +#define NAU8824_TSLOT_L_MASK 0x3ff + +/* TDM_CTRL (0x20) */ +#define NAU8824_TDM_MODE (0x1 << 15) +#define NAU8824_TDM_OFFSET_EN (0x1 << 14) +#define NAU8824_TDM_DACL_RX_SFT 6 +#define NAU8824_TDM_DACL_RX_MASK (0x3 << NAU8824_TDM_DACL_RX_SFT) +#define NAU8824_TDM_DACR_RX_SFT 4 +#define NAU8824_TDM_DACR_RX_MASK (0x3 << NAU8824_TDM_DACR_RX_SFT) +#define NAU8824_TDM_TX_MASK 0xf + +/* ADC_FILTER_CTRL (0x24) */ +#define NAU8824_ADC_SYNC_DOWN_MASK 0x3 +#define NAU8824_ADC_SYNC_DOWN_32 0 +#define NAU8824_ADC_SYNC_DOWN_64 1 +#define NAU8824_ADC_SYNC_DOWN_128 2 +#define NAU8824_ADC_SYNC_DOWN_256 3 + +/* DAC_FILTER_CTRL_1 (0x25) */ +#define NAU8824_DAC_CICCLP_OFF (0x1 << 7) +#define NAU8824_DAC_OVERSAMPLE_MASK 0x7 +#define NAU8824_DAC_OVERSAMPLE_64 0 +#define NAU8824_DAC_OVERSAMPLE_256 1 +#define NAU8824_DAC_OVERSAMPLE_128 2 +#define NAU8824_DAC_OVERSAMPLE_32 4 + +/* DAC_MUTE_CTRL (0x31) */ +#define NAU8824_DAC_CH01_MIX 0x3 +#define NAU8824_DAC_ZC_EN (0x1 << 11) + +/* DAC_CH0_DGAIN_CTRL (0x32) */ +#define NAU8824_DAC_CH0_SEL_SFT 9 +#define NAU8824_DAC_CH0_SEL_MASK (0x1 << NAU8824_DAC_CH0_SEL_SFT) +#define NAU8824_DAC_CH0_SEL_I2S0 (0x0 << NAU8824_DAC_CH0_SEL_SFT) +#define NAU8824_DAC_CH0_SEL_I2S1 (0x1 << NAU8824_DAC_CH0_SEL_SFT) +#define NAU8824_DAC_CH0_VOL_MASK 0x1ff + +/* DAC_CH1_DGAIN_CTRL (0x33) */ +#define NAU8824_DAC_CH1_SEL_SFT 9 +#define NAU8824_DAC_CH1_SEL_MASK (0x1 << NAU8824_DAC_CH1_SEL_SFT) +#define NAU8824_DAC_CH1_SEL_I2S0 (0x0 << NAU8824_DAC_CH1_SEL_SFT) +#define NAU8824_DAC_CH1_SEL_I2S1 (0x1 << NAU8824_DAC_CH1_SEL_SFT) +#define NAU8824_DAC_CH1_VOL_MASK 0x1ff + +/* CLASSG (0x50) */ +#define NAU8824_CLASSG_TIMER_SFT 8 +#define NAU8824_CLASSG_TIMER_MASK (0x3f << NAU8824_CLASSG_TIMER_SFT) +#define NAU8824_CLASSG_LDAC_EN_SFT 2 +#define NAU8824_CLASSG_RDAC_EN_SFT 1 +#define NAU8824_CLASSG_EN_SFT 0 + +/* SAR_ADC_DATA_OUT (0x59) */ +#define NAU8824_SAR_ADC_DATA_MASK 0xff + +/* BIAS_ADJ (0x66) */ +#define NAU8824_VMID (1 << 6) +#define NAU8824_VMID_SEL_SFT 4 +#define NAU8824_VMID_SEL_MASK (3 << NAU8824_VMID_SEL_SFT) +#define NAU8824_DMIC2_EN_SFT 3 +#define NAU8824_DMIC1_EN_SFT 2 + +/* TRIM_SETTINGS (0x68) */ +#define NAU8824_DRV_CURR_INC (1 << 15) + +/* ANALOG_CONTROL_1 (0x69) */ +#define NAU8824_DMIC_CLK_DRV_STRG (1 << 3) +#define NAU8824_DMIC_CLK_SLEW_FAST (0x7) + +/* ANALOG_CONTROL_2 (0x6A) */ +#define NAU8824_CLASSD_CLAMP_DIS_SFT 3 +#define NAU8824_CLASSD_CLAMP_DIS (0x1 << NAU8824_CLASSD_CLAMP_DIS_SFT) + +/* ENABLE_LO (0x6B) */ +#define NAU8824_TEST_DAC_SFT 14 +#define NAU8824_TEST_DAC_EN (0x3 << NAU8824_TEST_DAC_SFT) +#define NAU8824_DACL_HPR_EN_SFT 3 +#define NAU8824_DACL_HPR_EN (0x1 << NAU8824_DACL_HPR_EN_SFT) +#define NAU8824_DACR_HPR_EN_SFT 2 +#define NAU8824_DACR_HPR_EN (0x1 << NAU8824_DACR_HPR_EN_SFT) +#define NAU8824_DACR_HPL_EN_SFT 1 +#define NAU8824_DACR_HPL_EN (0x1 << NAU8824_DACR_HPL_EN_SFT) +#define NAU8824_DACL_HPL_EN_SFT 0 +#define NAU8824_DACL_HPL_EN 0x1 + +/* CLASSD_GAIN_1 (0x6D) */ +#define NAU8824_CLASSD_GAIN_1R_SFT 8 +#define NAU8824_CLASSD_GAIN_1R_MASK (0x1f << NAU8824_CLASSD_GAIN_1R_SFT) +#define NAU8824_CLASSD_EN_SFT 7 +#define NAU8824_CLASSD_EN (0x1 << NAU8824_CLASSD_EN_SFT) +#define NAU8824_CLASSD_GAIN_1L_MASK 0x1f + +/* CLASSD_GAIN_2 (0x6E) */ +#define NAU8824_CLASSD_GAIN_2R_SFT 8 +#define NAU8824_CLASSD_GAIN_2R_MASK (0x1f << NAU8824_CLASSD_GAIN_1R_SFT) +#define NAU8824_CLASSD_EN_SFT 7 +#define NAU8824_CLASSD_EN (0x1 << NAU8824_CLASSD_EN_SFT) +#define NAU8824_CLASSD_GAIN_2L_MASK 0x1f + +/* ANALOG_ADC_2 (0x72) */ +#define NAU8824_ADCR_EN_SFT 7 +#define NAU8824_ADCL_EN_SFT 6 + +/* RDAC (0x73) */ +#define NAU8824_DACR_EN_SFT 13 +#define NAU8824_DACL_EN_SFT 12 +#define NAU8824_DACR_CLK_SFT 9 +#define NAU8824_DACL_CLK_SFT 8 +#define NAU8824_RDAC_CLK_DELAY_SFT 4 +#define NAU8824_RDAC_CLK_DELAY_MASK (0x7 << NAU8824_RDAC_CLK_DELAY_SFT) +#define NAU8824_RDAC_VREF_SFT 2 +#define NAU8824_RDAC_VREF_MASK (0x3 << NAU8824_RDAC_VREF_SFT) + +/* MIC_BIAS (0x74) */ +#define NAU8824_MICBIAS_JKSLV (1 << 14) +#define NAU8824_MICBIAS_JKR2 (1 << 12) +#define NAU8824_MICBIAS_POWERUP_SFT 8 +#define NAU8824_MICBIAS_VOLTAGE_SFT 0 +#define NAU8824_MICBIAS_VOLTAGE_MASK 0x7 + +/* BOOST (0x76) */ +#define NAU8824_PRECHARGE_DIS (0x1 << 13) +#define NAU8824_GLOBAL_BIAS_EN (0x1 << 12) +#define NAU8824_HP_BOOST_DIS_SFT 9 +#define NAU8824_HP_BOOST_DIS (0x1 << NAU8824_HP_BOOST_DIS_SFT) +#define NAU8824_HP_BOOST_G_DIS_SFT 8 +#define NAU8824_HP_BOOST_G_DIS (0x1 << NAU8824_HP_BOOST_G_DIS_SFT) +#define NAU8824_SHORT_SHUTDOWN_DIG_EN (1 << 7) +#define NAU8824_SHORT_SHUTDOWN_EN (1 << 6) + +/* FEPGA (0x77) */ +#define NAU8824_FEPGA_MODER_SHORT_SFT 7 +#define NAU8824_FEPGA_MODER_SHORT_EN (0x1 << NAU8824_FEPGA_MODER_SHORT_SFT) +#define NAU8824_FEPGA_MODER_MIC2_SFT 5 +#define NAU8824_FEPGA_MODER_MIC2_EN (0x1 << NAU8824_FEPGA_MODER_MIC2_SFT) +#define NAU8824_FEPGA_MODER_HSMIC_SFT 4 +#define NAU8824_FEPGA_MODER_HSMIC_EN (0x1 << NAU8824_FEPGA_MODER_HSMIC_SFT) +#define NAU8824_FEPGA_MODEL_SHORT_SFT 3 +#define NAU8824_FEPGA_MODEL_SHORT_EN (0x1 << NAU8824_FEPGA_MODEL_SHORT_SFT) +#define NAU8824_FEPGA_MODEL_MIC1_SFT 1 +#define NAU8824_FEPGA_MODEL_MIC1_EN (0x1 << NAU8824_FEPGA_MODEL_MIC1_SFT) +#define NAU8824_FEPGA_MODEL_HSMIC_SFT 0 +#define NAU8824_FEPGA_MODEL_HSMIC_EN (0x1 << NAU8824_FEPGA_MODEL_HSMIC_SFT) + +/* FEPGA_II (0x78) */ +#define NAU8824_FEPGA_GAINR_SFT 5 +#define NAU8824_FEPGA_GAINR_MASK (0x1f << NAU8824_FEPGA_GAINR_SFT) +#define NAU8824_FEPGA_GAINL_SFT 0 +#define NAU8824_FEPGA_GAINL_MASK 0x1f + +/* CHARGE_PUMP_CONTROL (0x80) */ +#define NAU8824_JAMNODCLOW (0x1 << 15) +#define NAU8824_SPKR_PULL_DOWN (0x1 << 13) +#define NAU8824_SPKL_PULL_DOWN (0x1 << 12) +#define NAU8824_POWER_DOWN_DACR (0x1 << 9) +#define NAU8824_POWER_DOWN_DACL (0x1 << 8) +#define NAU8824_CHARGE_PUMP_EN_SFT 5 +#define NAU8824_CHARGE_PUMP_EN (0x1 << NAU8824_CHARGE_PUMP_EN_SFT) + + +#define NAU8824_CODEC_DAI "nau8824-hifi" + +/* System Clock Source */ +enum { + NAU8824_CLK_DIS, + NAU8824_CLK_MCLK, + NAU8824_CLK_INTERNAL, + NAU8824_CLK_FLL_MCLK, + NAU8824_CLK_FLL_BLK, + NAU8824_CLK_FLL_FS, +}; + +struct nau8824 { + struct device *dev; + struct regmap *regmap; + struct snd_soc_dapm_context *dapm; + struct snd_soc_jack *jack; + struct work_struct jdet_work; + struct semaphore jd_sem; + int fs; + int irq; + int resume_lock; + int micbias_voltage; + int vref_impedance; + int jkdet_polarity; + int sar_threshold_num; + int sar_threshold[8]; + int sar_hysteresis; + int sar_voltage; + int sar_compare_time; + int sar_sampling_time; + int key_debounce; + int jack_eject_debounce; +}; + +struct nau8824_fll { + int mclk_src; + int ratio; + int fll_frac; + int fll_int; + int clk_ref_div; +}; + +struct nau8824_fll_attr { + unsigned int param; + unsigned int val; +}; + +struct nau8824_osr_attr { + unsigned int osr; + unsigned int clk_src; +}; + + +int nau8824_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack); +const char *nau8824_components(void); + +#endif /* _NAU8824_H */ + diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c new file mode 100644 index 0000000000..5cb0de648b --- /dev/null +++ b/sound/soc/codecs/nau8825.c @@ -0,0 +1,2976 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nuvoton NAU8825 audio codec driver + * + * Copyright 2015 Google Chromium project. + * Author: Anatol Pomozov + * Copyright 2015 Nuvoton Technology Corp. + * Co-author: Meng-Huang Kuo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#include "nau8825.h" + + +#define NUVOTON_CODEC_DAI "nau8825-hifi" + +#define NAU_FREF_MAX 13500000 +#define NAU_FVCO_MAX 124000000 +#define NAU_FVCO_MIN 90000000 + +/* cross talk suppression detection */ +#define GAIN_AUGMENT 22500 +#define SIDETONE_BASE 207000 + +/* the maximum frequency of CLK_ADC and CLK_DAC */ +#define CLK_DA_AD_MAX 6144000 + +static int nau8825_configure_sysclk(struct nau8825 *nau8825, + int clk_id, unsigned int freq); +static bool nau8825_is_jack_inserted(struct regmap *regmap); + +struct nau8825_fll { + int mclk_src; + int ratio; + int fll_frac; + int fll_frac_num; + int fll_int; + int clk_ref_div; +}; + +struct nau8825_fll_attr { + unsigned int param; + unsigned int val; +}; + +/* scaling for mclk from sysclk_src output */ +static const struct nau8825_fll_attr mclk_src_scaling[] = { + { 1, 0x0 }, + { 2, 0x2 }, + { 4, 0x3 }, + { 8, 0x4 }, + { 16, 0x5 }, + { 32, 0x6 }, + { 3, 0x7 }, + { 6, 0xa }, + { 12, 0xb }, + { 24, 0xc }, + { 48, 0xd }, + { 96, 0xe }, + { 5, 0xf }, +}; + +/* ratio for input clk freq */ +static const struct nau8825_fll_attr fll_ratio[] = { + { 512000, 0x01 }, + { 256000, 0x02 }, + { 128000, 0x04 }, + { 64000, 0x08 }, + { 32000, 0x10 }, + { 8000, 0x20 }, + { 4000, 0x40 }, +}; + +static const struct nau8825_fll_attr fll_pre_scalar[] = { + { 1, 0x0 }, + { 2, 0x1 }, + { 4, 0x2 }, + { 8, 0x3 }, +}; + +/* over sampling rate */ +struct nau8825_osr_attr { + unsigned int osr; + unsigned int clk_src; +}; + +static const struct nau8825_osr_attr osr_dac_sel[] = { + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 256, 0 }, /* OSR 256, SRC 1 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 0, 0 }, + { 32, 3 }, /* OSR 32, SRC 1/8 */ +}; + +static const struct nau8825_osr_attr osr_adc_sel[] = { + { 32, 3 }, /* OSR 32, SRC 1/8 */ + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 256, 0 }, /* OSR 256, SRC 1 */ +}; + +static const struct reg_default nau8825_reg_defaults[] = { + { NAU8825_REG_ENA_CTRL, 0x00ff }, + { NAU8825_REG_IIC_ADDR_SET, 0x0 }, + { NAU8825_REG_CLK_DIVIDER, 0x0050 }, + { NAU8825_REG_FLL1, 0x0 }, + { NAU8825_REG_FLL2, 0x3126 }, + { NAU8825_REG_FLL3, 0x0008 }, + { NAU8825_REG_FLL4, 0x0010 }, + { NAU8825_REG_FLL5, 0x0 }, + { NAU8825_REG_FLL6, 0x6000 }, + { NAU8825_REG_FLL_VCO_RSV, 0xf13c }, + { NAU8825_REG_HSD_CTRL, 0x000c }, + { NAU8825_REG_JACK_DET_CTRL, 0x0 }, + { NAU8825_REG_INTERRUPT_MASK, 0x0 }, + { NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff }, + { NAU8825_REG_SAR_CTRL, 0x0015 }, + { NAU8825_REG_KEYDET_CTRL, 0x0110 }, + { NAU8825_REG_VDET_THRESHOLD_1, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_2, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_3, 0x0 }, + { NAU8825_REG_VDET_THRESHOLD_4, 0x0 }, + { NAU8825_REG_GPIO34_CTRL, 0x0 }, + { NAU8825_REG_GPIO12_CTRL, 0x0 }, + { NAU8825_REG_TDM_CTRL, 0x0 }, + { NAU8825_REG_I2S_PCM_CTRL1, 0x000b }, + { NAU8825_REG_I2S_PCM_CTRL2, 0x8010 }, + { NAU8825_REG_LEFT_TIME_SLOT, 0x0 }, + { NAU8825_REG_RIGHT_TIME_SLOT, 0x0 }, + { NAU8825_REG_BIQ_CTRL, 0x0 }, + { NAU8825_REG_BIQ_COF1, 0x0 }, + { NAU8825_REG_BIQ_COF2, 0x0 }, + { NAU8825_REG_BIQ_COF3, 0x0 }, + { NAU8825_REG_BIQ_COF4, 0x0 }, + { NAU8825_REG_BIQ_COF5, 0x0 }, + { NAU8825_REG_BIQ_COF6, 0x0 }, + { NAU8825_REG_BIQ_COF7, 0x0 }, + { NAU8825_REG_BIQ_COF8, 0x0 }, + { NAU8825_REG_BIQ_COF9, 0x0 }, + { NAU8825_REG_BIQ_COF10, 0x0 }, + { NAU8825_REG_ADC_RATE, 0x0010 }, + { NAU8825_REG_DAC_CTRL1, 0x0001 }, + { NAU8825_REG_DAC_CTRL2, 0x0 }, + { NAU8825_REG_DAC_DGAIN_CTRL, 0x0 }, + { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf }, + { NAU8825_REG_MUTE_CTRL, 0x0 }, + { NAU8825_REG_HSVOL_CTRL, 0x0 }, + { NAU8825_REG_DACL_CTRL, 0x02cf }, + { NAU8825_REG_DACR_CTRL, 0x00cf }, + { NAU8825_REG_ADC_DRC_KNEE_IP12, 0x1486 }, + { NAU8825_REG_ADC_DRC_KNEE_IP34, 0x0f12 }, + { NAU8825_REG_ADC_DRC_SLOPES, 0x25ff }, + { NAU8825_REG_ADC_DRC_ATKDCY, 0x3457 }, + { NAU8825_REG_DAC_DRC_KNEE_IP12, 0x1486 }, + { NAU8825_REG_DAC_DRC_KNEE_IP34, 0x0f12 }, + { NAU8825_REG_DAC_DRC_SLOPES, 0x25f9 }, + { NAU8825_REG_DAC_DRC_ATKDCY, 0x3457 }, + { NAU8825_REG_IMM_MODE_CTRL, 0x0 }, + { NAU8825_REG_CLASSG_CTRL, 0x0 }, + { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 }, + { NAU8825_REG_MISC_CTRL, 0x0 }, + { NAU8825_REG_FLL2_LOWER, 0x0 }, + { NAU8825_REG_FLL2_UPPER, 0x0 }, + { NAU8825_REG_BIAS_ADJ, 0x0 }, + { NAU8825_REG_TRIM_SETTINGS, 0x0 }, + { NAU8825_REG_ANALOG_CONTROL_1, 0x0 }, + { NAU8825_REG_ANALOG_CONTROL_2, 0x0 }, + { NAU8825_REG_ANALOG_ADC_1, 0x0011 }, + { NAU8825_REG_ANALOG_ADC_2, 0x0020 }, + { NAU8825_REG_RDAC, 0x0008 }, + { NAU8825_REG_MIC_BIAS, 0x0006 }, + { NAU8825_REG_BOOST, 0x0 }, + { NAU8825_REG_FEPGA, 0x0 }, + { NAU8825_REG_POWER_UP_CONTROL, 0x0 }, + { NAU8825_REG_CHARGE_PUMP, 0x0 }, +}; + +/* register backup table when cross talk detection */ +static struct reg_default nau8825_xtalk_baktab[] = { + { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf }, + { NAU8825_REG_HSVOL_CTRL, 0 }, + { NAU8825_REG_DACL_CTRL, 0x00cf }, + { NAU8825_REG_DACR_CTRL, 0x02cf }, +}; + +/* The regmap patch for Rev C */ +static const struct reg_sequence nau8825_regmap_patch[] = { + { NAU8825_REG_FLL2, 0x0000 }, + { NAU8825_REG_FLL4, 0x8010 }, + { NAU8825_REG_FLL_VCO_RSV, 0x0bc0 }, + { NAU8825_REG_INTERRUPT_MASK, 0x0800 }, + { NAU8825_REG_DACL_CTRL, 0x00cf }, + { NAU8825_REG_DACR_CTRL, 0x02cf }, + { NAU8825_REG_OPT_EFUSE_CTRL, 0x0400 }, + { NAU8825_REG_FLL2_LOWER, 0x26e9 }, + { NAU8825_REG_FLL2_UPPER, 0x0031 }, + { NAU8825_REG_ANALOG_CONTROL_2, 0x0020 }, + { NAU8825_REG_ANALOG_ADC_2, 0x0220 }, + { NAU8825_REG_MIC_BIAS, 0x0046 }, +}; + +/** + * nau8825_sema_acquire - acquire the semaphore of nau88l25 + * @nau8825: component to register the codec private data with + * @timeout: how long in jiffies to wait before failure or zero to wait + * until release + * + * Attempts to acquire the semaphore with number of jiffies. If no more + * tasks are allowed to acquire the semaphore, calling this function will + * put the task to sleep. If the semaphore is not released within the + * specified number of jiffies, this function returns. + * If the semaphore is not released within the specified number of jiffies, + * this function returns -ETIME. If the sleep is interrupted by a signal, + * this function will return -EINTR. It returns 0 if the semaphore was + * acquired successfully. + * + * Acquires the semaphore without jiffies. Try to acquire the semaphore + * atomically. Returns 0 if the semaphore has been acquired successfully + * or 1 if it cannot be acquired. + */ +static int nau8825_sema_acquire(struct nau8825 *nau8825, long timeout) +{ + int ret; + + if (timeout) { + ret = down_timeout(&nau8825->xtalk_sem, timeout); + if (ret < 0) + dev_warn(nau8825->dev, "Acquire semaphore timeout\n"); + } else { + ret = down_trylock(&nau8825->xtalk_sem); + if (ret) + dev_warn(nau8825->dev, "Acquire semaphore fail\n"); + } + + return ret; +} + +/** + * nau8825_sema_release - release the semaphore of nau88l25 + * @nau8825: component to register the codec private data with + * + * Release the semaphore which may be called from any context and + * even by tasks which have never called down(). + */ +static inline void nau8825_sema_release(struct nau8825 *nau8825) +{ + up(&nau8825->xtalk_sem); +} + +/** + * nau8825_sema_reset - reset the semaphore for nau88l25 + * @nau8825: component to register the codec private data with + * + * Reset the counter of the semaphore. Call this function to restart + * a new round task management. + */ +static inline void nau8825_sema_reset(struct nau8825 *nau8825) +{ + nau8825->xtalk_sem.count = 1; +} + +/** + * nau8825_hpvol_ramp - Ramp up the headphone volume change gradually to target level. + * + * @nau8825: component to register the codec private data with + * @vol_from: the volume to start up + * @vol_to: the target volume + * @step: the volume span to move on + * + * The headphone volume is from 0dB to minimum -54dB and -1dB per step. + * If the volume changes sharp, there is a pop noise heard in headphone. We + * provide the function to ramp up the volume up or down by delaying 10ms + * per step. + */ +static void nau8825_hpvol_ramp(struct nau8825 *nau8825, + unsigned int vol_from, unsigned int vol_to, unsigned int step) +{ + unsigned int value, volume, ramp_up, from, to; + + if (vol_from == vol_to || step == 0) { + return; + } else if (vol_from < vol_to) { + ramp_up = true; + from = vol_from; + to = vol_to; + } else { + ramp_up = false; + from = vol_to; + to = vol_from; + } + /* only handle volume from 0dB to minimum -54dB */ + if (to > NAU8825_HP_VOL_MIN) + to = NAU8825_HP_VOL_MIN; + + for (volume = from; volume < to; volume += step) { + if (ramp_up) + value = volume; + else + value = to - volume + from; + regmap_update_bits(nau8825->regmap, NAU8825_REG_HSVOL_CTRL, + NAU8825_HPL_VOL_MASK | NAU8825_HPR_VOL_MASK, + (value << NAU8825_HPL_VOL_SFT) | value); + usleep_range(10000, 10500); + } + if (ramp_up) + value = to; + else + value = from; + regmap_update_bits(nau8825->regmap, NAU8825_REG_HSVOL_CTRL, + NAU8825_HPL_VOL_MASK | NAU8825_HPR_VOL_MASK, + (value << NAU8825_HPL_VOL_SFT) | value); +} + +/** + * nau8825_intlog10_dec3 - Computes log10 of a value, rounding the result to 3 decimal places. + * @value: input for log10 + * + * return log10(value) * 1000 + */ +static u32 nau8825_intlog10_dec3(u32 value) +{ + return intlog10(value) / ((1 << 24) / 1000); +} + +/** + * nau8825_xtalk_sidetone - computes cross talk suppression sidetone gain. + * + * @sig_org: orignal signal level + * @sig_cros: cross talk signal level + * + * The orignal and cross talk signal vlues need to be characterized. + * Once these values have been characterized, this sidetone value + * can be converted to decibel with the equation below. + * sidetone = 20 * log (original signal level / crosstalk signal level) + * + * return cross talk sidetone gain + */ +static u32 nau8825_xtalk_sidetone(u32 sig_org, u32 sig_cros) +{ + u32 gain, sidetone; + + if (WARN_ON(sig_org == 0 || sig_cros == 0)) + return 0; + + sig_org = nau8825_intlog10_dec3(sig_org); + sig_cros = nau8825_intlog10_dec3(sig_cros); + if (sig_org >= sig_cros) + gain = (sig_org - sig_cros) * 20 + GAIN_AUGMENT; + else + gain = (sig_cros - sig_org) * 20 + GAIN_AUGMENT; + sidetone = SIDETONE_BASE - gain * 2; + sidetone /= 1000; + + return sidetone; +} + +static int nau8825_xtalk_baktab_index_by_reg(unsigned int reg) +{ + int index; + + for (index = 0; index < ARRAY_SIZE(nau8825_xtalk_baktab); index++) + if (nau8825_xtalk_baktab[index].reg == reg) + return index; + return -EINVAL; +} + +static void nau8825_xtalk_backup(struct nau8825 *nau8825) +{ + int i; + + if (nau8825->xtalk_baktab_initialized) + return; + + /* Backup some register values to backup table */ + for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++) + regmap_read(nau8825->regmap, nau8825_xtalk_baktab[i].reg, + &nau8825_xtalk_baktab[i].def); + + nau8825->xtalk_baktab_initialized = true; +} + +static void nau8825_xtalk_restore(struct nau8825 *nau8825, bool cause_cancel) +{ + int i, volume; + + if (!nau8825->xtalk_baktab_initialized) + return; + + /* Restore register values from backup table; When the driver restores + * the headphone volume in XTALK_DONE state, it needs recover to + * original level gradually with 3dB per step for less pop noise. + * Otherwise, the restore should do ASAP. + */ + for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++) { + if (!cause_cancel && nau8825_xtalk_baktab[i].reg == + NAU8825_REG_HSVOL_CTRL) { + /* Ramping up the volume change to reduce pop noise */ + volume = nau8825_xtalk_baktab[i].def & + NAU8825_HPR_VOL_MASK; + nau8825_hpvol_ramp(nau8825, 0, volume, 3); + continue; + } + regmap_write(nau8825->regmap, nau8825_xtalk_baktab[i].reg, + nau8825_xtalk_baktab[i].def); + } + + nau8825->xtalk_baktab_initialized = false; +} + +static void nau8825_xtalk_prepare_dac(struct nau8825 *nau8825) +{ + /* Enable power of DAC path */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACR | NAU8825_ENABLE_DACL | + NAU8825_ENABLE_ADC | NAU8825_ENABLE_ADC_CLK | + NAU8825_ENABLE_DAC_CLK, NAU8825_ENABLE_DACR | + NAU8825_ENABLE_DACL | NAU8825_ENABLE_ADC | + NAU8825_ENABLE_ADC_CLK | NAU8825_ENABLE_DAC_CLK); + /* Prevent startup click by letting charge pump to ramp up and + * change bump enable + */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN, + NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN); + /* Enable clock sync of DAC and DAC clock */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_RDAC, + NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN | + NAU8825_RDAC_FS_BCLK_ENB, + NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN); + /* Power up output driver with 2 stage */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL, + NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L | + NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L, + NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L | + NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L); + regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL, + NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L, + NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L); + /* HP outputs not shouted to ground */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL, + NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, 0); + /* Enable HP boost driver */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_HP_BOOST_DIS, NAU8825_HP_BOOST_DIS); + /* Enable class G compare path to supply 1.8V or 0.9V. */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLASSG_CTRL, + NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN, + NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN); +} + +static void nau8825_xtalk_prepare_adc(struct nau8825 *nau8825) +{ + /* Power up left ADC and raise 5dB than Vmid for Vref */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_ANALOG_ADC_2, + NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK, + NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB); +} + +static void nau8825_xtalk_clock(struct nau8825 *nau8825) +{ + /* Recover FLL default value */ + regmap_write(nau8825->regmap, NAU8825_REG_FLL1, 0x0); + regmap_write(nau8825->regmap, NAU8825_REG_FLL2, 0x3126); + regmap_write(nau8825->regmap, NAU8825_REG_FLL3, 0x0008); + regmap_write(nau8825->regmap, NAU8825_REG_FLL4, 0x0010); + regmap_write(nau8825->regmap, NAU8825_REG_FLL5, 0x0); + regmap_write(nau8825->regmap, NAU8825_REG_FLL6, 0x6000); + /* Enable internal VCO clock for detection signal generated */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, + NAU8825_DCO_EN); + /* Given specific clock frequency of internal clock to + * generate signal. + */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_MCLK_SRC_MASK, 0xf); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, + NAU8825_FLL_RATIO_MASK, 0x10); +} + +static void nau8825_xtalk_prepare(struct nau8825 *nau8825) +{ + int volume, index; + + /* Backup those registers changed by cross talk detection */ + nau8825_xtalk_backup(nau8825); + /* Config IIS as master to output signal by codec */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK | NAU8825_I2S_LRC_DIV_MASK | + NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_MASTER | + (0x2 << NAU8825_I2S_LRC_DIV_SFT) | 0x1); + /* Ramp up headphone volume to 0dB to get better performance and + * avoid pop noise in headphone. + */ + index = nau8825_xtalk_baktab_index_by_reg(NAU8825_REG_HSVOL_CTRL); + if (index != -EINVAL) { + volume = nau8825_xtalk_baktab[index].def & + NAU8825_HPR_VOL_MASK; + nau8825_hpvol_ramp(nau8825, volume, 0, 3); + } + nau8825_xtalk_clock(nau8825); + nau8825_xtalk_prepare_dac(nau8825); + nau8825_xtalk_prepare_adc(nau8825); + /* Config channel path and digital gain */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_DACL_CTRL, + NAU8825_DACL_CH_SEL_MASK | NAU8825_DACL_CH_VOL_MASK, + NAU8825_DACL_CH_SEL_L | 0xab); + regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL, + NAU8825_DACR_CH_SEL_MASK | NAU8825_DACR_CH_VOL_MASK, + NAU8825_DACR_CH_SEL_R | 0xab); + /* Config cross talk parameters and generate the 23Hz sine wave with + * 1/16 full scale of signal level for impedance measurement. + */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL, + NAU8825_IMM_THD_MASK | NAU8825_IMM_GEN_VOL_MASK | + NAU8825_IMM_CYC_MASK | NAU8825_IMM_DAC_SRC_MASK, + (0x9 << NAU8825_IMM_THD_SFT) | NAU8825_IMM_GEN_VOL_1_16th | + NAU8825_IMM_CYC_8192 | NAU8825_IMM_DAC_SRC_SIN); + /* RMS intrruption enable */ + regmap_update_bits(nau8825->regmap, + NAU8825_REG_INTERRUPT_MASK, NAU8825_IRQ_RMS_EN, 0); + /* Power up left and right DAC */ + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); +} + +static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825) +{ + /* Disable HP boost driver */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_HP_BOOST_DIS, 0); + /* HP outputs shouted to ground */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL, + NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, + NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); + /* Power down left and right DAC */ + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + + /* Enable the TESTDAC and disable L/R HP impedance */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP | + NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); + /* Power down output driver with 2 stage */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL, + NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L, 0); + regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL, + NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L | + NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L, 0); + /* Disable clock sync of DAC and DAC clock */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_RDAC, + NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN, 0); + /* Disable charge pump ramp up function and change bump */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN, 0); + /* Disable power of DAC path */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACR | NAU8825_ENABLE_DACL | + NAU8825_ENABLE_ADC_CLK | NAU8825_ENABLE_DAC_CLK, 0); + if (!nau8825->irq) + regmap_update_bits(nau8825->regmap, + NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, 0); +} + +static void nau8825_xtalk_clean_adc(struct nau8825 *nau8825) +{ + /* Power down left ADC and restore voltage to Vmid */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_ANALOG_ADC_2, + NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK, 0); +} + +static void nau8825_xtalk_clean(struct nau8825 *nau8825, bool cause_cancel) +{ + /* Enable internal VCO needed for interruptions */ + nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0); + nau8825_xtalk_clean_dac(nau8825); + nau8825_xtalk_clean_adc(nau8825); + /* Clear cross talk parameters and disable */ + regmap_write(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL, 0); + /* RMS intrruption disable */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_RMS_EN, NAU8825_IRQ_RMS_EN); + /* Recover default value for IIS */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK | NAU8825_I2S_LRC_DIV_MASK | + NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_SLAVE); + /* Restore value of specific register for cross talk */ + nau8825_xtalk_restore(nau8825, cause_cancel); +} + +static void nau8825_xtalk_imm_start(struct nau8825 *nau8825, int vol) +{ + /* Apply ADC volume for better cross talk performance */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_ADC_DGAIN_CTRL, + NAU8825_ADC_DIG_VOL_MASK, vol); + /* Disables JKTIP(HPL) DAC channel for right to left measurement. + * Do it before sending signal in order to erase pop noise. + */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_TESTDACR_EN | NAU8825_BIAS_TESTDACL_EN, + NAU8825_BIAS_TESTDACL_EN); + switch (nau8825->xtalk_state) { + case NAU8825_XTALK_HPR_R2L: + /* Enable right headphone impedance */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP, + NAU8825_BIAS_HPR_IMP); + break; + case NAU8825_XTALK_HPL_R2L: + /* Enable left headphone impedance */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP, + NAU8825_BIAS_HPL_IMP); + break; + default: + break; + } + msleep(100); + /* Impedance measurement mode enable */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL, + NAU8825_IMM_EN, NAU8825_IMM_EN); +} + +static void nau8825_xtalk_imm_stop(struct nau8825 *nau8825) +{ + /* Impedance measurement mode disable */ + regmap_update_bits(nau8825->regmap, + NAU8825_REG_IMM_MODE_CTRL, NAU8825_IMM_EN, 0); +} + +/* The cross talk measurement function can reduce cross talk across the + * JKTIP(HPL) and JKR1(HPR) outputs which measures the cross talk signal + * level to determine what cross talk reduction gain is. This system works by + * sending a 23Hz -24dBV sine wave into the headset output DAC and through + * the PGA. The output of the PGA is then connected to an internal current + * sense which measures the attenuated 23Hz signal and passing the output to + * an ADC which converts the measurement to a binary code. With two separated + * measurement, one for JKR1(HPR) and the other JKTIP(HPL), measurement data + * can be separated read in IMM_RMS_L for HSR and HSL after each measurement. + * Thus, the measurement function has four states to complete whole sequence. + * 1. Prepare state : Prepare the resource for detection and transfer to HPR + * IMM stat to make JKR1(HPR) impedance measure. + * 2. HPR IMM state : Read out orignal signal level of JKR1(HPR) and transfer + * to HPL IMM state to make JKTIP(HPL) impedance measure. + * 3. HPL IMM state : Read out cross talk signal level of JKTIP(HPL) and + * transfer to IMM state to determine suppression sidetone gain. + * 4. IMM state : Computes cross talk suppression sidetone gain with orignal + * and cross talk signal level. Apply this gain and then restore codec + * configuration. Then transfer to Done state for ending. + */ +static void nau8825_xtalk_measure(struct nau8825 *nau8825) +{ + u32 sidetone; + + switch (nau8825->xtalk_state) { + case NAU8825_XTALK_PREPARE: + /* In prepare state, set up clock, intrruption, DAC path, ADC + * path and cross talk detection parameters for preparation. + */ + nau8825_xtalk_prepare(nau8825); + msleep(280); + /* Trigger right headphone impedance detection */ + nau8825->xtalk_state = NAU8825_XTALK_HPR_R2L; + nau8825_xtalk_imm_start(nau8825, 0x00d2); + break; + case NAU8825_XTALK_HPR_R2L: + /* In right headphone IMM state, read out right headphone + * impedance measure result, and then start up left side. + */ + regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L, + &nau8825->imp_rms[NAU8825_XTALK_HPR_R2L]); + dev_dbg(nau8825->dev, "HPR_R2L imm: %x\n", + nau8825->imp_rms[NAU8825_XTALK_HPR_R2L]); + /* Disable then re-enable IMM mode to update */ + nau8825_xtalk_imm_stop(nau8825); + /* Trigger left headphone impedance detection */ + nau8825->xtalk_state = NAU8825_XTALK_HPL_R2L; + nau8825_xtalk_imm_start(nau8825, 0x00ff); + break; + case NAU8825_XTALK_HPL_R2L: + /* In left headphone IMM state, read out left headphone + * impedance measure result, and delay some time to wait + * detection sine wave output finish. Then, we can calculate + * the cross talk suppresstion side tone according to the L/R + * headphone imedance. + */ + regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L, + &nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]); + dev_dbg(nau8825->dev, "HPL_R2L imm: %x\n", + nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]); + nau8825_xtalk_imm_stop(nau8825); + msleep(150); + nau8825->xtalk_state = NAU8825_XTALK_IMM; + break; + case NAU8825_XTALK_IMM: + /* In impedance measure state, the orignal and cross talk + * signal level vlues are ready. The side tone gain is deter- + * mined with these signal level. After all, restore codec + * configuration. + */ + sidetone = nau8825_xtalk_sidetone( + nau8825->imp_rms[NAU8825_XTALK_HPR_R2L], + nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]); + dev_dbg(nau8825->dev, "cross talk sidetone: %x\n", sidetone); + regmap_write(nau8825->regmap, NAU8825_REG_DAC_DGAIN_CTRL, + (sidetone << 8) | sidetone); + nau8825_xtalk_clean(nau8825, false); + nau8825->xtalk_state = NAU8825_XTALK_DONE; + break; + default: + break; + } +} + +static void nau8825_xtalk_work(struct work_struct *work) +{ + struct nau8825 *nau8825 = container_of( + work, struct nau8825, xtalk_work); + + nau8825_xtalk_measure(nau8825); + /* To determine the cross talk side tone gain when reach + * the impedance measure state. + */ + if (nau8825->xtalk_state == NAU8825_XTALK_IMM) + nau8825_xtalk_measure(nau8825); + + /* Delay jack report until cross talk detection process + * completed. It can avoid application to do playback + * preparation before cross talk detection is still working. + * Meanwhile, the protection of the cross talk detection + * is released. + */ + if (nau8825->xtalk_state == NAU8825_XTALK_DONE) { + snd_soc_jack_report(nau8825->jack, nau8825->xtalk_event, + nau8825->xtalk_event_mask); + nau8825_sema_release(nau8825); + nau8825->xtalk_protect = false; + } +} + +static void nau8825_xtalk_cancel(struct nau8825 *nau8825) +{ + /* If the crosstalk is eanbled and the process is on going, + * the driver forces to cancel the crosstalk task and + * restores the configuration to original status. + */ + if (nau8825->xtalk_enable && nau8825->xtalk_state != + NAU8825_XTALK_DONE) { + cancel_work_sync(&nau8825->xtalk_work); + nau8825_xtalk_clean(nau8825, true); + } + /* Reset parameters for cross talk suppression function */ + nau8825_sema_reset(nau8825); + nau8825->xtalk_state = NAU8825_XTALK_DONE; + nau8825->xtalk_protect = false; +} + +static bool nau8825_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_ENA_CTRL ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: + case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL: + case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: + case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY: + case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY: + case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R: + case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: + case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_FLL2_UPPER: + case NAU8825_REG_BIAS_ADJ: + case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: + case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: + case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA: + case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_GENERAL_STATUS: + return true; + default: + return false; + } + +} + +static bool nau8825_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_RESET ... NAU8825_REG_FLL_VCO_RSV: + case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: + case NAU8825_REG_INTERRUPT_MASK: + case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL: + case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: + case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY: + case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY: + case NAU8825_REG_IMM_MODE_CTRL: + case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: + case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_FLL2_LOWER ... NAU8825_REG_FLL2_UPPER: + case NAU8825_REG_BIAS_ADJ: + case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: + case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: + case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA: + case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_CHARGE_PUMP: + return true; + default: + return false; + } +} + +static bool nau8825_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8825_REG_RESET: + case NAU8825_REG_IRQ_STATUS: + case NAU8825_REG_INT_CLR_KEY_STATUS: + case NAU8825_REG_IMM_RMS_L: + case NAU8825_REG_IMM_RMS_R: + case NAU8825_REG_I2C_DEVICE_ID: + case NAU8825_REG_SARDOUT_RAM_STATUS: + case NAU8825_REG_CHARGE_PUMP_INPUT_READ: + case NAU8825_REG_GENERAL_STATUS: + case NAU8825_REG_BIQ_CTRL ... NAU8825_REG_BIQ_COF10: + return true; + default: + return false; + } +} + +static int nau8825_fepga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA, + NAU8825_ACDC_CTRL_MASK, + NAU8825_ACDC_VREF_MICP | NAU8825_ACDC_VREF_MICN); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_DISCHRG_EN, NAU8825_DISCHRG_EN); + msleep(40); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_DISCHRG_EN, 0); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA, + NAU8825_ACDC_CTRL_MASK, 0); + break; + default: + break; + } + + return 0; +} + +static int nau8825_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(nau8825->adc_delay); + regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC); + break; + case SND_SOC_DAPM_POST_PMD: + if (!nau8825->irq) + regmap_update_bits(nau8825->regmap, + NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8825_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Prevent startup click by letting charge pump to ramp up */ + msleep(10); + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_JAMNODCLOW, NAU8825_JAMNODCLOW); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_JAMNODCLOW, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disables the TESTDAC to let DAC signal pass through. */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_TESTDAC_EN, 0); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int system_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + struct regmap *regmap = nau8825->regmap; + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + dev_dbg(nau8825->dev, "system clock control : POWER OFF\n"); + /* Set clock source to disable or internal clock before the + * playback or capture end. Codec needs clock for Jack + * detection and button press if jack inserted; otherwise, + * the clock should be closed. + */ + if (nau8825_is_jack_inserted(regmap)) { + nau8825_configure_sysclk(nau8825, + NAU8825_CLK_INTERNAL, 0); + } else { + nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0); + } + } + + return 0; +} + +static int nau8825_biq_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + + if (!component->regmap) + return -EINVAL; + + regmap_raw_read(component->regmap, NAU8825_REG_BIQ_COF1, + ucontrol->value.bytes.data, params->max); + return 0; +} + +static int nau8825_biq_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + void *data; + + if (!component->regmap) + return -EINVAL; + + data = kmemdup(ucontrol->value.bytes.data, + params->max, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + regmap_update_bits(component->regmap, NAU8825_REG_BIQ_CTRL, + NAU8825_BIQ_WRT_EN, 0); + regmap_raw_write(component->regmap, NAU8825_REG_BIQ_COF1, + data, params->max); + regmap_update_bits(component->regmap, NAU8825_REG_BIQ_CTRL, + NAU8825_BIQ_WRT_EN, NAU8825_BIQ_WRT_EN); + + kfree(data); + return 0; +} + +static const char * const nau8825_biq_path[] = { + "ADC", "DAC" +}; + +static const struct soc_enum nau8825_biq_path_enum = + SOC_ENUM_SINGLE(NAU8825_REG_BIQ_CTRL, NAU8825_BIQ_PATH_SFT, + ARRAY_SIZE(nau8825_biq_path), nau8825_biq_path); + +static const char * const nau8825_adc_decimation[] = { + "32", "64", "128", "256" +}; + +static const struct soc_enum nau8825_adc_decimation_enum = + SOC_ENUM_SINGLE(NAU8825_REG_ADC_RATE, NAU8825_ADC_SYNC_DOWN_SFT, + ARRAY_SIZE(nau8825_adc_decimation), nau8825_adc_decimation); + +static const char * const nau8825_dac_oversampl[] = { + "64", "256", "128", "", "32" +}; + +static const struct soc_enum nau8825_dac_oversampl_enum = + SOC_ENUM_SINGLE(NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_SFT, + ARRAY_SIZE(nau8825_dac_oversampl), nau8825_dac_oversampl); + +static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -10300, 2400); +static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0); +static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -5400, 0); +static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600); +static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -9600, 2400); + +static const struct snd_kcontrol_new nau8825_controls[] = { + SOC_SINGLE_TLV("Mic Volume", NAU8825_REG_ADC_DGAIN_CTRL, + 0, 0xff, 0, adc_vol_tlv), + SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8825_REG_ADC_DGAIN_CTRL, + 12, 8, 0x0f, 0, sidetone_vol_tlv), + SOC_DOUBLE_TLV("Headphone Volume", NAU8825_REG_HSVOL_CTRL, + 6, 0, 0x3f, 1, dac_vol_tlv), + SOC_SINGLE_TLV("Frontend PGA Volume", NAU8825_REG_POWER_UP_CONTROL, + 8, 37, 0, fepga_gain_tlv), + SOC_DOUBLE_TLV("Headphone Crosstalk Volume", NAU8825_REG_DAC_DGAIN_CTRL, + 0, 8, 0xff, 0, crosstalk_vol_tlv), + + SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum), + SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum), + /* programmable biquad filter */ + SOC_ENUM("BIQ Path Select", nau8825_biq_path_enum), + SND_SOC_BYTES_EXT("BIQ Coefficients", 20, + nau8825_biq_coeff_get, nau8825_biq_coeff_put), +}; + +/* DAC Mux 0x33[9] and 0x34[9] */ +static const char * const nau8825_dac_src[] = { + "DACL", "DACR", +}; + +static SOC_ENUM_SINGLE_DECL( + nau8825_dacl_enum, NAU8825_REG_DACL_CTRL, + NAU8825_DACL_CH_SEL_SFT, nau8825_dac_src); + +static SOC_ENUM_SINGLE_DECL( + nau8825_dacr_enum, NAU8825_REG_DACR_CTRL, + NAU8825_DACR_CH_SEL_SFT, nau8825_dac_src); + +static const struct snd_kcontrol_new nau8825_dacl_mux = + SOC_DAPM_ENUM("DACL Source", nau8825_dacl_enum); + +static const struct snd_kcontrol_new nau8825_dacr_mux = + SOC_DAPM_ENUM("DACR Source", nau8825_dacr_enum); + + +static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { + SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2, + 15, 1), + SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0, + system_clock_control, SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0), + + SND_SOC_DAPM_PGA_E("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0, + NULL, 0, nau8825_fepga_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, + nau8825_adc_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL, + 0), + + /* ADC for button press detection. A dapm supply widget is used to + * prevent dapm_power_widgets keeping the codec at SND_SOC_BIAS_ON + * during suspend. + */ + SND_SOC_DAPM_SUPPLY("SAR", NAU8825_REG_SAR_CTRL, + NAU8825_SAR_ADC_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8825_REG_RDAC, 8, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8825_REG_RDAC, 9, 0, NULL, 0), + + SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACR_SFT, 0), + SND_SOC_DAPM_DAC("DDACL", NULL, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_DACL_SFT, 0), + SND_SOC_DAPM_SUPPLY("DDAC Clock", NAU8825_REG_ENA_CTRL, 6, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux), + SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux), + + SND_SOC_DAPM_PGA_S("HP amp L", 0, + NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("HP amp R", 0, + NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0), + + SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8825_REG_CHARGE_PUMP, 5, 0, + nau8825_pump_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4, + NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4, + NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5, + NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5, + NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6, + NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6, + NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA_S("Output DACL", 7, + SND_SOC_NOPM, 0, 0, nau8825_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("Output DACR", 7, + SND_SOC_NOPM, 0, 0, nau8825_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + + /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */ + SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8, + NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8, + NAU8825_REG_HSD_CTRL, 1, 1, NULL, 0), + + /* High current HPOL/R boost driver */ + SND_SOC_DAPM_PGA_S("HP Boost Driver", 9, + NAU8825_REG_BOOST, 9, 1, NULL, 0), + + /* Class G operation control*/ + SND_SOC_DAPM_PGA_S("Class G", 10, + NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route nau8825_dapm_routes[] = { + {"Frontend PGA", NULL, "MIC"}, + {"ADC", NULL, "Frontend PGA"}, + {"ADC", NULL, "ADC Clock"}, + {"ADC", NULL, "ADC Power"}, + {"AIFTX", NULL, "ADC"}, + {"AIFTX", NULL, "System Clock"}, + + {"AIFRX", NULL, "System Clock"}, + {"DDACL", NULL, "AIFRX"}, + {"DDACR", NULL, "AIFRX"}, + {"DDACL", NULL, "DDAC Clock"}, + {"DDACR", NULL, "DDAC Clock"}, + {"DACL Mux", "DACL", "DDACL"}, + {"DACL Mux", "DACR", "DDACR"}, + {"DACR Mux", "DACL", "DDACL"}, + {"DACR Mux", "DACR", "DDACR"}, + {"HP amp L", NULL, "DACL Mux"}, + {"HP amp R", NULL, "DACR Mux"}, + {"Charge Pump", NULL, "HP amp L"}, + {"Charge Pump", NULL, "HP amp R"}, + {"ADACL", NULL, "Charge Pump"}, + {"ADACR", NULL, "Charge Pump"}, + {"ADACL Clock", NULL, "ADACL"}, + {"ADACR Clock", NULL, "ADACR"}, + {"Output Driver L Stage 1", NULL, "ADACL Clock"}, + {"Output Driver R Stage 1", NULL, "ADACR Clock"}, + {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"}, + {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"}, + {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"}, + {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"}, + {"Output DACL", NULL, "Output Driver L Stage 3"}, + {"Output DACR", NULL, "Output Driver R Stage 3"}, + {"HPOL Pulldown", NULL, "Output DACL"}, + {"HPOR Pulldown", NULL, "Output DACR"}, + {"HP Boost Driver", NULL, "HPOL Pulldown"}, + {"HP Boost Driver", NULL, "HPOR Pulldown"}, + {"Class G", NULL, "HP Boost Driver"}, + {"HPOL", NULL, "Class G"}, + {"HPOR", NULL, "Class G"}, +}; + +static const struct nau8825_osr_attr * +nau8825_get_osr(struct nau8825 *nau8825, int stream) +{ + unsigned int osr; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_read(nau8825->regmap, + NAU8825_REG_DAC_CTRL1, &osr); + osr &= NAU8825_DAC_OVERSAMPLE_MASK; + if (osr >= ARRAY_SIZE(osr_dac_sel)) + return NULL; + return &osr_dac_sel[osr]; + } else { + regmap_read(nau8825->regmap, + NAU8825_REG_ADC_RATE, &osr); + osr &= NAU8825_ADC_SYNC_DOWN_MASK; + if (osr >= ARRAY_SIZE(osr_adc_sel)) + return NULL; + return &osr_adc_sel[osr]; + } +} + +static int nau8825_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + const struct nau8825_osr_attr *osr; + + osr = nau8825_get_osr(nau8825, substream->stream); + if (!osr || !osr->osr) + return -EINVAL; + + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + 0, CLK_DA_AD_MAX / osr->osr); +} + +static int nau8825_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, ctrl_val, bclk_fs, bclk_div; + const struct nau8825_osr_attr *osr; + int err = -EINVAL; + + nau8825_sema_acquire(nau8825, 3 * HZ); + + /* CLK_DAC or CLK_ADC = OSR * FS + * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR) + * multiplied by the audio sample rate (Fs). Note that the OSR and Fs + * values must be selected such that the maximum frequency is less + * than 6.144 MHz. + */ + osr = nau8825_get_osr(nau8825, substream->stream); + if (!osr || !osr->osr) + goto error; + if (params_rate(params) * osr->osr > CLK_DA_AD_MAX) + goto error; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_DAC_SRC_MASK, + osr->clk_src << NAU8825_CLK_DAC_SRC_SFT); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_ADC_SRC_MASK, + osr->clk_src << NAU8825_CLK_ADC_SRC_SFT); + + /* make BCLK and LRC divde configuration if the codec as master. */ + regmap_read(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, &ctrl_val); + if (ctrl_val & NAU8825_I2S_MS_MASTER) { + /* get the bclk and fs ratio */ + bclk_fs = snd_soc_params_to_bclk(params) / params_rate(params); + if (bclk_fs <= 32) + bclk_div = 2; + else if (bclk_fs <= 64) + bclk_div = 1; + else if (bclk_fs <= 128) + bclk_div = 0; + else + goto error; + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_LRC_DIV_MASK | NAU8825_I2S_BLK_DIV_MASK, + ((bclk_div + 1) << NAU8825_I2S_LRC_DIV_SFT) | bclk_div); + } + + switch (params_width(params)) { + case 16: + val_len |= NAU8825_I2S_DL_16; + break; + case 20: + val_len |= NAU8825_I2S_DL_20; + break; + case 24: + val_len |= NAU8825_I2S_DL_24; + break; + case 32: + val_len |= NAU8825_I2S_DL_32; + break; + default: + goto error; + } + + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, + NAU8825_I2S_DL_MASK, val_len); + err = 0; + + error: + /* Release the semaphore. */ + nau8825_sema_release(nau8825); + + return err; +} + +static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + unsigned int ctrl1_val = 0, ctrl2_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= NAU8825_I2S_MS_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8825_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8825_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8825_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1_val |= NAU8825_I2S_DF_RIGTH; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8825_I2S_DF_PCM_AB; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1_val |= NAU8825_I2S_DF_PCM_AB; + ctrl1_val |= NAU8825_I2S_PCMB_EN; + break; + default: + return -EINVAL; + } + + nau8825_sema_acquire(nau8825, 3 * HZ); + + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, + NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK | + NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK, + ctrl1_val); + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, ctrl2_val); + + /* Release the semaphore. */ + nau8825_sema_release(nau8825); + + return 0; +} + +/** + * nau8825_set_tdm_slot - configure DAI TDM. + * @dai: DAI + * @tx_mask: bitmask representing active TX slots. + * @rx_mask: bitmask representing active RX slots. + * @slots: Number of slots in use. + * @slot_width: Width in bits for each slot. + * + * Configures a DAI for TDM operation. Support TDM 4/8 slots. + * The limitation is DAC and ADC need shift 4 slots at 8 slots mode. + */ +static int nau8825_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + unsigned int ctrl_val = 0, ctrl_offset = 0, value = 0, dac_s, adc_s; + + if (slots != 4 && slots != 8) { + dev_err(nau8825->dev, "Only support 4 or 8 slots!\n"); + return -EINVAL; + } + + /* The driver is limited to 1-channel for ADC, and 2-channel for DAC on TDM mode */ + if (hweight_long((unsigned long) tx_mask) != 1 || + hweight_long((unsigned long) rx_mask) != 2) { + dev_err(nau8825->dev, + "The limitation is 1-channel for ADC, and 2-channel for DAC on TDM mode.\n"); + return -EINVAL; + } + + if (((tx_mask & 0xf) && (tx_mask & 0xf0)) || + ((rx_mask & 0xf) && (rx_mask & 0xf0)) || + ((tx_mask & 0xf) && (rx_mask & 0xf0)) || + ((rx_mask & 0xf) && (tx_mask & 0xf0))) { + dev_err(nau8825->dev, + "Slot assignment of DAC and ADC need to set same interval.\n"); + return -EINVAL; + } + + /* The offset of fixed 4 slots for 8 slots support */ + if (rx_mask & 0xf0) { + regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_PCM_TS_EN_MASK, NAU8825_I2S_PCM_TS_EN); + regmap_read(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1, &value); + ctrl_val |= NAU8825_TDM_OFFSET_EN; + ctrl_offset = 4 * slot_width; + if (!(value & NAU8825_I2S_PCMB_MASK)) + ctrl_offset += 1; + dac_s = (rx_mask & 0xf0) >> 4; + adc_s = fls((tx_mask & 0xf0) >> 4); + } else { + dac_s = rx_mask & 0xf; + adc_s = fls(tx_mask & 0xf); + } + + ctrl_val |= NAU8825_TDM_MODE; + + switch (dac_s) { + case 0x3: + ctrl_val |= 1 << NAU8825_TDM_DACR_RX_SFT; + break; + case 0x5: + ctrl_val |= 2 << NAU8825_TDM_DACR_RX_SFT; + break; + case 0x6: + ctrl_val |= 1 << NAU8825_TDM_DACL_RX_SFT; + ctrl_val |= 2 << NAU8825_TDM_DACR_RX_SFT; + break; + case 0x9: + ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT; + break; + case 0xa: + ctrl_val |= 1 << NAU8825_TDM_DACL_RX_SFT; + ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT; + break; + case 0xc: + ctrl_val |= 2 << NAU8825_TDM_DACL_RX_SFT; + ctrl_val |= 3 << NAU8825_TDM_DACR_RX_SFT; + break; + default: + return -EINVAL; + } + + ctrl_val |= adc_s - 1; + + regmap_update_bits(nau8825->regmap, NAU8825_REG_TDM_CTRL, + NAU8825_TDM_MODE | NAU8825_TDM_OFFSET_EN | + NAU8825_TDM_DACL_RX_MASK | NAU8825_TDM_DACR_RX_MASK | + NAU8825_TDM_TX_MASK, ctrl_val); + regmap_update_bits(nau8825->regmap, NAU8825_REG_LEFT_TIME_SLOT, + NAU8825_TSLOT_L0_MASK, ctrl_offset); + + return 0; +} + +static const struct snd_soc_dai_ops nau8825_dai_ops = { + .startup = nau8825_dai_startup, + .hw_params = nau8825_hw_params, + .set_fmt = nau8825_set_dai_fmt, + .set_tdm_slot = nau8825_set_tdm_slot, +}; + +#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000 +#define NAU8825_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver nau8825_dai = { + .name = "nau8825-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8825_RATES, + .formats = NAU8825_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, /* Only 1 channel of data */ + .rates = NAU8825_RATES, + .formats = NAU8825_FORMATS, + }, + .ops = &nau8825_dai_ops, +}; + +/** + * nau8825_enable_jack_detect - Specify a jack for event reporting + * + * @component: component to register the jack with + * @jack: jack to use to report headset and button events on + * + * After this function has been called the headset insert/remove and button + * events will be routed to the given jack. Jack can be null to stop + * reporting. + */ +int nau8825_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + struct regmap *regmap = nau8825->regmap; + + nau8825->jack = jack; + + if (!nau8825->jack) { + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | + NAU8825_SPKR_DWN1L, 0); + return 0; + } + /* Ground HP Outputs[1:0], needed for headset auto detection + * Enable Automatic Mic/Gnd switching reading on insert interrupt[6] + */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, + NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); + + return 0; +} +EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect); + + +static bool nau8825_is_jack_inserted(struct regmap *regmap) +{ + bool active_high, is_high; + int status, jkdet; + + regmap_read(regmap, NAU8825_REG_JACK_DET_CTRL, &jkdet); + active_high = jkdet & NAU8825_JACK_POLARITY; + regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status); + is_high = status & NAU8825_GPIO2JD1; + /* return jack connection status according to jack insertion logic + * active high or active low. + */ + return active_high == is_high; +} + +static void nau8825_restart_jack_detection(struct regmap *regmap) +{ + /* this will restart the entire jack detection process including MIC/GND + * switching and create interrupts. We have to go from 0 to 1 and back + * to 0 to restart. + */ + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_RESTART, NAU8825_JACK_DET_RESTART); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_RESTART, 0); +} + +static void nau8825_int_status_clear_all(struct regmap *regmap) +{ + int active_irq, clear_irq, i; + + /* Reset the intrruption status from rightmost bit if the corres- + * ponding irq event occurs. + */ + regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq); + for (i = 0; i < NAU8825_REG_DATA_LEN; i++) { + clear_irq = (0x1 << i); + if (active_irq & clear_irq) + regmap_write(regmap, + NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq); + } +} + +static void nau8825_eject_jack(struct nau8825 *nau8825) +{ + struct snd_soc_dapm_context *dapm = nau8825->dapm; + struct regmap *regmap = nau8825->regmap; + + /* Force to cancel the cross talk detection process */ + nau8825_xtalk_cancel(nau8825); + + snd_soc_dapm_disable_pin(dapm, "SAR"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0); + /* ground HPL/HPR, MICGRND1/2 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf); + + snd_soc_dapm_sync(dapm); + + /* Clear all interruption status */ + nau8825_int_status_clear_all(regmap); + + /* Enable the insertion interruption, disable the ejection inter- + * ruption, and then bypass de-bounce circuit. + */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, + NAU8825_IRQ_EJECT_DIS | NAU8825_IRQ_INSERT_DIS, + NAU8825_IRQ_EJECT_DIS); + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN | + NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_INSERT_EN, + NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN | + NAU8825_IRQ_HEADSET_COMPLETE_EN); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS); + + /* Disable ADC needed for interruptions at audo mode */ + regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_ADC, 0); + + /* Close clock for jack type detection at manual mode */ + nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0); +} + +/* Enable audo mode interruptions with internal clock. */ +static void nau8825_setup_auto_irq(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + + /* Enable HSD function */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_HSD_AUTO_MODE, NAU8825_HSD_AUTO_MODE); + + /* Enable headset jack type detection complete interruption and + * jack ejection interruption. + */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0); + + /* Enable internal VCO needed for interruptions */ + nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0); + /* Raise up the internal clock for jack detection */ + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_MCLK_SRC_MASK, 0); + + /* Enable ADC needed for interruptions */ + regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC); + + /* Chip needs one FSCLK cycle in order to generate interruptions, + * as we cannot guarantee one will be provided by the system. Turning + * master mode on then off enables us to generate that FSCLK cycle + * with a minimum of contention on the clock bus. + */ + regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER); + regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2, + NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE); + + /* Not bypass de-bounce circuit */ + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_DB_BYPASS, 0); + + /* Unmask all interruptions */ + regmap_write(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0); + + /* Restart the jack detection process at auto mode */ + nau8825_restart_jack_detection(regmap); +} + +static int nau8825_button_decode(int value) +{ + int buttons = 0; + + /* The chip supports up to 8 buttons, but ALSA defines only 6 buttons */ + if (value & BIT(0)) + buttons |= SND_JACK_BTN_0; + if (value & BIT(1)) + buttons |= SND_JACK_BTN_1; + if (value & BIT(2)) + buttons |= SND_JACK_BTN_2; + if (value & BIT(3)) + buttons |= SND_JACK_BTN_3; + if (value & BIT(4)) + buttons |= SND_JACK_BTN_4; + if (value & BIT(5)) + buttons |= SND_JACK_BTN_5; + + return buttons; +} + +static int nau8825_high_imped_detection(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + struct snd_soc_dapm_context *dapm = nau8825->dapm; + unsigned int adc_mg1, adc_mg2; + + /* Initial phase */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R | + NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2); + regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_1, + NAU8825_TESTDACIN_MASK, NAU8825_TESTDACIN_GND); + regmap_write(regmap, NAU8825_REG_TRIM_SETTINGS, 0x6); + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_LOWNOISE_MASK | NAU8825_MICBIAS_VOLTAGE_MASK, + NAU8825_MICBIAS_LOWNOISE_EN); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK | NAU8825_SAR_TRACKING_GAIN_MASK | + NAU8825_SAR_HV_SEL_MASK | NAU8825_SAR_RES_SEL_MASK | + NAU8825_SAR_COMPARE_TIME_MASK | NAU8825_SAR_SAMPLING_TIME_MASK, + NAU8825_SAR_HV_SEL_VDDMIC | NAU8825_SAR_RES_SEL_70K); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + + /* Configure settings for first reading of SARADC */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R | + NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND2); + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKR2); + regmap_read(regmap, NAU8825_REG_SARDOUT_RAM_STATUS, &adc_mg1); + + /* Configure settings for second reading of SARADC */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0); + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R | + NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | + NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R | + NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1); + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKSLV); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, NAU8825_SAR_INPUT_JKSLV); + regmap_read(regmap, NAU8825_REG_SARDOUT_RAM_STATUS, &adc_mg2); + + /* Disable phase */ + snd_soc_dapm_disable_pin(dapm, "SAR"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + snd_soc_dapm_sync(dapm); + + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_LOWNOISE_MASK | + NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage); + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | NAU8825_SPKR_DWN1R | + NAU8825_SPKR_DWN1L, NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2 | + NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); + regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_1, + NAU8825_TESTDACIN_MASK, NAU8825_TESTDACIN_GND); + regmap_write(regmap, NAU8825_REG_TRIM_SETTINGS, 0); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_TRACKING_GAIN_MASK | NAU8825_SAR_HV_SEL_MASK, + nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_COMPARE_TIME_MASK | NAU8825_SAR_SAMPLING_TIME_MASK, + (nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT) | + (nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT)); + dev_dbg(nau8825->dev, "adc_mg1:%x, adc_mg2:%x\n", adc_mg1, adc_mg2); + + /* Confirmation phase */ + if (adc_mg1 > adc_mg2) { + dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n"); + + /* Unground MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2, + NAU8825_SPKR_ENGND2); + /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKR2); + /* Attach SARADC to MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, + NAU8825_SAR_INPUT_JKR2); + } else if (adc_mg1 < adc_mg2) { + dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n"); + + /* Unground MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_SPKR_ENGND1 | NAU8825_SPKR_ENGND2, + NAU8825_SPKR_ENGND1); + /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKSLV); + /* Attach SARADC to MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, + NAU8825_SAR_INPUT_JKSLV); + } else { + dev_err(nau8825->dev, "Jack broken.\n"); + return -EINVAL; + } + + return 0; +} + +static int nau8825_jack_insert(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + struct snd_soc_dapm_context *dapm = nau8825->dapm; + int jack_status_reg, mic_detected; + int type = 0; + + regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg); + mic_detected = (jack_status_reg >> 10) & 3; + /* The JKSLV and JKR2 all detected in high impedance headset */ + if (mic_detected == 0x3) + nau8825->high_imped = true; + else + nau8825->high_imped = false; + + switch (mic_detected) { + case 0: + /* no mic */ + type = SND_JACK_HEADPHONE; + break; + case 1: + dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n"); + type = SND_JACK_HEADSET; + + /* Unground MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2, + 1 << 2); + /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKR2); + /* Attach SARADC to MICGND1 */ + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, + NAU8825_SAR_INPUT_JKR2); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + break; + case 2: + dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n"); + type = SND_JACK_HEADSET; + + /* Unground MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2, + 2 << 2); + /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, + NAU8825_MICBIAS_JKSLV); + /* Attach SARADC to MICGND2 */ + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_INPUT_MASK, + NAU8825_SAR_INPUT_JKSLV); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + break; + case 3: + /* Detection failure case */ + dev_warn(nau8825->dev, + "Detection failure. Try the manually mechanism for jack type checking.\n"); + if (!nau8825_high_imped_detection(nau8825)) { + type = SND_JACK_HEADSET; + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + } else + type = SND_JACK_HEADPHONE; + break; + } + + /* Update to the default divider of internal clock for power saving */ + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_MCLK_SRC_MASK, 0xf); + + /* Disable HSD function */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, NAU8825_HSD_AUTO_MODE, 0); + + /* Leaving HPOL/R grounded after jack insert by default. They will be + * ungrounded as part of the widget power up sequence at the beginning + * of playback to reduce pop. + */ + return type; +} + +#define NAU8825_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + +static irqreturn_t nau8825_interrupt(int irq, void *data) +{ + struct nau8825 *nau8825 = (struct nau8825 *)data; + struct regmap *regmap = nau8825->regmap; + int active_irq, clear_irq = 0, event = 0, event_mask = 0; + + if (regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq)) { + dev_err(nau8825->dev, "failed to read irq status\n"); + return IRQ_NONE; + } + + if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) == + NAU8825_JACK_EJECTION_DETECTED) { + + nau8825_eject_jack(nau8825); + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8825_JACK_EJECTION_IRQ_MASK; + } else if (active_irq & NAU8825_KEY_SHORT_PRESS_IRQ) { + int key_status; + + regmap_read(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, + &key_status); + + /* upper 8 bits of the register are for short pressed keys, + * lower 8 bits - for long pressed buttons + */ + nau8825->button_pressed = nau8825_button_decode( + key_status >> 8); + + event |= nau8825->button_pressed; + event_mask |= NAU8825_BUTTONS; + clear_irq = NAU8825_KEY_SHORT_PRESS_IRQ; + } else if (active_irq & NAU8825_KEY_RELEASE_IRQ) { + event_mask = NAU8825_BUTTONS; + clear_irq = NAU8825_KEY_RELEASE_IRQ; + } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) { + if (nau8825_is_jack_inserted(regmap)) { + event |= nau8825_jack_insert(nau8825); + if (nau8825->xtalk_enable && !nau8825->high_imped) { + /* Apply the cross talk suppression in the + * headset without high impedance. + */ + if (!nau8825->xtalk_protect) { + /* Raise protection for cross talk de- + * tection if no protection before. + * The driver has to cancel the pro- + * cess and restore changes if process + * is ongoing when ejection. + */ + int ret; + nau8825->xtalk_protect = true; + ret = nau8825_sema_acquire(nau8825, 0); + if (ret) + nau8825->xtalk_protect = false; + } + /* Startup cross talk detection process */ + if (nau8825->xtalk_protect) { + nau8825->xtalk_state = + NAU8825_XTALK_PREPARE; + schedule_work(&nau8825->xtalk_work); + } + } else { + /* The cross talk suppression shouldn't apply + * in the headset with high impedance. Thus, + * relieve the protection raised before. + */ + if (nau8825->xtalk_protect) { + nau8825_sema_release(nau8825); + nau8825->xtalk_protect = false; + } + } + } else { + dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n"); + nau8825_eject_jack(nau8825); + } + + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8825_HEADSET_COMPLETION_IRQ; + /* Record the interruption report event for driver to report + * the event later. The jack report will delay until cross + * talk detection process is done. + */ + if (nau8825->xtalk_state == NAU8825_XTALK_PREPARE) { + nau8825->xtalk_event = event; + nau8825->xtalk_event_mask = event_mask; + } + } else if (active_irq & NAU8825_IMPEDANCE_MEAS_IRQ) { + /* crosstalk detection enable and process on going */ + if (nau8825->xtalk_enable && nau8825->xtalk_protect) + schedule_work(&nau8825->xtalk_work); + clear_irq = NAU8825_IMPEDANCE_MEAS_IRQ; + } else if ((active_irq & NAU8825_JACK_INSERTION_IRQ_MASK) == + NAU8825_JACK_INSERTION_DETECTED) { + /* One more step to check GPIO status directly. Thus, the + * driver can confirm the real insertion interruption because + * the intrruption at manual mode has bypassed debounce + * circuit which can get rid of unstable status. + */ + if (nau8825_is_jack_inserted(regmap)) { + /* Turn off insertion interruption at manual mode */ + regmap_update_bits(regmap, + NAU8825_REG_INTERRUPT_DIS_CTRL, + NAU8825_IRQ_INSERT_DIS, + NAU8825_IRQ_INSERT_DIS); + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_INSERT_EN, NAU8825_IRQ_INSERT_EN); + /* Enable interruption for jack type detection at audo + * mode which can detect microphone and jack type. + */ + nau8825_setup_auto_irq(nau8825); + } + } + + if (!clear_irq) + clear_irq = active_irq; + /* clears the rightmost interruption */ + regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq); + + /* Delay jack report until cross talk detection is done. It can avoid + * application to do playback preparation when cross talk detection + * process is still working. Otherwise, the resource like clock and + * power will be issued by them at the same time and conflict happens. + */ + if (event_mask && nau8825->xtalk_state == NAU8825_XTALK_DONE) + snd_soc_jack_report(nau8825->jack, event, event_mask); + + return IRQ_HANDLED; +} + +static void nau8825_setup_buttons(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_TRACKING_GAIN_MASK, + nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_COMPARE_TIME_MASK, + nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT); + regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL, + NAU8825_SAR_SAMPLING_TIME_MASK, + nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT); + + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_LEVELS_NR_MASK, + (nau8825->sar_threshold_num - 1) << NAU8825_KEYDET_LEVELS_NR_SFT); + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_HYSTERESIS_MASK, + nau8825->sar_hysteresis << NAU8825_KEYDET_HYSTERESIS_SFT); + regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL, + NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK, + nau8825->key_debounce << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT); + + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_1, + (nau8825->sar_threshold[0] << 8) | nau8825->sar_threshold[1]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_2, + (nau8825->sar_threshold[2] << 8) | nau8825->sar_threshold[3]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_3, + (nau8825->sar_threshold[4] << 8) | nau8825->sar_threshold[5]); + regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_4, + (nau8825->sar_threshold[6] << 8) | nau8825->sar_threshold[7]); + + /* Enable short press and release interruptions */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_KEY_SHORT_PRESS_EN | NAU8825_IRQ_KEY_RELEASE_EN, + 0); +} + +static void nau8825_init_regs(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + + /* Latch IIC LSB value */ + regmap_write(regmap, NAU8825_REG_IIC_ADDR_SET, 0x0001); + /* Enable Bias/Vmid */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_VMID, NAU8825_BIAS_VMID); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_GLOBAL_BIAS_EN, NAU8825_GLOBAL_BIAS_EN); + + /* VMID Tieoff */ + regmap_update_bits(regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_VMID_SEL_MASK, + nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT); + /* Disable Boost Driver, Automatic Short circuit protection enable */ + regmap_update_bits(regmap, NAU8825_REG_BOOST, + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS | + NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN, + NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS | + NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN); + + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_OUTPUT_EN, + nau8825->jkdet_enable ? 0 : NAU8825_JKDET_OUTPUT_EN); + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_PULL_EN, + nau8825->jkdet_pull_enable ? 0 : NAU8825_JKDET_PULL_EN); + regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, + NAU8825_JKDET_PULL_UP, + nau8825->jkdet_pull_up ? NAU8825_JKDET_PULL_UP : 0); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_POLARITY, + /* jkdet_polarity - 1 is for active-low */ + nau8825->jkdet_polarity ? 0 : NAU8825_JACK_POLARITY); + + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_INSERT_DEBOUNCE_MASK, + nau8825->jack_insert_debounce << NAU8825_JACK_INSERT_DEBOUNCE_SFT); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_EJECT_DEBOUNCE_MASK, + nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT); + + /* Pull up IRQ pin */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_PIN_PULLUP | NAU8825_IRQ_PIN_PULL_EN, + NAU8825_IRQ_PIN_PULLUP | NAU8825_IRQ_PIN_PULL_EN); + /* Mask unneeded IRQs: 1 - disable, 0 - enable */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff); + + regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage); + + if (nau8825->sar_threshold_num) + nau8825_setup_buttons(nau8825); + + /* Default oversampling/decimations settings are unusable + * (audible hiss). Set it to something better. + */ + regmap_update_bits(regmap, NAU8825_REG_ADC_RATE, + NAU8825_ADC_SYNC_DOWN_MASK | NAU8825_ADC_SINC4_EN, + NAU8825_ADC_SYNC_DOWN_64); + regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, + NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_64); + /* Disable DACR/L power */ + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input + * signal to avoid any glitches due to power up transients in both + * the analog and digital DAC circuit. + */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, + NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); + /* CICCLP off */ + regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, + NAU8825_DAC_CLIP_OFF, NAU8825_DAC_CLIP_OFF); + + /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */ + regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_2, + NAU8825_HP_NON_CLASSG_CURRENT_2xADJ | + NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB, + NAU8825_HP_NON_CLASSG_CURRENT_2xADJ | + NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB); + /* Class G timer 64ms */ + regmap_update_bits(regmap, NAU8825_REG_CLASSG_CTRL, + NAU8825_CLASSG_TIMER_MASK, + 0x20 << NAU8825_CLASSG_TIMER_SFT); + /* DAC clock delay 2ns, VREF */ + regmap_update_bits(regmap, NAU8825_REG_RDAC, + NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK, + (0x2 << NAU8825_RDAC_CLK_DELAY_SFT) | + (0x3 << NAU8825_RDAC_VREF_SFT)); + /* Config L/R channel */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_DACL_CTRL, + NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_L); + regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL, + NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_R); + /* Disable short Frame Sync detection logic */ + regmap_update_bits(regmap, NAU8825_REG_LEFT_TIME_SLOT, + NAU8825_DIS_FS_SHORT_DET, NAU8825_DIS_FS_SHORT_DET); + /* ADCDAT IO drive strength control */ + regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_ADCOUT_DS_MASK, + nau8825->adcout_ds << NAU8825_ADCOUT_DS_SFT); +} + +static const struct regmap_config nau8825_regmap_config = { + .val_bits = NAU8825_REG_DATA_LEN, + .reg_bits = NAU8825_REG_ADDR_LEN, + + .max_register = NAU8825_REG_MAX, + .readable_reg = nau8825_readable_reg, + .writeable_reg = nau8825_writeable_reg, + .volatile_reg = nau8825_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8825_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults), +}; + +static int nau8825_component_probe(struct snd_soc_component *component) +{ + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + nau8825->dapm = dapm; + + return 0; +} + +static void nau8825_component_remove(struct snd_soc_component *component) +{ + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + + /* Cancel and reset cross tak suppresstion detection funciton */ + nau8825_xtalk_cancel(nau8825); +} + +/** + * nau8825_calc_fll_param - Calculate FLL parameters. + * @fll_in: external clock provided to codec. + * @fs: sampling rate. + * @fll_param: Pointer to structure of FLL parameters. + * + * Calculate FLL parameters to configure codec. + * + * Returns 0 for success or negative error code. + */ +static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, + struct nau8825_fll *fll_param) +{ + u64 fvco, fvco_max; + unsigned int fref, i, fvco_sel; + + /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing + * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. + * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK + */ + for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) { + fref = fll_in / fll_pre_scalar[i].param; + if (fref <= NAU_FREF_MAX) + break; + } + if (i == ARRAY_SIZE(fll_pre_scalar)) + return -EINVAL; + fll_param->clk_ref_div = fll_pre_scalar[i].val; + + /* Choose the FLL ratio based on FREF */ + for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) { + if (fref >= fll_ratio[i].param) + break; + } + if (i == ARRAY_SIZE(fll_ratio)) + return -EINVAL; + fll_param->ratio = fll_ratio[i].val; + + /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. + * FDCO must be within the 90MHz - 124MHz or the FFL cannot be + * guaranteed across the full range of operation. + * FDCO = freq_out * 2 * mclk_src_scaling + */ + fvco_max = 0; + fvco_sel = ARRAY_SIZE(mclk_src_scaling); + for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { + fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; + if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && + fvco_max < fvco) { + fvco_max = fvco; + fvco_sel = i; + } + } + if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel) + return -EINVAL; + fll_param->mclk_src = mclk_src_scaling[fvco_sel].val; + + /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional + * input based on FDCO, FREF and FLL ratio. + */ + fvco = div_u64(fvco_max << fll_param->fll_frac_num, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> fll_param->fll_frac_num) & 0x3FF; + if (fll_param->fll_frac_num == 16) + fll_param->fll_frac = fvco & 0xFFFF; + else + fll_param->fll_frac = fvco & 0xFFFFFF; + return 0; +} + +static void nau8825_fll_apply(struct nau8825 *nau8825, + struct nau8825_fll *fll_param) +{ + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK | NAU8825_CLK_MCLK_SRC_MASK, + NAU8825_CLK_SRC_MCLK | fll_param->mclk_src); + /* Make DSP operate at high speed for better performance. */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, + NAU8825_FLL_RATIO_MASK | NAU8825_ICTRL_LATCH_MASK, + fll_param->ratio | (0x6 << NAU8825_ICTRL_LATCH_SFT)); + /* FLL 16/24 bit fractional input */ + if (fll_param->fll_frac_num == 16) + regmap_write(nau8825->regmap, NAU8825_REG_FLL2, + fll_param->fll_frac); + else { + regmap_write(nau8825->regmap, NAU8825_REG_FLL2_LOWER, + fll_param->fll_frac & 0xffff); + regmap_write(nau8825->regmap, NAU8825_REG_FLL2_UPPER, + (fll_param->fll_frac >> 16) & 0xff); + } + /* FLL 10-bit integer input */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3, + NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); + /* FLL pre-scaler */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4, + NAU8825_FLL_REF_DIV_MASK, + fll_param->clk_ref_div << NAU8825_FLL_REF_DIV_SFT); + /* select divided VCO input */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, + NAU8825_FLL_CLK_SW_MASK, NAU8825_FLL_CLK_SW_REF); + /* Disable free-running mode */ + regmap_update_bits(nau8825->regmap, + NAU8825_REG_FLL6, NAU8825_DCO_EN, 0); + if (fll_param->fll_frac) { + /* set FLL loop filter enable and cutoff frequency at 500Khz */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, + NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | + NAU8825_FLL_FTR_SW_MASK, + NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | + NAU8825_FLL_FTR_SW_FILTER); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, + NAU8825_SDM_EN | NAU8825_CUTOFF500, + NAU8825_SDM_EN | NAU8825_CUTOFF500); + } else { + /* disable FLL loop filter and cutoff frequency */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5, + NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN | + NAU8825_FLL_FTR_SW_MASK, NAU8825_FLL_FTR_SW_ACCU); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, + NAU8825_SDM_EN | NAU8825_CUTOFF500, 0); + } +} + +/* freq_out must be 256*Fs in order to achieve the best performance */ +static int nau8825_set_pll(struct snd_soc_component *component, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + struct nau8825_fll fll_param; + int ret, fs; + + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + fll_param.fll_frac_num = 16; + else + fll_param.fll_frac_num = 24; + + fs = freq_out / 256; + ret = nau8825_calc_fll_param(freq_in, fs, &fll_param); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + dev_dbg(component->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n", + fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac, + fll_param.fll_int, fll_param.clk_ref_div); + + nau8825_fll_apply(nau8825, &fll_param); + mdelay(2); + regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); + return 0; +} + +static int nau8825_mclk_prepare(struct nau8825 *nau8825, unsigned int freq) +{ + int ret; + + nau8825->mclk = devm_clk_get(nau8825->dev, "mclk"); + if (IS_ERR(nau8825->mclk)) { + dev_info(nau8825->dev, "No 'mclk' clock found, assume MCLK is managed externally"); + return 0; + } + + if (!nau8825->mclk_freq) { + ret = clk_prepare_enable(nau8825->mclk); + if (ret) { + dev_err(nau8825->dev, "Unable to prepare codec mclk\n"); + return ret; + } + } + + if (nau8825->mclk_freq != freq) { + freq = clk_round_rate(nau8825->mclk, freq); + ret = clk_set_rate(nau8825->mclk, freq); + if (ret) { + dev_err(nau8825->dev, "Unable to set mclk rate\n"); + return ret; + } + nau8825->mclk_freq = freq; + } + + return 0; +} + +static void nau8825_configure_mclk_as_sysclk(struct regmap *regmap) +{ + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK); + regmap_update_bits(regmap, NAU8825_REG_FLL6, + NAU8825_DCO_EN, 0); + /* Make DSP operate as default setting for power saving. */ + regmap_update_bits(regmap, NAU8825_REG_FLL1, + NAU8825_ICTRL_LATCH_MASK, 0); +} + +static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id, + unsigned int freq) +{ + struct regmap *regmap = nau8825->regmap; + int ret; + + switch (clk_id) { + case NAU8825_CLK_DIS: + /* Clock provided externally and disable internal VCO clock */ + nau8825_configure_mclk_as_sysclk(regmap); + if (nau8825->mclk_freq) { + clk_disable_unprepare(nau8825->mclk); + nau8825->mclk_freq = 0; + } + + break; + case NAU8825_CLK_MCLK: + /* Acquire the semaphore to synchronize the playback and + * interrupt handler. In order to avoid the playback inter- + * fered by cross talk process, the driver make the playback + * preparation halted until cross talk process finish. + */ + nau8825_sema_acquire(nau8825, 3 * HZ); + nau8825_configure_mclk_as_sysclk(regmap); + /* MCLK not changed by clock tree */ + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_MCLK_SRC_MASK, 0); + /* Release the semaphore. */ + nau8825_sema_release(nau8825); + + ret = nau8825_mclk_prepare(nau8825, freq); + if (ret) + return ret; + + break; + case NAU8825_CLK_INTERNAL: + if (nau8825_is_jack_inserted(nau8825->regmap)) { + regmap_update_bits(regmap, NAU8825_REG_FLL6, + NAU8825_DCO_EN, NAU8825_DCO_EN); + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO); + /* Decrease the VCO frequency and make DSP operate + * as default setting for power saving. + */ + regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, + NAU8825_CLK_MCLK_SRC_MASK, 0xf); + regmap_update_bits(regmap, NAU8825_REG_FLL1, + NAU8825_ICTRL_LATCH_MASK | + NAU8825_FLL_RATIO_MASK, 0x10); + regmap_update_bits(regmap, NAU8825_REG_FLL6, + NAU8825_SDM_EN, NAU8825_SDM_EN); + } else { + /* The clock turns off intentionally for power saving + * when no headset connected. + */ + nau8825_configure_mclk_as_sysclk(regmap); + dev_warn(nau8825->dev, "Disable clock for power saving when no headset connected\n"); + } + if (nau8825->mclk_freq) { + clk_disable_unprepare(nau8825->mclk); + nau8825->mclk_freq = 0; + } + + break; + case NAU8825_CLK_FLL_MCLK: + /* Acquire the semaphore to synchronize the playback and + * interrupt handler. In order to avoid the playback inter- + * fered by cross talk process, the driver make the playback + * preparation halted until cross talk process finish. + */ + nau8825_sema_acquire(nau8825, 3 * HZ); + /* Higher FLL reference input frequency can only set lower + * gain error, such as 0000 for input reference from MCLK + * 12.288Mhz. + */ + regmap_update_bits(regmap, NAU8825_REG_FLL3, + NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK, + NAU8825_FLL_CLK_SRC_MCLK | 0); + /* Release the semaphore. */ + nau8825_sema_release(nau8825); + + ret = nau8825_mclk_prepare(nau8825, freq); + if (ret) + return ret; + + break; + case NAU8825_CLK_FLL_BLK: + /* Acquire the semaphore to synchronize the playback and + * interrupt handler. In order to avoid the playback inter- + * fered by cross talk process, the driver make the playback + * preparation halted until cross talk process finish. + */ + nau8825_sema_acquire(nau8825, 3 * HZ); + /* If FLL reference input is from low frequency source, + * higher error gain can apply such as 0xf which has + * the most sensitive gain error correction threshold, + * Therefore, FLL has the most accurate DCO to + * target frequency. + */ + regmap_update_bits(regmap, NAU8825_REG_FLL3, + NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK, + NAU8825_FLL_CLK_SRC_BLK | + (0xf << NAU8825_GAIN_ERR_SFT)); + /* Release the semaphore. */ + nau8825_sema_release(nau8825); + + if (nau8825->mclk_freq) { + clk_disable_unprepare(nau8825->mclk); + nau8825->mclk_freq = 0; + } + + break; + case NAU8825_CLK_FLL_FS: + /* Acquire the semaphore to synchronize the playback and + * interrupt handler. In order to avoid the playback inter- + * fered by cross talk process, the driver make the playback + * preparation halted until cross talk process finish. + */ + nau8825_sema_acquire(nau8825, 3 * HZ); + /* If FLL reference input is from low frequency source, + * higher error gain can apply such as 0xf which has + * the most sensitive gain error correction threshold, + * Therefore, FLL has the most accurate DCO to + * target frequency. + */ + regmap_update_bits(regmap, NAU8825_REG_FLL3, + NAU8825_FLL_CLK_SRC_MASK | NAU8825_GAIN_ERR_MASK, + NAU8825_FLL_CLK_SRC_FS | + (0xf << NAU8825_GAIN_ERR_SFT)); + /* Release the semaphore. */ + nau8825_sema_release(nau8825); + + if (nau8825->mclk_freq) { + clk_disable_unprepare(nau8825->mclk); + nau8825->mclk_freq = 0; + } + + break; + default: + dev_err(nau8825->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + dev_dbg(nau8825->dev, "Sysclk is %dHz and clock id is %d\n", freq, + clk_id); + return 0; +} + +static int nau8825_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + + return nau8825_configure_sysclk(nau8825, clk_id, freq); +} + +static int nau8825_resume_setup(struct nau8825 *nau8825) +{ + struct regmap *regmap = nau8825->regmap; + + /* Close clock when jack type detection at manual mode */ + nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0); + + /* Clear all interruption status */ + nau8825_int_status_clear_all(regmap); + + /* Enable both insertion and ejection interruptions, and then + * bypass de-bounce circuit. + */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN | + NAU8825_IRQ_EJECT_EN | NAU8825_IRQ_INSERT_EN, + NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN); + regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL, + NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS); + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, + NAU8825_IRQ_INSERT_DIS | NAU8825_IRQ_EJECT_DIS, 0); + + return 0; +} + +static int nau8825_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + if (nau8825->mclk_freq) { + ret = clk_prepare_enable(nau8825->mclk); + if (ret) { + dev_err(nau8825->dev, "Unable to prepare component mclk\n"); + return ret; + } + } + /* Setup codec configuration after resume */ + nau8825_resume_setup(nau8825); + } + break; + + case SND_SOC_BIAS_OFF: + /* Reset the configuration of jack type for detection */ + /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_MIC_BIAS, + NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0); + /* ground HPL/HPR, MICGRND1/2 */ + regmap_update_bits(nau8825->regmap, + NAU8825_REG_HSD_CTRL, 0xf, 0xf); + /* Cancel and reset cross talk detection funciton */ + nau8825_xtalk_cancel(nau8825); + /* Turn off all interruptions before system shutdown. Keep the + * interruption quiet before resume setup completes. + */ + regmap_write(nau8825->regmap, + NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff); + /* Disable ADC needed for interruptions at audo mode */ + regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL, + NAU8825_ENABLE_ADC, 0); + if (nau8825->mclk_freq) + clk_disable_unprepare(nau8825->mclk); + break; + } + return 0; +} + +static int __maybe_unused nau8825_suspend(struct snd_soc_component *component) +{ + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + + disable_irq(nau8825->irq); + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + /* Power down codec power; don't suppoet button wakeup */ + snd_soc_dapm_disable_pin(nau8825->dapm, "SAR"); + snd_soc_dapm_disable_pin(nau8825->dapm, "MICBIAS"); + snd_soc_dapm_sync(nau8825->dapm); + regcache_cache_only(nau8825->regmap, true); + regcache_mark_dirty(nau8825->regmap); + + return 0; +} + +static int __maybe_unused nau8825_resume(struct snd_soc_component *component) +{ + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + int ret; + + regcache_cache_only(nau8825->regmap, false); + regcache_sync(nau8825->regmap); + nau8825->xtalk_protect = true; + ret = nau8825_sema_acquire(nau8825, 0); + if (ret) + nau8825->xtalk_protect = false; + enable_irq(nau8825->irq); + + return 0; +} + +static int nau8825_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + return nau8825_enable_jack_detect(component, jack); +} + +static const struct snd_soc_component_driver nau8825_component_driver = { + .probe = nau8825_component_probe, + .remove = nau8825_component_remove, + .set_sysclk = nau8825_set_sysclk, + .set_pll = nau8825_set_pll, + .set_bias_level = nau8825_set_bias_level, + .suspend = nau8825_suspend, + .resume = nau8825_resume, + .controls = nau8825_controls, + .num_controls = ARRAY_SIZE(nau8825_controls), + .dapm_widgets = nau8825_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets), + .dapm_routes = nau8825_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes), + .set_jack = nau8825_set_jack, + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static void nau8825_reset_chip(struct regmap *regmap) +{ + regmap_write(regmap, NAU8825_REG_RESET, 0x00); + regmap_write(regmap, NAU8825_REG_RESET, 0x00); +} + +static void nau8825_print_device_properties(struct nau8825 *nau8825) +{ + int i; + struct device *dev = nau8825->dev; + + dev_dbg(dev, "jkdet-enable: %d\n", nau8825->jkdet_enable); + dev_dbg(dev, "jkdet-pull-enable: %d\n", nau8825->jkdet_pull_enable); + dev_dbg(dev, "jkdet-pull-up: %d\n", nau8825->jkdet_pull_up); + dev_dbg(dev, "jkdet-polarity: %d\n", nau8825->jkdet_polarity); + dev_dbg(dev, "micbias-voltage: %d\n", nau8825->micbias_voltage); + dev_dbg(dev, "vref-impedance: %d\n", nau8825->vref_impedance); + + dev_dbg(dev, "sar-threshold-num: %d\n", nau8825->sar_threshold_num); + for (i = 0; i < nau8825->sar_threshold_num; i++) + dev_dbg(dev, "sar-threshold[%d]=%d\n", i, + nau8825->sar_threshold[i]); + + dev_dbg(dev, "sar-hysteresis: %d\n", nau8825->sar_hysteresis); + dev_dbg(dev, "sar-voltage: %d\n", nau8825->sar_voltage); + dev_dbg(dev, "sar-compare-time: %d\n", nau8825->sar_compare_time); + dev_dbg(dev, "sar-sampling-time: %d\n", nau8825->sar_sampling_time); + dev_dbg(dev, "short-key-debounce: %d\n", nau8825->key_debounce); + dev_dbg(dev, "jack-insert-debounce: %d\n", + nau8825->jack_insert_debounce); + dev_dbg(dev, "jack-eject-debounce: %d\n", + nau8825->jack_eject_debounce); + dev_dbg(dev, "crosstalk-enable: %d\n", + nau8825->xtalk_enable); + dev_dbg(dev, "adcout-drive-strong: %d\n", nau8825->adcout_ds); + dev_dbg(dev, "adc-delay-ms: %d\n", nau8825->adc_delay); +} + +static int nau8825_read_device_properties(struct device *dev, + struct nau8825 *nau8825) { + int ret; + + nau8825->jkdet_enable = device_property_read_bool(dev, + "nuvoton,jkdet-enable"); + nau8825->jkdet_pull_enable = device_property_read_bool(dev, + "nuvoton,jkdet-pull-enable"); + nau8825->jkdet_pull_up = device_property_read_bool(dev, + "nuvoton,jkdet-pull-up"); + ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity", + &nau8825->jkdet_polarity); + if (ret) + nau8825->jkdet_polarity = 1; + ret = device_property_read_u32(dev, "nuvoton,micbias-voltage", + &nau8825->micbias_voltage); + if (ret) + nau8825->micbias_voltage = 6; + ret = device_property_read_u32(dev, "nuvoton,vref-impedance", + &nau8825->vref_impedance); + if (ret) + nau8825->vref_impedance = 2; + ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num", + &nau8825->sar_threshold_num); + if (ret) + nau8825->sar_threshold_num = 4; + ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold", + nau8825->sar_threshold, nau8825->sar_threshold_num); + if (ret) { + nau8825->sar_threshold[0] = 0x08; + nau8825->sar_threshold[1] = 0x12; + nau8825->sar_threshold[2] = 0x26; + nau8825->sar_threshold[3] = 0x73; + } + ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis", + &nau8825->sar_hysteresis); + if (ret) + nau8825->sar_hysteresis = 0; + ret = device_property_read_u32(dev, "nuvoton,sar-voltage", + &nau8825->sar_voltage); + if (ret) + nau8825->sar_voltage = 6; + ret = device_property_read_u32(dev, "nuvoton,sar-compare-time", + &nau8825->sar_compare_time); + if (ret) + nau8825->sar_compare_time = 1; + ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time", + &nau8825->sar_sampling_time); + if (ret) + nau8825->sar_sampling_time = 1; + ret = device_property_read_u32(dev, "nuvoton,short-key-debounce", + &nau8825->key_debounce); + if (ret) + nau8825->key_debounce = 3; + ret = device_property_read_u32(dev, "nuvoton,jack-insert-debounce", + &nau8825->jack_insert_debounce); + if (ret) + nau8825->jack_insert_debounce = 7; + ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce", + &nau8825->jack_eject_debounce); + if (ret) + nau8825->jack_eject_debounce = 0; + nau8825->xtalk_enable = device_property_read_bool(dev, + "nuvoton,crosstalk-enable"); + nau8825->adcout_ds = device_property_read_bool(dev, "nuvoton,adcout-drive-strong"); + ret = device_property_read_u32(dev, "nuvoton,adc-delay-ms", &nau8825->adc_delay); + if (ret) + nau8825->adc_delay = 125; + if (nau8825->adc_delay < 125 || nau8825->adc_delay > 500) + dev_warn(dev, "Please set the suitable delay time!\n"); + + nau8825->mclk = devm_clk_get(dev, "mclk"); + if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (PTR_ERR(nau8825->mclk) == -ENOENT) { + /* The MCLK is managed externally or not used at all */ + nau8825->mclk = NULL; + dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally"); + } else if (IS_ERR(nau8825->mclk)) { + return -EINVAL; + } + + return 0; +} + +static int nau8825_setup_irq(struct nau8825 *nau8825) +{ + int ret; + + ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL, + nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "nau8825", nau8825); + + if (ret) { + dev_err(nau8825->dev, "Cannot request irq %d (%d)\n", + nau8825->irq, ret); + return ret; + } + + return 0; +} + +static int nau8825_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev); + int ret, value; + + if (!nau8825) { + nau8825 = devm_kzalloc(dev, sizeof(*nau8825), GFP_KERNEL); + if (!nau8825) + return -ENOMEM; + ret = nau8825_read_device_properties(dev, nau8825); + if (ret) + return ret; + } + + i2c_set_clientdata(i2c, nau8825); + + nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap_config); + if (IS_ERR(nau8825->regmap)) + return PTR_ERR(nau8825->regmap); + nau8825->dev = dev; + nau8825->irq = i2c->irq; + /* Initiate parameters, semaphore and work queue which are needed in + * cross talk suppression measurment function. + */ + nau8825->xtalk_state = NAU8825_XTALK_DONE; + nau8825->xtalk_protect = false; + nau8825->xtalk_baktab_initialized = false; + sema_init(&nau8825->xtalk_sem, 1); + INIT_WORK(&nau8825->xtalk_work, nau8825_xtalk_work); + + nau8825_print_device_properties(nau8825); + + nau8825_reset_chip(nau8825->regmap); + ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value); + if (ret < 0) { + dev_err(dev, "Failed to read device id from the NAU8825: %d\n", + ret); + return ret; + } + nau8825->sw_id = value & NAU8825_SOFTWARE_ID_MASK; + switch (nau8825->sw_id) { + case NAU8825_SOFTWARE_ID_NAU8825: + break; + case NAU8825_SOFTWARE_ID_NAU8825C: + ret = regmap_register_patch(nau8825->regmap, nau8825_regmap_patch, + ARRAY_SIZE(nau8825_regmap_patch)); + if (ret) { + dev_err(dev, "Failed to register Rev C patch: %d\n", ret); + return ret; + } + break; + default: + dev_err(dev, "Not a NAU8825 chip\n"); + return -ENODEV; + } + + nau8825_init_regs(nau8825); + + if (i2c->irq) + nau8825_setup_irq(nau8825); + + return devm_snd_soc_register_component(&i2c->dev, + &nau8825_component_driver, + &nau8825_dai, 1); +} + +static void nau8825_i2c_remove(struct i2c_client *client) +{} + +static const struct i2c_device_id nau8825_i2c_ids[] = { + { "nau8825", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau8825_i2c_ids); + +#ifdef CONFIG_OF +static const struct of_device_id nau8825_of_ids[] = { + { .compatible = "nuvoton,nau8825", }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8825_of_ids); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id nau8825_acpi_match[] = { + { "10508825", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match); +#endif + +static struct i2c_driver nau8825_driver = { + .driver = { + .name = "nau8825", + .of_match_table = of_match_ptr(nau8825_of_ids), + .acpi_match_table = ACPI_PTR(nau8825_acpi_match), + }, + .probe = nau8825_i2c_probe, + .remove = nau8825_i2c_remove, + .id_table = nau8825_i2c_ids, +}; +module_i2c_driver(nau8825_driver); + +MODULE_DESCRIPTION("ASoC nau8825 driver"); +MODULE_AUTHOR("Anatol Pomozov "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h new file mode 100644 index 0000000000..2abfbb5184 --- /dev/null +++ b/sound/soc/codecs/nau8825.h @@ -0,0 +1,538 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NAU8825 ALSA SoC audio driver + * + * Copyright 2015 Google Inc. + * Author: Anatol Pomozov + */ + +#ifndef __NAU8825_H__ +#define __NAU8825_H__ + +#define NAU8825_REG_RESET 0x00 +#define NAU8825_REG_ENA_CTRL 0x01 +#define NAU8825_REG_IIC_ADDR_SET 0x02 +#define NAU8825_REG_CLK_DIVIDER 0x03 +#define NAU8825_REG_FLL1 0x04 +#define NAU8825_REG_FLL2 0x05 +#define NAU8825_REG_FLL3 0x06 +#define NAU8825_REG_FLL4 0x07 +#define NAU8825_REG_FLL5 0x08 +#define NAU8825_REG_FLL6 0x09 +#define NAU8825_REG_FLL_VCO_RSV 0x0a +#define NAU8825_REG_HSD_CTRL 0x0c +#define NAU8825_REG_JACK_DET_CTRL 0x0d +#define NAU8825_REG_INTERRUPT_MASK 0x0f +#define NAU8825_REG_IRQ_STATUS 0x10 +#define NAU8825_REG_INT_CLR_KEY_STATUS 0x11 +#define NAU8825_REG_INTERRUPT_DIS_CTRL 0x12 +#define NAU8825_REG_SAR_CTRL 0x13 +#define NAU8825_REG_KEYDET_CTRL 0x14 +#define NAU8825_REG_VDET_THRESHOLD_1 0x15 +#define NAU8825_REG_VDET_THRESHOLD_2 0x16 +#define NAU8825_REG_VDET_THRESHOLD_3 0x17 +#define NAU8825_REG_VDET_THRESHOLD_4 0x18 +#define NAU8825_REG_GPIO34_CTRL 0x19 +#define NAU8825_REG_GPIO12_CTRL 0x1a +#define NAU8825_REG_TDM_CTRL 0x1b +#define NAU8825_REG_I2S_PCM_CTRL1 0x1c +#define NAU8825_REG_I2S_PCM_CTRL2 0x1d +#define NAU8825_REG_LEFT_TIME_SLOT 0x1e +#define NAU8825_REG_RIGHT_TIME_SLOT 0x1f +#define NAU8825_REG_BIQ_CTRL 0x20 +#define NAU8825_REG_BIQ_COF1 0x21 +#define NAU8825_REG_BIQ_COF2 0x22 +#define NAU8825_REG_BIQ_COF3 0x23 +#define NAU8825_REG_BIQ_COF4 0x24 +#define NAU8825_REG_BIQ_COF5 0x25 +#define NAU8825_REG_BIQ_COF6 0x26 +#define NAU8825_REG_BIQ_COF7 0x27 +#define NAU8825_REG_BIQ_COF8 0x28 +#define NAU8825_REG_BIQ_COF9 0x29 +#define NAU8825_REG_BIQ_COF10 0x2a +#define NAU8825_REG_ADC_RATE 0x2b +#define NAU8825_REG_DAC_CTRL1 0x2c +#define NAU8825_REG_DAC_CTRL2 0x2d +#define NAU8825_REG_DAC_DGAIN_CTRL 0x2f +#define NAU8825_REG_ADC_DGAIN_CTRL 0x30 +#define NAU8825_REG_MUTE_CTRL 0x31 +#define NAU8825_REG_HSVOL_CTRL 0x32 +#define NAU8825_REG_DACL_CTRL 0x33 +#define NAU8825_REG_DACR_CTRL 0x34 +#define NAU8825_REG_ADC_DRC_KNEE_IP12 0x38 +#define NAU8825_REG_ADC_DRC_KNEE_IP34 0x39 +#define NAU8825_REG_ADC_DRC_SLOPES 0x3a +#define NAU8825_REG_ADC_DRC_ATKDCY 0x3b +#define NAU8825_REG_DAC_DRC_KNEE_IP12 0x45 +#define NAU8825_REG_DAC_DRC_KNEE_IP34 0x46 +#define NAU8825_REG_DAC_DRC_SLOPES 0x47 +#define NAU8825_REG_DAC_DRC_ATKDCY 0x48 +#define NAU8825_REG_IMM_MODE_CTRL 0x4c +#define NAU8825_REG_IMM_RMS_L 0x4d +#define NAU8825_REG_IMM_RMS_R 0x4e +#define NAU8825_REG_CLASSG_CTRL 0x50 +#define NAU8825_REG_OPT_EFUSE_CTRL 0x51 +#define NAU8825_REG_MISC_CTRL 0x55 +#define NAU8825_REG_I2C_DEVICE_ID 0x58 +#define NAU8825_REG_SARDOUT_RAM_STATUS 0x59 +#define NAU8825_REG_FLL2_LOWER 0x5a +#define NAU8825_REG_FLL2_UPPER 0x5b +#define NAU8825_REG_BIAS_ADJ 0x66 +#define NAU8825_REG_TRIM_SETTINGS 0x68 +#define NAU8825_REG_ANALOG_CONTROL_1 0x69 +#define NAU8825_REG_ANALOG_CONTROL_2 0x6a +#define NAU8825_REG_ANALOG_ADC_1 0x71 +#define NAU8825_REG_ANALOG_ADC_2 0x72 +#define NAU8825_REG_RDAC 0x73 +#define NAU8825_REG_MIC_BIAS 0x74 +#define NAU8825_REG_BOOST 0x76 +#define NAU8825_REG_FEPGA 0x77 +#define NAU8825_REG_POWER_UP_CONTROL 0x7f +#define NAU8825_REG_CHARGE_PUMP 0x80 +#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81 +#define NAU8825_REG_GENERAL_STATUS 0x82 +#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS +/* 16-bit control register address, and 16-bits control register data */ +#define NAU8825_REG_ADDR_LEN 16 +#define NAU8825_REG_DATA_LEN 16 + +/* ENA_CTRL (0x1) */ +#define NAU8825_ENABLE_DACR_SFT 10 +#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT) +#define NAU8825_ENABLE_DACL_SFT 9 +#define NAU8825_ENABLE_DACL (1 << NAU8825_ENABLE_DACL_SFT) +#define NAU8825_ENABLE_ADC_SFT 8 +#define NAU8825_ENABLE_ADC (1 << NAU8825_ENABLE_ADC_SFT) +#define NAU8825_ENABLE_ADC_CLK_SFT 7 +#define NAU8825_ENABLE_ADC_CLK (1 << NAU8825_ENABLE_ADC_CLK_SFT) +#define NAU8825_ENABLE_DAC_CLK_SFT 6 +#define NAU8825_ENABLE_DAC_CLK (1 << NAU8825_ENABLE_DAC_CLK_SFT) +#define NAU8825_ENABLE_SAR_SFT 1 + +/* CLK_DIVIDER (0x3) */ +#define NAU8825_CLK_SRC_SFT 15 +#define NAU8825_CLK_SRC_MASK (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_VCO (1 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_SRC_MCLK (0 << NAU8825_CLK_SRC_SFT) +#define NAU8825_CLK_ADC_SRC_SFT 6 +#define NAU8825_CLK_ADC_SRC_MASK (0x3 << NAU8825_CLK_ADC_SRC_SFT) +#define NAU8825_CLK_DAC_SRC_SFT 4 +#define NAU8825_CLK_DAC_SRC_MASK (0x3 << NAU8825_CLK_DAC_SRC_SFT) +#define NAU8825_CLK_MCLK_SRC_MASK (0xf << 0) + +/* FLL1 (0x04) */ +#define NAU8825_ICTRL_LATCH_SFT 10 +#define NAU8825_ICTRL_LATCH_MASK (0x7 << NAU8825_ICTRL_LATCH_SFT) +#define NAU8825_FLL_RATIO_MASK (0x7f << 0) + +/* FLL3 (0x06) */ +#define NAU8825_GAIN_ERR_SFT 12 +#define NAU8825_GAIN_ERR_MASK (0xf << NAU8825_GAIN_ERR_SFT) +#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0) +#define NAU8825_FLL_CLK_SRC_SFT 10 +#define NAU8825_FLL_CLK_SRC_MASK (0x3 << NAU8825_FLL_CLK_SRC_SFT) +#define NAU8825_FLL_CLK_SRC_MCLK (0 << NAU8825_FLL_CLK_SRC_SFT) +#define NAU8825_FLL_CLK_SRC_BLK (0x2 << NAU8825_FLL_CLK_SRC_SFT) +#define NAU8825_FLL_CLK_SRC_FS (0x3 << NAU8825_FLL_CLK_SRC_SFT) + +/* FLL4 (0x07) */ +#define NAU8825_FLL_REF_DIV_SFT 10 +#define NAU8825_FLL_REF_DIV_MASK (0x3 << NAU8825_FLL_REF_DIV_SFT) + +/* FLL5 (0x08) */ +#define NAU8825_FLL_PDB_DAC_EN (0x1 << 15) +#define NAU8825_FLL_LOOP_FTR_EN (0x1 << 14) +#define NAU8825_FLL_CLK_SW_MASK (0x1 << 13) +#define NAU8825_FLL_CLK_SW_N2 (0x1 << 13) +#define NAU8825_FLL_CLK_SW_REF (0x0 << 13) +#define NAU8825_FLL_FTR_SW_MASK (0x1 << 12) +#define NAU8825_FLL_FTR_SW_ACCU (0x1 << 12) +#define NAU8825_FLL_FTR_SW_FILTER (0x0 << 12) + +/* FLL6 (0x9) */ +#define NAU8825_DCO_EN (0x1 << 15) +#define NAU8825_SDM_EN (0x1 << 14) +#define NAU8825_CUTOFF500 (0x1 << 13) + +/* HSD_CTRL (0xc) */ +#define NAU8825_HSD_AUTO_MODE (1 << 6) +/* 0 - open, 1 - short to GND */ +#define NAU8825_SPKR_ENGND1 (1 << 3) +#define NAU8825_SPKR_ENGND2 (1 << 2) +#define NAU8825_SPKR_DWN1R (1 << 1) +#define NAU8825_SPKR_DWN1L (1 << 0) + +/* JACK_DET_CTRL (0xd) */ +#define NAU8825_JACK_DET_RESTART (1 << 9) +#define NAU8825_JACK_DET_DB_BYPASS (1 << 8) +#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5 +#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT) +#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2 +#define NAU8825_JACK_EJECT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT) +#define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */ + +/* INTERRUPT_MASK (0xf) */ +#define NAU8825_IRQ_PIN_PULLUP (1 << 14) +#define NAU8825_IRQ_PIN_PULL_EN (1 << 13) +#define NAU8825_IRQ_OUTPUT_EN (1 << 11) +#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10) +#define NAU8825_IRQ_RMS_EN (1 << 8) +#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7) +#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5) +#define NAU8825_IRQ_EJECT_EN (1 << 2) +#define NAU8825_IRQ_INSERT_EN (1 << 0) + +/* IRQ_STATUS (0x10) */ +#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10) +#define NAU8825_SHORT_CIRCUIT_IRQ (1 << 9) +#define NAU8825_IMPEDANCE_MEAS_IRQ (1 << 8) +#define NAU8825_KEY_IRQ_MASK (0x7 << 5) +#define NAU8825_KEY_RELEASE_IRQ (1 << 7) +#define NAU8825_KEY_LONG_PRESS_IRQ (1 << 6) +#define NAU8825_KEY_SHORT_PRESS_IRQ (1 << 5) +#define NAU8825_MIC_DETECTION_IRQ (1 << 4) +#define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2) +#define NAU8825_JACK_EJECTION_DETECTED (1 << 2) +#define NAU8825_JACK_INSERTION_IRQ_MASK (3 << 0) +#define NAU8825_JACK_INSERTION_DETECTED (1 << 0) + +/* INTERRUPT_DIS_CTRL (0x12) */ +#define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10) +#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7) +#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5) +#define NAU8825_IRQ_EJECT_DIS (1 << 2) +#define NAU8825_IRQ_INSERT_DIS (1 << 0) + +/* SAR_CTRL (0x13) */ +#define NAU8825_SAR_ADC_EN_SFT 12 +#define NAU8825_SAR_ADC_EN (1 << NAU8825_SAR_ADC_EN_SFT) +#define NAU8825_SAR_INPUT_MASK (1 << 11) +#define NAU8825_SAR_INPUT_JKSLV (1 << 11) +#define NAU8825_SAR_INPUT_JKR2 (0 << 11) +#define NAU8825_SAR_TRACKING_GAIN_SFT 8 +#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT) +#define NAU8825_SAR_HV_SEL_SFT 7 +#define NAU8825_SAR_HV_SEL_MASK (1 << NAU8825_SAR_HV_SEL_SFT) +#define NAU8825_SAR_HV_SEL_MICBIAS (0 << NAU8825_SAR_HV_SEL_SFT) +#define NAU8825_SAR_HV_SEL_VDDMIC (1 << NAU8825_SAR_HV_SEL_SFT) +#define NAU8825_SAR_RES_SEL_SFT 4 +#define NAU8825_SAR_RES_SEL_MASK (0x7 << NAU8825_SAR_RES_SEL_SFT) +#define NAU8825_SAR_RES_SEL_35K (0 << NAU8825_SAR_RES_SEL_SFT) +#define NAU8825_SAR_RES_SEL_70K (1 << NAU8825_SAR_RES_SEL_SFT) +#define NAU8825_SAR_RES_SEL_170K (2 << NAU8825_SAR_RES_SEL_SFT) +#define NAU8825_SAR_RES_SEL_360K (3 << NAU8825_SAR_RES_SEL_SFT) +#define NAU8825_SAR_RES_SEL_SHORTED (4 << NAU8825_SAR_RES_SEL_SFT) +#define NAU8825_SAR_COMPARE_TIME_SFT 2 +#define NAU8825_SAR_COMPARE_TIME_MASK (3 << 2) +#define NAU8825_SAR_SAMPLING_TIME_SFT 0 +#define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0) + +/* KEYDET_CTRL (0x14) */ +#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT 12 +#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT) +#define NAU8825_KEYDET_LEVELS_NR_SFT 8 +#define NAU8825_KEYDET_LEVELS_NR_MASK (0x7 << 8) +#define NAU8825_KEYDET_HYSTERESIS_SFT 0 +#define NAU8825_KEYDET_HYSTERESIS_MASK 0xf + +/* GPIO12_CTRL (0x1a) */ +#define NAU8825_JKDET_PULL_UP (1 << 11) /* 0 - pull down, 1 - pull up */ +#define NAU8825_JKDET_PULL_EN (1 << 9) /* 0 - enable pull, 1 - disable */ +#define NAU8825_JKDET_OUTPUT_EN (1 << 8) /* 0 - enable input, 1 - enable output */ + +/* TDM_CTRL (0x1b) */ +#define NAU8825_TDM_MODE (0x1 << 15) +#define NAU8825_TDM_OFFSET_EN (0x1 << 14) +#define NAU8825_TDM_DACL_RX_SFT 6 +#define NAU8825_TDM_DACL_RX_MASK (0x3 << NAU8825_TDM_DACL_RX_SFT) +#define NAU8825_TDM_DACR_RX_SFT 4 +#define NAU8825_TDM_DACR_RX_MASK (0x3 << NAU8825_TDM_DACR_RX_SFT) +#define NAU8825_TDM_TX_MASK 0x3 + +/* I2S_PCM_CTRL1 (0x1c) */ +#define NAU8825_I2S_BP_SFT 7 +#define NAU8825_I2S_BP_MASK (1 << NAU8825_I2S_BP_SFT) +#define NAU8825_I2S_BP_INV (1 << NAU8825_I2S_BP_SFT) +#define NAU8825_I2S_PCMB_SFT 6 +#define NAU8825_I2S_PCMB_MASK (1 << NAU8825_I2S_PCMB_SFT) +#define NAU8825_I2S_PCMB_EN (1 << NAU8825_I2S_PCMB_SFT) +#define NAU8825_I2S_DL_SFT 2 +#define NAU8825_I2S_DL_MASK (0x3 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_16 (0 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_20 (1 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_24 (2 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DL_32 (3 << NAU8825_I2S_DL_SFT) +#define NAU8825_I2S_DF_SFT 0 +#define NAU8825_I2S_DF_MASK (0x3 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_RIGTH (0 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_LEFT (1 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_I2S (2 << NAU8825_I2S_DF_SFT) +#define NAU8825_I2S_DF_PCM_AB (3 << NAU8825_I2S_DF_SFT) + +/* I2S_PCM_CTRL2 (0x1d) */ +#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */ +#define NAU8825_I2S_LRC_DIV_SFT 12 +#define NAU8825_I2S_LRC_DIV_MASK (0x3 << NAU8825_I2S_LRC_DIV_SFT) +#define NAU8825_I2S_PCM_TS_EN_SFT 10 +#define NAU8825_I2S_PCM_TS_EN_MASK (1 << NAU8825_I2S_PCM_TS_EN_SFT) +#define NAU8825_I2S_PCM_TS_EN (1 << NAU8825_I2S_PCM_TS_EN_SFT) +#define NAU8825_I2S_MS_SFT 3 +#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT) +#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT) +#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT) +#define NAU8825_I2S_BLK_DIV_MASK 0x7 + +/* LEFT_TIME_SLOT (0x1e) */ +#define NAU8825_FS_ERR_CMP_SEL_SFT 14 +#define NAU8825_FS_ERR_CMP_SEL_MASK (0x3 << NAU8825_FS_ERR_CMP_SEL_SFT) +#define NAU8825_DIS_FS_SHORT_DET (1 << 13) +#define NAU8825_TSLOT_L0_MASK 0x3ff +#define NAU8825_TSLOT_R0_MASK 0x3ff + +/* BIQ_CTRL (0x20) */ +#define NAU8825_BIQ_WRT_SFT 4 +#define NAU8825_BIQ_WRT_EN (1 << NAU8825_BIQ_WRT_SFT) +#define NAU8825_BIQ_PATH_SFT 0 +#define NAU8825_BIQ_PATH_MASK (1 << NAU8825_BIQ_PATH_SFT) +#define NAU8825_BIQ_PATH_ADC (0 << NAU8825_BIQ_PATH_SFT) +#define NAU8825_BIQ_PATH_DAC (1 << NAU8825_BIQ_PATH_SFT) + +/* ADC_RATE (0x2b) */ +#define NAU8825_ADC_SINC4_SFT 4 +#define NAU8825_ADC_SINC4_EN (1 << NAU8825_ADC_SINC4_SFT) +#define NAU8825_ADC_SYNC_DOWN_SFT 0 +#define NAU8825_ADC_SYNC_DOWN_MASK 0x3 +#define NAU8825_ADC_SYNC_DOWN_32 0 +#define NAU8825_ADC_SYNC_DOWN_64 1 +#define NAU8825_ADC_SYNC_DOWN_128 2 +#define NAU8825_ADC_SYNC_DOWN_256 3 + +/* DAC_CTRL1 (0x2c) */ +#define NAU8825_DAC_CLIP_OFF (1 << 7) +#define NAU8825_DAC_OVERSAMPLE_SFT 0 +#define NAU8825_DAC_OVERSAMPLE_MASK 0x7 +#define NAU8825_DAC_OVERSAMPLE_64 0 +#define NAU8825_DAC_OVERSAMPLE_256 1 +#define NAU8825_DAC_OVERSAMPLE_128 2 +#define NAU8825_DAC_OVERSAMPLE_32 4 + +/* ADC_DGAIN_CTRL (0x30) */ +#define NAU8825_ADC_DIG_VOL_MASK 0xff + +/* MUTE_CTRL (0x31) */ +#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9) +#define NAU8825_DAC_SOFT_MUTE (1 << 9) + +/* HSVOL_CTRL (0x32) */ +#define NAU8825_HP_MUTE (1 << 15) +#define NAU8825_HP_MUTE_AUTO (1 << 14) +#define NAU8825_HPL_MUTE (1 << 13) +#define NAU8825_HPR_MUTE (1 << 12) +#define NAU8825_HPL_VOL_SFT 6 +#define NAU8825_HPL_VOL_MASK (0x3f << NAU8825_HPL_VOL_SFT) +#define NAU8825_HPR_VOL_SFT 0 +#define NAU8825_HPR_VOL_MASK (0x3f << NAU8825_HPR_VOL_SFT) +#define NAU8825_HP_VOL_MIN 0x36 + +/* DACL_CTRL (0x33) */ +#define NAU8825_DACL_CH_SEL_SFT 9 +#define NAU8825_DACL_CH_SEL_MASK (0x1 << NAU8825_DACL_CH_SEL_SFT) +#define NAU8825_DACL_CH_SEL_L (0x0 << NAU8825_DACL_CH_SEL_SFT) +#define NAU8825_DACL_CH_SEL_R (0x1 << NAU8825_DACL_CH_SEL_SFT) +#define NAU8825_DACL_CH_VOL_MASK 0xff + +/* DACR_CTRL (0x34) */ +#define NAU8825_DACR_CH_SEL_SFT 9 +#define NAU8825_DACR_CH_SEL_MASK (0x1 << NAU8825_DACR_CH_SEL_SFT) +#define NAU8825_DACR_CH_SEL_L (0x0 << NAU8825_DACR_CH_SEL_SFT) +#define NAU8825_DACR_CH_SEL_R (0x1 << NAU8825_DACR_CH_SEL_SFT) +#define NAU8825_DACR_CH_VOL_MASK 0xff + +/* IMM_MODE_CTRL (0x4C) */ +#define NAU8825_IMM_THD_SFT 8 +#define NAU8825_IMM_THD_MASK (0x3f << NAU8825_IMM_THD_SFT) +#define NAU8825_IMM_GEN_VOL_SFT 6 +#define NAU8825_IMM_GEN_VOL_MASK (0x3 << NAU8825_IMM_GEN_VOL_SFT) +#define NAU8825_IMM_GEN_VOL_1_2nd (0x0 << NAU8825_IMM_GEN_VOL_SFT) +#define NAU8825_IMM_GEN_VOL_1_4th (0x1 << NAU8825_IMM_GEN_VOL_SFT) +#define NAU8825_IMM_GEN_VOL_1_8th (0x2 << NAU8825_IMM_GEN_VOL_SFT) +#define NAU8825_IMM_GEN_VOL_1_16th (0x3 << NAU8825_IMM_GEN_VOL_SFT) + +#define NAU8825_IMM_CYC_SFT 4 +#define NAU8825_IMM_CYC_MASK (0x3 << NAU8825_IMM_CYC_SFT) +#define NAU8825_IMM_CYC_1024 (0x0 << NAU8825_IMM_CYC_SFT) +#define NAU8825_IMM_CYC_2048 (0x1 << NAU8825_IMM_CYC_SFT) +#define NAU8825_IMM_CYC_4096 (0x2 << NAU8825_IMM_CYC_SFT) +#define NAU8825_IMM_CYC_8192 (0x3 << NAU8825_IMM_CYC_SFT) +#define NAU8825_IMM_EN (1 << 3) +#define NAU8825_IMM_DAC_SRC_MASK 0x7 +#define NAU8825_IMM_DAC_SRC_BIQ 0x0 +#define NAU8825_IMM_DAC_SRC_DRC 0x1 +#define NAU8825_IMM_DAC_SRC_MIX 0x2 +#define NAU8825_IMM_DAC_SRC_SIN 0x3 + +/* CLASSG_CTRL (0x50) */ +#define NAU8825_CLASSG_TIMER_SFT 8 +#define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT) +#define NAU8825_CLASSG_TIMER_1ms (0x1 << NAU8825_CLASSG_TIMER_SFT) +#define NAU8825_CLASSG_TIMER_2ms (0x2 << NAU8825_CLASSG_TIMER_SFT) +#define NAU8825_CLASSG_TIMER_8ms (0x4 << NAU8825_CLASSG_TIMER_SFT) +#define NAU8825_CLASSG_TIMER_16ms (0x8 << NAU8825_CLASSG_TIMER_SFT) +#define NAU8825_CLASSG_TIMER_32ms (0x10 << NAU8825_CLASSG_TIMER_SFT) +#define NAU8825_CLASSG_TIMER_64ms (0x20 << NAU8825_CLASSG_TIMER_SFT) +#define NAU8825_CLASSG_LDAC_EN (0x1 << 2) +#define NAU8825_CLASSG_RDAC_EN (0x1 << 1) +#define NAU8825_CLASSG_EN (1 << 0) + +/* I2C_DEVICE_ID (0x58) */ +#define NAU8825_GPIO2JD1 (1 << 7) +#define NAU8825_SOFTWARE_ID_MASK 0x3 +#define NAU8825_SOFTWARE_ID_NAU8825 0x0 +#define NAU8825_SOFTWARE_ID_NAU8825C 0x1 + +/* BIAS_ADJ (0x66) */ +#define NAU8825_BIAS_HPR_IMP (1 << 15) +#define NAU8825_BIAS_HPL_IMP (1 << 14) +#define NAU8825_BIAS_TESTDAC_SFT 8 +#define NAU8825_BIAS_TESTDAC_EN (0x3 << NAU8825_BIAS_TESTDAC_SFT) +#define NAU8825_BIAS_TESTDACR_EN (0x2 << NAU8825_BIAS_TESTDAC_SFT) +#define NAU8825_BIAS_TESTDACL_EN (0x1 << NAU8825_BIAS_TESTDAC_SFT) +#define NAU8825_BIAS_VMID (1 << 6) +#define NAU8825_BIAS_VMID_SEL_SFT 4 +#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT) + +/* ANALOG_CONTROL_1 (0x69) */ +#define NAU8825_TESTDACIN_SFT 14 +#define NAU8825_TESTDACIN_MASK (0x3 << NAU8825_TESTDACIN_SFT) +#define NAU8825_TESTDACIN_HIGH (1 << NAU8825_TESTDACIN_SFT) +#define NAU8825_TESTDACIN_LOW (2 << NAU8825_TESTDACIN_SFT) +#define NAU8825_TESTDACIN_GND (3 << NAU8825_TESTDACIN_SFT) + +/* ANALOG_CONTROL_2 (0x6a) */ +#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12) +#define NAU8825_DAC_CAPACITOR_MSB (1 << 1) +#define NAU8825_DAC_CAPACITOR_LSB (1 << 0) + +/* ANALOG_ADC_2 (0x72) */ +#define NAU8825_ADC_VREFSEL_MASK (0x3 << 8) +#define NAU8825_ADC_VREFSEL_ANALOG (0 << 8) +#define NAU8825_ADC_VREFSEL_VMID (1 << 8) +#define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB (2 << 8) +#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8) +#define NAU8825_POWERUP_ADCL (1 << 6) + +/* RDAC (0x73) */ +#define NAU8825_RDAC_FS_BCLK_ENB (1 << 15) +#define NAU8825_RDAC_EN_SFT 12 +#define NAU8825_RDAC_EN (0x3 << NAU8825_RDAC_EN_SFT) +#define NAU8825_RDAC_CLK_EN_SFT 8 +#define NAU8825_RDAC_CLK_EN (0x3 << NAU8825_RDAC_CLK_EN_SFT) +#define NAU8825_RDAC_CLK_DELAY_SFT 4 +#define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT) +#define NAU8825_RDAC_VREF_SFT 2 +#define NAU8825_RDAC_VREF_MASK (0x3 << NAU8825_RDAC_VREF_SFT) + +/* MIC_BIAS (0x74) */ +#define NAU8825_MICBIAS_JKSLV (1 << 14) +#define NAU8825_MICBIAS_JKR2 (1 << 12) +#define NAU8825_MICBIAS_LOWNOISE_SFT 10 +#define NAU8825_MICBIAS_LOWNOISE_MASK (0x1 << NAU8825_MICBIAS_LOWNOISE_SFT) +#define NAU8825_MICBIAS_LOWNOISE_EN (0x1 << NAU8825_MICBIAS_LOWNOISE_SFT) +#define NAU8825_MICBIAS_POWERUP_SFT 8 +#define NAU8825_MICBIAS_VOLTAGE_SFT 0 +#define NAU8825_MICBIAS_VOLTAGE_MASK 0x7 + +/* BOOST (0x76) */ +#define NAU8825_PRECHARGE_DIS (1 << 13) +#define NAU8825_GLOBAL_BIAS_EN (1 << 12) +#define NAU8825_DISCHRG_EN (1 << 11) +#define NAU8825_HP_BOOST_DIS (1 << 9) +#define NAU8825_HP_BOOST_G_DIS (1 << 8) +#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) + +/* FEPGA (0x77) */ +#define NAU8825_ACDC_CTRL_SFT 14 +#define NAU8825_ACDC_CTRL_MASK (0x3 << NAU8825_ACDC_CTRL_SFT) +#define NAU8825_ACDC_VREF_MICP (0x1 << NAU8825_ACDC_CTRL_SFT) +#define NAU8825_ACDC_VREF_MICN (0x2 << NAU8825_ACDC_CTRL_SFT) + +/* POWER_UP_CONTROL (0x7f) */ +#define NAU8825_POWERUP_INTEGR_R (1 << 5) +#define NAU8825_POWERUP_INTEGR_L (1 << 4) +#define NAU8825_POWERUP_DRV_IN_R (1 << 3) +#define NAU8825_POWERUP_DRV_IN_L (1 << 2) +#define NAU8825_POWERUP_HP_DRV_R (1 << 1) +#define NAU8825_POWERUP_HP_DRV_L (1 << 0) + +/* CHARGE_PUMP (0x80) */ +#define NAU8825_ADCOUT_DS_SFT 12 +#define NAU8825_ADCOUT_DS_MASK (1 << NAU8825_ADCOUT_DS_SFT) +#define NAU8825_JAMNODCLOW (1 << 10) +#define NAU8825_POWER_DOWN_DACR (1 << 9) +#define NAU8825_POWER_DOWN_DACL (1 << 8) +#define NAU8825_CHANRGE_PUMP_EN (1 << 5) + + +/* System Clock Source */ +enum { + NAU8825_CLK_DIS = 0, + NAU8825_CLK_MCLK, + NAU8825_CLK_INTERNAL, + NAU8825_CLK_FLL_MCLK, + NAU8825_CLK_FLL_BLK, + NAU8825_CLK_FLL_FS, +}; + +/* Cross talk detection state */ +enum { + NAU8825_XTALK_PREPARE = 0, + NAU8825_XTALK_HPR_R2L, + NAU8825_XTALK_HPL_R2L, + NAU8825_XTALK_IMM, + NAU8825_XTALK_DONE, +}; + +struct nau8825 { + struct device *dev; + struct regmap *regmap; + struct snd_soc_dapm_context *dapm; + struct snd_soc_jack *jack; + struct clk *mclk; + struct work_struct xtalk_work; + struct semaphore xtalk_sem; + int sw_id; + int irq; + int mclk_freq; /* 0 - mclk is disabled */ + int button_pressed; + int micbias_voltage; + int vref_impedance; + bool jkdet_enable; + bool jkdet_pull_enable; + bool jkdet_pull_up; + int jkdet_polarity; + int sar_threshold_num; + int sar_threshold[8]; + int sar_hysteresis; + int sar_voltage; + int sar_compare_time; + int sar_sampling_time; + int key_debounce; + int jack_insert_debounce; + int jack_eject_debounce; + int high_imped; + int xtalk_state; + int xtalk_event; + int xtalk_event_mask; + bool xtalk_protect; + int imp_rms[NAU8825_XTALK_IMM]; + int xtalk_enable; + bool xtalk_baktab_initialized; /* True if initialized. */ + bool adcout_ds; + int adc_delay; +}; + +int nau8825_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack); + + +#endif /* __NAU8825_H__ */ diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c new file mode 100644 index 0000000000..735e1942b5 --- /dev/null +++ b/sound/soc/codecs/pcm1681.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PCM1681 ASoC codec driver + * + * Copyright (c) StreamUnlimited GmbH 2013 + * Marek Belisko + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCM1681_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define PCM1681_PCM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define PCM1681_SOFT_MUTE_ALL 0xff +#define PCM1681_DEEMPH_RATE_MASK 0x18 +#define PCM1681_DEEMPH_MASK 0x01 + +#define PCM1681_ATT_CONTROL(X) (X <= 6 ? X : X + 9) /* Attenuation level */ +#define PCM1681_SOFT_MUTE 0x07 /* Soft mute control register */ +#define PCM1681_DAC_CONTROL 0x08 /* DAC operation control */ +#define PCM1681_FMT_CONTROL 0x09 /* Audio interface data format */ +#define PCM1681_DEEMPH_CONTROL 0x0a /* De-emphasis control */ +#define PCM1681_ZERO_DETECT_STATUS 0x0e /* Zero detect status reg */ + +static const struct reg_default pcm1681_reg_defaults[] = { + { 0x01, 0xff }, + { 0x02, 0xff }, + { 0x03, 0xff }, + { 0x04, 0xff }, + { 0x05, 0xff }, + { 0x06, 0xff }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x06 }, + { 0x0A, 0x00 }, + { 0x0B, 0xff }, + { 0x0C, 0x0f }, + { 0x0D, 0x00 }, + { 0x10, 0xff }, + { 0x11, 0xff }, + { 0x12, 0x00 }, + { 0x13, 0x00 }, +}; + +static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg) +{ + return !((reg == 0x00) || (reg == 0x0f)); +} + +static bool pcm1681_writeable_reg(struct device *dev, unsigned int reg) +{ + return pcm1681_accessible_reg(dev, reg) && + (reg != PCM1681_ZERO_DETECT_STATUS); +} + +struct pcm1681_private { + struct regmap *regmap; + unsigned int format; + /* Current deemphasis status */ + unsigned int deemph; + /* Current rate for deemphasis control */ + unsigned int rate; +}; + +static const int pcm1681_deemph[] = { 44100, 48000, 32000 }; + +static int pcm1681_set_deemph(struct snd_soc_component *component) +{ + struct pcm1681_private *priv = snd_soc_component_get_drvdata(component); + int i, val = -1, enable = 0; + + if (priv->deemph) { + for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++) { + if (pcm1681_deemph[i] == priv->rate) { + val = i; + break; + } + } + } + + if (val != -1) { + regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL, + PCM1681_DEEMPH_RATE_MASK, val << 3); + enable = 1; + } else { + enable = 0; + } + + /* enable/disable deemphasis functionality */ + return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL, + PCM1681_DEEMPH_MASK, enable); +} + +static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm1681_private *priv = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = priv->deemph; + + return 0; +} + +static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm1681_private *priv = snd_soc_component_get_drvdata(component); + + priv->deemph = ucontrol->value.integer.value[0]; + + return pcm1681_set_deemph(component); +} + +static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct pcm1681_private *priv = snd_soc_component_get_drvdata(component); + + /* The PCM1681 can only be consumer to all clocks */ + if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { + dev_err(component->dev, "Invalid clocking mode\n"); + return -EINVAL; + } + + priv->format = format; + + return 0; +} + +static int pcm1681_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct pcm1681_private *priv = snd_soc_component_get_drvdata(component); + int val; + + if (mute) + val = PCM1681_SOFT_MUTE_ALL; + else + val = 0; + + return regmap_write(priv->regmap, PCM1681_SOFT_MUTE, val); +} + +static int pcm1681_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm1681_private *priv = snd_soc_component_get_drvdata(component); + int val = 0, ret; + + priv->rate = params_rate(params); + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + switch (params_width(params)) { + case 24: + val = 0; + break; + case 16: + val = 3; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_I2S: + val = 0x04; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = 0x05; + break; + default: + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + ret = regmap_update_bits(priv->regmap, PCM1681_FMT_CONTROL, 0x0f, val); + if (ret < 0) + return ret; + + return pcm1681_set_deemph(component); +} + +static const struct snd_soc_dai_ops pcm1681_dai_ops = { + .set_fmt = pcm1681_set_dai_fmt, + .hw_params = pcm1681_hw_params, + .mute_stream = pcm1681_mute, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("VOUT1"), +SND_SOC_DAPM_OUTPUT("VOUT2"), +SND_SOC_DAPM_OUTPUT("VOUT3"), +SND_SOC_DAPM_OUTPUT("VOUT4"), +SND_SOC_DAPM_OUTPUT("VOUT5"), +SND_SOC_DAPM_OUTPUT("VOUT6"), +SND_SOC_DAPM_OUTPUT("VOUT7"), +SND_SOC_DAPM_OUTPUT("VOUT8"), +}; + +static const struct snd_soc_dapm_route pcm1681_dapm_routes[] = { + { "VOUT1", NULL, "Playback" }, + { "VOUT2", NULL, "Playback" }, + { "VOUT3", NULL, "Playback" }, + { "VOUT4", NULL, "Playback" }, + { "VOUT5", NULL, "Playback" }, + { "VOUT6", NULL, "Playback" }, + { "VOUT7", NULL, "Playback" }, + { "VOUT8", NULL, "Playback" }, +}; + +static const DECLARE_TLV_DB_SCALE(pcm1681_dac_tlv, -6350, 50, 1); + +static const struct snd_kcontrol_new pcm1681_controls[] = { + SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume", + PCM1681_ATT_CONTROL(1), PCM1681_ATT_CONTROL(2), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume", + PCM1681_ATT_CONTROL(3), PCM1681_ATT_CONTROL(4), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume", + PCM1681_ATT_CONTROL(5), PCM1681_ATT_CONTROL(6), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 7/8 Playback Volume", + PCM1681_ATT_CONTROL(7), PCM1681_ATT_CONTROL(8), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + pcm1681_get_deemph, pcm1681_put_deemph), +}; + +static struct snd_soc_dai_driver pcm1681_dai = { + .name = "pcm1681-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = PCM1681_PCM_RATES, + .formats = PCM1681_PCM_FORMATS, + }, + .ops = &pcm1681_dai_ops, +}; + +#ifdef CONFIG_OF +static const struct of_device_id pcm1681_dt_ids[] = { + { .compatible = "ti,pcm1681", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm1681_dt_ids); +#endif + +static const struct regmap_config pcm1681_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x13, + .reg_defaults = pcm1681_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm1681_reg_defaults), + .writeable_reg = pcm1681_writeable_reg, + .readable_reg = pcm1681_accessible_reg, +}; + +static const struct snd_soc_component_driver soc_component_dev_pcm1681 = { + .controls = pcm1681_controls, + .num_controls = ARRAY_SIZE(pcm1681_controls), + .dapm_widgets = pcm1681_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets), + .dapm_routes = pcm1681_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct i2c_device_id pcm1681_i2c_id[] = { + {"pcm1681", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id); + +static int pcm1681_i2c_probe(struct i2c_client *client) +{ + int ret; + struct pcm1681_private *priv; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&client->dev, "Failed to create regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(client, priv); + + return devm_snd_soc_register_component(&client->dev, + &soc_component_dev_pcm1681, + &pcm1681_dai, 1); +} + +static struct i2c_driver pcm1681_i2c_driver = { + .driver = { + .name = "pcm1681", + .of_match_table = of_match_ptr(pcm1681_dt_ids), + }, + .id_table = pcm1681_i2c_id, + .probe = pcm1681_i2c_probe, +}; + +module_i2c_driver(pcm1681_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments PCM1681 ALSA SoC Codec Driver"); +MODULE_AUTHOR("Marek Belisko "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c new file mode 100644 index 0000000000..f2d0b4d21e --- /dev/null +++ b/sound/soc/codecs/pcm1789-i2c.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +// Audio driver for PCM1789 I2C +// Copyright (C) 2018 Bootlin +// Mylène Josserand + +#include +#include +#include +#include +#include +#include + +#include "pcm1789.h" + +static int pcm1789_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_i2c(client, &pcm1789_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + return pcm1789_common_init(&client->dev, regmap); +} + +static void pcm1789_i2c_remove(struct i2c_client *client) +{ + pcm1789_common_exit(&client->dev); +} + +#ifdef CONFIG_OF +static const struct of_device_id pcm1789_of_match[] = { + { .compatible = "ti,pcm1789", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm1789_of_match); +#endif + +static const struct i2c_device_id pcm1789_i2c_ids[] = { + { "pcm1789", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm1789_i2c_ids); + +static struct i2c_driver pcm1789_i2c_driver = { + .driver = { + .name = "pcm1789", + .of_match_table = of_match_ptr(pcm1789_of_match), + }, + .id_table = pcm1789_i2c_ids, + .probe = pcm1789_i2c_probe, + .remove = pcm1789_i2c_remove, +}; + +module_i2c_driver(pcm1789_i2c_driver); + +MODULE_DESCRIPTION("ASoC PCM1789 I2C driver"); +MODULE_AUTHOR("Mylène Josserand "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c new file mode 100644 index 0000000000..3ab381e9a8 --- /dev/null +++ b/sound/soc/codecs/pcm1789.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0 +// Audio driver for PCM1789 +// Copyright (C) 2018 Bootlin +// Mylène Josserand + +#include +#include +#include + +#include +#include +#include + +#include "pcm1789.h" + +#define PCM1789_MUTE_CONTROL 0x10 +#define PCM1789_FMT_CONTROL 0x11 +#define PCM1789_SOFT_MUTE 0x14 +#define PCM1789_DAC_VOL_LEFT 0x18 +#define PCM1789_DAC_VOL_RIGHT 0x19 + +#define PCM1789_FMT_MASK 0x07 +#define PCM1789_MUTE_MASK 0x03 +#define PCM1789_MUTE_SRET 0x06 + +struct pcm1789_private { + struct regmap *regmap; + unsigned int format; + unsigned int rate; + struct gpio_desc *reset; + struct work_struct work; + struct device *dev; +}; + +static const struct reg_default pcm1789_reg_defaults[] = { + { PCM1789_FMT_CONTROL, 0x00 }, + { PCM1789_SOFT_MUTE, 0x00 }, + { PCM1789_DAC_VOL_LEFT, 0xff }, + { PCM1789_DAC_VOL_RIGHT, 0xff }, +}; + +static bool pcm1789_accessible_reg(struct device *dev, unsigned int reg) +{ + return reg >= PCM1789_MUTE_CONTROL && reg <= PCM1789_DAC_VOL_RIGHT; +} + +static bool pcm1789_writeable_reg(struct device *dev, unsigned int reg) +{ + return pcm1789_accessible_reg(dev, reg); +} + +static int pcm1789_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); + + priv->format = format; + + return 0; +} + +static int pcm1789_mute(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); + + return regmap_update_bits(priv->regmap, PCM1789_SOFT_MUTE, + PCM1789_MUTE_MASK, + mute ? 0 : PCM1789_MUTE_MASK); +} + +static int pcm1789_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_component *component = codec_dai->component; + struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); + int val = 0, ret; + + priv->rate = params_rate(params); + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + switch (params_width(params)) { + case 24: + val = 2; + break; + case 16: + val = 3; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_I2S: + switch (params_width(params)) { + case 16: + case 24: + case 32: + val = 0; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_LEFT_J: + switch (params_width(params)) { + case 16: + case 24: + case 32: + val = 1; + break; + default: + return -EINVAL; + } + break; + default: + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + ret = regmap_update_bits(priv->regmap, PCM1789_FMT_CONTROL, + PCM1789_FMT_MASK, val); + if (ret < 0) + return ret; + + return 0; +} + +static void pcm1789_work_queue(struct work_struct *work) +{ + struct pcm1789_private *priv = container_of(work, + struct pcm1789_private, + work); + + /* Perform a software reset to remove codec from desynchronized state */ + if (regmap_update_bits(priv->regmap, PCM1789_MUTE_CONTROL, + 0x3 << PCM1789_MUTE_SRET, 0) < 0) + dev_err(priv->dev, "Error while setting SRET"); +} + +static int pcm1789_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm1789_private *priv = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + schedule_work(&priv->work); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct snd_soc_dai_ops pcm1789_dai_ops = { + .set_fmt = pcm1789_set_dai_fmt, + .hw_params = pcm1789_hw_params, + .mute_stream = pcm1789_mute, + .trigger = pcm1789_trigger, + .no_capture_mute = 1, +}; + +static const DECLARE_TLV_DB_SCALE(pcm1789_dac_tlv, -12000, 50, 1); + +static const struct snd_kcontrol_new pcm1789_controls[] = { + SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1789_DAC_VOL_LEFT, + PCM1789_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0, + pcm1789_dac_tlv), +}; + +static const struct snd_soc_dapm_widget pcm1789_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("IOUTL+"), + SND_SOC_DAPM_OUTPUT("IOUTL-"), + SND_SOC_DAPM_OUTPUT("IOUTR+"), + SND_SOC_DAPM_OUTPUT("IOUTR-"), +}; + +static const struct snd_soc_dapm_route pcm1789_dapm_routes[] = { + { "IOUTL+", NULL, "Playback" }, + { "IOUTL-", NULL, "Playback" }, + { "IOUTR+", NULL, "Playback" }, + { "IOUTR-", NULL, "Playback" }, +}; + +static struct snd_soc_dai_driver pcm1789_dai = { + .name = "pcm1789-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 10000, + .rate_max = 200000, + .formats = PCM1789_FORMATS, + }, + .ops = &pcm1789_dai_ops, +}; + +const struct regmap_config pcm1789_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PCM1789_DAC_VOL_RIGHT, + .reg_defaults = pcm1789_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm1789_reg_defaults), + .writeable_reg = pcm1789_writeable_reg, + .readable_reg = pcm1789_accessible_reg, +}; +EXPORT_SYMBOL_GPL(pcm1789_regmap_config); + +static const struct snd_soc_component_driver soc_component_dev_pcm1789 = { + .controls = pcm1789_controls, + .num_controls = ARRAY_SIZE(pcm1789_controls), + .dapm_widgets = pcm1789_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1789_dapm_widgets), + .dapm_routes = pcm1789_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1789_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +int pcm1789_common_init(struct device *dev, struct regmap *regmap) +{ + struct pcm1789_private *pcm1789; + + pcm1789 = devm_kzalloc(dev, sizeof(struct pcm1789_private), + GFP_KERNEL); + if (!pcm1789) + return -ENOMEM; + + pcm1789->regmap = regmap; + pcm1789->dev = dev; + dev_set_drvdata(dev, pcm1789); + + pcm1789->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(pcm1789->reset)) + return PTR_ERR(pcm1789->reset); + + gpiod_set_value_cansleep(pcm1789->reset, 0); + msleep(300); + + INIT_WORK(&pcm1789->work, pcm1789_work_queue); + + return devm_snd_soc_register_component(dev, &soc_component_dev_pcm1789, + &pcm1789_dai, 1); +} +EXPORT_SYMBOL_GPL(pcm1789_common_init); + +void pcm1789_common_exit(struct device *dev) +{ + struct pcm1789_private *priv = dev_get_drvdata(dev); + + flush_work(&priv->work); +} +EXPORT_SYMBOL_GPL(pcm1789_common_exit); + +MODULE_DESCRIPTION("ASoC PCM1789 driver"); +MODULE_AUTHOR("Mylène Josserand "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm1789.h b/sound/soc/codecs/pcm1789.h new file mode 100644 index 0000000000..79439c8322 --- /dev/null +++ b/sound/soc/codecs/pcm1789.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 +// Definitions for PCM1789 audio driver +// Copyright (C) 2018 Bootlin +// Mylène Josserand + +#ifndef __PCM1789_H__ +#define __PCM1789_H__ + +#define PCM1789_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S16_LE) + +extern const struct regmap_config pcm1789_regmap_config; + +int pcm1789_common_init(struct device *dev, struct regmap *regmap); +void pcm1789_common_exit(struct device *dev); + +#endif diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c new file mode 100644 index 0000000000..10579681f4 --- /dev/null +++ b/sound/soc/codecs/pcm179x-i2c.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PCM179X ASoC I2C driver + * + * Copyright (c) Teenage Engineering AB 2016 + * + * Jacob Siverskog + */ + +#include +#include +#include +#include + +#include "pcm179x.h" + +static int pcm179x_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_i2c(client, &pcm179x_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + return pcm179x_common_init(&client->dev, regmap); +} + +#ifdef CONFIG_OF +static const struct of_device_id pcm179x_of_match[] = { + { .compatible = "ti,pcm1792a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm179x_of_match); +#endif + +static const struct i2c_device_id pcm179x_i2c_ids[] = { + { "pcm179x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids); + +static struct i2c_driver pcm179x_i2c_driver = { + .driver = { + .name = "pcm179x", + .of_match_table = of_match_ptr(pcm179x_of_match), + }, + .id_table = pcm179x_i2c_ids, + .probe = pcm179x_i2c_probe, +}; + +module_i2c_driver(pcm179x_i2c_driver); + +MODULE_DESCRIPTION("ASoC PCM179X I2C driver"); +MODULE_AUTHOR("Jacob Siverskog "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c new file mode 100644 index 0000000000..192fee90c9 --- /dev/null +++ b/sound/soc/codecs/pcm179x-spi.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PCM179X ASoC SPI driver + * + * Copyright (c) Amarula Solutions B.V. 2013 + * + * Michael Trimarchi + */ + +#include +#include +#include +#include + +#include "pcm179x.h" + +static int pcm179x_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_spi(spi, &pcm179x_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&spi->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + return pcm179x_common_init(&spi->dev, regmap); +} + +static const struct of_device_id pcm179x_of_match[] __maybe_unused = { + { .compatible = "ti,pcm1792a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm179x_of_match); + +static const struct spi_device_id pcm179x_spi_ids[] = { + { "pcm1792a", 0 }, + { "pcm179x", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids); + +static struct spi_driver pcm179x_spi_driver = { + .driver = { + .name = "pcm179x", + .of_match_table = of_match_ptr(pcm179x_of_match), + }, + .id_table = pcm179x_spi_ids, + .probe = pcm179x_spi_probe, +}; + +module_spi_driver(pcm179x_spi_driver); + +MODULE_DESCRIPTION("ASoC PCM179X SPI driver"); +MODULE_AUTHOR("Michael Trimarchi "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c new file mode 100644 index 0000000000..f52ff66b6e --- /dev/null +++ b/sound/soc/codecs/pcm179x.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PCM179X ASoC codec driver + * + * Copyright (c) Amarula Solutions B.V. 2013 + * + * Michael Trimarchi + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pcm179x.h" + +#define PCM179X_DAC_VOL_LEFT 0x10 +#define PCM179X_DAC_VOL_RIGHT 0x11 +#define PCM179X_FMT_CONTROL 0x12 +#define PCM179X_MODE_CONTROL 0x13 +#define PCM179X_SOFT_MUTE PCM179X_FMT_CONTROL + +#define PCM179X_FMT_MASK 0x70 +#define PCM179X_FMT_SHIFT 4 +#define PCM179X_MUTE_MASK 0x01 +#define PCM179X_MUTE_SHIFT 0 +#define PCM179X_ATLD_ENABLE (1 << 7) + +static const struct reg_default pcm179x_reg_defaults[] = { + { 0x10, 0xff }, + { 0x11, 0xff }, + { 0x12, 0x50 }, + { 0x13, 0x00 }, + { 0x14, 0x00 }, + { 0x15, 0x01 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, +}; + +static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg) +{ + return reg >= 0x10 && reg <= 0x17; +} + +static bool pcm179x_writeable_reg(struct device *dev, unsigned int reg) +{ + bool accessible; + + accessible = pcm179x_accessible_reg(dev, reg); + + return accessible && reg != 0x16 && reg != 0x17; +} + +struct pcm179x_private { + struct regmap *regmap; + unsigned int format; + unsigned int rate; +}; + +static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct pcm179x_private *priv = snd_soc_component_get_drvdata(component); + + priv->format = format; + + return 0; +} + +static int pcm179x_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct pcm179x_private *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = regmap_update_bits(priv->regmap, PCM179X_SOFT_MUTE, + PCM179X_MUTE_MASK, !!mute); + if (ret < 0) + return ret; + + return 0; +} + +static int pcm179x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm179x_private *priv = snd_soc_component_get_drvdata(component); + int val = 0, ret; + + priv->rate = params_rate(params); + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + switch (params_width(params)) { + case 24: + case 32: + val = 2; + break; + case 16: + val = 0; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_I2S: + switch (params_width(params)) { + case 24: + case 32: + val = 5; + break; + case 16: + val = 4; + break; + default: + return -EINVAL; + } + break; + default: + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + val = val << PCM179X_FMT_SHIFT | PCM179X_ATLD_ENABLE; + + ret = regmap_update_bits(priv->regmap, PCM179X_FMT_CONTROL, + PCM179X_FMT_MASK | PCM179X_ATLD_ENABLE, val); + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_soc_dai_ops pcm179x_dai_ops = { + .set_fmt = pcm179x_set_dai_fmt, + .hw_params = pcm179x_hw_params, + .mute_stream = pcm179x_mute, + .no_capture_mute = 1, +}; + +static const DECLARE_TLV_DB_SCALE(pcm179x_dac_tlv, -12000, 50, 1); + +static const struct snd_kcontrol_new pcm179x_controls[] = { + SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM179X_DAC_VOL_LEFT, + PCM179X_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0, + pcm179x_dac_tlv), + SOC_SINGLE("DAC Invert Output Switch", PCM179X_MODE_CONTROL, 7, 1, 0), + SOC_SINGLE("DAC Rolloff Filter Switch", PCM179X_MODE_CONTROL, 1, 1, 0), +}; + +static const struct snd_soc_dapm_widget pcm179x_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("IOUTL+"), +SND_SOC_DAPM_OUTPUT("IOUTL-"), +SND_SOC_DAPM_OUTPUT("IOUTR+"), +SND_SOC_DAPM_OUTPUT("IOUTR-"), +}; + +static const struct snd_soc_dapm_route pcm179x_dapm_routes[] = { + { "IOUTL+", NULL, "Playback" }, + { "IOUTL-", NULL, "Playback" }, + { "IOUTR+", NULL, "Playback" }, + { "IOUTR-", NULL, "Playback" }, +}; + +static struct snd_soc_dai_driver pcm179x_dai = { + .name = "pcm179x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 10000, + .rate_max = 200000, + .formats = PCM1792A_FORMATS, }, + .ops = &pcm179x_dai_ops, +}; + +const struct regmap_config pcm179x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 23, + .reg_defaults = pcm179x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm179x_reg_defaults), + .writeable_reg = pcm179x_writeable_reg, + .readable_reg = pcm179x_accessible_reg, +}; +EXPORT_SYMBOL_GPL(pcm179x_regmap_config); + +static const struct snd_soc_component_driver soc_component_dev_pcm179x = { + .controls = pcm179x_controls, + .num_controls = ARRAY_SIZE(pcm179x_controls), + .dapm_widgets = pcm179x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm179x_dapm_widgets), + .dapm_routes = pcm179x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm179x_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +int pcm179x_common_init(struct device *dev, struct regmap *regmap) +{ + struct pcm179x_private *pcm179x; + + pcm179x = devm_kzalloc(dev, sizeof(struct pcm179x_private), + GFP_KERNEL); + if (!pcm179x) + return -ENOMEM; + + pcm179x->regmap = regmap; + dev_set_drvdata(dev, pcm179x); + + return devm_snd_soc_register_component(dev, + &soc_component_dev_pcm179x, &pcm179x_dai, 1); +} +EXPORT_SYMBOL_GPL(pcm179x_common_init); + +MODULE_DESCRIPTION("ASoC PCM179X driver"); +MODULE_AUTHOR("Michael Trimarchi "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm179x.h b/sound/soc/codecs/pcm179x.h new file mode 100644 index 0000000000..0039ca8ee7 --- /dev/null +++ b/sound/soc/codecs/pcm179x.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * definitions for PCM179X + * + * Copyright 2013 Amarula Solutions + */ + +#ifndef __PCM179X_H__ +#define __PCM179X_H__ + +#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S16_LE) + +extern const struct regmap_config pcm179x_regmap_config; + +int pcm179x_common_init(struct device *dev, struct regmap *regmap); + +#endif diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c new file mode 100644 index 0000000000..a514ebd1b6 --- /dev/null +++ b/sound/soc/codecs/pcm186x-i2c.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments PCM186x Universal Audio ADC - I2C + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com + * Andreas Dannenberg + * Andrew F. Davis + */ + +#include +#include +#include + +#include "pcm186x.h" + +static const struct of_device_id pcm186x_of_match[] = { + { .compatible = "ti,pcm1862", .data = (void *)PCM1862 }, + { .compatible = "ti,pcm1863", .data = (void *)PCM1863 }, + { .compatible = "ti,pcm1864", .data = (void *)PCM1864 }, + { .compatible = "ti,pcm1865", .data = (void *)PCM1865 }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm186x_of_match); + +static const struct i2c_device_id pcm186x_i2c_id[] = { + { "pcm1862", PCM1862 }, + { "pcm1863", PCM1863 }, + { "pcm1864", PCM1864 }, + { "pcm1865", PCM1865 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id); + +static int pcm186x_i2c_probe(struct i2c_client *i2c) +{ + const struct i2c_device_id *id = i2c_match_id(pcm186x_i2c_id, i2c); + const enum pcm186x_type type = (enum pcm186x_type)id->driver_data; + int irq = i2c->irq; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &pcm186x_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm186x_probe(&i2c->dev, type, irq, regmap); +} + +static struct i2c_driver pcm186x_i2c_driver = { + .probe = pcm186x_i2c_probe, + .id_table = pcm186x_i2c_id, + .driver = { + .name = "pcm186x", + .of_match_table = pcm186x_of_match, + }, +}; +module_i2c_driver(pcm186x_i2c_driver); + +MODULE_AUTHOR("Andreas Dannenberg "); +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("PCM186x Universal Audio ADC I2C Interface Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm186x-spi.c b/sound/soc/codecs/pcm186x-spi.c new file mode 100644 index 0000000000..bc1b0f0698 --- /dev/null +++ b/sound/soc/codecs/pcm186x-spi.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments PCM186x Universal Audio ADC - SPI + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com + * Andreas Dannenberg + * Andrew F. Davis + */ + +#include +#include +#include + +#include "pcm186x.h" + +static const struct of_device_id pcm186x_of_match[] = { + { .compatible = "ti,pcm1862", .data = (void *)PCM1862 }, + { .compatible = "ti,pcm1863", .data = (void *)PCM1863 }, + { .compatible = "ti,pcm1864", .data = (void *)PCM1864 }, + { .compatible = "ti,pcm1865", .data = (void *)PCM1865 }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm186x_of_match); + +static int pcm186x_spi_probe(struct spi_device *spi) +{ + const enum pcm186x_type type = + (enum pcm186x_type)spi_get_device_id(spi)->driver_data; + int irq = spi->irq; + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &pcm186x_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm186x_probe(&spi->dev, type, irq, regmap); +} + +static const struct spi_device_id pcm186x_spi_id[] = { + { "pcm1862", PCM1862 }, + { "pcm1863", PCM1863 }, + { "pcm1864", PCM1864 }, + { "pcm1865", PCM1865 }, + { } +}; +MODULE_DEVICE_TABLE(spi, pcm186x_spi_id); + +static struct spi_driver pcm186x_spi_driver = { + .probe = pcm186x_spi_probe, + .id_table = pcm186x_spi_id, + .driver = { + .name = "pcm186x", + .of_match_table = pcm186x_of_match, + }, +}; +module_spi_driver(pcm186x_spi_driver); + +MODULE_AUTHOR("Andreas Dannenberg "); +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("PCM186x Universal Audio ADC SPI Interface Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c new file mode 100644 index 0000000000..451a8fd8fa --- /dev/null +++ b/sound/soc/codecs/pcm186x.c @@ -0,0 +1,704 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments PCM186x Universal Audio ADC + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com + * Andreas Dannenberg + * Andrew F. Davis + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcm186x.h" + +static const char * const pcm186x_supply_names[] = { + "avdd", /* Analog power supply. Connect to 3.3-V supply. */ + "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ + "iovdd", /* I/O power supply. Connect to 3.3-V or 1.8-V. */ +}; +#define PCM186x_NUM_SUPPLIES ARRAY_SIZE(pcm186x_supply_names) + +struct pcm186x_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[PCM186x_NUM_SUPPLIES]; + unsigned int sysclk; + unsigned int tdm_offset; + bool is_tdm_mode; + bool is_provider_mode; +}; + +static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 50, 0); + +static const struct snd_kcontrol_new pcm1863_snd_controls[] = { + SOC_DOUBLE_R_S_TLV("ADC Capture Volume", PCM186X_PGA_VAL_CH1_L, + PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0, + pcm186x_pga_tlv), +}; + +static const struct snd_kcontrol_new pcm1865_snd_controls[] = { + SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", PCM186X_PGA_VAL_CH1_L, + PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0, + pcm186x_pga_tlv), + SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", PCM186X_PGA_VAL_CH2_L, + PCM186X_PGA_VAL_CH2_R, 0, -24, 80, 7, 0, + pcm186x_pga_tlv), +}; + +static const unsigned int pcm186x_adc_input_channel_sel_value[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x20, 0x30 +}; + +static const char * const pcm186x_adcl_input_channel_sel_text[] = { + "No Select", + "VINL1[SE]", /* Default for ADC1L */ + "VINL2[SE]", /* Default for ADC2L */ + "VINL2[SE] + VINL1[SE]", + "VINL3[SE]", + "VINL3[SE] + VINL1[SE]", + "VINL3[SE] + VINL2[SE]", + "VINL3[SE] + VINL2[SE] + VINL1[SE]", + "VINL4[SE]", + "VINL4[SE] + VINL1[SE]", + "VINL4[SE] + VINL2[SE]", + "VINL4[SE] + VINL2[SE] + VINL1[SE]", + "VINL4[SE] + VINL3[SE]", + "VINL4[SE] + VINL3[SE] + VINL1[SE]", + "VINL4[SE] + VINL3[SE] + VINL2[SE]", + "VINL4[SE] + VINL3[SE] + VINL2[SE] + VINL1[SE]", + "{VIN1P, VIN1M}[DIFF]", + "{VIN4P, VIN4M}[DIFF]", + "{VIN1P, VIN1M}[DIFF] + {VIN4P, VIN4M}[DIFF]" +}; + +static const char * const pcm186x_adcr_input_channel_sel_text[] = { + "No Select", + "VINR1[SE]", /* Default for ADC1R */ + "VINR2[SE]", /* Default for ADC2R */ + "VINR2[SE] + VINR1[SE]", + "VINR3[SE]", + "VINR3[SE] + VINR1[SE]", + "VINR3[SE] + VINR2[SE]", + "VINR3[SE] + VINR2[SE] + VINR1[SE]", + "VINR4[SE]", + "VINR4[SE] + VINR1[SE]", + "VINR4[SE] + VINR2[SE]", + "VINR4[SE] + VINR2[SE] + VINR1[SE]", + "VINR4[SE] + VINR3[SE]", + "VINR4[SE] + VINR3[SE] + VINR1[SE]", + "VINR4[SE] + VINR3[SE] + VINR2[SE]", + "VINR4[SE] + VINR3[SE] + VINR2[SE] + VINR1[SE]", + "{VIN2P, VIN2M}[DIFF]", + "{VIN3P, VIN3M}[DIFF]", + "{VIN2P, VIN2M}[DIFF] + {VIN3P, VIN3M}[DIFF]" +}; + +static const struct soc_enum pcm186x_adc_input_channel_sel[] = { + SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0, + PCM186X_ADC_INPUT_SEL_MASK, + ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text), + pcm186x_adcl_input_channel_sel_text, + pcm186x_adc_input_channel_sel_value), + SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0, + PCM186X_ADC_INPUT_SEL_MASK, + ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text), + pcm186x_adcr_input_channel_sel_text, + pcm186x_adc_input_channel_sel_value), + SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_L, 0, + PCM186X_ADC_INPUT_SEL_MASK, + ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text), + pcm186x_adcl_input_channel_sel_text, + pcm186x_adc_input_channel_sel_value), + SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_R, 0, + PCM186X_ADC_INPUT_SEL_MASK, + ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text), + pcm186x_adcr_input_channel_sel_text, + pcm186x_adc_input_channel_sel_value), +}; + +static const struct snd_kcontrol_new pcm186x_adc_mux_controls[] = { + SOC_DAPM_ENUM("ADC1 Left Input", pcm186x_adc_input_channel_sel[0]), + SOC_DAPM_ENUM("ADC1 Right Input", pcm186x_adc_input_channel_sel[1]), + SOC_DAPM_ENUM("ADC2 Left Input", pcm186x_adc_input_channel_sel[2]), + SOC_DAPM_ENUM("ADC2 Right Input", pcm186x_adc_input_channel_sel[3]), +}; + +static const struct snd_soc_dapm_widget pcm1863_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("VINL1"), + SND_SOC_DAPM_INPUT("VINR1"), + SND_SOC_DAPM_INPUT("VINL2"), + SND_SOC_DAPM_INPUT("VINR2"), + SND_SOC_DAPM_INPUT("VINL3"), + SND_SOC_DAPM_INPUT("VINR3"), + SND_SOC_DAPM_INPUT("VINL4"), + SND_SOC_DAPM_INPUT("VINR4"), + + SND_SOC_DAPM_MUX("ADC Left Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[0]), + SND_SOC_DAPM_MUX("ADC Right Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[1]), + + /* + * Put the codec into SLEEP mode when not in use, allowing the + * Energysense mechanism to operate. + */ + SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1, 1), +}; + +static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("VINL1"), + SND_SOC_DAPM_INPUT("VINR1"), + SND_SOC_DAPM_INPUT("VINL2"), + SND_SOC_DAPM_INPUT("VINR2"), + SND_SOC_DAPM_INPUT("VINL3"), + SND_SOC_DAPM_INPUT("VINR3"), + SND_SOC_DAPM_INPUT("VINL4"), + SND_SOC_DAPM_INPUT("VINR4"), + + SND_SOC_DAPM_MUX("ADC1 Left Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[0]), + SND_SOC_DAPM_MUX("ADC1 Right Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[1]), + SND_SOC_DAPM_MUX("ADC2 Left Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[2]), + SND_SOC_DAPM_MUX("ADC2 Right Capture Source", SND_SOC_NOPM, 0, 0, + &pcm186x_adc_mux_controls[3]), + + /* + * Put the codec into SLEEP mode when not in use, allowing the + * Energysense mechanism to operate. + */ + SND_SOC_DAPM_ADC("ADC1", "HiFi Capture 1", PCM186X_POWER_CTRL, 1, 1), + SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1, 1), +}; + +static const struct snd_soc_dapm_route pcm1863_dapm_routes[] = { + { "ADC Left Capture Source", NULL, "VINL1" }, + { "ADC Left Capture Source", NULL, "VINR1" }, + { "ADC Left Capture Source", NULL, "VINL2" }, + { "ADC Left Capture Source", NULL, "VINR2" }, + { "ADC Left Capture Source", NULL, "VINL3" }, + { "ADC Left Capture Source", NULL, "VINR3" }, + { "ADC Left Capture Source", NULL, "VINL4" }, + { "ADC Left Capture Source", NULL, "VINR4" }, + + { "ADC", NULL, "ADC Left Capture Source" }, + + { "ADC Right Capture Source", NULL, "VINL1" }, + { "ADC Right Capture Source", NULL, "VINR1" }, + { "ADC Right Capture Source", NULL, "VINL2" }, + { "ADC Right Capture Source", NULL, "VINR2" }, + { "ADC Right Capture Source", NULL, "VINL3" }, + { "ADC Right Capture Source", NULL, "VINR3" }, + { "ADC Right Capture Source", NULL, "VINL4" }, + { "ADC Right Capture Source", NULL, "VINR4" }, + + { "ADC", NULL, "ADC Right Capture Source" }, +}; + +static const struct snd_soc_dapm_route pcm1865_dapm_routes[] = { + { "ADC1 Left Capture Source", NULL, "VINL1" }, + { "ADC1 Left Capture Source", NULL, "VINR1" }, + { "ADC1 Left Capture Source", NULL, "VINL2" }, + { "ADC1 Left Capture Source", NULL, "VINR2" }, + { "ADC1 Left Capture Source", NULL, "VINL3" }, + { "ADC1 Left Capture Source", NULL, "VINR3" }, + { "ADC1 Left Capture Source", NULL, "VINL4" }, + { "ADC1 Left Capture Source", NULL, "VINR4" }, + + { "ADC1", NULL, "ADC1 Left Capture Source" }, + + { "ADC1 Right Capture Source", NULL, "VINL1" }, + { "ADC1 Right Capture Source", NULL, "VINR1" }, + { "ADC1 Right Capture Source", NULL, "VINL2" }, + { "ADC1 Right Capture Source", NULL, "VINR2" }, + { "ADC1 Right Capture Source", NULL, "VINL3" }, + { "ADC1 Right Capture Source", NULL, "VINR3" }, + { "ADC1 Right Capture Source", NULL, "VINL4" }, + { "ADC1 Right Capture Source", NULL, "VINR4" }, + + { "ADC1", NULL, "ADC1 Right Capture Source" }, + + { "ADC2 Left Capture Source", NULL, "VINL1" }, + { "ADC2 Left Capture Source", NULL, "VINR1" }, + { "ADC2 Left Capture Source", NULL, "VINL2" }, + { "ADC2 Left Capture Source", NULL, "VINR2" }, + { "ADC2 Left Capture Source", NULL, "VINL3" }, + { "ADC2 Left Capture Source", NULL, "VINR3" }, + { "ADC2 Left Capture Source", NULL, "VINL4" }, + { "ADC2 Left Capture Source", NULL, "VINR4" }, + + { "ADC2", NULL, "ADC2 Left Capture Source" }, + + { "ADC2 Right Capture Source", NULL, "VINL1" }, + { "ADC2 Right Capture Source", NULL, "VINR1" }, + { "ADC2 Right Capture Source", NULL, "VINL2" }, + { "ADC2 Right Capture Source", NULL, "VINR2" }, + { "ADC2 Right Capture Source", NULL, "VINL3" }, + { "ADC2 Right Capture Source", NULL, "VINR3" }, + { "ADC2 Right Capture Source", NULL, "VINL4" }, + { "ADC2 Right Capture Source", NULL, "VINR4" }, + + { "ADC2", NULL, "ADC2 Right Capture Source" }, +}; + +static int pcm186x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int rate = params_rate(params); + snd_pcm_format_t format = params_format(params); + unsigned int width = params_width(params); + unsigned int channels = params_channels(params); + unsigned int div_lrck; + unsigned int div_bck; + u8 tdm_tx_sel = 0; + u8 pcm_cfg = 0; + + dev_dbg(component->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n", + __func__, rate, format, width, channels); + + switch (width) { + case 16: + pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_16 << + PCM186X_PCM_CFG_RX_WLEN_SHIFT | + PCM186X_PCM_CFG_TX_WLEN_16 << + PCM186X_PCM_CFG_TX_WLEN_SHIFT; + break; + case 20: + pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_20 << + PCM186X_PCM_CFG_RX_WLEN_SHIFT | + PCM186X_PCM_CFG_TX_WLEN_20 << + PCM186X_PCM_CFG_TX_WLEN_SHIFT; + break; + case 24: + pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_24 << + PCM186X_PCM_CFG_RX_WLEN_SHIFT | + PCM186X_PCM_CFG_TX_WLEN_24 << + PCM186X_PCM_CFG_TX_WLEN_SHIFT; + break; + case 32: + pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_32 << + PCM186X_PCM_CFG_RX_WLEN_SHIFT | + PCM186X_PCM_CFG_TX_WLEN_32 << + PCM186X_PCM_CFG_TX_WLEN_SHIFT; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, PCM186X_PCM_CFG, + PCM186X_PCM_CFG_RX_WLEN_MASK | + PCM186X_PCM_CFG_TX_WLEN_MASK, + pcm_cfg); + + div_lrck = width * channels; + + if (priv->is_tdm_mode) { + /* Select TDM transmission data */ + switch (channels) { + case 2: + tdm_tx_sel = PCM186X_TDM_TX_SEL_2CH; + break; + case 4: + tdm_tx_sel = PCM186X_TDM_TX_SEL_4CH; + break; + case 6: + tdm_tx_sel = PCM186X_TDM_TX_SEL_6CH; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, PCM186X_TDM_TX_SEL, + PCM186X_TDM_TX_SEL_MASK, tdm_tx_sel); + + /* In DSP/TDM mode, the LRCLK divider must be 256 */ + div_lrck = 256; + + /* Configure 1/256 duty cycle for LRCK */ + snd_soc_component_update_bits(component, PCM186X_PCM_CFG, + PCM186X_PCM_CFG_TDM_LRCK_MODE, + PCM186X_PCM_CFG_TDM_LRCK_MODE); + } + + /* Only configure clock dividers in provider mode. */ + if (priv->is_provider_mode) { + div_bck = priv->sysclk / (div_lrck * rate); + + dev_dbg(component->dev, + "%s() master_clk=%u div_bck=%u div_lrck=%u\n", + __func__, priv->sysclk, div_bck, div_lrck); + + snd_soc_component_write(component, PCM186X_BCK_DIV, div_bck - 1); + snd_soc_component_write(component, PCM186X_LRK_DIV, div_lrck - 1); + } + + return 0; +} + +static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct snd_soc_component *component = dai->component; + struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component); + u8 clk_ctrl = 0; + u8 pcm_cfg = 0; + + dev_dbg(component->dev, "%s() format=0x%x\n", __func__, format); + + switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + if (!priv->sysclk) { + dev_err(component->dev, "operating in provider mode requires sysclock to be configured\n"); + return -EINVAL; + } + clk_ctrl |= PCM186X_CLK_CTRL_MST_MODE; + priv->is_provider_mode = true; + break; + case SND_SOC_DAIFMT_CBC_CFC: + priv->is_provider_mode = false; + break; + default: + dev_err(component->dev, "Invalid DAI master/slave interface\n"); + return -EINVAL; + } + + /* set interface polarity */ + switch (format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_err(component->dev, "Inverted DAI clocks not supported\n"); + return -EINVAL; + } + + /* set interface format */ + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + pcm_cfg = PCM186X_PCM_CFG_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + pcm_cfg = PCM186X_PCM_CFG_FMT_LEFTJ; + break; + case SND_SOC_DAIFMT_DSP_A: + priv->tdm_offset += 1; + fallthrough; + /* DSP_A uses the same basic config as DSP_B + * except we need to shift the TDM output by one BCK cycle + */ + case SND_SOC_DAIFMT_DSP_B: + priv->is_tdm_mode = true; + pcm_cfg = PCM186X_PCM_CFG_FMT_TDM; + break; + default: + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, PCM186X_CLK_CTRL, + PCM186X_CLK_CTRL_MST_MODE, clk_ctrl); + + snd_soc_component_write(component, PCM186X_TDM_TX_OFFSET, priv->tdm_offset); + + snd_soc_component_update_bits(component, PCM186X_PCM_CFG, + PCM186X_PCM_CFG_FMT_MASK, pcm_cfg); + + return 0; +} + +static int pcm186x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int first_slot, last_slot, tdm_offset; + + dev_dbg(component->dev, + "%s() tx_mask=0x%x rx_mask=0x%x slots=%d slot_width=%d\n", + __func__, tx_mask, rx_mask, slots, slot_width); + + if (!tx_mask) { + dev_err(component->dev, "tdm tx mask must not be 0\n"); + return -EINVAL; + } + + first_slot = __ffs(tx_mask); + last_slot = __fls(tx_mask); + + if (last_slot - first_slot != hweight32(tx_mask) - 1) { + dev_err(component->dev, "tdm tx mask must be contiguous\n"); + return -EINVAL; + } + + tdm_offset = first_slot * slot_width; + + if (tdm_offset > 255) { + dev_err(component->dev, "tdm tx slot selection out of bounds\n"); + return -EINVAL; + } + + priv->tdm_offset = tdm_offset; + + return 0; +} + +static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s() clk_id=%d freq=%u dir=%d\n", + __func__, clk_id, freq, dir); + + priv->sysclk = freq; + + return 0; +} + +static const struct snd_soc_dai_ops pcm186x_dai_ops = { + .set_sysclk = pcm186x_set_dai_sysclk, + .set_tdm_slot = pcm186x_set_tdm_slot, + .set_fmt = pcm186x_set_fmt, + .hw_params = pcm186x_hw_params, +}; + +static struct snd_soc_dai_driver pcm1863_dai = { + .name = "pcm1863-aif", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = PCM186X_RATES, + .formats = PCM186X_FORMATS, + }, + .ops = &pcm186x_dai_ops, +}; + +static struct snd_soc_dai_driver pcm1865_dai = { + .name = "pcm1865-aif", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = PCM186X_RATES, + .formats = PCM186X_FORMATS, + }, + .ops = &pcm186x_dai_ops, +}; + +static int pcm186x_power_on(struct snd_soc_component *component) +{ + struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component); + int ret = 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) + return ret; + + regcache_cache_only(priv->regmap, false); + ret = regcache_sync(priv->regmap); + if (ret) { + dev_err(component->dev, "Failed to restore cache\n"); + regcache_cache_only(priv->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), + priv->supplies); + return ret; + } + + snd_soc_component_update_bits(component, PCM186X_POWER_CTRL, + PCM186X_PWR_CTRL_PWRDN, 0); + + return 0; +} + +static int pcm186x_power_off(struct snd_soc_component *component) +{ + struct pcm186x_priv *priv = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, PCM186X_POWER_CTRL, + PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN); + + regcache_cache_only(priv->regmap, true); + + return regulator_bulk_disable(ARRAY_SIZE(priv->supplies), + priv->supplies); +} + +static int pcm186x_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + dev_dbg(component->dev, "## %s: %d -> %d\n", __func__, + snd_soc_component_get_bias_level(component), level); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + pcm186x_power_on(component); + break; + case SND_SOC_BIAS_OFF: + pcm186x_power_off(component); + break; + } + + return 0; +} + +static struct snd_soc_component_driver soc_codec_dev_pcm1863 = { + .set_bias_level = pcm186x_set_bias_level, + .controls = pcm1863_snd_controls, + .num_controls = ARRAY_SIZE(pcm1863_snd_controls), + .dapm_widgets = pcm1863_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1863_dapm_widgets), + .dapm_routes = pcm1863_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1863_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct snd_soc_component_driver soc_codec_dev_pcm1865 = { + .set_bias_level = pcm186x_set_bias_level, + .controls = pcm1865_snd_controls, + .num_controls = ARRAY_SIZE(pcm1865_snd_controls), + .dapm_widgets = pcm1865_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1865_dapm_widgets), + .dapm_routes = pcm1865_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1865_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static bool pcm186x_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PCM186X_PAGE: + case PCM186X_DEVICE_STATUS: + case PCM186X_FSAMPLE_STATUS: + case PCM186X_DIV_STATUS: + case PCM186X_CLK_STATUS: + case PCM186X_SUPPLY_STATUS: + case PCM186X_MMAP_STAT_CTRL: + case PCM186X_MMAP_ADDRESS: + return true; + } + + return false; +} + +static const struct regmap_range_cfg pcm186x_range = { + .name = "Pages", + .range_max = PCM186X_MAX_REGISTER, + .selector_reg = PCM186X_PAGE, + .selector_mask = 0xff, + .window_len = PCM186X_PAGE_LEN, +}; + +const struct regmap_config pcm186x_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .volatile_reg = pcm186x_volatile, + + .ranges = &pcm186x_range, + .num_ranges = 1, + + .max_register = PCM186X_MAX_REGISTER, + + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(pcm186x_regmap); + +int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq, + struct regmap *regmap) +{ + struct pcm186x_priv *priv; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(struct pcm186x_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + priv->regmap = regmap; + + for (i = 0; i < ARRAY_SIZE(priv->supplies); i++) + priv->supplies[i].supply = pcm186x_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) { + dev_err(dev, "failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) { + dev_err(dev, "failed enable supplies: %d\n", ret); + return ret; + } + + /* Reset device registers for a consistent power-on like state */ + ret = regmap_write(regmap, PCM186X_PAGE, PCM186X_RESET); + if (ret) { + dev_err(dev, "failed to write device: %d\n", ret); + return ret; + } + + ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret) { + dev_err(dev, "failed disable supplies: %d\n", ret); + return ret; + } + + switch (type) { + case PCM1865: + case PCM1864: + ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1865, + &pcm1865_dai, 1); + break; + case PCM1863: + case PCM1862: + default: + ret = devm_snd_soc_register_component(dev, &soc_codec_dev_pcm1863, + &pcm1863_dai, 1); + } + if (ret) { + dev_err(dev, "failed to register CODEC: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(pcm186x_probe); + +MODULE_AUTHOR("Andreas Dannenberg "); +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm186x.h b/sound/soc/codecs/pcm186x.h new file mode 100644 index 0000000000..4d493754a3 --- /dev/null +++ b/sound/soc/codecs/pcm186x.h @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments PCM186x Universal Audio ADC + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com + * Andreas Dannenberg + * Andrew F. Davis + */ + +#ifndef _PCM186X_H_ +#define _PCM186X_H_ + +#include +#include + +enum pcm186x_type { + PCM1862, + PCM1863, + PCM1864, + PCM1865, +}; + +#define PCM186X_RATES SNDRV_PCM_RATE_8000_192000 +#define PCM186X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define PCM186X_PAGE_LEN 0x0100 +#define PCM186X_PAGE_BASE(n) (PCM186X_PAGE_LEN * n) + +/* The page selection register address is the same on all pages */ +#define PCM186X_PAGE 0 + +/* Register Definitions - Page 0 */ +#define PCM186X_PGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 1) +#define PCM186X_PGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 2) +#define PCM186X_PGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 3) +#define PCM186X_PGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 4) +#define PCM186X_PGA_CTRL (PCM186X_PAGE_BASE(0) + 5) +#define PCM186X_ADC1_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 6) +#define PCM186X_ADC1_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 7) +#define PCM186X_ADC2_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 8) +#define PCM186X_ADC2_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 9) +#define PCM186X_AUXADC_INPUT_SEL (PCM186X_PAGE_BASE(0) + 10) +#define PCM186X_PCM_CFG (PCM186X_PAGE_BASE(0) + 11) +#define PCM186X_TDM_TX_SEL (PCM186X_PAGE_BASE(0) + 12) +#define PCM186X_TDM_TX_OFFSET (PCM186X_PAGE_BASE(0) + 13) +#define PCM186X_TDM_RX_OFFSET (PCM186X_PAGE_BASE(0) + 14) +#define PCM186X_DPGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 15) +#define PCM186X_GPIO1_0_CTRL (PCM186X_PAGE_BASE(0) + 16) +#define PCM186X_GPIO3_2_CTRL (PCM186X_PAGE_BASE(0) + 17) +#define PCM186X_GPIO1_0_DIR_CTRL (PCM186X_PAGE_BASE(0) + 18) +#define PCM186X_GPIO3_2_DIR_CTRL (PCM186X_PAGE_BASE(0) + 19) +#define PCM186X_GPIO_IN_OUT (PCM186X_PAGE_BASE(0) + 20) +#define PCM186X_GPIO_PULL_CTRL (PCM186X_PAGE_BASE(0) + 21) +#define PCM186X_DPGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 22) +#define PCM186X_DPGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 23) +#define PCM186X_DPGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 24) +#define PCM186X_DPGA_GAIN_CTRL (PCM186X_PAGE_BASE(0) + 25) +#define PCM186X_DPGA_MIC_CTRL (PCM186X_PAGE_BASE(0) + 26) +#define PCM186X_DIN_RESAMP_CTRL (PCM186X_PAGE_BASE(0) + 27) +#define PCM186X_CLK_CTRL (PCM186X_PAGE_BASE(0) + 32) +#define PCM186X_DSP1_CLK_DIV (PCM186X_PAGE_BASE(0) + 33) +#define PCM186X_DSP2_CLK_DIV (PCM186X_PAGE_BASE(0) + 34) +#define PCM186X_ADC_CLK_DIV (PCM186X_PAGE_BASE(0) + 35) +#define PCM186X_PLL_SCK_DIV (PCM186X_PAGE_BASE(0) + 37) +#define PCM186X_BCK_DIV (PCM186X_PAGE_BASE(0) + 38) +#define PCM186X_LRK_DIV (PCM186X_PAGE_BASE(0) + 39) +#define PCM186X_PLL_CTRL (PCM186X_PAGE_BASE(0) + 40) +#define PCM186X_PLL_P_DIV (PCM186X_PAGE_BASE(0) + 41) +#define PCM186X_PLL_R_DIV (PCM186X_PAGE_BASE(0) + 42) +#define PCM186X_PLL_J_DIV (PCM186X_PAGE_BASE(0) + 43) +#define PCM186X_PLL_D_DIV_LSB (PCM186X_PAGE_BASE(0) + 44) +#define PCM186X_PLL_D_DIV_MSB (PCM186X_PAGE_BASE(0) + 45) +#define PCM186X_SIGDET_MODE (PCM186X_PAGE_BASE(0) + 48) +#define PCM186X_SIGDET_MASK (PCM186X_PAGE_BASE(0) + 49) +#define PCM186X_SIGDET_STAT (PCM186X_PAGE_BASE(0) + 50) +#define PCM186X_SIGDET_LOSS_TIME (PCM186X_PAGE_BASE(0) + 52) +#define PCM186X_SIGDET_SCAN_TIME (PCM186X_PAGE_BASE(0) + 53) +#define PCM186X_SIGDET_INT_INTVL (PCM186X_PAGE_BASE(0) + 54) +#define PCM186X_SIGDET_DC_REF_CH1_L (PCM186X_PAGE_BASE(0) + 64) +#define PCM186X_SIGDET_DC_DIFF_CH1_L (PCM186X_PAGE_BASE(0) + 65) +#define PCM186X_SIGDET_DC_LEV_CH1_L (PCM186X_PAGE_BASE(0) + 66) +#define PCM186X_SIGDET_DC_REF_CH1_R (PCM186X_PAGE_BASE(0) + 67) +#define PCM186X_SIGDET_DC_DIFF_CH1_R (PCM186X_PAGE_BASE(0) + 68) +#define PCM186X_SIGDET_DC_LEV_CH1_R (PCM186X_PAGE_BASE(0) + 69) +#define PCM186X_SIGDET_DC_REF_CH2_L (PCM186X_PAGE_BASE(0) + 70) +#define PCM186X_SIGDET_DC_DIFF_CH2_L (PCM186X_PAGE_BASE(0) + 71) +#define PCM186X_SIGDET_DC_LEV_CH2_L (PCM186X_PAGE_BASE(0) + 72) +#define PCM186X_SIGDET_DC_REF_CH2_R (PCM186X_PAGE_BASE(0) + 73) +#define PCM186X_SIGDET_DC_DIFF_CH2_R (PCM186X_PAGE_BASE(0) + 74) +#define PCM186X_SIGDET_DC_LEV_CH2_R (PCM186X_PAGE_BASE(0) + 75) +#define PCM186X_SIGDET_DC_REF_CH3_L (PCM186X_PAGE_BASE(0) + 76) +#define PCM186X_SIGDET_DC_DIFF_CH3_L (PCM186X_PAGE_BASE(0) + 77) +#define PCM186X_SIGDET_DC_LEV_CH3_L (PCM186X_PAGE_BASE(0) + 78) +#define PCM186X_SIGDET_DC_REF_CH3_R (PCM186X_PAGE_BASE(0) + 79) +#define PCM186X_SIGDET_DC_DIFF_CH3_R (PCM186X_PAGE_BASE(0) + 80) +#define PCM186X_SIGDET_DC_LEV_CH3_R (PCM186X_PAGE_BASE(0) + 81) +#define PCM186X_SIGDET_DC_REF_CH4_L (PCM186X_PAGE_BASE(0) + 82) +#define PCM186X_SIGDET_DC_DIFF_CH4_L (PCM186X_PAGE_BASE(0) + 83) +#define PCM186X_SIGDET_DC_LEV_CH4_L (PCM186X_PAGE_BASE(0) + 84) +#define PCM186X_SIGDET_DC_REF_CH4_R (PCM186X_PAGE_BASE(0) + 85) +#define PCM186X_SIGDET_DC_DIFF_CH4_R (PCM186X_PAGE_BASE(0) + 86) +#define PCM186X_SIGDET_DC_LEV_CH4_R (PCM186X_PAGE_BASE(0) + 87) +#define PCM186X_AUXADC_DATA_CTRL (PCM186X_PAGE_BASE(0) + 88) +#define PCM186X_AUXADC_DATA_LSB (PCM186X_PAGE_BASE(0) + 89) +#define PCM186X_AUXADC_DATA_MSB (PCM186X_PAGE_BASE(0) + 90) +#define PCM186X_INT_ENABLE (PCM186X_PAGE_BASE(0) + 96) +#define PCM186X_INT_FLAG (PCM186X_PAGE_BASE(0) + 97) +#define PCM186X_INT_POL_WIDTH (PCM186X_PAGE_BASE(0) + 98) +#define PCM186X_POWER_CTRL (PCM186X_PAGE_BASE(0) + 112) +#define PCM186X_FILTER_MUTE_CTRL (PCM186X_PAGE_BASE(0) + 113) +#define PCM186X_DEVICE_STATUS (PCM186X_PAGE_BASE(0) + 114) +#define PCM186X_FSAMPLE_STATUS (PCM186X_PAGE_BASE(0) + 115) +#define PCM186X_DIV_STATUS (PCM186X_PAGE_BASE(0) + 116) +#define PCM186X_CLK_STATUS (PCM186X_PAGE_BASE(0) + 117) +#define PCM186X_SUPPLY_STATUS (PCM186X_PAGE_BASE(0) + 120) + +/* Register Definitions - Page 1 */ +#define PCM186X_MMAP_STAT_CTRL (PCM186X_PAGE_BASE(1) + 1) +#define PCM186X_MMAP_ADDRESS (PCM186X_PAGE_BASE(1) + 2) +#define PCM186X_MEM_WDATA0 (PCM186X_PAGE_BASE(1) + 4) +#define PCM186X_MEM_WDATA1 (PCM186X_PAGE_BASE(1) + 5) +#define PCM186X_MEM_WDATA2 (PCM186X_PAGE_BASE(1) + 6) +#define PCM186X_MEM_WDATA3 (PCM186X_PAGE_BASE(1) + 7) +#define PCM186X_MEM_RDATA0 (PCM186X_PAGE_BASE(1) + 8) +#define PCM186X_MEM_RDATA1 (PCM186X_PAGE_BASE(1) + 9) +#define PCM186X_MEM_RDATA2 (PCM186X_PAGE_BASE(1) + 10) +#define PCM186X_MEM_RDATA3 (PCM186X_PAGE_BASE(1) + 11) + +/* Register Definitions - Page 3 */ +#define PCM186X_OSC_PWR_DOWN_CTRL (PCM186X_PAGE_BASE(3) + 18) +#define PCM186X_MIC_BIAS_CTRL (PCM186X_PAGE_BASE(3) + 21) + +/* Register Definitions - Page 253 */ +#define PCM186X_CURR_TRIM_CTRL (PCM186X_PAGE_BASE(253) + 20) + +#define PCM186X_MAX_REGISTER PCM186X_CURR_TRIM_CTRL + +/* PCM186X_PAGE */ +#define PCM186X_RESET 0xfe + +/* PCM186X_ADCX_INPUT_SEL_X */ +#define PCM186X_ADC_INPUT_SEL_POL BIT(7) +#define PCM186X_ADC_INPUT_SEL_MASK GENMASK(5, 0) + +/* PCM186X_PCM_CFG */ +#define PCM186X_PCM_CFG_RX_WLEN_MASK GENMASK(7, 6) +#define PCM186X_PCM_CFG_RX_WLEN_SHIFT 6 +#define PCM186X_PCM_CFG_RX_WLEN_32 0x00 +#define PCM186X_PCM_CFG_RX_WLEN_24 0x01 +#define PCM186X_PCM_CFG_RX_WLEN_20 0x02 +#define PCM186X_PCM_CFG_RX_WLEN_16 0x03 +#define PCM186X_PCM_CFG_TDM_LRCK_MODE BIT(4) +#define PCM186X_PCM_CFG_TX_WLEN_MASK GENMASK(3, 2) +#define PCM186X_PCM_CFG_TX_WLEN_SHIFT 2 +#define PCM186X_PCM_CFG_TX_WLEN_32 0x00 +#define PCM186X_PCM_CFG_TX_WLEN_24 0x01 +#define PCM186X_PCM_CFG_TX_WLEN_20 0x02 +#define PCM186X_PCM_CFG_TX_WLEN_16 0x03 +#define PCM186X_PCM_CFG_FMT_MASK GENMASK(1, 0) +#define PCM186X_PCM_CFG_FMT_SHIFT 0 +#define PCM186X_PCM_CFG_FMT_I2S 0x00 +#define PCM186X_PCM_CFG_FMT_LEFTJ 0x01 +#define PCM186X_PCM_CFG_FMT_RIGHTJ 0x02 +#define PCM186X_PCM_CFG_FMT_TDM 0x03 + +/* PCM186X_TDM_TX_SEL */ +#define PCM186X_TDM_TX_SEL_2CH 0x00 +#define PCM186X_TDM_TX_SEL_4CH 0x01 +#define PCM186X_TDM_TX_SEL_6CH 0x02 +#define PCM186X_TDM_TX_SEL_MASK 0x03 + +/* PCM186X_CLK_CTRL */ +#define PCM186X_CLK_CTRL_SCK_XI_SEL1 BIT(7) +#define PCM186X_CLK_CTRL_SCK_XI_SEL0 BIT(6) +#define PCM186X_CLK_CTRL_SCK_SRC_PLL BIT(5) +#define PCM186X_CLK_CTRL_MST_MODE BIT(4) +#define PCM186X_CLK_CTRL_ADC_SRC_PLL BIT(3) +#define PCM186X_CLK_CTRL_DSP2_SRC_PLL BIT(2) +#define PCM186X_CLK_CTRL_DSP1_SRC_PLL BIT(1) +#define PCM186X_CLK_CTRL_CLKDET_EN BIT(0) + +/* PCM186X_PLL_CTRL */ +#define PCM186X_PLL_CTRL_LOCK BIT(4) +#define PCM186X_PLL_CTRL_REF_SEL BIT(1) +#define PCM186X_PLL_CTRL_EN BIT(0) + +/* PCM186X_POWER_CTRL */ +#define PCM186X_PWR_CTRL_PWRDN BIT(2) +#define PCM186X_PWR_CTRL_SLEEP BIT(1) +#define PCM186X_PWR_CTRL_STBY BIT(0) + +/* PCM186X_CLK_STATUS */ +#define PCM186X_CLK_STATUS_LRCKHLT BIT(6) +#define PCM186X_CLK_STATUS_BCKHLT BIT(5) +#define PCM186X_CLK_STATUS_SCKHLT BIT(4) +#define PCM186X_CLK_STATUS_LRCKERR BIT(2) +#define PCM186X_CLK_STATUS_BCKERR BIT(1) +#define PCM186X_CLK_STATUS_SCKERR BIT(0) + +/* PCM186X_SUPPLY_STATUS */ +#define PCM186X_SUPPLY_STATUS_DVDD BIT(2) +#define PCM186X_SUPPLY_STATUS_AVDD BIT(1) +#define PCM186X_SUPPLY_STATUS_LDO BIT(0) + +/* PCM186X_MMAP_STAT_CTRL */ +#define PCM186X_MMAP_STAT_DONE BIT(4) +#define PCM186X_MMAP_STAT_BUSY BIT(2) +#define PCM186X_MMAP_STAT_R_REQ BIT(1) +#define PCM186X_MMAP_STAT_W_REQ BIT(0) + +extern const struct regmap_config pcm186x_regmap; + +int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq, + struct regmap *regmap); + +#endif /* _PCM186X_H_ */ diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c new file mode 100644 index 0000000000..09c6c13268 --- /dev/null +++ b/sound/soc/codecs/pcm3008.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ALSA Soc PCM3008 codec support + * + * Author: Hugo Villeneuve + * Copyright (C) 2008 Lyrtech inc + * + * Based on AC97 Soc codec, original copyright follow: + * Copyright 2005 Wolfson Microelectronics PLC. + * + * Generic PCM3008 support. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcm3008.h" + +static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct pcm3008_setup_data *setup = component->dev->platform_data; + + gpio_set_value_cansleep(setup->pdda_pin, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct pcm3008_setup_data *setup = component->dev->platform_data; + + gpio_set_value_cansleep(setup->pdad_pin, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("VINL"), +SND_SOC_DAPM_INPUT("VINR"), + +SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_dac_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_adc_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_OUTPUT("VOUTL"), +SND_SOC_DAPM_OUTPUT("VOUTR"), +}; + +static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = { + { "PCM3008 Capture", NULL, "ADC" }, + { "ADC", NULL, "VINL" }, + { "ADC", NULL, "VINR" }, + + { "DAC", NULL, "PCM3008 Playback" }, + { "VOUTL", NULL, "DAC" }, + { "VOUTR", NULL, "DAC" }, +}; + +#define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +static struct snd_soc_dai_driver pcm3008_dai = { + .name = "pcm3008-hifi", + .playback = { + .stream_name = "PCM3008 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = PCM3008_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "PCM3008 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = PCM3008_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_pcm3008 = { + .dapm_widgets = pcm3008_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets), + .dapm_routes = pcm3008_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int pcm3008_codec_probe(struct platform_device *pdev) +{ + struct pcm3008_setup_data *setup = pdev->dev.platform_data; + int ret; + + if (!setup) + return -EINVAL; + + /* DEM1 DEM0 DE-EMPHASIS_MODE + * Low Low De-emphasis 44.1 kHz ON + * Low High De-emphasis OFF + * High Low De-emphasis 48 kHz ON + * High High De-emphasis 32 kHz ON + */ + + /* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */ + ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin, + GPIOF_OUT_INIT_HIGH, "codec_dem0"); + if (ret != 0) + return ret; + + /* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */ + ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin, + GPIOF_OUT_INIT_LOW, "codec_dem1"); + if (ret != 0) + return ret; + + /* Configure PDAD GPIO. */ + ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin, + GPIOF_OUT_INIT_LOW, "codec_pdad"); + if (ret != 0) + return ret; + + /* Configure PDDA GPIO. */ + ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin, + GPIOF_OUT_INIT_LOW, "codec_pdda"); + if (ret != 0) + return ret; + + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_pcm3008, &pcm3008_dai, 1); +} + +MODULE_ALIAS("platform:pcm3008-codec"); + +static struct platform_driver pcm3008_codec_driver = { + .probe = pcm3008_codec_probe, + .driver = { + .name = "pcm3008-codec", + }, +}; + +module_platform_driver(pcm3008_codec_driver); + +MODULE_DESCRIPTION("Soc PCM3008 driver"); +MODULE_AUTHOR("Hugo Villeneuve"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm3008.h b/sound/soc/codecs/pcm3008.h new file mode 100644 index 0000000000..f7f4fbbd89 --- /dev/null +++ b/sound/soc/codecs/pcm3008.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * PCM3008 ALSA SoC Layer + * + * Author: Hugo Villeneuve + * Copyright (C) 2008 Lyrtech inc + */ + +#ifndef __LINUX_SND_SOC_PCM3008_H +#define __LINUX_SND_SOC_PCM3008_H + +struct pcm3008_setup_data { + unsigned dem0_pin; + unsigned dem1_pin; + unsigned pdad_pin; + unsigned pdda_pin; +}; + +#endif diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c new file mode 100644 index 0000000000..5330cf46b1 --- /dev/null +++ b/sound/soc/codecs/pcm3060-i2c.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// PCM3060 I2C driver +// +// Copyright (C) 2018 Kirill Marinushkin + +#include +#include +#include + +#include "pcm3060.h" + +static int pcm3060_i2c_probe(struct i2c_client *i2c) +{ + struct pcm3060_priv *priv; + + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_clientdata(i2c, priv); + + priv->regmap = devm_regmap_init_i2c(i2c, &pcm3060_regmap); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + return pcm3060_probe(&i2c->dev); +} + +static const struct i2c_device_id pcm3060_i2c_id[] = { + { .name = "pcm3060" }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, pcm3060_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id pcm3060_of_match[] = { + { .compatible = "ti,pcm3060" }, + { }, +}; +MODULE_DEVICE_TABLE(of, pcm3060_of_match); +#endif /* CONFIG_OF */ + +static struct i2c_driver pcm3060_i2c_driver = { + .driver = { + .name = "pcm3060", +#ifdef CONFIG_OF + .of_match_table = pcm3060_of_match, +#endif /* CONFIG_OF */ + }, + .id_table = pcm3060_i2c_id, + .probe = pcm3060_i2c_probe, +}; + +module_i2c_driver(pcm3060_i2c_driver); + +MODULE_DESCRIPTION("PCM3060 I2C driver"); +MODULE_AUTHOR("Kirill Marinushkin "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3060-spi.c b/sound/soc/codecs/pcm3060-spi.c new file mode 100644 index 0000000000..3b79734b83 --- /dev/null +++ b/sound/soc/codecs/pcm3060-spi.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// PCM3060 SPI driver +// +// Copyright (C) 2018 Kirill Marinushkin + +#include +#include +#include + +#include "pcm3060.h" + +static int pcm3060_spi_probe(struct spi_device *spi) +{ + struct pcm3060_priv *priv; + + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spi_set_drvdata(spi, priv); + + priv->regmap = devm_regmap_init_spi(spi, &pcm3060_regmap); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + return pcm3060_probe(&spi->dev); +} + +static const struct spi_device_id pcm3060_spi_id[] = { + { .name = "pcm3060" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm3060_spi_id); + +#ifdef CONFIG_OF +static const struct of_device_id pcm3060_of_match[] = { + { .compatible = "ti,pcm3060" }, + { }, +}; +MODULE_DEVICE_TABLE(of, pcm3060_of_match); +#endif /* CONFIG_OF */ + +static struct spi_driver pcm3060_spi_driver = { + .driver = { + .name = "pcm3060", +#ifdef CONFIG_OF + .of_match_table = pcm3060_of_match, +#endif /* CONFIG_OF */ + }, + .id_table = pcm3060_spi_id, + .probe = pcm3060_spi_probe, +}; + +module_spi_driver(pcm3060_spi_driver); + +MODULE_DESCRIPTION("PCM3060 SPI driver"); +MODULE_AUTHOR("Kirill Marinushkin "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c new file mode 100644 index 0000000000..586ec8c724 --- /dev/null +++ b/sound/soc/codecs/pcm3060.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// PCM3060 codec driver +// +// Copyright (C) 2018 Kirill Marinushkin + +#include +#include +#include +#include + +#include "pcm3060.h" + +/* dai */ + +static int pcm3060_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *comp = dai->component; + struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); + unsigned int reg; + unsigned int val; + + if (dir != SND_SOC_CLOCK_IN) { + dev_err(comp->dev, "unsupported sysclock dir: %d\n", dir); + return -EINVAL; + } + + switch (clk_id) { + case PCM3060_CLK_DEF: + val = 0; + break; + + case PCM3060_CLK1: + val = (dai->id == PCM3060_DAI_ID_DAC ? PCM3060_REG_CSEL : 0); + break; + + case PCM3060_CLK2: + val = (dai->id == PCM3060_DAI_ID_DAC ? 0 : PCM3060_REG_CSEL); + break; + + default: + dev_err(comp->dev, "unsupported sysclock id: %d\n", clk_id); + return -EINVAL; + } + + if (dai->id == PCM3060_DAI_ID_DAC) + reg = PCM3060_REG67; + else + reg = PCM3060_REG72; + + regmap_update_bits(priv->regmap, reg, PCM3060_REG_CSEL, val); + + priv->dai[dai->id].sclk_freq = freq; + + return 0; +} + +static int pcm3060_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *comp = dai->component; + struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); + unsigned int reg; + unsigned int val; + + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) { + dev_err(comp->dev, "unsupported DAI polarity: 0x%x\n", fmt); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + priv->dai[dai->id].is_provider = true; + break; + case SND_SOC_DAIFMT_CBC_CFC: + priv->dai[dai->id].is_provider = false; + break; + default: + dev_err(comp->dev, "unsupported DAI mode: 0x%x\n", fmt); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val = PCM3060_REG_FMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + val = PCM3060_REG_FMT_RJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = PCM3060_REG_FMT_LJ; + break; + default: + dev_err(comp->dev, "unsupported DAI format: 0x%x\n", fmt); + return -EINVAL; + } + + if (dai->id == PCM3060_DAI_ID_DAC) + reg = PCM3060_REG67; + else + reg = PCM3060_REG72; + + regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_FMT, val); + + return 0; +} + +static int pcm3060_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *comp = dai->component; + struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); + unsigned int rate; + unsigned int ratio; + unsigned int reg; + unsigned int val; + + if (!priv->dai[dai->id].is_provider) { + val = PCM3060_REG_MS_S; + goto val_ready; + } + + rate = params_rate(params); + if (!rate) { + dev_err(comp->dev, "rate is not configured\n"); + return -EINVAL; + } + + ratio = priv->dai[dai->id].sclk_freq / rate; + + switch (ratio) { + case 768: + val = PCM3060_REG_MS_M768; + break; + case 512: + val = PCM3060_REG_MS_M512; + break; + case 384: + val = PCM3060_REG_MS_M384; + break; + case 256: + val = PCM3060_REG_MS_M256; + break; + case 192: + val = PCM3060_REG_MS_M192; + break; + case 128: + val = PCM3060_REG_MS_M128; + break; + default: + dev_err(comp->dev, "unsupported ratio: %d\n", ratio); + return -EINVAL; + } + +val_ready: + if (dai->id == PCM3060_DAI_ID_DAC) + reg = PCM3060_REG67; + else + reg = PCM3060_REG72; + + regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_MS, val); + + return 0; +} + +static const struct snd_soc_dai_ops pcm3060_dai_ops = { + .set_sysclk = pcm3060_set_sysclk, + .set_fmt = pcm3060_set_fmt, + .hw_params = pcm3060_hw_params, +}; + +#define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define PCM3060_DAI_RATES_DAC (PCM3060_DAI_RATES_ADC | \ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +static struct snd_soc_dai_driver pcm3060_dai[] = { + { + .name = "pcm3060-dac", + .id = PCM3060_DAI_ID_DAC, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = PCM3060_DAI_RATES_DAC, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &pcm3060_dai_ops, + }, + { + .name = "pcm3060-adc", + .id = PCM3060_DAI_ID_ADC, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = PCM3060_DAI_RATES_ADC, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &pcm3060_dai_ops, + }, +}; + +/* dapm */ + +static DECLARE_TLV_DB_SCALE(pcm3060_dapm_tlv, -10050, 50, 1); + +static const struct snd_kcontrol_new pcm3060_dapm_controls[] = { + SOC_DOUBLE_R_RANGE_TLV("Master Playback Volume", + PCM3060_REG65, PCM3060_REG66, 0, + PCM3060_REG_AT2_MIN, PCM3060_REG_AT2_MAX, + 0, pcm3060_dapm_tlv), + SOC_DOUBLE("Master Playback Switch", PCM3060_REG68, + PCM3060_REG_SHIFT_MUT21, PCM3060_REG_SHIFT_MUT22, 1, 1), + + SOC_DOUBLE_R_RANGE_TLV("Master Capture Volume", + PCM3060_REG70, PCM3060_REG71, 0, + PCM3060_REG_AT1_MIN, PCM3060_REG_AT1_MAX, + 0, pcm3060_dapm_tlv), + SOC_DOUBLE("Master Capture Switch", PCM3060_REG73, + PCM3060_REG_SHIFT_MUT11, PCM3060_REG_SHIFT_MUT12, 1, 1), +}; + +static const struct snd_soc_dapm_widget pcm3060_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", PCM3060_REG64, + PCM3060_REG_SHIFT_DAPSV, 1), + + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_OUTPUT("OUTR"), + + SND_SOC_DAPM_INPUT("INL"), + SND_SOC_DAPM_INPUT("INR"), + + SND_SOC_DAPM_ADC("ADC", "Capture", PCM3060_REG64, + PCM3060_REG_SHIFT_ADPSV, 1), +}; + +static const struct snd_soc_dapm_route pcm3060_dapm_map[] = { + { "OUTL", NULL, "DAC" }, + { "OUTR", NULL, "DAC" }, + + { "ADC", NULL, "INL" }, + { "ADC", NULL, "INR" }, +}; + +/* soc component */ + +static const struct snd_soc_component_driver pcm3060_soc_comp_driver = { + .controls = pcm3060_dapm_controls, + .num_controls = ARRAY_SIZE(pcm3060_dapm_controls), + .dapm_widgets = pcm3060_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm3060_dapm_widgets), + .dapm_routes = pcm3060_dapm_map, + .num_dapm_routes = ARRAY_SIZE(pcm3060_dapm_map), + .endianness = 1, +}; + +/* regmap */ + +static bool pcm3060_reg_writeable(struct device *dev, unsigned int reg) +{ + return (reg >= PCM3060_REG64); +} + +static bool pcm3060_reg_readable(struct device *dev, unsigned int reg) +{ + return (reg >= PCM3060_REG64); +} + +static bool pcm3060_reg_volatile(struct device *dev, unsigned int reg) +{ + /* PCM3060_REG64 is volatile */ + return (reg == PCM3060_REG64); +} + +static const struct reg_default pcm3060_reg_defaults[] = { + { PCM3060_REG64, 0xF0 }, + { PCM3060_REG65, 0xFF }, + { PCM3060_REG66, 0xFF }, + { PCM3060_REG67, 0x00 }, + { PCM3060_REG68, 0x00 }, + { PCM3060_REG69, 0x00 }, + { PCM3060_REG70, 0xD7 }, + { PCM3060_REG71, 0xD7 }, + { PCM3060_REG72, 0x00 }, + { PCM3060_REG73, 0x00 }, +}; + +const struct regmap_config pcm3060_regmap = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = pcm3060_reg_writeable, + .readable_reg = pcm3060_reg_readable, + .volatile_reg = pcm3060_reg_volatile, + .max_register = PCM3060_REG73, + .reg_defaults = pcm3060_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm3060_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL(pcm3060_regmap); + +/* device */ + +static void pcm3060_parse_dt(const struct device_node *np, + struct pcm3060_priv *priv) +{ + priv->out_se = of_property_read_bool(np, "ti,out-single-ended"); +} + +int pcm3060_probe(struct device *dev) +{ + int rc; + struct pcm3060_priv *priv = dev_get_drvdata(dev); + + /* soft reset */ + rc = regmap_update_bits(priv->regmap, PCM3060_REG64, + PCM3060_REG_MRST, 0); + if (rc) { + dev_err(dev, "failed to reset component, rc=%d\n", rc); + return rc; + } + + if (dev->of_node) + pcm3060_parse_dt(dev->of_node, priv); + + if (priv->out_se) + regmap_update_bits(priv->regmap, PCM3060_REG64, + PCM3060_REG_SE, PCM3060_REG_SE); + + rc = devm_snd_soc_register_component(dev, &pcm3060_soc_comp_driver, + pcm3060_dai, + ARRAY_SIZE(pcm3060_dai)); + if (rc) { + dev_err(dev, "failed to register component, rc=%d\n", rc); + return rc; + } + + return 0; +} +EXPORT_SYMBOL(pcm3060_probe); + +MODULE_DESCRIPTION("PCM3060 codec driver"); +MODULE_AUTHOR("Kirill Marinushkin "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h new file mode 100644 index 0000000000..5e1185e7b0 --- /dev/null +++ b/sound/soc/codecs/pcm3060.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PCM3060 codec driver + * + * Copyright (C) 2018 Kirill Marinushkin + */ + +#ifndef _SND_SOC_PCM3060_H +#define _SND_SOC_PCM3060_H + +#include +#include + +extern const struct regmap_config pcm3060_regmap; + +#define PCM3060_DAI_ID_DAC 0 +#define PCM3060_DAI_ID_ADC 1 +#define PCM3060_DAI_IDS_NUM 2 + +/* ADC and DAC can be clocked from separate or same sources CLK1 and CLK2 */ +#define PCM3060_CLK_DEF 0 /* default: CLK1->ADC, CLK2->DAC */ +#define PCM3060_CLK1 1 +#define PCM3060_CLK2 2 + +struct pcm3060_priv_dai { + bool is_provider; + unsigned int sclk_freq; +}; + +struct pcm3060_priv { + struct regmap *regmap; + struct pcm3060_priv_dai dai[PCM3060_DAI_IDS_NUM]; + u8 out_se: 1; +}; + +int pcm3060_probe(struct device *dev); +int pcm3060_remove(struct device *dev); + +/* registers */ + +#define PCM3060_REG64 0x40 +#define PCM3060_REG_MRST 0x80 +#define PCM3060_REG_SRST 0x40 +#define PCM3060_REG_ADPSV 0x20 +#define PCM3060_REG_SHIFT_ADPSV 0x05 +#define PCM3060_REG_DAPSV 0x10 +#define PCM3060_REG_SHIFT_DAPSV 0x04 +#define PCM3060_REG_SE 0x01 + +#define PCM3060_REG65 0x41 +#define PCM3060_REG66 0x42 +#define PCM3060_REG_AT2_MIN 0x36 +#define PCM3060_REG_AT2_MAX 0xFF + +#define PCM3060_REG67 0x43 +#define PCM3060_REG72 0x48 +#define PCM3060_REG_CSEL 0x80 +#define PCM3060_REG_MASK_MS 0x70 +#define PCM3060_REG_MS_S 0x00 +#define PCM3060_REG_MS_M768 (0x01 << 4) +#define PCM3060_REG_MS_M512 (0x02 << 4) +#define PCM3060_REG_MS_M384 (0x03 << 4) +#define PCM3060_REG_MS_M256 (0x04 << 4) +#define PCM3060_REG_MS_M192 (0x05 << 4) +#define PCM3060_REG_MS_M128 (0x06 << 4) +#define PCM3060_REG_MASK_FMT 0x03 +#define PCM3060_REG_FMT_I2S 0x00 +#define PCM3060_REG_FMT_LJ 0x01 +#define PCM3060_REG_FMT_RJ 0x02 + +#define PCM3060_REG68 0x44 +#define PCM3060_REG_OVER 0x40 +#define PCM3060_REG_DREV2 0x04 +#define PCM3060_REG_SHIFT_MUT21 0x00 +#define PCM3060_REG_SHIFT_MUT22 0x01 + +#define PCM3060_REG69 0x45 +#define PCM3060_REG_FLT 0x80 +#define PCM3060_REG_MASK_DMF 0x60 +#define PCM3060_REG_DMC 0x10 +#define PCM3060_REG_ZREV 0x02 +#define PCM3060_REG_AZRO 0x01 + +#define PCM3060_REG70 0x46 +#define PCM3060_REG71 0x47 +#define PCM3060_REG_AT1_MIN 0x0E +#define PCM3060_REG_AT1_MAX 0xFF + +#define PCM3060_REG73 0x49 +#define PCM3060_REG_ZCDD 0x10 +#define PCM3060_REG_BYP 0x08 +#define PCM3060_REG_DREV1 0x04 +#define PCM3060_REG_SHIFT_MUT11 0x00 +#define PCM3060_REG_SHIFT_MUT12 0x01 + +#endif /* _SND_SOC_PCM3060_H */ diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c new file mode 100644 index 0000000000..7052cc0c97 --- /dev/null +++ b/sound/soc/codecs/pcm3168a-i2c.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PCM3168A codec i2c driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley + */ + +#include +#include +#include + +#include + +#include "pcm3168a.h" + +static int pcm3168a_i2c_probe(struct i2c_client *i2c) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &pcm3168a_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm3168a_probe(&i2c->dev, regmap); +} + +static void pcm3168a_i2c_remove(struct i2c_client *i2c) +{ + pcm3168a_remove(&i2c->dev); +} + +static const struct i2c_device_id pcm3168a_i2c_id[] = { + { "pcm3168a", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id); + +static const struct of_device_id pcm3168a_of_match[] = { + { .compatible = "ti,pcm3168a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm3168a_of_match); + +static struct i2c_driver pcm3168a_i2c_driver = { + .probe = pcm3168a_i2c_probe, + .remove = pcm3168a_i2c_remove, + .id_table = pcm3168a_i2c_id, + .driver = { + .name = "pcm3168a", + .of_match_table = pcm3168a_of_match, + .pm = &pcm3168a_pm_ops, + }, +}; +module_i2c_driver(pcm3168a_i2c_driver); + +MODULE_DESCRIPTION("PCM3168A I2C codec driver"); +MODULE_AUTHOR("Damien Horsley "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3168a-spi.c b/sound/soc/codecs/pcm3168a-spi.c new file mode 100644 index 0000000000..b5b08046f5 --- /dev/null +++ b/sound/soc/codecs/pcm3168a-spi.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PCM3168A codec spi driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley + */ + +#include +#include +#include + +#include + +#include "pcm3168a.h" + +static int pcm3168a_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &pcm3168a_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm3168a_probe(&spi->dev, regmap); +} + +static void pcm3168a_spi_remove(struct spi_device *spi) +{ + pcm3168a_remove(&spi->dev); +} + +static const struct spi_device_id pcm3168a_spi_id[] = { + { "pcm3168a", }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm3168a_spi_id); + +static const struct of_device_id pcm3168a_of_match[] = { + { .compatible = "ti,pcm3168a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm3168a_of_match); + +static struct spi_driver pcm3168a_spi_driver = { + .probe = pcm3168a_spi_probe, + .remove = pcm3168a_spi_remove, + .id_table = pcm3168a_spi_id, + .driver = { + .name = "pcm3168a", + .of_match_table = pcm3168a_of_match, + .pm = &pcm3168a_pm_ops, + }, +}; +module_spi_driver(pcm3168a_spi_driver); + +MODULE_DESCRIPTION("PCM3168A SPI codec driver"); +MODULE_AUTHOR("Damien Horsley "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c new file mode 100644 index 0000000000..9d6431338f --- /dev/null +++ b/sound/soc/codecs/pcm3168a.c @@ -0,0 +1,915 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PCM3168A codec driver + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pcm3168a.h" + +#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define PCM3168A_FMT_I2S 0x0 +#define PCM3168A_FMT_LEFT_J 0x1 +#define PCM3168A_FMT_RIGHT_J 0x2 +#define PCM3168A_FMT_RIGHT_J_16 0x3 +#define PCM3168A_FMT_DSP_A 0x4 +#define PCM3168A_FMT_DSP_B 0x5 +#define PCM3168A_FMT_I2S_TDM 0x6 +#define PCM3168A_FMT_LEFT_J_TDM 0x7 + +static const char *const pcm3168a_supply_names[] = { + "VDD1", + "VDD2", + "VCCAD1", + "VCCAD2", + "VCCDA1", + "VCCDA2" +}; + +#define PCM3168A_DAI_DAC 0 +#define PCM3168A_DAI_ADC 1 + +/* ADC/DAC side parameters */ +struct pcm3168a_io_params { + bool provider_mode; + unsigned int format; + int tdm_slots; + u32 tdm_mask; + int slot_width; +}; + +struct pcm3168a_priv { + struct regulator_bulk_data supplies[ARRAY_SIZE(pcm3168a_supply_names)]; + struct regmap *regmap; + struct clk *scki; + struct gpio_desc *gpio_rst; + unsigned long sysclk; + + struct pcm3168a_io_params io_params[2]; + struct snd_soc_dai_driver dai_drv[2]; +}; + +static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_d1_roll_off, PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_FLT_SHIFT, pcm3168a_roll_off); +static SOC_ENUM_SINGLE_DECL(pcm3168a_d2_roll_off, PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_FLT_SHIFT + 1, pcm3168a_roll_off); +static SOC_ENUM_SINGLE_DECL(pcm3168a_d3_roll_off, PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_FLT_SHIFT + 2, pcm3168a_roll_off); +static SOC_ENUM_SINGLE_DECL(pcm3168a_d4_roll_off, PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_FLT_SHIFT + 3, pcm3168a_roll_off); + +static const char *const pcm3168a_volume_type[] = { + "Individual", "Master + Individual" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_volume_type, PCM3168A_DAC_ATT_DEMP_ZF, + PCM3168A_DAC_ATMDDA_SHIFT, pcm3168a_volume_type); + +static const char *const pcm3168a_att_speed_mult[] = { "2048", "4096" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_att_mult, PCM3168A_DAC_ATT_DEMP_ZF, + PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_att_speed_mult); + +static const char *const pcm3168a_demp[] = { + "Disabled", "48khz", "44.1khz", "32khz" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_demp, PCM3168A_DAC_ATT_DEMP_ZF, + PCM3168A_DAC_DEMP_SHIFT, pcm3168a_demp); + +static const char *const pcm3168a_zf_func[] = { + "DAC 1/2/3/4 AND", "DAC 1/2/3/4 OR", "DAC 1/2/3 AND", + "DAC 1/2/3 OR", "DAC 4 AND", "DAC 4 OR" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF, + PCM3168A_DAC_AZRO_SHIFT, pcm3168a_zf_func); + +static const char *const pcm3168a_pol[] = { "Active High", "Active Low" }; + +static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF, + PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol); + +static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" }; + +static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc1_con, PCM3168A_ADC_SEAD, + 0, 1, pcm3168a_con); +static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc2_con, PCM3168A_ADC_SEAD, + 2, 3, pcm3168a_con); +static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc3_con, PCM3168A_ADC_SEAD, + 4, 5, pcm3168a_con); + +static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_volume_type, PCM3168A_ADC_ATT_OVF, + PCM3168A_ADC_ATMDAD_SHIFT, pcm3168a_volume_type); + +static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_att_mult, PCM3168A_ADC_ATT_OVF, + PCM3168A_ADC_ATSPAD_SHIFT, pcm3168a_att_speed_mult); + +static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_ov_pol, PCM3168A_ADC_ATT_OVF, + PCM3168A_ADC_OVFP_SHIFT, pcm3168a_pol); + +/* -100db to 0db, register values 0-54 cause mute */ +static const DECLARE_TLV_DB_SCALE(pcm3168a_dac_tlv, -10050, 50, 1); + +/* -100db to 20db, register values 0-14 cause mute */ +static const DECLARE_TLV_DB_SCALE(pcm3168a_adc_tlv, -10050, 50, 1); + +static const struct snd_kcontrol_new pcm3168a_snd_controls[] = { + SOC_SINGLE("DAC Power-Save Switch", PCM3168A_DAC_PWR_MST_FMT, + PCM3168A_DAC_PSMDA_SHIFT, 1, 1), + SOC_ENUM("DAC1 Digital Filter roll-off", pcm3168a_d1_roll_off), + SOC_ENUM("DAC2 Digital Filter roll-off", pcm3168a_d2_roll_off), + SOC_ENUM("DAC3 Digital Filter roll-off", pcm3168a_d3_roll_off), + SOC_ENUM("DAC4 Digital Filter roll-off", pcm3168a_d4_roll_off), + SOC_DOUBLE("DAC1 Invert Switch", PCM3168A_DAC_INV, 0, 1, 1, 0), + SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0), + SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0), + SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0), + SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type), + SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult), + SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp), + SOC_ENUM("DAC Zero Flag Function", pcm3168a_dac_zf_func), + SOC_ENUM("DAC Zero Flag Polarity", pcm3168a_dac_zf_pol), + SOC_SINGLE_RANGE_TLV("Master Playback Volume", + PCM3168A_DAC_VOL_MASTER, 0, 54, 255, 0, + pcm3168a_dac_tlv), + SOC_DOUBLE_R_RANGE_TLV("DAC1 Playback Volume", + PCM3168A_DAC_VOL_CHAN_START, + PCM3168A_DAC_VOL_CHAN_START + 1, + 0, 54, 255, 0, pcm3168a_dac_tlv), + SOC_DOUBLE_R_RANGE_TLV("DAC2 Playback Volume", + PCM3168A_DAC_VOL_CHAN_START + 2, + PCM3168A_DAC_VOL_CHAN_START + 3, + 0, 54, 255, 0, pcm3168a_dac_tlv), + SOC_DOUBLE_R_RANGE_TLV("DAC3 Playback Volume", + PCM3168A_DAC_VOL_CHAN_START + 4, + PCM3168A_DAC_VOL_CHAN_START + 5, + 0, 54, 255, 0, pcm3168a_dac_tlv), + SOC_DOUBLE_R_RANGE_TLV("DAC4 Playback Volume", + PCM3168A_DAC_VOL_CHAN_START + 6, + PCM3168A_DAC_VOL_CHAN_START + 7, + 0, 54, 255, 0, pcm3168a_dac_tlv), + SOC_SINGLE("ADC1 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_BYP_SHIFT, 1, 1), + SOC_SINGLE("ADC2 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_BYP_SHIFT + 1, 1, 1), + SOC_SINGLE("ADC3 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_BYP_SHIFT + 2, 1, 1), + SOC_ENUM("ADC1 Connection Type", pcm3168a_adc1_con), + SOC_ENUM("ADC2 Connection Type", pcm3168a_adc2_con), + SOC_ENUM("ADC3 Connection Type", pcm3168a_adc3_con), + SOC_DOUBLE("ADC1 Invert Switch", PCM3168A_ADC_INV, 0, 1, 1, 0), + SOC_DOUBLE("ADC2 Invert Switch", PCM3168A_ADC_INV, 2, 3, 1, 0), + SOC_DOUBLE("ADC3 Invert Switch", PCM3168A_ADC_INV, 4, 5, 1, 0), + SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0), + SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0), + SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0), + SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type), + SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult), + SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol), + SOC_SINGLE_RANGE_TLV("Master Capture Volume", + PCM3168A_ADC_VOL_MASTER, 0, 14, 255, 0, + pcm3168a_adc_tlv), + SOC_DOUBLE_R_RANGE_TLV("ADC1 Capture Volume", + PCM3168A_ADC_VOL_CHAN_START, + PCM3168A_ADC_VOL_CHAN_START + 1, + 0, 14, 255, 0, pcm3168a_adc_tlv), + SOC_DOUBLE_R_RANGE_TLV("ADC2 Capture Volume", + PCM3168A_ADC_VOL_CHAN_START + 2, + PCM3168A_ADC_VOL_CHAN_START + 3, + 0, 14, 255, 0, pcm3168a_adc_tlv), + SOC_DOUBLE_R_RANGE_TLV("ADC3 Capture Volume", + PCM3168A_ADC_VOL_CHAN_START + 4, + PCM3168A_ADC_VOL_CHAN_START + 5, + 0, 14, 255, 0, pcm3168a_adc_tlv) +}; + +static const struct snd_soc_dapm_widget pcm3168a_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC1", "Playback", PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_OPEDA_SHIFT, 1), + SND_SOC_DAPM_DAC("DAC2", "Playback", PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_OPEDA_SHIFT + 1, 1), + SND_SOC_DAPM_DAC("DAC3", "Playback", PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_OPEDA_SHIFT + 2, 1), + SND_SOC_DAPM_DAC("DAC4", "Playback", PCM3168A_DAC_OP_FLT, + PCM3168A_DAC_OPEDA_SHIFT + 3, 1), + + SND_SOC_DAPM_OUTPUT("AOUT1L"), + SND_SOC_DAPM_OUTPUT("AOUT1R"), + SND_SOC_DAPM_OUTPUT("AOUT2L"), + SND_SOC_DAPM_OUTPUT("AOUT2R"), + SND_SOC_DAPM_OUTPUT("AOUT3L"), + SND_SOC_DAPM_OUTPUT("AOUT3R"), + SND_SOC_DAPM_OUTPUT("AOUT4L"), + SND_SOC_DAPM_OUTPUT("AOUT4R"), + + SND_SOC_DAPM_ADC("ADC1", "Capture", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_PSVAD_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC2", "Capture", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_PSVAD_SHIFT + 1, 1), + SND_SOC_DAPM_ADC("ADC3", "Capture", PCM3168A_ADC_PWR_HPFB, + PCM3168A_ADC_PSVAD_SHIFT + 2, 1), + + SND_SOC_DAPM_INPUT("AIN1L"), + SND_SOC_DAPM_INPUT("AIN1R"), + SND_SOC_DAPM_INPUT("AIN2L"), + SND_SOC_DAPM_INPUT("AIN2R"), + SND_SOC_DAPM_INPUT("AIN3L"), + SND_SOC_DAPM_INPUT("AIN3R") +}; + +static const struct snd_soc_dapm_route pcm3168a_dapm_routes[] = { + /* Playback */ + { "AOUT1L", NULL, "DAC1" }, + { "AOUT1R", NULL, "DAC1" }, + + { "AOUT2L", NULL, "DAC2" }, + { "AOUT2R", NULL, "DAC2" }, + + { "AOUT3L", NULL, "DAC3" }, + { "AOUT3R", NULL, "DAC3" }, + + { "AOUT4L", NULL, "DAC4" }, + { "AOUT4R", NULL, "DAC4" }, + + /* Capture */ + { "ADC1", NULL, "AIN1L" }, + { "ADC1", NULL, "AIN1R" }, + + { "ADC2", NULL, "AIN2L" }, + { "ADC2", NULL, "AIN2R" }, + + { "ADC3", NULL, "AIN3L" }, + { "ADC3", NULL, "AIN3R" } +}; + +static unsigned int pcm3168a_scki_ratios[] = { + 768, + 512, + 384, + 256, + 192, + 128 +}; + +#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios) +#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2) + +#define PCM3168A_MAX_SYSCLK 36864000 + +static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a) +{ + int ret; + + ret = regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, 0); + if (ret) + return ret; + + /* Internal reset is de-asserted after 3846 SCKI cycles */ + msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk)); + + return regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, + PCM3168A_MRST_MASK | PCM3168A_SRST_MASK); +} + +static int pcm3168a_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); + + regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0); + + return 0; +} + +static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(dai->component); + int ret; + + /* + * Some sound card sets 0 Hz as reset, + * but it is impossible to set. Ignore it here + */ + if (freq == 0) + return 0; + + if (freq > PCM3168A_MAX_SYSCLK) + return -EINVAL; + + ret = clk_set_rate(pcm3168a->scki, freq); + if (ret) + return ret; + + pcm3168a->sysclk = freq; + + return 0; +} + +static void pcm3168a_update_fixup_pcm_stream(struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); + struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id]; + u64 formats = SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE; + unsigned int channel_max = dai->id == PCM3168A_DAI_DAC ? 8 : 6; + + if (io_params->format == SND_SOC_DAIFMT_RIGHT_J) { + /* S16_LE is only supported in RIGHT_J mode */ + formats |= SNDRV_PCM_FMTBIT_S16_LE; + + /* + * If multi DIN/DOUT is not selected, RIGHT_J can only support + * two channels (no TDM support) + */ + if (io_params->tdm_slots != 2) + channel_max = 2; + } + + if (dai->id == PCM3168A_DAI_DAC) { + dai->driver->playback.channels_max = channel_max; + dai->driver->playback.formats = formats; + } else { + dai->driver->capture.channels_max = channel_max; + dai->driver->capture.formats = formats; + } +} + +static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct snd_soc_component *component = dai->component; + struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); + struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id]; + bool provider_mode; + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + break; + default: + dev_err(component->dev, "unsupported dai format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + provider_mode = false; + break; + case SND_SOC_DAIFMT_CBP_CFP: + provider_mode = true; + break; + default: + dev_err(component->dev, "unsupported provider mode\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + io_params->provider_mode = provider_mode; + io_params->format = format & SND_SOC_DAIFMT_FORMAT_MASK; + + pcm3168a_update_fixup_pcm_stream(dai); + + return 0; +} + +static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, + int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); + struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id]; + + if (tx_mask >= (1<= (1<dev, + "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n", + tx_mask, rx_mask, slots); + return -EINVAL; + } + + if (slot_width && + (slot_width != 16 && slot_width != 24 && slot_width != 32 )) { + dev_err(component->dev, "Unsupported slot_width %d\n", + slot_width); + return -EINVAL; + } + + io_params->tdm_slots = slots; + io_params->slot_width = slot_width; + /* Ignore the not relevant mask for the DAI/direction */ + if (dai->id == PCM3168A_DAI_DAC) + io_params->tdm_mask = tx_mask; + else + io_params->tdm_mask = rx_mask; + + pcm3168a_update_fixup_pcm_stream(dai); + + return 0; +} + +static int pcm3168a_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); + struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id]; + bool provider_mode, tdm_mode; + unsigned int format; + unsigned int reg, mask, ms, ms_shift, fmt, fmt_shift, ratio, tdm_slots; + int i, num_scki_ratios, slot_width; + + if (dai->id == PCM3168A_DAI_DAC) { + num_scki_ratios = PCM3168A_NUM_SCKI_RATIOS_DAC; + reg = PCM3168A_DAC_PWR_MST_FMT; + mask = PCM3168A_DAC_MSDA_MASK | PCM3168A_DAC_FMT_MASK; + ms_shift = PCM3168A_DAC_MSDA_SHIFT; + fmt_shift = PCM3168A_DAC_FMT_SHIFT; + } else { + num_scki_ratios = PCM3168A_NUM_SCKI_RATIOS_ADC; + reg = PCM3168A_ADC_MST_FMT; + mask = PCM3168A_ADC_MSAD_MASK | PCM3168A_ADC_FMTAD_MASK; + ms_shift = PCM3168A_ADC_MSAD_SHIFT; + fmt_shift = PCM3168A_ADC_FMTAD_SHIFT; + } + + provider_mode = io_params->provider_mode; + + if (provider_mode) { + ratio = pcm3168a->sysclk / params_rate(params); + + for (i = 0; i < num_scki_ratios; i++) { + if (pcm3168a_scki_ratios[i] == ratio) + break; + } + + if (i == num_scki_ratios) { + dev_err(component->dev, "unsupported sysclk ratio\n"); + return -EINVAL; + } + + ms = (i + 1); + } else { + ms = 0; + } + + format = io_params->format; + + if (io_params->slot_width) + slot_width = io_params->slot_width; + else + slot_width = params_width(params); + + switch (slot_width) { + case 16: + if (provider_mode || (format != SND_SOC_DAIFMT_RIGHT_J)) { + dev_err(component->dev, "16-bit slots are supported only for consumer mode using right justified\n"); + return -EINVAL; + } + break; + case 24: + if (provider_mode || (format == SND_SOC_DAIFMT_DSP_A) || + (format == SND_SOC_DAIFMT_DSP_B)) { + dev_err(component->dev, "24-bit slots not supported in provider mode, or consumer mode using DSP\n"); + return -EINVAL; + } + break; + case 32: + break; + default: + dev_err(component->dev, "unsupported frame size: %d\n", slot_width); + return -EINVAL; + } + + if (io_params->tdm_slots) + tdm_slots = io_params->tdm_slots; + else + tdm_slots = params_channels(params); + + /* + * Switch the codec to TDM mode when more than 2 TDM slots are needed + * for the stream. + * If pcm3168a->tdm_slots is not set or set to more than 2 (8/6 usually) + * then DIN1/DOUT1 is used in TDM mode. + * If pcm3168a->tdm_slots is set to 2 then DIN1/2/3/4 and DOUT1/2/3 is + * used in normal mode, no need to switch to TDM modes. + */ + tdm_mode = (tdm_slots > 2); + + if (tdm_mode) { + switch (format) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_DSP_B: + break; + default: + dev_err(component->dev, + "TDM is supported under DSP/I2S/Left_J only\n"); + return -EINVAL; + } + } + + switch (format) { + case SND_SOC_DAIFMT_I2S: + fmt = tdm_mode ? PCM3168A_FMT_I2S_TDM : PCM3168A_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + fmt = tdm_mode ? PCM3168A_FMT_LEFT_J_TDM : PCM3168A_FMT_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + fmt = (slot_width == 16) ? PCM3168A_FMT_RIGHT_J_16 : + PCM3168A_FMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_DSP_A: + fmt = tdm_mode ? PCM3168A_FMT_I2S_TDM : PCM3168A_FMT_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + fmt = tdm_mode ? PCM3168A_FMT_LEFT_J_TDM : PCM3168A_FMT_DSP_B; + break; + default: + return -EINVAL; + } + + regmap_update_bits(pcm3168a->regmap, reg, mask, + (ms << ms_shift) | (fmt << fmt_shift)); + + return 0; +} + +static u64 pcm3168a_dai_formats[] = { + /* + * Select below from Sound Card, not here + * SND_SOC_DAIFMT_CBC_CFC + * SND_SOC_DAIFMT_CBP_CFP + */ + + /* + * First Priority + */ + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J, + /* + * Second Priority + * + * These have picky limitation. + * see + * pcm3168a_hw_params() + */ + SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops pcm3168a_dai_ops = { + .set_fmt = pcm3168a_set_dai_fmt, + .set_sysclk = pcm3168a_set_dai_sysclk, + .hw_params = pcm3168a_hw_params, + .mute_stream = pcm3168a_mute, + .set_tdm_slot = pcm3168a_set_tdm_slot, + .no_capture_mute = 1, + .auto_selectable_formats = pcm3168a_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(pcm3168a_dai_formats), +}; + +static struct snd_soc_dai_driver pcm3168a_dais[] = { + { + .name = "pcm3168a-dac", + .id = PCM3168A_DAI_DAC, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = PCM3168A_FORMATS + }, + .ops = &pcm3168a_dai_ops + }, + { + .name = "pcm3168a-adc", + .id = PCM3168A_DAI_ADC, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 6, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = PCM3168A_FORMATS + }, + .ops = &pcm3168a_dai_ops + }, +}; + +static const struct reg_default pcm3168a_reg_default[] = { + { PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK }, + { PCM3168A_DAC_PWR_MST_FMT, 0x00 }, + { PCM3168A_DAC_OP_FLT, 0x00 }, + { PCM3168A_DAC_INV, 0x00 }, + { PCM3168A_DAC_MUTE, 0x00 }, + { PCM3168A_DAC_ZERO, 0x00 }, + { PCM3168A_DAC_ATT_DEMP_ZF, 0x00 }, + { PCM3168A_DAC_VOL_MASTER, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 1, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 2, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 3, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 4, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 5, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 6, 0xff }, + { PCM3168A_DAC_VOL_CHAN_START + 7, 0xff }, + { PCM3168A_ADC_SMODE, 0x00 }, + { PCM3168A_ADC_MST_FMT, 0x00 }, + { PCM3168A_ADC_PWR_HPFB, 0x00 }, + { PCM3168A_ADC_SEAD, 0x00 }, + { PCM3168A_ADC_INV, 0x00 }, + { PCM3168A_ADC_MUTE, 0x00 }, + { PCM3168A_ADC_OV, 0x00 }, + { PCM3168A_ADC_ATT_OVF, 0x00 }, + { PCM3168A_ADC_VOL_MASTER, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START + 1, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START + 2, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START + 3, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START + 4, 0xd3 }, + { PCM3168A_ADC_VOL_CHAN_START + 5, 0xd3 } +}; + +static bool pcm3168a_readable_register(struct device *dev, unsigned int reg) +{ + if (reg >= PCM3168A_RST_SMODE) + return true; + else + return false; +} + +static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PCM3168A_RST_SMODE: + case PCM3168A_DAC_ZERO: + case PCM3168A_ADC_OV: + return true; + default: + return false; + } +} + +static bool pcm3168a_writeable_register(struct device *dev, unsigned int reg) +{ + if (reg < PCM3168A_RST_SMODE) + return false; + + switch (reg) { + case PCM3168A_DAC_ZERO: + case PCM3168A_ADC_OV: + return false; + default: + return true; + } +} + +const struct regmap_config pcm3168a_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = PCM3168A_ADC_VOL_CHAN_START + 5, + .reg_defaults = pcm3168a_reg_default, + .num_reg_defaults = ARRAY_SIZE(pcm3168a_reg_default), + .readable_reg = pcm3168a_readable_register, + .volatile_reg = pcm3168a_volatile_register, + .writeable_reg = pcm3168a_writeable_register, + .cache_type = REGCACHE_FLAT +}; +EXPORT_SYMBOL_GPL(pcm3168a_regmap); + +static const struct snd_soc_component_driver pcm3168a_driver = { + .controls = pcm3168a_snd_controls, + .num_controls = ARRAY_SIZE(pcm3168a_snd_controls), + .dapm_widgets = pcm3168a_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets), + .dapm_routes = pcm3168a_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +int pcm3168a_probe(struct device *dev, struct regmap *regmap) +{ + struct pcm3168a_priv *pcm3168a; + int ret, i; + + pcm3168a = devm_kzalloc(dev, sizeof(*pcm3168a), GFP_KERNEL); + if (pcm3168a == NULL) + return -ENOMEM; + + dev_set_drvdata(dev, pcm3168a); + + /* + * Request the reset (connected to RST pin) gpio line as non exclusive + * as the same reset line might be connected to multiple pcm3168a codec + * + * The RST is low active, we want the GPIO line to be high initially, so + * request the initial level to LOW which in practice means DEASSERTED: + * The deasserted level of GPIO_ACTIVE_LOW is HIGH. + */ + pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW | + GPIOD_FLAGS_BIT_NONEXCLUSIVE); + if (IS_ERR(pcm3168a->gpio_rst)) + return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst), + "failed to acquire RST gpio\n"); + + pcm3168a->scki = devm_clk_get(dev, "scki"); + if (IS_ERR(pcm3168a->scki)) + return dev_err_probe(dev, PTR_ERR(pcm3168a->scki), + "failed to acquire clock 'scki'\n"); + + ret = clk_prepare_enable(pcm3168a->scki); + if (ret) { + dev_err(dev, "Failed to enable mclk: %d\n", ret); + return ret; + } + + pcm3168a->sysclk = clk_get_rate(pcm3168a->scki); + + for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++) + pcm3168a->supplies[i].supply = pcm3168a_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, + ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies); + if (ret) { + dev_err_probe(dev, ret, "failed to request supplies\n"); + goto err_clk; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); + if (ret) { + dev_err(dev, "failed to enable supplies: %d\n", ret); + goto err_clk; + } + + pcm3168a->regmap = regmap; + if (IS_ERR(pcm3168a->regmap)) { + ret = PTR_ERR(pcm3168a->regmap); + dev_err(dev, "failed to allocate regmap: %d\n", ret); + goto err_regulator; + } + + if (pcm3168a->gpio_rst) { + /* + * The device is taken out from reset via GPIO line, wait for + * 3846 SCKI clock cycles for the internal reset de-assertion + */ + msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk)); + } else { + ret = pcm3168a_reset(pcm3168a); + if (ret) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err_regulator; + } + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + memcpy(pcm3168a->dai_drv, pcm3168a_dais, sizeof(pcm3168a->dai_drv)); + ret = devm_snd_soc_register_component(dev, &pcm3168a_driver, + pcm3168a->dai_drv, + ARRAY_SIZE(pcm3168a->dai_drv)); + if (ret) { + dev_err(dev, "failed to register component: %d\n", ret); + goto err_regulator; + } + + return 0; + +err_regulator: + regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); +err_clk: + clk_disable_unprepare(pcm3168a->scki); + + return ret; +} +EXPORT_SYMBOL_GPL(pcm3168a_probe); + +static void pcm3168a_disable(struct device *dev) +{ + struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); + + regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); + clk_disable_unprepare(pcm3168a->scki); +} + +void pcm3168a_remove(struct device *dev) +{ + struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); + + /* + * The RST is low active, we want the GPIO line to be low when the + * driver is removed, so set level to 1 which in practice means + * ASSERTED: + * The asserted level of GPIO_ACTIVE_LOW is LOW. + */ + gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1); + pm_runtime_disable(dev); +#ifndef CONFIG_PM + pcm3168a_disable(dev); +#endif +} +EXPORT_SYMBOL_GPL(pcm3168a_remove); + +#ifdef CONFIG_PM +static int pcm3168a_rt_resume(struct device *dev) +{ + struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(pcm3168a->scki); + if (ret) { + dev_err(dev, "Failed to enable mclk: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); + if (ret) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + goto err_clk; + } + + ret = pcm3168a_reset(pcm3168a); + if (ret) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err_regulator; + } + + regcache_cache_only(pcm3168a->regmap, false); + + regcache_mark_dirty(pcm3168a->regmap); + + ret = regcache_sync(pcm3168a->regmap); + if (ret) { + dev_err(dev, "Failed to sync regmap: %d\n", ret); + goto err_regulator; + } + + return 0; + +err_regulator: + regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), + pcm3168a->supplies); +err_clk: + clk_disable_unprepare(pcm3168a->scki); + + return ret; +} + +static int pcm3168a_rt_suspend(struct device *dev) +{ + struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); + + regcache_cache_only(pcm3168a->regmap, true); + + pcm3168a_disable(dev); + + return 0; +} +#endif + +const struct dev_pm_ops pcm3168a_pm_ops = { + SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL) +}; +EXPORT_SYMBOL_GPL(pcm3168a_pm_ops); + +MODULE_DESCRIPTION("PCM3168A codec driver"); +MODULE_AUTHOR("Damien Horsley "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h new file mode 100644 index 0000000000..c4b7140dce --- /dev/null +++ b/sound/soc/codecs/pcm3168a.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * PCM3168A codec driver header + * + * Copyright (C) 2015 Imagination Technologies Ltd. + * + * Author: Damien Horsley + */ + +#ifndef __PCM3168A_H__ +#define __PCM3168A_H__ + +extern const struct dev_pm_ops pcm3168a_pm_ops; +extern const struct regmap_config pcm3168a_regmap; + +extern int pcm3168a_probe(struct device *dev, struct regmap *regmap); +extern void pcm3168a_remove(struct device *dev); + +#define PCM3168A_RST_SMODE 0x40 +#define PCM3168A_MRST_MASK 0x80 +#define PCM3168A_SRST_MASK 0x40 +#define PCM3168A_DAC_SRDA_SHIFT 0 +#define PCM3168A_DAC_SRDA_MASK 0x3 + +#define PCM3168A_DAC_PWR_MST_FMT 0x41 +#define PCM3168A_DAC_PSMDA_SHIFT 7 +#define PCM3168A_DAC_PSMDA_MASK 0x80 +#define PCM3168A_DAC_MSDA_SHIFT 4 +#define PCM3168A_DAC_MSDA_MASK 0x70 +#define PCM3168A_DAC_FMT_SHIFT 0 +#define PCM3168A_DAC_FMT_MASK 0xf + +#define PCM3168A_DAC_OP_FLT 0x42 +#define PCM3168A_DAC_OPEDA_SHIFT 4 +#define PCM3168A_DAC_OPEDA_MASK 0xf0 +#define PCM3168A_DAC_FLT_SHIFT 0 +#define PCM3168A_DAC_FLT_MASK 0xf + +#define PCM3168A_DAC_INV 0x43 + +#define PCM3168A_DAC_MUTE 0x44 + +#define PCM3168A_DAC_ZERO 0x45 + +#define PCM3168A_DAC_ATT_DEMP_ZF 0x46 +#define PCM3168A_DAC_ATMDDA_MASK 0x80 +#define PCM3168A_DAC_ATMDDA_SHIFT 7 +#define PCM3168A_DAC_ATSPDA_MASK 0x40 +#define PCM3168A_DAC_ATSPDA_SHIFT 6 +#define PCM3168A_DAC_DEMP_SHIFT 4 +#define PCM3168A_DAC_DEMP_MASK 0x30 +#define PCM3168A_DAC_AZRO_SHIFT 1 +#define PCM3168A_DAC_AZRO_MASK 0xe +#define PCM3168A_DAC_ZREV_MASK 0x1 +#define PCM3168A_DAC_ZREV_SHIFT 0 + +#define PCM3168A_DAC_VOL_MASTER 0x47 + +#define PCM3168A_DAC_VOL_CHAN_START 0x48 + +#define PCM3168A_ADC_SMODE 0x50 +#define PCM3168A_ADC_SRAD_SHIFT 0 +#define PCM3168A_ADC_SRAD_MASK 0x3 + +#define PCM3168A_ADC_MST_FMT 0x51 +#define PCM3168A_ADC_MSAD_SHIFT 4 +#define PCM3168A_ADC_MSAD_MASK 0x70 +#define PCM3168A_ADC_FMTAD_SHIFT 0 +#define PCM3168A_ADC_FMTAD_MASK 0x7 + +#define PCM3168A_ADC_PWR_HPFB 0x52 +#define PCM3168A_ADC_PSVAD_SHIFT 4 +#define PCM3168A_ADC_PSVAD_MASK 0x70 +#define PCM3168A_ADC_BYP_SHIFT 0 +#define PCM3168A_ADC_BYP_MASK 0x7 + +#define PCM3168A_ADC_SEAD 0x53 + +#define PCM3168A_ADC_INV 0x54 + +#define PCM3168A_ADC_MUTE 0x55 + +#define PCM3168A_ADC_OV 0x56 + +#define PCM3168A_ADC_ATT_OVF 0x57 +#define PCM3168A_ADC_ATMDAD_MASK 0x80 +#define PCM3168A_ADC_ATMDAD_SHIFT 7 +#define PCM3168A_ADC_ATSPAD_MASK 0x40 +#define PCM3168A_ADC_ATSPAD_SHIFT 6 +#define PCM3168A_ADC_OVFP_MASK 0x1 +#define PCM3168A_ADC_OVFP_SHIFT 0 + +#define PCM3168A_ADC_VOL_MASTER 0x58 + +#define PCM3168A_ADC_VOL_CHAN_START 0x59 + +#endif diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c new file mode 100644 index 0000000000..3401a25341 --- /dev/null +++ b/sound/soc/codecs/pcm5102a.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the PCM5102A codec + * + * Author: Florian Meier + * Copyright 2013 + */ + +#include +#include +#include + +#include + +static struct snd_soc_dai_driver pcm5102a_dai = { + .name = "pcm5102a-hifi", + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE + }, +}; + +static struct snd_soc_component_driver soc_component_dev_pcm5102a = { + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int pcm5102a_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_pcm5102a, + &pcm5102a_dai, 1); +} + +static const struct of_device_id pcm5102a_of_match[] = { + { .compatible = "ti,pcm5102a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm5102a_of_match); + +static struct platform_driver pcm5102a_codec_driver = { + .probe = pcm5102a_probe, + .driver = { + .name = "pcm5102a-codec", + .of_match_table = pcm5102a_of_match, + }, +}; + +module_platform_driver(pcm5102a_codec_driver); + +MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); +MODULE_AUTHOR("Florian Meier "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c new file mode 100644 index 0000000000..5cd2b64b93 --- /dev/null +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the PCM512x CODECs + * + * Author: Mark Brown + * Copyright 2014 Linaro Ltd + */ + +#include +#include +#include +#include + +#include "pcm512x.h" + +static int pcm512x_i2c_probe(struct i2c_client *i2c) +{ + struct regmap *regmap; + struct regmap_config config = pcm512x_regmap; + + /* msb needs to be set to enable auto-increment of addresses */ + config.read_flag_mask = 0x80; + config.write_flag_mask = 0x80; + + regmap = devm_regmap_init_i2c(i2c, &config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return pcm512x_probe(&i2c->dev, regmap); +} + +static void pcm512x_i2c_remove(struct i2c_client *i2c) +{ + pcm512x_remove(&i2c->dev); +} + +static const struct i2c_device_id pcm512x_i2c_id[] = { + { "pcm5121", }, + { "pcm5122", }, + { "pcm5141", }, + { "pcm5142", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id pcm512x_of_match[] = { + { .compatible = "ti,pcm5121", }, + { .compatible = "ti,pcm5122", }, + { .compatible = "ti,pcm5141", }, + { .compatible = "ti,pcm5142", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm512x_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id pcm512x_acpi_match[] = { + { "104C5121", 0 }, + { "104C5122", 0 }, + { "104C5141", 0 }, + { "104C5142", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match); +#endif + +static struct i2c_driver pcm512x_i2c_driver = { + .probe = pcm512x_i2c_probe, + .remove = pcm512x_i2c_remove, + .id_table = pcm512x_i2c_id, + .driver = { + .name = "pcm512x", + .of_match_table = of_match_ptr(pcm512x_of_match), + .acpi_match_table = ACPI_PTR(pcm512x_acpi_match), + .pm = &pcm512x_pm_ops, + }, +}; + +module_i2c_driver(pcm512x_i2c_driver); + +MODULE_DESCRIPTION("ASoC PCM512x codec driver - I2C"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c new file mode 100644 index 0000000000..4d29e71963 --- /dev/null +++ b/sound/soc/codecs/pcm512x-spi.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the PCM512x CODECs + * + * Author: Mark Brown + * Copyright 2014 Linaro Ltd + */ + +#include +#include +#include + +#include "pcm512x.h" + +static int pcm512x_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + int ret; + + regmap = devm_regmap_init_spi(spi, &pcm512x_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + return ret; + } + + return pcm512x_probe(&spi->dev, regmap); +} + +static void pcm512x_spi_remove(struct spi_device *spi) +{ + pcm512x_remove(&spi->dev); +} + +static const struct spi_device_id pcm512x_spi_id[] = { + { "pcm5121", }, + { "pcm5122", }, + { "pcm5141", }, + { "pcm5142", }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); + +static const struct of_device_id pcm512x_of_match[] = { + { .compatible = "ti,pcm5121", }, + { .compatible = "ti,pcm5122", }, + { .compatible = "ti,pcm5141", }, + { .compatible = "ti,pcm5142", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm512x_of_match); + +static struct spi_driver pcm512x_spi_driver = { + .probe = pcm512x_spi_probe, + .remove = pcm512x_spi_remove, + .id_table = pcm512x_spi_id, + .driver = { + .name = "pcm512x", + .of_match_table = pcm512x_of_match, + .pm = &pcm512x_pm_ops, + }, +}; + +module_spi_driver(pcm512x_spi_driver); + +MODULE_DESCRIPTION("ASoC PCM512x codec driver - SPI"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c new file mode 100644 index 0000000000..89059a673c --- /dev/null +++ b/sound/soc/codecs/pcm512x.c @@ -0,0 +1,1769 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the PCM512x CODECs + * + * Author: Mark Brown + * Copyright 2014 Linaro Ltd + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcm512x.h" + +#define PCM512x_NUM_SUPPLIES 3 +static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = { + "AVDD", + "DVDD", + "CPVDD", +}; + +struct pcm512x_priv { + struct regmap *regmap; + struct clk *sclk; + struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; + struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; + int fmt; + int pll_in; + int pll_out; + int pll_r; + int pll_j; + int pll_d; + int pll_p; + unsigned long real_pll; + unsigned long overclock_pll; + unsigned long overclock_dac; + unsigned long overclock_dsp; + int mute; + struct mutex mutex; + unsigned int bclk_ratio; +}; + +/* + * We can't use the same notifier block for more than one supply and + * there's no way I can see to get from a callback to the caller + * except container_of(). + */ +#define PCM512x_REGULATOR_EVENT(n) \ +static int pcm512x_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct pcm512x_priv *pcm512x = container_of(nb, struct pcm512x_priv, \ + supply_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + regcache_mark_dirty(pcm512x->regmap); \ + regcache_cache_only(pcm512x->regmap, true); \ + } \ + return 0; \ +} + +PCM512x_REGULATOR_EVENT(0) +PCM512x_REGULATOR_EVENT(1) +PCM512x_REGULATOR_EVENT(2) + +static const struct reg_default pcm512x_reg_defaults[] = { + { PCM512x_RESET, 0x00 }, + { PCM512x_POWER, 0x00 }, + { PCM512x_MUTE, 0x00 }, + { PCM512x_DSP, 0x00 }, + { PCM512x_PLL_REF, 0x00 }, + { PCM512x_DAC_REF, 0x00 }, + { PCM512x_DAC_ROUTING, 0x11 }, + { PCM512x_DSP_PROGRAM, 0x01 }, + { PCM512x_CLKDET, 0x00 }, + { PCM512x_AUTO_MUTE, 0x00 }, + { PCM512x_ERROR_DETECT, 0x00 }, + { PCM512x_DIGITAL_VOLUME_1, 0x00 }, + { PCM512x_DIGITAL_VOLUME_2, 0x30 }, + { PCM512x_DIGITAL_VOLUME_3, 0x30 }, + { PCM512x_DIGITAL_MUTE_1, 0x22 }, + { PCM512x_DIGITAL_MUTE_2, 0x00 }, + { PCM512x_DIGITAL_MUTE_3, 0x07 }, + { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, + { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, + { PCM512x_UNDERVOLTAGE_PROT, 0x00 }, + { PCM512x_ANALOG_MUTE_CTRL, 0x00 }, + { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, + { PCM512x_VCOM_CTRL_1, 0x00 }, + { PCM512x_VCOM_CTRL_2, 0x01 }, + { PCM512x_BCLK_LRCLK_CFG, 0x00 }, + { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_GPIO_DACIN, 0x00 }, + { PCM512x_GPIO_PLLIN, 0x00 }, + { PCM512x_SYNCHRONIZE, 0x10 }, + { PCM512x_PLL_COEFF_0, 0x00 }, + { PCM512x_PLL_COEFF_1, 0x00 }, + { PCM512x_PLL_COEFF_2, 0x00 }, + { PCM512x_PLL_COEFF_3, 0x00 }, + { PCM512x_PLL_COEFF_4, 0x00 }, + { PCM512x_DSP_CLKDIV, 0x00 }, + { PCM512x_DAC_CLKDIV, 0x00 }, + { PCM512x_NCP_CLKDIV, 0x00 }, + { PCM512x_OSR_CLKDIV, 0x00 }, + { PCM512x_MASTER_CLKDIV_1, 0x00 }, + { PCM512x_MASTER_CLKDIV_2, 0x00 }, + { PCM512x_FS_SPEED_MODE, 0x00 }, + { PCM512x_IDAC_1, 0x01 }, + { PCM512x_IDAC_2, 0x00 }, + { PCM512x_I2S_1, 0x02 }, + { PCM512x_I2S_2, 0x00 }, +}; + +static bool pcm512x_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PCM512x_RESET: + case PCM512x_POWER: + case PCM512x_MUTE: + case PCM512x_PLL_EN: + case PCM512x_SPI_MISO_FUNCTION: + case PCM512x_DSP: + case PCM512x_GPIO_EN: + case PCM512x_BCLK_LRCLK_CFG: + case PCM512x_DSP_GPIO_INPUT: + case PCM512x_MASTER_MODE: + case PCM512x_PLL_REF: + case PCM512x_DAC_REF: + case PCM512x_GPIO_DACIN: + case PCM512x_GPIO_PLLIN: + case PCM512x_SYNCHRONIZE: + case PCM512x_PLL_COEFF_0: + case PCM512x_PLL_COEFF_1: + case PCM512x_PLL_COEFF_2: + case PCM512x_PLL_COEFF_3: + case PCM512x_PLL_COEFF_4: + case PCM512x_DSP_CLKDIV: + case PCM512x_DAC_CLKDIV: + case PCM512x_NCP_CLKDIV: + case PCM512x_OSR_CLKDIV: + case PCM512x_MASTER_CLKDIV_1: + case PCM512x_MASTER_CLKDIV_2: + case PCM512x_FS_SPEED_MODE: + case PCM512x_IDAC_1: + case PCM512x_IDAC_2: + case PCM512x_ERROR_DETECT: + case PCM512x_I2S_1: + case PCM512x_I2S_2: + case PCM512x_DAC_ROUTING: + case PCM512x_DSP_PROGRAM: + case PCM512x_CLKDET: + case PCM512x_AUTO_MUTE: + case PCM512x_DIGITAL_VOLUME_1: + case PCM512x_DIGITAL_VOLUME_2: + case PCM512x_DIGITAL_VOLUME_3: + case PCM512x_DIGITAL_MUTE_1: + case PCM512x_DIGITAL_MUTE_2: + case PCM512x_DIGITAL_MUTE_3: + case PCM512x_GPIO_OUTPUT_1: + case PCM512x_GPIO_OUTPUT_2: + case PCM512x_GPIO_OUTPUT_3: + case PCM512x_GPIO_OUTPUT_4: + case PCM512x_GPIO_OUTPUT_5: + case PCM512x_GPIO_OUTPUT_6: + case PCM512x_GPIO_CONTROL_1: + case PCM512x_GPIO_CONTROL_2: + case PCM512x_OVERFLOW: + case PCM512x_RATE_DET_1: + case PCM512x_RATE_DET_2: + case PCM512x_RATE_DET_3: + case PCM512x_RATE_DET_4: + case PCM512x_CLOCK_STATUS: + case PCM512x_ANALOG_MUTE_DET: + case PCM512x_GPIN: + case PCM512x_DIGITAL_MUTE_DET: + case PCM512x_OUTPUT_AMPLITUDE: + case PCM512x_ANALOG_GAIN_CTRL: + case PCM512x_UNDERVOLTAGE_PROT: + case PCM512x_ANALOG_MUTE_CTRL: + case PCM512x_ANALOG_GAIN_BOOST: + case PCM512x_VCOM_CTRL_1: + case PCM512x_VCOM_CTRL_2: + case PCM512x_CRAM_CTRL: + case PCM512x_FLEX_A: + case PCM512x_FLEX_B: + return true; + default: + /* There are 256 raw register addresses */ + return reg < 0xff; + } +} + +static bool pcm512x_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case PCM512x_PLL_EN: + case PCM512x_OVERFLOW: + case PCM512x_RATE_DET_1: + case PCM512x_RATE_DET_2: + case PCM512x_RATE_DET_3: + case PCM512x_RATE_DET_4: + case PCM512x_CLOCK_STATUS: + case PCM512x_ANALOG_MUTE_DET: + case PCM512x_GPIN: + case PCM512x_DIGITAL_MUTE_DET: + case PCM512x_CRAM_CTRL: + return true; + default: + /* There are 256 raw register addresses */ + return reg < 0xff; + } +} + +static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = pcm512x->overclock_pll; + return 0; +} + +static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + switch (snd_soc_component_get_bias_level(component)) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x->overclock_pll = ucontrol->value.integer.value[0]; + return 0; +} + +static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = pcm512x->overclock_dsp; + return 0; +} + +static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + switch (snd_soc_component_get_bias_level(component)) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x->overclock_dsp = ucontrol->value.integer.value[0]; + return 0; +} + +static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = pcm512x->overclock_dac; + return 0; +} + +static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + switch (snd_soc_component_get_bias_level(component)) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x->overclock_dac = ucontrol->value.integer.value[0]; + return 0; +} + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1); +static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0); + +static const char * const pcm512x_dsp_program_texts[] = { + "FIR interpolation with de-emphasis", + "Low latency IIR with de-emphasis", + "High attenuation with de-emphasis", + "Fixed process flow", + "Ringing-less low latency FIR", +}; + +static const unsigned int pcm512x_dsp_program_values[] = { + 1, + 2, + 3, + 5, + 7, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pcm512x_dsp_program, + PCM512x_DSP_PROGRAM, 0, 0x1f, + pcm512x_dsp_program_texts, + pcm512x_dsp_program_values); + +static const char * const pcm512x_clk_missing_text[] = { + "1s", "2s", "3s", "4s", "5s", "6s", "7s", "8s" +}; + +static const struct soc_enum pcm512x_clk_missing = + SOC_ENUM_SINGLE(PCM512x_CLKDET, 0, 8, pcm512x_clk_missing_text); + +static const char * const pcm512x_autom_text[] = { + "21ms", "106ms", "213ms", "533ms", "1.07s", "2.13s", "5.33s", "10.66s" +}; + +static const struct soc_enum pcm512x_autom_l = + SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATML_SHIFT, 8, + pcm512x_autom_text); + +static const struct soc_enum pcm512x_autom_r = + SOC_ENUM_SINGLE(PCM512x_AUTO_MUTE, PCM512x_ATMR_SHIFT, 8, + pcm512x_autom_text); + +static const char * const pcm512x_ramp_rate_text[] = { + "1 sample/update", "2 samples/update", "4 samples/update", + "Immediate" +}; + +static const struct soc_enum pcm512x_vndf = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDF_SHIFT, 4, + pcm512x_ramp_rate_text); + +static const struct soc_enum pcm512x_vnuf = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUF_SHIFT, 4, + pcm512x_ramp_rate_text); + +static const struct soc_enum pcm512x_vedf = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDF_SHIFT, 4, + pcm512x_ramp_rate_text); + +static const char * const pcm512x_ramp_step_text[] = { + "4dB/step", "2dB/step", "1dB/step", "0.5dB/step" +}; + +static const struct soc_enum pcm512x_vnds = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNDS_SHIFT, 4, + pcm512x_ramp_step_text); + +static const struct soc_enum pcm512x_vnus = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_1, PCM512x_VNUS_SHIFT, 4, + pcm512x_ramp_step_text); + +static const struct soc_enum pcm512x_veds = + SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4, + pcm512x_ramp_step_text); + +static int pcm512x_update_mute(struct pcm512x_priv *pcm512x) +{ + return regmap_update_bits( + pcm512x->regmap, PCM512x_MUTE, PCM512x_RQML | PCM512x_RQMR, + (!!(pcm512x->mute & 0x5) << PCM512x_RQML_SHIFT) + | (!!(pcm512x->mute & 0x3) << PCM512x_RQMR_SHIFT)); +} + +static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + mutex_lock(&pcm512x->mutex); + ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4); + ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2); + mutex_unlock(&pcm512x->mutex); + + return 0; +} + +static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int ret, changed = 0; + + mutex_lock(&pcm512x->mutex); + + if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) { + pcm512x->mute ^= 0x4; + changed = 1; + } + if ((pcm512x->mute & 0x2) == (ucontrol->value.integer.value[1] << 1)) { + pcm512x->mute ^= 0x2; + changed = 1; + } + + if (changed) { + ret = pcm512x_update_mute(pcm512x); + if (ret != 0) { + dev_err(component->dev, + "Failed to update digital mute: %d\n", ret); + mutex_unlock(&pcm512x->mutex); + return ret; + } + } + + mutex_unlock(&pcm512x->mutex); + + return changed; +} + +static const struct snd_kcontrol_new pcm512x_controls[] = { +SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2, + PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv), +SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL, + PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv), +SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST, + PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Playback Switch", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_stereo_info, + .get = pcm512x_digital_playback_switch_get, + .put = pcm512x_digital_playback_switch_put +}, + +SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1), +SOC_ENUM("DSP Program", pcm512x_dsp_program), + +SOC_ENUM("Clock Missing Period", pcm512x_clk_missing), +SOC_ENUM("Auto Mute Time Left", pcm512x_autom_l), +SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r), +SOC_SINGLE("Auto Mute Mono Switch", PCM512x_DIGITAL_MUTE_3, + PCM512x_ACTL_SHIFT, 1, 0), +SOC_DOUBLE("Auto Mute Switch", PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT, + PCM512x_AMRE_SHIFT, 1, 0), + +SOC_ENUM("Volume Ramp Down Rate", pcm512x_vndf), +SOC_ENUM("Volume Ramp Down Step", pcm512x_vnds), +SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf), +SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus), +SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf), +SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds), + +SOC_SINGLE_EXT("Max Overclock PLL", SND_SOC_NOPM, 0, 20, 0, + pcm512x_overclock_pll_get, pcm512x_overclock_pll_put), +SOC_SINGLE_EXT("Max Overclock DSP", SND_SOC_NOPM, 0, 40, 0, + pcm512x_overclock_dsp_get, pcm512x_overclock_dsp_put), +SOC_SINGLE_EXT("Max Overclock DAC", SND_SOC_NOPM, 0, 40, 0, + pcm512x_overclock_dac_get, pcm512x_overclock_dac_put), +}; + +static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_OUTPUT("OUTL"), +SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { + { "DACL", NULL, "Playback" }, + { "DACR", NULL, "Playback" }, + + { "OUTL", NULL, "DACL" }, + { "OUTR", NULL, "DACR" }, +}; + +static unsigned long pcm512x_pll_max(struct pcm512x_priv *pcm512x) +{ + return 25000000 + 25000000 * pcm512x->overclock_pll / 100; +} + +static unsigned long pcm512x_dsp_max(struct pcm512x_priv *pcm512x) +{ + return 50000000 + 50000000 * pcm512x->overclock_dsp / 100; +} + +static unsigned long pcm512x_dac_max(struct pcm512x_priv *pcm512x, + unsigned long rate) +{ + return rate + rate * pcm512x->overclock_dac / 100; +} + +static unsigned long pcm512x_sck_max(struct pcm512x_priv *pcm512x) +{ + if (!pcm512x->pll_out) + return 25000000; + return pcm512x_pll_max(pcm512x); +} + +static unsigned long pcm512x_ncp_target(struct pcm512x_priv *pcm512x, + unsigned long dac_rate) +{ + /* + * If the DAC is not actually overclocked, use the good old + * NCP target rate... + */ + if (dac_rate <= 6144000) + return 1536000; + /* + * ...but if the DAC is in fact overclocked, bump the NCP target + * rate to get the recommended dividers even when overclocking. + */ + return pcm512x_dac_max(pcm512x, 1536000); +} + +static const u32 pcm512x_dai_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000, 384000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_slave = { + .count = ARRAY_SIZE(pcm512x_dai_rates), + .list = pcm512x_dai_rates, +}; + +static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct pcm512x_priv *pcm512x = rule->private; + struct snd_interval ranges[2]; + int frame_size; + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) + return frame_size; + + switch (frame_size) { + case 32: + /* No hole when the frame size is 32. */ + return 0; + case 48: + case 64: + /* There is only one hole in the range of supported + * rates, but it moves with the frame size. + */ + memset(ranges, 0, sizeof(ranges)); + ranges[0].min = 8000; + ranges[0].max = pcm512x_sck_max(pcm512x) / frame_size / 2; + ranges[1].min = DIV_ROUND_UP(16000000, frame_size); + ranges[1].max = 384000; + break; + default: + return -EINVAL; + } + + return snd_interval_ranges(hw_param_interval(params, rule->var), + ARRAY_SIZE(ranges), ranges, 0); +} + +static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + struct device *dev = dai->dev; + struct snd_pcm_hw_constraint_ratnums *constraints_no_pll; + struct snd_ratnum *rats_no_pll; + + if (IS_ERR(pcm512x->sclk)) { + dev_err(dev, "Need SCLK for master mode: %ld\n", + PTR_ERR(pcm512x->sclk)); + return PTR_ERR(pcm512x->sclk); + } + + if (pcm512x->pll_out) + return snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + pcm512x_hw_rule_rate, + pcm512x, + SNDRV_PCM_HW_PARAM_FRAME_BITS, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + constraints_no_pll = devm_kzalloc(dev, sizeof(*constraints_no_pll), + GFP_KERNEL); + if (!constraints_no_pll) + return -ENOMEM; + constraints_no_pll->nrats = 1; + rats_no_pll = devm_kzalloc(dev, sizeof(*rats_no_pll), GFP_KERNEL); + if (!rats_no_pll) + return -ENOMEM; + constraints_no_pll->rats = rats_no_pll; + rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64; + rats_no_pll->den_min = 1; + rats_no_pll->den_max = 128; + rats_no_pll->den_step = 1; + + return snd_pcm_hw_constraint_ratnums(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + constraints_no_pll); +} + +static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + struct device *dev = dai->dev; + struct regmap *regmap = pcm512x->regmap; + + if (IS_ERR(pcm512x->sclk)) { + dev_info(dev, "No SCLK, using BCLK: %ld\n", + PTR_ERR(pcm512x->sclk)); + + /* Disable reporting of missing SCLK as an error */ + regmap_update_bits(regmap, PCM512x_ERROR_DETECT, + PCM512x_IDCH, PCM512x_IDCH); + + /* Switch PLL input to BCLK */ + regmap_update_bits(regmap, PCM512x_PLL_REF, + PCM512x_SREF, PCM512x_SREF_BCK); + } + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_slave); +} + +static int pcm512x_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + switch (pcm512x->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + case SND_SOC_DAIFMT_CBP_CFC: + return pcm512x_dai_startup_master(substream, dai); + + case SND_SOC_DAIFMT_CBC_CFC: + return pcm512x_dai_startup_slave(substream, dai); + + default: + return -EINVAL; + } +} + +static int pcm512x_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(component->dev); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQST, 0); + if (ret != 0) { + dev_err(component->dev, "Failed to remove standby: %d\n", + ret); + return ret; + } + break; + + case SND_SOC_BIAS_OFF: + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQST, PCM512x_RQST); + if (ret != 0) { + dev_err(component->dev, "Failed to request standby: %d\n", + ret); + return ret; + } + break; + } + + return 0; +} + +static unsigned long pcm512x_find_sck(struct snd_soc_dai *dai, + unsigned long bclk_rate) +{ + struct device *dev = dai->dev; + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + unsigned long sck_rate; + int pow2; + + /* 64 MHz <= pll_rate <= 100 MHz, VREF mode */ + /* 16 MHz <= sck_rate <= 25 MHz, VREF mode */ + + /* select sck_rate as a multiple of bclk_rate but still with + * as many factors of 2 as possible, as that makes it easier + * to find a fast DAC rate + */ + pow2 = 1 << fls((pcm512x_pll_max(pcm512x) - 16000000) / bclk_rate); + for (; pow2; pow2 >>= 1) { + sck_rate = rounddown(pcm512x_pll_max(pcm512x), + bclk_rate * pow2); + if (sck_rate >= 16000000) + break; + } + if (!pow2) { + dev_err(dev, "Impossible to generate a suitable SCK\n"); + return 0; + } + + dev_dbg(dev, "sck_rate %lu\n", sck_rate); + return sck_rate; +} + +/* pll_rate = pllin_rate * R * J.D / P + * 1 <= R <= 16 + * 1 <= J <= 63 + * 0 <= D <= 9999 + * 1 <= P <= 15 + * 64 MHz <= pll_rate <= 100 MHz + * if D == 0 + * 1 MHz <= pllin_rate / P <= 20 MHz + * else if D > 0 + * 6.667 MHz <= pllin_rate / P <= 20 MHz + * 4 <= J <= 11 + * R = 1 + */ +static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai, + unsigned long pllin_rate, + unsigned long pll_rate) +{ + struct device *dev = dai->dev; + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + unsigned long common; + int R, J, D, P; + unsigned long K; /* 10000 * J.D */ + unsigned long num; + unsigned long den; + + common = gcd(pll_rate, pllin_rate); + dev_dbg(dev, "pll %lu pllin %lu common %lu\n", + pll_rate, pllin_rate, common); + num = pll_rate / common; + den = pllin_rate / common; + + /* pllin_rate / P (or here, den) cannot be greater than 20 MHz */ + if (pllin_rate / den > 20000000 && num < 8) { + num *= DIV_ROUND_UP(pllin_rate / den, 20000000); + den *= DIV_ROUND_UP(pllin_rate / den, 20000000); + } + dev_dbg(dev, "num / den = %lu / %lu\n", num, den); + + P = den; + if (den <= 15 && num <= 16 * 63 + && 1000000 <= pllin_rate / P && pllin_rate / P <= 20000000) { + /* Try the case with D = 0 */ + D = 0; + /* factor 'num' into J and R, such that R <= 16 and J <= 63 */ + for (R = 16; R; R--) { + if (num % R) + continue; + J = num / R; + if (J == 0 || J > 63) + continue; + + dev_dbg(dev, "R * J / P = %d * %d / %d\n", R, J, P); + pcm512x->real_pll = pll_rate; + goto done; + } + /* no luck */ + } + + R = 1; + + if (num > 0xffffffffUL / 10000) + goto fallback; + + /* Try to find an exact pll_rate using the D > 0 case */ + common = gcd(10000 * num, den); + num = 10000 * num / common; + den /= common; + dev_dbg(dev, "num %lu den %lu common %lu\n", num, den, common); + + for (P = den; P <= 15; P++) { + if (pllin_rate / P < 6667000 || 200000000 < pllin_rate / P) + continue; + if (num * P % den) + continue; + K = num * P / den; + /* J == 12 is ok if D == 0 */ + if (K < 40000 || K > 120000) + continue; + + J = K / 10000; + D = K % 10000; + dev_dbg(dev, "J.D / P = %d.%04d / %d\n", J, D, P); + pcm512x->real_pll = pll_rate; + goto done; + } + + /* Fall back to an approximate pll_rate */ + +fallback: + /* find smallest possible P */ + P = DIV_ROUND_UP(pllin_rate, 20000000); + if (!P) + P = 1; + else if (P > 15) { + dev_err(dev, "Need a slower clock as pll-input\n"); + return -EINVAL; + } + if (pllin_rate / P < 6667000) { + dev_err(dev, "Need a faster clock as pll-input\n"); + return -EINVAL; + } + K = DIV_ROUND_CLOSEST_ULL(10000ULL * pll_rate * P, pllin_rate); + if (K < 40000) + K = 40000; + /* J == 12 is ok if D == 0 */ + if (K > 120000) + K = 120000; + J = K / 10000; + D = K % 10000; + dev_dbg(dev, "J.D / P ~ %d.%04d / %d\n", J, D, P); + pcm512x->real_pll = DIV_ROUND_DOWN_ULL((u64)K * pllin_rate, 10000 * P); + +done: + pcm512x->pll_r = R; + pcm512x->pll_j = J; + pcm512x->pll_d = D; + pcm512x->pll_p = P; + return 0; +} + +static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai, + unsigned long osr_rate, + unsigned long pllin_rate) +{ + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + unsigned long dac_rate; + + if (!pcm512x->pll_out) + return 0; /* no PLL to bypass, force SCK as DAC input */ + + if (pllin_rate % osr_rate) + return 0; /* futile, quit early */ + + /* run DAC no faster than 6144000 Hz */ + for (dac_rate = rounddown(pcm512x_dac_max(pcm512x, 6144000), osr_rate); + dac_rate; + dac_rate -= osr_rate) { + + if (pllin_rate / dac_rate > 128) + return 0; /* DAC divider would be too big */ + + if (!(pllin_rate % dac_rate)) + return dac_rate; + + dac_rate -= osr_rate; + } + + return 0; +} + +static int pcm512x_set_dividers(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params) +{ + struct device *dev = dai->dev; + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + unsigned long pllin_rate = 0; + unsigned long pll_rate; + unsigned long sck_rate; + unsigned long mck_rate; + unsigned long bclk_rate; + unsigned long sample_rate; + unsigned long osr_rate; + unsigned long dacsrc_rate; + int bclk_div; + int lrclk_div; + int dsp_div; + int dac_div; + unsigned long dac_rate; + int ncp_div; + int osr_div; + int ret; + int idac; + int fssp; + int gpio; + + if (pcm512x->bclk_ratio > 0) { + lrclk_div = pcm512x->bclk_ratio; + } else { + lrclk_div = snd_soc_params_to_frame_size(params); + + if (lrclk_div == 0) { + dev_err(dev, "No LRCLK?\n"); + return -EINVAL; + } + } + + if (!pcm512x->pll_out) { + sck_rate = clk_get_rate(pcm512x->sclk); + bclk_rate = params_rate(params) * lrclk_div; + bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate); + + mck_rate = sck_rate; + } else { + ret = snd_soc_params_to_bclk(params); + if (ret < 0) { + dev_err(dev, "Failed to find suitable BCLK: %d\n", ret); + return ret; + } + if (ret == 0) { + dev_err(dev, "No BCLK?\n"); + return -EINVAL; + } + bclk_rate = ret; + + pllin_rate = clk_get_rate(pcm512x->sclk); + + sck_rate = pcm512x_find_sck(dai, bclk_rate); + if (!sck_rate) + return -EINVAL; + pll_rate = 4 * sck_rate; + + ret = pcm512x_find_pll_coeff(dai, pllin_rate, pll_rate); + if (ret != 0) + return ret; + + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_0, pcm512x->pll_p - 1); + if (ret != 0) { + dev_err(dev, "Failed to write PLL P: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_1, pcm512x->pll_j); + if (ret != 0) { + dev_err(dev, "Failed to write PLL J: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_2, pcm512x->pll_d >> 8); + if (ret != 0) { + dev_err(dev, "Failed to write PLL D msb: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_3, pcm512x->pll_d & 0xff); + if (ret != 0) { + dev_err(dev, "Failed to write PLL D lsb: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_PLL_COEFF_4, pcm512x->pll_r - 1); + if (ret != 0) { + dev_err(dev, "Failed to write PLL R: %d\n", ret); + return ret; + } + + mck_rate = pcm512x->real_pll; + + bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate); + } + + if (bclk_div > 128) { + dev_err(dev, "Failed to find BCLK divider\n"); + return -EINVAL; + } + + /* the actual rate */ + sample_rate = sck_rate / bclk_div / lrclk_div; + osr_rate = 16 * sample_rate; + + /* run DSP no faster than 50 MHz */ + dsp_div = mck_rate > pcm512x_dsp_max(pcm512x) ? 2 : 1; + + dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate); + if (dac_rate) { + /* the desired clock rate is "compatible" with the pll input + * clock, so use that clock as dac input instead of the pll + * output clock since the pll will introduce jitter and thus + * noise. + */ + dev_dbg(dev, "using pll input as dac input\n"); + ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF, + PCM512x_SDAC, PCM512x_SDAC_GPIO); + if (ret != 0) { + dev_err(component->dev, + "Failed to set gpio as dacref: %d\n", ret); + return ret; + } + + gpio = PCM512x_GREF_GPIO1 + pcm512x->pll_in - 1; + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_DACIN, + PCM512x_GREF, gpio); + if (ret != 0) { + dev_err(component->dev, + "Failed to set gpio %d as dacin: %d\n", + pcm512x->pll_in, ret); + return ret; + } + + dacsrc_rate = pllin_rate; + } else { + /* run DAC no faster than 6144000 Hz */ + unsigned long dac_mul = pcm512x_dac_max(pcm512x, 6144000) + / osr_rate; + unsigned long sck_mul = sck_rate / osr_rate; + + for (; dac_mul; dac_mul--) { + if (!(sck_mul % dac_mul)) + break; + } + if (!dac_mul) { + dev_err(dev, "Failed to find DAC rate\n"); + return -EINVAL; + } + + dac_rate = dac_mul * osr_rate; + dev_dbg(dev, "dac_rate %lu sample_rate %lu\n", + dac_rate, sample_rate); + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF, + PCM512x_SDAC, PCM512x_SDAC_SCK); + if (ret != 0) { + dev_err(component->dev, + "Failed to set sck as dacref: %d\n", ret); + return ret; + } + + dacsrc_rate = sck_rate; + } + + osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate); + if (osr_div > 128) { + dev_err(dev, "Failed to find OSR divider\n"); + return -EINVAL; + } + + dac_div = DIV_ROUND_CLOSEST(dacsrc_rate, dac_rate); + if (dac_div > 128) { + dev_err(dev, "Failed to find DAC divider\n"); + return -EINVAL; + } + dac_rate = dacsrc_rate / dac_div; + + ncp_div = DIV_ROUND_CLOSEST(dac_rate, + pcm512x_ncp_target(pcm512x, dac_rate)); + if (ncp_div > 128 || dac_rate / ncp_div > 2048000) { + /* run NCP no faster than 2048000 Hz, but why? */ + ncp_div = DIV_ROUND_UP(dac_rate, 2048000); + if (ncp_div > 128) { + dev_err(dev, "Failed to find NCP divider\n"); + return -EINVAL; + } + } + + idac = mck_rate / (dsp_div * sample_rate); + + ret = regmap_write(pcm512x->regmap, PCM512x_DSP_CLKDIV, dsp_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write DSP divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_DAC_CLKDIV, dac_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write DAC divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_NCP_CLKDIV, ncp_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write NCP divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_OSR_CLKDIV, osr_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write OSR divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_MASTER_CLKDIV_1, bclk_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write BCLK divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, + PCM512x_MASTER_CLKDIV_2, lrclk_div - 1); + if (ret != 0) { + dev_err(dev, "Failed to write LRCLK divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_IDAC_1, idac >> 8); + if (ret != 0) { + dev_err(dev, "Failed to write IDAC msb divider: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_IDAC_2, idac & 0xff); + if (ret != 0) { + dev_err(dev, "Failed to write IDAC lsb divider: %d\n", ret); + return ret; + } + + if (sample_rate <= pcm512x_dac_max(pcm512x, 48000)) + fssp = PCM512x_FSSP_48KHZ; + else if (sample_rate <= pcm512x_dac_max(pcm512x, 96000)) + fssp = PCM512x_FSSP_96KHZ; + else if (sample_rate <= pcm512x_dac_max(pcm512x, 192000)) + fssp = PCM512x_FSSP_192KHZ; + else + fssp = PCM512x_FSSP_384KHZ; + ret = regmap_update_bits(pcm512x->regmap, PCM512x_FS_SPEED_MODE, + PCM512x_FSSP, fssp); + if (ret != 0) { + dev_err(component->dev, "Failed to set fs speed: %d\n", ret); + return ret; + } + + dev_dbg(component->dev, "DSP divider %d\n", dsp_div); + dev_dbg(component->dev, "DAC divider %d\n", dac_div); + dev_dbg(component->dev, "NCP divider %d\n", ncp_div); + dev_dbg(component->dev, "OSR divider %d\n", osr_div); + dev_dbg(component->dev, "BCK divider %d\n", bclk_div); + dev_dbg(component->dev, "LRCK divider %d\n", lrclk_div); + dev_dbg(component->dev, "IDAC %d\n", idac); + dev_dbg(component->dev, "1<component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int alen; + int gpio; + int ret; + + dev_dbg(component->dev, "hw_params %u Hz, %u channels\n", + params_rate(params), + params_channels(params)); + + switch (params_width(params)) { + case 16: + alen = PCM512x_ALEN_16; + break; + case 20: + alen = PCM512x_ALEN_20; + break; + case 24: + alen = PCM512x_ALEN_24; + break; + case 32: + alen = PCM512x_ALEN_32; + break; + default: + dev_err(component->dev, "Bad frame size: %d\n", + params_width(params)); + return -EINVAL; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1, + PCM512x_ALEN, alen); + if (ret != 0) { + dev_err(component->dev, "Failed to set frame size: %d\n", ret); + return ret; + } + + if ((pcm512x->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == + SND_SOC_DAIFMT_CBC_CFC) { + ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT, + PCM512x_DCAS, 0); + if (ret != 0) { + dev_err(component->dev, + "Failed to enable clock divider autoset: %d\n", + ret); + return ret; + } + goto skip_pll; + } + + if (pcm512x->pll_out) { + ret = regmap_write(pcm512x->regmap, PCM512x_FLEX_A, 0x11); + if (ret != 0) { + dev_err(component->dev, "Failed to set FLEX_A: %d\n", ret); + return ret; + } + + ret = regmap_write(pcm512x->regmap, PCM512x_FLEX_B, 0xff); + if (ret != 0) { + dev_err(component->dev, "Failed to set FLEX_B: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT, + PCM512x_IDFS | PCM512x_IDBK + | PCM512x_IDSK | PCM512x_IDCH + | PCM512x_IDCM | PCM512x_DCAS + | PCM512x_IPLK, + PCM512x_IDFS | PCM512x_IDBK + | PCM512x_IDSK | PCM512x_IDCH + | PCM512x_DCAS); + if (ret != 0) { + dev_err(component->dev, + "Failed to ignore auto-clock failures: %d\n", + ret); + return ret; + } + } else { + ret = regmap_update_bits(pcm512x->regmap, PCM512x_ERROR_DETECT, + PCM512x_IDFS | PCM512x_IDBK + | PCM512x_IDSK | PCM512x_IDCH + | PCM512x_IDCM | PCM512x_DCAS + | PCM512x_IPLK, + PCM512x_IDFS | PCM512x_IDBK + | PCM512x_IDSK | PCM512x_IDCH + | PCM512x_DCAS | PCM512x_IPLK); + if (ret != 0) { + dev_err(component->dev, + "Failed to ignore auto-clock failures: %d\n", + ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN, + PCM512x_PLLE, 0); + if (ret != 0) { + dev_err(component->dev, "Failed to disable pll: %d\n", ret); + return ret; + } + } + + ret = pcm512x_set_dividers(dai, params); + if (ret != 0) + return ret; + + if (pcm512x->pll_out) { + ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_REF, + PCM512x_SREF, PCM512x_SREF_GPIO); + if (ret != 0) { + dev_err(component->dev, + "Failed to set gpio as pllref: %d\n", ret); + return ret; + } + + gpio = PCM512x_GREF_GPIO1 + pcm512x->pll_in - 1; + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_PLLIN, + PCM512x_GREF, gpio); + if (ret != 0) { + dev_err(component->dev, + "Failed to set gpio %d as pllin: %d\n", + pcm512x->pll_in, ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN, + PCM512x_PLLE, PCM512x_PLLE); + if (ret != 0) { + dev_err(component->dev, "Failed to enable pll: %d\n", ret); + return ret; + } + + gpio = PCM512x_G1OE << (pcm512x->pll_out - 1); + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN, + gpio, gpio); + if (ret != 0) { + dev_err(component->dev, "Failed to enable gpio %d: %d\n", + pcm512x->pll_out, ret); + return ret; + } + + gpio = PCM512x_GPIO_OUTPUT_1 + pcm512x->pll_out - 1; + ret = regmap_update_bits(pcm512x->regmap, gpio, + PCM512x_GxSL, PCM512x_GxSL_PLLCK); + if (ret != 0) { + dev_err(component->dev, "Failed to output pll on %d: %d\n", + ret, pcm512x->pll_out); + return ret; + } + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE, + PCM512x_RQSY, PCM512x_RQSY_HALT); + if (ret != 0) { + dev_err(component->dev, "Failed to halt clocks: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE, + PCM512x_RQSY, PCM512x_RQSY_RESUME); + if (ret != 0) { + dev_err(component->dev, "Failed to resume clocks: %d\n", ret); + return ret; + } + +skip_pll: + return 0; +} + +static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int afmt; + int offset = 0; + int clock_output; + int provider_mode; + int ret; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + clock_output = 0; + provider_mode = 0; + break; + case SND_SOC_DAIFMT_CBP_CFP: + clock_output = PCM512x_BCKO | PCM512x_LRKO; + provider_mode = PCM512x_RLRK | PCM512x_RBCK; + break; + case SND_SOC_DAIFMT_CBP_CFC: + clock_output = PCM512x_BCKO; + provider_mode = PCM512x_RBCK; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG, + PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO, + clock_output); + if (ret != 0) { + dev_err(component->dev, "Failed to enable clock output: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE, + PCM512x_RLRK | PCM512x_RBCK, + provider_mode); + if (ret != 0) { + dev_err(component->dev, "Failed to enable provider mode: %d\n", ret); + return ret; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + afmt = PCM512x_AFMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + afmt = PCM512x_AFMT_RTJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + afmt = PCM512x_AFMT_LTJ; + break; + case SND_SOC_DAIFMT_DSP_A: + offset = 1; + fallthrough; + case SND_SOC_DAIFMT_DSP_B: + afmt = PCM512x_AFMT_DSP; + break; + default: + dev_err(component->dev, "unsupported DAI format: 0x%x\n", + pcm512x->fmt); + return -EINVAL; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1, + PCM512x_AFMT, afmt); + if (ret != 0) { + dev_err(component->dev, "Failed to set data format: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_2, + 0xFF, offset); + if (ret != 0) { + dev_err(component->dev, "Failed to set data offset: %d\n", ret); + return ret; + } + + pcm512x->fmt = fmt; + + return 0; +} + +static int pcm512x_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + if (ratio > 256) + return -EINVAL; + + pcm512x->bclk_ratio = ratio; + + return 0; +} + +static int pcm512x_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int ret; + unsigned int mute_det; + + mutex_lock(&pcm512x->mutex); + + if (mute) { + pcm512x->mute |= 0x1; + ret = regmap_update_bits(pcm512x->regmap, PCM512x_MUTE, + PCM512x_RQML | PCM512x_RQMR, + PCM512x_RQML | PCM512x_RQMR); + if (ret != 0) { + dev_err(component->dev, + "Failed to set digital mute: %d\n", ret); + goto unlock; + } + + regmap_read_poll_timeout(pcm512x->regmap, + PCM512x_ANALOG_MUTE_DET, + mute_det, (mute_det & 0x3) == 0, + 200, 10000); + } else { + pcm512x->mute &= ~0x1; + ret = pcm512x_update_mute(pcm512x); + if (ret != 0) { + dev_err(component->dev, + "Failed to update digital mute: %d\n", ret); + goto unlock; + } + + regmap_read_poll_timeout(pcm512x->regmap, + PCM512x_ANALOG_MUTE_DET, + mute_det, + (mute_det & 0x3) + == ((~pcm512x->mute >> 1) & 0x3), + 200, 10000); + } + +unlock: + mutex_unlock(&pcm512x->mutex); + + return ret; +} + +static const struct snd_soc_dai_ops pcm512x_dai_ops = { + .startup = pcm512x_dai_startup, + .hw_params = pcm512x_hw_params, + .set_fmt = pcm512x_set_fmt, + .mute_stream = pcm512x_mute, + .set_bclk_ratio = pcm512x_set_bclk_ratio, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver pcm512x_dai = { + .name = "pcm512x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 384000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE + }, + .ops = &pcm512x_dai_ops, +}; + +static const struct snd_soc_component_driver pcm512x_component_driver = { + .set_bias_level = pcm512x_set_bias_level, + .controls = pcm512x_controls, + .num_controls = ARRAY_SIZE(pcm512x_controls), + .dapm_widgets = pcm512x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets), + .dapm_routes = pcm512x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_range_cfg pcm512x_range = { + .name = "Pages", .range_min = PCM512x_VIRT_BASE, + .range_max = PCM512x_MAX_REGISTER, + .selector_reg = PCM512x_PAGE, + .selector_mask = 0xff, + .window_start = 0, .window_len = 0x100, +}; + +const struct regmap_config pcm512x_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = pcm512x_readable, + .volatile_reg = pcm512x_volatile, + + .ranges = &pcm512x_range, + .num_ranges = 1, + + .max_register = PCM512x_MAX_REGISTER, + .reg_defaults = pcm512x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm512x_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(pcm512x_regmap); + +int pcm512x_probe(struct device *dev, struct regmap *regmap) +{ + struct pcm512x_priv *pcm512x; + int i, ret; + + pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL); + if (!pcm512x) + return -ENOMEM; + + mutex_init(&pcm512x->mutex); + + dev_set_drvdata(dev, pcm512x); + pcm512x->regmap = regmap; + + for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) + pcm512x->supplies[i].supply = pcm512x_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to get supplies: %d\n", ret); + return ret; + } + + pcm512x->supply_nb[0].notifier_call = pcm512x_regulator_event_0; + pcm512x->supply_nb[1].notifier_call = pcm512x_regulator_event_1; + pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2; + + for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) { + ret = devm_regulator_register_notifier( + pcm512x->supplies[i].consumer, + &pcm512x->supply_nb[i]); + if (ret != 0) { + dev_err(dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + /* Reset the device, verifying I/O in the process for I2C */ + ret = regmap_write(regmap, PCM512x_RESET, + PCM512x_RSTM | PCM512x_RSTR); + if (ret != 0) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err; + } + + ret = regmap_write(regmap, PCM512x_RESET, 0); + if (ret != 0) { + dev_err(dev, "Failed to reset device: %d\n", ret); + goto err; + } + + pcm512x->sclk = devm_clk_get(dev, NULL); + if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err; + } + if (!IS_ERR(pcm512x->sclk)) { + ret = clk_prepare_enable(pcm512x->sclk); + if (ret != 0) { + dev_err(dev, "Failed to enable SCLK: %d\n", ret); + goto err; + } + } + + /* Default to standby mode */ + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQST, PCM512x_RQST); + if (ret != 0) { + dev_err(dev, "Failed to request standby: %d\n", + ret); + goto err_clk; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + +#ifdef CONFIG_OF + if (dev->of_node) { + const struct device_node *np = dev->of_node; + u32 val; + + if (of_property_read_u32(np, "pll-in", &val) >= 0) { + if (val > 6) { + dev_err(dev, "Invalid pll-in\n"); + ret = -EINVAL; + goto err_pm; + } + pcm512x->pll_in = val; + } + + if (of_property_read_u32(np, "pll-out", &val) >= 0) { + if (val > 6) { + dev_err(dev, "Invalid pll-out\n"); + ret = -EINVAL; + goto err_pm; + } + pcm512x->pll_out = val; + } + + if (!pcm512x->pll_in != !pcm512x->pll_out) { + dev_err(dev, + "Error: both pll-in and pll-out, or none\n"); + ret = -EINVAL; + goto err_pm; + } + if (pcm512x->pll_in && pcm512x->pll_in == pcm512x->pll_out) { + dev_err(dev, "Error: pll-in == pll-out\n"); + ret = -EINVAL; + goto err_pm; + } + } +#endif + + ret = devm_snd_soc_register_component(dev, &pcm512x_component_driver, + &pcm512x_dai, 1); + if (ret != 0) { + dev_err(dev, "Failed to register CODEC: %d\n", ret); + goto err_pm; + } + + return 0; + +err_pm: + pm_runtime_disable(dev); +err_clk: + if (!IS_ERR(pcm512x->sclk)) + clk_disable_unprepare(pcm512x->sclk); +err: + regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + return ret; +} +EXPORT_SYMBOL_GPL(pcm512x_probe); + +void pcm512x_remove(struct device *dev) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + + pm_runtime_disable(dev); + if (!IS_ERR(pcm512x->sclk)) + clk_disable_unprepare(pcm512x->sclk); + regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); +} +EXPORT_SYMBOL_GPL(pcm512x_remove); + +#ifdef CONFIG_PM +static int pcm512x_suspend(struct device *dev) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQPD, PCM512x_RQPD); + if (ret != 0) { + dev_err(dev, "Failed to request power down: %d\n", ret); + return ret; + } + + ret = regulator_bulk_disable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to disable supplies: %d\n", ret); + return ret; + } + + if (!IS_ERR(pcm512x->sclk)) + clk_disable_unprepare(pcm512x->sclk); + + return 0; +} + +static int pcm512x_resume(struct device *dev) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + int ret; + + if (!IS_ERR(pcm512x->sclk)) { + ret = clk_prepare_enable(pcm512x->sclk); + if (ret != 0) { + dev_err(dev, "Failed to enable SCLK: %d\n", ret); + return ret; + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(pcm512x->supplies), + pcm512x->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_cache_only(pcm512x->regmap, false); + ret = regcache_sync(pcm512x->regmap); + if (ret != 0) { + dev_err(dev, "Failed to sync cache: %d\n", ret); + return ret; + } + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER, + PCM512x_RQPD, 0); + if (ret != 0) { + dev_err(dev, "Failed to remove power down: %d\n", ret); + return ret; + } + + return 0; +} +#endif + +const struct dev_pm_ops pcm512x_pm_ops = { + SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL) +}; +EXPORT_SYMBOL_GPL(pcm512x_pm_ops); + +MODULE_DESCRIPTION("ASoC PCM512x codec driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h new file mode 100644 index 0000000000..08d04f5398 --- /dev/null +++ b/sound/soc/codecs/pcm512x.h @@ -0,0 +1,264 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Driver for the PCM512x CODECs + * + * Author: Mark Brown + * Copyright 2014 Linaro Ltd + */ + +#ifndef _SND_SOC_PCM512X +#define _SND_SOC_PCM512X + +#include +#include + +#define PCM512x_VIRT_BASE 0x100 +#define PCM512x_PAGE_LEN 0x100 +#define PCM512x_PAGE_BASE(n) (PCM512x_VIRT_BASE + (PCM512x_PAGE_LEN * n)) + +#define PCM512x_PAGE 0 + +#define PCM512x_RESET (PCM512x_PAGE_BASE(0) + 1) +#define PCM512x_POWER (PCM512x_PAGE_BASE(0) + 2) +#define PCM512x_MUTE (PCM512x_PAGE_BASE(0) + 3) +#define PCM512x_PLL_EN (PCM512x_PAGE_BASE(0) + 4) +#define PCM512x_SPI_MISO_FUNCTION (PCM512x_PAGE_BASE(0) + 6) +#define PCM512x_DSP (PCM512x_PAGE_BASE(0) + 7) +#define PCM512x_GPIO_EN (PCM512x_PAGE_BASE(0) + 8) +#define PCM512x_BCLK_LRCLK_CFG (PCM512x_PAGE_BASE(0) + 9) +#define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_BASE(0) + 10) +#define PCM512x_MASTER_MODE (PCM512x_PAGE_BASE(0) + 12) +#define PCM512x_PLL_REF (PCM512x_PAGE_BASE(0) + 13) +#define PCM512x_DAC_REF (PCM512x_PAGE_BASE(0) + 14) +#define PCM512x_GPIO_DACIN (PCM512x_PAGE_BASE(0) + 16) +#define PCM512x_GPIO_PLLIN (PCM512x_PAGE_BASE(0) + 18) +#define PCM512x_SYNCHRONIZE (PCM512x_PAGE_BASE(0) + 19) +#define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_BASE(0) + 20) +#define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_BASE(0) + 21) +#define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_BASE(0) + 22) +#define PCM512x_PLL_COEFF_3 (PCM512x_PAGE_BASE(0) + 23) +#define PCM512x_PLL_COEFF_4 (PCM512x_PAGE_BASE(0) + 24) +#define PCM512x_DSP_CLKDIV (PCM512x_PAGE_BASE(0) + 27) +#define PCM512x_DAC_CLKDIV (PCM512x_PAGE_BASE(0) + 28) +#define PCM512x_NCP_CLKDIV (PCM512x_PAGE_BASE(0) + 29) +#define PCM512x_OSR_CLKDIV (PCM512x_PAGE_BASE(0) + 30) +#define PCM512x_MASTER_CLKDIV_1 (PCM512x_PAGE_BASE(0) + 32) +#define PCM512x_MASTER_CLKDIV_2 (PCM512x_PAGE_BASE(0) + 33) +#define PCM512x_FS_SPEED_MODE (PCM512x_PAGE_BASE(0) + 34) +#define PCM512x_IDAC_1 (PCM512x_PAGE_BASE(0) + 35) +#define PCM512x_IDAC_2 (PCM512x_PAGE_BASE(0) + 36) +#define PCM512x_ERROR_DETECT (PCM512x_PAGE_BASE(0) + 37) +#define PCM512x_I2S_1 (PCM512x_PAGE_BASE(0) + 40) +#define PCM512x_I2S_2 (PCM512x_PAGE_BASE(0) + 41) +#define PCM512x_DAC_ROUTING (PCM512x_PAGE_BASE(0) + 42) +#define PCM512x_DSP_PROGRAM (PCM512x_PAGE_BASE(0) + 43) +#define PCM512x_CLKDET (PCM512x_PAGE_BASE(0) + 44) +#define PCM512x_AUTO_MUTE (PCM512x_PAGE_BASE(0) + 59) +#define PCM512x_DIGITAL_VOLUME_1 (PCM512x_PAGE_BASE(0) + 60) +#define PCM512x_DIGITAL_VOLUME_2 (PCM512x_PAGE_BASE(0) + 61) +#define PCM512x_DIGITAL_VOLUME_3 (PCM512x_PAGE_BASE(0) + 62) +#define PCM512x_DIGITAL_MUTE_1 (PCM512x_PAGE_BASE(0) + 63) +#define PCM512x_DIGITAL_MUTE_2 (PCM512x_PAGE_BASE(0) + 64) +#define PCM512x_DIGITAL_MUTE_3 (PCM512x_PAGE_BASE(0) + 65) +#define PCM512x_GPIO_OUTPUT_1 (PCM512x_PAGE_BASE(0) + 80) +#define PCM512x_GPIO_OUTPUT_2 (PCM512x_PAGE_BASE(0) + 81) +#define PCM512x_GPIO_OUTPUT_3 (PCM512x_PAGE_BASE(0) + 82) +#define PCM512x_GPIO_OUTPUT_4 (PCM512x_PAGE_BASE(0) + 83) +#define PCM512x_GPIO_OUTPUT_5 (PCM512x_PAGE_BASE(0) + 84) +#define PCM512x_GPIO_OUTPUT_6 (PCM512x_PAGE_BASE(0) + 85) +#define PCM512x_GPIO_CONTROL_1 (PCM512x_PAGE_BASE(0) + 86) +#define PCM512x_GPIO_CONTROL_2 (PCM512x_PAGE_BASE(0) + 87) +#define PCM512x_OVERFLOW (PCM512x_PAGE_BASE(0) + 90) +#define PCM512x_RATE_DET_1 (PCM512x_PAGE_BASE(0) + 91) +#define PCM512x_RATE_DET_2 (PCM512x_PAGE_BASE(0) + 92) +#define PCM512x_RATE_DET_3 (PCM512x_PAGE_BASE(0) + 93) +#define PCM512x_RATE_DET_4 (PCM512x_PAGE_BASE(0) + 94) +#define PCM512x_CLOCK_STATUS (PCM512x_PAGE_BASE(0) + 95) +#define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_BASE(0) + 108) +#define PCM512x_GPIN (PCM512x_PAGE_BASE(0) + 119) +#define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_BASE(0) + 120) + +#define PCM512x_OUTPUT_AMPLITUDE (PCM512x_PAGE_BASE(1) + 1) +#define PCM512x_ANALOG_GAIN_CTRL (PCM512x_PAGE_BASE(1) + 2) +#define PCM512x_UNDERVOLTAGE_PROT (PCM512x_PAGE_BASE(1) + 5) +#define PCM512x_ANALOG_MUTE_CTRL (PCM512x_PAGE_BASE(1) + 6) +#define PCM512x_ANALOG_GAIN_BOOST (PCM512x_PAGE_BASE(1) + 7) +#define PCM512x_VCOM_CTRL_1 (PCM512x_PAGE_BASE(1) + 8) +#define PCM512x_VCOM_CTRL_2 (PCM512x_PAGE_BASE(1) + 9) + +#define PCM512x_CRAM_CTRL (PCM512x_PAGE_BASE(44) + 1) + +#define PCM512x_FLEX_A (PCM512x_PAGE_BASE(253) + 63) +#define PCM512x_FLEX_B (PCM512x_PAGE_BASE(253) + 64) + +#define PCM512x_MAX_REGISTER (PCM512x_PAGE_BASE(253) + 64) + +/* Page 0, Register 1 - reset */ +#define PCM512x_RSTR (1 << 0) +#define PCM512x_RSTM (1 << 4) + +/* Page 0, Register 2 - power */ +#define PCM512x_RQPD (1 << 0) +#define PCM512x_RQPD_SHIFT 0 +#define PCM512x_RQST (1 << 4) +#define PCM512x_RQST_SHIFT 4 + +/* Page 0, Register 3 - mute */ +#define PCM512x_RQMR (1 << 0) +#define PCM512x_RQMR_SHIFT 0 +#define PCM512x_RQML (1 << 4) +#define PCM512x_RQML_SHIFT 4 + +/* Page 0, Register 4 - PLL */ +#define PCM512x_PLLE (1 << 0) +#define PCM512x_PLLE_SHIFT 0 +#define PCM512x_PLCK (1 << 4) +#define PCM512x_PLCK_SHIFT 4 + +/* Page 0, Register 7 - DSP */ +#define PCM512x_SDSL (1 << 0) +#define PCM512x_SDSL_SHIFT 0 +#define PCM512x_DEMP (1 << 4) +#define PCM512x_DEMP_SHIFT 4 + +/* Page 0, Register 8 - GPIO output enable */ +#define PCM512x_G1OE (1 << 0) +#define PCM512x_G2OE (1 << 1) +#define PCM512x_G3OE (1 << 2) +#define PCM512x_G4OE (1 << 3) +#define PCM512x_G5OE (1 << 4) +#define PCM512x_G6OE (1 << 5) + +/* Page 0, Register 9 - BCK, LRCLK configuration */ +#define PCM512x_LRKO (1 << 0) +#define PCM512x_LRKO_SHIFT 0 +#define PCM512x_BCKO (1 << 4) +#define PCM512x_BCKO_SHIFT 4 +#define PCM512x_BCKP (1 << 5) +#define PCM512x_BCKP_SHIFT 5 + +/* Page 0, Register 12 - Master mode BCK, LRCLK reset */ +#define PCM512x_RLRK (1 << 0) +#define PCM512x_RLRK_SHIFT 0 +#define PCM512x_RBCK (1 << 1) +#define PCM512x_RBCK_SHIFT 1 + +/* Page 0, Register 13 - PLL reference */ +#define PCM512x_SREF (7 << 4) +#define PCM512x_SREF_SHIFT 4 +#define PCM512x_SREF_SCK (0 << 4) +#define PCM512x_SREF_BCK (1 << 4) +#define PCM512x_SREF_GPIO (3 << 4) + +/* Page 0, Register 14 - DAC reference */ +#define PCM512x_SDAC (7 << 4) +#define PCM512x_SDAC_SHIFT 4 +#define PCM512x_SDAC_MCK (0 << 4) +#define PCM512x_SDAC_PLL (1 << 4) +#define PCM512x_SDAC_SCK (3 << 4) +#define PCM512x_SDAC_BCK (4 << 4) +#define PCM512x_SDAC_GPIO (5 << 4) + +/* Page 0, Register 16, 18 - GPIO source for DAC, PLL */ +#define PCM512x_GREF (7 << 0) +#define PCM512x_GREF_SHIFT 0 +#define PCM512x_GREF_GPIO1 (0 << 0) +#define PCM512x_GREF_GPIO2 (1 << 0) +#define PCM512x_GREF_GPIO3 (2 << 0) +#define PCM512x_GREF_GPIO4 (3 << 0) +#define PCM512x_GREF_GPIO5 (4 << 0) +#define PCM512x_GREF_GPIO6 (5 << 0) + +/* Page 0, Register 19 - synchronize */ +#define PCM512x_RQSY (1 << 0) +#define PCM512x_RQSY_RESUME (0 << 0) +#define PCM512x_RQSY_HALT (1 << 0) + +/* Page 0, Register 34 - fs speed mode */ +#define PCM512x_FSSP (3 << 0) +#define PCM512x_FSSP_SHIFT 0 +#define PCM512x_FSSP_48KHZ (0 << 0) +#define PCM512x_FSSP_96KHZ (1 << 0) +#define PCM512x_FSSP_192KHZ (2 << 0) +#define PCM512x_FSSP_384KHZ (3 << 0) + +/* Page 0, Register 37 - Error detection */ +#define PCM512x_IPLK (1 << 0) +#define PCM512x_DCAS (1 << 1) +#define PCM512x_IDCM (1 << 2) +#define PCM512x_IDCH (1 << 3) +#define PCM512x_IDSK (1 << 4) +#define PCM512x_IDBK (1 << 5) +#define PCM512x_IDFS (1 << 6) + +/* Page 0, Register 40 - I2S configuration */ +#define PCM512x_ALEN (3 << 0) +#define PCM512x_ALEN_SHIFT 0 +#define PCM512x_ALEN_16 (0 << 0) +#define PCM512x_ALEN_20 (1 << 0) +#define PCM512x_ALEN_24 (2 << 0) +#define PCM512x_ALEN_32 (3 << 0) +#define PCM512x_AFMT (3 << 4) +#define PCM512x_AFMT_SHIFT 4 +#define PCM512x_AFMT_I2S (0 << 4) +#define PCM512x_AFMT_DSP (1 << 4) +#define PCM512x_AFMT_RTJ (2 << 4) +#define PCM512x_AFMT_LTJ (3 << 4) + +/* Page 0, Register 42 - DAC routing */ +#define PCM512x_AUPR_SHIFT 0 +#define PCM512x_AUPL_SHIFT 4 + +/* Page 0, Register 59 - auto mute */ +#define PCM512x_ATMR_SHIFT 0 +#define PCM512x_ATML_SHIFT 4 + +/* Page 0, Register 63 - ramp rates */ +#define PCM512x_VNDF_SHIFT 6 +#define PCM512x_VNDS_SHIFT 4 +#define PCM512x_VNUF_SHIFT 2 +#define PCM512x_VNUS_SHIFT 0 + +/* Page 0, Register 64 - emergency ramp rates */ +#define PCM512x_VEDF_SHIFT 6 +#define PCM512x_VEDS_SHIFT 4 + +/* Page 0, Register 65 - Digital mute enables */ +#define PCM512x_ACTL_SHIFT 2 +#define PCM512x_AMLE_SHIFT 1 +#define PCM512x_AMRE_SHIFT 0 + +/* Page 0, Register 80-85, GPIO output selection */ +#define PCM512x_GxSL (31 << 0) +#define PCM512x_GxSL_SHIFT 0 +#define PCM512x_GxSL_OFF (0 << 0) +#define PCM512x_GxSL_DSP (1 << 0) +#define PCM512x_GxSL_REG (2 << 0) +#define PCM512x_GxSL_AMUTB (3 << 0) +#define PCM512x_GxSL_AMUTL (4 << 0) +#define PCM512x_GxSL_AMUTR (5 << 0) +#define PCM512x_GxSL_CLKI (6 << 0) +#define PCM512x_GxSL_SDOUT (7 << 0) +#define PCM512x_GxSL_ANMUL (8 << 0) +#define PCM512x_GxSL_ANMUR (9 << 0) +#define PCM512x_GxSL_PLLLK (10 << 0) +#define PCM512x_GxSL_CPCLK (11 << 0) +#define PCM512x_GxSL_UV0_7 (14 << 0) +#define PCM512x_GxSL_UV0_3 (15 << 0) +#define PCM512x_GxSL_PLLCK (16 << 0) + +/* Page 1, Register 2 - analog volume control */ +#define PCM512x_RAGN_SHIFT 0 +#define PCM512x_LAGN_SHIFT 4 + +/* Page 1, Register 7 - analog boost control */ +#define PCM512x_AGBR_SHIFT 0 +#define PCM512x_AGBL_SHIFT 4 + +extern const struct dev_pm_ops pcm512x_pm_ops; +extern const struct regmap_config pcm512x_regmap; + +int pcm512x_probe(struct device *dev, struct regmap *regmap); +void pcm512x_remove(struct device *dev); + +#endif diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c new file mode 100644 index 0000000000..5dec69be0a --- /dev/null +++ b/sound/soc/codecs/peb2466.c @@ -0,0 +1,2071 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// peb2466.c -- Infineon PEB2466 ALSA SoC driver +// +// Copyright 2023 CS GROUP France +// +// Author: Herve Codina + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PEB2466_NB_CHANNEL 4 + +struct peb2466_lookup { + u8 (*table)[4]; + unsigned int count; +}; + +#define PEB2466_TLV_SIZE (sizeof((unsigned int []){TLV_DB_SCALE_ITEM(0, 0, 0)}) / \ + sizeof(unsigned int)) + +struct peb2466_lkup_ctrl { + int reg; + unsigned int index; + const struct peb2466_lookup *lookup; + unsigned int tlv_array[PEB2466_TLV_SIZE]; +}; + +struct peb2466 { + struct spi_device *spi; + struct clk *mclk; + struct gpio_desc *reset_gpio; + u8 spi_tx_buf[2 + 8]; /* Cannot use stack area for SPI (dma-safe memory) */ + u8 spi_rx_buf[2 + 8]; /* Cannot use stack area for SPI (dma-safe memory) */ + struct regmap *regmap; + struct { + struct peb2466_lookup ax_lookup; + struct peb2466_lookup ar_lookup; + struct peb2466_lkup_ctrl ax_lkup_ctrl; + struct peb2466_lkup_ctrl ar_lkup_ctrl; + unsigned int tg1_freq_item; + unsigned int tg2_freq_item; + } ch[PEB2466_NB_CHANNEL]; + int max_chan_playback; + int max_chan_capture; + struct { + struct gpio_chip gpio_chip; + struct mutex lock; + struct { + unsigned int xr0; + unsigned int xr1; + unsigned int xr2; + unsigned int xr3; + } cache; + } gpio; +}; + +#define PEB2466_CMD_R (1 << 5) +#define PEB2466_CMD_W (0 << 5) + +#define PEB2466_CMD_MASK 0x18 +#define PEB2466_CMD_XOP 0x18 /* XOP is 0bxxx11xxx */ +#define PEB2466_CMD_SOP 0x10 /* SOP is 0bxxx10xxx */ +#define PEB2466_CMD_COP 0x00 /* COP is 0bxxx0xxxx, handle 0bxxx00xxx */ +#define PEB2466_CMD_COP1 0x08 /* COP is 0bxxx0xxxx, handle 0bxxx01xxx */ + +#define PEB2466_MAKE_XOP(_lsel) (PEB2466_CMD_XOP | (_lsel)) +#define PEB2466_MAKE_SOP(_ad, _lsel) (PEB2466_CMD_SOP | ((_ad) << 6) | (_lsel)) +#define PEB2466_MAKE_COP(_ad, _code) (PEB2466_CMD_COP | ((_ad) << 6) | (_code)) + +#define PEB2466_CR0(_ch) PEB2466_MAKE_SOP(_ch, 0x0) +#define PEB2466_CR0_TH (1 << 7) +#define PEB2466_CR0_IMR1 (1 << 6) +#define PEB2466_CR0_FRX (1 << 5) +#define PEB2466_CR0_FRR (1 << 4) +#define PEB2466_CR0_AX (1 << 3) +#define PEB2466_CR0_AR (1 << 2) +#define PEB2466_CR0_THSEL_MASK (0x3 << 0) +#define PEB2466_CR0_THSEL(_set) ((_set) << 0) + +#define PEB2466_CR1(_ch) PEB2466_MAKE_SOP(_ch, 0x1) +#define PEB2466_CR1_ETG2 (1 << 7) +#define PEB2466_CR1_ETG1 (1 << 6) +#define PEB2466_CR1_PTG2 (1 << 5) +#define PEB2466_CR1_PTG1 (1 << 4) +#define PEB2466_CR1_LAW_MASK (1 << 3) +#define PEB2466_CR1_LAW_ALAW (0 << 3) +#define PEB2466_CR1_LAW_MULAW (1 << 3) +#define PEB2466_CR1_PU (1 << 0) + +#define PEB2466_CR2(_ch) PEB2466_MAKE_SOP(_ch, 0x2) +#define PEB2466_CR3(_ch) PEB2466_MAKE_SOP(_ch, 0x3) +#define PEB2466_CR4(_ch) PEB2466_MAKE_SOP(_ch, 0x4) +#define PEB2466_CR5(_ch) PEB2466_MAKE_SOP(_ch, 0x5) + +#define PEB2466_XR0 PEB2466_MAKE_XOP(0x0) +#define PEB2466_XR1 PEB2466_MAKE_XOP(0x1) +#define PEB2466_XR2 PEB2466_MAKE_XOP(0x2) +#define PEB2466_XR3 PEB2466_MAKE_XOP(0x3) +#define PEB2466_XR4 PEB2466_MAKE_XOP(0x4) +#define PEB2466_XR5 PEB2466_MAKE_XOP(0x5) +#define PEB2466_XR5_MCLK_1536 (0x0 << 6) +#define PEB2466_XR5_MCLK_2048 (0x1 << 6) +#define PEB2466_XR5_MCLK_4096 (0x2 << 6) +#define PEB2466_XR5_MCLK_8192 (0x3 << 6) + +#define PEB2466_XR6 PEB2466_MAKE_XOP(0x6) +#define PEB2466_XR6_PCM_OFFSET(_off) ((_off) << 0) + +#define PEB2466_XR7 PEB2466_MAKE_XOP(0x7) + +#define PEB2466_TH_FILTER_P1(_ch) PEB2466_MAKE_COP(_ch, 0x0) +#define PEB2466_TH_FILTER_P2(_ch) PEB2466_MAKE_COP(_ch, 0x1) +#define PEB2466_TH_FILTER_P3(_ch) PEB2466_MAKE_COP(_ch, 0x2) +#define PEB2466_IMR1_FILTER_P1(_ch) PEB2466_MAKE_COP(_ch, 0x4) +#define PEB2466_IMR1_FILTER_P2(_ch) PEB2466_MAKE_COP(_ch, 0x5) +#define PEB2466_FRX_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x6) +#define PEB2466_FRR_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x7) +#define PEB2466_AX_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x8) +#define PEB2466_AR_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x9) +#define PEB2466_TG1(_ch) PEB2466_MAKE_COP(_ch, 0xc) +#define PEB2466_TG2(_ch) PEB2466_MAKE_COP(_ch, 0xd) + +static int peb2466_write_byte(struct peb2466 *peb2466, u8 cmd, u8 val) +{ + struct spi_transfer xfer = { + .tx_buf = &peb2466->spi_tx_buf, + .len = 2, + }; + + peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_W; + peb2466->spi_tx_buf[1] = val; + + dev_dbg(&peb2466->spi->dev, "write byte (cmd %02x) %02x\n", + peb2466->spi_tx_buf[0], peb2466->spi_tx_buf[1]); + + return spi_sync_transfer(peb2466->spi, &xfer, 1); +} + +static int peb2466_read_byte(struct peb2466 *peb2466, u8 cmd, u8 *val) +{ + struct spi_transfer xfer = { + .tx_buf = &peb2466->spi_tx_buf, + .rx_buf = &peb2466->spi_rx_buf, + .len = 3, + }; + int ret; + + peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_R; + + ret = spi_sync_transfer(peb2466->spi, &xfer, 1); + if (ret) + return ret; + + if (peb2466->spi_rx_buf[1] != 0x81) { + dev_err(&peb2466->spi->dev, + "spi xfer rd (cmd %02x) invalid ident byte (0x%02x)\n", + peb2466->spi_tx_buf[0], peb2466->spi_rx_buf[1]); + return -EILSEQ; + } + + *val = peb2466->spi_rx_buf[2]; + + dev_dbg(&peb2466->spi->dev, "read byte (cmd %02x) %02x\n", + peb2466->spi_tx_buf[0], *val); + + return 0; +} + +static int peb2466_write_buf(struct peb2466 *peb2466, u8 cmd, const u8 *buf, unsigned int len) +{ + struct spi_transfer xfer = { + .tx_buf = &peb2466->spi_tx_buf, + .len = len + 1, + }; + + if (len > 8) + return -EINVAL; + + peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_W; + memcpy(&peb2466->spi_tx_buf[1], buf, len); + + dev_dbg(&peb2466->spi->dev, "write buf (cmd %02x, %u) %*ph\n", + peb2466->spi_tx_buf[0], len, len, &peb2466->spi_tx_buf[1]); + + return spi_sync_transfer(peb2466->spi, &xfer, 1); +} + +static int peb2466_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct peb2466 *peb2466 = context; + int ret; + + /* + * Only XOP and SOP commands can be handled as registers. + * COP commands are handled using direct peb2466_write_buf() calls. + */ + switch (reg & PEB2466_CMD_MASK) { + case PEB2466_CMD_XOP: + case PEB2466_CMD_SOP: + ret = peb2466_write_byte(peb2466, reg, val); + break; + default: + dev_err(&peb2466->spi->dev, "Not a XOP or SOP command\n"); + ret = -EINVAL; + break; + } + return ret; +} + +static int peb2466_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct peb2466 *peb2466 = context; + int ret; + u8 tmp; + + /* Only XOP and SOP commands can be handled as registers */ + switch (reg & PEB2466_CMD_MASK) { + case PEB2466_CMD_XOP: + case PEB2466_CMD_SOP: + ret = peb2466_read_byte(peb2466, reg, &tmp); + *val = tmp; + break; + default: + dev_err(&peb2466->spi->dev, "Not a XOP or SOP command\n"); + ret = -EINVAL; + break; + } + return ret; +} + +static const struct regmap_config peb2466_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xFF, + .reg_write = peb2466_reg_write, + .reg_read = peb2466_reg_read, + .cache_type = REGCACHE_NONE, +}; + +static int peb2466_lkup_ctrl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct peb2466_lkup_ctrl *lkup_ctrl = + (struct peb2466_lkup_ctrl *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = lkup_ctrl->lookup->count - 1; + return 0; +} + +static int peb2466_lkup_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct peb2466_lkup_ctrl *lkup_ctrl = + (struct peb2466_lkup_ctrl *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = lkup_ctrl->index; + return 0; +} + +static int peb2466_lkup_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct peb2466_lkup_ctrl *lkup_ctrl = + (struct peb2466_lkup_ctrl *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + unsigned int index; + int ret; + + index = ucontrol->value.integer.value[0]; + if (index >= lkup_ctrl->lookup->count) + return -EINVAL; + + if (index == lkup_ctrl->index) + return 0; + + ret = peb2466_write_buf(peb2466, lkup_ctrl->reg, + lkup_ctrl->lookup->table[index], 4); + if (ret) + return ret; + + lkup_ctrl->index = index; + return 1; /* The value changed */ +} + +static int peb2466_add_lkup_ctrl(struct snd_soc_component *component, + struct peb2466_lkup_ctrl *lkup_ctrl, + const char *name, int min_val, int step) +{ + DECLARE_TLV_DB_SCALE(tlv_array, min_val, step, 0); + struct snd_kcontrol_new control = {0}; + + BUILD_BUG_ON(sizeof(lkup_ctrl->tlv_array) < sizeof(tlv_array)); + memcpy(lkup_ctrl->tlv_array, tlv_array, sizeof(tlv_array)); + + control.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + control.name = name; + control.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE; + control.tlv.p = lkup_ctrl->tlv_array; + control.info = peb2466_lkup_ctrl_info; + control.get = peb2466_lkup_ctrl_get; + control.put = peb2466_lkup_ctrl_put; + control.private_value = (unsigned long)lkup_ctrl; + + return snd_soc_add_component_controls(component, &control, 1); +} + +enum peb2466_tone_freq { + PEB2466_TONE_697HZ, + PEB2466_TONE_800HZ, + PEB2466_TONE_950HZ, + PEB2466_TONE_1000HZ, + PEB2466_TONE_1008HZ, + PEB2466_TONE_2000HZ, +}; + +static const u8 peb2466_tone_lookup[][4] = { + [PEB2466_TONE_697HZ] = {0x0a, 0x33, 0x5a, 0x2c}, + [PEB2466_TONE_800HZ] = {0x12, 0xD6, 0x5a, 0xc0}, + [PEB2466_TONE_950HZ] = {0x1c, 0xf0, 0x5c, 0xc0}, + [PEB2466_TONE_1000HZ] = {0}, /* lookup value not used for 1000Hz */ + [PEB2466_TONE_1008HZ] = {0x1a, 0xae, 0x57, 0x70}, + [PEB2466_TONE_2000HZ] = {0x00, 0x80, 0x50, 0x09}, +}; + +static const char * const peb2466_tone_freq_txt[] = { + [PEB2466_TONE_697HZ] = "697Hz", + [PEB2466_TONE_800HZ] = "800Hz", + [PEB2466_TONE_950HZ] = "950Hz", + [PEB2466_TONE_1000HZ] = "1000Hz", + [PEB2466_TONE_1008HZ] = "1008Hz", + [PEB2466_TONE_2000HZ] = "2000Hz" +}; + +static const struct soc_enum peb2466_tg_freq[][2] = { + [0] = { + SOC_ENUM_SINGLE(PEB2466_TG1(0), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt), + SOC_ENUM_SINGLE(PEB2466_TG2(0), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt) + }, + [1] = { + SOC_ENUM_SINGLE(PEB2466_TG1(1), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt), + SOC_ENUM_SINGLE(PEB2466_TG2(1), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt) + }, + [2] = { + SOC_ENUM_SINGLE(PEB2466_TG1(2), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt), + SOC_ENUM_SINGLE(PEB2466_TG2(2), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt) + }, + [3] = { + SOC_ENUM_SINGLE(PEB2466_TG1(3), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt), + SOC_ENUM_SINGLE(PEB2466_TG2(3), 0, ARRAY_SIZE(peb2466_tone_freq_txt), + peb2466_tone_freq_txt) + } +}; + +static int peb2466_tg_freq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + switch (e->reg) { + case PEB2466_TG1(0): + ucontrol->value.enumerated.item[0] = peb2466->ch[0].tg1_freq_item; + break; + case PEB2466_TG2(0): + ucontrol->value.enumerated.item[0] = peb2466->ch[0].tg2_freq_item; + break; + case PEB2466_TG1(1): + ucontrol->value.enumerated.item[0] = peb2466->ch[1].tg1_freq_item; + break; + case PEB2466_TG2(1): + ucontrol->value.enumerated.item[0] = peb2466->ch[1].tg2_freq_item; + break; + case PEB2466_TG1(2): + ucontrol->value.enumerated.item[0] = peb2466->ch[2].tg1_freq_item; + break; + case PEB2466_TG2(2): + ucontrol->value.enumerated.item[0] = peb2466->ch[2].tg2_freq_item; + break; + case PEB2466_TG1(3): + ucontrol->value.enumerated.item[0] = peb2466->ch[3].tg1_freq_item; + break; + case PEB2466_TG2(3): + ucontrol->value.enumerated.item[0] = peb2466->ch[3].tg2_freq_item; + break; + default: + return -EINVAL; + } + return 0; +} + +static int peb2466_tg_freq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *tg_freq_item; + u8 cr1_reg, cr1_mask; + unsigned int index; + int ret; + + index = ucontrol->value.enumerated.item[0]; + + if (index >= ARRAY_SIZE(peb2466_tone_lookup)) + return -EINVAL; + + switch (e->reg) { + case PEB2466_TG1(0): + tg_freq_item = &peb2466->ch[0].tg1_freq_item; + cr1_reg = PEB2466_CR1(0); + cr1_mask = PEB2466_CR1_PTG1; + break; + case PEB2466_TG2(0): + tg_freq_item = &peb2466->ch[0].tg2_freq_item; + cr1_reg = PEB2466_CR1(0); + cr1_mask = PEB2466_CR1_PTG2; + break; + case PEB2466_TG1(1): + tg_freq_item = &peb2466->ch[1].tg1_freq_item; + cr1_reg = PEB2466_CR1(1); + cr1_mask = PEB2466_CR1_PTG1; + break; + case PEB2466_TG2(1): + tg_freq_item = &peb2466->ch[1].tg2_freq_item; + cr1_reg = PEB2466_CR1(1); + cr1_mask = PEB2466_CR1_PTG2; + break; + case PEB2466_TG1(2): + tg_freq_item = &peb2466->ch[2].tg1_freq_item; + cr1_reg = PEB2466_CR1(2); + cr1_mask = PEB2466_CR1_PTG1; + break; + case PEB2466_TG2(2): + tg_freq_item = &peb2466->ch[2].tg2_freq_item; + cr1_reg = PEB2466_CR1(2); + cr1_mask = PEB2466_CR1_PTG2; + break; + case PEB2466_TG1(3): + tg_freq_item = &peb2466->ch[3].tg1_freq_item; + cr1_reg = PEB2466_CR1(3); + cr1_mask = PEB2466_CR1_PTG1; + break; + case PEB2466_TG2(3): + tg_freq_item = &peb2466->ch[3].tg2_freq_item; + cr1_reg = PEB2466_CR1(3); + cr1_mask = PEB2466_CR1_PTG2; + break; + default: + return -EINVAL; + } + + if (index == *tg_freq_item) + return 0; + + if (index == PEB2466_TONE_1000HZ) { + ret = regmap_update_bits(peb2466->regmap, cr1_reg, cr1_mask, 0); + if (ret) + return ret; + } else { + ret = peb2466_write_buf(peb2466, e->reg, peb2466_tone_lookup[index], 4); + if (ret) + return ret; + ret = regmap_update_bits(peb2466->regmap, cr1_reg, cr1_mask, cr1_mask); + if (ret) + return ret; + } + + *tg_freq_item = index; + return 1; /* The value changed */ +} + +static const struct snd_kcontrol_new peb2466_ch0_out_mix_controls[] = { + SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(0), 6, 1, 0), + SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(0), 7, 1, 0), + SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(0), 0, 1, 0) +}; + +static const struct snd_kcontrol_new peb2466_ch1_out_mix_controls[] = { + SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(1), 6, 1, 0), + SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(1), 7, 1, 0), + SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(1), 0, 1, 0) +}; + +static const struct snd_kcontrol_new peb2466_ch2_out_mix_controls[] = { + SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(2), 6, 1, 0), + SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(2), 7, 1, 0), + SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(2), 0, 1, 0) +}; + +static const struct snd_kcontrol_new peb2466_ch3_out_mix_controls[] = { + SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(3), 6, 1, 0), + SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(3), 7, 1, 0), + SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(3), 0, 1, 0) +}; + +static const struct snd_kcontrol_new peb2466_controls[] = { + /* Attenuators */ + SOC_SINGLE("DAC0 -6dB Playback Switch", PEB2466_CR3(0), 2, 1, 0), + SOC_SINGLE("DAC1 -6dB Playback Switch", PEB2466_CR3(1), 2, 1, 0), + SOC_SINGLE("DAC2 -6dB Playback Switch", PEB2466_CR3(2), 2, 1, 0), + SOC_SINGLE("DAC3 -6dB Playback Switch", PEB2466_CR3(3), 2, 1, 0), + + /* Amplifiers */ + SOC_SINGLE("ADC0 +6dB Capture Switch", PEB2466_CR3(0), 3, 1, 0), + SOC_SINGLE("ADC1 +6dB Capture Switch", PEB2466_CR3(1), 3, 1, 0), + SOC_SINGLE("ADC2 +6dB Capture Switch", PEB2466_CR3(2), 3, 1, 0), + SOC_SINGLE("ADC3 +6dB Capture Switch", PEB2466_CR3(3), 3, 1, 0), + + /* Tone generators */ + SOC_ENUM_EXT("DAC0 TG1 Freq", peb2466_tg_freq[0][0], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC1 TG1 Freq", peb2466_tg_freq[1][0], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC2 TG1 Freq", peb2466_tg_freq[2][0], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC3 TG1 Freq", peb2466_tg_freq[3][0], + peb2466_tg_freq_get, peb2466_tg_freq_put), + + SOC_ENUM_EXT("DAC0 TG2 Freq", peb2466_tg_freq[0][1], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC1 TG2 Freq", peb2466_tg_freq[1][1], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC2 TG2 Freq", peb2466_tg_freq[2][1], + peb2466_tg_freq_get, peb2466_tg_freq_put), + SOC_ENUM_EXT("DAC3 TG2 Freq", peb2466_tg_freq[3][1], + peb2466_tg_freq_get, peb2466_tg_freq_put), +}; + +static const struct snd_soc_dapm_widget peb2466_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("CH0 PWR", PEB2466_CR1(0), 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CH1 PWR", PEB2466_CR1(1), 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CH2 PWR", PEB2466_CR1(2), 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CH3 PWR", PEB2466_CR1(3), 0, 0, NULL, 0), + + SND_SOC_DAPM_DAC("CH0 DIN", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("CH1 DIN", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("CH2 DIN", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("CH3 DIN", "Playback", SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SIGGEN("CH0 TG1"), + SND_SOC_DAPM_SIGGEN("CH1 TG1"), + SND_SOC_DAPM_SIGGEN("CH2 TG1"), + SND_SOC_DAPM_SIGGEN("CH3 TG1"), + + SND_SOC_DAPM_SIGGEN("CH0 TG2"), + SND_SOC_DAPM_SIGGEN("CH1 TG2"), + SND_SOC_DAPM_SIGGEN("CH2 TG2"), + SND_SOC_DAPM_SIGGEN("CH3 TG2"), + + SND_SOC_DAPM_MIXER("DAC0 Mixer", SND_SOC_NOPM, 0, 0, + peb2466_ch0_out_mix_controls, + ARRAY_SIZE(peb2466_ch0_out_mix_controls)), + SND_SOC_DAPM_MIXER("DAC1 Mixer", SND_SOC_NOPM, 0, 0, + peb2466_ch1_out_mix_controls, + ARRAY_SIZE(peb2466_ch1_out_mix_controls)), + SND_SOC_DAPM_MIXER("DAC2 Mixer", SND_SOC_NOPM, 0, 0, + peb2466_ch2_out_mix_controls, + ARRAY_SIZE(peb2466_ch2_out_mix_controls)), + SND_SOC_DAPM_MIXER("DAC3 Mixer", SND_SOC_NOPM, 0, 0, + peb2466_ch3_out_mix_controls, + ARRAY_SIZE(peb2466_ch3_out_mix_controls)), + + SND_SOC_DAPM_PGA("DAC0 PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC1 PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC2 PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC3 PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("OUT0"), + SND_SOC_DAPM_OUTPUT("OUT1"), + SND_SOC_DAPM_OUTPUT("OUT2"), + SND_SOC_DAPM_OUTPUT("OUT3"), + + SND_SOC_DAPM_INPUT("IN0"), + SND_SOC_DAPM_INPUT("IN1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_INPUT("IN3"), + + SND_SOC_DAPM_DAC("ADC0", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("ADC1", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("ADC2", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("ADC3", "Capture", SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route peb2466_dapm_routes[] = { + { "CH0 DIN", NULL, "CH0 PWR" }, + { "CH1 DIN", NULL, "CH1 PWR" }, + { "CH2 DIN", NULL, "CH2 PWR" }, + { "CH3 DIN", NULL, "CH3 PWR" }, + + { "CH0 TG1", NULL, "CH0 PWR" }, + { "CH1 TG1", NULL, "CH1 PWR" }, + { "CH2 TG1", NULL, "CH2 PWR" }, + { "CH3 TG1", NULL, "CH3 PWR" }, + + { "CH0 TG2", NULL, "CH0 PWR" }, + { "CH1 TG2", NULL, "CH1 PWR" }, + { "CH2 TG2", NULL, "CH2 PWR" }, + { "CH3 TG2", NULL, "CH3 PWR" }, + + { "DAC0 Mixer", "TG1 Switch", "CH0 TG1" }, + { "DAC0 Mixer", "TG2 Switch", "CH0 TG2" }, + { "DAC0 Mixer", "Voice Switch", "CH0 DIN" }, + { "DAC0 Mixer", NULL, "CH0 DIN" }, + + { "DAC1 Mixer", "TG1 Switch", "CH1 TG1" }, + { "DAC1 Mixer", "TG2 Switch", "CH1 TG2" }, + { "DAC1 Mixer", "Voice Switch", "CH1 DIN" }, + { "DAC1 Mixer", NULL, "CH1 DIN" }, + + { "DAC2 Mixer", "TG1 Switch", "CH2 TG1" }, + { "DAC2 Mixer", "TG2 Switch", "CH2 TG2" }, + { "DAC2 Mixer", "Voice Switch", "CH2 DIN" }, + { "DAC2 Mixer", NULL, "CH2 DIN" }, + + { "DAC3 Mixer", "TG1 Switch", "CH3 TG1" }, + { "DAC3 Mixer", "TG2 Switch", "CH3 TG2" }, + { "DAC3 Mixer", "Voice Switch", "CH3 DIN" }, + { "DAC3 Mixer", NULL, "CH3 DIN" }, + + { "DAC0 PGA", NULL, "DAC0 Mixer" }, + { "DAC1 PGA", NULL, "DAC1 Mixer" }, + { "DAC2 PGA", NULL, "DAC2 Mixer" }, + { "DAC3 PGA", NULL, "DAC3 Mixer" }, + + { "OUT0", NULL, "DAC0 PGA" }, + { "OUT1", NULL, "DAC1 PGA" }, + { "OUT2", NULL, "DAC2 PGA" }, + { "OUT3", NULL, "DAC3 PGA" }, + + { "ADC0", NULL, "IN0" }, + { "ADC1", NULL, "IN1" }, + { "ADC2", NULL, "IN2" }, + { "ADC3", NULL, "IN3" }, + + { "ADC0", NULL, "CH0 PWR" }, + { "ADC1", NULL, "CH1 PWR" }, + { "ADC2", NULL, "CH2 PWR" }, + { "ADC3", NULL, "CH3 PWR" }, +}; + +static int peb2466_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component); + unsigned int chan; + unsigned int mask; + u8 slot; + int ret; + + switch (width) { + case 0: + /* Not set -> default 8 */ + case 8: + break; + default: + dev_err(dai->dev, "tdm slot width %d not supported\n", width); + return -EINVAL; + } + + mask = tx_mask; + slot = 0; + chan = 0; + while (mask && chan < PEB2466_NB_CHANNEL) { + if (mask & 0x1) { + ret = regmap_write(peb2466->regmap, PEB2466_CR5(chan), slot); + if (ret) { + dev_err(dai->dev, "chan %d set tx tdm slot failed (%d)\n", + chan, ret); + return ret; + } + chan++; + } + mask >>= 1; + slot++; + } + if (mask) { + dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n", + tx_mask, PEB2466_NB_CHANNEL); + return -EINVAL; + } + peb2466->max_chan_playback = chan; + + mask = rx_mask; + slot = 0; + chan = 0; + while (mask && chan < PEB2466_NB_CHANNEL) { + if (mask & 0x1) { + ret = regmap_write(peb2466->regmap, PEB2466_CR4(chan), slot); + if (ret) { + dev_err(dai->dev, "chan %d set rx tdm slot failed (%d)\n", + chan, ret); + return ret; + } + chan++; + } + mask >>= 1; + slot++; + } + if (mask) { + dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n", + rx_mask, PEB2466_NB_CHANNEL); + return -EINVAL; + } + peb2466->max_chan_capture = chan; + + return 0; +} + +static int peb2466_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component); + u8 xr6; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + xr6 = PEB2466_XR6_PCM_OFFSET(1); + break; + case SND_SOC_DAIFMT_DSP_B: + xr6 = PEB2466_XR6_PCM_OFFSET(0); + break; + default: + dev_err(dai->dev, "Unsupported format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return regmap_write(peb2466->regmap, PEB2466_XR6, xr6); +} + +static int peb2466_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component); + unsigned int ch; + int ret; + u8 cr1; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_MU_LAW: + cr1 = PEB2466_CR1_LAW_MULAW; + break; + case SNDRV_PCM_FORMAT_A_LAW: + cr1 = PEB2466_CR1_LAW_ALAW; + break; + default: + dev_err(&peb2466->spi->dev, "Unsupported format 0x%x\n", + params_format(params)); + return -EINVAL; + } + + for (ch = 0; ch < PEB2466_NB_CHANNEL; ch++) { + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR1(ch), + PEB2466_CR1_LAW_MASK, cr1); + if (ret) + return ret; + } + + return 0; +} + +static const unsigned int peb2466_sample_bits[] = {8}; + +static struct snd_pcm_hw_constraint_list peb2466_sample_bits_constr = { + .list = peb2466_sample_bits, + .count = ARRAY_SIZE(peb2466_sample_bits), +}; + +static int peb2466_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component); + unsigned int max_ch; + int ret; + + max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + peb2466->max_chan_playback : peb2466->max_chan_capture; + + /* + * Disable stream support (min = 0, max = 0) if no timeslots were + * configured. + */ + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + max_ch ? 1 : 0, max_ch); + if (ret < 0) + return ret; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &peb2466_sample_bits_constr); +} + +static u64 peb2466_dai_formats[] = { + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops peb2466_dai_ops = { + .startup = peb2466_dai_startup, + .hw_params = peb2466_dai_hw_params, + .set_tdm_slot = peb2466_dai_set_tdm_slot, + .set_fmt = peb2466_dai_set_fmt, + .auto_selectable_formats = peb2466_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(peb2466_dai_formats), +}; + +static struct snd_soc_dai_driver peb2466_dai_driver = { + .name = "peb2466", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = PEB2466_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = PEB2466_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + }, + .ops = &peb2466_dai_ops, +}; + +static int peb2466_reset_audio(struct peb2466 *peb2466) +{ + static const struct reg_sequence reg_reset[] = { + { .reg = PEB2466_XR6, .def = 0x00 }, + + { .reg = PEB2466_CR5(0), .def = 0x00 }, + { .reg = PEB2466_CR4(0), .def = 0x00 }, + { .reg = PEB2466_CR3(0), .def = 0x00 }, + { .reg = PEB2466_CR2(0), .def = 0x00 }, + { .reg = PEB2466_CR1(0), .def = 0x00 }, + { .reg = PEB2466_CR0(0), .def = PEB2466_CR0_IMR1 }, + + { .reg = PEB2466_CR5(1), .def = 0x00 }, + { .reg = PEB2466_CR4(1), .def = 0x00 }, + { .reg = PEB2466_CR3(1), .def = 0x00 }, + { .reg = PEB2466_CR2(1), .def = 0x00 }, + { .reg = PEB2466_CR1(1), .def = 0x00 }, + { .reg = PEB2466_CR0(1), .def = PEB2466_CR0_IMR1 }, + + { .reg = PEB2466_CR5(2), .def = 0x00 }, + { .reg = PEB2466_CR4(2), .def = 0x00 }, + { .reg = PEB2466_CR3(2), .def = 0x00 }, + { .reg = PEB2466_CR2(2), .def = 0x00 }, + { .reg = PEB2466_CR1(2), .def = 0x00 }, + { .reg = PEB2466_CR0(2), .def = PEB2466_CR0_IMR1 }, + + { .reg = PEB2466_CR5(3), .def = 0x00 }, + { .reg = PEB2466_CR4(3), .def = 0x00 }, + { .reg = PEB2466_CR3(3), .def = 0x00 }, + { .reg = PEB2466_CR2(3), .def = 0x00 }, + { .reg = PEB2466_CR1(3), .def = 0x00 }, + { .reg = PEB2466_CR0(3), .def = PEB2466_CR0_IMR1 }, + }; + static const u8 imr1_p1[8] = {0x00, 0x90, 0x09, 0x00, 0x90, 0x09, 0x00, 0x00}; + static const u8 imr1_p2[8] = {0x7F, 0xFF, 0x00, 0x00, 0x90, 0x14, 0x40, 0x08}; + static const u8 zero[8] = {0}; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + peb2466->ch[i].tg1_freq_item = PEB2466_TONE_1000HZ; + peb2466->ch[i].tg2_freq_item = PEB2466_TONE_1000HZ; + + /* + * Even if not used, disabling IM/R1 filter is not recommended. + * Instead, we must configure it with default coefficients and + * enable it. + * The filter will be enabled right after (in the following + * regmap_multi_reg_write() call). + */ + ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P1(i), imr1_p1, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P2(i), imr1_p2, 8); + if (ret) + return ret; + + /* Set all other filters coefficients to zero */ + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P1(i), zero, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P2(i), zero, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P3(i), zero, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_FRX_FILTER(i), zero, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_FRR_FILTER(i), zero, 8); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), zero, 4); + if (ret) + return ret; + ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), zero, 4); + if (ret) + return ret; + } + + return regmap_multi_reg_write(peb2466->regmap, reg_reset, ARRAY_SIZE(reg_reset)); +} + +static int peb2466_fw_parse_thfilter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw TH filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * TH_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 8 bytes: TH-Filter coefficients part1 + * - @9 8 bytes: TH-Filter coefficients part2 + * - @17 8 bytes: TH-Filter coefficients part3 + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_TH, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P1(i), data + 1, 8); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P2(i), data + 9, 8); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P3(i), data + 17, 8); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_TH | PEB2466_CR0_THSEL_MASK, + PEB2466_CR0_TH | PEB2466_CR0_THSEL(i)); + if (ret) + return ret; + } + return 0; +} + +static int peb2466_fw_parse_imr1filter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw IM/R1 filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * IMR1_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 8 bytes: IM/R1-Filter coefficients part1 + * - @9 8 bytes: IM/R1-Filter coefficients part2 + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_IMR1, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P1(i), data + 1, 8); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P2(i), data + 9, 8); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_IMR1, PEB2466_CR0_IMR1); + if (ret) + return ret; + } + return 0; +} + +static int peb2466_fw_parse_frxfilter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw FRX filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * FRX_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 8 bytes: FRX-Filter coefficients + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_FRX, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_FRX_FILTER(i), data + 1, 8); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_FRX, PEB2466_CR0_FRX); + if (ret) + return ret; + } + return 0; +} + +static int peb2466_fw_parse_frrfilter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw FRR filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * FRR_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 8 bytes: FRR-Filter coefficients + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_FRR, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_FRR_FILTER(i), data + 1, 8); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_FRR, PEB2466_CR0_FRR); + if (ret) + return ret; + } + return 0; +} + +static int peb2466_fw_parse_axfilter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw AX filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * AX_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 4 bytes: AX-Filter coefficients + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AX, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), data + 1, 4); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AX, PEB2466_CR0_AX); + if (ret) + return ret; + } + return 0; +} + +static int peb2466_fw_parse_arfilter(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + u8 mask; + int ret; + int i; + + dev_info(component->dev, "fw AR filter: mask %x, %*phN\n", *data, + lng - 1, data + 1); + + /* + * AR_FILTER TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 4 bytes: AR-Filter coefficients + */ + mask = *data; + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AR, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), data + 1, 4); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AR, PEB2466_CR0_AR); + if (ret) + return ret; + } + return 0; +} + +static const char * const peb2466_ax_ctrl_names[] = { + "ADC0 Capture Volume", + "ADC1 Capture Volume", + "ADC2 Capture Volume", + "ADC3 Capture Volume", +}; + +static int peb2466_fw_parse_axtable(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + struct peb2466_lkup_ctrl *lkup_ctrl; + struct peb2466_lookup *lookup; + u8 (*table)[4]; + u32 table_size; + u32 init_index; + s32 min_val; + s32 step; + u8 mask; + int ret; + int i; + + /* + * AX_TABLE TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 32bits signed: Min table value in centi dB (MinVal) + * ie -300 means -3.0 dB + * - @5 32bits signed: Step from on item to other item in centi dB (Step) + * ie 25 means 0.25 dB) + * - @9 32bits unsigned: Item index in the table to use for the initial + * value + * - @13 N*4 bytes: Table composed of 4 bytes items. + * Each item correspond to an AX filter value. + * + * The conversion from raw value item in the table to/from the value in + * dB is: Raw value at index i <-> (MinVal + i * Step) in centi dB. + */ + + /* Check Lng and extract the table size. */ + if (lng < 13 || ((lng - 13) % 4)) { + dev_err(component->dev, "fw AX table lng %u invalid\n", lng); + return -EINVAL; + } + table_size = lng - 13; + + min_val = get_unaligned_be32(data + 1); + step = get_unaligned_be32(data + 5); + init_index = get_unaligned_be32(data + 9); + if (init_index >= (table_size / 4)) { + dev_err(component->dev, "fw AX table index %u out of table[%u]\n", + init_index, table_size / 4); + return -EINVAL; + } + + dev_info(component->dev, + "fw AX table: mask %x, min %d, step %d, %u items, tbl[%u] %*phN\n", + *data, min_val, step, table_size / 4, init_index, + 4, data + 13 + (init_index * 4)); + + BUILD_BUG_ON(sizeof(*table) != 4); + table = devm_kzalloc(&peb2466->spi->dev, table_size, GFP_KERNEL); + if (!table) + return -ENOMEM; + memcpy(table, data + 13, table_size); + + mask = *data; + BUILD_BUG_ON(ARRAY_SIZE(peb2466_ax_ctrl_names) != ARRAY_SIZE(peb2466->ch)); + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + lookup = &peb2466->ch[i].ax_lookup; + lookup->table = table; + lookup->count = table_size / 4; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AX, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), + lookup->table[init_index], 4); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AX, PEB2466_CR0_AX); + if (ret) + return ret; + + lkup_ctrl = &peb2466->ch[i].ax_lkup_ctrl; + lkup_ctrl->lookup = lookup; + lkup_ctrl->reg = PEB2466_AX_FILTER(i); + lkup_ctrl->index = init_index; + + ret = peb2466_add_lkup_ctrl(component, lkup_ctrl, + peb2466_ax_ctrl_names[i], + min_val, step); + if (ret) + return ret; + } + return 0; +} + +static const char * const peb2466_ar_ctrl_names[] = { + "DAC0 Playback Volume", + "DAC1 Playback Volume", + "DAC2 Playback Volume", + "DAC3 Playback Volume", +}; + +static int peb2466_fw_parse_artable(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + struct peb2466_lkup_ctrl *lkup_ctrl; + struct peb2466_lookup *lookup; + u8 (*table)[4]; + u32 table_size; + u32 init_index; + s32 min_val; + s32 step; + u8 mask; + int ret; + int i; + + /* + * AR_TABLE TLV data: + * - @0 1 byte: Chan mask (bit set means related channel is concerned) + * - @1 32bits signed: Min table value in centi dB (MinVal) + * ie -300 means -3.0 dB + * - @5 32bits signed: Step from on item to other item in centi dB (Step) + * ie 25 means 0.25 dB) + * - @9 32bits unsigned: Item index in the table to use for the initial + * value + * - @13 N*4 bytes: Table composed of 4 bytes items. + * Each item correspond to an AR filter value. + * + * The conversion from raw value item in the table to/from the value in + * dB is: Raw value at index i <-> (MinVal + i * Step) in centi dB. + */ + + /* Check Lng and extract the table size. */ + if (lng < 13 || ((lng - 13) % 4)) { + dev_err(component->dev, "fw AR table lng %u invalid\n", lng); + return -EINVAL; + } + table_size = lng - 13; + + min_val = get_unaligned_be32(data + 1); + step = get_unaligned_be32(data + 5); + init_index = get_unaligned_be32(data + 9); + if (init_index >= (table_size / 4)) { + dev_err(component->dev, "fw AR table index %u out of table[%u]\n", + init_index, table_size / 4); + return -EINVAL; + } + + dev_info(component->dev, + "fw AR table: mask %x, min %d, step %d, %u items, tbl[%u] %*phN\n", + *data, min_val, step, table_size / 4, init_index, + 4, data + 13 + (init_index * 4)); + + BUILD_BUG_ON(sizeof(*table) != 4); + table = devm_kzalloc(&peb2466->spi->dev, table_size, GFP_KERNEL); + if (!table) + return -ENOMEM; + memcpy(table, data + 13, table_size); + + mask = *data; + BUILD_BUG_ON(ARRAY_SIZE(peb2466_ar_ctrl_names) != ARRAY_SIZE(peb2466->ch)); + for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) { + if (!(mask & (1 << i))) + continue; + + lookup = &peb2466->ch[i].ar_lookup; + lookup->table = table; + lookup->count = table_size / 4; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AR, 0); + if (ret) + return ret; + + ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), + lookup->table[init_index], 4); + if (ret) + return ret; + + ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i), + PEB2466_CR0_AR, PEB2466_CR0_AR); + if (ret) + return ret; + + lkup_ctrl = &peb2466->ch[i].ar_lkup_ctrl; + lkup_ctrl->lookup = lookup; + lkup_ctrl->reg = PEB2466_AR_FILTER(i); + lkup_ctrl->index = init_index; + + ret = peb2466_add_lkup_ctrl(component, lkup_ctrl, + peb2466_ar_ctrl_names[i], + min_val, step); + if (ret) + return ret; + } + return 0; +} + +struct peb2466_fw_tag_def { + u16 tag; + u32 lng_min; + u32 lng_max; + int (*parse)(struct snd_soc_component *component, + u16 tag, u32 lng, const u8 *data); +}; + +#define PEB2466_TAG_DEF_LNG_EQ(__tag, __lng, __parse) { \ + .tag = __tag, \ + .lng_min = __lng, \ + .lng_max = __lng, \ + .parse = __parse, \ +} + +#define PEB2466_TAG_DEF_LNG_MIN(__tag, __lng_min, __parse) { \ + .tag = __tag, \ + .lng_min = __lng_min, \ + .lng_max = U32_MAX, \ + .parse = __parse, \ +} + +static const struct peb2466_fw_tag_def peb2466_fw_tag_defs[] = { + /* TH FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0001, 1 + 3 * 8, peb2466_fw_parse_thfilter), + /* IMR1 FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0002, 1 + 2 * 8, peb2466_fw_parse_imr1filter), + /* FRX FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0003, 1 + 8, peb2466_fw_parse_frxfilter), + /* FRR FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0004, 1 + 8, peb2466_fw_parse_frrfilter), + /* AX FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0005, 1 + 4, peb2466_fw_parse_axfilter), + /* AR FILTER */ + PEB2466_TAG_DEF_LNG_EQ(0x0006, 1 + 4, peb2466_fw_parse_arfilter), + /* AX TABLE */ + PEB2466_TAG_DEF_LNG_MIN(0x0105, 1 + 3 * 4, peb2466_fw_parse_axtable), + /* AR TABLE */ + PEB2466_TAG_DEF_LNG_MIN(0x0106, 1 + 3 * 4, peb2466_fw_parse_artable), +}; + +static const struct peb2466_fw_tag_def *peb2466_fw_get_tag_def(u16 tag) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(peb2466_fw_tag_defs); i++) { + if (peb2466_fw_tag_defs[i].tag == tag) + return &peb2466_fw_tag_defs[i]; + } + return NULL; +} + +static int peb2466_fw_parse(struct snd_soc_component *component, + const u8 *data, size_t size) +{ + const struct peb2466_fw_tag_def *tag_def; + size_t left; + const u8 *buf; + u16 val16; + u16 tag; + u32 lng; + int ret; + + /* + * Coefficients firmware binary structure (16bits and 32bits are + * big-endian values). + * + * @0, 16bits: Magic (0x2466) + * @2, 16bits: Version (0x0100 for version 1.0) + * @4, 2+4+N bytes: TLV block + * @4+(2+4+N) bytes: Next TLV block + * ... + * + * Detail of a TLV block: + * @0, 16bits: Tag + * @2, 32bits: Lng + * @6, lng bytes: Data + * + * The detail the Data for a given TLV Tag is provided in the related + * parser. + */ + + left = size; + buf = data; + + if (left < 4) { + dev_err(component->dev, "fw size %zu, exp at least 4\n", left); + return -EINVAL; + } + + /* Check magic */ + val16 = get_unaligned_be16(buf); + if (val16 != 0x2466) { + dev_err(component->dev, "fw magic 0x%04x exp 0x2466\n", val16); + return -EINVAL; + } + buf += 2; + left -= 2; + + /* Check version */ + val16 = get_unaligned_be16(buf); + if (val16 != 0x0100) { + dev_err(component->dev, "fw magic 0x%04x exp 0x0100\n", val16); + return -EINVAL; + } + buf += 2; + left -= 2; + + while (left) { + if (left < 6) { + dev_err(component->dev, "fw %td/%zu left %zu, exp at least 6\n", + buf - data, size, left); + return -EINVAL; + } + /* Check tag and lng */ + tag = get_unaligned_be16(buf); + lng = get_unaligned_be32(buf + 2); + tag_def = peb2466_fw_get_tag_def(tag); + if (!tag_def) { + dev_err(component->dev, "fw %td/%zu tag 0x%04x unknown\n", + buf - data, size, tag); + return -EINVAL; + } + if (lng < tag_def->lng_min || lng > tag_def->lng_max) { + dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u, exp [%u;%u]\n", + buf - data, size, tag, lng, tag_def->lng_min, tag_def->lng_max); + return -EINVAL; + } + buf += 6; + left -= 6; + if (left < lng) { + dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u, left %zu\n", + buf - data, size, tag, lng, left); + return -EINVAL; + } + + /* TLV block is valid -> parse the data part */ + ret = tag_def->parse(component, tag, lng, buf); + if (ret) { + dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u parse failed\n", + buf - data, size, tag, lng); + return ret; + } + + buf += lng; + left -= lng; + } + return 0; +} + +static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name) +{ + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, fw_name, component->dev); + if (ret) + return ret; + + ret = peb2466_fw_parse(component, fw->data, fw->size); + release_firmware(fw); + + return ret; +} + +static int peb2466_component_probe(struct snd_soc_component *component) +{ + struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component); + const char *firmware_name; + int ret; + + /* reset peb2466 audio part */ + ret = peb2466_reset_audio(peb2466); + if (ret) + return ret; + + ret = of_property_read_string(peb2466->spi->dev.of_node, + "firmware-name", &firmware_name); + if (ret) + return (ret == -EINVAL) ? 0 : ret; + + return peb2466_load_coeffs(component, firmware_name); +} + +static const struct snd_soc_component_driver peb2466_component_driver = { + .probe = peb2466_component_probe, + .controls = peb2466_controls, + .num_controls = ARRAY_SIZE(peb2466_controls), + .dapm_widgets = peb2466_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(peb2466_dapm_widgets), + .dapm_routes = peb2466_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(peb2466_dapm_routes), + .endianness = 1, +}; + +/* + * The mapping used for the relationship between the gpio offset and the + * physical pin is the following: + * + * offset pin + * 0 SI1_0 + * 1 SI1_1 + * 2 SI2_0 + * 3 SI2_1 + * 4 SI3_0 + * 5 SI3_1 + * 6 SI4_0 + * 7 SI4_1 + * 8 SO1_0 + * 9 SO1_1 + * 10 SO2_0 + * 11 SO2_1 + * 12 SO3_0 + * 13 SO3_1 + * 14 SO4_0 + * 15 SO4_1 + * 16 SB1_0 + * 17 SB1_1 + * 18 SB2_0 + * 19 SB2_1 + * 20 SB3_0 + * 21 SB3_1 + * 22 SB4_0 + * 23 SB4_1 + * 24 SB1_2 + * 25 SB2_2 + * 26 SB3_2 + * 27 SB4_2 + */ + +static int peb2466_chip_gpio_offset_to_data_regmask(unsigned int offset, + unsigned int *xr_reg, + unsigned int *mask) +{ + if (offset < 16) { + /* + * SIx_{0,1} and SOx_{0,1} + * Read accesses read SIx_{0,1} values + * Write accesses write SOx_{0,1} values + */ + *xr_reg = PEB2466_XR0; + *mask = (1 << (offset % 8)); + return 0; + } + if (offset < 24) { + /* SBx_{0,1} */ + *xr_reg = PEB2466_XR1; + *mask = (1 << (offset - 16)); + return 0; + } + if (offset < 28) { + /* SBx_2 */ + *xr_reg = PEB2466_XR3; + *mask = (1 << (offset - 24 + 4)); + return 0; + } + return -EINVAL; +} + +static int peb2466_chip_gpio_offset_to_dir_regmask(unsigned int offset, + unsigned int *xr_reg, + unsigned int *mask) +{ + if (offset < 16) { + /* Direction cannot be changed for these GPIOs */ + return -EINVAL; + } + if (offset < 24) { + *xr_reg = PEB2466_XR2; + *mask = (1 << (offset - 16)); + return 0; + } + if (offset < 28) { + *xr_reg = PEB2466_XR3; + *mask = (1 << (offset - 24)); + return 0; + } + return -EINVAL; +} + +static unsigned int *peb2466_chip_gpio_get_cache(struct peb2466 *peb2466, + unsigned int xr_reg) +{ + unsigned int *cache; + + switch (xr_reg) { + case PEB2466_XR0: + cache = &peb2466->gpio.cache.xr0; + break; + case PEB2466_XR1: + cache = &peb2466->gpio.cache.xr1; + break; + case PEB2466_XR2: + cache = &peb2466->gpio.cache.xr2; + break; + case PEB2466_XR3: + cache = &peb2466->gpio.cache.xr3; + break; + default: + cache = NULL; + break; + } + return cache; +} + +static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int xr_reg, + unsigned int mask, unsigned int val) +{ + unsigned int tmp; + unsigned int *cache; + int ret; + + /* + * Read and write accesses use different peb2466 internal signals (input + * signals on reads and output signals on writes). regmap_update_bits + * cannot be used to read/modify/write the value. + * So, a specific cache value is used. + */ + + mutex_lock(&peb2466->gpio.lock); + + cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg); + if (!cache) { + ret = -EINVAL; + goto end; + } + + tmp = *cache; + tmp &= ~mask; + tmp |= val; + + ret = regmap_write(peb2466->regmap, xr_reg, tmp); + if (ret) + goto end; + + *cache = tmp; + ret = 0; + +end: + mutex_unlock(&peb2466->gpio.lock); + return ret; +} + +static void peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) +{ + struct peb2466 *peb2466 = gpiochip_get_data(c); + unsigned int xr_reg; + unsigned int mask; + int ret; + + if (offset < 8) { + /* + * SIx_{0,1} signals cannot be set and writing the related + * register will change the SOx_{0,1} signals + */ + dev_warn(&peb2466->spi->dev, "cannot set gpio %d (read-only)\n", + offset); + return; + } + + ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask); + if (ret) { + dev_err(&peb2466->spi->dev, "cannot set gpio %d (%d)\n", + offset, ret); + return; + } + + ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, val ? mask : 0); + if (ret) { + dev_err(&peb2466->spi->dev, "set gpio %d (0x%x, 0x%x) failed (%d)\n", + offset, xr_reg, mask, ret); + } +} + +static int peb2466_chip_gpio_get(struct gpio_chip *c, unsigned int offset) +{ + struct peb2466 *peb2466 = gpiochip_get_data(c); + bool use_cache = false; + unsigned int *cache; + unsigned int xr_reg; + unsigned int mask; + unsigned int val; + int ret; + + if (offset >= 8 && offset < 16) { + /* + * SOx_{0,1} signals cannot be read. Reading the related + * register will read the SIx_{0,1} signals. + * Use the cache to get value; + */ + use_cache = true; + } + + ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask); + if (ret) { + dev_err(&peb2466->spi->dev, "cannot get gpio %d (%d)\n", + offset, ret); + return -EINVAL; + } + + if (use_cache) { + cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg); + if (!cache) + return -EINVAL; + val = *cache; + } else { + ret = regmap_read(peb2466->regmap, xr_reg, &val); + if (ret) { + dev_err(&peb2466->spi->dev, "get gpio %d (0x%x, 0x%x) failed (%d)\n", + offset, xr_reg, mask, ret); + return ret; + } + } + + return !!(val & mask); +} + +static int peb2466_chip_get_direction(struct gpio_chip *c, unsigned int offset) +{ + struct peb2466 *peb2466 = gpiochip_get_data(c); + unsigned int xr_reg; + unsigned int mask; + unsigned int val; + int ret; + + if (offset < 8) { + /* SIx_{0,1} */ + return GPIO_LINE_DIRECTION_IN; + } + if (offset < 16) { + /* SOx_{0,1} */ + return GPIO_LINE_DIRECTION_OUT; + } + + ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask); + if (ret) { + dev_err(&peb2466->spi->dev, "cannot get gpio %d direction (%d)\n", + offset, ret); + return ret; + } + + ret = regmap_read(peb2466->regmap, xr_reg, &val); + if (ret) { + dev_err(&peb2466->spi->dev, "get dir gpio %d (0x%x, 0x%x) failed (%d)\n", + offset, xr_reg, mask, ret); + return ret; + } + + return val & mask ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static int peb2466_chip_direction_input(struct gpio_chip *c, unsigned int offset) +{ + struct peb2466 *peb2466 = gpiochip_get_data(c); + unsigned int xr_reg; + unsigned int mask; + int ret; + + if (offset < 8) { + /* SIx_{0,1} */ + return 0; + } + if (offset < 16) { + /* SOx_{0,1} */ + return -EINVAL; + } + + ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask); + if (ret) { + dev_err(&peb2466->spi->dev, "cannot set gpio %d direction (%d)\n", + offset, ret); + return ret; + } + + ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, 0); + if (ret) { + dev_err(&peb2466->spi->dev, "Set dir in gpio %d (0x%x, 0x%x) failed (%d)\n", + offset, xr_reg, mask, ret); + return ret; + } + + return 0; +} + +static int peb2466_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val) +{ + struct peb2466 *peb2466 = gpiochip_get_data(c); + unsigned int xr_reg; + unsigned int mask; + int ret; + + if (offset < 8) { + /* SIx_{0,1} */ + return -EINVAL; + } + + peb2466_chip_gpio_set(c, offset, val); + + if (offset < 16) { + /* SOx_{0,1} */ + return 0; + } + + ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask); + if (ret) { + dev_err(&peb2466->spi->dev, "cannot set gpio %d direction (%d)\n", + offset, ret); + return ret; + } + + ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, mask); + if (ret) { + dev_err(&peb2466->spi->dev, "Set dir in gpio %d (0x%x, 0x%x) failed (%d)\n", + offset, xr_reg, mask, ret); + return ret; + } + + return 0; +} + +static int peb2466_reset_gpio(struct peb2466 *peb2466) +{ + static const struct reg_sequence reg_reset[] = { + /* Output pins at 0, input/output pins as input */ + { .reg = PEB2466_XR0, .def = 0 }, + { .reg = PEB2466_XR1, .def = 0 }, + { .reg = PEB2466_XR2, .def = 0 }, + { .reg = PEB2466_XR3, .def = 0 }, + }; + + peb2466->gpio.cache.xr0 = 0; + peb2466->gpio.cache.xr1 = 0; + peb2466->gpio.cache.xr2 = 0; + peb2466->gpio.cache.xr3 = 0; + + return regmap_multi_reg_write(peb2466->regmap, reg_reset, ARRAY_SIZE(reg_reset)); +} + +static int peb2466_gpio_init(struct peb2466 *peb2466) +{ + int ret; + + mutex_init(&peb2466->gpio.lock); + + ret = peb2466_reset_gpio(peb2466); + if (ret) + return ret; + + peb2466->gpio.gpio_chip.owner = THIS_MODULE; + peb2466->gpio.gpio_chip.label = dev_name(&peb2466->spi->dev); + peb2466->gpio.gpio_chip.parent = &peb2466->spi->dev; + peb2466->gpio.gpio_chip.base = -1; + peb2466->gpio.gpio_chip.ngpio = 28; + peb2466->gpio.gpio_chip.get_direction = peb2466_chip_get_direction; + peb2466->gpio.gpio_chip.direction_input = peb2466_chip_direction_input; + peb2466->gpio.gpio_chip.direction_output = peb2466_chip_direction_output; + peb2466->gpio.gpio_chip.get = peb2466_chip_gpio_get; + peb2466->gpio.gpio_chip.set = peb2466_chip_gpio_set; + peb2466->gpio.gpio_chip.can_sleep = true; + + return devm_gpiochip_add_data(&peb2466->spi->dev, &peb2466->gpio.gpio_chip, + peb2466); +} + +static int peb2466_spi_probe(struct spi_device *spi) +{ + struct peb2466 *peb2466; + unsigned long mclk_rate; + int ret; + u8 xr5; + + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + peb2466 = devm_kzalloc(&spi->dev, sizeof(*peb2466), GFP_KERNEL); + if (!peb2466) + return -ENOMEM; + + peb2466->spi = spi; + + peb2466->regmap = devm_regmap_init(&peb2466->spi->dev, NULL, peb2466, + &peb2466_regmap_config); + if (IS_ERR(peb2466->regmap)) + return PTR_ERR(peb2466->regmap); + + peb2466->reset_gpio = devm_gpiod_get_optional(&peb2466->spi->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(peb2466->reset_gpio)) + return PTR_ERR(peb2466->reset_gpio); + + peb2466->mclk = devm_clk_get(&peb2466->spi->dev, "mclk"); + if (IS_ERR(peb2466->mclk)) + return PTR_ERR(peb2466->mclk); + ret = clk_prepare_enable(peb2466->mclk); + if (ret) + return ret; + + if (peb2466->reset_gpio) { + gpiod_set_value_cansleep(peb2466->reset_gpio, 1); + udelay(4); + gpiod_set_value_cansleep(peb2466->reset_gpio, 0); + udelay(4); + } + + spi_set_drvdata(spi, peb2466); + + mclk_rate = clk_get_rate(peb2466->mclk); + switch (mclk_rate) { + case 1536000: + xr5 = PEB2466_XR5_MCLK_1536; + break; + case 2048000: + xr5 = PEB2466_XR5_MCLK_2048; + break; + case 4096000: + xr5 = PEB2466_XR5_MCLK_4096; + break; + case 8192000: + xr5 = PEB2466_XR5_MCLK_8192; + break; + default: + dev_err(&peb2466->spi->dev, "Unsupported clock rate %lu\n", + mclk_rate); + ret = -EINVAL; + goto failed; + } + ret = regmap_write(peb2466->regmap, PEB2466_XR5, xr5); + if (ret) { + dev_err(&peb2466->spi->dev, "Setting MCLK failed (%d)\n", ret); + goto failed; + } + + ret = devm_snd_soc_register_component(&spi->dev, &peb2466_component_driver, + &peb2466_dai_driver, 1); + if (ret) + goto failed; + + if (IS_ENABLED(CONFIG_GPIOLIB)) { + ret = peb2466_gpio_init(peb2466); + if (ret) + goto failed; + } + + return 0; + +failed: + clk_disable_unprepare(peb2466->mclk); + return ret; +} + +static void peb2466_spi_remove(struct spi_device *spi) +{ + struct peb2466 *peb2466 = spi_get_drvdata(spi); + + clk_disable_unprepare(peb2466->mclk); +} + +static const struct of_device_id peb2466_of_match[] = { + { .compatible = "infineon,peb2466", }, + { } +}; +MODULE_DEVICE_TABLE(of, peb2466_of_match); + +static const struct spi_device_id peb2466_id_table[] = { + { "peb2466", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, peb2466_id_table); + +static struct spi_driver peb2466_spi_driver = { + .driver = { + .name = "peb2466", + .of_match_table = peb2466_of_match, + }, + .id_table = peb2466_id_table, + .probe = peb2466_spi_probe, + .remove = peb2466_spi_remove, +}; + +module_spi_driver(peb2466_spi_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("PEB2466 ALSA SoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c new file mode 100644 index 0000000000..9697aefc6e --- /dev/null +++ b/sound/soc/codecs/rk3328_codec.c @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rk3328 ALSA SoC Audio driver +// +// Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk3328_codec.h" + +/* + * volume setting + * 0: -39dB + * 26: 0dB + * 31: 6dB + * Step: 1.5dB + */ +#define OUT_VOLUME (0x18) +#define RK3328_GRF_SOC_CON2 (0x0408) +#define RK3328_GRF_SOC_CON10 (0x0428) +#define INITIAL_FREQ (11289600) + +struct rk3328_codec_priv { + struct regmap *regmap; + struct gpio_desc *mute; + struct clk *mclk; + struct clk *pclk; + unsigned int sclk; + int spk_depop_time; /* msec */ +}; + +static const struct reg_default rk3328_codec_reg_defaults[] = { + { CODEC_RESET, 0x03 }, + { DAC_INIT_CTRL1, 0x00 }, + { DAC_INIT_CTRL2, 0x50 }, + { DAC_INIT_CTRL3, 0x0e }, + { DAC_PRECHARGE_CTRL, 0x01 }, + { DAC_PWR_CTRL, 0x00 }, + { DAC_CLK_CTRL, 0x00 }, + { HPMIX_CTRL, 0x00 }, + { HPOUT_CTRL, 0x00 }, + { HPOUTL_GAIN_CTRL, 0x00 }, + { HPOUTR_GAIN_CTRL, 0x00 }, + { HPOUT_POP_CTRL, 0x11 }, +}; + +static int rk3328_codec_reset(struct rk3328_codec_priv *rk3328) +{ + regmap_write(rk3328->regmap, CODEC_RESET, 0x00); + mdelay(10); + regmap_write(rk3328->regmap, CODEC_RESET, 0x03); + + return 0; +} + +static int rk3328_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + val = PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE; + break; + case SND_SOC_DAIFMT_CBP_CFP: + val = PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL1, + PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK, val); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + val = DAC_MODE_PCM; + break; + case SND_SOC_DAIFMT_I2S: + val = DAC_MODE_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + val = DAC_MODE_RJM; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = DAC_MODE_LJM; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2, + DAC_MODE_MASK, val); + + return 0; +} + +static int rk3328_mute_stream(struct snd_soc_dai *dai, int mute, int direction) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + if (mute) + val = HPOUTL_MUTE | HPOUTR_MUTE; + else + val = HPOUTL_UNMUTE | HPOUTR_UNMUTE; + + regmap_update_bits(rk3328->regmap, HPOUT_CTRL, + HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, val); + + return 0; +} + +static int rk3328_codec_power_on(struct rk3328_codec_priv *rk3328, int wait_ms) +{ + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_PRECHARGE); + mdelay(10); + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_CURRENT_ALL_MASK, + DAC_CHARGE_CURRENT_ALL_ON); + mdelay(wait_ms); + + return 0; +} + +static int rk3328_codec_power_off(struct rk3328_codec_priv *rk3328, int wait_ms) +{ + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_XCHARGE_MASK, DAC_CHARGE_DISCHARGE); + mdelay(10); + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_CURRENT_ALL_MASK, + DAC_CHARGE_CURRENT_ALL_ON); + mdelay(wait_ms); + + return 0; +} + +static const struct rk3328_reg_msk_val playback_open_list[] = { + { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_ON }, + { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK, + DACL_PATH_REFV_ON | DACR_PATH_REFV_ON }, + { DAC_PWR_CTRL, HPOUTL_ZERO_CROSSING_MASK | HPOUTR_ZERO_CROSSING_MASK, + HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON }, + { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK, + HPOUTR_POP_WORK | HPOUTL_POP_WORK }, + { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_EN | HPMIXR_EN }, + { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK, + HPMIXL_INIT_EN | HPMIXR_INIT_EN }, + { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_EN | HPOUTR_EN }, + { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK, + HPOUTL_INIT_EN | HPOUTR_INIT_EN }, + { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK, + DACL_REFV_ON | DACR_REFV_ON }, + { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK, + DACL_CLK_ON | DACR_CLK_ON }, + { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_ON | DACR_ON }, + { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK, + DACL_INIT_ON | DACR_INIT_ON }, + { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK, + DACL_SELECT | DACR_SELECT }, + { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK, + HPMIXL_INIT2_EN | HPMIXR_INIT2_EN }, + { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, + HPOUTL_UNMUTE | HPOUTR_UNMUTE }, +}; + +static int rk3328_codec_open_playback(struct rk3328_codec_priv *rk3328) +{ + int i; + + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_CURRENT_ALL_MASK, + DAC_CHARGE_CURRENT_I); + + for (i = 0; i < ARRAY_SIZE(playback_open_list); i++) { + regmap_update_bits(rk3328->regmap, + playback_open_list[i].reg, + playback_open_list[i].msk, + playback_open_list[i].val); + mdelay(1); + } + + msleep(rk3328->spk_depop_time); + gpiod_set_value(rk3328->mute, 0); + + regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL, + HPOUTL_GAIN_MASK, OUT_VOLUME); + regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL, + HPOUTR_GAIN_MASK, OUT_VOLUME); + + return 0; +} + +static const struct rk3328_reg_msk_val playback_close_list[] = { + { HPMIX_CTRL, HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK, + HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS }, + { DAC_SELECT, DACL_SELECT_MASK | DACR_SELECT_MASK, + DACL_UNSELECT | DACR_UNSELECT }, + { HPOUT_CTRL, HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK, + HPOUTL_MUTE | HPOUTR_MUTE }, + { HPOUT_CTRL, HPOUTL_INIT_MASK | HPOUTR_INIT_MASK, + HPOUTL_INIT_DIS | HPOUTR_INIT_DIS }, + { HPOUT_CTRL, HPOUTL_MASK | HPOUTR_MASK, HPOUTL_DIS | HPOUTR_DIS }, + { HPMIX_CTRL, HPMIXL_MASK | HPMIXR_MASK, HPMIXL_DIS | HPMIXR_DIS }, + { DAC_CLK_CTRL, DACL_MASK | DACR_MASK, DACL_OFF | DACR_OFF }, + { DAC_CLK_CTRL, DACL_CLK_MASK | DACR_CLK_MASK, + DACL_CLK_OFF | DACR_CLK_OFF }, + { DAC_CLK_CTRL, DACL_REFV_MASK | DACR_REFV_MASK, + DACL_REFV_OFF | DACR_REFV_OFF }, + { HPOUT_POP_CTRL, HPOUTR_POP_MASK | HPOUTL_POP_MASK, + HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE }, + { DAC_PWR_CTRL, DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK, + DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF }, + { DAC_PWR_CTRL, DAC_PWR_MASK, DAC_PWR_OFF }, + { HPMIX_CTRL, HPMIXL_INIT_MASK | HPMIXR_INIT_MASK, + HPMIXL_INIT_DIS | HPMIXR_INIT_DIS }, + { DAC_CLK_CTRL, DACL_INIT_MASK | DACR_INIT_MASK, + DACL_INIT_OFF | DACR_INIT_OFF }, +}; + +static int rk3328_codec_close_playback(struct rk3328_codec_priv *rk3328) +{ + size_t i; + + gpiod_set_value(rk3328->mute, 1); + + regmap_update_bits(rk3328->regmap, HPOUTL_GAIN_CTRL, + HPOUTL_GAIN_MASK, 0); + regmap_update_bits(rk3328->regmap, HPOUTR_GAIN_CTRL, + HPOUTR_GAIN_MASK, 0); + + for (i = 0; i < ARRAY_SIZE(playback_close_list); i++) { + regmap_update_bits(rk3328->regmap, + playback_close_list[i].reg, + playback_close_list[i].msk, + playback_close_list[i].val); + mdelay(1); + } + + /* Workaround for silence when changed Fs 48 -> 44.1kHz */ + rk3328_codec_reset(rk3328); + + regmap_update_bits(rk3328->regmap, DAC_PRECHARGE_CTRL, + DAC_CHARGE_CURRENT_ALL_MASK, + DAC_CHARGE_CURRENT_ALL_ON); + + return 0; +} + +static int rk3328_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(dai->component); + unsigned int val = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + val = DAC_VDL_16BITS; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val = DAC_VDL_20BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = DAC_VDL_24BITS; + break; + case SNDRV_PCM_FORMAT_S32_LE: + val = DAC_VDL_32BITS; + break; + default: + return -EINVAL; + } + regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL2, DAC_VDL_MASK, val); + + val = DAC_WL_32BITS | DAC_RST_DIS; + regmap_update_bits(rk3328->regmap, DAC_INIT_CTRL3, + DAC_WL_MASK | DAC_RST_MASK, val); + + return 0; +} + +static int rk3328_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(dai->component); + + return rk3328_codec_open_playback(rk3328); +} + +static void rk3328_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(dai->component); + + rk3328_codec_close_playback(rk3328); +} + +static const struct snd_soc_dai_ops rk3328_dai_ops = { + .hw_params = rk3328_hw_params, + .set_fmt = rk3328_set_dai_fmt, + .mute_stream = rk3328_mute_stream, + .startup = rk3328_pcm_startup, + .shutdown = rk3328_pcm_shutdown, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver rk3328_dai[] = { + { + .name = "rk3328-hifi", + .id = RK3328_HIFI, + .playback = { + .stream_name = "HIFI Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + }, + .capture = { + .stream_name = "HIFI Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + }, + .ops = &rk3328_dai_ops, + }, +}; + +static int rk3328_codec_probe(struct snd_soc_component *component) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(component); + + rk3328_codec_reset(rk3328); + rk3328_codec_power_on(rk3328, 0); + + return 0; +} + +static void rk3328_codec_remove(struct snd_soc_component *component) +{ + struct rk3328_codec_priv *rk3328 = + snd_soc_component_get_drvdata(component); + + rk3328_codec_close_playback(rk3328); + rk3328_codec_power_off(rk3328, 0); +} + +static const struct snd_soc_component_driver soc_codec_rk3328 = { + .probe = rk3328_codec_probe, + .remove = rk3328_codec_remove, +}; + +static bool rk3328_codec_write_read_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CODEC_RESET: + case DAC_INIT_CTRL1: + case DAC_INIT_CTRL2: + case DAC_INIT_CTRL3: + case DAC_PRECHARGE_CTRL: + case DAC_PWR_CTRL: + case DAC_CLK_CTRL: + case HPMIX_CTRL: + case DAC_SELECT: + case HPOUT_CTRL: + case HPOUTL_GAIN_CTRL: + case HPOUTR_GAIN_CTRL: + case HPOUT_POP_CTRL: + return true; + default: + return false; + } +} + +static bool rk3328_codec_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CODEC_RESET: + return true; + default: + return false; + } +} + +static const struct regmap_config rk3328_codec_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = HPOUT_POP_CTRL, + .writeable_reg = rk3328_codec_write_read_reg, + .readable_reg = rk3328_codec_write_read_reg, + .volatile_reg = rk3328_codec_volatile_reg, + .reg_defaults = rk3328_codec_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rk3328_codec_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static int rk3328_platform_probe(struct platform_device *pdev) +{ + struct device_node *rk3328_np = pdev->dev.of_node; + struct rk3328_codec_priv *rk3328; + struct regmap *grf; + void __iomem *base; + int ret = 0; + + rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL); + if (!rk3328) + return -ENOMEM; + + grf = syscon_regmap_lookup_by_phandle(rk3328_np, + "rockchip,grf"); + if (IS_ERR(grf)) { + dev_err(&pdev->dev, "missing 'rockchip,grf'\n"); + return PTR_ERR(grf); + } + /* enable i2s_acodec_en */ + regmap_write(grf, RK3328_GRF_SOC_CON2, + (BIT(14) << 16 | BIT(14))); + + ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms", + &rk3328->spk_depop_time); + if (ret < 0) { + dev_info(&pdev->dev, "spk_depop_time use default value.\n"); + rk3328->spk_depop_time = 200; + } + + rk3328->mute = gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_HIGH); + if (IS_ERR(rk3328->mute)) + return PTR_ERR(rk3328->mute); + /* + * Rock64 is the only supported platform to have widely relied on + * this; if we do happen to come across an old DTB, just leave the + * external mute forced off. + */ + if (!rk3328->mute && of_machine_is_compatible("pine64,rock64")) { + dev_warn(&pdev->dev, "assuming implicit control of GPIO_MUTE; update devicetree if possible\n"); + regmap_write(grf, RK3328_GRF_SOC_CON10, BIT(17) | BIT(1)); + } + + rk3328->mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(rk3328->mclk)) + return PTR_ERR(rk3328->mclk); + + ret = clk_prepare_enable(rk3328->mclk); + if (ret) + return ret; + clk_set_rate(rk3328->mclk, INITIAL_FREQ); + + rk3328->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(rk3328->pclk)) { + dev_err(&pdev->dev, "can't get acodec pclk\n"); + ret = PTR_ERR(rk3328->pclk); + goto err_unprepare_mclk; + } + + ret = clk_prepare_enable(rk3328->pclk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable acodec pclk\n"); + goto err_unprepare_mclk; + } + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto err_unprepare_pclk; + } + + rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &rk3328_codec_regmap_config); + if (IS_ERR(rk3328->regmap)) { + ret = PTR_ERR(rk3328->regmap); + goto err_unprepare_pclk; + } + + platform_set_drvdata(pdev, rk3328); + + ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328, + rk3328_dai, + ARRAY_SIZE(rk3328_dai)); + if (ret) + goto err_unprepare_pclk; + + return 0; + +err_unprepare_pclk: + clk_disable_unprepare(rk3328->pclk); + +err_unprepare_mclk: + clk_disable_unprepare(rk3328->mclk); + return ret; +} + +static const struct of_device_id rk3328_codec_of_match[] __maybe_unused = { + { .compatible = "rockchip,rk3328-codec", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk3328_codec_of_match); + +static struct platform_driver rk3328_codec_driver = { + .driver = { + .name = "rk3328-codec", + .of_match_table = of_match_ptr(rk3328_codec_of_match), + }, + .probe = rk3328_platform_probe, +}; +module_platform_driver(rk3328_codec_driver); + +MODULE_AUTHOR("Sugar Zhang "); +MODULE_DESCRIPTION("ASoC rk3328 codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rk3328_codec.h b/sound/soc/codecs/rk3328_codec.h new file mode 100644 index 0000000000..6551035862 --- /dev/null +++ b/sound/soc/codecs/rk3328_codec.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rk3328 ALSA SoC Audio driver + * + * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. + */ + +#ifndef _RK3328_CODEC_H +#define _RK3328_CODEC_H + +#include + +/* codec register */ +#define CODEC_RESET (0x00 << 2) +#define DAC_INIT_CTRL1 (0x03 << 2) +#define DAC_INIT_CTRL2 (0x04 << 2) +#define DAC_INIT_CTRL3 (0x05 << 2) +#define DAC_PRECHARGE_CTRL (0x22 << 2) +#define DAC_PWR_CTRL (0x23 << 2) +#define DAC_CLK_CTRL (0x24 << 2) +#define HPMIX_CTRL (0x25 << 2) +#define DAC_SELECT (0x26 << 2) +#define HPOUT_CTRL (0x27 << 2) +#define HPOUTL_GAIN_CTRL (0x28 << 2) +#define HPOUTR_GAIN_CTRL (0x29 << 2) +#define HPOUT_POP_CTRL (0x2a << 2) + +/* REG00: CODEC_RESET */ +#define PWR_RST_BYPASS_DIS (0x0 << 6) +#define PWR_RST_BYPASS_EN (0x1 << 6) +#define DIG_CORE_RST (0x0 << 1) +#define DIG_CORE_WORK (0x1 << 1) +#define SYS_RST (0x0 << 0) +#define SYS_WORK (0x1 << 0) + +/* REG03: DAC_INIT_CTRL1 */ +#define PIN_DIRECTION_MASK BIT(5) +#define PIN_DIRECTION_IN (0x0 << 5) +#define PIN_DIRECTION_OUT (0x1 << 5) +#define DAC_I2S_MODE_MASK BIT(4) +#define DAC_I2S_MODE_SLAVE (0x0 << 4) +#define DAC_I2S_MODE_MASTER (0x1 << 4) + +/* REG04: DAC_INIT_CTRL2 */ +#define DAC_I2S_LRP_MASK BIT(7) +#define DAC_I2S_LRP_NORMAL (0x0 << 7) +#define DAC_I2S_LRP_REVERSAL (0x1 << 7) +#define DAC_VDL_MASK GENMASK(6, 5) +#define DAC_VDL_16BITS (0x0 << 5) +#define DAC_VDL_20BITS (0x1 << 5) +#define DAC_VDL_24BITS (0x2 << 5) +#define DAC_VDL_32BITS (0x3 << 5) +#define DAC_MODE_MASK GENMASK(4, 3) +#define DAC_MODE_RJM (0x0 << 3) +#define DAC_MODE_LJM (0x1 << 3) +#define DAC_MODE_I2S (0x2 << 3) +#define DAC_MODE_PCM (0x3 << 3) +#define DAC_LR_SWAP_MASK BIT(2) +#define DAC_LR_SWAP_DIS (0x0 << 2) +#define DAC_LR_SWAP_EN (0x1 << 2) + +/* REG05: DAC_INIT_CTRL3 */ +#define DAC_WL_MASK GENMASK(3, 2) +#define DAC_WL_16BITS (0x0 << 2) +#define DAC_WL_20BITS (0x1 << 2) +#define DAC_WL_24BITS (0x2 << 2) +#define DAC_WL_32BITS (0x3 << 2) +#define DAC_RST_MASK BIT(1) +#define DAC_RST_EN (0x0 << 1) +#define DAC_RST_DIS (0x1 << 1) +#define DAC_BCP_MASK BIT(0) +#define DAC_BCP_NORMAL (0x0 << 0) +#define DAC_BCP_REVERSAL (0x1 << 0) + +/* REG22: DAC_PRECHARGE_CTRL */ +#define DAC_CHARGE_XCHARGE_MASK BIT(7) +#define DAC_CHARGE_DISCHARGE (0x0 << 7) +#define DAC_CHARGE_PRECHARGE (0x1 << 7) +#define DAC_CHARGE_CURRENT_64I_MASK BIT(6) +#define DAC_CHARGE_CURRENT_64I (0x1 << 6) +#define DAC_CHARGE_CURRENT_32I_MASK BIT(5) +#define DAC_CHARGE_CURRENT_32I (0x1 << 5) +#define DAC_CHARGE_CURRENT_16I_MASK BIT(4) +#define DAC_CHARGE_CURRENT_16I (0x1 << 4) +#define DAC_CHARGE_CURRENT_08I_MASK BIT(3) +#define DAC_CHARGE_CURRENT_08I (0x1 << 3) +#define DAC_CHARGE_CURRENT_04I_MASK BIT(2) +#define DAC_CHARGE_CURRENT_04I (0x1 << 2) +#define DAC_CHARGE_CURRENT_02I_MASK BIT(1) +#define DAC_CHARGE_CURRENT_02I (0x1 << 1) +#define DAC_CHARGE_CURRENT_I_MASK BIT(0) +#define DAC_CHARGE_CURRENT_I (0x1 << 0) +#define DAC_CHARGE_CURRENT_ALL_MASK GENMASK(6, 0) +#define DAC_CHARGE_CURRENT_ALL_OFF 0x00 +#define DAC_CHARGE_CURRENT_ALL_ON 0x7f + +/* REG23: DAC_PWR_CTRL */ +#define DAC_PWR_MASK BIT(6) +#define DAC_PWR_OFF (0x0 << 6) +#define DAC_PWR_ON (0x1 << 6) +#define DACL_PATH_REFV_MASK BIT(5) +#define DACL_PATH_REFV_OFF (0x0 << 5) +#define DACL_PATH_REFV_ON (0x1 << 5) +#define HPOUTL_ZERO_CROSSING_MASK BIT(4) +#define HPOUTL_ZERO_CROSSING_OFF (0x0 << 4) +#define HPOUTL_ZERO_CROSSING_ON (0x1 << 4) +#define DACR_PATH_REFV_MASK BIT(1) +#define DACR_PATH_REFV_OFF (0x0 << 1) +#define DACR_PATH_REFV_ON (0x1 << 1) +#define HPOUTR_ZERO_CROSSING_MASK BIT(0) +#define HPOUTR_ZERO_CROSSING_OFF (0x0 << 0) +#define HPOUTR_ZERO_CROSSING_ON (0x1 << 0) + +/* REG24: DAC_CLK_CTRL */ +#define DACL_REFV_MASK BIT(7) +#define DACL_REFV_OFF (0x0 << 7) +#define DACL_REFV_ON (0x1 << 7) +#define DACL_CLK_MASK BIT(6) +#define DACL_CLK_OFF (0x0 << 6) +#define DACL_CLK_ON (0x1 << 6) +#define DACL_MASK BIT(5) +#define DACL_OFF (0x0 << 5) +#define DACL_ON (0x1 << 5) +#define DACL_INIT_MASK BIT(4) +#define DACL_INIT_OFF (0x0 << 4) +#define DACL_INIT_ON (0x1 << 4) +#define DACR_REFV_MASK BIT(3) +#define DACR_REFV_OFF (0x0 << 3) +#define DACR_REFV_ON (0x1 << 3) +#define DACR_CLK_MASK BIT(2) +#define DACR_CLK_OFF (0x0 << 2) +#define DACR_CLK_ON (0x1 << 2) +#define DACR_MASK BIT(1) +#define DACR_OFF (0x0 << 1) +#define DACR_ON (0x1 << 1) +#define DACR_INIT_MASK BIT(0) +#define DACR_INIT_OFF (0x0 << 0) +#define DACR_INIT_ON (0x1 << 0) + +/* REG25: HPMIX_CTRL*/ +#define HPMIXL_MASK BIT(6) +#define HPMIXL_DIS (0x0 << 6) +#define HPMIXL_EN (0x1 << 6) +#define HPMIXL_INIT_MASK BIT(5) +#define HPMIXL_INIT_DIS (0x0 << 5) +#define HPMIXL_INIT_EN (0x1 << 5) +#define HPMIXL_INIT2_MASK BIT(4) +#define HPMIXL_INIT2_DIS (0x0 << 4) +#define HPMIXL_INIT2_EN (0x1 << 4) +#define HPMIXR_MASK BIT(2) +#define HPMIXR_DIS (0x0 << 2) +#define HPMIXR_EN (0x1 << 2) +#define HPMIXR_INIT_MASK BIT(1) +#define HPMIXR_INIT_DIS (0x0 << 1) +#define HPMIXR_INIT_EN (0x1 << 1) +#define HPMIXR_INIT2_MASK BIT(0) +#define HPMIXR_INIT2_DIS (0x0 << 0) +#define HPMIXR_INIT2_EN (0x1 << 0) + +/* REG26: DAC_SELECT */ +#define DACL_SELECT_MASK BIT(4) +#define DACL_UNSELECT (0x0 << 4) +#define DACL_SELECT (0x1 << 4) +#define DACR_SELECT_MASK BIT(0) +#define DACR_UNSELECT (0x0 << 0) +#define DACR_SELECT (0x1 << 0) + +/* REG27: HPOUT_CTRL */ +#define HPOUTL_MASK BIT(7) +#define HPOUTL_DIS (0x0 << 7) +#define HPOUTL_EN (0x1 << 7) +#define HPOUTL_INIT_MASK BIT(6) +#define HPOUTL_INIT_DIS (0x0 << 6) +#define HPOUTL_INIT_EN (0x1 << 6) +#define HPOUTL_MUTE_MASK BIT(5) +#define HPOUTL_MUTE (0x0 << 5) +#define HPOUTL_UNMUTE (0x1 << 5) +#define HPOUTR_MASK BIT(4) +#define HPOUTR_DIS (0x0 << 4) +#define HPOUTR_EN (0x1 << 4) +#define HPOUTR_INIT_MASK BIT(3) +#define HPOUTR_INIT_DIS (0x0 << 3) +#define HPOUTR_INIT_EN (0x1 << 3) +#define HPOUTR_MUTE_MASK BIT(2) +#define HPOUTR_MUTE (0x0 << 2) +#define HPOUTR_UNMUTE (0x1 << 2) + +/* REG28: HPOUTL_GAIN_CTRL */ +#define HPOUTL_GAIN_MASK GENMASK(4, 0) + +/* REG29: HPOUTR_GAIN_CTRL */ +#define HPOUTR_GAIN_MASK GENMASK(4, 0) + +/* REG2a: HPOUT_POP_CTRL */ +#define HPOUTR_POP_MASK GENMASK(5, 4) +#define HPOUTR_POP_XCHARGE (0x1 << 4) +#define HPOUTR_POP_WORK (0x2 << 4) +#define HPOUTL_POP_MASK GENMASK(1, 0) +#define HPOUTL_POP_XCHARGE (0x1 << 0) +#define HPOUTL_POP_WORK (0x2 << 0) + +#define RK3328_HIFI 0 + +struct rk3328_reg_msk_val { + unsigned int reg; + unsigned int msk; + unsigned int val; +}; + +#endif diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c new file mode 100644 index 0000000000..d4da98469f --- /dev/null +++ b/sound/soc/codecs/rk817_codec.c @@ -0,0 +1,541 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rk817 ALSA SoC Audio driver +// +// Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rk817_codec_priv { + struct snd_soc_component *component; + struct rk808 *rk808; + struct clk *mclk; + unsigned int stereo_sysclk; + bool mic_in_differential; +}; + +/* + * This sets the codec up with the values defined in the default implementation including the APLL + * from the Rockchip vendor kernel. I do not know if these values are universal despite differing + * from the default values defined above and taken from the datasheet, or implementation specific. + * I don't have another implementation to compare from the Rockchip sources. Hard-coding for now. + * Additionally, I do not know according to the documentation the units accepted for the clock + * values, so for the moment those are left unvalidated. + */ + +static int rk817_init(struct snd_soc_component *component) +{ + struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); + + snd_soc_component_write(component, RK817_CODEC_DDAC_POPD_DACST, 0x02); + snd_soc_component_write(component, RK817_CODEC_DDAC_SR_LMT0, 0x02); + snd_soc_component_write(component, RK817_CODEC_DADC_SR_ACL0, 0x02); + snd_soc_component_write(component, RK817_CODEC_DTOP_VUCTIME, 0xf4); + if (rk817->mic_in_differential) { + snd_soc_component_update_bits(component, RK817_CODEC_AMIC_CFG0, MIC_DIFF_MASK, + MIC_DIFF_EN); + } + + return 0; +} + +static int rk817_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + /* Set resistor value and charge pump current for PLL. */ + snd_soc_component_write(component, RK817_CODEC_APLL_CFG1, 0x58); + /* Set the PLL feedback clock divide value (values not documented). */ + snd_soc_component_write(component, RK817_CODEC_APLL_CFG2, 0x2d); + /* Set the PLL pre-divide value (values not documented). */ + snd_soc_component_write(component, RK817_CODEC_APLL_CFG3, 0x0c); + /* Set the PLL VCO output clock divide and PLL divided ratio of PLL High Clk (values not + * documented). + */ + snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0xa5); + + return 0; +} + +/* + * DDAC/DADC L/R volume setting + * 0db~-95db, 0.375db/step, for example: + * 0x00: 0dB + * 0xff: -95dB + */ + +static const DECLARE_TLV_DB_MINMAX(rk817_vol_tlv, -9500, 0); + +/* + * PGA GAIN L/R volume setting + * 27db~-18db, 3db/step, for example: + * 0x0: -18dB + * 0xf: 27dB + */ + +static const DECLARE_TLV_DB_MINMAX(rk817_gain_tlv, -1800, 2700); + +static const struct snd_kcontrol_new rk817_volume_controls[] = { + SOC_DOUBLE_R_RANGE_TLV("Master Playback Volume", RK817_CODEC_DDAC_VOLL, + RK817_CODEC_DDAC_VOLR, 0, 0x00, 0xff, 1, rk817_vol_tlv), + SOC_DOUBLE_R_RANGE_TLV("Master Capture Volume", RK817_CODEC_DADC_VOLL, + RK817_CODEC_DADC_VOLR, 0, 0x00, 0xff, 1, rk817_vol_tlv), + SOC_DOUBLE_TLV("Mic Capture Gain", RK817_CODEC_DMIC_PGA_GAIN, 4, 0, 0xf, 0, + rk817_gain_tlv), +}; + +/* Since the speaker output and L headphone pin are internally the same, make audio path mutually + * exclusive with a mux. + */ + +static const char *dac_mux_text[] = { + "HP", + "SPK", +}; + +static SOC_ENUM_SINGLE_VIRT_DECL(dac_enum, dac_mux_text); + +static const struct snd_kcontrol_new dac_mux = + SOC_DAPM_ENUM("Playback Mux", dac_enum); + +static const struct snd_soc_dapm_widget rk817_dapm_widgets[] = { + + /* capture/playback common */ + SND_SOC_DAPM_SUPPLY("LDO Regulator", RK817_CODEC_AREF_RTCFG1, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("IBIAS Block", RK817_CODEC_AREF_RTCFG1, 2, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("VAvg Buffer", RK817_CODEC_AREF_RTCFG1, 1, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL Power", RK817_CODEC_APLL_CFG5, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S TX1 Transfer Start", RK817_CODEC_DI2S_RXCMD_TSD, 5, 0, NULL, 0), + + /* capture path common */ + SND_SOC_DAPM_SUPPLY("ADC Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S TX Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S TX Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC Power On", RK817_CODEC_AMIC_CFG0, 6, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S TX3 Transfer Start", RK817_CODEC_DI2S_TXCR3_TXCMD, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S TX3 Right Justified", RK817_CODEC_DI2S_TXCR3_TXCMD, 3, 0, NULL, 0), + + /* capture path L */ + SND_SOC_DAPM_ADC("ADC L", "Capture", RK817_CODEC_AADC_CFG0, 7, 1), + SND_SOC_DAPM_SUPPLY("PGA L Power On", RK817_CODEC_AMIC_CFG0, 5, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Boost L1", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Boost L2", RK817_CODEC_AMIC_CFG0, 2, 0, NULL, 0), + + /* capture path R */ + SND_SOC_DAPM_ADC("ADC R", "Capture", RK817_CODEC_AADC_CFG0, 6, 1), + SND_SOC_DAPM_SUPPLY("PGA R Power On", RK817_CODEC_AMIC_CFG0, 4, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Boost R1", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Boost R2", RK817_CODEC_AMIC_CFG0, 3, 0, NULL, 0), + + /* playback path common */ + SND_SOC_DAPM_SUPPLY("DAC Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S RX Clock", RK817_CODEC_DTOP_DIGEN_CLKE, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S RX Channel Enable", RK817_CODEC_DTOP_DIGEN_CLKE, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Bias", RK817_CODEC_ADAC_CFG1, 3, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mute Off", RK817_CODEC_DDAC_MUTE_MIXCTL, 0, 1, NULL, 0), + + /* playback path speaker */ + SND_SOC_DAPM_SUPPLY("Class D Mode", RK817_CODEC_DDAC_MUTE_MIXCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("High Pass Filter", RK817_CODEC_DDAC_MUTE_MIXCTL, 7, 0, NULL, 0), + SND_SOC_DAPM_DAC("SPK DAC", "Playback", RK817_CODEC_ADAC_CFG1, 2, 1), + SND_SOC_DAPM_SUPPLY("Enable Class D", RK817_CODEC_ACLASSD_CFG1, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Disable Class D Mute Ramp", RK817_CODEC_ACLASSD_CFG1, 6, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Class D Mute Rate 1", RK817_CODEC_ACLASSD_CFG1, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Class D Mute Rate 2", RK817_CODEC_ACLASSD_CFG1, 2, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Class D OCPP 2", RK817_CODEC_ACLASSD_CFG2, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Class D OCPP 3", RK817_CODEC_ACLASSD_CFG2, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Class D OCPN 2", RK817_CODEC_ACLASSD_CFG2, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Class D OCPN 3", RK817_CODEC_ACLASSD_CFG2, 0, 0, NULL, 0), + + /* playback path headphones */ + SND_SOC_DAPM_SUPPLY("Headphone Charge Pump", RK817_CODEC_AHP_CP, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone CP Discharge LDO", RK817_CODEC_AHP_CP, 3, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone OStage", RK817_CODEC_AHP_CFG0, 6, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Pre Amp", RK817_CODEC_AHP_CFG0, 5, 1, NULL, 0), + SND_SOC_DAPM_DAC("DAC L", "Playback", RK817_CODEC_ADAC_CFG1, 1, 1), + SND_SOC_DAPM_DAC("DAC R", "Playback", RK817_CODEC_ADAC_CFG1, 0, 1), + + /* Mux for input/output path selection */ + SND_SOC_DAPM_MUX("Playback Mux", SND_SOC_NOPM, 1, 0, &dac_mux), + + /* Pins for Simple Card Bindings */ + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("MICR"), + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("SPKO"), +}; + +static const struct snd_soc_dapm_route rk817_dapm_routes[] = { + + /* capture path */ + /* left mic */ + {"ADC L", NULL, "LDO Regulator"}, + {"ADC L", NULL, "IBIAS Block"}, + {"ADC L", NULL, "VAvg Buffer"}, + {"ADC L", NULL, "PLL Power"}, + {"ADC L", NULL, "ADC Clock"}, + {"ADC L", NULL, "I2S TX Clock"}, + {"ADC L", NULL, "ADC Channel Enable"}, + {"ADC L", NULL, "I2S TX Channel Enable"}, + {"ADC L", NULL, "I2S TX1 Transfer Start"}, + {"MICL", NULL, "MIC Power On"}, + {"MICL", NULL, "PGA L Power On"}, + {"MICL", NULL, "Mic Boost L1"}, + {"MICL", NULL, "Mic Boost L2"}, + {"MICL", NULL, "I2S TX3 Transfer Start"}, + {"MICL", NULL, "I2S TX3 Right Justified"}, + {"ADC L", NULL, "MICL"}, + + /* right mic */ + {"ADC R", NULL, "LDO Regulator"}, + {"ADC R", NULL, "IBIAS Block"}, + {"ADC R", NULL, "VAvg Buffer"}, + {"ADC R", NULL, "PLL Power"}, + {"ADC R", NULL, "ADC Clock"}, + {"ADC R", NULL, "I2S TX Clock"}, + {"ADC R", NULL, "ADC Channel Enable"}, + {"ADC R", NULL, "I2S TX Channel Enable"}, + {"ADC R", NULL, "I2S TX1 Transfer Start"}, + {"MICR", NULL, "MIC Power On"}, + {"MICR", NULL, "PGA R Power On"}, + {"MICR", NULL, "Mic Boost R1"}, + {"MICR", NULL, "Mic Boost R2"}, + {"MICR", NULL, "I2S TX3 Transfer Start"}, + {"MICR", NULL, "I2S TX3 Right Justified"}, + {"ADC R", NULL, "MICR"}, + + /* playback path */ + /* speaker path */ + {"SPK DAC", NULL, "LDO Regulator"}, + {"SPK DAC", NULL, "IBIAS Block"}, + {"SPK DAC", NULL, "VAvg Buffer"}, + {"SPK DAC", NULL, "PLL Power"}, + {"SPK DAC", NULL, "I2S TX1 Transfer Start"}, + {"SPK DAC", NULL, "DAC Clock"}, + {"SPK DAC", NULL, "I2S RX Clock"}, + {"SPK DAC", NULL, "DAC Channel Enable"}, + {"SPK DAC", NULL, "I2S RX Channel Enable"}, + {"SPK DAC", NULL, "Class D Mode"}, + {"SPK DAC", NULL, "DAC Bias"}, + {"SPK DAC", NULL, "DAC Mute Off"}, + {"SPK DAC", NULL, "Enable Class D"}, + {"SPK DAC", NULL, "Disable Class D Mute Ramp"}, + {"SPK DAC", NULL, "Class D Mute Rate 1"}, + {"SPK DAC", NULL, "Class D Mute Rate 2"}, + {"SPK DAC", NULL, "Class D OCPP 2"}, + {"SPK DAC", NULL, "Class D OCPP 3"}, + {"SPK DAC", NULL, "Class D OCPN 2"}, + {"SPK DAC", NULL, "Class D OCPN 3"}, + {"SPK DAC", NULL, "High Pass Filter"}, + + /* headphone path L */ + {"DAC L", NULL, "LDO Regulator"}, + {"DAC L", NULL, "IBIAS Block"}, + {"DAC L", NULL, "VAvg Buffer"}, + {"DAC L", NULL, "PLL Power"}, + {"DAC L", NULL, "I2S TX1 Transfer Start"}, + {"DAC L", NULL, "DAC Clock"}, + {"DAC L", NULL, "I2S RX Clock"}, + {"DAC L", NULL, "DAC Channel Enable"}, + {"DAC L", NULL, "I2S RX Channel Enable"}, + {"DAC L", NULL, "DAC Bias"}, + {"DAC L", NULL, "DAC Mute Off"}, + {"DAC L", NULL, "Headphone Charge Pump"}, + {"DAC L", NULL, "Headphone CP Discharge LDO"}, + {"DAC L", NULL, "Headphone OStage"}, + {"DAC L", NULL, "Headphone Pre Amp"}, + + /* headphone path R */ + {"DAC R", NULL, "LDO Regulator"}, + {"DAC R", NULL, "IBIAS Block"}, + {"DAC R", NULL, "VAvg Buffer"}, + {"DAC R", NULL, "PLL Power"}, + {"DAC R", NULL, "I2S TX1 Transfer Start"}, + {"DAC R", NULL, "DAC Clock"}, + {"DAC R", NULL, "I2S RX Clock"}, + {"DAC R", NULL, "DAC Channel Enable"}, + {"DAC R", NULL, "I2S RX Channel Enable"}, + {"DAC R", NULL, "DAC Bias"}, + {"DAC R", NULL, "DAC Mute Off"}, + {"DAC R", NULL, "Headphone Charge Pump"}, + {"DAC R", NULL, "Headphone CP Discharge LDO"}, + {"DAC R", NULL, "Headphone OStage"}, + {"DAC R", NULL, "Headphone Pre Amp"}, + + /* mux path for output selection */ + {"Playback Mux", "HP", "DAC L"}, + {"Playback Mux", "HP", "DAC R"}, + {"Playback Mux", "SPK", "SPK DAC"}, + {"SPKO", NULL, "Playback Mux"}, + {"HPOL", NULL, "Playback Mux"}, + {"HPOR", NULL, "Playback Mux"}, +}; + +static int rk817_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); + + rk817->stereo_sysclk = freq; + + return 0; +} + +static int rk817_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + unsigned int i2s_mst = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + i2s_mst |= RK817_I2S_MODE_SLV; + break; + case SND_SOC_DAIFMT_CBM_CFM: + i2s_mst |= RK817_I2S_MODE_MST; + break; + default: + dev_err(component->dev, "%s : set master mask failed!\n", __func__); + return -EINVAL; + } + + snd_soc_component_update_bits(component, RK817_CODEC_DI2S_CKM, + RK817_I2S_MODE_MASK, i2s_mst); + + return 0; +} + +static int rk817_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_component_write(component, RK817_CODEC_DI2S_RXCR2, + VDW_RX_16BITS); + snd_soc_component_write(component, RK817_CODEC_DI2S_TXCR2, + VDW_TX_16BITS); + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + snd_soc_component_write(component, RK817_CODEC_DI2S_RXCR2, + VDW_RX_24BITS); + snd_soc_component_write(component, RK817_CODEC_DI2S_TXCR2, + VDW_TX_24BITS); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rk817_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + + if (mute) + snd_soc_component_update_bits(component, + RK817_CODEC_DDAC_MUTE_MIXCTL, + DACMT_MASK, DACMT_ENABLE); + else + snd_soc_component_update_bits(component, + RK817_CODEC_DDAC_MUTE_MIXCTL, + DACMT_MASK, DACMT_DISABLE); + + return 0; +} + +#define RK817_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000) + +#define RK817_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000) + +#define RK817_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops rk817_dai_ops = { + .hw_params = rk817_hw_params, + .set_fmt = rk817_set_dai_fmt, + .set_sysclk = rk817_set_dai_sysclk, + .mute_stream = rk817_digital_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver rk817_dai[] = { + { + .name = "rk817-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = RK817_PLAYBACK_RATES, + .formats = RK817_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RK817_CAPTURE_RATES, + .formats = RK817_FORMATS, + }, + .ops = &rk817_dai_ops, + }, +}; + +static int rk817_probe(struct snd_soc_component *component) +{ + struct rk817_codec_priv *rk817 = snd_soc_component_get_drvdata(component); + struct rk808 *rk808 = dev_get_drvdata(component->dev->parent); + + snd_soc_component_init_regmap(component, rk808->regmap); + rk817->component = component; + + snd_soc_component_write(component, RK817_CODEC_DTOP_LPT_SRST, 0x40); + + rk817_init(component); + + /* setting initial pll values so that we can continue to leverage simple-audio-card. + * The values aren't important since no parameters are used. + */ + + snd_soc_component_set_pll(component, 0, 0, 0, 0); + + return 0; +} + +static void rk817_remove(struct snd_soc_component *component) +{ + snd_soc_component_exit_regmap(component); +} + +static const struct snd_soc_component_driver soc_codec_dev_rk817 = { + .probe = rk817_probe, + .remove = rk817_remove, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .controls = rk817_volume_controls, + .num_controls = ARRAY_SIZE(rk817_volume_controls), + .dapm_routes = rk817_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rk817_dapm_routes), + .dapm_widgets = rk817_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk817_dapm_widgets), + .set_pll = rk817_set_component_pll, +}; + +static void rk817_codec_parse_dt_property(struct device *dev, + struct rk817_codec_priv *rk817) +{ + struct device_node *node; + + node = of_get_child_by_name(dev->parent->of_node, "codec"); + if (!node) { + dev_dbg(dev, "%s() Can not get child: codec\n", + __func__); + } + + rk817->mic_in_differential = + of_property_read_bool(node, "rockchip,mic-in-differential"); + + of_node_put(node); +} + +static int rk817_platform_probe(struct platform_device *pdev) +{ + struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); + struct rk817_codec_priv *rk817_codec_data; + int ret; + + rk817_codec_data = devm_kzalloc(&pdev->dev, + sizeof(struct rk817_codec_priv), + GFP_KERNEL); + if (!rk817_codec_data) + return -ENOMEM; + + platform_set_drvdata(pdev, rk817_codec_data); + + rk817_codec_data->rk808 = rk808; + + rk817_codec_parse_dt_property(&pdev->dev, rk817_codec_data); + + rk817_codec_data->mclk = devm_clk_get(pdev->dev.parent, "mclk"); + if (IS_ERR(rk817_codec_data->mclk)) { + dev_dbg(&pdev->dev, "Unable to get mclk\n"); + ret = -ENXIO; + goto err_; + } + + ret = clk_prepare_enable(rk817_codec_data->mclk); + if (ret < 0) { + dev_err(&pdev->dev, "%s() clock prepare error %d\n", + __func__, ret); + goto err_; + } + + ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_rk817, + rk817_dai, ARRAY_SIZE(rk817_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "%s() register codec error %d\n", + __func__, ret); + goto err_clk; + } + + return 0; + +err_clk: + clk_disable_unprepare(rk817_codec_data->mclk); +err_: + return ret; +} + +static void rk817_platform_remove(struct platform_device *pdev) +{ + struct rk817_codec_priv *rk817 = platform_get_drvdata(pdev); + + clk_disable_unprepare(rk817->mclk); +} + +static struct platform_driver rk817_codec_driver = { + .driver = { + .name = "rk817-codec", + }, + .probe = rk817_platform_probe, + .remove_new = rk817_platform_remove, +}; + +module_platform_driver(rk817_codec_driver); + +MODULE_DESCRIPTION("ASoC RK817 codec driver"); +MODULE_AUTHOR("binyuan "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:rk817-codec"); diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c new file mode 100644 index 0000000000..d1fc170642 --- /dev/null +++ b/sound/soc/codecs/rl6231.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rl6231.c - RL6231 class device shared support + * + * Copyright 2014 Realtek Semiconductor Corp. + * + * Author: Oder Chiou + */ + +#include +#include + +#include +#include "rl6231.h" + +/** + * rl6231_get_pre_div - Return the value of pre divider. + * + * @map: map for setting. + * @reg: register. + * @sft: shift. + * + * Return the value of pre divider from given register value. + * Return negative error code for unexpected register value. + */ +int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft) +{ + int pd, val; + + regmap_read(map, reg, &val); + + val = (val >> sft) & 0x7; + + switch (val) { + case 0: + case 1: + case 2: + case 3: + pd = val + 1; + break; + case 4: + pd = 6; + break; + case 5: + pd = 8; + break; + case 6: + pd = 12; + break; + case 7: + pd = 16; + break; + default: + pd = -EINVAL; + break; + } + + return pd; +} +EXPORT_SYMBOL_GPL(rl6231_get_pre_div); + +/** + * rl6231_calc_dmic_clk - Calculate the frequency divider parameter of dmic. + * + * @rate: base clock rate. + * + * Choose divider parameter that gives the highest possible DMIC frequency in + * 1MHz - 3MHz range. + */ +int rl6231_calc_dmic_clk(int rate) +{ + static const int div[] = {2, 3, 4, 6, 8, 12}; + int i; + + if (rate < 1000000 * div[0]) { + pr_warn("Base clock rate %d is too low\n", rate); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(div); i++) { + if ((div[i] % 3) == 0) + continue; + /* find divider that gives DMIC frequency below 1.536MHz */ + if (1536000 * div[i] >= rate) + return i; + } + + pr_warn("Base clock rate %d is too high\n", rate); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk); + +struct pll_calc_map { + unsigned int pll_in; + unsigned int pll_out; + int k; + int n; + int m; + bool m_bp; + bool k_bp; +}; + +static const struct pll_calc_map pll_preset_table[] = { + {19200000, 4096000, 23, 14, 1, false, false}, + {19200000, 24576000, 3, 30, 3, false, false}, + {48000000, 3840000, 23, 2, 0, false, false}, + {3840000, 24576000, 3, 30, 0, true, false}, + {3840000, 22579200, 3, 5, 0, true, false}, +}; + +static unsigned int find_best_div(unsigned int in, + unsigned int max, unsigned int div) +{ + unsigned int d; + + if (in <= max) + return 1; + + d = in / max; + if (in % max) + d++; + + while (div % d != 0) + d++; + + + return d; +} + +/** + * rl6231_pll_calc - Calcualte PLL M/N/K code. + * @freq_in: external clock provided to codec. + * @freq_out: target clock which codec works on. + * @pll_code: Pointer to structure with M, N, K, m_bypass and k_bypass flag. + * + * Calcualte M/N/K code to configure PLL for codec. + * + * Returns 0 for success or negative error code. + */ +int rl6231_pll_calc(const unsigned int freq_in, + const unsigned int freq_out, struct rl6231_pll_code *pll_code) +{ + int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX; + int i, k, n_t; + int k_t, min_k, max_k, n = 0, m = 0, m_t = 0; + unsigned int red, pll_out, in_t, out_t, div, div_t; + unsigned int red_t = abs(freq_out - freq_in); + unsigned int f_in, f_out, f_max; + bool m_bypass = false, k_bypass = false; + + if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) { + if (freq_in == pll_preset_table[i].pll_in && + freq_out == pll_preset_table[i].pll_out) { + k = pll_preset_table[i].k; + m = pll_preset_table[i].m; + n = pll_preset_table[i].n; + m_bypass = pll_preset_table[i].m_bp; + k_bypass = pll_preset_table[i].k_bp; + pr_debug("Use preset PLL parameter table\n"); + goto code_find; + } + } + + min_k = 80000000 / freq_out - 2; + max_k = 150000000 / freq_out - 2; + if (max_k > RL6231_PLL_K_MAX) + max_k = RL6231_PLL_K_MAX; + if (min_k > RL6231_PLL_K_MAX) + min_k = max_k = RL6231_PLL_K_MAX; + div_t = gcd(freq_in, freq_out); + f_max = 0xffffffff / RL6231_PLL_N_MAX; + div = find_best_div(freq_in, f_max, div_t); + f_in = freq_in / div; + f_out = freq_out / div; + k = min_k; + if (min_k < -1) + min_k = -1; + for (k_t = min_k; k_t <= max_k; k_t++) { + for (n_t = 0; n_t <= max_n; n_t++) { + in_t = f_in * (n_t + 2); + pll_out = f_out * (k_t + 2); + if (in_t == pll_out) { + m_bypass = true; + n = n_t; + k = k_t; + goto code_find; + } + out_t = in_t / (k_t + 2); + red = abs(f_out - out_t); + if (red < red_t) { + m_bypass = true; + n = n_t; + m = 0; + k = k_t; + if (red == 0) + goto code_find; + red_t = red; + } + for (m_t = 0; m_t <= max_m; m_t++) { + out_t = in_t / ((m_t + 2) * (k_t + 2)); + red = abs(f_out - out_t); + if (red < red_t) { + m_bypass = false; + n = n_t; + m = m_t; + k = k_t; + if (red == 0) + goto code_find; + red_t = red; + } + } + } + } + pr_debug("Only get approximation about PLL\n"); + +code_find: + if (k == -1) { + k_bypass = true; + k = 0; + } + + pll_code->m_bp = m_bypass; + pll_code->k_bp = k_bypass; + pll_code->m_code = m; + pll_code->n_code = n; + pll_code->k_code = k; + return 0; +} +EXPORT_SYMBOL_GPL(rl6231_pll_calc); + +int rl6231_get_clk_info(int sclk, int rate) +{ + int i; + static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(rl6231_get_clk_info); + +MODULE_DESCRIPTION("RL6231 class device shared support"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rl6231.h b/sound/soc/codecs/rl6231.h new file mode 100644 index 0000000000..9280827508 --- /dev/null +++ b/sound/soc/codecs/rl6231.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rl6231.h - RL6231 class device shared support + * + * Copyright 2014 Realtek Semiconductor Corp. + * + * Author: Oder Chiou + */ + +#ifndef __RL6231_H__ +#define __RL6231_H__ + +#define RL6231_PLL_INP_MAX 50000000 +#define RL6231_PLL_INP_MIN 256000 +#define RL6231_PLL_N_MAX 0x1ff +#define RL6231_PLL_K_MAX 0x1f +#define RL6231_PLL_M_MAX 0xf + +struct rl6231_pll_code { + bool m_bp; /* Indicates bypass m code or not. */ + bool k_bp; /* Indicates bypass k code or not. */ + int m_code; + int n_code; + int k_code; +}; + +int rl6231_calc_dmic_clk(int rate); +int rl6231_pll_calc(const unsigned int freq_in, + const unsigned int freq_out, struct rl6231_pll_code *pll_code); +int rl6231_get_clk_info(int sclk, int rate); +int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft); + +#endif /* __RL6231_H__ */ diff --git a/sound/soc/codecs/rl6347a.c b/sound/soc/codecs/rl6347a.c new file mode 100644 index 0000000000..fa8ac34549 --- /dev/null +++ b/sound/soc/codecs/rl6347a.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rl6347a.c - RL6347A class device shared support + * + * Copyright 2015 Realtek Semiconductor Corp. + * + * Author: Oder Chiou + */ + +#include +#include +#include + +#include "rl6347a.h" + +int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value) +{ + struct i2c_client *client = context; + struct rl6347a_priv *rl6347a = i2c_get_clientdata(client); + u8 data[4]; + int ret, i; + + /* handle index registers */ + if (reg <= 0xff) { + rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg); + for (i = 0; i < rl6347a->index_cache_size; i++) { + if (reg == rl6347a->index_cache[i].reg) { + rl6347a->index_cache[i].def = value; + break; + } + + } + reg = RL6347A_PROC_COEF; + } + + data[0] = (reg >> 24) & 0xff; + data[1] = (reg >> 16) & 0xff; + /* + * 4 bit VID: reg should be 0 + * 12 bit VID: value should be 0 + * So we use an OR operator to handle it rather than use if condition. + */ + data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff); + data[3] = value & 0xff; + + ret = i2c_master_send(client, data, 4); + + if (ret == 4) + return 0; + else + dev_err(&client->dev, "I2C error %d\n", ret); + if (ret < 0) + return ret; + else + return -EIO; +} +EXPORT_SYMBOL_GPL(rl6347a_hw_write); + +int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value) +{ + struct i2c_client *client = context; + struct i2c_msg xfer[2]; + int ret; + __be32 be_reg, buf = 0x0; + unsigned int index, vid; + + /* handle index registers */ + if (reg <= 0xff) { + rl6347a_hw_write(client, RL6347A_COEF_INDEX, reg); + reg = RL6347A_PROC_COEF; + } + + reg = reg | 0x80000; + vid = (reg >> 8) & 0xfff; + + if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) { + index = (reg >> 8) & 0xf; + reg = (reg & ~0xf0f) | index; + } + be_reg = cpu_to_be32(reg); + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 4; + xfer[0].buf = (u8 *)&be_reg; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 4; + xfer[1].buf = (u8 *)&buf; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + + *value = be32_to_cpu(buf); + + return 0; +} +EXPORT_SYMBOL_GPL(rl6347a_hw_read); + +MODULE_DESCRIPTION("RL6347A class device shared support"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h new file mode 100644 index 0000000000..761455a2fa --- /dev/null +++ b/sound/soc/codecs/rl6347a.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rl6347a.h - RL6347A class device shared support + * + * Copyright 2015 Realtek Semiconductor Corp. + * + * Author: Oder Chiou + */ +#ifndef __RL6347A_H__ +#define __RL6347A_H__ + +#include + +#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) + +#define RL6347A_VENDOR_REGISTERS 0x20 + +#define RL6347A_COEF_INDEX\ + VERB_CMD(AC_VERB_SET_COEF_INDEX, RL6347A_VENDOR_REGISTERS, 0) +#define RL6347A_PROC_COEF\ + VERB_CMD(AC_VERB_SET_PROC_COEF, RL6347A_VENDOR_REGISTERS, 0) + +struct rl6347a_priv { + struct reg_default *index_cache; + int index_cache_size; +}; + +int rl6347a_hw_write(void *context, unsigned int reg, unsigned int value); +int rl6347a_hw_read(void *context, unsigned int reg, unsigned int *value); + +#endif /* __RL6347A_H__ */ diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c new file mode 100644 index 0000000000..d5285baad5 --- /dev/null +++ b/sound/soc/codecs/rt1011.c @@ -0,0 +1,2492 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rt1011.c -- rt1011 ALSA SoC amplifier component driver + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + * + * Author: Shuming Fan + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt1011.h" + +static int rt1011_calibrate(struct rt1011_priv *rt1011, + unsigned char cali_flag); + +static const struct reg_sequence init_list[] = { + + { RT1011_POWER_9, 0xa840 }, + + { RT1011_ADC_SET_5, 0x0a20 }, + { RT1011_DAC_SET_2, 0xa032 }, + + { RT1011_SPK_PRO_DC_DET_1, 0xb00c }, + { RT1011_SPK_PRO_DC_DET_2, 0xcccc }, + + { RT1011_A_TIMING_1, 0x6054 }, + + { RT1011_POWER_7, 0x3e55 }, + { RT1011_POWER_8, 0x0520 }, + { RT1011_BOOST_CON_1, 0xe188 }, + { RT1011_POWER_4, 0x16f2 }, + + { RT1011_CROSS_BQ_SET_1, 0x0004 }, + { RT1011_SIL_DET, 0xc313 }, + { RT1011_SINE_GEN_REG_1, 0x0707 }, + + { RT1011_DC_CALIB_CLASSD_3, 0xcb00 }, + + { RT1011_DAC_SET_1, 0xe702 }, + { RT1011_DAC_SET_3, 0x2004 }, +}; + +static const struct reg_default rt1011_reg[] = { + {0x0000, 0x0000}, + {0x0002, 0x0000}, + {0x0004, 0xa000}, + {0x0006, 0x0000}, + {0x0008, 0x0003}, + {0x000a, 0x087e}, + {0x000c, 0x0020}, + {0x000e, 0x9002}, + {0x0010, 0x0000}, + {0x0012, 0x0000}, + {0x0020, 0x0c40}, + {0x0022, 0x4313}, + {0x0076, 0x0000}, + {0x0078, 0x0000}, + {0x007a, 0x0000}, + {0x007c, 0x10ec}, + {0x007d, 0x1011}, + {0x00f0, 0x5000}, + {0x00f2, 0x0374}, + {0x00f3, 0x0000}, + {0x00f4, 0x0000}, + {0x0100, 0x0038}, + {0x0102, 0xff02}, + {0x0104, 0x0232}, + {0x0106, 0x200c}, + {0x0107, 0x0000}, + {0x0108, 0x2f2f}, + {0x010a, 0x2f2f}, + {0x010c, 0x002f}, + {0x010e, 0xe000}, + {0x0110, 0x0820}, + {0x0111, 0x4010}, + {0x0112, 0x0000}, + {0x0114, 0x0000}, + {0x0116, 0x0000}, + {0x0118, 0x0000}, + {0x011a, 0x0101}, + {0x011c, 0x4567}, + {0x011e, 0x0000}, + {0x0120, 0x0000}, + {0x0122, 0x0000}, + {0x0124, 0x0123}, + {0x0126, 0x4567}, + {0x0200, 0x0000}, + {0x0300, 0xffdd}, + {0x0302, 0x001e}, + {0x0311, 0x0000}, + {0x0313, 0x5254}, + {0x0314, 0x0062}, + {0x0316, 0x7f40}, + {0x0319, 0x000f}, + {0x031a, 0xffff}, + {0x031b, 0x0000}, + {0x031c, 0x009f}, + {0x031d, 0xffff}, + {0x031e, 0x0000}, + {0x031f, 0x0000}, + {0x0320, 0xe31c}, + {0x0321, 0x0000}, + {0x0322, 0x0000}, + {0x0324, 0x0000}, + {0x0326, 0x0002}, + {0x0328, 0x20b2}, + {0x0329, 0x0175}, + {0x032a, 0x32ad}, + {0x032b, 0x3455}, + {0x032c, 0x0528}, + {0x032d, 0xa800}, + {0x032e, 0x030e}, + {0x0330, 0x2080}, + {0x0332, 0x0034}, + {0x0334, 0x0000}, + {0x0508, 0x0010}, + {0x050a, 0x0018}, + {0x050c, 0x0000}, + {0x050d, 0xffff}, + {0x050e, 0x1f1f}, + {0x050f, 0x04ff}, + {0x0510, 0x4020}, + {0x0511, 0x01f0}, + {0x0512, 0x0702}, + {0x0516, 0xbb80}, + {0x0517, 0xffff}, + {0x0518, 0xffff}, + {0x0519, 0x307f}, + {0x051a, 0xffff}, + {0x051b, 0x0000}, + {0x051c, 0x0000}, + {0x051d, 0x2000}, + {0x051e, 0x0000}, + {0x051f, 0x0000}, + {0x0520, 0x0000}, + {0x0521, 0x1001}, + {0x0522, 0x7fff}, + {0x0524, 0x7fff}, + {0x0526, 0x0000}, + {0x0528, 0x0000}, + {0x052a, 0x0000}, + {0x0530, 0x0401}, + {0x0532, 0x3000}, + {0x0534, 0x0000}, + {0x0535, 0xffff}, + {0x0536, 0x101c}, + {0x0538, 0x1814}, + {0x053a, 0x100c}, + {0x053c, 0x0804}, + {0x053d, 0x0000}, + {0x053e, 0x0000}, + {0x053f, 0x0000}, + {0x0540, 0x0000}, + {0x0541, 0x0000}, + {0x0542, 0x0000}, + {0x0543, 0x0000}, + {0x0544, 0x001c}, + {0x0545, 0x1814}, + {0x0546, 0x100c}, + {0x0547, 0x0804}, + {0x0548, 0x0000}, + {0x0549, 0x0000}, + {0x054a, 0x0000}, + {0x054b, 0x0000}, + {0x054c, 0x0000}, + {0x054d, 0x0000}, + {0x054e, 0x0000}, + {0x054f, 0x0000}, + {0x0566, 0x0000}, + {0x0568, 0x20f1}, + {0x056a, 0x0007}, + {0x0600, 0x9d00}, + {0x0611, 0x2000}, + {0x0612, 0x505f}, + {0x0613, 0x0444}, + {0x0614, 0x4000}, + {0x0615, 0x4004}, + {0x0616, 0x0606}, + {0x0617, 0x8904}, + {0x0618, 0xe021}, + {0x0621, 0x2000}, + {0x0622, 0x505f}, + {0x0623, 0x0444}, + {0x0624, 0x4000}, + {0x0625, 0x4004}, + {0x0626, 0x0606}, + {0x0627, 0x8704}, + {0x0628, 0xe021}, + {0x0631, 0x2000}, + {0x0632, 0x517f}, + {0x0633, 0x0440}, + {0x0634, 0x4000}, + {0x0635, 0x4104}, + {0x0636, 0x0306}, + {0x0637, 0x8904}, + {0x0638, 0xe021}, + {0x0702, 0x0014}, + {0x0704, 0x0000}, + {0x0706, 0x0014}, + {0x0708, 0x0000}, + {0x070a, 0x0000}, + {0x0710, 0x0200}, + {0x0711, 0x0000}, + {0x0712, 0x0200}, + {0x0713, 0x0000}, + {0x0720, 0x0200}, + {0x0721, 0x0000}, + {0x0722, 0x0000}, + {0x0723, 0x0000}, + {0x0724, 0x0000}, + {0x0725, 0x0000}, + {0x0726, 0x0000}, + {0x0727, 0x0000}, + {0x0728, 0x0000}, + {0x0729, 0x0000}, + {0x0730, 0x0200}, + {0x0731, 0x0000}, + {0x0732, 0x0000}, + {0x0733, 0x0000}, + {0x0734, 0x0000}, + {0x0735, 0x0000}, + {0x0736, 0x0000}, + {0x0737, 0x0000}, + {0x0738, 0x0000}, + {0x0739, 0x0000}, + {0x0740, 0x0200}, + {0x0741, 0x0000}, + {0x0742, 0x0000}, + {0x0743, 0x0000}, + {0x0744, 0x0000}, + {0x0745, 0x0000}, + {0x0746, 0x0000}, + {0x0747, 0x0000}, + {0x0748, 0x0000}, + {0x0749, 0x0000}, + {0x0750, 0x0200}, + {0x0751, 0x0000}, + {0x0752, 0x0000}, + {0x0753, 0x0000}, + {0x0754, 0x0000}, + {0x0755, 0x0000}, + {0x0756, 0x0000}, + {0x0757, 0x0000}, + {0x0758, 0x0000}, + {0x0759, 0x0000}, + {0x0760, 0x0200}, + {0x0761, 0x0000}, + {0x0762, 0x0000}, + {0x0763, 0x0000}, + {0x0764, 0x0000}, + {0x0765, 0x0000}, + {0x0766, 0x0000}, + {0x0767, 0x0000}, + {0x0768, 0x0000}, + {0x0769, 0x0000}, + {0x0770, 0x0200}, + {0x0771, 0x0000}, + {0x0772, 0x0000}, + {0x0773, 0x0000}, + {0x0774, 0x0000}, + {0x0775, 0x0000}, + {0x0776, 0x0000}, + {0x0777, 0x0000}, + {0x0778, 0x0000}, + {0x0779, 0x0000}, + {0x0780, 0x0200}, + {0x0781, 0x0000}, + {0x0782, 0x0000}, + {0x0783, 0x0000}, + {0x0784, 0x0000}, + {0x0785, 0x0000}, + {0x0786, 0x0000}, + {0x0787, 0x0000}, + {0x0788, 0x0000}, + {0x0789, 0x0000}, + {0x0790, 0x0200}, + {0x0791, 0x0000}, + {0x0792, 0x0000}, + {0x0793, 0x0000}, + {0x0794, 0x0000}, + {0x0795, 0x0000}, + {0x0796, 0x0000}, + {0x0797, 0x0000}, + {0x0798, 0x0000}, + {0x0799, 0x0000}, + {0x07a0, 0x0200}, + {0x07a1, 0x0000}, + {0x07a2, 0x0000}, + {0x07a3, 0x0000}, + {0x07a4, 0x0000}, + {0x07a5, 0x0000}, + {0x07a6, 0x0000}, + {0x07a7, 0x0000}, + {0x07a8, 0x0000}, + {0x07a9, 0x0000}, + {0x07b0, 0x0200}, + {0x07b1, 0x0000}, + {0x07b2, 0x0000}, + {0x07b3, 0x0000}, + {0x07b4, 0x0000}, + {0x07b5, 0x0000}, + {0x07b6, 0x0000}, + {0x07b7, 0x0000}, + {0x07b8, 0x0000}, + {0x07b9, 0x0000}, + {0x07c0, 0x0200}, + {0x07c1, 0x0000}, + {0x07c2, 0x0000}, + {0x07c3, 0x0000}, + {0x07c4, 0x0000}, + {0x07c5, 0x0000}, + {0x07c6, 0x0000}, + {0x07c7, 0x0000}, + {0x07c8, 0x0000}, + {0x07c9, 0x0000}, + {0x1000, 0x4040}, + {0x1002, 0x6505}, + {0x1004, 0x5405}, + {0x1006, 0x5555}, + {0x1007, 0x003f}, + {0x1008, 0x7fd7}, + {0x1009, 0x770f}, + {0x100a, 0xfffe}, + {0x100b, 0xe000}, + {0x100c, 0x0000}, + {0x100d, 0x0007}, + {0x1010, 0xa433}, + {0x1020, 0x0000}, + {0x1022, 0x0000}, + {0x1024, 0x0000}, + {0x1200, 0x5a01}, + {0x1202, 0x6324}, + {0x1204, 0x0b00}, + {0x1206, 0x0000}, + {0x1208, 0x0000}, + {0x120a, 0x0024}, + {0x120c, 0x0000}, + {0x120e, 0x000e}, + {0x1210, 0x0000}, + {0x1212, 0x0000}, + {0x1300, 0x0701}, + {0x1302, 0x12f9}, + {0x1304, 0x3405}, + {0x1305, 0x0844}, + {0x1306, 0x5611}, + {0x1308, 0x555e}, + {0x130a, 0xa605}, + {0x130c, 0x2000}, + {0x130e, 0x0000}, + {0x130f, 0x0001}, + {0x1310, 0xaa48}, + {0x1312, 0x0285}, + {0x1314, 0xaaaa}, + {0x1316, 0xaaa0}, + {0x1318, 0x2aaa}, + {0x131a, 0xaa07}, + {0x1322, 0x0029}, + {0x1323, 0x4a52}, + {0x1324, 0x002c}, + {0x1325, 0x0b02}, + {0x1326, 0x002d}, + {0x1327, 0x6b5a}, + {0x1328, 0x002e}, + {0x1329, 0xcbb2}, + {0x132a, 0x0030}, + {0x132b, 0x2c0b}, + {0x1330, 0x0031}, + {0x1331, 0x8c63}, + {0x1332, 0x0032}, + {0x1333, 0xecbb}, + {0x1334, 0x0034}, + {0x1335, 0x4d13}, + {0x1336, 0x0037}, + {0x1337, 0x0dc3}, + {0x1338, 0x003d}, + {0x1339, 0xef7b}, + {0x133a, 0x0044}, + {0x133b, 0xd134}, + {0x133c, 0x0047}, + {0x133d, 0x91e4}, + {0x133e, 0x004d}, + {0x133f, 0xc370}, + {0x1340, 0x0053}, + {0x1341, 0xf4fd}, + {0x1342, 0x0060}, + {0x1343, 0x5816}, + {0x1344, 0x006c}, + {0x1345, 0xbb2e}, + {0x1346, 0x0072}, + {0x1347, 0xecbb}, + {0x1348, 0x0076}, + {0x1349, 0x5d97}, + {0x1500, 0x0702}, + {0x1502, 0x002f}, + {0x1504, 0x0000}, + {0x1510, 0x0064}, + {0x1512, 0x0000}, + {0x1514, 0xdf47}, + {0x1516, 0x079c}, + {0x1518, 0xfbf5}, + {0x151a, 0x00bc}, + {0x151c, 0x3b85}, + {0x151e, 0x02b3}, + {0x1520, 0x3333}, + {0x1522, 0x0000}, + {0x1524, 0x4000}, + {0x1528, 0x0064}, + {0x152a, 0x0000}, + {0x152c, 0x0000}, + {0x152e, 0x0000}, + {0x1530, 0x0000}, + {0x1532, 0x0000}, + {0x1534, 0x0000}, + {0x1536, 0x0000}, + {0x1538, 0x0040}, + {0x1539, 0x0000}, + {0x153a, 0x0040}, + {0x153b, 0x0000}, + {0x153c, 0x0064}, + {0x153e, 0x0bf9}, + {0x1540, 0xb2a9}, + {0x1544, 0x0200}, + {0x1546, 0x0000}, + {0x1548, 0x00ca}, + {0x1552, 0x03ff}, + {0x1554, 0x017f}, + {0x1556, 0x017f}, + {0x155a, 0x0000}, + {0x155c, 0x0000}, + {0x1560, 0x0040}, + {0x1562, 0x0000}, + {0x1570, 0x03ff}, + {0x1571, 0xdcff}, + {0x1572, 0x1e00}, + {0x1573, 0x224f}, + {0x1574, 0x0000}, + {0x1575, 0x0000}, + {0x1576, 0x1e00}, + {0x1577, 0x0000}, + {0x1578, 0x0000}, + {0x1579, 0x1128}, + {0x157a, 0x03ff}, + {0x157b, 0xdcff}, + {0x157c, 0x1e00}, + {0x157d, 0x224f}, + {0x157e, 0x0000}, + {0x157f, 0x0000}, + {0x1580, 0x1e00}, + {0x1581, 0x0000}, + {0x1582, 0x0000}, + {0x1583, 0x1128}, + {0x1590, 0x03ff}, + {0x1591, 0xdcff}, + {0x1592, 0x1e00}, + {0x1593, 0x224f}, + {0x1594, 0x0000}, + {0x1595, 0x0000}, + {0x1596, 0x1e00}, + {0x1597, 0x0000}, + {0x1598, 0x0000}, + {0x1599, 0x1128}, + {0x159a, 0x03ff}, + {0x159b, 0xdcff}, + {0x159c, 0x1e00}, + {0x159d, 0x224f}, + {0x159e, 0x0000}, + {0x159f, 0x0000}, + {0x15a0, 0x1e00}, + {0x15a1, 0x0000}, + {0x15a2, 0x0000}, + {0x15a3, 0x1128}, + {0x15b0, 0x007f}, + {0x15b1, 0xffff}, + {0x15b2, 0x007f}, + {0x15b3, 0xffff}, + {0x15b4, 0x007f}, + {0x15b5, 0xffff}, + {0x15b8, 0x007f}, + {0x15b9, 0xffff}, + {0x15bc, 0x0000}, + {0x15bd, 0x0000}, + {0x15be, 0xff00}, + {0x15bf, 0x0000}, + {0x15c0, 0xff00}, + {0x15c1, 0x0000}, + {0x15c3, 0xfc00}, + {0x15c4, 0xbb80}, + {0x15d0, 0x0000}, + {0x15d1, 0x0000}, + {0x15d2, 0x0000}, + {0x15d3, 0x0000}, + {0x15d4, 0x0000}, + {0x15d5, 0x0000}, + {0x15d6, 0x0000}, + {0x15d7, 0x0000}, + {0x15d8, 0x0200}, + {0x15d9, 0x0000}, + {0x15da, 0x0000}, + {0x15db, 0x0000}, + {0x15dc, 0x0000}, + {0x15dd, 0x0000}, + {0x15de, 0x0000}, + {0x15df, 0x0000}, + {0x15e0, 0x0000}, + {0x15e1, 0x0000}, + {0x15e2, 0x0200}, + {0x15e3, 0x0000}, + {0x15e4, 0x0000}, + {0x15e5, 0x0000}, + {0x15e6, 0x0000}, + {0x15e7, 0x0000}, + {0x15e8, 0x0000}, + {0x15e9, 0x0000}, + {0x15ea, 0x0000}, + {0x15eb, 0x0000}, + {0x15ec, 0x0200}, + {0x15ed, 0x0000}, + {0x15ee, 0x0000}, + {0x15ef, 0x0000}, + {0x15f0, 0x0000}, + {0x15f1, 0x0000}, + {0x15f2, 0x0000}, + {0x15f3, 0x0000}, + {0x15f4, 0x0000}, + {0x15f5, 0x0000}, + {0x15f6, 0x0200}, + {0x15f7, 0x0200}, + {0x15f8, 0x8200}, + {0x15f9, 0x0000}, + {0x1600, 0x007d}, + {0x1601, 0xa178}, + {0x1602, 0x00c2}, + {0x1603, 0x5383}, + {0x1604, 0x0000}, + {0x1605, 0x02c1}, + {0x1606, 0x007d}, + {0x1607, 0xa178}, + {0x1608, 0x00c2}, + {0x1609, 0x5383}, + {0x160a, 0x003e}, + {0x160b, 0xd37d}, + {0x1611, 0x3210}, + {0x1612, 0x7418}, + {0x1613, 0xc0ff}, + {0x1614, 0x0000}, + {0x1615, 0x00ff}, + {0x1616, 0x0000}, + {0x1617, 0x0000}, + {0x1621, 0x6210}, + {0x1622, 0x7418}, + {0x1623, 0xc0ff}, + {0x1624, 0x0000}, + {0x1625, 0x00ff}, + {0x1626, 0x0000}, + {0x1627, 0x0000}, + {0x1631, 0x3a14}, + {0x1632, 0x7418}, + {0x1633, 0xc3ff}, + {0x1634, 0x0000}, + {0x1635, 0x00ff}, + {0x1636, 0x0000}, + {0x1637, 0x0000}, + {0x1638, 0x0000}, + {0x163a, 0x0000}, + {0x163c, 0x0000}, + {0x163e, 0x0000}, + {0x1640, 0x0000}, + {0x1642, 0x0000}, + {0x1644, 0x0000}, + {0x1646, 0x0000}, + {0x1648, 0x0000}, + {0x1650, 0x0000}, + {0x1652, 0x0000}, + {0x1654, 0x0000}, + {0x1656, 0x0000}, + {0x1658, 0x0000}, + {0x1660, 0x0000}, + {0x1662, 0x0000}, + {0x1664, 0x0000}, + {0x1666, 0x0000}, + {0x1668, 0x0000}, + {0x1670, 0x0000}, + {0x1672, 0x0000}, + {0x1674, 0x0000}, + {0x1676, 0x0000}, + {0x1678, 0x0000}, + {0x1680, 0x0000}, + {0x1682, 0x0000}, + {0x1684, 0x0000}, + {0x1686, 0x0000}, + {0x1688, 0x0000}, + {0x1690, 0x0000}, + {0x1692, 0x0000}, + {0x1694, 0x0000}, + {0x1696, 0x0000}, + {0x1698, 0x0000}, + {0x1700, 0x0000}, + {0x1702, 0x0000}, + {0x1704, 0x0000}, + {0x1706, 0x0000}, + {0x1708, 0x0000}, + {0x1710, 0x0000}, + {0x1712, 0x0000}, + {0x1714, 0x0000}, + {0x1716, 0x0000}, + {0x1718, 0x0000}, + {0x1720, 0x0000}, + {0x1722, 0x0000}, + {0x1724, 0x0000}, + {0x1726, 0x0000}, + {0x1728, 0x0000}, + {0x1730, 0x0000}, + {0x1732, 0x0000}, + {0x1734, 0x0000}, + {0x1736, 0x0000}, + {0x1738, 0x0000}, + {0x173a, 0x0000}, + {0x173c, 0x0000}, + {0x173e, 0x0000}, + {0x17bb, 0x0500}, + {0x17bd, 0x0004}, + {0x17bf, 0x0004}, + {0x17c1, 0x0004}, + {0x17c2, 0x7fff}, + {0x17c3, 0x0000}, + {0x17c5, 0x0000}, + {0x17c7, 0x0000}, + {0x17c9, 0x0000}, + {0x17cb, 0x2010}, + {0x17cd, 0x0000}, + {0x17cf, 0x0000}, + {0x17d1, 0x0000}, + {0x17d3, 0x0000}, + {0x17d5, 0x0000}, + {0x17d7, 0x0000}, + {0x17d9, 0x0000}, + {0x17db, 0x0000}, + {0x17dd, 0x0000}, + {0x17df, 0x0000}, + {0x17e1, 0x0000}, + {0x17e3, 0x0000}, + {0x17e5, 0x0000}, + {0x17e7, 0x0000}, + {0x17e9, 0x0000}, + {0x17eb, 0x0000}, + {0x17ed, 0x0000}, + {0x17ef, 0x0000}, + {0x17f1, 0x0000}, + {0x17f3, 0x0000}, + {0x17f5, 0x0000}, + {0x17f7, 0x0000}, + {0x17f9, 0x0000}, + {0x17fb, 0x0000}, + {0x17fd, 0x0000}, + {0x17ff, 0x0000}, + {0x1801, 0x0000}, + {0x1803, 0x0000}, +}; + +static int rt1011_reg_init(struct snd_soc_component *component) +{ + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + + regmap_multi_reg_write(rt1011->regmap, + init_list, ARRAY_SIZE(init_list)); + return 0; +} + +static bool rt1011_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1011_RESET: + case RT1011_SRC_2: + case RT1011_CLK_DET: + case RT1011_SIL_DET: + case RT1011_VERSION_ID: + case RT1011_VENDOR_ID: + case RT1011_DEVICE_ID: + case RT1011_DUM_RO: + case RT1011_DAC_SET_3: + case RT1011_PWM_CAL: + case RT1011_SPK_VOL_TEST_OUT: + case RT1011_VBAT_VOL_DET_1: + case RT1011_VBAT_TEST_OUT_1: + case RT1011_VBAT_TEST_OUT_2: + case RT1011_VBAT_PROTECTION: + case RT1011_VBAT_DET: + case RT1011_BOOST_CON_1: + case RT1011_SHORT_CIRCUIT_DET_1: + case RT1011_SPK_TEMP_PROTECT_3: + case RT1011_SPK_TEMP_PROTECT_6: + case RT1011_SPK_PRO_DC_DET_3: + case RT1011_SPK_PRO_DC_DET_7: + case RT1011_SPK_PRO_DC_DET_8: + case RT1011_SPL_1: + case RT1011_SPL_4: + case RT1011_EXCUR_PROTECT_1: + case RT1011_CROSS_BQ_SET_1: + case RT1011_CROSS_BQ_SET_2: + case RT1011_BQ_SET_0: + case RT1011_BQ_SET_1: + case RT1011_BQ_SET_2: + case RT1011_TEST_PAD_STATUS: + case RT1011_DC_CALIB_CLASSD_1: + case RT1011_DC_CALIB_CLASSD_5: + case RT1011_DC_CALIB_CLASSD_6: + case RT1011_DC_CALIB_CLASSD_7: + case RT1011_DC_CALIB_CLASSD_8: + case RT1011_SINE_GEN_REG_2: + case RT1011_STP_CALIB_RS_TEMP: + case RT1011_SPK_RESISTANCE_1: + case RT1011_SPK_RESISTANCE_2: + case RT1011_SPK_THERMAL: + case RT1011_ALC_BK_GAIN_O: + case RT1011_ALC_BK_GAIN_O_PRE: + case RT1011_SPK_DC_O_23_16: + case RT1011_SPK_DC_O_15_0: + case RT1011_INIT_RECIPROCAL_SYN_24_16: + case RT1011_INIT_RECIPROCAL_SYN_15_0: + case RT1011_SPK_EXCURSION_23_16: + case RT1011_SPK_EXCURSION_15_0: + case RT1011_SEP_MAIN_OUT_23_16: + case RT1011_SEP_MAIN_OUT_15_0: + case RT1011_ALC_DRC_HB_INTERNAL_5: + case RT1011_ALC_DRC_HB_INTERNAL_6: + case RT1011_ALC_DRC_HB_INTERNAL_7: + case RT1011_ALC_DRC_BB_INTERNAL_5: + case RT1011_ALC_DRC_BB_INTERNAL_6: + case RT1011_ALC_DRC_BB_INTERNAL_7: + case RT1011_ALC_DRC_POS_INTERNAL_5: + case RT1011_ALC_DRC_POS_INTERNAL_6: + case RT1011_ALC_DRC_POS_INTERNAL_7: + case RT1011_ALC_DRC_POS_INTERNAL_8: + case RT1011_ALC_DRC_POS_INTERNAL_9: + case RT1011_ALC_DRC_POS_INTERNAL_10: + case RT1011_ALC_DRC_POS_INTERNAL_11: + case RT1011_IRQ_1: + case RT1011_EFUSE_CONTROL_1: + case RT1011_EFUSE_CONTROL_2: + case RT1011_EFUSE_MATCH_DONE ... RT1011_EFUSE_READ_R0_3_15_0: + return true; + + default: + return false; + } +} + +static bool rt1011_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1011_RESET: + case RT1011_CLK_1: + case RT1011_CLK_2: + case RT1011_CLK_3: + case RT1011_CLK_4: + case RT1011_PLL_1: + case RT1011_PLL_2: + case RT1011_SRC_1: + case RT1011_SRC_2: + case RT1011_SRC_3: + case RT1011_CLK_DET: + case RT1011_SIL_DET: + case RT1011_PRIV_INDEX: + case RT1011_PRIV_DATA: + case RT1011_CUSTOMER_ID: + case RT1011_FM_VER: + case RT1011_VERSION_ID: + case RT1011_VENDOR_ID: + case RT1011_DEVICE_ID: + case RT1011_DUM_RW_0: + case RT1011_DUM_YUN: + case RT1011_DUM_RW_1: + case RT1011_DUM_RO: + case RT1011_MAN_I2C_DEV: + case RT1011_DAC_SET_1: + case RT1011_DAC_SET_2: + case RT1011_DAC_SET_3: + case RT1011_ADC_SET: + case RT1011_ADC_SET_1: + case RT1011_ADC_SET_2: + case RT1011_ADC_SET_3: + case RT1011_ADC_SET_4: + case RT1011_ADC_SET_5: + case RT1011_TDM_TOTAL_SET: + case RT1011_TDM1_SET_TCON: + case RT1011_TDM1_SET_1: + case RT1011_TDM1_SET_2: + case RT1011_TDM1_SET_3: + case RT1011_TDM1_SET_4: + case RT1011_TDM1_SET_5: + case RT1011_TDM2_SET_1: + case RT1011_TDM2_SET_2: + case RT1011_TDM2_SET_3: + case RT1011_TDM2_SET_4: + case RT1011_TDM2_SET_5: + case RT1011_PWM_CAL: + case RT1011_MIXER_1: + case RT1011_MIXER_2: + case RT1011_ADRC_LIMIT: + case RT1011_A_PRO: + case RT1011_A_TIMING_1: + case RT1011_A_TIMING_2: + case RT1011_A_TEMP_SEN: + case RT1011_SPK_VOL_DET_1: + case RT1011_SPK_VOL_DET_2: + case RT1011_SPK_VOL_TEST_OUT: + case RT1011_VBAT_VOL_DET_1: + case RT1011_VBAT_VOL_DET_2: + case RT1011_VBAT_TEST_OUT_1: + case RT1011_VBAT_TEST_OUT_2: + case RT1011_VBAT_PROTECTION: + case RT1011_VBAT_DET: + case RT1011_POWER_1: + case RT1011_POWER_2: + case RT1011_POWER_3: + case RT1011_POWER_4: + case RT1011_POWER_5: + case RT1011_POWER_6: + case RT1011_POWER_7: + case RT1011_POWER_8: + case RT1011_POWER_9: + case RT1011_CLASS_D_POS: + case RT1011_BOOST_CON_1: + case RT1011_BOOST_CON_2: + case RT1011_ANALOG_CTRL: + case RT1011_POWER_SEQ: + case RT1011_SHORT_CIRCUIT_DET_1: + case RT1011_SHORT_CIRCUIT_DET_2: + case RT1011_SPK_TEMP_PROTECT_0: + case RT1011_SPK_TEMP_PROTECT_1: + case RT1011_SPK_TEMP_PROTECT_2: + case RT1011_SPK_TEMP_PROTECT_3: + case RT1011_SPK_TEMP_PROTECT_4: + case RT1011_SPK_TEMP_PROTECT_5: + case RT1011_SPK_TEMP_PROTECT_6: + case RT1011_SPK_TEMP_PROTECT_7: + case RT1011_SPK_TEMP_PROTECT_8: + case RT1011_SPK_TEMP_PROTECT_9: + case RT1011_SPK_PRO_DC_DET_1: + case RT1011_SPK_PRO_DC_DET_2: + case RT1011_SPK_PRO_DC_DET_3: + case RT1011_SPK_PRO_DC_DET_4: + case RT1011_SPK_PRO_DC_DET_5: + case RT1011_SPK_PRO_DC_DET_6: + case RT1011_SPK_PRO_DC_DET_7: + case RT1011_SPK_PRO_DC_DET_8: + case RT1011_SPL_1: + case RT1011_SPL_2: + case RT1011_SPL_3: + case RT1011_SPL_4: + case RT1011_THER_FOLD_BACK_1: + case RT1011_THER_FOLD_BACK_2: + case RT1011_EXCUR_PROTECT_1: + case RT1011_EXCUR_PROTECT_2: + case RT1011_EXCUR_PROTECT_3: + case RT1011_EXCUR_PROTECT_4: + case RT1011_BAT_GAIN_1: + case RT1011_BAT_GAIN_2: + case RT1011_BAT_GAIN_3: + case RT1011_BAT_GAIN_4: + case RT1011_BAT_GAIN_5: + case RT1011_BAT_GAIN_6: + case RT1011_BAT_GAIN_7: + case RT1011_BAT_GAIN_8: + case RT1011_BAT_GAIN_9: + case RT1011_BAT_GAIN_10: + case RT1011_BAT_GAIN_11: + case RT1011_BAT_RT_THMAX_1: + case RT1011_BAT_RT_THMAX_2: + case RT1011_BAT_RT_THMAX_3: + case RT1011_BAT_RT_THMAX_4: + case RT1011_BAT_RT_THMAX_5: + case RT1011_BAT_RT_THMAX_6: + case RT1011_BAT_RT_THMAX_7: + case RT1011_BAT_RT_THMAX_8: + case RT1011_BAT_RT_THMAX_9: + case RT1011_BAT_RT_THMAX_10: + case RT1011_BAT_RT_THMAX_11: + case RT1011_BAT_RT_THMAX_12: + case RT1011_SPREAD_SPECTURM: + case RT1011_PRO_GAIN_MODE: + case RT1011_RT_DRC_CROSS: + case RT1011_RT_DRC_HB_1: + case RT1011_RT_DRC_HB_2: + case RT1011_RT_DRC_HB_3: + case RT1011_RT_DRC_HB_4: + case RT1011_RT_DRC_HB_5: + case RT1011_RT_DRC_HB_6: + case RT1011_RT_DRC_HB_7: + case RT1011_RT_DRC_HB_8: + case RT1011_RT_DRC_BB_1: + case RT1011_RT_DRC_BB_2: + case RT1011_RT_DRC_BB_3: + case RT1011_RT_DRC_BB_4: + case RT1011_RT_DRC_BB_5: + case RT1011_RT_DRC_BB_6: + case RT1011_RT_DRC_BB_7: + case RT1011_RT_DRC_BB_8: + case RT1011_RT_DRC_POS_1: + case RT1011_RT_DRC_POS_2: + case RT1011_RT_DRC_POS_3: + case RT1011_RT_DRC_POS_4: + case RT1011_RT_DRC_POS_5: + case RT1011_RT_DRC_POS_6: + case RT1011_RT_DRC_POS_7: + case RT1011_RT_DRC_POS_8: + case RT1011_CROSS_BQ_SET_1: + case RT1011_CROSS_BQ_SET_2: + case RT1011_BQ_SET_0: + case RT1011_BQ_SET_1: + case RT1011_BQ_SET_2: + case RT1011_BQ_PRE_GAIN_28_16: + case RT1011_BQ_PRE_GAIN_15_0: + case RT1011_BQ_POST_GAIN_28_16: + case RT1011_BQ_POST_GAIN_15_0: + case RT1011_BQ_H0_28_16 ... RT1011_BQ_A2_15_0: + case RT1011_BQ_1_H0_28_16 ... RT1011_BQ_1_A2_15_0: + case RT1011_BQ_2_H0_28_16 ... RT1011_BQ_2_A2_15_0: + case RT1011_BQ_3_H0_28_16 ... RT1011_BQ_3_A2_15_0: + case RT1011_BQ_4_H0_28_16 ... RT1011_BQ_4_A2_15_0: + case RT1011_BQ_5_H0_28_16 ... RT1011_BQ_5_A2_15_0: + case RT1011_BQ_6_H0_28_16 ... RT1011_BQ_6_A2_15_0: + case RT1011_BQ_7_H0_28_16 ... RT1011_BQ_7_A2_15_0: + case RT1011_BQ_8_H0_28_16 ... RT1011_BQ_8_A2_15_0: + case RT1011_BQ_9_H0_28_16 ... RT1011_BQ_9_A2_15_0: + case RT1011_BQ_10_H0_28_16 ... RT1011_BQ_10_A2_15_0: + case RT1011_TEST_PAD_STATUS ... RT1011_PLL_INTERNAL_SET: + case RT1011_TEST_OUT_1 ... RT1011_TEST_OUT_3: + case RT1011_DC_CALIB_CLASSD_1 ... RT1011_DC_CALIB_CLASSD_10: + case RT1011_CLASSD_INTERNAL_SET_1 ... RT1011_VREF_LV_1: + case RT1011_SMART_BOOST_TIMING_1 ... RT1011_SMART_BOOST_TIMING_36: + case RT1011_SINE_GEN_REG_1 ... RT1011_SINE_GEN_REG_3: + case RT1011_STP_INITIAL_RS_TEMP ... RT1011_SPK_THERMAL: + case RT1011_STP_OTP_TH ... RT1011_INIT_RECIPROCAL_SYN_15_0: + case RT1011_STP_BQ_1_A1_L_28_16 ... RT1011_STP_BQ_1_H0_R_15_0: + case RT1011_STP_BQ_2_A1_L_28_16 ... RT1011_SEP_RE_REG_15_0: + case RT1011_DRC_CF_PARAMS_1 ... RT1011_DRC_CF_PARAMS_12: + case RT1011_ALC_DRC_HB_INTERNAL_1 ... RT1011_ALC_DRC_HB_INTERNAL_7: + case RT1011_ALC_DRC_BB_INTERNAL_1 ... RT1011_ALC_DRC_BB_INTERNAL_7: + case RT1011_ALC_DRC_POS_INTERNAL_1 ... RT1011_ALC_DRC_POS_INTERNAL_8: + case RT1011_ALC_DRC_POS_INTERNAL_9 ... RT1011_BQ_1_PARAMS_CHECK_5: + case RT1011_BQ_2_PARAMS_CHECK_1 ... RT1011_BQ_2_PARAMS_CHECK_5: + case RT1011_BQ_3_PARAMS_CHECK_1 ... RT1011_BQ_3_PARAMS_CHECK_5: + case RT1011_BQ_4_PARAMS_CHECK_1 ... RT1011_BQ_4_PARAMS_CHECK_5: + case RT1011_BQ_5_PARAMS_CHECK_1 ... RT1011_BQ_5_PARAMS_CHECK_5: + case RT1011_BQ_6_PARAMS_CHECK_1 ... RT1011_BQ_6_PARAMS_CHECK_5: + case RT1011_BQ_7_PARAMS_CHECK_1 ... RT1011_BQ_7_PARAMS_CHECK_5: + case RT1011_BQ_8_PARAMS_CHECK_1 ... RT1011_BQ_8_PARAMS_CHECK_5: + case RT1011_BQ_9_PARAMS_CHECK_1 ... RT1011_BQ_9_PARAMS_CHECK_5: + case RT1011_BQ_10_PARAMS_CHECK_1 ... RT1011_BQ_10_PARAMS_CHECK_5: + case RT1011_IRQ_1 ... RT1011_PART_NUMBER_EFUSE: + case RT1011_EFUSE_CONTROL_1 ... RT1011_EFUSE_READ_R0_3_15_0: + return true; + default: + return false; + } +} + +static const char * const rt1011_din_source_select[] = { + "Left", + "Right", + "Left + Right average", +}; + +static SOC_ENUM_SINGLE_DECL(rt1011_din_source_enum, RT1011_CROSS_BQ_SET_1, 5, + rt1011_din_source_select); + +static const char * const rt1011_tdm_data_out_select[] = { + "TDM_O_LR", "BQ1", "DVOL", "BQ10", "ALC", "DMIX", "ADC_SRC_LR", + "ADC_O_LR", "ADC_MONO", "RSPK_BPF_LR", "DMIX_ADD", "ENVELOPE_FS", + "SEP_O_GAIN", "ALC_BK_GAIN", "STP_V_C", "DMIX_ABST" +}; + +static const char * const rt1011_tdm_l_ch_data_select[] = { + "Slot0", "Slot1", "Slot2", "Slot3", "Slot4", "Slot5", "Slot6", "Slot7" +}; +static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_l_dac1_enum, RT1011_TDM1_SET_4, 12, + rt1011_tdm_l_ch_data_select); +static SOC_ENUM_SINGLE_DECL(rt1011_tdm2_l_dac1_enum, RT1011_TDM2_SET_4, 12, + rt1011_tdm_l_ch_data_select); + +static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_dat_enum, + RT1011_ADCDAT_OUT_SOURCE, 0, rt1011_tdm_data_out_select); +static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_adc1_loc_enum, RT1011_TDM1_SET_2, 0, + rt1011_tdm_l_ch_data_select); + +static const char * const rt1011_adc_data_mode_select[] = { + "Stereo", "Mono" +}; +static SOC_ENUM_SINGLE_DECL(rt1011_adc_dout_mode_enum, RT1011_TDM1_SET_1, 12, + rt1011_adc_data_mode_select); + +static const char * const rt1011_tdm_adc_data_len_control[] = { + "1CH", "2CH", "3CH", "4CH", "5CH", "6CH", "7CH", "8CH" +}; +static SOC_ENUM_SINGLE_DECL(rt1011_tdm1_dout_len_enum, RT1011_TDM1_SET_2, 13, + rt1011_tdm_adc_data_len_control); +static SOC_ENUM_SINGLE_DECL(rt1011_tdm2_dout_len_enum, RT1011_TDM2_SET_2, 13, + rt1011_tdm_adc_data_len_control); + +static const char * const rt1011_tdm_adc_swap_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc1_1_enum, RT1011_TDM1_SET_3, 6, + rt1011_tdm_adc_swap_select); +static SOC_ENUM_SINGLE_DECL(rt1011_tdm_adc2_1_enum, RT1011_TDM1_SET_3, 4, + rt1011_tdm_adc_swap_select); + +static void rt1011_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT1011_RESET, 0); +} + +static int rt1011_recv_spk_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1011_priv *rt1011 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1011->recv_spk_mode; + + return 0; +} + +static int rt1011_recv_spk_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1011_priv *rt1011 = + snd_soc_component_get_drvdata(component); + + if (ucontrol->value.integer.value[0] == rt1011->recv_spk_mode) + return 0; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + rt1011->recv_spk_mode = ucontrol->value.integer.value[0]; + + if (rt1011->recv_spk_mode) { + + /* 1: recevier mode on */ + snd_soc_component_update_bits(component, + RT1011_CLASSD_INTERNAL_SET_3, + RT1011_REG_GAIN_CLASSD_RI_SPK_MASK, + RT1011_REG_GAIN_CLASSD_RI_410K); + snd_soc_component_update_bits(component, + RT1011_CLASSD_INTERNAL_SET_1, + RT1011_RECV_MODE_SPK_MASK, + RT1011_RECV_MODE); + } else { + /* 0: speaker mode on */ + snd_soc_component_update_bits(component, + RT1011_CLASSD_INTERNAL_SET_3, + RT1011_REG_GAIN_CLASSD_RI_SPK_MASK, + RT1011_REG_GAIN_CLASSD_RI_72P5K); + snd_soc_component_update_bits(component, + RT1011_CLASSD_INTERNAL_SET_1, + RT1011_RECV_MODE_SPK_MASK, + RT1011_SPK_MODE); + } + } + + return 0; +} + +static bool rt1011_validate_bq_drc_coeff(unsigned short reg) +{ + if ((reg == RT1011_DAC_SET_1) || + (reg >= RT1011_ADC_SET && reg <= RT1011_ADC_SET_1) || + (reg == RT1011_ADC_SET_4) || (reg == RT1011_ADC_SET_5) || + (reg == RT1011_MIXER_1) || + (reg == RT1011_A_TIMING_1) || + (reg >= RT1011_POWER_7 && reg <= RT1011_POWER_8) || + (reg == RT1011_CLASS_D_POS) || (reg == RT1011_ANALOG_CTRL) || + (reg >= RT1011_SPK_TEMP_PROTECT_0 && reg <= RT1011_SPK_TEMP_PROTECT_6) || + (reg >= RT1011_SPK_PRO_DC_DET_5 && reg <= RT1011_BAT_GAIN_1) || + (reg >= RT1011_RT_DRC_CROSS && reg <= RT1011_RT_DRC_POS_8) || + (reg >= RT1011_CROSS_BQ_SET_1 && reg <= RT1011_BQ_10_A2_15_0) || + (reg >= RT1011_SMART_BOOST_TIMING_1 && reg <= RT1011_SMART_BOOST_TIMING_36) || + (reg == RT1011_SINE_GEN_REG_1) || + (reg >= RT1011_STP_ALPHA_RECIPROCAL_MSB && reg <= RT1011_BQ_6_PARAMS_CHECK_5) || + (reg >= RT1011_BQ_7_PARAMS_CHECK_1 && reg <= RT1011_BQ_10_PARAMS_CHECK_5)) + return true; + + return false; +} + +static int rt1011_bq_drc_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1011_priv *rt1011 = + snd_soc_component_get_drvdata(component); + struct rt1011_bq_drc_params *bq_drc_info; + struct rt1011_bq_drc_params *params = + (struct rt1011_bq_drc_params *)ucontrol->value.integer.value; + unsigned int i, mode_idx = 0; + + if (strstr(ucontrol->id.name, "AdvanceMode Initial Set")) + mode_idx = RT1011_ADVMODE_INITIAL_SET; + else if (strstr(ucontrol->id.name, "AdvanceMode SEP BQ Coeff")) + mode_idx = RT1011_ADVMODE_SEP_BQ_COEFF; + else if (strstr(ucontrol->id.name, "AdvanceMode EQ BQ Coeff")) + mode_idx = RT1011_ADVMODE_EQ_BQ_COEFF; + else if (strstr(ucontrol->id.name, "AdvanceMode BQ UI Coeff")) + mode_idx = RT1011_ADVMODE_BQ_UI_COEFF; + else if (strstr(ucontrol->id.name, "AdvanceMode SmartBoost Coeff")) + mode_idx = RT1011_ADVMODE_SMARTBOOST_COEFF; + else + return -EINVAL; + + pr_info("%s, id.name=%s, mode_idx=%d\n", __func__, + ucontrol->id.name, mode_idx); + bq_drc_info = rt1011->bq_drc_params[mode_idx]; + + for (i = 0; i < RT1011_BQ_DRC_NUM; i++) { + params[i].reg = bq_drc_info[i].reg; + params[i].val = bq_drc_info[i].val; + } + + return 0; +} + +static int rt1011_bq_drc_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1011_priv *rt1011 = + snd_soc_component_get_drvdata(component); + struct rt1011_bq_drc_params *bq_drc_info; + struct rt1011_bq_drc_params *params = + (struct rt1011_bq_drc_params *)ucontrol->value.integer.value; + unsigned int i, mode_idx = 0; + + if (strstr(ucontrol->id.name, "AdvanceMode Initial Set")) + mode_idx = RT1011_ADVMODE_INITIAL_SET; + else if (strstr(ucontrol->id.name, "AdvanceMode SEP BQ Coeff")) + mode_idx = RT1011_ADVMODE_SEP_BQ_COEFF; + else if (strstr(ucontrol->id.name, "AdvanceMode EQ BQ Coeff")) + mode_idx = RT1011_ADVMODE_EQ_BQ_COEFF; + else if (strstr(ucontrol->id.name, "AdvanceMode BQ UI Coeff")) + mode_idx = RT1011_ADVMODE_BQ_UI_COEFF; + else if (strstr(ucontrol->id.name, "AdvanceMode SmartBoost Coeff")) + mode_idx = RT1011_ADVMODE_SMARTBOOST_COEFF; + else + return -EINVAL; + + bq_drc_info = rt1011->bq_drc_params[mode_idx]; + memset(bq_drc_info, 0, + sizeof(struct rt1011_bq_drc_params) * RT1011_BQ_DRC_NUM); + + pr_info("%s, id.name=%s, mode_idx=%d\n", __func__, + ucontrol->id.name, mode_idx); + for (i = 0; i < RT1011_BQ_DRC_NUM; i++) { + bq_drc_info[i].reg = params[i].reg; + bq_drc_info[i].val = params[i].val; + } + + for (i = 0; i < RT1011_BQ_DRC_NUM; i++) { + if (bq_drc_info[i].reg == 0) + break; + else if (rt1011_validate_bq_drc_coeff(bq_drc_info[i].reg)) { + snd_soc_component_write(component, bq_drc_info[i].reg, + bq_drc_info[i].val); + } + } + + return 0; +} + +static int rt1011_bq_drc_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 128; + uinfo->value.integer.max = 0x17ffffff; + + return 0; +} + +#define RT1011_BQ_DRC(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = rt1011_bq_drc_info, \ + .get = rt1011_bq_drc_coeff_get, \ + .put = rt1011_bq_drc_coeff_put \ +} + +static int rt1011_r0_cali_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1011->cali_done; + + return 0; +} + +static int rt1011_r0_cali_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + + rt1011->cali_done = 0; + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF && + ucontrol->value.integer.value[0]) + rt1011_calibrate(rt1011, 1); + + return 0; +} + +static int rt1011_r0_load(struct rt1011_priv *rt1011) +{ + if (!rt1011->r0_reg) + return -EINVAL; + + /* write R0 to register */ + regmap_write(rt1011->regmap, RT1011_INIT_RECIPROCAL_REG_24_16, + ((rt1011->r0_reg>>16) & 0x1ff)); + regmap_write(rt1011->regmap, RT1011_INIT_RECIPROCAL_REG_15_0, + (rt1011->r0_reg & 0xffff)); + regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_4, 0x4080); + + return 0; +} + +static int rt1011_r0_load_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1011->r0_reg; + + return 0; +} + +static int rt1011_r0_load_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + struct device *dev; + unsigned int r0_integer, r0_factor, format; + + if (ucontrol->value.integer.value[0] == rt1011->r0_reg) + return 0; + + if (ucontrol->value.integer.value[0] == 0) + return -EINVAL; + + dev = regmap_get_device(rt1011->regmap); + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + rt1011->r0_reg = ucontrol->value.integer.value[0]; + + format = 2147483648U; /* 2^24 * 128 */ + r0_integer = format / rt1011->r0_reg / 128; + r0_factor = ((format / rt1011->r0_reg * 100) / 128) + - (r0_integer * 100); + dev_info(dev, "New r0 resistance about %d.%02d ohm, reg=0x%X\n", + r0_integer, r0_factor, rt1011->r0_reg); + + if (rt1011->r0_reg) + rt1011_r0_load(rt1011); + } + + return 0; +} + +static int rt1011_r0_load_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.max = 0x1ffffff; + + return 0; +} + +#define RT1011_R0_LOAD(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = rt1011_r0_load_info, \ + .get = rt1011_r0_load_mode_get, \ + .put = rt1011_r0_load_mode_put \ +} + +static const char * const rt1011_i2s_ref[] = { + "None", "Left Channel", "Right Channel" +}; + +static SOC_ENUM_SINGLE_DECL(rt1011_i2s_ref_enum, 0, 0, + rt1011_i2s_ref); + +static int rt1011_i2s_ref_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1011_priv *rt1011 = + snd_soc_component_get_drvdata(component); + + rt1011->i2s_ref = ucontrol->value.enumerated.item[0]; + switch (rt1011->i2s_ref) { + case RT1011_I2S_REF_LEFT_CH: + regmap_write(rt1011->regmap, RT1011_TDM_TOTAL_SET, 0x0240); + regmap_write(rt1011->regmap, RT1011_TDM1_SET_2, 0x8); + regmap_write(rt1011->regmap, RT1011_TDM1_SET_1, 0x1022); + regmap_write(rt1011->regmap, RT1011_ADCDAT_OUT_SOURCE, 0x4); + break; + case RT1011_I2S_REF_RIGHT_CH: + regmap_write(rt1011->regmap, RT1011_TDM_TOTAL_SET, 0x0240); + regmap_write(rt1011->regmap, RT1011_TDM1_SET_2, 0x8); + regmap_write(rt1011->regmap, RT1011_TDM1_SET_1, 0x10a2); + regmap_write(rt1011->regmap, RT1011_ADCDAT_OUT_SOURCE, 0x4); + break; + default: + dev_info(component->dev, "I2S Reference: Do nothing\n"); + } + + return 0; +} + +static int rt1011_i2s_ref_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1011_priv *rt1011 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = rt1011->i2s_ref; + + return 0; +} + +static const struct snd_kcontrol_new rt1011_snd_controls[] = { + /* I2S Data In Selection */ + SOC_ENUM("DIN Source", rt1011_din_source_enum), + + /* TDM Data In Selection */ + SOC_ENUM("TDM1 DIN Source", rt1011_tdm1_l_dac1_enum), + SOC_ENUM("TDM2 DIN Source", rt1011_tdm2_l_dac1_enum), + + /* TDM1 Data Out Selection */ + SOC_ENUM("TDM1 DOUT Source", rt1011_tdm1_adc1_dat_enum), + SOC_ENUM("TDM1 DOUT Location", rt1011_tdm1_adc1_loc_enum), + SOC_ENUM("TDM1 ADC1DAT Swap Select", rt1011_tdm_adc1_1_enum), + SOC_ENUM("TDM1 ADC2DAT Swap Select", rt1011_tdm_adc2_1_enum), + + /* Data Out Mode */ + SOC_ENUM("I2S ADC DOUT Mode", rt1011_adc_dout_mode_enum), + SOC_ENUM("TDM1 DOUT Length", rt1011_tdm1_dout_len_enum), + SOC_ENUM("TDM2 DOUT Length", rt1011_tdm2_dout_len_enum), + + /* Speaker/Receiver Mode */ + SOC_SINGLE_EXT("RECV SPK Mode", SND_SOC_NOPM, 0, 1, 0, + rt1011_recv_spk_mode_get, rt1011_recv_spk_mode_put), + + /* BiQuad/DRC/SmartBoost Settings */ + RT1011_BQ_DRC("AdvanceMode Initial Set"), + RT1011_BQ_DRC("AdvanceMode SEP BQ Coeff"), + RT1011_BQ_DRC("AdvanceMode EQ BQ Coeff"), + RT1011_BQ_DRC("AdvanceMode BQ UI Coeff"), + RT1011_BQ_DRC("AdvanceMode SmartBoost Coeff"), + + /* R0 */ + SOC_SINGLE_EXT("R0 Calibration", SND_SOC_NOPM, 0, 1, 0, + rt1011_r0_cali_get, rt1011_r0_cali_put), + RT1011_R0_LOAD("R0 Load Mode"), + + /* R0 temperature */ + SOC_SINGLE("R0 Temperature", RT1011_STP_INITIAL_RESISTANCE_TEMP, + 2, 255, 0), + /* I2S Reference */ + SOC_ENUM_EXT("I2S Reference", rt1011_i2s_ref_enum, + rt1011_i2s_ref_get, rt1011_i2s_ref_put), +}; + +static int rt1011_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + + if (rt1011->sysclk_src == RT1011_FS_SYS_PRE_S_PLL1) + return 1; + else + return 0; +} + +static int rt1011_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, + RT1011_SPK_TEMP_PROTECT_0, + RT1011_STP_EN_MASK | RT1011_STP_RS_CLB_EN_MASK, + RT1011_STP_EN | RT1011_STP_RS_CLB_EN); + snd_soc_component_update_bits(component, RT1011_POWER_9, + RT1011_POW_MNL_SDB_MASK, RT1011_POW_MNL_SDB); + msleep(50); + snd_soc_component_update_bits(component, + RT1011_CLASSD_INTERNAL_SET_1, + RT1011_DRIVER_READY_SPK, RT1011_DRIVER_READY_SPK); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT1011_POWER_9, + RT1011_POW_MNL_SDB_MASK, 0); + snd_soc_component_update_bits(component, + RT1011_SPK_TEMP_PROTECT_0, + RT1011_STP_EN_MASK | RT1011_STP_RS_CLB_EN_MASK, 0); + msleep(200); + snd_soc_component_update_bits(component, + RT1011_CLASSD_INTERNAL_SET_1, + RT1011_DRIVER_READY_SPK, 0); + break; + + default: + return 0; + } + + return 0; +} + + +static const struct snd_soc_dapm_widget rt1011_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT1011_POWER_1, + RT1011_POW_LDO2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ISENSE SPK", RT1011_POWER_1, + RT1011_POW_ISENSE_SPK_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VSENSE SPK", RT1011_POWER_1, + RT1011_POW_VSENSE_SPK_BIT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PLL", RT1011_POWER_2, + RT1011_PLLEN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG", RT1011_POWER_2, + RT1011_POW_BG_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG MBIAS", RT1011_POWER_2, + RT1011_POW_BG_MBIAS_LV_BIT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DET VBAT", RT1011_POWER_3, + RT1011_POW_DET_VBAT_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS", RT1011_POWER_3, + RT1011_POW_MBIAS_LV_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC I", RT1011_POWER_3, + RT1011_POW_ADC_I_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC V", RT1011_POWER_3, + RT1011_POW_ADC_V_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC T", RT1011_POWER_3, + RT1011_POW_ADC_T_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DITHER ADC T", RT1011_POWER_3, + RT1011_POWD_ADC_T_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIX I", RT1011_POWER_3, + RT1011_POW_MIX_I_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIX V", RT1011_POWER_3, + RT1011_POW_MIX_V_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SUM I", RT1011_POWER_3, + RT1011_POW_SUM_I_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SUM V", RT1011_POWER_3, + RT1011_POW_SUM_V_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIX T", RT1011_POWER_3, + RT1011_POW_MIX_T_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", RT1011_POWER_3, + RT1011_POW_VREF_LV_BIT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("BOOST SWR", RT1011_POWER_4, + RT1011_POW_EN_SWR_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BGOK SWR", RT1011_POWER_4, + RT1011_POW_EN_PASS_BGOK_SWR_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VPOK SWR", RT1011_POWER_4, + RT1011_POW_EN_PASS_VPOK_SWR_BIT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("TEMP REG", RT1011_A_TEMP_SEN, + RT1011_POW_TEMP_REG_BIT, 0, NULL, 0), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("DAC Power", RT1011_POWER_1, + RT1011_POW_DAC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK12M", RT1011_POWER_1, + RT1011_POW_CLK12M_BIT, 0, NULL, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, RT1011_DAC_SET_3, + RT1011_DA_MUTE_EN_SFT, 1, rt1011_dac_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("SPO"), +}; + +static const struct snd_soc_dapm_route rt1011_dapm_routes[] = { + + { "DAC", NULL, "AIF1RX" }, + { "DAC", NULL, "DAC Power" }, + { "DAC", NULL, "LDO2" }, + { "DAC", NULL, "ISENSE SPK" }, + { "DAC", NULL, "VSENSE SPK" }, + { "DAC", NULL, "CLK12M" }, + + { "DAC", NULL, "PLL", rt1011_is_sys_clk_from_pll }, + { "DAC", NULL, "BG" }, + { "DAC", NULL, "BG MBIAS" }, + + { "DAC", NULL, "BOOST SWR" }, + { "DAC", NULL, "BGOK SWR" }, + { "DAC", NULL, "VPOK SWR" }, + + { "DAC", NULL, "DET VBAT" }, + { "DAC", NULL, "MBIAS" }, + { "DAC", NULL, "VREF" }, + { "DAC", NULL, "ADC I" }, + { "DAC", NULL, "ADC V" }, + { "DAC", NULL, "ADC T" }, + { "DAC", NULL, "DITHER ADC T" }, + { "DAC", NULL, "MIX I" }, + { "DAC", NULL, "MIX V" }, + { "DAC", NULL, "SUM I" }, + { "DAC", NULL, "SUM V" }, + { "DAC", NULL, "MIX T" }, + + { "DAC", NULL, "TEMP REG" }, + + { "SPO", NULL, "DAC" }, +}; + +static int rt1011_get_clk_info(int sclk, int rate) +{ + int i; + static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +static int rt1011_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, ch_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt1011->lrck = params_rate(params); + pre_div = rt1011_get_clk_info(rt1011->sysclk, rt1011->lrck); + if (pre_div < 0) { + dev_warn(component->dev, "Force using PLL "); + snd_soc_dai_set_pll(dai, 0, RT1011_PLL1_S_BCLK, + rt1011->lrck * 64, rt1011->lrck * 256); + snd_soc_dai_set_sysclk(dai, RT1011_FS_SYS_PRE_S_PLL1, + rt1011->lrck * 256, SND_SOC_CLOCK_IN); + pre_div = 0; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + bclk_ms = frame_size > 32; + rt1011->bclk = rt1011->lrck * (32 << bclk_ms); + + dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt1011->lrck, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + val_len |= RT1011_I2S_TX_DL_16B; + val_len |= RT1011_I2S_RX_DL_16B; + ch_len |= RT1011_I2S_CH_TX_LEN_16B; + ch_len |= RT1011_I2S_CH_RX_LEN_16B; + break; + case 20: + val_len |= RT1011_I2S_TX_DL_20B; + val_len |= RT1011_I2S_RX_DL_20B; + ch_len |= RT1011_I2S_CH_TX_LEN_20B; + ch_len |= RT1011_I2S_CH_RX_LEN_20B; + break; + case 24: + val_len |= RT1011_I2S_TX_DL_24B; + val_len |= RT1011_I2S_RX_DL_24B; + ch_len |= RT1011_I2S_CH_TX_LEN_24B; + ch_len |= RT1011_I2S_CH_RX_LEN_24B; + break; + case 32: + val_len |= RT1011_I2S_TX_DL_32B; + val_len |= RT1011_I2S_RX_DL_32B; + ch_len |= RT1011_I2S_CH_TX_LEN_32B; + ch_len |= RT1011_I2S_CH_RX_LEN_32B; + break; + case 8: + val_len |= RT1011_I2S_TX_DL_8B; + val_len |= RT1011_I2S_RX_DL_8B; + ch_len |= RT1011_I2S_CH_TX_LEN_8B; + ch_len |= RT1011_I2S_CH_RX_LEN_8B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT1011_AIF1: + mask_clk = RT1011_FS_SYS_DIV_MASK; + val_clk = pre_div << RT1011_FS_SYS_DIV_SFT; + snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET, + RT1011_I2S_TX_DL_MASK | RT1011_I2S_RX_DL_MASK, + val_len); + snd_soc_component_update_bits(component, RT1011_TDM1_SET_1, + RT1011_I2S_CH_TX_LEN_MASK | + RT1011_I2S_CH_RX_LEN_MASK, + ch_len); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, + RT1011_CLK_2, mask_clk, val_clk); + + return 0; +} + +static int rt1011_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + unsigned int reg_val = 0, reg_bclk_inv = 0; + int ret = 0; + + snd_soc_dapm_mutex_lock(dapm); + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT1011_I2S_TDM_MS_S; + break; + default: + ret = -EINVAL; + goto _set_fmt_err_; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_bclk_inv |= RT1011_TDM_INV_BCLK; + break; + default: + ret = -EINVAL; + goto _set_fmt_err_; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT1011_I2S_TDM_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT1011_I2S_TDM_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT1011_I2S_TDM_DF_PCM_B; + break; + default: + ret = -EINVAL; + goto _set_fmt_err_; + } + + switch (dai->id) { + case RT1011_AIF1: + snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET, + RT1011_I2S_TDM_MS_MASK | RT1011_I2S_TDM_DF_MASK, + reg_val); + snd_soc_component_update_bits(component, RT1011_TDM1_SET_1, + RT1011_TDM_INV_BCLK_MASK, reg_bclk_inv); + snd_soc_component_update_bits(component, RT1011_TDM2_SET_1, + RT1011_TDM_INV_BCLK_MASK, reg_bclk_inv); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + ret = -EINVAL; + } + +_set_fmt_err_: + snd_soc_dapm_mutex_unlock(dapm); + return ret; +} + +static int rt1011_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt1011->sysclk && clk_id == rt1011->sysclk_src) + return 0; + + /* disable MCLK detect in default */ + snd_soc_component_update_bits(component, RT1011_CLK_DET, + RT1011_EN_MCLK_DET_MASK, 0); + + switch (clk_id) { + case RT1011_FS_SYS_PRE_S_MCLK: + reg_val |= RT1011_FS_SYS_PRE_MCLK; + snd_soc_component_update_bits(component, RT1011_CLK_DET, + RT1011_EN_MCLK_DET_MASK, RT1011_EN_MCLK_DET); + break; + case RT1011_FS_SYS_PRE_S_BCLK: + reg_val |= RT1011_FS_SYS_PRE_BCLK; + break; + case RT1011_FS_SYS_PRE_S_PLL1: + reg_val |= RT1011_FS_SYS_PRE_PLL1; + break; + case RT1011_FS_SYS_PRE_S_RCCLK: + reg_val |= RT1011_FS_SYS_PRE_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT1011_CLK_2, + RT1011_FS_SYS_PRE_MASK, reg_val); + rt1011->sysclk = freq; + rt1011->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt1011_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt1011->pll_src && freq_in == rt1011->pll_in && + freq_out == rt1011->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt1011->pll_in = 0; + rt1011->pll_out = 0; + snd_soc_component_update_bits(component, RT1011_CLK_2, + RT1011_FS_SYS_PRE_MASK, RT1011_FS_SYS_PRE_BCLK); + return 0; + } + + switch (source) { + case RT1011_PLL2_S_MCLK: + snd_soc_component_update_bits(component, RT1011_CLK_2, + RT1011_PLL2_SRC_MASK, RT1011_PLL2_SRC_MCLK); + snd_soc_component_update_bits(component, RT1011_CLK_2, + RT1011_PLL1_SRC_MASK, RT1011_PLL1_SRC_PLL2); + snd_soc_component_update_bits(component, RT1011_CLK_DET, + RT1011_EN_MCLK_DET_MASK, RT1011_EN_MCLK_DET); + break; + case RT1011_PLL1_S_BCLK: + snd_soc_component_update_bits(component, RT1011_CLK_2, + RT1011_PLL1_SRC_MASK, RT1011_PLL1_SRC_BCLK); + break; + case RT1011_PLL2_S_RCCLK: + snd_soc_component_update_bits(component, RT1011_CLK_2, + RT1011_PLL2_SRC_MASK, RT1011_PLL2_SRC_RCCLK); + snd_soc_component_update_bits(component, RT1011_CLK_2, + RT1011_PLL1_SRC_MASK, RT1011_PLL1_SRC_PLL2); + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", + freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT1011_PLL_1, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1011_PLL1_QM_SFT) | + (pll_code.m_bp << RT1011_PLL1_BPM_SFT) | + pll_code.n_code); + snd_soc_component_write(component, RT1011_PLL_2, + pll_code.k_code); + + rt1011->pll_in = freq_in; + rt1011->pll_out = freq_out; + rt1011->pll_src = source; + + return 0; +} + +static int rt1011_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + unsigned int val = 0, tdm_en = 0, rx_slotnum, tx_slotnum; + int ret = 0, first_bit, last_bit; + + snd_soc_dapm_mutex_lock(dapm); + if (rx_mask || tx_mask) + tdm_en = RT1011_TDM_I2S_DOCK_EN_1; + + switch (slots) { + case 4: + val |= RT1011_I2S_TX_4CH; + val |= RT1011_I2S_RX_4CH; + break; + case 6: + val |= RT1011_I2S_TX_6CH; + val |= RT1011_I2S_RX_6CH; + break; + case 8: + val |= RT1011_I2S_TX_8CH; + val |= RT1011_I2S_RX_8CH; + break; + case 2: + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + switch (slot_width) { + case 20: + val |= RT1011_I2S_CH_TX_LEN_20B; + val |= RT1011_I2S_CH_RX_LEN_20B; + break; + case 24: + val |= RT1011_I2S_CH_TX_LEN_24B; + val |= RT1011_I2S_CH_RX_LEN_24B; + break; + case 32: + val |= RT1011_I2S_CH_TX_LEN_32B; + val |= RT1011_I2S_CH_RX_LEN_32B; + break; + case 16: + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + /* Rx slot configuration */ + rx_slotnum = hweight_long(rx_mask); + if (rx_slotnum > 1 || !rx_slotnum) { + ret = -EINVAL; + dev_err(component->dev, "too many rx slots or zero slot\n"); + goto _set_tdm_err_; + } + + first_bit = __ffs(rx_mask); + switch (first_bit) { + case 0: + case 2: + case 4: + case 6: + snd_soc_component_update_bits(component, + RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK, + RT1011_MONO_L_CHANNEL); + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_4, + RT1011_TDM_I2S_TX_L_DAC1_1_MASK | + RT1011_TDM_I2S_TX_R_DAC1_1_MASK, + (first_bit << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) | + ((first_bit+1) << RT1011_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + case 1: + case 3: + case 5: + case 7: + snd_soc_component_update_bits(component, + RT1011_CROSS_BQ_SET_1, RT1011_MONO_LR_SEL_MASK, + RT1011_MONO_R_CHANNEL); + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_4, + RT1011_TDM_I2S_TX_L_DAC1_1_MASK | + RT1011_TDM_I2S_TX_R_DAC1_1_MASK, + ((first_bit-1) << RT1011_TDM_I2S_TX_L_DAC1_1_SFT) | + (first_bit << RT1011_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + /* Tx slot configuration */ + tx_slotnum = hweight_long(tx_mask); + if (tx_slotnum > 2 || !tx_slotnum) { + ret = -EINVAL; + dev_err(component->dev, "too many tx slots or zero slot\n"); + goto _set_tdm_err_; + } + + first_bit = __ffs(tx_mask); + last_bit = __fls(tx_mask); + if (last_bit - first_bit > 1) { + ret = -EINVAL; + dev_err(component->dev, "tx slot location error\n"); + goto _set_tdm_err_; + } + + if (tx_slotnum == 1) { + snd_soc_component_update_bits(component, RT1011_TDM1_SET_2, + RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK | + RT1011_TDM_ADCDAT1_DATA_LOCATION, first_bit); + switch (first_bit) { + case 1: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC1_1_MASK, + RT1011_TDM_I2S_RX_ADC1_1_LL); + break; + case 3: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC2_1_MASK, + RT1011_TDM_I2S_RX_ADC2_1_LL); + break; + case 5: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC3_1_MASK, + RT1011_TDM_I2S_RX_ADC3_1_LL); + break; + case 7: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC4_1_MASK, + RT1011_TDM_I2S_RX_ADC4_1_LL); + break; + case 0: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC1_1_MASK, 0); + break; + case 2: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC2_1_MASK, 0); + break; + case 4: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC3_1_MASK, 0); + break; + case 6: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_3, + RT1011_TDM_I2S_RX_ADC4_1_MASK, 0); + break; + default: + ret = -EINVAL; + dev_dbg(component->dev, + "tx slot location error\n"); + goto _set_tdm_err_; + } + } else if (tx_slotnum == 2) { + switch (first_bit) { + case 0: + case 2: + case 4: + case 6: + snd_soc_component_update_bits(component, + RT1011_TDM1_SET_2, + RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK | + RT1011_TDM_ADCDAT1_DATA_LOCATION, + RT1011_TDM_I2S_DOCK_ADCDAT_2CH | first_bit); + break; + default: + ret = -EINVAL; + dev_dbg(component->dev, + "tx slot location should be paired and start from slot0/2/4/6\n"); + goto _set_tdm_err_; + } + } + + snd_soc_component_update_bits(component, RT1011_TDM1_SET_1, + RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK | + RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val); + snd_soc_component_update_bits(component, RT1011_TDM2_SET_1, + RT1011_I2S_CH_TX_MASK | RT1011_I2S_CH_RX_MASK | + RT1011_I2S_CH_TX_LEN_MASK | RT1011_I2S_CH_RX_LEN_MASK, val); + snd_soc_component_update_bits(component, RT1011_TDM1_SET_2, + RT1011_TDM_I2S_DOCK_EN_1_MASK, tdm_en); + snd_soc_component_update_bits(component, RT1011_TDM2_SET_2, + RT1011_TDM_I2S_DOCK_EN_2_MASK, tdm_en); + + snd_soc_component_update_bits(component, RT1011_TDM_TOTAL_SET, + RT1011_ADCDAT1_PIN_CONFIG | RT1011_ADCDAT2_PIN_CONFIG, + RT1011_ADCDAT1_OUTPUT | RT1011_ADCDAT2_OUTPUT); + +_set_tdm_err_: + snd_soc_dapm_mutex_unlock(dapm); + return ret; +} + +static int rt1011_probe(struct snd_soc_component *component) +{ + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + int i; + + rt1011->component = component; + + schedule_work(&rt1011->cali_work); + + rt1011->i2s_ref = 0; + rt1011->bq_drc_params = devm_kcalloc(component->dev, + RT1011_ADVMODE_NUM, sizeof(struct rt1011_bq_drc_params *), + GFP_KERNEL); + if (!rt1011->bq_drc_params) + return -ENOMEM; + + for (i = 0; i < RT1011_ADVMODE_NUM; i++) { + rt1011->bq_drc_params[i] = devm_kcalloc(component->dev, + RT1011_BQ_DRC_NUM, sizeof(struct rt1011_bq_drc_params), + GFP_KERNEL); + if (!rt1011->bq_drc_params[i]) + return -ENOMEM; + } + + return 0; +} + +static void rt1011_remove(struct snd_soc_component *component) +{ + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + + cancel_work_sync(&rt1011->cali_work); + rt1011_reset(rt1011->regmap); +} + +#ifdef CONFIG_PM +static int rt1011_suspend(struct snd_soc_component *component) +{ + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1011->regmap, true); + regcache_mark_dirty(rt1011->regmap); + + return 0; +} + +static int rt1011_resume(struct snd_soc_component *component) +{ + struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1011->regmap, false); + regcache_sync(rt1011->regmap); + + return 0; +} +#else +#define rt1011_suspend NULL +#define rt1011_resume NULL +#endif + +static int rt1011_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, + RT1011_SYSTEM_RESET_1, 0x0000); + snd_soc_component_write(component, + RT1011_SYSTEM_RESET_2, 0x0000); + snd_soc_component_write(component, + RT1011_SYSTEM_RESET_3, 0x0001); + snd_soc_component_write(component, + RT1011_SYSTEM_RESET_1, 0x003f); + snd_soc_component_write(component, + RT1011_SYSTEM_RESET_2, 0x7fd7); + snd_soc_component_write(component, + RT1011_SYSTEM_RESET_3, 0x770f); + break; + default: + break; + } + + return 0; +} + +#define RT1011_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT1011_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops rt1011_aif_dai_ops = { + .hw_params = rt1011_hw_params, + .set_fmt = rt1011_set_dai_fmt, + .set_tdm_slot = rt1011_set_tdm_slot, +}; + +static struct snd_soc_dai_driver rt1011_dai[] = { + { + .name = "rt1011-aif", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1011_STEREO_RATES, + .formats = RT1011_FORMATS, + }, + .ops = &rt1011_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt1011 = { + .probe = rt1011_probe, + .remove = rt1011_remove, + .suspend = rt1011_suspend, + .resume = rt1011_resume, + .set_bias_level = rt1011_set_bias_level, + .controls = rt1011_snd_controls, + .num_controls = ARRAY_SIZE(rt1011_snd_controls), + .dapm_widgets = rt1011_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1011_dapm_widgets), + .dapm_routes = rt1011_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1011_dapm_routes), + .set_sysclk = rt1011_set_component_sysclk, + .set_pll = rt1011_set_component_pll, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt1011_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT1011_MAX_REG + 1, + .volatile_reg = rt1011_volatile_register, + .readable_reg = rt1011_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt1011_reg, + .num_reg_defaults = ARRAY_SIZE(rt1011_reg), + .use_single_read = true, + .use_single_write = true, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id rt1011_of_match[] = { + { .compatible = "realtek,rt1011", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1011_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt1011_acpi_match[] = { + {"10EC1011", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt1011_acpi_match); +#endif + +static const struct i2c_device_id rt1011_i2c_id[] = { + { "rt1011", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1011_i2c_id); + +static int rt1011_calibrate(struct rt1011_priv *rt1011, unsigned char cali_flag) +{ + unsigned int value, count = 0, r0[3]; + unsigned int chk_cnt = 50; /* DONT change this */ + unsigned int dc_offset; + unsigned int r0_integer, r0_factor, format; + struct device *dev = regmap_get_device(rt1011->regmap); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(rt1011->component); + int ret = 0; + + snd_soc_dapm_mutex_lock(dapm); + regcache_cache_bypass(rt1011->regmap, true); + + regmap_write(rt1011->regmap, RT1011_RESET, 0x0000); + regmap_write(rt1011->regmap, RT1011_SYSTEM_RESET_3, 0x740f); + regmap_write(rt1011->regmap, RT1011_SYSTEM_RESET_3, 0x770f); + + /* RC clock */ + regmap_write(rt1011->regmap, RT1011_CLK_2, 0x9400); + regmap_write(rt1011->regmap, RT1011_PLL_1, 0x0800); + regmap_write(rt1011->regmap, RT1011_PLL_2, 0x0020); + regmap_write(rt1011->regmap, RT1011_CLK_DET, 0x0800); + + /* ADC/DAC setting */ + regmap_write(rt1011->regmap, RT1011_ADC_SET_5, 0x0a20); + regmap_write(rt1011->regmap, RT1011_DAC_SET_2, 0xe232); + regmap_write(rt1011->regmap, RT1011_ADC_SET_4, 0xc000); + + /* DC detection */ + regmap_write(rt1011->regmap, RT1011_SPK_PRO_DC_DET_1, 0xb00c); + regmap_write(rt1011->regmap, RT1011_SPK_PRO_DC_DET_2, 0xcccc); + + /* Power */ + regmap_write(rt1011->regmap, RT1011_POWER_1, 0xe0e0); + regmap_write(rt1011->regmap, RT1011_POWER_3, 0x5003); + regmap_write(rt1011->regmap, RT1011_POWER_9, 0xa860); + regmap_write(rt1011->regmap, RT1011_DAC_SET_2, 0xa032); + + /* POW_PLL / POW_BG / POW_BG_MBIAS_LV / POW_V/I */ + regmap_write(rt1011->regmap, RT1011_POWER_2, 0x0007); + regmap_write(rt1011->regmap, RT1011_POWER_3, 0x5ff7); + regmap_write(rt1011->regmap, RT1011_A_TEMP_SEN, 0x7f44); + regmap_write(rt1011->regmap, RT1011_A_TIMING_1, 0x4054); + regmap_write(rt1011->regmap, RT1011_BAT_GAIN_1, 0x309c); + + /* DC offset from EFUSE */ + regmap_write(rt1011->regmap, RT1011_DC_CALIB_CLASSD_3, 0xcb00); + regmap_write(rt1011->regmap, RT1011_BOOST_CON_1, 0xe080); + regmap_write(rt1011->regmap, RT1011_POWER_4, 0x16f2); + regmap_write(rt1011->regmap, RT1011_POWER_6, 0x36ad); + + /* mixer */ + regmap_write(rt1011->regmap, RT1011_MIXER_1, 0x3f1d); + + /* EFUSE read */ + regmap_write(rt1011->regmap, RT1011_EFUSE_CONTROL_1, 0x0d0a); + msleep(30); + + regmap_read(rt1011->regmap, RT1011_EFUSE_ADC_OFFSET_18_16, &value); + dc_offset = value << 16; + regmap_read(rt1011->regmap, RT1011_EFUSE_ADC_OFFSET_15_0, &value); + dc_offset |= (value & 0xffff); + dev_info(dev, "ADC offset=0x%x\n", dc_offset); + regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_20_16, &value); + dc_offset = value << 16; + regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G0_15_0, &value); + dc_offset |= (value & 0xffff); + dev_info(dev, "Gain0 offset=0x%x\n", dc_offset); + regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_20_16, &value); + dc_offset = value << 16; + regmap_read(rt1011->regmap, RT1011_EFUSE_DAC_OFFSET_G1_15_0, &value); + dc_offset |= (value & 0xffff); + dev_info(dev, "Gain1 offset=0x%x\n", dc_offset); + + if (cali_flag) { + + regmap_write(rt1011->regmap, RT1011_ADC_SET_1, 0x2925); + /* Class D on */ + regmap_write(rt1011->regmap, RT1011_CLASS_D_POS, 0x010e); + regmap_write(rt1011->regmap, + RT1011_CLASSD_INTERNAL_SET_1, 0x1701); + + /* STP enable */ + regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_0, 0x8000); + regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_7, 0xf000); + regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_4, 0x4040); + regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_0, 0xc000); + regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_6, 0x07c2); + + r0[0] = r0[1] = r0[2] = count = 0; + while (count < chk_cnt) { + msleep(100); + regmap_read(rt1011->regmap, + RT1011_INIT_RECIPROCAL_SYN_24_16, &value); + r0[count%3] = value << 16; + regmap_read(rt1011->regmap, + RT1011_INIT_RECIPROCAL_SYN_15_0, &value); + r0[count%3] |= value; + + if (r0[count%3] == 0) + continue; + + count++; + + if (r0[0] == r0[1] && r0[1] == r0[2]) + break; + } + if (count > chk_cnt) { + dev_err(dev, "Calibrate R0 Failure\n"); + ret = -EAGAIN; + } else { + format = 2147483648U; /* 2^24 * 128 */ + r0_integer = format / r0[0] / 128; + r0_factor = ((format / r0[0] * 100) / 128) + - (r0_integer * 100); + rt1011->r0_reg = r0[0]; + rt1011->cali_done = 1; + dev_info(dev, "r0 resistance about %d.%02d ohm, reg=0x%X\n", + r0_integer, r0_factor, r0[0]); + } + } + + /* depop */ + regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_0, 0x0000); + msleep(400); + regmap_write(rt1011->regmap, RT1011_POWER_9, 0xa840); + regmap_write(rt1011->regmap, RT1011_SPK_TEMP_PROTECT_6, 0x0702); + regmap_write(rt1011->regmap, RT1011_MIXER_1, 0xffdd); + regmap_write(rt1011->regmap, RT1011_CLASSD_INTERNAL_SET_1, 0x0701); + regmap_write(rt1011->regmap, RT1011_DAC_SET_3, 0xe004); + regmap_write(rt1011->regmap, RT1011_A_TEMP_SEN, 0x7f40); + regmap_write(rt1011->regmap, RT1011_POWER_1, 0x0000); + regmap_write(rt1011->regmap, RT1011_POWER_2, 0x0000); + regmap_write(rt1011->regmap, RT1011_POWER_3, 0x0002); + regmap_write(rt1011->regmap, RT1011_POWER_4, 0x00f2); + + regmap_write(rt1011->regmap, RT1011_RESET, 0x0000); + + if (cali_flag) { + if (count <= chk_cnt) { + regmap_write(rt1011->regmap, + RT1011_INIT_RECIPROCAL_REG_24_16, + ((r0[0]>>16) & 0x1ff)); + regmap_write(rt1011->regmap, + RT1011_INIT_RECIPROCAL_REG_15_0, + (r0[0] & 0xffff)); + regmap_write(rt1011->regmap, + RT1011_SPK_TEMP_PROTECT_4, 0x4080); + } + } + + regcache_cache_bypass(rt1011->regmap, false); + regcache_mark_dirty(rt1011->regmap); + regcache_sync(rt1011->regmap); + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static void rt1011_calibration_work(struct work_struct *work) +{ + struct rt1011_priv *rt1011 = + container_of(work, struct rt1011_priv, cali_work); + struct snd_soc_component *component = rt1011->component; + unsigned int r0_integer, r0_factor, format; + + if (rt1011->r0_calib) + rt1011_calibrate(rt1011, 0); + else + rt1011_calibrate(rt1011, 1); + + /* + * This flag should reset after booting. + * The factory test will do calibration again and use this flag to check + * whether the calibration completed + */ + rt1011->cali_done = 0; + + /* initial */ + rt1011_reg_init(component); + + /* Apply temperature and calibration data from device property */ + if (rt1011->temperature_calib <= 0xff && + rt1011->temperature_calib > 0) { + snd_soc_component_update_bits(component, + RT1011_STP_INITIAL_RESISTANCE_TEMP, 0x3ff, + (rt1011->temperature_calib << 2)); + } + + if (rt1011->r0_calib) { + rt1011->r0_reg = rt1011->r0_calib; + + format = 2147483648U; /* 2^24 * 128 */ + r0_integer = format / rt1011->r0_reg / 128; + r0_factor = ((format / rt1011->r0_reg * 100) / 128) + - (r0_integer * 100); + dev_info(component->dev, "DP r0 resistance about %d.%02d ohm, reg=0x%X\n", + r0_integer, r0_factor, rt1011->r0_reg); + + rt1011_r0_load(rt1011); + } + + snd_soc_component_write(component, RT1011_ADC_SET_1, 0x2925); +} + +static int rt1011_parse_dp(struct rt1011_priv *rt1011, struct device *dev) +{ + device_property_read_u32(dev, "realtek,temperature_calib", + &rt1011->temperature_calib); + device_property_read_u32(dev, "realtek,r0_calib", + &rt1011->r0_calib); + + dev_dbg(dev, "%s: r0_calib: 0x%x, temperature_calib: 0x%x", + __func__, rt1011->r0_calib, rt1011->temperature_calib); + + return 0; +} + +static int rt1011_i2c_probe(struct i2c_client *i2c) +{ + struct rt1011_priv *rt1011; + int ret; + unsigned int val; + + rt1011 = devm_kzalloc(&i2c->dev, sizeof(struct rt1011_priv), + GFP_KERNEL); + if (!rt1011) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1011); + + rt1011_parse_dp(rt1011, &i2c->dev); + + rt1011->regmap = devm_regmap_init_i2c(i2c, &rt1011_regmap); + if (IS_ERR(rt1011->regmap)) { + ret = PTR_ERR(rt1011->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt1011->regmap, RT1011_DEVICE_ID, &val); + if (val != RT1011_DEVICE_ID_NUM) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt1011\n", val); + return -ENODEV; + } + + INIT_WORK(&rt1011->cali_work, rt1011_calibration_work); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1011, + rt1011_dai, ARRAY_SIZE(rt1011_dai)); + +} + +static void rt1011_i2c_shutdown(struct i2c_client *client) +{ + struct rt1011_priv *rt1011 = i2c_get_clientdata(client); + + rt1011_reset(rt1011->regmap); +} + +static struct i2c_driver rt1011_i2c_driver = { + .driver = { + .name = "rt1011", + .of_match_table = of_match_ptr(rt1011_of_match), + .acpi_match_table = ACPI_PTR(rt1011_acpi_match) + }, + .probe = rt1011_i2c_probe, + .shutdown = rt1011_i2c_shutdown, + .id_table = rt1011_i2c_id, +}; +module_i2c_driver(rt1011_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1011 amplifier driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1011.h b/sound/soc/codecs/rt1011.h new file mode 100644 index 0000000000..4d6e7492d9 --- /dev/null +++ b/sound/soc/codecs/rt1011.h @@ -0,0 +1,704 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt1011.h -- RT1011 ALSA SoC amplifier component driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef _RT1011_H_ +#define _RT1011_H_ + +#define RT1011_DEVICE_ID_NUM 0x1011 + +#define RT1011_RESET 0x0000 +#define RT1011_CLK_1 0x0002 +#define RT1011_CLK_2 0x0004 +#define RT1011_CLK_3 0x0006 +#define RT1011_CLK_4 0x0008 +#define RT1011_PLL_1 0x000a +#define RT1011_PLL_2 0x000c +#define RT1011_SRC_1 0x000e +#define RT1011_SRC_2 0x0010 +#define RT1011_SRC_3 0x0012 +#define RT1011_CLK_DET 0x0020 +#define RT1011_SIL_DET 0x0022 +#define RT1011_PRIV_INDEX 0x006a +#define RT1011_PRIV_DATA 0x006c +#define RT1011_CUSTOMER_ID 0x0076 +#define RT1011_FM_VER 0x0078 +#define RT1011_VERSION_ID 0x007a +#define RT1011_VENDOR_ID 0x007c +#define RT1011_DEVICE_ID 0x007d +#define RT1011_DUM_RW_0 0x00f0 +#define RT1011_DUM_YUN 0x00f2 +#define RT1011_DUM_RW_1 0x00f3 +#define RT1011_DUM_RO 0x00f4 +#define RT1011_MAN_I2C_DEV 0x0100 +#define RT1011_DAC_SET_1 0x0102 +#define RT1011_DAC_SET_2 0x0104 +#define RT1011_DAC_SET_3 0x0106 +#define RT1011_ADC_SET 0x0107 +#define RT1011_ADC_SET_1 0x0108 +#define RT1011_ADC_SET_2 0x010a +#define RT1011_ADC_SET_3 0x010c +#define RT1011_ADC_SET_4 0x010e +#define RT1011_ADC_SET_5 0x0110 +#define RT1011_TDM_TOTAL_SET 0x0111 +#define RT1011_TDM1_SET_TCON 0x0112 +#define RT1011_TDM1_SET_1 0x0114 +#define RT1011_TDM1_SET_2 0x0116 +#define RT1011_TDM1_SET_3 0x0118 +#define RT1011_TDM1_SET_4 0x011a +#define RT1011_TDM1_SET_5 0x011c +#define RT1011_TDM2_SET_1 0x011e +#define RT1011_TDM2_SET_2 0x0120 +#define RT1011_TDM2_SET_3 0x0122 +#define RT1011_TDM2_SET_4 0x0124 +#define RT1011_TDM2_SET_5 0x0126 +#define RT1011_PWM_CAL 0x0200 +#define RT1011_MIXER_1 0x0300 +#define RT1011_MIXER_2 0x0302 +#define RT1011_ADRC_LIMIT 0x0310 +#define RT1011_A_PRO 0x0311 +#define RT1011_A_TIMING_1 0x0313 +#define RT1011_A_TIMING_2 0x0314 +#define RT1011_A_TEMP_SEN 0x0316 +#define RT1011_SPK_VOL_DET_1 0x0319 +#define RT1011_SPK_VOL_DET_2 0x031a +#define RT1011_SPK_VOL_TEST_OUT 0x031b +#define RT1011_VBAT_VOL_DET_1 0x031c +#define RT1011_VBAT_VOL_DET_2 0x031d +#define RT1011_VBAT_TEST_OUT_1 0x031e +#define RT1011_VBAT_TEST_OUT_2 0x031f +#define RT1011_VBAT_PROTECTION 0x0320 +#define RT1011_VBAT_DET 0x0321 +#define RT1011_POWER_1 0x0322 +#define RT1011_POWER_2 0x0324 +#define RT1011_POWER_3 0x0326 +#define RT1011_POWER_4 0x0328 +#define RT1011_POWER_5 0x0329 +#define RT1011_POWER_6 0x032a +#define RT1011_POWER_7 0x032b +#define RT1011_POWER_8 0x032c +#define RT1011_POWER_9 0x032d +#define RT1011_CLASS_D_POS 0x032e +#define RT1011_BOOST_CON_1 0x0330 +#define RT1011_BOOST_CON_2 0x0332 +#define RT1011_ANALOG_CTRL 0x0334 +#define RT1011_POWER_SEQ 0x0340 +#define RT1011_SHORT_CIRCUIT_DET_1 0x0508 +#define RT1011_SHORT_CIRCUIT_DET_2 0x050a +#define RT1011_SPK_TEMP_PROTECT_0 0x050c +#define RT1011_SPK_TEMP_PROTECT_1 0x050d +#define RT1011_SPK_TEMP_PROTECT_2 0x050e +#define RT1011_SPK_TEMP_PROTECT_3 0x050f +#define RT1011_SPK_TEMP_PROTECT_4 0x0510 +#define RT1011_SPK_TEMP_PROTECT_5 0x0511 +#define RT1011_SPK_TEMP_PROTECT_6 0x0512 +#define RT1011_SPK_TEMP_PROTECT_7 0x0516 +#define RT1011_SPK_TEMP_PROTECT_8 0x0517 +#define RT1011_SPK_TEMP_PROTECT_9 0x0518 +#define RT1011_SPK_PRO_DC_DET_1 0x0519 +#define RT1011_SPK_PRO_DC_DET_2 0x051a +#define RT1011_SPK_PRO_DC_DET_3 0x051b +#define RT1011_SPK_PRO_DC_DET_4 0x051c +#define RT1011_SPK_PRO_DC_DET_5 0x051d +#define RT1011_SPK_PRO_DC_DET_6 0x051e +#define RT1011_SPK_PRO_DC_DET_7 0x051f +#define RT1011_SPK_PRO_DC_DET_8 0x0520 +#define RT1011_SPL_1 0x0521 +#define RT1011_SPL_2 0x0522 +#define RT1011_SPL_3 0x0524 +#define RT1011_SPL_4 0x0526 +#define RT1011_THER_FOLD_BACK_1 0x0528 +#define RT1011_THER_FOLD_BACK_2 0x052a +#define RT1011_EXCUR_PROTECT_1 0x0530 +#define RT1011_EXCUR_PROTECT_2 0x0532 +#define RT1011_EXCUR_PROTECT_3 0x0534 +#define RT1011_EXCUR_PROTECT_4 0x0535 +#define RT1011_BAT_GAIN_1 0x0536 +#define RT1011_BAT_GAIN_2 0x0538 +#define RT1011_BAT_GAIN_3 0x053a +#define RT1011_BAT_GAIN_4 0x053c +#define RT1011_BAT_GAIN_5 0x053d +#define RT1011_BAT_GAIN_6 0x053e +#define RT1011_BAT_GAIN_7 0x053f +#define RT1011_BAT_GAIN_8 0x0540 +#define RT1011_BAT_GAIN_9 0x0541 +#define RT1011_BAT_GAIN_10 0x0542 +#define RT1011_BAT_GAIN_11 0x0543 +#define RT1011_BAT_RT_THMAX_1 0x0544 +#define RT1011_BAT_RT_THMAX_2 0x0545 +#define RT1011_BAT_RT_THMAX_3 0x0546 +#define RT1011_BAT_RT_THMAX_4 0x0547 +#define RT1011_BAT_RT_THMAX_5 0x0548 +#define RT1011_BAT_RT_THMAX_6 0x0549 +#define RT1011_BAT_RT_THMAX_7 0x054a +#define RT1011_BAT_RT_THMAX_8 0x054b +#define RT1011_BAT_RT_THMAX_9 0x054c +#define RT1011_BAT_RT_THMAX_10 0x054d +#define RT1011_BAT_RT_THMAX_11 0x054e +#define RT1011_BAT_RT_THMAX_12 0x054f +#define RT1011_SPREAD_SPECTURM 0x0568 +#define RT1011_PRO_GAIN_MODE 0x056a +#define RT1011_RT_DRC_CROSS 0x0600 +#define RT1011_RT_DRC_HB_1 0x0611 +#define RT1011_RT_DRC_HB_2 0x0612 +#define RT1011_RT_DRC_HB_3 0x0613 +#define RT1011_RT_DRC_HB_4 0x0614 +#define RT1011_RT_DRC_HB_5 0x0615 +#define RT1011_RT_DRC_HB_6 0x0616 +#define RT1011_RT_DRC_HB_7 0x0617 +#define RT1011_RT_DRC_HB_8 0x0618 +#define RT1011_RT_DRC_BB_1 0x0621 +#define RT1011_RT_DRC_BB_2 0x0622 +#define RT1011_RT_DRC_BB_3 0x0623 +#define RT1011_RT_DRC_BB_4 0x0624 +#define RT1011_RT_DRC_BB_5 0x0625 +#define RT1011_RT_DRC_BB_6 0x0626 +#define RT1011_RT_DRC_BB_7 0x0627 +#define RT1011_RT_DRC_BB_8 0x0628 +#define RT1011_RT_DRC_POS_1 0x0631 +#define RT1011_RT_DRC_POS_2 0x0632 +#define RT1011_RT_DRC_POS_3 0x0633 +#define RT1011_RT_DRC_POS_4 0x0634 +#define RT1011_RT_DRC_POS_5 0x0635 +#define RT1011_RT_DRC_POS_6 0x0636 +#define RT1011_RT_DRC_POS_7 0x0637 +#define RT1011_RT_DRC_POS_8 0x0638 +#define RT1011_CROSS_BQ_SET_1 0x0702 +#define RT1011_CROSS_BQ_SET_2 0x0704 +#define RT1011_BQ_SET_0 0x0706 +#define RT1011_BQ_SET_1 0x0708 +#define RT1011_BQ_SET_2 0x070a +#define RT1011_BQ_PRE_GAIN_28_16 0x0710 +#define RT1011_BQ_PRE_GAIN_15_0 0x0711 +#define RT1011_BQ_POST_GAIN_28_16 0x0712 +#define RT1011_BQ_POST_GAIN_15_0 0x0713 + +#define RT1011_BQ_H0_28_16 0x0720 +#define RT1011_BQ_A2_15_0 0x0729 +#define RT1011_BQ_1_H0_28_16 0x0730 +#define RT1011_BQ_1_A2_15_0 0x0739 +#define RT1011_BQ_2_H0_28_16 0x0740 +#define RT1011_BQ_2_A2_15_0 0x0749 +#define RT1011_BQ_3_H0_28_16 0x0750 +#define RT1011_BQ_3_A2_15_0 0x0759 +#define RT1011_BQ_4_H0_28_16 0x0760 +#define RT1011_BQ_4_A2_15_0 0x0769 +#define RT1011_BQ_5_H0_28_16 0x0770 +#define RT1011_BQ_5_A2_15_0 0x0779 +#define RT1011_BQ_6_H0_28_16 0x0780 +#define RT1011_BQ_6_A2_15_0 0x0789 +#define RT1011_BQ_7_H0_28_16 0x0790 +#define RT1011_BQ_7_A2_15_0 0x0799 +#define RT1011_BQ_8_H0_28_16 0x07a0 +#define RT1011_BQ_8_A2_15_0 0x07a9 +#define RT1011_BQ_9_H0_28_16 0x07b0 +#define RT1011_BQ_9_A2_15_0 0x07b9 +#define RT1011_BQ_10_H0_28_16 0x07c0 +#define RT1011_BQ_10_A2_15_0 0x07c9 +#define RT1011_TEST_PAD_STATUS 0x1000 +#define RT1011_SYSTEM_RESET_1 0x1007 +#define RT1011_SYSTEM_RESET_2 0x1008 +#define RT1011_SYSTEM_RESET_3 0x1009 +#define RT1011_ADCDAT_OUT_SOURCE 0x100D +#define RT1011_PLL_INTERNAL_SET 0x1010 +#define RT1011_TEST_OUT_1 0x1020 +#define RT1011_TEST_OUT_3 0x1024 +#define RT1011_DC_CALIB_CLASSD_1 0x1200 +#define RT1011_DC_CALIB_CLASSD_2 0x1202 +#define RT1011_DC_CALIB_CLASSD_3 0x1204 +#define RT1011_DC_CALIB_CLASSD_5 0x1208 +#define RT1011_DC_CALIB_CLASSD_6 0x120a +#define RT1011_DC_CALIB_CLASSD_7 0x120c +#define RT1011_DC_CALIB_CLASSD_8 0x120e +#define RT1011_DC_CALIB_CLASSD_10 0x1212 +#define RT1011_CLASSD_INTERNAL_SET_1 0x1300 +#define RT1011_CLASSD_INTERNAL_SET_3 0x1304 +#define RT1011_CLASSD_INTERNAL_SET_8 0x130c +#define RT1011_VREF_LV_1 0x131a +#define RT1011_SMART_BOOST_TIMING_1 0x1322 +#define RT1011_SMART_BOOST_TIMING_36 0x1349 +#define RT1011_SINE_GEN_REG_1 0x1500 +#define RT1011_SINE_GEN_REG_2 0x1502 +#define RT1011_SINE_GEN_REG_3 0x1504 +#define RT1011_STP_INITIAL_RS_TEMP 0x1510 +#define RT1011_STP_CALIB_RS_TEMP 0x152a +#define RT1011_INIT_RECIPROCAL_REG_24_16 0x1538 +#define RT1011_INIT_RECIPROCAL_REG_15_0 0x1539 +#define RT1011_STP_INITIAL_RESISTANCE_TEMP 0x153c +#define RT1011_STP_ALPHA_RECIPROCAL_MSB 0x153e +#define RT1011_SPK_RESISTANCE_1 0x1544 +#define RT1011_SPK_RESISTANCE_2 0x1546 +#define RT1011_SPK_THERMAL 0x1548 +#define RT1011_STP_OTP_TH 0x1552 +#define RT1011_ALC_BK_GAIN_O 0x1554 +#define RT1011_ALC_BK_GAIN_O_PRE 0x1556 +#define RT1011_SPK_DC_O_23_16 0x155a +#define RT1011_SPK_DC_O_15_0 0x155c +#define RT1011_INIT_RECIPROCAL_SYN_24_16 0x1560 +#define RT1011_INIT_RECIPROCAL_SYN_15_0 0x1562 +#define RT1011_STP_BQ_1_A1_L_28_16 0x1570 +#define RT1011_STP_BQ_1_H0_R_15_0 0x1583 +#define RT1011_STP_BQ_2_A1_L_28_16 0x1590 +#define RT1011_SPK_EXCURSION_23_16 0x15be +#define RT1011_SPK_EXCURSION_15_0 0x15bf +#define RT1011_SEP_MAIN_OUT_23_16 0x15c0 +#define RT1011_SEP_MAIN_OUT_15_0 0x15c1 +#define RT1011_SEP_RE_REG_15_0 0x15f9 +#define RT1011_DRC_CF_PARAMS_1 0x1600 +#define RT1011_DRC_CF_PARAMS_12 0x160b +#define RT1011_ALC_DRC_HB_INTERNAL_1 0x1611 +#define RT1011_ALC_DRC_HB_INTERNAL_5 0x1615 +#define RT1011_ALC_DRC_HB_INTERNAL_6 0x1616 +#define RT1011_ALC_DRC_HB_INTERNAL_7 0x1617 +#define RT1011_ALC_DRC_BB_INTERNAL_1 0x1621 +#define RT1011_ALC_DRC_BB_INTERNAL_5 0x1625 +#define RT1011_ALC_DRC_BB_INTERNAL_6 0x1626 +#define RT1011_ALC_DRC_BB_INTERNAL_7 0x1627 +#define RT1011_ALC_DRC_POS_INTERNAL_1 0x1631 +#define RT1011_ALC_DRC_POS_INTERNAL_5 0x1635 +#define RT1011_ALC_DRC_POS_INTERNAL_6 0x1636 +#define RT1011_ALC_DRC_POS_INTERNAL_7 0x1637 +#define RT1011_ALC_DRC_POS_INTERNAL_8 0x1638 +#define RT1011_ALC_DRC_POS_INTERNAL_9 0x163a +#define RT1011_ALC_DRC_POS_INTERNAL_10 0x163c +#define RT1011_ALC_DRC_POS_INTERNAL_11 0x163e +#define RT1011_BQ_1_PARAMS_CHECK_5 0x1648 +#define RT1011_BQ_2_PARAMS_CHECK_1 0x1650 +#define RT1011_BQ_2_PARAMS_CHECK_5 0x1658 +#define RT1011_BQ_3_PARAMS_CHECK_1 0x1660 +#define RT1011_BQ_3_PARAMS_CHECK_5 0x1668 +#define RT1011_BQ_4_PARAMS_CHECK_1 0x1670 +#define RT1011_BQ_4_PARAMS_CHECK_5 0x1678 +#define RT1011_BQ_5_PARAMS_CHECK_1 0x1680 +#define RT1011_BQ_5_PARAMS_CHECK_5 0x1688 +#define RT1011_BQ_6_PARAMS_CHECK_1 0x1690 +#define RT1011_BQ_6_PARAMS_CHECK_5 0x1698 +#define RT1011_BQ_7_PARAMS_CHECK_1 0x1700 +#define RT1011_BQ_7_PARAMS_CHECK_5 0x1708 +#define RT1011_BQ_8_PARAMS_CHECK_1 0x1710 +#define RT1011_BQ_8_PARAMS_CHECK_5 0x1718 +#define RT1011_BQ_9_PARAMS_CHECK_1 0x1720 +#define RT1011_BQ_9_PARAMS_CHECK_5 0x1728 +#define RT1011_BQ_10_PARAMS_CHECK_1 0x1730 +#define RT1011_BQ_10_PARAMS_CHECK_5 0x1738 +#define RT1011_IRQ_1 0x173a +#define RT1011_PART_NUMBER_EFUSE 0x173e +#define RT1011_EFUSE_CONTROL_1 0x17bb +#define RT1011_EFUSE_CONTROL_2 0x17bd +#define RT1011_EFUSE_MATCH_DONE 0x17cb +#define RT1011_EFUSE_ADC_OFFSET_18_16 0x17e5 +#define RT1011_EFUSE_ADC_OFFSET_15_0 0x17e7 +#define RT1011_EFUSE_DAC_OFFSET_G0_20_16 0x17e9 +#define RT1011_EFUSE_DAC_OFFSET_G0_15_0 0x17eb +#define RT1011_EFUSE_DAC_OFFSET_G1_20_16 0x17ed +#define RT1011_EFUSE_DAC_OFFSET_G1_15_0 0x17ef +#define RT1011_EFUSE_READ_R0_3_15_0 0x1803 +#define RT1011_MAX_REG 0x1803 +#define RT1011_REG_DISP_LEN 23 + + +/* CLOCK-2 (0x0004) */ +#define RT1011_FS_SYS_PRE_MASK (0x3 << 14) +#define RT1011_FS_SYS_PRE_SFT 14 +#define RT1011_FS_SYS_PRE_MCLK (0x0 << 14) +#define RT1011_FS_SYS_PRE_BCLK (0x1 << 14) +#define RT1011_FS_SYS_PRE_PLL1 (0x2 << 14) +#define RT1011_FS_SYS_PRE_RCCLK (0x3 << 14) +#define RT1011_PLL1_SRC_MASK (0x1 << 13) +#define RT1011_PLL1_SRC_SFT 13 +#define RT1011_PLL1_SRC_PLL2 (0x0 << 13) +#define RT1011_PLL1_SRC_BCLK (0x1 << 13) +#define RT1011_PLL2_SRC_MASK (0x1 << 12) +#define RT1011_PLL2_SRC_SFT 12 +#define RT1011_PLL2_SRC_MCLK (0x0 << 12) +#define RT1011_PLL2_SRC_RCCLK (0x1 << 12) +#define RT1011_PLL2_SRC_DIV_MASK (0x3 << 10) +#define RT1011_PLL2_SRC_DIV_SFT 10 +#define RT1011_SRCIN_DIV_MASK (0x3 << 8) +#define RT1011_SRCIN_DIV_SFT 8 +#define RT1011_FS_SYS_DIV_MASK (0x7 << 4) +#define RT1011_FS_SYS_DIV_SFT 4 + +/* PLL-1 (0x000a) */ +#define RT1011_PLL1_QM_MASK (0xf << 12) +#define RT1011_PLL1_QM_SFT 12 +#define RT1011_PLL1_BPM_MASK (0x1 << 11) +#define RT1011_PLL1_BPM_SFT 11 +#define RT1011_PLL1_BPM (0x1 << 11) +#define RT1011_PLL1_QN_MASK (0x1ff << 0) +#define RT1011_PLL1_QN_SFT 0 + +/* PLL-2 (0x000c) */ +#define RT1011_PLL2_BPK_MASK (0x1 << 5) +#define RT1011_PLL2_BPK_SFT 5 +#define RT1011_PLL2_BPK (0x1 << 5) +#define RT1011_PLL2_QK_MASK (0x1f << 0) +#define RT1011_PLL2_QK_SFT 0 + +/* Clock Detect (0x0020) */ +#define RT1011_EN_MCLK_DET_MASK (0x1 << 15) +#define RT1011_EN_MCLK_DET_SFT 15 +#define RT1011_EN_MCLK_DET (0x1 << 15) + +/* DAC Setting-2 (0x0104) */ +#define RT1011_EN_CKGEN_DAC_MASK (0x1 << 13) +#define RT1011_EN_CKGEN_DAC_SFT 13 +#define RT1011_EN_CKGEN_DAC (0x1 << 13) + +/* DAC Setting-3 (0x0106) */ +#define RT1011_DA_MUTE_EN_MASK (0x1 << 15) +#define RT1011_DA_MUTE_EN_SFT 15 + +/* ADC Setting-5 (0x0110) */ +#define RT1011_AD_EN_CKGEN_ADC_MASK (0x1 << 9) +#define RT1011_AD_EN_CKGEN_ADC_SFT 9 +#define RT1011_AD_EN_CKGEN_ADC (0x1 << 9) + +/* TDM Total Setting (0x0111) */ +#define RT1011_I2S_TDM_MS_MASK (0x1 << 14) +#define RT1011_I2S_TDM_MS_SFT 14 +#define RT1011_I2S_TDM_MS_S (0x0 << 14) +#define RT1011_I2S_TDM_MS_M (0x1 << 14) +#define RT1011_I2S_TX_DL_MASK (0x7 << 8) +#define RT1011_I2S_TX_DL_SFT 8 +#define RT1011_I2S_TX_DL_16B (0x0 << 8) +#define RT1011_I2S_TX_DL_20B (0x1 << 8) +#define RT1011_I2S_TX_DL_24B (0x2 << 8) +#define RT1011_I2S_TX_DL_32B (0x3 << 8) +#define RT1011_I2S_TX_DL_8B (0x4 << 8) +#define RT1011_I2S_RX_DL_MASK (0x7 << 5) +#define RT1011_I2S_RX_DL_SFT 5 +#define RT1011_I2S_RX_DL_16B (0x0 << 5) +#define RT1011_I2S_RX_DL_20B (0x1 << 5) +#define RT1011_I2S_RX_DL_24B (0x2 << 5) +#define RT1011_I2S_RX_DL_32B (0x3 << 5) +#define RT1011_I2S_RX_DL_8B (0x4 << 5) +#define RT1011_ADCDAT1_PIN_CONFIG (0x1 << 4) +#define RT1011_ADCDAT1_OUTPUT (0x0 << 4) +#define RT1011_ADCDAT1_INPUT (0x1 << 4) +#define RT1011_ADCDAT2_PIN_CONFIG (0x1 << 3) +#define RT1011_ADCDAT2_OUTPUT (0x0 << 3) +#define RT1011_ADCDAT2_INPUT (0x1 << 3) +#define RT1011_I2S_TDM_DF_MASK (0x7 << 0) +#define RT1011_I2S_TDM_DF_SFT 0 +#define RT1011_I2S_TDM_DF_I2S (0x0) +#define RT1011_I2S_TDM_DF_LEFT (0x1) +#define RT1011_I2S_TDM_DF_PCM_A (0x2) +#define RT1011_I2S_TDM_DF_PCM_B (0x3) +#define RT1011_I2S_TDM_DF_PCM_A_N (0x6) +#define RT1011_I2S_TDM_DF_PCM_B_N (0x7) + +/* TDM_tcon Setting (0x0112) */ +#define RT1011_TCON_DF_MASK (0x7 << 13) +#define RT1011_TCON_DF_SFT 13 +#define RT1011_TCON_DF_I2S (0x0 << 13) +#define RT1011_TCON_DF_LEFT (0x1 << 13) +#define RT1011_TCON_DF_PCM_A (0x2 << 13) +#define RT1011_TCON_DF_PCM_B (0x3 << 13) +#define RT1011_TCON_DF_PCM_A_N (0x6 << 13) +#define RT1011_TCON_DF_PCM_B_N (0x7 << 13) +#define RT1011_TCON_BCLK_SEL_MASK (0x3 << 10) +#define RT1011_TCON_BCLK_SEL_SFT 10 +#define RT1011_TCON_BCLK_SEL_32FS (0x0 << 10) +#define RT1011_TCON_BCLK_SEL_64FS (0x1 << 10) +#define RT1011_TCON_BCLK_SEL_128FS (0x2 << 10) +#define RT1011_TCON_BCLK_SEL_256FS (0x3 << 10) +#define RT1011_TCON_CH_LEN_MASK (0x3 << 5) +#define RT1011_TCON_CH_LEN_SFT 5 +#define RT1011_TCON_CH_LEN_16B (0x0 << 5) +#define RT1011_TCON_CH_LEN_20B (0x1 << 5) +#define RT1011_TCON_CH_LEN_24B (0x2 << 5) +#define RT1011_TCON_CH_LEN_32B (0x3 << 5) +#define RT1011_TCON_BCLK_MST_MASK (0x1 << 4) +#define RT1011_TCON_BCLK_MST_SFT 4 +#define RT1011_TCON_BCLK_MST_INV (0x1 << 4) + +/* TDM1 Setting-1 (0x0114) */ +#define RT1011_TDM_INV_BCLK_MASK (0x1 << 15) +#define RT1011_TDM_INV_BCLK_SFT 15 +#define RT1011_TDM_INV_BCLK (0x1 << 15) +#define RT1011_I2S_CH_TX_MASK (0x3 << 10) +#define RT1011_I2S_CH_TX_SFT 10 +#define RT1011_I2S_TX_2CH (0x0 << 10) +#define RT1011_I2S_TX_4CH (0x1 << 10) +#define RT1011_I2S_TX_6CH (0x2 << 10) +#define RT1011_I2S_TX_8CH (0x3 << 10) +#define RT1011_I2S_CH_RX_MASK (0x3 << 8) +#define RT1011_I2S_CH_RX_SFT 8 +#define RT1011_I2S_RX_2CH (0x0 << 8) +#define RT1011_I2S_RX_4CH (0x1 << 8) +#define RT1011_I2S_RX_6CH (0x2 << 8) +#define RT1011_I2S_RX_8CH (0x3 << 8) +#define RT1011_I2S_LR_CH_SEL_MASK (0x1 << 7) +#define RT1011_I2S_LR_CH_SEL_SFT 7 +#define RT1011_I2S_LEFT_CH_SEL (0x0 << 7) +#define RT1011_I2S_RIGHT_CH_SEL (0x1 << 7) +#define RT1011_I2S_CH_TX_LEN_MASK (0x7 << 4) +#define RT1011_I2S_CH_TX_LEN_SFT 4 +#define RT1011_I2S_CH_TX_LEN_16B (0x0 << 4) +#define RT1011_I2S_CH_TX_LEN_20B (0x1 << 4) +#define RT1011_I2S_CH_TX_LEN_24B (0x2 << 4) +#define RT1011_I2S_CH_TX_LEN_32B (0x3 << 4) +#define RT1011_I2S_CH_TX_LEN_8B (0x4 << 4) +#define RT1011_I2S_CH_RX_LEN_MASK (0x7 << 0) +#define RT1011_I2S_CH_RX_LEN_SFT 0 +#define RT1011_I2S_CH_RX_LEN_16B (0x0 << 0) +#define RT1011_I2S_CH_RX_LEN_20B (0x1 << 0) +#define RT1011_I2S_CH_RX_LEN_24B (0x2 << 0) +#define RT1011_I2S_CH_RX_LEN_32B (0x3 << 0) +#define RT1011_I2S_CH_RX_LEN_8B (0x4 << 0) + +/* TDM1 Setting-2 (0x0116) */ +#define RT1011_TDM_I2S_DOCK_ADCDAT_LEN_1_MASK (0x7 << 13) +#define RT1011_TDM_I2S_DOCK_ADCDAT_2CH (0x1 << 13) +#define RT1011_TDM_I2S_DOCK_ADCDAT_4CH (0x3 << 13) +#define RT1011_TDM_I2S_DOCK_ADCDAT_6CH (0x5 << 13) +#define RT1011_TDM_I2S_DOCK_ADCDAT_8CH (0x7 << 13) +#define RT1011_TDM_I2S_DOCK_EN_1_MASK (0x1 << 3) +#define RT1011_TDM_I2S_DOCK_EN_1_SFT 3 +#define RT1011_TDM_I2S_DOCK_EN_1 (0x1 << 3) +#define RT1011_TDM_ADCDAT1_DATA_LOCATION (0x7 << 0) + +/* TDM1 Setting-3 (0x0118) */ +#define RT1011_TDM_I2S_RX_ADC1_1_MASK (0x3 << 6) +#define RT1011_TDM_I2S_RX_ADC2_1_MASK (0x3 << 4) +#define RT1011_TDM_I2S_RX_ADC3_1_MASK (0x3 << 2) +#define RT1011_TDM_I2S_RX_ADC4_1_MASK (0x3 << 0) +#define RT1011_TDM_I2S_RX_ADC1_1_LL (0x2 << 6) +#define RT1011_TDM_I2S_RX_ADC2_1_LL (0x2 << 4) +#define RT1011_TDM_I2S_RX_ADC3_1_LL (0x2 << 2) +#define RT1011_TDM_I2S_RX_ADC4_1_LL (0x2 << 0) + +/* TDM1 Setting-4 (0x011a) */ +#define RT1011_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 12) +#define RT1011_TDM_I2S_TX_R_DAC1_1_MASK (0x7 << 8) +#define RT1011_TDM_I2S_TX_L_DAC1_1_SFT 12 +#define RT1011_TDM_I2S_TX_R_DAC1_1_SFT 8 + +/* TDM2 Setting-2 (0x0120) */ +#define RT1011_TDM_I2S_DOCK_ADCDAT_LEN_2_MASK (0x7 << 13) +#define RT1011_TDM_I2S_DOCK_EN_2_MASK (0x1 << 3) +#define RT1011_TDM_I2S_DOCK_EN_2_SFT 3 +#define RT1011_TDM_I2S_DOCK_EN_2 (0x1 << 3) + +/* MIXER 1 (0x0300) */ +#define RT1011_MIXER_MUTE_MIX_I_MASK (0x1 << 15) +#define RT1011_MIXER_MUTE_MIX_I_SFT 15 +#define RT1011_MIXER_MUTE_MIX_I (0x1 << 15) +#define RT1011_MIXER_MUTE_SUM_I_MASK (0x1 << 14) +#define RT1011_MIXER_MUTE_SUM_I_SFT 14 +#define RT1011_MIXER_MUTE_SUM_I (0x1 << 14) +#define RT1011_MIXER_MUTE_MIX_V_MASK (0x1 << 7) +#define RT1011_MIXER_MUTE_MIX_V_SFT 7 +#define RT1011_MIXER_MUTE_MIX_V (0x1 << 7) +#define RT1011_MIXER_MUTE_SUM_V_MASK (0x1 << 6) +#define RT1011_MIXER_MUTE_SUM_V_SFT 6 +#define RT1011_MIXER_MUTE_SUM_V (0x1 << 6) + +/* Analog Temperature Sensor (0x0316) */ +#define RT1011_POW_TEMP_REG (0x1 << 2) +#define RT1011_POW_TEMP_REG_BIT 2 + +/* POWER-1 (0x0322) */ +#define RT1011_POW_LDO2 (0x1 << 15) +#define RT1011_POW_LDO2_BIT 15 +#define RT1011_POW_DAC (0x1 << 14) +#define RT1011_POW_DAC_BIT 14 +#define RT1011_POW_CLK12M (0x1 << 13) +#define RT1011_POW_CLK12M_BIT 13 +#define RT1011_POW_TEMP (0x1 << 12) +#define RT1011_POW_TEMP_BIT 12 +#define RT1011_POW_ISENSE_SPK (0x1 << 7) +#define RT1011_POW_ISENSE_SPK_BIT 7 +#define RT1011_POW_LPF_SPK (0x1 << 6) +#define RT1011_POW_LPF_SPK_BIT 6 +#define RT1011_POW_VSENSE_SPK (0x1 << 5) +#define RT1011_POW_VSENSE_SPK_BIT 5 +#define RT1011_POW_TWO_BATTERY_SPK (0x1 << 4) +#define RT1011_POW_TWO_BATTERY_SPK_BIT 4 + +/* POWER-2 (0x0324) */ +#define RT1011_PLLEN (0x1 << 2) +#define RT1011_PLLEN_BIT 2 +#define RT1011_POW_BG (0x1 << 1) +#define RT1011_POW_BG_BIT 1 +#define RT1011_POW_BG_MBIAS_LV (0x1 << 0) +#define RT1011_POW_BG_MBIAS_LV_BIT 0 + +/* POWER-3 (0x0326) */ +#define RT1011_POW_DET_SPKVDD (0x1 << 15) +#define RT1011_POW_DET_SPKVDD_BIT 15 +#define RT1011_POW_DET_VBAT (0x1 << 14) +#define RT1011_POW_DET_VBAT_BIT 14 +#define RT1011_POW_FC (0x1 << 13) +#define RT1011_POW_FC_BIT 13 +#define RT1011_POW_MBIAS_LV (0x1 << 12) +#define RT1011_POW_MBIAS_LV_BIT 12 +#define RT1011_POW_ADC_I (0x1 << 11) +#define RT1011_POW_ADC_I_BIT 11 +#define RT1011_POW_ADC_V (0x1 << 10) +#define RT1011_POW_ADC_V_BIT 10 +#define RT1011_POW_ADC_T (0x1 << 9) +#define RT1011_POW_ADC_T_BIT 9 +#define RT1011_POWD_ADC_T (0x1 << 8) +#define RT1011_POWD_ADC_T_BIT 8 +#define RT1011_POW_MIX_I (0x1 << 7) +#define RT1011_POW_MIX_I_BIT 7 +#define RT1011_POW_MIX_V (0x1 << 6) +#define RT1011_POW_MIX_V_BIT 6 +#define RT1011_POW_SUM_I (0x1 << 5) +#define RT1011_POW_SUM_I_BIT 5 +#define RT1011_POW_SUM_V (0x1 << 4) +#define RT1011_POW_SUM_V_BIT 4 +#define RT1011_POW_MIX_T (0x1 << 2) +#define RT1011_POW_MIX_T_BIT 2 +#define RT1011_BYPASS_MIX_T (0x1 << 1) +#define RT1011_BYPASS_MIX_T_BIT 1 +#define RT1011_POW_VREF_LV (0x1 << 0) +#define RT1011_POW_VREF_LV_BIT 0 + +/* POWER-4 (0x0328) */ +#define RT1011_POW_EN_SWR (0x1 << 12) +#define RT1011_POW_EN_SWR_BIT 12 +#define RT1011_POW_EN_PASS_BGOK_SWR (0x1 << 10) +#define RT1011_POW_EN_PASS_BGOK_SWR_BIT 10 +#define RT1011_POW_EN_PASS_VPOK_SWR (0x1 << 9) +#define RT1011_POW_EN_PASS_VPOK_SWR_BIT 9 + +/* POWER-9 (0x032d) */ +#define RT1011_POW_SDB_REG_MASK (0x1 << 9) +#define RT1011_POW_SDB_REG_BIT 9 +#define RT1011_POW_SDB_REG (0x1 << 9) +#define RT1011_POW_SEL_SDB_MODE_MASK (0x1 << 6) +#define RT1011_POW_SEL_SDB_MODE_BIT 6 +#define RT1011_POW_SEL_SDB_MODE (0x1 << 6) +#define RT1011_POW_MNL_SDB_MASK (0x1 << 5) +#define RT1011_POW_MNL_SDB_BIT 5 +#define RT1011_POW_MNL_SDB (0x1 << 5) + +/* SPK Protection-Temperature Protection (0x050c) */ +#define RT1011_STP_EN_MASK (0x1 << 15) +#define RT1011_STP_EN_BIT 15 +#define RT1011_STP_EN (0x1 << 15) +#define RT1011_STP_RS_CLB_EN_MASK (0x1 << 14) +#define RT1011_STP_RS_CLB_EN_BIT 14 +#define RT1011_STP_RS_CLB_EN (0x1 << 14) + +/* SPK Protection-Temperature Protection-4 (0x0510) */ +#define RT1011_STP_R0_SELECT_MASK (0x3 << 6) +#define RT1011_STP_R0_SELECT_EFUSE (0x0 << 6) +#define RT1011_STP_R0_SELECT_START_VAL (0x1 << 6) +#define RT1011_STP_R0_SELECT_REG (0x2 << 6) +#define RT1011_STP_R0_SELECT_FORCE_ZERO (0x3 << 6) + +/* SPK Protection-Temperature Protection-6 (0x0512) */ +#define RT1011_STP_R0_EN_MASK (0x1 << 7) +#define RT1011_STP_R0_EN_BIT 7 +#define RT1011_STP_R0_EN (0x1 << 7) +#define RT1011_STP_T0_EN_MASK (0x1 << 6) +#define RT1011_STP_T0_EN_BIT 6 +#define RT1011_STP_T0_EN (0x1 << 6) + +/* Cross Biquad Setting-1 (0x0702) */ +#define RT1011_MONO_LR_SEL_MASK (0x3 << 5) +#define RT1011_MONO_L_CHANNEL (0x0 << 5) +#define RT1011_MONO_R_CHANNEL (0x1 << 5) +#define RT1011_MONO_LR_MIX_CHANNEL (0x2 << 5) + +/* ClassD Internal Setting-1 (0x1300) */ +#define RT1011_DRIVER_READY_SPK (0x1 << 12) +#define RT1011_DRIVER_READY_SPK_BIT 12 +#define RT1011_RECV_MODE_SPK_MASK (0x1 << 5) +#define RT1011_SPK_MODE (0x0 << 5) +#define RT1011_RECV_MODE (0x1 << 5) +#define RT1011_RECV_MODE_SPK_BIT 5 + +/* ClassD Internal Setting-3 (0x1304) */ +#define RT1011_REG_GAIN_CLASSD_RI_SPK_MASK (0x7 << 12) +#define RT1011_REG_GAIN_CLASSD_RI_410K (0x0 << 12) +#define RT1011_REG_GAIN_CLASSD_RI_95K (0x1 << 12) +#define RT1011_REG_GAIN_CLASSD_RI_82P5K (0x2 << 12) +#define RT1011_REG_GAIN_CLASSD_RI_72P5K (0x3 << 12) +#define RT1011_REG_GAIN_CLASSD_RI_62P5K (0x4 << 12) + +/* ClassD Internal Setting-8 (0x130c) */ +#define RT1011_TM_PORPVDD_SPK (0x1 << 1) +#define RT1011_TM_PORPVDD_SPK_BIT 1 + +/* SPK Protection-Temperature Protection-SINE_GEN_REG-1 (0x1500) */ +#define RT1011_STP_SIN_GEN_EN_MASK (0x1 << 13) +#define RT1011_STP_SIN_GEN_EN (0x1 << 13) +#define RT1011_STP_SIN_GEN_EN_BIT 13 + + +/* System Clock Source */ +enum { + RT1011_FS_SYS_PRE_S_MCLK, + RT1011_FS_SYS_PRE_S_BCLK, + RT1011_FS_SYS_PRE_S_PLL1, + RT1011_FS_SYS_PRE_S_RCCLK, /* 12M Hz */ +}; + +/* PLL Source 1/2 */ +enum { + RT1011_PLL1_S_BCLK, + RT1011_PLL2_S_MCLK, + RT1011_PLL2_S_RCCLK, /* 12M Hz */ +}; + +enum { + RT1011_AIF1, + RT1011_AIFS +}; + +enum { + RT1011_I2S_REF_NONE, + RT1011_I2S_REF_LEFT_CH, + RT1011_I2S_REF_RIGHT_CH, +}; + +/* BiQual & DRC related settings */ +#define RT1011_BQ_DRC_NUM 128 +struct rt1011_bq_drc_params { + unsigned short val; + unsigned short reg; +#ifdef CONFIG_64BIT + unsigned int reserved; +#endif +}; +enum { + RT1011_ADVMODE_INITIAL_SET, + RT1011_ADVMODE_SEP_BQ_COEFF, + RT1011_ADVMODE_EQ_BQ_COEFF, + RT1011_ADVMODE_BQ_UI_COEFF, + RT1011_ADVMODE_SMARTBOOST_COEFF, + RT1011_ADVMODE_NUM, +}; + +struct rt1011_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct work_struct cali_work; + struct rt1011_bq_drc_params **bq_drc_params; + + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int id; + + int pll_src; + int pll_in; + int pll_out; + + int bq_drc_set; + unsigned int r0_reg, cali_done; + unsigned int r0_calib, temperature_calib; + int recv_spk_mode; + int i2s_ref; +}; + +#endif /* end of _RT1011_H_ */ diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c new file mode 100644 index 0000000000..99ec0f9a83 --- /dev/null +++ b/sound/soc/codecs/rt1015.c @@ -0,0 +1,1180 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt1015.c -- RT1015 ALSA SoC audio amplifier driver +// +// Copyright 2019 Realtek Semiconductor Corp. +// +// Author: Jack Yu +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt1015.h" + +static const struct rt1015_platform_data i2s_default_platform_data = { + .power_up_delay_ms = 50, +}; + +static const struct reg_default rt1015_reg[] = { + { 0x0000, 0x0000 }, + { 0x0004, 0xa000 }, + { 0x0006, 0x0003 }, + { 0x000a, 0x081e }, + { 0x000c, 0x0006 }, + { 0x000e, 0x0000 }, + { 0x0010, 0x0000 }, + { 0x0012, 0x0000 }, + { 0x0014, 0x0000 }, + { 0x0016, 0x0000 }, + { 0x0018, 0x0000 }, + { 0x0020, 0x8000 }, + { 0x0022, 0x8043 }, + { 0x0076, 0x0000 }, + { 0x0078, 0x0000 }, + { 0x007a, 0x0002 }, + { 0x007c, 0x10ec }, + { 0x007d, 0x1015 }, + { 0x00f0, 0x5000 }, + { 0x00f2, 0x004c }, + { 0x00f3, 0xecfe }, + { 0x00f4, 0x0000 }, + { 0x00f6, 0x0400 }, + { 0x0100, 0x0028 }, + { 0x0102, 0xff02 }, + { 0x0104, 0xa213 }, + { 0x0106, 0x200c }, + { 0x010c, 0x0000 }, + { 0x010e, 0x0058 }, + { 0x0111, 0x0200 }, + { 0x0112, 0x0400 }, + { 0x0114, 0x0022 }, + { 0x0116, 0x0000 }, + { 0x0118, 0x0000 }, + { 0x011a, 0x0123 }, + { 0x011c, 0x4567 }, + { 0x0300, 0x203d }, + { 0x0302, 0x001e }, + { 0x0311, 0x0000 }, + { 0x0313, 0x6014 }, + { 0x0314, 0x00a2 }, + { 0x031a, 0x00a0 }, + { 0x031c, 0x001f }, + { 0x031d, 0xffff }, + { 0x031e, 0x0000 }, + { 0x031f, 0x0000 }, + { 0x0320, 0x0000 }, + { 0x0321, 0x0000 }, + { 0x0322, 0xd7df }, + { 0x0328, 0x10b2 }, + { 0x0329, 0x0175 }, + { 0x032a, 0x36ad }, + { 0x032b, 0x7e55 }, + { 0x032c, 0x0520 }, + { 0x032d, 0xaa00 }, + { 0x032e, 0x570e }, + { 0x0330, 0xe180 }, + { 0x0332, 0x0034 }, + { 0x0334, 0x0001 }, + { 0x0336, 0x0010 }, + { 0x0338, 0x0000 }, + { 0x04fa, 0x0030 }, + { 0x04fc, 0x35c8 }, + { 0x04fe, 0x0800 }, + { 0x0500, 0x0400 }, + { 0x0502, 0x1000 }, + { 0x0504, 0x0000 }, + { 0x0506, 0x04ff }, + { 0x0508, 0x0010 }, + { 0x050a, 0x001a }, + { 0x0519, 0x1c68 }, + { 0x051a, 0x0ccc }, + { 0x051b, 0x0666 }, + { 0x051d, 0x0000 }, + { 0x051f, 0x0000 }, + { 0x0536, 0x061c }, + { 0x0538, 0x0000 }, + { 0x053a, 0x0000 }, + { 0x053c, 0x0000 }, + { 0x053d, 0x0000 }, + { 0x053e, 0x0000 }, + { 0x053f, 0x0000 }, + { 0x0540, 0x0000 }, + { 0x0541, 0x0000 }, + { 0x0542, 0x0000 }, + { 0x0543, 0x0000 }, + { 0x0544, 0x0000 }, + { 0x0568, 0x0000 }, + { 0x056a, 0x0000 }, + { 0x1000, 0x0040 }, + { 0x1002, 0x5405 }, + { 0x1006, 0x5515 }, + { 0x1007, 0x05f7 }, + { 0x1009, 0x0b0a }, + { 0x100a, 0x00ef }, + { 0x100d, 0x0003 }, + { 0x1010, 0xa433 }, + { 0x1020, 0x0000 }, + { 0x1200, 0x5a01 }, + { 0x1202, 0x6524 }, + { 0x1204, 0x1f00 }, + { 0x1206, 0x0000 }, + { 0x1208, 0x0000 }, + { 0x120a, 0x0000 }, + { 0x120c, 0x0000 }, + { 0x120e, 0x0000 }, + { 0x1210, 0x0000 }, + { 0x1212, 0x0000 }, + { 0x1300, 0x10a1 }, + { 0x1302, 0x12ff }, + { 0x1304, 0x0400 }, + { 0x1305, 0x0844 }, + { 0x1306, 0x4611 }, + { 0x1308, 0x555e }, + { 0x130a, 0x0000 }, + { 0x130c, 0x2000 }, + { 0x130e, 0x0100 }, + { 0x130f, 0x0001 }, + { 0x1310, 0x0000 }, + { 0x1312, 0x0000 }, + { 0x1314, 0x0000 }, + { 0x1316, 0x0000 }, + { 0x1318, 0x0000 }, + { 0x131a, 0x0000 }, + { 0x1322, 0x0029 }, + { 0x1323, 0x4a52 }, + { 0x1324, 0x002c }, + { 0x1325, 0x0b02 }, + { 0x1326, 0x002d }, + { 0x1327, 0x6b5a }, + { 0x1328, 0x002e }, + { 0x1329, 0xcbb2 }, + { 0x132a, 0x0030 }, + { 0x132b, 0x2c0b }, + { 0x1330, 0x0031 }, + { 0x1331, 0x8c63 }, + { 0x1332, 0x0032 }, + { 0x1333, 0xecbb }, + { 0x1334, 0x0034 }, + { 0x1335, 0x4d13 }, + { 0x1336, 0x0037 }, + { 0x1337, 0x0dc3 }, + { 0x1338, 0x003d }, + { 0x1339, 0xef7b }, + { 0x133a, 0x0044 }, + { 0x133b, 0xd134 }, + { 0x133c, 0x0047 }, + { 0x133d, 0x91e4 }, + { 0x133e, 0x004d }, + { 0x133f, 0xc370 }, + { 0x1340, 0x0053 }, + { 0x1341, 0xf4fd }, + { 0x1342, 0x0060 }, + { 0x1343, 0x5816 }, + { 0x1344, 0x006c }, + { 0x1345, 0xbb2e }, + { 0x1346, 0x0072 }, + { 0x1347, 0xecbb }, + { 0x1348, 0x0076 }, + { 0x1349, 0x5d97 }, +}; + +static bool rt1015_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1015_RESET: + case RT1015_CLK_DET: + case RT1015_SIL_DET: + case RT1015_VER_ID: + case RT1015_VENDOR_ID: + case RT1015_DEVICE_ID: + case RT1015_PRO_ALT: + case RT1015_MAN_I2C: + case RT1015_DAC3: + case RT1015_VBAT_TEST_OUT1: + case RT1015_VBAT_TEST_OUT2: + case RT1015_VBAT_PROT_ATT: + case RT1015_VBAT_DET_CODE: + case RT1015_SMART_BST_CTRL1: + case RT1015_SPK_DC_DETECT1: + case RT1015_SPK_DC_DETECT4: + case RT1015_SPK_DC_DETECT5: + case RT1015_DC_CALIB_CLSD1: + case RT1015_DC_CALIB_CLSD5: + case RT1015_DC_CALIB_CLSD6: + case RT1015_DC_CALIB_CLSD7: + case RT1015_DC_CALIB_CLSD8: + case RT1015_S_BST_TIMING_INTER1: + case RT1015_OSCK_STA: + case RT1015_MONO_DYNA_CTRL1: + case RT1015_MONO_DYNA_CTRL5: + return true; + + default: + return false; + } +} + +static bool rt1015_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1015_RESET: + case RT1015_CLK2: + case RT1015_CLK3: + case RT1015_PLL1: + case RT1015_PLL2: + case RT1015_DUM_RW1: + case RT1015_DUM_RW2: + case RT1015_DUM_RW3: + case RT1015_DUM_RW4: + case RT1015_DUM_RW5: + case RT1015_DUM_RW6: + case RT1015_CLK_DET: + case RT1015_SIL_DET: + case RT1015_CUSTOMER_ID: + case RT1015_PCODE_FWVER: + case RT1015_VER_ID: + case RT1015_VENDOR_ID: + case RT1015_DEVICE_ID: + case RT1015_PAD_DRV1: + case RT1015_PAD_DRV2: + case RT1015_GAT_BOOST: + case RT1015_PRO_ALT: + case RT1015_OSCK_STA: + case RT1015_MAN_I2C: + case RT1015_DAC1: + case RT1015_DAC2: + case RT1015_DAC3: + case RT1015_ADC1: + case RT1015_ADC2: + case RT1015_TDM_MASTER: + case RT1015_TDM_TCON: + case RT1015_TDM1_1: + case RT1015_TDM1_2: + case RT1015_TDM1_3: + case RT1015_TDM1_4: + case RT1015_TDM1_5: + case RT1015_MIXER1: + case RT1015_MIXER2: + case RT1015_ANA_PROTECT1: + case RT1015_ANA_CTRL_SEQ1: + case RT1015_ANA_CTRL_SEQ2: + case RT1015_VBAT_DET_DEB: + case RT1015_VBAT_VOLT_DET1: + case RT1015_VBAT_VOLT_DET2: + case RT1015_VBAT_TEST_OUT1: + case RT1015_VBAT_TEST_OUT2: + case RT1015_VBAT_PROT_ATT: + case RT1015_VBAT_DET_CODE: + case RT1015_PWR1: + case RT1015_PWR4: + case RT1015_PWR5: + case RT1015_PWR6: + case RT1015_PWR7: + case RT1015_PWR8: + case RT1015_PWR9: + case RT1015_CLASSD_SEQ: + case RT1015_SMART_BST_CTRL1: + case RT1015_SMART_BST_CTRL2: + case RT1015_ANA_CTRL1: + case RT1015_ANA_CTRL2: + case RT1015_PWR_STATE_CTRL: + case RT1015_MONO_DYNA_CTRL: + case RT1015_MONO_DYNA_CTRL1: + case RT1015_MONO_DYNA_CTRL2: + case RT1015_MONO_DYNA_CTRL3: + case RT1015_MONO_DYNA_CTRL4: + case RT1015_MONO_DYNA_CTRL5: + case RT1015_SPK_VOL: + case RT1015_SHORT_DETTOP1: + case RT1015_SHORT_DETTOP2: + case RT1015_SPK_DC_DETECT1: + case RT1015_SPK_DC_DETECT2: + case RT1015_SPK_DC_DETECT3: + case RT1015_SPK_DC_DETECT4: + case RT1015_SPK_DC_DETECT5: + case RT1015_BAT_RPO_STEP1: + case RT1015_BAT_RPO_STEP2: + case RT1015_BAT_RPO_STEP3: + case RT1015_BAT_RPO_STEP4: + case RT1015_BAT_RPO_STEP5: + case RT1015_BAT_RPO_STEP6: + case RT1015_BAT_RPO_STEP7: + case RT1015_BAT_RPO_STEP8: + case RT1015_BAT_RPO_STEP9: + case RT1015_BAT_RPO_STEP10: + case RT1015_BAT_RPO_STEP11: + case RT1015_BAT_RPO_STEP12: + case RT1015_SPREAD_SPEC1: + case RT1015_SPREAD_SPEC2: + case RT1015_PAD_STATUS: + case RT1015_PADS_PULLING_CTRL1: + case RT1015_PADS_DRIVING: + case RT1015_SYS_RST1: + case RT1015_SYS_RST2: + case RT1015_SYS_GATING1: + case RT1015_TEST_MODE1: + case RT1015_TEST_MODE2: + case RT1015_TIMING_CTRL1: + case RT1015_PLL_INT: + case RT1015_TEST_OUT1: + case RT1015_DC_CALIB_CLSD1: + case RT1015_DC_CALIB_CLSD2: + case RT1015_DC_CALIB_CLSD3: + case RT1015_DC_CALIB_CLSD4: + case RT1015_DC_CALIB_CLSD5: + case RT1015_DC_CALIB_CLSD6: + case RT1015_DC_CALIB_CLSD7: + case RT1015_DC_CALIB_CLSD8: + case RT1015_DC_CALIB_CLSD9: + case RT1015_DC_CALIB_CLSD10: + case RT1015_CLSD_INTERNAL1: + case RT1015_CLSD_INTERNAL2: + case RT1015_CLSD_INTERNAL3: + case RT1015_CLSD_INTERNAL4: + case RT1015_CLSD_INTERNAL5: + case RT1015_CLSD_INTERNAL6: + case RT1015_CLSD_INTERNAL7: + case RT1015_CLSD_INTERNAL8: + case RT1015_CLSD_INTERNAL9: + case RT1015_CLSD_OCP_CTRL: + case RT1015_VREF_LV: + case RT1015_MBIAS1: + case RT1015_MBIAS2: + case RT1015_MBIAS3: + case RT1015_MBIAS4: + case RT1015_VREF_LV1: + case RT1015_S_BST_TIMING_INTER1: + case RT1015_S_BST_TIMING_INTER2: + case RT1015_S_BST_TIMING_INTER3: + case RT1015_S_BST_TIMING_INTER4: + case RT1015_S_BST_TIMING_INTER5: + case RT1015_S_BST_TIMING_INTER6: + case RT1015_S_BST_TIMING_INTER7: + case RT1015_S_BST_TIMING_INTER8: + case RT1015_S_BST_TIMING_INTER9: + case RT1015_S_BST_TIMING_INTER10: + case RT1015_S_BST_TIMING_INTER11: + case RT1015_S_BST_TIMING_INTER12: + case RT1015_S_BST_TIMING_INTER13: + case RT1015_S_BST_TIMING_INTER14: + case RT1015_S_BST_TIMING_INTER15: + case RT1015_S_BST_TIMING_INTER16: + case RT1015_S_BST_TIMING_INTER17: + case RT1015_S_BST_TIMING_INTER18: + case RT1015_S_BST_TIMING_INTER19: + case RT1015_S_BST_TIMING_INTER20: + case RT1015_S_BST_TIMING_INTER21: + case RT1015_S_BST_TIMING_INTER22: + case RT1015_S_BST_TIMING_INTER23: + case RT1015_S_BST_TIMING_INTER24: + case RT1015_S_BST_TIMING_INTER25: + case RT1015_S_BST_TIMING_INTER26: + case RT1015_S_BST_TIMING_INTER27: + case RT1015_S_BST_TIMING_INTER28: + case RT1015_S_BST_TIMING_INTER29: + case RT1015_S_BST_TIMING_INTER30: + case RT1015_S_BST_TIMING_INTER31: + case RT1015_S_BST_TIMING_INTER32: + case RT1015_S_BST_TIMING_INTER33: + case RT1015_S_BST_TIMING_INTER34: + case RT1015_S_BST_TIMING_INTER35: + case RT1015_S_BST_TIMING_INTER36: + return true; + + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9525, 75, 0); + +static const char * const rt1015_din_source_select[] = { + "Left", + "Right", + "Left + Right average", +}; + +static SOC_ENUM_SINGLE_DECL(rt1015_mono_lr_sel, RT1015_PAD_DRV2, 4, + rt1015_din_source_select); + +static const char * const rt1015_boost_mode[] = { + "Bypass", "Adaptive", "Fixed Adaptive" +}; + +static SOC_ENUM_SINGLE_DECL(rt1015_boost_mode_enum, 0, 0, + rt1015_boost_mode); + +static int rt1015_boost_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1015_priv *rt1015 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1015->boost_mode; + + return 0; +} + +static int rt1015_boost_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1015_priv *rt1015 = + snd_soc_component_get_drvdata(component); + int boost_mode = ucontrol->value.integer.value[0]; + + switch (boost_mode) { + case BYPASS: + snd_soc_component_update_bits(component, + RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK | + RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK, + RT1015_ABST_REG_MODE | RT1015_ABST_FIX_TGT_DIS | + RT1015_BYPASS_SWRREG_BYPASS); + break; + case ADAPTIVE: + snd_soc_component_update_bits(component, + RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK | + RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK, + RT1015_ABST_AUTO_MODE | RT1015_ABST_FIX_TGT_DIS | + RT1015_BYPASS_SWRREG_PASS); + break; + case FIXED_ADAPTIVE: + snd_soc_component_update_bits(component, + RT1015_SMART_BST_CTRL1, RT1015_ABST_AUTO_EN_MASK | + RT1015_ABST_FIX_TGT_MASK | RT1015_BYPASS_SWR_REG_MASK, + RT1015_ABST_AUTO_MODE | RT1015_ABST_FIX_TGT_EN | + RT1015_BYPASS_SWRREG_PASS); + break; + default: + dev_err(component->dev, "Unknown boost control.\n"); + return -EINVAL; + } + + rt1015->boost_mode = boost_mode; + + return 0; +} + +static int rt1015_bypass_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1015_priv *rt1015 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt1015->bypass_boost; + + return 0; +} + +static void rt1015_calibrate(struct rt1015_priv *rt1015) +{ + struct snd_soc_component *component = rt1015->component; + struct regmap *regmap = rt1015->regmap; + + snd_soc_dapm_mutex_lock(&component->dapm); + regcache_cache_bypass(regmap, true); + + regmap_write(regmap, RT1015_CLK_DET, 0x0000); + regmap_write(regmap, RT1015_PWR4, 0x00B2); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0009); + msleep(100); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000A); + msleep(100); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000C); + msleep(100); + regmap_write(regmap, RT1015_CLSD_INTERNAL8, 0x2028); + regmap_write(regmap, RT1015_CLSD_INTERNAL9, 0x0140); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000D); + msleep(300); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0008); + regmap_write(regmap, RT1015_SYS_RST1, 0x05F5); + regmap_write(regmap, RT1015_CLK_DET, 0x8000); + + regcache_cache_bypass(regmap, false); + regcache_mark_dirty(regmap); + regcache_sync(regmap); + snd_soc_dapm_mutex_unlock(&component->dapm); +} + +static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct rt1015_priv *rt1015 = + snd_soc_component_get_drvdata(component); + + if (rt1015->dac_is_used) { + dev_err(component->dev, "DAC is being used!\n"); + return -EBUSY; + } + + rt1015->bypass_boost = ucontrol->value.integer.value[0]; + if (rt1015->bypass_boost == RT1015_Bypass_Boost && + !rt1015->cali_done) { + rt1015_calibrate(rt1015); + rt1015->cali_done = 1; + + regmap_write(rt1015->regmap, RT1015_MONO_DYNA_CTRL, 0x0010); + } + + return 0; +} + +static const struct snd_kcontrol_new rt1015_snd_controls[] = { + SOC_SINGLE_TLV("DAC Playback Volume", RT1015_DAC1, RT1015_DAC_VOL_SFT, + 127, 0, dac_vol_tlv), + SOC_DOUBLE("DAC Playback Switch", RT1015_DAC3, + RT1015_DA_MUTE_SFT, RT1015_DVOL_MUTE_FLAG_SFT, 1, 1), + SOC_ENUM_EXT("Boost Mode", rt1015_boost_mode_enum, + rt1015_boost_mode_get, rt1015_boost_mode_put), + SOC_ENUM("Mono LR Select", rt1015_mono_lr_sel), + SOC_SINGLE_EXT("Bypass Boost", SND_SOC_NOPM, 0, 1, 0, + rt1015_bypass_boost_get, rt1015_bypass_boost_put), +}; + +static int rt1015_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + if (rt1015->sysclk_src == RT1015_SCLK_S_PLL) + return 1; + else + return 0; +} + +static int r1015_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rt1015->dac_is_used = 1; + if (rt1015->bypass_boost == RT1015_Enable_Boost) { + snd_soc_component_write(component, + RT1015_SYS_RST1, 0x05f7); + snd_soc_component_write(component, + RT1015_SYS_RST2, 0x0b0a); + snd_soc_component_write(component, + RT1015_GAT_BOOST, 0xacfe); + snd_soc_component_write(component, + RT1015_PWR9, 0xaa00); + snd_soc_component_write(component, + RT1015_GAT_BOOST, 0xecfe); + } else { + snd_soc_component_write(component, + 0x032d, 0xaa60); + snd_soc_component_write(component, + RT1015_SYS_RST1, 0x05f7); + snd_soc_component_write(component, + RT1015_SYS_RST2, 0x0b0a); + snd_soc_component_write(component, + RT1015_PWR_STATE_CTRL, 0x008e); + } + break; + + case SND_SOC_DAPM_POST_PMD: + if (rt1015->bypass_boost == RT1015_Enable_Boost) { + snd_soc_component_write(component, + RT1015_PWR9, 0xa800); + snd_soc_component_write(component, + RT1015_SYS_RST1, 0x05f5); + snd_soc_component_write(component, + RT1015_SYS_RST2, 0x0b9a); + } else { + snd_soc_component_write(component, + 0x032d, 0xaa60); + snd_soc_component_write(component, + RT1015_PWR_STATE_CTRL, 0x0088); + snd_soc_component_write(component, + RT1015_SYS_RST1, 0x05f5); + snd_soc_component_write(component, + RT1015_SYS_RST2, 0x0b9a); + } + rt1015->dac_is_used = 0; + break; + + default: + break; + } + return 0; +} + +static int rt1015_amp_drv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + unsigned int ret, ret2; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = snd_soc_component_read(component, RT1015_CLK_DET); + ret2 = snd_soc_component_read(component, RT1015_SPK_DC_DETECT1); + if (!((ret >> 15) & 0x1)) { + snd_soc_component_update_bits(component, RT1015_CLK_DET, + RT1015_EN_BCLK_DET_MASK, RT1015_EN_BCLK_DET); + dev_dbg(component->dev, "BCLK Detection Enabled.\n"); + } + if (!((ret2 >> 12) & 0x1)) { + snd_soc_component_update_bits(component, RT1015_SPK_DC_DETECT1, + RT1015_EN_CLA_D_DC_DET_MASK, RT1015_EN_CLA_D_DC_DET); + dev_dbg(component->dev, "Class-D DC Detection Enabled.\n"); + } + break; + case SND_SOC_DAPM_POST_PMU: + msleep(rt1015->pdata.power_up_delay_ms); + break; + default: + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt1015_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL", RT1015_PWR1, RT1015_PWR_PLL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, + r1015_dac_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1015_amp_drv_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPO"), +}; + +static const struct snd_soc_dapm_route rt1015_dapm_routes[] = { + { "DAC", NULL, "AIFRX" }, + { "DAC", NULL, "PLL", rt1015_is_sys_clk_from_pll}, + { "Amp Drv", NULL, "DAC" }, + { "SPO", NULL, "Amp Drv" }, +}; + +static int rt1015_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + int pre_div, frame_size, lrck; + unsigned int val_len = 0; + + lrck = params_rate(params); + pre_div = rl6231_get_clk_info(rt1015->sysclk, lrck); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock rate\n"); + return -EINVAL; + } + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + dev_dbg(component->dev, "pre_div is %d for iis %d\n", pre_div, dai->id); + + dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + lrck, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len = RT1015_I2S_DL_20; + break; + case 24: + val_len = RT1015_I2S_DL_24; + break; + case 8: + val_len = RT1015_I2S_DL_8; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1015_TDM_MASTER, + RT1015_I2S_DL_MASK, val_len); + snd_soc_component_update_bits(component, RT1015_CLK2, + RT1015_FS_PD_MASK, pre_div << RT1015_FS_PD_SFT); + + return 0; +} + +static int rt1015_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + unsigned int reg_val = 0, reg_val2 = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + reg_val |= RT1015_TCON_TDM_MS_M; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT1015_TCON_TDM_MS_S; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val2 |= RT1015_TDM_INV_BCLK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT1015_I2S_M_DF_LEFT; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT1015_I2S_M_DF_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT1015_I2S_M_DF_PCM_B; + break; + + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1015_TDM_MASTER, + RT1015_TCON_TDM_MS_MASK | RT1015_I2S_M_DF_MASK, + reg_val); + snd_soc_component_update_bits(component, RT1015_TDM1_1, + RT1015_TDM_INV_BCLK_MASK, reg_val2); + + return 0; +} + +static int rt1015_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt1015->sysclk && clk_id == rt1015->sysclk_src) + return 0; + + switch (clk_id) { + case RT1015_SCLK_S_MCLK: + reg_val |= RT1015_CLK_SYS_PRE_SEL_MCLK; + break; + + case RT1015_SCLK_S_PLL: + reg_val |= RT1015_CLK_SYS_PRE_SEL_PLL; + break; + + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + rt1015->sysclk = freq; + rt1015->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + snd_soc_component_update_bits(component, RT1015_CLK2, + RT1015_CLK_SYS_PRE_SEL_MASK, reg_val); + + return 0; +} + +static int rt1015_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt1015->pll_in = 0; + rt1015->pll_out = 0; + + return 0; + } + + if (source == rt1015->pll_src && freq_in == rt1015->pll_in && + freq_out == rt1015->pll_out) + return 0; + + switch (source) { + case RT1015_PLL_S_MCLK: + snd_soc_component_update_bits(component, RT1015_CLK2, + RT1015_PLL_SEL_MASK, RT1015_PLL_SEL_PLL_SRC2); + break; + + case RT1015_PLL_S_BCLK: + snd_soc_component_update_bits(component, RT1015_CLK2, + RT1015_PLL_SEL_MASK, RT1015_PLL_SEL_BCLK); + break; + + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT1015_PLL1, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1015_PLL_M_SFT) | + (pll_code.m_bp << RT1015_PLL_M_BP_SFT) | + pll_code.n_code); + snd_soc_component_write(component, RT1015_PLL2, + pll_code.k_code); + + rt1015->pll_in = freq_in; + rt1015->pll_out = freq_out; + rt1015->pll_src = source; + + return 0; +} + +static int rt1015_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int val = 0, rx_slotnum, tx_slotnum; + int ret = 0, first_bit; + + switch (slots) { + case 2: + val |= RT1015_I2S_TX_2CH; + break; + case 4: + val |= RT1015_I2S_TX_4CH; + break; + case 6: + val |= RT1015_I2S_TX_6CH; + break; + case 8: + val |= RT1015_I2S_TX_8CH; + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + switch (slot_width) { + case 16: + val |= RT1015_I2S_CH_TX_LEN_16B; + break; + case 20: + val |= RT1015_I2S_CH_TX_LEN_20B; + break; + case 24: + val |= RT1015_I2S_CH_TX_LEN_24B; + break; + case 32: + val |= RT1015_I2S_CH_TX_LEN_32B; + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + /* Rx slot configuration */ + rx_slotnum = hweight_long(rx_mask); + if (rx_slotnum != 1) { + ret = -EINVAL; + dev_err(component->dev, "too many rx slots or zero slot\n"); + goto _set_tdm_err_; + } + + /* This is an assumption that the system sends stereo audio to the amplifier typically. + * And the stereo audio is placed in slot 0/2/4/6 as the starting slot. + * The users could select the channel from L/R/L+R by "Mono LR Select" control. + */ + first_bit = __ffs(rx_mask); + switch (first_bit) { + case 0: + case 2: + case 4: + case 6: + snd_soc_component_update_bits(component, + RT1015_TDM1_4, + RT1015_TDM_I2S_TX_L_DAC1_1_MASK | + RT1015_TDM_I2S_TX_R_DAC1_1_MASK, + (first_bit << RT1015_TDM_I2S_TX_L_DAC1_1_SFT) | + ((first_bit+1) << RT1015_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + case 1: + case 3: + case 5: + case 7: + snd_soc_component_update_bits(component, + RT1015_TDM1_4, + RT1015_TDM_I2S_TX_L_DAC1_1_MASK | + RT1015_TDM_I2S_TX_R_DAC1_1_MASK, + ((first_bit-1) << RT1015_TDM_I2S_TX_L_DAC1_1_SFT) | + (first_bit << RT1015_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + /* Tx slot configuration */ + tx_slotnum = hweight_long(tx_mask); + if (tx_slotnum) { + ret = -EINVAL; + dev_err(component->dev, "doesn't need to support tx slots\n"); + goto _set_tdm_err_; + } + + snd_soc_component_update_bits(component, RT1015_TDM1_1, + RT1015_I2S_CH_TX_MASK | RT1015_I2S_CH_RX_MASK | + RT1015_I2S_CH_TX_LEN_MASK | RT1015_I2S_CH_RX_LEN_MASK, val); + +_set_tdm_err_: + return ret; +} + +static int rt1015_probe(struct snd_soc_component *component) +{ + struct rt1015_priv *rt1015 = + snd_soc_component_get_drvdata(component); + + rt1015->component = component; + + return 0; +} + +static void rt1015_remove(struct snd_soc_component *component) +{ + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + regmap_write(rt1015->regmap, RT1015_RESET, 0); +} + +#define RT1015_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT1015_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt1015_aif_dai_ops = { + .hw_params = rt1015_hw_params, + .set_fmt = rt1015_set_dai_fmt, + .set_tdm_slot = rt1015_set_tdm_slot, +}; + +static struct snd_soc_dai_driver rt1015_dai[] = { + { + .name = "rt1015-aif", + .id = 0, + .playback = { + .stream_name = "AIF Playback", + .channels_min = 1, + .channels_max = 4, + .rates = RT1015_STEREO_RATES, + .formats = RT1015_FORMATS, + }, + .ops = &rt1015_aif_dai_ops, + } +}; + +#ifdef CONFIG_PM +static int rt1015_suspend(struct snd_soc_component *component) +{ + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1015->regmap, true); + regcache_mark_dirty(rt1015->regmap); + + return 0; +} + +static int rt1015_resume(struct snd_soc_component *component) +{ + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1015->regmap, false); + regcache_sync(rt1015->regmap); + + if (rt1015->cali_done) + rt1015_calibrate(rt1015); + + return 0; +} +#else +#define rt1015_suspend NULL +#define rt1015_resume NULL +#endif + +static const struct snd_soc_component_driver soc_component_dev_rt1015 = { + .probe = rt1015_probe, + .remove = rt1015_remove, + .suspend = rt1015_suspend, + .resume = rt1015_resume, + .controls = rt1015_snd_controls, + .num_controls = ARRAY_SIZE(rt1015_snd_controls), + .dapm_widgets = rt1015_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1015_dapm_widgets), + .dapm_routes = rt1015_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1015_dapm_routes), + .set_sysclk = rt1015_set_component_sysclk, + .set_pll = rt1015_set_component_pll, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt1015_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT1015_S_BST_TIMING_INTER36, + .volatile_reg = rt1015_volatile_register, + .readable_reg = rt1015_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt1015_reg, + .num_reg_defaults = ARRAY_SIZE(rt1015_reg), +}; + +static const struct i2c_device_id rt1015_i2c_id[] = { + { "rt1015", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1015_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt1015_of_match[] = { + { .compatible = "realtek,rt1015", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1015_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt1015_acpi_match[] = { + {"10EC1015", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt1015_acpi_match); +#endif + +static void rt1015_parse_dt(struct rt1015_priv *rt1015, struct device *dev) +{ + device_property_read_u32(dev, "realtek,power-up-delay-ms", + &rt1015->pdata.power_up_delay_ms); +} + +static int rt1015_i2c_probe(struct i2c_client *i2c) +{ + struct rt1015_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt1015_priv *rt1015; + int ret; + unsigned int val; + + rt1015 = devm_kzalloc(&i2c->dev, sizeof(*rt1015), GFP_KERNEL); + if (!rt1015) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1015); + + rt1015->pdata = i2s_default_platform_data; + + if (pdata) + rt1015->pdata = *pdata; + else + rt1015_parse_dt(rt1015, &i2c->dev); + + rt1015->regmap = devm_regmap_init_i2c(i2c, &rt1015_regmap); + if (IS_ERR(rt1015->regmap)) { + ret = PTR_ERR(rt1015->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = regmap_read(rt1015->regmap, RT1015_DEVICE_ID, &val); + if (ret) { + dev_err(&i2c->dev, + "Failed to read device register: %d\n", ret); + return ret; + } else if ((val != RT1015_DEVICE_ID_VAL) && + (val != RT1015_DEVICE_ID_VAL2)) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt1015\n", val); + return -ENODEV; + } + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1015, + rt1015_dai, ARRAY_SIZE(rt1015_dai)); +} + +static void rt1015_i2c_shutdown(struct i2c_client *client) +{ + struct rt1015_priv *rt1015 = i2c_get_clientdata(client); + + regmap_write(rt1015->regmap, RT1015_RESET, 0); +} + +static struct i2c_driver rt1015_i2c_driver = { + .driver = { + .name = "rt1015", + .of_match_table = of_match_ptr(rt1015_of_match), + .acpi_match_table = ACPI_PTR(rt1015_acpi_match), + }, + .probe = rt1015_i2c_probe, + .shutdown = rt1015_i2c_shutdown, + .id_table = rt1015_i2c_id, +}; +module_i2c_driver(rt1015_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1015 driver"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1015.h b/sound/soc/codecs/rt1015.h new file mode 100644 index 0000000000..c9f636af7f --- /dev/null +++ b/sound/soc/codecs/rt1015.h @@ -0,0 +1,449 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt1015.h -- RT1015 ALSA SoC audio amplifier driver +// +// Copyright 2019 Realtek Semiconductor Corp. +// Author: Jack Yu +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// + +#ifndef __RT1015_H__ +#define __RT1015_H__ +#include + +#define RT1015_DEVICE_ID_VAL 0x1011 +#define RT1015_DEVICE_ID_VAL2 0x1015 + +#define RT1015_RESET 0x0000 +#define RT1015_CLK2 0x0004 +#define RT1015_CLK3 0x0006 +#define RT1015_PLL1 0x000a +#define RT1015_PLL2 0x000c +#define RT1015_DUM_RW1 0x000e +#define RT1015_DUM_RW2 0x0010 +#define RT1015_DUM_RW3 0x0012 +#define RT1015_DUM_RW4 0x0014 +#define RT1015_DUM_RW5 0x0016 +#define RT1015_DUM_RW6 0x0018 +#define RT1015_CLK_DET 0x0020 +#define RT1015_SIL_DET 0x0022 +#define RT1015_CUSTOMER_ID 0x0076 +#define RT1015_PCODE_FWVER 0x0078 +#define RT1015_VER_ID 0x007a +#define RT1015_VENDOR_ID 0x007c +#define RT1015_DEVICE_ID 0x007d +#define RT1015_PAD_DRV1 0x00f0 +#define RT1015_PAD_DRV2 0x00f2 +#define RT1015_GAT_BOOST 0x00f3 +#define RT1015_PRO_ALT 0x00f4 +#define RT1015_OSCK_STA 0x00f6 +#define RT1015_MAN_I2C 0x0100 +#define RT1015_DAC1 0x0102 +#define RT1015_DAC2 0x0104 +#define RT1015_DAC3 0x0106 +#define RT1015_ADC1 0x010c +#define RT1015_ADC2 0x010e +#define RT1015_TDM_MASTER 0x0111 +#define RT1015_TDM_TCON 0x0112 +#define RT1015_TDM1_1 0x0114 +#define RT1015_TDM1_2 0x0116 +#define RT1015_TDM1_3 0x0118 +#define RT1015_TDM1_4 0x011a +#define RT1015_TDM1_5 0x011c +#define RT1015_MIXER1 0x0300 +#define RT1015_MIXER2 0x0302 +#define RT1015_ANA_PROTECT1 0x0311 +#define RT1015_ANA_CTRL_SEQ1 0x0313 +#define RT1015_ANA_CTRL_SEQ2 0x0314 +#define RT1015_VBAT_DET_DEB 0x031a +#define RT1015_VBAT_VOLT_DET1 0x031c +#define RT1015_VBAT_VOLT_DET2 0x031d +#define RT1015_VBAT_TEST_OUT1 0x031e +#define RT1015_VBAT_TEST_OUT2 0x031f +#define RT1015_VBAT_PROT_ATT 0x0320 +#define RT1015_VBAT_DET_CODE 0x0321 +#define RT1015_PWR1 0x0322 +#define RT1015_PWR4 0x0328 +#define RT1015_PWR5 0x0329 +#define RT1015_PWR6 0x032a +#define RT1015_PWR7 0x032b +#define RT1015_PWR8 0x032c +#define RT1015_PWR9 0x032d +#define RT1015_CLASSD_SEQ 0x032e +#define RT1015_SMART_BST_CTRL1 0x0330 +#define RT1015_SMART_BST_CTRL2 0x0332 +#define RT1015_ANA_CTRL1 0x0334 +#define RT1015_ANA_CTRL2 0x0336 +#define RT1015_PWR_STATE_CTRL 0x0338 +#define RT1015_MONO_DYNA_CTRL 0x04fa +#define RT1015_MONO_DYNA_CTRL1 0x04fc +#define RT1015_MONO_DYNA_CTRL2 0x04fe +#define RT1015_MONO_DYNA_CTRL3 0x0500 +#define RT1015_MONO_DYNA_CTRL4 0x0502 +#define RT1015_MONO_DYNA_CTRL5 0x0504 +#define RT1015_SPK_VOL 0x0506 +#define RT1015_SHORT_DETTOP1 0x0508 +#define RT1015_SHORT_DETTOP2 0x050a +#define RT1015_SPK_DC_DETECT1 0x0519 +#define RT1015_SPK_DC_DETECT2 0x051a +#define RT1015_SPK_DC_DETECT3 0x051b +#define RT1015_SPK_DC_DETECT4 0x051d +#define RT1015_SPK_DC_DETECT5 0x051f +#define RT1015_BAT_RPO_STEP1 0x0536 +#define RT1015_BAT_RPO_STEP2 0x0538 +#define RT1015_BAT_RPO_STEP3 0x053a +#define RT1015_BAT_RPO_STEP4 0x053c +#define RT1015_BAT_RPO_STEP5 0x053d +#define RT1015_BAT_RPO_STEP6 0x053e +#define RT1015_BAT_RPO_STEP7 0x053f +#define RT1015_BAT_RPO_STEP8 0x0540 +#define RT1015_BAT_RPO_STEP9 0x0541 +#define RT1015_BAT_RPO_STEP10 0x0542 +#define RT1015_BAT_RPO_STEP11 0x0543 +#define RT1015_BAT_RPO_STEP12 0x0544 +#define RT1015_SPREAD_SPEC1 0x0568 +#define RT1015_SPREAD_SPEC2 0x056a +#define RT1015_PAD_STATUS 0x1000 +#define RT1015_PADS_PULLING_CTRL1 0x1002 +#define RT1015_PADS_DRIVING 0x1006 +#define RT1015_SYS_RST1 0x1007 +#define RT1015_SYS_RST2 0x1009 +#define RT1015_SYS_GATING1 0x100a +#define RT1015_TEST_MODE1 0x100c +#define RT1015_TEST_MODE2 0x100d +#define RT1015_TIMING_CTRL1 0x100e +#define RT1015_PLL_INT 0x1010 +#define RT1015_TEST_OUT1 0x1020 +#define RT1015_DC_CALIB_CLSD1 0x1200 +#define RT1015_DC_CALIB_CLSD2 0x1202 +#define RT1015_DC_CALIB_CLSD3 0x1204 +#define RT1015_DC_CALIB_CLSD4 0x1206 +#define RT1015_DC_CALIB_CLSD5 0x1208 +#define RT1015_DC_CALIB_CLSD6 0x120a +#define RT1015_DC_CALIB_CLSD7 0x120c +#define RT1015_DC_CALIB_CLSD8 0x120e +#define RT1015_DC_CALIB_CLSD9 0x1210 +#define RT1015_DC_CALIB_CLSD10 0x1212 +#define RT1015_CLSD_INTERNAL1 0x1300 +#define RT1015_CLSD_INTERNAL2 0x1302 +#define RT1015_CLSD_INTERNAL3 0x1304 +#define RT1015_CLSD_INTERNAL4 0x1305 +#define RT1015_CLSD_INTERNAL5 0x1306 +#define RT1015_CLSD_INTERNAL6 0x1308 +#define RT1015_CLSD_INTERNAL7 0x130a +#define RT1015_CLSD_INTERNAL8 0x130c +#define RT1015_CLSD_INTERNAL9 0x130e +#define RT1015_CLSD_OCP_CTRL 0x130f +#define RT1015_VREF_LV 0x1310 +#define RT1015_MBIAS1 0x1312 +#define RT1015_MBIAS2 0x1314 +#define RT1015_MBIAS3 0x1316 +#define RT1015_MBIAS4 0x1318 +#define RT1015_VREF_LV1 0x131a +#define RT1015_S_BST_TIMING_INTER1 0x1322 +#define RT1015_S_BST_TIMING_INTER2 0x1323 +#define RT1015_S_BST_TIMING_INTER3 0x1324 +#define RT1015_S_BST_TIMING_INTER4 0x1325 +#define RT1015_S_BST_TIMING_INTER5 0x1326 +#define RT1015_S_BST_TIMING_INTER6 0x1327 +#define RT1015_S_BST_TIMING_INTER7 0x1328 +#define RT1015_S_BST_TIMING_INTER8 0x1329 +#define RT1015_S_BST_TIMING_INTER9 0x132a +#define RT1015_S_BST_TIMING_INTER10 0x132b +#define RT1015_S_BST_TIMING_INTER11 0x1330 +#define RT1015_S_BST_TIMING_INTER12 0x1331 +#define RT1015_S_BST_TIMING_INTER13 0x1332 +#define RT1015_S_BST_TIMING_INTER14 0x1333 +#define RT1015_S_BST_TIMING_INTER15 0x1334 +#define RT1015_S_BST_TIMING_INTER16 0x1335 +#define RT1015_S_BST_TIMING_INTER17 0x1336 +#define RT1015_S_BST_TIMING_INTER18 0x1337 +#define RT1015_S_BST_TIMING_INTER19 0x1338 +#define RT1015_S_BST_TIMING_INTER20 0x1339 +#define RT1015_S_BST_TIMING_INTER21 0x133a +#define RT1015_S_BST_TIMING_INTER22 0x133b +#define RT1015_S_BST_TIMING_INTER23 0x133c +#define RT1015_S_BST_TIMING_INTER24 0x133d +#define RT1015_S_BST_TIMING_INTER25 0x133e +#define RT1015_S_BST_TIMING_INTER26 0x133f +#define RT1015_S_BST_TIMING_INTER27 0x1340 +#define RT1015_S_BST_TIMING_INTER28 0x1341 +#define RT1015_S_BST_TIMING_INTER29 0x1342 +#define RT1015_S_BST_TIMING_INTER30 0x1343 +#define RT1015_S_BST_TIMING_INTER31 0x1344 +#define RT1015_S_BST_TIMING_INTER32 0x1345 +#define RT1015_S_BST_TIMING_INTER33 0x1346 +#define RT1015_S_BST_TIMING_INTER34 0x1347 +#define RT1015_S_BST_TIMING_INTER35 0x1348 +#define RT1015_S_BST_TIMING_INTER36 0x1349 + +/* 0x0004 */ +#define RT1015_CLK_SYS_PRE_SEL_MASK (0x3 << 14) +#define RT1015_CLK_SYS_PRE_SEL_SFT 14 +#define RT1015_CLK_SYS_PRE_SEL_MCLK (0x0 << 14) +#define RT1015_CLK_SYS_PRE_SEL_PLL (0x2 << 14) +#define RT1015_PLL_SEL_MASK (0x1 << 13) +#define RT1015_PLL_SEL_SFT 13 +#define RT1015_PLL_SEL_PLL_SRC2 (0x0 << 13) +#define RT1015_PLL_SEL_BCLK (0x1 << 13) +#define RT1015_FS_PD_MASK (0x7 << 4) +#define RT1015_FS_PD_SFT 4 + +/* 0x000a */ +#define RT1015_PLL_M_MAX 0xf +#define RT1015_PLL_M_MASK (RT1015_PLL_M_MAX << 12) +#define RT1015_PLL_M_SFT 12 +#define RT1015_PLL_M_BP (0x1 << 11) +#define RT1015_PLL_M_BP_SFT 11 +#define RT1015_PLL_N_MAX 0x1ff +#define RT1015_PLL_N_MASK (RT1015_PLL_N_MAX << 0) +#define RT1015_PLL_N_SFT 0 + +/* 0x000c */ +#define RT1015_PLL_BPK_MASK (0x1 << 5) +#define RT1015_PLL_BPK (0x0 << 5) +#define RT1015_PLL_K_MAX 0x1f +#define RT1015_PLL_K_MASK (RT1015_PLL_K_MAX) +#define RT1015_PLL_K_SFT 0 + +/* 0x0020 */ +#define RT1015_EN_BCLK_DET_MASK (0x1 << 15) +#define RT1015_EN_BCLK_DET (0x1 << 15) +#define RT1015_DIS_BCLK_DET (0x0 << 15) + +/* 0x007a */ +#define RT1015_ID_MASK 0xff +#define RT1015_ID_VERA 0x0 +#define RT1015_ID_VERB 0x1 + +/* 0x00f2 */ +#define RT1015_MONO_LR_SEL_MASK (0x3 << 4) +#define RT1015_MONO_L_CHANNEL (0x0 << 4) +#define RT1015_MONO_R_CHANNEL (0x1 << 4) +#define RT1015_MONO_LR_MIX_CHANNEL (0x2 << 4) + +/* 0x0102 */ +#define RT1015_DAC_VOL_MASK (0x7f << 9) +#define RT1015_DAC_VOL_SFT 9 + +/* 0x0104 */ +#define RT1015_DAC_CLK (0x1 << 13) +#define RT1015_DAC_CLK_BIT 13 + +/* 0x0106 */ +#define RT1015_DAC_MUTE_MASK (0x1 << 15) +#define RT1015_DA_MUTE_SFT 15 +#define RT1015_DVOL_MUTE_FLAG_SFT 12 + +/* 0x0111 */ +#define RT1015_TCON_TDM_MS_MASK (0x1 << 14) +#define RT1015_TCON_TDM_MS_SFT 14 +#define RT1015_TCON_TDM_MS_S (0x0 << 14) +#define RT1015_TCON_TDM_MS_M (0x1 << 14) +#define RT1015_I2S_DL_MASK (0x7 << 8) +#define RT1015_I2S_DL_SFT 8 +#define RT1015_I2S_DL_16 (0x0 << 8) +#define RT1015_I2S_DL_20 (0x1 << 8) +#define RT1015_I2S_DL_24 (0x2 << 8) +#define RT1015_I2S_DL_8 (0x3 << 8) +#define RT1015_I2S_M_DF_MASK (0x7 << 0) +#define RT1015_I2S_M_DF_SFT 0 +#define RT1015_I2S_M_DF_I2S (0x0) +#define RT1015_I2S_M_DF_LEFT (0x1) +#define RT1015_I2S_M_DF_PCM_A (0x2) +#define RT1015_I2S_M_DF_PCM_B (0x3) +#define RT1015_I2S_M_DF_PCM_A_N (0x6) +#define RT1015_I2S_M_DF_PCM_B_N (0x7) + +/* TDM_tcon Setting (0x0112) */ +#define RT1015_I2S_TCON_DF_MASK (0x7 << 13) +#define RT1015_I2S_TCON_DF_SFT 13 +#define RT1015_I2S_TCON_DF_I2S (0x0 << 13) +#define RT1015_I2S_TCON_DF_LEFT (0x1 << 13) +#define RT1015_I2S_TCON_DF_PCM_A (0x2 << 13) +#define RT1015_I2S_TCON_DF_PCM_B (0x3 << 13) +#define RT1015_I2S_TCON_DF_PCM_A_N (0x6 << 13) +#define RT1015_I2S_TCON_DF_PCM_B_N (0x7 << 13) +#define RT1015_TCON_BCLK_SEL_MASK (0x3 << 10) +#define RT1015_TCON_BCLK_SEL_SFT 10 +#define RT1015_TCON_BCLK_SEL_32FS (0x0 << 10) +#define RT1015_TCON_BCLK_SEL_64FS (0x1 << 10) +#define RT1015_TCON_BCLK_SEL_128FS (0x2 << 10) +#define RT1015_TCON_BCLK_SEL_256FS (0x3 << 10) +#define RT1015_TCON_CH_LEN_MASK (0x3 << 5) +#define RT1015_TCON_CH_LEN_SFT 5 +#define RT1015_TCON_CH_LEN_16B (0x0 << 5) +#define RT1015_TCON_CH_LEN_20B (0x1 << 5) +#define RT1015_TCON_CH_LEN_24B (0x2 << 5) +#define RT1015_TCON_CH_LEN_32B (0x3 << 5) +#define RT1015_TCON_BCLK_MST_MASK (0x1 << 4) +#define RT1015_TCON_BCLK_MST_SFT 4 +#define RT1015_TCON_BCLK_MST_INV (0x1 << 4) + +/* TDM1 Setting-1 (0x0114) */ +#define RT1015_TDM_INV_BCLK_MASK (0x1 << 15) +#define RT1015_TDM_INV_BCLK_SFT 15 +#define RT1015_TDM_INV_BCLK (0x1 << 15) +#define RT1015_I2S_CH_TX_MASK (0x3 << 10) +#define RT1015_I2S_CH_TX_SFT 10 +#define RT1015_I2S_TX_2CH (0x0 << 10) +#define RT1015_I2S_TX_4CH (0x1 << 10) +#define RT1015_I2S_TX_6CH (0x2 << 10) +#define RT1015_I2S_TX_8CH (0x3 << 10) +#define RT1015_I2S_CH_RX_MASK (0x3 << 8) +#define RT1015_I2S_CH_RX_SFT 8 +#define RT1015_I2S_RX_2CH (0x0 << 8) +#define RT1015_I2S_RX_4CH (0x1 << 8) +#define RT1015_I2S_RX_6CH (0x2 << 8) +#define RT1015_I2S_RX_8CH (0x3 << 8) +#define RT1015_I2S_LR_CH_SEL_MASK (0x1 << 7) +#define RT1015_I2S_LR_CH_SEL_SFT 7 +#define RT1015_I2S_LEFT_CH_SEL (0x0 << 7) +#define RT1015_I2S_RIGHT_CH_SEL (0x1 << 7) +#define RT1015_I2S_CH_TX_LEN_MASK (0x7 << 4) +#define RT1015_I2S_CH_TX_LEN_SFT 4 +#define RT1015_I2S_CH_TX_LEN_16B (0x0 << 4) +#define RT1015_I2S_CH_TX_LEN_20B (0x1 << 4) +#define RT1015_I2S_CH_TX_LEN_24B (0x2 << 4) +#define RT1015_I2S_CH_TX_LEN_32B (0x3 << 4) +#define RT1015_I2S_CH_TX_LEN_8B (0x4 << 4) +#define RT1015_I2S_CH_RX_LEN_MASK (0x7 << 0) +#define RT1015_I2S_CH_RX_LEN_SFT 0 +#define RT1015_I2S_CH_RX_LEN_16B (0x0 << 0) +#define RT1015_I2S_CH_RX_LEN_20B (0x1 << 0) +#define RT1015_I2S_CH_RX_LEN_24B (0x2 << 0) +#define RT1015_I2S_CH_RX_LEN_32B (0x3 << 0) +#define RT1015_I2S_CH_RX_LEN_8B (0x4 << 0) + +/* TDM1 Setting-4 (0x011a) */ +#define RT1015_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 12) +#define RT1015_TDM_I2S_TX_R_DAC1_1_MASK (0x7 << 8) +#define RT1015_TDM_I2S_TX_L_DAC1_1_SFT 12 +#define RT1015_TDM_I2S_TX_R_DAC1_1_SFT 8 + +/* 0x0330 */ +#define RT1015_ABST_AUTO_EN_MASK (0x1 << 13) +#define RT1015_ABST_AUTO_MODE (0x1 << 13) +#define RT1015_ABST_REG_MODE (0x0 << 13) +#define RT1015_ABST_FIX_TGT_MASK (0x1 << 12) +#define RT1015_ABST_FIX_TGT_EN (0x1 << 12) +#define RT1015_ABST_FIX_TGT_DIS (0x0 << 12) +#define RT1015_BYPASS_SWR_REG_MASK (0x1 << 7) +#define RT1015_BYPASS_SWRREG_BYPASS (0x1 << 7) +#define RT1015_BYPASS_SWRREG_PASS (0x0 << 7) + +/* 0x0322 */ +#define RT1015_PWR_LDO2 (0x1 << 15) +#define RT1015_PWR_LDO2_BIT 15 +#define RT1015_PWR_DAC (0x1 << 14) +#define RT1015_PWR_DAC_BIT 14 +#define RT1015_PWR_INTCLK (0x1 << 13) +#define RT1015_PWR_INTCLK_BIT 13 +#define RT1015_PWR_ISENSE (0x1 << 12) +#define RT1015_PWR_ISENSE_BIT 12 +#define RT1015_PWR_VSENSE (0x1 << 10) +#define RT1015_PWR_VSENSE_BIT 10 +#define RT1015_PWR_PLL (0x1 << 9) +#define RT1015_PWR_PLL_BIT 9 +#define RT1015_PWR_BG_1_2 (0x1 << 8) +#define RT1015_PWR_BG_1_2_BIT 8 +#define RT1015_PWR_MBIAS_BG (0x1 << 7) +#define RT1015_PWR_MBIAS_BG_BIT 7 +#define RT1015_PWR_VBAT (0x1 << 6) +#define RT1015_PWR_VBAT_BIT 6 +#define RT1015_PWR_MBIAS (0x1 << 4) +#define RT1015_PWR_MBIAS_BIT 4 +#define RT1015_PWR_ADCV (0x1 << 3) +#define RT1015_PWR_ADCV_BIT 3 +#define RT1015_PWR_MIXERV (0x1 << 2) +#define RT1015_PWR_MIXERV_BIT 2 +#define RT1015_PWR_SUMV (0x1 << 1) +#define RT1015_PWR_SUMV_BIT 1 +#define RT1015_PWR_VREFLV (0x1 << 0) +#define RT1015_PWR_VREFLV_BIT 0 + +/* 0x0324 */ +#define RT1015_PWR_BASIC (0x1 << 15) +#define RT1015_PWR_BASIC_BIT 15 +#define RT1015_PWR_SD (0x1 << 14) +#define RT1015_PWR_SD_BIT 14 +#define RT1015_PWR_IBIAS (0x1 << 13) +#define RT1015_PWR_IBIAS_BIT 13 +#define RT1015_PWR_VCM (0x1 << 11) +#define RT1015_PWR_VCM_BIT 11 + +/* 0x0328 */ +#define RT1015_PWR_SWR (0x1 << 12) +#define RT1015_PWR_SWR_BIT 12 + +/* 0x0519 */ +#define RT1015_EN_CLA_D_DC_DET_MASK (0x1 << 12) +#define RT1015_EN_CLA_D_DC_DET (0x1 << 12) +#define RT1015_DIS_CLA_D_DC_DET (0x0 << 12) + +/* 0x1300 */ +#define RT1015_PWR_CLSD (0x1 << 12) +#define RT1015_PWR_CLSD_BIT 12 + +/* 0x007a */ +#define RT1015_ID_MASK 0xff +#define RT1015_ID_VERA 0x0 +#define RT1015_ID_VERB 0x1 + +/* System Clock Source */ +enum { + RT1015_SCLK_S_MCLK, + RT1015_SCLK_S_PLL, +}; + +/* PLL1 Source */ +enum { + RT1015_PLL_S_MCLK, + RT1015_PLL_S_BCLK, +}; + +enum { + RT1015_AIF1, + RT1015_AIFS, +}; + +enum { + RT1015_VERA, + RT1015_VERB, +}; + +enum { + BYPASS, + ADAPTIVE, + FIXED_ADAPTIVE, +}; + +enum { + RT1015_Enable_Boost = 0, + RT1015_Bypass_Boost, +}; + +enum { + RT1015_HW_28 = 0, + RT1015_HW_29, +}; + +struct rt1015_priv { + struct snd_soc_component *component; + struct rt1015_platform_data pdata; + struct regmap *regmap; + int sysclk; + int sysclk_src; + int pll_src; + int pll_in; + int pll_out; + int boost_mode; + int bypass_boost; + int dac_is_used; + int cali_done; +}; + +#endif /* __RT1015_H__ */ diff --git a/sound/soc/codecs/rt1015p.c b/sound/soc/codecs/rt1015p.c new file mode 100644 index 0000000000..44e7fe3c32 --- /dev/null +++ b/sound/soc/codecs/rt1015p.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1015p.c -- RT1015P ALSA SoC audio amplifier driver +// +// Copyright 2020 The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rt1015p_priv { + struct gpio_desc *sdb; + bool calib_done; +}; + +static int rt1015p_sdb_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt1015p_priv *rt1015p = + snd_soc_component_get_drvdata(component); + + if (!rt1015p->sdb) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + gpiod_set_value_cansleep(rt1015p->sdb, 1); + dev_dbg(component->dev, "set sdb to 1"); + + if (!rt1015p->calib_done) { + msleep(300); + rt1015p->calib_done = true; + } + break; + case SND_SOC_DAPM_POST_PMD: + gpiod_set_value_cansleep(rt1015p->sdb, 0); + dev_dbg(component->dev, "set sdb to 0"); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt1015p_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("Speaker"), + SND_SOC_DAPM_OUT_DRV_E("SDB", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1015p_sdb_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route rt1015p_dapm_routes[] = { + {"SDB", NULL, "HiFi Playback"}, + {"Speaker", NULL, "SDB"}, +}; + +#ifdef CONFIG_PM +static int rt1015p_suspend(struct snd_soc_component *component) +{ + struct rt1015p_priv *rt1015p = snd_soc_component_get_drvdata(component); + + rt1015p->calib_done = false; + return 0; +} +#else +#define rt1015p_suspend NULL +#endif + +static const struct snd_soc_component_driver rt1015p_component_driver = { + .suspend = rt1015p_suspend, + .dapm_widgets = rt1015p_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1015p_dapm_widgets), + .dapm_routes = rt1015p_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1015p_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct snd_soc_dai_driver rt1015p_dai_driver = { + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .formats = SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 1, + .channels_max = 2, + }, +}; + +static int rt1015p_platform_probe(struct platform_device *pdev) +{ + struct rt1015p_priv *rt1015p; + + rt1015p = devm_kzalloc(&pdev->dev, sizeof(*rt1015p), GFP_KERNEL); + if (!rt1015p) + return -ENOMEM; + + rt1015p->sdb = devm_gpiod_get_optional(&pdev->dev, + "sdb", GPIOD_OUT_LOW); + if (IS_ERR(rt1015p->sdb)) + return PTR_ERR(rt1015p->sdb); + + dev_set_drvdata(&pdev->dev, rt1015p); + + return devm_snd_soc_register_component(&pdev->dev, + &rt1015p_component_driver, + &rt1015p_dai_driver, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt1015p_device_id[] = { + { .compatible = "realtek,rt1015p" }, + { .compatible = "realtek,rt1019p" }, + {} +}; +MODULE_DEVICE_TABLE(of, rt1015p_device_id); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt1015p_acpi_match[] = { + { "RTL1015", 0}, + { "RTL1019", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt1015p_acpi_match); +#endif + +static struct platform_driver rt1015p_platform_driver = { + .driver = { + .name = "rt1015p", + .of_match_table = of_match_ptr(rt1015p_device_id), + .acpi_match_table = ACPI_PTR(rt1015p_acpi_match), + }, + .probe = rt1015p_platform_probe, +}; +module_platform_driver(rt1015p_platform_driver); + +MODULE_DESCRIPTION("ASoC RT1015P driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c new file mode 100644 index 0000000000..919a1f25e5 --- /dev/null +++ b/sound/soc/codecs/rt1016.c @@ -0,0 +1,693 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt1016.c -- RT1016 ALSA SoC audio amplifier driver +// +// Copyright 2020 Realtek Semiconductor Corp. +// Author: Oder Chiou +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt1016.h" + +static const struct reg_sequence rt1016_patch[] = { + {RT1016_VOL_CTRL_3, 0x8900}, + {RT1016_ANA_CTRL_1, 0xa002}, + {RT1016_ANA_CTRL_2, 0x0002}, + {RT1016_CLOCK_4, 0x6700}, + {RT1016_CLASSD_3, 0xdc55}, + {RT1016_CLASSD_4, 0x376a}, + {RT1016_CLASSD_5, 0x009f}, +}; + +static const struct reg_default rt1016_reg[] = { + {0x00, 0x0000}, + {0x01, 0x5400}, + {0x02, 0x5506}, + {0x03, 0xf800}, + {0x04, 0x0000}, + {0x05, 0xbfbf}, + {0x06, 0x8900}, + {0x07, 0xa002}, + {0x08, 0x0000}, + {0x09, 0x0000}, + {0x0a, 0x0000}, + {0x0c, 0x0000}, + {0x0d, 0x0000}, + {0x0e, 0x10ec}, + {0x0f, 0x6595}, + {0x11, 0x0002}, + {0x1c, 0x0000}, + {0x1d, 0x0000}, + {0x1e, 0x0000}, + {0x1f, 0xf000}, + {0x20, 0x0000}, + {0x21, 0x6000}, + {0x22, 0x0000}, + {0x23, 0x6700}, + {0x24, 0x0000}, + {0x25, 0x0000}, + {0x26, 0x0000}, + {0x40, 0x0018}, + {0x60, 0x00a5}, + {0x80, 0x0010}, + {0x81, 0x0009}, + {0x82, 0x0000}, + {0x83, 0x0000}, + {0xa0, 0x0700}, + {0xc0, 0x0080}, + {0xc1, 0x02a0}, + {0xc2, 0x1400}, + {0xc3, 0x0a4a}, + {0xc4, 0x552a}, + {0xc5, 0x087e}, + {0xc6, 0x0020}, + {0xc7, 0xa833}, + {0xc8, 0x0433}, + {0xc9, 0x8040}, + {0xca, 0xdc55}, + {0xcb, 0x376a}, + {0xcc, 0x009f}, + {0xcf, 0x0020}, +}; + +static bool rt1016_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1016_ANA_FLAG: + case RT1016_VERSION2_ID: + case RT1016_VERSION1_ID: + case RT1016_VENDER_ID: + case RT1016_DEVICE_ID: + case RT1016_TEST_SIGNAL: + case RT1016_SC_CTRL_1: + return true; + + default: + return false; + } +} + +static bool rt1016_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1016_RESET: + case RT1016_PADS_CTRL_1: + case RT1016_PADS_CTRL_2: + case RT1016_I2C_CTRL: + case RT1016_VOL_CTRL_1: + case RT1016_VOL_CTRL_2: + case RT1016_VOL_CTRL_3: + case RT1016_ANA_CTRL_1: + case RT1016_MUX_SEL: + case RT1016_RX_I2S_CTRL: + case RT1016_ANA_FLAG: + case RT1016_VERSION2_ID: + case RT1016_VERSION1_ID: + case RT1016_VENDER_ID: + case RT1016_DEVICE_ID: + case RT1016_ANA_CTRL_2: + case RT1016_TEST_SIGNAL: + case RT1016_TEST_CTRL_1: + case RT1016_TEST_CTRL_2: + case RT1016_TEST_CTRL_3: + case RT1016_CLOCK_1: + case RT1016_CLOCK_2: + case RT1016_CLOCK_3: + case RT1016_CLOCK_4: + case RT1016_CLOCK_5: + case RT1016_CLOCK_6: + case RT1016_CLOCK_7: + case RT1016_I2S_CTRL: + case RT1016_DAC_CTRL_1: + case RT1016_SC_CTRL_1: + case RT1016_SC_CTRL_2: + case RT1016_SC_CTRL_3: + case RT1016_SC_CTRL_4: + case RT1016_SIL_DET: + case RT1016_SYS_CLK: + case RT1016_BIAS_CUR: + case RT1016_DAC_CTRL_2: + case RT1016_LDO_CTRL: + case RT1016_CLASSD_1: + case RT1016_PLL1: + case RT1016_PLL2: + case RT1016_PLL3: + case RT1016_CLASSD_2: + case RT1016_CLASSD_OUT: + case RT1016_CLASSD_3: + case RT1016_CLASSD_4: + case RT1016_CLASSD_5: + case RT1016_PWR_CTRL: + return true; + + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0); + +static const struct snd_kcontrol_new rt1016_snd_controls[] = { + SOC_DOUBLE_TLV("DAC Playback Volume", RT1016_VOL_CTRL_2, + RT1016_L_VOL_SFT, RT1016_R_VOL_SFT, 191, 0, dac_vol_tlv), + SOC_DOUBLE("DAC Playback Switch", RT1016_VOL_CTRL_1, + RT1016_DA_MUTE_L_SFT, RT1016_DA_MUTE_R_SFT, 1, 1), +}; + +static int rt1016_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component); + + if (rt1016->sysclk_src == RT1016_SCLK_S_PLL) + return 1; + else + return 0; +} + +/* Interface data select */ +static const char * const rt1016_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt1016_if_data_swap_enum, + RT1016_I2S_CTRL, RT1016_I2S_DATA_SWAP_SFT, rt1016_data_select); + +static const struct snd_kcontrol_new rt1016_if_data_swap_mux = + SOC_DAPM_ENUM("Data Swap Mux", rt1016_if_data_swap_enum); + +static const struct snd_soc_dapm_widget rt1016_dapm_widgets[] = { + SND_SOC_DAPM_MUX("Data Swap Mux", SND_SOC_NOPM, 0, 0, + &rt1016_if_data_swap_mux), + + SND_SOC_DAPM_SUPPLY("DAC Filter", RT1016_CLOCK_3, + RT1016_PWR_DAC_FILTER_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAMOD", RT1016_CLOCK_3, RT1016_PWR_DACMOD_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("FIFO", RT1016_CLOCK_3, RT1016_PWR_CLK_FIFO_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Pure DC", RT1016_CLOCK_3, + RT1016_PWR_CLK_PUREDC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK Silence Det", RT1016_CLOCK_3, + RT1016_PWR_SIL_DET_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RC 25M", RT1016_CLOCK_3, RT1016_PWR_RC_25M_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT1016_CLOCK_3, RT1016_PWR_PLL1_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ANA CTRL", RT1016_CLOCK_3, RT1016_PWR_ANA_CTRL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK SYS", RT1016_CLOCK_3, RT1016_PWR_CLK_SYS_BIT, + 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("LRCK Det", RT1016_CLOCK_4, RT1016_PWR_LRCK_DET_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BCLK Det", RT1016_CLOCK_4, RT1016_PWR_BCLK_DET_BIT, + 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("CKGEN DAC", RT1016_DAC_CTRL_2, + RT1016_CKGEN_DAC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VCM SLOW", RT1016_CLASSD_1, RT1016_VCM_SLOW_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Silence Det", RT1016_SIL_DET, + RT1016_SIL_DET_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2", RT1016_PLL2, RT1016_PLL2_EN_BIT, 0, NULL, + 0), + + SND_SOC_DAPM_SUPPLY_S("BG1 BG2", 1, RT1016_PWR_CTRL, + RT1016_PWR_BG_1_2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("MBIAS BG", 1, RT1016_PWR_CTRL, + RT1016_PWR_MBIAS_BG_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("PLL", 1, RT1016_PWR_CTRL, RT1016_PWR_PLL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("BASIC", 1, RT1016_PWR_CTRL, RT1016_PWR_BASIC_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("CLASS D", 1, RT1016_PWR_CTRL, + RT1016_PWR_CLSD_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("25M", 1, RT1016_PWR_CTRL, RT1016_PWR_25M_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DACL", 1, RT1016_PWR_CTRL, RT1016_PWR_DACL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DACR", 1, RT1016_PWR_CTRL, RT1016_PWR_DACR_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO2", 1, RT1016_PWR_CTRL, RT1016_PWR_LDO2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("VREF", 1, RT1016_PWR_CTRL, RT1016_PWR_VREF_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("MBIAS", 1, RT1016_PWR_CTRL, RT1016_PWR_MBIAS_BIT, + 0, NULL, 0), + + SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("SPO"), +}; + +static const struct snd_soc_dapm_route rt1016_dapm_routes[] = { + { "Data Swap Mux", "L/R", "AIFRX" }, + { "Data Swap Mux", "R/L", "AIFRX" }, + { "Data Swap Mux", "L/L", "AIFRX" }, + { "Data Swap Mux", "R/R", "AIFRX" }, + + { "DAC", NULL, "DAC Filter" }, + { "DAC", NULL, "DAMOD" }, + { "DAC", NULL, "FIFO" }, + { "DAC", NULL, "Pure DC" }, + { "DAC", NULL, "Silence Det" }, + { "DAC", NULL, "ANA CTRL" }, + { "DAC", NULL, "CLK SYS" }, + { "DAC", NULL, "LRCK Det" }, + { "DAC", NULL, "BCLK Det" }, + { "DAC", NULL, "CKGEN DAC" }, + { "DAC", NULL, "VCM SLOW" }, + + { "PLL", NULL, "PLL1" }, + { "PLL", NULL, "PLL2" }, + { "25M", NULL, "RC 25M" }, + { "Silence Det", NULL, "CLK Silence Det" }, + + { "DAC", NULL, "Data Swap Mux" }, + { "DAC", NULL, "BG1 BG2" }, + { "DAC", NULL, "MBIAS BG" }, + { "DAC", NULL, "PLL", rt1016_is_sys_clk_from_pll}, + { "DAC", NULL, "BASIC" }, + { "DAC", NULL, "CLASS D" }, + { "DAC", NULL, "25M" }, + { "DAC", NULL, "DACL" }, + { "DAC", NULL, "DACR" }, + { "DAC", NULL, "LDO2" }, + { "DAC", NULL, "VREF" }, + { "DAC", NULL, "MBIAS" }, + + { "SPO", NULL, "DAC" }, +}; + +static int rt1016_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component); + int pre_div, bclk_ms, frame_size; + unsigned int val_len = 0; + + rt1016->lrck = params_rate(params); + pre_div = rl6231_get_clk_info(rt1016->sysclk, rt1016->lrck); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock rate\n"); + return -EINVAL; + } + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + bclk_ms = frame_size > 32; + rt1016->bclk = rt1016->lrck * (32 << bclk_ms); + + if (bclk_ms && rt1016->master) + snd_soc_component_update_bits(component, RT1016_I2S_CTRL, + RT1016_I2S_BCLK_MS_MASK, RT1016_I2S_BCLK_MS_64); + + dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt1016->lrck, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + val_len = RT1016_I2S_DL_16; + break; + case 20: + val_len = RT1016_I2S_DL_20; + break; + case 24: + val_len = RT1016_I2S_DL_24; + break; + case 32: + val_len = RT1016_I2S_DL_32; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1016_I2S_CTRL, + RT1016_I2S_DL_MASK, val_len); + snd_soc_component_update_bits(component, RT1016_CLOCK_2, + RT1016_FS_PD_MASK | RT1016_OSR_PD_MASK, + ((pre_div + 3) << RT1016_FS_PD_SFT) | + (pre_div << RT1016_OSR_PD_SFT)); + + return 0; +} + +static int rt1016_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + reg_val |= RT1016_I2S_MS_M; + rt1016->master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT1016_I2S_MS_S; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT1016_I2S_BCLK_POL_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT1016_I2S_DF_LEFT; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT1016_I2S_DF_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT1016_I2S_DF_PCM_B; + break; + + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1016_I2S_CTRL, + RT1016_I2S_MS_MASK | RT1016_I2S_BCLK_POL_MASK | + RT1016_I2S_DF_MASK, reg_val); + + return 0; +} + +static int rt1016_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt1016->sysclk && clk_id == rt1016->sysclk_src) + return 0; + + switch (clk_id) { + case RT1016_SCLK_S_MCLK: + reg_val |= RT1016_CLK_SYS_SEL_MCLK; + break; + + case RT1016_SCLK_S_PLL: + reg_val |= RT1016_CLK_SYS_SEL_PLL; + break; + + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + rt1016->sysclk = freq; + rt1016->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + snd_soc_component_update_bits(component, RT1016_CLOCK_1, + RT1016_CLK_SYS_SEL_MASK, reg_val); + + return 0; +} + +static int rt1016_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt1016->pll_in = 0; + rt1016->pll_out = 0; + + return 0; + } + + if (source == rt1016->pll_src && freq_in == rt1016->pll_in && + freq_out == rt1016->pll_out) + return 0; + + switch (source) { + case RT1016_PLL_S_MCLK: + snd_soc_component_update_bits(component, RT1016_CLOCK_1, + RT1016_PLL_SEL_MASK, RT1016_PLL_SEL_MCLK); + break; + + case RT1016_PLL_S_BCLK: + snd_soc_component_update_bits(component, RT1016_CLOCK_1, + RT1016_PLL_SEL_MASK, RT1016_PLL_SEL_BCLK); + break; + + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out * 4, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "mbypass=%d m=%d n=%d kbypass=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_bp, + (pll_code.k_bp ? 0 : pll_code.k_code)); + + snd_soc_component_write(component, RT1016_PLL1, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1016_PLL_M_SFT) | + (pll_code.m_bp << RT1016_PLL_M_BP_SFT) | + pll_code.n_code); + snd_soc_component_write(component, RT1016_PLL2, + (pll_code.k_bp << RT1016_PLL_K_BP_SFT) | + (pll_code.k_bp ? 0 : pll_code.k_code)); + + rt1016->pll_in = freq_in; + rt1016->pll_out = freq_out; + rt1016->pll_src = source; + + return 0; +} + +static int rt1016_probe(struct snd_soc_component *component) +{ + struct rt1016_priv *rt1016 = + snd_soc_component_get_drvdata(component); + + rt1016->component = component; + + return 0; +} + +static void rt1016_remove(struct snd_soc_component *component) +{ + struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component); + + regmap_write(rt1016->regmap, RT1016_RESET, 0); +} + +#define RT1016_STEREO_RATES SNDRV_PCM_RATE_8000_48000 +#define RT1016_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt1016_aif_dai_ops = { + .hw_params = rt1016_hw_params, + .set_fmt = rt1016_set_dai_fmt, +}; + +static struct snd_soc_dai_driver rt1016_dai[] = { + { + .name = "rt1016-aif", + .id = 0, + .playback = { + .stream_name = "AIF Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1016_STEREO_RATES, + .formats = RT1016_FORMATS, + }, + .ops = &rt1016_aif_dai_ops, + } +}; + +#ifdef CONFIG_PM +static int rt1016_suspend(struct snd_soc_component *component) +{ + struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1016->regmap, true); + regcache_mark_dirty(rt1016->regmap); + + return 0; +} + +static int rt1016_resume(struct snd_soc_component *component) +{ + struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1016->regmap, false); + regcache_sync(rt1016->regmap); + + return 0; +} +#else +#define rt1016_suspend NULL +#define rt1016_resume NULL +#endif + +static const struct snd_soc_component_driver soc_component_dev_rt1016 = { + .probe = rt1016_probe, + .remove = rt1016_remove, + .suspend = rt1016_suspend, + .resume = rt1016_resume, + .controls = rt1016_snd_controls, + .num_controls = ARRAY_SIZE(rt1016_snd_controls), + .dapm_widgets = rt1016_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1016_dapm_widgets), + .dapm_routes = rt1016_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1016_dapm_routes), + .set_sysclk = rt1016_set_component_sysclk, + .set_pll = rt1016_set_component_pll, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt1016_regmap = { + .reg_bits = 8, + .val_bits = 16, + .max_register = RT1016_PWR_CTRL, + .volatile_reg = rt1016_volatile_register, + .readable_reg = rt1016_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt1016_reg, + .num_reg_defaults = ARRAY_SIZE(rt1016_reg), +}; + +static const struct i2c_device_id rt1016_i2c_id[] = { + { "rt1016", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1016_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt1016_of_match[] = { + { .compatible = "realtek,rt1016", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1016_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt1016_acpi_match[] = { + {"10EC1016", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt1016_acpi_match); +#endif + +static int rt1016_i2c_probe(struct i2c_client *i2c) +{ + struct rt1016_priv *rt1016; + int ret; + unsigned int val; + + rt1016 = devm_kzalloc(&i2c->dev, sizeof(struct rt1016_priv), + GFP_KERNEL); + if (rt1016 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1016); + + rt1016->regmap = devm_regmap_init_i2c(i2c, &rt1016_regmap); + if (IS_ERR(rt1016->regmap)) { + ret = PTR_ERR(rt1016->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt1016->regmap, RT1016_DEVICE_ID, &val); + if (val != RT1016_DEVICE_ID_VAL) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt1016\n", val); + return -ENODEV; + } + + regmap_write(rt1016->regmap, RT1016_RESET, 0); + + ret = regmap_register_patch(rt1016->regmap, rt1016_patch, + ARRAY_SIZE(rt1016_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1016, + rt1016_dai, ARRAY_SIZE(rt1016_dai)); +} + +static void rt1016_i2c_shutdown(struct i2c_client *client) +{ + struct rt1016_priv *rt1016 = i2c_get_clientdata(client); + + regmap_write(rt1016->regmap, RT1016_RESET, 0); +} + +static struct i2c_driver rt1016_i2c_driver = { + .driver = { + .name = "rt1016", + .of_match_table = of_match_ptr(rt1016_of_match), + .acpi_match_table = ACPI_PTR(rt1016_acpi_match), + }, + .probe = rt1016_i2c_probe, + .shutdown = rt1016_i2c_shutdown, + .id_table = rt1016_i2c_id, +}; +module_i2c_driver(rt1016_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1016 driver"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1016.h b/sound/soc/codecs/rt1016.h new file mode 100644 index 0000000000..041d6a5a6f --- /dev/null +++ b/sound/soc/codecs/rt1016.h @@ -0,0 +1,232 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt1016.h -- RT1016 ALSA SoC audio amplifier driver + * + * Copyright 2020 Realtek Semiconductor Corp. + * Author: Oder Chiou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __RT1016_H__ +#define __RT1016_H__ + +#define RT1016_DEVICE_ID_VAL 0x6595 + +#define RT1016_RESET 0x00 +#define RT1016_PADS_CTRL_1 0x01 +#define RT1016_PADS_CTRL_2 0x02 +#define RT1016_I2C_CTRL 0x03 +#define RT1016_VOL_CTRL_1 0x04 +#define RT1016_VOL_CTRL_2 0x05 +#define RT1016_VOL_CTRL_3 0x06 +#define RT1016_ANA_CTRL_1 0x07 +#define RT1016_MUX_SEL 0x08 +#define RT1016_RX_I2S_CTRL 0x09 +#define RT1016_ANA_FLAG 0x0a +#define RT1016_VERSION2_ID 0x0c +#define RT1016_VERSION1_ID 0x0d +#define RT1016_VENDER_ID 0x0e +#define RT1016_DEVICE_ID 0x0f +#define RT1016_ANA_CTRL_2 0x11 +#define RT1016_TEST_SIGNAL 0x1c +#define RT1016_TEST_CTRL_1 0x1d +#define RT1016_TEST_CTRL_2 0x1e +#define RT1016_TEST_CTRL_3 0x1f +#define RT1016_CLOCK_1 0x20 +#define RT1016_CLOCK_2 0x21 +#define RT1016_CLOCK_3 0x22 +#define RT1016_CLOCK_4 0x23 +#define RT1016_CLOCK_5 0x24 +#define RT1016_CLOCK_6 0x25 +#define RT1016_CLOCK_7 0x26 +#define RT1016_I2S_CTRL 0x40 +#define RT1016_DAC_CTRL_1 0x60 +#define RT1016_SC_CTRL_1 0x80 +#define RT1016_SC_CTRL_2 0x81 +#define RT1016_SC_CTRL_3 0x82 +#define RT1016_SC_CTRL_4 0x83 +#define RT1016_SIL_DET 0xa0 +#define RT1016_SYS_CLK 0xc0 +#define RT1016_BIAS_CUR 0xc1 +#define RT1016_DAC_CTRL_2 0xc2 +#define RT1016_LDO_CTRL 0xc3 +#define RT1016_CLASSD_1 0xc4 +#define RT1016_PLL1 0xc5 +#define RT1016_PLL2 0xc6 +#define RT1016_PLL3 0xc7 +#define RT1016_CLASSD_2 0xc8 +#define RT1016_CLASSD_OUT 0xc9 +#define RT1016_CLASSD_3 0xca +#define RT1016_CLASSD_4 0xcb +#define RT1016_CLASSD_5 0xcc +#define RT1016_PWR_CTRL 0xcf + +/* global definition */ +#define RT1016_L_VOL_MASK (0xff << 8) +#define RT1016_L_VOL_SFT 8 +#define RT1016_R_VOL_MASK (0xff) +#define RT1016_R_VOL_SFT 0 + +/* 0x04 */ +#define RT1016_DA_MUTE_L_SFT 7 +#define RT1016_DA_MUTE_R_SFT 6 + +/* 0x20 */ +#define RT1016_CLK_SYS_SEL_MASK (0x1 << 15) +#define RT1016_CLK_SYS_SEL_SFT 15 +#define RT1016_CLK_SYS_SEL_MCLK (0x0 << 15) +#define RT1016_CLK_SYS_SEL_PLL (0x1 << 15) +#define RT1016_PLL_SEL_MASK (0x1 << 13) +#define RT1016_PLL_SEL_SFT 13 +#define RT1016_PLL_SEL_MCLK (0x0 << 13) +#define RT1016_PLL_SEL_BCLK (0x1 << 13) + +/* 0x21 */ +#define RT1016_FS_PD_MASK (0x7 << 13) +#define RT1016_FS_PD_SFT 13 +#define RT1016_OSR_PD_MASK (0x3 << 10) +#define RT1016_OSR_PD_SFT 10 + +/* 0x22 */ +#define RT1016_PWR_DAC_FILTER (0x1 << 11) +#define RT1016_PWR_DAC_FILTER_BIT 11 +#define RT1016_PWR_DACMOD (0x1 << 10) +#define RT1016_PWR_DACMOD_BIT 10 +#define RT1016_PWR_CLK_FIFO (0x1 << 9) +#define RT1016_PWR_CLK_FIFO_BIT 9 +#define RT1016_PWR_CLK_PUREDC (0x1 << 8) +#define RT1016_PWR_CLK_PUREDC_BIT 8 +#define RT1016_PWR_SIL_DET (0x1 << 7) +#define RT1016_PWR_SIL_DET_BIT 7 +#define RT1016_PWR_RC_25M (0x1 << 6) +#define RT1016_PWR_RC_25M_BIT 6 +#define RT1016_PWR_PLL1 (0x1 << 5) +#define RT1016_PWR_PLL1_BIT 5 +#define RT1016_PWR_ANA_CTRL (0x1 << 4) +#define RT1016_PWR_ANA_CTRL_BIT 4 +#define RT1016_PWR_CLK_SYS (0x1 << 3) +#define RT1016_PWR_CLK_SYS_BIT 3 + +/* 0x23 */ +#define RT1016_PWR_LRCK_DET (0x1 << 15) +#define RT1016_PWR_LRCK_DET_BIT 15 +#define RT1016_PWR_BCLK_DET (0x1 << 11) +#define RT1016_PWR_BCLK_DET_BIT 11 + +/* 0x40 */ +#define RT1016_I2S_BCLK_MS_MASK (0x1 << 15) +#define RT1016_I2S_BCLK_MS_SFT 15 +#define RT1016_I2S_BCLK_MS_32 (0x0 << 15) +#define RT1016_I2S_BCLK_MS_64 (0x1 << 15) +#define RT1016_I2S_BCLK_POL_MASK (0x1 << 13) +#define RT1016_I2S_BCLK_POL_SFT 13 +#define RT1016_I2S_BCLK_POL_NOR (0x0 << 13) +#define RT1016_I2S_BCLK_POL_INV (0x1 << 13) +#define RT1016_I2S_DATA_SWAP_MASK (0x1 << 10) +#define RT1016_I2S_DATA_SWAP_SFT 10 +#define RT1016_I2S_DL_MASK (0x7 << 4) +#define RT1016_I2S_DL_SFT 4 +#define RT1016_I2S_DL_16 (0x1 << 4) +#define RT1016_I2S_DL_20 (0x2 << 4) +#define RT1016_I2S_DL_24 (0x3 << 4) +#define RT1016_I2S_DL_32 (0x4 << 4) +#define RT1016_I2S_MS_MASK (0x1 << 3) +#define RT1016_I2S_MS_SFT 3 +#define RT1016_I2S_MS_M (0x0 << 3) +#define RT1016_I2S_MS_S (0x1 << 3) +#define RT1016_I2S_DF_MASK (0x7 << 0) +#define RT1016_I2S_DF_SFT 0 +#define RT1016_I2S_DF_I2S (0x0) +#define RT1016_I2S_DF_LEFT (0x1) +#define RT1016_I2S_DF_PCM_A (0x2) +#define RT1016_I2S_DF_PCM_B (0x3) + +/* 0xa0 */ +#define RT1016_SIL_DET_EN (0x1 << 15) +#define RT1016_SIL_DET_EN_BIT 15 + +/* 0xc2 */ +#define RT1016_CKGEN_DAC (0x1 << 13) +#define RT1016_CKGEN_DAC_BIT 13 + +/* 0xc4 */ +#define RT1016_VCM_SLOW (0x1 << 6) +#define RT1016_VCM_SLOW_BIT 6 + +/* 0xc5 */ +#define RT1016_PLL_M_MAX 0xf +#define RT1016_PLL_M_MASK (RT1016_PLL_M_MAX << 12) +#define RT1016_PLL_M_SFT 12 +#define RT1016_PLL_M_BP (0x1 << 11) +#define RT1016_PLL_M_BP_SFT 11 +#define RT1016_PLL_N_MAX 0x1ff +#define RT1016_PLL_N_MASK (RT1016_PLL_N_MAX << 0) +#define RT1016_PLL_N_SFT 0 + +/* 0xc6 */ +#define RT1016_PLL2_EN (0x1 << 15) +#define RT1016_PLL2_EN_BIT 15 +#define RT1016_PLL_K_BP (0x1 << 5) +#define RT1016_PLL_K_BP_SFT 5 +#define RT1016_PLL_K_MAX 0x1f +#define RT1016_PLL_K_MASK (RT1016_PLL_K_MAX) +#define RT1016_PLL_K_SFT 0 + +/* 0xcf */ +#define RT1016_PWR_BG_1_2 (0x1 << 12) +#define RT1016_PWR_BG_1_2_BIT 12 +#define RT1016_PWR_MBIAS_BG (0x1 << 11) +#define RT1016_PWR_MBIAS_BG_BIT 11 +#define RT1016_PWR_PLL (0x1 << 9) +#define RT1016_PWR_PLL_BIT 9 +#define RT1016_PWR_BASIC (0x1 << 8) +#define RT1016_PWR_BASIC_BIT 8 +#define RT1016_PWR_CLSD (0x1 << 7) +#define RT1016_PWR_CLSD_BIT 7 +#define RT1016_PWR_25M (0x1 << 6) +#define RT1016_PWR_25M_BIT 6 +#define RT1016_PWR_DACL (0x1 << 4) +#define RT1016_PWR_DACL_BIT 4 +#define RT1016_PWR_DACR (0x1 << 3) +#define RT1016_PWR_DACR_BIT 3 +#define RT1016_PWR_LDO2 (0x1 << 2) +#define RT1016_PWR_LDO2_BIT 2 +#define RT1016_PWR_VREF (0x1 << 1) +#define RT1016_PWR_VREF_BIT 1 +#define RT1016_PWR_MBIAS (0x1 << 0) +#define RT1016_PWR_MBIAS_BIT 0 + +/* System Clock Source */ +enum { + RT1016_SCLK_S_MCLK, + RT1016_SCLK_S_PLL, +}; + +/* PLL1 Source */ +enum { + RT1016_PLL_S_MCLK, + RT1016_PLL_S_BCLK, +}; + +enum { + RT1016_AIF1, + RT1016_AIFS, +}; + +struct rt1016_priv { + struct snd_soc_component *component; + struct regmap *regmap; + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int master; + int pll_src; + int pll_in; + int pll_out; +}; + +#endif /* __RT1016_H__ */ diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c new file mode 100644 index 0000000000..7295f44c77 --- /dev/null +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -0,0 +1,824 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1017-sdca-sdw.c -- rt1017 SDCA ALSA SoC amplifier audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt1017-sdca-sdw.h" + +static bool rt1017_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f55: + case 0x3206: + case 0xc000: + case 0xc001: + case 0xc022: + case 0xc030: + case 0xc104: + case 0xc10b: + case 0xc10c: + case 0xc110: + case 0xc112: + case 0xc300: + case 0xc301: + case 0xc318: + case 0xc325 ... 0xc328: + case 0xc331: + case 0xc340: + case 0xc350 ... 0xc351: + case 0xc500: + case 0xc502: + case 0xc504: + case 0xc507: + case 0xc509: + case 0xc510: + case 0xc512: + case 0xc518: + case 0xc51b: + case 0xc51d: + case 0xc520: + case 0xc540 ... 0xc542: + case 0xc550 ... 0xc552: + case 0xc600: + case 0xc602: + case 0xc612: + case 0xc622: + case 0xc632: + case 0xc642: + case 0xc651: + case 0xca00: + case 0xca09 ... 0xca0c: + case 0xca0e ... 0xca0f: + case 0xca10 ... 0xca11: + case 0xca16 ... 0xca17: + case 0xcb00: + case 0xcc00: + case 0xcc02: + case 0xd017: + case 0xd01a ... 0xd01c: + case 0xd101: + case 0xd20c: + case 0xd300: + case 0xd370: + case 0xd500: + case 0xd545 ... 0xd548: + case 0xd5a5 ... 0xd5a8: + case 0xd5aa ... 0xd5ad: + case 0xda04 ... 0xda07: + case 0xda09 ... 0xda0a: + case 0xda0c ... 0xda0f: + case 0xda11 ... 0xda14: + case 0xda16 ... 0xda19: + case 0xdab6 ... 0xdabb: + case 0xdb09 ... 0xdb0a: + case 0xdb14: + + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, + RT1017_SDCA_CTL_FU_MUTE, 0x01): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22, + RT1017_SDCA_CTL_BYPASS, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29, + RT1017_SDCA_CTL_PROT_STAT, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0): + return true; + default: + return false; + } +} + +static bool rt1017_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f55: + case 0xc000: + case 0xc022: + case 0xc351: + case 0xc518: + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29, + RT1017_SDCA_CTL_PROT_STAT, 0): + return true; + default: + return false; + } +} + +static const struct reg_sequence rt1017_blind_write[] = { + { 0xc001, 0x43 }, + { 0x2f55, 0x02 }, + { 0x3206, 0x80 }, + { 0x005f, 0x7f }, + { 0xd101, 0xa0 }, + { 0xc112, 0xc0 }, + { 0xc104, 0xaa }, + { 0xc110, 0x59 }, + { 0xc112, 0xc0 }, + { 0xc340, 0x80 }, + { 0xd017, 0x2c }, + { 0xd01a, 0xc8 }, + { 0xd01b, 0xcf }, + { 0xd01c, 0x0c }, + { 0xd20c, 0x14 }, + { 0xdb09, 0x0f }, + { 0xdb0a, 0x7f }, + { 0xdb14, 0x03 }, + { 0xcb00, 0x31 }, + { 0xc318, 0x44 }, + { 0xc325, 0xce }, + { 0xc326, 0x13 }, + { 0xc327, 0x5f }, + { 0xc328, 0xf3 }, + { 0xc350, 0xe1 }, + { 0xc351, 0x88 }, + { 0xc030, 0x14 }, + { 0xc331, 0xf2 }, + { 0xc551, 0x0f }, + { 0xc552, 0xff }, + { 0xc651, 0xc0 }, + { 0xc550, 0xd0 }, + { 0xc612, 0x00 }, + { 0xc622, 0x00 }, + { 0xc632, 0x00 }, + { 0xc642, 0x00 }, + { 0xc602, 0xf0 }, + { 0xc600, 0xd0 }, + { 0xcc02, 0x78 }, + { 0xcc00, 0x90 }, + { 0xc300, 0x3f }, + { 0xc301, 0x1d }, + { 0xc10b, 0x2e }, + { 0xc10c, 0x36 }, + + { 0xd5a5, 0x00 }, + { 0xd5a6, 0x6a }, + { 0xd5a7, 0xaa }, + { 0xd5a8, 0xaa }, + { 0xd5aa, 0x00 }, + { 0xd5ab, 0x16 }, + { 0xd5ac, 0xdb }, + { 0xd5ad, 0x6d }, + { 0xd545, 0x09 }, + { 0xd546, 0x30 }, + { 0xd547, 0xf0 }, + { 0xd548, 0xf0 }, + { 0xd500, 0x20 }, + { 0xc504, 0x3f }, + { 0xc540, 0x00 }, + { 0xc541, 0x0a }, + { 0xc542, 0x1a }, + { 0xc512, 0x00 }, + { 0xc520, 0x40 }, + { 0xc51b, 0x7f }, + { 0xc51d, 0x0f }, + { 0xc500, 0x40 }, + { 0xc502, 0xde }, + { 0xc507, 0x05 }, + { 0xc509, 0x05 }, + { 0xc510, 0x40 }, + { 0xc518, 0xc0 }, + { 0xc500, 0xc0 }, + + { 0xda0c, 0x00 }, + { 0xda0d, 0x0b }, + { 0xda0e, 0x55 }, + { 0xda0f, 0x55 }, + { 0xda04, 0x00 }, + { 0xda05, 0x51 }, + { 0xda06, 0xeb }, + { 0xda07, 0x85 }, + { 0xca16, 0x0f }, + { 0xca17, 0x00 }, + { 0xda09, 0x5d }, + { 0xda0a, 0xc0 }, + { 0xda11, 0x26 }, + { 0xda12, 0x66 }, + { 0xda13, 0x66 }, + { 0xda14, 0x66 }, + { 0xda16, 0x79 }, + { 0xda17, 0x99 }, + { 0xda18, 0x99 }, + { 0xda19, 0x99 }, + { 0xca09, 0x00 }, + { 0xca0a, 0x07 }, + { 0xca0b, 0x89 }, + { 0xca0c, 0x61 }, + { 0xca0e, 0x00 }, + { 0xca0f, 0x03 }, + { 0xca10, 0xc4 }, + { 0xca11, 0xb0 }, + { 0xdab6, 0x00 }, + { 0xdab7, 0x01 }, + { 0xdab8, 0x00 }, + { 0xdab9, 0x00 }, + { 0xdaba, 0x00 }, + { 0xdabb, 0x00 }, + { 0xd017, 0x0e }, + { 0xca00, 0xcd }, + { 0xc022, 0x84 }, +}; + +#define RT1017_MAX_REG_NUM 0x4108ffff + +static const struct regmap_config rt1017_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1017_sdca_readable_register, + .volatile_reg = rt1017_sdca_volatile_register, + .max_register = RT1017_MAX_REG_NUM, + .reg_defaults = rt1017_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1017_sdca_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt1017_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists + * port = 1 for AMP playback + * port = 2 for IV capture + */ + prop->source_ports = BIT(2); /* BITMAP: 00000100 */ + prop->sink_ports = BIT(1); /* BITMAP: 00000010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 64; + + return 0; +} + +static int rt1017_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); + + if (rt1017->hw_init) + return 0; + + if (rt1017->first_hw_init) { + regcache_cache_only(rt1017->regmap, false); + regcache_cache_bypass(rt1017->regmap, true); + } else { + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + /* sw reset */ + regmap_write(rt1017->regmap, 0xc000, 0x02); + + /* initial settings - blind write */ + regmap_multi_reg_write(rt1017->regmap, rt1017_blind_write, + ARRAY_SIZE(rt1017_blind_write)); + + if (rt1017->first_hw_init) { + regcache_cache_bypass(rt1017->regmap, false); + regcache_mark_dirty(rt1017->regmap); + } else + rt1017->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt1017->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "hw_init complete\n"); + return 0; +} + +static int rt1017_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt1017->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt1017->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt1017_sdca_io_init(&slave->dev, slave); +} + +static const char * const rt1017_rx_data_ch_select[] = { + "Bypass", + "CN1", + "CN2", + "CN3", + "CN4", + "(1+2)/2", + "(1+3)/2", + "(1+4)/2", + "(2+3)/2", + "(2+4)/2", + "(3+4)/2", +}; + +static SOC_ENUM_SINGLE_DECL(rt1017_rx_data_ch_enum, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), + 0, rt1017_rx_data_ch_select); + +static const struct snd_kcontrol_new rt1017_sdca_controls[] = { + /* UDMPU Cluster Selection */ + SOC_ENUM("RX Channel Select", rt1017_rx_data_ch_enum), +}; + +static const struct snd_kcontrol_new rt1017_sto_dac = + SOC_DAPM_SINGLE("Switch", + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, RT1017_SDCA_CTL_FU_MUTE, 0x1), + 0, 1, 1); + +static int rt1017_sdca_pde23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt1017->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt1017->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + default: + break; + } + return 0; +} + +static int rt1017_sdca_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt1017->regmap, RT1017_PWM_TRIM_1, + RT1017_PWM_FREQ_CTL_SRC_SEL_MASK, RT1017_PWM_FREQ_CTL_SRC_SEL_REG); + regmap_write(rt1017->regmap, RT1017_CLASSD_INT_1, 0x10); + break; + default: + break; + } + + return 0; +} + +static int rt1017_sdca_feedback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x08); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x09); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt1017_sdca_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0, + rt1017_sdca_feedback_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Digital Interface */ + SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1017_sto_dac), + + /* Output Lines */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1017_sdca_classd_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPO"), + + SND_SOC_DAPM_SUPPLY("PDE23", SND_SOC_NOPM, 0, 0, + rt1017_sdca_pde23_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA("I Sense", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("V Sense", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SIGGEN("I Gen"), + SND_SOC_DAPM_SIGGEN("V Gen"), +}; + +static const struct snd_soc_dapm_route rt1017_sdca_dapm_routes[] = { + + { "DAC", "Switch", "DP1RX" }, + { "CLASS D", NULL, "DAC" }, + { "CLASS D", NULL, "PDE23" }, + { "SPO", NULL, "CLASS D" }, + + { "I Sense", NULL, "I Gen" }, + { "V Sense", NULL, "V Gen" }, + { "I Sense", NULL, "PDE23" }, + { "V Sense", NULL, "PDE23" }, + { "DP2TX", NULL, "I Sense" }, + { "DP2TX", NULL, "V Sense" }, +}; + +static struct sdw_slave_ops rt1017_sdca_slave_ops = { + .read_prop = rt1017_sdca_read_prop, + .update_status = rt1017_sdca_update_status, +}; + +static int rt1017_sdca_component_probe(struct snd_soc_component *component) +{ + int ret; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static void rt1017_sdca_component_remove(struct snd_soc_component *component) +{ + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1017->regmap, true); +} + +static const struct snd_soc_component_driver soc_sdca_component_rt1017 = { + .probe = rt1017_sdca_component_probe, + .remove = rt1017_sdca_component_remove, + .controls = rt1017_sdca_controls, + .num_controls = ARRAY_SIZE(rt1017_sdca_controls), + .dapm_widgets = rt1017_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1017_sdca_dapm_widgets), + .dapm_routes = rt1017_sdca_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1017_sdca_dapm_routes), + .endianness = 1, +}; + +static int rt1017_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt1017_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + int retval, port, num_channels, ch_mask; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt1017->sdw_slave) + return -EINVAL; + + /* SoundWire specific configuration */ + /* port 1 for playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + direction = SDW_DATA_DIR_TX; + port = 2; + } + + num_channels = params_channels(params); + ch_mask = (1 << num_channels) - 1; + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = num_channels; + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + port_config.ch_mask = ch_mask; + port_config.num = port; + + dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n", + params_rate(params), num_channels, snd_pcm_format_width(params_format(params)), + direction, ch_mask, port); + + retval = sdw_stream_add_slave(rt1017->sdw_slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT1017_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT1017_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT1017_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT1017_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + regmap_write(rt1017->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0), + sampling_rate); + + return 0; +} + +static int rt1017_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt1017->sdw_slave) + return -EINVAL; + + sdw_stream_remove_slave(rt1017->sdw_slave, sdw_stream); + return 0; +} + +static const struct snd_soc_dai_ops rt1017_sdca_ops = { + .hw_params = rt1017_sdca_pcm_hw_params, + .hw_free = rt1017_sdca_pcm_hw_free, + .set_stream = rt1017_sdca_set_sdw_stream, + .shutdown = rt1017_sdca_shutdown, +}; + +#define RT1017_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT1017_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver rt1017_sdca_dai[] = { + { + .name = "rt1017-aif", + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 1, + .rates = RT1017_STEREO_RATES, + .formats = RT1017_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 1, + .rates = RT1017_STEREO_RATES, + .formats = RT1017_FORMATS, + }, + .ops = &rt1017_sdca_ops, + }, +}; + +static int rt1017_sdca_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt1017_sdca_priv *rt1017; + int ret; + + rt1017 = devm_kzalloc(dev, sizeof(*rt1017), GFP_KERNEL); + if (!rt1017) + return -ENOMEM; + + dev_set_drvdata(dev, rt1017); + rt1017->sdw_slave = slave; + rt1017->regmap = regmap; + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt1017->hw_init = false; + rt1017->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_sdca_component_rt1017, + rt1017_sdca_dai, + ARRAY_SIZE(rt1017_sdca_dai)); + + return ret; +} + +static int rt1017_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt1017_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt1017_sdca_init(&slave->dev, regmap, slave); +} + +static int rt1017_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev); + + if (rt1017->first_hw_init) + pm_runtime_disable(&slave->dev); + + return 0; +} + +static const struct sdw_device_id rt1017_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1017, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt1017_sdca_id); + +static int __maybe_unused rt1017_sdca_dev_suspend(struct device *dev) +{ + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); + + if (!rt1017->hw_init) + return 0; + + regcache_cache_only(rt1017->regmap, true); + + return 0; +} + +#define RT1017_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt1017_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt1017->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT1017_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt1017->regmap, false); + regcache_sync(rt1017->regmap); + + return 0; +} + +static const struct dev_pm_ops rt1017_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt1017_sdca_sdw_driver = { + .driver = { + .name = "rt1017-sdca", + .owner = THIS_MODULE, + .pm = &rt1017_sdca_pm, + }, + .probe = rt1017_sdca_sdw_probe, + .remove = rt1017_sdca_sdw_remove, + .ops = &rt1017_sdca_slave_ops, + .id_table = rt1017_sdca_id, +}; +module_sdw_driver(rt1017_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT1017 driver SDCA SDW"); +MODULE_AUTHOR("Derek Fang "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1017-sdca-sdw.h b/sound/soc/codecs/rt1017-sdca-sdw.h new file mode 100644 index 0000000000..4932b5dbe3 --- /dev/null +++ b/sound/soc/codecs/rt1017-sdca-sdw.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1017-sdca-sdw.h -- RT1017 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT1017_SDW_H__ +#define __RT1017_SDW_H__ + +#include +#include +#include +#include +#include + +/* RT1017 SDCA Control - function number */ +#define FUNC_NUM_SMART_AMP 0x04 + +/* RT1017 SDCA entity */ +#define RT1017_SDCA_ENT_PDE23 0x31 +#define RT1017_SDCA_ENT_PDE22 0x33 +#define RT1017_SDCA_ENT_CS21 0x21 +#define RT1017_SDCA_ENT_SAPU29 0x29 +#define RT1017_SDCA_ENT_XU22 0x22 +#define RT1017_SDCA_ENT_FU 0x03 +#define RT1017_SDCA_ENT_UDMPU21 0x02 + +/* RT1017 SDCA control */ +#define RT1017_SDCA_CTL_FS_INDEX 0x10 +#define RT1017_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT1017_SDCA_CTL_PROT_STAT 0x11 +#define RT1017_SDCA_CTL_BYPASS 0x01 +#define RT1017_SDCA_CTL_FU_MUTE 0x01 +#define RT1017_SDCA_CTL_FU_VOLUME 0x02 +#define RT1017_SDCA_CTL_UDMPU_CLUSTER 0x10 + + +#define RT1017_CLASSD_INT_1 0xd300 +#define RT1017_PWM_TRIM_1 0xd370 + + +#define RT1017_PWM_FREQ_CTL_SRC_SEL_MASK (0x3 << 2) +#define RT1017_PWM_FREQ_CTL_SRC_SEL_EFUSE (0x2 << 2) +#define RT1017_PWM_FREQ_CTL_SRC_SEL_REG (0x0 << 2) + +enum { + RT1017_SDCA_RATE_44100HZ = 0x8, + RT1017_SDCA_RATE_48000HZ = 0x9, + RT1017_SDCA_RATE_96000HZ = 0xb, + RT1017_SDCA_RATE_192000HZ = 0xd, +}; + +struct rt1017_sdca_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct sdw_slave *sdw_slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; +}; + +static const struct reg_default rt1017_sdca_reg_defaults[] = { + { 0x3206, 0x00 }, + { 0xc001, 0x43 }, + { 0xc030, 0x54 }, + { 0xc104, 0x8a }, + { 0xc10b, 0x2f }, + { 0xc10c, 0x2f }, + { 0xc110, 0x49 }, + { 0xc112, 0x10 }, + { 0xc300, 0xff }, + { 0xc301, 0xdd }, + { 0xc318, 0x40 }, + { 0xc325, 0x00 }, + { 0xc326, 0x00 }, + { 0xc327, 0x00 }, + { 0xc328, 0x02 }, + { 0xc331, 0xb2 }, + { 0xc340, 0x02 }, + { 0xc350, 0x21 }, + { 0xc500, 0x00 }, + { 0xc502, 0x00 }, + { 0xc504, 0x3f }, + { 0xc507, 0x1f }, + { 0xc509, 0x1f }, + { 0xc510, 0x40 }, + { 0xc512, 0x00 }, + { 0xc518, 0x02 }, + { 0xc51b, 0x7f }, + { 0xc51d, 0x0f }, + { 0xc520, 0x00 }, + { 0xc540, 0x80 }, + { 0xc541, 0x00 }, + { 0xc542, 0x0a }, + { 0xc550, 0x80 }, + { 0xc551, 0x0f }, + { 0xc552, 0xff }, + { 0xc600, 0x10 }, + { 0xc602, 0x83 }, + { 0xc612, 0x40 }, + { 0xc622, 0x40 }, + { 0xc632, 0x40 }, + { 0xc642, 0x40 }, + { 0xc651, 0x00 }, + { 0xca00, 0xc1 }, + { 0xca09, 0x00 }, + { 0xca0a, 0x51 }, + { 0xca0b, 0xeb }, + { 0xca0c, 0x85 }, + { 0xca0e, 0x00 }, + { 0xca0f, 0x10 }, + { 0xca10, 0x62 }, + { 0xca11, 0x4d }, + { 0xca16, 0x0f }, + { 0xca17, 0x00 }, + { 0xcb00, 0x10 }, + { 0xcc00, 0x10 }, + { 0xcc02, 0x0b }, + { 0xd017, 0x09 }, + { 0xd01a, 0x00 }, + { 0xd01b, 0x00 }, + { 0xd01c, 0x00 }, + { 0xd101, 0xa0 }, + { 0xd20c, 0x14 }, + { 0xd300, 0x0f }, + { 0xd370, 0x18 }, + { 0xd500, 0x00 }, + { 0xd545, 0x0b }, + { 0xd546, 0xf9 }, + { 0xd547, 0xb2 }, + { 0xd548, 0xa9 }, + { 0xd5a5, 0x00 }, + { 0xd5a6, 0x00 }, + { 0xd5a7, 0x00 }, + { 0xd5a8, 0x00 }, + { 0xd5aa, 0x00 }, + { 0xd5ab, 0x00 }, + { 0xd5ac, 0x00 }, + { 0xd5ad, 0x00 }, + { 0xda04, 0x03 }, + { 0xda05, 0x33 }, + { 0xda06, 0x33 }, + { 0xda07, 0x33 }, + { 0xda09, 0x5d }, + { 0xda0a, 0xc0 }, + { 0xda0c, 0x00 }, + { 0xda0d, 0x01 }, + { 0xda0e, 0x5d }, + { 0xda0f, 0x86 }, + { 0xda11, 0x20 }, + { 0xda12, 0x00 }, + { 0xda13, 0x00 }, + { 0xda14, 0x00 }, + { 0xda16, 0x7f }, + { 0xda17, 0xff }, + { 0xda18, 0xff }, + { 0xda19, 0xff }, + { 0xdab6, 0x00 }, + { 0xdab7, 0x01 }, + { 0xdab8, 0x00 }, + { 0xdab9, 0x01 }, + { 0xdaba, 0x00 }, + { 0xdabb, 0x01 }, + { 0xdb09, 0x0f }, + { 0xdb0a, 0xff }, + { 0xdb14, 0x00 }, + + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21, + RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, + RT1017_SDCA_CTL_FU_MUTE, 0x01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22, + RT1017_SDCA_CTL_BYPASS, 0), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21, + RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22, + RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, +}; + +#endif /* __RT1017_SDW_H__ */ diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c new file mode 100644 index 0000000000..ceb8baa6a2 --- /dev/null +++ b/sound/soc/codecs/rt1019.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1019.c -- RT1019 ALSA SoC audio amplifier driver +// Author: Jack Yu +// +// Copyright(c) 2021 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt1019.h" + +static const struct reg_default rt1019_reg[] = { + { 0x0000, 0x00 }, + { 0x0011, 0x04 }, + { 0x0013, 0x00 }, + { 0x0019, 0x30 }, + { 0x001b, 0x01 }, + { 0x005c, 0x00 }, + { 0x005e, 0x10 }, + { 0x005f, 0xec }, + { 0x0061, 0x10 }, + { 0x0062, 0x19 }, + { 0x0066, 0x08 }, + { 0x0100, 0x80 }, + { 0x0100, 0x51 }, + { 0x0102, 0x23 }, + { 0x0311, 0x00 }, + { 0x0312, 0x3e }, + { 0x0313, 0x86 }, + { 0x0400, 0x03 }, + { 0x0401, 0x02 }, + { 0x0402, 0x01 }, + { 0x0504, 0xff }, + { 0x0505, 0x24 }, + { 0x0b00, 0x50 }, + { 0x0b01, 0xc3 }, +}; + +static bool rt1019_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1019_PWR_STRP_2: + case RT1019_VER_ID: + case RT1019_VEND_ID_1: + case RT1019_VEND_ID_2: + case RT1019_DEV_ID_1: + case RT1019_DEV_ID_2: + return true; + + default: + return false; + } +} + +static bool rt1019_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1019_RESET: + case RT1019_IDS_CTRL: + case RT1019_ASEL_CTRL: + case RT1019_PWR_STRP_2: + case RT1019_BEEP_TONE: + case RT1019_VER_ID: + case RT1019_VEND_ID_1: + case RT1019_VEND_ID_2: + case RT1019_DEV_ID_1: + case RT1019_DEV_ID_2: + case RT1019_SDB_CTRL: + case RT1019_CLK_TREE_1: + case RT1019_CLK_TREE_2: + case RT1019_CLK_TREE_3: + case RT1019_PLL_1: + case RT1019_PLL_2: + case RT1019_PLL_3: + case RT1019_TDM_1: + case RT1019_TDM_2: + case RT1019_TDM_3: + case RT1019_DMIX_MONO_1: + case RT1019_DMIX_MONO_2: + case RT1019_BEEP_1: + case RT1019_BEEP_2: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9525, 75, 0); + +static const char * const rt1019_din_source_select[] = { + "Left", + "Right", + "Left + Right average", +}; + +static SOC_ENUM_SINGLE_DECL(rt1019_mono_lr_sel, RT1019_IDS_CTRL, 0, + rt1019_din_source_select); + +static const struct snd_kcontrol_new rt1019_snd_controls[] = { + SOC_SINGLE_TLV("DAC Playback Volume", RT1019_DMIX_MONO_1, 0, + 127, 0, dac_vol_tlv), + SOC_ENUM("Mono LR Select", rt1019_mono_lr_sel), +}; + +static int r1019_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write(component, RT1019_SDB_CTRL, 0xb); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write(component, RT1019_SDB_CTRL, 0xa); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt1019_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, + r1019_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("SPO"), +}; + +static const struct snd_soc_dapm_route rt1019_dapm_routes[] = { + { "DAC", NULL, "AIFRX" }, + { "SPO", NULL, "DAC" }, +}; + +static int rt1019_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component); + int pre_div, bclk_ms, frame_size; + unsigned int val_len = 0, sys_div_da_filter = 0; + unsigned int sys_dac_osr = 0, sys_fifo_clk = 0; + unsigned int sys_clk_cal = 0, sys_asrc_in = 0; + + rt1019->lrck = params_rate(params); + pre_div = rl6231_get_clk_info(rt1019->sysclk, rt1019->lrck); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + + bclk_ms = frame_size > 32; + rt1019->bclk = rt1019->lrck * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt1019->bclk, rt1019->lrck); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (pre_div) { + case 0: + sys_div_da_filter = RT1019_SYS_DIV_DA_FIL_DIV1; + sys_dac_osr = RT1019_SYS_DA_OSR_DIV1; + sys_asrc_in = RT1019_ASRC_256FS_DIV1; + sys_fifo_clk = RT1019_SEL_FIFO_DIV1; + sys_clk_cal = RT1019_SEL_CLK_CAL_DIV1; + break; + case 1: + sys_div_da_filter = RT1019_SYS_DIV_DA_FIL_DIV2; + sys_dac_osr = RT1019_SYS_DA_OSR_DIV2; + sys_asrc_in = RT1019_ASRC_256FS_DIV2; + sys_fifo_clk = RT1019_SEL_FIFO_DIV2; + sys_clk_cal = RT1019_SEL_CLK_CAL_DIV2; + break; + case 3: + sys_div_da_filter = RT1019_SYS_DIV_DA_FIL_DIV4; + sys_dac_osr = RT1019_SYS_DA_OSR_DIV4; + sys_asrc_in = RT1019_ASRC_256FS_DIV4; + sys_fifo_clk = RT1019_SEL_FIFO_DIV4; + sys_clk_cal = RT1019_SEL_CLK_CAL_DIV4; + break; + default: + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len = RT1019_I2S_DL_20; + break; + case 24: + val_len = RT1019_I2S_DL_24; + break; + case 32: + val_len = RT1019_I2S_DL_32; + break; + case 8: + val_len = RT1019_I2S_DL_8; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1019_TDM_2, RT1019_I2S_DL_MASK, + val_len); + snd_soc_component_update_bits(component, RT1019_CLK_TREE_1, + RT1019_SEL_FIFO_MASK, sys_fifo_clk); + snd_soc_component_update_bits(component, RT1019_CLK_TREE_2, + RT1019_SYS_DIV_DA_FIL_MASK | RT1019_SYS_DA_OSR_MASK | + RT1019_ASRC_256FS_MASK, sys_div_da_filter | sys_dac_osr | + sys_asrc_in); + snd_soc_component_update_bits(component, RT1019_CLK_TREE_3, + RT1019_SEL_CLK_CAL_MASK, sys_clk_cal); + + return 0; +} + +static int rt1019_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + unsigned int reg_val = 0, reg_val2 = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val2 |= RT1019_TDM_BCLK_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT1019_I2S_DF_LEFT; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT1019_I2S_DF_PCM_A_R; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT1019_I2S_DF_PCM_B_R; + break; + + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1019_TDM_2, + RT1019_I2S_DF_MASK, reg_val); + snd_soc_component_update_bits(component, RT1019_TDM_1, + RT1019_TDM_BCLK_MASK, reg_val2); + + return 0; +} + +static int rt1019_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt1019->sysclk && clk_id == rt1019->sysclk_src) + return 0; + + switch (clk_id) { + case RT1019_SCLK_S_BCLK: + reg_val |= RT1019_CLK_SYS_PRE_SEL_BCLK; + break; + + case RT1019_SCLK_S_PLL: + reg_val |= RT1019_CLK_SYS_PRE_SEL_PLL; + break; + + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + rt1019->sysclk = freq; + rt1019->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + snd_soc_component_update_bits(component, RT1019_CLK_TREE_1, + RT1019_CLK_SYS_PRE_SEL_MASK, reg_val); + + return 0; +} + +static int rt1019_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + rt1019->pll_in = 0; + rt1019->pll_out = 0; + return 0; + } + + if (source == rt1019->pll_src && freq_in == rt1019->pll_in && + freq_out == rt1019->pll_out) + return 0; + + switch (source) { + case RT1019_PLL_S_BCLK: + snd_soc_component_update_bits(component, RT1019_CLK_TREE_1, + RT1019_PLL_SRC_MASK, RT1019_PLL_SRC_SEL_BCLK); + break; + + case RT1019_PLL_S_RC25M: + snd_soc_component_update_bits(component, RT1019_CLK_TREE_1, + RT1019_PLL_SRC_MASK, RT1019_PLL_SRC_SEL_RC); + break; + + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_update_bits(component, RT1019_PWR_STRP_2, + RT1019_AUTO_BITS_SEL_MASK | RT1019_AUTO_CLK_SEL_MASK, + RT1019_AUTO_BITS_SEL_MANU | RT1019_AUTO_CLK_SEL_MANU); + snd_soc_component_update_bits(component, RT1019_PLL_1, + RT1019_PLL_M_MASK | RT1019_PLL_M_BP_MASK | RT1019_PLL_Q_8_8_MASK, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1019_PLL_M_SFT) | + (pll_code.m_bp << RT1019_PLL_M_BP_SFT) | + ((pll_code.n_code >> 8) & RT1019_PLL_Q_8_8_MASK)); + snd_soc_component_update_bits(component, RT1019_PLL_2, + RT1019_PLL_Q_7_0_MASK, pll_code.n_code & RT1019_PLL_Q_7_0_MASK); + snd_soc_component_update_bits(component, RT1019_PLL_3, + RT1019_PLL_K_MASK, pll_code.k_code); + + rt1019->pll_in = freq_in; + rt1019->pll_out = freq_out; + rt1019->pll_src = source; + + return 0; +} + +static int rt1019_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int cn = 0, cl = 0, rx_slotnum; + int ret = 0, first_bit; + + switch (slots) { + case 4: + cn = RT1019_I2S_TX_4CH; + break; + case 6: + cn = RT1019_I2S_TX_6CH; + break; + case 8: + cn = RT1019_I2S_TX_8CH; + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + cl = RT1019_TDM_CL_20; + break; + case 24: + cl = RT1019_TDM_CL_24; + break; + case 32: + cl = RT1019_TDM_CL_32; + break; + case 8: + cl = RT1019_TDM_CL_8; + break; + case 16: + break; + default: + return -EINVAL; + } + + /* Rx slot configuration */ + rx_slotnum = hweight_long(rx_mask); + if (rx_slotnum != 1) { + ret = -EINVAL; + dev_err(component->dev, "too many rx slots or zero slot\n"); + goto _set_tdm_err_; + } + /* This is an assumption that the system sends stereo audio to the + * amplifier typically. And the stereo audio is placed in slot 0/2/4/6 + * as the starting slot. The users could select the channel from + * L/R/L+R by "Mono LR Select" control. + */ + first_bit = __ffs(rx_mask); + switch (first_bit) { + case 0: + case 2: + case 4: + case 6: + snd_soc_component_update_bits(component, + RT1019_TDM_3, + RT1019_TDM_I2S_TX_L_DAC1_1_MASK | + RT1019_TDM_I2S_TX_R_DAC1_1_MASK, + (first_bit << RT1019_TDM_I2S_TX_L_DAC1_1_SFT) | + ((first_bit + 1) << RT1019_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + case 1: + case 3: + case 5: + case 7: + snd_soc_component_update_bits(component, + RT1019_TDM_3, + RT1019_TDM_I2S_TX_L_DAC1_1_MASK | + RT1019_TDM_I2S_TX_R_DAC1_1_MASK, + ((first_bit - 1) << RT1019_TDM_I2S_TX_L_DAC1_1_SFT) | + (first_bit << RT1019_TDM_I2S_TX_R_DAC1_1_SFT)); + break; + default: + ret = -EINVAL; + goto _set_tdm_err_; + } + + snd_soc_component_update_bits(component, RT1019_TDM_1, + RT1019_TDM_CL_MASK, cl); + snd_soc_component_update_bits(component, RT1019_TDM_2, + RT1019_I2S_CH_TX_MASK, cn); + +_set_tdm_err_: + return ret; +} + +static int rt1019_probe(struct snd_soc_component *component) +{ + struct rt1019_priv *rt1019 = snd_soc_component_get_drvdata(component); + + rt1019->component = component; + snd_soc_component_write(component, RT1019_SDB_CTRL, 0xa); + + return 0; +} + +#define RT1019_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT1019_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt1019_aif_dai_ops = { + .hw_params = rt1019_hw_params, + .set_fmt = rt1019_set_dai_fmt, + .set_sysclk = rt1019_set_dai_sysclk, + .set_pll = rt1019_set_dai_pll, + .set_tdm_slot = rt1019_set_tdm_slot, +}; + +static struct snd_soc_dai_driver rt1019_dai[] = { + { + .name = "rt1019-aif", + .id = 0, + .playback = { + .stream_name = "AIF Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1019_STEREO_RATES, + .formats = RT1019_FORMATS, + }, + .ops = &rt1019_aif_dai_ops, + } +}; + +static const struct snd_soc_component_driver soc_component_dev_rt1019 = { + .probe = rt1019_probe, + .controls = rt1019_snd_controls, + .num_controls = ARRAY_SIZE(rt1019_snd_controls), + .dapm_widgets = rt1019_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1019_dapm_widgets), + .dapm_routes = rt1019_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1019_dapm_routes), + .endianness = 1, +}; + +static const struct regmap_config rt1019_regmap = { + .reg_bits = 16, + .val_bits = 8, + .use_single_read = true, + .use_single_write = true, + .max_register = RT1019_BEEP_2, + .volatile_reg = rt1019_volatile_register, + .readable_reg = rt1019_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt1019_reg, + .num_reg_defaults = ARRAY_SIZE(rt1019_reg), +}; + +static const struct i2c_device_id rt1019_i2c_id[] = { + { "rt1019", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1019_i2c_id); + +static const struct of_device_id rt1019_of_match[] __maybe_unused = { + { .compatible = "realtek,rt1019", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1019_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt1019_acpi_match[] = { + { "10EC1019", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt1019_acpi_match); +#endif + +static int rt1019_i2c_probe(struct i2c_client *i2c) +{ + struct rt1019_priv *rt1019; + int ret; + unsigned int val, val2, dev_id; + + rt1019 = devm_kzalloc(&i2c->dev, sizeof(struct rt1019_priv), + GFP_KERNEL); + if (!rt1019) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1019); + + rt1019->regmap = devm_regmap_init_i2c(i2c, &rt1019_regmap); + if (IS_ERR(rt1019->regmap)) { + ret = PTR_ERR(rt1019->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt1019->regmap, RT1019_DEV_ID_1, &val); + regmap_read(rt1019->regmap, RT1019_DEV_ID_2, &val2); + dev_id = val << 8 | val2; + if (dev_id != RT1019_DEVICE_ID_VAL && dev_id != RT1019_DEVICE_ID_VAL2) { + dev_err(&i2c->dev, + "Device with ID register 0x%x is not rt1019\n", dev_id); + return -ENODEV; + } + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1019, rt1019_dai, ARRAY_SIZE(rt1019_dai)); +} + +static struct i2c_driver rt1019_i2c_driver = { + .driver = { + .name = "rt1019", + .of_match_table = of_match_ptr(rt1019_of_match), + .acpi_match_table = ACPI_PTR(rt1019_acpi_match), + }, + .probe = rt1019_i2c_probe, + .id_table = rt1019_i2c_id, +}; +module_i2c_driver(rt1019_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1019 driver"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1019.h b/sound/soc/codecs/rt1019.h new file mode 100644 index 0000000000..48ba15efb4 --- /dev/null +++ b/sound/soc/codecs/rt1019.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1019.h -- RT1019 ALSA SoC audio amplifier driver + * + * Copyright(c) 2021 Realtek Semiconductor Corp. + */ + +#ifndef __RT1019_H__ +#define __RT1019_H__ + +#define RT1019_DEVICE_ID_VAL 0x1019 +#define RT1019_DEVICE_ID_VAL2 0x6731 + +#define RT1019_RESET 0x0000 +#define RT1019_IDS_CTRL 0x0011 +#define RT1019_ASEL_CTRL 0x0013 +#define RT1019_PWR_STRP_2 0x0019 +#define RT1019_BEEP_TONE 0x001b +#define RT1019_VER_ID 0x005c +#define RT1019_VEND_ID_1 0x005e +#define RT1019_VEND_ID_2 0x005f +#define RT1019_DEV_ID_1 0x0061 +#define RT1019_DEV_ID_2 0x0062 +#define RT1019_SDB_CTRL 0x0066 +#define RT1019_CLK_TREE_1 0x0100 +#define RT1019_CLK_TREE_2 0x0101 +#define RT1019_CLK_TREE_3 0x0102 +#define RT1019_PLL_1 0x0311 +#define RT1019_PLL_2 0x0312 +#define RT1019_PLL_3 0x0313 +#define RT1019_TDM_1 0x0400 +#define RT1019_TDM_2 0x0401 +#define RT1019_TDM_3 0x0402 +#define RT1019_DMIX_MONO_1 0x0504 +#define RT1019_DMIX_MONO_2 0x0505 +#define RT1019_BEEP_1 0x0b00 +#define RT1019_BEEP_2 0x0b01 + +/* 0x0019 Power On Strap Control-2 */ +#define RT1019_AUTO_BITS_SEL_MASK (0x1 << 5) +#define RT1019_AUTO_BITS_SEL_AUTO (0x1 << 5) +#define RT1019_AUTO_BITS_SEL_MANU (0x0 << 5) +#define RT1019_AUTO_CLK_SEL_MASK (0x1 << 4) +#define RT1019_AUTO_CLK_SEL_AUTO (0x1 << 4) +#define RT1019_AUTO_CLK_SEL_MANU (0x0 << 4) + +/* 0x0100 Clock Tree Control-1 */ +#define RT1019_CLK_SYS_PRE_SEL_MASK (0x1 << 7) +#define RT1019_CLK_SYS_PRE_SEL_SFT 7 +#define RT1019_CLK_SYS_PRE_SEL_BCLK (0x0 << 7) +#define RT1019_CLK_SYS_PRE_SEL_PLL (0x1 << 7) +#define RT1019_PLL_SRC_MASK (0x1 << 4) +#define RT1019_PLL_SRC_SFT 4 +#define RT1019_PLL_SRC_SEL_BCLK (0x0 << 4) +#define RT1019_PLL_SRC_SEL_RC (0x1 << 4) +#define RT1019_SEL_FIFO_MASK (0x3 << 2) +#define RT1019_SEL_FIFO_DIV1 (0x0 << 2) +#define RT1019_SEL_FIFO_DIV2 (0x1 << 2) +#define RT1019_SEL_FIFO_DIV4 (0x2 << 2) + +/* 0x0101 clock tree control-2 */ +#define RT1019_SYS_DIV_DA_FIL_MASK (0x7 << 5) +#define RT1019_SYS_DIV_DA_FIL_DIV1 (0x2 << 5) +#define RT1019_SYS_DIV_DA_FIL_DIV2 (0x3 << 5) +#define RT1019_SYS_DIV_DA_FIL_DIV4 (0x4 << 5) +#define RT1019_SYS_DA_OSR_MASK (0x3 << 2) +#define RT1019_SYS_DA_OSR_DIV1 (0x0 << 2) +#define RT1019_SYS_DA_OSR_DIV2 (0x1 << 2) +#define RT1019_SYS_DA_OSR_DIV4 (0x2 << 2) +#define RT1019_ASRC_256FS_MASK 0x3 +#define RT1019_ASRC_256FS_DIV1 0x0 +#define RT1019_ASRC_256FS_DIV2 0x1 +#define RT1019_ASRC_256FS_DIV4 0x2 + +/* 0x0102 clock tree control-3 */ +#define RT1019_SEL_CLK_CAL_MASK (0x3 << 6) +#define RT1019_SEL_CLK_CAL_DIV1 (0x0 << 6) +#define RT1019_SEL_CLK_CAL_DIV2 (0x1 << 6) +#define RT1019_SEL_CLK_CAL_DIV4 (0x2 << 6) + +/* 0x0311 PLL-1 */ +#define RT1019_PLL_M_MASK (0xf << 4) +#define RT1019_PLL_M_SFT 4 +#define RT1019_PLL_M_BP_MASK (0x1 << 1) +#define RT1019_PLL_M_BP_SFT 1 +#define RT1019_PLL_Q_8_8_MASK (0x1) + +/* 0x0312 PLL-2 */ +#define RT1019_PLL_Q_7_0_MASK 0xff + +/* 0x0313 PLL-3 */ +#define RT1019_PLL_K_MASK 0x1f + +/* 0x0400 TDM Control-1 */ +#define RT1019_TDM_BCLK_MASK (0x1 << 6) +#define RT1019_TDM_BCLK_NORM (0x0 << 6) +#define RT1019_TDM_BCLK_INV (0x1 << 6) +#define RT1019_TDM_CL_MASK (0x7) +#define RT1019_TDM_CL_8 (0x4) +#define RT1019_TDM_CL_32 (0x3) +#define RT1019_TDM_CL_24 (0x2) +#define RT1019_TDM_CL_20 (0x1) +#define RT1019_TDM_CL_16 (0x0) + +/* 0x0401 TDM Control-2 */ +#define RT1019_I2S_CH_TX_MASK (0x3 << 6) +#define RT1019_I2S_CH_TX_SFT 6 +#define RT1019_I2S_TX_2CH (0x0 << 6) +#define RT1019_I2S_TX_4CH (0x1 << 6) +#define RT1019_I2S_TX_6CH (0x2 << 6) +#define RT1019_I2S_TX_8CH (0x3 << 6) +#define RT1019_I2S_DF_MASK (0x7 << 3) +#define RT1019_I2S_DF_SFT 3 +#define RT1019_I2S_DF_I2S (0x0 << 3) +#define RT1019_I2S_DF_LEFT (0x1 << 3) +#define RT1019_I2S_DF_PCM_A_R (0x2 << 3) +#define RT1019_I2S_DF_PCM_B_R (0x3 << 3) +#define RT1019_I2S_DF_PCM_A_F (0x6 << 3) +#define RT1019_I2S_DF_PCM_B_F (0x7 << 3) +#define RT1019_I2S_DL_MASK 0x7 +#define RT1019_I2S_DL_SFT 0 +#define RT1019_I2S_DL_16 0x0 +#define RT1019_I2S_DL_20 0x1 +#define RT1019_I2S_DL_24 0x2 +#define RT1019_I2S_DL_32 0x3 +#define RT1019_I2S_DL_8 0x4 + +/* TDM1 Control-3 (0x0402) */ +#define RT1019_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 4) +#define RT1019_TDM_I2S_TX_R_DAC1_1_MASK 0x7 +#define RT1019_TDM_I2S_TX_L_DAC1_1_SFT 4 +#define RT1019_TDM_I2S_TX_R_DAC1_1_SFT 0 + +/* System Clock Source */ +enum { + RT1019_SCLK_S_BCLK, + RT1019_SCLK_S_PLL, +}; + +/* PLL1 Source */ +enum { + RT1019_PLL_S_BCLK, + RT1019_PLL_S_RC25M, +}; + +enum { + RT1019_AIF1, + RT1019_AIFS +}; + +struct rt1019_priv { + struct snd_soc_component *component; + struct regmap *regmap; + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int pll_src; + int pll_in; + int pll_out; + unsigned int bclk_ratio; +}; + +#endif /* __RT1019_H__ */ diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c new file mode 100644 index 0000000000..80888cbcf4 --- /dev/null +++ b/sound/soc/codecs/rt1305.c @@ -0,0 +1,1179 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt1305.c -- RT1305 ALSA SoC amplifier component driver + * + * Copyright 2018 Realtek Semiconductor Corp. + * Author: Shuming Fan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt1305.h" + + +#define RT1305_PR_RANGE_BASE (0xff + 1) +#define RT1305_PR_SPACING 0x100 + +#define RT1305_PR_BASE (RT1305_PR_RANGE_BASE + (0 * RT1305_PR_SPACING)) + + +static const struct regmap_range_cfg rt1305_ranges[] = { + { + .name = "PR", + .range_min = RT1305_PR_BASE, + .range_max = RT1305_PR_BASE + 0xff, + .selector_reg = RT1305_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT1305_PRIV_DATA, + .window_len = 0x1, + }, +}; + + +static const struct reg_sequence init_list[] = { + + { RT1305_PR_BASE + 0xcf, 0x5548 }, + { RT1305_PR_BASE + 0x5d, 0x0442 }, + { RT1305_PR_BASE + 0xc1, 0x0320 }, + + { RT1305_POWER_STATUS, 0x0000 }, + + { RT1305_SPK_TEMP_PROTECTION_1, 0xd6de }, + { RT1305_SPK_TEMP_PROTECTION_2, 0x0707 }, + { RT1305_SPK_TEMP_PROTECTION_3, 0x4090 }, + + { RT1305_DAC_SET_1, 0xdfdf }, /* 4 ohm 2W */ + { RT1305_ADC_SET_3, 0x0219 }, + { RT1305_ADC_SET_1, 0x170f }, /* 0.2 ohm RSense*/ + +}; +#define RT1305_INIT_REG_LEN ARRAY_SIZE(init_list) + +struct rt1305_priv { + struct snd_soc_component *component; + struct regmap *regmap; + + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int master; + + int pll_src; + int pll_in; + int pll_out; +}; + +static const struct reg_default rt1305_reg[] = { + + { 0x04, 0x0400 }, + { 0x05, 0x0880 }, + { 0x06, 0x0000 }, + { 0x07, 0x3100 }, + { 0x08, 0x8000 }, + { 0x09, 0x0000 }, + { 0x0a, 0x087e }, + { 0x0b, 0x0020 }, + { 0x0c, 0x0802 }, + { 0x0d, 0x0020 }, + { 0x10, 0x1d1d }, + { 0x11, 0x1d1d }, + { 0x12, 0xffff }, + { 0x14, 0x000c }, + { 0x16, 0x1717 }, + { 0x17, 0x4000 }, + { 0x18, 0x0019 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x24, 0x0000 }, + { 0x26, 0x0000 }, + { 0x28, 0x0000 }, + { 0x2a, 0x4000 }, + { 0x2b, 0x3000 }, + { 0x2d, 0x6000 }, + { 0x2e, 0x0000 }, + { 0x2f, 0x8000 }, + { 0x32, 0x0000 }, + { 0x39, 0x0001 }, + { 0x3a, 0x0000 }, + { 0x3b, 0x1020 }, + { 0x3c, 0x0000 }, + { 0x3d, 0x0000 }, + { 0x3e, 0x4c00 }, + { 0x3f, 0x3000 }, + { 0x40, 0x000c }, + { 0x42, 0x0400 }, + { 0x46, 0xc22c }, + { 0x47, 0x0000 }, + { 0x4b, 0x0000 }, + { 0x4c, 0x0300 }, + { 0x4f, 0xf000 }, + { 0x50, 0xc200 }, + { 0x51, 0x1f1f }, + { 0x52, 0x01f0 }, + { 0x53, 0x407f }, + { 0x54, 0xffff }, + { 0x58, 0x4005 }, + { 0x5e, 0x0000 }, + { 0x5f, 0x0000 }, + { 0x60, 0xee13 }, + { 0x62, 0x0000 }, + { 0x63, 0x5f5f }, + { 0x64, 0x0040 }, + { 0x65, 0x4000 }, + { 0x66, 0x4004 }, + { 0x67, 0x0306 }, + { 0x68, 0x8c04 }, + { 0x69, 0xe021 }, + { 0x6a, 0x0000 }, + { 0x6c, 0xaaaa }, + { 0x70, 0x0333 }, + { 0x71, 0x3330 }, + { 0x72, 0x3333 }, + { 0x73, 0x3300 }, + { 0x74, 0x0000 }, + { 0x75, 0x0000 }, + { 0x76, 0x0000 }, + { 0x7a, 0x0003 }, + { 0x7c, 0x10ec }, + { 0x7e, 0x6251 }, + { 0x80, 0x0800 }, + { 0x81, 0x4000 }, + { 0x82, 0x0000 }, + { 0x90, 0x7a01 }, + { 0x91, 0x8431 }, + { 0x92, 0x0180 }, + { 0x93, 0x0000 }, + { 0x94, 0x0000 }, + { 0x95, 0x0000 }, + { 0x96, 0x0000 }, + { 0x97, 0x0000 }, + { 0x98, 0x0000 }, + { 0x99, 0x0000 }, + { 0x9a, 0x0000 }, + { 0x9b, 0x0000 }, + { 0x9c, 0x0000 }, + { 0x9d, 0x0000 }, + { 0x9e, 0x0000 }, + { 0x9f, 0x0000 }, + { 0xa0, 0x0000 }, + { 0xb0, 0x8200 }, + { 0xb1, 0x00ff }, + { 0xb2, 0x0008 }, + { 0xc0, 0x0200 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xc3, 0x0000 }, + { 0xc4, 0x0000 }, + { 0xc5, 0x0000 }, + { 0xc6, 0x0000 }, + { 0xc7, 0x0000 }, + { 0xc8, 0x0000 }, + { 0xc9, 0x0000 }, + { 0xca, 0x0200 }, + { 0xcb, 0x0000 }, + { 0xcc, 0x0000 }, + { 0xcd, 0x0000 }, + { 0xce, 0x0000 }, + { 0xcf, 0x0000 }, + { 0xd0, 0x0000 }, + { 0xd1, 0x0000 }, + { 0xd2, 0x0000 }, + { 0xd3, 0x0000 }, + { 0xd4, 0x0200 }, + { 0xd5, 0x0000 }, + { 0xd6, 0x0000 }, + { 0xd7, 0x0000 }, + { 0xd8, 0x0000 }, + { 0xd9, 0x0000 }, + { 0xda, 0x0000 }, + { 0xdb, 0x0000 }, + { 0xdc, 0x0000 }, + { 0xdd, 0x0000 }, + { 0xde, 0x0200 }, + { 0xdf, 0x0000 }, + { 0xe0, 0x0000 }, + { 0xe1, 0x0000 }, + { 0xe2, 0x0000 }, + { 0xe3, 0x0000 }, + { 0xe4, 0x0000 }, + { 0xe5, 0x0000 }, + { 0xe6, 0x0000 }, + { 0xe7, 0x0000 }, + { 0xe8, 0x0200 }, + { 0xe9, 0x0000 }, + { 0xea, 0x0000 }, + { 0xeb, 0x0000 }, + { 0xec, 0x0000 }, + { 0xed, 0x0000 }, + { 0xee, 0x0000 }, + { 0xef, 0x0000 }, + { 0xf0, 0x0000 }, + { 0xf1, 0x0000 }, + { 0xf2, 0x0200 }, + { 0xf3, 0x0000 }, + { 0xf4, 0x0000 }, + { 0xf5, 0x0000 }, + { 0xf6, 0x0000 }, + { 0xf7, 0x0000 }, + { 0xf8, 0x0000 }, + { 0xf9, 0x0000 }, + { 0xfa, 0x0000 }, + { 0xfb, 0x0000 }, +}; + +static int rt1305_reg_init(struct snd_soc_component *component) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + regmap_multi_reg_write(rt1305->regmap, init_list, RT1305_INIT_REG_LEN); + return 0; +} + +static bool rt1305_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt1305_ranges); i++) { + if (reg >= rt1305_ranges[i].range_min && + reg <= rt1305_ranges[i].range_max) { + return true; + } + } + + switch (reg) { + case RT1305_RESET: + case RT1305_SPDIF_IN_SET_1: + case RT1305_SPDIF_IN_SET_2: + case RT1305_SPDIF_IN_SET_3: + case RT1305_POWER_CTRL_2: + case RT1305_CLOCK_DETECT: + case RT1305_BIQUAD_SET_1: + case RT1305_BIQUAD_SET_2: + case RT1305_EQ_SET_2: + case RT1305_SPK_TEMP_PROTECTION_0: + case RT1305_SPK_TEMP_PROTECTION_2: + case RT1305_SPK_DC_DETECT_1: + case RT1305_SILENCE_DETECT: + case RT1305_VERSION_ID: + case RT1305_VENDOR_ID: + case RT1305_DEVICE_ID: + case RT1305_EFUSE_1: + case RT1305_EFUSE_3: + case RT1305_DC_CALIB_1: + case RT1305_DC_CALIB_3: + case RT1305_DAC_OFFSET_1: + case RT1305_DAC_OFFSET_2: + case RT1305_DAC_OFFSET_3: + case RT1305_DAC_OFFSET_4: + case RT1305_DAC_OFFSET_5: + case RT1305_DAC_OFFSET_6: + case RT1305_DAC_OFFSET_7: + case RT1305_DAC_OFFSET_8: + case RT1305_DAC_OFFSET_9: + case RT1305_DAC_OFFSET_10: + case RT1305_DAC_OFFSET_11: + case RT1305_TRIM_1: + case RT1305_TRIM_2: + return true; + + default: + return false; + } +} + +static bool rt1305_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt1305_ranges); i++) { + if (reg >= rt1305_ranges[i].range_min && + reg <= rt1305_ranges[i].range_max) { + return true; + } + } + + switch (reg) { + case RT1305_RESET: + case RT1305_CLK_1 ... RT1305_CAL_EFUSE_CLOCK: + case RT1305_PLL0_1 ... RT1305_PLL1_2: + case RT1305_MIXER_CTRL_1: + case RT1305_MIXER_CTRL_2: + case RT1305_DAC_SET_1: + case RT1305_DAC_SET_2: + case RT1305_ADC_SET_1: + case RT1305_ADC_SET_2: + case RT1305_ADC_SET_3: + case RT1305_PATH_SET: + case RT1305_SPDIF_IN_SET_1: + case RT1305_SPDIF_IN_SET_2: + case RT1305_SPDIF_IN_SET_3: + case RT1305_SPDIF_OUT_SET_1: + case RT1305_SPDIF_OUT_SET_2: + case RT1305_SPDIF_OUT_SET_3: + case RT1305_I2S_SET_1: + case RT1305_I2S_SET_2: + case RT1305_PBTL_MONO_MODE_SRC: + case RT1305_MANUALLY_I2C_DEVICE: + case RT1305_POWER_STATUS: + case RT1305_POWER_CTRL_1: + case RT1305_POWER_CTRL_2: + case RT1305_POWER_CTRL_3: + case RT1305_POWER_CTRL_4: + case RT1305_POWER_CTRL_5: + case RT1305_CLOCK_DETECT: + case RT1305_BIQUAD_SET_1: + case RT1305_BIQUAD_SET_2: + case RT1305_ADJUSTED_HPF_1: + case RT1305_ADJUSTED_HPF_2: + case RT1305_EQ_SET_1: + case RT1305_EQ_SET_2: + case RT1305_SPK_TEMP_PROTECTION_0: + case RT1305_SPK_TEMP_PROTECTION_1: + case RT1305_SPK_TEMP_PROTECTION_2: + case RT1305_SPK_TEMP_PROTECTION_3: + case RT1305_SPK_DC_DETECT_1: + case RT1305_SPK_DC_DETECT_2: + case RT1305_LOUDNESS: + case RT1305_THERMAL_FOLD_BACK_1: + case RT1305_THERMAL_FOLD_BACK_2: + case RT1305_SILENCE_DETECT ... RT1305_SPK_EXCURSION_LIMITER_7: + case RT1305_VERSION_ID: + case RT1305_VENDOR_ID: + case RT1305_DEVICE_ID: + case RT1305_EFUSE_1: + case RT1305_EFUSE_2: + case RT1305_EFUSE_3: + case RT1305_DC_CALIB_1: + case RT1305_DC_CALIB_2: + case RT1305_DC_CALIB_3: + case RT1305_DAC_OFFSET_1 ... RT1305_DAC_OFFSET_14: + case RT1305_TRIM_1: + case RT1305_TRIM_2: + case RT1305_TUNE_INTERNAL_OSC: + case RT1305_BIQUAD1_H0_L_28_16 ... RT1305_BIQUAD3_A2_R_15_0: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9435, 37, 0); + +static const char * const rt1305_rx_data_ch_select[] = { + "LR", + "RL", + "Copy L", + "Copy R", +}; + +static SOC_ENUM_SINGLE_DECL(rt1305_rx_data_ch_enum, RT1305_I2S_SET_2, 2, + rt1305_rx_data_ch_select); + +static void rt1305_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT1305_RESET, 0); +} + +static const struct snd_kcontrol_new rt1305_snd_controls[] = { + SOC_DOUBLE_TLV("DAC Playback Volume", RT1305_DAC_SET_1, + 8, 0, 0xff, 0, dac_vol_tlv), + + /* I2S Data Channel Selection */ + SOC_ENUM("RX Channel Select", rt1305_rx_data_ch_enum), +}; + +static int rt1305_is_rc_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + unsigned int val; + + val = snd_soc_component_read(component, RT1305_CLK_1); + + if (rt1305->sysclk_src == RT1305_FS_SYS_PRE_S_PLL1 && + (val & RT1305_SEL_PLL_SRC_2_RCCLK)) + return 1; + else + return 0; +} + +static int rt1305_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + if (rt1305->sysclk_src == RT1305_FS_SYS_PRE_S_PLL1) + return 1; + else + return 0; +} + +static int rt1305_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT1305_POWER_CTRL_1, + RT1305_POW_PDB_JD_MASK, RT1305_POW_PDB_JD); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT1305_POWER_CTRL_1, + RT1305_POW_PDB_JD_MASK, 0); + usleep_range(150000, 200000); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_kcontrol_new rt1305_sto_dac_l = + SOC_DAPM_SINGLE("Switch", RT1305_DAC_SET_2, + RT1305_DVOL_MUTE_L_EN_SFT, 1, 1); + +static const struct snd_kcontrol_new rt1305_sto_dac_r = + SOC_DAPM_SINGLE("Switch", RT1305_DAC_SET_2, + RT1305_DVOL_MUTE_R_EN_SFT, 1, 1); + +static const struct snd_soc_dapm_widget rt1305_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL0", RT1305_POWER_CTRL_1, + RT1305_POW_PLL0_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT1305_POWER_CTRL_1, + RT1305_POW_PLL1_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS", RT1305_POWER_CTRL_1, + RT1305_POW_MBIAS_LV_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG MBIAS", RT1305_POWER_CTRL_1, + RT1305_POW_BG_MBIAS_LV_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO2", RT1305_POWER_CTRL_1, + RT1305_POW_LDO2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG2", RT1305_POWER_CTRL_1, + RT1305_POW_BG2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO2 IB2", RT1305_POWER_CTRL_1, + RT1305_POW_LDO2_IB2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", RT1305_POWER_CTRL_1, + RT1305_POW_VREF_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF1", RT1305_POWER_CTRL_1, + RT1305_POW_VREF1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF2", RT1305_POWER_CTRL_1, + RT1305_POW_VREF2_BIT, 0, NULL, 0), + + + SND_SOC_DAPM_SUPPLY("DISC VREF", RT1305_POWER_CTRL_2, + RT1305_POW_DISC_VREF_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("FASTB VREF", RT1305_POWER_CTRL_2, + RT1305_POW_FASTB_VREF_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ULTRA FAST VREF", RT1305_POWER_CTRL_2, + RT1305_POW_ULTRA_FAST_VREF_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CHOP DAC", RT1305_POWER_CTRL_2, + RT1305_POW_CKXEN_DAC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CKGEN DAC", RT1305_POWER_CTRL_2, + RT1305_POW_EN_CKGEN_DAC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLAMP", RT1305_POWER_CTRL_2, + RT1305_POW_CLAMP_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BUFL", RT1305_POWER_CTRL_2, + RT1305_POW_BUFL_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BUFR", RT1305_POWER_CTRL_2, + RT1305_POW_BUFR_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CKGEN ADC", RT1305_POWER_CTRL_2, + RT1305_POW_EN_CKGEN_ADC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC3 L", RT1305_POWER_CTRL_2, + RT1305_POW_ADC3_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC3 R", RT1305_POWER_CTRL_2, + RT1305_POW_ADC3_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TRIOSC", RT1305_POWER_CTRL_2, + RT1305_POW_TRIOSC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AVDD1", RT1305_POWER_CTRL_2, + RT1305_POR_AVDD1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AVDD2", RT1305_POWER_CTRL_2, + RT1305_POR_AVDD2_BIT, 0, NULL, 0), + + + SND_SOC_DAPM_SUPPLY("VSENSE R", RT1305_POWER_CTRL_3, + RT1305_POW_VSENSE_RCH_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VSENSE L", RT1305_POWER_CTRL_3, + RT1305_POW_VSENSE_LCH_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ISENSE R", RT1305_POWER_CTRL_3, + RT1305_POW_ISENSE_RCH_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ISENSE L", RT1305_POWER_CTRL_3, + RT1305_POW_ISENSE_LCH_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("POR AVDD1", RT1305_POWER_CTRL_3, + RT1305_POW_POR_AVDD1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("POR AVDD2", RT1305_POWER_CTRL_3, + RT1305_POW_POR_AVDD2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VCM 6172", RT1305_POWER_CTRL_3, + RT1305_EN_VCM_6172_BIT, 0, NULL, 0), + + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("DAC L Power", RT1305_POWER_CTRL_2, + RT1305_POW_DAC1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R Power", RT1305_POWER_CTRL_2, + RT1305_POW_DAC1_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1305_sto_dac_l), + SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1305_sto_dac_r), + + /* Output Lines */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1305_classd_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), +}; + +static const struct snd_soc_dapm_route rt1305_dapm_routes[] = { + + { "DAC", NULL, "AIF1RX" }, + + { "DAC", NULL, "PLL0", rt1305_is_rc_clk_from_pll }, + { "DAC", NULL, "PLL1", rt1305_is_sys_clk_from_pll }, + + { "DAC", NULL, "MBIAS" }, + { "DAC", NULL, "BG MBIAS" }, + { "DAC", NULL, "LDO2" }, + { "DAC", NULL, "BG2" }, + { "DAC", NULL, "LDO2 IB2" }, + { "DAC", NULL, "VREF" }, + { "DAC", NULL, "VREF1" }, + { "DAC", NULL, "VREF2" }, + + { "DAC", NULL, "DISC VREF" }, + { "DAC", NULL, "FASTB VREF" }, + { "DAC", NULL, "ULTRA FAST VREF" }, + { "DAC", NULL, "CHOP DAC" }, + { "DAC", NULL, "CKGEN DAC" }, + { "DAC", NULL, "CLAMP" }, + { "DAC", NULL, "CKGEN ADC" }, + { "DAC", NULL, "TRIOSC" }, + { "DAC", NULL, "AVDD1" }, + { "DAC", NULL, "AVDD2" }, + + { "DAC", NULL, "POR AVDD1" }, + { "DAC", NULL, "POR AVDD2" }, + { "DAC", NULL, "VCM 6172" }, + + { "DAC L", "Switch", "DAC" }, + { "DAC R", "Switch", "DAC" }, + + { "DAC R", NULL, "VSENSE R" }, + { "DAC L", NULL, "VSENSE L" }, + { "DAC R", NULL, "ISENSE R" }, + { "DAC L", NULL, "ISENSE L" }, + { "DAC L", NULL, "ADC3 L" }, + { "DAC R", NULL, "ADC3 R" }, + { "DAC L", NULL, "BUFL" }, + { "DAC R", NULL, "BUFR" }, + { "DAC L", NULL, "DAC L Power" }, + { "DAC R", NULL, "DAC R Power" }, + + { "CLASS D", NULL, "DAC L" }, + { "CLASS D", NULL, "DAC R" }, + + { "SPOL", NULL, "CLASS D" }, + { "SPOR", NULL, "CLASS D" }, +}; + +static int rt1305_get_clk_info(int sclk, int rate) +{ + int i; + static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +static int rt1305_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt1305->lrck = params_rate(params); + pre_div = rt1305_get_clk_info(rt1305->sysclk, rt1305->lrck); + if (pre_div < 0) { + dev_warn(component->dev, "Force using PLL "); + snd_soc_dai_set_pll(dai, 0, RT1305_PLL1_S_BCLK, + rt1305->lrck * 64, rt1305->lrck * 256); + snd_soc_dai_set_sysclk(dai, RT1305_FS_SYS_PRE_S_PLL1, + rt1305->lrck * 256, SND_SOC_CLOCK_IN); + pre_div = 0; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + bclk_ms = frame_size > 32; + rt1305->bclk = rt1305->lrck * (32 << bclk_ms); + + dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt1305->lrck, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + val_len |= RT1305_I2S_DL_SEL_16B; + break; + case 20: + val_len |= RT1305_I2S_DL_SEL_20B; + break; + case 24: + val_len |= RT1305_I2S_DL_SEL_24B; + break; + case 8: + val_len |= RT1305_I2S_DL_SEL_8B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT1305_AIF1: + mask_clk = RT1305_DIV_FS_SYS_MASK; + val_clk = pre_div << RT1305_DIV_FS_SYS_SFT; + snd_soc_component_update_bits(component, RT1305_I2S_SET_2, + RT1305_I2S_DL_SEL_MASK, + val_len); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1305_CLK_2, + mask_clk, val_clk); + + return 0; +} + +static int rt1305_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, reg1_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + reg_val |= RT1305_SEL_I2S_OUT_MODE_M; + rt1305->master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT1305_SEL_I2S_OUT_MODE_S; + rt1305->master = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg1_val |= RT1305_I2S_BCLK_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg1_val |= RT1305_I2S_DF_SEL_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg1_val |= RT1305_I2S_DF_SEL_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg1_val |= RT1305_I2S_DF_SEL_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT1305_AIF1: + snd_soc_component_update_bits(component, RT1305_I2S_SET_1, + RT1305_SEL_I2S_OUT_MODE_MASK, reg_val); + snd_soc_component_update_bits(component, RT1305_I2S_SET_2, + RT1305_I2S_DF_SEL_MASK | RT1305_I2S_BCLK_MASK, + reg1_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt1305_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt1305->sysclk && clk_id == rt1305->sysclk_src) + return 0; + + switch (clk_id) { + case RT1305_FS_SYS_PRE_S_MCLK: + reg_val |= RT1305_SEL_FS_SYS_PRE_MCLK; + snd_soc_component_update_bits(component, + RT1305_CLOCK_DETECT, RT1305_SEL_CLK_DET_SRC_MASK, + RT1305_SEL_CLK_DET_SRC_MCLK); + break; + case RT1305_FS_SYS_PRE_S_PLL1: + reg_val |= RT1305_SEL_FS_SYS_PRE_PLL; + break; + case RT1305_FS_SYS_PRE_S_RCCLK: + reg_val |= RT1305_SEL_FS_SYS_PRE_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT1305_CLK_1, + RT1305_SEL_FS_SYS_PRE_MASK, reg_val); + rt1305->sysclk = freq; + rt1305->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt1305_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt1305->pll_src && freq_in == rt1305->pll_in && + freq_out == rt1305->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt1305->pll_in = 0; + rt1305->pll_out = 0; + snd_soc_component_update_bits(component, RT1305_CLK_1, + RT1305_SEL_FS_SYS_PRE_MASK | RT1305_SEL_PLL_SRC_1_MASK, + RT1305_SEL_FS_SYS_PRE_PLL | RT1305_SEL_PLL_SRC_1_BCLK); + return 0; + } + + switch (source) { + case RT1305_PLL2_S_MCLK: + snd_soc_component_update_bits(component, RT1305_CLK_1, + RT1305_SEL_PLL_SRC_2_MASK | RT1305_SEL_PLL_SRC_1_MASK | + RT1305_DIV_PLL_SRC_2_MASK, + RT1305_SEL_PLL_SRC_2_MCLK | RT1305_SEL_PLL_SRC_1_PLL2); + snd_soc_component_update_bits(component, + RT1305_CLOCK_DETECT, RT1305_SEL_CLK_DET_SRC_MASK, + RT1305_SEL_CLK_DET_SRC_MCLK); + break; + case RT1305_PLL1_S_BCLK: + snd_soc_component_update_bits(component, + RT1305_CLK_1, RT1305_SEL_PLL_SRC_1_MASK, + RT1305_SEL_PLL_SRC_1_BCLK); + break; + case RT1305_PLL2_S_RCCLK: + snd_soc_component_update_bits(component, RT1305_CLK_1, + RT1305_SEL_PLL_SRC_2_MASK | RT1305_SEL_PLL_SRC_1_MASK | + RT1305_DIV_PLL_SRC_2_MASK, + RT1305_SEL_PLL_SRC_2_RCCLK | RT1305_SEL_PLL_SRC_1_PLL2); + freq_in = 98304000; + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT1305_PLL1_1, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1305_PLL_1_M_SFT) | + (pll_code.m_bp << RT1305_PLL_1_M_BYPASS_SFT) | + pll_code.n_code); + snd_soc_component_write(component, RT1305_PLL1_2, + pll_code.k_code); + + rt1305->pll_in = freq_in; + rt1305->pll_out = freq_out; + rt1305->pll_src = source; + + return 0; +} + +static int rt1305_probe(struct snd_soc_component *component) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + rt1305->component = component; + + /* initial settings */ + rt1305_reg_init(component); + + return 0; +} + +static void rt1305_remove(struct snd_soc_component *component) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + rt1305_reset(rt1305->regmap); +} + +#ifdef CONFIG_PM +static int rt1305_suspend(struct snd_soc_component *component) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1305->regmap, true); + regcache_mark_dirty(rt1305->regmap); + + return 0; +} + +static int rt1305_resume(struct snd_soc_component *component) +{ + struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1305->regmap, false); + regcache_sync(rt1305->regmap); + + return 0; +} +#else +#define rt1305_suspend NULL +#define rt1305_resume NULL +#endif + +#define RT1305_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT1305_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt1305_aif_dai_ops = { + .hw_params = rt1305_hw_params, + .set_fmt = rt1305_set_dai_fmt, +}; + +static struct snd_soc_dai_driver rt1305_dai[] = { + { + .name = "rt1305-aif", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1305_STEREO_RATES, + .formats = RT1305_FORMATS, + }, + .ops = &rt1305_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt1305 = { + .probe = rt1305_probe, + .remove = rt1305_remove, + .suspend = rt1305_suspend, + .resume = rt1305_resume, + .controls = rt1305_snd_controls, + .num_controls = ARRAY_SIZE(rt1305_snd_controls), + .dapm_widgets = rt1305_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1305_dapm_widgets), + .dapm_routes = rt1305_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1305_dapm_routes), + .set_sysclk = rt1305_set_component_sysclk, + .set_pll = rt1305_set_component_pll, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt1305_regmap = { + .reg_bits = 8, + .val_bits = 16, + .max_register = RT1305_MAX_REG + 1 + (ARRAY_SIZE(rt1305_ranges) * + RT1305_PR_SPACING), + .volatile_reg = rt1305_volatile_register, + .readable_reg = rt1305_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt1305_reg, + .num_reg_defaults = ARRAY_SIZE(rt1305_reg), + .ranges = rt1305_ranges, + .num_ranges = ARRAY_SIZE(rt1305_ranges), + .use_single_read = true, + .use_single_write = true, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id rt1305_of_match[] = { + { .compatible = "realtek,rt1305", }, + { .compatible = "realtek,rt1306", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt1305_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt1305_acpi_match[] = { + {"10EC1305", 0,}, + {"10EC1306", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt1305_acpi_match); +#endif + +static const struct i2c_device_id rt1305_i2c_id[] = { + { "rt1305", 0 }, + { "rt1306", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1305_i2c_id); + +static void rt1305_calibrate(struct rt1305_priv *rt1305) +{ + unsigned int valmsb, vallsb, offsetl, offsetr; + unsigned int rh, rl, rhl, r0ohm; + u64 r0l, r0r; + + regcache_cache_bypass(rt1305->regmap, true); + + rt1305_reset(rt1305->regmap); + regmap_write(rt1305->regmap, RT1305_ADC_SET_3, 0x0219); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xcf, 0x5548); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc1, 0x0320); + regmap_write(rt1305->regmap, RT1305_CLOCK_DETECT, 0x1000); + regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0600); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xffd0); + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0080); + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0dfe); + + /* Sin Gen */ + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x0442); + + regmap_write(rt1305->regmap, RT1305_CAL_EFUSE_CLOCK, 0xb000); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc3, 0xd4a0); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xcc, 0x00cc); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xc1, 0x0320); + regmap_write(rt1305->regmap, RT1305_POWER_STATUS, 0x0000); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0xffff); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfc20); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x06, 0x00c0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfca0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfce0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfcf0); + + /* EFUSE read */ + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0080); + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880); + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0880); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfce0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfca0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfc20); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x06, 0x0000); + regmap_write(rt1305->regmap, RT1305_EFUSE_1, 0x0000); + + regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_5, &valmsb); + regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_6, &vallsb); + offsetl = valmsb << 16 | vallsb; + regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_7, &valmsb); + regmap_read(rt1305->regmap, RT1305_DAC_OFFSET_8, &vallsb); + offsetr = valmsb << 16 | vallsb; + pr_info("DC offsetl=0x%x, offsetr=0x%x\n", offsetl, offsetr); + + /* R0 calibration */ + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x9542); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0xfcf0); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0xffff); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x1dfe); + regmap_write(rt1305->regmap, RT1305_SILENCE_DETECT, 0x0e13); + regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0650); + + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x50, 0x0064); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x51, 0x0770); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x52, 0xc30c); + regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0x8200); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xfb00); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xff80); + msleep(2000); + regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x55, &rh); + regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x56, &rl); + rhl = (rh << 16) | rl; + r0ohm = (rhl*10) / 33554432; + + pr_debug("Left_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl); + pr_info("Left channel %d.%dohm\n", (r0ohm/10), (r0ohm%10)); + + r0l = 562949953421312ULL; + if (rhl != 0) + do_div(r0l, rhl); + pr_debug("Left_r0 = 0x%llx\n", r0l); + + regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0x9200); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xfb00); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xd4, 0xff80); + msleep(2000); + regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x55, &rh); + regmap_read(rt1305->regmap, RT1305_PR_BASE + 0x56, &rl); + rhl = (rh << 16) | rl; + r0ohm = (rhl*10) / 33554432; + + pr_debug("Right_rhl = 0x%x rh=0x%x rl=0x%x\n", rhl, rh, rl); + pr_info("Right channel %d.%dohm\n", (r0ohm/10), (r0ohm%10)); + + r0r = 562949953421312ULL; + if (rhl != 0) + do_div(r0r, rhl); + pr_debug("Right_r0 = 0x%llx\n", r0r); + + regmap_write(rt1305->regmap, RT1305_SPK_TEMP_PROTECTION_1, 0xc2ec); + + if ((r0l > R0_UPPER) && (r0l < R0_LOWER) && + (r0r > R0_UPPER) && (r0r < R0_LOWER)) { + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x4e, + (r0l >> 16) & 0xffff); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x4f, + r0l & 0xffff); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xfe, + ((r0r >> 16) & 0xffff) | 0xf800); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0xfd, + r0r & 0xffff); + } else { + pr_err("R0 calibration failed\n"); + } + + /* restore some registers */ + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0dfe); + usleep_range(200000, 400000); + regmap_write(rt1305->regmap, RT1305_PR_BASE + 0x5d, 0x0442); + regmap_write(rt1305->regmap, RT1305_CLOCK_DETECT, 0x3000); + regmap_write(rt1305->regmap, RT1305_CLK_1, 0x0400); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_1, 0x0000); + regmap_write(rt1305->regmap, RT1305_CAL_EFUSE_CLOCK, 0x8000); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_2, 0x1020); + regmap_write(rt1305->regmap, RT1305_POWER_CTRL_3, 0x0000); + + regcache_cache_bypass(rt1305->regmap, false); +} + +static int rt1305_i2c_probe(struct i2c_client *i2c) +{ + struct rt1305_priv *rt1305; + int ret; + unsigned int val; + + rt1305 = devm_kzalloc(&i2c->dev, sizeof(struct rt1305_priv), + GFP_KERNEL); + if (rt1305 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1305); + + rt1305->regmap = devm_regmap_init_i2c(i2c, &rt1305_regmap); + if (IS_ERR(rt1305->regmap)) { + ret = PTR_ERR(rt1305->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt1305->regmap, RT1305_DEVICE_ID, &val); + if (val != RT1305_DEVICE_ID_NUM) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt1305\n", val); + return -ENODEV; + } + + rt1305_reset(rt1305->regmap); + rt1305_calibrate(rt1305); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1305, + rt1305_dai, ARRAY_SIZE(rt1305_dai)); +} + +static void rt1305_i2c_shutdown(struct i2c_client *client) +{ + struct rt1305_priv *rt1305 = i2c_get_clientdata(client); + + rt1305_reset(rt1305->regmap); +} + + +static struct i2c_driver rt1305_i2c_driver = { + .driver = { + .name = "rt1305", +#if defined(CONFIG_OF) + .of_match_table = rt1305_of_match, +#endif +#if defined(CONFIG_ACPI) + .acpi_match_table = ACPI_PTR(rt1305_acpi_match) +#endif + }, + .probe = rt1305_i2c_probe, + .shutdown = rt1305_i2c_shutdown, + .id_table = rt1305_i2c_id, +}; +module_i2c_driver(rt1305_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1305 amplifier driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1305.h b/sound/soc/codecs/rt1305.h new file mode 100644 index 0000000000..026f74eb68 --- /dev/null +++ b/sound/soc/codecs/rt1305.h @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * RT1305.h -- RT1305 ALSA SoC amplifier component driver + * + * Copyright 2018 Realtek Semiconductor Corp. + * Author: Shuming Fan + */ + +#ifndef _RT1305_H_ +#define _RT1305_H_ + +#define RT1305_DEVICE_ID_NUM 0x6251 + +#define RT1305_RESET 0x00 +#define RT1305_CLK_1 0x04 +#define RT1305_CLK_2 0x05 +#define RT1305_CLK_3 0x06 +#define RT1305_DFLL_REG 0x07 +#define RT1305_CAL_EFUSE_CLOCK 0x08 +#define RT1305_PLL0_1 0x0a +#define RT1305_PLL0_2 0x0b +#define RT1305_PLL1_1 0x0c +#define RT1305_PLL1_2 0x0d +#define RT1305_MIXER_CTRL_1 0x10 +#define RT1305_MIXER_CTRL_2 0x11 +#define RT1305_DAC_SET_1 0x12 +#define RT1305_DAC_SET_2 0x14 +#define RT1305_ADC_SET_1 0x16 +#define RT1305_ADC_SET_2 0x17 +#define RT1305_ADC_SET_3 0x18 +#define RT1305_PATH_SET 0x20 +#define RT1305_SPDIF_IN_SET_1 0x22 +#define RT1305_SPDIF_IN_SET_2 0x24 +#define RT1305_SPDIF_IN_SET_3 0x26 +#define RT1305_SPDIF_OUT_SET_1 0x28 +#define RT1305_SPDIF_OUT_SET_2 0x2a +#define RT1305_SPDIF_OUT_SET_3 0x2b +#define RT1305_I2S_SET_1 0x2d +#define RT1305_I2S_SET_2 0x2e +#define RT1305_PBTL_MONO_MODE_SRC 0x2f +#define RT1305_MANUALLY_I2C_DEVICE 0x32 +#define RT1305_POWER_STATUS 0x39 +#define RT1305_POWER_CTRL_1 0x3a +#define RT1305_POWER_CTRL_2 0x3b +#define RT1305_POWER_CTRL_3 0x3c +#define RT1305_POWER_CTRL_4 0x3d +#define RT1305_POWER_CTRL_5 0x3e +#define RT1305_CLOCK_DETECT 0x3f +#define RT1305_BIQUAD_SET_1 0x40 +#define RT1305_BIQUAD_SET_2 0x42 +#define RT1305_ADJUSTED_HPF_1 0x46 +#define RT1305_ADJUSTED_HPF_2 0x47 +#define RT1305_EQ_SET_1 0x4b +#define RT1305_EQ_SET_2 0x4c +#define RT1305_SPK_TEMP_PROTECTION_0 0x4f +#define RT1305_SPK_TEMP_PROTECTION_1 0x50 +#define RT1305_SPK_TEMP_PROTECTION_2 0x51 +#define RT1305_SPK_TEMP_PROTECTION_3 0x52 +#define RT1305_SPK_DC_DETECT_1 0x53 +#define RT1305_SPK_DC_DETECT_2 0x54 +#define RT1305_LOUDNESS 0x58 +#define RT1305_THERMAL_FOLD_BACK_1 0x5e +#define RT1305_THERMAL_FOLD_BACK_2 0x5f +#define RT1305_SILENCE_DETECT 0x60 +#define RT1305_ALC_DRC_1 0x62 +#define RT1305_ALC_DRC_2 0x63 +#define RT1305_ALC_DRC_3 0x64 +#define RT1305_ALC_DRC_4 0x65 +#define RT1305_PRIV_INDEX 0x6a +#define RT1305_PRIV_DATA 0x6c +#define RT1305_SPK_EXCURSION_LIMITER_7 0x76 +#define RT1305_VERSION_ID 0x7a +#define RT1305_VENDOR_ID 0x7c +#define RT1305_DEVICE_ID 0x7e +#define RT1305_EFUSE_1 0x80 +#define RT1305_EFUSE_2 0x81 +#define RT1305_EFUSE_3 0x82 +#define RT1305_DC_CALIB_1 0x90 +#define RT1305_DC_CALIB_2 0x91 +#define RT1305_DC_CALIB_3 0x92 +#define RT1305_DAC_OFFSET_1 0x93 +#define RT1305_DAC_OFFSET_2 0x94 +#define RT1305_DAC_OFFSET_3 0x95 +#define RT1305_DAC_OFFSET_4 0x96 +#define RT1305_DAC_OFFSET_5 0x97 +#define RT1305_DAC_OFFSET_6 0x98 +#define RT1305_DAC_OFFSET_7 0x99 +#define RT1305_DAC_OFFSET_8 0x9a +#define RT1305_DAC_OFFSET_9 0x9b +#define RT1305_DAC_OFFSET_10 0x9c +#define RT1305_DAC_OFFSET_11 0x9d +#define RT1305_DAC_OFFSET_12 0x9e +#define RT1305_DAC_OFFSET_13 0x9f +#define RT1305_DAC_OFFSET_14 0xa0 +#define RT1305_TRIM_1 0xb0 +#define RT1305_TRIM_2 0xb1 +#define RT1305_TUNE_INTERNAL_OSC 0xb2 +#define RT1305_BIQUAD1_H0_L_28_16 0xc0 +#define RT1305_BIQUAD3_A2_R_15_0 0xfb +#define RT1305_MAX_REG 0xff + +/* CLOCK-1 (0x04) */ +#define RT1305_SEL_PLL_SRC_2_MASK (0x1 << 15) +#define RT1305_SEL_PLL_SRC_2_SFT 15 +#define RT1305_SEL_PLL_SRC_2_MCLK (0x0 << 15) +#define RT1305_SEL_PLL_SRC_2_RCCLK (0x1 << 15) +#define RT1305_DIV_PLL_SRC_2_MASK (0x3 << 13) +#define RT1305_DIV_PLL_SRC_2_SFT 13 +#define RT1305_SEL_PLL_SRC_1_MASK (0x3 << 10) +#define RT1305_SEL_PLL_SRC_1_SFT 10 +#define RT1305_SEL_PLL_SRC_1_PLL2 (0x0 << 10) +#define RT1305_SEL_PLL_SRC_1_BCLK (0x1 << 10) +#define RT1305_SEL_PLL_SRC_1_DFLL (0x2 << 10) +#define RT1305_SEL_FS_SYS_PRE_MASK (0x3 << 8) +#define RT1305_SEL_FS_SYS_PRE_SFT 8 +#define RT1305_SEL_FS_SYS_PRE_MCLK (0x0 << 8) +#define RT1305_SEL_FS_SYS_PRE_PLL (0x1 << 8) +#define RT1305_SEL_FS_SYS_PRE_RCCLK (0x2 << 8) +#define RT1305_DIV_FS_SYS_MASK (0x7 << 4) +#define RT1305_DIV_FS_SYS_SFT 4 + +/* PLL1M/N/K Code-1 (0x0c) */ +#define RT1305_PLL_1_M_SFT 12 +#define RT1305_PLL_1_M_BYPASS_MASK (0x1 << 11) +#define RT1305_PLL_1_M_BYPASS_SFT 11 +#define RT1305_PLL_1_M_BYPASS (0x1 << 11) +#define RT1305_PLL_1_N_MASK (0x1ff << 0) + +/* DAC Setting (0x14) */ +#define RT1305_DVOL_MUTE_L_EN_SFT 15 +#define RT1305_DVOL_MUTE_R_EN_SFT 14 + +/* I2S Setting-1 (0x2d) */ +#define RT1305_SEL_I2S_OUT_MODE_MASK (0x1 << 15) +#define RT1305_SEL_I2S_OUT_MODE_SFT 15 +#define RT1305_SEL_I2S_OUT_MODE_S (0x0 << 15) +#define RT1305_SEL_I2S_OUT_MODE_M (0x1 << 15) + +/* I2S Setting-2 (0x2e) */ +#define RT1305_I2S_DF_SEL_MASK (0x3 << 12) +#define RT1305_I2S_DF_SEL_SFT 12 +#define RT1305_I2S_DF_SEL_I2S (0x0 << 12) +#define RT1305_I2S_DF_SEL_LEFT (0x1 << 12) +#define RT1305_I2S_DF_SEL_PCM_A (0x2 << 12) +#define RT1305_I2S_DF_SEL_PCM_B (0x3 << 12) +#define RT1305_I2S_DL_SEL_MASK (0x3 << 10) +#define RT1305_I2S_DL_SEL_SFT 10 +#define RT1305_I2S_DL_SEL_16B (0x0 << 10) +#define RT1305_I2S_DL_SEL_20B (0x1 << 10) +#define RT1305_I2S_DL_SEL_24B (0x2 << 10) +#define RT1305_I2S_DL_SEL_8B (0x3 << 10) +#define RT1305_I2S_BCLK_MASK (0x1 << 9) +#define RT1305_I2S_BCLK_SFT 9 +#define RT1305_I2S_BCLK_NORMAL (0x0 << 9) +#define RT1305_I2S_BCLK_INV (0x1 << 9) + +/* Power Control-1 (0x3a) */ +#define RT1305_POW_PDB_JD_MASK (0x1 << 12) +#define RT1305_POW_PDB_JD (0x1 << 12) +#define RT1305_POW_PDB_JD_BIT 12 +#define RT1305_POW_PLL0_EN (0x1 << 11) +#define RT1305_POW_PLL0_EN_BIT 11 +#define RT1305_POW_PLL1_EN (0x1 << 10) +#define RT1305_POW_PLL1_EN_BIT 10 +#define RT1305_POW_PDB_JD_POLARITY (0x1 << 9) +#define RT1305_POW_PDB_JD_POLARITY_BIT 9 +#define RT1305_POW_MBIAS_LV (0x1 << 8) +#define RT1305_POW_MBIAS_LV_BIT 8 +#define RT1305_POW_BG_MBIAS_LV (0x1 << 7) +#define RT1305_POW_BG_MBIAS_LV_BIT 7 +#define RT1305_POW_LDO2 (0x1 << 6) +#define RT1305_POW_LDO2_BIT 6 +#define RT1305_POW_BG2 (0x1 << 5) +#define RT1305_POW_BG2_BIT 5 +#define RT1305_POW_LDO2_IB2 (0x1 << 4) +#define RT1305_POW_LDO2_IB2_BIT 4 +#define RT1305_POW_VREF (0x1 << 3) +#define RT1305_POW_VREF_BIT 3 +#define RT1305_POW_VREF1 (0x1 << 2) +#define RT1305_POW_VREF1_BIT 2 +#define RT1305_POW_VREF2 (0x1 << 1) +#define RT1305_POW_VREF2_BIT 1 + +/* Power Control-2 (0x3b) */ +#define RT1305_POW_DISC_VREF (1 << 15) +#define RT1305_POW_DISC_VREF_BIT 15 +#define RT1305_POW_FASTB_VREF (1 << 14) +#define RT1305_POW_FASTB_VREF_BIT 14 +#define RT1305_POW_ULTRA_FAST_VREF (1 << 13) +#define RT1305_POW_ULTRA_FAST_VREF_BIT 13 +#define RT1305_POW_CKXEN_DAC (1 << 12) +#define RT1305_POW_CKXEN_DAC_BIT 12 +#define RT1305_POW_EN_CKGEN_DAC (1 << 11) +#define RT1305_POW_EN_CKGEN_DAC_BIT 11 +#define RT1305_POW_DAC1_L (1 << 10) +#define RT1305_POW_DAC1_L_BIT 10 +#define RT1305_POW_DAC1_R (1 << 9) +#define RT1305_POW_DAC1_R_BIT 9 +#define RT1305_POW_CLAMP (1 << 8) +#define RT1305_POW_CLAMP_BIT 8 +#define RT1305_POW_BUFL (1 << 7) +#define RT1305_POW_BUFL_BIT 7 +#define RT1305_POW_BUFR (1 << 6) +#define RT1305_POW_BUFR_BIT 6 +#define RT1305_POW_EN_CKGEN_ADC (1 << 5) +#define RT1305_POW_EN_CKGEN_ADC_BIT 5 +#define RT1305_POW_ADC3_L (1 << 4) +#define RT1305_POW_ADC3_L_BIT 4 +#define RT1305_POW_ADC3_R (1 << 3) +#define RT1305_POW_ADC3_R_BIT 3 +#define RT1305_POW_TRIOSC (1 << 2) +#define RT1305_POW_TRIOSC_BIT 2 +#define RT1305_POR_AVDD1 (1 << 1) +#define RT1305_POR_AVDD1_BIT 1 +#define RT1305_POR_AVDD2 (1 << 0) +#define RT1305_POR_AVDD2_BIT 0 + +/* Power Control-3 (0x3c) */ +#define RT1305_POW_VSENSE_RCH (1 << 15) +#define RT1305_POW_VSENSE_RCH_BIT 15 +#define RT1305_POW_VSENSE_LCH (1 << 14) +#define RT1305_POW_VSENSE_LCH_BIT 14 +#define RT1305_POW_ISENSE_RCH (1 << 13) +#define RT1305_POW_ISENSE_RCH_BIT 13 +#define RT1305_POW_ISENSE_LCH (1 << 12) +#define RT1305_POW_ISENSE_LCH_BIT 12 +#define RT1305_POW_POR_AVDD1 (1 << 11) +#define RT1305_POW_POR_AVDD1_BIT 11 +#define RT1305_POW_POR_AVDD2 (1 << 10) +#define RT1305_POW_POR_AVDD2_BIT 10 +#define RT1305_EN_K_HV (1 << 9) +#define RT1305_EN_K_HV_BIT 9 +#define RT1305_EN_PRE_K_HV (1 << 8) +#define RT1305_EN_PRE_K_HV_BIT 8 +#define RT1305_EN_EFUSE_1P8V (1 << 7) +#define RT1305_EN_EFUSE_1P8V_BIT 7 +#define RT1305_EN_EFUSE_5V (1 << 6) +#define RT1305_EN_EFUSE_5V_BIT 6 +#define RT1305_EN_VCM_6172 (1 << 5) +#define RT1305_EN_VCM_6172_BIT 5 +#define RT1305_POR_EFUSE (1 << 4) +#define RT1305_POR_EFUSE_BIT 4 + +/* Clock Detect (0x3f) */ +#define RT1305_SEL_CLK_DET_SRC_MASK (0x1 << 12) +#define RT1305_SEL_CLK_DET_SRC_SFT 12 +#define RT1305_SEL_CLK_DET_SRC_MCLK (0x0 << 12) +#define RT1305_SEL_CLK_DET_SRC_BCLK (0x1 << 12) + + +/* System Clock Source */ +enum { + RT1305_FS_SYS_PRE_S_MCLK, + RT1305_FS_SYS_PRE_S_PLL1, + RT1305_FS_SYS_PRE_S_RCCLK, /* 98.304M Hz */ +}; + +/* PLL Source 1/2 */ +enum { + RT1305_PLL1_S_BCLK, + RT1305_PLL2_S_MCLK, + RT1305_PLL2_S_RCCLK, /* 98.304M Hz */ +}; + +enum { + RT1305_AIF1, + RT1305_AIFS +}; + +#define R0_UPPER 0x2E8BA2 //5.5 ohm +#define R0_LOWER 0x666666 //2.5 ohm + +#endif /* end of _RT1305_H_ */ diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c new file mode 100644 index 0000000000..63d4abf964 --- /dev/null +++ b/sound/soc/codecs/rt1308-sdw.c @@ -0,0 +1,819 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt1308-sdw.c -- rt1308 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt1308.h" +#include "rt1308-sdw.h" + +static bool rt1308_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x2f01 ... 0x2f07: + case 0x3000 ... 0x3001: + case 0x3004 ... 0x3005: + case 0x3008: + case 0x300a: + case 0xc000 ... 0xcff3: + return true; + default: + return false; + } +} + +static bool rt1308_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01 ... 0x2f07: + case 0x3000 ... 0x3001: + case 0x3004 ... 0x3005: + case 0x3008: + case 0x300a: + case 0xc000: + case 0xc710: + case 0xcf01: + case 0xc860 ... 0xc863: + case 0xc870 ... 0xc873: + return true; + default: + return false; + } +} + +static const struct regmap_config rt1308_sdw_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1308_readable_register, + .volatile_reg = rt1308_volatile_register, + .max_register = 0xcfff, + .reg_defaults = rt1308_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +/* Bus clock frequency */ +#define RT1308_CLK_FREQ_9600000HZ 9600000 +#define RT1308_CLK_FREQ_12000000HZ 12000000 +#define RT1308_CLK_FREQ_6000000HZ 6000000 +#define RT1308_CLK_FREQ_4800000HZ 4800000 +#define RT1308_CLK_FREQ_2400000HZ 2400000 +#define RT1308_CLK_FREQ_12288000HZ 12288000 + +static int rt1308_clock_config(struct device *dev) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt1308->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT1308_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT1308_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT1308_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT1308_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT1308_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT1308_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt1308->regmap, 0xe0, value); + regmap_write(rt1308->regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +static int rt1308_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x00; /* BITMAP: 00010100 (not enable yet) */ + prop->sink_ports = 0x2; /* BITMAP: 00000010 */ + + /* for sink */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + dev_dbg(&slave->dev, "%s\n", __func__); + + return 0; +} + +static void rt1308_apply_calib_params(struct rt1308_sdw_priv *rt1308) +{ + unsigned int efuse_m_btl_l, efuse_m_btl_r, tmp; + unsigned int efuse_c_btl_l, efuse_c_btl_r; + + /* read efuse to apply calibration parameters */ + regmap_write(rt1308->regmap, 0xc7f0, 0x04); + regmap_write(rt1308->regmap, 0xc7f1, 0xfe); + msleep(100); + regmap_write(rt1308->regmap, 0xc7f0, 0x44); + msleep(20); + regmap_write(rt1308->regmap, 0xc240, 0x10); + + regmap_read(rt1308->regmap, 0xc861, &tmp); + efuse_m_btl_l = tmp; + regmap_read(rt1308->regmap, 0xc860, &tmp); + efuse_m_btl_l = efuse_m_btl_l | (tmp << 8); + regmap_read(rt1308->regmap, 0xc863, &tmp); + efuse_c_btl_l = tmp; + regmap_read(rt1308->regmap, 0xc862, &tmp); + efuse_c_btl_l = efuse_c_btl_l | (tmp << 8); + regmap_read(rt1308->regmap, 0xc871, &tmp); + efuse_m_btl_r = tmp; + regmap_read(rt1308->regmap, 0xc870, &tmp); + efuse_m_btl_r = efuse_m_btl_r | (tmp << 8); + regmap_read(rt1308->regmap, 0xc873, &tmp); + efuse_c_btl_r = tmp; + regmap_read(rt1308->regmap, 0xc872, &tmp); + efuse_c_btl_r = efuse_c_btl_r | (tmp << 8); + dev_dbg(&rt1308->sdw_slave->dev, "%s m_btl_l=0x%x, m_btl_r=0x%x\n", __func__, + efuse_m_btl_l, efuse_m_btl_r); + dev_dbg(&rt1308->sdw_slave->dev, "%s c_btl_l=0x%x, c_btl_r=0x%x\n", __func__, + efuse_c_btl_l, efuse_c_btl_r); +} + +static void rt1308_apply_bq_params(struct rt1308_sdw_priv *rt1308) +{ + unsigned int i, reg, data; + + for (i = 0; i < rt1308->bq_params_cnt; i += 3) { + reg = rt1308->bq_params[i] | (rt1308->bq_params[i + 1] << 8); + data = rt1308->bq_params[i + 2]; + regmap_write(rt1308->regmap, reg, data); + } +} + +static int rt1308_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + int ret = 0; + unsigned int tmp, hibernation_flag; + + if (rt1308->hw_init) + return 0; + + regcache_cache_only(rt1308->regmap, false); + if (rt1308->first_hw_init) + regcache_cache_bypass(rt1308->regmap, true); + + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + if (!rt1308->first_hw_init) + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + pm_runtime_get_noresume(&slave->dev); + + regmap_read(rt1308->regmap, 0xcf01, &hibernation_flag); + if ((hibernation_flag != 0x00) && rt1308->first_hw_init) + goto _preset_ready_; + + /* sw reset */ + regmap_write(rt1308->regmap, RT1308_SDW_RESET, 0); + + regmap_read(rt1308->regmap, 0xc710, &tmp); + rt1308->hw_ver = tmp; + dev_dbg(dev, "%s, hw_ver=0x%x\n", __func__, rt1308->hw_ver); + + /* initial settings */ + regmap_write(rt1308->regmap, 0xc103, 0xc0); + regmap_write(rt1308->regmap, 0xc030, 0x17); + regmap_write(rt1308->regmap, 0xc031, 0x81); + regmap_write(rt1308->regmap, 0xc032, 0x26); + regmap_write(rt1308->regmap, 0xc040, 0x80); + regmap_write(rt1308->regmap, 0xc041, 0x80); + regmap_write(rt1308->regmap, 0xc042, 0x06); + regmap_write(rt1308->regmap, 0xc052, 0x0a); + regmap_write(rt1308->regmap, 0xc080, 0x0a); + regmap_write(rt1308->regmap, 0xc060, 0x02); + regmap_write(rt1308->regmap, 0xc061, 0x75); + regmap_write(rt1308->regmap, 0xc062, 0x05); + regmap_write(rt1308->regmap, 0xc171, 0x07); + regmap_write(rt1308->regmap, 0xc173, 0x0d); + if (rt1308->hw_ver == RT1308_VER_C) { + regmap_write(rt1308->regmap, 0xc311, 0x7f); + regmap_write(rt1308->regmap, 0xc300, 0x09); + } else { + regmap_write(rt1308->regmap, 0xc311, 0x4f); + regmap_write(rt1308->regmap, 0xc300, 0x0b); + } + regmap_write(rt1308->regmap, 0xc900, 0x5a); + regmap_write(rt1308->regmap, 0xc1a0, 0x84); + regmap_write(rt1308->regmap, 0xc1a1, 0x01); + regmap_write(rt1308->regmap, 0xc360, 0x78); + regmap_write(rt1308->regmap, 0xc361, 0x87); + regmap_write(rt1308->regmap, 0xc0a1, 0x71); + regmap_write(rt1308->regmap, 0xc210, 0x00); + regmap_write(rt1308->regmap, 0xc070, 0x00); + regmap_write(rt1308->regmap, 0xc100, 0xd7); + regmap_write(rt1308->regmap, 0xc101, 0xd7); + + /* apply BQ params */ + rt1308_apply_bq_params(rt1308); + + regmap_write(rt1308->regmap, 0xcf01, 0x01); + +_preset_ready_: + if (rt1308->first_hw_init) { + regcache_cache_bypass(rt1308->regmap, false); + regcache_mark_dirty(rt1308->regmap); + } else + rt1308->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt1308->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + + return ret; +} + +static int rt1308_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt1308->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt1308->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt1308_io_init(&slave->dev, slave); +} + +static int rt1308_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt1308->params, params, sizeof(*params)); + + ret = rt1308_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt1308_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + return 0; +} + +static int rt1308_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(30); + snd_soc_component_update_bits(component, + RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4), + 0x3, 0x3); + msleep(40); + rt1308_apply_calib_params(rt1308); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, + RT1308_SDW_OFFSET | (RT1308_POWER_STATUS << 4), + 0x3, 0); + usleep_range(150000, 200000); + break; + + default: + break; + } + + return 0; +} + +static const char * const rt1308_rx_data_ch_select[] = { + "LR", + "LL", + "RL", + "RR", +}; + +static SOC_ENUM_SINGLE_DECL(rt1308_rx_data_ch_enum, + RT1308_SDW_OFFSET | (RT1308_DATA_PATH << 4), 0, + rt1308_rx_data_ch_select); + +static const struct snd_kcontrol_new rt1308_snd_controls[] = { + + /* I2S Data Channel Selection */ + SOC_ENUM("RX Channel Select", rt1308_rx_data_ch_enum), +}; + +static const struct snd_kcontrol_new rt1308_sto_dac_l = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4), + RT1308_DVOL_MUTE_L_EN_SFT, 1, 1); + +static const struct snd_kcontrol_new rt1308_sto_dac_r = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", + RT1308_SDW_OFFSET_BYTE3 | (RT1308_DAC_SET << 4), + RT1308_DVOL_MUTE_R_EN_SFT, 1, 1); + +static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Supply Widgets */ + SND_SOC_DAPM_SUPPLY("MBIAS20U", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ALDO", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DBG", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DACL", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK25M", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_R", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_L", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Power", + RT1308_SDW_OFFSET | (RT1308_POWER << 4), 3, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DLDO", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIXER_R", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIXER_L", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS4U", + RT1308_SDW_OFFSET_BYTE1 | (RT1308_POWER << 4), 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PLL2_LDO", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F2", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B2", + RT1308_SDW_OFFSET_BYTE2 | (RT1308_POWER << 4), 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_l), + SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_r), + + /* Output Lines */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1308_classd_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), +}; + +static const struct snd_soc_dapm_route rt1308_dapm_routes[] = { + + { "DAC", NULL, "AIF1RX" }, + + { "DAC", NULL, "MBIAS20U" }, + { "DAC", NULL, "ALDO" }, + { "DAC", NULL, "DBG" }, + { "DAC", NULL, "DACL" }, + { "DAC", NULL, "CLK25M" }, + { "DAC", NULL, "ADC_R" }, + { "DAC", NULL, "ADC_L" }, + { "DAC", NULL, "DLDO" }, + { "DAC", NULL, "VREF" }, + { "DAC", NULL, "MIXER_R" }, + { "DAC", NULL, "MIXER_L" }, + { "DAC", NULL, "MBIAS4U" }, + { "DAC", NULL, "PLL2_LDO" }, + { "DAC", NULL, "PLL2B" }, + { "DAC", NULL, "PLL2F" }, + { "DAC", NULL, "PLL2F2" }, + { "DAC", NULL, "PLL2B2" }, + + { "DAC L", "Switch", "DAC" }, + { "DAC R", "Switch", "DAC" }, + { "DAC L", NULL, "DAC Power" }, + { "DAC R", NULL, "DAC Power" }, + + { "CLASS D", NULL, "DAC L" }, + { "CLASS D", NULL, "DAC R" }, + { "SPOL", NULL, "CLASS D" }, + { "SPOR", NULL, "CLASS D" }, +}; + +static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt1308_sdw_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); + + if (tx_mask) + return -EINVAL; + + if (slots > 2) + return -EINVAL; + + rt1308->rx_mask = rx_mask; + rt1308->slots = slots; + /* slot_width is not used since it's irrelevant for SoundWire */ + + return 0; +} + +static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream; + int retval; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt1308->sdw_slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + /* port 1 for playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_config.num = 1; + else + return -EINVAL; + + if (rt1308->slots) { + stream_config.ch_count = rt1308->slots; + port_config.ch_mask = rt1308->rx_mask; + } + + retval = sdw_stream_add_slave(rt1308->sdw_slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + return retval; +} + +static int rt1308_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1308_sdw_priv *rt1308 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt1308->sdw_slave) + return -EINVAL; + + sdw_stream_remove_slave(rt1308->sdw_slave, sdw_stream); + return 0; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static const struct sdw_slave_ops rt1308_slave_ops = { + .read_prop = rt1308_read_prop, + .interrupt_callback = rt1308_interrupt_callback, + .update_status = rt1308_update_status, + .bus_config = rt1308_bus_config, +}; + +static int rt1308_sdw_parse_dt(struct rt1308_sdw_priv *rt1308, struct device *dev) +{ + int ret = 0; + + device_property_read_u32(dev, "realtek,bq-params-cnt", &rt1308->bq_params_cnt); + if (rt1308->bq_params_cnt) { + rt1308->bq_params = devm_kzalloc(dev, rt1308->bq_params_cnt, GFP_KERNEL); + if (!rt1308->bq_params) { + dev_err(dev, "Could not allocate bq_params memory\n"); + ret = -ENOMEM; + } else { + ret = device_property_read_u8_array(dev, "realtek,bq-params", rt1308->bq_params, rt1308->bq_params_cnt); + if (ret < 0) + dev_err(dev, "Could not read list of realtek,bq-params\n"); + } + } + + dev_dbg(dev, "bq_params_cnt=%d\n", rt1308->bq_params_cnt); + return ret; +} + +static int rt1308_sdw_component_probe(struct snd_soc_component *component) +{ + struct rt1308_sdw_priv *rt1308 = snd_soc_component_get_drvdata(component); + int ret; + + rt1308->component = component; + rt1308_sdw_parse_dt(rt1308, &rt1308->sdw_slave->dev); + + if (!rt1308->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + /* apply BQ params */ + rt1308_apply_bq_params(rt1308); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_sdw_rt1308 = { + .probe = rt1308_sdw_component_probe, + .controls = rt1308_snd_controls, + .num_controls = ARRAY_SIZE(rt1308_snd_controls), + .dapm_widgets = rt1308_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets), + .dapm_routes = rt1308_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes), + .endianness = 1, +}; + +static const struct snd_soc_dai_ops rt1308_aif_dai_ops = { + .hw_params = rt1308_sdw_hw_params, + .hw_free = rt1308_sdw_pcm_hw_free, + .set_stream = rt1308_set_sdw_stream, + .shutdown = rt1308_sdw_shutdown, + .set_tdm_slot = rt1308_sdw_set_tdm_slot, +}; + +#define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000 +#define RT1308_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver rt1308_sdw_dai[] = { + { + .name = "rt1308-aif", + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1308_STEREO_RATES, + .formats = RT1308_FORMATS, + }, + .ops = &rt1308_aif_dai_ops, + }, +}; + +static int rt1308_sdw_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt1308_sdw_priv *rt1308; + int ret; + + rt1308 = devm_kzalloc(dev, sizeof(*rt1308), GFP_KERNEL); + if (!rt1308) + return -ENOMEM; + + dev_set_drvdata(dev, rt1308); + rt1308->sdw_slave = slave; + rt1308->regmap = regmap; + + regcache_cache_only(rt1308->regmap, true); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt1308->hw_init = false; + rt1308->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_component_sdw_rt1308, + rt1308_sdw_dai, + ARRAY_SIZE(rt1308_sdw_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +static int rt1308_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt1308_sdw_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt1308_sdw_init(&slave->dev, regmap, slave); +} + +static int rt1308_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); + + return 0; +} + +static const struct sdw_device_id rt1308_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1308, 0x2, 0, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt1308_id); + +static int __maybe_unused rt1308_dev_suspend(struct device *dev) +{ + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + + if (!rt1308->hw_init) + return 0; + + regcache_cache_only(rt1308->regmap, true); + + return 0; +} + +#define RT1308_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt1308_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt1308->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT1308_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt1308->regmap, false); + regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff); + + return 0; +} + +static const struct dev_pm_ops rt1308_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume) + SET_RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL) +}; + +static struct sdw_driver rt1308_sdw_driver = { + .driver = { + .name = "rt1308", + .owner = THIS_MODULE, + .pm = &rt1308_pm, + }, + .probe = rt1308_sdw_probe, + .remove = rt1308_sdw_remove, + .ops = &rt1308_slave_ops, + .id_table = rt1308_id, +}; +module_sdw_driver(rt1308_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT1308 driver SDW"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h new file mode 100644 index 0000000000..f816c73e24 --- /dev/null +++ b/sound/soc/codecs/rt1308-sdw.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt1308-sdw.h -- RT1308 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT1308_SDW_H__ +#define __RT1308_SDW_H__ + +static const struct reg_default rt1308_reg_defaults[] = { + { 0x0000, 0x00 }, + { 0x0001, 0x00 }, + { 0x0002, 0x00 }, + { 0x0003, 0x00 }, + { 0x0004, 0x00 }, + { 0x0005, 0x01 }, + { 0x0020, 0x00 }, + { 0x0022, 0x00 }, + { 0x0023, 0x00 }, + { 0x0024, 0x00 }, + { 0x0025, 0x00 }, + { 0x0026, 0x00 }, + { 0x0030, 0x00 }, + { 0x0032, 0x00 }, + { 0x0033, 0x00 }, + { 0x0034, 0x00 }, + { 0x0035, 0x00 }, + { 0x0036, 0x00 }, + { 0x0040, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0043, 0x00 }, + { 0x0044, 0x20 }, + { 0x0045, 0x01 }, + { 0x0046, 0x01 }, + { 0x0048, 0x00 }, + { 0x0049, 0x00 }, + { 0x0050, 0x20 }, + { 0x0051, 0x02 }, + { 0x0052, 0x5D }, + { 0x0053, 0x13 }, + { 0x0054, 0x08 }, + { 0x0055, 0x00 }, + { 0x0060, 0x00 }, + { 0x0070, 0x00 }, + { 0x00E0, 0x00 }, + { 0x00F0, 0x00 }, + { 0x0100, 0x00 }, + { 0x0101, 0x00 }, + { 0x0102, 0x20 }, + { 0x0103, 0x00 }, + { 0x0104, 0x00 }, + { 0x0105, 0x03 }, + { 0x0120, 0x00 }, + { 0x0122, 0x00 }, + { 0x0123, 0x00 }, + { 0x0124, 0x00 }, + { 0x0125, 0x00 }, + { 0x0126, 0x00 }, + { 0x0127, 0x00 }, + { 0x0130, 0x00 }, + { 0x0132, 0x00 }, + { 0x0133, 0x00 }, + { 0x0134, 0x00 }, + { 0x0135, 0x00 }, + { 0x0136, 0x00 }, + { 0x0137, 0x00 }, + { 0x0200, 0x00 }, + { 0x0201, 0x00 }, + { 0x0202, 0x00 }, + { 0x0203, 0x00 }, + { 0x0204, 0x00 }, + { 0x0205, 0x03 }, + { 0x0220, 0x00 }, + { 0x0222, 0x00 }, + { 0x0223, 0x00 }, + { 0x0224, 0x00 }, + { 0x0225, 0x00 }, + { 0x0226, 0x00 }, + { 0x0227, 0x00 }, + { 0x0230, 0x00 }, + { 0x0232, 0x00 }, + { 0x0233, 0x00 }, + { 0x0234, 0x00 }, + { 0x0235, 0x00 }, + { 0x0236, 0x00 }, + { 0x0237, 0x00 }, + { 0x0400, 0x00 }, + { 0x0401, 0x00 }, + { 0x0402, 0x00 }, + { 0x0403, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x03 }, + { 0x0420, 0x00 }, + { 0x0422, 0x00 }, + { 0x0423, 0x00 }, + { 0x0424, 0x00 }, + { 0x0425, 0x00 }, + { 0x0426, 0x00 }, + { 0x0427, 0x00 }, + { 0x0430, 0x00 }, + { 0x0432, 0x00 }, + { 0x0433, 0x00 }, + { 0x0434, 0x00 }, + { 0x0435, 0x00 }, + { 0x0436, 0x00 }, + { 0x0437, 0x00 }, + { 0x0f00, 0x00 }, + { 0x0f01, 0x00 }, + { 0x0f02, 0x00 }, + { 0x0f03, 0x00 }, + { 0x0f04, 0x00 }, + { 0x0f05, 0x00 }, + { 0x0f20, 0x00 }, + { 0x0f22, 0x00 }, + { 0x0f23, 0x00 }, + { 0x0f24, 0x00 }, + { 0x0f25, 0x00 }, + { 0x0f26, 0x00 }, + { 0x0f27, 0x00 }, + { 0x0f30, 0x00 }, + { 0x0f32, 0x00 }, + { 0x0f33, 0x00 }, + { 0x0f34, 0x00 }, + { 0x0f35, 0x00 }, + { 0x0f36, 0x00 }, + { 0x0f37, 0x00 }, + { 0x2f01, 0x01 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x0f }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f07, 0x8e }, + { 0x3000, 0x00 }, + { 0x3001, 0x00 }, + { 0x3004, 0x01 }, + { 0x3005, 0x23 }, + { 0x3008, 0x02 }, + { 0x300a, 0x00 }, + { 0xc000 | (RT1308_DATA_PATH << 4), 0x00 }, + { 0xc003 | (RT1308_DAC_SET << 4), 0x00 }, + { 0xc000 | (RT1308_POWER << 4), 0x00 }, + { 0xc001 | (RT1308_POWER << 4), 0x00 }, + { 0xc002 | (RT1308_POWER << 4), 0x00 }, + { 0xc000 | (RT1308_POWER_STATUS << 4), 0x00 }, +}; + +#define RT1308_SDW_OFFSET 0xc000 +#define RT1308_SDW_OFFSET_BYTE0 0xc000 +#define RT1308_SDW_OFFSET_BYTE1 0xc001 +#define RT1308_SDW_OFFSET_BYTE2 0xc002 +#define RT1308_SDW_OFFSET_BYTE3 0xc003 + +#define RT1308_SDW_RESET (RT1308_SDW_OFFSET | (RT1308_RESET << 4)) + +struct rt1308_sdw_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct sdw_slave *sdw_slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + int rx_mask; + int slots; + int hw_ver; + unsigned char *bq_params; + unsigned int bq_params_cnt; +}; + +#endif /* __RT1308_SDW_H__ */ diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c new file mode 100644 index 0000000000..86afb429d4 --- /dev/null +++ b/sound/soc/codecs/rt1308.c @@ -0,0 +1,871 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt1308.c -- RT1308 ALSA SoC amplifier component driver +// +// Copyright 2019 Realtek Semiconductor Corp. +// Author: Derek Fang +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt1308.h" + +static const struct reg_sequence init_list[] = { + + { RT1308_I2C_I2S_SDW_SET, 0x01014005 }, + { RT1308_CLASS_D_SET_2, 0x227f5501 }, + { RT1308_PADS_1, 0x50150505 }, + { RT1308_VREF, 0x18100000 }, + { RT1308_IV_SENSE, 0x87010000 }, + { RT1308_DUMMY_REG, 0x00000200 }, + { RT1308_SIL_DET, 0xe1c30000 }, + { RT1308_DC_CAL_2, 0x00ffff00 }, + { RT1308_CLK_DET, 0x01000000 }, + { RT1308_POWER_STATUS, 0x08800000 }, + { RT1308_DAC_SET, 0xafaf0700 }, + +}; +#define RT1308_INIT_REG_LEN ARRAY_SIZE(init_list) + +struct rt1308_priv { + struct snd_soc_component *component; + struct regmap *regmap; + + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int master; + + int pll_src; + int pll_in; + int pll_out; +}; + +static const struct reg_default rt1308_reg[] = { + + { 0x01, 0x1f3f5f00 }, + { 0x02, 0x07000000 }, + { 0x03, 0x80003e00 }, + { 0x04, 0x80800600 }, + { 0x05, 0x0aaa1a0a }, + { 0x06, 0x52000000 }, + { 0x07, 0x00000000 }, + { 0x08, 0x00600000 }, + { 0x09, 0xe1030000 }, + { 0x0a, 0x00000000 }, + { 0x0b, 0x30000000 }, + { 0x0c, 0x7fff7000 }, + { 0x10, 0xffff0700 }, + { 0x11, 0x0a000000 }, + { 0x12, 0x60040000 }, + { 0x13, 0x00000000 }, + { 0x14, 0x0f300000 }, + { 0x15, 0x00000022 }, + { 0x16, 0x02000000 }, + { 0x17, 0x01004045 }, + { 0x18, 0x00000000 }, + { 0x19, 0x00000000 }, + { 0x1a, 0x80000000 }, + { 0x1b, 0x10325476 }, + { 0x1c, 0x1d1d0000 }, + { 0x20, 0xd2101300 }, + { 0x21, 0xf3ffff00 }, + { 0x22, 0x00000000 }, + { 0x23, 0x00000000 }, + { 0x24, 0x00000000 }, + { 0x25, 0x00000000 }, + { 0x26, 0x00000000 }, + { 0x27, 0x00000000 }, + { 0x28, 0x00000000 }, + { 0x29, 0x00000000 }, + { 0x2a, 0x00000000 }, + { 0x2b, 0x00000000 }, + { 0x2c, 0x00000000 }, + { 0x2d, 0x00000000 }, + { 0x2e, 0x00000000 }, + { 0x2f, 0x00000000 }, + { 0x30, 0x01000000 }, + { 0x31, 0x20025501 }, + { 0x32, 0x00000000 }, + { 0x33, 0x105a0000 }, + { 0x34, 0x10100000 }, + { 0x35, 0x2aaa52aa }, + { 0x36, 0x00c00000 }, + { 0x37, 0x20046100 }, + { 0x50, 0x10022f00 }, + { 0x51, 0x003c0000 }, + { 0x54, 0x04000000 }, + { 0x55, 0x01000000 }, + { 0x56, 0x02000000 }, + { 0x57, 0x02000000 }, + { 0x58, 0x02000000 }, + { 0x59, 0x02000000 }, + { 0x5b, 0x02000000 }, + { 0x5c, 0x00000000 }, + { 0x5d, 0x00000000 }, + { 0x5e, 0x00000000 }, + { 0x5f, 0x00000000 }, + { 0x60, 0x02000000 }, + { 0x61, 0x00000000 }, + { 0x62, 0x00000000 }, + { 0x63, 0x00000000 }, + { 0x64, 0x00000000 }, + { 0x65, 0x02000000 }, + { 0x66, 0x00000000 }, + { 0x67, 0x00000000 }, + { 0x68, 0x00000000 }, + { 0x69, 0x00000000 }, + { 0x6a, 0x02000000 }, + { 0x6c, 0x00000000 }, + { 0x6d, 0x00000000 }, + { 0x6e, 0x00000000 }, + { 0x70, 0x10EC1308 }, + { 0x71, 0x00000000 }, + { 0x72, 0x00000000 }, + { 0x73, 0x00000000 }, + { 0x74, 0x00000000 }, + { 0x75, 0x00000000 }, + { 0x76, 0x00000000 }, + { 0x77, 0x00000000 }, + { 0x78, 0x00000000 }, + { 0x79, 0x00000000 }, + { 0x7a, 0x00000000 }, + { 0x7b, 0x00000000 }, + { 0x7c, 0x00000000 }, + { 0x7d, 0x00000000 }, + { 0x7e, 0x00000000 }, + { 0x7f, 0x00020f00 }, + { 0x80, 0x00000000 }, + { 0x81, 0x00000000 }, + { 0x82, 0x00000000 }, + { 0x83, 0x00000000 }, + { 0x84, 0x00000000 }, + { 0x85, 0x00000000 }, + { 0x86, 0x00000000 }, + { 0x87, 0x00000000 }, + { 0x88, 0x00000000 }, + { 0x89, 0x00000000 }, + { 0x8a, 0x00000000 }, + { 0x8b, 0x00000000 }, + { 0x8c, 0x00000000 }, + { 0x8d, 0x00000000 }, + { 0x8e, 0x00000000 }, + { 0x90, 0x50250905 }, + { 0x91, 0x15050000 }, + { 0xa0, 0x00000000 }, + { 0xa1, 0x00000000 }, + { 0xa2, 0x00000000 }, + { 0xa3, 0x00000000 }, + { 0xa4, 0x00000000 }, + { 0xb0, 0x00000000 }, + { 0xb1, 0x00000000 }, + { 0xb2, 0x00000000 }, + { 0xb3, 0x00000000 }, + { 0xb4, 0x00000000 }, + { 0xb5, 0x00000000 }, + { 0xb6, 0x00000000 }, + { 0xb7, 0x00000000 }, + { 0xb8, 0x00000000 }, + { 0xb9, 0x00000000 }, + { 0xba, 0x00000000 }, + { 0xbb, 0x00000000 }, + { 0xc0, 0x01000000 }, + { 0xc1, 0x00000000 }, + { 0xf0, 0x00000000 }, +}; + +static int rt1308_reg_init(struct snd_soc_component *component) +{ + struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component); + + return regmap_multi_reg_write(rt1308->regmap, init_list, + RT1308_INIT_REG_LEN); +} + +static bool rt1308_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1308_RESET: + case RT1308_RESET_N: + case RT1308_CLK_2: + case RT1308_SIL_DET: + case RT1308_CLK_DET: + case RT1308_DC_DET: + case RT1308_DAC_SET: + case RT1308_DAC_BUF: + case RT1308_SDW_REG_RDATA: + case RT1308_DC_CAL_1: + case RT1308_PVDD_OFFSET_CTL: + case RT1308_CAL_OFFSET_DAC_PBTL: + case RT1308_CAL_OFFSET_DAC_L: + case RT1308_CAL_OFFSET_DAC_R: + case RT1308_CAL_OFFSET_PWM_L: + case RT1308_CAL_OFFSET_PWM_R: + case RT1308_CAL_PWM_VOS_ADC_L: + case RT1308_CAL_PWM_VOS_ADC_R: + case RT1308_MBIAS: + case RT1308_POWER_STATUS: + case RT1308_POWER_INT: + case RT1308_SINE_TONE_GEN_2: + case RT1308_BQ_SET: + case RT1308_BQ_PARA_UPDATE: + case RT1308_VEN_DEV_ID: + case RT1308_VERSION_ID: + case RT1308_EFUSE_1: + case RT1308_EFUSE_READ_PVDD_L: + case RT1308_EFUSE_READ_PVDD_R: + case RT1308_EFUSE_READ_PVDD_PTBL: + case RT1308_EFUSE_READ_DEV: + case RT1308_EFUSE_READ_R0: + case RT1308_EFUSE_READ_ADC_L: + case RT1308_EFUSE_READ_ADC_R: + case RT1308_EFUSE_READ_ADC_PBTL: + case RT1308_EFUSE_RESERVE: + case RT1308_EFUSE_DATA_0_MSB: + case RT1308_EFUSE_DATA_0_LSB: + case RT1308_EFUSE_DATA_1_MSB: + case RT1308_EFUSE_DATA_1_LSB: + case RT1308_EFUSE_DATA_2_MSB: + case RT1308_EFUSE_DATA_2_LSB: + case RT1308_EFUSE_DATA_3_MSB: + case RT1308_EFUSE_DATA_3_LSB: + case RT1308_EFUSE_STATUS_1: + case RT1308_EFUSE_STATUS_2: + case RT1308_DUMMY_REG: + return true; + default: + return false; + } +} + +static bool rt1308_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT1308_RESET: + case RT1308_RESET_N: + case RT1308_CLK_GATING ... RT1308_DC_DET_THRES: + case RT1308_DAC_SET ... RT1308_AD_FILTER_SET: + case RT1308_DC_CAL_1 ... RT1308_POWER_INT: + case RT1308_SINE_TONE_GEN_1: + case RT1308_SINE_TONE_GEN_2: + case RT1308_BQ_SET: + case RT1308_BQ_PARA_UPDATE: + case RT1308_BQ_PRE_VOL_L ... RT1308_BQ_POST_VOL_R: + case RT1308_BQ1_L_H0 ... RT1308_BQ2_R_A2: + case RT1308_VEN_DEV_ID: + case RT1308_VERSION_ID: + case RT1308_SPK_BOUND: + case RT1308_BQ1_EQ_L_1 ... RT1308_BQ2_EQ_R_3: + case RT1308_EFUSE_1 ... RT1308_EFUSE_RESERVE: + case RT1308_PADS_1: + case RT1308_PADS_2: + case RT1308_TEST_MODE: + case RT1308_TEST_1: + case RT1308_TEST_2: + case RT1308_TEST_3: + case RT1308_TEST_4: + case RT1308_EFUSE_DATA_0_MSB ... RT1308_EFUSE_STATUS_2: + case RT1308_TCON_1: + case RT1308_TCON_2: + case RT1308_DUMMY_REG: + case RT1308_MAX_REG: + return true; + default: + return false; + } +} + +static int rt1308_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(30); + snd_soc_component_update_bits(component, RT1308_POWER_STATUS, + RT1308_POW_PDB_REG_BIT | RT1308_POW_PDB_MN_BIT, + RT1308_POW_PDB_REG_BIT | RT1308_POW_PDB_MN_BIT); + msleep(40); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT1308_POWER_STATUS, + RT1308_POW_PDB_REG_BIT | RT1308_POW_PDB_MN_BIT, 0); + usleep_range(150000, 200000); + break; + + default: + break; + } + + return 0; +} + +static const char * const rt1308_rx_data_ch_select[] = { + "LR", + "LL", + "RL", + "RR", +}; + +static SOC_ENUM_SINGLE_DECL(rt1308_rx_data_ch_enum, RT1308_DATA_PATH, 24, + rt1308_rx_data_ch_select); + +static const struct snd_kcontrol_new rt1308_snd_controls[] = { + + /* I2S Data Channel Selection */ + SOC_ENUM("RX Channel Select", rt1308_rx_data_ch_enum), +}; + +static const struct snd_kcontrol_new rt1308_sto_dac_l = + SOC_DAPM_SINGLE("Switch", RT1308_DAC_SET, + RT1308_DVOL_MUTE_L_EN_SFT, 1, 1); + +static const struct snd_kcontrol_new rt1308_sto_dac_r = + SOC_DAPM_SINGLE("Switch", RT1308_DAC_SET, + RT1308_DVOL_MUTE_R_EN_SFT, 1, 1); + +static const struct snd_soc_dapm_widget rt1308_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Supply Widgets */ + SND_SOC_DAPM_SUPPLY("MBIAS20U", RT1308_POWER, + RT1308_POW_MBIAS20U_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ALDO", RT1308_POWER, + RT1308_POW_ALDO_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DBG", RT1308_POWER, + RT1308_POW_DBG_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DACL", RT1308_POWER, + RT1308_POW_DACL_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK25M", RT1308_POWER, + RT1308_POW_CLK25M_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_R", RT1308_POWER, + RT1308_POW_ADC_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_L", RT1308_POWER, + RT1308_POW_ADC_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DLDO", RT1308_POWER, + RT1308_POW_DLDO_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", RT1308_POWER, + RT1308_POW_VREF_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIXER_R", RT1308_POWER, + RT1308_POW_MIXER_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIXER_L", RT1308_POWER, + RT1308_POW_MIXER_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS4U", RT1308_POWER, + RT1308_POW_MBIAS4U_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2_LDO", RT1308_POWER, + RT1308_POW_PLL2_LDO_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B", RT1308_POWER, + RT1308_POW_PLL2B_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F", RT1308_POWER, + RT1308_POW_PLL2F_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F2", RT1308_POWER, + RT1308_POW_PLL2F2_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B2", RT1308_POWER, + RT1308_POW_PLL2B2_EN_BIT, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("DAC Power", RT1308_POWER, + RT1308_POW_DAC1_BIT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_l), + SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1308_sto_dac_r), + + /* Output Lines */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1308_classd_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), +}; + +static const struct snd_soc_dapm_route rt1308_dapm_routes[] = { + + { "DAC", NULL, "AIF1RX" }, + + { "DAC", NULL, "MBIAS20U" }, + { "DAC", NULL, "ALDO" }, + { "DAC", NULL, "DBG" }, + { "DAC", NULL, "DACL" }, + { "DAC", NULL, "CLK25M" }, + { "DAC", NULL, "ADC_R" }, + { "DAC", NULL, "ADC_L" }, + { "DAC", NULL, "DLDO" }, + { "DAC", NULL, "VREF" }, + { "DAC", NULL, "MIXER_R" }, + { "DAC", NULL, "MIXER_L" }, + { "DAC", NULL, "MBIAS4U" }, + { "DAC", NULL, "PLL2_LDO" }, + { "DAC", NULL, "PLL2B" }, + { "DAC", NULL, "PLL2F" }, + { "DAC", NULL, "PLL2F2" }, + { "DAC", NULL, "PLL2B2" }, + + { "DAC L", "Switch", "DAC" }, + { "DAC R", "Switch", "DAC" }, + { "DAC L", NULL, "DAC Power" }, + { "DAC R", NULL, "DAC Power" }, + + { "CLASS D", NULL, "DAC L" }, + { "CLASS D", NULL, "DAC R" }, + { "SPOL", NULL, "CLASS D" }, + { "SPOR", NULL, "CLASS D" }, +}; + +static int rt1308_get_clk_info(int sclk, int rate) +{ + int i; + static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +static int rt1308_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt1308->lrck = params_rate(params); + pre_div = rt1308_get_clk_info(rt1308->sysclk, rt1308->lrck); + if (pre_div < 0) { + dev_err(component->dev, + "Unsupported clock setting %d\n", rt1308->lrck); + return -EINVAL; + } + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + bclk_ms = frame_size > 32; + rt1308->bclk = rt1308->lrck * (32 << bclk_ms); + + dev_dbg(component->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt1308->lrck, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + val_len |= RT1308_I2S_DL_SEL_16B; + break; + case 20: + val_len |= RT1308_I2S_DL_SEL_20B; + break; + case 24: + val_len |= RT1308_I2S_DL_SEL_24B; + break; + case 8: + val_len |= RT1308_I2S_DL_SEL_8B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT1308_AIF1: + mask_clk = RT1308_DIV_FS_SYS_MASK; + val_clk = pre_div << RT1308_DIV_FS_SYS_SFT; + snd_soc_component_update_bits(component, + RT1308_I2S_SET_2, RT1308_I2S_DL_SEL_MASK, + val_len); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT1308_CLK_1, + mask_clk, val_clk); + + return 0; +} + +static int rt1308_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, reg1_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + rt1308->master = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT1308_I2S_DF_SEL_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT1308_I2S_DF_SEL_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT1308_I2S_DF_SEL_PCM_B; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg1_val |= RT1308_I2S_BCLK_INV; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT1308_AIF1: + snd_soc_component_update_bits(component, + RT1308_I2S_SET_1, RT1308_I2S_DF_SEL_MASK, + reg_val); + snd_soc_component_update_bits(component, + RT1308_I2S_SET_2, RT1308_I2S_BCLK_MASK, + reg1_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt1308_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt1308->sysclk && clk_id == rt1308->sysclk_src) + return 0; + + switch (clk_id) { + case RT1308_FS_SYS_S_MCLK: + reg_val |= RT1308_SEL_FS_SYS_SRC_MCLK; + snd_soc_component_update_bits(component, + RT1308_CLK_DET, RT1308_MCLK_DET_EN_MASK, + RT1308_MCLK_DET_EN); + break; + case RT1308_FS_SYS_S_BCLK: + reg_val |= RT1308_SEL_FS_SYS_SRC_BCLK; + break; + case RT1308_FS_SYS_S_PLL: + reg_val |= RT1308_SEL_FS_SYS_SRC_PLL; + break; + case RT1308_FS_SYS_S_RCCLK: + reg_val |= RT1308_SEL_FS_SYS_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT1308_CLK_1, + RT1308_SEL_FS_SYS_MASK, reg_val); + rt1308->sysclk = freq; + rt1308->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt1308_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt1308->pll_src && freq_in == rt1308->pll_in && + freq_out == rt1308->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt1308->pll_in = 0; + rt1308->pll_out = 0; + snd_soc_component_update_bits(component, + RT1308_CLK_1, RT1308_SEL_FS_SYS_MASK, + RT1308_SEL_FS_SYS_SRC_MCLK); + return 0; + } + + switch (source) { + case RT1308_PLL_S_MCLK: + snd_soc_component_update_bits(component, + RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK, + RT1308_SEL_PLL_SRC_MCLK); + snd_soc_component_update_bits(component, + RT1308_CLK_DET, RT1308_MCLK_DET_EN_MASK, + RT1308_MCLK_DET_EN); + break; + case RT1308_PLL_S_BCLK: + snd_soc_component_update_bits(component, + RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK, + RT1308_SEL_PLL_SRC_BCLK); + break; + case RT1308_PLL_S_RCCLK: + snd_soc_component_update_bits(component, + RT1308_CLK_2, RT1308_SEL_PLL_SRC_MASK, + RT1308_SEL_PLL_SRC_RCCLK); + freq_in = 25000000; + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT1308_PLL_1, + (pll_code.k_code << RT1308_PLL1_K_SFT) | + (pll_code.m_bp << RT1308_PLL1_M_BYPASS_SFT) | + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT1308_PLL1_M_SFT) | + (pll_code.n_code << RT1308_PLL1_N_SFT)); + + rt1308->pll_in = freq_in; + rt1308->pll_out = freq_out; + rt1308->pll_src = source; + + return 0; +} + +static int rt1308_probe(struct snd_soc_component *component) +{ + struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component); + + rt1308->component = component; + + return rt1308_reg_init(component); +} + +static void rt1308_remove(struct snd_soc_component *component) +{ + struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component); + + regmap_write(rt1308->regmap, RT1308_RESET, 0); +} + +#ifdef CONFIG_PM +static int rt1308_suspend(struct snd_soc_component *component) +{ + struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1308->regmap, true); + regcache_mark_dirty(rt1308->regmap); + + return 0; +} + +static int rt1308_resume(struct snd_soc_component *component) +{ + struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt1308->regmap, false); + regcache_sync(rt1308->regmap); + + return 0; +} +#else +#define rt1308_suspend NULL +#define rt1308_resume NULL +#endif + +#define RT1308_STEREO_RATES SNDRV_PCM_RATE_48000 +#define RT1308_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt1308_aif_dai_ops = { + .hw_params = rt1308_hw_params, + .set_fmt = rt1308_set_dai_fmt, +}; + +static struct snd_soc_dai_driver rt1308_dai[] = { + { + .name = "rt1308-aif", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1308_STEREO_RATES, + .formats = RT1308_FORMATS, + }, + .ops = &rt1308_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt1308 = { + .probe = rt1308_probe, + .remove = rt1308_remove, + .suspend = rt1308_suspend, + .resume = rt1308_resume, + .controls = rt1308_snd_controls, + .num_controls = ARRAY_SIZE(rt1308_snd_controls), + .dapm_widgets = rt1308_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1308_dapm_widgets), + .dapm_routes = rt1308_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1308_dapm_routes), + .set_sysclk = rt1308_set_component_sysclk, + .set_pll = rt1308_set_component_pll, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt1308_regmap = { + .reg_bits = 8, + .val_bits = 32, + .max_register = RT1308_MAX_REG, + .volatile_reg = rt1308_volatile_register, + .readable_reg = rt1308_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt1308_reg, + .num_reg_defaults = ARRAY_SIZE(rt1308_reg), + .use_single_read = true, + .use_single_write = true, +}; + +#ifdef CONFIG_OF +static const struct of_device_id rt1308_of_match[] = { + { .compatible = "realtek,rt1308", }, + { }, +}; +MODULE_DEVICE_TABLE(of, rt1308_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt1308_acpi_match[] = { + { "10EC1308", 0, }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt1308_acpi_match); +#endif + +static const struct i2c_device_id rt1308_i2c_id[] = { + { "rt1308", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt1308_i2c_id); + +static void rt1308_efuse(struct rt1308_priv *rt1308) +{ + regmap_write(rt1308->regmap, RT1308_RESET, 0); + + regmap_write(rt1308->regmap, RT1308_POWER_STATUS, 0x01800000); + msleep(100); + regmap_write(rt1308->regmap, RT1308_EFUSE_1, 0x44fe0f00); + msleep(20); + regmap_write(rt1308->regmap, RT1308_PVDD_OFFSET_CTL, 0x10000000); +} + +static int rt1308_i2c_probe(struct i2c_client *i2c) +{ + struct rt1308_priv *rt1308; + int ret; + unsigned int val; + + rt1308 = devm_kzalloc(&i2c->dev, sizeof(struct rt1308_priv), + GFP_KERNEL); + if (rt1308 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt1308); + + rt1308->regmap = devm_regmap_init_i2c(i2c, &rt1308_regmap); + if (IS_ERR(rt1308->regmap)) { + ret = PTR_ERR(rt1308->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt1308->regmap, RT1308_VEN_DEV_ID, &val); + /* ignore last byte difference */ + if ((val & 0xFFFFFF00) != RT1308_DEVICE_ID_NUM) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt1308\n", val); + return -ENODEV; + } + + rt1308_efuse(rt1308); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt1308, + rt1308_dai, ARRAY_SIZE(rt1308_dai)); +} + +static void rt1308_i2c_shutdown(struct i2c_client *client) +{ + struct rt1308_priv *rt1308 = i2c_get_clientdata(client); + + regmap_write(rt1308->regmap, RT1308_RESET, 0); +} + +static struct i2c_driver rt1308_i2c_driver = { + .driver = { + .name = "rt1308", + .of_match_table = of_match_ptr(rt1308_of_match), + .acpi_match_table = ACPI_PTR(rt1308_acpi_match), + }, + .probe = rt1308_i2c_probe, + .shutdown = rt1308_i2c_shutdown, + .id_table = rt1308_i2c_id, +}; +module_i2c_driver(rt1308_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT1308 amplifier driver"); +MODULE_AUTHOR("Derek Fang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt1308.h b/sound/soc/codecs/rt1308.h new file mode 100644 index 0000000000..d3a0f91630 --- /dev/null +++ b/sound/soc/codecs/rt1308.h @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt1308.h -- RT1308 ALSA SoC amplifier component driver + * + * Copyright 2019 Realtek Semiconductor Corp. + * Author: Derek Fang + * + */ + +#ifndef _RT1308_H_ +#define _RT1308_H_ + +#define RT1308_DEVICE_ID_NUM 0x10ec1300 + +#define RT1308_RESET 0x00 +#define RT1308_RESET_N 0x01 +#define RT1308_CLK_GATING 0x02 +#define RT1308_PLL_1 0x03 +#define RT1308_PLL_2 0x04 +#define RT1308_PLL_INT 0x05 +#define RT1308_CLK_1 0x06 +#define RT1308_DATA_PATH 0x07 +#define RT1308_CLK_2 0x08 +#define RT1308_SIL_DET 0x09 +#define RT1308_CLK_DET 0x0a +#define RT1308_DC_DET 0x0b +#define RT1308_DC_DET_THRES 0x0c +#define RT1308_DAC_SET 0x10 +#define RT1308_SRC_SET 0x11 +#define RT1308_DAC_BUF 0x12 +#define RT1308_ADC_SET 0x13 +#define RT1308_ADC_SET_INT 0x14 +#define RT1308_I2S_SET_1 0x15 +#define RT1308_I2S_SET_2 0x16 +#define RT1308_I2C_I2S_SDW_SET 0x17 +#define RT1308_SDW_REG_RW 0x18 +#define RT1308_SDW_REG_RDATA 0x19 +#define RT1308_IV_SENSE 0x1a +#define RT1308_I2S_TX_DAC_SET 0x1b +#define RT1308_AD_FILTER_SET 0x1c +#define RT1308_DC_CAL_1 0x20 +#define RT1308_DC_CAL_2 0x21 +#define RT1308_DC_CAL_L_OFFSET 0x22 +#define RT1308_DC_CAL_R_OFFSET 0x23 +#define RT1308_PVDD_OFFSET_CTL 0x24 +#define RT1308_PVDD_OFFSET_L 0x25 +#define RT1308_PVDD_OFFSET_R 0x26 +#define RT1308_PVDD_OFFSET_PBTL 0x27 +#define RT1308_PVDD_OFFSET_PVDD 0x28 +#define RT1308_CAL_OFFSET_DAC_PBTL 0x29 +#define RT1308_CAL_OFFSET_DAC_L 0x2a +#define RT1308_CAL_OFFSET_DAC_R 0x2b +#define RT1308_CAL_OFFSET_PWM_L 0x2c +#define RT1308_CAL_OFFSET_PWM_R 0x2d +#define RT1308_CAL_PWM_VOS_ADC_L 0x2e +#define RT1308_CAL_PWM_VOS_ADC_R 0x2f +#define RT1308_CLASS_D_SET_1 0x30 +#define RT1308_CLASS_D_SET_2 0x31 +#define RT1308_POWER 0x32 +#define RT1308_LDO 0x33 +#define RT1308_VREF 0x34 +#define RT1308_MBIAS 0x35 +#define RT1308_POWER_STATUS 0x36 +#define RT1308_POWER_INT 0x37 +#define RT1308_SINE_TONE_GEN_1 0x50 +#define RT1308_SINE_TONE_GEN_2 0x51 +#define RT1308_BQ_SET 0x54 +#define RT1308_BQ_PARA_UPDATE 0x55 +#define RT1308_BQ_PRE_VOL_L 0x56 +#define RT1308_BQ_PRE_VOL_R 0x57 +#define RT1308_BQ_POST_VOL_L 0x58 +#define RT1308_BQ_POST_VOL_R 0x59 +#define RT1308_BQ1_L_H0 0x5b +#define RT1308_BQ1_L_B1 0x5c +#define RT1308_BQ1_L_B2 0x5d +#define RT1308_BQ1_L_A1 0x5e +#define RT1308_BQ1_L_A2 0x5f +#define RT1308_BQ1_R_H0 0x60 +#define RT1308_BQ1_R_B1 0x61 +#define RT1308_BQ1_R_B2 0x62 +#define RT1308_BQ1_R_A1 0x63 +#define RT1308_BQ1_R_A2 0x64 +#define RT1308_BQ2_L_H0 0x65 +#define RT1308_BQ2_L_B1 0x66 +#define RT1308_BQ2_L_B2 0x67 +#define RT1308_BQ2_L_A1 0x68 +#define RT1308_BQ2_L_A2 0x69 +#define RT1308_BQ2_R_H0 0x6a +#define RT1308_BQ2_R_B1 0x6b +#define RT1308_BQ2_R_B2 0x6c +#define RT1308_BQ2_R_A1 0x6d +#define RT1308_BQ2_R_A2 0x6e +#define RT1308_VEN_DEV_ID 0x70 +#define RT1308_VERSION_ID 0x71 +#define RT1308_SPK_BOUND 0x72 +#define RT1308_BQ1_EQ_L_1 0x73 +#define RT1308_BQ1_EQ_L_2 0x74 +#define RT1308_BQ1_EQ_L_3 0x75 +#define RT1308_BQ1_EQ_R_1 0x76 +#define RT1308_BQ1_EQ_R_2 0x77 +#define RT1308_BQ1_EQ_R_3 0x78 +#define RT1308_BQ2_EQ_L_1 0x79 +#define RT1308_BQ2_EQ_L_2 0x7a +#define RT1308_BQ2_EQ_L_3 0x7b +#define RT1308_BQ2_EQ_R_1 0x7c +#define RT1308_BQ2_EQ_R_2 0x7d +#define RT1308_BQ2_EQ_R_3 0x7e +#define RT1308_EFUSE_1 0x7f +#define RT1308_EFUSE_2 0x80 +#define RT1308_EFUSE_PROG_PVDD_L 0x81 +#define RT1308_EFUSE_PROG_PVDD_R 0x82 +#define RT1308_EFUSE_PROG_R0_L 0x83 +#define RT1308_EFUSE_PROG_R0_R 0x84 +#define RT1308_EFUSE_PROG_DEV 0x85 +#define RT1308_EFUSE_READ_PVDD_L 0x86 +#define RT1308_EFUSE_READ_PVDD_R 0x87 +#define RT1308_EFUSE_READ_PVDD_PTBL 0x88 +#define RT1308_EFUSE_READ_DEV 0x89 +#define RT1308_EFUSE_READ_R0 0x8a +#define RT1308_EFUSE_READ_ADC_L 0x8b +#define RT1308_EFUSE_READ_ADC_R 0x8c +#define RT1308_EFUSE_READ_ADC_PBTL 0x8d +#define RT1308_EFUSE_RESERVE 0x8e +#define RT1308_PADS_1 0x90 +#define RT1308_PADS_2 0x91 +#define RT1308_TEST_MODE 0xa0 +#define RT1308_TEST_1 0xa1 +#define RT1308_TEST_2 0xa2 +#define RT1308_TEST_3 0xa3 +#define RT1308_TEST_4 0xa4 +#define RT1308_EFUSE_DATA_0_MSB 0xb0 +#define RT1308_EFUSE_DATA_0_LSB 0xb1 +#define RT1308_EFUSE_DATA_1_MSB 0xb2 +#define RT1308_EFUSE_DATA_1_LSB 0xb3 +#define RT1308_EFUSE_DATA_2_MSB 0xb4 +#define RT1308_EFUSE_DATA_2_LSB 0xb5 +#define RT1308_EFUSE_DATA_3_MSB 0xb6 +#define RT1308_EFUSE_DATA_3_LSB 0xb7 +#define RT1308_EFUSE_DATA_TEST_MSB 0xb8 +#define RT1308_EFUSE_DATA_TEST_LSB 0xb9 +#define RT1308_EFUSE_STATUS_1 0xba +#define RT1308_EFUSE_STATUS_2 0xbb +#define RT1308_TCON_1 0xc0 +#define RT1308_TCON_2 0xc1 +#define RT1308_DUMMY_REG 0xf0 +#define RT1308_MAX_REG 0xff + +/* PLL1 M/N/K Code-1 (0x03) */ +#define RT1308_PLL1_K_SFT 24 +#define RT1308_PLL1_K_MASK (0x1f << 24) +#define RT1308_PLL1_M_BYPASS_MASK (0x1 << 23) +#define RT1308_PLL1_M_BYPASS_SFT 23 +#define RT1308_PLL1_M_BYPASS (0x1 << 23) +#define RT1308_PLL1_M_MASK (0x3f << 16) +#define RT1308_PLL1_M_SFT 16 +#define RT1308_PLL1_N_MASK (0x7f << 8) +#define RT1308_PLL1_N_SFT 8 + +/* CLOCK-1 (0x06) */ +#define RT1308_DIV_FS_SYS_MASK (0xf << 28) +#define RT1308_DIV_FS_SYS_SFT 28 +#define RT1308_SEL_FS_SYS_MASK (0x7 << 24) +#define RT1308_SEL_FS_SYS_SFT 24 +#define RT1308_SEL_FS_SYS_SRC_MCLK (0x0 << 24) +#define RT1308_SEL_FS_SYS_SRC_BCLK (0x1 << 24) +#define RT1308_SEL_FS_SYS_SRC_PLL (0x2 << 24) +#define RT1308_SEL_FS_SYS_SRC_RCCLK (0x4 << 24) + +/* CLOCK-2 (0x08) */ +#define RT1308_DIV_PRE_PLL_MASK (0xf << 28) +#define RT1308_DIV_PRE_PLL_SFT 28 +#define RT1308_SEL_PLL_SRC_MASK (0x7 << 24) +#define RT1308_SEL_PLL_SRC_SFT 24 +#define RT1308_SEL_PLL_SRC_MCLK (0x0 << 24) +#define RT1308_SEL_PLL_SRC_BCLK (0x1 << 24) +#define RT1308_SEL_PLL_SRC_RCCLK (0x4 << 24) + +/* Clock Detect (0x0a) */ +#define RT1308_MCLK_DET_EN_MASK (0x1 << 25) +#define RT1308_MCLK_DET_EN_SFT 25 +#define RT1308_MCLK_DET_EN (0x1 << 25) +#define RT1308_BCLK_DET_EN_MASK (0x1 << 24) +#define RT1308_BCLK_DET_EN_SFT 24 +#define RT1308_BCLK_DET_EN (0x1 << 24) + +/* DAC Setting (0x10) */ +#define RT1308_DVOL_MUTE_R_EN_SFT 7 +#define RT1308_DVOL_MUTE_L_EN_SFT 6 + +/* I2S Setting-1 (0x15) */ +#define RT1308_I2S_DF_SEL_MASK (0x3 << 12) +#define RT1308_I2S_DF_SEL_SFT 12 +#define RT1308_I2S_DF_SEL_I2S (0x0 << 12) +#define RT1308_I2S_DF_SEL_LEFT (0x1 << 12) +#define RT1308_I2S_DF_SEL_PCM_A (0x2 << 12) +#define RT1308_I2S_DF_SEL_PCM_B (0x3 << 12) +#define RT1308_I2S_DL_RX_SEL_MASK (0x7 << 4) +#define RT1308_I2S_DL_RX_SEL_SFT 4 +#define RT1308_I2S_DL_RX_SEL_16B (0x0 << 4) +#define RT1308_I2S_DL_RX_SEL_20B (0x1 << 4) +#define RT1308_I2S_DL_RX_SEL_24B (0x2 << 4) +#define RT1308_I2S_DL_RX_SEL_32B (0x3 << 4) +#define RT1308_I2S_DL_RX_SEL_8B (0x4 << 4) +#define RT1308_I2S_DL_TX_SEL_MASK (0x7 << 0) +#define RT1308_I2S_DL_TX_SEL_SFT 0 +#define RT1308_I2S_DL_TX_SEL_16B (0x0 << 0) +#define RT1308_I2S_DL_TX_SEL_20B (0x1 << 0) +#define RT1308_I2S_DL_TX_SEL_24B (0x2 << 0) +#define RT1308_I2S_DL_TX_SEL_32B (0x3 << 0) +#define RT1308_I2S_DL_TX_SEL_8B (0x4 << 0) + +/* I2S Setting-2 (0x16) */ +#define RT1308_I2S_DL_SEL_MASK (0x7 << 24) +#define RT1308_I2S_DL_SEL_SFT 24 +#define RT1308_I2S_DL_SEL_16B (0x0 << 24) +#define RT1308_I2S_DL_SEL_20B (0x1 << 24) +#define RT1308_I2S_DL_SEL_24B (0x2 << 24) +#define RT1308_I2S_DL_SEL_32B (0x3 << 24) +#define RT1308_I2S_DL_SEL_8B (0x4 << 24) +#define RT1308_I2S_BCLK_MASK (0x1 << 14) +#define RT1308_I2S_BCLK_SFT 14 +#define RT1308_I2S_BCLK_NORMAL (0x0 << 14) +#define RT1308_I2S_BCLK_INV (0x1 << 14) + +/* Power Control-1 (0x32) */ +#define RT1308_POW_MBIAS20U (0x1 << 31) +#define RT1308_POW_MBIAS20U_BIT 31 +#define RT1308_POW_ALDO (0x1 << 30) +#define RT1308_POW_ALDO_BIT 30 +#define RT1308_POW_DBG (0x1 << 29) +#define RT1308_POW_DBG_BIT 29 +#define RT1308_POW_DACL (0x1 << 28) +#define RT1308_POW_DACL_BIT 28 +#define RT1308_POW_DAC1 (0x1 << 27) +#define RT1308_POW_DAC1_BIT 27 +#define RT1308_POW_CLK25M (0x1 << 26) +#define RT1308_POW_CLK25M_BIT 26 +#define RT1308_POW_ADC_R (0x1 << 25) +#define RT1308_POW_ADC_R_BIT 25 +#define RT1308_POW_ADC_L (0x1 << 24) +#define RT1308_POW_ADC_L_BIT 24 +#define RT1308_POW_DLDO (0x1 << 21) +#define RT1308_POW_DLDO_BIT 21 +#define RT1308_POW_VREF (0x1 << 20) +#define RT1308_POW_VREF_BIT 20 +#define RT1308_POW_MIXER_R (0x1 << 18) +#define RT1308_POW_MIXER_R_BIT 18 +#define RT1308_POW_MIXER_L (0x1 << 17) +#define RT1308_POW_MIXER_L_BIT 17 +#define RT1308_POW_MBIAS4U (0x1 << 16) +#define RT1308_POW_MBIAS4U_BIT 16 +#define RT1308_POW_PLL2_LDO_EN (0x1 << 12) +#define RT1308_POW_PLL2_LDO_EN_BIT 12 +#define RT1308_POW_PLL2B_EN (0x1 << 11) +#define RT1308_POW_PLL2B_EN_BIT 11 +#define RT1308_POW_PLL2F_EN (0x1 << 10) +#define RT1308_POW_PLL2F_EN_BIT 10 +#define RT1308_POW_PLL2F2_EN (0x1 << 9) +#define RT1308_POW_PLL2F2_EN_BIT 9 +#define RT1308_POW_PLL2B2_EN (0x1 << 8) +#define RT1308_POW_PLL2B2_EN_BIT 8 + +/* Power Control-2 (0x36) */ +#define RT1308_POW_PDB_SRC_BIT (0x1 << 27) +#define RT1308_POW_PDB_MN_BIT (0x1 << 25) +#define RT1308_POW_PDB_REG_BIT (0x1 << 24) + + +/* System Clock Source */ +enum { + RT1308_FS_SYS_S_MCLK, + RT1308_FS_SYS_S_BCLK, + RT1308_FS_SYS_S_PLL, + RT1308_FS_SYS_S_RCCLK, /* 25.0 MHz */ +}; + +/* PLL Source */ +enum { + RT1308_PLL_S_MCLK, + RT1308_PLL_S_BCLK, + RT1308_PLL_S_RCCLK, +}; + +enum { + RT1308_AIF1, + RT1308_AIFS +}; + +enum rt1308_hw_ver { + RT1308_VER_C = 2, + RT1308_VER_D +}; + +#endif /* end of _RT1308_H_ */ diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c new file mode 100644 index 0000000000..47511f7011 --- /dev/null +++ b/sound/soc/codecs/rt1316-sdw.c @@ -0,0 +1,796 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1316-sdw.c -- rt1316 SDCA ALSA SoC amplifier audio driver +// +// Copyright(c) 2021 Realtek Semiconductor Corp. +// +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt1316-sdw.h" + +static const struct reg_default rt1316_reg_defaults[] = { + { 0x3004, 0x00 }, + { 0x3005, 0x00 }, + { 0x3206, 0x00 }, + { 0xc001, 0x00 }, + { 0xc002, 0x00 }, + { 0xc003, 0x00 }, + { 0xc004, 0x00 }, + { 0xc005, 0x00 }, + { 0xc006, 0x00 }, + { 0xc007, 0x00 }, + { 0xc008, 0x00 }, + { 0xc009, 0x00 }, + { 0xc00a, 0x00 }, + { 0xc00b, 0x00 }, + { 0xc00c, 0x00 }, + { 0xc00d, 0x00 }, + { 0xc00e, 0x00 }, + { 0xc00f, 0x00 }, + { 0xc010, 0xa5 }, + { 0xc011, 0x00 }, + { 0xc012, 0xff }, + { 0xc013, 0xff }, + { 0xc014, 0x40 }, + { 0xc015, 0x00 }, + { 0xc016, 0x00 }, + { 0xc017, 0x00 }, + { 0xc605, 0x30 }, + { 0xc700, 0x0a }, + { 0xc701, 0xaa }, + { 0xc702, 0x1a }, + { 0xc703, 0x0a }, + { 0xc710, 0x80 }, + { 0xc711, 0x00 }, + { 0xc712, 0x3e }, + { 0xc713, 0x80 }, + { 0xc714, 0x80 }, + { 0xc715, 0x06 }, + { 0xd101, 0x00 }, + { 0xd102, 0x30 }, + { 0xd103, 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, +}; + +static const struct reg_sequence rt1316_blind_write[] = { + { 0xc710, 0x17 }, + { 0xc711, 0x80 }, + { 0xc712, 0x26 }, + { 0xc713, 0x06 }, + { 0xc714, 0x80 }, + { 0xc715, 0x06 }, + { 0xc702, 0x0a }, + { 0xc703, 0x0a }, + { 0xc001, 0x45 }, + { 0xc003, 0x00 }, + { 0xc004, 0x11 }, + { 0xc005, 0x00 }, + { 0xc006, 0x00 }, + { 0xc106, 0x00 }, + { 0xc007, 0x11 }, + { 0xc008, 0x11 }, + { 0xc009, 0x00 }, + + { 0x2f0a, 0x00 }, + { 0xd101, 0xf0 }, + { 0xd103, 0x9b }, + { 0x2f36, 0x8e }, + { 0x3206, 0x80 }, + { 0x3211, 0x0b }, + { 0x3216, 0x06 }, + { 0xc614, 0x20 }, + { 0xc615, 0x0a }, + { 0xc616, 0x02 }, + { 0xc617, 0x00 }, + { 0xc60b, 0x10 }, + { 0xc60e, 0x05 }, + { 0xc102, 0x00 }, + { 0xc090, 0xb0 }, + { 0xc00f, 0x01 }, + { 0xc09c, 0x7b }, + + { 0xc602, 0x07 }, + { 0xc603, 0x07 }, + { 0xc0a3, 0x71 }, + { 0xc00b, 0x30 }, + { 0xc093, 0x80 }, + { 0xc09d, 0x80 }, + { 0xc0b0, 0x77 }, + { 0xc010, 0xa5 }, + { 0xc050, 0x83 }, + { 0x2f55, 0x03 }, + { 0x3217, 0xb5 }, + { 0x3202, 0x02 }, + + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x00 }, + + /* for IV sense */ + { 0x2232, 0x80 }, + { 0xc0b0, 0x77 }, + { 0xc011, 0x00 }, + { 0xc020, 0x00 }, + { 0xc023, 0x00 }, + { 0x3101, 0x00 }, + { 0x3004, 0xa0 }, + { 0x3005, 0xb1 }, + { 0xc007, 0x11 }, + { 0xc008, 0x11 }, + { 0xc009, 0x00 }, + { 0xc022, 0xd6 }, + { 0xc025, 0xd6 }, + + { 0xd001, 0x03 }, + { 0xd002, 0xbf }, + { 0xd003, 0x03 }, + { 0xd004, 0xbf }, +}; + +static bool rt1316_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f0a: + case 0x2f36: + case 0x3203 ... 0x320e: + case 0xc000 ... 0xc7b4: + case 0xcf00 ... 0xcf03: + case 0xd101 ... 0xd103: + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE27, RT1316_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0): + return true; + default: + return false; + } +} + +static bool rt1316_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0xc000: + case 0xc093: + case 0xc09d: + case 0xc0a3: + case 0xc201: + case 0xc427 ... 0xc428: + case 0xd102: + return true; + default: + return false; + } +} + +static const struct regmap_config rt1316_sdw_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1316_readable_register, + .volatile_reg = rt1316_volatile_register, + .max_register = 0x4108ffff, + .reg_defaults = rt1316_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1316_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt1316_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x04; /* BITMAP: 00000100 */ + prop->sink_ports = 0x2; /* BITMAP: 00000010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + dev_dbg(&slave->dev, "%s\n", __func__); + + return 0; +} + +static void rt1316_apply_bq_params(struct rt1316_sdw_priv *rt1316) +{ + unsigned int i, reg, data; + + for (i = 0; i < rt1316->bq_params_cnt; i += 3) { + reg = rt1316->bq_params[i] | (rt1316->bq_params[i + 1] << 8); + data = rt1316->bq_params[i + 2]; + regmap_write(rt1316->regmap, reg, data); + } +} + +static int rt1316_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev); + + if (rt1316->hw_init) + return 0; + + regcache_cache_only(rt1316->regmap, false); + if (rt1316->first_hw_init) { + regcache_cache_bypass(rt1316->regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + /* sw reset */ + regmap_write(rt1316->regmap, 0xc000, 0x02); + + /* initial settings - blind write */ + regmap_multi_reg_write(rt1316->regmap, rt1316_blind_write, + ARRAY_SIZE(rt1316_blind_write)); + + if (rt1316->first_hw_init) { + regcache_cache_bypass(rt1316->regmap, false); + regcache_mark_dirty(rt1316->regmap); + } else + rt1316->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt1316->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +static int rt1316_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt1316->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt1316->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt1316_io_init(&slave->dev, slave); +} + +static int rt1316_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt1316->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, + RT1316_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + regmap_write(rt1316->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE27, + RT1316_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + regmap_write(rt1316->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, + RT1316_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt1316->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE23, + RT1316_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + regmap_write(rt1316->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE27, + RT1316_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + regmap_write(rt1316->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE22, + RT1316_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + + default: + break; + } + + return 0; +} + +static int rt1316_pde24_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt1316->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, + RT1316_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt1316->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_PDE24, + RT1316_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static const char * const rt1316_rx_data_ch_select[] = { + "L,R", + "L,L", + "L,R", + "L,L+R", + "R,L", + "R,R", + "R,L+R", + "L+R,L", + "L+R,R", + "L+R,L+R", +}; + +static SOC_ENUM_SINGLE_DECL(rt1316_rx_data_ch_enum, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0, + rt1316_rx_data_ch_select); + +static const char * const rt1316_dac_output_vol_select[] = { + "immediately", + "zero crossing", + "zero crossing with soft ramp", +}; + +static SOC_ENUM_SINGLE_DECL(rt1316_dac_vol_ctl_enum, + 0xc010, 6, rt1316_dac_output_vol_select); + +static const struct snd_kcontrol_new rt1316_snd_controls[] = { + + /* I2S Data Channel Selection */ + SOC_ENUM("RX Channel Select", rt1316_rx_data_ch_enum), + + /* XU24 Bypass Control */ + SOC_SINGLE("XU24 Bypass Switch", + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0, 1, 0), + + /* Left/Right IV tag */ + SOC_SINGLE("Left V Tag Select", 0x3004, 0, 7, 0), + SOC_SINGLE("Left I Tag Select", 0x3004, 4, 7, 0), + SOC_SINGLE("Right V Tag Select", 0x3005, 0, 7, 0), + SOC_SINGLE("Right I Tag Select", 0x3005, 4, 7, 0), + + /* IV mixer Control */ + SOC_DOUBLE("Isense Mixer Switch", 0xc605, 2, 0, 1, 1), + SOC_DOUBLE("Vsense Mixer Switch", 0xc605, 3, 1, 1, 1), + + /* DAC Output Volume Control */ + SOC_ENUM("DAC Output Vol Control", rt1316_dac_vol_ctl_enum), +}; + +static const struct snd_kcontrol_new rt1316_sto_dac = + SOC_DAPM_DOUBLE_R("Switch", + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L), + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R), + 0, 1, 1); + +static const struct snd_soc_dapm_widget rt1316_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1316_sto_dac), + + /* Output Lines */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1316_classd_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + + SND_SOC_DAPM_SUPPLY("PDE 24", SND_SOC_NOPM, 0, 0, + rt1316_pde24_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA("I Sense", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("V Sense", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SIGGEN("I Gen"), + SND_SOC_DAPM_SIGGEN("V Gen"), +}; + +static const struct snd_soc_dapm_route rt1316_dapm_routes[] = { + { "DAC", "Switch", "DP1RX" }, + { "CLASS D", NULL, "DAC" }, + { "SPOL", NULL, "CLASS D" }, + { "SPOR", NULL, "CLASS D" }, + + { "I Sense", NULL, "I Gen" }, + { "V Sense", NULL, "V Gen" }, + { "I Sense", NULL, "PDE 24" }, + { "V Sense", NULL, "PDE 24" }, + { "DP2TX", NULL, "I Sense" }, + { "DP2TX", NULL, "V Sense" }, +}; + +static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt1316_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt1316_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1316_sdw_priv *rt1316 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream; + int retval; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt1316->sdw_slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + /* port 1 for playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_config.num = 1; + else + port_config.num = 2; + + retval = sdw_stream_add_slave(rt1316->sdw_slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + return 0; +} + +static int rt1316_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1316_sdw_priv *rt1316 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt1316->sdw_slave) + return -EINVAL; + + sdw_stream_remove_slave(rt1316->sdw_slave, sdw_stream); + return 0; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static const struct sdw_slave_ops rt1316_slave_ops = { + .read_prop = rt1316_read_prop, + .update_status = rt1316_update_status, +}; + +static int rt1316_sdw_parse_dt(struct rt1316_sdw_priv *rt1316, struct device *dev) +{ + int ret = 0; + + device_property_read_u32(dev, "realtek,bq-params-cnt", &rt1316->bq_params_cnt); + if (rt1316->bq_params_cnt) { + rt1316->bq_params = devm_kzalloc(dev, rt1316->bq_params_cnt, GFP_KERNEL); + if (!rt1316->bq_params) { + dev_err(dev, "Could not allocate bq_params memory\n"); + ret = -ENOMEM; + } else { + ret = device_property_read_u8_array(dev, "realtek,bq-params", rt1316->bq_params, rt1316->bq_params_cnt); + if (ret < 0) + dev_err(dev, "Could not read list of realtek,bq-params\n"); + } + } + + dev_dbg(dev, "bq_params_cnt=%d\n", rt1316->bq_params_cnt); + return ret; +} + +static int rt1316_sdw_component_probe(struct snd_soc_component *component) +{ + struct rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component); + int ret; + + rt1316->component = component; + rt1316_sdw_parse_dt(rt1316, &rt1316->sdw_slave->dev); + + if (!rt1316->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + /* apply BQ params */ + rt1316_apply_bq_params(rt1316); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_sdw_rt1316 = { + .probe = rt1316_sdw_component_probe, + .controls = rt1316_snd_controls, + .num_controls = ARRAY_SIZE(rt1316_snd_controls), + .dapm_widgets = rt1316_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1316_dapm_widgets), + .dapm_routes = rt1316_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1316_dapm_routes), + .endianness = 1, +}; + +static const struct snd_soc_dai_ops rt1316_aif_dai_ops = { + .hw_params = rt1316_sdw_hw_params, + .hw_free = rt1316_sdw_pcm_hw_free, + .set_stream = rt1316_set_sdw_stream, + .shutdown = rt1316_sdw_shutdown, +}; + +#define RT1316_STEREO_RATES SNDRV_PCM_RATE_48000 +#define RT1316_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver rt1316_sdw_dai[] = { + { + .name = "rt1316-aif", + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1316_STEREO_RATES, + .formats = RT1316_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT1316_STEREO_RATES, + .formats = RT1316_FORMATS, + }, + .ops = &rt1316_aif_dai_ops, + }, +}; + +static int rt1316_sdw_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt1316_sdw_priv *rt1316; + int ret; + + rt1316 = devm_kzalloc(dev, sizeof(*rt1316), GFP_KERNEL); + if (!rt1316) + return -ENOMEM; + + dev_set_drvdata(dev, rt1316); + rt1316->sdw_slave = slave; + rt1316->regmap = regmap; + + regcache_cache_only(rt1316->regmap, true); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt1316->hw_init = false; + rt1316->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_component_sdw_rt1316, + rt1316_sdw_dai, + ARRAY_SIZE(rt1316_sdw_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +static int rt1316_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt1316_sdw_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt1316_sdw_init(&slave->dev, regmap, slave); +} + +static int rt1316_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); + + return 0; +} + +static const struct sdw_device_id rt1316_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1316, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt1316_id); + +static int __maybe_unused rt1316_dev_suspend(struct device *dev) +{ + struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev); + + if (!rt1316->hw_init) + return 0; + + regcache_cache_only(rt1316->regmap, true); + + return 0; +} + +#define RT1316_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt1316_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt1316->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT1316_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt1316->regmap, false); + regcache_sync(rt1316->regmap); + + return 0; +} + +static const struct dev_pm_ops rt1316_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume) + SET_RUNTIME_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume, NULL) +}; + +static struct sdw_driver rt1316_sdw_driver = { + .driver = { + .name = "rt1316-sdca", + .owner = THIS_MODULE, + .pm = &rt1316_pm, + }, + .probe = rt1316_sdw_probe, + .remove = rt1316_sdw_remove, + .ops = &rt1316_slave_ops, + .id_table = rt1316_id, +}; +module_sdw_driver(rt1316_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT1316 driver SDCA SDW"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1316-sdw.h b/sound/soc/codecs/rt1316-sdw.h new file mode 100644 index 0000000000..dc1bfe40ed --- /dev/null +++ b/sound/soc/codecs/rt1316-sdw.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1316-sdw.h -- RT1316 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2021 Realtek Semiconductor Corp. + */ + +#ifndef __RT1316_SDW_H__ +#define __RT1316_SDW_H__ + +#include +#include +#include +#include +#include + +/* RT1316 SDCA Control - function number */ +#define FUNC_NUM_SMART_AMP 0x04 + +/* RT1316 SDCA entity */ +#define RT1316_SDCA_ENT_PDE23 0x31 +#define RT1316_SDCA_ENT_PDE27 0x32 +#define RT1316_SDCA_ENT_PDE22 0x33 +#define RT1316_SDCA_ENT_PDE24 0x34 +#define RT1316_SDCA_ENT_XU24 0x24 +#define RT1316_SDCA_ENT_FU21 0x03 +#define RT1316_SDCA_ENT_UDMPU21 0x02 + +/* RT1316 SDCA control */ +#define RT1316_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT1316_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT1316_SDCA_CTL_BYPASS 0x01 +#define RT1316_SDCA_CTL_FU_MUTE 0x01 +#define RT1316_SDCA_CTL_FU_VOLUME 0x02 +#define RT1316_SDCA_CTL_UDMPU_CLUSTER 0x10 + +/* RT1316 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 + +struct rt1316_sdw_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct sdw_slave *sdw_slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + unsigned char *bq_params; + unsigned int bq_params_cnt; +}; + +#endif /* __RT1316_SDW_H__ */ diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c new file mode 100644 index 0000000000..ff364bde4a --- /dev/null +++ b/sound/soc/codecs/rt1318-sdw.c @@ -0,0 +1,870 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1318-sdw.c -- rt1318 SDCA ALSA SoC amplifier audio driver +// +// Copyright(c) 2022 Realtek Semiconductor Corp. +// +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt1318-sdw.h" + +static const struct reg_sequence rt1318_blind_write[] = { + { 0xc001, 0x43 }, + { 0xc003, 0xa2 }, + { 0xc004, 0x44 }, + { 0xc005, 0x44 }, + { 0xc006, 0x33 }, + { 0xc007, 0x64 }, + { 0xc320, 0x20 }, + { 0xf203, 0x18 }, + { 0xf211, 0x00 }, + { 0xf212, 0x26 }, + { 0xf20d, 0x17 }, + { 0xf214, 0x06 }, + { 0xf20e, 0x00 }, + { 0xf223, 0x7f }, + { 0xf224, 0xdb }, + { 0xf225, 0xee }, + { 0xf226, 0x3f }, + { 0xf227, 0x0f }, + { 0xf21a, 0x78 }, + { 0xf242, 0x3c }, + { 0xc321, 0x0b }, + { 0xc200, 0xd8 }, + { 0xc201, 0x27 }, + { 0xc202, 0x0f }, + { 0xf800, 0x20 }, + { 0xdf00, 0x10 }, + { 0xdf5f, 0x01 }, + { 0xdf60, 0xa7 }, + { 0xc400, 0x0e }, + { 0xc401, 0x43 }, + { 0xc402, 0xe0 }, + { 0xc403, 0x00 }, + { 0xc404, 0x4c }, + { 0xc407, 0x02 }, + { 0xc408, 0x3f }, + { 0xc300, 0x01 }, + { 0xc206, 0x78 }, + { 0xc203, 0x84 }, + { 0xc120, 0xc0 }, + { 0xc121, 0x03 }, + { 0xe000, 0x88 }, + { 0xc321, 0x09 }, + { 0xc322, 0x01 }, + { 0xe706, 0x0f }, + { 0xe707, 0x30 }, + { 0xe806, 0x0f }, + { 0xe807, 0x30 }, + { 0xed00, 0xb0 }, + { 0xce04, 0x02 }, + { 0xce05, 0x63 }, + { 0xce06, 0x68 }, + { 0xce07, 0x07 }, + { 0xcf04, 0x02 }, + { 0xcf05, 0x63 }, + { 0xcf06, 0x68 }, + { 0xcf07, 0x07 }, + { 0xce60, 0xe3 }, + { 0xc130, 0x51 }, + { 0xf102, 0x00 }, + { 0xf103, 0x00 }, + { 0xf104, 0xf5 }, + { 0xf105, 0x06 }, + { 0xf109, 0x9b }, + { 0xf10a, 0x0b }, + { 0xf10b, 0x4c }, + { 0xf10b, 0x5c }, + { 0xf102, 0x00 }, + { 0xf103, 0x00 }, + { 0xf104, 0xf5 }, + { 0xf105, 0x0b }, + { 0xf109, 0x03 }, + { 0xf10a, 0x0b }, + { 0xf10b, 0x4c }, + { 0xf10b, 0x5c }, + { 0xf102, 0x00 }, + { 0xf103, 0x00 }, + { 0xf104, 0xf5 }, + { 0xf105, 0x0c }, + { 0xf109, 0x7f }, + { 0xf10a, 0x0b }, + { 0xf10b, 0x4c }, + { 0xf10b, 0x5c }, + + { 0xe604, 0x00 }, + { 0xdb00, 0x0c }, + { 0xdd00, 0x0c }, + { 0xdc19, 0x00 }, + { 0xdc1a, 0xff }, + { 0xdc1b, 0xff }, + { 0xdc1c, 0xff }, + { 0xdc1d, 0x00 }, + { 0xdc1e, 0x00 }, + { 0xdc1f, 0x00 }, + { 0xdc20, 0xff }, + { 0xde19, 0x00 }, + { 0xde1a, 0xff }, + { 0xde1b, 0xff }, + { 0xde1c, 0xff }, + { 0xde1d, 0x00 }, + { 0xde1e, 0x00 }, + { 0xde1f, 0x00 }, + { 0xde20, 0xff }, + { 0xdb32, 0x00 }, + { 0xdd32, 0x00 }, + { 0xdb33, 0x0a }, + { 0xdd33, 0x0a }, + { 0xdb34, 0x1a }, + { 0xdd34, 0x1a }, + { 0xdb17, 0xef }, + { 0xdd17, 0xef }, + { 0xdba7, 0x00 }, + { 0xdba8, 0x64 }, + { 0xdda7, 0x00 }, + { 0xdda8, 0x64 }, + { 0xdb19, 0x40 }, + { 0xdd19, 0x40 }, + { 0xdb00, 0x4c }, + { 0xdb01, 0x79 }, + { 0xdd01, 0x79 }, + { 0xdb04, 0x05 }, + { 0xdb05, 0x03 }, + { 0xdd04, 0x05 }, + { 0xdd05, 0x03 }, + { 0xdbbb, 0x09 }, + { 0xdbbc, 0x30 }, + { 0xdbbd, 0xf0 }, + { 0xdbbe, 0xf1 }, + { 0xddbb, 0x09 }, + { 0xddbc, 0x30 }, + { 0xddbd, 0xf0 }, + { 0xddbe, 0xf1 }, + { 0xdb01, 0x79 }, + { 0xdd01, 0x79 }, + { 0xdc52, 0xef }, + { 0xde52, 0xef }, + { 0x2f55, 0x22 }, +}; + +static const struct reg_default rt1318_reg_defaults[] = { + { 0x3000, 0x00 }, + { 0x3004, 0x01 }, + { 0x3005, 0x23 }, + { 0x3202, 0x00 }, + { 0x3203, 0x01 }, + { 0x3206, 0x00 }, + { 0xc000, 0x00 }, + { 0xc001, 0x43 }, + { 0xc003, 0x22 }, + { 0xc004, 0x44 }, + { 0xc005, 0x44 }, + { 0xc006, 0x33 }, + { 0xc007, 0x64 }, + { 0xc008, 0x05 }, + { 0xc00a, 0xfc }, + { 0xc00b, 0x0f }, + { 0xc00c, 0x0e }, + { 0xc00d, 0xef }, + { 0xc00e, 0xe5 }, + { 0xc00f, 0xff }, + { 0xc120, 0xc0 }, + { 0xc121, 0x00 }, + { 0xc122, 0x00 }, + { 0xc123, 0x14 }, + { 0xc125, 0x00 }, + { 0xc200, 0x00 }, + { 0xc201, 0x00 }, + { 0xc202, 0x00 }, + { 0xc203, 0x04 }, + { 0xc204, 0x00 }, + { 0xc205, 0x00 }, + { 0xc206, 0x68 }, + { 0xc207, 0x70 }, + { 0xc208, 0x00 }, + { 0xc20a, 0x00 }, + { 0xc20b, 0x01 }, + { 0xc20c, 0x7f }, + { 0xc20d, 0x01 }, + { 0xc20e, 0x7f }, + { 0xc300, 0x00 }, + { 0xc301, 0x00 }, + { 0xc303, 0x80 }, + { 0xc320, 0x00 }, + { 0xc321, 0x09 }, + { 0xc322, 0x02 }, + { 0xc410, 0x04 }, + { 0xc430, 0x00 }, + { 0xc431, 0x00 }, + { 0xca00, 0x10 }, + { 0xca01, 0x00 }, + { 0xca02, 0x0b }, + { 0xca10, 0x10 }, + { 0xca11, 0x00 }, + { 0xca12, 0x0b }, + { 0xdd93, 0x00 }, + { 0xdd94, 0x64 }, + { 0xe300, 0xa0 }, + { 0xed00, 0x80 }, + { 0xed01, 0x0f }, + { 0xed02, 0xff }, + { 0xed03, 0x00 }, + { 0xed04, 0x00 }, + { 0xed05, 0x0f }, + { 0xed06, 0xff }, + { 0xf010, 0x10 }, + { 0xf011, 0xec }, + { 0xf012, 0x68 }, + { 0xf013, 0x21 }, + { 0xf800, 0x00 }, + { 0xf801, 0x12 }, + { 0xf802, 0xe0 }, + { 0xf803, 0x2f }, + { 0xf804, 0x00 }, + { 0xf805, 0x00 }, + { 0xf806, 0x07 }, + { 0xf807, 0xff }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, RT1318_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, +}; + +static bool rt1318_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f55: + case 0x3000: + case 0x3004 ... 0x3005: + case 0x3202 ... 0x3203: + case 0x3206: + case 0xc000 ... 0xc00f: + case 0xc120 ... 0xc125: + case 0xc200 ... 0xc20e: + case 0xc300 ... 0xc303: + case 0xc320 ... 0xc322: + case 0xc410: + case 0xc430 ... 0xc431: + case 0xca00 ... 0xca02: + case 0xca10 ... 0xca12: + case 0xcb00 ... 0xcb0b: + case 0xcc00 ... 0xcce5: + case 0xcd00 ... 0xcde5: + case 0xce00 ... 0xce6a: + case 0xcf00 ... 0xcf53: + case 0xd000 ... 0xd0cc: + case 0xd100 ... 0xd1b9: + case 0xdb00 ... 0xdc53: + case 0xdd00 ... 0xde53: + case 0xdf00 ... 0xdf6b: + case 0xe300: + case 0xeb00 ... 0xebcc: + case 0xec00 ... 0xecb9: + case 0xed00 ... 0xed06: + case 0xf010 ... 0xf014: + case 0xf800 ... 0xf807: + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, RT1318_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_STATUS, 0): + return true; + default: + return false; + } +} + +static bool rt1318_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f55: + case 0x3000 ... 0x3001: + case 0xc000: + case 0xc301: + case 0xc410: + case 0xc430 ... 0xc431: + case 0xdb06: + case 0xdb12: + case 0xdb1d ... 0xdb1f: + case 0xdb35: + case 0xdb37: + case 0xdb8a ... 0xdb92: + case 0xdbc5 ... 0xdbc8: + case 0xdc2b ... 0xdc49: + case 0xdd0b: + case 0xdd12: + case 0xdd1d ... 0xdd1f: + case 0xdd35: + case 0xdd8a ... 0xdd92: + case 0xddc5 ... 0xddc8: + case 0xde2b ... 0xde44: + case 0xdf4a ... 0xdf55: + case 0xe224 ... 0xe23b: + case 0xea01: + case 0xebc5: + case 0xebc8: + case 0xebcb ... 0xebcc: + case 0xed03 ... 0xed06: + case 0xf010 ... 0xf014: + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_SAPU, RT1318_SDCA_CTL_SAPU_PROTECTION_STATUS, 0): + return true; + default: + return false; + } +} + +static const struct regmap_config rt1318_sdw_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt1318_readable_register, + .volatile_reg = rt1318_volatile_register, + .max_register = 0x41081488, + .reg_defaults = rt1318_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt1318_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt1318_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = BIT(2); + prop->sink_ports = BIT(1); + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + return 0; +} + +static int rt1318_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev); + + if (rt1318->hw_init) + return 0; + + regcache_cache_only(rt1318->regmap, false); + if (rt1318->first_hw_init) { + regcache_cache_bypass(rt1318->regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + /* blind write */ + regmap_multi_reg_write(rt1318->regmap, rt1318_blind_write, + ARRAY_SIZE(rt1318_blind_write)); + + if (rt1318->first_hw_init) { + regcache_cache_bypass(rt1318->regmap, false); + regcache_mark_dirty(rt1318->regmap); + } + + /* Mark Slave initialization complete */ + rt1318->first_hw_init = true; + rt1318->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +static int rt1318_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt1318->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt1318->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt1318_io_init(&slave->dev, slave); +} + +static int rt1318_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt1318_sdw_priv *rt1318 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt1318->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, + RT1318_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt1318->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_PDE23, + RT1318_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + + default: + break; + } + + return 0; +} + +static const char * const rt1318_rx_data_ch_select[] = { + "L,R", + "L,L", + "L,R", + "L,L+R", + "R,L", + "R,R", + "R,L+R", + "L+R,L", + "L+R,R", + "L+R,L+R", +}; + +static SOC_ENUM_SINGLE_DECL(rt1318_rx_data_ch_enum, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_UDMPU21, RT1318_SDCA_CTL_UDMPU_CLUSTER, 0), 0, + rt1318_rx_data_ch_select); + +static const struct snd_kcontrol_new rt1318_snd_controls[] = { + + /* UDMPU Cluster Selection */ + SOC_ENUM("RX Channel Select", rt1318_rx_data_ch_enum), +}; + +static const struct snd_kcontrol_new rt1318_sto_dac = + SOC_DAPM_DOUBLE_R("Switch", + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_L), + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_FU21, RT1318_SDCA_CTL_FU_MUTE, CH_R), + 0, 1, 1); + +static const struct snd_soc_dapm_widget rt1318_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1318_sto_dac), + + /* Output */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1318_classd_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + /* Input */ + SND_SOC_DAPM_PGA("FB Data", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SIGGEN("FB Gen"), +}; + +static const struct snd_soc_dapm_route rt1318_dapm_routes[] = { + { "DAC", "Switch", "DP1RX" }, + { "CLASS D", NULL, "DAC" }, + { "SPOL", NULL, "CLASS D" }, + { "SPOR", NULL, "CLASS D" }, + + { "FB Data", NULL, "FB Gen" }, + { "DP2TX", NULL, "FB Data" }, +}; + +static int rt1318_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt1318_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_sdw_priv *rt1318 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + int retval, port, num_channels, ch_mask; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt1318->sdw_slave) + return -EINVAL; + + /* SoundWire specific configuration */ + /* port 1 for playback */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + port = 1; + } else { + direction = SDW_DATA_DIR_TX; + port = 2; + } + + num_channels = params_channels(params); + ch_mask = (1 << num_channels) - 1; + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = num_channels; + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + port_config.ch_mask = ch_mask; + port_config.num = port; + + retval = sdw_stream_add_slave(rt1318->sdw_slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 16000: + sampling_rate = RT1318_SDCA_RATE_16000HZ; + break; + case 32000: + sampling_rate = RT1318_SDCA_RATE_32000HZ; + break; + case 44100: + sampling_rate = RT1318_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT1318_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT1318_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT1318_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + regmap_write(rt1318->regmap, + SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1318_SDCA_ENT_CS21, RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + + return 0; +} + +static int rt1318_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1318_sdw_priv *rt1318 = + snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt1318->sdw_slave) + return -EINVAL; + + sdw_stream_remove_slave(rt1318->sdw_slave, sdw_stream); + return 0; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static const struct sdw_slave_ops rt1318_slave_ops = { + .read_prop = rt1318_read_prop, + .update_status = rt1318_update_status, +}; + +static int rt1318_sdw_component_probe(struct snd_soc_component *component) +{ + int ret; + struct rt1318_sdw_priv *rt1318 = snd_soc_component_get_drvdata(component); + + rt1318->component = component; + + if (!rt1318->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + dev_dbg(&rt1318->sdw_slave->dev, "%s pm_runtime_resume, ret=%d", __func__, ret); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_component_sdw_rt1318 = { + .probe = rt1318_sdw_component_probe, + .controls = rt1318_snd_controls, + .num_controls = ARRAY_SIZE(rt1318_snd_controls), + .dapm_widgets = rt1318_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1318_dapm_widgets), + .dapm_routes = rt1318_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1318_dapm_routes), + .endianness = 1, +}; + +static const struct snd_soc_dai_ops rt1318_aif_dai_ops = { + .hw_params = rt1318_sdw_hw_params, + .hw_free = rt1318_sdw_pcm_hw_free, + .set_stream = rt1318_set_sdw_stream, + .shutdown = rt1318_sdw_shutdown, +}; + +#define RT1318_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT1318_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver rt1318_sdw_dai[] = { + { + .name = "rt1318-aif", + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT1318_STEREO_RATES, + .formats = RT1318_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT1318_STEREO_RATES, + .formats = RT1318_FORMATS, + }, + .ops = &rt1318_aif_dai_ops, + }, +}; + +static int rt1318_sdw_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt1318_sdw_priv *rt1318; + int ret; + + rt1318 = devm_kzalloc(dev, sizeof(*rt1318), GFP_KERNEL); + if (!rt1318) + return -ENOMEM; + + dev_set_drvdata(dev, rt1318); + rt1318->sdw_slave = slave; + rt1318->regmap = regmap; + + regcache_cache_only(rt1318->regmap, true); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt1318->hw_init = false; + rt1318->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_component_sdw_rt1318, + rt1318_sdw_dai, + ARRAY_SIZE(rt1318_sdw_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return ret; +} + +static int rt1318_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt1318_sdw_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt1318_sdw_init(&slave->dev, regmap, slave); +} + +static int rt1318_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); + + return 0; +} + +static const struct sdw_device_id rt1318_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1318, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt1318_id); + +static int __maybe_unused rt1318_dev_suspend(struct device *dev) +{ + struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev); + + if (!rt1318->hw_init) + return 0; + + regcache_cache_only(rt1318->regmap, true); + return 0; +} + +#define RT1318_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt1318_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt1318->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT1318_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt1318->regmap, false); + regcache_sync(rt1318->regmap); + + return 0; +} + +static const struct dev_pm_ops rt1318_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume) + SET_RUNTIME_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume, NULL) +}; + +static struct sdw_driver rt1318_sdw_driver = { + .driver = { + .name = "rt1318-sdca", + .owner = THIS_MODULE, + .pm = &rt1318_pm, + }, + .probe = rt1318_sdw_probe, + .remove = rt1318_sdw_remove, + .ops = &rt1318_slave_ops, + .id_table = rt1318_id, +}; +module_sdw_driver(rt1318_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT1318 driver SDCA SDW"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt1318-sdw.h b/sound/soc/codecs/rt1318-sdw.h new file mode 100644 index 0000000000..86e83d63a0 --- /dev/null +++ b/sound/soc/codecs/rt1318-sdw.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt1318-sdw.h -- RT1318 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2022 Realtek Semiconductor Corp. + */ + +#ifndef __RT1318_SDW_H__ +#define __RT1318_SDW_H__ + +#include +#include +#include +#include +#include + +/* imp-defined registers */ +#define RT1318_SAPU_SM 0x3203 + +#define R1318_TCON 0xc203 +#define R1318_TCON_RELATED_1 0xc206 + +#define R1318_SPK_TEMPERATRUE_PROTECTION_0 0xdb00 +#define R1318_SPK_TEMPERATRUE_PROTECTION_L_4 0xdb08 +#define R1318_SPK_TEMPERATRUE_PROTECTION_R_4 0xdd08 + +#define R1318_SPK_TEMPERATRUE_PROTECTION_L_6 0xdb12 +#define R1318_SPK_TEMPERATRUE_PROTECTION_R_6 0xdd12 + +#define RT1318_INIT_RECIPROCAL_REG_L_24 0xdbb5 +#define RT1318_INIT_RECIPROCAL_REG_L_23_16 0xdbb6 +#define RT1318_INIT_RECIPROCAL_REG_L_15_8 0xdbb7 +#define RT1318_INIT_RECIPROCAL_REG_L_7_0 0xdbb8 +#define RT1318_INIT_RECIPROCAL_REG_R_24 0xddb5 +#define RT1318_INIT_RECIPROCAL_REG_R_23_16 0xddb6 +#define RT1318_INIT_RECIPROCAL_REG_R_15_8 0xddb7 +#define RT1318_INIT_RECIPROCAL_REG_R_7_0 0xddb8 + +#define RT1318_INIT_R0_RECIPROCAL_SYN_L_24 0xdbc5 +#define RT1318_INIT_R0_RECIPROCAL_SYN_L_23_16 0xdbc6 +#define RT1318_INIT_R0_RECIPROCAL_SYN_L_15_8 0xdbc7 +#define RT1318_INIT_R0_RECIPROCAL_SYN_L_7_0 0xdbc8 +#define RT1318_INIT_R0_RECIPROCAL_SYN_R_24 0xddc5 +#define RT1318_INIT_R0_RECIPROCAL_SYN_R_23_16 0xddc6 +#define RT1318_INIT_R0_RECIPROCAL_SYN_R_15_8 0xddc7 +#define RT1318_INIT_R0_RECIPROCAL_SYN_R_7_0 0xddc8 + +#define RT1318_R0_COMPARE_FLAG_L 0xdb35 +#define RT1318_R0_COMPARE_FLAG_R 0xdd35 + +#define RT1318_STP_INITIAL_RS_TEMP_H 0xdd93 +#define RT1318_STP_INITIAL_RS_TEMP_L 0xdd94 + +/* RT1318 SDCA Control - function number */ +#define FUNC_NUM_SMART_AMP 0x04 + +/* RT1318 SDCA entity */ +#define RT1318_SDCA_ENT_PDE23 0x31 +#define RT1318_SDCA_ENT_XU24 0x24 +#define RT1318_SDCA_ENT_FU21 0x03 +#define RT1318_SDCA_ENT_UDMPU21 0x02 +#define RT1318_SDCA_ENT_CS21 0x21 +#define RT1318_SDCA_ENT_SAPU 0x29 + +/* RT1318 SDCA control */ +#define RT1318_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT1318_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT1318_SDCA_CTL_FU_MUTE 0x01 +#define RT1318_SDCA_CTL_FU_VOLUME 0x02 +#define RT1318_SDCA_CTL_UDMPU_CLUSTER 0x10 +#define RT1318_SDCA_CTL_SAPU_PROTECTION_MODE 0x10 +#define RT1318_SDCA_CTL_SAPU_PROTECTION_STATUS 0x11 + +/* RT1318 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 + +/* sample frequency index */ +#define RT1318_SDCA_RATE_16000HZ 0x04 +#define RT1318_SDCA_RATE_32000HZ 0x07 +#define RT1318_SDCA_RATE_44100HZ 0x08 +#define RT1318_SDCA_RATE_48000HZ 0x09 +#define RT1318_SDCA_RATE_96000HZ 0x0b +#define RT1318_SDCA_RATE_192000HZ 0x0d + + +struct rt1318_sdw_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct sdw_slave *sdw_slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; +}; + +#endif /* __RT1318_SDW_H__ */ diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c new file mode 100644 index 0000000000..9a33e3776b --- /dev/null +++ b/sound/soc/codecs/rt274.c @@ -0,0 +1,1233 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt274.c -- RT274 ALSA SoC audio codec driver + * + * Copyright 2017 Realtek Semiconductor Corp. + * Author: Bard Liao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6347a.h" +#include "rt274.h" + +#define RT274_VENDOR_ID 0x10ec0274 + +struct rt274_priv { + struct reg_default *index_cache; + int index_cache_size; + struct regmap *regmap; + struct snd_soc_component *component; + struct i2c_client *i2c; + struct snd_soc_jack *jack; + struct delayed_work jack_detect_work; + int sys_clk; + int clk_id; + int fs; + bool master; +}; + +static const struct reg_default rt274_index_def[] = { + { 0x00, 0x1004 }, + { 0x01, 0xaaaa }, + { 0x02, 0x88aa }, + { 0x03, 0x0002 }, + { 0x04, 0xaa09 }, + { 0x05, 0x0700 }, + { 0x06, 0x6110 }, + { 0x07, 0x0200 }, + { 0x08, 0xa807 }, + { 0x09, 0x0021 }, + { 0x0a, 0x7770 }, + { 0x0b, 0x7770 }, + { 0x0c, 0x002b }, + { 0x0d, 0x2420 }, + { 0x0e, 0x65c0 }, + { 0x0f, 0x7770 }, + { 0x10, 0x0420 }, + { 0x11, 0x7418 }, + { 0x12, 0x6bd0 }, + { 0x13, 0x645f }, + { 0x14, 0x0400 }, + { 0x15, 0x8ccc }, + { 0x16, 0x4c50 }, + { 0x17, 0xff00 }, + { 0x18, 0x0003 }, + { 0x19, 0x2c11 }, + { 0x1a, 0x830b }, + { 0x1b, 0x4e4b }, + { 0x1c, 0x0000 }, + { 0x1d, 0x0000 }, + { 0x1e, 0x0000 }, + { 0x1f, 0x0000 }, + { 0x20, 0x51ff }, + { 0x21, 0x8000 }, + { 0x22, 0x8f00 }, + { 0x23, 0x88f4 }, + { 0x24, 0x0000 }, + { 0x25, 0x0000 }, + { 0x26, 0x0000 }, + { 0x27, 0x0000 }, + { 0x28, 0x0000 }, + { 0x29, 0x3000 }, + { 0x2a, 0x0000 }, + { 0x2b, 0x0000 }, + { 0x2c, 0x0f00 }, + { 0x2d, 0x100f }, + { 0x2e, 0x2902 }, + { 0x2f, 0xe280 }, + { 0x30, 0x1000 }, + { 0x31, 0x8400 }, + { 0x32, 0x5aaa }, + { 0x33, 0x8420 }, + { 0x34, 0xa20c }, + { 0x35, 0x096a }, + { 0x36, 0x5757 }, + { 0x37, 0xfe05 }, + { 0x38, 0x4901 }, + { 0x39, 0x110a }, + { 0x3a, 0x0010 }, + { 0x3b, 0x60d9 }, + { 0x3c, 0xf214 }, + { 0x3d, 0xc2ba }, + { 0x3e, 0xa928 }, + { 0x3f, 0x0000 }, + { 0x40, 0x9800 }, + { 0x41, 0x0000 }, + { 0x42, 0x2000 }, + { 0x43, 0x3d90 }, + { 0x44, 0x4900 }, + { 0x45, 0x5289 }, + { 0x46, 0x0004 }, + { 0x47, 0xa47a }, + { 0x48, 0xd049 }, + { 0x49, 0x0049 }, + { 0x4a, 0xa83b }, + { 0x4b, 0x0777 }, + { 0x4c, 0x065c }, + { 0x4d, 0x7fff }, + { 0x4e, 0x7fff }, + { 0x4f, 0x0000 }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0xbf5f }, + { 0x53, 0x3320 }, + { 0x54, 0xcc00 }, + { 0x55, 0x0000 }, + { 0x56, 0x3f00 }, + { 0x57, 0x0000 }, + { 0x58, 0x0000 }, + { 0x59, 0x0000 }, + { 0x5a, 0x1300 }, + { 0x5b, 0x005f }, + { 0x5c, 0x0000 }, + { 0x5d, 0x1001 }, + { 0x5e, 0x1000 }, + { 0x5f, 0x0000 }, + { 0x60, 0x5554 }, + { 0x61, 0xffc0 }, + { 0x62, 0xa000 }, + { 0x63, 0xd010 }, + { 0x64, 0x0000 }, + { 0x65, 0x3fb1 }, + { 0x66, 0x1881 }, + { 0x67, 0xc810 }, + { 0x68, 0x2000 }, + { 0x69, 0xfff0 }, + { 0x6a, 0x0300 }, + { 0x6b, 0x5060 }, + { 0x6c, 0x0000 }, + { 0x6d, 0x0000 }, + { 0x6e, 0x0c25 }, + { 0x6f, 0x0c0b }, + { 0x70, 0x8000 }, + { 0x71, 0x4008 }, + { 0x72, 0x0000 }, + { 0x73, 0x0800 }, + { 0x74, 0xa28f }, + { 0x75, 0xa050 }, + { 0x76, 0x7fe8 }, + { 0x77, 0xdb8c }, + { 0x78, 0x0000 }, + { 0x79, 0x0000 }, + { 0x7a, 0x2a96 }, + { 0x7b, 0x800f }, + { 0x7c, 0x0200 }, + { 0x7d, 0x1600 }, + { 0x7e, 0x0000 }, + { 0x7f, 0x0000 }, +}; +#define INDEX_CACHE_SIZE ARRAY_SIZE(rt274_index_def) + +static const struct reg_default rt274_reg[] = { + { 0x00170500, 0x00000400 }, + { 0x00220000, 0x00000031 }, + { 0x00239000, 0x00000057 }, + { 0x0023a000, 0x00000057 }, + { 0x00270500, 0x00000400 }, + { 0x00370500, 0x00000400 }, + { 0x00870500, 0x00000400 }, + { 0x00920000, 0x00000031 }, + { 0x00935000, 0x00000097 }, + { 0x00936000, 0x00000097 }, + { 0x00970500, 0x00000400 }, + { 0x00b37000, 0x00000400 }, + { 0x00b37200, 0x00000400 }, + { 0x00b37300, 0x00000400 }, + { 0x00c37000, 0x00000400 }, + { 0x00c37100, 0x00000400 }, + { 0x01270500, 0x00000400 }, + { 0x01370500, 0x00000400 }, + { 0x01371f00, 0x411111f0 }, + { 0x01937000, 0x00000000 }, + { 0x01970500, 0x00000400 }, + { 0x02050000, 0x0000001b }, + { 0x02139000, 0x00000080 }, + { 0x0213a000, 0x00000080 }, + { 0x02170100, 0x00000001 }, + { 0x02170500, 0x00000400 }, + { 0x02170700, 0x00000000 }, + { 0x02270100, 0x00000000 }, + { 0x02370100, 0x00000000 }, + { 0x01970700, 0x00000020 }, + { 0x00830000, 0x00000097 }, + { 0x00930000, 0x00000097 }, + { 0x01270700, 0x00000000 }, +}; + +static bool rt274_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... 0xff: + case RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID): + case RT274_GET_HP_SENSE: + case RT274_GET_MIC_SENSE: + case RT274_PROC_COEF: + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_MIC, 0): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_HP_OUT, 0): + case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT0, 0): + case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT1, 0): + case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN1, 0): + case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN2, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN2, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC1, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC2, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_MIC, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE1, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE2, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_HP_OUT, 0): + case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_HP_OUT, 0): + case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN1, 0): + case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN2, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC1, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC2, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_MIC, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE1, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE2, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_HP_OUT, 0): + case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_HP_OUT, 0): + case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_MIC, 0): + case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_INLINE_CMD, 0): + return true; + default: + return false; + } + + +} + +static bool rt274_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... 0xff: + case RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID): + case RT274_GET_HP_SENSE: + case RT274_GET_MIC_SENSE: + case RT274_SET_AUDIO_POWER: + case RT274_SET_HPO_POWER: + case RT274_SET_DMIC1_POWER: + case RT274_LOUT_MUX: + case RT274_HPO_MUX: + case RT274_ADC0_MUX: + case RT274_ADC1_MUX: + case RT274_SET_MIC: + case RT274_SET_PIN_HPO: + case RT274_SET_PIN_LOUT3: + case RT274_SET_PIN_DMIC1: + case RT274_SET_AMP_GAIN_HPO: + case RT274_SET_DMIC2_DEFAULT: + case RT274_DAC0L_GAIN: + case RT274_DAC0R_GAIN: + case RT274_DAC1L_GAIN: + case RT274_DAC1R_GAIN: + case RT274_ADCL_GAIN: + case RT274_ADCR_GAIN: + case RT274_MIC_GAIN: + case RT274_HPOL_GAIN: + case RT274_HPOR_GAIN: + case RT274_LOUTL_GAIN: + case RT274_LOUTR_GAIN: + case RT274_DAC_FORMAT: + case RT274_ADC_FORMAT: + case RT274_COEF_INDEX: + case RT274_PROC_COEF: + case RT274_SET_AMP_GAIN_ADC_IN1: + case RT274_SET_AMP_GAIN_ADC_IN2: + case RT274_SET_POWER(RT274_DAC_OUT0): + case RT274_SET_POWER(RT274_DAC_OUT1): + case RT274_SET_POWER(RT274_ADC_IN1): + case RT274_SET_POWER(RT274_ADC_IN2): + case RT274_SET_POWER(RT274_DMIC2): + case RT274_SET_POWER(RT274_MIC): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_MIC, 0): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT274_HP_OUT, 0): + case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT0, 0): + case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_DAC_OUT1, 0): + case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN1, 0): + case VERB_CMD(AC_VERB_GET_STREAM_FORMAT, RT274_ADC_IN2, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_ADC_IN2, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC1, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_DMIC2, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_MIC, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE1, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_LINE2, 0): + case VERB_CMD(AC_VERB_GET_AMP_GAIN_MUTE, RT274_HP_OUT, 0): + case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_HP_OUT, 0): + case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN1, 0): + case VERB_CMD(AC_VERB_GET_CONNECT_SEL, RT274_MIXER_IN2, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC1, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_DMIC2, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_MIC, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE1, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_LINE2, 0): + case VERB_CMD(AC_VERB_GET_PIN_WIDGET_CONTROL, RT274_HP_OUT, 0): + case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_HP_OUT, 0): + case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_MIC, 0): + case VERB_CMD(AC_VERB_GET_UNSOLICITED_RESPONSE, RT274_INLINE_CMD, 0): + return true; + default: + return false; + } +} + +#ifdef CONFIG_PM +static void rt274_index_sync(struct snd_soc_component *component) +{ + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + int i; + + for (i = 0; i < INDEX_CACHE_SIZE; i++) { + snd_soc_component_write(component, rt274->index_cache[i].reg, + rt274->index_cache[i].def); + } +} +#endif + +static int rt274_jack_detect(struct rt274_priv *rt274, bool *hp, bool *mic) +{ + unsigned int buf; + int ret; + + *hp = false; + *mic = false; + + if (!rt274->component) + return -EINVAL; + + ret = regmap_read(rt274->regmap, RT274_GET_HP_SENSE, &buf); + if (ret) + return ret; + + *hp = buf & 0x80000000; + ret = regmap_read(rt274->regmap, RT274_GET_MIC_SENSE, &buf); + if (ret) + return ret; + + *mic = buf & 0x80000000; + + pr_debug("*hp = %d *mic = %d\n", *hp, *mic); + + return 0; +} + +static void rt274_jack_detect_work(struct work_struct *work) +{ + struct rt274_priv *rt274 = + container_of(work, struct rt274_priv, jack_detect_work.work); + int status = 0; + bool hp = false; + bool mic = false; + + if (rt274_jack_detect(rt274, &hp, &mic) < 0) + return; + + if (hp) + status |= SND_JACK_HEADPHONE; + + if (mic) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt274->jack, status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); +} + +static irqreturn_t rt274_irq(int irq, void *data); + +static int rt274_mic_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + + rt274->jack = jack; + + if (jack == NULL) { + /* Disable jack detection */ + regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL, + RT274_IRQ_EN, RT274_IRQ_DIS); + + return 0; + } + + regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL, + RT274_IRQ_EN, RT274_IRQ_EN); + + /* Send an initial report */ + rt274_irq(0, rt274); + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt274_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT274_DAC0L_GAIN, + RT274_DAC0R_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_DOUBLE_R_TLV("DAC1 Playback Volume", RT274_DAC1L_GAIN, + RT274_DAC1R_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT274_ADCL_GAIN, + RT274_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_DOUBLE_R("ADC0 Capture Switch", RT274_ADCL_GAIN, + RT274_ADCR_GAIN, RT274_MUTE_SFT, 1, 1), + SOC_SINGLE_TLV("AMIC Volume", RT274_MIC_GAIN, + 0, 0x3, 0, mic_vol_tlv), +}; + +static const struct snd_kcontrol_new hpol_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_HPOL_GAIN, + RT274_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpor_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_HPOR_GAIN, + RT274_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new loutl_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_LOUTL_GAIN, + RT274_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new loutr_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT274_LOUTR_GAIN, + RT274_MUTE_SFT, 1, 1); + +/* ADC0 source */ +static const char * const rt274_adc_src[] = { + "Mic", "Line1", "Line2", "Dmic" +}; + +static SOC_ENUM_SINGLE_DECL( + rt274_adc0_enum, RT274_ADC0_MUX, RT274_ADC_SEL_SFT, + rt274_adc_src); + +static const struct snd_kcontrol_new rt274_adc0_mux = + SOC_DAPM_ENUM("ADC 0 source", rt274_adc0_enum); + +static SOC_ENUM_SINGLE_DECL( + rt274_adc1_enum, RT274_ADC1_MUX, RT274_ADC_SEL_SFT, + rt274_adc_src); + +static const struct snd_kcontrol_new rt274_adc1_mux = + SOC_DAPM_ENUM("ADC 1 source", rt274_adc1_enum); + +static const char * const rt274_dac_src[] = { + "DAC OUT0", "DAC OUT1" +}; +/* HP-OUT source */ +static SOC_ENUM_SINGLE_DECL(rt274_hpo_enum, RT274_HPO_MUX, + 0, rt274_dac_src); + +static const struct snd_kcontrol_new rt274_hpo_mux = +SOC_DAPM_ENUM("HPO source", rt274_hpo_enum); + +/* Line out source */ +static SOC_ENUM_SINGLE_DECL(rt274_lout_enum, RT274_LOUT_MUX, + 0, rt274_dac_src); + +static const struct snd_kcontrol_new rt274_lout_mux = +SOC_DAPM_ENUM("LOUT source", rt274_lout_enum); + +static const struct snd_soc_dapm_widget rt274_dapm_widgets[] = { + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1 Pin"), + SND_SOC_DAPM_INPUT("DMIC2 Pin"), + SND_SOC_DAPM_INPUT("MIC"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + + /* DMIC */ + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC 0", NULL, RT274_SET_STREAMID_ADC1, 4, 0), + SND_SOC_DAPM_ADC("ADC 1", NULL, RT274_SET_STREAMID_ADC2, 4, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("ADC 0 Mux", SND_SOC_NOPM, 0, 0, + &rt274_adc0_mux), + SND_SOC_DAPM_MUX("ADC 1 Mux", SND_SOC_NOPM, 0, 0, + &rt274_adc1_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RXL", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF1RXR", "AIF1 Playback", 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TXL", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TXR", "AIF1 Capture", 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RXL", "AIF1 Playback", 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RXR", "AIF1 Playback", 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TXL", "AIF1 Capture", 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TXR", "AIF1 Capture", 3, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("DAC 0", NULL, RT274_SET_STREAMID_DAC0, 4, 0), + SND_SOC_DAPM_DAC("DAC 1", NULL, RT274_SET_STREAMID_DAC1, 4, 0), + + /* Output Mux */ + SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt274_hpo_mux), + SND_SOC_DAPM_MUX("LOUT Mux", SND_SOC_NOPM, 0, 0, &rt274_lout_mux), + + SND_SOC_DAPM_SUPPLY("HP Power", RT274_SET_PIN_HPO, + RT274_SET_PIN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LOUT Power", RT274_SET_PIN_LOUT3, + RT274_SET_PIN_SFT, 0, NULL, 0), + + /* Output Mixer */ + SND_SOC_DAPM_PGA("DAC OUT0", SND_SOC_NOPM, 0, 0, + NULL, 0), + SND_SOC_DAPM_PGA("DAC OUT1", SND_SOC_NOPM, 0, 0, + NULL, 0), + + /* Output Pga */ + SND_SOC_DAPM_SWITCH("LOUT L", SND_SOC_NOPM, 0, 0, + &loutl_enable_control), + SND_SOC_DAPM_SWITCH("LOUT R", SND_SOC_NOPM, 0, 0, + &loutr_enable_control), + SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0, + &hpol_enable_control), + SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0, + &hpor_enable_control), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPO Pin"), + SND_SOC_DAPM_OUTPUT("SPDIF"), + SND_SOC_DAPM_OUTPUT("LINE3"), +}; + +static const struct snd_soc_dapm_route rt274_dapm_routes[] = { + {"DMIC1", NULL, "DMIC1 Pin"}, + {"DMIC2", NULL, "DMIC2 Pin"}, + + {"ADC 0 Mux", "Mic", "MIC"}, + {"ADC 0 Mux", "Dmic", "DMIC1"}, + {"ADC 0 Mux", "Line1", "LINE1"}, + {"ADC 0 Mux", "Line2", "LINE2"}, + {"ADC 1 Mux", "Mic", "MIC"}, + {"ADC 1 Mux", "Dmic", "DMIC2"}, + {"ADC 1 Mux", "Line1", "LINE1"}, + {"ADC 1 Mux", "Line2", "LINE2"}, + + {"ADC 0", NULL, "ADC 0 Mux"}, + {"ADC 1", NULL, "ADC 1 Mux"}, + + {"AIF1TXL", NULL, "ADC 0"}, + {"AIF1TXR", NULL, "ADC 0"}, + {"AIF2TXL", NULL, "ADC 1"}, + {"AIF2TXR", NULL, "ADC 1"}, + + {"DAC 0", NULL, "AIF1RXL"}, + {"DAC 0", NULL, "AIF1RXR"}, + {"DAC 1", NULL, "AIF2RXL"}, + {"DAC 1", NULL, "AIF2RXR"}, + + {"DAC OUT0", NULL, "DAC 0"}, + + {"DAC OUT1", NULL, "DAC 1"}, + + {"LOUT Mux", "DAC OUT0", "DAC OUT0"}, + {"LOUT Mux", "DAC OUT1", "DAC OUT1"}, + + {"LOUT L", "Switch", "LOUT Mux"}, + {"LOUT R", "Switch", "LOUT Mux"}, + {"LOUT L", NULL, "LOUT Power"}, + {"LOUT R", NULL, "LOUT Power"}, + + {"LINE3", NULL, "LOUT L"}, + {"LINE3", NULL, "LOUT R"}, + + {"HPO Mux", "DAC OUT0", "DAC OUT0"}, + {"HPO Mux", "DAC OUT1", "DAC OUT1"}, + + {"HPO L", "Switch", "HPO Mux"}, + {"HPO R", "Switch", "HPO Mux"}, + {"HPO L", NULL, "HP Power"}, + {"HPO R", NULL, "HP Power"}, + + {"HPO Pin", NULL, "HPO L"}, + {"HPO Pin", NULL, "HPO R"}, +}; + +static int rt274_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + unsigned int val = 0; + int d_len_code = 0, c_len_code = 0; + + switch (params_rate(params)) { + /* bit 14 0:48K 1:44.1K */ + case 44100: + case 48000: + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + switch (rt274->sys_clk) { + case 12288000: + case 24576000: + if (params_rate(params) != 48000) { + dev_err(component->dev, "Sys_clk is not matched (%d %d)\n", + params_rate(params), rt274->sys_clk); + return -EINVAL; + } + break; + case 11289600: + case 22579200: + if (params_rate(params) != 44100) { + dev_err(component->dev, "Sys_clk is not matched (%d %d)\n", + params_rate(params), rt274->sys_clk); + return -EINVAL; + } + break; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 16: + d_len_code = 0; + c_len_code = 0; + val |= (0x1 << 4); + break; + case 32: + d_len_code = 2; + c_len_code = 3; + val |= (0x4 << 4); + break; + case 20: + d_len_code = 1; + c_len_code = 1; + val |= (0x2 << 4); + break; + case 24: + d_len_code = 2; + c_len_code = 2; + val |= (0x3 << 4); + break; + case 8: + d_len_code = 3; + c_len_code = 0; + break; + default: + return -EINVAL; + } + + if (rt274->master) + c_len_code = 0x3; + + snd_soc_component_update_bits(component, + RT274_I2S_CTRL1, 0xc018, d_len_code << 3 | c_len_code << 14); + dev_dbg(component->dev, "format val = 0x%x\n", val); + + snd_soc_component_update_bits(component, RT274_DAC_FORMAT, 0x407f, val); + snd_soc_component_update_bits(component, RT274_ADC_FORMAT, 0x407f, val); + + return 0; +} + +static int rt274_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + snd_soc_component_update_bits(component, + RT274_I2S_CTRL1, RT274_I2S_MODE_MASK, RT274_I2S_MODE_M); + rt274->master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + snd_soc_component_update_bits(component, + RT274_I2S_CTRL1, RT274_I2S_MODE_MASK, RT274_I2S_MODE_S); + rt274->master = false; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + snd_soc_component_update_bits(component, RT274_I2S_CTRL1, + RT274_I2S_FMT_MASK, RT274_I2S_FMT_I2S); + break; + case SND_SOC_DAIFMT_LEFT_J: + snd_soc_component_update_bits(component, RT274_I2S_CTRL1, + RT274_I2S_FMT_MASK, RT274_I2S_FMT_LJ); + break; + case SND_SOC_DAIFMT_DSP_A: + snd_soc_component_update_bits(component, RT274_I2S_CTRL1, + RT274_I2S_FMT_MASK, RT274_I2S_FMT_PCMA); + break; + case SND_SOC_DAIFMT_DSP_B: + snd_soc_component_update_bits(component, RT274_I2S_CTRL1, + RT274_I2S_FMT_MASK, RT274_I2S_FMT_PCMB); + break; + default: + return -EINVAL; + } + /* bit 15 Stream Type 0:PCM 1:Non-PCM */ + snd_soc_component_update_bits(component, RT274_DAC_FORMAT, 0x8000, 0); + snd_soc_component_update_bits(component, RT274_ADC_FORMAT, 0x8000, 0); + + return 0; +} + +static int rt274_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + + switch (source) { + case RT274_PLL2_S_MCLK: + snd_soc_component_update_bits(component, RT274_PLL2_CTRL, + RT274_PLL2_SRC_MASK, RT274_PLL2_SRC_MCLK); + break; + default: + dev_warn(component->dev, "invalid pll source, use BCLK\n"); + fallthrough; + case RT274_PLL2_S_BCLK: + snd_soc_component_update_bits(component, RT274_PLL2_CTRL, + RT274_PLL2_SRC_MASK, RT274_PLL2_SRC_BCLK); + break; + } + + if (source == RT274_PLL2_S_BCLK) { + snd_soc_component_update_bits(component, RT274_MCLK_CTRL, + (0x3 << 12), (0x3 << 12)); + switch (rt274->fs) { + case 50: + snd_soc_component_write(component, 0x7a, 0xaab6); + snd_soc_component_write(component, 0x7b, 0x0301); + snd_soc_component_write(component, 0x7c, 0x04fe); + break; + case 64: + snd_soc_component_write(component, 0x7a, 0xaa96); + snd_soc_component_write(component, 0x7b, 0x8003); + snd_soc_component_write(component, 0x7c, 0x081e); + break; + case 128: + snd_soc_component_write(component, 0x7a, 0xaa96); + snd_soc_component_write(component, 0x7b, 0x8003); + snd_soc_component_write(component, 0x7c, 0x080e); + break; + default: + dev_warn(component->dev, "invalid freq_in, assume 4.8M\n"); + fallthrough; + case 100: + snd_soc_component_write(component, 0x7a, 0xaab6); + snd_soc_component_write(component, 0x7b, 0x0301); + snd_soc_component_write(component, 0x7c, 0x047e); + break; + } + } + + return 0; +} + +static int rt274_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + unsigned int clk_src, mclk_en; + + dev_dbg(component->dev, "%s freq=%d\n", __func__, freq); + + switch (clk_id) { + case RT274_SCLK_S_MCLK: + mclk_en = RT274_MCLK_MODE_EN; + clk_src = RT274_CLK_SRC_MCLK; + break; + case RT274_SCLK_S_PLL1: + mclk_en = RT274_MCLK_MODE_DIS; + clk_src = RT274_CLK_SRC_MCLK; + break; + case RT274_SCLK_S_PLL2: + mclk_en = RT274_MCLK_MODE_EN; + clk_src = RT274_CLK_SRC_PLL2; + break; + default: + mclk_en = RT274_MCLK_MODE_DIS; + clk_src = RT274_CLK_SRC_MCLK; + dev_warn(component->dev, "invalid sysclk source, use PLL1\n"); + break; + } + snd_soc_component_update_bits(component, RT274_MCLK_CTRL, + RT274_MCLK_MODE_MASK, mclk_en); + snd_soc_component_update_bits(component, RT274_CLK_CTRL, + RT274_CLK_SRC_MASK, clk_src); + + switch (freq) { + case 19200000: + if (clk_id == RT274_SCLK_S_MCLK) { + dev_err(component->dev, "Should not use MCLK\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, + RT274_I2S_CTRL2, 0x40, 0x40); + break; + case 24000000: + if (clk_id == RT274_SCLK_S_MCLK) { + dev_err(component->dev, "Should not use MCLK\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, + RT274_I2S_CTRL2, 0x40, 0x0); + break; + case 12288000: + case 11289600: + snd_soc_component_update_bits(component, + RT274_MCLK_CTRL, 0x1fcf, 0x0008); + break; + case 24576000: + case 22579200: + snd_soc_component_update_bits(component, + RT274_MCLK_CTRL, 0x1fcf, 0x1543); + break; + default: + dev_err(component->dev, "Unsupported system clock\n"); + return -EINVAL; + } + + rt274->sys_clk = freq; + rt274->clk_id = clk_id; + + return 0; +} + +static int rt274_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio); + rt274->fs = ratio; + if ((ratio / 50) == 0) + snd_soc_component_update_bits(component, + RT274_I2S_CTRL1, 0x1000, 0x1000); + else + snd_soc_component_update_bits(component, + RT274_I2S_CTRL1, 0x1000, 0x0); + + + return 0; +} + +static int rt274_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) + +{ + struct snd_soc_component *component = dai->component; + + if (rx_mask || tx_mask) { + snd_soc_component_update_bits(component, + RT274_I2S_CTRL1, RT274_TDM_EN, RT274_TDM_EN); + } else { + snd_soc_component_update_bits(component, + RT274_I2S_CTRL1, RT274_TDM_EN, RT274_TDM_DIS); + return 0; + } + + switch (slots) { + case 4: + snd_soc_component_update_bits(component, + RT274_I2S_CTRL1, RT274_TDM_CH_NUM, RT274_TDM_4CH); + break; + case 2: + snd_soc_component_update_bits(component, + RT274_I2S_CTRL1, RT274_TDM_CH_NUM, RT274_TDM_2CH); + break; + default: + dev_err(component->dev, + "Support 2 or 4 slots TDM only\n"); + return -EINVAL; + } + + return 0; +} + +static int rt274_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == + snd_soc_component_get_bias_level(component)) { + snd_soc_component_write(component, + RT274_SET_AUDIO_POWER, AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_component_write(component, + RT274_SET_AUDIO_POWER, AC_PWRST_D3); + break; + + default: + break; + } + + return 0; +} + +static irqreturn_t rt274_irq(int irq, void *data) +{ + struct rt274_priv *rt274 = data; + bool hp = false; + bool mic = false; + int ret, status = 0; + + /* Clear IRQ */ + regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL, + RT274_IRQ_CLR, RT274_IRQ_CLR); + + ret = rt274_jack_detect(rt274, &hp, &mic); + + if (ret == 0) { + if (hp) + status |= SND_JACK_HEADPHONE; + + if (mic) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt274->jack, status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + + pm_wakeup_event(&rt274->i2c->dev, 300); + } + + return IRQ_HANDLED; +} + +static int rt274_probe(struct snd_soc_component *component) +{ + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + + rt274->component = component; + INIT_DELAYED_WORK(&rt274->jack_detect_work, rt274_jack_detect_work); + + if (rt274->i2c->irq) + schedule_delayed_work(&rt274->jack_detect_work, + msecs_to_jiffies(1250)); + return 0; +} + +static void rt274_remove(struct snd_soc_component *component) +{ + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&rt274->jack_detect_work); + rt274->component = NULL; +} + +#ifdef CONFIG_PM +static int rt274_suspend(struct snd_soc_component *component) +{ + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt274->regmap, true); + regcache_mark_dirty(rt274->regmap); + + return 0; +} + +static int rt274_resume(struct snd_soc_component *component) +{ + struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt274->regmap, false); + rt274_index_sync(component); + regcache_sync(rt274->regmap); + + return 0; +} +#else +#define rt274_suspend NULL +#define rt274_resume NULL +#endif + +#define RT274_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT274_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt274_aif_dai_ops = { + .hw_params = rt274_hw_params, + .set_fmt = rt274_set_dai_fmt, + .set_sysclk = rt274_set_dai_sysclk, + .set_pll = rt274_set_dai_pll, + .set_bclk_ratio = rt274_set_bclk_ratio, + .set_tdm_slot = rt274_set_tdm_slot, +}; + +static struct snd_soc_dai_driver rt274_dai[] = { + { + .name = "rt274-aif1", + .id = RT274_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT274_STEREO_RATES, + .formats = RT274_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT274_STEREO_RATES, + .formats = RT274_FORMATS, + }, + .ops = &rt274_aif_dai_ops, + .symmetric_rate = 1, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt274 = { + .probe = rt274_probe, + .remove = rt274_remove, + .suspend = rt274_suspend, + .resume = rt274_resume, + .set_bias_level = rt274_set_bias_level, + .set_jack = rt274_mic_detect, + .controls = rt274_snd_controls, + .num_controls = ARRAY_SIZE(rt274_snd_controls), + .dapm_widgets = rt274_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt274_dapm_widgets), + .dapm_routes = rt274_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt274_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt274_regmap = { + .reg_bits = 32, + .val_bits = 32, + .max_register = 0x05bfffff, + .volatile_reg = rt274_volatile_register, + .readable_reg = rt274_readable_register, + .reg_write = rl6347a_hw_write, + .reg_read = rl6347a_hw_read, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt274_reg, + .num_reg_defaults = ARRAY_SIZE(rt274_reg), +}; + +#ifdef CONFIG_OF +static const struct of_device_id rt274_of_match[] = { + {.compatible = "realtek,rt274"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rt274_of_match); +#endif + +static const struct i2c_device_id rt274_i2c_id[] = { + {"rt274", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt274_i2c_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt274_acpi_match[] = { + { "10EC0274", 0 }, + { "INT34C2", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt274_acpi_match); +#endif + +static int rt274_i2c_probe(struct i2c_client *i2c) +{ + struct rt274_priv *rt274; + + int ret; + unsigned int val; + + rt274 = devm_kzalloc(&i2c->dev, sizeof(*rt274), + GFP_KERNEL); + if (rt274 == NULL) + return -ENOMEM; + + rt274->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt274_regmap); + if (IS_ERR(rt274->regmap)) { + ret = PTR_ERR(rt274->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = regmap_read(rt274->regmap, + RT274_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val); + if (ret) + return ret; + + if (val != RT274_VENDOR_ID) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt274\n", val); + return -ENODEV; + } + + rt274->index_cache = devm_kmemdup(&i2c->dev, rt274_index_def, + sizeof(rt274_index_def), GFP_KERNEL); + if (!rt274->index_cache) + return -ENOMEM; + + rt274->index_cache_size = INDEX_CACHE_SIZE; + rt274->i2c = i2c; + i2c_set_clientdata(i2c, rt274); + + /* reset codec */ + regmap_write(rt274->regmap, RT274_RESET, 0); + regmap_update_bits(rt274->regmap, 0x1a, 0x4000, 0x4000); + + /* Set Pad PDB is floating */ + regmap_update_bits(rt274->regmap, RT274_PAD_CTRL12, 0x3, 0x0); + regmap_write(rt274->regmap, RT274_COEF5b_INDEX, 0x01); + regmap_write(rt274->regmap, RT274_COEF5b_COEF, 0x8540); + regmap_update_bits(rt274->regmap, 0x6f, 0x0100, 0x0100); + /* Combo jack auto detect */ + regmap_write(rt274->regmap, 0x4a, 0x201b); + /* Aux mode off */ + regmap_update_bits(rt274->regmap, 0x6f, 0x3000, 0x2000); + /* HP DC Calibration */ + regmap_update_bits(rt274->regmap, 0x6f, 0xf, 0x0); + /* Set NID=58h.Index 00h [15]= 1b; */ + regmap_write(rt274->regmap, RT274_COEF58_INDEX, 0x00); + regmap_write(rt274->regmap, RT274_COEF58_COEF, 0xb888); + msleep(500); + regmap_update_bits(rt274->regmap, 0x6f, 0xf, 0xb); + regmap_write(rt274->regmap, RT274_COEF58_INDEX, 0x00); + regmap_write(rt274->regmap, RT274_COEF58_COEF, 0x3888); + /* Set pin widget */ + regmap_write(rt274->regmap, RT274_SET_PIN_HPO, 0x40); + regmap_write(rt274->regmap, RT274_SET_PIN_LOUT3, 0x40); + regmap_write(rt274->regmap, RT274_SET_MIC, 0x20); + regmap_write(rt274->regmap, RT274_SET_PIN_DMIC1, 0x20); + + regmap_update_bits(rt274->regmap, RT274_I2S_CTRL2, 0xc004, 0x4004); + regmap_update_bits(rt274->regmap, RT274_EAPD_GPIO_IRQ_CTRL, + RT274_GPI2_SEL_MASK, RT274_GPI2_SEL_DMIC_CLK); + + /* jack detection */ + regmap_write(rt274->regmap, RT274_UNSOLICITED_HP_OUT, 0x81); + regmap_write(rt274->regmap, RT274_UNSOLICITED_MIC, 0x82); + + if (rt274->i2c->irq) { + ret = request_threaded_irq(rt274->i2c->irq, NULL, rt274_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt274", rt274); + if (ret != 0) { + dev_err(&i2c->dev, + "Failed to reguest IRQ: %d\n", ret); + return ret; + } + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt274, + rt274_dai, ARRAY_SIZE(rt274_dai)); + + return ret; +} + +static void rt274_i2c_remove(struct i2c_client *i2c) +{ + struct rt274_priv *rt274 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, rt274); +} + + +static struct i2c_driver rt274_i2c_driver = { + .driver = { + .name = "rt274", + .acpi_match_table = ACPI_PTR(rt274_acpi_match), +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(rt274_of_match), +#endif + }, + .probe = rt274_i2c_probe, + .remove = rt274_i2c_remove, + .id_table = rt274_i2c_id, +}; + +module_i2c_driver(rt274_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT274 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt274.h b/sound/soc/codecs/rt274.h new file mode 100644 index 0000000000..0fcf942fa1 --- /dev/null +++ b/sound/soc/codecs/rt274.h @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt274.h -- RT274 ALSA SoC audio driver + * + * Copyright 2016 Realtek Microelectronics + * Author: Bard Liao + */ + +#ifndef __RT274_H__ +#define __RT274_H__ + +#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) + +#define RT274_AUDIO_FUNCTION_GROUP 0x01 +#define RT274_DAC_OUT0 0x02 +#define RT274_DAC_OUT1 0x03 +#define RT274_ADC_IN2 0x08 +#define RT274_ADC_IN1 0x09 +#define RT274_DIG_CVT 0x0a +#define RT274_DMIC1 0x12 +#define RT274_DMIC2 0x13 +#define RT274_MIC 0x19 +#define RT274_LINE1 0x1a +#define RT274_LINE2 0x1b +#define RT274_LINE3 0x16 +#define RT274_SPDIF 0x1e +#define RT274_VENDOR_REGISTERS 0x20 +#define RT274_HP_OUT 0x21 +#define RT274_MIXER_IN1 0x22 +#define RT274_MIXER_IN2 0x23 +#define RT274_INLINE_CMD 0x55 + +#define RT274_SET_PIN_SFT 6 +#define RT274_SET_PIN_ENABLE 0x40 +#define RT274_SET_PIN_DISABLE 0 +#define RT274_SET_EAPD_HIGH 0x2 +#define RT274_SET_EAPD_LOW 0 + +#define RT274_MUTE_SFT 7 + +/* Verb commands */ +#define RT274_RESET\ + VERB_CMD(AC_VERB_SET_CODEC_RESET, RT274_AUDIO_FUNCTION_GROUP, 0) +#define RT274_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM) +#define RT274_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0) +#define RT274_SET_AUDIO_POWER RT274_SET_POWER(RT274_AUDIO_FUNCTION_GROUP) +#define RT274_SET_HPO_POWER RT274_SET_POWER(RT274_HP_OUT) +#define RT274_SET_DMIC1_POWER RT274_SET_POWER(RT274_DMIC1) +#define RT274_LOUT_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_LINE3, 0) +#define RT274_HPO_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_HP_OUT, 0) +#define RT274_ADC0_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_MIXER_IN1, 0) +#define RT274_ADC1_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT274_MIXER_IN2, 0) +#define RT274_SET_MIC\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_MIC, 0) +#define RT274_SET_PIN_LOUT3\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_LINE3, 0) +#define RT274_SET_PIN_HPO\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_HP_OUT, 0) +#define RT274_SET_PIN_DMIC1\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_DMIC1, 0) +#define RT274_SET_PIN_SPDIF\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT274_SPDIF, 0) +#define RT274_SET_PIN_DIG_CVT\ + VERB_CMD(AC_VERB_SET_DIGI_CONVERT_1, RT274_DIG_CVT, 0) +#define RT274_SET_AMP_GAIN_HPO\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_HP_OUT, 0) +#define RT274_SET_AMP_GAIN_ADC_IN1\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0) +#define RT274_SET_AMP_GAIN_ADC_IN2\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN2, 0) +#define RT274_GET_HP_SENSE\ + VERB_CMD(AC_VERB_GET_PIN_SENSE, RT274_HP_OUT, 0) +#define RT274_GET_MIC_SENSE\ + VERB_CMD(AC_VERB_GET_PIN_SENSE, RT274_MIC, 0) +#define RT274_SET_DMIC2_DEFAULT\ + VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT274_DMIC2, 0) +#define RT274_SET_SPDIF_DEFAULT\ + VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT274_SPDIF, 0) +#define RT274_DAC0L_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0xa000) +#define RT274_DAC0R_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT0, 0x9000) +#define RT274_DAC1L_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0xa000) +#define RT274_DAC1R_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_DAC_OUT1, 0x9000) +#define RT274_ADCL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0x6000) +#define RT274_ADCR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_ADC_IN1, 0x5000) +#define RT274_MIC_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_MIC, 0x7000) +#define RT274_LOUTL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_LINE3, 0xa000) +#define RT274_LOUTR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_LINE3, 0x9000) +#define RT274_HPOL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_HP_OUT, 0xa000) +#define RT274_HPOR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT274_HP_OUT, 0x9000) +#define RT274_DAC_FORMAT\ + VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT274_DAC_OUT0, 0) +#define RT274_ADC_FORMAT\ + VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT274_ADC_IN1, 0) +#define RT274_COEF_INDEX\ + VERB_CMD(AC_VERB_SET_COEF_INDEX, RT274_VENDOR_REGISTERS, 0) +#define RT274_PROC_COEF\ + VERB_CMD(AC_VERB_SET_PROC_COEF, RT274_VENDOR_REGISTERS, 0) +#define RT274_UNSOLICITED_INLINE_CMD\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT274_INLINE_CMD, 0) +#define RT274_UNSOLICITED_HP_OUT\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT274_HP_OUT, 0) +#define RT274_UNSOLICITED_MIC\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT274_MIC, 0) +#define RT274_COEF58_INDEX\ + VERB_CMD(AC_VERB_SET_COEF_INDEX, 0x58, 0) +#define RT274_COEF58_COEF\ + VERB_CMD(AC_VERB_SET_PROC_COEF, 0x58, 0) +#define RT274_COEF5b_INDEX\ + VERB_CMD(AC_VERB_SET_COEF_INDEX, 0x5b, 0) +#define RT274_COEF5b_COEF\ + VERB_CMD(AC_VERB_SET_PROC_COEF, 0x5b, 0) +#define RT274_SET_STREAMID_DAC0\ + VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_DAC_OUT0, 0) +#define RT274_SET_STREAMID_DAC1\ + VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_DAC_OUT1, 0) +#define RT274_SET_STREAMID_ADC1\ + VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_ADC_IN1, 0) +#define RT274_SET_STREAMID_ADC2\ + VERB_CMD(AC_VERB_SET_CHANNEL_STREAMID, RT274_ADC_IN2, 0) + +/* Index registers */ +#define RT274_EAPD_GPIO_IRQ_CTRL 0x10 +#define RT274_PAD_CTRL12 0x35 +#define RT274_I2S_CTRL1 0x63 +#define RT274_I2S_CTRL2 0x64 +#define RT274_MCLK_CTRL 0x71 +#define RT274_CLK_CTRL 0x72 +#define RT274_PLL2_CTRL 0x7b + + +/* EAPD GPIO IRQ control (Index 0x10) */ +#define RT274_IRQ_DIS (0x0 << 13) +#define RT274_IRQ_EN (0x1 << 13) +#define RT274_IRQ_CLR (0x1 << 12) +#define RT274_GPI2_SEL_MASK (0x3 << 7) +#define RT274_GPI2_SEL_GPIO2 (0x0 << 7) +#define RT274_GPI2_SEL_I2S (0x1 << 7) +#define RT274_GPI2_SEL_DMIC_CLK (0x2 << 7) +#define RT274_GPI2_SEL_CBJ (0x3 << 7) + +/* Front I2S_Interface control 1 (Index 0x63) */ +#define RT274_I2S_MODE_MASK (0x1 << 11) +#define RT274_I2S_MODE_S (0x0 << 11) +#define RT274_I2S_MODE_M (0x1 << 11) +#define RT274_TDM_DIS (0x0 << 10) +#define RT274_TDM_EN (0x1 << 10) +#define RT274_TDM_CH_NUM (0x1 << 7) +#define RT274_TDM_2CH (0x0 << 7) +#define RT274_TDM_4CH (0x1 << 7) +#define RT274_I2S_FMT_MASK (0x3 << 8) +#define RT274_I2S_FMT_I2S (0x0 << 8) +#define RT274_I2S_FMT_LJ (0x1 << 8) +#define RT274_I2S_FMT_PCMA (0x2 << 8) +#define RT274_I2S_FMT_PCMB (0x3 << 8) + +/* MCLK clock domain control (Index 0x71) */ +#define RT274_MCLK_MODE_MASK (0x1 << 14) +#define RT274_MCLK_MODE_DIS (0x0 << 14) +#define RT274_MCLK_MODE_EN (0x1 << 14) + +/* Clock control (Index 0x72) */ +#define RT274_CLK_SRC_MASK (0x7 << 3) +#define RT274_CLK_SRC_MCLK (0x0 << 3) +#define RT274_CLK_SRC_PLL2 (0x3 << 3) + +/* PLL2 control (Index 0x7b) */ +#define RT274_PLL2_SRC_MASK (0x1 << 13) +#define RT274_PLL2_SRC_MCLK (0x0 << 13) +#define RT274_PLL2_SRC_BCLK (0x1 << 13) + +/* HP-OUT (0x21) */ +#define RT274_M_HP_MUX_SFT 14 +#define RT274_HP_SEL_MASK 0x1 +#define RT274_HP_SEL_SFT 0 +#define RT274_HP_SEL_F 0 +#define RT274_HP_SEL_S 1 + +/* ADC (0x22) (0x23) */ +#define RT274_ADC_SEL_MASK 0x7 +#define RT274_ADC_SEL_SFT 0 +#define RT274_ADC_SEL_MIC 0 +#define RT274_ADC_SEL_LINE1 1 +#define RT274_ADC_SEL_LINE2 2 +#define RT274_ADC_SEL_DMIC 3 + +#define RT274_SCLK_S_MCLK 0 +#define RT274_SCLK_S_PLL1 1 +#define RT274_SCLK_S_PLL2 2 + +#define RT274_PLL2_S_MCLK 0 +#define RT274_PLL2_S_BCLK 1 + +enum { + RT274_AIF1, + RT274_AIFS, +}; + +#endif /* __RT274_H__ */ + diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c new file mode 100644 index 0000000000..981155b046 --- /dev/null +++ b/sound/soc/codecs/rt286.c @@ -0,0 +1,1275 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt286.c -- RT286 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Bard Liao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6347a.h" +#include "rt286.h" + +#define RT286_VENDOR_ID 0x10ec0286 +#define RT288_VENDOR_ID 0x10ec0288 + +struct rt286_priv { + struct reg_default *index_cache; + int index_cache_size; + struct regmap *regmap; + struct snd_soc_component *component; + struct rt286_platform_data pdata; + struct i2c_client *i2c; + struct snd_soc_jack *jack; + struct delayed_work jack_detect_work; + int sys_clk; + int clk_id; +}; + +static const struct reg_default rt286_index_def[] = { + { 0x01, 0xaaaa }, + { 0x02, 0x8aaa }, + { 0x03, 0x0002 }, + { 0x04, 0xaf01 }, + { 0x08, 0x000d }, + { 0x09, 0xd810 }, + { 0x0a, 0x0120 }, + { 0x0b, 0x0000 }, + { 0x0d, 0x2800 }, + { 0x0f, 0x0000 }, + { 0x19, 0x0a17 }, + { 0x20, 0x0020 }, + { 0x33, 0x0208 }, + { 0x49, 0x0004 }, + { 0x4f, 0x50e9 }, + { 0x50, 0x2000 }, + { 0x63, 0x2902 }, + { 0x67, 0x1111 }, + { 0x68, 0x1016 }, + { 0x69, 0x273f }, +}; +#define INDEX_CACHE_SIZE ARRAY_SIZE(rt286_index_def) + +static const struct reg_default rt286_reg[] = { + { 0x00170500, 0x00000400 }, + { 0x00220000, 0x00000031 }, + { 0x00239000, 0x0000007f }, + { 0x0023a000, 0x0000007f }, + { 0x00270500, 0x00000400 }, + { 0x00370500, 0x00000400 }, + { 0x00870500, 0x00000400 }, + { 0x00920000, 0x00000031 }, + { 0x00935000, 0x000000c3 }, + { 0x00936000, 0x000000c3 }, + { 0x00970500, 0x00000400 }, + { 0x00b37000, 0x00000097 }, + { 0x00b37200, 0x00000097 }, + { 0x00b37300, 0x00000097 }, + { 0x00c37000, 0x00000000 }, + { 0x00c37100, 0x00000080 }, + { 0x01270500, 0x00000400 }, + { 0x01370500, 0x00000400 }, + { 0x01371f00, 0x411111f0 }, + { 0x01439000, 0x00000080 }, + { 0x0143a000, 0x00000080 }, + { 0x01470700, 0x00000000 }, + { 0x01470500, 0x00000400 }, + { 0x01470c00, 0x00000000 }, + { 0x01470100, 0x00000000 }, + { 0x01837000, 0x00000000 }, + { 0x01870500, 0x00000400 }, + { 0x02050000, 0x00000000 }, + { 0x02139000, 0x00000080 }, + { 0x0213a000, 0x00000080 }, + { 0x02170100, 0x00000000 }, + { 0x02170500, 0x00000400 }, + { 0x02170700, 0x00000000 }, + { 0x02270100, 0x00000000 }, + { 0x02370100, 0x00000000 }, + { 0x01870700, 0x00000020 }, + { 0x00830000, 0x000000c3 }, + { 0x00930000, 0x000000c3 }, + { 0x01270700, 0x00000000 }, +}; + +static bool rt286_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... 0xff: + case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID): + case RT286_GET_HP_SENSE: + case RT286_GET_MIC1_SENSE: + case RT286_PROC_COEF: + return true; + default: + return false; + } + + +} + +static bool rt286_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... 0xff: + case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID): + case RT286_GET_HP_SENSE: + case RT286_GET_MIC1_SENSE: + case RT286_SET_AUDIO_POWER: + case RT286_SET_HPO_POWER: + case RT286_SET_SPK_POWER: + case RT286_SET_DMIC1_POWER: + case RT286_SPK_MUX: + case RT286_HPO_MUX: + case RT286_ADC0_MUX: + case RT286_ADC1_MUX: + case RT286_SET_MIC1: + case RT286_SET_PIN_HPO: + case RT286_SET_PIN_SPK: + case RT286_SET_PIN_DMIC1: + case RT286_SPK_EAPD: + case RT286_SET_AMP_GAIN_HPO: + case RT286_SET_DMIC2_DEFAULT: + case RT286_DACL_GAIN: + case RT286_DACR_GAIN: + case RT286_ADCL_GAIN: + case RT286_ADCR_GAIN: + case RT286_MIC_GAIN: + case RT286_SPOL_GAIN: + case RT286_SPOR_GAIN: + case RT286_HPOL_GAIN: + case RT286_HPOR_GAIN: + case RT286_F_DAC_SWITCH: + case RT286_F_RECMIX_SWITCH: + case RT286_REC_MIC_SWITCH: + case RT286_REC_I2S_SWITCH: + case RT286_REC_LINE_SWITCH: + case RT286_REC_BEEP_SWITCH: + case RT286_DAC_FORMAT: + case RT286_ADC_FORMAT: + case RT286_COEF_INDEX: + case RT286_PROC_COEF: + case RT286_SET_AMP_GAIN_ADC_IN1: + case RT286_SET_AMP_GAIN_ADC_IN2: + case RT286_SET_GPIO_MASK: + case RT286_SET_GPIO_DIRECTION: + case RT286_SET_GPIO_DATA: + case RT286_SET_POWER(RT286_DAC_OUT1): + case RT286_SET_POWER(RT286_DAC_OUT2): + case RT286_SET_POWER(RT286_ADC_IN1): + case RT286_SET_POWER(RT286_ADC_IN2): + case RT286_SET_POWER(RT286_DMIC2): + case RT286_SET_POWER(RT286_MIC1): + return true; + default: + return false; + } +} + +#ifdef CONFIG_PM +static void rt286_index_sync(struct snd_soc_component *component) +{ + struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component); + int i; + + for (i = 0; i < INDEX_CACHE_SIZE; i++) { + snd_soc_component_write(component, rt286->index_cache[i].reg, + rt286->index_cache[i].def); + } +} +#endif + +static int rt286_support_power_controls[] = { + RT286_DAC_OUT1, + RT286_DAC_OUT2, + RT286_ADC_IN1, + RT286_ADC_IN2, + RT286_MIC1, + RT286_DMIC1, + RT286_DMIC2, + RT286_SPK_OUT, + RT286_HP_OUT, +}; +#define RT286_POWER_REG_LEN ARRAY_SIZE(rt286_support_power_controls) + +static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic) +{ + struct snd_soc_dapm_context *dapm; + unsigned int val, buf; + + *hp = false; + *mic = false; + + if (!rt286->component) + return -EINVAL; + + dapm = snd_soc_component_get_dapm(rt286->component); + + if (rt286->pdata.cbj_en) { + regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf); + *hp = buf & 0x80000000; + if (*hp) { + /* power on HV,VERF */ + regmap_update_bits(rt286->regmap, + RT286_DC_GAIN, 0x200, 0x200); + + snd_soc_dapm_force_enable_pin(dapm, "HV"); + snd_soc_dapm_force_enable_pin(dapm, "VREF"); + /* power LDO1 */ + snd_soc_dapm_force_enable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); + + regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24); + msleep(50); + + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0xfcc0, 0xd400); + msleep(300); + regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val); + + if (0x0070 == (val & 0x0070)) { + *mic = true; + } else { + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0xfcc0, 0xe400); + msleep(300); + regmap_read(rt286->regmap, + RT286_CBJ_CTRL2, &val); + if (0x0070 == (val & 0x0070)) { + *mic = true; + } else { + *mic = false; + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, + 0xfcc0, 0xc400); + } + } + + regmap_update_bits(rt286->regmap, + RT286_DC_GAIN, 0x200, 0x0); + + } else { + *mic = false; + regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20); + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0x0400, 0x0000); + } + } else { + regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf); + *hp = buf & 0x80000000; + regmap_read(rt286->regmap, RT286_GET_MIC1_SENSE, &buf); + *mic = buf & 0x80000000; + } + + if (!*hp) { + snd_soc_dapm_disable_pin(dapm, "HV"); + snd_soc_dapm_disable_pin(dapm, "VREF"); + snd_soc_dapm_disable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); + } + + return 0; +} + +static void rt286_jack_detect_work(struct work_struct *work) +{ + struct rt286_priv *rt286 = + container_of(work, struct rt286_priv, jack_detect_work.work); + int status = 0; + bool hp = false; + bool mic = false; + + rt286_jack_detect(rt286, &hp, &mic); + + if (hp) + status |= SND_JACK_HEADPHONE; + + if (mic) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt286->jack, status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); +} + +static int rt286_mic_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component); + + rt286->jack = jack; + + if (jack) { + /* enable IRQ */ + if (rt286->jack->status & SND_JACK_HEADPHONE) + snd_soc_dapm_force_enable_pin(dapm, "LDO1"); + regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2); + /* Send an initial empty report */ + snd_soc_jack_report(rt286->jack, rt286->jack->status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + } else { + /* disable IRQ */ + regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x0); + snd_soc_dapm_disable_pin(dapm, "LDO1"); + } + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int is_mclk_mode(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component); + + if (rt286->clk_id == RT286_SCLK_S_MCLK) + return 1; + else + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt286_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT286_DACL_GAIN, + RT286_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_DOUBLE_R("ADC0 Capture Switch", RT286_ADCL_GAIN, + RT286_ADCR_GAIN, 7, 1, 1), + SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT286_ADCL_GAIN, + RT286_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_SINGLE_TLV("AMIC Volume", RT286_MIC_GAIN, + 0, 0x3, 0, mic_vol_tlv), + SOC_DOUBLE_R("Speaker Playback Switch", RT286_SPOL_GAIN, + RT286_SPOR_GAIN, RT286_MUTE_SFT, 1, 1), +}; + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt286_front_mix[] = { + SOC_DAPM_SINGLE("DAC Switch", RT286_F_DAC_SWITCH, + RT286_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("RECMIX Switch", RT286_F_RECMIX_SWITCH, + RT286_MUTE_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt286_rec_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", RT286_REC_MIC_SWITCH, + RT286_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("I2S Switch", RT286_REC_I2S_SWITCH, + RT286_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("Line1 Switch", RT286_REC_LINE_SWITCH, + RT286_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("Beep Switch", RT286_REC_BEEP_SWITCH, + RT286_MUTE_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new spo_enable_control = + SOC_DAPM_SINGLE("Switch", RT286_SET_PIN_SPK, + RT286_SET_PIN_SFT, 1, 0); + +static const struct snd_kcontrol_new hpol_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOL_GAIN, + RT286_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpor_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOR_GAIN, + RT286_MUTE_SFT, 1, 1); + +/* ADC0 source */ +static const char * const rt286_adc_src[] = { + "Mic", "RECMIX", "Dmic" +}; + +static const int rt286_adc_values[] = { + 0, 4, 5, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL( + rt286_adc0_enum, RT286_ADC0_MUX, RT286_ADC_SEL_SFT, + RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values); + +static const struct snd_kcontrol_new rt286_adc0_mux = + SOC_DAPM_ENUM("ADC 0 source", rt286_adc0_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL( + rt286_adc1_enum, RT286_ADC1_MUX, RT286_ADC_SEL_SFT, + RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values); + +static const struct snd_kcontrol_new rt286_adc1_mux = + SOC_DAPM_ENUM("ADC 1 source", rt286_adc1_enum); + +static const char * const rt286_dac_src[] = { + "Front", "Surround" +}; +/* HP-OUT source */ +static SOC_ENUM_SINGLE_DECL(rt286_hpo_enum, RT286_HPO_MUX, + 0, rt286_dac_src); + +static const struct snd_kcontrol_new rt286_hpo_mux = +SOC_DAPM_ENUM("HPO source", rt286_hpo_enum); + +/* SPK-OUT source */ +static SOC_ENUM_SINGLE_DECL(rt286_spo_enum, RT286_SPK_MUX, + 0, rt286_dac_src); + +static const struct snd_kcontrol_new rt286_spo_mux = +SOC_DAPM_ENUM("SPO source", rt286_spo_enum); + +static int rt286_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(component, + RT286_SPK_EAPD, RT286_SET_EAPD_HIGH); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write(component, + RT286_SPK_EAPD, RT286_SET_EAPD_LOW); + break; + + default: + return 0; + } + + return 0; +} + +static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(component, RT286_SET_PIN_DMIC1, 0x20); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write(component, RT286_SET_PIN_DMIC1, 0); + break; + default: + return 0; + } + + return 0; +} + +static int rt286_ldo2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT286_POWER_CTRL2, 0x38, 0x08); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT286_POWER_CTRL2, 0x38, 0x30); + break; + default: + return 0; + } + + return 0; +} + +static int rt286_mic1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, + RT286_A_BIAS_CTRL3, 0xc000, 0x8000); + snd_soc_component_update_bits(component, + RT286_A_BIAS_CTRL2, 0xc000, 0x8000); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + RT286_A_BIAS_CTRL3, 0xc000, 0x0000); + snd_soc_component_update_bits(component, + RT286_A_BIAS_CTRL2, 0xc000, 0x0000); + break; + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1, + 12, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1, + 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1, + 13, 1, rt286_ldo2_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("MCLK MODE", RT286_PLL_CTRL1, + 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM, + 0, 0, rt286_mic1_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1 Pin"), + SND_SOC_DAPM_INPUT("DMIC2 Pin"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("Beep"), + + /* DMIC */ + SND_SOC_DAPM_PGA_E("DMIC1", RT286_SET_POWER(RT286_DMIC1), 0, 1, + NULL, 0, rt286_set_dmic1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA("DMIC2", RT286_SET_POWER(RT286_DMIC2), 0, 1, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0, + rt286_rec_mix, ARRAY_SIZE(rt286_rec_mix)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("ADC 0 Mux", RT286_SET_POWER(RT286_ADC_IN1), 0, 1, + &rt286_adc0_mux), + SND_SOC_DAPM_MUX("ADC 1 Mux", RT286_SET_POWER(RT286_ADC_IN2), 0, 1, + &rt286_adc1_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0), + + /* Output Mux */ + SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt286_spo_mux), + SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt286_hpo_mux), + + SND_SOC_DAPM_SUPPLY("HP Power", RT286_SET_PIN_HPO, + RT286_SET_PIN_SFT, 0, NULL, 0), + + /* Output Mixer */ + SND_SOC_DAPM_MIXER("Front", RT286_SET_POWER(RT286_DAC_OUT1), 0, 1, + rt286_front_mix, ARRAY_SIZE(rt286_front_mix)), + SND_SOC_DAPM_PGA("Surround", RT286_SET_POWER(RT286_DAC_OUT2), 0, 1, + NULL, 0), + + /* Output Pga */ + SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0, + &spo_enable_control, rt286_spk_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0, + &hpol_enable_control), + SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0, + &hpor_enable_control), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + SND_SOC_DAPM_OUTPUT("HPO Pin"), + SND_SOC_DAPM_OUTPUT("SPDIF"), +}; + +static const struct snd_soc_dapm_route rt286_dapm_routes[] = { + {"ADC 0", NULL, "MCLK MODE", is_mclk_mode}, + {"ADC 1", NULL, "MCLK MODE", is_mclk_mode}, + {"Front", NULL, "MCLK MODE", is_mclk_mode}, + {"Surround", NULL, "MCLK MODE", is_mclk_mode}, + + {"HP Power", NULL, "LDO1"}, + {"HP Power", NULL, "LDO2"}, + + {"MIC1", NULL, "LDO1"}, + {"MIC1", NULL, "LDO2"}, + {"MIC1", NULL, "HV"}, + {"MIC1", NULL, "VREF"}, + {"MIC1", NULL, "MIC1 Input Buffer"}, + + {"SPO", NULL, "LDO1"}, + {"SPO", NULL, "LDO2"}, + {"SPO", NULL, "HV"}, + {"SPO", NULL, "VREF"}, + + {"DMIC1", NULL, "DMIC1 Pin"}, + {"DMIC2", NULL, "DMIC2 Pin"}, + {"DMIC1", NULL, "DMIC Receiver"}, + {"DMIC2", NULL, "DMIC Receiver"}, + + {"RECMIX", "Beep Switch", "Beep"}, + {"RECMIX", "Line1 Switch", "LINE1"}, + {"RECMIX", "Mic1 Switch", "MIC1"}, + + {"ADC 0 Mux", "Dmic", "DMIC1"}, + {"ADC 0 Mux", "RECMIX", "RECMIX"}, + {"ADC 0 Mux", "Mic", "MIC1"}, + {"ADC 1 Mux", "Dmic", "DMIC2"}, + {"ADC 1 Mux", "RECMIX", "RECMIX"}, + {"ADC 1 Mux", "Mic", "MIC1"}, + + {"ADC 0", NULL, "ADC 0 Mux"}, + {"ADC 1", NULL, "ADC 1 Mux"}, + + {"AIF1TX", NULL, "ADC 0"}, + {"AIF2TX", NULL, "ADC 1"}, + + {"DAC 0", NULL, "AIF1RX"}, + {"DAC 1", NULL, "AIF2RX"}, + + {"Front", "DAC Switch", "DAC 0"}, + {"Front", "RECMIX Switch", "RECMIX"}, + + {"Surround", NULL, "DAC 1"}, + + {"SPK Mux", "Front", "Front"}, + {"SPK Mux", "Surround", "Surround"}, + + {"HPO Mux", "Front", "Front"}, + {"HPO Mux", "Surround", "Surround"}, + + {"SPO", "Switch", "SPK Mux"}, + {"HPO L", "Switch", "HPO Mux"}, + {"HPO R", "Switch", "HPO Mux"}, + {"HPO L", NULL, "HP Power"}, + {"HPO R", NULL, "HP Power"}, + + {"SPOL", NULL, "SPO"}, + {"SPOR", NULL, "SPO"}, + {"HPO Pin", NULL, "HPO L"}, + {"HPO Pin", NULL, "HPO R"}, +}; + +static int rt286_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component); + unsigned int val = 0; + int d_len_code; + + switch (params_rate(params)) { + /* bit 14 0:48K 1:44.1K */ + case 44100: + val |= 0x4000; + break; + case 48000: + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + switch (rt286->sys_clk) { + case 12288000: + case 24576000: + if (params_rate(params) != 48000) { + dev_err(component->dev, "Sys_clk is not matched (%d %d)\n", + params_rate(params), rt286->sys_clk); + return -EINVAL; + } + break; + case 11289600: + case 22579200: + if (params_rate(params) != 44100) { + dev_err(component->dev, "Sys_clk is not matched (%d %d)\n", + params_rate(params), rt286->sys_clk); + return -EINVAL; + } + break; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 16: + d_len_code = 0; + val |= (0x1 << 4); + break; + case 32: + d_len_code = 2; + val |= (0x4 << 4); + break; + case 20: + d_len_code = 1; + val |= (0x2 << 4); + break; + case 24: + d_len_code = 2; + val |= (0x3 << 4); + break; + case 8: + d_len_code = 3; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, + RT286_I2S_CTRL1, 0x0018, d_len_code << 3); + dev_dbg(component->dev, "format val = 0x%x\n", val); + + snd_soc_component_update_bits(component, RT286_DAC_FORMAT, 0x407f, val); + snd_soc_component_update_bits(component, RT286_ADC_FORMAT, 0x407f, val); + + return 0; +} + +static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + snd_soc_component_update_bits(component, + RT286_I2S_CTRL1, 0x800, 0x800); + break; + case SND_SOC_DAIFMT_CBS_CFS: + snd_soc_component_update_bits(component, + RT286_I2S_CTRL1, 0x800, 0x0); + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + snd_soc_component_update_bits(component, + RT286_I2S_CTRL1, 0x300, 0x0); + break; + case SND_SOC_DAIFMT_LEFT_J: + snd_soc_component_update_bits(component, + RT286_I2S_CTRL1, 0x300, 0x1 << 8); + break; + case SND_SOC_DAIFMT_DSP_A: + snd_soc_component_update_bits(component, + RT286_I2S_CTRL1, 0x300, 0x2 << 8); + break; + case SND_SOC_DAIFMT_DSP_B: + snd_soc_component_update_bits(component, + RT286_I2S_CTRL1, 0x300, 0x3 << 8); + break; + default: + return -EINVAL; + } + /* bit 15 Stream Type 0:PCM 1:Non-PCM */ + snd_soc_component_update_bits(component, RT286_DAC_FORMAT, 0x8000, 0); + snd_soc_component_update_bits(component, RT286_ADC_FORMAT, 0x8000, 0); + + return 0; +} + +static int rt286_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s freq=%d\n", __func__, freq); + + if (RT286_SCLK_S_MCLK == clk_id) { + snd_soc_component_update_bits(component, + RT286_I2S_CTRL2, 0x0100, 0x0); + snd_soc_component_update_bits(component, + RT286_PLL_CTRL1, 0x20, 0x20); + } else { + snd_soc_component_update_bits(component, + RT286_I2S_CTRL2, 0x0100, 0x0100); + snd_soc_component_update_bits(component, + RT286_PLL_CTRL, 0x4, 0x4); + snd_soc_component_update_bits(component, + RT286_PLL_CTRL1, 0x20, 0x0); + } + + switch (freq) { + case 19200000: + if (RT286_SCLK_S_MCLK == clk_id) { + dev_err(component->dev, "Should not use MCLK\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, + RT286_I2S_CTRL2, 0x40, 0x40); + break; + case 24000000: + if (RT286_SCLK_S_MCLK == clk_id) { + dev_err(component->dev, "Should not use MCLK\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, + RT286_I2S_CTRL2, 0x40, 0x0); + break; + case 12288000: + case 11289600: + snd_soc_component_update_bits(component, + RT286_I2S_CTRL2, 0x8, 0x0); + snd_soc_component_update_bits(component, + RT286_CLK_DIV, 0xfc1e, 0x0004); + break; + case 24576000: + case 22579200: + snd_soc_component_update_bits(component, + RT286_I2S_CTRL2, 0x8, 0x8); + snd_soc_component_update_bits(component, + RT286_CLK_DIV, 0xfc1e, 0x5406); + break; + default: + dev_err(component->dev, "Unsupported system clock\n"); + return -EINVAL; + } + + rt286->sys_clk = freq; + rt286->clk_id = clk_id; + + return 0; +} + +static int rt286_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + + dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio); + if (50 == ratio) + snd_soc_component_update_bits(component, + RT286_I2S_CTRL1, 0x1000, 0x1000); + else + snd_soc_component_update_bits(component, + RT286_I2S_CTRL1, 0x1000, 0x0); + + + return 0; +} + +static int rt286_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) { + snd_soc_component_write(component, + RT286_SET_AUDIO_POWER, AC_PWRST_D0); + snd_soc_component_update_bits(component, + RT286_DC_GAIN, 0x200, 0x200); + } + break; + + case SND_SOC_BIAS_ON: + mdelay(10); + snd_soc_component_update_bits(component, + RT286_DC_GAIN, 0x200, 0x0); + + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_component_write(component, + RT286_SET_AUDIO_POWER, AC_PWRST_D3); + break; + + default: + break; + } + + return 0; +} + +static irqreturn_t rt286_irq(int irq, void *data) +{ + struct rt286_priv *rt286 = data; + bool hp = false; + bool mic = false; + int status = 0; + + rt286_jack_detect(rt286, &hp, &mic); + + /* Clear IRQ */ + regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x1, 0x1); + + if (hp) + status |= SND_JACK_HEADPHONE; + + if (mic) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt286->jack, status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + + pm_wakeup_event(&rt286->i2c->dev, 300); + + return IRQ_HANDLED; +} + +static int rt286_probe(struct snd_soc_component *component) +{ + struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component); + + rt286->component = component; + INIT_DELAYED_WORK(&rt286->jack_detect_work, rt286_jack_detect_work); + + if (rt286->i2c->irq) + schedule_delayed_work(&rt286->jack_detect_work, + msecs_to_jiffies(50)); + return 0; +} + +static void rt286_remove(struct snd_soc_component *component) +{ + struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&rt286->jack_detect_work); + rt286->component = NULL; +} + +#ifdef CONFIG_PM +static int rt286_suspend(struct snd_soc_component *component) +{ + struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt286->regmap, true); + regcache_mark_dirty(rt286->regmap); + + return 0; +} + +static int rt286_resume(struct snd_soc_component *component) +{ + struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt286->regmap, false); + rt286_index_sync(component); + regcache_sync(rt286->regmap); + + return 0; +} +#else +#define rt286_suspend NULL +#define rt286_resume NULL +#endif + +#define RT286_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT286_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt286_aif_dai_ops = { + .hw_params = rt286_hw_params, + .set_fmt = rt286_set_dai_fmt, + .set_sysclk = rt286_set_dai_sysclk, + .set_bclk_ratio = rt286_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt286_dai[] = { + { + .name = "rt286-aif1", + .id = RT286_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT286_STEREO_RATES, + .formats = RT286_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT286_STEREO_RATES, + .formats = RT286_FORMATS, + }, + .ops = &rt286_aif_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "rt286-aif2", + .id = RT286_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT286_STEREO_RATES, + .formats = RT286_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT286_STEREO_RATES, + .formats = RT286_FORMATS, + }, + .ops = &rt286_aif_dai_ops, + .symmetric_rate = 1, + }, + +}; + +static const struct snd_soc_component_driver soc_component_dev_rt286 = { + .probe = rt286_probe, + .remove = rt286_remove, + .suspend = rt286_suspend, + .resume = rt286_resume, + .set_bias_level = rt286_set_bias_level, + .set_jack = rt286_mic_detect, + .controls = rt286_snd_controls, + .num_controls = ARRAY_SIZE(rt286_snd_controls), + .dapm_widgets = rt286_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets), + .dapm_routes = rt286_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt286_regmap = { + .reg_bits = 32, + .val_bits = 32, + .max_register = 0x02370100, + .volatile_reg = rt286_volatile_register, + .readable_reg = rt286_readable_register, + .reg_write = rl6347a_hw_write, + .reg_read = rl6347a_hw_read, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt286_reg, + .num_reg_defaults = ARRAY_SIZE(rt286_reg), +}; + +static const struct i2c_device_id rt286_i2c_id[] = { + {"rt286", 0}, + {"rt288", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt286_i2c_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt286_acpi_match[] = { + { "INT343A", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt286_acpi_match); +#endif + +static const struct dmi_system_id force_combo_jack_table[] = { + { + .ident = "Intel Wilson Beach", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS") + } + }, + { + .ident = "Intel Skylake RVP", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform") + } + }, + { + .ident = "Intel Kabylake RVP", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform") + } + }, + { + .ident = "Thinkpad Helix 2nd", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix 2nd") + } + }, + + { } +}; + +static const struct dmi_system_id dmi_dell[] = { + { + .ident = "Dell", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + } + }, + { } +}; + +static int rt286_i2c_probe(struct i2c_client *i2c) +{ + struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt286_priv *rt286; + int i, ret, vendor_id; + + rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286), + GFP_KERNEL); + if (NULL == rt286) + return -ENOMEM; + + rt286->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt286_regmap); + if (IS_ERR(rt286->regmap)) { + ret = PTR_ERR(rt286->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = regmap_read(rt286->regmap, + RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &vendor_id); + if (ret != 0) { + dev_err(&i2c->dev, "I2C error %d\n", ret); + return ret; + } + if (vendor_id != RT286_VENDOR_ID && vendor_id != RT288_VENDOR_ID) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt286\n", + vendor_id); + return -ENODEV; + } + + rt286->index_cache = devm_kmemdup(&i2c->dev, rt286_index_def, + sizeof(rt286_index_def), GFP_KERNEL); + if (!rt286->index_cache) + return -ENOMEM; + + rt286->index_cache_size = INDEX_CACHE_SIZE; + rt286->i2c = i2c; + i2c_set_clientdata(i2c, rt286); + + /* restore codec default */ + for (i = 0; i < INDEX_CACHE_SIZE; i++) + regmap_write(rt286->regmap, rt286->index_cache[i].reg, + rt286->index_cache[i].def); + for (i = 0; i < ARRAY_SIZE(rt286_reg); i++) + regmap_write(rt286->regmap, rt286_reg[i].reg, + rt286_reg[i].def); + + if (pdata) + rt286->pdata = *pdata; + + if ((vendor_id == RT288_VENDOR_ID && dmi_check_system(dmi_dell)) || + dmi_check_system(force_combo_jack_table)) + rt286->pdata.cbj_en = true; + + regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3); + + for (i = 0; i < RT286_POWER_REG_LEN; i++) + regmap_write(rt286->regmap, + RT286_SET_POWER(rt286_support_power_controls[i]), + AC_PWRST_D1); + + if (!rt286->pdata.cbj_en) { + regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000); + regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816); + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0xf000, 0xb000); + } else { + regmap_update_bits(rt286->regmap, + RT286_CBJ_CTRL1, 0xf000, 0x5000); + } + + mdelay(10); + + if (!rt286->pdata.gpio2_en) + regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x40); + else + regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0); + + mdelay(10); + + regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000); + /* Power down LDO, VREF */ + regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0xc, 0x0); + regmap_update_bits(rt286->regmap, RT286_POWER_CTRL1, 0x1001, 0x1001); + + /* Set depop parameter */ + regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a); + regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737); + regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f); + + if (vendor_id == RT288_VENDOR_ID && dmi_check_system(dmi_dell)) { + regmap_update_bits(rt286->regmap, + RT286_SET_GPIO_MASK, 0x40, 0x40); + regmap_update_bits(rt286->regmap, + RT286_SET_GPIO_DIRECTION, 0x40, 0x40); + regmap_update_bits(rt286->regmap, + RT286_SET_GPIO_DATA, 0x40, 0x40); + regmap_update_bits(rt286->regmap, + RT286_GPIO_CTRL, 0xc, 0x8); + } + + if (rt286->i2c->irq) { + ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286); + if (ret != 0) { + dev_err(&i2c->dev, + "Failed to reguest IRQ: %d\n", ret); + return ret; + } + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt286, + rt286_dai, ARRAY_SIZE(rt286_dai)); + + return ret; +} + +static void rt286_i2c_remove(struct i2c_client *i2c) +{ + struct rt286_priv *rt286 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, rt286); +} + + +static struct i2c_driver rt286_i2c_driver = { + .driver = { + .name = "rt286", + .acpi_match_table = ACPI_PTR(rt286_acpi_match), + }, + .probe = rt286_i2c_probe, + .remove = rt286_i2c_remove, + .id_table = rt286_i2c_id, +}; + +module_i2c_driver(rt286_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT286 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt286.h b/sound/soc/codecs/rt286.h new file mode 100644 index 0000000000..4b7a3bd604 --- /dev/null +++ b/sound/soc/codecs/rt286.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt286.h -- RT286 ALSA SoC audio driver + * + * Copyright 2011 Realtek Microelectronics + * Author: Johnny Hsu + */ + +#ifndef __RT286_H__ +#define __RT286_H__ + +#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) + +#define RT286_AUDIO_FUNCTION_GROUP 0x01 +#define RT286_DAC_OUT1 0x02 +#define RT286_DAC_OUT2 0x03 +#define RT286_ADC_IN1 0x09 +#define RT286_ADC_IN2 0x08 +#define RT286_MIXER_IN 0x0b +#define RT286_MIXER_OUT1 0x0c +#define RT286_MIXER_OUT2 0x0d +#define RT286_DMIC1 0x12 +#define RT286_DMIC2 0x13 +#define RT286_SPK_OUT 0x14 +#define RT286_MIC1 0x18 +#define RT286_LINE1 0x1a +#define RT286_BEEP 0x1d +#define RT286_SPDIF 0x1e +#define RT286_VENDOR_REGISTERS 0x20 +#define RT286_HP_OUT 0x21 +#define RT286_MIXER_IN1 0x22 +#define RT286_MIXER_IN2 0x23 + +#define RT286_SET_PIN_SFT 6 +#define RT286_SET_PIN_ENABLE 0x40 +#define RT286_SET_PIN_DISABLE 0 +#define RT286_SET_EAPD_HIGH 0x2 +#define RT286_SET_EAPD_LOW 0 + +#define RT286_MUTE_SFT 7 + +/* Verb commands */ +#define RT286_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM) +#define RT286_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0) +#define RT286_SET_AUDIO_POWER RT286_SET_POWER(RT286_AUDIO_FUNCTION_GROUP) +#define RT286_SET_HPO_POWER RT286_SET_POWER(RT286_HP_OUT) +#define RT286_SET_SPK_POWER RT286_SET_POWER(RT286_SPK_OUT) +#define RT286_SET_DMIC1_POWER RT286_SET_POWER(RT286_DMIC1) +#define RT286_SPK_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_SPK_OUT, 0) +#define RT286_HPO_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_HP_OUT, 0) +#define RT286_ADC0_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN1, 0) +#define RT286_ADC1_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN2, 0) +#define RT286_SET_MIC1\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_MIC1, 0) +#define RT286_SET_PIN_HPO\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_HP_OUT, 0) +#define RT286_SET_PIN_SPK\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_SPK_OUT, 0) +#define RT286_SET_PIN_DMIC1\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_DMIC1, 0) +#define RT286_SPK_EAPD\ + VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT286_SPK_OUT, 0) +#define RT286_SET_AMP_GAIN_HPO\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0) +#define RT286_SET_AMP_GAIN_ADC_IN1\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0) +#define RT286_SET_AMP_GAIN_ADC_IN2\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN2, 0) +#define RT286_GET_HP_SENSE\ + VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_HP_OUT, 0) +#define RT286_GET_MIC1_SENSE\ + VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_MIC1, 0) +#define RT286_SET_DMIC2_DEFAULT\ + VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT286_DMIC2, 0) +#define RT286_DACL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0xa000) +#define RT286_DACR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0x9000) +#define RT286_ADCL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x6000) +#define RT286_ADCR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x5000) +#define RT286_MIC_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIC1, 0x7000) +#define RT286_SPOL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0xa000) +#define RT286_SPOR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0x9000) +#define RT286_HPOL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0xa000) +#define RT286_HPOR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0x9000) +#define RT286_F_DAC_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7000) +#define RT286_F_RECMIX_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7100) +#define RT286_REC_MIC_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7000) +#define RT286_REC_I2S_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7100) +#define RT286_REC_LINE_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7200) +#define RT286_REC_BEEP_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7300) +#define RT286_DAC_FORMAT\ + VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_DAC_OUT1, 0) +#define RT286_ADC_FORMAT\ + VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_ADC_IN1, 0) +#define RT286_COEF_INDEX\ + VERB_CMD(AC_VERB_SET_COEF_INDEX, RT286_VENDOR_REGISTERS, 0) +#define RT286_PROC_COEF\ + VERB_CMD(AC_VERB_SET_PROC_COEF, RT286_VENDOR_REGISTERS, 0) +#define RT286_SET_GPIO_MASK\ + VERB_CMD(AC_VERB_SET_GPIO_MASK, RT286_AUDIO_FUNCTION_GROUP, 0) +#define RT286_SET_GPIO_DIRECTION\ + VERB_CMD(AC_VERB_SET_GPIO_DIRECTION, RT286_AUDIO_FUNCTION_GROUP, 0) +#define RT286_SET_GPIO_DATA\ + VERB_CMD(AC_VERB_SET_GPIO_DATA, RT286_AUDIO_FUNCTION_GROUP, 0) + +/* Index registers */ +#define RT286_A_BIAS_CTRL1 0x01 +#define RT286_A_BIAS_CTRL2 0x02 +#define RT286_POWER_CTRL1 0x03 +#define RT286_A_BIAS_CTRL3 0x04 +#define RT286_POWER_CTRL2 0x08 +#define RT286_I2S_CTRL1 0x09 +#define RT286_I2S_CTRL2 0x0a +#define RT286_CLK_DIV 0x0b +#define RT286_DC_GAIN 0x0d +#define RT286_POWER_CTRL3 0x0f +#define RT286_MIC1_DET_CTRL 0x19 +#define RT286_MISC_CTRL1 0x20 +#define RT286_GPIO_CTRL 0x29 +#define RT286_IRQ_CTRL 0x33 +#define RT286_PLL_CTRL1 0x49 +#define RT286_CBJ_CTRL1 0x4f +#define RT286_CBJ_CTRL2 0x50 +#define RT286_PLL_CTRL 0x63 +#define RT286_DEPOP_CTRL1 0x66 +#define RT286_DEPOP_CTRL2 0x67 +#define RT286_DEPOP_CTRL3 0x68 +#define RT286_DEPOP_CTRL4 0x69 + +/* SPDIF (0x06) */ +#define RT286_SPDIF_SEL_SFT 0 +#define RT286_SPDIF_SEL_PCM0 0 +#define RT286_SPDIF_SEL_PCM1 1 +#define RT286_SPDIF_SEL_SPOUT 2 +#define RT286_SPDIF_SEL_PP 3 + +/* RECMIX (0x0b) */ +#define RT286_M_REC_BEEP_SFT 0 +#define RT286_M_REC_LINE1_SFT 1 +#define RT286_M_REC_MIC1_SFT 2 +#define RT286_M_REC_I2S_SFT 3 + +/* Front (0x0c) */ +#define RT286_M_FRONT_DAC_SFT 0 +#define RT286_M_FRONT_REC_SFT 1 + +/* SPK-OUT (0x14) */ +#define RT286_M_SPK_MUX_SFT 14 +#define RT286_SPK_SEL_MASK 0x1 +#define RT286_SPK_SEL_SFT 0 +#define RT286_SPK_SEL_F 0 +#define RT286_SPK_SEL_S 1 + +/* HP-OUT (0x21) */ +#define RT286_M_HP_MUX_SFT 14 +#define RT286_HP_SEL_MASK 0x1 +#define RT286_HP_SEL_SFT 0 +#define RT286_HP_SEL_F 0 +#define RT286_HP_SEL_S 1 + +/* ADC (0x22) (0x23) */ +#define RT286_ADC_SEL_MASK 0x7 +#define RT286_ADC_SEL_SFT 0 +#define RT286_ADC_SEL_SURR 0 +#define RT286_ADC_SEL_FRONT 1 +#define RT286_ADC_SEL_DMIC 2 +#define RT286_ADC_SEL_BEEP 4 +#define RT286_ADC_SEL_LINE1 5 +#define RT286_ADC_SEL_I2S 6 +#define RT286_ADC_SEL_MIC1 7 + +#define RT286_SCLK_S_MCLK 0 +#define RT286_SCLK_S_PLL 1 + +enum { + RT286_AIF1, + RT286_AIF2, + RT286_AIFS, +}; + +#endif /* __RT286_H__ */ + diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c new file mode 100644 index 0000000000..8fbd25ad9b --- /dev/null +++ b/sound/soc/codecs/rt298.c @@ -0,0 +1,1323 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt298.c -- RT298 ALSA SoC audio codec driver + * + * Copyright 2015 Realtek Semiconductor Corp. + * Author: Bard Liao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6347a.h" +#include "rt298.h" + +#define RT298_VENDOR_ID 0x10ec0298 + +struct rt298_priv { + struct reg_default *index_cache; + int index_cache_size; + struct regmap *regmap; + struct snd_soc_component *component; + struct rt298_platform_data pdata; + struct i2c_client *i2c; + struct snd_soc_jack *jack; + struct delayed_work jack_detect_work; + int sys_clk; + int clk_id; + int is_hp_in; +}; + +static const struct reg_default rt298_index_def[] = { + { 0x01, 0xa5a8 }, + { 0x02, 0x8e95 }, + { 0x03, 0x0002 }, + { 0x04, 0xaf67 }, + { 0x08, 0x200f }, + { 0x09, 0xd010 }, + { 0x0a, 0x0100 }, + { 0x0b, 0x0000 }, + { 0x0d, 0x2800 }, + { 0x0f, 0x0022 }, + { 0x19, 0x0217 }, + { 0x20, 0x0020 }, + { 0x33, 0x0208 }, + { 0x46, 0x0300 }, + { 0x49, 0x4004 }, + { 0x4f, 0x50c9 }, + { 0x50, 0x3000 }, + { 0x63, 0x1b02 }, + { 0x67, 0x1111 }, + { 0x68, 0x1016 }, + { 0x69, 0x273f }, +}; +#define INDEX_CACHE_SIZE ARRAY_SIZE(rt298_index_def) + +static const struct reg_default rt298_reg[] = { + { 0x00170500, 0x00000400 }, + { 0x00220000, 0x00000031 }, + { 0x00239000, 0x0000007f }, + { 0x0023a000, 0x0000007f }, + { 0x00270500, 0x00000400 }, + { 0x00370500, 0x00000400 }, + { 0x00870500, 0x00000400 }, + { 0x00920000, 0x00000031 }, + { 0x00935000, 0x000000c3 }, + { 0x00936000, 0x000000c3 }, + { 0x00970500, 0x00000400 }, + { 0x00b37000, 0x00000097 }, + { 0x00b37200, 0x00000097 }, + { 0x00b37300, 0x00000097 }, + { 0x00c37000, 0x00000000 }, + { 0x00c37100, 0x00000080 }, + { 0x01270500, 0x00000400 }, + { 0x01370500, 0x00000400 }, + { 0x01371f00, 0x411111f0 }, + { 0x01439000, 0x00000080 }, + { 0x0143a000, 0x00000080 }, + { 0x01470700, 0x00000000 }, + { 0x01470500, 0x00000400 }, + { 0x01470c00, 0x00000000 }, + { 0x01470100, 0x00000000 }, + { 0x01837000, 0x00000000 }, + { 0x01870500, 0x00000400 }, + { 0x02050000, 0x00000000 }, + { 0x02139000, 0x00000080 }, + { 0x0213a000, 0x00000080 }, + { 0x02170100, 0x00000000 }, + { 0x02170500, 0x00000400 }, + { 0x02170700, 0x00000000 }, + { 0x02270100, 0x00000000 }, + { 0x02370100, 0x00000000 }, + { 0x01870700, 0x00000020 }, + { 0x00830000, 0x000000c3 }, + { 0x00930000, 0x000000c3 }, + { 0x01270700, 0x00000000 }, +}; + +static bool rt298_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... 0xff: + case RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID): + case RT298_GET_HP_SENSE: + case RT298_GET_MIC1_SENSE: + case RT298_PROC_COEF: + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_MIC1, 0): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_SPK_OUT, 0): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0): + return true; + default: + return false; + } + + +} + +static bool rt298_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0 ... 0xff: + case RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID): + case RT298_GET_HP_SENSE: + case RT298_GET_MIC1_SENSE: + case RT298_SET_AUDIO_POWER: + case RT298_SET_HPO_POWER: + case RT298_SET_SPK_POWER: + case RT298_SET_DMIC1_POWER: + case RT298_SPK_MUX: + case RT298_HPO_MUX: + case RT298_ADC0_MUX: + case RT298_ADC1_MUX: + case RT298_SET_MIC1: + case RT298_SET_PIN_HPO: + case RT298_SET_PIN_SPK: + case RT298_SET_PIN_DMIC1: + case RT298_SPK_EAPD: + case RT298_SET_AMP_GAIN_HPO: + case RT298_SET_DMIC2_DEFAULT: + case RT298_DACL_GAIN: + case RT298_DACR_GAIN: + case RT298_ADCL_GAIN: + case RT298_ADCR_GAIN: + case RT298_MIC_GAIN: + case RT298_SPOL_GAIN: + case RT298_SPOR_GAIN: + case RT298_HPOL_GAIN: + case RT298_HPOR_GAIN: + case RT298_F_DAC_SWITCH: + case RT298_F_RECMIX_SWITCH: + case RT298_REC_MIC_SWITCH: + case RT298_REC_I2S_SWITCH: + case RT298_REC_LINE_SWITCH: + case RT298_REC_BEEP_SWITCH: + case RT298_DAC_FORMAT: + case RT298_ADC_FORMAT: + case RT298_COEF_INDEX: + case RT298_PROC_COEF: + case RT298_SET_AMP_GAIN_ADC_IN1: + case RT298_SET_AMP_GAIN_ADC_IN2: + case RT298_SET_POWER(RT298_DAC_OUT1): + case RT298_SET_POWER(RT298_DAC_OUT2): + case RT298_SET_POWER(RT298_ADC_IN1): + case RT298_SET_POWER(RT298_ADC_IN2): + case RT298_SET_POWER(RT298_DMIC2): + case RT298_SET_POWER(RT298_MIC1): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_MIC1, 0): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_SPK_OUT, 0): + case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0): + return true; + default: + return false; + } +} + +#ifdef CONFIG_PM +static void rt298_index_sync(struct snd_soc_component *component) +{ + struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component); + int i; + + for (i = 0; i < INDEX_CACHE_SIZE; i++) { + snd_soc_component_write(component, rt298->index_cache[i].reg, + rt298->index_cache[i].def); + } +} +#endif + +static int rt298_support_power_controls[] = { + RT298_DAC_OUT1, + RT298_DAC_OUT2, + RT298_ADC_IN1, + RT298_ADC_IN2, + RT298_MIC1, + RT298_DMIC1, + RT298_DMIC2, + RT298_SPK_OUT, + RT298_HP_OUT, +}; +#define RT298_POWER_REG_LEN ARRAY_SIZE(rt298_support_power_controls) + +static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic) +{ + struct snd_soc_dapm_context *dapm; + unsigned int val, buf; + + *hp = false; + *mic = false; + + if (!rt298->component) + return -EINVAL; + + dapm = snd_soc_component_get_dapm(rt298->component); + + if (rt298->pdata.cbj_en) { + regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf); + *hp = buf & 0x80000000; + if (*hp == rt298->is_hp_in) + return -1; + rt298->is_hp_in = *hp; + if (*hp) { + /* power on HV,VERF */ + regmap_update_bits(rt298->regmap, + RT298_DC_GAIN, 0x200, 0x200); + + snd_soc_dapm_force_enable_pin(dapm, "HV"); + snd_soc_dapm_force_enable_pin(dapm, "VREF"); + /* power LDO1 */ + snd_soc_dapm_force_enable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); + + regmap_update_bits(rt298->regmap, + RT298_POWER_CTRL1, 0x1001, 0); + regmap_update_bits(rt298->regmap, + RT298_POWER_CTRL2, 0x4, 0x4); + + regmap_write(rt298->regmap, RT298_SET_MIC1, 0x24); + msleep(50); + + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, 0xfcc0, 0xd400); + msleep(300); + regmap_read(rt298->regmap, RT298_CBJ_CTRL2, &val); + + if (0x0070 == (val & 0x0070)) { + *mic = true; + } else { + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, 0xfcc0, 0xe400); + msleep(300); + regmap_read(rt298->regmap, + RT298_CBJ_CTRL2, &val); + if (0x0070 == (val & 0x0070)) { + *mic = true; + } else { + *mic = false; + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, + 0xfcc0, 0xc400); + } + } + + regmap_update_bits(rt298->regmap, + RT298_DC_GAIN, 0x200, 0x0); + + } else { + *mic = false; + regmap_write(rt298->regmap, RT298_SET_MIC1, 0x20); + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, 0x0400, 0x0000); + } + } else { + regmap_read(rt298->regmap, RT298_GET_HP_SENSE, &buf); + *hp = buf & 0x80000000; + regmap_read(rt298->regmap, RT298_GET_MIC1_SENSE, &buf); + *mic = buf & 0x80000000; + } + if (!*mic) { + snd_soc_dapm_disable_pin(dapm, "HV"); + snd_soc_dapm_disable_pin(dapm, "VREF"); + } + if (!*hp) + snd_soc_dapm_disable_pin(dapm, "LDO1"); + snd_soc_dapm_sync(dapm); + + pr_debug("*hp = %d *mic = %d\n", *hp, *mic); + + return 0; +} + +static void rt298_jack_detect_work(struct work_struct *work) +{ + struct rt298_priv *rt298 = + container_of(work, struct rt298_priv, jack_detect_work.work); + int status = 0; + bool hp = false; + bool mic = false; + + if (rt298_jack_detect(rt298, &hp, &mic) < 0) + return; + + if (hp) + status |= SND_JACK_HEADPHONE; + + if (mic) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt298->jack, status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); +} + +static int rt298_mic_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component); + + rt298->jack = jack; + + if (jack) { + /* Enable IRQ */ + if (rt298->jack->status & SND_JACK_HEADPHONE) + snd_soc_dapm_force_enable_pin(dapm, "LDO1"); + if (rt298->jack->status & SND_JACK_MICROPHONE) { + snd_soc_dapm_force_enable_pin(dapm, "HV"); + snd_soc_dapm_force_enable_pin(dapm, "VREF"); + } + regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2); + /* Send an initial empty report */ + snd_soc_jack_report(rt298->jack, rt298->jack->status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + } else { + /* Disable IRQ */ + regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x0); + snd_soc_dapm_disable_pin(dapm, "HV"); + snd_soc_dapm_disable_pin(dapm, "VREF"); + snd_soc_dapm_disable_pin(dapm, "LDO1"); + } + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int is_mclk_mode(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component); + + if (rt298->clk_id == RT298_SCLK_S_MCLK) + return 1; + else + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt298_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT298_DACL_GAIN, + RT298_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT298_ADCL_GAIN, + RT298_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv), + SOC_SINGLE_TLV("AMIC Volume", RT298_MIC_GAIN, + 0, 0x3, 0, mic_vol_tlv), + SOC_DOUBLE_R("Speaker Playback Switch", RT298_SPOL_GAIN, + RT298_SPOR_GAIN, RT298_MUTE_SFT, 1, 1), +}; + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt298_front_mix[] = { + SOC_DAPM_SINGLE("DAC Switch", RT298_F_DAC_SWITCH, + RT298_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("RECMIX Switch", RT298_F_RECMIX_SWITCH, + RT298_MUTE_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt298_rec_mix[] = { + SOC_DAPM_SINGLE("Mic1 Switch", RT298_REC_MIC_SWITCH, + RT298_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("I2S Switch", RT298_REC_I2S_SWITCH, + RT298_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("Line1 Switch", RT298_REC_LINE_SWITCH, + RT298_MUTE_SFT, 1, 1), + SOC_DAPM_SINGLE("Beep Switch", RT298_REC_BEEP_SWITCH, + RT298_MUTE_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new spo_enable_control = + SOC_DAPM_SINGLE("Switch", RT298_SET_PIN_SPK, + RT298_SET_PIN_SFT, 1, 0); + +static const struct snd_kcontrol_new hpol_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT298_HPOL_GAIN, + RT298_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpor_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT298_HPOR_GAIN, + RT298_MUTE_SFT, 1, 1); + +/* ADC0 source */ +static const char * const rt298_adc_src[] = { + "Mic", "RECMIX", "Dmic" +}; + +static const int rt298_adc_values[] = { + 0, 4, 5, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL( + rt298_adc0_enum, RT298_ADC0_MUX, RT298_ADC_SEL_SFT, + RT298_ADC_SEL_MASK, rt298_adc_src, rt298_adc_values); + +static const struct snd_kcontrol_new rt298_adc0_mux = + SOC_DAPM_ENUM("ADC 0 source", rt298_adc0_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL( + rt298_adc1_enum, RT298_ADC1_MUX, RT298_ADC_SEL_SFT, + RT298_ADC_SEL_MASK, rt298_adc_src, rt298_adc_values); + +static const struct snd_kcontrol_new rt298_adc1_mux = + SOC_DAPM_ENUM("ADC 1 source", rt298_adc1_enum); + +static const char * const rt298_dac_src[] = { + "Front", "Surround" +}; +/* HP-OUT source */ +static SOC_ENUM_SINGLE_DECL(rt298_hpo_enum, RT298_HPO_MUX, + 0, rt298_dac_src); + +static const struct snd_kcontrol_new rt298_hpo_mux = +SOC_DAPM_ENUM("HPO source", rt298_hpo_enum); + +/* SPK-OUT source */ +static SOC_ENUM_SINGLE_DECL(rt298_spo_enum, RT298_SPK_MUX, + 0, rt298_dac_src); + +static const struct snd_kcontrol_new rt298_spo_mux = +SOC_DAPM_ENUM("SPO source", rt298_spo_enum); + +static int rt298_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(component, + RT298_SPK_EAPD, RT298_SET_EAPD_HIGH); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write(component, + RT298_SPK_EAPD, RT298_SET_EAPD_LOW); + break; + + default: + return 0; + } + + return 0; +} + +static int rt298_set_dmic1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(component, RT298_SET_PIN_DMIC1, 0x20); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write(component, RT298_SET_PIN_DMIC1, 0); + break; + default: + return 0; + } + + return 0; +} + +static int rt298_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int nid; + + nid = (w->reg >> 20) & 0xff; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0), + 0x7080, 0x7000); + /* If MCLK doesn't exist, reset AD filter */ + if (!(snd_soc_component_read(component, RT298_VAD_CTRL) & 0x200)) { + pr_info("NO MCLK\n"); + switch (nid) { + case RT298_ADC_IN1: + snd_soc_component_update_bits(component, + RT298_D_FILTER_CTRL, 0x2, 0x2); + mdelay(10); + snd_soc_component_update_bits(component, + RT298_D_FILTER_CTRL, 0x2, 0x0); + break; + case RT298_ADC_IN2: + snd_soc_component_update_bits(component, + RT298_D_FILTER_CTRL, 0x4, 0x4); + mdelay(10); + snd_soc_component_update_bits(component, + RT298_D_FILTER_CTRL, 0x4, 0x0); + break; + } + } + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0), + 0x7080, 0x7080); + break; + default: + return 0; + } + + return 0; +} + +static int rt298_mic1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, + RT298_A_BIAS_CTRL3, 0xc000, 0x8000); + snd_soc_component_update_bits(component, + RT298_A_BIAS_CTRL2, 0xc000, 0x8000); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + RT298_A_BIAS_CTRL3, 0xc000, 0x0000); + snd_soc_component_update_bits(component, + RT298_A_BIAS_CTRL2, 0xc000, 0x0000); + break; + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt298_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY_S("HV", 1, RT298_POWER_CTRL1, + 12, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF", RT298_POWER_CTRL1, + 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("BG_MBIAS", 1, RT298_POWER_CTRL2, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT298_POWER_CTRL2, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO2", 1, RT298_POWER_CTRL2, + 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("VREF1", 1, RT298_POWER_CTRL2, + 4, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LV", 2, RT298_POWER_CTRL1, + 13, 1, NULL, 0), + + + SND_SOC_DAPM_SUPPLY("MCLK MODE", RT298_PLL_CTRL1, + 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM, + 0, 0, rt298_mic1_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1 Pin"), + SND_SOC_DAPM_INPUT("DMIC2 Pin"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("Beep"), + + /* DMIC */ + SND_SOC_DAPM_PGA_E("DMIC1", RT298_SET_POWER(RT298_DMIC1), 0, 1, + NULL, 0, rt298_set_dmic1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA("DMIC2", RT298_SET_POWER(RT298_DMIC2), 0, 1, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0, + rt298_rec_mix, ARRAY_SIZE(rt298_rec_mix)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX_E("ADC 0 Mux", RT298_SET_POWER(RT298_ADC_IN1), 0, 1, + &rt298_adc0_mux, rt298_adc_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("ADC 1 Mux", RT298_SET_POWER(RT298_ADC_IN2), 0, 1, + &rt298_adc1_mux, rt298_adc_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0), + + /* Output Mux */ + SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt298_spo_mux), + SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt298_hpo_mux), + + SND_SOC_DAPM_SUPPLY("HP Power", RT298_SET_PIN_HPO, + RT298_SET_PIN_SFT, 0, NULL, 0), + + /* Output Mixer */ + SND_SOC_DAPM_MIXER("Front", RT298_SET_POWER(RT298_DAC_OUT1), 0, 1, + rt298_front_mix, ARRAY_SIZE(rt298_front_mix)), + SND_SOC_DAPM_PGA("Surround", RT298_SET_POWER(RT298_DAC_OUT2), 0, 1, + NULL, 0), + + /* Output Pga */ + SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0, + &spo_enable_control, rt298_spk_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0, + &hpol_enable_control), + SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0, + &hpor_enable_control), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + SND_SOC_DAPM_OUTPUT("HPO Pin"), + SND_SOC_DAPM_OUTPUT("SPDIF"), +}; + +static const struct snd_soc_dapm_route rt298_dapm_routes[] = { + + {"ADC 0", NULL, "MCLK MODE", is_mclk_mode}, + {"ADC 1", NULL, "MCLK MODE", is_mclk_mode}, + {"Front", NULL, "MCLK MODE", is_mclk_mode}, + {"Surround", NULL, "MCLK MODE", is_mclk_mode}, + + {"HP Power", NULL, "LDO1"}, + {"HP Power", NULL, "LDO2"}, + {"HP Power", NULL, "LV"}, + {"HP Power", NULL, "VREF1"}, + {"HP Power", NULL, "BG_MBIAS"}, + + {"MIC1", NULL, "LDO1"}, + {"MIC1", NULL, "LDO2"}, + {"MIC1", NULL, "HV"}, + {"MIC1", NULL, "LV"}, + {"MIC1", NULL, "VREF"}, + {"MIC1", NULL, "VREF1"}, + {"MIC1", NULL, "BG_MBIAS"}, + {"MIC1", NULL, "MIC1 Input Buffer"}, + + {"SPO", NULL, "LDO1"}, + {"SPO", NULL, "LDO2"}, + {"SPO", NULL, "HV"}, + {"SPO", NULL, "LV"}, + {"SPO", NULL, "VREF"}, + {"SPO", NULL, "VREF1"}, + {"SPO", NULL, "BG_MBIAS"}, + + {"DMIC1", NULL, "DMIC1 Pin"}, + {"DMIC2", NULL, "DMIC2 Pin"}, + {"DMIC1", NULL, "DMIC Receiver"}, + {"DMIC2", NULL, "DMIC Receiver"}, + + {"RECMIX", "Beep Switch", "Beep"}, + {"RECMIX", "Line1 Switch", "LINE1"}, + {"RECMIX", "Mic1 Switch", "MIC1"}, + + {"ADC 0 Mux", "Dmic", "DMIC1"}, + {"ADC 0 Mux", "RECMIX", "RECMIX"}, + {"ADC 0 Mux", "Mic", "MIC1"}, + {"ADC 1 Mux", "Dmic", "DMIC2"}, + {"ADC 1 Mux", "RECMIX", "RECMIX"}, + {"ADC 1 Mux", "Mic", "MIC1"}, + + {"ADC 0", NULL, "ADC 0 Mux"}, + {"ADC 1", NULL, "ADC 1 Mux"}, + + {"AIF1TX", NULL, "ADC 0"}, + {"AIF2TX", NULL, "ADC 1"}, + + {"DAC 0", NULL, "AIF1RX"}, + {"DAC 1", NULL, "AIF2RX"}, + + {"Front", "DAC Switch", "DAC 0"}, + {"Front", "RECMIX Switch", "RECMIX"}, + + {"Surround", NULL, "DAC 1"}, + + {"SPK Mux", "Front", "Front"}, + {"SPK Mux", "Surround", "Surround"}, + + {"HPO Mux", "Front", "Front"}, + {"HPO Mux", "Surround", "Surround"}, + + {"SPO", "Switch", "SPK Mux"}, + {"HPO L", "Switch", "HPO Mux"}, + {"HPO R", "Switch", "HPO Mux"}, + {"HPO L", NULL, "HP Power"}, + {"HPO R", NULL, "HP Power"}, + + {"SPOL", NULL, "SPO"}, + {"SPOR", NULL, "SPO"}, + {"HPO Pin", NULL, "HPO L"}, + {"HPO Pin", NULL, "HPO R"}, +}; + +static int rt298_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component); + unsigned int val = 0; + int d_len_code; + + switch (params_rate(params)) { + /* bit 14 0:48K 1:44.1K */ + case 44100: + case 48000: + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + switch (rt298->sys_clk) { + case 12288000: + case 24576000: + if (params_rate(params) != 48000) { + dev_err(component->dev, "Sys_clk is not matched (%d %d)\n", + params_rate(params), rt298->sys_clk); + return -EINVAL; + } + break; + case 11289600: + case 22579200: + if (params_rate(params) != 44100) { + dev_err(component->dev, "Sys_clk is not matched (%d %d)\n", + params_rate(params), rt298->sys_clk); + return -EINVAL; + } + break; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + d_len_code = 0; + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 16: + d_len_code = 0; + val |= (0x1 << 4); + break; + case 32: + d_len_code = 2; + val |= (0x4 << 4); + break; + case 20: + d_len_code = 1; + val |= (0x2 << 4); + break; + case 24: + d_len_code = 2; + val |= (0x3 << 4); + break; + case 8: + d_len_code = 3; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, + RT298_I2S_CTRL1, 0x0018, d_len_code << 3); + dev_dbg(component->dev, "format val = 0x%x\n", val); + + snd_soc_component_update_bits(component, RT298_DAC_FORMAT, 0x407f, val); + snd_soc_component_update_bits(component, RT298_ADC_FORMAT, 0x407f, val); + + return 0; +} + +static int rt298_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + snd_soc_component_update_bits(component, + RT298_I2S_CTRL1, 0x800, 0x800); + break; + case SND_SOC_DAIFMT_CBS_CFS: + snd_soc_component_update_bits(component, + RT298_I2S_CTRL1, 0x800, 0x0); + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + snd_soc_component_update_bits(component, + RT298_I2S_CTRL1, 0x300, 0x0); + break; + case SND_SOC_DAIFMT_LEFT_J: + snd_soc_component_update_bits(component, + RT298_I2S_CTRL1, 0x300, 0x1 << 8); + break; + case SND_SOC_DAIFMT_DSP_A: + snd_soc_component_update_bits(component, + RT298_I2S_CTRL1, 0x300, 0x2 << 8); + break; + case SND_SOC_DAIFMT_DSP_B: + snd_soc_component_update_bits(component, + RT298_I2S_CTRL1, 0x300, 0x3 << 8); + break; + default: + return -EINVAL; + } + /* bit 15 Stream Type 0:PCM 1:Non-PCM */ + snd_soc_component_update_bits(component, RT298_DAC_FORMAT, 0x8000, 0); + snd_soc_component_update_bits(component, RT298_ADC_FORMAT, 0x8000, 0); + + return 0; +} + +static int rt298_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s freq=%d\n", __func__, freq); + + if (RT298_SCLK_S_MCLK == clk_id) { + snd_soc_component_update_bits(component, + RT298_I2S_CTRL2, 0x0100, 0x0); + snd_soc_component_update_bits(component, + RT298_PLL_CTRL1, 0x20, 0x20); + } else { + snd_soc_component_update_bits(component, + RT298_I2S_CTRL2, 0x0100, 0x0100); + snd_soc_component_update_bits(component, + RT298_PLL_CTRL1, 0x20, 0x0); + } + + switch (freq) { + case 19200000: + if (RT298_SCLK_S_MCLK == clk_id) { + dev_err(component->dev, "Should not use MCLK\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, + RT298_I2S_CTRL2, 0x40, 0x40); + break; + case 24000000: + if (RT298_SCLK_S_MCLK == clk_id) { + dev_err(component->dev, "Should not use MCLK\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, + RT298_I2S_CTRL2, 0x40, 0x0); + break; + case 12288000: + case 11289600: + snd_soc_component_update_bits(component, + RT298_I2S_CTRL2, 0x8, 0x0); + snd_soc_component_update_bits(component, + RT298_CLK_DIV, 0xfc1e, 0x0004); + break; + case 24576000: + case 22579200: + snd_soc_component_update_bits(component, + RT298_I2S_CTRL2, 0x8, 0x8); + snd_soc_component_update_bits(component, + RT298_CLK_DIV, 0xfc1e, 0x5406); + break; + default: + dev_err(component->dev, "Unsupported system clock\n"); + return -EINVAL; + } + + rt298->sys_clk = freq; + rt298->clk_id = clk_id; + + return 0; +} + +static int rt298_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + + dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio); + if (50 == ratio) + snd_soc_component_update_bits(component, + RT298_I2S_CTRL1, 0x1000, 0x1000); + else + snd_soc_component_update_bits(component, + RT298_I2S_CTRL1, 0x1000, 0x0); + + + return 0; +} + +static int rt298_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == + snd_soc_component_get_bias_level(component)) { + snd_soc_component_write(component, + RT298_SET_AUDIO_POWER, AC_PWRST_D0); + snd_soc_component_update_bits(component, 0x0d, 0x200, 0x200); + snd_soc_component_update_bits(component, 0x52, 0x80, 0x0); + mdelay(20); + snd_soc_component_update_bits(component, 0x0d, 0x200, 0x0); + snd_soc_component_update_bits(component, 0x52, 0x80, 0x80); + } + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_component_write(component, + RT298_SET_AUDIO_POWER, AC_PWRST_D3); + break; + + default: + break; + } + + return 0; +} + +static irqreturn_t rt298_irq(int irq, void *data) +{ + struct rt298_priv *rt298 = data; + bool hp = false; + bool mic = false; + int ret, status = 0; + + ret = rt298_jack_detect(rt298, &hp, &mic); + + /* Clear IRQ */ + regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x1, 0x1); + + if (ret == 0) { + if (hp) + status |= SND_JACK_HEADPHONE; + + if (mic) + status |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(rt298->jack, status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + + pm_wakeup_event(&rt298->i2c->dev, 300); + } + + return IRQ_HANDLED; +} + +static int rt298_probe(struct snd_soc_component *component) +{ + struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component); + + rt298->component = component; + INIT_DELAYED_WORK(&rt298->jack_detect_work, rt298_jack_detect_work); + + if (rt298->i2c->irq) + schedule_delayed_work(&rt298->jack_detect_work, + msecs_to_jiffies(1250)); + return 0; +} + +static void rt298_remove(struct snd_soc_component *component) +{ + struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&rt298->jack_detect_work); + rt298->component = NULL; +} + +#ifdef CONFIG_PM +static int rt298_suspend(struct snd_soc_component *component) +{ + struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component); + + rt298->is_hp_in = -1; + regcache_cache_only(rt298->regmap, true); + regcache_mark_dirty(rt298->regmap); + + return 0; +} + +static int rt298_resume(struct snd_soc_component *component) +{ + struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt298->regmap, false); + rt298_index_sync(component); + regcache_sync(rt298->regmap); + + return 0; +} +#else +#define rt298_suspend NULL +#define rt298_resume NULL +#endif + +#define RT298_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT298_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt298_aif_dai_ops = { + .hw_params = rt298_hw_params, + .set_fmt = rt298_set_dai_fmt, + .set_sysclk = rt298_set_dai_sysclk, + .set_bclk_ratio = rt298_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt298_dai[] = { + { + .name = "rt298-aif1", + .id = RT298_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT298_STEREO_RATES, + .formats = RT298_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT298_STEREO_RATES, + .formats = RT298_FORMATS, + }, + .ops = &rt298_aif_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "rt298-aif2", + .id = RT298_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT298_STEREO_RATES, + .formats = RT298_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT298_STEREO_RATES, + .formats = RT298_FORMATS, + }, + .ops = &rt298_aif_dai_ops, + .symmetric_rate = 1, + }, + +}; + +static const struct snd_soc_component_driver soc_component_dev_rt298 = { + .probe = rt298_probe, + .remove = rt298_remove, + .suspend = rt298_suspend, + .resume = rt298_resume, + .set_bias_level = rt298_set_bias_level, + .set_jack = rt298_mic_detect, + .controls = rt298_snd_controls, + .num_controls = ARRAY_SIZE(rt298_snd_controls), + .dapm_widgets = rt298_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt298_dapm_widgets), + .dapm_routes = rt298_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt298_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt298_regmap = { + .reg_bits = 32, + .val_bits = 32, + .max_register = 0x02370100, + .volatile_reg = rt298_volatile_register, + .readable_reg = rt298_readable_register, + .reg_write = rl6347a_hw_write, + .reg_read = rl6347a_hw_read, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt298_reg, + .num_reg_defaults = ARRAY_SIZE(rt298_reg), +}; + +static const struct i2c_device_id rt298_i2c_id[] = { + {"rt298", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt298_i2c_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt298_acpi_match[] = { + { "INT343A", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt298_acpi_match); +#endif + +static const struct dmi_system_id force_combo_jack_table[] = { + { + .ident = "Intel Broxton P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"), + DMI_MATCH(DMI_PRODUCT_NAME, "Broxton P") + } + }, + { + .ident = "Intel Gemini Lake", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"), + DMI_MATCH(DMI_PRODUCT_NAME, "Geminilake") + } + }, + { + .ident = "Intel Kabylake R RVP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform") + } + }, + { } +}; + +static int rt298_i2c_probe(struct i2c_client *i2c) +{ + struct rt298_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt298_priv *rt298; + struct device *dev = &i2c->dev; + const struct acpi_device_id *acpiid; + int i, ret; + + rt298 = devm_kzalloc(&i2c->dev, sizeof(*rt298), + GFP_KERNEL); + if (NULL == rt298) + return -ENOMEM; + + rt298->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt298_regmap); + if (IS_ERR(rt298->regmap)) { + ret = PTR_ERR(rt298->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt298->regmap, + RT298_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret); + if (ret != RT298_VENDOR_ID) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt298\n", ret); + return -ENODEV; + } + + rt298->index_cache = devm_kmemdup(&i2c->dev, rt298_index_def, + sizeof(rt298_index_def), GFP_KERNEL); + if (!rt298->index_cache) + return -ENOMEM; + + rt298->index_cache_size = INDEX_CACHE_SIZE; + rt298->i2c = i2c; + i2c_set_clientdata(i2c, rt298); + + /* restore codec default */ + for (i = 0; i < INDEX_CACHE_SIZE; i++) + regmap_write(rt298->regmap, rt298->index_cache[i].reg, + rt298->index_cache[i].def); + for (i = 0; i < ARRAY_SIZE(rt298_reg); i++) + regmap_write(rt298->regmap, rt298_reg[i].reg, + rt298_reg[i].def); + + if (pdata) + rt298->pdata = *pdata; + + /* enable jack combo mode on supported devices */ + acpiid = acpi_match_device(dev->driver->acpi_match_table, dev); + if (acpiid && acpiid->driver_data) { + rt298->pdata = *(struct rt298_platform_data *) + acpiid->driver_data; + } + + if (dmi_check_system(force_combo_jack_table)) { + rt298->pdata.cbj_en = true; + rt298->pdata.gpio2_en = false; + } + + /* VREF Charging */ + regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80); + regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860); + /* Vref2 */ + regmap_update_bits(rt298->regmap, 0x08, 0x20, 0x20); + + regmap_write(rt298->regmap, RT298_SET_AUDIO_POWER, AC_PWRST_D3); + + for (i = 0; i < RT298_POWER_REG_LEN; i++) + regmap_write(rt298->regmap, + RT298_SET_POWER(rt298_support_power_controls[i]), + AC_PWRST_D1); + + if (!rt298->pdata.cbj_en) { + regmap_write(rt298->regmap, RT298_CBJ_CTRL2, 0x0000); + regmap_write(rt298->regmap, RT298_MIC1_DET_CTRL, 0x0816); + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, 0xf000, 0xb000); + } else { + regmap_update_bits(rt298->regmap, + RT298_CBJ_CTRL1, 0xf000, 0x5000); + } + + mdelay(10); + + if (!rt298->pdata.gpio2_en) + regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0x40); + else + regmap_write(rt298->regmap, RT298_SET_DMIC2_DEFAULT, 0); + + mdelay(10); + + regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000); + regmap_update_bits(rt298->regmap, + RT298_WIND_FILTER_CTRL, 0x0082, 0x0082); + + regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81); + regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82); + regmap_write(rt298->regmap, RT298_UNSOLICITED_MIC1, 0x84); + regmap_update_bits(rt298->regmap, RT298_IRQ_FLAG_CTRL, 0x2, 0x2); + + rt298->is_hp_in = -1; + + if (rt298->i2c->irq) { + ret = request_threaded_irq(rt298->i2c->irq, NULL, rt298_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt298", rt298); + if (ret != 0) { + dev_err(&i2c->dev, + "Failed to reguest IRQ: %d\n", ret); + return ret; + } + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt298, + rt298_dai, ARRAY_SIZE(rt298_dai)); + + return ret; +} + +static void rt298_i2c_remove(struct i2c_client *i2c) +{ + struct rt298_priv *rt298 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, rt298); +} + + +static struct i2c_driver rt298_i2c_driver = { + .driver = { + .name = "rt298", + .acpi_match_table = ACPI_PTR(rt298_acpi_match), + }, + .probe = rt298_i2c_probe, + .remove = rt298_i2c_remove, + .id_table = rt298_i2c_id, +}; + +module_i2c_driver(rt298_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT298 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h new file mode 100644 index 0000000000..f1be9c1354 --- /dev/null +++ b/sound/soc/codecs/rt298.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt298.h -- RT298 ALSA SoC audio driver + * + * Copyright 2011 Realtek Microelectronics + * Author: Johnny Hsu + */ + +#ifndef __RT298_H__ +#define __RT298_H__ + +#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D) + +#define RT298_AUDIO_FUNCTION_GROUP 0x01 +#define RT298_DAC_OUT1 0x02 +#define RT298_DAC_OUT2 0x03 +#define RT298_DIG_CVT 0x06 +#define RT298_ADC_IN1 0x09 +#define RT298_ADC_IN2 0x08 +#define RT298_MIXER_IN 0x0b +#define RT298_MIXER_OUT1 0x0c +#define RT298_MIXER_OUT2 0x0d +#define RT298_DMIC1 0x12 +#define RT298_DMIC2 0x13 +#define RT298_SPK_OUT 0x14 +#define RT298_MIC1 0x18 +#define RT298_LINE1 0x1a +#define RT298_BEEP 0x1d +#define RT298_SPDIF 0x1e +#define RT298_VENDOR_REGISTERS 0x20 +#define RT298_HP_OUT 0x21 +#define RT298_MIXER_IN1 0x22 +#define RT298_MIXER_IN2 0x23 +#define RT298_INLINE_CMD 0x55 + +#define RT298_SET_PIN_SFT 6 +#define RT298_SET_PIN_ENABLE 0x40 +#define RT298_SET_PIN_DISABLE 0 +#define RT298_SET_EAPD_HIGH 0x2 +#define RT298_SET_EAPD_LOW 0 + +#define RT298_MUTE_SFT 7 + +/* Verb commands */ +#define RT298_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM) +#define RT298_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0) +#define RT298_SET_AUDIO_POWER RT298_SET_POWER(RT298_AUDIO_FUNCTION_GROUP) +#define RT298_SET_HPO_POWER RT298_SET_POWER(RT298_HP_OUT) +#define RT298_SET_SPK_POWER RT298_SET_POWER(RT298_SPK_OUT) +#define RT298_SET_DMIC1_POWER RT298_SET_POWER(RT298_DMIC1) +#define RT298_SPK_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_SPK_OUT, 0) +#define RT298_HPO_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_HP_OUT, 0) +#define RT298_ADC0_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_MIXER_IN1, 0) +#define RT298_ADC1_MUX\ + VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT298_MIXER_IN2, 0) +#define RT298_SET_MIC1\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_MIC1, 0) +#define RT298_SET_PIN_HPO\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_HP_OUT, 0) +#define RT298_SET_PIN_SPK\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_SPK_OUT, 0) +#define RT298_SET_PIN_DMIC1\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_DMIC1, 0) +#define RT298_SET_PIN_SPDIF\ + VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT298_SPDIF, 0) +#define RT298_SET_PIN_DIG_CVT\ + VERB_CMD(AC_VERB_SET_DIGI_CONVERT_1, RT298_DIG_CVT, 0) +#define RT298_SPK_EAPD\ + VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT298_SPK_OUT, 0) +#define RT298_SET_AMP_GAIN_HPO\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0) +#define RT298_SET_AMP_GAIN_ADC_IN1\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0) +#define RT298_SET_AMP_GAIN_ADC_IN2\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN2, 0) +#define RT298_GET_HP_SENSE\ + VERB_CMD(AC_VERB_GET_PIN_SENSE, RT298_HP_OUT, 0) +#define RT298_GET_MIC1_SENSE\ + VERB_CMD(AC_VERB_GET_PIN_SENSE, RT298_MIC1, 0) +#define RT298_SET_DMIC2_DEFAULT\ + VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT298_DMIC2, 0) +#define RT298_SET_SPDIF_DEFAULT\ + VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT298_SPDIF, 0) +#define RT298_DACL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_DAC_OUT1, 0xa000) +#define RT298_DACR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_DAC_OUT1, 0x9000) +#define RT298_ADCL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0x6000) +#define RT298_ADCR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_ADC_IN1, 0x5000) +#define RT298_MIC_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIC1, 0x7000) +#define RT298_SPOL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_SPK_OUT, 0xa000) +#define RT298_SPOR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_SPK_OUT, 0x9000) +#define RT298_HPOL_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0xa000) +#define RT298_HPOR_GAIN\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_HP_OUT, 0x9000) +#define RT298_F_DAC_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_OUT1, 0x7000) +#define RT298_F_RECMIX_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_OUT1, 0x7100) +#define RT298_REC_MIC_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7000) +#define RT298_REC_I2S_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7100) +#define RT298_REC_LINE_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7200) +#define RT298_REC_BEEP_SWITCH\ + VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT298_MIXER_IN, 0x7300) +#define RT298_DAC_FORMAT\ + VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT298_DAC_OUT1, 0) +#define RT298_ADC_FORMAT\ + VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT298_ADC_IN1, 0) +#define RT298_COEF_INDEX\ + VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0) +#define RT298_PROC_COEF\ + VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0) +#define RT298_UNSOLICITED_INLINE_CMD\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_INLINE_CMD, 0) +#define RT298_UNSOLICITED_HP_OUT\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_HP_OUT, 0) +#define RT298_UNSOLICITED_MIC1\ + VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_MIC1, 0) + +/* Index registers */ +#define RT298_A_BIAS_CTRL1 0x01 +#define RT298_A_BIAS_CTRL2 0x02 +#define RT298_POWER_CTRL1 0x03 +#define RT298_A_BIAS_CTRL3 0x04 +#define RT298_D_FILTER_CTRL 0x05 +#define RT298_POWER_CTRL2 0x08 +#define RT298_I2S_CTRL1 0x09 +#define RT298_I2S_CTRL2 0x0a +#define RT298_CLK_DIV 0x0b +#define RT298_DC_GAIN 0x0d +#define RT298_POWER_CTRL3 0x0f +#define RT298_MIC1_DET_CTRL 0x19 +#define RT298_MISC_CTRL1 0x20 +#define RT298_IRQ_CTRL 0x33 +#define RT298_WIND_FILTER_CTRL 0x46 +#define RT298_PLL_CTRL1 0x49 +#define RT298_VAD_CTRL 0x4e +#define RT298_CBJ_CTRL1 0x4f +#define RT298_CBJ_CTRL2 0x50 +#define RT298_PLL_CTRL 0x63 +#define RT298_DEPOP_CTRL1 0x66 +#define RT298_DEPOP_CTRL2 0x67 +#define RT298_DEPOP_CTRL3 0x68 +#define RT298_DEPOP_CTRL4 0x69 +#define RT298_IRQ_FLAG_CTRL 0x7c + +/* SPDIF (0x06) */ +#define RT298_SPDIF_SEL_SFT 0 +#define RT298_SPDIF_SEL_PCM0 0 +#define RT298_SPDIF_SEL_PCM1 1 +#define RT298_SPDIF_SEL_SPOUT 2 +#define RT298_SPDIF_SEL_PP 3 + +/* RECMIX (0x0b) */ +#define RT298_M_REC_BEEP_SFT 0 +#define RT298_M_REC_LINE1_SFT 1 +#define RT298_M_REC_MIC1_SFT 2 +#define RT298_M_REC_I2S_SFT 3 + +/* Front (0x0c) */ +#define RT298_M_FRONT_DAC_SFT 0 +#define RT298_M_FRONT_REC_SFT 1 + +/* SPK-OUT (0x14) */ +#define RT298_M_SPK_MUX_SFT 14 +#define RT298_SPK_SEL_MASK 0x1 +#define RT298_SPK_SEL_SFT 0 +#define RT298_SPK_SEL_F 0 +#define RT298_SPK_SEL_S 1 + +/* HP-OUT (0x21) */ +#define RT298_M_HP_MUX_SFT 14 +#define RT298_HP_SEL_MASK 0x1 +#define RT298_HP_SEL_SFT 0 +#define RT298_HP_SEL_F 0 +#define RT298_HP_SEL_S 1 + +/* ADC (0x22) (0x23) */ +#define RT298_ADC_SEL_MASK 0x7 +#define RT298_ADC_SEL_SFT 0 +#define RT298_ADC_SEL_SURR 0 +#define RT298_ADC_SEL_FRONT 1 +#define RT298_ADC_SEL_DMIC 2 +#define RT298_ADC_SEL_BEEP 4 +#define RT298_ADC_SEL_LINE1 5 +#define RT298_ADC_SEL_I2S 6 +#define RT298_ADC_SEL_MIC1 7 + +#define RT298_SCLK_S_MCLK 0 +#define RT298_SCLK_S_PLL 1 + +enum { + RT298_AIF1, + RT298_AIF2, + RT298_AIFS, +}; + +#endif /* __RT298_H__ */ + diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c new file mode 100644 index 0000000000..3ee6d85268 --- /dev/null +++ b/sound/soc/codecs/rt5514-spi.c @@ -0,0 +1,514 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5514-spi.c -- RT5514 SPI driver + * + * Copyright 2015 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5514-spi.h" + +#define DRV_NAME "rt5514-spi" + +static struct spi_device *rt5514_spi; + +struct rt5514_dsp { + struct device *dev; + struct delayed_work copy_work; + struct mutex dma_lock; + struct snd_pcm_substream *substream; + unsigned int buf_base, buf_limit, buf_rp; + size_t buf_size, get_size, dma_offset; +}; + +static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .period_bytes_min = PAGE_SIZE, + .period_bytes_max = 0x20000 / 8, + .periods_min = 8, + .periods_max = 8, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = 0x20000, +}; + +static struct snd_soc_dai_driver rt5514_spi_dai = { + .name = "rt5514-dsp-cpu-dai", + .id = 0, + .capture = { + .stream_name = "DSP Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static void rt5514_spi_copy_work(struct work_struct *work) +{ + struct rt5514_dsp *rt5514_dsp = + container_of(work, struct rt5514_dsp, copy_work.work); + struct snd_pcm_runtime *runtime; + size_t period_bytes, truncated_bytes = 0; + unsigned int cur_wp, remain_data; + u8 buf[8]; + + mutex_lock(&rt5514_dsp->dma_lock); + if (!rt5514_dsp->substream) { + dev_err(rt5514_dsp->dev, "No pcm substream\n"); + goto done; + } + + runtime = rt5514_dsp->substream->runtime; + period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); + if (!period_bytes) { + schedule_delayed_work(&rt5514_dsp->copy_work, 5); + goto done; + } + + if (rt5514_dsp->buf_size % period_bytes) + rt5514_dsp->buf_size = (rt5514_dsp->buf_size / period_bytes) * + period_bytes; + + if (rt5514_dsp->get_size >= rt5514_dsp->buf_size) { + rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf, + sizeof(buf)); + cur_wp = buf[0] | buf[1] << 8 | buf[2] << 16 | + buf[3] << 24; + + if (cur_wp >= rt5514_dsp->buf_rp) + remain_data = (cur_wp - rt5514_dsp->buf_rp); + else + remain_data = + (rt5514_dsp->buf_limit - rt5514_dsp->buf_rp) + + (cur_wp - rt5514_dsp->buf_base); + + if (remain_data < period_bytes) { + schedule_delayed_work(&rt5514_dsp->copy_work, 5); + goto done; + } + } + + if (rt5514_dsp->buf_rp + period_bytes <= rt5514_dsp->buf_limit) { + rt5514_spi_burst_read(rt5514_dsp->buf_rp, + runtime->dma_area + rt5514_dsp->dma_offset, + period_bytes); + + if (rt5514_dsp->buf_rp + period_bytes == rt5514_dsp->buf_limit) + rt5514_dsp->buf_rp = rt5514_dsp->buf_base; + else + rt5514_dsp->buf_rp += period_bytes; + } else { + truncated_bytes = rt5514_dsp->buf_limit - rt5514_dsp->buf_rp; + rt5514_spi_burst_read(rt5514_dsp->buf_rp, + runtime->dma_area + rt5514_dsp->dma_offset, + truncated_bytes); + + rt5514_spi_burst_read(rt5514_dsp->buf_base, + runtime->dma_area + rt5514_dsp->dma_offset + + truncated_bytes, period_bytes - truncated_bytes); + + rt5514_dsp->buf_rp = rt5514_dsp->buf_base + period_bytes - + truncated_bytes; + } + + rt5514_dsp->get_size += period_bytes; + rt5514_dsp->dma_offset += period_bytes; + if (rt5514_dsp->dma_offset >= runtime->dma_bytes) + rt5514_dsp->dma_offset = 0; + + snd_pcm_period_elapsed(rt5514_dsp->substream); + + schedule_delayed_work(&rt5514_dsp->copy_work, 5); + +done: + mutex_unlock(&rt5514_dsp->dma_lock); +} + +static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) +{ + u8 buf[8]; + + if (!rt5514_dsp->substream) + return; + + rt5514_dsp->get_size = 0; + + /** + * The address area x1800XXXX is the register address, and it cannot + * support spi burst read perfectly. So we use the spi burst read + * individually to make sure the data correctly. + */ + rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf, + sizeof(buf)); + rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 | + buf[3] << 24; + + rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf, + sizeof(buf)); + rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 | + buf[3] << 24; + + rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf, + sizeof(buf)); + rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 | + buf[3] << 24; + + if (rt5514_dsp->buf_rp % 8) + rt5514_dsp->buf_rp = (rt5514_dsp->buf_rp / 8) * 8; + + rt5514_dsp->buf_size = rt5514_dsp->buf_limit - rt5514_dsp->buf_base; + + if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit && + rt5514_dsp->buf_rp && rt5514_dsp->buf_size) + schedule_delayed_work(&rt5514_dsp->copy_work, 0); +} + +static irqreturn_t rt5514_spi_irq(int irq, void *data) +{ + struct rt5514_dsp *rt5514_dsp = data; + + rt5514_schedule_copy(rt5514_dsp); + + return IRQ_HANDLED; +} + +/* PCM for streaming audio from the DSP buffer */ +static int rt5514_spi_pcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware); + + return 0; +} + +static int rt5514_spi_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct rt5514_dsp *rt5514_dsp = + snd_soc_component_get_drvdata(component); + u8 buf[8]; + + mutex_lock(&rt5514_dsp->dma_lock); + rt5514_dsp->substream = substream; + rt5514_dsp->dma_offset = 0; + + /* Read IRQ status and schedule copy accordingly. */ + rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf, sizeof(buf)); + if (buf[0] & RT5514_IRQ_STATUS_BIT) + rt5514_schedule_copy(rt5514_dsp); + + mutex_unlock(&rt5514_dsp->dma_lock); + + return 0; +} + +static int rt5514_spi_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct rt5514_dsp *rt5514_dsp = + snd_soc_component_get_drvdata(component); + + mutex_lock(&rt5514_dsp->dma_lock); + rt5514_dsp->substream = NULL; + mutex_unlock(&rt5514_dsp->dma_lock); + + cancel_delayed_work_sync(&rt5514_dsp->copy_work); + + return 0; +} + +static snd_pcm_uframes_t rt5514_spi_pcm_pointer( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct rt5514_dsp *rt5514_dsp = + snd_soc_component_get_drvdata(component); + + return bytes_to_frames(runtime, rt5514_dsp->dma_offset); +} + + +static int rt5514_spi_pcm_probe(struct snd_soc_component *component) +{ + struct rt5514_dsp *rt5514_dsp; + int ret; + + rt5514_dsp = devm_kzalloc(component->dev, sizeof(*rt5514_dsp), + GFP_KERNEL); + if (!rt5514_dsp) + return -ENOMEM; + + rt5514_dsp->dev = &rt5514_spi->dev; + mutex_init(&rt5514_dsp->dma_lock); + INIT_DELAYED_WORK(&rt5514_dsp->copy_work, rt5514_spi_copy_work); + snd_soc_component_set_drvdata(component, rt5514_dsp); + + if (rt5514_spi->irq) { + ret = devm_request_threaded_irq(&rt5514_spi->dev, + rt5514_spi->irq, NULL, rt5514_spi_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5514-spi", + rt5514_dsp); + if (ret) + dev_err(&rt5514_spi->dev, + "%s Failed to reguest IRQ: %d\n", __func__, + ret); + else + device_init_wakeup(rt5514_dsp->dev, true); + } + + return 0; +} + +static int rt5514_spi_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC, + NULL, 0, 0); + return 0; +} + +static const struct snd_soc_component_driver rt5514_spi_component = { + .name = DRV_NAME, + .probe = rt5514_spi_pcm_probe, + .open = rt5514_spi_pcm_open, + .hw_params = rt5514_spi_hw_params, + .hw_free = rt5514_spi_hw_free, + .pointer = rt5514_spi_pcm_pointer, + .pcm_construct = rt5514_spi_pcm_new, + .legacy_dai_naming = 1, +}; + +/** + * rt5514_spi_burst_read - Read data from SPI by rt5514 address. + * @addr: Start address. + * @rxbuf: Data Buffer for reading. + * @len: Data length, it must be a multiple of 8. + * + * + * Returns true for success. + */ +int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len) +{ + u8 spi_cmd = RT5514_SPI_CMD_BURST_READ; + int status; + u8 write_buf[8]; + unsigned int i, end, offset = 0; + + struct spi_message message; + struct spi_transfer x[3]; + + while (offset < len) { + if (offset + RT5514_SPI_BUF_LEN <= len) + end = RT5514_SPI_BUF_LEN; + else + end = len % RT5514_SPI_BUF_LEN; + + write_buf[0] = spi_cmd; + write_buf[1] = ((addr + offset) & 0xff000000) >> 24; + write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16; + write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8; + write_buf[4] = ((addr + offset) & 0x000000ff) >> 0; + + spi_message_init(&message); + memset(x, 0, sizeof(x)); + + x[0].len = 5; + x[0].tx_buf = write_buf; + spi_message_add_tail(&x[0], &message); + + x[1].len = 4; + x[1].tx_buf = write_buf; + spi_message_add_tail(&x[1], &message); + + x[2].len = end; + x[2].rx_buf = rxbuf + offset; + spi_message_add_tail(&x[2], &message); + + status = spi_sync(rt5514_spi, &message); + + if (status) + return false; + + offset += RT5514_SPI_BUF_LEN; + } + + for (i = 0; i < len; i += 8) { + write_buf[0] = rxbuf[i + 0]; + write_buf[1] = rxbuf[i + 1]; + write_buf[2] = rxbuf[i + 2]; + write_buf[3] = rxbuf[i + 3]; + write_buf[4] = rxbuf[i + 4]; + write_buf[5] = rxbuf[i + 5]; + write_buf[6] = rxbuf[i + 6]; + write_buf[7] = rxbuf[i + 7]; + + rxbuf[i + 0] = write_buf[7]; + rxbuf[i + 1] = write_buf[6]; + rxbuf[i + 2] = write_buf[5]; + rxbuf[i + 3] = write_buf[4]; + rxbuf[i + 4] = write_buf[3]; + rxbuf[i + 5] = write_buf[2]; + rxbuf[i + 6] = write_buf[1]; + rxbuf[i + 7] = write_buf[0]; + } + + return true; +} +EXPORT_SYMBOL_GPL(rt5514_spi_burst_read); + +/** + * rt5514_spi_burst_write - Write data to SPI by rt5514 address. + * @addr: Start address. + * @txbuf: Data Buffer for writng. + * @len: Data length, it must be a multiple of 8. + * + * + * Returns true for success. + */ +int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len) +{ + u8 spi_cmd = RT5514_SPI_CMD_BURST_WRITE; + u8 *write_buf; + unsigned int i, end, offset = 0; + + write_buf = kmalloc(RT5514_SPI_BUF_LEN + 6, GFP_KERNEL); + + if (write_buf == NULL) + return -ENOMEM; + + while (offset < len) { + if (offset + RT5514_SPI_BUF_LEN <= len) + end = RT5514_SPI_BUF_LEN; + else + end = len % RT5514_SPI_BUF_LEN; + + write_buf[0] = spi_cmd; + write_buf[1] = ((addr + offset) & 0xff000000) >> 24; + write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16; + write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8; + write_buf[4] = ((addr + offset) & 0x000000ff) >> 0; + + for (i = 0; i < end; i += 8) { + write_buf[i + 12] = txbuf[offset + i + 0]; + write_buf[i + 11] = txbuf[offset + i + 1]; + write_buf[i + 10] = txbuf[offset + i + 2]; + write_buf[i + 9] = txbuf[offset + i + 3]; + write_buf[i + 8] = txbuf[offset + i + 4]; + write_buf[i + 7] = txbuf[offset + i + 5]; + write_buf[i + 6] = txbuf[offset + i + 6]; + write_buf[i + 5] = txbuf[offset + i + 7]; + } + + write_buf[end + 5] = spi_cmd; + + spi_write(rt5514_spi, write_buf, end + 6); + + offset += RT5514_SPI_BUF_LEN; + } + + kfree(write_buf); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5514_spi_burst_write); + +static int rt5514_spi_probe(struct spi_device *spi) +{ + int ret; + + rt5514_spi = spi; + + ret = devm_snd_soc_register_component(&spi->dev, + &rt5514_spi_component, + &rt5514_spi_dai, 1); + if (ret < 0) { + dev_err(&spi->dev, "Failed to register component.\n"); + return ret; + } + + return 0; +} + +static int __maybe_unused rt5514_suspend(struct device *dev) +{ + int irq = to_spi_device(dev)->irq; + + if (device_may_wakeup(dev)) + enable_irq_wake(irq); + + return 0; +} + +static int __maybe_unused rt5514_resume(struct device *dev) +{ + struct rt5514_dsp *rt5514_dsp = dev_get_drvdata(dev); + int irq = to_spi_device(dev)->irq; + u8 buf[8]; + + if (device_may_wakeup(dev)) + disable_irq_wake(irq); + + if (rt5514_dsp) { + if (rt5514_dsp->substream) { + rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf, + sizeof(buf)); + if (buf[0] & RT5514_IRQ_STATUS_BIT) + rt5514_schedule_copy(rt5514_dsp); + } + } + + return 0; +} + +static const struct dev_pm_ops rt5514_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rt5514_suspend, rt5514_resume) +}; + +static const struct of_device_id rt5514_of_match[] = { + { .compatible = "realtek,rt5514", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5514_of_match); + +static struct spi_driver rt5514_spi_driver = { + .driver = { + .name = "rt5514", + .pm = &rt5514_pm_ops, + .of_match_table = of_match_ptr(rt5514_of_match), + }, + .probe = rt5514_spi_probe, +}; +module_spi_driver(rt5514_spi_driver); + +MODULE_DESCRIPTION("RT5514 SPI driver"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5514-spi.h b/sound/soc/codecs/rt5514-spi.h new file mode 100644 index 0000000000..cedb19709c --- /dev/null +++ b/sound/soc/codecs/rt5514-spi.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5514-spi.h -- RT5514 driver + * + * Copyright 2015 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#ifndef __RT5514_SPI_H__ +#define __RT5514_SPI_H__ + +/** + * RT5514_SPI_BUF_LEN is the buffer size of SPI master controller. +*/ +#define RT5514_SPI_BUF_LEN 240 + +#define RT5514_BUFFER_VOICE_BASE 0x18000200 +#define RT5514_BUFFER_VOICE_LIMIT 0x18000204 +#define RT5514_BUFFER_VOICE_WP 0x1800020c +#define RT5514_IRQ_CTRL 0x18002094 + +#define RT5514_IRQ_STATUS_BIT (0x1 << 5) + +/* SPI Command */ +enum { + RT5514_SPI_CMD_16_READ = 0, + RT5514_SPI_CMD_16_WRITE, + RT5514_SPI_CMD_32_READ, + RT5514_SPI_CMD_32_WRITE, + RT5514_SPI_CMD_BURST_READ, + RT5514_SPI_CMD_BURST_WRITE, +}; + +int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len); +int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len); + +#endif /* __RT5514_SPI_H__ */ diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c new file mode 100644 index 0000000000..43fc7814fd --- /dev/null +++ b/sound/soc/codecs/rt5514.c @@ -0,0 +1,1337 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5514.c -- RT5514 ALSA SoC audio codec driver + * + * Copyright 2015 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5514.h" +#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI) +#include "rt5514-spi.h" +#endif + +static const struct reg_sequence rt5514_i2c_patch[] = { + {0x1800101c, 0x00000000}, + {0x18001100, 0x0000031f}, + {0x18001104, 0x00000007}, + {0x18001108, 0x00000000}, + {0x1800110c, 0x00000000}, + {0x18001110, 0x00000000}, + {0x18001114, 0x00000001}, + {0x18001118, 0x00000000}, + {0x18002f08, 0x00000006}, + {0x18002f00, 0x00055149}, + {0x18002f00, 0x0005514b}, + {0x18002f00, 0x00055149}, + {0xfafafafa, 0x00000001}, + {0x18002f10, 0x00000001}, + {0x18002f10, 0x00000000}, + {0x18002f10, 0x00000001}, + {0xfafafafa, 0x00000001}, + {0x18002000, 0x000010ec}, + {0xfafafafa, 0x00000000}, +}; + +static const struct reg_sequence rt5514_patch[] = { + {RT5514_DIG_IO_CTRL, 0x00000040}, + {RT5514_CLK_CTRL1, 0x38020041}, + {RT5514_SRC_CTRL, 0x44000eee}, + {RT5514_ANA_CTRL_LDO10, 0x00028604}, + {RT5514_ANA_CTRL_ADCFED, 0x00000800}, + {RT5514_ASRC_IN_CTRL1, 0x00000003}, + {RT5514_DOWNFILTER0_CTRL3, 0x10000342}, + {RT5514_DOWNFILTER1_CTRL3, 0x10000342}, +}; + +static const struct reg_default rt5514_reg[] = { + {RT5514_RESET, 0x00000000}, + {RT5514_PWR_ANA1, 0x00808880}, + {RT5514_PWR_ANA2, 0x00220000}, + {RT5514_I2S_CTRL1, 0x00000330}, + {RT5514_I2S_CTRL2, 0x20000000}, + {RT5514_VAD_CTRL6, 0xc00007d2}, + {RT5514_EXT_VAD_CTRL, 0x80000080}, + {RT5514_DIG_IO_CTRL, 0x00000040}, + {RT5514_PAD_CTRL1, 0x00804000}, + {RT5514_DMIC_DATA_CTRL, 0x00000005}, + {RT5514_DIG_SOURCE_CTRL, 0x00000002}, + {RT5514_SRC_CTRL, 0x44000eee}, + {RT5514_DOWNFILTER2_CTRL1, 0x0000882f}, + {RT5514_PLL_SOURCE_CTRL, 0x00000004}, + {RT5514_CLK_CTRL1, 0x38020041}, + {RT5514_CLK_CTRL2, 0x00000000}, + {RT5514_PLL3_CALIB_CTRL1, 0x00400200}, + {RT5514_PLL3_CALIB_CTRL5, 0x40220012}, + {RT5514_DELAY_BUF_CTRL1, 0x7fff006a}, + {RT5514_DELAY_BUF_CTRL3, 0x00000000}, + {RT5514_ASRC_IN_CTRL1, 0x00000003}, + {RT5514_DOWNFILTER0_CTRL1, 0x00020c2f}, + {RT5514_DOWNFILTER0_CTRL2, 0x00020c2f}, + {RT5514_DOWNFILTER0_CTRL3, 0x10000342}, + {RT5514_DOWNFILTER1_CTRL1, 0x00020c2f}, + {RT5514_DOWNFILTER1_CTRL2, 0x00020c2f}, + {RT5514_DOWNFILTER1_CTRL3, 0x10000342}, + {RT5514_ANA_CTRL_LDO10, 0x00028604}, + {RT5514_ANA_CTRL_LDO18_16, 0x02000345}, + {RT5514_ANA_CTRL_ADC12, 0x0000a2a8}, + {RT5514_ANA_CTRL_ADC21, 0x00001180}, + {RT5514_ANA_CTRL_ADC22, 0x0000aaa8}, + {RT5514_ANA_CTRL_ADC23, 0x00151427}, + {RT5514_ANA_CTRL_MICBST, 0x00002000}, + {RT5514_ANA_CTRL_ADCFED, 0x00000800}, + {RT5514_ANA_CTRL_INBUF, 0x00000143}, + {RT5514_ANA_CTRL_VREF, 0x00008d50}, + {RT5514_ANA_CTRL_PLL3, 0x0000000e}, + {RT5514_ANA_CTRL_PLL1_1, 0x00000000}, + {RT5514_ANA_CTRL_PLL1_2, 0x00030220}, + {RT5514_DMIC_LP_CTRL, 0x00000000}, + {RT5514_MISC_CTRL_DSP, 0x00000000}, + {RT5514_DSP_CTRL1, 0x00055149}, + {RT5514_DSP_CTRL3, 0x00000006}, + {RT5514_DSP_CTRL4, 0x00000001}, + {RT5514_VENDOR_ID1, 0x00000001}, + {RT5514_VENDOR_ID2, 0x10ec5514}, +}; + +static void rt5514_enable_dsp_prepare(struct rt5514_priv *rt5514) +{ + /* Reset */ + regmap_write(rt5514->i2c_regmap, 0x18002000, 0x000010ec); + /* LDO_I_limit */ + regmap_write(rt5514->i2c_regmap, 0x18002200, 0x00028604); + /* I2C bypass enable */ + regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000001); + /* mini-core reset */ + regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x0005514b); + regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055149); + /* I2C bypass disable */ + regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000000); + /* PIN config */ + regmap_write(rt5514->i2c_regmap, 0x18002070, 0x00000040); + /* PLL3(QN)=RCOSC*(10+2) */ + regmap_write(rt5514->i2c_regmap, 0x18002240, 0x0000000a); + /* PLL3 source=RCOSC, fsi=rt_clk */ + regmap_write(rt5514->i2c_regmap, 0x18002100, 0x0000000b); + /* Power on RCOSC, pll3 */ + regmap_write(rt5514->i2c_regmap, 0x18002004, 0x00808b81); + /* DSP clk source = pll3, ENABLE DSP clk */ + regmap_write(rt5514->i2c_regmap, 0x18002f08, 0x00000005); + /* Enable DSP clk auto switch */ + regmap_write(rt5514->i2c_regmap, 0x18001114, 0x00000001); + /* Reduce DSP power */ + regmap_write(rt5514->i2c_regmap, 0x18001118, 0x00000001); +} + +static bool rt5514_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5514_VENDOR_ID1: + case RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +static bool rt5514_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5514_RESET: + case RT5514_PWR_ANA1: + case RT5514_PWR_ANA2: + case RT5514_I2S_CTRL1: + case RT5514_I2S_CTRL2: + case RT5514_VAD_CTRL6: + case RT5514_EXT_VAD_CTRL: + case RT5514_DIG_IO_CTRL: + case RT5514_PAD_CTRL1: + case RT5514_DMIC_DATA_CTRL: + case RT5514_DIG_SOURCE_CTRL: + case RT5514_SRC_CTRL: + case RT5514_DOWNFILTER2_CTRL1: + case RT5514_PLL_SOURCE_CTRL: + case RT5514_CLK_CTRL1: + case RT5514_CLK_CTRL2: + case RT5514_PLL3_CALIB_CTRL1: + case RT5514_PLL3_CALIB_CTRL5: + case RT5514_DELAY_BUF_CTRL1: + case RT5514_DELAY_BUF_CTRL3: + case RT5514_ASRC_IN_CTRL1: + case RT5514_DOWNFILTER0_CTRL1: + case RT5514_DOWNFILTER0_CTRL2: + case RT5514_DOWNFILTER0_CTRL3: + case RT5514_DOWNFILTER1_CTRL1: + case RT5514_DOWNFILTER1_CTRL2: + case RT5514_DOWNFILTER1_CTRL3: + case RT5514_ANA_CTRL_LDO10: + case RT5514_ANA_CTRL_LDO18_16: + case RT5514_ANA_CTRL_ADC12: + case RT5514_ANA_CTRL_ADC21: + case RT5514_ANA_CTRL_ADC22: + case RT5514_ANA_CTRL_ADC23: + case RT5514_ANA_CTRL_MICBST: + case RT5514_ANA_CTRL_ADCFED: + case RT5514_ANA_CTRL_INBUF: + case RT5514_ANA_CTRL_VREF: + case RT5514_ANA_CTRL_PLL3: + case RT5514_ANA_CTRL_PLL1_1: + case RT5514_ANA_CTRL_PLL1_2: + case RT5514_DMIC_LP_CTRL: + case RT5514_MISC_CTRL_DSP: + case RT5514_DSP_CTRL1: + case RT5514_DSP_CTRL3: + case RT5514_DSP_CTRL4: + case RT5514_VENDOR_ID1: + case RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +static bool rt5514_i2c_readable_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case RT5514_DSP_MAPPING | RT5514_RESET: + case RT5514_DSP_MAPPING | RT5514_PWR_ANA1: + case RT5514_DSP_MAPPING | RT5514_PWR_ANA2: + case RT5514_DSP_MAPPING | RT5514_I2S_CTRL1: + case RT5514_DSP_MAPPING | RT5514_I2S_CTRL2: + case RT5514_DSP_MAPPING | RT5514_VAD_CTRL6: + case RT5514_DSP_MAPPING | RT5514_EXT_VAD_CTRL: + case RT5514_DSP_MAPPING | RT5514_DIG_IO_CTRL: + case RT5514_DSP_MAPPING | RT5514_PAD_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DMIC_DATA_CTRL: + case RT5514_DSP_MAPPING | RT5514_DIG_SOURCE_CTRL: + case RT5514_DSP_MAPPING | RT5514_SRC_CTRL: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER2_CTRL1: + case RT5514_DSP_MAPPING | RT5514_PLL_SOURCE_CTRL: + case RT5514_DSP_MAPPING | RT5514_CLK_CTRL1: + case RT5514_DSP_MAPPING | RT5514_CLK_CTRL2: + case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL1: + case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL5: + case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL3: + case RT5514_DSP_MAPPING | RT5514_ASRC_IN_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL2: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL2: + case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL3: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO10: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO18_16: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC12: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC21: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC22: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC23: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_MICBST: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADCFED: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_INBUF: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_VREF: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL3: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_1: + case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_2: + case RT5514_DSP_MAPPING | RT5514_DMIC_LP_CTRL: + case RT5514_DSP_MAPPING | RT5514_MISC_CTRL_DSP: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL1: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL3: + case RT5514_DSP_MAPPING | RT5514_DSP_CTRL4: + case RT5514_DSP_MAPPING | RT5514_VENDOR_ID1: + case RT5514_DSP_MAPPING | RT5514_VENDOR_ID2: + return true; + + default: + return false; + } +} + +/* {-3, 0, +3, +4.5, +7.5, +9.5, +12, +14, +17} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-300, 300, 0), + 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0), + 4, 4, TLV_DB_SCALE_ITEM(750, 0, 0), + 5, 5, TLV_DB_SCALE_ITEM(950, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(1200, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(1400, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0) +); + +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); + +static int rt5514_dsp_voice_wake_up_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt5514->dsp_enabled; + + return 0; +} + +static int rt5514_calibration(struct rt5514_priv *rt5514, bool on) +{ + if (on) { + regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL3, 0x0000000a); + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, 0xf, + 0xa); + regmap_update_bits(rt5514->regmap, RT5514_PWR_ANA1, 0x301, + 0x301); + regmap_write(rt5514->regmap, RT5514_PLL3_CALIB_CTRL4, + 0x80000000 | rt5514->pll3_cal_value); + regmap_write(rt5514->regmap, RT5514_PLL3_CALIB_CTRL1, + 0x8bb80800); + regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5, + 0xc0000000, 0x80000000); + regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5, + 0xc0000000, 0xc0000000); + } else { + regmap_update_bits(rt5514->regmap, RT5514_PLL3_CALIB_CTRL5, + 0xc0000000, 0x40000000); + regmap_update_bits(rt5514->regmap, RT5514_PWR_ANA1, 0x301, 0); + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, 0xf, + 0x4); + } + + return 0; +} + +static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + const struct firmware *fw = NULL; + u8 buf[8]; + + if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled) + return 0; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + rt5514->dsp_enabled = ucontrol->value.integer.value[0]; + + if (rt5514->dsp_enabled) { + if (rt5514->pdata.dsp_calib_clk_name && + !IS_ERR(rt5514->dsp_calib_clk)) { + if (clk_set_rate(rt5514->dsp_calib_clk, + rt5514->pdata.dsp_calib_clk_rate)) + dev_err(component->dev, + "Can't set rate for mclk"); + + if (clk_prepare_enable(rt5514->dsp_calib_clk)) + dev_err(component->dev, + "Can't enable dsp_calib_clk"); + + rt5514_calibration(rt5514, true); + + msleep(20); +#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI) + rt5514_spi_burst_read(RT5514_PLL3_CALIB_CTRL6 | + RT5514_DSP_MAPPING, buf, sizeof(buf)); +#else + dev_err(component->dev, "There is no SPI driver for" + " loading the firmware\n"); + memset(buf, 0, sizeof(buf)); +#endif + rt5514->pll3_cal_value = buf[0] | buf[1] << 8 | + buf[2] << 16 | buf[3] << 24; + + rt5514_calibration(rt5514, false); + clk_disable_unprepare(rt5514->dsp_calib_clk); + } + + rt5514_enable_dsp_prepare(rt5514); + + request_firmware(&fw, RT5514_FIRMWARE1, component->dev); + if (fw) { +#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI) + rt5514_spi_burst_write(0x4ff60000, fw->data, + ((fw->size/8)+1)*8); +#else + dev_err(component->dev, "There is no SPI driver for" + " loading the firmware\n"); +#endif + release_firmware(fw); + fw = NULL; + } + + request_firmware(&fw, RT5514_FIRMWARE2, component->dev); + if (fw) { +#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI) + rt5514_spi_burst_write(0x4ffc0000, fw->data, + ((fw->size/8)+1)*8); +#else + dev_err(component->dev, "There is no SPI driver for" + " loading the firmware\n"); +#endif + release_firmware(fw); + fw = NULL; + } + + /* DSP run */ + regmap_write(rt5514->i2c_regmap, 0x18002f00, + 0x00055148); + + if (rt5514->pdata.dsp_calib_clk_name && + !IS_ERR(rt5514->dsp_calib_clk)) { + msleep(20); + + regmap_write(rt5514->i2c_regmap, 0x1800211c, + rt5514->pll3_cal_value); + regmap_write(rt5514->i2c_regmap, 0x18002124, + 0x00220012); + regmap_write(rt5514->i2c_regmap, 0x18002124, + 0x80220042); + regmap_write(rt5514->i2c_regmap, 0x18002124, + 0xe0220042); + } + } else { + regmap_multi_reg_write(rt5514->i2c_regmap, + rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch)); + regcache_mark_dirty(rt5514->regmap); + regcache_sync(rt5514->regmap); + } + } + + return 1; +} + +static const struct snd_kcontrol_new rt5514_snd_controls[] = { + SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST, + RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv), + SOC_DOUBLE_R_TLV("ADC1 Capture Volume", RT5514_DOWNFILTER0_CTRL1, + RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 63, 0, + adc_vol_tlv), + SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1, + RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 63, 0, + adc_vol_tlv), + SOC_SINGLE_EXT("DSP Voice Wake Up", SND_SOC_NOPM, 0, 1, 0, + rt5514_dsp_voice_wake_up_get, rt5514_dsp_voice_wake_up_put), +}; + +/* ADC Mixer*/ +static const struct snd_kcontrol_new rt5514_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5514_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_DMIC_MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_AD_MIX_BIT, 1, 1), +}; + +/* DMIC Source */ +static const char * const rt5514_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL, + RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src); + +static const struct snd_kcontrol_new rt5514_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL, + RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src); + +static const struct snd_kcontrol_new rt5514_sto2_dmic_mux = + SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5514_stereo2_dmic_enum); + +/** + * rt5514_calc_dmic_clk - Calculate the frequency divider parameter of dmic. + * + * @component: only used for dev_warn + * @rate: base clock rate. + * + * Choose divider parameter that gives the highest possible DMIC frequency in + * 1MHz - 3MHz range. + */ +static int rt5514_calc_dmic_clk(struct snd_soc_component *component, int rate) +{ + static const int div[] = {2, 3, 4, 8, 12, 16, 24, 32}; + int i; + + if (rate < 1000000 * div[0]) { + pr_warn("Base clock rate %d is too low\n", rate); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(div); i++) { + /* find divider that gives DMIC frequency below 3.072MHz */ + if (3072000 * div[i] >= rate) + return i; + } + + dev_warn(component->dev, "Base clock rate %d is too high\n", rate); + return -EINVAL; +} + +static int rt5514_set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + int idx; + + idx = rt5514_calc_dmic_clk(component, rt5514->sysclk); + if (idx < 0) + dev_err(component->dev, "Failed to set DMIC clock\n"); + else + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1, + RT5514_CLK_DMIC_OUT_SEL_MASK, + idx << RT5514_CLK_DMIC_OUT_SEL_SFT); + + if (rt5514->pdata.dmic_init_delay) + msleep(rt5514->pdata.dmic_init_delay); + + return idx; +} + +static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + + if (rt5514->sysclk_src == RT5514_SCLK_S_PLL1) + return 1; + else + return 0; +} + +static int rt5514_i2s_use_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + + return (rt5514->sysclk > rt5514->lrck * 384); +} + +static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = { + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1L"), + SND_SOC_DAPM_INPUT("DMIC1R"), + SND_SOC_DAPM_INPUT("DMIC2L"), + SND_SOC_DAPM_INPUT("DMIC2R"), + + SND_SOC_DAPM_INPUT("AMICL"), + SND_SOC_DAPM_INPUT("AMICR"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("DMIC CLK", 1, SND_SOC_NOPM, 0, 0, + rt5514_set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("ADC CLK", RT5514_CLK_CTRL1, + RT5514_CLK_AD_ANA1_EN_BIT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("LDO18 IN", RT5514_PWR_ANA1, + RT5514_POW_LDO18_IN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO18 ADC", RT5514_PWR_ANA1, + RT5514_POW_LDO18_ADC_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO21", RT5514_PWR_ANA1, RT5514_POW_LDO21_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BG LDO18 IN", RT5514_PWR_ANA1, + RT5514_POW_BG_LDO18_IN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG LDO21", RT5514_PWR_ANA1, + RT5514_POW_BG_LDO21_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BG MBIAS", RT5514_PWR_ANA2, + RT5514_POW_BG_MBIAS_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MBIAS", RT5514_PWR_ANA2, RT5514_POW_MBIAS_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF2", RT5514_PWR_ANA2, RT5514_POW_VREF2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("VREF1", RT5514_PWR_ANA2, RT5514_POW_VREF1_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + + SND_SOC_DAPM_SUPPLY("LDO16L", RT5514_PWR_ANA2, RT5514_POWL_LDO16_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1L", RT5514_PWR_ANA2, RT5514_POW_ADC1_L_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTL2", RT5514_PWR_ANA2, RT5514_POW2_BSTL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTL", RT5514_PWR_ANA2, RT5514_POW_BSTL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCFEDL", RT5514_PWR_ANA2, RT5514_POW_ADCFEDL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCL Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("LDO16R", RT5514_PWR_ANA2, RT5514_POWR_LDO16_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1R", RT5514_PWR_ANA2, RT5514_POW_ADC1_R_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTR2", RT5514_PWR_ANA2, RT5514_POW2_BSTR_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("BSTR", RT5514_PWR_ANA2, RT5514_POW_BSTR_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCFEDR", RT5514_PWR_ANA2, RT5514_POW_ADCFEDR_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADCR Power", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("PLL1 LDO ENABLE", RT5514_ANA_CTRL_PLL1_2, + RT5514_EN_LDO_PLL1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1 LDO", RT5514_PWR_ANA2, + RT5514_POW_PLL1_LDO_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5514_PWR_ANA2, RT5514_POW_PLL1_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ASRC AD1", 1, RT5514_CLK_CTRL2, + RT5514_CLK_AD0_ASRC_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ASRC AD2", 1, RT5514_CLK_CTRL2, + RT5514_CLK_AD1_ASRC_EN_BIT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5514_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5514_sto2_dmic_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5514_CLK_CTRL1, + RT5514_CLK_AD0_EN_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5514_CLK_CTRL1, + RT5514_CLK_AD1_EN_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5514_sto1_adc_l_mix, ARRAY_SIZE(rt5514_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5514_sto1_adc_r_mix, ARRAY_SIZE(rt5514_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5514_sto2_adc_l_mix, ARRAY_SIZE(rt5514_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5514_sto2_adc_r_mix, ARRAY_SIZE(rt5514_sto2_adc_r_mix)), + + SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5514_DOWNFILTER0_CTRL1, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5514_DOWNFILTER0_CTRL2, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo2 ADC MIXL", NULL, RT5514_DOWNFILTER1_CTRL1, + RT5514_AD_AD_MUTE_BIT, 1), + SND_SOC_DAPM_ADC("Stereo2 ADC MIXR", NULL, RT5514_DOWNFILTER1_CTRL2, + RT5514_AD_AD_MUTE_BIT, 1), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt5514_dapm_routes[] = { + { "DMIC1", NULL, "DMIC1L" }, + { "DMIC1", NULL, "DMIC1R" }, + { "DMIC2", NULL, "DMIC2L" }, + { "DMIC2", NULL, "DMIC2R" }, + + { "DMIC1L", NULL, "DMIC CLK" }, + { "DMIC1R", NULL, "DMIC CLK" }, + { "DMIC2L", NULL, "DMIC CLK" }, + { "DMIC2R", NULL, "DMIC CLK" }, + + { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" }, + + { "Sto1 ADC MIXL", "DMIC Switch", "Stereo1 DMIC Mux" }, + { "Sto1 ADC MIXL", "ADC Switch", "AMICL" }, + { "Sto1 ADC MIXR", "DMIC Switch", "Stereo1 DMIC Mux" }, + { "Sto1 ADC MIXR", "ADC Switch", "AMICR" }, + + { "ADC Power", NULL, "LDO18 IN" }, + { "ADC Power", NULL, "LDO18 ADC" }, + { "ADC Power", NULL, "LDO21" }, + { "ADC Power", NULL, "BG LDO18 IN" }, + { "ADC Power", NULL, "BG LDO21" }, + { "ADC Power", NULL, "BG MBIAS" }, + { "ADC Power", NULL, "MBIAS" }, + { "ADC Power", NULL, "VREF2" }, + { "ADC Power", NULL, "VREF1" }, + + { "ADCL Power", NULL, "LDO16L" }, + { "ADCL Power", NULL, "ADC1L" }, + { "ADCL Power", NULL, "BSTL2" }, + { "ADCL Power", NULL, "BSTL" }, + { "ADCL Power", NULL, "ADCFEDL" }, + + { "ADCR Power", NULL, "LDO16R" }, + { "ADCR Power", NULL, "ADC1R" }, + { "ADCR Power", NULL, "BSTR2" }, + { "ADCR Power", NULL, "BSTR" }, + { "ADCR Power", NULL, "ADCFEDR" }, + + { "AMICL", NULL, "ADC CLK" }, + { "AMICL", NULL, "ADC Power" }, + { "AMICL", NULL, "ADCL Power" }, + { "AMICR", NULL, "ADC CLK" }, + { "AMICR", NULL, "ADC Power" }, + { "AMICR", NULL, "ADCR Power" }, + + { "PLL1 LDO", NULL, "PLL1 LDO ENABLE" }, + { "PLL1", NULL, "PLL1 LDO" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" }, + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" }, + { "Stereo1 ADC MIX", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll }, + { "adc stereo1 filter", NULL, "ASRC AD1", rt5514_i2s_use_asrc }, + + { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" }, + + { "Sto2 ADC MIXL", "DMIC Switch", "Stereo2 DMIC Mux" }, + { "Sto2 ADC MIXL", "ADC Switch", "AMICL" }, + { "Sto2 ADC MIXR", "DMIC Switch", "Stereo2 DMIC Mux" }, + { "Sto2 ADC MIXR", "ADC Switch", "AMICR" }, + + { "Stereo2 ADC MIXL", NULL, "Sto2 ADC MIXL" }, + { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" }, + + { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" }, + { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" }, + { "Stereo2 ADC MIX", NULL, "adc stereo2 filter" }, + { "adc stereo2 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll }, + { "adc stereo2 filter", NULL, "ASRC AD2", rt5514_i2s_use_asrc }, + + { "AIF1TX", NULL, "Stereo1 ADC MIX"}, + { "AIF1TX", NULL, "Stereo2 ADC MIX"}, +}; + +static int rt5514_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + int pre_div, bclk_ms, frame_size; + unsigned int val_len = 0; + + rt5514->lrck = params_rate(params); + pre_div = rl6231_get_clk_info(rt5514->sysclk, rt5514->lrck); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + + bclk_ms = frame_size > 32; + rt5514->bclk = rt5514->lrck * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5514->bclk, rt5514->lrck); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val_len = RT5514_I2S_DL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val_len = RT5514_I2S_DL_24; + break; + case SNDRV_PCM_FORMAT_S8: + val_len = RT5514_I2S_DL_8; + break; + default: + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_I2S_DL_MASK, + val_len); + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1, + RT5514_CLK_AD_ANA1_SEL_MASK, + (pre_div + 1) << RT5514_CLK_AD_ANA1_SEL_SFT); + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_DIV_OUT_MASK | RT5514_SEL_ADC_OSR_MASK, + pre_div << RT5514_CLK_SYS_DIV_OUT_SFT | + pre_div << RT5514_SEL_ADC_OSR_SFT); + + return 0; +} + +static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + + case SND_SOC_DAIFMT_NB_IF: + reg_val |= RT5514_I2S_LR_INV; + break; + + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5514_I2S_BP_INV; + break; + + case SND_SOC_DAIFMT_IB_IF: + reg_val |= RT5514_I2S_BP_INV | RT5514_I2S_LR_INV; + break; + + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5514_I2S_DF_LEFT; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5514_I2S_DF_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5514_I2S_DF_PCM_B; + break; + + default: + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, + RT5514_I2S_DF_MASK | RT5514_I2S_BP_MASK | RT5514_I2S_LR_MASK, + reg_val); + + return 0; +} + +static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt5514->sysclk && clk_id == rt5514->sysclk_src) + return 0; + + switch (clk_id) { + case RT5514_SCLK_S_MCLK: + reg_val |= RT5514_CLK_SYS_PRE_SEL_MCLK; + break; + + case RT5514_SCLK_S_PLL1: + reg_val |= RT5514_CLK_SYS_PRE_SEL_PLL; + break; + + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_PRE_SEL_MASK, reg_val); + + rt5514->sysclk = freq; + rt5514->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5514->pll_in = 0; + rt5514->pll_out = 0; + regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2, + RT5514_CLK_SYS_PRE_SEL_MASK, + RT5514_CLK_SYS_PRE_SEL_MCLK); + + return 0; + } + + if (source == rt5514->pll_src && freq_in == rt5514->pll_in && + freq_out == rt5514->pll_out) + return 0; + + switch (source) { + case RT5514_PLL1_S_MCLK: + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, + RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_MCLK); + break; + + case RT5514_PLL1_S_BCLK: + regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL, + RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_SCLK); + break; + + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL1_1, + pll_code.k_code << RT5514_PLL_K_SFT | + pll_code.n_code << RT5514_PLL_N_SFT | + (pll_code.m_bp ? 0 : pll_code.m_code) << RT5514_PLL_M_SFT); + regmap_update_bits(rt5514->regmap, RT5514_ANA_CTRL_PLL1_2, + RT5514_PLL_M_BP, pll_code.m_bp << RT5514_PLL_M_BP_SFT); + + rt5514->pll_in = freq_in; + rt5514->pll_out = freq_out; + rt5514->pll_src = source; + + return 0; +} + +static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, val2 = 0; + + if (rx_mask || tx_mask) + val |= RT5514_TDM_MODE; + + switch (tx_mask) { + case 0x3: + val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH2 | + RT5514_TDM_DOCKING_START_SLOT0; + break; + + case 0x30: + val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH2 | + RT5514_TDM_DOCKING_START_SLOT4; + break; + + case 0xf: + val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH4 | + RT5514_TDM_DOCKING_START_SLOT0; + break; + + case 0xf0: + val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH4 | + RT5514_TDM_DOCKING_START_SLOT4; + break; + + default: + break; + } + + + + switch (slots) { + case 4: + val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH; + break; + + case 6: + val |= RT5514_TDMSLOT_SEL_RX_6CH | RT5514_TDMSLOT_SEL_TX_6CH; + break; + + case 8: + val |= RT5514_TDMSLOT_SEL_RX_8CH | RT5514_TDMSLOT_SEL_TX_8CH; + break; + + case 2: + default: + break; + } + + switch (slot_width) { + case 20: + val |= RT5514_CH_LEN_RX_20 | RT5514_CH_LEN_TX_20; + break; + + case 24: + val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24; + break; + + case 25: + val |= RT5514_TDM_MODE2; + break; + + case 32: + val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32; + break; + + case 16: + default: + break; + } + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE | + RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK | + RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK | + RT5514_TDM_MODE2, val); + + regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL2, + RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH_MASK | + RT5514_TDM_DOCKING_START_MASK, val2); + + return 0; +} + +static int rt5514_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (IS_ERR(rt5514->mclk)) + break; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(rt5514->mclk); + } else { + ret = clk_prepare_enable(rt5514->mclk); + if (ret) + return ret; + } + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* + * If the DSP is enabled in start of recording, the DSP + * should be disabled, and sync back to normal recording + * settings to make sure recording properly. + */ + if (rt5514->dsp_enabled) { + rt5514->dsp_enabled = 0; + regmap_multi_reg_write(rt5514->i2c_regmap, + rt5514_i2c_patch, + ARRAY_SIZE(rt5514_i2c_patch)); + regcache_mark_dirty(rt5514->regmap); + regcache_sync(rt5514->regmap); + } + } + break; + + default: + break; + } + + return 0; +} + +static int rt5514_probe(struct snd_soc_component *component) +{ + struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component); + struct platform_device *pdev = container_of(component->dev, + struct platform_device, dev); + + rt5514->mclk = devm_clk_get(component->dev, "mclk"); + if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (rt5514->pdata.dsp_calib_clk_name) { + rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev, + rt5514->pdata.dsp_calib_clk_name); + if (PTR_ERR(rt5514->dsp_calib_clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } + + rt5514->component = component; + rt5514->pll3_cal_value = 0x0078b000; + + return 0; +} + +static int rt5514_i2c_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + struct rt5514_priv *rt5514 = i2c_get_clientdata(client); + + regmap_read(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val); + + return 0; +} + +static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + struct rt5514_priv *rt5514 = i2c_get_clientdata(client); + + regmap_write(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val); + + return 0; +} + +#define RT5514_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5514_aif_dai_ops = { + .hw_params = rt5514_hw_params, + .set_fmt = rt5514_set_dai_fmt, + .set_sysclk = rt5514_set_dai_sysclk, + .set_pll = rt5514_set_dai_pll, + .set_tdm_slot = rt5514_set_tdm_slot, +}; + +static struct snd_soc_dai_driver rt5514_dai[] = { + { + .name = "rt5514-aif1", + .id = 0, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT5514_STEREO_RATES, + .formats = RT5514_FORMATS, + }, + .ops = &rt5514_aif_dai_ops, + } +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5514 = { + .probe = rt5514_probe, + .set_bias_level = rt5514_set_bias_level, + .controls = rt5514_snd_controls, + .num_controls = ARRAY_SIZE(rt5514_snd_controls), + .dapm_widgets = rt5514_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets), + .dapm_routes = rt5514_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5514_i2c_regmap = { + .name = "i2c", + .reg_bits = 32, + .val_bits = 32, + + .readable_reg = rt5514_i2c_readable_register, + + .cache_type = REGCACHE_NONE, +}; + +static const struct regmap_config rt5514_regmap = { + .reg_bits = 16, + .val_bits = 32, + + .max_register = RT5514_VENDOR_ID2, + .volatile_reg = rt5514_volatile_register, + .readable_reg = rt5514_readable_register, + .reg_read = rt5514_i2c_read, + .reg_write = rt5514_i2c_write, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5514_reg, + .num_reg_defaults = ARRAY_SIZE(rt5514_reg), + .use_single_read = true, + .use_single_write = true, +}; + +static const struct i2c_device_id rt5514_i2c_id[] = { + { "rt5514", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt5514_of_match[] = { + { .compatible = "realtek,rt5514", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5514_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5514_acpi_match[] = { + { "10EC5514", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5514_acpi_match); +#endif + +static int rt5514_parse_dp(struct rt5514_priv *rt5514, struct device *dev) +{ + device_property_read_u32(dev, "realtek,dmic-init-delay-ms", + &rt5514->pdata.dmic_init_delay); + device_property_read_string(dev, "realtek,dsp-calib-clk-name", + &rt5514->pdata.dsp_calib_clk_name); + device_property_read_u32(dev, "realtek,dsp-calib-clk-rate", + &rt5514->pdata.dsp_calib_clk_rate); + + return 0; +} + +static __maybe_unused int rt5514_i2c_resume(struct device *dev) +{ + struct rt5514_priv *rt5514 = dev_get_drvdata(dev); + unsigned int val; + + /* + * Add a bogus read to avoid rt5514's confusion after s2r in case it + * saw glitches on the i2c lines and thought the other side sent a + * start bit. + */ + regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val); + + return 0; +} + +static int rt5514_i2c_probe(struct i2c_client *i2c) +{ + struct rt5514_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5514_priv *rt5514; + int ret; + unsigned int val = ~0; + + rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv), + GFP_KERNEL); + if (rt5514 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5514); + + if (pdata) + rt5514->pdata = *pdata; + else + rt5514_parse_dp(rt5514, &i2c->dev); + + rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap); + if (IS_ERR(rt5514->i2c_regmap)) { + ret = PTR_ERR(rt5514->i2c_regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + rt5514->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5514_regmap); + if (IS_ERR(rt5514->regmap)) { + ret = PTR_ERR(rt5514->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + /* + * The rt5514 can get confused if the i2c lines glitch together, as + * can happen at bootup as regulators are turned off and on. If it's + * in this glitched state the first i2c read will fail, so we'll give + * it one change to retry. + */ + ret = regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val); + if (ret || val != RT5514_DEVICE_ID) + ret = regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val); + if (ret || val != RT5514_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5514\n", val); + return -ENODEV; + } + + ret = regmap_multi_reg_write(rt5514->i2c_regmap, rt5514_i2c_patch, + ARRAY_SIZE(rt5514_i2c_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n", + ret); + + ret = regmap_register_patch(rt5514->regmap, rt5514_patch, + ARRAY_SIZE(rt5514_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5514, + rt5514_dai, ARRAY_SIZE(rt5514_dai)); +} + +static const struct dev_pm_ops rt5514_i2_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, rt5514_i2c_resume) +}; + +static struct i2c_driver rt5514_i2c_driver = { + .driver = { + .name = "rt5514", + .acpi_match_table = ACPI_PTR(rt5514_acpi_match), + .of_match_table = of_match_ptr(rt5514_of_match), + .pm = &rt5514_i2_pm_ops, + }, + .probe = rt5514_i2c_probe, + .id_table = rt5514_i2c_id, +}; +module_i2c_driver(rt5514_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5514 driver"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h new file mode 100644 index 0000000000..75755599f9 --- /dev/null +++ b/sound/soc/codecs/rt5514.h @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5514.h -- RT5514 ALSA SoC audio driver + * + * Copyright 2015 Realtek Microelectronics + * Author: Oder Chiou + */ + +#ifndef __RT5514_H__ +#define __RT5514_H__ + +#include +#include + +#define RT5514_DEVICE_ID 0x10ec5514 + +#define RT5514_RESET 0x2000 +#define RT5514_PWR_ANA1 0x2004 +#define RT5514_PWR_ANA2 0x2008 +#define RT5514_I2S_CTRL1 0x2010 +#define RT5514_I2S_CTRL2 0x2014 +#define RT5514_VAD_CTRL6 0x2030 +#define RT5514_EXT_VAD_CTRL 0x206c +#define RT5514_DIG_IO_CTRL 0x2070 +#define RT5514_PAD_CTRL1 0x2080 +#define RT5514_DMIC_DATA_CTRL 0x20a0 +#define RT5514_DIG_SOURCE_CTRL 0x20a4 +#define RT5514_SRC_CTRL 0x20ac +#define RT5514_DOWNFILTER2_CTRL1 0x20d0 +#define RT5514_PLL_SOURCE_CTRL 0x2100 +#define RT5514_CLK_CTRL1 0x2104 +#define RT5514_CLK_CTRL2 0x2108 +#define RT5514_PLL3_CALIB_CTRL1 0x2110 +#define RT5514_PLL3_CALIB_CTRL4 0x2120 +#define RT5514_PLL3_CALIB_CTRL5 0x2124 +#define RT5514_PLL3_CALIB_CTRL6 0x2128 +#define RT5514_DELAY_BUF_CTRL1 0x2140 +#define RT5514_DELAY_BUF_CTRL3 0x2148 +#define RT5514_ASRC_IN_CTRL1 0x2180 +#define RT5514_DOWNFILTER0_CTRL1 0x2190 +#define RT5514_DOWNFILTER0_CTRL2 0x2194 +#define RT5514_DOWNFILTER0_CTRL3 0x2198 +#define RT5514_DOWNFILTER1_CTRL1 0x21a0 +#define RT5514_DOWNFILTER1_CTRL2 0x21a4 +#define RT5514_DOWNFILTER1_CTRL3 0x21a8 +#define RT5514_ANA_CTRL_LDO10 0x2200 +#define RT5514_ANA_CTRL_LDO18_16 0x2204 +#define RT5514_ANA_CTRL_ADC12 0x2210 +#define RT5514_ANA_CTRL_ADC21 0x2214 +#define RT5514_ANA_CTRL_ADC22 0x2218 +#define RT5514_ANA_CTRL_ADC23 0x221c +#define RT5514_ANA_CTRL_MICBST 0x2220 +#define RT5514_ANA_CTRL_ADCFED 0x2224 +#define RT5514_ANA_CTRL_INBUF 0x2228 +#define RT5514_ANA_CTRL_VREF 0x222c +#define RT5514_ANA_CTRL_PLL3 0x2240 +#define RT5514_ANA_CTRL_PLL1_1 0x2260 +#define RT5514_ANA_CTRL_PLL1_2 0x2264 +#define RT5514_DMIC_LP_CTRL 0x2e00 +#define RT5514_MISC_CTRL_DSP 0x2e04 +#define RT5514_DSP_CTRL1 0x2f00 +#define RT5514_DSP_CTRL3 0x2f08 +#define RT5514_DSP_CTRL4 0x2f10 +#define RT5514_VENDOR_ID1 0x2ff0 +#define RT5514_VENDOR_ID2 0x2ff4 + +#define RT5514_DSP_MAPPING 0x18000000 + +/* RT5514_PWR_ANA1 (0x2004) */ +#define RT5514_POW_LDO18_IN (0x1 << 5) +#define RT5514_POW_LDO18_IN_BIT 5 +#define RT5514_POW_LDO18_ADC (0x1 << 4) +#define RT5514_POW_LDO18_ADC_BIT 4 +#define RT5514_POW_LDO21 (0x1 << 3) +#define RT5514_POW_LDO21_BIT 3 +#define RT5514_POW_BG_LDO18_IN (0x1 << 2) +#define RT5514_POW_BG_LDO18_IN_BIT 2 +#define RT5514_POW_BG_LDO21 (0x1 << 1) +#define RT5514_POW_BG_LDO21_BIT 1 + +/* RT5514_PWR_ANA2 (0x2008) */ +#define RT5514_POW_PLL1 (0x1 << 18) +#define RT5514_POW_PLL1_BIT 18 +#define RT5514_POW_PLL1_LDO (0x1 << 16) +#define RT5514_POW_PLL1_LDO_BIT 16 +#define RT5514_POW_BG_MBIAS (0x1 << 15) +#define RT5514_POW_BG_MBIAS_BIT 15 +#define RT5514_POW_MBIAS (0x1 << 14) +#define RT5514_POW_MBIAS_BIT 14 +#define RT5514_POW_VREF2 (0x1 << 13) +#define RT5514_POW_VREF2_BIT 13 +#define RT5514_POW_VREF1 (0x1 << 12) +#define RT5514_POW_VREF1_BIT 12 +#define RT5514_POWR_LDO16 (0x1 << 11) +#define RT5514_POWR_LDO16_BIT 11 +#define RT5514_POWL_LDO16 (0x1 << 10) +#define RT5514_POWL_LDO16_BIT 10 +#define RT5514_POW_ADC2 (0x1 << 9) +#define RT5514_POW_ADC2_BIT 9 +#define RT5514_POW_INPUT_BUF (0x1 << 8) +#define RT5514_POW_INPUT_BUF_BIT 8 +#define RT5514_POW_ADC1_R (0x1 << 7) +#define RT5514_POW_ADC1_R_BIT 7 +#define RT5514_POW_ADC1_L (0x1 << 6) +#define RT5514_POW_ADC1_L_BIT 6 +#define RT5514_POW2_BSTR (0x1 << 5) +#define RT5514_POW2_BSTR_BIT 5 +#define RT5514_POW2_BSTL (0x1 << 4) +#define RT5514_POW2_BSTL_BIT 4 +#define RT5514_POW_BSTR (0x1 << 3) +#define RT5514_POW_BSTR_BIT 3 +#define RT5514_POW_BSTL (0x1 << 2) +#define RT5514_POW_BSTL_BIT 2 +#define RT5514_POW_ADCFEDR (0x1 << 1) +#define RT5514_POW_ADCFEDR_BIT 1 +#define RT5514_POW_ADCFEDL (0x1 << 0) +#define RT5514_POW_ADCFEDL_BIT 0 + +/* RT5514_I2S_CTRL1 (0x2010) */ +#define RT5514_TDM_MODE2 (0x1 << 30) +#define RT5514_TDM_MODE2_SFT 30 +#define RT5514_TDM_MODE (0x1 << 28) +#define RT5514_TDM_MODE_SFT 28 +#define RT5514_I2S_LR_MASK (0x1 << 26) +#define RT5514_I2S_LR_SFT 26 +#define RT5514_I2S_LR_NOR (0x0 << 26) +#define RT5514_I2S_LR_INV (0x1 << 26) +#define RT5514_I2S_BP_MASK (0x1 << 25) +#define RT5514_I2S_BP_SFT 25 +#define RT5514_I2S_BP_NOR (0x0 << 25) +#define RT5514_I2S_BP_INV (0x1 << 25) +#define RT5514_I2S_DF_MASK (0x7 << 16) +#define RT5514_I2S_DF_SFT 16 +#define RT5514_I2S_DF_I2S (0x0 << 16) +#define RT5514_I2S_DF_LEFT (0x1 << 16) +#define RT5514_I2S_DF_PCM_A (0x2 << 16) +#define RT5514_I2S_DF_PCM_B (0x3 << 16) +#define RT5514_TDMSLOT_SEL_RX_MASK (0x3 << 10) +#define RT5514_TDMSLOT_SEL_RX_SFT 10 +#define RT5514_TDMSLOT_SEL_RX_4CH (0x1 << 10) +#define RT5514_TDMSLOT_SEL_RX_6CH (0x2 << 10) +#define RT5514_TDMSLOT_SEL_RX_8CH (0x3 << 10) +#define RT5514_CH_LEN_RX_MASK (0x3 << 8) +#define RT5514_CH_LEN_RX_SFT 8 +#define RT5514_CH_LEN_RX_16 (0x0 << 8) +#define RT5514_CH_LEN_RX_20 (0x1 << 8) +#define RT5514_CH_LEN_RX_24 (0x2 << 8) +#define RT5514_CH_LEN_RX_32 (0x3 << 8) +#define RT5514_TDMSLOT_SEL_TX_MASK (0x3 << 6) +#define RT5514_TDMSLOT_SEL_TX_SFT 6 +#define RT5514_TDMSLOT_SEL_TX_4CH (0x1 << 6) +#define RT5514_TDMSLOT_SEL_TX_6CH (0x2 << 6) +#define RT5514_TDMSLOT_SEL_TX_8CH (0x3 << 6) +#define RT5514_CH_LEN_TX_MASK (0x3 << 4) +#define RT5514_CH_LEN_TX_SFT 4 +#define RT5514_CH_LEN_TX_16 (0x0 << 4) +#define RT5514_CH_LEN_TX_20 (0x1 << 4) +#define RT5514_CH_LEN_TX_24 (0x2 << 4) +#define RT5514_CH_LEN_TX_32 (0x3 << 4) +#define RT5514_I2S_DL_MASK (0x3 << 0) +#define RT5514_I2S_DL_SFT 0 +#define RT5514_I2S_DL_16 (0x0 << 0) +#define RT5514_I2S_DL_20 (0x1 << 0) +#define RT5514_I2S_DL_24 (0x2 << 0) +#define RT5514_I2S_DL_8 (0x3 << 0) + +/* RT5514_I2S_CTRL2 (0x2014) */ +#define RT5514_TDM_DOCKING_MODE (0x1 << 31) +#define RT5514_TDM_DOCKING_MODE_SFT 31 +#define RT5514_TDM_DOCKING_VALID_CH_MASK (0x1 << 29) +#define RT5514_TDM_DOCKING_VALID_CH_SFT 29 +#define RT5514_TDM_DOCKING_VALID_CH2 (0x0 << 29) +#define RT5514_TDM_DOCKING_VALID_CH4 (0x1 << 29) +#define RT5514_TDM_DOCKING_START_MASK (0x1 << 28) +#define RT5514_TDM_DOCKING_START_SFT 28 +#define RT5514_TDM_DOCKING_START_SLOT0 (0x0 << 28) +#define RT5514_TDM_DOCKING_START_SLOT4 (0x1 << 28) + +/* RT5514_DIG_SOURCE_CTRL (0x20a4) */ +#define RT5514_AD1_DMIC_INPUT_SEL (0x1 << 1) +#define RT5514_AD1_DMIC_INPUT_SEL_SFT 1 +#define RT5514_AD0_DMIC_INPUT_SEL (0x1 << 0) +#define RT5514_AD0_DMIC_INPUT_SEL_SFT 0 + +/* RT5514_PLL_SOURCE_CTRL (0x2100) */ +#define RT5514_PLL_1_SEL_MASK (0x7 << 12) +#define RT5514_PLL_1_SEL_SFT 12 +#define RT5514_PLL_1_SEL_SCLK (0x3 << 12) +#define RT5514_PLL_1_SEL_MCLK (0x4 << 12) + +/* RT5514_CLK_CTRL1 (0x2104) */ +#define RT5514_CLK_AD_ANA1_EN (0x1 << 31) +#define RT5514_CLK_AD_ANA1_EN_BIT 31 +#define RT5514_CLK_AD1_EN (0x1 << 24) +#define RT5514_CLK_AD1_EN_BIT 24 +#define RT5514_CLK_AD0_EN (0x1 << 23) +#define RT5514_CLK_AD0_EN_BIT 23 +#define RT5514_CLK_DMIC_OUT_SEL_MASK (0x7 << 8) +#define RT5514_CLK_DMIC_OUT_SEL_SFT 8 +#define RT5514_CLK_AD_ANA1_SEL_MASK (0xf << 0) +#define RT5514_CLK_AD_ANA1_SEL_SFT 0 + +/* RT5514_CLK_CTRL2 (0x2108) */ +#define RT5514_CLK_AD1_ASRC_EN (0x1 << 17) +#define RT5514_CLK_AD1_ASRC_EN_BIT 17 +#define RT5514_CLK_AD0_ASRC_EN (0x1 << 16) +#define RT5514_CLK_AD0_ASRC_EN_BIT 16 +#define RT5514_CLK_SYS_DIV_OUT_MASK (0x7 << 8) +#define RT5514_CLK_SYS_DIV_OUT_SFT 8 +#define RT5514_SEL_ADC_OSR_MASK (0x7 << 4) +#define RT5514_SEL_ADC_OSR_SFT 4 +#define RT5514_CLK_SYS_PRE_SEL_MASK (0x3 << 0) +#define RT5514_CLK_SYS_PRE_SEL_SFT 0 +#define RT5514_CLK_SYS_PRE_SEL_MCLK (0x2 << 0) +#define RT5514_CLK_SYS_PRE_SEL_PLL (0x3 << 0) + +/* RT5514_DOWNFILTER_CTRL (0x2190 0x2194 0x21a0 0x21a4) */ +#define RT5514_AD_DMIC_MIX (0x1 << 11) +#define RT5514_AD_DMIC_MIX_BIT 11 +#define RT5514_AD_AD_MIX (0x1 << 10) +#define RT5514_AD_AD_MIX_BIT 10 +#define RT5514_AD_AD_MUTE (0x1 << 7) +#define RT5514_AD_AD_MUTE_BIT 7 +#define RT5514_AD_GAIN_MASK (0x3f << 1) +#define RT5514_AD_GAIN_SFT 1 + +/* RT5514_ANA_CTRL_MICBST (0x2220) */ +#define RT5514_SEL_BSTL_MASK (0xf << 4) +#define RT5514_SEL_BSTL_SFT 4 +#define RT5514_SEL_BSTR_MASK (0xf << 0) +#define RT5514_SEL_BSTR_SFT 0 + +/* RT5514_ANA_CTRL_PLL1_1 (0x2260) */ +#define RT5514_PLL_K_MAX 0x1f +#define RT5514_PLL_K_MASK (RT5514_PLL_K_MAX << 16) +#define RT5514_PLL_K_SFT 16 +#define RT5514_PLL_N_MAX 0x1ff +#define RT5514_PLL_N_MASK (RT5514_PLL_N_MAX << 7) +#define RT5514_PLL_N_SFT 4 +#define RT5514_PLL_M_MAX 0xf +#define RT5514_PLL_M_MASK (RT5514_PLL_M_MAX << 0) +#define RT5514_PLL_M_SFT 0 + +/* RT5514_ANA_CTRL_PLL1_2 (0x2264) */ +#define RT5514_PLL_M_BP (0x1 << 2) +#define RT5514_PLL_M_BP_SFT 2 +#define RT5514_PLL_K_BP (0x1 << 1) +#define RT5514_PLL_K_BP_SFT 1 +#define RT5514_EN_LDO_PLL1 (0x1 << 0) +#define RT5514_EN_LDO_PLL1_BIT 0 + +#define RT5514_PLL_INP_MAX 40000000 +#define RT5514_PLL_INP_MIN 256000 + +#define RT5514_FIRMWARE1 "rt5514_dsp_fw1.bin" +#define RT5514_FIRMWARE2 "rt5514_dsp_fw2.bin" + +/* System Clock Source */ +enum { + RT5514_SCLK_S_MCLK, + RT5514_SCLK_S_PLL1, +}; + +/* PLL1 Source */ +enum { + RT5514_PLL1_S_MCLK, + RT5514_PLL1_S_BCLK, +}; + +struct rt5514_priv { + struct rt5514_platform_data pdata; + struct snd_soc_component *component; + struct regmap *i2c_regmap, *regmap; + struct clk *mclk, *dsp_calib_clk; + int sysclk; + int sysclk_src; + int lrck; + int bclk; + int pll_src; + int pll_in; + int pll_out; + int dsp_enabled; + unsigned int pll3_cal_value; +}; + +#endif /* __RT5514_H__ */ diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c new file mode 100644 index 0000000000..c13108b51e --- /dev/null +++ b/sound/soc/codecs/rt5616.c @@ -0,0 +1,1416 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5616.c -- RT5616 ALSA SoC audio codec driver + * + * Copyright 2015 Realtek Semiconductor Corp. + * Author: Bard Liao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5616.h" + +#define RT5616_PR_RANGE_BASE (0xff + 1) +#define RT5616_PR_SPACING 0x100 + +#define RT5616_PR_BASE (RT5616_PR_RANGE_BASE + (0 * RT5616_PR_SPACING)) + +static const struct regmap_range_cfg rt5616_ranges[] = { + { + .name = "PR", + .range_min = RT5616_PR_BASE, + .range_max = RT5616_PR_BASE + 0xf8, + .selector_reg = RT5616_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5616_PRIV_DATA, + .window_len = 0x1, + }, +}; + +static const struct reg_sequence init_list[] = { + {RT5616_PR_BASE + 0x3d, 0x3e00}, + {RT5616_PR_BASE + 0x25, 0x6110}, + {RT5616_PR_BASE + 0x20, 0x611f}, + {RT5616_PR_BASE + 0x21, 0x4040}, + {RT5616_PR_BASE + 0x23, 0x0004}, +}; + +#define RT5616_INIT_REG_LEN ARRAY_SIZE(init_list) + +static const struct reg_default rt5616_reg[] = { + { 0x00, 0x0021 }, + { 0x02, 0xc8c8 }, + { 0x03, 0xc8c8 }, + { 0x05, 0x0000 }, + { 0x0d, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x19, 0xafaf }, + { 0x1c, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x27, 0x7860 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5252 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x006f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x006f }, + { 0x45, 0x6000 }, + { 0x4d, 0x0000 }, + { 0x4e, 0x0000 }, + { 0x4f, 0x0279 }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x0279 }, + { 0x53, 0xf000 }, + { 0x61, 0x0000 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c0 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x70, 0x8000 }, + { 0x73, 0x1104 }, + { 0x74, 0x0c00 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x8b, 0x0600 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0000 }, + { 0x91, 0x0c00 }, + { 0x92, 0x0000 }, + { 0x93, 0x2000 }, + { 0x94, 0x0200 }, + { 0x95, 0x0000 }, + { 0xb0, 0x2080 }, + { 0xb1, 0x0000 }, + { 0xb2, 0x0000 }, + { 0xb4, 0x2206 }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xb7, 0x0000 }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x0000 }, + { 0xc0, 0x0100 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xc8, 0x0000 }, + { 0xc9, 0x0000 }, + { 0xca, 0x0000 }, + { 0xcb, 0x0000 }, + { 0xcc, 0x0000 }, + { 0xcd, 0x0000 }, + { 0xce, 0x0000 }, + { 0xcf, 0x0013 }, + { 0xd0, 0x0680 }, + { 0xd1, 0x1c17 }, + { 0xd3, 0xb320 }, + { 0xd4, 0x0000 }, + { 0xd6, 0x0000 }, + { 0xd7, 0x0000 }, + { 0xd9, 0x0809 }, + { 0xda, 0x0000 }, + { 0xfa, 0x0010 }, + { 0xfb, 0x0000 }, + { 0xfc, 0x0000 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6281 }, +}; + +struct rt5616_priv { + struct snd_soc_component *component; + struct delayed_work patch_work; + struct regmap *regmap; + struct clk *mclk; + + int sysclk; + int sysclk_src; + int lrck[RT5616_AIFS]; + int bclk[RT5616_AIFS]; + int master[RT5616_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + +}; + +static bool rt5616_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) { + if (reg >= rt5616_ranges[i].range_min && + reg <= rt5616_ranges[i].range_max) + return true; + } + + switch (reg) { + case RT5616_RESET: + case RT5616_PRIV_DATA: + case RT5616_EQ_CTRL1: + case RT5616_DRC_AGC_1: + case RT5616_IRQ_CTRL2: + case RT5616_INT_IRQ_ST: + case RT5616_PGM_REG_ARR1: + case RT5616_PGM_REG_ARR3: + case RT5616_VENDOR_ID: + case RT5616_DEVICE_ID: + return true; + default: + return false; + } +} + +static bool rt5616_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) { + if (reg >= rt5616_ranges[i].range_min && + reg <= rt5616_ranges[i].range_max) + return true; + } + + switch (reg) { + case RT5616_RESET: + case RT5616_VERSION_ID: + case RT5616_VENDOR_ID: + case RT5616_DEVICE_ID: + case RT5616_HP_VOL: + case RT5616_LOUT_CTRL1: + case RT5616_LOUT_CTRL2: + case RT5616_IN1_IN2: + case RT5616_INL1_INR1_VOL: + case RT5616_DAC1_DIG_VOL: + case RT5616_ADC_DIG_VOL: + case RT5616_ADC_BST_VOL: + case RT5616_STO1_ADC_MIXER: + case RT5616_AD_DA_MIXER: + case RT5616_STO_DAC_MIXER: + case RT5616_REC_L1_MIXER: + case RT5616_REC_L2_MIXER: + case RT5616_REC_R1_MIXER: + case RT5616_REC_R2_MIXER: + case RT5616_HPO_MIXER: + case RT5616_OUT_L1_MIXER: + case RT5616_OUT_L2_MIXER: + case RT5616_OUT_L3_MIXER: + case RT5616_OUT_R1_MIXER: + case RT5616_OUT_R2_MIXER: + case RT5616_OUT_R3_MIXER: + case RT5616_LOUT_MIXER: + case RT5616_PWR_DIG1: + case RT5616_PWR_DIG2: + case RT5616_PWR_ANLG1: + case RT5616_PWR_ANLG2: + case RT5616_PWR_MIXER: + case RT5616_PWR_VOL: + case RT5616_PRIV_INDEX: + case RT5616_PRIV_DATA: + case RT5616_I2S1_SDP: + case RT5616_ADDA_CLK1: + case RT5616_ADDA_CLK2: + case RT5616_GLB_CLK: + case RT5616_PLL_CTRL1: + case RT5616_PLL_CTRL2: + case RT5616_HP_OVCD: + case RT5616_DEPOP_M1: + case RT5616_DEPOP_M2: + case RT5616_DEPOP_M3: + case RT5616_CHARGE_PUMP: + case RT5616_PV_DET_SPK_G: + case RT5616_MICBIAS: + case RT5616_A_JD_CTL1: + case RT5616_A_JD_CTL2: + case RT5616_EQ_CTRL1: + case RT5616_EQ_CTRL2: + case RT5616_WIND_FILTER: + case RT5616_DRC_AGC_1: + case RT5616_DRC_AGC_2: + case RT5616_DRC_AGC_3: + case RT5616_SVOL_ZC: + case RT5616_JD_CTRL1: + case RT5616_JD_CTRL2: + case RT5616_IRQ_CTRL1: + case RT5616_IRQ_CTRL2: + case RT5616_INT_IRQ_ST: + case RT5616_GPIO_CTRL1: + case RT5616_GPIO_CTRL2: + case RT5616_GPIO_CTRL3: + case RT5616_PGM_REG_ARR1: + case RT5616_PGM_REG_ARR2: + case RT5616_PGM_REG_ARR3: + case RT5616_PGM_REG_ARR4: + case RT5616_PGM_REG_ARR5: + case RT5616_SCB_FUNC: + case RT5616_SCB_CTRL: + case RT5616_BASE_BACK: + case RT5616_MP3_PLUS1: + case RT5616_MP3_PLUS2: + case RT5616_ADJ_HPF_CTRL1: + case RT5616_ADJ_HPF_CTRL2: + case RT5616_HP_CALIB_AMP_DET: + case RT5616_HP_CALIB2: + case RT5616_SV_ZCD1: + case RT5616_SV_ZCD2: + case RT5616_D_MISC: + case RT5616_DUMMY2: + case RT5616_DUMMY3: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), +); + +static const struct snd_kcontrol_new rt5616_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL, + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("HPVOL Playback Switch", RT5616_HP_VOL, + RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL, + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), + /* OUTPUT Control */ + SOC_DOUBLE("OUT Playback Switch", RT5616_LOUT_CTRL1, + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("OUT Channel Switch", RT5616_LOUT_CTRL1, + RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("OUT Playback Volume", RT5616_LOUT_CTRL1, + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5616_DAC1_DIG_VOL, + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, + 175, 0, dac_vol_tlv), + /* IN1/IN2 Control */ + SOC_SINGLE_TLV("IN1 Boost Volume", RT5616_IN1_IN2, + RT5616_BST_SFT1, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN2 Boost Volume", RT5616_IN1_IN2, + RT5616_BST_SFT2, 8, 0, bst_tlv), + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5616_INL1_INR1_VOL, + RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT, + 31, 1, in_vol_tlv), + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5616_ADC_DIG_VOL, + RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5616_ADC_DIG_VOL, + RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, + 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("ADC Boost Volume", RT5616_ADC_BST_VOL, + RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), +}; + +static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + + val = snd_soc_component_read(snd_soc_dapm_to_component(source->dapm), RT5616_GLB_CLK); + val &= RT5616_SCLK_SRC_MASK; + if (val == RT5616_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5616_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5616_STO1_ADC_MIXER, + RT5616_M_STO1_ADC_L1_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5616_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5616_STO1_ADC_MIXER, + RT5616_M_STO1_ADC_R1_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5616_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5616_AD_DA_MIXER, + RT5616_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INF1 Switch", RT5616_AD_DA_MIXER, + RT5616_M_IF1_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5616_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5616_AD_DA_MIXER, + RT5616_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INF1 Switch", RT5616_AD_DA_MIXER, + RT5616_M_IF1_DAC_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5616_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5616_STO_DAC_MIXER, + RT5616_M_DAC_L1_MIXL_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5616_STO_DAC_MIXER, + RT5616_M_DAC_R1_MIXL_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5616_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5616_STO_DAC_MIXER, + RT5616_M_DAC_R1_MIXR_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5616_STO_DAC_MIXER, + RT5616_M_DAC_L1_MIXR_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5616_rec_l_mix[] = { + SOC_DAPM_SINGLE("INL1 Switch", RT5616_REC_L2_MIXER, + RT5616_M_IN1_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5616_REC_L2_MIXER, + RT5616_M_BST2_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5616_REC_L2_MIXER, + RT5616_M_BST1_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5616_rec_r_mix[] = { + SOC_DAPM_SINGLE("INR1 Switch", RT5616_REC_R2_MIXER, + RT5616_M_IN1_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5616_REC_R2_MIXER, + RT5616_M_BST2_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5616_REC_R2_MIXER, + RT5616_M_BST1_RM_R_SFT, 1, 1), +}; + +/* Analog Output Mixer */ + +static const struct snd_kcontrol_new rt5616_out_l_mix[] = { + SOC_DAPM_SINGLE("BST1 Switch", RT5616_OUT_L3_MIXER, + RT5616_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5616_OUT_L3_MIXER, + RT5616_M_BST2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL1 Switch", RT5616_OUT_L3_MIXER, + RT5616_M_IN1_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXL Switch", RT5616_OUT_L3_MIXER, + RT5616_M_RM_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5616_OUT_L3_MIXER, + RT5616_M_DAC_L1_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5616_out_r_mix[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5616_OUT_R3_MIXER, + RT5616_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5616_OUT_R3_MIXER, + RT5616_M_BST1_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR1 Switch", RT5616_OUT_R3_MIXER, + RT5616_M_IN1_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXR Switch", RT5616_OUT_R3_MIXER, + RT5616_M_RM_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5616_OUT_R3_MIXER, + RT5616_M_DAC_R1_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5616_hpo_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5616_HPO_MIXER, + RT5616_M_DAC1_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPVOL Switch", RT5616_HPO_MIXER, + RT5616_M_HPVOL_HM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5616_lout_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5616_LOUT_MIXER, + RT5616_M_DAC_L1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5616_LOUT_MIXER, + RT5616_M_DAC_R1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL L Switch", RT5616_LOUT_MIXER, + RT5616_M_OV_L_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL R Switch", RT5616_LOUT_MIXER, + RT5616_M_OV_R_LM_SFT, 1, 1), +}; + +static int rt5616_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5616_ADC_DIG_VOL, + RT5616_L_MUTE | RT5616_R_MUTE, 0); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, RT5616_ADC_DIG_VOL, + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* depop parameters */ + snd_soc_component_update_bits(component, RT5616_DEPOP_M2, + RT5616_DEPOP_MASK, RT5616_DEPOP_MAN); + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | + RT5616_HP_CB_MASK, RT5616_HP_CP_PU | + RT5616_HP_SG_DIS | RT5616_HP_CB_PU); + snd_soc_component_write(component, RT5616_PR_BASE + + RT5616_HP_DCC_INT1, 0x9f00); + /* headphone amp power on */ + snd_soc_component_update_bits(component, RT5616_PWR_ANLG1, + RT5616_PWR_FV1 | RT5616_PWR_FV2, 0); + snd_soc_component_update_bits(component, RT5616_PWR_VOL, + RT5616_PWR_HV_L | RT5616_PWR_HV_R, + RT5616_PWR_HV_L | RT5616_PWR_HV_R); + snd_soc_component_update_bits(component, RT5616_PWR_ANLG1, + RT5616_PWR_HP_L | RT5616_PWR_HP_R | + RT5616_PWR_HA, RT5616_PWR_HP_L | + RT5616_PWR_HP_R | RT5616_PWR_HA); + msleep(50); + snd_soc_component_update_bits(component, RT5616_PWR_ANLG1, + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); + + snd_soc_component_update_bits(component, RT5616_CHARGE_PUMP, + RT5616_PM_HP_MASK, RT5616_PM_HP_HV); + snd_soc_component_update_bits(component, RT5616_PR_BASE + + RT5616_CHOP_DAC_ADC, 0x0200, 0x0200); + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_HP_CO_MASK | RT5616_HP_SG_MASK, + RT5616_HP_CO_EN | RT5616_HP_SG_EN); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5616_PR_BASE + + RT5616_CHOP_DAC_ADC, 0x0200, 0x0); + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | + RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); + /* headphone amp power down */ + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_SMT_TRIG_MASK | + RT5616_HP_CD_PD_MASK | RT5616_HP_CO_MASK | + RT5616_HP_CP_MASK | RT5616_HP_SG_MASK | + RT5616_HP_CB_MASK, + RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN | + RT5616_HP_CO_DIS | RT5616_HP_CP_PD | + RT5616_HP_SG_EN | RT5616_HP_CB_PD); + snd_soc_component_update_bits(component, RT5616_PWR_ANLG1, + RT5616_PWR_HP_L | RT5616_PWR_HP_R | + RT5616_PWR_HA, 0); + break; + default: + return 0; + } + + return 0; +} + +static int rt5616_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* headphone unmute sequence */ + snd_soc_component_update_bits(component, RT5616_DEPOP_M3, + RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | + RT5616_CP_FQ3_MASK, + RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT | + RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT | + RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT); + snd_soc_component_write(component, RT5616_PR_BASE + + RT5616_MAMP_INT_REG2, 0xfc00); + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN); + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_RSTN_MASK, RT5616_RSTN_EN); + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS | + RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); + snd_soc_component_update_bits(component, RT5616_HP_VOL, + RT5616_L_MUTE | RT5616_R_MUTE, 0); + msleep(100); + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS | + RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS); + msleep(20); + snd_soc_component_update_bits(component, RT5616_HP_CALIB_AMP_DET, + RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN); + break; + + case SND_SOC_DAPM_PRE_PMD: + /* headphone mute sequence */ + snd_soc_component_update_bits(component, RT5616_DEPOP_M3, + RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK | + RT5616_CP_FQ3_MASK, + RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT | + RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT | + RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT); + snd_soc_component_write(component, RT5616_PR_BASE + + RT5616_MAMP_INT_REG2, 0xfc00); + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_HP_SG_MASK, RT5616_HP_SG_EN); + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_RSTP_MASK, RT5616_RSTP_EN); + snd_soc_component_update_bits(component, RT5616_DEPOP_M1, + RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK | + RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS | + RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN); + snd_soc_component_update_bits(component, RT5616_HP_CALIB_AMP_DET, + RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS); + msleep(90); + snd_soc_component_update_bits(component, RT5616_HP_VOL, + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); + msleep(30); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5616_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5616_PWR_ANLG1, + RT5616_PWR_LM, RT5616_PWR_LM); + snd_soc_component_update_bits(component, RT5616_LOUT_CTRL1, + RT5616_L_MUTE | RT5616_R_MUTE, 0); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5616_LOUT_CTRL1, + RT5616_L_MUTE | RT5616_R_MUTE, + RT5616_L_MUTE | RT5616_R_MUTE); + snd_soc_component_update_bits(component, RT5616_PWR_ANLG1, + RT5616_PWR_LM, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5616_bst1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5616_PWR_ANLG2, + RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5616_PWR_ANLG2, + RT5616_PWR_BST1_OP2, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5616_bst2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5616_PWR_ANLG2, + RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5616_PWR_ANLG2, + RT5616_PWR_BST2_OP2, 0); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL1", RT5616_PWR_ANLG2, + RT5616_PWR_PLL_BIT, 0, NULL, 0), + /* Input Side */ + /* micbias */ + SND_SOC_DAPM_SUPPLY("LDO", RT5616_PWR_ANLG1, + RT5616_PWR_LDO_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("micbias1", RT5616_PWR_ANLG2, + RT5616_PWR_MB1_BIT, 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + + /* Boost */ + SND_SOC_DAPM_PGA_E("BST1", RT5616_PWR_ANLG2, + RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("BST2", RT5616_PWR_ANLG2, + RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + /* Input Volume */ + SND_SOC_DAPM_PGA("INL1 VOL", RT5616_PWR_VOL, + RT5616_PWR_IN1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR1 VOL", RT5616_PWR_VOL, + RT5616_PWR_IN1_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INL2 VOL", RT5616_PWR_VOL, + RT5616_PWR_IN2_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR2 VOL", RT5616_PWR_VOL, + RT5616_PWR_IN2_R_BIT, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5616_PWR_MIXER, RT5616_PWR_RM_L_BIT, 0, + rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5616_PWR_MIXER, RT5616_PWR_RM_R_BIT, 0, + rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)), + /* ADCs */ + SND_SOC_DAPM_ADC_E("ADC L", NULL, RT5616_PWR_DIG1, + RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_ADC_E("ADC R", NULL, RT5616_PWR_DIG1, + RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2, + RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5616_sto1_adc_l_mix, + ARRAY_SIZE(rt5616_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5616_sto1_adc_r_mix, + ARRAY_SIZE(rt5616_sto1_adc_r_mix)), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5616_PWR_DIG1, + RT5616_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Audio DSP */ + SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)), + + SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5616_PWR_DIG2, + RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0), + + /* DAC Mixer */ + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5616_sto_dac_l_mix, + ARRAY_SIZE(rt5616_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5616_sto_dac_r_mix, + ARRAY_SIZE(rt5616_sto_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5616_PWR_DIG1, + RT5616_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5616_PWR_DIG1, + RT5616_PWR_DAC_R1_BIT, 0), + /* OUT Mixer */ + SND_SOC_DAPM_MIXER("OUT MIXL", RT5616_PWR_MIXER, RT5616_PWR_OM_L_BIT, + 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5616_PWR_MIXER, RT5616_PWR_OM_R_BIT, + 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)), + /* Output Volume */ + SND_SOC_DAPM_PGA("OUTVOL L", RT5616_PWR_VOL, + RT5616_PWR_OV_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("OUTVOL R", RT5616_PWR_VOL, + RT5616_PWR_OV_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL L", RT5616_PWR_VOL, + RT5616_PWR_HV_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL R", RT5616_PWR_VOL, + RT5616_PWR_HV_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("INL1", RT5616_PWR_VOL, + RT5616_PWR_IN1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR1", RT5616_PWR_VOL, + RT5616_PWR_IN1_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INL2", RT5616_PWR_VOL, + RT5616_PWR_IN2_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR2", RT5616_PWR_VOL, + RT5616_PWR_IN2_R_BIT, 0, NULL, 0), + /* HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, + rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)), + SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, + rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)), + + SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0, + rt5616_hp_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, + rt5616_lout_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, SND_SOC_NOPM, 0, 0, + rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), +}; + +static const struct snd_soc_dapm_route rt5616_dapm_routes[] = { + {"IN1P", NULL, "LDO"}, + {"IN2P", NULL, "LDO"}, + + {"IN1P", NULL, "MIC1"}, + {"IN2P", NULL, "MIC2"}, + {"IN2N", NULL, "MIC2"}, + + {"BST1", NULL, "IN1P"}, + {"BST2", NULL, "IN2P"}, + {"BST2", NULL, "IN2N"}, + {"BST1", NULL, "micbias1"}, + {"BST2", NULL, "micbias1"}, + + {"INL1 VOL", NULL, "IN2P"}, + {"INR1 VOL", NULL, "IN2N"}, + + {"RECMIXL", "INL1 Switch", "INL1 VOL"}, + {"RECMIXL", "BST2 Switch", "BST2"}, + {"RECMIXL", "BST1 Switch", "BST1"}, + + {"RECMIXR", "INR1 Switch", "INR1 VOL"}, + {"RECMIXR", "BST2 Switch", "BST2"}, + {"RECMIXR", "BST1 Switch", "BST1"}, + + {"ADC L", NULL, "RECMIXL"}, + {"ADC R", NULL, "RECMIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "ADC L"}, + {"Stereo1 ADC MIXL", NULL, "stereo1 filter"}, + {"stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "ADC R"}, + {"Stereo1 ADC MIXR", NULL, "stereo1 filter"}, + {"stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll}, + + {"IF1 ADC1", NULL, "Stereo1 ADC MIXL"}, + {"IF1 ADC1", NULL, "Stereo1 ADC MIXR"}, + {"IF1 ADC1", NULL, "I2S1"}, + + {"AIF1TX", NULL, "IF1 ADC1"}, + + {"IF1 DAC", NULL, "AIF1RX"}, + {"IF1 DAC", NULL, "I2S1"}, + + {"IF1 DAC1 L", NULL, "IF1 DAC"}, + {"IF1 DAC1 R", NULL, "IF1 DAC"}, + + {"DAC MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC MIXL", "INF1 Switch", "IF1 DAC1 L"}, + {"DAC MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC MIXR", "INF1 Switch", "IF1 DAC1 R"}, + + {"Audio DSP", NULL, "DAC MIXL"}, + {"Audio DSP", NULL, "DAC MIXR"}, + + {"Stereo DAC MIXL", "DAC L1 Switch", "Audio DSP"}, + {"Stereo DAC MIXL", "DAC R1 Switch", "DAC MIXR"}, + {"Stereo DAC MIXL", NULL, "Stero1 DAC Power"}, + {"Stereo DAC MIXR", "DAC R1 Switch", "Audio DSP"}, + {"Stereo DAC MIXR", "DAC L1 Switch", "DAC MIXL"}, + {"Stereo DAC MIXR", NULL, "Stero1 DAC Power"}, + + {"DAC L1", NULL, "Stereo DAC MIXL"}, + {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll}, + {"DAC R1", NULL, "Stereo DAC MIXR"}, + {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll}, + + {"OUT MIXL", "BST1 Switch", "BST1"}, + {"OUT MIXL", "BST2 Switch", "BST2"}, + {"OUT MIXL", "INL1 Switch", "INL1 VOL"}, + {"OUT MIXL", "REC MIXL Switch", "RECMIXL"}, + {"OUT MIXL", "DAC L1 Switch", "DAC L1"}, + + {"OUT MIXR", "BST2 Switch", "BST2"}, + {"OUT MIXR", "BST1 Switch", "BST1"}, + {"OUT MIXR", "INR1 Switch", "INR1 VOL"}, + {"OUT MIXR", "REC MIXR Switch", "RECMIXR"}, + {"OUT MIXR", "DAC R1 Switch", "DAC R1"}, + + {"HPOVOL L", NULL, "OUT MIXL"}, + {"HPOVOL R", NULL, "OUT MIXR"}, + {"OUTVOL L", NULL, "OUT MIXL"}, + {"OUTVOL R", NULL, "OUT MIXR"}, + + {"DAC 1", NULL, "DAC L1"}, + {"DAC 1", NULL, "DAC R1"}, + {"HPOVOL", NULL, "HPOVOL L"}, + {"HPOVOL", NULL, "HPOVOL R"}, + {"HPO MIX", "DAC1 Switch", "DAC 1"}, + {"HPO MIX", "HPVOL Switch", "HPOVOL"}, + + {"LOUT MIX", "DAC L1 Switch", "DAC L1"}, + {"LOUT MIX", "DAC R1 Switch", "DAC R1"}, + {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"}, + {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"}, + + {"HP amp", NULL, "HPO MIX"}, + {"HP amp", NULL, "Charge Pump"}, + {"HPOL", NULL, "HP amp"}, + {"HPOR", NULL, "HP amp"}, + + {"LOUT amp", NULL, "LOUT MIX"}, + {"LOUT amp", NULL, "Charge Pump"}, + {"LOUTL", NULL, "LOUT amp"}, + {"LOUTR", NULL, "LOUT amp"}, + +}; + +static int rt5616_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt5616->lrck[dai->id] = params_rate(params); + + pre_div = rl6231_get_clk_info(rt5616->sysclk, rt5616->lrck[dai->id]); + + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + bclk_ms = frame_size > 32 ? 1 : 0; + rt5616->bclk[dai->id] = rt5616->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5616->bclk[dai->id], rt5616->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val_len |= RT5616_I2S_DL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val_len |= RT5616_I2S_DL_24; + break; + case SNDRV_PCM_FORMAT_S8: + val_len |= RT5616_I2S_DL_8; + break; + default: + return -EINVAL; + } + + mask_clk = RT5616_I2S_PD1_MASK; + val_clk = pre_div << RT5616_I2S_PD1_SFT; + snd_soc_component_update_bits(component, RT5616_I2S1_SDP, + RT5616_I2S_DL_MASK, val_len); + snd_soc_component_update_bits(component, RT5616_ADDA_CLK1, mask_clk, val_clk); + + return 0; +} + +static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5616->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5616_I2S_MS_S; + rt5616->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5616_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5616_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5616_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5616_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5616_I2S1_SDP, + RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK | + RT5616_I2S_DF_MASK, reg_val); + + return 0; +} + +static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt5616->sysclk && clk_id == rt5616->sysclk_src) + return 0; + + switch (clk_id) { + case RT5616_SCLK_S_MCLK: + reg_val |= RT5616_SCLK_SRC_MCLK; + break; + case RT5616_SCLK_S_PLL1: + reg_val |= RT5616_SCLK_SRC_PLL1; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5616_GLB_CLK, + RT5616_SCLK_SRC_MASK, reg_val); + rt5616->sysclk = freq; + rt5616->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5616->pll_src && freq_in == rt5616->pll_in && + freq_out == rt5616->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5616->pll_in = 0; + rt5616->pll_out = 0; + snd_soc_component_update_bits(component, RT5616_GLB_CLK, + RT5616_SCLK_SRC_MASK, + RT5616_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5616_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5616_GLB_CLK, + RT5616_PLL1_SRC_MASK, + RT5616_PLL1_SRC_MCLK); + break; + case RT5616_PLL1_S_BCLK1: + case RT5616_PLL1_S_BCLK2: + snd_soc_component_update_bits(component, RT5616_GLB_CLK, + RT5616_PLL1_SRC_MASK, + RT5616_PLL1_SRC_BCLK1); + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5616_PLL_CTRL1, + pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5616_PLL_CTRL2, + (pll_code.m_bp ? 0 : pll_code.m_code) << + RT5616_PLL_M_SFT | + pll_code.m_bp << RT5616_PLL_M_BP_SFT); + + rt5616->pll_in = freq_in; + rt5616->pll_out = freq_out; + rt5616->pll_src = source; + + return 0; +} + +static int rt5616_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (IS_ERR(rt5616->mclk)) + break; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(rt5616->mclk); + } else { + ret = clk_prepare_enable(rt5616->mclk); + if (ret) + return ret; + } + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + snd_soc_component_update_bits(component, RT5616_PWR_ANLG1, + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2, + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2); + mdelay(10); + snd_soc_component_update_bits(component, RT5616_PWR_ANLG1, + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); + snd_soc_component_update_bits(component, RT5616_D_MISC, + RT5616_D_GATE_EN, + RT5616_D_GATE_EN); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, RT5616_D_MISC, RT5616_D_GATE_EN, 0); + snd_soc_component_write(component, RT5616_PWR_DIG1, 0x0000); + snd_soc_component_write(component, RT5616_PWR_DIG2, 0x0000); + snd_soc_component_write(component, RT5616_PWR_VOL, 0x0000); + snd_soc_component_write(component, RT5616_PWR_MIXER, 0x0000); + snd_soc_component_write(component, RT5616_PWR_ANLG1, 0x0000); + snd_soc_component_write(component, RT5616_PWR_ANLG2, 0x0000); + break; + + default: + break; + } + + return 0; +} + +static int rt5616_probe(struct snd_soc_component *component) +{ + struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); + + /* Check if MCLK provided */ + rt5616->mclk = devm_clk_get(component->dev, "mclk"); + if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + rt5616->component = component; + + return 0; +} + +#ifdef CONFIG_PM +static int rt5616_suspend(struct snd_soc_component *component) +{ + struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5616->regmap, true); + regcache_mark_dirty(rt5616->regmap); + + return 0; +} + +static int rt5616_resume(struct snd_soc_component *component) +{ + struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5616->regmap, false); + regcache_sync(rt5616->regmap); + return 0; +} +#else +#define rt5616_suspend NULL +#define rt5616_resume NULL +#endif + +#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5616_aif_dai_ops = { + .hw_params = rt5616_hw_params, + .set_fmt = rt5616_set_dai_fmt, + .set_sysclk = rt5616_set_dai_sysclk, + .set_pll = rt5616_set_dai_pll, +}; + +static struct snd_soc_dai_driver rt5616_dai[] = { + { + .name = "rt5616-aif1", + .id = RT5616_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5616_STEREO_RATES, + .formats = RT5616_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5616_STEREO_RATES, + .formats = RT5616_FORMATS, + }, + .ops = &rt5616_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5616 = { + .probe = rt5616_probe, + .suspend = rt5616_suspend, + .resume = rt5616_resume, + .set_bias_level = rt5616_set_bias_level, + .controls = rt5616_snd_controls, + .num_controls = ARRAY_SIZE(rt5616_snd_controls), + .dapm_widgets = rt5616_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5616_dapm_widgets), + .dapm_routes = rt5616_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5616_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5616_regmap = { + .reg_bits = 8, + .val_bits = 16, + .use_single_read = true, + .use_single_write = true, + .max_register = RT5616_DEVICE_ID + 1 + (ARRAY_SIZE(rt5616_ranges) * + RT5616_PR_SPACING), + .volatile_reg = rt5616_volatile_register, + .readable_reg = rt5616_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5616_reg, + .num_reg_defaults = ARRAY_SIZE(rt5616_reg), + .ranges = rt5616_ranges, + .num_ranges = ARRAY_SIZE(rt5616_ranges), +}; + +static const struct i2c_device_id rt5616_i2c_id[] = { + { "rt5616", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5616_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt5616_of_match[] = { + { .compatible = "realtek,rt5616", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5616_of_match); +#endif + +static int rt5616_i2c_probe(struct i2c_client *i2c) +{ + struct rt5616_priv *rt5616; + unsigned int val; + int ret; + + rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv), + GFP_KERNEL); + if (!rt5616) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5616); + + rt5616->regmap = devm_regmap_init_i2c(i2c, &rt5616_regmap); + if (IS_ERR(rt5616->regmap)) { + ret = PTR_ERR(rt5616->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5616->regmap, RT5616_DEVICE_ID, &val); + if (val != 0x6281) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5616\n", + val); + return -ENODEV; + } + regmap_write(rt5616->regmap, RT5616_RESET, 0); + regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2, + RT5616_PWR_VREF1 | RT5616_PWR_MB | + RT5616_PWR_BG | RT5616_PWR_VREF2); + mdelay(10); + regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, + RT5616_PWR_FV1 | RT5616_PWR_FV2, + RT5616_PWR_FV1 | RT5616_PWR_FV2); + + ret = regmap_register_patch(rt5616->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1, + RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5616, + rt5616_dai, ARRAY_SIZE(rt5616_dai)); +} + +static void rt5616_i2c_remove(struct i2c_client *i2c) +{} + +static void rt5616_i2c_shutdown(struct i2c_client *client) +{ + struct rt5616_priv *rt5616 = i2c_get_clientdata(client); + + regmap_write(rt5616->regmap, RT5616_HP_VOL, 0xc8c8); + regmap_write(rt5616->regmap, RT5616_LOUT_CTRL1, 0xc8c8); +} + +static struct i2c_driver rt5616_i2c_driver = { + .driver = { + .name = "rt5616", + .of_match_table = of_match_ptr(rt5616_of_match), + }, + .probe = rt5616_i2c_probe, + .remove = rt5616_i2c_remove, + .shutdown = rt5616_i2c_shutdown, + .id_table = rt5616_i2c_id, +}; +module_i2c_driver(rt5616_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5616 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt5616.h b/sound/soc/codecs/rt5616.h new file mode 100644 index 0000000000..ad9c5de905 --- /dev/null +++ b/sound/soc/codecs/rt5616.h @@ -0,0 +1,1816 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5616.h -- RT5616 ALSA SoC audio driver + * + * Copyright 2011 Realtek Microelectronics + * Author: Johnny Hsu + */ + +#ifndef __RT5616_H__ +#define __RT5616_H__ + +/* Info */ +#define RT5616_RESET 0x00 +#define RT5616_VERSION_ID 0xfd +#define RT5616_VENDOR_ID 0xfe +#define RT5616_DEVICE_ID 0xff +/* I/O - Output */ +#define RT5616_HP_VOL 0x02 +#define RT5616_LOUT_CTRL1 0x03 +#define RT5616_LOUT_CTRL2 0x05 +/* I/O - Input */ +#define RT5616_IN1_IN2 0x0d +#define RT5616_INL1_INR1_VOL 0x0f +/* I/O - ADC/DAC/DMIC */ +#define RT5616_DAC1_DIG_VOL 0x19 +#define RT5616_ADC_DIG_VOL 0x1c +#define RT5616_ADC_BST_VOL 0x1e +/* Mixer - D-D */ +#define RT5616_STO1_ADC_MIXER 0x27 +#define RT5616_AD_DA_MIXER 0x29 +#define RT5616_STO_DAC_MIXER 0x2a + +/* Mixer - ADC */ +#define RT5616_REC_L1_MIXER 0x3b +#define RT5616_REC_L2_MIXER 0x3c +#define RT5616_REC_R1_MIXER 0x3d +#define RT5616_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5616_HPO_MIXER 0x45 +#define RT5616_OUT_L1_MIXER 0x4d +#define RT5616_OUT_L2_MIXER 0x4e +#define RT5616_OUT_L3_MIXER 0x4f +#define RT5616_OUT_R1_MIXER 0x50 +#define RT5616_OUT_R2_MIXER 0x51 +#define RT5616_OUT_R3_MIXER 0x52 +#define RT5616_LOUT_MIXER 0x53 +/* Power */ +#define RT5616_PWR_DIG1 0x61 +#define RT5616_PWR_DIG2 0x62 +#define RT5616_PWR_ANLG1 0x63 +#define RT5616_PWR_ANLG2 0x64 +#define RT5616_PWR_MIXER 0x65 +#define RT5616_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5616_PRIV_INDEX 0x6a +#define RT5616_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5616_I2S1_SDP 0x70 +#define RT5616_ADDA_CLK1 0x73 +#define RT5616_ADDA_CLK2 0x74 + +/* Function - Analog */ +#define RT5616_GLB_CLK 0x80 +#define RT5616_PLL_CTRL1 0x81 +#define RT5616_PLL_CTRL2 0x82 +#define RT5616_HP_OVCD 0x8b +#define RT5616_DEPOP_M1 0x8e +#define RT5616_DEPOP_M2 0x8f +#define RT5616_DEPOP_M3 0x90 +#define RT5616_CHARGE_PUMP 0x91 +#define RT5616_PV_DET_SPK_G 0x92 +#define RT5616_MICBIAS 0x93 +#define RT5616_A_JD_CTL1 0x94 +#define RT5616_A_JD_CTL2 0x95 +/* Function - Digital */ +#define RT5616_EQ_CTRL1 0xb0 +#define RT5616_EQ_CTRL2 0xb1 +#define RT5616_WIND_FILTER 0xb2 +#define RT5616_DRC_AGC_1 0xb4 +#define RT5616_DRC_AGC_2 0xb5 +#define RT5616_DRC_AGC_3 0xb6 +#define RT5616_SVOL_ZC 0xb7 +#define RT5616_JD_CTRL1 0xbb +#define RT5616_JD_CTRL2 0xbc +#define RT5616_IRQ_CTRL1 0xbd +#define RT5616_IRQ_CTRL2 0xbe +#define RT5616_INT_IRQ_ST 0xbf +#define RT5616_GPIO_CTRL1 0xc0 +#define RT5616_GPIO_CTRL2 0xc1 +#define RT5616_GPIO_CTRL3 0xc2 +#define RT5616_PGM_REG_ARR1 0xc8 +#define RT5616_PGM_REG_ARR2 0xc9 +#define RT5616_PGM_REG_ARR3 0xca +#define RT5616_PGM_REG_ARR4 0xcb +#define RT5616_PGM_REG_ARR5 0xcc +#define RT5616_SCB_FUNC 0xcd +#define RT5616_SCB_CTRL 0xce +#define RT5616_BASE_BACK 0xcf +#define RT5616_MP3_PLUS1 0xd0 +#define RT5616_MP3_PLUS2 0xd1 +#define RT5616_ADJ_HPF_CTRL1 0xd3 +#define RT5616_ADJ_HPF_CTRL2 0xd4 +#define RT5616_HP_CALIB_AMP_DET 0xd6 +#define RT5616_HP_CALIB2 0xd7 +#define RT5616_SV_ZCD1 0xd9 +#define RT5616_SV_ZCD2 0xda +#define RT5616_D_MISC 0xfa +/* Dummy Register */ +#define RT5616_DUMMY2 0xfb +#define RT5616_DUMMY3 0xfc + + +/* Index of Codec Private Register definition */ +#define RT5616_BIAS_CUR1 0x12 +#define RT5616_BIAS_CUR3 0x14 +#define RT5616_CLSD_INT_REG1 0x1c +#define RT5616_MAMP_INT_REG2 0x37 +#define RT5616_CHOP_DAC_ADC 0x3d +#define RT5616_3D_SPK 0x63 +#define RT5616_WND_1 0x6c +#define RT5616_WND_2 0x6d +#define RT5616_WND_3 0x6e +#define RT5616_WND_4 0x6f +#define RT5616_WND_5 0x70 +#define RT5616_WND_8 0x73 +#define RT5616_DIP_SPK_INF 0x75 +#define RT5616_HP_DCC_INT1 0x77 +#define RT5616_EQ_BW_LOP 0xa0 +#define RT5616_EQ_GN_LOP 0xa1 +#define RT5616_EQ_FC_BP1 0xa2 +#define RT5616_EQ_BW_BP1 0xa3 +#define RT5616_EQ_GN_BP1 0xa4 +#define RT5616_EQ_FC_BP2 0xa5 +#define RT5616_EQ_BW_BP2 0xa6 +#define RT5616_EQ_GN_BP2 0xa7 +#define RT5616_EQ_FC_BP3 0xa8 +#define RT5616_EQ_BW_BP3 0xa9 +#define RT5616_EQ_GN_BP3 0xaa +#define RT5616_EQ_FC_BP4 0xab +#define RT5616_EQ_BW_BP4 0xac +#define RT5616_EQ_GN_BP4 0xad +#define RT5616_EQ_FC_HIP1 0xae +#define RT5616_EQ_GN_HIP1 0xaf +#define RT5616_EQ_FC_HIP2 0xb0 +#define RT5616_EQ_BW_HIP2 0xb1 +#define RT5616_EQ_GN_HIP2 0xb2 +#define RT5616_EQ_PRE_VOL 0xb3 +#define RT5616_EQ_PST_VOL 0xb4 + + +/* global definition */ +#define RT5616_L_MUTE (0x1 << 15) +#define RT5616_L_MUTE_SFT 15 +#define RT5616_VOL_L_MUTE (0x1 << 14) +#define RT5616_VOL_L_SFT 14 +#define RT5616_R_MUTE (0x1 << 7) +#define RT5616_R_MUTE_SFT 7 +#define RT5616_VOL_R_MUTE (0x1 << 6) +#define RT5616_VOL_R_SFT 6 +#define RT5616_L_VOL_MASK (0x3f << 8) +#define RT5616_L_VOL_SFT 8 +#define RT5616_R_VOL_MASK (0x3f) +#define RT5616_R_VOL_SFT 0 + +/* LOUT Control 2(0x05) */ +#define RT5616_EN_DFO (0x1 << 15) + +/* IN1 and IN2 Control (0x0d) */ +/* IN3 and IN4 Control (0x0e) */ +#define RT5616_BST_MASK1 (0xf<<12) +#define RT5616_BST_SFT1 12 +#define RT5616_BST_MASK2 (0xf<<8) +#define RT5616_BST_SFT2 8 +#define RT5616_IN_DF1 (0x1 << 7) +#define RT5616_IN_SFT1 7 +#define RT5616_IN_DF2 (0x1 << 6) +#define RT5616_IN_SFT2 6 + +/* INL1 and INR1 Volume Control (0x0f) */ +#define RT5616_INL_VOL_MASK (0x1f << 8) +#define RT5616_INL_VOL_SFT 8 +#define RT5616_INR_SEL_MASK (0x1 << 7) +#define RT5616_INR_SEL_SFT 7 +#define RT5616_INR_SEL_IN4N (0x0 << 7) +#define RT5616_INR_SEL_MONON (0x1 << 7) +#define RT5616_INR_VOL_MASK (0x1f) +#define RT5616_INR_VOL_SFT 0 + +/* DAC1 Digital Volume (0x19) */ +#define RT5616_DAC_L1_VOL_MASK (0xff << 8) +#define RT5616_DAC_L1_VOL_SFT 8 +#define RT5616_DAC_R1_VOL_MASK (0xff) +#define RT5616_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x1a) */ +#define RT5616_DAC_L2_VOL_MASK (0xff << 8) +#define RT5616_DAC_L2_VOL_SFT 8 +#define RT5616_DAC_R2_VOL_MASK (0xff) +#define RT5616_DAC_R2_VOL_SFT 0 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5616_ADC_L_VOL_MASK (0x7f << 8) +#define RT5616_ADC_L_VOL_SFT 8 +#define RT5616_ADC_R_VOL_MASK (0x7f) +#define RT5616_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x1d) */ +#define RT5616_M_MONO_ADC_L (0x1 << 15) +#define RT5616_M_MONO_ADC_L_SFT 15 +#define RT5616_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5616_MONO_ADC_L_VOL_SFT 8 +#define RT5616_M_MONO_ADC_R (0x1 << 7) +#define RT5616_M_MONO_ADC_R_SFT 7 +#define RT5616_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5616_MONO_ADC_R_VOL_SFT 0 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5616_ADC_L_BST_MASK (0x3 << 14) +#define RT5616_ADC_L_BST_SFT 14 +#define RT5616_ADC_R_BST_MASK (0x3 << 12) +#define RT5616_ADC_R_BST_SFT 12 +#define RT5616_ADC_COMP_MASK (0x3 << 10) +#define RT5616_ADC_COMP_SFT 10 + +/* Stereo ADC1 Mixer Control (0x27) */ +#define RT5616_M_STO1_ADC_L1 (0x1 << 14) +#define RT5616_M_STO1_ADC_L1_SFT 14 +#define RT5616_M_STO1_ADC_R1 (0x1 << 6) +#define RT5616_M_STO1_ADC_R1_SFT 6 + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5616_M_ADCMIX_L (0x1 << 15) +#define RT5616_M_ADCMIX_L_SFT 15 +#define RT5616_M_IF1_DAC_L (0x1 << 14) +#define RT5616_M_IF1_DAC_L_SFT 14 +#define RT5616_M_ADCMIX_R (0x1 << 7) +#define RT5616_M_ADCMIX_R_SFT 7 +#define RT5616_M_IF1_DAC_R (0x1 << 6) +#define RT5616_M_IF1_DAC_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5616_M_DAC_L1_MIXL (0x1 << 14) +#define RT5616_M_DAC_L1_MIXL_SFT 14 +#define RT5616_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5616_DAC_L1_STO_L_VOL_SFT 13 +#define RT5616_M_DAC_R1_MIXL (0x1 << 9) +#define RT5616_M_DAC_R1_MIXL_SFT 9 +#define RT5616_DAC_R1_STO_L_VOL_MASK (0x1 << 8) +#define RT5616_DAC_R1_STO_L_VOL_SFT 8 +#define RT5616_M_DAC_R1_MIXR (0x1 << 6) +#define RT5616_M_DAC_R1_MIXR_SFT 6 +#define RT5616_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5616_DAC_R1_STO_R_VOL_SFT 5 +#define RT5616_M_DAC_L1_MIXR (0x1 << 1) +#define RT5616_M_DAC_L1_MIXR_SFT 1 +#define RT5616_DAC_L1_STO_R_VOL_MASK (0x1) +#define RT5616_DAC_L1_STO_R_VOL_SFT 0 + +/* DD Mixer Control (0x2b) */ +#define RT5616_M_STO_DD_L1 (0x1 << 14) +#define RT5616_M_STO_DD_L1_SFT 14 +#define RT5616_STO_DD_L1_VOL_MASK (0x1 << 13) +#define RT5616_DAC_DD_L1_VOL_SFT 13 +#define RT5616_M_STO_DD_L2 (0x1 << 12) +#define RT5616_M_STO_DD_L2_SFT 12 +#define RT5616_STO_DD_L2_VOL_MASK (0x1 << 11) +#define RT5616_STO_DD_L2_VOL_SFT 11 +#define RT5616_M_STO_DD_R2_L (0x1 << 10) +#define RT5616_M_STO_DD_R2_L_SFT 10 +#define RT5616_STO_DD_R2_L_VOL_MASK (0x1 << 9) +#define RT5616_STO_DD_R2_L_VOL_SFT 9 +#define RT5616_M_STO_DD_R1 (0x1 << 6) +#define RT5616_M_STO_DD_R1_SFT 6 +#define RT5616_STO_DD_R1_VOL_MASK (0x1 << 5) +#define RT5616_STO_DD_R1_VOL_SFT 5 +#define RT5616_M_STO_DD_R2 (0x1 << 4) +#define RT5616_M_STO_DD_R2_SFT 4 +#define RT5616_STO_DD_R2_VOL_MASK (0x1 << 3) +#define RT5616_STO_DD_R2_VOL_SFT 3 +#define RT5616_M_STO_DD_L2_R (0x1 << 2) +#define RT5616_M_STO_DD_L2_R_SFT 2 +#define RT5616_STO_DD_L2_R_VOL_MASK (0x1 << 1) +#define RT5616_STO_DD_L2_R_VOL_SFT 1 + +/* Digital Mixer Control (0x2c) */ +#define RT5616_M_STO_L_DAC_L (0x1 << 15) +#define RT5616_M_STO_L_DAC_L_SFT 15 +#define RT5616_STO_L_DAC_L_VOL_MASK (0x1 << 14) +#define RT5616_STO_L_DAC_L_VOL_SFT 14 +#define RT5616_M_DAC_L2_DAC_L (0x1 << 13) +#define RT5616_M_DAC_L2_DAC_L_SFT 13 +#define RT5616_DAC_L2_DAC_L_VOL_MASK (0x1 << 12) +#define RT5616_DAC_L2_DAC_L_VOL_SFT 12 +#define RT5616_M_STO_R_DAC_R (0x1 << 11) +#define RT5616_M_STO_R_DAC_R_SFT 11 +#define RT5616_STO_R_DAC_R_VOL_MASK (0x1 << 10) +#define RT5616_STO_R_DAC_R_VOL_SFT 10 +#define RT5616_M_DAC_R2_DAC_R (0x1 << 9) +#define RT5616_M_DAC_R2_DAC_R_SFT 9 +#define RT5616_DAC_R2_DAC_R_VOL_MASK (0x1 << 8) +#define RT5616_DAC_R2_DAC_R_VOL_SFT 8 + +/* DSP Path Control 1 (0x2d) */ +#define RT5616_RXDP_SRC_MASK (0x1 << 15) +#define RT5616_RXDP_SRC_SFT 15 +#define RT5616_RXDP_SRC_NOR (0x0 << 15) +#define RT5616_RXDP_SRC_DIV3 (0x1 << 15) +#define RT5616_TXDP_SRC_MASK (0x1 << 14) +#define RT5616_TXDP_SRC_SFT 14 +#define RT5616_TXDP_SRC_NOR (0x0 << 14) +#define RT5616_TXDP_SRC_DIV3 (0x1 << 14) + +/* DSP Path Control 2 (0x2e) */ +#define RT5616_DAC_L2_SEL_MASK (0x3 << 14) +#define RT5616_DAC_L2_SEL_SFT 14 +#define RT5616_DAC_L2_SEL_IF2 (0x0 << 14) +#define RT5616_DAC_L2_SEL_IF3 (0x1 << 14) +#define RT5616_DAC_L2_SEL_TXDC (0x2 << 14) +#define RT5616_DAC_L2_SEL_BASS (0x3 << 14) +#define RT5616_DAC_R2_SEL_MASK (0x3 << 12) +#define RT5616_DAC_R2_SEL_SFT 12 +#define RT5616_DAC_R2_SEL_IF2 (0x0 << 12) +#define RT5616_DAC_R2_SEL_IF3 (0x1 << 12) +#define RT5616_DAC_R2_SEL_TXDC (0x2 << 12) +#define RT5616_IF2_ADC_L_SEL_MASK (0x1 << 11) +#define RT5616_IF2_ADC_L_SEL_SFT 11 +#define RT5616_IF2_ADC_L_SEL_TXDP (0x0 << 11) +#define RT5616_IF2_ADC_L_SEL_PASS (0x1 << 11) +#define RT5616_IF2_ADC_R_SEL_MASK (0x1 << 10) +#define RT5616_IF2_ADC_R_SEL_SFT 10 +#define RT5616_IF2_ADC_R_SEL_TXDP (0x0 << 10) +#define RT5616_IF2_ADC_R_SEL_PASS (0x1 << 10) +#define RT5616_RXDC_SEL_MASK (0x3 << 8) +#define RT5616_RXDC_SEL_SFT 8 +#define RT5616_RXDC_SEL_NOR (0x0 << 8) +#define RT5616_RXDC_SEL_L2R (0x1 << 8) +#define RT5616_RXDC_SEL_R2L (0x2 << 8) +#define RT5616_RXDC_SEL_SWAP (0x3 << 8) +#define RT5616_RXDP_SEL_MASK (0x3 << 6) +#define RT5616_RXDP_SEL_SFT 6 +#define RT5616_RXDP_SEL_NOR (0x0 << 6) +#define RT5616_RXDP_SEL_L2R (0x1 << 6) +#define RT5616_RXDP_SEL_R2L (0x2 << 6) +#define RT5616_RXDP_SEL_SWAP (0x3 << 6) +#define RT5616_TXDC_SEL_MASK (0x3 << 4) +#define RT5616_TXDC_SEL_SFT 4 +#define RT5616_TXDC_SEL_NOR (0x0 << 4) +#define RT5616_TXDC_SEL_L2R (0x1 << 4) +#define RT5616_TXDC_SEL_R2L (0x2 << 4) +#define RT5616_TXDC_SEL_SWAP (0x3 << 4) +#define RT5616_TXDP_SEL_MASK (0x3 << 2) +#define RT5616_TXDP_SEL_SFT 2 +#define RT5616_TXDP_SEL_NOR (0x0 << 2) +#define RT5616_TXDP_SEL_L2R (0x1 << 2) +#define RT5616_TXDP_SEL_R2L (0x2 << 2) +#define RT5616_TRXDP_SEL_SWAP (0x3 << 2) + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5616_G_LN_L2_RM_L_MASK (0x7 << 13) +#define RT5616_G_IN_L2_RM_L_SFT 13 +#define RT5616_G_LN_L1_RM_L_MASK (0x7 << 10) +#define RT5616_G_IN_L1_RM_L_SFT 10 +#define RT5616_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5616_G_BST3_RM_L_SFT 4 +#define RT5616_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5616_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5616_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5616_G_BST1_RM_L_SFT 13 +#define RT5616_G_OM_L_RM_L_MASK (0x7 << 10) +#define RT5616_G_OM_L_RM_L_SFT 10 +#define RT5616_M_IN2_L_RM_L (0x1 << 6) +#define RT5616_M_IN2_L_RM_L_SFT 6 +#define RT5616_M_IN1_L_RM_L (0x1 << 5) +#define RT5616_M_IN1_L_RM_L_SFT 5 +#define RT5616_M_BST3_RM_L (0x1 << 3) +#define RT5616_M_BST3_RM_L_SFT 3 +#define RT5616_M_BST2_RM_L (0x1 << 2) +#define RT5616_M_BST2_RM_L_SFT 2 +#define RT5616_M_BST1_RM_L (0x1 << 1) +#define RT5616_M_BST1_RM_L_SFT 1 +#define RT5616_M_OM_L_RM_L (0x1) +#define RT5616_M_OM_L_RM_L_SFT 0 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5616_G_IN2_R_RM_R_MASK (0x7 << 13) +#define RT5616_G_IN2_R_RM_R_SFT 13 +#define RT5616_G_IN1_R_RM_R_MASK (0x7 << 10) +#define RT5616_G_IN1_R_RM_R_SFT 10 +#define RT5616_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5616_G_BST3_RM_R_SFT 4 +#define RT5616_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5616_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5616_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5616_G_BST1_RM_R_SFT 13 +#define RT5616_G_OM_R_RM_R_MASK (0x7 << 10) +#define RT5616_G_OM_R_RM_R_SFT 10 +#define RT5616_M_IN2_R_RM_R (0x1 << 6) +#define RT5616_M_IN2_R_RM_R_SFT 6 +#define RT5616_M_IN1_R_RM_R (0x1 << 5) +#define RT5616_M_IN1_R_RM_R_SFT 5 +#define RT5616_M_BST3_RM_R (0x1 << 3) +#define RT5616_M_BST3_RM_R_SFT 3 +#define RT5616_M_BST2_RM_R (0x1 << 2) +#define RT5616_M_BST2_RM_R_SFT 2 +#define RT5616_M_BST1_RM_R (0x1 << 1) +#define RT5616_M_BST1_RM_R_SFT 1 +#define RT5616_M_OM_R_RM_R (0x1) +#define RT5616_M_OM_R_RM_R_SFT 0 + +/* HPMIX Control (0x45) */ +#define RT5616_M_DAC1_HM (0x1 << 14) +#define RT5616_M_DAC1_HM_SFT 14 +#define RT5616_M_HPVOL_HM (0x1 << 13) +#define RT5616_M_HPVOL_HM_SFT 13 +#define RT5616_G_HPOMIX_MASK (0x1 << 12) +#define RT5616_G_HPOMIX_SFT 12 + +/* SPK Left Mixer Control (0x46) */ +#define RT5616_G_RM_L_SM_L_MASK (0x3 << 14) +#define RT5616_G_RM_L_SM_L_SFT 14 +#define RT5616_G_IN_L_SM_L_MASK (0x3 << 12) +#define RT5616_G_IN_L_SM_L_SFT 12 +#define RT5616_G_DAC_L1_SM_L_MASK (0x3 << 10) +#define RT5616_G_DAC_L1_SM_L_SFT 10 +#define RT5616_G_DAC_L2_SM_L_MASK (0x3 << 8) +#define RT5616_G_DAC_L2_SM_L_SFT 8 +#define RT5616_G_OM_L_SM_L_MASK (0x3 << 6) +#define RT5616_G_OM_L_SM_L_SFT 6 +#define RT5616_M_RM_L_SM_L (0x1 << 5) +#define RT5616_M_RM_L_SM_L_SFT 5 +#define RT5616_M_IN_L_SM_L (0x1 << 4) +#define RT5616_M_IN_L_SM_L_SFT 4 +#define RT5616_M_DAC_L1_SM_L (0x1 << 3) +#define RT5616_M_DAC_L1_SM_L_SFT 3 +#define RT5616_M_DAC_L2_SM_L (0x1 << 2) +#define RT5616_M_DAC_L2_SM_L_SFT 2 +#define RT5616_M_OM_L_SM_L (0x1 << 1) +#define RT5616_M_OM_L_SM_L_SFT 1 + +/* SPK Right Mixer Control (0x47) */ +#define RT5616_G_RM_R_SM_R_MASK (0x3 << 14) +#define RT5616_G_RM_R_SM_R_SFT 14 +#define RT5616_G_IN_R_SM_R_MASK (0x3 << 12) +#define RT5616_G_IN_R_SM_R_SFT 12 +#define RT5616_G_DAC_R1_SM_R_MASK (0x3 << 10) +#define RT5616_G_DAC_R1_SM_R_SFT 10 +#define RT5616_G_DAC_R2_SM_R_MASK (0x3 << 8) +#define RT5616_G_DAC_R2_SM_R_SFT 8 +#define RT5616_G_OM_R_SM_R_MASK (0x3 << 6) +#define RT5616_G_OM_R_SM_R_SFT 6 +#define RT5616_M_RM_R_SM_R (0x1 << 5) +#define RT5616_M_RM_R_SM_R_SFT 5 +#define RT5616_M_IN_R_SM_R (0x1 << 4) +#define RT5616_M_IN_R_SM_R_SFT 4 +#define RT5616_M_DAC_R1_SM_R (0x1 << 3) +#define RT5616_M_DAC_R1_SM_R_SFT 3 +#define RT5616_M_DAC_R2_SM_R (0x1 << 2) +#define RT5616_M_DAC_R2_SM_R_SFT 2 +#define RT5616_M_OM_R_SM_R (0x1 << 1) +#define RT5616_M_OM_R_SM_R_SFT 1 + +/* SPOLMIX Control (0x48) */ +#define RT5616_M_DAC_R1_SPM_L (0x1 << 15) +#define RT5616_M_DAC_R1_SPM_L_SFT 15 +#define RT5616_M_DAC_L1_SPM_L (0x1 << 14) +#define RT5616_M_DAC_L1_SPM_L_SFT 14 +#define RT5616_M_SV_R_SPM_L (0x1 << 13) +#define RT5616_M_SV_R_SPM_L_SFT 13 +#define RT5616_M_SV_L_SPM_L (0x1 << 12) +#define RT5616_M_SV_L_SPM_L_SFT 12 +#define RT5616_M_BST1_SPM_L (0x1 << 11) +#define RT5616_M_BST1_SPM_L_SFT 11 + +/* SPORMIX Control (0x49) */ +#define RT5616_M_DAC_R1_SPM_R (0x1 << 13) +#define RT5616_M_DAC_R1_SPM_R_SFT 13 +#define RT5616_M_SV_R_SPM_R (0x1 << 12) +#define RT5616_M_SV_R_SPM_R_SFT 12 +#define RT5616_M_BST1_SPM_R (0x1 << 11) +#define RT5616_M_BST1_SPM_R_SFT 11 + +/* SPOLMIX / SPORMIX Ratio Control (0x4a) */ +#define RT5616_SPO_CLSD_RATIO_MASK (0x7) +#define RT5616_SPO_CLSD_RATIO_SFT 0 + +/* Mono Output Mixer Control (0x4c) */ +#define RT5616_M_DAC_R2_MM (0x1 << 15) +#define RT5616_M_DAC_R2_MM_SFT 15 +#define RT5616_M_DAC_L2_MM (0x1 << 14) +#define RT5616_M_DAC_L2_MM_SFT 14 +#define RT5616_M_OV_R_MM (0x1 << 13) +#define RT5616_M_OV_R_MM_SFT 13 +#define RT5616_M_OV_L_MM (0x1 << 12) +#define RT5616_M_OV_L_MM_SFT 12 +#define RT5616_M_BST1_MM (0x1 << 11) +#define RT5616_M_BST1_MM_SFT 11 +#define RT5616_G_MONOMIX_MASK (0x1 << 10) +#define RT5616_G_MONOMIX_SFT 10 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5616_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5616_G_BST2_OM_L_SFT 10 +#define RT5616_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5616_G_BST1_OM_L_SFT 7 +#define RT5616_G_IN1_L_OM_L_MASK (0x7 << 4) +#define RT5616_G_IN1_L_OM_L_SFT 4 +#define RT5616_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5616_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5616_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5616_G_DAC_L1_OM_L_SFT 7 +#define RT5616_G_IN2_L_OM_L_MASK (0x7 << 4) +#define RT5616_G_IN2_L_OM_L_SFT 4 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5616_M_IN2_L_OM_L (0x1 << 9) +#define RT5616_M_IN2_L_OM_L_SFT 9 +#define RT5616_M_BST2_OM_L (0x1 << 6) +#define RT5616_M_BST2_OM_L_SFT 6 +#define RT5616_M_BST1_OM_L (0x1 << 5) +#define RT5616_M_BST1_OM_L_SFT 5 +#define RT5616_M_IN1_L_OM_L (0x1 << 4) +#define RT5616_M_IN1_L_OM_L_SFT 4 +#define RT5616_M_RM_L_OM_L (0x1 << 3) +#define RT5616_M_RM_L_OM_L_SFT 3 +#define RT5616_M_DAC_L1_OM_L (0x1) +#define RT5616_M_DAC_L1_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5616_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5616_G_BST2_OM_R_SFT 10 +#define RT5616_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5616_G_BST1_OM_R_SFT 7 +#define RT5616_G_IN1_R_OM_R_MASK (0x7 << 4) +#define RT5616_G_IN1_R_OM_R_SFT 4 +#define RT5616_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5616_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5616_G_DAC_R1_OM_R_MASK (0x7 << 7) +#define RT5616_G_DAC_R1_OM_R_SFT 7 +#define RT5616_G_IN2_R_OM_R_MASK (0x7 << 4) +#define RT5616_G_IN2_R_OM_R_SFT 4 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5616_M_IN2_R_OM_R (0x1 << 9) +#define RT5616_M_IN2_R_OM_R_SFT 9 +#define RT5616_M_BST2_OM_R (0x1 << 6) +#define RT5616_M_BST2_OM_R_SFT 6 +#define RT5616_M_BST1_OM_R (0x1 << 5) +#define RT5616_M_BST1_OM_R_SFT 5 +#define RT5616_M_IN1_R_OM_R (0x1 << 4) +#define RT5616_M_IN1_R_OM_R_SFT 4 +#define RT5616_M_RM_R_OM_R (0x1 << 3) +#define RT5616_M_RM_R_OM_R_SFT 3 +#define RT5616_M_DAC_R1_OM_R (0x1) +#define RT5616_M_DAC_R1_OM_R_SFT 0 + +/* LOUT Mixer Control (0x53) */ +#define RT5616_M_DAC_L1_LM (0x1 << 15) +#define RT5616_M_DAC_L1_LM_SFT 15 +#define RT5616_M_DAC_R1_LM (0x1 << 14) +#define RT5616_M_DAC_R1_LM_SFT 14 +#define RT5616_M_OV_L_LM (0x1 << 13) +#define RT5616_M_OV_L_LM_SFT 13 +#define RT5616_M_OV_R_LM (0x1 << 12) +#define RT5616_M_OV_R_LM_SFT 12 +#define RT5616_G_LOUTMIX_MASK (0x1 << 11) +#define RT5616_G_LOUTMIX_SFT 11 + +/* Power Management for Digital 1 (0x61) */ +#define RT5616_PWR_I2S1 (0x1 << 15) +#define RT5616_PWR_I2S1_BIT 15 +#define RT5616_PWR_I2S2 (0x1 << 14) +#define RT5616_PWR_I2S2_BIT 14 +#define RT5616_PWR_DAC_L1 (0x1 << 12) +#define RT5616_PWR_DAC_L1_BIT 12 +#define RT5616_PWR_DAC_R1 (0x1 << 11) +#define RT5616_PWR_DAC_R1_BIT 11 +#define RT5616_PWR_ADC_L (0x1 << 2) +#define RT5616_PWR_ADC_L_BIT 2 +#define RT5616_PWR_ADC_R (0x1 << 1) +#define RT5616_PWR_ADC_R_BIT 1 + +/* Power Management for Digital 2 (0x62) */ +#define RT5616_PWR_ADC_STO1_F (0x1 << 15) +#define RT5616_PWR_ADC_STO1_F_BIT 15 +#define RT5616_PWR_DAC_STO1_F (0x1 << 11) +#define RT5616_PWR_DAC_STO1_F_BIT 11 + +/* Power Management for Analog 1 (0x63) */ +#define RT5616_PWR_VREF1 (0x1 << 15) +#define RT5616_PWR_VREF1_BIT 15 +#define RT5616_PWR_FV1 (0x1 << 14) +#define RT5616_PWR_FV1_BIT 14 +#define RT5616_PWR_MB (0x1 << 13) +#define RT5616_PWR_MB_BIT 13 +#define RT5616_PWR_LM (0x1 << 12) +#define RT5616_PWR_LM_BIT 12 +#define RT5616_PWR_BG (0x1 << 11) +#define RT5616_PWR_BG_BIT 11 +#define RT5616_PWR_HP_L (0x1 << 7) +#define RT5616_PWR_HP_L_BIT 7 +#define RT5616_PWR_HP_R (0x1 << 6) +#define RT5616_PWR_HP_R_BIT 6 +#define RT5616_PWR_HA (0x1 << 5) +#define RT5616_PWR_HA_BIT 5 +#define RT5616_PWR_VREF2 (0x1 << 4) +#define RT5616_PWR_VREF2_BIT 4 +#define RT5616_PWR_FV2 (0x1 << 3) +#define RT5616_PWR_FV2_BIT 3 +#define RT5616_PWR_LDO (0x1 << 2) +#define RT5616_PWR_LDO_BIT 2 +#define RT5616_PWR_LDO_DVO_MASK (0x3) +#define RT5616_PWR_LDO_DVO_1_0V 0 +#define RT5616_PWR_LDO_DVO_1_1V 1 +#define RT5616_PWR_LDO_DVO_1_2V 2 +#define RT5616_PWR_LDO_DVO_1_3V 3 + +/* Power Management for Analog 2 (0x64) */ +#define RT5616_PWR_BST1 (0x1 << 15) +#define RT5616_PWR_BST1_BIT 15 +#define RT5616_PWR_BST2 (0x1 << 14) +#define RT5616_PWR_BST2_BIT 14 +#define RT5616_PWR_MB1 (0x1 << 11) +#define RT5616_PWR_MB1_BIT 11 +#define RT5616_PWR_PLL (0x1 << 9) +#define RT5616_PWR_PLL_BIT 9 +#define RT5616_PWR_BST1_OP2 (0x1 << 5) +#define RT5616_PWR_BST1_OP2_BIT 5 +#define RT5616_PWR_BST2_OP2 (0x1 << 4) +#define RT5616_PWR_BST2_OP2_BIT 4 +#define RT5616_PWR_BST3_OP2 (0x1 << 3) +#define RT5616_PWR_BST3_OP2_BIT 3 +#define RT5616_PWR_JD_M (0x1 << 2) +#define RT5616_PWM_JD_M_BIT 2 +#define RT5616_PWR_JD2 (0x1 << 1) +#define RT5616_PWM_JD2_BIT 1 +#define RT5616_PWR_JD3 (0x1) +#define RT5616_PWM_JD3_BIT 0 + +/* Power Management for Mixer (0x65) */ +#define RT5616_PWR_OM_L (0x1 << 15) +#define RT5616_PWR_OM_L_BIT 15 +#define RT5616_PWR_OM_R (0x1 << 14) +#define RT5616_PWR_OM_R_BIT 14 +#define RT5616_PWR_RM_L (0x1 << 11) +#define RT5616_PWR_RM_L_BIT 11 +#define RT5616_PWR_RM_R (0x1 << 10) +#define RT5616_PWR_RM_R_BIT 10 + +/* Power Management for Volume (0x66) */ +#define RT5616_PWR_OV_L (0x1 << 13) +#define RT5616_PWR_OV_L_BIT 13 +#define RT5616_PWR_OV_R (0x1 << 12) +#define RT5616_PWR_OV_R_BIT 12 +#define RT5616_PWR_HV_L (0x1 << 11) +#define RT5616_PWR_HV_L_BIT 11 +#define RT5616_PWR_HV_R (0x1 << 10) +#define RT5616_PWR_HV_R_BIT 10 +#define RT5616_PWR_IN1_L (0x1 << 9) +#define RT5616_PWR_IN1_L_BIT 9 +#define RT5616_PWR_IN1_R (0x1 << 8) +#define RT5616_PWR_IN1_R_BIT 8 +#define RT5616_PWR_IN2_L (0x1 << 7) +#define RT5616_PWR_IN2_L_BIT 7 +#define RT5616_PWR_IN2_R (0x1 << 6) +#define RT5616_PWR_IN2_R_BIT 6 + +/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71) */ +#define RT5616_I2S_MS_MASK (0x1 << 15) +#define RT5616_I2S_MS_SFT 15 +#define RT5616_I2S_MS_M (0x0 << 15) +#define RT5616_I2S_MS_S (0x1 << 15) +#define RT5616_I2S_O_CP_MASK (0x3 << 10) +#define RT5616_I2S_O_CP_SFT 10 +#define RT5616_I2S_O_CP_OFF (0x0 << 10) +#define RT5616_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5616_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5616_I2S_I_CP_MASK (0x3 << 8) +#define RT5616_I2S_I_CP_SFT 8 +#define RT5616_I2S_I_CP_OFF (0x0 << 8) +#define RT5616_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5616_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5616_I2S_BP_MASK (0x1 << 7) +#define RT5616_I2S_BP_SFT 7 +#define RT5616_I2S_BP_NOR (0x0 << 7) +#define RT5616_I2S_BP_INV (0x1 << 7) +#define RT5616_I2S_DL_MASK (0x3 << 2) +#define RT5616_I2S_DL_SFT 2 +#define RT5616_I2S_DL_16 (0x0 << 2) +#define RT5616_I2S_DL_20 (0x1 << 2) +#define RT5616_I2S_DL_24 (0x2 << 2) +#define RT5616_I2S_DL_8 (0x3 << 2) +#define RT5616_I2S_DF_MASK (0x3) +#define RT5616_I2S_DF_SFT 0 +#define RT5616_I2S_DF_I2S (0x0) +#define RT5616_I2S_DF_LEFT (0x1) +#define RT5616_I2S_DF_PCM_A (0x2) +#define RT5616_I2S_DF_PCM_B (0x3) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5616_I2S_PD1_MASK (0x7 << 12) +#define RT5616_I2S_PD1_SFT 12 +#define RT5616_I2S_PD1_1 (0x0 << 12) +#define RT5616_I2S_PD1_2 (0x1 << 12) +#define RT5616_I2S_PD1_3 (0x2 << 12) +#define RT5616_I2S_PD1_4 (0x3 << 12) +#define RT5616_I2S_PD1_6 (0x4 << 12) +#define RT5616_I2S_PD1_8 (0x5 << 12) +#define RT5616_I2S_PD1_12 (0x6 << 12) +#define RT5616_I2S_PD1_16 (0x7 << 12) +#define RT5616_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5616_DAC_OSR_MASK (0x3 << 2) +#define RT5616_DAC_OSR_SFT 2 +#define RT5616_DAC_OSR_128 (0x0 << 2) +#define RT5616_DAC_OSR_64 (0x1 << 2) +#define RT5616_DAC_OSR_32 (0x2 << 2) +#define RT5616_DAC_OSR_128_3 (0x3 << 2) +#define RT5616_ADC_OSR_MASK (0x3) +#define RT5616_ADC_OSR_SFT 0 +#define RT5616_ADC_OSR_128 (0x0) +#define RT5616_ADC_OSR_64 (0x1) +#define RT5616_ADC_OSR_32 (0x2) +#define RT5616_ADC_OSR_128_3 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5616_DAHPF_EN (0x1 << 11) +#define RT5616_DAHPF_EN_SFT 11 +#define RT5616_ADHPF_EN (0x1 << 10) +#define RT5616_ADHPF_EN_SFT 10 + +/* TDM Control 1 (0x77) */ +#define RT5616_TDM_INTEL_SEL_MASK (0x1 << 15) +#define RT5616_TDM_INTEL_SEL_SFT 15 +#define RT5616_TDM_INTEL_SEL_64 (0x0 << 15) +#define RT5616_TDM_INTEL_SEL_50 (0x1 << 15) +#define RT5616_TDM_MODE_SEL_MASK (0x1 << 14) +#define RT5616_TDM_MODE_SEL_SFT 14 +#define RT5616_TDM_MODE_SEL_NOR (0x0 << 14) +#define RT5616_TDM_MODE_SEL_TDM (0x1 << 14) +#define RT5616_TDM_CH_NUM_SEL_MASK (0x3 << 12) +#define RT5616_TDM_CH_NUM_SEL_SFT 12 +#define RT5616_TDM_CH_NUM_SEL_2 (0x0 << 12) +#define RT5616_TDM_CH_NUM_SEL_4 (0x1 << 12) +#define RT5616_TDM_CH_NUM_SEL_6 (0x2 << 12) +#define RT5616_TDM_CH_NUM_SEL_8 (0x3 << 12) +#define RT5616_TDM_CH_LEN_SEL_MASK (0x3 << 10) +#define RT5616_TDM_CH_LEN_SEL_SFT 10 +#define RT5616_TDM_CH_LEN_SEL_16 (0x0 << 10) +#define RT5616_TDM_CH_LEN_SEL_20 (0x1 << 10) +#define RT5616_TDM_CH_LEN_SEL_24 (0x2 << 10) +#define RT5616_TDM_CH_LEN_SEL_32 (0x3 << 10) +#define RT5616_TDM_ADC_SEL_MASK (0x1 << 9) +#define RT5616_TDM_ADC_SEL_SFT 9 +#define RT5616_TDM_ADC_SEL_NOR (0x0 << 9) +#define RT5616_TDM_ADC_SEL_SWAP (0x1 << 9) +#define RT5616_TDM_ADC_START_SEL_MASK (0x1 << 8) +#define RT5616_TDM_ADC_START_SEL_SFT 8 +#define RT5616_TDM_ADC_START_SEL_SL0 (0x0 << 8) +#define RT5616_TDM_ADC_START_SEL_SL4 (0x1 << 8) +#define RT5616_TDM_I2S_CH2_SEL_MASK (0x3 << 6) +#define RT5616_TDM_I2S_CH2_SEL_SFT 6 +#define RT5616_TDM_I2S_CH2_SEL_LR (0x0 << 6) +#define RT5616_TDM_I2S_CH2_SEL_RL (0x1 << 6) +#define RT5616_TDM_I2S_CH2_SEL_LL (0x2 << 6) +#define RT5616_TDM_I2S_CH2_SEL_RR (0x3 << 6) +#define RT5616_TDM_I2S_CH4_SEL_MASK (0x3 << 4) +#define RT5616_TDM_I2S_CH4_SEL_SFT 4 +#define RT5616_TDM_I2S_CH4_SEL_LR (0x0 << 4) +#define RT5616_TDM_I2S_CH4_SEL_RL (0x1 << 4) +#define RT5616_TDM_I2S_CH4_SEL_LL (0x2 << 4) +#define RT5616_TDM_I2S_CH4_SEL_RR (0x3 << 4) +#define RT5616_TDM_I2S_CH6_SEL_MASK (0x3 << 2) +#define RT5616_TDM_I2S_CH6_SEL_SFT 2 +#define RT5616_TDM_I2S_CH6_SEL_LR (0x0 << 2) +#define RT5616_TDM_I2S_CH6_SEL_RL (0x1 << 2) +#define RT5616_TDM_I2S_CH6_SEL_LL (0x2 << 2) +#define RT5616_TDM_I2S_CH6_SEL_RR (0x3 << 2) +#define RT5616_TDM_I2S_CH8_SEL_MASK (0x3) +#define RT5616_TDM_I2S_CH8_SEL_SFT 0 +#define RT5616_TDM_I2S_CH8_SEL_LR (0x0) +#define RT5616_TDM_I2S_CH8_SEL_RL (0x1) +#define RT5616_TDM_I2S_CH8_SEL_LL (0x2) +#define RT5616_TDM_I2S_CH8_SEL_RR (0x3) + +/* TDM Control 2 (0x78) */ +#define RT5616_TDM_LRCK_POL_SEL_MASK (0x1 << 15) +#define RT5616_TDM_LRCK_POL_SEL_SFT 15 +#define RT5616_TDM_LRCK_POL_SEL_NOR (0x0 << 15) +#define RT5616_TDM_LRCK_POL_SEL_INV (0x1 << 15) +#define RT5616_TDM_CH_VAL_SEL_MASK (0x1 << 14) +#define RT5616_TDM_CH_VAL_SEL_SFT 14 +#define RT5616_TDM_CH_VAL_SEL_CH01 (0x0 << 14) +#define RT5616_TDM_CH_VAL_SEL_CH0123 (0x1 << 14) +#define RT5616_TDM_CH_VAL_EN (0x1 << 13) +#define RT5616_TDM_CH_VAL_SFT 13 +#define RT5616_TDM_LPBK_EN (0x1 << 12) +#define RT5616_TDM_LPBK_SFT 12 +#define RT5616_TDM_LRCK_PULSE_SEL_MASK (0x1 << 11) +#define RT5616_TDM_LRCK_PULSE_SEL_SFT 11 +#define RT5616_TDM_LRCK_PULSE_SEL_BCLK (0x0 << 11) +#define RT5616_TDM_LRCK_PULSE_SEL_CH (0x1 << 11) +#define RT5616_TDM_END_EDGE_SEL_MASK (0x1 << 10) +#define RT5616_TDM_END_EDGE_SEL_SFT 10 +#define RT5616_TDM_END_EDGE_SEL_POS (0x0 << 10) +#define RT5616_TDM_END_EDGE_SEL_NEG (0x1 << 10) +#define RT5616_TDM_END_EDGE_EN (0x1 << 9) +#define RT5616_TDM_END_EDGE_EN_SFT 9 +#define RT5616_TDM_TRAN_EDGE_SEL_MASK (0x1 << 8) +#define RT5616_TDM_TRAN_EDGE_SEL_SFT 8 +#define RT5616_TDM_TRAN_EDGE_SEL_POS (0x0 << 8) +#define RT5616_TDM_TRAN_EDGE_SEL_NEG (0x1 << 8) +#define RT5616_M_TDM2_L (0x1 << 7) +#define RT5616_M_TDM2_L_SFT 7 +#define RT5616_M_TDM2_R (0x1 << 6) +#define RT5616_M_TDM2_R_SFT 6 +#define RT5616_M_TDM4_L (0x1 << 5) +#define RT5616_M_TDM4_L_SFT 5 +#define RT5616_M_TDM4_R (0x1 << 4) +#define RT5616_M_TDM4_R_SFT 4 + +/* Global Clock Control (0x80) */ +#define RT5616_SCLK_SRC_MASK (0x3 << 14) +#define RT5616_SCLK_SRC_SFT 14 +#define RT5616_SCLK_SRC_MCLK (0x0 << 14) +#define RT5616_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5616_PLL1_SRC_MASK (0x3 << 12) +#define RT5616_PLL1_SRC_SFT 12 +#define RT5616_PLL1_SRC_MCLK (0x0 << 12) +#define RT5616_PLL1_SRC_BCLK1 (0x1 << 12) +#define RT5616_PLL1_SRC_BCLK2 (0x2 << 12) +#define RT5616_PLL1_PD_MASK (0x1 << 3) +#define RT5616_PLL1_PD_SFT 3 +#define RT5616_PLL1_PD_1 (0x0 << 3) +#define RT5616_PLL1_PD_2 (0x1 << 3) + +#define RT5616_PLL_INP_MAX 40000000 +#define RT5616_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5616_PLL_N_MAX 0x1ff +#define RT5616_PLL_N_MASK (RT5616_PLL_N_MAX << 7) +#define RT5616_PLL_N_SFT 7 +#define RT5616_PLL_K_MAX 0x1f +#define RT5616_PLL_K_MASK (RT5616_PLL_K_MAX) +#define RT5616_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5616_PLL_M_MAX 0xf +#define RT5616_PLL_M_MASK (RT5616_PLL_M_MAX << 12) +#define RT5616_PLL_M_SFT 12 +#define RT5616_PLL_M_BP (0x1 << 11) +#define RT5616_PLL_M_BP_SFT 11 + +/* PLL tracking mode 1 (0x83) */ +#define RT5616_STO1_T_MASK (0x1 << 15) +#define RT5616_STO1_T_SFT 15 +#define RT5616_STO1_T_SCLK (0x0 << 15) +#define RT5616_STO1_T_LRCK1 (0x1 << 15) +#define RT5616_STO2_T_MASK (0x1 << 12) +#define RT5616_STO2_T_SFT 12 +#define RT5616_STO2_T_I2S2 (0x0 << 12) +#define RT5616_STO2_T_LRCK2 (0x1 << 12) +#define RT5616_ASRC2_REF_MASK (0x1 << 11) +#define RT5616_ASRC2_REF_SFT 11 +#define RT5616_ASRC2_REF_LRCK2 (0x0 << 11) +#define RT5616_ASRC2_REF_LRCK1 (0x1 << 11) +#define RT5616_DMIC_1_M_MASK (0x1 << 9) +#define RT5616_DMIC_1_M_SFT 9 +#define RT5616_DMIC_1_M_NOR (0x0 << 9) +#define RT5616_DMIC_1_M_ASYN (0x1 << 9) + +/* PLL tracking mode 2 (0x84) */ +#define RT5616_STO1_ASRC_EN (0x1 << 15) +#define RT5616_STO1_ASRC_EN_SFT 15 +#define RT5616_STO2_ASRC_EN (0x1 << 14) +#define RT5616_STO2_ASRC_EN_SFT 14 +#define RT5616_STO1_DAC_M_MASK (0x1 << 13) +#define RT5616_STO1_DAC_M_SFT 13 +#define RT5616_STO1_DAC_M_NOR (0x0 << 13) +#define RT5616_STO1_DAC_M_ASRC (0x1 << 13) +#define RT5616_STO2_DAC_M_MASK (0x1 << 12) +#define RT5616_STO2_DAC_M_SFT 12 +#define RT5616_STO2_DAC_M_NOR (0x0 << 12) +#define RT5616_STO2_DAC_M_ASRC (0x1 << 12) +#define RT5616_ADC_M_MASK (0x1 << 11) +#define RT5616_ADC_M_SFT 11 +#define RT5616_ADC_M_NOR (0x0 << 11) +#define RT5616_ADC_M_ASRC (0x1 << 11) +#define RT5616_I2S1_R_D_MASK (0x1 << 4) +#define RT5616_I2S1_R_D_SFT 4 +#define RT5616_I2S1_R_D_DIS (0x0 << 4) +#define RT5616_I2S1_R_D_EN (0x1 << 4) +#define RT5616_I2S2_R_D_MASK (0x1 << 3) +#define RT5616_I2S2_R_D_SFT 3 +#define RT5616_I2S2_R_D_DIS (0x0 << 3) +#define RT5616_I2S2_R_D_EN (0x1 << 3) +#define RT5616_PRE_SCLK_MASK (0x3) +#define RT5616_PRE_SCLK_SFT 0 +#define RT5616_PRE_SCLK_512 (0x0) +#define RT5616_PRE_SCLK_1024 (0x1) +#define RT5616_PRE_SCLK_2048 (0x2) + +/* PLL tracking mode 3 (0x85) */ +#define RT5616_I2S1_RATE_MASK (0xf << 12) +#define RT5616_I2S1_RATE_SFT 12 +#define RT5616_I2S2_RATE_MASK (0xf << 8) +#define RT5616_I2S2_RATE_SFT 8 +#define RT5616_G_ASRC_LP_MASK (0x1 << 3) +#define RT5616_G_ASRC_LP_SFT 3 +#define RT5616_ASRC_LP_F_M (0x1 << 2) +#define RT5616_ASRC_LP_F_SFT 2 +#define RT5616_ASRC_LP_F_NOR (0x0 << 2) +#define RT5616_ASRC_LP_F_SB (0x1 << 2) +#define RT5616_FTK_PH_DET_MASK (0x3) +#define RT5616_FTK_PH_DET_SFT 0 +#define RT5616_FTK_PH_DET_DIV1 (0x0) +#define RT5616_FTK_PH_DET_DIV2 (0x1) +#define RT5616_FTK_PH_DET_DIV4 (0x2) +#define RT5616_FTK_PH_DET_DIV8 (0x3) + +/*PLL tracking mode 6 (0x89) */ +#define RT5616_I2S1_PD_MASK (0x7 << 12) +#define RT5616_I2S1_PD_SFT 12 +#define RT5616_I2S2_PD_MASK (0x7 << 8) +#define RT5616_I2S2_PD_SFT 8 + +/*PLL tracking mode 7 (0x8a) */ +#define RT5616_FSI1_RATE_MASK (0xf << 12) +#define RT5616_FSI1_RATE_SFT 12 +#define RT5616_FSI2_RATE_MASK (0xf << 8) +#define RT5616_FSI2_RATE_SFT 8 + +/* HPOUT Over Current Detection (0x8b) */ +#define RT5616_HP_OVCD_MASK (0x1 << 10) +#define RT5616_HP_OVCD_SFT 10 +#define RT5616_HP_OVCD_DIS (0x0 << 10) +#define RT5616_HP_OVCD_EN (0x1 << 10) +#define RT5616_HP_OC_TH_MASK (0x3 << 8) +#define RT5616_HP_OC_TH_SFT 8 +#define RT5616_HP_OC_TH_90 (0x0 << 8) +#define RT5616_HP_OC_TH_105 (0x1 << 8) +#define RT5616_HP_OC_TH_120 (0x2 << 8) +#define RT5616_HP_OC_TH_135 (0x3 << 8) + +/* Depop Mode Control 1 (0x8e) */ +#define RT5616_SMT_TRIG_MASK (0x1 << 15) +#define RT5616_SMT_TRIG_SFT 15 +#define RT5616_SMT_TRIG_DIS (0x0 << 15) +#define RT5616_SMT_TRIG_EN (0x1 << 15) +#define RT5616_HP_L_SMT_MASK (0x1 << 9) +#define RT5616_HP_L_SMT_SFT 9 +#define RT5616_HP_L_SMT_DIS (0x0 << 9) +#define RT5616_HP_L_SMT_EN (0x1 << 9) +#define RT5616_HP_R_SMT_MASK (0x1 << 8) +#define RT5616_HP_R_SMT_SFT 8 +#define RT5616_HP_R_SMT_DIS (0x0 << 8) +#define RT5616_HP_R_SMT_EN (0x1 << 8) +#define RT5616_HP_CD_PD_MASK (0x1 << 7) +#define RT5616_HP_CD_PD_SFT 7 +#define RT5616_HP_CD_PD_DIS (0x0 << 7) +#define RT5616_HP_CD_PD_EN (0x1 << 7) +#define RT5616_RSTN_MASK (0x1 << 6) +#define RT5616_RSTN_SFT 6 +#define RT5616_RSTN_DIS (0x0 << 6) +#define RT5616_RSTN_EN (0x1 << 6) +#define RT5616_RSTP_MASK (0x1 << 5) +#define RT5616_RSTP_SFT 5 +#define RT5616_RSTP_DIS (0x0 << 5) +#define RT5616_RSTP_EN (0x1 << 5) +#define RT5616_HP_CO_MASK (0x1 << 4) +#define RT5616_HP_CO_SFT 4 +#define RT5616_HP_CO_DIS (0x0 << 4) +#define RT5616_HP_CO_EN (0x1 << 4) +#define RT5616_HP_CP_MASK (0x1 << 3) +#define RT5616_HP_CP_SFT 3 +#define RT5616_HP_CP_PD (0x0 << 3) +#define RT5616_HP_CP_PU (0x1 << 3) +#define RT5616_HP_SG_MASK (0x1 << 2) +#define RT5616_HP_SG_SFT 2 +#define RT5616_HP_SG_DIS (0x0 << 2) +#define RT5616_HP_SG_EN (0x1 << 2) +#define RT5616_HP_DP_MASK (0x1 << 1) +#define RT5616_HP_DP_SFT 1 +#define RT5616_HP_DP_PD (0x0 << 1) +#define RT5616_HP_DP_PU (0x1 << 1) +#define RT5616_HP_CB_MASK (0x1) +#define RT5616_HP_CB_SFT 0 +#define RT5616_HP_CB_PD (0x0) +#define RT5616_HP_CB_PU (0x1) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5616_DEPOP_MASK (0x1 << 13) +#define RT5616_DEPOP_SFT 13 +#define RT5616_DEPOP_AUTO (0x0 << 13) +#define RT5616_DEPOP_MAN (0x1 << 13) +#define RT5616_RAMP_MASK (0x1 << 12) +#define RT5616_RAMP_SFT 12 +#define RT5616_RAMP_DIS (0x0 << 12) +#define RT5616_RAMP_EN (0x1 << 12) +#define RT5616_BPS_MASK (0x1 << 11) +#define RT5616_BPS_SFT 11 +#define RT5616_BPS_DIS (0x0 << 11) +#define RT5616_BPS_EN (0x1 << 11) +#define RT5616_FAST_UPDN_MASK (0x1 << 10) +#define RT5616_FAST_UPDN_SFT 10 +#define RT5616_FAST_UPDN_DIS (0x0 << 10) +#define RT5616_FAST_UPDN_EN (0x1 << 10) +#define RT5616_MRES_MASK (0x3 << 8) +#define RT5616_MRES_SFT 8 +#define RT5616_MRES_15MO (0x0 << 8) +#define RT5616_MRES_25MO (0x1 << 8) +#define RT5616_MRES_35MO (0x2 << 8) +#define RT5616_MRES_45MO (0x3 << 8) +#define RT5616_VLO_MASK (0x1 << 7) +#define RT5616_VLO_SFT 7 +#define RT5616_VLO_3V (0x0 << 7) +#define RT5616_VLO_32V (0x1 << 7) +#define RT5616_DIG_DP_MASK (0x1 << 6) +#define RT5616_DIG_DP_SFT 6 +#define RT5616_DIG_DP_DIS (0x0 << 6) +#define RT5616_DIG_DP_EN (0x1 << 6) +#define RT5616_DP_TH_MASK (0x3 << 4) +#define RT5616_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5616_CP_SYS_MASK (0x7 << 12) +#define RT5616_CP_SYS_SFT 12 +#define RT5616_CP_FQ1_MASK (0x7 << 8) +#define RT5616_CP_FQ1_SFT 8 +#define RT5616_CP_FQ2_MASK (0x7 << 4) +#define RT5616_CP_FQ2_SFT 4 +#define RT5616_CP_FQ3_MASK (0x7) +#define RT5616_CP_FQ3_SFT 0 +#define RT5616_CP_FQ_1_5_KHZ 0 +#define RT5616_CP_FQ_3_KHZ 1 +#define RT5616_CP_FQ_6_KHZ 2 +#define RT5616_CP_FQ_12_KHZ 3 +#define RT5616_CP_FQ_24_KHZ 4 +#define RT5616_CP_FQ_48_KHZ 5 +#define RT5616_CP_FQ_96_KHZ 6 +#define RT5616_CP_FQ_192_KHZ 7 + +/* HPOUT charge pump (0x91) */ +#define RT5616_OSW_L_MASK (0x1 << 11) +#define RT5616_OSW_L_SFT 11 +#define RT5616_OSW_L_DIS (0x0 << 11) +#define RT5616_OSW_L_EN (0x1 << 11) +#define RT5616_OSW_R_MASK (0x1 << 10) +#define RT5616_OSW_R_SFT 10 +#define RT5616_OSW_R_DIS (0x0 << 10) +#define RT5616_OSW_R_EN (0x1 << 10) +#define RT5616_PM_HP_MASK (0x3 << 8) +#define RT5616_PM_HP_SFT 8 +#define RT5616_PM_HP_LV (0x0 << 8) +#define RT5616_PM_HP_MV (0x1 << 8) +#define RT5616_PM_HP_HV (0x2 << 8) +#define RT5616_IB_HP_MASK (0x3 << 6) +#define RT5616_IB_HP_SFT 6 +#define RT5616_IB_HP_125IL (0x0 << 6) +#define RT5616_IB_HP_25IL (0x1 << 6) +#define RT5616_IB_HP_5IL (0x2 << 6) +#define RT5616_IB_HP_1IL (0x3 << 6) + +/* Micbias Control (0x93) */ +#define RT5616_MIC1_BS_MASK (0x1 << 15) +#define RT5616_MIC1_BS_SFT 15 +#define RT5616_MIC1_BS_9AV (0x0 << 15) +#define RT5616_MIC1_BS_75AV (0x1 << 15) +#define RT5616_MIC1_CLK_MASK (0x1 << 13) +#define RT5616_MIC1_CLK_SFT 13 +#define RT5616_MIC1_CLK_DIS (0x0 << 13) +#define RT5616_MIC1_CLK_EN (0x1 << 13) +#define RT5616_MIC1_OVCD_MASK (0x1 << 11) +#define RT5616_MIC1_OVCD_SFT 11 +#define RT5616_MIC1_OVCD_DIS (0x0 << 11) +#define RT5616_MIC1_OVCD_EN (0x1 << 11) +#define RT5616_MIC1_OVTH_MASK (0x3 << 9) +#define RT5616_MIC1_OVTH_SFT 9 +#define RT5616_MIC1_OVTH_600UA (0x0 << 9) +#define RT5616_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5616_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5616_PWR_MB_MASK (0x1 << 5) +#define RT5616_PWR_MB_SFT 5 +#define RT5616_PWR_MB_PD (0x0 << 5) +#define RT5616_PWR_MB_PU (0x1 << 5) +#define RT5616_PWR_CLK12M_MASK (0x1 << 4) +#define RT5616_PWR_CLK12M_SFT 4 +#define RT5616_PWR_CLK12M_PD (0x0 << 4) +#define RT5616_PWR_CLK12M_PU (0x1 << 4) + +/* Analog JD Control 1 (0x94) */ +#define RT5616_JD2_CMP_MASK (0x7 << 12) +#define RT5616_JD2_CMP_SFT 12 +#define RT5616_JD_PU (0x1 << 11) +#define RT5616_JD_PU_SFT 11 +#define RT5616_JD_PD (0x1 << 10) +#define RT5616_JD_PD_SFT 10 +#define RT5616_JD_MODE_SEL_MASK (0x3 << 8) +#define RT5616_JD_MODE_SEL_SFT 8 +#define RT5616_JD_MODE_SEL_M0 (0x0 << 8) +#define RT5616_JD_MODE_SEL_M1 (0x1 << 8) +#define RT5616_JD_MODE_SEL_M2 (0x2 << 8) +#define RT5616_JD_M_CMP (0x7 << 4) +#define RT5616_JD_M_CMP_SFT 4 +#define RT5616_JD_M_PU (0x1 << 3) +#define RT5616_JD_M_PU_SFT 3 +#define RT5616_JD_M_PD (0x1 << 2) +#define RT5616_JD_M_PD_SFT 2 +#define RT5616_JD_M_MODE_SEL_MASK (0x3) +#define RT5616_JD_M_MODE_SEL_SFT 0 +#define RT5616_JD_M_MODE_SEL_M0 (0x0) +#define RT5616_JD_M_MODE_SEL_M1 (0x1) +#define RT5616_JD_M_MODE_SEL_M2 (0x2) + +/* Analog JD Control 2 (0x95) */ +#define RT5616_JD3_CMP_MASK (0x7 << 12) +#define RT5616_JD3_CMP_SFT 12 + +/* EQ Control 1 (0xb0) */ +#define RT5616_EQ_SRC_MASK (0x1 << 15) +#define RT5616_EQ_SRC_SFT 15 +#define RT5616_EQ_SRC_DAC (0x0 << 15) +#define RT5616_EQ_SRC_ADC (0x1 << 15) +#define RT5616_EQ_UPD (0x1 << 14) +#define RT5616_EQ_UPD_BIT 14 +#define RT5616_EQ_CD_MASK (0x1 << 13) +#define RT5616_EQ_CD_SFT 13 +#define RT5616_EQ_CD_DIS (0x0 << 13) +#define RT5616_EQ_CD_EN (0x1 << 13) +#define RT5616_EQ_DITH_MASK (0x3 << 8) +#define RT5616_EQ_DITH_SFT 8 +#define RT5616_EQ_DITH_NOR (0x0 << 8) +#define RT5616_EQ_DITH_LSB (0x1 << 8) +#define RT5616_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5616_EQ_DITH_LSB_2 (0x3 << 8) +#define RT5616_EQ_CD_F (0x1 << 7) +#define RT5616_EQ_CD_F_BIT 7 +#define RT5616_EQ_STA_HP2 (0x1 << 6) +#define RT5616_EQ_STA_HP2_BIT 6 +#define RT5616_EQ_STA_HP1 (0x1 << 5) +#define RT5616_EQ_STA_HP1_BIT 5 +#define RT5616_EQ_STA_BP4 (0x1 << 4) +#define RT5616_EQ_STA_BP4_BIT 4 +#define RT5616_EQ_STA_BP3 (0x1 << 3) +#define RT5616_EQ_STA_BP3_BIT 3 +#define RT5616_EQ_STA_BP2 (0x1 << 2) +#define RT5616_EQ_STA_BP2_BIT 2 +#define RT5616_EQ_STA_BP1 (0x1 << 1) +#define RT5616_EQ_STA_BP1_BIT 1 +#define RT5616_EQ_STA_LP (0x1) +#define RT5616_EQ_STA_LP_BIT 0 + +/* EQ Control 2 (0xb1) */ +#define RT5616_EQ_HPF1_M_MASK (0x1 << 8) +#define RT5616_EQ_HPF1_M_SFT 8 +#define RT5616_EQ_HPF1_M_HI (0x0 << 8) +#define RT5616_EQ_HPF1_M_1ST (0x1 << 8) +#define RT5616_EQ_LPF1_M_MASK (0x1 << 7) +#define RT5616_EQ_LPF1_M_SFT 7 +#define RT5616_EQ_LPF1_M_LO (0x0 << 7) +#define RT5616_EQ_LPF1_M_1ST (0x1 << 7) +#define RT5616_EQ_HPF2_MASK (0x1 << 6) +#define RT5616_EQ_HPF2_SFT 6 +#define RT5616_EQ_HPF2_DIS (0x0 << 6) +#define RT5616_EQ_HPF2_EN (0x1 << 6) +#define RT5616_EQ_HPF1_MASK (0x1 << 5) +#define RT5616_EQ_HPF1_SFT 5 +#define RT5616_EQ_HPF1_DIS (0x0 << 5) +#define RT5616_EQ_HPF1_EN (0x1 << 5) +#define RT5616_EQ_BPF4_MASK (0x1 << 4) +#define RT5616_EQ_BPF4_SFT 4 +#define RT5616_EQ_BPF4_DIS (0x0 << 4) +#define RT5616_EQ_BPF4_EN (0x1 << 4) +#define RT5616_EQ_BPF3_MASK (0x1 << 3) +#define RT5616_EQ_BPF3_SFT 3 +#define RT5616_EQ_BPF3_DIS (0x0 << 3) +#define RT5616_EQ_BPF3_EN (0x1 << 3) +#define RT5616_EQ_BPF2_MASK (0x1 << 2) +#define RT5616_EQ_BPF2_SFT 2 +#define RT5616_EQ_BPF2_DIS (0x0 << 2) +#define RT5616_EQ_BPF2_EN (0x1 << 2) +#define RT5616_EQ_BPF1_MASK (0x1 << 1) +#define RT5616_EQ_BPF1_SFT 1 +#define RT5616_EQ_BPF1_DIS (0x0 << 1) +#define RT5616_EQ_BPF1_EN (0x1 << 1) +#define RT5616_EQ_LPF_MASK (0x1) +#define RT5616_EQ_LPF_SFT 0 +#define RT5616_EQ_LPF_DIS (0x0) +#define RT5616_EQ_LPF_EN (0x1) +#define RT5616_EQ_CTRL_MASK (0x7f) + +/* Memory Test (0xb2) */ +#define RT5616_MT_MASK (0x1 << 15) +#define RT5616_MT_SFT 15 +#define RT5616_MT_DIS (0x0 << 15) +#define RT5616_MT_EN (0x1 << 15) + +/* DRC/AGC Control 1 (0xb4) */ +#define RT5616_DRC_AGC_P_MASK (0x1 << 15) +#define RT5616_DRC_AGC_P_SFT 15 +#define RT5616_DRC_AGC_P_DAC (0x0 << 15) +#define RT5616_DRC_AGC_P_ADC (0x1 << 15) +#define RT5616_DRC_AGC_MASK (0x1 << 14) +#define RT5616_DRC_AGC_SFT 14 +#define RT5616_DRC_AGC_DIS (0x0 << 14) +#define RT5616_DRC_AGC_EN (0x1 << 14) +#define RT5616_DRC_AGC_UPD (0x1 << 13) +#define RT5616_DRC_AGC_UPD_BIT 13 +#define RT5616_DRC_AGC_AR_MASK (0x1f << 8) +#define RT5616_DRC_AGC_AR_SFT 8 +#define RT5616_DRC_AGC_R_MASK (0x7 << 5) +#define RT5616_DRC_AGC_R_SFT 5 +#define RT5616_DRC_AGC_R_48K (0x1 << 5) +#define RT5616_DRC_AGC_R_96K (0x2 << 5) +#define RT5616_DRC_AGC_R_192K (0x3 << 5) +#define RT5616_DRC_AGC_R_441K (0x5 << 5) +#define RT5616_DRC_AGC_R_882K (0x6 << 5) +#define RT5616_DRC_AGC_R_1764K (0x7 << 5) +#define RT5616_DRC_AGC_RC_MASK (0x1f) +#define RT5616_DRC_AGC_RC_SFT 0 + +/* DRC/AGC Control 2 (0xb5) */ +#define RT5616_DRC_AGC_POB_MASK (0x3f << 8) +#define RT5616_DRC_AGC_POB_SFT 8 +#define RT5616_DRC_AGC_CP_MASK (0x1 << 7) +#define RT5616_DRC_AGC_CP_SFT 7 +#define RT5616_DRC_AGC_CP_DIS (0x0 << 7) +#define RT5616_DRC_AGC_CP_EN (0x1 << 7) +#define RT5616_DRC_AGC_CPR_MASK (0x3 << 5) +#define RT5616_DRC_AGC_CPR_SFT 5 +#define RT5616_DRC_AGC_CPR_1_1 (0x0 << 5) +#define RT5616_DRC_AGC_CPR_1_2 (0x1 << 5) +#define RT5616_DRC_AGC_CPR_1_3 (0x2 << 5) +#define RT5616_DRC_AGC_CPR_1_4 (0x3 << 5) +#define RT5616_DRC_AGC_PRB_MASK (0x1f) +#define RT5616_DRC_AGC_PRB_SFT 0 + +/* DRC/AGC Control 3 (0xb6) */ +#define RT5616_DRC_AGC_NGB_MASK (0xf << 12) +#define RT5616_DRC_AGC_NGB_SFT 12 +#define RT5616_DRC_AGC_TAR_MASK (0x1f << 7) +#define RT5616_DRC_AGC_TAR_SFT 7 +#define RT5616_DRC_AGC_NG_MASK (0x1 << 6) +#define RT5616_DRC_AGC_NG_SFT 6 +#define RT5616_DRC_AGC_NG_DIS (0x0 << 6) +#define RT5616_DRC_AGC_NG_EN (0x1 << 6) +#define RT5616_DRC_AGC_NGH_MASK (0x1 << 5) +#define RT5616_DRC_AGC_NGH_SFT 5 +#define RT5616_DRC_AGC_NGH_DIS (0x0 << 5) +#define RT5616_DRC_AGC_NGH_EN (0x1 << 5) +#define RT5616_DRC_AGC_NGT_MASK (0x1f) +#define RT5616_DRC_AGC_NGT_SFT 0 + +/* Jack Detect Control 1 (0xbb) */ +#define RT5616_JD_MASK (0x7 << 13) +#define RT5616_JD_SFT 13 +#define RT5616_JD_DIS (0x0 << 13) +#define RT5616_JD_GPIO1 (0x1 << 13) +#define RT5616_JD_GPIO2 (0x2 << 13) +#define RT5616_JD_GPIO3 (0x3 << 13) +#define RT5616_JD_GPIO4 (0x4 << 13) +#define RT5616_JD_GPIO5 (0x5 << 13) +#define RT5616_JD_GPIO6 (0x6 << 13) +#define RT5616_JD_HP_MASK (0x1 << 11) +#define RT5616_JD_HP_SFT 11 +#define RT5616_JD_HP_DIS (0x0 << 11) +#define RT5616_JD_HP_EN (0x1 << 11) +#define RT5616_JD_HP_TRG_MASK (0x1 << 10) +#define RT5616_JD_HP_TRG_SFT 10 +#define RT5616_JD_HP_TRG_LO (0x0 << 10) +#define RT5616_JD_HP_TRG_HI (0x1 << 10) +#define RT5616_JD_SPL_MASK (0x1 << 9) +#define RT5616_JD_SPL_SFT 9 +#define RT5616_JD_SPL_DIS (0x0 << 9) +#define RT5616_JD_SPL_EN (0x1 << 9) +#define RT5616_JD_SPL_TRG_MASK (0x1 << 8) +#define RT5616_JD_SPL_TRG_SFT 8 +#define RT5616_JD_SPL_TRG_LO (0x0 << 8) +#define RT5616_JD_SPL_TRG_HI (0x1 << 8) +#define RT5616_JD_SPR_MASK (0x1 << 7) +#define RT5616_JD_SPR_SFT 7 +#define RT5616_JD_SPR_DIS (0x0 << 7) +#define RT5616_JD_SPR_EN (0x1 << 7) +#define RT5616_JD_SPR_TRG_MASK (0x1 << 6) +#define RT5616_JD_SPR_TRG_SFT 6 +#define RT5616_JD_SPR_TRG_LO (0x0 << 6) +#define RT5616_JD_SPR_TRG_HI (0x1 << 6) +#define RT5616_JD_LO_MASK (0x1 << 3) +#define RT5616_JD_LO_SFT 3 +#define RT5616_JD_LO_DIS (0x0 << 3) +#define RT5616_JD_LO_EN (0x1 << 3) +#define RT5616_JD_LO_TRG_MASK (0x1 << 2) +#define RT5616_JD_LO_TRG_SFT 2 +#define RT5616_JD_LO_TRG_LO (0x0 << 2) +#define RT5616_JD_LO_TRG_HI (0x1 << 2) + +/* Jack Detect Control 2 (0xbc) */ +#define RT5616_JD_TRG_SEL_MASK (0x7 << 9) +#define RT5616_JD_TRG_SEL_SFT 9 +#define RT5616_JD_TRG_SEL_GPIO (0x0 << 9) +#define RT5616_JD_TRG_SEL_JD1_1 (0x1 << 9) +#define RT5616_JD_TRG_SEL_JD1_2 (0x2 << 9) +#define RT5616_JD_TRG_SEL_JD2 (0x3 << 9) +#define RT5616_JD_TRG_SEL_JD3 (0x4 << 9) +#define RT5616_JD3_IRQ_EN (0x1 << 8) +#define RT5616_JD3_IRQ_EN_SFT 8 +#define RT5616_JD3_EN_STKY (0x1 << 7) +#define RT5616_JD3_EN_STKY_SFT 7 +#define RT5616_JD3_INV (0x1 << 6) +#define RT5616_JD3_INV_SFT 6 + +/* IRQ Control 1 (0xbd) */ +#define RT5616_IRQ_JD_MASK (0x1 << 15) +#define RT5616_IRQ_JD_SFT 15 +#define RT5616_IRQ_JD_BP (0x0 << 15) +#define RT5616_IRQ_JD_NOR (0x1 << 15) +#define RT5616_JD_STKY_MASK (0x1 << 13) +#define RT5616_JD_STKY_SFT 13 +#define RT5616_JD_STKY_DIS (0x0 << 13) +#define RT5616_JD_STKY_EN (0x1 << 13) +#define RT5616_JD_P_MASK (0x1 << 11) +#define RT5616_JD_P_SFT 11 +#define RT5616_JD_P_NOR (0x0 << 11) +#define RT5616_JD_P_INV (0x1 << 11) +#define RT5616_JD1_1_IRQ_EN (0x1 << 9) +#define RT5616_JD1_1_IRQ_EN_SFT 9 +#define RT5616_JD1_1_EN_STKY (0x1 << 8) +#define RT5616_JD1_1_EN_STKY_SFT 8 +#define RT5616_JD1_1_INV (0x1 << 7) +#define RT5616_JD1_1_INV_SFT 7 +#define RT5616_JD1_2_IRQ_EN (0x1 << 6) +#define RT5616_JD1_2_IRQ_EN_SFT 6 +#define RT5616_JD1_2_EN_STKY (0x1 << 5) +#define RT5616_JD1_2_EN_STKY_SFT 5 +#define RT5616_JD1_2_INV (0x1 << 4) +#define RT5616_JD1_2_INV_SFT 4 +#define RT5616_JD2_IRQ_EN (0x1 << 3) +#define RT5616_JD2_IRQ_EN_SFT 3 +#define RT5616_JD2_EN_STKY (0x1 << 2) +#define RT5616_JD2_EN_STKY_SFT 2 +#define RT5616_JD2_INV (0x1 << 1) +#define RT5616_JD2_INV_SFT 1 + +/* IRQ Control 2 (0xbe) */ +#define RT5616_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5616_IRQ_MB1_OC_SFT 15 +#define RT5616_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5616_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5616_MB1_OC_STKY_MASK (0x1 << 11) +#define RT5616_MB1_OC_STKY_SFT 11 +#define RT5616_MB1_OC_STKY_DIS (0x0 << 11) +#define RT5616_MB1_OC_STKY_EN (0x1 << 11) +#define RT5616_MB1_OC_P_MASK (0x1 << 7) +#define RT5616_MB1_OC_P_SFT 7 +#define RT5616_MB1_OC_P_NOR (0x0 << 7) +#define RT5616_MB1_OC_P_INV (0x1 << 7) +#define RT5616_MB2_OC_P_MASK (0x1 << 6) +#define RT5616_MB1_OC_CLR (0x1 << 3) +#define RT5616_MB1_OC_CLR_SFT 3 +#define RT5616_STA_GPIO8 (0x1) +#define RT5616_STA_GPIO8_BIT 0 + +/* Internal Status and GPIO status (0xbf) */ +#define RT5616_STA_JD3 (0x1 << 15) +#define RT5616_STA_JD3_BIT 15 +#define RT5616_STA_JD2 (0x1 << 14) +#define RT5616_STA_JD2_BIT 14 +#define RT5616_STA_JD1_2 (0x1 << 13) +#define RT5616_STA_JD1_2_BIT 13 +#define RT5616_STA_JD1_1 (0x1 << 12) +#define RT5616_STA_JD1_1_BIT 12 +#define RT5616_STA_GP7 (0x1 << 11) +#define RT5616_STA_GP7_BIT 11 +#define RT5616_STA_GP6 (0x1 << 10) +#define RT5616_STA_GP6_BIT 10 +#define RT5616_STA_GP5 (0x1 << 9) +#define RT5616_STA_GP5_BIT 9 +#define RT5616_STA_GP1 (0x1 << 8) +#define RT5616_STA_GP1_BIT 8 +#define RT5616_STA_GP2 (0x1 << 7) +#define RT5616_STA_GP2_BIT 7 +#define RT5616_STA_GP3 (0x1 << 6) +#define RT5616_STA_GP3_BIT 6 +#define RT5616_STA_GP4 (0x1 << 5) +#define RT5616_STA_GP4_BIT 5 +#define RT5616_STA_GP_JD (0x1 << 4) +#define RT5616_STA_GP_JD_BIT 4 + +/* GPIO Control 1 (0xc0) */ +#define RT5616_GP1_PIN_MASK (0x1 << 15) +#define RT5616_GP1_PIN_SFT 15 +#define RT5616_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5616_GP1_PIN_IRQ (0x1 << 15) +#define RT5616_GP2_PIN_MASK (0x1 << 14) +#define RT5616_GP2_PIN_SFT 14 +#define RT5616_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5616_GP2_PIN_DMIC1_SCL (0x1 << 14) +#define RT5616_GPIO_M_MASK (0x1 << 9) +#define RT5616_GPIO_M_SFT 9 +#define RT5616_GPIO_M_FLT (0x0 << 9) +#define RT5616_GPIO_M_PH (0x1 << 9) +#define RT5616_I2S2_SEL_MASK (0x1 << 8) +#define RT5616_I2S2_SEL_SFT 8 +#define RT5616_I2S2_SEL_I2S (0x0 << 8) +#define RT5616_I2S2_SEL_GPIO (0x1 << 8) +#define RT5616_GP5_PIN_MASK (0x1 << 7) +#define RT5616_GP5_PIN_SFT 7 +#define RT5616_GP5_PIN_GPIO5 (0x0 << 7) +#define RT5616_GP5_PIN_IRQ (0x1 << 7) +#define RT5616_GP6_PIN_MASK (0x1 << 6) +#define RT5616_GP6_PIN_SFT 6 +#define RT5616_GP6_PIN_GPIO6 (0x0 << 6) +#define RT5616_GP6_PIN_DMIC_SDA (0x1 << 6) +#define RT5616_GP7_PIN_MASK (0x1 << 5) +#define RT5616_GP7_PIN_SFT 5 +#define RT5616_GP7_PIN_GPIO7 (0x0 << 5) +#define RT5616_GP7_PIN_IRQ (0x1 << 5) +#define RT5616_GP8_PIN_MASK (0x1 << 4) +#define RT5616_GP8_PIN_SFT 4 +#define RT5616_GP8_PIN_GPIO8 (0x0 << 4) +#define RT5616_GP8_PIN_DMIC_SDA (0x1 << 4) +#define RT5616_GPIO_PDM_SEL_MASK (0x1 << 3) +#define RT5616_GPIO_PDM_SEL_SFT 3 +#define RT5616_GPIO_PDM_SEL_GPIO (0x0 << 3) +#define RT5616_GPIO_PDM_SEL_PDM (0x1 << 3) + +/* GPIO Control 2 (0xc1) */ +#define RT5616_GP5_DR_MASK (0x1 << 14) +#define RT5616_GP5_DR_SFT 14 +#define RT5616_GP5_DR_IN (0x0 << 14) +#define RT5616_GP5_DR_OUT (0x1 << 14) +#define RT5616_GP5_OUT_MASK (0x1 << 13) +#define RT5616_GP5_OUT_SFT 13 +#define RT5616_GP5_OUT_LO (0x0 << 13) +#define RT5616_GP5_OUT_HI (0x1 << 13) +#define RT5616_GP5_P_MASK (0x1 << 12) +#define RT5616_GP5_P_SFT 12 +#define RT5616_GP5_P_NOR (0x0 << 12) +#define RT5616_GP5_P_INV (0x1 << 12) +#define RT5616_GP4_DR_MASK (0x1 << 11) +#define RT5616_GP4_DR_SFT 11 +#define RT5616_GP4_DR_IN (0x0 << 11) +#define RT5616_GP4_DR_OUT (0x1 << 11) +#define RT5616_GP4_OUT_MASK (0x1 << 10) +#define RT5616_GP4_OUT_SFT 10 +#define RT5616_GP4_OUT_LO (0x0 << 10) +#define RT5616_GP4_OUT_HI (0x1 << 10) +#define RT5616_GP4_P_MASK (0x1 << 9) +#define RT5616_GP4_P_SFT 9 +#define RT5616_GP4_P_NOR (0x0 << 9) +#define RT5616_GP4_P_INV (0x1 << 9) +#define RT5616_GP3_DR_MASK (0x1 << 8) +#define RT5616_GP3_DR_SFT 8 +#define RT5616_GP3_DR_IN (0x0 << 8) +#define RT5616_GP3_DR_OUT (0x1 << 8) +#define RT5616_GP3_OUT_MASK (0x1 << 7) +#define RT5616_GP3_OUT_SFT 7 +#define RT5616_GP3_OUT_LO (0x0 << 7) +#define RT5616_GP3_OUT_HI (0x1 << 7) +#define RT5616_GP3_P_MASK (0x1 << 6) +#define RT5616_GP3_P_SFT 6 +#define RT5616_GP3_P_NOR (0x0 << 6) +#define RT5616_GP3_P_INV (0x1 << 6) +#define RT5616_GP2_DR_MASK (0x1 << 5) +#define RT5616_GP2_DR_SFT 5 +#define RT5616_GP2_DR_IN (0x0 << 5) +#define RT5616_GP2_DR_OUT (0x1 << 5) +#define RT5616_GP2_OUT_MASK (0x1 << 4) +#define RT5616_GP2_OUT_SFT 4 +#define RT5616_GP2_OUT_LO (0x0 << 4) +#define RT5616_GP2_OUT_HI (0x1 << 4) +#define RT5616_GP2_P_MASK (0x1 << 3) +#define RT5616_GP2_P_SFT 3 +#define RT5616_GP2_P_NOR (0x0 << 3) +#define RT5616_GP2_P_INV (0x1 << 3) +#define RT5616_GP1_DR_MASK (0x1 << 2) +#define RT5616_GP1_DR_SFT 2 +#define RT5616_GP1_DR_IN (0x0 << 2) +#define RT5616_GP1_DR_OUT (0x1 << 2) +#define RT5616_GP1_OUT_MASK (0x1 << 1) +#define RT5616_GP1_OUT_SFT 1 +#define RT5616_GP1_OUT_LO (0x0 << 1) +#define RT5616_GP1_OUT_HI (0x1 << 1) +#define RT5616_GP1_P_MASK (0x1) +#define RT5616_GP1_P_SFT 0 +#define RT5616_GP1_P_NOR (0x0) +#define RT5616_GP1_P_INV (0x1) + +/* GPIO Control 3 (0xc2) */ +#define RT5616_GP8_DR_MASK (0x1 << 8) +#define RT5616_GP8_DR_SFT 8 +#define RT5616_GP8_DR_IN (0x0 << 8) +#define RT5616_GP8_DR_OUT (0x1 << 8) +#define RT5616_GP8_OUT_MASK (0x1 << 7) +#define RT5616_GP8_OUT_SFT 7 +#define RT5616_GP8_OUT_LO (0x0 << 7) +#define RT5616_GP8_OUT_HI (0x1 << 7) +#define RT5616_GP8_P_MASK (0x1 << 6) +#define RT5616_GP8_P_SFT 6 +#define RT5616_GP8_P_NOR (0x0 << 6) +#define RT5616_GP8_P_INV (0x1 << 6) +#define RT5616_GP7_DR_MASK (0x1 << 5) +#define RT5616_GP7_DR_SFT 5 +#define RT5616_GP7_DR_IN (0x0 << 5) +#define RT5616_GP7_DR_OUT (0x1 << 5) +#define RT5616_GP7_OUT_MASK (0x1 << 4) +#define RT5616_GP7_OUT_SFT 4 +#define RT5616_GP7_OUT_LO (0x0 << 4) +#define RT5616_GP7_OUT_HI (0x1 << 4) +#define RT5616_GP7_P_MASK (0x1 << 3) +#define RT5616_GP7_P_SFT 3 +#define RT5616_GP7_P_NOR (0x0 << 3) +#define RT5616_GP7_P_INV (0x1 << 3) +#define RT5616_GP6_DR_MASK (0x1 << 2) +#define RT5616_GP6_DR_SFT 2 +#define RT5616_GP6_DR_IN (0x0 << 2) +#define RT5616_GP6_DR_OUT (0x1 << 2) +#define RT5616_GP6_OUT_MASK (0x1 << 1) +#define RT5616_GP6_OUT_SFT 1 +#define RT5616_GP6_OUT_LO (0x0 << 1) +#define RT5616_GP6_OUT_HI (0x1 << 1) +#define RT5616_GP6_P_MASK (0x1) +#define RT5616_GP6_P_SFT 0 +#define RT5616_GP6_P_NOR (0x0) +#define RT5616_GP6_P_INV (0x1) + +/* Scramble Control (0xce) */ +#define RT5616_SCB_SWAP_MASK (0x1 << 15) +#define RT5616_SCB_SWAP_SFT 15 +#define RT5616_SCB_SWAP_DIS (0x0 << 15) +#define RT5616_SCB_SWAP_EN (0x1 << 15) +#define RT5616_SCB_MASK (0x1 << 14) +#define RT5616_SCB_SFT 14 +#define RT5616_SCB_DIS (0x0 << 14) +#define RT5616_SCB_EN (0x1 << 14) + +/* Baseback Control (0xcf) */ +#define RT5616_BB_MASK (0x1 << 15) +#define RT5616_BB_SFT 15 +#define RT5616_BB_DIS (0x0 << 15) +#define RT5616_BB_EN (0x1 << 15) +#define RT5616_BB_CT_MASK (0x7 << 12) +#define RT5616_BB_CT_SFT 12 +#define RT5616_BB_CT_A (0x0 << 12) +#define RT5616_BB_CT_B (0x1 << 12) +#define RT5616_BB_CT_C (0x2 << 12) +#define RT5616_BB_CT_D (0x3 << 12) +#define RT5616_M_BB_L_MASK (0x1 << 9) +#define RT5616_M_BB_L_SFT 9 +#define RT5616_M_BB_R_MASK (0x1 << 8) +#define RT5616_M_BB_R_SFT 8 +#define RT5616_M_BB_HPF_L_MASK (0x1 << 7) +#define RT5616_M_BB_HPF_L_SFT 7 +#define RT5616_M_BB_HPF_R_MASK (0x1 << 6) +#define RT5616_M_BB_HPF_R_SFT 6 +#define RT5616_G_BB_BST_MASK (0x3f) +#define RT5616_G_BB_BST_SFT 0 + +/* MP3 Plus Control 1 (0xd0) */ +#define RT5616_M_MP3_L_MASK (0x1 << 15) +#define RT5616_M_MP3_L_SFT 15 +#define RT5616_M_MP3_R_MASK (0x1 << 14) +#define RT5616_M_MP3_R_SFT 14 +#define RT5616_M_MP3_MASK (0x1 << 13) +#define RT5616_M_MP3_SFT 13 +#define RT5616_M_MP3_DIS (0x0 << 13) +#define RT5616_M_MP3_EN (0x1 << 13) +#define RT5616_EG_MP3_MASK (0x1f << 8) +#define RT5616_EG_MP3_SFT 8 +#define RT5616_MP3_HLP_MASK (0x1 << 7) +#define RT5616_MP3_HLP_SFT 7 +#define RT5616_MP3_HLP_DIS (0x0 << 7) +#define RT5616_MP3_HLP_EN (0x1 << 7) +#define RT5616_M_MP3_ORG_L_MASK (0x1 << 6) +#define RT5616_M_MP3_ORG_L_SFT 6 +#define RT5616_M_MP3_ORG_R_MASK (0x1 << 5) +#define RT5616_M_MP3_ORG_R_SFT 5 + +/* MP3 Plus Control 2 (0xd1) */ +#define RT5616_MP3_WT_MASK (0x1 << 13) +#define RT5616_MP3_WT_SFT 13 +#define RT5616_MP3_WT_1_4 (0x0 << 13) +#define RT5616_MP3_WT_1_2 (0x1 << 13) +#define RT5616_OG_MP3_MASK (0x1f << 8) +#define RT5616_OG_MP3_SFT 8 +#define RT5616_HG_MP3_MASK (0x3f) +#define RT5616_HG_MP3_SFT 0 + +/* 3D HP Control 1 (0xd2) */ +#define RT5616_3D_CF_MASK (0x1 << 15) +#define RT5616_3D_CF_SFT 15 +#define RT5616_3D_CF_DIS (0x0 << 15) +#define RT5616_3D_CF_EN (0x1 << 15) +#define RT5616_3D_HP_MASK (0x1 << 14) +#define RT5616_3D_HP_SFT 14 +#define RT5616_3D_HP_DIS (0x0 << 14) +#define RT5616_3D_HP_EN (0x1 << 14) +#define RT5616_3D_BT_MASK (0x1 << 13) +#define RT5616_3D_BT_SFT 13 +#define RT5616_3D_BT_DIS (0x0 << 13) +#define RT5616_3D_BT_EN (0x1 << 13) +#define RT5616_3D_1F_MIX_MASK (0x3 << 11) +#define RT5616_3D_1F_MIX_SFT 11 +#define RT5616_3D_HP_M_MASK (0x1 << 10) +#define RT5616_3D_HP_M_SFT 10 +#define RT5616_3D_HP_M_SUR (0x0 << 10) +#define RT5616_3D_HP_M_FRO (0x1 << 10) +#define RT5616_M_3D_HRTF_MASK (0x1 << 9) +#define RT5616_M_3D_HRTF_SFT 9 +#define RT5616_M_3D_D2H_MASK (0x1 << 8) +#define RT5616_M_3D_D2H_SFT 8 +#define RT5616_M_3D_D2R_MASK (0x1 << 7) +#define RT5616_M_3D_D2R_SFT 7 +#define RT5616_M_3D_REVB_MASK (0x1 << 6) +#define RT5616_M_3D_REVB_SFT 6 + +/* Adjustable high pass filter control 1 (0xd3) */ +#define RT5616_2ND_HPF_MASK (0x1 << 15) +#define RT5616_2ND_HPF_SFT 15 +#define RT5616_2ND_HPF_DIS (0x0 << 15) +#define RT5616_2ND_HPF_EN (0x1 << 15) +#define RT5616_HPF_CF_L_MASK (0x7 << 12) +#define RT5616_HPF_CF_L_SFT 12 +#define RT5616_HPF_CF_R_MASK (0x7 << 8) +#define RT5616_HPF_CF_R_SFT 8 +#define RT5616_ZD_T_MASK (0x3 << 6) +#define RT5616_ZD_T_SFT 6 +#define RT5616_ZD_F_MASK (0x3 << 4) +#define RT5616_ZD_F_SFT 4 +#define RT5616_ZD_F_IM (0x0 << 4) +#define RT5616_ZD_F_ZC_IM (0x1 << 4) +#define RT5616_ZD_F_ZC_IOD (0x2 << 4) +#define RT5616_ZD_F_UN (0x3 << 4) + +/* Adjustable high pass filter control 2 (0xd4) */ +#define RT5616_HPF_CF_L_NUM_MASK (0x3f << 8) +#define RT5616_HPF_CF_L_NUM_SFT 8 +#define RT5616_HPF_CF_R_NUM_MASK (0x3f) +#define RT5616_HPF_CF_R_NUM_SFT 0 + +/* HP calibration control and Amp detection (0xd6) */ +#define RT5616_SI_DAC_MASK (0x1 << 11) +#define RT5616_SI_DAC_SFT 11 +#define RT5616_SI_DAC_AUTO (0x0 << 11) +#define RT5616_SI_DAC_TEST (0x1 << 11) +#define RT5616_DC_CAL_M_MASK (0x1 << 10) +#define RT5616_DC_CAL_M_SFT 10 +#define RT5616_DC_CAL_M_NOR (0x0 << 10) +#define RT5616_DC_CAL_M_CAL (0x1 << 10) +#define RT5616_DC_CAL_MASK (0x1 << 9) +#define RT5616_DC_CAL_SFT 9 +#define RT5616_DC_CAL_DIS (0x0 << 9) +#define RT5616_DC_CAL_EN (0x1 << 9) +#define RT5616_HPD_RCV_MASK (0x7 << 6) +#define RT5616_HPD_RCV_SFT 6 +#define RT5616_HPD_PS_MASK (0x1 << 5) +#define RT5616_HPD_PS_SFT 5 +#define RT5616_HPD_PS_DIS (0x0 << 5) +#define RT5616_HPD_PS_EN (0x1 << 5) +#define RT5616_CAL_M_MASK (0x1 << 4) +#define RT5616_CAL_M_SFT 4 +#define RT5616_CAL_M_DEP (0x0 << 4) +#define RT5616_CAL_M_CAL (0x1 << 4) +#define RT5616_CAL_MASK (0x1 << 3) +#define RT5616_CAL_SFT 3 +#define RT5616_CAL_DIS (0x0 << 3) +#define RT5616_CAL_EN (0x1 << 3) +#define RT5616_CAL_TEST_MASK (0x1 << 2) +#define RT5616_CAL_TEST_SFT 2 +#define RT5616_CAL_TEST_DIS (0x0 << 2) +#define RT5616_CAL_TEST_EN (0x1 << 2) +#define RT5616_CAL_P_MASK (0x3) +#define RT5616_CAL_P_SFT 0 +#define RT5616_CAL_P_NONE (0x0) +#define RT5616_CAL_P_CAL (0x1) +#define RT5616_CAL_P_DAC_CAL (0x2) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5616_SV_MASK (0x1 << 15) +#define RT5616_SV_SFT 15 +#define RT5616_SV_DIS (0x0 << 15) +#define RT5616_SV_EN (0x1 << 15) +#define RT5616_OUT_SV_MASK (0x1 << 13) +#define RT5616_OUT_SV_SFT 13 +#define RT5616_OUT_SV_DIS (0x0 << 13) +#define RT5616_OUT_SV_EN (0x1 << 13) +#define RT5616_HP_SV_MASK (0x1 << 12) +#define RT5616_HP_SV_SFT 12 +#define RT5616_HP_SV_DIS (0x0 << 12) +#define RT5616_HP_SV_EN (0x1 << 12) +#define RT5616_ZCD_DIG_MASK (0x1 << 11) +#define RT5616_ZCD_DIG_SFT 11 +#define RT5616_ZCD_DIG_DIS (0x0 << 11) +#define RT5616_ZCD_DIG_EN (0x1 << 11) +#define RT5616_ZCD_MASK (0x1 << 10) +#define RT5616_ZCD_SFT 10 +#define RT5616_ZCD_PD (0x0 << 10) +#define RT5616_ZCD_PU (0x1 << 10) +#define RT5616_M_ZCD_MASK (0x3f << 4) +#define RT5616_M_ZCD_SFT 4 +#define RT5616_M_ZCD_OM_L (0x1 << 7) +#define RT5616_M_ZCD_OM_R (0x1 << 6) +#define RT5616_M_ZCD_RM_L (0x1 << 5) +#define RT5616_M_ZCD_RM_R (0x1 << 4) +#define RT5616_SV_DLY_MASK (0xf) +#define RT5616_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5616_ZCD_HP_MASK (0x1 << 15) +#define RT5616_ZCD_HP_SFT 15 +#define RT5616_ZCD_HP_DIS (0x0 << 15) +#define RT5616_ZCD_HP_EN (0x1 << 15) + +/* Digital Misc Control (0xfa) */ +#define RT5616_I2S2_MS_SP_MASK (0x1 << 8) +#define RT5616_I2S2_MS_SP_SEL 8 +#define RT5616_I2S2_MS_SP_64 (0x0 << 8) +#define RT5616_I2S2_MS_SP_50 (0x1 << 8) +#define RT5616_CLK_DET_EN (0x1 << 3) +#define RT5616_CLK_DET_EN_SFT 3 +#define RT5616_AMP_DET_EN (0x1 << 1) +#define RT5616_AMP_DET_EN_SFT 1 +#define RT5616_D_GATE_EN (0x1) +#define RT5616_D_GATE_EN_SFT 0 + +/* Codec Private Register definition */ +/* 3D Speaker Control (0x63) */ +#define RT5616_3D_SPK_MASK (0x1 << 15) +#define RT5616_3D_SPK_SFT 15 +#define RT5616_3D_SPK_DIS (0x0 << 15) +#define RT5616_3D_SPK_EN (0x1 << 15) +#define RT5616_3D_SPK_M_MASK (0x3 << 13) +#define RT5616_3D_SPK_M_SFT 13 +#define RT5616_3D_SPK_CG_MASK (0x1f << 8) +#define RT5616_3D_SPK_CG_SFT 8 +#define RT5616_3D_SPK_SG_MASK (0x1f) +#define RT5616_3D_SPK_SG_SFT 0 + +/* Wind Noise Detection Control 1 (0x6c) */ +#define RT5616_WND_MASK (0x1 << 15) +#define RT5616_WND_SFT 15 +#define RT5616_WND_DIS (0x0 << 15) +#define RT5616_WND_EN (0x1 << 15) + +/* Wind Noise Detection Control 2 (0x6d) */ +#define RT5616_WND_FC_NW_MASK (0x3f << 10) +#define RT5616_WND_FC_NW_SFT 10 +#define RT5616_WND_FC_WK_MASK (0x3f << 4) +#define RT5616_WND_FC_WK_SFT 4 + +/* Wind Noise Detection Control 3 (0x6e) */ +#define RT5616_HPF_FC_MASK (0x3f << 6) +#define RT5616_HPF_FC_SFT 6 +#define RT5616_WND_FC_ST_MASK (0x3f) +#define RT5616_WND_FC_ST_SFT 0 + +/* Wind Noise Detection Control 4 (0x6f) */ +#define RT5616_WND_TH_LO_MASK (0x3ff) +#define RT5616_WND_TH_LO_SFT 0 + +/* Wind Noise Detection Control 5 (0x70) */ +#define RT5616_WND_TH_HI_MASK (0x3ff) +#define RT5616_WND_TH_HI_SFT 0 + +/* Wind Noise Detection Control 8 (0x73) */ +#define RT5616_WND_WIND_MASK (0x1 << 13) /* Read-Only */ +#define RT5616_WND_WIND_SFT 13 +#define RT5616_WND_STRONG_MASK (0x1 << 12) /* Read-Only */ +#define RT5616_WND_STRONG_SFT 12 +enum { + RT5616_NO_WIND, + RT5616_BREEZE, + RT5616_STORM, +}; + +/* Dipole Speaker Interface (0x75) */ +#define RT5616_DP_ATT_MASK (0x3 << 14) +#define RT5616_DP_ATT_SFT 14 +#define RT5616_DP_SPK_MASK (0x1 << 10) +#define RT5616_DP_SPK_SFT 10 +#define RT5616_DP_SPK_DIS (0x0 << 10) +#define RT5616_DP_SPK_EN (0x1 << 10) + +/* EQ Pre Volume Control (0xb3) */ +#define RT5616_EQ_PRE_VOL_MASK (0xffff) +#define RT5616_EQ_PRE_VOL_SFT 0 + +/* EQ Post Volume Control (0xb4) */ +#define RT5616_EQ_PST_VOL_MASK (0xffff) +#define RT5616_EQ_PST_VOL_SFT 0 + +/* System Clock Source */ +enum { + RT5616_SCLK_S_MCLK, + RT5616_SCLK_S_PLL1, +}; + +/* PLL1 Source */ +enum { + RT5616_PLL1_S_MCLK, + RT5616_PLL1_S_BCLK1, + RT5616_PLL1_S_BCLK2, +}; + +enum { + RT5616_AIF1, + RT5616_AIFS, +}; + +#endif /* __RT5616_H__ */ diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c new file mode 100644 index 0000000000..a64e66c2d3 --- /dev/null +++ b/sound/soc/codecs/rt5631.c @@ -0,0 +1,1740 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5631.c -- RT5631 ALSA Soc Audio driver + * + * Copyright 2011 Realtek Microelectronics + * + * Author: flove + * + * Based on WM8753.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5631.h" + +struct rt5631_priv { + struct regmap *regmap; + int codec_version; + int master; + int sysclk; + int rx_rate; + int bclk_rate; + int dmic_used_flag; +}; + +static const struct reg_default rt5631_reg[] = { + { RT5631_SPK_OUT_VOL, 0x8888 }, + { RT5631_HP_OUT_VOL, 0x8080 }, + { RT5631_MONO_AXO_1_2_VOL, 0xa080 }, + { RT5631_AUX_IN_VOL, 0x0808 }, + { RT5631_ADC_REC_MIXER, 0xf0f0 }, + { RT5631_VDAC_DIG_VOL, 0x0010 }, + { RT5631_OUTMIXER_L_CTRL, 0xffc0 }, + { RT5631_OUTMIXER_R_CTRL, 0xffc0 }, + { RT5631_AXO1MIXER_CTRL, 0x88c0 }, + { RT5631_AXO2MIXER_CTRL, 0x88c0 }, + { RT5631_DIG_MIC_CTRL, 0x3000 }, + { RT5631_MONO_INPUT_VOL, 0x8808 }, + { RT5631_SPK_MIXER_CTRL, 0xf8f8 }, + { RT5631_SPK_MONO_OUT_CTRL, 0xfc00 }, + { RT5631_SPK_MONO_HP_OUT_CTRL, 0x4440 }, + { RT5631_SDP_CTRL, 0x8000 }, + { RT5631_MONO_SDP_CTRL, 0x8000 }, + { RT5631_STEREO_AD_DA_CLK_CTRL, 0x2010 }, + { RT5631_GEN_PUR_CTRL_REG, 0x0e00 }, + { RT5631_INT_ST_IRQ_CTRL_2, 0x071a }, + { RT5631_MISC_CTRL, 0x2040 }, + { RT5631_DEPOP_FUN_CTRL_2, 0x8000 }, + { RT5631_SOFT_VOL_CTRL, 0x07e0 }, + { RT5631_ALC_CTRL_1, 0x0206 }, + { RT5631_ALC_CTRL_3, 0x2000 }, + { RT5631_PSEUDO_SPATL_CTRL, 0x0553 }, +}; + +/* + * rt5631_write_index - write index register of 2nd layer + */ +static void rt5631_write_index(struct snd_soc_component *component, + unsigned int reg, unsigned int value) +{ + snd_soc_component_write(component, RT5631_INDEX_ADD, reg); + snd_soc_component_write(component, RT5631_INDEX_DATA, value); +} + +/* + * rt5631_read_index - read index register of 2nd layer + */ +static unsigned int rt5631_read_index(struct snd_soc_component *component, + unsigned int reg) +{ + unsigned int value; + + snd_soc_component_write(component, RT5631_INDEX_ADD, reg); + value = snd_soc_component_read(component, RT5631_INDEX_DATA); + + return value; +} + +static int rt5631_reset(struct snd_soc_component *component) +{ + return snd_soc_component_write(component, RT5631_RESET, 0); +} + +static bool rt5631_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5631_RESET: + case RT5631_INT_ST_IRQ_CTRL_2: + case RT5631_INDEX_ADD: + case RT5631_INDEX_DATA: + case RT5631_EQ_CTRL: + return true; + default: + return false; + } +} + +static bool rt5631_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5631_RESET: + case RT5631_SPK_OUT_VOL: + case RT5631_HP_OUT_VOL: + case RT5631_MONO_AXO_1_2_VOL: + case RT5631_AUX_IN_VOL: + case RT5631_STEREO_DAC_VOL_1: + case RT5631_MIC_CTRL_1: + case RT5631_STEREO_DAC_VOL_2: + case RT5631_ADC_CTRL_1: + case RT5631_ADC_REC_MIXER: + case RT5631_ADC_CTRL_2: + case RT5631_VDAC_DIG_VOL: + case RT5631_OUTMIXER_L_CTRL: + case RT5631_OUTMIXER_R_CTRL: + case RT5631_AXO1MIXER_CTRL: + case RT5631_AXO2MIXER_CTRL: + case RT5631_MIC_CTRL_2: + case RT5631_DIG_MIC_CTRL: + case RT5631_MONO_INPUT_VOL: + case RT5631_SPK_MIXER_CTRL: + case RT5631_SPK_MONO_OUT_CTRL: + case RT5631_SPK_MONO_HP_OUT_CTRL: + case RT5631_SDP_CTRL: + case RT5631_MONO_SDP_CTRL: + case RT5631_STEREO_AD_DA_CLK_CTRL: + case RT5631_PWR_MANAG_ADD1: + case RT5631_PWR_MANAG_ADD2: + case RT5631_PWR_MANAG_ADD3: + case RT5631_PWR_MANAG_ADD4: + case RT5631_GEN_PUR_CTRL_REG: + case RT5631_GLOBAL_CLK_CTRL: + case RT5631_PLL_CTRL: + case RT5631_INT_ST_IRQ_CTRL_1: + case RT5631_INT_ST_IRQ_CTRL_2: + case RT5631_GPIO_CTRL: + case RT5631_MISC_CTRL: + case RT5631_DEPOP_FUN_CTRL_1: + case RT5631_DEPOP_FUN_CTRL_2: + case RT5631_JACK_DET_CTRL: + case RT5631_SOFT_VOL_CTRL: + case RT5631_ALC_CTRL_1: + case RT5631_ALC_CTRL_2: + case RT5631_ALC_CTRL_3: + case RT5631_PSEUDO_SPATL_CTRL: + case RT5631_INDEX_ADD: + case RT5631_INDEX_DATA: + case RT5631_EQ_CTRL: + case RT5631_VENDOR_ID: + case RT5631_VENDOR_ID1: + case RT5631_VENDOR_ID2: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -95625, 375, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +/* {0, +20, +24, +30, +35, +40, +44, +50, +52}dB */ +static const DECLARE_TLV_DB_RANGE(mic_bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +static int rt5631_dmic_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt5631->dmic_used_flag; + + return 0; +} + +static int rt5631_dmic_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + + rt5631->dmic_used_flag = ucontrol->value.integer.value[0]; + return 0; +} + +/* MIC Input Type */ +static const char *rt5631_input_mode[] = { + "Single ended", "Differential"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_mic1_mode_enum, RT5631_MIC_CTRL_1, + RT5631_MIC1_DIFF_INPUT_SHIFT, rt5631_input_mode); + +static SOC_ENUM_SINGLE_DECL(rt5631_mic2_mode_enum, RT5631_MIC_CTRL_1, + RT5631_MIC2_DIFF_INPUT_SHIFT, rt5631_input_mode); + +/* MONO Input Type */ +static SOC_ENUM_SINGLE_DECL(rt5631_monoin_mode_enum, RT5631_MONO_INPUT_VOL, + RT5631_MONO_DIFF_INPUT_SHIFT, rt5631_input_mode); + +/* SPK Ratio Gain Control */ +static const char *rt5631_spk_ratio[] = {"1.00x", "1.09x", "1.27x", "1.44x", + "1.56x", "1.68x", "1.99x", "2.34x"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG, + RT5631_SPK_AMP_RATIO_CTRL_SHIFT, rt5631_spk_ratio); + +static const struct snd_kcontrol_new rt5631_snd_controls[] = { + /* MIC */ + SOC_ENUM("MIC1 Mode Control", rt5631_mic1_mode_enum), + SOC_SINGLE_TLV("MIC1 Boost Volume", RT5631_MIC_CTRL_2, + RT5631_MIC1_BOOST_SHIFT, 8, 0, mic_bst_tlv), + SOC_ENUM("MIC2 Mode Control", rt5631_mic2_mode_enum), + SOC_SINGLE_TLV("MIC2 Boost Volume", RT5631_MIC_CTRL_2, + RT5631_MIC2_BOOST_SHIFT, 8, 0, mic_bst_tlv), + /* MONO IN */ + SOC_ENUM("MONOIN Mode Control", rt5631_monoin_mode_enum), + SOC_DOUBLE_TLV("MONOIN_RX Capture Volume", RT5631_MONO_INPUT_VOL, + RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT, + RT5631_VOL_MASK, 1, in_vol_tlv), + /* AXI */ + SOC_DOUBLE_TLV("AXI Capture Volume", RT5631_AUX_IN_VOL, + RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT, + RT5631_VOL_MASK, 1, in_vol_tlv), + /* DAC */ + SOC_DOUBLE_TLV("PCM Playback Volume", RT5631_STEREO_DAC_VOL_2, + RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT, + RT5631_DAC_VOL_MASK, 1, dac_vol_tlv), + SOC_DOUBLE("PCM Playback Switch", RT5631_STEREO_DAC_VOL_1, + RT5631_L_MUTE_SHIFT, RT5631_R_MUTE_SHIFT, 1, 1), + /* AXO */ + SOC_SINGLE("AXO1 Playback Switch", RT5631_MONO_AXO_1_2_VOL, + RT5631_L_MUTE_SHIFT, 1, 1), + SOC_SINGLE("AXO2 Playback Switch", RT5631_MONO_AXO_1_2_VOL, + RT5631_R_VOL_SHIFT, 1, 1), + /* OUTVOL */ + SOC_DOUBLE("OUTVOL Channel Switch", RT5631_SPK_OUT_VOL, + RT5631_L_EN_SHIFT, RT5631_R_EN_SHIFT, 1, 0), + + /* SPK */ + SOC_DOUBLE("Speaker Playback Switch", RT5631_SPK_OUT_VOL, + RT5631_L_MUTE_SHIFT, RT5631_R_MUTE_SHIFT, 1, 1), + SOC_DOUBLE_TLV("Speaker Playback Volume", RT5631_SPK_OUT_VOL, + RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT, 39, 1, out_vol_tlv), + /* MONO OUT */ + SOC_SINGLE("MONO Playback Switch", RT5631_MONO_AXO_1_2_VOL, + RT5631_MUTE_MONO_SHIFT, 1, 1), + /* HP */ + SOC_DOUBLE("HP Playback Switch", RT5631_HP_OUT_VOL, + RT5631_L_MUTE_SHIFT, RT5631_R_MUTE_SHIFT, 1, 1), + SOC_DOUBLE_TLV("HP Playback Volume", RT5631_HP_OUT_VOL, + RT5631_L_VOL_SHIFT, RT5631_R_VOL_SHIFT, + RT5631_VOL_MASK, 1, out_vol_tlv), + /* DMIC */ + SOC_SINGLE_EXT("DMIC Switch", 0, 0, 1, 0, + rt5631_dmic_get, rt5631_dmic_put), + SOC_DOUBLE("DMIC Capture Switch", RT5631_DIG_MIC_CTRL, + RT5631_DMIC_L_CH_MUTE_SHIFT, + RT5631_DMIC_R_CH_MUTE_SHIFT, 1, 1), + + /* SPK Ratio Gain Control */ + SOC_ENUM("SPK Ratio Control", rt5631_spk_ratio_enum), +}; + +static int check_sysclk1_source(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int reg; + + reg = snd_soc_component_read(component, RT5631_GLOBAL_CLK_CTRL); + return reg & RT5631_SYSCLK_SOUR_SEL_PLL; +} + +static int check_dmic_used(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + return rt5631->dmic_used_flag; +} + +static int check_dacl_to_outmixl(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int reg; + + reg = snd_soc_component_read(component, RT5631_OUTMIXER_L_CTRL); + return !(reg & RT5631_M_DAC_L_TO_OUTMIXER_L); +} + +static int check_dacr_to_outmixr(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int reg; + + reg = snd_soc_component_read(component, RT5631_OUTMIXER_R_CTRL); + return !(reg & RT5631_M_DAC_R_TO_OUTMIXER_R); +} + +static int check_dacl_to_spkmixl(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int reg; + + reg = snd_soc_component_read(component, RT5631_SPK_MIXER_CTRL); + return !(reg & RT5631_M_DAC_L_TO_SPKMIXER_L); +} + +static int check_dacr_to_spkmixr(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int reg; + + reg = snd_soc_component_read(component, RT5631_SPK_MIXER_CTRL); + return !(reg & RT5631_M_DAC_R_TO_SPKMIXER_R); +} + +static int check_adcl_select(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int reg; + + reg = snd_soc_component_read(component, RT5631_ADC_REC_MIXER); + return !(reg & RT5631_M_MIC1_TO_RECMIXER_L); +} + +static int check_adcr_select(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int reg; + + reg = snd_soc_component_read(component, RT5631_ADC_REC_MIXER); + return !(reg & RT5631_M_MIC2_TO_RECMIXER_R); +} + +/** + * onebit_depop_power_stage - auto depop in power stage. + * @component: ASoC component + * @enable: power on/off + * + * When power on/off headphone, the depop sequence is done by hardware. + */ +static void onebit_depop_power_stage(struct snd_soc_component *component, int enable) +{ + unsigned int soft_vol, hp_zc; + + /* enable one-bit depop function */ + snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2, + RT5631_EN_ONE_BIT_DEPOP, 0); + + /* keep soft volume and zero crossing setting */ + soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL); + snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0); + hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2); + snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff); + if (enable) { + /* config one-bit depop parameter */ + rt5631_write_index(component, RT5631_TEST_MODE_CTRL, 0x84c0); + rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x309f); + rt5631_write_index(component, RT5631_CP_INTL_REG2, 0x6530); + /* power on capless block */ + snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_2, + RT5631_EN_CAP_FREE_DEPOP); + } else { + /* power off capless block */ + snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_2, 0); + msleep(100); + } + + /* recover soft volume and zero crossing setting */ + snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol); + snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc); +} + +/** + * onebit_depop_mute_stage - auto depop in mute stage. + * @component: ASoC component + * @enable: mute/unmute + * + * When mute/unmute headphone, the depop sequence is done by hardware. + */ +static void onebit_depop_mute_stage(struct snd_soc_component *component, int enable) +{ + unsigned int soft_vol, hp_zc; + + /* enable one-bit depop function */ + snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2, + RT5631_EN_ONE_BIT_DEPOP, 0); + + /* keep soft volume and zero crossing setting */ + soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL); + snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0); + hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2); + snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff); + if (enable) { + schedule_timeout_uninterruptible(msecs_to_jiffies(10)); + /* config one-bit depop parameter */ + rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x307f); + snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL, + RT5631_L_MUTE | RT5631_R_MUTE, 0); + msleep(300); + } else { + snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL, + RT5631_L_MUTE | RT5631_R_MUTE, + RT5631_L_MUTE | RT5631_R_MUTE); + msleep(100); + } + + /* recover soft volume and zero crossing setting */ + snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol); + snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc); +} + +/** + * depop_seq_power_stage - step by step depop sequence in power stage. + * @component: ASoC component + * @enable: power on/off + * + * When power on/off headphone, the depop sequence is done in step by step. + */ +static void depop_seq_power_stage(struct snd_soc_component *component, int enable) +{ + unsigned int soft_vol, hp_zc; + + /* depop control by register */ + snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2, + RT5631_EN_ONE_BIT_DEPOP, RT5631_EN_ONE_BIT_DEPOP); + + /* keep soft volume and zero crossing setting */ + soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL); + snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0); + hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2); + snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff); + if (enable) { + /* config depop sequence parameter */ + rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x303e); + + /* power on headphone and charge pump */ + snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3, + RT5631_PWR_CHARGE_PUMP | RT5631_PWR_HP_L_AMP | + RT5631_PWR_HP_R_AMP, + RT5631_PWR_CHARGE_PUMP | RT5631_PWR_HP_L_AMP | + RT5631_PWR_HP_R_AMP); + + /* power on soft generator and depop mode2 */ + snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1, + RT5631_POW_ON_SOFT_GEN | RT5631_EN_DEPOP2_FOR_HP); + msleep(100); + + /* stop depop mode */ + snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3, + RT5631_PWR_HP_DEPOP_DIS, RT5631_PWR_HP_DEPOP_DIS); + } else { + /* config depop sequence parameter */ + rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x303F); + snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1, + RT5631_POW_ON_SOFT_GEN | RT5631_EN_MUTE_UNMUTE_DEPOP | + RT5631_PD_HPAMP_L_ST_UP | RT5631_PD_HPAMP_R_ST_UP); + msleep(75); + snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1, + RT5631_POW_ON_SOFT_GEN | RT5631_PD_HPAMP_L_ST_UP | + RT5631_PD_HPAMP_R_ST_UP); + + /* start depop mode */ + snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3, + RT5631_PWR_HP_DEPOP_DIS, 0); + + /* config depop sequence parameter */ + snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1, + RT5631_POW_ON_SOFT_GEN | RT5631_EN_DEPOP2_FOR_HP | + RT5631_PD_HPAMP_L_ST_UP | RT5631_PD_HPAMP_R_ST_UP); + msleep(80); + snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1, + RT5631_POW_ON_SOFT_GEN); + + /* power down headphone and charge pump */ + snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3, + RT5631_PWR_CHARGE_PUMP | RT5631_PWR_HP_L_AMP | + RT5631_PWR_HP_R_AMP, 0); + } + + /* recover soft volume and zero crossing setting */ + snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol); + snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc); +} + +/** + * depop_seq_mute_stage - step by step depop sequence in mute stage. + * @component: ASoC component + * @enable: mute/unmute + * + * When mute/unmute headphone, the depop sequence is done in step by step. + */ +static void depop_seq_mute_stage(struct snd_soc_component *component, int enable) +{ + unsigned int soft_vol, hp_zc; + + /* depop control by register */ + snd_soc_component_update_bits(component, RT5631_DEPOP_FUN_CTRL_2, + RT5631_EN_ONE_BIT_DEPOP, RT5631_EN_ONE_BIT_DEPOP); + + /* keep soft volume and zero crossing setting */ + soft_vol = snd_soc_component_read(component, RT5631_SOFT_VOL_CTRL); + snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, 0); + hp_zc = snd_soc_component_read(component, RT5631_INT_ST_IRQ_CTRL_2); + snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff); + if (enable) { + schedule_timeout_uninterruptible(msecs_to_jiffies(10)); + + /* config depop sequence parameter */ + rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x302f); + snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1, + RT5631_POW_ON_SOFT_GEN | RT5631_EN_MUTE_UNMUTE_DEPOP | + RT5631_EN_HP_R_M_UN_MUTE_DEPOP | + RT5631_EN_HP_L_M_UN_MUTE_DEPOP); + + snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL, + RT5631_L_MUTE | RT5631_R_MUTE, 0); + msleep(160); + } else { + /* config depop sequence parameter */ + rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x302f); + snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1, + RT5631_POW_ON_SOFT_GEN | RT5631_EN_MUTE_UNMUTE_DEPOP | + RT5631_EN_HP_R_M_UN_MUTE_DEPOP | + RT5631_EN_HP_L_M_UN_MUTE_DEPOP); + + snd_soc_component_update_bits(component, RT5631_HP_OUT_VOL, + RT5631_L_MUTE | RT5631_R_MUTE, + RT5631_L_MUTE | RT5631_R_MUTE); + msleep(150); + } + + /* recover soft volume and zero crossing setting */ + snd_soc_component_write(component, RT5631_SOFT_VOL_CTRL, soft_vol); + snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, hp_zc); +} + +static int hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMD: + if (rt5631->codec_version) { + onebit_depop_mute_stage(component, 0); + onebit_depop_power_stage(component, 0); + } else { + depop_seq_mute_stage(component, 0); + depop_seq_power_stage(component, 0); + } + break; + + case SND_SOC_DAPM_POST_PMU: + if (rt5631->codec_version) { + onebit_depop_power_stage(component, 1); + onebit_depop_mute_stage(component, 1); + } else { + depop_seq_power_stage(component, 1); + depop_seq_mute_stage(component, 1); + } + break; + + default: + break; + } + + return 0; +} + +static int set_dmic_params(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + + switch (rt5631->rx_rate) { + case 44100: + case 48000: + snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL, + RT5631_DMIC_CLK_CTRL_MASK, + RT5631_DMIC_CLK_CTRL_TO_32FS); + break; + + case 32000: + case 22050: + snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL, + RT5631_DMIC_CLK_CTRL_MASK, + RT5631_DMIC_CLK_CTRL_TO_64FS); + break; + + case 16000: + case 11025: + case 8000: + snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL, + RT5631_DMIC_CLK_CTRL_MASK, + RT5631_DMIC_CLK_CTRL_TO_128FS); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_kcontrol_new rt5631_recmixl_mixer_controls[] = { + SOC_DAPM_SINGLE("OUTMIXL Capture Switch", RT5631_ADC_REC_MIXER, + RT5631_M_OUTMIXL_RECMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("MIC1_BST1 Capture Switch", RT5631_ADC_REC_MIXER, + RT5631_M_MIC1_RECMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("AXILVOL Capture Switch", RT5631_ADC_REC_MIXER, + RT5631_M_AXIL_RECMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("MONOIN_RX Capture Switch", RT5631_ADC_REC_MIXER, + RT5631_M_MONO_IN_RECMIXL_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_recmixr_mixer_controls[] = { + SOC_DAPM_SINGLE("MONOIN_RX Capture Switch", RT5631_ADC_REC_MIXER, + RT5631_M_MONO_IN_RECMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("AXIRVOL Capture Switch", RT5631_ADC_REC_MIXER, + RT5631_M_AXIR_RECMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("MIC2_BST2 Capture Switch", RT5631_ADC_REC_MIXER, + RT5631_M_MIC2_RECMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("OUTMIXR Capture Switch", RT5631_ADC_REC_MIXER, + RT5631_M_OUTMIXR_RECMIXR_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_spkmixl_mixer_controls[] = { + SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_SPK_MIXER_CTRL, + RT5631_M_RECMIXL_SPKMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("MIC1_P Playback Switch", RT5631_SPK_MIXER_CTRL, + RT5631_M_MIC1P_SPKMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("DACL Playback Switch", RT5631_SPK_MIXER_CTRL, + RT5631_M_DACL_SPKMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("OUTMIXL Playback Switch", RT5631_SPK_MIXER_CTRL, + RT5631_M_OUTMIXL_SPKMIXL_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_spkmixr_mixer_controls[] = { + SOC_DAPM_SINGLE("OUTMIXR Playback Switch", RT5631_SPK_MIXER_CTRL, + RT5631_M_OUTMIXR_SPKMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("DACR Playback Switch", RT5631_SPK_MIXER_CTRL, + RT5631_M_DACR_SPKMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("MIC2_P Playback Switch", RT5631_SPK_MIXER_CTRL, + RT5631_M_MIC2P_SPKMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_SPK_MIXER_CTRL, + RT5631_M_RECMIXR_SPKMIXR_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_outmixl_mixer_controls[] = { + SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_OUTMIXER_L_CTRL, + RT5631_M_RECMIXL_OUTMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_OUTMIXER_L_CTRL, + RT5631_M_RECMIXR_OUTMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("DACL Playback Switch", RT5631_OUTMIXER_L_CTRL, + RT5631_M_DACL_OUTMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_OUTMIXER_L_CTRL, + RT5631_M_MIC1_OUTMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_OUTMIXER_L_CTRL, + RT5631_M_MIC2_OUTMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("MONOIN_RXP Playback Switch", RT5631_OUTMIXER_L_CTRL, + RT5631_M_MONO_INP_OUTMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("AXILVOL Playback Switch", RT5631_OUTMIXER_L_CTRL, + RT5631_M_AXIL_OUTMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("AXIRVOL Playback Switch", RT5631_OUTMIXER_L_CTRL, + RT5631_M_AXIR_OUTMIXL_BIT, 1, 1), + SOC_DAPM_SINGLE("VDAC Playback Switch", RT5631_OUTMIXER_L_CTRL, + RT5631_M_VDAC_OUTMIXL_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_outmixr_mixer_controls[] = { + SOC_DAPM_SINGLE("VDAC Playback Switch", RT5631_OUTMIXER_R_CTRL, + RT5631_M_VDAC_OUTMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("AXIRVOL Playback Switch", RT5631_OUTMIXER_R_CTRL, + RT5631_M_AXIR_OUTMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("AXILVOL Playback Switch", RT5631_OUTMIXER_R_CTRL, + RT5631_M_AXIL_OUTMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("MONOIN_RXN Playback Switch", RT5631_OUTMIXER_R_CTRL, + RT5631_M_MONO_INN_OUTMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_OUTMIXER_R_CTRL, + RT5631_M_MIC2_OUTMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_OUTMIXER_R_CTRL, + RT5631_M_MIC1_OUTMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("DACR Playback Switch", RT5631_OUTMIXER_R_CTRL, + RT5631_M_DACR_OUTMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("RECMIXR Playback Switch", RT5631_OUTMIXER_R_CTRL, + RT5631_M_RECMIXR_OUTMIXR_BIT, 1, 1), + SOC_DAPM_SINGLE("RECMIXL Playback Switch", RT5631_OUTMIXER_R_CTRL, + RT5631_M_RECMIXL_OUTMIXR_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_AXO1MIX_mixer_controls[] = { + SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_AXO1MIXER_CTRL, + RT5631_M_MIC1_AXO1MIX_BIT , 1, 1), + SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_AXO1MIXER_CTRL, + RT5631_M_MIC2_AXO1MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_AXO1MIXER_CTRL, + RT5631_M_OUTMIXL_AXO1MIX_BIT , 1 , 1), + SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_AXO1MIXER_CTRL, + RT5631_M_OUTMIXR_AXO1MIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_AXO2MIX_mixer_controls[] = { + SOC_DAPM_SINGLE("MIC1_BST1 Playback Switch", RT5631_AXO2MIXER_CTRL, + RT5631_M_MIC1_AXO2MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("MIC2_BST2 Playback Switch", RT5631_AXO2MIXER_CTRL, + RT5631_M_MIC2_AXO2MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_AXO2MIXER_CTRL, + RT5631_M_OUTMIXL_AXO2MIX_BIT, 1, 1), + SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_AXO2MIXER_CTRL, + RT5631_M_OUTMIXR_AXO2MIX_BIT, 1 , 1), +}; + +static const struct snd_kcontrol_new rt5631_spolmix_mixer_controls[] = { + SOC_DAPM_SINGLE("SPKVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL, + RT5631_M_SPKVOLL_SPOLMIX_BIT, 1, 1), + SOC_DAPM_SINGLE("SPKVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL, + RT5631_M_SPKVOLR_SPOLMIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_spormix_mixer_controls[] = { + SOC_DAPM_SINGLE("SPKVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL, + RT5631_M_SPKVOLL_SPORMIX_BIT, 1, 1), + SOC_DAPM_SINGLE("SPKVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL, + RT5631_M_SPKVOLR_SPORMIX_BIT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5631_monomix_mixer_controls[] = { + SOC_DAPM_SINGLE("OUTVOLL Playback Switch", RT5631_SPK_MONO_OUT_CTRL, + RT5631_M_OUTVOLL_MONOMIX_BIT, 1, 1), + SOC_DAPM_SINGLE("OUTVOLR Playback Switch", RT5631_SPK_MONO_OUT_CTRL, + RT5631_M_OUTVOLR_MONOMIX_BIT, 1, 1), +}; + +/* Left SPK Volume Input */ +static const char *rt5631_spkvoll_sel[] = {"Vmid", "SPKMIXL"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_spkvoll_enum, RT5631_SPK_OUT_VOL, + RT5631_L_EN_SHIFT, rt5631_spkvoll_sel); + +static const struct snd_kcontrol_new rt5631_spkvoll_mux_control = + SOC_DAPM_ENUM("Left SPKVOL SRC", rt5631_spkvoll_enum); + +/* Left HP Volume Input */ +static const char *rt5631_hpvoll_sel[] = {"Vmid", "OUTMIXL"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_hpvoll_enum, RT5631_HP_OUT_VOL, + RT5631_L_EN_SHIFT, rt5631_hpvoll_sel); + +static const struct snd_kcontrol_new rt5631_hpvoll_mux_control = + SOC_DAPM_ENUM("Left HPVOL SRC", rt5631_hpvoll_enum); + +/* Left Out Volume Input */ +static const char *rt5631_outvoll_sel[] = {"Vmid", "OUTMIXL"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_outvoll_enum, RT5631_MONO_AXO_1_2_VOL, + RT5631_L_EN_SHIFT, rt5631_outvoll_sel); + +static const struct snd_kcontrol_new rt5631_outvoll_mux_control = + SOC_DAPM_ENUM("Left OUTVOL SRC", rt5631_outvoll_enum); + +/* Right Out Volume Input */ +static const char *rt5631_outvolr_sel[] = {"Vmid", "OUTMIXR"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_outvolr_enum, RT5631_MONO_AXO_1_2_VOL, + RT5631_R_EN_SHIFT, rt5631_outvolr_sel); + +static const struct snd_kcontrol_new rt5631_outvolr_mux_control = + SOC_DAPM_ENUM("Right OUTVOL SRC", rt5631_outvolr_enum); + +/* Right HP Volume Input */ +static const char *rt5631_hpvolr_sel[] = {"Vmid", "OUTMIXR"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_hpvolr_enum, RT5631_HP_OUT_VOL, + RT5631_R_EN_SHIFT, rt5631_hpvolr_sel); + +static const struct snd_kcontrol_new rt5631_hpvolr_mux_control = + SOC_DAPM_ENUM("Right HPVOL SRC", rt5631_hpvolr_enum); + +/* Right SPK Volume Input */ +static const char *rt5631_spkvolr_sel[] = {"Vmid", "SPKMIXR"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_spkvolr_enum, RT5631_SPK_OUT_VOL, + RT5631_R_EN_SHIFT, rt5631_spkvolr_sel); + +static const struct snd_kcontrol_new rt5631_spkvolr_mux_control = + SOC_DAPM_ENUM("Right SPKVOL SRC", rt5631_spkvolr_enum); + +/* SPO Left Channel Input */ +static const char *rt5631_spol_src_sel[] = { + "SPOLMIX", "MONOIN_RX", "VDAC", "DACL"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_spol_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, + RT5631_SPK_L_MUX_SEL_SHIFT, rt5631_spol_src_sel); + +static const struct snd_kcontrol_new rt5631_spol_mux_control = + SOC_DAPM_ENUM("SPOL SRC", rt5631_spol_src_enum); + +/* SPO Right Channel Input */ +static const char *rt5631_spor_src_sel[] = { + "SPORMIX", "MONOIN_RX", "VDAC", "DACR"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_spor_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, + RT5631_SPK_R_MUX_SEL_SHIFT, rt5631_spor_src_sel); + +static const struct snd_kcontrol_new rt5631_spor_mux_control = + SOC_DAPM_ENUM("SPOR SRC", rt5631_spor_src_enum); + +/* MONO Input */ +static const char *rt5631_mono_src_sel[] = {"MONOMIX", "MONOIN_RX", "VDAC"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_mono_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, + RT5631_MONO_MUX_SEL_SHIFT, rt5631_mono_src_sel); + +static const struct snd_kcontrol_new rt5631_mono_mux_control = + SOC_DAPM_ENUM("MONO SRC", rt5631_mono_src_enum); + +/* Left HPO Input */ +static const char *rt5631_hpl_src_sel[] = {"Left HPVOL", "Left DAC"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_hpl_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, + RT5631_HP_L_MUX_SEL_SHIFT, rt5631_hpl_src_sel); + +static const struct snd_kcontrol_new rt5631_hpl_mux_control = + SOC_DAPM_ENUM("HPL SRC", rt5631_hpl_src_enum); + +/* Right HPO Input */ +static const char *rt5631_hpr_src_sel[] = {"Right HPVOL", "Right DAC"}; + +static SOC_ENUM_SINGLE_DECL(rt5631_hpr_src_enum, RT5631_SPK_MONO_HP_OUT_CTRL, + RT5631_HP_R_MUX_SEL_SHIFT, rt5631_hpr_src_sel); + +static const struct snd_kcontrol_new rt5631_hpr_mux_control = + SOC_DAPM_ENUM("HPR SRC", rt5631_hpr_src_enum); + +static const struct snd_soc_dapm_widget rt5631_dapm_widgets[] = { + /* Vmid */ + SND_SOC_DAPM_VMID("Vmid"), + /* PLL1 */ + SND_SOC_DAPM_SUPPLY("PLL1", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_PLL1_BIT, 0, NULL, 0), + + /* Input Side */ + /* Input Lines */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("AXIL"), + SND_SOC_DAPM_INPUT("AXIR"), + SND_SOC_DAPM_INPUT("MONOIN_RXN"), + SND_SOC_DAPM_INPUT("MONOIN_RXP"), + SND_SOC_DAPM_INPUT("DMIC"), + + /* MICBIAS */ + SND_SOC_DAPM_MICBIAS("MIC Bias1", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_MICBIAS1_VOL_BIT, 0), + SND_SOC_DAPM_MICBIAS("MIC Bias2", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_MICBIAS2_VOL_BIT, 0), + + /* Boost */ + SND_SOC_DAPM_PGA("MIC1 Boost", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_MIC1_BOOT_GAIN_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC2 Boost", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_MIC2_BOOT_GAIN_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("MONOIN_RXP Boost", RT5631_PWR_MANAG_ADD4, + RT5631_PWR_MONO_IN_P_VOL_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("MONOIN_RXN Boost", RT5631_PWR_MANAG_ADD4, + RT5631_PWR_MONO_IN_N_VOL_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("AXIL Boost", RT5631_PWR_MANAG_ADD4, + RT5631_PWR_AXIL_IN_VOL_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("AXIR Boost", RT5631_PWR_MANAG_ADD4, + RT5631_PWR_AXIR_IN_VOL_BIT, 0, NULL, 0), + + /* MONO In */ + SND_SOC_DAPM_MIXER("MONO_IN", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL Mixer", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_RECMIXER_L_BIT, 0, + &rt5631_recmixl_mixer_controls[0], + ARRAY_SIZE(rt5631_recmixl_mixer_controls)), + SND_SOC_DAPM_MIXER("RECMIXR Mixer", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_RECMIXER_R_BIT, 0, + &rt5631_recmixr_mixer_controls[0], + ARRAY_SIZE(rt5631_recmixr_mixer_controls)), + /* Because of record duplication for L/R channel, + * L/R ADCs need power up at the same time */ + SND_SOC_DAPM_MIXER("ADC Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DMIC */ + SND_SOC_DAPM_SUPPLY("DMIC Supply", RT5631_DIG_MIC_CTRL, + RT5631_DMIC_ENA_SHIFT, 0, + set_dmic_params, SND_SOC_DAPM_PRE_PMU), + /* ADC Data Srouce */ + SND_SOC_DAPM_SUPPLY("Left ADC Select", RT5631_INT_ST_IRQ_CTRL_2, + RT5631_ADC_DATA_SEL_MIC1_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Right ADC Select", RT5631_INT_ST_IRQ_CTRL_2, + RT5631_ADC_DATA_SEL_MIC2_SHIFT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("Left ADC", "HIFI Capture", + RT5631_PWR_MANAG_ADD1, RT5631_PWR_ADC_L_CLK_BIT, 0), + SND_SOC_DAPM_ADC("Right ADC", "HIFI Capture", + RT5631_PWR_MANAG_ADD1, RT5631_PWR_ADC_R_CLK_BIT, 0), + + /* DAC and ADC supply power */ + SND_SOC_DAPM_SUPPLY("I2S", RT5631_PWR_MANAG_ADD1, + RT5631_PWR_MAIN_I2S_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC REF", RT5631_PWR_MANAG_ADD1, + RT5631_PWR_DAC_REF_BIT, 0, NULL, 0), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("Left DAC", "HIFI Playback", + RT5631_PWR_MANAG_ADD1, RT5631_PWR_DAC_L_CLK_BIT, 0), + SND_SOC_DAPM_DAC("Right DAC", "HIFI Playback", + RT5631_PWR_MANAG_ADD1, RT5631_PWR_DAC_R_CLK_BIT, 0), + SND_SOC_DAPM_DAC("Voice DAC", "Voice DAC Mono Playback", + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("Voice DAC Boost", SND_SOC_NOPM, 0, 0, NULL, 0), + /* DAC supply power */ + SND_SOC_DAPM_SUPPLY("Left DAC To Mixer", RT5631_PWR_MANAG_ADD1, + RT5631_PWR_DAC_L_TO_MIXER_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Right DAC To Mixer", RT5631_PWR_MANAG_ADD1, + RT5631_PWR_DAC_R_TO_MIXER_BIT, 0, NULL, 0), + + /* Left SPK Mixer */ + SND_SOC_DAPM_MIXER("SPKMIXL Mixer", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_SPKMIXER_L_BIT, 0, + &rt5631_spkmixl_mixer_controls[0], + ARRAY_SIZE(rt5631_spkmixl_mixer_controls)), + /* Left Out Mixer */ + SND_SOC_DAPM_MIXER("OUTMIXL Mixer", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_OUTMIXER_L_BIT, 0, + &rt5631_outmixl_mixer_controls[0], + ARRAY_SIZE(rt5631_outmixl_mixer_controls)), + /* Right Out Mixer */ + SND_SOC_DAPM_MIXER("OUTMIXR Mixer", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_OUTMIXER_R_BIT, 0, + &rt5631_outmixr_mixer_controls[0], + ARRAY_SIZE(rt5631_outmixr_mixer_controls)), + /* Right SPK Mixer */ + SND_SOC_DAPM_MIXER("SPKMIXR Mixer", RT5631_PWR_MANAG_ADD2, + RT5631_PWR_SPKMIXER_R_BIT, 0, + &rt5631_spkmixr_mixer_controls[0], + ARRAY_SIZE(rt5631_spkmixr_mixer_controls)), + + /* Volume Mux */ + SND_SOC_DAPM_MUX("Left SPKVOL Mux", RT5631_PWR_MANAG_ADD4, + RT5631_PWR_SPK_L_VOL_BIT, 0, + &rt5631_spkvoll_mux_control), + SND_SOC_DAPM_MUX("Left HPVOL Mux", RT5631_PWR_MANAG_ADD4, + RT5631_PWR_HP_L_OUT_VOL_BIT, 0, + &rt5631_hpvoll_mux_control), + SND_SOC_DAPM_MUX("Left OUTVOL Mux", RT5631_PWR_MANAG_ADD4, + RT5631_PWR_LOUT_VOL_BIT, 0, + &rt5631_outvoll_mux_control), + SND_SOC_DAPM_MUX("Right OUTVOL Mux", RT5631_PWR_MANAG_ADD4, + RT5631_PWR_ROUT_VOL_BIT, 0, + &rt5631_outvolr_mux_control), + SND_SOC_DAPM_MUX("Right HPVOL Mux", RT5631_PWR_MANAG_ADD4, + RT5631_PWR_HP_R_OUT_VOL_BIT, 0, + &rt5631_hpvolr_mux_control), + SND_SOC_DAPM_MUX("Right SPKVOL Mux", RT5631_PWR_MANAG_ADD4, + RT5631_PWR_SPK_R_VOL_BIT, 0, + &rt5631_spkvolr_mux_control), + + /* DAC To HP */ + SND_SOC_DAPM_PGA_S("Left DAC_HP", 0, SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("Right DAC_HP", 0, SND_SOC_NOPM, 0, 0, NULL, 0), + + /* HP Depop */ + SND_SOC_DAPM_PGA_S("HP Depop", 1, SND_SOC_NOPM, 0, 0, + hp_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + + /* AXO1 Mixer */ + SND_SOC_DAPM_MIXER("AXO1MIX Mixer", RT5631_PWR_MANAG_ADD3, + RT5631_PWR_AXO1MIXER_BIT, 0, + &rt5631_AXO1MIX_mixer_controls[0], + ARRAY_SIZE(rt5631_AXO1MIX_mixer_controls)), + /* SPOL Mixer */ + SND_SOC_DAPM_MIXER("SPOLMIX Mixer", SND_SOC_NOPM, 0, 0, + &rt5631_spolmix_mixer_controls[0], + ARRAY_SIZE(rt5631_spolmix_mixer_controls)), + /* MONO Mixer */ + SND_SOC_DAPM_MIXER("MONOMIX Mixer", RT5631_PWR_MANAG_ADD3, + RT5631_PWR_MONOMIXER_BIT, 0, + &rt5631_monomix_mixer_controls[0], + ARRAY_SIZE(rt5631_monomix_mixer_controls)), + /* SPOR Mixer */ + SND_SOC_DAPM_MIXER("SPORMIX Mixer", SND_SOC_NOPM, 0, 0, + &rt5631_spormix_mixer_controls[0], + ARRAY_SIZE(rt5631_spormix_mixer_controls)), + /* AXO2 Mixer */ + SND_SOC_DAPM_MIXER("AXO2MIX Mixer", RT5631_PWR_MANAG_ADD3, + RT5631_PWR_AXO2MIXER_BIT, 0, + &rt5631_AXO2MIX_mixer_controls[0], + ARRAY_SIZE(rt5631_AXO2MIX_mixer_controls)), + + /* Mux */ + SND_SOC_DAPM_MUX("SPOL Mux", SND_SOC_NOPM, 0, 0, + &rt5631_spol_mux_control), + SND_SOC_DAPM_MUX("SPOR Mux", SND_SOC_NOPM, 0, 0, + &rt5631_spor_mux_control), + SND_SOC_DAPM_MUX("MONO Mux", SND_SOC_NOPM, 0, 0, + &rt5631_mono_mux_control), + SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0, + &rt5631_hpl_mux_control), + SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0, + &rt5631_hpr_mux_control), + + /* AMP supply */ + SND_SOC_DAPM_SUPPLY("MONO Depop", RT5631_PWR_MANAG_ADD3, + RT5631_PWR_MONO_DEPOP_DIS_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Class D", RT5631_PWR_MANAG_ADD1, + RT5631_PWR_CLASS_D_BIT, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("AUXO1"), + SND_SOC_DAPM_OUTPUT("AUXO2"), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("MONO"), +}; + +static const struct snd_soc_dapm_route rt5631_dapm_routes[] = { + {"MIC1 Boost", NULL, "MIC1"}, + {"MIC2 Boost", NULL, "MIC2"}, + {"MONOIN_RXP Boost", NULL, "MONOIN_RXP"}, + {"MONOIN_RXN Boost", NULL, "MONOIN_RXN"}, + {"AXIL Boost", NULL, "AXIL"}, + {"AXIR Boost", NULL, "AXIR"}, + + {"MONO_IN", NULL, "MONOIN_RXP Boost"}, + {"MONO_IN", NULL, "MONOIN_RXN Boost"}, + + {"RECMIXL Mixer", "OUTMIXL Capture Switch", "OUTMIXL Mixer"}, + {"RECMIXL Mixer", "MIC1_BST1 Capture Switch", "MIC1 Boost"}, + {"RECMIXL Mixer", "AXILVOL Capture Switch", "AXIL Boost"}, + {"RECMIXL Mixer", "MONOIN_RX Capture Switch", "MONO_IN"}, + + {"RECMIXR Mixer", "OUTMIXR Capture Switch", "OUTMIXR Mixer"}, + {"RECMIXR Mixer", "MIC2_BST2 Capture Switch", "MIC2 Boost"}, + {"RECMIXR Mixer", "AXIRVOL Capture Switch", "AXIR Boost"}, + {"RECMIXR Mixer", "MONOIN_RX Capture Switch", "MONO_IN"}, + + {"ADC Mixer", NULL, "RECMIXL Mixer"}, + {"ADC Mixer", NULL, "RECMIXR Mixer"}, + + {"Left ADC", NULL, "ADC Mixer"}, + {"Left ADC", NULL, "Left ADC Select", check_adcl_select}, + {"Left ADC", NULL, "PLL1", check_sysclk1_source}, + {"Left ADC", NULL, "I2S"}, + {"Left ADC", NULL, "DAC REF"}, + + {"Right ADC", NULL, "ADC Mixer"}, + {"Right ADC", NULL, "Right ADC Select", check_adcr_select}, + {"Right ADC", NULL, "PLL1", check_sysclk1_source}, + {"Right ADC", NULL, "I2S"}, + {"Right ADC", NULL, "DAC REF"}, + + {"DMIC", NULL, "DMIC Supply", check_dmic_used}, + {"Left ADC", NULL, "DMIC"}, + {"Right ADC", NULL, "DMIC"}, + + {"Left DAC", NULL, "PLL1", check_sysclk1_source}, + {"Left DAC", NULL, "I2S"}, + {"Left DAC", NULL, "DAC REF"}, + {"Right DAC", NULL, "PLL1", check_sysclk1_source}, + {"Right DAC", NULL, "I2S"}, + {"Right DAC", NULL, "DAC REF"}, + + {"Voice DAC Boost", NULL, "Voice DAC"}, + + {"SPKMIXL Mixer", NULL, "Left DAC To Mixer", check_dacl_to_spkmixl}, + {"SPKMIXL Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"}, + {"SPKMIXL Mixer", "MIC1_P Playback Switch", "MIC1"}, + {"SPKMIXL Mixer", "DACL Playback Switch", "Left DAC"}, + {"SPKMIXL Mixer", "OUTMIXL Playback Switch", "OUTMIXL Mixer"}, + + {"SPKMIXR Mixer", NULL, "Right DAC To Mixer", check_dacr_to_spkmixr}, + {"SPKMIXR Mixer", "OUTMIXR Playback Switch", "OUTMIXR Mixer"}, + {"SPKMIXR Mixer", "DACR Playback Switch", "Right DAC"}, + {"SPKMIXR Mixer", "MIC2_P Playback Switch", "MIC2"}, + {"SPKMIXR Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"}, + + {"OUTMIXL Mixer", NULL, "Left DAC To Mixer", check_dacl_to_outmixl}, + {"OUTMIXL Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"}, + {"OUTMIXL Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"}, + {"OUTMIXL Mixer", "DACL Playback Switch", "Left DAC"}, + {"OUTMIXL Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"}, + {"OUTMIXL Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"}, + {"OUTMIXL Mixer", "MONOIN_RXP Playback Switch", "MONOIN_RXP Boost"}, + {"OUTMIXL Mixer", "AXILVOL Playback Switch", "AXIL Boost"}, + {"OUTMIXL Mixer", "AXIRVOL Playback Switch", "AXIR Boost"}, + {"OUTMIXL Mixer", "VDAC Playback Switch", "Voice DAC Boost"}, + + {"OUTMIXR Mixer", NULL, "Right DAC To Mixer", check_dacr_to_outmixr}, + {"OUTMIXR Mixer", "RECMIXL Playback Switch", "RECMIXL Mixer"}, + {"OUTMIXR Mixer", "RECMIXR Playback Switch", "RECMIXR Mixer"}, + {"OUTMIXR Mixer", "DACR Playback Switch", "Right DAC"}, + {"OUTMIXR Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"}, + {"OUTMIXR Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"}, + {"OUTMIXR Mixer", "MONOIN_RXN Playback Switch", "MONOIN_RXN Boost"}, + {"OUTMIXR Mixer", "AXILVOL Playback Switch", "AXIL Boost"}, + {"OUTMIXR Mixer", "AXIRVOL Playback Switch", "AXIR Boost"}, + {"OUTMIXR Mixer", "VDAC Playback Switch", "Voice DAC Boost"}, + + {"Left SPKVOL Mux", "SPKMIXL", "SPKMIXL Mixer"}, + {"Left SPKVOL Mux", "Vmid", "Vmid"}, + {"Left HPVOL Mux", "OUTMIXL", "OUTMIXL Mixer"}, + {"Left HPVOL Mux", "Vmid", "Vmid"}, + {"Left OUTVOL Mux", "OUTMIXL", "OUTMIXL Mixer"}, + {"Left OUTVOL Mux", "Vmid", "Vmid"}, + {"Right OUTVOL Mux", "OUTMIXR", "OUTMIXR Mixer"}, + {"Right OUTVOL Mux", "Vmid", "Vmid"}, + {"Right HPVOL Mux", "OUTMIXR", "OUTMIXR Mixer"}, + {"Right HPVOL Mux", "Vmid", "Vmid"}, + {"Right SPKVOL Mux", "SPKMIXR", "SPKMIXR Mixer"}, + {"Right SPKVOL Mux", "Vmid", "Vmid"}, + + {"AXO1MIX Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"}, + {"AXO1MIX Mixer", "OUTVOLL Playback Switch", "Left OUTVOL Mux"}, + {"AXO1MIX Mixer", "OUTVOLR Playback Switch", "Right OUTVOL Mux"}, + {"AXO1MIX Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"}, + + {"AXO2MIX Mixer", "MIC1_BST1 Playback Switch", "MIC1 Boost"}, + {"AXO2MIX Mixer", "OUTVOLL Playback Switch", "Left OUTVOL Mux"}, + {"AXO2MIX Mixer", "OUTVOLR Playback Switch", "Right OUTVOL Mux"}, + {"AXO2MIX Mixer", "MIC2_BST2 Playback Switch", "MIC2 Boost"}, + + {"SPOLMIX Mixer", "SPKVOLL Playback Switch", "Left SPKVOL Mux"}, + {"SPOLMIX Mixer", "SPKVOLR Playback Switch", "Right SPKVOL Mux"}, + + {"SPORMIX Mixer", "SPKVOLL Playback Switch", "Left SPKVOL Mux"}, + {"SPORMIX Mixer", "SPKVOLR Playback Switch", "Right SPKVOL Mux"}, + + {"MONOMIX Mixer", "OUTVOLL Playback Switch", "Left OUTVOL Mux"}, + {"MONOMIX Mixer", "OUTVOLR Playback Switch", "Right OUTVOL Mux"}, + + {"SPOL Mux", "SPOLMIX", "SPOLMIX Mixer"}, + {"SPOL Mux", "MONOIN_RX", "MONO_IN"}, + {"SPOL Mux", "VDAC", "Voice DAC Boost"}, + {"SPOL Mux", "DACL", "Left DAC"}, + + {"SPOR Mux", "SPORMIX", "SPORMIX Mixer"}, + {"SPOR Mux", "MONOIN_RX", "MONO_IN"}, + {"SPOR Mux", "VDAC", "Voice DAC Boost"}, + {"SPOR Mux", "DACR", "Right DAC"}, + + {"MONO Mux", "MONOMIX", "MONOMIX Mixer"}, + {"MONO Mux", "MONOIN_RX", "MONO_IN"}, + {"MONO Mux", "VDAC", "Voice DAC Boost"}, + + {"Right DAC_HP", NULL, "Right DAC"}, + {"Left DAC_HP", NULL, "Left DAC"}, + + {"HPL Mux", "Left HPVOL", "Left HPVOL Mux"}, + {"HPL Mux", "Left DAC", "Left DAC_HP"}, + {"HPR Mux", "Right HPVOL", "Right HPVOL Mux"}, + {"HPR Mux", "Right DAC", "Right DAC_HP"}, + + {"HP Depop", NULL, "HPL Mux"}, + {"HP Depop", NULL, "HPR Mux"}, + + {"AUXO1", NULL, "AXO1MIX Mixer"}, + {"AUXO2", NULL, "AXO2MIX Mixer"}, + + {"SPOL", NULL, "Class D"}, + {"SPOL", NULL, "SPOL Mux"}, + {"SPOR", NULL, "Class D"}, + {"SPOR", NULL, "SPOR Mux"}, + + {"HPOL", NULL, "HP Depop"}, + {"HPOR", NULL, "HP Depop"}, + + {"MONO", NULL, "MONO Depop"}, + {"MONO", NULL, "MONO Mux"}, +}; + +struct coeff_clk_div { + u32 mclk; + u32 bclk; + u32 rate; + u16 reg_val; +}; + +/* PLL divisors */ +struct pll_div { + u32 pll_in; + u32 pll_out; + u16 reg_val; +}; + +static const struct pll_div codec_master_pll_div[] = { + {2048000, 8192000, 0x0ea0}, + {3686400, 8192000, 0x4e27}, + {12000000, 8192000, 0x456b}, + {13000000, 8192000, 0x495f}, + {13100000, 8192000, 0x0320}, + {2048000, 11289600, 0xf637}, + {3686400, 11289600, 0x2f22}, + {12000000, 11289600, 0x3e2f}, + {13000000, 11289600, 0x4d5b}, + {13100000, 11289600, 0x363b}, + {2048000, 16384000, 0x1ea0}, + {3686400, 16384000, 0x9e27}, + {12000000, 16384000, 0x452b}, + {13000000, 16384000, 0x542f}, + {13100000, 16384000, 0x03a0}, + {2048000, 16934400, 0xe625}, + {3686400, 16934400, 0x9126}, + {12000000, 16934400, 0x4d2c}, + {13000000, 16934400, 0x742f}, + {13100000, 16934400, 0x3c27}, + {2048000, 22579200, 0x2aa0}, + {3686400, 22579200, 0x2f20}, + {12000000, 22579200, 0x7e2f}, + {13000000, 22579200, 0x742f}, + {13100000, 22579200, 0x3c27}, + {2048000, 24576000, 0x2ea0}, + {3686400, 24576000, 0xee27}, + {12000000, 24576000, 0x2915}, + {13000000, 24576000, 0x772e}, + {13100000, 24576000, 0x0d20}, + {26000000, 24576000, 0x2027}, + {26000000, 22579200, 0x392f}, + {24576000, 22579200, 0x0921}, + {24576000, 24576000, 0x02a0}, +}; + +static const struct pll_div codec_slave_pll_div[] = { + {256000, 2048000, 0x46f0}, + {256000, 4096000, 0x3ea0}, + {352800, 5644800, 0x3ea0}, + {512000, 8192000, 0x3ea0}, + {1024000, 8192000, 0x46f0}, + {705600, 11289600, 0x3ea0}, + {1024000, 16384000, 0x3ea0}, + {1411200, 22579200, 0x3ea0}, + {1536000, 24576000, 0x3ea0}, + {2048000, 16384000, 0x1ea0}, + {2822400, 22579200, 0x1ea0}, + {2822400, 45158400, 0x5ec0}, + {5644800, 45158400, 0x46f0}, + {3072000, 24576000, 0x1ea0}, + {3072000, 49152000, 0x5ec0}, + {6144000, 49152000, 0x46f0}, + {705600, 11289600, 0x3ea0}, + {705600, 8467200, 0x3ab0}, + {24576000, 24576000, 0x02a0}, + {1411200, 11289600, 0x1690}, + {2822400, 11289600, 0x0a90}, + {1536000, 12288000, 0x1690}, + {3072000, 12288000, 0x0a90}, +}; + +static const struct coeff_clk_div coeff_div[] = { + /* sysclk is 256fs */ + {2048000, 8000 * 32, 8000, 0x1000}, + {2048000, 8000 * 64, 8000, 0x0000}, + {2822400, 11025 * 32, 11025, 0x1000}, + {2822400, 11025 * 64, 11025, 0x0000}, + {4096000, 16000 * 32, 16000, 0x1000}, + {4096000, 16000 * 64, 16000, 0x0000}, + {5644800, 22050 * 32, 22050, 0x1000}, + {5644800, 22050 * 64, 22050, 0x0000}, + {8192000, 32000 * 32, 32000, 0x1000}, + {8192000, 32000 * 64, 32000, 0x0000}, + {11289600, 44100 * 32, 44100, 0x1000}, + {11289600, 44100 * 64, 44100, 0x0000}, + {12288000, 48000 * 32, 48000, 0x1000}, + {12288000, 48000 * 64, 48000, 0x0000}, + {22579200, 88200 * 32, 88200, 0x1000}, + {22579200, 88200 * 64, 88200, 0x0000}, + {24576000, 96000 * 32, 96000, 0x1000}, + {24576000, 96000 * 64, 96000, 0x0000}, + /* sysclk is 512fs */ + {4096000, 8000 * 32, 8000, 0x3000}, + {4096000, 8000 * 64, 8000, 0x2000}, + {5644800, 11025 * 32, 11025, 0x3000}, + {5644800, 11025 * 64, 11025, 0x2000}, + {8192000, 16000 * 32, 16000, 0x3000}, + {8192000, 16000 * 64, 16000, 0x2000}, + {11289600, 22050 * 32, 22050, 0x3000}, + {11289600, 22050 * 64, 22050, 0x2000}, + {16384000, 32000 * 32, 32000, 0x3000}, + {16384000, 32000 * 64, 32000, 0x2000}, + {22579200, 44100 * 32, 44100, 0x3000}, + {22579200, 44100 * 64, 44100, 0x2000}, + {24576000, 48000 * 32, 48000, 0x3000}, + {24576000, 48000 * 64, 48000, 0x2000}, + {45158400, 88200 * 32, 88200, 0x3000}, + {45158400, 88200 * 64, 88200, 0x2000}, + {49152000, 96000 * 32, 96000, 0x3000}, + {49152000, 96000 * 64, 96000, 0x2000}, + /* sysclk is 24.576Mhz or 22.5792Mhz */ + {24576000, 8000 * 32, 8000, 0x7080}, + {24576000, 8000 * 64, 8000, 0x6080}, + {24576000, 16000 * 32, 16000, 0x5080}, + {24576000, 16000 * 64, 16000, 0x4080}, + {24576000, 24000 * 32, 24000, 0x5000}, + {24576000, 24000 * 64, 24000, 0x4000}, + {24576000, 32000 * 32, 32000, 0x3080}, + {24576000, 32000 * 64, 32000, 0x2080}, + {22579200, 11025 * 32, 11025, 0x7000}, + {22579200, 11025 * 64, 11025, 0x6000}, + {22579200, 22050 * 32, 22050, 0x5000}, + {22579200, 22050 * 64, 22050, 0x4000}, +}; + +static int get_coeff(int mclk, int rate, int timesofbclk) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].mclk == mclk && coeff_div[i].rate == rate && + (coeff_div[i].bclk / coeff_div[i].rate) == timesofbclk) + return i; + } + return -EINVAL; +} + +static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + int timesofbclk = 32, coeff; + unsigned int iface = 0; + + dev_dbg(component->dev, "enter %s\n", __func__); + + rt5631->bclk_rate = snd_soc_params_to_bclk(params); + if (rt5631->bclk_rate < 0) { + dev_err(component->dev, "Fail to get BCLK rate\n"); + return rt5631->bclk_rate; + } + rt5631->rx_rate = params_rate(params); + + if (rt5631->master) + coeff = get_coeff(rt5631->sysclk, rt5631->rx_rate, + rt5631->bclk_rate / rt5631->rx_rate); + else + coeff = get_coeff(rt5631->sysclk, rt5631->rx_rate, + timesofbclk); + if (coeff < 0) { + dev_err(component->dev, "Fail to get coeff\n"); + return coeff; + } + + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= RT5631_SDP_I2S_DL_20; + break; + case 24: + iface |= RT5631_SDP_I2S_DL_24; + break; + case 8: + iface |= RT5631_SDP_I2S_DL_8; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5631_SDP_CTRL, + RT5631_SDP_I2S_DL_MASK, iface); + snd_soc_component_write(component, RT5631_STEREO_AD_DA_CLK_CTRL, + coeff_div[coeff].reg_val); + + return 0; +} + +static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + unsigned int iface = 0; + + dev_dbg(component->dev, "enter %s\n", __func__); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5631->master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface |= RT5631_SDP_MODE_SEL_SLAVE; + rt5631->master = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= RT5631_SDP_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= RT5631_SDP_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= RT5631_SDP_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= RT5631_SDP_I2S_BCLK_POL_CTRL; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, RT5631_SDP_CTRL, iface); + + return 0; +} + +static int rt5631_hifi_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "enter %s, syclk=%d\n", __func__, freq); + + if ((freq >= (256 * 8000)) && (freq <= (512 * 96000))) { + rt5631->sysclk = freq; + return 0; + } + + return -EINVAL; +} + +static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = codec_dai->component; + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + int i, ret = -EINVAL; + + dev_dbg(component->dev, "enter %s\n", __func__); + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + snd_soc_component_update_bits(component, RT5631_GLOBAL_CLK_CTRL, + RT5631_SYSCLK_SOUR_SEL_MASK, + RT5631_SYSCLK_SOUR_SEL_MCLK); + + return 0; + } + + if (rt5631->master) { + for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) + if (freq_in == codec_master_pll_div[i].pll_in && + freq_out == codec_master_pll_div[i].pll_out) { + dev_info(component->dev, + "change PLL in master mode\n"); + snd_soc_component_write(component, RT5631_PLL_CTRL, + codec_master_pll_div[i].reg_val); + schedule_timeout_uninterruptible( + msecs_to_jiffies(20)); + snd_soc_component_update_bits(component, + RT5631_GLOBAL_CLK_CTRL, + RT5631_SYSCLK_SOUR_SEL_MASK | + RT5631_PLLCLK_SOUR_SEL_MASK, + RT5631_SYSCLK_SOUR_SEL_PLL | + RT5631_PLLCLK_SOUR_SEL_MCLK); + ret = 0; + break; + } + } else { + for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) + if (freq_in == codec_slave_pll_div[i].pll_in && + freq_out == codec_slave_pll_div[i].pll_out) { + dev_info(component->dev, + "change PLL in slave mode\n"); + snd_soc_component_write(component, RT5631_PLL_CTRL, + codec_slave_pll_div[i].reg_val); + schedule_timeout_uninterruptible( + msecs_to_jiffies(20)); + snd_soc_component_update_bits(component, + RT5631_GLOBAL_CLK_CTRL, + RT5631_SYSCLK_SOUR_SEL_MASK | + RT5631_PLLCLK_SOUR_SEL_MASK, + RT5631_SYSCLK_SOUR_SEL_PLL | + RT5631_PLLCLK_SOUR_SEL_BCLK); + ret = 0; + break; + } + } + + return ret; +} + +static int rt5631_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD2, + RT5631_PWR_MICBIAS1_VOL | RT5631_PWR_MICBIAS2_VOL, + RT5631_PWR_MICBIAS1_VOL | RT5631_PWR_MICBIAS2_VOL); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3, + RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS, + RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS); + msleep(80); + snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3, + RT5631_PWR_FAST_VREF_CTRL, + RT5631_PWR_FAST_VREF_CTRL); + regcache_cache_only(rt5631->regmap, false); + regcache_sync(rt5631->regmap); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, RT5631_PWR_MANAG_ADD1, 0x0000); + snd_soc_component_write(component, RT5631_PWR_MANAG_ADD2, 0x0000); + snd_soc_component_write(component, RT5631_PWR_MANAG_ADD3, 0x0000); + snd_soc_component_write(component, RT5631_PWR_MANAG_ADD4, 0x0000); + break; + + default: + break; + } + + return 0; +} + +static int rt5631_probe(struct snd_soc_component *component) +{ + struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component); + unsigned int val; + + val = rt5631_read_index(component, RT5631_ADDA_MIXER_INTL_REG3); + if (val & 0x0002) + rt5631->codec_version = 1; + else + rt5631->codec_version = 0; + + rt5631_reset(component); + snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3, + RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS, + RT5631_PWR_VREF | RT5631_PWR_MAIN_BIAS); + msleep(80); + snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3, + RT5631_PWR_FAST_VREF_CTRL, RT5631_PWR_FAST_VREF_CTRL); + /* enable HP zero cross */ + snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, 0x0f18); + /* power off ClassD auto Recovery */ + if (rt5631->codec_version) + snd_soc_component_update_bits(component, RT5631_INT_ST_IRQ_CTRL_2, + 0x2000, 0x2000); + else + snd_soc_component_update_bits(component, RT5631_INT_ST_IRQ_CTRL_2, + 0x2000, 0); + /* DMIC */ + if (rt5631->dmic_used_flag) { + snd_soc_component_update_bits(component, RT5631_GPIO_CTRL, + RT5631_GPIO_PIN_FUN_SEL_MASK | + RT5631_GPIO_DMIC_FUN_SEL_MASK, + RT5631_GPIO_PIN_FUN_SEL_GPIO_DIMC | + RT5631_GPIO_DMIC_FUN_SEL_DIMC); + snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL, + RT5631_DMIC_L_CH_LATCH_MASK | + RT5631_DMIC_R_CH_LATCH_MASK, + RT5631_DMIC_L_CH_LATCH_FALLING | + RT5631_DMIC_R_CH_LATCH_RISING); + } + + snd_soc_component_init_bias_level(component, SND_SOC_BIAS_STANDBY); + + return 0; +} + +#define RT5631_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5631_FORMAT (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5631_ops = { + .hw_params = rt5631_hifi_pcm_params, + .set_fmt = rt5631_hifi_codec_set_dai_fmt, + .set_sysclk = rt5631_hifi_codec_set_dai_sysclk, + .set_pll = rt5631_codec_set_dai_pll, +}; + +static struct snd_soc_dai_driver rt5631_dai[] = { + { + .name = "rt5631-hifi", + .id = 1, + .playback = { + .stream_name = "HIFI Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5631_STEREO_RATES, + .formats = RT5631_FORMAT, + }, + .capture = { + .stream_name = "HIFI Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5631_STEREO_RATES, + .formats = RT5631_FORMAT, + }, + .ops = &rt5631_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5631 = { + .probe = rt5631_probe, + .set_bias_level = rt5631_set_bias_level, + .controls = rt5631_snd_controls, + .num_controls = ARRAY_SIZE(rt5631_snd_controls), + .dapm_widgets = rt5631_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5631_dapm_widgets), + .dapm_routes = rt5631_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5631_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct i2c_device_id rt5631_i2c_id[] = { + { "rt5631", 0 }, + { "alc5631", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id rt5631_i2c_dt_ids[] = { + { .compatible = "realtek,rt5631"}, + { .compatible = "realtek,alc5631"}, + { } +}; +MODULE_DEVICE_TABLE(of, rt5631_i2c_dt_ids); +#endif + +static const struct regmap_config rt5631_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + + .readable_reg = rt5631_readable_register, + .volatile_reg = rt5631_volatile_register, + .max_register = RT5631_VENDOR_ID2, + .reg_defaults = rt5631_reg, + .num_reg_defaults = ARRAY_SIZE(rt5631_reg), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt5631_i2c_probe(struct i2c_client *i2c) +{ + struct rt5631_priv *rt5631; + int ret; + + rt5631 = devm_kzalloc(&i2c->dev, sizeof(struct rt5631_priv), + GFP_KERNEL); + if (NULL == rt5631) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5631); + + rt5631->regmap = devm_regmap_init_i2c(i2c, &rt5631_regmap_config); + if (IS_ERR(rt5631->regmap)) + return PTR_ERR(rt5631->regmap); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5631, + rt5631_dai, ARRAY_SIZE(rt5631_dai)); + return ret; +} + +static void rt5631_i2c_remove(struct i2c_client *client) +{} + +static struct i2c_driver rt5631_i2c_driver = { + .driver = { + .name = "rt5631", + .of_match_table = of_match_ptr(rt5631_i2c_dt_ids), + }, + .probe = rt5631_i2c_probe, + .remove = rt5631_i2c_remove, + .id_table = rt5631_i2c_id, +}; + +module_i2c_driver(rt5631_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5631 driver"); +MODULE_AUTHOR("flove "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt5631.h b/sound/soc/codecs/rt5631.h new file mode 100644 index 0000000000..8a6b99a48c --- /dev/null +++ b/sound/soc/codecs/rt5631.h @@ -0,0 +1,702 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __RTCODEC5631_H__ +#define __RTCODEC5631_H__ + + +#define RT5631_RESET 0x00 +#define RT5631_SPK_OUT_VOL 0x02 +#define RT5631_HP_OUT_VOL 0x04 +#define RT5631_MONO_AXO_1_2_VOL 0x06 +#define RT5631_AUX_IN_VOL 0x0A +#define RT5631_STEREO_DAC_VOL_1 0x0C +#define RT5631_MIC_CTRL_1 0x0E +#define RT5631_STEREO_DAC_VOL_2 0x10 +#define RT5631_ADC_CTRL_1 0x12 +#define RT5631_ADC_REC_MIXER 0x14 +#define RT5631_ADC_CTRL_2 0x16 +#define RT5631_VDAC_DIG_VOL 0x18 +#define RT5631_OUTMIXER_L_CTRL 0x1A +#define RT5631_OUTMIXER_R_CTRL 0x1C +#define RT5631_AXO1MIXER_CTRL 0x1E +#define RT5631_AXO2MIXER_CTRL 0x20 +#define RT5631_MIC_CTRL_2 0x22 +#define RT5631_DIG_MIC_CTRL 0x24 +#define RT5631_MONO_INPUT_VOL 0x26 +#define RT5631_SPK_MIXER_CTRL 0x28 +#define RT5631_SPK_MONO_OUT_CTRL 0x2A +#define RT5631_SPK_MONO_HP_OUT_CTRL 0x2C +#define RT5631_SDP_CTRL 0x34 +#define RT5631_MONO_SDP_CTRL 0x36 +#define RT5631_STEREO_AD_DA_CLK_CTRL 0x38 +#define RT5631_PWR_MANAG_ADD1 0x3A +#define RT5631_PWR_MANAG_ADD2 0x3B +#define RT5631_PWR_MANAG_ADD3 0x3C +#define RT5631_PWR_MANAG_ADD4 0x3E +#define RT5631_GEN_PUR_CTRL_REG 0x40 +#define RT5631_GLOBAL_CLK_CTRL 0x42 +#define RT5631_PLL_CTRL 0x44 +#define RT5631_INT_ST_IRQ_CTRL_1 0x48 +#define RT5631_INT_ST_IRQ_CTRL_2 0x4A +#define RT5631_GPIO_CTRL 0x4C +#define RT5631_MISC_CTRL 0x52 +#define RT5631_DEPOP_FUN_CTRL_1 0x54 +#define RT5631_DEPOP_FUN_CTRL_2 0x56 +#define RT5631_JACK_DET_CTRL 0x5A +#define RT5631_SOFT_VOL_CTRL 0x5C +#define RT5631_ALC_CTRL_1 0x64 +#define RT5631_ALC_CTRL_2 0x65 +#define RT5631_ALC_CTRL_3 0x66 +#define RT5631_PSEUDO_SPATL_CTRL 0x68 +#define RT5631_INDEX_ADD 0x6A +#define RT5631_INDEX_DATA 0x6C +#define RT5631_EQ_CTRL 0x6E +#define RT5631_VENDOR_ID 0x7A +#define RT5631_VENDOR_ID1 0x7C +#define RT5631_VENDOR_ID2 0x7E + +/* Index of Codec Private Register definition */ +#define RT5631_EQ_BW_LOP 0x00 +#define RT5631_EQ_GAIN_LOP 0x01 +#define RT5631_EQ_FC_BP1 0x02 +#define RT5631_EQ_BW_BP1 0x03 +#define RT5631_EQ_GAIN_BP1 0x04 +#define RT5631_EQ_FC_BP2 0x05 +#define RT5631_EQ_BW_BP2 0x06 +#define RT5631_EQ_GAIN_BP2 0x07 +#define RT5631_EQ_FC_BP3 0x08 +#define RT5631_EQ_BW_BP3 0x09 +#define RT5631_EQ_GAIN_BP3 0x0a +#define RT5631_EQ_BW_HIP 0x0b +#define RT5631_EQ_GAIN_HIP 0x0c +#define RT5631_EQ_HPF_A1 0x0d +#define RT5631_EQ_HPF_A2 0x0e +#define RT5631_EQ_HPF_GAIN 0x0f +#define RT5631_EQ_PRE_VOL_CTRL 0x11 +#define RT5631_EQ_POST_VOL_CTRL 0x12 +#define RT5631_TEST_MODE_CTRL 0x39 +#define RT5631_CP_INTL_REG2 0x45 +#define RT5631_ADDA_MIXER_INTL_REG3 0x52 +#define RT5631_SPK_INTL_CTRL 0x56 + + +/* global definition */ +#define RT5631_L_MUTE (0x1 << 15) +#define RT5631_L_MUTE_SHIFT 15 +#define RT5631_L_EN (0x1 << 14) +#define RT5631_L_EN_SHIFT 14 +#define RT5631_R_MUTE (0x1 << 7) +#define RT5631_R_MUTE_SHIFT 7 +#define RT5631_R_EN (0x1 << 6) +#define RT5631_R_EN_SHIFT 6 +#define RT5631_VOL_MASK 0x1f +#define RT5631_L_VOL_SHIFT 8 +#define RT5631_R_VOL_SHIFT 0 + +/* Speaker Output Control(0x02) */ +#define RT5631_SPK_L_VOL_SEL_MASK (0x1 << 14) +#define RT5631_SPK_L_VOL_SEL_VMID (0x0 << 14) +#define RT5631_SPK_L_VOL_SEL_SPKMIX_L (0x1 << 14) +#define RT5631_SPK_R_VOL_SEL_MASK (0x1 << 6) +#define RT5631_SPK_R_VOL_SEL_VMID (0x0 << 6) +#define RT5631_SPK_R_VOL_SEL_SPKMIX_R (0x1 << 6) + +/* Headphone Output Control(0x04) */ +#define RT5631_HP_L_VOL_SEL_MASK (0x1 << 14) +#define RT5631_HP_L_VOL_SEL_VMID (0x0 << 14) +#define RT5631_HP_L_VOL_SEL_OUTMIX_L (0x1 << 14) +#define RT5631_HP_R_VOL_SEL_MASK (0x1 << 6) +#define RT5631_HP_R_VOL_SEL_VMID (0x0 << 6) +#define RT5631_HP_R_VOL_SEL_OUTMIX_R (0x1 << 6) + +/* Output Control for AUXOUT/MONO(0x06) */ +#define RT5631_AUXOUT_1_VOL_SEL_MASK (0x1 << 14) +#define RT5631_AUXOUT_1_VOL_SEL_VMID (0x0 << 14) +#define RT5631_AUXOUT_1_VOL_SEL_OUTMIX_L (0x1 << 14) +#define RT5631_MUTE_MONO (0x1 << 13) +#define RT5631_MUTE_MONO_SHIFT 13 +#define RT5631_AUXOUT_2_VOL_SEL_MASK (0x1 << 6) +#define RT5631_AUXOUT_2_VOL_SEL_VMID (0x0 << 6) +#define RT5631_AUXOUT_2_VOL_SEL_OUTMIX_R (0x1 << 6) + +/* Microphone Input Control 1(0x0E) */ +#define RT5631_MIC1_DIFF_INPUT_CTRL (0x1 << 15) +#define RT5631_MIC1_DIFF_INPUT_SHIFT 15 +#define RT5631_MIC2_DIFF_INPUT_CTRL (0x1 << 7) +#define RT5631_MIC2_DIFF_INPUT_SHIFT 7 + +/* Stereo DAC Digital Volume2(0x10) */ +#define RT5631_DAC_VOL_MASK 0xff + +/* ADC Recording Mixer Control(0x14) */ +#define RT5631_M_OUTMIXER_L_TO_RECMIXER_L (0x1 << 15) +#define RT5631_M_OUTMIXL_RECMIXL_BIT 15 +#define RT5631_M_MIC1_TO_RECMIXER_L (0x1 << 14) +#define RT5631_M_MIC1_RECMIXL_BIT 14 +#define RT5631_M_AXIL_TO_RECMIXER_L (0x1 << 13) +#define RT5631_M_AXIL_RECMIXL_BIT 13 +#define RT5631_M_MONO_IN_TO_RECMIXER_L (0x1 << 12) +#define RT5631_M_MONO_IN_RECMIXL_BIT 12 +#define RT5631_M_OUTMIXER_R_TO_RECMIXER_R (0x1 << 7) +#define RT5631_M_OUTMIXR_RECMIXR_BIT 7 +#define RT5631_M_MIC2_TO_RECMIXER_R (0x1 << 6) +#define RT5631_M_MIC2_RECMIXR_BIT 6 +#define RT5631_M_AXIR_TO_RECMIXER_R (0x1 << 5) +#define RT5631_M_AXIR_RECMIXR_BIT 5 +#define RT5631_M_MONO_IN_TO_RECMIXER_R (0x1 << 4) +#define RT5631_M_MONO_IN_RECMIXR_BIT 4 + +/* Left Output Mixer Control(0x1A) */ +#define RT5631_M_RECMIXER_L_TO_OUTMIXER_L (0x1 << 15) +#define RT5631_M_RECMIXL_OUTMIXL_BIT 15 +#define RT5631_M_RECMIXER_R_TO_OUTMIXER_L (0x1 << 14) +#define RT5631_M_RECMIXR_OUTMIXL_BIT 14 +#define RT5631_M_DAC_L_TO_OUTMIXER_L (0x1 << 13) +#define RT5631_M_DACL_OUTMIXL_BIT 13 +#define RT5631_M_MIC1_TO_OUTMIXER_L (0x1 << 12) +#define RT5631_M_MIC1_OUTMIXL_BIT 12 +#define RT5631_M_MIC2_TO_OUTMIXER_L (0x1 << 11) +#define RT5631_M_MIC2_OUTMIXL_BIT 11 +#define RT5631_M_MONO_IN_P_TO_OUTMIXER_L (0x1 << 10) +#define RT5631_M_MONO_INP_OUTMIXL_BIT 10 +#define RT5631_M_AXIL_TO_OUTMIXER_L (0x1 << 9) +#define RT5631_M_AXIL_OUTMIXL_BIT 9 +#define RT5631_M_AXIR_TO_OUTMIXER_L (0x1 << 8) +#define RT5631_M_AXIR_OUTMIXL_BIT 8 +#define RT5631_M_VDAC_TO_OUTMIXER_L (0x1 << 7) +#define RT5631_M_VDAC_OUTMIXL_BIT 7 + +/* Right Output Mixer Control(0x1C) */ +#define RT5631_M_RECMIXER_L_TO_OUTMIXER_R (0x1 << 15) +#define RT5631_M_RECMIXL_OUTMIXR_BIT 15 +#define RT5631_M_RECMIXER_R_TO_OUTMIXER_R (0x1 << 14) +#define RT5631_M_RECMIXR_OUTMIXR_BIT 14 +#define RT5631_M_DAC_R_TO_OUTMIXER_R (0x1 << 13) +#define RT5631_M_DACR_OUTMIXR_BIT 13 +#define RT5631_M_MIC1_TO_OUTMIXER_R (0x1 << 12) +#define RT5631_M_MIC1_OUTMIXR_BIT 12 +#define RT5631_M_MIC2_TO_OUTMIXER_R (0x1 << 11) +#define RT5631_M_MIC2_OUTMIXR_BIT 11 +#define RT5631_M_MONO_IN_N_TO_OUTMIXER_R (0x1 << 10) +#define RT5631_M_MONO_INN_OUTMIXR_BIT 10 +#define RT5631_M_AXIL_TO_OUTMIXER_R (0x1 << 9) +#define RT5631_M_AXIL_OUTMIXR_BIT 9 +#define RT5631_M_AXIR_TO_OUTMIXER_R (0x1 << 8) +#define RT5631_M_AXIR_OUTMIXR_BIT 8 +#define RT5631_M_VDAC_TO_OUTMIXER_R (0x1 << 7) +#define RT5631_M_VDAC_OUTMIXR_BIT 7 + +/* Lout Mixer Control(0x1E) */ +#define RT5631_M_MIC1_TO_AXO1MIXER (0x1 << 15) +#define RT5631_M_MIC1_AXO1MIX_BIT 15 +#define RT5631_M_MIC2_TO_AXO1MIXER (0x1 << 11) +#define RT5631_M_MIC2_AXO1MIX_BIT 11 +#define RT5631_M_OUTMIXER_L_TO_AXO1MIXER (0x1 << 7) +#define RT5631_M_OUTMIXL_AXO1MIX_BIT 7 +#define RT5631_M_OUTMIXER_R_TO_AXO1MIXER (0x1 << 6) +#define RT5631_M_OUTMIXR_AXO1MIX_BIT 6 + +/* Rout Mixer Control(0x20) */ +#define RT5631_M_MIC1_TO_AXO2MIXER (0x1 << 15) +#define RT5631_M_MIC1_AXO2MIX_BIT 15 +#define RT5631_M_MIC2_TO_AXO2MIXER (0x1 << 11) +#define RT5631_M_MIC2_AXO2MIX_BIT 11 +#define RT5631_M_OUTMIXER_L_TO_AXO2MIXER (0x1 << 7) +#define RT5631_M_OUTMIXL_AXO2MIX_BIT 7 +#define RT5631_M_OUTMIXER_R_TO_AXO2MIXER (0x1 << 6) +#define RT5631_M_OUTMIXR_AXO2MIX_BIT 6 + +/* Micphone Input Control 2(0x22) */ +#define RT5631_MIC_BIAS_90_PRECNET_AVDD 1 +#define RT5631_MIC_BIAS_75_PRECNET_AVDD 2 + +#define RT5631_MIC1_BOOST_CTRL_MASK (0xf << 12) +#define RT5631_MIC1_BOOST_CTRL_BYPASS (0x0 << 12) +#define RT5631_MIC1_BOOST_CTRL_20DB (0x1 << 12) +#define RT5631_MIC1_BOOST_CTRL_24DB (0x2 << 12) +#define RT5631_MIC1_BOOST_CTRL_30DB (0x3 << 12) +#define RT5631_MIC1_BOOST_CTRL_35DB (0x4 << 12) +#define RT5631_MIC1_BOOST_CTRL_40DB (0x5 << 12) +#define RT5631_MIC1_BOOST_CTRL_34DB (0x6 << 12) +#define RT5631_MIC1_BOOST_CTRL_50DB (0x7 << 12) +#define RT5631_MIC1_BOOST_CTRL_52DB (0x8 << 12) +#define RT5631_MIC1_BOOST_SHIFT 12 + +#define RT5631_MIC2_BOOST_CTRL_MASK (0xf << 8) +#define RT5631_MIC2_BOOST_CTRL_BYPASS (0x0 << 8) +#define RT5631_MIC2_BOOST_CTRL_20DB (0x1 << 8) +#define RT5631_MIC2_BOOST_CTRL_24DB (0x2 << 8) +#define RT5631_MIC2_BOOST_CTRL_30DB (0x3 << 8) +#define RT5631_MIC2_BOOST_CTRL_35DB (0x4 << 8) +#define RT5631_MIC2_BOOST_CTRL_40DB (0x5 << 8) +#define RT5631_MIC2_BOOST_CTRL_34DB (0x6 << 8) +#define RT5631_MIC2_BOOST_CTRL_50DB (0x7 << 8) +#define RT5631_MIC2_BOOST_CTRL_52DB (0x8 << 8) +#define RT5631_MIC2_BOOST_SHIFT 8 + +#define RT5631_MICBIAS1_VOLT_CTRL_MASK (0x1 << 7) +#define RT5631_MICBIAS1_VOLT_CTRL_90P (0x0 << 7) +#define RT5631_MICBIAS1_VOLT_CTRL_75P (0x1 << 7) + +#define RT5631_MICBIAS1_S_C_DET_MASK (0x1 << 6) +#define RT5631_MICBIAS1_S_C_DET_DIS (0x0 << 6) +#define RT5631_MICBIAS1_S_C_DET_ENA (0x1 << 6) + +#define RT5631_MICBIAS1_SHORT_CURR_DET_MASK (0x3 << 4) +#define RT5631_MICBIAS1_SHORT_CURR_DET_600UA (0x0 << 4) +#define RT5631_MICBIAS1_SHORT_CURR_DET_1500UA (0x1 << 4) +#define RT5631_MICBIAS1_SHORT_CURR_DET_2000UA (0x2 << 4) + +#define RT5631_MICBIAS2_VOLT_CTRL_MASK (0x1 << 3) +#define RT5631_MICBIAS2_VOLT_CTRL_90P (0x0 << 3) +#define RT5631_MICBIAS2_VOLT_CTRL_75P (0x1 << 3) + +#define RT5631_MICBIAS2_S_C_DET_MASK (0x1 << 2) +#define RT5631_MICBIAS2_S_C_DET_DIS (0x0 << 2) +#define RT5631_MICBIAS2_S_C_DET_ENA (0x1 << 2) + +#define RT5631_MICBIAS2_SHORT_CURR_DET_MASK (0x3) +#define RT5631_MICBIAS2_SHORT_CURR_DET_600UA (0x0) +#define RT5631_MICBIAS2_SHORT_CURR_DET_1500UA (0x1) +#define RT5631_MICBIAS2_SHORT_CURR_DET_2000UA (0x2) + + +/* Digital Microphone Control(0x24) */ +#define RT5631_DMIC_ENA_MASK (0x1 << 15) +#define RT5631_DMIC_ENA_SHIFT 15 +/* DMIC_ENA: DMIC to ADC Digital filter */ +#define RT5631_DMIC_ENA (0x1 << 15) +/* DMIC_DIS: ADC mixer to ADC Digital filter */ +#define RT5631_DMIC_DIS (0x0 << 15) +#define RT5631_DMIC_L_CH_MUTE (0x1 << 13) +#define RT5631_DMIC_L_CH_MUTE_SHIFT 13 +#define RT5631_DMIC_R_CH_MUTE (0x1 << 12) +#define RT5631_DMIC_R_CH_MUTE_SHIFT 12 +#define RT5631_DMIC_L_CH_LATCH_MASK (0x1 << 9) +#define RT5631_DMIC_L_CH_LATCH_RISING (0x1 << 9) +#define RT5631_DMIC_L_CH_LATCH_FALLING (0x0 << 9) +#define RT5631_DMIC_R_CH_LATCH_MASK (0x1 << 8) +#define RT5631_DMIC_R_CH_LATCH_RISING (0x1 << 8) +#define RT5631_DMIC_R_CH_LATCH_FALLING (0x0 << 8) +#define RT5631_DMIC_CLK_CTRL_MASK (0x3 << 4) +#define RT5631_DMIC_CLK_CTRL_TO_128FS (0x0 << 4) +#define RT5631_DMIC_CLK_CTRL_TO_64FS (0x1 << 4) +#define RT5631_DMIC_CLK_CTRL_TO_32FS (0x2 << 4) + +/* Microphone Input Volume(0x26) */ +#define RT5631_MONO_DIFF_INPUT_SHIFT 15 + +/* Speaker Mixer Control(0x28) */ +#define RT5631_M_RECMIXER_L_TO_SPKMIXER_L (0x1 << 15) +#define RT5631_M_RECMIXL_SPKMIXL_BIT 15 +#define RT5631_M_MIC1_P_TO_SPKMIXER_L (0x1 << 14) +#define RT5631_M_MIC1P_SPKMIXL_BIT 14 +#define RT5631_M_DAC_L_TO_SPKMIXER_L (0x1 << 13) +#define RT5631_M_DACL_SPKMIXL_BIT 13 +#define RT5631_M_OUTMIXER_L_TO_SPKMIXER_L (0x1 << 12) +#define RT5631_M_OUTMIXL_SPKMIXL_BIT 12 + +#define RT5631_M_RECMIXER_R_TO_SPKMIXER_R (0x1 << 7) +#define RT5631_M_RECMIXR_SPKMIXR_BIT 7 +#define RT5631_M_MIC2_P_TO_SPKMIXER_R (0x1 << 6) +#define RT5631_M_MIC2P_SPKMIXR_BIT 6 +#define RT5631_M_DAC_R_TO_SPKMIXER_R (0x1 << 5) +#define RT5631_M_DACR_SPKMIXR_BIT 5 +#define RT5631_M_OUTMIXER_R_TO_SPKMIXER_R (0x1 << 4) +#define RT5631_M_OUTMIXR_SPKMIXR_BIT 4 + +/* Speaker/Mono Output Control(0x2A) */ +#define RT5631_M_SPKVOL_L_TO_SPOL_MIXER (0x1 << 15) +#define RT5631_M_SPKVOLL_SPOLMIX_BIT 15 +#define RT5631_M_SPKVOL_R_TO_SPOL_MIXER (0x1 << 14) +#define RT5631_M_SPKVOLR_SPOLMIX_BIT 14 +#define RT5631_M_SPKVOL_L_TO_SPOR_MIXER (0x1 << 13) +#define RT5631_M_SPKVOLL_SPORMIX_BIT 13 +#define RT5631_M_SPKVOL_R_TO_SPOR_MIXER (0x1 << 12) +#define RT5631_M_SPKVOLR_SPORMIX_BIT 12 +#define RT5631_M_OUTVOL_L_TO_MONOMIXER (0x1 << 11) +#define RT5631_M_OUTVOLL_MONOMIX_BIT 11 +#define RT5631_M_OUTVOL_R_TO_MONOMIXER (0x1 << 10) +#define RT5631_M_OUTVOLR_MONOMIX_BIT 10 + +/* Speaker/Mono/HP Output Control(0x2C) */ +#define RT5631_SPK_L_MUX_SEL_MASK (0x3 << 14) +#define RT5631_SPK_L_MUX_SEL_SPKMIXER_L (0x0 << 14) +#define RT5631_SPK_L_MUX_SEL_MONO_IN (0x1 << 14) +#define RT5631_SPK_L_MUX_SEL_DAC_L (0x3 << 14) +#define RT5631_SPK_L_MUX_SEL_SHIFT 14 + +#define RT5631_SPK_R_MUX_SEL_MASK (0x3 << 10) +#define RT5631_SPK_R_MUX_SEL_SPKMIXER_R (0x0 << 10) +#define RT5631_SPK_R_MUX_SEL_MONO_IN (0x1 << 10) +#define RT5631_SPK_R_MUX_SEL_DAC_R (0x3 << 10) +#define RT5631_SPK_R_MUX_SEL_SHIFT 10 + +#define RT5631_MONO_MUX_SEL_MASK (0x3 << 6) +#define RT5631_MONO_MUX_SEL_MONOMIXER (0x0 << 6) +#define RT5631_MONO_MUX_SEL_MONO_IN (0x1 << 6) +#define RT5631_MONO_MUX_SEL_SHIFT 6 + +#define RT5631_HP_L_MUX_SEL_MASK (0x1 << 3) +#define RT5631_HP_L_MUX_SEL_HPVOL_L (0x0 << 3) +#define RT5631_HP_L_MUX_SEL_DAC_L (0x1 << 3) +#define RT5631_HP_L_MUX_SEL_SHIFT 3 + +#define RT5631_HP_R_MUX_SEL_MASK (0x1 << 2) +#define RT5631_HP_R_MUX_SEL_HPVOL_R (0x0 << 2) +#define RT5631_HP_R_MUX_SEL_DAC_R (0x1 << 2) +#define RT5631_HP_R_MUX_SEL_SHIFT 2 + +/* Stereo I2S Serial Data Port Control(0x34) */ +#define RT5631_SDP_MODE_SEL_MASK (0x1 << 15) +#define RT5631_SDP_MODE_SEL_MASTER (0x0 << 15) +#define RT5631_SDP_MODE_SEL_SLAVE (0x1 << 15) + +#define RT5631_SDP_ADC_CPS_SEL_MASK (0x3 << 10) +#define RT5631_SDP_ADC_CPS_SEL_OFF (0x0 << 10) +#define RT5631_SDP_ADC_CPS_SEL_U_LAW (0x1 << 10) +#define RT5631_SDP_ADC_CPS_SEL_A_LAW (0x2 << 10) + +#define RT5631_SDP_DAC_CPS_SEL_MASK (0x3 << 8) +#define RT5631_SDP_DAC_CPS_SEL_OFF (0x0 << 8) +#define RT5631_SDP_DAC_CPS_SEL_U_LAW (0x1 << 8) +#define RT5631_SDP_DAC_CPS_SEL_A_LAW (0x2 << 8) +/* 0:Normal 1:Invert */ +#define RT5631_SDP_I2S_BCLK_POL_CTRL (0x1 << 7) +/* 0:Normal 1:Invert */ +#define RT5631_SDP_DAC_R_INV (0x1 << 6) +/* 0:ADC data appear at left phase of LRCK + * 1:ADC data appear at right phase of LRCK + */ +#define RT5631_SDP_ADC_DATA_L_R_SWAP (0x1 << 5) +/* 0:DAC data appear at left phase of LRCK + * 1:DAC data appear at right phase of LRCK + */ +#define RT5631_SDP_DAC_DATA_L_R_SWAP (0x1 << 4) + +/* Data Length Slection */ +#define RT5631_SDP_I2S_DL_MASK (0x3 << 2) +#define RT5631_SDP_I2S_DL_16 (0x0 << 2) +#define RT5631_SDP_I2S_DL_20 (0x1 << 2) +#define RT5631_SDP_I2S_DL_24 (0x2 << 2) +#define RT5631_SDP_I2S_DL_8 (0x3 << 2) + +/* PCM Data Format Selection */ +#define RT5631_SDP_I2S_DF_MASK (0x3) +#define RT5631_SDP_I2S_DF_I2S (0x0) +#define RT5631_SDP_I2S_DF_LEFT (0x1) +#define RT5631_SDP_I2S_DF_PCM_A (0x2) +#define RT5631_SDP_I2S_DF_PCM_B (0x3) + +/* Stereo AD/DA Clock Control(0x38h) */ +#define RT5631_I2S_PRE_DIV_MASK (0x7 << 13) +#define RT5631_I2S_PRE_DIV_1 (0x0 << 13) +#define RT5631_I2S_PRE_DIV_2 (0x1 << 13) +#define RT5631_I2S_PRE_DIV_4 (0x2 << 13) +#define RT5631_I2S_PRE_DIV_8 (0x3 << 13) +#define RT5631_I2S_PRE_DIV_16 (0x4 << 13) +#define RT5631_I2S_PRE_DIV_32 (0x5 << 13) +/* CLOCK RELATIVE OF BCLK AND LCRK */ +#define RT5631_I2S_LRCK_SEL_N_BCLK_MASK (0x1 << 12) +#define RT5631_I2S_LRCK_SEL_64_BCLK (0x0 << 12) /* 64FS */ +#define RT5631_I2S_LRCK_SEL_32_BCLK (0x1 << 12) /* 32FS */ + +#define RT5631_DAC_OSR_SEL_MASK (0x3 << 10) +#define RT5631_DAC_OSR_SEL_128FS (0x3 << 10) +#define RT5631_DAC_OSR_SEL_64FS (0x3 << 10) +#define RT5631_DAC_OSR_SEL_32FS (0x3 << 10) +#define RT5631_DAC_OSR_SEL_16FS (0x3 << 10) + +#define RT5631_ADC_OSR_SEL_MASK (0x3 << 8) +#define RT5631_ADC_OSR_SEL_128FS (0x3 << 8) +#define RT5631_ADC_OSR_SEL_64FS (0x3 << 8) +#define RT5631_ADC_OSR_SEL_32FS (0x3 << 8) +#define RT5631_ADC_OSR_SEL_16FS (0x3 << 8) + +#define RT5631_ADDA_FILTER_CLK_SEL_256FS (0 << 7) /* 256FS */ +#define RT5631_ADDA_FILTER_CLK_SEL_384FS (1 << 7) /* 384FS */ + +/* Power managment addition 1 (0x3A) */ +#define RT5631_PWR_MAIN_I2S_EN (0x1 << 15) +#define RT5631_PWR_MAIN_I2S_BIT 15 +#define RT5631_PWR_CLASS_D (0x1 << 12) +#define RT5631_PWR_CLASS_D_BIT 12 +#define RT5631_PWR_ADC_L_CLK (0x1 << 11) +#define RT5631_PWR_ADC_L_CLK_BIT 11 +#define RT5631_PWR_ADC_R_CLK (0x1 << 10) +#define RT5631_PWR_ADC_R_CLK_BIT 10 +#define RT5631_PWR_DAC_L_CLK (0x1 << 9) +#define RT5631_PWR_DAC_L_CLK_BIT 9 +#define RT5631_PWR_DAC_R_CLK (0x1 << 8) +#define RT5631_PWR_DAC_R_CLK_BIT 8 +#define RT5631_PWR_DAC_REF (0x1 << 7) +#define RT5631_PWR_DAC_REF_BIT 7 +#define RT5631_PWR_DAC_L_TO_MIXER (0x1 << 6) +#define RT5631_PWR_DAC_L_TO_MIXER_BIT 6 +#define RT5631_PWR_DAC_R_TO_MIXER (0x1 << 5) +#define RT5631_PWR_DAC_R_TO_MIXER_BIT 5 + +/* Power managment addition 2 (0x3B) */ +#define RT5631_PWR_OUTMIXER_L (0x1 << 15) +#define RT5631_PWR_OUTMIXER_L_BIT 15 +#define RT5631_PWR_OUTMIXER_R (0x1 << 14) +#define RT5631_PWR_OUTMIXER_R_BIT 14 +#define RT5631_PWR_SPKMIXER_L (0x1 << 13) +#define RT5631_PWR_SPKMIXER_L_BIT 13 +#define RT5631_PWR_SPKMIXER_R (0x1 << 12) +#define RT5631_PWR_SPKMIXER_R_BIT 12 +#define RT5631_PWR_RECMIXER_L (0x1 << 11) +#define RT5631_PWR_RECMIXER_L_BIT 11 +#define RT5631_PWR_RECMIXER_R (0x1 << 10) +#define RT5631_PWR_RECMIXER_R_BIT 10 +#define RT5631_PWR_MIC1_BOOT_GAIN (0x1 << 5) +#define RT5631_PWR_MIC1_BOOT_GAIN_BIT 5 +#define RT5631_PWR_MIC2_BOOT_GAIN (0x1 << 4) +#define RT5631_PWR_MIC2_BOOT_GAIN_BIT 4 +#define RT5631_PWR_MICBIAS1_VOL (0x1 << 3) +#define RT5631_PWR_MICBIAS1_VOL_BIT 3 +#define RT5631_PWR_MICBIAS2_VOL (0x1 << 2) +#define RT5631_PWR_MICBIAS2_VOL_BIT 2 +#define RT5631_PWR_PLL1 (0x1 << 1) +#define RT5631_PWR_PLL1_BIT 1 +#define RT5631_PWR_PLL2 (0x1 << 0) +#define RT5631_PWR_PLL2_BIT 0 + +/* Power managment addition 3(0x3C) */ +#define RT5631_PWR_VREF (0x1 << 15) +#define RT5631_PWR_VREF_BIT 15 +#define RT5631_PWR_FAST_VREF_CTRL (0x1 << 14) +#define RT5631_PWR_FAST_VREF_CTRL_BIT 14 +#define RT5631_PWR_MAIN_BIAS (0x1 << 13) +#define RT5631_PWR_MAIN_BIAS_BIT 13 +#define RT5631_PWR_AXO1MIXER (0x1 << 11) +#define RT5631_PWR_AXO1MIXER_BIT 11 +#define RT5631_PWR_AXO2MIXER (0x1 << 10) +#define RT5631_PWR_AXO2MIXER_BIT 10 +#define RT5631_PWR_MONOMIXER (0x1 << 9) +#define RT5631_PWR_MONOMIXER_BIT 9 +#define RT5631_PWR_MONO_DEPOP_DIS (0x1 << 8) +#define RT5631_PWR_MONO_DEPOP_DIS_BIT 8 +#define RT5631_PWR_MONO_AMP_EN (0x1 << 7) +#define RT5631_PWR_MONO_AMP_EN_BIT 7 +#define RT5631_PWR_CHARGE_PUMP (0x1 << 4) +#define RT5631_PWR_CHARGE_PUMP_BIT 4 +#define RT5631_PWR_HP_L_AMP (0x1 << 3) +#define RT5631_PWR_HP_L_AMP_BIT 3 +#define RT5631_PWR_HP_R_AMP (0x1 << 2) +#define RT5631_PWR_HP_R_AMP_BIT 2 +#define RT5631_PWR_HP_DEPOP_DIS (0x1 << 1) +#define RT5631_PWR_HP_DEPOP_DIS_BIT 1 +#define RT5631_PWR_HP_AMP_DRIVING (0x1 << 0) +#define RT5631_PWR_HP_AMP_DRIVING_BIT 0 + +/* Power managment addition 4(0x3E) */ +#define RT5631_PWR_SPK_L_VOL (0x1 << 15) +#define RT5631_PWR_SPK_L_VOL_BIT 15 +#define RT5631_PWR_SPK_R_VOL (0x1 << 14) +#define RT5631_PWR_SPK_R_VOL_BIT 14 +#define RT5631_PWR_LOUT_VOL (0x1 << 13) +#define RT5631_PWR_LOUT_VOL_BIT 13 +#define RT5631_PWR_ROUT_VOL (0x1 << 12) +#define RT5631_PWR_ROUT_VOL_BIT 12 +#define RT5631_PWR_HP_L_OUT_VOL (0x1 << 11) +#define RT5631_PWR_HP_L_OUT_VOL_BIT 11 +#define RT5631_PWR_HP_R_OUT_VOL (0x1 << 10) +#define RT5631_PWR_HP_R_OUT_VOL_BIT 10 +#define RT5631_PWR_AXIL_IN_VOL (0x1 << 9) +#define RT5631_PWR_AXIL_IN_VOL_BIT 9 +#define RT5631_PWR_AXIR_IN_VOL (0x1 << 8) +#define RT5631_PWR_AXIR_IN_VOL_BIT 8 +#define RT5631_PWR_MONO_IN_P_VOL (0x1 << 7) +#define RT5631_PWR_MONO_IN_P_VOL_BIT 7 +#define RT5631_PWR_MONO_IN_N_VOL (0x1 << 6) +#define RT5631_PWR_MONO_IN_N_VOL_BIT 6 + +/* General Purpose Control Register(0x40) */ +#define RT5631_SPK_AMP_AUTO_RATIO_EN (0x1 << 15) + +#define RT5631_SPK_AMP_RATIO_CTRL_MASK (0x7 << 12) +#define RT5631_SPK_AMP_RATIO_CTRL_2_34 (0x0 << 12) /* 7.40DB */ +#define RT5631_SPK_AMP_RATIO_CTRL_1_99 (0x1 << 12) /* 5.99DB */ +#define RT5631_SPK_AMP_RATIO_CTRL_1_68 (0x2 << 12) /* 4.50DB */ +#define RT5631_SPK_AMP_RATIO_CTRL_1_56 (0x3 << 12) /* 3.86DB */ +#define RT5631_SPK_AMP_RATIO_CTRL_1_44 (0x4 << 12) /* 3.16DB */ +#define RT5631_SPK_AMP_RATIO_CTRL_1_27 (0x5 << 12) /* 2.10DB */ +#define RT5631_SPK_AMP_RATIO_CTRL_1_09 (0x6 << 12) /* 0.80DB */ +#define RT5631_SPK_AMP_RATIO_CTRL_1_00 (0x7 << 12) /* 0.00DB */ +#define RT5631_SPK_AMP_RATIO_CTRL_SHIFT 12 + +#define RT5631_STEREO_DAC_HI_PASS_FILT_EN (0x1 << 11) +#define RT5631_STEREO_ADC_HI_PASS_FILT_EN (0x1 << 10) +/* Select ADC Wind Filter Clock type */ +#define RT5631_ADC_WIND_FILT_MASK (0x3 << 4) +#define RT5631_ADC_WIND_FILT_8_16_32K (0x0 << 4) /*8/16/32k*/ +#define RT5631_ADC_WIND_FILT_11_22_44K (0x1 << 4) /*11/22/44k*/ +#define RT5631_ADC_WIND_FILT_12_24_48K (0x2 << 4) /*12/24/48k*/ +#define RT5631_ADC_WIND_FILT_EN (0x1 << 3) +/* SelectADC Wind Filter Corner Frequency */ +#define RT5631_ADC_WIND_CNR_FREQ_MASK (0x7 << 0) +#define RT5631_ADC_WIND_CNR_FREQ_82_113_122 (0x0 << 0) /* 82/113/122 Hz */ +#define RT5631_ADC_WIND_CNR_FREQ_102_141_153 (0x1 << 0) /* 102/141/153 Hz */ +#define RT5631_ADC_WIND_CNR_FREQ_131_180_156 (0x2 << 0) /* 131/180/156 Hz */ +#define RT5631_ADC_WIND_CNR_FREQ_163_225_245 (0x3 << 0) /* 163/225/245 Hz */ +#define RT5631_ADC_WIND_CNR_FREQ_204_281_306 (0x4 << 0) /* 204/281/306 Hz */ +#define RT5631_ADC_WIND_CNR_FREQ_261_360_392 (0x5 << 0) /* 261/360/392 Hz */ +#define RT5631_ADC_WIND_CNR_FREQ_327_450_490 (0x6 << 0) /* 327/450/490 Hz */ +#define RT5631_ADC_WIND_CNR_FREQ_408_563_612 (0x7 << 0) /* 408/563/612 Hz */ + +/* Global Clock Control Register(0x42) */ +#define RT5631_SYSCLK_SOUR_SEL_MASK (0x3 << 14) +#define RT5631_SYSCLK_SOUR_SEL_MCLK (0x0 << 14) +#define RT5631_SYSCLK_SOUR_SEL_PLL (0x1 << 14) +#define RT5631_SYSCLK_SOUR_SEL_PLL_TCK (0x2 << 14) + +#define RT5631_PLLCLK_SOUR_SEL_MASK (0x3 << 12) +#define RT5631_PLLCLK_SOUR_SEL_MCLK (0x0 << 12) +#define RT5631_PLLCLK_SOUR_SEL_BCLK (0x1 << 12) +#define RT5631_PLLCLK_SOUR_SEL_VBCLK (0x2 << 12) + +#define RT5631_PLLCLK_PRE_DIV1 (0x0 << 11) +#define RT5631_PLLCLK_PRE_DIV2 (0x1 << 11) + +/* PLL Control(0x44) */ +#define RT5631_PLL_CTRL_M_VAL(m) ((m)&0xf) +#define RT5631_PLL_CTRL_K_VAL(k) (((k)&0x7) << 4) +#define RT5631_PLL_CTRL_N_VAL(n) (((n)&0xff) << 8) + +/* Internal Status and IRQ Control2(0x4A) */ +#define RT5631_ADC_DATA_SEL_MASK (0x3 << 14) +#define RT5631_ADC_DATA_SEL_Disable (0x0 << 14) +#define RT5631_ADC_DATA_SEL_MIC1 (0x1 << 14) +#define RT5631_ADC_DATA_SEL_MIC1_SHIFT 14 +#define RT5631_ADC_DATA_SEL_MIC2 (0x2 << 14) +#define RT5631_ADC_DATA_SEL_MIC2_SHIFT 15 +#define RT5631_ADC_DATA_SEL_STO (0x3 << 14) +#define RT5631_ADC_DATA_SEL_SHIFT 14 + +/* GPIO Pin Configuration(0x4C) */ +#define RT5631_GPIO_PIN_FUN_SEL_MASK (0x1 << 15) +#define RT5631_GPIO_PIN_FUN_SEL_IRQ (0x1 << 15) +#define RT5631_GPIO_PIN_FUN_SEL_GPIO_DIMC (0x0 << 15) + +#define RT5631_GPIO_DMIC_FUN_SEL_MASK (0x1 << 3) +#define RT5631_GPIO_DMIC_FUN_SEL_DIMC (0x1 << 3) +#define RT5631_GPIO_DMIC_FUN_SEL_GPIO (0x0 << 3) + +#define RT5631_GPIO_PIN_CON_MASK (0x1 << 2) +#define RT5631_GPIO_PIN_SET_INPUT (0x0 << 2) +#define RT5631_GPIO_PIN_SET_OUTPUT (0x1 << 2) + +/* De-POP function Control 1(0x54) */ +#define RT5631_POW_ON_SOFT_GEN (0x1 << 15) +#define RT5631_EN_MUTE_UNMUTE_DEPOP (0x1 << 14) +#define RT5631_EN_DEPOP2_FOR_HP (0x1 << 7) +/* Power Down HPAMP_L Starts Up Signal */ +#define RT5631_PD_HPAMP_L_ST_UP (0x1 << 5) +/* Power Down HPAMP_R Starts Up Signal */ +#define RT5631_PD_HPAMP_R_ST_UP (0x1 << 4) +/* Enable left HP mute/unmute depop */ +#define RT5631_EN_HP_L_M_UN_MUTE_DEPOP (0x1 << 1) +/* Enable right HP mute/unmute depop */ +#define RT5631_EN_HP_R_M_UN_MUTE_DEPOP (0x1 << 0) + +/* De-POP Fnction Control(0x56) */ +#define RT5631_EN_ONE_BIT_DEPOP (0x1 << 15) +#define RT5631_EN_CAP_FREE_DEPOP (0x1 << 14) + +/* Jack Detect Control Register(0x5A) */ +#define RT5631_JD_USE_MASK (0x3 << 14) +#define RT5631_JD_USE_JD2 (0x3 << 14) +#define RT5631_JD_USE_JD1 (0x2 << 14) +#define RT5631_JD_USE_GPIO (0x1 << 14) +#define RT5631_JD_OFF (0x0 << 14) +/* JD trigger enable for HP */ +#define RT5631_JD_HP_EN (0x1 << 11) +#define RT5631_JD_HP_TRI_MASK (0x1 << 10) +#define RT5631_JD_HP_TRI_HI (0x1 << 10) +#define RT5631_JD_HP_TRI_LO (0x1 << 10) +/* JD trigger enable for speaker LP/LN */ +#define RT5631_JD_SPK_L_EN (0x1 << 9) +#define RT5631_JD_SPK_L_TRI_MASK (0x1 << 8) +#define RT5631_JD_SPK_L_TRI_HI (0x1 << 8) +#define RT5631_JD_SPK_L_TRI_LO (0x0 << 8) +/* JD trigger enable for speaker RP/RN */ +#define RT5631_JD_SPK_R_EN (0x1 << 7) +#define RT5631_JD_SPK_R_TRI_MASK (0x1 << 6) +#define RT5631_JD_SPK_R_TRI_HI (0x1 << 6) +#define RT5631_JD_SPK_R_TRI_LO (0x0 << 6) +/* JD trigger enable for monoout */ +#define RT5631_JD_MONO_EN (0x1 << 5) +#define RT5631_JD_MONO_TRI_MASK (0x1 << 4) +#define RT5631_JD_MONO_TRI_HI (0x1 << 4) +#define RT5631_JD_MONO_TRI_LO (0x0 << 4) +/* JD trigger enable for Lout */ +#define RT5631_JD_AUX_1_EN (0x1 << 3) +#define RT5631_JD_AUX_1_MASK (0x1 << 2) +#define RT5631_JD_AUX_1_TRI_HI (0x1 << 2) +#define RT5631_JD_AUX_1_TRI_LO (0x0 << 2) +/* JD trigger enable for Rout */ +#define RT5631_JD_AUX_2_EN (0x1 << 1) +#define RT5631_JD_AUX_2_MASK (0x1 << 0) +#define RT5631_JD_AUX_2_TRI_HI (0x1 << 0) +#define RT5631_JD_AUX_2_TRI_LO (0x0 << 0) + +/* ALC CONTROL 1(0x64) */ +#define RT5631_ALC_ATTACK_RATE_MASK (0x1F << 8) +#define RT5631_ALC_RECOVERY_RATE_MASK (0x1F << 0) + +/* ALC CONTROL 2(0x65) */ +/* select Compensation gain for Noise gate function */ +#define RT5631_ALC_COM_NOISE_GATE_MASK (0xF << 0) + +/* ALC CONTROL 3(0x66) */ +#define RT5631_ALC_FUN_MASK (0x3 << 14) +#define RT5631_ALC_FUN_DIS (0x0 << 14) +#define RT5631_ALC_ENA_DAC_PATH (0x1 << 14) +#define RT5631_ALC_ENA_ADC_PATH (0x3 << 14) +#define RT5631_ALC_PARA_UPDATE (0x1 << 13) +#define RT5631_ALC_LIMIT_LEVEL_MASK (0x1F << 8) +#define RT5631_ALC_NOISE_GATE_FUN_MASK (0x1 << 7) +#define RT5631_ALC_NOISE_GATE_FUN_DIS (0x0 << 7) +#define RT5631_ALC_NOISE_GATE_FUN_ENA (0x1 << 7) +/* ALC noise gate hold data function */ +#define RT5631_ALC_NOISE_GATE_H_D_MASK (0x1 << 6) +#define RT5631_ALC_NOISE_GATE_H_D_DIS (0x0 << 6) +#define RT5631_ALC_NOISE_GATE_H_D_ENA (0x1 << 6) + +/* Psedueo Stereo & Spatial Effect Block Control(0x68) */ +#define RT5631_SPATIAL_CTRL_EN (0x1 << 15) +#define RT5631_ALL_PASS_FILTER_EN (0x1 << 14) +#define RT5631_PSEUDO_STEREO_EN (0x1 << 13) +#define RT5631_STEREO_EXPENSION_EN (0x1 << 12) +/* 3D gain parameter */ +#define RT5631_GAIN_3D_PARA_MASK (0x3 << 6) +#define RT5631_GAIN_3D_PARA_1_00 (0x0 << 6) /* 3D gain 1.0 */ +#define RT5631_GAIN_3D_PARA_1_50 (0x1 << 6) /* 3D gain 1.5 */ +#define RT5631_GAIN_3D_PARA_2_00 (0x2 << 6) /* 3D gain 2.0 */ +/* 3D ratio parameter */ +#define RT5631_RATIO_3D_MASK (0x3 << 4) +#define RT5631_RATIO_3D_0_0 (0x0 << 4) /* 3D ratio 0.0 */ +#define RT5631_RATIO_3D_0_66 (0x1 << 4) /* 3D ratio 0.66 */ +#define RT5631_RATIO_3D_1_0 (0x2 << 4) /* 3D ratio 1.0 */ +/* select samplerate for all pass filter */ +#define RT5631_APF_FUN_SLE_MASK (0x3 << 0) +#define RT5631_APF_FUN_SEL_48K (0x3 << 0) +#define RT5631_APF_FUN_SEL_44_1K (0x2 << 0) +#define RT5631_APF_FUN_SEL_32K (0x1 << 0) +#define RT5631_APF_FUN_DIS (0x0 << 0) + +/* EQ CONTROL 1(0x6E) */ +#define RT5631_HW_EQ_PATH_SEL_MASK (0x1 << 15) +#define RT5631_HW_EQ_PATH_SEL_DAC (0x0 << 15) +#define RT5631_HW_EQ_PATH_SEL_ADC (0x1 << 15) +#define RT5631_HW_EQ_UPDATE_CTRL (0x1 << 14) + +#define RT5631_EN_HW_EQ_HPF2 (0x1 << 5) +#define RT5631_EN_HW_EQ_HPF1 (0x1 << 4) +#define RT5631_EN_HW_EQ_BP3 (0x1 << 3) +#define RT5631_EN_HW_EQ_BP2 (0x1 << 2) +#define RT5631_EN_HW_EQ_BP1 (0x1 << 1) +#define RT5631_EN_HW_EQ_LPF (0x1 << 0) + + +#endif /* __RTCODEC5631_H__ */ diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c new file mode 100644 index 0000000000..e8cdc166bd --- /dev/null +++ b/sound/soc/codecs/rt5640.c @@ -0,0 +1,3060 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5640.c -- RT5640/RT5639 ALSA SoC audio codec driver + * + * Copyright 2011 Realtek Semiconductor Corp. + * Author: Johnny Hsu + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5640.h" + +#define RT5640_DEVICE_ID 0x6231 + +#define RT5640_PR_RANGE_BASE (0xff + 1) +#define RT5640_PR_SPACING 0x100 + +#define RT5640_PR_BASE (RT5640_PR_RANGE_BASE + (0 * RT5640_PR_SPACING)) + +static const struct regmap_range_cfg rt5640_ranges[] = { + { .name = "PR", .range_min = RT5640_PR_BASE, + .range_max = RT5640_PR_BASE + 0xb4, + .selector_reg = RT5640_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5640_PRIV_DATA, + .window_len = 0x1, }, +}; + +static const struct reg_sequence init_list[] = { + {RT5640_PR_BASE + 0x3d, 0x3600}, + {RT5640_PR_BASE + 0x12, 0x0aa8}, + {RT5640_PR_BASE + 0x14, 0x0aaa}, + {RT5640_PR_BASE + 0x21, 0xe0e0}, + {RT5640_PR_BASE + 0x23, 0x1804}, +}; + +static const struct reg_default rt5640_reg[] = { + { 0x00, 0x000e }, + { 0x01, 0xc8c8 }, + { 0x02, 0xc8c8 }, + { 0x03, 0xc8c8 }, + { 0x04, 0x8000 }, + { 0x0d, 0x0000 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0000 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x27, 0x7060 }, + { 0x28, 0x7070 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5454 }, + { 0x2b, 0x5454 }, + { 0x2c, 0xaa00 }, + { 0x2d, 0x0000 }, + { 0x2e, 0xa000 }, + { 0x2f, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x45, 0xe000 }, + { 0x46, 0x003e }, + { 0x47, 0x003e }, + { 0x48, 0xf800 }, + { 0x49, 0x3800 }, + { 0x4a, 0x0004 }, + { 0x4c, 0xfc00 }, + { 0x4d, 0x0000 }, + { 0x4f, 0x01ff }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x01ff }, + { 0x53, 0xf000 }, + { 0x61, 0x0000 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c0 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x6a, 0x0000 }, + { 0x6c, 0x0000 }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x72, 0x8000 }, + { 0x73, 0x1114 }, + { 0x74, 0x0c00 }, + { 0x75, 0x1d00 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0000 }, + { 0x84, 0x0000 }, + { 0x85, 0x0008 }, + { 0x89, 0x0000 }, + { 0x8a, 0x0000 }, + { 0x8b, 0x0600 }, + { 0x8c, 0x0228 }, + { 0x8d, 0xa000 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0646 }, + { 0x91, 0x0c00 }, + { 0x92, 0x0000 }, + { 0x93, 0x3000 }, + { 0xb0, 0x2080 }, + { 0xb1, 0x0000 }, + { 0xb4, 0x2206 }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xb8, 0x034b }, + { 0xb9, 0x0066 }, + { 0xba, 0x000b }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x0000 }, + { 0xc0, 0x0400 }, + { 0xc2, 0x0000 }, + { 0xc4, 0x0000 }, + { 0xc5, 0x0000 }, + { 0xc6, 0x2000 }, + { 0xc8, 0x0000 }, + { 0xc9, 0x0000 }, + { 0xca, 0x0000 }, + { 0xcb, 0x0000 }, + { 0xcc, 0x0000 }, + { 0xcf, 0x0013 }, + { 0xd0, 0x0680 }, + { 0xd1, 0x1c17 }, + { 0xd2, 0x8c00 }, + { 0xd3, 0xaa20 }, + { 0xd6, 0x0400 }, + { 0xd9, 0x0809 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6231 }, +}; + +static int rt5640_reset(struct snd_soc_component *component) +{ + return snd_soc_component_write(component, RT5640_RESET, 0); +} + +static bool rt5640_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++) + if ((reg >= rt5640_ranges[i].window_start && + reg <= rt5640_ranges[i].window_start + + rt5640_ranges[i].window_len) || + (reg >= rt5640_ranges[i].range_min && + reg <= rt5640_ranges[i].range_max)) + return true; + + switch (reg) { + case RT5640_RESET: + case RT5640_ASRC_5: + case RT5640_EQ_CTRL1: + case RT5640_DRC_AGC_1: + case RT5640_ANC_CTRL1: + case RT5640_IRQ_CTRL2: + case RT5640_INT_IRQ_ST: + case RT5640_DSP_CTRL2: + case RT5640_DSP_CTRL3: + case RT5640_PRIV_INDEX: + case RT5640_PRIV_DATA: + case RT5640_PGM_REG_ARR1: + case RT5640_PGM_REG_ARR3: + case RT5640_DUMMY2: + case RT5640_VENDOR_ID: + case RT5640_VENDOR_ID1: + case RT5640_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool rt5640_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++) + if ((reg >= rt5640_ranges[i].window_start && + reg <= rt5640_ranges[i].window_start + + rt5640_ranges[i].window_len) || + (reg >= rt5640_ranges[i].range_min && + reg <= rt5640_ranges[i].range_max)) + return true; + + switch (reg) { + case RT5640_RESET: + case RT5640_SPK_VOL: + case RT5640_HP_VOL: + case RT5640_OUTPUT: + case RT5640_MONO_OUT: + case RT5640_IN1_IN2: + case RT5640_IN3_IN4: + case RT5640_INL_INR_VOL: + case RT5640_DAC1_DIG_VOL: + case RT5640_DAC2_DIG_VOL: + case RT5640_DAC2_CTRL: + case RT5640_ADC_DIG_VOL: + case RT5640_ADC_DATA: + case RT5640_ADC_BST_VOL: + case RT5640_STO_ADC_MIXER: + case RT5640_MONO_ADC_MIXER: + case RT5640_AD_DA_MIXER: + case RT5640_STO_DAC_MIXER: + case RT5640_MONO_DAC_MIXER: + case RT5640_DIG_MIXER: + case RT5640_DSP_PATH1: + case RT5640_DSP_PATH2: + case RT5640_DIG_INF_DATA: + case RT5640_REC_L1_MIXER: + case RT5640_REC_L2_MIXER: + case RT5640_REC_R1_MIXER: + case RT5640_REC_R2_MIXER: + case RT5640_HPO_MIXER: + case RT5640_SPK_L_MIXER: + case RT5640_SPK_R_MIXER: + case RT5640_SPO_L_MIXER: + case RT5640_SPO_R_MIXER: + case RT5640_SPO_CLSD_RATIO: + case RT5640_MONO_MIXER: + case RT5640_OUT_L1_MIXER: + case RT5640_OUT_L2_MIXER: + case RT5640_OUT_L3_MIXER: + case RT5640_OUT_R1_MIXER: + case RT5640_OUT_R2_MIXER: + case RT5640_OUT_R3_MIXER: + case RT5640_LOUT_MIXER: + case RT5640_PWR_DIG1: + case RT5640_PWR_DIG2: + case RT5640_PWR_ANLG1: + case RT5640_PWR_ANLG2: + case RT5640_PWR_MIXER: + case RT5640_PWR_VOL: + case RT5640_PRIV_INDEX: + case RT5640_PRIV_DATA: + case RT5640_I2S1_SDP: + case RT5640_I2S2_SDP: + case RT5640_ADDA_CLK1: + case RT5640_ADDA_CLK2: + case RT5640_DMIC: + case RT5640_GLB_CLK: + case RT5640_PLL_CTRL1: + case RT5640_PLL_CTRL2: + case RT5640_ASRC_1: + case RT5640_ASRC_2: + case RT5640_ASRC_3: + case RT5640_ASRC_4: + case RT5640_ASRC_5: + case RT5640_HP_OVCD: + case RT5640_CLS_D_OVCD: + case RT5640_CLS_D_OUT: + case RT5640_DEPOP_M1: + case RT5640_DEPOP_M2: + case RT5640_DEPOP_M3: + case RT5640_CHARGE_PUMP: + case RT5640_PV_DET_SPK_G: + case RT5640_MICBIAS: + case RT5640_EQ_CTRL1: + case RT5640_EQ_CTRL2: + case RT5640_WIND_FILTER: + case RT5640_DRC_AGC_1: + case RT5640_DRC_AGC_2: + case RT5640_DRC_AGC_3: + case RT5640_SVOL_ZC: + case RT5640_ANC_CTRL1: + case RT5640_ANC_CTRL2: + case RT5640_ANC_CTRL3: + case RT5640_JD_CTRL: + case RT5640_ANC_JD: + case RT5640_IRQ_CTRL1: + case RT5640_IRQ_CTRL2: + case RT5640_INT_IRQ_ST: + case RT5640_GPIO_CTRL1: + case RT5640_GPIO_CTRL2: + case RT5640_GPIO_CTRL3: + case RT5640_DSP_CTRL1: + case RT5640_DSP_CTRL2: + case RT5640_DSP_CTRL3: + case RT5640_DSP_CTRL4: + case RT5640_PGM_REG_ARR1: + case RT5640_PGM_REG_ARR2: + case RT5640_PGM_REG_ARR3: + case RT5640_PGM_REG_ARR4: + case RT5640_PGM_REG_ARR5: + case RT5640_SCB_FUNC: + case RT5640_SCB_CTRL: + case RT5640_BASE_BACK: + case RT5640_MP3_PLUS1: + case RT5640_MP3_PLUS2: + case RT5640_3D_HP: + case RT5640_ADJ_HPF: + case RT5640_HP_CALIB_AMP_DET: + case RT5640_HP_CALIB2: + case RT5640_SV_ZCD1: + case RT5640_SV_ZCD2: + case RT5640_DUMMY1: + case RT5640_DUMMY2: + case RT5640_DUMMY3: + case RT5640_VENDOR_ID: + case RT5640_VENDOR_ID1: + case RT5640_VENDOR_ID2: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -6562, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -1762, 3000); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5640_data_select[] = { + "Normal", "Swap", "left copy to right", "right copy to left"}; + +static SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA, + RT5640_IF1_DAC_SEL_SFT, rt5640_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5640_if1_adc_enum, RT5640_DIG_INF_DATA, + RT5640_IF1_ADC_SEL_SFT, rt5640_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5640_if2_dac_enum, RT5640_DIG_INF_DATA, + RT5640_IF2_DAC_SEL_SFT, rt5640_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5640_if2_adc_enum, RT5640_DIG_INF_DATA, + RT5640_IF2_ADC_SEL_SFT, rt5640_data_select); + +/* Class D speaker gain ratio */ +static const char * const rt5640_clsd_spk_ratio[] = {"1.66x", "1.83x", "1.94x", + "2x", "2.11x", "2.22x", "2.33x", "2.44x", "2.55x", "2.66x", "2.77x"}; + +static SOC_ENUM_SINGLE_DECL(rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT, + RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio); + +static const struct snd_kcontrol_new rt5640_snd_controls[] = { + /* Speaker Output Volume */ + SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL, + RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv), + /* Headphone Output Volume */ + SOC_DOUBLE("HP Channel Switch", RT5640_HP_VOL, + RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("HP Playback Volume", RT5640_HP_VOL, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv), + /* OUTPUT Control */ + SOC_DOUBLE("OUT Playback Switch", RT5640_OUTPUT, + RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("OUT Channel Switch", RT5640_OUTPUT, + RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("OUT Playback Volume", RT5640_OUTPUT, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE("DAC2 Playback Switch", RT5640_DAC2_CTRL, + RT5640_M_DAC_L2_VOL_SFT, RT5640_M_DAC_R2_VOL_SFT, 1, 1), + SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5640_DAC2_DIG_VOL, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, + 175, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, + 175, 0, dac_vol_tlv), + /* IN1/IN2/IN3 Control */ + SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2, + RT5640_BST_SFT1, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN2 Boost", RT5640_IN3_IN4, + RT5640_BST_SFT2, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN3 Boost", RT5640_IN1_IN2, + RT5640_BST_SFT2, 8, 0, bst_tlv), + + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL, + RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT, + 31, 1, in_vol_tlv), + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5640_ADC_DIG_VOL, + RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, + 127, 0, adc_vol_tlv), + SOC_DOUBLE("Mono ADC Capture Switch", RT5640_DUMMY1, + RT5640_M_MONO_ADC_L_SFT, RT5640_M_MONO_ADC_R_SFT, 1, 1), + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, + 127, 0, adc_vol_tlv), + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("ADC Boost Gain", RT5640_ADC_BST_VOL, + RT5640_ADC_L_BST_SFT, RT5640_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + /* Class D speaker gain ratio */ + SOC_ENUM("Class D SPK Ratio Control", rt5640_clsd_spk_ratio_enum), + + SOC_ENUM("ADC IF1 Data Switch", rt5640_if1_adc_enum), + SOC_ENUM("DAC IF1 Data Switch", rt5640_if1_dac_enum), + SOC_ENUM("ADC IF2 Data Switch", rt5640_if2_adc_enum), + SOC_ENUM("DAC IF2 Data Switch", rt5640_if2_dac_enum), +}; + +static const struct snd_kcontrol_new rt5640_specific_snd_controls[] = { + /* MONO Output Control */ + SOC_SINGLE("Mono Playback Switch", RT5640_MONO_OUT, RT5640_L_MUTE_SFT, + 1, 1), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + int idx, rate; + + rate = rt5640->sysclk / rl6231_get_pre_div(rt5640->regmap, + RT5640_ADDA_CLK1, RT5640_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); + if (idx < 0) + dev_err(component->dev, "Failed to set DMIC clock\n"); + else + snd_soc_component_update_bits(component, RT5640_DMIC, RT5640_DMIC_CLK_MASK, + idx << RT5640_DMIC_CLK_SFT); + return idx; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + if (!rt5640->asrc_en) + return 0; + + return 1; +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5640_sto_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER, + RT5640_M_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER, + RT5640_M_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_sto_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER, + RT5640_M_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER, + RT5640_M_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_mono_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER, + RT5640_M_MONO_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER, + RT5640_M_MONO_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_mono_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER, + RT5640_M_MONO_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER, + RT5640_M_MONO_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER, + RT5640_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER, + RT5640_M_IF1_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER, + RT5640_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER, + RT5640_M_IF1_DAC_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_L2_SFT, 1, 1), + SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER, + RT5640_M_ANC_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_R2_SFT, 1, 1), + SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER, + RT5640_M_ANC_DAC_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5639_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5639_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_mono_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_L1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_L2_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_R2_MONO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_mono_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_R1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_R2_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_L2_MONO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_dig_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_DIG_MIXER, + RT5640_M_STO_L_DAC_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_DIG_MIXER, + RT5640_M_DAC_L2_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_dig_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_DIG_MIXER, + RT5640_M_STO_R_DAC_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_DIG_MIXER, + RT5640_M_DAC_R2_DAC_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5640_rec_l_mix[] = { + SOC_DAPM_SINGLE("HPOL Switch", RT5640_REC_L2_MIXER, + RT5640_M_HP_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER, + RT5640_M_IN_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_L2_MIXER, + RT5640_M_BST2_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER, + RT5640_M_BST4_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER, + RT5640_M_BST1_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_REC_L2_MIXER, + RT5640_M_OM_L_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_rec_r_mix[] = { + SOC_DAPM_SINGLE("HPOR Switch", RT5640_REC_R2_MIXER, + RT5640_M_HP_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER, + RT5640_M_IN_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_R2_MIXER, + RT5640_M_BST2_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER, + RT5640_M_BST4_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER, + RT5640_M_BST1_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_REC_R2_MIXER, + RT5640_M_OM_R_RM_R_SFT, 1, 1), +}; + +/* Analog Output Mixer */ +static const struct snd_kcontrol_new rt5640_spk_l_mix[] = { + SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_SPK_L_MIXER, + RT5640_M_RM_L_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5640_SPK_L_MIXER, + RT5640_M_IN_L_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPK_L_MIXER, + RT5640_M_DAC_L1_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_SPK_L_MIXER, + RT5640_M_DAC_L2_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_SPK_L_MIXER, + RT5640_M_OM_L_SM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_spk_r_mix[] = { + SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_SPK_R_MIXER, + RT5640_M_RM_R_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5640_SPK_R_MIXER, + RT5640_M_IN_R_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPK_R_MIXER, + RT5640_M_DAC_R1_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_SPK_R_MIXER, + RT5640_M_DAC_R2_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_SPK_R_MIXER, + RT5640_M_OM_R_SM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_out_l_mix[] = { + SOC_DAPM_SINGLE("SPK MIXL Switch", RT5640_OUT_L3_MIXER, + RT5640_M_SM_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER, + RT5640_M_IN_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_OUT_L3_MIXER, + RT5640_M_RM_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_DAC_R2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_DAC_L2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_DAC_L1_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_out_r_mix[] = { + SOC_DAPM_SINGLE("SPK MIXR Switch", RT5640_OUT_R3_MIXER, + RT5640_M_SM_L_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_BST4_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_BST1_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER, + RT5640_M_IN_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_OUT_R3_MIXER, + RT5640_M_RM_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_DAC_L2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_DAC_R2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_DAC_R1_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5639_out_l_mix[] = { + SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER, + RT5640_M_IN_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_OUT_L3_MIXER, + RT5640_M_RM_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_DAC_L1_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5639_out_r_mix[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_BST4_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_BST1_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER, + RT5640_M_IN_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_OUT_R3_MIXER, + RT5640_M_RM_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_DAC_R1_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_spo_l_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_L_MIXER, + RT5640_M_DAC_R1_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPO_L_MIXER, + RT5640_M_DAC_L1_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_L_MIXER, + RT5640_M_SV_R_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL L Switch", RT5640_SPO_L_MIXER, + RT5640_M_SV_L_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_L_MIXER, + RT5640_M_BST1_SPM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_spo_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_R_MIXER, + RT5640_M_DAC_R1_SPM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_R_MIXER, + RT5640_M_SV_R_SPM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_R_MIXER, + RT5640_M_BST1_SPM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_hpo_mix[] = { + SOC_DAPM_SINGLE("HPO MIX DAC2 Switch", RT5640_HPO_MIXER, + RT5640_M_DAC2_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5640_HPO_MIXER, + RT5640_M_DAC1_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5640_HPO_MIXER, + RT5640_M_HPVOL_HM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5639_hpo_mix[] = { + SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5640_HPO_MIXER, + RT5640_M_DAC1_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5640_HPO_MIXER, + RT5640_M_HPVOL_HM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_lout_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_LOUT_MIXER, + RT5640_M_DAC_L1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_LOUT_MIXER, + RT5640_M_DAC_R1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_LOUT_MIXER, + RT5640_M_OV_L_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_LOUT_MIXER, + RT5640_M_OV_R_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_mono_mix[] = { + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_MIXER, + RT5640_M_DAC_R2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_MIXER, + RT5640_M_DAC_L2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_MONO_MIXER, + RT5640_M_OV_R_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_MONO_MIXER, + RT5640_M_OV_L_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_MONO_MIXER, + RT5640_M_BST1_MM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new spk_l_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL, + RT5640_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new spk_r_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL, + RT5640_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hp_l_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL, + RT5640_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hp_r_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL, + RT5640_R_MUTE_SFT, 1, 1); + +/* Stereo ADC source */ +static const char * const rt5640_stereo_adc1_src[] = { + "DIG MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5640_stereo_adc1_enum, RT5640_STO_ADC_MIXER, + RT5640_ADC_1_SRC_SFT, rt5640_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5640_sto_adc_1_mux = + SOC_DAPM_ENUM("Stereo ADC1 Mux", rt5640_stereo_adc1_enum); + +static const char * const rt5640_stereo_adc2_src[] = { + "DMIC1", "DMIC2", "DIG MIX" +}; + +static SOC_ENUM_SINGLE_DECL(rt5640_stereo_adc2_enum, RT5640_STO_ADC_MIXER, + RT5640_ADC_2_SRC_SFT, rt5640_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5640_sto_adc_2_mux = + SOC_DAPM_ENUM("Stereo ADC2 Mux", rt5640_stereo_adc2_enum); + +/* Mono ADC source */ +static const char * const rt5640_mono_adc_l1_src[] = { + "Mono DAC MIXL", "ADCL" +}; + +static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_l1_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_L1_SRC_SFT, rt5640_mono_adc_l1_src); + +static const struct snd_kcontrol_new rt5640_mono_adc_l1_mux = + SOC_DAPM_ENUM("Mono ADC1 left source", rt5640_mono_adc_l1_enum); + +static const char * const rt5640_mono_adc_l2_src[] = { + "DMIC L1", "DMIC L2", "Mono DAC MIXL" +}; + +static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_l2_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_L2_SRC_SFT, rt5640_mono_adc_l2_src); + +static const struct snd_kcontrol_new rt5640_mono_adc_l2_mux = + SOC_DAPM_ENUM("Mono ADC2 left source", rt5640_mono_adc_l2_enum); + +static const char * const rt5640_mono_adc_r1_src[] = { + "Mono DAC MIXR", "ADCR" +}; + +static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_r1_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_R1_SRC_SFT, rt5640_mono_adc_r1_src); + +static const struct snd_kcontrol_new rt5640_mono_adc_r1_mux = + SOC_DAPM_ENUM("Mono ADC1 right source", rt5640_mono_adc_r1_enum); + +static const char * const rt5640_mono_adc_r2_src[] = { + "DMIC R1", "DMIC R2", "Mono DAC MIXR" +}; + +static SOC_ENUM_SINGLE_DECL(rt5640_mono_adc_r2_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_R2_SRC_SFT, rt5640_mono_adc_r2_src); + +static const struct snd_kcontrol_new rt5640_mono_adc_r2_mux = + SOC_DAPM_ENUM("Mono ADC2 right source", rt5640_mono_adc_r2_enum); + +/* DAC2 channel source */ +static const char * const rt5640_dac_l2_src[] = { + "IF2", "Base L/R" +}; + +static int rt5640_dac_l2_values[] = { + 0, + 3, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_l2_enum, + RT5640_DSP_PATH2, RT5640_DAC_L2_SEL_SFT, + 0x3, rt5640_dac_l2_src, rt5640_dac_l2_values); + +static const struct snd_kcontrol_new rt5640_dac_l2_mux = + SOC_DAPM_ENUM("DAC2 left channel source", rt5640_dac_l2_enum); + +static const char * const rt5640_dac_r2_src[] = { + "IF2", +}; + +static int rt5640_dac_r2_values[] = { + 0, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dac_r2_enum, + RT5640_DSP_PATH2, RT5640_DAC_R2_SEL_SFT, + 0x3, rt5640_dac_r2_src, rt5640_dac_r2_values); + +static const struct snd_kcontrol_new rt5640_dac_r2_mux = + SOC_DAPM_ENUM("DAC2 right channel source", rt5640_dac_r2_enum); + +/* digital interface and iis interface map */ +static const char * const rt5640_dai_iis_map[] = { + "1:1|2:2", "1:2|2:1", "1:1|2:1", "1:2|2:2" +}; + +static int rt5640_dai_iis_map_values[] = { + 0, + 5, + 6, + 7, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5640_dai_iis_map_enum, + RT5640_I2S1_SDP, RT5640_I2S_IF_SFT, + 0x7, rt5640_dai_iis_map, + rt5640_dai_iis_map_values); + +static const struct snd_kcontrol_new rt5640_dai_mux = + SOC_DAPM_ENUM("DAI select", rt5640_dai_iis_map_enum); + +/* SDI select */ +static const char * const rt5640_sdi_sel[] = { + "IF1", "IF2" +}; + +static SOC_ENUM_SINGLE_DECL(rt5640_sdi_sel_enum, RT5640_I2S2_SDP, + RT5640_I2S2_SDI_SFT, rt5640_sdi_sel); + +static const struct snd_kcontrol_new rt5640_sdi_mux = + SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum); + +static void hp_amp_power_on(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + /* depop parameters */ + regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + + RT5640_CHPUMP_INT_REG1, 0x0700, 0x0200); + regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2, + RT5640_DEPOP_MASK, RT5640_DEPOP_MAN); + regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1, + RT5640_HP_CP_MASK | RT5640_HP_SG_MASK | RT5640_HP_CB_MASK, + RT5640_HP_CP_PU | RT5640_HP_SG_DIS | RT5640_HP_CB_PU); + regmap_write(rt5640->regmap, RT5640_PR_BASE + RT5640_HP_DCC_INT1, + 0x9f00); + /* headphone amp power on */ + regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1, + RT5640_PWR_FV1 | RT5640_PWR_FV2, 0); + regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1, + RT5640_PWR_HA, + RT5640_PWR_HA); + usleep_range(10000, 15000); + regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1, + RT5640_PWR_FV1 | RT5640_PWR_FV2 , + RT5640_PWR_FV1 | RT5640_PWR_FV2); +} + +static void rt5640_pmu_depop(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2, + RT5640_DEPOP_MASK | RT5640_DIG_DP_MASK, + RT5640_DEPOP_AUTO | RT5640_DIG_DP_EN); + regmap_update_bits(rt5640->regmap, RT5640_CHARGE_PUMP, + RT5640_PM_HP_MASK, RT5640_PM_HP_HV); + + regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M3, + RT5640_CP_FQ1_MASK | RT5640_CP_FQ2_MASK | RT5640_CP_FQ3_MASK, + (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ1_SFT) | + (RT5640_CP_FQ_12_KHZ << RT5640_CP_FQ2_SFT) | + (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ3_SFT)); + + regmap_write(rt5640->regmap, RT5640_PR_BASE + + RT5640_MAMP_INT_REG2, 0x1c00); + regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1, + RT5640_HP_CP_MASK | RT5640_HP_SG_MASK, + RT5640_HP_CP_PD | RT5640_HP_SG_EN); + regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + + RT5640_CHPUMP_INT_REG1, 0x0700, 0x0400); +} + +static int rt5640_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt5640_pmu_depop(component); + rt5640->hp_mute = false; + break; + + case SND_SOC_DAPM_PRE_PMD: + rt5640->hp_mute = true; + msleep(70); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5640_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + hp_amp_power_on(component); + snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, + RT5640_PWR_LM, RT5640_PWR_LM); + snd_soc_component_update_bits(component, RT5640_OUTPUT, + RT5640_L_MUTE | RT5640_R_MUTE, 0); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5640_OUTPUT, + RT5640_L_MUTE | RT5640_R_MUTE, + RT5640_L_MUTE | RT5640_R_MUTE); + snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, + RT5640_PWR_LM, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + hp_amp_power_on(component); + break; + default: + return 0; + } + + return 0; +} + +static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!rt5640->hp_mute) + msleep(80); + + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("Stereo Filter ASRC", 1, RT5640_ASRC_1, + 15, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 Filter ASRC", 1, RT5640_ASRC_1, + 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5640_ASRC_1, + 11, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC1 ASRC", 1, RT5640_ASRC_1, + 9, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC2 ASRC", 1, RT5640_ASRC_1, + 8, 0, NULL, 0), + + + /* Input Side */ + /* micbias */ + SND_SOC_DAPM_SUPPLY("LDO2", RT5640_PWR_ANLG1, + RT5640_PWR_LDO2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5640_PWR_ANLG2, + RT5640_PWR_MB1_BIT, 0, NULL, 0), + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("IN3N"), + SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC R2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5640_DMIC, RT5640_DMIC_1_EN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5640_DMIC, RT5640_DMIC_2_EN_SFT, 0, + NULL, 0), + /* Boost */ + SND_SOC_DAPM_PGA("BST1", RT5640_PWR_ANLG2, + RT5640_PWR_BST1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2, + RT5640_PWR_BST4_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST3", RT5640_PWR_ANLG2, + RT5640_PWR_BST2_BIT, 0, NULL, 0), + /* Input Volume */ + SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL, + RT5640_PWR_IN_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR VOL", RT5640_PWR_VOL, + RT5640_PWR_IN_R_BIT, 0, NULL, 0), + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5640_PWR_MIXER, RT5640_PWR_RM_L_BIT, 0, + rt5640_rec_l_mix, ARRAY_SIZE(rt5640_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5640_PWR_MIXER, RT5640_PWR_RM_R_BIT, 0, + rt5640_rec_r_mix, ARRAY_SIZE(rt5640_rec_r_mix)), + /* ADCs */ + SND_SOC_DAPM_ADC("ADC L", NULL, RT5640_PWR_DIG1, + RT5640_PWR_ADC_L_BIT, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, RT5640_PWR_DIG1, + RT5640_PWR_ADC_R_BIT, 0), + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_sto_adc_2_mux), + SND_SOC_DAPM_MUX("Stereo ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_sto_adc_2_mux), + SND_SOC_DAPM_MUX("Stereo ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_sto_adc_1_mux), + SND_SOC_DAPM_MUX("Stereo ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_sto_adc_1_mux), + SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_mono_adc_l2_mux), + SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_mono_adc_l1_mux), + SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_mono_adc_r1_mux), + SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_mono_adc_r2_mux), + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("Stereo Filter", RT5640_PWR_DIG2, + RT5640_PWR_ADC_SF_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5640_sto_adc_l_mix, ARRAY_SIZE(rt5640_sto_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5640_sto_adc_r_mix, ARRAY_SIZE(rt5640_sto_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("Mono Left Filter", RT5640_PWR_DIG2, + RT5640_PWR_ADC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5640_mono_adc_l_mix, ARRAY_SIZE(rt5640_mono_adc_l_mix)), + SND_SOC_DAPM_SUPPLY("Mono Right Filter", RT5640_PWR_DIG2, + RT5640_PWR_ADC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5640_mono_adc_r_mix, ARRAY_SIZE(rt5640_mono_adc_r_mix)), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5640_PWR_DIG1, + RT5640_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5640_PWR_DIG1, + RT5640_PWR_I2S2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("DAI1 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI1 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI1 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("SDI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux), + SND_SOC_DAPM_MUX("DAI2 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI2 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI2 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("SDI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux), + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5640_dac_l_mix, ARRAY_SIZE(rt5640_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5640_dac_r_mix, ARRAY_SIZE(rt5640_dac_r_mix)), + + /* DAC Mixer */ + SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5640_mono_dac_l_mix, ARRAY_SIZE(rt5640_mono_dac_l_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5640_mono_dac_r_mix, ARRAY_SIZE(rt5640_mono_dac_r_mix)), + SND_SOC_DAPM_MIXER("DIG MIXL", SND_SOC_NOPM, 0, 0, + rt5640_dig_l_mix, ARRAY_SIZE(rt5640_dig_l_mix)), + SND_SOC_DAPM_MIXER("DIG MIXR", SND_SOC_NOPM, 0, 0, + rt5640_dig_r_mix, ARRAY_SIZE(rt5640_dig_r_mix)), + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5640_PWR_DIG1, + RT5640_PWR_DAC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5640_PWR_DIG1, + RT5640_PWR_DAC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5640_PWR_DIG1, + RT5640_PWR_DAC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5640_PWR_DIG1, + RT5640_PWR_DAC_R2_BIT, 0, NULL, 0), + /* SPK/OUT Mixer */ + SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT, + 0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)), + SND_SOC_DAPM_MIXER("SPK MIXR", RT5640_PWR_MIXER, RT5640_PWR_SM_R_BIT, + 0, rt5640_spk_r_mix, ARRAY_SIZE(rt5640_spk_r_mix)), + /* Ouput Volume */ + SND_SOC_DAPM_PGA("SPKVOL L", RT5640_PWR_VOL, + RT5640_PWR_SV_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPKVOL R", RT5640_PWR_VOL, + RT5640_PWR_SV_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("OUTVOL L", RT5640_PWR_VOL, + RT5640_PWR_OV_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("OUTVOL R", RT5640_PWR_VOL, + RT5640_PWR_OV_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL L", RT5640_PWR_VOL, + RT5640_PWR_HV_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL R", RT5640_PWR_VOL, + RT5640_PWR_HV_R_BIT, 0, NULL, 0), + /* SPO/HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0, + 0, rt5640_spo_l_mix, ARRAY_SIZE(rt5640_spo_l_mix)), + SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0, + 0, rt5640_spo_r_mix, ARRAY_SIZE(rt5640_spo_r_mix)), + SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, + rt5640_lout_mix, ARRAY_SIZE(rt5640_lout_mix)), + SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM, + 0, 0, rt5640_hp_power_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, + rt5640_hp_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, + rt5640_lout_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("HP L Amp", RT5640_PWR_ANLG1, + RT5640_PWR_HP_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP R Amp", RT5640_PWR_ANLG1, + RT5640_PWR_HP_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1, + RT5640_PWR_CLS_D_BIT, 0, NULL, 0), + + /* Output Switch */ + SND_SOC_DAPM_SWITCH("Speaker L Playback", SND_SOC_NOPM, 0, 0, + &spk_l_enable_control), + SND_SOC_DAPM_SWITCH("Speaker R Playback", SND_SOC_NOPM, 0, 0, + &spk_r_enable_control), + SND_SOC_DAPM_SWITCH("HP L Playback", SND_SOC_NOPM, 0, 0, + &hp_l_enable_control), + SND_SOC_DAPM_SWITCH("HP R Playback", SND_SOC_NOPM, 0, 0, + &hp_r_enable_control), + SND_SOC_DAPM_POST("HP Post", rt5640_hp_post_event), + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("SPOLP"), + SND_SOC_DAPM_OUTPUT("SPOLN"), + SND_SOC_DAPM_OUTPUT("SPORP"), + SND_SOC_DAPM_OUTPUT("SPORN"), + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), +}; + +static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = { + /* Audio DSP */ + SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0), + /* ANC */ + SND_SOC_DAPM_PGA("ANC", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DAC2 channel Mux */ + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dac_r2_mux), + + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5640_sto_dac_l_mix, ARRAY_SIZE(rt5640_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)), + + SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0, + 0), + SND_SOC_DAPM_DAC("DAC L2", NULL, SND_SOC_NOPM, 0, + 0), + + SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT, + 0, rt5640_out_l_mix, ARRAY_SIZE(rt5640_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT, + 0, rt5640_out_r_mix, ARRAY_SIZE(rt5640_out_r_mix)), + + SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0, + rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)), + SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0, + rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)), + + SND_SOC_DAPM_MIXER("Mono MIX", RT5640_PWR_ANLG1, RT5640_PWR_MM_BIT, 0, + rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)), + SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1, + RT5640_PWR_MA_BIT, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("MONOP"), + SND_SOC_DAPM_OUTPUT("MONON"), +}; + +static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5639_sto_dac_l_mix, ARRAY_SIZE(rt5639_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5639_sto_dac_r_mix, ARRAY_SIZE(rt5639_sto_dac_r_mix)), + + SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT, + 0, rt5639_out_l_mix, ARRAY_SIZE(rt5639_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT, + 0, rt5639_out_r_mix, ARRAY_SIZE(rt5639_out_r_mix)), + + SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0, + rt5639_hpo_mix, ARRAY_SIZE(rt5639_hpo_mix)), + SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0, + rt5639_hpo_mix, ARRAY_SIZE(rt5639_hpo_mix)), +}; + +static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { + { "I2S1", NULL, "Stereo Filter ASRC", is_using_asrc }, + { "I2S2", NULL, "I2S2 ASRC", is_using_asrc }, + { "I2S2", NULL, "I2S2 Filter ASRC", is_using_asrc }, + { "DMIC1", NULL, "DMIC1 ASRC", is_using_asrc }, + { "DMIC2", NULL, "DMIC2 ASRC", is_using_asrc }, + + {"IN1P", NULL, "LDO2"}, + {"IN2P", NULL, "LDO2"}, + {"IN3P", NULL, "LDO2"}, + + {"DMIC L1", NULL, "DMIC1"}, + {"DMIC R1", NULL, "DMIC1"}, + {"DMIC L2", NULL, "DMIC2"}, + {"DMIC R2", NULL, "DMIC2"}, + + {"BST1", NULL, "IN1P"}, + {"BST1", NULL, "IN1N"}, + {"BST2", NULL, "IN2P"}, + {"BST2", NULL, "IN2N"}, + {"BST3", NULL, "IN3P"}, + {"BST3", NULL, "IN3N"}, + + {"INL VOL", NULL, "IN2P"}, + {"INR VOL", NULL, "IN2N"}, + + {"RECMIXL", "HPOL Switch", "HPOL"}, + {"RECMIXL", "INL Switch", "INL VOL"}, + {"RECMIXL", "BST3 Switch", "BST3"}, + {"RECMIXL", "BST2 Switch", "BST2"}, + {"RECMIXL", "BST1 Switch", "BST1"}, + {"RECMIXL", "OUT MIXL Switch", "OUT MIXL"}, + + {"RECMIXR", "HPOR Switch", "HPOR"}, + {"RECMIXR", "INR Switch", "INR VOL"}, + {"RECMIXR", "BST3 Switch", "BST3"}, + {"RECMIXR", "BST2 Switch", "BST2"}, + {"RECMIXR", "BST1 Switch", "BST1"}, + {"RECMIXR", "OUT MIXR Switch", "OUT MIXR"}, + + {"ADC L", NULL, "RECMIXL"}, + {"ADC R", NULL, "RECMIXR"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC L2", NULL, "DMIC CLK"}, + {"DMIC L2", NULL, "DMIC2 Power"}, + {"DMIC R2", NULL, "DMIC CLK"}, + {"DMIC R2", NULL, "DMIC2 Power"}, + + {"Stereo ADC L2 Mux", "DMIC1", "DMIC L1"}, + {"Stereo ADC L2 Mux", "DMIC2", "DMIC L2"}, + {"Stereo ADC L2 Mux", "DIG MIX", "DIG MIXL"}, + {"Stereo ADC L1 Mux", "ADC", "ADC L"}, + {"Stereo ADC L1 Mux", "DIG MIX", "DIG MIXL"}, + + {"Stereo ADC R1 Mux", "ADC", "ADC R"}, + {"Stereo ADC R1 Mux", "DIG MIX", "DIG MIXR"}, + {"Stereo ADC R2 Mux", "DMIC1", "DMIC R1"}, + {"Stereo ADC R2 Mux", "DMIC2", "DMIC R2"}, + {"Stereo ADC R2 Mux", "DIG MIX", "DIG MIXR"}, + + {"Mono ADC L2 Mux", "DMIC L1", "DMIC L1"}, + {"Mono ADC L2 Mux", "DMIC L2", "DMIC L2"}, + {"Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL"}, + {"Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL"}, + {"Mono ADC L1 Mux", "ADCL", "ADC L"}, + + {"Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR"}, + {"Mono ADC R1 Mux", "ADCR", "ADC R"}, + {"Mono ADC R2 Mux", "DMIC R1", "DMIC R1"}, + {"Mono ADC R2 Mux", "DMIC R2", "DMIC R2"}, + {"Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR"}, + + {"Stereo ADC MIXL", "ADC1 Switch", "Stereo ADC L1 Mux"}, + {"Stereo ADC MIXL", "ADC2 Switch", "Stereo ADC L2 Mux"}, + {"Stereo ADC MIXL", NULL, "Stereo Filter"}, + + {"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"}, + {"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"}, + {"Stereo ADC MIXR", NULL, "Stereo Filter"}, + + {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"}, + {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"}, + {"Mono ADC MIXL", NULL, "Mono Left Filter"}, + + {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"}, + {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"}, + {"Mono ADC MIXR", NULL, "Mono Right Filter"}, + + {"IF2 ADC L", NULL, "Mono ADC MIXL"}, + {"IF2 ADC R", NULL, "Mono ADC MIXR"}, + {"IF1 ADC L", NULL, "Stereo ADC MIXL"}, + {"IF1 ADC R", NULL, "Stereo ADC MIXR"}, + + {"IF1 ADC", NULL, "I2S1"}, + {"IF1 ADC", NULL, "IF1 ADC L"}, + {"IF1 ADC", NULL, "IF1 ADC R"}, + {"IF2 ADC", NULL, "I2S2"}, + {"IF2 ADC", NULL, "IF2 ADC L"}, + {"IF2 ADC", NULL, "IF2 ADC R"}, + + {"DAI1 TX Mux", "1:1|2:2", "IF1 ADC"}, + {"DAI1 TX Mux", "1:2|2:1", "IF2 ADC"}, + {"DAI1 IF1 Mux", "1:1|2:1", "IF1 ADC"}, + {"DAI1 IF2 Mux", "1:1|2:1", "IF2 ADC"}, + {"SDI1 TX Mux", "IF1", "DAI1 IF1 Mux"}, + {"SDI1 TX Mux", "IF2", "DAI1 IF2 Mux"}, + + {"DAI2 TX Mux", "1:2|2:1", "IF1 ADC"}, + {"DAI2 TX Mux", "1:1|2:2", "IF2 ADC"}, + {"DAI2 IF1 Mux", "1:2|2:2", "IF1 ADC"}, + {"DAI2 IF2 Mux", "1:2|2:2", "IF2 ADC"}, + {"SDI2 TX Mux", "IF1", "DAI2 IF1 Mux"}, + {"SDI2 TX Mux", "IF2", "DAI2 IF2 Mux"}, + + {"AIF1TX", NULL, "DAI1 TX Mux"}, + {"AIF1TX", NULL, "SDI1 TX Mux"}, + {"AIF2TX", NULL, "DAI2 TX Mux"}, + {"AIF2TX", NULL, "SDI2 TX Mux"}, + + {"DAI1 RX Mux", "1:1|2:2", "AIF1RX"}, + {"DAI1 RX Mux", "1:1|2:1", "AIF1RX"}, + {"DAI1 RX Mux", "1:2|2:1", "AIF2RX"}, + {"DAI1 RX Mux", "1:2|2:2", "AIF2RX"}, + + {"DAI2 RX Mux", "1:2|2:1", "AIF1RX"}, + {"DAI2 RX Mux", "1:1|2:1", "AIF1RX"}, + {"DAI2 RX Mux", "1:1|2:2", "AIF2RX"}, + {"DAI2 RX Mux", "1:2|2:2", "AIF2RX"}, + + {"IF1 DAC", NULL, "I2S1"}, + {"IF1 DAC", NULL, "DAI1 RX Mux"}, + {"IF2 DAC", NULL, "I2S2"}, + {"IF2 DAC", NULL, "DAI2 RX Mux"}, + + {"IF1 DAC L", NULL, "IF1 DAC"}, + {"IF1 DAC R", NULL, "IF1 DAC"}, + {"IF2 DAC L", NULL, "IF2 DAC"}, + {"IF2 DAC R", NULL, "IF2 DAC"}, + + {"DAC MIXL", "Stereo ADC Switch", "Stereo ADC MIXL"}, + {"DAC MIXL", "INF1 Switch", "IF1 DAC L"}, + {"DAC MIXL", NULL, "DAC L1 Power"}, + {"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"}, + {"DAC MIXR", "INF1 Switch", "IF1 DAC R"}, + {"DAC MIXR", NULL, "DAC R1 Power"}, + + {"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"}, + {"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"}, + + {"Mono DAC MIXL", "DAC L1 Switch", "DAC MIXL"}, + {"Mono DAC MIXR", "DAC R1 Switch", "DAC MIXR"}, + + {"DIG MIXL", "DAC L1 Switch", "DAC MIXL"}, + {"DIG MIXR", "DAC R1 Switch", "DAC MIXR"}, + + {"DAC L1", NULL, "Stereo DAC MIXL"}, + {"DAC L1", NULL, "DAC L1 Power"}, + {"DAC R1", NULL, "Stereo DAC MIXR"}, + {"DAC R1", NULL, "DAC R1 Power"}, + + {"SPK MIXL", "REC MIXL Switch", "RECMIXL"}, + {"SPK MIXL", "INL Switch", "INL VOL"}, + {"SPK MIXL", "DAC L1 Switch", "DAC L1"}, + {"SPK MIXL", "OUT MIXL Switch", "OUT MIXL"}, + {"SPK MIXR", "REC MIXR Switch", "RECMIXR"}, + {"SPK MIXR", "INR Switch", "INR VOL"}, + {"SPK MIXR", "DAC R1 Switch", "DAC R1"}, + {"SPK MIXR", "OUT MIXR Switch", "OUT MIXR"}, + + {"OUT MIXL", "BST1 Switch", "BST1"}, + {"OUT MIXL", "INL Switch", "INL VOL"}, + {"OUT MIXL", "REC MIXL Switch", "RECMIXL"}, + {"OUT MIXL", "DAC L1 Switch", "DAC L1"}, + + {"OUT MIXR", "BST2 Switch", "BST2"}, + {"OUT MIXR", "BST1 Switch", "BST1"}, + {"OUT MIXR", "INR Switch", "INR VOL"}, + {"OUT MIXR", "REC MIXR Switch", "RECMIXR"}, + {"OUT MIXR", "DAC R1 Switch", "DAC R1"}, + + {"SPKVOL L", NULL, "SPK MIXL"}, + {"SPKVOL R", NULL, "SPK MIXR"}, + {"HPOVOL L", NULL, "OUT MIXL"}, + {"HPOVOL R", NULL, "OUT MIXR"}, + {"OUTVOL L", NULL, "OUT MIXL"}, + {"OUTVOL R", NULL, "OUT MIXR"}, + + {"SPOL MIX", "DAC R1 Switch", "DAC R1"}, + {"SPOL MIX", "DAC L1 Switch", "DAC L1"}, + {"SPOL MIX", "SPKVOL R Switch", "SPKVOL R"}, + {"SPOL MIX", "SPKVOL L Switch", "SPKVOL L"}, + {"SPOL MIX", "BST1 Switch", "BST1"}, + {"SPOR MIX", "DAC R1 Switch", "DAC R1"}, + {"SPOR MIX", "SPKVOL R Switch", "SPKVOL R"}, + {"SPOR MIX", "BST1 Switch", "BST1"}, + + {"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"}, + {"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"}, + {"HPO MIX L", NULL, "HP L Amp"}, + {"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"}, + {"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"}, + {"HPO MIX R", NULL, "HP R Amp"}, + + {"LOUT MIX", "DAC L1 Switch", "DAC L1"}, + {"LOUT MIX", "DAC R1 Switch", "DAC R1"}, + {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"}, + {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"}, + + {"HP Amp", NULL, "HPO MIX L"}, + {"HP Amp", NULL, "HPO MIX R"}, + + {"Speaker L Playback", "Switch", "SPOL MIX"}, + {"Speaker R Playback", "Switch", "SPOR MIX"}, + {"SPOLP", NULL, "Speaker L Playback"}, + {"SPOLN", NULL, "Speaker L Playback"}, + {"SPORP", NULL, "Speaker R Playback"}, + {"SPORN", NULL, "Speaker R Playback"}, + + {"SPOLP", NULL, "Improve SPK Amp Drv"}, + {"SPOLN", NULL, "Improve SPK Amp Drv"}, + {"SPORP", NULL, "Improve SPK Amp Drv"}, + {"SPORN", NULL, "Improve SPK Amp Drv"}, + + {"HPOL", NULL, "Improve HP Amp Drv"}, + {"HPOR", NULL, "Improve HP Amp Drv"}, + + {"HP L Playback", "Switch", "HP Amp"}, + {"HP R Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HP L Playback"}, + {"HPOR", NULL, "HP R Playback"}, + + {"LOUT amp", NULL, "LOUT MIX"}, + {"LOUTL", NULL, "LOUT amp"}, + {"LOUTR", NULL, "LOUT amp"}, +}; + +static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = { + {"ANC", NULL, "Stereo ADC MIXL"}, + {"ANC", NULL, "Stereo ADC MIXR"}, + + {"Audio DSP", NULL, "DAC MIXL"}, + {"Audio DSP", NULL, "DAC MIXR"}, + + {"DAC L2 Mux", "IF2", "IF2 DAC L"}, + {"DAC L2 Mux", "Base L/R", "Audio DSP"}, + {"DAC L2 Mux", NULL, "DAC L2 Power"}, + {"DAC R2 Mux", "IF2", "IF2 DAC R"}, + {"DAC R2 Mux", NULL, "DAC R2 Power"}, + + {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"Stereo DAC MIXL", "ANC Switch", "ANC"}, + {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + {"Stereo DAC MIXR", "ANC Switch", "ANC"}, + + {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"}, + + {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"}, + + {"DIG MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + {"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + + {"DAC L2", NULL, "Mono DAC MIXL"}, + {"DAC L2", NULL, "DAC L2 Power"}, + {"DAC R2", NULL, "Mono DAC MIXR"}, + {"DAC R2", NULL, "DAC R2 Power"}, + + {"SPK MIXL", "DAC L2 Switch", "DAC L2"}, + {"SPK MIXR", "DAC R2 Switch", "DAC R2"}, + + {"OUT MIXL", "SPK MIXL Switch", "SPK MIXL"}, + {"OUT MIXR", "SPK MIXR Switch", "SPK MIXR"}, + + {"OUT MIXL", "DAC R2 Switch", "DAC R2"}, + {"OUT MIXL", "DAC L2 Switch", "DAC L2"}, + + {"OUT MIXR", "DAC L2 Switch", "DAC L2"}, + {"OUT MIXR", "DAC R2 Switch", "DAC R2"}, + + {"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"}, + {"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"}, + + {"Mono MIX", "DAC R2 Switch", "DAC R2"}, + {"Mono MIX", "DAC L2 Switch", "DAC L2"}, + {"Mono MIX", "OUTVOL R Switch", "OUTVOL R"}, + {"Mono MIX", "OUTVOL L Switch", "OUTVOL L"}, + {"Mono MIX", "BST1 Switch", "BST1"}, + + {"MONOP", NULL, "Mono MIX"}, + {"MONON", NULL, "Mono MIX"}, + {"MONOP", NULL, "Improve MONO Amp Drv"}, +}; + +static const struct snd_soc_dapm_route rt5639_specific_dapm_routes[] = { + {"Stereo DAC MIXL", "DAC L2 Switch", "IF2 DAC L"}, + {"Stereo DAC MIXR", "DAC R2 Switch", "IF2 DAC R"}, + + {"Mono DAC MIXL", "DAC L2 Switch", "IF2 DAC L"}, + {"Mono DAC MIXL", "DAC R2 Switch", "IF2 DAC R"}, + + {"Mono DAC MIXR", "DAC R2 Switch", "IF2 DAC R"}, + {"Mono DAC MIXR", "DAC L2 Switch", "IF2 DAC L"}, + + {"DIG MIXL", "DAC L2 Switch", "IF2 DAC L"}, + {"DIG MIXR", "DAC R2 Switch", "IF2 DAC R"}, + + {"IF2 DAC L", NULL, "DAC L2 Power"}, + {"IF2 DAC R", NULL, "DAC R2 Power"}, +}; + +static int get_sdp_info(struct snd_soc_component *component, int dai_id) +{ + int ret = 0, val; + + if (component == NULL) + return -EINVAL; + + val = snd_soc_component_read(component, RT5640_I2S1_SDP); + val = (val & RT5640_I2S_IF_MASK) >> RT5640_I2S_IF_SFT; + switch (dai_id) { + case RT5640_AIF1: + switch (val) { + case RT5640_IF_123: + case RT5640_IF_132: + ret |= RT5640_U_IF1; + break; + case RT5640_IF_113: + ret |= RT5640_U_IF1; + fallthrough; + case RT5640_IF_312: + case RT5640_IF_213: + ret |= RT5640_U_IF2; + break; + } + break; + + case RT5640_AIF2: + switch (val) { + case RT5640_IF_231: + case RT5640_IF_213: + ret |= RT5640_U_IF1; + break; + case RT5640_IF_223: + ret |= RT5640_U_IF1; + fallthrough; + case RT5640_IF_123: + case RT5640_IF_321: + ret |= RT5640_U_IF2; + break; + } + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int rt5640_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk; + int dai_sel, pre_div, bclk_ms, frame_size; + + rt5640->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n", + rt5640->lrck[dai->id], dai->id); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return frame_size; + } + if (frame_size > 32) + bclk_ms = 1; + else + bclk_ms = 0; + rt5640->bclk[dai->id] = rt5640->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5640->bclk[dai->id], rt5640->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= RT5640_I2S_DL_20; + break; + case 24: + val_len |= RT5640_I2S_DL_24; + break; + case 8: + val_len |= RT5640_I2S_DL_8; + break; + default: + return -EINVAL; + } + + dai_sel = get_sdp_info(component, dai->id); + if (dai_sel < 0) { + dev_err(component->dev, "Failed to get sdp info: %d\n", dai_sel); + return -EINVAL; + } + if (dai_sel & RT5640_U_IF1) { + mask_clk = RT5640_I2S_BCLK_MS1_MASK | RT5640_I2S_PD1_MASK; + val_clk = bclk_ms << RT5640_I2S_BCLK_MS1_SFT | + pre_div << RT5640_I2S_PD1_SFT; + snd_soc_component_update_bits(component, RT5640_I2S1_SDP, + RT5640_I2S_DL_MASK, val_len); + snd_soc_component_update_bits(component, RT5640_ADDA_CLK1, mask_clk, val_clk); + } + if (dai_sel & RT5640_U_IF2) { + mask_clk = RT5640_I2S_BCLK_MS2_MASK | RT5640_I2S_PD2_MASK; + val_clk = bclk_ms << RT5640_I2S_BCLK_MS2_SFT | + pre_div << RT5640_I2S_PD2_SFT; + snd_soc_component_update_bits(component, RT5640_I2S2_SDP, + RT5640_I2S_DL_MASK, val_len); + snd_soc_component_update_bits(component, RT5640_ADDA_CLK1, mask_clk, val_clk); + } + + return 0; +} + +static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + int dai_sel; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5640->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5640_I2S_MS_S; + rt5640->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5640_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5640_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5640_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5640_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + dai_sel = get_sdp_info(component, dai->id); + if (dai_sel < 0) { + dev_err(component->dev, "Failed to get sdp info: %d\n", dai_sel); + return -EINVAL; + } + if (dai_sel & RT5640_U_IF1) { + snd_soc_component_update_bits(component, RT5640_I2S1_SDP, + RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK | + RT5640_I2S_DF_MASK, reg_val); + } + if (dai_sel & RT5640_U_IF2) { + snd_soc_component_update_bits(component, RT5640_I2S2_SDP, + RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK | + RT5640_I2S_DF_MASK, reg_val); + } + + return 0; +} + +static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + unsigned int pll_bit = 0; + int ret; + + switch (clk_id) { + case RT5640_SCLK_S_MCLK: + ret = clk_set_rate(rt5640->mclk, freq); + if (ret) + return ret; + + reg_val |= RT5640_SCLK_SRC_MCLK; + break; + case RT5640_SCLK_S_PLL1: + reg_val |= RT5640_SCLK_SRC_PLL1; + pll_bit |= RT5640_PWR_PLL; + break; + case RT5640_SCLK_S_RCCLK: + reg_val |= RT5640_SCLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5640_PWR_ANLG2, + RT5640_PWR_PLL, pll_bit); + snd_soc_component_update_bits(component, RT5640_GLB_CLK, + RT5640_SCLK_SRC_MASK, reg_val); + rt5640->sysclk = freq; + rt5640->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + return 0; +} + +static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5640->pll_src && freq_in == rt5640->pll_in && + freq_out == rt5640->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5640->pll_in = 0; + rt5640->pll_out = 0; + snd_soc_component_update_bits(component, RT5640_GLB_CLK, + RT5640_SCLK_SRC_MASK, RT5640_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5640_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5640_GLB_CLK, + RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_MCLK); + break; + case RT5640_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, RT5640_GLB_CLK, + RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1); + break; + case RT5640_PLL1_S_BCLK2: + snd_soc_component_update_bits(component, RT5640_GLB_CLK, + RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2); + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5640_PLL_CTRL1, + (pll_code.n_code << RT5640_PLL_N_SFT) | pll_code.k_code); + snd_soc_component_write(component, RT5640_PLL_CTRL2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5640_PLL_M_SFT) | + (pll_code.m_bp << RT5640_PLL_M_BP_SFT)); + + rt5640->pll_in = freq_in; + rt5640->pll_out = freq_out; + rt5640->pll_src = source; + + return 0; +} + +static int rt5640_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (IS_ERR(rt5640->mclk)) + break; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(rt5640->mclk); + } else { + ret = clk_prepare_enable(rt5640->mclk); + if (ret) + return ret; + } + break; + + case SND_SOC_BIAS_STANDBY: + if (SND_SOC_BIAS_OFF == snd_soc_component_get_bias_level(component)) { + snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, + RT5640_PWR_VREF1 | RT5640_PWR_MB | + RT5640_PWR_BG | RT5640_PWR_VREF2, + RT5640_PWR_VREF1 | RT5640_PWR_MB | + RT5640_PWR_BG | RT5640_PWR_VREF2); + usleep_range(10000, 15000); + snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, + RT5640_PWR_FV1 | RT5640_PWR_FV2, + RT5640_PWR_FV1 | RT5640_PWR_FV2); + snd_soc_component_update_bits(component, RT5640_DUMMY1, + 0x1, 0x1); + snd_soc_component_update_bits(component, RT5640_MICBIAS, + 0x0030, 0x0030); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, RT5640_DEPOP_M1, 0x0004); + snd_soc_component_write(component, RT5640_DEPOP_M2, 0x1100); + snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x1, 0); + snd_soc_component_write(component, RT5640_PWR_DIG1, 0x0000); + snd_soc_component_write(component, RT5640_PWR_DIG2, 0x0000); + snd_soc_component_write(component, RT5640_PWR_VOL, 0x0000); + snd_soc_component_write(component, RT5640_PWR_MIXER, 0x0000); + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) + snd_soc_component_write(component, RT5640_PWR_ANLG1, + 0x2818); + else + snd_soc_component_write(component, RT5640_PWR_ANLG1, + 0x0000); + snd_soc_component_write(component, RT5640_PWR_ANLG2, 0x0000); + break; + + default: + break; + } + + return 0; +} + +int rt5640_dmic_enable(struct snd_soc_component *component, + bool dmic1_data_pin, bool dmic2_data_pin) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, + RT5640_GP2_PIN_MASK, RT5640_GP2_PIN_DMIC1_SCL); + + if (dmic1_data_pin) { + regmap_update_bits(rt5640->regmap, RT5640_DMIC, + RT5640_DMIC_1_DP_MASK, RT5640_DMIC_1_DP_GPIO3); + regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, + RT5640_GP3_PIN_MASK, RT5640_GP3_PIN_DMIC1_SDA); + } + + if (dmic2_data_pin) { + regmap_update_bits(rt5640->regmap, RT5640_DMIC, + RT5640_DMIC_2_DP_MASK, RT5640_DMIC_2_DP_GPIO4); + regmap_update_bits(rt5640->regmap, RT5640_GPIO_CTRL1, + RT5640_GP4_PIN_MASK, RT5640_GP4_PIN_DMIC2_SDA); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5640_dmic_enable); + +int rt5640_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + unsigned int asrc2_mask = 0; + unsigned int asrc2_value = 0; + + switch (clk_src) { + case RT5640_CLK_SEL_SYS: + case RT5640_CLK_SEL_ASRC: + break; + + default: + return -EINVAL; + } + + if (!filter_mask) + return -EINVAL; + + if (filter_mask & RT5640_DA_STEREO_FILTER) { + asrc2_mask |= RT5640_STO_DAC_M_MASK; + asrc2_value = (asrc2_value & ~RT5640_STO_DAC_M_MASK) + | (clk_src << RT5640_STO_DAC_M_SFT); + } + + if (filter_mask & RT5640_DA_MONO_L_FILTER) { + asrc2_mask |= RT5640_MDA_L_M_MASK; + asrc2_value = (asrc2_value & ~RT5640_MDA_L_M_MASK) + | (clk_src << RT5640_MDA_L_M_SFT); + } + + if (filter_mask & RT5640_DA_MONO_R_FILTER) { + asrc2_mask |= RT5640_MDA_R_M_MASK; + asrc2_value = (asrc2_value & ~RT5640_MDA_R_M_MASK) + | (clk_src << RT5640_MDA_R_M_SFT); + } + + if (filter_mask & RT5640_AD_STEREO_FILTER) { + asrc2_mask |= RT5640_ADC_M_MASK; + asrc2_value = (asrc2_value & ~RT5640_ADC_M_MASK) + | (clk_src << RT5640_ADC_M_SFT); + } + + if (filter_mask & RT5640_AD_MONO_L_FILTER) { + asrc2_mask |= RT5640_MAD_L_M_MASK; + asrc2_value = (asrc2_value & ~RT5640_MAD_L_M_MASK) + | (clk_src << RT5640_MAD_L_M_SFT); + } + + if (filter_mask & RT5640_AD_MONO_R_FILTER) { + asrc2_mask |= RT5640_MAD_R_M_MASK; + asrc2_value = (asrc2_value & ~RT5640_MAD_R_M_MASK) + | (clk_src << RT5640_MAD_R_M_SFT); + } + + snd_soc_component_update_bits(component, RT5640_ASRC_2, + asrc2_mask, asrc2_value); + + if (snd_soc_component_read(component, RT5640_ASRC_2)) { + rt5640->asrc_en = true; + snd_soc_component_update_bits(component, RT5640_JD_CTRL, 0x3, 0x3); + } else { + rt5640->asrc_en = false; + snd_soc_component_update_bits(component, RT5640_JD_CTRL, 0x3, 0x0); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5640_sel_asrc_clk_src); + +void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO2"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS1"); + /* OVCD is unreliable when used with RCCLK as sysclk-source */ + if (rt5640->use_platform_clock) + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Platform Clock"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} +EXPORT_SYMBOL_GPL(rt5640_enable_micbias1_for_ovcd); + +void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + snd_soc_dapm_mutex_lock(dapm); + if (rt5640->use_platform_clock) + snd_soc_dapm_disable_pin_unlocked(dapm, "Platform Clock"); + snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS1"); + snd_soc_dapm_disable_pin_unlocked(dapm, "LDO2"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} +EXPORT_SYMBOL_GPL(rt5640_disable_micbias1_for_ovcd); + +static void rt5640_enable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_IRQ_MB1_OC_MASK, RT5640_IRQ_MB1_OC_NOR); + rt5640->ovcd_irq_enabled = true; +} + +static void rt5640_disable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_IRQ_MB1_OC_MASK, RT5640_IRQ_MB1_OC_BP); + rt5640->ovcd_irq_enabled = false; +} + +static void rt5640_clear_micbias1_ovcd(struct snd_soc_component *component) +{ + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_MB1_OC_STATUS, 0); +} + +static bool rt5640_micbias1_ovcd(struct snd_soc_component *component) +{ + int val; + + val = snd_soc_component_read(component, RT5640_IRQ_CTRL2); + dev_dbg(component->dev, "irq ctrl2 %#04x\n", val); + + return (val & RT5640_MB1_OC_STATUS); +} + +static bool rt5640_jack_inserted(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + int val; + + if (rt5640->jd_gpio) + val = gpiod_get_value(rt5640->jd_gpio) ? RT5640_JD_STATUS : 0; + else + val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + + dev_dbg(component->dev, "irq status %#04x\n", val); + + if (rt5640->jd_inverted) + return !(val & RT5640_JD_STATUS); + else + return (val & RT5640_JD_STATUS); +} + +/* Jack detect and button-press timings */ +#define JACK_SETTLE_TIME 100 /* milli seconds */ +#define JACK_DETECT_COUNT 5 +#define JACK_DETECT_MAXCOUNT 20 /* Aprox. 2 seconds worth of tries */ +#define JACK_UNPLUG_TIME 80 /* milli seconds */ +#define BP_POLL_TIME 10 /* milli seconds */ +#define BP_POLL_MAXCOUNT 200 /* assume something is wrong after this */ +#define BP_THRESHOLD 3 + +static void rt5640_start_button_press_work(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + rt5640->poll_count = 0; + rt5640->press_count = 0; + rt5640->release_count = 0; + rt5640->pressed = false; + rt5640->press_reported = false; + rt5640_clear_micbias1_ovcd(component); + schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} + +static void rt5640_button_press_work(struct work_struct *work) +{ + struct rt5640_priv *rt5640 = + container_of(work, struct rt5640_priv, bp_work.work); + struct snd_soc_component *component = rt5640->component; + + /* Check the jack was not removed underneath us */ + if (!rt5640_jack_inserted(component)) + return; + + if (rt5640_micbias1_ovcd(component)) { + rt5640->release_count = 0; + rt5640->press_count++; + /* Remember till after JACK_UNPLUG_TIME wait */ + if (rt5640->press_count >= BP_THRESHOLD) + rt5640->pressed = true; + rt5640_clear_micbias1_ovcd(component); + } else { + rt5640->press_count = 0; + rt5640->release_count++; + } + + /* + * The pins get temporarily shorted on jack unplug, so we poll for + * at least JACK_UNPLUG_TIME milli-seconds before reporting a press. + */ + rt5640->poll_count++; + if (rt5640->poll_count < (JACK_UNPLUG_TIME / BP_POLL_TIME)) { + schedule_delayed_work(&rt5640->bp_work, + msecs_to_jiffies(BP_POLL_TIME)); + return; + } + + if (rt5640->pressed && !rt5640->press_reported) { + dev_dbg(component->dev, "headset button press\n"); + snd_soc_jack_report(rt5640->jack, SND_JACK_BTN_0, + SND_JACK_BTN_0); + rt5640->press_reported = true; + } + + if (rt5640->release_count >= BP_THRESHOLD) { + if (rt5640->press_reported) { + dev_dbg(component->dev, "headset button release\n"); + snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); + } + /* Re-enable OVCD IRQ to detect next press */ + rt5640_enable_micbias1_ovcd_irq(component); + return; /* Stop polling */ + } + + schedule_delayed_work(&rt5640->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} + +int rt5640_detect_headset(struct snd_soc_component *component, struct gpio_desc *hp_det_gpio) +{ + int i, headset_count = 0, headphone_count = 0; + + /* + * We get the insertion event before the jack is fully inserted at which + * point the second ring on a TRRS connector may short the 2nd ring and + * sleeve contacts, also the overcurrent detection is not entirely + * reliable. So we try several times with a wait in between until we + * detect the same type JACK_DETECT_COUNT times in a row. + */ + for (i = 0; i < JACK_DETECT_MAXCOUNT; i++) { + /* Clear any previous over-current status flag */ + rt5640_clear_micbias1_ovcd(component); + + msleep(JACK_SETTLE_TIME); + + /* Check the jack is still connected before checking ovcd */ + if (hp_det_gpio) { + if (gpiod_get_value_cansleep(hp_det_gpio)) + return 0; + } else { + if (!rt5640_jack_inserted(component)) + return 0; + } + + if (rt5640_micbias1_ovcd(component)) { + /* + * Over current detected, there is a short between the + * 2nd ring contact and the ground, so a TRS connector + * without a mic contact and thus plain headphones. + */ + dev_dbg(component->dev, "jack mic-gnd shorted\n"); + headset_count = 0; + headphone_count++; + if (headphone_count == JACK_DETECT_COUNT) + return SND_JACK_HEADPHONE; + } else { + dev_dbg(component->dev, "jack mic-gnd open\n"); + headphone_count = 0; + headset_count++; + if (headset_count == JACK_DETECT_COUNT) + return SND_JACK_HEADSET; + } + } + + dev_err(component->dev, "Error detecting headset vs headphones, bad contact?, assuming headphones\n"); + return SND_JACK_HEADPHONE; +} +EXPORT_SYMBOL_GPL(rt5640_detect_headset); + +static void rt5640_jack_work(struct work_struct *work) +{ + struct rt5640_priv *rt5640 = + container_of(work, struct rt5640_priv, jack_work.work); + struct snd_soc_component *component = rt5640->component; + int status; + + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) { + int val, jack_type = 0, hda_mic_plugged, hda_hp_plugged; + + /* mic jack */ + val = snd_soc_component_read(component, RT5640_INT_IRQ_ST); + hda_mic_plugged = !(val & RT5640_JD_STATUS); + dev_dbg(component->dev, "mic jack status %d\n", + hda_mic_plugged); + + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL1, + RT5640_JD_P_MASK, !hda_mic_plugged << RT5640_JD_P_SFT); + + if (hda_mic_plugged) + jack_type |= SND_JACK_MICROPHONE; + + /* headphone jack */ + val = snd_soc_component_read(component, RT5640_DUMMY2); + hda_hp_plugged = !(val & (0x1 << 11)); + dev_dbg(component->dev, "headphone jack status %d\n", + hda_hp_plugged); + + snd_soc_component_update_bits(component, RT5640_DUMMY2, + (0x1 << 10), !hda_hp_plugged << 10); + + if (hda_hp_plugged) + jack_type |= SND_JACK_HEADPHONE; + + snd_soc_jack_report(rt5640->jack, jack_type, SND_JACK_HEADSET); + + return; + } + + if (!rt5640_jack_inserted(component)) { + /* Jack removed, or spurious IRQ? */ + if (rt5640->jack->status & SND_JACK_HEADPHONE) { + if (rt5640->jack->status & SND_JACK_MICROPHONE) { + cancel_delayed_work_sync(&rt5640->bp_work); + rt5640_disable_micbias1_ovcd_irq(component); + rt5640_disable_micbias1_for_ovcd(component); + } + snd_soc_jack_report(rt5640->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + dev_dbg(component->dev, "jack unplugged\n"); + } + } else if (!(rt5640->jack->status & SND_JACK_HEADPHONE)) { + /* Jack inserted */ + WARN_ON(rt5640->ovcd_irq_enabled); + rt5640_enable_micbias1_for_ovcd(component); + status = rt5640_detect_headset(component, NULL); + if (status == SND_JACK_HEADSET) { + /* Enable ovcd IRQ for button press detect. */ + rt5640_enable_micbias1_ovcd_irq(component); + } else { + /* No more need for overcurrent detect. */ + rt5640_disable_micbias1_for_ovcd(component); + } + dev_dbg(component->dev, "detect status %#02x\n", status); + snd_soc_jack_report(rt5640->jack, status, SND_JACK_HEADSET); + } else if (rt5640->ovcd_irq_enabled && rt5640_micbias1_ovcd(component)) { + dev_dbg(component->dev, "OVCD IRQ\n"); + + /* + * The ovcd IRQ keeps firing while the button is pressed, so + * we disable it and start polling the button until released. + * + * The disable will make the IRQ pin 0 again and since we get + * IRQs on both edges (so as to detect both jack plugin and + * unplug) this means we will immediately get another IRQ. + * The ovcd_irq_enabled check above makes the 2ND IRQ a NOP. + */ + rt5640_disable_micbias1_ovcd_irq(component); + rt5640_start_button_press_work(component); + + /* + * If the jack-detect IRQ flag goes high (unplug) after our + * above rt5640_jack_inserted() check and before we have + * disabled the OVCD IRQ, the IRQ pin will stay high and as + * we react to edges, we miss the unplug event -> recheck. + */ + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + } +} + +static irqreturn_t rt5640_irq(int irq, void *data) +{ + struct rt5640_priv *rt5640 = data; + int delay = 0; + + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) + delay = 100; + + if (rt5640->jack) + mod_delayed_work(system_long_wq, &rt5640->jack_work, delay); + + return IRQ_HANDLED; +} + +static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data) +{ + struct rt5640_priv *rt5640 = data; + + queue_delayed_work(system_long_wq, &rt5640->jack_work, + msecs_to_jiffies(JACK_SETTLE_TIME)); + + return IRQ_HANDLED; +} + +static void rt5640_cancel_work(void *data) +{ + struct rt5640_priv *rt5640 = data; + + cancel_delayed_work_sync(&rt5640->jack_work); + cancel_delayed_work_sync(&rt5640->bp_work); +} + +void rt5640_set_ovcd_params(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + snd_soc_component_write(component, RT5640_PR_BASE + RT5640_BIAS_CUR4, + 0xa800 | rt5640->ovcd_sf); + + snd_soc_component_update_bits(component, RT5640_MICBIAS, + RT5640_MIC1_OVTH_MASK | RT5640_MIC1_OVCD_MASK, + rt5640->ovcd_th | RT5640_MIC1_OVCD_EN); + + /* + * The over-current-detect is only reliable in detecting the absence + * of over-current, when the mic-contact in the jack is short-circuited, + * the hardware periodically retries if it can apply the bias-current + * leading to the ovcd status flip-flopping 1-0-1 with it being 0 about + * 10% of the time, as we poll the ovcd status bit we might hit that + * 10%, so we enable sticky mode and when checking OVCD we clear the + * status, msleep() a bit and then check to get a reliable reading. + */ + snd_soc_component_update_bits(component, RT5640_IRQ_CTRL2, + RT5640_MB1_OC_STKY_MASK, RT5640_MB1_OC_STKY_EN); +} +EXPORT_SYMBOL_GPL(rt5640_set_ovcd_params); + +static void rt5640_disable_jack_detect(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + /* + * soc_remove_component() force-disables jack and thus rt5640->jack + * could be NULL at the time of driver's module unloading. + */ + if (!rt5640->jack) + return; + + if (rt5640->jd_gpio_irq_requested) + free_irq(rt5640->jd_gpio_irq, rt5640); + + if (rt5640->irq_requested) + free_irq(rt5640->irq, rt5640); + + rt5640_cancel_work(rt5640); + + if (rt5640->jack->status & SND_JACK_MICROPHONE) { + rt5640_disable_micbias1_ovcd_irq(component); + rt5640_disable_micbias1_for_ovcd(component); + snd_soc_jack_report(rt5640->jack, 0, SND_JACK_BTN_0); + } + + rt5640->jd_gpio_irq_requested = false; + rt5640->irq_requested = false; + rt5640->jd_gpio = NULL; + rt5640->jack = NULL; +} + +static void rt5640_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, + struct rt5640_set_jack_data *jack_data) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + int ret; + + /* Select JD-source */ + snd_soc_component_update_bits(component, RT5640_JD_CTRL, + RT5640_JD_MASK, rt5640->jd_src << RT5640_JD_SFT); + + /* Selecting GPIO01 as an interrupt */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1, + RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ); + + /* Set GPIO1 output */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, + RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); + + snd_soc_component_write(component, RT5640_DUMMY1, 0x3f41); + + rt5640_set_ovcd_params(component); + + /* + * All IRQs get or-ed together, so we need the jack IRQ to report 0 + * when a jack is inserted so that the OVCD IRQ then toggles the IRQ + * pin 0/1 instead of it being stuck to 1. So we invert the JD polarity + * on systems where the hardware does not already do this. + */ + if (rt5640->jd_inverted) { + if (rt5640->jd_src == RT5640_JD_SRC_JD1_IN4P) + snd_soc_component_write(component, RT5640_IRQ_CTRL1, + RT5640_IRQ_JD_NOR); + else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) + snd_soc_component_update_bits(component, RT5640_DUMMY2, + RT5640_IRQ_JD2_MASK | RT5640_JD2_MASK, + RT5640_IRQ_JD2_NOR | RT5640_JD2_EN); + } else { + if (rt5640->jd_src == RT5640_JD_SRC_JD1_IN4P) + snd_soc_component_write(component, RT5640_IRQ_CTRL1, + RT5640_IRQ_JD_NOR | RT5640_JD_P_INV); + else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) + snd_soc_component_update_bits(component, RT5640_DUMMY2, + RT5640_IRQ_JD2_MASK | RT5640_JD2_P_MASK | + RT5640_JD2_MASK, + RT5640_IRQ_JD2_NOR | RT5640_JD2_P_INV | + RT5640_JD2_EN); + } + + rt5640->jack = jack; + if (rt5640->jack->status & SND_JACK_MICROPHONE) { + rt5640_enable_micbias1_for_ovcd(component); + rt5640_enable_micbias1_ovcd_irq(component); + } + + if (jack_data && jack_data->codec_irq_override) + rt5640->irq = jack_data->codec_irq_override; + + if (jack_data && jack_data->jd_gpio) { + rt5640->jd_gpio = jack_data->jd_gpio; + rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio); + + ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "rt5640-jd-gpio", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n", + rt5640->jd_gpio_irq, ret); + rt5640_disable_jack_detect(component); + return; + } + rt5640->jd_gpio_irq_requested = true; + } + + if (jack_data && jack_data->use_platform_clock) + rt5640->use_platform_clock = jack_data->use_platform_clock; + + ret = request_irq(rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "rt5640", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); + rt5640_disable_jack_detect(component); + return; + } + rt5640->irq_requested = true; + + /* sync initial jack state */ + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); +} + +static const struct snd_soc_dapm_route rt5640_hda_jack_dapm_routes[] = { + {"IN1P", NULL, "MICBIAS1"}, + {"IN2P", NULL, "MICBIAS1"}, + {"IN3P", NULL, "MICBIAS1"}, +}; + +static void rt5640_enable_hda_jack_detect( + struct snd_soc_component *component, struct snd_soc_jack *jack) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + int ret; + + /* Select JD1 for Mic */ + snd_soc_component_update_bits(component, RT5640_JD_CTRL, + RT5640_JD_MASK, RT5640_JD_JD1_IN4P); + snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR); + + /* Select JD2 for Headphone */ + snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100); + + /* Selecting GPIO01 as an interrupt */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1, + RT5640_GP1_PIN_MASK, RT5640_GP1_PIN_IRQ); + + /* Set GPIO1 output */ + snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3, + RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT); + + snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0); + + snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, + RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG, + RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG); + usleep_range(10000, 15000); + snd_soc_component_update_bits(component, RT5640_PWR_ANLG1, + RT5640_PWR_FV2, RT5640_PWR_FV2); + + rt5640->jack = jack; + + ret = request_irq(rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640); + if (ret) { + dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); + rt5640->jack = NULL; + return; + } + rt5640->irq_requested = true; + + /* sync initial jack state */ + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + + snd_soc_dapm_add_routes(dapm, rt5640_hda_jack_dapm_routes, + ARRAY_SIZE(rt5640_hda_jack_dapm_routes)); +} + +static int rt5640_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + if (jack) { + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) + rt5640_enable_hda_jack_detect(component, jack); + else + rt5640_enable_jack_detect(component, jack, data); + } else { + rt5640_disable_jack_detect(component); + } + + return 0; +} + +static int rt5640_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + u32 dmic1_data_pin = 0; + u32 dmic2_data_pin = 0; + bool dmic_en = false; + u32 val; + + /* Check if MCLK provided */ + rt5640->mclk = devm_clk_get(component->dev, "mclk"); + if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + rt5640->component = component; + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x0301, 0x0301); + snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030); + snd_soc_component_update_bits(component, RT5640_DSP_PATH2, 0xfc00, 0x0c00); + + switch (snd_soc_component_read(component, RT5640_RESET) & RT5640_ID_MASK) { + case RT5640_ID_5640: + case RT5640_ID_5642: + snd_soc_add_component_controls(component, + rt5640_specific_snd_controls, + ARRAY_SIZE(rt5640_specific_snd_controls)); + snd_soc_dapm_new_controls(dapm, + rt5640_specific_dapm_widgets, + ARRAY_SIZE(rt5640_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt5640_specific_dapm_routes, + ARRAY_SIZE(rt5640_specific_dapm_routes)); + break; + case RT5640_ID_5639: + snd_soc_dapm_new_controls(dapm, + rt5639_specific_dapm_widgets, + ARRAY_SIZE(rt5639_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt5639_specific_dapm_routes, + ARRAY_SIZE(rt5639_specific_dapm_routes)); + break; + default: + dev_err(component->dev, + "The driver is for RT5639 RT5640 or RT5642 only\n"); + return -ENODEV; + } + + /* + * Note on some platforms the platform code may need to add device-props + * rather then relying only on properties set by the firmware. + * Therefor the property parsing MUST be done here, rather then from + * rt5640_i2c_probe(), so that the platform-code can attach extra + * properties before calling snd_soc_register_card(). + */ + if (device_property_read_bool(component->dev, "realtek,in1-differential")) + snd_soc_component_update_bits(component, RT5640_IN1_IN2, + RT5640_IN_DF1, RT5640_IN_DF1); + + if (device_property_read_bool(component->dev, "realtek,in2-differential")) + snd_soc_component_update_bits(component, RT5640_IN3_IN4, + RT5640_IN_DF2, RT5640_IN_DF2); + + if (device_property_read_bool(component->dev, "realtek,in3-differential")) + snd_soc_component_update_bits(component, RT5640_IN1_IN2, + RT5640_IN_DF2, RT5640_IN_DF2); + + if (device_property_read_bool(component->dev, "realtek,lout-differential")) + snd_soc_component_update_bits(component, RT5640_DUMMY1, + RT5640_EN_LOUT_DF, RT5640_EN_LOUT_DF); + + if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin", + &val) == 0 && val) { + dmic1_data_pin = val - 1; + dmic_en = true; + } + + if (device_property_read_u32(component->dev, "realtek,dmic2-data-pin", + &val) == 0 && val) { + dmic2_data_pin = val - 1; + dmic_en = true; + } + + if (dmic_en) + rt5640_dmic_enable(component, dmic1_data_pin, dmic2_data_pin); + + if (device_property_read_u32(component->dev, + "realtek,jack-detect-source", &val) == 0) { + if (val <= RT5640_JD_SRC_HDA_HEADER) + rt5640->jd_src = val; + else + dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n", + val); + } + + if (!device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted")) + rt5640->jd_inverted = true; + + /* + * Testing on various boards has shown that good defaults for the OVCD + * threshold and scale-factor are 2000µA and 0.75. For an effective + * limit of 1500µA, this seems to be more reliable then 1500µA and 1.0. + */ + rt5640->ovcd_th = RT5640_MIC1_OVTH_2000UA; + rt5640->ovcd_sf = RT5640_MIC_OVCD_SF_0P75; + + if (device_property_read_u32(component->dev, + "realtek,over-current-threshold-microamp", &val) == 0) { + switch (val) { + case 600: + rt5640->ovcd_th = RT5640_MIC1_OVTH_600UA; + break; + case 1500: + rt5640->ovcd_th = RT5640_MIC1_OVTH_1500UA; + break; + case 2000: + rt5640->ovcd_th = RT5640_MIC1_OVTH_2000UA; + break; + default: + dev_warn(component->dev, "Warning: Invalid over-current-threshold-microamp value: %d, defaulting to 2000uA\n", + val); + } + } + + if (device_property_read_u32(component->dev, + "realtek,over-current-scale-factor", &val) == 0) { + if (val <= RT5640_OVCD_SF_1P5) + rt5640->ovcd_sf = val << RT5640_MIC_OVCD_SF_SFT; + else + dev_warn(component->dev, "Warning: Invalid over-current-scale-factor value: %d, defaulting to 0.75\n", + val); + } + + return 0; +} + +static void rt5640_remove(struct snd_soc_component *component) +{ + rt5640_reset(component); +} + +#ifdef CONFIG_PM +static int rt5640_suspend(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + if (rt5640->jack) { + /* disable jack interrupts during system suspend */ + disable_irq(rt5640->irq); + rt5640_cancel_work(rt5640); + } + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + rt5640_reset(component); + regcache_cache_only(rt5640->regmap, true); + regcache_mark_dirty(rt5640->regmap); + if (rt5640->ldo1_en) + gpiod_set_value_cansleep(rt5640->ldo1_en, 0); + + return 0; +} + +static int rt5640_resume(struct snd_soc_component *component) +{ + struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); + + if (rt5640->ldo1_en) { + gpiod_set_value_cansleep(rt5640->ldo1_en, 1); + msleep(400); + } + + regcache_cache_only(rt5640->regmap, false); + regcache_sync(rt5640->regmap); + + if (rt5640->jack) { + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) { + snd_soc_component_update_bits(component, + RT5640_DUMMY2, 0x1100, 0x1100); + } else { + if (rt5640->jd_inverted) { + if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) + snd_soc_component_update_bits( + component, RT5640_DUMMY2, + RT5640_IRQ_JD2_MASK | + RT5640_JD2_MASK, + RT5640_IRQ_JD2_NOR | + RT5640_JD2_EN); + + } else { + if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N) + snd_soc_component_update_bits( + component, RT5640_DUMMY2, + RT5640_IRQ_JD2_MASK | + RT5640_JD2_P_MASK | + RT5640_JD2_MASK, + RT5640_IRQ_JD2_NOR | + RT5640_JD2_P_INV | + RT5640_JD2_EN); + } + } + + enable_irq(rt5640->irq); + queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + } + + return 0; +} +#else +#define rt5640_suspend NULL +#define rt5640_resume NULL +#endif + +#define RT5640_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5640_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5640_aif_dai_ops = { + .hw_params = rt5640_hw_params, + .set_fmt = rt5640_set_dai_fmt, + .set_sysclk = rt5640_set_dai_sysclk, + .set_pll = rt5640_set_dai_pll, +}; + +static struct snd_soc_dai_driver rt5640_dai[] = { + { + .name = "rt5640-aif1", + .id = RT5640_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5640_STEREO_RATES, + .formats = RT5640_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5640_STEREO_RATES, + .formats = RT5640_FORMATS, + }, + .ops = &rt5640_aif_dai_ops, + }, + { + .name = "rt5640-aif2", + .id = RT5640_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5640_STEREO_RATES, + .formats = RT5640_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5640_STEREO_RATES, + .formats = RT5640_FORMATS, + }, + .ops = &rt5640_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5640 = { + .probe = rt5640_probe, + .remove = rt5640_remove, + .suspend = rt5640_suspend, + .resume = rt5640_resume, + .set_bias_level = rt5640_set_bias_level, + .set_jack = rt5640_set_jack, + .controls = rt5640_snd_controls, + .num_controls = ARRAY_SIZE(rt5640_snd_controls), + .dapm_widgets = rt5640_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets), + .dapm_routes = rt5640_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5640_regmap = { + .reg_bits = 8, + .val_bits = 16, + .use_single_read = true, + .use_single_write = true, + + .max_register = RT5640_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5640_ranges) * + RT5640_PR_SPACING), + .volatile_reg = rt5640_volatile_register, + .readable_reg = rt5640_readable_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5640_reg, + .num_reg_defaults = ARRAY_SIZE(rt5640_reg), + .ranges = rt5640_ranges, + .num_ranges = ARRAY_SIZE(rt5640_ranges), +}; + +static const struct i2c_device_id rt5640_i2c_id[] = { + { "rt5640", 0 }, + { "rt5639", 0 }, + { "rt5642", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt5640_of_match[] = { + { .compatible = "realtek,rt5639", }, + { .compatible = "realtek,rt5640", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5640_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5640_acpi_match[] = { + { "INT33CA", 0 }, + { "10EC3276", 0 }, + { "10EC5640", 0 }, + { "10EC5642", 0 }, + { "INTCCFFD", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match); +#endif + +static int rt5640_i2c_probe(struct i2c_client *i2c) +{ + struct rt5640_priv *rt5640; + int ret; + unsigned int val; + + rt5640 = devm_kzalloc(&i2c->dev, + sizeof(struct rt5640_priv), + GFP_KERNEL); + if (NULL == rt5640) + return -ENOMEM; + i2c_set_clientdata(i2c, rt5640); + + rt5640->ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5640->ldo1_en)) + return PTR_ERR(rt5640->ldo1_en); + + if (rt5640->ldo1_en) { + gpiod_set_consumer_name(rt5640->ldo1_en, "RT5640 LDO1_EN"); + msleep(400); + } + + rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap); + if (IS_ERR(rt5640->regmap)) { + ret = PTR_ERR(rt5640->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val); + if (val != RT5640_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5640/39\n", val); + return -ENODEV; + } + + regmap_write(rt5640->regmap, RT5640_RESET, 0); + + ret = regmap_register_patch(rt5640->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + regmap_update_bits(rt5640->regmap, RT5640_DUMMY1, + RT5640_MCLK_DET, RT5640_MCLK_DET); + + rt5640->hp_mute = true; + rt5640->irq = i2c->irq; + INIT_DELAYED_WORK(&rt5640->bp_work, rt5640_button_press_work); + INIT_DELAYED_WORK(&rt5640->jack_work, rt5640_jack_work); + + /* Make sure work is stopped on probe-error / remove */ + ret = devm_add_action_or_reset(&i2c->dev, rt5640_cancel_work, rt5640); + if (ret) + return ret; + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5640, + rt5640_dai, ARRAY_SIZE(rt5640_dai)); +} + +static struct i2c_driver rt5640_i2c_driver = { + .driver = { + .name = "rt5640", + .acpi_match_table = ACPI_PTR(rt5640_acpi_match), + .of_match_table = of_match_ptr(rt5640_of_match), + }, + .probe = rt5640_i2c_probe, + .id_table = rt5640_i2c_id, +}; +module_i2c_driver(rt5640_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5640/RT5639 driver"); +MODULE_AUTHOR("Johnny Hsu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h new file mode 100644 index 0000000000..94b9a502f7 --- /dev/null +++ b/sound/soc/codecs/rt5640.h @@ -0,0 +1,2193 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5640.h -- RT5640 ALSA SoC audio driver + * + * Copyright 2011 Realtek Microelectronics + * Author: Johnny Hsu + */ + +#ifndef _RT5640_H +#define _RT5640_H + +#include +#include +#include +#include + +/* Info */ +#define RT5640_RESET 0x00 +#define RT5640_VENDOR_ID 0xfd +#define RT5640_VENDOR_ID1 0xfe +#define RT5640_VENDOR_ID2 0xff +/* I/O - Output */ +#define RT5640_SPK_VOL 0x01 +#define RT5640_HP_VOL 0x02 +#define RT5640_OUTPUT 0x03 +#define RT5640_MONO_OUT 0x04 +/* I/O - Input */ +#define RT5640_IN1_IN2 0x0d +#define RT5640_IN3_IN4 0x0e +#define RT5640_INL_INR_VOL 0x0f +/* I/O - ADC/DAC/DMIC */ +#define RT5640_DAC1_DIG_VOL 0x19 +#define RT5640_DAC2_DIG_VOL 0x1a +#define RT5640_DAC2_CTRL 0x1b +#define RT5640_ADC_DIG_VOL 0x1c +#define RT5640_ADC_DATA 0x1d +#define RT5640_ADC_BST_VOL 0x1e +/* Mixer - D-D */ +#define RT5640_STO_ADC_MIXER 0x27 +#define RT5640_MONO_ADC_MIXER 0x28 +#define RT5640_AD_DA_MIXER 0x29 +#define RT5640_STO_DAC_MIXER 0x2a +#define RT5640_MONO_DAC_MIXER 0x2b +#define RT5640_DIG_MIXER 0x2c +#define RT5640_DSP_PATH1 0x2d +#define RT5640_DSP_PATH2 0x2e +#define RT5640_DIG_INF_DATA 0x2f +/* Mixer - ADC */ +#define RT5640_REC_L1_MIXER 0x3b +#define RT5640_REC_L2_MIXER 0x3c +#define RT5640_REC_R1_MIXER 0x3d +#define RT5640_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5640_HPO_MIXER 0x45 +#define RT5640_SPK_L_MIXER 0x46 +#define RT5640_SPK_R_MIXER 0x47 +#define RT5640_SPO_L_MIXER 0x48 +#define RT5640_SPO_R_MIXER 0x49 +#define RT5640_SPO_CLSD_RATIO 0x4a +#define RT5640_MONO_MIXER 0x4c +#define RT5640_OUT_L1_MIXER 0x4d +#define RT5640_OUT_L2_MIXER 0x4e +#define RT5640_OUT_L3_MIXER 0x4f +#define RT5640_OUT_R1_MIXER 0x50 +#define RT5640_OUT_R2_MIXER 0x51 +#define RT5640_OUT_R3_MIXER 0x52 +#define RT5640_LOUT_MIXER 0x53 +/* Power */ +#define RT5640_PWR_DIG1 0x61 +#define RT5640_PWR_DIG2 0x62 +#define RT5640_PWR_ANLG1 0x63 +#define RT5640_PWR_ANLG2 0x64 +#define RT5640_PWR_MIXER 0x65 +#define RT5640_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5640_PRIV_INDEX 0x6a +#define RT5640_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5640_I2S1_SDP 0x70 +#define RT5640_I2S2_SDP 0x71 +#define RT5640_ADDA_CLK1 0x73 +#define RT5640_ADDA_CLK2 0x74 +#define RT5640_DMIC 0x75 +/* Function - Analog */ +#define RT5640_GLB_CLK 0x80 +#define RT5640_PLL_CTRL1 0x81 +#define RT5640_PLL_CTRL2 0x82 +#define RT5640_ASRC_1 0x83 +#define RT5640_ASRC_2 0x84 +#define RT5640_ASRC_3 0x85 +#define RT5640_ASRC_4 0x89 +#define RT5640_ASRC_5 0x8a +#define RT5640_HP_OVCD 0x8b +#define RT5640_CLS_D_OVCD 0x8c +#define RT5640_CLS_D_OUT 0x8d +#define RT5640_DEPOP_M1 0x8e +#define RT5640_DEPOP_M2 0x8f +#define RT5640_DEPOP_M3 0x90 +#define RT5640_CHARGE_PUMP 0x91 +#define RT5640_PV_DET_SPK_G 0x92 +#define RT5640_MICBIAS 0x93 +/* Function - Digital */ +#define RT5640_EQ_CTRL1 0xb0 +#define RT5640_EQ_CTRL2 0xb1 +#define RT5640_WIND_FILTER 0xb2 +#define RT5640_DRC_AGC_1 0xb4 +#define RT5640_DRC_AGC_2 0xb5 +#define RT5640_DRC_AGC_3 0xb6 +#define RT5640_SVOL_ZC 0xb7 +#define RT5640_ANC_CTRL1 0xb8 +#define RT5640_ANC_CTRL2 0xb9 +#define RT5640_ANC_CTRL3 0xba +#define RT5640_JD_CTRL 0xbb +#define RT5640_ANC_JD 0xbc +#define RT5640_IRQ_CTRL1 0xbd +#define RT5640_IRQ_CTRL2 0xbe +#define RT5640_INT_IRQ_ST 0xbf +#define RT5640_GPIO_CTRL1 0xc0 +#define RT5640_GPIO_CTRL2 0xc1 +#define RT5640_GPIO_CTRL3 0xc2 +#define RT5640_DSP_CTRL1 0xc4 +#define RT5640_DSP_CTRL2 0xc5 +#define RT5640_DSP_CTRL3 0xc6 +#define RT5640_DSP_CTRL4 0xc7 +#define RT5640_PGM_REG_ARR1 0xc8 +#define RT5640_PGM_REG_ARR2 0xc9 +#define RT5640_PGM_REG_ARR3 0xca +#define RT5640_PGM_REG_ARR4 0xcb +#define RT5640_PGM_REG_ARR5 0xcc +#define RT5640_SCB_FUNC 0xcd +#define RT5640_SCB_CTRL 0xce +#define RT5640_BASE_BACK 0xcf +#define RT5640_MP3_PLUS1 0xd0 +#define RT5640_MP3_PLUS2 0xd1 +#define RT5640_3D_HP 0xd2 +#define RT5640_ADJ_HPF 0xd3 +#define RT5640_HP_CALIB_AMP_DET 0xd6 +#define RT5640_HP_CALIB2 0xd7 +#define RT5640_SV_ZCD1 0xd9 +#define RT5640_SV_ZCD2 0xda +/* Dummy Register */ +#define RT5640_DUMMY1 0xfa +#define RT5640_DUMMY2 0xfb +#define RT5640_DUMMY3 0xfc + + +/* Index of Codec Private Register definition */ +#define RT5640_BIAS_CUR4 0x15 +#define RT5640_CHPUMP_INT_REG1 0x24 +#define RT5640_MAMP_INT_REG2 0x37 +#define RT5640_3D_SPK 0x63 +#define RT5640_WND_1 0x6c +#define RT5640_WND_2 0x6d +#define RT5640_WND_3 0x6e +#define RT5640_WND_4 0x6f +#define RT5640_WND_5 0x70 +#define RT5640_WND_8 0x73 +#define RT5640_DIP_SPK_INF 0x75 +#define RT5640_HP_DCC_INT1 0x77 +#define RT5640_EQ_BW_LOP 0xa0 +#define RT5640_EQ_GN_LOP 0xa1 +#define RT5640_EQ_FC_BP1 0xa2 +#define RT5640_EQ_BW_BP1 0xa3 +#define RT5640_EQ_GN_BP1 0xa4 +#define RT5640_EQ_FC_BP2 0xa5 +#define RT5640_EQ_BW_BP2 0xa6 +#define RT5640_EQ_GN_BP2 0xa7 +#define RT5640_EQ_FC_BP3 0xa8 +#define RT5640_EQ_BW_BP3 0xa9 +#define RT5640_EQ_GN_BP3 0xaa +#define RT5640_EQ_FC_BP4 0xab +#define RT5640_EQ_BW_BP4 0xac +#define RT5640_EQ_GN_BP4 0xad +#define RT5640_EQ_FC_HIP1 0xae +#define RT5640_EQ_GN_HIP1 0xaf +#define RT5640_EQ_FC_HIP2 0xb0 +#define RT5640_EQ_BW_HIP2 0xb1 +#define RT5640_EQ_GN_HIP2 0xb2 +#define RT5640_EQ_PRE_VOL 0xb3 +#define RT5640_EQ_PST_VOL 0xb4 + +/* global definition */ +#define RT5640_L_MUTE (0x1 << 15) +#define RT5640_L_MUTE_SFT 15 +#define RT5640_VOL_L_MUTE (0x1 << 14) +#define RT5640_VOL_L_SFT 14 +#define RT5640_R_MUTE (0x1 << 7) +#define RT5640_R_MUTE_SFT 7 +#define RT5640_VOL_R_MUTE (0x1 << 6) +#define RT5640_VOL_R_SFT 6 +#define RT5640_L_VOL_MASK (0x3f << 8) +#define RT5640_L_VOL_SFT 8 +#define RT5640_R_VOL_MASK (0x3f) +#define RT5640_R_VOL_SFT 0 + +/* SW Reset & Device ID (0x00) */ +#define RT5640_ID_MASK (0x3 << 1) +#define RT5640_ID_5639 (0x0 << 1) +#define RT5640_ID_5640 (0x2 << 1) +#define RT5640_ID_5642 (0x3 << 1) + + +/* IN1 and IN2 Control (0x0d) */ +/* IN3 and IN4 Control (0x0e) */ +#define RT5640_BST_SFT1 12 +#define RT5640_BST_SFT2 8 +#define RT5640_IN_DF1 (0x1 << 7) +#define RT5640_IN_SFT1 7 +#define RT5640_IN_DF2 (0x1 << 6) +#define RT5640_IN_SFT2 6 + +/* INL and INR Volume Control (0x0f) */ +#define RT5640_INL_SEL_MASK (0x1 << 15) +#define RT5640_INL_SEL_SFT 15 +#define RT5640_INL_SEL_IN4P (0x0 << 15) +#define RT5640_INL_SEL_MONOP (0x1 << 15) +#define RT5640_INL_VOL_MASK (0x1f << 8) +#define RT5640_INL_VOL_SFT 8 +#define RT5640_INR_SEL_MASK (0x1 << 7) +#define RT5640_INR_SEL_SFT 7 +#define RT5640_INR_SEL_IN4N (0x0 << 7) +#define RT5640_INR_SEL_MONON (0x1 << 7) +#define RT5640_INR_VOL_MASK (0x1f) +#define RT5640_INR_VOL_SFT 0 + +/* DAC1 Digital Volume (0x19) */ +#define RT5640_DAC_L1_VOL_MASK (0xff << 8) +#define RT5640_DAC_L1_VOL_SFT 8 +#define RT5640_DAC_R1_VOL_MASK (0xff) +#define RT5640_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x1a) */ +#define RT5640_DAC_L2_VOL_MASK (0xff << 8) +#define RT5640_DAC_L2_VOL_SFT 8 +#define RT5640_DAC_R2_VOL_MASK (0xff) +#define RT5640_DAC_R2_VOL_SFT 0 + +/* DAC2 Control (0x1b) */ +#define RT5640_M_DAC_L2_VOL (0x1 << 13) +#define RT5640_M_DAC_L2_VOL_SFT 13 +#define RT5640_M_DAC_R2_VOL (0x1 << 12) +#define RT5640_M_DAC_R2_VOL_SFT 12 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5640_ADC_L_VOL_MASK (0x7f << 8) +#define RT5640_ADC_L_VOL_SFT 8 +#define RT5640_ADC_R_VOL_MASK (0x7f) +#define RT5640_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x1d) */ +#define RT5640_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5640_MONO_ADC_L_VOL_SFT 8 +#define RT5640_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5640_MONO_ADC_R_VOL_SFT 0 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5640_ADC_L_BST_MASK (0x3 << 14) +#define RT5640_ADC_L_BST_SFT 14 +#define RT5640_ADC_R_BST_MASK (0x3 << 12) +#define RT5640_ADC_R_BST_SFT 12 +#define RT5640_ADC_COMP_MASK (0x3 << 10) +#define RT5640_ADC_COMP_SFT 10 + +/* Stereo ADC Mixer Control (0x27) */ +#define RT5640_M_ADC_L1 (0x1 << 14) +#define RT5640_M_ADC_L1_SFT 14 +#define RT5640_M_ADC_L2 (0x1 << 13) +#define RT5640_M_ADC_L2_SFT 13 +#define RT5640_ADC_1_SRC_MASK (0x1 << 12) +#define RT5640_ADC_1_SRC_SFT 12 +#define RT5640_ADC_1_SRC_ADC (0x1 << 12) +#define RT5640_ADC_1_SRC_DACMIX (0x0 << 12) +#define RT5640_ADC_2_SRC_MASK (0x3 << 10) +#define RT5640_ADC_2_SRC_SFT 10 +#define RT5640_ADC_2_SRC_DMIC1 (0x0 << 10) +#define RT5640_ADC_2_SRC_DMIC2 (0x1 << 10) +#define RT5640_ADC_2_SRC_DACMIX (0x2 << 10) +#define RT5640_M_ADC_R1 (0x1 << 6) +#define RT5640_M_ADC_R1_SFT 6 +#define RT5640_M_ADC_R2 (0x1 << 5) +#define RT5640_M_ADC_R2_SFT 5 + +/* Mono ADC Mixer Control (0x28) */ +#define RT5640_M_MONO_ADC_L1 (0x1 << 14) +#define RT5640_M_MONO_ADC_L1_SFT 14 +#define RT5640_M_MONO_ADC_L2 (0x1 << 13) +#define RT5640_M_MONO_ADC_L2_SFT 13 +#define RT5640_MONO_ADC_L1_SRC_MASK (0x1 << 12) +#define RT5640_MONO_ADC_L1_SRC_SFT 12 +#define RT5640_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12) +#define RT5640_MONO_ADC_L1_SRC_ADCL (0x1 << 12) +#define RT5640_MONO_ADC_L2_SRC_MASK (0x3 << 10) +#define RT5640_MONO_ADC_L2_SRC_SFT 10 +#define RT5640_MONO_ADC_L2_SRC_DMIC_L1 (0x0 << 10) +#define RT5640_MONO_ADC_L2_SRC_DMIC_L2 (0x1 << 10) +#define RT5640_MONO_ADC_L2_SRC_DACMIXL (0x2 << 10) +#define RT5640_M_MONO_ADC_R1 (0x1 << 6) +#define RT5640_M_MONO_ADC_R1_SFT 6 +#define RT5640_M_MONO_ADC_R2 (0x1 << 5) +#define RT5640_M_MONO_ADC_R2_SFT 5 +#define RT5640_MONO_ADC_R1_SRC_MASK (0x1 << 4) +#define RT5640_MONO_ADC_R1_SRC_SFT 4 +#define RT5640_MONO_ADC_R1_SRC_ADCR (0x1 << 4) +#define RT5640_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4) +#define RT5640_MONO_ADC_R2_SRC_MASK (0x3 << 2) +#define RT5640_MONO_ADC_R2_SRC_SFT 2 +#define RT5640_MONO_ADC_R2_SRC_DMIC_R1 (0x0 << 2) +#define RT5640_MONO_ADC_R2_SRC_DMIC_R2 (0x1 << 2) +#define RT5640_MONO_ADC_R2_SRC_DACMIXR (0x2 << 2) + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5640_M_ADCMIX_L (0x1 << 15) +#define RT5640_M_ADCMIX_L_SFT 15 +#define RT5640_M_IF1_DAC_L (0x1 << 14) +#define RT5640_M_IF1_DAC_L_SFT 14 +#define RT5640_M_ADCMIX_R (0x1 << 7) +#define RT5640_M_ADCMIX_R_SFT 7 +#define RT5640_M_IF1_DAC_R (0x1 << 6) +#define RT5640_M_IF1_DAC_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5640_M_DAC_L1 (0x1 << 14) +#define RT5640_M_DAC_L1_SFT 14 +#define RT5640_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5640_DAC_L1_STO_L_VOL_SFT 13 +#define RT5640_M_DAC_L2 (0x1 << 12) +#define RT5640_M_DAC_L2_SFT 12 +#define RT5640_DAC_L2_STO_L_VOL_MASK (0x1 << 11) +#define RT5640_DAC_L2_STO_L_VOL_SFT 11 +#define RT5640_M_ANC_DAC_L (0x1 << 10) +#define RT5640_M_ANC_DAC_L_SFT 10 +#define RT5640_M_DAC_R1 (0x1 << 6) +#define RT5640_M_DAC_R1_SFT 6 +#define RT5640_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5640_DAC_R1_STO_R_VOL_SFT 5 +#define RT5640_M_DAC_R2 (0x1 << 4) +#define RT5640_M_DAC_R2_SFT 4 +#define RT5640_DAC_R2_STO_R_VOL_MASK (0x1 << 3) +#define RT5640_DAC_R2_STO_R_VOL_SFT 3 +#define RT5640_M_ANC_DAC_R (0x1 << 2) +#define RT5640_M_ANC_DAC_R_SFT 2 + +/* Mono DAC Mixer Control (0x2b) */ +#define RT5640_M_DAC_L1_MONO_L (0x1 << 14) +#define RT5640_M_DAC_L1_MONO_L_SFT 14 +#define RT5640_DAC_L1_MONO_L_VOL_MASK (0x1 << 13) +#define RT5640_DAC_L1_MONO_L_VOL_SFT 13 +#define RT5640_M_DAC_L2_MONO_L (0x1 << 12) +#define RT5640_M_DAC_L2_MONO_L_SFT 12 +#define RT5640_DAC_L2_MONO_L_VOL_MASK (0x1 << 11) +#define RT5640_DAC_L2_MONO_L_VOL_SFT 11 +#define RT5640_M_DAC_R2_MONO_L (0x1 << 10) +#define RT5640_M_DAC_R2_MONO_L_SFT 10 +#define RT5640_DAC_R2_MONO_L_VOL_MASK (0x1 << 9) +#define RT5640_DAC_R2_MONO_L_VOL_SFT 9 +#define RT5640_M_DAC_R1_MONO_R (0x1 << 6) +#define RT5640_M_DAC_R1_MONO_R_SFT 6 +#define RT5640_DAC_R1_MONO_R_VOL_MASK (0x1 << 5) +#define RT5640_DAC_R1_MONO_R_VOL_SFT 5 +#define RT5640_M_DAC_R2_MONO_R (0x1 << 4) +#define RT5640_M_DAC_R2_MONO_R_SFT 4 +#define RT5640_DAC_R2_MONO_R_VOL_MASK (0x1 << 3) +#define RT5640_DAC_R2_MONO_R_VOL_SFT 3 +#define RT5640_M_DAC_L2_MONO_R (0x1 << 2) +#define RT5640_M_DAC_L2_MONO_R_SFT 2 +#define RT5640_DAC_L2_MONO_R_VOL_MASK (0x1 << 1) +#define RT5640_DAC_L2_MONO_R_VOL_SFT 1 + +/* Digital Mixer Control (0x2c) */ +#define RT5640_M_STO_L_DAC_L (0x1 << 15) +#define RT5640_M_STO_L_DAC_L_SFT 15 +#define RT5640_STO_L_DAC_L_VOL_MASK (0x1 << 14) +#define RT5640_STO_L_DAC_L_VOL_SFT 14 +#define RT5640_M_DAC_L2_DAC_L (0x1 << 13) +#define RT5640_M_DAC_L2_DAC_L_SFT 13 +#define RT5640_DAC_L2_DAC_L_VOL_MASK (0x1 << 12) +#define RT5640_DAC_L2_DAC_L_VOL_SFT 12 +#define RT5640_M_STO_R_DAC_R (0x1 << 11) +#define RT5640_M_STO_R_DAC_R_SFT 11 +#define RT5640_STO_R_DAC_R_VOL_MASK (0x1 << 10) +#define RT5640_STO_R_DAC_R_VOL_SFT 10 +#define RT5640_M_DAC_R2_DAC_R (0x1 << 9) +#define RT5640_M_DAC_R2_DAC_R_SFT 9 +#define RT5640_DAC_R2_DAC_R_VOL_MASK (0x1 << 8) +#define RT5640_DAC_R2_DAC_R_VOL_SFT 8 + +/* DSP Path Control 1 (0x2d) */ +#define RT5640_RXDP_SRC_MASK (0x1 << 15) +#define RT5640_RXDP_SRC_SFT 15 +#define RT5640_RXDP_SRC_NOR (0x0 << 15) +#define RT5640_RXDP_SRC_DIV3 (0x1 << 15) +#define RT5640_TXDP_SRC_MASK (0x1 << 14) +#define RT5640_TXDP_SRC_SFT 14 +#define RT5640_TXDP_SRC_NOR (0x0 << 14) +#define RT5640_TXDP_SRC_DIV3 (0x1 << 14) + +/* DSP Path Control 2 (0x2e) */ +#define RT5640_DAC_L2_SEL_MASK (0x3 << 14) +#define RT5640_DAC_L2_SEL_SFT 14 +#define RT5640_DAC_L2_SEL_IF2 (0x0 << 14) +#define RT5640_DAC_L2_SEL_IF3 (0x1 << 14) +#define RT5640_DAC_L2_SEL_TXDC (0x2 << 14) +#define RT5640_DAC_L2_SEL_BASS (0x3 << 14) +#define RT5640_DAC_R2_SEL_MASK (0x3 << 12) +#define RT5640_DAC_R2_SEL_SFT 12 +#define RT5640_DAC_R2_SEL_IF2 (0x0 << 12) +#define RT5640_DAC_R2_SEL_IF3 (0x1 << 12) +#define RT5640_DAC_R2_SEL_TXDC (0x2 << 12) +#define RT5640_IF2_ADC_L_SEL_MASK (0x1 << 11) +#define RT5640_IF2_ADC_L_SEL_SFT 11 +#define RT5640_IF2_ADC_L_SEL_TXDP (0x0 << 11) +#define RT5640_IF2_ADC_L_SEL_PASS (0x1 << 11) +#define RT5640_IF2_ADC_R_SEL_MASK (0x1 << 10) +#define RT5640_IF2_ADC_R_SEL_SFT 10 +#define RT5640_IF2_ADC_R_SEL_TXDP (0x0 << 10) +#define RT5640_IF2_ADC_R_SEL_PASS (0x1 << 10) +#define RT5640_RXDC_SEL_MASK (0x3 << 8) +#define RT5640_RXDC_SEL_SFT 8 +#define RT5640_RXDC_SEL_NOR (0x0 << 8) +#define RT5640_RXDC_SEL_L2R (0x1 << 8) +#define RT5640_RXDC_SEL_R2L (0x2 << 8) +#define RT5640_RXDC_SEL_SWAP (0x3 << 8) +#define RT5640_RXDP_SEL_MASK (0x3 << 6) +#define RT5640_RXDP_SEL_SFT 6 +#define RT5640_RXDP_SEL_NOR (0x0 << 6) +#define RT5640_RXDP_SEL_L2R (0x1 << 6) +#define RT5640_RXDP_SEL_R2L (0x2 << 6) +#define RT5640_RXDP_SEL_SWAP (0x3 << 6) +#define RT5640_TXDC_SEL_MASK (0x3 << 4) +#define RT5640_TXDC_SEL_SFT 4 +#define RT5640_TXDC_SEL_NOR (0x0 << 4) +#define RT5640_TXDC_SEL_L2R (0x1 << 4) +#define RT5640_TXDC_SEL_R2L (0x2 << 4) +#define RT5640_TXDC_SEL_SWAP (0x3 << 4) +#define RT5640_TXDP_SEL_MASK (0x3 << 2) +#define RT5640_TXDP_SEL_SFT 2 +#define RT5640_TXDP_SEL_NOR (0x0 << 2) +#define RT5640_TXDP_SEL_L2R (0x1 << 2) +#define RT5640_TXDP_SEL_R2L (0x2 << 2) +#define RT5640_TRXDP_SEL_SWAP (0x3 << 2) + +/* Digital Interface Data Control (0x2f) */ +#define RT5640_IF1_DAC_SEL_MASK (0x3 << 14) +#define RT5640_IF1_DAC_SEL_SFT 14 +#define RT5640_IF1_DAC_SEL_NOR (0x0 << 14) +#define RT5640_IF1_DAC_SEL_SWAP (0x1 << 14) +#define RT5640_IF1_DAC_SEL_L2R (0x2 << 14) +#define RT5640_IF1_DAC_SEL_R2L (0x3 << 14) +#define RT5640_IF1_ADC_SEL_MASK (0x3 << 12) +#define RT5640_IF1_ADC_SEL_SFT 12 +#define RT5640_IF1_ADC_SEL_NOR (0x0 << 12) +#define RT5640_IF1_ADC_SEL_SWAP (0x1 << 12) +#define RT5640_IF1_ADC_SEL_L2R (0x2 << 12) +#define RT5640_IF1_ADC_SEL_R2L (0x3 << 12) +#define RT5640_IF2_DAC_SEL_MASK (0x3 << 10) +#define RT5640_IF2_DAC_SEL_SFT 10 +#define RT5640_IF2_DAC_SEL_NOR (0x0 << 10) +#define RT5640_IF2_DAC_SEL_SWAP (0x1 << 10) +#define RT5640_IF2_DAC_SEL_L2R (0x2 << 10) +#define RT5640_IF2_DAC_SEL_R2L (0x3 << 10) +#define RT5640_IF2_ADC_SEL_MASK (0x3 << 8) +#define RT5640_IF2_ADC_SEL_SFT 8 +#define RT5640_IF2_ADC_SEL_NOR (0x0 << 8) +#define RT5640_IF2_ADC_SEL_SWAP (0x1 << 8) +#define RT5640_IF2_ADC_SEL_L2R (0x2 << 8) +#define RT5640_IF2_ADC_SEL_R2L (0x3 << 8) +#define RT5640_IF3_DAC_SEL_MASK (0x3 << 6) +#define RT5640_IF3_DAC_SEL_SFT 6 +#define RT5640_IF3_DAC_SEL_NOR (0x0 << 6) +#define RT5640_IF3_DAC_SEL_SWAP (0x1 << 6) +#define RT5640_IF3_DAC_SEL_L2R (0x2 << 6) +#define RT5640_IF3_DAC_SEL_R2L (0x3 << 6) +#define RT5640_IF3_ADC_SEL_MASK (0x3 << 4) +#define RT5640_IF3_ADC_SEL_SFT 4 +#define RT5640_IF3_ADC_SEL_NOR (0x0 << 4) +#define RT5640_IF3_ADC_SEL_SWAP (0x1 << 4) +#define RT5640_IF3_ADC_SEL_L2R (0x2 << 4) +#define RT5640_IF3_ADC_SEL_R2L (0x3 << 4) + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5640_G_HP_L_RM_L_MASK (0x7 << 13) +#define RT5640_G_HP_L_RM_L_SFT 13 +#define RT5640_G_IN_L_RM_L_MASK (0x7 << 10) +#define RT5640_G_IN_L_RM_L_SFT 10 +#define RT5640_G_BST4_RM_L_MASK (0x7 << 7) +#define RT5640_G_BST4_RM_L_SFT 7 +#define RT5640_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5640_G_BST3_RM_L_SFT 4 +#define RT5640_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5640_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5640_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5640_G_BST1_RM_L_SFT 13 +#define RT5640_G_OM_L_RM_L_MASK (0x7 << 10) +#define RT5640_G_OM_L_RM_L_SFT 10 +#define RT5640_M_HP_L_RM_L (0x1 << 6) +#define RT5640_M_HP_L_RM_L_SFT 6 +#define RT5640_M_IN_L_RM_L (0x1 << 5) +#define RT5640_M_IN_L_RM_L_SFT 5 +#define RT5640_M_BST4_RM_L (0x1 << 4) +#define RT5640_M_BST4_RM_L_SFT 4 +#define RT5640_M_BST3_RM_L (0x1 << 3) +#define RT5640_M_BST3_RM_L_SFT 3 +#define RT5640_M_BST2_RM_L (0x1 << 2) +#define RT5640_M_BST2_RM_L_SFT 2 +#define RT5640_M_BST1_RM_L (0x1 << 1) +#define RT5640_M_BST1_RM_L_SFT 1 +#define RT5640_M_OM_L_RM_L (0x1) +#define RT5640_M_OM_L_RM_L_SFT 0 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5640_G_HP_R_RM_R_MASK (0x7 << 13) +#define RT5640_G_HP_R_RM_R_SFT 13 +#define RT5640_G_IN_R_RM_R_MASK (0x7 << 10) +#define RT5640_G_IN_R_RM_R_SFT 10 +#define RT5640_G_BST4_RM_R_MASK (0x7 << 7) +#define RT5640_G_BST4_RM_R_SFT 7 +#define RT5640_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5640_G_BST3_RM_R_SFT 4 +#define RT5640_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5640_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5640_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5640_G_BST1_RM_R_SFT 13 +#define RT5640_G_OM_R_RM_R_MASK (0x7 << 10) +#define RT5640_G_OM_R_RM_R_SFT 10 +#define RT5640_M_HP_R_RM_R (0x1 << 6) +#define RT5640_M_HP_R_RM_R_SFT 6 +#define RT5640_M_IN_R_RM_R (0x1 << 5) +#define RT5640_M_IN_R_RM_R_SFT 5 +#define RT5640_M_BST4_RM_R (0x1 << 4) +#define RT5640_M_BST4_RM_R_SFT 4 +#define RT5640_M_BST3_RM_R (0x1 << 3) +#define RT5640_M_BST3_RM_R_SFT 3 +#define RT5640_M_BST2_RM_R (0x1 << 2) +#define RT5640_M_BST2_RM_R_SFT 2 +#define RT5640_M_BST1_RM_R (0x1 << 1) +#define RT5640_M_BST1_RM_R_SFT 1 +#define RT5640_M_OM_R_RM_R (0x1) +#define RT5640_M_OM_R_RM_R_SFT 0 + +/* HPMIX Control (0x45) */ +#define RT5640_M_DAC2_HM (0x1 << 15) +#define RT5640_M_DAC2_HM_SFT 15 +#define RT5640_M_DAC1_HM (0x1 << 14) +#define RT5640_M_DAC1_HM_SFT 14 +#define RT5640_M_HPVOL_HM (0x1 << 13) +#define RT5640_M_HPVOL_HM_SFT 13 +#define RT5640_G_HPOMIX_MASK (0x1 << 12) +#define RT5640_G_HPOMIX_SFT 12 + +/* SPK Left Mixer Control (0x46) */ +#define RT5640_G_RM_L_SM_L_MASK (0x3 << 14) +#define RT5640_G_RM_L_SM_L_SFT 14 +#define RT5640_G_IN_L_SM_L_MASK (0x3 << 12) +#define RT5640_G_IN_L_SM_L_SFT 12 +#define RT5640_G_DAC_L1_SM_L_MASK (0x3 << 10) +#define RT5640_G_DAC_L1_SM_L_SFT 10 +#define RT5640_G_DAC_L2_SM_L_MASK (0x3 << 8) +#define RT5640_G_DAC_L2_SM_L_SFT 8 +#define RT5640_G_OM_L_SM_L_MASK (0x3 << 6) +#define RT5640_G_OM_L_SM_L_SFT 6 +#define RT5640_M_RM_L_SM_L (0x1 << 5) +#define RT5640_M_RM_L_SM_L_SFT 5 +#define RT5640_M_IN_L_SM_L (0x1 << 4) +#define RT5640_M_IN_L_SM_L_SFT 4 +#define RT5640_M_DAC_L1_SM_L (0x1 << 3) +#define RT5640_M_DAC_L1_SM_L_SFT 3 +#define RT5640_M_DAC_L2_SM_L (0x1 << 2) +#define RT5640_M_DAC_L2_SM_L_SFT 2 +#define RT5640_M_OM_L_SM_L (0x1 << 1) +#define RT5640_M_OM_L_SM_L_SFT 1 + +/* SPK Right Mixer Control (0x47) */ +#define RT5640_G_RM_R_SM_R_MASK (0x3 << 14) +#define RT5640_G_RM_R_SM_R_SFT 14 +#define RT5640_G_IN_R_SM_R_MASK (0x3 << 12) +#define RT5640_G_IN_R_SM_R_SFT 12 +#define RT5640_G_DAC_R1_SM_R_MASK (0x3 << 10) +#define RT5640_G_DAC_R1_SM_R_SFT 10 +#define RT5640_G_DAC_R2_SM_R_MASK (0x3 << 8) +#define RT5640_G_DAC_R2_SM_R_SFT 8 +#define RT5640_G_OM_R_SM_R_MASK (0x3 << 6) +#define RT5640_G_OM_R_SM_R_SFT 6 +#define RT5640_M_RM_R_SM_R (0x1 << 5) +#define RT5640_M_RM_R_SM_R_SFT 5 +#define RT5640_M_IN_R_SM_R (0x1 << 4) +#define RT5640_M_IN_R_SM_R_SFT 4 +#define RT5640_M_DAC_R1_SM_R (0x1 << 3) +#define RT5640_M_DAC_R1_SM_R_SFT 3 +#define RT5640_M_DAC_R2_SM_R (0x1 << 2) +#define RT5640_M_DAC_R2_SM_R_SFT 2 +#define RT5640_M_OM_R_SM_R (0x1 << 1) +#define RT5640_M_OM_R_SM_R_SFT 1 + +/* SPOLMIX Control (0x48) */ +#define RT5640_M_DAC_R1_SPM_L (0x1 << 15) +#define RT5640_M_DAC_R1_SPM_L_SFT 15 +#define RT5640_M_DAC_L1_SPM_L (0x1 << 14) +#define RT5640_M_DAC_L1_SPM_L_SFT 14 +#define RT5640_M_SV_R_SPM_L (0x1 << 13) +#define RT5640_M_SV_R_SPM_L_SFT 13 +#define RT5640_M_SV_L_SPM_L (0x1 << 12) +#define RT5640_M_SV_L_SPM_L_SFT 12 +#define RT5640_M_BST1_SPM_L (0x1 << 11) +#define RT5640_M_BST1_SPM_L_SFT 11 + +/* SPORMIX Control (0x49) */ +#define RT5640_M_DAC_R1_SPM_R (0x1 << 13) +#define RT5640_M_DAC_R1_SPM_R_SFT 13 +#define RT5640_M_SV_R_SPM_R (0x1 << 12) +#define RT5640_M_SV_R_SPM_R_SFT 12 +#define RT5640_M_BST1_SPM_R (0x1 << 11) +#define RT5640_M_BST1_SPM_R_SFT 11 + +/* SPOLMIX / SPORMIX Ratio Control (0x4a) */ +#define RT5640_SPO_CLSD_RATIO_MASK (0x7) +#define RT5640_SPO_CLSD_RATIO_SFT 0 + +/* Mono Output Mixer Control (0x4c) */ +#define RT5640_M_DAC_R2_MM (0x1 << 15) +#define RT5640_M_DAC_R2_MM_SFT 15 +#define RT5640_M_DAC_L2_MM (0x1 << 14) +#define RT5640_M_DAC_L2_MM_SFT 14 +#define RT5640_M_OV_R_MM (0x1 << 13) +#define RT5640_M_OV_R_MM_SFT 13 +#define RT5640_M_OV_L_MM (0x1 << 12) +#define RT5640_M_OV_L_MM_SFT 12 +#define RT5640_M_BST1_MM (0x1 << 11) +#define RT5640_M_BST1_MM_SFT 11 +#define RT5640_G_MONOMIX_MASK (0x1 << 10) +#define RT5640_G_MONOMIX_SFT 10 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5640_G_BST3_OM_L_MASK (0x7 << 13) +#define RT5640_G_BST3_OM_L_SFT 13 +#define RT5640_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5640_G_BST2_OM_L_SFT 10 +#define RT5640_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5640_G_BST1_OM_L_SFT 7 +#define RT5640_G_IN_L_OM_L_MASK (0x7 << 4) +#define RT5640_G_IN_L_OM_L_SFT 4 +#define RT5640_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5640_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5640_G_DAC_R2_OM_L_MASK (0x7 << 13) +#define RT5640_G_DAC_R2_OM_L_SFT 13 +#define RT5640_G_DAC_L2_OM_L_MASK (0x7 << 10) +#define RT5640_G_DAC_L2_OM_L_SFT 10 +#define RT5640_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5640_G_DAC_L1_OM_L_SFT 7 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5640_M_SM_L_OM_L (0x1 << 8) +#define RT5640_M_SM_L_OM_L_SFT 8 +#define RT5640_M_BST3_OM_L (0x1 << 7) +#define RT5640_M_BST3_OM_L_SFT 7 +#define RT5640_M_BST2_OM_L (0x1 << 6) +#define RT5640_M_BST2_OM_L_SFT 6 +#define RT5640_M_BST1_OM_L (0x1 << 5) +#define RT5640_M_BST1_OM_L_SFT 5 +#define RT5640_M_IN_L_OM_L (0x1 << 4) +#define RT5640_M_IN_L_OM_L_SFT 4 +#define RT5640_M_RM_L_OM_L (0x1 << 3) +#define RT5640_M_RM_L_OM_L_SFT 3 +#define RT5640_M_DAC_R2_OM_L (0x1 << 2) +#define RT5640_M_DAC_R2_OM_L_SFT 2 +#define RT5640_M_DAC_L2_OM_L (0x1 << 1) +#define RT5640_M_DAC_L2_OM_L_SFT 1 +#define RT5640_M_DAC_L1_OM_L (0x1) +#define RT5640_M_DAC_L1_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5640_G_BST4_OM_R_MASK (0x7 << 13) +#define RT5640_G_BST4_OM_R_SFT 13 +#define RT5640_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5640_G_BST2_OM_R_SFT 10 +#define RT5640_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5640_G_BST1_OM_R_SFT 7 +#define RT5640_G_IN_R_OM_R_MASK (0x7 << 4) +#define RT5640_G_IN_R_OM_R_SFT 4 +#define RT5640_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5640_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5640_G_DAC_L2_OM_R_MASK (0x7 << 13) +#define RT5640_G_DAC_L2_OM_R_SFT 13 +#define RT5640_G_DAC_R2_OM_R_MASK (0x7 << 10) +#define RT5640_G_DAC_R2_OM_R_SFT 10 +#define RT5640_G_DAC_R1_OM_R_MASK (0x7 << 7) +#define RT5640_G_DAC_R1_OM_R_SFT 7 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5640_M_SM_L_OM_R (0x1 << 8) +#define RT5640_M_SM_L_OM_R_SFT 8 +#define RT5640_M_BST4_OM_R (0x1 << 7) +#define RT5640_M_BST4_OM_R_SFT 7 +#define RT5640_M_BST2_OM_R (0x1 << 6) +#define RT5640_M_BST2_OM_R_SFT 6 +#define RT5640_M_BST1_OM_R (0x1 << 5) +#define RT5640_M_BST1_OM_R_SFT 5 +#define RT5640_M_IN_R_OM_R (0x1 << 4) +#define RT5640_M_IN_R_OM_R_SFT 4 +#define RT5640_M_RM_R_OM_R (0x1 << 3) +#define RT5640_M_RM_R_OM_R_SFT 3 +#define RT5640_M_DAC_L2_OM_R (0x1 << 2) +#define RT5640_M_DAC_L2_OM_R_SFT 2 +#define RT5640_M_DAC_R2_OM_R (0x1 << 1) +#define RT5640_M_DAC_R2_OM_R_SFT 1 +#define RT5640_M_DAC_R1_OM_R (0x1) +#define RT5640_M_DAC_R1_OM_R_SFT 0 + +/* LOUT Mixer Control (0x53) */ +#define RT5640_M_DAC_L1_LM (0x1 << 15) +#define RT5640_M_DAC_L1_LM_SFT 15 +#define RT5640_M_DAC_R1_LM (0x1 << 14) +#define RT5640_M_DAC_R1_LM_SFT 14 +#define RT5640_M_OV_L_LM (0x1 << 13) +#define RT5640_M_OV_L_LM_SFT 13 +#define RT5640_M_OV_R_LM (0x1 << 12) +#define RT5640_M_OV_R_LM_SFT 12 +#define RT5640_G_LOUTMIX_MASK (0x1 << 11) +#define RT5640_G_LOUTMIX_SFT 11 + +/* Power Management for Digital 1 (0x61) */ +#define RT5640_PWR_I2S1 (0x1 << 15) +#define RT5640_PWR_I2S1_BIT 15 +#define RT5640_PWR_I2S2 (0x1 << 14) +#define RT5640_PWR_I2S2_BIT 14 +#define RT5640_PWR_DAC_L1 (0x1 << 12) +#define RT5640_PWR_DAC_L1_BIT 12 +#define RT5640_PWR_DAC_R1 (0x1 << 11) +#define RT5640_PWR_DAC_R1_BIT 11 +#define RT5640_PWR_DAC_L2 (0x1 << 7) +#define RT5640_PWR_DAC_L2_BIT 7 +#define RT5640_PWR_DAC_R2 (0x1 << 6) +#define RT5640_PWR_DAC_R2_BIT 6 +#define RT5640_PWR_ADC_L (0x1 << 2) +#define RT5640_PWR_ADC_L_BIT 2 +#define RT5640_PWR_ADC_R (0x1 << 1) +#define RT5640_PWR_ADC_R_BIT 1 +#define RT5640_PWR_CLS_D (0x1) +#define RT5640_PWR_CLS_D_BIT 0 + +/* Power Management for Digital 2 (0x62) */ +#define RT5640_PWR_ADC_SF (0x1 << 15) +#define RT5640_PWR_ADC_SF_BIT 15 +#define RT5640_PWR_ADC_MF_L (0x1 << 14) +#define RT5640_PWR_ADC_MF_L_BIT 14 +#define RT5640_PWR_ADC_MF_R (0x1 << 13) +#define RT5640_PWR_ADC_MF_R_BIT 13 +#define RT5640_PWR_I2S_DSP (0x1 << 12) +#define RT5640_PWR_I2S_DSP_BIT 12 + +/* Power Management for Analog 1 (0x63) */ +#define RT5640_PWR_VREF1 (0x1 << 15) +#define RT5640_PWR_VREF1_BIT 15 +#define RT5640_PWR_FV1 (0x1 << 14) +#define RT5640_PWR_FV1_BIT 14 +#define RT5640_PWR_MB (0x1 << 13) +#define RT5640_PWR_MB_BIT 13 +#define RT5640_PWR_LM (0x1 << 12) +#define RT5640_PWR_LM_BIT 12 +#define RT5640_PWR_BG (0x1 << 11) +#define RT5640_PWR_BG_BIT 11 +#define RT5640_PWR_MM (0x1 << 10) +#define RT5640_PWR_MM_BIT 10 +#define RT5640_PWR_MA (0x1 << 8) +#define RT5640_PWR_MA_BIT 8 +#define RT5640_PWR_HP_L (0x1 << 7) +#define RT5640_PWR_HP_L_BIT 7 +#define RT5640_PWR_HP_R (0x1 << 6) +#define RT5640_PWR_HP_R_BIT 6 +#define RT5640_PWR_HA (0x1 << 5) +#define RT5640_PWR_HA_BIT 5 +#define RT5640_PWR_VREF2 (0x1 << 4) +#define RT5640_PWR_VREF2_BIT 4 +#define RT5640_PWR_FV2 (0x1 << 3) +#define RT5640_PWR_FV2_BIT 3 +#define RT5640_PWR_LDO2 (0x1 << 2) +#define RT5640_PWR_LDO2_BIT 2 + +/* Power Management for Analog 2 (0x64) */ +#define RT5640_PWR_BST1 (0x1 << 15) +#define RT5640_PWR_BST1_BIT 15 +#define RT5640_PWR_BST2 (0x1 << 14) +#define RT5640_PWR_BST2_BIT 14 +#define RT5640_PWR_BST3 (0x1 << 13) +#define RT5640_PWR_BST3_BIT 13 +#define RT5640_PWR_BST4 (0x1 << 12) +#define RT5640_PWR_BST4_BIT 12 +#define RT5640_PWR_MB1 (0x1 << 11) +#define RT5640_PWR_MB1_BIT 11 +#define RT5640_PWR_PLL (0x1 << 9) +#define RT5640_PWR_PLL_BIT 9 + +/* Power Management for Mixer (0x65) */ +#define RT5640_PWR_OM_L (0x1 << 15) +#define RT5640_PWR_OM_L_BIT 15 +#define RT5640_PWR_OM_R (0x1 << 14) +#define RT5640_PWR_OM_R_BIT 14 +#define RT5640_PWR_SM_L (0x1 << 13) +#define RT5640_PWR_SM_L_BIT 13 +#define RT5640_PWR_SM_R (0x1 << 12) +#define RT5640_PWR_SM_R_BIT 12 +#define RT5640_PWR_RM_L (0x1 << 11) +#define RT5640_PWR_RM_L_BIT 11 +#define RT5640_PWR_RM_R (0x1 << 10) +#define RT5640_PWR_RM_R_BIT 10 + +/* Power Management for Volume (0x66) */ +#define RT5640_PWR_SV_L (0x1 << 15) +#define RT5640_PWR_SV_L_BIT 15 +#define RT5640_PWR_SV_R (0x1 << 14) +#define RT5640_PWR_SV_R_BIT 14 +#define RT5640_PWR_OV_L (0x1 << 13) +#define RT5640_PWR_OV_L_BIT 13 +#define RT5640_PWR_OV_R (0x1 << 12) +#define RT5640_PWR_OV_R_BIT 12 +#define RT5640_PWR_HV_L (0x1 << 11) +#define RT5640_PWR_HV_L_BIT 11 +#define RT5640_PWR_HV_R (0x1 << 10) +#define RT5640_PWR_HV_R_BIT 10 +#define RT5640_PWR_IN_L (0x1 << 9) +#define RT5640_PWR_IN_L_BIT 9 +#define RT5640_PWR_IN_R (0x1 << 8) +#define RT5640_PWR_IN_R_BIT 8 + +/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71 0x72) */ +#define RT5640_I2S_MS_MASK (0x1 << 15) +#define RT5640_I2S_MS_SFT 15 +#define RT5640_I2S_MS_M (0x0 << 15) +#define RT5640_I2S_MS_S (0x1 << 15) +#define RT5640_I2S_IF_MASK (0x7 << 12) +#define RT5640_I2S_IF_SFT 12 +#define RT5640_I2S_O_CP_MASK (0x3 << 10) +#define RT5640_I2S_O_CP_SFT 10 +#define RT5640_I2S_O_CP_OFF (0x0 << 10) +#define RT5640_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5640_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5640_I2S_I_CP_MASK (0x3 << 8) +#define RT5640_I2S_I_CP_SFT 8 +#define RT5640_I2S_I_CP_OFF (0x0 << 8) +#define RT5640_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5640_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5640_I2S_BP_MASK (0x1 << 7) +#define RT5640_I2S_BP_SFT 7 +#define RT5640_I2S_BP_NOR (0x0 << 7) +#define RT5640_I2S_BP_INV (0x1 << 7) +#define RT5640_I2S_DL_MASK (0x3 << 2) +#define RT5640_I2S_DL_SFT 2 +#define RT5640_I2S_DL_16 (0x0 << 2) +#define RT5640_I2S_DL_20 (0x1 << 2) +#define RT5640_I2S_DL_24 (0x2 << 2) +#define RT5640_I2S_DL_8 (0x3 << 2) +#define RT5640_I2S_DF_MASK (0x3) +#define RT5640_I2S_DF_SFT 0 +#define RT5640_I2S_DF_I2S (0x0) +#define RT5640_I2S_DF_LEFT (0x1) +#define RT5640_I2S_DF_PCM_A (0x2) +#define RT5640_I2S_DF_PCM_B (0x3) + +/* I2S2 Audio Serial Data Port Control (0x71) */ +#define RT5640_I2S2_SDI_MASK (0x1 << 6) +#define RT5640_I2S2_SDI_SFT 6 +#define RT5640_I2S2_SDI_I2S1 (0x0 << 6) +#define RT5640_I2S2_SDI_I2S2 (0x1 << 6) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5640_I2S_BCLK_MS1_MASK (0x1 << 15) +#define RT5640_I2S_BCLK_MS1_SFT 15 +#define RT5640_I2S_BCLK_MS1_32 (0x0 << 15) +#define RT5640_I2S_BCLK_MS1_64 (0x1 << 15) +#define RT5640_I2S_PD1_MASK (0x7 << 12) +#define RT5640_I2S_PD1_SFT 12 +#define RT5640_I2S_PD1_1 (0x0 << 12) +#define RT5640_I2S_PD1_2 (0x1 << 12) +#define RT5640_I2S_PD1_3 (0x2 << 12) +#define RT5640_I2S_PD1_4 (0x3 << 12) +#define RT5640_I2S_PD1_6 (0x4 << 12) +#define RT5640_I2S_PD1_8 (0x5 << 12) +#define RT5640_I2S_PD1_12 (0x6 << 12) +#define RT5640_I2S_PD1_16 (0x7 << 12) +#define RT5640_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5640_I2S_BCLK_MS2_SFT 11 +#define RT5640_I2S_BCLK_MS2_32 (0x0 << 11) +#define RT5640_I2S_BCLK_MS2_64 (0x1 << 11) +#define RT5640_I2S_PD2_MASK (0x7 << 8) +#define RT5640_I2S_PD2_SFT 8 +#define RT5640_I2S_PD2_1 (0x0 << 8) +#define RT5640_I2S_PD2_2 (0x1 << 8) +#define RT5640_I2S_PD2_3 (0x2 << 8) +#define RT5640_I2S_PD2_4 (0x3 << 8) +#define RT5640_I2S_PD2_6 (0x4 << 8) +#define RT5640_I2S_PD2_8 (0x5 << 8) +#define RT5640_I2S_PD2_12 (0x6 << 8) +#define RT5640_I2S_PD2_16 (0x7 << 8) +#define RT5640_I2S_BCLK_MS3_MASK (0x1 << 7) +#define RT5640_I2S_BCLK_MS3_SFT 7 +#define RT5640_I2S_BCLK_MS3_32 (0x0 << 7) +#define RT5640_I2S_BCLK_MS3_64 (0x1 << 7) +#define RT5640_I2S_PD3_MASK (0x7 << 4) +#define RT5640_I2S_PD3_SFT 4 +#define RT5640_I2S_PD3_1 (0x0 << 4) +#define RT5640_I2S_PD3_2 (0x1 << 4) +#define RT5640_I2S_PD3_3 (0x2 << 4) +#define RT5640_I2S_PD3_4 (0x3 << 4) +#define RT5640_I2S_PD3_6 (0x4 << 4) +#define RT5640_I2S_PD3_8 (0x5 << 4) +#define RT5640_I2S_PD3_12 (0x6 << 4) +#define RT5640_I2S_PD3_16 (0x7 << 4) +#define RT5640_DAC_OSR_MASK (0x3 << 2) +#define RT5640_DAC_OSR_SFT 2 +#define RT5640_DAC_OSR_128 (0x0 << 2) +#define RT5640_DAC_OSR_64 (0x1 << 2) +#define RT5640_DAC_OSR_32 (0x2 << 2) +#define RT5640_DAC_OSR_16 (0x3 << 2) +#define RT5640_ADC_OSR_MASK (0x3) +#define RT5640_ADC_OSR_SFT 0 +#define RT5640_ADC_OSR_128 (0x0) +#define RT5640_ADC_OSR_64 (0x1) +#define RT5640_ADC_OSR_32 (0x2) +#define RT5640_ADC_OSR_16 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5640_DAC_L_OSR_MASK (0x3 << 14) +#define RT5640_DAC_L_OSR_SFT 14 +#define RT5640_DAC_L_OSR_128 (0x0 << 14) +#define RT5640_DAC_L_OSR_64 (0x1 << 14) +#define RT5640_DAC_L_OSR_32 (0x2 << 14) +#define RT5640_DAC_L_OSR_16 (0x3 << 14) +#define RT5640_ADC_R_OSR_MASK (0x3 << 12) +#define RT5640_ADC_R_OSR_SFT 12 +#define RT5640_ADC_R_OSR_128 (0x0 << 12) +#define RT5640_ADC_R_OSR_64 (0x1 << 12) +#define RT5640_ADC_R_OSR_32 (0x2 << 12) +#define RT5640_ADC_R_OSR_16 (0x3 << 12) +#define RT5640_DAHPF_EN (0x1 << 11) +#define RT5640_DAHPF_EN_SFT 11 +#define RT5640_ADHPF_EN (0x1 << 10) +#define RT5640_ADHPF_EN_SFT 10 + +/* Digital Microphone Control (0x75) */ +#define RT5640_DMIC_1_EN_MASK (0x1 << 15) +#define RT5640_DMIC_1_EN_SFT 15 +#define RT5640_DMIC_1_DIS (0x0 << 15) +#define RT5640_DMIC_1_EN (0x1 << 15) +#define RT5640_DMIC_2_EN_MASK (0x1 << 14) +#define RT5640_DMIC_2_EN_SFT 14 +#define RT5640_DMIC_2_DIS (0x0 << 14) +#define RT5640_DMIC_2_EN (0x1 << 14) +#define RT5640_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5640_DMIC_1L_LH_SFT 13 +#define RT5640_DMIC_1L_LH_FALLING (0x0 << 13) +#define RT5640_DMIC_1L_LH_RISING (0x1 << 13) +#define RT5640_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5640_DMIC_1R_LH_SFT 12 +#define RT5640_DMIC_1R_LH_FALLING (0x0 << 12) +#define RT5640_DMIC_1R_LH_RISING (0x1 << 12) +#define RT5640_DMIC_1_DP_MASK (0x1 << 11) +#define RT5640_DMIC_1_DP_SFT 11 +#define RT5640_DMIC_1_DP_GPIO3 (0x0 << 11) +#define RT5640_DMIC_1_DP_IN1P (0x1 << 11) +#define RT5640_DMIC_2_DP_MASK (0x1 << 10) +#define RT5640_DMIC_2_DP_SFT 10 +#define RT5640_DMIC_2_DP_GPIO4 (0x0 << 10) +#define RT5640_DMIC_2_DP_IN1N (0x1 << 10) +#define RT5640_DMIC_2L_LH_MASK (0x1 << 9) +#define RT5640_DMIC_2L_LH_SFT 9 +#define RT5640_DMIC_2L_LH_FALLING (0x0 << 9) +#define RT5640_DMIC_2L_LH_RISING (0x1 << 9) +#define RT5640_DMIC_2R_LH_MASK (0x1 << 8) +#define RT5640_DMIC_2R_LH_SFT 8 +#define RT5640_DMIC_2R_LH_FALLING (0x0 << 8) +#define RT5640_DMIC_2R_LH_RISING (0x1 << 8) +#define RT5640_DMIC_CLK_MASK (0x7 << 5) +#define RT5640_DMIC_CLK_SFT 5 + +/* Global Clock Control (0x80) */ +#define RT5640_SCLK_SRC_MASK (0x3 << 14) +#define RT5640_SCLK_SRC_SFT 14 +#define RT5640_SCLK_SRC_MCLK (0x0 << 14) +#define RT5640_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5640_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5640_PLL1_SRC_MASK (0x3 << 12) +#define RT5640_PLL1_SRC_SFT 12 +#define RT5640_PLL1_SRC_MCLK (0x0 << 12) +#define RT5640_PLL1_SRC_BCLK1 (0x1 << 12) +#define RT5640_PLL1_SRC_BCLK2 (0x2 << 12) +#define RT5640_PLL1_SRC_BCLK3 (0x3 << 12) +#define RT5640_PLL1_PD_MASK (0x1 << 3) +#define RT5640_PLL1_PD_SFT 3 +#define RT5640_PLL1_PD_1 (0x0 << 3) +#define RT5640_PLL1_PD_2 (0x1 << 3) + +#define RT5640_PLL_INP_MAX 40000000 +#define RT5640_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5640_PLL_N_MAX 0x1ff +#define RT5640_PLL_N_MASK (RT5640_PLL_N_MAX << 7) +#define RT5640_PLL_N_SFT 7 +#define RT5640_PLL_K_MAX 0x1f +#define RT5640_PLL_K_MASK (RT5640_PLL_K_MAX) +#define RT5640_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5640_PLL_M_MAX 0xf +#define RT5640_PLL_M_MASK (RT5640_PLL_M_MAX << 12) +#define RT5640_PLL_M_SFT 12 +#define RT5640_PLL_M_BP (0x1 << 11) +#define RT5640_PLL_M_BP_SFT 11 + +/* ASRC Control 1 (0x83) */ +#define RT5640_STO_T_MASK (0x1 << 15) +#define RT5640_STO_T_SFT 15 +#define RT5640_STO_T_SCLK (0x0 << 15) +#define RT5640_STO_T_LRCK1 (0x1 << 15) +#define RT5640_M1_T_MASK (0x1 << 14) +#define RT5640_M1_T_SFT 14 +#define RT5640_M1_T_I2S2 (0x0 << 14) +#define RT5640_M1_T_I2S2_D3 (0x1 << 14) +#define RT5640_I2S2_F_MASK (0x1 << 12) +#define RT5640_I2S2_F_SFT 12 +#define RT5640_I2S2_F_I2S2_D2 (0x0 << 12) +#define RT5640_I2S2_F_I2S1_TCLK (0x1 << 12) +#define RT5640_DMIC_1_M_MASK (0x1 << 9) +#define RT5640_DMIC_1_M_SFT 9 +#define RT5640_DMIC_1_M_NOR (0x0 << 9) +#define RT5640_DMIC_1_M_ASYN (0x1 << 9) +#define RT5640_DMIC_2_M_MASK (0x1 << 8) +#define RT5640_DMIC_2_M_SFT 8 +#define RT5640_DMIC_2_M_NOR (0x0 << 8) +#define RT5640_DMIC_2_M_ASYN (0x1 << 8) + +/* ASRC clock source selection (0x84) */ +#define RT5640_CLK_SEL_SYS (0x0) +#define RT5640_CLK_SEL_ASRC (0x1) + +/* ASRC Control 2 (0x84) */ +#define RT5640_MDA_L_M_MASK (0x1 << 15) +#define RT5640_MDA_L_M_SFT 15 +#define RT5640_MDA_L_M_NOR (0x0 << 15) +#define RT5640_MDA_L_M_ASYN (0x1 << 15) +#define RT5640_MDA_R_M_MASK (0x1 << 14) +#define RT5640_MDA_R_M_SFT 14 +#define RT5640_MDA_R_M_NOR (0x0 << 14) +#define RT5640_MDA_R_M_ASYN (0x1 << 14) +#define RT5640_MAD_L_M_MASK (0x1 << 13) +#define RT5640_MAD_L_M_SFT 13 +#define RT5640_MAD_L_M_NOR (0x0 << 13) +#define RT5640_MAD_L_M_ASYN (0x1 << 13) +#define RT5640_MAD_R_M_MASK (0x1 << 12) +#define RT5640_MAD_R_M_SFT 12 +#define RT5640_MAD_R_M_NOR (0x0 << 12) +#define RT5640_MAD_R_M_ASYN (0x1 << 12) +#define RT5640_ADC_M_MASK (0x1 << 11) +#define RT5640_ADC_M_SFT 11 +#define RT5640_ADC_M_NOR (0x0 << 11) +#define RT5640_ADC_M_ASYN (0x1 << 11) +#define RT5640_STO_DAC_M_MASK (0x1 << 5) +#define RT5640_STO_DAC_M_SFT 5 +#define RT5640_STO_DAC_M_NOR (0x0 << 5) +#define RT5640_STO_DAC_M_ASYN (0x1 << 5) +#define RT5640_I2S1_R_D_MASK (0x1 << 4) +#define RT5640_I2S1_R_D_SFT 4 +#define RT5640_I2S1_R_D_DIS (0x0 << 4) +#define RT5640_I2S1_R_D_EN (0x1 << 4) +#define RT5640_I2S2_R_D_MASK (0x1 << 3) +#define RT5640_I2S2_R_D_SFT 3 +#define RT5640_I2S2_R_D_DIS (0x0 << 3) +#define RT5640_I2S2_R_D_EN (0x1 << 3) +#define RT5640_PRE_SCLK_MASK (0x3) +#define RT5640_PRE_SCLK_SFT 0 +#define RT5640_PRE_SCLK_512 (0x0) +#define RT5640_PRE_SCLK_1024 (0x1) +#define RT5640_PRE_SCLK_2048 (0x2) + +/* ASRC Control 3 (0x85) */ +#define RT5640_I2S1_RATE_MASK (0xf << 12) +#define RT5640_I2S1_RATE_SFT 12 +#define RT5640_I2S2_RATE_MASK (0xf << 8) +#define RT5640_I2S2_RATE_SFT 8 + +/* ASRC Control 4 (0x89) */ +#define RT5640_I2S1_PD_MASK (0x7 << 12) +#define RT5640_I2S1_PD_SFT 12 +#define RT5640_I2S2_PD_MASK (0x7 << 8) +#define RT5640_I2S2_PD_SFT 8 + +/* HPOUT Over Current Detection (0x8b) */ +#define RT5640_HP_OVCD_MASK (0x1 << 10) +#define RT5640_HP_OVCD_SFT 10 +#define RT5640_HP_OVCD_DIS (0x0 << 10) +#define RT5640_HP_OVCD_EN (0x1 << 10) +#define RT5640_HP_OC_TH_MASK (0x3 << 8) +#define RT5640_HP_OC_TH_SFT 8 +#define RT5640_HP_OC_TH_90 (0x0 << 8) +#define RT5640_HP_OC_TH_105 (0x1 << 8) +#define RT5640_HP_OC_TH_120 (0x2 << 8) +#define RT5640_HP_OC_TH_135 (0x3 << 8) + +/* Class D Over Current Control (0x8c) */ +#define RT5640_CLSD_OC_MASK (0x1 << 9) +#define RT5640_CLSD_OC_SFT 9 +#define RT5640_CLSD_OC_PU (0x0 << 9) +#define RT5640_CLSD_OC_PD (0x1 << 9) +#define RT5640_AUTO_PD_MASK (0x1 << 8) +#define RT5640_AUTO_PD_SFT 8 +#define RT5640_AUTO_PD_DIS (0x0 << 8) +#define RT5640_AUTO_PD_EN (0x1 << 8) +#define RT5640_CLSD_OC_TH_MASK (0x3f) +#define RT5640_CLSD_OC_TH_SFT 0 + +/* Class D Output Control (0x8d) */ +#define RT5640_CLSD_RATIO_MASK (0xf << 12) +#define RT5640_CLSD_RATIO_SFT 12 +#define RT5640_CLSD_OM_MASK (0x1 << 11) +#define RT5640_CLSD_OM_SFT 11 +#define RT5640_CLSD_OM_MONO (0x0 << 11) +#define RT5640_CLSD_OM_STO (0x1 << 11) +#define RT5640_CLSD_SCH_MASK (0x1 << 10) +#define RT5640_CLSD_SCH_SFT 10 +#define RT5640_CLSD_SCH_L (0x0 << 10) +#define RT5640_CLSD_SCH_S (0x1 << 10) + +/* Depop Mode Control 1 (0x8e) */ +#define RT5640_SMT_TRIG_MASK (0x1 << 15) +#define RT5640_SMT_TRIG_SFT 15 +#define RT5640_SMT_TRIG_DIS (0x0 << 15) +#define RT5640_SMT_TRIG_EN (0x1 << 15) +#define RT5640_HP_L_SMT_MASK (0x1 << 9) +#define RT5640_HP_L_SMT_SFT 9 +#define RT5640_HP_L_SMT_DIS (0x0 << 9) +#define RT5640_HP_L_SMT_EN (0x1 << 9) +#define RT5640_HP_R_SMT_MASK (0x1 << 8) +#define RT5640_HP_R_SMT_SFT 8 +#define RT5640_HP_R_SMT_DIS (0x0 << 8) +#define RT5640_HP_R_SMT_EN (0x1 << 8) +#define RT5640_HP_CD_PD_MASK (0x1 << 7) +#define RT5640_HP_CD_PD_SFT 7 +#define RT5640_HP_CD_PD_DIS (0x0 << 7) +#define RT5640_HP_CD_PD_EN (0x1 << 7) +#define RT5640_RSTN_MASK (0x1 << 6) +#define RT5640_RSTN_SFT 6 +#define RT5640_RSTN_DIS (0x0 << 6) +#define RT5640_RSTN_EN (0x1 << 6) +#define RT5640_RSTP_MASK (0x1 << 5) +#define RT5640_RSTP_SFT 5 +#define RT5640_RSTP_DIS (0x0 << 5) +#define RT5640_RSTP_EN (0x1 << 5) +#define RT5640_HP_CO_MASK (0x1 << 4) +#define RT5640_HP_CO_SFT 4 +#define RT5640_HP_CO_DIS (0x0 << 4) +#define RT5640_HP_CO_EN (0x1 << 4) +#define RT5640_HP_CP_MASK (0x1 << 3) +#define RT5640_HP_CP_SFT 3 +#define RT5640_HP_CP_PD (0x0 << 3) +#define RT5640_HP_CP_PU (0x1 << 3) +#define RT5640_HP_SG_MASK (0x1 << 2) +#define RT5640_HP_SG_SFT 2 +#define RT5640_HP_SG_DIS (0x0 << 2) +#define RT5640_HP_SG_EN (0x1 << 2) +#define RT5640_HP_DP_MASK (0x1 << 1) +#define RT5640_HP_DP_SFT 1 +#define RT5640_HP_DP_PD (0x0 << 1) +#define RT5640_HP_DP_PU (0x1 << 1) +#define RT5640_HP_CB_MASK (0x1) +#define RT5640_HP_CB_SFT 0 +#define RT5640_HP_CB_PD (0x0) +#define RT5640_HP_CB_PU (0x1) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5640_DEPOP_MASK (0x1 << 13) +#define RT5640_DEPOP_SFT 13 +#define RT5640_DEPOP_AUTO (0x0 << 13) +#define RT5640_DEPOP_MAN (0x1 << 13) +#define RT5640_RAMP_MASK (0x1 << 12) +#define RT5640_RAMP_SFT 12 +#define RT5640_RAMP_DIS (0x0 << 12) +#define RT5640_RAMP_EN (0x1 << 12) +#define RT5640_BPS_MASK (0x1 << 11) +#define RT5640_BPS_SFT 11 +#define RT5640_BPS_DIS (0x0 << 11) +#define RT5640_BPS_EN (0x1 << 11) +#define RT5640_FAST_UPDN_MASK (0x1 << 10) +#define RT5640_FAST_UPDN_SFT 10 +#define RT5640_FAST_UPDN_DIS (0x0 << 10) +#define RT5640_FAST_UPDN_EN (0x1 << 10) +#define RT5640_MRES_MASK (0x3 << 8) +#define RT5640_MRES_SFT 8 +#define RT5640_MRES_15MO (0x0 << 8) +#define RT5640_MRES_25MO (0x1 << 8) +#define RT5640_MRES_35MO (0x2 << 8) +#define RT5640_MRES_45MO (0x3 << 8) +#define RT5640_VLO_MASK (0x1 << 7) +#define RT5640_VLO_SFT 7 +#define RT5640_VLO_3V (0x0 << 7) +#define RT5640_VLO_32V (0x1 << 7) +#define RT5640_DIG_DP_MASK (0x1 << 6) +#define RT5640_DIG_DP_SFT 6 +#define RT5640_DIG_DP_DIS (0x0 << 6) +#define RT5640_DIG_DP_EN (0x1 << 6) +#define RT5640_DP_TH_MASK (0x3 << 4) +#define RT5640_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5640_CP_SYS_MASK (0x7 << 12) +#define RT5640_CP_SYS_SFT 12 +#define RT5640_CP_FQ1_MASK (0x7 << 8) +#define RT5640_CP_FQ1_SFT 8 +#define RT5640_CP_FQ2_MASK (0x7 << 4) +#define RT5640_CP_FQ2_SFT 4 +#define RT5640_CP_FQ3_MASK (0x7) +#define RT5640_CP_FQ3_SFT 0 +#define RT5640_CP_FQ_1_5_KHZ 0 +#define RT5640_CP_FQ_3_KHZ 1 +#define RT5640_CP_FQ_6_KHZ 2 +#define RT5640_CP_FQ_12_KHZ 3 +#define RT5640_CP_FQ_24_KHZ 4 +#define RT5640_CP_FQ_48_KHZ 5 +#define RT5640_CP_FQ_96_KHZ 6 +#define RT5640_CP_FQ_192_KHZ 7 + +/* HPOUT charge pump (0x91) */ +#define RT5640_OSW_L_MASK (0x1 << 11) +#define RT5640_OSW_L_SFT 11 +#define RT5640_OSW_L_DIS (0x0 << 11) +#define RT5640_OSW_L_EN (0x1 << 11) +#define RT5640_OSW_R_MASK (0x1 << 10) +#define RT5640_OSW_R_SFT 10 +#define RT5640_OSW_R_DIS (0x0 << 10) +#define RT5640_OSW_R_EN (0x1 << 10) +#define RT5640_PM_HP_MASK (0x3 << 8) +#define RT5640_PM_HP_SFT 8 +#define RT5640_PM_HP_LV (0x0 << 8) +#define RT5640_PM_HP_MV (0x1 << 8) +#define RT5640_PM_HP_HV (0x2 << 8) +#define RT5640_IB_HP_MASK (0x3 << 6) +#define RT5640_IB_HP_SFT 6 +#define RT5640_IB_HP_125IL (0x0 << 6) +#define RT5640_IB_HP_25IL (0x1 << 6) +#define RT5640_IB_HP_5IL (0x2 << 6) +#define RT5640_IB_HP_1IL (0x3 << 6) + +/* PV detection and SPK gain control (0x92) */ +#define RT5640_PVDD_DET_MASK (0x1 << 15) +#define RT5640_PVDD_DET_SFT 15 +#define RT5640_PVDD_DET_DIS (0x0 << 15) +#define RT5640_PVDD_DET_EN (0x1 << 15) +#define RT5640_SPK_AG_MASK (0x1 << 14) +#define RT5640_SPK_AG_SFT 14 +#define RT5640_SPK_AG_DIS (0x0 << 14) +#define RT5640_SPK_AG_EN (0x1 << 14) + +/* Micbias Control (0x93) */ +#define RT5640_MIC1_BS_MASK (0x1 << 15) +#define RT5640_MIC1_BS_SFT 15 +#define RT5640_MIC1_BS_9AV (0x0 << 15) +#define RT5640_MIC1_BS_75AV (0x1 << 15) +#define RT5640_MIC2_BS_MASK (0x1 << 14) +#define RT5640_MIC2_BS_SFT 14 +#define RT5640_MIC2_BS_9AV (0x0 << 14) +#define RT5640_MIC2_BS_75AV (0x1 << 14) +#define RT5640_MIC1_CLK_MASK (0x1 << 13) +#define RT5640_MIC1_CLK_SFT 13 +#define RT5640_MIC1_CLK_DIS (0x0 << 13) +#define RT5640_MIC1_CLK_EN (0x1 << 13) +#define RT5640_MIC2_CLK_MASK (0x1 << 12) +#define RT5640_MIC2_CLK_SFT 12 +#define RT5640_MIC2_CLK_DIS (0x0 << 12) +#define RT5640_MIC2_CLK_EN (0x1 << 12) +#define RT5640_MIC1_OVCD_MASK (0x1 << 11) +#define RT5640_MIC1_OVCD_SFT 11 +#define RT5640_MIC1_OVCD_DIS (0x0 << 11) +#define RT5640_MIC1_OVCD_EN (0x1 << 11) +#define RT5640_MIC1_OVTH_MASK (0x3 << 9) +#define RT5640_MIC1_OVTH_SFT 9 +#define RT5640_MIC1_OVTH_600UA (0x0 << 9) +#define RT5640_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5640_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5640_MIC2_OVCD_MASK (0x1 << 8) +#define RT5640_MIC2_OVCD_SFT 8 +#define RT5640_MIC2_OVCD_DIS (0x0 << 8) +#define RT5640_MIC2_OVCD_EN (0x1 << 8) +#define RT5640_MIC2_OVTH_MASK (0x3 << 6) +#define RT5640_MIC2_OVTH_SFT 6 +#define RT5640_MIC2_OVTH_600UA (0x0 << 6) +#define RT5640_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5640_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5640_PWR_MB_MASK (0x1 << 5) +#define RT5640_PWR_MB_SFT 5 +#define RT5640_PWR_MB_PD (0x0 << 5) +#define RT5640_PWR_MB_PU (0x1 << 5) +#define RT5640_PWR_CLK25M_MASK (0x1 << 4) +#define RT5640_PWR_CLK25M_SFT 4 +#define RT5640_PWR_CLK25M_PD (0x0 << 4) +#define RT5640_PWR_CLK25M_PU (0x1 << 4) + +/* EQ Control 1 (0xb0) */ +#define RT5640_EQ_SRC_MASK (0x1 << 15) +#define RT5640_EQ_SRC_SFT 15 +#define RT5640_EQ_SRC_DAC (0x0 << 15) +#define RT5640_EQ_SRC_ADC (0x1 << 15) +#define RT5640_EQ_UPD (0x1 << 14) +#define RT5640_EQ_UPD_BIT 14 +#define RT5640_EQ_CD_MASK (0x1 << 13) +#define RT5640_EQ_CD_SFT 13 +#define RT5640_EQ_CD_DIS (0x0 << 13) +#define RT5640_EQ_CD_EN (0x1 << 13) +#define RT5640_EQ_DITH_MASK (0x3 << 8) +#define RT5640_EQ_DITH_SFT 8 +#define RT5640_EQ_DITH_NOR (0x0 << 8) +#define RT5640_EQ_DITH_LSB (0x1 << 8) +#define RT5640_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5640_EQ_DITH_LSB_2 (0x3 << 8) + +/* EQ Control 2 (0xb1) */ +#define RT5640_EQ_HPF1_M_MASK (0x1 << 8) +#define RT5640_EQ_HPF1_M_SFT 8 +#define RT5640_EQ_HPF1_M_HI (0x0 << 8) +#define RT5640_EQ_HPF1_M_1ST (0x1 << 8) +#define RT5640_EQ_LPF1_M_MASK (0x1 << 7) +#define RT5640_EQ_LPF1_M_SFT 7 +#define RT5640_EQ_LPF1_M_LO (0x0 << 7) +#define RT5640_EQ_LPF1_M_1ST (0x1 << 7) +#define RT5640_EQ_HPF2_MASK (0x1 << 6) +#define RT5640_EQ_HPF2_SFT 6 +#define RT5640_EQ_HPF2_DIS (0x0 << 6) +#define RT5640_EQ_HPF2_EN (0x1 << 6) +#define RT5640_EQ_HPF1_MASK (0x1 << 5) +#define RT5640_EQ_HPF1_SFT 5 +#define RT5640_EQ_HPF1_DIS (0x0 << 5) +#define RT5640_EQ_HPF1_EN (0x1 << 5) +#define RT5640_EQ_BPF4_MASK (0x1 << 4) +#define RT5640_EQ_BPF4_SFT 4 +#define RT5640_EQ_BPF4_DIS (0x0 << 4) +#define RT5640_EQ_BPF4_EN (0x1 << 4) +#define RT5640_EQ_BPF3_MASK (0x1 << 3) +#define RT5640_EQ_BPF3_SFT 3 +#define RT5640_EQ_BPF3_DIS (0x0 << 3) +#define RT5640_EQ_BPF3_EN (0x1 << 3) +#define RT5640_EQ_BPF2_MASK (0x1 << 2) +#define RT5640_EQ_BPF2_SFT 2 +#define RT5640_EQ_BPF2_DIS (0x0 << 2) +#define RT5640_EQ_BPF2_EN (0x1 << 2) +#define RT5640_EQ_BPF1_MASK (0x1 << 1) +#define RT5640_EQ_BPF1_SFT 1 +#define RT5640_EQ_BPF1_DIS (0x0 << 1) +#define RT5640_EQ_BPF1_EN (0x1 << 1) +#define RT5640_EQ_LPF_MASK (0x1) +#define RT5640_EQ_LPF_SFT 0 +#define RT5640_EQ_LPF_DIS (0x0) +#define RT5640_EQ_LPF_EN (0x1) + +/* Memory Test (0xb2) */ +#define RT5640_MT_MASK (0x1 << 15) +#define RT5640_MT_SFT 15 +#define RT5640_MT_DIS (0x0 << 15) +#define RT5640_MT_EN (0x1 << 15) + +/* DRC/AGC Control 1 (0xb4) */ +#define RT5640_DRC_AGC_P_MASK (0x1 << 15) +#define RT5640_DRC_AGC_P_SFT 15 +#define RT5640_DRC_AGC_P_DAC (0x0 << 15) +#define RT5640_DRC_AGC_P_ADC (0x1 << 15) +#define RT5640_DRC_AGC_MASK (0x1 << 14) +#define RT5640_DRC_AGC_SFT 14 +#define RT5640_DRC_AGC_DIS (0x0 << 14) +#define RT5640_DRC_AGC_EN (0x1 << 14) +#define RT5640_DRC_AGC_UPD (0x1 << 13) +#define RT5640_DRC_AGC_UPD_BIT 13 +#define RT5640_DRC_AGC_AR_MASK (0x1f << 8) +#define RT5640_DRC_AGC_AR_SFT 8 +#define RT5640_DRC_AGC_R_MASK (0x7 << 5) +#define RT5640_DRC_AGC_R_SFT 5 +#define RT5640_DRC_AGC_R_48K (0x1 << 5) +#define RT5640_DRC_AGC_R_96K (0x2 << 5) +#define RT5640_DRC_AGC_R_192K (0x3 << 5) +#define RT5640_DRC_AGC_R_441K (0x5 << 5) +#define RT5640_DRC_AGC_R_882K (0x6 << 5) +#define RT5640_DRC_AGC_R_1764K (0x7 << 5) +#define RT5640_DRC_AGC_RC_MASK (0x1f) +#define RT5640_DRC_AGC_RC_SFT 0 + +/* DRC/AGC Control 2 (0xb5) */ +#define RT5640_DRC_AGC_POB_MASK (0x3f << 8) +#define RT5640_DRC_AGC_POB_SFT 8 +#define RT5640_DRC_AGC_CP_MASK (0x1 << 7) +#define RT5640_DRC_AGC_CP_SFT 7 +#define RT5640_DRC_AGC_CP_DIS (0x0 << 7) +#define RT5640_DRC_AGC_CP_EN (0x1 << 7) +#define RT5640_DRC_AGC_CPR_MASK (0x3 << 5) +#define RT5640_DRC_AGC_CPR_SFT 5 +#define RT5640_DRC_AGC_CPR_1_1 (0x0 << 5) +#define RT5640_DRC_AGC_CPR_1_2 (0x1 << 5) +#define RT5640_DRC_AGC_CPR_1_3 (0x2 << 5) +#define RT5640_DRC_AGC_CPR_1_4 (0x3 << 5) +#define RT5640_DRC_AGC_PRB_MASK (0x1f) +#define RT5640_DRC_AGC_PRB_SFT 0 + +/* DRC/AGC Control 3 (0xb6) */ +#define RT5640_DRC_AGC_NGB_MASK (0xf << 12) +#define RT5640_DRC_AGC_NGB_SFT 12 +#define RT5640_DRC_AGC_TAR_MASK (0x1f << 7) +#define RT5640_DRC_AGC_TAR_SFT 7 +#define RT5640_DRC_AGC_NG_MASK (0x1 << 6) +#define RT5640_DRC_AGC_NG_SFT 6 +#define RT5640_DRC_AGC_NG_DIS (0x0 << 6) +#define RT5640_DRC_AGC_NG_EN (0x1 << 6) +#define RT5640_DRC_AGC_NGH_MASK (0x1 << 5) +#define RT5640_DRC_AGC_NGH_SFT 5 +#define RT5640_DRC_AGC_NGH_DIS (0x0 << 5) +#define RT5640_DRC_AGC_NGH_EN (0x1 << 5) +#define RT5640_DRC_AGC_NGT_MASK (0x1f) +#define RT5640_DRC_AGC_NGT_SFT 0 + +/* ANC Control 1 (0xb8) */ +#define RT5640_ANC_M_MASK (0x1 << 15) +#define RT5640_ANC_M_SFT 15 +#define RT5640_ANC_M_NOR (0x0 << 15) +#define RT5640_ANC_M_REV (0x1 << 15) +#define RT5640_ANC_MASK (0x1 << 14) +#define RT5640_ANC_SFT 14 +#define RT5640_ANC_DIS (0x0 << 14) +#define RT5640_ANC_EN (0x1 << 14) +#define RT5640_ANC_MD_MASK (0x3 << 12) +#define RT5640_ANC_MD_SFT 12 +#define RT5640_ANC_MD_DIS (0x0 << 12) +#define RT5640_ANC_MD_67MS (0x1 << 12) +#define RT5640_ANC_MD_267MS (0x2 << 12) +#define RT5640_ANC_MD_1067MS (0x3 << 12) +#define RT5640_ANC_SN_MASK (0x1 << 11) +#define RT5640_ANC_SN_SFT 11 +#define RT5640_ANC_SN_DIS (0x0 << 11) +#define RT5640_ANC_SN_EN (0x1 << 11) +#define RT5640_ANC_CLK_MASK (0x1 << 10) +#define RT5640_ANC_CLK_SFT 10 +#define RT5640_ANC_CLK_ANC (0x0 << 10) +#define RT5640_ANC_CLK_REG (0x1 << 10) +#define RT5640_ANC_ZCD_MASK (0x3 << 8) +#define RT5640_ANC_ZCD_SFT 8 +#define RT5640_ANC_ZCD_DIS (0x0 << 8) +#define RT5640_ANC_ZCD_T1 (0x1 << 8) +#define RT5640_ANC_ZCD_T2 (0x2 << 8) +#define RT5640_ANC_ZCD_WT (0x3 << 8) +#define RT5640_ANC_CS_MASK (0x1 << 7) +#define RT5640_ANC_CS_SFT 7 +#define RT5640_ANC_CS_DIS (0x0 << 7) +#define RT5640_ANC_CS_EN (0x1 << 7) +#define RT5640_ANC_SW_MASK (0x1 << 6) +#define RT5640_ANC_SW_SFT 6 +#define RT5640_ANC_SW_NOR (0x0 << 6) +#define RT5640_ANC_SW_AUTO (0x1 << 6) +#define RT5640_ANC_CO_L_MASK (0x3f) +#define RT5640_ANC_CO_L_SFT 0 + +/* ANC Control 2 (0xb6) */ +#define RT5640_ANC_FG_R_MASK (0xf << 12) +#define RT5640_ANC_FG_R_SFT 12 +#define RT5640_ANC_FG_L_MASK (0xf << 8) +#define RT5640_ANC_FG_L_SFT 8 +#define RT5640_ANC_CG_R_MASK (0xf << 4) +#define RT5640_ANC_CG_R_SFT 4 +#define RT5640_ANC_CG_L_MASK (0xf) +#define RT5640_ANC_CG_L_SFT 0 + +/* ANC Control 3 (0xb6) */ +#define RT5640_ANC_CD_MASK (0x1 << 6) +#define RT5640_ANC_CD_SFT 6 +#define RT5640_ANC_CD_BOTH (0x0 << 6) +#define RT5640_ANC_CD_IND (0x1 << 6) +#define RT5640_ANC_CO_R_MASK (0x3f) +#define RT5640_ANC_CO_R_SFT 0 + +/* Jack Detect Control (0xbb) */ +#define RT5640_JD_MASK (0x7 << 13) +#define RT5640_JD_SFT 13 +#define RT5640_JD_DIS (0x0 << 13) +#define RT5640_JD_GPIO1 (0x1 << 13) +#define RT5640_JD_JD1_IN4P (0x2 << 13) +#define RT5640_JD_JD2_IN4N (0x3 << 13) +#define RT5640_JD_GPIO2 (0x4 << 13) +#define RT5640_JD_GPIO3 (0x5 << 13) +#define RT5640_JD_GPIO4 (0x6 << 13) +#define RT5640_JD_HP_MASK (0x1 << 11) +#define RT5640_JD_HP_SFT 11 +#define RT5640_JD_HP_DIS (0x0 << 11) +#define RT5640_JD_HP_EN (0x1 << 11) +#define RT5640_JD_HP_TRG_MASK (0x1 << 10) +#define RT5640_JD_HP_TRG_SFT 10 +#define RT5640_JD_HP_TRG_LO (0x0 << 10) +#define RT5640_JD_HP_TRG_HI (0x1 << 10) +#define RT5640_JD_SPL_MASK (0x1 << 9) +#define RT5640_JD_SPL_SFT 9 +#define RT5640_JD_SPL_DIS (0x0 << 9) +#define RT5640_JD_SPL_EN (0x1 << 9) +#define RT5640_JD_SPL_TRG_MASK (0x1 << 8) +#define RT5640_JD_SPL_TRG_SFT 8 +#define RT5640_JD_SPL_TRG_LO (0x0 << 8) +#define RT5640_JD_SPL_TRG_HI (0x1 << 8) +#define RT5640_JD_SPR_MASK (0x1 << 7) +#define RT5640_JD_SPR_SFT 7 +#define RT5640_JD_SPR_DIS (0x0 << 7) +#define RT5640_JD_SPR_EN (0x1 << 7) +#define RT5640_JD_SPR_TRG_MASK (0x1 << 6) +#define RT5640_JD_SPR_TRG_SFT 6 +#define RT5640_JD_SPR_TRG_LO (0x0 << 6) +#define RT5640_JD_SPR_TRG_HI (0x1 << 6) +#define RT5640_JD_MO_MASK (0x1 << 5) +#define RT5640_JD_MO_SFT 5 +#define RT5640_JD_MO_DIS (0x0 << 5) +#define RT5640_JD_MO_EN (0x1 << 5) +#define RT5640_JD_MO_TRG_MASK (0x1 << 4) +#define RT5640_JD_MO_TRG_SFT 4 +#define RT5640_JD_MO_TRG_LO (0x0 << 4) +#define RT5640_JD_MO_TRG_HI (0x1 << 4) +#define RT5640_JD_LO_MASK (0x1 << 3) +#define RT5640_JD_LO_SFT 3 +#define RT5640_JD_LO_DIS (0x0 << 3) +#define RT5640_JD_LO_EN (0x1 << 3) +#define RT5640_JD_LO_TRG_MASK (0x1 << 2) +#define RT5640_JD_LO_TRG_SFT 2 +#define RT5640_JD_LO_TRG_LO (0x0 << 2) +#define RT5640_JD_LO_TRG_HI (0x1 << 2) +#define RT5640_JD1_IN4P_MASK (0x1 << 1) +#define RT5640_JD1_IN4P_SFT 1 +#define RT5640_JD1_IN4P_DIS (0x0 << 1) +#define RT5640_JD1_IN4P_EN (0x1 << 1) +#define RT5640_JD2_IN4N_MASK (0x1) +#define RT5640_JD2_IN4N_SFT 0 +#define RT5640_JD2_IN4N_DIS (0x0) +#define RT5640_JD2_IN4N_EN (0x1) + +/* Jack detect for ANC (0xbc) */ +#define RT5640_ANC_DET_MASK (0x3 << 4) +#define RT5640_ANC_DET_SFT 4 +#define RT5640_ANC_DET_DIS (0x0 << 4) +#define RT5640_ANC_DET_MB1 (0x1 << 4) +#define RT5640_ANC_DET_MB2 (0x2 << 4) +#define RT5640_ANC_DET_JD (0x3 << 4) +#define RT5640_AD_TRG_MASK (0x1 << 3) +#define RT5640_AD_TRG_SFT 3 +#define RT5640_AD_TRG_LO (0x0 << 3) +#define RT5640_AD_TRG_HI (0x1 << 3) +#define RT5640_ANCM_DET_MASK (0x3 << 4) +#define RT5640_ANCM_DET_SFT 4 +#define RT5640_ANCM_DET_DIS (0x0 << 4) +#define RT5640_ANCM_DET_MB1 (0x1 << 4) +#define RT5640_ANCM_DET_MB2 (0x2 << 4) +#define RT5640_ANCM_DET_JD (0x3 << 4) +#define RT5640_AMD_TRG_MASK (0x1 << 3) +#define RT5640_AMD_TRG_SFT 3 +#define RT5640_AMD_TRG_LO (0x0 << 3) +#define RT5640_AMD_TRG_HI (0x1 << 3) + +/* IRQ Control 1 (0xbd) */ +#define RT5640_IRQ_JD_MASK (0x1 << 15) +#define RT5640_IRQ_JD_SFT 15 +#define RT5640_IRQ_JD_BP (0x0 << 15) +#define RT5640_IRQ_JD_NOR (0x1 << 15) +#define RT5640_IRQ_OT_MASK (0x1 << 14) +#define RT5640_IRQ_OT_SFT 14 +#define RT5640_IRQ_OT_BP (0x0 << 14) +#define RT5640_IRQ_OT_NOR (0x1 << 14) +#define RT5640_JD_STKY_MASK (0x1 << 13) +#define RT5640_JD_STKY_SFT 13 +#define RT5640_JD_STKY_DIS (0x0 << 13) +#define RT5640_JD_STKY_EN (0x1 << 13) +#define RT5640_OT_STKY_MASK (0x1 << 12) +#define RT5640_OT_STKY_SFT 12 +#define RT5640_OT_STKY_DIS (0x0 << 12) +#define RT5640_OT_STKY_EN (0x1 << 12) +#define RT5640_JD_P_MASK (0x1 << 11) +#define RT5640_JD_P_SFT 11 +#define RT5640_JD_P_NOR (0x0 << 11) +#define RT5640_JD_P_INV (0x1 << 11) +#define RT5640_OT_P_MASK (0x1 << 10) +#define RT5640_OT_P_SFT 10 +#define RT5640_OT_P_NOR (0x0 << 10) +#define RT5640_OT_P_INV (0x1 << 10) + +/* IRQ Control 2 (0xbe) */ +#define RT5640_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5640_IRQ_MB1_OC_SFT 15 +#define RT5640_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5640_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5640_IRQ_MB2_OC_MASK (0x1 << 14) +#define RT5640_IRQ_MB2_OC_SFT 14 +#define RT5640_IRQ_MB2_OC_BP (0x0 << 14) +#define RT5640_IRQ_MB2_OC_NOR (0x1 << 14) +#define RT5640_MB1_OC_STKY_MASK (0x1 << 11) +#define RT5640_MB1_OC_STKY_SFT 11 +#define RT5640_MB1_OC_STKY_DIS (0x0 << 11) +#define RT5640_MB1_OC_STKY_EN (0x1 << 11) +#define RT5640_MB2_OC_STKY_MASK (0x1 << 10) +#define RT5640_MB2_OC_STKY_SFT 10 +#define RT5640_MB2_OC_STKY_DIS (0x0 << 10) +#define RT5640_MB2_OC_STKY_EN (0x1 << 10) +#define RT5640_MB1_OC_P_MASK (0x1 << 7) +#define RT5640_MB1_OC_P_SFT 7 +#define RT5640_MB1_OC_P_NOR (0x0 << 7) +#define RT5640_MB1_OC_P_INV (0x1 << 7) +#define RT5640_MB2_OC_P_MASK (0x1 << 6) +#define RT5640_MB2_OC_P_SFT 6 +#define RT5640_MB2_OC_P_NOR (0x0 << 6) +#define RT5640_MB2_OC_P_INV (0x1 << 6) +#define RT5640_MB1_OC_STATUS (0x1 << 3) +#define RT5640_MB1_OC_STATUS_SFT 3 +#define RT5640_MB2_OC_STATUS (0x1 << 2) +#define RT5640_MB2_OC_STATUS_SFT 2 + +/* GPIO and Internal Status (0xbf) */ +#define RT5640_GPIO1_STATUS (0x1 << 8) +#define RT5640_GPIO2_STATUS (0x1 << 7) +#define RT5640_JD_STATUS (0x1 << 4) +#define RT5640_OVT_STATUS (0x1 << 3) +#define RT5640_CLS_D_OVCD_STATUS (0x1 << 0) + +/* GPIO Control 1 (0xc0) */ +#define RT5640_GP1_PIN_MASK (0x1 << 15) +#define RT5640_GP1_PIN_SFT 15 +#define RT5640_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5640_GP1_PIN_IRQ (0x1 << 15) +#define RT5640_GP2_PIN_MASK (0x1 << 14) +#define RT5640_GP2_PIN_SFT 14 +#define RT5640_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5640_GP2_PIN_DMIC1_SCL (0x1 << 14) +#define RT5640_GP3_PIN_MASK (0x3 << 12) +#define RT5640_GP3_PIN_SFT 12 +#define RT5640_GP3_PIN_GPIO3 (0x0 << 12) +#define RT5640_GP3_PIN_DMIC1_SDA (0x1 << 12) +#define RT5640_GP3_PIN_IRQ (0x2 << 12) +#define RT5640_GP4_PIN_MASK (0x1 << 11) +#define RT5640_GP4_PIN_SFT 11 +#define RT5640_GP4_PIN_GPIO4 (0x0 << 11) +#define RT5640_GP4_PIN_DMIC2_SDA (0x1 << 11) +#define RT5640_DP_SIG_MASK (0x1 << 10) +#define RT5640_DP_SIG_SFT 10 +#define RT5640_DP_SIG_TEST (0x0 << 10) +#define RT5640_DP_SIG_AP (0x1 << 10) +#define RT5640_GPIO_M_MASK (0x1 << 9) +#define RT5640_GPIO_M_SFT 9 +#define RT5640_GPIO_M_FLT (0x0 << 9) +#define RT5640_GPIO_M_PH (0x1 << 9) + +/* GPIO Control 3 (0xc2) */ +#define RT5640_GP4_PF_MASK (0x1 << 11) +#define RT5640_GP4_PF_SFT 11 +#define RT5640_GP4_PF_IN (0x0 << 11) +#define RT5640_GP4_PF_OUT (0x1 << 11) +#define RT5640_GP4_OUT_MASK (0x1 << 10) +#define RT5640_GP4_OUT_SFT 10 +#define RT5640_GP4_OUT_LO (0x0 << 10) +#define RT5640_GP4_OUT_HI (0x1 << 10) +#define RT5640_GP4_P_MASK (0x1 << 9) +#define RT5640_GP4_P_SFT 9 +#define RT5640_GP4_P_NOR (0x0 << 9) +#define RT5640_GP4_P_INV (0x1 << 9) +#define RT5640_GP3_PF_MASK (0x1 << 8) +#define RT5640_GP3_PF_SFT 8 +#define RT5640_GP3_PF_IN (0x0 << 8) +#define RT5640_GP3_PF_OUT (0x1 << 8) +#define RT5640_GP3_OUT_MASK (0x1 << 7) +#define RT5640_GP3_OUT_SFT 7 +#define RT5640_GP3_OUT_LO (0x0 << 7) +#define RT5640_GP3_OUT_HI (0x1 << 7) +#define RT5640_GP3_P_MASK (0x1 << 6) +#define RT5640_GP3_P_SFT 6 +#define RT5640_GP3_P_NOR (0x0 << 6) +#define RT5640_GP3_P_INV (0x1 << 6) +#define RT5640_GP2_PF_MASK (0x1 << 5) +#define RT5640_GP2_PF_SFT 5 +#define RT5640_GP2_PF_IN (0x0 << 5) +#define RT5640_GP2_PF_OUT (0x1 << 5) +#define RT5640_GP2_OUT_MASK (0x1 << 4) +#define RT5640_GP2_OUT_SFT 4 +#define RT5640_GP2_OUT_LO (0x0 << 4) +#define RT5640_GP2_OUT_HI (0x1 << 4) +#define RT5640_GP2_P_MASK (0x1 << 3) +#define RT5640_GP2_P_SFT 3 +#define RT5640_GP2_P_NOR (0x0 << 3) +#define RT5640_GP2_P_INV (0x1 << 3) +#define RT5640_GP1_PF_MASK (0x1 << 2) +#define RT5640_GP1_PF_SFT 2 +#define RT5640_GP1_PF_IN (0x0 << 2) +#define RT5640_GP1_PF_OUT (0x1 << 2) +#define RT5640_GP1_OUT_MASK (0x1 << 1) +#define RT5640_GP1_OUT_SFT 1 +#define RT5640_GP1_OUT_LO (0x0 << 1) +#define RT5640_GP1_OUT_HI (0x1 << 1) +#define RT5640_GP1_P_MASK (0x1) +#define RT5640_GP1_P_SFT 0 +#define RT5640_GP1_P_NOR (0x0) +#define RT5640_GP1_P_INV (0x1) + +/* FM34-500 Register Control 1 (0xc4) */ +#define RT5640_DSP_ADD_SFT 0 + +/* FM34-500 Register Control 2 (0xc5) */ +#define RT5640_DSP_DAT_SFT 0 + +/* FM34-500 Register Control 3 (0xc6) */ +#define RT5640_DSP_BUSY_MASK (0x1 << 15) +#define RT5640_DSP_BUSY_BIT 15 +#define RT5640_DSP_DS_MASK (0x1 << 14) +#define RT5640_DSP_DS_SFT 14 +#define RT5640_DSP_DS_FM3010 (0x1 << 14) +#define RT5640_DSP_DS_TEMP (0x1 << 14) +#define RT5640_DSP_CLK_MASK (0x3 << 12) +#define RT5640_DSP_CLK_SFT 12 +#define RT5640_DSP_CLK_384K (0x0 << 12) +#define RT5640_DSP_CLK_192K (0x1 << 12) +#define RT5640_DSP_CLK_96K (0x2 << 12) +#define RT5640_DSP_CLK_64K (0x3 << 12) +#define RT5640_DSP_PD_PIN_MASK (0x1 << 11) +#define RT5640_DSP_PD_PIN_SFT 11 +#define RT5640_DSP_PD_PIN_LO (0x0 << 11) +#define RT5640_DSP_PD_PIN_HI (0x1 << 11) +#define RT5640_DSP_RST_PIN_MASK (0x1 << 10) +#define RT5640_DSP_RST_PIN_SFT 10 +#define RT5640_DSP_RST_PIN_LO (0x0 << 10) +#define RT5640_DSP_RST_PIN_HI (0x1 << 10) +#define RT5640_DSP_R_EN (0x1 << 9) +#define RT5640_DSP_R_EN_BIT 9 +#define RT5640_DSP_W_EN (0x1 << 8) +#define RT5640_DSP_W_EN_BIT 8 +#define RT5640_DSP_CMD_MASK (0xff) +#define RT5640_DSP_CMD_SFT 0 +#define RT5640_DSP_CMD_MW (0x3B) /* Memory Write */ +#define RT5640_DSP_CMD_MR (0x37) /* Memory Read */ +#define RT5640_DSP_CMD_RR (0x60) /* Register Read */ +#define RT5640_DSP_CMD_RW (0x68) /* Register Write */ + +/* Programmable Register Array Control 1 (0xc8) */ +#define RT5640_REG_SEQ_MASK (0xf << 12) +#define RT5640_REG_SEQ_SFT 12 +#define RT5640_SEQ1_ST_MASK (0x1 << 11) /*RO*/ +#define RT5640_SEQ1_ST_SFT 11 +#define RT5640_SEQ1_ST_RUN (0x0 << 11) +#define RT5640_SEQ1_ST_FIN (0x1 << 11) +#define RT5640_SEQ2_ST_MASK (0x1 << 10) /*RO*/ +#define RT5640_SEQ2_ST_SFT 10 +#define RT5640_SEQ2_ST_RUN (0x0 << 10) +#define RT5640_SEQ2_ST_FIN (0x1 << 10) +#define RT5640_REG_LV_MASK (0x1 << 9) +#define RT5640_REG_LV_SFT 9 +#define RT5640_REG_LV_MX (0x0 << 9) +#define RT5640_REG_LV_PR (0x1 << 9) +#define RT5640_SEQ_2_PT_MASK (0x1 << 8) +#define RT5640_SEQ_2_PT_BIT 8 +#define RT5640_REG_IDX_MASK (0xff) +#define RT5640_REG_IDX_SFT 0 + +/* Programmable Register Array Control 2 (0xc9) */ +#define RT5640_REG_DAT_MASK (0xffff) +#define RT5640_REG_DAT_SFT 0 + +/* Programmable Register Array Control 3 (0xca) */ +#define RT5640_SEQ_DLY_MASK (0xff << 8) +#define RT5640_SEQ_DLY_SFT 8 +#define RT5640_PROG_MASK (0x1 << 7) +#define RT5640_PROG_SFT 7 +#define RT5640_PROG_DIS (0x0 << 7) +#define RT5640_PROG_EN (0x1 << 7) +#define RT5640_SEQ1_PT_RUN (0x1 << 6) +#define RT5640_SEQ1_PT_RUN_BIT 6 +#define RT5640_SEQ2_PT_RUN (0x1 << 5) +#define RT5640_SEQ2_PT_RUN_BIT 5 + +/* Programmable Register Array Control 4 (0xcb) */ +#define RT5640_SEQ1_START_MASK (0xf << 8) +#define RT5640_SEQ1_START_SFT 8 +#define RT5640_SEQ1_END_MASK (0xf) +#define RT5640_SEQ1_END_SFT 0 + +/* Programmable Register Array Control 5 (0xcc) */ +#define RT5640_SEQ2_START_MASK (0xf << 8) +#define RT5640_SEQ2_START_SFT 8 +#define RT5640_SEQ2_END_MASK (0xf) +#define RT5640_SEQ2_END_SFT 0 + +/* Scramble Function (0xcd) */ +#define RT5640_SCB_KEY_MASK (0xff) +#define RT5640_SCB_KEY_SFT 0 + +/* Scramble Control (0xce) */ +#define RT5640_SCB_SWAP_MASK (0x1 << 15) +#define RT5640_SCB_SWAP_SFT 15 +#define RT5640_SCB_SWAP_DIS (0x0 << 15) +#define RT5640_SCB_SWAP_EN (0x1 << 15) +#define RT5640_SCB_MASK (0x1 << 14) +#define RT5640_SCB_SFT 14 +#define RT5640_SCB_DIS (0x0 << 14) +#define RT5640_SCB_EN (0x1 << 14) + +/* Baseback Control (0xcf) */ +#define RT5640_BB_MASK (0x1 << 15) +#define RT5640_BB_SFT 15 +#define RT5640_BB_DIS (0x0 << 15) +#define RT5640_BB_EN (0x1 << 15) +#define RT5640_BB_CT_MASK (0x7 << 12) +#define RT5640_BB_CT_SFT 12 +#define RT5640_BB_CT_A (0x0 << 12) +#define RT5640_BB_CT_B (0x1 << 12) +#define RT5640_BB_CT_C (0x2 << 12) +#define RT5640_BB_CT_D (0x3 << 12) +#define RT5640_M_BB_L_MASK (0x1 << 9) +#define RT5640_M_BB_L_SFT 9 +#define RT5640_M_BB_R_MASK (0x1 << 8) +#define RT5640_M_BB_R_SFT 8 +#define RT5640_M_BB_HPF_L_MASK (0x1 << 7) +#define RT5640_M_BB_HPF_L_SFT 7 +#define RT5640_M_BB_HPF_R_MASK (0x1 << 6) +#define RT5640_M_BB_HPF_R_SFT 6 +#define RT5640_G_BB_BST_MASK (0x3f) +#define RT5640_G_BB_BST_SFT 0 + +/* MP3 Plus Control 1 (0xd0) */ +#define RT5640_M_MP3_L_MASK (0x1 << 15) +#define RT5640_M_MP3_L_SFT 15 +#define RT5640_M_MP3_R_MASK (0x1 << 14) +#define RT5640_M_MP3_R_SFT 14 +#define RT5640_M_MP3_MASK (0x1 << 13) +#define RT5640_M_MP3_SFT 13 +#define RT5640_M_MP3_DIS (0x0 << 13) +#define RT5640_M_MP3_EN (0x1 << 13) +#define RT5640_EG_MP3_MASK (0x1f << 8) +#define RT5640_EG_MP3_SFT 8 +#define RT5640_MP3_HLP_MASK (0x1 << 7) +#define RT5640_MP3_HLP_SFT 7 +#define RT5640_MP3_HLP_DIS (0x0 << 7) +#define RT5640_MP3_HLP_EN (0x1 << 7) +#define RT5640_M_MP3_ORG_L_MASK (0x1 << 6) +#define RT5640_M_MP3_ORG_L_SFT 6 +#define RT5640_M_MP3_ORG_R_MASK (0x1 << 5) +#define RT5640_M_MP3_ORG_R_SFT 5 + +/* MP3 Plus Control 2 (0xd1) */ +#define RT5640_MP3_WT_MASK (0x1 << 13) +#define RT5640_MP3_WT_SFT 13 +#define RT5640_MP3_WT_1_4 (0x0 << 13) +#define RT5640_MP3_WT_1_2 (0x1 << 13) +#define RT5640_OG_MP3_MASK (0x1f << 8) +#define RT5640_OG_MP3_SFT 8 +#define RT5640_HG_MP3_MASK (0x3f) +#define RT5640_HG_MP3_SFT 0 + +/* 3D HP Control 1 (0xd2) */ +#define RT5640_3D_CF_MASK (0x1 << 15) +#define RT5640_3D_CF_SFT 15 +#define RT5640_3D_CF_DIS (0x0 << 15) +#define RT5640_3D_CF_EN (0x1 << 15) +#define RT5640_3D_HP_MASK (0x1 << 14) +#define RT5640_3D_HP_SFT 14 +#define RT5640_3D_HP_DIS (0x0 << 14) +#define RT5640_3D_HP_EN (0x1 << 14) +#define RT5640_3D_BT_MASK (0x1 << 13) +#define RT5640_3D_BT_SFT 13 +#define RT5640_3D_BT_DIS (0x0 << 13) +#define RT5640_3D_BT_EN (0x1 << 13) +#define RT5640_3D_1F_MIX_MASK (0x3 << 11) +#define RT5640_3D_1F_MIX_SFT 11 +#define RT5640_3D_HP_M_MASK (0x1 << 10) +#define RT5640_3D_HP_M_SFT 10 +#define RT5640_3D_HP_M_SUR (0x0 << 10) +#define RT5640_3D_HP_M_FRO (0x1 << 10) +#define RT5640_M_3D_HRTF_MASK (0x1 << 9) +#define RT5640_M_3D_HRTF_SFT 9 +#define RT5640_M_3D_D2H_MASK (0x1 << 8) +#define RT5640_M_3D_D2H_SFT 8 +#define RT5640_M_3D_D2R_MASK (0x1 << 7) +#define RT5640_M_3D_D2R_SFT 7 +#define RT5640_M_3D_REVB_MASK (0x1 << 6) +#define RT5640_M_3D_REVB_SFT 6 + +/* Adjustable high pass filter control 1 (0xd3) */ +#define RT5640_2ND_HPF_MASK (0x1 << 15) +#define RT5640_2ND_HPF_SFT 15 +#define RT5640_2ND_HPF_DIS (0x0 << 15) +#define RT5640_2ND_HPF_EN (0x1 << 15) +#define RT5640_HPF_CF_L_MASK (0x7 << 12) +#define RT5640_HPF_CF_L_SFT 12 +#define RT5640_1ST_HPF_MASK (0x1 << 11) +#define RT5640_1ST_HPF_SFT 11 +#define RT5640_1ST_HPF_DIS (0x0 << 11) +#define RT5640_1ST_HPF_EN (0x1 << 11) +#define RT5640_HPF_CF_R_MASK (0x7 << 8) +#define RT5640_HPF_CF_R_SFT 8 +#define RT5640_ZD_T_MASK (0x3 << 6) +#define RT5640_ZD_T_SFT 6 +#define RT5640_ZD_F_MASK (0x3 << 4) +#define RT5640_ZD_F_SFT 4 +#define RT5640_ZD_F_IM (0x0 << 4) +#define RT5640_ZD_F_ZC_IM (0x1 << 4) +#define RT5640_ZD_F_ZC_IOD (0x2 << 4) +#define RT5640_ZD_F_UN (0x3 << 4) + +/* HP calibration control and Amp detection (0xd6) */ +#define RT5640_SI_DAC_MASK (0x1 << 11) +#define RT5640_SI_DAC_SFT 11 +#define RT5640_SI_DAC_AUTO (0x0 << 11) +#define RT5640_SI_DAC_TEST (0x1 << 11) +#define RT5640_DC_CAL_M_MASK (0x1 << 10) +#define RT5640_DC_CAL_M_SFT 10 +#define RT5640_DC_CAL_M_CAL (0x0 << 10) +#define RT5640_DC_CAL_M_NOR (0x1 << 10) +#define RT5640_DC_CAL_MASK (0x1 << 9) +#define RT5640_DC_CAL_SFT 9 +#define RT5640_DC_CAL_DIS (0x0 << 9) +#define RT5640_DC_CAL_EN (0x1 << 9) +#define RT5640_HPD_RCV_MASK (0x7 << 6) +#define RT5640_HPD_RCV_SFT 6 +#define RT5640_HPD_PS_MASK (0x1 << 5) +#define RT5640_HPD_PS_SFT 5 +#define RT5640_HPD_PS_DIS (0x0 << 5) +#define RT5640_HPD_PS_EN (0x1 << 5) +#define RT5640_CAL_M_MASK (0x1 << 4) +#define RT5640_CAL_M_SFT 4 +#define RT5640_CAL_M_DEP (0x0 << 4) +#define RT5640_CAL_M_CAL (0x1 << 4) +#define RT5640_CAL_MASK (0x1 << 3) +#define RT5640_CAL_SFT 3 +#define RT5640_CAL_DIS (0x0 << 3) +#define RT5640_CAL_EN (0x1 << 3) +#define RT5640_CAL_TEST_MASK (0x1 << 2) +#define RT5640_CAL_TEST_SFT 2 +#define RT5640_CAL_TEST_DIS (0x0 << 2) +#define RT5640_CAL_TEST_EN (0x1 << 2) +#define RT5640_CAL_P_MASK (0x3) +#define RT5640_CAL_P_SFT 0 +#define RT5640_CAL_P_NONE (0x0) +#define RT5640_CAL_P_CAL (0x1) +#define RT5640_CAL_P_DAC_CAL (0x2) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5640_SV_MASK (0x1 << 15) +#define RT5640_SV_SFT 15 +#define RT5640_SV_DIS (0x0 << 15) +#define RT5640_SV_EN (0x1 << 15) +#define RT5640_SPO_SV_MASK (0x1 << 14) +#define RT5640_SPO_SV_SFT 14 +#define RT5640_SPO_SV_DIS (0x0 << 14) +#define RT5640_SPO_SV_EN (0x1 << 14) +#define RT5640_OUT_SV_MASK (0x1 << 13) +#define RT5640_OUT_SV_SFT 13 +#define RT5640_OUT_SV_DIS (0x0 << 13) +#define RT5640_OUT_SV_EN (0x1 << 13) +#define RT5640_HP_SV_MASK (0x1 << 12) +#define RT5640_HP_SV_SFT 12 +#define RT5640_HP_SV_DIS (0x0 << 12) +#define RT5640_HP_SV_EN (0x1 << 12) +#define RT5640_ZCD_DIG_MASK (0x1 << 11) +#define RT5640_ZCD_DIG_SFT 11 +#define RT5640_ZCD_DIG_DIS (0x0 << 11) +#define RT5640_ZCD_DIG_EN (0x1 << 11) +#define RT5640_ZCD_MASK (0x1 << 10) +#define RT5640_ZCD_SFT 10 +#define RT5640_ZCD_PD (0x0 << 10) +#define RT5640_ZCD_PU (0x1 << 10) +#define RT5640_M_ZCD_MASK (0x3f << 4) +#define RT5640_M_ZCD_SFT 4 +#define RT5640_M_ZCD_RM_L (0x1 << 9) +#define RT5640_M_ZCD_RM_R (0x1 << 8) +#define RT5640_M_ZCD_SM_L (0x1 << 7) +#define RT5640_M_ZCD_SM_R (0x1 << 6) +#define RT5640_M_ZCD_OM_L (0x1 << 5) +#define RT5640_M_ZCD_OM_R (0x1 << 4) +#define RT5640_SV_DLY_MASK (0xf) +#define RT5640_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5640_ZCD_HP_MASK (0x1 << 15) +#define RT5640_ZCD_HP_SFT 15 +#define RT5640_ZCD_HP_DIS (0x0 << 15) +#define RT5640_ZCD_HP_EN (0x1 << 15) + +/* General Control 1 (0xfa) */ +#define RT5640_EN_LOUT_DF (0x1 << 14) +#define RT5640_EN_LOUT_DF_SFT 14 +#define RT5640_M_MONO_ADC_L (0x1 << 13) +#define RT5640_M_MONO_ADC_L_SFT 13 +#define RT5640_M_MONO_ADC_R (0x1 << 12) +#define RT5640_M_MONO_ADC_R_SFT 12 +#define RT5640_MCLK_DET (0x1 << 11) + +/* General Control 1 (0xfb) */ +#define RT5640_IRQ_JD2_MASK (0x1 << 12) +#define RT5640_IRQ_JD2_SFT 12 +#define RT5640_IRQ_JD2_BP (0x0 << 12) +#define RT5640_IRQ_JD2_NOR (0x1 << 12) +#define RT5640_JD2_P_MASK (0x1 << 10) +#define RT5640_JD2_P_SFT 10 +#define RT5640_JD2_P_NOR (0x0 << 10) +#define RT5640_JD2_P_INV (0x1 << 10) +#define RT5640_JD2_MASK (0x1 << 8) +#define RT5640_JD2_SFT 8 +#define RT5640_JD2_DIS (0x0 << 8) +#define RT5640_JD2_EN (0x1 << 8) + +/* Codec Private Register definition */ + +/* MIC Over current threshold scale factor (0x15) */ +#define RT5640_MIC_OVCD_SF_MASK (0x3 << 8) +#define RT5640_MIC_OVCD_SF_SFT 8 +#define RT5640_MIC_OVCD_SF_0P5 (0x0 << 8) +#define RT5640_MIC_OVCD_SF_0P75 (0x1 << 8) +#define RT5640_MIC_OVCD_SF_1P0 (0x2 << 8) +#define RT5640_MIC_OVCD_SF_1P5 (0x3 << 8) + +/* 3D Speaker Control (0x63) */ +#define RT5640_3D_SPK_MASK (0x1 << 15) +#define RT5640_3D_SPK_SFT 15 +#define RT5640_3D_SPK_DIS (0x0 << 15) +#define RT5640_3D_SPK_EN (0x1 << 15) +#define RT5640_3D_SPK_M_MASK (0x3 << 13) +#define RT5640_3D_SPK_M_SFT 13 +#define RT5640_3D_SPK_CG_MASK (0x1f << 8) +#define RT5640_3D_SPK_CG_SFT 8 +#define RT5640_3D_SPK_SG_MASK (0x1f) +#define RT5640_3D_SPK_SG_SFT 0 + +/* Wind Noise Detection Control 1 (0x6c) */ +#define RT5640_WND_MASK (0x1 << 15) +#define RT5640_WND_SFT 15 +#define RT5640_WND_DIS (0x0 << 15) +#define RT5640_WND_EN (0x1 << 15) + +/* Wind Noise Detection Control 2 (0x6d) */ +#define RT5640_WND_FC_NW_MASK (0x3f << 10) +#define RT5640_WND_FC_NW_SFT 10 +#define RT5640_WND_FC_WK_MASK (0x3f << 4) +#define RT5640_WND_FC_WK_SFT 4 + +/* Wind Noise Detection Control 3 (0x6e) */ +#define RT5640_HPF_FC_MASK (0x3f << 6) +#define RT5640_HPF_FC_SFT 6 +#define RT5640_WND_FC_ST_MASK (0x3f) +#define RT5640_WND_FC_ST_SFT 0 + +/* Wind Noise Detection Control 4 (0x6f) */ +#define RT5640_WND_TH_LO_MASK (0x3ff) +#define RT5640_WND_TH_LO_SFT 0 + +/* Wind Noise Detection Control 5 (0x70) */ +#define RT5640_WND_TH_HI_MASK (0x3ff) +#define RT5640_WND_TH_HI_SFT 0 + +/* Wind Noise Detection Control 8 (0x73) */ +#define RT5640_WND_WIND_MASK (0x1 << 13) /* Read-Only */ +#define RT5640_WND_WIND_SFT 13 +#define RT5640_WND_STRONG_MASK (0x1 << 12) /* Read-Only */ +#define RT5640_WND_STRONG_SFT 12 +enum { + RT5640_NO_WIND, + RT5640_BREEZE, + RT5640_STORM, +}; + +/* Dipole Speaker Interface (0x75) */ +#define RT5640_DP_ATT_MASK (0x3 << 14) +#define RT5640_DP_ATT_SFT 14 +#define RT5640_DP_SPK_MASK (0x1 << 10) +#define RT5640_DP_SPK_SFT 10 +#define RT5640_DP_SPK_DIS (0x0 << 10) +#define RT5640_DP_SPK_EN (0x1 << 10) + +/* EQ Pre Volume Control (0xb3) */ +#define RT5640_EQ_PRE_VOL_MASK (0xffff) +#define RT5640_EQ_PRE_VOL_SFT 0 + +/* EQ Post Volume Control (0xb4) */ +#define RT5640_EQ_PST_VOL_MASK (0xffff) +#define RT5640_EQ_PST_VOL_SFT 0 + +#define RT5640_NO_JACK BIT(0) +#define RT5640_HEADSET_DET BIT(1) +#define RT5640_HEADPHO_DET BIT(2) + +/* System Clock Source */ +#define RT5640_SCLK_S_MCLK 0 +#define RT5640_SCLK_S_PLL1 1 +#define RT5640_SCLK_S_PLL1_TK 2 +#define RT5640_SCLK_S_RCCLK 3 + +/* PLL1 Source */ +#define RT5640_PLL1_S_MCLK 0 +#define RT5640_PLL1_S_BCLK1 1 +#define RT5640_PLL1_S_BCLK2 2 +#define RT5640_PLL1_S_BCLK3 3 + + +enum { + RT5640_AIF1, + RT5640_AIF2, + RT5640_AIF3, + RT5640_AIFS, +}; + +enum { + RT5640_U_IF1 = 0x1, + RT5640_U_IF2 = 0x2, + RT5640_U_IF3 = 0x4, +}; + +enum { + RT5640_IF_123, + RT5640_IF_132, + RT5640_IF_312, + RT5640_IF_321, + RT5640_IF_231, + RT5640_IF_213, + RT5640_IF_113, + RT5640_IF_223, + RT5640_IF_ALL, +}; + +enum { + RT5640_DMIC_DIS, + RT5640_DMIC1, + RT5640_DMIC2, +}; + +/* filter mask */ +enum { + RT5640_DA_STEREO_FILTER = 0x1, + RT5640_DA_MONO_L_FILTER = (0x1 << 1), + RT5640_DA_MONO_R_FILTER = (0x1 << 2), + RT5640_AD_STEREO_FILTER = (0x1 << 3), + RT5640_AD_MONO_L_FILTER = (0x1 << 4), + RT5640_AD_MONO_R_FILTER = (0x1 << 5), +}; + +struct rt5640_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct clk *mclk; + + struct gpio_desc *ldo1_en; /* GPIO for LDO1_EN */ + int irq; + int jd_gpio_irq; + int sysclk; + int sysclk_src; + int lrck[RT5640_AIFS]; + int bclk[RT5640_AIFS]; + int master[RT5640_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + bool hp_mute; + bool asrc_en; + bool irq_requested; + bool jd_gpio_irq_requested; + + /* Jack and button detect data */ + bool ovcd_irq_enabled; + bool pressed; + bool press_reported; + int press_count; + int release_count; + int poll_count; + struct delayed_work bp_work; + struct delayed_work jack_work; + struct snd_soc_jack *jack; + struct gpio_desc *jd_gpio; + unsigned int jd_src; + bool jd_inverted; + unsigned int ovcd_th; + unsigned int ovcd_sf; + bool use_platform_clock; +}; + +struct rt5640_set_jack_data { + int codec_irq_override; + struct gpio_desc *jd_gpio; + bool use_platform_clock; +}; + +int rt5640_dmic_enable(struct snd_soc_component *component, + bool dmic1_data_pin, bool dmic2_data_pin); +int rt5640_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +void rt5640_set_ovcd_params(struct snd_soc_component *component); +void rt5640_enable_micbias1_for_ovcd(struct snd_soc_component *component); +void rt5640_disable_micbias1_for_ovcd(struct snd_soc_component *component); +int rt5640_detect_headset(struct snd_soc_component *component, struct gpio_desc *hp_det_gpio); + +#endif diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c new file mode 100644 index 0000000000..edcb85bd8e --- /dev/null +++ b/sound/soc/codecs/rt5645.c @@ -0,0 +1,4261 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5645.c -- RT5645 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Bard Liao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5645.h" + +#define QUIRK_INV_JD1_1(q) ((q) & 1) +#define QUIRK_LEVEL_IRQ(q) (((q) >> 1) & 1) +#define QUIRK_IN2_DIFF(q) (((q) >> 2) & 1) +#define QUIRK_INV_HP_POL(q) (((q) >> 3) & 1) +#define QUIRK_JD_MODE(q) (((q) >> 4) & 7) +#define QUIRK_DMIC1_DATA_PIN(q) (((q) >> 8) & 3) +#define QUIRK_DMIC2_DATA_PIN(q) (((q) >> 12) & 3) + +static unsigned int quirk = -1; +module_param(quirk, uint, 0444); +MODULE_PARM_DESC(quirk, "RT5645 pdata quirk override"); + +static const struct acpi_gpio_mapping *cht_rt5645_gpios; + +#define RT5645_DEVICE_ID 0x6308 +#define RT5650_DEVICE_ID 0x6419 + +#define RT5645_PR_RANGE_BASE (0xff + 1) +#define RT5645_PR_SPACING 0x100 + +#define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING)) + +#define RT5645_HWEQ_NUM 57 + +#define TIME_TO_POWER_MS 400 + +static const struct regmap_range_cfg rt5645_ranges[] = { + { + .name = "PR", + .range_min = RT5645_PR_BASE, + .range_max = RT5645_PR_BASE + 0xf8, + .selector_reg = RT5645_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5645_PRIV_DATA, + .window_len = 0x1, + }, +}; + +static const struct reg_sequence init_list[] = { + {RT5645_PR_BASE + 0x3d, 0x3600}, + {RT5645_PR_BASE + 0x1c, 0xfd70}, + {RT5645_PR_BASE + 0x20, 0x611f}, + {RT5645_PR_BASE + 0x21, 0x4040}, + {RT5645_PR_BASE + 0x23, 0x0004}, + {RT5645_ASRC_4, 0x0120}, +}; + +static const struct reg_sequence rt5650_init_list[] = { + {0xf6, 0x0100}, + {RT5645_PWR_ANLG1, 0x02}, +}; + +static const struct reg_default rt5645_reg[] = { + { 0x00, 0x0000 }, + { 0x01, 0xc8c8 }, + { 0x02, 0xc8c8 }, + { 0x03, 0xc8c8 }, + { 0x0a, 0x0002 }, + { 0x0b, 0x2827 }, + { 0x0c, 0xe000 }, + { 0x0d, 0x0000 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x14, 0x3333 }, + { 0x16, 0x4b00 }, + { 0x18, 0x018b }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0001 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x20, 0x0000 }, + { 0x27, 0x7060 }, + { 0x28, 0x7070 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5656 }, + { 0x2b, 0x5454 }, + { 0x2c, 0xaaa0 }, + { 0x2d, 0x0000 }, + { 0x2f, 0x1002 }, + { 0x31, 0x5000 }, + { 0x32, 0x0000 }, + { 0x33, 0x0000 }, + { 0x34, 0x0000 }, + { 0x35, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x3f, 0x0000 }, + { 0x40, 0x001f }, + { 0x41, 0x0000 }, + { 0x42, 0x001f }, + { 0x45, 0x6000 }, + { 0x46, 0x003e }, + { 0x47, 0x003e }, + { 0x48, 0xf807 }, + { 0x4a, 0x0004 }, + { 0x4d, 0x0000 }, + { 0x4e, 0x0000 }, + { 0x4f, 0x01ff }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x01ff }, + { 0x53, 0xf000 }, + { 0x56, 0x0111 }, + { 0x57, 0x0064 }, + { 0x58, 0xef0e }, + { 0x59, 0xf0f0 }, + { 0x5a, 0xef0e }, + { 0x5b, 0xf0f0 }, + { 0x5c, 0xef0e }, + { 0x5d, 0xf0f0 }, + { 0x5e, 0xf000 }, + { 0x5f, 0x0000 }, + { 0x61, 0x0300 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c2 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x6a, 0x0000 }, + { 0x6c, 0x0aaa }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x72, 0x8000 }, + { 0x73, 0x7770 }, + { 0x74, 0x3e00 }, + { 0x75, 0x2409 }, + { 0x76, 0x000a }, + { 0x77, 0x0c00 }, + { 0x78, 0x0000 }, + { 0x79, 0x0123 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0000 }, + { 0x84, 0x0000 }, + { 0x85, 0x0000 }, + { 0x8a, 0x0120 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0646 }, + { 0x91, 0x0c06 }, + { 0x93, 0x0000 }, + { 0x94, 0x0200 }, + { 0x95, 0x0000 }, + { 0x9a, 0x2184 }, + { 0x9b, 0x010a }, + { 0x9c, 0x0aea }, + { 0x9d, 0x000c }, + { 0x9e, 0x0400 }, + { 0xa0, 0xa0a8 }, + { 0xa1, 0x0059 }, + { 0xa2, 0x0001 }, + { 0xae, 0x6000 }, + { 0xaf, 0x0000 }, + { 0xb0, 0x6000 }, + { 0xb1, 0x0000 }, + { 0xb2, 0x0000 }, + { 0xb3, 0x001f }, + { 0xb4, 0x020c }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x3100 }, + { 0xc0, 0x0000 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xc3, 0x2000 }, + { 0xcd, 0x0000 }, + { 0xce, 0x0000 }, + { 0xcf, 0x1813 }, + { 0xd0, 0x0690 }, + { 0xd1, 0x1c17 }, + { 0xd3, 0xb320 }, + { 0xd4, 0x0000 }, + { 0xd6, 0x0400 }, + { 0xd9, 0x0809 }, + { 0xda, 0x0000 }, + { 0xdb, 0x0003 }, + { 0xdc, 0x0049 }, + { 0xdd, 0x001b }, + { 0xdf, 0x0008 }, + { 0xe0, 0x4000 }, + { 0xe6, 0x8000 }, + { 0xe7, 0x0200 }, + { 0xec, 0xb300 }, + { 0xed, 0x0000 }, + { 0xf0, 0x001f }, + { 0xf1, 0x020c }, + { 0xf2, 0x1f00 }, + { 0xf3, 0x0000 }, + { 0xf4, 0x4000 }, + { 0xf8, 0x0000 }, + { 0xf9, 0x0000 }, + { 0xfa, 0x2060 }, + { 0xfb, 0x4040 }, + { 0xfc, 0x0000 }, + { 0xfd, 0x0002 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6308 }, +}; + +static const struct reg_default rt5650_reg[] = { + { 0x00, 0x0000 }, + { 0x01, 0xc8c8 }, + { 0x02, 0xc8c8 }, + { 0x03, 0xc8c8 }, + { 0x0a, 0x0002 }, + { 0x0b, 0x2827 }, + { 0x0c, 0xe000 }, + { 0x0d, 0x0000 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x14, 0x3333 }, + { 0x16, 0x4b00 }, + { 0x18, 0x018b }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0001 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x20, 0x0000 }, + { 0x27, 0x7060 }, + { 0x28, 0x7070 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5656 }, + { 0x2b, 0x5454 }, + { 0x2c, 0xaaa0 }, + { 0x2d, 0x0000 }, + { 0x2f, 0x5002 }, + { 0x31, 0x5000 }, + { 0x32, 0x0000 }, + { 0x33, 0x0000 }, + { 0x34, 0x0000 }, + { 0x35, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x3f, 0x0000 }, + { 0x40, 0x001f }, + { 0x41, 0x0000 }, + { 0x42, 0x001f }, + { 0x45, 0x6000 }, + { 0x46, 0x003e }, + { 0x47, 0x003e }, + { 0x48, 0xf807 }, + { 0x4a, 0x0004 }, + { 0x4d, 0x0000 }, + { 0x4e, 0x0000 }, + { 0x4f, 0x01ff }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x01ff }, + { 0x53, 0xf000 }, + { 0x56, 0x0111 }, + { 0x57, 0x0064 }, + { 0x58, 0xef0e }, + { 0x59, 0xf0f0 }, + { 0x5a, 0xef0e }, + { 0x5b, 0xf0f0 }, + { 0x5c, 0xef0e }, + { 0x5d, 0xf0f0 }, + { 0x5e, 0xf000 }, + { 0x5f, 0x0000 }, + { 0x61, 0x0300 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c2 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x6a, 0x0000 }, + { 0x6c, 0x0aaa }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x72, 0x8000 }, + { 0x73, 0x7770 }, + { 0x74, 0x3e00 }, + { 0x75, 0x2409 }, + { 0x76, 0x000a }, + { 0x77, 0x0c00 }, + { 0x78, 0x0000 }, + { 0x79, 0x0123 }, + { 0x7a, 0x0123 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0000 }, + { 0x84, 0x0000 }, + { 0x85, 0x0000 }, + { 0x8a, 0x0120 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0646 }, + { 0x91, 0x0c06 }, + { 0x93, 0x0000 }, + { 0x94, 0x0200 }, + { 0x95, 0x0000 }, + { 0x9a, 0x2184 }, + { 0x9b, 0x010a }, + { 0x9c, 0x0aea }, + { 0x9d, 0x000c }, + { 0x9e, 0x0400 }, + { 0xa0, 0xa0a8 }, + { 0xa1, 0x0059 }, + { 0xa2, 0x0001 }, + { 0xae, 0x6000 }, + { 0xaf, 0x0000 }, + { 0xb0, 0x6000 }, + { 0xb1, 0x0000 }, + { 0xb2, 0x0000 }, + { 0xb3, 0x001f }, + { 0xb4, 0x020c }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x3100 }, + { 0xc0, 0x0000 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xc3, 0x2000 }, + { 0xcd, 0x0000 }, + { 0xce, 0x0000 }, + { 0xcf, 0x1813 }, + { 0xd0, 0x0690 }, + { 0xd1, 0x1c17 }, + { 0xd3, 0xb320 }, + { 0xd4, 0x0000 }, + { 0xd6, 0x0400 }, + { 0xd9, 0x0809 }, + { 0xda, 0x0000 }, + { 0xdb, 0x0003 }, + { 0xdc, 0x0049 }, + { 0xdd, 0x001b }, + { 0xdf, 0x0008 }, + { 0xe0, 0x4000 }, + { 0xe6, 0x8000 }, + { 0xe7, 0x0200 }, + { 0xec, 0xb300 }, + { 0xed, 0x0000 }, + { 0xf0, 0x001f }, + { 0xf1, 0x020c }, + { 0xf2, 0x1f00 }, + { 0xf3, 0x0000 }, + { 0xf4, 0x4000 }, + { 0xf8, 0x0000 }, + { 0xf9, 0x0000 }, + { 0xfa, 0x2060 }, + { 0xfb, 0x4040 }, + { 0xfc, 0x0000 }, + { 0xfd, 0x0002 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6308 }, +}; + +struct rt5645_eq_param_s { + unsigned short reg; + unsigned short val; +}; + +struct rt5645_eq_param_s_be16 { + __be16 reg; + __be16 val; +}; + +static const char *const rt5645_supply_names[] = { + "avdd", + "cpvdd", +}; + +struct rt5645_platform_data { + /* IN2 can optionally be differential */ + bool in2_diff; + + unsigned int dmic1_data_pin; + /* 0 = IN2N; 1 = GPIO5; 2 = GPIO11 */ + unsigned int dmic2_data_pin; + /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */ + + unsigned int jd_mode; + /* Use level triggered irq */ + bool level_trigger_irq; + /* Invert JD1_1 status polarity */ + bool inv_jd1_1; + /* Invert HP detect status polarity */ + bool inv_hp_pol; + + /* Value to assign to snd_soc_card.long_name */ + const char *long_name; + + /* Some (package) variants have the headset-mic pin not-connected */ + bool no_headset_mic; +}; + +struct rt5645_priv { + struct snd_soc_component *component; + struct rt5645_platform_data pdata; + struct regmap *regmap; + struct i2c_client *i2c; + struct gpio_desc *gpiod_hp_det; + struct snd_soc_jack *hp_jack; + struct snd_soc_jack *mic_jack; + struct snd_soc_jack *btn_jack; + struct delayed_work jack_detect_work, rcclock_work; + struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)]; + struct rt5645_eq_param_s *eq_param; + struct timer_list btn_check_timer; + struct mutex jd_mutex; + + int codec_type; + int sysclk; + int sysclk_src; + int lrck[RT5645_AIFS]; + int bclk[RT5645_AIFS]; + int master[RT5645_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; + bool en_button_func; + int v_id; +}; + +static int rt5645_reset(struct snd_soc_component *component) +{ + return snd_soc_component_write(component, RT5645_RESET, 0); +} + +static bool rt5645_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5645_ranges); i++) { + if (reg >= rt5645_ranges[i].range_min && + reg <= rt5645_ranges[i].range_max) { + return true; + } + } + + switch (reg) { + case RT5645_RESET: + case RT5645_PRIV_INDEX: + case RT5645_PRIV_DATA: + case RT5645_IN1_CTRL1: + case RT5645_IN1_CTRL2: + case RT5645_IN1_CTRL3: + case RT5645_A_JD_CTRL1: + case RT5645_ADC_EQ_CTRL1: + case RT5645_EQ_CTRL1: + case RT5645_ALC_CTRL_1: + case RT5645_IRQ_CTRL2: + case RT5645_IRQ_CTRL3: + case RT5645_INT_IRQ_ST: + case RT5645_IL_CMD: + case RT5650_4BTN_IL_CMD1: + case RT5645_VENDOR_ID: + case RT5645_VENDOR_ID1: + case RT5645_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool rt5645_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5645_ranges); i++) { + if (reg >= rt5645_ranges[i].range_min && + reg <= rt5645_ranges[i].range_max) { + return true; + } + } + + switch (reg) { + case RT5645_RESET: + case RT5645_SPK_VOL: + case RT5645_HP_VOL: + case RT5645_LOUT1: + case RT5645_IN1_CTRL1: + case RT5645_IN1_CTRL2: + case RT5645_IN1_CTRL3: + case RT5645_IN2_CTRL: + case RT5645_INL1_INR1_VOL: + case RT5645_SPK_FUNC_LIM: + case RT5645_ADJ_HPF_CTRL: + case RT5645_DAC1_DIG_VOL: + case RT5645_DAC2_DIG_VOL: + case RT5645_DAC_CTRL: + case RT5645_STO1_ADC_DIG_VOL: + case RT5645_MONO_ADC_DIG_VOL: + case RT5645_ADC_BST_VOL1: + case RT5645_ADC_BST_VOL2: + case RT5645_STO1_ADC_MIXER: + case RT5645_MONO_ADC_MIXER: + case RT5645_AD_DA_MIXER: + case RT5645_STO_DAC_MIXER: + case RT5645_MONO_DAC_MIXER: + case RT5645_DIG_MIXER: + case RT5650_A_DAC_SOUR: + case RT5645_DIG_INF1_DATA: + case RT5645_PDM_OUT_CTRL: + case RT5645_REC_L1_MIXER: + case RT5645_REC_L2_MIXER: + case RT5645_REC_R1_MIXER: + case RT5645_REC_R2_MIXER: + case RT5645_HPMIXL_CTRL: + case RT5645_HPOMIXL_CTRL: + case RT5645_HPMIXR_CTRL: + case RT5645_HPOMIXR_CTRL: + case RT5645_HPO_MIXER: + case RT5645_SPK_L_MIXER: + case RT5645_SPK_R_MIXER: + case RT5645_SPO_MIXER: + case RT5645_SPO_CLSD_RATIO: + case RT5645_OUT_L1_MIXER: + case RT5645_OUT_R1_MIXER: + case RT5645_OUT_L_GAIN1: + case RT5645_OUT_L_GAIN2: + case RT5645_OUT_R_GAIN1: + case RT5645_OUT_R_GAIN2: + case RT5645_LOUT_MIXER: + case RT5645_HAPTIC_CTRL1: + case RT5645_HAPTIC_CTRL2: + case RT5645_HAPTIC_CTRL3: + case RT5645_HAPTIC_CTRL4: + case RT5645_HAPTIC_CTRL5: + case RT5645_HAPTIC_CTRL6: + case RT5645_HAPTIC_CTRL7: + case RT5645_HAPTIC_CTRL8: + case RT5645_HAPTIC_CTRL9: + case RT5645_HAPTIC_CTRL10: + case RT5645_PWR_DIG1: + case RT5645_PWR_DIG2: + case RT5645_PWR_ANLG1: + case RT5645_PWR_ANLG2: + case RT5645_PWR_MIXER: + case RT5645_PWR_VOL: + case RT5645_PRIV_INDEX: + case RT5645_PRIV_DATA: + case RT5645_I2S1_SDP: + case RT5645_I2S2_SDP: + case RT5645_ADDA_CLK1: + case RT5645_ADDA_CLK2: + case RT5645_DMIC_CTRL1: + case RT5645_DMIC_CTRL2: + case RT5645_TDM_CTRL_1: + case RT5645_TDM_CTRL_2: + case RT5645_TDM_CTRL_3: + case RT5650_TDM_CTRL_4: + case RT5645_GLB_CLK: + case RT5645_PLL_CTRL1: + case RT5645_PLL_CTRL2: + case RT5645_ASRC_1: + case RT5645_ASRC_2: + case RT5645_ASRC_3: + case RT5645_ASRC_4: + case RT5645_DEPOP_M1: + case RT5645_DEPOP_M2: + case RT5645_DEPOP_M3: + case RT5645_CHARGE_PUMP: + case RT5645_MICBIAS: + case RT5645_A_JD_CTRL1: + case RT5645_VAD_CTRL4: + case RT5645_CLSD_OUT_CTRL: + case RT5645_ADC_EQ_CTRL1: + case RT5645_ADC_EQ_CTRL2: + case RT5645_EQ_CTRL1: + case RT5645_EQ_CTRL2: + case RT5645_ALC_CTRL_1: + case RT5645_ALC_CTRL_2: + case RT5645_ALC_CTRL_3: + case RT5645_ALC_CTRL_4: + case RT5645_ALC_CTRL_5: + case RT5645_JD_CTRL: + case RT5645_IRQ_CTRL1: + case RT5645_IRQ_CTRL2: + case RT5645_IRQ_CTRL3: + case RT5645_INT_IRQ_ST: + case RT5645_GPIO_CTRL1: + case RT5645_GPIO_CTRL2: + case RT5645_GPIO_CTRL3: + case RT5645_BASS_BACK: + case RT5645_MP3_PLUS1: + case RT5645_MP3_PLUS2: + case RT5645_ADJ_HPF1: + case RT5645_ADJ_HPF2: + case RT5645_HP_CALIB_AMP_DET: + case RT5645_SV_ZCD1: + case RT5645_SV_ZCD2: + case RT5645_IL_CMD: + case RT5645_IL_CMD2: + case RT5645_IL_CMD3: + case RT5650_4BTN_IL_CMD1: + case RT5650_4BTN_IL_CMD2: + case RT5645_DRC1_HL_CTRL1: + case RT5645_DRC2_HL_CTRL1: + case RT5645_ADC_MONO_HP_CTRL1: + case RT5645_ADC_MONO_HP_CTRL2: + case RT5645_DRC2_CTRL1: + case RT5645_DRC2_CTRL2: + case RT5645_DRC2_CTRL3: + case RT5645_DRC2_CTRL4: + case RT5645_DRC2_CTRL5: + case RT5645_JD_CTRL3: + case RT5645_JD_CTRL4: + case RT5645_GEN_CTRL1: + case RT5645_GEN_CTRL2: + case RT5645_GEN_CTRL3: + case RT5645_VENDOR_ID: + case RT5645_VENDOR_ID1: + case RT5645_VENDOR_ID2: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* {-6, -4.5, -3, -1.5, 0, 0.82, 1.58, 2.28} dB */ +static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv, + 0, 4, TLV_DB_SCALE_ITEM(-600, 150, 0), + 5, 5, TLV_DB_SCALE_ITEM(82, 0, 0), + 6, 6, TLV_DB_SCALE_ITEM(158, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(228, 0, 0) +); + +static int rt5645_hweq_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s); + + return 0; +} + +static int rt5645_hweq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + struct rt5645_eq_param_s_be16 *eq_param = + (struct rt5645_eq_param_s_be16 *)ucontrol->value.bytes.data; + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + eq_param[i].reg = cpu_to_be16(rt5645->eq_param[i].reg); + eq_param[i].val = cpu_to_be16(rt5645->eq_param[i].val); + } + + return 0; +} + +static bool rt5645_validate_hweq(unsigned short reg) +{ + if ((reg >= 0x1a4 && reg <= 0x1cd) || (reg >= 0x1e5 && reg <= 0x1f8) || + (reg == RT5645_EQ_CTRL2)) + return true; + + return false; +} + +static int rt5645_hweq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + struct rt5645_eq_param_s_be16 *eq_param = + (struct rt5645_eq_param_s_be16 *)ucontrol->value.bytes.data; + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + rt5645->eq_param[i].reg = be16_to_cpu(eq_param[i].reg); + rt5645->eq_param[i].val = be16_to_cpu(eq_param[i].val); + } + + /* The final setting of the table should be RT5645_EQ_CTRL2 */ + for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) { + if (rt5645->eq_param[i].reg == 0) + continue; + else if (rt5645->eq_param[i].reg != RT5645_EQ_CTRL2) + return 0; + else + break; + } + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + if (!rt5645_validate_hweq(rt5645->eq_param[i].reg) && + rt5645->eq_param[i].reg != 0) + return 0; + else if (rt5645->eq_param[i].reg == 0) + break; + } + + return 0; +} + +#define RT5645_HWEQ(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = rt5645_hweq_info, \ + .get = rt5645_hweq_get, \ + .put = rt5645_hweq_put \ +} + +static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + int ret; + + regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, + RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PU); + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + mod_delayed_work(system_power_efficient_wq, &rt5645->rcclock_work, + msecs_to_jiffies(200)); + + return ret; +} + +static const char * const rt5645_dac1_vol_ctrl_mode_text[] = { + "immediately", "zero crossing", "soft ramp" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_dac1_vol_ctrl_mode, RT5645_PR_BASE, + RT5645_DA1_ZDET_SFT, rt5645_dac1_vol_ctrl_mode_text); + +static const struct snd_kcontrol_new rt5645_snd_controls[] = { + /* Speaker Output Volume */ + SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL, + RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), + SOC_DOUBLE_EXT_TLV("Speaker Playback Volume", RT5645_SPK_VOL, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, snd_soc_get_volsw, + rt5645_spk_put_volsw, out_vol_tlv), + + /* ClassD modulator Speaker Gain Ratio */ + SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO, + RT5645_SPK_G_CLSD_SFT, 7, 0, spk_clsd_tlv), + + /* Headphone Output Volume */ + SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL, + RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("Headphone Playback Volume", RT5645_HP_VOL, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* OUTPUT Control */ + SOC_DOUBLE("OUT Playback Switch", RT5645_LOUT1, + RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("OUT Channel Switch", RT5645_LOUT1, + RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("OUT Playback Volume", RT5645_LOUT1, + RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE("DAC2 Playback Switch", RT5645_DAC_CTRL, + RT5645_M_DAC_L2_VOL_SFT, RT5645_M_DAC_R2_VOL_SFT, 1, 1), + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5645_DAC1_DIG_VOL, + RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 87, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5645_DAC2_DIG_VOL, + RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 87, 0, dac_vol_tlv), + + /* IN1/IN2 Control */ + SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1, + RT5645_BST_SFT1, 12, 0, bst_tlv), + SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL, + RT5645_BST_SFT2, 8, 0, bst_tlv), + + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5645_INL1_INR1_VOL, + RT5645_INL_VOL_SFT, RT5645_INR_VOL_SFT, 31, 1, in_vol_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5645_STO1_ADC_DIG_VOL, + RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5645_STO1_ADC_DIG_VOL, + RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv), + SOC_DOUBLE("Mono ADC Capture Switch", RT5645_MONO_ADC_DIG_VOL, + RT5645_L_MUTE_SFT, RT5645_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5645_MONO_ADC_DIG_VOL, + RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("ADC Boost Capture Volume", RT5645_ADC_BST_VOL1, + RT5645_STO1_ADC_L_BST_SFT, RT5645_STO1_ADC_R_BST_SFT, 3, 0, + adc_bst_tlv), + SOC_DOUBLE_TLV("Mono ADC Boost Capture Volume", RT5645_ADC_BST_VOL2, + RT5645_MONO_ADC_L_BST_SFT, RT5645_MONO_ADC_R_BST_SFT, 3, 0, + adc_bst_tlv), + + /* I2S2 function select */ + SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT, + 1, 1), + RT5645_HWEQ("Speaker HWEQ"), + + /* Digital Soft Volume Control */ + SOC_ENUM("DAC1 Digital Volume Control Func", rt5645_dac1_vol_ctrl_mode), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + int idx, rate; + + rate = rt5645->sysclk / rl6231_get_pre_div(rt5645->regmap, + RT5645_ADDA_CLK1, RT5645_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); + if (idx < 0) + dev_err(component->dev, "Failed to set DMIC clock\n"); + else + snd_soc_component_update_bits(component, RT5645_DMIC_CTRL1, + RT5645_DMIC_CLK_MASK, idx << RT5645_DMIC_CLK_SFT); + return idx; +} + +static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int val; + + val = snd_soc_component_read(component, RT5645_GLB_CLK); + val &= RT5645_SCLK_SRC_MASK; + if (val == RT5645_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int reg, shift, val; + + switch (source->shift) { + case 0: + reg = RT5645_ASRC_3; + shift = 0; + break; + case 1: + reg = RT5645_ASRC_3; + shift = 4; + break; + case 3: + reg = RT5645_ASRC_2; + shift = 0; + break; + case 8: + reg = RT5645_ASRC_2; + shift = 4; + break; + case 9: + reg = RT5645_ASRC_2; + shift = 8; + break; + case 10: + reg = RT5645_ASRC_2; + shift = 12; + break; + default: + return 0; + } + + val = (snd_soc_component_read(component, reg) >> shift) & 0xf; + switch (val) { + case 1: + case 2: + case 3: + case 4: + return 1; + default: + return 0; + } + +} + +static int rt5645_enable_hweq(struct snd_soc_component *component) +{ + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + int i; + + for (i = 0; i < RT5645_HWEQ_NUM; i++) { + if (rt5645_validate_hweq(rt5645->eq_param[i].reg)) + regmap_write(rt5645->regmap, rt5645->eq_param[i].reg, + rt5645->eq_param[i].val); + else + break; + } + + return 0; +} + +/** + * rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5645 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the codec driver will turn on ASRC + * for these filters if ASRC is selected as their clock source. + */ +int rt5645_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + unsigned int asrc2_mask = 0; + unsigned int asrc2_value = 0; + unsigned int asrc3_mask = 0; + unsigned int asrc3_value = 0; + + switch (clk_src) { + case RT5645_CLK_SEL_SYS: + case RT5645_CLK_SEL_I2S1_ASRC: + case RT5645_CLK_SEL_I2S2_ASRC: + case RT5645_CLK_SEL_SYS2: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5645_DA_STEREO_FILTER) { + asrc2_mask |= RT5645_DA_STO_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5645_DA_STO_CLK_SEL_MASK) + | (clk_src << RT5645_DA_STO_CLK_SEL_SFT); + } + + if (filter_mask & RT5645_DA_MONO_L_FILTER) { + asrc2_mask |= RT5645_DA_MONOL_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5645_DA_MONOL_CLK_SEL_MASK) + | (clk_src << RT5645_DA_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5645_DA_MONO_R_FILTER) { + asrc2_mask |= RT5645_DA_MONOR_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5645_DA_MONOR_CLK_SEL_MASK) + | (clk_src << RT5645_DA_MONOR_CLK_SEL_SFT); + } + + if (filter_mask & RT5645_AD_STEREO_FILTER) { + asrc2_mask |= RT5645_AD_STO1_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5645_AD_STO1_CLK_SEL_MASK) + | (clk_src << RT5645_AD_STO1_CLK_SEL_SFT); + } + + if (filter_mask & RT5645_AD_MONO_L_FILTER) { + asrc3_mask |= RT5645_AD_MONOL_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5645_AD_MONOL_CLK_SEL_MASK) + | (clk_src << RT5645_AD_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5645_AD_MONO_R_FILTER) { + asrc3_mask |= RT5645_AD_MONOR_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5645_AD_MONOR_CLK_SEL_MASK) + | (clk_src << RT5645_AD_MONOR_CLK_SEL_SFT); + } + + if (asrc2_mask) + snd_soc_component_update_bits(component, RT5645_ASRC_2, + asrc2_mask, asrc2_value); + + if (asrc3_mask) + snd_soc_component_update_bits(component, RT5645_ASRC_3, + asrc3_mask, asrc3_value); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5645_sel_asrc_clk_src); + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER, + RT5645_M_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5645_STO1_ADC_MIXER, + RT5645_M_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER, + RT5645_M_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5645_STO1_ADC_MIXER, + RT5645_M_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_mono_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5645_MONO_ADC_MIXER, + RT5645_M_MONO_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5645_MONO_ADC_MIXER, + RT5645_M_MONO_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_mono_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5645_MONO_ADC_MIXER, + RT5645_M_MONO_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5645_MONO_ADC_MIXER, + RT5645_M_MONO_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER, + RT5645_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER, + RT5645_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5645_AD_DA_MIXER, + RT5645_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER, + RT5645_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_L2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_R2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_STO_DAC_MIXER, + RT5645_M_DAC_L1_STO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_mono_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_L1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_L2_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_R2_MONO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_mono_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_R1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_R2_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_MONO_DAC_MIXER, + RT5645_M_DAC_L2_MONO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_dig_l_mix[] = { + SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5645_DIG_MIXER, + RT5645_M_STO_L_DAC_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_DIG_MIXER, + RT5645_M_DAC_L2_DAC_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_DIG_MIXER, + RT5645_M_DAC_R2_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_dig_r_mix[] = { + SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5645_DIG_MIXER, + RT5645_M_STO_R_DAC_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_DIG_MIXER, + RT5645_M_DAC_R2_DAC_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_DIG_MIXER, + RT5645_M_DAC_L2_DAC_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5645_rec_l_mix[] = { + SOC_DAPM_SINGLE("HPOL Switch", RT5645_REC_L2_MIXER, + RT5645_M_HP_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5645_REC_L2_MIXER, + RT5645_M_IN_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5645_REC_L2_MIXER, + RT5645_M_BST2_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5645_REC_L2_MIXER, + RT5645_M_BST1_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXL Switch", RT5645_REC_L2_MIXER, + RT5645_M_OM_L_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_rec_r_mix[] = { + SOC_DAPM_SINGLE("HPOR Switch", RT5645_REC_R2_MIXER, + RT5645_M_HP_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5645_REC_R2_MIXER, + RT5645_M_IN_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5645_REC_R2_MIXER, + RT5645_M_BST2_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5645_REC_R2_MIXER, + RT5645_M_BST1_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXR Switch", RT5645_REC_R2_MIXER, + RT5645_M_OM_R_RM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_spk_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_SPK_L_MIXER, + RT5645_M_DAC_L1_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_SPK_L_MIXER, + RT5645_M_DAC_L2_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5645_SPK_L_MIXER, + RT5645_M_IN_L_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5645_SPK_L_MIXER, + RT5645_M_BST1_L_SM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_spk_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPK_R_MIXER, + RT5645_M_DAC_R1_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_SPK_R_MIXER, + RT5645_M_DAC_R2_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5645_SPK_R_MIXER, + RT5645_M_IN_R_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5645_SPK_R_MIXER, + RT5645_M_BST2_R_SM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_out_l_mix[] = { + SOC_DAPM_SINGLE("BST1 Switch", RT5645_OUT_L1_MIXER, + RT5645_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5645_OUT_L1_MIXER, + RT5645_M_IN_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5645_OUT_L1_MIXER, + RT5645_M_DAC_L2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_OUT_L1_MIXER, + RT5645_M_DAC_L1_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_out_r_mix[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5645_OUT_R1_MIXER, + RT5645_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5645_OUT_R1_MIXER, + RT5645_M_IN_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5645_OUT_R1_MIXER, + RT5645_M_DAC_R2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_OUT_R1_MIXER, + RT5645_M_DAC_R1_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_spo_l_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPO_MIXER, + RT5645_M_DAC_R1_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_SPO_MIXER, + RT5645_M_DAC_L1_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL R Switch", RT5645_SPO_MIXER, + RT5645_M_SV_R_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL L Switch", RT5645_SPO_MIXER, + RT5645_M_SV_L_SPM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_spo_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_SPO_MIXER, + RT5645_M_DAC_R1_SPM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL R Switch", RT5645_SPO_MIXER, + RT5645_M_SV_R_SPM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_hpo_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPO_MIXER, + RT5645_M_DAC1_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPVOL Switch", RT5645_HPO_MIXER, + RT5645_M_HPVOL_HM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_hpvoll_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPOMIXL_CTRL, + RT5645_M_DAC1_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC2 Switch", RT5645_HPOMIXL_CTRL, + RT5645_M_DAC2_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5645_HPOMIXL_CTRL, + RT5645_M_IN_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5645_HPOMIXL_CTRL, + RT5645_M_BST1_HV_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_hpvolr_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5645_HPOMIXR_CTRL, + RT5645_M_DAC1_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC2 Switch", RT5645_HPOMIXR_CTRL, + RT5645_M_DAC2_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5645_HPOMIXR_CTRL, + RT5645_M_IN_HV_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5645_HPOMIXR_CTRL, + RT5645_M_BST2_HV_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5645_lout_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5645_LOUT_MIXER, + RT5645_M_DAC_L1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5645_LOUT_MIXER, + RT5645_M_DAC_R1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIX L Switch", RT5645_LOUT_MIXER, + RT5645_M_OV_L_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIX R Switch", RT5645_LOUT_MIXER, + RT5645_M_OV_R_LM_SFT, 1, 1), +}; + +/*DAC1 L/R source*/ /* MX-29 [9:8] [11:10] */ +static const char * const rt5645_dac1_src[] = { + "IF1 DAC", "IF2 DAC", "IF3 DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_dac1l_enum, RT5645_AD_DA_MIXER, + RT5645_DAC1_L_SEL_SFT, rt5645_dac1_src); + +static const struct snd_kcontrol_new rt5645_dac1l_mux = + SOC_DAPM_ENUM("DAC1 L source", rt5645_dac1l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5645_dac1r_enum, RT5645_AD_DA_MIXER, + RT5645_DAC1_R_SEL_SFT, rt5645_dac1_src); + +static const struct snd_kcontrol_new rt5645_dac1r_mux = + SOC_DAPM_ENUM("DAC1 R source", rt5645_dac1r_enum); + +/*DAC2 L/R source*/ /* MX-1B [6:4] [2:0] */ +static const char * const rt5645_dac12_src[] = { + "IF1 DAC", "IF2 DAC", "IF3 DAC", "Mono ADC", "VAD_ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_dac2l_enum, RT5645_DAC_CTRL, + RT5645_DAC2_L_SEL_SFT, rt5645_dac12_src); + +static const struct snd_kcontrol_new rt5645_dac_l2_mux = + SOC_DAPM_ENUM("DAC2 L source", rt5645_dac2l_enum); + +static const char * const rt5645_dacr2_src[] = { + "IF1 DAC", "IF2 DAC", "IF3 DAC", "Mono ADC", "Haptic" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_dac2r_enum, RT5645_DAC_CTRL, + RT5645_DAC2_R_SEL_SFT, rt5645_dacr2_src); + +static const struct snd_kcontrol_new rt5645_dac_r2_mux = + SOC_DAPM_ENUM("DAC2 R source", rt5645_dac2r_enum); + +/* Stereo1 ADC source */ +/* MX-27 [12] */ +static const char * const rt5645_stereo_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_stereo1_adc1_enum, RT5645_STO1_ADC_MIXER, + RT5645_ADC_1_SRC_SFT, rt5645_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5645_sto_adc1_mux = + SOC_DAPM_ENUM("Stereo1 ADC1 Mux", rt5645_stereo1_adc1_enum); + +/* MX-27 [11] */ +static const char * const rt5645_stereo_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_stereo1_adc2_enum, RT5645_STO1_ADC_MIXER, + RT5645_ADC_2_SRC_SFT, rt5645_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5645_sto_adc2_mux = + SOC_DAPM_ENUM("Stereo1 ADC2 Mux", rt5645_stereo1_adc2_enum); + +/* MX-27 [8] */ +static const char * const rt5645_stereo_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_stereo1_dmic_enum, RT5645_STO1_ADC_MIXER, + RT5645_DMIC_SRC_SFT, rt5645_stereo_dmic_src); + +static const struct snd_kcontrol_new rt5645_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC source", rt5645_stereo1_dmic_enum); + +/* Mono ADC source */ +/* MX-28 [12] */ +static const char * const rt5645_mono_adc_l1_src[] = { + "Mono DAC MIXL", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_adc_l1_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_ADC_L1_SRC_SFT, rt5645_mono_adc_l1_src); + +static const struct snd_kcontrol_new rt5645_mono_adc_l1_mux = + SOC_DAPM_ENUM("Mono ADC1 left source", rt5645_mono_adc_l1_enum); +/* MX-28 [11] */ +static const char * const rt5645_mono_adc_l2_src[] = { + "Mono DAC MIXL", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_adc_l2_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_ADC_L2_SRC_SFT, rt5645_mono_adc_l2_src); + +static const struct snd_kcontrol_new rt5645_mono_adc_l2_mux = + SOC_DAPM_ENUM("Mono ADC2 left source", rt5645_mono_adc_l2_enum); + +/* MX-28 [8] */ +static const char * const rt5645_mono_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_dmic_l_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_DMIC_L_SRC_SFT, rt5645_mono_dmic_src); + +static const struct snd_kcontrol_new rt5645_mono_dmic_l_mux = + SOC_DAPM_ENUM("Mono DMIC left source", rt5645_mono_dmic_l_enum); +/* MX-28 [1:0] */ +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_dmic_r_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_DMIC_R_SRC_SFT, rt5645_mono_dmic_src); + +static const struct snd_kcontrol_new rt5645_mono_dmic_r_mux = + SOC_DAPM_ENUM("Mono DMIC Right source", rt5645_mono_dmic_r_enum); +/* MX-28 [4] */ +static const char * const rt5645_mono_adc_r1_src[] = { + "Mono DAC MIXR", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_adc_r1_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_ADC_R1_SRC_SFT, rt5645_mono_adc_r1_src); + +static const struct snd_kcontrol_new rt5645_mono_adc_r1_mux = + SOC_DAPM_ENUM("Mono ADC1 right source", rt5645_mono_adc_r1_enum); +/* MX-28 [3] */ +static const char * const rt5645_mono_adc_r2_src[] = { + "Mono DAC MIXR", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_mono_adc_r2_enum, RT5645_MONO_ADC_MIXER, + RT5645_MONO_ADC_R2_SRC_SFT, rt5645_mono_adc_r2_src); + +static const struct snd_kcontrol_new rt5645_mono_adc_r2_mux = + SOC_DAPM_ENUM("Mono ADC2 right source", rt5645_mono_adc_r2_enum); + +/* MX-77 [9:8] */ +static const char * const rt5645_if1_adc_in_src[] = { + "IF_ADC1/IF_ADC2/VAD_ADC", "IF_ADC2/IF_ADC1/VAD_ADC", + "VAD_ADC/IF_ADC1/IF_ADC2", "VAD_ADC/IF_ADC2/IF_ADC1" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_if1_adc_in_enum, RT5645_TDM_CTRL_1, + RT5645_IF1_ADC_IN_SFT, rt5645_if1_adc_in_src); + +static const struct snd_kcontrol_new rt5645_if1_adc_in_mux = + SOC_DAPM_ENUM("IF1 ADC IN source", rt5645_if1_adc_in_enum); + +/* MX-78 [4:0] */ +static const char * const rt5650_if1_adc_in_src[] = { + "IF_ADC1/IF_ADC2/DAC_REF/Null", + "IF_ADC1/IF_ADC2/Null/DAC_REF", + "IF_ADC1/DAC_REF/IF_ADC2/Null", + "IF_ADC1/DAC_REF/Null/IF_ADC2", + "IF_ADC1/Null/DAC_REF/IF_ADC2", + "IF_ADC1/Null/IF_ADC2/DAC_REF", + + "IF_ADC2/IF_ADC1/DAC_REF/Null", + "IF_ADC2/IF_ADC1/Null/DAC_REF", + "IF_ADC2/DAC_REF/IF_ADC1/Null", + "IF_ADC2/DAC_REF/Null/IF_ADC1", + "IF_ADC2/Null/DAC_REF/IF_ADC1", + "IF_ADC2/Null/IF_ADC1/DAC_REF", + + "DAC_REF/IF_ADC1/IF_ADC2/Null", + "DAC_REF/IF_ADC1/Null/IF_ADC2", + "DAC_REF/IF_ADC2/IF_ADC1/Null", + "DAC_REF/IF_ADC2/Null/IF_ADC1", + "DAC_REF/Null/IF_ADC1/IF_ADC2", + "DAC_REF/Null/IF_ADC2/IF_ADC1", + + "Null/IF_ADC1/IF_ADC2/DAC_REF", + "Null/IF_ADC1/DAC_REF/IF_ADC2", + "Null/IF_ADC2/IF_ADC1/DAC_REF", + "Null/IF_ADC2/DAC_REF/IF_ADC1", + "Null/DAC_REF/IF_ADC1/IF_ADC2", + "Null/DAC_REF/IF_ADC2/IF_ADC1", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5650_if1_adc_in_enum, RT5645_TDM_CTRL_2, + 0, rt5650_if1_adc_in_src); + +static const struct snd_kcontrol_new rt5650_if1_adc_in_mux = + SOC_DAPM_ENUM("IF1 ADC IN source", rt5650_if1_adc_in_enum); + +/* MX-78 [15:14][13:12][11:10] */ +static const char * const rt5645_tdm_adc_swap_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot0_1_enum, + RT5645_TDM_CTRL_2, 14, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_adc1_in_mux = + SOC_DAPM_ENUM("IF1 ADC1 IN source", rt5650_tdm_adc_slot0_1_enum); + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot2_3_enum, + RT5645_TDM_CTRL_2, 12, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_adc2_in_mux = + SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5650_tdm_adc_slot2_3_enum); + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_adc_slot4_5_enum, + RT5645_TDM_CTRL_2, 10, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_adc3_in_mux = + SOC_DAPM_ENUM("IF1 ADC3 IN source", rt5650_tdm_adc_slot4_5_enum); + +/* MX-77 [7:6][5:4][3:2] */ +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum, + RT5645_TDM_CTRL_1, 6, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_adc1_in_mux = + SOC_DAPM_ENUM("IF1 ADC1 IN source", rt5645_tdm_adc_slot0_1_enum); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum, + RT5645_TDM_CTRL_1, 4, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_adc2_in_mux = + SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5645_tdm_adc_slot2_3_enum); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum, + RT5645_TDM_CTRL_1, 2, rt5645_tdm_adc_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_adc3_in_mux = + SOC_DAPM_ENUM("IF1 ADC3 IN source", rt5645_tdm_adc_slot4_5_enum); + +/* MX-79 [14:12][10:8][6:4][2:0] */ +static const char * const rt5645_tdm_dac_swap_select[] = { + "Slot0", "Slot1", "Slot2", "Slot3" +}; + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac0_enum, + RT5645_TDM_CTRL_3, 12, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_dac0_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC0 source", rt5645_tdm_dac0_enum); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac1_enum, + RT5645_TDM_CTRL_3, 8, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_dac1_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC1 source", rt5645_tdm_dac1_enum); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac2_enum, + RT5645_TDM_CTRL_3, 4, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_dac2_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC2 source", rt5645_tdm_dac2_enum); + +static SOC_ENUM_SINGLE_DECL(rt5645_tdm_dac3_enum, + RT5645_TDM_CTRL_3, 0, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5645_if1_dac3_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC3 source", rt5645_tdm_dac3_enum); + +/* MX-7a [14:12][10:8][6:4][2:0] */ +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac0_enum, + RT5650_TDM_CTRL_4, 12, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_dac0_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC0 source", rt5650_tdm_dac0_enum); + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac1_enum, + RT5650_TDM_CTRL_4, 8, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_dac1_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC1 source", rt5650_tdm_dac1_enum); + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac2_enum, + RT5650_TDM_CTRL_4, 4, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_dac2_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC2 source", rt5650_tdm_dac2_enum); + +static SOC_ENUM_SINGLE_DECL(rt5650_tdm_dac3_enum, + RT5650_TDM_CTRL_4, 0, rt5645_tdm_dac_swap_select); + +static const struct snd_kcontrol_new rt5650_if1_dac3_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC3 source", rt5650_tdm_dac3_enum); + +/* MX-2d [3] [2] */ +static const char * const rt5650_a_dac1_src[] = { + "DAC1", "Stereo DAC Mixer" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5650_a_dac1_l_enum, RT5650_A_DAC_SOUR, + RT5650_A_DAC1_L_IN_SFT, rt5650_a_dac1_src); + +static const struct snd_kcontrol_new rt5650_a_dac1_l_mux = + SOC_DAPM_ENUM("A DAC1 L source", rt5650_a_dac1_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5650_a_dac1_r_enum, RT5650_A_DAC_SOUR, + RT5650_A_DAC1_R_IN_SFT, rt5650_a_dac1_src); + +static const struct snd_kcontrol_new rt5650_a_dac1_r_mux = + SOC_DAPM_ENUM("A DAC1 R source", rt5650_a_dac1_r_enum); + +/* MX-2d [1] [0] */ +static const char * const rt5650_a_dac2_src[] = { + "Stereo DAC Mixer", "Mono DAC Mixer" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5650_a_dac2_l_enum, RT5650_A_DAC_SOUR, + RT5650_A_DAC2_L_IN_SFT, rt5650_a_dac2_src); + +static const struct snd_kcontrol_new rt5650_a_dac2_l_mux = + SOC_DAPM_ENUM("A DAC2 L source", rt5650_a_dac2_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5650_a_dac2_r_enum, RT5650_A_DAC_SOUR, + RT5650_A_DAC2_R_IN_SFT, rt5650_a_dac2_src); + +static const struct snd_kcontrol_new rt5650_a_dac2_r_mux = + SOC_DAPM_ENUM("A DAC2 R source", rt5650_a_dac2_r_enum); + +/* MX-2F [13:12] */ +static const char * const rt5645_if2_adc_in_src[] = { + "IF_ADC1", "IF_ADC2", "VAD_ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_if2_adc_in_enum, RT5645_DIG_INF1_DATA, + RT5645_IF2_ADC_IN_SFT, rt5645_if2_adc_in_src); + +static const struct snd_kcontrol_new rt5645_if2_adc_in_mux = + SOC_DAPM_ENUM("IF2 ADC IN source", rt5645_if2_adc_in_enum); + +/* MX-31 [15] [13] [11] [9] */ +static const char * const rt5645_pdm_src[] = { + "Mono DAC", "Stereo DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_pdm1_l_enum, RT5645_PDM_OUT_CTRL, + RT5645_PDM1_L_SFT, rt5645_pdm_src); + +static const struct snd_kcontrol_new rt5645_pdm1_l_mux = + SOC_DAPM_ENUM("PDM1 L source", rt5645_pdm1_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5645_pdm1_r_enum, RT5645_PDM_OUT_CTRL, + RT5645_PDM1_R_SFT, rt5645_pdm_src); + +static const struct snd_kcontrol_new rt5645_pdm1_r_mux = + SOC_DAPM_ENUM("PDM1 R source", rt5645_pdm1_r_enum); + +/* MX-9D [9:8] */ +static const char * const rt5645_vad_adc_src[] = { + "Sto1 ADC L", "Mono ADC L", "Mono ADC R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5645_vad_adc_enum, RT5645_VAD_CTRL4, + RT5645_VAD_SEL_SFT, rt5645_vad_adc_src); + +static const struct snd_kcontrol_new rt5645_vad_adc_mux = + SOC_DAPM_ENUM("VAD ADC source", rt5645_vad_adc_enum); + +static const struct snd_kcontrol_new spk_l_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_SPK_VOL, + RT5645_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new spk_r_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_SPK_VOL, + RT5645_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hp_l_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_HP_VOL, + RT5645_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hp_r_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_HP_VOL, + RT5645_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new pdm1_l_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL, + RT5645_M_PDM1_L, 1, 1); + +static const struct snd_kcontrol_new pdm1_r_vol_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5645_PDM_OUT_CTRL, + RT5645_M_PDM1_R, 1, 1); + +static void hp_amp_power(struct snd_soc_component *component, int on) +{ + static int hp_amp_power_count; + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + int i, val; + + if (on) { + if (hp_amp_power_count <= 0) { + if (rt5645->codec_type == CODEC_TYPE_RT5650) { + snd_soc_component_write(component, RT5645_DEPOP_M2, 0x3100); + snd_soc_component_write(component, RT5645_CHARGE_PUMP, + 0x0e06); + snd_soc_component_write(component, RT5645_DEPOP_M1, 0x000d); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_HP_DCC_INT1, 0x9f01); + for (i = 0; i < 20; i++) { + usleep_range(1000, 1500); + regmap_read(rt5645->regmap, RT5645_PR_BASE + + RT5645_HP_DCC_INT1, &val); + if (!(val & 0x8000)) + break; + } + snd_soc_component_update_bits(component, RT5645_DEPOP_M1, + RT5645_HP_CO_MASK, RT5645_HP_CO_EN); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + 0x3e, 0x7400); + snd_soc_component_write(component, RT5645_DEPOP_M3, 0x0737); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_MAMP_INT_REG2, 0xfc00); + snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R, + RT5645_PWR_HP_L | RT5645_PWR_HP_R); + msleep(90); + } else { + /* depop parameters */ + snd_soc_component_update_bits(component, RT5645_DEPOP_M2, + RT5645_DEPOP_MASK, RT5645_DEPOP_MAN); + snd_soc_component_write(component, RT5645_DEPOP_M1, 0x000d); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_HP_DCC_INT1, 0x9f01); + mdelay(150); + /* headphone amp power on */ + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_FV1 | RT5645_PWR_FV2, 0); + snd_soc_component_update_bits(component, RT5645_PWR_VOL, + RT5645_PWR_HV_L | RT5645_PWR_HV_R, + RT5645_PWR_HV_L | RT5645_PWR_HV_R); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R | + RT5645_PWR_HA, + RT5645_PWR_HP_L | RT5645_PWR_HP_R | + RT5645_PWR_HA); + mdelay(5); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_FV1 | RT5645_PWR_FV2, + RT5645_PWR_FV1 | RT5645_PWR_FV2); + + snd_soc_component_update_bits(component, RT5645_DEPOP_M1, + RT5645_HP_CO_MASK | RT5645_HP_SG_MASK, + RT5645_HP_CO_EN | RT5645_HP_SG_EN); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + 0x14, 0x1aaa); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + 0x24, 0x0430); + } + } + hp_amp_power_count++; + } else { + hp_amp_power_count--; + if (hp_amp_power_count <= 0) { + if (rt5645->codec_type == CODEC_TYPE_RT5650) { + regmap_write(rt5645->regmap, RT5645_PR_BASE + + 0x3e, 0x7400); + snd_soc_component_write(component, RT5645_DEPOP_M3, 0x0737); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_MAMP_INT_REG2, 0xfc00); + snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140); + msleep(100); + snd_soc_component_write(component, RT5645_DEPOP_M1, 0x0001); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R, 0); + } else { + snd_soc_component_update_bits(component, RT5645_DEPOP_M1, + RT5645_HP_SG_MASK | + RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, + RT5645_HP_SG_DIS | + RT5645_HP_L_SMT_DIS | + RT5645_HP_R_SMT_DIS); + /* headphone amp power down */ + snd_soc_component_write(component, RT5645_DEPOP_M1, 0x0000); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_HP_L | RT5645_PWR_HP_R | + RT5645_PWR_HA, 0); + snd_soc_component_update_bits(component, RT5645_DEPOP_M2, + RT5645_DEPOP_MASK, 0); + } + } + } +} + +static int rt5645_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + hp_amp_power(component, 1); + /* headphone unmute sequence */ + if (rt5645->codec_type == CODEC_TYPE_RT5645) { + snd_soc_component_update_bits(component, RT5645_DEPOP_M3, + RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK | + RT5645_CP_FQ3_MASK, + (RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ1_SFT) | + (RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) | + (RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ3_SFT)); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_MAMP_INT_REG2, 0xfc00); + snd_soc_component_update_bits(component, RT5645_DEPOP_M1, + RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN); + snd_soc_component_update_bits(component, RT5645_DEPOP_M1, + RT5645_RSTN_MASK, RT5645_RSTN_EN); + snd_soc_component_update_bits(component, RT5645_DEPOP_M1, + RT5645_RSTN_MASK | RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, RT5645_RSTN_DIS | + RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN); + msleep(40); + snd_soc_component_update_bits(component, RT5645_DEPOP_M1, + RT5645_HP_SG_MASK | RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, RT5645_HP_SG_DIS | + RT5645_HP_L_SMT_DIS | RT5645_HP_R_SMT_DIS); + } + break; + + case SND_SOC_DAPM_PRE_PMD: + /* headphone mute sequence */ + if (rt5645->codec_type == CODEC_TYPE_RT5645) { + snd_soc_component_update_bits(component, RT5645_DEPOP_M3, + RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK | + RT5645_CP_FQ3_MASK, + (RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ1_SFT) | + (RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) | + (RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ3_SFT)); + regmap_write(rt5645->regmap, RT5645_PR_BASE + + RT5645_MAMP_INT_REG2, 0xfc00); + snd_soc_component_update_bits(component, RT5645_DEPOP_M1, + RT5645_HP_SG_MASK, RT5645_HP_SG_EN); + snd_soc_component_update_bits(component, RT5645_DEPOP_M1, + RT5645_RSTP_MASK, RT5645_RSTP_EN); + snd_soc_component_update_bits(component, RT5645_DEPOP_M1, + RT5645_RSTP_MASK | RT5645_HP_L_SMT_MASK | + RT5645_HP_R_SMT_MASK, RT5645_RSTP_DIS | + RT5645_HP_L_SMT_EN | RT5645_HP_R_SMT_EN); + msleep(30); + } + hp_amp_power(component, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5645_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt5645_enable_hweq(component); + snd_soc_component_update_bits(component, RT5645_PWR_DIG1, + RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | + RT5645_PWR_CLS_D_L, + RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | + RT5645_PWR_CLS_D_L); + snd_soc_component_update_bits(component, RT5645_GEN_CTRL3, + RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5645_GEN_CTRL3, + RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS); + snd_soc_component_write(component, RT5645_EQ_CTRL2, 0); + snd_soc_component_update_bits(component, RT5645_PWR_DIG1, + RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | + RT5645_PWR_CLS_D_L, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5645_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + hp_amp_power(component, 1); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_LM, RT5645_PWR_LM); + snd_soc_component_update_bits(component, RT5645_LOUT1, + RT5645_L_MUTE | RT5645_R_MUTE, 0); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5645_LOUT1, + RT5645_L_MUTE | RT5645_R_MUTE, + RT5645_L_MUTE | RT5645_R_MUTE); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_LM, 0); + hp_amp_power(component, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5645_bst2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5645_PWR_ANLG2, + RT5645_PWR_BST2_P, RT5645_PWR_BST2_P); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5645_PWR_ANLG2, + RT5645_PWR_BST2_P, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5645_set_micbias1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, RT5645_GEN_CTRL2, + RT5645_MICBIAS1_POW_CTRL_SEL_MASK, + RT5645_MICBIAS1_POW_CTRL_SEL_M); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, RT5645_GEN_CTRL2, + RT5645_MICBIAS1_POW_CTRL_SEL_MASK, + RT5645_MICBIAS1_POW_CTRL_SEL_A); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5645_set_micbias2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, RT5645_GEN_CTRL2, + RT5645_MICBIAS2_POW_CTRL_SEL_MASK, + RT5645_MICBIAS2_POW_CTRL_SEL_M); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, RT5645_GEN_CTRL2, + RT5645_MICBIAS2_POW_CTRL_SEL_MASK, + RT5645_MICBIAS2_POW_CTRL_SEL_A); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5645_PWR_MIXER, + RT5645_PWR_LDO2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5645_PWR_ANLG2, + RT5645_PWR_PLL_BIT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("JD Power", RT5645_PWR_ANLG2, + RT5645_PWR_JD1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5645_PWR_VOL, + RT5645_PWR_MIC_DET_BIT, 0, NULL, 0), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5645_ASRC_1, + 11, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5645_ASRC_1, + 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5645_ASRC_1, + 10, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5645_ASRC_1, + 9, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5645_ASRC_1, + 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5645_ASRC_1, + 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5645_ASRC_1, + 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5645_ASRC_1, + 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5645_ASRC_1, + 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5645_ASRC_1, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5645_ASRC_1, + 0, 0, NULL, 0), + + /* Input Side */ + /* micbias */ + SND_SOC_DAPM_SUPPLY("micbias1", RT5645_PWR_ANLG2, + RT5645_PWR_MB1_BIT, 0, rt5645_set_micbias1_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("micbias2", RT5645_PWR_ANLG2, + RT5645_PWR_MB2_BIT, 0, rt5645_set_micbias2_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + SND_SOC_DAPM_INPUT("DMIC L2"), + SND_SOC_DAPM_INPUT("DMIC R2"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + + SND_SOC_DAPM_INPUT("Haptic Generator"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5645_DMIC_CTRL1, + RT5645_DMIC_1_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5645_DMIC_CTRL1, + RT5645_DMIC_2_EN_SFT, 0, NULL, 0), + /* Boost */ + SND_SOC_DAPM_PGA("BST1", RT5645_PWR_ANLG2, + RT5645_PWR_BST1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("BST2", RT5645_PWR_ANLG2, + RT5645_PWR_BST2_BIT, 0, NULL, 0, rt5645_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + /* Input Volume */ + SND_SOC_DAPM_PGA("INL VOL", RT5645_PWR_VOL, + RT5645_PWR_IN_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR VOL", RT5645_PWR_VOL, + RT5645_PWR_IN_R_BIT, 0, NULL, 0), + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5645_PWR_MIXER, RT5645_PWR_RM_L_BIT, + 0, rt5645_rec_l_mix, ARRAY_SIZE(rt5645_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5645_PWR_MIXER, RT5645_PWR_RM_R_BIT, + 0, rt5645_rec_r_mix, ARRAY_SIZE(rt5645_rec_r_mix)), + /* ADCs */ + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC L power", RT5645_PWR_DIG1, + RT5645_PWR_ADC_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC R power", RT5645_PWR_DIG1, + RT5645_PWR_ADC_R_BIT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5645_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_sto_adc2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_sto_adc2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_sto_adc1_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_sto_adc1_mux), + SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_dmic_l_mux), + SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_dmic_r_mux), + SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_adc_l2_mux), + SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_adc_l1_mux), + SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_adc_r1_mux), + SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5645_mono_adc_r2_mux), + /* ADC Mixer */ + + SND_SOC_DAPM_SUPPLY_S("adc stereo1 filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_ADC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5645_sto1_adc_l_mix, ARRAY_SIZE(rt5645_sto1_adc_l_mix), + NULL, 0), + SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5645_sto1_adc_r_mix, ARRAY_SIZE(rt5645_sto1_adc_r_mix), + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("adc mono left filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_ADC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("Mono ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5645_mono_adc_l_mix, ARRAY_SIZE(rt5645_mono_adc_l_mix), + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("adc mono right filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_ADC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("Mono ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5645_mono_adc_r_mix, ARRAY_SIZE(rt5645_mono_adc_r_mix), + NULL, 0), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("VAD_ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* IF1 2 Mux */ + SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if2_adc_in_mux), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5645_PWR_DIG1, + RT5645_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5645_PWR_DIG1, + RT5645_PWR_I2S2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, + 0, 0, &rt5645_vad_adc_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5645_dac_l_mix, ARRAY_SIZE(rt5645_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5645_dac_r_mix, ARRAY_SIZE(rt5645_dac_r_mix)), + + /* DAC2 channel Mux */ + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac_r2_mux), + SND_SOC_DAPM_PGA("DAC L2 Volume", RT5645_PWR_DIG1, + RT5645_PWR_DAC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC R2 Volume", RT5645_PWR_DIG1, + RT5645_PWR_DAC_R2_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DAC1 L Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac1l_mux), + SND_SOC_DAPM_MUX("DAC1 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_dac1r_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY_S("dac stereo1 filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_DAC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("dac mono left filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_DAC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("dac mono right filter", 1, RT5645_PWR_DIG2, + RT5645_PWR_DAC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5645_sto_dac_l_mix, ARRAY_SIZE(rt5645_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5645_sto_dac_r_mix, ARRAY_SIZE(rt5645_sto_dac_r_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5645_mono_dac_l_mix, ARRAY_SIZE(rt5645_mono_dac_l_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5645_mono_dac_r_mix, ARRAY_SIZE(rt5645_mono_dac_r_mix)), + SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5645_dig_l_mix, ARRAY_SIZE(rt5645_dig_l_mix)), + SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5645_dig_r_mix, ARRAY_SIZE(rt5645_dig_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_L1_BIT, + 0), + SND_SOC_DAPM_DAC("DAC L2", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_L2_BIT, + 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_R1_BIT, + 0), + SND_SOC_DAPM_DAC("DAC R2", NULL, RT5645_PWR_DIG1, RT5645_PWR_DAC_R2_BIT, + 0), + /* OUT Mixer */ + SND_SOC_DAPM_MIXER("SPK MIXL", RT5645_PWR_MIXER, RT5645_PWR_SM_L_BIT, + 0, rt5645_spk_l_mix, ARRAY_SIZE(rt5645_spk_l_mix)), + SND_SOC_DAPM_MIXER("SPK MIXR", RT5645_PWR_MIXER, RT5645_PWR_SM_R_BIT, + 0, rt5645_spk_r_mix, ARRAY_SIZE(rt5645_spk_r_mix)), + SND_SOC_DAPM_MIXER("OUT MIXL", RT5645_PWR_MIXER, RT5645_PWR_OM_L_BIT, + 0, rt5645_out_l_mix, ARRAY_SIZE(rt5645_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5645_PWR_MIXER, RT5645_PWR_OM_R_BIT, + 0, rt5645_out_r_mix, ARRAY_SIZE(rt5645_out_r_mix)), + /* Ouput Volume */ + SND_SOC_DAPM_SWITCH("SPKVOL L", RT5645_PWR_VOL, RT5645_PWR_SV_L_BIT, 0, + &spk_l_vol_control), + SND_SOC_DAPM_SWITCH("SPKVOL R", RT5645_PWR_VOL, RT5645_PWR_SV_R_BIT, 0, + &spk_r_vol_control), + SND_SOC_DAPM_MIXER("HPOVOL MIXL", RT5645_PWR_VOL, RT5645_PWR_HV_L_BIT, + 0, rt5645_hpvoll_mix, ARRAY_SIZE(rt5645_hpvoll_mix)), + SND_SOC_DAPM_MIXER("HPOVOL MIXR", RT5645_PWR_VOL, RT5645_PWR_HV_R_BIT, + 0, rt5645_hpvolr_mix, ARRAY_SIZE(rt5645_hpvolr_mix)), + SND_SOC_DAPM_SUPPLY("HPOVOL MIXL Power", RT5645_PWR_MIXER, + RT5645_PWR_HM_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HPOVOL MIXR Power", RT5645_PWR_MIXER, + RT5645_PWR_HM_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("HPOVOL L", SND_SOC_NOPM, 0, 0, &hp_l_vol_control), + SND_SOC_DAPM_SWITCH("HPOVOL R", SND_SOC_NOPM, 0, 0, &hp_r_vol_control), + + /* HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0, 0, rt5645_spo_l_mix, + ARRAY_SIZE(rt5645_spo_l_mix)), + SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0, 0, rt5645_spo_r_mix, + ARRAY_SIZE(rt5645_spo_r_mix)), + SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, rt5645_hpo_mix, + ARRAY_SIZE(rt5645_hpo_mix)), + SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, rt5645_lout_mix, + ARRAY_SIZE(rt5645_lout_mix)), + + SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0, rt5645_hp_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, rt5645_lout_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("SPK amp", 2, SND_SOC_NOPM, 0, 0, rt5645_spk_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + + /* PDM */ + SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5645_PWR_DIG2, RT5645_PWR_PDM1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_MUX("PDM1 L Mux", SND_SOC_NOPM, 0, 0, &rt5645_pdm1_l_mux), + SND_SOC_DAPM_MUX("PDM1 R Mux", SND_SOC_NOPM, 0, 0, &rt5645_pdm1_r_mux), + + SND_SOC_DAPM_SWITCH("PDM1 L", SND_SOC_NOPM, 0, 0, &pdm1_l_vol_control), + SND_SOC_DAPM_SWITCH("PDM1 R", SND_SOC_NOPM, 0, 0, &pdm1_r_vol_control), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("PDM1L"), + SND_SOC_DAPM_OUTPUT("PDM1R"), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), +}; + +static const struct snd_soc_dapm_widget rt5645_specific_dapm_widgets[] = { + SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0, + &rt5645_if1_dac0_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0, + &rt5645_if1_dac1_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0, + &rt5645_if1_dac2_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0, + &rt5645_if1_dac3_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc_in_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC1 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc1_in_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC2 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc2_in_mux), + SND_SOC_DAPM_MUX("RT5645 IF1 ADC3 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5645_if1_adc3_in_mux), +}; + +static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = { + SND_SOC_DAPM_MUX("A DAC1 L Mux", SND_SOC_NOPM, + 0, 0, &rt5650_a_dac1_l_mux), + SND_SOC_DAPM_MUX("A DAC1 R Mux", SND_SOC_NOPM, + 0, 0, &rt5650_a_dac1_r_mux), + SND_SOC_DAPM_MUX("A DAC2 L Mux", SND_SOC_NOPM, + 0, 0, &rt5650_a_dac2_l_mux), + SND_SOC_DAPM_MUX("A DAC2 R Mux", SND_SOC_NOPM, + 0, 0, &rt5650_a_dac2_r_mux), + + SND_SOC_DAPM_MUX("RT5650 IF1 ADC1 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc1_in_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 ADC2 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc2_in_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 ADC3 Swap Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc3_in_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 ADC Mux", SND_SOC_NOPM, + 0, 0, &rt5650_if1_adc_in_mux), + + SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 L Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac0_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 DAC1 R Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac1_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 L Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac2_tdm_sel_mux), + SND_SOC_DAPM_MUX("RT5650 IF1 DAC2 R Mux", SND_SOC_NOPM, 0, 0, + &rt5650_if1_dac3_tdm_sel_mux), +}; + +static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { + { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc }, + { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc }, + { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc }, + { "dac mono left filter", NULL, "DAC MONO L ASRC", is_using_asrc }, + { "dac mono right filter", NULL, "DAC MONO R ASRC", is_using_asrc }, + { "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc }, + + { "I2S1", NULL, "I2S1 ASRC" }, + { "I2S2", NULL, "I2S2 ASRC" }, + + { "IN1P", NULL, "LDO2" }, + { "IN2P", NULL, "LDO2" }, + + { "DMIC1", NULL, "DMIC L1" }, + { "DMIC1", NULL, "DMIC R1" }, + { "DMIC2", NULL, "DMIC L2" }, + { "DMIC2", NULL, "DMIC R2" }, + + { "BST1", NULL, "IN1P" }, + { "BST1", NULL, "IN1N" }, + { "BST1", NULL, "JD Power" }, + { "BST1", NULL, "Mic Det Power" }, + { "BST2", NULL, "IN2P" }, + { "BST2", NULL, "IN2N" }, + + { "INL VOL", NULL, "IN2P" }, + { "INR VOL", NULL, "IN2N" }, + + { "RECMIXL", "HPOL Switch", "HPOL" }, + { "RECMIXL", "INL Switch", "INL VOL" }, + { "RECMIXL", "BST2 Switch", "BST2" }, + { "RECMIXL", "BST1 Switch", "BST1" }, + { "RECMIXL", "OUT MIXL Switch", "OUT MIXL" }, + + { "RECMIXR", "HPOR Switch", "HPOR" }, + { "RECMIXR", "INR Switch", "INR VOL" }, + { "RECMIXR", "BST2 Switch", "BST2" }, + { "RECMIXR", "BST1 Switch", "BST1" }, + { "RECMIXR", "OUT MIXR Switch", "OUT MIXR" }, + + { "ADC L", NULL, "RECMIXL" }, + { "ADC L", NULL, "ADC L power" }, + { "ADC R", NULL, "RECMIXR" }, + { "ADC R", NULL, "ADC R power" }, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC L2", NULL, "DMIC CLK"}, + {"DMIC L2", NULL, "DMIC2 Power"}, + {"DMIC R2", NULL, "DMIC CLK"}, + {"DMIC R2", NULL, "DMIC2 Power"}, + + { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" }, + { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC" }, + + { "Mono DMIC L Mux", "DMIC1", "DMIC L1" }, + { "Mono DMIC L Mux", "DMIC2", "DMIC L2" }, + { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC" }, + + { "Mono DMIC R Mux", "DMIC1", "DMIC R1" }, + { "Mono DMIC R Mux", "DMIC2", "DMIC R2" }, + { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC" }, + + { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" }, + { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" }, + { "Stereo1 ADC L1 Mux", "ADC", "ADC L" }, + { "Stereo1 ADC L1 Mux", "DAC MIX", "DAC MIXL" }, + + { "Stereo1 ADC R1 Mux", "ADC", "ADC R" }, + { "Stereo1 ADC R1 Mux", "DAC MIX", "DAC MIXR" }, + { "Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC Mux" }, + { "Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR" }, + + { "Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux" }, + { "Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL" }, + { "Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL" }, + { "Mono ADC L1 Mux", "ADC", "ADC L" }, + + { "Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR" }, + { "Mono ADC R1 Mux", "ADC", "ADC R" }, + { "Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux" }, + { "Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR" }, + + { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux" }, + { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux" }, + { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux" }, + { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux" }, + { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux" }, + { "Mono ADC MIXL", NULL, "adc mono left filter" }, + { "adc mono left filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux" }, + { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux" }, + { "Mono ADC MIXR", NULL, "adc mono right filter" }, + { "adc mono right filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "VAD ADC Mux", "Sto1 ADC L", "Stereo1 ADC MIXL" }, + { "VAD ADC Mux", "Mono ADC L", "Mono ADC MIXL" }, + { "VAD ADC Mux", "Mono ADC R", "Mono ADC MIXR" }, + + { "IF_ADC1", NULL, "Stereo1 ADC MIXL" }, + { "IF_ADC1", NULL, "Stereo1 ADC MIXR" }, + { "IF_ADC2", NULL, "Mono ADC MIXL" }, + { "IF_ADC2", NULL, "Mono ADC MIXR" }, + { "VAD_ADC", NULL, "VAD ADC Mux" }, + + { "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" }, + { "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" }, + { "IF2 ADC Mux", "VAD_ADC", "VAD_ADC" }, + + { "IF1 ADC", NULL, "I2S1" }, + { "IF2 ADC", NULL, "I2S2" }, + { "IF2 ADC", NULL, "IF2 ADC Mux" }, + + { "AIF2TX", NULL, "IF2 ADC" }, + + { "IF1 DAC0", NULL, "AIF1RX" }, + { "IF1 DAC1", NULL, "AIF1RX" }, + { "IF1 DAC2", NULL, "AIF1RX" }, + { "IF1 DAC3", NULL, "AIF1RX" }, + { "IF2 DAC", NULL, "AIF2RX" }, + + { "IF1 DAC0", NULL, "I2S1" }, + { "IF1 DAC1", NULL, "I2S1" }, + { "IF1 DAC2", NULL, "I2S1" }, + { "IF1 DAC3", NULL, "I2S1" }, + { "IF2 DAC", NULL, "I2S2" }, + + { "IF2 DAC L", NULL, "IF2 DAC" }, + { "IF2 DAC R", NULL, "IF2 DAC" }, + + { "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" }, + { "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" }, + + { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" }, + { "DAC1 MIXL", "DAC1 Switch", "DAC1 L Mux" }, + { "DAC1 MIXL", NULL, "dac stereo1 filter" }, + { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" }, + { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" }, + { "DAC1 MIXR", NULL, "dac stereo1 filter" }, + + { "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" }, + { "DAC L2 Mux", "Mono ADC", "Mono ADC MIXL" }, + { "DAC L2 Mux", "VAD_ADC", "VAD_ADC" }, + { "DAC L2 Volume", NULL, "DAC L2 Mux" }, + { "DAC L2 Volume", NULL, "dac mono left filter" }, + + { "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" }, + { "DAC R2 Mux", "Mono ADC", "Mono ADC MIXR" }, + { "DAC R2 Mux", "Haptic", "Haptic Generator" }, + { "DAC R2 Volume", NULL, "DAC R2 Mux" }, + { "DAC R2 Volume", NULL, "dac mono right filter" }, + + { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "Stereo DAC MIXL", NULL, "dac stereo1 filter" }, + { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "Stereo DAC MIXR", NULL, "dac stereo1 filter" }, + + { "Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" }, + { "Mono DAC MIXL", NULL, "dac mono left filter" }, + { "Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" }, + { "Mono DAC MIXR", NULL, "dac mono right filter" }, + + { "DAC MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" }, + { "DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" }, + { "DAC MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" }, + { "DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" }, + + { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll }, + + { "SPK MIXL", "BST1 Switch", "BST1" }, + { "SPK MIXL", "INL Switch", "INL VOL" }, + { "SPK MIXL", "DAC L1 Switch", "DAC L1" }, + { "SPK MIXL", "DAC L2 Switch", "DAC L2" }, + { "SPK MIXR", "BST2 Switch", "BST2" }, + { "SPK MIXR", "INR Switch", "INR VOL" }, + { "SPK MIXR", "DAC R1 Switch", "DAC R1" }, + { "SPK MIXR", "DAC R2 Switch", "DAC R2" }, + + { "OUT MIXL", "BST1 Switch", "BST1" }, + { "OUT MIXL", "INL Switch", "INL VOL" }, + { "OUT MIXL", "DAC L2 Switch", "DAC L2" }, + { "OUT MIXL", "DAC L1 Switch", "DAC L1" }, + + { "OUT MIXR", "BST2 Switch", "BST2" }, + { "OUT MIXR", "INR Switch", "INR VOL" }, + { "OUT MIXR", "DAC R2 Switch", "DAC R2" }, + { "OUT MIXR", "DAC R1 Switch", "DAC R1" }, + + { "HPOVOL MIXL", "DAC1 Switch", "DAC L1" }, + { "HPOVOL MIXL", "DAC2 Switch", "DAC L2" }, + { "HPOVOL MIXL", "INL Switch", "INL VOL" }, + { "HPOVOL MIXL", "BST1 Switch", "BST1" }, + { "HPOVOL MIXL", NULL, "HPOVOL MIXL Power" }, + { "HPOVOL MIXR", "DAC1 Switch", "DAC R1" }, + { "HPOVOL MIXR", "DAC2 Switch", "DAC R2" }, + { "HPOVOL MIXR", "INR Switch", "INR VOL" }, + { "HPOVOL MIXR", "BST2 Switch", "BST2" }, + { "HPOVOL MIXR", NULL, "HPOVOL MIXR Power" }, + + { "DAC 2", NULL, "DAC L2" }, + { "DAC 2", NULL, "DAC R2" }, + { "DAC 1", NULL, "DAC L1" }, + { "DAC 1", NULL, "DAC R1" }, + { "HPOVOL L", "Switch", "HPOVOL MIXL" }, + { "HPOVOL R", "Switch", "HPOVOL MIXR" }, + { "HPOVOL", NULL, "HPOVOL L" }, + { "HPOVOL", NULL, "HPOVOL R" }, + { "HPO MIX", "DAC1 Switch", "DAC 1" }, + { "HPO MIX", "HPVOL Switch", "HPOVOL" }, + + { "SPKVOL L", "Switch", "SPK MIXL" }, + { "SPKVOL R", "Switch", "SPK MIXR" }, + + { "SPOL MIX", "DAC L1 Switch", "DAC L1" }, + { "SPOL MIX", "SPKVOL L Switch", "SPKVOL L" }, + { "SPOR MIX", "DAC R1 Switch", "DAC R1" }, + { "SPOR MIX", "SPKVOL R Switch", "SPKVOL R" }, + + { "LOUT MIX", "DAC L1 Switch", "DAC L1" }, + { "LOUT MIX", "DAC R1 Switch", "DAC R1" }, + { "LOUT MIX", "OUTMIX L Switch", "OUT MIXL" }, + { "LOUT MIX", "OUTMIX R Switch", "OUT MIXR" }, + + { "PDM1 L Mux", "Stereo DAC", "Stereo DAC MIXL" }, + { "PDM1 L Mux", "Mono DAC", "Mono DAC MIXL" }, + { "PDM1 L Mux", NULL, "PDM1 Power" }, + { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" }, + { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" }, + { "PDM1 R Mux", NULL, "PDM1 Power" }, + + { "HP amp", NULL, "HPO MIX" }, + { "HP amp", NULL, "JD Power" }, + { "HP amp", NULL, "Mic Det Power" }, + { "HP amp", NULL, "LDO2" }, + { "HPOL", NULL, "HP amp" }, + { "HPOR", NULL, "HP amp" }, + + { "LOUT amp", NULL, "LOUT MIX" }, + { "LOUTL", NULL, "LOUT amp" }, + { "LOUTR", NULL, "LOUT amp" }, + + { "PDM1 L", "Switch", "PDM1 L Mux" }, + { "PDM1 R", "Switch", "PDM1 R Mux" }, + + { "PDM1L", NULL, "PDM1 L" }, + { "PDM1R", NULL, "PDM1 R" }, + + { "SPK amp", NULL, "SPOL MIX" }, + { "SPK amp", NULL, "SPOR MIX" }, + { "SPOL", NULL, "SPK amp" }, + { "SPOR", NULL, "SPK amp" }, +}; + +static const struct snd_soc_dapm_route rt5650_specific_dapm_routes[] = { + { "A DAC1 L Mux", "DAC1", "DAC1 MIXL"}, + { "A DAC1 L Mux", "Stereo DAC Mixer", "Stereo DAC MIXL"}, + { "A DAC1 R Mux", "DAC1", "DAC1 MIXR"}, + { "A DAC1 R Mux", "Stereo DAC Mixer", "Stereo DAC MIXR"}, + + { "A DAC2 L Mux", "Stereo DAC Mixer", "Stereo DAC MIXL"}, + { "A DAC2 L Mux", "Mono DAC Mixer", "Mono DAC MIXL"}, + { "A DAC2 R Mux", "Stereo DAC Mixer", "Stereo DAC MIXR"}, + { "A DAC2 R Mux", "Mono DAC Mixer", "Mono DAC MIXR"}, + + { "DAC L1", NULL, "A DAC1 L Mux" }, + { "DAC R1", NULL, "A DAC1 R Mux" }, + { "DAC L2", NULL, "A DAC2 L Mux" }, + { "DAC R2", NULL, "A DAC2 R Mux" }, + + { "RT5650 IF1 ADC1 Swap Mux", "L/R", "IF_ADC1" }, + { "RT5650 IF1 ADC1 Swap Mux", "R/L", "IF_ADC1" }, + { "RT5650 IF1 ADC1 Swap Mux", "L/L", "IF_ADC1" }, + { "RT5650 IF1 ADC1 Swap Mux", "R/R", "IF_ADC1" }, + + { "RT5650 IF1 ADC2 Swap Mux", "L/R", "IF_ADC2" }, + { "RT5650 IF1 ADC2 Swap Mux", "R/L", "IF_ADC2" }, + { "RT5650 IF1 ADC2 Swap Mux", "L/L", "IF_ADC2" }, + { "RT5650 IF1 ADC2 Swap Mux", "R/R", "IF_ADC2" }, + + { "RT5650 IF1 ADC3 Swap Mux", "L/R", "VAD_ADC" }, + { "RT5650 IF1 ADC3 Swap Mux", "R/L", "VAD_ADC" }, + { "RT5650 IF1 ADC3 Swap Mux", "L/L", "VAD_ADC" }, + { "RT5650 IF1 ADC3 Swap Mux", "R/R", "VAD_ADC" }, + + { "IF1 ADC", NULL, "RT5650 IF1 ADC1 Swap Mux" }, + { "IF1 ADC", NULL, "RT5650 IF1 ADC2 Swap Mux" }, + { "IF1 ADC", NULL, "RT5650 IF1 ADC3 Swap Mux" }, + + { "RT5650 IF1 ADC Mux", "IF_ADC1/IF_ADC2/DAC_REF/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC1/IF_ADC2/Null/DAC_REF", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC1/DAC_REF/IF_ADC2/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC1/DAC_REF/Null/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC1/Null/DAC_REF/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC1/Null/IF_ADC2/DAC_REF", "IF1 ADC" }, + + { "RT5650 IF1 ADC Mux", "IF_ADC2/IF_ADC1/DAC_REF/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC2/IF_ADC1/Null/DAC_REF", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC2/DAC_REF/IF_ADC1/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC2/DAC_REF/Null/IF_ADC1", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC2/Null/DAC_REF/IF_ADC1", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "IF_ADC2/Null/IF_ADC1/DAC_REF", "IF1 ADC" }, + + { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC1/IF_ADC2/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC1/Null/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC2/IF_ADC1/Null", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "DAC_REF/IF_ADC2/Null/IF_ADC1", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "DAC_REF/Null/IF_ADC1/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "DAC_REF/Null/IF_ADC2/IF_ADC1", "IF1 ADC" }, + + { "RT5650 IF1 ADC Mux", "Null/IF_ADC1/IF_ADC2/DAC_REF", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "Null/IF_ADC1/DAC_REF/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "Null/IF_ADC2/IF_ADC1/DAC_REF", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "Null/IF_ADC2/DAC_REF/IF_ADC1", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "Null/DAC_REF/IF_ADC1/IF_ADC2", "IF1 ADC" }, + { "RT5650 IF1 ADC Mux", "Null/DAC_REF/IF_ADC2/IF_ADC1", "IF1 ADC" }, + { "AIF1TX", NULL, "RT5650 IF1 ADC Mux" }, + + { "RT5650 IF1 DAC1 L Mux", "Slot0", "IF1 DAC0" }, + { "RT5650 IF1 DAC1 L Mux", "Slot1", "IF1 DAC1" }, + { "RT5650 IF1 DAC1 L Mux", "Slot2", "IF1 DAC2" }, + { "RT5650 IF1 DAC1 L Mux", "Slot3", "IF1 DAC3" }, + + { "RT5650 IF1 DAC1 R Mux", "Slot0", "IF1 DAC0" }, + { "RT5650 IF1 DAC1 R Mux", "Slot1", "IF1 DAC1" }, + { "RT5650 IF1 DAC1 R Mux", "Slot2", "IF1 DAC2" }, + { "RT5650 IF1 DAC1 R Mux", "Slot3", "IF1 DAC3" }, + + { "RT5650 IF1 DAC2 L Mux", "Slot0", "IF1 DAC0" }, + { "RT5650 IF1 DAC2 L Mux", "Slot1", "IF1 DAC1" }, + { "RT5650 IF1 DAC2 L Mux", "Slot2", "IF1 DAC2" }, + { "RT5650 IF1 DAC2 L Mux", "Slot3", "IF1 DAC3" }, + + { "RT5650 IF1 DAC2 R Mux", "Slot0", "IF1 DAC0" }, + { "RT5650 IF1 DAC2 R Mux", "Slot1", "IF1 DAC1" }, + { "RT5650 IF1 DAC2 R Mux", "Slot2", "IF1 DAC2" }, + { "RT5650 IF1 DAC2 R Mux", "Slot3", "IF1 DAC3" }, + + { "DAC1 L Mux", "IF1 DAC", "RT5650 IF1 DAC1 L Mux" }, + { "DAC1 R Mux", "IF1 DAC", "RT5650 IF1 DAC1 R Mux" }, + + { "DAC L2 Mux", "IF1 DAC", "RT5650 IF1 DAC2 L Mux" }, + { "DAC R2 Mux", "IF1 DAC", "RT5650 IF1 DAC2 R Mux" }, +}; + +static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = { + { "DAC L1", NULL, "Stereo DAC MIXL" }, + { "DAC R1", NULL, "Stereo DAC MIXR" }, + { "DAC L2", NULL, "Mono DAC MIXL" }, + { "DAC R2", NULL, "Mono DAC MIXR" }, + + { "RT5645 IF1 ADC1 Swap Mux", "L/R", "IF_ADC1" }, + { "RT5645 IF1 ADC1 Swap Mux", "R/L", "IF_ADC1" }, + { "RT5645 IF1 ADC1 Swap Mux", "L/L", "IF_ADC1" }, + { "RT5645 IF1 ADC1 Swap Mux", "R/R", "IF_ADC1" }, + + { "RT5645 IF1 ADC2 Swap Mux", "L/R", "IF_ADC2" }, + { "RT5645 IF1 ADC2 Swap Mux", "R/L", "IF_ADC2" }, + { "RT5645 IF1 ADC2 Swap Mux", "L/L", "IF_ADC2" }, + { "RT5645 IF1 ADC2 Swap Mux", "R/R", "IF_ADC2" }, + + { "RT5645 IF1 ADC3 Swap Mux", "L/R", "VAD_ADC" }, + { "RT5645 IF1 ADC3 Swap Mux", "R/L", "VAD_ADC" }, + { "RT5645 IF1 ADC3 Swap Mux", "L/L", "VAD_ADC" }, + { "RT5645 IF1 ADC3 Swap Mux", "R/R", "VAD_ADC" }, + + { "IF1 ADC", NULL, "RT5645 IF1 ADC1 Swap Mux" }, + { "IF1 ADC", NULL, "RT5645 IF1 ADC2 Swap Mux" }, + { "IF1 ADC", NULL, "RT5645 IF1 ADC3 Swap Mux" }, + + { "RT5645 IF1 ADC Mux", "IF_ADC1/IF_ADC2/VAD_ADC", "IF1 ADC" }, + { "RT5645 IF1 ADC Mux", "IF_ADC2/IF_ADC1/VAD_ADC", "IF1 ADC" }, + { "RT5645 IF1 ADC Mux", "VAD_ADC/IF_ADC1/IF_ADC2", "IF1 ADC" }, + { "RT5645 IF1 ADC Mux", "VAD_ADC/IF_ADC2/IF_ADC1", "IF1 ADC" }, + { "AIF1TX", NULL, "RT5645 IF1 ADC Mux" }, + + { "RT5645 IF1 DAC1 L Mux", "Slot0", "IF1 DAC0" }, + { "RT5645 IF1 DAC1 L Mux", "Slot1", "IF1 DAC1" }, + { "RT5645 IF1 DAC1 L Mux", "Slot2", "IF1 DAC2" }, + { "RT5645 IF1 DAC1 L Mux", "Slot3", "IF1 DAC3" }, + + { "RT5645 IF1 DAC1 R Mux", "Slot0", "IF1 DAC0" }, + { "RT5645 IF1 DAC1 R Mux", "Slot1", "IF1 DAC1" }, + { "RT5645 IF1 DAC1 R Mux", "Slot2", "IF1 DAC2" }, + { "RT5645 IF1 DAC1 R Mux", "Slot3", "IF1 DAC3" }, + + { "RT5645 IF1 DAC2 L Mux", "Slot0", "IF1 DAC0" }, + { "RT5645 IF1 DAC2 L Mux", "Slot1", "IF1 DAC1" }, + { "RT5645 IF1 DAC2 L Mux", "Slot2", "IF1 DAC2" }, + { "RT5645 IF1 DAC2 L Mux", "Slot3", "IF1 DAC3" }, + + { "RT5645 IF1 DAC2 R Mux", "Slot0", "IF1 DAC0" }, + { "RT5645 IF1 DAC2 R Mux", "Slot1", "IF1 DAC1" }, + { "RT5645 IF1 DAC2 R Mux", "Slot2", "IF1 DAC2" }, + { "RT5645 IF1 DAC2 R Mux", "Slot3", "IF1 DAC3" }, + + { "DAC1 L Mux", "IF1 DAC", "RT5645 IF1 DAC1 L Mux" }, + { "DAC1 R Mux", "IF1 DAC", "RT5645 IF1 DAC1 R Mux" }, + + { "DAC L2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 L Mux" }, + { "DAC R2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 R Mux" }, +}; + +static const struct snd_soc_dapm_route rt5645_old_dapm_routes[] = { + { "SPOL MIX", "DAC R1 Switch", "DAC R1" }, + { "SPOL MIX", "SPKVOL R Switch", "SPKVOL R" }, +}; + +static int rt5645_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk, dl_sft; + int pre_div, bclk_ms, frame_size; + + rt5645->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5645->sysclk, rt5645->lrck[dai->id]); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + + switch (rt5645->codec_type) { + case CODEC_TYPE_RT5650: + dl_sft = 4; + break; + default: + dl_sft = 2; + break; + } + + bclk_ms = frame_size > 32; + rt5645->bclk[dai->id] = rt5645->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5645->bclk[dai->id], rt5645->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len = 0x1; + break; + case 24: + val_len = 0x2; + break; + case 8: + val_len = 0x3; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5645_AIF1: + mask_clk = RT5645_I2S_PD1_MASK; + val_clk = pre_div << RT5645_I2S_PD1_SFT; + snd_soc_component_update_bits(component, RT5645_I2S1_SDP, + (0x3 << dl_sft), (val_len << dl_sft)); + snd_soc_component_update_bits(component, RT5645_ADDA_CLK1, mask_clk, val_clk); + break; + case RT5645_AIF2: + mask_clk = RT5645_I2S_BCLK_MS2_MASK | RT5645_I2S_PD2_MASK; + val_clk = bclk_ms << RT5645_I2S_BCLK_MS2_SFT | + pre_div << RT5645_I2S_PD2_SFT; + snd_soc_component_update_bits(component, RT5645_I2S2_SDP, + (0x3 << dl_sft), (val_len << dl_sft)); + snd_soc_component_update_bits(component, RT5645_ADDA_CLK1, mask_clk, val_clk); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, pol_sft; + + switch (rt5645->codec_type) { + case CODEC_TYPE_RT5650: + pol_sft = 8; + break; + default: + pol_sft = 7; + break; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5645->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5645_I2S_MS_S; + rt5645->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= (1 << pol_sft); + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5645_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5645_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5645_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + switch (dai->id) { + case RT5645_AIF1: + snd_soc_component_update_bits(component, RT5645_I2S1_SDP, + RT5645_I2S_MS_MASK | (1 << pol_sft) | + RT5645_I2S_DF_MASK, reg_val); + break; + case RT5645_AIF2: + snd_soc_component_update_bits(component, RT5645_I2S2_SDP, + RT5645_I2S_MS_MASK | (1 << pol_sft) | + RT5645_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt5645->sysclk && clk_id == rt5645->sysclk_src) + return 0; + + switch (clk_id) { + case RT5645_SCLK_S_MCLK: + reg_val |= RT5645_SCLK_SRC_MCLK; + break; + case RT5645_SCLK_S_PLL1: + reg_val |= RT5645_SCLK_SRC_PLL1; + break; + case RT5645_SCLK_S_RCCLK: + reg_val |= RT5645_SCLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5645_GLB_CLK, + RT5645_SCLK_SRC_MASK, reg_val); + rt5645->sysclk = freq; + rt5645->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5645->pll_src && freq_in == rt5645->pll_in && + freq_out == rt5645->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5645->pll_in = 0; + rt5645->pll_out = 0; + snd_soc_component_update_bits(component, RT5645_GLB_CLK, + RT5645_SCLK_SRC_MASK, RT5645_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5645_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5645_GLB_CLK, + RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_MCLK); + break; + case RT5645_PLL1_S_BCLK1: + case RT5645_PLL1_S_BCLK2: + switch (dai->id) { + case RT5645_AIF1: + snd_soc_component_update_bits(component, RT5645_GLB_CLK, + RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK1); + break; + case RT5645_AIF2: + snd_soc_component_update_bits(component, RT5645_GLB_CLK, + RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK2); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5645_PLL_CTRL1, + pll_code.n_code << RT5645_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5645_PLL_CTRL2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5645_PLL_M_SFT) | + (pll_code.m_bp << RT5645_PLL_M_BP_SFT)); + + rt5645->pll_in = freq_in; + rt5645->pll_out = freq_out; + rt5645->pll_src = source; + + return 0; +} + +static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + unsigned int i_slot_sft, o_slot_sft, i_width_sht, o_width_sht, en_sft; + unsigned int mask, val = 0; + + switch (rt5645->codec_type) { + case CODEC_TYPE_RT5650: + en_sft = 15; + i_slot_sft = 10; + o_slot_sft = 8; + i_width_sht = 6; + o_width_sht = 4; + mask = 0x8ff0; + break; + default: + en_sft = 14; + i_slot_sft = o_slot_sft = 12; + i_width_sht = o_width_sht = 10; + mask = 0x7c00; + break; + } + if (rx_mask || tx_mask) { + val |= (1 << en_sft); + if (rt5645->codec_type == CODEC_TYPE_RT5645) + snd_soc_component_update_bits(component, RT5645_BASS_BACK, + RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB); + } + + switch (slots) { + case 4: + val |= (1 << i_slot_sft) | (1 << o_slot_sft); + break; + case 6: + val |= (2 << i_slot_sft) | (2 << o_slot_sft); + break; + case 8: + val |= (3 << i_slot_sft) | (3 << o_slot_sft); + break; + case 2: + default: + break; + } + + switch (slot_width) { + case 20: + val |= (1 << i_width_sht) | (1 << o_width_sht); + break; + case 24: + val |= (2 << i_width_sht) | (2 << o_width_sht); + break; + case 32: + val |= (3 << i_width_sht) | (3 << o_width_sht); + break; + case 16: + default: + break; + } + + snd_soc_component_update_bits(component, RT5645_TDM_CTRL_1, mask, val); + + return 0; +} + +static int rt5645_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) { + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_VREF1 | RT5645_PWR_MB | + RT5645_PWR_BG | RT5645_PWR_VREF2, + RT5645_PWR_VREF1 | RT5645_PWR_MB | + RT5645_PWR_BG | RT5645_PWR_VREF2); + mdelay(10); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_FV1 | RT5645_PWR_FV2, + RT5645_PWR_FV1 | RT5645_PWR_FV2); + snd_soc_component_update_bits(component, RT5645_GEN_CTRL1, + RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL); + } + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_VREF1 | RT5645_PWR_MB | + RT5645_PWR_BG | RT5645_PWR_VREF2, + RT5645_PWR_VREF1 | RT5645_PWR_MB | + RT5645_PWR_BG | RT5645_PWR_VREF2); + mdelay(10); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_FV1 | RT5645_PWR_FV2, + RT5645_PWR_FV1 | RT5645_PWR_FV2); + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140); + msleep(40); + if (rt5645->en_button_func) + queue_delayed_work(system_power_efficient_wq, + &rt5645->jack_detect_work, + msecs_to_jiffies(0)); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1100); + if (!rt5645->en_button_func) + snd_soc_component_update_bits(component, RT5645_GEN_CTRL1, + RT5645_DIG_GATE_CTRL, 0); + snd_soc_component_update_bits(component, RT5645_PWR_ANLG1, + RT5645_PWR_VREF1 | RT5645_PWR_MB | + RT5645_PWR_BG | RT5645_PWR_VREF2 | + RT5645_PWR_FV1 | RT5645_PWR_FV2, 0x0); + break; + + default: + break; + } + + return 0; +} + +static void rt5645_enable_push_button_irq(struct snd_soc_component *component, + bool enable) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + if (enable) { + snd_soc_dapm_force_enable_pin(dapm, "ADC L power"); + snd_soc_dapm_force_enable_pin(dapm, "ADC R power"); + snd_soc_dapm_sync(dapm); + + snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD1, 0x3, 0x3); + snd_soc_component_update_bits(component, + RT5645_INT_IRQ_ST, 0x8, 0x8); + snd_soc_component_update_bits(component, + RT5650_4BTN_IL_CMD2, 0x8000, 0x8000); + snd_soc_component_read(component, RT5650_4BTN_IL_CMD1); + pr_debug("%s read %x = %x\n", __func__, RT5650_4BTN_IL_CMD1, + snd_soc_component_read(component, RT5650_4BTN_IL_CMD1)); + } else { + snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD2, 0x8000, 0x0); + snd_soc_component_update_bits(component, RT5645_INT_IRQ_ST, 0x8, 0x0); + + snd_soc_dapm_disable_pin(dapm, "ADC L power"); + snd_soc_dapm_disable_pin(dapm, "ADC R power"); + snd_soc_dapm_sync(dapm); + } +} + +static int rt5645_jack_detect(struct snd_soc_component *component, int jack_insert) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (jack_insert) { + regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0206); + + /* for jack type detect */ + snd_soc_dapm_force_enable_pin(dapm, "LDO2"); + snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); + if (!snd_soc_card_is_instantiated(dapm->card)) { + /* Power up necessary bits for JD if dapm is + not ready yet */ + regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1, + RT5645_PWR_MB | RT5645_PWR_VREF2, + RT5645_PWR_MB | RT5645_PWR_VREF2); + regmap_update_bits(rt5645->regmap, RT5645_PWR_MIXER, + RT5645_PWR_LDO2, RT5645_PWR_LDO2); + regmap_update_bits(rt5645->regmap, RT5645_PWR_VOL, + RT5645_PWR_MIC_DET, RT5645_PWR_MIC_DET); + } + + regmap_write(rt5645->regmap, RT5645_JD_CTRL3, 0x00f0); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, + RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, + RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN); + msleep(100); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, + RT5645_CBJ_MN_JD, 0); + + msleep(600); + regmap_read(rt5645->regmap, RT5645_IN1_CTRL3, &val); + val &= 0x7; + dev_dbg(component->dev, "val = %d\n", val); + + if ((val == 1 || val == 2) && !rt5645->pdata.no_headset_mic) { + rt5645->jack_type = SND_JACK_HEADSET; + if (rt5645->en_button_func) { + rt5645_enable_push_button_irq(component, true); + } + } else { + if (rt5645->en_button_func) + rt5645_enable_push_button_irq(component, false); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); + rt5645->jack_type = SND_JACK_HEADPHONE; + } + if (rt5645->pdata.level_trigger_irq) + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR); + + regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06); + } else { /* jack out */ + rt5645->jack_type = 0; + + regmap_update_bits(rt5645->regmap, RT5645_HP_VOL, + RT5645_L_MUTE | RT5645_R_MUTE, + RT5645_L_MUTE | RT5645_R_MUTE); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, + RT5645_CBJ_MN_JD, RT5645_CBJ_MN_JD); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, + RT5645_CBJ_BST1_EN, 0); + + if (rt5645->en_button_func) + rt5645_enable_push_button_irq(component, false); + + if (rt5645->pdata.jd_mode == 0) + snd_soc_dapm_disable_pin(dapm, "LDO2"); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); + if (rt5645->pdata.level_trigger_irq) + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); + } + + return rt5645->jack_type; +} + +static int rt5645_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read(component, RT5650_4BTN_IL_CMD1); + pr_debug("val=0x%x\n", val); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5650_4BTN_IL_CMD1, val); + + return btn_type; +} + +static irqreturn_t rt5645_irq(int irq, void *data); + +int rt5645_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack, + struct snd_soc_jack *btn_jack) +{ + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + + rt5645->hp_jack = hp_jack; + rt5645->mic_jack = mic_jack; + rt5645->btn_jack = btn_jack; + if (rt5645->btn_jack && rt5645->codec_type == CODEC_TYPE_RT5650) { + rt5645->en_button_func = true; + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ); + regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL1, + RT5645_DIG_GATE_CTRL, RT5645_DIG_GATE_CTRL); + regmap_update_bits(rt5645->regmap, RT5645_DEPOP_M1, + RT5645_HP_CB_MASK, RT5645_HP_CB_PU); + } + rt5645_irq(0, rt5645); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5645_set_jack_detect); + +static int rt5645_component_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct snd_soc_jack *mic_jack = NULL; + struct snd_soc_jack *btn_jack = NULL; + int type; + + if (hs_jack) { + type = *(int *)data; + + if (type & SND_JACK_MICROPHONE) + mic_jack = hs_jack; + if (type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + btn_jack = hs_jack; + } + + return rt5645_set_jack_detect(component, hs_jack, mic_jack, btn_jack); +} + +static void rt5645_jack_detect_work(struct work_struct *work) +{ + struct rt5645_priv *rt5645 = + container_of(work, struct rt5645_priv, jack_detect_work.work); + int val, btn_type, gpio_state = 0, report = 0; + + if (!rt5645->component) + return; + + mutex_lock(&rt5645->jd_mutex); + + switch (rt5645->pdata.jd_mode) { + case 0: /* Not using rt5645 JD */ + if (rt5645->gpiod_hp_det) { + gpio_state = gpiod_get_value(rt5645->gpiod_hp_det); + if (rt5645->pdata.inv_hp_pol) + gpio_state ^= 1; + dev_dbg(rt5645->component->dev, "gpio_state = %d\n", + gpio_state); + report = rt5645_jack_detect(rt5645->component, gpio_state); + } + snd_soc_jack_report(rt5645->hp_jack, + report, SND_JACK_HEADPHONE); + snd_soc_jack_report(rt5645->mic_jack, + report, SND_JACK_MICROPHONE); + return; + case 4: + val = snd_soc_component_read(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020; + break; + default: /* read rt5645 jd1_1 status */ + val = snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000; + break; + + } + + if (!val && (rt5645->jack_type == 0)) { /* jack in */ + report = rt5645_jack_detect(rt5645->component, 1); + } else if (!val && rt5645->jack_type == SND_JACK_HEADSET) { + /* for push button and jack out */ + btn_type = 0; + if (snd_soc_component_read(rt5645->component, RT5645_INT_IRQ_ST) & 0x4) { + /* button pressed */ + report = SND_JACK_HEADSET; + btn_type = rt5645_button_detect(rt5645->component); + /* rt5650 can report three kinds of button behavior, + one click, double click and hold. However, + currently we will report button pressed/released + event. So all the three button behaviors are + treated as button pressed. */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + report |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + report |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + report |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + report |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + dev_err(rt5645->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + if (btn_type == 0)/* button release */ + report = rt5645->jack_type; + else { + mod_timer(&rt5645->btn_check_timer, + msecs_to_jiffies(100)); + } + } else { + /* jack out */ + report = 0; + snd_soc_component_update_bits(rt5645->component, + RT5645_INT_IRQ_ST, 0x1, 0x0); + rt5645_jack_detect(rt5645->component, 0); + } + + mutex_unlock(&rt5645->jd_mutex); + + snd_soc_jack_report(rt5645->hp_jack, report, SND_JACK_HEADPHONE); + snd_soc_jack_report(rt5645->mic_jack, report, SND_JACK_MICROPHONE); + if (rt5645->en_button_func) + snd_soc_jack_report(rt5645->btn_jack, + report, SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); +} + +static void rt5645_rcclock_work(struct work_struct *work) +{ + struct rt5645_priv *rt5645 = + container_of(work, struct rt5645_priv, rcclock_work.work); + + regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, + RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PD); +} + +static irqreturn_t rt5645_irq(int irq, void *data) +{ + struct rt5645_priv *rt5645 = data; + + queue_delayed_work(system_power_efficient_wq, + &rt5645->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static void rt5645_btn_check_callback(struct timer_list *t) +{ + struct rt5645_priv *rt5645 = from_timer(rt5645, t, btn_check_timer); + + queue_delayed_work(system_power_efficient_wq, + &rt5645->jack_detect_work, msecs_to_jiffies(5)); +} + +static int rt5645_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + + rt5645->component = component; + + switch (rt5645->codec_type) { + case CODEC_TYPE_RT5645: + snd_soc_dapm_new_controls(dapm, + rt5645_specific_dapm_widgets, + ARRAY_SIZE(rt5645_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt5645_specific_dapm_routes, + ARRAY_SIZE(rt5645_specific_dapm_routes)); + if (rt5645->v_id < 3) { + snd_soc_dapm_add_routes(dapm, + rt5645_old_dapm_routes, + ARRAY_SIZE(rt5645_old_dapm_routes)); + } + break; + case CODEC_TYPE_RT5650: + snd_soc_dapm_new_controls(dapm, + rt5650_specific_dapm_widgets, + ARRAY_SIZE(rt5650_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt5650_specific_dapm_routes, + ARRAY_SIZE(rt5650_specific_dapm_routes)); + break; + } + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + /* for JD function */ + if (rt5645->pdata.jd_mode) { + snd_soc_dapm_force_enable_pin(dapm, "JD Power"); + snd_soc_dapm_force_enable_pin(dapm, "LDO2"); + snd_soc_dapm_sync(dapm); + } + + if (rt5645->pdata.long_name) + component->card->long_name = rt5645->pdata.long_name; + + rt5645->eq_param = devm_kcalloc(component->dev, + RT5645_HWEQ_NUM, sizeof(struct rt5645_eq_param_s), + GFP_KERNEL); + + if (!rt5645->eq_param) + return -ENOMEM; + + return 0; +} + +static void rt5645_remove(struct snd_soc_component *component) +{ + rt5645_reset(component); +} + +#ifdef CONFIG_PM +static int rt5645_suspend(struct snd_soc_component *component) +{ + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5645->regmap, true); + regcache_mark_dirty(rt5645->regmap); + + return 0; +} + +static int rt5645_resume(struct snd_soc_component *component) +{ + struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5645->regmap, false); + regcache_sync(rt5645->regmap); + + return 0; +} +#else +#define rt5645_suspend NULL +#define rt5645_resume NULL +#endif + +#define RT5645_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5645_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5645_aif_dai_ops = { + .hw_params = rt5645_hw_params, + .set_fmt = rt5645_set_dai_fmt, + .set_sysclk = rt5645_set_dai_sysclk, + .set_tdm_slot = rt5645_set_tdm_slot, + .set_pll = rt5645_set_dai_pll, +}; + +static struct snd_soc_dai_driver rt5645_dai[] = { + { + .name = "rt5645-aif1", + .id = RT5645_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5645_STEREO_RATES, + .formats = RT5645_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT5645_STEREO_RATES, + .formats = RT5645_FORMATS, + }, + .ops = &rt5645_aif_dai_ops, + }, + { + .name = "rt5645-aif2", + .id = RT5645_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5645_STEREO_RATES, + .formats = RT5645_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5645_STEREO_RATES, + .formats = RT5645_FORMATS, + }, + .ops = &rt5645_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5645 = { + .probe = rt5645_probe, + .remove = rt5645_remove, + .suspend = rt5645_suspend, + .resume = rt5645_resume, + .set_bias_level = rt5645_set_bias_level, + .controls = rt5645_snd_controls, + .num_controls = ARRAY_SIZE(rt5645_snd_controls), + .dapm_widgets = rt5645_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets), + .dapm_routes = rt5645_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes), + .set_jack = rt5645_component_set_jack, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5645_regmap = { + .reg_bits = 8, + .val_bits = 16, + .use_single_read = true, + .use_single_write = true, + .max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) * + RT5645_PR_SPACING), + .volatile_reg = rt5645_volatile_register, + .readable_reg = rt5645_readable_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5645_reg, + .num_reg_defaults = ARRAY_SIZE(rt5645_reg), + .ranges = rt5645_ranges, + .num_ranges = ARRAY_SIZE(rt5645_ranges), +}; + +static const struct regmap_config rt5650_regmap = { + .reg_bits = 8, + .val_bits = 16, + .use_single_read = true, + .use_single_write = true, + .max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) * + RT5645_PR_SPACING), + .volatile_reg = rt5645_volatile_register, + .readable_reg = rt5645_readable_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5650_reg, + .num_reg_defaults = ARRAY_SIZE(rt5650_reg), + .ranges = rt5645_ranges, + .num_ranges = ARRAY_SIZE(rt5645_ranges), +}; + +static const struct regmap_config temp_regmap = { + .name="nocache", + .reg_bits = 8, + .val_bits = 16, + .use_single_read = true, + .use_single_write = true, + .max_register = RT5645_VENDOR_ID2 + 1, + .cache_type = REGCACHE_NONE, +}; + +static const struct i2c_device_id rt5645_i2c_id[] = { + { "rt5645", 0 }, + { "rt5650", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id rt5645_of_match[] = { + { .compatible = "realtek,rt5645", }, + { .compatible = "realtek,rt5650", }, + { } +}; +MODULE_DEVICE_TABLE(of, rt5645_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5645_acpi_match[] = { + { "10EC5645", 0 }, + { "10EC5648", 0 }, + { "10EC5650", 0 }, + { "10EC5640", 0 }, + { "10EC3270", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match); +#endif + +static const struct rt5645_platform_data intel_braswell_platform_data = { + .dmic1_data_pin = RT5645_DMIC1_DISABLE, + .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, + .jd_mode = 3, +}; + +static const struct rt5645_platform_data buddy_platform_data = { + .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5, + .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, + .jd_mode = 4, + .level_trigger_irq = true, +}; + +static const struct rt5645_platform_data gpd_win_platform_data = { + .jd_mode = 3, + .inv_jd1_1 = true, + .long_name = "gpd-win-pocket-rt5645", + /* The GPD pocket has a diff. mic, for the win this does not matter. */ + .in2_diff = true, +}; + +static const struct rt5645_platform_data asus_t100ha_platform_data = { + .dmic1_data_pin = RT5645_DMIC_DATA_IN2N, + .dmic2_data_pin = RT5645_DMIC2_DISABLE, + .jd_mode = 3, + .inv_jd1_1 = true, +}; + +static const struct rt5645_platform_data asus_t101ha_platform_data = { + .dmic1_data_pin = RT5645_DMIC_DATA_IN2N, + .dmic2_data_pin = RT5645_DMIC2_DISABLE, + .jd_mode = 3, +}; + +static const struct rt5645_platform_data lenovo_ideapad_miix_310_pdata = { + .jd_mode = 3, + .in2_diff = true, +}; + +static const struct rt5645_platform_data jd_mode3_platform_data = { + .jd_mode = 3, +}; + +static const struct rt5645_platform_data lattepanda_board_platform_data = { + .jd_mode = 2, + .inv_jd1_1 = true +}; + +static const struct rt5645_platform_data kahlee_platform_data = { + .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5, + .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, + .jd_mode = 3, +}; + +static const struct rt5645_platform_data ecs_ef20_platform_data = { + .dmic1_data_pin = RT5645_DMIC1_DISABLE, + .dmic2_data_pin = RT5645_DMIC_DATA_IN2P, + .inv_hp_pol = 1, +}; + +static const struct acpi_gpio_params ef20_hp_detect = { 1, 0, false }; + +static const struct acpi_gpio_mapping cht_rt5645_ef20_gpios[] = { + { "hp-detect-gpios", &ef20_hp_detect, 1 }, + { }, +}; + +static int cht_rt5645_ef20_quirk_cb(const struct dmi_system_id *id) +{ + cht_rt5645_gpios = cht_rt5645_ef20_gpios; + return 1; +} + +static const struct dmi_system_id dmi_platform_data[] = { + { + .ident = "Chrome Buddy", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"), + }, + .driver_data = (void *)&buddy_platform_data, + }, + { + .ident = "Intel Strago", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Strago"), + }, + .driver_data = (void *)&intel_braswell_platform_data, + }, + { + .ident = "Google Chrome", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + }, + .driver_data = (void *)&intel_braswell_platform_data, + }, + { + .ident = "Google Setzer", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"), + }, + .driver_data = (void *)&intel_braswell_platform_data, + }, + { + .ident = "Microsoft Surface 3", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), + }, + .driver_data = (void *)&intel_braswell_platform_data, + }, + { + /* + * Match for the GPDwin which unfortunately uses somewhat + * generic dmi strings, which is why we test for 4 strings. + * Comparing against 23 other byt/cht boards, board_vendor + * and board_name are unique to the GPDwin, where as only one + * other board has the same board_serial and 3 others have + * the same default product_name. Also the GPDwin is the + * only device to have both board_ and product_name not set. + */ + .ident = "GPD Win / Pocket", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Default string"), + DMI_MATCH(DMI_BOARD_SERIAL, "Default string"), + DMI_MATCH(DMI_PRODUCT_NAME, "Default string"), + }, + .driver_data = (void *)&gpd_win_platform_data, + }, + { + .ident = "ASUS T100HAN", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "T100HAN"), + }, + .driver_data = (void *)&asus_t100ha_platform_data, + }, + { + .ident = "ASUS T101HA", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "T101HA"), + }, + .driver_data = (void *)&asus_t101ha_platform_data, + }, + { + .ident = "MINIX Z83-4", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MINIX"), + DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), + }, + .driver_data = (void *)&jd_mode3_platform_data, + }, + { + .ident = "Teclast X80 Pro", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"), + DMI_MATCH(DMI_PRODUCT_NAME, "X80 Pro"), + }, + .driver_data = (void *)&jd_mode3_platform_data, + }, + { + .ident = "Lenovo Ideapad Miix 310", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80SG"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"), + }, + .driver_data = (void *)&lenovo_ideapad_miix_310_pdata, + }, + { + .ident = "Lenovo Ideapad Miix 320", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"), + }, + .driver_data = (void *)&intel_braswell_platform_data, + }, + { + .ident = "LattePanda board", + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + DMI_EXACT_MATCH(DMI_BOARD_VERSION, "Default string"), + }, + .driver_data = (void *)&lattepanda_board_platform_data, + }, + { + .ident = "Chrome Kahlee", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Kahlee"), + }, + .driver_data = (void *)&kahlee_platform_data, + }, + { + .ident = "Medion E1239T", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MEDION"), + DMI_MATCH(DMI_PRODUCT_NAME, "E1239T MD60568"), + }, + .driver_data = (void *)&intel_braswell_platform_data, + }, + { + .ident = "EF20", + .callback = cht_rt5645_ef20_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "EF20"), + }, + .driver_data = (void *)&ecs_ef20_platform_data, + }, + { } +}; + +static bool rt5645_check_dp(struct device *dev) +{ + if (device_property_present(dev, "realtek,in2-differential") || + device_property_present(dev, "realtek,dmic1-data-pin") || + device_property_present(dev, "realtek,dmic2-data-pin") || + device_property_present(dev, "realtek,jd-mode")) + return true; + + return false; +} + +static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev) +{ + rt5645->pdata.in2_diff = device_property_read_bool(dev, + "realtek,in2-differential"); + device_property_read_u32(dev, + "realtek,dmic1-data-pin", &rt5645->pdata.dmic1_data_pin); + device_property_read_u32(dev, + "realtek,dmic2-data-pin", &rt5645->pdata.dmic2_data_pin); + device_property_read_u32(dev, + "realtek,jd-mode", &rt5645->pdata.jd_mode); + + return 0; +} + +static int rt5645_i2c_probe(struct i2c_client *i2c) +{ + struct rt5645_platform_data *pdata = NULL; + const struct dmi_system_id *dmi_data; + struct rt5645_priv *rt5645; + int ret, i; + unsigned int val; + struct regmap *regmap; + + rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv), + GFP_KERNEL); + if (rt5645 == NULL) + return -ENOMEM; + + rt5645->i2c = i2c; + i2c_set_clientdata(i2c, rt5645); + + dmi_data = dmi_first_match(dmi_platform_data); + if (dmi_data) { + dev_info(&i2c->dev, "Detected %s platform\n", dmi_data->ident); + pdata = dmi_data->driver_data; + } + + if (pdata) + rt5645->pdata = *pdata; + else if (rt5645_check_dp(&i2c->dev)) + rt5645_parse_dt(rt5645, &i2c->dev); + else + rt5645->pdata = jd_mode3_platform_data; + + if (quirk != -1) { + rt5645->pdata.in2_diff = QUIRK_IN2_DIFF(quirk); + rt5645->pdata.level_trigger_irq = QUIRK_LEVEL_IRQ(quirk); + rt5645->pdata.inv_jd1_1 = QUIRK_INV_JD1_1(quirk); + rt5645->pdata.inv_hp_pol = QUIRK_INV_HP_POL(quirk); + rt5645->pdata.jd_mode = QUIRK_JD_MODE(quirk); + rt5645->pdata.dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk); + rt5645->pdata.dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk); + } + + if (has_acpi_companion(&i2c->dev)) { + if (cht_rt5645_gpios) { + if (devm_acpi_dev_add_driver_gpios(&i2c->dev, cht_rt5645_gpios)) + dev_dbg(&i2c->dev, "Failed to add driver gpios\n"); + } + + /* The ALC3270 package has the headset-mic pin not-connected */ + if (acpi_dev_hid_uid_match(ACPI_COMPANION(&i2c->dev), "10EC3270", NULL)) + rt5645->pdata.no_headset_mic = true; + } + + rt5645->gpiod_hp_det = devm_gpiod_get_optional(&i2c->dev, "hp-detect", + GPIOD_IN); + + if (IS_ERR(rt5645->gpiod_hp_det)) { + dev_info(&i2c->dev, "failed to initialize gpiod\n"); + ret = PTR_ERR(rt5645->gpiod_hp_det); + /* + * Continue if optional gpiod is missing, bail for all other + * errors, including -EPROBE_DEFER + */ + if (ret != -ENOENT) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(rt5645->supplies); i++) + rt5645->supplies[i].supply = rt5645_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, + ARRAY_SIZE(rt5645->supplies), + rt5645->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5645->supplies), + rt5645->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + regmap = devm_regmap_init_i2c(i2c, &temp_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n", + ret); + goto err_enable; + } + + /* + * Read after 400msec, as it is the interval required between + * read and power On. + */ + msleep(TIME_TO_POWER_MS); + ret = regmap_read(regmap, RT5645_VENDOR_ID2, &val); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read: 0x%02X\n, ret = %d", RT5645_VENDOR_ID2, ret); + goto err_enable; + } + + switch (val) { + case RT5645_DEVICE_ID: + rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap); + rt5645->codec_type = CODEC_TYPE_RT5645; + break; + case RT5650_DEVICE_ID: + rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5650_regmap); + rt5645->codec_type = CODEC_TYPE_RT5650; + break; + default: + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5645 or rt5650\n", + val); + ret = -ENODEV; + goto err_enable; + } + + if (IS_ERR(rt5645->regmap)) { + ret = PTR_ERR(rt5645->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + goto err_enable; + } + + regmap_write(rt5645->regmap, RT5645_RESET, 0); + + regmap_read(regmap, RT5645_VENDOR_ID, &val); + rt5645->v_id = val & 0xff; + + regmap_write(rt5645->regmap, RT5645_AD_DA_MIXER, 0x8080); + + ret = regmap_multi_reg_write(rt5645->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + if (rt5645->codec_type == CODEC_TYPE_RT5650) { + ret = regmap_multi_reg_write(rt5645->regmap, rt5650_init_list, + ARRAY_SIZE(rt5650_init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Apply rt5650 patch failed: %d\n", + ret); + } + + regmap_update_bits(rt5645->regmap, RT5645_CLSD_OUT_CTRL, 0xc0, 0xc0); + + if (rt5645->pdata.in2_diff) + regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL, + RT5645_IN_DF2, RT5645_IN_DF2); + + if (rt5645->pdata.dmic1_data_pin || rt5645->pdata.dmic2_data_pin) { + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP2_PIN_MASK, RT5645_GP2_PIN_DMIC1_SCL); + } + switch (rt5645->pdata.dmic1_data_pin) { + case RT5645_DMIC_DATA_IN2N: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_IN2N); + break; + + case RT5645_DMIC_DATA_GPIO5: + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_I2S2_DAC_PIN_MASK, RT5645_I2S2_DAC_PIN_GPIO); + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP5_PIN_MASK, RT5645_GP5_PIN_DMIC1_SDA); + break; + + case RT5645_DMIC_DATA_GPIO11: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_1_DP_MASK, RT5645_DMIC_1_DP_GPIO11); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP11_PIN_MASK, + RT5645_GP11_PIN_DMIC1_SDA); + break; + + default: + break; + } + + switch (rt5645->pdata.dmic2_data_pin) { + case RT5645_DMIC_DATA_IN2P: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_IN2P); + break; + + case RT5645_DMIC_DATA_GPIO6: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO6); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP6_PIN_MASK, RT5645_GP6_PIN_DMIC2_SDA); + break; + + case RT5645_DMIC_DATA_GPIO10: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO10); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP10_PIN_MASK, + RT5645_GP10_PIN_DMIC2_SDA); + break; + + case RT5645_DMIC_DATA_GPIO12: + regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1, + RT5645_DMIC_2_DP_MASK, RT5645_DMIC_2_DP_GPIO12); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP12_PIN_MASK, + RT5645_GP12_PIN_DMIC2_SDA); + break; + + default: + break; + } + + if (rt5645->pdata.jd_mode) { + regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, + RT5645_IRQ_CLK_GATE_CTRL, + RT5645_IRQ_CLK_GATE_CTRL); + regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, + RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT); + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_IRQ_JD_1_1_EN, RT5645_IRQ_JD_1_1_EN); + regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, + RT5645_JD_PSV_MODE, RT5645_JD_PSV_MODE); + regmap_update_bits(rt5645->regmap, RT5645_HPO_MIXER, + RT5645_IRQ_PSV_MODE, RT5645_IRQ_PSV_MODE); + regmap_update_bits(rt5645->regmap, RT5645_MICBIAS, + RT5645_MIC2_OVCD_EN, RT5645_MIC2_OVCD_EN); + regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1, + RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ); + switch (rt5645->pdata.jd_mode) { + case 1: + regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1, + RT5645_JD1_MODE_MASK, + RT5645_JD1_MODE_0); + break; + case 2: + regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1, + RT5645_JD1_MODE_MASK, + RT5645_JD1_MODE_1); + break; + case 3: + case 4: + regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1, + RT5645_JD1_MODE_MASK, + RT5645_JD1_MODE_2); + break; + default: + break; + } + if (rt5645->pdata.inv_jd1_1) { + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); + } + } + + regmap_update_bits(rt5645->regmap, RT5645_ADDA_CLK1, + RT5645_I2S_PD1_MASK, RT5645_I2S_PD1_2); + + if (rt5645->pdata.level_trigger_irq) { + regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, + RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); + } + timer_setup(&rt5645->btn_check_timer, rt5645_btn_check_callback, 0); + + mutex_init(&rt5645->jd_mutex); + INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work); + INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work); + + if (rt5645->i2c->irq) { + ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5645", rt5645); + if (ret) { + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + goto err_enable; + } + } + + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5645, + rt5645_dai, ARRAY_SIZE(rt5645_dai)); + if (ret) + goto err_irq; + + return 0; + +err_irq: + if (rt5645->i2c->irq) + free_irq(rt5645->i2c->irq, rt5645); +err_enable: + regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies); + return ret; +} + +static void rt5645_i2c_remove(struct i2c_client *i2c) +{ + struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, rt5645); + + /* + * Since the rt5645_btn_check_callback() can queue jack_detect_work, + * the timer need to be delted first + */ + del_timer_sync(&rt5645->btn_check_timer); + + cancel_delayed_work_sync(&rt5645->jack_detect_work); + cancel_delayed_work_sync(&rt5645->rcclock_work); + + regulator_bulk_disable(ARRAY_SIZE(rt5645->supplies), rt5645->supplies); +} + +static void rt5645_i2c_shutdown(struct i2c_client *i2c) +{ + struct rt5645_priv *rt5645 = i2c_get_clientdata(i2c); + + regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3, + RT5645_RING2_SLEEVE_GND, RT5645_RING2_SLEEVE_GND); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL2, RT5645_CBJ_MN_JD, + RT5645_CBJ_MN_JD); + regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, RT5645_CBJ_BST1_EN, + 0); + msleep(20); + regmap_write(rt5645->regmap, RT5645_RESET, 0); +} + +static int __maybe_unused rt5645_sys_suspend(struct device *dev) +{ + struct rt5645_priv *rt5645 = dev_get_drvdata(dev); + + del_timer_sync(&rt5645->btn_check_timer); + cancel_delayed_work_sync(&rt5645->jack_detect_work); + cancel_delayed_work_sync(&rt5645->rcclock_work); + + regcache_cache_only(rt5645->regmap, true); + regcache_mark_dirty(rt5645->regmap); + return 0; +} + +static int __maybe_unused rt5645_sys_resume(struct device *dev) +{ + struct rt5645_priv *rt5645 = dev_get_drvdata(dev); + + regcache_cache_only(rt5645->regmap, false); + regcache_sync(rt5645->regmap); + + if (rt5645->hp_jack) { + rt5645->jack_type = 0; + rt5645_jack_detect_work(&rt5645->jack_detect_work.work); + } + return 0; +} + +static const struct dev_pm_ops rt5645_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt5645_sys_suspend, rt5645_sys_resume) +}; + +static struct i2c_driver rt5645_i2c_driver = { + .driver = { + .name = "rt5645", + .of_match_table = of_match_ptr(rt5645_of_match), + .acpi_match_table = ACPI_PTR(rt5645_acpi_match), + .pm = &rt5645_pm, + }, + .probe = rt5645_i2c_probe, + .remove = rt5645_i2c_remove, + .shutdown = rt5645_i2c_shutdown, + .id_table = rt5645_i2c_id, +}; +module_i2c_driver(rt5645_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5645 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h new file mode 100644 index 0000000000..ac3de6f3bc --- /dev/null +++ b/sound/soc/codecs/rt5645.h @@ -0,0 +1,2204 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5645.h -- RT5645 ALSA SoC audio driver + * + * Copyright 2013 Realtek Microelectronics + * Author: Bard Liao + */ + +#ifndef __RT5645_H__ +#define __RT5645_H__ + +/* Info */ +#define RT5645_RESET 0x00 +#define RT5645_VENDOR_ID 0xfd +#define RT5645_VENDOR_ID1 0xfe +#define RT5645_VENDOR_ID2 0xff +/* I/O - Output */ +#define RT5645_SPK_VOL 0x01 +#define RT5645_HP_VOL 0x02 +#define RT5645_LOUT1 0x03 +#define RT5645_LOUT_CTRL 0x05 +/* I/O - Input */ +#define RT5645_IN1_CTRL1 0x0a +#define RT5645_IN1_CTRL2 0x0b +#define RT5645_IN1_CTRL3 0x0c +#define RT5645_IN2_CTRL 0x0d +#define RT5645_INL1_INR1_VOL 0x0f +#define RT5645_SPK_FUNC_LIM 0x14 +#define RT5645_ADJ_HPF_CTRL 0x16 +/* I/O - ADC/DAC/DMIC */ +#define RT5645_DAC1_DIG_VOL 0x19 +#define RT5645_DAC2_DIG_VOL 0x1a +#define RT5645_DAC_CTRL 0x1b +#define RT5645_STO1_ADC_DIG_VOL 0x1c +#define RT5645_MONO_ADC_DIG_VOL 0x1d +#define RT5645_ADC_BST_VOL1 0x1e +#define RT5645_ADC_BST_VOL2 0x20 +/* Mixer - D-D */ +#define RT5645_STO1_ADC_MIXER 0x27 +#define RT5645_MONO_ADC_MIXER 0x28 +#define RT5645_AD_DA_MIXER 0x29 +#define RT5645_STO_DAC_MIXER 0x2a +#define RT5645_MONO_DAC_MIXER 0x2b +#define RT5645_DIG_MIXER 0x2c +#define RT5650_A_DAC_SOUR 0x2d +#define RT5645_DIG_INF1_DATA 0x2f +/* Mixer - PDM */ +#define RT5645_PDM_OUT_CTRL 0x31 +/* Mixer - ADC */ +#define RT5645_REC_L1_MIXER 0x3b +#define RT5645_REC_L2_MIXER 0x3c +#define RT5645_REC_R1_MIXER 0x3d +#define RT5645_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5645_HPMIXL_CTRL 0x3f +#define RT5645_HPOMIXL_CTRL 0x40 +#define RT5645_HPMIXR_CTRL 0x41 +#define RT5645_HPOMIXR_CTRL 0x42 +#define RT5645_HPO_MIXER 0x45 +#define RT5645_SPK_L_MIXER 0x46 +#define RT5645_SPK_R_MIXER 0x47 +#define RT5645_SPO_MIXER 0x48 +#define RT5645_SPO_CLSD_RATIO 0x4a +#define RT5645_OUT_L_GAIN1 0x4d +#define RT5645_OUT_L_GAIN2 0x4e +#define RT5645_OUT_L1_MIXER 0x4f +#define RT5645_OUT_R_GAIN1 0x50 +#define RT5645_OUT_R_GAIN2 0x51 +#define RT5645_OUT_R1_MIXER 0x52 +#define RT5645_LOUT_MIXER 0x53 +/* Haptic */ +#define RT5645_HAPTIC_CTRL1 0x56 +#define RT5645_HAPTIC_CTRL2 0x57 +#define RT5645_HAPTIC_CTRL3 0x58 +#define RT5645_HAPTIC_CTRL4 0x59 +#define RT5645_HAPTIC_CTRL5 0x5a +#define RT5645_HAPTIC_CTRL6 0x5b +#define RT5645_HAPTIC_CTRL7 0x5c +#define RT5645_HAPTIC_CTRL8 0x5d +#define RT5645_HAPTIC_CTRL9 0x5e +#define RT5645_HAPTIC_CTRL10 0x5f +/* Power */ +#define RT5645_PWR_DIG1 0x61 +#define RT5645_PWR_DIG2 0x62 +#define RT5645_PWR_ANLG1 0x63 +#define RT5645_PWR_ANLG2 0x64 +#define RT5645_PWR_MIXER 0x65 +#define RT5645_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5645_PRIV_INDEX 0x6a +#define RT5645_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5645_I2S1_SDP 0x70 +#define RT5645_I2S2_SDP 0x71 +#define RT5645_ADDA_CLK1 0x73 +#define RT5645_ADDA_CLK2 0x74 +#define RT5645_DMIC_CTRL1 0x75 +#define RT5645_DMIC_CTRL2 0x76 +/* Format - TDM Control */ +#define RT5645_TDM_CTRL_1 0x77 +#define RT5645_TDM_CTRL_2 0x78 +#define RT5645_TDM_CTRL_3 0x79 +#define RT5650_TDM_CTRL_4 0x7a + +/* Function - Analog */ +#define RT5645_GLB_CLK 0x80 +#define RT5645_PLL_CTRL1 0x81 +#define RT5645_PLL_CTRL2 0x82 +#define RT5645_ASRC_1 0x83 +#define RT5645_ASRC_2 0x84 +#define RT5645_ASRC_3 0x85 +#define RT5645_ASRC_4 0x8a +#define RT5645_DEPOP_M1 0x8e +#define RT5645_DEPOP_M2 0x8f +#define RT5645_DEPOP_M3 0x90 +#define RT5645_CHARGE_PUMP 0x91 +#define RT5645_MICBIAS 0x93 +#define RT5645_A_JD_CTRL1 0x94 +#define RT5645_VAD_CTRL4 0x9d +#define RT5645_CLSD_OUT_CTRL 0xa0 +/* Function - Digital */ +#define RT5645_ADC_EQ_CTRL1 0xae +#define RT5645_ADC_EQ_CTRL2 0xaf +#define RT5645_EQ_CTRL1 0xb0 +#define RT5645_EQ_CTRL2 0xb1 +#define RT5645_ALC_CTRL_1 0xb3 +#define RT5645_ALC_CTRL_2 0xb4 +#define RT5645_ALC_CTRL_3 0xb5 +#define RT5645_ALC_CTRL_4 0xb6 +#define RT5645_ALC_CTRL_5 0xb7 +#define RT5645_JD_CTRL 0xbb +#define RT5645_IRQ_CTRL1 0xbc +#define RT5645_IRQ_CTRL2 0xbd +#define RT5645_IRQ_CTRL3 0xbe +#define RT5645_INT_IRQ_ST 0xbf +#define RT5645_GPIO_CTRL1 0xc0 +#define RT5645_GPIO_CTRL2 0xc1 +#define RT5645_GPIO_CTRL3 0xc2 +#define RT5645_BASS_BACK 0xcf +#define RT5645_MP3_PLUS1 0xd0 +#define RT5645_MP3_PLUS2 0xd1 +#define RT5645_ADJ_HPF1 0xd3 +#define RT5645_ADJ_HPF2 0xd4 +#define RT5645_HP_CALIB_AMP_DET 0xd6 +#define RT5645_SV_ZCD1 0xd9 +#define RT5645_SV_ZCD2 0xda +#define RT5645_IL_CMD 0xdb +#define RT5645_IL_CMD2 0xdc +#define RT5645_IL_CMD3 0xdd +#define RT5650_4BTN_IL_CMD1 0xdf +#define RT5650_4BTN_IL_CMD2 0xe0 +#define RT5645_DRC1_HL_CTRL1 0xe7 +#define RT5645_DRC2_HL_CTRL1 0xe9 +#define RT5645_MUTI_DRC_CTRL1 0xea +#define RT5645_ADC_MONO_HP_CTRL1 0xec +#define RT5645_ADC_MONO_HP_CTRL2 0xed +#define RT5645_DRC2_CTRL1 0xf0 +#define RT5645_DRC2_CTRL2 0xf1 +#define RT5645_DRC2_CTRL3 0xf2 +#define RT5645_DRC2_CTRL4 0xf3 +#define RT5645_DRC2_CTRL5 0xf4 +#define RT5645_JD_CTRL3 0xf8 +#define RT5645_JD_CTRL4 0xf9 +/* General Control */ +#define RT5645_GEN_CTRL1 0xfa +#define RT5645_GEN_CTRL2 0xfb +#define RT5645_GEN_CTRL3 0xfc + + +/* Index of Codec Private Register definition */ +#define RT5645_DIG_VOL 0x00 +#define RT5645_PR_ALC_CTRL_1 0x01 +#define RT5645_PR_ALC_CTRL_2 0x02 +#define RT5645_PR_ALC_CTRL_3 0x03 +#define RT5645_PR_ALC_CTRL_4 0x04 +#define RT5645_PR_ALC_CTRL_5 0x05 +#define RT5645_PR_ALC_CTRL_6 0x06 +#define RT5645_BIAS_CUR1 0x12 +#define RT5645_BIAS_CUR3 0x14 +#define RT5645_CLSD_INT_REG1 0x1c +#define RT5645_MAMP_INT_REG2 0x37 +#define RT5645_CHOP_DAC_ADC 0x3d +#define RT5645_MIXER_INT_REG 0x3f +#define RT5645_3D_SPK 0x63 +#define RT5645_WND_1 0x6c +#define RT5645_WND_2 0x6d +#define RT5645_WND_3 0x6e +#define RT5645_WND_4 0x6f +#define RT5645_WND_5 0x70 +#define RT5645_WND_8 0x73 +#define RT5645_DIP_SPK_INF 0x75 +#define RT5645_HP_DCC_INT1 0x77 +#define RT5645_EQ_BW_LOP 0xa0 +#define RT5645_EQ_GN_LOP 0xa1 +#define RT5645_EQ_FC_BP1 0xa2 +#define RT5645_EQ_BW_BP1 0xa3 +#define RT5645_EQ_GN_BP1 0xa4 +#define RT5645_EQ_FC_BP2 0xa5 +#define RT5645_EQ_BW_BP2 0xa6 +#define RT5645_EQ_GN_BP2 0xa7 +#define RT5645_EQ_FC_BP3 0xa8 +#define RT5645_EQ_BW_BP3 0xa9 +#define RT5645_EQ_GN_BP3 0xaa +#define RT5645_EQ_FC_BP4 0xab +#define RT5645_EQ_BW_BP4 0xac +#define RT5645_EQ_GN_BP4 0xad +#define RT5645_EQ_FC_HIP1 0xae +#define RT5645_EQ_GN_HIP1 0xaf +#define RT5645_EQ_FC_HIP2 0xb0 +#define RT5645_EQ_BW_HIP2 0xb1 +#define RT5645_EQ_GN_HIP2 0xb2 +#define RT5645_EQ_PRE_VOL 0xb3 +#define RT5645_EQ_PST_VOL 0xb4 + + +/* global definition */ +#define RT5645_L_MUTE (0x1 << 15) +#define RT5645_L_MUTE_SFT 15 +#define RT5645_VOL_L_MUTE (0x1 << 14) +#define RT5645_VOL_L_SFT 14 +#define RT5645_R_MUTE (0x1 << 7) +#define RT5645_R_MUTE_SFT 7 +#define RT5645_VOL_R_MUTE (0x1 << 6) +#define RT5645_VOL_R_SFT 6 +#define RT5645_L_VOL_MASK (0x3f << 8) +#define RT5645_L_VOL_SFT 8 +#define RT5645_R_VOL_MASK (0x3f) +#define RT5645_R_VOL_SFT 0 + +/* IN1 Control 1 (0x0a) */ +#define RT5645_CBJ_BST1_MASK (0xf << 12) +#define RT5645_CBJ_BST1_SFT (12) +#define RT5645_CBJ_JD_HP_EN (0x1 << 9) +#define RT5645_CBJ_JD_MIC_EN (0x1 << 8) +#define RT5645_CBJ_JD_MIC_SW_EN (0x1 << 7) +#define RT5645_CBJ_MIC_SEL_R (0x1 << 6) +#define RT5645_CBJ_MIC_SEL_L (0x1 << 5) +#define RT5645_CBJ_MIC_SW (0x1 << 4) +#define RT5645_CBJ_BST1_EN (0x1 << 2) + +/* IN1 Control 2 (0x0b) */ +#define RT5645_CBJ_MN_JD (0x1 << 12) +#define RT5645_CAPLESS_EN (0x1 << 11) +#define RT5645_CBJ_DET_MODE (0x1 << 7) + +/* IN1 Control 3 (0x0c) */ +#define RT5645_CBJ_TIE_G_L (0x1 << 15) +#define RT5645_CBJ_TIE_G_R (0x1 << 14) + +/* IN2 Control (0x0d) */ +#define RT5645_BST_MASK1 (0xf<<12) +#define RT5645_BST_SFT1 12 +#define RT5645_BST_MASK2 (0xf<<8) +#define RT5645_BST_SFT2 8 +#define RT5645_IN_DF2 (0x1 << 6) +#define RT5645_IN_SFT2 6 + +/* INL and INR Volume Control (0x0f) */ +#define RT5645_INL_SEL_MASK (0x1 << 15) +#define RT5645_INL_SEL_SFT 15 +#define RT5645_INL_SEL_IN4P (0x0 << 15) +#define RT5645_INL_SEL_MONOP (0x1 << 15) +#define RT5645_INL_VOL_MASK (0x1f << 8) +#define RT5645_INL_VOL_SFT 8 +#define RT5645_INR_SEL_MASK (0x1 << 7) +#define RT5645_INR_SEL_SFT 7 +#define RT5645_INR_SEL_IN4N (0x0 << 7) +#define RT5645_INR_SEL_MONON (0x1 << 7) +#define RT5645_INR_VOL_MASK (0x1f) +#define RT5645_INR_VOL_SFT 0 + +/* DAC1 Digital Volume (0x19) */ +#define RT5645_DAC_L1_VOL_MASK (0xff << 8) +#define RT5645_DAC_L1_VOL_SFT 8 +#define RT5645_DAC_R1_VOL_MASK (0xff) +#define RT5645_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x1a) */ +#define RT5645_DAC_L2_VOL_MASK (0xff << 8) +#define RT5645_DAC_L2_VOL_SFT 8 +#define RT5645_DAC_R2_VOL_MASK (0xff) +#define RT5645_DAC_R2_VOL_SFT 0 + +/* DAC2 Control (0x1b) */ +#define RT5645_M_DAC_L2_VOL (0x1 << 13) +#define RT5645_M_DAC_L2_VOL_SFT 13 +#define RT5645_M_DAC_R2_VOL (0x1 << 12) +#define RT5645_M_DAC_R2_VOL_SFT 12 +#define RT5645_DAC2_L_SEL_MASK (0x7 << 4) +#define RT5645_DAC2_L_SEL_SFT 4 +#define RT5645_DAC2_R_SEL_MASK (0x7 << 0) +#define RT5645_DAC2_R_SEL_SFT 0 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5645_ADC_L_VOL_MASK (0x7f << 8) +#define RT5645_ADC_L_VOL_SFT 8 +#define RT5645_ADC_R_VOL_MASK (0x7f) +#define RT5645_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x1d) */ +#define RT5645_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5645_MONO_ADC_L_VOL_SFT 8 +#define RT5645_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5645_MONO_ADC_R_VOL_SFT 0 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5645_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5645_STO1_ADC_L_BST_SFT 14 +#define RT5645_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5645_STO1_ADC_R_BST_SFT 12 +#define RT5645_STO1_ADC_COMP_MASK (0x3 << 10) +#define RT5645_STO1_ADC_COMP_SFT 10 + +/* ADC Boost Volume Control (0x20) */ +#define RT5645_MONO_ADC_L_BST_MASK (0x3 << 14) +#define RT5645_MONO_ADC_L_BST_SFT 14 +#define RT5645_MONO_ADC_R_BST_MASK (0x3 << 12) +#define RT5645_MONO_ADC_R_BST_SFT 12 +#define RT5645_MONO_ADC_COMP_MASK (0x3 << 10) +#define RT5645_MONO_ADC_COMP_SFT 10 + +/* Stereo2 ADC Mixer Control (0x26) */ +#define RT5645_STO2_ADC_SRC_MASK (0x1 << 15) +#define RT5645_STO2_ADC_SRC_SFT 15 + +/* Stereo ADC Mixer Control (0x27) */ +#define RT5645_M_ADC_L1 (0x1 << 14) +#define RT5645_M_ADC_L1_SFT 14 +#define RT5645_M_ADC_L2 (0x1 << 13) +#define RT5645_M_ADC_L2_SFT 13 +#define RT5645_ADC_1_SRC_MASK (0x1 << 12) +#define RT5645_ADC_1_SRC_SFT 12 +#define RT5645_ADC_1_SRC_ADC (0x1 << 12) +#define RT5645_ADC_1_SRC_DACMIX (0x0 << 12) +#define RT5645_ADC_2_SRC_MASK (0x1 << 11) +#define RT5645_ADC_2_SRC_SFT 11 +#define RT5645_DMIC_SRC_MASK (0x1 << 8) +#define RT5645_DMIC_SRC_SFT 8 +#define RT5645_M_ADC_R1 (0x1 << 6) +#define RT5645_M_ADC_R1_SFT 6 +#define RT5645_M_ADC_R2 (0x1 << 5) +#define RT5645_M_ADC_R2_SFT 5 +#define RT5645_DMIC3_SRC_MASK (0x1 << 1) +#define RT5645_DMIC3_SRC_SFT 0 + +/* Mono ADC Mixer Control (0x28) */ +#define RT5645_M_MONO_ADC_L1 (0x1 << 14) +#define RT5645_M_MONO_ADC_L1_SFT 14 +#define RT5645_M_MONO_ADC_L2 (0x1 << 13) +#define RT5645_M_MONO_ADC_L2_SFT 13 +#define RT5645_MONO_ADC_L1_SRC_MASK (0x1 << 12) +#define RT5645_MONO_ADC_L1_SRC_SFT 12 +#define RT5645_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12) +#define RT5645_MONO_ADC_L1_SRC_ADCL (0x1 << 12) +#define RT5645_MONO_ADC_L2_SRC_MASK (0x1 << 11) +#define RT5645_MONO_ADC_L2_SRC_SFT 11 +#define RT5645_MONO_DMIC_L_SRC_MASK (0x1 << 8) +#define RT5645_MONO_DMIC_L_SRC_SFT 8 +#define RT5645_M_MONO_ADC_R1 (0x1 << 6) +#define RT5645_M_MONO_ADC_R1_SFT 6 +#define RT5645_M_MONO_ADC_R2 (0x1 << 5) +#define RT5645_M_MONO_ADC_R2_SFT 5 +#define RT5645_MONO_ADC_R1_SRC_MASK (0x1 << 4) +#define RT5645_MONO_ADC_R1_SRC_SFT 4 +#define RT5645_MONO_ADC_R1_SRC_ADCR (0x1 << 4) +#define RT5645_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4) +#define RT5645_MONO_ADC_R2_SRC_MASK (0x1 << 3) +#define RT5645_MONO_ADC_R2_SRC_SFT 3 +#define RT5645_MONO_DMIC_R_SRC_MASK (0x3) +#define RT5645_MONO_DMIC_R_SRC_SFT 0 + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5645_M_ADCMIX_L (0x1 << 15) +#define RT5645_M_ADCMIX_L_SFT 15 +#define RT5645_M_DAC1_L (0x1 << 14) +#define RT5645_M_DAC1_L_SFT 14 +#define RT5645_DAC1_R_SEL_MASK (0x3 << 10) +#define RT5645_DAC1_R_SEL_SFT 10 +#define RT5645_DAC1_R_SEL_IF1 (0x0 << 10) +#define RT5645_DAC1_R_SEL_IF2 (0x1 << 10) +#define RT5645_DAC1_R_SEL_IF3 (0x2 << 10) +#define RT5645_DAC1_R_SEL_IF4 (0x3 << 10) +#define RT5645_DAC1_L_SEL_MASK (0x3 << 8) +#define RT5645_DAC1_L_SEL_SFT 8 +#define RT5645_DAC1_L_SEL_IF1 (0x0 << 8) +#define RT5645_DAC1_L_SEL_IF2 (0x1 << 8) +#define RT5645_DAC1_L_SEL_IF3 (0x2 << 8) +#define RT5645_DAC1_L_SEL_IF4 (0x3 << 8) +#define RT5645_M_ADCMIX_R (0x1 << 7) +#define RT5645_M_ADCMIX_R_SFT 7 +#define RT5645_M_DAC1_R (0x1 << 6) +#define RT5645_M_DAC1_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5645_M_DAC_L1 (0x1 << 14) +#define RT5645_M_DAC_L1_SFT 14 +#define RT5645_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5645_DAC_L1_STO_L_VOL_SFT 13 +#define RT5645_M_DAC_L2 (0x1 << 12) +#define RT5645_M_DAC_L2_SFT 12 +#define RT5645_DAC_L2_STO_L_VOL_MASK (0x1 << 11) +#define RT5645_DAC_L2_STO_L_VOL_SFT 11 +#define RT5645_M_ANC_DAC_L (0x1 << 10) +#define RT5645_M_ANC_DAC_L_SFT 10 +#define RT5645_M_DAC_R1_STO_L (0x1 << 9) +#define RT5645_M_DAC_R1_STO_L_SFT 9 +#define RT5645_DAC_R1_STO_L_VOL_MASK (0x1 << 8) +#define RT5645_DAC_R1_STO_L_VOL_SFT 8 +#define RT5645_M_DAC_R1 (0x1 << 6) +#define RT5645_M_DAC_R1_SFT 6 +#define RT5645_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5645_DAC_R1_STO_R_VOL_SFT 5 +#define RT5645_M_DAC_R2 (0x1 << 4) +#define RT5645_M_DAC_R2_SFT 4 +#define RT5645_DAC_R2_STO_R_VOL_MASK (0x1 << 3) +#define RT5645_DAC_R2_STO_R_VOL_SFT 3 +#define RT5645_M_ANC_DAC_R (0x1 << 2) +#define RT5645_M_ANC_DAC_R_SFT 2 +#define RT5645_M_DAC_L1_STO_R (0x1 << 1) +#define RT5645_M_DAC_L1_STO_R_SFT 1 +#define RT5645_DAC_L1_STO_R_VOL_MASK (0x1) +#define RT5645_DAC_L1_STO_R_VOL_SFT 0 + +/* Mono DAC Mixer Control (0x2b) */ +#define RT5645_M_DAC_L1_MONO_L (0x1 << 14) +#define RT5645_M_DAC_L1_MONO_L_SFT 14 +#define RT5645_DAC_L1_MONO_L_VOL_MASK (0x1 << 13) +#define RT5645_DAC_L1_MONO_L_VOL_SFT 13 +#define RT5645_M_DAC_L2_MONO_L (0x1 << 12) +#define RT5645_M_DAC_L2_MONO_L_SFT 12 +#define RT5645_DAC_L2_MONO_L_VOL_MASK (0x1 << 11) +#define RT5645_DAC_L2_MONO_L_VOL_SFT 11 +#define RT5645_M_DAC_R2_MONO_L (0x1 << 10) +#define RT5645_M_DAC_R2_MONO_L_SFT 10 +#define RT5645_DAC_R2_MONO_L_VOL_MASK (0x1 << 9) +#define RT5645_DAC_R2_MONO_L_VOL_SFT 9 +#define RT5645_M_DAC_R1_MONO_R (0x1 << 6) +#define RT5645_M_DAC_R1_MONO_R_SFT 6 +#define RT5645_DAC_R1_MONO_R_VOL_MASK (0x1 << 5) +#define RT5645_DAC_R1_MONO_R_VOL_SFT 5 +#define RT5645_M_DAC_R2_MONO_R (0x1 << 4) +#define RT5645_M_DAC_R2_MONO_R_SFT 4 +#define RT5645_DAC_R2_MONO_R_VOL_MASK (0x1 << 3) +#define RT5645_DAC_R2_MONO_R_VOL_SFT 3 +#define RT5645_M_DAC_L2_MONO_R (0x1 << 2) +#define RT5645_M_DAC_L2_MONO_R_SFT 2 +#define RT5645_DAC_L2_MONO_R_VOL_MASK (0x1 << 1) +#define RT5645_DAC_L2_MONO_R_VOL_SFT 1 + +/* Digital Mixer Control (0x2c) */ +#define RT5645_M_STO_L_DAC_L (0x1 << 15) +#define RT5645_M_STO_L_DAC_L_SFT 15 +#define RT5645_STO_L_DAC_L_VOL_MASK (0x1 << 14) +#define RT5645_STO_L_DAC_L_VOL_SFT 14 +#define RT5645_M_DAC_L2_DAC_L (0x1 << 13) +#define RT5645_M_DAC_L2_DAC_L_SFT 13 +#define RT5645_DAC_L2_DAC_L_VOL_MASK (0x1 << 12) +#define RT5645_DAC_L2_DAC_L_VOL_SFT 12 +#define RT5645_M_STO_R_DAC_R (0x1 << 11) +#define RT5645_M_STO_R_DAC_R_SFT 11 +#define RT5645_STO_R_DAC_R_VOL_MASK (0x1 << 10) +#define RT5645_STO_R_DAC_R_VOL_SFT 10 +#define RT5645_M_DAC_R2_DAC_R (0x1 << 9) +#define RT5645_M_DAC_R2_DAC_R_SFT 9 +#define RT5645_DAC_R2_DAC_R_VOL_MASK (0x1 << 8) +#define RT5645_DAC_R2_DAC_R_VOL_SFT 8 +#define RT5645_M_DAC_R2_DAC_L (0x1 << 7) +#define RT5645_M_DAC_R2_DAC_L_SFT 7 +#define RT5645_DAC_R2_DAC_L_VOL_MASK (0x1 << 6) +#define RT5645_DAC_R2_DAC_L_VOL_SFT 6 +#define RT5645_M_DAC_L2_DAC_R (0x1 << 5) +#define RT5645_M_DAC_L2_DAC_R_SFT 5 +#define RT5645_DAC_L2_DAC_R_VOL_MASK (0x1 << 4) +#define RT5645_DAC_L2_DAC_R_VOL_SFT 4 + +/* Analog DAC1/2 Input Source Control (0x2d) */ +#define RT5650_A_DAC1_L_IN_SFT 3 +#define RT5650_A_DAC1_R_IN_SFT 2 +#define RT5650_A_DAC2_L_IN_SFT 1 +#define RT5650_A_DAC2_R_IN_SFT 0 + +/* Digital Interface Data Control (0x2f) */ +#define RT5645_IF1_ADC2_IN_SEL (0x1 << 15) +#define RT5645_IF1_ADC2_IN_SFT 15 +#define RT5645_IF2_ADC_IN_MASK (0x7 << 12) +#define RT5645_IF2_ADC_IN_SFT 12 +#define RT5645_IF2_DAC_SEL_MASK (0x3 << 10) +#define RT5645_IF2_DAC_SEL_SFT 10 +#define RT5645_IF2_ADC_SEL_MASK (0x3 << 8) +#define RT5645_IF2_ADC_SEL_SFT 8 +#define RT5645_IF3_DAC_SEL_MASK (0x3 << 6) +#define RT5645_IF3_DAC_SEL_SFT 6 +#define RT5645_IF3_ADC_SEL_MASK (0x3 << 4) +#define RT5645_IF3_ADC_SEL_SFT 4 +#define RT5645_IF3_ADC_IN_MASK (0x7) +#define RT5645_IF3_ADC_IN_SFT 0 + +/* PDM Output Control (0x31) */ +#define RT5645_PDM1_L_MASK (0x1 << 15) +#define RT5645_PDM1_L_SFT 15 +#define RT5645_M_PDM1_L (0x1 << 14) +#define RT5645_M_PDM1_L_SFT 14 +#define RT5645_PDM1_R_MASK (0x1 << 13) +#define RT5645_PDM1_R_SFT 13 +#define RT5645_M_PDM1_R (0x1 << 12) +#define RT5645_M_PDM1_R_SFT 12 +#define RT5645_PDM2_L_MASK (0x1 << 11) +#define RT5645_PDM2_L_SFT 11 +#define RT5645_M_PDM2_L (0x1 << 10) +#define RT5645_M_PDM2_L_SFT 10 +#define RT5645_PDM2_R_MASK (0x1 << 9) +#define RT5645_PDM2_R_SFT 9 +#define RT5645_M_PDM2_R (0x1 << 8) +#define RT5645_M_PDM2_R_SFT 8 +#define RT5645_PDM2_BUSY (0x1 << 7) +#define RT5645_PDM1_BUSY (0x1 << 6) +#define RT5645_PDM_PATTERN (0x1 << 5) +#define RT5645_PDM_GAIN (0x1 << 4) +#define RT5645_PDM_DIV_MASK (0x3) + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5645_G_HP_L_RM_L_MASK (0x7 << 13) +#define RT5645_G_HP_L_RM_L_SFT 13 +#define RT5645_G_IN_L_RM_L_MASK (0x7 << 10) +#define RT5645_G_IN_L_RM_L_SFT 10 +#define RT5645_G_BST4_RM_L_MASK (0x7 << 7) +#define RT5645_G_BST4_RM_L_SFT 7 +#define RT5645_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5645_G_BST3_RM_L_SFT 4 +#define RT5645_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5645_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5645_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5645_G_BST1_RM_L_SFT 13 +#define RT5645_G_OM_L_RM_L_MASK (0x7 << 10) +#define RT5645_G_OM_L_RM_L_SFT 10 +#define RT5645_M_MM_L_RM_L (0x1 << 6) +#define RT5645_M_MM_L_RM_L_SFT 6 +#define RT5645_M_IN_L_RM_L (0x1 << 5) +#define RT5645_M_IN_L_RM_L_SFT 5 +#define RT5645_M_HP_L_RM_L (0x1 << 4) +#define RT5645_M_HP_L_RM_L_SFT 4 +#define RT5645_M_BST3_RM_L (0x1 << 3) +#define RT5645_M_BST3_RM_L_SFT 3 +#define RT5645_M_BST2_RM_L (0x1 << 2) +#define RT5645_M_BST2_RM_L_SFT 2 +#define RT5645_M_BST1_RM_L (0x1 << 1) +#define RT5645_M_BST1_RM_L_SFT 1 +#define RT5645_M_OM_L_RM_L (0x1) +#define RT5645_M_OM_L_RM_L_SFT 0 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5645_G_HP_R_RM_R_MASK (0x7 << 13) +#define RT5645_G_HP_R_RM_R_SFT 13 +#define RT5645_G_IN_R_RM_R_MASK (0x7 << 10) +#define RT5645_G_IN_R_RM_R_SFT 10 +#define RT5645_G_BST4_RM_R_MASK (0x7 << 7) +#define RT5645_G_BST4_RM_R_SFT 7 +#define RT5645_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5645_G_BST3_RM_R_SFT 4 +#define RT5645_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5645_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5645_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5645_G_BST1_RM_R_SFT 13 +#define RT5645_G_OM_R_RM_R_MASK (0x7 << 10) +#define RT5645_G_OM_R_RM_R_SFT 10 +#define RT5645_M_MM_R_RM_R (0x1 << 6) +#define RT5645_M_MM_R_RM_R_SFT 6 +#define RT5645_M_IN_R_RM_R (0x1 << 5) +#define RT5645_M_IN_R_RM_R_SFT 5 +#define RT5645_M_HP_R_RM_R (0x1 << 4) +#define RT5645_M_HP_R_RM_R_SFT 4 +#define RT5645_M_BST3_RM_R (0x1 << 3) +#define RT5645_M_BST3_RM_R_SFT 3 +#define RT5645_M_BST2_RM_R (0x1 << 2) +#define RT5645_M_BST2_RM_R_SFT 2 +#define RT5645_M_BST1_RM_R (0x1 << 1) +#define RT5645_M_BST1_RM_R_SFT 1 +#define RT5645_M_OM_R_RM_R (0x1) +#define RT5645_M_OM_R_RM_R_SFT 0 + +/* HPOMIX Control (0x40) (0x42) */ +#define RT5645_M_BST1_HV (0x1 << 4) +#define RT5645_M_BST1_HV_SFT 4 +#define RT5645_M_BST2_HV (0x1 << 4) +#define RT5645_M_BST2_HV_SFT 4 +#define RT5645_M_BST3_HV (0x1 << 3) +#define RT5645_M_BST3_HV_SFT 3 +#define RT5645_M_IN_HV (0x1 << 2) +#define RT5645_M_IN_HV_SFT 2 +#define RT5645_M_DAC2_HV (0x1 << 1) +#define RT5645_M_DAC2_HV_SFT 1 +#define RT5645_M_DAC1_HV (0x1 << 0) +#define RT5645_M_DAC1_HV_SFT 0 + +/* HPMIX Control (0x45) */ +#define RT5645_M_DAC1_HM (0x1 << 14) +#define RT5645_M_DAC1_HM_SFT 14 +#define RT5645_M_HPVOL_HM (0x1 << 13) +#define RT5645_M_HPVOL_HM_SFT 13 +#define RT5645_IRQ_PSV_MODE (0x1 << 12) + +/* SPK Left Mixer Control (0x46) */ +#define RT5645_G_RM_L_SM_L_MASK (0x3 << 14) +#define RT5645_G_RM_L_SM_L_SFT 14 +#define RT5645_G_IN_L_SM_L_MASK (0x3 << 12) +#define RT5645_G_IN_L_SM_L_SFT 12 +#define RT5645_G_DAC_L1_SM_L_MASK (0x3 << 10) +#define RT5645_G_DAC_L1_SM_L_SFT 10 +#define RT5645_G_DAC_L2_SM_L_MASK (0x3 << 8) +#define RT5645_G_DAC_L2_SM_L_SFT 8 +#define RT5645_G_OM_L_SM_L_MASK (0x3 << 6) +#define RT5645_G_OM_L_SM_L_SFT 6 +#define RT5645_M_BST1_L_SM_L (0x1 << 5) +#define RT5645_M_BST1_L_SM_L_SFT 5 +#define RT5645_M_BST3_L_SM_L (0x1 << 4) +#define RT5645_M_BST3_L_SM_L_SFT 4 +#define RT5645_M_IN_L_SM_L (0x1 << 3) +#define RT5645_M_IN_L_SM_L_SFT 3 +#define RT5645_M_DAC_L2_SM_L (0x1 << 2) +#define RT5645_M_DAC_L2_SM_L_SFT 2 +#define RT5645_M_DAC_L1_SM_L (0x1 << 1) +#define RT5645_M_DAC_L1_SM_L_SFT 1 + +/* SPK Right Mixer Control (0x47) */ +#define RT5645_G_RM_R_SM_R_MASK (0x3 << 14) +#define RT5645_G_RM_R_SM_R_SFT 14 +#define RT5645_G_IN_R_SM_R_MASK (0x3 << 12) +#define RT5645_G_IN_R_SM_R_SFT 12 +#define RT5645_G_DAC_R1_SM_R_MASK (0x3 << 10) +#define RT5645_G_DAC_R1_SM_R_SFT 10 +#define RT5645_G_DAC_R2_SM_R_MASK (0x3 << 8) +#define RT5645_G_DAC_R2_SM_R_SFT 8 +#define RT5645_G_OM_R_SM_R_MASK (0x3 << 6) +#define RT5645_G_OM_R_SM_R_SFT 6 +#define RT5645_M_BST2_R_SM_R (0x1 << 5) +#define RT5645_M_BST2_R_SM_R_SFT 5 +#define RT5645_M_BST3_R_SM_R (0x1 << 4) +#define RT5645_M_BST3_R_SM_R_SFT 4 +#define RT5645_M_IN_R_SM_R (0x1 << 3) +#define RT5645_M_IN_R_SM_R_SFT 3 +#define RT5645_M_DAC_R2_SM_R (0x1 << 2) +#define RT5645_M_DAC_R2_SM_R_SFT 2 +#define RT5645_M_DAC_R1_SM_R (0x1 << 1) +#define RT5645_M_DAC_R1_SM_R_SFT 1 + +/* SPOLMIX Control (0x48) */ +#define RT5645_M_DAC_L1_SPM_L (0x1 << 15) +#define RT5645_M_DAC_L1_SPM_L_SFT 15 +#define RT5645_M_DAC_R1_SPM_L (0x1 << 14) +#define RT5645_M_DAC_R1_SPM_L_SFT 14 +#define RT5645_M_SV_L_SPM_L (0x1 << 13) +#define RT5645_M_SV_L_SPM_L_SFT 13 +#define RT5645_M_SV_R_SPM_L (0x1 << 12) +#define RT5645_M_SV_R_SPM_L_SFT 12 +#define RT5645_M_BST3_SPM_L (0x1 << 11) +#define RT5645_M_BST3_SPM_L_SFT 11 +#define RT5645_M_DAC_R1_SPM_R (0x1 << 2) +#define RT5645_M_DAC_R1_SPM_R_SFT 2 +#define RT5645_M_BST3_SPM_R (0x1 << 1) +#define RT5645_M_BST3_SPM_R_SFT 1 +#define RT5645_M_SV_R_SPM_R (0x1 << 0) +#define RT5645_M_SV_R_SPM_R_SFT 0 + +/* SPOMIX Ratio Control (0x4a) */ +#define RT5645_SPK_G_CLSD_MASK (0x7 << 0) +#define RT5645_SPK_G_CLSD_SFT 0 + +/* Mono Output Mixer Control (0x4c) */ +#define RT5645_G_MONOMIX_MASK (0x1 << 10) +#define RT5645_G_MONOMIX_SFT 10 +#define RT5645_M_OV_L_MM (0x1 << 9) +#define RT5645_M_OV_L_MM_SFT 9 +#define RT5645_M_DAC_L2_MA (0x1 << 8) +#define RT5645_M_DAC_L2_MA_SFT 8 +#define RT5645_M_BST2_MM (0x1 << 4) +#define RT5645_M_BST2_MM_SFT 4 +#define RT5645_M_DAC_R1_MM (0x1 << 3) +#define RT5645_M_DAC_R1_MM_SFT 3 +#define RT5645_M_DAC_R2_MM (0x1 << 2) +#define RT5645_M_DAC_R2_MM_SFT 2 +#define RT5645_M_DAC_L2_MM (0x1 << 1) +#define RT5645_M_DAC_L2_MM_SFT 1 +#define RT5645_M_BST3_MM (0x1 << 0) +#define RT5645_M_BST3_MM_SFT 0 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5645_G_BST3_OM_L_MASK (0x7 << 13) +#define RT5645_G_BST3_OM_L_SFT 13 +#define RT5645_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5645_G_BST2_OM_L_SFT 10 +#define RT5645_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5645_G_BST1_OM_L_SFT 7 +#define RT5645_G_IN_L_OM_L_MASK (0x7 << 4) +#define RT5645_G_IN_L_OM_L_SFT 4 +#define RT5645_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5645_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5645_G_DAC_R2_OM_L_MASK (0x7 << 13) +#define RT5645_G_DAC_R2_OM_L_SFT 13 +#define RT5645_G_DAC_L2_OM_L_MASK (0x7 << 10) +#define RT5645_G_DAC_L2_OM_L_SFT 10 +#define RT5645_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5645_G_DAC_L1_OM_L_SFT 7 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5645_M_BST3_OM_L (0x1 << 4) +#define RT5645_M_BST3_OM_L_SFT 4 +#define RT5645_M_BST1_OM_L (0x1 << 3) +#define RT5645_M_BST1_OM_L_SFT 3 +#define RT5645_M_IN_L_OM_L (0x1 << 2) +#define RT5645_M_IN_L_OM_L_SFT 2 +#define RT5645_M_DAC_L2_OM_L (0x1 << 1) +#define RT5645_M_DAC_L2_OM_L_SFT 1 +#define RT5645_M_DAC_L1_OM_L (0x1) +#define RT5645_M_DAC_L1_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5645_G_BST4_OM_R_MASK (0x7 << 13) +#define RT5645_G_BST4_OM_R_SFT 13 +#define RT5645_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5645_G_BST2_OM_R_SFT 10 +#define RT5645_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5645_G_BST1_OM_R_SFT 7 +#define RT5645_G_IN_R_OM_R_MASK (0x7 << 4) +#define RT5645_G_IN_R_OM_R_SFT 4 +#define RT5645_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5645_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5645_G_DAC_L2_OM_R_MASK (0x7 << 13) +#define RT5645_G_DAC_L2_OM_R_SFT 13 +#define RT5645_G_DAC_R2_OM_R_MASK (0x7 << 10) +#define RT5645_G_DAC_R2_OM_R_SFT 10 +#define RT5645_G_DAC_R1_OM_R_MASK (0x7 << 7) +#define RT5645_G_DAC_R1_OM_R_SFT 7 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5645_M_BST3_OM_R (0x1 << 4) +#define RT5645_M_BST3_OM_R_SFT 4 +#define RT5645_M_BST2_OM_R (0x1 << 3) +#define RT5645_M_BST2_OM_R_SFT 3 +#define RT5645_M_IN_R_OM_R (0x1 << 2) +#define RT5645_M_IN_R_OM_R_SFT 2 +#define RT5645_M_DAC_R2_OM_R (0x1 << 1) +#define RT5645_M_DAC_R2_OM_R_SFT 1 +#define RT5645_M_DAC_R1_OM_R (0x1) +#define RT5645_M_DAC_R1_OM_R_SFT 0 + +/* LOUT Mixer Control (0x53) */ +#define RT5645_M_DAC_L1_LM (0x1 << 15) +#define RT5645_M_DAC_L1_LM_SFT 15 +#define RT5645_M_DAC_R1_LM (0x1 << 14) +#define RT5645_M_DAC_R1_LM_SFT 14 +#define RT5645_M_OV_L_LM (0x1 << 13) +#define RT5645_M_OV_L_LM_SFT 13 +#define RT5645_M_OV_R_LM (0x1 << 12) +#define RT5645_M_OV_R_LM_SFT 12 +#define RT5645_G_LOUTMIX_MASK (0x1 << 11) +#define RT5645_G_LOUTMIX_SFT 11 + +/* Power Management for Digital 1 (0x61) */ +#define RT5645_PWR_I2S1 (0x1 << 15) +#define RT5645_PWR_I2S1_BIT 15 +#define RT5645_PWR_I2S2 (0x1 << 14) +#define RT5645_PWR_I2S2_BIT 14 +#define RT5645_PWR_I2S3 (0x1 << 13) +#define RT5645_PWR_I2S3_BIT 13 +#define RT5645_PWR_DAC_L1 (0x1 << 12) +#define RT5645_PWR_DAC_L1_BIT 12 +#define RT5645_PWR_DAC_R1 (0x1 << 11) +#define RT5645_PWR_DAC_R1_BIT 11 +#define RT5645_PWR_CLS_D_R (0x1 << 9) +#define RT5645_PWR_CLS_D_R_BIT 9 +#define RT5645_PWR_CLS_D_L (0x1 << 8) +#define RT5645_PWR_CLS_D_L_BIT 8 +#define RT5645_PWR_DAC_L2 (0x1 << 7) +#define RT5645_PWR_DAC_L2_BIT 7 +#define RT5645_PWR_DAC_R2 (0x1 << 6) +#define RT5645_PWR_DAC_R2_BIT 6 +#define RT5645_PWR_ADC_L (0x1 << 2) +#define RT5645_PWR_ADC_L_BIT 2 +#define RT5645_PWR_ADC_R (0x1 << 1) +#define RT5645_PWR_ADC_R_BIT 1 +#define RT5645_PWR_CLS_D (0x1) +#define RT5645_PWR_CLS_D_BIT 0 + +/* Power Management for Digital 2 (0x62) */ +#define RT5645_PWR_ADC_S1F (0x1 << 15) +#define RT5645_PWR_ADC_S1F_BIT 15 +#define RT5645_PWR_ADC_MF_L (0x1 << 14) +#define RT5645_PWR_ADC_MF_L_BIT 14 +#define RT5645_PWR_ADC_MF_R (0x1 << 13) +#define RT5645_PWR_ADC_MF_R_BIT 13 +#define RT5645_PWR_I2S_DSP (0x1 << 12) +#define RT5645_PWR_I2S_DSP_BIT 12 +#define RT5645_PWR_DAC_S1F (0x1 << 11) +#define RT5645_PWR_DAC_S1F_BIT 11 +#define RT5645_PWR_DAC_MF_L (0x1 << 10) +#define RT5645_PWR_DAC_MF_L_BIT 10 +#define RT5645_PWR_DAC_MF_R (0x1 << 9) +#define RT5645_PWR_DAC_MF_R_BIT 9 +#define RT5645_PWR_PDM1 (0x1 << 7) +#define RT5645_PWR_PDM1_BIT 7 +#define RT5645_PWR_PDM2 (0x1 << 6) +#define RT5645_PWR_PDM2_BIT 6 +#define RT5645_PWR_IPTV (0x1 << 1) +#define RT5645_PWR_IPTV_BIT 1 +#define RT5645_PWR_PAD (0x1) +#define RT5645_PWR_PAD_BIT 0 + +/* Power Management for Analog 1 (0x63) */ +#define RT5645_PWR_VREF1 (0x1 << 15) +#define RT5645_PWR_VREF1_BIT 15 +#define RT5645_PWR_FV1 (0x1 << 14) +#define RT5645_PWR_FV1_BIT 14 +#define RT5645_PWR_MB (0x1 << 13) +#define RT5645_PWR_MB_BIT 13 +#define RT5645_PWR_LM (0x1 << 12) +#define RT5645_PWR_LM_BIT 12 +#define RT5645_PWR_BG (0x1 << 11) +#define RT5645_PWR_BG_BIT 11 +#define RT5645_PWR_MA (0x1 << 10) +#define RT5645_PWR_MA_BIT 10 +#define RT5645_PWR_HP_L (0x1 << 7) +#define RT5645_PWR_HP_L_BIT 7 +#define RT5645_PWR_HP_R (0x1 << 6) +#define RT5645_PWR_HP_R_BIT 6 +#define RT5645_PWR_HA (0x1 << 5) +#define RT5645_PWR_HA_BIT 5 +#define RT5645_PWR_VREF2 (0x1 << 4) +#define RT5645_PWR_VREF2_BIT 4 +#define RT5645_PWR_FV2 (0x1 << 3) +#define RT5645_PWR_FV2_BIT 3 +#define RT5645_LDO_SEL_MASK (0x3) +#define RT5645_LDO_SEL_SFT 0 + +/* Power Management for Analog 2 (0x64) */ +#define RT5645_PWR_BST1 (0x1 << 15) +#define RT5645_PWR_BST1_BIT 15 +#define RT5645_PWR_BST2 (0x1 << 14) +#define RT5645_PWR_BST2_BIT 14 +#define RT5645_PWR_BST3 (0x1 << 13) +#define RT5645_PWR_BST3_BIT 13 +#define RT5645_PWR_BST4 (0x1 << 12) +#define RT5645_PWR_BST4_BIT 12 +#define RT5645_PWR_MB1 (0x1 << 11) +#define RT5645_PWR_MB1_BIT 11 +#define RT5645_PWR_MB2 (0x1 << 10) +#define RT5645_PWR_MB2_BIT 10 +#define RT5645_PWR_PLL (0x1 << 9) +#define RT5645_PWR_PLL_BIT 9 +#define RT5645_PWR_BST2_P (0x1 << 5) +#define RT5645_PWR_BST2_P_BIT 5 +#define RT5645_PWR_BST3_P (0x1 << 4) +#define RT5645_PWR_BST3_P_BIT 4 +#define RT5645_PWR_BST4_P (0x1 << 3) +#define RT5645_PWR_BST4_P_BIT 3 +#define RT5645_PWR_JD1 (0x1 << 2) +#define RT5645_PWR_JD1_BIT 2 +#define RT5645_PWR_JD (0x1 << 1) +#define RT5645_PWR_JD_BIT 1 + +/* Power Management for Mixer (0x65) */ +#define RT5645_PWR_OM_L (0x1 << 15) +#define RT5645_PWR_OM_L_BIT 15 +#define RT5645_PWR_OM_R (0x1 << 14) +#define RT5645_PWR_OM_R_BIT 14 +#define RT5645_PWR_SM_L (0x1 << 13) +#define RT5645_PWR_SM_L_BIT 13 +#define RT5645_PWR_SM_R (0x1 << 12) +#define RT5645_PWR_SM_R_BIT 12 +#define RT5645_PWR_RM_L (0x1 << 11) +#define RT5645_PWR_RM_L_BIT 11 +#define RT5645_PWR_RM_R (0x1 << 10) +#define RT5645_PWR_RM_R_BIT 10 +#define RT5645_PWR_MM (0x1 << 8) +#define RT5645_PWR_MM_BIT 8 +#define RT5645_PWR_HM_L (0x1 << 7) +#define RT5645_PWR_HM_L_BIT 7 +#define RT5645_PWR_HM_R (0x1 << 6) +#define RT5645_PWR_HM_R_BIT 6 +#define RT5645_PWR_LDO2 (0x1 << 1) +#define RT5645_PWR_LDO2_BIT 1 + +/* Power Management for Volume (0x66) */ +#define RT5645_PWR_SV_L (0x1 << 15) +#define RT5645_PWR_SV_L_BIT 15 +#define RT5645_PWR_SV_R (0x1 << 14) +#define RT5645_PWR_SV_R_BIT 14 +#define RT5645_PWR_HV_L (0x1 << 11) +#define RT5645_PWR_HV_L_BIT 11 +#define RT5645_PWR_HV_R (0x1 << 10) +#define RT5645_PWR_HV_R_BIT 10 +#define RT5645_PWR_IN_L (0x1 << 9) +#define RT5645_PWR_IN_L_BIT 9 +#define RT5645_PWR_IN_R (0x1 << 8) +#define RT5645_PWR_IN_R_BIT 8 +#define RT5645_PWR_MIC_DET (0x1 << 5) +#define RT5645_PWR_MIC_DET_BIT 5 + +/* I2S1/2 Audio Serial Data Port Control (0x70 0x71) */ +#define RT5645_I2S_MS_MASK (0x1 << 15) +#define RT5645_I2S_MS_SFT 15 +#define RT5645_I2S_MS_M (0x0 << 15) +#define RT5645_I2S_MS_S (0x1 << 15) +#define RT5645_I2S_O_CP_MASK (0x3 << 10) +#define RT5645_I2S_O_CP_SFT 10 +#define RT5645_I2S_O_CP_OFF (0x0 << 10) +#define RT5645_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5645_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5645_I2S_I_CP_MASK (0x3 << 8) +#define RT5645_I2S_I_CP_SFT 8 +#define RT5645_I2S_I_CP_OFF (0x0 << 8) +#define RT5645_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5645_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5645_I2S_BP_MASK (0x1 << 7) +#define RT5645_I2S_BP_SFT 7 +#define RT5645_I2S_BP_NOR (0x0 << 7) +#define RT5645_I2S_BP_INV (0x1 << 7) +#define RT5645_I2S_DL_MASK (0x3 << 2) +#define RT5645_I2S_DL_SFT 2 +#define RT5645_I2S_DL_16 (0x0 << 2) +#define RT5645_I2S_DL_20 (0x1 << 2) +#define RT5645_I2S_DL_24 (0x2 << 2) +#define RT5645_I2S_DL_8 (0x3 << 2) +#define RT5645_I2S_DF_MASK (0x3) +#define RT5645_I2S_DF_SFT 0 +#define RT5645_I2S_DF_I2S (0x0) +#define RT5645_I2S_DF_LEFT (0x1) +#define RT5645_I2S_DF_PCM_A (0x2) +#define RT5645_I2S_DF_PCM_B (0x3) + +/* I2S2 Audio Serial Data Port Control (0x71) */ +#define RT5645_I2S2_SDI_MASK (0x1 << 6) +#define RT5645_I2S2_SDI_SFT 6 +#define RT5645_I2S2_SDI_I2S1 (0x0 << 6) +#define RT5645_I2S2_SDI_I2S2 (0x1 << 6) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5645_I2S_PD1_MASK (0x7 << 12) +#define RT5645_I2S_PD1_SFT 12 +#define RT5645_I2S_PD1_1 (0x0 << 12) +#define RT5645_I2S_PD1_2 (0x1 << 12) +#define RT5645_I2S_PD1_3 (0x2 << 12) +#define RT5645_I2S_PD1_4 (0x3 << 12) +#define RT5645_I2S_PD1_6 (0x4 << 12) +#define RT5645_I2S_PD1_8 (0x5 << 12) +#define RT5645_I2S_PD1_12 (0x6 << 12) +#define RT5645_I2S_PD1_16 (0x7 << 12) +#define RT5645_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5645_I2S_BCLK_MS2_SFT 11 +#define RT5645_I2S_BCLK_MS2_32 (0x0 << 11) +#define RT5645_I2S_BCLK_MS2_64 (0x1 << 11) +#define RT5645_I2S_PD2_MASK (0x7 << 8) +#define RT5645_I2S_PD2_SFT 8 +#define RT5645_I2S_PD2_1 (0x0 << 8) +#define RT5645_I2S_PD2_2 (0x1 << 8) +#define RT5645_I2S_PD2_3 (0x2 << 8) +#define RT5645_I2S_PD2_4 (0x3 << 8) +#define RT5645_I2S_PD2_6 (0x4 << 8) +#define RT5645_I2S_PD2_8 (0x5 << 8) +#define RT5645_I2S_PD2_12 (0x6 << 8) +#define RT5645_I2S_PD2_16 (0x7 << 8) +#define RT5645_I2S_BCLK_MS3_MASK (0x1 << 7) +#define RT5645_I2S_BCLK_MS3_SFT 7 +#define RT5645_I2S_BCLK_MS3_32 (0x0 << 7) +#define RT5645_I2S_BCLK_MS3_64 (0x1 << 7) +#define RT5645_I2S_PD3_MASK (0x7 << 4) +#define RT5645_I2S_PD3_SFT 4 +#define RT5645_I2S_PD3_1 (0x0 << 4) +#define RT5645_I2S_PD3_2 (0x1 << 4) +#define RT5645_I2S_PD3_3 (0x2 << 4) +#define RT5645_I2S_PD3_4 (0x3 << 4) +#define RT5645_I2S_PD3_6 (0x4 << 4) +#define RT5645_I2S_PD3_8 (0x5 << 4) +#define RT5645_I2S_PD3_12 (0x6 << 4) +#define RT5645_I2S_PD3_16 (0x7 << 4) +#define RT5645_DAC_OSR_MASK (0x3 << 2) +#define RT5645_DAC_OSR_SFT 2 +#define RT5645_DAC_OSR_128 (0x0 << 2) +#define RT5645_DAC_OSR_64 (0x1 << 2) +#define RT5645_DAC_OSR_32 (0x2 << 2) +#define RT5645_DAC_OSR_16 (0x3 << 2) +#define RT5645_ADC_OSR_MASK (0x3) +#define RT5645_ADC_OSR_SFT 0 +#define RT5645_ADC_OSR_128 (0x0) +#define RT5645_ADC_OSR_64 (0x1) +#define RT5645_ADC_OSR_32 (0x2) +#define RT5645_ADC_OSR_16 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5645_DAC_L_OSR_MASK (0x3 << 14) +#define RT5645_DAC_L_OSR_SFT 14 +#define RT5645_DAC_L_OSR_128 (0x0 << 14) +#define RT5645_DAC_L_OSR_64 (0x1 << 14) +#define RT5645_DAC_L_OSR_32 (0x2 << 14) +#define RT5645_DAC_L_OSR_16 (0x3 << 14) +#define RT5645_ADC_R_OSR_MASK (0x3 << 12) +#define RT5645_ADC_R_OSR_SFT 12 +#define RT5645_ADC_R_OSR_128 (0x0 << 12) +#define RT5645_ADC_R_OSR_64 (0x1 << 12) +#define RT5645_ADC_R_OSR_32 (0x2 << 12) +#define RT5645_ADC_R_OSR_16 (0x3 << 12) +#define RT5645_DAHPF_EN (0x1 << 11) +#define RT5645_DAHPF_EN_SFT 11 +#define RT5645_ADHPF_EN (0x1 << 10) +#define RT5645_ADHPF_EN_SFT 10 + +/* Digital Microphone Control (0x75) */ +#define RT5645_DMIC_1_EN_MASK (0x1 << 15) +#define RT5645_DMIC_1_EN_SFT 15 +#define RT5645_DMIC_1_DIS (0x0 << 15) +#define RT5645_DMIC_1_EN (0x1 << 15) +#define RT5645_DMIC_2_EN_MASK (0x1 << 14) +#define RT5645_DMIC_2_EN_SFT 14 +#define RT5645_DMIC_2_DIS (0x0 << 14) +#define RT5645_DMIC_2_EN (0x1 << 14) +#define RT5645_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5645_DMIC_1L_LH_SFT 13 +#define RT5645_DMIC_1L_LH_FALLING (0x0 << 13) +#define RT5645_DMIC_1L_LH_RISING (0x1 << 13) +#define RT5645_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5645_DMIC_1R_LH_SFT 12 +#define RT5645_DMIC_1R_LH_FALLING (0x0 << 12) +#define RT5645_DMIC_1R_LH_RISING (0x1 << 12) +#define RT5645_DMIC_2_DP_MASK (0x3 << 10) +#define RT5645_DMIC_2_DP_SFT 10 +#define RT5645_DMIC_2_DP_GPIO6 (0x0 << 10) +#define RT5645_DMIC_2_DP_GPIO10 (0x1 << 10) +#define RT5645_DMIC_2_DP_GPIO12 (0x2 << 10) +#define RT5645_DMIC_2_DP_IN2P (0x3 << 10) +#define RT5645_DMIC_2L_LH_MASK (0x1 << 9) +#define RT5645_DMIC_2L_LH_SFT 9 +#define RT5645_DMIC_2L_LH_FALLING (0x0 << 9) +#define RT5645_DMIC_2L_LH_RISING (0x1 << 9) +#define RT5645_DMIC_2R_LH_MASK (0x1 << 8) +#define RT5645_DMIC_2R_LH_SFT 8 +#define RT5645_DMIC_2R_LH_FALLING (0x0 << 8) +#define RT5645_DMIC_2R_LH_RISING (0x1 << 8) +#define RT5645_DMIC_CLK_MASK (0x7 << 5) +#define RT5645_DMIC_CLK_SFT 5 +#define RT5645_DMIC_3_EN_MASK (0x1 << 4) +#define RT5645_DMIC_3_EN_SFT 4 +#define RT5645_DMIC_3_DIS (0x0 << 4) +#define RT5645_DMIC_3_EN (0x1 << 4) +#define RT5645_DMIC_1_DP_MASK (0x3 << 0) +#define RT5645_DMIC_1_DP_SFT 0 +#define RT5645_DMIC_1_DP_GPIO5 (0x0 << 0) +#define RT5645_DMIC_1_DP_IN2N (0x1 << 0) +#define RT5645_DMIC_1_DP_GPIO11 (0x2 << 0) + +/* TDM Control 1 (0x77) */ +#define RT5645_IF1_ADC_IN_MASK (0x3 << 8) +#define RT5645_IF1_ADC_IN_SFT 8 + +/* Global Clock Control (0x80) */ +#define RT5645_SCLK_SRC_MASK (0x3 << 14) +#define RT5645_SCLK_SRC_SFT 14 +#define RT5645_SCLK_SRC_MCLK (0x0 << 14) +#define RT5645_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5645_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5645_PLL1_SRC_MASK (0x7 << 11) +#define RT5645_PLL1_SRC_SFT 11 +#define RT5645_PLL1_SRC_MCLK (0x0 << 11) +#define RT5645_PLL1_SRC_BCLK1 (0x1 << 11) +#define RT5645_PLL1_SRC_BCLK2 (0x2 << 11) +#define RT5645_PLL1_SRC_BCLK3 (0x3 << 11) +#define RT5645_PLL1_SRC_RCCLK (0x4 << 11) +#define RT5645_PLL1_PD_MASK (0x1 << 3) +#define RT5645_PLL1_PD_SFT 3 +#define RT5645_PLL1_PD_1 (0x0 << 3) +#define RT5645_PLL1_PD_2 (0x1 << 3) + +#define RT5645_PLL_INP_MAX 40000000 +#define RT5645_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5645_PLL_N_MAX 0x1ff +#define RT5645_PLL_N_MASK (RT5645_PLL_N_MAX << 7) +#define RT5645_PLL_N_SFT 7 +#define RT5645_PLL_K_MAX 0x1f +#define RT5645_PLL_K_MASK (RT5645_PLL_K_MAX) +#define RT5645_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5645_PLL_M_MAX 0xf +#define RT5645_PLL_M_MASK (RT5645_PLL_M_MAX << 12) +#define RT5645_PLL_M_SFT 12 +#define RT5645_PLL_M_BP (0x1 << 11) +#define RT5645_PLL_M_BP_SFT 11 + +/* ASRC Control 1 (0x83) */ +#define RT5645_STO_T_MASK (0x1 << 15) +#define RT5645_STO_T_SFT 15 +#define RT5645_STO_T_SCLK (0x0 << 15) +#define RT5645_STO_T_LRCK1 (0x1 << 15) +#define RT5645_M1_T_MASK (0x1 << 14) +#define RT5645_M1_T_SFT 14 +#define RT5645_M1_T_I2S2 (0x0 << 14) +#define RT5645_M1_T_I2S2_D3 (0x1 << 14) +#define RT5645_I2S2_F_MASK (0x1 << 12) +#define RT5645_I2S2_F_SFT 12 +#define RT5645_I2S2_F_I2S2_D2 (0x0 << 12) +#define RT5645_I2S2_F_I2S1_TCLK (0x1 << 12) +#define RT5645_DMIC_1_M_MASK (0x1 << 9) +#define RT5645_DMIC_1_M_SFT 9 +#define RT5645_DMIC_1_M_NOR (0x0 << 9) +#define RT5645_DMIC_1_M_ASYN (0x1 << 9) +#define RT5645_DMIC_2_M_MASK (0x1 << 8) +#define RT5645_DMIC_2_M_SFT 8 +#define RT5645_DMIC_2_M_NOR (0x0 << 8) +#define RT5645_DMIC_2_M_ASYN (0x1 << 8) + +/* ASRC clock source selection (0x84, 0x85) */ +#define RT5645_CLK_SEL_SYS (0x0) +#define RT5645_CLK_SEL_I2S1_ASRC (0x1) +#define RT5645_CLK_SEL_I2S2_ASRC (0x2) +#define RT5645_CLK_SEL_SYS2 (0x5) + +/* ASRC Control 2 (0x84) */ +#define RT5645_DA_STO_CLK_SEL_MASK (0xf << 12) +#define RT5645_DA_STO_CLK_SEL_SFT 12 +#define RT5645_DA_MONOL_CLK_SEL_MASK (0xf << 8) +#define RT5645_DA_MONOL_CLK_SEL_SFT 8 +#define RT5645_DA_MONOR_CLK_SEL_MASK (0xf << 4) +#define RT5645_DA_MONOR_CLK_SEL_SFT 4 +#define RT5645_AD_STO1_CLK_SEL_MASK (0xf << 0) +#define RT5645_AD_STO1_CLK_SEL_SFT 0 + +/* ASRC Control 3 (0x85) */ +#define RT5645_AD_MONOL_CLK_SEL_MASK (0xf << 4) +#define RT5645_AD_MONOL_CLK_SEL_SFT 4 +#define RT5645_AD_MONOR_CLK_SEL_MASK (0xf << 0) +#define RT5645_AD_MONOR_CLK_SEL_SFT 0 + +/* ASRC Control 4 (0x89) */ +#define RT5645_I2S1_PD_MASK (0x7 << 12) +#define RT5645_I2S1_PD_SFT 12 +#define RT5645_I2S2_PD_MASK (0x7 << 8) +#define RT5645_I2S2_PD_SFT 8 + +/* HPOUT Over Current Detection (0x8b) */ +#define RT5645_HP_OVCD_MASK (0x1 << 10) +#define RT5645_HP_OVCD_SFT 10 +#define RT5645_HP_OVCD_DIS (0x0 << 10) +#define RT5645_HP_OVCD_EN (0x1 << 10) +#define RT5645_HP_OC_TH_MASK (0x3 << 8) +#define RT5645_HP_OC_TH_SFT 8 +#define RT5645_HP_OC_TH_90 (0x0 << 8) +#define RT5645_HP_OC_TH_105 (0x1 << 8) +#define RT5645_HP_OC_TH_120 (0x2 << 8) +#define RT5645_HP_OC_TH_135 (0x3 << 8) + +/* Class D Over Current Control (0x8c) */ +#define RT5645_CLSD_OC_MASK (0x1 << 9) +#define RT5645_CLSD_OC_SFT 9 +#define RT5645_CLSD_OC_PU (0x0 << 9) +#define RT5645_CLSD_OC_PD (0x1 << 9) +#define RT5645_AUTO_PD_MASK (0x1 << 8) +#define RT5645_AUTO_PD_SFT 8 +#define RT5645_AUTO_PD_DIS (0x0 << 8) +#define RT5645_AUTO_PD_EN (0x1 << 8) +#define RT5645_CLSD_OC_TH_MASK (0x3f) +#define RT5645_CLSD_OC_TH_SFT 0 + +/* Class D Output Control (0x8d) */ +#define RT5645_CLSD_RATIO_MASK (0xf << 12) +#define RT5645_CLSD_RATIO_SFT 12 +#define RT5645_CLSD_OM_MASK (0x1 << 11) +#define RT5645_CLSD_OM_SFT 11 +#define RT5645_CLSD_OM_MONO (0x0 << 11) +#define RT5645_CLSD_OM_STO (0x1 << 11) +#define RT5645_CLSD_SCH_MASK (0x1 << 10) +#define RT5645_CLSD_SCH_SFT 10 +#define RT5645_CLSD_SCH_L (0x0 << 10) +#define RT5645_CLSD_SCH_S (0x1 << 10) + +/* Depop Mode Control 1 (0x8e) */ +#define RT5645_SMT_TRIG_MASK (0x1 << 15) +#define RT5645_SMT_TRIG_SFT 15 +#define RT5645_SMT_TRIG_DIS (0x0 << 15) +#define RT5645_SMT_TRIG_EN (0x1 << 15) +#define RT5645_HP_L_SMT_MASK (0x1 << 9) +#define RT5645_HP_L_SMT_SFT 9 +#define RT5645_HP_L_SMT_DIS (0x0 << 9) +#define RT5645_HP_L_SMT_EN (0x1 << 9) +#define RT5645_HP_R_SMT_MASK (0x1 << 8) +#define RT5645_HP_R_SMT_SFT 8 +#define RT5645_HP_R_SMT_DIS (0x0 << 8) +#define RT5645_HP_R_SMT_EN (0x1 << 8) +#define RT5645_HP_CD_PD_MASK (0x1 << 7) +#define RT5645_HP_CD_PD_SFT 7 +#define RT5645_HP_CD_PD_DIS (0x0 << 7) +#define RT5645_HP_CD_PD_EN (0x1 << 7) +#define RT5645_RSTN_MASK (0x1 << 6) +#define RT5645_RSTN_SFT 6 +#define RT5645_RSTN_DIS (0x0 << 6) +#define RT5645_RSTN_EN (0x1 << 6) +#define RT5645_RSTP_MASK (0x1 << 5) +#define RT5645_RSTP_SFT 5 +#define RT5645_RSTP_DIS (0x0 << 5) +#define RT5645_RSTP_EN (0x1 << 5) +#define RT5645_HP_CO_MASK (0x1 << 4) +#define RT5645_HP_CO_SFT 4 +#define RT5645_HP_CO_DIS (0x0 << 4) +#define RT5645_HP_CO_EN (0x1 << 4) +#define RT5645_HP_CP_MASK (0x1 << 3) +#define RT5645_HP_CP_SFT 3 +#define RT5645_HP_CP_PD (0x0 << 3) +#define RT5645_HP_CP_PU (0x1 << 3) +#define RT5645_HP_SG_MASK (0x1 << 2) +#define RT5645_HP_SG_SFT 2 +#define RT5645_HP_SG_DIS (0x0 << 2) +#define RT5645_HP_SG_EN (0x1 << 2) +#define RT5645_HP_DP_MASK (0x1 << 1) +#define RT5645_HP_DP_SFT 1 +#define RT5645_HP_DP_PD (0x0 << 1) +#define RT5645_HP_DP_PU (0x1 << 1) +#define RT5645_HP_CB_MASK (0x1) +#define RT5645_HP_CB_SFT 0 +#define RT5645_HP_CB_PD (0x0) +#define RT5645_HP_CB_PU (0x1) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5645_DEPOP_MASK (0x1 << 13) +#define RT5645_DEPOP_SFT 13 +#define RT5645_DEPOP_AUTO (0x0 << 13) +#define RT5645_DEPOP_MAN (0x1 << 13) +#define RT5645_RAMP_MASK (0x1 << 12) +#define RT5645_RAMP_SFT 12 +#define RT5645_RAMP_DIS (0x0 << 12) +#define RT5645_RAMP_EN (0x1 << 12) +#define RT5645_BPS_MASK (0x1 << 11) +#define RT5645_BPS_SFT 11 +#define RT5645_BPS_DIS (0x0 << 11) +#define RT5645_BPS_EN (0x1 << 11) +#define RT5645_FAST_UPDN_MASK (0x1 << 10) +#define RT5645_FAST_UPDN_SFT 10 +#define RT5645_FAST_UPDN_DIS (0x0 << 10) +#define RT5645_FAST_UPDN_EN (0x1 << 10) +#define RT5645_MRES_MASK (0x3 << 8) +#define RT5645_MRES_SFT 8 +#define RT5645_MRES_15MO (0x0 << 8) +#define RT5645_MRES_25MO (0x1 << 8) +#define RT5645_MRES_35MO (0x2 << 8) +#define RT5645_MRES_45MO (0x3 << 8) +#define RT5645_VLO_MASK (0x1 << 7) +#define RT5645_VLO_SFT 7 +#define RT5645_VLO_3V (0x0 << 7) +#define RT5645_VLO_32V (0x1 << 7) +#define RT5645_DIG_DP_MASK (0x1 << 6) +#define RT5645_DIG_DP_SFT 6 +#define RT5645_DIG_DP_DIS (0x0 << 6) +#define RT5645_DIG_DP_EN (0x1 << 6) +#define RT5645_DP_TH_MASK (0x3 << 4) +#define RT5645_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5645_CP_SYS_MASK (0x7 << 12) +#define RT5645_CP_SYS_SFT 12 +#define RT5645_CP_FQ1_MASK (0x7 << 8) +#define RT5645_CP_FQ1_SFT 8 +#define RT5645_CP_FQ2_MASK (0x7 << 4) +#define RT5645_CP_FQ2_SFT 4 +#define RT5645_CP_FQ3_MASK (0x7) +#define RT5645_CP_FQ3_SFT 0 +#define RT5645_CP_FQ_1_5_KHZ 0 +#define RT5645_CP_FQ_3_KHZ 1 +#define RT5645_CP_FQ_6_KHZ 2 +#define RT5645_CP_FQ_12_KHZ 3 +#define RT5645_CP_FQ_24_KHZ 4 +#define RT5645_CP_FQ_48_KHZ 5 +#define RT5645_CP_FQ_96_KHZ 6 +#define RT5645_CP_FQ_192_KHZ 7 + +/* PV detection and SPK gain control (0x92) */ +#define RT5645_PVDD_DET_MASK (0x1 << 15) +#define RT5645_PVDD_DET_SFT 15 +#define RT5645_PVDD_DET_DIS (0x0 << 15) +#define RT5645_PVDD_DET_EN (0x1 << 15) +#define RT5645_SPK_AG_MASK (0x1 << 14) +#define RT5645_SPK_AG_SFT 14 +#define RT5645_SPK_AG_DIS (0x0 << 14) +#define RT5645_SPK_AG_EN (0x1 << 14) + +/* Micbias Control (0x93) */ +#define RT5645_MIC1_BS_MASK (0x1 << 15) +#define RT5645_MIC1_BS_SFT 15 +#define RT5645_MIC1_BS_9AV (0x0 << 15) +#define RT5645_MIC1_BS_75AV (0x1 << 15) +#define RT5645_MIC2_BS_MASK (0x1 << 14) +#define RT5645_MIC2_BS_SFT 14 +#define RT5645_MIC2_BS_9AV (0x0 << 14) +#define RT5645_MIC2_BS_75AV (0x1 << 14) +#define RT5645_MIC1_CLK_MASK (0x1 << 13) +#define RT5645_MIC1_CLK_SFT 13 +#define RT5645_MIC1_CLK_DIS (0x0 << 13) +#define RT5645_MIC1_CLK_EN (0x1 << 13) +#define RT5645_MIC2_CLK_MASK (0x1 << 12) +#define RT5645_MIC2_CLK_SFT 12 +#define RT5645_MIC2_CLK_DIS (0x0 << 12) +#define RT5645_MIC2_CLK_EN (0x1 << 12) +#define RT5645_MIC1_OVCD_MASK (0x1 << 11) +#define RT5645_MIC1_OVCD_SFT 11 +#define RT5645_MIC1_OVCD_DIS (0x0 << 11) +#define RT5645_MIC1_OVCD_EN (0x1 << 11) +#define RT5645_MIC1_OVTH_MASK (0x3 << 9) +#define RT5645_MIC1_OVTH_SFT 9 +#define RT5645_MIC1_OVTH_600UA (0x0 << 9) +#define RT5645_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5645_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5645_MIC2_OVCD_MASK (0x1 << 8) +#define RT5645_MIC2_OVCD_SFT 8 +#define RT5645_MIC2_OVCD_DIS (0x0 << 8) +#define RT5645_MIC2_OVCD_EN (0x1 << 8) +#define RT5645_MIC2_OVTH_MASK (0x3 << 6) +#define RT5645_MIC2_OVTH_SFT 6 +#define RT5645_MIC2_OVTH_600UA (0x0 << 6) +#define RT5645_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5645_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5645_PWR_MB_MASK (0x1 << 5) +#define RT5645_PWR_MB_SFT 5 +#define RT5645_PWR_MB_PD (0x0 << 5) +#define RT5645_PWR_MB_PU (0x1 << 5) +#define RT5645_PWR_CLK25M_MASK (0x1 << 4) +#define RT5645_PWR_CLK25M_SFT 4 +#define RT5645_PWR_CLK25M_PD (0x0 << 4) +#define RT5645_PWR_CLK25M_PU (0x1 << 4) +#define RT5645_IRQ_CLK_MCLK (0x0 << 3) +#define RT5645_IRQ_CLK_INT (0x1 << 3) +#define RT5645_JD1_MODE_MASK (0x3 << 0) +#define RT5645_JD1_MODE_0 (0x0 << 0) +#define RT5645_JD1_MODE_1 (0x1 << 0) +#define RT5645_JD1_MODE_2 (0x2 << 0) + +/* VAD Control 4 (0x9d) */ +#define RT5645_VAD_SEL_MASK (0x3 << 8) +#define RT5645_VAD_SEL_SFT 8 + +/* EQ Control 1 (0xb0) */ +#define RT5645_EQ_SRC_MASK (0x1 << 15) +#define RT5645_EQ_SRC_SFT 15 +#define RT5645_EQ_SRC_DAC (0x0 << 15) +#define RT5645_EQ_SRC_ADC (0x1 << 15) +#define RT5645_EQ_UPD (0x1 << 14) +#define RT5645_EQ_UPD_BIT 14 +#define RT5645_EQ_CD_MASK (0x1 << 13) +#define RT5645_EQ_CD_SFT 13 +#define RT5645_EQ_CD_DIS (0x0 << 13) +#define RT5645_EQ_CD_EN (0x1 << 13) +#define RT5645_EQ_DITH_MASK (0x3 << 8) +#define RT5645_EQ_DITH_SFT 8 +#define RT5645_EQ_DITH_NOR (0x0 << 8) +#define RT5645_EQ_DITH_LSB (0x1 << 8) +#define RT5645_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5645_EQ_DITH_LSB_2 (0x3 << 8) + +/* EQ Control 2 (0xb1) */ +#define RT5645_EQ_HPF1_M_MASK (0x1 << 8) +#define RT5645_EQ_HPF1_M_SFT 8 +#define RT5645_EQ_HPF1_M_HI (0x0 << 8) +#define RT5645_EQ_HPF1_M_1ST (0x1 << 8) +#define RT5645_EQ_LPF1_M_MASK (0x1 << 7) +#define RT5645_EQ_LPF1_M_SFT 7 +#define RT5645_EQ_LPF1_M_LO (0x0 << 7) +#define RT5645_EQ_LPF1_M_1ST (0x1 << 7) +#define RT5645_EQ_HPF2_MASK (0x1 << 6) +#define RT5645_EQ_HPF2_SFT 6 +#define RT5645_EQ_HPF2_DIS (0x0 << 6) +#define RT5645_EQ_HPF2_EN (0x1 << 6) +#define RT5645_EQ_HPF1_MASK (0x1 << 5) +#define RT5645_EQ_HPF1_SFT 5 +#define RT5645_EQ_HPF1_DIS (0x0 << 5) +#define RT5645_EQ_HPF1_EN (0x1 << 5) +#define RT5645_EQ_BPF4_MASK (0x1 << 4) +#define RT5645_EQ_BPF4_SFT 4 +#define RT5645_EQ_BPF4_DIS (0x0 << 4) +#define RT5645_EQ_BPF4_EN (0x1 << 4) +#define RT5645_EQ_BPF3_MASK (0x1 << 3) +#define RT5645_EQ_BPF3_SFT 3 +#define RT5645_EQ_BPF3_DIS (0x0 << 3) +#define RT5645_EQ_BPF3_EN (0x1 << 3) +#define RT5645_EQ_BPF2_MASK (0x1 << 2) +#define RT5645_EQ_BPF2_SFT 2 +#define RT5645_EQ_BPF2_DIS (0x0 << 2) +#define RT5645_EQ_BPF2_EN (0x1 << 2) +#define RT5645_EQ_BPF1_MASK (0x1 << 1) +#define RT5645_EQ_BPF1_SFT 1 +#define RT5645_EQ_BPF1_DIS (0x0 << 1) +#define RT5645_EQ_BPF1_EN (0x1 << 1) +#define RT5645_EQ_LPF_MASK (0x1) +#define RT5645_EQ_LPF_SFT 0 +#define RT5645_EQ_LPF_DIS (0x0) +#define RT5645_EQ_LPF_EN (0x1) +#define RT5645_EQ_CTRL_MASK (0x7f) + +/* Memory Test (0xb2) */ +#define RT5645_MT_MASK (0x1 << 15) +#define RT5645_MT_SFT 15 +#define RT5645_MT_DIS (0x0 << 15) +#define RT5645_MT_EN (0x1 << 15) + +/* DRC/AGC Control 1 (0xb4) */ +#define RT5645_DRC_AGC_P_MASK (0x1 << 15) +#define RT5645_DRC_AGC_P_SFT 15 +#define RT5645_DRC_AGC_P_DAC (0x0 << 15) +#define RT5645_DRC_AGC_P_ADC (0x1 << 15) +#define RT5645_DRC_AGC_MASK (0x1 << 14) +#define RT5645_DRC_AGC_SFT 14 +#define RT5645_DRC_AGC_DIS (0x0 << 14) +#define RT5645_DRC_AGC_EN (0x1 << 14) +#define RT5645_DRC_AGC_UPD (0x1 << 13) +#define RT5645_DRC_AGC_UPD_BIT 13 +#define RT5645_DRC_AGC_AR_MASK (0x1f << 8) +#define RT5645_DRC_AGC_AR_SFT 8 +#define RT5645_DRC_AGC_R_MASK (0x7 << 5) +#define RT5645_DRC_AGC_R_SFT 5 +#define RT5645_DRC_AGC_R_48K (0x1 << 5) +#define RT5645_DRC_AGC_R_96K (0x2 << 5) +#define RT5645_DRC_AGC_R_192K (0x3 << 5) +#define RT5645_DRC_AGC_R_441K (0x5 << 5) +#define RT5645_DRC_AGC_R_882K (0x6 << 5) +#define RT5645_DRC_AGC_R_1764K (0x7 << 5) +#define RT5645_DRC_AGC_RC_MASK (0x1f) +#define RT5645_DRC_AGC_RC_SFT 0 + +/* DRC/AGC Control 2 (0xb5) */ +#define RT5645_DRC_AGC_POB_MASK (0x3f << 8) +#define RT5645_DRC_AGC_POB_SFT 8 +#define RT5645_DRC_AGC_CP_MASK (0x1 << 7) +#define RT5645_DRC_AGC_CP_SFT 7 +#define RT5645_DRC_AGC_CP_DIS (0x0 << 7) +#define RT5645_DRC_AGC_CP_EN (0x1 << 7) +#define RT5645_DRC_AGC_CPR_MASK (0x3 << 5) +#define RT5645_DRC_AGC_CPR_SFT 5 +#define RT5645_DRC_AGC_CPR_1_1 (0x0 << 5) +#define RT5645_DRC_AGC_CPR_1_2 (0x1 << 5) +#define RT5645_DRC_AGC_CPR_1_3 (0x2 << 5) +#define RT5645_DRC_AGC_CPR_1_4 (0x3 << 5) +#define RT5645_DRC_AGC_PRB_MASK (0x1f) +#define RT5645_DRC_AGC_PRB_SFT 0 + +/* DRC/AGC Control 3 (0xb6) */ +#define RT5645_DRC_AGC_NGB_MASK (0xf << 12) +#define RT5645_DRC_AGC_NGB_SFT 12 +#define RT5645_DRC_AGC_TAR_MASK (0x1f << 7) +#define RT5645_DRC_AGC_TAR_SFT 7 +#define RT5645_DRC_AGC_NG_MASK (0x1 << 6) +#define RT5645_DRC_AGC_NG_SFT 6 +#define RT5645_DRC_AGC_NG_DIS (0x0 << 6) +#define RT5645_DRC_AGC_NG_EN (0x1 << 6) +#define RT5645_DRC_AGC_NGH_MASK (0x1 << 5) +#define RT5645_DRC_AGC_NGH_SFT 5 +#define RT5645_DRC_AGC_NGH_DIS (0x0 << 5) +#define RT5645_DRC_AGC_NGH_EN (0x1 << 5) +#define RT5645_DRC_AGC_NGT_MASK (0x1f) +#define RT5645_DRC_AGC_NGT_SFT 0 + +/* ANC Control 1 (0xb8) */ +#define RT5645_ANC_M_MASK (0x1 << 15) +#define RT5645_ANC_M_SFT 15 +#define RT5645_ANC_M_NOR (0x0 << 15) +#define RT5645_ANC_M_REV (0x1 << 15) +#define RT5645_ANC_MASK (0x1 << 14) +#define RT5645_ANC_SFT 14 +#define RT5645_ANC_DIS (0x0 << 14) +#define RT5645_ANC_EN (0x1 << 14) +#define RT5645_ANC_MD_MASK (0x3 << 12) +#define RT5645_ANC_MD_SFT 12 +#define RT5645_ANC_MD_DIS (0x0 << 12) +#define RT5645_ANC_MD_67MS (0x1 << 12) +#define RT5645_ANC_MD_267MS (0x2 << 12) +#define RT5645_ANC_MD_1067MS (0x3 << 12) +#define RT5645_ANC_SN_MASK (0x1 << 11) +#define RT5645_ANC_SN_SFT 11 +#define RT5645_ANC_SN_DIS (0x0 << 11) +#define RT5645_ANC_SN_EN (0x1 << 11) +#define RT5645_ANC_CLK_MASK (0x1 << 10) +#define RT5645_ANC_CLK_SFT 10 +#define RT5645_ANC_CLK_ANC (0x0 << 10) +#define RT5645_ANC_CLK_REG (0x1 << 10) +#define RT5645_ANC_ZCD_MASK (0x3 << 8) +#define RT5645_ANC_ZCD_SFT 8 +#define RT5645_ANC_ZCD_DIS (0x0 << 8) +#define RT5645_ANC_ZCD_T1 (0x1 << 8) +#define RT5645_ANC_ZCD_T2 (0x2 << 8) +#define RT5645_ANC_ZCD_WT (0x3 << 8) +#define RT5645_ANC_CS_MASK (0x1 << 7) +#define RT5645_ANC_CS_SFT 7 +#define RT5645_ANC_CS_DIS (0x0 << 7) +#define RT5645_ANC_CS_EN (0x1 << 7) +#define RT5645_ANC_SW_MASK (0x1 << 6) +#define RT5645_ANC_SW_SFT 6 +#define RT5645_ANC_SW_NOR (0x0 << 6) +#define RT5645_ANC_SW_AUTO (0x1 << 6) +#define RT5645_ANC_CO_L_MASK (0x3f) +#define RT5645_ANC_CO_L_SFT 0 + +/* ANC Control 2 (0xb6) */ +#define RT5645_ANC_FG_R_MASK (0xf << 12) +#define RT5645_ANC_FG_R_SFT 12 +#define RT5645_ANC_FG_L_MASK (0xf << 8) +#define RT5645_ANC_FG_L_SFT 8 +#define RT5645_ANC_CG_R_MASK (0xf << 4) +#define RT5645_ANC_CG_R_SFT 4 +#define RT5645_ANC_CG_L_MASK (0xf) +#define RT5645_ANC_CG_L_SFT 0 + +/* ANC Control 3 (0xb6) */ +#define RT5645_ANC_CD_MASK (0x1 << 6) +#define RT5645_ANC_CD_SFT 6 +#define RT5645_ANC_CD_BOTH (0x0 << 6) +#define RT5645_ANC_CD_IND (0x1 << 6) +#define RT5645_ANC_CO_R_MASK (0x3f) +#define RT5645_ANC_CO_R_SFT 0 + +/* Jack Detect Control (0xbb) */ +#define RT5645_JD_MASK (0x7 << 13) +#define RT5645_JD_SFT 13 +#define RT5645_JD_DIS (0x0 << 13) +#define RT5645_JD_GPIO1 (0x1 << 13) +#define RT5645_JD_JD1_IN4P (0x2 << 13) +#define RT5645_JD_JD2_IN4N (0x3 << 13) +#define RT5645_JD_GPIO2 (0x4 << 13) +#define RT5645_JD_GPIO3 (0x5 << 13) +#define RT5645_JD_GPIO4 (0x6 << 13) +#define RT5645_JD_HP_MASK (0x1 << 11) +#define RT5645_JD_HP_SFT 11 +#define RT5645_JD_HP_DIS (0x0 << 11) +#define RT5645_JD_HP_EN (0x1 << 11) +#define RT5645_JD_HP_TRG_MASK (0x1 << 10) +#define RT5645_JD_HP_TRG_SFT 10 +#define RT5645_JD_HP_TRG_LO (0x0 << 10) +#define RT5645_JD_HP_TRG_HI (0x1 << 10) +#define RT5645_JD_SPL_MASK (0x1 << 9) +#define RT5645_JD_SPL_SFT 9 +#define RT5645_JD_SPL_DIS (0x0 << 9) +#define RT5645_JD_SPL_EN (0x1 << 9) +#define RT5645_JD_SPL_TRG_MASK (0x1 << 8) +#define RT5645_JD_SPL_TRG_SFT 8 +#define RT5645_JD_SPL_TRG_LO (0x0 << 8) +#define RT5645_JD_SPL_TRG_HI (0x1 << 8) +#define RT5645_JD_SPR_MASK (0x1 << 7) +#define RT5645_JD_SPR_SFT 7 +#define RT5645_JD_SPR_DIS (0x0 << 7) +#define RT5645_JD_SPR_EN (0x1 << 7) +#define RT5645_JD_SPR_TRG_MASK (0x1 << 6) +#define RT5645_JD_SPR_TRG_SFT 6 +#define RT5645_JD_SPR_TRG_LO (0x0 << 6) +#define RT5645_JD_SPR_TRG_HI (0x1 << 6) +#define RT5645_JD_MO_MASK (0x1 << 5) +#define RT5645_JD_MO_SFT 5 +#define RT5645_JD_MO_DIS (0x0 << 5) +#define RT5645_JD_MO_EN (0x1 << 5) +#define RT5645_JD_MO_TRG_MASK (0x1 << 4) +#define RT5645_JD_MO_TRG_SFT 4 +#define RT5645_JD_MO_TRG_LO (0x0 << 4) +#define RT5645_JD_MO_TRG_HI (0x1 << 4) +#define RT5645_JD_LO_MASK (0x1 << 3) +#define RT5645_JD_LO_SFT 3 +#define RT5645_JD_LO_DIS (0x0 << 3) +#define RT5645_JD_LO_EN (0x1 << 3) +#define RT5645_JD_LO_TRG_MASK (0x1 << 2) +#define RT5645_JD_LO_TRG_SFT 2 +#define RT5645_JD_LO_TRG_LO (0x0 << 2) +#define RT5645_JD_LO_TRG_HI (0x1 << 2) +#define RT5645_JD1_IN4P_MASK (0x1 << 1) +#define RT5645_JD1_IN4P_SFT 1 +#define RT5645_JD1_IN4P_DIS (0x0 << 1) +#define RT5645_JD1_IN4P_EN (0x1 << 1) +#define RT5645_JD2_IN4N_MASK (0x1) +#define RT5645_JD2_IN4N_SFT 0 +#define RT5645_JD2_IN4N_DIS (0x0) +#define RT5645_JD2_IN4N_EN (0x1) + +/* Jack detect for ANC (0xbc) */ +#define RT5645_ANC_DET_MASK (0x3 << 4) +#define RT5645_ANC_DET_SFT 4 +#define RT5645_ANC_DET_DIS (0x0 << 4) +#define RT5645_ANC_DET_MB1 (0x1 << 4) +#define RT5645_ANC_DET_MB2 (0x2 << 4) +#define RT5645_ANC_DET_JD (0x3 << 4) +#define RT5645_AD_TRG_MASK (0x1 << 3) +#define RT5645_AD_TRG_SFT 3 +#define RT5645_AD_TRG_LO (0x0 << 3) +#define RT5645_AD_TRG_HI (0x1 << 3) +#define RT5645_ANCM_DET_MASK (0x3 << 4) +#define RT5645_ANCM_DET_SFT 4 +#define RT5645_ANCM_DET_DIS (0x0 << 4) +#define RT5645_ANCM_DET_MB1 (0x1 << 4) +#define RT5645_ANCM_DET_MB2 (0x2 << 4) +#define RT5645_ANCM_DET_JD (0x3 << 4) +#define RT5645_AMD_TRG_MASK (0x1 << 3) +#define RT5645_AMD_TRG_SFT 3 +#define RT5645_AMD_TRG_LO (0x0 << 3) +#define RT5645_AMD_TRG_HI (0x1 << 3) + +/* IRQ Control 1 (0xbd) */ +#define RT5645_IRQ_JD_MASK (0x1 << 15) +#define RT5645_IRQ_JD_SFT 15 +#define RT5645_IRQ_JD_BP (0x0 << 15) +#define RT5645_IRQ_JD_NOR (0x1 << 15) +#define RT5645_IRQ_OT_MASK (0x1 << 14) +#define RT5645_IRQ_OT_SFT 14 +#define RT5645_IRQ_OT_BP (0x0 << 14) +#define RT5645_IRQ_OT_NOR (0x1 << 14) +#define RT5645_JD_STKY_MASK (0x1 << 13) +#define RT5645_JD_STKY_SFT 13 +#define RT5645_JD_STKY_DIS (0x0 << 13) +#define RT5645_JD_STKY_EN (0x1 << 13) +#define RT5645_OT_STKY_MASK (0x1 << 12) +#define RT5645_OT_STKY_SFT 12 +#define RT5645_OT_STKY_DIS (0x0 << 12) +#define RT5645_OT_STKY_EN (0x1 << 12) +#define RT5645_JD_P_MASK (0x1 << 11) +#define RT5645_JD_P_SFT 11 +#define RT5645_JD_P_NOR (0x0 << 11) +#define RT5645_JD_P_INV (0x1 << 11) +#define RT5645_OT_P_MASK (0x1 << 10) +#define RT5645_OT_P_SFT 10 +#define RT5645_OT_P_NOR (0x0 << 10) +#define RT5645_OT_P_INV (0x1 << 10) +#define RT5645_IRQ_JD_1_1_EN (0x1 << 9) +#define RT5645_JD_1_1_MASK (0x1 << 7) +#define RT5645_JD_1_1_SFT 7 +#define RT5645_JD_1_1_NOR (0x0 << 7) +#define RT5645_JD_1_1_INV (0x1 << 7) + +/* IRQ Control 2 (0xbe) */ +#define RT5645_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5645_IRQ_MB1_OC_SFT 15 +#define RT5645_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5645_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5645_IRQ_MB2_OC_MASK (0x1 << 14) +#define RT5645_IRQ_MB2_OC_SFT 14 +#define RT5645_IRQ_MB2_OC_BP (0x0 << 14) +#define RT5645_IRQ_MB2_OC_NOR (0x1 << 14) +#define RT5645_MB1_OC_STKY_MASK (0x1 << 13) +#define RT5645_MB1_OC_STKY_SFT 13 +#define RT5645_MB1_OC_STKY_DIS (0x0 << 13) +#define RT5645_MB1_OC_STKY_EN (0x1 << 13) +#define RT5645_MB2_OC_STKY_MASK (0x1 << 12) +#define RT5645_MB2_OC_STKY_SFT 12 +#define RT5645_MB2_OC_STKY_DIS (0x0 << 12) +#define RT5645_MB2_OC_STKY_EN (0x1 << 12) +#define RT5645_MB1_OC_P_MASK (0x1 << 7) +#define RT5645_MB1_OC_P_SFT 7 +#define RT5645_MB1_OC_P_NOR (0x0 << 7) +#define RT5645_MB1_OC_P_INV (0x1 << 7) +#define RT5645_MB2_OC_P_MASK (0x1 << 6) +#define RT5645_MB2_OC_P_SFT 6 +#define RT5645_MB2_OC_P_NOR (0x0 << 6) +#define RT5645_MB2_OC_P_INV (0x1 << 6) +#define RT5645_MB1_OC_CLR (0x1 << 3) +#define RT5645_MB1_OC_CLR_SFT 3 +#define RT5645_MB2_OC_CLR (0x1 << 2) +#define RT5645_MB2_OC_CLR_SFT 2 + +/* GPIO Control 1 (0xc0) */ +#define RT5645_GP1_PIN_MASK (0x1 << 15) +#define RT5645_GP1_PIN_SFT 15 +#define RT5645_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5645_GP1_PIN_IRQ (0x1 << 15) +#define RT5645_GP2_PIN_MASK (0x1 << 14) +#define RT5645_GP2_PIN_SFT 14 +#define RT5645_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5645_GP2_PIN_DMIC1_SCL (0x1 << 14) +#define RT5645_GP3_PIN_MASK (0x3 << 12) +#define RT5645_GP3_PIN_SFT 12 +#define RT5645_GP3_PIN_GPIO3 (0x0 << 12) +#define RT5645_GP3_PIN_DMIC1_SDA (0x1 << 12) +#define RT5645_GP3_PIN_IRQ (0x2 << 12) +#define RT5645_GP4_PIN_MASK (0x1 << 11) +#define RT5645_GP4_PIN_SFT 11 +#define RT5645_GP4_PIN_GPIO4 (0x0 << 11) +#define RT5645_GP4_PIN_DMIC2_SDA (0x1 << 11) +#define RT5645_DP_SIG_MASK (0x1 << 10) +#define RT5645_DP_SIG_SFT 10 +#define RT5645_DP_SIG_TEST (0x0 << 10) +#define RT5645_DP_SIG_AP (0x1 << 10) +#define RT5645_GPIO_M_MASK (0x1 << 9) +#define RT5645_GPIO_M_SFT 9 +#define RT5645_GPIO_M_FLT (0x0 << 9) +#define RT5645_GPIO_M_PH (0x1 << 9) +#define RT5645_I2S2_SEL (0x1 << 8) +#define RT5645_I2S2_SEL_SFT 8 +#define RT5645_GP5_PIN_MASK (0x1 << 7) +#define RT5645_GP5_PIN_SFT 7 +#define RT5645_GP5_PIN_GPIO5 (0x0 << 7) +#define RT5645_GP5_PIN_DMIC1_SDA (0x1 << 7) +#define RT5645_GP6_PIN_MASK (0x1 << 6) +#define RT5645_GP6_PIN_SFT 6 +#define RT5645_GP6_PIN_GPIO6 (0x0 << 6) +#define RT5645_GP6_PIN_DMIC2_SDA (0x1 << 6) +#define RT5645_I2S2_DAC_PIN_MASK (0x1 << 4) +#define RT5645_I2S2_DAC_PIN_SFT 4 +#define RT5645_I2S2_DAC_PIN_I2S (0x0 << 4) +#define RT5645_I2S2_DAC_PIN_GPIO (0x1 << 4) +#define RT5645_GP8_PIN_MASK (0x1 << 3) +#define RT5645_GP8_PIN_SFT 3 +#define RT5645_GP8_PIN_GPIO8 (0x0 << 3) +#define RT5645_GP8_PIN_DMIC2_SDA (0x1 << 3) +#define RT5645_GP12_PIN_MASK (0x1 << 2) +#define RT5645_GP12_PIN_SFT 2 +#define RT5645_GP12_PIN_GPIO12 (0x0 << 2) +#define RT5645_GP12_PIN_DMIC2_SDA (0x1 << 2) +#define RT5645_GP11_PIN_MASK (0x1 << 1) +#define RT5645_GP11_PIN_SFT 1 +#define RT5645_GP11_PIN_GPIO11 (0x0 << 1) +#define RT5645_GP11_PIN_DMIC1_SDA (0x1 << 1) +#define RT5645_GP10_PIN_MASK (0x1) +#define RT5645_GP10_PIN_SFT 0 +#define RT5645_GP10_PIN_GPIO10 (0x0) +#define RT5645_GP10_PIN_DMIC2_SDA (0x1) + +/* GPIO Control 3 (0xc2) */ +#define RT5645_GP4_PF_MASK (0x1 << 11) +#define RT5645_GP4_PF_SFT 11 +#define RT5645_GP4_PF_IN (0x0 << 11) +#define RT5645_GP4_PF_OUT (0x1 << 11) +#define RT5645_GP4_OUT_MASK (0x1 << 10) +#define RT5645_GP4_OUT_SFT 10 +#define RT5645_GP4_OUT_LO (0x0 << 10) +#define RT5645_GP4_OUT_HI (0x1 << 10) +#define RT5645_GP4_P_MASK (0x1 << 9) +#define RT5645_GP4_P_SFT 9 +#define RT5645_GP4_P_NOR (0x0 << 9) +#define RT5645_GP4_P_INV (0x1 << 9) +#define RT5645_GP3_PF_MASK (0x1 << 8) +#define RT5645_GP3_PF_SFT 8 +#define RT5645_GP3_PF_IN (0x0 << 8) +#define RT5645_GP3_PF_OUT (0x1 << 8) +#define RT5645_GP3_OUT_MASK (0x1 << 7) +#define RT5645_GP3_OUT_SFT 7 +#define RT5645_GP3_OUT_LO (0x0 << 7) +#define RT5645_GP3_OUT_HI (0x1 << 7) +#define RT5645_GP3_P_MASK (0x1 << 6) +#define RT5645_GP3_P_SFT 6 +#define RT5645_GP3_P_NOR (0x0 << 6) +#define RT5645_GP3_P_INV (0x1 << 6) +#define RT5645_GP2_PF_MASK (0x1 << 5) +#define RT5645_GP2_PF_SFT 5 +#define RT5645_GP2_PF_IN (0x0 << 5) +#define RT5645_GP2_PF_OUT (0x1 << 5) +#define RT5645_GP2_OUT_MASK (0x1 << 4) +#define RT5645_GP2_OUT_SFT 4 +#define RT5645_GP2_OUT_LO (0x0 << 4) +#define RT5645_GP2_OUT_HI (0x1 << 4) +#define RT5645_GP2_P_MASK (0x1 << 3) +#define RT5645_GP2_P_SFT 3 +#define RT5645_GP2_P_NOR (0x0 << 3) +#define RT5645_GP2_P_INV (0x1 << 3) +#define RT5645_GP1_PF_MASK (0x1 << 2) +#define RT5645_GP1_PF_SFT 2 +#define RT5645_GP1_PF_IN (0x0 << 2) +#define RT5645_GP1_PF_OUT (0x1 << 2) +#define RT5645_GP1_OUT_MASK (0x1 << 1) +#define RT5645_GP1_OUT_SFT 1 +#define RT5645_GP1_OUT_LO (0x0 << 1) +#define RT5645_GP1_OUT_HI (0x1 << 1) +#define RT5645_GP1_P_MASK (0x1) +#define RT5645_GP1_P_SFT 0 +#define RT5645_GP1_P_NOR (0x0) +#define RT5645_GP1_P_INV (0x1) + +/* Programmable Register Array Control 1 (0xc8) */ +#define RT5645_REG_SEQ_MASK (0xf << 12) +#define RT5645_REG_SEQ_SFT 12 +#define RT5645_SEQ1_ST_MASK (0x1 << 11) /*RO*/ +#define RT5645_SEQ1_ST_SFT 11 +#define RT5645_SEQ1_ST_RUN (0x0 << 11) +#define RT5645_SEQ1_ST_FIN (0x1 << 11) +#define RT5645_SEQ2_ST_MASK (0x1 << 10) /*RO*/ +#define RT5645_SEQ2_ST_SFT 10 +#define RT5645_SEQ2_ST_RUN (0x0 << 10) +#define RT5645_SEQ2_ST_FIN (0x1 << 10) +#define RT5645_REG_LV_MASK (0x1 << 9) +#define RT5645_REG_LV_SFT 9 +#define RT5645_REG_LV_MX (0x0 << 9) +#define RT5645_REG_LV_PR (0x1 << 9) +#define RT5645_SEQ_2_PT_MASK (0x1 << 8) +#define RT5645_SEQ_2_PT_BIT 8 +#define RT5645_REG_IDX_MASK (0xff) +#define RT5645_REG_IDX_SFT 0 + +/* Programmable Register Array Control 2 (0xc9) */ +#define RT5645_REG_DAT_MASK (0xffff) +#define RT5645_REG_DAT_SFT 0 + +/* Programmable Register Array Control 3 (0xca) */ +#define RT5645_SEQ_DLY_MASK (0xff << 8) +#define RT5645_SEQ_DLY_SFT 8 +#define RT5645_PROG_MASK (0x1 << 7) +#define RT5645_PROG_SFT 7 +#define RT5645_PROG_DIS (0x0 << 7) +#define RT5645_PROG_EN (0x1 << 7) +#define RT5645_SEQ1_PT_RUN (0x1 << 6) +#define RT5645_SEQ1_PT_RUN_BIT 6 +#define RT5645_SEQ2_PT_RUN (0x1 << 5) +#define RT5645_SEQ2_PT_RUN_BIT 5 + +/* Programmable Register Array Control 4 (0xcb) */ +#define RT5645_SEQ1_START_MASK (0xf << 8) +#define RT5645_SEQ1_START_SFT 8 +#define RT5645_SEQ1_END_MASK (0xf) +#define RT5645_SEQ1_END_SFT 0 + +/* Programmable Register Array Control 5 (0xcc) */ +#define RT5645_SEQ2_START_MASK (0xf << 8) +#define RT5645_SEQ2_START_SFT 8 +#define RT5645_SEQ2_END_MASK (0xf) +#define RT5645_SEQ2_END_SFT 0 + +/* Scramble Function (0xcd) */ +#define RT5645_SCB_KEY_MASK (0xff) +#define RT5645_SCB_KEY_SFT 0 + +/* Scramble Control (0xce) */ +#define RT5645_SCB_SWAP_MASK (0x1 << 15) +#define RT5645_SCB_SWAP_SFT 15 +#define RT5645_SCB_SWAP_DIS (0x0 << 15) +#define RT5645_SCB_SWAP_EN (0x1 << 15) +#define RT5645_SCB_MASK (0x1 << 14) +#define RT5645_SCB_SFT 14 +#define RT5645_SCB_DIS (0x0 << 14) +#define RT5645_SCB_EN (0x1 << 14) + +/* Baseback Control (0xcf) */ +#define RT5645_BB_MASK (0x1 << 15) +#define RT5645_BB_SFT 15 +#define RT5645_BB_DIS (0x0 << 15) +#define RT5645_BB_EN (0x1 << 15) +#define RT5645_BB_CT_MASK (0x7 << 12) +#define RT5645_BB_CT_SFT 12 +#define RT5645_BB_CT_A (0x0 << 12) +#define RT5645_BB_CT_B (0x1 << 12) +#define RT5645_BB_CT_C (0x2 << 12) +#define RT5645_BB_CT_D (0x3 << 12) +#define RT5645_M_BB_L_MASK (0x1 << 9) +#define RT5645_M_BB_L_SFT 9 +#define RT5645_M_BB_R_MASK (0x1 << 8) +#define RT5645_M_BB_R_SFT 8 +#define RT5645_M_BB_HPF_L_MASK (0x1 << 7) +#define RT5645_M_BB_HPF_L_SFT 7 +#define RT5645_M_BB_HPF_R_MASK (0x1 << 6) +#define RT5645_M_BB_HPF_R_SFT 6 +#define RT5645_G_BB_BST_MASK (0x3f) +#define RT5645_G_BB_BST_SFT 0 +#define RT5645_G_BB_BST_25DB 0x14 + +/* MP3 Plus Control 1 (0xd0) */ +#define RT5645_M_MP3_L_MASK (0x1 << 15) +#define RT5645_M_MP3_L_SFT 15 +#define RT5645_M_MP3_R_MASK (0x1 << 14) +#define RT5645_M_MP3_R_SFT 14 +#define RT5645_M_MP3_MASK (0x1 << 13) +#define RT5645_M_MP3_SFT 13 +#define RT5645_M_MP3_DIS (0x0 << 13) +#define RT5645_M_MP3_EN (0x1 << 13) +#define RT5645_EG_MP3_MASK (0x1f << 8) +#define RT5645_EG_MP3_SFT 8 +#define RT5645_MP3_HLP_MASK (0x1 << 7) +#define RT5645_MP3_HLP_SFT 7 +#define RT5645_MP3_HLP_DIS (0x0 << 7) +#define RT5645_MP3_HLP_EN (0x1 << 7) +#define RT5645_M_MP3_ORG_L_MASK (0x1 << 6) +#define RT5645_M_MP3_ORG_L_SFT 6 +#define RT5645_M_MP3_ORG_R_MASK (0x1 << 5) +#define RT5645_M_MP3_ORG_R_SFT 5 + +/* MP3 Plus Control 2 (0xd1) */ +#define RT5645_MP3_WT_MASK (0x1 << 13) +#define RT5645_MP3_WT_SFT 13 +#define RT5645_MP3_WT_1_4 (0x0 << 13) +#define RT5645_MP3_WT_1_2 (0x1 << 13) +#define RT5645_OG_MP3_MASK (0x1f << 8) +#define RT5645_OG_MP3_SFT 8 +#define RT5645_HG_MP3_MASK (0x3f) +#define RT5645_HG_MP3_SFT 0 + +/* 3D HP Control 1 (0xd2) */ +#define RT5645_3D_CF_MASK (0x1 << 15) +#define RT5645_3D_CF_SFT 15 +#define RT5645_3D_CF_DIS (0x0 << 15) +#define RT5645_3D_CF_EN (0x1 << 15) +#define RT5645_3D_HP_MASK (0x1 << 14) +#define RT5645_3D_HP_SFT 14 +#define RT5645_3D_HP_DIS (0x0 << 14) +#define RT5645_3D_HP_EN (0x1 << 14) +#define RT5645_3D_BT_MASK (0x1 << 13) +#define RT5645_3D_BT_SFT 13 +#define RT5645_3D_BT_DIS (0x0 << 13) +#define RT5645_3D_BT_EN (0x1 << 13) +#define RT5645_3D_1F_MIX_MASK (0x3 << 11) +#define RT5645_3D_1F_MIX_SFT 11 +#define RT5645_3D_HP_M_MASK (0x1 << 10) +#define RT5645_3D_HP_M_SFT 10 +#define RT5645_3D_HP_M_SUR (0x0 << 10) +#define RT5645_3D_HP_M_FRO (0x1 << 10) +#define RT5645_M_3D_HRTF_MASK (0x1 << 9) +#define RT5645_M_3D_HRTF_SFT 9 +#define RT5645_M_3D_D2H_MASK (0x1 << 8) +#define RT5645_M_3D_D2H_SFT 8 +#define RT5645_M_3D_D2R_MASK (0x1 << 7) +#define RT5645_M_3D_D2R_SFT 7 +#define RT5645_M_3D_REVB_MASK (0x1 << 6) +#define RT5645_M_3D_REVB_SFT 6 + +/* Adjustable high pass filter control 1 (0xd3) */ +#define RT5645_2ND_HPF_MASK (0x1 << 15) +#define RT5645_2ND_HPF_SFT 15 +#define RT5645_2ND_HPF_DIS (0x0 << 15) +#define RT5645_2ND_HPF_EN (0x1 << 15) +#define RT5645_HPF_CF_L_MASK (0x7 << 12) +#define RT5645_HPF_CF_L_SFT 12 +#define RT5645_1ST_HPF_MASK (0x1 << 11) +#define RT5645_1ST_HPF_SFT 11 +#define RT5645_1ST_HPF_DIS (0x0 << 11) +#define RT5645_1ST_HPF_EN (0x1 << 11) +#define RT5645_HPF_CF_R_MASK (0x7 << 8) +#define RT5645_HPF_CF_R_SFT 8 +#define RT5645_ZD_T_MASK (0x3 << 6) +#define RT5645_ZD_T_SFT 6 +#define RT5645_ZD_F_MASK (0x3 << 4) +#define RT5645_ZD_F_SFT 4 +#define RT5645_ZD_F_IM (0x0 << 4) +#define RT5645_ZD_F_ZC_IM (0x1 << 4) +#define RT5645_ZD_F_ZC_IOD (0x2 << 4) +#define RT5645_ZD_F_UN (0x3 << 4) + +/* HP calibration control and Amp detection (0xd6) */ +#define RT5645_SI_DAC_MASK (0x1 << 11) +#define RT5645_SI_DAC_SFT 11 +#define RT5645_SI_DAC_AUTO (0x0 << 11) +#define RT5645_SI_DAC_TEST (0x1 << 11) +#define RT5645_DC_CAL_M_MASK (0x1 << 10) +#define RT5645_DC_CAL_M_SFT 10 +#define RT5645_DC_CAL_M_CAL (0x0 << 10) +#define RT5645_DC_CAL_M_NOR (0x1 << 10) +#define RT5645_DC_CAL_MASK (0x1 << 9) +#define RT5645_DC_CAL_SFT 9 +#define RT5645_DC_CAL_DIS (0x0 << 9) +#define RT5645_DC_CAL_EN (0x1 << 9) +#define RT5645_HPD_RCV_MASK (0x7 << 6) +#define RT5645_HPD_RCV_SFT 6 +#define RT5645_HPD_PS_MASK (0x1 << 5) +#define RT5645_HPD_PS_SFT 5 +#define RT5645_HPD_PS_DIS (0x0 << 5) +#define RT5645_HPD_PS_EN (0x1 << 5) +#define RT5645_CAL_M_MASK (0x1 << 4) +#define RT5645_CAL_M_SFT 4 +#define RT5645_CAL_M_DEP (0x0 << 4) +#define RT5645_CAL_M_CAL (0x1 << 4) +#define RT5645_CAL_MASK (0x1 << 3) +#define RT5645_CAL_SFT 3 +#define RT5645_CAL_DIS (0x0 << 3) +#define RT5645_CAL_EN (0x1 << 3) +#define RT5645_CAL_TEST_MASK (0x1 << 2) +#define RT5645_CAL_TEST_SFT 2 +#define RT5645_CAL_TEST_DIS (0x0 << 2) +#define RT5645_CAL_TEST_EN (0x1 << 2) +#define RT5645_CAL_P_MASK (0x3) +#define RT5645_CAL_P_SFT 0 +#define RT5645_CAL_P_NONE (0x0) +#define RT5645_CAL_P_CAL (0x1) +#define RT5645_CAL_P_DAC_CAL (0x2) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5645_SV_MASK (0x1 << 15) +#define RT5645_SV_SFT 15 +#define RT5645_SV_DIS (0x0 << 15) +#define RT5645_SV_EN (0x1 << 15) +#define RT5645_SPO_SV_MASK (0x1 << 14) +#define RT5645_SPO_SV_SFT 14 +#define RT5645_SPO_SV_DIS (0x0 << 14) +#define RT5645_SPO_SV_EN (0x1 << 14) +#define RT5645_OUT_SV_MASK (0x1 << 13) +#define RT5645_OUT_SV_SFT 13 +#define RT5645_OUT_SV_DIS (0x0 << 13) +#define RT5645_OUT_SV_EN (0x1 << 13) +#define RT5645_HP_SV_MASK (0x1 << 12) +#define RT5645_HP_SV_SFT 12 +#define RT5645_HP_SV_DIS (0x0 << 12) +#define RT5645_HP_SV_EN (0x1 << 12) +#define RT5645_ZCD_DIG_MASK (0x1 << 11) +#define RT5645_ZCD_DIG_SFT 11 +#define RT5645_ZCD_DIG_DIS (0x0 << 11) +#define RT5645_ZCD_DIG_EN (0x1 << 11) +#define RT5645_ZCD_MASK (0x1 << 10) +#define RT5645_ZCD_SFT 10 +#define RT5645_ZCD_PD (0x0 << 10) +#define RT5645_ZCD_PU (0x1 << 10) +#define RT5645_M_ZCD_MASK (0x3f << 4) +#define RT5645_M_ZCD_SFT 4 +#define RT5645_M_ZCD_RM_L (0x1 << 9) +#define RT5645_M_ZCD_RM_R (0x1 << 8) +#define RT5645_M_ZCD_SM_L (0x1 << 7) +#define RT5645_M_ZCD_SM_R (0x1 << 6) +#define RT5645_M_ZCD_OM_L (0x1 << 5) +#define RT5645_M_ZCD_OM_R (0x1 << 4) +#define RT5645_SV_DLY_MASK (0xf) +#define RT5645_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5645_ZCD_HP_MASK (0x1 << 15) +#define RT5645_ZCD_HP_SFT 15 +#define RT5645_ZCD_HP_DIS (0x0 << 15) +#define RT5645_ZCD_HP_EN (0x1 << 15) + + +/* Codec Private Register definition */ +/* DAC ADC Digital Volume (0x00) */ +#define RT5645_DA1_ZDET_SFT 6 + +/* 3D Speaker Control (0x63) */ +#define RT5645_3D_SPK_MASK (0x1 << 15) +#define RT5645_3D_SPK_SFT 15 +#define RT5645_3D_SPK_DIS (0x0 << 15) +#define RT5645_3D_SPK_EN (0x1 << 15) +#define RT5645_3D_SPK_M_MASK (0x3 << 13) +#define RT5645_3D_SPK_M_SFT 13 +#define RT5645_3D_SPK_CG_MASK (0x1f << 8) +#define RT5645_3D_SPK_CG_SFT 8 +#define RT5645_3D_SPK_SG_MASK (0x1f) +#define RT5645_3D_SPK_SG_SFT 0 + +/* Wind Noise Detection Control 1 (0x6c) */ +#define RT5645_WND_MASK (0x1 << 15) +#define RT5645_WND_SFT 15 +#define RT5645_WND_DIS (0x0 << 15) +#define RT5645_WND_EN (0x1 << 15) + +/* Wind Noise Detection Control 2 (0x6d) */ +#define RT5645_WND_FC_NW_MASK (0x3f << 10) +#define RT5645_WND_FC_NW_SFT 10 +#define RT5645_WND_FC_WK_MASK (0x3f << 4) +#define RT5645_WND_FC_WK_SFT 4 + +/* Wind Noise Detection Control 3 (0x6e) */ +#define RT5645_HPF_FC_MASK (0x3f << 6) +#define RT5645_HPF_FC_SFT 6 +#define RT5645_WND_FC_ST_MASK (0x3f) +#define RT5645_WND_FC_ST_SFT 0 + +/* Wind Noise Detection Control 4 (0x6f) */ +#define RT5645_WND_TH_LO_MASK (0x3ff) +#define RT5645_WND_TH_LO_SFT 0 + +/* Wind Noise Detection Control 5 (0x70) */ +#define RT5645_WND_TH_HI_MASK (0x3ff) +#define RT5645_WND_TH_HI_SFT 0 + +/* Wind Noise Detection Control 8 (0x73) */ +#define RT5645_WND_WIND_MASK (0x1 << 13) /* Read-Only */ +#define RT5645_WND_WIND_SFT 13 +#define RT5645_WND_STRONG_MASK (0x1 << 12) /* Read-Only */ +#define RT5645_WND_STRONG_SFT 12 +enum { + RT5645_NO_WIND, + RT5645_BREEZE, + RT5645_STORM, +}; + +/* Dipole Speaker Interface (0x75) */ +#define RT5645_DP_ATT_MASK (0x3 << 14) +#define RT5645_DP_ATT_SFT 14 +#define RT5645_DP_SPK_MASK (0x1 << 10) +#define RT5645_DP_SPK_SFT 10 +#define RT5645_DP_SPK_DIS (0x0 << 10) +#define RT5645_DP_SPK_EN (0x1 << 10) + +/* EQ Pre Volume Control (0xb3) */ +#define RT5645_EQ_PRE_VOL_MASK (0xffff) +#define RT5645_EQ_PRE_VOL_SFT 0 + +/* EQ Post Volume Control (0xb4) */ +#define RT5645_EQ_PST_VOL_MASK (0xffff) +#define RT5645_EQ_PST_VOL_SFT 0 + +/* Jack Detect Control 3 (0xf8) */ +#define RT5645_CMP_MIC_IN_DET_MASK (0x7 << 12) +#define RT5645_JD_CBJ_EN (0x1 << 7) +#define RT5645_JD_CBJ_POL (0x1 << 6) +#define RT5645_JD_TRI_CBJ_SEL_MASK (0x7 << 3) +#define RT5645_JD_TRI_CBJ_SEL_SFT (3) +#define RT5645_JD_TRI_HPO_SEL_MASK (0x7) +#define RT5645_JD_TRI_HPO_SEL_SFT (0) +#define RT5645_JD_F_GPIO_JD1 (0x0) +#define RT5645_JD_F_JD1_1 (0x1) +#define RT5645_JD_F_JD1_2 (0x2) +#define RT5645_JD_F_JD2 (0x3) +#define RT5645_JD_F_JD3 (0x4) +#define RT5645_JD_F_GPIO_JD2 (0x5) +#define RT5645_JD_F_MX0B_12 (0x6) + +/* Digital Misc Control (0xfa) */ +#define RT5645_RST_DSP (0x1 << 13) +#define RT5645_IF1_ADC1_IN1_SEL (0x1 << 12) +#define RT5645_IF1_ADC1_IN1_SFT 12 +#define RT5645_IF1_ADC1_IN2_SEL (0x1 << 11) +#define RT5645_IF1_ADC1_IN2_SFT 11 +#define RT5645_IF1_ADC2_IN1_SEL (0x1 << 10) +#define RT5645_IF1_ADC2_IN1_SFT 10 +#define RT5645_DIG_GATE_CTRL 0x1 + +/* General Control2 (0xfb) */ +#define RT5645_RXDC_SRC_MASK (0x1 << 7) +#define RT5645_RXDC_SRC_STO (0x0 << 7) +#define RT5645_RXDC_SRC_MONO (0x1 << 7) +#define RT5645_RXDC_SRC_SFT (7) +#define RT5645_MICBIAS1_POW_CTRL_SEL_MASK (0x1 << 5) +#define RT5645_MICBIAS1_POW_CTRL_SEL_A (0x0 << 5) +#define RT5645_MICBIAS1_POW_CTRL_SEL_M (0x1 << 5) +#define RT5645_MICBIAS2_POW_CTRL_SEL_MASK (0x1 << 4) +#define RT5645_MICBIAS2_POW_CTRL_SEL_A (0x0 << 4) +#define RT5645_MICBIAS2_POW_CTRL_SEL_M (0x1 << 4) +#define RT5645_RXDP2_SEL_MASK (0x1 << 3) +#define RT5645_RXDP2_SEL_IF2 (0x0 << 3) +#define RT5645_RXDP2_SEL_ADC (0x1 << 3) +#define RT5645_RXDP2_SEL_SFT (3) + +/* General Control3 (0xfc) */ +#define RT5645_JD_PSV_MODE (0x1 << 12) +#define RT5645_IRQ_CLK_GATE_CTRL (0x1 << 11) +#define RT5645_DET_CLK_MASK (0x3 << 9) +#define RT5645_DET_CLK_DIS (0x0 << 9) +#define RT5645_DET_CLK_MODE1 (0x1 << 9) +#define RT5645_DET_CLK_MODE2 (0x2 << 9) +#define RT5645_MICINDET_MANU (0x1 << 7) +#define RT5645_RING2_SLEEVE_GND (0x1 << 5) + +/* Vendor ID (0xfd) */ +#define RT5645_VER_C 0x2 +#define RT5645_VER_D 0x3 + + +/* Volume Rescale */ +#define RT5645_VOL_RSCL_MAX 0x27 +#define RT5645_VOL_RSCL_RANGE 0x1F +/* Debug String Length */ +#define RT5645_REG_DISP_LEN 23 + + +/* System Clock Source */ +enum { + RT5645_SCLK_S_MCLK, + RT5645_SCLK_S_PLL1, + RT5645_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5645_PLL1_S_MCLK, + RT5645_PLL1_S_BCLK1, + RT5645_PLL1_S_BCLK2, +}; + +enum { + RT5645_AIF1, + RT5645_AIF2, + RT5645_AIFS, +}; + +enum { + RT5645_DMIC1_DISABLE, + RT5645_DMIC_DATA_IN2P, + RT5645_DMIC_DATA_GPIO6, + RT5645_DMIC_DATA_GPIO10, + RT5645_DMIC_DATA_GPIO12, +}; + +enum { + RT5645_DMIC2_DISABLE, + RT5645_DMIC_DATA_IN2N, + RT5645_DMIC_DATA_GPIO5, + RT5645_DMIC_DATA_GPIO11, +}; + +enum { + CODEC_TYPE_RT5645, + CODEC_TYPE_RT5650, +}; + +/* filter mask */ +enum { + RT5645_DA_STEREO_FILTER = 0x1, + RT5645_DA_MONO_L_FILTER = (0x1 << 1), + RT5645_DA_MONO_R_FILTER = (0x1 << 2), + RT5645_AD_STEREO_FILTER = (0x1 << 3), + RT5645_AD_MONO_L_FILTER = (0x1 << 4), + RT5645_AD_MONO_R_FILTER = (0x1 << 5), +}; + +int rt5645_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +int rt5645_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack, + struct snd_soc_jack *btn_jack); +#endif /* __RT5645_H__ */ diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c new file mode 100644 index 0000000000..0cee4fd1c8 --- /dev/null +++ b/sound/soc/codecs/rt5651.c @@ -0,0 +1,2289 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5651.c -- RT5651 ALSA SoC audio codec driver + * + * Copyright 2014 Realtek Semiconductor Corp. + * Author: Bard Liao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5651.h" + +#define RT5651_DEVICE_ID_VALUE 0x6281 + +#define RT5651_PR_RANGE_BASE (0xff + 1) +#define RT5651_PR_SPACING 0x100 + +#define RT5651_PR_BASE (RT5651_PR_RANGE_BASE + (0 * RT5651_PR_SPACING)) + +static const struct regmap_range_cfg rt5651_ranges[] = { + { .name = "PR", .range_min = RT5651_PR_BASE, + .range_max = RT5651_PR_BASE + 0xb4, + .selector_reg = RT5651_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5651_PRIV_DATA, + .window_len = 0x1, }, +}; + +static const struct reg_sequence init_list[] = { + {RT5651_PR_BASE + 0x3d, 0x3e00}, +}; + +static const struct reg_default rt5651_reg[] = { + { 0x00, 0x0000 }, + { 0x02, 0xc8c8 }, + { 0x03, 0xc8c8 }, + { 0x05, 0x0000 }, + { 0x0d, 0x0000 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x10, 0x0808 }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0c00 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x27, 0x7860 }, + { 0x28, 0x7070 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5252 }, + { 0x2b, 0x5454 }, + { 0x2f, 0x0000 }, + { 0x30, 0x5000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x006f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x006f }, + { 0x45, 0x6000 }, + { 0x4d, 0x0000 }, + { 0x4e, 0x0000 }, + { 0x4f, 0x0279 }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x0279 }, + { 0x53, 0xf000 }, + { 0x61, 0x0000 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c0 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x73, 0x1104 }, + { 0x74, 0x0c00 }, + { 0x75, 0x1400 }, + { 0x77, 0x0c00 }, + { 0x78, 0x4000 }, + { 0x79, 0x0123 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0800 }, + { 0x84, 0x0000 }, + { 0x85, 0x0008 }, + { 0x89, 0x0000 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0000 }, + { 0x93, 0x2000 }, + { 0x94, 0x0200 }, + { 0xb0, 0x2080 }, + { 0xb1, 0x0000 }, + { 0xb4, 0x2206 }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x0000 }, + { 0xc0, 0x0400 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xcf, 0x0013 }, + { 0xd0, 0x0680 }, + { 0xd1, 0x1c17 }, + { 0xd3, 0xb320 }, + { 0xd9, 0x0809 }, + { 0xfa, 0x0010 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6281 }, +}; + +static bool rt5651_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5651_ranges); i++) { + if ((reg >= rt5651_ranges[i].window_start && + reg <= rt5651_ranges[i].window_start + + rt5651_ranges[i].window_len) || + (reg >= rt5651_ranges[i].range_min && + reg <= rt5651_ranges[i].range_max)) { + return true; + } + } + + switch (reg) { + case RT5651_RESET: + case RT5651_PRIV_DATA: + case RT5651_EQ_CTRL1: + case RT5651_ALC_1: + case RT5651_IRQ_CTRL2: + case RT5651_INT_IRQ_ST: + case RT5651_PGM_REG_ARR1: + case RT5651_PGM_REG_ARR3: + case RT5651_VENDOR_ID: + case RT5651_DEVICE_ID: + return true; + default: + return false; + } +} + +static bool rt5651_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5651_ranges); i++) { + if ((reg >= rt5651_ranges[i].window_start && + reg <= rt5651_ranges[i].window_start + + rt5651_ranges[i].window_len) || + (reg >= rt5651_ranges[i].range_min && + reg <= rt5651_ranges[i].range_max)) { + return true; + } + } + + switch (reg) { + case RT5651_RESET: + case RT5651_VERSION_ID: + case RT5651_VENDOR_ID: + case RT5651_DEVICE_ID: + case RT5651_HP_VOL: + case RT5651_LOUT_CTRL1: + case RT5651_LOUT_CTRL2: + case RT5651_IN1_IN2: + case RT5651_IN3: + case RT5651_INL1_INR1_VOL: + case RT5651_INL2_INR2_VOL: + case RT5651_DAC1_DIG_VOL: + case RT5651_DAC2_DIG_VOL: + case RT5651_DAC2_CTRL: + case RT5651_ADC_DIG_VOL: + case RT5651_ADC_DATA: + case RT5651_ADC_BST_VOL: + case RT5651_STO1_ADC_MIXER: + case RT5651_STO2_ADC_MIXER: + case RT5651_AD_DA_MIXER: + case RT5651_STO_DAC_MIXER: + case RT5651_DD_MIXER: + case RT5651_DIG_INF_DATA: + case RT5651_PDM_CTL: + case RT5651_REC_L1_MIXER: + case RT5651_REC_L2_MIXER: + case RT5651_REC_R1_MIXER: + case RT5651_REC_R2_MIXER: + case RT5651_HPO_MIXER: + case RT5651_OUT_L1_MIXER: + case RT5651_OUT_L2_MIXER: + case RT5651_OUT_L3_MIXER: + case RT5651_OUT_R1_MIXER: + case RT5651_OUT_R2_MIXER: + case RT5651_OUT_R3_MIXER: + case RT5651_LOUT_MIXER: + case RT5651_PWR_DIG1: + case RT5651_PWR_DIG2: + case RT5651_PWR_ANLG1: + case RT5651_PWR_ANLG2: + case RT5651_PWR_MIXER: + case RT5651_PWR_VOL: + case RT5651_PRIV_INDEX: + case RT5651_PRIV_DATA: + case RT5651_I2S1_SDP: + case RT5651_I2S2_SDP: + case RT5651_ADDA_CLK1: + case RT5651_ADDA_CLK2: + case RT5651_DMIC: + case RT5651_TDM_CTL_1: + case RT5651_TDM_CTL_2: + case RT5651_TDM_CTL_3: + case RT5651_GLB_CLK: + case RT5651_PLL_CTRL1: + case RT5651_PLL_CTRL2: + case RT5651_PLL_MODE_1: + case RT5651_PLL_MODE_2: + case RT5651_PLL_MODE_3: + case RT5651_PLL_MODE_4: + case RT5651_PLL_MODE_5: + case RT5651_PLL_MODE_6: + case RT5651_PLL_MODE_7: + case RT5651_DEPOP_M1: + case RT5651_DEPOP_M2: + case RT5651_DEPOP_M3: + case RT5651_CHARGE_PUMP: + case RT5651_MICBIAS: + case RT5651_A_JD_CTL1: + case RT5651_EQ_CTRL1: + case RT5651_EQ_CTRL2: + case RT5651_ALC_1: + case RT5651_ALC_2: + case RT5651_ALC_3: + case RT5651_JD_CTRL1: + case RT5651_JD_CTRL2: + case RT5651_IRQ_CTRL1: + case RT5651_IRQ_CTRL2: + case RT5651_INT_IRQ_ST: + case RT5651_GPIO_CTRL1: + case RT5651_GPIO_CTRL2: + case RT5651_GPIO_CTRL3: + case RT5651_PGM_REG_ARR1: + case RT5651_PGM_REG_ARR2: + case RT5651_PGM_REG_ARR3: + case RT5651_PGM_REG_ARR4: + case RT5651_PGM_REG_ARR5: + case RT5651_SCB_FUNC: + case RT5651_SCB_CTRL: + case RT5651_BASE_BACK: + case RT5651_MP3_PLUS1: + case RT5651_MP3_PLUS2: + case RT5651_ADJ_HPF_CTRL1: + case RT5651_ADJ_HPF_CTRL2: + case RT5651_HP_CALIB_AMP_DET: + case RT5651_HP_CALIB2: + case RT5651_SV_ZCD1: + case RT5651_SV_ZCD2: + case RT5651_D_MISC: + case RT5651_DUMMY2: + case RT5651_DUMMY3: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -6562, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -1762, 3000); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5651_data_select[] = { + "Normal", "Swap", "left copy to right", "right copy to left"}; + +static SOC_ENUM_SINGLE_DECL(rt5651_if2_dac_enum, RT5651_DIG_INF_DATA, + RT5651_IF2_DAC_SEL_SFT, rt5651_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5651_if2_adc_enum, RT5651_DIG_INF_DATA, + RT5651_IF2_ADC_SEL_SFT, rt5651_data_select); + +static const struct snd_kcontrol_new rt5651_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_TLV("HP Playback Volume", RT5651_HP_VOL, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv), + /* OUTPUT Control */ + SOC_DOUBLE_TLV("OUT Playback Volume", RT5651_LOUT_CTRL1, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE("DAC2 Playback Switch", RT5651_DAC2_CTRL, + RT5651_M_DAC_L2_VOL_SFT, RT5651_M_DAC_R2_VOL_SFT, 1, 1), + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5651_DAC1_DIG_VOL, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, + 175, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5651_DAC2_DIG_VOL, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, + 175, 0, dac_vol_tlv), + /* IN1/IN2/IN3 Control */ + SOC_SINGLE_TLV("IN1 Boost", RT5651_IN1_IN2, + RT5651_BST_SFT1, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN2 Boost", RT5651_IN1_IN2, + RT5651_BST_SFT2, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN3 Boost", RT5651_IN3, + RT5651_BST_SFT1, 8, 0, bst_tlv), + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5651_INL1_INR1_VOL, + RT5651_INL_VOL_SFT, RT5651_INR_VOL_SFT, + 31, 1, in_vol_tlv), + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5651_ADC_DIG_VOL, + RT5651_L_MUTE_SFT, RT5651_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5651_ADC_DIG_VOL, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, + 127, 0, adc_vol_tlv), + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5651_ADC_DATA, + RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, + 127, 0, adc_vol_tlv), + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("ADC Boost Gain", RT5651_ADC_BST_VOL, + RT5651_ADC_L_BST_SFT, RT5651_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + /* ASRC */ + SOC_SINGLE("IF1 ASRC Switch", RT5651_PLL_MODE_1, + RT5651_STO1_T_SFT, 1, 0), + SOC_SINGLE("IF2 ASRC Switch", RT5651_PLL_MODE_1, + RT5651_STO2_T_SFT, 1, 0), + SOC_SINGLE("DMIC ASRC Switch", RT5651_PLL_MODE_1, + RT5651_DMIC_1_M_SFT, 1, 0), + + SOC_ENUM("ADC IF2 Data Switch", rt5651_if2_adc_enum), + SOC_ENUM("DAC IF2 Data Switch", rt5651_if2_dac_enum), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + int idx, rate; + + rate = rt5651->sysclk / rl6231_get_pre_div(rt5651->regmap, + RT5651_ADDA_CLK1, RT5651_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); + if (idx < 0) + dev_err(component->dev, "Failed to set DMIC clock\n"); + else + snd_soc_component_update_bits(component, RT5651_DMIC, RT5651_DMIC_CLK_MASK, + idx << RT5651_DMIC_CLK_SFT); + + return idx; +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5651_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER, + RT5651_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO1_ADC_MIXER, + RT5651_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER, + RT5651_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO1_ADC_MIXER, + RT5651_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO2_ADC_MIXER, + RT5651_M_STO2_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO2_ADC_MIXER, + RT5651_M_STO2_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO2_ADC_MIXER, + RT5651_M_STO2_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO2_ADC_MIXER, + RT5651_M_STO2_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5651_AD_DA_MIXER, + RT5651_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INF1 Switch", RT5651_AD_DA_MIXER, + RT5651_M_IF1_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5651_AD_DA_MIXER, + RT5651_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INF1 Switch", RT5651_AD_DA_MIXER, + RT5651_M_IF1_DAC_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_L1_MIXL_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_L2_MIXL_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_R1_MIXL_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_R1_MIXR_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_R2_MIXR_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_STO_DAC_MIXER, + RT5651_M_DAC_L1_MIXR_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_dd_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_L2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_R2_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_dd_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_R2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_DD_MIXER, + RT5651_M_STO_DD_L2_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5651_rec_l_mix[] = { + SOC_DAPM_SINGLE("INL1 Switch", RT5651_REC_L2_MIXER, + RT5651_M_IN1_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5651_REC_L2_MIXER, + RT5651_M_BST3_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5651_REC_L2_MIXER, + RT5651_M_BST2_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5651_REC_L2_MIXER, + RT5651_M_BST1_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_rec_r_mix[] = { + SOC_DAPM_SINGLE("INR1 Switch", RT5651_REC_R2_MIXER, + RT5651_M_IN1_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5651_REC_R2_MIXER, + RT5651_M_BST3_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5651_REC_R2_MIXER, + RT5651_M_BST2_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5651_REC_R2_MIXER, + RT5651_M_BST1_RM_R_SFT, 1, 1), +}; + +/* Analog Output Mixer */ + +static const struct snd_kcontrol_new rt5651_out_l_mix[] = { + SOC_DAPM_SINGLE("BST1 Switch", RT5651_OUT_L3_MIXER, + RT5651_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5651_OUT_L3_MIXER, + RT5651_M_BST2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL1 Switch", RT5651_OUT_L3_MIXER, + RT5651_M_IN1_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXL Switch", RT5651_OUT_L3_MIXER, + RT5651_M_RM_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_OUT_L3_MIXER, + RT5651_M_DAC_L1_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_out_r_mix[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5651_OUT_R3_MIXER, + RT5651_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5651_OUT_R3_MIXER, + RT5651_M_BST1_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR1 Switch", RT5651_OUT_R3_MIXER, + RT5651_M_IN1_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXR Switch", RT5651_OUT_R3_MIXER, + RT5651_M_RM_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_OUT_R3_MIXER, + RT5651_M_DAC_R1_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_hpo_mix[] = { + SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5651_HPO_MIXER, + RT5651_M_DAC1_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5651_HPO_MIXER, + RT5651_M_HPVOL_HM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5651_lout_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_LOUT_MIXER, + RT5651_M_DAC_L1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5651_LOUT_MIXER, + RT5651_M_DAC_R1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL L Switch", RT5651_LOUT_MIXER, + RT5651_M_OV_L_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL R Switch", RT5651_LOUT_MIXER, + RT5651_M_OV_R_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new outvol_l_control = + SOC_DAPM_SINGLE("Switch", RT5651_LOUT_CTRL1, + RT5651_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new outvol_r_control = + SOC_DAPM_SINGLE("Switch", RT5651_LOUT_CTRL1, + RT5651_VOL_R_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_l_mute_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_LOUT_CTRL1, + RT5651_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_r_mute_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_LOUT_CTRL1, + RT5651_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpovol_l_control = + SOC_DAPM_SINGLE("Switch", RT5651_HP_VOL, + RT5651_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new hpovol_r_control = + SOC_DAPM_SINGLE("Switch", RT5651_HP_VOL, + RT5651_VOL_R_SFT, 1, 1); + +static const struct snd_kcontrol_new hpo_l_mute_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL, + RT5651_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpo_r_mute_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL, + RT5651_R_MUTE_SFT, 1, 1); + +/* Stereo ADC source */ +static const char * const rt5651_stereo1_adc1_src[] = {"DD MIX", "ADC"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_stereo1_adc1_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO1_ADC_1_SRC_SFT, rt5651_stereo1_adc1_src); + +static const struct snd_kcontrol_new rt5651_sto1_adc_l1_mux = + SOC_DAPM_ENUM("Stereo1 ADC L1 source", rt5651_stereo1_adc1_enum); + +static const struct snd_kcontrol_new rt5651_sto1_adc_r1_mux = + SOC_DAPM_ENUM("Stereo1 ADC R1 source", rt5651_stereo1_adc1_enum); + +static const char * const rt5651_stereo1_adc2_src[] = {"DMIC", "DD MIX"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_stereo1_adc2_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO1_ADC_2_SRC_SFT, rt5651_stereo1_adc2_src); + +static const struct snd_kcontrol_new rt5651_sto1_adc_l2_mux = + SOC_DAPM_ENUM("Stereo1 ADC L2 source", rt5651_stereo1_adc2_enum); + +static const struct snd_kcontrol_new rt5651_sto1_adc_r2_mux = + SOC_DAPM_ENUM("Stereo1 ADC R2 source", rt5651_stereo1_adc2_enum); + +/* Mono ADC source */ +static const char * const rt5651_sto2_adc_l1_src[] = {"DD MIXL", "ADCL"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_sto2_adc_l1_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO2_ADC_L1_SRC_SFT, rt5651_sto2_adc_l1_src); + +static const struct snd_kcontrol_new rt5651_sto2_adc_l1_mux = + SOC_DAPM_ENUM("Stereo2 ADC1 left source", rt5651_sto2_adc_l1_enum); + +static const char * const rt5651_sto2_adc_l2_src[] = {"DMIC L", "DD MIXL"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_sto2_adc_l2_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO2_ADC_L2_SRC_SFT, rt5651_sto2_adc_l2_src); + +static const struct snd_kcontrol_new rt5651_sto2_adc_l2_mux = + SOC_DAPM_ENUM("Stereo2 ADC2 left source", rt5651_sto2_adc_l2_enum); + +static const char * const rt5651_sto2_adc_r1_src[] = {"DD MIXR", "ADCR"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_sto2_adc_r1_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO2_ADC_R1_SRC_SFT, rt5651_sto2_adc_r1_src); + +static const struct snd_kcontrol_new rt5651_sto2_adc_r1_mux = + SOC_DAPM_ENUM("Stereo2 ADC1 right source", rt5651_sto2_adc_r1_enum); + +static const char * const rt5651_sto2_adc_r2_src[] = {"DMIC R", "DD MIXR"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_sto2_adc_r2_enum, RT5651_STO1_ADC_MIXER, + RT5651_STO2_ADC_R2_SRC_SFT, rt5651_sto2_adc_r2_src); + +static const struct snd_kcontrol_new rt5651_sto2_adc_r2_mux = + SOC_DAPM_ENUM("Stereo2 ADC2 right source", rt5651_sto2_adc_r2_enum); + +/* DAC2 channel source */ + +static const char * const rt5651_dac_src[] = {"IF1", "IF2"}; + +static SOC_ENUM_SINGLE_DECL(rt5651_dac_l2_enum, RT5651_DAC2_CTRL, + RT5651_SEL_DAC_L2_SFT, rt5651_dac_src); + +static const struct snd_kcontrol_new rt5651_dac_l2_mux = + SOC_DAPM_ENUM("DAC2 left channel source", rt5651_dac_l2_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5651_dac_r2_enum, RT5651_DAC2_CTRL, + RT5651_SEL_DAC_R2_SFT, rt5651_dac_src); + +static const struct snd_kcontrol_new rt5651_dac_r2_mux = + SOC_DAPM_ENUM("DAC2 right channel source", rt5651_dac_r2_enum); + +/* IF2_ADC channel source */ + +static const char * const rt5651_adc_src[] = {"IF1 ADC1", "IF1 ADC2"}; + +static SOC_ENUM_SINGLE_DECL(rt5651_if2_adc_src_enum, RT5651_DIG_INF_DATA, + RT5651_IF2_ADC_SRC_SFT, rt5651_adc_src); + +static const struct snd_kcontrol_new rt5651_if2_adc_src_mux = + SOC_DAPM_ENUM("IF2 ADC channel source", rt5651_if2_adc_src_enum); + +/* PDM select */ +static const char * const rt5651_pdm_sel[] = {"DD MIX", "Stereo DAC MIX"}; + +static SOC_ENUM_SINGLE_DECL( + rt5651_pdm_l_sel_enum, RT5651_PDM_CTL, + RT5651_PDM_L_SEL_SFT, rt5651_pdm_sel); + +static SOC_ENUM_SINGLE_DECL( + rt5651_pdm_r_sel_enum, RT5651_PDM_CTL, + RT5651_PDM_R_SEL_SFT, rt5651_pdm_sel); + +static const struct snd_kcontrol_new rt5651_pdm_l_mux = + SOC_DAPM_ENUM("PDM L select", rt5651_pdm_l_sel_enum); + +static const struct snd_kcontrol_new rt5651_pdm_r_mux = + SOC_DAPM_ENUM("PDM R select", rt5651_pdm_r_sel_enum); + +static int rt5651_amp_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* depop parameters */ + regmap_update_bits(rt5651->regmap, RT5651_PR_BASE + + RT5651_CHPUMP_INT_REG1, 0x0700, 0x0200); + regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M2, + RT5651_DEPOP_MASK, RT5651_DEPOP_MAN); + regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M1, + RT5651_HP_CP_MASK | RT5651_HP_SG_MASK | + RT5651_HP_CB_MASK, RT5651_HP_CP_PU | + RT5651_HP_SG_DIS | RT5651_HP_CB_PU); + regmap_write(rt5651->regmap, RT5651_PR_BASE + + RT5651_HP_DCC_INT1, 0x9f00); + /* headphone amp power on */ + regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1, + RT5651_PWR_FV1 | RT5651_PWR_FV2, 0); + regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1, + RT5651_PWR_HA, + RT5651_PWR_HA); + usleep_range(10000, 15000); + regmap_update_bits(rt5651->regmap, RT5651_PWR_ANLG1, + RT5651_PWR_FV1 | RT5651_PWR_FV2 , + RT5651_PWR_FV1 | RT5651_PWR_FV2); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5651_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* headphone unmute sequence */ + regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M2, + RT5651_DEPOP_MASK | RT5651_DIG_DP_MASK, + RT5651_DEPOP_AUTO | RT5651_DIG_DP_EN); + regmap_update_bits(rt5651->regmap, RT5651_CHARGE_PUMP, + RT5651_PM_HP_MASK, RT5651_PM_HP_HV); + + regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M3, + RT5651_CP_FQ1_MASK | RT5651_CP_FQ2_MASK | + RT5651_CP_FQ3_MASK, + (RT5651_CP_FQ_192_KHZ << RT5651_CP_FQ1_SFT) | + (RT5651_CP_FQ_12_KHZ << RT5651_CP_FQ2_SFT) | + (RT5651_CP_FQ_192_KHZ << RT5651_CP_FQ3_SFT)); + + regmap_write(rt5651->regmap, RT5651_PR_BASE + + RT5651_MAMP_INT_REG2, 0x1c00); + regmap_update_bits(rt5651->regmap, RT5651_DEPOP_M1, + RT5651_HP_CP_MASK | RT5651_HP_SG_MASK, + RT5651_HP_CP_PD | RT5651_HP_SG_EN); + regmap_update_bits(rt5651->regmap, RT5651_PR_BASE + + RT5651_CHPUMP_INT_REG1, 0x0700, 0x0400); + rt5651->hp_mute = false; + break; + + case SND_SOC_DAPM_PRE_PMD: + rt5651->hp_mute = true; + usleep_range(70000, 75000); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5651_hp_post_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!rt5651->hp_mute) + usleep_range(80000, 85000); + + break; + + default: + return 0; + } + + return 0; +} + +static int rt5651_bst1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, + RT5651_PWR_BST1_OP2, RT5651_PWR_BST1_OP2); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, + RT5651_PWR_BST1_OP2, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5651_bst2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, + RT5651_PWR_BST2_OP2, RT5651_PWR_BST2_OP2); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, + RT5651_PWR_BST2_OP2, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5651_bst3_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, + RT5651_PWR_BST3_OP2, RT5651_PWR_BST3_OP2); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, + RT5651_PWR_BST3_OP2, 0); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = { + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5651_PLL_MODE_2, + 15, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5651_PLL_MODE_2, + 14, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("STO1 DAC ASRC", 1, RT5651_PLL_MODE_2, + 13, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("STO2 DAC ASRC", 1, RT5651_PLL_MODE_2, + 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC ASRC", 1, RT5651_PLL_MODE_2, + 11, 0, NULL, 0), + + /* micbias */ + SND_SOC_DAPM_SUPPLY("LDO", RT5651_PWR_ANLG1, + RT5651_PWR_LDO_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("micbias1", RT5651_PWR_ANLG2, + RT5651_PWR_MB1_BIT, 0, NULL, 0), + /* Input Lines */ + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("MIC3"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + SND_SOC_DAPM_SUPPLY("DMIC CLK", RT5651_DMIC, RT5651_DMIC_1_EN_SFT, + 0, set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + /* Boost */ + SND_SOC_DAPM_PGA_E("BST1", RT5651_PWR_ANLG2, + RT5651_PWR_BST1_BIT, 0, NULL, 0, rt5651_bst1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("BST2", RT5651_PWR_ANLG2, + RT5651_PWR_BST2_BIT, 0, NULL, 0, rt5651_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("BST3", RT5651_PWR_ANLG2, + RT5651_PWR_BST3_BIT, 0, NULL, 0, rt5651_bst3_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + /* Input Volume */ + SND_SOC_DAPM_PGA("INL1 VOL", RT5651_PWR_VOL, + RT5651_PWR_IN1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR1 VOL", RT5651_PWR_VOL, + RT5651_PWR_IN1_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INL2 VOL", RT5651_PWR_VOL, + RT5651_PWR_IN2_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR2 VOL", RT5651_PWR_VOL, + RT5651_PWR_IN2_R_BIT, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5651_PWR_MIXER, RT5651_PWR_RM_L_BIT, 0, + rt5651_rec_l_mix, ARRAY_SIZE(rt5651_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5651_PWR_MIXER, RT5651_PWR_RM_R_BIT, 0, + rt5651_rec_r_mix, ARRAY_SIZE(rt5651_rec_r_mix)), + /* ADCs */ + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC L Power", RT5651_PWR_DIG1, + RT5651_PWR_ADC_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC R Power", RT5651_PWR_DIG1, + RT5651_PWR_ADC_R_BIT, 0, NULL, 0), + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto1_adc_l2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto1_adc_r2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto1_adc_l1_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto1_adc_r1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto2_adc_l2_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto2_adc_l1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto2_adc_r1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5651_sto2_adc_r2_mux), + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("Stereo1 Filter", RT5651_PWR_DIG2, + RT5651_PWR_ADC_STO1_F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Stereo2 Filter", RT5651_PWR_DIG2, + RT5651_PWR_ADC_STO2_F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5651_sto1_adc_l_mix, + ARRAY_SIZE(rt5651_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5651_sto1_adc_r_mix, + ARRAY_SIZE(rt5651_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Stereo2 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5651_sto2_adc_l_mix, + ARRAY_SIZE(rt5651_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo2 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5651_sto2_adc_r_mix, + ARRAY_SIZE(rt5651_sto2_adc_r_mix)), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5651_PWR_DIG1, + RT5651_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5651_PWR_DIG1, + RT5651_PWR_I2S2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("IF2 ADC", SND_SOC_NOPM, 0, 0, + &rt5651_if2_adc_src_mux), + + /* Digital Interface Select */ + + SND_SOC_DAPM_MUX("PDM L Mux", RT5651_PDM_CTL, + RT5651_M_PDM_L_SFT, 1, &rt5651_pdm_l_mux), + SND_SOC_DAPM_MUX("PDM R Mux", RT5651_PDM_CTL, + RT5651_M_PDM_R_SFT, 1, &rt5651_pdm_r_mux), + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Audio DSP */ + SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5651_dac_l_mix, ARRAY_SIZE(rt5651_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5651_dac_r_mix, ARRAY_SIZE(rt5651_dac_r_mix)), + + /* DAC2 channel Mux */ + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_dac_r2_mux), + SND_SOC_DAPM_PGA("DAC L2 Volume", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC R2 Volume", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5651_PWR_DIG2, + RT5651_PWR_DAC_STO1_F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Stero2 DAC Power", RT5651_PWR_DIG2, + RT5651_PWR_DAC_STO2_F_BIT, 0, NULL, 0), + /* DAC Mixer */ + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5651_sto_dac_l_mix, + ARRAY_SIZE(rt5651_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5651_sto_dac_r_mix, + ARRAY_SIZE(rt5651_sto_dac_r_mix)), + SND_SOC_DAPM_MIXER("DD MIXL", SND_SOC_NOPM, 0, 0, + rt5651_dd_dac_l_mix, + ARRAY_SIZE(rt5651_dd_dac_l_mix)), + SND_SOC_DAPM_MIXER("DD MIXR", SND_SOC_NOPM, 0, 0, + rt5651_dd_dac_r_mix, + ARRAY_SIZE(rt5651_dd_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5651_PWR_DIG1, + RT5651_PWR_DAC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5651_PWR_DIG1, + RT5651_PWR_DAC_R1_BIT, 0, NULL, 0), + /* OUT Mixer */ + SND_SOC_DAPM_MIXER("OUT MIXL", RT5651_PWR_MIXER, RT5651_PWR_OM_L_BIT, + 0, rt5651_out_l_mix, ARRAY_SIZE(rt5651_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5651_PWR_MIXER, RT5651_PWR_OM_R_BIT, + 0, rt5651_out_r_mix, ARRAY_SIZE(rt5651_out_r_mix)), + /* Ouput Volume */ + SND_SOC_DAPM_SWITCH("OUTVOL L", RT5651_PWR_VOL, + RT5651_PWR_OV_L_BIT, 0, &outvol_l_control), + SND_SOC_DAPM_SWITCH("OUTVOL R", RT5651_PWR_VOL, + RT5651_PWR_OV_R_BIT, 0, &outvol_r_control), + SND_SOC_DAPM_SWITCH("HPOVOL L", RT5651_PWR_VOL, + RT5651_PWR_HV_L_BIT, 0, &hpovol_l_control), + SND_SOC_DAPM_SWITCH("HPOVOL R", RT5651_PWR_VOL, + RT5651_PWR_HV_R_BIT, 0, &hpovol_r_control), + SND_SOC_DAPM_PGA("INL1", RT5651_PWR_VOL, + RT5651_PWR_IN1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR1", RT5651_PWR_VOL, + RT5651_PWR_IN1_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INL2", RT5651_PWR_VOL, + RT5651_PWR_IN2_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR2", RT5651_PWR_VOL, + RT5651_PWR_IN2_R_BIT, 0, NULL, 0), + /* HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("HPOL MIX", SND_SOC_NOPM, 0, 0, + rt5651_hpo_mix, ARRAY_SIZE(rt5651_hpo_mix)), + SND_SOC_DAPM_MIXER("HPOR MIX", SND_SOC_NOPM, 0, 0, + rt5651_hpo_mix, ARRAY_SIZE(rt5651_hpo_mix)), + SND_SOC_DAPM_SUPPLY("HP L Amp", RT5651_PWR_ANLG1, + RT5651_PWR_HP_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP R Amp", RT5651_PWR_ANLG1, + RT5651_PWR_HP_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("LOUT MIX", RT5651_PWR_ANLG1, RT5651_PWR_LM_BIT, 0, + rt5651_lout_mix, ARRAY_SIZE(rt5651_lout_mix)), + + SND_SOC_DAPM_SUPPLY("Amp Power", RT5651_PWR_ANLG1, + RT5651_PWR_HA_BIT, 0, rt5651_amp_power_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5651_hp_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SWITCH("HPO L Playback", SND_SOC_NOPM, 0, 0, + &hpo_l_mute_control), + SND_SOC_DAPM_SWITCH("HPO R Playback", SND_SOC_NOPM, 0, 0, + &hpo_r_mute_control), + SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0, + &lout_l_mute_control), + SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0, + &lout_r_mute_control), + SND_SOC_DAPM_POST("HP Post", rt5651_hp_post_event), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("PDML"), + SND_SOC_DAPM_OUTPUT("PDMR"), +}; + +static const struct snd_soc_dapm_route rt5651_dapm_routes[] = { + {"Stero1 DAC Power", NULL, "STO1 DAC ASRC"}, + {"Stero2 DAC Power", NULL, "STO2 DAC ASRC"}, + {"I2S1", NULL, "I2S1 ASRC"}, + {"I2S2", NULL, "I2S2 ASRC"}, + + {"IN1P", NULL, "LDO"}, + {"IN2P", NULL, "LDO"}, + {"IN3P", NULL, "LDO"}, + + {"IN1P", NULL, "MIC1"}, + {"IN2P", NULL, "MIC2"}, + {"IN2N", NULL, "MIC2"}, + {"IN3P", NULL, "MIC3"}, + + {"BST1", NULL, "IN1P"}, + {"BST2", NULL, "IN2P"}, + {"BST2", NULL, "IN2N"}, + {"BST3", NULL, "IN3P"}, + + {"INL1 VOL", NULL, "IN2P"}, + {"INR1 VOL", NULL, "IN2N"}, + + {"RECMIXL", "INL1 Switch", "INL1 VOL"}, + {"RECMIXL", "BST3 Switch", "BST3"}, + {"RECMIXL", "BST2 Switch", "BST2"}, + {"RECMIXL", "BST1 Switch", "BST1"}, + + {"RECMIXR", "INR1 Switch", "INR1 VOL"}, + {"RECMIXR", "BST3 Switch", "BST3"}, + {"RECMIXR", "BST2 Switch", "BST2"}, + {"RECMIXR", "BST1 Switch", "BST1"}, + + {"ADC L", NULL, "RECMIXL"}, + {"ADC L", NULL, "ADC L Power"}, + {"ADC R", NULL, "RECMIXR"}, + {"ADC R", NULL, "ADC R Power"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC CLK"}, + + {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"}, + {"Stereo1 ADC L2 Mux", "DD MIX", "DD MIXL"}, + {"Stereo1 ADC L1 Mux", "ADC", "ADC L"}, + {"Stereo1 ADC L1 Mux", "DD MIX", "DD MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "ADC R"}, + {"Stereo1 ADC R1 Mux", "DD MIX", "DD MIXR"}, + {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"}, + {"Stereo1 ADC R2 Mux", "DD MIX", "DD MIXR"}, + + {"Stereo2 ADC L2 Mux", "DMIC L", "DMIC L1"}, + {"Stereo2 ADC L2 Mux", "DD MIXL", "DD MIXL"}, + {"Stereo2 ADC L1 Mux", "DD MIXL", "DD MIXL"}, + {"Stereo2 ADC L1 Mux", "ADCL", "ADC L"}, + + {"Stereo2 ADC R1 Mux", "DD MIXR", "DD MIXR"}, + {"Stereo2 ADC R1 Mux", "ADCR", "ADC R"}, + {"Stereo2 ADC R2 Mux", "DMIC R", "DMIC R1"}, + {"Stereo2 ADC R2 Mux", "DD MIXR", "DD MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "Stereo1 Filter"}, + {"Stereo1 Filter", NULL, "ADC ASRC"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "Stereo1 Filter"}, + + {"Stereo2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux"}, + {"Stereo2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux"}, + {"Stereo2 ADC MIXL", NULL, "Stereo2 Filter"}, + {"Stereo2 Filter", NULL, "ADC ASRC"}, + + {"Stereo2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux"}, + {"Stereo2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC R2 Mux"}, + {"Stereo2 ADC MIXR", NULL, "Stereo2 Filter"}, + + {"IF1 ADC2", NULL, "Stereo2 ADC MIXL"}, + {"IF1 ADC2", NULL, "Stereo2 ADC MIXR"}, + {"IF1 ADC1", NULL, "Stereo1 ADC MIXL"}, + {"IF1 ADC1", NULL, "Stereo1 ADC MIXR"}, + + {"IF1 ADC1", NULL, "I2S1"}, + + {"IF2 ADC", "IF1 ADC1", "IF1 ADC1"}, + {"IF2 ADC", "IF1 ADC2", "IF1 ADC2"}, + {"IF2 ADC", NULL, "I2S2"}, + + {"AIF1TX", NULL, "IF1 ADC1"}, + {"AIF1TX", NULL, "IF1 ADC2"}, + {"AIF2TX", NULL, "IF2 ADC"}, + + {"IF1 DAC", NULL, "AIF1RX"}, + {"IF1 DAC", NULL, "I2S1"}, + {"IF2 DAC", NULL, "AIF2RX"}, + {"IF2 DAC", NULL, "I2S2"}, + + {"IF1 DAC1 L", NULL, "IF1 DAC"}, + {"IF1 DAC1 R", NULL, "IF1 DAC"}, + {"IF1 DAC2 L", NULL, "IF1 DAC"}, + {"IF1 DAC2 R", NULL, "IF1 DAC"}, + {"IF2 DAC L", NULL, "IF2 DAC"}, + {"IF2 DAC R", NULL, "IF2 DAC"}, + + {"DAC MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC MIXL", "INF1 Switch", "IF1 DAC1 L"}, + {"DAC MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC MIXR", "INF1 Switch", "IF1 DAC1 R"}, + + {"Audio DSP", NULL, "DAC MIXL"}, + {"Audio DSP", NULL, "DAC MIXR"}, + + {"DAC L2 Mux", "IF1", "IF1 DAC2 L"}, + {"DAC L2 Mux", "IF2", "IF2 DAC L"}, + {"DAC L2 Volume", NULL, "DAC L2 Mux"}, + + {"DAC R2 Mux", "IF1", "IF1 DAC2 R"}, + {"DAC R2 Mux", "IF2", "IF2 DAC R"}, + {"DAC R2 Volume", NULL, "DAC R2 Mux"}, + + {"Stereo DAC MIXL", "DAC L1 Switch", "Audio DSP"}, + {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume"}, + {"Stereo DAC MIXL", "DAC R1 Switch", "DAC MIXR"}, + {"Stereo DAC MIXL", NULL, "Stero1 DAC Power"}, + {"Stereo DAC MIXL", NULL, "Stero2 DAC Power"}, + {"Stereo DAC MIXR", "DAC R1 Switch", "Audio DSP"}, + {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume"}, + {"Stereo DAC MIXR", "DAC L1 Switch", "DAC MIXL"}, + {"Stereo DAC MIXR", NULL, "Stero1 DAC Power"}, + {"Stereo DAC MIXR", NULL, "Stero2 DAC Power"}, + + {"PDM L Mux", "Stereo DAC MIX", "Stereo DAC MIXL"}, + {"PDM L Mux", "DD MIX", "DAC MIXL"}, + {"PDM R Mux", "Stereo DAC MIX", "Stereo DAC MIXR"}, + {"PDM R Mux", "DD MIX", "DAC MIXR"}, + + {"DAC L1", NULL, "Stereo DAC MIXL"}, + {"DAC L1", NULL, "DAC L1 Power"}, + {"DAC R1", NULL, "Stereo DAC MIXR"}, + {"DAC R1", NULL, "DAC R1 Power"}, + + {"DD MIXL", "DAC L1 Switch", "DAC MIXL"}, + {"DD MIXL", "DAC L2 Switch", "DAC L2 Volume"}, + {"DD MIXL", "DAC R2 Switch", "DAC R2 Volume"}, + {"DD MIXL", NULL, "Stero2 DAC Power"}, + + {"DD MIXR", "DAC R1 Switch", "DAC MIXR"}, + {"DD MIXR", "DAC R2 Switch", "DAC R2 Volume"}, + {"DD MIXR", "DAC L2 Switch", "DAC L2 Volume"}, + {"DD MIXR", NULL, "Stero2 DAC Power"}, + + {"OUT MIXL", "BST1 Switch", "BST1"}, + {"OUT MIXL", "BST2 Switch", "BST2"}, + {"OUT MIXL", "INL1 Switch", "INL1 VOL"}, + {"OUT MIXL", "REC MIXL Switch", "RECMIXL"}, + {"OUT MIXL", "DAC L1 Switch", "DAC L1"}, + + {"OUT MIXR", "BST2 Switch", "BST2"}, + {"OUT MIXR", "BST1 Switch", "BST1"}, + {"OUT MIXR", "INR1 Switch", "INR1 VOL"}, + {"OUT MIXR", "REC MIXR Switch", "RECMIXR"}, + {"OUT MIXR", "DAC R1 Switch", "DAC R1"}, + + {"HPOVOL L", "Switch", "OUT MIXL"}, + {"HPOVOL R", "Switch", "OUT MIXR"}, + {"OUTVOL L", "Switch", "OUT MIXL"}, + {"OUTVOL R", "Switch", "OUT MIXR"}, + + {"HPOL MIX", "HPO MIX DAC1 Switch", "DAC L1"}, + {"HPOL MIX", "HPO MIX HPVOL Switch", "HPOVOL L"}, + {"HPOL MIX", NULL, "HP L Amp"}, + {"HPOR MIX", "HPO MIX DAC1 Switch", "DAC R1"}, + {"HPOR MIX", "HPO MIX HPVOL Switch", "HPOVOL R"}, + {"HPOR MIX", NULL, "HP R Amp"}, + + {"LOUT MIX", "DAC L1 Switch", "DAC L1"}, + {"LOUT MIX", "DAC R1 Switch", "DAC R1"}, + {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"}, + {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"}, + + {"HP Amp", NULL, "HPOL MIX"}, + {"HP Amp", NULL, "HPOR MIX"}, + {"HP Amp", NULL, "Amp Power"}, + {"HPO L Playback", "Switch", "HP Amp"}, + {"HPO R Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HPO L Playback"}, + {"HPOR", NULL, "HPO R Playback"}, + + {"LOUT L Playback", "Switch", "LOUT MIX"}, + {"LOUT R Playback", "Switch", "LOUT MIX"}, + {"LOUTL", NULL, "LOUT L Playback"}, + {"LOUTL", NULL, "Amp Power"}, + {"LOUTR", NULL, "LOUT R Playback"}, + {"LOUTR", NULL, "Amp Power"}, + + {"PDML", NULL, "PDM L Mux"}, + {"PDMR", NULL, "PDM R Mux"}, +}; + +static int rt5651_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt5651->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5651->sysclk, rt5651->lrck[dai->id]); + + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + bclk_ms = frame_size > 32 ? 1 : 0; + rt5651->bclk[dai->id] = rt5651->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5651->bclk[dai->id], rt5651->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= RT5651_I2S_DL_20; + break; + case 24: + val_len |= RT5651_I2S_DL_24; + break; + case 8: + val_len |= RT5651_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5651_AIF1: + mask_clk = RT5651_I2S_PD1_MASK; + val_clk = pre_div << RT5651_I2S_PD1_SFT; + snd_soc_component_update_bits(component, RT5651_I2S1_SDP, + RT5651_I2S_DL_MASK, val_len); + snd_soc_component_update_bits(component, RT5651_ADDA_CLK1, mask_clk, val_clk); + break; + case RT5651_AIF2: + mask_clk = RT5651_I2S_BCLK_MS2_MASK | RT5651_I2S_PD2_MASK; + val_clk = pre_div << RT5651_I2S_PD2_SFT; + snd_soc_component_update_bits(component, RT5651_I2S2_SDP, + RT5651_I2S_DL_MASK, val_len); + snd_soc_component_update_bits(component, RT5651_ADDA_CLK1, mask_clk, val_clk); + break; + default: + dev_err(component->dev, "Wrong dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5651->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5651_I2S_MS_S; + rt5651->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5651_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5651_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5651_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5651_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5651_AIF1: + snd_soc_component_update_bits(component, RT5651_I2S1_SDP, + RT5651_I2S_MS_MASK | RT5651_I2S_BP_MASK | + RT5651_I2S_DF_MASK, reg_val); + break; + case RT5651_AIF2: + snd_soc_component_update_bits(component, RT5651_I2S2_SDP, + RT5651_I2S_MS_MASK | RT5651_I2S_BP_MASK | + RT5651_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Wrong dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + unsigned int pll_bit = 0; + + if (freq == rt5651->sysclk && clk_id == rt5651->sysclk_src) + return 0; + + switch (clk_id) { + case RT5651_SCLK_S_MCLK: + reg_val |= RT5651_SCLK_SRC_MCLK; + break; + case RT5651_SCLK_S_PLL1: + reg_val |= RT5651_SCLK_SRC_PLL1; + pll_bit |= RT5651_PWR_PLL; + break; + case RT5651_SCLK_S_RCCLK: + reg_val |= RT5651_SCLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, + RT5651_PWR_PLL, pll_bit); + snd_soc_component_update_bits(component, RT5651_GLB_CLK, + RT5651_SCLK_SRC_MASK, reg_val); + rt5651->sysclk = freq; + rt5651->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5651->pll_src && freq_in == rt5651->pll_in && + freq_out == rt5651->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5651->pll_in = 0; + rt5651->pll_out = 0; + snd_soc_component_update_bits(component, RT5651_GLB_CLK, + RT5651_SCLK_SRC_MASK, RT5651_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5651_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5651_GLB_CLK, + RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_MCLK); + break; + case RT5651_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, RT5651_GLB_CLK, + RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK1); + break; + case RT5651_PLL1_S_BCLK2: + snd_soc_component_update_bits(component, RT5651_GLB_CLK, + RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK2); + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5651_PLL_CTRL1, + pll_code.n_code << RT5651_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5651_PLL_CTRL2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5651_PLL_M_SFT) | + (pll_code.m_bp << RT5651_PLL_M_BP_SFT)); + + rt5651->pll_in = freq_in; + rt5651->pll_out = freq_out; + rt5651->pll_src = source; + + return 0; +} + +static int rt5651_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) { + if (snd_soc_component_read(component, RT5651_PLL_MODE_1) & 0x9200) + snd_soc_component_update_bits(component, RT5651_D_MISC, + 0xc00, 0xc00); + } + break; + case SND_SOC_BIAS_STANDBY: + if (SND_SOC_BIAS_OFF == snd_soc_component_get_bias_level(component)) { + snd_soc_component_update_bits(component, RT5651_PWR_ANLG1, + RT5651_PWR_VREF1 | RT5651_PWR_MB | + RT5651_PWR_BG | RT5651_PWR_VREF2, + RT5651_PWR_VREF1 | RT5651_PWR_MB | + RT5651_PWR_BG | RT5651_PWR_VREF2); + usleep_range(10000, 15000); + snd_soc_component_update_bits(component, RT5651_PWR_ANLG1, + RT5651_PWR_FV1 | RT5651_PWR_FV2, + RT5651_PWR_FV1 | RT5651_PWR_FV2); + snd_soc_component_update_bits(component, RT5651_D_MISC, 0x1, 0x1); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, RT5651_D_MISC, 0x0010); + snd_soc_component_write(component, RT5651_PWR_DIG1, 0x0000); + snd_soc_component_write(component, RT5651_PWR_DIG2, 0x0000); + snd_soc_component_write(component, RT5651_PWR_VOL, 0x0000); + snd_soc_component_write(component, RT5651_PWR_MIXER, 0x0000); + /* Do not touch the LDO voltage select bits on bias-off */ + snd_soc_component_update_bits(component, RT5651_PWR_ANLG1, + ~RT5651_PWR_LDO_DVO_MASK, 0); + /* Leave PLL1 and jack-detect power as is, all others off */ + snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, + ~(RT5651_PWR_PLL | RT5651_PWR_JD_M), 0); + break; + + default: + break; + } + + return 0; +} + +static void rt5651_enable_micbias1_for_ovcd(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "micbias1"); + /* OVCD is unreliable when used with RCCLK as sysclk-source */ + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Platform Clock"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +static void rt5651_disable_micbias1_for_ovcd(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_disable_pin_unlocked(dapm, "Platform Clock"); + snd_soc_dapm_disable_pin_unlocked(dapm, "micbias1"); + snd_soc_dapm_disable_pin_unlocked(dapm, "LDO"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +static void rt5651_enable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2, + RT5651_IRQ_MB1_OC_MASK, RT5651_IRQ_MB1_OC_NOR); + rt5651->ovcd_irq_enabled = true; +} + +static void rt5651_disable_micbias1_ovcd_irq(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2, + RT5651_IRQ_MB1_OC_MASK, RT5651_IRQ_MB1_OC_BP); + rt5651->ovcd_irq_enabled = false; +} + +static void rt5651_clear_micbias1_ovcd(struct snd_soc_component *component) +{ + snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2, + RT5651_MB1_OC_CLR, 0); +} + +static bool rt5651_micbias1_ovcd(struct snd_soc_component *component) +{ + int val; + + val = snd_soc_component_read(component, RT5651_IRQ_CTRL2); + dev_dbg(component->dev, "irq ctrl2 %#04x\n", val); + + return (val & RT5651_MB1_OC_CLR); +} + +static bool rt5651_jack_inserted(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + int val; + + if (rt5651->gpiod_hp_det) { + val = gpiod_get_value_cansleep(rt5651->gpiod_hp_det); + dev_dbg(component->dev, "jack-detect gpio %d\n", val); + return val; + } + + val = snd_soc_component_read(component, RT5651_INT_IRQ_ST); + dev_dbg(component->dev, "irq status %#04x\n", val); + + switch (rt5651->jd_src) { + case RT5651_JD1_1: + val &= 0x1000; + break; + case RT5651_JD1_2: + val &= 0x2000; + break; + case RT5651_JD2: + val &= 0x4000; + break; + default: + break; + } + + if (rt5651->jd_active_high) + return val != 0; + else + return val == 0; +} + +/* Jack detect and button-press timings */ +#define JACK_SETTLE_TIME 100 /* milli seconds */ +#define JACK_DETECT_COUNT 5 +#define JACK_DETECT_MAXCOUNT 20 /* Aprox. 2 seconds worth of tries */ +#define JACK_UNPLUG_TIME 80 /* milli seconds */ +#define BP_POLL_TIME 10 /* milli seconds */ +#define BP_POLL_MAXCOUNT 200 /* assume something is wrong after this */ +#define BP_THRESHOLD 3 + +static void rt5651_start_button_press_work(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + rt5651->poll_count = 0; + rt5651->press_count = 0; + rt5651->release_count = 0; + rt5651->pressed = false; + rt5651->press_reported = false; + rt5651_clear_micbias1_ovcd(component); + schedule_delayed_work(&rt5651->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} + +static void rt5651_button_press_work(struct work_struct *work) +{ + struct rt5651_priv *rt5651 = + container_of(work, struct rt5651_priv, bp_work.work); + struct snd_soc_component *component = rt5651->component; + + /* Check the jack was not removed underneath us */ + if (!rt5651_jack_inserted(component)) + return; + + if (rt5651_micbias1_ovcd(component)) { + rt5651->release_count = 0; + rt5651->press_count++; + /* Remember till after JACK_UNPLUG_TIME wait */ + if (rt5651->press_count >= BP_THRESHOLD) + rt5651->pressed = true; + rt5651_clear_micbias1_ovcd(component); + } else { + rt5651->press_count = 0; + rt5651->release_count++; + } + + /* + * The pins get temporarily shorted on jack unplug, so we poll for + * at least JACK_UNPLUG_TIME milli-seconds before reporting a press. + */ + rt5651->poll_count++; + if (rt5651->poll_count < (JACK_UNPLUG_TIME / BP_POLL_TIME)) { + schedule_delayed_work(&rt5651->bp_work, + msecs_to_jiffies(BP_POLL_TIME)); + return; + } + + if (rt5651->pressed && !rt5651->press_reported) { + dev_dbg(component->dev, "headset button press\n"); + snd_soc_jack_report(rt5651->hp_jack, SND_JACK_BTN_0, + SND_JACK_BTN_0); + rt5651->press_reported = true; + } + + if (rt5651->release_count >= BP_THRESHOLD) { + if (rt5651->press_reported) { + dev_dbg(component->dev, "headset button release\n"); + snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0); + } + /* Re-enable OVCD IRQ to detect next press */ + rt5651_enable_micbias1_ovcd_irq(component); + return; /* Stop polling */ + } + + schedule_delayed_work(&rt5651->bp_work, msecs_to_jiffies(BP_POLL_TIME)); +} + +static int rt5651_detect_headset(struct snd_soc_component *component) +{ + int i, headset_count = 0, headphone_count = 0; + + /* + * We get the insertion event before the jack is fully inserted at which + * point the second ring on a TRRS connector may short the 2nd ring and + * sleeve contacts, also the overcurrent detection is not entirely + * reliable. So we try several times with a wait in between until we + * detect the same type JACK_DETECT_COUNT times in a row. + */ + for (i = 0; i < JACK_DETECT_MAXCOUNT; i++) { + /* Clear any previous over-current status flag */ + rt5651_clear_micbias1_ovcd(component); + + msleep(JACK_SETTLE_TIME); + + /* Check the jack is still connected before checking ovcd */ + if (!rt5651_jack_inserted(component)) + return 0; + + if (rt5651_micbias1_ovcd(component)) { + /* + * Over current detected, there is a short between the + * 2nd ring contact and the ground, so a TRS connector + * without a mic contact and thus plain headphones. + */ + dev_dbg(component->dev, "mic-gnd shorted\n"); + headset_count = 0; + headphone_count++; + if (headphone_count == JACK_DETECT_COUNT) + return SND_JACK_HEADPHONE; + } else { + dev_dbg(component->dev, "mic-gnd open\n"); + headphone_count = 0; + headset_count++; + if (headset_count == JACK_DETECT_COUNT) + return SND_JACK_HEADSET; + } + } + + dev_err(component->dev, "Error detecting headset vs headphones, bad contact?, assuming headphones\n"); + return SND_JACK_HEADPHONE; +} + +static bool rt5651_support_button_press(struct rt5651_priv *rt5651) +{ + if (!rt5651->hp_jack) + return false; + + /* Button press support only works with internal jack-detection */ + return (rt5651->hp_jack->status & SND_JACK_MICROPHONE) && + rt5651->gpiod_hp_det == NULL; +} + +static void rt5651_jack_detect_work(struct work_struct *work) +{ + struct rt5651_priv *rt5651 = + container_of(work, struct rt5651_priv, jack_detect_work); + struct snd_soc_component *component = rt5651->component; + int report; + + if (!rt5651_jack_inserted(component)) { + /* Jack removed, or spurious IRQ? */ + if (rt5651->hp_jack->status & SND_JACK_HEADPHONE) { + if (rt5651->hp_jack->status & SND_JACK_MICROPHONE) { + cancel_delayed_work_sync(&rt5651->bp_work); + rt5651_disable_micbias1_ovcd_irq(component); + rt5651_disable_micbias1_for_ovcd(component); + } + snd_soc_jack_report(rt5651->hp_jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + dev_dbg(component->dev, "jack unplugged\n"); + } + } else if (!(rt5651->hp_jack->status & SND_JACK_HEADPHONE)) { + /* Jack inserted */ + WARN_ON(rt5651->ovcd_irq_enabled); + rt5651_enable_micbias1_for_ovcd(component); + report = rt5651_detect_headset(component); + dev_dbg(component->dev, "detect report %#02x\n", report); + snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET); + if (rt5651_support_button_press(rt5651)) { + /* Enable ovcd IRQ for button press detect. */ + rt5651_enable_micbias1_ovcd_irq(component); + } else { + /* No more need for overcurrent detect. */ + rt5651_disable_micbias1_for_ovcd(component); + } + } else if (rt5651->ovcd_irq_enabled && rt5651_micbias1_ovcd(component)) { + dev_dbg(component->dev, "OVCD IRQ\n"); + + /* + * The ovcd IRQ keeps firing while the button is pressed, so + * we disable it and start polling the button until released. + * + * The disable will make the IRQ pin 0 again and since we get + * IRQs on both edges (so as to detect both jack plugin and + * unplug) this means we will immediately get another IRQ. + * The ovcd_irq_enabled check above makes the 2ND IRQ a NOP. + */ + rt5651_disable_micbias1_ovcd_irq(component); + rt5651_start_button_press_work(component); + + /* + * If the jack-detect IRQ flag goes high (unplug) after our + * above rt5651_jack_inserted() check and before we have + * disabled the OVCD IRQ, the IRQ pin will stay high and as + * we react to edges, we miss the unplug event -> recheck. + */ + queue_work(system_long_wq, &rt5651->jack_detect_work); + } +} + +static irqreturn_t rt5651_irq(int irq, void *data) +{ + struct rt5651_priv *rt5651 = data; + + queue_work(system_power_efficient_wq, &rt5651->jack_detect_work); + + return IRQ_HANDLED; +} + +static void rt5651_cancel_work(void *data) +{ + struct rt5651_priv *rt5651 = data; + + cancel_work_sync(&rt5651->jack_detect_work); + cancel_delayed_work_sync(&rt5651->bp_work); +} + +static void rt5651_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hp_jack, + struct gpio_desc *gpiod_hp_det) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + bool using_internal_jack_detect = true; + + /* Select jack detect source */ + switch (rt5651->jd_src) { + case RT5651_JD_NULL: + rt5651->gpiod_hp_det = gpiod_hp_det; + if (!rt5651->gpiod_hp_det) + return; /* No jack detect */ + using_internal_jack_detect = false; + break; + case RT5651_JD1_1: + snd_soc_component_update_bits(component, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_1); + /* active-low is normal, set inv flag for active-high */ + if (rt5651->jd_active_high) + snd_soc_component_update_bits(component, + RT5651_IRQ_CTRL1, + RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV, + RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV); + else + snd_soc_component_update_bits(component, + RT5651_IRQ_CTRL1, + RT5651_JD1_1_IRQ_EN | RT5651_JD1_1_INV, + RT5651_JD1_1_IRQ_EN); + break; + case RT5651_JD1_2: + snd_soc_component_update_bits(component, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD1_2); + /* active-low is normal, set inv flag for active-high */ + if (rt5651->jd_active_high) + snd_soc_component_update_bits(component, + RT5651_IRQ_CTRL1, + RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV, + RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV); + else + snd_soc_component_update_bits(component, + RT5651_IRQ_CTRL1, + RT5651_JD1_2_IRQ_EN | RT5651_JD1_2_INV, + RT5651_JD1_2_IRQ_EN); + break; + case RT5651_JD2: + snd_soc_component_update_bits(component, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, RT5651_JD_TRG_SEL_JD2); + /* active-low is normal, set inv flag for active-high */ + if (rt5651->jd_active_high) + snd_soc_component_update_bits(component, + RT5651_IRQ_CTRL1, + RT5651_JD2_IRQ_EN | RT5651_JD2_INV, + RT5651_JD2_IRQ_EN | RT5651_JD2_INV); + else + snd_soc_component_update_bits(component, + RT5651_IRQ_CTRL1, + RT5651_JD2_IRQ_EN | RT5651_JD2_INV, + RT5651_JD2_IRQ_EN); + break; + default: + dev_err(component->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n"); + return; + } + + if (using_internal_jack_detect) { + /* IRQ output on GPIO1 */ + snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1, + RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ); + + /* Enable jack detect power */ + snd_soc_component_update_bits(component, RT5651_PWR_ANLG2, + RT5651_PWR_JD_M, RT5651_PWR_JD_M); + } + + /* Set OVCD threshold current and scale-factor */ + snd_soc_component_write(component, RT5651_PR_BASE + RT5651_BIAS_CUR4, + 0xa800 | rt5651->ovcd_sf); + + snd_soc_component_update_bits(component, RT5651_MICBIAS, + RT5651_MIC1_OVCD_MASK | + RT5651_MIC1_OVTH_MASK | + RT5651_PWR_CLK12M_MASK | + RT5651_PWR_MB_MASK, + RT5651_MIC1_OVCD_EN | + rt5651->ovcd_th | + RT5651_PWR_MB_PU | + RT5651_PWR_CLK12M_PU); + + /* + * The over-current-detect is only reliable in detecting the absence + * of over-current, when the mic-contact in the jack is short-circuited, + * the hardware periodically retries if it can apply the bias-current + * leading to the ovcd status flip-flopping 1-0-1 with it being 0 about + * 10% of the time, as we poll the ovcd status bit we might hit that + * 10%, so we enable sticky mode and when checking OVCD we clear the + * status, msleep() a bit and then check to get a reliable reading. + */ + snd_soc_component_update_bits(component, RT5651_IRQ_CTRL2, + RT5651_MB1_OC_STKY_MASK, RT5651_MB1_OC_STKY_EN); + + rt5651->hp_jack = hp_jack; + if (rt5651_support_button_press(rt5651)) { + rt5651_enable_micbias1_for_ovcd(component); + rt5651_enable_micbias1_ovcd_irq(component); + } + + enable_irq(rt5651->irq); + /* sync initial jack state */ + queue_work(system_power_efficient_wq, &rt5651->jack_detect_work); +} + +static void rt5651_disable_jack_detect(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + disable_irq(rt5651->irq); + rt5651_cancel_work(rt5651); + + if (rt5651_support_button_press(rt5651)) { + rt5651_disable_micbias1_ovcd_irq(component); + rt5651_disable_micbias1_for_ovcd(component); + snd_soc_jack_report(rt5651->hp_jack, 0, SND_JACK_BTN_0); + } + + rt5651->hp_jack = NULL; +} + +static int rt5651_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + if (jack) + rt5651_enable_jack_detect(component, jack, data); + else + rt5651_disable_jack_detect(component); + + return 0; +} + +/* + * Note on some platforms the platform code may need to add device-properties, + * rather then relying only on properties set by the firmware. Therefor the + * property parsing MUST be done from the component driver's probe function, + * rather then from the i2c driver's probe function, so that the platform-code + * can attach extra properties before calling snd_soc_register_card(). + */ +static void rt5651_apply_properties(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + u32 val; + + if (device_property_read_bool(component->dev, "realtek,in2-differential")) + snd_soc_component_update_bits(component, RT5651_IN1_IN2, + RT5651_IN_DF2, RT5651_IN_DF2); + + if (device_property_read_bool(component->dev, "realtek,dmic-en")) + snd_soc_component_update_bits(component, RT5651_GPIO_CTRL1, + RT5651_GP2_PIN_MASK, RT5651_GP2_PIN_DMIC1_SCL); + + if (device_property_read_u32(component->dev, + "realtek,jack-detect-source", &val) == 0) + rt5651->jd_src = val; + + if (device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted")) + rt5651->jd_active_high = true; + + /* + * Testing on various boards has shown that good defaults for the OVCD + * threshold and scale-factor are 2000µA and 0.75. For an effective + * limit of 1500µA, this seems to be more reliable then 1500µA and 1.0. + */ + rt5651->ovcd_th = RT5651_MIC1_OVTH_2000UA; + rt5651->ovcd_sf = RT5651_MIC_OVCD_SF_0P75; + + if (device_property_read_u32(component->dev, + "realtek,over-current-threshold-microamp", &val) == 0) { + switch (val) { + case 600: + rt5651->ovcd_th = RT5651_MIC1_OVTH_600UA; + break; + case 1500: + rt5651->ovcd_th = RT5651_MIC1_OVTH_1500UA; + break; + case 2000: + rt5651->ovcd_th = RT5651_MIC1_OVTH_2000UA; + break; + default: + dev_warn(component->dev, "Warning: Invalid over-current-threshold-microamp value: %d, defaulting to 2000uA\n", + val); + } + } + + if (device_property_read_u32(component->dev, + "realtek,over-current-scale-factor", &val) == 0) { + if (val <= RT5651_OVCD_SF_1P5) + rt5651->ovcd_sf = val << RT5651_MIC_OVCD_SF_SFT; + else + dev_warn(component->dev, "Warning: Invalid over-current-scale-factor value: %d, defaulting to 0.75\n", + val); + } +} + +static int rt5651_probe(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + rt5651->component = component; + + snd_soc_component_update_bits(component, RT5651_PWR_ANLG1, + RT5651_PWR_LDO_DVO_MASK, RT5651_PWR_LDO_DVO_1_2V); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + rt5651_apply_properties(component); + + return 0; +} + +#ifdef CONFIG_PM +static int rt5651_suspend(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5651->regmap, true); + regcache_mark_dirty(rt5651->regmap); + return 0; +} + +static int rt5651_resume(struct snd_soc_component *component) +{ + struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5651->regmap, false); + snd_soc_component_cache_sync(component); + + return 0; +} +#else +#define rt5651_suspend NULL +#define rt5651_resume NULL +#endif + +#define RT5651_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5651_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5651_aif_dai_ops = { + .hw_params = rt5651_hw_params, + .set_fmt = rt5651_set_dai_fmt, + .set_sysclk = rt5651_set_dai_sysclk, + .set_pll = rt5651_set_dai_pll, +}; + +static struct snd_soc_dai_driver rt5651_dai[] = { + { + .name = "rt5651-aif1", + .id = RT5651_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5651_STEREO_RATES, + .formats = RT5651_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5651_STEREO_RATES, + .formats = RT5651_FORMATS, + }, + .ops = &rt5651_aif_dai_ops, + }, + { + .name = "rt5651-aif2", + .id = RT5651_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5651_STEREO_RATES, + .formats = RT5651_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5651_STEREO_RATES, + .formats = RT5651_FORMATS, + }, + .ops = &rt5651_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5651 = { + .probe = rt5651_probe, + .suspend = rt5651_suspend, + .resume = rt5651_resume, + .set_bias_level = rt5651_set_bias_level, + .set_jack = rt5651_set_jack, + .controls = rt5651_snd_controls, + .num_controls = ARRAY_SIZE(rt5651_snd_controls), + .dapm_widgets = rt5651_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5651_dapm_widgets), + .dapm_routes = rt5651_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5651_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5651_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = RT5651_DEVICE_ID + 1 + (ARRAY_SIZE(rt5651_ranges) * + RT5651_PR_SPACING), + .volatile_reg = rt5651_volatile_register, + .readable_reg = rt5651_readable_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5651_reg, + .num_reg_defaults = ARRAY_SIZE(rt5651_reg), + .ranges = rt5651_ranges, + .num_ranges = ARRAY_SIZE(rt5651_ranges), + .use_single_read = true, + .use_single_write = true, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id rt5651_of_match[] = { + { .compatible = "realtek,rt5651", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5651_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5651_acpi_match[] = { + { "10EC5651", 0 }, + { "10EC5640", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt5651_acpi_match); +#endif + +static const struct i2c_device_id rt5651_i2c_id[] = { + { "rt5651", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id); + +/* + * Note this function MUST not look at device-properties, see the comment + * above rt5651_apply_properties(). + */ +static int rt5651_i2c_probe(struct i2c_client *i2c) +{ + struct rt5651_priv *rt5651; + int ret; + int err; + + rt5651 = devm_kzalloc(&i2c->dev, sizeof(*rt5651), + GFP_KERNEL); + if (NULL == rt5651) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5651); + + rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap); + if (IS_ERR(rt5651->regmap)) { + ret = PTR_ERR(rt5651->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + err = regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &ret); + if (err) + return err; + + if (ret != RT5651_DEVICE_ID_VALUE) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5651\n", ret); + return -ENODEV; + } + + regmap_write(rt5651->regmap, RT5651_RESET, 0); + + ret = regmap_register_patch(rt5651->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + rt5651->irq = i2c->irq; + rt5651->hp_mute = true; + + INIT_DELAYED_WORK(&rt5651->bp_work, rt5651_button_press_work); + INIT_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work); + + /* Make sure work is stopped on probe-error / remove */ + ret = devm_add_action_or_reset(&i2c->dev, rt5651_cancel_work, rt5651); + if (ret) + return ret; + + ret = devm_request_irq(&i2c->dev, rt5651->irq, rt5651_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT | IRQF_NO_AUTOEN, "rt5651", rt5651); + if (ret) { + dev_warn(&i2c->dev, "Failed to reguest IRQ %d: %d\n", + rt5651->irq, ret); + rt5651->irq = -ENXIO; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5651, + rt5651_dai, ARRAY_SIZE(rt5651_dai)); + + return ret; +} + +static struct i2c_driver rt5651_i2c_driver = { + .driver = { + .name = "rt5651", + .acpi_match_table = ACPI_PTR(rt5651_acpi_match), + .of_match_table = of_match_ptr(rt5651_of_match), + }, + .probe = rt5651_i2c_probe, + .id_table = rt5651_i2c_id, +}; +module_i2c_driver(rt5651_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5651 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h new file mode 100644 index 0000000000..20c33a3ece --- /dev/null +++ b/sound/soc/codecs/rt5651.h @@ -0,0 +1,2102 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5651.h -- RT5651 ALSA SoC audio driver + * + * Copyright 2011 Realtek Microelectronics + * Author: Johnny Hsu + */ + +#ifndef __RT5651_H__ +#define __RT5651_H__ + +#include + +/* Info */ +#define RT5651_RESET 0x00 +#define RT5651_VERSION_ID 0xfd +#define RT5651_VENDOR_ID 0xfe +#define RT5651_DEVICE_ID 0xff +/* I/O - Output */ +#define RT5651_HP_VOL 0x02 +#define RT5651_LOUT_CTRL1 0x03 +#define RT5651_LOUT_CTRL2 0x05 +/* I/O - Input */ +#define RT5651_IN1_IN2 0x0d +#define RT5651_IN3 0x0e +#define RT5651_INL1_INR1_VOL 0x0f +#define RT5651_INL2_INR2_VOL 0x10 +/* I/O - ADC/DAC/DMIC */ +#define RT5651_DAC1_DIG_VOL 0x19 +#define RT5651_DAC2_DIG_VOL 0x1a +#define RT5651_DAC2_CTRL 0x1b +#define RT5651_ADC_DIG_VOL 0x1c +#define RT5651_ADC_DATA 0x1d +#define RT5651_ADC_BST_VOL 0x1e +/* Mixer - D-D */ +#define RT5651_STO1_ADC_MIXER 0x27 +#define RT5651_STO2_ADC_MIXER 0x28 +#define RT5651_AD_DA_MIXER 0x29 +#define RT5651_STO_DAC_MIXER 0x2a +#define RT5651_DD_MIXER 0x2b +#define RT5651_DIG_INF_DATA 0x2f +/* PDM */ +#define RT5651_PDM_CTL 0x30 +#define RT5651_PDM_I2C_CTL1 0x31 +#define RT5651_PDM_I2C_CTL2 0x32 +#define RT5651_PDM_I2C_DATA_W 0x33 +#define RT5651_PDM_I2C_DATA_R 0x34 +/* Mixer - ADC */ +#define RT5651_REC_L1_MIXER 0x3b +#define RT5651_REC_L2_MIXER 0x3c +#define RT5651_REC_R1_MIXER 0x3d +#define RT5651_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5651_HPO_MIXER 0x45 +#define RT5651_OUT_L1_MIXER 0x4d +#define RT5651_OUT_L2_MIXER 0x4e +#define RT5651_OUT_L3_MIXER 0x4f +#define RT5651_OUT_R1_MIXER 0x50 +#define RT5651_OUT_R2_MIXER 0x51 +#define RT5651_OUT_R3_MIXER 0x52 +#define RT5651_LOUT_MIXER 0x53 +/* Power */ +#define RT5651_PWR_DIG1 0x61 +#define RT5651_PWR_DIG2 0x62 +#define RT5651_PWR_ANLG1 0x63 +#define RT5651_PWR_ANLG2 0x64 +#define RT5651_PWR_MIXER 0x65 +#define RT5651_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5651_PRIV_INDEX 0x6a +#define RT5651_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5651_I2S1_SDP 0x70 +#define RT5651_I2S2_SDP 0x71 +#define RT5651_ADDA_CLK1 0x73 +#define RT5651_ADDA_CLK2 0x74 +#define RT5651_DMIC 0x75 +/* TDM Control */ +#define RT5651_TDM_CTL_1 0x77 +#define RT5651_TDM_CTL_2 0x78 +#define RT5651_TDM_CTL_3 0x79 +/* Function - Analog */ +#define RT5651_GLB_CLK 0x80 +#define RT5651_PLL_CTRL1 0x81 +#define RT5651_PLL_CTRL2 0x82 +#define RT5651_PLL_MODE_1 0x83 +#define RT5651_PLL_MODE_2 0x84 +#define RT5651_PLL_MODE_3 0x85 +#define RT5651_PLL_MODE_4 0x86 +#define RT5651_PLL_MODE_5 0x87 +#define RT5651_PLL_MODE_6 0x89 +#define RT5651_PLL_MODE_7 0x8a +#define RT5651_DEPOP_M1 0x8e +#define RT5651_DEPOP_M2 0x8f +#define RT5651_DEPOP_M3 0x90 +#define RT5651_CHARGE_PUMP 0x91 +#define RT5651_MICBIAS 0x93 +#define RT5651_A_JD_CTL1 0x94 +/* Function - Digital */ +#define RT5651_EQ_CTRL1 0xb0 +#define RT5651_EQ_CTRL2 0xb1 +#define RT5651_ALC_1 0xb4 +#define RT5651_ALC_2 0xb5 +#define RT5651_ALC_3 0xb6 +#define RT5651_JD_CTRL1 0xbb +#define RT5651_JD_CTRL2 0xbc +#define RT5651_IRQ_CTRL1 0xbd +#define RT5651_IRQ_CTRL2 0xbe +#define RT5651_INT_IRQ_ST 0xbf +#define RT5651_GPIO_CTRL1 0xc0 +#define RT5651_GPIO_CTRL2 0xc1 +#define RT5651_GPIO_CTRL3 0xc2 +#define RT5651_PGM_REG_ARR1 0xc8 +#define RT5651_PGM_REG_ARR2 0xc9 +#define RT5651_PGM_REG_ARR3 0xca +#define RT5651_PGM_REG_ARR4 0xcb +#define RT5651_PGM_REG_ARR5 0xcc +#define RT5651_SCB_FUNC 0xcd +#define RT5651_SCB_CTRL 0xce +#define RT5651_BASE_BACK 0xcf +#define RT5651_MP3_PLUS1 0xd0 +#define RT5651_MP3_PLUS2 0xd1 +#define RT5651_ADJ_HPF_CTRL1 0xd3 +#define RT5651_ADJ_HPF_CTRL2 0xd4 +#define RT5651_HP_CALIB_AMP_DET 0xd6 +#define RT5651_HP_CALIB2 0xd7 +#define RT5651_SV_ZCD1 0xd9 +#define RT5651_SV_ZCD2 0xda +#define RT5651_D_MISC 0xfa +/* Dummy Register */ +#define RT5651_DUMMY2 0xfb +#define RT5651_DUMMY3 0xfc + + +/* Index of Codec Private Register definition */ +#define RT5651_BIAS_CUR1 0x12 +#define RT5651_BIAS_CUR3 0x14 +#define RT5651_BIAS_CUR4 0x15 +#define RT5651_CLSD_INT_REG1 0x1c +#define RT5651_CHPUMP_INT_REG1 0x24 +#define RT5651_MAMP_INT_REG2 0x37 +#define RT5651_CHOP_DAC_ADC 0x3d +#define RT5651_3D_SPK 0x63 +#define RT5651_WND_1 0x6c +#define RT5651_WND_2 0x6d +#define RT5651_WND_3 0x6e +#define RT5651_WND_4 0x6f +#define RT5651_WND_5 0x70 +#define RT5651_WND_8 0x73 +#define RT5651_DIP_SPK_INF 0x75 +#define RT5651_HP_DCC_INT1 0x77 +#define RT5651_EQ_BW_LOP 0xa0 +#define RT5651_EQ_GN_LOP 0xa1 +#define RT5651_EQ_FC_BP1 0xa2 +#define RT5651_EQ_BW_BP1 0xa3 +#define RT5651_EQ_GN_BP1 0xa4 +#define RT5651_EQ_FC_BP2 0xa5 +#define RT5651_EQ_BW_BP2 0xa6 +#define RT5651_EQ_GN_BP2 0xa7 +#define RT5651_EQ_FC_BP3 0xa8 +#define RT5651_EQ_BW_BP3 0xa9 +#define RT5651_EQ_GN_BP3 0xaa +#define RT5651_EQ_FC_BP4 0xab +#define RT5651_EQ_BW_BP4 0xac +#define RT5651_EQ_GN_BP4 0xad +#define RT5651_EQ_FC_HIP1 0xae +#define RT5651_EQ_GN_HIP1 0xaf +#define RT5651_EQ_FC_HIP2 0xb0 +#define RT5651_EQ_BW_HIP2 0xb1 +#define RT5651_EQ_GN_HIP2 0xb2 +#define RT5651_EQ_PRE_VOL 0xb3 +#define RT5651_EQ_PST_VOL 0xb4 + + +/* global definition */ +#define RT5651_L_MUTE (0x1 << 15) +#define RT5651_L_MUTE_SFT 15 +#define RT5651_VOL_L_MUTE (0x1 << 14) +#define RT5651_VOL_L_SFT 14 +#define RT5651_R_MUTE (0x1 << 7) +#define RT5651_R_MUTE_SFT 7 +#define RT5651_VOL_R_MUTE (0x1 << 6) +#define RT5651_VOL_R_SFT 6 +#define RT5651_L_VOL_MASK (0x3f << 8) +#define RT5651_L_VOL_SFT 8 +#define RT5651_R_VOL_MASK (0x3f) +#define RT5651_R_VOL_SFT 0 + +/* LOUT Control 2(0x05) */ +#define RT5651_EN_DFO (0x1 << 15) + +/* IN1 and IN2 Control (0x0d) */ +/* IN3 and IN4 Control (0x0e) */ +#define RT5651_BST_MASK1 (0xf<<12) +#define RT5651_BST_SFT1 12 +#define RT5651_BST_MASK2 (0xf<<8) +#define RT5651_BST_SFT2 8 +#define RT5651_IN_DF1 (0x1 << 7) +#define RT5651_IN_SFT1 7 +#define RT5651_IN_DF2 (0x1 << 6) +#define RT5651_IN_SFT2 6 + +/* INL1 and INR1 Volume Control (0x0f) */ +/* INL2 and INR2 Volume Control (0x10) */ +#define RT5651_INL_SEL_MASK (0x1 << 15) +#define RT5651_INL_SEL_SFT 15 +#define RT5651_INL_SEL_IN4P (0x0 << 15) +#define RT5651_INL_SEL_MONOP (0x1 << 15) +#define RT5651_INL_VOL_MASK (0x1f << 8) +#define RT5651_INL_VOL_SFT 8 +#define RT5651_INR_SEL_MASK (0x1 << 7) +#define RT5651_INR_SEL_SFT 7 +#define RT5651_INR_SEL_IN4N (0x0 << 7) +#define RT5651_INR_SEL_MONON (0x1 << 7) +#define RT5651_INR_VOL_MASK (0x1f) +#define RT5651_INR_VOL_SFT 0 + +/* DAC1 Digital Volume (0x19) */ +#define RT5651_DAC_L1_VOL_MASK (0xff << 8) +#define RT5651_DAC_L1_VOL_SFT 8 +#define RT5651_DAC_R1_VOL_MASK (0xff) +#define RT5651_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x1a) */ +#define RT5651_DAC_L2_VOL_MASK (0xff << 8) +#define RT5651_DAC_L2_VOL_SFT 8 +#define RT5651_DAC_R2_VOL_MASK (0xff) +#define RT5651_DAC_R2_VOL_SFT 0 + +/* DAC2 Control (0x1b) */ +#define RT5651_M_DAC_L2_VOL (0x1 << 13) +#define RT5651_M_DAC_L2_VOL_SFT 13 +#define RT5651_M_DAC_R2_VOL (0x1 << 12) +#define RT5651_M_DAC_R2_VOL_SFT 12 +#define RT5651_SEL_DAC_L2 (0x1 << 11) +#define RT5651_IF2_DAC_L2 (0x1 << 11) +#define RT5651_IF1_DAC_L2 (0x0 << 11) +#define RT5651_SEL_DAC_L2_SFT 11 +#define RT5651_SEL_DAC_R2 (0x1 << 10) +#define RT5651_IF2_DAC_R2 (0x1 << 11) +#define RT5651_IF1_DAC_R2 (0x0 << 11) +#define RT5651_SEL_DAC_R2_SFT 10 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5651_ADC_L_VOL_MASK (0x7f << 8) +#define RT5651_ADC_L_VOL_SFT 8 +#define RT5651_ADC_R_VOL_MASK (0x7f) +#define RT5651_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x1d) */ +#define RT5651_M_MONO_ADC_L (0x1 << 15) +#define RT5651_M_MONO_ADC_L_SFT 15 +#define RT5651_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5651_MONO_ADC_L_VOL_SFT 8 +#define RT5651_M_MONO_ADC_R (0x1 << 7) +#define RT5651_M_MONO_ADC_R_SFT 7 +#define RT5651_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5651_MONO_ADC_R_VOL_SFT 0 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5651_ADC_L_BST_MASK (0x3 << 14) +#define RT5651_ADC_L_BST_SFT 14 +#define RT5651_ADC_R_BST_MASK (0x3 << 12) +#define RT5651_ADC_R_BST_SFT 12 +#define RT5651_ADC_COMP_MASK (0x3 << 10) +#define RT5651_ADC_COMP_SFT 10 + +/* Stereo ADC1 Mixer Control (0x27) */ +#define RT5651_M_STO1_ADC_L1 (0x1 << 14) +#define RT5651_M_STO1_ADC_L1_SFT 14 +#define RT5651_M_STO1_ADC_L2 (0x1 << 13) +#define RT5651_M_STO1_ADC_L2_SFT 13 +#define RT5651_STO1_ADC_1_SRC_MASK (0x1 << 12) +#define RT5651_STO1_ADC_1_SRC_SFT 12 +#define RT5651_STO1_ADC_1_SRC_ADC (0x1 << 12) +#define RT5651_STO1_ADC_1_SRC_DACMIX (0x0 << 12) +#define RT5651_STO1_ADC_2_SRC_MASK (0x1 << 11) +#define RT5651_STO1_ADC_2_SRC_SFT 11 +#define RT5651_STO1_ADC_2_SRC_DMIC (0x0 << 11) +#define RT5651_STO1_ADC_2_SRC_DACMIXR (0x1 << 11) +#define RT5651_M_STO1_ADC_R1 (0x1 << 6) +#define RT5651_M_STO1_ADC_R1_SFT 6 +#define RT5651_M_STO1_ADC_R2 (0x1 << 5) +#define RT5651_M_STO1_ADC_R2_SFT 5 + +/* Stereo ADC2 Mixer Control (0x28) */ +#define RT5651_M_STO2_ADC_L1 (0x1 << 14) +#define RT5651_M_STO2_ADC_L1_SFT 14 +#define RT5651_M_STO2_ADC_L2 (0x1 << 13) +#define RT5651_M_STO2_ADC_L2_SFT 13 +#define RT5651_STO2_ADC_L1_SRC_MASK (0x1 << 12) +#define RT5651_STO2_ADC_L1_SRC_SFT 12 +#define RT5651_STO2_ADC_L1_SRC_DACMIXL (0x0 << 12) +#define RT5651_STO2_ADC_L1_SRC_ADCL (0x1 << 12) +#define RT5651_STO2_ADC_L2_SRC_MASK (0x1 << 11) +#define RT5651_STO2_ADC_L2_SRC_SFT 11 +#define RT5651_STO2_ADC_L2_SRC_DMIC (0x0 << 11) +#define RT5651_STO2_ADC_L2_SRC_DACMIXR (0x1 << 11) +#define RT5651_M_STO2_ADC_R1 (0x1 << 6) +#define RT5651_M_STO2_ADC_R1_SFT 6 +#define RT5651_M_STO2_ADC_R2 (0x1 << 5) +#define RT5651_M_STO2_ADC_R2_SFT 5 +#define RT5651_STO2_ADC_R1_SRC_MASK (0x1 << 4) +#define RT5651_STO2_ADC_R1_SRC_SFT 4 +#define RT5651_STO2_ADC_R1_SRC_ADCR (0x1 << 4) +#define RT5651_STO2_ADC_R1_SRC_DACMIXR (0x0 << 4) +#define RT5651_STO2_ADC_R2_SRC_MASK (0x1 << 3) +#define RT5651_STO2_ADC_R2_SRC_SFT 3 +#define RT5651_STO2_ADC_R2_SRC_DMIC (0x0 << 3) +#define RT5651_STO2_ADC_R2_SRC_DACMIXR (0x1 << 3) + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5651_M_ADCMIX_L (0x1 << 15) +#define RT5651_M_ADCMIX_L_SFT 15 +#define RT5651_M_IF1_DAC_L (0x1 << 14) +#define RT5651_M_IF1_DAC_L_SFT 14 +#define RT5651_M_ADCMIX_R (0x1 << 7) +#define RT5651_M_ADCMIX_R_SFT 7 +#define RT5651_M_IF1_DAC_R (0x1 << 6) +#define RT5651_M_IF1_DAC_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5651_M_DAC_L1_MIXL (0x1 << 14) +#define RT5651_M_DAC_L1_MIXL_SFT 14 +#define RT5651_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5651_DAC_L1_STO_L_VOL_SFT 13 +#define RT5651_M_DAC_L2_MIXL (0x1 << 12) +#define RT5651_M_DAC_L2_MIXL_SFT 12 +#define RT5651_DAC_L2_STO_L_VOL_MASK (0x1 << 11) +#define RT5651_DAC_L2_STO_L_VOL_SFT 11 +#define RT5651_M_DAC_R1_MIXL (0x1 << 9) +#define RT5651_M_DAC_R1_MIXL_SFT 9 +#define RT5651_DAC_R1_STO_L_VOL_MASK (0x1 << 8) +#define RT5651_DAC_R1_STO_L_VOL_SFT 8 +#define RT5651_M_DAC_R1_MIXR (0x1 << 6) +#define RT5651_M_DAC_R1_MIXR_SFT 6 +#define RT5651_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5651_DAC_R1_STO_R_VOL_SFT 5 +#define RT5651_M_DAC_R2_MIXR (0x1 << 4) +#define RT5651_M_DAC_R2_MIXR_SFT 4 +#define RT5651_DAC_R2_STO_R_VOL_MASK (0x1 << 3) +#define RT5651_DAC_R2_STO_R_VOL_SFT 3 +#define RT5651_M_DAC_L1_MIXR (0x1 << 1) +#define RT5651_M_DAC_L1_MIXR_SFT 1 +#define RT5651_DAC_L1_STO_R_VOL_MASK (0x1) +#define RT5651_DAC_L1_STO_R_VOL_SFT 0 + +/* DD Mixer Control (0x2b) */ +#define RT5651_M_STO_DD_L1 (0x1 << 14) +#define RT5651_M_STO_DD_L1_SFT 14 +#define RT5651_STO_DD_L1_VOL_MASK (0x1 << 13) +#define RT5651_DAC_DD_L1_VOL_SFT 13 +#define RT5651_M_STO_DD_L2 (0x1 << 12) +#define RT5651_M_STO_DD_L2_SFT 12 +#define RT5651_STO_DD_L2_VOL_MASK (0x1 << 11) +#define RT5651_STO_DD_L2_VOL_SFT 11 +#define RT5651_M_STO_DD_R2_L (0x1 << 10) +#define RT5651_M_STO_DD_R2_L_SFT 10 +#define RT5651_STO_DD_R2_L_VOL_MASK (0x1 << 9) +#define RT5651_STO_DD_R2_L_VOL_SFT 9 +#define RT5651_M_STO_DD_R1 (0x1 << 6) +#define RT5651_M_STO_DD_R1_SFT 6 +#define RT5651_STO_DD_R1_VOL_MASK (0x1 << 5) +#define RT5651_STO_DD_R1_VOL_SFT 5 +#define RT5651_M_STO_DD_R2 (0x1 << 4) +#define RT5651_M_STO_DD_R2_SFT 4 +#define RT5651_STO_DD_R2_VOL_MASK (0x1 << 3) +#define RT5651_STO_DD_R2_VOL_SFT 3 +#define RT5651_M_STO_DD_L2_R (0x1 << 2) +#define RT5651_M_STO_DD_L2_R_SFT 2 +#define RT5651_STO_DD_L2_R_VOL_MASK (0x1 << 1) +#define RT5651_STO_DD_L2_R_VOL_SFT 1 + +/* Digital Mixer Control (0x2c) */ +#define RT5651_M_STO_L_DAC_L (0x1 << 15) +#define RT5651_M_STO_L_DAC_L_SFT 15 +#define RT5651_STO_L_DAC_L_VOL_MASK (0x1 << 14) +#define RT5651_STO_L_DAC_L_VOL_SFT 14 +#define RT5651_M_DAC_L2_DAC_L (0x1 << 13) +#define RT5651_M_DAC_L2_DAC_L_SFT 13 +#define RT5651_DAC_L2_DAC_L_VOL_MASK (0x1 << 12) +#define RT5651_DAC_L2_DAC_L_VOL_SFT 12 +#define RT5651_M_STO_R_DAC_R (0x1 << 11) +#define RT5651_M_STO_R_DAC_R_SFT 11 +#define RT5651_STO_R_DAC_R_VOL_MASK (0x1 << 10) +#define RT5651_STO_R_DAC_R_VOL_SFT 10 +#define RT5651_M_DAC_R2_DAC_R (0x1 << 9) +#define RT5651_M_DAC_R2_DAC_R_SFT 9 +#define RT5651_DAC_R2_DAC_R_VOL_MASK (0x1 << 8) +#define RT5651_DAC_R2_DAC_R_VOL_SFT 8 + +/* DSP Path Control 1 (0x2d) */ +#define RT5651_RXDP_SRC_MASK (0x1 << 15) +#define RT5651_RXDP_SRC_SFT 15 +#define RT5651_RXDP_SRC_NOR (0x0 << 15) +#define RT5651_RXDP_SRC_DIV3 (0x1 << 15) +#define RT5651_TXDP_SRC_MASK (0x1 << 14) +#define RT5651_TXDP_SRC_SFT 14 +#define RT5651_TXDP_SRC_NOR (0x0 << 14) +#define RT5651_TXDP_SRC_DIV3 (0x1 << 14) + +/* DSP Path Control 2 (0x2e) */ +#define RT5651_DAC_L2_SEL_MASK (0x3 << 14) +#define RT5651_DAC_L2_SEL_SFT 14 +#define RT5651_DAC_L2_SEL_IF2 (0x0 << 14) +#define RT5651_DAC_L2_SEL_IF3 (0x1 << 14) +#define RT5651_DAC_L2_SEL_TXDC (0x2 << 14) +#define RT5651_DAC_L2_SEL_BASS (0x3 << 14) +#define RT5651_DAC_R2_SEL_MASK (0x3 << 12) +#define RT5651_DAC_R2_SEL_SFT 12 +#define RT5651_DAC_R2_SEL_IF2 (0x0 << 12) +#define RT5651_DAC_R2_SEL_IF3 (0x1 << 12) +#define RT5651_DAC_R2_SEL_TXDC (0x2 << 12) +#define RT5651_IF2_ADC_L_SEL_MASK (0x1 << 11) +#define RT5651_IF2_ADC_L_SEL_SFT 11 +#define RT5651_IF2_ADC_L_SEL_TXDP (0x0 << 11) +#define RT5651_IF2_ADC_L_SEL_PASS (0x1 << 11) +#define RT5651_IF2_ADC_R_SEL_MASK (0x1 << 10) +#define RT5651_IF2_ADC_R_SEL_SFT 10 +#define RT5651_IF2_ADC_R_SEL_TXDP (0x0 << 10) +#define RT5651_IF2_ADC_R_SEL_PASS (0x1 << 10) +#define RT5651_RXDC_SEL_MASK (0x3 << 8) +#define RT5651_RXDC_SEL_SFT 8 +#define RT5651_RXDC_SEL_NOR (0x0 << 8) +#define RT5651_RXDC_SEL_L2R (0x1 << 8) +#define RT5651_RXDC_SEL_R2L (0x2 << 8) +#define RT5651_RXDC_SEL_SWAP (0x3 << 8) +#define RT5651_RXDP_SEL_MASK (0x3 << 6) +#define RT5651_RXDP_SEL_SFT 6 +#define RT5651_RXDP_SEL_NOR (0x0 << 6) +#define RT5651_RXDP_SEL_L2R (0x1 << 6) +#define RT5651_RXDP_SEL_R2L (0x2 << 6) +#define RT5651_RXDP_SEL_SWAP (0x3 << 6) +#define RT5651_TXDC_SEL_MASK (0x3 << 4) +#define RT5651_TXDC_SEL_SFT 4 +#define RT5651_TXDC_SEL_NOR (0x0 << 4) +#define RT5651_TXDC_SEL_L2R (0x1 << 4) +#define RT5651_TXDC_SEL_R2L (0x2 << 4) +#define RT5651_TXDC_SEL_SWAP (0x3 << 4) +#define RT5651_TXDP_SEL_MASK (0x3 << 2) +#define RT5651_TXDP_SEL_SFT 2 +#define RT5651_TXDP_SEL_NOR (0x0 << 2) +#define RT5651_TXDP_SEL_L2R (0x1 << 2) +#define RT5651_TXDP_SEL_R2L (0x2 << 2) +#define RT5651_TRXDP_SEL_SWAP (0x3 << 2) + +/* Digital Interface Data Control (0x2f) */ +#define RT5651_IF2_DAC_SEL_MASK (0x3 << 10) +#define RT5651_IF2_DAC_SEL_SFT 10 +#define RT5651_IF2_DAC_SEL_NOR (0x0 << 10) +#define RT5651_IF2_DAC_SEL_SWAP (0x1 << 10) +#define RT5651_IF2_DAC_SEL_L2R (0x2 << 10) +#define RT5651_IF2_DAC_SEL_R2L (0x3 << 10) +#define RT5651_IF2_ADC_SEL_MASK (0x3 << 8) +#define RT5651_IF2_ADC_SEL_SFT 8 +#define RT5651_IF2_ADC_SEL_NOR (0x0 << 8) +#define RT5651_IF2_ADC_SEL_SWAP (0x1 << 8) +#define RT5651_IF2_ADC_SEL_L2R (0x2 << 8) +#define RT5651_IF2_ADC_SEL_R2L (0x3 << 8) +#define RT5651_IF2_ADC_SRC_MASK (0x1 << 7) +#define RT5651_IF2_ADC_SRC_SFT 7 +#define RT5651_IF1_ADC1 (0x0 << 7) +#define RT5651_IF1_ADC2 (0x1 << 7) + +/* PDM Output Control (0x30) */ +#define RT5651_PDM_L_SEL_MASK (0x1 << 15) +#define RT5651_PDM_L_SEL_SFT 15 +#define RT5651_PDM_L_SEL_DD_L (0x0 << 15) +#define RT5651_PDM_L_SEL_STO_L (0x1 << 15) +#define RT5651_M_PDM_L (0x1 << 14) +#define RT5651_M_PDM_L_SFT 14 +#define RT5651_PDM_R_SEL_MASK (0x1 << 13) +#define RT5651_PDM_R_SEL_SFT 13 +#define RT5651_PDM_R_SEL_DD_L (0x0 << 13) +#define RT5651_PDM_R_SEL_STO_L (0x1 << 13) +#define RT5651_M_PDM_R (0x1 << 12) +#define RT5651_M_PDM_R_SFT 12 +#define RT5651_PDM_BUSY (0x1 << 6) +#define RT5651_PDM_BUSY_SFT 6 +#define RT5651_PDM_PATTERN_SEL_MASK (0x1 << 5) +#define RT5651_PDM_PATTERN_SEL_64 (0x0 << 5) +#define RT5651_PDM_PATTERN_SEL_128 (0x1 << 5) +#define RT5651_PDM_VOL_MASK (0x1 << 4) +#define RT5651_PDM_VOL_SFT 4 +#define RT5651_PDM_DIV_MASK (0x3) +#define RT5651_PDM_DIV_SFT 0 +#define RT5651_PDM_DIV_1 0 +#define RT5651_PDM_DIV_2 1 +#define RT5651_PDM_DIV_3 2 +#define RT5651_PDM_DIV_4 3 + +/* PDM I2C/Data Control 1 (0x31) */ +#define RT5651_PDM_I2C_ID_MASK (0xf << 12) +#define PT5631_PDM_CMD_EXE (0x1 << 11) +#define RT5651_PDM_I2C_CMD_MASK (0x1 << 10) +#define RT5651_PDM_I2C_CMD_R (0x0 << 10) +#define RT5651_PDM_I2C_CMD_W (0x1 << 10) +#define RT5651_PDM_I2C_CMD_EXE (0x1 << 9) +#define RT5651_PDM_I2C_NORMAL (0x0 << 8) +#define RT5651_PDM_I2C_BUSY (0x1 << 8) + +/* PDM I2C/Data Control 2 (0x32) */ +#define RT5651_PDM_I2C_ADDR (0xff << 8) +#define RT5651_PDM_I2C_CMD_PATTERN (0xff) + + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5651_G_LN_L2_RM_L_MASK (0x7 << 13) +#define RT5651_G_IN_L2_RM_L_SFT 13 +#define RT5651_G_LN_L1_RM_L_MASK (0x7 << 10) +#define RT5651_G_IN_L1_RM_L_SFT 10 +#define RT5651_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5651_G_BST3_RM_L_SFT 4 +#define RT5651_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5651_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5651_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5651_G_BST1_RM_L_SFT 13 +#define RT5651_G_OM_L_RM_L_MASK (0x7 << 10) +#define RT5651_G_OM_L_RM_L_SFT 10 +#define RT5651_M_IN2_L_RM_L (0x1 << 6) +#define RT5651_M_IN2_L_RM_L_SFT 6 +#define RT5651_M_IN1_L_RM_L (0x1 << 5) +#define RT5651_M_IN1_L_RM_L_SFT 5 +#define RT5651_M_BST3_RM_L (0x1 << 3) +#define RT5651_M_BST3_RM_L_SFT 3 +#define RT5651_M_BST2_RM_L (0x1 << 2) +#define RT5651_M_BST2_RM_L_SFT 2 +#define RT5651_M_BST1_RM_L (0x1 << 1) +#define RT5651_M_BST1_RM_L_SFT 1 +#define RT5651_M_OM_L_RM_L (0x1) +#define RT5651_M_OM_L_RM_L_SFT 0 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5651_G_IN2_R_RM_R_MASK (0x7 << 13) +#define RT5651_G_IN2_R_RM_R_SFT 13 +#define RT5651_G_IN1_R_RM_R_MASK (0x7 << 10) +#define RT5651_G_IN1_R_RM_R_SFT 10 +#define RT5651_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5651_G_BST3_RM_R_SFT 4 +#define RT5651_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5651_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5651_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5651_G_BST1_RM_R_SFT 13 +#define RT5651_G_OM_R_RM_R_MASK (0x7 << 10) +#define RT5651_G_OM_R_RM_R_SFT 10 +#define RT5651_M_IN2_R_RM_R (0x1 << 6) +#define RT5651_M_IN2_R_RM_R_SFT 6 +#define RT5651_M_IN1_R_RM_R (0x1 << 5) +#define RT5651_M_IN1_R_RM_R_SFT 5 +#define RT5651_M_BST3_RM_R (0x1 << 3) +#define RT5651_M_BST3_RM_R_SFT 3 +#define RT5651_M_BST2_RM_R (0x1 << 2) +#define RT5651_M_BST2_RM_R_SFT 2 +#define RT5651_M_BST1_RM_R (0x1 << 1) +#define RT5651_M_BST1_RM_R_SFT 1 +#define RT5651_M_OM_R_RM_R (0x1) +#define RT5651_M_OM_R_RM_R_SFT 0 + +/* HPMIX Control (0x45) */ +#define RT5651_M_DAC1_HM (0x1 << 14) +#define RT5651_M_DAC1_HM_SFT 14 +#define RT5651_M_HPVOL_HM (0x1 << 13) +#define RT5651_M_HPVOL_HM_SFT 13 +#define RT5651_G_HPOMIX_MASK (0x1 << 12) +#define RT5651_G_HPOMIX_SFT 12 + +/* SPK Left Mixer Control (0x46) */ +#define RT5651_G_RM_L_SM_L_MASK (0x3 << 14) +#define RT5651_G_RM_L_SM_L_SFT 14 +#define RT5651_G_IN_L_SM_L_MASK (0x3 << 12) +#define RT5651_G_IN_L_SM_L_SFT 12 +#define RT5651_G_DAC_L1_SM_L_MASK (0x3 << 10) +#define RT5651_G_DAC_L1_SM_L_SFT 10 +#define RT5651_G_DAC_L2_SM_L_MASK (0x3 << 8) +#define RT5651_G_DAC_L2_SM_L_SFT 8 +#define RT5651_G_OM_L_SM_L_MASK (0x3 << 6) +#define RT5651_G_OM_L_SM_L_SFT 6 +#define RT5651_M_RM_L_SM_L (0x1 << 5) +#define RT5651_M_RM_L_SM_L_SFT 5 +#define RT5651_M_IN_L_SM_L (0x1 << 4) +#define RT5651_M_IN_L_SM_L_SFT 4 +#define RT5651_M_DAC_L1_SM_L (0x1 << 3) +#define RT5651_M_DAC_L1_SM_L_SFT 3 +#define RT5651_M_DAC_L2_SM_L (0x1 << 2) +#define RT5651_M_DAC_L2_SM_L_SFT 2 +#define RT5651_M_OM_L_SM_L (0x1 << 1) +#define RT5651_M_OM_L_SM_L_SFT 1 + +/* SPK Right Mixer Control (0x47) */ +#define RT5651_G_RM_R_SM_R_MASK (0x3 << 14) +#define RT5651_G_RM_R_SM_R_SFT 14 +#define RT5651_G_IN_R_SM_R_MASK (0x3 << 12) +#define RT5651_G_IN_R_SM_R_SFT 12 +#define RT5651_G_DAC_R1_SM_R_MASK (0x3 << 10) +#define RT5651_G_DAC_R1_SM_R_SFT 10 +#define RT5651_G_DAC_R2_SM_R_MASK (0x3 << 8) +#define RT5651_G_DAC_R2_SM_R_SFT 8 +#define RT5651_G_OM_R_SM_R_MASK (0x3 << 6) +#define RT5651_G_OM_R_SM_R_SFT 6 +#define RT5651_M_RM_R_SM_R (0x1 << 5) +#define RT5651_M_RM_R_SM_R_SFT 5 +#define RT5651_M_IN_R_SM_R (0x1 << 4) +#define RT5651_M_IN_R_SM_R_SFT 4 +#define RT5651_M_DAC_R1_SM_R (0x1 << 3) +#define RT5651_M_DAC_R1_SM_R_SFT 3 +#define RT5651_M_DAC_R2_SM_R (0x1 << 2) +#define RT5651_M_DAC_R2_SM_R_SFT 2 +#define RT5651_M_OM_R_SM_R (0x1 << 1) +#define RT5651_M_OM_R_SM_R_SFT 1 + +/* SPOLMIX Control (0x48) */ +#define RT5651_M_DAC_R1_SPM_L (0x1 << 15) +#define RT5651_M_DAC_R1_SPM_L_SFT 15 +#define RT5651_M_DAC_L1_SPM_L (0x1 << 14) +#define RT5651_M_DAC_L1_SPM_L_SFT 14 +#define RT5651_M_SV_R_SPM_L (0x1 << 13) +#define RT5651_M_SV_R_SPM_L_SFT 13 +#define RT5651_M_SV_L_SPM_L (0x1 << 12) +#define RT5651_M_SV_L_SPM_L_SFT 12 +#define RT5651_M_BST1_SPM_L (0x1 << 11) +#define RT5651_M_BST1_SPM_L_SFT 11 + +/* SPORMIX Control (0x49) */ +#define RT5651_M_DAC_R1_SPM_R (0x1 << 13) +#define RT5651_M_DAC_R1_SPM_R_SFT 13 +#define RT5651_M_SV_R_SPM_R (0x1 << 12) +#define RT5651_M_SV_R_SPM_R_SFT 12 +#define RT5651_M_BST1_SPM_R (0x1 << 11) +#define RT5651_M_BST1_SPM_R_SFT 11 + +/* SPOLMIX / SPORMIX Ratio Control (0x4a) */ +#define RT5651_SPO_CLSD_RATIO_MASK (0x7) +#define RT5651_SPO_CLSD_RATIO_SFT 0 + +/* Mono Output Mixer Control (0x4c) */ +#define RT5651_M_DAC_R2_MM (0x1 << 15) +#define RT5651_M_DAC_R2_MM_SFT 15 +#define RT5651_M_DAC_L2_MM (0x1 << 14) +#define RT5651_M_DAC_L2_MM_SFT 14 +#define RT5651_M_OV_R_MM (0x1 << 13) +#define RT5651_M_OV_R_MM_SFT 13 +#define RT5651_M_OV_L_MM (0x1 << 12) +#define RT5651_M_OV_L_MM_SFT 12 +#define RT5651_M_BST1_MM (0x1 << 11) +#define RT5651_M_BST1_MM_SFT 11 +#define RT5651_G_MONOMIX_MASK (0x1 << 10) +#define RT5651_G_MONOMIX_SFT 10 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5651_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5651_G_BST2_OM_L_SFT 10 +#define RT5651_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5651_G_BST1_OM_L_SFT 7 +#define RT5651_G_IN1_L_OM_L_MASK (0x7 << 4) +#define RT5651_G_IN1_L_OM_L_SFT 4 +#define RT5651_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5651_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5651_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5651_G_DAC_L1_OM_L_SFT 7 +#define RT5651_G_IN2_L_OM_L_MASK (0x7 << 4) +#define RT5651_G_IN2_L_OM_L_SFT 4 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5651_M_IN2_L_OM_L (0x1 << 9) +#define RT5651_M_IN2_L_OM_L_SFT 9 +#define RT5651_M_BST2_OM_L (0x1 << 6) +#define RT5651_M_BST2_OM_L_SFT 6 +#define RT5651_M_BST1_OM_L (0x1 << 5) +#define RT5651_M_BST1_OM_L_SFT 5 +#define RT5651_M_IN1_L_OM_L (0x1 << 4) +#define RT5651_M_IN1_L_OM_L_SFT 4 +#define RT5651_M_RM_L_OM_L (0x1 << 3) +#define RT5651_M_RM_L_OM_L_SFT 3 +#define RT5651_M_DAC_L1_OM_L (0x1) +#define RT5651_M_DAC_L1_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5651_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5651_G_BST2_OM_R_SFT 10 +#define RT5651_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5651_G_BST1_OM_R_SFT 7 +#define RT5651_G_IN1_R_OM_R_MASK (0x7 << 4) +#define RT5651_G_IN1_R_OM_R_SFT 4 +#define RT5651_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5651_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5651_G_DAC_R1_OM_R_MASK (0x7 << 7) +#define RT5651_G_DAC_R1_OM_R_SFT 7 +#define RT5651_G_IN2_R_OM_R_MASK (0x7 << 4) +#define RT5651_G_IN2_R_OM_R_SFT 4 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5651_M_IN2_R_OM_R (0x1 << 9) +#define RT5651_M_IN2_R_OM_R_SFT 9 +#define RT5651_M_BST2_OM_R (0x1 << 6) +#define RT5651_M_BST2_OM_R_SFT 6 +#define RT5651_M_BST1_OM_R (0x1 << 5) +#define RT5651_M_BST1_OM_R_SFT 5 +#define RT5651_M_IN1_R_OM_R (0x1 << 4) +#define RT5651_M_IN1_R_OM_R_SFT 4 +#define RT5651_M_RM_R_OM_R (0x1 << 3) +#define RT5651_M_RM_R_OM_R_SFT 3 +#define RT5651_M_DAC_R1_OM_R (0x1) +#define RT5651_M_DAC_R1_OM_R_SFT 0 + +/* LOUT Mixer Control (0x53) */ +#define RT5651_M_DAC_L1_LM (0x1 << 15) +#define RT5651_M_DAC_L1_LM_SFT 15 +#define RT5651_M_DAC_R1_LM (0x1 << 14) +#define RT5651_M_DAC_R1_LM_SFT 14 +#define RT5651_M_OV_L_LM (0x1 << 13) +#define RT5651_M_OV_L_LM_SFT 13 +#define RT5651_M_OV_R_LM (0x1 << 12) +#define RT5651_M_OV_R_LM_SFT 12 +#define RT5651_G_LOUTMIX_MASK (0x1 << 11) +#define RT5651_G_LOUTMIX_SFT 11 + +/* Power Management for Digital 1 (0x61) */ +#define RT5651_PWR_I2S1 (0x1 << 15) +#define RT5651_PWR_I2S1_BIT 15 +#define RT5651_PWR_I2S2 (0x1 << 14) +#define RT5651_PWR_I2S2_BIT 14 +#define RT5651_PWR_DAC_L1 (0x1 << 12) +#define RT5651_PWR_DAC_L1_BIT 12 +#define RT5651_PWR_DAC_R1 (0x1 << 11) +#define RT5651_PWR_DAC_R1_BIT 11 +#define RT5651_PWR_ADC_L (0x1 << 2) +#define RT5651_PWR_ADC_L_BIT 2 +#define RT5651_PWR_ADC_R (0x1 << 1) +#define RT5651_PWR_ADC_R_BIT 1 + +/* Power Management for Digital 2 (0x62) */ +#define RT5651_PWR_ADC_STO1_F (0x1 << 15) +#define RT5651_PWR_ADC_STO1_F_BIT 15 +#define RT5651_PWR_ADC_STO2_F (0x1 << 14) +#define RT5651_PWR_ADC_STO2_F_BIT 14 +#define RT5651_PWR_DAC_STO1_F (0x1 << 11) +#define RT5651_PWR_DAC_STO1_F_BIT 11 +#define RT5651_PWR_DAC_STO2_F (0x1 << 10) +#define RT5651_PWR_DAC_STO2_F_BIT 10 +#define RT5651_PWR_PDM (0x1 << 9) +#define RT5651_PWR_PDM_BIT 9 + +/* Power Management for Analog 1 (0x63) */ +#define RT5651_PWR_VREF1 (0x1 << 15) +#define RT5651_PWR_VREF1_BIT 15 +#define RT5651_PWR_FV1 (0x1 << 14) +#define RT5651_PWR_FV1_BIT 14 +#define RT5651_PWR_MB (0x1 << 13) +#define RT5651_PWR_MB_BIT 13 +#define RT5651_PWR_LM (0x1 << 12) +#define RT5651_PWR_LM_BIT 12 +#define RT5651_PWR_BG (0x1 << 11) +#define RT5651_PWR_BG_BIT 11 +#define RT5651_PWR_HP_L (0x1 << 7) +#define RT5651_PWR_HP_L_BIT 7 +#define RT5651_PWR_HP_R (0x1 << 6) +#define RT5651_PWR_HP_R_BIT 6 +#define RT5651_PWR_HA (0x1 << 5) +#define RT5651_PWR_HA_BIT 5 +#define RT5651_PWR_VREF2 (0x1 << 4) +#define RT5651_PWR_VREF2_BIT 4 +#define RT5651_PWR_FV2 (0x1 << 3) +#define RT5651_PWR_FV2_BIT 3 +#define RT5651_PWR_LDO (0x1 << 2) +#define RT5651_PWR_LDO_BIT 2 +#define RT5651_PWR_LDO_DVO_MASK (0x3) +#define RT5651_PWR_LDO_DVO_1_0V 0 +#define RT5651_PWR_LDO_DVO_1_1V 1 +#define RT5651_PWR_LDO_DVO_1_2V 2 +#define RT5651_PWR_LDO_DVO_1_3V 3 + +/* Power Management for Analog 2 (0x64) */ +#define RT5651_PWR_BST1 (0x1 << 15) +#define RT5651_PWR_BST1_BIT 15 +#define RT5651_PWR_BST2 (0x1 << 14) +#define RT5651_PWR_BST2_BIT 14 +#define RT5651_PWR_BST3 (0x1 << 13) +#define RT5651_PWR_BST3_BIT 13 +#define RT5651_PWR_MB1 (0x1 << 11) +#define RT5651_PWR_MB1_BIT 11 +#define RT5651_PWR_PLL (0x1 << 9) +#define RT5651_PWR_PLL_BIT 9 +#define RT5651_PWR_BST1_OP2 (0x1 << 5) +#define RT5651_PWR_BST1_OP2_BIT 5 +#define RT5651_PWR_BST2_OP2 (0x1 << 4) +#define RT5651_PWR_BST2_OP2_BIT 4 +#define RT5651_PWR_BST3_OP2 (0x1 << 3) +#define RT5651_PWR_BST3_OP2_BIT 3 +#define RT5651_PWR_JD_M (0x1 << 2) +#define RT5651_PWM_JD_M_BIT 2 +#define RT5651_PWR_JD2 (0x1 << 1) +#define RT5651_PWM_JD2_BIT 1 +#define RT5651_PWR_JD3 (0x1) +#define RT5651_PWM_JD3_BIT 0 + +/* Power Management for Mixer (0x65) */ +#define RT5651_PWR_OM_L (0x1 << 15) +#define RT5651_PWR_OM_L_BIT 15 +#define RT5651_PWR_OM_R (0x1 << 14) +#define RT5651_PWR_OM_R_BIT 14 +#define RT5651_PWR_RM_L (0x1 << 11) +#define RT5651_PWR_RM_L_BIT 11 +#define RT5651_PWR_RM_R (0x1 << 10) +#define RT5651_PWR_RM_R_BIT 10 + +/* Power Management for Volume (0x66) */ +#define RT5651_PWR_OV_L (0x1 << 13) +#define RT5651_PWR_OV_L_BIT 13 +#define RT5651_PWR_OV_R (0x1 << 12) +#define RT5651_PWR_OV_R_BIT 12 +#define RT5651_PWR_HV_L (0x1 << 11) +#define RT5651_PWR_HV_L_BIT 11 +#define RT5651_PWR_HV_R (0x1 << 10) +#define RT5651_PWR_HV_R_BIT 10 +#define RT5651_PWR_IN1_L (0x1 << 9) +#define RT5651_PWR_IN1_L_BIT 9 +#define RT5651_PWR_IN1_R (0x1 << 8) +#define RT5651_PWR_IN1_R_BIT 8 +#define RT5651_PWR_IN2_L (0x1 << 7) +#define RT5651_PWR_IN2_L_BIT 7 +#define RT5651_PWR_IN2_R (0x1 << 6) +#define RT5651_PWR_IN2_R_BIT 6 + +/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71) */ +#define RT5651_I2S_MS_MASK (0x1 << 15) +#define RT5651_I2S_MS_SFT 15 +#define RT5651_I2S_MS_M (0x0 << 15) +#define RT5651_I2S_MS_S (0x1 << 15) +#define RT5651_I2S_O_CP_MASK (0x3 << 10) +#define RT5651_I2S_O_CP_SFT 10 +#define RT5651_I2S_O_CP_OFF (0x0 << 10) +#define RT5651_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5651_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5651_I2S_I_CP_MASK (0x3 << 8) +#define RT5651_I2S_I_CP_SFT 8 +#define RT5651_I2S_I_CP_OFF (0x0 << 8) +#define RT5651_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5651_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5651_I2S_BP_MASK (0x1 << 7) +#define RT5651_I2S_BP_SFT 7 +#define RT5651_I2S_BP_NOR (0x0 << 7) +#define RT5651_I2S_BP_INV (0x1 << 7) +#define RT5651_I2S_DL_MASK (0x3 << 2) +#define RT5651_I2S_DL_SFT 2 +#define RT5651_I2S_DL_16 (0x0 << 2) +#define RT5651_I2S_DL_20 (0x1 << 2) +#define RT5651_I2S_DL_24 (0x2 << 2) +#define RT5651_I2S_DL_8 (0x3 << 2) +#define RT5651_I2S_DF_MASK (0x3) +#define RT5651_I2S_DF_SFT 0 +#define RT5651_I2S_DF_I2S (0x0) +#define RT5651_I2S_DF_LEFT (0x1) +#define RT5651_I2S_DF_PCM_A (0x2) +#define RT5651_I2S_DF_PCM_B (0x3) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5651_I2S_PD1_MASK (0x7 << 12) +#define RT5651_I2S_PD1_SFT 12 +#define RT5651_I2S_PD1_1 (0x0 << 12) +#define RT5651_I2S_PD1_2 (0x1 << 12) +#define RT5651_I2S_PD1_3 (0x2 << 12) +#define RT5651_I2S_PD1_4 (0x3 << 12) +#define RT5651_I2S_PD1_6 (0x4 << 12) +#define RT5651_I2S_PD1_8 (0x5 << 12) +#define RT5651_I2S_PD1_12 (0x6 << 12) +#define RT5651_I2S_PD1_16 (0x7 << 12) +#define RT5651_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5651_I2S_BCLK_MS2_SFT 11 +#define RT5651_I2S_BCLK_MS2_32 (0x0 << 11) +#define RT5651_I2S_BCLK_MS2_64 (0x1 << 11) +#define RT5651_I2S_PD2_MASK (0x7 << 8) +#define RT5651_I2S_PD2_SFT 8 +#define RT5651_I2S_PD2_1 (0x0 << 8) +#define RT5651_I2S_PD2_2 (0x1 << 8) +#define RT5651_I2S_PD2_3 (0x2 << 8) +#define RT5651_I2S_PD2_4 (0x3 << 8) +#define RT5651_I2S_PD2_6 (0x4 << 8) +#define RT5651_I2S_PD2_8 (0x5 << 8) +#define RT5651_I2S_PD2_12 (0x6 << 8) +#define RT5651_I2S_PD2_16 (0x7 << 8) +#define RT5651_DAC_OSR_MASK (0x3 << 2) +#define RT5651_DAC_OSR_SFT 2 +#define RT5651_DAC_OSR_128 (0x0 << 2) +#define RT5651_DAC_OSR_64 (0x1 << 2) +#define RT5651_DAC_OSR_32 (0x2 << 2) +#define RT5651_DAC_OSR_128_3 (0x3 << 2) +#define RT5651_ADC_OSR_MASK (0x3) +#define RT5651_ADC_OSR_SFT 0 +#define RT5651_ADC_OSR_128 (0x0) +#define RT5651_ADC_OSR_64 (0x1) +#define RT5651_ADC_OSR_32 (0x2) +#define RT5651_ADC_OSR_128_3 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5651_DAHPF_EN (0x1 << 11) +#define RT5651_DAHPF_EN_SFT 11 +#define RT5651_ADHPF_EN (0x1 << 10) +#define RT5651_ADHPF_EN_SFT 10 + +/* Digital Microphone Control (0x75) */ +#define RT5651_DMIC_1_EN_MASK (0x1 << 15) +#define RT5651_DMIC_1_EN_SFT 15 +#define RT5651_DMIC_1_DIS (0x0 << 15) +#define RT5651_DMIC_1_EN (0x1 << 15) +#define RT5651_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5651_DMIC_1L_LH_SFT 13 +#define RT5651_DMIC_1L_LH_FALLING (0x0 << 13) +#define RT5651_DMIC_1L_LH_RISING (0x1 << 13) +#define RT5651_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5651_DMIC_1R_LH_SFT 12 +#define RT5651_DMIC_1R_LH_FALLING (0x0 << 12) +#define RT5651_DMIC_1R_LH_RISING (0x1 << 12) +#define RT5651_DMIC_1_DP_MASK (0x3 << 10) +#define RT5651_DMIC_1_DP_SFT 10 +#define RT5651_DMIC_1_DP_GPIO6 (0x0 << 10) +#define RT5651_DMIC_1_DP_IN1P (0x1 << 10) +#define RT5651_DMIC_2_DP_GPIO8 (0x2 << 10) +#define RT5651_DMIC_CLK_MASK (0x7 << 5) +#define RT5651_DMIC_CLK_SFT 5 + +/* TDM Control 1 (0x77) */ +#define RT5651_TDM_INTEL_SEL_MASK (0x1 << 15) +#define RT5651_TDM_INTEL_SEL_SFT 15 +#define RT5651_TDM_INTEL_SEL_64 (0x0 << 15) +#define RT5651_TDM_INTEL_SEL_50 (0x1 << 15) +#define RT5651_TDM_MODE_SEL_MASK (0x1 << 14) +#define RT5651_TDM_MODE_SEL_SFT 14 +#define RT5651_TDM_MODE_SEL_NOR (0x0 << 14) +#define RT5651_TDM_MODE_SEL_TDM (0x1 << 14) +#define RT5651_TDM_CH_NUM_SEL_MASK (0x3 << 12) +#define RT5651_TDM_CH_NUM_SEL_SFT 12 +#define RT5651_TDM_CH_NUM_SEL_2 (0x0 << 12) +#define RT5651_TDM_CH_NUM_SEL_4 (0x1 << 12) +#define RT5651_TDM_CH_NUM_SEL_6 (0x2 << 12) +#define RT5651_TDM_CH_NUM_SEL_8 (0x3 << 12) +#define RT5651_TDM_CH_LEN_SEL_MASK (0x3 << 10) +#define RT5651_TDM_CH_LEN_SEL_SFT 10 +#define RT5651_TDM_CH_LEN_SEL_16 (0x0 << 10) +#define RT5651_TDM_CH_LEN_SEL_20 (0x1 << 10) +#define RT5651_TDM_CH_LEN_SEL_24 (0x2 << 10) +#define RT5651_TDM_CH_LEN_SEL_32 (0x3 << 10) +#define RT5651_TDM_ADC_SEL_MASK (0x1 << 9) +#define RT5651_TDM_ADC_SEL_SFT 9 +#define RT5651_TDM_ADC_SEL_NOR (0x0 << 9) +#define RT5651_TDM_ADC_SEL_SWAP (0x1 << 9) +#define RT5651_TDM_ADC_START_SEL_MASK (0x1 << 8) +#define RT5651_TDM_ADC_START_SEL_SFT 8 +#define RT5651_TDM_ADC_START_SEL_SL0 (0x0 << 8) +#define RT5651_TDM_ADC_START_SEL_SL4 (0x1 << 8) +#define RT5651_TDM_I2S_CH2_SEL_MASK (0x3 << 6) +#define RT5651_TDM_I2S_CH2_SEL_SFT 6 +#define RT5651_TDM_I2S_CH2_SEL_LR (0x0 << 6) +#define RT5651_TDM_I2S_CH2_SEL_RL (0x1 << 6) +#define RT5651_TDM_I2S_CH2_SEL_LL (0x2 << 6) +#define RT5651_TDM_I2S_CH2_SEL_RR (0x3 << 6) +#define RT5651_TDM_I2S_CH4_SEL_MASK (0x3 << 4) +#define RT5651_TDM_I2S_CH4_SEL_SFT 4 +#define RT5651_TDM_I2S_CH4_SEL_LR (0x0 << 4) +#define RT5651_TDM_I2S_CH4_SEL_RL (0x1 << 4) +#define RT5651_TDM_I2S_CH4_SEL_LL (0x2 << 4) +#define RT5651_TDM_I2S_CH4_SEL_RR (0x3 << 4) +#define RT5651_TDM_I2S_CH6_SEL_MASK (0x3 << 2) +#define RT5651_TDM_I2S_CH6_SEL_SFT 2 +#define RT5651_TDM_I2S_CH6_SEL_LR (0x0 << 2) +#define RT5651_TDM_I2S_CH6_SEL_RL (0x1 << 2) +#define RT5651_TDM_I2S_CH6_SEL_LL (0x2 << 2) +#define RT5651_TDM_I2S_CH6_SEL_RR (0x3 << 2) +#define RT5651_TDM_I2S_CH8_SEL_MASK (0x3) +#define RT5651_TDM_I2S_CH8_SEL_SFT 0 +#define RT5651_TDM_I2S_CH8_SEL_LR (0x0) +#define RT5651_TDM_I2S_CH8_SEL_RL (0x1) +#define RT5651_TDM_I2S_CH8_SEL_LL (0x2) +#define RT5651_TDM_I2S_CH8_SEL_RR (0x3) + +/* TDM Control 2 (0x78) */ +#define RT5651_TDM_LRCK_POL_SEL_MASK (0x1 << 15) +#define RT5651_TDM_LRCK_POL_SEL_SFT 15 +#define RT5651_TDM_LRCK_POL_SEL_NOR (0x0 << 15) +#define RT5651_TDM_LRCK_POL_SEL_INV (0x1 << 15) +#define RT5651_TDM_CH_VAL_SEL_MASK (0x1 << 14) +#define RT5651_TDM_CH_VAL_SEL_SFT 14 +#define RT5651_TDM_CH_VAL_SEL_CH01 (0x0 << 14) +#define RT5651_TDM_CH_VAL_SEL_CH0123 (0x1 << 14) +#define RT5651_TDM_CH_VAL_EN (0x1 << 13) +#define RT5651_TDM_CH_VAL_SFT 13 +#define RT5651_TDM_LPBK_EN (0x1 << 12) +#define RT5651_TDM_LPBK_SFT 12 +#define RT5651_TDM_LRCK_PULSE_SEL_MASK (0x1 << 11) +#define RT5651_TDM_LRCK_PULSE_SEL_SFT 11 +#define RT5651_TDM_LRCK_PULSE_SEL_BCLK (0x0 << 11) +#define RT5651_TDM_LRCK_PULSE_SEL_CH (0x1 << 11) +#define RT5651_TDM_END_EDGE_SEL_MASK (0x1 << 10) +#define RT5651_TDM_END_EDGE_SEL_SFT 10 +#define RT5651_TDM_END_EDGE_SEL_POS (0x0 << 10) +#define RT5651_TDM_END_EDGE_SEL_NEG (0x1 << 10) +#define RT5651_TDM_END_EDGE_EN (0x1 << 9) +#define RT5651_TDM_END_EDGE_EN_SFT 9 +#define RT5651_TDM_TRAN_EDGE_SEL_MASK (0x1 << 8) +#define RT5651_TDM_TRAN_EDGE_SEL_SFT 8 +#define RT5651_TDM_TRAN_EDGE_SEL_POS (0x0 << 8) +#define RT5651_TDM_TRAN_EDGE_SEL_NEG (0x1 << 8) +#define RT5651_M_TDM2_L (0x1 << 7) +#define RT5651_M_TDM2_L_SFT 7 +#define RT5651_M_TDM2_R (0x1 << 6) +#define RT5651_M_TDM2_R_SFT 6 +#define RT5651_M_TDM4_L (0x1 << 5) +#define RT5651_M_TDM4_L_SFT 5 +#define RT5651_M_TDM4_R (0x1 << 4) +#define RT5651_M_TDM4_R_SFT 4 + +/* TDM Control 3 (0x79) */ +#define RT5651_CH2_L_SEL_MASK (0x7 << 12) +#define RT5651_CH2_L_SEL_SFT 12 +#define RT5651_CH2_L_SEL_SL0 (0x0 << 12) +#define RT5651_CH2_L_SEL_SL1 (0x1 << 12) +#define RT5651_CH2_L_SEL_SL2 (0x2 << 12) +#define RT5651_CH2_L_SEL_SL3 (0x3 << 12) +#define RT5651_CH2_L_SEL_SL4 (0x4 << 12) +#define RT5651_CH2_L_SEL_SL5 (0x5 << 12) +#define RT5651_CH2_L_SEL_SL6 (0x6 << 12) +#define RT5651_CH2_L_SEL_SL7 (0x7 << 12) +#define RT5651_CH2_R_SEL_MASK (0x7 << 8) +#define RT5651_CH2_R_SEL_SFT 8 +#define RT5651_CH2_R_SEL_SL0 (0x0 << 8) +#define RT5651_CH2_R_SEL_SL1 (0x1 << 8) +#define RT5651_CH2_R_SEL_SL2 (0x2 << 8) +#define RT5651_CH2_R_SEL_SL3 (0x3 << 8) +#define RT5651_CH2_R_SEL_SL4 (0x4 << 8) +#define RT5651_CH2_R_SEL_SL5 (0x5 << 8) +#define RT5651_CH2_R_SEL_SL6 (0x6 << 8) +#define RT5651_CH2_R_SEL_SL7 (0x7 << 8) +#define RT5651_CH4_L_SEL_MASK (0x7 << 4) +#define RT5651_CH4_L_SEL_SFT 4 +#define RT5651_CH4_L_SEL_SL0 (0x0 << 4) +#define RT5651_CH4_L_SEL_SL1 (0x1 << 4) +#define RT5651_CH4_L_SEL_SL2 (0x2 << 4) +#define RT5651_CH4_L_SEL_SL3 (0x3 << 4) +#define RT5651_CH4_L_SEL_SL4 (0x4 << 4) +#define RT5651_CH4_L_SEL_SL5 (0x5 << 4) +#define RT5651_CH4_L_SEL_SL6 (0x6 << 4) +#define RT5651_CH4_L_SEL_SL7 (0x7 << 4) +#define RT5651_CH4_R_SEL_MASK (0x7) +#define RT5651_CH4_R_SEL_SFT 0 +#define RT5651_CH4_R_SEL_SL0 (0x0) +#define RT5651_CH4_R_SEL_SL1 (0x1) +#define RT5651_CH4_R_SEL_SL2 (0x2) +#define RT5651_CH4_R_SEL_SL3 (0x3) +#define RT5651_CH4_R_SEL_SL4 (0x4) +#define RT5651_CH4_R_SEL_SL5 (0x5) +#define RT5651_CH4_R_SEL_SL6 (0x6) +#define RT5651_CH4_R_SEL_SL7 (0x7) + +/* Global Clock Control (0x80) */ +#define RT5651_SCLK_SRC_MASK (0x3 << 14) +#define RT5651_SCLK_SRC_SFT 14 +#define RT5651_SCLK_SRC_MCLK (0x0 << 14) +#define RT5651_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5651_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5651_PLL1_SRC_MASK (0x3 << 12) +#define RT5651_PLL1_SRC_SFT 12 +#define RT5651_PLL1_SRC_MCLK (0x0 << 12) +#define RT5651_PLL1_SRC_BCLK1 (0x1 << 12) +#define RT5651_PLL1_SRC_BCLK2 (0x2 << 12) +#define RT5651_PLL1_PD_MASK (0x1 << 3) +#define RT5651_PLL1_PD_SFT 3 +#define RT5651_PLL1_PD_1 (0x0 << 3) +#define RT5651_PLL1_PD_2 (0x1 << 3) + +#define RT5651_PLL_INP_MAX 40000000 +#define RT5651_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5651_PLL_N_MAX 0x1ff +#define RT5651_PLL_N_MASK (RT5651_PLL_N_MAX << 7) +#define RT5651_PLL_N_SFT 7 +#define RT5651_PLL_K_MAX 0x1f +#define RT5651_PLL_K_MASK (RT5651_PLL_K_MAX) +#define RT5651_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5651_PLL_M_MAX 0xf +#define RT5651_PLL_M_MASK (RT5651_PLL_M_MAX << 12) +#define RT5651_PLL_M_SFT 12 +#define RT5651_PLL_M_BP (0x1 << 11) +#define RT5651_PLL_M_BP_SFT 11 + +/* PLL tracking mode 1 (0x83) */ +#define RT5651_STO1_T_MASK (0x1 << 15) +#define RT5651_STO1_T_SFT 15 +#define RT5651_STO1_T_SCLK (0x0 << 15) +#define RT5651_STO1_T_LRCK1 (0x1 << 15) +#define RT5651_STO2_T_MASK (0x1 << 12) +#define RT5651_STO2_T_SFT 12 +#define RT5651_STO2_T_I2S2 (0x0 << 12) +#define RT5651_STO2_T_LRCK2 (0x1 << 12) +#define RT5651_ASRC2_REF_MASK (0x1 << 11) +#define RT5651_ASRC2_REF_SFT 11 +#define RT5651_ASRC2_REF_LRCK2 (0x0 << 11) +#define RT5651_ASRC2_REF_LRCK1 (0x1 << 11) +#define RT5651_DMIC_1_M_MASK (0x1 << 9) +#define RT5651_DMIC_1_M_SFT 9 +#define RT5651_DMIC_1_M_NOR (0x0 << 9) +#define RT5651_DMIC_1_M_ASYN (0x1 << 9) + +/* PLL tracking mode 2 (0x84) */ +#define RT5651_STO1_ASRC_EN (0x1 << 15) +#define RT5651_STO1_ASRC_EN_SFT 15 +#define RT5651_STO2_ASRC_EN (0x1 << 14) +#define RT5651_STO2_ASRC_EN_SFT 14 +#define RT5651_STO1_DAC_M_MASK (0x1 << 13) +#define RT5651_STO1_DAC_M_SFT 13 +#define RT5651_STO1_DAC_M_NOR (0x0 << 13) +#define RT5651_STO1_DAC_M_ASRC (0x1 << 13) +#define RT5651_STO2_DAC_M_MASK (0x1 << 12) +#define RT5651_STO2_DAC_M_SFT 12 +#define RT5651_STO2_DAC_M_NOR (0x0 << 12) +#define RT5651_STO2_DAC_M_ASRC (0x1 << 12) +#define RT5651_ADC_M_MASK (0x1 << 11) +#define RT5651_ADC_M_SFT 11 +#define RT5651_ADC_M_NOR (0x0 << 11) +#define RT5651_ADC_M_ASRC (0x1 << 11) +#define RT5651_I2S1_R_D_MASK (0x1 << 4) +#define RT5651_I2S1_R_D_SFT 4 +#define RT5651_I2S1_R_D_DIS (0x0 << 4) +#define RT5651_I2S1_R_D_EN (0x1 << 4) +#define RT5651_I2S2_R_D_MASK (0x1 << 3) +#define RT5651_I2S2_R_D_SFT 3 +#define RT5651_I2S2_R_D_DIS (0x0 << 3) +#define RT5651_I2S2_R_D_EN (0x1 << 3) +#define RT5651_PRE_SCLK_MASK (0x3) +#define RT5651_PRE_SCLK_SFT 0 +#define RT5651_PRE_SCLK_512 (0x0) +#define RT5651_PRE_SCLK_1024 (0x1) +#define RT5651_PRE_SCLK_2048 (0x2) + +/* PLL tracking mode 3 (0x85) */ +#define RT5651_I2S1_RATE_MASK (0xf << 12) +#define RT5651_I2S1_RATE_SFT 12 +#define RT5651_I2S2_RATE_MASK (0xf << 8) +#define RT5651_I2S2_RATE_SFT 8 +#define RT5651_G_ASRC_LP_MASK (0x1 << 3) +#define RT5651_G_ASRC_LP_SFT 3 +#define RT5651_ASRC_LP_F_M (0x1 << 2) +#define RT5651_ASRC_LP_F_SFT 2 +#define RT5651_ASRC_LP_F_NOR (0x0 << 2) +#define RT5651_ASRC_LP_F_SB (0x1 << 2) +#define RT5651_FTK_PH_DET_MASK (0x3) +#define RT5651_FTK_PH_DET_SFT 0 +#define RT5651_FTK_PH_DET_DIV1 (0x0) +#define RT5651_FTK_PH_DET_DIV2 (0x1) +#define RT5651_FTK_PH_DET_DIV4 (0x2) +#define RT5651_FTK_PH_DET_DIV8 (0x3) + +/*PLL tracking mode 6 (0x89) */ +#define RT5651_I2S1_PD_MASK (0x7 << 12) +#define RT5651_I2S1_PD_SFT 12 +#define RT5651_I2S2_PD_MASK (0x7 << 8) +#define RT5651_I2S2_PD_SFT 8 + +/*PLL tracking mode 7 (0x8a) */ +#define RT5651_FSI1_RATE_MASK (0xf << 12) +#define RT5651_FSI1_RATE_SFT 12 +#define RT5651_FSI2_RATE_MASK (0xf << 8) +#define RT5651_FSI2_RATE_SFT 8 + +/* HPOUT Over Current Detection (0x8b) */ +#define RT5651_HP_OVCD_MASK (0x1 << 10) +#define RT5651_HP_OVCD_SFT 10 +#define RT5651_HP_OVCD_DIS (0x0 << 10) +#define RT5651_HP_OVCD_EN (0x1 << 10) +#define RT5651_HP_OC_TH_MASK (0x3 << 8) +#define RT5651_HP_OC_TH_SFT 8 +#define RT5651_HP_OC_TH_90 (0x0 << 8) +#define RT5651_HP_OC_TH_105 (0x1 << 8) +#define RT5651_HP_OC_TH_120 (0x2 << 8) +#define RT5651_HP_OC_TH_135 (0x3 << 8) + +/* Depop Mode Control 1 (0x8e) */ +#define RT5651_SMT_TRIG_MASK (0x1 << 15) +#define RT5651_SMT_TRIG_SFT 15 +#define RT5651_SMT_TRIG_DIS (0x0 << 15) +#define RT5651_SMT_TRIG_EN (0x1 << 15) +#define RT5651_HP_L_SMT_MASK (0x1 << 9) +#define RT5651_HP_L_SMT_SFT 9 +#define RT5651_HP_L_SMT_DIS (0x0 << 9) +#define RT5651_HP_L_SMT_EN (0x1 << 9) +#define RT5651_HP_R_SMT_MASK (0x1 << 8) +#define RT5651_HP_R_SMT_SFT 8 +#define RT5651_HP_R_SMT_DIS (0x0 << 8) +#define RT5651_HP_R_SMT_EN (0x1 << 8) +#define RT5651_HP_CD_PD_MASK (0x1 << 7) +#define RT5651_HP_CD_PD_SFT 7 +#define RT5651_HP_CD_PD_DIS (0x0 << 7) +#define RT5651_HP_CD_PD_EN (0x1 << 7) +#define RT5651_RSTN_MASK (0x1 << 6) +#define RT5651_RSTN_SFT 6 +#define RT5651_RSTN_DIS (0x0 << 6) +#define RT5651_RSTN_EN (0x1 << 6) +#define RT5651_RSTP_MASK (0x1 << 5) +#define RT5651_RSTP_SFT 5 +#define RT5651_RSTP_DIS (0x0 << 5) +#define RT5651_RSTP_EN (0x1 << 5) +#define RT5651_HP_CO_MASK (0x1 << 4) +#define RT5651_HP_CO_SFT 4 +#define RT5651_HP_CO_DIS (0x0 << 4) +#define RT5651_HP_CO_EN (0x1 << 4) +#define RT5651_HP_CP_MASK (0x1 << 3) +#define RT5651_HP_CP_SFT 3 +#define RT5651_HP_CP_PD (0x0 << 3) +#define RT5651_HP_CP_PU (0x1 << 3) +#define RT5651_HP_SG_MASK (0x1 << 2) +#define RT5651_HP_SG_SFT 2 +#define RT5651_HP_SG_DIS (0x0 << 2) +#define RT5651_HP_SG_EN (0x1 << 2) +#define RT5651_HP_DP_MASK (0x1 << 1) +#define RT5651_HP_DP_SFT 1 +#define RT5651_HP_DP_PD (0x0 << 1) +#define RT5651_HP_DP_PU (0x1 << 1) +#define RT5651_HP_CB_MASK (0x1) +#define RT5651_HP_CB_SFT 0 +#define RT5651_HP_CB_PD (0x0) +#define RT5651_HP_CB_PU (0x1) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5651_DEPOP_MASK (0x1 << 13) +#define RT5651_DEPOP_SFT 13 +#define RT5651_DEPOP_AUTO (0x0 << 13) +#define RT5651_DEPOP_MAN (0x1 << 13) +#define RT5651_RAMP_MASK (0x1 << 12) +#define RT5651_RAMP_SFT 12 +#define RT5651_RAMP_DIS (0x0 << 12) +#define RT5651_RAMP_EN (0x1 << 12) +#define RT5651_BPS_MASK (0x1 << 11) +#define RT5651_BPS_SFT 11 +#define RT5651_BPS_DIS (0x0 << 11) +#define RT5651_BPS_EN (0x1 << 11) +#define RT5651_FAST_UPDN_MASK (0x1 << 10) +#define RT5651_FAST_UPDN_SFT 10 +#define RT5651_FAST_UPDN_DIS (0x0 << 10) +#define RT5651_FAST_UPDN_EN (0x1 << 10) +#define RT5651_MRES_MASK (0x3 << 8) +#define RT5651_MRES_SFT 8 +#define RT5651_MRES_15MO (0x0 << 8) +#define RT5651_MRES_25MO (0x1 << 8) +#define RT5651_MRES_35MO (0x2 << 8) +#define RT5651_MRES_45MO (0x3 << 8) +#define RT5651_VLO_MASK (0x1 << 7) +#define RT5651_VLO_SFT 7 +#define RT5651_VLO_3V (0x0 << 7) +#define RT5651_VLO_32V (0x1 << 7) +#define RT5651_DIG_DP_MASK (0x1 << 6) +#define RT5651_DIG_DP_SFT 6 +#define RT5651_DIG_DP_DIS (0x0 << 6) +#define RT5651_DIG_DP_EN (0x1 << 6) +#define RT5651_DP_TH_MASK (0x3 << 4) +#define RT5651_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5651_CP_SYS_MASK (0x7 << 12) +#define RT5651_CP_SYS_SFT 12 +#define RT5651_CP_FQ1_MASK (0x7 << 8) +#define RT5651_CP_FQ1_SFT 8 +#define RT5651_CP_FQ2_MASK (0x7 << 4) +#define RT5651_CP_FQ2_SFT 4 +#define RT5651_CP_FQ3_MASK (0x7) +#define RT5651_CP_FQ3_SFT 0 +#define RT5651_CP_FQ_1_5_KHZ 0 +#define RT5651_CP_FQ_3_KHZ 1 +#define RT5651_CP_FQ_6_KHZ 2 +#define RT5651_CP_FQ_12_KHZ 3 +#define RT5651_CP_FQ_24_KHZ 4 +#define RT5651_CP_FQ_48_KHZ 5 +#define RT5651_CP_FQ_96_KHZ 6 +#define RT5651_CP_FQ_192_KHZ 7 + +/* HPOUT charge pump (0x91) */ +#define RT5651_OSW_L_MASK (0x1 << 11) +#define RT5651_OSW_L_SFT 11 +#define RT5651_OSW_L_DIS (0x0 << 11) +#define RT5651_OSW_L_EN (0x1 << 11) +#define RT5651_OSW_R_MASK (0x1 << 10) +#define RT5651_OSW_R_SFT 10 +#define RT5651_OSW_R_DIS (0x0 << 10) +#define RT5651_OSW_R_EN (0x1 << 10) +#define RT5651_PM_HP_MASK (0x3 << 8) +#define RT5651_PM_HP_SFT 8 +#define RT5651_PM_HP_LV (0x0 << 8) +#define RT5651_PM_HP_MV (0x1 << 8) +#define RT5651_PM_HP_HV (0x2 << 8) +#define RT5651_IB_HP_MASK (0x3 << 6) +#define RT5651_IB_HP_SFT 6 +#define RT5651_IB_HP_125IL (0x0 << 6) +#define RT5651_IB_HP_25IL (0x1 << 6) +#define RT5651_IB_HP_5IL (0x2 << 6) +#define RT5651_IB_HP_1IL (0x3 << 6) + +/* Micbias Control (0x93) */ +#define RT5651_MIC1_BS_MASK (0x1 << 15) +#define RT5651_MIC1_BS_SFT 15 +#define RT5651_MIC1_BS_9AV (0x0 << 15) +#define RT5651_MIC1_BS_75AV (0x1 << 15) +#define RT5651_MIC1_CLK_MASK (0x1 << 13) +#define RT5651_MIC1_CLK_SFT 13 +#define RT5651_MIC1_CLK_DIS (0x0 << 13) +#define RT5651_MIC1_CLK_EN (0x1 << 13) +#define RT5651_MIC1_OVCD_MASK (0x1 << 11) +#define RT5651_MIC1_OVCD_SFT 11 +#define RT5651_MIC1_OVCD_DIS (0x0 << 11) +#define RT5651_MIC1_OVCD_EN (0x1 << 11) +#define RT5651_MIC1_OVTH_MASK (0x3 << 9) +#define RT5651_MIC1_OVTH_SFT 9 +#define RT5651_MIC1_OVTH_600UA (0x0 << 9) +#define RT5651_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5651_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5651_PWR_MB_MASK (0x1 << 5) +#define RT5651_PWR_MB_SFT 5 +#define RT5651_PWR_MB_PD (0x0 << 5) +#define RT5651_PWR_MB_PU (0x1 << 5) +#define RT5651_PWR_CLK12M_MASK (0x1 << 4) +#define RT5651_PWR_CLK12M_SFT 4 +#define RT5651_PWR_CLK12M_PD (0x0 << 4) +#define RT5651_PWR_CLK12M_PU (0x1 << 4) + +/* Analog JD Control 1 (0x94) */ +#define RT5651_JD2_CMP_MASK (0x7 << 12) +#define RT5651_JD2_CMP_SFT 12 +#define RT5651_JD_PU (0x1 << 11) +#define RT5651_JD_PU_SFT 11 +#define RT5651_JD_PD (0x1 << 10) +#define RT5651_JD_PD_SFT 10 +#define RT5651_JD_MODE_SEL_MASK (0x3 << 8) +#define RT5651_JD_MODE_SEL_SFT 8 +#define RT5651_JD_MODE_SEL_M0 (0x0 << 8) +#define RT5651_JD_MODE_SEL_M1 (0x1 << 8) +#define RT5651_JD_MODE_SEL_M2 (0x2 << 8) +#define RT5651_JD_M_CMP (0x7 << 4) +#define RT5651_JD_M_CMP_SFT 4 +#define RT5651_JD_M_PU (0x1 << 3) +#define RT5651_JD_M_PU_SFT 3 +#define RT5651_JD_M_PD (0x1 << 2) +#define RT5651_JD_M_PD_SFT 2 +#define RT5651_JD_M_MODE_SEL_MASK (0x3) +#define RT5651_JD_M_MODE_SEL_SFT 0 +#define RT5651_JD_M_MODE_SEL_M0 (0x0) +#define RT5651_JD_M_MODE_SEL_M1 (0x1) +#define RT5651_JD_M_MODE_SEL_M2 (0x2) + +/* Analog JD Control 2 (0x95) */ +#define RT5651_JD3_CMP_MASK (0x7 << 12) +#define RT5651_JD3_CMP_SFT 12 + +/* EQ Control 1 (0xb0) */ +#define RT5651_EQ_SRC_MASK (0x1 << 15) +#define RT5651_EQ_SRC_SFT 15 +#define RT5651_EQ_SRC_DAC (0x0 << 15) +#define RT5651_EQ_SRC_ADC (0x1 << 15) +#define RT5651_EQ_UPD (0x1 << 14) +#define RT5651_EQ_UPD_BIT 14 +#define RT5651_EQ_CD_MASK (0x1 << 13) +#define RT5651_EQ_CD_SFT 13 +#define RT5651_EQ_CD_DIS (0x0 << 13) +#define RT5651_EQ_CD_EN (0x1 << 13) +#define RT5651_EQ_DITH_MASK (0x3 << 8) +#define RT5651_EQ_DITH_SFT 8 +#define RT5651_EQ_DITH_NOR (0x0 << 8) +#define RT5651_EQ_DITH_LSB (0x1 << 8) +#define RT5651_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5651_EQ_DITH_LSB_2 (0x3 << 8) +#define RT5651_EQ_CD_F (0x1 << 7) +#define RT5651_EQ_CD_F_BIT 7 +#define RT5651_EQ_STA_HP2 (0x1 << 6) +#define RT5651_EQ_STA_HP2_BIT 6 +#define RT5651_EQ_STA_HP1 (0x1 << 5) +#define RT5651_EQ_STA_HP1_BIT 5 +#define RT5651_EQ_STA_BP4 (0x1 << 4) +#define RT5651_EQ_STA_BP4_BIT 4 +#define RT5651_EQ_STA_BP3 (0x1 << 3) +#define RT5651_EQ_STA_BP3_BIT 3 +#define RT5651_EQ_STA_BP2 (0x1 << 2) +#define RT5651_EQ_STA_BP2_BIT 2 +#define RT5651_EQ_STA_BP1 (0x1 << 1) +#define RT5651_EQ_STA_BP1_BIT 1 +#define RT5651_EQ_STA_LP (0x1) +#define RT5651_EQ_STA_LP_BIT 0 + +/* EQ Control 2 (0xb1) */ +#define RT5651_EQ_HPF1_M_MASK (0x1 << 8) +#define RT5651_EQ_HPF1_M_SFT 8 +#define RT5651_EQ_HPF1_M_HI (0x0 << 8) +#define RT5651_EQ_HPF1_M_1ST (0x1 << 8) +#define RT5651_EQ_LPF1_M_MASK (0x1 << 7) +#define RT5651_EQ_LPF1_M_SFT 7 +#define RT5651_EQ_LPF1_M_LO (0x0 << 7) +#define RT5651_EQ_LPF1_M_1ST (0x1 << 7) +#define RT5651_EQ_HPF2_MASK (0x1 << 6) +#define RT5651_EQ_HPF2_SFT 6 +#define RT5651_EQ_HPF2_DIS (0x0 << 6) +#define RT5651_EQ_HPF2_EN (0x1 << 6) +#define RT5651_EQ_HPF1_MASK (0x1 << 5) +#define RT5651_EQ_HPF1_SFT 5 +#define RT5651_EQ_HPF1_DIS (0x0 << 5) +#define RT5651_EQ_HPF1_EN (0x1 << 5) +#define RT5651_EQ_BPF4_MASK (0x1 << 4) +#define RT5651_EQ_BPF4_SFT 4 +#define RT5651_EQ_BPF4_DIS (0x0 << 4) +#define RT5651_EQ_BPF4_EN (0x1 << 4) +#define RT5651_EQ_BPF3_MASK (0x1 << 3) +#define RT5651_EQ_BPF3_SFT 3 +#define RT5651_EQ_BPF3_DIS (0x0 << 3) +#define RT5651_EQ_BPF3_EN (0x1 << 3) +#define RT5651_EQ_BPF2_MASK (0x1 << 2) +#define RT5651_EQ_BPF2_SFT 2 +#define RT5651_EQ_BPF2_DIS (0x0 << 2) +#define RT5651_EQ_BPF2_EN (0x1 << 2) +#define RT5651_EQ_BPF1_MASK (0x1 << 1) +#define RT5651_EQ_BPF1_SFT 1 +#define RT5651_EQ_BPF1_DIS (0x0 << 1) +#define RT5651_EQ_BPF1_EN (0x1 << 1) +#define RT5651_EQ_LPF_MASK (0x1) +#define RT5651_EQ_LPF_SFT 0 +#define RT5651_EQ_LPF_DIS (0x0) +#define RT5651_EQ_LPF_EN (0x1) +#define RT5651_EQ_CTRL_MASK (0x7f) + +/* Memory Test (0xb2) */ +#define RT5651_MT_MASK (0x1 << 15) +#define RT5651_MT_SFT 15 +#define RT5651_MT_DIS (0x0 << 15) +#define RT5651_MT_EN (0x1 << 15) + +/* ALC Control 1 (0xb4) */ +#define RT5651_ALC_P_MASK (0x1 << 15) +#define RT5651_ALC_P_SFT 15 +#define RT5651_ALC_P_DAC (0x0 << 15) +#define RT5651_ALC_P_ADC (0x1 << 15) +#define RT5651_ALC_MASK (0x1 << 14) +#define RT5651_ALC_SFT 14 +#define RT5651_ALC_DIS (0x0 << 14) +#define RT5651_ALC_EN (0x1 << 14) +#define RT5651_ALC_UPD (0x1 << 13) +#define RT5651_ALC_UPD_BIT 13 +#define RT5651_ALC_AR_MASK (0x1f << 8) +#define RT5651_ALC_AR_SFT 8 +#define RT5651_ALC_R_MASK (0x7 << 5) +#define RT5651_ALC_R_SFT 5 +#define RT5651_ALC_R_48K (0x1 << 5) +#define RT5651_ALC_R_96K (0x2 << 5) +#define RT5651_ALC_R_192K (0x3 << 5) +#define RT5651_ALC_R_441K (0x5 << 5) +#define RT5651_ALC_R_882K (0x6 << 5) +#define RT5651_ALC_R_1764K (0x7 << 5) +#define RT5651_ALC_RC_MASK (0x1f) +#define RT5651_ALC_RC_SFT 0 + +/* ALC Control 2 (0xb5) */ +#define RT5651_ALC_POB_MASK (0x3f << 8) +#define RT5651_ALC_POB_SFT 8 +#define RT5651_ALC_DRC_MASK (0x1 << 7) +#define RT5651_ALC_DRC_SFT 7 +#define RT5651_ALC_DRC_DIS (0x0 << 7) +#define RT5651_ALC_DRC_EN (0x1 << 7) +#define RT5651_ALC_CPR_MASK (0x3 << 5) +#define RT5651_ALC_CPR_SFT 5 +#define RT5651_ALC_CPR_1_1 (0x0 << 5) +#define RT5651_ALC_CPR_1_2 (0x1 << 5) +#define RT5651_ALC_CPR_1_4 (0x2 << 5) +#define RT5651_ALC_CPR_1_8 (0x3 << 5) +#define RT5651_ALC_PRB_MASK (0x1f) +#define RT5651_ALC_PRB_SFT 0 + +/* ALC Control 3 (0xb6) */ +#define RT5651_ALC_NGB_MASK (0xf << 12) +#define RT5651_ALC_NGB_SFT 12 +#define RT5651_ALC_TAR_MASK (0x1f << 7) +#define RT5651_ALC_TAR_SFT 7 +#define RT5651_ALC_NG_MASK (0x1 << 6) +#define RT5651_ALC_NG_SFT 6 +#define RT5651_ALC_NG_DIS (0x0 << 6) +#define RT5651_ALC_NG_EN (0x1 << 6) +#define RT5651_ALC_NGH_MASK (0x1 << 5) +#define RT5651_ALC_NGH_SFT 5 +#define RT5651_ALC_NGH_DIS (0x0 << 5) +#define RT5651_ALC_NGH_EN (0x1 << 5) +#define RT5651_ALC_NGT_MASK (0x1f) +#define RT5651_ALC_NGT_SFT 0 + +/* Jack Detect Control 1 (0xbb) */ +#define RT5651_JD_MASK (0x7 << 13) +#define RT5651_JD_SFT 13 +#define RT5651_JD_DIS (0x0 << 13) +#define RT5651_JD_GPIO1 (0x1 << 13) +#define RT5651_JD_GPIO2 (0x2 << 13) +#define RT5651_JD_GPIO3 (0x3 << 13) +#define RT5651_JD_GPIO4 (0x4 << 13) +#define RT5651_JD_GPIO5 (0x5 << 13) +#define RT5651_JD_GPIO6 (0x6 << 13) +#define RT5651_JD_HP_MASK (0x1 << 11) +#define RT5651_JD_HP_SFT 11 +#define RT5651_JD_HP_DIS (0x0 << 11) +#define RT5651_JD_HP_EN (0x1 << 11) +#define RT5651_JD_HP_TRG_MASK (0x1 << 10) +#define RT5651_JD_HP_TRG_SFT 10 +#define RT5651_JD_HP_TRG_LO (0x0 << 10) +#define RT5651_JD_HP_TRG_HI (0x1 << 10) +#define RT5651_JD_SPL_MASK (0x1 << 9) +#define RT5651_JD_SPL_SFT 9 +#define RT5651_JD_SPL_DIS (0x0 << 9) +#define RT5651_JD_SPL_EN (0x1 << 9) +#define RT5651_JD_SPL_TRG_MASK (0x1 << 8) +#define RT5651_JD_SPL_TRG_SFT 8 +#define RT5651_JD_SPL_TRG_LO (0x0 << 8) +#define RT5651_JD_SPL_TRG_HI (0x1 << 8) +#define RT5651_JD_SPR_MASK (0x1 << 7) +#define RT5651_JD_SPR_SFT 7 +#define RT5651_JD_SPR_DIS (0x0 << 7) +#define RT5651_JD_SPR_EN (0x1 << 7) +#define RT5651_JD_SPR_TRG_MASK (0x1 << 6) +#define RT5651_JD_SPR_TRG_SFT 6 +#define RT5651_JD_SPR_TRG_LO (0x0 << 6) +#define RT5651_JD_SPR_TRG_HI (0x1 << 6) +#define RT5651_JD_LO_MASK (0x1 << 3) +#define RT5651_JD_LO_SFT 3 +#define RT5651_JD_LO_DIS (0x0 << 3) +#define RT5651_JD_LO_EN (0x1 << 3) +#define RT5651_JD_LO_TRG_MASK (0x1 << 2) +#define RT5651_JD_LO_TRG_SFT 2 +#define RT5651_JD_LO_TRG_LO (0x0 << 2) +#define RT5651_JD_LO_TRG_HI (0x1 << 2) + +/* Jack Detect Control 2 (0xbc) */ +#define RT5651_JD_TRG_SEL_MASK (0x7 << 9) +#define RT5651_JD_TRG_SEL_SFT 9 +#define RT5651_JD_TRG_SEL_GPIO (0x0 << 9) +#define RT5651_JD_TRG_SEL_JD1_1 (0x1 << 9) +#define RT5651_JD_TRG_SEL_JD1_2 (0x2 << 9) +#define RT5651_JD_TRG_SEL_JD2 (0x3 << 9) +#define RT5651_JD_TRG_SEL_JD3 (0x4 << 9) +#define RT5651_JD3_IRQ_EN (0x1 << 8) +#define RT5651_JD3_IRQ_EN_SFT 8 +#define RT5651_JD3_EN_STKY (0x1 << 7) +#define RT5651_JD3_EN_STKY_SFT 7 +#define RT5651_JD3_INV (0x1 << 6) +#define RT5651_JD3_INV_SFT 6 + +/* IRQ Control 1 (0xbd) */ +#define RT5651_IRQ_JD_MASK (0x1 << 15) +#define RT5651_IRQ_JD_SFT 15 +#define RT5651_IRQ_JD_BP (0x0 << 15) +#define RT5651_IRQ_JD_NOR (0x1 << 15) +#define RT5651_JD_STKY_MASK (0x1 << 13) +#define RT5651_JD_STKY_SFT 13 +#define RT5651_JD_STKY_DIS (0x0 << 13) +#define RT5651_JD_STKY_EN (0x1 << 13) +#define RT5651_JD_P_MASK (0x1 << 11) +#define RT5651_JD_P_SFT 11 +#define RT5651_JD_P_NOR (0x0 << 11) +#define RT5651_JD_P_INV (0x1 << 11) +#define RT5651_JD1_1_IRQ_EN (0x1 << 9) +#define RT5651_JD1_1_IRQ_EN_SFT 9 +#define RT5651_JD1_1_EN_STKY (0x1 << 8) +#define RT5651_JD1_1_EN_STKY_SFT 8 +#define RT5651_JD1_1_INV (0x1 << 7) +#define RT5651_JD1_1_INV_SFT 7 +#define RT5651_JD1_2_IRQ_EN (0x1 << 6) +#define RT5651_JD1_2_IRQ_EN_SFT 6 +#define RT5651_JD1_2_EN_STKY (0x1 << 5) +#define RT5651_JD1_2_EN_STKY_SFT 5 +#define RT5651_JD1_2_INV (0x1 << 4) +#define RT5651_JD1_2_INV_SFT 4 +#define RT5651_JD2_IRQ_EN (0x1 << 3) +#define RT5651_JD2_IRQ_EN_SFT 3 +#define RT5651_JD2_EN_STKY (0x1 << 2) +#define RT5651_JD2_EN_STKY_SFT 2 +#define RT5651_JD2_INV (0x1 << 1) +#define RT5651_JD2_INV_SFT 1 + +/* IRQ Control 2 (0xbe) */ +#define RT5651_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5651_IRQ_MB1_OC_SFT 15 +#define RT5651_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5651_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5651_MB1_OC_STKY_MASK (0x1 << 11) +#define RT5651_MB1_OC_STKY_SFT 11 +#define RT5651_MB1_OC_STKY_DIS (0x0 << 11) +#define RT5651_MB1_OC_STKY_EN (0x1 << 11) +#define RT5651_MB1_OC_P_MASK (0x1 << 7) +#define RT5651_MB1_OC_P_SFT 7 +#define RT5651_MB1_OC_P_NOR (0x0 << 7) +#define RT5651_MB1_OC_P_INV (0x1 << 7) +#define RT5651_MB2_OC_P_MASK (0x1 << 6) +#define RT5651_MB1_OC_CLR (0x1 << 3) +#define RT5651_MB1_OC_CLR_SFT 3 +#define RT5651_STA_GPIO8 (0x1) +#define RT5651_STA_GPIO8_BIT 0 + +/* Internal Status and GPIO status (0xbf) */ +#define RT5651_STA_JD3 (0x1 << 15) +#define RT5651_STA_JD3_BIT 15 +#define RT5651_STA_JD2 (0x1 << 14) +#define RT5651_STA_JD2_BIT 14 +#define RT5651_STA_JD1_2 (0x1 << 13) +#define RT5651_STA_JD1_2_BIT 13 +#define RT5651_STA_JD1_1 (0x1 << 12) +#define RT5651_STA_JD1_1_BIT 12 +#define RT5651_STA_GP7 (0x1 << 11) +#define RT5651_STA_GP7_BIT 11 +#define RT5651_STA_GP6 (0x1 << 10) +#define RT5651_STA_GP6_BIT 10 +#define RT5651_STA_GP5 (0x1 << 9) +#define RT5651_STA_GP5_BIT 9 +#define RT5651_STA_GP1 (0x1 << 8) +#define RT5651_STA_GP1_BIT 8 +#define RT5651_STA_GP2 (0x1 << 7) +#define RT5651_STA_GP2_BIT 7 +#define RT5651_STA_GP3 (0x1 << 6) +#define RT5651_STA_GP3_BIT 6 +#define RT5651_STA_GP4 (0x1 << 5) +#define RT5651_STA_GP4_BIT 5 +#define RT5651_STA_GP_JD (0x1 << 4) +#define RT5651_STA_GP_JD_BIT 4 + +/* GPIO Control 1 (0xc0) */ +#define RT5651_GP1_PIN_MASK (0x1 << 15) +#define RT5651_GP1_PIN_SFT 15 +#define RT5651_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5651_GP1_PIN_IRQ (0x1 << 15) +#define RT5651_GP2_PIN_MASK (0x1 << 14) +#define RT5651_GP2_PIN_SFT 14 +#define RT5651_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5651_GP2_PIN_DMIC1_SCL (0x1 << 14) +#define RT5651_GPIO_M_MASK (0x1 << 9) +#define RT5651_GPIO_M_SFT 9 +#define RT5651_GPIO_M_FLT (0x0 << 9) +#define RT5651_GPIO_M_PH (0x1 << 9) +#define RT5651_I2S2_SEL_MASK (0x1 << 8) +#define RT5651_I2S2_SEL_SFT 8 +#define RT5651_I2S2_SEL_I2S (0x0 << 8) +#define RT5651_I2S2_SEL_GPIO (0x1 << 8) +#define RT5651_GP5_PIN_MASK (0x1 << 7) +#define RT5651_GP5_PIN_SFT 7 +#define RT5651_GP5_PIN_GPIO5 (0x0 << 7) +#define RT5651_GP5_PIN_IRQ (0x1 << 7) +#define RT5651_GP6_PIN_MASK (0x1 << 6) +#define RT5651_GP6_PIN_SFT 6 +#define RT5651_GP6_PIN_GPIO6 (0x0 << 6) +#define RT5651_GP6_PIN_DMIC_SDA (0x1 << 6) +#define RT5651_GP7_PIN_MASK (0x1 << 5) +#define RT5651_GP7_PIN_SFT 5 +#define RT5651_GP7_PIN_GPIO7 (0x0 << 5) +#define RT5651_GP7_PIN_IRQ (0x1 << 5) +#define RT5651_GP8_PIN_MASK (0x1 << 4) +#define RT5651_GP8_PIN_SFT 4 +#define RT5651_GP8_PIN_GPIO8 (0x0 << 4) +#define RT5651_GP8_PIN_DMIC_SDA (0x1 << 4) +#define RT5651_GPIO_PDM_SEL_MASK (0x1 << 3) +#define RT5651_GPIO_PDM_SEL_SFT 3 +#define RT5651_GPIO_PDM_SEL_GPIO (0x0 << 3) +#define RT5651_GPIO_PDM_SEL_PDM (0x1 << 3) + +/* GPIO Control 2 (0xc1) */ +#define RT5651_GP5_DR_MASK (0x1 << 14) +#define RT5651_GP5_DR_SFT 14 +#define RT5651_GP5_DR_IN (0x0 << 14) +#define RT5651_GP5_DR_OUT (0x1 << 14) +#define RT5651_GP5_OUT_MASK (0x1 << 13) +#define RT5651_GP5_OUT_SFT 13 +#define RT5651_GP5_OUT_LO (0x0 << 13) +#define RT5651_GP5_OUT_HI (0x1 << 13) +#define RT5651_GP5_P_MASK (0x1 << 12) +#define RT5651_GP5_P_SFT 12 +#define RT5651_GP5_P_NOR (0x0 << 12) +#define RT5651_GP5_P_INV (0x1 << 12) +#define RT5651_GP4_DR_MASK (0x1 << 11) +#define RT5651_GP4_DR_SFT 11 +#define RT5651_GP4_DR_IN (0x0 << 11) +#define RT5651_GP4_DR_OUT (0x1 << 11) +#define RT5651_GP4_OUT_MASK (0x1 << 10) +#define RT5651_GP4_OUT_SFT 10 +#define RT5651_GP4_OUT_LO (0x0 << 10) +#define RT5651_GP4_OUT_HI (0x1 << 10) +#define RT5651_GP4_P_MASK (0x1 << 9) +#define RT5651_GP4_P_SFT 9 +#define RT5651_GP4_P_NOR (0x0 << 9) +#define RT5651_GP4_P_INV (0x1 << 9) +#define RT5651_GP3_DR_MASK (0x1 << 8) +#define RT5651_GP3_DR_SFT 8 +#define RT5651_GP3_DR_IN (0x0 << 8) +#define RT5651_GP3_DR_OUT (0x1 << 8) +#define RT5651_GP3_OUT_MASK (0x1 << 7) +#define RT5651_GP3_OUT_SFT 7 +#define RT5651_GP3_OUT_LO (0x0 << 7) +#define RT5651_GP3_OUT_HI (0x1 << 7) +#define RT5651_GP3_P_MASK (0x1 << 6) +#define RT5651_GP3_P_SFT 6 +#define RT5651_GP3_P_NOR (0x0 << 6) +#define RT5651_GP3_P_INV (0x1 << 6) +#define RT5651_GP2_DR_MASK (0x1 << 5) +#define RT5651_GP2_DR_SFT 5 +#define RT5651_GP2_DR_IN (0x0 << 5) +#define RT5651_GP2_DR_OUT (0x1 << 5) +#define RT5651_GP2_OUT_MASK (0x1 << 4) +#define RT5651_GP2_OUT_SFT 4 +#define RT5651_GP2_OUT_LO (0x0 << 4) +#define RT5651_GP2_OUT_HI (0x1 << 4) +#define RT5651_GP2_P_MASK (0x1 << 3) +#define RT5651_GP2_P_SFT 3 +#define RT5651_GP2_P_NOR (0x0 << 3) +#define RT5651_GP2_P_INV (0x1 << 3) +#define RT5651_GP1_DR_MASK (0x1 << 2) +#define RT5651_GP1_DR_SFT 2 +#define RT5651_GP1_DR_IN (0x0 << 2) +#define RT5651_GP1_DR_OUT (0x1 << 2) +#define RT5651_GP1_OUT_MASK (0x1 << 1) +#define RT5651_GP1_OUT_SFT 1 +#define RT5651_GP1_OUT_LO (0x0 << 1) +#define RT5651_GP1_OUT_HI (0x1 << 1) +#define RT5651_GP1_P_MASK (0x1) +#define RT5651_GP1_P_SFT 0 +#define RT5651_GP1_P_NOR (0x0) +#define RT5651_GP1_P_INV (0x1) + +/* GPIO Control 3 (0xc2) */ +#define RT5651_GP8_DR_MASK (0x1 << 8) +#define RT5651_GP8_DR_SFT 8 +#define RT5651_GP8_DR_IN (0x0 << 8) +#define RT5651_GP8_DR_OUT (0x1 << 8) +#define RT5651_GP8_OUT_MASK (0x1 << 7) +#define RT5651_GP8_OUT_SFT 7 +#define RT5651_GP8_OUT_LO (0x0 << 7) +#define RT5651_GP8_OUT_HI (0x1 << 7) +#define RT5651_GP8_P_MASK (0x1 << 6) +#define RT5651_GP8_P_SFT 6 +#define RT5651_GP8_P_NOR (0x0 << 6) +#define RT5651_GP8_P_INV (0x1 << 6) +#define RT5651_GP7_DR_MASK (0x1 << 5) +#define RT5651_GP7_DR_SFT 5 +#define RT5651_GP7_DR_IN (0x0 << 5) +#define RT5651_GP7_DR_OUT (0x1 << 5) +#define RT5651_GP7_OUT_MASK (0x1 << 4) +#define RT5651_GP7_OUT_SFT 4 +#define RT5651_GP7_OUT_LO (0x0 << 4) +#define RT5651_GP7_OUT_HI (0x1 << 4) +#define RT5651_GP7_P_MASK (0x1 << 3) +#define RT5651_GP7_P_SFT 3 +#define RT5651_GP7_P_NOR (0x0 << 3) +#define RT5651_GP7_P_INV (0x1 << 3) +#define RT5651_GP6_DR_MASK (0x1 << 2) +#define RT5651_GP6_DR_SFT 2 +#define RT5651_GP6_DR_IN (0x0 << 2) +#define RT5651_GP6_DR_OUT (0x1 << 2) +#define RT5651_GP6_OUT_MASK (0x1 << 1) +#define RT5651_GP6_OUT_SFT 1 +#define RT5651_GP6_OUT_LO (0x0 << 1) +#define RT5651_GP6_OUT_HI (0x1 << 1) +#define RT5651_GP6_P_MASK (0x1) +#define RT5651_GP6_P_SFT 0 +#define RT5651_GP6_P_NOR (0x0) +#define RT5651_GP6_P_INV (0x1) + +/* Scramble Control (0xce) */ +#define RT5651_SCB_SWAP_MASK (0x1 << 15) +#define RT5651_SCB_SWAP_SFT 15 +#define RT5651_SCB_SWAP_DIS (0x0 << 15) +#define RT5651_SCB_SWAP_EN (0x1 << 15) +#define RT5651_SCB_MASK (0x1 << 14) +#define RT5651_SCB_SFT 14 +#define RT5651_SCB_DIS (0x0 << 14) +#define RT5651_SCB_EN (0x1 << 14) + +/* Baseback Control (0xcf) */ +#define RT5651_BB_MASK (0x1 << 15) +#define RT5651_BB_SFT 15 +#define RT5651_BB_DIS (0x0 << 15) +#define RT5651_BB_EN (0x1 << 15) +#define RT5651_BB_CT_MASK (0x7 << 12) +#define RT5651_BB_CT_SFT 12 +#define RT5651_BB_CT_A (0x0 << 12) +#define RT5651_BB_CT_B (0x1 << 12) +#define RT5651_BB_CT_C (0x2 << 12) +#define RT5651_BB_CT_D (0x3 << 12) +#define RT5651_M_BB_L_MASK (0x1 << 9) +#define RT5651_M_BB_L_SFT 9 +#define RT5651_M_BB_R_MASK (0x1 << 8) +#define RT5651_M_BB_R_SFT 8 +#define RT5651_M_BB_HPF_L_MASK (0x1 << 7) +#define RT5651_M_BB_HPF_L_SFT 7 +#define RT5651_M_BB_HPF_R_MASK (0x1 << 6) +#define RT5651_M_BB_HPF_R_SFT 6 +#define RT5651_G_BB_BST_MASK (0x3f) +#define RT5651_G_BB_BST_SFT 0 + +/* MP3 Plus Control 1 (0xd0) */ +#define RT5651_M_MP3_L_MASK (0x1 << 15) +#define RT5651_M_MP3_L_SFT 15 +#define RT5651_M_MP3_R_MASK (0x1 << 14) +#define RT5651_M_MP3_R_SFT 14 +#define RT5651_M_MP3_MASK (0x1 << 13) +#define RT5651_M_MP3_SFT 13 +#define RT5651_M_MP3_DIS (0x0 << 13) +#define RT5651_M_MP3_EN (0x1 << 13) +#define RT5651_EG_MP3_MASK (0x1f << 8) +#define RT5651_EG_MP3_SFT 8 +#define RT5651_MP3_HLP_MASK (0x1 << 7) +#define RT5651_MP3_HLP_SFT 7 +#define RT5651_MP3_HLP_DIS (0x0 << 7) +#define RT5651_MP3_HLP_EN (0x1 << 7) +#define RT5651_M_MP3_ORG_L_MASK (0x1 << 6) +#define RT5651_M_MP3_ORG_L_SFT 6 +#define RT5651_M_MP3_ORG_R_MASK (0x1 << 5) +#define RT5651_M_MP3_ORG_R_SFT 5 + +/* MP3 Plus Control 2 (0xd1) */ +#define RT5651_MP3_WT_MASK (0x1 << 13) +#define RT5651_MP3_WT_SFT 13 +#define RT5651_MP3_WT_1_4 (0x0 << 13) +#define RT5651_MP3_WT_1_2 (0x1 << 13) +#define RT5651_OG_MP3_MASK (0x1f << 8) +#define RT5651_OG_MP3_SFT 8 +#define RT5651_HG_MP3_MASK (0x3f) +#define RT5651_HG_MP3_SFT 0 + +/* 3D HP Control 1 (0xd2) */ +#define RT5651_3D_CF_MASK (0x1 << 15) +#define RT5651_3D_CF_SFT 15 +#define RT5651_3D_CF_DIS (0x0 << 15) +#define RT5651_3D_CF_EN (0x1 << 15) +#define RT5651_3D_HP_MASK (0x1 << 14) +#define RT5651_3D_HP_SFT 14 +#define RT5651_3D_HP_DIS (0x0 << 14) +#define RT5651_3D_HP_EN (0x1 << 14) +#define RT5651_3D_BT_MASK (0x1 << 13) +#define RT5651_3D_BT_SFT 13 +#define RT5651_3D_BT_DIS (0x0 << 13) +#define RT5651_3D_BT_EN (0x1 << 13) +#define RT5651_3D_1F_MIX_MASK (0x3 << 11) +#define RT5651_3D_1F_MIX_SFT 11 +#define RT5651_3D_HP_M_MASK (0x1 << 10) +#define RT5651_3D_HP_M_SFT 10 +#define RT5651_3D_HP_M_SUR (0x0 << 10) +#define RT5651_3D_HP_M_FRO (0x1 << 10) +#define RT5651_M_3D_HRTF_MASK (0x1 << 9) +#define RT5651_M_3D_HRTF_SFT 9 +#define RT5651_M_3D_D2H_MASK (0x1 << 8) +#define RT5651_M_3D_D2H_SFT 8 +#define RT5651_M_3D_D2R_MASK (0x1 << 7) +#define RT5651_M_3D_D2R_SFT 7 +#define RT5651_M_3D_REVB_MASK (0x1 << 6) +#define RT5651_M_3D_REVB_SFT 6 + +/* Adjustable high pass filter control 1 (0xd3) */ +#define RT5651_2ND_HPF_MASK (0x1 << 15) +#define RT5651_2ND_HPF_SFT 15 +#define RT5651_2ND_HPF_DIS (0x0 << 15) +#define RT5651_2ND_HPF_EN (0x1 << 15) +#define RT5651_HPF_CF_L_MASK (0x7 << 12) +#define RT5651_HPF_CF_L_SFT 12 +#define RT5651_HPF_CF_R_MASK (0x7 << 8) +#define RT5651_HPF_CF_R_SFT 8 +#define RT5651_ZD_T_MASK (0x3 << 6) +#define RT5651_ZD_T_SFT 6 +#define RT5651_ZD_F_MASK (0x3 << 4) +#define RT5651_ZD_F_SFT 4 +#define RT5651_ZD_F_IM (0x0 << 4) +#define RT5651_ZD_F_ZC_IM (0x1 << 4) +#define RT5651_ZD_F_ZC_IOD (0x2 << 4) +#define RT5651_ZD_F_UN (0x3 << 4) + +/* Adjustable high pass filter control 2 (0xd4) */ +#define RT5651_HPF_CF_L_NUM_MASK (0x3f << 8) +#define RT5651_HPF_CF_L_NUM_SFT 8 +#define RT5651_HPF_CF_R_NUM_MASK (0x3f) +#define RT5651_HPF_CF_R_NUM_SFT 0 + +/* HP calibration control and Amp detection (0xd6) */ +#define RT5651_SI_DAC_MASK (0x1 << 11) +#define RT5651_SI_DAC_SFT 11 +#define RT5651_SI_DAC_AUTO (0x0 << 11) +#define RT5651_SI_DAC_TEST (0x1 << 11) +#define RT5651_DC_CAL_M_MASK (0x1 << 10) +#define RT5651_DC_CAL_M_SFT 10 +#define RT5651_DC_CAL_M_NOR (0x0 << 10) +#define RT5651_DC_CAL_M_CAL (0x1 << 10) +#define RT5651_DC_CAL_MASK (0x1 << 9) +#define RT5651_DC_CAL_SFT 9 +#define RT5651_DC_CAL_DIS (0x0 << 9) +#define RT5651_DC_CAL_EN (0x1 << 9) +#define RT5651_HPD_RCV_MASK (0x7 << 6) +#define RT5651_HPD_RCV_SFT 6 +#define RT5651_HPD_PS_MASK (0x1 << 5) +#define RT5651_HPD_PS_SFT 5 +#define RT5651_HPD_PS_DIS (0x0 << 5) +#define RT5651_HPD_PS_EN (0x1 << 5) +#define RT5651_CAL_M_MASK (0x1 << 4) +#define RT5651_CAL_M_SFT 4 +#define RT5651_CAL_M_DEP (0x0 << 4) +#define RT5651_CAL_M_CAL (0x1 << 4) +#define RT5651_CAL_MASK (0x1 << 3) +#define RT5651_CAL_SFT 3 +#define RT5651_CAL_DIS (0x0 << 3) +#define RT5651_CAL_EN (0x1 << 3) +#define RT5651_CAL_TEST_MASK (0x1 << 2) +#define RT5651_CAL_TEST_SFT 2 +#define RT5651_CAL_TEST_DIS (0x0 << 2) +#define RT5651_CAL_TEST_EN (0x1 << 2) +#define RT5651_CAL_P_MASK (0x3) +#define RT5651_CAL_P_SFT 0 +#define RT5651_CAL_P_NONE (0x0) +#define RT5651_CAL_P_CAL (0x1) +#define RT5651_CAL_P_DAC_CAL (0x2) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5651_SV_MASK (0x1 << 15) +#define RT5651_SV_SFT 15 +#define RT5651_SV_DIS (0x0 << 15) +#define RT5651_SV_EN (0x1 << 15) +#define RT5651_OUT_SV_MASK (0x1 << 13) +#define RT5651_OUT_SV_SFT 13 +#define RT5651_OUT_SV_DIS (0x0 << 13) +#define RT5651_OUT_SV_EN (0x1 << 13) +#define RT5651_HP_SV_MASK (0x1 << 12) +#define RT5651_HP_SV_SFT 12 +#define RT5651_HP_SV_DIS (0x0 << 12) +#define RT5651_HP_SV_EN (0x1 << 12) +#define RT5651_ZCD_DIG_MASK (0x1 << 11) +#define RT5651_ZCD_DIG_SFT 11 +#define RT5651_ZCD_DIG_DIS (0x0 << 11) +#define RT5651_ZCD_DIG_EN (0x1 << 11) +#define RT5651_ZCD_MASK (0x1 << 10) +#define RT5651_ZCD_SFT 10 +#define RT5651_ZCD_PD (0x0 << 10) +#define RT5651_ZCD_PU (0x1 << 10) +#define RT5651_M_ZCD_MASK (0x3f << 4) +#define RT5651_M_ZCD_SFT 4 +#define RT5651_M_ZCD_OM_L (0x1 << 7) +#define RT5651_M_ZCD_OM_R (0x1 << 6) +#define RT5651_M_ZCD_RM_L (0x1 << 5) +#define RT5651_M_ZCD_RM_R (0x1 << 4) +#define RT5651_SV_DLY_MASK (0xf) +#define RT5651_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5651_ZCD_HP_MASK (0x1 << 15) +#define RT5651_ZCD_HP_SFT 15 +#define RT5651_ZCD_HP_DIS (0x0 << 15) +#define RT5651_ZCD_HP_EN (0x1 << 15) + +/* Digital Misc Control (0xfa) */ +#define RT5651_I2S2_MS_SP_MASK (0x1 << 8) +#define RT5651_I2S2_MS_SP_SEL 8 +#define RT5651_I2S2_MS_SP_64 (0x0 << 8) +#define RT5651_I2S2_MS_SP_50 (0x1 << 8) +#define RT5651_CLK_DET_EN (0x1 << 3) +#define RT5651_CLK_DET_EN_SFT 3 +#define RT5651_AMP_DET_EN (0x1 << 1) +#define RT5651_AMP_DET_EN_SFT 1 +#define RT5651_D_GATE_EN (0x1) +#define RT5651_D_GATE_EN_SFT 0 + +/* Codec Private Register definition */ + +/* MIC Over current threshold scale factor (0x15) */ +#define RT5651_MIC_OVCD_SF_MASK (0x3 << 8) +#define RT5651_MIC_OVCD_SF_SFT 8 +#define RT5651_MIC_OVCD_SF_0P5 (0x0 << 8) +#define RT5651_MIC_OVCD_SF_0P75 (0x1 << 8) +#define RT5651_MIC_OVCD_SF_1P0 (0x2 << 8) +#define RT5651_MIC_OVCD_SF_1P5 (0x3 << 8) + +/* 3D Speaker Control (0x63) */ +#define RT5651_3D_SPK_MASK (0x1 << 15) +#define RT5651_3D_SPK_SFT 15 +#define RT5651_3D_SPK_DIS (0x0 << 15) +#define RT5651_3D_SPK_EN (0x1 << 15) +#define RT5651_3D_SPK_M_MASK (0x3 << 13) +#define RT5651_3D_SPK_M_SFT 13 +#define RT5651_3D_SPK_CG_MASK (0x1f << 8) +#define RT5651_3D_SPK_CG_SFT 8 +#define RT5651_3D_SPK_SG_MASK (0x1f) +#define RT5651_3D_SPK_SG_SFT 0 + +/* Wind Noise Detection Control 1 (0x6c) */ +#define RT5651_WND_MASK (0x1 << 15) +#define RT5651_WND_SFT 15 +#define RT5651_WND_DIS (0x0 << 15) +#define RT5651_WND_EN (0x1 << 15) + +/* Wind Noise Detection Control 2 (0x6d) */ +#define RT5651_WND_FC_NW_MASK (0x3f << 10) +#define RT5651_WND_FC_NW_SFT 10 +#define RT5651_WND_FC_WK_MASK (0x3f << 4) +#define RT5651_WND_FC_WK_SFT 4 + +/* Wind Noise Detection Control 3 (0x6e) */ +#define RT5651_HPF_FC_MASK (0x3f << 6) +#define RT5651_HPF_FC_SFT 6 +#define RT5651_WND_FC_ST_MASK (0x3f) +#define RT5651_WND_FC_ST_SFT 0 + +/* Wind Noise Detection Control 4 (0x6f) */ +#define RT5651_WND_TH_LO_MASK (0x3ff) +#define RT5651_WND_TH_LO_SFT 0 + +/* Wind Noise Detection Control 5 (0x70) */ +#define RT5651_WND_TH_HI_MASK (0x3ff) +#define RT5651_WND_TH_HI_SFT 0 + +/* Wind Noise Detection Control 8 (0x73) */ +#define RT5651_WND_WIND_MASK (0x1 << 13) /* Read-Only */ +#define RT5651_WND_WIND_SFT 13 +#define RT5651_WND_STRONG_MASK (0x1 << 12) /* Read-Only */ +#define RT5651_WND_STRONG_SFT 12 +enum { + RT5651_NO_WIND, + RT5651_BREEZE, + RT5651_STORM, +}; + +/* Dipole Speaker Interface (0x75) */ +#define RT5651_DP_ATT_MASK (0x3 << 14) +#define RT5651_DP_ATT_SFT 14 +#define RT5651_DP_SPK_MASK (0x1 << 10) +#define RT5651_DP_SPK_SFT 10 +#define RT5651_DP_SPK_DIS (0x0 << 10) +#define RT5651_DP_SPK_EN (0x1 << 10) + +/* EQ Pre Volume Control (0xb3) */ +#define RT5651_EQ_PRE_VOL_MASK (0xffff) +#define RT5651_EQ_PRE_VOL_SFT 0 + +/* EQ Post Volume Control (0xb4) */ +#define RT5651_EQ_PST_VOL_MASK (0xffff) +#define RT5651_EQ_PST_VOL_SFT 0 + +/* System Clock Source */ +enum { + RT5651_SCLK_S_MCLK, + RT5651_SCLK_S_PLL1, + RT5651_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5651_PLL1_S_MCLK, + RT5651_PLL1_S_BCLK1, + RT5651_PLL1_S_BCLK2, +}; + +enum { + RT5651_AIF1, + RT5651_AIF2, + RT5651_AIFS, +}; + +struct rt5651_pll_code { + bool m_bp; /* Indicates bypass m code or not. */ + int m_code; + int n_code; + int k_code; +}; + +struct rt5651_priv { + struct snd_soc_component *component; + struct regmap *regmap; + /* Jack and button detect data */ + struct snd_soc_jack *hp_jack; + struct gpio_desc *gpiod_hp_det; + struct work_struct jack_detect_work; + struct delayed_work bp_work; + bool ovcd_irq_enabled; + bool pressed; + bool press_reported; + int press_count; + int release_count; + int poll_count; + unsigned int jd_src; + bool jd_active_high; + unsigned int ovcd_th; + unsigned int ovcd_sf; + + int irq; + int sysclk; + int sysclk_src; + int lrck[RT5651_AIFS]; + int bclk[RT5651_AIFS]; + int master[RT5651_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int dmic_en; + bool hp_mute; +}; + +#endif /* __RT5651_H__ */ diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c new file mode 100644 index 0000000000..a061028a16 --- /dev/null +++ b/sound/soc/codecs/rt5659.c @@ -0,0 +1,4346 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5659.c -- RT5659/RT5658 ALSA SoC audio codec driver + * + * Copyright 2015 Realtek Semiconductor Corp. + * Author: Bard Liao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5659.h" + +static const struct reg_default rt5659_reg[] = { + { 0x0000, 0x0000 }, + { 0x0001, 0x4848 }, + { 0x0002, 0x8080 }, + { 0x0003, 0xc8c8 }, + { 0x0004, 0xc80a }, + { 0x0005, 0x0000 }, + { 0x0006, 0x0000 }, + { 0x0007, 0x0103 }, + { 0x0008, 0x0080 }, + { 0x0009, 0x0000 }, + { 0x000a, 0x0000 }, + { 0x000c, 0x0000 }, + { 0x000d, 0x0000 }, + { 0x000f, 0x0808 }, + { 0x0010, 0x3080 }, + { 0x0011, 0x4a00 }, + { 0x0012, 0x4e00 }, + { 0x0015, 0x42c1 }, + { 0x0016, 0x0000 }, + { 0x0018, 0x000b }, + { 0x0019, 0xafaf }, + { 0x001a, 0xafaf }, + { 0x001b, 0x0011 }, + { 0x001c, 0x2f2f }, + { 0x001d, 0x2f2f }, + { 0x001e, 0x2f2f }, + { 0x001f, 0x0000 }, + { 0x0020, 0x0000 }, + { 0x0021, 0x0000 }, + { 0x0022, 0x5757 }, + { 0x0023, 0x0039 }, + { 0x0026, 0xc060 }, + { 0x0027, 0xd8d8 }, + { 0x0029, 0x8080 }, + { 0x002a, 0xaaaa }, + { 0x002b, 0xaaaa }, + { 0x002c, 0x00af }, + { 0x002d, 0x0000 }, + { 0x002f, 0x1002 }, + { 0x0031, 0x5000 }, + { 0x0032, 0x0000 }, + { 0x0033, 0x0000 }, + { 0x0034, 0x0000 }, + { 0x0035, 0x0000 }, + { 0x0036, 0x0000 }, + { 0x003a, 0x0000 }, + { 0x003b, 0x0000 }, + { 0x003c, 0x007f }, + { 0x003d, 0x0000 }, + { 0x003e, 0x007f }, + { 0x0040, 0x0808 }, + { 0x0046, 0x001f }, + { 0x0047, 0x001f }, + { 0x0048, 0x0003 }, + { 0x0049, 0xe061 }, + { 0x004a, 0x0000 }, + { 0x004b, 0x031f }, + { 0x004d, 0x0000 }, + { 0x004e, 0x001f }, + { 0x004f, 0x0000 }, + { 0x0050, 0x001f }, + { 0x0052, 0xf000 }, + { 0x0053, 0x0111 }, + { 0x0054, 0x0064 }, + { 0x0055, 0x0080 }, + { 0x0056, 0xef0e }, + { 0x0057, 0xf0f0 }, + { 0x0058, 0xef0e }, + { 0x0059, 0xf0f0 }, + { 0x005a, 0xef0e }, + { 0x005b, 0xf0f0 }, + { 0x005c, 0xf000 }, + { 0x005d, 0x0000 }, + { 0x005e, 0x1f2c }, + { 0x005f, 0x1f2c }, + { 0x0060, 0x2717 }, + { 0x0061, 0x0000 }, + { 0x0062, 0x0000 }, + { 0x0063, 0x003e }, + { 0x0064, 0x0000 }, + { 0x0065, 0x0000 }, + { 0x0066, 0x0000 }, + { 0x0067, 0x0000 }, + { 0x006a, 0x0000 }, + { 0x006b, 0x0000 }, + { 0x006c, 0x0000 }, + { 0x006e, 0x0000 }, + { 0x006f, 0x0000 }, + { 0x0070, 0x8000 }, + { 0x0071, 0x8000 }, + { 0x0072, 0x8000 }, + { 0x0073, 0x1110 }, + { 0x0074, 0xfe00 }, + { 0x0075, 0x2409 }, + { 0x0076, 0x000a }, + { 0x0077, 0x00f0 }, + { 0x0078, 0x0000 }, + { 0x0079, 0x0000 }, + { 0x007a, 0x0123 }, + { 0x007b, 0x8003 }, + { 0x0080, 0x0000 }, + { 0x0081, 0x0000 }, + { 0x0082, 0x0000 }, + { 0x0083, 0x0000 }, + { 0x0084, 0x0000 }, + { 0x0085, 0x0000 }, + { 0x0086, 0x0008 }, + { 0x0087, 0x0000 }, + { 0x0088, 0x0000 }, + { 0x0089, 0x0000 }, + { 0x008a, 0x0000 }, + { 0x008b, 0x0000 }, + { 0x008c, 0x0003 }, + { 0x008e, 0x0000 }, + { 0x008f, 0x1000 }, + { 0x0090, 0x0646 }, + { 0x0091, 0x0c16 }, + { 0x0092, 0x0073 }, + { 0x0093, 0x0000 }, + { 0x0094, 0x0080 }, + { 0x0097, 0x0000 }, + { 0x0098, 0x0000 }, + { 0x0099, 0x0000 }, + { 0x009a, 0x0000 }, + { 0x009b, 0x0000 }, + { 0x009c, 0x007f }, + { 0x009d, 0x0000 }, + { 0x009e, 0x007f }, + { 0x009f, 0x0000 }, + { 0x00a0, 0x0060 }, + { 0x00a1, 0x90a1 }, + { 0x00ae, 0x2000 }, + { 0x00af, 0x0000 }, + { 0x00b0, 0x2000 }, + { 0x00b1, 0x0000 }, + { 0x00b2, 0x0000 }, + { 0x00b6, 0x0000 }, + { 0x00b7, 0x0000 }, + { 0x00b8, 0x0000 }, + { 0x00b9, 0x0000 }, + { 0x00ba, 0x0000 }, + { 0x00bb, 0x0000 }, + { 0x00be, 0x0000 }, + { 0x00bf, 0x0000 }, + { 0x00c0, 0x0000 }, + { 0x00c1, 0x0000 }, + { 0x00c2, 0x0000 }, + { 0x00c3, 0x0000 }, + { 0x00c4, 0x0003 }, + { 0x00c5, 0x0000 }, + { 0x00cb, 0xa02f }, + { 0x00cc, 0x0000 }, + { 0x00cd, 0x0e02 }, + { 0x00d6, 0x0000 }, + { 0x00d7, 0x2244 }, + { 0x00d9, 0x0809 }, + { 0x00da, 0x0000 }, + { 0x00db, 0x0008 }, + { 0x00dc, 0x00c0 }, + { 0x00dd, 0x6724 }, + { 0x00de, 0x3131 }, + { 0x00df, 0x0008 }, + { 0x00e0, 0x4000 }, + { 0x00e1, 0x3131 }, + { 0x00e4, 0x400c }, + { 0x00e5, 0x8031 }, + { 0x00ea, 0xb320 }, + { 0x00eb, 0x0000 }, + { 0x00ec, 0xb300 }, + { 0x00ed, 0x0000 }, + { 0x00f0, 0x0000 }, + { 0x00f1, 0x0202 }, + { 0x00f2, 0x0ddd }, + { 0x00f3, 0x0ddd }, + { 0x00f4, 0x0ddd }, + { 0x00f6, 0x0000 }, + { 0x00f7, 0x0000 }, + { 0x00f8, 0x0000 }, + { 0x00f9, 0x0000 }, + { 0x00fa, 0x8000 }, + { 0x00fb, 0x0000 }, + { 0x00fc, 0x0000 }, + { 0x00fd, 0x0001 }, + { 0x00fe, 0x10ec }, + { 0x00ff, 0x6311 }, + { 0x0100, 0xaaaa }, + { 0x010a, 0xaaaa }, + { 0x010b, 0x00a0 }, + { 0x010c, 0xaeae }, + { 0x010d, 0xaaaa }, + { 0x010e, 0xaaa8 }, + { 0x010f, 0xa0aa }, + { 0x0110, 0xe02a }, + { 0x0111, 0xa702 }, + { 0x0112, 0xaaaa }, + { 0x0113, 0x2800 }, + { 0x0116, 0x0000 }, + { 0x0117, 0x0f00 }, + { 0x011a, 0x0020 }, + { 0x011b, 0x0011 }, + { 0x011c, 0x0150 }, + { 0x011d, 0x0000 }, + { 0x011e, 0x0000 }, + { 0x011f, 0x0000 }, + { 0x0120, 0x0000 }, + { 0x0121, 0x009b }, + { 0x0122, 0x5014 }, + { 0x0123, 0x0421 }, + { 0x0124, 0x7cea }, + { 0x0125, 0x0420 }, + { 0x0126, 0x5550 }, + { 0x0132, 0x0000 }, + { 0x0133, 0x0000 }, + { 0x0137, 0x5055 }, + { 0x0138, 0x3700 }, + { 0x0139, 0x79a1 }, + { 0x013a, 0x2020 }, + { 0x013b, 0x2020 }, + { 0x013c, 0x2005 }, + { 0x013e, 0x1f00 }, + { 0x013f, 0x0000 }, + { 0x0145, 0x0002 }, + { 0x0146, 0x0000 }, + { 0x0147, 0x0000 }, + { 0x0148, 0x0000 }, + { 0x0150, 0x1813 }, + { 0x0151, 0x0690 }, + { 0x0152, 0x1c17 }, + { 0x0153, 0x6883 }, + { 0x0154, 0xd3ce }, + { 0x0155, 0x352d }, + { 0x0156, 0x00eb }, + { 0x0157, 0x3717 }, + { 0x0158, 0x4c6a }, + { 0x0159, 0xe41b }, + { 0x015a, 0x2a13 }, + { 0x015b, 0xb600 }, + { 0x015c, 0xc730 }, + { 0x015d, 0x35d4 }, + { 0x015e, 0x00bf }, + { 0x0160, 0x0ec0 }, + { 0x0161, 0x0020 }, + { 0x0162, 0x0080 }, + { 0x0163, 0x0800 }, + { 0x0164, 0x0000 }, + { 0x0165, 0x0000 }, + { 0x0166, 0x0000 }, + { 0x0167, 0x001f }, + { 0x0170, 0x4e80 }, + { 0x0171, 0x0020 }, + { 0x0172, 0x0080 }, + { 0x0173, 0x0800 }, + { 0x0174, 0x000c }, + { 0x0175, 0x0000 }, + { 0x0190, 0x3300 }, + { 0x0191, 0x2200 }, + { 0x0192, 0x0000 }, + { 0x01b0, 0x4b38 }, + { 0x01b1, 0x0000 }, + { 0x01b2, 0x0000 }, + { 0x01b3, 0x0000 }, + { 0x01c0, 0x0045 }, + { 0x01c1, 0x0540 }, + { 0x01c2, 0x0000 }, + { 0x01c3, 0x0030 }, + { 0x01c7, 0x0000 }, + { 0x01c8, 0x5757 }, + { 0x01c9, 0x5757 }, + { 0x01ca, 0x5757 }, + { 0x01cb, 0x5757 }, + { 0x01cc, 0x5757 }, + { 0x01cd, 0x5757 }, + { 0x01ce, 0x006f }, + { 0x01da, 0x0000 }, + { 0x01db, 0x0000 }, + { 0x01de, 0x7d00 }, + { 0x01df, 0x10c0 }, + { 0x01e0, 0x06a1 }, + { 0x01e1, 0x0000 }, + { 0x01e2, 0x0000 }, + { 0x01e3, 0x0000 }, + { 0x01e4, 0x0001 }, + { 0x01e6, 0x0000 }, + { 0x01e7, 0x0000 }, + { 0x01e8, 0x0000 }, + { 0x01ea, 0x0000 }, + { 0x01eb, 0x0000 }, + { 0x01ec, 0x0000 }, + { 0x01ed, 0x0000 }, + { 0x01ee, 0x0000 }, + { 0x01ef, 0x0000 }, + { 0x01f0, 0x0000 }, + { 0x01f1, 0x0000 }, + { 0x01f2, 0x0000 }, + { 0x01f6, 0x1e04 }, + { 0x01f7, 0x01a1 }, + { 0x01f8, 0x0000 }, + { 0x01f9, 0x0000 }, + { 0x01fa, 0x0002 }, + { 0x01fb, 0x0000 }, + { 0x01fc, 0x0000 }, + { 0x01fd, 0x0000 }, + { 0x01fe, 0x0000 }, + { 0x0200, 0x066c }, + { 0x0201, 0x7fff }, + { 0x0202, 0x7fff }, + { 0x0203, 0x0000 }, + { 0x0204, 0x0000 }, + { 0x0205, 0x0000 }, + { 0x0206, 0x0000 }, + { 0x0207, 0x0000 }, + { 0x0208, 0x0000 }, + { 0x0256, 0x0000 }, + { 0x0257, 0x0000 }, + { 0x0258, 0x0000 }, + { 0x0259, 0x0000 }, + { 0x025a, 0x0000 }, + { 0x025b, 0x3333 }, + { 0x025c, 0x3333 }, + { 0x025d, 0x3333 }, + { 0x025e, 0x0000 }, + { 0x025f, 0x0000 }, + { 0x0260, 0x0000 }, + { 0x0261, 0x0022 }, + { 0x0262, 0x0300 }, + { 0x0265, 0x1e80 }, + { 0x0266, 0x0131 }, + { 0x0267, 0x0003 }, + { 0x0268, 0x0000 }, + { 0x0269, 0x0000 }, + { 0x026a, 0x0000 }, + { 0x026b, 0x0000 }, + { 0x026c, 0x0000 }, + { 0x026d, 0x0000 }, + { 0x026e, 0x0000 }, + { 0x026f, 0x0000 }, + { 0x0270, 0x0000 }, + { 0x0271, 0x0000 }, + { 0x0272, 0x0000 }, + { 0x0273, 0x0000 }, + { 0x0280, 0x0000 }, + { 0x0281, 0x0000 }, + { 0x0282, 0x0418 }, + { 0x0283, 0x7fff }, + { 0x0284, 0x7000 }, + { 0x0290, 0x01d0 }, + { 0x0291, 0x0100 }, + { 0x02fa, 0x0000 }, + { 0x02fb, 0x0000 }, + { 0x02fc, 0x0000 }, + { 0x0300, 0x001f }, + { 0x0301, 0x032c }, + { 0x0302, 0x5f21 }, + { 0x0303, 0x4000 }, + { 0x0304, 0x4000 }, + { 0x0305, 0x0600 }, + { 0x0306, 0x8000 }, + { 0x0307, 0x0700 }, + { 0x0308, 0x001f }, + { 0x0309, 0x032c }, + { 0x030a, 0x5f21 }, + { 0x030b, 0x4000 }, + { 0x030c, 0x4000 }, + { 0x030d, 0x0600 }, + { 0x030e, 0x8000 }, + { 0x030f, 0x0700 }, + { 0x0310, 0x4560 }, + { 0x0311, 0xa4a8 }, + { 0x0312, 0x7418 }, + { 0x0313, 0x0000 }, + { 0x0314, 0x0006 }, + { 0x0315, 0x00ff }, + { 0x0316, 0xc400 }, + { 0x0317, 0x4560 }, + { 0x0318, 0xa4a8 }, + { 0x0319, 0x7418 }, + { 0x031a, 0x0000 }, + { 0x031b, 0x0006 }, + { 0x031c, 0x00ff }, + { 0x031d, 0xc400 }, + { 0x0320, 0x0f20 }, + { 0x0321, 0x8700 }, + { 0x0322, 0x7dc2 }, + { 0x0323, 0xa178 }, + { 0x0324, 0x5383 }, + { 0x0325, 0x7dc2 }, + { 0x0326, 0xa178 }, + { 0x0327, 0x5383 }, + { 0x0328, 0x003e }, + { 0x0329, 0x02c1 }, + { 0x032a, 0xd37d }, + { 0x0330, 0x00a6 }, + { 0x0331, 0x04c3 }, + { 0x0332, 0x27c8 }, + { 0x0333, 0xbf50 }, + { 0x0334, 0x0045 }, + { 0x0335, 0x2007 }, + { 0x0336, 0x7418 }, + { 0x0337, 0x0501 }, + { 0x0338, 0x0000 }, + { 0x0339, 0x0010 }, + { 0x033a, 0x1010 }, + { 0x0340, 0x0800 }, + { 0x0341, 0x0800 }, + { 0x0342, 0x0800 }, + { 0x0343, 0x0800 }, + { 0x0344, 0x0000 }, + { 0x0345, 0x0000 }, + { 0x0346, 0x0000 }, + { 0x0347, 0x0000 }, + { 0x0348, 0x0000 }, + { 0x0349, 0x0000 }, + { 0x034a, 0x0000 }, + { 0x034b, 0x0000 }, + { 0x034c, 0x0000 }, + { 0x034d, 0x0000 }, + { 0x034e, 0x0000 }, + { 0x034f, 0x0000 }, + { 0x0350, 0x0000 }, + { 0x0351, 0x0000 }, + { 0x0352, 0x0000 }, + { 0x0353, 0x0000 }, + { 0x0354, 0x0000 }, + { 0x0355, 0x0000 }, + { 0x0356, 0x0000 }, + { 0x0357, 0x0000 }, + { 0x0358, 0x0000 }, + { 0x0359, 0x0000 }, + { 0x035a, 0x0000 }, + { 0x035b, 0x0000 }, + { 0x035c, 0x0000 }, + { 0x035d, 0x0000 }, + { 0x035e, 0x2000 }, + { 0x035f, 0x0000 }, + { 0x0360, 0x2000 }, + { 0x0361, 0x2000 }, + { 0x0362, 0x0000 }, + { 0x0363, 0x2000 }, + { 0x0364, 0x0200 }, + { 0x0365, 0x0000 }, + { 0x0366, 0x0000 }, + { 0x0367, 0x0000 }, + { 0x0368, 0x0000 }, + { 0x0369, 0x0000 }, + { 0x036a, 0x0000 }, + { 0x036b, 0x0000 }, + { 0x036c, 0x0000 }, + { 0x036d, 0x0000 }, + { 0x036e, 0x0200 }, + { 0x036f, 0x0000 }, + { 0x0370, 0x0000 }, + { 0x0371, 0x0000 }, + { 0x0372, 0x0000 }, + { 0x0373, 0x0000 }, + { 0x0374, 0x0000 }, + { 0x0375, 0x0000 }, + { 0x0376, 0x0000 }, + { 0x0377, 0x0000 }, + { 0x03d0, 0x0000 }, + { 0x03d1, 0x0000 }, + { 0x03d2, 0x0000 }, + { 0x03d3, 0x0000 }, + { 0x03d4, 0x2000 }, + { 0x03d5, 0x2000 }, + { 0x03d6, 0x0000 }, + { 0x03d7, 0x0000 }, + { 0x03d8, 0x2000 }, + { 0x03d9, 0x2000 }, + { 0x03da, 0x2000 }, + { 0x03db, 0x2000 }, + { 0x03dc, 0x0000 }, + { 0x03dd, 0x0000 }, + { 0x03de, 0x0000 }, + { 0x03df, 0x2000 }, + { 0x03e0, 0x0000 }, + { 0x03e1, 0x0000 }, + { 0x03e2, 0x0000 }, + { 0x03e3, 0x0000 }, + { 0x03e4, 0x0000 }, + { 0x03e5, 0x0000 }, + { 0x03e6, 0x0000 }, + { 0x03e7, 0x0000 }, + { 0x03e8, 0x0000 }, + { 0x03e9, 0x0000 }, + { 0x03ea, 0x0000 }, + { 0x03eb, 0x0000 }, + { 0x03ec, 0x0000 }, + { 0x03ed, 0x0000 }, + { 0x03ee, 0x0000 }, + { 0x03ef, 0x0000 }, + { 0x03f0, 0x0800 }, + { 0x03f1, 0x0800 }, + { 0x03f2, 0x0800 }, + { 0x03f3, 0x0800 }, +}; + +static bool rt5659_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5659_RESET: + case RT5659_EJD_CTRL_2: + case RT5659_SILENCE_CTRL: + case RT5659_DAC2_DIG_VOL: + case RT5659_HP_IMP_GAIN_2: + case RT5659_PDM_OUT_CTRL: + case RT5659_PDM_DATA_CTRL_1: + case RT5659_PDM_DATA_CTRL_4: + case RT5659_HAPTIC_GEN_CTRL_1: + case RT5659_HAPTIC_GEN_CTRL_3: + case RT5659_HAPTIC_LPF_CTRL_3: + case RT5659_CLK_DET: + case RT5659_MICBIAS_1: + case RT5659_ASRC_11: + case RT5659_ADC_EQ_CTRL_1: + case RT5659_DAC_EQ_CTRL_1: + case RT5659_INT_ST_1: + case RT5659_INT_ST_2: + case RT5659_GPIO_STA: + case RT5659_SINE_GEN_CTRL_1: + case RT5659_IL_CMD_1: + case RT5659_4BTN_IL_CMD_1: + case RT5659_PSV_IL_CMD_1: + case RT5659_AJD1_CTRL: + case RT5659_AJD2_AJD3_CTRL: + case RT5659_JD_CTRL_3: + case RT5659_VENDOR_ID: + case RT5659_VENDOR_ID_1: + case RT5659_DEVICE_ID: + case RT5659_MEMORY_TEST: + case RT5659_SOFT_RAMP_DEPOP_DAC_CLK_CTRL: + case RT5659_VOL_TEST: + case RT5659_STO_NG2_CTRL_1: + case RT5659_STO_NG2_CTRL_5: + case RT5659_STO_NG2_CTRL_6: + case RT5659_STO_NG2_CTRL_7: + case RT5659_MONO_NG2_CTRL_1: + case RT5659_MONO_NG2_CTRL_5: + case RT5659_MONO_NG2_CTRL_6: + case RT5659_HP_IMP_SENS_CTRL_1: + case RT5659_HP_IMP_SENS_CTRL_3: + case RT5659_HP_IMP_SENS_CTRL_4: + case RT5659_HP_CALIB_CTRL_1: + case RT5659_HP_CALIB_CTRL_9: + case RT5659_HP_CALIB_STA_1: + case RT5659_HP_CALIB_STA_2: + case RT5659_HP_CALIB_STA_3: + case RT5659_HP_CALIB_STA_4: + case RT5659_HP_CALIB_STA_5: + case RT5659_HP_CALIB_STA_6: + case RT5659_HP_CALIB_STA_7: + case RT5659_HP_CALIB_STA_8: + case RT5659_HP_CALIB_STA_9: + case RT5659_MONO_AMP_CALIB_CTRL_1: + case RT5659_MONO_AMP_CALIB_CTRL_3: + case RT5659_MONO_AMP_CALIB_STA_1: + case RT5659_MONO_AMP_CALIB_STA_2: + case RT5659_MONO_AMP_CALIB_STA_3: + case RT5659_MONO_AMP_CALIB_STA_4: + case RT5659_SPK_PWR_LMT_STA_1: + case RT5659_SPK_PWR_LMT_STA_2: + case RT5659_SPK_PWR_LMT_STA_3: + case RT5659_SPK_PWR_LMT_STA_4: + case RT5659_SPK_PWR_LMT_STA_5: + case RT5659_SPK_PWR_LMT_STA_6: + case RT5659_SPK_DC_CAILB_CTRL_1: + case RT5659_SPK_DC_CAILB_STA_1: + case RT5659_SPK_DC_CAILB_STA_2: + case RT5659_SPK_DC_CAILB_STA_3: + case RT5659_SPK_DC_CAILB_STA_4: + case RT5659_SPK_DC_CAILB_STA_5: + case RT5659_SPK_DC_CAILB_STA_6: + case RT5659_SPK_DC_CAILB_STA_7: + case RT5659_SPK_DC_CAILB_STA_8: + case RT5659_SPK_DC_CAILB_STA_9: + case RT5659_SPK_DC_CAILB_STA_10: + case RT5659_SPK_VDD_STA_1: + case RT5659_SPK_VDD_STA_2: + case RT5659_SPK_DC_DET_CTRL_1: + case RT5659_PURE_DC_DET_CTRL_1: + case RT5659_PURE_DC_DET_CTRL_2: + case RT5659_DRC1_PRIV_1: + case RT5659_DRC1_PRIV_4: + case RT5659_DRC1_PRIV_5: + case RT5659_DRC1_PRIV_6: + case RT5659_DRC1_PRIV_7: + case RT5659_DRC2_PRIV_1: + case RT5659_DRC2_PRIV_4: + case RT5659_DRC2_PRIV_5: + case RT5659_DRC2_PRIV_6: + case RT5659_DRC2_PRIV_7: + case RT5659_ALC_PGA_STA_1: + case RT5659_ALC_PGA_STA_2: + case RT5659_ALC_PGA_STA_3: + return true; + default: + return false; + } +} + +static bool rt5659_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5659_RESET: + case RT5659_SPO_VOL: + case RT5659_HP_VOL: + case RT5659_LOUT: + case RT5659_MONO_OUT: + case RT5659_HPL_GAIN: + case RT5659_HPR_GAIN: + case RT5659_MONO_GAIN: + case RT5659_SPDIF_CTRL_1: + case RT5659_SPDIF_CTRL_2: + case RT5659_CAL_BST_CTRL: + case RT5659_IN1_IN2: + case RT5659_IN3_IN4: + case RT5659_INL1_INR1_VOL: + case RT5659_EJD_CTRL_1: + case RT5659_EJD_CTRL_2: + case RT5659_EJD_CTRL_3: + case RT5659_SILENCE_CTRL: + case RT5659_PSV_CTRL: + case RT5659_SIDETONE_CTRL: + case RT5659_DAC1_DIG_VOL: + case RT5659_DAC2_DIG_VOL: + case RT5659_DAC_CTRL: + case RT5659_STO1_ADC_DIG_VOL: + case RT5659_MONO_ADC_DIG_VOL: + case RT5659_STO2_ADC_DIG_VOL: + case RT5659_STO1_BOOST: + case RT5659_MONO_BOOST: + case RT5659_STO2_BOOST: + case RT5659_HP_IMP_GAIN_1: + case RT5659_HP_IMP_GAIN_2: + case RT5659_STO1_ADC_MIXER: + case RT5659_MONO_ADC_MIXER: + case RT5659_AD_DA_MIXER: + case RT5659_STO_DAC_MIXER: + case RT5659_MONO_DAC_MIXER: + case RT5659_DIG_MIXER: + case RT5659_A_DAC_MUX: + case RT5659_DIG_INF23_DATA: + case RT5659_PDM_OUT_CTRL: + case RT5659_PDM_DATA_CTRL_1: + case RT5659_PDM_DATA_CTRL_2: + case RT5659_PDM_DATA_CTRL_3: + case RT5659_PDM_DATA_CTRL_4: + case RT5659_SPDIF_CTRL: + case RT5659_REC1_GAIN: + case RT5659_REC1_L1_MIXER: + case RT5659_REC1_L2_MIXER: + case RT5659_REC1_R1_MIXER: + case RT5659_REC1_R2_MIXER: + case RT5659_CAL_REC: + case RT5659_REC2_L1_MIXER: + case RT5659_REC2_L2_MIXER: + case RT5659_REC2_R1_MIXER: + case RT5659_REC2_R2_MIXER: + case RT5659_SPK_L_MIXER: + case RT5659_SPK_R_MIXER: + case RT5659_SPO_AMP_GAIN: + case RT5659_ALC_BACK_GAIN: + case RT5659_MONOMIX_GAIN: + case RT5659_MONOMIX_IN_GAIN: + case RT5659_OUT_L_GAIN: + case RT5659_OUT_L_MIXER: + case RT5659_OUT_R_GAIN: + case RT5659_OUT_R_MIXER: + case RT5659_LOUT_MIXER: + case RT5659_HAPTIC_GEN_CTRL_1: + case RT5659_HAPTIC_GEN_CTRL_2: + case RT5659_HAPTIC_GEN_CTRL_3: + case RT5659_HAPTIC_GEN_CTRL_4: + case RT5659_HAPTIC_GEN_CTRL_5: + case RT5659_HAPTIC_GEN_CTRL_6: + case RT5659_HAPTIC_GEN_CTRL_7: + case RT5659_HAPTIC_GEN_CTRL_8: + case RT5659_HAPTIC_GEN_CTRL_9: + case RT5659_HAPTIC_GEN_CTRL_10: + case RT5659_HAPTIC_GEN_CTRL_11: + case RT5659_HAPTIC_LPF_CTRL_1: + case RT5659_HAPTIC_LPF_CTRL_2: + case RT5659_HAPTIC_LPF_CTRL_3: + case RT5659_PWR_DIG_1: + case RT5659_PWR_DIG_2: + case RT5659_PWR_ANLG_1: + case RT5659_PWR_ANLG_2: + case RT5659_PWR_ANLG_3: + case RT5659_PWR_MIXER: + case RT5659_PWR_VOL: + case RT5659_PRIV_INDEX: + case RT5659_CLK_DET: + case RT5659_PRIV_DATA: + case RT5659_PRE_DIV_1: + case RT5659_PRE_DIV_2: + case RT5659_I2S1_SDP: + case RT5659_I2S2_SDP: + case RT5659_I2S3_SDP: + case RT5659_ADDA_CLK_1: + case RT5659_ADDA_CLK_2: + case RT5659_DMIC_CTRL_1: + case RT5659_DMIC_CTRL_2: + case RT5659_TDM_CTRL_1: + case RT5659_TDM_CTRL_2: + case RT5659_TDM_CTRL_3: + case RT5659_TDM_CTRL_4: + case RT5659_TDM_CTRL_5: + case RT5659_GLB_CLK: + case RT5659_PLL_CTRL_1: + case RT5659_PLL_CTRL_2: + case RT5659_ASRC_1: + case RT5659_ASRC_2: + case RT5659_ASRC_3: + case RT5659_ASRC_4: + case RT5659_ASRC_5: + case RT5659_ASRC_6: + case RT5659_ASRC_7: + case RT5659_ASRC_8: + case RT5659_ASRC_9: + case RT5659_ASRC_10: + case RT5659_DEPOP_1: + case RT5659_DEPOP_2: + case RT5659_DEPOP_3: + case RT5659_HP_CHARGE_PUMP_1: + case RT5659_HP_CHARGE_PUMP_2: + case RT5659_MICBIAS_1: + case RT5659_MICBIAS_2: + case RT5659_ASRC_11: + case RT5659_ASRC_12: + case RT5659_ASRC_13: + case RT5659_REC_M1_M2_GAIN_CTRL: + case RT5659_RC_CLK_CTRL: + case RT5659_CLASSD_CTRL_1: + case RT5659_CLASSD_CTRL_2: + case RT5659_ADC_EQ_CTRL_1: + case RT5659_ADC_EQ_CTRL_2: + case RT5659_DAC_EQ_CTRL_1: + case RT5659_DAC_EQ_CTRL_2: + case RT5659_DAC_EQ_CTRL_3: + case RT5659_IRQ_CTRL_1: + case RT5659_IRQ_CTRL_2: + case RT5659_IRQ_CTRL_3: + case RT5659_IRQ_CTRL_4: + case RT5659_IRQ_CTRL_5: + case RT5659_IRQ_CTRL_6: + case RT5659_INT_ST_1: + case RT5659_INT_ST_2: + case RT5659_GPIO_CTRL_1: + case RT5659_GPIO_CTRL_2: + case RT5659_GPIO_CTRL_3: + case RT5659_GPIO_CTRL_4: + case RT5659_GPIO_CTRL_5: + case RT5659_GPIO_STA: + case RT5659_SINE_GEN_CTRL_1: + case RT5659_SINE_GEN_CTRL_2: + case RT5659_SINE_GEN_CTRL_3: + case RT5659_HP_AMP_DET_CTRL_1: + case RT5659_HP_AMP_DET_CTRL_2: + case RT5659_SV_ZCD_1: + case RT5659_SV_ZCD_2: + case RT5659_IL_CMD_1: + case RT5659_IL_CMD_2: + case RT5659_IL_CMD_3: + case RT5659_IL_CMD_4: + case RT5659_4BTN_IL_CMD_1: + case RT5659_4BTN_IL_CMD_2: + case RT5659_4BTN_IL_CMD_3: + case RT5659_PSV_IL_CMD_1: + case RT5659_PSV_IL_CMD_2: + case RT5659_ADC_STO1_HP_CTRL_1: + case RT5659_ADC_STO1_HP_CTRL_2: + case RT5659_ADC_MONO_HP_CTRL_1: + case RT5659_ADC_MONO_HP_CTRL_2: + case RT5659_AJD1_CTRL: + case RT5659_AJD2_AJD3_CTRL: + case RT5659_JD1_THD: + case RT5659_JD2_THD: + case RT5659_JD3_THD: + case RT5659_JD_CTRL_1: + case RT5659_JD_CTRL_2: + case RT5659_JD_CTRL_3: + case RT5659_JD_CTRL_4: + case RT5659_DIG_MISC: + case RT5659_DUMMY_2: + case RT5659_DUMMY_3: + case RT5659_VENDOR_ID: + case RT5659_VENDOR_ID_1: + case RT5659_DEVICE_ID: + case RT5659_DAC_ADC_DIG_VOL: + case RT5659_BIAS_CUR_CTRL_1: + case RT5659_BIAS_CUR_CTRL_2: + case RT5659_BIAS_CUR_CTRL_3: + case RT5659_BIAS_CUR_CTRL_4: + case RT5659_BIAS_CUR_CTRL_5: + case RT5659_BIAS_CUR_CTRL_6: + case RT5659_BIAS_CUR_CTRL_7: + case RT5659_BIAS_CUR_CTRL_8: + case RT5659_BIAS_CUR_CTRL_9: + case RT5659_BIAS_CUR_CTRL_10: + case RT5659_MEMORY_TEST: + case RT5659_VREF_REC_OP_FB_CAP_CTRL: + case RT5659_CLASSD_0: + case RT5659_CLASSD_1: + case RT5659_CLASSD_2: + case RT5659_CLASSD_3: + case RT5659_CLASSD_4: + case RT5659_CLASSD_5: + case RT5659_CLASSD_6: + case RT5659_CLASSD_7: + case RT5659_CLASSD_8: + case RT5659_CLASSD_9: + case RT5659_CLASSD_10: + case RT5659_CHARGE_PUMP_1: + case RT5659_CHARGE_PUMP_2: + case RT5659_DIG_IN_CTRL_1: + case RT5659_DIG_IN_CTRL_2: + case RT5659_PAD_DRIVING_CTRL: + case RT5659_SOFT_RAMP_DEPOP: + case RT5659_PLL: + case RT5659_CHOP_DAC: + case RT5659_CHOP_ADC: + case RT5659_CALIB_ADC_CTRL: + case RT5659_SOFT_RAMP_DEPOP_DAC_CLK_CTRL: + case RT5659_VOL_TEST: + case RT5659_TEST_MODE_CTRL_1: + case RT5659_TEST_MODE_CTRL_2: + case RT5659_TEST_MODE_CTRL_3: + case RT5659_TEST_MODE_CTRL_4: + case RT5659_BASSBACK_CTRL: + case RT5659_MP3_PLUS_CTRL_1: + case RT5659_MP3_PLUS_CTRL_2: + case RT5659_MP3_HPF_A1: + case RT5659_MP3_HPF_A2: + case RT5659_MP3_HPF_H0: + case RT5659_MP3_LPF_H0: + case RT5659_3D_SPK_CTRL: + case RT5659_3D_SPK_COEF_1: + case RT5659_3D_SPK_COEF_2: + case RT5659_3D_SPK_COEF_3: + case RT5659_3D_SPK_COEF_4: + case RT5659_3D_SPK_COEF_5: + case RT5659_3D_SPK_COEF_6: + case RT5659_3D_SPK_COEF_7: + case RT5659_STO_NG2_CTRL_1: + case RT5659_STO_NG2_CTRL_2: + case RT5659_STO_NG2_CTRL_3: + case RT5659_STO_NG2_CTRL_4: + case RT5659_STO_NG2_CTRL_5: + case RT5659_STO_NG2_CTRL_6: + case RT5659_STO_NG2_CTRL_7: + case RT5659_STO_NG2_CTRL_8: + case RT5659_MONO_NG2_CTRL_1: + case RT5659_MONO_NG2_CTRL_2: + case RT5659_MONO_NG2_CTRL_3: + case RT5659_MONO_NG2_CTRL_4: + case RT5659_MONO_NG2_CTRL_5: + case RT5659_MONO_NG2_CTRL_6: + case RT5659_MID_HP_AMP_DET: + case RT5659_LOW_HP_AMP_DET: + case RT5659_LDO_CTRL: + case RT5659_HP_DECROSS_CTRL_1: + case RT5659_HP_DECROSS_CTRL_2: + case RT5659_HP_DECROSS_CTRL_3: + case RT5659_HP_DECROSS_CTRL_4: + case RT5659_HP_IMP_SENS_CTRL_1: + case RT5659_HP_IMP_SENS_CTRL_2: + case RT5659_HP_IMP_SENS_CTRL_3: + case RT5659_HP_IMP_SENS_CTRL_4: + case RT5659_HP_IMP_SENS_MAP_1: + case RT5659_HP_IMP_SENS_MAP_2: + case RT5659_HP_IMP_SENS_MAP_3: + case RT5659_HP_IMP_SENS_MAP_4: + case RT5659_HP_IMP_SENS_MAP_5: + case RT5659_HP_IMP_SENS_MAP_6: + case RT5659_HP_IMP_SENS_MAP_7: + case RT5659_HP_IMP_SENS_MAP_8: + case RT5659_HP_LOGIC_CTRL_1: + case RT5659_HP_LOGIC_CTRL_2: + case RT5659_HP_CALIB_CTRL_1: + case RT5659_HP_CALIB_CTRL_2: + case RT5659_HP_CALIB_CTRL_3: + case RT5659_HP_CALIB_CTRL_4: + case RT5659_HP_CALIB_CTRL_5: + case RT5659_HP_CALIB_CTRL_6: + case RT5659_HP_CALIB_CTRL_7: + case RT5659_HP_CALIB_CTRL_9: + case RT5659_HP_CALIB_CTRL_10: + case RT5659_HP_CALIB_CTRL_11: + case RT5659_HP_CALIB_STA_1: + case RT5659_HP_CALIB_STA_2: + case RT5659_HP_CALIB_STA_3: + case RT5659_HP_CALIB_STA_4: + case RT5659_HP_CALIB_STA_5: + case RT5659_HP_CALIB_STA_6: + case RT5659_HP_CALIB_STA_7: + case RT5659_HP_CALIB_STA_8: + case RT5659_HP_CALIB_STA_9: + case RT5659_MONO_AMP_CALIB_CTRL_1: + case RT5659_MONO_AMP_CALIB_CTRL_2: + case RT5659_MONO_AMP_CALIB_CTRL_3: + case RT5659_MONO_AMP_CALIB_CTRL_4: + case RT5659_MONO_AMP_CALIB_CTRL_5: + case RT5659_MONO_AMP_CALIB_STA_1: + case RT5659_MONO_AMP_CALIB_STA_2: + case RT5659_MONO_AMP_CALIB_STA_3: + case RT5659_MONO_AMP_CALIB_STA_4: + case RT5659_SPK_PWR_LMT_CTRL_1: + case RT5659_SPK_PWR_LMT_CTRL_2: + case RT5659_SPK_PWR_LMT_CTRL_3: + case RT5659_SPK_PWR_LMT_STA_1: + case RT5659_SPK_PWR_LMT_STA_2: + case RT5659_SPK_PWR_LMT_STA_3: + case RT5659_SPK_PWR_LMT_STA_4: + case RT5659_SPK_PWR_LMT_STA_5: + case RT5659_SPK_PWR_LMT_STA_6: + case RT5659_FLEX_SPK_BST_CTRL_1: + case RT5659_FLEX_SPK_BST_CTRL_2: + case RT5659_FLEX_SPK_BST_CTRL_3: + case RT5659_FLEX_SPK_BST_CTRL_4: + case RT5659_SPK_EX_LMT_CTRL_1: + case RT5659_SPK_EX_LMT_CTRL_2: + case RT5659_SPK_EX_LMT_CTRL_3: + case RT5659_SPK_EX_LMT_CTRL_4: + case RT5659_SPK_EX_LMT_CTRL_5: + case RT5659_SPK_EX_LMT_CTRL_6: + case RT5659_SPK_EX_LMT_CTRL_7: + case RT5659_ADJ_HPF_CTRL_1: + case RT5659_ADJ_HPF_CTRL_2: + case RT5659_SPK_DC_CAILB_CTRL_1: + case RT5659_SPK_DC_CAILB_CTRL_2: + case RT5659_SPK_DC_CAILB_CTRL_3: + case RT5659_SPK_DC_CAILB_CTRL_4: + case RT5659_SPK_DC_CAILB_CTRL_5: + case RT5659_SPK_DC_CAILB_STA_1: + case RT5659_SPK_DC_CAILB_STA_2: + case RT5659_SPK_DC_CAILB_STA_3: + case RT5659_SPK_DC_CAILB_STA_4: + case RT5659_SPK_DC_CAILB_STA_5: + case RT5659_SPK_DC_CAILB_STA_6: + case RT5659_SPK_DC_CAILB_STA_7: + case RT5659_SPK_DC_CAILB_STA_8: + case RT5659_SPK_DC_CAILB_STA_9: + case RT5659_SPK_DC_CAILB_STA_10: + case RT5659_SPK_VDD_STA_1: + case RT5659_SPK_VDD_STA_2: + case RT5659_SPK_DC_DET_CTRL_1: + case RT5659_SPK_DC_DET_CTRL_2: + case RT5659_SPK_DC_DET_CTRL_3: + case RT5659_PURE_DC_DET_CTRL_1: + case RT5659_PURE_DC_DET_CTRL_2: + case RT5659_DUMMY_4: + case RT5659_DUMMY_5: + case RT5659_DUMMY_6: + case RT5659_DRC1_CTRL_1: + case RT5659_DRC1_CTRL_2: + case RT5659_DRC1_CTRL_3: + case RT5659_DRC1_CTRL_4: + case RT5659_DRC1_CTRL_5: + case RT5659_DRC1_CTRL_6: + case RT5659_DRC1_HARD_LMT_CTRL_1: + case RT5659_DRC1_HARD_LMT_CTRL_2: + case RT5659_DRC2_CTRL_1: + case RT5659_DRC2_CTRL_2: + case RT5659_DRC2_CTRL_3: + case RT5659_DRC2_CTRL_4: + case RT5659_DRC2_CTRL_5: + case RT5659_DRC2_CTRL_6: + case RT5659_DRC2_HARD_LMT_CTRL_1: + case RT5659_DRC2_HARD_LMT_CTRL_2: + case RT5659_DRC1_PRIV_1: + case RT5659_DRC1_PRIV_2: + case RT5659_DRC1_PRIV_3: + case RT5659_DRC1_PRIV_4: + case RT5659_DRC1_PRIV_5: + case RT5659_DRC1_PRIV_6: + case RT5659_DRC1_PRIV_7: + case RT5659_DRC2_PRIV_1: + case RT5659_DRC2_PRIV_2: + case RT5659_DRC2_PRIV_3: + case RT5659_DRC2_PRIV_4: + case RT5659_DRC2_PRIV_5: + case RT5659_DRC2_PRIV_6: + case RT5659_DRC2_PRIV_7: + case RT5659_MULTI_DRC_CTRL: + case RT5659_CROSS_OVER_1: + case RT5659_CROSS_OVER_2: + case RT5659_CROSS_OVER_3: + case RT5659_CROSS_OVER_4: + case RT5659_CROSS_OVER_5: + case RT5659_CROSS_OVER_6: + case RT5659_CROSS_OVER_7: + case RT5659_CROSS_OVER_8: + case RT5659_CROSS_OVER_9: + case RT5659_CROSS_OVER_10: + case RT5659_ALC_PGA_CTRL_1: + case RT5659_ALC_PGA_CTRL_2: + case RT5659_ALC_PGA_CTRL_3: + case RT5659_ALC_PGA_CTRL_4: + case RT5659_ALC_PGA_CTRL_5: + case RT5659_ALC_PGA_CTRL_6: + case RT5659_ALC_PGA_CTRL_7: + case RT5659_ALC_PGA_CTRL_8: + case RT5659_ALC_PGA_STA_1: + case RT5659_ALC_PGA_STA_2: + case RT5659_ALC_PGA_STA_3: + case RT5659_DAC_L_EQ_PRE_VOL: + case RT5659_DAC_R_EQ_PRE_VOL: + case RT5659_DAC_L_EQ_POST_VOL: + case RT5659_DAC_R_EQ_POST_VOL: + case RT5659_DAC_L_EQ_LPF1_A1: + case RT5659_DAC_L_EQ_LPF1_H0: + case RT5659_DAC_R_EQ_LPF1_A1: + case RT5659_DAC_R_EQ_LPF1_H0: + case RT5659_DAC_L_EQ_BPF2_A1: + case RT5659_DAC_L_EQ_BPF2_A2: + case RT5659_DAC_L_EQ_BPF2_H0: + case RT5659_DAC_R_EQ_BPF2_A1: + case RT5659_DAC_R_EQ_BPF2_A2: + case RT5659_DAC_R_EQ_BPF2_H0: + case RT5659_DAC_L_EQ_BPF3_A1: + case RT5659_DAC_L_EQ_BPF3_A2: + case RT5659_DAC_L_EQ_BPF3_H0: + case RT5659_DAC_R_EQ_BPF3_A1: + case RT5659_DAC_R_EQ_BPF3_A2: + case RT5659_DAC_R_EQ_BPF3_H0: + case RT5659_DAC_L_EQ_BPF4_A1: + case RT5659_DAC_L_EQ_BPF4_A2: + case RT5659_DAC_L_EQ_BPF4_H0: + case RT5659_DAC_R_EQ_BPF4_A1: + case RT5659_DAC_R_EQ_BPF4_A2: + case RT5659_DAC_R_EQ_BPF4_H0: + case RT5659_DAC_L_EQ_HPF1_A1: + case RT5659_DAC_L_EQ_HPF1_H0: + case RT5659_DAC_R_EQ_HPF1_A1: + case RT5659_DAC_R_EQ_HPF1_H0: + case RT5659_DAC_L_EQ_HPF2_A1: + case RT5659_DAC_L_EQ_HPF2_A2: + case RT5659_DAC_L_EQ_HPF2_H0: + case RT5659_DAC_R_EQ_HPF2_A1: + case RT5659_DAC_R_EQ_HPF2_A2: + case RT5659_DAC_R_EQ_HPF2_H0: + case RT5659_DAC_L_BI_EQ_BPF1_H0_1: + case RT5659_DAC_L_BI_EQ_BPF1_H0_2: + case RT5659_DAC_L_BI_EQ_BPF1_B1_1: + case RT5659_DAC_L_BI_EQ_BPF1_B1_2: + case RT5659_DAC_L_BI_EQ_BPF1_B2_1: + case RT5659_DAC_L_BI_EQ_BPF1_B2_2: + case RT5659_DAC_L_BI_EQ_BPF1_A1_1: + case RT5659_DAC_L_BI_EQ_BPF1_A1_2: + case RT5659_DAC_L_BI_EQ_BPF1_A2_1: + case RT5659_DAC_L_BI_EQ_BPF1_A2_2: + case RT5659_DAC_R_BI_EQ_BPF1_H0_1: + case RT5659_DAC_R_BI_EQ_BPF1_H0_2: + case RT5659_DAC_R_BI_EQ_BPF1_B1_1: + case RT5659_DAC_R_BI_EQ_BPF1_B1_2: + case RT5659_DAC_R_BI_EQ_BPF1_B2_1: + case RT5659_DAC_R_BI_EQ_BPF1_B2_2: + case RT5659_DAC_R_BI_EQ_BPF1_A1_1: + case RT5659_DAC_R_BI_EQ_BPF1_A1_2: + case RT5659_DAC_R_BI_EQ_BPF1_A2_1: + case RT5659_DAC_R_BI_EQ_BPF1_A2_2: + case RT5659_ADC_L_EQ_LPF1_A1: + case RT5659_ADC_R_EQ_LPF1_A1: + case RT5659_ADC_L_EQ_LPF1_H0: + case RT5659_ADC_R_EQ_LPF1_H0: + case RT5659_ADC_L_EQ_BPF1_A1: + case RT5659_ADC_R_EQ_BPF1_A1: + case RT5659_ADC_L_EQ_BPF1_A2: + case RT5659_ADC_R_EQ_BPF1_A2: + case RT5659_ADC_L_EQ_BPF1_H0: + case RT5659_ADC_R_EQ_BPF1_H0: + case RT5659_ADC_L_EQ_BPF2_A1: + case RT5659_ADC_R_EQ_BPF2_A1: + case RT5659_ADC_L_EQ_BPF2_A2: + case RT5659_ADC_R_EQ_BPF2_A2: + case RT5659_ADC_L_EQ_BPF2_H0: + case RT5659_ADC_R_EQ_BPF2_H0: + case RT5659_ADC_L_EQ_BPF3_A1: + case RT5659_ADC_R_EQ_BPF3_A1: + case RT5659_ADC_L_EQ_BPF3_A2: + case RT5659_ADC_R_EQ_BPF3_A2: + case RT5659_ADC_L_EQ_BPF3_H0: + case RT5659_ADC_R_EQ_BPF3_H0: + case RT5659_ADC_L_EQ_BPF4_A1: + case RT5659_ADC_R_EQ_BPF4_A1: + case RT5659_ADC_L_EQ_BPF4_A2: + case RT5659_ADC_R_EQ_BPF4_A2: + case RT5659_ADC_L_EQ_BPF4_H0: + case RT5659_ADC_R_EQ_BPF4_H0: + case RT5659_ADC_L_EQ_HPF1_A1: + case RT5659_ADC_R_EQ_HPF1_A1: + case RT5659_ADC_L_EQ_HPF1_H0: + case RT5659_ADC_R_EQ_HPF1_H0: + case RT5659_ADC_L_EQ_PRE_VOL: + case RT5659_ADC_R_EQ_PRE_VOL: + case RT5659_ADC_L_EQ_POST_VOL: + case RT5659_ADC_R_EQ_POST_VOL: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2325, 75, 0); +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); +static const DECLARE_TLV_DB_SCALE(in_bst_tlv, -1200, 75, 0); + +/* Interface data select */ +static const char * const rt5659_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5659_if1_01_adc_enum, + RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT01_SFT, rt5659_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5659_if1_23_adc_enum, + RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT23_SFT, rt5659_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5659_if1_45_adc_enum, + RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT45_SFT, rt5659_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5659_if1_67_adc_enum, + RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT67_SFT, rt5659_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5659_if2_dac_enum, + RT5659_DIG_INF23_DATA, RT5659_IF2_DAC_SEL_SFT, rt5659_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5659_if2_adc_enum, + RT5659_DIG_INF23_DATA, RT5659_IF2_ADC_SEL_SFT, rt5659_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5659_if3_dac_enum, + RT5659_DIG_INF23_DATA, RT5659_IF3_DAC_SEL_SFT, rt5659_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5659_if3_adc_enum, + RT5659_DIG_INF23_DATA, RT5659_IF3_ADC_SEL_SFT, rt5659_data_select); + +static const struct snd_kcontrol_new rt5659_if1_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1 01 ADC Swap Source", rt5659_if1_01_adc_enum); + +static const struct snd_kcontrol_new rt5659_if1_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1 23 ADC1 Swap Source", rt5659_if1_23_adc_enum); + +static const struct snd_kcontrol_new rt5659_if1_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1 45 ADC1 Swap Source", rt5659_if1_45_adc_enum); + +static const struct snd_kcontrol_new rt5659_if1_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1 67 ADC1 Swap Source", rt5659_if1_67_adc_enum); + +static const struct snd_kcontrol_new rt5659_if2_dac_swap_mux = + SOC_DAPM_ENUM("IF2 DAC Swap Source", rt5659_if2_dac_enum); + +static const struct snd_kcontrol_new rt5659_if2_adc_swap_mux = + SOC_DAPM_ENUM("IF2 ADC Swap Source", rt5659_if2_adc_enum); + +static const struct snd_kcontrol_new rt5659_if3_dac_swap_mux = + SOC_DAPM_ENUM("IF3 DAC Swap Source", rt5659_if3_dac_enum); + +static const struct snd_kcontrol_new rt5659_if3_adc_swap_mux = + SOC_DAPM_ENUM("IF3 ADC Swap Source", rt5659_if3_adc_enum); + +static int rt5659_hp_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int ret = snd_soc_put_volsw(kcontrol, ucontrol); + + if (snd_soc_component_read(component, RT5659_STO_NG2_CTRL_1) & RT5659_NG2_EN) { + snd_soc_component_update_bits(component, RT5659_STO_NG2_CTRL_1, + RT5659_NG2_EN_MASK, RT5659_NG2_DIS); + snd_soc_component_update_bits(component, RT5659_STO_NG2_CTRL_1, + RT5659_NG2_EN_MASK, RT5659_NG2_EN); + } + + return ret; +} + +static void rt5659_enable_push_button_irq(struct snd_soc_component *component, + bool enable) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + if (enable) { + snd_soc_component_write(component, RT5659_4BTN_IL_CMD_1, 0x000b); + + /* MICBIAS1 and Mic Det Power for button detect*/ + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_force_enable_pin(dapm, + "Mic Det Power"); + snd_soc_dapm_sync(dapm); + + snd_soc_component_update_bits(component, RT5659_PWR_ANLG_2, + RT5659_PWR_MB1, RT5659_PWR_MB1); + snd_soc_component_update_bits(component, RT5659_PWR_VOL, + RT5659_PWR_MIC_DET, RT5659_PWR_MIC_DET); + + snd_soc_component_update_bits(component, RT5659_IRQ_CTRL_2, + RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_EN); + snd_soc_component_update_bits(component, RT5659_4BTN_IL_CMD_2, + RT5659_4BTN_IL_MASK, RT5659_4BTN_IL_EN); + } else { + snd_soc_component_update_bits(component, RT5659_4BTN_IL_CMD_2, + RT5659_4BTN_IL_MASK, RT5659_4BTN_IL_DIS); + snd_soc_component_update_bits(component, RT5659_IRQ_CTRL_2, + RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_DIS); + /* MICBIAS1 and Mic Det Power for button detect*/ + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); + } +} + +/** + * rt5659_headset_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ + +static int rt5659_headset_detect(struct snd_soc_component *component, int jack_insert) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int val, i = 0, sleep_time[5] = {300, 150, 100, 50, 30}; + int reg_63; + + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, + "Mic Det Power"); + snd_soc_dapm_sync(dapm); + reg_63 = snd_soc_component_read(component, RT5659_PWR_ANLG_1); + + snd_soc_component_update_bits(component, RT5659_PWR_ANLG_1, + RT5659_PWR_VREF2 | RT5659_PWR_MB, + RT5659_PWR_VREF2 | RT5659_PWR_MB); + msleep(20); + snd_soc_component_update_bits(component, RT5659_PWR_ANLG_1, + RT5659_PWR_FV2, RT5659_PWR_FV2); + + snd_soc_component_write(component, RT5659_EJD_CTRL_2, 0x4160); + snd_soc_component_update_bits(component, RT5659_EJD_CTRL_1, + 0x20, 0x0); + msleep(20); + snd_soc_component_update_bits(component, RT5659_EJD_CTRL_1, + 0x20, 0x20); + + while (i < 5) { + msleep(sleep_time[i]); + val = snd_soc_component_read(component, RT5659_EJD_CTRL_2) & 0x0003; + i++; + if (val == 0x1 || val == 0x2 || val == 0x3) + break; + } + + switch (val) { + case 1: + rt5659->jack_type = SND_JACK_HEADSET; + rt5659_enable_push_button_irq(component, true); + break; + default: + snd_soc_component_write(component, RT5659_PWR_ANLG_1, reg_63); + rt5659->jack_type = SND_JACK_HEADPHONE; + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); + break; + } + } else { + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); + if (rt5659->jack_type == SND_JACK_HEADSET) + rt5659_enable_push_button_irq(component, false); + rt5659->jack_type = 0; + } + + dev_dbg(component->dev, "jack_type = %d\n", rt5659->jack_type); + return rt5659->jack_type; +} + +static int rt5659_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read(component, RT5659_4BTN_IL_CMD_1); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5659_4BTN_IL_CMD_1, val); + + return btn_type; +} + +static irqreturn_t rt5659_irq(int irq, void *data) +{ + struct rt5659_priv *rt5659 = data; + + queue_delayed_work(system_power_efficient_wq, + &rt5659->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +int rt5659_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack) +{ + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + + rt5659->hs_jack = hs_jack; + + rt5659_irq(0, rt5659); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5659_set_jack_detect); + +static void rt5659_jack_detect_work(struct work_struct *work) +{ + struct rt5659_priv *rt5659 = + container_of(work, struct rt5659_priv, jack_detect_work.work); + int val, btn_type, report = 0; + + if (!rt5659->component) + return; + + val = snd_soc_component_read(rt5659->component, RT5659_INT_ST_1) & 0x0080; + if (!val) { + /* jack in */ + if (rt5659->jack_type == 0) { + /* jack was out, report jack type */ + report = rt5659_headset_detect(rt5659->component, 1); + } else { + /* jack is already in, report button event */ + report = SND_JACK_HEADSET; + btn_type = rt5659_button_detect(rt5659->component); + /** + * rt5659 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + report |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + report |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + report |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + report |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + btn_type = 0; + dev_err(rt5659->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + + /* button release or spurious interrput*/ + if (btn_type == 0) + report = rt5659->jack_type; + } + } else { + /* jack out */ + report = rt5659_headset_detect(rt5659->component, 0); + } + + snd_soc_jack_report(rt5659->hs_jack, report, SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); +} + +static void rt5659_jack_detect_intel_hd_header(struct work_struct *work) +{ + struct rt5659_priv *rt5659 = + container_of(work, struct rt5659_priv, jack_detect_work.work); + unsigned int value; + bool hp_flag, mic_flag; + + if (!rt5659->hs_jack) + return; + + /* headphone jack */ + regmap_read(rt5659->regmap, RT5659_GPIO_STA, &value); + hp_flag = (!(value & 0x8)) ? true : false; + + if (hp_flag != rt5659->hda_hp_plugged) { + rt5659->hda_hp_plugged = hp_flag; + + if (hp_flag) { + regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1, + 0x10, 0x0); + rt5659->jack_type |= SND_JACK_HEADPHONE; + } else { + regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1, + 0x10, 0x10); + rt5659->jack_type = rt5659->jack_type & + (~SND_JACK_HEADPHONE); + } + + snd_soc_jack_report(rt5659->hs_jack, rt5659->jack_type, + SND_JACK_HEADPHONE); + } + + /* mic jack */ + regmap_read(rt5659->regmap, RT5659_4BTN_IL_CMD_1, &value); + regmap_write(rt5659->regmap, RT5659_4BTN_IL_CMD_1, value); + mic_flag = (value & 0x2000) ? true : false; + + if (mic_flag != rt5659->hda_mic_plugged) { + rt5659->hda_mic_plugged = mic_flag; + if (mic_flag) { + regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2, + 0x2, 0x2); + rt5659->jack_type |= SND_JACK_MICROPHONE; + } else { + regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2, + 0x2, 0x0); + rt5659->jack_type = rt5659->jack_type + & (~SND_JACK_MICROPHONE); + } + + snd_soc_jack_report(rt5659->hs_jack, rt5659->jack_type, + SND_JACK_MICROPHONE); + } +} + +static const struct snd_kcontrol_new rt5659_snd_controls[] = { + /* Speaker Output Volume */ + SOC_DOUBLE_TLV("Speaker Playback Volume", RT5659_SPO_VOL, + RT5659_L_VOL_SFT, RT5659_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* Headphone Output Volume */ + SOC_DOUBLE_R_EXT_TLV("Headphone Playback Volume", RT5659_HPL_GAIN, + RT5659_HPR_GAIN, RT5659_G_HP_SFT, 31, 1, snd_soc_get_volsw, + rt5659_hp_vol_put, hp_vol_tlv), + + /* Mono Output Volume */ + SOC_SINGLE_TLV("Mono Playback Volume", RT5659_MONO_OUT, + RT5659_L_VOL_SFT, 39, 1, out_vol_tlv), + + /* Output Volume */ + SOC_DOUBLE_TLV("OUT Playback Volume", RT5659_LOUT, + RT5659_L_VOL_SFT, RT5659_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5659_DAC1_DIG_VOL, + RT5659_L_VOL_SFT, RT5659_R_VOL_SFT, 175, 0, dac_vol_tlv), + SOC_DOUBLE("DAC1 Playback Switch", RT5659_AD_DA_MIXER, + RT5659_M_DAC1_L_SFT, RT5659_M_DAC1_R_SFT, 1, 1), + + SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5659_DAC2_DIG_VOL, + RT5659_L_VOL_SFT, RT5659_R_VOL_SFT, 175, 0, dac_vol_tlv), + SOC_DOUBLE("DAC2 Playback Switch", RT5659_DAC_CTRL, + RT5659_M_DAC2_L_VOL_SFT, RT5659_M_DAC2_R_VOL_SFT, 1, 1), + + /* IN1/IN2/IN3/IN4 Volume */ + SOC_SINGLE_TLV("IN1 Boost Volume", RT5659_IN1_IN2, + RT5659_BST1_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("IN2 Boost Volume", RT5659_IN1_IN2, + RT5659_BST2_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("IN3 Boost Volume", RT5659_IN3_IN4, + RT5659_BST3_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("IN4 Boost Volume", RT5659_IN3_IN4, + RT5659_BST4_SFT, 69, 0, in_bst_tlv), + + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5659_INL1_INR1_VOL, + RT5659_INL_VOL_SFT, RT5659_INR_VOL_SFT, 31, 1, in_vol_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("STO1 ADC Capture Switch", RT5659_STO1_ADC_DIG_VOL, + RT5659_L_MUTE_SFT, RT5659_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5659_STO1_ADC_DIG_VOL, + RT5659_L_VOL_SFT, RT5659_R_VOL_SFT, 127, 0, adc_vol_tlv), + SOC_DOUBLE("Mono ADC Capture Switch", RT5659_MONO_ADC_DIG_VOL, + RT5659_L_MUTE_SFT, RT5659_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5659_MONO_ADC_DIG_VOL, + RT5659_L_VOL_SFT, RT5659_R_VOL_SFT, 127, 0, adc_vol_tlv), + SOC_DOUBLE("STO2 ADC Capture Switch", RT5659_STO2_ADC_DIG_VOL, + RT5659_L_MUTE_SFT, RT5659_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO2 ADC Capture Volume", RT5659_STO2_ADC_DIG_VOL, + RT5659_L_VOL_SFT, RT5659_R_VOL_SFT, 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5659_STO1_BOOST, + RT5659_STO1_ADC_L_BST_SFT, RT5659_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_DOUBLE_TLV("Mono ADC Boost Gain Volume", RT5659_MONO_BOOST, + RT5659_MONO_ADC_L_BST_SFT, RT5659_MONO_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_DOUBLE_TLV("STO2 ADC Boost Gain Volume", RT5659_STO2_BOOST, + RT5659_STO2_ADC_L_BST_SFT, RT5659_STO2_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_SINGLE("DAC IF1 DAC1 L Data Switch", RT5659_TDM_CTRL_4, 12, 7, 0), + SOC_SINGLE("DAC IF1 DAC1 R Data Switch", RT5659_TDM_CTRL_4, 8, 7, 0), + SOC_SINGLE("DAC IF1 DAC2 L Data Switch", RT5659_TDM_CTRL_4, 4, 7, 0), + SOC_SINGLE("DAC IF1 DAC2 R Data Switch", RT5659_TDM_CTRL_4, 0, 7, 0), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + int pd, idx; + + pd = rl6231_get_pre_div(rt5659->regmap, + RT5659_ADDA_CLK_1, RT5659_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rt5659->sysclk / pd); + + if (idx < 0) + dev_err(component->dev, "Failed to set DMIC clock\n"); + else { + snd_soc_component_update_bits(component, RT5659_DMIC_CTRL_1, + RT5659_DMIC_CLK_MASK, idx << RT5659_DMIC_CLK_SFT); + } + return idx; +} + +static int set_adc1_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5659_CHOP_ADC, + RT5659_CKXEN_ADC1_MASK | RT5659_CKGEN_ADC1_MASK, + RT5659_CKXEN_ADC1_MASK | RT5659_CKGEN_ADC1_MASK); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5659_CHOP_ADC, + RT5659_CKXEN_ADC1_MASK | RT5659_CKGEN_ADC1_MASK, 0); + break; + + default: + return 0; + } + + return 0; + +} + +static int set_adc2_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5659_CHOP_ADC, + RT5659_CKXEN_ADC2_MASK | RT5659_CKGEN_ADC2_MASK, + RT5659_CKXEN_ADC2_MASK | RT5659_CKGEN_ADC2_MASK); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5659_CHOP_ADC, + RT5659_CKXEN_ADC2_MASK | RT5659_CKGEN_ADC2_MASK, 0); + break; + + default: + return 0; + } + + return 0; + +} + +static int rt5659_charge_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Depop */ + snd_soc_component_write(component, RT5659_DEPOP_1, 0x0009); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write(component, RT5659_HP_CHARGE_PUMP_1, 0x0c16); + break; + default: + return 0; + } + + return 0; +} + +static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read(component, RT5659_GLB_CLK); + val &= RT5659_SCLK_SRC_MASK; + if (val == RT5659_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case RT5659_ADC_MONO_R_ASRC_SFT: + reg = RT5659_ASRC_3; + shift = RT5659_AD_MONO_R_T_SFT; + break; + case RT5659_ADC_MONO_L_ASRC_SFT: + reg = RT5659_ASRC_3; + shift = RT5659_AD_MONO_L_T_SFT; + break; + case RT5659_ADC_STO1_ASRC_SFT: + reg = RT5659_ASRC_2; + shift = RT5659_AD_STO1_T_SFT; + break; + case RT5659_DAC_MONO_R_ASRC_SFT: + reg = RT5659_ASRC_2; + shift = RT5659_DA_MONO_R_T_SFT; + break; + case RT5659_DAC_MONO_L_ASRC_SFT: + reg = RT5659_ASRC_2; + shift = RT5659_DA_MONO_L_T_SFT; + break; + case RT5659_DAC_STO_ASRC_SFT: + reg = RT5659_ASRC_2; + shift = RT5659_DA_STO_T_SFT; + break; + default: + return 0; + } + + val = (snd_soc_component_read(component, reg) >> shift) & 0xf; + switch (val) { + case 1: + case 2: + case 3: + /* I2S_Pre_Div1 should be 1 in asrc mode */ + snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1, + RT5659_I2S_PD1_MASK, RT5659_I2S_PD1_2); + return 1; + default: + return 0; + } + +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5659_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5659_STO1_ADC_MIXER, + RT5659_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5659_STO1_ADC_MIXER, + RT5659_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5659_STO1_ADC_MIXER, + RT5659_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5659_STO1_ADC_MIXER, + RT5659_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_mono_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5659_MONO_ADC_MIXER, + RT5659_M_MONO_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5659_MONO_ADC_MIXER, + RT5659_M_MONO_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_mono_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5659_MONO_ADC_MIXER, + RT5659_M_MONO_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5659_MONO_ADC_MIXER, + RT5659_M_MONO_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5659_AD_DA_MIXER, + RT5659_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5659_AD_DA_MIXER, + RT5659_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5659_AD_DA_MIXER, + RT5659_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5659_AD_DA_MIXER, + RT5659_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5659_STO_DAC_MIXER, + RT5659_M_DAC_L1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5659_STO_DAC_MIXER, + RT5659_M_DAC_R1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5659_STO_DAC_MIXER, + RT5659_M_DAC_L2_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5659_STO_DAC_MIXER, + RT5659_M_DAC_R2_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5659_STO_DAC_MIXER, + RT5659_M_DAC_L1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5659_STO_DAC_MIXER, + RT5659_M_DAC_R1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5659_STO_DAC_MIXER, + RT5659_M_DAC_L2_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5659_STO_DAC_MIXER, + RT5659_M_DAC_R2_STO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_mono_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5659_MONO_DAC_MIXER, + RT5659_M_DAC_L1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5659_MONO_DAC_MIXER, + RT5659_M_DAC_R1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5659_MONO_DAC_MIXER, + RT5659_M_DAC_L2_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5659_MONO_DAC_MIXER, + RT5659_M_DAC_R2_MONO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_mono_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5659_MONO_DAC_MIXER, + RT5659_M_DAC_L1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5659_MONO_DAC_MIXER, + RT5659_M_DAC_R1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5659_MONO_DAC_MIXER, + RT5659_M_DAC_L2_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5659_MONO_DAC_MIXER, + RT5659_M_DAC_R2_MONO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5659_rec1_l_mix[] = { + SOC_DAPM_SINGLE("SPKVOLL Switch", RT5659_REC1_L2_MIXER, + RT5659_M_SPKVOLL_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5659_REC1_L2_MIXER, + RT5659_M_INL_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5659_REC1_L2_MIXER, + RT5659_M_BST4_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5659_REC1_L2_MIXER, + RT5659_M_BST3_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5659_REC1_L2_MIXER, + RT5659_M_BST2_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5659_REC1_L2_MIXER, + RT5659_M_BST1_RM1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_rec1_r_mix[] = { + SOC_DAPM_SINGLE("HPOVOLR Switch", RT5659_REC1_L2_MIXER, + RT5659_M_HPOVOLR_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5659_REC1_R2_MIXER, + RT5659_M_INR_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5659_REC1_R2_MIXER, + RT5659_M_BST4_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5659_REC1_R2_MIXER, + RT5659_M_BST3_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5659_REC1_R2_MIXER, + RT5659_M_BST2_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5659_REC1_R2_MIXER, + RT5659_M_BST1_RM1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_rec2_l_mix[] = { + SOC_DAPM_SINGLE("SPKVOLL Switch", RT5659_REC2_L2_MIXER, + RT5659_M_SPKVOL_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOLL Switch", RT5659_REC2_L2_MIXER, + RT5659_M_OUTVOLL_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5659_REC2_L2_MIXER, + RT5659_M_BST4_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5659_REC2_L2_MIXER, + RT5659_M_BST3_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5659_REC2_L2_MIXER, + RT5659_M_BST2_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5659_REC2_L2_MIXER, + RT5659_M_BST1_RM2_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_rec2_r_mix[] = { + SOC_DAPM_SINGLE("MONOVOL Switch", RT5659_REC2_R2_MIXER, + RT5659_M_MONOVOL_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOLR Switch", RT5659_REC2_R2_MIXER, + RT5659_M_OUTVOLR_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5659_REC2_R2_MIXER, + RT5659_M_BST4_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5659_REC2_R2_MIXER, + RT5659_M_BST3_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5659_REC2_R2_MIXER, + RT5659_M_BST2_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5659_REC2_R2_MIXER, + RT5659_M_BST1_RM2_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_spk_l_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5659_SPK_L_MIXER, + RT5659_M_DAC_L2_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5659_SPK_L_MIXER, + RT5659_M_BST1_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5659_SPK_L_MIXER, + RT5659_M_IN_L_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5659_SPK_L_MIXER, + RT5659_M_IN_R_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5659_SPK_L_MIXER, + RT5659_M_BST3_SM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_spk_r_mix[] = { + SOC_DAPM_SINGLE("DAC R2 Switch", RT5659_SPK_R_MIXER, + RT5659_M_DAC_R2_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5659_SPK_R_MIXER, + RT5659_M_BST4_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5659_SPK_R_MIXER, + RT5659_M_IN_L_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5659_SPK_R_MIXER, + RT5659_M_IN_R_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5659_SPK_R_MIXER, + RT5659_M_BST3_SM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_monovol_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5659_MONOMIX_IN_GAIN, + RT5659_M_DAC_L2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5659_MONOMIX_IN_GAIN, + RT5659_M_DAC_R2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5659_MONOMIX_IN_GAIN, + RT5659_M_BST1_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5659_MONOMIX_IN_GAIN, + RT5659_M_BST2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5659_MONOMIX_IN_GAIN, + RT5659_M_BST3_MM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_out_l_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5659_OUT_L_MIXER, + RT5659_M_DAC_L2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5659_OUT_L_MIXER, + RT5659_M_IN_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5659_OUT_L_MIXER, + RT5659_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5659_OUT_L_MIXER, + RT5659_M_BST2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5659_OUT_L_MIXER, + RT5659_M_BST3_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_out_r_mix[] = { + SOC_DAPM_SINGLE("DAC R2 Switch", RT5659_OUT_R_MIXER, + RT5659_M_DAC_R2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5659_OUT_R_MIXER, + RT5659_M_IN_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5659_OUT_R_MIXER, + RT5659_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5659_OUT_R_MIXER, + RT5659_M_BST3_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5659_OUT_R_MIXER, + RT5659_M_BST4_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_spo_l_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5659_SPO_AMP_GAIN, + RT5659_M_DAC_L2_SPKOMIX_SFT, 1, 0), + SOC_DAPM_SINGLE("SPKVOL L Switch", RT5659_SPO_AMP_GAIN, + RT5659_M_SPKVOLL_SPKOMIX_SFT, 1, 0), +}; + +static const struct snd_kcontrol_new rt5659_spo_r_mix[] = { + SOC_DAPM_SINGLE("DAC R2 Switch", RT5659_SPO_AMP_GAIN, + RT5659_M_DAC_R2_SPKOMIX_SFT, 1, 0), + SOC_DAPM_SINGLE("SPKVOL R Switch", RT5659_SPO_AMP_GAIN, + RT5659_M_SPKVOLR_SPKOMIX_SFT, 1, 0), +}; + +static const struct snd_kcontrol_new rt5659_mono_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5659_MONOMIX_IN_GAIN, + RT5659_M_DAC_L2_MA_SFT, 1, 1), + SOC_DAPM_SINGLE("MONOVOL Switch", RT5659_MONOMIX_IN_GAIN, + RT5659_M_MONOVOL_MA_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_lout_l_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5659_LOUT_MIXER, + RT5659_M_DAC_L2_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL L Switch", RT5659_LOUT_MIXER, + RT5659_M_OV_L_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5659_lout_r_mix[] = { + SOC_DAPM_SINGLE("DAC R2 Switch", RT5659_LOUT_MIXER, + RT5659_M_DAC_R2_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL R Switch", RT5659_LOUT_MIXER, + RT5659_M_OV_R_LM_SFT, 1, 1), +}; + +/*DAC L2, DAC R2*/ +/*MX-1B [6:4], MX-1B [2:0]*/ +static const char * const rt5659_dac2_src[] = { + "IF1 DAC2", "IF2 DAC", "IF3 DAC", "Mono ADC MIX" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_dac_l2_enum, RT5659_DAC_CTRL, + RT5659_DAC_L2_SEL_SFT, rt5659_dac2_src); + +static const struct snd_kcontrol_new rt5659_dac_l2_mux = + SOC_DAPM_ENUM("DAC L2 Source", rt5659_dac_l2_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5659_dac_r2_enum, RT5659_DAC_CTRL, + RT5659_DAC_R2_SEL_SFT, rt5659_dac2_src); + +static const struct snd_kcontrol_new rt5659_dac_r2_mux = + SOC_DAPM_ENUM("DAC R2 Source", rt5659_dac_r2_enum); + + +/* STO1 ADC1 Source */ +/* MX-26 [13] */ +static const char * const rt5659_sto1_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_sto1_adc1_enum, RT5659_STO1_ADC_MIXER, + RT5659_STO1_ADC1_SRC_SFT, rt5659_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5659_sto1_adc1_mux = + SOC_DAPM_ENUM("Stereo1 ADC1 Source", rt5659_sto1_adc1_enum); + +/* STO1 ADC Source */ +/* MX-26 [12] */ +static const char * const rt5659_sto1_adc_src[] = { + "ADC1", "ADC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_sto1_adc_enum, RT5659_STO1_ADC_MIXER, + RT5659_STO1_ADC_SRC_SFT, rt5659_sto1_adc_src); + +static const struct snd_kcontrol_new rt5659_sto1_adc_mux = + SOC_DAPM_ENUM("Stereo1 ADC Source", rt5659_sto1_adc_enum); + +/* STO1 ADC2 Source */ +/* MX-26 [11] */ +static const char * const rt5659_sto1_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_sto1_adc2_enum, RT5659_STO1_ADC_MIXER, + RT5659_STO1_ADC2_SRC_SFT, rt5659_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5659_sto1_adc2_mux = + SOC_DAPM_ENUM("Stereo1 ADC2 Source", rt5659_sto1_adc2_enum); + +/* STO1 DMIC Source */ +/* MX-26 [8] */ +static const char * const rt5659_sto1_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_sto1_dmic_enum, RT5659_STO1_ADC_MIXER, + RT5659_STO1_DMIC_SRC_SFT, rt5659_sto1_dmic_src); + +static const struct snd_kcontrol_new rt5659_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5659_sto1_dmic_enum); + + +/* MONO ADC L2 Source */ +/* MX-27 [12] */ +static const char * const rt5659_mono_adc_l2_src[] = { + "Mono DAC MIXL", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_mono_adc_l2_enum, RT5659_MONO_ADC_MIXER, + RT5659_MONO_ADC_L2_SRC_SFT, rt5659_mono_adc_l2_src); + +static const struct snd_kcontrol_new rt5659_mono_adc_l2_mux = + SOC_DAPM_ENUM("Mono ADC L2 Source", rt5659_mono_adc_l2_enum); + + +/* MONO ADC L1 Source */ +/* MX-27 [11] */ +static const char * const rt5659_mono_adc_l1_src[] = { + "Mono DAC MIXL", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_mono_adc_l1_enum, RT5659_MONO_ADC_MIXER, + RT5659_MONO_ADC_L1_SRC_SFT, rt5659_mono_adc_l1_src); + +static const struct snd_kcontrol_new rt5659_mono_adc_l1_mux = + SOC_DAPM_ENUM("Mono ADC L1 Source", rt5659_mono_adc_l1_enum); + +/* MONO ADC L Source, MONO ADC R Source*/ +/* MX-27 [10:9], MX-27 [2:1] */ +static const char * const rt5659_mono_adc_src[] = { + "ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_mono_adc_l_enum, RT5659_MONO_ADC_MIXER, + RT5659_MONO_ADC_L_SRC_SFT, rt5659_mono_adc_src); + +static const struct snd_kcontrol_new rt5659_mono_adc_l_mux = + SOC_DAPM_ENUM("Mono ADC L Source", rt5659_mono_adc_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5659_mono_adcr_enum, RT5659_MONO_ADC_MIXER, + RT5659_MONO_ADC_R_SRC_SFT, rt5659_mono_adc_src); + +static const struct snd_kcontrol_new rt5659_mono_adc_r_mux = + SOC_DAPM_ENUM("Mono ADC R Source", rt5659_mono_adcr_enum); + +/* MONO DMIC L Source */ +/* MX-27 [8] */ +static const char * const rt5659_mono_dmic_l_src[] = { + "DMIC1 L", "DMIC2 L" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_mono_dmic_l_enum, RT5659_MONO_ADC_MIXER, + RT5659_MONO_DMIC_L_SRC_SFT, rt5659_mono_dmic_l_src); + +static const struct snd_kcontrol_new rt5659_mono_dmic_l_mux = + SOC_DAPM_ENUM("Mono DMIC L Source", rt5659_mono_dmic_l_enum); + +/* MONO ADC R2 Source */ +/* MX-27 [4] */ +static const char * const rt5659_mono_adc_r2_src[] = { + "Mono DAC MIXR", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_mono_adc_r2_enum, RT5659_MONO_ADC_MIXER, + RT5659_MONO_ADC_R2_SRC_SFT, rt5659_mono_adc_r2_src); + +static const struct snd_kcontrol_new rt5659_mono_adc_r2_mux = + SOC_DAPM_ENUM("Mono ADC R2 Source", rt5659_mono_adc_r2_enum); + +/* MONO ADC R1 Source */ +/* MX-27 [3] */ +static const char * const rt5659_mono_adc_r1_src[] = { + "Mono DAC MIXR", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_mono_adc_r1_enum, RT5659_MONO_ADC_MIXER, + RT5659_MONO_ADC_R1_SRC_SFT, rt5659_mono_adc_r1_src); + +static const struct snd_kcontrol_new rt5659_mono_adc_r1_mux = + SOC_DAPM_ENUM("Mono ADC R1 Source", rt5659_mono_adc_r1_enum); + +/* MONO DMIC R Source */ +/* MX-27 [0] */ +static const char * const rt5659_mono_dmic_r_src[] = { + "DMIC1 R", "DMIC2 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_mono_dmic_r_enum, RT5659_MONO_ADC_MIXER, + RT5659_MONO_DMIC_R_SRC_SFT, rt5659_mono_dmic_r_src); + +static const struct snd_kcontrol_new rt5659_mono_dmic_r_mux = + SOC_DAPM_ENUM("Mono DMIC R Source", rt5659_mono_dmic_r_enum); + + +/* DAC R1 Source, DAC L1 Source*/ +/* MX-29 [11:10], MX-29 [9:8]*/ +static const char * const rt5659_dac1_src[] = { + "IF1 DAC1", "IF2 DAC", "IF3 DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_dac_r1_enum, RT5659_AD_DA_MIXER, + RT5659_DAC1_R_SEL_SFT, rt5659_dac1_src); + +static const struct snd_kcontrol_new rt5659_dac_r1_mux = + SOC_DAPM_ENUM("DAC R1 Source", rt5659_dac_r1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5659_dac_l1_enum, RT5659_AD_DA_MIXER, + RT5659_DAC1_L_SEL_SFT, rt5659_dac1_src); + +static const struct snd_kcontrol_new rt5659_dac_l1_mux = + SOC_DAPM_ENUM("DAC L1 Source", rt5659_dac_l1_enum); + +/* DAC Digital Mixer L Source, DAC Digital Mixer R Source*/ +/* MX-2C [6], MX-2C [4]*/ +static const char * const rt5659_dig_dac_mix_src[] = { + "Stereo DAC Mixer", "Mono DAC Mixer" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_dig_dac_mixl_enum, RT5659_DIG_MIXER, + RT5659_DAC_MIX_L_SFT, rt5659_dig_dac_mix_src); + +static const struct snd_kcontrol_new rt5659_dig_dac_mixl_mux = + SOC_DAPM_ENUM("DAC Digital Mixer L Source", rt5659_dig_dac_mixl_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5659_dig_dac_mixr_enum, RT5659_DIG_MIXER, + RT5659_DAC_MIX_R_SFT, rt5659_dig_dac_mix_src); + +static const struct snd_kcontrol_new rt5659_dig_dac_mixr_mux = + SOC_DAPM_ENUM("DAC Digital Mixer R Source", rt5659_dig_dac_mixr_enum); + +/* Analog DAC L1 Source, Analog DAC R1 Source*/ +/* MX-2D [3], MX-2D [2]*/ +static const char * const rt5659_alg_dac1_src[] = { + "DAC", "Stereo DAC Mixer" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_alg_dac_l1_enum, RT5659_A_DAC_MUX, + RT5659_A_DACL1_SFT, rt5659_alg_dac1_src); + +static const struct snd_kcontrol_new rt5659_alg_dac_l1_mux = + SOC_DAPM_ENUM("Analog DACL1 Source", rt5659_alg_dac_l1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5659_alg_dac_r1_enum, RT5659_A_DAC_MUX, + RT5659_A_DACR1_SFT, rt5659_alg_dac1_src); + +static const struct snd_kcontrol_new rt5659_alg_dac_r1_mux = + SOC_DAPM_ENUM("Analog DACR1 Source", rt5659_alg_dac_r1_enum); + +/* Analog DAC LR Source, Analog DAC R2 Source*/ +/* MX-2D [1], MX-2D [0]*/ +static const char * const rt5659_alg_dac2_src[] = { + "Stereo DAC Mixer", "Mono DAC Mixer" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_alg_dac_l2_enum, RT5659_A_DAC_MUX, + RT5659_A_DACL2_SFT, rt5659_alg_dac2_src); + +static const struct snd_kcontrol_new rt5659_alg_dac_l2_mux = + SOC_DAPM_ENUM("Analog DAC L2 Source", rt5659_alg_dac_l2_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5659_alg_dac_r2_enum, RT5659_A_DAC_MUX, + RT5659_A_DACR2_SFT, rt5659_alg_dac2_src); + +static const struct snd_kcontrol_new rt5659_alg_dac_r2_mux = + SOC_DAPM_ENUM("Analog DAC R2 Source", rt5659_alg_dac_r2_enum); + +/* Interface2 ADC Data Input*/ +/* MX-2F [13:12] */ +static const char * const rt5659_if2_adc_in_src[] = { + "IF_ADC1", "IF_ADC2", "DAC_REF", "IF_ADC3" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_if2_adc_in_enum, RT5659_DIG_INF23_DATA, + RT5659_IF2_ADC_IN_SFT, rt5659_if2_adc_in_src); + +static const struct snd_kcontrol_new rt5659_if2_adc_in_mux = + SOC_DAPM_ENUM("IF2 ADC IN Source", rt5659_if2_adc_in_enum); + +/* Interface3 ADC Data Input*/ +/* MX-2F [1:0] */ +static const char * const rt5659_if3_adc_in_src[] = { + "IF_ADC1", "IF_ADC2", "DAC_REF", "Stereo2_ADC_L/R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_if3_adc_in_enum, RT5659_DIG_INF23_DATA, + RT5659_IF3_ADC_IN_SFT, rt5659_if3_adc_in_src); + +static const struct snd_kcontrol_new rt5659_if3_adc_in_mux = + SOC_DAPM_ENUM("IF3 ADC IN Source", rt5659_if3_adc_in_enum); + +/* PDM 1 L/R*/ +/* MX-31 [15] [13] */ +static const char * const rt5659_pdm_src[] = { + "Mono DAC", "Stereo DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_pdm_l_enum, RT5659_PDM_OUT_CTRL, + RT5659_PDM1_L_SFT, rt5659_pdm_src); + +static const struct snd_kcontrol_new rt5659_pdm_l_mux = + SOC_DAPM_ENUM("PDM L Source", rt5659_pdm_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5659_pdm_r_enum, RT5659_PDM_OUT_CTRL, + RT5659_PDM1_R_SFT, rt5659_pdm_src); + +static const struct snd_kcontrol_new rt5659_pdm_r_mux = + SOC_DAPM_ENUM("PDM R Source", rt5659_pdm_r_enum); + +/* SPDIF Output source*/ +/* MX-36 [1:0] */ +static const char * const rt5659_spdif_src[] = { + "IF1_DAC1", "IF1_DAC2", "IF2_DAC", "IF3_DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_spdif_enum, RT5659_SPDIF_CTRL, + RT5659_SPDIF_SEL_SFT, rt5659_spdif_src); + +static const struct snd_kcontrol_new rt5659_spdif_mux = + SOC_DAPM_ENUM("SPDIF Source", rt5659_spdif_enum); + +/* I2S1 TDM ADCDAT Source */ +/* MX-78[4:0] */ +static const char * const rt5659_rx_adc_data_src[] = { + "AD1:AD2:DAC:NUL", "AD1:AD2:NUL:DAC", "AD1:DAC:AD2:NUL", + "AD1:DAC:NUL:AD2", "AD1:NUL:DAC:AD2", "AD1:NUL:AD2:DAC", + "AD2:AD1:DAC:NUL", "AD2:AD1:NUL:DAC", "AD2:DAC:AD1:NUL", + "AD2:DAC:NUL:AD1", "AD2:NUL:DAC:AD1", "AD1:NUL:AD1:DAC", + "DAC:AD1:AD2:NUL", "DAC:AD1:NUL:AD2", "DAC:AD2:AD1:NUL", + "DAC:AD2:NUL:AD1", "DAC:NUL:DAC:AD2", "DAC:NUL:AD2:DAC", + "NUL:AD1:AD2:DAC", "NUL:AD1:DAC:AD2", "NUL:AD2:AD1:DAC", + "NUL:AD2:DAC:AD1", "NUL:DAC:DAC:AD2", "NUL:DAC:AD2:DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5659_rx_adc_data_enum, RT5659_TDM_CTRL_2, + RT5659_ADCDAT_SRC_SFT, rt5659_rx_adc_data_src); + +static const struct snd_kcontrol_new rt5659_rx_adc_dac_mux = + SOC_DAPM_ENUM("TDM ADCDAT Source", rt5659_rx_adc_data_enum); + +/* Out Volume Switch */ +static const struct snd_kcontrol_new spkvol_l_switch = + SOC_DAPM_SINGLE("Switch", RT5659_SPO_VOL, RT5659_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new spkvol_r_switch = + SOC_DAPM_SINGLE("Switch", RT5659_SPO_VOL, RT5659_VOL_R_SFT, 1, 1); + +static const struct snd_kcontrol_new monovol_switch = + SOC_DAPM_SINGLE("Switch", RT5659_MONO_OUT, RT5659_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new outvol_l_switch = + SOC_DAPM_SINGLE("Switch", RT5659_LOUT, RT5659_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new outvol_r_switch = + SOC_DAPM_SINGLE("Switch", RT5659_LOUT, RT5659_VOL_R_SFT, 1, 1); + +/* Out Switch */ +static const struct snd_kcontrol_new spo_switch = + SOC_DAPM_SINGLE("Switch", RT5659_CLASSD_2, RT5659_M_RF_DIG_SFT, 1, 1); + +static const struct snd_kcontrol_new mono_switch = + SOC_DAPM_SINGLE("Switch", RT5659_MONO_OUT, RT5659_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpo_l_switch = + SOC_DAPM_SINGLE("Switch", RT5659_HP_VOL, RT5659_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpo_r_switch = + SOC_DAPM_SINGLE("Switch", RT5659_HP_VOL, RT5659_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_l_switch = + SOC_DAPM_SINGLE("Switch", RT5659_LOUT, RT5659_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_r_switch = + SOC_DAPM_SINGLE("Switch", RT5659_LOUT, RT5659_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new pdm_l_switch = + SOC_DAPM_SINGLE("Switch", RT5659_PDM_OUT_CTRL, RT5659_M_PDM1_L_SFT, 1, + 1); + +static const struct snd_kcontrol_new pdm_r_switch = + SOC_DAPM_SINGLE("Switch", RT5659_PDM_OUT_CTRL, RT5659_M_PDM1_R_SFT, 1, + 1); + +static int rt5659_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, RT5659_CLASSD_CTRL_1, + RT5659_POW_CLSD_DB_MASK, RT5659_POW_CLSD_DB_EN); + snd_soc_component_update_bits(component, RT5659_CLASSD_2, + RT5659_M_RI_DIG, RT5659_M_RI_DIG); + snd_soc_component_write(component, RT5659_CLASSD_1, 0x0803); + snd_soc_component_write(component, RT5659_SPK_DC_CAILB_CTRL_3, 0x0000); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write(component, RT5659_CLASSD_1, 0x0011); + snd_soc_component_update_bits(component, RT5659_CLASSD_2, + RT5659_M_RI_DIG, 0x0); + snd_soc_component_write(component, RT5659_SPK_DC_CAILB_CTRL_3, 0x0003); + snd_soc_component_update_bits(component, RT5659_CLASSD_CTRL_1, + RT5659_POW_CLSD_DB_MASK, RT5659_POW_CLSD_DB_DIS); + break; + + default: + return 0; + } + + return 0; + +} + +static int rt5659_mono_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write(component, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e00); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write(component, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e04); + break; + + default: + return 0; + } + + return 0; + +} + +static int rt5659_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(component, RT5659_HP_CHARGE_PUMP_1, 0x0e1e); + snd_soc_component_update_bits(component, RT5659_DEPOP_1, 0x0010, 0x0010); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write(component, RT5659_DEPOP_1, 0x0000); + break; + + default: + return 0; + } + + return 0; +} + +static int set_dmic_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /*Add delay to avoid pop noise*/ + msleep(450); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5659_particular_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5659_PWR_ANLG_3, RT5659_PWR_LDO2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5659_PWR_ANLG_2, RT5659_PWR_MB1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5659_PWR_VOL, + RT5659_PWR_MIC_DET_BIT, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_widget rt5659_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL", RT5659_PWR_ANLG_3, RT5659_PWR_PLL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Mono Vref", RT5659_PWR_ANLG_1, + RT5659_PWR_VREF3_BIT, 0, NULL, 0), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5659_ASRC_1, + RT5659_I2S1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5659_ASRC_1, + RT5659_I2S2_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S3 ASRC", 1, RT5659_ASRC_1, + RT5659_I2S3_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5659_ASRC_1, + RT5659_DAC_STO_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC Mono L ASRC", 1, RT5659_ASRC_1, + RT5659_DAC_MONO_L_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC Mono R ASRC", 1, RT5659_ASRC_1, + RT5659_DAC_MONO_R_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5659_ASRC_1, + RT5659_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC Mono L ASRC", 1, RT5659_ASRC_1, + RT5659_ADC_MONO_L_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC Mono R ASRC", 1, RT5659_ASRC_1, + RT5659_ADC_MONO_R_ASRC_SFT, 0, NULL, 0), + + /* Input Side */ + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5659_PWR_ANLG_2, RT5659_PWR_MB2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS3", RT5659_PWR_ANLG_2, RT5659_PWR_MB3_BIT, + 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + SND_SOC_DAPM_INPUT("DMIC L2"), + SND_SOC_DAPM_INPUT("DMIC R2"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("IN3N"), + SND_SOC_DAPM_INPUT("IN4P"), + SND_SOC_DAPM_INPUT("IN4N"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5659_DMIC_CTRL_1, + RT5659_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5659_DMIC_CTRL_1, + RT5659_DMIC_2_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1", RT5659_PWR_ANLG_2, + RT5659_PWR_BST1_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST2", RT5659_PWR_ANLG_2, + RT5659_PWR_BST2_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST3", RT5659_PWR_ANLG_2, + RT5659_PWR_BST3_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST4", RT5659_PWR_ANLG_2, + RT5659_PWR_BST4_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST1 Power", RT5659_PWR_ANLG_2, + RT5659_PWR_BST1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST2 Power", RT5659_PWR_ANLG_2, + RT5659_PWR_BST2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST3 Power", RT5659_PWR_ANLG_2, + RT5659_PWR_BST3_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST4 Power", RT5659_PWR_ANLG_2, + RT5659_PWR_BST4_BIT, 0, NULL, 0), + + + /* Input Volume */ + SND_SOC_DAPM_PGA("INL VOL", RT5659_PWR_VOL, RT5659_PWR_IN_L_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("INR VOL", RT5659_PWR_VOL, RT5659_PWR_IN_R_BIT, + 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", RT5659_PWR_MIXER, RT5659_PWR_RM1_L_BIT, + 0, rt5659_rec1_l_mix, ARRAY_SIZE(rt5659_rec1_l_mix)), + SND_SOC_DAPM_MIXER("RECMIX1R", RT5659_PWR_MIXER, RT5659_PWR_RM1_R_BIT, + 0, rt5659_rec1_r_mix, ARRAY_SIZE(rt5659_rec1_r_mix)), + SND_SOC_DAPM_MIXER("RECMIX2L", RT5659_PWR_MIXER, RT5659_PWR_RM2_L_BIT, + 0, rt5659_rec2_l_mix, ARRAY_SIZE(rt5659_rec2_l_mix)), + SND_SOC_DAPM_MIXER("RECMIX2R", RT5659_PWR_MIXER, RT5659_PWR_RM2_R_BIT, + 0, rt5659_rec2_r_mix, ARRAY_SIZE(rt5659_rec2_r_mix)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC2 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC2 R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5659_PWR_DIG_1, + RT5659_PWR_ADC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5659_PWR_DIG_1, + RT5659_PWR_ADC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 L Power", RT5659_PWR_DIG_1, + RT5659_PWR_ADC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 R Power", RT5659_PWR_DIG_1, + RT5659_PWR_ADC_R2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", SND_SOC_NOPM, 0, 0, set_adc1_clk, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("ADC2 clock", SND_SOC_NOPM, 0, 0, set_adc2_clk, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5659_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5659_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5659_sto1_adc1_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5659_sto1_adc1_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5659_sto1_adc2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5659_sto1_adc2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5659_sto1_adc_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5659_sto1_adc_mux), + SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5659_mono_adc_l2_mux), + SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5659_mono_adc_r2_mux), + SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5659_mono_adc_l1_mux), + SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5659_mono_adc_r1_mux), + SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5659_mono_dmic_l_mux), + SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5659_mono_dmic_r_mux), + SND_SOC_DAPM_MUX("Mono ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5659_mono_adc_l_mux), + SND_SOC_DAPM_MUX("Mono ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5659_mono_adc_r_mux), + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5659_PWR_DIG_2, + RT5659_PWR_ADC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Stereo2 Filter", RT5659_PWR_DIG_2, + RT5659_PWR_ADC_S2F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, + 0, 0, rt5659_sto1_adc_l_mix, + ARRAY_SIZE(rt5659_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, + 0, 0, rt5659_sto1_adc_r_mix, + ARRAY_SIZE(rt5659_sto1_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("ADC Mono Left Filter", RT5659_PWR_DIG_2, + RT5659_PWR_ADC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXL", RT5659_MONO_ADC_DIG_VOL, + RT5659_L_MUTE_SFT, 1, rt5659_mono_adc_l_mix, + ARRAY_SIZE(rt5659_mono_adc_l_mix)), + SND_SOC_DAPM_SUPPLY("ADC Mono Right Filter", RT5659_PWR_DIG_2, + RT5659_PWR_ADC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXR", RT5659_MONO_ADC_DIG_VOL, + RT5659_R_MUTE_SFT, 1, rt5659_mono_adc_r_mix, + ARRAY_SIZE(rt5659_mono_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("IF_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC LR", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Stereo1 ADC Volume L", RT5659_STO1_ADC_DIG_VOL, + RT5659_L_MUTE_SFT, 1, NULL, 0), + SND_SOC_DAPM_PGA("Stereo1 ADC Volume R", RT5659_STO1_ADC_DIG_VOL, + RT5659_R_MUTE_SFT, 1, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5659_PWR_DIG_1, RT5659_PWR_I2S1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5659_PWR_DIG_1, RT5659_PWR_I2S2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S3", RT5659_PWR_DIG_1, RT5659_PWR_I2S3_BIT, 0, + NULL, 0), + SND_SOC_DAPM_PGA("IF3 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_PGA("TDM AD1:AD2:DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("TDM AD2:DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("TDM Data Mux", SND_SOC_NOPM, 0, 0, + &rt5659_rx_adc_dac_mux), + SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5659_if2_adc_in_mux), + SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5659_if3_adc_in_mux), + SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5659_if1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5659_if1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5659_if1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5659_if1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2 DAC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5659_if2_dac_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5659_if2_adc_swap_mux), + SND_SOC_DAPM_MUX("IF3 DAC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5659_if3_dac_swap_mux), + SND_SOC_DAPM_MUX("IF3 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5659_if3_adc_swap_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF3RX", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF3TX", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5659_dac_l_mix, ARRAY_SIZE(rt5659_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5659_dac_r_mix, ARRAY_SIZE(rt5659_dac_r_mix)), + + /* DAC channel Mux */ + SND_SOC_DAPM_MUX("DAC L1 Mux", SND_SOC_NOPM, 0, 0, &rt5659_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Mux", SND_SOC_NOPM, 0, 0, &rt5659_dac_r1_mux), + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5659_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5659_dac_r2_mux), + + SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, + &rt5659_alg_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, + &rt5659_alg_dac_r1_mux), + SND_SOC_DAPM_MUX("DAC L2 Source", SND_SOC_NOPM, 0, 0, + &rt5659_alg_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Source", SND_SOC_NOPM, 0, 0, + &rt5659_alg_dac_r2_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5659_PWR_DIG_2, + RT5659_PWR_DAC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mono Left Filter", RT5659_PWR_DIG_2, + RT5659_PWR_DAC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mono Right Filter", RT5659_PWR_DIG_2, + RT5659_PWR_DAC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5659_sto_dac_l_mix, ARRAY_SIZE(rt5659_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5659_sto_dac_r_mix, ARRAY_SIZE(rt5659_sto_dac_r_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5659_mono_dac_l_mix, ARRAY_SIZE(rt5659_mono_dac_l_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5659_mono_dac_r_mix, ARRAY_SIZE(rt5659_mono_dac_r_mix)), + SND_SOC_DAPM_MUX("DAC MIXL", SND_SOC_NOPM, 0, 0, + &rt5659_dig_dac_mixl_mux), + SND_SOC_DAPM_MUX("DAC MIXR", SND_SOC_NOPM, 0, 0, + &rt5659_dig_dac_mixr_mux), + + /* DACs */ + SND_SOC_DAPM_SUPPLY_S("DAC L1 Power", 1, RT5659_PWR_DIG_1, + RT5659_PWR_DAC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC R1 Power", 1, RT5659_PWR_DIG_1, + RT5659_PWR_DAC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5659_PWR_DIG_1, + RT5659_PWR_DAC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5659_PWR_DIG_1, + RT5659_PWR_DAC_R2_BIT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC L2", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("DAC_REF", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* OUT Mixer */ + SND_SOC_DAPM_MIXER("SPK MIXL", RT5659_PWR_MIXER, RT5659_PWR_SM_L_BIT, + 0, rt5659_spk_l_mix, ARRAY_SIZE(rt5659_spk_l_mix)), + SND_SOC_DAPM_MIXER("SPK MIXR", RT5659_PWR_MIXER, RT5659_PWR_SM_R_BIT, + 0, rt5659_spk_r_mix, ARRAY_SIZE(rt5659_spk_r_mix)), + SND_SOC_DAPM_MIXER("MONOVOL MIX", RT5659_PWR_MIXER, RT5659_PWR_MM_BIT, + 0, rt5659_monovol_mix, ARRAY_SIZE(rt5659_monovol_mix)), + SND_SOC_DAPM_MIXER("OUT MIXL", RT5659_PWR_MIXER, RT5659_PWR_OM_L_BIT, + 0, rt5659_out_l_mix, ARRAY_SIZE(rt5659_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5659_PWR_MIXER, RT5659_PWR_OM_R_BIT, + 0, rt5659_out_r_mix, ARRAY_SIZE(rt5659_out_r_mix)), + + /* Output Volume */ + SND_SOC_DAPM_SWITCH("SPKVOL L", RT5659_PWR_VOL, RT5659_PWR_SV_L_BIT, 0, + &spkvol_l_switch), + SND_SOC_DAPM_SWITCH("SPKVOL R", RT5659_PWR_VOL, RT5659_PWR_SV_R_BIT, 0, + &spkvol_r_switch), + SND_SOC_DAPM_SWITCH("MONOVOL", RT5659_PWR_VOL, RT5659_PWR_MV_BIT, 0, + &monovol_switch), + SND_SOC_DAPM_SWITCH("OUTVOL L", RT5659_PWR_VOL, RT5659_PWR_OV_L_BIT, 0, + &outvol_l_switch), + SND_SOC_DAPM_SWITCH("OUTVOL R", RT5659_PWR_VOL, RT5659_PWR_OV_R_BIT, 0, + &outvol_r_switch), + + /* SPO/MONO/HPO/LOUT */ + SND_SOC_DAPM_MIXER("SPO L MIX", SND_SOC_NOPM, 0, 0, rt5659_spo_l_mix, + ARRAY_SIZE(rt5659_spo_l_mix)), + SND_SOC_DAPM_MIXER("SPO R MIX", SND_SOC_NOPM, 0, 0, rt5659_spo_r_mix, + ARRAY_SIZE(rt5659_spo_r_mix)), + SND_SOC_DAPM_MIXER("Mono MIX", SND_SOC_NOPM, 0, 0, rt5659_mono_mix, + ARRAY_SIZE(rt5659_mono_mix)), + SND_SOC_DAPM_MIXER("LOUT L MIX", SND_SOC_NOPM, 0, 0, rt5659_lout_l_mix, + ARRAY_SIZE(rt5659_lout_l_mix)), + SND_SOC_DAPM_MIXER("LOUT R MIX", SND_SOC_NOPM, 0, 0, rt5659_lout_r_mix, + ARRAY_SIZE(rt5659_lout_r_mix)), + + SND_SOC_DAPM_PGA_S("SPK Amp", 1, RT5659_PWR_DIG_1, RT5659_PWR_CLS_D_BIT, + 0, rt5659_spk_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_PGA_S("Mono Amp", 1, RT5659_PWR_ANLG_1, RT5659_PWR_MA_BIT, + 0, rt5659_mono_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5659_hp_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("LOUT Amp", 1, RT5659_PWR_ANLG_1, RT5659_PWR_LM_BIT, + 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Charge Pump", SND_SOC_NOPM, 0, 0, + rt5659_charge_pump_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SWITCH("SPO Playback", SND_SOC_NOPM, 0, 0, &spo_switch), + SND_SOC_DAPM_SWITCH("Mono Playback", SND_SOC_NOPM, 0, 0, + &mono_switch), + SND_SOC_DAPM_SWITCH("HPO L Playback", SND_SOC_NOPM, 0, 0, + &hpo_l_switch), + SND_SOC_DAPM_SWITCH("HPO R Playback", SND_SOC_NOPM, 0, 0, + &hpo_r_switch), + SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0, + &lout_l_switch), + SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0, + &lout_r_switch), + SND_SOC_DAPM_SWITCH("PDM L Playback", SND_SOC_NOPM, 0, 0, + &pdm_l_switch), + SND_SOC_DAPM_SWITCH("PDM R Playback", SND_SOC_NOPM, 0, 0, + &pdm_r_switch), + + /* PDM */ + SND_SOC_DAPM_SUPPLY("PDM Power", RT5659_PWR_DIG_2, + RT5659_PWR_PDM1_BIT, 0, NULL, 0), + SND_SOC_DAPM_MUX("PDM L Mux", RT5659_PDM_OUT_CTRL, + RT5659_M_PDM1_L_SFT, 1, &rt5659_pdm_l_mux), + SND_SOC_DAPM_MUX("PDM R Mux", RT5659_PDM_OUT_CTRL, + RT5659_M_PDM1_R_SFT, 1, &rt5659_pdm_r_mux), + + /* SPDIF */ + SND_SOC_DAPM_MUX("SPDIF Mux", SND_SOC_NOPM, 0, 0, &rt5659_spdif_mux), + + SND_SOC_DAPM_SUPPLY("SYS CLK DET", RT5659_CLK_DET, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET", RT5659_CLK_DET, 0, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("MONOOUT"), + SND_SOC_DAPM_OUTPUT("PDML"), + SND_SOC_DAPM_OUTPUT("PDMR"), + SND_SOC_DAPM_OUTPUT("SPDIF"), +}; + +static const struct snd_soc_dapm_route rt5659_dapm_routes[] = { + /*PLL*/ + { "ADC Stereo1 Filter", NULL, "PLL", is_sys_clk_from_pll }, + { "ADC Stereo2 Filter", NULL, "PLL", is_sys_clk_from_pll }, + { "ADC Mono Left Filter", NULL, "PLL", is_sys_clk_from_pll }, + { "ADC Mono Right Filter", NULL, "PLL", is_sys_clk_from_pll }, + { "DAC Stereo1 Filter", NULL, "PLL", is_sys_clk_from_pll }, + { "DAC Mono Left Filter", NULL, "PLL", is_sys_clk_from_pll }, + { "DAC Mono Right Filter", NULL, "PLL", is_sys_clk_from_pll }, + + /*ASRC*/ + { "ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc }, + { "ADC Mono Left Filter", NULL, "ADC Mono L ASRC", is_using_asrc }, + { "ADC Mono Right Filter", NULL, "ADC Mono R ASRC", is_using_asrc }, + { "DAC Mono Left Filter", NULL, "DAC Mono L ASRC", is_using_asrc }, + { "DAC Mono Right Filter", NULL, "DAC Mono R ASRC", is_using_asrc }, + { "DAC Stereo1 Filter", NULL, "DAC STO ASRC", is_using_asrc }, + + { "SYS CLK DET", NULL, "CLKDET" }, + + { "I2S1", NULL, "I2S1 ASRC" }, + { "I2S2", NULL, "I2S2 ASRC" }, + { "I2S3", NULL, "I2S3 ASRC" }, + + { "DMIC1", NULL, "DMIC L1" }, + { "DMIC1", NULL, "DMIC R1" }, + { "DMIC2", NULL, "DMIC L2" }, + { "DMIC2", NULL, "DMIC R2" }, + + { "BST1", NULL, "IN1P" }, + { "BST1", NULL, "IN1N" }, + { "BST1", NULL, "BST1 Power" }, + { "BST2", NULL, "IN2P" }, + { "BST2", NULL, "IN2N" }, + { "BST2", NULL, "BST2 Power" }, + { "BST3", NULL, "IN3P" }, + { "BST3", NULL, "IN3N" }, + { "BST3", NULL, "BST3 Power" }, + { "BST4", NULL, "IN4P" }, + { "BST4", NULL, "IN4N" }, + { "BST4", NULL, "BST4 Power" }, + + { "INL VOL", NULL, "IN2P" }, + { "INR VOL", NULL, "IN2N" }, + + { "RECMIX1L", "SPKVOLL Switch", "SPKVOL L" }, + { "RECMIX1L", "INL Switch", "INL VOL" }, + { "RECMIX1L", "BST4 Switch", "BST4" }, + { "RECMIX1L", "BST3 Switch", "BST3" }, + { "RECMIX1L", "BST2 Switch", "BST2" }, + { "RECMIX1L", "BST1 Switch", "BST1" }, + + { "RECMIX1R", "HPOVOLR Switch", "HPO R Playback" }, + { "RECMIX1R", "INR Switch", "INR VOL" }, + { "RECMIX1R", "BST4 Switch", "BST4" }, + { "RECMIX1R", "BST3 Switch", "BST3" }, + { "RECMIX1R", "BST2 Switch", "BST2" }, + { "RECMIX1R", "BST1 Switch", "BST1" }, + + { "RECMIX2L", "SPKVOLL Switch", "SPKVOL L" }, + { "RECMIX2L", "OUTVOLL Switch", "OUTVOL L" }, + { "RECMIX2L", "BST4 Switch", "BST4" }, + { "RECMIX2L", "BST3 Switch", "BST3" }, + { "RECMIX2L", "BST2 Switch", "BST2" }, + { "RECMIX2L", "BST1 Switch", "BST1" }, + + { "RECMIX2R", "MONOVOL Switch", "MONOVOL" }, + { "RECMIX2R", "OUTVOLR Switch", "OUTVOL R" }, + { "RECMIX2R", "BST4 Switch", "BST4" }, + { "RECMIX2R", "BST3 Switch", "BST3" }, + { "RECMIX2R", "BST2 Switch", "BST2" }, + { "RECMIX2R", "BST1 Switch", "BST1" }, + + { "ADC1 L", NULL, "RECMIX1L" }, + { "ADC1 L", NULL, "ADC1 L Power" }, + { "ADC1 L", NULL, "ADC1 clock" }, + { "ADC1 R", NULL, "RECMIX1R" }, + { "ADC1 R", NULL, "ADC1 R Power" }, + { "ADC1 R", NULL, "ADC1 clock" }, + + { "ADC2 L", NULL, "RECMIX2L" }, + { "ADC2 L", NULL, "ADC2 L Power" }, + { "ADC2 L", NULL, "ADC2 clock" }, + { "ADC2 R", NULL, "RECMIX2R" }, + { "ADC2 R", NULL, "ADC2 R Power" }, + { "ADC2 R", NULL, "ADC2 clock" }, + + { "DMIC L1", NULL, "DMIC CLK" }, + { "DMIC L1", NULL, "DMIC1 Power" }, + { "DMIC R1", NULL, "DMIC CLK" }, + { "DMIC R1", NULL, "DMIC1 Power" }, + { "DMIC L2", NULL, "DMIC CLK" }, + { "DMIC L2", NULL, "DMIC2 Power" }, + { "DMIC R2", NULL, "DMIC CLK" }, + { "DMIC R2", NULL, "DMIC2 Power" }, + + { "Stereo1 DMIC L Mux", "DMIC1", "DMIC L1" }, + { "Stereo1 DMIC L Mux", "DMIC2", "DMIC L2" }, + + { "Stereo1 DMIC R Mux", "DMIC1", "DMIC R1" }, + { "Stereo1 DMIC R Mux", "DMIC2", "DMIC R2" }, + + { "Mono DMIC L Mux", "DMIC1 L", "DMIC L1" }, + { "Mono DMIC L Mux", "DMIC2 L", "DMIC L2" }, + + { "Mono DMIC R Mux", "DMIC1 R", "DMIC R1" }, + { "Mono DMIC R Mux", "DMIC2 R", "DMIC R2" }, + + { "Stereo1 ADC L Mux", "ADC1", "ADC1 L" }, + { "Stereo1 ADC L Mux", "ADC2", "ADC2 L" }, + { "Stereo1 ADC R Mux", "ADC1", "ADC1 R" }, + { "Stereo1 ADC R Mux", "ADC2", "ADC2 R" }, + + { "Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux" }, + { "Stereo1 ADC L1 Mux", "DAC MIX", "DAC MIXL" }, + { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC L Mux" }, + { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" }, + + { "Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux" }, + { "Stereo1 ADC R1 Mux", "DAC MIX", "DAC MIXR" }, + { "Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC R Mux" }, + { "Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR" }, + + { "Mono ADC L Mux", "ADC1 L", "ADC1 L" }, + { "Mono ADC L Mux", "ADC1 R", "ADC1 R" }, + { "Mono ADC L Mux", "ADC2 L", "ADC2 L" }, + { "Mono ADC L Mux", "ADC2 R", "ADC2 R" }, + + { "Mono ADC R Mux", "ADC1 L", "ADC1 L" }, + { "Mono ADC R Mux", "ADC1 R", "ADC1 R" }, + { "Mono ADC R Mux", "ADC2 L", "ADC2 L" }, + { "Mono ADC R Mux", "ADC2 R", "ADC2 R" }, + + { "Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux" }, + { "Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL" }, + { "Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL" }, + { "Mono ADC L1 Mux", "ADC", "Mono ADC L Mux" }, + + { "Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR" }, + { "Mono ADC R1 Mux", "ADC", "Mono ADC R Mux" }, + { "Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux" }, + { "Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR" }, + + { "Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux" }, + { "Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux" }, + { "Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter" }, + + { "Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux" }, + { "Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux" }, + { "Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter" }, + + { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux" }, + { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux" }, + { "Mono ADC MIXL", NULL, "ADC Mono Left Filter" }, + + { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux" }, + { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux" }, + { "Mono ADC MIXR", NULL, "ADC Mono Right Filter" }, + + { "Stereo1 ADC Volume L", NULL, "Stereo1 ADC MIXL" }, + { "Stereo1 ADC Volume R", NULL, "Stereo1 ADC MIXR" }, + + { "IF_ADC1", NULL, "Stereo1 ADC Volume L" }, + { "IF_ADC1", NULL, "Stereo1 ADC Volume R" }, + { "IF_ADC2", NULL, "Mono ADC MIXL" }, + { "IF_ADC2", NULL, "Mono ADC MIXR" }, + + { "TDM AD1:AD2:DAC", NULL, "IF_ADC1" }, + { "TDM AD1:AD2:DAC", NULL, "IF_ADC2" }, + { "TDM AD1:AD2:DAC", NULL, "DAC_REF" }, + { "TDM AD2:DAC", NULL, "IF_ADC2" }, + { "TDM AD2:DAC", NULL, "DAC_REF" }, + { "TDM Data Mux", "AD1:AD2:DAC:NUL", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD1:AD2:NUL:DAC", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD1:DAC:AD2:NUL", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD1:DAC:NUL:AD2", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD1:NUL:DAC:AD2", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD1:NUL:AD2:DAC", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD2:AD1:DAC:NUL", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD2:AD1:NUL:DAC", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD2:DAC:AD1:NUL", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD2:DAC:NUL:AD1", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD2:NUL:DAC:AD1", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "AD1:NUL:AD1:DAC", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "DAC:AD1:AD2:NUL", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "DAC:AD1:NUL:AD2", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "DAC:AD2:AD1:NUL", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "DAC:AD2:NUL:AD1", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "DAC:NUL:DAC:AD2", "TDM AD2:DAC" }, + { "TDM Data Mux", "DAC:NUL:AD2:DAC", "TDM AD2:DAC" }, + { "TDM Data Mux", "NUL:AD1:AD2:DAC", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "NUL:AD1:DAC:AD2", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "NUL:AD2:AD1:DAC", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "NUL:AD2:DAC:AD1", "TDM AD1:AD2:DAC" }, + { "TDM Data Mux", "NUL:DAC:DAC:AD2", "TDM AD2:DAC" }, + { "TDM Data Mux", "NUL:DAC:AD2:DAC", "TDM AD2:DAC" }, + { "IF1 01 ADC Swap Mux", "L/R", "TDM Data Mux" }, + { "IF1 01 ADC Swap Mux", "R/L", "TDM Data Mux" }, + { "IF1 01 ADC Swap Mux", "L/L", "TDM Data Mux" }, + { "IF1 01 ADC Swap Mux", "R/R", "TDM Data Mux" }, + { "IF1 23 ADC Swap Mux", "L/R", "TDM Data Mux" }, + { "IF1 23 ADC Swap Mux", "R/L", "TDM Data Mux" }, + { "IF1 23 ADC Swap Mux", "L/L", "TDM Data Mux" }, + { "IF1 23 ADC Swap Mux", "R/R", "TDM Data Mux" }, + { "IF1 45 ADC Swap Mux", "L/R", "TDM Data Mux" }, + { "IF1 45 ADC Swap Mux", "R/L", "TDM Data Mux" }, + { "IF1 45 ADC Swap Mux", "L/L", "TDM Data Mux" }, + { "IF1 45 ADC Swap Mux", "R/R", "TDM Data Mux" }, + { "IF1 67 ADC Swap Mux", "L/R", "TDM Data Mux" }, + { "IF1 67 ADC Swap Mux", "R/L", "TDM Data Mux" }, + { "IF1 67 ADC Swap Mux", "L/L", "TDM Data Mux" }, + { "IF1 67 ADC Swap Mux", "R/R", "TDM Data Mux" }, + { "IF1 ADC", NULL, "IF1 01 ADC Swap Mux" }, + { "IF1 ADC", NULL, "IF1 23 ADC Swap Mux" }, + { "IF1 ADC", NULL, "IF1 45 ADC Swap Mux" }, + { "IF1 ADC", NULL, "IF1 67 ADC Swap Mux" }, + { "IF1 ADC", NULL, "I2S1" }, + + { "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" }, + { "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" }, + { "IF2 ADC Mux", "IF_ADC3", "IF_ADC3" }, + { "IF2 ADC Mux", "DAC_REF", "DAC_REF" }, + { "IF2 ADC", NULL, "IF2 ADC Mux"}, + { "IF2 ADC", NULL, "I2S2" }, + + { "IF3 ADC Mux", "IF_ADC1", "IF_ADC1" }, + { "IF3 ADC Mux", "IF_ADC2", "IF_ADC2" }, + { "IF3 ADC Mux", "Stereo2_ADC_L/R", "Stereo2 ADC LR" }, + { "IF3 ADC Mux", "DAC_REF", "DAC_REF" }, + { "IF3 ADC", NULL, "IF3 ADC Mux"}, + { "IF3 ADC", NULL, "I2S3" }, + + { "AIF1TX", NULL, "IF1 ADC" }, + { "IF2 ADC Swap Mux", "L/R", "IF2 ADC" }, + { "IF2 ADC Swap Mux", "R/L", "IF2 ADC" }, + { "IF2 ADC Swap Mux", "L/L", "IF2 ADC" }, + { "IF2 ADC Swap Mux", "R/R", "IF2 ADC" }, + { "AIF2TX", NULL, "IF2 ADC Swap Mux" }, + { "IF3 ADC Swap Mux", "L/R", "IF3 ADC" }, + { "IF3 ADC Swap Mux", "R/L", "IF3 ADC" }, + { "IF3 ADC Swap Mux", "L/L", "IF3 ADC" }, + { "IF3 ADC Swap Mux", "R/R", "IF3 ADC" }, + { "AIF3TX", NULL, "IF3 ADC Swap Mux" }, + + { "IF1 DAC1", NULL, "AIF1RX" }, + { "IF1 DAC2", NULL, "AIF1RX" }, + { "IF2 DAC Swap Mux", "L/R", "AIF2RX" }, + { "IF2 DAC Swap Mux", "R/L", "AIF2RX" }, + { "IF2 DAC Swap Mux", "L/L", "AIF2RX" }, + { "IF2 DAC Swap Mux", "R/R", "AIF2RX" }, + { "IF2 DAC", NULL, "IF2 DAC Swap Mux" }, + { "IF3 DAC Swap Mux", "L/R", "AIF3RX" }, + { "IF3 DAC Swap Mux", "R/L", "AIF3RX" }, + { "IF3 DAC Swap Mux", "L/L", "AIF3RX" }, + { "IF3 DAC Swap Mux", "R/R", "AIF3RX" }, + { "IF3 DAC", NULL, "IF3 DAC Swap Mux" }, + + { "IF1 DAC1", NULL, "I2S1" }, + { "IF1 DAC2", NULL, "I2S1" }, + { "IF2 DAC", NULL, "I2S2" }, + { "IF3 DAC", NULL, "I2S3" }, + + { "IF1 DAC2 L", NULL, "IF1 DAC2" }, + { "IF1 DAC2 R", NULL, "IF1 DAC2" }, + { "IF1 DAC1 L", NULL, "IF1 DAC1" }, + { "IF1 DAC1 R", NULL, "IF1 DAC1" }, + { "IF2 DAC L", NULL, "IF2 DAC" }, + { "IF2 DAC R", NULL, "IF2 DAC" }, + { "IF3 DAC L", NULL, "IF3 DAC" }, + { "IF3 DAC R", NULL, "IF3 DAC" }, + + { "DAC L1 Mux", "IF1 DAC1", "IF1 DAC1 L" }, + { "DAC L1 Mux", "IF2 DAC", "IF2 DAC L" }, + { "DAC L1 Mux", "IF3 DAC", "IF3 DAC L" }, + { "DAC L1 Mux", NULL, "DAC Stereo1 Filter" }, + + { "DAC R1 Mux", "IF1 DAC1", "IF1 DAC1 R" }, + { "DAC R1 Mux", "IF2 DAC", "IF2 DAC R" }, + { "DAC R1 Mux", "IF3 DAC", "IF3 DAC R" }, + { "DAC R1 Mux", NULL, "DAC Stereo1 Filter" }, + + { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC Volume L" }, + { "DAC1 MIXL", "DAC1 Switch", "DAC L1 Mux" }, + { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC Volume R" }, + { "DAC1 MIXR", "DAC1 Switch", "DAC R1 Mux" }, + + { "DAC_REF", NULL, "DAC1 MIXL" }, + { "DAC_REF", NULL, "DAC1 MIXR" }, + + { "DAC L2 Mux", "IF1 DAC2", "IF1 DAC2 L" }, + { "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" }, + { "DAC L2 Mux", "IF3 DAC", "IF3 DAC L" }, + { "DAC L2 Mux", "Mono ADC MIX", "Mono ADC MIXL" }, + { "DAC L2 Mux", NULL, "DAC Mono Left Filter" }, + + { "DAC R2 Mux", "IF1 DAC2", "IF1 DAC2 R" }, + { "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" }, + { "DAC R2 Mux", "IF3 DAC", "IF3 DAC R" }, + { "DAC R2 Mux", "Mono ADC MIX", "Mono ADC MIXR" }, + { "DAC R2 Mux", NULL, "DAC Mono Right Filter" }, + + { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux" }, + { "Stereo DAC MIXL", "DAC R2 Switch", "DAC R2 Mux" }, + + { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXR", "DAC L2 Switch", "DAC L2 Mux" }, + { "Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Mux" }, + + { "Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Mono DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" }, + { "Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux" }, + { "Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux" }, + { "Mono DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" }, + { "Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux" }, + { "Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux" }, + + { "DAC MIXL", "Stereo DAC Mixer", "Stereo DAC MIXL" }, + { "DAC MIXL", "Mono DAC Mixer", "Mono DAC MIXL" }, + { "DAC MIXR", "Stereo DAC Mixer", "Stereo DAC MIXR" }, + { "DAC MIXR", "Mono DAC Mixer", "Mono DAC MIXR" }, + + { "DAC L1 Source", NULL, "DAC L1 Power" }, + { "DAC L1 Source", "DAC", "DAC1 MIXL" }, + { "DAC L1 Source", "Stereo DAC Mixer", "Stereo DAC MIXL" }, + { "DAC R1 Source", NULL, "DAC R1 Power" }, + { "DAC R1 Source", "DAC", "DAC1 MIXR" }, + { "DAC R1 Source", "Stereo DAC Mixer", "Stereo DAC MIXR" }, + { "DAC L2 Source", "Stereo DAC Mixer", "Stereo DAC MIXL" }, + { "DAC L2 Source", "Mono DAC Mixer", "Mono DAC MIXL" }, + { "DAC L2 Source", NULL, "DAC L2 Power" }, + { "DAC R2 Source", "Stereo DAC Mixer", "Stereo DAC MIXR" }, + { "DAC R2 Source", "Mono DAC Mixer", "Mono DAC MIXR" }, + { "DAC R2 Source", NULL, "DAC R2 Power" }, + + { "DAC L1", NULL, "DAC L1 Source" }, + { "DAC R1", NULL, "DAC R1 Source" }, + { "DAC L2", NULL, "DAC L2 Source" }, + { "DAC R2", NULL, "DAC R2 Source" }, + + { "SPK MIXL", "DAC L2 Switch", "DAC L2" }, + { "SPK MIXL", "BST1 Switch", "BST1" }, + { "SPK MIXL", "INL Switch", "INL VOL" }, + { "SPK MIXL", "INR Switch", "INR VOL" }, + { "SPK MIXL", "BST3 Switch", "BST3" }, + { "SPK MIXR", "DAC R2 Switch", "DAC R2" }, + { "SPK MIXR", "BST4 Switch", "BST4" }, + { "SPK MIXR", "INL Switch", "INL VOL" }, + { "SPK MIXR", "INR Switch", "INR VOL" }, + { "SPK MIXR", "BST3 Switch", "BST3" }, + + { "MONOVOL MIX", "DAC L2 Switch", "DAC L2" }, + { "MONOVOL MIX", "DAC R2 Switch", "DAC R2" }, + { "MONOVOL MIX", "BST1 Switch", "BST1" }, + { "MONOVOL MIX", "BST2 Switch", "BST2" }, + { "MONOVOL MIX", "BST3 Switch", "BST3" }, + + { "OUT MIXL", "DAC L2 Switch", "DAC L2" }, + { "OUT MIXL", "INL Switch", "INL VOL" }, + { "OUT MIXL", "BST1 Switch", "BST1" }, + { "OUT MIXL", "BST2 Switch", "BST2" }, + { "OUT MIXL", "BST3 Switch", "BST3" }, + { "OUT MIXR", "DAC R2 Switch", "DAC R2" }, + { "OUT MIXR", "INR Switch", "INR VOL" }, + { "OUT MIXR", "BST2 Switch", "BST2" }, + { "OUT MIXR", "BST3 Switch", "BST3" }, + { "OUT MIXR", "BST4 Switch", "BST4" }, + + { "SPKVOL L", "Switch", "SPK MIXL" }, + { "SPKVOL R", "Switch", "SPK MIXR" }, + { "SPO L MIX", "DAC L2 Switch", "DAC L2" }, + { "SPO L MIX", "SPKVOL L Switch", "SPKVOL L" }, + { "SPO R MIX", "DAC R2 Switch", "DAC R2" }, + { "SPO R MIX", "SPKVOL R Switch", "SPKVOL R" }, + { "SPK Amp", NULL, "SPO L MIX" }, + { "SPK Amp", NULL, "SPO R MIX" }, + { "SPK Amp", NULL, "SYS CLK DET" }, + { "SPO Playback", "Switch", "SPK Amp" }, + { "SPOL", NULL, "SPO Playback" }, + { "SPOR", NULL, "SPO Playback" }, + + { "MONOVOL", "Switch", "MONOVOL MIX" }, + { "Mono MIX", "DAC L2 Switch", "DAC L2" }, + { "Mono MIX", "MONOVOL Switch", "MONOVOL" }, + { "Mono Amp", NULL, "Mono MIX" }, + { "Mono Amp", NULL, "Mono Vref" }, + { "Mono Amp", NULL, "SYS CLK DET" }, + { "Mono Playback", "Switch", "Mono Amp" }, + { "MONOOUT", NULL, "Mono Playback" }, + + { "HP Amp", NULL, "DAC L1" }, + { "HP Amp", NULL, "DAC R1" }, + { "HP Amp", NULL, "Charge Pump" }, + { "HP Amp", NULL, "SYS CLK DET" }, + { "HPO L Playback", "Switch", "HP Amp"}, + { "HPO R Playback", "Switch", "HP Amp"}, + { "HPOL", NULL, "HPO L Playback" }, + { "HPOR", NULL, "HPO R Playback" }, + + { "OUTVOL L", "Switch", "OUT MIXL" }, + { "OUTVOL R", "Switch", "OUT MIXR" }, + { "LOUT L MIX", "DAC L2 Switch", "DAC L2" }, + { "LOUT L MIX", "OUTVOL L Switch", "OUTVOL L" }, + { "LOUT R MIX", "DAC R2 Switch", "DAC R2" }, + { "LOUT R MIX", "OUTVOL R Switch", "OUTVOL R" }, + { "LOUT Amp", NULL, "LOUT L MIX" }, + { "LOUT Amp", NULL, "LOUT R MIX" }, + { "LOUT Amp", NULL, "Charge Pump" }, + { "LOUT Amp", NULL, "SYS CLK DET" }, + { "LOUT L Playback", "Switch", "LOUT Amp" }, + { "LOUT R Playback", "Switch", "LOUT Amp" }, + { "LOUTL", NULL, "LOUT L Playback" }, + { "LOUTR", NULL, "LOUT R Playback" }, + + { "PDM L Mux", "Mono DAC", "Mono DAC MIXL" }, + { "PDM L Mux", "Stereo DAC", "Stereo DAC MIXL" }, + { "PDM L Mux", NULL, "PDM Power" }, + { "PDM R Mux", "Mono DAC", "Mono DAC MIXR" }, + { "PDM R Mux", "Stereo DAC", "Stereo DAC MIXR" }, + { "PDM R Mux", NULL, "PDM Power" }, + { "PDM L Playback", "Switch", "PDM L Mux" }, + { "PDM R Playback", "Switch", "PDM R Mux" }, + { "PDML", NULL, "PDM L Playback" }, + { "PDMR", NULL, "PDM R Playback" }, + + { "SPDIF Mux", "IF3_DAC", "IF3 DAC" }, + { "SPDIF Mux", "IF2_DAC", "IF2 DAC" }, + { "SPDIF Mux", "IF1_DAC2", "IF1 DAC2" }, + { "SPDIF Mux", "IF1_DAC1", "IF1 DAC1" }, + { "SPDIF", NULL, "SPDIF Mux" }, +}; + +static int rt5659_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, frame_size; + + rt5659->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5659->sysclk, rt5659->lrck[dai->id]); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n", + rt5659->lrck[dai->id], dai->id); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + + dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt5659->lrck[dai->id], pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= RT5659_I2S_DL_20; + break; + case 24: + val_len |= RT5659_I2S_DL_24; + break; + case 8: + val_len |= RT5659_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5659_AIF1: + mask_clk = RT5659_I2S_PD1_MASK; + val_clk = pre_div << RT5659_I2S_PD1_SFT; + snd_soc_component_update_bits(component, RT5659_I2S1_SDP, + RT5659_I2S_DL_MASK, val_len); + break; + case RT5659_AIF2: + mask_clk = RT5659_I2S_PD2_MASK; + val_clk = pre_div << RT5659_I2S_PD2_SFT; + snd_soc_component_update_bits(component, RT5659_I2S2_SDP, + RT5659_I2S_DL_MASK, val_len); + break; + case RT5659_AIF3: + mask_clk = RT5659_I2S_PD3_MASK; + val_clk = pre_div << RT5659_I2S_PD3_SFT; + snd_soc_component_update_bits(component, RT5659_I2S3_SDP, + RT5659_I2S_DL_MASK, val_len); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1, mask_clk, val_clk); + + switch (rt5659->lrck[dai->id]) { + case 192000: + snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1, + RT5659_DAC_OSR_MASK, RT5659_DAC_OSR_32); + break; + case 96000: + snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1, + RT5659_DAC_OSR_MASK, RT5659_DAC_OSR_64); + break; + default: + snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1, + RT5659_DAC_OSR_MASK, RT5659_DAC_OSR_128); + break; + } + + return 0; +} + +static int rt5659_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5659->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5659_I2S_MS_S; + rt5659->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5659_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5659_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5659_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5659_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5659_AIF1: + snd_soc_component_update_bits(component, RT5659_I2S1_SDP, + RT5659_I2S_MS_MASK | RT5659_I2S_BP_MASK | + RT5659_I2S_DF_MASK, reg_val); + break; + case RT5659_AIF2: + snd_soc_component_update_bits(component, RT5659_I2S2_SDP, + RT5659_I2S_MS_MASK | RT5659_I2S_BP_MASK | + RT5659_I2S_DF_MASK, reg_val); + break; + case RT5659_AIF3: + snd_soc_component_update_bits(component, RT5659_I2S3_SDP, + RT5659_I2S_MS_MASK | RT5659_I2S_BP_MASK | + RT5659_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5659_set_component_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + int ret; + + if (freq == rt5659->sysclk && clk_id == rt5659->sysclk_src) + return 0; + + switch (clk_id) { + case RT5659_SCLK_S_MCLK: + ret = clk_set_rate(rt5659->mclk, freq); + if (ret) + return ret; + + reg_val |= RT5659_SCLK_SRC_MCLK; + break; + case RT5659_SCLK_S_PLL1: + reg_val |= RT5659_SCLK_SRC_PLL1; + break; + case RT5659_SCLK_S_RCCLK: + reg_val |= RT5659_SCLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5659_GLB_CLK, + RT5659_SCLK_SRC_MASK, reg_val); + rt5659->sysclk = freq; + rt5659->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt5659_set_component_pll(struct snd_soc_component *component, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5659->pll_src && freq_in == rt5659->pll_in && + freq_out == rt5659->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5659->pll_in = 0; + rt5659->pll_out = 0; + snd_soc_component_update_bits(component, RT5659_GLB_CLK, + RT5659_SCLK_SRC_MASK, RT5659_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5659_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5659_GLB_CLK, + RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_MCLK); + break; + case RT5659_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, RT5659_GLB_CLK, + RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK1); + break; + case RT5659_PLL1_S_BCLK2: + snd_soc_component_update_bits(component, RT5659_GLB_CLK, + RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK2); + break; + case RT5659_PLL1_S_BCLK3: + snd_soc_component_update_bits(component, RT5659_GLB_CLK, + RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK3); + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5659_PLL_CTRL_1, + pll_code.n_code << RT5659_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5659_PLL_CTRL_2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5659_PLL_M_SFT) | + (pll_code.m_bp << RT5659_PLL_M_BP_SFT)); + + rt5659->pll_in = freq_in; + rt5659->pll_out = freq_out; + rt5659->pll_src = source; + + return 0; +} + +static int rt5659_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int val = 0; + + if (rx_mask || tx_mask) + val |= (1 << 15); + + switch (slots) { + case 4: + val |= (1 << 10); + val |= (1 << 8); + break; + case 6: + val |= (2 << 10); + val |= (2 << 8); + break; + case 8: + val |= (3 << 10); + val |= (3 << 8); + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + val |= (1 << 6); + val |= (1 << 4); + break; + case 24: + val |= (2 << 6); + val |= (2 << 4); + break; + case 32: + val |= (3 << 6); + val |= (3 << 4); + break; + case 16: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5659_TDM_CTRL_1, 0x8ff0, val); + + return 0; +} + +static int rt5659_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio); + + rt5659->bclk[dai->id] = ratio; + + if (ratio == 64) { + switch (dai->id) { + case RT5659_AIF2: + snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1, + RT5659_I2S_BCLK_MS2_MASK, + RT5659_I2S_BCLK_MS2_64); + break; + case RT5659_AIF3: + snd_soc_component_update_bits(component, RT5659_ADDA_CLK_1, + RT5659_I2S_BCLK_MS3_MASK, + RT5659_I2S_BCLK_MS3_64); + break; + } + } + + return 0; +} + +static int rt5659_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(rt5659->regmap, RT5659_DIG_MISC, + RT5659_DIG_GATE_CTRL, RT5659_DIG_GATE_CTRL); + regmap_update_bits(rt5659->regmap, RT5659_PWR_DIG_1, + RT5659_PWR_LDO, RT5659_PWR_LDO); + regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_1, + RT5659_PWR_MB | RT5659_PWR_VREF1 | RT5659_PWR_VREF2, + RT5659_PWR_MB | RT5659_PWR_VREF1 | RT5659_PWR_VREF2); + msleep(20); + regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_1, + RT5659_PWR_FV1 | RT5659_PWR_FV2, + RT5659_PWR_FV1 | RT5659_PWR_FV2); + break; + + case SND_SOC_BIAS_STANDBY: + if (dapm->bias_level == SND_SOC_BIAS_OFF) { + ret = clk_prepare_enable(rt5659->mclk); + if (ret) { + dev_err(component->dev, + "failed to enable MCLK: %d\n", ret); + return ret; + } + } + break; + + case SND_SOC_BIAS_OFF: + regmap_update_bits(rt5659->regmap, RT5659_PWR_DIG_1, + RT5659_PWR_LDO, 0); + regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_1, + RT5659_PWR_MB | RT5659_PWR_VREF1 | RT5659_PWR_VREF2 + | RT5659_PWR_FV1 | RT5659_PWR_FV2, + RT5659_PWR_MB | RT5659_PWR_VREF2); + regmap_update_bits(rt5659->regmap, RT5659_DIG_MISC, + RT5659_DIG_GATE_CTRL, 0); + clk_disable_unprepare(rt5659->mclk); + break; + + default: + break; + } + + return 0; +} + +static int rt5659_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + + rt5659->component = component; + + switch (rt5659->pdata.jd_src) { + case RT5659_JD_HDA_HEADER: + break; + + default: + snd_soc_dapm_new_controls(dapm, + rt5659_particular_dapm_widgets, + ARRAY_SIZE(rt5659_particular_dapm_widgets)); + break; + } + + return 0; +} + +static void rt5659_remove(struct snd_soc_component *component) +{ + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + + regmap_write(rt5659->regmap, RT5659_RESET, 0); +} + +#ifdef CONFIG_PM +static int rt5659_suspend(struct snd_soc_component *component) +{ + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5659->regmap, true); + regcache_mark_dirty(rt5659->regmap); + return 0; +} + +static int rt5659_resume(struct snd_soc_component *component) +{ + struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5659->regmap, false); + regcache_sync(rt5659->regmap); + + return 0; +} +#else +#define rt5659_suspend NULL +#define rt5659_resume NULL +#endif + +#define RT5659_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5659_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5659_aif_dai_ops = { + .hw_params = rt5659_hw_params, + .set_fmt = rt5659_set_dai_fmt, + .set_tdm_slot = rt5659_set_tdm_slot, + .set_bclk_ratio = rt5659_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5659_dai[] = { + { + .name = "rt5659-aif1", + .id = RT5659_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5659_STEREO_RATES, + .formats = RT5659_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5659_STEREO_RATES, + .formats = RT5659_FORMATS, + }, + .ops = &rt5659_aif_dai_ops, + }, + { + .name = "rt5659-aif2", + .id = RT5659_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5659_STEREO_RATES, + .formats = RT5659_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5659_STEREO_RATES, + .formats = RT5659_FORMATS, + }, + .ops = &rt5659_aif_dai_ops, + }, + { + .name = "rt5659-aif3", + .id = RT5659_AIF3, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5659_STEREO_RATES, + .formats = RT5659_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5659_STEREO_RATES, + .formats = RT5659_FORMATS, + }, + .ops = &rt5659_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5659 = { + .probe = rt5659_probe, + .remove = rt5659_remove, + .suspend = rt5659_suspend, + .resume = rt5659_resume, + .set_bias_level = rt5659_set_bias_level, + .controls = rt5659_snd_controls, + .num_controls = ARRAY_SIZE(rt5659_snd_controls), + .dapm_widgets = rt5659_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5659_dapm_widgets), + .dapm_routes = rt5659_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5659_dapm_routes), + .set_sysclk = rt5659_set_component_sysclk, + .set_pll = rt5659_set_component_pll, + .use_pmdown_time = 1, + .endianness = 1, +}; + + +static const struct regmap_config rt5659_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = 0x0400, + .volatile_reg = rt5659_volatile_register, + .readable_reg = rt5659_readable_register, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5659_reg, + .num_reg_defaults = ARRAY_SIZE(rt5659_reg), +}; + +static const struct i2c_device_id rt5659_i2c_id[] = { + { "rt5658", 0 }, + { "rt5659", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5659_i2c_id); + +static int rt5659_parse_dt(struct rt5659_priv *rt5659, struct device *dev) +{ + rt5659->pdata.in1_diff = device_property_read_bool(dev, + "realtek,in1-differential"); + rt5659->pdata.in3_diff = device_property_read_bool(dev, + "realtek,in3-differential"); + rt5659->pdata.in4_diff = device_property_read_bool(dev, + "realtek,in4-differential"); + + + device_property_read_u32(dev, "realtek,dmic1-data-pin", + &rt5659->pdata.dmic1_data_pin); + device_property_read_u32(dev, "realtek,dmic2-data-pin", + &rt5659->pdata.dmic2_data_pin); + device_property_read_u32(dev, "realtek,jd-src", + &rt5659->pdata.jd_src); + + return 0; +} + +static void rt5659_calibrate(struct rt5659_priv *rt5659) +{ + int value, count; + + /* Calibrate HPO Start */ + /* Fine tune HP Performance */ + regmap_write(rt5659->regmap, RT5659_BIAS_CUR_CTRL_8, 0xa502); + regmap_write(rt5659->regmap, RT5659_CHOP_DAC, 0x3030); + + regmap_write(rt5659->regmap, RT5659_PRE_DIV_1, 0xef00); + regmap_write(rt5659->regmap, RT5659_PRE_DIV_2, 0xeffc); + regmap_write(rt5659->regmap, RT5659_MICBIAS_2, 0x0280); + regmap_write(rt5659->regmap, RT5659_DIG_MISC, 0x0001); + regmap_write(rt5659->regmap, RT5659_GLB_CLK, 0x8000); + + regmap_write(rt5659->regmap, RT5659_PWR_ANLG_1, 0xaa7e); + msleep(60); + regmap_write(rt5659->regmap, RT5659_PWR_ANLG_1, 0xfe7e); + msleep(50); + regmap_write(rt5659->regmap, RT5659_PWR_ANLG_3, 0x0004); + regmap_write(rt5659->regmap, RT5659_PWR_DIG_2, 0x0400); + msleep(50); + regmap_write(rt5659->regmap, RT5659_PWR_DIG_1, 0x0080); + usleep_range(10000, 10005); + regmap_write(rt5659->regmap, RT5659_DEPOP_1, 0x0009); + msleep(50); + regmap_write(rt5659->regmap, RT5659_PWR_DIG_1, 0x0f80); + msleep(50); + regmap_write(rt5659->regmap, RT5659_HP_CHARGE_PUMP_1, 0x0e16); + msleep(50); + + /* Enalbe K ADC Power And Clock */ + regmap_write(rt5659->regmap, RT5659_CAL_REC, 0x0505); + msleep(50); + regmap_write(rt5659->regmap, RT5659_PWR_ANLG_3, 0x0184); + regmap_write(rt5659->regmap, RT5659_CALIB_ADC_CTRL, 0x3c05); + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_2, 0x20c1); + + /* K Headphone */ + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_2, 0x2cc1); + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_1, 0x5100); + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_7, 0x0014); + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_1, 0xd100); + msleep(60); + + /* Manual K ADC Offset */ + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_2, 0x2cc1); + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_1, 0x4900); + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_7, 0x0016); + regmap_update_bits(rt5659->regmap, RT5659_HP_CALIB_CTRL_1, + 0x8000, 0x8000); + + count = 0; + while (true) { + regmap_read(rt5659->regmap, RT5659_HP_CALIB_CTRL_1, &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 30) { + dev_err(rt5659->component->dev, + "HP Calibration 1 Failure\n"); + return; + } + + count++; + } + + /* Manual K Internal Path Offset */ + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_2, 0x2cc1); + regmap_write(rt5659->regmap, RT5659_HP_VOL, 0x0000); + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_1, 0x4500); + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_7, 0x001f); + regmap_update_bits(rt5659->regmap, RT5659_HP_CALIB_CTRL_1, + 0x8000, 0x8000); + + count = 0; + while (true) { + regmap_read(rt5659->regmap, RT5659_HP_CALIB_CTRL_1, &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 85) { + dev_err(rt5659->component->dev, + "HP Calibration 2 Failure\n"); + return; + } + + count++; + } + + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_7, 0x0000); + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_2, 0x20c0); + /* Calibrate HPO End */ + + /* Calibrate SPO Start */ + regmap_write(rt5659->regmap, RT5659_CLASSD_0, 0x2021); + regmap_write(rt5659->regmap, RT5659_CLASSD_CTRL_1, 0x0260); + regmap_write(rt5659->regmap, RT5659_PWR_MIXER, 0x3000); + regmap_write(rt5659->regmap, RT5659_PWR_VOL, 0xc000); + regmap_write(rt5659->regmap, RT5659_A_DAC_MUX, 0x000c); + regmap_write(rt5659->regmap, RT5659_DIG_MISC, 0x8000); + regmap_write(rt5659->regmap, RT5659_SPO_VOL, 0x0808); + regmap_write(rt5659->regmap, RT5659_SPK_L_MIXER, 0x001e); + regmap_write(rt5659->regmap, RT5659_SPK_R_MIXER, 0x001e); + regmap_write(rt5659->regmap, RT5659_CLASSD_1, 0x0803); + regmap_write(rt5659->regmap, RT5659_CLASSD_2, 0x0554); + regmap_write(rt5659->regmap, RT5659_SPO_AMP_GAIN, 0x1103); + + /* Enalbe K ADC Power And Clock */ + regmap_write(rt5659->regmap, RT5659_CAL_REC, 0x0909); + regmap_update_bits(rt5659->regmap, RT5659_HP_CALIB_CTRL_2, 0x0001, + 0x0001); + + /* Start Calibration */ + regmap_write(rt5659->regmap, RT5659_SPK_DC_CAILB_CTRL_3, 0x0000); + regmap_write(rt5659->regmap, RT5659_CLASSD_0, 0x0021); + regmap_write(rt5659->regmap, RT5659_SPK_DC_CAILB_CTRL_1, 0x3e80); + regmap_update_bits(rt5659->regmap, RT5659_SPK_DC_CAILB_CTRL_1, + 0x8000, 0x8000); + + count = 0; + while (true) { + regmap_read(rt5659->regmap, + RT5659_SPK_DC_CAILB_CTRL_1, &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 10) { + dev_err(rt5659->component->dev, + "SPK Calibration Failure\n"); + return; + } + + count++; + } + /* Calibrate SPO End */ + + /* Calibrate MONO Start */ + regmap_write(rt5659->regmap, RT5659_DIG_MISC, 0x0000); + regmap_write(rt5659->regmap, RT5659_MONOMIX_IN_GAIN, 0x021f); + regmap_write(rt5659->regmap, RT5659_MONO_OUT, 0x480a); + /* MONO NG2 GAIN 5dB */ + regmap_write(rt5659->regmap, RT5659_MONO_GAIN, 0x0003); + regmap_write(rt5659->regmap, RT5659_MONO_NG2_CTRL_5, 0x0009); + + /* Start Calibration */ + regmap_write(rt5659->regmap, RT5659_SPK_DC_CAILB_CTRL_3, 0x000f); + regmap_write(rt5659->regmap, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e00); + regmap_update_bits(rt5659->regmap, RT5659_MONO_AMP_CALIB_CTRL_1, + 0x8000, 0x8000); + + count = 0; + while (true) { + regmap_read(rt5659->regmap, RT5659_MONO_AMP_CALIB_CTRL_1, + &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 35) { + dev_err(rt5659->component->dev, + "Mono Calibration Failure\n"); + return; + } + + count++; + } + + regmap_write(rt5659->regmap, RT5659_SPK_DC_CAILB_CTRL_3, 0x0003); + /* Calibrate MONO End */ + + /* Power Off */ + regmap_write(rt5659->regmap, RT5659_CAL_REC, 0x0808); + regmap_write(rt5659->regmap, RT5659_PWR_ANLG_3, 0x0000); + regmap_write(rt5659->regmap, RT5659_CALIB_ADC_CTRL, 0x2005); + regmap_write(rt5659->regmap, RT5659_HP_CALIB_CTRL_2, 0x20c0); + regmap_write(rt5659->regmap, RT5659_DEPOP_1, 0x0000); + regmap_write(rt5659->regmap, RT5659_CLASSD_1, 0x0011); + regmap_write(rt5659->regmap, RT5659_CLASSD_2, 0x0150); + regmap_write(rt5659->regmap, RT5659_PWR_ANLG_1, 0xfe3e); + regmap_write(rt5659->regmap, RT5659_MONO_OUT, 0xc80a); + regmap_write(rt5659->regmap, RT5659_MONO_AMP_CALIB_CTRL_1, 0x1e04); + regmap_write(rt5659->regmap, RT5659_PWR_MIXER, 0x0000); + regmap_write(rt5659->regmap, RT5659_PWR_VOL, 0x0000); + regmap_write(rt5659->regmap, RT5659_PWR_DIG_1, 0x0000); + regmap_write(rt5659->regmap, RT5659_PWR_DIG_2, 0x0000); + regmap_write(rt5659->regmap, RT5659_PWR_ANLG_1, 0x003e); + regmap_write(rt5659->regmap, RT5659_CLASSD_CTRL_1, 0x0060); + regmap_write(rt5659->regmap, RT5659_CLASSD_0, 0x2021); + regmap_write(rt5659->regmap, RT5659_GLB_CLK, 0x0000); + regmap_write(rt5659->regmap, RT5659_MICBIAS_2, 0x0080); + regmap_write(rt5659->regmap, RT5659_HP_VOL, 0x8080); + regmap_write(rt5659->regmap, RT5659_HP_CHARGE_PUMP_1, 0x0c16); +} + +static void rt5659_intel_hd_header_probe_setup(struct rt5659_priv *rt5659) +{ + int value; + + regmap_read(rt5659->regmap, RT5659_GPIO_STA, &value); + if (!(value & 0x8)) { + rt5659->hda_hp_plugged = true; + regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1, + 0x10, 0x0); + } else { + regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_1, + 0x10, 0x10); + } + + regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_1, + RT5659_PWR_VREF2 | RT5659_PWR_MB, + RT5659_PWR_VREF2 | RT5659_PWR_MB); + msleep(20); + regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_1, + RT5659_PWR_FV2, RT5659_PWR_FV2); + + regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_3, RT5659_PWR_LDO2, + RT5659_PWR_LDO2); + regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_2, RT5659_PWR_MB1, + RT5659_PWR_MB1); + regmap_update_bits(rt5659->regmap, RT5659_PWR_VOL, RT5659_PWR_MIC_DET, + RT5659_PWR_MIC_DET); + msleep(20); + + regmap_update_bits(rt5659->regmap, RT5659_4BTN_IL_CMD_2, + RT5659_4BTN_IL_MASK, RT5659_4BTN_IL_EN); + regmap_read(rt5659->regmap, RT5659_4BTN_IL_CMD_1, &value); + regmap_write(rt5659->regmap, RT5659_4BTN_IL_CMD_1, value); + regmap_read(rt5659->regmap, RT5659_4BTN_IL_CMD_1, &value); + + if (value & 0x2000) { + rt5659->hda_mic_plugged = true; + regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2, + 0x2, 0x2); + } else { + regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2, + 0x2, 0x0); + } + + regmap_update_bits(rt5659->regmap, RT5659_IRQ_CTRL_2, + RT5659_IL_IRQ_MASK, RT5659_IL_IRQ_EN); +} + +static int rt5659_i2c_probe(struct i2c_client *i2c) +{ + struct rt5659_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5659_priv *rt5659; + int ret; + unsigned int val; + + rt5659 = devm_kzalloc(&i2c->dev, sizeof(struct rt5659_priv), + GFP_KERNEL); + + if (rt5659 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5659); + + if (pdata) + rt5659->pdata = *pdata; + else + rt5659_parse_dt(rt5659, &i2c->dev); + + rt5659->gpiod_ldo1_en = devm_gpiod_get_optional(&i2c->dev, "ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5659->gpiod_ldo1_en)) + dev_warn(&i2c->dev, "Request ldo1-en GPIO failed\n"); + + rt5659->gpiod_reset = devm_gpiod_get_optional(&i2c->dev, "reset", + GPIOD_OUT_HIGH); + + /* Sleep for 300 ms miniumum */ + msleep(300); + + rt5659->regmap = devm_regmap_init_i2c(i2c, &rt5659_regmap); + if (IS_ERR(rt5659->regmap)) { + ret = PTR_ERR(rt5659->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5659->regmap, RT5659_DEVICE_ID, &val); + if (val != DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5659\n", val); + return -ENODEV; + } + + regmap_write(rt5659->regmap, RT5659_RESET, 0); + + /* Check if MCLK provided */ + rt5659->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(rt5659->mclk)) + return PTR_ERR(rt5659->mclk); + + rt5659_calibrate(rt5659); + + /* line in diff mode*/ + if (rt5659->pdata.in1_diff) + regmap_update_bits(rt5659->regmap, RT5659_IN1_IN2, + RT5659_IN1_DF_MASK, RT5659_IN1_DF_MASK); + if (rt5659->pdata.in3_diff) + regmap_update_bits(rt5659->regmap, RT5659_IN3_IN4, + RT5659_IN3_DF_MASK, RT5659_IN3_DF_MASK); + if (rt5659->pdata.in4_diff) + regmap_update_bits(rt5659->regmap, RT5659_IN3_IN4, + RT5659_IN4_DF_MASK, RT5659_IN4_DF_MASK); + + /* DMIC pin*/ + if (rt5659->pdata.dmic1_data_pin != RT5659_DMIC1_NULL || + rt5659->pdata.dmic2_data_pin != RT5659_DMIC2_NULL) { + regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1, + RT5659_GP2_PIN_MASK, RT5659_GP2_PIN_DMIC1_SCL); + + switch (rt5659->pdata.dmic1_data_pin) { + case RT5659_DMIC1_DATA_IN2N: + regmap_update_bits(rt5659->regmap, RT5659_DMIC_CTRL_1, + RT5659_DMIC_1_DP_MASK, RT5659_DMIC_1_DP_IN2N); + break; + + case RT5659_DMIC1_DATA_GPIO5: + regmap_update_bits(rt5659->regmap, + RT5659_GPIO_CTRL_3, + RT5659_I2S2_PIN_MASK, + RT5659_I2S2_PIN_GPIO); + regmap_update_bits(rt5659->regmap, RT5659_DMIC_CTRL_1, + RT5659_DMIC_1_DP_MASK, RT5659_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1, + RT5659_GP5_PIN_MASK, RT5659_GP5_PIN_DMIC1_SDA); + break; + + case RT5659_DMIC1_DATA_GPIO9: + regmap_update_bits(rt5659->regmap, RT5659_DMIC_CTRL_1, + RT5659_DMIC_1_DP_MASK, RT5659_DMIC_1_DP_GPIO9); + regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1, + RT5659_GP9_PIN_MASK, RT5659_GP9_PIN_DMIC1_SDA); + break; + + case RT5659_DMIC1_DATA_GPIO11: + regmap_update_bits(rt5659->regmap, RT5659_DMIC_CTRL_1, + RT5659_DMIC_1_DP_MASK, RT5659_DMIC_1_DP_GPIO11); + regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1, + RT5659_GP11_PIN_MASK, + RT5659_GP11_PIN_DMIC1_SDA); + break; + + default: + dev_dbg(&i2c->dev, "no DMIC1\n"); + break; + } + + switch (rt5659->pdata.dmic2_data_pin) { + case RT5659_DMIC2_DATA_IN2P: + regmap_update_bits(rt5659->regmap, + RT5659_DMIC_CTRL_1, + RT5659_DMIC_2_DP_MASK, + RT5659_DMIC_2_DP_IN2P); + break; + + case RT5659_DMIC2_DATA_GPIO6: + regmap_update_bits(rt5659->regmap, + RT5659_DMIC_CTRL_1, + RT5659_DMIC_2_DP_MASK, + RT5659_DMIC_2_DP_GPIO6); + regmap_update_bits(rt5659->regmap, + RT5659_GPIO_CTRL_1, + RT5659_GP6_PIN_MASK, + RT5659_GP6_PIN_DMIC2_SDA); + break; + + case RT5659_DMIC2_DATA_GPIO10: + regmap_update_bits(rt5659->regmap, + RT5659_DMIC_CTRL_1, + RT5659_DMIC_2_DP_MASK, + RT5659_DMIC_2_DP_GPIO10); + regmap_update_bits(rt5659->regmap, + RT5659_GPIO_CTRL_1, + RT5659_GP10_PIN_MASK, + RT5659_GP10_PIN_DMIC2_SDA); + break; + + case RT5659_DMIC2_DATA_GPIO12: + regmap_update_bits(rt5659->regmap, + RT5659_DMIC_CTRL_1, + RT5659_DMIC_2_DP_MASK, + RT5659_DMIC_2_DP_GPIO12); + regmap_update_bits(rt5659->regmap, + RT5659_GPIO_CTRL_1, + RT5659_GP12_PIN_MASK, + RT5659_GP12_PIN_DMIC2_SDA); + break; + + default: + dev_dbg(&i2c->dev, "no DMIC2\n"); + break; + + } + } else { + regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1, + RT5659_GP2_PIN_MASK | RT5659_GP5_PIN_MASK | + RT5659_GP9_PIN_MASK | RT5659_GP11_PIN_MASK | + RT5659_GP6_PIN_MASK | RT5659_GP10_PIN_MASK | + RT5659_GP12_PIN_MASK, + RT5659_GP2_PIN_GPIO2 | RT5659_GP5_PIN_GPIO5 | + RT5659_GP9_PIN_GPIO9 | RT5659_GP11_PIN_GPIO11 | + RT5659_GP6_PIN_GPIO6 | RT5659_GP10_PIN_GPIO10 | + RT5659_GP12_PIN_GPIO12); + regmap_update_bits(rt5659->regmap, RT5659_DMIC_CTRL_1, + RT5659_DMIC_1_DP_MASK | RT5659_DMIC_2_DP_MASK, + RT5659_DMIC_1_DP_IN2N | RT5659_DMIC_2_DP_IN2P); + } + + switch (rt5659->pdata.jd_src) { + case RT5659_JD3: + regmap_write(rt5659->regmap, RT5659_EJD_CTRL_1, 0xa880); + regmap_write(rt5659->regmap, RT5659_RC_CLK_CTRL, 0x9000); + regmap_write(rt5659->regmap, RT5659_GPIO_CTRL_1, 0xc800); + regmap_update_bits(rt5659->regmap, RT5659_PWR_ANLG_1, + RT5659_PWR_MB, RT5659_PWR_MB); + regmap_write(rt5659->regmap, RT5659_PWR_ANLG_2, 0x0001); + regmap_write(rt5659->regmap, RT5659_IRQ_CTRL_2, 0x0040); + INIT_DELAYED_WORK(&rt5659->jack_detect_work, + rt5659_jack_detect_work); + break; + case RT5659_JD_HDA_HEADER: + regmap_write(rt5659->regmap, RT5659_GPIO_CTRL_3, 0x8000); + regmap_write(rt5659->regmap, RT5659_RC_CLK_CTRL, 0x0900); + regmap_write(rt5659->regmap, RT5659_EJD_CTRL_1, 0x70c0); + regmap_write(rt5659->regmap, RT5659_JD_CTRL_1, 0x2000); + regmap_write(rt5659->regmap, RT5659_IRQ_CTRL_1, 0x0040); + INIT_DELAYED_WORK(&rt5659->jack_detect_work, + rt5659_jack_detect_intel_hd_header); + rt5659_intel_hd_header_probe_setup(rt5659); + break; + default: + break; + } + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5659", rt5659); + if (ret) + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + + /* Enable IRQ output for GPIO1 pin any way */ + regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1, + RT5659_GP1_PIN_MASK, RT5659_GP1_PIN_IRQ); + } + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5659, + rt5659_dai, ARRAY_SIZE(rt5659_dai)); +} + +static void rt5659_i2c_shutdown(struct i2c_client *client) +{ + struct rt5659_priv *rt5659 = i2c_get_clientdata(client); + + regmap_write(rt5659->regmap, RT5659_RESET, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt5659_of_match[] = { + { .compatible = "realtek,rt5658", }, + { .compatible = "realtek,rt5659", }, + { }, +}; +MODULE_DEVICE_TABLE(of, rt5659_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5659_acpi_match[] = { + { "10EC5658", 0, }, + { "10EC5659", 0, }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt5659_acpi_match); +#endif + +static struct i2c_driver rt5659_i2c_driver = { + .driver = { + .name = "rt5659", + .of_match_table = of_match_ptr(rt5659_of_match), + .acpi_match_table = ACPI_PTR(rt5659_acpi_match), + }, + .probe = rt5659_i2c_probe, + .shutdown = rt5659_i2c_shutdown, + .id_table = rt5659_i2c_id, +}; +module_i2c_driver(rt5659_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5659 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h new file mode 100644 index 0000000000..b49fd8baf4 --- /dev/null +++ b/sound/soc/codecs/rt5659.h @@ -0,0 +1,1821 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5659.h -- RT5659/RT5658 ALSA SoC audio driver + * + * Copyright 2015 Realtek Microelectronics + * Author: Bard Liao + */ + +#ifndef __RT5659_H__ +#define __RT5659_H__ + +#include + +#define DEVICE_ID 0x6311 + +/* Info */ +#define RT5659_RESET 0x0000 +#define RT5659_VENDOR_ID 0x00fd +#define RT5659_VENDOR_ID_1 0x00fe +#define RT5659_DEVICE_ID 0x00ff +/* I/O - Output */ +#define RT5659_SPO_VOL 0x0001 +#define RT5659_HP_VOL 0x0002 +#define RT5659_LOUT 0x0003 +#define RT5659_MONO_OUT 0x0004 +#define RT5659_HPL_GAIN 0x0005 +#define RT5659_HPR_GAIN 0x0006 +#define RT5659_MONO_GAIN 0x0007 +#define RT5659_SPDIF_CTRL_1 0x0008 +#define RT5659_SPDIF_CTRL_2 0x0009 +/* I/O - Input */ +#define RT5659_CAL_BST_CTRL 0x000a +#define RT5659_IN1_IN2 0x000c +#define RT5659_IN3_IN4 0x000d +#define RT5659_INL1_INR1_VOL 0x000f +/* I/O - Speaker */ +#define RT5659_EJD_CTRL_1 0x0010 +#define RT5659_EJD_CTRL_2 0x0011 +#define RT5659_EJD_CTRL_3 0x0012 +#define RT5659_SILENCE_CTRL 0x0015 +#define RT5659_PSV_CTRL 0x0016 +/* I/O - Sidetone */ +#define RT5659_SIDETONE_CTRL 0x0018 +/* I/O - ADC/DAC/DMIC */ +#define RT5659_DAC1_DIG_VOL 0x0019 +#define RT5659_DAC2_DIG_VOL 0x001a +#define RT5659_DAC_CTRL 0x001b +#define RT5659_STO1_ADC_DIG_VOL 0x001c +#define RT5659_MONO_ADC_DIG_VOL 0x001d +#define RT5659_STO2_ADC_DIG_VOL 0x001e +#define RT5659_STO1_BOOST 0x001f +#define RT5659_MONO_BOOST 0x0020 +#define RT5659_STO2_BOOST 0x0021 +#define RT5659_HP_IMP_GAIN_1 0x0022 +#define RT5659_HP_IMP_GAIN_2 0x0023 +/* Mixer - D-D */ +#define RT5659_STO1_ADC_MIXER 0x0026 +#define RT5659_MONO_ADC_MIXER 0x0027 +#define RT5659_AD_DA_MIXER 0x0029 +#define RT5659_STO_DAC_MIXER 0x002a +#define RT5659_MONO_DAC_MIXER 0x002b +#define RT5659_DIG_MIXER 0x002c +#define RT5659_A_DAC_MUX 0x002d +#define RT5659_DIG_INF23_DATA 0x002f +/* Mixer - PDM */ +#define RT5659_PDM_OUT_CTRL 0x0031 +#define RT5659_PDM_DATA_CTRL_1 0x0032 +#define RT5659_PDM_DATA_CTRL_2 0x0033 +#define RT5659_PDM_DATA_CTRL_3 0x0034 +#define RT5659_PDM_DATA_CTRL_4 0x0035 +#define RT5659_SPDIF_CTRL 0x0036 + +/* Mixer - ADC */ +#define RT5659_REC1_GAIN 0x003a +#define RT5659_REC1_L1_MIXER 0x003b +#define RT5659_REC1_L2_MIXER 0x003c +#define RT5659_REC1_R1_MIXER 0x003d +#define RT5659_REC1_R2_MIXER 0x003e +#define RT5659_CAL_REC 0x0040 +#define RT5659_REC2_L1_MIXER 0x009b +#define RT5659_REC2_L2_MIXER 0x009c +#define RT5659_REC2_R1_MIXER 0x009d +#define RT5659_REC2_R2_MIXER 0x009e +#define RT5659_RC_CLK_CTRL 0x009f +/* Mixer - DAC */ +#define RT5659_SPK_L_MIXER 0x0046 +#define RT5659_SPK_R_MIXER 0x0047 +#define RT5659_SPO_AMP_GAIN 0x0048 +#define RT5659_ALC_BACK_GAIN 0x0049 +#define RT5659_MONOMIX_GAIN 0x004a +#define RT5659_MONOMIX_IN_GAIN 0x004b +#define RT5659_OUT_L_GAIN 0x004d +#define RT5659_OUT_L_MIXER 0x004e +#define RT5659_OUT_R_GAIN 0x004f +#define RT5659_OUT_R_MIXER 0x0050 +#define RT5659_LOUT_MIXER 0x0052 + +#define RT5659_HAPTIC_GEN_CTRL_1 0x0053 +#define RT5659_HAPTIC_GEN_CTRL_2 0x0054 +#define RT5659_HAPTIC_GEN_CTRL_3 0x0055 +#define RT5659_HAPTIC_GEN_CTRL_4 0x0056 +#define RT5659_HAPTIC_GEN_CTRL_5 0x0057 +#define RT5659_HAPTIC_GEN_CTRL_6 0x0058 +#define RT5659_HAPTIC_GEN_CTRL_7 0x0059 +#define RT5659_HAPTIC_GEN_CTRL_8 0x005a +#define RT5659_HAPTIC_GEN_CTRL_9 0x005b +#define RT5659_HAPTIC_GEN_CTRL_10 0x005c +#define RT5659_HAPTIC_GEN_CTRL_11 0x005d +#define RT5659_HAPTIC_LPF_CTRL_1 0x005e +#define RT5659_HAPTIC_LPF_CTRL_2 0x005f +#define RT5659_HAPTIC_LPF_CTRL_3 0x0060 +/* Power */ +#define RT5659_PWR_DIG_1 0x0061 +#define RT5659_PWR_DIG_2 0x0062 +#define RT5659_PWR_ANLG_1 0x0063 +#define RT5659_PWR_ANLG_2 0x0064 +#define RT5659_PWR_ANLG_3 0x0065 +#define RT5659_PWR_MIXER 0x0066 +#define RT5659_PWR_VOL 0x0067 +/* Private Register Control */ +#define RT5659_PRIV_INDEX 0x006a +#define RT5659_CLK_DET 0x006b +#define RT5659_PRIV_DATA 0x006c +/* System Clock Pre Divider Gating Control */ +#define RT5659_PRE_DIV_1 0x006e +#define RT5659_PRE_DIV_2 0x006f +/* Format - ADC/DAC */ +#define RT5659_I2S1_SDP 0x0070 +#define RT5659_I2S2_SDP 0x0071 +#define RT5659_I2S3_SDP 0x0072 +#define RT5659_ADDA_CLK_1 0x0073 +#define RT5659_ADDA_CLK_2 0x0074 +#define RT5659_DMIC_CTRL_1 0x0075 +#define RT5659_DMIC_CTRL_2 0x0076 +/* Format - TDM Control */ +#define RT5659_TDM_CTRL_1 0x0077 +#define RT5659_TDM_CTRL_2 0x0078 +#define RT5659_TDM_CTRL_3 0x0079 +#define RT5659_TDM_CTRL_4 0x007a +#define RT5659_TDM_CTRL_5 0x007b + +/* Function - Analog */ +#define RT5659_GLB_CLK 0x0080 +#define RT5659_PLL_CTRL_1 0x0081 +#define RT5659_PLL_CTRL_2 0x0082 +#define RT5659_ASRC_1 0x0083 +#define RT5659_ASRC_2 0x0084 +#define RT5659_ASRC_3 0x0085 +#define RT5659_ASRC_4 0x0086 +#define RT5659_ASRC_5 0x0087 +#define RT5659_ASRC_6 0x0088 +#define RT5659_ASRC_7 0x0089 +#define RT5659_ASRC_8 0x008a +#define RT5659_ASRC_9 0x008b +#define RT5659_ASRC_10 0x008c +#define RT5659_DEPOP_1 0x008e +#define RT5659_DEPOP_2 0x008f +#define RT5659_DEPOP_3 0x0090 +#define RT5659_HP_CHARGE_PUMP_1 0x0091 +#define RT5659_HP_CHARGE_PUMP_2 0x0092 +#define RT5659_MICBIAS_1 0x0093 +#define RT5659_MICBIAS_2 0x0094 +#define RT5659_ASRC_11 0x0097 +#define RT5659_ASRC_12 0x0098 +#define RT5659_ASRC_13 0x0099 +#define RT5659_REC_M1_M2_GAIN_CTRL 0x009a +#define RT5659_CLASSD_CTRL_1 0x00a0 +#define RT5659_CLASSD_CTRL_2 0x00a1 + +/* Function - Digital */ +#define RT5659_ADC_EQ_CTRL_1 0x00ae +#define RT5659_ADC_EQ_CTRL_2 0x00af +#define RT5659_DAC_EQ_CTRL_1 0x00b0 +#define RT5659_DAC_EQ_CTRL_2 0x00b1 +#define RT5659_DAC_EQ_CTRL_3 0x00b2 + +#define RT5659_IRQ_CTRL_1 0x00b6 +#define RT5659_IRQ_CTRL_2 0x00b7 +#define RT5659_IRQ_CTRL_3 0x00b8 +#define RT5659_IRQ_CTRL_4 0x00ba +#define RT5659_IRQ_CTRL_5 0x00bb +#define RT5659_IRQ_CTRL_6 0x00bc +#define RT5659_INT_ST_1 0x00be +#define RT5659_INT_ST_2 0x00bf +#define RT5659_GPIO_CTRL_1 0x00c0 +#define RT5659_GPIO_CTRL_2 0x00c1 +#define RT5659_GPIO_CTRL_3 0x00c2 +#define RT5659_GPIO_CTRL_4 0x00c3 +#define RT5659_GPIO_CTRL_5 0x00c4 +#define RT5659_GPIO_STA 0x00c5 +#define RT5659_SINE_GEN_CTRL_1 0x00cb +#define RT5659_SINE_GEN_CTRL_2 0x00cc +#define RT5659_SINE_GEN_CTRL_3 0x00cd +#define RT5659_HP_AMP_DET_CTRL_1 0x00d6 +#define RT5659_HP_AMP_DET_CTRL_2 0x00d7 +#define RT5659_SV_ZCD_1 0x00d9 +#define RT5659_SV_ZCD_2 0x00da +#define RT5659_IL_CMD_1 0x00db +#define RT5659_IL_CMD_2 0x00dc +#define RT5659_IL_CMD_3 0x00dd +#define RT5659_IL_CMD_4 0x00de +#define RT5659_4BTN_IL_CMD_1 0x00df +#define RT5659_4BTN_IL_CMD_2 0x00e0 +#define RT5659_4BTN_IL_CMD_3 0x00e1 +#define RT5659_PSV_IL_CMD_1 0x00e4 +#define RT5659_PSV_IL_CMD_2 0x00e5 + +#define RT5659_ADC_STO1_HP_CTRL_1 0x00ea +#define RT5659_ADC_STO1_HP_CTRL_2 0x00eb +#define RT5659_ADC_MONO_HP_CTRL_1 0x00ec +#define RT5659_ADC_MONO_HP_CTRL_2 0x00ed +#define RT5659_AJD1_CTRL 0x00f0 +#define RT5659_AJD2_AJD3_CTRL 0x00f1 +#define RT5659_JD1_THD 0x00f2 +#define RT5659_JD2_THD 0x00f3 +#define RT5659_JD3_THD 0x00f4 +#define RT5659_JD_CTRL_1 0x00f6 +#define RT5659_JD_CTRL_2 0x00f7 +#define RT5659_JD_CTRL_3 0x00f8 +#define RT5659_JD_CTRL_4 0x00f9 +/* General Control */ +#define RT5659_DIG_MISC 0x00fa +#define RT5659_DUMMY_2 0x00fb +#define RT5659_DUMMY_3 0x00fc + +#define RT5659_DAC_ADC_DIG_VOL 0x0100 +#define RT5659_BIAS_CUR_CTRL_1 0x010a +#define RT5659_BIAS_CUR_CTRL_2 0x010b +#define RT5659_BIAS_CUR_CTRL_3 0x010c +#define RT5659_BIAS_CUR_CTRL_4 0x010d +#define RT5659_BIAS_CUR_CTRL_5 0x010e +#define RT5659_BIAS_CUR_CTRL_6 0x010f +#define RT5659_BIAS_CUR_CTRL_7 0x0110 +#define RT5659_BIAS_CUR_CTRL_8 0x0111 +#define RT5659_BIAS_CUR_CTRL_9 0x0112 +#define RT5659_BIAS_CUR_CTRL_10 0x0113 +#define RT5659_MEMORY_TEST 0x0116 +#define RT5659_VREF_REC_OP_FB_CAP_CTRL 0x0117 +#define RT5659_CLASSD_0 0x011a +#define RT5659_CLASSD_1 0x011b +#define RT5659_CLASSD_2 0x011c +#define RT5659_CLASSD_3 0x011d +#define RT5659_CLASSD_4 0x011e +#define RT5659_CLASSD_5 0x011f +#define RT5659_CLASSD_6 0x0120 +#define RT5659_CLASSD_7 0x0121 +#define RT5659_CLASSD_8 0x0122 +#define RT5659_CLASSD_9 0x0123 +#define RT5659_CLASSD_10 0x0124 +#define RT5659_CHARGE_PUMP_1 0x0125 +#define RT5659_CHARGE_PUMP_2 0x0126 +#define RT5659_DIG_IN_CTRL_1 0x0132 +#define RT5659_DIG_IN_CTRL_2 0x0133 +#define RT5659_PAD_DRIVING_CTRL 0x0137 +#define RT5659_SOFT_RAMP_DEPOP 0x0138 +#define RT5659_PLL 0x0139 +#define RT5659_CHOP_DAC 0x013a +#define RT5659_CHOP_ADC 0x013b +#define RT5659_CALIB_ADC_CTRL 0x013c +#define RT5659_SOFT_RAMP_DEPOP_DAC_CLK_CTRL 0x013e +#define RT5659_VOL_TEST 0x013f +#define RT5659_TEST_MODE_CTRL_1 0x0145 +#define RT5659_TEST_MODE_CTRL_2 0x0146 +#define RT5659_TEST_MODE_CTRL_3 0x0147 +#define RT5659_TEST_MODE_CTRL_4 0x0148 +#define RT5659_BASSBACK_CTRL 0x0150 +#define RT5659_MP3_PLUS_CTRL_1 0x0151 +#define RT5659_MP3_PLUS_CTRL_2 0x0152 +#define RT5659_MP3_HPF_A1 0x0153 +#define RT5659_MP3_HPF_A2 0x0154 +#define RT5659_MP3_HPF_H0 0x0155 +#define RT5659_MP3_LPF_H0 0x0156 +#define RT5659_3D_SPK_CTRL 0x0157 +#define RT5659_3D_SPK_COEF_1 0x0158 +#define RT5659_3D_SPK_COEF_2 0x0159 +#define RT5659_3D_SPK_COEF_3 0x015a +#define RT5659_3D_SPK_COEF_4 0x015b +#define RT5659_3D_SPK_COEF_5 0x015c +#define RT5659_3D_SPK_COEF_6 0x015d +#define RT5659_3D_SPK_COEF_7 0x015e +#define RT5659_STO_NG2_CTRL_1 0x0160 +#define RT5659_STO_NG2_CTRL_2 0x0161 +#define RT5659_STO_NG2_CTRL_3 0x0162 +#define RT5659_STO_NG2_CTRL_4 0x0163 +#define RT5659_STO_NG2_CTRL_5 0x0164 +#define RT5659_STO_NG2_CTRL_6 0x0165 +#define RT5659_STO_NG2_CTRL_7 0x0166 +#define RT5659_STO_NG2_CTRL_8 0x0167 +#define RT5659_MONO_NG2_CTRL_1 0x0170 +#define RT5659_MONO_NG2_CTRL_2 0x0171 +#define RT5659_MONO_NG2_CTRL_3 0x0172 +#define RT5659_MONO_NG2_CTRL_4 0x0173 +#define RT5659_MONO_NG2_CTRL_5 0x0174 +#define RT5659_MONO_NG2_CTRL_6 0x0175 +#define RT5659_MID_HP_AMP_DET 0x0190 +#define RT5659_LOW_HP_AMP_DET 0x0191 +#define RT5659_LDO_CTRL 0x0192 +#define RT5659_HP_DECROSS_CTRL_1 0x01b0 +#define RT5659_HP_DECROSS_CTRL_2 0x01b1 +#define RT5659_HP_DECROSS_CTRL_3 0x01b2 +#define RT5659_HP_DECROSS_CTRL_4 0x01b3 +#define RT5659_HP_IMP_SENS_CTRL_1 0x01c0 +#define RT5659_HP_IMP_SENS_CTRL_2 0x01c1 +#define RT5659_HP_IMP_SENS_CTRL_3 0x01c2 +#define RT5659_HP_IMP_SENS_CTRL_4 0x01c3 +#define RT5659_HP_IMP_SENS_MAP_1 0x01c7 +#define RT5659_HP_IMP_SENS_MAP_2 0x01c8 +#define RT5659_HP_IMP_SENS_MAP_3 0x01c9 +#define RT5659_HP_IMP_SENS_MAP_4 0x01ca +#define RT5659_HP_IMP_SENS_MAP_5 0x01cb +#define RT5659_HP_IMP_SENS_MAP_6 0x01cc +#define RT5659_HP_IMP_SENS_MAP_7 0x01cd +#define RT5659_HP_IMP_SENS_MAP_8 0x01ce +#define RT5659_HP_LOGIC_CTRL_1 0x01da +#define RT5659_HP_LOGIC_CTRL_2 0x01db +#define RT5659_HP_CALIB_CTRL_1 0x01de +#define RT5659_HP_CALIB_CTRL_2 0x01df +#define RT5659_HP_CALIB_CTRL_3 0x01e0 +#define RT5659_HP_CALIB_CTRL_4 0x01e1 +#define RT5659_HP_CALIB_CTRL_5 0x01e2 +#define RT5659_HP_CALIB_CTRL_6 0x01e3 +#define RT5659_HP_CALIB_CTRL_7 0x01e4 +#define RT5659_HP_CALIB_CTRL_9 0x01e6 +#define RT5659_HP_CALIB_CTRL_10 0x01e7 +#define RT5659_HP_CALIB_CTRL_11 0x01e8 +#define RT5659_HP_CALIB_STA_1 0x01ea +#define RT5659_HP_CALIB_STA_2 0x01eb +#define RT5659_HP_CALIB_STA_3 0x01ec +#define RT5659_HP_CALIB_STA_4 0x01ed +#define RT5659_HP_CALIB_STA_5 0x01ee +#define RT5659_HP_CALIB_STA_6 0x01ef +#define RT5659_HP_CALIB_STA_7 0x01f0 +#define RT5659_HP_CALIB_STA_8 0x01f1 +#define RT5659_HP_CALIB_STA_9 0x01f2 +#define RT5659_MONO_AMP_CALIB_CTRL_1 0x01f6 +#define RT5659_MONO_AMP_CALIB_CTRL_2 0x01f7 +#define RT5659_MONO_AMP_CALIB_CTRL_3 0x01f8 +#define RT5659_MONO_AMP_CALIB_CTRL_4 0x01f9 +#define RT5659_MONO_AMP_CALIB_CTRL_5 0x01fa +#define RT5659_MONO_AMP_CALIB_STA_1 0x01fb +#define RT5659_MONO_AMP_CALIB_STA_2 0x01fc +#define RT5659_MONO_AMP_CALIB_STA_3 0x01fd +#define RT5659_MONO_AMP_CALIB_STA_4 0x01fe +#define RT5659_SPK_PWR_LMT_CTRL_1 0x0200 +#define RT5659_SPK_PWR_LMT_CTRL_2 0x0201 +#define RT5659_SPK_PWR_LMT_CTRL_3 0x0202 +#define RT5659_SPK_PWR_LMT_STA_1 0x0203 +#define RT5659_SPK_PWR_LMT_STA_2 0x0204 +#define RT5659_SPK_PWR_LMT_STA_3 0x0205 +#define RT5659_SPK_PWR_LMT_STA_4 0x0206 +#define RT5659_SPK_PWR_LMT_STA_5 0x0207 +#define RT5659_SPK_PWR_LMT_STA_6 0x0208 +#define RT5659_FLEX_SPK_BST_CTRL_1 0x0256 +#define RT5659_FLEX_SPK_BST_CTRL_2 0x0257 +#define RT5659_FLEX_SPK_BST_CTRL_3 0x0258 +#define RT5659_FLEX_SPK_BST_CTRL_4 0x0259 +#define RT5659_SPK_EX_LMT_CTRL_1 0x025a +#define RT5659_SPK_EX_LMT_CTRL_2 0x025b +#define RT5659_SPK_EX_LMT_CTRL_3 0x025c +#define RT5659_SPK_EX_LMT_CTRL_4 0x025d +#define RT5659_SPK_EX_LMT_CTRL_5 0x025e +#define RT5659_SPK_EX_LMT_CTRL_6 0x025f +#define RT5659_SPK_EX_LMT_CTRL_7 0x0260 +#define RT5659_ADJ_HPF_CTRL_1 0x0261 +#define RT5659_ADJ_HPF_CTRL_2 0x0262 +#define RT5659_SPK_DC_CAILB_CTRL_1 0x0265 +#define RT5659_SPK_DC_CAILB_CTRL_2 0x0266 +#define RT5659_SPK_DC_CAILB_CTRL_3 0x0267 +#define RT5659_SPK_DC_CAILB_CTRL_4 0x0268 +#define RT5659_SPK_DC_CAILB_CTRL_5 0x0269 +#define RT5659_SPK_DC_CAILB_STA_1 0x026a +#define RT5659_SPK_DC_CAILB_STA_2 0x026b +#define RT5659_SPK_DC_CAILB_STA_3 0x026c +#define RT5659_SPK_DC_CAILB_STA_4 0x026d +#define RT5659_SPK_DC_CAILB_STA_5 0x026e +#define RT5659_SPK_DC_CAILB_STA_6 0x026f +#define RT5659_SPK_DC_CAILB_STA_7 0x0270 +#define RT5659_SPK_DC_CAILB_STA_8 0x0271 +#define RT5659_SPK_DC_CAILB_STA_9 0x0272 +#define RT5659_SPK_DC_CAILB_STA_10 0x0273 +#define RT5659_SPK_VDD_STA_1 0x0280 +#define RT5659_SPK_VDD_STA_2 0x0281 +#define RT5659_SPK_DC_DET_CTRL_1 0x0282 +#define RT5659_SPK_DC_DET_CTRL_2 0x0283 +#define RT5659_SPK_DC_DET_CTRL_3 0x0284 +#define RT5659_PURE_DC_DET_CTRL_1 0x0290 +#define RT5659_PURE_DC_DET_CTRL_2 0x0291 +#define RT5659_DUMMY_4 0x02fa +#define RT5659_DUMMY_5 0x02fb +#define RT5659_DUMMY_6 0x02fc +#define RT5659_DRC1_CTRL_1 0x0300 +#define RT5659_DRC1_CTRL_2 0x0301 +#define RT5659_DRC1_CTRL_3 0x0302 +#define RT5659_DRC1_CTRL_4 0x0303 +#define RT5659_DRC1_CTRL_5 0x0304 +#define RT5659_DRC1_CTRL_6 0x0305 +#define RT5659_DRC1_HARD_LMT_CTRL_1 0x0306 +#define RT5659_DRC1_HARD_LMT_CTRL_2 0x0307 +#define RT5659_DRC2_CTRL_1 0x0308 +#define RT5659_DRC2_CTRL_2 0x0309 +#define RT5659_DRC2_CTRL_3 0x030a +#define RT5659_DRC2_CTRL_4 0x030b +#define RT5659_DRC2_CTRL_5 0x030c +#define RT5659_DRC2_CTRL_6 0x030d +#define RT5659_DRC2_HARD_LMT_CTRL_1 0x030e +#define RT5659_DRC2_HARD_LMT_CTRL_2 0x030f +#define RT5659_DRC1_PRIV_1 0x0310 +#define RT5659_DRC1_PRIV_2 0x0311 +#define RT5659_DRC1_PRIV_3 0x0312 +#define RT5659_DRC1_PRIV_4 0x0313 +#define RT5659_DRC1_PRIV_5 0x0314 +#define RT5659_DRC1_PRIV_6 0x0315 +#define RT5659_DRC1_PRIV_7 0x0316 +#define RT5659_DRC2_PRIV_1 0x0317 +#define RT5659_DRC2_PRIV_2 0x0318 +#define RT5659_DRC2_PRIV_3 0x0319 +#define RT5659_DRC2_PRIV_4 0x031a +#define RT5659_DRC2_PRIV_5 0x031b +#define RT5659_DRC2_PRIV_6 0x031c +#define RT5659_DRC2_PRIV_7 0x031d +#define RT5659_MULTI_DRC_CTRL 0x0320 +#define RT5659_CROSS_OVER_1 0x0321 +#define RT5659_CROSS_OVER_2 0x0322 +#define RT5659_CROSS_OVER_3 0x0323 +#define RT5659_CROSS_OVER_4 0x0324 +#define RT5659_CROSS_OVER_5 0x0325 +#define RT5659_CROSS_OVER_6 0x0326 +#define RT5659_CROSS_OVER_7 0x0327 +#define RT5659_CROSS_OVER_8 0x0328 +#define RT5659_CROSS_OVER_9 0x0329 +#define RT5659_CROSS_OVER_10 0x032a +#define RT5659_ALC_PGA_CTRL_1 0x0330 +#define RT5659_ALC_PGA_CTRL_2 0x0331 +#define RT5659_ALC_PGA_CTRL_3 0x0332 +#define RT5659_ALC_PGA_CTRL_4 0x0333 +#define RT5659_ALC_PGA_CTRL_5 0x0334 +#define RT5659_ALC_PGA_CTRL_6 0x0335 +#define RT5659_ALC_PGA_CTRL_7 0x0336 +#define RT5659_ALC_PGA_CTRL_8 0x0337 +#define RT5659_ALC_PGA_STA_1 0x0338 +#define RT5659_ALC_PGA_STA_2 0x0339 +#define RT5659_ALC_PGA_STA_3 0x033a +#define RT5659_DAC_L_EQ_PRE_VOL 0x0340 +#define RT5659_DAC_R_EQ_PRE_VOL 0x0341 +#define RT5659_DAC_L_EQ_POST_VOL 0x0342 +#define RT5659_DAC_R_EQ_POST_VOL 0x0343 +#define RT5659_DAC_L_EQ_LPF1_A1 0x0344 +#define RT5659_DAC_L_EQ_LPF1_H0 0x0345 +#define RT5659_DAC_R_EQ_LPF1_A1 0x0346 +#define RT5659_DAC_R_EQ_LPF1_H0 0x0347 +#define RT5659_DAC_L_EQ_BPF2_A1 0x0348 +#define RT5659_DAC_L_EQ_BPF2_A2 0x0349 +#define RT5659_DAC_L_EQ_BPF2_H0 0x034a +#define RT5659_DAC_R_EQ_BPF2_A1 0x034b +#define RT5659_DAC_R_EQ_BPF2_A2 0x034c +#define RT5659_DAC_R_EQ_BPF2_H0 0x034d +#define RT5659_DAC_L_EQ_BPF3_A1 0x034e +#define RT5659_DAC_L_EQ_BPF3_A2 0x034f +#define RT5659_DAC_L_EQ_BPF3_H0 0x0350 +#define RT5659_DAC_R_EQ_BPF3_A1 0x0351 +#define RT5659_DAC_R_EQ_BPF3_A2 0x0352 +#define RT5659_DAC_R_EQ_BPF3_H0 0x0353 +#define RT5659_DAC_L_EQ_BPF4_A1 0x0354 +#define RT5659_DAC_L_EQ_BPF4_A2 0x0355 +#define RT5659_DAC_L_EQ_BPF4_H0 0x0356 +#define RT5659_DAC_R_EQ_BPF4_A1 0x0357 +#define RT5659_DAC_R_EQ_BPF4_A2 0x0358 +#define RT5659_DAC_R_EQ_BPF4_H0 0x0359 +#define RT5659_DAC_L_EQ_HPF1_A1 0x035a +#define RT5659_DAC_L_EQ_HPF1_H0 0x035b +#define RT5659_DAC_R_EQ_HPF1_A1 0x035c +#define RT5659_DAC_R_EQ_HPF1_H0 0x035d +#define RT5659_DAC_L_EQ_HPF2_A1 0x035e +#define RT5659_DAC_L_EQ_HPF2_A2 0x035f +#define RT5659_DAC_L_EQ_HPF2_H0 0x0360 +#define RT5659_DAC_R_EQ_HPF2_A1 0x0361 +#define RT5659_DAC_R_EQ_HPF2_A2 0x0362 +#define RT5659_DAC_R_EQ_HPF2_H0 0x0363 +#define RT5659_DAC_L_BI_EQ_BPF1_H0_1 0x0364 +#define RT5659_DAC_L_BI_EQ_BPF1_H0_2 0x0365 +#define RT5659_DAC_L_BI_EQ_BPF1_B1_1 0x0366 +#define RT5659_DAC_L_BI_EQ_BPF1_B1_2 0x0367 +#define RT5659_DAC_L_BI_EQ_BPF1_B2_1 0x0368 +#define RT5659_DAC_L_BI_EQ_BPF1_B2_2 0x0369 +#define RT5659_DAC_L_BI_EQ_BPF1_A1_1 0x036a +#define RT5659_DAC_L_BI_EQ_BPF1_A1_2 0x036b +#define RT5659_DAC_L_BI_EQ_BPF1_A2_1 0x036c +#define RT5659_DAC_L_BI_EQ_BPF1_A2_2 0x036d +#define RT5659_DAC_R_BI_EQ_BPF1_H0_1 0x036e +#define RT5659_DAC_R_BI_EQ_BPF1_H0_2 0x036f +#define RT5659_DAC_R_BI_EQ_BPF1_B1_1 0x0370 +#define RT5659_DAC_R_BI_EQ_BPF1_B1_2 0x0371 +#define RT5659_DAC_R_BI_EQ_BPF1_B2_1 0x0372 +#define RT5659_DAC_R_BI_EQ_BPF1_B2_2 0x0373 +#define RT5659_DAC_R_BI_EQ_BPF1_A1_1 0x0374 +#define RT5659_DAC_R_BI_EQ_BPF1_A1_2 0x0375 +#define RT5659_DAC_R_BI_EQ_BPF1_A2_1 0x0376 +#define RT5659_DAC_R_BI_EQ_BPF1_A2_2 0x0377 +#define RT5659_ADC_L_EQ_LPF1_A1 0x03d0 +#define RT5659_ADC_R_EQ_LPF1_A1 0x03d1 +#define RT5659_ADC_L_EQ_LPF1_H0 0x03d2 +#define RT5659_ADC_R_EQ_LPF1_H0 0x03d3 +#define RT5659_ADC_L_EQ_BPF1_A1 0x03d4 +#define RT5659_ADC_R_EQ_BPF1_A1 0x03d5 +#define RT5659_ADC_L_EQ_BPF1_A2 0x03d6 +#define RT5659_ADC_R_EQ_BPF1_A2 0x03d7 +#define RT5659_ADC_L_EQ_BPF1_H0 0x03d8 +#define RT5659_ADC_R_EQ_BPF1_H0 0x03d9 +#define RT5659_ADC_L_EQ_BPF2_A1 0x03da +#define RT5659_ADC_R_EQ_BPF2_A1 0x03db +#define RT5659_ADC_L_EQ_BPF2_A2 0x03dc +#define RT5659_ADC_R_EQ_BPF2_A2 0x03dd +#define RT5659_ADC_L_EQ_BPF2_H0 0x03de +#define RT5659_ADC_R_EQ_BPF2_H0 0x03df +#define RT5659_ADC_L_EQ_BPF3_A1 0x03e0 +#define RT5659_ADC_R_EQ_BPF3_A1 0x03e1 +#define RT5659_ADC_L_EQ_BPF3_A2 0x03e2 +#define RT5659_ADC_R_EQ_BPF3_A2 0x03e3 +#define RT5659_ADC_L_EQ_BPF3_H0 0x03e4 +#define RT5659_ADC_R_EQ_BPF3_H0 0x03e5 +#define RT5659_ADC_L_EQ_BPF4_A1 0x03e6 +#define RT5659_ADC_R_EQ_BPF4_A1 0x03e7 +#define RT5659_ADC_L_EQ_BPF4_A2 0x03e8 +#define RT5659_ADC_R_EQ_BPF4_A2 0x03e9 +#define RT5659_ADC_L_EQ_BPF4_H0 0x03ea +#define RT5659_ADC_R_EQ_BPF4_H0 0x03eb +#define RT5659_ADC_L_EQ_HPF1_A1 0x03ec +#define RT5659_ADC_R_EQ_HPF1_A1 0x03ed +#define RT5659_ADC_L_EQ_HPF1_H0 0x03ee +#define RT5659_ADC_R_EQ_HPF1_H0 0x03ef +#define RT5659_ADC_L_EQ_PRE_VOL 0x03f0 +#define RT5659_ADC_R_EQ_PRE_VOL 0x03f1 +#define RT5659_ADC_L_EQ_POST_VOL 0x03f2 +#define RT5659_ADC_R_EQ_POST_VOL 0x03f3 + + + +/* global definition */ +#define RT5659_L_MUTE (0x1 << 15) +#define RT5659_L_MUTE_SFT 15 +#define RT5659_VOL_L_MUTE (0x1 << 14) +#define RT5659_VOL_L_SFT 14 +#define RT5659_R_MUTE (0x1 << 7) +#define RT5659_R_MUTE_SFT 7 +#define RT5659_VOL_R_MUTE (0x1 << 6) +#define RT5659_VOL_R_SFT 6 +#define RT5659_L_VOL_MASK (0x3f << 8) +#define RT5659_L_VOL_SFT 8 +#define RT5659_R_VOL_MASK (0x3f) +#define RT5659_R_VOL_SFT 0 + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5659_G_HP (0x1f << 8) +#define RT5659_G_HP_SFT 8 +#define RT5659_G_STO_DA_DMIX (0x1f) +#define RT5659_G_STO_DA_SFT 0 + +/* IN1/IN2 Control (0x000c) */ +#define RT5659_IN1_DF_MASK (0x1 << 15) +#define RT5659_IN1_DF 15 +#define RT5659_BST1_MASK (0x7f << 8) +#define RT5659_BST1_SFT 8 +#define RT5659_BST2_MASK (0x7f) +#define RT5659_BST2_SFT 0 + +/* IN3/IN4 Control (0x000d) */ +#define RT5659_IN3_DF_MASK (0x1 << 15) +#define RT5659_IN3_DF 15 +#define RT5659_BST3_MASK (0x7f << 8) +#define RT5659_BST3_SFT 8 +#define RT5659_IN4_DF_MASK (0x1 << 7) +#define RT5659_IN4_DF 7 +#define RT5659_BST4_MASK (0x7f) +#define RT5659_BST4_SFT 0 + +/* INL and INR Volume Control (0x000f) */ +#define RT5659_INL_VOL_MASK (0x1f << 8) +#define RT5659_INL_VOL_SFT 8 +#define RT5659_INR_VOL_MASK (0x1f) +#define RT5659_INR_VOL_SFT 0 + +/* Embeeded Jack and Type Detection Control 1 (0x0010) */ +#define RT5659_EMB_JD_EN (0x1 << 15) +#define RT5659_EMB_JD_EN_SFT 15 +#define RT5659_JD_MODE (0x1 << 13) +#define RT5659_JD_MODE_SFT 13 +#define RT5659_EXT_JD_EN (0x1 << 11) +#define RT5659_EXT_JD_EN_SFT 11 +#define RT5659_EXT_JD_DIG (0x1 << 9) + +/* Embeeded Jack and Type Detection Control 2 (0x0011) */ +#define RT5659_EXT_JD_SRC (0x7 << 4) +#define RT5659_EXT_JD_SRC_SFT 4 +#define RT5659_EXT_JD_SRC_GPIO_JD1 (0x0 << 4) +#define RT5659_EXT_JD_SRC_GPIO_JD2 (0x1 << 4) +#define RT5659_EXT_JD_SRC_JD1_1 (0x2 << 4) +#define RT5659_EXT_JD_SRC_JD1_2 (0x3 << 4) +#define RT5659_EXT_JD_SRC_JD2 (0x4 << 4) +#define RT5659_EXT_JD_SRC_JD3 (0x5 << 4) +#define RT5659_EXT_JD_SRC_MANUAL (0x6 << 4) + +/* Slience Detection Control (0x0015) */ +#define RT5659_SIL_DET_MASK (0x1 << 15) +#define RT5659_SIL_DET_DIS (0x0 << 15) +#define RT5659_SIL_DET_EN (0x1 << 15) + +/* Sidetone Control (0x0018) */ +#define RT5659_ST_SEL_MASK (0x7 << 9) +#define RT5659_ST_SEL_SFT 9 +#define RT5659_ST_EN (0x1 << 6) +#define RT5659_ST_EN_SFT 6 + +/* DAC1 Digital Volume (0x0019) */ +#define RT5659_DAC_L1_VOL_MASK (0xff << 8) +#define RT5659_DAC_L1_VOL_SFT 8 +#define RT5659_DAC_R1_VOL_MASK (0xff) +#define RT5659_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x001a) */ +#define RT5659_DAC_L2_VOL_MASK (0xff << 8) +#define RT5659_DAC_L2_VOL_SFT 8 +#define RT5659_DAC_R2_VOL_MASK (0xff) +#define RT5659_DAC_R2_VOL_SFT 0 + +/* DAC2 Control (0x001b) */ +#define RT5659_M_DAC2_L_VOL (0x1 << 13) +#define RT5659_M_DAC2_L_VOL_SFT 13 +#define RT5659_M_DAC2_R_VOL (0x1 << 12) +#define RT5659_M_DAC2_R_VOL_SFT 12 +#define RT5659_DAC_L2_SEL_MASK (0x7 << 4) +#define RT5659_DAC_L2_SEL_SFT 4 +#define RT5659_DAC_R2_SEL_MASK (0x7 << 0) +#define RT5659_DAC_R2_SEL_SFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5659_ADC_L_VOL_MASK (0x7f << 8) +#define RT5659_ADC_L_VOL_SFT 8 +#define RT5659_ADC_R_VOL_MASK (0x7f) +#define RT5659_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x001d) */ +#define RT5659_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5659_MONO_ADC_L_VOL_SFT 8 +#define RT5659_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5659_MONO_ADC_R_VOL_SFT 0 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5659_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5659_STO1_ADC_L_BST_SFT 14 +#define RT5659_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5659_STO1_ADC_R_BST_SFT 12 + +/* Mono ADC Boost Gain Control (0x0020) */ +#define RT5659_MONO_ADC_L_BST_MASK (0x3 << 14) +#define RT5659_MONO_ADC_L_BST_SFT 14 +#define RT5659_MONO_ADC_R_BST_MASK (0x3 << 12) +#define RT5659_MONO_ADC_R_BST_SFT 12 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5659_STO2_ADC_L_BST_MASK (0x3 << 14) +#define RT5659_STO2_ADC_L_BST_SFT 14 +#define RT5659_STO2_ADC_R_BST_MASK (0x3 << 12) +#define RT5659_STO2_ADC_R_BST_SFT 12 + +/* Stereo ADC Mixer Control (0x0026) */ +#define RT5659_M_STO1_ADC_L1 (0x1 << 15) +#define RT5659_M_STO1_ADC_L1_SFT 15 +#define RT5659_M_STO1_ADC_L2 (0x1 << 14) +#define RT5659_M_STO1_ADC_L2_SFT 14 +#define RT5659_STO1_ADC1_SRC_MASK (0x1 << 13) +#define RT5659_STO1_ADC1_SRC_SFT 13 +#define RT5659_STO1_ADC1_SRC_ADC (0x1 << 13) +#define RT5659_STO1_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5659_STO1_ADC_SRC_MASK (0x1 << 12) +#define RT5659_STO1_ADC_SRC_SFT 12 +#define RT5659_STO1_ADC_SRC_ADC1 (0x1 << 12) +#define RT5659_STO1_ADC_SRC_ADC2 (0x0 << 12) +#define RT5659_STO1_ADC2_SRC_MASK (0x1 << 11) +#define RT5659_STO1_ADC2_SRC_SFT 11 +#define RT5659_STO1_DMIC_SRC_MASK (0x1 << 8) +#define RT5659_STO1_DMIC_SRC_SFT 8 +#define RT5659_STO1_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5659_STO1_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5659_M_STO1_ADC_R1 (0x1 << 6) +#define RT5659_M_STO1_ADC_R1_SFT 6 +#define RT5659_M_STO1_ADC_R2 (0x1 << 5) +#define RT5659_M_STO1_ADC_R2_SFT 5 + +/* Mono1 ADC Mixer control (0x0027) */ +#define RT5659_M_MONO_ADC_L1 (0x1 << 15) +#define RT5659_M_MONO_ADC_L1_SFT 15 +#define RT5659_M_MONO_ADC_L2 (0x1 << 14) +#define RT5659_M_MONO_ADC_L2_SFT 14 +#define RT5659_MONO_ADC_L2_SRC_MASK (0x1 << 12) +#define RT5659_MONO_ADC_L2_SRC_SFT 12 +#define RT5659_MONO_ADC_L1_SRC_MASK (0x1 << 11) +#define RT5659_MONO_ADC_L1_SRC_SFT 11 +#define RT5659_MONO_ADC_L_SRC_MASK (0x3 << 9) +#define RT5659_MONO_ADC_L_SRC_SFT 9 +#define RT5659_MONO_DMIC_L_SRC_MASK (0x1 << 8) +#define RT5659_MONO_DMIC_L_SRC_SFT 8 +#define RT5659_M_MONO_ADC_R1 (0x1 << 7) +#define RT5659_M_MONO_ADC_R1_SFT 7 +#define RT5659_M_MONO_ADC_R2 (0x1 << 6) +#define RT5659_M_MONO_ADC_R2_SFT 6 +#define RT5659_STO2_ADC_SRC_MASK (0x1 << 5) +#define RT5659_STO2_ADC_SRC_SFT 5 +#define RT5659_MONO_ADC_R2_SRC_MASK (0x1 << 4) +#define RT5659_MONO_ADC_R2_SRC_SFT 4 +#define RT5659_MONO_ADC_R1_SRC_MASK (0x1 << 3) +#define RT5659_MONO_ADC_R1_SRC_SFT 3 +#define RT5659_MONO_ADC_R_SRC_MASK (0x3 << 1) +#define RT5659_MONO_ADC_R_SRC_SFT 1 +#define RT5659_MONO_DMIC_R_SRC_MASK 0x1 +#define RT5659_MONO_DMIC_R_SRC_SFT 0 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5659_M_ADCMIX_L (0x1 << 15) +#define RT5659_M_ADCMIX_L_SFT 15 +#define RT5659_M_DAC1_L (0x1 << 14) +#define RT5659_M_DAC1_L_SFT 14 +#define RT5659_DAC1_R_SEL_MASK (0x3 << 10) +#define RT5659_DAC1_R_SEL_SFT 10 +#define RT5659_DAC1_R_SEL_IF1 (0x0 << 10) +#define RT5659_DAC1_R_SEL_IF2 (0x1 << 10) +#define RT5659_DAC1_R_SEL_IF3 (0x2 << 10) +#define RT5659_DAC1_L_SEL_MASK (0x3 << 8) +#define RT5659_DAC1_L_SEL_SFT 8 +#define RT5659_DAC1_L_SEL_IF1 (0x0 << 8) +#define RT5659_DAC1_L_SEL_IF2 (0x1 << 8) +#define RT5659_DAC1_L_SEL_IF3 (0x2 << 8) +#define RT5659_M_ADCMIX_R (0x1 << 7) +#define RT5659_M_ADCMIX_R_SFT 7 +#define RT5659_M_DAC1_R (0x1 << 6) +#define RT5659_M_DAC1_R_SFT 6 + +/* Stereo DAC Mixer Control (0x002a) */ +#define RT5659_M_DAC_L1_STO_L (0x1 << 15) +#define RT5659_M_DAC_L1_STO_L_SFT 15 +#define RT5659_G_DAC_L1_STO_L_MASK (0x1 << 14) +#define RT5659_G_DAC_L1_STO_L_SFT 14 +#define RT5659_M_DAC_R1_STO_L (0x1 << 13) +#define RT5659_M_DAC_R1_STO_L_SFT 13 +#define RT5659_G_DAC_R1_STO_L_MASK (0x1 << 12) +#define RT5659_G_DAC_R1_STO_L_SFT 12 +#define RT5659_M_DAC_L2_STO_L (0x1 << 11) +#define RT5659_M_DAC_L2_STO_L_SFT 11 +#define RT5659_G_DAC_L2_STO_L_MASK (0x1 << 10) +#define RT5659_G_DAC_L2_STO_L_SFT 10 +#define RT5659_M_DAC_R2_STO_L (0x1 << 9) +#define RT5659_M_DAC_R2_STO_L_SFT 9 +#define RT5659_G_DAC_R2_STO_L_MASK (0x1 << 8) +#define RT5659_G_DAC_R2_STO_L_SFT 8 +#define RT5659_M_DAC_L1_STO_R (0x1 << 7) +#define RT5659_M_DAC_L1_STO_R_SFT 7 +#define RT5659_G_DAC_L1_STO_R_MASK (0x1 << 6) +#define RT5659_G_DAC_L1_STO_R_SFT 6 +#define RT5659_M_DAC_R1_STO_R (0x1 << 5) +#define RT5659_M_DAC_R1_STO_R_SFT 5 +#define RT5659_G_DAC_R1_STO_R_MASK (0x1 << 4) +#define RT5659_G_DAC_R1_STO_R_SFT 4 +#define RT5659_M_DAC_L2_STO_R (0x1 << 3) +#define RT5659_M_DAC_L2_STO_R_SFT 3 +#define RT5659_G_DAC_L2_STO_R_MASK (0x1 << 2) +#define RT5659_G_DAC_L2_STO_R_SFT 2 +#define RT5659_M_DAC_R2_STO_R (0x1 << 1) +#define RT5659_M_DAC_R2_STO_R_SFT 1 +#define RT5659_G_DAC_R2_STO_R_MASK (0x1) +#define RT5659_G_DAC_R2_STO_R_SFT 0 + +/* Mono DAC Mixer Control (0x002b) */ +#define RT5659_M_DAC_L1_MONO_L (0x1 << 15) +#define RT5659_M_DAC_L1_MONO_L_SFT 15 +#define RT5659_G_DAC_L1_MONO_L_MASK (0x1 << 14) +#define RT5659_G_DAC_L1_MONO_L_SFT 14 +#define RT5659_M_DAC_R1_MONO_L (0x1 << 13) +#define RT5659_M_DAC_R1_MONO_L_SFT 13 +#define RT5659_G_DAC_R1_MONO_L_MASK (0x1 << 12) +#define RT5659_G_DAC_R1_MONO_L_SFT 12 +#define RT5659_M_DAC_L2_MONO_L (0x1 << 11) +#define RT5659_M_DAC_L2_MONO_L_SFT 11 +#define RT5659_G_DAC_L2_MONO_L_MASK (0x1 << 10) +#define RT5659_G_DAC_L2_MONO_L_SFT 10 +#define RT5659_M_DAC_R2_MONO_L (0x1 << 9) +#define RT5659_M_DAC_R2_MONO_L_SFT 9 +#define RT5659_G_DAC_R2_MONO_L_MASK (0x1 << 8) +#define RT5659_G_DAC_R2_MONO_L_SFT 8 +#define RT5659_M_DAC_L1_MONO_R (0x1 << 7) +#define RT5659_M_DAC_L1_MONO_R_SFT 7 +#define RT5659_G_DAC_L1_MONO_R_MASK (0x1 << 6) +#define RT5659_G_DAC_L1_MONO_R_SFT 6 +#define RT5659_M_DAC_R1_MONO_R (0x1 << 5) +#define RT5659_M_DAC_R1_MONO_R_SFT 5 +#define RT5659_G_DAC_R1_MONO_R_MASK (0x1 << 4) +#define RT5659_G_DAC_R1_MONO_R_SFT 4 +#define RT5659_M_DAC_L2_MONO_R (0x1 << 3) +#define RT5659_M_DAC_L2_MONO_R_SFT 3 +#define RT5659_G_DAC_L2_MONO_R_MASK (0x1 << 2) +#define RT5659_G_DAC_L2_MONO_R_SFT 2 +#define RT5659_M_DAC_R2_MONO_R (0x1 << 1) +#define RT5659_M_DAC_R2_MONO_R_SFT 1 +#define RT5659_G_DAC_R2_MONO_R_MASK (0x1) +#define RT5659_G_DAC_R2_MONO_R_SFT 0 + +/* Digital Mixer Control (0x002c) */ +#define RT5659_M_DAC_MIX_L (0x1 << 7) +#define RT5659_M_DAC_MIX_L_SFT 7 +#define RT5659_DAC_MIX_L_MASK (0x1 << 6) +#define RT5659_DAC_MIX_L_SFT 6 +#define RT5659_M_DAC_MIX_R (0x1 << 5) +#define RT5659_M_DAC_MIX_R_SFT 5 +#define RT5659_DAC_MIX_R_MASK (0x1 << 4) +#define RT5659_DAC_MIX_R_SFT 4 + +/* Analog DAC Input Source Control (0x002d) */ +#define RT5659_A_DACL1_SEL (0x1 << 3) +#define RT5659_A_DACL1_SFT 3 +#define RT5659_A_DACR1_SEL (0x1 << 2) +#define RT5659_A_DACR1_SFT 2 +#define RT5659_A_DACL2_SEL (0x1 << 1) +#define RT5659_A_DACL2_SFT 1 +#define RT5659_A_DACR2_SEL (0x1 << 0) +#define RT5659_A_DACR2_SFT 0 + +/* Digital Interface Data Control (0x002f) */ +#define RT5659_IF2_ADC3_IN_MASK (0x3 << 14) +#define RT5659_IF2_ADC3_IN_SFT 14 +#define RT5659_IF2_ADC_IN_MASK (0x3 << 12) +#define RT5659_IF2_ADC_IN_SFT 12 +#define RT5659_IF2_DAC_SEL_MASK (0x3 << 10) +#define RT5659_IF2_DAC_SEL_SFT 10 +#define RT5659_IF2_ADC_SEL_MASK (0x3 << 8) +#define RT5659_IF2_ADC_SEL_SFT 8 +#define RT5659_IF3_DAC_SEL_MASK (0x3 << 6) +#define RT5659_IF3_DAC_SEL_SFT 6 +#define RT5659_IF3_ADC_SEL_MASK (0x3 << 4) +#define RT5659_IF3_ADC_SEL_SFT 4 +#define RT5659_IF3_ADC_IN_MASK (0x3 << 0) +#define RT5659_IF3_ADC_IN_SFT 0 + +/* PDM Output Control (0x0031) */ +#define RT5659_PDM1_L_MASK (0x1 << 15) +#define RT5659_PDM1_L_SFT 15 +#define RT5659_M_PDM1_L (0x1 << 14) +#define RT5659_M_PDM1_L_SFT 14 +#define RT5659_PDM1_R_MASK (0x1 << 13) +#define RT5659_PDM1_R_SFT 13 +#define RT5659_M_PDM1_R (0x1 << 12) +#define RT5659_M_PDM1_R_SFT 12 +#define RT5659_PDM2_BUSY (0x1 << 7) +#define RT5659_PDM1_BUSY (0x1 << 6) +#define RT5659_PDM_PATTERN (0x1 << 5) +#define RT5659_PDM_GAIN (0x1 << 4) +#define RT5659_PDM_DIV_MASK (0x3) + +/*S/PDIF Output Control (0x0036) */ +#define RT5659_SPDIF_SEL_MASK (0x3 << 0) +#define RT5659_SPDIF_SEL_SFT 0 + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5659_M_BST1_RM1_L (0x1 << 5) +#define RT5659_M_BST1_RM1_L_SFT 5 +#define RT5659_M_BST2_RM1_L (0x1 << 4) +#define RT5659_M_BST2_RM1_L_SFT 4 +#define RT5659_M_BST3_RM1_L (0x1 << 3) +#define RT5659_M_BST3_RM1_L_SFT 3 +#define RT5659_M_BST4_RM1_L (0x1 << 2) +#define RT5659_M_BST4_RM1_L_SFT 2 +#define RT5659_M_INL_RM1_L (0x1 << 1) +#define RT5659_M_INL_RM1_L_SFT 1 +#define RT5659_M_SPKVOLL_RM1_L (0x1) +#define RT5659_M_SPKVOLL_RM1_L_SFT 0 + +/* REC Right Mixer Control 2 (0x003e) */ +#define RT5659_M_BST1_RM1_R (0x1 << 5) +#define RT5659_M_BST1_RM1_R_SFT 5 +#define RT5659_M_BST2_RM1_R (0x1 << 4) +#define RT5659_M_BST2_RM1_R_SFT 4 +#define RT5659_M_BST3_RM1_R (0x1 << 3) +#define RT5659_M_BST3_RM1_R_SFT 3 +#define RT5659_M_BST4_RM1_R (0x1 << 2) +#define RT5659_M_BST4_RM1_R_SFT 2 +#define RT5659_M_INR_RM1_R (0x1 << 1) +#define RT5659_M_INR_RM1_R_SFT 1 +#define RT5659_M_HPOVOLR_RM1_R (0x1) +#define RT5659_M_HPOVOLR_RM1_R_SFT 0 + +/* SPK Left Mixer Control (0x0046) */ +#define RT5659_M_BST3_SM_L (0x1 << 4) +#define RT5659_M_BST3_SM_L_SFT 4 +#define RT5659_M_IN_R_SM_L (0x1 << 3) +#define RT5659_M_IN_R_SM_L_SFT 3 +#define RT5659_M_IN_L_SM_L (0x1 << 2) +#define RT5659_M_IN_L_SM_L_SFT 2 +#define RT5659_M_BST1_SM_L (0x1 << 1) +#define RT5659_M_BST1_SM_L_SFT 1 +#define RT5659_M_DAC_L2_SM_L (0x1) +#define RT5659_M_DAC_L2_SM_L_SFT 0 + +/* SPK Right Mixer Control (0x0047) */ +#define RT5659_M_BST3_SM_R (0x1 << 4) +#define RT5659_M_BST3_SM_R_SFT 4 +#define RT5659_M_IN_R_SM_R (0x1 << 3) +#define RT5659_M_IN_R_SM_R_SFT 3 +#define RT5659_M_IN_L_SM_R (0x1 << 2) +#define RT5659_M_IN_L_SM_R_SFT 2 +#define RT5659_M_BST4_SM_R (0x1 << 1) +#define RT5659_M_BST4_SM_R_SFT 1 +#define RT5659_M_DAC_R2_SM_R (0x1) +#define RT5659_M_DAC_R2_SM_R_SFT 0 + +/* SPO Amp Input and Gain Control (0x0048) */ +#define RT5659_M_DAC_L2_SPKOMIX (0x1 << 13) +#define RT5659_M_DAC_L2_SPKOMIX_SFT 13 +#define RT5659_M_SPKVOLL_SPKOMIX (0x1 << 12) +#define RT5659_M_SPKVOLL_SPKOMIX_SFT 12 +#define RT5659_M_DAC_R2_SPKOMIX (0x1 << 9) +#define RT5659_M_DAC_R2_SPKOMIX_SFT 9 +#define RT5659_M_SPKVOLR_SPKOMIX (0x1 << 8) +#define RT5659_M_SPKVOLR_SPKOMIX_SFT 8 + +/* MONOMIX Input and Gain Control (0x004b) */ +#define RT5659_M_MONOVOL_MA (0x1 << 9) +#define RT5659_M_MONOVOL_MA_SFT 9 +#define RT5659_M_DAC_L2_MA (0x1 << 8) +#define RT5659_M_DAC_L2_MA_SFT 8 +#define RT5659_M_BST3_MM (0x1 << 4) +#define RT5659_M_BST3_MM_SFT 4 +#define RT5659_M_BST2_MM (0x1 << 3) +#define RT5659_M_BST2_MM_SFT 3 +#define RT5659_M_BST1_MM (0x1 << 2) +#define RT5659_M_BST1_MM_SFT 2 +#define RT5659_M_DAC_R2_MM (0x1 << 1) +#define RT5659_M_DAC_R2_MM_SFT 1 +#define RT5659_M_DAC_L2_MM (0x1) +#define RT5659_M_DAC_L2_MM_SFT 0 + +/* Output Left Mixer Control 1 (0x004d) */ +#define RT5659_G_BST3_OM_L_MASK (0x7 << 12) +#define RT5659_G_BST3_OM_L_SFT 12 +#define RT5659_G_BST2_OM_L_MASK (0x7 << 9) +#define RT5659_G_BST2_OM_L_SFT 9 +#define RT5659_G_BST1_OM_L_MASK (0x7 << 6) +#define RT5659_G_BST1_OM_L_SFT 6 +#define RT5659_G_IN_L_OM_L_MASK (0x7 << 3) +#define RT5659_G_IN_L_OM_L_SFT 3 +#define RT5659_G_DAC_L2_OM_L_MASK (0x7 << 0) +#define RT5659_G_DAC_L2_OM_L_SFT 0 + +/* Output Left Mixer Input Control (0x004e) */ +#define RT5659_M_BST3_OM_L (0x1 << 4) +#define RT5659_M_BST3_OM_L_SFT 4 +#define RT5659_M_BST2_OM_L (0x1 << 3) +#define RT5659_M_BST2_OM_L_SFT 3 +#define RT5659_M_BST1_OM_L (0x1 << 2) +#define RT5659_M_BST1_OM_L_SFT 2 +#define RT5659_M_IN_L_OM_L (0x1 << 1) +#define RT5659_M_IN_L_OM_L_SFT 1 +#define RT5659_M_DAC_L2_OM_L (0x1) +#define RT5659_M_DAC_L2_OM_L_SFT 0 + +/* Output Right Mixer Input Control (0x0050) */ +#define RT5659_M_BST4_OM_R (0x1 << 4) +#define RT5659_M_BST4_OM_R_SFT 4 +#define RT5659_M_BST3_OM_R (0x1 << 3) +#define RT5659_M_BST3_OM_R_SFT 3 +#define RT5659_M_BST2_OM_R (0x1 << 2) +#define RT5659_M_BST2_OM_R_SFT 2 +#define RT5659_M_IN_R_OM_R (0x1 << 1) +#define RT5659_M_IN_R_OM_R_SFT 1 +#define RT5659_M_DAC_R2_OM_R (0x1) +#define RT5659_M_DAC_R2_OM_R_SFT 0 + +/* LOUT Mixer Control (0x0052) */ +#define RT5659_M_DAC_L2_LM (0x1 << 15) +#define RT5659_M_DAC_L2_LM_SFT 15 +#define RT5659_M_DAC_R2_LM (0x1 << 14) +#define RT5659_M_DAC_R2_LM_SFT 14 +#define RT5659_M_OV_L_LM (0x1 << 13) +#define RT5659_M_OV_L_LM_SFT 13 +#define RT5659_M_OV_R_LM (0x1 << 12) +#define RT5659_M_OV_R_LM_SFT 12 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5659_PWR_I2S1 (0x1 << 15) +#define RT5659_PWR_I2S1_BIT 15 +#define RT5659_PWR_I2S2 (0x1 << 14) +#define RT5659_PWR_I2S2_BIT 14 +#define RT5659_PWR_I2S3 (0x1 << 13) +#define RT5659_PWR_I2S3_BIT 13 +#define RT5659_PWR_SPDIF (0x1 << 12) +#define RT5659_PWR_SPDIF_BIT 12 +#define RT5659_PWR_DAC_L1 (0x1 << 11) +#define RT5659_PWR_DAC_L1_BIT 11 +#define RT5659_PWR_DAC_R1 (0x1 << 10) +#define RT5659_PWR_DAC_R1_BIT 10 +#define RT5659_PWR_DAC_L2 (0x1 << 9) +#define RT5659_PWR_DAC_L2_BIT 9 +#define RT5659_PWR_DAC_R2 (0x1 << 8) +#define RT5659_PWR_DAC_R2_BIT 8 +#define RT5659_PWR_LDO (0x1 << 7) +#define RT5659_PWR_LDO_BIT 7 +#define RT5659_PWR_ADC_L1 (0x1 << 4) +#define RT5659_PWR_ADC_L1_BIT 4 +#define RT5659_PWR_ADC_R1 (0x1 << 3) +#define RT5659_PWR_ADC_R1_BIT 3 +#define RT5659_PWR_ADC_L2 (0x1 << 2) +#define RT5659_PWR_ADC_L2_BIT 2 +#define RT5659_PWR_ADC_R2 (0x1 << 1) +#define RT5659_PWR_ADC_R2_BIT 1 +#define RT5659_PWR_CLS_D (0x1) +#define RT5659_PWR_CLS_D_BIT 0 + +/* Power Management for Digital 2 (0x0062) */ +#define RT5659_PWR_ADC_S1F (0x1 << 15) +#define RT5659_PWR_ADC_S1F_BIT 15 +#define RT5659_PWR_ADC_S2F (0x1 << 14) +#define RT5659_PWR_ADC_S2F_BIT 14 +#define RT5659_PWR_ADC_MF_L (0x1 << 13) +#define RT5659_PWR_ADC_MF_L_BIT 13 +#define RT5659_PWR_ADC_MF_R (0x1 << 12) +#define RT5659_PWR_ADC_MF_R_BIT 12 +#define RT5659_PWR_DAC_S1F (0x1 << 10) +#define RT5659_PWR_DAC_S1F_BIT 10 +#define RT5659_PWR_DAC_MF_L (0x1 << 9) +#define RT5659_PWR_DAC_MF_L_BIT 9 +#define RT5659_PWR_DAC_MF_R (0x1 << 8) +#define RT5659_PWR_DAC_MF_R_BIT 8 +#define RT5659_PWR_PDM1 (0x1 << 7) +#define RT5659_PWR_PDM1_BIT 7 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5659_PWR_VREF1 (0x1 << 15) +#define RT5659_PWR_VREF1_BIT 15 +#define RT5659_PWR_FV1 (0x1 << 14) +#define RT5659_PWR_FV1_BIT 14 +#define RT5659_PWR_VREF2 (0x1 << 13) +#define RT5659_PWR_VREF2_BIT 13 +#define RT5659_PWR_FV2 (0x1 << 12) +#define RT5659_PWR_FV2_BIT 12 +#define RT5659_PWR_VREF3 (0x1 << 11) +#define RT5659_PWR_VREF3_BIT 11 +#define RT5659_PWR_FV3 (0x1 << 10) +#define RT5659_PWR_FV3_BIT 10 +#define RT5659_PWR_MB (0x1 << 9) +#define RT5659_PWR_MB_BIT 9 +#define RT5659_PWR_LM (0x1 << 8) +#define RT5659_PWR_LM_BIT 8 +#define RT5659_PWR_BG (0x1 << 7) +#define RT5659_PWR_BG_BIT 7 +#define RT5659_PWR_MA (0x1 << 6) +#define RT5659_PWR_MA_BIT 6 +#define RT5659_PWR_HA_L (0x1 << 5) +#define RT5659_PWR_HA_L_BIT 5 +#define RT5659_PWR_HA_R (0x1 << 4) +#define RT5659_PWR_HA_R_BIT 4 + +/* Power Management for Analog 2 (0x0064) */ +#define RT5659_PWR_BST1 (0x1 << 15) +#define RT5659_PWR_BST1_BIT 15 +#define RT5659_PWR_BST2 (0x1 << 14) +#define RT5659_PWR_BST2_BIT 14 +#define RT5659_PWR_BST3 (0x1 << 13) +#define RT5659_PWR_BST3_BIT 13 +#define RT5659_PWR_BST4 (0x1 << 12) +#define RT5659_PWR_BST4_BIT 12 +#define RT5659_PWR_MB1 (0x1 << 11) +#define RT5659_PWR_MB1_BIT 11 +#define RT5659_PWR_MB2 (0x1 << 10) +#define RT5659_PWR_MB2_BIT 10 +#define RT5659_PWR_MB3 (0x1 << 9) +#define RT5659_PWR_MB3_BIT 9 +#define RT5659_PWR_BST1_P (0x1 << 6) +#define RT5659_PWR_BST1_P_BIT 6 +#define RT5659_PWR_BST2_P (0x1 << 5) +#define RT5659_PWR_BST2_P_BIT 5 +#define RT5659_PWR_BST3_P (0x1 << 4) +#define RT5659_PWR_BST3_P_BIT 4 +#define RT5659_PWR_BST4_P (0x1 << 3) +#define RT5659_PWR_BST4_P_BIT 3 +#define RT5659_PWR_JD1 (0x1 << 2) +#define RT5659_PWR_JD1_BIT 2 +#define RT5659_PWR_JD2 (0x1 << 1) +#define RT5659_PWR_JD2_BIT 1 +#define RT5659_PWR_JD3 (0x1) +#define RT5659_PWR_JD3_BIT 0 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5659_PWR_BST_L (0x1 << 8) +#define RT5659_PWR_BST_L_BIT 8 +#define RT5659_PWR_BST_R (0x1 << 7) +#define RT5659_PWR_BST_R_BIT 7 +#define RT5659_PWR_PLL (0x1 << 6) +#define RT5659_PWR_PLL_BIT 6 +#define RT5659_PWR_LDO5 (0x1 << 5) +#define RT5659_PWR_LDO5_BIT 5 +#define RT5659_PWR_LDO4 (0x1 << 4) +#define RT5659_PWR_LDO4_BIT 4 +#define RT5659_PWR_LDO3 (0x1 << 3) +#define RT5659_PWR_LDO3_BIT 3 +#define RT5659_PWR_LDO2 (0x1 << 2) +#define RT5659_PWR_LDO2_BIT 2 +#define RT5659_PWR_SVD (0x1 << 1) +#define RT5659_PWR_SVD_BIT 1 + +/* Power Management for Mixer (0x0066) */ +#define RT5659_PWR_OM_L (0x1 << 15) +#define RT5659_PWR_OM_L_BIT 15 +#define RT5659_PWR_OM_R (0x1 << 14) +#define RT5659_PWR_OM_R_BIT 14 +#define RT5659_PWR_SM_L (0x1 << 13) +#define RT5659_PWR_SM_L_BIT 13 +#define RT5659_PWR_SM_R (0x1 << 12) +#define RT5659_PWR_SM_R_BIT 12 +#define RT5659_PWR_RM1_L (0x1 << 11) +#define RT5659_PWR_RM1_L_BIT 11 +#define RT5659_PWR_RM1_R (0x1 << 10) +#define RT5659_PWR_RM1_R_BIT 10 +#define RT5659_PWR_MM (0x1 << 8) +#define RT5659_PWR_MM_BIT 8 +#define RT5659_PWR_RM2_L (0x1 << 3) +#define RT5659_PWR_RM2_L_BIT 3 +#define RT5659_PWR_RM2_R (0x1 << 2) +#define RT5659_PWR_RM2_R_BIT 2 + +/* Power Management for Volume (0x0067) */ +#define RT5659_PWR_SV_L (0x1 << 15) +#define RT5659_PWR_SV_L_BIT 15 +#define RT5659_PWR_SV_R (0x1 << 14) +#define RT5659_PWR_SV_R_BIT 14 +#define RT5659_PWR_OV_L (0x1 << 13) +#define RT5659_PWR_OV_L_BIT 13 +#define RT5659_PWR_OV_R (0x1 << 12) +#define RT5659_PWR_OV_R_BIT 12 +#define RT5659_PWR_IN_L (0x1 << 9) +#define RT5659_PWR_IN_L_BIT 9 +#define RT5659_PWR_IN_R (0x1 << 8) +#define RT5659_PWR_IN_R_BIT 8 +#define RT5659_PWR_MV (0x1 << 7) +#define RT5659_PWR_MV_BIT 7 +#define RT5659_PWR_MIC_DET (0x1 << 5) +#define RT5659_PWR_MIC_DET_BIT 5 + +/* I2S1/2/3 Audio Serial Data Port Control (0x0070 0x0071 0x0072) */ +#define RT5659_I2S_MS_MASK (0x1 << 15) +#define RT5659_I2S_MS_SFT 15 +#define RT5659_I2S_MS_M (0x0 << 15) +#define RT5659_I2S_MS_S (0x1 << 15) +#define RT5659_I2S_O_CP_MASK (0x3 << 12) +#define RT5659_I2S_O_CP_SFT 12 +#define RT5659_I2S_O_CP_OFF (0x0 << 12) +#define RT5659_I2S_O_CP_U_LAW (0x1 << 12) +#define RT5659_I2S_O_CP_A_LAW (0x2 << 12) +#define RT5659_I2S_I_CP_MASK (0x3 << 10) +#define RT5659_I2S_I_CP_SFT 10 +#define RT5659_I2S_I_CP_OFF (0x0 << 10) +#define RT5659_I2S_I_CP_U_LAW (0x1 << 10) +#define RT5659_I2S_I_CP_A_LAW (0x2 << 10) +#define RT5659_I2S_BP_MASK (0x1 << 8) +#define RT5659_I2S_BP_SFT 8 +#define RT5659_I2S_BP_NOR (0x0 << 8) +#define RT5659_I2S_BP_INV (0x1 << 8) +#define RT5659_I2S_DL_MASK (0x3 << 4) +#define RT5659_I2S_DL_SFT 4 +#define RT5659_I2S_DL_16 (0x0 << 4) +#define RT5659_I2S_DL_20 (0x1 << 4) +#define RT5659_I2S_DL_24 (0x2 << 4) +#define RT5659_I2S_DL_8 (0x3 << 4) +#define RT5659_I2S_DF_MASK (0x7) +#define RT5659_I2S_DF_SFT 0 +#define RT5659_I2S_DF_I2S (0x0) +#define RT5659_I2S_DF_LEFT (0x1) +#define RT5659_I2S_DF_PCM_A (0x2) +#define RT5659_I2S_DF_PCM_B (0x3) +#define RT5659_I2S_DF_PCM_A_N (0x6) +#define RT5659_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5659_I2S_PD1_MASK (0x7 << 12) +#define RT5659_I2S_PD1_SFT 12 +#define RT5659_I2S_PD1_1 (0x0 << 12) +#define RT5659_I2S_PD1_2 (0x1 << 12) +#define RT5659_I2S_PD1_3 (0x2 << 12) +#define RT5659_I2S_PD1_4 (0x3 << 12) +#define RT5659_I2S_PD1_6 (0x4 << 12) +#define RT5659_I2S_PD1_8 (0x5 << 12) +#define RT5659_I2S_PD1_12 (0x6 << 12) +#define RT5659_I2S_PD1_16 (0x7 << 12) +#define RT5659_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5659_I2S_BCLK_MS2_SFT 11 +#define RT5659_I2S_BCLK_MS2_32 (0x0 << 11) +#define RT5659_I2S_BCLK_MS2_64 (0x1 << 11) +#define RT5659_I2S_PD2_MASK (0x7 << 8) +#define RT5659_I2S_PD2_SFT 8 +#define RT5659_I2S_PD2_1 (0x0 << 8) +#define RT5659_I2S_PD2_2 (0x1 << 8) +#define RT5659_I2S_PD2_3 (0x2 << 8) +#define RT5659_I2S_PD2_4 (0x3 << 8) +#define RT5659_I2S_PD2_6 (0x4 << 8) +#define RT5659_I2S_PD2_8 (0x5 << 8) +#define RT5659_I2S_PD2_12 (0x6 << 8) +#define RT5659_I2S_PD2_16 (0x7 << 8) +#define RT5659_I2S_BCLK_MS3_MASK (0x1 << 7) +#define RT5659_I2S_BCLK_MS3_SFT 7 +#define RT5659_I2S_BCLK_MS3_32 (0x0 << 7) +#define RT5659_I2S_BCLK_MS3_64 (0x1 << 7) +#define RT5659_I2S_PD3_MASK (0x7 << 4) +#define RT5659_I2S_PD3_SFT 4 +#define RT5659_I2S_PD3_1 (0x0 << 4) +#define RT5659_I2S_PD3_2 (0x1 << 4) +#define RT5659_I2S_PD3_3 (0x2 << 4) +#define RT5659_I2S_PD3_4 (0x3 << 4) +#define RT5659_I2S_PD3_6 (0x4 << 4) +#define RT5659_I2S_PD3_8 (0x5 << 4) +#define RT5659_I2S_PD3_12 (0x6 << 4) +#define RT5659_I2S_PD3_16 (0x7 << 4) +#define RT5659_DAC_OSR_MASK (0x3 << 2) +#define RT5659_DAC_OSR_SFT 2 +#define RT5659_DAC_OSR_128 (0x0 << 2) +#define RT5659_DAC_OSR_64 (0x1 << 2) +#define RT5659_DAC_OSR_32 (0x2 << 2) +#define RT5659_DAC_OSR_16 (0x3 << 2) +#define RT5659_ADC_OSR_MASK (0x3) +#define RT5659_ADC_OSR_SFT 0 +#define RT5659_ADC_OSR_128 (0x0) +#define RT5659_ADC_OSR_64 (0x1) +#define RT5659_ADC_OSR_32 (0x2) +#define RT5659_ADC_OSR_16 (0x3) + +/* Digital Microphone Control (0x0075) */ +#define RT5659_DMIC_1_EN_MASK (0x1 << 15) +#define RT5659_DMIC_1_EN_SFT 15 +#define RT5659_DMIC_1_DIS (0x0 << 15) +#define RT5659_DMIC_1_EN (0x1 << 15) +#define RT5659_DMIC_2_EN_MASK (0x1 << 14) +#define RT5659_DMIC_2_EN_SFT 14 +#define RT5659_DMIC_2_DIS (0x0 << 14) +#define RT5659_DMIC_2_EN (0x1 << 14) +#define RT5659_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5659_DMIC_1L_LH_SFT 13 +#define RT5659_DMIC_1L_LH_RISING (0x0 << 13) +#define RT5659_DMIC_1L_LH_FALLING (0x1 << 13) +#define RT5659_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5659_DMIC_1R_LH_SFT 12 +#define RT5659_DMIC_1R_LH_RISING (0x0 << 12) +#define RT5659_DMIC_1R_LH_FALLING (0x1 << 12) +#define RT5659_DMIC_2_DP_MASK (0x3 << 10) +#define RT5659_DMIC_2_DP_SFT 10 +#define RT5659_DMIC_2_DP_GPIO6 (0x0 << 10) +#define RT5659_DMIC_2_DP_GPIO10 (0x1 << 10) +#define RT5659_DMIC_2_DP_GPIO12 (0x2 << 10) +#define RT5659_DMIC_2_DP_IN2P (0x3 << 10) +#define RT5659_DMIC_CLK_MASK (0x7 << 5) +#define RT5659_DMIC_CLK_SFT 5 +#define RT5659_DMIC_1_DP_MASK (0x3 << 0) +#define RT5659_DMIC_1_DP_SFT 0 +#define RT5659_DMIC_1_DP_GPIO5 (0x0 << 0) +#define RT5659_DMIC_1_DP_GPIO9 (0x1 << 0) +#define RT5659_DMIC_1_DP_GPIO11 (0x2 << 0) +#define RT5659_DMIC_1_DP_IN2N (0x3 << 0) + +/* TDM control 1 (0x0078)*/ +#define RT5659_DS_ADC_SLOT01_SFT 14 +#define RT5659_DS_ADC_SLOT23_SFT 12 +#define RT5659_DS_ADC_SLOT45_SFT 10 +#define RT5659_DS_ADC_SLOT67_SFT 8 +#define RT5659_ADCDAT_SRC_MASK 0x1f +#define RT5659_ADCDAT_SRC_SFT 0 + +/* Global Clock Control (0x0080) */ +#define RT5659_SCLK_SRC_MASK (0x3 << 14) +#define RT5659_SCLK_SRC_SFT 14 +#define RT5659_SCLK_SRC_MCLK (0x0 << 14) +#define RT5659_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5659_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5659_PLL1_SRC_MASK (0x7 << 11) +#define RT5659_PLL1_SRC_SFT 11 +#define RT5659_PLL1_SRC_MCLK (0x0 << 11) +#define RT5659_PLL1_SRC_BCLK1 (0x1 << 11) +#define RT5659_PLL1_SRC_BCLK2 (0x2 << 11) +#define RT5659_PLL1_SRC_BCLK3 (0x3 << 11) +#define RT5659_PLL1_PD_MASK (0x1 << 3) +#define RT5659_PLL1_PD_SFT 3 +#define RT5659_PLL1_PD_1 (0x0 << 3) +#define RT5659_PLL1_PD_2 (0x1 << 3) + +#define RT5659_PLL_INP_MAX 40000000 +#define RT5659_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5659_PLL_N_MAX 0x001ff +#define RT5659_PLL_N_MASK (RT5659_PLL_N_MAX << 7) +#define RT5659_PLL_N_SFT 7 +#define RT5659_PLL_K_MAX 0x001f +#define RT5659_PLL_K_MASK (RT5659_PLL_K_MAX) +#define RT5659_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5659_PLL_M_MAX 0x00f +#define RT5659_PLL_M_MASK (RT5659_PLL_M_MAX << 12) +#define RT5659_PLL_M_SFT 12 +#define RT5659_PLL_M_BP (0x1 << 11) +#define RT5659_PLL_M_BP_SFT 11 + +/* PLL tracking mode 1 (0x0083) */ +#define RT5659_I2S3_ASRC_MASK (0x1 << 13) +#define RT5659_I2S3_ASRC_SFT 13 +#define RT5659_I2S2_ASRC_MASK (0x1 << 12) +#define RT5659_I2S2_ASRC_SFT 12 +#define RT5659_I2S1_ASRC_MASK (0x1 << 11) +#define RT5659_I2S1_ASRC_SFT 11 +#define RT5659_DAC_STO_ASRC_MASK (0x1 << 10) +#define RT5659_DAC_STO_ASRC_SFT 10 +#define RT5659_DAC_MONO_L_ASRC_MASK (0x1 << 9) +#define RT5659_DAC_MONO_L_ASRC_SFT 9 +#define RT5659_DAC_MONO_R_ASRC_MASK (0x1 << 8) +#define RT5659_DAC_MONO_R_ASRC_SFT 8 +#define RT5659_DMIC_STO1_ASRC_MASK (0x1 << 7) +#define RT5659_DMIC_STO1_ASRC_SFT 7 +#define RT5659_DMIC_MONO_L_ASRC_MASK (0x1 << 5) +#define RT5659_DMIC_MONO_L_ASRC_SFT 5 +#define RT5659_DMIC_MONO_R_ASRC_MASK (0x1 << 4) +#define RT5659_DMIC_MONO_R_ASRC_SFT 4 +#define RT5659_ADC_STO1_ASRC_MASK (0x1 << 3) +#define RT5659_ADC_STO1_ASRC_SFT 3 +#define RT5659_ADC_MONO_L_ASRC_MASK (0x1 << 1) +#define RT5659_ADC_MONO_L_ASRC_SFT 1 +#define RT5659_ADC_MONO_R_ASRC_MASK (0x1) +#define RT5659_ADC_MONO_R_ASRC_SFT 0 + +/* PLL tracking mode 2 (0x0084)*/ +#define RT5659_DA_STO_T_MASK (0x7 << 12) +#define RT5659_DA_STO_T_SFT 12 +#define RT5659_DA_MONO_L_T_MASK (0x7 << 8) +#define RT5659_DA_MONO_L_T_SFT 8 +#define RT5659_DA_MONO_R_T_MASK (0x7 << 4) +#define RT5659_DA_MONO_R_T_SFT 4 +#define RT5659_AD_STO1_T_MASK (0x7) +#define RT5659_AD_STO1_T_SFT 0 + +/* PLL tracking mode 3 (0x0085)*/ +#define RT5659_AD_STO2_T_MASK (0x7 << 8) +#define RT5659_AD_STO2_T_SFT 8 +#define RT5659_AD_MONO_L_T_MASK (0x7 << 4) +#define RT5659_AD_MONO_L_T_SFT 4 +#define RT5659_AD_MONO_R_T_MASK (0x7) +#define RT5659_AD_MONO_R_T_SFT 0 + +/* ASRC Control 4 (0x0086) */ +#define RT5659_I2S1_RATE_MASK (0xf << 12) +#define RT5659_I2S1_RATE_SFT 12 +#define RT5659_I2S2_RATE_MASK (0xf << 8) +#define RT5659_I2S2_RATE_SFT 8 +#define RT5659_I2S3_RATE_MASK (0xf << 4) +#define RT5659_I2S3_RATE_SFT 4 + +/* Depop Mode Control 1 (0x8e) */ +#define RT5659_SMT_TRIG_MASK (0x1 << 15) +#define RT5659_SMT_TRIG_SFT 15 +#define RT5659_SMT_TRIG_DIS (0x0 << 15) +#define RT5659_SMT_TRIG_EN (0x1 << 15) +#define RT5659_HP_L_SMT_MASK (0x1 << 9) +#define RT5659_HP_L_SMT_SFT 9 +#define RT5659_HP_L_SMT_DIS (0x0 << 9) +#define RT5659_HP_L_SMT_EN (0x1 << 9) +#define RT5659_HP_R_SMT_MASK (0x1 << 8) +#define RT5659_HP_R_SMT_SFT 8 +#define RT5659_HP_R_SMT_DIS (0x0 << 8) +#define RT5659_HP_R_SMT_EN (0x1 << 8) +#define RT5659_HP_CD_PD_MASK (0x1 << 7) +#define RT5659_HP_CD_PD_SFT 7 +#define RT5659_HP_CD_PD_DIS (0x0 << 7) +#define RT5659_HP_CD_PD_EN (0x1 << 7) +#define RT5659_RSTN_MASK (0x1 << 6) +#define RT5659_RSTN_SFT 6 +#define RT5659_RSTN_DIS (0x0 << 6) +#define RT5659_RSTN_EN (0x1 << 6) +#define RT5659_RSTP_MASK (0x1 << 5) +#define RT5659_RSTP_SFT 5 +#define RT5659_RSTP_DIS (0x0 << 5) +#define RT5659_RSTP_EN (0x1 << 5) +#define RT5659_HP_CO_MASK (0x1 << 4) +#define RT5659_HP_CO_SFT 4 +#define RT5659_HP_CO_DIS (0x0 << 4) +#define RT5659_HP_CO_EN (0x1 << 4) +#define RT5659_HP_CP_MASK (0x1 << 3) +#define RT5659_HP_CP_SFT 3 +#define RT5659_HP_CP_PD (0x0 << 3) +#define RT5659_HP_CP_PU (0x1 << 3) +#define RT5659_HP_SG_MASK (0x1 << 2) +#define RT5659_HP_SG_SFT 2 +#define RT5659_HP_SG_DIS (0x0 << 2) +#define RT5659_HP_SG_EN (0x1 << 2) +#define RT5659_HP_DP_MASK (0x1 << 1) +#define RT5659_HP_DP_SFT 1 +#define RT5659_HP_DP_PD (0x0 << 1) +#define RT5659_HP_DP_PU (0x1 << 1) +#define RT5659_HP_CB_MASK (0x1) +#define RT5659_HP_CB_SFT 0 +#define RT5659_HP_CB_PD (0x0) +#define RT5659_HP_CB_PU (0x1) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5659_DEPOP_MASK (0x1 << 13) +#define RT5659_DEPOP_SFT 13 +#define RT5659_DEPOP_AUTO (0x0 << 13) +#define RT5659_DEPOP_MAN (0x1 << 13) +#define RT5659_RAMP_MASK (0x1 << 12) +#define RT5659_RAMP_SFT 12 +#define RT5659_RAMP_DIS (0x0 << 12) +#define RT5659_RAMP_EN (0x1 << 12) +#define RT5659_BPS_MASK (0x1 << 11) +#define RT5659_BPS_SFT 11 +#define RT5659_BPS_DIS (0x0 << 11) +#define RT5659_BPS_EN (0x1 << 11) +#define RT5659_FAST_UPDN_MASK (0x1 << 10) +#define RT5659_FAST_UPDN_SFT 10 +#define RT5659_FAST_UPDN_DIS (0x0 << 10) +#define RT5659_FAST_UPDN_EN (0x1 << 10) +#define RT5659_MRES_MASK (0x3 << 8) +#define RT5659_MRES_SFT 8 +#define RT5659_MRES_15MO (0x0 << 8) +#define RT5659_MRES_25MO (0x1 << 8) +#define RT5659_MRES_35MO (0x2 << 8) +#define RT5659_MRES_45MO (0x3 << 8) +#define RT5659_VLO_MASK (0x1 << 7) +#define RT5659_VLO_SFT 7 +#define RT5659_VLO_3V (0x0 << 7) +#define RT5659_VLO_32V (0x1 << 7) +#define RT5659_DIG_DP_MASK (0x1 << 6) +#define RT5659_DIG_DP_SFT 6 +#define RT5659_DIG_DP_DIS (0x0 << 6) +#define RT5659_DIG_DP_EN (0x1 << 6) +#define RT5659_DP_TH_MASK (0x3 << 4) +#define RT5659_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5659_CP_SYS_MASK (0x7 << 12) +#define RT5659_CP_SYS_SFT 12 +#define RT5659_CP_FQ1_MASK (0x7 << 8) +#define RT5659_CP_FQ1_SFT 8 +#define RT5659_CP_FQ2_MASK (0x7 << 4) +#define RT5659_CP_FQ2_SFT 4 +#define RT5659_CP_FQ3_MASK (0x7) +#define RT5659_CP_FQ3_SFT 0 +#define RT5659_CP_FQ_1_5_KHZ 0 +#define RT5659_CP_FQ_3_KHZ 1 +#define RT5659_CP_FQ_6_KHZ 2 +#define RT5659_CP_FQ_12_KHZ 3 +#define RT5659_CP_FQ_24_KHZ 4 +#define RT5659_CP_FQ_48_KHZ 5 +#define RT5659_CP_FQ_96_KHZ 6 +#define RT5659_CP_FQ_192_KHZ 7 + +/* HPOUT charge pump 1 (0x0091) */ +#define RT5659_OSW_L_MASK (0x1 << 11) +#define RT5659_OSW_L_SFT 11 +#define RT5659_OSW_L_DIS (0x0 << 11) +#define RT5659_OSW_L_EN (0x1 << 11) +#define RT5659_OSW_R_MASK (0x1 << 10) +#define RT5659_OSW_R_SFT 10 +#define RT5659_OSW_R_DIS (0x0 << 10) +#define RT5659_OSW_R_EN (0x1 << 10) +#define RT5659_PM_HP_MASK (0x3 << 8) +#define RT5659_PM_HP_SFT 8 +#define RT5659_PM_HP_LV (0x0 << 8) +#define RT5659_PM_HP_MV (0x1 << 8) +#define RT5659_PM_HP_HV (0x2 << 8) +#define RT5659_IB_HP_MASK (0x3 << 6) +#define RT5659_IB_HP_SFT 6 +#define RT5659_IB_HP_125IL (0x0 << 6) +#define RT5659_IB_HP_25IL (0x1 << 6) +#define RT5659_IB_HP_5IL (0x2 << 6) +#define RT5659_IB_HP_1IL (0x3 << 6) + +/* PV detection and SPK gain control (0x92) */ +#define RT5659_PVDD_DET_MASK (0x1 << 15) +#define RT5659_PVDD_DET_SFT 15 +#define RT5659_PVDD_DET_DIS (0x0 << 15) +#define RT5659_PVDD_DET_EN (0x1 << 15) +#define RT5659_SPK_AG_MASK (0x1 << 14) +#define RT5659_SPK_AG_SFT 14 +#define RT5659_SPK_AG_DIS (0x0 << 14) +#define RT5659_SPK_AG_EN (0x1 << 14) + +/* Micbias Control (0x93) */ +#define RT5659_MIC1_BS_MASK (0x1 << 15) +#define RT5659_MIC1_BS_SFT 15 +#define RT5659_MIC1_BS_9AV (0x0 << 15) +#define RT5659_MIC1_BS_75AV (0x1 << 15) +#define RT5659_MIC2_BS_MASK (0x1 << 14) +#define RT5659_MIC2_BS_SFT 14 +#define RT5659_MIC2_BS_9AV (0x0 << 14) +#define RT5659_MIC2_BS_75AV (0x1 << 14) +#define RT5659_MIC1_CLK_MASK (0x1 << 13) +#define RT5659_MIC1_CLK_SFT 13 +#define RT5659_MIC1_CLK_DIS (0x0 << 13) +#define RT5659_MIC1_CLK_EN (0x1 << 13) +#define RT5659_MIC2_CLK_MASK (0x1 << 12) +#define RT5659_MIC2_CLK_SFT 12 +#define RT5659_MIC2_CLK_DIS (0x0 << 12) +#define RT5659_MIC2_CLK_EN (0x1 << 12) +#define RT5659_MIC1_OVCD_MASK (0x1 << 11) +#define RT5659_MIC1_OVCD_SFT 11 +#define RT5659_MIC1_OVCD_DIS (0x0 << 11) +#define RT5659_MIC1_OVCD_EN (0x1 << 11) +#define RT5659_MIC1_OVTH_MASK (0x3 << 9) +#define RT5659_MIC1_OVTH_SFT 9 +#define RT5659_MIC1_OVTH_600UA (0x0 << 9) +#define RT5659_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5659_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5659_MIC2_OVCD_MASK (0x1 << 8) +#define RT5659_MIC2_OVCD_SFT 8 +#define RT5659_MIC2_OVCD_DIS (0x0 << 8) +#define RT5659_MIC2_OVCD_EN (0x1 << 8) +#define RT5659_MIC2_OVTH_MASK (0x3 << 6) +#define RT5659_MIC2_OVTH_SFT 6 +#define RT5659_MIC2_OVTH_600UA (0x0 << 6) +#define RT5659_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5659_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5659_PWR_MB_MASK (0x1 << 5) +#define RT5659_PWR_MB_SFT 5 +#define RT5659_PWR_MB_PD (0x0 << 5) +#define RT5659_PWR_MB_PU (0x1 << 5) +#define RT5659_PWR_CLK25M_MASK (0x1 << 4) +#define RT5659_PWR_CLK25M_SFT 4 +#define RT5659_PWR_CLK25M_PD (0x0 << 4) +#define RT5659_PWR_CLK25M_PU (0x1 << 4) + +/* REC Mixer 2 Left Control 2 (0x009c) */ +#define RT5659_M_BST1_RM2_L (0x1 << 5) +#define RT5659_M_BST1_RM2_L_SFT 5 +#define RT5659_M_BST2_RM2_L (0x1 << 4) +#define RT5659_M_BST2_RM2_L_SFT 4 +#define RT5659_M_BST3_RM2_L (0x1 << 3) +#define RT5659_M_BST3_RM2_L_SFT 3 +#define RT5659_M_BST4_RM2_L (0x1 << 2) +#define RT5659_M_BST4_RM2_L_SFT 2 +#define RT5659_M_OUTVOLL_RM2_L (0x1 << 1) +#define RT5659_M_OUTVOLL_RM2_L_SFT 1 +#define RT5659_M_SPKVOL_RM2_L (0x1) +#define RT5659_M_SPKVOL_RM2_L_SFT 0 + +/* REC Mixer 2 Right Control 2 (0x009e) */ +#define RT5659_M_BST1_RM2_R (0x1 << 5) +#define RT5659_M_BST1_RM2_R_SFT 5 +#define RT5659_M_BST2_RM2_R (0x1 << 4) +#define RT5659_M_BST2_RM2_R_SFT 4 +#define RT5659_M_BST3_RM2_R (0x1 << 3) +#define RT5659_M_BST3_RM2_R_SFT 3 +#define RT5659_M_BST4_RM2_R (0x1 << 2) +#define RT5659_M_BST4_RM2_R_SFT 2 +#define RT5659_M_OUTVOLR_RM2_R (0x1 << 1) +#define RT5659_M_OUTVOLR_RM2_R_SFT 1 +#define RT5659_M_MONOVOL_RM2_R (0x1) +#define RT5659_M_MONOVOL_RM2_R_SFT 0 + +/* Class D Output Control (0x00a0) */ +#define RT5659_POW_CLSD_DB_MASK (0x1 << 9) +#define RT5659_POW_CLSD_DB_EN (0x1 << 9) +#define RT5659_POW_CLSD_DB_DIS (0x0 << 9) + +/* EQ Control 1 (0x00b0) */ +#define RT5659_EQ_SRC_DAC (0x0 << 15) +#define RT5659_EQ_SRC_ADC (0x1 << 15) +#define RT5659_EQ_UPD (0x1 << 14) +#define RT5659_EQ_UPD_BIT 14 +#define RT5659_EQ_CD_MASK (0x1 << 13) +#define RT5659_EQ_CD_SFT 13 +#define RT5659_EQ_CD_DIS (0x0 << 13) +#define RT5659_EQ_CD_EN (0x1 << 13) +#define RT5659_EQ_DITH_MASK (0x3 << 8) +#define RT5659_EQ_DITH_SFT 8 +#define RT5659_EQ_DITH_NOR (0x0 << 8) +#define RT5659_EQ_DITH_LSB (0x1 << 8) +#define RT5659_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5659_EQ_DITH_LSB_2 (0x3 << 8) + +/* IRQ Control 1 (0x00b7) */ +#define RT5659_JD1_1_EN_MASK (0x1 << 15) +#define RT5659_JD1_1_EN_SFT 15 +#define RT5659_JD1_1_DIS (0x0 << 15) +#define RT5659_JD1_1_EN (0x1 << 15) +#define RT5659_JD1_2_EN_MASK (0x1 << 12) +#define RT5659_JD1_2_EN_SFT 12 +#define RT5659_JD1_2_DIS (0x0 << 12) +#define RT5659_JD1_2_EN (0x1 << 12) +#define RT5659_IL_IRQ_MASK (0x1 << 3) +#define RT5659_IL_IRQ_DIS (0x0 << 3) +#define RT5659_IL_IRQ_EN (0x1 << 3) + +/* IRQ Control 5 (0x00ba) */ +#define RT5659_IRQ_JD_EN (0x1 << 3) +#define RT5659_IRQ_JD_EN_SFT 3 + +/* GPIO Control 1 (0x00c0) */ +#define RT5659_GP1_PIN_MASK (0x1 << 15) +#define RT5659_GP1_PIN_SFT 15 +#define RT5659_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5659_GP1_PIN_IRQ (0x1 << 15) +#define RT5659_GP2_PIN_MASK (0x1 << 14) +#define RT5659_GP2_PIN_SFT 14 +#define RT5659_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5659_GP2_PIN_DMIC1_SCL (0x1 << 14) +#define RT5659_GP3_PIN_MASK (0x1 << 13) +#define RT5659_GP3_PIN_SFT 13 +#define RT5659_GP3_PIN_GPIO3 (0x0 << 13) +#define RT5659_GP3_PIN_PDM_SCL (0x1 << 13) +#define RT5659_GP4_PIN_MASK (0x1 << 12) +#define RT5659_GP4_PIN_SFT 12 +#define RT5659_GP4_PIN_GPIO4 (0x0 << 12) +#define RT5659_GP4_PIN_PDM_SDA (0x1 << 12) +#define RT5659_GP5_PIN_MASK (0x1 << 11) +#define RT5659_GP5_PIN_SFT 11 +#define RT5659_GP5_PIN_GPIO5 (0x0 << 11) +#define RT5659_GP5_PIN_DMIC1_SDA (0x1 << 11) +#define RT5659_GP6_PIN_MASK (0x1 << 10) +#define RT5659_GP6_PIN_SFT 10 +#define RT5659_GP6_PIN_GPIO6 (0x0 << 10) +#define RT5659_GP6_PIN_DMIC2_SDA (0x1 << 10) +#define RT5659_GP7_PIN_MASK (0x1 << 9) +#define RT5659_GP7_PIN_SFT 9 +#define RT5659_GP7_PIN_GPIO7 (0x0 << 9) +#define RT5659_GP7_PIN_PDM_SCL (0x1 << 9) +#define RT5659_GP8_PIN_MASK (0x1 << 8) +#define RT5659_GP8_PIN_SFT 8 +#define RT5659_GP8_PIN_GPIO8 (0x0 << 8) +#define RT5659_GP8_PIN_PDM_SDA (0x1 << 8) +#define RT5659_GP9_PIN_MASK (0x1 << 7) +#define RT5659_GP9_PIN_SFT 7 +#define RT5659_GP9_PIN_GPIO9 (0x0 << 7) +#define RT5659_GP9_PIN_DMIC1_SDA (0x1 << 7) +#define RT5659_GP10_PIN_MASK (0x1 << 6) +#define RT5659_GP10_PIN_SFT 6 +#define RT5659_GP10_PIN_GPIO10 (0x0 << 6) +#define RT5659_GP10_PIN_DMIC2_SDA (0x1 << 6) +#define RT5659_GP11_PIN_MASK (0x1 << 5) +#define RT5659_GP11_PIN_SFT 5 +#define RT5659_GP11_PIN_GPIO11 (0x0 << 5) +#define RT5659_GP11_PIN_DMIC1_SDA (0x1 << 5) +#define RT5659_GP12_PIN_MASK (0x1 << 4) +#define RT5659_GP12_PIN_SFT 4 +#define RT5659_GP12_PIN_GPIO12 (0x0 << 4) +#define RT5659_GP12_PIN_DMIC2_SDA (0x1 << 4) +#define RT5659_GP13_PIN_MASK (0x3 << 2) +#define RT5659_GP13_PIN_SFT 2 +#define RT5659_GP13_PIN_GPIO13 (0x0 << 2) +#define RT5659_GP13_PIN_SPDIF_SDA (0x1 << 2) +#define RT5659_GP13_PIN_DMIC2_SCL (0x2 << 2) +#define RT5659_GP13_PIN_PDM_SCL (0x3 << 2) +#define RT5659_GP15_PIN_MASK (0x3) +#define RT5659_GP15_PIN_SFT 0 +#define RT5659_GP15_PIN_GPIO15 (0x0) +#define RT5659_GP15_PIN_DMIC3_SCL (0x1) +#define RT5659_GP15_PIN_PDM_SDA (0x2) + +/* GPIO Control 2 (0x00c1)*/ +#define RT5659_GP1_PF_IN (0x0 << 2) +#define RT5659_GP1_PF_OUT (0x1 << 2) +#define RT5659_GP1_PF_MASK (0x1 << 2) +#define RT5659_GP1_PF_SFT 2 + +/* GPIO Control 3 (0x00c2) */ +#define RT5659_I2S2_PIN_MASK (0x1 << 15) +#define RT5659_I2S2_PIN_SFT 15 +#define RT5659_I2S2_PIN_I2S (0x0 << 15) +#define RT5659_I2S2_PIN_GPIO (0x1 << 15) + +/* Soft volume and zero cross control 1 (0x00d9) */ +#define RT5659_SV_MASK (0x1 << 15) +#define RT5659_SV_SFT 15 +#define RT5659_SV_DIS (0x0 << 15) +#define RT5659_SV_EN (0x1 << 15) +#define RT5659_OUT_SV_MASK (0x1 << 13) +#define RT5659_OUT_SV_SFT 13 +#define RT5659_OUT_SV_DIS (0x0 << 13) +#define RT5659_OUT_SV_EN (0x1 << 13) +#define RT5659_HP_SV_MASK (0x1 << 12) +#define RT5659_HP_SV_SFT 12 +#define RT5659_HP_SV_DIS (0x0 << 12) +#define RT5659_HP_SV_EN (0x1 << 12) +#define RT5659_ZCD_DIG_MASK (0x1 << 11) +#define RT5659_ZCD_DIG_SFT 11 +#define RT5659_ZCD_DIG_DIS (0x0 << 11) +#define RT5659_ZCD_DIG_EN (0x1 << 11) +#define RT5659_ZCD_MASK (0x1 << 10) +#define RT5659_ZCD_SFT 10 +#define RT5659_ZCD_PD (0x0 << 10) +#define RT5659_ZCD_PU (0x1 << 10) +#define RT5659_SV_DLY_MASK (0xf) +#define RT5659_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0x00da) */ +#define RT5659_ZCD_HP_MASK (0x1 << 15) +#define RT5659_ZCD_HP_SFT 15 +#define RT5659_ZCD_HP_DIS (0x0 << 15) +#define RT5659_ZCD_HP_EN (0x1 << 15) + +/* 4 Button Inline Command Control 2 (0x00e0) */ +#define RT5659_4BTN_IL_MASK (0x1 << 15) +#define RT5659_4BTN_IL_EN (0x1 << 15) +#define RT5659_4BTN_IL_DIS (0x0 << 15) + +/* Analog JD Control 1 (0x00f0) */ +#define RT5659_JD1_MODE_MASK (0x3 << 0) +#define RT5659_JD1_MODE_0 (0x0 << 0) +#define RT5659_JD1_MODE_1 (0x1 << 0) +#define RT5659_JD1_MODE_2 (0x2 << 0) + +/* Jack Detect Control 3 (0x00f8) */ +#define RT5659_JD_TRI_HPO_SEL_MASK (0x7) +#define RT5659_JD_TRI_HPO_SEL_SFT (0) +#define RT5659_JD_HPO_GPIO_JD1 (0x0) +#define RT5659_JD_HPO_JD1_1 (0x1) +#define RT5659_JD_HPO_JD1_2 (0x2) +#define RT5659_JD_HPO_JD2 (0x3) +#define RT5659_JD_HPO_GPIO_JD2 (0x4) +#define RT5659_JD_HPO_JD3 (0x5) +#define RT5659_JD_HPO_JD_D (0x6) + +/* Digital Misc Control (0x00fa) */ +#define RT5659_AM_MASK (0x1 << 7) +#define RT5659_AM_EN (0x1 << 7) +#define RT5659_AM_DIS (0x1 << 7) +#define RT5659_DIG_GATE_CTRL 0x1 +#define RT5659_DIG_GATE_CTRL_SFT (0) + +/* Chopper and Clock control for ADC (0x011c)*/ +#define RT5659_M_RF_DIG_MASK (0x1 << 12) +#define RT5659_M_RF_DIG_SFT 12 +#define RT5659_M_RI_DIG (0x1 << 11) + +/* Chopper and Clock control for DAC (0x013a)*/ +#define RT5659_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5659_CKXEN_DAC1_SFT 13 +#define RT5659_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5659_CKGEN_DAC1_SFT 12 +#define RT5659_CKXEN_DAC2_MASK (0x1 << 5) +#define RT5659_CKXEN_DAC2_SFT 5 +#define RT5659_CKGEN_DAC2_MASK (0x1 << 4) +#define RT5659_CKGEN_DAC2_SFT 4 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5659_CKXEN_ADC1_MASK (0x1 << 13) +#define RT5659_CKXEN_ADC1_SFT 13 +#define RT5659_CKGEN_ADC1_MASK (0x1 << 12) +#define RT5659_CKGEN_ADC1_SFT 12 +#define RT5659_CKXEN_ADC2_MASK (0x1 << 5) +#define RT5659_CKXEN_ADC2_SFT 5 +#define RT5659_CKGEN_ADC2_MASK (0x1 << 4) +#define RT5659_CKGEN_ADC2_SFT 4 + +/* Test Mode Control 1 (0x0145) */ +#define RT5659_AD2DA_LB_MASK (0x1 << 9) +#define RT5659_AD2DA_LB_SFT 9 + +/* Stereo Noise Gate Control 1 (0x0160) */ +#define RT5659_NG2_EN_MASK (0x1 << 15) +#define RT5659_NG2_EN (0x1 << 15) +#define RT5659_NG2_DIS (0x0 << 15) + +/* System Clock Source */ +enum { + RT5659_SCLK_S_MCLK, + RT5659_SCLK_S_PLL1, + RT5659_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5659_PLL1_S_MCLK, + RT5659_PLL1_S_BCLK1, + RT5659_PLL1_S_BCLK2, + RT5659_PLL1_S_BCLK3, + RT5659_PLL1_S_BCLK4, +}; + +enum { + RT5659_AIF1, + RT5659_AIF2, + RT5659_AIF3, + RT5659_AIF4, + RT5659_AIFS, +}; + +struct rt5659_pll_code { + bool m_bp; + int m_code; + int n_code; + int k_code; +}; + +struct rt5659_priv { + struct snd_soc_component *component; + struct rt5659_platform_data pdata; + struct regmap *regmap; + struct gpio_desc *gpiod_ldo1_en; + struct gpio_desc *gpiod_reset; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct clk *mclk; + + int sysclk; + int sysclk_src; + int lrck[RT5659_AIFS]; + int bclk[RT5659_AIFS]; + int master[RT5659_AIFS]; + int v_id; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; + bool hda_hp_plugged; + bool hda_mic_plugged; +}; + +int rt5659_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack); + +#endif /* __RT5659_H__ */ diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c new file mode 100644 index 0000000000..0cecfd6024 --- /dev/null +++ b/sound/soc/codecs/rt5660.c @@ -0,0 +1,1349 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5660.c -- RT5660 ALSA SoC audio codec driver + * + * Copyright 2016 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5660.h" + +#define RT5660_DEVICE_ID 0x6338 + +#define RT5660_PR_RANGE_BASE (0xff + 1) +#define RT5660_PR_SPACING 0x100 + +#define RT5660_PR_BASE (RT5660_PR_RANGE_BASE + (0 * RT5660_PR_SPACING)) + +static const struct regmap_range_cfg rt5660_ranges[] = { + { .name = "PR", .range_min = RT5660_PR_BASE, + .range_max = RT5660_PR_BASE + 0xf3, + .selector_reg = RT5660_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5660_PRIV_DATA, + .window_len = 0x1, }, +}; + +static const struct reg_sequence rt5660_patch[] = { + { RT5660_ALC_PGA_CTRL2, 0x44c3 }, + { RT5660_PR_BASE + 0x3d, 0x2600 }, +}; + +static const struct reg_default rt5660_reg[] = { + { 0x00, 0x0000 }, + { 0x01, 0xc800 }, + { 0x02, 0xc8c8 }, + { 0x0d, 0x1010 }, + { 0x0e, 0x1010 }, + { 0x19, 0xafaf }, + { 0x1c, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x27, 0x6060 }, + { 0x29, 0x8080 }, + { 0x2a, 0x4242 }, + { 0x2f, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x45, 0xe000 }, + { 0x46, 0x003e }, + { 0x48, 0xf800 }, + { 0x4a, 0x0004 }, + { 0x4d, 0x0000 }, + { 0x4e, 0x0000 }, + { 0x4f, 0x01ff }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x01ff }, + { 0x61, 0x0000 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c0 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x70, 0x8000 }, + { 0x73, 0x7000 }, + { 0x74, 0x3c00 }, + { 0x75, 0x2800 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x8c, 0x0228 }, + { 0x8d, 0xa000 }, + { 0x8e, 0x0000 }, + { 0x92, 0x0000 }, + { 0x93, 0x3000 }, + { 0xa1, 0x0059 }, + { 0xa2, 0x0001 }, + { 0xa3, 0x5c80 }, + { 0xa4, 0x0146 }, + { 0xa5, 0x1f1f }, + { 0xa6, 0x78c6 }, + { 0xa7, 0xe5ec }, + { 0xa8, 0xba61 }, + { 0xa9, 0x3c78 }, + { 0xaa, 0x8ae2 }, + { 0xab, 0xe5ec }, + { 0xac, 0xc600 }, + { 0xad, 0xba61 }, + { 0xae, 0x17ed }, + { 0xb0, 0x2080 }, + { 0xb1, 0x0000 }, + { 0xb3, 0x001f }, + { 0xb4, 0x020c }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xb7, 0x4000 }, + { 0xbb, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x0100 }, + { 0xc0, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xd3, 0xa220 }, + { 0xd9, 0x0809 }, + { 0xda, 0x0000 }, + { 0xe0, 0x8000 }, + { 0xe1, 0x0200 }, + { 0xe2, 0x8000 }, + { 0xe3, 0x0200 }, + { 0xe4, 0x0f20 }, + { 0xe5, 0x001f }, + { 0xe6, 0x020c }, + { 0xe7, 0x1f00 }, + { 0xe8, 0x0000 }, + { 0xe9, 0x4000 }, + { 0xea, 0x00a6 }, + { 0xeb, 0x04c3 }, + { 0xec, 0x27c8 }, + { 0xed, 0x7418 }, + { 0xee, 0xbf50 }, + { 0xef, 0x0045 }, + { 0xf0, 0x0007 }, + { 0xfa, 0x0000 }, + { 0xfd, 0x0000 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6338 }, +}; + +static bool rt5660_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5660_ranges); i++) + if ((reg >= rt5660_ranges[i].window_start && + reg <= rt5660_ranges[i].window_start + + rt5660_ranges[i].window_len) || + (reg >= rt5660_ranges[i].range_min && + reg <= rt5660_ranges[i].range_max)) + return true; + + switch (reg) { + case RT5660_RESET: + case RT5660_PRIV_DATA: + case RT5660_EQ_CTRL1: + case RT5660_IRQ_CTRL2: + case RT5660_INT_IRQ_ST: + case RT5660_VENDOR_ID: + case RT5660_VENDOR_ID1: + case RT5660_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool rt5660_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5660_ranges); i++) + if ((reg >= rt5660_ranges[i].window_start && + reg <= rt5660_ranges[i].window_start + + rt5660_ranges[i].window_len) || + (reg >= rt5660_ranges[i].range_min && + reg <= rt5660_ranges[i].range_max)) + return true; + + switch (reg) { + case RT5660_RESET: + case RT5660_SPK_VOL: + case RT5660_LOUT_VOL: + case RT5660_IN1_IN2: + case RT5660_IN3_IN4: + case RT5660_DAC1_DIG_VOL: + case RT5660_STO1_ADC_DIG_VOL: + case RT5660_ADC_BST_VOL1: + case RT5660_STO1_ADC_MIXER: + case RT5660_AD_DA_MIXER: + case RT5660_STO_DAC_MIXER: + case RT5660_DIG_INF1_DATA: + case RT5660_REC_L1_MIXER: + case RT5660_REC_L2_MIXER: + case RT5660_REC_R1_MIXER: + case RT5660_REC_R2_MIXER: + case RT5660_LOUT_MIXER: + case RT5660_SPK_MIXER: + case RT5660_SPO_MIXER: + case RT5660_SPO_CLSD_RATIO: + case RT5660_OUT_L_GAIN1: + case RT5660_OUT_L_GAIN2: + case RT5660_OUT_L1_MIXER: + case RT5660_OUT_R_GAIN1: + case RT5660_OUT_R_GAIN2: + case RT5660_OUT_R1_MIXER: + case RT5660_PWR_DIG1: + case RT5660_PWR_DIG2: + case RT5660_PWR_ANLG1: + case RT5660_PWR_ANLG2: + case RT5660_PWR_MIXER: + case RT5660_PWR_VOL: + case RT5660_PRIV_INDEX: + case RT5660_PRIV_DATA: + case RT5660_I2S1_SDP: + case RT5660_ADDA_CLK1: + case RT5660_ADDA_CLK2: + case RT5660_DMIC_CTRL1: + case RT5660_GLB_CLK: + case RT5660_PLL_CTRL1: + case RT5660_PLL_CTRL2: + case RT5660_CLSD_AMP_OC_CTRL: + case RT5660_CLSD_AMP_CTRL: + case RT5660_LOUT_AMP_CTRL: + case RT5660_SPK_AMP_SPKVDD: + case RT5660_MICBIAS: + case RT5660_CLSD_OUT_CTRL1: + case RT5660_CLSD_OUT_CTRL2: + case RT5660_DIPOLE_MIC_CTRL1: + case RT5660_DIPOLE_MIC_CTRL2: + case RT5660_DIPOLE_MIC_CTRL3: + case RT5660_DIPOLE_MIC_CTRL4: + case RT5660_DIPOLE_MIC_CTRL5: + case RT5660_DIPOLE_MIC_CTRL6: + case RT5660_DIPOLE_MIC_CTRL7: + case RT5660_DIPOLE_MIC_CTRL8: + case RT5660_DIPOLE_MIC_CTRL9: + case RT5660_DIPOLE_MIC_CTRL10: + case RT5660_DIPOLE_MIC_CTRL11: + case RT5660_DIPOLE_MIC_CTRL12: + case RT5660_EQ_CTRL1: + case RT5660_EQ_CTRL2: + case RT5660_DRC_AGC_CTRL1: + case RT5660_DRC_AGC_CTRL2: + case RT5660_DRC_AGC_CTRL3: + case RT5660_DRC_AGC_CTRL4: + case RT5660_DRC_AGC_CTRL5: + case RT5660_JD_CTRL: + case RT5660_IRQ_CTRL1: + case RT5660_IRQ_CTRL2: + case RT5660_INT_IRQ_ST: + case RT5660_GPIO_CTRL1: + case RT5660_GPIO_CTRL2: + case RT5660_WIND_FILTER_CTRL1: + case RT5660_SV_ZCD1: + case RT5660_SV_ZCD2: + case RT5660_DRC1_LM_CTRL1: + case RT5660_DRC1_LM_CTRL2: + case RT5660_DRC2_LM_CTRL1: + case RT5660_DRC2_LM_CTRL2: + case RT5660_MULTI_DRC_CTRL: + case RT5660_DRC2_CTRL1: + case RT5660_DRC2_CTRL2: + case RT5660_DRC2_CTRL3: + case RT5660_DRC2_CTRL4: + case RT5660_DRC2_CTRL5: + case RT5660_ALC_PGA_CTRL1: + case RT5660_ALC_PGA_CTRL2: + case RT5660_ALC_PGA_CTRL3: + case RT5660_ALC_PGA_CTRL4: + case RT5660_ALC_PGA_CTRL5: + case RT5660_ALC_PGA_CTRL6: + case RT5660_ALC_PGA_CTRL7: + case RT5660_GEN_CTRL1: + case RT5660_GEN_CTRL2: + case RT5660_GEN_CTRL3: + case RT5660_VENDOR_ID: + case RT5660_VENDOR_ID1: + case RT5660_VENDOR_ID2: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(rt5660_out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(rt5660_dac_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(rt5660_adc_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(rt5660_adc_bst_tlv, 0, 1200, 0); +static const DECLARE_TLV_DB_SCALE(rt5660_bst_tlv, -1200, 75, 0); + +static const struct snd_kcontrol_new rt5660_snd_controls[] = { + /* Speaker Output Volume */ + SOC_SINGLE("Speaker Playback Switch", RT5660_SPK_VOL, RT5660_L_MUTE_SFT, + 1, 1), + SOC_SINGLE_TLV("Speaker Playback Volume", RT5660_SPK_VOL, + RT5660_L_VOL_SFT, 39, 1, rt5660_out_vol_tlv), + + /* OUTPUT Control */ + SOC_DOUBLE("OUT Playback Switch", RT5660_LOUT_VOL, RT5660_L_MUTE_SFT, + RT5660_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("OUT Playback Volume", RT5660_LOUT_VOL, RT5660_L_VOL_SFT, + RT5660_R_VOL_SFT, 39, 1, rt5660_out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5660_DAC1_DIG_VOL, + RT5660_DAC_L1_VOL_SFT, RT5660_DAC_R1_VOL_SFT, 87, 0, + rt5660_dac_vol_tlv), + + /* IN1/IN2/IN3 Control */ + SOC_SINGLE_TLV("IN1 Boost Volume", RT5660_IN1_IN2, RT5660_BST_SFT1, 69, + 0, rt5660_bst_tlv), + SOC_SINGLE_TLV("IN2 Boost Volume", RT5660_IN1_IN2, RT5660_BST_SFT2, 69, + 0, rt5660_bst_tlv), + SOC_SINGLE_TLV("IN3 Boost Volume", RT5660_IN3_IN4, RT5660_BST_SFT3, 69, + 0, rt5660_bst_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5660_STO1_ADC_DIG_VOL, + RT5660_L_MUTE_SFT, RT5660_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5660_STO1_ADC_DIG_VOL, + RT5660_ADC_L_VOL_SFT, RT5660_ADC_R_VOL_SFT, 63, 0, + rt5660_adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5660_ADC_BST_VOL1, + RT5660_STO1_ADC_L_BST_SFT, RT5660_STO1_ADC_R_BST_SFT, 3, 0, + rt5660_adc_bst_tlv), +}; + +/** + * rt5660_set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + */ +static int rt5660_set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component); + int idx, rate; + + rate = rt5660->sysclk / rl6231_get_pre_div(rt5660->regmap, + RT5660_ADDA_CLK1, RT5660_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); + if (idx < 0) + dev_err(component->dev, "Failed to set DMIC clock\n"); + else + snd_soc_component_update_bits(component, RT5660_DMIC_CTRL1, + RT5660_DMIC_CLK_MASK, idx << RT5660_DMIC_CLK_SFT); + + return idx; +} + +static int rt5660_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int val; + + val = snd_soc_component_read(component, RT5660_GLB_CLK); + val &= RT5660_SCLK_SRC_MASK; + if (val == RT5660_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5660_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5660_STO1_ADC_MIXER, + RT5660_M_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5660_STO1_ADC_MIXER, + RT5660_M_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5660_STO1_ADC_MIXER, + RT5660_M_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5660_STO1_ADC_MIXER, + RT5660_M_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5660_AD_DA_MIXER, + RT5660_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5660_AD_DA_MIXER, + RT5660_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5660_AD_DA_MIXER, + RT5660_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5660_AD_DA_MIXER, + RT5660_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5660_STO_DAC_MIXER, + RT5660_M_DAC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5660_STO_DAC_MIXER, + RT5660_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5660_STO_DAC_MIXER, + RT5660_M_DAC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5660_STO_DAC_MIXER, + RT5660_M_DAC_L1_STO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5660_rec_l_mix[] = { + SOC_DAPM_SINGLE("BST3 Switch", RT5660_REC_L2_MIXER, + RT5660_M_BST3_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5660_REC_L2_MIXER, + RT5660_M_BST2_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_REC_L2_MIXER, + RT5660_M_BST1_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXL Switch", RT5660_REC_L2_MIXER, + RT5660_M_OM_L_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_rec_r_mix[] = { + SOC_DAPM_SINGLE("BST3 Switch", RT5660_REC_R2_MIXER, + RT5660_M_BST3_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5660_REC_R2_MIXER, + RT5660_M_BST2_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_REC_R2_MIXER, + RT5660_M_BST1_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXR Switch", RT5660_REC_R2_MIXER, + RT5660_M_OM_R_RM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_spk_mix[] = { + SOC_DAPM_SINGLE("BST3 Switch", RT5660_SPK_MIXER, + RT5660_M_BST3_SM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_SPK_MIXER, + RT5660_M_BST1_SM_SFT, 1, 1), + SOC_DAPM_SINGLE("DACL Switch", RT5660_SPK_MIXER, + RT5660_M_DACL_SM_SFT, 1, 1), + SOC_DAPM_SINGLE("DACR Switch", RT5660_SPK_MIXER, + RT5660_M_DACR_SM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIXL Switch", RT5660_SPK_MIXER, + RT5660_M_OM_L_SM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_out_l_mix[] = { + SOC_DAPM_SINGLE("BST3 Switch", RT5660_OUT_L1_MIXER, + RT5660_M_BST3_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5660_OUT_L1_MIXER, + RT5660_M_BST2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_OUT_L1_MIXER, + RT5660_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("RECMIXL Switch", RT5660_OUT_L1_MIXER, + RT5660_M_RM_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DACR Switch", RT5660_OUT_L1_MIXER, + RT5660_M_DAC_R_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DACL Switch", RT5660_OUT_L1_MIXER, + RT5660_M_DAC_L_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_out_r_mix[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5660_OUT_R1_MIXER, + RT5660_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_OUT_R1_MIXER, + RT5660_M_BST1_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("RECMIXR Switch", RT5660_OUT_R1_MIXER, + RT5660_M_RM_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DACR Switch", RT5660_OUT_R1_MIXER, + RT5660_M_DAC_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DACL Switch", RT5660_OUT_R1_MIXER, + RT5660_M_DAC_L_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_spo_mix[] = { + SOC_DAPM_SINGLE("DACR Switch", RT5660_SPO_MIXER, + RT5660_M_DAC_R_SPM_SFT, 1, 1), + SOC_DAPM_SINGLE("DACL Switch", RT5660_SPO_MIXER, + RT5660_M_DAC_L_SPM_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL Switch", RT5660_SPO_MIXER, + RT5660_M_SV_SPM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5660_SPO_MIXER, + RT5660_M_BST1_SPM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5660_lout_mix[] = { + SOC_DAPM_SINGLE("DAC Switch", RT5660_LOUT_MIXER, + RT5660_M_DAC1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIX Switch", RT5660_LOUT_MIXER, + RT5660_M_LOVOL_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new spk_vol_control = + SOC_DAPM_SINGLE("Switch", RT5660_SPK_VOL, + RT5660_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_l_vol_control = + SOC_DAPM_SINGLE("Switch", RT5660_LOUT_VOL, + RT5660_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_r_vol_control = + SOC_DAPM_SINGLE("Switch", RT5660_LOUT_VOL, + RT5660_VOL_R_SFT, 1, 1); + +/* Interface data select */ +static const char * const rt5660_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5660_if1_dac_enum, + RT5660_DIG_INF1_DATA, RT5660_IF1_DAC_IN_SFT, rt5660_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5660_if1_adc_enum, + RT5660_DIG_INF1_DATA, RT5660_IF1_ADC_IN_SFT, rt5660_data_select); + +static const struct snd_kcontrol_new rt5660_if1_dac_swap_mux = + SOC_DAPM_ENUM("IF1 DAC Swap Source", rt5660_if1_dac_enum); + +static const struct snd_kcontrol_new rt5660_if1_adc_swap_mux = + SOC_DAPM_ENUM("IF1 ADC Swap Source", rt5660_if1_adc_enum); + +static int rt5660_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5660_LOUT_AMP_CTRL, + RT5660_LOUT_CO_MASK | RT5660_LOUT_CB_MASK, + RT5660_LOUT_CO_EN | RT5660_LOUT_CB_PU); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5660_LOUT_AMP_CTRL, + RT5660_LOUT_CO_MASK | RT5660_LOUT_CB_MASK, + RT5660_LOUT_CO_DIS | RT5660_LOUT_CB_PD); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5660_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5660_PWR_ANLG1, + RT5660_PWR_LDO2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5660_PWR_ANLG2, + RT5660_PWR_PLL_BIT, 0, NULL, 0), + + /* MICBIAS */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5660_PWR_ANLG2, + RT5660_PWR_MB1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5660_PWR_ANLG2, + RT5660_PWR_MB2_BIT, 0, NULL, 0), + + /* Input Side */ + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("IN3N"), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + rt5660_set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC Power", RT5660_DMIC_CTRL1, + RT5660_DMIC_1_EN_SFT, 0, NULL, 0), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1", RT5660_PWR_ANLG2, RT5660_PWR_BST1_BIT, 0, + NULL, 0), + SND_SOC_DAPM_PGA("BST2", RT5660_PWR_ANLG2, RT5660_PWR_BST2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_PGA("BST3", RT5660_PWR_ANLG2, RT5660_PWR_BST3_BIT, 0, + NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5660_PWR_MIXER, RT5660_PWR_RM_L_BIT, + 0, rt5660_rec_l_mix, ARRAY_SIZE(rt5660_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5660_PWR_MIXER, RT5660_PWR_RM_R_BIT, + 0, rt5660_rec_r_mix, ARRAY_SIZE(rt5660_rec_r_mix)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC L power", RT5660_PWR_DIG1, + RT5660_PWR_ADC_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC R power", RT5660_PWR_DIG1, + RT5660_PWR_ADC_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC clock", RT5660_PR_BASE + RT5660_CHOP_DAC_ADC, + 12, 0, NULL, 0), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5660_PWR_DIG2, + RT5660_PWR_ADC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5660_sto1_adc_l_mix, ARRAY_SIZE(rt5660_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5660_sto1_adc_r_mix, ARRAY_SIZE(rt5660_sto1_adc_r_mix)), + + /* ADC */ + SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5660_STO1_ADC_DIG_VOL, + RT5660_L_MUTE_SFT, 1), + SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5660_STO1_ADC_DIG_VOL, + RT5660_R_MUTE_SFT, 1), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5660_PWR_DIG1, RT5660_PWR_I2S1_BIT, 0, + NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("IF1 DAC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5660_if1_dac_swap_mux), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("IF1 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5660_if1_adc_swap_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, rt5660_dac_l_mix, + ARRAY_SIZE(rt5660_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, rt5660_dac_r_mix, + ARRAY_SIZE(rt5660_dac_r_mix)), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5660_PWR_DIG2, + RT5660_PWR_DAC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5660_sto_dac_l_mix, ARRAY_SIZE(rt5660_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5660_sto_dac_r_mix, ARRAY_SIZE(rt5660_sto_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5660_PWR_DIG1, + RT5660_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5660_PWR_DIG1, + RT5660_PWR_DAC_R1_BIT, 0), + + /* OUT Mixer */ + SND_SOC_DAPM_MIXER("SPK MIX", RT5660_PWR_MIXER, RT5660_PWR_SM_BIT, + 0, rt5660_spk_mix, ARRAY_SIZE(rt5660_spk_mix)), + SND_SOC_DAPM_MIXER("OUT MIXL", RT5660_PWR_MIXER, RT5660_PWR_OM_L_BIT, + 0, rt5660_out_l_mix, ARRAY_SIZE(rt5660_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5660_PWR_MIXER, RT5660_PWR_OM_R_BIT, + 0, rt5660_out_r_mix, ARRAY_SIZE(rt5660_out_r_mix)), + + /* Output Volume */ + SND_SOC_DAPM_SWITCH("SPKVOL", RT5660_PWR_VOL, + RT5660_PWR_SV_BIT, 0, &spk_vol_control), + SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("LOUTVOL", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH("LOUTVOL L", SND_SOC_NOPM, + RT5660_PWR_LV_L_BIT, 0, &lout_l_vol_control), + SND_SOC_DAPM_SWITCH("LOUTVOL R", SND_SOC_NOPM, + RT5660_PWR_LV_R_BIT, 0, &lout_r_vol_control), + + /* HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("SPO MIX", SND_SOC_NOPM, 0, + 0, rt5660_spo_mix, ARRAY_SIZE(rt5660_spo_mix)), + SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, + rt5660_lout_mix, ARRAY_SIZE(rt5660_lout_mix)), + SND_SOC_DAPM_SUPPLY("VREF HP", RT5660_GEN_CTRL1, + RT5660_PWR_VREF_HP_SFT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("LOUT amp", 1, RT5660_PWR_ANLG1, + RT5660_PWR_HA_BIT, 0, rt5660_lout_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("SPK amp", 1, RT5660_PWR_DIG1, + RT5660_PWR_CLS_D_BIT, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("SPO"), +}; + +static const struct snd_soc_dapm_route rt5660_dapm_routes[] = { + { "MICBIAS1", NULL, "LDO2" }, + { "MICBIAS2", NULL, "LDO2" }, + + { "BST1", NULL, "IN1P" }, + { "BST1", NULL, "IN1N" }, + { "BST2", NULL, "IN2P" }, + { "BST3", NULL, "IN3P" }, + { "BST3", NULL, "IN3N" }, + + { "RECMIXL", "BST3 Switch", "BST3" }, + { "RECMIXL", "BST2 Switch", "BST2" }, + { "RECMIXL", "BST1 Switch", "BST1" }, + { "RECMIXL", "OUT MIXL Switch", "OUT MIXL" }, + + { "RECMIXR", "BST3 Switch", "BST3" }, + { "RECMIXR", "BST2 Switch", "BST2" }, + { "RECMIXR", "BST1 Switch", "BST1" }, + { "RECMIXR", "OUT MIXR Switch", "OUT MIXR" }, + + { "ADC L", NULL, "RECMIXL" }, + { "ADC L", NULL, "ADC L power" }, + { "ADC L", NULL, "ADC clock" }, + { "ADC R", NULL, "RECMIXR" }, + { "ADC R", NULL, "ADC R power" }, + { "ADC R", NULL, "ADC clock" }, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC Power"}, + + { "Sto1 ADC MIXL", "ADC1 Switch", "ADC L" }, + { "Sto1 ADC MIXL", "ADC2 Switch", "DMIC L1" }, + { "Sto1 ADC MIXR", "ADC1 Switch", "ADC R" }, + { "Sto1 ADC MIXR", "ADC2 Switch", "DMIC R1" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll }, + + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll }, + + { "IF1 ADC", NULL, "Stereo1 ADC MIXL" }, + { "IF1 ADC", NULL, "Stereo1 ADC MIXR" }, + { "IF1 ADC", NULL, "I2S1" }, + + { "IF1 ADC Swap Mux", "L/R", "IF1 ADC" }, + { "IF1 ADC Swap Mux", "R/L", "IF1 ADC" }, + { "IF1 ADC Swap Mux", "L/L", "IF1 ADC" }, + { "IF1 ADC Swap Mux", "R/R", "IF1 ADC" }, + { "AIF1TX", NULL, "IF1 ADC Swap Mux" }, + + { "IF1 DAC", NULL, "AIF1RX" }, + { "IF1 DAC", NULL, "I2S1" }, + + { "IF1 DAC Swap Mux", "L/R", "IF1 DAC" }, + { "IF1 DAC Swap Mux", "R/L", "IF1 DAC" }, + { "IF1 DAC Swap Mux", "L/L", "IF1 DAC" }, + { "IF1 DAC Swap Mux", "R/R", "IF1 DAC" }, + + { "IF1 DAC L", NULL, "IF1 DAC Swap Mux" }, + { "IF1 DAC R", NULL, "IF1 DAC Swap Mux" }, + + { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" }, + { "DAC1 MIXL", "DAC1 Switch", "IF1 DAC L" }, + { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" }, + { "DAC1 MIXR", "DAC1 Switch", "IF1 DAC R" }, + + { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXL", NULL, "dac stereo1 filter" }, + { "dac stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll }, + { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXR", NULL, "dac stereo1 filter" }, + { "dac stereo1 filter", NULL, "PLL1", rt5660_is_sys_clk_from_pll }, + + { "DAC L1", NULL, "Stereo DAC MIXL" }, + { "DAC R1", NULL, "Stereo DAC MIXR" }, + + { "SPK MIX", "BST3 Switch", "BST3" }, + { "SPK MIX", "BST1 Switch", "BST1" }, + { "SPK MIX", "DACL Switch", "DAC L1" }, + { "SPK MIX", "DACR Switch", "DAC R1" }, + { "SPK MIX", "OUTMIXL Switch", "OUT MIXL" }, + + { "OUT MIXL", "BST3 Switch", "BST3" }, + { "OUT MIXL", "BST2 Switch", "BST2" }, + { "OUT MIXL", "BST1 Switch", "BST1" }, + { "OUT MIXL", "RECMIXL Switch", "RECMIXL" }, + { "OUT MIXL", "DACR Switch", "DAC R1" }, + { "OUT MIXL", "DACL Switch", "DAC L1" }, + + { "OUT MIXR", "BST2 Switch", "BST2" }, + { "OUT MIXR", "BST1 Switch", "BST1" }, + { "OUT MIXR", "RECMIXR Switch", "RECMIXR" }, + { "OUT MIXR", "DACR Switch", "DAC R1" }, + { "OUT MIXR", "DACL Switch", "DAC L1" }, + + { "SPO MIX", "DACR Switch", "DAC R1" }, + { "SPO MIX", "DACL Switch", "DAC L1" }, + { "SPO MIX", "SPKVOL Switch", "SPKVOL" }, + { "SPO MIX", "BST1 Switch", "BST1" }, + + { "SPKVOL", "Switch", "SPK MIX" }, + { "LOUTVOL L", "Switch", "OUT MIXL" }, + { "LOUTVOL R", "Switch", "OUT MIXR" }, + + { "LOUTVOL", NULL, "LOUTVOL L" }, + { "LOUTVOL", NULL, "LOUTVOL R" }, + + { "DAC 1", NULL, "DAC L1" }, + { "DAC 1", NULL, "DAC R1" }, + + { "LOUT MIX", "DAC Switch", "DAC 1" }, + { "LOUT MIX", "OUTMIX Switch", "LOUTVOL" }, + + { "LOUT amp", NULL, "LOUT MIX" }, + { "LOUT amp", NULL, "VREF HP" }, + { "LOUTL", NULL, "LOUT amp" }, + { "LOUTR", NULL, "LOUT amp" }, + + { "SPK amp", NULL, "SPO MIX" }, + { "SPO", NULL, "SPK amp" }, +}; + +static int rt5660_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt5660->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5660->sysclk, rt5660->lrck[dai->id]); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n", + rt5660->lrck[dai->id], dai->id); + return -EINVAL; + } + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return frame_size; + } + + if (frame_size > 32) + bclk_ms = 1; + else + bclk_ms = 0; + + rt5660->bclk[dai->id] = rt5660->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5660->bclk[dai->id], rt5660->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= RT5660_I2S_DL_20; + break; + case 24: + val_len |= RT5660_I2S_DL_24; + break; + case 8: + val_len |= RT5660_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5660_AIF1: + mask_clk = RT5660_I2S_BCLK_MS1_MASK | RT5660_I2S_PD1_MASK; + val_clk = bclk_ms << RT5660_I2S_BCLK_MS1_SFT | + pre_div << RT5660_I2S_PD1_SFT; + snd_soc_component_update_bits(component, RT5660_I2S1_SDP, RT5660_I2S_DL_MASK, + val_len); + snd_soc_component_update_bits(component, RT5660_ADDA_CLK1, mask_clk, val_clk); + break; + + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5660_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5660->master[dai->id] = 1; + break; + + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5660_I2S_MS_S; + rt5660->master[dai->id] = 0; + break; + + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5660_I2S_BP_INV; + break; + + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5660_I2S_DF_LEFT; + break; + + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5660_I2S_DF_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5660_I2S_DF_PCM_B; + break; + + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5660_AIF1: + snd_soc_component_update_bits(component, RT5660_I2S1_SDP, + RT5660_I2S_MS_MASK | RT5660_I2S_BP_MASK | + RT5660_I2S_DF_MASK, reg_val); + break; + + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5660_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt5660->sysclk && clk_id == rt5660->sysclk_src) + return 0; + + switch (clk_id) { + case RT5660_SCLK_S_MCLK: + reg_val |= RT5660_SCLK_SRC_MCLK; + break; + + case RT5660_SCLK_S_PLL1: + reg_val |= RT5660_SCLK_SRC_PLL1; + break; + + case RT5660_SCLK_S_RCCLK: + reg_val |= RT5660_SCLK_SRC_RCCLK; + break; + + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5660_GLB_CLK, RT5660_SCLK_SRC_MASK, + reg_val); + + rt5660->sysclk = freq; + rt5660->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5660_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5660->pll_src && freq_in == rt5660->pll_in && + freq_out == rt5660->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5660->pll_in = 0; + rt5660->pll_out = 0; + snd_soc_component_update_bits(component, RT5660_GLB_CLK, + RT5660_SCLK_SRC_MASK, RT5660_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5660_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5660_GLB_CLK, + RT5660_PLL1_SRC_MASK, RT5660_PLL1_SRC_MCLK); + break; + + case RT5660_PLL1_S_BCLK: + snd_soc_component_update_bits(component, RT5660_GLB_CLK, + RT5660_PLL1_SRC_MASK, RT5660_PLL1_SRC_BCLK1); + break; + + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5660_PLL_CTRL1, + pll_code.n_code << RT5660_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5660_PLL_CTRL2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5660_PLL_M_SFT) | + (pll_code.m_bp << RT5660_PLL_M_BP_SFT)); + + rt5660->pll_in = freq_in; + rt5660->pll_out = freq_out; + rt5660->pll_src = source; + + return 0; +} + +static int rt5660_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, RT5660_GEN_CTRL1, + RT5660_DIG_GATE_CTRL, RT5660_DIG_GATE_CTRL); + + if (IS_ERR(rt5660->mclk)) + break; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) { + clk_disable_unprepare(rt5660->mclk); + } else { + ret = clk_prepare_enable(rt5660->mclk); + if (ret) + return ret; + } + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + snd_soc_component_update_bits(component, RT5660_PWR_ANLG1, + RT5660_PWR_VREF1 | RT5660_PWR_MB | + RT5660_PWR_BG | RT5660_PWR_VREF2, + RT5660_PWR_VREF1 | RT5660_PWR_MB | + RT5660_PWR_BG | RT5660_PWR_VREF2); + usleep_range(10000, 15000); + snd_soc_component_update_bits(component, RT5660_PWR_ANLG1, + RT5660_PWR_FV1 | RT5660_PWR_FV2, + RT5660_PWR_FV1 | RT5660_PWR_FV2); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, RT5660_GEN_CTRL1, + RT5660_DIG_GATE_CTRL, 0); + break; + + default: + break; + } + + return 0; +} + +static int rt5660_probe(struct snd_soc_component *component) +{ + struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component); + + rt5660->component = component; + + return 0; +} + +static void rt5660_remove(struct snd_soc_component *component) +{ + snd_soc_component_write(component, RT5660_RESET, 0); +} + +#ifdef CONFIG_PM +static int rt5660_suspend(struct snd_soc_component *component) +{ + struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5660->regmap, true); + regcache_mark_dirty(rt5660->regmap); + + return 0; +} + +static int rt5660_resume(struct snd_soc_component *component) +{ + struct rt5660_priv *rt5660 = snd_soc_component_get_drvdata(component); + + if (rt5660->pdata.poweroff_codec_in_suspend) + msleep(350); + + regcache_cache_only(rt5660->regmap, false); + regcache_sync(rt5660->regmap); + + return 0; +} +#else +#define rt5660_suspend NULL +#define rt5660_resume NULL +#endif + +#define RT5660_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5660_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5660_aif_dai_ops = { + .hw_params = rt5660_hw_params, + .set_fmt = rt5660_set_dai_fmt, + .set_sysclk = rt5660_set_dai_sysclk, + .set_pll = rt5660_set_dai_pll, +}; + +static struct snd_soc_dai_driver rt5660_dai[] = { + { + .name = "rt5660-aif1", + .id = RT5660_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5660_STEREO_RATES, + .formats = RT5660_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5660_STEREO_RATES, + .formats = RT5660_FORMATS, + }, + .ops = &rt5660_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5660 = { + .probe = rt5660_probe, + .remove = rt5660_remove, + .suspend = rt5660_suspend, + .resume = rt5660_resume, + .set_bias_level = rt5660_set_bias_level, + .controls = rt5660_snd_controls, + .num_controls = ARRAY_SIZE(rt5660_snd_controls), + .dapm_widgets = rt5660_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5660_dapm_widgets), + .dapm_routes = rt5660_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5660_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5660_regmap = { + .reg_bits = 8, + .val_bits = 16, + .use_single_read = true, + .use_single_write = true, + + .max_register = RT5660_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5660_ranges) * + RT5660_PR_SPACING), + .volatile_reg = rt5660_volatile_register, + .readable_reg = rt5660_readable_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5660_reg, + .num_reg_defaults = ARRAY_SIZE(rt5660_reg), + .ranges = rt5660_ranges, + .num_ranges = ARRAY_SIZE(rt5660_ranges), +}; + +static const struct i2c_device_id rt5660_i2c_id[] = { + { "rt5660", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5660_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id rt5660_of_match[] = { + { .compatible = "realtek,rt5660", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5660_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5660_acpi_match[] = { + { "10EC5660", 0 }, + { "10EC3277", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt5660_acpi_match); +#endif + +static int rt5660_parse_dt(struct rt5660_priv *rt5660, struct device *dev) +{ + rt5660->pdata.in1_diff = device_property_read_bool(dev, + "realtek,in1-differential"); + rt5660->pdata.in3_diff = device_property_read_bool(dev, + "realtek,in3-differential"); + rt5660->pdata.poweroff_codec_in_suspend = device_property_read_bool(dev, + "realtek,poweroff-in-suspend"); + device_property_read_u32(dev, "realtek,dmic1-data-pin", + &rt5660->pdata.dmic1_data_pin); + + return 0; +} + +static int rt5660_i2c_probe(struct i2c_client *i2c) +{ + struct rt5660_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5660_priv *rt5660; + int ret; + unsigned int val; + + rt5660 = devm_kzalloc(&i2c->dev, sizeof(struct rt5660_priv), + GFP_KERNEL); + + if (rt5660 == NULL) + return -ENOMEM; + + /* Check if MCLK provided */ + rt5660->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (PTR_ERR(rt5660->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + i2c_set_clientdata(i2c, rt5660); + + if (pdata) + rt5660->pdata = *pdata; + else if (i2c->dev.of_node) + rt5660_parse_dt(rt5660, &i2c->dev); + + rt5660->regmap = devm_regmap_init_i2c(i2c, &rt5660_regmap); + if (IS_ERR(rt5660->regmap)) { + ret = PTR_ERR(rt5660->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5660->regmap, RT5660_VENDOR_ID2, &val); + if (val != RT5660_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5660\n", val); + return -ENODEV; + } + + regmap_write(rt5660->regmap, RT5660_RESET, 0); + + ret = regmap_register_patch(rt5660->regmap, rt5660_patch, + ARRAY_SIZE(rt5660_patch)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + regmap_update_bits(rt5660->regmap, RT5660_GEN_CTRL1, + RT5660_AUTO_DIS_AMP | RT5660_MCLK_DET | RT5660_POW_CLKDET, + RT5660_AUTO_DIS_AMP | RT5660_MCLK_DET | RT5660_POW_CLKDET); + + if (rt5660->pdata.dmic1_data_pin) { + regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1, + RT5660_GP1_PIN_MASK, RT5660_GP1_PIN_DMIC1_SCL); + + if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_GPIO2) + regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1, + RT5660_SEL_DMIC_DATA_MASK, + RT5660_SEL_DMIC_DATA_GPIO2); + else if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_IN1P) + regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1, + RT5660_SEL_DMIC_DATA_MASK, + RT5660_SEL_DMIC_DATA_IN1P); + } + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5660, + rt5660_dai, ARRAY_SIZE(rt5660_dai)); +} + +static struct i2c_driver rt5660_i2c_driver = { + .driver = { + .name = "rt5660", + .acpi_match_table = ACPI_PTR(rt5660_acpi_match), + .of_match_table = of_match_ptr(rt5660_of_match), + }, + .probe = rt5660_i2c_probe, + .id_table = rt5660_i2c_id, +}; +module_i2c_driver(rt5660_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5660 driver"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5660.h b/sound/soc/codecs/rt5660.h new file mode 100644 index 0000000000..a33025c920 --- /dev/null +++ b/sound/soc/codecs/rt5660.h @@ -0,0 +1,847 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5660.h -- RT5660 ALSA SoC audio driver + * + * Copyright 2016 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#ifndef _RT5660_H +#define _RT5660_H + +#include +#include + +/* Info */ +#define RT5660_RESET 0x00 +#define RT5660_VENDOR_ID 0xfd +#define RT5660_VENDOR_ID1 0xfe +#define RT5660_VENDOR_ID2 0xff +/* I/O - Output */ +#define RT5660_SPK_VOL 0x01 +#define RT5660_LOUT_VOL 0x02 +/* I/O - Input */ +#define RT5660_IN1_IN2 0x0d +#define RT5660_IN3_IN4 0x0e +/* I/O - ADC/DAC/DMIC */ +#define RT5660_DAC1_DIG_VOL 0x19 +#define RT5660_STO1_ADC_DIG_VOL 0x1c +#define RT5660_ADC_BST_VOL1 0x1e +/* Mixer - D-D */ +#define RT5660_STO1_ADC_MIXER 0x27 +#define RT5660_AD_DA_MIXER 0x29 +#define RT5660_STO_DAC_MIXER 0x2a +#define RT5660_DIG_INF1_DATA 0x2f +/* Mixer - ADC */ +#define RT5660_REC_L1_MIXER 0x3b +#define RT5660_REC_L2_MIXER 0x3c +#define RT5660_REC_R1_MIXER 0x3d +#define RT5660_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5660_LOUT_MIXER 0x45 +#define RT5660_SPK_MIXER 0x46 +#define RT5660_SPO_MIXER 0x48 +#define RT5660_SPO_CLSD_RATIO 0x4a +#define RT5660_OUT_L_GAIN1 0x4d +#define RT5660_OUT_L_GAIN2 0x4e +#define RT5660_OUT_L1_MIXER 0x4f +#define RT5660_OUT_R_GAIN1 0x50 +#define RT5660_OUT_R_GAIN2 0x51 +#define RT5660_OUT_R1_MIXER 0x52 +/* Power */ +#define RT5660_PWR_DIG1 0x61 +#define RT5660_PWR_DIG2 0x62 +#define RT5660_PWR_ANLG1 0x63 +#define RT5660_PWR_ANLG2 0x64 +#define RT5660_PWR_MIXER 0x65 +#define RT5660_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5660_PRIV_INDEX 0x6a +#define RT5660_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5660_I2S1_SDP 0x70 +#define RT5660_ADDA_CLK1 0x73 +#define RT5660_ADDA_CLK2 0x74 +#define RT5660_DMIC_CTRL1 0x75 +/* Function - Analog */ +#define RT5660_GLB_CLK 0x80 +#define RT5660_PLL_CTRL1 0x81 +#define RT5660_PLL_CTRL2 0x82 +#define RT5660_CLSD_AMP_OC_CTRL 0x8c +#define RT5660_CLSD_AMP_CTRL 0x8d +#define RT5660_LOUT_AMP_CTRL 0x8e +#define RT5660_SPK_AMP_SPKVDD 0x92 +#define RT5660_MICBIAS 0x93 +#define RT5660_CLSD_OUT_CTRL1 0xa1 +#define RT5660_CLSD_OUT_CTRL2 0xa2 +#define RT5660_DIPOLE_MIC_CTRL1 0xa3 +#define RT5660_DIPOLE_MIC_CTRL2 0xa4 +#define RT5660_DIPOLE_MIC_CTRL3 0xa5 +#define RT5660_DIPOLE_MIC_CTRL4 0xa6 +#define RT5660_DIPOLE_MIC_CTRL5 0xa7 +#define RT5660_DIPOLE_MIC_CTRL6 0xa8 +#define RT5660_DIPOLE_MIC_CTRL7 0xa9 +#define RT5660_DIPOLE_MIC_CTRL8 0xaa +#define RT5660_DIPOLE_MIC_CTRL9 0xab +#define RT5660_DIPOLE_MIC_CTRL10 0xac +#define RT5660_DIPOLE_MIC_CTRL11 0xad +#define RT5660_DIPOLE_MIC_CTRL12 0xae +/* Function - Digital */ +#define RT5660_EQ_CTRL1 0xb0 +#define RT5660_EQ_CTRL2 0xb1 +#define RT5660_DRC_AGC_CTRL1 0xb3 +#define RT5660_DRC_AGC_CTRL2 0xb4 +#define RT5660_DRC_AGC_CTRL3 0xb5 +#define RT5660_DRC_AGC_CTRL4 0xb6 +#define RT5660_DRC_AGC_CTRL5 0xb7 +#define RT5660_JD_CTRL 0xbb +#define RT5660_IRQ_CTRL1 0xbd +#define RT5660_IRQ_CTRL2 0xbe +#define RT5660_INT_IRQ_ST 0xbf +#define RT5660_GPIO_CTRL1 0xc0 +#define RT5660_GPIO_CTRL2 0xc2 +#define RT5660_WIND_FILTER_CTRL1 0xd3 +#define RT5660_SV_ZCD1 0xd9 +#define RT5660_SV_ZCD2 0xda +#define RT5660_DRC1_LM_CTRL1 0xe0 +#define RT5660_DRC1_LM_CTRL2 0xe1 +#define RT5660_DRC2_LM_CTRL1 0xe2 +#define RT5660_DRC2_LM_CTRL2 0xe3 +#define RT5660_MULTI_DRC_CTRL 0xe4 +#define RT5660_DRC2_CTRL1 0xe5 +#define RT5660_DRC2_CTRL2 0xe6 +#define RT5660_DRC2_CTRL3 0xe7 +#define RT5660_DRC2_CTRL4 0xe8 +#define RT5660_DRC2_CTRL5 0xe9 +#define RT5660_ALC_PGA_CTRL1 0xea +#define RT5660_ALC_PGA_CTRL2 0xeb +#define RT5660_ALC_PGA_CTRL3 0xec +#define RT5660_ALC_PGA_CTRL4 0xed +#define RT5660_ALC_PGA_CTRL5 0xee +#define RT5660_ALC_PGA_CTRL6 0xef +#define RT5660_ALC_PGA_CTRL7 0xf0 + +/* General Control */ +#define RT5660_GEN_CTRL1 0xfa +#define RT5660_GEN_CTRL2 0xfb +#define RT5660_GEN_CTRL3 0xfc + +/* Index of Codec Private Register definition */ +#define RT5660_CHOP_DAC_ADC 0x3d + +/* Global Definition */ +#define RT5660_L_MUTE (0x1 << 15) +#define RT5660_L_MUTE_SFT 15 +#define RT5660_VOL_L_MUTE (0x1 << 14) +#define RT5660_VOL_L_SFT 14 +#define RT5660_R_MUTE (0x1 << 7) +#define RT5660_R_MUTE_SFT 7 +#define RT5660_VOL_R_MUTE (0x1 << 6) +#define RT5660_VOL_R_SFT 6 +#define RT5660_L_VOL_MASK (0x3f << 8) +#define RT5660_L_VOL_SFT 8 +#define RT5660_R_VOL_MASK (0x3f) +#define RT5660_R_VOL_SFT 0 + +/* IN1 and IN2 Control (0x0d) */ +#define RT5660_IN_DF1 (0x1 << 15) +#define RT5660_IN_SFT1 15 +#define RT5660_BST_MASK1 (0x7f << 8) +#define RT5660_BST_SFT1 8 +#define RT5660_IN_DF2 (0x1 << 7) +#define RT5660_IN_SFT2 7 +#define RT5660_BST_MASK2 (0x7f << 0) +#define RT5660_BST_SFT2 0 + +/* IN3 and IN4 Control (0x0e) */ +#define RT5660_IN_DF3 (0x1 << 15) +#define RT5660_IN_SFT3 15 +#define RT5660_BST_MASK3 (0x7f << 8) +#define RT5660_BST_SFT3 8 +#define RT5660_IN_DF4 (0x1 << 7) +#define RT5660_IN_SFT4 7 +#define RT5660_BST_MASK4 (0x7f << 0) +#define RT5660_BST_SFT4 0 + +/* DAC1 Digital Volume (0x19) */ +#define RT5660_DAC_L1_VOL_MASK (0x7f << 9) +#define RT5660_DAC_L1_VOL_SFT 9 +#define RT5660_DAC_R1_VOL_MASK (0x7f << 1) +#define RT5660_DAC_R1_VOL_SFT 1 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5660_ADC_L_VOL_MASK (0x3f << 9) +#define RT5660_ADC_L_VOL_SFT 9 +#define RT5660_ADC_R_VOL_MASK (0x3f << 1) +#define RT5660_ADC_R_VOL_SFT 1 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5660_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5660_STO1_ADC_L_BST_SFT 14 +#define RT5660_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5660_STO1_ADC_R_BST_SFT 12 + +/* Stereo ADC Mixer Control (0x27) */ +#define RT5660_M_ADC_L1 (0x1 << 14) +#define RT5660_M_ADC_L1_SFT 14 +#define RT5660_M_ADC_L2 (0x1 << 13) +#define RT5660_M_ADC_L2_SFT 13 +#define RT5660_M_ADC_R1 (0x1 << 6) +#define RT5660_M_ADC_R1_SFT 6 +#define RT5660_M_ADC_R2 (0x1 << 5) +#define RT5660_M_ADC_R2_SFT 5 + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5660_M_ADCMIX_L (0x1 << 15) +#define RT5660_M_ADCMIX_L_SFT 15 +#define RT5660_M_DAC1_L (0x1 << 14) +#define RT5660_M_DAC1_L_SFT 14 +#define RT5660_M_ADCMIX_R (0x1 << 7) +#define RT5660_M_ADCMIX_R_SFT 7 +#define RT5660_M_DAC1_R (0x1 << 6) +#define RT5660_M_DAC1_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5660_M_DAC_L1 (0x1 << 14) +#define RT5660_M_DAC_L1_SFT 14 +#define RT5660_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5660_DAC_L1_STO_L_VOL_SFT 13 +#define RT5660_M_DAC_R1_STO_L (0x1 << 9) +#define RT5660_M_DAC_R1_STO_L_SFT 9 +#define RT5660_DAC_R1_STO_L_VOL_MASK (0x1 << 8) +#define RT5660_DAC_R1_STO_L_VOL_SFT 8 +#define RT5660_M_DAC_R1 (0x1 << 6) +#define RT5660_M_DAC_R1_SFT 6 +#define RT5660_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5660_DAC_R1_STO_R_VOL_SFT 5 +#define RT5660_M_DAC_L1_STO_R (0x1 << 1) +#define RT5660_M_DAC_L1_STO_R_SFT 1 +#define RT5660_DAC_L1_STO_R_VOL_MASK (0x1) +#define RT5660_DAC_L1_STO_R_VOL_SFT 0 + +/* Digital Interface Data Control (0x2f) */ +#define RT5660_IF1_DAC_IN_SEL (0x3 << 14) +#define RT5660_IF1_DAC_IN_SFT 14 +#define RT5660_IF1_ADC_IN_SEL (0x3 << 12) +#define RT5660_IF1_ADC_IN_SFT 12 + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5660_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5660_G_BST3_RM_L_SFT 4 +#define RT5660_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5660_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5660_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5660_G_BST1_RM_L_SFT 13 +#define RT5660_G_OM_L_RM_L_MASK (0x7 << 10) +#define RT5660_G_OM_L_RM_L_SFT 10 +#define RT5660_M_BST3_RM_L (0x1 << 3) +#define RT5660_M_BST3_RM_L_SFT 3 +#define RT5660_M_BST2_RM_L (0x1 << 2) +#define RT5660_M_BST2_RM_L_SFT 2 +#define RT5660_M_BST1_RM_L (0x1 << 1) +#define RT5660_M_BST1_RM_L_SFT 1 +#define RT5660_M_OM_L_RM_L (0x1) +#define RT5660_M_OM_L_RM_L_SFT 0 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5660_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5660_G_BST3_RM_R_SFT 4 +#define RT5660_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5660_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5660_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5660_G_BST1_RM_R_SFT 13 +#define RT5660_G_OM_R_RM_R_MASK (0x7 << 10) +#define RT5660_G_OM_R_RM_R_SFT 10 +#define RT5660_M_BST3_RM_R (0x1 << 3) +#define RT5660_M_BST3_RM_R_SFT 3 +#define RT5660_M_BST2_RM_R (0x1 << 2) +#define RT5660_M_BST2_RM_R_SFT 2 +#define RT5660_M_BST1_RM_R (0x1 << 1) +#define RT5660_M_BST1_RM_R_SFT 1 +#define RT5660_M_OM_R_RM_R (0x1) +#define RT5660_M_OM_R_RM_R_SFT 0 + +/* LOUTMIX Control (0x45) */ +#define RT5660_M_DAC1_LM (0x1 << 14) +#define RT5660_M_DAC1_LM_SFT 14 +#define RT5660_M_LOVOL_M (0x1 << 13) +#define RT5660_M_LOVOL_LM_SFT 13 + +/* SPK Mixer Control (0x46) */ +#define RT5660_G_BST3_SM_MASK (0x3 << 14) +#define RT5660_G_BST3_SM_SFT 14 +#define RT5660_G_BST1_SM_MASK (0x3 << 12) +#define RT5660_G_BST1_SM_SFT 12 +#define RT5660_G_DACl_SM_MASK (0x3 << 10) +#define RT5660_G_DACl_SM_SFT 10 +#define RT5660_G_DACR_SM_MASK (0x3 << 8) +#define RT5660_G_DACR_SM_SFT 8 +#define RT5660_G_OM_L_SM_MASK (0x3 << 6) +#define RT5660_G_OM_L_SM_SFT 6 +#define RT5660_M_DACR_SM (0x1 << 5) +#define RT5660_M_DACR_SM_SFT 5 +#define RT5660_M_BST1_SM (0x1 << 4) +#define RT5660_M_BST1_SM_SFT 4 +#define RT5660_M_BST3_SM (0x1 << 3) +#define RT5660_M_BST3_SM_SFT 3 +#define RT5660_M_DACL_SM (0x1 << 2) +#define RT5660_M_DACL_SM_SFT 2 +#define RT5660_M_OM_L_SM (0x1 << 1) +#define RT5660_M_OM_L_SM_SFT 1 + +/* SPOMIX Control (0x48) */ +#define RT5660_M_DAC_R_SPM (0x1 << 14) +#define RT5660_M_DAC_R_SPM_SFT 14 +#define RT5660_M_DAC_L_SPM (0x1 << 13) +#define RT5660_M_DAC_L_SPM_SFT 13 +#define RT5660_M_SV_SPM (0x1 << 12) +#define RT5660_M_SV_SPM_SFT 12 +#define RT5660_M_BST1_SPM (0x1 << 11) +#define RT5660_M_BST1_SPM_SFT 11 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5660_G_BST3_OM_L_MASK (0x7 << 13) +#define RT5660_G_BST3_OM_L_SFT 13 +#define RT5660_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5660_G_BST2_OM_L_SFT 10 +#define RT5660_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5660_G_BST1_OM_L_SFT 7 +#define RT5660_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5660_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5660_G_DAC_R1_OM_L_MASK (0x7 << 10) +#define RT5660_G_DAC_R1_OM_L_SFT 10 +#define RT5660_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5660_G_DAC_L1_OM_L_SFT 7 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5660_M_BST3_OM_L (0x1 << 5) +#define RT5660_M_BST3_OM_L_SFT 5 +#define RT5660_M_BST2_OM_L (0x1 << 4) +#define RT5660_M_BST2_OM_L_SFT 4 +#define RT5660_M_BST1_OM_L (0x1 << 3) +#define RT5660_M_BST1_OM_L_SFT 3 +#define RT5660_M_RM_L_OM_L (0x1 << 2) +#define RT5660_M_RM_L_OM_L_SFT 2 +#define RT5660_M_DAC_R_OM_L (0x1 << 1) +#define RT5660_M_DAC_R_OM_L_SFT 1 +#define RT5660_M_DAC_L_OM_L (0x1) +#define RT5660_M_DAC_L_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5660_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5660_G_BST2_OM_R_SFT 10 +#define RT5660_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5660_G_BST1_OM_R_SFT 7 +#define RT5660_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5660_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5660_G_DAC_L_OM_R_MASK (0x7 << 10) +#define RT5660_G_DAC_L_OM_R_SFT 10 +#define RT5660_G_DAC_R_OM_R_MASK (0x7 << 7) +#define RT5660_G_DAC_R_OM_R_SFT 7 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5660_M_BST2_OM_R (0x1 << 4) +#define RT5660_M_BST2_OM_R_SFT 4 +#define RT5660_M_BST1_OM_R (0x1 << 3) +#define RT5660_M_BST1_OM_R_SFT 3 +#define RT5660_M_RM_R_OM_R (0x1 << 2) +#define RT5660_M_RM_R_OM_R_SFT 2 +#define RT5660_M_DAC_L_OM_R (0x1 << 1) +#define RT5660_M_DAC_L_OM_R_SFT 1 +#define RT5660_M_DAC_R_OM_R (0x1) +#define RT5660_M_DAC_R_OM_R_SFT 0 + +/* Power Management for Digital 1 (0x61) */ +#define RT5660_PWR_I2S1 (0x1 << 15) +#define RT5660_PWR_I2S1_BIT 15 +#define RT5660_PWR_DAC_L1 (0x1 << 12) +#define RT5660_PWR_DAC_L1_BIT 12 +#define RT5660_PWR_DAC_R1 (0x1 << 11) +#define RT5660_PWR_DAC_R1_BIT 11 +#define RT5660_PWR_ADC_L (0x1 << 2) +#define RT5660_PWR_ADC_L_BIT 2 +#define RT5660_PWR_ADC_R (0x1 << 1) +#define RT5660_PWR_ADC_R_BIT 1 +#define RT5660_PWR_CLS_D (0x1) +#define RT5660_PWR_CLS_D_BIT 0 + +/* Power Management for Digital 2 (0x62) */ +#define RT5660_PWR_ADC_S1F (0x1 << 15) +#define RT5660_PWR_ADC_S1F_BIT 15 +#define RT5660_PWR_DAC_S1F (0x1 << 11) +#define RT5660_PWR_DAC_S1F_BIT 11 + +/* Power Management for Analog 1 (0x63) */ +#define RT5660_PWR_VREF1 (0x1 << 15) +#define RT5660_PWR_VREF1_BIT 15 +#define RT5660_PWR_FV1 (0x1 << 14) +#define RT5660_PWR_FV1_BIT 14 +#define RT5660_PWR_MB (0x1 << 13) +#define RT5660_PWR_MB_BIT 13 +#define RT5660_PWR_BG (0x1 << 11) +#define RT5660_PWR_BG_BIT 11 +#define RT5660_PWR_HP_L (0x1 << 7) +#define RT5660_PWR_HP_L_BIT 7 +#define RT5660_PWR_HP_R (0x1 << 6) +#define RT5660_PWR_HP_R_BIT 6 +#define RT5660_PWR_HA (0x1 << 5) +#define RT5660_PWR_HA_BIT 5 +#define RT5660_PWR_VREF2 (0x1 << 4) +#define RT5660_PWR_VREF2_BIT 4 +#define RT5660_PWR_FV2 (0x1 << 3) +#define RT5660_PWR_FV2_BIT 3 +#define RT5660_PWR_LDO2 (0x1 << 2) +#define RT5660_PWR_LDO2_BIT 2 + +/* Power Management for Analog 2 (0x64) */ +#define RT5660_PWR_BST1 (0x1 << 15) +#define RT5660_PWR_BST1_BIT 15 +#define RT5660_PWR_BST2 (0x1 << 14) +#define RT5660_PWR_BST2_BIT 14 +#define RT5660_PWR_BST3 (0x1 << 13) +#define RT5660_PWR_BST3_BIT 13 +#define RT5660_PWR_MB1 (0x1 << 11) +#define RT5660_PWR_MB1_BIT 11 +#define RT5660_PWR_MB2 (0x1 << 10) +#define RT5660_PWR_MB2_BIT 10 +#define RT5660_PWR_PLL (0x1 << 9) +#define RT5660_PWR_PLL_BIT 9 + +/* Power Management for Mixer (0x65) */ +#define RT5660_PWR_OM_L (0x1 << 15) +#define RT5660_PWR_OM_L_BIT 15 +#define RT5660_PWR_OM_R (0x1 << 14) +#define RT5660_PWR_OM_R_BIT 14 +#define RT5660_PWR_SM (0x1 << 13) +#define RT5660_PWR_SM_BIT 13 +#define RT5660_PWR_RM_L (0x1 << 11) +#define RT5660_PWR_RM_L_BIT 11 +#define RT5660_PWR_RM_R (0x1 << 10) +#define RT5660_PWR_RM_R_BIT 10 + +/* Power Management for Volume (0x66) */ +#define RT5660_PWR_SV (0x1 << 15) +#define RT5660_PWR_SV_BIT 15 +#define RT5660_PWR_LV_L (0x1 << 11) +#define RT5660_PWR_LV_L_BIT 11 +#define RT5660_PWR_LV_R (0x1 << 10) +#define RT5660_PWR_LV_R_BIT 10 + +/* I2S1 Audio Serial Data Port Control (0x70) */ +#define RT5660_I2S_MS_MASK (0x1 << 15) +#define RT5660_I2S_MS_SFT 15 +#define RT5660_I2S_MS_M (0x0 << 15) +#define RT5660_I2S_MS_S (0x1 << 15) +#define RT5660_I2S_O_CP_MASK (0x3 << 10) +#define RT5660_I2S_O_CP_SFT 10 +#define RT5660_I2S_O_CP_OFF (0x0 << 10) +#define RT5660_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5660_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5660_I2S_I_CP_MASK (0x3 << 8) +#define RT5660_I2S_I_CP_SFT 8 +#define RT5660_I2S_I_CP_OFF (0x0 << 8) +#define RT5660_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5660_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5660_I2S_BP_MASK (0x1 << 7) +#define RT5660_I2S_BP_SFT 7 +#define RT5660_I2S_BP_NOR (0x0 << 7) +#define RT5660_I2S_BP_INV (0x1 << 7) +#define RT5660_I2S_DL_MASK (0x3 << 2) +#define RT5660_I2S_DL_SFT 2 +#define RT5660_I2S_DL_16 (0x0 << 2) +#define RT5660_I2S_DL_20 (0x1 << 2) +#define RT5660_I2S_DL_24 (0x2 << 2) +#define RT5660_I2S_DL_8 (0x3 << 2) +#define RT5660_I2S_DF_MASK (0x3) +#define RT5660_I2S_DF_SFT 0 +#define RT5660_I2S_DF_I2S (0x0) +#define RT5660_I2S_DF_LEFT (0x1) +#define RT5660_I2S_DF_PCM_A (0x2) +#define RT5660_I2S_DF_PCM_B (0x3) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5660_I2S_BCLK_MS1_MASK (0x1 << 15) +#define RT5660_I2S_BCLK_MS1_SFT 15 +#define RT5660_I2S_BCLK_MS1_32 (0x0 << 15) +#define RT5660_I2S_BCLK_MS1_64 (0x1 << 15) +#define RT5660_I2S_PD1_MASK (0x7 << 12) +#define RT5660_I2S_PD1_SFT 12 +#define RT5660_I2S_PD1_1 (0x0 << 12) +#define RT5660_I2S_PD1_2 (0x1 << 12) +#define RT5660_I2S_PD1_3 (0x2 << 12) +#define RT5660_I2S_PD1_4 (0x3 << 12) +#define RT5660_I2S_PD1_6 (0x4 << 12) +#define RT5660_I2S_PD1_8 (0x5 << 12) +#define RT5660_I2S_PD1_12 (0x6 << 12) +#define RT5660_I2S_PD1_16 (0x7 << 12) +#define RT5660_DAC_OSR_MASK (0x3 << 2) +#define RT5660_DAC_OSR_SFT 2 +#define RT5660_DAC_OSR_128 (0x0 << 2) +#define RT5660_DAC_OSR_64 (0x1 << 2) +#define RT5660_DAC_OSR_32 (0x2 << 2) +#define RT5660_DAC_OSR_16 (0x3 << 2) +#define RT5660_ADC_OSR_MASK (0x3) +#define RT5660_ADC_OSR_SFT 0 +#define RT5660_ADC_OSR_128 (0x0) +#define RT5660_ADC_OSR_64 (0x1) +#define RT5660_ADC_OSR_32 (0x2) +#define RT5660_ADC_OSR_16 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5660_RESET_ADF (0x1 << 13) +#define RT5660_RESET_ADF_SFT 13 +#define RT5660_RESET_DAF (0x1 << 12) +#define RT5660_RESET_DAF_SFT 12 +#define RT5660_DAHPF_EN (0x1 << 11) +#define RT5660_DAHPF_EN_SFT 11 +#define RT5660_ADHPF_EN (0x1 << 10) +#define RT5660_ADHPF_EN_SFT 10 + +/* Digital Microphone Control (0x75) */ +#define RT5660_DMIC_1_EN_MASK (0x1 << 15) +#define RT5660_DMIC_1_EN_SFT 15 +#define RT5660_DMIC_1_DIS (0x0 << 15) +#define RT5660_DMIC_1_EN (0x1 << 15) +#define RT5660_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5660_DMIC_1L_LH_SFT 13 +#define RT5660_DMIC_1L_LH_RISING (0x0 << 13) +#define RT5660_DMIC_1L_LH_FALLING (0x1 << 13) +#define RT5660_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5660_DMIC_1R_LH_SFT 12 +#define RT5660_DMIC_1R_LH_RISING (0x0 << 12) +#define RT5660_DMIC_1R_LH_FALLING (0x1 << 12) +#define RT5660_SEL_DMIC_DATA_MASK (0x1 << 11) +#define RT5660_SEL_DMIC_DATA_SFT 11 +#define RT5660_SEL_DMIC_DATA_GPIO2 (0x0 << 11) +#define RT5660_SEL_DMIC_DATA_IN1P (0x1 << 11) +#define RT5660_DMIC_CLK_MASK (0x7 << 5) +#define RT5660_DMIC_CLK_SFT 5 + +/* Global Clock Control (0x80) */ +#define RT5660_SCLK_SRC_MASK (0x3 << 14) +#define RT5660_SCLK_SRC_SFT 14 +#define RT5660_SCLK_SRC_MCLK (0x0 << 14) +#define RT5660_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5660_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5660_PLL1_SRC_MASK (0x3 << 12) +#define RT5660_PLL1_SRC_SFT 12 +#define RT5660_PLL1_SRC_MCLK (0x0 << 12) +#define RT5660_PLL1_SRC_BCLK1 (0x1 << 12) +#define RT5660_PLL1_SRC_RCCLK (0x2 << 12) +#define RT5660_PLL1_PD_MASK (0x1 << 3) +#define RT5660_PLL1_PD_SFT 3 +#define RT5660_PLL1_PD_1 (0x0 << 3) +#define RT5660_PLL1_PD_2 (0x1 << 3) + +#define RT5660_PLL_INP_MAX 40000000 +#define RT5660_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5660_PLL_N_MAX 0x1ff +#define RT5660_PLL_N_MASK (RT5660_PLL_N_MAX << 7) +#define RT5660_PLL_N_SFT 7 +#define RT5660_PLL_K_MAX 0x1f +#define RT5660_PLL_K_MASK (RT5660_PLL_K_MAX) +#define RT5660_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5660_PLL_M_MAX 0xf +#define RT5660_PLL_M_MASK (RT5660_PLL_M_MAX << 12) +#define RT5660_PLL_M_SFT 12 +#define RT5660_PLL_M_BP (0x1 << 11) +#define RT5660_PLL_M_BP_SFT 11 + +/* Class D Over Current Control (0x8c) */ +#define RT5660_CLSD_OC_MASK (0x1 << 9) +#define RT5660_CLSD_OC_SFT 9 +#define RT5660_CLSD_OC_PU (0x0 << 9) +#define RT5660_CLSD_OC_PD (0x1 << 9) +#define RT5660_AUTO_PD_MASK (0x1 << 8) +#define RT5660_AUTO_PD_SFT 8 +#define RT5660_AUTO_PD_DIS (0x0 << 8) +#define RT5660_AUTO_PD_EN (0x1 << 8) +#define RT5660_CLSD_OC_TH_MASK (0x3f) +#define RT5660_CLSD_OC_TH_SFT 0 + +/* Class D Output Control (0x8d) */ +#define RT5660_CLSD_RATIO_MASK (0xf << 12) +#define RT5660_CLSD_RATIO_SFT 12 + +/* Lout Amp Control 1 (0x8e) */ +#define RT5660_LOUT_CO_MASK (0x1 << 4) +#define RT5660_LOUT_CO_SFT 4 +#define RT5660_LOUT_CO_DIS (0x0 << 4) +#define RT5660_LOUT_CO_EN (0x1 << 4) +#define RT5660_LOUT_CB_MASK (0x1) +#define RT5660_LOUT_CB_SFT 0 +#define RT5660_LOUT_CB_PD (0x0) +#define RT5660_LOUT_CB_PU (0x1) + +/* SPKVDD detection control (0x92) */ +#define RT5660_SPKVDD_DET_MASK (0x1 << 15) +#define RT5660_SPKVDD_DET_SFT 15 +#define RT5660_SPKVDD_DET_DIS (0x0 << 15) +#define RT5660_SPKVDD_DET_EN (0x1 << 15) +#define RT5660_SPK_AG_MASK (0x1 << 14) +#define RT5660_SPK_AG_SFT 14 +#define RT5660_SPK_AG_DIS (0x0 << 14) +#define RT5660_SPK_AG_EN (0x1 << 14) + +/* Micbias Control (0x93) */ +#define RT5660_MIC1_BS_MASK (0x1 << 15) +#define RT5660_MIC1_BS_SFT 15 +#define RT5660_MIC1_BS_9AV (0x0 << 15) +#define RT5660_MIC1_BS_75AV (0x1 << 15) +#define RT5660_MIC2_BS_MASK (0x1 << 14) +#define RT5660_MIC2_BS_SFT 14 +#define RT5660_MIC2_BS_9AV (0x0 << 14) +#define RT5660_MIC2_BS_75AV (0x1 << 14) +#define RT5660_MIC1_OVCD_MASK (0x1 << 11) +#define RT5660_MIC1_OVCD_SFT 11 +#define RT5660_MIC1_OVCD_DIS (0x0 << 11) +#define RT5660_MIC1_OVCD_EN (0x1 << 11) +#define RT5660_MIC1_OVTH_MASK (0x3 << 9) +#define RT5660_MIC1_OVTH_SFT 9 +#define RT5660_MIC1_OVTH_600UA (0x0 << 9) +#define RT5660_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5660_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5660_MIC2_OVCD_MASK (0x1 << 8) +#define RT5660_MIC2_OVCD_SFT 8 +#define RT5660_MIC2_OVCD_DIS (0x0 << 8) +#define RT5660_MIC2_OVCD_EN (0x1 << 8) +#define RT5660_MIC2_OVTH_MASK (0x3 << 6) +#define RT5660_MIC2_OVTH_SFT 6 +#define RT5660_MIC2_OVTH_600UA (0x0 << 6) +#define RT5660_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5660_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5660_PWR_CLK25M_MASK (0x1 << 4) +#define RT5660_PWR_CLK25M_SFT 4 +#define RT5660_PWR_CLK25M_PD (0x0 << 4) +#define RT5660_PWR_CLK25M_PU (0x1 << 4) + +/* EQ Control 1 (0xb0) */ +#define RT5660_EQ_SRC_MASK (0x1 << 15) +#define RT5660_EQ_SRC_SFT 15 +#define RT5660_EQ_SRC_DAC (0x0 << 15) +#define RT5660_EQ_SRC_ADC (0x1 << 15) +#define RT5660_EQ_UPD (0x1 << 14) +#define RT5660_EQ_UPD_BIT 14 + +/* Jack Detect Control (0xbb) */ +#define RT5660_JD_MASK (0x3 << 14) +#define RT5660_JD_SFT 14 +#define RT5660_JD_DIS (0x0 << 14) +#define RT5660_JD_GPIO1 (0x1 << 14) +#define RT5660_JD_GPIO2 (0x2 << 14) +#define RT5660_JD_LOUT_MASK (0x1 << 11) +#define RT5660_JD_LOUT_SFT 11 +#define RT5660_JD_LOUT_DIS (0x0 << 11) +#define RT5660_JD_LOUT_EN (0x1 << 11) +#define RT5660_JD_LOUT_TRG_MASK (0x1 << 10) +#define RT5660_JD_LOUT_TRG_SFT 10 +#define RT5660_JD_LOUT_TRG_LO (0x0 << 10) +#define RT5660_JD_LOUT_TRG_HI (0x1 << 10) +#define RT5660_JD_SPO_MASK (0x1 << 9) +#define RT5660_JD_SPO_SFT 9 +#define RT5660_JD_SPO_DIS (0x0 << 9) +#define RT5660_JD_SPO_EN (0x1 << 9) +#define RT5660_JD_SPO_TRG_MASK (0x1 << 8) +#define RT5660_JD_SPO_TRG_SFT 8 +#define RT5660_JD_SPO_TRG_LO (0x0 << 8) +#define RT5660_JD_SPO_TRG_HI (0x1 << 8) + +/* IRQ Control 1 (0xbd) */ +#define RT5660_IRQ_JD_MASK (0x1 << 15) +#define RT5660_IRQ_JD_SFT 15 +#define RT5660_IRQ_JD_BP (0x0 << 15) +#define RT5660_IRQ_JD_NOR (0x1 << 15) +#define RT5660_IRQ_OT_MASK (0x1 << 14) +#define RT5660_IRQ_OT_SFT 14 +#define RT5660_IRQ_OT_BP (0x0 << 14) +#define RT5660_IRQ_OT_NOR (0x1 << 14) +#define RT5660_JD_STKY_MASK (0x1 << 13) +#define RT5660_JD_STKY_SFT 13 +#define RT5660_JD_STKY_DIS (0x0 << 13) +#define RT5660_JD_STKY_EN (0x1 << 13) +#define RT5660_OT_STKY_MASK (0x1 << 12) +#define RT5660_OT_STKY_SFT 12 +#define RT5660_OT_STKY_DIS (0x0 << 12) +#define RT5660_OT_STKY_EN (0x1 << 12) +#define RT5660_JD_P_MASK (0x1 << 11) +#define RT5660_JD_P_SFT 11 +#define RT5660_JD_P_NOR (0x0 << 11) +#define RT5660_JD_P_INV (0x1 << 11) +#define RT5660_OT_P_MASK (0x1 << 10) +#define RT5660_OT_P_SFT 10 +#define RT5660_OT_P_NOR (0x0 << 10) +#define RT5660_OT_P_INV (0x1 << 10) + +/* IRQ Control 2 (0xbe) */ +#define RT5660_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5660_IRQ_MB1_OC_SFT 15 +#define RT5660_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5660_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5660_IRQ_MB2_OC_MASK (0x1 << 14) +#define RT5660_IRQ_MB2_OC_SFT 14 +#define RT5660_IRQ_MB2_OC_BP (0x0 << 14) +#define RT5660_IRQ_MB2_OC_NOR (0x1 << 14) +#define RT5660_MB1_OC_STKY_MASK (0x1 << 11) +#define RT5660_MB1_OC_STKY_SFT 11 +#define RT5660_MB1_OC_STKY_DIS (0x0 << 11) +#define RT5660_MB1_OC_STKY_EN (0x1 << 11) +#define RT5660_MB2_OC_STKY_MASK (0x1 << 10) +#define RT5660_MB2_OC_STKY_SFT 10 +#define RT5660_MB2_OC_STKY_DIS (0x0 << 10) +#define RT5660_MB2_OC_STKY_EN (0x1 << 10) +#define RT5660_MB1_OC_P_MASK (0x1 << 7) +#define RT5660_MB1_OC_P_SFT 7 +#define RT5660_MB1_OC_P_NOR (0x0 << 7) +#define RT5660_MB1_OC_P_INV (0x1 << 7) +#define RT5660_MB2_OC_P_MASK (0x1 << 6) +#define RT5660_MB2_OC_P_SFT 6 +#define RT5660_MB2_OC_P_NOR (0x0 << 6) +#define RT5660_MB2_OC_P_INV (0x1 << 6) +#define RT5660_MB1_OC_CLR (0x1 << 3) +#define RT5660_MB1_OC_CLR_SFT 3 +#define RT5660_MB2_OC_CLR (0x1 << 2) +#define RT5660_MB2_OC_CLR_SFT 2 + +/* GPIO Control 1 (0xc0) */ +#define RT5660_GP2_PIN_MASK (0x1 << 14) +#define RT5660_GP2_PIN_SFT 14 +#define RT5660_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5660_GP2_PIN_DMIC1_SDA (0x1 << 14) +#define RT5660_GP1_PIN_MASK (0x3 << 12) +#define RT5660_GP1_PIN_SFT 12 +#define RT5660_GP1_PIN_GPIO1 (0x0 << 12) +#define RT5660_GP1_PIN_DMIC1_SCL (0x1 << 12) +#define RT5660_GP1_PIN_IRQ (0x2 << 12) +#define RT5660_GPIO_M_MASK (0x1 << 9) +#define RT5660_GPIO_M_SFT 9 +#define RT5660_GPIO_M_FLT (0x0 << 9) +#define RT5660_GPIO_M_PH (0x1 << 9) + +/* GPIO Control 3 (0xc2) */ +#define RT5660_GP2_PF_MASK (0x1 << 5) +#define RT5660_GP2_PF_SFT 5 +#define RT5660_GP2_PF_IN (0x0 << 5) +#define RT5660_GP2_PF_OUT (0x1 << 5) +#define RT5660_GP2_OUT_MASK (0x1 << 4) +#define RT5660_GP2_OUT_SFT 4 +#define RT5660_GP2_OUT_LO (0x0 << 4) +#define RT5660_GP2_OUT_HI (0x1 << 4) +#define RT5660_GP2_P_MASK (0x1 << 3) +#define RT5660_GP2_P_SFT 3 +#define RT5660_GP2_P_NOR (0x0 << 3) +#define RT5660_GP2_P_INV (0x1 << 3) +#define RT5660_GP1_PF_MASK (0x1 << 2) +#define RT5660_GP1_PF_SFT 2 +#define RT5660_GP1_PF_IN (0x0 << 2) +#define RT5660_GP1_PF_OUT (0x1 << 2) +#define RT5660_GP1_OUT_MASK (0x1 << 1) +#define RT5660_GP1_OUT_SFT 1 +#define RT5660_GP1_OUT_LO (0x0 << 1) +#define RT5660_GP1_OUT_HI (0x1 << 1) +#define RT5660_GP1_P_MASK (0x1) +#define RT5660_GP1_P_SFT 0 +#define RT5660_GP1_P_NOR (0x0) +#define RT5660_GP1_P_INV (0x1) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5660_SV_MASK (0x1 << 15) +#define RT5660_SV_SFT 15 +#define RT5660_SV_DIS (0x0 << 15) +#define RT5660_SV_EN (0x1 << 15) +#define RT5660_SPO_SV_MASK (0x1 << 14) +#define RT5660_SPO_SV_SFT 14 +#define RT5660_SPO_SV_DIS (0x0 << 14) +#define RT5660_SPO_SV_EN (0x1 << 14) +#define RT5660_OUT_SV_MASK (0x1 << 12) +#define RT5660_OUT_SV_SFT 12 +#define RT5660_OUT_SV_DIS (0x0 << 12) +#define RT5660_OUT_SV_EN (0x1 << 12) +#define RT5660_ZCD_DIG_MASK (0x1 << 11) +#define RT5660_ZCD_DIG_SFT 11 +#define RT5660_ZCD_DIG_DIS (0x0 << 11) +#define RT5660_ZCD_DIG_EN (0x1 << 11) +#define RT5660_ZCD_MASK (0x1 << 10) +#define RT5660_ZCD_SFT 10 +#define RT5660_ZCD_PD (0x0 << 10) +#define RT5660_ZCD_PU (0x1 << 10) +#define RT5660_SV_DLY_MASK (0xf) +#define RT5660_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5660_ZCD_SPO_MASK (0x1 << 15) +#define RT5660_ZCD_SPO_SFT 15 +#define RT5660_ZCD_SPO_DIS (0x0 << 15) +#define RT5660_ZCD_SPO_EN (0x1 << 15) +#define RT5660_ZCD_OMR_MASK (0x1 << 8) +#define RT5660_ZCD_OMR_SFT 8 +#define RT5660_ZCD_OMR_DIS (0x0 << 8) +#define RT5660_ZCD_OMR_EN (0x1 << 8) +#define RT5660_ZCD_OML_MASK (0x1 << 7) +#define RT5660_ZCD_OML_SFT 7 +#define RT5660_ZCD_OML_DIS (0x0 << 7) +#define RT5660_ZCD_OML_EN (0x1 << 7) +#define RT5660_ZCD_SPM_MASK (0x1 << 6) +#define RT5660_ZCD_SPM_SFT 6 +#define RT5660_ZCD_SPM_DIS (0x0 << 6) +#define RT5660_ZCD_SPM_EN (0x1 << 6) +#define RT5660_ZCD_RMR_MASK (0x1 << 5) +#define RT5660_ZCD_RMR_SFT 5 +#define RT5660_ZCD_RMR_DIS (0x0 << 5) +#define RT5660_ZCD_RMR_EN (0x1 << 5) +#define RT5660_ZCD_RML_MASK (0x1 << 4) +#define RT5660_ZCD_RML_SFT 4 +#define RT5660_ZCD_RML_DIS (0x0 << 4) +#define RT5660_ZCD_RML_EN (0x1 << 4) + +/* General Control 1 (0xfa) */ +#define RT5660_PWR_VREF_HP (0x1 << 11) +#define RT5660_PWR_VREF_HP_SFT 11 +#define RT5660_AUTO_DIS_AMP (0x1 << 6) +#define RT5660_MCLK_DET (0x1 << 5) +#define RT5660_POW_CLKDET (0x1 << 1) +#define RT5660_DIG_GATE_CTRL (0x1) +#define RT5660_DIG_GATE_CTRL_SFT 0 + +/* System Clock Source */ +#define RT5660_SCLK_S_MCLK 0 +#define RT5660_SCLK_S_PLL1 1 +#define RT5660_SCLK_S_RCCLK 2 + +/* PLL1 Source */ +#define RT5660_PLL1_S_MCLK 0 +#define RT5660_PLL1_S_BCLK 1 + +enum { + RT5660_AIF1, + RT5660_AIFS, +}; + +struct rt5660_priv { + struct snd_soc_component *component; + struct rt5660_platform_data pdata; + struct regmap *regmap; + struct clk *mclk; + + int sysclk; + int sysclk_src; + int lrck[RT5660_AIFS]; + int bclk[RT5660_AIFS]; + int master[RT5660_AIFS]; + + int pll_src; + int pll_in; + int pll_out; +}; + +#endif diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c new file mode 100644 index 0000000000..77246f84de --- /dev/null +++ b/sound/soc/codecs/rt5663.c @@ -0,0 +1,3745 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5663.c -- RT5663 ALSA SoC audio codec driver + * + * Copyright 2016 Realtek Semiconductor Corp. + * Author: Jack Yu + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5663.h" +#include "rl6231.h" + +#define RT5663_DEVICE_ID_2 0x6451 +#define RT5663_DEVICE_ID_1 0x6406 + +#define RT5663_POWER_ON_DELAY_MS 300 +#define RT5663_SUPPLY_CURRENT_UA 500000 + +enum { + CODEC_VER_1, + CODEC_VER_0, +}; + +struct impedance_mapping_table { + unsigned int imp_min; + unsigned int imp_max; + unsigned int vol; + unsigned int dc_offset_l_manual; + unsigned int dc_offset_r_manual; + unsigned int dc_offset_l_manual_mic; + unsigned int dc_offset_r_manual_mic; +}; + +static const char *const rt5663_supply_names[] = { + "avdd", + "cpvdd", +}; + +struct rt5663_priv { + struct snd_soc_component *component; + struct rt5663_platform_data pdata; + struct regmap *regmap; + struct delayed_work jack_detect_work, jd_unplug_work; + struct snd_soc_jack *hs_jack; + struct timer_list btn_check_timer; + struct impedance_mapping_table *imp_table; + struct regulator_bulk_data supplies[ARRAY_SIZE(rt5663_supply_names)]; + + int codec_ver; + int sysclk; + int sysclk_src; + int lrck; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; +}; + +static const struct reg_sequence rt5663_patch_list[] = { + { 0x002a, 0x8020 }, + { 0x0086, 0x0028 }, + { 0x0100, 0xa020 }, + { 0x0117, 0x0f28 }, + { 0x02fb, 0x8089 }, +}; + +static const struct reg_default rt5663_v2_reg[] = { + { 0x0000, 0x0000 }, + { 0x0001, 0xc8c8 }, + { 0x0002, 0x8080 }, + { 0x0003, 0x8000 }, + { 0x0004, 0xc80a }, + { 0x0005, 0x0000 }, + { 0x0006, 0x0000 }, + { 0x0007, 0x0000 }, + { 0x000a, 0x0000 }, + { 0x000b, 0x0000 }, + { 0x000c, 0x0000 }, + { 0x000d, 0x0000 }, + { 0x000f, 0x0808 }, + { 0x0010, 0x4000 }, + { 0x0011, 0x0000 }, + { 0x0012, 0x1404 }, + { 0x0013, 0x1000 }, + { 0x0014, 0xa00a }, + { 0x0015, 0x0404 }, + { 0x0016, 0x0404 }, + { 0x0017, 0x0011 }, + { 0x0018, 0xafaf }, + { 0x0019, 0xafaf }, + { 0x001a, 0xafaf }, + { 0x001b, 0x0011 }, + { 0x001c, 0x2f2f }, + { 0x001d, 0x2f2f }, + { 0x001e, 0x2f2f }, + { 0x001f, 0x0000 }, + { 0x0020, 0x0000 }, + { 0x0021, 0x0000 }, + { 0x0022, 0x5757 }, + { 0x0023, 0x0039 }, + { 0x0024, 0x000b }, + { 0x0026, 0xc0c0 }, + { 0x0027, 0xc0c0 }, + { 0x0028, 0xc0c0 }, + { 0x0029, 0x8080 }, + { 0x002a, 0xaaaa }, + { 0x002b, 0xaaaa }, + { 0x002c, 0xaba8 }, + { 0x002d, 0x0000 }, + { 0x002e, 0x0000 }, + { 0x002f, 0x0000 }, + { 0x0030, 0x0000 }, + { 0x0031, 0x5000 }, + { 0x0032, 0x0000 }, + { 0x0033, 0x0000 }, + { 0x0034, 0x0000 }, + { 0x0035, 0x0000 }, + { 0x003a, 0x0000 }, + { 0x003b, 0x0000 }, + { 0x003c, 0x00ff }, + { 0x003d, 0x0000 }, + { 0x003e, 0x00ff }, + { 0x003f, 0x0000 }, + { 0x0040, 0x0000 }, + { 0x0041, 0x00ff }, + { 0x0042, 0x0000 }, + { 0x0043, 0x00ff }, + { 0x0044, 0x0c0c }, + { 0x0049, 0xc00b }, + { 0x004a, 0x0000 }, + { 0x004b, 0x031f }, + { 0x004d, 0x0000 }, + { 0x004e, 0x001f }, + { 0x004f, 0x0000 }, + { 0x0050, 0x001f }, + { 0x0052, 0xf000 }, + { 0x0061, 0x0000 }, + { 0x0062, 0x0000 }, + { 0x0063, 0x003e }, + { 0x0064, 0x0000 }, + { 0x0065, 0x0000 }, + { 0x0066, 0x003f }, + { 0x0067, 0x0000 }, + { 0x006b, 0x0000 }, + { 0x006d, 0xff00 }, + { 0x006e, 0x2808 }, + { 0x006f, 0x000a }, + { 0x0070, 0x8000 }, + { 0x0071, 0x8000 }, + { 0x0072, 0x8000 }, + { 0x0073, 0x7000 }, + { 0x0074, 0x7770 }, + { 0x0075, 0x0002 }, + { 0x0076, 0x0001 }, + { 0x0078, 0x00f0 }, + { 0x0079, 0x0000 }, + { 0x007a, 0x0000 }, + { 0x007b, 0x0000 }, + { 0x007c, 0x0000 }, + { 0x007d, 0x0123 }, + { 0x007e, 0x4500 }, + { 0x007f, 0x8003 }, + { 0x0080, 0x0000 }, + { 0x0081, 0x0000 }, + { 0x0082, 0x0000 }, + { 0x0083, 0x0000 }, + { 0x0084, 0x0000 }, + { 0x0085, 0x0000 }, + { 0x0086, 0x0008 }, + { 0x0087, 0x0000 }, + { 0x0088, 0x0000 }, + { 0x0089, 0x0000 }, + { 0x008a, 0x0000 }, + { 0x008b, 0x0000 }, + { 0x008c, 0x0003 }, + { 0x008e, 0x0060 }, + { 0x008f, 0x1000 }, + { 0x0091, 0x0c26 }, + { 0x0092, 0x0073 }, + { 0x0093, 0x0000 }, + { 0x0094, 0x0080 }, + { 0x0098, 0x0000 }, + { 0x0099, 0x0000 }, + { 0x009a, 0x0007 }, + { 0x009f, 0x0000 }, + { 0x00a0, 0x0000 }, + { 0x00a1, 0x0002 }, + { 0x00a2, 0x0001 }, + { 0x00a3, 0x0002 }, + { 0x00a4, 0x0001 }, + { 0x00ae, 0x2040 }, + { 0x00af, 0x0000 }, + { 0x00b6, 0x0000 }, + { 0x00b7, 0x0000 }, + { 0x00b8, 0x0000 }, + { 0x00b9, 0x0000 }, + { 0x00ba, 0x0002 }, + { 0x00bb, 0x0000 }, + { 0x00be, 0x0000 }, + { 0x00c0, 0x0000 }, + { 0x00c1, 0x0aaa }, + { 0x00c2, 0xaa80 }, + { 0x00c3, 0x0003 }, + { 0x00c4, 0x0000 }, + { 0x00d0, 0x0000 }, + { 0x00d1, 0x2244 }, + { 0x00d2, 0x0000 }, + { 0x00d3, 0x3300 }, + { 0x00d4, 0x2200 }, + { 0x00d9, 0x0809 }, + { 0x00da, 0x0000 }, + { 0x00db, 0x0008 }, + { 0x00dc, 0x00c0 }, + { 0x00dd, 0x6724 }, + { 0x00de, 0x3131 }, + { 0x00df, 0x0008 }, + { 0x00e0, 0x4000 }, + { 0x00e1, 0x3131 }, + { 0x00e2, 0x600c }, + { 0x00ea, 0xb320 }, + { 0x00eb, 0x0000 }, + { 0x00ec, 0xb300 }, + { 0x00ed, 0x0000 }, + { 0x00ee, 0xb320 }, + { 0x00ef, 0x0000 }, + { 0x00f0, 0x0201 }, + { 0x00f1, 0x0ddd }, + { 0x00f2, 0x0ddd }, + { 0x00f6, 0x0000 }, + { 0x00f7, 0x0000 }, + { 0x00f8, 0x0000 }, + { 0x00fa, 0x0000 }, + { 0x00fb, 0x0000 }, + { 0x00fc, 0x0000 }, + { 0x00fd, 0x0000 }, + { 0x00fe, 0x10ec }, + { 0x00ff, 0x6451 }, + { 0x0100, 0xaaaa }, + { 0x0101, 0x000a }, + { 0x010a, 0xaaaa }, + { 0x010b, 0xa0a0 }, + { 0x010c, 0xaeae }, + { 0x010d, 0xaaaa }, + { 0x010e, 0xaaaa }, + { 0x010f, 0xaaaa }, + { 0x0110, 0xe002 }, + { 0x0111, 0xa602 }, + { 0x0112, 0xaaaa }, + { 0x0113, 0x2000 }, + { 0x0117, 0x0f00 }, + { 0x0125, 0x0420 }, + { 0x0132, 0x0000 }, + { 0x0133, 0x0000 }, + { 0x0136, 0x5555 }, + { 0x0137, 0x5540 }, + { 0x0138, 0x3700 }, + { 0x0139, 0x79a1 }, + { 0x013a, 0x2020 }, + { 0x013b, 0x2020 }, + { 0x013c, 0x2005 }, + { 0x013f, 0x0000 }, + { 0x0145, 0x0002 }, + { 0x0146, 0x0000 }, + { 0x0147, 0x0000 }, + { 0x0148, 0x0000 }, + { 0x0160, 0x4ec0 }, + { 0x0161, 0x0080 }, + { 0x0162, 0x0200 }, + { 0x0163, 0x0800 }, + { 0x0164, 0x0000 }, + { 0x0165, 0x0000 }, + { 0x0166, 0x0000 }, + { 0x0167, 0x000f }, + { 0x0168, 0x000f }, + { 0x0170, 0x4e80 }, + { 0x0171, 0x0080 }, + { 0x0172, 0x0200 }, + { 0x0173, 0x0800 }, + { 0x0174, 0x00ff }, + { 0x0175, 0x0000 }, + { 0x0190, 0x4131 }, + { 0x0191, 0x4131 }, + { 0x0192, 0x4131 }, + { 0x0193, 0x4131 }, + { 0x0194, 0x0000 }, + { 0x0195, 0x0000 }, + { 0x0196, 0x0000 }, + { 0x0197, 0x0000 }, + { 0x0198, 0x0000 }, + { 0x0199, 0x0000 }, + { 0x01a0, 0x1e64 }, + { 0x01a1, 0x06a3 }, + { 0x01a2, 0x0000 }, + { 0x01a3, 0x0000 }, + { 0x01a4, 0x0000 }, + { 0x01a5, 0x0000 }, + { 0x01a6, 0x0000 }, + { 0x01a7, 0x0000 }, + { 0x01a8, 0x0000 }, + { 0x01a9, 0x0000 }, + { 0x01aa, 0x0000 }, + { 0x01ab, 0x0000 }, + { 0x01b5, 0x0000 }, + { 0x01b6, 0x01c3 }, + { 0x01b7, 0x02a0 }, + { 0x01b8, 0x03e9 }, + { 0x01b9, 0x1389 }, + { 0x01ba, 0xc351 }, + { 0x01bb, 0x0009 }, + { 0x01bc, 0x0018 }, + { 0x01bd, 0x002a }, + { 0x01be, 0x004c }, + { 0x01bf, 0x0097 }, + { 0x01c0, 0x433d }, + { 0x01c1, 0x0000 }, + { 0x01c2, 0x0000 }, + { 0x01c3, 0x0000 }, + { 0x01c4, 0x0000 }, + { 0x01c5, 0x0000 }, + { 0x01c6, 0x0000 }, + { 0x01c7, 0x0000 }, + { 0x01c8, 0x40af }, + { 0x01c9, 0x0702 }, + { 0x01ca, 0x0000 }, + { 0x01cb, 0x0000 }, + { 0x01cc, 0x5757 }, + { 0x01cd, 0x5757 }, + { 0x01ce, 0x5757 }, + { 0x01cf, 0x5757 }, + { 0x01d0, 0x5757 }, + { 0x01d1, 0x5757 }, + { 0x01d2, 0x5757 }, + { 0x01d3, 0x5757 }, + { 0x01d4, 0x5757 }, + { 0x01d5, 0x5757 }, + { 0x01d6, 0x003c }, + { 0x01da, 0x0000 }, + { 0x01db, 0x0000 }, + { 0x01dc, 0x0000 }, + { 0x01de, 0x7c00 }, + { 0x01df, 0x0320 }, + { 0x01e0, 0x06a1 }, + { 0x01e1, 0x0000 }, + { 0x01e2, 0x0000 }, + { 0x01e3, 0x0000 }, + { 0x01e4, 0x0000 }, + { 0x01e5, 0x0000 }, + { 0x01e6, 0x0001 }, + { 0x01e7, 0x0000 }, + { 0x01e8, 0x0000 }, + { 0x01ea, 0x0000 }, + { 0x01eb, 0x0000 }, + { 0x01ec, 0x0000 }, + { 0x01ed, 0x0000 }, + { 0x01ee, 0x0000 }, + { 0x01ef, 0x0000 }, + { 0x01f0, 0x0000 }, + { 0x01f1, 0x0000 }, + { 0x01f2, 0x0000 }, + { 0x01f3, 0x0000 }, + { 0x01f4, 0x0000 }, + { 0x0200, 0x0000 }, + { 0x0201, 0x0000 }, + { 0x0202, 0x0000 }, + { 0x0203, 0x0000 }, + { 0x0204, 0x0000 }, + { 0x0205, 0x0000 }, + { 0x0206, 0x0000 }, + { 0x0207, 0x0000 }, + { 0x0208, 0x0000 }, + { 0x0210, 0x60b1 }, + { 0x0211, 0xa000 }, + { 0x0212, 0x024c }, + { 0x0213, 0xf7ff }, + { 0x0214, 0x024c }, + { 0x0215, 0x0102 }, + { 0x0216, 0x00a3 }, + { 0x0217, 0x0048 }, + { 0x0218, 0x92c0 }, + { 0x0219, 0x0000 }, + { 0x021a, 0x00c8 }, + { 0x021b, 0x0020 }, + { 0x02fa, 0x0000 }, + { 0x02fb, 0x0000 }, + { 0x02fc, 0x0000 }, + { 0x02ff, 0x0110 }, + { 0x0300, 0x001f }, + { 0x0301, 0x032c }, + { 0x0302, 0x5f21 }, + { 0x0303, 0x4000 }, + { 0x0304, 0x4000 }, + { 0x0305, 0x06d5 }, + { 0x0306, 0x8000 }, + { 0x0307, 0x0700 }, + { 0x0310, 0x4560 }, + { 0x0311, 0xa4a8 }, + { 0x0312, 0x7418 }, + { 0x0313, 0x0000 }, + { 0x0314, 0x0006 }, + { 0x0315, 0xffff }, + { 0x0316, 0xc400 }, + { 0x0317, 0x0000 }, + { 0x0330, 0x00a6 }, + { 0x0331, 0x04c3 }, + { 0x0332, 0x27c8 }, + { 0x0333, 0xbf50 }, + { 0x0334, 0x0045 }, + { 0x0335, 0x0007 }, + { 0x0336, 0x7418 }, + { 0x0337, 0x0501 }, + { 0x0338, 0x0000 }, + { 0x0339, 0x0010 }, + { 0x033a, 0x1010 }, + { 0x03c0, 0x7e00 }, + { 0x03c1, 0x8000 }, + { 0x03c2, 0x8000 }, + { 0x03c3, 0x8000 }, + { 0x03c4, 0x8000 }, + { 0x03c5, 0x8000 }, + { 0x03c6, 0x8000 }, + { 0x03c7, 0x8000 }, + { 0x03c8, 0x8000 }, + { 0x03c9, 0x8000 }, + { 0x03ca, 0x8000 }, + { 0x03cb, 0x8000 }, + { 0x03cc, 0x8000 }, + { 0x03d0, 0x0000 }, + { 0x03d1, 0x0000 }, + { 0x03d2, 0x0000 }, + { 0x03d3, 0x0000 }, + { 0x03d4, 0x2000 }, + { 0x03d5, 0x2000 }, + { 0x03d6, 0x0000 }, + { 0x03d7, 0x0000 }, + { 0x03d8, 0x2000 }, + { 0x03d9, 0x2000 }, + { 0x03da, 0x2000 }, + { 0x03db, 0x2000 }, + { 0x03dc, 0x0000 }, + { 0x03dd, 0x0000 }, + { 0x03de, 0x0000 }, + { 0x03df, 0x2000 }, + { 0x03e0, 0x0000 }, + { 0x03e1, 0x0000 }, + { 0x03e2, 0x0000 }, + { 0x03e3, 0x0000 }, + { 0x03e4, 0x0000 }, + { 0x03e5, 0x0000 }, + { 0x03e6, 0x0000 }, + { 0x03e7, 0x0000 }, + { 0x03e8, 0x0000 }, + { 0x03e9, 0x0000 }, + { 0x03ea, 0x0000 }, + { 0x03eb, 0x0000 }, + { 0x03ec, 0x0000 }, + { 0x03ed, 0x0000 }, + { 0x03ee, 0x0000 }, + { 0x03ef, 0x0000 }, + { 0x03f0, 0x0800 }, + { 0x03f1, 0x0800 }, + { 0x03f2, 0x0800 }, + { 0x03f3, 0x0800 }, + { 0x03fe, 0x0000 }, + { 0x03ff, 0x0000 }, + { 0x07f0, 0x0000 }, + { 0x07fa, 0x0000 }, +}; + +static const struct reg_default rt5663_reg[] = { + { 0x0000, 0x0000 }, + { 0x0002, 0x0008 }, + { 0x0005, 0x1000 }, + { 0x0006, 0x1000 }, + { 0x000a, 0x0000 }, + { 0x0010, 0x000f }, + { 0x0015, 0x42f1 }, + { 0x0016, 0x0000 }, + { 0x0018, 0x000b }, + { 0x0019, 0xafaf }, + { 0x001c, 0x2f2f }, + { 0x001f, 0x0000 }, + { 0x0022, 0x5757 }, + { 0x0023, 0x0039 }, + { 0x0026, 0xc0c0 }, + { 0x0029, 0x8080 }, + { 0x002a, 0x8020 }, + { 0x002c, 0x000c }, + { 0x002d, 0x0000 }, + { 0x0040, 0x0808 }, + { 0x0061, 0x0000 }, + { 0x0062, 0x0000 }, + { 0x0063, 0x003e }, + { 0x0064, 0x0000 }, + { 0x0065, 0x0000 }, + { 0x0066, 0x0000 }, + { 0x006b, 0x0000 }, + { 0x006e, 0x0000 }, + { 0x006f, 0x0000 }, + { 0x0070, 0x8020 }, + { 0x0073, 0x1000 }, + { 0x0074, 0xe400 }, + { 0x0075, 0x0002 }, + { 0x0076, 0x0001 }, + { 0x0077, 0x00f0 }, + { 0x0078, 0x0000 }, + { 0x0079, 0x0000 }, + { 0x007a, 0x0123 }, + { 0x007b, 0x8003 }, + { 0x0080, 0x0000 }, + { 0x0081, 0x0000 }, + { 0x0082, 0x0000 }, + { 0x0083, 0x0000 }, + { 0x0084, 0x0000 }, + { 0x0086, 0x0028 }, + { 0x0087, 0x0000 }, + { 0x008a, 0x0000 }, + { 0x008b, 0x0000 }, + { 0x008c, 0x0003 }, + { 0x008e, 0x0008 }, + { 0x008f, 0x1000 }, + { 0x0090, 0x0646 }, + { 0x0091, 0x0e3e }, + { 0x0092, 0x1071 }, + { 0x0093, 0x0000 }, + { 0x0094, 0x0080 }, + { 0x0097, 0x0000 }, + { 0x0098, 0x0000 }, + { 0x009a, 0x0000 }, + { 0x009f, 0x0000 }, + { 0x00ae, 0x6000 }, + { 0x00af, 0x0000 }, + { 0x00b6, 0x0000 }, + { 0x00b7, 0x0000 }, + { 0x00b8, 0x0000 }, + { 0x00ba, 0x0000 }, + { 0x00bb, 0x0000 }, + { 0x00be, 0x0000 }, + { 0x00bf, 0x0000 }, + { 0x00c0, 0x0000 }, + { 0x00c1, 0x0000 }, + { 0x00c5, 0x0000 }, + { 0x00cb, 0xa02f }, + { 0x00cc, 0x0000 }, + { 0x00cd, 0x0e02 }, + { 0x00d9, 0x08f9 }, + { 0x00db, 0x0008 }, + { 0x00dc, 0x00c0 }, + { 0x00dd, 0x6729 }, + { 0x00de, 0x3131 }, + { 0x00df, 0x0008 }, + { 0x00e0, 0x4000 }, + { 0x00e1, 0x3131 }, + { 0x00e2, 0x0043 }, + { 0x00e4, 0x400b }, + { 0x00e5, 0x8031 }, + { 0x00e6, 0x3080 }, + { 0x00e7, 0x4100 }, + { 0x00e8, 0x1400 }, + { 0x00e9, 0xe00a }, + { 0x00ea, 0x0404 }, + { 0x00eb, 0x0404 }, + { 0x00ec, 0xb320 }, + { 0x00ed, 0x0000 }, + { 0x00f4, 0x0000 }, + { 0x00f6, 0x0000 }, + { 0x00f8, 0x0000 }, + { 0x00fa, 0x8000 }, + { 0x00fd, 0x0001 }, + { 0x00fe, 0x10ec }, + { 0x00ff, 0x6406 }, + { 0x0100, 0xa020 }, + { 0x0108, 0x4444 }, + { 0x0109, 0x4444 }, + { 0x010a, 0xaaaa }, + { 0x010b, 0x00a0 }, + { 0x010c, 0x8aaa }, + { 0x010d, 0xaaaa }, + { 0x010e, 0x2aaa }, + { 0x010f, 0x002a }, + { 0x0110, 0xa0a4 }, + { 0x0111, 0x4602 }, + { 0x0112, 0x0101 }, + { 0x0113, 0x2000 }, + { 0x0114, 0x0000 }, + { 0x0116, 0x0000 }, + { 0x0117, 0x0f28 }, + { 0x0118, 0x0006 }, + { 0x0125, 0x2424 }, + { 0x0126, 0x5550 }, + { 0x0127, 0x0400 }, + { 0x0128, 0x7711 }, + { 0x0132, 0x0004 }, + { 0x0137, 0x5441 }, + { 0x0139, 0x79a1 }, + { 0x013a, 0x30c0 }, + { 0x013b, 0x2000 }, + { 0x013c, 0x2005 }, + { 0x013d, 0x30c0 }, + { 0x013e, 0x0000 }, + { 0x0140, 0x3700 }, + { 0x0141, 0x1f00 }, + { 0x0144, 0x0000 }, + { 0x0145, 0x0002 }, + { 0x0146, 0x0000 }, + { 0x0160, 0x0e80 }, + { 0x0161, 0x0080 }, + { 0x0162, 0x0200 }, + { 0x0163, 0x0800 }, + { 0x0164, 0x0000 }, + { 0x0165, 0x0000 }, + { 0x0166, 0x0000 }, + { 0x0167, 0x1417 }, + { 0x0168, 0x0017 }, + { 0x0169, 0x0017 }, + { 0x0180, 0x2000 }, + { 0x0181, 0x0000 }, + { 0x0182, 0x0000 }, + { 0x0183, 0x2000 }, + { 0x0184, 0x0000 }, + { 0x0185, 0x0000 }, + { 0x01b0, 0x4b30 }, + { 0x01b1, 0x0000 }, + { 0x01b2, 0xd870 }, + { 0x01b3, 0x0000 }, + { 0x01b4, 0x0030 }, + { 0x01b5, 0x5757 }, + { 0x01b6, 0x5757 }, + { 0x01b7, 0x5757 }, + { 0x01b8, 0x5757 }, + { 0x01c0, 0x433d }, + { 0x01c1, 0x0540 }, + { 0x01c2, 0x0000 }, + { 0x01c3, 0x0000 }, + { 0x01c4, 0x0000 }, + { 0x01c5, 0x0009 }, + { 0x01c6, 0x0018 }, + { 0x01c7, 0x002a }, + { 0x01c8, 0x004c }, + { 0x01c9, 0x0097 }, + { 0x01ca, 0x01c3 }, + { 0x01cb, 0x03e9 }, + { 0x01cc, 0x1389 }, + { 0x01cd, 0xc351 }, + { 0x01ce, 0x0000 }, + { 0x01cf, 0x0000 }, + { 0x01d0, 0x0000 }, + { 0x01d1, 0x0000 }, + { 0x01d2, 0x0000 }, + { 0x01d3, 0x003c }, + { 0x01d4, 0x5757 }, + { 0x01d5, 0x5757 }, + { 0x01d6, 0x5757 }, + { 0x01d7, 0x5757 }, + { 0x01d8, 0x5757 }, + { 0x01d9, 0x5757 }, + { 0x01da, 0x0000 }, + { 0x01db, 0x0000 }, + { 0x01dd, 0x0009 }, + { 0x01de, 0x7f00 }, + { 0x01df, 0x00c8 }, + { 0x01e0, 0x0691 }, + { 0x01e1, 0x0000 }, + { 0x01e2, 0x0000 }, + { 0x01e3, 0x0000 }, + { 0x01e4, 0x0000 }, + { 0x01e5, 0x0040 }, + { 0x01e6, 0x0000 }, + { 0x01e7, 0x0000 }, + { 0x01e8, 0x0000 }, + { 0x01ea, 0x0000 }, + { 0x01eb, 0x0000 }, + { 0x01ec, 0x0000 }, + { 0x01ed, 0x0000 }, + { 0x01ee, 0x0000 }, + { 0x01ef, 0x0000 }, + { 0x01f0, 0x0000 }, + { 0x01f1, 0x0000 }, + { 0x01f2, 0x0000 }, + { 0x0200, 0x0000 }, + { 0x0201, 0x2244 }, + { 0x0202, 0xaaaa }, + { 0x0250, 0x8010 }, + { 0x0251, 0x0000 }, + { 0x0252, 0x028a }, + { 0x02fa, 0x0000 }, + { 0x02fb, 0x8089 }, + { 0x02fc, 0x0300 }, + { 0x0300, 0x0000 }, + { 0x03d0, 0x0000 }, + { 0x03d1, 0x0000 }, + { 0x03d2, 0x0000 }, + { 0x03d3, 0x0000 }, + { 0x03d4, 0x2000 }, + { 0x03d5, 0x2000 }, + { 0x03d6, 0x0000 }, + { 0x03d7, 0x0000 }, + { 0x03d8, 0x2000 }, + { 0x03d9, 0x2000 }, + { 0x03da, 0x2000 }, + { 0x03db, 0x2000 }, + { 0x03dc, 0x0000 }, + { 0x03dd, 0x0000 }, + { 0x03de, 0x0000 }, + { 0x03df, 0x2000 }, + { 0x03e0, 0x0000 }, + { 0x03e1, 0x0000 }, + { 0x03e2, 0x0000 }, + { 0x03e3, 0x0000 }, + { 0x03e4, 0x0000 }, + { 0x03e5, 0x0000 }, + { 0x03e6, 0x0000 }, + { 0x03e7, 0x0000 }, + { 0x03e8, 0x0000 }, + { 0x03e9, 0x0000 }, + { 0x03ea, 0x0000 }, + { 0x03eb, 0x0000 }, + { 0x03ec, 0x0000 }, + { 0x03ed, 0x0000 }, + { 0x03ee, 0x0000 }, + { 0x03ef, 0x0000 }, + { 0x03f0, 0x0800 }, + { 0x03f1, 0x0800 }, + { 0x03f2, 0x0800 }, + { 0x03f3, 0x0800 }, +}; + +static bool rt5663_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5663_RESET: + case RT5663_SIL_DET_CTL: + case RT5663_HP_IMP_GAIN_2: + case RT5663_AD_DA_MIXER: + case RT5663_FRAC_DIV_2: + case RT5663_MICBIAS_1: + case RT5663_ASRC_11_2: + case RT5663_ADC_EQ_1: + case RT5663_INT_ST_1: + case RT5663_INT_ST_2: + case RT5663_GPIO_STA1: + case RT5663_SIN_GEN_1: + case RT5663_IL_CMD_1: + case RT5663_IL_CMD_5: + case RT5663_IL_CMD_PWRSAV1: + case RT5663_EM_JACK_TYPE_1: + case RT5663_EM_JACK_TYPE_2: + case RT5663_EM_JACK_TYPE_3: + case RT5663_JD_CTRL2: + case RT5663_VENDOR_ID: + case RT5663_VENDOR_ID_1: + case RT5663_VENDOR_ID_2: + case RT5663_PLL_INT_REG: + case RT5663_SOFT_RAMP: + case RT5663_STO_DRE_1: + case RT5663_STO_DRE_5: + case RT5663_STO_DRE_6: + case RT5663_STO_DRE_7: + case RT5663_MIC_DECRO_1: + case RT5663_MIC_DECRO_4: + case RT5663_HP_IMP_SEN_1: + case RT5663_HP_IMP_SEN_3: + case RT5663_HP_IMP_SEN_4: + case RT5663_HP_IMP_SEN_5: + case RT5663_HP_CALIB_1_1: + case RT5663_HP_CALIB_9: + case RT5663_HP_CALIB_ST1: + case RT5663_HP_CALIB_ST2: + case RT5663_HP_CALIB_ST3: + case RT5663_HP_CALIB_ST4: + case RT5663_HP_CALIB_ST5: + case RT5663_HP_CALIB_ST6: + case RT5663_HP_CALIB_ST7: + case RT5663_HP_CALIB_ST8: + case RT5663_HP_CALIB_ST9: + case RT5663_ANA_JD: + return true; + default: + return false; + } +} + +static bool rt5663_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5663_RESET: + case RT5663_HP_OUT_EN: + case RT5663_HP_LCH_DRE: + case RT5663_HP_RCH_DRE: + case RT5663_CALIB_BST: + case RT5663_RECMIX: + case RT5663_SIL_DET_CTL: + case RT5663_PWR_SAV_SILDET: + case RT5663_SIDETONE_CTL: + case RT5663_STO1_DAC_DIG_VOL: + case RT5663_STO1_ADC_DIG_VOL: + case RT5663_STO1_BOOST: + case RT5663_HP_IMP_GAIN_1: + case RT5663_HP_IMP_GAIN_2: + case RT5663_STO1_ADC_MIXER: + case RT5663_AD_DA_MIXER: + case RT5663_STO_DAC_MIXER: + case RT5663_DIG_SIDE_MIXER: + case RT5663_BYPASS_STO_DAC: + case RT5663_CALIB_REC_MIX: + case RT5663_PWR_DIG_1: + case RT5663_PWR_DIG_2: + case RT5663_PWR_ANLG_1: + case RT5663_PWR_ANLG_2: + case RT5663_PWR_ANLG_3: + case RT5663_PWR_MIXER: + case RT5663_SIG_CLK_DET: + case RT5663_PRE_DIV_GATING_1: + case RT5663_PRE_DIV_GATING_2: + case RT5663_I2S1_SDP: + case RT5663_ADDA_CLK_1: + case RT5663_ADDA_RST: + case RT5663_FRAC_DIV_1: + case RT5663_FRAC_DIV_2: + case RT5663_TDM_1: + case RT5663_TDM_2: + case RT5663_TDM_3: + case RT5663_TDM_4: + case RT5663_TDM_5: + case RT5663_GLB_CLK: + case RT5663_PLL_1: + case RT5663_PLL_2: + case RT5663_ASRC_1: + case RT5663_ASRC_2: + case RT5663_ASRC_4: + case RT5663_DUMMY_REG: + case RT5663_ASRC_8: + case RT5663_ASRC_9: + case RT5663_ASRC_11: + case RT5663_DEPOP_1: + case RT5663_DEPOP_2: + case RT5663_DEPOP_3: + case RT5663_HP_CHARGE_PUMP_1: + case RT5663_HP_CHARGE_PUMP_2: + case RT5663_MICBIAS_1: + case RT5663_RC_CLK: + case RT5663_ASRC_11_2: + case RT5663_DUMMY_REG_2: + case RT5663_REC_PATH_GAIN: + case RT5663_AUTO_1MRC_CLK: + case RT5663_ADC_EQ_1: + case RT5663_ADC_EQ_2: + case RT5663_IRQ_1: + case RT5663_IRQ_2: + case RT5663_IRQ_3: + case RT5663_IRQ_4: + case RT5663_IRQ_5: + case RT5663_INT_ST_1: + case RT5663_INT_ST_2: + case RT5663_GPIO_1: + case RT5663_GPIO_2: + case RT5663_GPIO_STA1: + case RT5663_SIN_GEN_1: + case RT5663_SIN_GEN_2: + case RT5663_SIN_GEN_3: + case RT5663_SOF_VOL_ZC1: + case RT5663_IL_CMD_1: + case RT5663_IL_CMD_2: + case RT5663_IL_CMD_3: + case RT5663_IL_CMD_4: + case RT5663_IL_CMD_5: + case RT5663_IL_CMD_6: + case RT5663_IL_CMD_7: + case RT5663_IL_CMD_8: + case RT5663_IL_CMD_PWRSAV1: + case RT5663_IL_CMD_PWRSAV2: + case RT5663_EM_JACK_TYPE_1: + case RT5663_EM_JACK_TYPE_2: + case RT5663_EM_JACK_TYPE_3: + case RT5663_EM_JACK_TYPE_4: + case RT5663_EM_JACK_TYPE_5: + case RT5663_EM_JACK_TYPE_6: + case RT5663_STO1_HPF_ADJ1: + case RT5663_STO1_HPF_ADJ2: + case RT5663_FAST_OFF_MICBIAS: + case RT5663_JD_CTRL1: + case RT5663_JD_CTRL2: + case RT5663_DIG_MISC: + case RT5663_VENDOR_ID: + case RT5663_VENDOR_ID_1: + case RT5663_VENDOR_ID_2: + case RT5663_DIG_VOL_ZCD: + case RT5663_ANA_BIAS_CUR_1: + case RT5663_ANA_BIAS_CUR_2: + case RT5663_ANA_BIAS_CUR_3: + case RT5663_ANA_BIAS_CUR_4: + case RT5663_ANA_BIAS_CUR_5: + case RT5663_ANA_BIAS_CUR_6: + case RT5663_BIAS_CUR_5: + case RT5663_BIAS_CUR_6: + case RT5663_BIAS_CUR_7: + case RT5663_BIAS_CUR_8: + case RT5663_DACREF_LDO: + case RT5663_DUMMY_REG_3: + case RT5663_BIAS_CUR_9: + case RT5663_DUMMY_REG_4: + case RT5663_VREFADJ_OP: + case RT5663_VREF_RECMIX: + case RT5663_CHARGE_PUMP_1: + case RT5663_CHARGE_PUMP_1_2: + case RT5663_CHARGE_PUMP_1_3: + case RT5663_CHARGE_PUMP_2: + case RT5663_DIG_IN_PIN1: + case RT5663_PAD_DRV_CTL: + case RT5663_PLL_INT_REG: + case RT5663_CHOP_DAC_L: + case RT5663_CHOP_ADC: + case RT5663_CALIB_ADC: + case RT5663_CHOP_DAC_R: + case RT5663_DUMMY_CTL_DACLR: + case RT5663_DUMMY_REG_5: + case RT5663_SOFT_RAMP: + case RT5663_TEST_MODE_1: + case RT5663_TEST_MODE_2: + case RT5663_TEST_MODE_3: + case RT5663_STO_DRE_1: + case RT5663_STO_DRE_2: + case RT5663_STO_DRE_3: + case RT5663_STO_DRE_4: + case RT5663_STO_DRE_5: + case RT5663_STO_DRE_6: + case RT5663_STO_DRE_7: + case RT5663_STO_DRE_8: + case RT5663_STO_DRE_9: + case RT5663_STO_DRE_10: + case RT5663_MIC_DECRO_1: + case RT5663_MIC_DECRO_2: + case RT5663_MIC_DECRO_3: + case RT5663_MIC_DECRO_4: + case RT5663_MIC_DECRO_5: + case RT5663_MIC_DECRO_6: + case RT5663_HP_DECRO_1: + case RT5663_HP_DECRO_2: + case RT5663_HP_DECRO_3: + case RT5663_HP_DECRO_4: + case RT5663_HP_DECOUP: + case RT5663_HP_IMP_SEN_MAP8: + case RT5663_HP_IMP_SEN_MAP9: + case RT5663_HP_IMP_SEN_MAP10: + case RT5663_HP_IMP_SEN_MAP11: + case RT5663_HP_IMP_SEN_1: + case RT5663_HP_IMP_SEN_2: + case RT5663_HP_IMP_SEN_3: + case RT5663_HP_IMP_SEN_4: + case RT5663_HP_IMP_SEN_5: + case RT5663_HP_IMP_SEN_6: + case RT5663_HP_IMP_SEN_7: + case RT5663_HP_IMP_SEN_8: + case RT5663_HP_IMP_SEN_9: + case RT5663_HP_IMP_SEN_10: + case RT5663_HP_IMP_SEN_11: + case RT5663_HP_IMP_SEN_12: + case RT5663_HP_IMP_SEN_13: + case RT5663_HP_IMP_SEN_14: + case RT5663_HP_IMP_SEN_15: + case RT5663_HP_IMP_SEN_16: + case RT5663_HP_IMP_SEN_17: + case RT5663_HP_IMP_SEN_18: + case RT5663_HP_IMP_SEN_19: + case RT5663_HP_IMPSEN_DIG5: + case RT5663_HP_IMPSEN_MAP1: + case RT5663_HP_IMPSEN_MAP2: + case RT5663_HP_IMPSEN_MAP3: + case RT5663_HP_IMPSEN_MAP4: + case RT5663_HP_IMPSEN_MAP5: + case RT5663_HP_IMPSEN_MAP7: + case RT5663_HP_LOGIC_1: + case RT5663_HP_LOGIC_2: + case RT5663_HP_CALIB_1: + case RT5663_HP_CALIB_1_1: + case RT5663_HP_CALIB_2: + case RT5663_HP_CALIB_3: + case RT5663_HP_CALIB_4: + case RT5663_HP_CALIB_5: + case RT5663_HP_CALIB_5_1: + case RT5663_HP_CALIB_6: + case RT5663_HP_CALIB_7: + case RT5663_HP_CALIB_9: + case RT5663_HP_CALIB_10: + case RT5663_HP_CALIB_11: + case RT5663_HP_CALIB_ST1: + case RT5663_HP_CALIB_ST2: + case RT5663_HP_CALIB_ST3: + case RT5663_HP_CALIB_ST4: + case RT5663_HP_CALIB_ST5: + case RT5663_HP_CALIB_ST6: + case RT5663_HP_CALIB_ST7: + case RT5663_HP_CALIB_ST8: + case RT5663_HP_CALIB_ST9: + case RT5663_HP_AMP_DET: + case RT5663_DUMMY_REG_6: + case RT5663_HP_BIAS: + case RT5663_CBJ_1: + case RT5663_CBJ_2: + case RT5663_CBJ_3: + case RT5663_DUMMY_1: + case RT5663_DUMMY_2: + case RT5663_DUMMY_3: + case RT5663_ANA_JD: + case RT5663_ADC_LCH_LPF1_A1: + case RT5663_ADC_RCH_LPF1_A1: + case RT5663_ADC_LCH_LPF1_H0: + case RT5663_ADC_RCH_LPF1_H0: + case RT5663_ADC_LCH_BPF1_A1: + case RT5663_ADC_RCH_BPF1_A1: + case RT5663_ADC_LCH_BPF1_A2: + case RT5663_ADC_RCH_BPF1_A2: + case RT5663_ADC_LCH_BPF1_H0: + case RT5663_ADC_RCH_BPF1_H0: + case RT5663_ADC_LCH_BPF2_A1: + case RT5663_ADC_RCH_BPF2_A1: + case RT5663_ADC_LCH_BPF2_A2: + case RT5663_ADC_RCH_BPF2_A2: + case RT5663_ADC_LCH_BPF2_H0: + case RT5663_ADC_RCH_BPF2_H0: + case RT5663_ADC_LCH_BPF3_A1: + case RT5663_ADC_RCH_BPF3_A1: + case RT5663_ADC_LCH_BPF3_A2: + case RT5663_ADC_RCH_BPF3_A2: + case RT5663_ADC_LCH_BPF3_H0: + case RT5663_ADC_RCH_BPF3_H0: + case RT5663_ADC_LCH_BPF4_A1: + case RT5663_ADC_RCH_BPF4_A1: + case RT5663_ADC_LCH_BPF4_A2: + case RT5663_ADC_RCH_BPF4_A2: + case RT5663_ADC_LCH_BPF4_H0: + case RT5663_ADC_RCH_BPF4_H0: + case RT5663_ADC_LCH_HPF1_A1: + case RT5663_ADC_RCH_HPF1_A1: + case RT5663_ADC_LCH_HPF1_H0: + case RT5663_ADC_RCH_HPF1_H0: + case RT5663_ADC_EQ_PRE_VOL_L: + case RT5663_ADC_EQ_PRE_VOL_R: + case RT5663_ADC_EQ_POST_VOL_L: + case RT5663_ADC_EQ_POST_VOL_R: + return true; + default: + return false; + } +} + +static bool rt5663_v2_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5663_RESET: + case RT5663_CBJ_TYPE_2: + case RT5663_PDM_OUT_CTL: + case RT5663_PDM_I2C_DATA_CTL1: + case RT5663_PDM_I2C_DATA_CTL4: + case RT5663_ALC_BK_GAIN: + case RT5663_PLL_2: + case RT5663_MICBIAS_1: + case RT5663_ADC_EQ_1: + case RT5663_INT_ST_1: + case RT5663_GPIO_STA2: + case RT5663_IL_CMD_1: + case RT5663_IL_CMD_5: + case RT5663_A_JD_CTRL: + case RT5663_JD_CTRL2: + case RT5663_VENDOR_ID: + case RT5663_VENDOR_ID_1: + case RT5663_VENDOR_ID_2: + case RT5663_STO_DRE_1: + case RT5663_STO_DRE_5: + case RT5663_STO_DRE_6: + case RT5663_STO_DRE_7: + case RT5663_MONO_DYNA_6: + case RT5663_STO1_SIL_DET: + case RT5663_MONOL_SIL_DET: + case RT5663_MONOR_SIL_DET: + case RT5663_STO2_DAC_SIL: + case RT5663_MONO_AMP_CAL_ST1: + case RT5663_MONO_AMP_CAL_ST2: + case RT5663_MONO_AMP_CAL_ST3: + case RT5663_MONO_AMP_CAL_ST4: + case RT5663_HP_IMP_SEN_2: + case RT5663_HP_IMP_SEN_3: + case RT5663_HP_IMP_SEN_4: + case RT5663_HP_IMP_SEN_10: + case RT5663_HP_CALIB_1: + case RT5663_HP_CALIB_10: + case RT5663_HP_CALIB_ST1: + case RT5663_HP_CALIB_ST4: + case RT5663_HP_CALIB_ST5: + case RT5663_HP_CALIB_ST6: + case RT5663_HP_CALIB_ST7: + case RT5663_HP_CALIB_ST8: + case RT5663_HP_CALIB_ST9: + case RT5663_HP_CALIB_ST10: + case RT5663_HP_CALIB_ST11: + return true; + default: + return false; + } +} + +static bool rt5663_v2_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5663_LOUT_CTRL: + case RT5663_HP_AMP_2: + case RT5663_MONO_OUT: + case RT5663_MONO_GAIN: + case RT5663_AEC_BST: + case RT5663_IN1_IN2: + case RT5663_IN3_IN4: + case RT5663_INL1_INR1: + case RT5663_CBJ_TYPE_2: + case RT5663_CBJ_TYPE_3: + case RT5663_CBJ_TYPE_4: + case RT5663_CBJ_TYPE_5: + case RT5663_CBJ_TYPE_8: + case RT5663_DAC3_DIG_VOL: + case RT5663_DAC3_CTRL: + case RT5663_MONO_ADC_DIG_VOL: + case RT5663_STO2_ADC_DIG_VOL: + case RT5663_MONO_ADC_BST_GAIN: + case RT5663_STO2_ADC_BST_GAIN: + case RT5663_SIDETONE_CTRL: + case RT5663_MONO1_ADC_MIXER: + case RT5663_STO2_ADC_MIXER: + case RT5663_MONO_DAC_MIXER: + case RT5663_DAC2_SRC_CTRL: + case RT5663_IF_3_4_DATA_CTL: + case RT5663_IF_5_DATA_CTL: + case RT5663_PDM_OUT_CTL: + case RT5663_PDM_I2C_DATA_CTL1: + case RT5663_PDM_I2C_DATA_CTL2: + case RT5663_PDM_I2C_DATA_CTL3: + case RT5663_PDM_I2C_DATA_CTL4: + case RT5663_RECMIX1_NEW: + case RT5663_RECMIX1L_0: + case RT5663_RECMIX1L: + case RT5663_RECMIX1R_0: + case RT5663_RECMIX1R: + case RT5663_RECMIX2_NEW: + case RT5663_RECMIX2_L_2: + case RT5663_RECMIX2_R: + case RT5663_RECMIX2_R_2: + case RT5663_CALIB_REC_LR: + case RT5663_ALC_BK_GAIN: + case RT5663_MONOMIX_GAIN: + case RT5663_MONOMIX_IN_GAIN: + case RT5663_OUT_MIXL_GAIN: + case RT5663_OUT_LMIX_IN_GAIN: + case RT5663_OUT_RMIX_IN_GAIN: + case RT5663_OUT_RMIX_IN_GAIN1: + case RT5663_LOUT_MIXER_CTRL: + case RT5663_PWR_VOL: + case RT5663_ADCDAC_RST: + case RT5663_I2S34_SDP: + case RT5663_I2S5_SDP: + case RT5663_TDM_6: + case RT5663_TDM_7: + case RT5663_TDM_8: + case RT5663_TDM_9: + case RT5663_ASRC_3: + case RT5663_ASRC_6: + case RT5663_ASRC_7: + case RT5663_PLL_TRK_13: + case RT5663_I2S_M_CLK_CTL: + case RT5663_FDIV_I2S34_M_CLK: + case RT5663_FDIV_I2S34_M_CLK2: + case RT5663_FDIV_I2S5_M_CLK: + case RT5663_FDIV_I2S5_M_CLK2: + case RT5663_V2_IRQ_4: + case RT5663_GPIO_3: + case RT5663_GPIO_4: + case RT5663_GPIO_STA2: + case RT5663_HP_AMP_DET1: + case RT5663_HP_AMP_DET2: + case RT5663_HP_AMP_DET3: + case RT5663_MID_BD_HP_AMP: + case RT5663_LOW_BD_HP_AMP: + case RT5663_SOF_VOL_ZC2: + case RT5663_ADC_STO2_ADJ1: + case RT5663_ADC_STO2_ADJ2: + case RT5663_A_JD_CTRL: + case RT5663_JD1_TRES_CTRL: + case RT5663_JD2_TRES_CTRL: + case RT5663_V2_JD_CTRL2: + case RT5663_DUM_REG_2: + case RT5663_DUM_REG_3: + case RT5663_VENDOR_ID: + case RT5663_VENDOR_ID_1: + case RT5663_VENDOR_ID_2: + case RT5663_DACADC_DIG_VOL2: + case RT5663_DIG_IN_PIN2: + case RT5663_PAD_DRV_CTL1: + case RT5663_SOF_RAM_DEPOP: + case RT5663_VOL_TEST: + case RT5663_TEST_MODE_4: + case RT5663_TEST_MODE_5: + case RT5663_STO_DRE_9: + case RT5663_MONO_DYNA_1: + case RT5663_MONO_DYNA_2: + case RT5663_MONO_DYNA_3: + case RT5663_MONO_DYNA_4: + case RT5663_MONO_DYNA_5: + case RT5663_MONO_DYNA_6: + case RT5663_STO1_SIL_DET: + case RT5663_MONOL_SIL_DET: + case RT5663_MONOR_SIL_DET: + case RT5663_STO2_DAC_SIL: + case RT5663_PWR_SAV_CTL1: + case RT5663_PWR_SAV_CTL2: + case RT5663_PWR_SAV_CTL3: + case RT5663_PWR_SAV_CTL4: + case RT5663_PWR_SAV_CTL5: + case RT5663_PWR_SAV_CTL6: + case RT5663_MONO_AMP_CAL1: + case RT5663_MONO_AMP_CAL2: + case RT5663_MONO_AMP_CAL3: + case RT5663_MONO_AMP_CAL4: + case RT5663_MONO_AMP_CAL5: + case RT5663_MONO_AMP_CAL6: + case RT5663_MONO_AMP_CAL7: + case RT5663_MONO_AMP_CAL_ST1: + case RT5663_MONO_AMP_CAL_ST2: + case RT5663_MONO_AMP_CAL_ST3: + case RT5663_MONO_AMP_CAL_ST4: + case RT5663_MONO_AMP_CAL_ST5: + case RT5663_V2_HP_IMP_SEN_13: + case RT5663_V2_HP_IMP_SEN_14: + case RT5663_V2_HP_IMP_SEN_6: + case RT5663_V2_HP_IMP_SEN_7: + case RT5663_V2_HP_IMP_SEN_8: + case RT5663_V2_HP_IMP_SEN_9: + case RT5663_V2_HP_IMP_SEN_10: + case RT5663_HP_LOGIC_3: + case RT5663_HP_CALIB_ST10: + case RT5663_HP_CALIB_ST11: + case RT5663_PRO_REG_TBL_4: + case RT5663_PRO_REG_TBL_5: + case RT5663_PRO_REG_TBL_6: + case RT5663_PRO_REG_TBL_7: + case RT5663_PRO_REG_TBL_8: + case RT5663_PRO_REG_TBL_9: + case RT5663_SAR_ADC_INL_1: + case RT5663_SAR_ADC_INL_2: + case RT5663_SAR_ADC_INL_3: + case RT5663_SAR_ADC_INL_4: + case RT5663_SAR_ADC_INL_5: + case RT5663_SAR_ADC_INL_6: + case RT5663_SAR_ADC_INL_7: + case RT5663_SAR_ADC_INL_8: + case RT5663_SAR_ADC_INL_9: + case RT5663_SAR_ADC_INL_10: + case RT5663_SAR_ADC_INL_11: + case RT5663_SAR_ADC_INL_12: + case RT5663_DRC_CTRL_1: + case RT5663_DRC1_CTRL_2: + case RT5663_DRC1_CTRL_3: + case RT5663_DRC1_CTRL_4: + case RT5663_DRC1_CTRL_5: + case RT5663_DRC1_CTRL_6: + case RT5663_DRC1_HD_CTRL_1: + case RT5663_DRC1_HD_CTRL_2: + case RT5663_DRC1_PRI_REG_1: + case RT5663_DRC1_PRI_REG_2: + case RT5663_DRC1_PRI_REG_3: + case RT5663_DRC1_PRI_REG_4: + case RT5663_DRC1_PRI_REG_5: + case RT5663_DRC1_PRI_REG_6: + case RT5663_DRC1_PRI_REG_7: + case RT5663_DRC1_PRI_REG_8: + case RT5663_ALC_PGA_CTL_1: + case RT5663_ALC_PGA_CTL_2: + case RT5663_ALC_PGA_CTL_3: + case RT5663_ALC_PGA_CTL_4: + case RT5663_ALC_PGA_CTL_5: + case RT5663_ALC_PGA_CTL_6: + case RT5663_ALC_PGA_CTL_7: + case RT5663_ALC_PGA_CTL_8: + case RT5663_ALC_PGA_REG_1: + case RT5663_ALC_PGA_REG_2: + case RT5663_ALC_PGA_REG_3: + case RT5663_ADC_EQ_RECOV_1: + case RT5663_ADC_EQ_RECOV_2: + case RT5663_ADC_EQ_RECOV_3: + case RT5663_ADC_EQ_RECOV_4: + case RT5663_ADC_EQ_RECOV_5: + case RT5663_ADC_EQ_RECOV_6: + case RT5663_ADC_EQ_RECOV_7: + case RT5663_ADC_EQ_RECOV_8: + case RT5663_ADC_EQ_RECOV_9: + case RT5663_ADC_EQ_RECOV_10: + case RT5663_ADC_EQ_RECOV_11: + case RT5663_ADC_EQ_RECOV_12: + case RT5663_ADC_EQ_RECOV_13: + case RT5663_VID_HIDDEN: + case RT5663_VID_CUSTOMER: + case RT5663_SCAN_MODE: + case RT5663_I2C_BYPA: + return true; + case RT5663_TDM_1: + case RT5663_DEPOP_3: + case RT5663_ASRC_11_2: + case RT5663_INT_ST_2: + case RT5663_GPIO_STA1: + case RT5663_SIN_GEN_1: + case RT5663_SIN_GEN_2: + case RT5663_SIN_GEN_3: + case RT5663_IL_CMD_PWRSAV1: + case RT5663_IL_CMD_PWRSAV2: + case RT5663_EM_JACK_TYPE_1: + case RT5663_EM_JACK_TYPE_2: + case RT5663_EM_JACK_TYPE_3: + case RT5663_EM_JACK_TYPE_4: + case RT5663_FAST_OFF_MICBIAS: + case RT5663_ANA_BIAS_CUR_1: + case RT5663_ANA_BIAS_CUR_2: + case RT5663_BIAS_CUR_9: + case RT5663_DUMMY_REG_4: + case RT5663_VREF_RECMIX: + case RT5663_CHARGE_PUMP_1_2: + case RT5663_CHARGE_PUMP_1_3: + case RT5663_CHARGE_PUMP_2: + case RT5663_CHOP_DAC_R: + case RT5663_DUMMY_CTL_DACLR: + case RT5663_DUMMY_REG_5: + case RT5663_SOFT_RAMP: + case RT5663_TEST_MODE_1: + case RT5663_STO_DRE_10: + case RT5663_MIC_DECRO_1: + case RT5663_MIC_DECRO_2: + case RT5663_MIC_DECRO_3: + case RT5663_MIC_DECRO_4: + case RT5663_MIC_DECRO_5: + case RT5663_MIC_DECRO_6: + case RT5663_HP_DECRO_1: + case RT5663_HP_DECRO_2: + case RT5663_HP_DECRO_3: + case RT5663_HP_DECRO_4: + case RT5663_HP_DECOUP: + case RT5663_HP_IMPSEN_MAP4: + case RT5663_HP_IMPSEN_MAP5: + case RT5663_HP_IMPSEN_MAP7: + case RT5663_HP_CALIB_1: + case RT5663_CBJ_1: + case RT5663_CBJ_2: + case RT5663_CBJ_3: + return false; + default: + return rt5663_readable_register(dev, reg); + } +} + +static const DECLARE_TLV_DB_SCALE(rt5663_hp_vol_tlv, -2400, 150, 0); +static const DECLARE_TLV_DB_SCALE(rt5663_v2_hp_vol_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(in_bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5663_if1_adc_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5663_if1_adc_enum, RT5663_TDM_2, + RT5663_DATA_SWAP_ADCDAT1_SHIFT, rt5663_if1_adc_data_select); + +static void rt5663_enable_push_button_irq(struct snd_soc_component *component, + bool enable) +{ + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + + if (enable) { + snd_soc_component_update_bits(component, RT5663_IL_CMD_6, + RT5663_EN_4BTN_INL_MASK, RT5663_EN_4BTN_INL_EN); + /* reset in-line command */ + snd_soc_component_update_bits(component, RT5663_IL_CMD_6, + RT5663_RESET_4BTN_INL_MASK, + RT5663_RESET_4BTN_INL_RESET); + snd_soc_component_update_bits(component, RT5663_IL_CMD_6, + RT5663_RESET_4BTN_INL_MASK, + RT5663_RESET_4BTN_INL_NOR); + switch (rt5663->codec_ver) { + case CODEC_VER_1: + snd_soc_component_update_bits(component, RT5663_IRQ_3, + RT5663_V2_EN_IRQ_INLINE_MASK, + RT5663_V2_EN_IRQ_INLINE_NOR); + break; + case CODEC_VER_0: + snd_soc_component_update_bits(component, RT5663_IRQ_2, + RT5663_EN_IRQ_INLINE_MASK, + RT5663_EN_IRQ_INLINE_NOR); + break; + default: + dev_err(component->dev, "Unknown CODEC Version\n"); + } + } else { + switch (rt5663->codec_ver) { + case CODEC_VER_1: + snd_soc_component_update_bits(component, RT5663_IRQ_3, + RT5663_V2_EN_IRQ_INLINE_MASK, + RT5663_V2_EN_IRQ_INLINE_BYP); + break; + case CODEC_VER_0: + snd_soc_component_update_bits(component, RT5663_IRQ_2, + RT5663_EN_IRQ_INLINE_MASK, + RT5663_EN_IRQ_INLINE_BYP); + break; + default: + dev_err(component->dev, "Unknown CODEC Version\n"); + } + snd_soc_component_update_bits(component, RT5663_IL_CMD_6, + RT5663_EN_4BTN_INL_MASK, RT5663_EN_4BTN_INL_DIS); + /* reset in-line command */ + snd_soc_component_update_bits(component, RT5663_IL_CMD_6, + RT5663_RESET_4BTN_INL_MASK, + RT5663_RESET_4BTN_INL_RESET); + snd_soc_component_update_bits(component, RT5663_IL_CMD_6, + RT5663_RESET_4BTN_INL_MASK, + RT5663_RESET_4BTN_INL_NOR); + } +} + +/** + * rt5663_v2_jack_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ + +static int rt5663_v2_jack_detect(struct snd_soc_component *component, int jack_insert) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + int val, i = 0, sleep_time[5] = {300, 150, 100, 50, 30}; + + dev_dbg(component->dev, "%s jack_insert:%d\n", __func__, jack_insert); + if (jack_insert) { + snd_soc_component_write(component, RT5663_CBJ_TYPE_2, 0x8040); + snd_soc_component_write(component, RT5663_CBJ_TYPE_3, 0x1484); + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS2"); + snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_force_enable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + snd_soc_component_update_bits(component, RT5663_RC_CLK, + RT5663_DIG_1M_CLK_MASK, RT5663_DIG_1M_CLK_EN); + snd_soc_component_update_bits(component, RT5663_RECMIX, 0x8, 0x8); + + while (i < 5) { + msleep(sleep_time[i]); + val = snd_soc_component_read(component, RT5663_CBJ_TYPE_2) & 0x0003; + if (val == 0x1 || val == 0x2 || val == 0x3) + break; + dev_dbg(component->dev, "%s: MX-0011 val=%x sleep %d\n", + __func__, val, sleep_time[i]); + i++; + } + dev_dbg(component->dev, "%s val = %d\n", __func__, val); + switch (val) { + case 1: + case 2: + rt5663->jack_type = SND_JACK_HEADSET; + rt5663_enable_push_button_irq(component, true); + break; + default: + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS2"); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_disable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + rt5663->jack_type = SND_JACK_HEADPHONE; + break; + } + } else { + snd_soc_component_update_bits(component, RT5663_RECMIX, 0x8, 0x0); + + if (rt5663->jack_type == SND_JACK_HEADSET) { + rt5663_enable_push_button_irq(component, false); + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS2"); + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_disable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + } + rt5663->jack_type = 0; + } + + dev_dbg(component->dev, "jack_type = %d\n", rt5663->jack_type); + return rt5663->jack_type; +} + +/** + * rt5663_jack_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5663_jack_detect(struct snd_soc_component *component, int jack_insert) +{ + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + int val, i = 0; + + dev_dbg(component->dev, "%s jack_insert:%d\n", __func__, jack_insert); + + if (jack_insert) { + snd_soc_component_update_bits(component, RT5663_DIG_MISC, + RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN); + snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1, + RT5663_SI_HP_MASK | RT5663_OSW_HP_L_MASK | + RT5663_OSW_HP_R_MASK, RT5663_SI_HP_EN | + RT5663_OSW_HP_L_DIS | RT5663_OSW_HP_R_DIS); + snd_soc_component_update_bits(component, RT5663_DUMMY_1, + RT5663_EMB_CLK_MASK | RT5663_HPA_CPL_BIAS_MASK | + RT5663_HPA_CPR_BIAS_MASK, RT5663_EMB_CLK_EN | + RT5663_HPA_CPL_BIAS_1 | RT5663_HPA_CPR_BIAS_1); + snd_soc_component_update_bits(component, RT5663_CBJ_1, + RT5663_INBUF_CBJ_BST1_MASK | RT5663_CBJ_SENSE_BST1_MASK, + RT5663_INBUF_CBJ_BST1_ON | RT5663_CBJ_SENSE_BST1_L); + snd_soc_component_update_bits(component, RT5663_IL_CMD_2, + RT5663_PWR_MIC_DET_MASK, RT5663_PWR_MIC_DET_ON); + /* BST1 power on for JD */ + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2, + RT5663_PWR_BST1_MASK, RT5663_PWR_BST1_ON); + snd_soc_component_update_bits(component, RT5663_EM_JACK_TYPE_1, + RT5663_CBJ_DET_MASK | RT5663_EXT_JD_MASK | + RT5663_POL_EXT_JD_MASK, RT5663_CBJ_DET_EN | + RT5663_EXT_JD_EN | RT5663_POL_EXT_JD_EN); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_MB_MASK | RT5663_LDO1_DVO_MASK | + RT5663_AMP_HP_MASK, RT5663_PWR_MB | + RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_VREF1 | RT5663_PWR_VREF2); + msleep(20); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_FV1 | RT5663_PWR_FV2); + snd_soc_component_update_bits(component, RT5663_AUTO_1MRC_CLK, + RT5663_IRQ_POW_SAV_MASK, RT5663_IRQ_POW_SAV_EN); + snd_soc_component_update_bits(component, RT5663_IRQ_1, + RT5663_EN_IRQ_JD1_MASK, RT5663_EN_IRQ_JD1_EN); + snd_soc_component_update_bits(component, RT5663_EM_JACK_TYPE_1, + RT5663_EM_JD_MASK, RT5663_EM_JD_RST); + snd_soc_component_update_bits(component, RT5663_EM_JACK_TYPE_1, + RT5663_EM_JD_MASK, RT5663_EM_JD_NOR); + + while (true) { + regmap_read(rt5663->regmap, RT5663_INT_ST_2, &val); + if (!(val & 0x80)) + usleep_range(10000, 10005); + else + break; + + if (i > 200) + break; + i++; + } + + val = snd_soc_component_read(component, RT5663_EM_JACK_TYPE_2) & 0x0003; + dev_dbg(component->dev, "%s val = %d\n", __func__, val); + + snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1, + RT5663_OSW_HP_L_MASK | RT5663_OSW_HP_R_MASK, + RT5663_OSW_HP_L_EN | RT5663_OSW_HP_R_EN); + + switch (val) { + case 1: + case 2: + rt5663->jack_type = SND_JACK_HEADSET; + rt5663_enable_push_button_irq(component, true); + + if (rt5663->pdata.impedance_sensing_num) + break; + + if (rt5663->pdata.dc_offset_l_manual_mic) { + regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2, + rt5663->pdata.dc_offset_l_manual_mic >> + 16); + regmap_write(rt5663->regmap, RT5663_MIC_DECRO_3, + rt5663->pdata.dc_offset_l_manual_mic & + 0xffff); + } + + if (rt5663->pdata.dc_offset_r_manual_mic) { + regmap_write(rt5663->regmap, RT5663_MIC_DECRO_5, + rt5663->pdata.dc_offset_r_manual_mic >> + 16); + regmap_write(rt5663->regmap, RT5663_MIC_DECRO_6, + rt5663->pdata.dc_offset_r_manual_mic & + 0xffff); + } + break; + default: + rt5663->jack_type = SND_JACK_HEADPHONE; + snd_soc_component_update_bits(component, + RT5663_PWR_ANLG_1, + RT5663_PWR_MB_MASK | RT5663_PWR_VREF1_MASK | + RT5663_PWR_VREF2_MASK, 0); + if (rt5663->pdata.impedance_sensing_num) + break; + + if (rt5663->pdata.dc_offset_l_manual) { + regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2, + rt5663->pdata.dc_offset_l_manual >> 16); + regmap_write(rt5663->regmap, RT5663_MIC_DECRO_3, + rt5663->pdata.dc_offset_l_manual & + 0xffff); + } + + if (rt5663->pdata.dc_offset_r_manual) { + regmap_write(rt5663->regmap, RT5663_MIC_DECRO_5, + rt5663->pdata.dc_offset_r_manual >> 16); + regmap_write(rt5663->regmap, RT5663_MIC_DECRO_6, + rt5663->pdata.dc_offset_r_manual & + 0xffff); + } + break; + } + } else { + if (rt5663->jack_type == SND_JACK_HEADSET) + rt5663_enable_push_button_irq(component, false); + rt5663->jack_type = 0; + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_MB_MASK | RT5663_PWR_VREF1_MASK | + RT5663_PWR_VREF2_MASK, 0); + } + + dev_dbg(component->dev, "jack_type = %d\n", rt5663->jack_type); + return rt5663->jack_type; +} + +static int rt5663_impedance_sensing(struct snd_soc_component *component) +{ + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + unsigned int value, i, reg84, reg26, reg2fa, reg91, reg10, reg80; + + for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) { + if (rt5663->imp_table[i].vol == 7) + break; + } + + if (rt5663->jack_type == SND_JACK_HEADSET) { + snd_soc_component_write(component, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual_mic >> 16); + snd_soc_component_write(component, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff); + snd_soc_component_write(component, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual_mic >> 16); + snd_soc_component_write(component, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff); + } else { + snd_soc_component_write(component, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual >> 16); + snd_soc_component_write(component, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual & 0xffff); + snd_soc_component_write(component, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual >> 16); + snd_soc_component_write(component, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual & 0xffff); + } + + reg84 = snd_soc_component_read(component, RT5663_ASRC_2); + reg26 = snd_soc_component_read(component, RT5663_STO1_ADC_MIXER); + reg2fa = snd_soc_component_read(component, RT5663_DUMMY_1); + reg91 = snd_soc_component_read(component, RT5663_HP_CHARGE_PUMP_1); + reg10 = snd_soc_component_read(component, RT5663_RECMIX); + reg80 = snd_soc_component_read(component, RT5663_GLB_CLK); + + snd_soc_component_update_bits(component, RT5663_STO_DRE_1, 0x8000, 0); + snd_soc_component_write(component, RT5663_ASRC_2, 0); + snd_soc_component_write(component, RT5663_STO1_ADC_MIXER, 0x4040); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_VREF1 | RT5663_PWR_VREF2); + usleep_range(10000, 10005); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_FV1 | RT5663_PWR_FV2); + snd_soc_component_update_bits(component, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK, + RT5663_SCLK_SRC_RCCLK); + snd_soc_component_update_bits(component, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK, + RT5663_DIG_25M_CLK_EN); + snd_soc_component_update_bits(component, RT5663_ADDA_CLK_1, RT5663_I2S_PD1_MASK, 0); + snd_soc_component_write(component, RT5663_PRE_DIV_GATING_1, 0xff00); + snd_soc_component_write(component, RT5663_PRE_DIV_GATING_2, 0xfffc); + snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_1, 0x1232); + snd_soc_component_write(component, RT5663_HP_LOGIC_2, 0x0005); + snd_soc_component_write(component, RT5663_DEPOP_2, 0x3003); + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030, 0x0030); + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003, 0x0003); + snd_soc_component_update_bits(component, RT5663_PWR_DIG_2, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F); + snd_soc_component_update_bits(component, RT5663_PWR_DIG_1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_ON | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1); + msleep(40); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2); + msleep(30); + snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_2, 0x1371); + snd_soc_component_write(component, RT5663_STO_DAC_MIXER, 0); + snd_soc_component_write(component, RT5663_BYPASS_STO_DAC, 0x000c); + snd_soc_component_write(component, RT5663_HP_BIAS, 0xafaa); + snd_soc_component_write(component, RT5663_CHARGE_PUMP_1, 0x2224); + snd_soc_component_write(component, RT5663_HP_OUT_EN, 0x8088); + snd_soc_component_write(component, RT5663_CHOP_ADC, 0x3000); + snd_soc_component_write(component, RT5663_ADDA_RST, 0xc000); + snd_soc_component_write(component, RT5663_STO1_HPF_ADJ1, 0x3320); + snd_soc_component_write(component, RT5663_HP_CALIB_2, 0x00c9); + snd_soc_component_write(component, RT5663_DUMMY_1, 0x004c); + snd_soc_component_write(component, RT5663_ANA_BIAS_CUR_1, 0x7733); + snd_soc_component_write(component, RT5663_CHARGE_PUMP_2, 0x7777); + snd_soc_component_write(component, RT5663_STO_DRE_9, 0x0007); + snd_soc_component_write(component, RT5663_STO_DRE_10, 0x0007); + snd_soc_component_write(component, RT5663_DUMMY_2, 0x02a4); + snd_soc_component_write(component, RT5663_RECMIX, 0x0005); + snd_soc_component_write(component, RT5663_HP_IMP_SEN_1, 0x4334); + snd_soc_component_update_bits(component, RT5663_IRQ_3, 0x0004, 0x0004); + snd_soc_component_write(component, RT5663_HP_LOGIC_1, 0x2200); + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0x3000); + snd_soc_component_write(component, RT5663_HP_LOGIC_1, 0x6200); + + for (i = 0; i < 100; i++) { + msleep(20); + if (snd_soc_component_read(component, RT5663_INT_ST_1) & 0x2) + break; + } + + value = snd_soc_component_read(component, RT5663_HP_IMP_SEN_4); + + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0); + snd_soc_component_write(component, RT5663_INT_ST_1, 0); + snd_soc_component_write(component, RT5663_HP_LOGIC_1, 0); + snd_soc_component_update_bits(component, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK, + RT5663_DIG_25M_CLK_DIS); + snd_soc_component_write(component, RT5663_GLB_CLK, reg80); + snd_soc_component_write(component, RT5663_RECMIX, reg10); + snd_soc_component_write(component, RT5663_DUMMY_2, 0x00a4); + snd_soc_component_write(component, RT5663_DUMMY_1, reg2fa); + snd_soc_component_write(component, RT5663_HP_CALIB_2, 0x00c8); + snd_soc_component_write(component, RT5663_STO1_HPF_ADJ1, 0xb320); + snd_soc_component_write(component, RT5663_ADDA_RST, 0xe400); + snd_soc_component_write(component, RT5663_CHOP_ADC, 0x2000); + snd_soc_component_write(component, RT5663_HP_OUT_EN, 0x0008); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, 0); + snd_soc_component_update_bits(component, RT5663_PWR_DIG_1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1, 0); + snd_soc_component_update_bits(component, RT5663_PWR_DIG_2, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, 0); + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003, 0); + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030, 0); + snd_soc_component_write(component, RT5663_HP_LOGIC_2, 0); + snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_1, reg91); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK, 0); + snd_soc_component_write(component, RT5663_STO1_ADC_MIXER, reg26); + snd_soc_component_write(component, RT5663_ASRC_2, reg84); + + for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) { + if (value >= rt5663->imp_table[i].imp_min && + value <= rt5663->imp_table[i].imp_max) + break; + } + + snd_soc_component_update_bits(component, RT5663_STO_DRE_9, RT5663_DRE_GAIN_HP_MASK, + rt5663->imp_table[i].vol); + snd_soc_component_update_bits(component, RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_MASK, + rt5663->imp_table[i].vol); + + if (rt5663->jack_type == SND_JACK_HEADSET) { + snd_soc_component_write(component, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual_mic >> 16); + snd_soc_component_write(component, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff); + snd_soc_component_write(component, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual_mic >> 16); + snd_soc_component_write(component, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff); + } else { + snd_soc_component_write(component, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual >> 16); + snd_soc_component_write(component, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual & 0xffff); + snd_soc_component_write(component, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual >> 16); + snd_soc_component_write(component, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual & 0xffff); + } + + return 0; +} + +static int rt5663_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read(component, RT5663_IL_CMD_5); + dev_dbg(component->dev, "%s: val=0x%x\n", __func__, val); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5663_IL_CMD_5, val); + + return btn_type; +} + +static irqreturn_t rt5663_irq(int irq, void *data) +{ + struct rt5663_priv *rt5663 = data; + + dev_dbg(regmap_get_device(rt5663->regmap), "%s IRQ queue work\n", + __func__); + + queue_delayed_work(system_wq, &rt5663->jack_detect_work, + msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static int rt5663_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + + rt5663->hs_jack = hs_jack; + + rt5663_irq(0, rt5663); + + return 0; +} + +static bool rt5663_check_jd_status(struct snd_soc_component *component) +{ + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + int val = snd_soc_component_read(component, RT5663_INT_ST_1); + + dev_dbg(component->dev, "%s val=%x\n", __func__, val); + + /* JD1 */ + switch (rt5663->codec_ver) { + case CODEC_VER_1: + return !(val & 0x2000); + case CODEC_VER_0: + return !(val & 0x1000); + default: + dev_err(component->dev, "Unknown CODEC Version\n"); + } + + return false; +} + +static void rt5663_jack_detect_work(struct work_struct *work) +{ + struct rt5663_priv *rt5663 = + container_of(work, struct rt5663_priv, jack_detect_work.work); + struct snd_soc_component *component = rt5663->component; + int btn_type, report = 0; + + if (!component) + return; + + if (rt5663_check_jd_status(component)) { + /* jack in */ + if (rt5663->jack_type == 0) { + /* jack was out, report jack type */ + switch (rt5663->codec_ver) { + case CODEC_VER_1: + report = rt5663_v2_jack_detect( + rt5663->component, 1); + break; + case CODEC_VER_0: + report = rt5663_jack_detect(rt5663->component, 1); + if (rt5663->pdata.impedance_sensing_num) + rt5663_impedance_sensing(rt5663->component); + break; + default: + dev_err(component->dev, "Unknown CODEC Version\n"); + } + + /* Delay the jack insert report to avoid pop noise */ + msleep(30); + } else { + /* jack is already in, report button event */ + report = SND_JACK_HEADSET; + btn_type = rt5663_button_detect(rt5663->component); + /** + * rt5663 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + report |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + report |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + report |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + report |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + btn_type = 0; + dev_err(rt5663->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + /* button release or spurious interrput*/ + if (btn_type == 0) { + report = rt5663->jack_type; + cancel_delayed_work_sync( + &rt5663->jd_unplug_work); + } else { + queue_delayed_work(system_wq, + &rt5663->jd_unplug_work, + msecs_to_jiffies(500)); + } + } + } else { + /* jack out */ + switch (rt5663->codec_ver) { + case CODEC_VER_1: + report = rt5663_v2_jack_detect(rt5663->component, 0); + break; + case CODEC_VER_0: + report = rt5663_jack_detect(rt5663->component, 0); + break; + default: + dev_err(component->dev, "Unknown CODEC Version\n"); + } + } + dev_dbg(component->dev, "%s jack report: 0x%04x\n", __func__, report); + snd_soc_jack_report(rt5663->hs_jack, report, SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); +} + +static void rt5663_jd_unplug_work(struct work_struct *work) +{ + struct rt5663_priv *rt5663 = + container_of(work, struct rt5663_priv, jd_unplug_work.work); + struct snd_soc_component *component = rt5663->component; + + if (!component) + return; + + if (!rt5663_check_jd_status(component)) { + /* jack out */ + switch (rt5663->codec_ver) { + case CODEC_VER_1: + rt5663_v2_jack_detect(rt5663->component, 0); + break; + case CODEC_VER_0: + rt5663_jack_detect(rt5663->component, 0); + break; + default: + dev_err(component->dev, "Unknown CODEC Version\n"); + } + + snd_soc_jack_report(rt5663->hs_jack, 0, SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else { + queue_delayed_work(system_wq, &rt5663->jd_unplug_work, + msecs_to_jiffies(500)); + } +} + +static const struct snd_kcontrol_new rt5663_snd_controls[] = { + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC Playback Volume", RT5663_STO1_DAC_DIG_VOL, + RT5663_DAC_L1_VOL_SHIFT + 1, RT5663_DAC_R1_VOL_SHIFT + 1, + 87, 0, dac_vol_tlv), + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5663_STO1_ADC_DIG_VOL, + RT5663_ADC_L_MUTE_SHIFT, RT5663_ADC_R_MUTE_SHIFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5663_STO1_ADC_DIG_VOL, + RT5663_ADC_L_VOL_SHIFT + 1, RT5663_ADC_R_VOL_SHIFT + 1, + 63, 0, adc_vol_tlv), +}; + +static const struct snd_kcontrol_new rt5663_v2_specific_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_HP_LCH_DRE, + RT5663_HP_RCH_DRE, RT5663_GAIN_HP_SHIFT, 15, 1, + rt5663_v2_hp_vol_tlv), + /* Mic Boost Volume */ + SOC_SINGLE_TLV("IN1 Capture Volume", RT5663_AEC_BST, + RT5663_GAIN_CBJ_SHIFT, 8, 0, in_bst_tlv), +}; + +static const struct snd_kcontrol_new rt5663_specific_controls[] = { + /* Mic Boost Volume*/ + SOC_SINGLE_TLV("IN1 Capture Volume", RT5663_CBJ_2, + RT5663_GAIN_BST1_SHIFT, 8, 0, in_bst_tlv), + /* Data Swap for Slot0/1 in ADCDAT1 */ + SOC_ENUM("IF1 ADC Data Swap", rt5663_if1_adc_enum), +}; + +static const struct snd_kcontrol_new rt5663_hpvol_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9, + RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1, + rt5663_hp_vol_tlv), +}; + +static int rt5663_is_sys_clk_from_pll(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read(component, RT5663_GLB_CLK); + val &= RT5663_SCLK_SRC_MASK; + if (val == RT5663_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int rt5663_is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + + if (rt5663->codec_ver == CODEC_VER_1) { + switch (w->shift) { + case RT5663_ADC_STO1_ASRC_SHIFT: + reg = RT5663_ASRC_3; + shift = RT5663_V2_AD_STO1_TRACK_SHIFT; + break; + case RT5663_DAC_STO1_ASRC_SHIFT: + reg = RT5663_ASRC_2; + shift = RT5663_DA_STO1_TRACK_SHIFT; + break; + default: + return 0; + } + } else { + switch (w->shift) { + case RT5663_ADC_STO1_ASRC_SHIFT: + reg = RT5663_ASRC_2; + shift = RT5663_AD_STO1_TRACK_SHIFT; + break; + case RT5663_DAC_STO1_ASRC_SHIFT: + reg = RT5663_ASRC_2; + shift = RT5663_DA_STO1_TRACK_SHIFT; + break; + default: + return 0; + } + } + + val = (snd_soc_component_read(component, reg) >> shift) & 0x7; + + if (val) + return 1; + + return 0; +} + +static int rt5663_i2s_use_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + int da_asrc_en, ad_asrc_en; + + da_asrc_en = (snd_soc_component_read(component, RT5663_ASRC_2) & + RT5663_DA_STO1_TRACK_MASK) ? 1 : 0; + switch (rt5663->codec_ver) { + case CODEC_VER_1: + ad_asrc_en = (snd_soc_component_read(component, RT5663_ASRC_3) & + RT5663_V2_AD_STO1_TRACK_MASK) ? 1 : 0; + break; + case CODEC_VER_0: + ad_asrc_en = (snd_soc_component_read(component, RT5663_ASRC_2) & + RT5663_AD_STO1_TRACK_MASK) ? 1 : 0; + break; + default: + dev_err(component->dev, "Unknown CODEC Version\n"); + return 1; + } + + if (da_asrc_en || ad_asrc_en) + if (rt5663->sysclk > rt5663->lrck * 384) + return 1; + + dev_err(component->dev, "sysclk < 384 x fs, disable i2s asrc\n"); + + return 0; +} + +/** + * rt5663_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5663 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the codec driver will turn on ASRC + * for these filters if ASRC is selected as their clock source. + */ +int rt5663_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + unsigned int asrc2_mask = 0; + unsigned int asrc2_value = 0; + unsigned int asrc3_mask = 0; + unsigned int asrc3_value = 0; + + switch (clk_src) { + case RT5663_CLK_SEL_SYS: + case RT5663_CLK_SEL_I2S1_ASRC: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5663_DA_STEREO_FILTER) { + asrc2_mask |= RT5663_DA_STO1_TRACK_MASK; + asrc2_value |= clk_src << RT5663_DA_STO1_TRACK_SHIFT; + } + + if (filter_mask & RT5663_AD_STEREO_FILTER) { + switch (rt5663->codec_ver) { + case CODEC_VER_1: + asrc3_mask |= RT5663_V2_AD_STO1_TRACK_MASK; + asrc3_value |= clk_src << RT5663_V2_AD_STO1_TRACK_SHIFT; + break; + case CODEC_VER_0: + asrc2_mask |= RT5663_AD_STO1_TRACK_MASK; + asrc2_value |= clk_src << RT5663_AD_STO1_TRACK_SHIFT; + break; + default: + dev_err(component->dev, "Unknown CODEC Version\n"); + } + } + + if (asrc2_mask) + snd_soc_component_update_bits(component, RT5663_ASRC_2, asrc2_mask, + asrc2_value); + + if (asrc3_mask) + snd_soc_component_update_bits(component, RT5663_ASRC_3, asrc3_mask, + asrc3_value); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5663_sel_asrc_clk_src); + +/* Analog Mixer */ +static const struct snd_kcontrol_new rt5663_recmix1l[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5663_RECMIX1L, + RT5663_RECMIX1L_BST2_SHIFT, 1, 1), + SOC_DAPM_SINGLE("BST1 CBJ Switch", RT5663_RECMIX1L, + RT5663_RECMIX1L_BST1_CBJ_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_recmix1r[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5663_RECMIX1R, + RT5663_RECMIX1R_BST2_SHIFT, 1, 1), +}; + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5663_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5663_STO1_ADC_MIXER, + RT5663_M_STO1_ADC_L1_SHIFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5663_STO1_ADC_MIXER, + RT5663_M_STO1_ADC_L2_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5663_STO1_ADC_MIXER, + RT5663_M_STO1_ADC_R1_SHIFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5663_STO1_ADC_MIXER, + RT5663_M_STO1_ADC_R2_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_adda_l_mix[] = { + SOC_DAPM_SINGLE("ADC L Switch", RT5663_AD_DA_MIXER, + RT5663_M_ADCMIX_L_SHIFT, 1, 1), + SOC_DAPM_SINGLE("DAC L Switch", RT5663_AD_DA_MIXER, + RT5663_M_DAC1_L_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_adda_r_mix[] = { + SOC_DAPM_SINGLE("ADC R Switch", RT5663_AD_DA_MIXER, + RT5663_M_ADCMIX_R_SHIFT, 1, 1), + SOC_DAPM_SINGLE("DAC R Switch", RT5663_AD_DA_MIXER, + RT5663_M_DAC1_R_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L Switch", RT5663_STO_DAC_MIXER, + RT5663_M_DAC_L1_STO_L_SHIFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5663_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R Switch", RT5663_STO_DAC_MIXER, + RT5663_M_DAC_R1_STO_R_SHIFT, 1, 1), +}; + +/* Out Switch */ +static const struct snd_kcontrol_new rt5663_hpo_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5663_HP_AMP_2, + RT5663_EN_DAC_HPO_SHIFT, 1, 0); + +/* Stereo ADC source */ +static const char * const rt5663_sto1_adc_src[] = { + "ADC L", "ADC R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5663_sto1_adcl_enum, RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_L_SRC_SHIFT, rt5663_sto1_adc_src); + +static const struct snd_kcontrol_new rt5663_sto1_adcl_mux = + SOC_DAPM_ENUM("STO1 ADC L Mux", rt5663_sto1_adcl_enum); + +static SOC_ENUM_SINGLE_DECL(rt5663_sto1_adcr_enum, RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_R_SRC_SHIFT, rt5663_sto1_adc_src); + +static const struct snd_kcontrol_new rt5663_sto1_adcr_mux = + SOC_DAPM_ENUM("STO1 ADC R Mux", rt5663_sto1_adcr_enum); + +/* RT5663: Analog DACL1 input source */ +static const char * const rt5663_alg_dacl_src[] = { + "DAC L", "STO DAC MIXL" +}; + +static SOC_ENUM_SINGLE_DECL(rt5663_alg_dacl_enum, RT5663_BYPASS_STO_DAC, + RT5663_DACL1_SRC_SHIFT, rt5663_alg_dacl_src); + +static const struct snd_kcontrol_new rt5663_alg_dacl_mux = + SOC_DAPM_ENUM("DAC L Mux", rt5663_alg_dacl_enum); + +/* RT5663: Analog DACR1 input source */ +static const char * const rt5663_alg_dacr_src[] = { + "DAC R", "STO DAC MIXR" +}; + +static SOC_ENUM_SINGLE_DECL(rt5663_alg_dacr_enum, RT5663_BYPASS_STO_DAC, + RT5663_DACR1_SRC_SHIFT, rt5663_alg_dacr_src); + +static const struct snd_kcontrol_new rt5663_alg_dacr_mux = + SOC_DAPM_ENUM("DAC R Mux", rt5663_alg_dacr_enum); + +static int rt5663_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (rt5663->codec_ver == CODEC_VER_1) { + snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1, + RT5663_SEL_PM_HP_SHIFT, RT5663_SEL_PM_HP_HIGH); + snd_soc_component_update_bits(component, RT5663_HP_LOGIC_2, + RT5663_HP_SIG_SRC1_MASK, + RT5663_HP_SIG_SRC1_SILENCE); + } else { + snd_soc_component_update_bits(component, + RT5663_DACREF_LDO, 0x3e0e, 0x3a0a); + snd_soc_component_write(component, RT5663_DEPOP_2, 0x3003); + snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1, + RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_DIS); + snd_soc_component_write(component, RT5663_HP_CHARGE_PUMP_2, 0x1371); + snd_soc_component_write(component, RT5663_HP_BIAS, 0xabba); + snd_soc_component_write(component, RT5663_CHARGE_PUMP_1, 0x2224); + snd_soc_component_write(component, RT5663_ANA_BIAS_CUR_1, 0x7766); + snd_soc_component_write(component, RT5663_HP_BIAS, 0xafaa); + snd_soc_component_write(component, RT5663_CHARGE_PUMP_2, 0x7777); + snd_soc_component_update_bits(component, RT5663_STO_DRE_1, 0x8000, + 0x8000); + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, + 0x3000); + snd_soc_component_update_bits(component, + RT5663_DIG_VOL_ZCD, 0x00c0, 0x0080); + } + break; + + case SND_SOC_DAPM_PRE_PMD: + if (rt5663->codec_ver == CODEC_VER_1) { + snd_soc_component_update_bits(component, RT5663_HP_LOGIC_2, + RT5663_HP_SIG_SRC1_MASK, + RT5663_HP_SIG_SRC1_REG); + } else { + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0x0); + snd_soc_component_update_bits(component, RT5663_HP_CHARGE_PUMP_1, + RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_EN); + snd_soc_component_update_bits(component, + RT5663_DACREF_LDO, 0x3e0e, 0); + snd_soc_component_update_bits(component, + RT5663_DIG_VOL_ZCD, 0x00c0, 0); + } + break; + + default: + return 0; + } + + return 0; +} + +static int rt5663_charge_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (rt5663->codec_ver == CODEC_VER_0) { + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030, + 0x0030); + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003, + 0x0003); + } + break; + + case SND_SOC_DAPM_POST_PMD: + if (rt5663->codec_ver == CODEC_VER_0) { + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0003, 0); + snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x0030, 0); + } + break; + + default: + return 0; + } + + return 0; +} + +static int rt5663_bst2_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2, + RT5663_PWR_BST2_MASK | RT5663_PWR_BST2_OP_MASK, + RT5663_PWR_BST2 | RT5663_PWR_BST2_OP); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_2, + RT5663_PWR_BST2_MASK | RT5663_PWR_BST2_OP_MASK, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5663_pre_div_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(component, RT5663_PRE_DIV_GATING_1, 0xff00); + snd_soc_component_write(component, RT5663_PRE_DIV_GATING_2, 0xfffc); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write(component, RT5663_PRE_DIV_GATING_1, 0x0000); + snd_soc_component_write(component, RT5663_PRE_DIV_GATING_2, 0x0000); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5663_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL", RT5663_PWR_ANLG_3, RT5663_PWR_PLL_SHIFT, 0, + NULL, 0), + + /* micbias */ + SND_SOC_DAPM_MICBIAS("MICBIAS1", RT5663_PWR_ANLG_2, + RT5663_PWR_MB1_SHIFT, 0), + SND_SOC_DAPM_MICBIAS("MICBIAS2", RT5663_PWR_ANLG_2, + RT5663_PWR_MB2_SHIFT, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + + /* REC Mixer Power */ + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX1_SHIFT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC L Power", RT5663_PWR_DIG_1, + RT5663_PWR_ADC_L1_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Clock", RT5663_CHOP_ADC, + RT5663_CKGEN_ADCC_SHIFT, 0, NULL, 0), + + /* ADC Mixer */ + SND_SOC_DAPM_MIXER("STO1 ADC MIXL", SND_SOC_NOPM, + 0, 0, rt5663_sto1_adc_l_mix, + ARRAY_SIZE(rt5663_sto1_adc_l_mix)), + + /* ADC Filter Power */ + SND_SOC_DAPM_SUPPLY("STO1 ADC Filter", RT5663_PWR_DIG_2, + RT5663_PWR_ADC_S1F_SHIFT, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S", RT5663_PWR_DIG_1, RT5663_PWR_I2S1_SHIFT, 0, + NULL, 0), + SND_SOC_DAPM_PGA("IF DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIFRX", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIFTX", "AIF Capture", 0, SND_SOC_NOPM, 0, 0), + + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("ADDA MIXL", SND_SOC_NOPM, 0, 0, rt5663_adda_l_mix, + ARRAY_SIZE(rt5663_adda_l_mix)), + SND_SOC_DAPM_MIXER("ADDA MIXR", SND_SOC_NOPM, 0, 0, rt5663_adda_r_mix, + ARRAY_SIZE(rt5663_adda_r_mix)), + SND_SOC_DAPM_PGA("DAC L1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC R1", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("STO1 DAC Filter", RT5663_PWR_DIG_2, + RT5663_PWR_DAC_S1F_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("STO1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5663_sto1_dac_l_mix, ARRAY_SIZE(rt5663_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("STO1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5663_sto1_dac_r_mix, ARRAY_SIZE(rt5663_sto1_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_SUPPLY("STO1 DAC L Power", RT5663_PWR_DIG_1, + RT5663_PWR_DAC_L1_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("STO1 DAC R Power", RT5663_PWR_DIG_1, + RT5663_PWR_DAC_R1_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R", NULL, SND_SOC_NOPM, 0, 0), + + /* Headphone*/ + SND_SOC_DAPM_SUPPLY("HP Charge Pump", SND_SOC_NOPM, 0, 0, + rt5663_charge_pump_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5663_hp_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_widget rt5663_v2_specific_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5663_PWR_ANLG_3, + RT5663_PWR_LDO2_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5663_PWR_VOL, + RT5663_V2_PWR_MIC_DET_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO DAC", RT5663_PWR_DIG_1, + RT5663_PWR_LDO_DACREF_SHIFT, 0, NULL, 0), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY("I2S ASRC", RT5663_ASRC_1, + RT5663_I2S1_ASRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC ASRC", RT5663_ASRC_1, + RT5663_DAC_STO1_ASRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC ASRC", RT5663_ASRC_1, + RT5663_ADC_STO1_ASRC_SHIFT, 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CBJ Power", RT5663_PWR_ANLG_3, + RT5663_PWR_CBJ_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST2 Power", SND_SOC_NOPM, 0, 0, + rt5663_bst2_power, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5663_recmix1l, + ARRAY_SIZE(rt5663_recmix1l)), + SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5663_recmix1r, + ARRAY_SIZE(rt5663_recmix1r)), + SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX2_SHIFT, 0, NULL, 0), + + /* ADC */ + SND_SOC_DAPM_ADC("ADC R", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("ADC R Power", RT5663_PWR_DIG_1, + RT5663_PWR_ADC_R1_SHIFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_PGA("STO1 ADC L1", RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_L1_SRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("STO1 ADC R1", RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_R1_SRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("STO1 ADC L2", RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_L2_SRC_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_PGA("STO1 ADC R2", RT5663_STO1_ADC_MIXER, + RT5663_STO1_ADC_R2_SRC_SHIFT, 1, NULL, 0), + + SND_SOC_DAPM_MUX("STO1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5663_sto1_adcl_mux), + SND_SOC_DAPM_MUX("STO1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5663_sto1_adcr_mux), + + /* ADC Mix */ + SND_SOC_DAPM_MIXER("STO1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5663_sto1_adc_r_mix, ARRAY_SIZE(rt5663_sto1_adc_r_mix)), + + /* Analog DAC Clock */ + SND_SOC_DAPM_SUPPLY("DAC Clock", RT5663_CHOP_DAC_L, + RT5663_CKGEN_DAC1_SHIFT, 0, NULL, 0), + + /* Headphone out */ + SND_SOC_DAPM_SWITCH("HPO Playback", SND_SOC_NOPM, 0, 0, + &rt5663_hpo_switch), +}; + +static const struct snd_soc_dapm_widget rt5663_specific_dapm_widgets[] = { + /* System Clock Pre Divider Gating */ + SND_SOC_DAPM_SUPPLY("Pre Div Power", SND_SOC_NOPM, 0, 0, + rt5663_pre_div_power, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + + /* LDO */ + SND_SOC_DAPM_SUPPLY("LDO ADC", RT5663_PWR_DIG_1, + RT5663_PWR_LDO_DACREF_SHIFT, 0, NULL, 0), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY("I2S ASRC", RT5663_ASRC_1, + RT5663_I2S1_ASRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC ASRC", RT5663_ASRC_1, + RT5663_DAC_STO1_ASRC_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC ASRC", RT5663_ASRC_1, + RT5663_ADC_STO1_ASRC_SHIFT, 0, NULL, 0), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* STO ADC */ + SND_SOC_DAPM_PGA("STO1 ADC L1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("STO1 ADC L2", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Analog DAC source */ + SND_SOC_DAPM_MUX("DAC L Mux", SND_SOC_NOPM, 0, 0, &rt5663_alg_dacl_mux), + SND_SOC_DAPM_MUX("DAC R Mux", SND_SOC_NOPM, 0, 0, &rt5663_alg_dacr_mux), +}; + +static const struct snd_soc_dapm_route rt5663_dapm_routes[] = { + /* PLL */ + { "I2S", NULL, "PLL", rt5663_is_sys_clk_from_pll }, + + /* ASRC */ + { "STO1 ADC Filter", NULL, "ADC ASRC", rt5663_is_using_asrc }, + { "STO1 DAC Filter", NULL, "DAC ASRC", rt5663_is_using_asrc }, + { "I2S", NULL, "I2S ASRC", rt5663_i2s_use_asrc }, + + { "ADC L", NULL, "ADC L Power" }, + { "ADC L", NULL, "ADC Clock" }, + + { "STO1 ADC L2", NULL, "STO1 DAC MIXL" }, + + { "STO1 ADC MIXL", "ADC1 Switch", "STO1 ADC L1" }, + { "STO1 ADC MIXL", "ADC2 Switch", "STO1 ADC L2" }, + { "STO1 ADC MIXL", NULL, "STO1 ADC Filter" }, + + { "IF1 ADC1", NULL, "STO1 ADC MIXL" }, + { "IF ADC", NULL, "IF1 ADC1" }, + { "AIFTX", NULL, "IF ADC" }, + { "AIFTX", NULL, "I2S" }, + + { "AIFRX", NULL, "I2S" }, + { "IF DAC", NULL, "AIFRX" }, + { "IF1 DAC1 L", NULL, "IF DAC" }, + { "IF1 DAC1 R", NULL, "IF DAC" }, + + { "ADDA MIXL", "ADC L Switch", "STO1 ADC MIXL" }, + { "ADDA MIXL", "DAC L Switch", "IF1 DAC1 L" }, + { "ADDA MIXL", NULL, "STO1 DAC Filter" }, + { "ADDA MIXL", NULL, "STO1 DAC L Power" }, + { "ADDA MIXR", "DAC R Switch", "IF1 DAC1 R" }, + { "ADDA MIXR", NULL, "STO1 DAC Filter" }, + { "ADDA MIXR", NULL, "STO1 DAC R Power" }, + + { "DAC L1", NULL, "ADDA MIXL" }, + { "DAC R1", NULL, "ADDA MIXR" }, + + { "STO1 DAC MIXL", "DAC L Switch", "DAC L1" }, + { "STO1 DAC MIXL", NULL, "STO1 DAC L Power" }, + { "STO1 DAC MIXL", NULL, "STO1 DAC Filter" }, + { "STO1 DAC MIXR", "DAC R Switch", "DAC R1" }, + { "STO1 DAC MIXR", NULL, "STO1 DAC R Power" }, + { "STO1 DAC MIXR", NULL, "STO1 DAC Filter" }, + + { "HP Amp", NULL, "HP Charge Pump" }, + { "HP Amp", NULL, "DAC L" }, + { "HP Amp", NULL, "DAC R" }, +}; + +static const struct snd_soc_dapm_route rt5663_v2_specific_dapm_routes[] = { + { "MICBIAS1", NULL, "LDO2" }, + { "MICBIAS2", NULL, "LDO2" }, + + { "BST1 CBJ", NULL, "IN1P" }, + { "BST1 CBJ", NULL, "IN1N" }, + { "BST1 CBJ", NULL, "CBJ Power" }, + + { "BST2", NULL, "IN2P" }, + { "BST2", NULL, "IN2N" }, + { "BST2", NULL, "BST2 Power" }, + + { "RECMIX1L", "BST2 Switch", "BST2" }, + { "RECMIX1L", "BST1 CBJ Switch", "BST1 CBJ" }, + { "RECMIX1L", NULL, "RECMIX1L Power" }, + { "RECMIX1R", "BST2 Switch", "BST2" }, + { "RECMIX1R", NULL, "RECMIX1R Power" }, + + { "ADC L", NULL, "RECMIX1L" }, + { "ADC R", NULL, "RECMIX1R" }, + { "ADC R", NULL, "ADC R Power" }, + { "ADC R", NULL, "ADC Clock" }, + + { "STO1 ADC L Mux", "ADC L", "ADC L" }, + { "STO1 ADC L Mux", "ADC R", "ADC R" }, + { "STO1 ADC L1", NULL, "STO1 ADC L Mux" }, + + { "STO1 ADC R Mux", "ADC L", "ADC L" }, + { "STO1 ADC R Mux", "ADC R", "ADC R" }, + { "STO1 ADC R1", NULL, "STO1 ADC R Mux" }, + { "STO1 ADC R2", NULL, "STO1 DAC MIXR" }, + + { "STO1 ADC MIXR", "ADC1 Switch", "STO1 ADC R1" }, + { "STO1 ADC MIXR", "ADC2 Switch", "STO1 ADC R2" }, + { "STO1 ADC MIXR", NULL, "STO1 ADC Filter" }, + + { "IF1 ADC1", NULL, "STO1 ADC MIXR" }, + + { "ADDA MIXR", "ADC R Switch", "STO1 ADC MIXR" }, + + { "DAC L", NULL, "STO1 DAC MIXL" }, + { "DAC L", NULL, "LDO DAC" }, + { "DAC L", NULL, "DAC Clock" }, + { "DAC R", NULL, "STO1 DAC MIXR" }, + { "DAC R", NULL, "LDO DAC" }, + { "DAC R", NULL, "DAC Clock" }, + + { "HPO Playback", "Switch", "HP Amp" }, + { "HPOL", NULL, "HPO Playback" }, + { "HPOR", NULL, "HPO Playback" }, +}; + +static const struct snd_soc_dapm_route rt5663_specific_dapm_routes[] = { + { "I2S", NULL, "Pre Div Power" }, + + { "BST1", NULL, "IN1P" }, + { "BST1", NULL, "IN1N" }, + { "BST1", NULL, "RECMIX1L Power" }, + + { "ADC L", NULL, "BST1" }, + + { "STO1 ADC L1", NULL, "ADC L" }, + + { "DAC L Mux", "DAC L", "DAC L1" }, + { "DAC L Mux", "STO DAC MIXL", "STO1 DAC MIXL" }, + { "DAC R Mux", "DAC R", "DAC R1"}, + { "DAC R Mux", "STO DAC MIXR", "STO1 DAC MIXR" }, + + { "DAC L", NULL, "DAC L Mux" }, + { "DAC R", NULL, "DAC R Mux" }, + + { "HPOL", NULL, "HP Amp" }, + { "HPOR", NULL, "HP Amp" }, +}; + +static int rt5663_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0; + int pre_div; + + rt5663->lrck = params_rate(params); + + dev_dbg(dai->dev, "bclk is %dHz and sysclk is %dHz\n", + rt5663->lrck, rt5663->sysclk); + + pre_div = rl6231_get_clk_info(rt5663->sysclk, rt5663->lrck); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n", + rt5663->lrck, dai->id); + return -EINVAL; + } + + dev_dbg(dai->dev, "pre_div is %d for iis %d\n", pre_div, dai->id); + + switch (params_width(params)) { + case 8: + val_len = RT5663_I2S_DL_8; + break; + case 16: + val_len = RT5663_I2S_DL_16; + break; + case 20: + val_len = RT5663_I2S_DL_20; + break; + case 24: + val_len = RT5663_I2S_DL_24; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5663_I2S1_SDP, + RT5663_I2S_DL_MASK, val_len); + + snd_soc_component_update_bits(component, RT5663_ADDA_CLK_1, + RT5663_I2S_PD1_MASK, pre_div << RT5663_I2S_PD1_SHIFT); + + return 0; +} + +static int rt5663_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5663_I2S_MS_S; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5663_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5663_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5663_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5663_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5663_I2S1_SDP, RT5663_I2S_MS_MASK | + RT5663_I2S_BP_MASK | RT5663_I2S_DF_MASK, reg_val); + + return 0; +} + +static int rt5663_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt5663->sysclk && clk_id == rt5663->sysclk_src) + return 0; + + switch (clk_id) { + case RT5663_SCLK_S_MCLK: + reg_val |= RT5663_SCLK_SRC_MCLK; + break; + case RT5663_SCLK_S_PLL1: + reg_val |= RT5663_SCLK_SRC_PLL1; + break; + case RT5663_SCLK_S_RCCLK: + reg_val |= RT5663_SCLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK, + reg_val); + rt5663->sysclk = freq; + rt5663->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt5663_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + int mask, shift, val; + + if (source == rt5663->pll_src && freq_in == rt5663->pll_in && + freq_out == rt5663->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5663->pll_in = 0; + rt5663->pll_out = 0; + snd_soc_component_update_bits(component, RT5663_GLB_CLK, + RT5663_SCLK_SRC_MASK, RT5663_SCLK_SRC_MCLK); + return 0; + } + + switch (rt5663->codec_ver) { + case CODEC_VER_1: + mask = RT5663_V2_PLL1_SRC_MASK; + shift = RT5663_V2_PLL1_SRC_SHIFT; + break; + case CODEC_VER_0: + mask = RT5663_PLL1_SRC_MASK; + shift = RT5663_PLL1_SRC_SHIFT; + break; + default: + dev_err(component->dev, "Unknown CODEC Version\n"); + return -EINVAL; + } + + switch (source) { + case RT5663_PLL1_S_MCLK: + val = 0x0; + break; + case RT5663_PLL1_S_BCLK1: + val = 0x1; + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5663_GLB_CLK, mask, (val << shift)); + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", pll_code.m_bp, + (pll_code.m_bp ? 0 : pll_code.m_code), pll_code.n_code, + pll_code.k_code); + + snd_soc_component_write(component, RT5663_PLL_1, + pll_code.n_code << RT5663_PLL_N_SHIFT | pll_code.k_code); + snd_soc_component_write(component, RT5663_PLL_2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5663_PLL_M_SHIFT) | + (pll_code.m_bp << RT5663_PLL_M_BP_SHIFT)); + + rt5663->pll_in = freq_in; + rt5663->pll_out = freq_out; + rt5663->pll_src = source; + + return 0; +} + +static int rt5663_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, reg; + + if (rx_mask || tx_mask) + val |= RT5663_TDM_MODE_TDM; + + switch (slots) { + case 4: + val |= RT5663_TDM_IN_CH_4; + val |= RT5663_TDM_OUT_CH_4; + break; + case 6: + val |= RT5663_TDM_IN_CH_6; + val |= RT5663_TDM_OUT_CH_6; + break; + case 8: + val |= RT5663_TDM_IN_CH_8; + val |= RT5663_TDM_OUT_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + val |= RT5663_TDM_IN_LEN_20; + val |= RT5663_TDM_OUT_LEN_20; + break; + case 24: + val |= RT5663_TDM_IN_LEN_24; + val |= RT5663_TDM_OUT_LEN_24; + break; + case 32: + val |= RT5663_TDM_IN_LEN_32; + val |= RT5663_TDM_OUT_LEN_32; + break; + case 16: + break; + default: + return -EINVAL; + } + + switch (rt5663->codec_ver) { + case CODEC_VER_1: + reg = RT5663_TDM_2; + break; + case CODEC_VER_0: + reg = RT5663_TDM_1; + break; + default: + dev_err(component->dev, "Unknown CODEC Version\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, reg, RT5663_TDM_MODE_MASK | + RT5663_TDM_IN_CH_MASK | RT5663_TDM_OUT_CH_MASK | + RT5663_TDM_IN_LEN_MASK | RT5663_TDM_OUT_LEN_MASK, val); + + return 0; +} + +static int rt5663_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + unsigned int reg; + + dev_dbg(component->dev, "%s ratio = %d\n", __func__, ratio); + + if (rt5663->codec_ver == CODEC_VER_1) + reg = RT5663_TDM_9; + else + reg = RT5663_TDM_5; + + switch (ratio) { + case 32: + snd_soc_component_update_bits(component, reg, + RT5663_TDM_LENGTN_MASK, + RT5663_TDM_LENGTN_16); + break; + case 40: + snd_soc_component_update_bits(component, reg, + RT5663_TDM_LENGTN_MASK, + RT5663_TDM_LENGTN_20); + break; + case 48: + snd_soc_component_update_bits(component, reg, + RT5663_TDM_LENGTN_MASK, + RT5663_TDM_LENGTN_24); + break; + case 64: + snd_soc_component_update_bits(component, reg, + RT5663_TDM_LENGTN_MASK, + RT5663_TDM_LENGTN_32); + break; + default: + dev_err(component->dev, "Invalid ratio!\n"); + return -EINVAL; + } + + return 0; +} + +static int rt5663_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_FV1 | RT5663_PWR_FV2); + break; + + case SND_SOC_BIAS_PREPARE: + if (rt5663->codec_ver == CODEC_VER_1) { + snd_soc_component_update_bits(component, RT5663_DIG_MISC, + RT5663_DIG_GATE_CTRL_MASK, + RT5663_DIG_GATE_CTRL_EN); + snd_soc_component_update_bits(component, RT5663_SIG_CLK_DET, + RT5663_EN_ANA_CLK_DET_MASK | + RT5663_PWR_CLK_DET_MASK, + RT5663_EN_ANA_CLK_DET_AUTO | + RT5663_PWR_CLK_DET_EN); + } + break; + + case SND_SOC_BIAS_STANDBY: + if (rt5663->codec_ver == CODEC_VER_1) + snd_soc_component_update_bits(component, RT5663_DIG_MISC, + RT5663_DIG_GATE_CTRL_MASK, + RT5663_DIG_GATE_CTRL_DIS); + snd_soc_component_update_bits(component, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK | + RT5663_PWR_MB_MASK, RT5663_PWR_VREF1 | + RT5663_PWR_VREF2 | RT5663_PWR_MB); + usleep_range(10000, 10005); + if (rt5663->codec_ver == CODEC_VER_1) { + snd_soc_component_update_bits(component, RT5663_SIG_CLK_DET, + RT5663_EN_ANA_CLK_DET_MASK | + RT5663_PWR_CLK_DET_MASK, + RT5663_EN_ANA_CLK_DET_DIS | + RT5663_PWR_CLK_DET_DIS); + } + break; + + case SND_SOC_BIAS_OFF: + if (rt5663->jack_type != SND_JACK_HEADSET) + snd_soc_component_update_bits(component, + RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1 | RT5663_PWR_FV2 | + RT5663_PWR_MB_MASK, 0); + else + snd_soc_component_update_bits(component, + RT5663_PWR_ANLG_1, + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_FV1 | RT5663_PWR_FV2); + break; + + default: + break; + } + + return 0; +} + +static int rt5663_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + + rt5663->component = component; + + switch (rt5663->codec_ver) { + case CODEC_VER_1: + snd_soc_dapm_new_controls(dapm, + rt5663_v2_specific_dapm_widgets, + ARRAY_SIZE(rt5663_v2_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt5663_v2_specific_dapm_routes, + ARRAY_SIZE(rt5663_v2_specific_dapm_routes)); + snd_soc_add_component_controls(component, rt5663_v2_specific_controls, + ARRAY_SIZE(rt5663_v2_specific_controls)); + break; + case CODEC_VER_0: + snd_soc_dapm_new_controls(dapm, + rt5663_specific_dapm_widgets, + ARRAY_SIZE(rt5663_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt5663_specific_dapm_routes, + ARRAY_SIZE(rt5663_specific_dapm_routes)); + snd_soc_add_component_controls(component, rt5663_specific_controls, + ARRAY_SIZE(rt5663_specific_controls)); + + if (!rt5663->imp_table) + snd_soc_add_component_controls(component, rt5663_hpvol_controls, + ARRAY_SIZE(rt5663_hpvol_controls)); + break; + } + + return 0; +} + +static void rt5663_remove(struct snd_soc_component *component) +{ + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + + regmap_write(rt5663->regmap, RT5663_RESET, 0); +} + +#ifdef CONFIG_PM +static int rt5663_suspend(struct snd_soc_component *component) +{ + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5663->regmap, true); + regcache_mark_dirty(rt5663->regmap); + + return 0; +} + +static int rt5663_resume(struct snd_soc_component *component) +{ + struct rt5663_priv *rt5663 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5663->regmap, false); + regcache_sync(rt5663->regmap); + + rt5663_irq(0, rt5663); + + return 0; +} +#else +#define rt5663_suspend NULL +#define rt5663_resume NULL +#endif + +#define RT5663_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5663_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5663_aif_dai_ops = { + .hw_params = rt5663_hw_params, + .set_fmt = rt5663_set_dai_fmt, + .set_sysclk = rt5663_set_dai_sysclk, + .set_pll = rt5663_set_dai_pll, + .set_tdm_slot = rt5663_set_tdm_slot, + .set_bclk_ratio = rt5663_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5663_dai[] = { + { + .name = "rt5663-aif", + .id = RT5663_AIF, + .playback = { + .stream_name = "AIF Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5663_STEREO_RATES, + .formats = RT5663_FORMATS, + }, + .capture = { + .stream_name = "AIF Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5663_STEREO_RATES, + .formats = RT5663_FORMATS, + }, + .ops = &rt5663_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5663 = { + .probe = rt5663_probe, + .remove = rt5663_remove, + .suspend = rt5663_suspend, + .resume = rt5663_resume, + .set_bias_level = rt5663_set_bias_level, + .controls = rt5663_snd_controls, + .num_controls = ARRAY_SIZE(rt5663_snd_controls), + .dapm_widgets = rt5663_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5663_dapm_widgets), + .dapm_routes = rt5663_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5663_dapm_routes), + .set_jack = rt5663_set_jack_detect, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5663_v2_regmap = { + .reg_bits = 16, + .val_bits = 16, + .use_single_read = true, + .use_single_write = true, + .max_register = 0x07fa, + .volatile_reg = rt5663_v2_volatile_register, + .readable_reg = rt5663_v2_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5663_v2_reg, + .num_reg_defaults = ARRAY_SIZE(rt5663_v2_reg), +}; + +static const struct regmap_config rt5663_regmap = { + .reg_bits = 16, + .val_bits = 16, + .use_single_read = true, + .use_single_write = true, + .max_register = 0x03f3, + .volatile_reg = rt5663_volatile_register, + .readable_reg = rt5663_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5663_reg, + .num_reg_defaults = ARRAY_SIZE(rt5663_reg), +}; + +static const struct regmap_config temp_regmap = { + .name = "nocache", + .reg_bits = 16, + .val_bits = 16, + .use_single_read = true, + .use_single_write = true, + .max_register = 0x03f3, + .cache_type = REGCACHE_NONE, +}; + +static const struct i2c_device_id rt5663_i2c_id[] = { + { "rt5663", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5663_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rt5663_of_match[] = { + { .compatible = "realtek,rt5663", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5663_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5663_acpi_match[] = { + { "10EC5663", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5663_acpi_match); +#endif + +static void rt5663_v2_calibrate(struct rt5663_priv *rt5663) +{ + regmap_write(rt5663->regmap, RT5663_BIAS_CUR_8, 0xa402); + regmap_write(rt5663->regmap, RT5663_PWR_DIG_1, 0x0100); + regmap_write(rt5663->regmap, RT5663_RECMIX, 0x4040); + regmap_write(rt5663->regmap, RT5663_DIG_MISC, 0x0001); + regmap_write(rt5663->regmap, RT5663_RC_CLK, 0x0380); + regmap_write(rt5663->regmap, RT5663_GLB_CLK, 0x8000); + regmap_write(rt5663->regmap, RT5663_ADDA_CLK_1, 0x1000); + regmap_write(rt5663->regmap, RT5663_CHOP_DAC_L, 0x3030); + regmap_write(rt5663->regmap, RT5663_CALIB_ADC, 0x3c05); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa23e); + msleep(40); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf23e); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_2, 0x0321); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_1, 0xfc00); + msleep(500); +} + +static void rt5663_calibrate(struct rt5663_priv *rt5663) +{ + int value, count; + + regmap_write(rt5663->regmap, RT5663_RESET, 0x0000); + msleep(20); + regmap_write(rt5663->regmap, RT5663_ANA_BIAS_CUR_4, 0x00a1); + regmap_write(rt5663->regmap, RT5663_RC_CLK, 0x0380); + regmap_write(rt5663->regmap, RT5663_GLB_CLK, 0x8000); + regmap_write(rt5663->regmap, RT5663_ADDA_CLK_1, 0x1000); + regmap_write(rt5663->regmap, RT5663_VREF_RECMIX, 0x0032); + regmap_write(rt5663->regmap, RT5663_HP_IMP_SEN_19, 0x000c); + regmap_write(rt5663->regmap, RT5663_DUMMY_1, 0x0324); + regmap_write(rt5663->regmap, RT5663_DIG_MISC, 0x8001); + regmap_write(rt5663->regmap, RT5663_VREFADJ_OP, 0x0f28); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa23b); + msleep(30); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf23b); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_2, 0x8000); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_3, 0x0008); + regmap_write(rt5663->regmap, RT5663_PRE_DIV_GATING_1, 0xffff); + regmap_write(rt5663->regmap, RT5663_PRE_DIV_GATING_2, 0xffff); + regmap_write(rt5663->regmap, RT5663_CBJ_1, 0x8c10); + regmap_write(rt5663->regmap, RT5663_IL_CMD_2, 0x00c1); + regmap_write(rt5663->regmap, RT5663_EM_JACK_TYPE_1, 0xb880); + regmap_write(rt5663->regmap, RT5663_EM_JACK_TYPE_2, 0x4110); + regmap_write(rt5663->regmap, RT5663_EM_JACK_TYPE_2, 0x4118); + + count = 0; + while (true) { + regmap_read(rt5663->regmap, RT5663_INT_ST_2, &value); + if (!(value & 0x80)) + usleep_range(10000, 10005); + else + break; + + if (++count > 200) + break; + } + + regmap_write(rt5663->regmap, RT5663_HP_IMP_SEN_19, 0x0000); + regmap_write(rt5663->regmap, RT5663_DEPOP_2, 0x3003); + regmap_write(rt5663->regmap, RT5663_DEPOP_1, 0x0038); + regmap_write(rt5663->regmap, RT5663_DEPOP_1, 0x003b); + regmap_write(rt5663->regmap, RT5663_PWR_DIG_2, 0x8400); + regmap_write(rt5663->regmap, RT5663_PWR_DIG_1, 0x8df8); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_2, 0x8003); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_3, 0x018c); + regmap_write(rt5663->regmap, RT5663_HP_CHARGE_PUMP_1, 0x1e32); + regmap_write(rt5663->regmap, RT5663_DUMMY_2, 0x8089); + regmap_write(rt5663->regmap, RT5663_DACREF_LDO, 0x3b0b); + msleep(40); + regmap_write(rt5663->regmap, RT5663_STO_DAC_MIXER, 0x0000); + regmap_write(rt5663->regmap, RT5663_BYPASS_STO_DAC, 0x000c); + regmap_write(rt5663->regmap, RT5663_HP_BIAS, 0xafaa); + regmap_write(rt5663->regmap, RT5663_CHARGE_PUMP_1, 0x2224); + regmap_write(rt5663->regmap, RT5663_HP_OUT_EN, 0x8088); + regmap_write(rt5663->regmap, RT5663_STO_DRE_9, 0x0017); + regmap_write(rt5663->regmap, RT5663_STO_DRE_10, 0x0017); + regmap_write(rt5663->regmap, RT5663_STO1_ADC_MIXER, 0x4040); + regmap_write(rt5663->regmap, RT5663_CHOP_ADC, 0x3000); + regmap_write(rt5663->regmap, RT5663_RECMIX, 0x0005); + regmap_write(rt5663->regmap, RT5663_ADDA_RST, 0xc000); + regmap_write(rt5663->regmap, RT5663_STO1_HPF_ADJ1, 0x3320); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_2, 0x00c9); + regmap_write(rt5663->regmap, RT5663_DUMMY_1, 0x004c); + regmap_write(rt5663->regmap, RT5663_ANA_BIAS_CUR_1, 0x1111); + regmap_write(rt5663->regmap, RT5663_BIAS_CUR_8, 0x4402); + regmap_write(rt5663->regmap, RT5663_CHARGE_PUMP_2, 0x3311); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_1, 0x0069); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_3, 0x06ce); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_1_1, 0x6800); + regmap_write(rt5663->regmap, RT5663_CHARGE_PUMP_2, 0x1100); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_7, 0x0057); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_1_1, 0xe800); + + count = 0; + while (true) { + regmap_read(rt5663->regmap, RT5663_HP_CALIB_1_1, &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 200) + return; + count++; + } + + regmap_write(rt5663->regmap, RT5663_HP_CALIB_1_1, 0x6200); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_7, 0x0059); + regmap_write(rt5663->regmap, RT5663_HP_CALIB_1_1, 0xe200); + + count = 0; + while (true) { + regmap_read(rt5663->regmap, RT5663_HP_CALIB_1_1, &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 200) + return; + count++; + } + + regmap_write(rt5663->regmap, RT5663_EM_JACK_TYPE_1, 0xb8e0); + usleep_range(10000, 10005); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0x003b); + usleep_range(10000, 10005); + regmap_write(rt5663->regmap, RT5663_PWR_DIG_1, 0x0000); + usleep_range(10000, 10005); + regmap_write(rt5663->regmap, RT5663_DEPOP_1, 0x000b); + usleep_range(10000, 10005); + regmap_write(rt5663->regmap, RT5663_DEPOP_1, 0x0008); + usleep_range(10000, 10005); + regmap_write(rt5663->regmap, RT5663_PWR_ANLG_2, 0x0000); + usleep_range(10000, 10005); +} + +static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) +{ + int table_size; + int ret; + + device_property_read_u32(dev, "realtek,dc_offset_l_manual", + &rt5663->pdata.dc_offset_l_manual); + device_property_read_u32(dev, "realtek,dc_offset_r_manual", + &rt5663->pdata.dc_offset_r_manual); + device_property_read_u32(dev, "realtek,dc_offset_l_manual_mic", + &rt5663->pdata.dc_offset_l_manual_mic); + device_property_read_u32(dev, "realtek,dc_offset_r_manual_mic", + &rt5663->pdata.dc_offset_r_manual_mic); + device_property_read_u32(dev, "realtek,impedance_sensing_num", + &rt5663->pdata.impedance_sensing_num); + + if (rt5663->pdata.impedance_sensing_num) { + table_size = sizeof(struct impedance_mapping_table) * + rt5663->pdata.impedance_sensing_num; + rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL); + if (!rt5663->imp_table) + return -ENOMEM; + ret = device_property_read_u32_array(dev, + "realtek,impedance_sensing_table", + (u32 *)rt5663->imp_table, table_size); + if (ret) + return ret; + } + + return 0; +} + +static int rt5663_i2c_probe(struct i2c_client *i2c) +{ + struct rt5663_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5663_priv *rt5663; + int ret, i; + unsigned int val; + struct regmap *regmap; + + rt5663 = devm_kzalloc(&i2c->dev, sizeof(struct rt5663_priv), + GFP_KERNEL); + + if (rt5663 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5663); + + if (pdata) + rt5663->pdata = *pdata; + else { + ret = rt5663_parse_dp(rt5663, &i2c->dev); + if (ret) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) + rt5663->supplies[i].supply = rt5663_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, + ARRAY_SIZE(rt5663->supplies), + rt5663->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + /* Set load for regulator. */ + for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) { + ret = regulator_set_load(rt5663->supplies[i].consumer, + RT5663_SUPPLY_CURRENT_UA); + if (ret < 0) { + dev_err(&i2c->dev, + "Failed to set regulator load on %s, ret: %d\n", + rt5663->supplies[i].supply, ret); + return ret; + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5663->supplies), + rt5663->supplies); + + if (ret) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + msleep(RT5663_POWER_ON_DELAY_MS); + + regmap = devm_regmap_init_i2c(i2c, &temp_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n", + ret); + goto err_enable; + } + + ret = regmap_read(regmap, RT5663_VENDOR_ID_2, &val); + if (ret || (val != RT5663_DEVICE_ID_2 && val != RT5663_DEVICE_ID_1)) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5663, retry one time.\n", + val); + msleep(100); + regmap_read(regmap, RT5663_VENDOR_ID_2, &val); + } + + switch (val) { + case RT5663_DEVICE_ID_2: + rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5663_v2_regmap); + rt5663->codec_ver = CODEC_VER_1; + break; + case RT5663_DEVICE_ID_1: + rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5663_regmap); + rt5663->codec_ver = CODEC_VER_0; + break; + default: + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5663\n", + val); + ret = -ENODEV; + goto err_enable; + } + + if (IS_ERR(rt5663->regmap)) { + ret = PTR_ERR(rt5663->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + goto err_enable; + } + + /* reset and calibrate */ + regmap_write(rt5663->regmap, RT5663_RESET, 0); + regcache_cache_bypass(rt5663->regmap, true); + switch (rt5663->codec_ver) { + case CODEC_VER_1: + rt5663_v2_calibrate(rt5663); + break; + case CODEC_VER_0: + rt5663_calibrate(rt5663); + break; + default: + dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__); + } + regcache_cache_bypass(rt5663->regmap, false); + regmap_write(rt5663->regmap, RT5663_RESET, 0); + dev_dbg(&i2c->dev, "calibrate done\n"); + + switch (rt5663->codec_ver) { + case CODEC_VER_1: + break; + case CODEC_VER_0: + ret = regmap_register_patch(rt5663->regmap, rt5663_patch_list, + ARRAY_SIZE(rt5663_patch_list)); + if (ret != 0) + dev_warn(&i2c->dev, + "Failed to apply regmap patch: %d\n", ret); + break; + default: + dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__); + } + + /* GPIO1 as IRQ */ + regmap_update_bits(rt5663->regmap, RT5663_GPIO_1, RT5663_GP1_PIN_MASK, + RT5663_GP1_PIN_IRQ); + /* 4btn inline command debounce */ + regmap_update_bits(rt5663->regmap, RT5663_IL_CMD_5, + RT5663_4BTN_CLK_DEB_MASK, RT5663_4BTN_CLK_DEB_65MS); + + switch (rt5663->codec_ver) { + case CODEC_VER_1: + regmap_write(rt5663->regmap, RT5663_BIAS_CUR_8, 0xa402); + /* JD1 */ + regmap_update_bits(rt5663->regmap, RT5663_AUTO_1MRC_CLK, + RT5663_IRQ_POW_SAV_MASK | RT5663_IRQ_POW_SAV_JD1_MASK, + RT5663_IRQ_POW_SAV_EN | RT5663_IRQ_POW_SAV_JD1_EN); + regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_2, + RT5663_PWR_JD1_MASK, RT5663_PWR_JD1); + regmap_update_bits(rt5663->regmap, RT5663_IRQ_1, + RT5663_EN_CB_JD_MASK, RT5663_EN_CB_JD_EN); + + regmap_update_bits(rt5663->regmap, RT5663_HP_LOGIC_2, + RT5663_HP_SIG_SRC1_MASK, RT5663_HP_SIG_SRC1_REG); + regmap_update_bits(rt5663->regmap, RT5663_RECMIX, + RT5663_VREF_BIAS_MASK | RT5663_CBJ_DET_MASK | + RT5663_DET_TYPE_MASK, RT5663_VREF_BIAS_REG | + RT5663_CBJ_DET_EN | RT5663_DET_TYPE_QFN); + /* Set GPIO4 and GPIO8 as input for combo jack */ + regmap_update_bits(rt5663->regmap, RT5663_GPIO_2, + RT5663_GP4_PIN_CONF_MASK, RT5663_GP4_PIN_CONF_INPUT); + regmap_update_bits(rt5663->regmap, RT5663_GPIO_3, + RT5663_GP8_PIN_CONF_MASK, RT5663_GP8_PIN_CONF_INPUT); + regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_1, + RT5663_LDO1_DVO_MASK | RT5663_AMP_HP_MASK, + RT5663_LDO1_DVO_0_9V | RT5663_AMP_HP_3X); + break; + case CODEC_VER_0: + regmap_update_bits(rt5663->regmap, RT5663_DIG_MISC, + RT5663_DIG_GATE_CTRL_MASK, RT5663_DIG_GATE_CTRL_EN); + regmap_update_bits(rt5663->regmap, RT5663_AUTO_1MRC_CLK, + RT5663_IRQ_MANUAL_MASK, RT5663_IRQ_MANUAL_EN); + regmap_update_bits(rt5663->regmap, RT5663_IRQ_1, + RT5663_EN_IRQ_JD1_MASK, RT5663_EN_IRQ_JD1_EN); + regmap_update_bits(rt5663->regmap, RT5663_GPIO_1, + RT5663_GPIO1_TYPE_MASK, RT5663_GPIO1_TYPE_EN); + regmap_write(rt5663->regmap, RT5663_VREF_RECMIX, 0x0032); + regmap_update_bits(rt5663->regmap, RT5663_GPIO_2, + RT5663_GP1_PIN_CONF_MASK | RT5663_SEL_GPIO1_MASK, + RT5663_GP1_PIN_CONF_OUTPUT | RT5663_SEL_GPIO1_EN); + regmap_update_bits(rt5663->regmap, RT5663_RECMIX, + RT5663_RECMIX1_BST1_MASK, RT5663_RECMIX1_BST1_ON); + regmap_update_bits(rt5663->regmap, RT5663_TDM_2, + RT5663_DATA_SWAP_ADCDAT1_MASK, + RT5663_DATA_SWAP_ADCDAT1_LL); + break; + default: + dev_err(&i2c->dev, "%s:Unknown codec type\n", __func__); + } + + INIT_DELAYED_WORK(&rt5663->jack_detect_work, rt5663_jack_detect_work); + INIT_DELAYED_WORK(&rt5663->jd_unplug_work, rt5663_jd_unplug_work); + + if (i2c->irq) { + ret = request_irq(i2c->irq, rt5663_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5663", rt5663); + if (ret) { + dev_err(&i2c->dev, "%s Failed to reguest IRQ: %d\n", + __func__, ret); + goto err_enable; + } + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5663, + rt5663_dai, ARRAY_SIZE(rt5663_dai)); + + if (ret) + goto err_enable; + + return 0; + + + /* + * Error after enabling regulators should goto err_enable + * to disable regulators. + */ +err_enable: + if (i2c->irq) + free_irq(i2c->irq, rt5663); + + regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies); + return ret; +} + +static void rt5663_i2c_remove(struct i2c_client *i2c) +{ + struct rt5663_priv *rt5663 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, rt5663); + + regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies); +} + +static void rt5663_i2c_shutdown(struct i2c_client *client) +{ + struct rt5663_priv *rt5663 = i2c_get_clientdata(client); + + regmap_write(rt5663->regmap, RT5663_RESET, 0); +} + +static struct i2c_driver rt5663_i2c_driver = { + .driver = { + .name = "rt5663", + .acpi_match_table = ACPI_PTR(rt5663_acpi_match), + .of_match_table = of_match_ptr(rt5663_of_match), + }, + .probe = rt5663_i2c_probe, + .remove = rt5663_i2c_remove, + .shutdown = rt5663_i2c_shutdown, + .id_table = rt5663_i2c_id, +}; +module_i2c_driver(rt5663_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5663 driver"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5663.h b/sound/soc/codecs/rt5663.h new file mode 100644 index 0000000000..2c485d0655 --- /dev/null +++ b/sound/soc/codecs/rt5663.h @@ -0,0 +1,1128 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5663.h -- RT5663 ALSA SoC audio driver + * + * Copyright 2016 Realtek Microelectronics + * Author: Jack Yu + */ + +#ifndef __RT5663_H__ +#define __RT5663_H__ + +#include + +/* Info */ +#define RT5663_RESET 0x0000 +#define RT5663_VENDOR_ID 0x00fd +#define RT5663_VENDOR_ID_1 0x00fe +#define RT5663_VENDOR_ID_2 0x00ff + +#define RT5663_LOUT_CTRL 0x0001 +#define RT5663_HP_AMP_2 0x0003 +#define RT5663_MONO_OUT 0x0004 +#define RT5663_MONO_GAIN 0x0007 + +#define RT5663_AEC_BST 0x000b +#define RT5663_IN1_IN2 0x000c +#define RT5663_IN3_IN4 0x000d +#define RT5663_INL1_INR1 0x000f +#define RT5663_CBJ_TYPE_2 0x0011 +#define RT5663_CBJ_TYPE_3 0x0012 +#define RT5663_CBJ_TYPE_4 0x0013 +#define RT5663_CBJ_TYPE_5 0x0014 +#define RT5663_CBJ_TYPE_8 0x0017 + +/* I/O - ADC/DAC/DMIC */ +#define RT5663_DAC3_DIG_VOL 0x001a +#define RT5663_DAC3_CTRL 0x001b +#define RT5663_MONO_ADC_DIG_VOL 0x001d +#define RT5663_STO2_ADC_DIG_VOL 0x001e +#define RT5663_MONO_ADC_BST_GAIN 0x0020 +#define RT5663_STO2_ADC_BST_GAIN 0x0021 +#define RT5663_SIDETONE_CTRL 0x0024 +/* Mixer - D-D */ +#define RT5663_MONO1_ADC_MIXER 0x0027 +#define RT5663_STO2_ADC_MIXER 0x0028 +#define RT5663_MONO_DAC_MIXER 0x002b +#define RT5663_DAC2_SRC_CTRL 0x002e +#define RT5663_IF_3_4_DATA_CTL 0x002f +#define RT5663_IF_5_DATA_CTL 0x0030 +#define RT5663_PDM_OUT_CTL 0x0031 +#define RT5663_PDM_I2C_DATA_CTL1 0x0032 +#define RT5663_PDM_I2C_DATA_CTL2 0x0033 +#define RT5663_PDM_I2C_DATA_CTL3 0x0034 +#define RT5663_PDM_I2C_DATA_CTL4 0x0035 + +/*Mixer - Analog*/ +#define RT5663_RECMIX1_NEW 0x003a +#define RT5663_RECMIX1L_0 0x003b +#define RT5663_RECMIX1L 0x003c +#define RT5663_RECMIX1R_0 0x003d +#define RT5663_RECMIX1R 0x003e +#define RT5663_RECMIX2_NEW 0x003f +#define RT5663_RECMIX2_L_2 0x0041 +#define RT5663_RECMIX2_R 0x0042 +#define RT5663_RECMIX2_R_2 0x0043 +#define RT5663_CALIB_REC_LR 0x0044 +#define RT5663_ALC_BK_GAIN 0x0049 +#define RT5663_MONOMIX_GAIN 0x004a +#define RT5663_MONOMIX_IN_GAIN 0x004b +#define RT5663_OUT_MIXL_GAIN 0x004d +#define RT5663_OUT_LMIX_IN_GAIN 0x004e +#define RT5663_OUT_RMIX_IN_GAIN 0x004f +#define RT5663_OUT_RMIX_IN_GAIN1 0x0050 +#define RT5663_LOUT_MIXER_CTRL 0x0052 +/* Power */ +#define RT5663_PWR_VOL 0x0067 + +#define RT5663_ADCDAC_RST 0x006d +/* Format - ADC/DAC */ +#define RT5663_I2S34_SDP 0x0071 +#define RT5663_I2S5_SDP 0x0072 + +/* Function - Analog */ +#define RT5663_ASRC_3 0x0085 +#define RT5663_ASRC_6 0x0088 +#define RT5663_ASRC_7 0x0089 +#define RT5663_PLL_TRK_13 0x0099 +#define RT5663_I2S_M_CLK_CTL 0x00a0 +#define RT5663_FDIV_I2S34_M_CLK 0x00a1 +#define RT5663_FDIV_I2S34_M_CLK2 0x00a2 +#define RT5663_FDIV_I2S5_M_CLK 0x00a3 +#define RT5663_FDIV_I2S5_M_CLK2 0x00a4 + +/* Function - Digital */ +#define RT5663_V2_IRQ_4 0x00b9 +#define RT5663_GPIO_3 0x00c2 +#define RT5663_GPIO_4 0x00c3 +#define RT5663_GPIO_STA2 0x00c4 +#define RT5663_HP_AMP_DET1 0x00d0 +#define RT5663_HP_AMP_DET2 0x00d1 +#define RT5663_HP_AMP_DET3 0x00d2 +#define RT5663_MID_BD_HP_AMP 0x00d3 +#define RT5663_LOW_BD_HP_AMP 0x00d4 +#define RT5663_SOF_VOL_ZC2 0x00da +#define RT5663_ADC_STO2_ADJ1 0x00ee +#define RT5663_ADC_STO2_ADJ2 0x00ef +/* General Control */ +#define RT5663_A_JD_CTRL 0x00f0 +#define RT5663_JD1_TRES_CTRL 0x00f1 +#define RT5663_JD2_TRES_CTRL 0x00f2 +#define RT5663_V2_JD_CTRL2 0x00f7 +#define RT5663_DUM_REG_2 0x00fb +#define RT5663_DUM_REG_3 0x00fc + + +#define RT5663_DACADC_DIG_VOL2 0x0101 +#define RT5663_DIG_IN_PIN2 0x0133 +#define RT5663_PAD_DRV_CTL1 0x0136 +#define RT5663_SOF_RAM_DEPOP 0x0138 +#define RT5663_VOL_TEST 0x013f +#define RT5663_MONO_DYNA_1 0x0170 +#define RT5663_MONO_DYNA_2 0x0171 +#define RT5663_MONO_DYNA_3 0x0172 +#define RT5663_MONO_DYNA_4 0x0173 +#define RT5663_MONO_DYNA_5 0x0174 +#define RT5663_MONO_DYNA_6 0x0175 +#define RT5663_STO1_SIL_DET 0x0190 +#define RT5663_MONOL_SIL_DET 0x0191 +#define RT5663_MONOR_SIL_DET 0x0192 +#define RT5663_STO2_DAC_SIL 0x0193 +#define RT5663_PWR_SAV_CTL1 0x0194 +#define RT5663_PWR_SAV_CTL2 0x0195 +#define RT5663_PWR_SAV_CTL3 0x0196 +#define RT5663_PWR_SAV_CTL4 0x0197 +#define RT5663_PWR_SAV_CTL5 0x0198 +#define RT5663_PWR_SAV_CTL6 0x0199 +#define RT5663_MONO_AMP_CAL1 0x01a0 +#define RT5663_MONO_AMP_CAL2 0x01a1 +#define RT5663_MONO_AMP_CAL3 0x01a2 +#define RT5663_MONO_AMP_CAL4 0x01a3 +#define RT5663_MONO_AMP_CAL5 0x01a4 +#define RT5663_MONO_AMP_CAL6 0x01a5 +#define RT5663_MONO_AMP_CAL7 0x01a6 +#define RT5663_MONO_AMP_CAL_ST1 0x01a7 +#define RT5663_MONO_AMP_CAL_ST2 0x01a8 +#define RT5663_MONO_AMP_CAL_ST3 0x01a9 +#define RT5663_MONO_AMP_CAL_ST4 0x01aa +#define RT5663_MONO_AMP_CAL_ST5 0x01ab +#define RT5663_V2_HP_IMP_SEN_13 0x01b9 +#define RT5663_V2_HP_IMP_SEN_14 0x01ba +#define RT5663_V2_HP_IMP_SEN_6 0x01bb +#define RT5663_V2_HP_IMP_SEN_7 0x01bc +#define RT5663_V2_HP_IMP_SEN_8 0x01bd +#define RT5663_V2_HP_IMP_SEN_9 0x01be +#define RT5663_V2_HP_IMP_SEN_10 0x01bf +#define RT5663_HP_LOGIC_3 0x01dc +#define RT5663_HP_CALIB_ST10 0x01f3 +#define RT5663_HP_CALIB_ST11 0x01f4 +#define RT5663_PRO_REG_TBL_4 0x0203 +#define RT5663_PRO_REG_TBL_5 0x0204 +#define RT5663_PRO_REG_TBL_6 0x0205 +#define RT5663_PRO_REG_TBL_7 0x0206 +#define RT5663_PRO_REG_TBL_8 0x0207 +#define RT5663_PRO_REG_TBL_9 0x0208 +#define RT5663_SAR_ADC_INL_1 0x0210 +#define RT5663_SAR_ADC_INL_2 0x0211 +#define RT5663_SAR_ADC_INL_3 0x0212 +#define RT5663_SAR_ADC_INL_4 0x0213 +#define RT5663_SAR_ADC_INL_5 0x0214 +#define RT5663_SAR_ADC_INL_6 0x0215 +#define RT5663_SAR_ADC_INL_7 0x0216 +#define RT5663_SAR_ADC_INL_8 0x0217 +#define RT5663_SAR_ADC_INL_9 0x0218 +#define RT5663_SAR_ADC_INL_10 0x0219 +#define RT5663_SAR_ADC_INL_11 0x021a +#define RT5663_SAR_ADC_INL_12 0x021b +#define RT5663_DRC_CTRL_1 0x02ff +#define RT5663_DRC1_CTRL_2 0x0301 +#define RT5663_DRC1_CTRL_3 0x0302 +#define RT5663_DRC1_CTRL_4 0x0303 +#define RT5663_DRC1_CTRL_5 0x0304 +#define RT5663_DRC1_CTRL_6 0x0305 +#define RT5663_DRC1_HD_CTRL_1 0x0306 +#define RT5663_DRC1_HD_CTRL_2 0x0307 +#define RT5663_DRC1_PRI_REG_1 0x0310 +#define RT5663_DRC1_PRI_REG_2 0x0311 +#define RT5663_DRC1_PRI_REG_3 0x0312 +#define RT5663_DRC1_PRI_REG_4 0x0313 +#define RT5663_DRC1_PRI_REG_5 0x0314 +#define RT5663_DRC1_PRI_REG_6 0x0315 +#define RT5663_DRC1_PRI_REG_7 0x0316 +#define RT5663_DRC1_PRI_REG_8 0x0317 +#define RT5663_ALC_PGA_CTL_1 0x0330 +#define RT5663_ALC_PGA_CTL_2 0x0331 +#define RT5663_ALC_PGA_CTL_3 0x0332 +#define RT5663_ALC_PGA_CTL_4 0x0333 +#define RT5663_ALC_PGA_CTL_5 0x0334 +#define RT5663_ALC_PGA_CTL_6 0x0335 +#define RT5663_ALC_PGA_CTL_7 0x0336 +#define RT5663_ALC_PGA_CTL_8 0x0337 +#define RT5663_ALC_PGA_REG_1 0x0338 +#define RT5663_ALC_PGA_REG_2 0x0339 +#define RT5663_ALC_PGA_REG_3 0x033a +#define RT5663_ADC_EQ_RECOV_1 0x03c0 +#define RT5663_ADC_EQ_RECOV_2 0x03c1 +#define RT5663_ADC_EQ_RECOV_3 0x03c2 +#define RT5663_ADC_EQ_RECOV_4 0x03c3 +#define RT5663_ADC_EQ_RECOV_5 0x03c4 +#define RT5663_ADC_EQ_RECOV_6 0x03c5 +#define RT5663_ADC_EQ_RECOV_7 0x03c6 +#define RT5663_ADC_EQ_RECOV_8 0x03c7 +#define RT5663_ADC_EQ_RECOV_9 0x03c8 +#define RT5663_ADC_EQ_RECOV_10 0x03c9 +#define RT5663_ADC_EQ_RECOV_11 0x03ca +#define RT5663_ADC_EQ_RECOV_12 0x03cb +#define RT5663_ADC_EQ_RECOV_13 0x03cc +#define RT5663_VID_HIDDEN 0x03fe +#define RT5663_VID_CUSTOMER 0x03ff +#define RT5663_SCAN_MODE 0x07f0 +#define RT5663_I2C_BYPA 0x07fa + +/* Headphone Amp Control 2 (0x0003) */ +#define RT5663_EN_DAC_HPO_MASK (0x1 << 14) +#define RT5663_EN_DAC_HPO_SHIFT 14 +#define RT5663_EN_DAC_HPO_DIS (0x0 << 14) +#define RT5663_EN_DAC_HPO_EN (0x1 << 14) + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5663_GAIN_HP (0x1f << 8) +#define RT5663_GAIN_HP_SHIFT 8 + +/* AEC BST Control (0x000b) */ +#define RT5663_GAIN_CBJ_MASK (0xf << 8) +#define RT5663_GAIN_CBJ_SHIFT 8 + +/* IN1 Control / MIC GND REF (0x000c) */ +#define RT5663_IN1_DF_MASK (0x1 << 15) +#define RT5663_IN1_DF_SHIFT 15 + +/* Combo Jack and Type Detection Control 1 (0x0010) */ +#define RT5663_CBJ_DET_MASK (0x1 << 15) +#define RT5663_CBJ_DET_SHIFT 15 +#define RT5663_CBJ_DET_DIS (0x0 << 15) +#define RT5663_CBJ_DET_EN (0x1 << 15) +#define RT5663_DET_TYPE_MASK (0x1 << 12) +#define RT5663_DET_TYPE_SHIFT 12 +#define RT5663_DET_TYPE_WLCSP (0x0 << 12) +#define RT5663_DET_TYPE_QFN (0x1 << 12) +#define RT5663_VREF_BIAS_MASK (0x1 << 6) +#define RT5663_VREF_BIAS_SHIFT 6 +#define RT5663_VREF_BIAS_FSM (0x0 << 6) +#define RT5663_VREF_BIAS_REG (0x1 << 6) + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5663_RECMIX1L_BST1_CBJ (0x1 << 7) +#define RT5663_RECMIX1L_BST1_CBJ_SHIFT 7 +#define RT5663_RECMIX1L_BST2 (0x1 << 4) +#define RT5663_RECMIX1L_BST2_SHIFT 4 + +/* REC Right Mixer Control 2 (0x003e) */ +#define RT5663_RECMIX1R_BST2 (0x1 << 4) +#define RT5663_RECMIX1R_BST2_SHIFT 4 + +/* DAC1 Digital Volume (0x0019) */ +#define RT5663_DAC_L1_VOL_MASK (0xff << 8) +#define RT5663_DAC_L1_VOL_SHIFT 8 +#define RT5663_DAC_R1_VOL_MASK (0xff) +#define RT5663_DAC_R1_VOL_SHIFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5663_ADC_L_MUTE_MASK (0x1 << 15) +#define RT5663_ADC_L_MUTE_SHIFT 15 +#define RT5663_ADC_L_VOL_MASK (0x7f << 8) +#define RT5663_ADC_L_VOL_SHIFT 8 +#define RT5663_ADC_R_MUTE_MASK (0x1 << 7) +#define RT5663_ADC_R_MUTE_SHIFT 7 +#define RT5663_ADC_R_VOL_MASK (0x7f) +#define RT5663_ADC_R_VOL_SHIFT 0 + +/* Stereo ADC Mixer Control (0x0026) */ +#define RT5663_M_STO1_ADC_L1 (0x1 << 15) +#define RT5663_M_STO1_ADC_L1_SHIFT 15 +#define RT5663_M_STO1_ADC_L2 (0x1 << 14) +#define RT5663_M_STO1_ADC_L2_SHIFT 14 +#define RT5663_STO1_ADC_L1_SRC (0x1 << 13) +#define RT5663_STO1_ADC_L1_SRC_SHIFT 13 +#define RT5663_STO1_ADC_L2_SRC (0x1 << 12) +#define RT5663_STO1_ADC_L2_SRC_SHIFT 12 +#define RT5663_STO1_ADC_L_SRC (0x3 << 10) +#define RT5663_STO1_ADC_L_SRC_SHIFT 10 +#define RT5663_M_STO1_ADC_R1 (0x1 << 7) +#define RT5663_M_STO1_ADC_R1_SHIFT 7 +#define RT5663_M_STO1_ADC_R2 (0x1 << 6) +#define RT5663_M_STO1_ADC_R2_SHIFT 6 +#define RT5663_STO1_ADC_R1_SRC (0x1 << 5) +#define RT5663_STO1_ADC_R1_SRC_SHIFT 5 +#define RT5663_STO1_ADC_R2_SRC (0x1 << 4) +#define RT5663_STO1_ADC_R2_SRC_SHIFT 4 +#define RT5663_STO1_ADC_R_SRC (0x3 << 2) +#define RT5663_STO1_ADC_R_SRC_SHIFT 2 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5663_M_ADCMIX_L (0x1 << 15) +#define RT5663_M_ADCMIX_L_SHIFT 15 +#define RT5663_M_DAC1_L (0x1 << 14) +#define RT5663_M_DAC1_L_SHIFT 14 +#define RT5663_M_ADCMIX_R (0x1 << 7) +#define RT5663_M_ADCMIX_R_SHIFT 7 +#define RT5663_M_DAC1_R (0x1 << 6) +#define RT5663_M_DAC1_R_SHIFT 6 + +/* Stereo DAC Mixer Control (0x002a) */ +#define RT5663_M_DAC_L1_STO_L (0x1 << 15) +#define RT5663_M_DAC_L1_STO_L_SHIFT 15 +#define RT5663_M_DAC_R1_STO_L (0x1 << 13) +#define RT5663_M_DAC_R1_STO_L_SHIFT 13 +#define RT5663_M_DAC_L1_STO_R (0x1 << 7) +#define RT5663_M_DAC_L1_STO_R_SHIFT 7 +#define RT5663_M_DAC_R1_STO_R (0x1 << 5) +#define RT5663_M_DAC_R1_STO_R_SHIFT 5 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5663_PWR_I2S1 (0x1 << 15) +#define RT5663_PWR_I2S1_SHIFT 15 +#define RT5663_PWR_DAC_L1 (0x1 << 11) +#define RT5663_PWR_DAC_L1_SHIFT 11 +#define RT5663_PWR_DAC_R1 (0x1 << 10) +#define RT5663_PWR_DAC_R1_SHIFT 10 +#define RT5663_PWR_LDO_DACREF_MASK (0x1 << 8) +#define RT5663_PWR_LDO_DACREF_SHIFT 8 +#define RT5663_PWR_LDO_DACREF_ON (0x1 << 8) +#define RT5663_PWR_LDO_DACREF_DOWN (0x0 << 8) +#define RT5663_PWR_LDO_SHIFT 8 +#define RT5663_PWR_ADC_L1 (0x1 << 4) +#define RT5663_PWR_ADC_L1_SHIFT 4 +#define RT5663_PWR_ADC_R1 (0x1 << 3) +#define RT5663_PWR_ADC_R1_SHIFT 3 + +/* Power Management for Digital 2 (0x0062) */ +#define RT5663_PWR_ADC_S1F (0x1 << 15) +#define RT5663_PWR_ADC_S1F_SHIFT 15 +#define RT5663_PWR_DAC_S1F (0x1 << 10) +#define RT5663_PWR_DAC_S1F_SHIFT 10 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5663_PWR_VREF1 (0x1 << 15) +#define RT5663_PWR_VREF1_MASK (0x1 << 15) +#define RT5663_PWR_VREF1_SHIFT 15 +#define RT5663_PWR_FV1 (0x1 << 14) +#define RT5663_PWR_FV1_MASK (0x1 << 14) +#define RT5663_PWR_FV1_SHIFT 14 +#define RT5663_PWR_VREF2 (0x1 << 13) +#define RT5663_PWR_VREF2_MASK (0x1 << 13) +#define RT5663_PWR_VREF2_SHIFT 13 +#define RT5663_PWR_FV2 (0x1 << 12) +#define RT5663_PWR_FV2_MASK (0x1 << 12) +#define RT5663_PWR_FV2_SHIFT 12 +#define RT5663_PWR_MB (0x1 << 9) +#define RT5663_PWR_MB_MASK (0x1 << 9) +#define RT5663_PWR_MB_SHIFT 9 +#define RT5663_AMP_HP_MASK (0x3 << 2) +#define RT5663_AMP_HP_SHIFT 2 +#define RT5663_AMP_HP_1X (0x0 << 2) +#define RT5663_AMP_HP_3X (0x1 << 2) +#define RT5663_AMP_HP_5X (0x3 << 2) +#define RT5663_LDO1_DVO_MASK (0x3) +#define RT5663_LDO1_DVO_SHIFT 0 +#define RT5663_LDO1_DVO_0_9V (0x0) +#define RT5663_LDO1_DVO_1_0V (0x1) +#define RT5663_LDO1_DVO_1_2V (0x2) +#define RT5663_LDO1_DVO_1_4V (0x3) + +/* Power Management for Analog 2 (0x0064) */ +#define RT5663_PWR_BST1 (0x1 << 15) +#define RT5663_PWR_BST1_MASK (0x1 << 15) +#define RT5663_PWR_BST1_SHIFT 15 +#define RT5663_PWR_BST1_OFF (0x0 << 15) +#define RT5663_PWR_BST1_ON (0x1 << 15) +#define RT5663_PWR_BST2 (0x1 << 14) +#define RT5663_PWR_BST2_MASK (0x1 << 14) +#define RT5663_PWR_BST2_SHIFT 14 +#define RT5663_PWR_MB1 (0x1 << 11) +#define RT5663_PWR_MB1_SHIFT 11 +#define RT5663_PWR_MB2 (0x1 << 10) +#define RT5663_PWR_MB2_SHIFT 10 +#define RT5663_PWR_BST2_OP (0x1 << 6) +#define RT5663_PWR_BST2_OP_MASK (0x1 << 6) +#define RT5663_PWR_BST2_OP_SHIFT 6 +#define RT5663_PWR_JD1 (0x1 << 3) +#define RT5663_PWR_JD1_MASK (0x1 << 3) +#define RT5663_PWR_JD1_SHIFT 3 +#define RT5663_PWR_JD2 (0x1 << 2) +#define RT5663_PWR_JD2_MASK (0x1 << 2) +#define RT5663_PWR_JD2_SHIFT 2 +#define RT5663_PWR_RECMIX1 (0x1 << 1) +#define RT5663_PWR_RECMIX1_SHIFT 1 +#define RT5663_PWR_RECMIX2 (0x1) +#define RT5663_PWR_RECMIX2_SHIFT 0 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5663_PWR_CBJ_MASK (0x1 << 9) +#define RT5663_PWR_CBJ_SHIFT 9 +#define RT5663_PWR_CBJ_OFF (0x0 << 9) +#define RT5663_PWR_CBJ_ON (0x1 << 9) +#define RT5663_PWR_PLL (0x1 << 6) +#define RT5663_PWR_PLL_SHIFT 6 +#define RT5663_PWR_LDO2 (0x1 << 2) +#define RT5663_PWR_LDO2_SHIFT 2 + +/* Power Management for Volume (0x0067) */ +#define RT5663_V2_PWR_MIC_DET (0x1 << 5) +#define RT5663_V2_PWR_MIC_DET_SHIFT 5 + +/* MCLK and System Clock Detection Control (0x006b) */ +#define RT5663_EN_ANA_CLK_DET_MASK (0x1 << 15) +#define RT5663_EN_ANA_CLK_DET_SHIFT 15 +#define RT5663_EN_ANA_CLK_DET_DIS (0x0 << 15) +#define RT5663_EN_ANA_CLK_DET_AUTO (0x1 << 15) +#define RT5663_PWR_CLK_DET_MASK (0x1) +#define RT5663_PWR_CLK_DET_SHIFT 0 +#define RT5663_PWR_CLK_DET_DIS (0x0) +#define RT5663_PWR_CLK_DET_EN (0x1) + +/* I2S1 Audio Serial Data Port Control (0x0070) */ +#define RT5663_I2S_MS_MASK (0x1 << 15) +#define RT5663_I2S_MS_SHIFT 15 +#define RT5663_I2S_MS_M (0x0 << 15) +#define RT5663_I2S_MS_S (0x1 << 15) +#define RT5663_I2S_BP_MASK (0x1 << 8) +#define RT5663_I2S_BP_SHIFT 8 +#define RT5663_I2S_BP_NOR (0x0 << 8) +#define RT5663_I2S_BP_INV (0x1 << 8) +#define RT5663_I2S_DL_MASK (0x3 << 4) +#define RT5663_I2S_DL_SHIFT 4 +#define RT5663_I2S_DL_16 (0x0 << 4) +#define RT5663_I2S_DL_20 (0x1 << 4) +#define RT5663_I2S_DL_24 (0x2 << 4) +#define RT5663_I2S_DL_8 (0x3 << 4) +#define RT5663_I2S_DF_MASK (0x7) +#define RT5663_I2S_DF_SHIFT 0 +#define RT5663_I2S_DF_I2S (0x0) +#define RT5663_I2S_DF_LEFT (0x1) +#define RT5663_I2S_DF_PCM_A (0x2) +#define RT5663_I2S_DF_PCM_B (0x3) +#define RT5663_I2S_DF_PCM_A_N (0x6) +#define RT5663_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5663_I2S_PD1_MASK (0x7 << 12) +#define RT5663_I2S_PD1_SHIFT 12 +#define RT5663_M_I2S_DIV_MASK (0x7 << 8) +#define RT5663_M_I2S_DIV_SHIFT 8 +#define RT5663_CLK_SRC_MASK (0x3 << 4) +#define RT5663_CLK_SRC_MCLK (0x0 << 4) +#define RT5663_CLK_SRC_PLL_OUT (0x1 << 4) +#define RT5663_CLK_SRC_DIV (0x2 << 4) +#define RT5663_CLK_SRC_RC (0x3 << 4) +#define RT5663_DAC_OSR_MASK (0x3 << 2) +#define RT5663_DAC_OSR_SHIFT 2 +#define RT5663_DAC_OSR_128 (0x0 << 2) +#define RT5663_DAC_OSR_64 (0x1 << 2) +#define RT5663_DAC_OSR_32 (0x2 << 2) +#define RT5663_ADC_OSR_MASK (0x3) +#define RT5663_ADC_OSR_SHIFT 0 +#define RT5663_ADC_OSR_128 (0x0) +#define RT5663_ADC_OSR_64 (0x1) +#define RT5663_ADC_OSR_32 (0x2) + +/* TDM1 control 1 (0x0078) */ +#define RT5663_TDM_MODE_MASK (0x1 << 15) +#define RT5663_TDM_MODE_SHIFT 15 +#define RT5663_TDM_MODE_I2S (0x0 << 15) +#define RT5663_TDM_MODE_TDM (0x1 << 15) +#define RT5663_TDM_IN_CH_MASK (0x3 << 10) +#define RT5663_TDM_IN_CH_SHIFT 10 +#define RT5663_TDM_IN_CH_2 (0x0 << 10) +#define RT5663_TDM_IN_CH_4 (0x1 << 10) +#define RT5663_TDM_IN_CH_6 (0x2 << 10) +#define RT5663_TDM_IN_CH_8 (0x3 << 10) +#define RT5663_TDM_OUT_CH_MASK (0x3 << 8) +#define RT5663_TDM_OUT_CH_SHIFT 8 +#define RT5663_TDM_OUT_CH_2 (0x0 << 8) +#define RT5663_TDM_OUT_CH_4 (0x1 << 8) +#define RT5663_TDM_OUT_CH_6 (0x2 << 8) +#define RT5663_TDM_OUT_CH_8 (0x3 << 8) +#define RT5663_TDM_IN_LEN_MASK (0x3 << 6) +#define RT5663_TDM_IN_LEN_SHIFT 6 +#define RT5663_TDM_IN_LEN_16 (0x0 << 6) +#define RT5663_TDM_IN_LEN_20 (0x1 << 6) +#define RT5663_TDM_IN_LEN_24 (0x2 << 6) +#define RT5663_TDM_IN_LEN_32 (0x3 << 6) +#define RT5663_TDM_OUT_LEN_MASK (0x3 << 4) +#define RT5663_TDM_OUT_LEN_SHIFT 4 +#define RT5663_TDM_OUT_LEN_16 (0x0 << 4) +#define RT5663_TDM_OUT_LEN_20 (0x1 << 4) +#define RT5663_TDM_OUT_LEN_24 (0x2 << 4) +#define RT5663_TDM_OUT_LEN_32 (0x3 << 4) + +/* Global Clock Control (0x0080) */ +#define RT5663_SCLK_SRC_MASK (0x3 << 14) +#define RT5663_SCLK_SRC_SHIFT 14 +#define RT5663_SCLK_SRC_MCLK (0x0 << 14) +#define RT5663_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5663_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5663_PLL1_SRC_MASK (0x7 << 11) +#define RT5663_PLL1_SRC_SHIFT 11 +#define RT5663_PLL1_SRC_MCLK (0x0 << 11) +#define RT5663_PLL1_SRC_BCLK1 (0x1 << 11) +#define RT5663_V2_PLL1_SRC_MASK (0x7 << 8) +#define RT5663_V2_PLL1_SRC_SHIFT 8 +#define RT5663_V2_PLL1_SRC_MCLK (0x0 << 8) +#define RT5663_V2_PLL1_SRC_BCLK1 (0x1 << 8) +#define RT5663_PLL1_PD_MASK (0x1 << 4) +#define RT5663_PLL1_PD_SHIFT 4 + +#define RT5663_PLL_INP_MAX 40000000 +#define RT5663_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5663_PLL_N_MAX 0x001ff +#define RT5663_PLL_N_MASK (RT5663_PLL_N_MAX << 7) +#define RT5663_PLL_N_SHIFT 7 +#define RT5663_PLL_K_MAX 0x001f +#define RT5663_PLL_K_MASK (RT5663_PLL_K_MAX) +#define RT5663_PLL_K_SHIFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5663_PLL_M_MAX 0x00f +#define RT5663_PLL_M_MASK (RT5663_PLL_M_MAX << 12) +#define RT5663_PLL_M_SHIFT 12 +#define RT5663_PLL_M_BP (0x1 << 11) +#define RT5663_PLL_M_BP_SHIFT 11 + +/* PLL tracking mode 1 (0x0083) */ +#define RT5663_V2_I2S1_ASRC_MASK (0x1 << 13) +#define RT5663_V2_I2S1_ASRC_SHIFT 13 +#define RT5663_V2_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5663_V2_DAC_STO1_ASRC_SHIFT 12 +#define RT5663_V2_ADC_STO1_ASRC_MASK (0x1 << 4) +#define RT5663_V2_ADC_STO1_ASRC_SHIFT 4 + +/* PLL tracking mode 2 (0x0084)*/ +#define RT5663_DA_STO1_TRACK_MASK (0x7 << 12) +#define RT5663_DA_STO1_TRACK_SHIFT 12 +#define RT5663_DA_STO1_TRACK_SYSCLK (0x0 << 12) +#define RT5663_DA_STO1_TRACK_I2S1 (0x1 << 12) + +/* PLL tracking mode 3 (0x0085)*/ +#define RT5663_V2_AD_STO1_TRACK_MASK (0x7 << 12) +#define RT5663_V2_AD_STO1_TRACK_SHIFT 12 +#define RT5663_V2_AD_STO1_TRACK_SYSCLK (0x0 << 12) +#define RT5663_V2_AD_STO1_TRACK_I2S1 (0x1 << 12) + +/* HPOUT Charge pump control 1 (0x0091) */ +#define RT5663_OSW_HP_L_MASK (0x1 << 11) +#define RT5663_OSW_HP_L_SHIFT 11 +#define RT5663_OSW_HP_L_EN (0x1 << 11) +#define RT5663_OSW_HP_L_DIS (0x0 << 11) +#define RT5663_OSW_HP_R_MASK (0x1 << 10) +#define RT5663_OSW_HP_R_SHIFT 10 +#define RT5663_OSW_HP_R_EN (0x1 << 10) +#define RT5663_OSW_HP_R_DIS (0x0 << 10) +#define RT5663_SEL_PM_HP_MASK (0x3 << 8) +#define RT5663_SEL_PM_HP_SHIFT 8 +#define RT5663_SEL_PM_HP_0_6 (0x0 << 8) +#define RT5663_SEL_PM_HP_0_9 (0x1 << 8) +#define RT5663_SEL_PM_HP_1_8 (0x2 << 8) +#define RT5663_SEL_PM_HP_HIGH (0x3 << 8) +#define RT5663_OVCD_HP_MASK (0x1 << 2) +#define RT5663_OVCD_HP_SHIFT 2 +#define RT5663_OVCD_HP_EN (0x1 << 2) +#define RT5663_OVCD_HP_DIS (0x0 << 2) + +/* RC Clock Control (0x0094) */ +#define RT5663_DIG_25M_CLK_MASK (0x1 << 9) +#define RT5663_DIG_25M_CLK_SHIFT 9 +#define RT5663_DIG_25M_CLK_DIS (0x0 << 9) +#define RT5663_DIG_25M_CLK_EN (0x1 << 9) +#define RT5663_DIG_1M_CLK_MASK (0x1 << 8) +#define RT5663_DIG_1M_CLK_SHIFT 8 +#define RT5663_DIG_1M_CLK_DIS (0x0 << 8) +#define RT5663_DIG_1M_CLK_EN (0x1 << 8) + +/* Auto Turn On 1M RC CLK (0x009f) */ +#define RT5663_IRQ_POW_SAV_MASK (0x1 << 15) +#define RT5663_IRQ_POW_SAV_SHIFT 15 +#define RT5663_IRQ_POW_SAV_DIS (0x0 << 15) +#define RT5663_IRQ_POW_SAV_EN (0x1 << 15) +#define RT5663_IRQ_POW_SAV_JD1_MASK (0x1 << 14) +#define RT5663_IRQ_POW_SAV_JD1_SHIFT 14 +#define RT5663_IRQ_POW_SAV_JD1_DIS (0x0 << 14) +#define RT5663_IRQ_POW_SAV_JD1_EN (0x1 << 14) +#define RT5663_IRQ_MANUAL_MASK (0x1 << 8) +#define RT5663_IRQ_MANUAL_SHIFT 8 +#define RT5663_IRQ_MANUAL_DIS (0x0 << 8) +#define RT5663_IRQ_MANUAL_EN (0x1 << 8) + +/* IRQ Control 1 (0x00b6) */ +#define RT5663_EN_CB_JD_MASK (0x1 << 3) +#define RT5663_EN_CB_JD_SHIFT 3 +#define RT5663_EN_CB_JD_EN (0x1 << 3) +#define RT5663_EN_CB_JD_DIS (0x0 << 3) + +/* IRQ Control 3 (0x00b8) */ +#define RT5663_V2_EN_IRQ_INLINE_MASK (0x1 << 6) +#define RT5663_V2_EN_IRQ_INLINE_SHIFT 6 +#define RT5663_V2_EN_IRQ_INLINE_BYP (0x0 << 6) +#define RT5663_V2_EN_IRQ_INLINE_NOR (0x1 << 6) + +/* GPIO Control 1 (0x00c0) */ +#define RT5663_GP1_PIN_MASK (0x1 << 15) +#define RT5663_GP1_PIN_SHIFT 15 +#define RT5663_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5663_GP1_PIN_IRQ (0x1 << 15) + +/* GPIO Control 2 (0x00c1) */ +#define RT5663_GP4_PIN_CONF_MASK (0x1 << 5) +#define RT5663_GP4_PIN_CONF_SHIFT 5 +#define RT5663_GP4_PIN_CONF_INPUT (0x0 << 5) +#define RT5663_GP4_PIN_CONF_OUTPUT (0x1 << 5) + +/* GPIO Control 2 (0x00c2) */ +#define RT5663_GP8_PIN_CONF_MASK (0x1 << 13) +#define RT5663_GP8_PIN_CONF_SHIFT 13 +#define RT5663_GP8_PIN_CONF_INPUT (0x0 << 13) +#define RT5663_GP8_PIN_CONF_OUTPUT (0x1 << 13) + +/* 4 Buttons Inline Command Function 1 (0x00df) */ +#define RT5663_4BTN_CLK_DEB_MASK (0x3 << 2) +#define RT5663_4BTN_CLK_DEB_SHIFT 2 +#define RT5663_4BTN_CLK_DEB_8MS (0x0 << 2) +#define RT5663_4BTN_CLK_DEB_16MS (0x1 << 2) +#define RT5663_4BTN_CLK_DEB_32MS (0x2 << 2) +#define RT5663_4BTN_CLK_DEB_65MS (0x3 << 2) + +/* Inline Command Function 6 (0x00e0) */ +#define RT5663_EN_4BTN_INL_MASK (0x1 << 15) +#define RT5663_EN_4BTN_INL_SHIFT 15 +#define RT5663_EN_4BTN_INL_DIS (0x0 << 15) +#define RT5663_EN_4BTN_INL_EN (0x1 << 15) +#define RT5663_RESET_4BTN_INL_MASK (0x1 << 14) +#define RT5663_RESET_4BTN_INL_SHIFT 14 +#define RT5663_RESET_4BTN_INL_RESET (0x0 << 14) +#define RT5663_RESET_4BTN_INL_NOR (0x1 << 14) + +/* Digital Misc Control (0x00fa) */ +#define RT5663_DIG_GATE_CTRL_MASK 0x1 +#define RT5663_DIG_GATE_CTRL_SHIFT (0) +#define RT5663_DIG_GATE_CTRL_DIS 0x0 +#define RT5663_DIG_GATE_CTRL_EN 0x1 + +/* Chopper and Clock control for DAC L (0x013a)*/ +#define RT5663_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5663_CKXEN_DAC1_SHIFT 13 +#define RT5663_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5663_CKGEN_DAC1_SHIFT 12 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5663_CKXEN_ADCC_MASK (0x1 << 13) +#define RT5663_CKXEN_ADCC_SHIFT 13 +#define RT5663_CKGEN_ADCC_MASK (0x1 << 12) +#define RT5663_CKGEN_ADCC_SHIFT 12 + +/* HP Behavior Logic Control 2 (0x01db) */ +#define RT5663_HP_SIG_SRC1_MASK (0x3) +#define RT5663_HP_SIG_SRC1_SHIFT 0 +#define RT5663_HP_SIG_SRC1_HP_DC (0x0) +#define RT5663_HP_SIG_SRC1_HP_CALIB (0x1) +#define RT5663_HP_SIG_SRC1_REG (0x2) +#define RT5663_HP_SIG_SRC1_SILENCE (0x3) + +/* RT5663 specific register */ +#define RT5663_HP_OUT_EN 0x0002 +#define RT5663_HP_LCH_DRE 0x0005 +#define RT5663_HP_RCH_DRE 0x0006 +#define RT5663_CALIB_BST 0x000a +#define RT5663_RECMIX 0x0010 +#define RT5663_SIL_DET_CTL 0x0015 +#define RT5663_PWR_SAV_SILDET 0x0016 +#define RT5663_SIDETONE_CTL 0x0018 +#define RT5663_STO1_DAC_DIG_VOL 0x0019 +#define RT5663_STO1_ADC_DIG_VOL 0x001c +#define RT5663_STO1_BOOST 0x001f +#define RT5663_HP_IMP_GAIN_1 0x0022 +#define RT5663_HP_IMP_GAIN_2 0x0023 +#define RT5663_STO1_ADC_MIXER 0x0026 +#define RT5663_AD_DA_MIXER 0x0029 +#define RT5663_STO_DAC_MIXER 0x002a +#define RT5663_DIG_SIDE_MIXER 0x002c +#define RT5663_BYPASS_STO_DAC 0x002d +#define RT5663_CALIB_REC_MIX 0x0040 +#define RT5663_PWR_DIG_1 0x0061 +#define RT5663_PWR_DIG_2 0x0062 +#define RT5663_PWR_ANLG_1 0x0063 +#define RT5663_PWR_ANLG_2 0x0064 +#define RT5663_PWR_ANLG_3 0x0065 +#define RT5663_PWR_MIXER 0x0066 +#define RT5663_SIG_CLK_DET 0x006b +#define RT5663_PRE_DIV_GATING_1 0x006e +#define RT5663_PRE_DIV_GATING_2 0x006f +#define RT5663_I2S1_SDP 0x0070 +#define RT5663_ADDA_CLK_1 0x0073 +#define RT5663_ADDA_RST 0x0074 +#define RT5663_FRAC_DIV_1 0x0075 +#define RT5663_FRAC_DIV_2 0x0076 +#define RT5663_TDM_1 0x0077 +#define RT5663_TDM_2 0x0078 +#define RT5663_TDM_3 0x0079 +#define RT5663_TDM_4 0x007a +#define RT5663_TDM_5 0x007b +#define RT5663_TDM_6 0x007c +#define RT5663_TDM_7 0x007d +#define RT5663_TDM_8 0x007e +#define RT5663_TDM_9 0x007f +#define RT5663_GLB_CLK 0x0080 +#define RT5663_PLL_1 0x0081 +#define RT5663_PLL_2 0x0082 +#define RT5663_ASRC_1 0x0083 +#define RT5663_ASRC_2 0x0084 +#define RT5663_ASRC_4 0x0086 +#define RT5663_DUMMY_REG 0x0087 +#define RT5663_ASRC_8 0x008a +#define RT5663_ASRC_9 0x008b +#define RT5663_ASRC_11 0x008c +#define RT5663_DEPOP_1 0x008e +#define RT5663_DEPOP_2 0x008f +#define RT5663_DEPOP_3 0x0090 +#define RT5663_HP_CHARGE_PUMP_1 0x0091 +#define RT5663_HP_CHARGE_PUMP_2 0x0092 +#define RT5663_MICBIAS_1 0x0093 +#define RT5663_RC_CLK 0x0094 +#define RT5663_ASRC_11_2 0x0097 +#define RT5663_DUMMY_REG_2 0x0098 +#define RT5663_REC_PATH_GAIN 0x009a +#define RT5663_AUTO_1MRC_CLK 0x009f +#define RT5663_ADC_EQ_1 0x00ae +#define RT5663_ADC_EQ_2 0x00af +#define RT5663_IRQ_1 0x00b6 +#define RT5663_IRQ_2 0x00b7 +#define RT5663_IRQ_3 0x00b8 +#define RT5663_IRQ_4 0x00ba +#define RT5663_IRQ_5 0x00bb +#define RT5663_INT_ST_1 0x00be +#define RT5663_INT_ST_2 0x00bf +#define RT5663_GPIO_1 0x00c0 +#define RT5663_GPIO_2 0x00c1 +#define RT5663_GPIO_STA1 0x00c5 +#define RT5663_SIN_GEN_1 0x00cb +#define RT5663_SIN_GEN_2 0x00cc +#define RT5663_SIN_GEN_3 0x00cd +#define RT5663_SOF_VOL_ZC1 0x00d9 +#define RT5663_IL_CMD_1 0x00db +#define RT5663_IL_CMD_2 0x00dc +#define RT5663_IL_CMD_3 0x00dd +#define RT5663_IL_CMD_4 0x00de +#define RT5663_IL_CMD_5 0x00df +#define RT5663_IL_CMD_6 0x00e0 +#define RT5663_IL_CMD_7 0x00e1 +#define RT5663_IL_CMD_8 0x00e2 +#define RT5663_IL_CMD_PWRSAV1 0x00e4 +#define RT5663_IL_CMD_PWRSAV2 0x00e5 +#define RT5663_EM_JACK_TYPE_1 0x00e6 +#define RT5663_EM_JACK_TYPE_2 0x00e7 +#define RT5663_EM_JACK_TYPE_3 0x00e8 +#define RT5663_EM_JACK_TYPE_4 0x00e9 +#define RT5663_EM_JACK_TYPE_5 0x00ea +#define RT5663_EM_JACK_TYPE_6 0x00eb +#define RT5663_STO1_HPF_ADJ1 0x00ec +#define RT5663_STO1_HPF_ADJ2 0x00ed +#define RT5663_FAST_OFF_MICBIAS 0x00f4 +#define RT5663_JD_CTRL1 0x00f6 +#define RT5663_JD_CTRL2 0x00f8 +#define RT5663_DIG_MISC 0x00fa +#define RT5663_DIG_VOL_ZCD 0x0100 +#define RT5663_ANA_BIAS_CUR_1 0x0108 +#define RT5663_ANA_BIAS_CUR_2 0x0109 +#define RT5663_ANA_BIAS_CUR_3 0x010a +#define RT5663_ANA_BIAS_CUR_4 0x010b +#define RT5663_ANA_BIAS_CUR_5 0x010c +#define RT5663_ANA_BIAS_CUR_6 0x010d +#define RT5663_BIAS_CUR_5 0x010e +#define RT5663_BIAS_CUR_6 0x010f +#define RT5663_BIAS_CUR_7 0x0110 +#define RT5663_BIAS_CUR_8 0x0111 +#define RT5663_DACREF_LDO 0x0112 +#define RT5663_DUMMY_REG_3 0x0113 +#define RT5663_BIAS_CUR_9 0x0114 +#define RT5663_DUMMY_REG_4 0x0116 +#define RT5663_VREFADJ_OP 0x0117 +#define RT5663_VREF_RECMIX 0x0118 +#define RT5663_CHARGE_PUMP_1 0x0125 +#define RT5663_CHARGE_PUMP_1_2 0x0126 +#define RT5663_CHARGE_PUMP_1_3 0x0127 +#define RT5663_CHARGE_PUMP_2 0x0128 +#define RT5663_DIG_IN_PIN1 0x0132 +#define RT5663_PAD_DRV_CTL 0x0137 +#define RT5663_PLL_INT_REG 0x0139 +#define RT5663_CHOP_DAC_L 0x013a +#define RT5663_CHOP_ADC 0x013b +#define RT5663_CALIB_ADC 0x013c +#define RT5663_CHOP_DAC_R 0x013d +#define RT5663_DUMMY_CTL_DACLR 0x013e +#define RT5663_DUMMY_REG_5 0x0140 +#define RT5663_SOFT_RAMP 0x0141 +#define RT5663_TEST_MODE_1 0x0144 +#define RT5663_TEST_MODE_2 0x0145 +#define RT5663_TEST_MODE_3 0x0146 +#define RT5663_TEST_MODE_4 0x0147 +#define RT5663_TEST_MODE_5 0x0148 +#define RT5663_STO_DRE_1 0x0160 +#define RT5663_STO_DRE_2 0x0161 +#define RT5663_STO_DRE_3 0x0162 +#define RT5663_STO_DRE_4 0x0163 +#define RT5663_STO_DRE_5 0x0164 +#define RT5663_STO_DRE_6 0x0165 +#define RT5663_STO_DRE_7 0x0166 +#define RT5663_STO_DRE_8 0x0167 +#define RT5663_STO_DRE_9 0x0168 +#define RT5663_STO_DRE_10 0x0169 +#define RT5663_MIC_DECRO_1 0x0180 +#define RT5663_MIC_DECRO_2 0x0181 +#define RT5663_MIC_DECRO_3 0x0182 +#define RT5663_MIC_DECRO_4 0x0183 +#define RT5663_MIC_DECRO_5 0x0184 +#define RT5663_MIC_DECRO_6 0x0185 +#define RT5663_HP_DECRO_1 0x01b0 +#define RT5663_HP_DECRO_2 0x01b1 +#define RT5663_HP_DECRO_3 0x01b2 +#define RT5663_HP_DECRO_4 0x01b3 +#define RT5663_HP_DECOUP 0x01b4 +#define RT5663_HP_IMP_SEN_MAP8 0x01b5 +#define RT5663_HP_IMP_SEN_MAP9 0x01b6 +#define RT5663_HP_IMP_SEN_MAP10 0x01b7 +#define RT5663_HP_IMP_SEN_MAP11 0x01b8 +#define RT5663_HP_IMP_SEN_1 0x01c0 +#define RT5663_HP_IMP_SEN_2 0x01c1 +#define RT5663_HP_IMP_SEN_3 0x01c2 +#define RT5663_HP_IMP_SEN_4 0x01c3 +#define RT5663_HP_IMP_SEN_5 0x01c4 +#define RT5663_HP_IMP_SEN_6 0x01c5 +#define RT5663_HP_IMP_SEN_7 0x01c6 +#define RT5663_HP_IMP_SEN_8 0x01c7 +#define RT5663_HP_IMP_SEN_9 0x01c8 +#define RT5663_HP_IMP_SEN_10 0x01c9 +#define RT5663_HP_IMP_SEN_11 0x01ca +#define RT5663_HP_IMP_SEN_12 0x01cb +#define RT5663_HP_IMP_SEN_13 0x01cc +#define RT5663_HP_IMP_SEN_14 0x01cd +#define RT5663_HP_IMP_SEN_15 0x01ce +#define RT5663_HP_IMP_SEN_16 0x01cf +#define RT5663_HP_IMP_SEN_17 0x01d0 +#define RT5663_HP_IMP_SEN_18 0x01d1 +#define RT5663_HP_IMP_SEN_19 0x01d2 +#define RT5663_HP_IMPSEN_DIG5 0x01d3 +#define RT5663_HP_IMPSEN_MAP1 0x01d4 +#define RT5663_HP_IMPSEN_MAP2 0x01d5 +#define RT5663_HP_IMPSEN_MAP3 0x01d6 +#define RT5663_HP_IMPSEN_MAP4 0x01d7 +#define RT5663_HP_IMPSEN_MAP5 0x01d8 +#define RT5663_HP_IMPSEN_MAP7 0x01d9 +#define RT5663_HP_LOGIC_1 0x01da +#define RT5663_HP_LOGIC_2 0x01db +#define RT5663_HP_CALIB_1 0x01dd +#define RT5663_HP_CALIB_1_1 0x01de +#define RT5663_HP_CALIB_2 0x01df +#define RT5663_HP_CALIB_3 0x01e0 +#define RT5663_HP_CALIB_4 0x01e1 +#define RT5663_HP_CALIB_5 0x01e2 +#define RT5663_HP_CALIB_5_1 0x01e3 +#define RT5663_HP_CALIB_6 0x01e4 +#define RT5663_HP_CALIB_7 0x01e5 +#define RT5663_HP_CALIB_9 0x01e6 +#define RT5663_HP_CALIB_10 0x01e7 +#define RT5663_HP_CALIB_11 0x01e8 +#define RT5663_HP_CALIB_ST1 0x01ea +#define RT5663_HP_CALIB_ST2 0x01eb +#define RT5663_HP_CALIB_ST3 0x01ec +#define RT5663_HP_CALIB_ST4 0x01ed +#define RT5663_HP_CALIB_ST5 0x01ee +#define RT5663_HP_CALIB_ST6 0x01ef +#define RT5663_HP_CALIB_ST7 0x01f0 +#define RT5663_HP_CALIB_ST8 0x01f1 +#define RT5663_HP_CALIB_ST9 0x01f2 +#define RT5663_HP_AMP_DET 0x0200 +#define RT5663_DUMMY_REG_6 0x0201 +#define RT5663_HP_BIAS 0x0202 +#define RT5663_CBJ_1 0x0250 +#define RT5663_CBJ_2 0x0251 +#define RT5663_CBJ_3 0x0252 +#define RT5663_DUMMY_1 0x02fa +#define RT5663_DUMMY_2 0x02fb +#define RT5663_DUMMY_3 0x02fc +#define RT5663_ANA_JD 0x0300 +#define RT5663_ADC_LCH_LPF1_A1 0x03d0 +#define RT5663_ADC_RCH_LPF1_A1 0x03d1 +#define RT5663_ADC_LCH_LPF1_H0 0x03d2 +#define RT5663_ADC_RCH_LPF1_H0 0x03d3 +#define RT5663_ADC_LCH_BPF1_A1 0x03d4 +#define RT5663_ADC_RCH_BPF1_A1 0x03d5 +#define RT5663_ADC_LCH_BPF1_A2 0x03d6 +#define RT5663_ADC_RCH_BPF1_A2 0x03d7 +#define RT5663_ADC_LCH_BPF1_H0 0x03d8 +#define RT5663_ADC_RCH_BPF1_H0 0x03d9 +#define RT5663_ADC_LCH_BPF2_A1 0x03da +#define RT5663_ADC_RCH_BPF2_A1 0x03db +#define RT5663_ADC_LCH_BPF2_A2 0x03dc +#define RT5663_ADC_RCH_BPF2_A2 0x03dd +#define RT5663_ADC_LCH_BPF2_H0 0x03de +#define RT5663_ADC_RCH_BPF2_H0 0x03df +#define RT5663_ADC_LCH_BPF3_A1 0x03e0 +#define RT5663_ADC_RCH_BPF3_A1 0x03e1 +#define RT5663_ADC_LCH_BPF3_A2 0x03e2 +#define RT5663_ADC_RCH_BPF3_A2 0x03e3 +#define RT5663_ADC_LCH_BPF3_H0 0x03e4 +#define RT5663_ADC_RCH_BPF3_H0 0x03e5 +#define RT5663_ADC_LCH_BPF4_A1 0x03e6 +#define RT5663_ADC_RCH_BPF4_A1 0x03e7 +#define RT5663_ADC_LCH_BPF4_A2 0x03e8 +#define RT5663_ADC_RCH_BPF4_A2 0x03e9 +#define RT5663_ADC_LCH_BPF4_H0 0x03ea +#define RT5663_ADC_RCH_BPF4_H0 0x03eb +#define RT5663_ADC_LCH_HPF1_A1 0x03ec +#define RT5663_ADC_RCH_HPF1_A1 0x03ed +#define RT5663_ADC_LCH_HPF1_H0 0x03ee +#define RT5663_ADC_RCH_HPF1_H0 0x03ef +#define RT5663_ADC_EQ_PRE_VOL_L 0x03f0 +#define RT5663_ADC_EQ_PRE_VOL_R 0x03f1 +#define RT5663_ADC_EQ_POST_VOL_L 0x03f2 +#define RT5663_ADC_EQ_POST_VOL_R 0x03f3 + +/* RECMIX Control (0x0010) */ +#define RT5663_RECMIX1_BST1_MASK (0x1) +#define RT5663_RECMIX1_BST1_SHIFT 0 +#define RT5663_RECMIX1_BST1_ON (0x0) +#define RT5663_RECMIX1_BST1_OFF (0x1) + +/* Bypass Stereo1 DAC Mixer Control (0x002d) */ +#define RT5663_DACL1_SRC_MASK (0x1 << 3) +#define RT5663_DACL1_SRC_SHIFT 3 +#define RT5663_DACR1_SRC_MASK (0x1 << 2) +#define RT5663_DACR1_SRC_SHIFT 2 + +/* TDM control 2 (0x0078) */ +#define RT5663_DATA_SWAP_ADCDAT1_MASK (0x3 << 14) +#define RT5663_DATA_SWAP_ADCDAT1_SHIFT 14 +#define RT5663_DATA_SWAP_ADCDAT1_LR (0x0 << 14) +#define RT5663_DATA_SWAP_ADCDAT1_RL (0x1 << 14) +#define RT5663_DATA_SWAP_ADCDAT1_LL (0x2 << 14) +#define RT5663_DATA_SWAP_ADCDAT1_RR (0x3 << 14) + +/* TDM control 5 (0x007b) */ +#define RT5663_TDM_LENGTN_MASK (0x3) +#define RT5663_TDM_LENGTN_SHIFT 0 +#define RT5663_TDM_LENGTN_16 (0x0) +#define RT5663_TDM_LENGTN_20 (0x1) +#define RT5663_TDM_LENGTN_24 (0x2) +#define RT5663_TDM_LENGTN_32 (0x3) + +/* PLL tracking mode 1 (0x0083) */ +#define RT5663_I2S1_ASRC_MASK (0x1 << 11) +#define RT5663_I2S1_ASRC_SHIFT 11 +#define RT5663_DAC_STO1_ASRC_MASK (0x1 << 10) +#define RT5663_DAC_STO1_ASRC_SHIFT 10 +#define RT5663_ADC_STO1_ASRC_MASK (0x1 << 3) +#define RT5663_ADC_STO1_ASRC_SHIFT 3 + +/* PLL tracking mode 2 (0x0084)*/ +#define RT5663_DA_STO1_TRACK_MASK (0x7 << 12) +#define RT5663_DA_STO1_TRACK_SHIFT 12 +#define RT5663_DA_STO1_TRACK_SYSCLK (0x0 << 12) +#define RT5663_DA_STO1_TRACK_I2S1 (0x1 << 12) +#define RT5663_AD_STO1_TRACK_MASK (0x7) +#define RT5663_AD_STO1_TRACK_SHIFT 0 +#define RT5663_AD_STO1_TRACK_SYSCLK (0x0) +#define RT5663_AD_STO1_TRACK_I2S1 (0x1) + +/* HPOUT Charge pump control 1 (0x0091) */ +#define RT5663_SI_HP_MASK (0x1 << 12) +#define RT5663_SI_HP_SHIFT 12 +#define RT5663_SI_HP_EN (0x1 << 12) +#define RT5663_SI_HP_DIS (0x0 << 12) + +/* GPIO Control 2 (0x00b6) */ +#define RT5663_GP1_PIN_CONF_MASK (0x1 << 2) +#define RT5663_GP1_PIN_CONF_SHIFT 2 +#define RT5663_GP1_PIN_CONF_OUTPUT (0x1 << 2) +#define RT5663_GP1_PIN_CONF_INPUT (0x0 << 2) + +/* GPIO Control 2 (0x00b7) */ +#define RT5663_EN_IRQ_INLINE_MASK (0x1 << 3) +#define RT5663_EN_IRQ_INLINE_SHIFT 3 +#define RT5663_EN_IRQ_INLINE_NOR (0x1 << 3) +#define RT5663_EN_IRQ_INLINE_BYP (0x0 << 3) + +/* GPIO Control 1 (0x00c0) */ +#define RT5663_GPIO1_TYPE_MASK (0x1 << 15) +#define RT5663_GPIO1_TYPE_SHIFT 15 +#define RT5663_GPIO1_TYPE_EN (0x1 << 15) +#define RT5663_GPIO1_TYPE_DIS (0x0 << 15) + +/* IRQ Control 1 (0x00c1) */ +#define RT5663_EN_IRQ_JD1_MASK (0x1 << 6) +#define RT5663_EN_IRQ_JD1_SHIFT 6 +#define RT5663_EN_IRQ_JD1_EN (0x1 << 6) +#define RT5663_EN_IRQ_JD1_DIS (0x0 << 6) +#define RT5663_SEL_GPIO1_MASK (0x1 << 2) +#define RT5663_SEL_GPIO1_SHIFT 6 +#define RT5663_SEL_GPIO1_EN (0x1 << 2) +#define RT5663_SEL_GPIO1_DIS (0x0 << 2) + +/* Inline Command Function 2 (0x00dc) */ +#define RT5663_PWR_MIC_DET_MASK (0x1) +#define RT5663_PWR_MIC_DET_SHIFT 0 +#define RT5663_PWR_MIC_DET_ON (0x1) +#define RT5663_PWR_MIC_DET_OFF (0x0) + +/* Embeeded Jack and Type Detection Control 1 (0x00e6)*/ +#define RT5663_CBJ_DET_MASK (0x1 << 15) +#define RT5663_CBJ_DET_SHIFT 15 +#define RT5663_CBJ_DET_DIS (0x0 << 15) +#define RT5663_CBJ_DET_EN (0x1 << 15) +#define RT5663_EXT_JD_MASK (0x1 << 11) +#define RT5663_EXT_JD_SHIFT 11 +#define RT5663_EXT_JD_EN (0x1 << 11) +#define RT5663_EXT_JD_DIS (0x0 << 11) +#define RT5663_POL_EXT_JD_MASK (0x1 << 10) +#define RT5663_POL_EXT_JD_SHIFT 10 +#define RT5663_POL_EXT_JD_EN (0x1 << 10) +#define RT5663_POL_EXT_JD_DIS (0x0 << 10) +#define RT5663_EM_JD_MASK (0x1 << 7) +#define RT5663_EM_JD_SHIFT 7 +#define RT5663_EM_JD_NOR (0x1 << 7) +#define RT5663_EM_JD_RST (0x0 << 7) + +/* DACREF LDO Control (0x0112)*/ +#define RT5663_PWR_LDO_DACREFL_MASK (0x1 << 9) +#define RT5663_PWR_LDO_DACREFL_SHIFT 9 +#define RT5663_PWR_LDO_DACREFR_MASK (0x1 << 1) +#define RT5663_PWR_LDO_DACREFR_SHIFT 1 + +/* Stereo Dynamic Range Enhancement Control 9 (0x0168, 0x0169)*/ +#define RT5663_DRE_GAIN_HP_MASK (0x1f) +#define RT5663_DRE_GAIN_HP_SHIFT 0 + +/* Combo Jack Control (0x0250) */ +#define RT5663_INBUF_CBJ_BST1_MASK (0x1 << 11) +#define RT5663_INBUF_CBJ_BST1_SHIFT 11 +#define RT5663_INBUF_CBJ_BST1_ON (0x1 << 11) +#define RT5663_INBUF_CBJ_BST1_OFF (0x0 << 11) +#define RT5663_CBJ_SENSE_BST1_MASK (0x1 << 10) +#define RT5663_CBJ_SENSE_BST1_SHIFT 10 +#define RT5663_CBJ_SENSE_BST1_L (0x1 << 10) +#define RT5663_CBJ_SENSE_BST1_R (0x0 << 10) + +/* Combo Jack Control (0x0251) */ +#define RT5663_GAIN_BST1_MASK (0xf) +#define RT5663_GAIN_BST1_SHIFT 0 + +/* Dummy register 1 (0x02fa) */ +#define RT5663_EMB_CLK_MASK (0x1 << 9) +#define RT5663_EMB_CLK_SHIFT 9 +#define RT5663_EMB_CLK_EN (0x1 << 9) +#define RT5663_EMB_CLK_DIS (0x0 << 9) +#define RT5663_HPA_CPL_BIAS_MASK (0x7 << 6) +#define RT5663_HPA_CPL_BIAS_SHIFT 6 +#define RT5663_HPA_CPL_BIAS_0_5 (0x0 << 6) +#define RT5663_HPA_CPL_BIAS_1 (0x1 << 6) +#define RT5663_HPA_CPL_BIAS_2 (0x2 << 6) +#define RT5663_HPA_CPL_BIAS_3 (0x3 << 6) +#define RT5663_HPA_CPL_BIAS_4_1 (0x4 << 6) +#define RT5663_HPA_CPL_BIAS_4_2 (0x5 << 6) +#define RT5663_HPA_CPL_BIAS_6 (0x6 << 6) +#define RT5663_HPA_CPL_BIAS_8 (0x7 << 6) +#define RT5663_HPA_CPR_BIAS_MASK (0x7 << 3) +#define RT5663_HPA_CPR_BIAS_SHIFT 3 +#define RT5663_HPA_CPR_BIAS_0_5 (0x0 << 3) +#define RT5663_HPA_CPR_BIAS_1 (0x1 << 3) +#define RT5663_HPA_CPR_BIAS_2 (0x2 << 3) +#define RT5663_HPA_CPR_BIAS_3 (0x3 << 3) +#define RT5663_HPA_CPR_BIAS_4_1 (0x4 << 3) +#define RT5663_HPA_CPR_BIAS_4_2 (0x5 << 3) +#define RT5663_HPA_CPR_BIAS_6 (0x6 << 3) +#define RT5663_HPA_CPR_BIAS_8 (0x7 << 3) +#define RT5663_DUMMY_BIAS_MASK (0x7) +#define RT5663_DUMMY_BIAS_SHIFT 0 +#define RT5663_DUMMY_BIAS_0_5 (0x0) +#define RT5663_DUMMY_BIAS_1 (0x1) +#define RT5663_DUMMY_BIAS_2 (0x2) +#define RT5663_DUMMY_BIAS_3 (0x3) +#define RT5663_DUMMY_BIAS_4_1 (0x4) +#define RT5663_DUMMY_BIAS_4_2 (0x5) +#define RT5663_DUMMY_BIAS_6 (0x6) +#define RT5663_DUMMY_BIAS_8 (0x7) + + +/* System Clock Source */ +enum { + RT5663_SCLK_S_MCLK, + RT5663_SCLK_S_PLL1, + RT5663_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5663_PLL1_S_MCLK, + RT5663_PLL1_S_BCLK1, +}; + +enum { + RT5663_AIF, + RT5663_AIFS, +}; + +/* asrc clock source */ +enum { + RT5663_CLK_SEL_SYS = 0x0, + RT5663_CLK_SEL_I2S1_ASRC = 0x1, +}; + +/* filter mask */ +enum { + RT5663_DA_STEREO_FILTER = 0x1, + RT5663_AD_STEREO_FILTER = 0x2, +}; + +int rt5663_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +#endif /* __RT5663_H__ */ diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c new file mode 100644 index 0000000000..a39de4a7df --- /dev/null +++ b/sound/soc/codecs/rt5665.c @@ -0,0 +1,4980 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5665.c -- RT5665/RT5658 ALSA SoC audio codec driver + * + * Copyright 2016 Realtek Semiconductor Corp. + * Author: Bard Liao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5665.h" + +#define RT5665_NUM_SUPPLIES 3 + +static const char *rt5665_supply_names[RT5665_NUM_SUPPLIES] = { + "AVDD", + "MICVDD", + "VBAT", +}; + +struct rt5665_priv { + struct snd_soc_component *component; + struct rt5665_platform_data pdata; + struct regmap *regmap; + struct gpio_desc *gpiod_ldo1_en; + struct gpio_desc *gpiod_reset; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5665_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work calibrate_work; + struct delayed_work jd_check_work; + struct mutex calibrate_mutex; + + int sysclk; + int sysclk_src; + int lrck[RT5665_AIFS]; + int bclk[RT5665_AIFS]; + int master[RT5665_AIFS]; + int id; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; + int irq_work_delay_time; + unsigned int sar_adc_value; + bool calibration_done; +}; + +static const struct reg_default rt5665_reg[] = { + {0x0000, 0x0000}, + {0x0001, 0xc8c8}, + {0x0002, 0x8080}, + {0x0003, 0x8000}, + {0x0004, 0xc80a}, + {0x0005, 0x0000}, + {0x0006, 0x0000}, + {0x0007, 0x0000}, + {0x000a, 0x0000}, + {0x000b, 0x0000}, + {0x000c, 0x0000}, + {0x000d, 0x0000}, + {0x000f, 0x0808}, + {0x0010, 0x4040}, + {0x0011, 0x0000}, + {0x0012, 0x1404}, + {0x0013, 0x1000}, + {0x0014, 0xa00a}, + {0x0015, 0x0404}, + {0x0016, 0x0404}, + {0x0017, 0x0011}, + {0x0018, 0xafaf}, + {0x0019, 0xafaf}, + {0x001a, 0xafaf}, + {0x001b, 0x0011}, + {0x001c, 0x2f2f}, + {0x001d, 0x2f2f}, + {0x001e, 0x2f2f}, + {0x001f, 0x0000}, + {0x0020, 0x0000}, + {0x0021, 0x0000}, + {0x0022, 0x5757}, + {0x0023, 0x0039}, + {0x0026, 0xc0c0}, + {0x0027, 0xc0c0}, + {0x0028, 0xc0c0}, + {0x0029, 0x8080}, + {0x002a, 0xaaaa}, + {0x002b, 0xaaaa}, + {0x002c, 0xaba8}, + {0x002d, 0x0000}, + {0x002e, 0x0000}, + {0x002f, 0x0000}, + {0x0030, 0x0000}, + {0x0031, 0x5000}, + {0x0032, 0x0000}, + {0x0033, 0x0000}, + {0x0034, 0x0000}, + {0x0035, 0x0000}, + {0x003a, 0x0000}, + {0x003b, 0x0000}, + {0x003c, 0x00ff}, + {0x003d, 0x0000}, + {0x003e, 0x00ff}, + {0x003f, 0x0000}, + {0x0040, 0x0000}, + {0x0041, 0x00ff}, + {0x0042, 0x0000}, + {0x0043, 0x00ff}, + {0x0044, 0x0c0c}, + {0x0049, 0xc00b}, + {0x004a, 0x0000}, + {0x004b, 0x031f}, + {0x004d, 0x0000}, + {0x004e, 0x001f}, + {0x004f, 0x0000}, + {0x0050, 0x001f}, + {0x0052, 0xf000}, + {0x0061, 0x0000}, + {0x0062, 0x0000}, + {0x0063, 0x003e}, + {0x0064, 0x0000}, + {0x0065, 0x0000}, + {0x0066, 0x003f}, + {0x0067, 0x0000}, + {0x006b, 0x0000}, + {0x006d, 0xff00}, + {0x006e, 0x2808}, + {0x006f, 0x000a}, + {0x0070, 0x8000}, + {0x0071, 0x8000}, + {0x0072, 0x8000}, + {0x0073, 0x7000}, + {0x0074, 0x7770}, + {0x0075, 0x0002}, + {0x0076, 0x0001}, + {0x0078, 0x00f0}, + {0x0079, 0x0000}, + {0x007a, 0x0000}, + {0x007b, 0x0000}, + {0x007c, 0x0000}, + {0x007d, 0x0123}, + {0x007e, 0x4500}, + {0x007f, 0x8003}, + {0x0080, 0x0000}, + {0x0081, 0x0000}, + {0x0082, 0x0000}, + {0x0083, 0x0000}, + {0x0084, 0x0000}, + {0x0085, 0x0000}, + {0x0086, 0x0008}, + {0x0087, 0x0000}, + {0x0088, 0x0000}, + {0x0089, 0x0000}, + {0x008a, 0x0000}, + {0x008b, 0x0000}, + {0x008c, 0x0003}, + {0x008e, 0x0060}, + {0x008f, 0x1000}, + {0x0091, 0x0c26}, + {0x0092, 0x0073}, + {0x0093, 0x0000}, + {0x0094, 0x0080}, + {0x0098, 0x0000}, + {0x0099, 0x0000}, + {0x009a, 0x0007}, + {0x009f, 0x0000}, + {0x00a0, 0x0000}, + {0x00a1, 0x0002}, + {0x00a2, 0x0001}, + {0x00a3, 0x0002}, + {0x00a4, 0x0001}, + {0x00ae, 0x2040}, + {0x00af, 0x0000}, + {0x00b6, 0x0000}, + {0x00b7, 0x0000}, + {0x00b8, 0x0000}, + {0x00b9, 0x0000}, + {0x00ba, 0x0002}, + {0x00bb, 0x0000}, + {0x00be, 0x0000}, + {0x00c0, 0x0000}, + {0x00c1, 0x0aaa}, + {0x00c2, 0xaa80}, + {0x00c3, 0x0003}, + {0x00c4, 0x0000}, + {0x00d0, 0x0000}, + {0x00d1, 0x2244}, + {0x00d3, 0x3300}, + {0x00d4, 0x2200}, + {0x00d9, 0x0809}, + {0x00da, 0x0000}, + {0x00db, 0x0008}, + {0x00dc, 0x00c0}, + {0x00dd, 0x6724}, + {0x00de, 0x3131}, + {0x00df, 0x0008}, + {0x00e0, 0x4000}, + {0x00e1, 0x3131}, + {0x00e2, 0x600c}, + {0x00ea, 0xb320}, + {0x00eb, 0x0000}, + {0x00ec, 0xb300}, + {0x00ed, 0x0000}, + {0x00ee, 0xb320}, + {0x00ef, 0x0000}, + {0x00f0, 0x0201}, + {0x00f1, 0x0ddd}, + {0x00f2, 0x0ddd}, + {0x00f6, 0x0000}, + {0x00f7, 0x0000}, + {0x00f8, 0x0000}, + {0x00fa, 0x0000}, + {0x00fb, 0x0000}, + {0x00fc, 0x0000}, + {0x00fd, 0x0000}, + {0x00fe, 0x10ec}, + {0x00ff, 0x6451}, + {0x0100, 0xaaaa}, + {0x0101, 0x000a}, + {0x010a, 0xaaaa}, + {0x010b, 0xa0a0}, + {0x010c, 0xaeae}, + {0x010d, 0xaaaa}, + {0x010e, 0xaaaa}, + {0x010f, 0xaaaa}, + {0x0110, 0xe002}, + {0x0111, 0xa402}, + {0x0112, 0xaaaa}, + {0x0113, 0x2000}, + {0x0117, 0x0f00}, + {0x0125, 0x0410}, + {0x0132, 0x0000}, + {0x0133, 0x0000}, + {0x0137, 0x5540}, + {0x0138, 0x3700}, + {0x0139, 0x79a1}, + {0x013a, 0x2020}, + {0x013b, 0x2020}, + {0x013c, 0x2005}, + {0x013f, 0x0000}, + {0x0145, 0x0002}, + {0x0146, 0x0000}, + {0x0147, 0x0000}, + {0x0148, 0x0000}, + {0x0150, 0x0000}, + {0x0160, 0x4eff}, + {0x0161, 0x0080}, + {0x0162, 0x0200}, + {0x0163, 0x0800}, + {0x0164, 0x0000}, + {0x0165, 0x0000}, + {0x0166, 0x0000}, + {0x0167, 0x000f}, + {0x0170, 0x4e87}, + {0x0171, 0x0080}, + {0x0172, 0x0200}, + {0x0173, 0x0800}, + {0x0174, 0x00ff}, + {0x0175, 0x0000}, + {0x0190, 0x413d}, + {0x0191, 0x4139}, + {0x0192, 0x4135}, + {0x0193, 0x413d}, + {0x0194, 0x0000}, + {0x0195, 0x0000}, + {0x0196, 0x0000}, + {0x0197, 0x0000}, + {0x0198, 0x0000}, + {0x0199, 0x0000}, + {0x01a0, 0x1e64}, + {0x01a1, 0x06a3}, + {0x01a2, 0x0000}, + {0x01a3, 0x0000}, + {0x01a4, 0x0000}, + {0x01a5, 0x0000}, + {0x01a6, 0x0000}, + {0x01a7, 0x8000}, + {0x01a8, 0x0000}, + {0x01a9, 0x0000}, + {0x01aa, 0x0000}, + {0x01ab, 0x0000}, + {0x01b5, 0x0000}, + {0x01b6, 0x01c3}, + {0x01b7, 0x02a0}, + {0x01b8, 0x03e9}, + {0x01b9, 0x1389}, + {0x01ba, 0xc351}, + {0x01bb, 0x0009}, + {0x01bc, 0x0018}, + {0x01bd, 0x002a}, + {0x01be, 0x004c}, + {0x01bf, 0x0097}, + {0x01c0, 0x433d}, + {0x01c1, 0x0000}, + {0x01c2, 0x0000}, + {0x01c3, 0x0000}, + {0x01c4, 0x0000}, + {0x01c5, 0x0000}, + {0x01c6, 0x0000}, + {0x01c7, 0x0000}, + {0x01c8, 0x40af}, + {0x01c9, 0x0702}, + {0x01ca, 0x0000}, + {0x01cb, 0x0000}, + {0x01cc, 0x5757}, + {0x01cd, 0x5757}, + {0x01ce, 0x5757}, + {0x01cf, 0x5757}, + {0x01d0, 0x5757}, + {0x01d1, 0x5757}, + {0x01d2, 0x5757}, + {0x01d3, 0x5757}, + {0x01d4, 0x5757}, + {0x01d5, 0x5757}, + {0x01d6, 0x003c}, + {0x01da, 0x0000}, + {0x01db, 0x0000}, + {0x01dc, 0x0000}, + {0x01de, 0x7c00}, + {0x01df, 0x0320}, + {0x01e0, 0x06a1}, + {0x01e1, 0x0000}, + {0x01e2, 0x0000}, + {0x01e3, 0x0000}, + {0x01e4, 0x0000}, + {0x01e6, 0x0001}, + {0x01e7, 0x0000}, + {0x01e8, 0x0000}, + {0x01ea, 0xbf3f}, + {0x01eb, 0x0000}, + {0x01ec, 0x0000}, + {0x01ed, 0x0000}, + {0x01ee, 0x0000}, + {0x01ef, 0x0000}, + {0x01f0, 0x0000}, + {0x01f1, 0x0000}, + {0x01f2, 0x0000}, + {0x01f3, 0x0000}, + {0x01f4, 0x0000}, + {0x0200, 0x0000}, + {0x0201, 0x0000}, + {0x0202, 0x0000}, + {0x0203, 0x0000}, + {0x0204, 0x0000}, + {0x0205, 0x0000}, + {0x0206, 0x0000}, + {0x0207, 0x0000}, + {0x0208, 0x0000}, + {0x0210, 0x60b1}, + {0x0211, 0xa005}, + {0x0212, 0x024c}, + {0x0213, 0xf7ff}, + {0x0214, 0x024c}, + {0x0215, 0x0102}, + {0x0216, 0x00a3}, + {0x0217, 0x0048}, + {0x0218, 0xa2c0}, + {0x0219, 0x0400}, + {0x021a, 0x00c8}, + {0x021b, 0x00c0}, + {0x02ff, 0x0110}, + {0x0300, 0x001f}, + {0x0301, 0x032c}, + {0x0302, 0x5f21}, + {0x0303, 0x4000}, + {0x0304, 0x4000}, + {0x0305, 0x06d5}, + {0x0306, 0x8000}, + {0x0307, 0x0700}, + {0x0310, 0x4560}, + {0x0311, 0xa4a8}, + {0x0312, 0x7418}, + {0x0313, 0x0000}, + {0x0314, 0x0006}, + {0x0315, 0xffff}, + {0x0316, 0xc400}, + {0x0317, 0x0000}, + {0x0330, 0x00a6}, + {0x0331, 0x04c3}, + {0x0332, 0x27c8}, + {0x0333, 0xbf50}, + {0x0334, 0x0045}, + {0x0335, 0x0007}, + {0x0336, 0x7418}, + {0x0337, 0x0501}, + {0x0338, 0x0000}, + {0x0339, 0x0010}, + {0x033a, 0x1010}, + {0x03c0, 0x7e00}, + {0x03c1, 0x8000}, + {0x03c2, 0x8000}, + {0x03c3, 0x8000}, + {0x03c4, 0x8000}, + {0x03c5, 0x8000}, + {0x03c6, 0x8000}, + {0x03c7, 0x8000}, + {0x03c8, 0x8000}, + {0x03c9, 0x8000}, + {0x03ca, 0x8000}, + {0x03cb, 0x8000}, + {0x03cc, 0x8000}, + {0x03d0, 0x0000}, + {0x03d1, 0x0000}, + {0x03d2, 0x0000}, + {0x03d3, 0x0000}, + {0x03d4, 0x2000}, + {0x03d5, 0x2000}, + {0x03d6, 0x0000}, + {0x03d7, 0x0000}, + {0x03d8, 0x2000}, + {0x03d9, 0x2000}, + {0x03da, 0x2000}, + {0x03db, 0x2000}, + {0x03dc, 0x0000}, + {0x03dd, 0x0000}, + {0x03de, 0x0000}, + {0x03df, 0x2000}, + {0x03e0, 0x0000}, + {0x03e1, 0x0000}, + {0x03e2, 0x0000}, + {0x03e3, 0x0000}, + {0x03e4, 0x0000}, + {0x03e5, 0x0000}, + {0x03e6, 0x0000}, + {0x03e7, 0x0000}, + {0x03e8, 0x0000}, + {0x03e9, 0x0000}, + {0x03ea, 0x0000}, + {0x03eb, 0x0000}, + {0x03ec, 0x0000}, + {0x03ed, 0x0000}, + {0x03ee, 0x0000}, + {0x03ef, 0x0000}, + {0x03f0, 0x0800}, + {0x03f1, 0x0800}, + {0x03f2, 0x0800}, + {0x03f3, 0x0800}, +}; + +static bool rt5665_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5665_RESET: + case RT5665_EJD_CTRL_2: + case RT5665_GPIO_STA: + case RT5665_INT_ST_1: + case RT5665_IL_CMD_1: + case RT5665_4BTN_IL_CMD_1: + case RT5665_PSV_IL_CMD_1: + case RT5665_AJD1_CTRL: + case RT5665_JD_CTRL_3: + case RT5665_STO_NG2_CTRL_1: + case RT5665_SAR_IL_CMD_4: + case RT5665_DEVICE_ID: + case RT5665_STO1_DAC_SIL_DET ... RT5665_STO2_DAC_SIL_DET: + case RT5665_MONO_AMP_CALIB_STA1 ... RT5665_MONO_AMP_CALIB_STA6: + case RT5665_HP_IMP_SENS_CTRL_12 ... RT5665_HP_IMP_SENS_CTRL_15: + case RT5665_HP_CALIB_STA_1 ... RT5665_HP_CALIB_STA_11: + return true; + default: + return false; + } +} + +static bool rt5665_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5665_RESET: + case RT5665_VENDOR_ID: + case RT5665_VENDOR_ID_1: + case RT5665_DEVICE_ID: + case RT5665_LOUT: + case RT5665_HP_CTRL_1: + case RT5665_HP_CTRL_2: + case RT5665_MONO_OUT: + case RT5665_HPL_GAIN: + case RT5665_HPR_GAIN: + case RT5665_MONO_GAIN: + case RT5665_CAL_BST_CTRL: + case RT5665_CBJ_BST_CTRL: + case RT5665_IN1_IN2: + case RT5665_IN3_IN4: + case RT5665_INL1_INR1_VOL: + case RT5665_EJD_CTRL_1: + case RT5665_EJD_CTRL_2: + case RT5665_EJD_CTRL_3: + case RT5665_EJD_CTRL_4: + case RT5665_EJD_CTRL_5: + case RT5665_EJD_CTRL_6: + case RT5665_EJD_CTRL_7: + case RT5665_DAC2_CTRL: + case RT5665_DAC2_DIG_VOL: + case RT5665_DAC1_DIG_VOL: + case RT5665_DAC3_DIG_VOL: + case RT5665_DAC3_CTRL: + case RT5665_STO1_ADC_DIG_VOL: + case RT5665_MONO_ADC_DIG_VOL: + case RT5665_STO2_ADC_DIG_VOL: + case RT5665_STO1_ADC_BOOST: + case RT5665_MONO_ADC_BOOST: + case RT5665_STO2_ADC_BOOST: + case RT5665_HP_IMP_GAIN_1: + case RT5665_HP_IMP_GAIN_2: + case RT5665_STO1_ADC_MIXER: + case RT5665_MONO_ADC_MIXER: + case RT5665_STO2_ADC_MIXER: + case RT5665_AD_DA_MIXER: + case RT5665_STO1_DAC_MIXER: + case RT5665_MONO_DAC_MIXER: + case RT5665_STO2_DAC_MIXER: + case RT5665_A_DAC1_MUX: + case RT5665_A_DAC2_MUX: + case RT5665_DIG_INF2_DATA: + case RT5665_DIG_INF3_DATA: + case RT5665_PDM_OUT_CTRL: + case RT5665_PDM_DATA_CTRL_1: + case RT5665_PDM_DATA_CTRL_2: + case RT5665_PDM_DATA_CTRL_3: + case RT5665_PDM_DATA_CTRL_4: + case RT5665_REC1_GAIN: + case RT5665_REC1_L1_MIXER: + case RT5665_REC1_L2_MIXER: + case RT5665_REC1_R1_MIXER: + case RT5665_REC1_R2_MIXER: + case RT5665_REC2_GAIN: + case RT5665_REC2_L1_MIXER: + case RT5665_REC2_L2_MIXER: + case RT5665_REC2_R1_MIXER: + case RT5665_REC2_R2_MIXER: + case RT5665_CAL_REC: + case RT5665_ALC_BACK_GAIN: + case RT5665_MONOMIX_GAIN: + case RT5665_MONOMIX_IN_GAIN: + case RT5665_OUT_L_GAIN: + case RT5665_OUT_L_MIXER: + case RT5665_OUT_R_GAIN: + case RT5665_OUT_R_MIXER: + case RT5665_LOUT_MIXER: + case RT5665_PWR_DIG_1: + case RT5665_PWR_DIG_2: + case RT5665_PWR_ANLG_1: + case RT5665_PWR_ANLG_2: + case RT5665_PWR_ANLG_3: + case RT5665_PWR_MIXER: + case RT5665_PWR_VOL: + case RT5665_CLK_DET: + case RT5665_HPF_CTRL1: + case RT5665_DMIC_CTRL_1: + case RT5665_DMIC_CTRL_2: + case RT5665_I2S1_SDP: + case RT5665_I2S2_SDP: + case RT5665_I2S3_SDP: + case RT5665_ADDA_CLK_1: + case RT5665_ADDA_CLK_2: + case RT5665_I2S1_F_DIV_CTRL_1: + case RT5665_I2S1_F_DIV_CTRL_2: + case RT5665_TDM_CTRL_1: + case RT5665_TDM_CTRL_2: + case RT5665_TDM_CTRL_3: + case RT5665_TDM_CTRL_4: + case RT5665_TDM_CTRL_5: + case RT5665_TDM_CTRL_6: + case RT5665_TDM_CTRL_7: + case RT5665_TDM_CTRL_8: + case RT5665_GLB_CLK: + case RT5665_PLL_CTRL_1: + case RT5665_PLL_CTRL_2: + case RT5665_ASRC_1: + case RT5665_ASRC_2: + case RT5665_ASRC_3: + case RT5665_ASRC_4: + case RT5665_ASRC_5: + case RT5665_ASRC_6: + case RT5665_ASRC_7: + case RT5665_ASRC_8: + case RT5665_ASRC_9: + case RT5665_ASRC_10: + case RT5665_DEPOP_1: + case RT5665_DEPOP_2: + case RT5665_HP_CHARGE_PUMP_1: + case RT5665_HP_CHARGE_PUMP_2: + case RT5665_MICBIAS_1: + case RT5665_MICBIAS_2: + case RT5665_ASRC_12: + case RT5665_ASRC_13: + case RT5665_ASRC_14: + case RT5665_RC_CLK_CTRL: + case RT5665_I2S_M_CLK_CTRL_1: + case RT5665_I2S2_F_DIV_CTRL_1: + case RT5665_I2S2_F_DIV_CTRL_2: + case RT5665_I2S3_F_DIV_CTRL_1: + case RT5665_I2S3_F_DIV_CTRL_2: + case RT5665_EQ_CTRL_1: + case RT5665_EQ_CTRL_2: + case RT5665_IRQ_CTRL_1: + case RT5665_IRQ_CTRL_2: + case RT5665_IRQ_CTRL_3: + case RT5665_IRQ_CTRL_4: + case RT5665_IRQ_CTRL_5: + case RT5665_IRQ_CTRL_6: + case RT5665_INT_ST_1: + case RT5665_GPIO_CTRL_1: + case RT5665_GPIO_CTRL_2: + case RT5665_GPIO_CTRL_3: + case RT5665_GPIO_CTRL_4: + case RT5665_GPIO_STA: + case RT5665_HP_AMP_DET_CTRL_1: + case RT5665_HP_AMP_DET_CTRL_2: + case RT5665_MID_HP_AMP_DET: + case RT5665_LOW_HP_AMP_DET: + case RT5665_SV_ZCD_1: + case RT5665_SV_ZCD_2: + case RT5665_IL_CMD_1: + case RT5665_IL_CMD_2: + case RT5665_IL_CMD_3: + case RT5665_IL_CMD_4: + case RT5665_4BTN_IL_CMD_1: + case RT5665_4BTN_IL_CMD_2: + case RT5665_4BTN_IL_CMD_3: + case RT5665_PSV_IL_CMD_1: + case RT5665_ADC_STO1_HP_CTRL_1: + case RT5665_ADC_STO1_HP_CTRL_2: + case RT5665_ADC_MONO_HP_CTRL_1: + case RT5665_ADC_MONO_HP_CTRL_2: + case RT5665_ADC_STO2_HP_CTRL_1: + case RT5665_ADC_STO2_HP_CTRL_2: + case RT5665_AJD1_CTRL: + case RT5665_JD1_THD: + case RT5665_JD2_THD: + case RT5665_JD_CTRL_1: + case RT5665_JD_CTRL_2: + case RT5665_JD_CTRL_3: + case RT5665_DIG_MISC: + case RT5665_DUMMY_2: + case RT5665_DUMMY_3: + case RT5665_DAC_ADC_DIG_VOL1: + case RT5665_DAC_ADC_DIG_VOL2: + case RT5665_BIAS_CUR_CTRL_1: + case RT5665_BIAS_CUR_CTRL_2: + case RT5665_BIAS_CUR_CTRL_3: + case RT5665_BIAS_CUR_CTRL_4: + case RT5665_BIAS_CUR_CTRL_5: + case RT5665_BIAS_CUR_CTRL_6: + case RT5665_BIAS_CUR_CTRL_7: + case RT5665_BIAS_CUR_CTRL_8: + case RT5665_BIAS_CUR_CTRL_9: + case RT5665_BIAS_CUR_CTRL_10: + case RT5665_VREF_REC_OP_FB_CAP_CTRL: + case RT5665_CHARGE_PUMP_1: + case RT5665_DIG_IN_CTRL_1: + case RT5665_DIG_IN_CTRL_2: + case RT5665_PAD_DRIVING_CTRL: + case RT5665_SOFT_RAMP_DEPOP: + case RT5665_PLL: + case RT5665_CHOP_DAC: + case RT5665_CHOP_ADC: + case RT5665_CALIB_ADC_CTRL: + case RT5665_VOL_TEST: + case RT5665_TEST_MODE_CTRL_1: + case RT5665_TEST_MODE_CTRL_2: + case RT5665_TEST_MODE_CTRL_3: + case RT5665_TEST_MODE_CTRL_4: + case RT5665_BASSBACK_CTRL: + case RT5665_STO_NG2_CTRL_1: + case RT5665_STO_NG2_CTRL_2: + case RT5665_STO_NG2_CTRL_3: + case RT5665_STO_NG2_CTRL_4: + case RT5665_STO_NG2_CTRL_5: + case RT5665_STO_NG2_CTRL_6: + case RT5665_STO_NG2_CTRL_7: + case RT5665_STO_NG2_CTRL_8: + case RT5665_MONO_NG2_CTRL_1: + case RT5665_MONO_NG2_CTRL_2: + case RT5665_MONO_NG2_CTRL_3: + case RT5665_MONO_NG2_CTRL_4: + case RT5665_MONO_NG2_CTRL_5: + case RT5665_MONO_NG2_CTRL_6: + case RT5665_STO1_DAC_SIL_DET: + case RT5665_MONOL_DAC_SIL_DET: + case RT5665_MONOR_DAC_SIL_DET: + case RT5665_STO2_DAC_SIL_DET: + case RT5665_SIL_PSV_CTRL1: + case RT5665_SIL_PSV_CTRL2: + case RT5665_SIL_PSV_CTRL3: + case RT5665_SIL_PSV_CTRL4: + case RT5665_SIL_PSV_CTRL5: + case RT5665_SIL_PSV_CTRL6: + case RT5665_MONO_AMP_CALIB_CTRL_1: + case RT5665_MONO_AMP_CALIB_CTRL_2: + case RT5665_MONO_AMP_CALIB_CTRL_3: + case RT5665_MONO_AMP_CALIB_CTRL_4: + case RT5665_MONO_AMP_CALIB_CTRL_5: + case RT5665_MONO_AMP_CALIB_CTRL_6: + case RT5665_MONO_AMP_CALIB_CTRL_7: + case RT5665_MONO_AMP_CALIB_STA1: + case RT5665_MONO_AMP_CALIB_STA2: + case RT5665_MONO_AMP_CALIB_STA3: + case RT5665_MONO_AMP_CALIB_STA4: + case RT5665_MONO_AMP_CALIB_STA6: + case RT5665_HP_IMP_SENS_CTRL_01: + case RT5665_HP_IMP_SENS_CTRL_02: + case RT5665_HP_IMP_SENS_CTRL_03: + case RT5665_HP_IMP_SENS_CTRL_04: + case RT5665_HP_IMP_SENS_CTRL_05: + case RT5665_HP_IMP_SENS_CTRL_06: + case RT5665_HP_IMP_SENS_CTRL_07: + case RT5665_HP_IMP_SENS_CTRL_08: + case RT5665_HP_IMP_SENS_CTRL_09: + case RT5665_HP_IMP_SENS_CTRL_10: + case RT5665_HP_IMP_SENS_CTRL_11: + case RT5665_HP_IMP_SENS_CTRL_12: + case RT5665_HP_IMP_SENS_CTRL_13: + case RT5665_HP_IMP_SENS_CTRL_14: + case RT5665_HP_IMP_SENS_CTRL_15: + case RT5665_HP_IMP_SENS_CTRL_16: + case RT5665_HP_IMP_SENS_CTRL_17: + case RT5665_HP_IMP_SENS_CTRL_18: + case RT5665_HP_IMP_SENS_CTRL_19: + case RT5665_HP_IMP_SENS_CTRL_20: + case RT5665_HP_IMP_SENS_CTRL_21: + case RT5665_HP_IMP_SENS_CTRL_22: + case RT5665_HP_IMP_SENS_CTRL_23: + case RT5665_HP_IMP_SENS_CTRL_24: + case RT5665_HP_IMP_SENS_CTRL_25: + case RT5665_HP_IMP_SENS_CTRL_26: + case RT5665_HP_IMP_SENS_CTRL_27: + case RT5665_HP_IMP_SENS_CTRL_28: + case RT5665_HP_IMP_SENS_CTRL_29: + case RT5665_HP_IMP_SENS_CTRL_30: + case RT5665_HP_IMP_SENS_CTRL_31: + case RT5665_HP_IMP_SENS_CTRL_32: + case RT5665_HP_IMP_SENS_CTRL_33: + case RT5665_HP_IMP_SENS_CTRL_34: + case RT5665_HP_LOGIC_CTRL_1: + case RT5665_HP_LOGIC_CTRL_2: + case RT5665_HP_LOGIC_CTRL_3: + case RT5665_HP_CALIB_CTRL_1: + case RT5665_HP_CALIB_CTRL_2: + case RT5665_HP_CALIB_CTRL_3: + case RT5665_HP_CALIB_CTRL_4: + case RT5665_HP_CALIB_CTRL_5: + case RT5665_HP_CALIB_CTRL_6: + case RT5665_HP_CALIB_CTRL_7: + case RT5665_HP_CALIB_CTRL_9: + case RT5665_HP_CALIB_CTRL_10: + case RT5665_HP_CALIB_CTRL_11: + case RT5665_HP_CALIB_STA_1: + case RT5665_HP_CALIB_STA_2: + case RT5665_HP_CALIB_STA_3: + case RT5665_HP_CALIB_STA_4: + case RT5665_HP_CALIB_STA_5: + case RT5665_HP_CALIB_STA_6: + case RT5665_HP_CALIB_STA_7: + case RT5665_HP_CALIB_STA_8: + case RT5665_HP_CALIB_STA_9: + case RT5665_HP_CALIB_STA_10: + case RT5665_HP_CALIB_STA_11: + case RT5665_PGM_TAB_CTRL1: + case RT5665_PGM_TAB_CTRL2: + case RT5665_PGM_TAB_CTRL3: + case RT5665_PGM_TAB_CTRL4: + case RT5665_PGM_TAB_CTRL5: + case RT5665_PGM_TAB_CTRL6: + case RT5665_PGM_TAB_CTRL7: + case RT5665_PGM_TAB_CTRL8: + case RT5665_PGM_TAB_CTRL9: + case RT5665_SAR_IL_CMD_1: + case RT5665_SAR_IL_CMD_2: + case RT5665_SAR_IL_CMD_3: + case RT5665_SAR_IL_CMD_4: + case RT5665_SAR_IL_CMD_5: + case RT5665_SAR_IL_CMD_6: + case RT5665_SAR_IL_CMD_7: + case RT5665_SAR_IL_CMD_8: + case RT5665_SAR_IL_CMD_9: + case RT5665_SAR_IL_CMD_10: + case RT5665_SAR_IL_CMD_11: + case RT5665_SAR_IL_CMD_12: + case RT5665_DRC1_CTRL_0: + case RT5665_DRC1_CTRL_1: + case RT5665_DRC1_CTRL_2: + case RT5665_DRC1_CTRL_3: + case RT5665_DRC1_CTRL_4: + case RT5665_DRC1_CTRL_5: + case RT5665_DRC1_CTRL_6: + case RT5665_DRC1_HARD_LMT_CTRL_1: + case RT5665_DRC1_HARD_LMT_CTRL_2: + case RT5665_DRC1_PRIV_1: + case RT5665_DRC1_PRIV_2: + case RT5665_DRC1_PRIV_3: + case RT5665_DRC1_PRIV_4: + case RT5665_DRC1_PRIV_5: + case RT5665_DRC1_PRIV_6: + case RT5665_DRC1_PRIV_7: + case RT5665_DRC1_PRIV_8: + case RT5665_ALC_PGA_CTRL_1: + case RT5665_ALC_PGA_CTRL_2: + case RT5665_ALC_PGA_CTRL_3: + case RT5665_ALC_PGA_CTRL_4: + case RT5665_ALC_PGA_CTRL_5: + case RT5665_ALC_PGA_CTRL_6: + case RT5665_ALC_PGA_CTRL_7: + case RT5665_ALC_PGA_CTRL_8: + case RT5665_ALC_PGA_STA_1: + case RT5665_ALC_PGA_STA_2: + case RT5665_ALC_PGA_STA_3: + case RT5665_EQ_AUTO_RCV_CTRL1: + case RT5665_EQ_AUTO_RCV_CTRL2: + case RT5665_EQ_AUTO_RCV_CTRL3: + case RT5665_EQ_AUTO_RCV_CTRL4: + case RT5665_EQ_AUTO_RCV_CTRL5: + case RT5665_EQ_AUTO_RCV_CTRL6: + case RT5665_EQ_AUTO_RCV_CTRL7: + case RT5665_EQ_AUTO_RCV_CTRL8: + case RT5665_EQ_AUTO_RCV_CTRL9: + case RT5665_EQ_AUTO_RCV_CTRL10: + case RT5665_EQ_AUTO_RCV_CTRL11: + case RT5665_EQ_AUTO_RCV_CTRL12: + case RT5665_EQ_AUTO_RCV_CTRL13: + case RT5665_ADC_L_EQ_LPF1_A1: + case RT5665_R_EQ_LPF1_A1: + case RT5665_L_EQ_LPF1_H0: + case RT5665_R_EQ_LPF1_H0: + case RT5665_L_EQ_BPF1_A1: + case RT5665_R_EQ_BPF1_A1: + case RT5665_L_EQ_BPF1_A2: + case RT5665_R_EQ_BPF1_A2: + case RT5665_L_EQ_BPF1_H0: + case RT5665_R_EQ_BPF1_H0: + case RT5665_L_EQ_BPF2_A1: + case RT5665_R_EQ_BPF2_A1: + case RT5665_L_EQ_BPF2_A2: + case RT5665_R_EQ_BPF2_A2: + case RT5665_L_EQ_BPF2_H0: + case RT5665_R_EQ_BPF2_H0: + case RT5665_L_EQ_BPF3_A1: + case RT5665_R_EQ_BPF3_A1: + case RT5665_L_EQ_BPF3_A2: + case RT5665_R_EQ_BPF3_A2: + case RT5665_L_EQ_BPF3_H0: + case RT5665_R_EQ_BPF3_H0: + case RT5665_L_EQ_BPF4_A1: + case RT5665_R_EQ_BPF4_A1: + case RT5665_L_EQ_BPF4_A2: + case RT5665_R_EQ_BPF4_A2: + case RT5665_L_EQ_BPF4_H0: + case RT5665_R_EQ_BPF4_H0: + case RT5665_L_EQ_HPF1_A1: + case RT5665_R_EQ_HPF1_A1: + case RT5665_L_EQ_HPF1_H0: + case RT5665_R_EQ_HPF1_H0: + case RT5665_L_EQ_PRE_VOL: + case RT5665_R_EQ_PRE_VOL: + case RT5665_L_EQ_POST_VOL: + case RT5665_R_EQ_POST_VOL: + case RT5665_SCAN_MODE_CTRL: + case RT5665_I2C_MODE: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(mono_vol_tlv, -1400, 150, 0); +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); +static const DECLARE_TLV_DB_SCALE(in_bst_tlv, -1200, 75, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5665_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_01_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT01_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_23_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT23_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_45_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT45_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if1_1_67_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_1_DS_ADC_SLOT67_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_01_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT01_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_23_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT23_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_45_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT45_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if1_2_67_adc_enum, + RT5665_TDM_CTRL_2, RT5665_I2S1_2_DS_ADC_SLOT67_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if2_1_dac_enum, + RT5665_DIG_INF2_DATA, RT5665_IF2_1_DAC_SEL_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if2_1_adc_enum, + RT5665_DIG_INF2_DATA, RT5665_IF2_1_ADC_SEL_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if2_2_dac_enum, + RT5665_DIG_INF2_DATA, RT5665_IF2_2_DAC_SEL_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if2_2_adc_enum, + RT5665_DIG_INF2_DATA, RT5665_IF2_2_ADC_SEL_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if3_dac_enum, + RT5665_DIG_INF3_DATA, RT5665_IF3_DAC_SEL_SFT, rt5665_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5665_if3_adc_enum, + RT5665_DIG_INF3_DATA, RT5665_IF3_ADC_SEL_SFT, rt5665_data_select); + +static const struct snd_kcontrol_new rt5665_if1_1_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1_1 01 ADC Swap Mux", rt5665_if1_1_01_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_1_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1_1 23 ADC Swap Mux", rt5665_if1_1_23_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_1_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1_1 45 ADC Swap Mux", rt5665_if1_1_45_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_1_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1_1 67 ADC Swap Mux", rt5665_if1_1_67_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_2_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1_2 01 ADC Swap Mux", rt5665_if1_2_01_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_2_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1_2 23 ADC1 Swap Mux", rt5665_if1_2_23_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_2_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1_2 45 ADC1 Swap Mux", rt5665_if1_2_45_adc_enum); + +static const struct snd_kcontrol_new rt5665_if1_2_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1_2 67 ADC1 Swap Mux", rt5665_if1_2_67_adc_enum); + +static const struct snd_kcontrol_new rt5665_if2_1_dac_swap_mux = + SOC_DAPM_ENUM("IF2_1 DAC Swap Source", rt5665_if2_1_dac_enum); + +static const struct snd_kcontrol_new rt5665_if2_1_adc_swap_mux = + SOC_DAPM_ENUM("IF2_1 ADC Swap Source", rt5665_if2_1_adc_enum); + +static const struct snd_kcontrol_new rt5665_if2_2_dac_swap_mux = + SOC_DAPM_ENUM("IF2_2 DAC Swap Source", rt5665_if2_2_dac_enum); + +static const struct snd_kcontrol_new rt5665_if2_2_adc_swap_mux = + SOC_DAPM_ENUM("IF2_2 ADC Swap Source", rt5665_if2_2_adc_enum); + +static const struct snd_kcontrol_new rt5665_if3_dac_swap_mux = + SOC_DAPM_ENUM("IF3 DAC Swap Source", rt5665_if3_dac_enum); + +static const struct snd_kcontrol_new rt5665_if3_adc_swap_mux = + SOC_DAPM_ENUM("IF3 ADC Swap Source", rt5665_if3_adc_enum); + +static int rt5665_hp_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int ret = snd_soc_put_volsw(kcontrol, ucontrol); + + if (snd_soc_component_read(component, RT5665_STO_NG2_CTRL_1) & RT5665_NG2_EN) { + snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_DIS); + snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_EN); + } + + return ret; +} + +static int rt5665_mono_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int ret = snd_soc_put_volsw(kcontrol, ucontrol); + + if (snd_soc_component_read(component, RT5665_MONO_NG2_CTRL_1) & RT5665_NG2_EN) { + snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_DIS); + snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_EN); + } + + return ret; +} + +/** + * rt5665_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5665 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the codec driver will turn on ASRC + * for these filters if ASRC is selected as their clock source. + */ +int rt5665_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + unsigned int asrc2_mask = 0; + unsigned int asrc2_value = 0; + unsigned int asrc3_mask = 0; + unsigned int asrc3_value = 0; + + switch (clk_src) { + case RT5665_CLK_SEL_SYS: + case RT5665_CLK_SEL_I2S1_ASRC: + case RT5665_CLK_SEL_I2S2_ASRC: + case RT5665_CLK_SEL_I2S3_ASRC: + case RT5665_CLK_SEL_SYS2: + case RT5665_CLK_SEL_SYS3: + case RT5665_CLK_SEL_SYS4: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5665_DA_STEREO1_FILTER) { + asrc2_mask |= RT5665_DA_STO1_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5665_DA_STO1_CLK_SEL_MASK) + | (clk_src << RT5665_DA_STO1_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_DA_STEREO2_FILTER) { + asrc2_mask |= RT5665_DA_STO2_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5665_DA_STO2_CLK_SEL_MASK) + | (clk_src << RT5665_DA_STO2_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_DA_MONO_L_FILTER) { + asrc2_mask |= RT5665_DA_MONOL_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5665_DA_MONOL_CLK_SEL_MASK) + | (clk_src << RT5665_DA_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_DA_MONO_R_FILTER) { + asrc2_mask |= RT5665_DA_MONOR_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5665_DA_MONOR_CLK_SEL_MASK) + | (clk_src << RT5665_DA_MONOR_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_AD_STEREO1_FILTER) { + asrc3_mask |= RT5665_AD_STO1_CLK_SEL_MASK; + asrc3_value = (asrc2_value & ~RT5665_AD_STO1_CLK_SEL_MASK) + | (clk_src << RT5665_AD_STO1_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_AD_STEREO2_FILTER) { + asrc3_mask |= RT5665_AD_STO2_CLK_SEL_MASK; + asrc3_value = (asrc2_value & ~RT5665_AD_STO2_CLK_SEL_MASK) + | (clk_src << RT5665_AD_STO2_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_AD_MONO_L_FILTER) { + asrc3_mask |= RT5665_AD_MONOL_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5665_AD_MONOL_CLK_SEL_MASK) + | (clk_src << RT5665_AD_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5665_AD_MONO_R_FILTER) { + asrc3_mask |= RT5665_AD_MONOR_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5665_AD_MONOR_CLK_SEL_MASK) + | (clk_src << RT5665_AD_MONOR_CLK_SEL_SFT); + } + + if (asrc2_mask) + snd_soc_component_update_bits(component, RT5665_ASRC_2, + asrc2_mask, asrc2_value); + + if (asrc3_mask) + snd_soc_component_update_bits(component, RT5665_ASRC_3, + asrc3_mask, asrc3_value); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5665_sel_asrc_clk_src); + +static int rt5665_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read(component, RT5665_4BTN_IL_CMD_1); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5665_4BTN_IL_CMD_1, val); + + return btn_type; +} + +static void rt5665_enable_push_button_irq(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write(component, RT5665_4BTN_IL_CMD_1, 0x0003); + snd_soc_component_update_bits(component, RT5665_SAR_IL_CMD_9, 0x1, 0x1); + snd_soc_component_write(component, RT5665_IL_CMD_1, 0x0048); + snd_soc_component_update_bits(component, RT5665_4BTN_IL_CMD_2, + RT5665_4BTN_IL_MASK | RT5665_4BTN_IL_RST_MASK, + RT5665_4BTN_IL_EN | RT5665_4BTN_IL_NOR); + snd_soc_component_update_bits(component, RT5665_IRQ_CTRL_3, + RT5665_IL_IRQ_MASK, RT5665_IL_IRQ_EN); + } else { + snd_soc_component_update_bits(component, RT5665_IRQ_CTRL_3, + RT5665_IL_IRQ_MASK, RT5665_IL_IRQ_DIS); + snd_soc_component_update_bits(component, RT5665_4BTN_IL_CMD_2, + RT5665_4BTN_IL_MASK, RT5665_4BTN_IL_DIS); + snd_soc_component_update_bits(component, RT5665_4BTN_IL_CMD_2, + RT5665_4BTN_IL_RST_MASK, RT5665_4BTN_IL_RST); + } +} + +/** + * rt5665_headset_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5665_headset_detect(struct snd_soc_component *component, int jack_insert) +{ + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + unsigned int sar_hs_type, val; + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_sync(dapm); + + regmap_update_bits(rt5665->regmap, RT5665_MICBIAS_2, 0x100, + 0x100); + + regmap_read(rt5665->regmap, RT5665_GPIO_STA, &val); + if (val & 0x4) { + regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1, + 0x100, 0); + + regmap_read(rt5665->regmap, RT5665_GPIO_STA, &val); + while (val & 0x4) { + usleep_range(10000, 15000); + regmap_read(rt5665->regmap, RT5665_GPIO_STA, + &val); + } + } + + regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1, + 0x1a0, 0x120); + regmap_write(rt5665->regmap, RT5665_EJD_CTRL_3, 0x3424); + regmap_write(rt5665->regmap, RT5665_IL_CMD_1, 0x0048); + regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, 0xa291); + + usleep_range(10000, 15000); + + rt5665->sar_adc_value = snd_soc_component_read(rt5665->component, + RT5665_SAR_IL_CMD_4) & 0x7ff; + + sar_hs_type = rt5665->pdata.sar_hs_type ? + rt5665->pdata.sar_hs_type : 729; + + if (rt5665->sar_adc_value > sar_hs_type) { + rt5665->jack_type = SND_JACK_HEADSET; + rt5665_enable_push_button_irq(component, true); + } else { + rt5665->jack_type = SND_JACK_HEADPHONE; + regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, + 0x2291); + regmap_update_bits(rt5665->regmap, RT5665_MICBIAS_2, + 0x100, 0); + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_sync(dapm); + } + } else { + regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, 0x2291); + regmap_update_bits(rt5665->regmap, RT5665_MICBIAS_2, 0x100, 0); + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_sync(dapm); + if (rt5665->jack_type == SND_JACK_HEADSET) + rt5665_enable_push_button_irq(component, false); + rt5665->jack_type = 0; + } + + dev_dbg(component->dev, "jack_type = %d\n", rt5665->jack_type); + return rt5665->jack_type; +} + +static irqreturn_t rt5665_irq(int irq, void *data) +{ + struct rt5665_priv *rt5665 = data; + + mod_delayed_work(system_power_efficient_wq, + &rt5665->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static void rt5665_jd_check_handler(struct work_struct *work) +{ + struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv, + jd_check_work.work); + + if (snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010) { + /* jack out */ + rt5665->jack_type = rt5665_headset_detect(rt5665->component, 0); + + snd_soc_jack_report(rt5665->hs_jack, rt5665->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else { + schedule_delayed_work(&rt5665->jd_check_work, 500); + } +} + +static int rt5665_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + + switch (rt5665->pdata.jd_src) { + case RT5665_JD1: + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1, + RT5665_GP1_PIN_MASK, RT5665_GP1_PIN_IRQ); + regmap_update_bits(rt5665->regmap, RT5665_RC_CLK_CTRL, + 0xc000, 0xc000); + regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_2, + RT5665_PWR_JD1, RT5665_PWR_JD1); + regmap_update_bits(rt5665->regmap, RT5665_IRQ_CTRL_1, 0x8, 0x8); + break; + + case RT5665_JD_NULL: + break; + + default: + dev_warn(component->dev, "Wrong JD source\n"); + break; + } + + rt5665->hs_jack = hs_jack; + + return 0; +} + +static void rt5665_jack_detect_handler(struct work_struct *work) +{ + struct rt5665_priv *rt5665 = + container_of(work, struct rt5665_priv, jack_detect_work.work); + int val, btn_type; + + while (!rt5665->component) { + pr_debug("%s codec = null\n", __func__); + usleep_range(10000, 15000); + } + + while (!snd_soc_card_is_instantiated(rt5665->component->card)) { + pr_debug("%s\n", __func__); + usleep_range(10000, 15000); + } + + while (!rt5665->calibration_done) { + pr_debug("%s calibration not ready\n", __func__); + usleep_range(10000, 15000); + } + + mutex_lock(&rt5665->calibrate_mutex); + + val = snd_soc_component_read(rt5665->component, RT5665_AJD1_CTRL) & 0x0010; + if (!val) { + /* jack in */ + if (rt5665->jack_type == 0) { + /* jack was out, report jack type */ + rt5665->jack_type = + rt5665_headset_detect(rt5665->component, 1); + } else { + /* jack is already in, report button event */ + rt5665->jack_type = SND_JACK_HEADSET; + btn_type = rt5665_button_detect(rt5665->component); + /** + * rt5665 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + rt5665->jack_type |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + rt5665->jack_type |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + rt5665->jack_type |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + rt5665->jack_type |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + btn_type = 0; + dev_err(rt5665->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + } else { + /* jack out */ + rt5665->jack_type = rt5665_headset_detect(rt5665->component, 0); + } + + snd_soc_jack_report(rt5665->hs_jack, rt5665->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (rt5665->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5665->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5665->jd_check_work); + + mutex_unlock(&rt5665->calibrate_mutex); +} + +static const char * const rt5665_clk_sync[] = { + "I2S1_1", "I2S1_2", "I2S2", "I2S3", "IF2 Slave", "IF3 Slave" +}; + +static const struct soc_enum rt5665_enum[] = { + SOC_ENUM_SINGLE(RT5665_I2S1_SDP, 11, 5, rt5665_clk_sync), + SOC_ENUM_SINGLE(RT5665_I2S2_SDP, 11, 5, rt5665_clk_sync), + SOC_ENUM_SINGLE(RT5665_I2S3_SDP, 11, 5, rt5665_clk_sync), +}; + +static const struct snd_kcontrol_new rt5665_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_EXT_TLV("Headphone Playback Volume", RT5665_HPL_GAIN, + RT5665_HPR_GAIN, RT5665_G_HP_SFT, 15, 1, snd_soc_get_volsw, + rt5665_hp_vol_put, hp_vol_tlv), + + /* Mono Output Volume */ + SOC_SINGLE_EXT_TLV("Mono Playback Volume", RT5665_MONO_GAIN, + RT5665_L_VOL_SFT, 15, 1, snd_soc_get_volsw, + rt5665_mono_vol_put, mono_vol_tlv), + + SOC_SINGLE_TLV("MONOVOL Playback Volume", RT5665_MONO_OUT, + RT5665_L_VOL_SFT, 39, 1, out_vol_tlv), + + /* Output Volume */ + SOC_DOUBLE_TLV("OUT Playback Volume", RT5665_LOUT, RT5665_L_VOL_SFT, + RT5665_R_VOL_SFT, 39, 1, out_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5665_DAC1_DIG_VOL, + RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 175, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5665_DAC2_DIG_VOL, + RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 175, 0, dac_vol_tlv), + SOC_DOUBLE("DAC2 Playback Switch", RT5665_DAC2_CTRL, + RT5665_M_DAC2_L_VOL_SFT, RT5665_M_DAC2_R_VOL_SFT, 1, 1), + + /* IN1/IN2/IN3/IN4 Volume */ + SOC_SINGLE_TLV("IN1 Boost Volume", RT5665_IN1_IN2, + RT5665_BST1_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("IN2 Boost Volume", RT5665_IN1_IN2, + RT5665_BST2_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("IN3 Boost Volume", RT5665_IN3_IN4, + RT5665_BST3_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("IN4 Boost Volume", RT5665_IN3_IN4, + RT5665_BST4_SFT, 69, 0, in_bst_tlv), + SOC_SINGLE_TLV("CBJ Boost Volume", RT5665_CBJ_BST_CTRL, + RT5665_BST_CBJ_SFT, 8, 0, bst_tlv), + + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5665_INL1_INR1_VOL, + RT5665_INL_VOL_SFT, RT5665_INR_VOL_SFT, 31, 1, in_vol_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("STO1 ADC Capture Switch", RT5665_STO1_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, RT5665_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5665_STO1_ADC_DIG_VOL, + RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 127, 0, adc_vol_tlv), + SOC_DOUBLE("Mono ADC Capture Switch", RT5665_MONO_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, RT5665_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5665_MONO_ADC_DIG_VOL, + RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 127, 0, adc_vol_tlv), + SOC_DOUBLE("STO2 ADC Capture Switch", RT5665_STO2_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, RT5665_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO2 ADC Capture Volume", RT5665_STO2_ADC_DIG_VOL, + RT5665_L_VOL_SFT, RT5665_R_VOL_SFT, 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5665_STO1_ADC_BOOST, + RT5665_STO1_ADC_L_BST_SFT, RT5665_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_DOUBLE_TLV("Mono ADC Boost Gain Volume", RT5665_MONO_ADC_BOOST, + RT5665_MONO_ADC_L_BST_SFT, RT5665_MONO_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_DOUBLE_TLV("STO2 ADC Boost Gain Volume", RT5665_STO2_ADC_BOOST, + RT5665_STO2_ADC_L_BST_SFT, RT5665_STO2_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + /* I2S3 CLK Source */ + SOC_ENUM("I2S1 Master Clk Sel", rt5665_enum[0]), + SOC_ENUM("I2S2 Master Clk Sel", rt5665_enum[1]), + SOC_ENUM("I2S3 Master Clk Sel", rt5665_enum[2]), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + int pd, idx; + + pd = rl6231_get_pre_div(rt5665->regmap, + RT5665_ADDA_CLK_1, RT5665_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rt5665->sysclk / pd); + + if (idx < 0) + dev_err(component->dev, "Failed to set DMIC clock\n"); + else { + snd_soc_component_update_bits(component, RT5665_DMIC_CTRL_1, + RT5665_DMIC_CLK_MASK, idx << RT5665_DMIC_CLK_SFT); + } + return idx; +} + +static int rt5665_charge_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, RT5665_HP_CHARGE_PUMP_1, + RT5665_PM_HP_MASK | RT5665_OSW_L_MASK, + RT5665_PM_HP_HV | RT5665_OSW_L_EN); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, RT5665_HP_CHARGE_PUMP_1, + RT5665_PM_HP_MASK | RT5665_OSW_L_MASK, + RT5665_PM_HP_LV | RT5665_OSW_L_DIS); + break; + default: + return 0; + } + + return 0; +} + +static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read(component, RT5665_GLB_CLK); + val &= RT5665_SCLK_SRC_MASK; + if (val == RT5665_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case RT5665_ADC_MONO_R_ASRC_SFT: + reg = RT5665_ASRC_3; + shift = RT5665_AD_MONOR_CLK_SEL_SFT; + break; + case RT5665_ADC_MONO_L_ASRC_SFT: + reg = RT5665_ASRC_3; + shift = RT5665_AD_MONOL_CLK_SEL_SFT; + break; + case RT5665_ADC_STO1_ASRC_SFT: + reg = RT5665_ASRC_3; + shift = RT5665_AD_STO1_CLK_SEL_SFT; + break; + case RT5665_ADC_STO2_ASRC_SFT: + reg = RT5665_ASRC_3; + shift = RT5665_AD_STO2_CLK_SEL_SFT; + break; + case RT5665_DAC_MONO_R_ASRC_SFT: + reg = RT5665_ASRC_2; + shift = RT5665_DA_MONOR_CLK_SEL_SFT; + break; + case RT5665_DAC_MONO_L_ASRC_SFT: + reg = RT5665_ASRC_2; + shift = RT5665_DA_MONOL_CLK_SEL_SFT; + break; + case RT5665_DAC_STO1_ASRC_SFT: + reg = RT5665_ASRC_2; + shift = RT5665_DA_STO1_CLK_SEL_SFT; + break; + case RT5665_DAC_STO2_ASRC_SFT: + reg = RT5665_ASRC_2; + shift = RT5665_DA_STO2_CLK_SEL_SFT; + break; + default: + return 0; + } + + val = (snd_soc_component_read(component, reg) >> shift) & 0xf; + switch (val) { + case RT5665_CLK_SEL_I2S1_ASRC: + case RT5665_CLK_SEL_I2S2_ASRC: + case RT5665_CLK_SEL_I2S3_ASRC: + /* I2S_Pre_Div1 should be 1 in asrc mode */ + snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1, + RT5665_I2S_PD1_MASK, RT5665_I2S_PD1_2); + return 1; + default: + return 0; + } + +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5665_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO1_ADC_MIXER, + RT5665_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO1_ADC_MIXER, + RT5665_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO1_ADC_MIXER, + RT5665_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO1_ADC_MIXER, + RT5665_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO2_ADC_MIXER, + RT5665_M_STO2_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO2_ADC_MIXER, + RT5665_M_STO2_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_STO2_ADC_MIXER, + RT5665_M_STO2_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_STO2_ADC_MIXER, + RT5665_M_STO2_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_mono_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_MONO_ADC_MIXER, + RT5665_M_MONO_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_MONO_ADC_MIXER, + RT5665_M_MONO_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_mono_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5665_MONO_ADC_MIXER, + RT5665_M_MONO_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5665_MONO_ADC_MIXER, + RT5665_M_MONO_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5665_AD_DA_MIXER, + RT5665_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5665_AD_DA_MIXER, + RT5665_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5665_AD_DA_MIXER, + RT5665_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5665_AD_DA_MIXER, + RT5665_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_L1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_R1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_L2_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_R2_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_L1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_R1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_L2_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_STO1_DAC_MIXER, + RT5665_M_DAC_R2_STO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto2_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_L1_STO2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_L2_STO2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L3 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_L3_STO2_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_sto2_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_R1_STO2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_R2_STO2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R3 Switch", RT5665_STO2_DAC_MIXER, + RT5665_M_DAC_R3_STO2_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_mono_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_L1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_R1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_L2_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_R2_MONO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_mono_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_L1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_R1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_L2_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_MONO_DAC_MIXER, + RT5665_M_DAC_R2_MONO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5665_rec1_l_mix[] = { + SOC_DAPM_SINGLE("CBJ Switch", RT5665_REC1_L2_MIXER, + RT5665_M_CBJ_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5665_REC1_L2_MIXER, + RT5665_M_INL_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5665_REC1_L2_MIXER, + RT5665_M_INR_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC1_L2_MIXER, + RT5665_M_BST4_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC1_L2_MIXER, + RT5665_M_BST3_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC1_L2_MIXER, + RT5665_M_BST2_RM1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC1_L2_MIXER, + RT5665_M_BST1_RM1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_rec1_r_mix[] = { + SOC_DAPM_SINGLE("MONOVOL Switch", RT5665_REC1_R2_MIXER, + RT5665_M_AEC_REF_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5665_REC1_R2_MIXER, + RT5665_M_INR_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC1_R2_MIXER, + RT5665_M_BST4_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC1_R2_MIXER, + RT5665_M_BST3_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC1_R2_MIXER, + RT5665_M_BST2_RM1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC1_R2_MIXER, + RT5665_M_BST1_RM1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_rec2_l_mix[] = { + SOC_DAPM_SINGLE("INL Switch", RT5665_REC2_L2_MIXER, + RT5665_M_INL_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5665_REC2_L2_MIXER, + RT5665_M_INR_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("CBJ Switch", RT5665_REC2_L2_MIXER, + RT5665_M_CBJ_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC2_L2_MIXER, + RT5665_M_BST4_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC2_L2_MIXER, + RT5665_M_BST3_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC2_L2_MIXER, + RT5665_M_BST2_RM2_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC2_L2_MIXER, + RT5665_M_BST1_RM2_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_rec2_r_mix[] = { + SOC_DAPM_SINGLE("MONOVOL Switch", RT5665_REC2_R2_MIXER, + RT5665_M_MONOVOL_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5665_REC2_R2_MIXER, + RT5665_M_INL_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5665_REC2_R2_MIXER, + RT5665_M_INR_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5665_REC2_R2_MIXER, + RT5665_M_BST4_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_REC2_R2_MIXER, + RT5665_M_BST3_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_REC2_R2_MIXER, + RT5665_M_BST2_RM2_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_REC2_R2_MIXER, + RT5665_M_BST1_RM2_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_monovol_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_DAC_L2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("RECMIX2L Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_RECMIC2L_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_BST1_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_BST2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_BST3_MM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_out_l_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_OUT_L_MIXER, + RT5665_M_DAC_L2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5665_OUT_L_MIXER, + RT5665_M_IN_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5665_OUT_L_MIXER, + RT5665_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_OUT_L_MIXER, + RT5665_M_BST2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_OUT_L_MIXER, + RT5665_M_BST3_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_out_r_mix[] = { + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_OUT_R_MIXER, + RT5665_M_DAC_R2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5665_OUT_R_MIXER, + RT5665_M_IN_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5665_OUT_R_MIXER, + RT5665_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST3 Switch", RT5665_OUT_R_MIXER, + RT5665_M_BST3_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST4 Switch", RT5665_OUT_R_MIXER, + RT5665_M_BST4_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_mono_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_DAC_L2_MA_SFT, 1, 1), + SOC_DAPM_SINGLE("MONOVOL Switch", RT5665_MONOMIX_IN_GAIN, + RT5665_M_MONOVOL_MA_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_lout_l_mix[] = { + SOC_DAPM_SINGLE("DAC L2 Switch", RT5665_LOUT_MIXER, + RT5665_M_DAC_L2_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL L Switch", RT5665_LOUT_MIXER, + RT5665_M_OV_L_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5665_lout_r_mix[] = { + SOC_DAPM_SINGLE("DAC R2 Switch", RT5665_LOUT_MIXER, + RT5665_M_DAC_R2_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL R Switch", RT5665_LOUT_MIXER, + RT5665_M_OV_R_LM_SFT, 1, 1), +}; + +/*DAC L2, DAC R2*/ +/*MX-17 [6:4], MX-17 [2:0]*/ +static const char * const rt5665_dac2_src[] = { + "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC", "Mono ADC MIX" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_dac_l2_enum, RT5665_DAC2_CTRL, + RT5665_DAC_L2_SEL_SFT, rt5665_dac2_src); + +static const struct snd_kcontrol_new rt5665_dac_l2_mux = + SOC_DAPM_ENUM("Digital DAC L2 Source", rt5665_dac_l2_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_dac_r2_enum, RT5665_DAC2_CTRL, + RT5665_DAC_R2_SEL_SFT, rt5665_dac2_src); + +static const struct snd_kcontrol_new rt5665_dac_r2_mux = + SOC_DAPM_ENUM("Digital DAC R2 Source", rt5665_dac_r2_enum); + +/*DAC L3, DAC R3*/ +/*MX-1B [6:4], MX-1B [2:0]*/ +static const char * const rt5665_dac3_src[] = { + "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC", "STO2 ADC MIX" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_dac_l3_enum, RT5665_DAC3_CTRL, + RT5665_DAC_L3_SEL_SFT, rt5665_dac3_src); + +static const struct snd_kcontrol_new rt5665_dac_l3_mux = + SOC_DAPM_ENUM("Digital DAC L3 Source", rt5665_dac_l3_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_dac_r3_enum, RT5665_DAC3_CTRL, + RT5665_DAC_R3_SEL_SFT, rt5665_dac3_src); + +static const struct snd_kcontrol_new rt5665_dac_r3_mux = + SOC_DAPM_ENUM("Digital DAC R3 Source", rt5665_dac_r3_enum); + +/* STO1 ADC1 Source */ +/* MX-26 [13] [5] */ +static const char * const rt5665_sto1_adc1_src[] = { + "DD Mux", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adc1l_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADC1L_SRC_SFT, rt5665_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5665_sto1_adc1l_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5665_sto1_adc1l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adc1r_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADC1R_SRC_SFT, rt5665_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5665_sto1_adc1r_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5665_sto1_adc1r_enum); + +/* STO1 ADC Source */ +/* MX-26 [11:10] [3:2] */ +static const char * const rt5665_sto1_adc_src[] = { + "ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adcl_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADCL_SRC_SFT, rt5665_sto1_adc_src); + +static const struct snd_kcontrol_new rt5665_sto1_adcl_mux = + SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5665_sto1_adcl_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adcr_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADCR_SRC_SFT, rt5665_sto1_adc_src); + +static const struct snd_kcontrol_new rt5665_sto1_adcr_mux = + SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5665_sto1_adcr_enum); + +/* STO1 ADC2 Source */ +/* MX-26 [12] [4] */ +static const char * const rt5665_sto1_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adc2l_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADC2L_SRC_SFT, rt5665_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5665_sto1_adc2l_mux = + SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5665_sto1_adc2l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto1_adc2r_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_ADC2R_SRC_SFT, rt5665_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5665_sto1_adc2r_mux = + SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5665_sto1_adc2r_enum); + +/* STO1 DMIC Source */ +/* MX-26 [8] */ +static const char * const rt5665_sto1_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto1_dmic_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_DMIC_SRC_SFT, rt5665_sto1_dmic_src); + +static const struct snd_kcontrol_new rt5665_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC Mux", rt5665_sto1_dmic_enum); + +/* MX-26 [9] */ +static const char * const rt5665_sto1_dd_l_src[] = { + "STO2 DAC", "MONO DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto1_dd_l_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_DD_L_SRC_SFT, rt5665_sto1_dd_l_src); + +static const struct snd_kcontrol_new rt5665_sto1_dd_l_mux = + SOC_DAPM_ENUM("Stereo1 DD L Source", rt5665_sto1_dd_l_enum); + +/* MX-26 [1:0] */ +static const char * const rt5665_sto1_dd_r_src[] = { + "STO2 DAC", "MONO DAC", "AEC REF" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto1_dd_r_enum, RT5665_STO1_ADC_MIXER, + RT5665_STO1_DD_R_SRC_SFT, rt5665_sto1_dd_r_src); + +static const struct snd_kcontrol_new rt5665_sto1_dd_r_mux = + SOC_DAPM_ENUM("Stereo1 DD R Source", rt5665_sto1_dd_r_enum); + +/* MONO ADC L2 Source */ +/* MX-27 [12] */ +static const char * const rt5665_mono_adc_l2_src[] = { + "DAC MIXL", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_mono_adc_l2_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_L2_SRC_SFT, rt5665_mono_adc_l2_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_l2_mux = + SOC_DAPM_ENUM("Mono ADC L2 Source", rt5665_mono_adc_l2_enum); + + +/* MONO ADC L1 Source */ +/* MX-27 [13] */ +static const char * const rt5665_mono_adc_l1_src[] = { + "DD Mux", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_mono_adc_l1_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_L1_SRC_SFT, rt5665_mono_adc_l1_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_l1_mux = + SOC_DAPM_ENUM("Mono ADC L1 Source", rt5665_mono_adc_l1_enum); + +/* MX-27 [9][1]*/ +static const char * const rt5665_mono_dd_src[] = { + "STO2 DAC", "MONO DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_mono_dd_l_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_DD_L_SRC_SFT, rt5665_mono_dd_src); + +static const struct snd_kcontrol_new rt5665_mono_dd_l_mux = + SOC_DAPM_ENUM("Mono DD L Source", rt5665_mono_dd_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_mono_dd_r_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_DD_R_SRC_SFT, rt5665_mono_dd_src); + +static const struct snd_kcontrol_new rt5665_mono_dd_r_mux = + SOC_DAPM_ENUM("Mono DD R Source", rt5665_mono_dd_r_enum); + +/* MONO ADC L Source, MONO ADC R Source*/ +/* MX-27 [11:10], MX-27 [3:2] */ +static const char * const rt5665_mono_adc_src[] = { + "ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_mono_adc_l_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_L_SRC_SFT, rt5665_mono_adc_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_l_mux = + SOC_DAPM_ENUM("Mono ADC L Source", rt5665_mono_adc_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_mono_adcr_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_R_SRC_SFT, rt5665_mono_adc_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_r_mux = + SOC_DAPM_ENUM("Mono ADC R Source", rt5665_mono_adcr_enum); + +/* MONO DMIC L Source */ +/* MX-27 [8] */ +static const char * const rt5665_mono_dmic_l_src[] = { + "DMIC1 L", "DMIC2 L" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_mono_dmic_l_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_DMIC_L_SRC_SFT, rt5665_mono_dmic_l_src); + +static const struct snd_kcontrol_new rt5665_mono_dmic_l_mux = + SOC_DAPM_ENUM("Mono DMIC L Source", rt5665_mono_dmic_l_enum); + +/* MONO ADC R2 Source */ +/* MX-27 [4] */ +static const char * const rt5665_mono_adc_r2_src[] = { + "DAC MIXR", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_mono_adc_r2_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_R2_SRC_SFT, rt5665_mono_adc_r2_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_r2_mux = + SOC_DAPM_ENUM("Mono ADC R2 Source", rt5665_mono_adc_r2_enum); + +/* MONO ADC R1 Source */ +/* MX-27 [5] */ +static const char * const rt5665_mono_adc_r1_src[] = { + "DD Mux", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_mono_adc_r1_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_ADC_R1_SRC_SFT, rt5665_mono_adc_r1_src); + +static const struct snd_kcontrol_new rt5665_mono_adc_r1_mux = + SOC_DAPM_ENUM("Mono ADC R1 Source", rt5665_mono_adc_r1_enum); + +/* MONO DMIC R Source */ +/* MX-27 [0] */ +static const char * const rt5665_mono_dmic_r_src[] = { + "DMIC1 R", "DMIC2 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_mono_dmic_r_enum, RT5665_MONO_ADC_MIXER, + RT5665_MONO_DMIC_R_SRC_SFT, rt5665_mono_dmic_r_src); + +static const struct snd_kcontrol_new rt5665_mono_dmic_r_mux = + SOC_DAPM_ENUM("Mono DMIC R Source", rt5665_mono_dmic_r_enum); + + +/* STO2 ADC1 Source */ +/* MX-28 [13] [5] */ +static const char * const rt5665_sto2_adc1_src[] = { + "DD Mux", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adc1l_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADC1L_SRC_SFT, rt5665_sto2_adc1_src); + +static const struct snd_kcontrol_new rt5665_sto2_adc1l_mux = + SOC_DAPM_ENUM("Stereo2 ADC1L Source", rt5665_sto2_adc1l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adc1r_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADC1R_SRC_SFT, rt5665_sto2_adc1_src); + +static const struct snd_kcontrol_new rt5665_sto2_adc1r_mux = + SOC_DAPM_ENUM("Stereo2 ADC1L Source", rt5665_sto2_adc1r_enum); + +/* STO2 ADC Source */ +/* MX-28 [11:10] [3:2] */ +static const char * const rt5665_sto2_adc_src[] = { + "ADC1 L", "ADC1 R", "ADC2 L" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adcl_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADCL_SRC_SFT, rt5665_sto2_adc_src); + +static const struct snd_kcontrol_new rt5665_sto2_adcl_mux = + SOC_DAPM_ENUM("Stereo2 ADCL Source", rt5665_sto2_adcl_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adcr_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADCR_SRC_SFT, rt5665_sto2_adc_src); + +static const struct snd_kcontrol_new rt5665_sto2_adcr_mux = + SOC_DAPM_ENUM("Stereo2 ADCR Source", rt5665_sto2_adcr_enum); + +/* STO2 ADC2 Source */ +/* MX-28 [12] [4] */ +static const char * const rt5665_sto2_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adc2l_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADC2L_SRC_SFT, rt5665_sto2_adc2_src); + +static const struct snd_kcontrol_new rt5665_sto2_adc2l_mux = + SOC_DAPM_ENUM("Stereo2 ADC2L Source", rt5665_sto2_adc2l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto2_adc2r_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_ADC2R_SRC_SFT, rt5665_sto2_adc2_src); + +static const struct snd_kcontrol_new rt5665_sto2_adc2r_mux = + SOC_DAPM_ENUM("Stereo2 ADC2R Source", rt5665_sto2_adc2r_enum); + +/* STO2 DMIC Source */ +/* MX-28 [8] */ +static const char * const rt5665_sto2_dmic_src[] = { + "DMIC1", "DMIC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto2_dmic_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_DMIC_SRC_SFT, rt5665_sto2_dmic_src); + +static const struct snd_kcontrol_new rt5665_sto2_dmic_mux = + SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5665_sto2_dmic_enum); + +/* MX-28 [9] */ +static const char * const rt5665_sto2_dd_l_src[] = { + "STO2 DAC", "MONO DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto2_dd_l_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_DD_L_SRC_SFT, rt5665_sto2_dd_l_src); + +static const struct snd_kcontrol_new rt5665_sto2_dd_l_mux = + SOC_DAPM_ENUM("Stereo2 DD L Source", rt5665_sto2_dd_l_enum); + +/* MX-28 [1] */ +static const char * const rt5665_sto2_dd_r_src[] = { + "STO2 DAC", "MONO DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_sto2_dd_r_enum, RT5665_STO2_ADC_MIXER, + RT5665_STO2_DD_R_SRC_SFT, rt5665_sto2_dd_r_src); + +static const struct snd_kcontrol_new rt5665_sto2_dd_r_mux = + SOC_DAPM_ENUM("Stereo2 DD R Source", rt5665_sto2_dd_r_enum); + +/* DAC R1 Source, DAC L1 Source*/ +/* MX-29 [11:10], MX-29 [9:8]*/ +static const char * const rt5665_dac1_src[] = { + "IF1 DAC1", "IF2_1 DAC", "IF2_2 DAC", "IF3 DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_dac_r1_enum, RT5665_AD_DA_MIXER, + RT5665_DAC1_R_SEL_SFT, rt5665_dac1_src); + +static const struct snd_kcontrol_new rt5665_dac_r1_mux = + SOC_DAPM_ENUM("DAC R1 Source", rt5665_dac_r1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_dac_l1_enum, RT5665_AD_DA_MIXER, + RT5665_DAC1_L_SEL_SFT, rt5665_dac1_src); + +static const struct snd_kcontrol_new rt5665_dac_l1_mux = + SOC_DAPM_ENUM("DAC L1 Source", rt5665_dac_l1_enum); + +/* DAC Digital Mixer L Source, DAC Digital Mixer R Source*/ +/* MX-2D [13:12], MX-2D [9:8]*/ +static const char * const rt5665_dig_dac_mix_src[] = { + "Stereo1 DAC Mixer", "Stereo2 DAC Mixer", "Mono DAC Mixer" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_dig_dac_mixl_enum, RT5665_A_DAC1_MUX, + RT5665_DAC_MIX_L_SFT, rt5665_dig_dac_mix_src); + +static const struct snd_kcontrol_new rt5665_dig_dac_mixl_mux = + SOC_DAPM_ENUM("DAC Digital Mixer L Source", rt5665_dig_dac_mixl_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_dig_dac_mixr_enum, RT5665_A_DAC1_MUX, + RT5665_DAC_MIX_R_SFT, rt5665_dig_dac_mix_src); + +static const struct snd_kcontrol_new rt5665_dig_dac_mixr_mux = + SOC_DAPM_ENUM("DAC Digital Mixer R Source", rt5665_dig_dac_mixr_enum); + +/* Analog DAC L1 Source, Analog DAC R1 Source*/ +/* MX-2D [5:4], MX-2D [1:0]*/ +static const char * const rt5665_alg_dac1_src[] = { + "Stereo1 DAC Mixer", "DAC1", "DMIC1" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_alg_dac_l1_enum, RT5665_A_DAC1_MUX, + RT5665_A_DACL1_SFT, rt5665_alg_dac1_src); + +static const struct snd_kcontrol_new rt5665_alg_dac_l1_mux = + SOC_DAPM_ENUM("Analog DAC L1 Source", rt5665_alg_dac_l1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_alg_dac_r1_enum, RT5665_A_DAC1_MUX, + RT5665_A_DACR1_SFT, rt5665_alg_dac1_src); + +static const struct snd_kcontrol_new rt5665_alg_dac_r1_mux = + SOC_DAPM_ENUM("Analog DAC R1 Source", rt5665_alg_dac_r1_enum); + +/* Analog DAC LR Source, Analog DAC R2 Source*/ +/* MX-2E [5:4], MX-2E [0]*/ +static const char * const rt5665_alg_dac2_src[] = { + "Mono DAC Mixer", "DAC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_alg_dac_l2_enum, RT5665_A_DAC2_MUX, + RT5665_A_DACL2_SFT, rt5665_alg_dac2_src); + +static const struct snd_kcontrol_new rt5665_alg_dac_l2_mux = + SOC_DAPM_ENUM("Analog DAC L2 Source", rt5665_alg_dac_l2_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_alg_dac_r2_enum, RT5665_A_DAC2_MUX, + RT5665_A_DACR2_SFT, rt5665_alg_dac2_src); + +static const struct snd_kcontrol_new rt5665_alg_dac_r2_mux = + SOC_DAPM_ENUM("Analog DAC R2 Source", rt5665_alg_dac_r2_enum); + +/* Interface2 ADC Data Input*/ +/* MX-2F [14:12] */ +static const char * const rt5665_if2_1_adc_in_src[] = { + "STO1 ADC", "STO2 ADC", "MONO ADC", "IF1 DAC1", + "IF1 DAC2", "IF2_2 DAC", "IF3 DAC", "DAC1 MIX" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_if2_1_adc_in_enum, RT5665_DIG_INF2_DATA, + RT5665_IF2_1_ADC_IN_SFT, rt5665_if2_1_adc_in_src); + +static const struct snd_kcontrol_new rt5665_if2_1_adc_in_mux = + SOC_DAPM_ENUM("IF2_1 ADC IN Source", rt5665_if2_1_adc_in_enum); + +/* MX-2F [6:4] */ +static const char * const rt5665_if2_2_adc_in_src[] = { + "STO1 ADC", "STO2 ADC", "MONO ADC", "IF1 DAC1", + "IF1 DAC2", "IF2_1 DAC", "IF3 DAC", "DAC1 MIX" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_if2_2_adc_in_enum, RT5665_DIG_INF2_DATA, + RT5665_IF2_2_ADC_IN_SFT, rt5665_if2_2_adc_in_src); + +static const struct snd_kcontrol_new rt5665_if2_2_adc_in_mux = + SOC_DAPM_ENUM("IF2_1 ADC IN Source", rt5665_if2_2_adc_in_enum); + +/* Interface3 ADC Data Input*/ +/* MX-30 [6:4] */ +static const char * const rt5665_if3_adc_in_src[] = { + "STO1 ADC", "STO2 ADC", "MONO ADC", "IF1 DAC1", + "IF1 DAC2", "IF2_1 DAC", "IF2_2 DAC", "DAC1 MIX" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_if3_adc_in_enum, RT5665_DIG_INF3_DATA, + RT5665_IF3_ADC_IN_SFT, rt5665_if3_adc_in_src); + +static const struct snd_kcontrol_new rt5665_if3_adc_in_mux = + SOC_DAPM_ENUM("IF3 ADC IN Source", rt5665_if3_adc_in_enum); + +/* PDM 1 L/R*/ +/* MX-31 [11:10] [9:8] */ +static const char * const rt5665_pdm_src[] = { + "Stereo1 DAC", "Stereo2 DAC", "Mono DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_pdm_l_enum, RT5665_PDM_OUT_CTRL, + RT5665_PDM1_L_SFT, rt5665_pdm_src); + +static const struct snd_kcontrol_new rt5665_pdm_l_mux = + SOC_DAPM_ENUM("PDM L Source", rt5665_pdm_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_pdm_r_enum, RT5665_PDM_OUT_CTRL, + RT5665_PDM1_R_SFT, rt5665_pdm_src); + +static const struct snd_kcontrol_new rt5665_pdm_r_mux = + SOC_DAPM_ENUM("PDM R Source", rt5665_pdm_r_enum); + + +/* I2S1 TDM ADCDAT Source */ +/* MX-7a[10] */ +static const char * const rt5665_if1_1_adc1_data_src[] = { + "STO1 ADC", "IF2_1 DAC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_if1_1_adc1_data_enum, RT5665_TDM_CTRL_3, + RT5665_IF1_ADC1_SEL_SFT, rt5665_if1_1_adc1_data_src); + +static const struct snd_kcontrol_new rt5665_if1_1_adc1_mux = + SOC_DAPM_ENUM("IF1_1 ADC1 Source", rt5665_if1_1_adc1_data_enum); + +/* MX-7a[9] */ +static const char * const rt5665_if1_1_adc2_data_src[] = { + "STO2 ADC", "IF2_2 DAC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_if1_1_adc2_data_enum, RT5665_TDM_CTRL_3, + RT5665_IF1_ADC2_SEL_SFT, rt5665_if1_1_adc2_data_src); + +static const struct snd_kcontrol_new rt5665_if1_1_adc2_mux = + SOC_DAPM_ENUM("IF1_1 ADC2 Source", rt5665_if1_1_adc2_data_enum); + +/* MX-7a[8] */ +static const char * const rt5665_if1_1_adc3_data_src[] = { + "MONO ADC", "IF3 DAC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_if1_1_adc3_data_enum, RT5665_TDM_CTRL_3, + RT5665_IF1_ADC3_SEL_SFT, rt5665_if1_1_adc3_data_src); + +static const struct snd_kcontrol_new rt5665_if1_1_adc3_mux = + SOC_DAPM_ENUM("IF1_1 ADC3 Source", rt5665_if1_1_adc3_data_enum); + +/* MX-7b[10] */ +static const char * const rt5665_if1_2_adc1_data_src[] = { + "STO1 ADC", "IF1 DAC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_if1_2_adc1_data_enum, RT5665_TDM_CTRL_4, + RT5665_IF1_ADC1_SEL_SFT, rt5665_if1_2_adc1_data_src); + +static const struct snd_kcontrol_new rt5665_if1_2_adc1_mux = + SOC_DAPM_ENUM("IF1_2 ADC1 Source", rt5665_if1_2_adc1_data_enum); + +/* MX-7b[9] */ +static const char * const rt5665_if1_2_adc2_data_src[] = { + "STO2 ADC", "IF2_1 DAC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_if1_2_adc2_data_enum, RT5665_TDM_CTRL_4, + RT5665_IF1_ADC2_SEL_SFT, rt5665_if1_2_adc2_data_src); + +static const struct snd_kcontrol_new rt5665_if1_2_adc2_mux = + SOC_DAPM_ENUM("IF1_2 ADC2 Source", rt5665_if1_2_adc2_data_enum); + +/* MX-7b[8] */ +static const char * const rt5665_if1_2_adc3_data_src[] = { + "MONO ADC", "IF2_2 DAC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_if1_2_adc3_data_enum, RT5665_TDM_CTRL_4, + RT5665_IF1_ADC3_SEL_SFT, rt5665_if1_2_adc3_data_src); + +static const struct snd_kcontrol_new rt5665_if1_2_adc3_mux = + SOC_DAPM_ENUM("IF1_2 ADC3 Source", rt5665_if1_2_adc3_data_enum); + +/* MX-7b[7] */ +static const char * const rt5665_if1_2_adc4_data_src[] = { + "DAC1", "IF3 DAC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_if1_2_adc4_data_enum, RT5665_TDM_CTRL_4, + RT5665_IF1_ADC4_SEL_SFT, rt5665_if1_2_adc4_data_src); + +static const struct snd_kcontrol_new rt5665_if1_2_adc4_mux = + SOC_DAPM_ENUM("IF1_2 ADC4 Source", rt5665_if1_2_adc4_data_enum); + +/* MX-7a[4:0] MX-7b[4:0] */ +static const char * const rt5665_tdm_adc_data_src[] = { + "1234", "1243", "1324", "1342", "1432", "1423", + "2134", "2143", "2314", "2341", "2431", "2413", + "3124", "3142", "3214", "3241", "3412", "3421", + "4123", "4132", "4213", "4231", "4312", "4321" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5665_tdm1_adc_data_enum, RT5665_TDM_CTRL_3, + RT5665_TDM_ADC_SEL_SFT, rt5665_tdm_adc_data_src); + +static const struct snd_kcontrol_new rt5665_tdm1_adc_mux = + SOC_DAPM_ENUM("TDM1 ADC Mux", rt5665_tdm1_adc_data_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5665_tdm2_adc_data_enum, RT5665_TDM_CTRL_4, + RT5665_TDM_ADC_SEL_SFT, rt5665_tdm_adc_data_src); + +static const struct snd_kcontrol_new rt5665_tdm2_adc_mux = + SOC_DAPM_ENUM("TDM2 ADCDAT Source", rt5665_tdm2_adc_data_enum); + +/* Out Volume Switch */ +static const struct snd_kcontrol_new monovol_switch = + SOC_DAPM_SINGLE("Switch", RT5665_MONO_OUT, RT5665_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new outvol_l_switch = + SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_VOL_L_SFT, 1, 1); + +static const struct snd_kcontrol_new outvol_r_switch = + SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_VOL_R_SFT, 1, 1); + +/* Out Switch */ +static const struct snd_kcontrol_new mono_switch = + SOC_DAPM_SINGLE("Switch", RT5665_MONO_OUT, RT5665_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hpo_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5665_HP_CTRL_2, + RT5665_VOL_L_SFT, 1, 0); + +static const struct snd_kcontrol_new lout_l_switch = + SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_r_switch = + SOC_DAPM_SINGLE("Switch", RT5665_LOUT, RT5665_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new pdm_l_switch = + SOC_DAPM_SINGLE("Switch", RT5665_PDM_OUT_CTRL, + RT5665_M_PDM1_L_SFT, 1, 1); + +static const struct snd_kcontrol_new pdm_r_switch = + SOC_DAPM_SINGLE("Switch", RT5665_PDM_OUT_CTRL, + RT5665_M_PDM1_R_SFT, 1, 1); + +static int rt5665_mono_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_EN); + snd_soc_component_update_bits(component, RT5665_MONO_AMP_CALIB_CTRL_1, 0x40, + 0x0); + snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x10, 0x10); + snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x20, 0x20); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x20, 0); + snd_soc_component_update_bits(component, RT5665_MONO_OUT, 0x10, 0); + snd_soc_component_update_bits(component, RT5665_MONO_AMP_CALIB_CTRL_1, 0x40, + 0x40); + snd_soc_component_update_bits(component, RT5665_MONO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_DIS); + break; + + default: + return 0; + } + + return 0; + +} + +static int rt5665_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_EN); + snd_soc_component_write(component, RT5665_HP_LOGIC_CTRL_2, 0x0003); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write(component, RT5665_HP_LOGIC_CTRL_2, 0x0002); + snd_soc_component_update_bits(component, RT5665_STO_NG2_CTRL_1, + RT5665_NG2_EN_MASK, RT5665_NG2_DIS); + break; + + default: + return 0; + } + + return 0; + +} + +static int rt5665_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5665_DEPOP_1, + RT5665_PUMP_EN, RT5665_PUMP_EN); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5665_DEPOP_1, + RT5665_PUMP_EN, 0); + break; + + default: + return 0; + } + + return 0; + +} + +static int set_dmic_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /*Add delay to avoid pop noise*/ + msleep(150); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5665_set_verf(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case RT5665_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1, + RT5665_PWR_FV1, 0); + break; + + case RT5665_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1, + RT5665_PWR_FV2, 0); + break; + + case RT5665_PWR_VREF3_BIT: + snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1, + RT5665_PWR_FV3, 0); + break; + + default: + break; + } + break; + + case SND_SOC_DAPM_POST_PMU: + usleep_range(15000, 20000); + switch (w->shift) { + case RT5665_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1, + RT5665_PWR_FV1, RT5665_PWR_FV1); + break; + + case RT5665_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1, + RT5665_PWR_FV2, RT5665_PWR_FV2); + break; + + case RT5665_PWR_VREF3_BIT: + snd_soc_component_update_bits(component, RT5665_PWR_ANLG_1, + RT5665_PWR_FV3, RT5665_PWR_FV3); + break; + + default: + break; + } + break; + + default: + return 0; + } + + return 0; +} + +static int rt5665_i2s_pin_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int val1, val2, mask1 = 0, mask2 = 0; + + switch (w->shift) { + case RT5665_PWR_I2S2_1_BIT: + mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK | + RT5665_GP4_PIN_MASK | RT5665_GP5_PIN_MASK; + val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 | + RT5665_GP4_PIN_DACDAT2_1 | RT5665_GP5_PIN_ADCDAT2_1; + break; + case RT5665_PWR_I2S2_2_BIT: + mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK | + RT5665_GP8_PIN_MASK; + val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 | + RT5665_GP8_PIN_DACDAT2_2; + mask2 = RT5665_GP9_PIN_MASK; + val2 = RT5665_GP9_PIN_ADCDAT2_2; + break; + case RT5665_PWR_I2S3_BIT: + mask1 = RT5665_GP6_PIN_MASK | RT5665_GP7_PIN_MASK | + RT5665_GP8_PIN_MASK; + val1 = RT5665_GP6_PIN_BCLK3 | RT5665_GP7_PIN_LRCK3 | + RT5665_GP8_PIN_DACDAT3; + mask2 = RT5665_GP9_PIN_MASK; + val2 = RT5665_GP9_PIN_ADCDAT3; + break; + } + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (mask1) + snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_1, + mask1, val1); + if (mask2) + snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_2, + mask2, val2); + break; + case SND_SOC_DAPM_POST_PMD: + if (mask1) + snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_1, + mask1, 0); + if (mask2) + snd_soc_component_update_bits(component, RT5665_GPIO_CTRL_2, + mask2, 0); + break; + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5665_PWR_ANLG_3, RT5665_PWR_LDO2_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL", RT5665_PWR_ANLG_3, RT5665_PWR_PLL_BIT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5665_PWR_VOL, + RT5665_PWR_MIC_DET_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Vref1", RT5665_PWR_ANLG_1, RT5665_PWR_VREF1_BIT, 0, + rt5665_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref2", RT5665_PWR_ANLG_1, RT5665_PWR_VREF2_BIT, 0, + rt5665_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref3", RT5665_PWR_ANLG_1, RT5665_PWR_VREF3_BIT, 0, + rt5665_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5665_ASRC_1, + RT5665_I2S1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5665_ASRC_1, + RT5665_I2S2_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S3 ASRC", 1, RT5665_ASRC_1, + RT5665_I2S3_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5665_ASRC_1, + RT5665_DAC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO2 ASRC", 1, RT5665_ASRC_1, + RT5665_DAC_STO2_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC Mono L ASRC", 1, RT5665_ASRC_1, + RT5665_DAC_MONO_L_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC Mono R ASRC", 1, RT5665_ASRC_1, + RT5665_DAC_MONO_R_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5665_ASRC_1, + RT5665_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5665_ASRC_1, + RT5665_ADC_STO2_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC Mono L ASRC", 1, RT5665_ASRC_1, + RT5665_ADC_MONO_L_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC Mono R ASRC", 1, RT5665_ASRC_1, + RT5665_ADC_MONO_R_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5665_ASRC_1, + RT5665_DMIC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5665_ASRC_1, + RT5665_DMIC_STO2_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5665_ASRC_1, + RT5665_DMIC_MONO_L_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5665_ASRC_1, + RT5665_DMIC_MONO_R_ASRC_SFT, 0, NULL, 0), + + /* Input Side */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5665_PWR_ANLG_2, RT5665_PWR_MB1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5665_PWR_ANLG_2, RT5665_PWR_MB2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS3", RT5665_PWR_ANLG_2, RT5665_PWR_MB3_BIT, + 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + SND_SOC_DAPM_INPUT("DMIC L2"), + SND_SOC_DAPM_INPUT("DMIC R2"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + SND_SOC_DAPM_INPUT("IN3P"), + SND_SOC_DAPM_INPUT("IN3N"), + SND_SOC_DAPM_INPUT("IN4P"), + SND_SOC_DAPM_INPUT("IN4N"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5665_DMIC_CTRL_1, + RT5665_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5665_DMIC_CTRL_1, + RT5665_DMIC_2_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST2", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST3", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST4", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST1 Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST2 Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST3 Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST3_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST4 Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST4_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST1P Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST1_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST2P Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST2_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST3P Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST3_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("BST4P Power", RT5665_PWR_ANLG_2, + RT5665_PWR_BST4_P_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CBJ Power", RT5665_PWR_ANLG_3, + RT5665_PWR_CBJ_BIT, 0, NULL, 0), + + + /* Input Volume */ + SND_SOC_DAPM_PGA("INL VOL", RT5665_PWR_VOL, RT5665_PWR_IN_L_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("INR VOL", RT5665_PWR_VOL, RT5665_PWR_IN_R_BIT, + 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5665_rec1_l_mix, + ARRAY_SIZE(rt5665_rec1_l_mix)), + SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5665_rec1_r_mix, + ARRAY_SIZE(rt5665_rec1_r_mix)), + SND_SOC_DAPM_MIXER("RECMIX2L", SND_SOC_NOPM, 0, 0, rt5665_rec2_l_mix, + ARRAY_SIZE(rt5665_rec2_l_mix)), + SND_SOC_DAPM_MIXER("RECMIX2R", SND_SOC_NOPM, 0, 0, rt5665_rec2_r_mix, + ARRAY_SIZE(rt5665_rec2_r_mix)), + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5665_PWR_ANLG_2, + RT5665_PWR_RM1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5665_PWR_ANLG_2, + RT5665_PWR_RM1_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RECMIX2L Power", RT5665_PWR_MIXER, + RT5665_PWR_RM2_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RECMIX2R Power", RT5665_PWR_MIXER, + RT5665_PWR_RM2_R_BIT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC2 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC2 R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5665_PWR_DIG_1, + RT5665_PWR_ADC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5665_PWR_DIG_1, + RT5665_PWR_ADC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 L Power", RT5665_PWR_DIG_1, + RT5665_PWR_ADC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 R Power", RT5665_PWR_DIG_1, + RT5665_PWR_ADC_R2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5665_CHOP_ADC, + RT5665_CKGEN_ADC1_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 clock", RT5665_CHOP_ADC, + RT5665_CKGEN_ADC2_SFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adcl_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_adcr_mux), + SND_SOC_DAPM_MUX("Stereo1 DD L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_dd_l_mux), + SND_SOC_DAPM_MUX("Stereo1 DD R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto1_dd_r_mux), + SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_l2_mux), + SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_r2_mux), + SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_l1_mux), + SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_r1_mux), + SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_dmic_l_mux), + SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_dmic_r_mux), + SND_SOC_DAPM_MUX("Mono ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_l_mux), + SND_SOC_DAPM_MUX("Mono ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_adc_r_mux), + SND_SOC_DAPM_MUX("Mono DD L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_dd_l_mux), + SND_SOC_DAPM_MUX("Mono DD R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_mono_dd_r_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adcl_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_adcr_mux), + SND_SOC_DAPM_MUX("Stereo2 DD L Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_dd_l_mux), + SND_SOC_DAPM_MUX("Stereo2 DD R Mux", SND_SOC_NOPM, 0, 0, + &rt5665_sto2_dd_r_mux), + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5665_PWR_DIG_2, + RT5665_PWR_ADC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Stereo2 Filter", RT5665_PWR_DIG_2, + RT5665_PWR_ADC_S2F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5665_STO1_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, 1, rt5665_sto1_adc_l_mix, + ARRAY_SIZE(rt5665_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5665_STO1_ADC_DIG_VOL, + RT5665_R_MUTE_SFT, 1, rt5665_sto1_adc_r_mix, + ARRAY_SIZE(rt5665_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Stereo2 ADC MIXL", RT5665_STO2_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, 1, rt5665_sto2_adc_l_mix, + ARRAY_SIZE(rt5665_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo2 ADC MIXR", RT5665_STO2_ADC_DIG_VOL, + RT5665_R_MUTE_SFT, 1, rt5665_sto2_adc_r_mix, + ARRAY_SIZE(rt5665_sto2_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("ADC Mono Left Filter", RT5665_PWR_DIG_2, + RT5665_PWR_ADC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXL", RT5665_MONO_ADC_DIG_VOL, + RT5665_L_MUTE_SFT, 1, rt5665_mono_adc_l_mix, + ARRAY_SIZE(rt5665_mono_adc_l_mix)), + SND_SOC_DAPM_SUPPLY("ADC Mono Right Filter", RT5665_PWR_DIG_2, + RT5665_PWR_ADC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXR", RT5665_MONO_ADC_DIG_VOL, + RT5665_R_MUTE_SFT, 1, rt5665_mono_adc_r_mix, + ARRAY_SIZE(rt5665_mono_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1_1", RT5665_PWR_DIG_1, RT5665_PWR_I2S1_1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S1_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S1_2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2_1", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_1_BIT, + 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("I2S2_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_2_BIT, + 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("I2S3", RT5665_PWR_DIG_1, RT5665_PWR_I2S3_BIT, + 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC3 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC3 R", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("IF2_1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2_2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("IF3 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1_1_ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_adc1_mux), + SND_SOC_DAPM_MUX("IF1_1_ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_adc2_mux), + SND_SOC_DAPM_MUX("IF1_1_ADC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_adc3_mux), + SND_SOC_DAPM_PGA("IF1_1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX("IF1_2_ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_adc1_mux), + SND_SOC_DAPM_MUX("IF1_2_ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_adc2_mux), + SND_SOC_DAPM_MUX("IF1_2_ADC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_adc3_mux), + SND_SOC_DAPM_MUX("IF1_2_ADC4 Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_adc4_mux), + SND_SOC_DAPM_MUX("TDM1 slot 01 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm1_adc_mux), + SND_SOC_DAPM_MUX("TDM1 slot 23 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm1_adc_mux), + SND_SOC_DAPM_MUX("TDM1 slot 45 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm1_adc_mux), + SND_SOC_DAPM_MUX("TDM1 slot 67 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm1_adc_mux), + SND_SOC_DAPM_MUX("TDM2 slot 01 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm2_adc_mux), + SND_SOC_DAPM_MUX("TDM2 slot 23 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm2_adc_mux), + SND_SOC_DAPM_MUX("TDM2 slot 45 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm2_adc_mux), + SND_SOC_DAPM_MUX("TDM2 slot 67 Data Mux", SND_SOC_NOPM, 0, 0, + &rt5665_tdm2_adc_mux), + SND_SOC_DAPM_MUX("IF2_1 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_1_adc_in_mux), + SND_SOC_DAPM_MUX("IF2_2 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_2_adc_in_mux), + SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if3_adc_in_mux), + SND_SOC_DAPM_MUX("IF1_1 0 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 1 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 3 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 4 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 5 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 6 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_1 7 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 0 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 1 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 3 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 4 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 5 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 6 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1_2 7 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if1_2_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2_1 DAC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_1_dac_swap_mux), + SND_SOC_DAPM_MUX("IF2_1 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_1_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2_2 DAC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_2_dac_swap_mux), + SND_SOC_DAPM_MUX("IF2_2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if2_2_adc_swap_mux), + SND_SOC_DAPM_MUX("IF3 DAC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if3_dac_swap_mux), + SND_SOC_DAPM_MUX("IF3 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5665_if3_adc_swap_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 0", "AIF1_1 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 1", "AIF1_1 Capture", + 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 2", "AIF1_1 Capture", + 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 3", "AIF1_1 Capture", + 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 4", "AIF1_1 Capture", + 4, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 5", "AIF1_1 Capture", + 5, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 6", "AIF1_1 Capture", + 6, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_1TX slot 7", "AIF1_1 Capture", + 7, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 0", "AIF1_2 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 1", "AIF1_2 Capture", + 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 2", "AIF1_2 Capture", + 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 3", "AIF1_2 Capture", + 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 4", "AIF1_2 Capture", + 4, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 5", "AIF1_2 Capture", + 5, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 6", "AIF1_2 Capture", + 6, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1_2TX slot 7", "AIF1_2 Capture", + 7, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2_1TX", "AIF2_1 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2_2TX", "AIF2_2 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF3TX", "AIF3 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2_1RX", "AIF2_1 Playback", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2_2RX", "AIF2_2 Playback", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF3RX", "AIF3 Playback", + 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5665_dac_l_mix, ARRAY_SIZE(rt5665_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5665_dac_r_mix, ARRAY_SIZE(rt5665_dac_r_mix)), + + /* DAC channel Mux */ + SND_SOC_DAPM_MUX("DAC L1 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_r1_mux), + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_r2_mux), + SND_SOC_DAPM_MUX("DAC L3 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_l3_mux), + SND_SOC_DAPM_MUX("DAC R3 Mux", SND_SOC_NOPM, 0, 0, &rt5665_dac_r3_mux), + + SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, + &rt5665_alg_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, + &rt5665_alg_dac_r1_mux), + SND_SOC_DAPM_MUX("DAC L2 Source", SND_SOC_NOPM, 0, 0, + &rt5665_alg_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Source", SND_SOC_NOPM, 0, 0, + &rt5665_alg_dac_r2_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5665_PWR_DIG_2, + RT5665_PWR_DAC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Stereo2 Filter", RT5665_PWR_DIG_2, + RT5665_PWR_DAC_S2F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mono Left Filter", RT5665_PWR_DIG_2, + RT5665_PWR_DAC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mono Right Filter", RT5665_PWR_DIG_2, + RT5665_PWR_DAC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5665_sto1_dac_l_mix, ARRAY_SIZE(rt5665_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5665_sto1_dac_r_mix, ARRAY_SIZE(rt5665_sto1_dac_r_mix)), + SND_SOC_DAPM_MIXER("Stereo2 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5665_sto2_dac_l_mix, ARRAY_SIZE(rt5665_sto2_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo2 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5665_sto2_dac_r_mix, ARRAY_SIZE(rt5665_sto2_dac_r_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5665_mono_dac_l_mix, ARRAY_SIZE(rt5665_mono_dac_l_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5665_mono_dac_r_mix, ARRAY_SIZE(rt5665_mono_dac_r_mix)), + SND_SOC_DAPM_MUX("DAC MIXL", SND_SOC_NOPM, 0, 0, + &rt5665_dig_dac_mixl_mux), + SND_SOC_DAPM_MUX("DAC MIXR", SND_SOC_NOPM, 0, 0, + &rt5665_dig_dac_mixr_mux), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5665_PWR_DIG_1, + RT5665_PWR_DAC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5665_PWR_DIG_1, + RT5665_PWR_DAC_R2_BIT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC L2", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("DAC1 MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 1, RT5665_CHOP_DAC, + RT5665_CKGEN_DAC1_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC 2 Clock", 1, RT5665_CHOP_DAC, + RT5665_CKGEN_DAC2_SFT, 0, NULL, 0), + + /* OUT Mixer */ + SND_SOC_DAPM_MIXER("MONOVOL MIX", RT5665_PWR_MIXER, RT5665_PWR_MM_BIT, + 0, rt5665_monovol_mix, ARRAY_SIZE(rt5665_monovol_mix)), + SND_SOC_DAPM_MIXER("OUT MIXL", RT5665_PWR_MIXER, RT5665_PWR_OM_L_BIT, + 0, rt5665_out_l_mix, ARRAY_SIZE(rt5665_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5665_PWR_MIXER, RT5665_PWR_OM_R_BIT, + 0, rt5665_out_r_mix, ARRAY_SIZE(rt5665_out_r_mix)), + + /* Output Volume */ + SND_SOC_DAPM_SWITCH("MONOVOL", RT5665_PWR_VOL, RT5665_PWR_MV_BIT, 0, + &monovol_switch), + SND_SOC_DAPM_SWITCH("OUTVOL L", RT5665_PWR_VOL, RT5665_PWR_OV_L_BIT, 0, + &outvol_l_switch), + SND_SOC_DAPM_SWITCH("OUTVOL R", RT5665_PWR_VOL, RT5665_PWR_OV_R_BIT, 0, + &outvol_r_switch), + + /* MONO/HPO/LOUT */ + SND_SOC_DAPM_MIXER("Mono MIX", SND_SOC_NOPM, 0, 0, rt5665_mono_mix, + ARRAY_SIZE(rt5665_mono_mix)), + SND_SOC_DAPM_MIXER("LOUT L MIX", SND_SOC_NOPM, 0, 0, rt5665_lout_l_mix, + ARRAY_SIZE(rt5665_lout_l_mix)), + SND_SOC_DAPM_MIXER("LOUT R MIX", SND_SOC_NOPM, 0, 0, rt5665_lout_r_mix, + ARRAY_SIZE(rt5665_lout_r_mix)), + SND_SOC_DAPM_PGA_S("Mono Amp", 1, RT5665_PWR_ANLG_1, RT5665_PWR_MA_BIT, + 0, rt5665_mono_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5665_hp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_PGA_S("LOUT Amp", 1, RT5665_PWR_ANLG_1, + RT5665_PWR_LM_BIT, 0, rt5665_lout_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("Charge Pump", SND_SOC_NOPM, 0, 0, + rt5665_charge_pump_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SWITCH("Mono Playback", SND_SOC_NOPM, 0, 0, + &mono_switch), + SND_SOC_DAPM_SWITCH("HPO Playback", SND_SOC_NOPM, 0, 0, + &hpo_switch), + SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0, + &lout_l_switch), + SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0, + &lout_r_switch), + SND_SOC_DAPM_SWITCH("PDM L Playback", SND_SOC_NOPM, 0, 0, + &pdm_l_switch), + SND_SOC_DAPM_SWITCH("PDM R Playback", SND_SOC_NOPM, 0, 0, + &pdm_r_switch), + + /* PDM */ + SND_SOC_DAPM_SUPPLY("PDM Power", RT5665_PWR_DIG_2, + RT5665_PWR_PDM1_BIT, 0, NULL, 0), + SND_SOC_DAPM_MUX("PDM L Mux", SND_SOC_NOPM, + 0, 1, &rt5665_pdm_l_mux), + SND_SOC_DAPM_MUX("PDM R Mux", SND_SOC_NOPM, + 0, 1, &rt5665_pdm_r_mux), + + /* CLK DET */ + SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5665_CLK_DET, RT5665_SYS_CLK_DET, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET HP", RT5665_CLK_DET, RT5665_HP_CLK_DET, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET MONO", RT5665_CLK_DET, RT5665_MONO_CLK_DET, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET LOUT", RT5665_CLK_DET, RT5665_LOUT_CLK_DET, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET", RT5665_CLK_DET, RT5665_POW_CLK_DET, + 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("MONOOUT"), + SND_SOC_DAPM_OUTPUT("PDML"), + SND_SOC_DAPM_OUTPUT("PDMR"), +}; + +static const struct snd_soc_dapm_route rt5665_dapm_routes[] = { + /*PLL*/ + {"ADC Stereo1 Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"ADC Stereo2 Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"ADC Mono Left Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"ADC Mono Right Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"DAC Stereo1 Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"DAC Stereo2 Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"DAC Mono Left Filter", NULL, "PLL", is_sys_clk_from_pll}, + {"DAC Mono Right Filter", NULL, "PLL", is_sys_clk_from_pll}, + + /*ASRC*/ + {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, + {"ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc}, + {"ADC Mono Left Filter", NULL, "ADC Mono L ASRC", is_using_asrc}, + {"ADC Mono Right Filter", NULL, "ADC Mono R ASRC", is_using_asrc}, + {"DAC Mono Left Filter", NULL, "DAC Mono L ASRC", is_using_asrc}, + {"DAC Mono Right Filter", NULL, "DAC Mono R ASRC", is_using_asrc}, + {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, + {"DAC Stereo2 Filter", NULL, "DAC STO2 ASRC", is_using_asrc}, + {"I2S1 ASRC", NULL, "CLKDET"}, + {"I2S2 ASRC", NULL, "CLKDET"}, + {"I2S3 ASRC", NULL, "CLKDET"}, + + /*Vref*/ + {"Mic Det Power", NULL, "Vref2"}, + {"MICBIAS1", NULL, "Vref1"}, + {"MICBIAS1", NULL, "Vref2"}, + {"MICBIAS2", NULL, "Vref1"}, + {"MICBIAS2", NULL, "Vref2"}, + {"MICBIAS3", NULL, "Vref1"}, + {"MICBIAS3", NULL, "Vref2"}, + + {"Stereo1 DMIC L Mux", NULL, "DMIC STO1 ASRC"}, + {"Stereo1 DMIC R Mux", NULL, "DMIC STO1 ASRC"}, + {"Stereo2 DMIC L Mux", NULL, "DMIC STO2 ASRC"}, + {"Stereo2 DMIC R Mux", NULL, "DMIC STO2 ASRC"}, + {"Mono DMIC L Mux", NULL, "DMIC MONO L ASRC"}, + {"Mono DMIC R Mux", NULL, "DMIC MONO R ASRC"}, + + {"I2S1_1", NULL, "I2S1 ASRC"}, + {"I2S1_2", NULL, "I2S1 ASRC"}, + {"I2S2_1", NULL, "I2S2 ASRC"}, + {"I2S2_2", NULL, "I2S2 ASRC"}, + {"I2S3", NULL, "I2S3 ASRC"}, + + {"CLKDET SYS", NULL, "CLKDET"}, + {"CLKDET HP", NULL, "CLKDET"}, + {"CLKDET MONO", NULL, "CLKDET"}, + {"CLKDET LOUT", NULL, "CLKDET"}, + + {"IN1P", NULL, "LDO2"}, + {"IN2P", NULL, "LDO2"}, + {"IN3P", NULL, "LDO2"}, + {"IN4P", NULL, "LDO2"}, + + {"DMIC1", NULL, "DMIC L1"}, + {"DMIC1", NULL, "DMIC R1"}, + {"DMIC2", NULL, "DMIC L2"}, + {"DMIC2", NULL, "DMIC R2"}, + + {"BST1", NULL, "IN1P"}, + {"BST1", NULL, "IN1N"}, + {"BST1", NULL, "BST1 Power"}, + {"BST1", NULL, "BST1P Power"}, + {"BST2", NULL, "IN2P"}, + {"BST2", NULL, "IN2N"}, + {"BST2", NULL, "BST2 Power"}, + {"BST2", NULL, "BST2P Power"}, + {"BST3", NULL, "IN3P"}, + {"BST3", NULL, "IN3N"}, + {"BST3", NULL, "BST3 Power"}, + {"BST3", NULL, "BST3P Power"}, + {"BST4", NULL, "IN4P"}, + {"BST4", NULL, "IN4N"}, + {"BST4", NULL, "BST4 Power"}, + {"BST4", NULL, "BST4P Power"}, + {"BST1 CBJ", NULL, "IN1P"}, + {"BST1 CBJ", NULL, "IN1N"}, + {"BST1 CBJ", NULL, "CBJ Power"}, + {"CBJ Power", NULL, "Vref2"}, + + {"INL VOL", NULL, "IN3P"}, + {"INR VOL", NULL, "IN3N"}, + + {"RECMIX1L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX1L", "INL Switch", "INL VOL"}, + {"RECMIX1L", "INR Switch", "INR VOL"}, + {"RECMIX1L", "BST4 Switch", "BST4"}, + {"RECMIX1L", "BST3 Switch", "BST3"}, + {"RECMIX1L", "BST2 Switch", "BST2"}, + {"RECMIX1L", "BST1 Switch", "BST1"}, + {"RECMIX1L", NULL, "RECMIX1L Power"}, + + {"RECMIX1R", "MONOVOL Switch", "MONOVOL"}, + {"RECMIX1R", "INR Switch", "INR VOL"}, + {"RECMIX1R", "BST4 Switch", "BST4"}, + {"RECMIX1R", "BST3 Switch", "BST3"}, + {"RECMIX1R", "BST2 Switch", "BST2"}, + {"RECMIX1R", "BST1 Switch", "BST1"}, + {"RECMIX1R", NULL, "RECMIX1R Power"}, + + {"RECMIX2L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX2L", "INL Switch", "INL VOL"}, + {"RECMIX2L", "INR Switch", "INR VOL"}, + {"RECMIX2L", "BST4 Switch", "BST4"}, + {"RECMIX2L", "BST3 Switch", "BST3"}, + {"RECMIX2L", "BST2 Switch", "BST2"}, + {"RECMIX2L", "BST1 Switch", "BST1"}, + {"RECMIX2L", NULL, "RECMIX2L Power"}, + + {"RECMIX2R", "MONOVOL Switch", "MONOVOL"}, + {"RECMIX2R", "INL Switch", "INL VOL"}, + {"RECMIX2R", "INR Switch", "INR VOL"}, + {"RECMIX2R", "BST4 Switch", "BST4"}, + {"RECMIX2R", "BST3 Switch", "BST3"}, + {"RECMIX2R", "BST2 Switch", "BST2"}, + {"RECMIX2R", "BST1 Switch", "BST1"}, + {"RECMIX2R", NULL, "RECMIX2R Power"}, + + {"ADC1 L", NULL, "RECMIX1L"}, + {"ADC1 L", NULL, "ADC1 L Power"}, + {"ADC1 L", NULL, "ADC1 clock"}, + {"ADC1 R", NULL, "RECMIX1R"}, + {"ADC1 R", NULL, "ADC1 R Power"}, + {"ADC1 R", NULL, "ADC1 clock"}, + + {"ADC2 L", NULL, "RECMIX2L"}, + {"ADC2 L", NULL, "ADC2 L Power"}, + {"ADC2 L", NULL, "ADC2 clock"}, + {"ADC2 R", NULL, "RECMIX2R"}, + {"ADC2 R", NULL, "ADC2 R Power"}, + {"ADC2 R", NULL, "ADC2 clock"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC L2", NULL, "DMIC CLK"}, + {"DMIC L2", NULL, "DMIC2 Power"}, + {"DMIC R2", NULL, "DMIC CLK"}, + {"DMIC R2", NULL, "DMIC2 Power"}, + + {"Stereo1 DMIC L Mux", "DMIC1", "DMIC L1"}, + {"Stereo1 DMIC L Mux", "DMIC2", "DMIC L2"}, + + {"Stereo1 DMIC R Mux", "DMIC1", "DMIC R1"}, + {"Stereo1 DMIC R Mux", "DMIC2", "DMIC R2"}, + + {"Mono DMIC L Mux", "DMIC1 L", "DMIC L1"}, + {"Mono DMIC L Mux", "DMIC2 L", "DMIC L2"}, + + {"Mono DMIC R Mux", "DMIC1 R", "DMIC R1"}, + {"Mono DMIC R Mux", "DMIC2 R", "DMIC R2"}, + + {"Stereo2 DMIC L Mux", "DMIC1", "DMIC L1"}, + {"Stereo2 DMIC L Mux", "DMIC2", "DMIC L2"}, + + {"Stereo2 DMIC R Mux", "DMIC1", "DMIC R1"}, + {"Stereo2 DMIC R Mux", "DMIC2", "DMIC R2"}, + + {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC L Mux", "ADC2 L", "ADC2 L"}, + {"Stereo1 ADC L Mux", "ADC2 R", "ADC2 R"}, + {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC R Mux", "ADC2 L", "ADC2 L"}, + {"Stereo1 ADC R Mux", "ADC2 R", "ADC2 R"}, + + {"Stereo1 DD L Mux", "STO2 DAC", "Stereo2 DAC MIXL"}, + {"Stereo1 DD L Mux", "MONO DAC", "Mono DAC MIXL"}, + + {"Stereo1 DD R Mux", "STO2 DAC", "Stereo2 DAC MIXR"}, + {"Stereo1 DD R Mux", "MONO DAC", "Mono DAC MIXR"}, + + {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"}, + {"Stereo1 ADC L1 Mux", "DD Mux", "Stereo1 DD L Mux"}, + {"Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC L Mux"}, + {"Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"}, + {"Stereo1 ADC R1 Mux", "DD Mux", "Stereo1 DD R Mux"}, + {"Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC R Mux"}, + {"Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR"}, + + {"Mono ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Mono ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Mono ADC L Mux", "ADC2 L", "ADC2 L"}, + {"Mono ADC L Mux", "ADC2 R", "ADC2 R"}, + + {"Mono ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Mono ADC R Mux", "ADC1 R", "ADC1 R"}, + {"Mono ADC R Mux", "ADC2 L", "ADC2 L"}, + {"Mono ADC R Mux", "ADC2 R", "ADC2 R"}, + + {"Mono DD L Mux", "STO2 DAC", "Stereo2 DAC MIXL"}, + {"Mono DD L Mux", "MONO DAC", "Mono DAC MIXL"}, + + {"Mono DD R Mux", "STO2 DAC", "Stereo2 DAC MIXR"}, + {"Mono DD R Mux", "MONO DAC", "Mono DAC MIXR"}, + + {"Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux"}, + {"Mono ADC L2 Mux", "DAC MIXL", "DAC MIXL"}, + {"Mono ADC L1 Mux", "DD Mux", "Mono DD L Mux"}, + {"Mono ADC L1 Mux", "ADC", "Mono ADC L Mux"}, + + {"Mono ADC R1 Mux", "DD Mux", "Mono DD R Mux"}, + {"Mono ADC R1 Mux", "ADC", "Mono ADC R Mux"}, + {"Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux"}, + {"Mono ADC R2 Mux", "DAC MIXR", "DAC MIXR"}, + + {"Stereo2 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo2 ADC L Mux", "ADC2 L", "ADC2 L"}, + {"Stereo2 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo2 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo2 ADC R Mux", "ADC2 L", "ADC2 L"}, + {"Stereo2 ADC R Mux", "ADC1 R", "ADC1 R"}, + + {"Stereo2 DD L Mux", "STO2 DAC", "Stereo2 DAC MIXL"}, + {"Stereo2 DD L Mux", "MONO DAC", "Mono DAC MIXL"}, + + {"Stereo2 DD R Mux", "STO2 DAC", "Stereo2 DAC MIXR"}, + {"Stereo2 DD R Mux", "MONO DAC", "Mono DAC MIXR"}, + + {"Stereo2 ADC L1 Mux", "ADC", "Stereo2 ADC L Mux"}, + {"Stereo2 ADC L1 Mux", "DD Mux", "Stereo2 DD L Mux"}, + {"Stereo2 ADC L2 Mux", "DMIC", "Stereo2 DMIC L Mux"}, + {"Stereo2 ADC L2 Mux", "DAC MIX", "DAC MIXL"}, + + {"Stereo2 ADC R1 Mux", "ADC", "Stereo2 ADC R Mux"}, + {"Stereo2 ADC R1 Mux", "DD Mux", "Stereo2 DD R Mux"}, + {"Stereo2 ADC R2 Mux", "DMIC", "Stereo2 DMIC R Mux"}, + {"Stereo2 ADC R2 Mux", "DAC MIX", "DAC MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, + + {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"}, + {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"}, + {"Mono ADC MIXL", NULL, "ADC Mono Left Filter"}, + + {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"}, + {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"}, + {"Mono ADC MIXR", NULL, "ADC Mono Right Filter"}, + + {"Stereo2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux"}, + {"Stereo2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux"}, + {"Stereo2 ADC MIXL", NULL, "ADC Stereo2 Filter"}, + + {"Stereo2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux"}, + {"Stereo2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC R2 Mux"}, + {"Stereo2 ADC MIXR", NULL, "ADC Stereo2 Filter"}, + + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, + {"Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL"}, + {"Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR"}, + {"Mono ADC MIX", NULL, "Mono ADC MIXL"}, + {"Mono ADC MIX", NULL, "Mono ADC MIXR"}, + + {"IF1_1_ADC1 Mux", "STO1 ADC", "Stereo1 ADC MIX"}, + {"IF1_1_ADC1 Mux", "IF2_1 DAC", "IF2_1 DAC"}, + {"IF1_1_ADC2 Mux", "STO2 ADC", "Stereo2 ADC MIX"}, + {"IF1_1_ADC2 Mux", "IF2_2 DAC", "IF2_2 DAC"}, + {"IF1_1_ADC3 Mux", "MONO ADC", "Mono ADC MIX"}, + {"IF1_1_ADC3 Mux", "IF3 DAC", "IF3 DAC"}, + {"IF1_1_ADC4", NULL, "DAC1 MIX"}, + + {"IF1_2_ADC1 Mux", "STO1 ADC", "Stereo1 ADC MIX"}, + {"IF1_2_ADC1 Mux", "IF1 DAC", "IF1 DAC1"}, + {"IF1_2_ADC2 Mux", "STO2 ADC", "Stereo2 ADC MIX"}, + {"IF1_2_ADC2 Mux", "IF2_1 DAC", "IF2_1 DAC"}, + {"IF1_2_ADC3 Mux", "MONO ADC", "Mono ADC MIX"}, + {"IF1_2_ADC3 Mux", "IF2_2 DAC", "IF2_2 DAC"}, + {"IF1_2_ADC4 Mux", "DAC1", "DAC1 MIX"}, + {"IF1_2_ADC4 Mux", "IF3 DAC", "IF3 DAC"}, + + {"TDM1 slot 01 Data Mux", "1234", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "1243", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "1324", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "1342", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "1432", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "1423", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 01 Data Mux", "2134", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "2143", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "2314", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "2341", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "2431", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "2413", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 01 Data Mux", "3124", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "3142", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "3214", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "3241", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "3412", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "3421", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 01 Data Mux", "4123", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", "4132", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", "4213", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", "4231", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", "4312", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", "4321", "IF1_1_ADC4"}, + {"TDM1 slot 01 Data Mux", NULL, "I2S1_1"}, + + {"TDM1 slot 23 Data Mux", "1234", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "1243", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "1324", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", "1342", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", "1432", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "1423", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "2134", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "2143", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "2314", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", "2341", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", "2431", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "2413", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "3124", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "3142", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "3214", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "3241", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "3412", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "3421", "IF1_1_ADC4"}, + {"TDM1 slot 23 Data Mux", "4123", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "4132", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 23 Data Mux", "4213", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "4231", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 23 Data Mux", "4312", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", "4321", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 23 Data Mux", NULL, "I2S1_1"}, + + {"TDM1 slot 45 Data Mux", "1234", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "1243", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "1324", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", "1342", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "1432", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "1423", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", "2134", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "2143", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "2314", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "2341", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "2431", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "2413", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "3124", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", "3142", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "3214", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "3241", "IF1_1_ADC4"}, + {"TDM1 slot 45 Data Mux", "3412", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "3421", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", "4123", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", "4132", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "4213", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "4231", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 45 Data Mux", "4312", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 45 Data Mux", "4321", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 45 Data Mux", NULL, "I2S1_1"}, + + {"TDM1 slot 67 Data Mux", "1234", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "1243", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "1324", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "1342", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "1432", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "1423", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "2134", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "2143", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "2314", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "2341", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", "2431", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", "2413", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "3124", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "3142", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "3214", "IF1_1_ADC4"}, + {"TDM1 slot 67 Data Mux", "3241", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", "3412", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "3421", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", "4123", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "4132", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "4213", "IF1_1_ADC3 Mux"}, + {"TDM1 slot 67 Data Mux", "4231", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", "4312", "IF1_1_ADC2 Mux"}, + {"TDM1 slot 67 Data Mux", "4321", "IF1_1_ADC1 Mux"}, + {"TDM1 slot 67 Data Mux", NULL, "I2S1_1"}, + + + {"TDM2 slot 01 Data Mux", "1234", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "1243", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "1324", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "1342", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "1432", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "1423", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 01 Data Mux", "2134", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "2143", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "2314", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "2341", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "2431", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "2413", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 01 Data Mux", "3124", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "3142", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "3214", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "3241", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "3412", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "3421", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 01 Data Mux", "4123", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", "4132", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", "4213", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", "4231", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", "4312", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", "4321", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 01 Data Mux", NULL, "I2S1_2"}, + + {"TDM2 slot 23 Data Mux", "1234", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "1243", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "1324", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", "1342", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", "1432", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "1423", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "2134", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "2143", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "2314", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", "2341", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", "2431", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "2413", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "3124", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "3142", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "3214", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "3241", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "3412", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "3421", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 23 Data Mux", "4123", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "4132", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 23 Data Mux", "4213", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "4231", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 23 Data Mux", "4312", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", "4321", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 23 Data Mux", NULL, "I2S1_2"}, + + {"TDM2 slot 45 Data Mux", "1234", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "1243", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "1324", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", "1342", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "1432", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "1423", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", "2134", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "2143", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "2314", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "2341", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "2431", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "2413", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "3124", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", "3142", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "3214", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "3241", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 45 Data Mux", "3412", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "3421", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", "4123", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", "4132", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "4213", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "4231", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 45 Data Mux", "4312", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 45 Data Mux", "4321", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 45 Data Mux", NULL, "I2S1_2"}, + + {"TDM2 slot 67 Data Mux", "1234", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "1243", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "1324", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "1342", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "1432", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "1423", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "2134", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "2143", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "2314", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "2341", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", "2431", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", "2413", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "3124", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "3142", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "3214", "IF1_2_ADC4 Mux"}, + {"TDM2 slot 67 Data Mux", "3241", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", "3412", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "3421", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", "4123", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "4132", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "4213", "IF1_2_ADC3 Mux"}, + {"TDM2 slot 67 Data Mux", "4231", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", "4312", "IF1_2_ADC2 Mux"}, + {"TDM2 slot 67 Data Mux", "4321", "IF1_2_ADC1 Mux"}, + {"TDM2 slot 67 Data Mux", NULL, "I2S1_2"}, + + {"IF1_1 0 ADC Swap Mux", "L/R", "TDM1 slot 01 Data Mux"}, + {"IF1_1 0 ADC Swap Mux", "L/L", "TDM1 slot 01 Data Mux"}, + {"IF1_1 1 ADC Swap Mux", "R/L", "TDM1 slot 01 Data Mux"}, + {"IF1_1 1 ADC Swap Mux", "R/R", "TDM1 slot 01 Data Mux"}, + {"IF1_1 2 ADC Swap Mux", "L/R", "TDM1 slot 23 Data Mux"}, + {"IF1_1 2 ADC Swap Mux", "R/L", "TDM1 slot 23 Data Mux"}, + {"IF1_1 3 ADC Swap Mux", "L/L", "TDM1 slot 23 Data Mux"}, + {"IF1_1 3 ADC Swap Mux", "R/R", "TDM1 slot 23 Data Mux"}, + {"IF1_1 4 ADC Swap Mux", "L/R", "TDM1 slot 45 Data Mux"}, + {"IF1_1 4 ADC Swap Mux", "R/L", "TDM1 slot 45 Data Mux"}, + {"IF1_1 5 ADC Swap Mux", "L/L", "TDM1 slot 45 Data Mux"}, + {"IF1_1 5 ADC Swap Mux", "R/R", "TDM1 slot 45 Data Mux"}, + {"IF1_1 6 ADC Swap Mux", "L/R", "TDM1 slot 67 Data Mux"}, + {"IF1_1 6 ADC Swap Mux", "R/L", "TDM1 slot 67 Data Mux"}, + {"IF1_1 7 ADC Swap Mux", "L/L", "TDM1 slot 67 Data Mux"}, + {"IF1_1 7 ADC Swap Mux", "R/R", "TDM1 slot 67 Data Mux"}, + {"IF1_2 0 ADC Swap Mux", "L/R", "TDM2 slot 01 Data Mux"}, + {"IF1_2 0 ADC Swap Mux", "R/L", "TDM2 slot 01 Data Mux"}, + {"IF1_2 1 ADC Swap Mux", "L/L", "TDM2 slot 01 Data Mux"}, + {"IF1_2 1 ADC Swap Mux", "R/R", "TDM2 slot 01 Data Mux"}, + {"IF1_2 2 ADC Swap Mux", "L/R", "TDM2 slot 23 Data Mux"}, + {"IF1_2 2 ADC Swap Mux", "R/L", "TDM2 slot 23 Data Mux"}, + {"IF1_2 3 ADC Swap Mux", "L/L", "TDM2 slot 23 Data Mux"}, + {"IF1_2 3 ADC Swap Mux", "R/R", "TDM2 slot 23 Data Mux"}, + {"IF1_2 4 ADC Swap Mux", "L/R", "TDM2 slot 45 Data Mux"}, + {"IF1_2 4 ADC Swap Mux", "R/L", "TDM2 slot 45 Data Mux"}, + {"IF1_2 5 ADC Swap Mux", "L/L", "TDM2 slot 45 Data Mux"}, + {"IF1_2 5 ADC Swap Mux", "R/R", "TDM2 slot 45 Data Mux"}, + {"IF1_2 6 ADC Swap Mux", "L/R", "TDM2 slot 67 Data Mux"}, + {"IF1_2 6 ADC Swap Mux", "R/L", "TDM2 slot 67 Data Mux"}, + {"IF1_2 7 ADC Swap Mux", "L/L", "TDM2 slot 67 Data Mux"}, + {"IF1_2 7 ADC Swap Mux", "R/R", "TDM2 slot 67 Data Mux"}, + + {"IF2_1 ADC Mux", "STO1 ADC", "Stereo1 ADC MIX"}, + {"IF2_1 ADC Mux", "STO2 ADC", "Stereo2 ADC MIX"}, + {"IF2_1 ADC Mux", "MONO ADC", "Mono ADC MIX"}, + {"IF2_1 ADC Mux", "IF1 DAC1", "IF1 DAC1"}, + {"IF2_1 ADC Mux", "IF1 DAC2", "IF1 DAC2"}, + {"IF2_1 ADC Mux", "IF2_2 DAC", "IF2_2 DAC"}, + {"IF2_1 ADC Mux", "IF3 DAC", "IF3 DAC"}, + {"IF2_1 ADC Mux", "DAC1 MIX", "DAC1 MIX"}, + {"IF2_1 ADC", NULL, "IF2_1 ADC Mux"}, + {"IF2_1 ADC", NULL, "I2S2_1"}, + + {"IF2_2 ADC Mux", "STO1 ADC", "Stereo1 ADC MIX"}, + {"IF2_2 ADC Mux", "STO2 ADC", "Stereo2 ADC MIX"}, + {"IF2_2 ADC Mux", "MONO ADC", "Mono ADC MIX"}, + {"IF2_2 ADC Mux", "IF1 DAC1", "IF1 DAC1"}, + {"IF2_2 ADC Mux", "IF1 DAC2", "IF1 DAC2"}, + {"IF2_2 ADC Mux", "IF2_1 DAC", "IF2_1 DAC"}, + {"IF2_2 ADC Mux", "IF3 DAC", "IF3 DAC"}, + {"IF2_2 ADC Mux", "DAC1 MIX", "DAC1 MIX"}, + {"IF2_2 ADC", NULL, "IF2_2 ADC Mux"}, + {"IF2_2 ADC", NULL, "I2S2_2"}, + + {"IF3 ADC Mux", "STO1 ADC", "Stereo1 ADC MIX"}, + {"IF3 ADC Mux", "STO2 ADC", "Stereo2 ADC MIX"}, + {"IF3 ADC Mux", "MONO ADC", "Mono ADC MIX"}, + {"IF3 ADC Mux", "IF1 DAC1", "IF1 DAC1"}, + {"IF3 ADC Mux", "IF1 DAC2", "IF1 DAC2"}, + {"IF3 ADC Mux", "IF2_1 DAC", "IF2_1 DAC"}, + {"IF3 ADC Mux", "IF2_2 DAC", "IF2_2 DAC"}, + {"IF3 ADC Mux", "DAC1 MIX", "DAC1 MIX"}, + {"IF3 ADC", NULL, "IF3 ADC Mux"}, + {"IF3 ADC", NULL, "I2S3"}, + + {"AIF1_1TX slot 0", NULL, "IF1_1 0 ADC Swap Mux"}, + {"AIF1_1TX slot 1", NULL, "IF1_1 1 ADC Swap Mux"}, + {"AIF1_1TX slot 2", NULL, "IF1_1 2 ADC Swap Mux"}, + {"AIF1_1TX slot 3", NULL, "IF1_1 3 ADC Swap Mux"}, + {"AIF1_1TX slot 4", NULL, "IF1_1 4 ADC Swap Mux"}, + {"AIF1_1TX slot 5", NULL, "IF1_1 5 ADC Swap Mux"}, + {"AIF1_1TX slot 6", NULL, "IF1_1 6 ADC Swap Mux"}, + {"AIF1_1TX slot 7", NULL, "IF1_1 7 ADC Swap Mux"}, + {"AIF1_2TX slot 0", NULL, "IF1_2 0 ADC Swap Mux"}, + {"AIF1_2TX slot 1", NULL, "IF1_2 1 ADC Swap Mux"}, + {"AIF1_2TX slot 2", NULL, "IF1_2 2 ADC Swap Mux"}, + {"AIF1_2TX slot 3", NULL, "IF1_2 3 ADC Swap Mux"}, + {"AIF1_2TX slot 4", NULL, "IF1_2 4 ADC Swap Mux"}, + {"AIF1_2TX slot 5", NULL, "IF1_2 5 ADC Swap Mux"}, + {"AIF1_2TX slot 6", NULL, "IF1_2 6 ADC Swap Mux"}, + {"AIF1_2TX slot 7", NULL, "IF1_2 7 ADC Swap Mux"}, + {"IF2_1 ADC Swap Mux", "L/R", "IF2_1 ADC"}, + {"IF2_1 ADC Swap Mux", "R/L", "IF2_1 ADC"}, + {"IF2_1 ADC Swap Mux", "L/L", "IF2_1 ADC"}, + {"IF2_1 ADC Swap Mux", "R/R", "IF2_1 ADC"}, + {"AIF2_1TX", NULL, "IF2_1 ADC Swap Mux"}, + {"IF2_2 ADC Swap Mux", "L/R", "IF2_2 ADC"}, + {"IF2_2 ADC Swap Mux", "R/L", "IF2_2 ADC"}, + {"IF2_2 ADC Swap Mux", "L/L", "IF2_2 ADC"}, + {"IF2_2 ADC Swap Mux", "R/R", "IF2_2 ADC"}, + {"AIF2_2TX", NULL, "IF2_2 ADC Swap Mux"}, + {"IF3 ADC Swap Mux", "L/R", "IF3 ADC"}, + {"IF3 ADC Swap Mux", "R/L", "IF3 ADC"}, + {"IF3 ADC Swap Mux", "L/L", "IF3 ADC"}, + {"IF3 ADC Swap Mux", "R/R", "IF3 ADC"}, + {"AIF3TX", NULL, "IF3 ADC Swap Mux"}, + + {"IF1 DAC1", NULL, "AIF1RX"}, + {"IF1 DAC2", NULL, "AIF1RX"}, + {"IF1 DAC3", NULL, "AIF1RX"}, + {"IF2_1 DAC Swap Mux", "L/R", "AIF2_1RX"}, + {"IF2_1 DAC Swap Mux", "R/L", "AIF2_1RX"}, + {"IF2_1 DAC Swap Mux", "L/L", "AIF2_1RX"}, + {"IF2_1 DAC Swap Mux", "R/R", "AIF2_1RX"}, + {"IF2_2 DAC Swap Mux", "L/R", "AIF2_2RX"}, + {"IF2_2 DAC Swap Mux", "R/L", "AIF2_2RX"}, + {"IF2_2 DAC Swap Mux", "L/L", "AIF2_2RX"}, + {"IF2_2 DAC Swap Mux", "R/R", "AIF2_2RX"}, + {"IF2_1 DAC", NULL, "IF2_1 DAC Swap Mux"}, + {"IF2_2 DAC", NULL, "IF2_2 DAC Swap Mux"}, + {"IF3 DAC Swap Mux", "L/R", "AIF3RX"}, + {"IF3 DAC Swap Mux", "R/L", "AIF3RX"}, + {"IF3 DAC Swap Mux", "L/L", "AIF3RX"}, + {"IF3 DAC Swap Mux", "R/R", "AIF3RX"}, + {"IF3 DAC", NULL, "IF3 DAC Swap Mux"}, + + {"IF1 DAC1", NULL, "I2S1_1"}, + {"IF1 DAC2", NULL, "I2S1_1"}, + {"IF1 DAC3", NULL, "I2S1_1"}, + {"IF2_1 DAC", NULL, "I2S2_1"}, + {"IF2_2 DAC", NULL, "I2S2_2"}, + {"IF3 DAC", NULL, "I2S3"}, + + {"IF1 DAC1 L", NULL, "IF1 DAC1"}, + {"IF1 DAC1 R", NULL, "IF1 DAC1"}, + {"IF1 DAC2 L", NULL, "IF1 DAC2"}, + {"IF1 DAC2 R", NULL, "IF1 DAC2"}, + {"IF1 DAC3 L", NULL, "IF1 DAC3"}, + {"IF1 DAC3 R", NULL, "IF1 DAC3"}, + {"IF2_1 DAC L", NULL, "IF2_1 DAC"}, + {"IF2_1 DAC R", NULL, "IF2_1 DAC"}, + {"IF2_2 DAC L", NULL, "IF2_2 DAC"}, + {"IF2_2 DAC R", NULL, "IF2_2 DAC"}, + {"IF3 DAC L", NULL, "IF3 DAC"}, + {"IF3 DAC R", NULL, "IF3 DAC"}, + + {"DAC L1 Mux", "IF1 DAC1", "IF1 DAC1 L"}, + {"DAC L1 Mux", "IF2_1 DAC", "IF2_1 DAC L"}, + {"DAC L1 Mux", "IF2_2 DAC", "IF2_2 DAC L"}, + {"DAC L1 Mux", "IF3 DAC", "IF3 DAC L"}, + {"DAC L1 Mux", NULL, "DAC Stereo1 Filter"}, + + {"DAC R1 Mux", "IF1 DAC1", "IF1 DAC1 R"}, + {"DAC R1 Mux", "IF2_1 DAC", "IF2_1 DAC R"}, + {"DAC R1 Mux", "IF2_2 DAC", "IF2_2 DAC R"}, + {"DAC R1 Mux", "IF3 DAC", "IF3 DAC R"}, + {"DAC R1 Mux", NULL, "DAC Stereo1 Filter"}, + + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC1 MIXL", "DAC1 Switch", "DAC L1 Mux"}, + {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC1 MIXR", "DAC1 Switch", "DAC R1 Mux"}, + + {"DAC1 MIX", NULL, "DAC1 MIXL"}, + {"DAC1 MIX", NULL, "DAC1 MIXR"}, + + {"DAC L2 Mux", "IF1 DAC2", "IF1 DAC2 L"}, + {"DAC L2 Mux", "IF2_1 DAC", "IF2_1 DAC L"}, + {"DAC L2 Mux", "IF2_2 DAC", "IF2_2 DAC L"}, + {"DAC L2 Mux", "IF3 DAC", "IF3 DAC L"}, + {"DAC L2 Mux", "Mono ADC MIX", "Mono ADC MIXL"}, + {"DAC L2 Mux", NULL, "DAC Mono Left Filter"}, + + {"DAC R2 Mux", "IF1 DAC2", "IF1 DAC2 R"}, + {"DAC R2 Mux", "IF2_1 DAC", "IF2_1 DAC R"}, + {"DAC R2 Mux", "IF2_2 DAC", "IF2_2 DAC R"}, + {"DAC R2 Mux", "IF3 DAC", "IF3 DAC R"}, + {"DAC R2 Mux", "Mono ADC MIX", "Mono ADC MIXR"}, + {"DAC R2 Mux", NULL, "DAC Mono Right Filter"}, + + {"DAC L3 Mux", "IF1 DAC2", "IF1 DAC2 L"}, + {"DAC L3 Mux", "IF2_1 DAC", "IF2_1 DAC L"}, + {"DAC L3 Mux", "IF2_2 DAC", "IF2_2 DAC L"}, + {"DAC L3 Mux", "IF3 DAC", "IF3 DAC L"}, + {"DAC L3 Mux", "STO2 ADC MIX", "Stereo2 ADC MIXL"}, + {"DAC L3 Mux", NULL, "DAC Stereo2 Filter"}, + + {"DAC R3 Mux", "IF1 DAC2", "IF1 DAC2 R"}, + {"DAC R3 Mux", "IF2_1 DAC", "IF2_1 DAC R"}, + {"DAC R3 Mux", "IF2_2 DAC", "IF2_2 DAC R"}, + {"DAC R3 Mux", "IF3 DAC", "IF3 DAC R"}, + {"DAC R3 Mux", "STO2 ADC MIX", "Stereo2 ADC MIXR"}, + {"DAC R3 Mux", NULL, "DAC Stereo2 Filter"}, + + {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"Stereo1 DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"}, + + {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"}, + {"Stereo1 DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + + {"Stereo2 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo2 DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"Stereo2 DAC MIXL", "DAC L3 Switch", "DAC L3 Mux"}, + + {"Stereo2 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo2 DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + {"Stereo2 DAC MIXR", "DAC R3 Switch", "DAC R3 Mux"}, + + {"Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Mono DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"}, + {"Mono DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + {"Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"}, + {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + + {"DAC MIXL", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC MIXL", "Stereo2 DAC Mixer", "Stereo2 DAC MIXL"}, + {"DAC MIXL", "Mono DAC Mixer", "Mono DAC MIXL"}, + {"DAC MIXR", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + {"DAC MIXR", "Stereo2 DAC Mixer", "Stereo2 DAC MIXR"}, + {"DAC MIXR", "Mono DAC Mixer", "Mono DAC MIXR"}, + + {"DAC L1 Source", "DAC1", "DAC1 MIXL"}, + {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC L1 Source", "DMIC1", "DMIC L1"}, + {"DAC R1 Source", "DAC1", "DAC1 MIXR"}, + {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + {"DAC R1 Source", "DMIC1", "DMIC R1"}, + + {"DAC L2 Source", "DAC2", "DAC L2 Mux"}, + {"DAC L2 Source", "Mono DAC Mixer", "Mono DAC MIXL"}, + {"DAC L2 Source", NULL, "DAC L2 Power"}, + {"DAC R2 Source", "DAC2", "DAC R2 Mux"}, + {"DAC R2 Source", "Mono DAC Mixer", "Mono DAC MIXR"}, + {"DAC R2 Source", NULL, "DAC R2 Power"}, + + {"DAC L1", NULL, "DAC L1 Source"}, + {"DAC R1", NULL, "DAC R1 Source"}, + {"DAC L2", NULL, "DAC L2 Source"}, + {"DAC R2", NULL, "DAC R2 Source"}, + + {"DAC L1", NULL, "DAC 1 Clock"}, + {"DAC R1", NULL, "DAC 1 Clock"}, + {"DAC L2", NULL, "DAC 2 Clock"}, + {"DAC R2", NULL, "DAC 2 Clock"}, + + {"MONOVOL MIX", "DAC L2 Switch", "DAC L2"}, + {"MONOVOL MIX", "RECMIX2L Switch", "RECMIX2L"}, + {"MONOVOL MIX", "BST1 Switch", "BST1"}, + {"MONOVOL MIX", "BST2 Switch", "BST2"}, + {"MONOVOL MIX", "BST3 Switch", "BST3"}, + + {"OUT MIXL", "DAC L2 Switch", "DAC L2"}, + {"OUT MIXL", "INL Switch", "INL VOL"}, + {"OUT MIXL", "BST1 Switch", "BST1"}, + {"OUT MIXL", "BST2 Switch", "BST2"}, + {"OUT MIXL", "BST3 Switch", "BST3"}, + {"OUT MIXR", "DAC R2 Switch", "DAC R2"}, + {"OUT MIXR", "INR Switch", "INR VOL"}, + {"OUT MIXR", "BST2 Switch", "BST2"}, + {"OUT MIXR", "BST3 Switch", "BST3"}, + {"OUT MIXR", "BST4 Switch", "BST4"}, + + {"MONOVOL", "Switch", "MONOVOL MIX"}, + {"Mono MIX", "DAC L2 Switch", "DAC L2"}, + {"Mono MIX", "MONOVOL Switch", "MONOVOL"}, + {"Mono Amp", NULL, "Mono MIX"}, + {"Mono Amp", NULL, "Vref2"}, + {"Mono Amp", NULL, "Vref3"}, + {"Mono Amp", NULL, "CLKDET SYS"}, + {"Mono Amp", NULL, "CLKDET MONO"}, + {"Mono Playback", "Switch", "Mono Amp"}, + {"MONOOUT", NULL, "Mono Playback"}, + + {"HP Amp", NULL, "DAC L1"}, + {"HP Amp", NULL, "DAC R1"}, + {"HP Amp", NULL, "Charge Pump"}, + {"HP Amp", NULL, "CLKDET SYS"}, + {"HP Amp", NULL, "CLKDET HP"}, + {"HP Amp", NULL, "CBJ Power"}, + {"HP Amp", NULL, "Vref2"}, + {"HPO Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HPO Playback"}, + {"HPOR", NULL, "HPO Playback"}, + + {"OUTVOL L", "Switch", "OUT MIXL"}, + {"OUTVOL R", "Switch", "OUT MIXR"}, + {"LOUT L MIX", "DAC L2 Switch", "DAC L2"}, + {"LOUT L MIX", "OUTVOL L Switch", "OUTVOL L"}, + {"LOUT R MIX", "DAC R2 Switch", "DAC R2"}, + {"LOUT R MIX", "OUTVOL R Switch", "OUTVOL R"}, + {"LOUT Amp", NULL, "LOUT L MIX"}, + {"LOUT Amp", NULL, "LOUT R MIX"}, + {"LOUT Amp", NULL, "Vref1"}, + {"LOUT Amp", NULL, "Vref2"}, + {"LOUT Amp", NULL, "CLKDET SYS"}, + {"LOUT Amp", NULL, "CLKDET LOUT"}, + {"LOUT L Playback", "Switch", "LOUT Amp"}, + {"LOUT R Playback", "Switch", "LOUT Amp"}, + {"LOUTL", NULL, "LOUT L Playback"}, + {"LOUTR", NULL, "LOUT R Playback"}, + + {"PDM L Mux", "Mono DAC", "Mono DAC MIXL"}, + {"PDM L Mux", "Stereo1 DAC", "Stereo1 DAC MIXL"}, + {"PDM L Mux", "Stereo2 DAC", "Stereo2 DAC MIXL"}, + {"PDM L Mux", NULL, "PDM Power"}, + {"PDM R Mux", "Mono DAC", "Mono DAC MIXR"}, + {"PDM R Mux", "Stereo1 DAC", "Stereo1 DAC MIXR"}, + {"PDM R Mux", "Stereo2 DAC", "Stereo2 DAC MIXR"}, + {"PDM R Mux", NULL, "PDM Power"}, + {"PDM L Playback", "Switch", "PDM L Mux"}, + {"PDM R Playback", "Switch", "PDM R Mux"}, + {"PDML", NULL, "PDM L Playback"}, + {"PDMR", NULL, "PDM R Playback"}, +}; + +static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int val = 0; + + if (rx_mask || tx_mask) + val |= RT5665_I2S1_MODE_TDM; + + switch (slots) { + case 4: + val |= RT5665_TDM_IN_CH_4; + val |= RT5665_TDM_OUT_CH_4; + break; + case 6: + val |= RT5665_TDM_IN_CH_6; + val |= RT5665_TDM_OUT_CH_6; + break; + case 8: + val |= RT5665_TDM_IN_CH_8; + val |= RT5665_TDM_OUT_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + val |= RT5665_TDM_IN_LEN_20; + val |= RT5665_TDM_OUT_LEN_20; + break; + case 24: + val |= RT5665_TDM_IN_LEN_24; + val |= RT5665_TDM_OUT_LEN_24; + break; + case 32: + val |= RT5665_TDM_IN_LEN_32; + val |= RT5665_TDM_OUT_LEN_32; + break; + case 16: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5665_TDM_CTRL_1, + RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK | + RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK | + RT5665_TDM_OUT_LEN_MASK, val); + + return 0; +} + + +static int rt5665_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, reg_clk, mask_clk, val_bits = 0x0100; + int pre_div, frame_size; + + rt5665->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5665->sysclk, rt5665->lrck[dai->id]); + if (pre_div < 0) { + dev_warn(component->dev, "Force using PLL"); + snd_soc_component_set_pll(component, 0, RT5665_PLL1_S_MCLK, + rt5665->sysclk, rt5665->lrck[dai->id] * 512); + snd_soc_component_set_sysclk(component, RT5665_SCLK_S_PLL1, 0, + rt5665->lrck[dai->id] * 512, 0); + pre_div = 1; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + + dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt5665->lrck[dai->id], pre_div, dai->id); + + switch (params_width(params)) { + case 16: + val_bits = 0x0100; + break; + case 20: + val_len |= RT5665_I2S_DL_20; + val_bits = 0x1300; + break; + case 24: + val_len |= RT5665_I2S_DL_24; + val_bits = 0x2500; + break; + case 8: + val_len |= RT5665_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5665_AIF1_1: + case RT5665_AIF1_2: + if (params_channels(params) > 2) + rt5665_set_tdm_slot(dai, 0xf, 0xf, + params_channels(params), params_width(params)); + reg_clk = RT5665_ADDA_CLK_1; + mask_clk = RT5665_I2S_PD1_MASK; + val_clk = pre_div << RT5665_I2S_PD1_SFT; + snd_soc_component_update_bits(component, RT5665_I2S1_SDP, + RT5665_I2S_DL_MASK, val_len); + break; + case RT5665_AIF2_1: + case RT5665_AIF2_2: + reg_clk = RT5665_ADDA_CLK_2; + mask_clk = RT5665_I2S_PD2_MASK; + val_clk = pre_div << RT5665_I2S_PD2_SFT; + snd_soc_component_update_bits(component, RT5665_I2S2_SDP, + RT5665_I2S_DL_MASK, val_len); + break; + case RT5665_AIF3: + reg_clk = RT5665_ADDA_CLK_2; + mask_clk = RT5665_I2S_PD3_MASK; + val_clk = pre_div << RT5665_I2S_PD3_SFT; + snd_soc_component_update_bits(component, RT5665_I2S3_SDP, + RT5665_I2S_DL_MASK, val_len); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, reg_clk, mask_clk, val_clk); + snd_soc_component_update_bits(component, RT5665_STO1_DAC_SIL_DET, 0x3700, val_bits); + + switch (rt5665->lrck[dai->id]) { + case 192000: + snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1, + RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK, + RT5665_DAC_OSR_32 | RT5665_ADC_OSR_32); + break; + case 96000: + snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1, + RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK, + RT5665_DAC_OSR_64 | RT5665_ADC_OSR_64); + break; + default: + snd_soc_component_update_bits(component, RT5665_ADDA_CLK_1, + RT5665_DAC_OSR_MASK | RT5665_ADC_OSR_MASK, + RT5665_DAC_OSR_128 | RT5665_ADC_OSR_128); + break; + } + + if (rt5665->master[RT5665_AIF2_1] || rt5665->master[RT5665_AIF2_2]) { + snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1, + RT5665_I2S2_M_PD_MASK, pre_div << RT5665_I2S2_M_PD_SFT); + } + if (rt5665->master[RT5665_AIF3]) { + snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1, + RT5665_I2S3_M_PD_MASK, pre_div << RT5665_I2S3_M_PD_SFT); + } + + return 0; +} + +static int rt5665_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5665->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5665_I2S_MS_S; + rt5665->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5665_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5665_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5665_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5665_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5665_AIF1_1: + case RT5665_AIF1_2: + snd_soc_component_update_bits(component, RT5665_I2S1_SDP, + RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK | + RT5665_I2S_DF_MASK, reg_val); + break; + case RT5665_AIF2_1: + case RT5665_AIF2_2: + snd_soc_component_update_bits(component, RT5665_I2S2_SDP, + RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK | + RT5665_I2S_DF_MASK, reg_val); + break; + case RT5665_AIF3: + snd_soc_component_update_bits(component, RT5665_I2S3_SDP, + RT5665_I2S_MS_MASK | RT5665_I2S_BP_MASK | + RT5665_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5665_set_component_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, src = 0; + + if (freq == rt5665->sysclk && clk_id == rt5665->sysclk_src) + return 0; + + switch (clk_id) { + case RT5665_SCLK_S_MCLK: + reg_val |= RT5665_SCLK_SRC_MCLK; + src = RT5665_CLK_SRC_MCLK; + break; + case RT5665_SCLK_S_PLL1: + reg_val |= RT5665_SCLK_SRC_PLL1; + src = RT5665_CLK_SRC_PLL1; + break; + case RT5665_SCLK_S_RCCLK: + reg_val |= RT5665_SCLK_SRC_RCCLK; + src = RT5665_CLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5665_GLB_CLK, + RT5665_SCLK_SRC_MASK, reg_val); + + if (rt5665->master[RT5665_AIF2_1] || rt5665->master[RT5665_AIF2_2]) { + snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1, + RT5665_I2S2_SRC_MASK, src << RT5665_I2S2_SRC_SFT); + } + if (rt5665->master[RT5665_AIF3]) { + snd_soc_component_update_bits(component, RT5665_I2S_M_CLK_CTRL_1, + RT5665_I2S3_SRC_MASK, src << RT5665_I2S3_SRC_SFT); + } + + rt5665->sysclk = freq; + rt5665->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +static int rt5665_set_component_pll(struct snd_soc_component *component, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5665->pll_src && freq_in == rt5665->pll_in && + freq_out == rt5665->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5665->pll_in = 0; + rt5665->pll_out = 0; + snd_soc_component_update_bits(component, RT5665_GLB_CLK, + RT5665_SCLK_SRC_MASK, RT5665_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5665_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5665_GLB_CLK, + RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_MCLK); + break; + case RT5665_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, RT5665_GLB_CLK, + RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK1); + break; + case RT5665_PLL1_S_BCLK2: + snd_soc_component_update_bits(component, RT5665_GLB_CLK, + RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK2); + break; + case RT5665_PLL1_S_BCLK3: + snd_soc_component_update_bits(component, RT5665_GLB_CLK, + RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK3); + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5665_PLL_CTRL_1, + pll_code.n_code << RT5665_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5665_PLL_CTRL_2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5665_PLL_M_SFT) | + (pll_code.m_bp << RT5665_PLL_M_BP_SFT)); + + rt5665->pll_in = freq_in; + rt5665->pll_out = freq_out; + rt5665->pll_src = source; + + return 0; +} + +static int rt5665_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio); + + rt5665->bclk[dai->id] = ratio; + + if (ratio == 64) { + switch (dai->id) { + case RT5665_AIF2_1: + case RT5665_AIF2_2: + snd_soc_component_update_bits(component, RT5665_ADDA_CLK_2, + RT5665_I2S_BCLK_MS2_MASK, + RT5665_I2S_BCLK_MS2_64); + break; + case RT5665_AIF3: + snd_soc_component_update_bits(component, RT5665_ADDA_CLK_2, + RT5665_I2S_BCLK_MS3_MASK, + RT5665_I2S_BCLK_MS3_64); + break; + } + } + + return 0; +} + +static int rt5665_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(rt5665->regmap, RT5665_DIG_MISC, + RT5665_DIG_GATE_CTRL, RT5665_DIG_GATE_CTRL); + break; + + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(rt5665->regmap, RT5665_PWR_DIG_1, + RT5665_PWR_LDO, RT5665_PWR_LDO); + regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1, + RT5665_PWR_MB, RT5665_PWR_MB); + regmap_update_bits(rt5665->regmap, RT5665_DIG_MISC, + RT5665_DIG_GATE_CTRL, 0); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(rt5665->regmap, RT5665_PWR_DIG_1, + RT5665_PWR_LDO, 0); + regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1, + RT5665_PWR_MB, 0); + break; + + default: + break; + } + + return 0; +} + +static int rt5665_probe(struct snd_soc_component *component) +{ + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + + rt5665->component = component; + + schedule_delayed_work(&rt5665->calibrate_work, msecs_to_jiffies(100)); + + return 0; +} + +static void rt5665_remove(struct snd_soc_component *component) +{ + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + + regmap_write(rt5665->regmap, RT5665_RESET, 0); + + regulator_bulk_disable(ARRAY_SIZE(rt5665->supplies), rt5665->supplies); +} + +#ifdef CONFIG_PM +static int rt5665_suspend(struct snd_soc_component *component) +{ + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5665->regmap, true); + regcache_mark_dirty(rt5665->regmap); + return 0; +} + +static int rt5665_resume(struct snd_soc_component *component) +{ + struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5665->regmap, false); + regcache_sync(rt5665->regmap); + + return 0; +} +#else +#define rt5665_suspend NULL +#define rt5665_resume NULL +#endif + +#define RT5665_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5665_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5665_aif_dai_ops = { + .hw_params = rt5665_hw_params, + .set_fmt = rt5665_set_dai_fmt, + .set_tdm_slot = rt5665_set_tdm_slot, + .set_bclk_ratio = rt5665_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5665_dai[] = { + { + .name = "rt5665-aif1_1", + .id = RT5665_AIF1_1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .capture = { + .stream_name = "AIF1_1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .ops = &rt5665_aif_dai_ops, + }, + { + .name = "rt5665-aif1_2", + .id = RT5665_AIF1_2, + .capture = { + .stream_name = "AIF1_2 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .ops = &rt5665_aif_dai_ops, + }, + { + .name = "rt5665-aif2_1", + .id = RT5665_AIF2_1, + .playback = { + .stream_name = "AIF2_1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .capture = { + .stream_name = "AIF2_1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .ops = &rt5665_aif_dai_ops, + }, + { + .name = "rt5665-aif2_2", + .id = RT5665_AIF2_2, + .playback = { + .stream_name = "AIF2_2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .capture = { + .stream_name = "AIF2_2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .ops = &rt5665_aif_dai_ops, + }, + { + .name = "rt5665-aif3", + .id = RT5665_AIF3, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5665_STEREO_RATES, + .formats = RT5665_FORMATS, + }, + .ops = &rt5665_aif_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5665 = { + .probe = rt5665_probe, + .remove = rt5665_remove, + .suspend = rt5665_suspend, + .resume = rt5665_resume, + .set_bias_level = rt5665_set_bias_level, + .controls = rt5665_snd_controls, + .num_controls = ARRAY_SIZE(rt5665_snd_controls), + .dapm_widgets = rt5665_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5665_dapm_widgets), + .dapm_routes = rt5665_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5665_dapm_routes), + .set_sysclk = rt5665_set_component_sysclk, + .set_pll = rt5665_set_component_pll, + .set_jack = rt5665_set_jack_detect, + .use_pmdown_time = 1, + .endianness = 1, +}; + + +static const struct regmap_config rt5665_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = 0x0400, + .volatile_reg = rt5665_volatile_register, + .readable_reg = rt5665_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5665_reg, + .num_reg_defaults = ARRAY_SIZE(rt5665_reg), + .use_single_read = true, + .use_single_write = true, +}; + +static const struct i2c_device_id rt5665_i2c_id[] = { + {"rt5665", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5665_i2c_id); + +static int rt5665_parse_dt(struct rt5665_priv *rt5665, struct device *dev) +{ + rt5665->pdata.in1_diff = of_property_read_bool(dev->of_node, + "realtek,in1-differential"); + rt5665->pdata.in2_diff = of_property_read_bool(dev->of_node, + "realtek,in2-differential"); + rt5665->pdata.in3_diff = of_property_read_bool(dev->of_node, + "realtek,in3-differential"); + rt5665->pdata.in4_diff = of_property_read_bool(dev->of_node, + "realtek,in4-differential"); + + of_property_read_u32(dev->of_node, "realtek,dmic1-data-pin", + &rt5665->pdata.dmic1_data_pin); + of_property_read_u32(dev->of_node, "realtek,dmic2-data-pin", + &rt5665->pdata.dmic2_data_pin); + of_property_read_u32(dev->of_node, "realtek,jd-src", + &rt5665->pdata.jd_src); + + return 0; +} + +static void rt5665_calibrate(struct rt5665_priv *rt5665) +{ + int value, count; + + mutex_lock(&rt5665->calibrate_mutex); + + regcache_cache_bypass(rt5665->regmap, true); + + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602); + regmap_write(rt5665->regmap, RT5665_HP_CHARGE_PUMP_1, 0x0c26); + regmap_write(rt5665->regmap, RT5665_MONOMIX_IN_GAIN, 0x021f); + regmap_write(rt5665->regmap, RT5665_MONO_OUT, 0x480a); + regmap_write(rt5665->regmap, RT5665_PWR_MIXER, 0x083f); + regmap_write(rt5665->regmap, RT5665_PWR_DIG_1, 0x0180); + regmap_write(rt5665->regmap, RT5665_EJD_CTRL_1, 0x4040); + regmap_write(rt5665->regmap, RT5665_HP_LOGIC_CTRL_2, 0x0000); + regmap_write(rt5665->regmap, RT5665_DIG_MISC, 0x0001); + regmap_write(rt5665->regmap, RT5665_MICBIAS_2, 0x0380); + regmap_write(rt5665->regmap, RT5665_GLB_CLK, 0x8000); + regmap_write(rt5665->regmap, RT5665_ADDA_CLK_1, 0x1000); + regmap_write(rt5665->regmap, RT5665_CHOP_DAC, 0x3030); + regmap_write(rt5665->regmap, RT5665_CALIB_ADC_CTRL, 0x3c05); + regmap_write(rt5665->regmap, RT5665_PWR_ANLG_1, 0xaa3e); + usleep_range(15000, 20000); + regmap_write(rt5665->regmap, RT5665_PWR_ANLG_1, 0xfe7e); + regmap_write(rt5665->regmap, RT5665_HP_CALIB_CTRL_2, 0x0321); + + regmap_write(rt5665->regmap, RT5665_HP_CALIB_CTRL_1, 0xfc00); + count = 0; + while (true) { + regmap_read(rt5665->regmap, RT5665_HP_CALIB_STA_1, &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 60) { + pr_err("HP Calibration Failure\n"); + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regcache_cache_bypass(rt5665->regmap, false); + goto out_unlock; + } + + count++; + } + + regmap_write(rt5665->regmap, RT5665_MONO_AMP_CALIB_CTRL_1, 0x9e24); + count = 0; + while (true) { + regmap_read(rt5665->regmap, RT5665_MONO_AMP_CALIB_STA1, &value); + if (value & 0x8000) + usleep_range(10000, 10005); + else + break; + + if (count > 60) { + pr_err("MONO Calibration Failure\n"); + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regcache_cache_bypass(rt5665->regmap, false); + goto out_unlock; + } + + count++; + } + + regmap_write(rt5665->regmap, RT5665_RESET, 0); + regcache_cache_bypass(rt5665->regmap, false); + + regcache_mark_dirty(rt5665->regmap); + regcache_sync(rt5665->regmap); + + regmap_write(rt5665->regmap, RT5665_BIAS_CUR_CTRL_8, 0xa602); + regmap_write(rt5665->regmap, RT5665_ASRC_8, 0x0120); + +out_unlock: + rt5665->calibration_done = true; + mutex_unlock(&rt5665->calibrate_mutex); +} + +static void rt5665_calibrate_handler(struct work_struct *work) +{ + struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv, + calibrate_work.work); + + while (!snd_soc_card_is_instantiated(rt5665->component->card)) { + pr_debug("%s\n", __func__); + usleep_range(10000, 15000); + } + + rt5665_calibrate(rt5665); +} + +static int rt5665_i2c_probe(struct i2c_client *i2c) +{ + struct rt5665_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5665_priv *rt5665; + int i, ret; + unsigned int val; + + rt5665 = devm_kzalloc(&i2c->dev, sizeof(struct rt5665_priv), + GFP_KERNEL); + + if (rt5665 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5665); + + if (pdata) + rt5665->pdata = *pdata; + else + rt5665_parse_dt(rt5665, &i2c->dev); + + for (i = 0; i < ARRAY_SIZE(rt5665->supplies); i++) + rt5665->supplies[i].supply = rt5665_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5665->supplies), + rt5665->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5665->supplies), + rt5665->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + + rt5665->gpiod_ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5665->gpiod_ldo1_en)) { + dev_err(&i2c->dev, "Failed gpio request ldo1_en\n"); + return PTR_ERR(rt5665->gpiod_ldo1_en); + } + + /* Sleep for 300 ms miniumum */ + usleep_range(300000, 350000); + + rt5665->regmap = devm_regmap_init_i2c(i2c, &rt5665_regmap); + if (IS_ERR(rt5665->regmap)) { + ret = PTR_ERR(rt5665->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5665->regmap, RT5665_DEVICE_ID, &val); + if (val != DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5665\n", val); + return -ENODEV; + } + + regmap_read(rt5665->regmap, RT5665_RESET, &val); + switch (val) { + case 0x0: + rt5665->id = CODEC_5666; + break; + case 0x3: + default: + rt5665->id = CODEC_5665; + break; + } + + regmap_write(rt5665->regmap, RT5665_RESET, 0); + + /* line in diff mode*/ + if (rt5665->pdata.in1_diff) + regmap_update_bits(rt5665->regmap, RT5665_IN1_IN2, + RT5665_IN1_DF_MASK, RT5665_IN1_DF_MASK); + if (rt5665->pdata.in2_diff) + regmap_update_bits(rt5665->regmap, RT5665_IN1_IN2, + RT5665_IN2_DF_MASK, RT5665_IN2_DF_MASK); + if (rt5665->pdata.in3_diff) + regmap_update_bits(rt5665->regmap, RT5665_IN3_IN4, + RT5665_IN3_DF_MASK, RT5665_IN3_DF_MASK); + if (rt5665->pdata.in4_diff) + regmap_update_bits(rt5665->regmap, RT5665_IN3_IN4, + RT5665_IN4_DF_MASK, RT5665_IN4_DF_MASK); + + /* DMIC pin*/ + if (rt5665->pdata.dmic1_data_pin != RT5665_DMIC1_NULL || + rt5665->pdata.dmic2_data_pin != RT5665_DMIC2_NULL) { + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_2, + RT5665_GP9_PIN_MASK, RT5665_GP9_PIN_DMIC1_SCL); + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1, + RT5665_GP8_PIN_MASK, RT5665_GP8_PIN_DMIC2_SCL); + switch (rt5665->pdata.dmic1_data_pin) { + case RT5665_DMIC1_DATA_IN2N: + regmap_update_bits(rt5665->regmap, RT5665_DMIC_CTRL_1, + RT5665_DMIC_1_DP_MASK, RT5665_DMIC_1_DP_IN2N); + break; + + case RT5665_DMIC1_DATA_GPIO4: + regmap_update_bits(rt5665->regmap, RT5665_DMIC_CTRL_1, + RT5665_DMIC_1_DP_MASK, RT5665_DMIC_1_DP_GPIO4); + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1, + RT5665_GP4_PIN_MASK, RT5665_GP4_PIN_DMIC1_SDA); + break; + + default: + dev_dbg(&i2c->dev, "no DMIC1\n"); + break; + } + + switch (rt5665->pdata.dmic2_data_pin) { + case RT5665_DMIC2_DATA_IN2P: + regmap_update_bits(rt5665->regmap, RT5665_DMIC_CTRL_1, + RT5665_DMIC_2_DP_MASK, RT5665_DMIC_2_DP_IN2P); + break; + + case RT5665_DMIC2_DATA_GPIO5: + regmap_update_bits(rt5665->regmap, + RT5665_DMIC_CTRL_1, + RT5665_DMIC_2_DP_MASK, + RT5665_DMIC_2_DP_GPIO5); + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_1, + RT5665_GP5_PIN_MASK, RT5665_GP5_PIN_DMIC2_SDA); + break; + + default: + dev_dbg(&i2c->dev, "no DMIC2\n"); + break; + + } + } + + regmap_write(rt5665->regmap, RT5665_HP_LOGIC_CTRL_2, 0x0002); + regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1, + 0xf000 | RT5665_VREF_POW_MASK, 0xe000 | RT5665_VREF_POW_REG); + /* Work around for pow_pump */ + regmap_update_bits(rt5665->regmap, RT5665_STO1_DAC_SIL_DET, + RT5665_DEB_STO_DAC_MASK, RT5665_DEB_80_MS); + + regmap_update_bits(rt5665->regmap, RT5665_HP_CHARGE_PUMP_1, + RT5665_PM_HP_MASK, RT5665_PM_HP_HV); + + /* Set GPIO4,8 as input for combo jack */ + if (rt5665->id == CODEC_5666) { + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_2, + RT5665_GP4_PF_MASK, RT5665_GP4_PF_IN); + regmap_update_bits(rt5665->regmap, RT5665_GPIO_CTRL_3, + RT5665_GP8_PF_MASK, RT5665_GP8_PF_IN); + } + + /* Enhance performance*/ + regmap_update_bits(rt5665->regmap, RT5665_PWR_ANLG_1, + RT5665_HP_DRIVER_MASK | RT5665_LDO1_DVO_MASK, + RT5665_HP_DRIVER_5X | RT5665_LDO1_DVO_12); + + INIT_DELAYED_WORK(&rt5665->jack_detect_work, + rt5665_jack_detect_handler); + INIT_DELAYED_WORK(&rt5665->calibrate_work, + rt5665_calibrate_handler); + INIT_DELAYED_WORK(&rt5665->jd_check_work, + rt5665_jd_check_handler); + + mutex_init(&rt5665->calibrate_mutex); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5665_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5665", rt5665); + if (ret) + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + + } + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5665, + rt5665_dai, ARRAY_SIZE(rt5665_dai)); +} + +static void rt5665_i2c_shutdown(struct i2c_client *client) +{ + struct rt5665_priv *rt5665 = i2c_get_clientdata(client); + + regmap_write(rt5665->regmap, RT5665_RESET, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt5665_of_match[] = { + {.compatible = "realtek,rt5665"}, + {.compatible = "realtek,rt5666"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5665_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5665_acpi_match[] = { + {"10EC5665", 0,}, + {"10EC5666", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5665_acpi_match); +#endif + +static struct i2c_driver rt5665_i2c_driver = { + .driver = { + .name = "rt5665", + .of_match_table = of_match_ptr(rt5665_of_match), + .acpi_match_table = ACPI_PTR(rt5665_acpi_match), + }, + .probe = rt5665_i2c_probe, + .shutdown = rt5665_i2c_shutdown, + .id_table = rt5665_i2c_id, +}; +module_i2c_driver(rt5665_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5665 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h new file mode 100644 index 0000000000..12ab28e5f1 --- /dev/null +++ b/sound/soc/codecs/rt5665.h @@ -0,0 +1,2005 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5665.h -- RT5665/RT5658 ALSA SoC audio driver + * + * Copyright 2016 Realtek Microelectronics + * Author: Bard Liao + */ + +#ifndef __RT5665_H__ +#define __RT5665_H__ + +#include + +#define DEVICE_ID 0x6451 + +/* Info */ +#define RT5665_RESET 0x0000 +#define RT5665_VENDOR_ID 0x00fd +#define RT5665_VENDOR_ID_1 0x00fe +#define RT5665_DEVICE_ID 0x00ff +/* I/O - Output */ +#define RT5665_LOUT 0x0001 +#define RT5665_HP_CTRL_1 0x0002 +#define RT5665_HP_CTRL_2 0x0003 +#define RT5665_MONO_OUT 0x0004 +#define RT5665_HPL_GAIN 0x0005 +#define RT5665_HPR_GAIN 0x0006 +#define RT5665_MONO_GAIN 0x0007 + +/* I/O - Input */ +#define RT5665_CAL_BST_CTRL 0x000a +#define RT5665_CBJ_BST_CTRL 0x000b +#define RT5665_IN1_IN2 0x000c +#define RT5665_IN3_IN4 0x000d +#define RT5665_INL1_INR1_VOL 0x000f +/* I/O - Speaker */ +#define RT5665_EJD_CTRL_1 0x0010 +#define RT5665_EJD_CTRL_2 0x0011 +#define RT5665_EJD_CTRL_3 0x0012 +#define RT5665_EJD_CTRL_4 0x0013 +#define RT5665_EJD_CTRL_5 0x0014 +#define RT5665_EJD_CTRL_6 0x0015 +#define RT5665_EJD_CTRL_7 0x0016 +/* I/O - ADC/DAC/DMIC */ +#define RT5665_DAC2_CTRL 0x0017 +#define RT5665_DAC2_DIG_VOL 0x0018 +#define RT5665_DAC1_DIG_VOL 0x0019 +#define RT5665_DAC3_DIG_VOL 0x001a +#define RT5665_DAC3_CTRL 0x001b +#define RT5665_STO1_ADC_DIG_VOL 0x001c +#define RT5665_MONO_ADC_DIG_VOL 0x001d +#define RT5665_STO2_ADC_DIG_VOL 0x001e +#define RT5665_STO1_ADC_BOOST 0x001f +#define RT5665_MONO_ADC_BOOST 0x0020 +#define RT5665_STO2_ADC_BOOST 0x0021 +#define RT5665_HP_IMP_GAIN_1 0x0022 +#define RT5665_HP_IMP_GAIN_2 0x0023 +/* Mixer - D-D */ +#define RT5665_STO1_ADC_MIXER 0x0026 +#define RT5665_MONO_ADC_MIXER 0x0027 +#define RT5665_STO2_ADC_MIXER 0x0028 +#define RT5665_AD_DA_MIXER 0x0029 +#define RT5665_STO1_DAC_MIXER 0x002a +#define RT5665_MONO_DAC_MIXER 0x002b +#define RT5665_STO2_DAC_MIXER 0x002c +#define RT5665_A_DAC1_MUX 0x002d +#define RT5665_A_DAC2_MUX 0x002e +#define RT5665_DIG_INF2_DATA 0x002f +#define RT5665_DIG_INF3_DATA 0x0030 +/* Mixer - PDM */ +#define RT5665_PDM_OUT_CTRL 0x0031 +#define RT5665_PDM_DATA_CTRL_1 0x0032 +#define RT5665_PDM_DATA_CTRL_2 0x0033 +#define RT5665_PDM_DATA_CTRL_3 0x0034 +#define RT5665_PDM_DATA_CTRL_4 0x0035 +/* Mixer - ADC */ +#define RT5665_REC1_GAIN 0x003a +#define RT5665_REC1_L1_MIXER 0x003b +#define RT5665_REC1_L2_MIXER 0x003c +#define RT5665_REC1_R1_MIXER 0x003d +#define RT5665_REC1_R2_MIXER 0x003e +#define RT5665_REC2_GAIN 0x003f +#define RT5665_REC2_L1_MIXER 0x0040 +#define RT5665_REC2_L2_MIXER 0x0041 +#define RT5665_REC2_R1_MIXER 0x0042 +#define RT5665_REC2_R2_MIXER 0x0043 +#define RT5665_CAL_REC 0x0044 +/* Mixer - DAC */ +#define RT5665_ALC_BACK_GAIN 0x0049 +#define RT5665_MONOMIX_GAIN 0x004a +#define RT5665_MONOMIX_IN_GAIN 0x004b +#define RT5665_OUT_L_GAIN 0x004d +#define RT5665_OUT_L_MIXER 0x004e +#define RT5665_OUT_R_GAIN 0x004f +#define RT5665_OUT_R_MIXER 0x0050 +#define RT5665_LOUT_MIXER 0x0052 +/* Power */ +#define RT5665_PWR_DIG_1 0x0061 +#define RT5665_PWR_DIG_2 0x0062 +#define RT5665_PWR_ANLG_1 0x0063 +#define RT5665_PWR_ANLG_2 0x0064 +#define RT5665_PWR_ANLG_3 0x0065 +#define RT5665_PWR_MIXER 0x0066 +#define RT5665_PWR_VOL 0x0067 +/* Clock Detect */ +#define RT5665_CLK_DET 0x006b +/* Filter */ +#define RT5665_HPF_CTRL1 0x006d +/* DMIC */ +#define RT5665_DMIC_CTRL_1 0x006e +#define RT5665_DMIC_CTRL_2 0x006f +/* Format - ADC/DAC */ +#define RT5665_I2S1_SDP 0x0070 +#define RT5665_I2S2_SDP 0x0071 +#define RT5665_I2S3_SDP 0x0072 +#define RT5665_ADDA_CLK_1 0x0073 +#define RT5665_ADDA_CLK_2 0x0074 +#define RT5665_I2S1_F_DIV_CTRL_1 0x0075 +#define RT5665_I2S1_F_DIV_CTRL_2 0x0076 +/* Format - TDM Control */ +#define RT5665_TDM_CTRL_1 0x0078 +#define RT5665_TDM_CTRL_2 0x0079 +#define RT5665_TDM_CTRL_3 0x007a +#define RT5665_TDM_CTRL_4 0x007b +#define RT5665_TDM_CTRL_5 0x007c +#define RT5665_TDM_CTRL_6 0x007d +#define RT5665_TDM_CTRL_7 0x007e +#define RT5665_TDM_CTRL_8 0x007f +/* Function - Analog */ +#define RT5665_GLB_CLK 0x0080 +#define RT5665_PLL_CTRL_1 0x0081 +#define RT5665_PLL_CTRL_2 0x0082 +#define RT5665_ASRC_1 0x0083 +#define RT5665_ASRC_2 0x0084 +#define RT5665_ASRC_3 0x0085 +#define RT5665_ASRC_4 0x0086 +#define RT5665_ASRC_5 0x0087 +#define RT5665_ASRC_6 0x0088 +#define RT5665_ASRC_7 0x0089 +#define RT5665_ASRC_8 0x008a +#define RT5665_ASRC_9 0x008b +#define RT5665_ASRC_10 0x008c +#define RT5665_DEPOP_1 0x008e +#define RT5665_DEPOP_2 0x008f +#define RT5665_HP_CHARGE_PUMP_1 0x0091 +#define RT5665_HP_CHARGE_PUMP_2 0x0092 +#define RT5665_MICBIAS_1 0x0093 +#define RT5665_MICBIAS_2 0x0094 +#define RT5665_ASRC_12 0x0098 +#define RT5665_ASRC_13 0x0099 +#define RT5665_ASRC_14 0x009a +#define RT5665_RC_CLK_CTRL 0x009f +#define RT5665_I2S_M_CLK_CTRL_1 0x00a0 +#define RT5665_I2S2_F_DIV_CTRL_1 0x00a1 +#define RT5665_I2S2_F_DIV_CTRL_2 0x00a2 +#define RT5665_I2S3_F_DIV_CTRL_1 0x00a3 +#define RT5665_I2S3_F_DIV_CTRL_2 0x00a4 +/* Function - Digital */ +#define RT5665_EQ_CTRL_1 0x00ae +#define RT5665_EQ_CTRL_2 0x00af +#define RT5665_IRQ_CTRL_1 0x00b6 +#define RT5665_IRQ_CTRL_2 0x00b7 +#define RT5665_IRQ_CTRL_3 0x00b8 +#define RT5665_IRQ_CTRL_4 0x00b9 +#define RT5665_IRQ_CTRL_5 0x00ba +#define RT5665_IRQ_CTRL_6 0x00bb +#define RT5665_INT_ST_1 0x00be +#define RT5665_GPIO_CTRL_1 0x00c0 +#define RT5665_GPIO_CTRL_2 0x00c1 +#define RT5665_GPIO_CTRL_3 0x00c2 +#define RT5665_GPIO_CTRL_4 0x00c3 +#define RT5665_GPIO_STA 0x00c4 +#define RT5665_HP_AMP_DET_CTRL_1 0x00d0 +#define RT5665_HP_AMP_DET_CTRL_2 0x00d1 +#define RT5665_MID_HP_AMP_DET 0x00d3 +#define RT5665_LOW_HP_AMP_DET 0x00d4 +#define RT5665_SV_ZCD_1 0x00d9 +#define RT5665_SV_ZCD_2 0x00da +#define RT5665_IL_CMD_1 0x00db +#define RT5665_IL_CMD_2 0x00dc +#define RT5665_IL_CMD_3 0x00dd +#define RT5665_IL_CMD_4 0x00de +#define RT5665_4BTN_IL_CMD_1 0x00df +#define RT5665_4BTN_IL_CMD_2 0x00e0 +#define RT5665_4BTN_IL_CMD_3 0x00e1 +#define RT5665_PSV_IL_CMD_1 0x00e2 + +#define RT5665_ADC_STO1_HP_CTRL_1 0x00ea +#define RT5665_ADC_STO1_HP_CTRL_2 0x00eb +#define RT5665_ADC_MONO_HP_CTRL_1 0x00ec +#define RT5665_ADC_MONO_HP_CTRL_2 0x00ed +#define RT5665_ADC_STO2_HP_CTRL_1 0x00ee +#define RT5665_ADC_STO2_HP_CTRL_2 0x00ef +#define RT5665_AJD1_CTRL 0x00f0 +#define RT5665_JD1_THD 0x00f1 +#define RT5665_JD2_THD 0x00f2 +#define RT5665_JD_CTRL_1 0x00f6 +#define RT5665_JD_CTRL_2 0x00f7 +#define RT5665_JD_CTRL_3 0x00f8 +/* General Control */ +#define RT5665_DIG_MISC 0x00fa +#define RT5665_DUMMY_2 0x00fb +#define RT5665_DUMMY_3 0x00fc + +#define RT5665_DAC_ADC_DIG_VOL1 0x0100 +#define RT5665_DAC_ADC_DIG_VOL2 0x0101 +#define RT5665_BIAS_CUR_CTRL_1 0x010a +#define RT5665_BIAS_CUR_CTRL_2 0x010b +#define RT5665_BIAS_CUR_CTRL_3 0x010c +#define RT5665_BIAS_CUR_CTRL_4 0x010d +#define RT5665_BIAS_CUR_CTRL_5 0x010e +#define RT5665_BIAS_CUR_CTRL_6 0x010f +#define RT5665_BIAS_CUR_CTRL_7 0x0110 +#define RT5665_BIAS_CUR_CTRL_8 0x0111 +#define RT5665_BIAS_CUR_CTRL_9 0x0112 +#define RT5665_BIAS_CUR_CTRL_10 0x0113 +#define RT5665_VREF_REC_OP_FB_CAP_CTRL 0x0117 +#define RT5665_CHARGE_PUMP_1 0x0125 +#define RT5665_DIG_IN_CTRL_1 0x0132 +#define RT5665_DIG_IN_CTRL_2 0x0133 +#define RT5665_PAD_DRIVING_CTRL 0x0137 +#define RT5665_SOFT_RAMP_DEPOP 0x0138 +#define RT5665_PLL 0x0139 +#define RT5665_CHOP_DAC 0x013a +#define RT5665_CHOP_ADC 0x013b +#define RT5665_CALIB_ADC_CTRL 0x013c +#define RT5665_VOL_TEST 0x013f +#define RT5665_TEST_MODE_CTRL_1 0x0145 +#define RT5665_TEST_MODE_CTRL_2 0x0146 +#define RT5665_TEST_MODE_CTRL_3 0x0147 +#define RT5665_TEST_MODE_CTRL_4 0x0148 +#define RT5665_BASSBACK_CTRL 0x0150 +#define RT5665_STO_NG2_CTRL_1 0x0160 +#define RT5665_STO_NG2_CTRL_2 0x0161 +#define RT5665_STO_NG2_CTRL_3 0x0162 +#define RT5665_STO_NG2_CTRL_4 0x0163 +#define RT5665_STO_NG2_CTRL_5 0x0164 +#define RT5665_STO_NG2_CTRL_6 0x0165 +#define RT5665_STO_NG2_CTRL_7 0x0166 +#define RT5665_STO_NG2_CTRL_8 0x0167 +#define RT5665_MONO_NG2_CTRL_1 0x0170 +#define RT5665_MONO_NG2_CTRL_2 0x0171 +#define RT5665_MONO_NG2_CTRL_3 0x0172 +#define RT5665_MONO_NG2_CTRL_4 0x0173 +#define RT5665_MONO_NG2_CTRL_5 0x0174 +#define RT5665_MONO_NG2_CTRL_6 0x0175 +#define RT5665_STO1_DAC_SIL_DET 0x0190 +#define RT5665_MONOL_DAC_SIL_DET 0x0191 +#define RT5665_MONOR_DAC_SIL_DET 0x0192 +#define RT5665_STO2_DAC_SIL_DET 0x0193 +#define RT5665_SIL_PSV_CTRL1 0x0194 +#define RT5665_SIL_PSV_CTRL2 0x0195 +#define RT5665_SIL_PSV_CTRL3 0x0196 +#define RT5665_SIL_PSV_CTRL4 0x0197 +#define RT5665_SIL_PSV_CTRL5 0x0198 +#define RT5665_SIL_PSV_CTRL6 0x0199 +#define RT5665_MONO_AMP_CALIB_CTRL_1 0x01a0 +#define RT5665_MONO_AMP_CALIB_CTRL_2 0x01a1 +#define RT5665_MONO_AMP_CALIB_CTRL_3 0x01a2 +#define RT5665_MONO_AMP_CALIB_CTRL_4 0x01a3 +#define RT5665_MONO_AMP_CALIB_CTRL_5 0x01a4 +#define RT5665_MONO_AMP_CALIB_CTRL_6 0x01a5 +#define RT5665_MONO_AMP_CALIB_CTRL_7 0x01a6 +#define RT5665_MONO_AMP_CALIB_STA1 0x01a7 +#define RT5665_MONO_AMP_CALIB_STA2 0x01a8 +#define RT5665_MONO_AMP_CALIB_STA3 0x01a9 +#define RT5665_MONO_AMP_CALIB_STA4 0x01aa +#define RT5665_MONO_AMP_CALIB_STA6 0x01ab +#define RT5665_HP_IMP_SENS_CTRL_01 0x01b5 +#define RT5665_HP_IMP_SENS_CTRL_02 0x01b6 +#define RT5665_HP_IMP_SENS_CTRL_03 0x01b7 +#define RT5665_HP_IMP_SENS_CTRL_04 0x01b8 +#define RT5665_HP_IMP_SENS_CTRL_05 0x01b9 +#define RT5665_HP_IMP_SENS_CTRL_06 0x01ba +#define RT5665_HP_IMP_SENS_CTRL_07 0x01bb +#define RT5665_HP_IMP_SENS_CTRL_08 0x01bc +#define RT5665_HP_IMP_SENS_CTRL_09 0x01bd +#define RT5665_HP_IMP_SENS_CTRL_10 0x01be +#define RT5665_HP_IMP_SENS_CTRL_11 0x01bf +#define RT5665_HP_IMP_SENS_CTRL_12 0x01c0 +#define RT5665_HP_IMP_SENS_CTRL_13 0x01c1 +#define RT5665_HP_IMP_SENS_CTRL_14 0x01c2 +#define RT5665_HP_IMP_SENS_CTRL_15 0x01c3 +#define RT5665_HP_IMP_SENS_CTRL_16 0x01c4 +#define RT5665_HP_IMP_SENS_CTRL_17 0x01c5 +#define RT5665_HP_IMP_SENS_CTRL_18 0x01c6 +#define RT5665_HP_IMP_SENS_CTRL_19 0x01c7 +#define RT5665_HP_IMP_SENS_CTRL_20 0x01c8 +#define RT5665_HP_IMP_SENS_CTRL_21 0x01c9 +#define RT5665_HP_IMP_SENS_CTRL_22 0x01ca +#define RT5665_HP_IMP_SENS_CTRL_23 0x01cb +#define RT5665_HP_IMP_SENS_CTRL_24 0x01cc +#define RT5665_HP_IMP_SENS_CTRL_25 0x01cd +#define RT5665_HP_IMP_SENS_CTRL_26 0x01ce +#define RT5665_HP_IMP_SENS_CTRL_27 0x01cf +#define RT5665_HP_IMP_SENS_CTRL_28 0x01d0 +#define RT5665_HP_IMP_SENS_CTRL_29 0x01d1 +#define RT5665_HP_IMP_SENS_CTRL_30 0x01d2 +#define RT5665_HP_IMP_SENS_CTRL_31 0x01d3 +#define RT5665_HP_IMP_SENS_CTRL_32 0x01d4 +#define RT5665_HP_IMP_SENS_CTRL_33 0x01d5 +#define RT5665_HP_IMP_SENS_CTRL_34 0x01d6 +#define RT5665_HP_LOGIC_CTRL_1 0x01da +#define RT5665_HP_LOGIC_CTRL_2 0x01db +#define RT5665_HP_LOGIC_CTRL_3 0x01dc +#define RT5665_HP_CALIB_CTRL_1 0x01de +#define RT5665_HP_CALIB_CTRL_2 0x01df +#define RT5665_HP_CALIB_CTRL_3 0x01e0 +#define RT5665_HP_CALIB_CTRL_4 0x01e1 +#define RT5665_HP_CALIB_CTRL_5 0x01e2 +#define RT5665_HP_CALIB_CTRL_6 0x01e3 +#define RT5665_HP_CALIB_CTRL_7 0x01e4 +#define RT5665_HP_CALIB_CTRL_9 0x01e6 +#define RT5665_HP_CALIB_CTRL_10 0x01e7 +#define RT5665_HP_CALIB_CTRL_11 0x01e8 +#define RT5665_HP_CALIB_STA_1 0x01ea +#define RT5665_HP_CALIB_STA_2 0x01eb +#define RT5665_HP_CALIB_STA_3 0x01ec +#define RT5665_HP_CALIB_STA_4 0x01ed +#define RT5665_HP_CALIB_STA_5 0x01ee +#define RT5665_HP_CALIB_STA_6 0x01ef +#define RT5665_HP_CALIB_STA_7 0x01f0 +#define RT5665_HP_CALIB_STA_8 0x01f1 +#define RT5665_HP_CALIB_STA_9 0x01f2 +#define RT5665_HP_CALIB_STA_10 0x01f3 +#define RT5665_HP_CALIB_STA_11 0x01f4 +#define RT5665_PGM_TAB_CTRL1 0x0200 +#define RT5665_PGM_TAB_CTRL2 0x0201 +#define RT5665_PGM_TAB_CTRL3 0x0202 +#define RT5665_PGM_TAB_CTRL4 0x0203 +#define RT5665_PGM_TAB_CTRL5 0x0204 +#define RT5665_PGM_TAB_CTRL6 0x0205 +#define RT5665_PGM_TAB_CTRL7 0x0206 +#define RT5665_PGM_TAB_CTRL8 0x0207 +#define RT5665_PGM_TAB_CTRL9 0x0208 +#define RT5665_SAR_IL_CMD_1 0x0210 +#define RT5665_SAR_IL_CMD_2 0x0211 +#define RT5665_SAR_IL_CMD_3 0x0212 +#define RT5665_SAR_IL_CMD_4 0x0213 +#define RT5665_SAR_IL_CMD_5 0x0214 +#define RT5665_SAR_IL_CMD_6 0x0215 +#define RT5665_SAR_IL_CMD_7 0x0216 +#define RT5665_SAR_IL_CMD_8 0x0217 +#define RT5665_SAR_IL_CMD_9 0x0218 +#define RT5665_SAR_IL_CMD_10 0x0219 +#define RT5665_SAR_IL_CMD_11 0x021a +#define RT5665_SAR_IL_CMD_12 0x021b +#define RT5665_DRC1_CTRL_0 0x02ff +#define RT5665_DRC1_CTRL_1 0x0300 +#define RT5665_DRC1_CTRL_2 0x0301 +#define RT5665_DRC1_CTRL_3 0x0302 +#define RT5665_DRC1_CTRL_4 0x0303 +#define RT5665_DRC1_CTRL_5 0x0304 +#define RT5665_DRC1_CTRL_6 0x0305 +#define RT5665_DRC1_HARD_LMT_CTRL_1 0x0306 +#define RT5665_DRC1_HARD_LMT_CTRL_2 0x0307 +#define RT5665_DRC1_PRIV_1 0x0310 +#define RT5665_DRC1_PRIV_2 0x0311 +#define RT5665_DRC1_PRIV_3 0x0312 +#define RT5665_DRC1_PRIV_4 0x0313 +#define RT5665_DRC1_PRIV_5 0x0314 +#define RT5665_DRC1_PRIV_6 0x0315 +#define RT5665_DRC1_PRIV_7 0x0316 +#define RT5665_DRC1_PRIV_8 0x0317 +#define RT5665_ALC_PGA_CTRL_1 0x0330 +#define RT5665_ALC_PGA_CTRL_2 0x0331 +#define RT5665_ALC_PGA_CTRL_3 0x0332 +#define RT5665_ALC_PGA_CTRL_4 0x0333 +#define RT5665_ALC_PGA_CTRL_5 0x0334 +#define RT5665_ALC_PGA_CTRL_6 0x0335 +#define RT5665_ALC_PGA_CTRL_7 0x0336 +#define RT5665_ALC_PGA_CTRL_8 0x0337 +#define RT5665_ALC_PGA_STA_1 0x0338 +#define RT5665_ALC_PGA_STA_2 0x0339 +#define RT5665_ALC_PGA_STA_3 0x033a +#define RT5665_EQ_AUTO_RCV_CTRL1 0x03c0 +#define RT5665_EQ_AUTO_RCV_CTRL2 0x03c1 +#define RT5665_EQ_AUTO_RCV_CTRL3 0x03c2 +#define RT5665_EQ_AUTO_RCV_CTRL4 0x03c3 +#define RT5665_EQ_AUTO_RCV_CTRL5 0x03c4 +#define RT5665_EQ_AUTO_RCV_CTRL6 0x03c5 +#define RT5665_EQ_AUTO_RCV_CTRL7 0x03c6 +#define RT5665_EQ_AUTO_RCV_CTRL8 0x03c7 +#define RT5665_EQ_AUTO_RCV_CTRL9 0x03c8 +#define RT5665_EQ_AUTO_RCV_CTRL10 0x03c9 +#define RT5665_EQ_AUTO_RCV_CTRL11 0x03ca +#define RT5665_EQ_AUTO_RCV_CTRL12 0x03cb +#define RT5665_EQ_AUTO_RCV_CTRL13 0x03cc +#define RT5665_ADC_L_EQ_LPF1_A1 0x03d0 +#define RT5665_R_EQ_LPF1_A1 0x03d1 +#define RT5665_L_EQ_LPF1_H0 0x03d2 +#define RT5665_R_EQ_LPF1_H0 0x03d3 +#define RT5665_L_EQ_BPF1_A1 0x03d4 +#define RT5665_R_EQ_BPF1_A1 0x03d5 +#define RT5665_L_EQ_BPF1_A2 0x03d6 +#define RT5665_R_EQ_BPF1_A2 0x03d7 +#define RT5665_L_EQ_BPF1_H0 0x03d8 +#define RT5665_R_EQ_BPF1_H0 0x03d9 +#define RT5665_L_EQ_BPF2_A1 0x03da +#define RT5665_R_EQ_BPF2_A1 0x03db +#define RT5665_L_EQ_BPF2_A2 0x03dc +#define RT5665_R_EQ_BPF2_A2 0x03dd +#define RT5665_L_EQ_BPF2_H0 0x03de +#define RT5665_R_EQ_BPF2_H0 0x03df +#define RT5665_L_EQ_BPF3_A1 0x03e0 +#define RT5665_R_EQ_BPF3_A1 0x03e1 +#define RT5665_L_EQ_BPF3_A2 0x03e2 +#define RT5665_R_EQ_BPF3_A2 0x03e3 +#define RT5665_L_EQ_BPF3_H0 0x03e4 +#define RT5665_R_EQ_BPF3_H0 0x03e5 +#define RT5665_L_EQ_BPF4_A1 0x03e6 +#define RT5665_R_EQ_BPF4_A1 0x03e7 +#define RT5665_L_EQ_BPF4_A2 0x03e8 +#define RT5665_R_EQ_BPF4_A2 0x03e9 +#define RT5665_L_EQ_BPF4_H0 0x03ea +#define RT5665_R_EQ_BPF4_H0 0x03eb +#define RT5665_L_EQ_HPF1_A1 0x03ec +#define RT5665_R_EQ_HPF1_A1 0x03ed +#define RT5665_L_EQ_HPF1_H0 0x03ee +#define RT5665_R_EQ_HPF1_H0 0x03ef +#define RT5665_L_EQ_PRE_VOL 0x03f0 +#define RT5665_R_EQ_PRE_VOL 0x03f1 +#define RT5665_L_EQ_POST_VOL 0x03f2 +#define RT5665_R_EQ_POST_VOL 0x03f3 +#define RT5665_SCAN_MODE_CTRL 0x07f0 +#define RT5665_I2C_MODE 0x07fa + + + +/* global definition */ +#define RT5665_L_MUTE (0x1 << 15) +#define RT5665_L_MUTE_SFT 15 +#define RT5665_VOL_L_MUTE (0x1 << 14) +#define RT5665_VOL_L_SFT 14 +#define RT5665_R_MUTE (0x1 << 7) +#define RT5665_R_MUTE_SFT 7 +#define RT5665_VOL_R_MUTE (0x1 << 6) +#define RT5665_VOL_R_SFT 6 +#define RT5665_L_VOL_MASK (0x3f << 8) +#define RT5665_L_VOL_SFT 8 +#define RT5665_R_VOL_MASK (0x3f) +#define RT5665_R_VOL_SFT 0 + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5665_G_HP (0xf << 8) +#define RT5665_G_HP_SFT 8 +#define RT5665_G_STO_DA_DMIX (0xf) +#define RT5665_G_STO_DA_SFT 0 + +/* CBJ Control (0x000b) */ +#define RT5665_BST_CBJ_MASK (0xf << 8) +#define RT5665_BST_CBJ_SFT 8 + +/* IN1/IN2 Control (0x000c) */ +#define RT5665_IN1_DF_MASK (0x1 << 15) +#define RT5665_IN1_DF 15 +#define RT5665_BST1_MASK (0x7f << 8) +#define RT5665_BST1_SFT 8 +#define RT5665_IN2_DF_MASK (0x1 << 7) +#define RT5665_IN2_DF 7 +#define RT5665_BST2_MASK (0x7f) +#define RT5665_BST2_SFT 0 + +/* IN3/IN4 Control (0x000d) */ +#define RT5665_IN3_DF_MASK (0x1 << 15) +#define RT5665_IN3_DF 15 +#define RT5665_BST3_MASK (0x7f << 8) +#define RT5665_BST3_SFT 8 +#define RT5665_IN4_DF_MASK (0x1 << 7) +#define RT5665_IN4_DF 7 +#define RT5665_BST4_MASK (0x7f) +#define RT5665_BST4_SFT 0 + +/* INL and INR Volume Control (0x000f) */ +#define RT5665_INL_VOL_MASK (0x1f << 8) +#define RT5665_INL_VOL_SFT 8 +#define RT5665_INR_VOL_MASK (0x1f) +#define RT5665_INR_VOL_SFT 0 + +/* Embeeded Jack and Type Detection Control 1 (0x0010) */ +#define RT5665_EMB_JD_EN (0x1 << 15) +#define RT5665_EMB_JD_EN_SFT 15 +#define RT5665_JD_MODE (0x1 << 13) +#define RT5665_JD_MODE_SFT 13 +#define RT5665_POLA_EXT_JD_MASK (0x1 << 11) +#define RT5665_POLA_EXT_JD_LOW (0x1 << 11) +#define RT5665_POLA_EXT_JD_HIGH (0x0 << 11) +#define RT5665_EXT_JD_DIG (0x1 << 9) +#define RT5665_POL_FAST_OFF_MASK (0x1 << 8) +#define RT5665_POL_FAST_OFF_HIGH (0x1 << 8) +#define RT5665_POL_FAST_OFF_LOW (0x0 << 8) +#define RT5665_VREF_POW_MASK (0x1 << 6) +#define RT5665_VREF_POW_FSM (0x0 << 6) +#define RT5665_VREF_POW_REG (0x1 << 6) +#define RT5665_MB1_PATH_MASK (0x1 << 5) +#define RT5665_CTRL_MB1_REG (0x1 << 5) +#define RT5665_CTRL_MB1_FSM (0x0 << 5) +#define RT5665_MB2_PATH_MASK (0x1 << 4) +#define RT5665_CTRL_MB2_REG (0x1 << 4) +#define RT5665_CTRL_MB2_FSM (0x0 << 4) +#define RT5665_TRIG_JD_MASK (0x1 << 3) +#define RT5665_TRIG_JD_HIGH (0x1 << 3) +#define RT5665_TRIG_JD_LOW (0x0 << 3) + +/* Embeeded Jack and Type Detection Control 2 (0x0011) */ +#define RT5665_EXT_JD_SRC (0x7 << 4) +#define RT5665_EXT_JD_SRC_SFT 4 +#define RT5665_EXT_JD_SRC_GPIO_JD1 (0x0 << 4) +#define RT5665_EXT_JD_SRC_GPIO_JD2 (0x1 << 4) +#define RT5665_EXT_JD_SRC_JD1_1 (0x2 << 4) +#define RT5665_EXT_JD_SRC_JD1_2 (0x3 << 4) +#define RT5665_EXT_JD_SRC_JD2 (0x4 << 4) +#define RT5665_EXT_JD_SRC_JD3 (0x5 << 4) +#define RT5665_EXT_JD_SRC_MANUAL (0x6 << 4) + +/* Combo Jack and Type Detection Control 4 (0x0013) */ +#define RT5665_SEL_SHT_MID_TON_MASK (0x3 << 12) +#define RT5665_SEL_SHT_MID_TON_2 (0x0 << 12) +#define RT5665_SEL_SHT_MID_TON_3 (0x1 << 12) +#define RT5665_CBJ_JD_TEST_MASK (0x1 << 6) +#define RT5665_CBJ_JD_TEST_NORM (0x0 << 6) +#define RT5665_CBJ_JD_TEST_MODE (0x1 << 6) + +/* Slience Detection Control (0x0015) */ +#define RT5665_SIL_DET_MASK (0x1 << 15) +#define RT5665_SIL_DET_DIS (0x0 << 15) +#define RT5665_SIL_DET_EN (0x1 << 15) + +/* DAC2 Control (0x0017) */ +#define RT5665_M_DAC2_L_VOL (0x1 << 13) +#define RT5665_M_DAC2_L_VOL_SFT 13 +#define RT5665_M_DAC2_R_VOL (0x1 << 12) +#define RT5665_M_DAC2_R_VOL_SFT 12 +#define RT5665_DAC_L2_SEL_MASK (0x7 << 4) +#define RT5665_DAC_L2_SEL_SFT 4 +#define RT5665_DAC_R2_SEL_MASK (0x7 << 0) +#define RT5665_DAC_R2_SEL_SFT 0 + +/* Sidetone Control (0x0018) */ +#define RT5665_ST_SEL_MASK (0x7 << 9) +#define RT5665_ST_SEL_SFT 9 +#define RT5665_ST_EN (0x1 << 6) +#define RT5665_ST_EN_SFT 6 + +/* DAC1 Digital Volume (0x0019) */ +#define RT5665_DAC_L1_VOL_MASK (0xff << 8) +#define RT5665_DAC_L1_VOL_SFT 8 +#define RT5665_DAC_R1_VOL_MASK (0xff) +#define RT5665_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x001a) */ +#define RT5665_DAC_L2_VOL_MASK (0xff << 8) +#define RT5665_DAC_L2_VOL_SFT 8 +#define RT5665_DAC_R2_VOL_MASK (0xff) +#define RT5665_DAC_R2_VOL_SFT 0 + +/* DAC3 Control (0x001b) */ +#define RT5665_M_DAC3_L_VOL (0x1 << 13) +#define RT5665_M_DAC3_L_VOL_SFT 13 +#define RT5665_M_DAC3_R_VOL (0x1 << 12) +#define RT5665_M_DAC3_R_VOL_SFT 12 +#define RT5665_DAC_L3_SEL_MASK (0x7 << 4) +#define RT5665_DAC_L3_SEL_SFT 4 +#define RT5665_DAC_R3_SEL_MASK (0x7 << 0) +#define RT5665_DAC_R3_SEL_SFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5665_ADC_L_VOL_MASK (0x7f << 8) +#define RT5665_ADC_L_VOL_SFT 8 +#define RT5665_ADC_R_VOL_MASK (0x7f) +#define RT5665_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x001d) */ +#define RT5665_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5665_MONO_ADC_L_VOL_SFT 8 +#define RT5665_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5665_MONO_ADC_R_VOL_SFT 0 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5665_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5665_STO1_ADC_L_BST_SFT 14 +#define RT5665_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5665_STO1_ADC_R_BST_SFT 12 + +/* Mono ADC Boost Gain Control (0x0020) */ +#define RT5665_MONO_ADC_L_BST_MASK (0x3 << 14) +#define RT5665_MONO_ADC_L_BST_SFT 14 +#define RT5665_MONO_ADC_R_BST_MASK (0x3 << 12) +#define RT5665_MONO_ADC_R_BST_SFT 12 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5665_STO2_ADC_L_BST_MASK (0x3 << 14) +#define RT5665_STO2_ADC_L_BST_SFT 14 +#define RT5665_STO2_ADC_R_BST_MASK (0x3 << 12) +#define RT5665_STO2_ADC_R_BST_SFT 12 + +/* Stereo1 ADC Mixer Control (0x0026) */ +#define RT5665_M_STO1_ADC_L1 (0x1 << 15) +#define RT5665_M_STO1_ADC_L1_SFT 15 +#define RT5665_M_STO1_ADC_L2 (0x1 << 14) +#define RT5665_M_STO1_ADC_L2_SFT 14 +#define RT5665_STO1_ADC1L_SRC_MASK (0x1 << 13) +#define RT5665_STO1_ADC1L_SRC_SFT 13 +#define RT5665_STO1_ADC1_SRC_ADC (0x1 << 13) +#define RT5665_STO1_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5665_STO1_ADC2L_SRC_MASK (0x1 << 12) +#define RT5665_STO1_ADC2L_SRC_SFT 12 +#define RT5665_STO1_ADCL_SRC_MASK (0x3 << 10) +#define RT5665_STO1_ADCL_SRC_SFT 10 +#define RT5665_STO1_DD_L_SRC_MASK (0x1 << 9) +#define RT5665_STO1_DD_L_SRC_SFT 9 +#define RT5665_STO1_DMIC_SRC_MASK (0x1 << 8) +#define RT5665_STO1_DMIC_SRC_SFT 8 +#define RT5665_STO1_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5665_STO1_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5665_M_STO1_ADC_R1 (0x1 << 7) +#define RT5665_M_STO1_ADC_R1_SFT 7 +#define RT5665_M_STO1_ADC_R2 (0x1 << 6) +#define RT5665_M_STO1_ADC_R2_SFT 6 +#define RT5665_STO1_ADC1R_SRC_MASK (0x1 << 5) +#define RT5665_STO1_ADC1R_SRC_SFT 5 +#define RT5665_STO1_ADC2R_SRC_MASK (0x1 << 4) +#define RT5665_STO1_ADC2R_SRC_SFT 4 +#define RT5665_STO1_ADCR_SRC_MASK (0x3 << 2) +#define RT5665_STO1_ADCR_SRC_SFT 2 +#define RT5665_STO1_DD_R_SRC_MASK (0x3) +#define RT5665_STO1_DD_R_SRC_SFT 0 + + +/* Mono1 ADC Mixer control (0x0027) */ +#define RT5665_M_MONO_ADC_L1 (0x1 << 15) +#define RT5665_M_MONO_ADC_L1_SFT 15 +#define RT5665_M_MONO_ADC_L2 (0x1 << 14) +#define RT5665_M_MONO_ADC_L2_SFT 14 +#define RT5665_MONO_ADC_L1_SRC_MASK (0x1 << 13) +#define RT5665_MONO_ADC_L1_SRC_SFT 13 +#define RT5665_MONO_ADC_L2_SRC_MASK (0x1 << 12) +#define RT5665_MONO_ADC_L2_SRC_SFT 12 +#define RT5665_MONO_ADC_L_SRC_MASK (0x3 << 10) +#define RT5665_MONO_ADC_L_SRC_SFT 10 +#define RT5665_MONO_DD_L_SRC_MASK (0x1 << 9) +#define RT5665_MONO_DD_L_SRC_SFT 9 +#define RT5665_MONO_DMIC_L_SRC_MASK (0x1 << 8) +#define RT5665_MONO_DMIC_L_SRC_SFT 8 +#define RT5665_M_MONO_ADC_R1 (0x1 << 7) +#define RT5665_M_MONO_ADC_R1_SFT 7 +#define RT5665_M_MONO_ADC_R2 (0x1 << 6) +#define RT5665_M_MONO_ADC_R2_SFT 6 +#define RT5665_MONO_ADC_R1_SRC_MASK (0x1 << 5) +#define RT5665_MONO_ADC_R1_SRC_SFT 5 +#define RT5665_MONO_ADC_R2_SRC_MASK (0x1 << 4) +#define RT5665_MONO_ADC_R2_SRC_SFT 4 +#define RT5665_MONO_ADC_R_SRC_MASK (0x3 << 2) +#define RT5665_MONO_ADC_R_SRC_SFT 2 +#define RT5665_MONO_DD_R_SRC_MASK (0x1 << 1) +#define RT5665_MONO_DD_R_SRC_SFT 1 +#define RT5665_MONO_DMIC_R_SRC_MASK 0x1 +#define RT5665_MONO_DMIC_R_SRC_SFT 0 + +/* Stereo2 ADC Mixer Control (0x0028) */ +#define RT5665_M_STO2_ADC_L1 (0x1 << 15) +#define RT5665_M_STO2_ADC_L1_UN (0x0 << 15) +#define RT5665_M_STO2_ADC_L1_SFT 15 +#define RT5665_M_STO2_ADC_L2 (0x1 << 14) +#define RT5665_M_STO2_ADC_L2_SFT 14 +#define RT5665_STO2_ADC1L_SRC_MASK (0x1 << 13) +#define RT5665_STO2_ADC1L_SRC_SFT 13 +#define RT5665_STO2_ADC1_SRC_ADC (0x1 << 13) +#define RT5665_STO2_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5665_STO2_ADC2L_SRC_MASK (0x1 << 12) +#define RT5665_STO2_ADC2L_SRC_SFT 12 +#define RT5665_STO2_ADCL_SRC_MASK (0x3 << 10) +#define RT5665_STO2_ADCL_SRC_SFT 10 +#define RT5665_STO2_DD_L_SRC_MASK (0x1 << 9) +#define RT5665_STO2_DD_L_SRC_SFT 9 +#define RT5665_STO2_DMIC_SRC_MASK (0x1 << 8) +#define RT5665_STO2_DMIC_SRC_SFT 8 +#define RT5665_STO2_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5665_STO2_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5665_M_STO2_ADC_R1 (0x1 << 7) +#define RT5665_M_STO2_ADC_R1_UN (0x0 << 7) +#define RT5665_M_STO2_ADC_R1_SFT 7 +#define RT5665_M_STO2_ADC_R2 (0x1 << 6) +#define RT5665_M_STO2_ADC_R2_SFT 6 +#define RT5665_STO2_ADC1R_SRC_MASK (0x1 << 5) +#define RT5665_STO2_ADC1R_SRC_SFT 5 +#define RT5665_STO2_ADC2R_SRC_MASK (0x1 << 4) +#define RT5665_STO2_ADC2R_SRC_SFT 4 +#define RT5665_STO2_ADCR_SRC_MASK (0x3 << 2) +#define RT5665_STO2_ADCR_SRC_SFT 2 +#define RT5665_STO2_DD_R_SRC_MASK (0x1 << 1) +#define RT5665_STO2_DD_R_SRC_SFT 1 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5665_M_ADCMIX_L (0x1 << 15) +#define RT5665_M_ADCMIX_L_SFT 15 +#define RT5665_M_DAC1_L (0x1 << 14) +#define RT5665_M_DAC1_L_SFT 14 +#define RT5665_DAC1_R_SEL_MASK (0x3 << 10) +#define RT5665_DAC1_R_SEL_SFT 10 +#define RT5665_DAC1_L_SEL_MASK (0x3 << 8) +#define RT5665_DAC1_L_SEL_SFT 8 +#define RT5665_M_ADCMIX_R (0x1 << 7) +#define RT5665_M_ADCMIX_R_SFT 7 +#define RT5665_M_DAC1_R (0x1 << 6) +#define RT5665_M_DAC1_R_SFT 6 + +/* Stereo1 DAC Mixer Control (0x002a) */ +#define RT5665_M_DAC_L1_STO_L (0x1 << 15) +#define RT5665_M_DAC_L1_STO_L_SFT 15 +#define RT5665_G_DAC_L1_STO_L_MASK (0x1 << 14) +#define RT5665_G_DAC_L1_STO_L_SFT 14 +#define RT5665_M_DAC_R1_STO_L (0x1 << 13) +#define RT5665_M_DAC_R1_STO_L_SFT 13 +#define RT5665_G_DAC_R1_STO_L_MASK (0x1 << 12) +#define RT5665_G_DAC_R1_STO_L_SFT 12 +#define RT5665_M_DAC_L2_STO_L (0x1 << 11) +#define RT5665_M_DAC_L2_STO_L_SFT 11 +#define RT5665_G_DAC_L2_STO_L_MASK (0x1 << 10) +#define RT5665_G_DAC_L2_STO_L_SFT 10 +#define RT5665_M_DAC_R2_STO_L (0x1 << 9) +#define RT5665_M_DAC_R2_STO_L_SFT 9 +#define RT5665_G_DAC_R2_STO_L_MASK (0x1 << 8) +#define RT5665_G_DAC_R2_STO_L_SFT 8 +#define RT5665_M_DAC_L1_STO_R (0x1 << 7) +#define RT5665_M_DAC_L1_STO_R_SFT 7 +#define RT5665_G_DAC_L1_STO_R_MASK (0x1 << 6) +#define RT5665_G_DAC_L1_STO_R_SFT 6 +#define RT5665_M_DAC_R1_STO_R (0x1 << 5) +#define RT5665_M_DAC_R1_STO_R_SFT 5 +#define RT5665_G_DAC_R1_STO_R_MASK (0x1 << 4) +#define RT5665_G_DAC_R1_STO_R_SFT 4 +#define RT5665_M_DAC_L2_STO_R (0x1 << 3) +#define RT5665_M_DAC_L2_STO_R_SFT 3 +#define RT5665_G_DAC_L2_STO_R_MASK (0x1 << 2) +#define RT5665_G_DAC_L2_STO_R_SFT 2 +#define RT5665_M_DAC_R2_STO_R (0x1 << 1) +#define RT5665_M_DAC_R2_STO_R_SFT 1 +#define RT5665_G_DAC_R2_STO_R_MASK (0x1) +#define RT5665_G_DAC_R2_STO_R_SFT 0 + +/* Mono DAC Mixer Control (0x002b) */ +#define RT5665_M_DAC_L1_MONO_L (0x1 << 15) +#define RT5665_M_DAC_L1_MONO_L_SFT 15 +#define RT5665_G_DAC_L1_MONO_L_MASK (0x1 << 14) +#define RT5665_G_DAC_L1_MONO_L_SFT 14 +#define RT5665_M_DAC_R1_MONO_L (0x1 << 13) +#define RT5665_M_DAC_R1_MONO_L_SFT 13 +#define RT5665_G_DAC_R1_MONO_L_MASK (0x1 << 12) +#define RT5665_G_DAC_R1_MONO_L_SFT 12 +#define RT5665_M_DAC_L2_MONO_L (0x1 << 11) +#define RT5665_M_DAC_L2_MONO_L_SFT 11 +#define RT5665_G_DAC_L2_MONO_L_MASK (0x1 << 10) +#define RT5665_G_DAC_L2_MONO_L_SFT 10 +#define RT5665_M_DAC_R2_MONO_L (0x1 << 9) +#define RT5665_M_DAC_R2_MONO_L_SFT 9 +#define RT5665_G_DAC_R2_MONO_L_MASK (0x1 << 8) +#define RT5665_G_DAC_R2_MONO_L_SFT 8 +#define RT5665_M_DAC_L1_MONO_R (0x1 << 7) +#define RT5665_M_DAC_L1_MONO_R_SFT 7 +#define RT5665_G_DAC_L1_MONO_R_MASK (0x1 << 6) +#define RT5665_G_DAC_L1_MONO_R_SFT 6 +#define RT5665_M_DAC_R1_MONO_R (0x1 << 5) +#define RT5665_M_DAC_R1_MONO_R_SFT 5 +#define RT5665_G_DAC_R1_MONO_R_MASK (0x1 << 4) +#define RT5665_G_DAC_R1_MONO_R_SFT 4 +#define RT5665_M_DAC_L2_MONO_R (0x1 << 3) +#define RT5665_M_DAC_L2_MONO_R_SFT 3 +#define RT5665_G_DAC_L2_MONO_R_MASK (0x1 << 2) +#define RT5665_G_DAC_L2_MONO_R_SFT 2 +#define RT5665_M_DAC_R2_MONO_R (0x1 << 1) +#define RT5665_M_DAC_R2_MONO_R_SFT 1 +#define RT5665_G_DAC_R2_MONO_R_MASK (0x1) +#define RT5665_G_DAC_R2_MONO_R_SFT 0 + +/* Stereo2 DAC Mixer Control (0x002c) */ +#define RT5665_M_DAC_L1_STO2_L (0x1 << 15) +#define RT5665_M_DAC_L1_STO2_L_SFT 15 +#define RT5665_G_DAC_L1_STO2_L_MASK (0x1 << 14) +#define RT5665_G_DAC_L1_STO2_L_SFT 14 +#define RT5665_M_DAC_L2_STO2_L (0x1 << 13) +#define RT5665_M_DAC_L2_STO2_L_SFT 13 +#define RT5665_G_DAC_L2_STO2_L_MASK (0x1 << 12) +#define RT5665_G_DAC_L2_STO2_L_SFT 12 +#define RT5665_M_DAC_L3_STO2_L (0x1 << 11) +#define RT5665_M_DAC_L3_STO2_L_SFT 11 +#define RT5665_G_DAC_L3_STO2_L_MASK (0x1 << 10) +#define RT5665_G_DAC_L3_STO2_L_SFT 10 +#define RT5665_M_ST_DAC_L1 (0x1 << 9) +#define RT5665_M_ST_DAC_L1_SFT 9 +#define RT5665_M_ST_DAC_R1 (0x1 << 8) +#define RT5665_M_ST_DAC_R1_SFT 8 +#define RT5665_M_DAC_R1_STO2_R (0x1 << 7) +#define RT5665_M_DAC_R1_STO2_R_SFT 7 +#define RT5665_G_DAC_R1_STO2_R_MASK (0x1 << 6) +#define RT5665_G_DAC_R1_STO2_R_SFT 6 +#define RT5665_M_DAC_R2_STO2_R (0x1 << 5) +#define RT5665_M_DAC_R2_STO2_R_SFT 5 +#define RT5665_G_DAC_R2_STO2_R_MASK (0x1 << 4) +#define RT5665_G_DAC_R2_STO2_R_SFT 4 +#define RT5665_M_DAC_R3_STO2_R (0x1 << 3) +#define RT5665_M_DAC_R3_STO2_R_SFT 3 +#define RT5665_G_DAC_R3_STO2_R_MASK (0x1 << 2) +#define RT5665_G_DAC_R3_STO2_R_SFT 2 + +/* Analog DAC1 Input Source Control (0x002d) */ +#define RT5665_DAC_MIX_L_MASK (0x3 << 12) +#define RT5665_DAC_MIX_L_SFT 12 +#define RT5665_DAC_MIX_R_MASK (0x3 << 8) +#define RT5665_DAC_MIX_R_SFT 8 +#define RT5665_DAC_L1_SRC_MASK (0x3 << 4) +#define RT5665_A_DACL1_SFT 4 +#define RT5665_DAC_R1_SRC_MASK (0x3) +#define RT5665_A_DACR1_SFT 0 + +/* Analog DAC Input Source Control (0x002e) */ +#define RT5665_A_DACL2_SEL (0x1 << 4) +#define RT5665_A_DACL2_SFT 4 +#define RT5665_A_DACR2_SEL (0x1 << 0) +#define RT5665_A_DACR2_SFT 0 + +/* Digital Interface Data Control (0x002f) */ +#define RT5665_IF2_1_ADC_IN_MASK (0x7 << 12) +#define RT5665_IF2_1_ADC_IN_SFT 12 +#define RT5665_IF2_1_DAC_SEL_MASK (0x3 << 10) +#define RT5665_IF2_1_DAC_SEL_SFT 10 +#define RT5665_IF2_1_ADC_SEL_MASK (0x3 << 8) +#define RT5665_IF2_1_ADC_SEL_SFT 8 +#define RT5665_IF2_2_ADC_IN_MASK (0x7 << 4) +#define RT5665_IF2_2_ADC_IN_SFT 4 +#define RT5665_IF2_2_DAC_SEL_MASK (0x3 << 2) +#define RT5665_IF2_2_DAC_SEL_SFT 2 +#define RT5665_IF2_2_ADC_SEL_MASK (0x3 << 0) +#define RT5665_IF2_2_ADC_SEL_SFT 0 + +/* Digital Interface Data Control (0x0030) */ +#define RT5665_IF3_ADC_IN_MASK (0x7 << 4) +#define RT5665_IF3_ADC_IN_SFT 4 +#define RT5665_IF3_DAC_SEL_MASK (0x3 << 2) +#define RT5665_IF3_DAC_SEL_SFT 2 +#define RT5665_IF3_ADC_SEL_MASK (0x3 << 0) +#define RT5665_IF3_ADC_SEL_SFT 0 + +/* PDM Output Control (0x0031) */ +#define RT5665_M_PDM1_L (0x1 << 14) +#define RT5665_M_PDM1_L_SFT 14 +#define RT5665_M_PDM1_R (0x1 << 12) +#define RT5665_M_PDM1_R_SFT 12 +#define RT5665_PDM1_L_MASK (0x3 << 10) +#define RT5665_PDM1_L_SFT 10 +#define RT5665_PDM1_R_MASK (0x3 << 8) +#define RT5665_PDM1_R_SFT 8 +#define RT5665_PDM1_BUSY (0x1 << 6) +#define RT5665_PDM_PATTERN (0x1 << 5) +#define RT5665_PDM_GAIN (0x1 << 4) +#define RT5665_LRCK_PDM_PI2C (0x1 << 3) +#define RT5665_PDM_DIV_MASK (0x3) + +/*S/PDIF Output Control (0x0036) */ +#define RT5665_SPDIF_SEL_MASK (0x3 << 0) +#define RT5665_SPDIF_SEL_SFT 0 + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5665_M_CBJ_RM1_L (0x1 << 7) +#define RT5665_M_CBJ_RM1_L_SFT 7 +#define RT5665_M_BST1_RM1_L (0x1 << 5) +#define RT5665_M_BST1_RM1_L_SFT 5 +#define RT5665_M_BST2_RM1_L (0x1 << 4) +#define RT5665_M_BST2_RM1_L_SFT 4 +#define RT5665_M_BST3_RM1_L (0x1 << 3) +#define RT5665_M_BST3_RM1_L_SFT 3 +#define RT5665_M_BST4_RM1_L (0x1 << 2) +#define RT5665_M_BST4_RM1_L_SFT 2 +#define RT5665_M_INL_RM1_L (0x1 << 1) +#define RT5665_M_INL_RM1_L_SFT 1 +#define RT5665_M_INR_RM1_L (0x1) +#define RT5665_M_INR_RM1_L_SFT 0 + +/* REC Right Mixer Control 2 (0x003e) */ +#define RT5665_M_AEC_REF_RM1_R (0x1 << 7) +#define RT5665_M_AEC_REF_RM1_R_SFT 7 +#define RT5665_M_BST1_RM1_R (0x1 << 5) +#define RT5665_M_BST1_RM1_R_SFT 5 +#define RT5665_M_BST2_RM1_R (0x1 << 4) +#define RT5665_M_BST2_RM1_R_SFT 4 +#define RT5665_M_BST3_RM1_R (0x1 << 3) +#define RT5665_M_BST3_RM1_R_SFT 3 +#define RT5665_M_BST4_RM1_R (0x1 << 2) +#define RT5665_M_BST4_RM1_R_SFT 2 +#define RT5665_M_INR_RM1_R (0x1 << 1) +#define RT5665_M_INR_RM1_R_SFT 1 +#define RT5665_M_MONOVOL_RM1_R (0x1) +#define RT5665_M_MONOVOL_RM1_R_SFT 0 + +/* REC Mixer 2 Left Control 2 (0x0041) */ +#define RT5665_M_CBJ_RM2_L (0x1 << 7) +#define RT5665_M_CBJ_RM2_L_SFT 7 +#define RT5665_M_BST1_RM2_L (0x1 << 5) +#define RT5665_M_BST1_RM2_L_SFT 5 +#define RT5665_M_BST2_RM2_L (0x1 << 4) +#define RT5665_M_BST2_RM2_L_SFT 4 +#define RT5665_M_BST3_RM2_L (0x1 << 3) +#define RT5665_M_BST3_RM2_L_SFT 3 +#define RT5665_M_BST4_RM2_L (0x1 << 2) +#define RT5665_M_BST4_RM2_L_SFT 2 +#define RT5665_M_INL_RM2_L (0x1 << 1) +#define RT5665_M_INL_RM2_L_SFT 1 +#define RT5665_M_INR_RM2_L (0x1) +#define RT5665_M_INR_RM2_L_SFT 0 + +/* REC Mixer 2 Right Control 2 (0x0043) */ +#define RT5665_M_MONOVOL_RM2_R (0x1 << 7) +#define RT5665_M_MONOVOL_RM2_R_SFT 7 +#define RT5665_M_BST1_RM2_R (0x1 << 5) +#define RT5665_M_BST1_RM2_R_SFT 5 +#define RT5665_M_BST2_RM2_R (0x1 << 4) +#define RT5665_M_BST2_RM2_R_SFT 4 +#define RT5665_M_BST3_RM2_R (0x1 << 3) +#define RT5665_M_BST3_RM2_R_SFT 3 +#define RT5665_M_BST4_RM2_R (0x1 << 2) +#define RT5665_M_BST4_RM2_R_SFT 2 +#define RT5665_M_INL_RM2_R (0x1 << 1) +#define RT5665_M_INL_RM2_R_SFT 1 +#define RT5665_M_INR_RM2_R (0x1) +#define RT5665_M_INR_RM2_R_SFT 0 + +/* SPK Left Mixer Control (0x0046) */ +#define RT5665_M_BST3_SM_L (0x1 << 4) +#define RT5665_M_BST3_SM_L_SFT 4 +#define RT5665_M_IN_R_SM_L (0x1 << 3) +#define RT5665_M_IN_R_SM_L_SFT 3 +#define RT5665_M_IN_L_SM_L (0x1 << 2) +#define RT5665_M_IN_L_SM_L_SFT 2 +#define RT5665_M_BST1_SM_L (0x1 << 1) +#define RT5665_M_BST1_SM_L_SFT 1 +#define RT5665_M_DAC_L2_SM_L (0x1) +#define RT5665_M_DAC_L2_SM_L_SFT 0 + +/* SPK Right Mixer Control (0x0047) */ +#define RT5665_M_BST3_SM_R (0x1 << 4) +#define RT5665_M_BST3_SM_R_SFT 4 +#define RT5665_M_IN_R_SM_R (0x1 << 3) +#define RT5665_M_IN_R_SM_R_SFT 3 +#define RT5665_M_IN_L_SM_R (0x1 << 2) +#define RT5665_M_IN_L_SM_R_SFT 2 +#define RT5665_M_BST4_SM_R (0x1 << 1) +#define RT5665_M_BST4_SM_R_SFT 1 +#define RT5665_M_DAC_R2_SM_R (0x1) +#define RT5665_M_DAC_R2_SM_R_SFT 0 + +/* SPO Amp Input and Gain Control (0x0048) */ +#define RT5665_M_DAC_L2_SPKOMIX (0x1 << 13) +#define RT5665_M_DAC_L2_SPKOMIX_SFT 13 +#define RT5665_M_SPKVOLL_SPKOMIX (0x1 << 12) +#define RT5665_M_SPKVOLL_SPKOMIX_SFT 12 +#define RT5665_M_DAC_R2_SPKOMIX (0x1 << 9) +#define RT5665_M_DAC_R2_SPKOMIX_SFT 9 +#define RT5665_M_SPKVOLR_SPKOMIX (0x1 << 8) +#define RT5665_M_SPKVOLR_SPKOMIX_SFT 8 + +/* MONOMIX Input and Gain Control (0x004b) */ +#define RT5665_G_MONOVOL_MA (0x1 << 10) +#define RT5665_G_MONOVOL_MA_SFT 10 +#define RT5665_M_MONOVOL_MA (0x1 << 9) +#define RT5665_M_MONOVOL_MA_SFT 9 +#define RT5665_M_DAC_L2_MA (0x1 << 8) +#define RT5665_M_DAC_L2_MA_SFT 8 +#define RT5665_M_BST3_MM (0x1 << 4) +#define RT5665_M_BST3_MM_SFT 4 +#define RT5665_M_BST2_MM (0x1 << 3) +#define RT5665_M_BST2_MM_SFT 3 +#define RT5665_M_BST1_MM (0x1 << 2) +#define RT5665_M_BST1_MM_SFT 2 +#define RT5665_M_RECMIC2L_MM (0x1 << 1) +#define RT5665_M_RECMIC2L_MM_SFT 1 +#define RT5665_M_DAC_L2_MM (0x1) +#define RT5665_M_DAC_L2_MM_SFT 0 + +/* Output Left Mixer Control 1 (0x004d) */ +#define RT5665_G_BST3_OM_L_MASK (0x7 << 12) +#define RT5665_G_BST3_OM_L_SFT 12 +#define RT5665_G_BST2_OM_L_MASK (0x7 << 9) +#define RT5665_G_BST2_OM_L_SFT 9 +#define RT5665_G_BST1_OM_L_MASK (0x7 << 6) +#define RT5665_G_BST1_OM_L_SFT 6 +#define RT5665_G_IN_L_OM_L_MASK (0x7 << 3) +#define RT5665_G_IN_L_OM_L_SFT 3 +#define RT5665_G_DAC_L2_OM_L_MASK (0x7 << 0) +#define RT5665_G_DAC_L2_OM_L_SFT 0 + +/* Output Left Mixer Input Control (0x004e) */ +#define RT5665_M_BST3_OM_L (0x1 << 4) +#define RT5665_M_BST3_OM_L_SFT 4 +#define RT5665_M_BST2_OM_L (0x1 << 3) +#define RT5665_M_BST2_OM_L_SFT 3 +#define RT5665_M_BST1_OM_L (0x1 << 2) +#define RT5665_M_BST1_OM_L_SFT 2 +#define RT5665_M_IN_L_OM_L (0x1 << 1) +#define RT5665_M_IN_L_OM_L_SFT 1 +#define RT5665_M_DAC_L2_OM_L (0x1) +#define RT5665_M_DAC_L2_OM_L_SFT 0 + +/* Output Right Mixer Input Control (0x0050) */ +#define RT5665_M_BST4_OM_R (0x1 << 4) +#define RT5665_M_BST4_OM_R_SFT 4 +#define RT5665_M_BST3_OM_R (0x1 << 3) +#define RT5665_M_BST3_OM_R_SFT 3 +#define RT5665_M_BST2_OM_R (0x1 << 2) +#define RT5665_M_BST2_OM_R_SFT 2 +#define RT5665_M_IN_R_OM_R (0x1 << 1) +#define RT5665_M_IN_R_OM_R_SFT 1 +#define RT5665_M_DAC_R2_OM_R (0x1) +#define RT5665_M_DAC_R2_OM_R_SFT 0 + +/* LOUT Mixer Control (0x0052) */ +#define RT5665_M_DAC_L2_LM (0x1 << 15) +#define RT5665_M_DAC_L2_LM_SFT 15 +#define RT5665_M_DAC_R2_LM (0x1 << 14) +#define RT5665_M_DAC_R2_LM_SFT 14 +#define RT5665_M_OV_L_LM (0x1 << 13) +#define RT5665_M_OV_L_LM_SFT 13 +#define RT5665_M_OV_R_LM (0x1 << 12) +#define RT5665_M_OV_R_LM_SFT 12 +#define RT5665_LOUT_BST_SFT 11 +#define RT5665_LOUT_DF (0x1 << 11) +#define RT5665_LOUT_DF_SFT 11 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5665_PWR_I2S1_1 (0x1 << 15) +#define RT5665_PWR_I2S1_1_BIT 15 +#define RT5665_PWR_I2S1_2 (0x1 << 14) +#define RT5665_PWR_I2S1_2_BIT 14 +#define RT5665_PWR_I2S2_1 (0x1 << 13) +#define RT5665_PWR_I2S2_1_BIT 13 +#define RT5665_PWR_I2S2_2 (0x1 << 12) +#define RT5665_PWR_I2S2_2_BIT 12 +#define RT5665_PWR_DAC_L1 (0x1 << 11) +#define RT5665_PWR_DAC_L1_BIT 11 +#define RT5665_PWR_DAC_R1 (0x1 << 10) +#define RT5665_PWR_DAC_R1_BIT 10 +#define RT5665_PWR_I2S3 (0x1 << 9) +#define RT5665_PWR_I2S3_BIT 9 +#define RT5665_PWR_LDO (0x1 << 8) +#define RT5665_PWR_LDO_BIT 8 +#define RT5665_PWR_DAC_L2 (0x1 << 7) +#define RT5665_PWR_DAC_L2_BIT 7 +#define RT5665_PWR_DAC_R2 (0x1 << 6) +#define RT5665_PWR_DAC_R2_BIT 6 +#define RT5665_PWR_ADC_L1 (0x1 << 4) +#define RT5665_PWR_ADC_L1_BIT 4 +#define RT5665_PWR_ADC_R1 (0x1 << 3) +#define RT5665_PWR_ADC_R1_BIT 3 +#define RT5665_PWR_ADC_L2 (0x1 << 2) +#define RT5665_PWR_ADC_L2_BIT 2 +#define RT5665_PWR_ADC_R2 (0x1 << 1) +#define RT5665_PWR_ADC_R2_BIT 1 + +/* Power Management for Digital 2 (0x0062) */ +#define RT5665_PWR_ADC_S1F (0x1 << 15) +#define RT5665_PWR_ADC_S1F_BIT 15 +#define RT5665_PWR_ADC_S2F (0x1 << 14) +#define RT5665_PWR_ADC_S2F_BIT 14 +#define RT5665_PWR_ADC_MF_L (0x1 << 13) +#define RT5665_PWR_ADC_MF_L_BIT 13 +#define RT5665_PWR_ADC_MF_R (0x1 << 12) +#define RT5665_PWR_ADC_MF_R_BIT 12 +#define RT5665_PWR_DAC_S2F (0x1 << 11) +#define RT5665_PWR_DAC_S2F_BIT 11 +#define RT5665_PWR_DAC_S1F (0x1 << 10) +#define RT5665_PWR_DAC_S1F_BIT 10 +#define RT5665_PWR_DAC_MF_L (0x1 << 9) +#define RT5665_PWR_DAC_MF_L_BIT 9 +#define RT5665_PWR_DAC_MF_R (0x1 << 8) +#define RT5665_PWR_DAC_MF_R_BIT 8 +#define RT5665_PWR_PDM1 (0x1 << 7) +#define RT5665_PWR_PDM1_BIT 7 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5665_PWR_VREF1 (0x1 << 15) +#define RT5665_PWR_VREF1_BIT 15 +#define RT5665_PWR_FV1 (0x1 << 14) +#define RT5665_PWR_FV1_BIT 14 +#define RT5665_PWR_VREF2 (0x1 << 13) +#define RT5665_PWR_VREF2_BIT 13 +#define RT5665_PWR_FV2 (0x1 << 12) +#define RT5665_PWR_FV2_BIT 12 +#define RT5665_PWR_VREF3 (0x1 << 11) +#define RT5665_PWR_VREF3_BIT 11 +#define RT5665_PWR_FV3 (0x1 << 10) +#define RT5665_PWR_FV3_BIT 10 +#define RT5665_PWR_MB (0x1 << 9) +#define RT5665_PWR_MB_BIT 9 +#define RT5665_PWR_LM (0x1 << 8) +#define RT5665_PWR_LM_BIT 8 +#define RT5665_PWR_BG (0x1 << 7) +#define RT5665_PWR_BG_BIT 7 +#define RT5665_PWR_MA (0x1 << 6) +#define RT5665_PWR_MA_BIT 6 +#define RT5665_PWR_HA_L (0x1 << 5) +#define RT5665_PWR_HA_L_BIT 5 +#define RT5665_PWR_HA_R (0x1 << 4) +#define RT5665_PWR_HA_R_BIT 4 +#define RT5665_HP_DRIVER_MASK (0x3 << 2) +#define RT5665_HP_DRIVER_1X (0x0 << 2) +#define RT5665_HP_DRIVER_3X (0x1 << 2) +#define RT5665_HP_DRIVER_5X (0x3 << 2) +#define RT5665_LDO1_DVO_MASK (0x3) +#define RT5665_LDO1_DVO_09 (0x0) +#define RT5665_LDO1_DVO_10 (0x1) +#define RT5665_LDO1_DVO_12 (0x2) +#define RT5665_LDO1_DVO_14 (0x3) + +/* Power Management for Analog 2 (0x0064) */ +#define RT5665_PWR_BST1 (0x1 << 15) +#define RT5665_PWR_BST1_BIT 15 +#define RT5665_PWR_BST2 (0x1 << 14) +#define RT5665_PWR_BST2_BIT 14 +#define RT5665_PWR_BST3 (0x1 << 13) +#define RT5665_PWR_BST3_BIT 13 +#define RT5665_PWR_BST4 (0x1 << 12) +#define RT5665_PWR_BST4_BIT 12 +#define RT5665_PWR_MB1 (0x1 << 11) +#define RT5665_PWR_MB1_PWR_DOWN (0x0 << 11) +#define RT5665_PWR_MB1_BIT 11 +#define RT5665_PWR_MB2 (0x1 << 10) +#define RT5665_PWR_MB2_PWR_DOWN (0x0 << 10) +#define RT5665_PWR_MB2_BIT 10 +#define RT5665_PWR_MB3 (0x1 << 9) +#define RT5665_PWR_MB3_BIT 9 +#define RT5665_PWR_BST1_P (0x1 << 7) +#define RT5665_PWR_BST1_P_BIT 7 +#define RT5665_PWR_BST2_P (0x1 << 6) +#define RT5665_PWR_BST2_P_BIT 6 +#define RT5665_PWR_BST3_P (0x1 << 5) +#define RT5665_PWR_BST3_P_BIT 5 +#define RT5665_PWR_BST4_P (0x1 << 4) +#define RT5665_PWR_BST4_P_BIT 4 +#define RT5665_PWR_JD1 (0x1 << 3) +#define RT5665_PWR_JD1_BIT 3 +#define RT5665_PWR_JD2 (0x1 << 2) +#define RT5665_PWR_JD2_BIT 2 +#define RT5665_PWR_RM1_L (0x1 << 1) +#define RT5665_PWR_RM1_L_BIT 1 +#define RT5665_PWR_RM1_R (0x1) +#define RT5665_PWR_RM1_R_BIT 0 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5665_PWR_CBJ (0x1 << 9) +#define RT5665_PWR_CBJ_BIT 9 +#define RT5665_PWR_BST_L (0x1 << 8) +#define RT5665_PWR_BST_L_BIT 8 +#define RT5665_PWR_BST_R (0x1 << 7) +#define RT5665_PWR_BST_R_BIT 7 +#define RT5665_PWR_PLL (0x1 << 6) +#define RT5665_PWR_PLL_BIT 6 +#define RT5665_PWR_LDO2 (0x1 << 2) +#define RT5665_PWR_LDO2_BIT 2 +#define RT5665_PWR_SVD (0x1 << 1) +#define RT5665_PWR_SVD_BIT 1 + +/* Power Management for Mixer (0x0066) */ +#define RT5665_PWR_RM2_L (0x1 << 15) +#define RT5665_PWR_RM2_L_BIT 15 +#define RT5665_PWR_RM2_R (0x1 << 14) +#define RT5665_PWR_RM2_R_BIT 14 +#define RT5665_PWR_OM_L (0x1 << 13) +#define RT5665_PWR_OM_L_BIT 13 +#define RT5665_PWR_OM_R (0x1 << 12) +#define RT5665_PWR_OM_R_BIT 12 +#define RT5665_PWR_MM (0x1 << 11) +#define RT5665_PWR_MM_BIT 11 +#define RT5665_PWR_AEC_REF (0x1 << 6) +#define RT5665_PWR_AEC_REF_BIT 6 +#define RT5665_PWR_STO1_DAC_L (0x1 << 5) +#define RT5665_PWR_STO1_DAC_L_BIT 5 +#define RT5665_PWR_STO1_DAC_R (0x1 << 4) +#define RT5665_PWR_STO1_DAC_R_BIT 4 +#define RT5665_PWR_MONO_DAC_L (0x1 << 3) +#define RT5665_PWR_MONO_DAC_L_BIT 3 +#define RT5665_PWR_MONO_DAC_R (0x1 << 2) +#define RT5665_PWR_MONO_DAC_R_BIT 2 +#define RT5665_PWR_STO2_DAC_L (0x1 << 1) +#define RT5665_PWR_STO2_DAC_L_BIT 1 +#define RT5665_PWR_STO2_DAC_R (0x1) +#define RT5665_PWR_STO2_DAC_R_BIT 0 + +/* Power Management for Volume (0x0067) */ +#define RT5665_PWR_OV_L (0x1 << 13) +#define RT5665_PWR_OV_L_BIT 13 +#define RT5665_PWR_OV_R (0x1 << 12) +#define RT5665_PWR_OV_R_BIT 12 +#define RT5665_PWR_IN_L (0x1 << 9) +#define RT5665_PWR_IN_L_BIT 9 +#define RT5665_PWR_IN_R (0x1 << 8) +#define RT5665_PWR_IN_R_BIT 8 +#define RT5665_PWR_MV (0x1 << 7) +#define RT5665_PWR_MV_BIT 7 +#define RT5665_PWR_MIC_DET (0x1 << 5) +#define RT5665_PWR_MIC_DET_BIT 5 + +/* (0x006b) */ +#define RT5665_SYS_CLK_DET 15 +#define RT5665_HP_CLK_DET 14 +#define RT5665_MONO_CLK_DET 13 +#define RT5665_LOUT_CLK_DET 12 +#define RT5665_POW_CLK_DET 0 + +/* Digital Microphone Control 1 (0x006e) */ +#define RT5665_DMIC_1_EN_MASK (0x1 << 15) +#define RT5665_DMIC_1_EN_SFT 15 +#define RT5665_DMIC_1_DIS (0x0 << 15) +#define RT5665_DMIC_1_EN (0x1 << 15) +#define RT5665_DMIC_2_EN_MASK (0x1 << 14) +#define RT5665_DMIC_2_EN_SFT 14 +#define RT5665_DMIC_2_DIS (0x0 << 14) +#define RT5665_DMIC_2_EN (0x1 << 14) +#define RT5665_DMIC_2_DP_MASK (0x1 << 9) +#define RT5665_DMIC_2_DP_SFT 9 +#define RT5665_DMIC_2_DP_GPIO5 (0x0 << 9) +#define RT5665_DMIC_2_DP_IN2P (0x1 << 9) +#define RT5665_DMIC_CLK_MASK (0x7 << 5) +#define RT5665_DMIC_CLK_SFT 5 +#define RT5665_DMIC_1_DP_MASK (0x1 << 1) +#define RT5665_DMIC_1_DP_SFT 1 +#define RT5665_DMIC_1_DP_GPIO4 (0x0 << 1) +#define RT5665_DMIC_1_DP_IN2N (0x1 << 1) + + +/* Digital Microphone Control 1 (0x006f) */ +#define RT5665_DMIC_2L_LH_MASK (0x1 << 3) +#define RT5665_DMIC_2L_LH_SFT 3 +#define RT5665_DMIC_2L_LH_RISING (0x0 << 3) +#define RT5665_DMIC_2L_LH_FALLING (0x1 << 3) +#define RT5665_DMIC_2R_LH_MASK (0x1 << 2) +#define RT5665_DMIC_2R_LH_SFT 2 +#define RT5665_DMIC_2R_LH_RISING (0x0 << 2) +#define RT5665_DMIC_2R_LH_FALLING (0x1 << 2) +#define RT5665_DMIC_1L_LH_MASK (0x1 << 1) +#define RT5665_DMIC_1L_LH_SFT 1 +#define RT5665_DMIC_1L_LH_RISING (0x0 << 1) +#define RT5665_DMIC_1L_LH_FALLING (0x1 << 1) +#define RT5665_DMIC_1R_LH_MASK (0x1 << 0) +#define RT5665_DMIC_1R_LH_SFT 0 +#define RT5665_DMIC_1R_LH_RISING (0x0) +#define RT5665_DMIC_1R_LH_FALLING (0x1) + +/* I2S1/2/3 Audio Serial Data Port Control (0x0070 0x0071 0x0072) */ +#define RT5665_I2S_MS_MASK (0x1 << 15) +#define RT5665_I2S_MS_SFT 15 +#define RT5665_I2S_MS_M (0x0 << 15) +#define RT5665_I2S_MS_S (0x1 << 15) +#define RT5665_I2S_PIN_CFG_MASK (0x1 << 14) +#define RT5665_I2S_PIN_CFG_SFT 14 +#define RT5665_I2S_CLK_SEL_MASK (0x1 << 11) +#define RT5665_I2S_CLK_SEL_SFT 11 +#define RT5665_I2S_BP_MASK (0x1 << 8) +#define RT5665_I2S_BP_SFT 8 +#define RT5665_I2S_BP_NOR (0x0 << 8) +#define RT5665_I2S_BP_INV (0x1 << 8) +#define RT5665_I2S_DL_MASK (0x3 << 4) +#define RT5665_I2S_DL_SFT 4 +#define RT5665_I2S_DL_16 (0x0 << 4) +#define RT5665_I2S_DL_20 (0x1 << 4) +#define RT5665_I2S_DL_24 (0x2 << 4) +#define RT5665_I2S_DL_8 (0x3 << 4) +#define RT5665_I2S_DF_MASK (0x7) +#define RT5665_I2S_DF_SFT 0 +#define RT5665_I2S_DF_I2S (0x0) +#define RT5665_I2S_DF_LEFT (0x1) +#define RT5665_I2S_DF_PCM_A (0x2) +#define RT5665_I2S_DF_PCM_B (0x3) +#define RT5665_I2S_DF_PCM_A_N (0x6) +#define RT5665_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5665_I2S_PD1_MASK (0x7 << 12) +#define RT5665_I2S_PD1_SFT 12 +#define RT5665_I2S_PD1_1 (0x0 << 12) +#define RT5665_I2S_PD1_2 (0x1 << 12) +#define RT5665_I2S_PD1_3 (0x2 << 12) +#define RT5665_I2S_PD1_4 (0x3 << 12) +#define RT5665_I2S_PD1_6 (0x4 << 12) +#define RT5665_I2S_PD1_8 (0x5 << 12) +#define RT5665_I2S_PD1_12 (0x6 << 12) +#define RT5665_I2S_PD1_16 (0x7 << 12) +#define RT5665_I2S_M_PD2_MASK (0x7 << 8) +#define RT5665_I2S_M_PD2_SFT 8 +#define RT5665_I2S_M_PD2_1 (0x0 << 8) +#define RT5665_I2S_M_PD2_2 (0x1 << 8) +#define RT5665_I2S_M_PD2_3 (0x2 << 8) +#define RT5665_I2S_M_PD2_4 (0x3 << 8) +#define RT5665_I2S_M_PD2_6 (0x4 << 8) +#define RT5665_I2S_M_PD2_8 (0x5 << 8) +#define RT5665_I2S_M_PD2_12 (0x6 << 8) +#define RT5665_I2S_M_PD2_16 (0x7 << 8) +#define RT5665_I2S_CLK_SRC_MASK (0x3 << 4) +#define RT5665_I2S_CLK_SRC_SFT 4 +#define RT5665_I2S_CLK_SRC_MCLK (0x0 << 4) +#define RT5665_I2S_CLK_SRC_PLL1 (0x1 << 4) +#define RT5665_I2S_CLK_SRC_RCCLK (0x2 << 4) +#define RT5665_DAC_OSR_MASK (0x3 << 2) +#define RT5665_DAC_OSR_SFT 2 +#define RT5665_DAC_OSR_128 (0x0 << 2) +#define RT5665_DAC_OSR_64 (0x1 << 2) +#define RT5665_DAC_OSR_32 (0x2 << 2) +#define RT5665_ADC_OSR_MASK (0x3) +#define RT5665_ADC_OSR_SFT 0 +#define RT5665_ADC_OSR_128 (0x0) +#define RT5665_ADC_OSR_64 (0x1) +#define RT5665_ADC_OSR_32 (0x2) + +/* ADC/DAC Clock Control 2 (0x0074) */ +#define RT5665_I2S_BCLK_MS2_MASK (0x1 << 15) +#define RT5665_I2S_BCLK_MS2_SFT 15 +#define RT5665_I2S_BCLK_MS2_32 (0x0 << 15) +#define RT5665_I2S_BCLK_MS2_64 (0x1 << 15) +#define RT5665_I2S_PD2_MASK (0x7 << 12) +#define RT5665_I2S_PD2_SFT 12 +#define RT5665_I2S_PD2_1 (0x0 << 12) +#define RT5665_I2S_PD2_2 (0x1 << 12) +#define RT5665_I2S_PD2_3 (0x2 << 12) +#define RT5665_I2S_PD2_4 (0x3 << 12) +#define RT5665_I2S_PD2_6 (0x4 << 12) +#define RT5665_I2S_PD2_8 (0x5 << 12) +#define RT5665_I2S_PD2_12 (0x6 << 12) +#define RT5665_I2S_PD2_16 (0x7 << 12) +#define RT5665_I2S_BCLK_MS3_MASK (0x1 << 11) +#define RT5665_I2S_BCLK_MS3_SFT 11 +#define RT5665_I2S_BCLK_MS3_32 (0x0 << 11) +#define RT5665_I2S_BCLK_MS3_64 (0x1 << 11) +#define RT5665_I2S_PD3_MASK (0x7 << 8) +#define RT5665_I2S_PD3_SFT 8 +#define RT5665_I2S_PD3_1 (0x0 << 8) +#define RT5665_I2S_PD3_2 (0x1 << 8) +#define RT5665_I2S_PD3_3 (0x2 << 8) +#define RT5665_I2S_PD3_4 (0x3 << 8) +#define RT5665_I2S_PD3_6 (0x4 << 8) +#define RT5665_I2S_PD3_8 (0x5 << 8) +#define RT5665_I2S_PD3_12 (0x6 << 8) +#define RT5665_I2S_PD3_16 (0x7 << 8) +#define RT5665_I2S_PD4_MASK (0x7 << 4) +#define RT5665_I2S_PD4_SFT 4 +#define RT5665_I2S_PD4_1 (0x0 << 4) +#define RT5665_I2S_PD4_2 (0x1 << 4) +#define RT5665_I2S_PD4_3 (0x2 << 4) +#define RT5665_I2S_PD4_4 (0x3 << 4) +#define RT5665_I2S_PD4_6 (0x4 << 4) +#define RT5665_I2S_PD4_8 (0x5 << 4) +#define RT5665_I2S_PD4_12 (0x6 << 4) +#define RT5665_I2S_PD4_16 (0x7 << 4) + +/* TDM control 1 (0x0078) */ +#define RT5665_I2S1_MODE_MASK (0x1 << 15) +#define RT5665_I2S1_MODE_I2S (0x0 << 15) +#define RT5665_I2S1_MODE_TDM (0x1 << 15) +#define RT5665_TDM_IN_CH_MASK (0x3 << 10) +#define RT5665_TDM_IN_CH_2 (0x0 << 10) +#define RT5665_TDM_IN_CH_4 (0x1 << 10) +#define RT5665_TDM_IN_CH_6 (0x2 << 10) +#define RT5665_TDM_IN_CH_8 (0x3 << 10) +#define RT5665_TDM_OUT_CH_MASK (0x3 << 8) +#define RT5665_TDM_OUT_CH_2 (0x0 << 8) +#define RT5665_TDM_OUT_CH_4 (0x1 << 8) +#define RT5665_TDM_OUT_CH_6 (0x2 << 8) +#define RT5665_TDM_OUT_CH_8 (0x3 << 8) +#define RT5665_TDM_IN_LEN_MASK (0x3 << 6) +#define RT5665_TDM_IN_LEN_16 (0x0 << 6) +#define RT5665_TDM_IN_LEN_20 (0x1 << 6) +#define RT5665_TDM_IN_LEN_24 (0x2 << 6) +#define RT5665_TDM_IN_LEN_32 (0x3 << 6) +#define RT5665_TDM_OUT_LEN_MASK (0x3 << 4) +#define RT5665_TDM_OUT_LEN_16 (0x0 << 4) +#define RT5665_TDM_OUT_LEN_20 (0x1 << 4) +#define RT5665_TDM_OUT_LEN_24 (0x2 << 4) +#define RT5665_TDM_OUT_LEN_32 (0x3 << 4) + + +/* TDM control 2 (0x0079) */ +#define RT5665_I2S1_1_DS_ADC_SLOT01_SFT 14 +#define RT5665_I2S1_1_DS_ADC_SLOT23_SFT 12 +#define RT5665_I2S1_1_DS_ADC_SLOT45_SFT 10 +#define RT5665_I2S1_1_DS_ADC_SLOT67_SFT 8 +#define RT5665_I2S1_2_DS_ADC_SLOT01_SFT 6 +#define RT5665_I2S1_2_DS_ADC_SLOT23_SFT 4 +#define RT5665_I2S1_2_DS_ADC_SLOT45_SFT 2 +#define RT5665_I2S1_2_DS_ADC_SLOT67_SFT 0 + +/* TDM control 3/4 (0x007a) (0x007b) */ +#define RT5665_IF1_ADC1_SEL_SFT 10 +#define RT5665_IF1_ADC2_SEL_SFT 9 +#define RT5665_IF1_ADC3_SEL_SFT 8 +#define RT5665_IF1_ADC4_SEL_SFT 7 +#define RT5665_TDM_ADC_SEL_SFT 0 +#define RT5665_TDM_ADC_CTRL_MASK (0x1f << 0) +#define RT5665_TDM_ADC_DATA_06 (0x6 << 0) + +/* Global Clock Control (0x0080) */ +#define RT5665_SCLK_SRC_MASK (0x3 << 14) +#define RT5665_SCLK_SRC_SFT 14 +#define RT5665_SCLK_SRC_MCLK (0x0 << 14) +#define RT5665_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5665_SCLK_SRC_RCCLK (0x2 << 14) +#define RT5665_PLL1_SRC_MASK (0x7 << 8) +#define RT5665_PLL1_SRC_SFT 8 +#define RT5665_PLL1_SRC_MCLK (0x0 << 8) +#define RT5665_PLL1_SRC_BCLK1 (0x1 << 8) +#define RT5665_PLL1_SRC_BCLK2 (0x2 << 8) +#define RT5665_PLL1_SRC_BCLK3 (0x3 << 8) +#define RT5665_PLL1_PD_MASK (0x7 << 4) +#define RT5665_PLL1_PD_SFT 4 + + +#define RT5665_PLL_INP_MAX 40000000 +#define RT5665_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5665_PLL_N_MAX 0x001ff +#define RT5665_PLL_N_MASK (RT5665_PLL_N_MAX << 7) +#define RT5665_PLL_N_SFT 7 +#define RT5665_PLL_K_MAX 0x001f +#define RT5665_PLL_K_MASK (RT5665_PLL_K_MAX) +#define RT5665_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5665_PLL_M_MAX 0x00f +#define RT5665_PLL_M_MASK (RT5665_PLL_M_MAX << 12) +#define RT5665_PLL_M_SFT 12 +#define RT5665_PLL_M_BP (0x1 << 11) +#define RT5665_PLL_M_BP_SFT 11 +#define RT5665_PLL_K_BP (0x1 << 10) +#define RT5665_PLL_K_BP_SFT 10 + +/* PLL tracking mode 1 (0x0083) */ +#define RT5665_I2S3_ASRC_MASK (0x1 << 15) +#define RT5665_I2S3_ASRC_SFT 15 +#define RT5665_I2S2_ASRC_MASK (0x1 << 14) +#define RT5665_I2S2_ASRC_SFT 14 +#define RT5665_I2S1_ASRC_MASK (0x1 << 13) +#define RT5665_I2S1_ASRC_SFT 13 +#define RT5665_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5665_DAC_STO1_ASRC_SFT 12 +#define RT5665_DAC_STO2_ASRC_MASK (0x1 << 11) +#define RT5665_DAC_STO2_ASRC_SFT 11 +#define RT5665_DAC_MONO_L_ASRC_MASK (0x1 << 10) +#define RT5665_DAC_MONO_L_ASRC_SFT 10 +#define RT5665_DAC_MONO_R_ASRC_MASK (0x1 << 9) +#define RT5665_DAC_MONO_R_ASRC_SFT 9 +#define RT5665_DMIC_STO1_ASRC_MASK (0x1 << 8) +#define RT5665_DMIC_STO1_ASRC_SFT 8 +#define RT5665_DMIC_STO2_ASRC_MASK (0x1 << 7) +#define RT5665_DMIC_STO2_ASRC_SFT 7 +#define RT5665_DMIC_MONO_L_ASRC_MASK (0x1 << 6) +#define RT5665_DMIC_MONO_L_ASRC_SFT 6 +#define RT5665_DMIC_MONO_R_ASRC_MASK (0x1 << 5) +#define RT5665_DMIC_MONO_R_ASRC_SFT 5 +#define RT5665_ADC_STO1_ASRC_MASK (0x1 << 4) +#define RT5665_ADC_STO1_ASRC_SFT 4 +#define RT5665_ADC_STO2_ASRC_MASK (0x1 << 3) +#define RT5665_ADC_STO2_ASRC_SFT 3 +#define RT5665_ADC_MONO_L_ASRC_MASK (0x1 << 2) +#define RT5665_ADC_MONO_L_ASRC_SFT 2 +#define RT5665_ADC_MONO_R_ASRC_MASK (0x1 << 1) +#define RT5665_ADC_MONO_R_ASRC_SFT 1 + +/* PLL tracking mode 2 (0x0084)*/ +#define RT5665_DA_STO1_CLK_SEL_MASK (0x7 << 12) +#define RT5665_DA_STO1_CLK_SEL_SFT 12 +#define RT5665_DA_STO2_CLK_SEL_MASK (0x7 << 8) +#define RT5665_DA_STO2_CLK_SEL_SFT 8 +#define RT5665_DA_MONOL_CLK_SEL_MASK (0x7 << 4) +#define RT5665_DA_MONOL_CLK_SEL_SFT 4 +#define RT5665_DA_MONOR_CLK_SEL_MASK (0x7) +#define RT5665_DA_MONOR_CLK_SEL_SFT 0 + +/* PLL tracking mode 3 (0x0085)*/ +#define RT5665_AD_STO1_CLK_SEL_MASK (0x7 << 12) +#define RT5665_AD_STO1_CLK_SEL_SFT 12 +#define RT5665_AD_STO2_CLK_SEL_MASK (0x7 << 8) +#define RT5665_AD_STO2_CLK_SEL_SFT 8 +#define RT5665_AD_MONOL_CLK_SEL_MASK (0x7 << 4) +#define RT5665_AD_MONOL_CLK_SEL_SFT 4 +#define RT5665_AD_MONOR_CLK_SEL_MASK (0x7) +#define RT5665_AD_MONOR_CLK_SEL_SFT 0 + +/* ASRC Control 4 (0x0086) */ +#define RT5665_I2S1_RATE_MASK (0xf << 12) +#define RT5665_I2S1_RATE_SFT 12 +#define RT5665_I2S2_RATE_MASK (0xf << 8) +#define RT5665_I2S2_RATE_SFT 8 +#define RT5665_I2S3_RATE_MASK (0xf << 4) +#define RT5665_I2S3_RATE_SFT 4 + +/* Depop Mode Control 1 (0x008e) */ +#define RT5665_PUMP_EN (0x1 << 3) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5665_DEPOP_MASK (0x1 << 13) +#define RT5665_DEPOP_SFT 13 +#define RT5665_DEPOP_AUTO (0x0 << 13) +#define RT5665_DEPOP_MAN (0x1 << 13) +#define RT5665_RAMP_MASK (0x1 << 12) +#define RT5665_RAMP_SFT 12 +#define RT5665_RAMP_DIS (0x0 << 12) +#define RT5665_RAMP_EN (0x1 << 12) +#define RT5665_BPS_MASK (0x1 << 11) +#define RT5665_BPS_SFT 11 +#define RT5665_BPS_DIS (0x0 << 11) +#define RT5665_BPS_EN (0x1 << 11) +#define RT5665_FAST_UPDN_MASK (0x1 << 10) +#define RT5665_FAST_UPDN_SFT 10 +#define RT5665_FAST_UPDN_DIS (0x0 << 10) +#define RT5665_FAST_UPDN_EN (0x1 << 10) +#define RT5665_MRES_MASK (0x3 << 8) +#define RT5665_MRES_SFT 8 +#define RT5665_MRES_15MO (0x0 << 8) +#define RT5665_MRES_25MO (0x1 << 8) +#define RT5665_MRES_35MO (0x2 << 8) +#define RT5665_MRES_45MO (0x3 << 8) +#define RT5665_VLO_MASK (0x1 << 7) +#define RT5665_VLO_SFT 7 +#define RT5665_VLO_3V (0x0 << 7) +#define RT5665_VLO_32V (0x1 << 7) +#define RT5665_DIG_DP_MASK (0x1 << 6) +#define RT5665_DIG_DP_SFT 6 +#define RT5665_DIG_DP_DIS (0x0 << 6) +#define RT5665_DIG_DP_EN (0x1 << 6) +#define RT5665_DP_TH_MASK (0x3 << 4) +#define RT5665_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5665_CP_SYS_MASK (0x7 << 12) +#define RT5665_CP_SYS_SFT 12 +#define RT5665_CP_FQ1_MASK (0x7 << 8) +#define RT5665_CP_FQ1_SFT 8 +#define RT5665_CP_FQ2_MASK (0x7 << 4) +#define RT5665_CP_FQ2_SFT 4 +#define RT5665_CP_FQ3_MASK (0x7) +#define RT5665_CP_FQ3_SFT 0 +#define RT5665_CP_FQ_1_5_KHZ 0 +#define RT5665_CP_FQ_3_KHZ 1 +#define RT5665_CP_FQ_6_KHZ 2 +#define RT5665_CP_FQ_12_KHZ 3 +#define RT5665_CP_FQ_24_KHZ 4 +#define RT5665_CP_FQ_48_KHZ 5 +#define RT5665_CP_FQ_96_KHZ 6 +#define RT5665_CP_FQ_192_KHZ 7 + +/* HPOUT charge pump 1 (0x0091) */ +#define RT5665_OSW_L_MASK (0x1 << 11) +#define RT5665_OSW_L_SFT 11 +#define RT5665_OSW_L_DIS (0x0 << 11) +#define RT5665_OSW_L_EN (0x1 << 11) +#define RT5665_OSW_R_MASK (0x1 << 10) +#define RT5665_OSW_R_SFT 10 +#define RT5665_OSW_R_DIS (0x0 << 10) +#define RT5665_OSW_R_EN (0x1 << 10) +#define RT5665_PM_HP_MASK (0x3 << 8) +#define RT5665_PM_HP_SFT 8 +#define RT5665_PM_HP_LV (0x0 << 8) +#define RT5665_PM_HP_MV (0x1 << 8) +#define RT5665_PM_HP_HV (0x2 << 8) +#define RT5665_IB_HP_MASK (0x3 << 6) +#define RT5665_IB_HP_SFT 6 +#define RT5665_IB_HP_125IL (0x0 << 6) +#define RT5665_IB_HP_25IL (0x1 << 6) +#define RT5665_IB_HP_5IL (0x2 << 6) +#define RT5665_IB_HP_1IL (0x3 << 6) + +/* PV detection and SPK gain control (0x92) */ +#define RT5665_PVDD_DET_MASK (0x1 << 15) +#define RT5665_PVDD_DET_SFT 15 +#define RT5665_PVDD_DET_DIS (0x0 << 15) +#define RT5665_PVDD_DET_EN (0x1 << 15) +#define RT5665_SPK_AG_MASK (0x1 << 14) +#define RT5665_SPK_AG_SFT 14 +#define RT5665_SPK_AG_DIS (0x0 << 14) +#define RT5665_SPK_AG_EN (0x1 << 14) + +/* Micbias Control1 (0x93) */ +#define RT5665_MIC1_BS_MASK (0x1 << 15) +#define RT5665_MIC1_BS_SFT 15 +#define RT5665_MIC1_BS_9AV (0x0 << 15) +#define RT5665_MIC1_BS_75AV (0x1 << 15) +#define RT5665_MIC2_BS_MASK (0x1 << 14) +#define RT5665_MIC2_BS_SFT 14 +#define RT5665_MIC2_BS_9AV (0x0 << 14) +#define RT5665_MIC2_BS_75AV (0x1 << 14) +#define RT5665_MIC1_CLK_MASK (0x1 << 13) +#define RT5665_MIC1_CLK_SFT 13 +#define RT5665_MIC1_CLK_DIS (0x0 << 13) +#define RT5665_MIC1_CLK_EN (0x1 << 13) +#define RT5665_MIC2_CLK_MASK (0x1 << 12) +#define RT5665_MIC2_CLK_SFT 12 +#define RT5665_MIC2_CLK_DIS (0x0 << 12) +#define RT5665_MIC2_CLK_EN (0x1 << 12) +#define RT5665_MIC1_OVCD_MASK (0x1 << 11) +#define RT5665_MIC1_OVCD_SFT 11 +#define RT5665_MIC1_OVCD_DIS (0x0 << 11) +#define RT5665_MIC1_OVCD_EN (0x1 << 11) +#define RT5665_MIC1_OVTH_MASK (0x3 << 9) +#define RT5665_MIC1_OVTH_SFT 9 +#define RT5665_MIC1_OVTH_600UA (0x0 << 9) +#define RT5665_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5665_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5665_MIC2_OVCD_MASK (0x1 << 8) +#define RT5665_MIC2_OVCD_SFT 8 +#define RT5665_MIC2_OVCD_DIS (0x0 << 8) +#define RT5665_MIC2_OVCD_EN (0x1 << 8) +#define RT5665_MIC2_OVTH_MASK (0x3 << 6) +#define RT5665_MIC2_OVTH_SFT 6 +#define RT5665_MIC2_OVTH_600UA (0x0 << 6) +#define RT5665_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5665_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5665_PWR_MB_MASK (0x1 << 5) +#define RT5665_PWR_MB_SFT 5 +#define RT5665_PWR_MB_PD (0x0 << 5) +#define RT5665_PWR_MB_PU (0x1 << 5) + +/* Micbias Control2 (0x94) */ +#define RT5665_PWR_CLK25M_MASK (0x1 << 9) +#define RT5665_PWR_CLK25M_SFT 9 +#define RT5665_PWR_CLK25M_PD (0x0 << 9) +#define RT5665_PWR_CLK25M_PU (0x1 << 9) +#define RT5665_PWR_CLK1M_MASK (0x1 << 8) +#define RT5665_PWR_CLK1M_SFT 8 +#define RT5665_PWR_CLK1M_PD (0x0 << 8) +#define RT5665_PWR_CLK1M_PU (0x1 << 8) + +/* I2S Master Mode Clock Control 1 (0x00a0) */ +#define RT5665_CLK_SRC_MCLK (0x0) +#define RT5665_CLK_SRC_PLL1 (0x1) +#define RT5665_CLK_SRC_RCCLK (0x2) +#define RT5665_I2S_PD_1 (0x0) +#define RT5665_I2S_PD_2 (0x1) +#define RT5665_I2S_PD_3 (0x2) +#define RT5665_I2S_PD_4 (0x3) +#define RT5665_I2S_PD_6 (0x4) +#define RT5665_I2S_PD_8 (0x5) +#define RT5665_I2S_PD_12 (0x6) +#define RT5665_I2S_PD_16 (0x7) +#define RT5665_I2S2_SRC_MASK (0x3 << 12) +#define RT5665_I2S2_SRC_SFT 12 +#define RT5665_I2S2_M_PD_MASK (0x7 << 8) +#define RT5665_I2S2_M_PD_SFT 8 +#define RT5665_I2S3_SRC_MASK (0x3 << 4) +#define RT5665_I2S3_SRC_SFT 4 +#define RT5665_I2S3_M_PD_MASK (0x7 << 0) +#define RT5665_I2S3_M_PD_SFT 0 + + +/* EQ Control 1 (0x00b0) */ +#define RT5665_EQ_SRC_DAC (0x0 << 15) +#define RT5665_EQ_SRC_ADC (0x1 << 15) +#define RT5665_EQ_UPD (0x1 << 14) +#define RT5665_EQ_UPD_BIT 14 +#define RT5665_EQ_CD_MASK (0x1 << 13) +#define RT5665_EQ_CD_SFT 13 +#define RT5665_EQ_CD_DIS (0x0 << 13) +#define RT5665_EQ_CD_EN (0x1 << 13) +#define RT5665_EQ_DITH_MASK (0x3 << 8) +#define RT5665_EQ_DITH_SFT 8 +#define RT5665_EQ_DITH_NOR (0x0 << 8) +#define RT5665_EQ_DITH_LSB (0x1 << 8) +#define RT5665_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5665_EQ_DITH_LSB_2 (0x3 << 8) + +/* IRQ Control 1 (0x00b7) */ +#define RT5665_JD1_1_EN_MASK (0x1 << 15) +#define RT5665_JD1_1_EN_SFT 15 +#define RT5665_JD1_1_DIS (0x0 << 15) +#define RT5665_JD1_1_EN (0x1 << 15) +#define RT5665_JD1_2_EN_MASK (0x1 << 12) +#define RT5665_JD1_2_EN_SFT 12 +#define RT5665_JD1_2_DIS (0x0 << 12) +#define RT5665_JD1_2_EN (0x1 << 12) + +/* IRQ Control 2 (0x00b8) */ +#define RT5665_IL_IRQ_MASK (0x1 << 6) +#define RT5665_IL_IRQ_DIS (0x0 << 6) +#define RT5665_IL_IRQ_EN (0x1 << 6) + +/* IRQ Control 5 (0x00ba) */ +#define RT5665_IRQ_JD_EN (0x1 << 3) +#define RT5665_IRQ_JD_EN_SFT 3 + +/* GPIO Control 1 (0x00c0) */ +#define RT5665_GP1_PIN_MASK (0x1 << 15) +#define RT5665_GP1_PIN_SFT 15 +#define RT5665_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5665_GP1_PIN_IRQ (0x1 << 15) +#define RT5665_GP2_PIN_MASK (0x3 << 13) +#define RT5665_GP2_PIN_SFT 13 +#define RT5665_GP2_PIN_GPIO2 (0x0 << 13) +#define RT5665_GP2_PIN_BCLK2 (0x1 << 13) +#define RT5665_GP2_PIN_PDM_SCL (0x2 << 13) +#define RT5665_GP3_PIN_MASK (0x3 << 11) +#define RT5665_GP3_PIN_SFT 11 +#define RT5665_GP3_PIN_GPIO3 (0x0 << 11) +#define RT5665_GP3_PIN_LRCK2 (0x1 << 11) +#define RT5665_GP3_PIN_PDM_SDA (0x2 << 11) +#define RT5665_GP4_PIN_MASK (0x3 << 9) +#define RT5665_GP4_PIN_SFT 9 +#define RT5665_GP4_PIN_GPIO4 (0x0 << 9) +#define RT5665_GP4_PIN_DACDAT2_1 (0x1 << 9) +#define RT5665_GP4_PIN_DMIC1_SDA (0x2 << 9) +#define RT5665_GP5_PIN_MASK (0x3 << 7) +#define RT5665_GP5_PIN_SFT 7 +#define RT5665_GP5_PIN_GPIO5 (0x0 << 7) +#define RT5665_GP5_PIN_ADCDAT2_1 (0x1 << 7) +#define RT5665_GP5_PIN_DMIC2_SDA (0x2 << 7) +#define RT5665_GP6_PIN_MASK (0x3 << 5) +#define RT5665_GP6_PIN_SFT 5 +#define RT5665_GP6_PIN_GPIO6 (0x0 << 5) +#define RT5665_GP6_PIN_BCLK3 (0x1 << 5) +#define RT5665_GP6_PIN_PDM_SCL (0x2 << 5) +#define RT5665_GP7_PIN_MASK (0x3 << 3) +#define RT5665_GP7_PIN_SFT 3 +#define RT5665_GP7_PIN_GPIO7 (0x0 << 3) +#define RT5665_GP7_PIN_LRCK3 (0x1 << 3) +#define RT5665_GP7_PIN_PDM_SDA (0x2 << 3) +#define RT5665_GP8_PIN_MASK (0x3 << 1) +#define RT5665_GP8_PIN_SFT 1 +#define RT5665_GP8_PIN_GPIO8 (0x0 << 1) +#define RT5665_GP8_PIN_DACDAT3 (0x1 << 1) +#define RT5665_GP8_PIN_DMIC2_SCL (0x2 << 1) +#define RT5665_GP8_PIN_DACDAT2_2 (0x3 << 1) + + +/* GPIO Control 2 (0x00c1)*/ +#define RT5665_GP9_PIN_MASK (0x3 << 14) +#define RT5665_GP9_PIN_SFT 14 +#define RT5665_GP9_PIN_GPIO9 (0x0 << 14) +#define RT5665_GP9_PIN_ADCDAT3 (0x1 << 14) +#define RT5665_GP9_PIN_DMIC1_SCL (0x2 << 14) +#define RT5665_GP9_PIN_ADCDAT2_2 (0x3 << 14) +#define RT5665_GP10_PIN_MASK (0x3 << 12) +#define RT5665_GP10_PIN_SFT 12 +#define RT5665_GP10_PIN_GPIO10 (0x0 << 12) +#define RT5665_GP10_PIN_ADCDAT1_2 (0x1 << 12) +#define RT5665_GP10_PIN_LPD (0x2 << 12) +#define RT5665_GP1_PF_MASK (0x1 << 11) +#define RT5665_GP1_PF_IN (0x0 << 11) +#define RT5665_GP1_PF_OUT (0x1 << 11) +#define RT5665_GP1_OUT_MASK (0x1 << 10) +#define RT5665_GP1_OUT_H (0x0 << 10) +#define RT5665_GP1_OUT_L (0x1 << 10) +#define RT5665_GP2_PF_MASK (0x1 << 9) +#define RT5665_GP2_PF_IN (0x0 << 9) +#define RT5665_GP2_PF_OUT (0x1 << 9) +#define RT5665_GP2_OUT_MASK (0x1 << 8) +#define RT5665_GP2_OUT_H (0x0 << 8) +#define RT5665_GP2_OUT_L (0x1 << 8) +#define RT5665_GP3_PF_MASK (0x1 << 7) +#define RT5665_GP3_PF_IN (0x0 << 7) +#define RT5665_GP3_PF_OUT (0x1 << 7) +#define RT5665_GP3_OUT_MASK (0x1 << 6) +#define RT5665_GP3_OUT_H (0x0 << 6) +#define RT5665_GP3_OUT_L (0x1 << 6) +#define RT5665_GP4_PF_MASK (0x1 << 5) +#define RT5665_GP4_PF_IN (0x0 << 5) +#define RT5665_GP4_PF_OUT (0x1 << 5) +#define RT5665_GP4_OUT_MASK (0x1 << 4) +#define RT5665_GP4_OUT_H (0x0 << 4) +#define RT5665_GP4_OUT_L (0x1 << 4) +#define RT5665_GP5_PF_MASK (0x1 << 3) +#define RT5665_GP5_PF_IN (0x0 << 3) +#define RT5665_GP5_PF_OUT (0x1 << 3) +#define RT5665_GP5_OUT_MASK (0x1 << 2) +#define RT5665_GP5_OUT_H (0x0 << 2) +#define RT5665_GP5_OUT_L (0x1 << 2) +#define RT5665_GP6_PF_MASK (0x1 << 1) +#define RT5665_GP6_PF_IN (0x0 << 1) +#define RT5665_GP6_PF_OUT (0x1 << 1) +#define RT5665_GP6_OUT_MASK (0x1) +#define RT5665_GP6_OUT_H (0x0) +#define RT5665_GP6_OUT_L (0x1) + + +/* GPIO Control 3 (0x00c2) */ +#define RT5665_GP7_PF_MASK (0x1 << 15) +#define RT5665_GP7_PF_IN (0x0 << 15) +#define RT5665_GP7_PF_OUT (0x1 << 15) +#define RT5665_GP7_OUT_MASK (0x1 << 14) +#define RT5665_GP7_OUT_H (0x0 << 14) +#define RT5665_GP7_OUT_L (0x1 << 14) +#define RT5665_GP8_PF_MASK (0x1 << 13) +#define RT5665_GP8_PF_IN (0x0 << 13) +#define RT5665_GP8_PF_OUT (0x1 << 13) +#define RT5665_GP8_OUT_MASK (0x1 << 12) +#define RT5665_GP8_OUT_H (0x0 << 12) +#define RT5665_GP8_OUT_L (0x1 << 12) +#define RT5665_GP9_PF_MASK (0x1 << 11) +#define RT5665_GP9_PF_IN (0x0 << 11) +#define RT5665_GP9_PF_OUT (0x1 << 11) +#define RT5665_GP9_OUT_MASK (0x1 << 10) +#define RT5665_GP9_OUT_H (0x0 << 10) +#define RT5665_GP9_OUT_L (0x1 << 10) +#define RT5665_GP10_PF_MASK (0x1 << 9) +#define RT5665_GP10_PF_IN (0x0 << 9) +#define RT5665_GP10_PF_OUT (0x1 << 9) +#define RT5665_GP10_OUT_MASK (0x1 << 8) +#define RT5665_GP10_OUT_H (0x0 << 8) +#define RT5665_GP10_OUT_L (0x1 << 8) +#define RT5665_GP11_PF_MASK (0x1 << 7) +#define RT5665_GP11_PF_IN (0x0 << 7) +#define RT5665_GP11_PF_OUT (0x1 << 7) +#define RT5665_GP11_OUT_MASK (0x1 << 6) +#define RT5665_GP11_OUT_H (0x0 << 6) +#define RT5665_GP11_OUT_L (0x1 << 6) + +/* Soft volume and zero cross control 1 (0x00d9) */ +#define RT5665_SV_MASK (0x1 << 15) +#define RT5665_SV_SFT 15 +#define RT5665_SV_DIS (0x0 << 15) +#define RT5665_SV_EN (0x1 << 15) +#define RT5665_OUT_SV_MASK (0x1 << 13) +#define RT5665_OUT_SV_SFT 13 +#define RT5665_OUT_SV_DIS (0x0 << 13) +#define RT5665_OUT_SV_EN (0x1 << 13) +#define RT5665_HP_SV_MASK (0x1 << 12) +#define RT5665_HP_SV_SFT 12 +#define RT5665_HP_SV_DIS (0x0 << 12) +#define RT5665_HP_SV_EN (0x1 << 12) +#define RT5665_ZCD_DIG_MASK (0x1 << 11) +#define RT5665_ZCD_DIG_SFT 11 +#define RT5665_ZCD_DIG_DIS (0x0 << 11) +#define RT5665_ZCD_DIG_EN (0x1 << 11) +#define RT5665_ZCD_MASK (0x1 << 10) +#define RT5665_ZCD_SFT 10 +#define RT5665_ZCD_PD (0x0 << 10) +#define RT5665_ZCD_PU (0x1 << 10) +#define RT5665_SV_DLY_MASK (0xf) +#define RT5665_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0x00da) */ +#define RT5665_ZCD_HP_MASK (0x1 << 15) +#define RT5665_ZCD_HP_SFT 15 +#define RT5665_ZCD_HP_DIS (0x0 << 15) +#define RT5665_ZCD_HP_EN (0x1 << 15) + +/* 4 Button Inline Command Control 2 (0x00e0) */ +#define RT5665_4BTN_IL_MASK (0x1 << 15) +#define RT5665_4BTN_IL_EN (0x1 << 15) +#define RT5665_4BTN_IL_DIS (0x0 << 15) +#define RT5665_4BTN_IL_RST_MASK (0x1 << 14) +#define RT5665_4BTN_IL_NOR (0x1 << 14) +#define RT5665_4BTN_IL_RST (0x0 << 14) + +/* Analog JD Control 1 (0x00f0) */ +#define RT5665_JD1_MODE_MASK (0x3 << 0) +#define RT5665_JD1_MODE_0 (0x0 << 0) +#define RT5665_JD1_MODE_1 (0x1 << 0) +#define RT5665_JD1_MODE_2 (0x2 << 0) + +/* Jack Detect Control 3 (0x00f8) */ +#define RT5665_JD_TRI_HPO_SEL_MASK (0x7) +#define RT5665_JD_TRI_HPO_SEL_SFT (0) +#define RT5665_JD_HPO_GPIO_JD1 (0x0) +#define RT5665_JD_HPO_JD1_1 (0x1) +#define RT5665_JD_HPO_JD1_2 (0x2) +#define RT5665_JD_HPO_JD2 (0x3) +#define RT5665_JD_HPO_GPIO_JD2 (0x4) +#define RT5665_JD_HPO_JD3 (0x5) +#define RT5665_JD_HPO_JD_D (0x6) + +/* Digital Misc Control (0x00fa) */ +#define RT5665_AM_MASK (0x1 << 7) +#define RT5665_AM_EN (0x1 << 7) +#define RT5665_AM_DIS (0x1 << 7) +#define RT5665_DIG_GATE_CTRL 0x1 +#define RT5665_DIG_GATE_CTRL_SFT (0) + +/* Chopper and Clock control for ADC (0x011c)*/ +#define RT5665_M_RF_DIG_MASK (0x1 << 12) +#define RT5665_M_RF_DIG_SFT 12 +#define RT5665_M_RI_DIG (0x1 << 11) + +/* Chopper and Clock control for DAC (0x013a)*/ +#define RT5665_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5665_CKXEN_DAC1_SFT 13 +#define RT5665_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5665_CKGEN_DAC1_SFT 12 +#define RT5665_CKXEN_DAC2_MASK (0x1 << 5) +#define RT5665_CKXEN_DAC2_SFT 5 +#define RT5665_CKGEN_DAC2_MASK (0x1 << 4) +#define RT5665_CKGEN_DAC2_SFT 4 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5665_CKXEN_ADC1_MASK (0x1 << 13) +#define RT5665_CKXEN_ADC1_SFT 13 +#define RT5665_CKGEN_ADC1_MASK (0x1 << 12) +#define RT5665_CKGEN_ADC1_SFT 12 +#define RT5665_CKXEN_ADC2_MASK (0x1 << 5) +#define RT5665_CKXEN_ADC2_SFT 5 +#define RT5665_CKGEN_ADC2_MASK (0x1 << 4) +#define RT5665_CKGEN_ADC2_SFT 4 + +/* Volume test (0x013f)*/ +#define RT5665_SEL_CLK_VOL_MASK (0x1 << 15) +#define RT5665_SEL_CLK_VOL_EN (0x1 << 15) +#define RT5665_SEL_CLK_VOL_DIS (0x0 << 15) + +/* Test Mode Control 1 (0x0145) */ +#define RT5665_AD2DA_LB_MASK (0x1 << 9) +#define RT5665_AD2DA_LB_SFT 9 + +/* Stereo Noise Gate Control 1 (0x0160) */ +#define RT5665_NG2_EN_MASK (0x1 << 15) +#define RT5665_NG2_EN (0x1 << 15) +#define RT5665_NG2_DIS (0x0 << 15) + +/* Stereo1 DAC Silence Detection Control (0x0190) */ +#define RT5665_DEB_STO_DAC_MASK (0x7 << 4) +#define RT5665_DEB_80_MS (0x0 << 4) + +/* SAR ADC Inline Command Control 1 (0x0210) */ +#define RT5665_SAR_BUTT_DET_MASK (0x1 << 15) +#define RT5665_SAR_BUTT_DET_EN (0x1 << 15) +#define RT5665_SAR_BUTT_DET_DIS (0x0 << 15) +#define RT5665_SAR_BUTDET_MODE_MASK (0x1 << 14) +#define RT5665_SAR_BUTDET_POW_SAV (0x1 << 14) +#define RT5665_SAR_BUTDET_POW_NORM (0x0 << 14) +#define RT5665_SAR_BUTDET_RST_MASK (0x1 << 13) +#define RT5665_SAR_BUTDET_RST_NORMAL (0x1 << 13) +#define RT5665_SAR_BUTDET_RST (0x0 << 13) +#define RT5665_SAR_POW_MASK (0x1 << 12) +#define RT5665_SAR_POW_EN (0x1 << 12) +#define RT5665_SAR_POW_DIS (0x0 << 12) +#define RT5665_SAR_RST_MASK (0x1 << 11) +#define RT5665_SAR_RST_NORMAL (0x1 << 11) +#define RT5665_SAR_RST (0x0 << 11) +#define RT5665_SAR_BYPASS_MASK (0x1 << 10) +#define RT5665_SAR_BYPASS_EN (0x1 << 10) +#define RT5665_SAR_BYPASS_DIS (0x0 << 10) +#define RT5665_SAR_SEL_MB1_MASK (0x1 << 9) +#define RT5665_SAR_SEL_MB1_SEL (0x1 << 9) +#define RT5665_SAR_SEL_MB1_NOSEL (0x0 << 9) +#define RT5665_SAR_SEL_MB2_MASK (0x1 << 8) +#define RT5665_SAR_SEL_MB2_SEL (0x1 << 8) +#define RT5665_SAR_SEL_MB2_NOSEL (0x0 << 8) +#define RT5665_SAR_SEL_MODE_MASK (0x1 << 7) +#define RT5665_SAR_SEL_MODE_CMP (0x1 << 7) +#define RT5665_SAR_SEL_MODE_ADC (0x0 << 7) +#define RT5665_SAR_SEL_MB1_MB2_MASK (0x1 << 5) +#define RT5665_SAR_SEL_MB1_MB2_AUTO (0x1 << 5) +#define RT5665_SAR_SEL_MB1_MB2_MANU (0x0 << 5) +#define RT5665_SAR_SEL_SIGNAL_MASK (0x1 << 4) +#define RT5665_SAR_SEL_SIGNAL_AUTO (0x1 << 4) +#define RT5665_SAR_SEL_SIGNAL_MANU (0x0 << 4) + +/* System Clock Source */ +enum { + RT5665_SCLK_S_MCLK, + RT5665_SCLK_S_PLL1, + RT5665_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5665_PLL1_S_MCLK, + RT5665_PLL1_S_BCLK1, + RT5665_PLL1_S_BCLK2, + RT5665_PLL1_S_BCLK3, + RT5665_PLL1_S_BCLK4, +}; + +enum { + RT5665_AIF1_1, + RT5665_AIF1_2, + RT5665_AIF2_1, + RT5665_AIF2_2, + RT5665_AIF3, + RT5665_AIFS +}; + +enum { + CODEC_5665, + CODEC_5666, +}; + +/* filter mask */ +enum { + RT5665_DA_STEREO1_FILTER = 0x1, + RT5665_DA_STEREO2_FILTER = (0x1 << 1), + RT5665_DA_MONO_L_FILTER = (0x1 << 2), + RT5665_DA_MONO_R_FILTER = (0x1 << 3), + RT5665_AD_STEREO1_FILTER = (0x1 << 4), + RT5665_AD_STEREO2_FILTER = (0x1 << 5), + RT5665_AD_MONO_L_FILTER = (0x1 << 6), + RT5665_AD_MONO_R_FILTER = (0x1 << 7), +}; + +enum { + RT5665_CLK_SEL_SYS, + RT5665_CLK_SEL_I2S1_ASRC, + RT5665_CLK_SEL_I2S2_ASRC, + RT5665_CLK_SEL_I2S3_ASRC, + RT5665_CLK_SEL_SYS2, + RT5665_CLK_SEL_SYS3, + RT5665_CLK_SEL_SYS4, +}; + +int rt5665_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +#endif /* __RT5665_H__ */ diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c new file mode 100644 index 0000000000..4623b3e624 --- /dev/null +++ b/sound/soc/codecs/rt5668.c @@ -0,0 +1,2628 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5668.c -- RT5668B ALSA SoC audio component driver + * + * Copyright 2018 Realtek Semiconductor Corp. + * Author: Bard Liao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5668.h" + +#define RT5668_NUM_SUPPLIES 3 + +static const char *rt5668_supply_names[RT5668_NUM_SUPPLIES] = { + "AVDD", + "MICVDD", + "VBAT", +}; + +struct rt5668_priv { + struct snd_soc_component *component; + struct rt5668_platform_data pdata; + struct gpio_desc *ldo1_en; + struct regmap *regmap; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5668_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work jd_check_work; + struct mutex calibrate_mutex; + + int sysclk; + int sysclk_src; + int lrck[RT5668_AIFS]; + int bclk[RT5668_AIFS]; + int master[RT5668_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int jack_type; +}; + +static const struct reg_default rt5668_reg[] = { + {0x0002, 0x8080}, + {0x0003, 0x8000}, + {0x0005, 0x0000}, + {0x0006, 0x0000}, + {0x0008, 0x800f}, + {0x000b, 0x0000}, + {0x0010, 0x4040}, + {0x0011, 0x0000}, + {0x0012, 0x1404}, + {0x0013, 0x1000}, + {0x0014, 0xa00a}, + {0x0015, 0x0404}, + {0x0016, 0x0404}, + {0x0019, 0xafaf}, + {0x001c, 0x2f2f}, + {0x001f, 0x0000}, + {0x0022, 0x5757}, + {0x0023, 0x0039}, + {0x0024, 0x000b}, + {0x0026, 0xc0c4}, + {0x0029, 0x8080}, + {0x002a, 0xa0a0}, + {0x002b, 0x0300}, + {0x0030, 0x0000}, + {0x003c, 0x0080}, + {0x0044, 0x0c0c}, + {0x0049, 0x0000}, + {0x0061, 0x0000}, + {0x0062, 0x0000}, + {0x0063, 0x003f}, + {0x0064, 0x0000}, + {0x0065, 0x0000}, + {0x0066, 0x0030}, + {0x0067, 0x0000}, + {0x006b, 0x0000}, + {0x006c, 0x0000}, + {0x006d, 0x2200}, + {0x006e, 0x0a10}, + {0x0070, 0x8000}, + {0x0071, 0x8000}, + {0x0073, 0x0000}, + {0x0074, 0x0000}, + {0x0075, 0x0002}, + {0x0076, 0x0001}, + {0x0079, 0x0000}, + {0x007a, 0x0000}, + {0x007b, 0x0000}, + {0x007c, 0x0100}, + {0x007e, 0x0000}, + {0x0080, 0x0000}, + {0x0081, 0x0000}, + {0x0082, 0x0000}, + {0x0083, 0x0000}, + {0x0084, 0x0000}, + {0x0085, 0x0000}, + {0x0086, 0x0005}, + {0x0087, 0x0000}, + {0x0088, 0x0000}, + {0x008c, 0x0003}, + {0x008d, 0x0000}, + {0x008e, 0x0060}, + {0x008f, 0x1000}, + {0x0091, 0x0c26}, + {0x0092, 0x0073}, + {0x0093, 0x0000}, + {0x0094, 0x0080}, + {0x0098, 0x0000}, + {0x009a, 0x0000}, + {0x009b, 0x0000}, + {0x009c, 0x0000}, + {0x009d, 0x0000}, + {0x009e, 0x100c}, + {0x009f, 0x0000}, + {0x00a0, 0x0000}, + {0x00a3, 0x0002}, + {0x00a4, 0x0001}, + {0x00ae, 0x2040}, + {0x00af, 0x0000}, + {0x00b6, 0x0000}, + {0x00b7, 0x0000}, + {0x00b8, 0x0000}, + {0x00b9, 0x0002}, + {0x00be, 0x0000}, + {0x00c0, 0x0160}, + {0x00c1, 0x82a0}, + {0x00c2, 0x0000}, + {0x00d0, 0x0000}, + {0x00d1, 0x2244}, + {0x00d2, 0x3300}, + {0x00d3, 0x2200}, + {0x00d4, 0x0000}, + {0x00d9, 0x0009}, + {0x00da, 0x0000}, + {0x00db, 0x0000}, + {0x00dc, 0x00c0}, + {0x00dd, 0x2220}, + {0x00de, 0x3131}, + {0x00df, 0x3131}, + {0x00e0, 0x3131}, + {0x00e2, 0x0000}, + {0x00e3, 0x4000}, + {0x00e4, 0x0aa0}, + {0x00e5, 0x3131}, + {0x00e6, 0x3131}, + {0x00e7, 0x3131}, + {0x00e8, 0x3131}, + {0x00ea, 0xb320}, + {0x00eb, 0x0000}, + {0x00f0, 0x0000}, + {0x00f1, 0x00d0}, + {0x00f2, 0x00d0}, + {0x00f6, 0x0000}, + {0x00fa, 0x0000}, + {0x00fb, 0x0000}, + {0x00fc, 0x0000}, + {0x00fd, 0x0000}, + {0x00fe, 0x10ec}, + {0x00ff, 0x6530}, + {0x0100, 0xa0a0}, + {0x010b, 0x0000}, + {0x010c, 0xae00}, + {0x010d, 0xaaa0}, + {0x010e, 0x8aa2}, + {0x010f, 0x02a2}, + {0x0110, 0xc000}, + {0x0111, 0x04a2}, + {0x0112, 0x2800}, + {0x0113, 0x0000}, + {0x0117, 0x0100}, + {0x0125, 0x0410}, + {0x0132, 0x6026}, + {0x0136, 0x5555}, + {0x0138, 0x3700}, + {0x013a, 0x2000}, + {0x013b, 0x2000}, + {0x013c, 0x2005}, + {0x013f, 0x0000}, + {0x0142, 0x0000}, + {0x0145, 0x0002}, + {0x0146, 0x0000}, + {0x0147, 0x0000}, + {0x0148, 0x0000}, + {0x0149, 0x0000}, + {0x0150, 0x79a1}, + {0x0151, 0x0000}, + {0x0160, 0x4ec0}, + {0x0161, 0x0080}, + {0x0162, 0x0200}, + {0x0163, 0x0800}, + {0x0164, 0x0000}, + {0x0165, 0x0000}, + {0x0166, 0x0000}, + {0x0167, 0x000f}, + {0x0168, 0x000f}, + {0x0169, 0x0021}, + {0x0190, 0x413d}, + {0x0194, 0x0000}, + {0x0195, 0x0000}, + {0x0197, 0x0022}, + {0x0198, 0x0000}, + {0x0199, 0x0000}, + {0x01af, 0x0000}, + {0x01b0, 0x0400}, + {0x01b1, 0x0000}, + {0x01b2, 0x0000}, + {0x01b3, 0x0000}, + {0x01b4, 0x0000}, + {0x01b5, 0x0000}, + {0x01b6, 0x01c3}, + {0x01b7, 0x02a0}, + {0x01b8, 0x03e9}, + {0x01b9, 0x1389}, + {0x01ba, 0xc351}, + {0x01bb, 0x0009}, + {0x01bc, 0x0018}, + {0x01bd, 0x002a}, + {0x01be, 0x004c}, + {0x01bf, 0x0097}, + {0x01c0, 0x433d}, + {0x01c1, 0x2800}, + {0x01c2, 0x0000}, + {0x01c3, 0x0000}, + {0x01c4, 0x0000}, + {0x01c5, 0x0000}, + {0x01c6, 0x0000}, + {0x01c7, 0x0000}, + {0x01c8, 0x40af}, + {0x01c9, 0x0702}, + {0x01ca, 0x0000}, + {0x01cb, 0x0000}, + {0x01cc, 0x5757}, + {0x01cd, 0x5757}, + {0x01ce, 0x5757}, + {0x01cf, 0x5757}, + {0x01d0, 0x5757}, + {0x01d1, 0x5757}, + {0x01d2, 0x5757}, + {0x01d3, 0x5757}, + {0x01d4, 0x5757}, + {0x01d5, 0x5757}, + {0x01d6, 0x0000}, + {0x01d7, 0x0008}, + {0x01d8, 0x0029}, + {0x01d9, 0x3333}, + {0x01da, 0x0000}, + {0x01db, 0x0004}, + {0x01dc, 0x0000}, + {0x01de, 0x7c00}, + {0x01df, 0x0320}, + {0x01e0, 0x06a1}, + {0x01e1, 0x0000}, + {0x01e2, 0x0000}, + {0x01e3, 0x0000}, + {0x01e4, 0x0000}, + {0x01e6, 0x0001}, + {0x01e7, 0x0000}, + {0x01e8, 0x0000}, + {0x01ea, 0x0000}, + {0x01eb, 0x0000}, + {0x01ec, 0x0000}, + {0x01ed, 0x0000}, + {0x01ee, 0x0000}, + {0x01ef, 0x0000}, + {0x01f0, 0x0000}, + {0x01f1, 0x0000}, + {0x01f2, 0x0000}, + {0x01f3, 0x0000}, + {0x01f4, 0x0000}, + {0x0210, 0x6297}, + {0x0211, 0xa005}, + {0x0212, 0x824c}, + {0x0213, 0xf7ff}, + {0x0214, 0xf24c}, + {0x0215, 0x0102}, + {0x0216, 0x00a3}, + {0x0217, 0x0048}, + {0x0218, 0xa2c0}, + {0x0219, 0x0400}, + {0x021a, 0x00c8}, + {0x021b, 0x00c0}, + {0x021c, 0x0000}, + {0x0250, 0x4500}, + {0x0251, 0x40b3}, + {0x0252, 0x0000}, + {0x0253, 0x0000}, + {0x0254, 0x0000}, + {0x0255, 0x0000}, + {0x0256, 0x0000}, + {0x0257, 0x0000}, + {0x0258, 0x0000}, + {0x0259, 0x0000}, + {0x025a, 0x0005}, + {0x0270, 0x0000}, + {0x02ff, 0x0110}, + {0x0300, 0x001f}, + {0x0301, 0x032c}, + {0x0302, 0x5f21}, + {0x0303, 0x4000}, + {0x0304, 0x4000}, + {0x0305, 0x06d5}, + {0x0306, 0x8000}, + {0x0307, 0x0700}, + {0x0310, 0x4560}, + {0x0311, 0xa4a8}, + {0x0312, 0x7418}, + {0x0313, 0x0000}, + {0x0314, 0x0006}, + {0x0315, 0xffff}, + {0x0316, 0xc400}, + {0x0317, 0x0000}, + {0x03c0, 0x7e00}, + {0x03c1, 0x8000}, + {0x03c2, 0x8000}, + {0x03c3, 0x8000}, + {0x03c4, 0x8000}, + {0x03c5, 0x8000}, + {0x03c6, 0x8000}, + {0x03c7, 0x8000}, + {0x03c8, 0x8000}, + {0x03c9, 0x8000}, + {0x03ca, 0x8000}, + {0x03cb, 0x8000}, + {0x03cc, 0x8000}, + {0x03d0, 0x0000}, + {0x03d1, 0x0000}, + {0x03d2, 0x0000}, + {0x03d3, 0x0000}, + {0x03d4, 0x2000}, + {0x03d5, 0x2000}, + {0x03d6, 0x0000}, + {0x03d7, 0x0000}, + {0x03d8, 0x2000}, + {0x03d9, 0x2000}, + {0x03da, 0x2000}, + {0x03db, 0x2000}, + {0x03dc, 0x0000}, + {0x03dd, 0x0000}, + {0x03de, 0x0000}, + {0x03df, 0x2000}, + {0x03e0, 0x0000}, + {0x03e1, 0x0000}, + {0x03e2, 0x0000}, + {0x03e3, 0x0000}, + {0x03e4, 0x0000}, + {0x03e5, 0x0000}, + {0x03e6, 0x0000}, + {0x03e7, 0x0000}, + {0x03e8, 0x0000}, + {0x03e9, 0x0000}, + {0x03ea, 0x0000}, + {0x03eb, 0x0000}, + {0x03ec, 0x0000}, + {0x03ed, 0x0000}, + {0x03ee, 0x0000}, + {0x03ef, 0x0000}, + {0x03f0, 0x0800}, + {0x03f1, 0x0800}, + {0x03f2, 0x0800}, + {0x03f3, 0x0800}, +}; + +static bool rt5668_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5668_RESET: + case RT5668_CBJ_CTRL_2: + case RT5668_INT_ST_1: + case RT5668_4BTN_IL_CMD_1: + case RT5668_AJD1_CTRL: + case RT5668_HP_CALIB_CTRL_1: + case RT5668_DEVICE_ID: + case RT5668_I2C_MODE: + case RT5668_HP_CALIB_CTRL_10: + case RT5668_EFUSE_CTRL_2: + case RT5668_JD_TOP_VC_VTRL: + case RT5668_HP_IMP_SENS_CTRL_19: + case RT5668_IL_CMD_1: + case RT5668_SAR_IL_CMD_2: + case RT5668_SAR_IL_CMD_4: + case RT5668_SAR_IL_CMD_10: + case RT5668_SAR_IL_CMD_11: + case RT5668_EFUSE_CTRL_6...RT5668_EFUSE_CTRL_11: + case RT5668_HP_CALIB_STA_1...RT5668_HP_CALIB_STA_11: + return true; + default: + return false; + } +} + +static bool rt5668_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5668_RESET: + case RT5668_VERSION_ID: + case RT5668_VENDOR_ID: + case RT5668_DEVICE_ID: + case RT5668_HP_CTRL_1: + case RT5668_HP_CTRL_2: + case RT5668_HPL_GAIN: + case RT5668_HPR_GAIN: + case RT5668_I2C_CTRL: + case RT5668_CBJ_BST_CTRL: + case RT5668_CBJ_CTRL_1: + case RT5668_CBJ_CTRL_2: + case RT5668_CBJ_CTRL_3: + case RT5668_CBJ_CTRL_4: + case RT5668_CBJ_CTRL_5: + case RT5668_CBJ_CTRL_6: + case RT5668_CBJ_CTRL_7: + case RT5668_DAC1_DIG_VOL: + case RT5668_STO1_ADC_DIG_VOL: + case RT5668_STO1_ADC_BOOST: + case RT5668_HP_IMP_GAIN_1: + case RT5668_HP_IMP_GAIN_2: + case RT5668_SIDETONE_CTRL: + case RT5668_STO1_ADC_MIXER: + case RT5668_AD_DA_MIXER: + case RT5668_STO1_DAC_MIXER: + case RT5668_A_DAC1_MUX: + case RT5668_DIG_INF2_DATA: + case RT5668_REC_MIXER: + case RT5668_CAL_REC: + case RT5668_ALC_BACK_GAIN: + case RT5668_PWR_DIG_1: + case RT5668_PWR_DIG_2: + case RT5668_PWR_ANLG_1: + case RT5668_PWR_ANLG_2: + case RT5668_PWR_ANLG_3: + case RT5668_PWR_MIXER: + case RT5668_PWR_VOL: + case RT5668_CLK_DET: + case RT5668_RESET_LPF_CTRL: + case RT5668_RESET_HPF_CTRL: + case RT5668_DMIC_CTRL_1: + case RT5668_I2S1_SDP: + case RT5668_I2S2_SDP: + case RT5668_ADDA_CLK_1: + case RT5668_ADDA_CLK_2: + case RT5668_I2S1_F_DIV_CTRL_1: + case RT5668_I2S1_F_DIV_CTRL_2: + case RT5668_TDM_CTRL: + case RT5668_TDM_ADDA_CTRL_1: + case RT5668_TDM_ADDA_CTRL_2: + case RT5668_DATA_SEL_CTRL_1: + case RT5668_TDM_TCON_CTRL: + case RT5668_GLB_CLK: + case RT5668_PLL_CTRL_1: + case RT5668_PLL_CTRL_2: + case RT5668_PLL_TRACK_1: + case RT5668_PLL_TRACK_2: + case RT5668_PLL_TRACK_3: + case RT5668_PLL_TRACK_4: + case RT5668_PLL_TRACK_5: + case RT5668_PLL_TRACK_6: + case RT5668_PLL_TRACK_11: + case RT5668_SDW_REF_CLK: + case RT5668_DEPOP_1: + case RT5668_DEPOP_2: + case RT5668_HP_CHARGE_PUMP_1: + case RT5668_HP_CHARGE_PUMP_2: + case RT5668_MICBIAS_1: + case RT5668_MICBIAS_2: + case RT5668_PLL_TRACK_12: + case RT5668_PLL_TRACK_14: + case RT5668_PLL2_CTRL_1: + case RT5668_PLL2_CTRL_2: + case RT5668_PLL2_CTRL_3: + case RT5668_PLL2_CTRL_4: + case RT5668_RC_CLK_CTRL: + case RT5668_I2S_M_CLK_CTRL_1: + case RT5668_I2S2_F_DIV_CTRL_1: + case RT5668_I2S2_F_DIV_CTRL_2: + case RT5668_EQ_CTRL_1: + case RT5668_EQ_CTRL_2: + case RT5668_IRQ_CTRL_1: + case RT5668_IRQ_CTRL_2: + case RT5668_IRQ_CTRL_3: + case RT5668_IRQ_CTRL_4: + case RT5668_INT_ST_1: + case RT5668_GPIO_CTRL_1: + case RT5668_GPIO_CTRL_2: + case RT5668_GPIO_CTRL_3: + case RT5668_HP_AMP_DET_CTRL_1: + case RT5668_HP_AMP_DET_CTRL_2: + case RT5668_MID_HP_AMP_DET: + case RT5668_LOW_HP_AMP_DET: + case RT5668_DELAY_BUF_CTRL: + case RT5668_SV_ZCD_1: + case RT5668_SV_ZCD_2: + case RT5668_IL_CMD_1: + case RT5668_IL_CMD_2: + case RT5668_IL_CMD_3: + case RT5668_IL_CMD_4: + case RT5668_IL_CMD_5: + case RT5668_IL_CMD_6: + case RT5668_4BTN_IL_CMD_1: + case RT5668_4BTN_IL_CMD_2: + case RT5668_4BTN_IL_CMD_3: + case RT5668_4BTN_IL_CMD_4: + case RT5668_4BTN_IL_CMD_5: + case RT5668_4BTN_IL_CMD_6: + case RT5668_4BTN_IL_CMD_7: + case RT5668_ADC_STO1_HP_CTRL_1: + case RT5668_ADC_STO1_HP_CTRL_2: + case RT5668_AJD1_CTRL: + case RT5668_JD1_THD: + case RT5668_JD2_THD: + case RT5668_JD_CTRL_1: + case RT5668_DUMMY_1: + case RT5668_DUMMY_2: + case RT5668_DUMMY_3: + case RT5668_DAC_ADC_DIG_VOL1: + case RT5668_BIAS_CUR_CTRL_2: + case RT5668_BIAS_CUR_CTRL_3: + case RT5668_BIAS_CUR_CTRL_4: + case RT5668_BIAS_CUR_CTRL_5: + case RT5668_BIAS_CUR_CTRL_6: + case RT5668_BIAS_CUR_CTRL_7: + case RT5668_BIAS_CUR_CTRL_8: + case RT5668_BIAS_CUR_CTRL_9: + case RT5668_BIAS_CUR_CTRL_10: + case RT5668_VREF_REC_OP_FB_CAP_CTRL: + case RT5668_CHARGE_PUMP_1: + case RT5668_DIG_IN_CTRL_1: + case RT5668_PAD_DRIVING_CTRL: + case RT5668_SOFT_RAMP_DEPOP: + case RT5668_CHOP_DAC: + case RT5668_CHOP_ADC: + case RT5668_CALIB_ADC_CTRL: + case RT5668_VOL_TEST: + case RT5668_SPKVDD_DET_STA: + case RT5668_TEST_MODE_CTRL_1: + case RT5668_TEST_MODE_CTRL_2: + case RT5668_TEST_MODE_CTRL_3: + case RT5668_TEST_MODE_CTRL_4: + case RT5668_TEST_MODE_CTRL_5: + case RT5668_PLL1_INTERNAL: + case RT5668_PLL2_INTERNAL: + case RT5668_STO_NG2_CTRL_1: + case RT5668_STO_NG2_CTRL_2: + case RT5668_STO_NG2_CTRL_3: + case RT5668_STO_NG2_CTRL_4: + case RT5668_STO_NG2_CTRL_5: + case RT5668_STO_NG2_CTRL_6: + case RT5668_STO_NG2_CTRL_7: + case RT5668_STO_NG2_CTRL_8: + case RT5668_STO_NG2_CTRL_9: + case RT5668_STO_NG2_CTRL_10: + case RT5668_STO1_DAC_SIL_DET: + case RT5668_SIL_PSV_CTRL1: + case RT5668_SIL_PSV_CTRL2: + case RT5668_SIL_PSV_CTRL3: + case RT5668_SIL_PSV_CTRL4: + case RT5668_SIL_PSV_CTRL5: + case RT5668_HP_IMP_SENS_CTRL_01: + case RT5668_HP_IMP_SENS_CTRL_02: + case RT5668_HP_IMP_SENS_CTRL_03: + case RT5668_HP_IMP_SENS_CTRL_04: + case RT5668_HP_IMP_SENS_CTRL_05: + case RT5668_HP_IMP_SENS_CTRL_06: + case RT5668_HP_IMP_SENS_CTRL_07: + case RT5668_HP_IMP_SENS_CTRL_08: + case RT5668_HP_IMP_SENS_CTRL_09: + case RT5668_HP_IMP_SENS_CTRL_10: + case RT5668_HP_IMP_SENS_CTRL_11: + case RT5668_HP_IMP_SENS_CTRL_12: + case RT5668_HP_IMP_SENS_CTRL_13: + case RT5668_HP_IMP_SENS_CTRL_14: + case RT5668_HP_IMP_SENS_CTRL_15: + case RT5668_HP_IMP_SENS_CTRL_16: + case RT5668_HP_IMP_SENS_CTRL_17: + case RT5668_HP_IMP_SENS_CTRL_18: + case RT5668_HP_IMP_SENS_CTRL_19: + case RT5668_HP_IMP_SENS_CTRL_20: + case RT5668_HP_IMP_SENS_CTRL_21: + case RT5668_HP_IMP_SENS_CTRL_22: + case RT5668_HP_IMP_SENS_CTRL_23: + case RT5668_HP_IMP_SENS_CTRL_24: + case RT5668_HP_IMP_SENS_CTRL_25: + case RT5668_HP_IMP_SENS_CTRL_26: + case RT5668_HP_IMP_SENS_CTRL_27: + case RT5668_HP_IMP_SENS_CTRL_28: + case RT5668_HP_IMP_SENS_CTRL_29: + case RT5668_HP_IMP_SENS_CTRL_30: + case RT5668_HP_IMP_SENS_CTRL_31: + case RT5668_HP_IMP_SENS_CTRL_32: + case RT5668_HP_IMP_SENS_CTRL_33: + case RT5668_HP_IMP_SENS_CTRL_34: + case RT5668_HP_IMP_SENS_CTRL_35: + case RT5668_HP_IMP_SENS_CTRL_36: + case RT5668_HP_IMP_SENS_CTRL_37: + case RT5668_HP_IMP_SENS_CTRL_38: + case RT5668_HP_IMP_SENS_CTRL_39: + case RT5668_HP_IMP_SENS_CTRL_40: + case RT5668_HP_IMP_SENS_CTRL_41: + case RT5668_HP_IMP_SENS_CTRL_42: + case RT5668_HP_IMP_SENS_CTRL_43: + case RT5668_HP_LOGIC_CTRL_1: + case RT5668_HP_LOGIC_CTRL_2: + case RT5668_HP_LOGIC_CTRL_3: + case RT5668_HP_CALIB_CTRL_1: + case RT5668_HP_CALIB_CTRL_2: + case RT5668_HP_CALIB_CTRL_3: + case RT5668_HP_CALIB_CTRL_4: + case RT5668_HP_CALIB_CTRL_5: + case RT5668_HP_CALIB_CTRL_6: + case RT5668_HP_CALIB_CTRL_7: + case RT5668_HP_CALIB_CTRL_9: + case RT5668_HP_CALIB_CTRL_10: + case RT5668_HP_CALIB_CTRL_11: + case RT5668_HP_CALIB_STA_1: + case RT5668_HP_CALIB_STA_2: + case RT5668_HP_CALIB_STA_3: + case RT5668_HP_CALIB_STA_4: + case RT5668_HP_CALIB_STA_5: + case RT5668_HP_CALIB_STA_6: + case RT5668_HP_CALIB_STA_7: + case RT5668_HP_CALIB_STA_8: + case RT5668_HP_CALIB_STA_9: + case RT5668_HP_CALIB_STA_10: + case RT5668_HP_CALIB_STA_11: + case RT5668_SAR_IL_CMD_1: + case RT5668_SAR_IL_CMD_2: + case RT5668_SAR_IL_CMD_3: + case RT5668_SAR_IL_CMD_4: + case RT5668_SAR_IL_CMD_5: + case RT5668_SAR_IL_CMD_6: + case RT5668_SAR_IL_CMD_7: + case RT5668_SAR_IL_CMD_8: + case RT5668_SAR_IL_CMD_9: + case RT5668_SAR_IL_CMD_10: + case RT5668_SAR_IL_CMD_11: + case RT5668_SAR_IL_CMD_12: + case RT5668_SAR_IL_CMD_13: + case RT5668_EFUSE_CTRL_1: + case RT5668_EFUSE_CTRL_2: + case RT5668_EFUSE_CTRL_3: + case RT5668_EFUSE_CTRL_4: + case RT5668_EFUSE_CTRL_5: + case RT5668_EFUSE_CTRL_6: + case RT5668_EFUSE_CTRL_7: + case RT5668_EFUSE_CTRL_8: + case RT5668_EFUSE_CTRL_9: + case RT5668_EFUSE_CTRL_10: + case RT5668_EFUSE_CTRL_11: + case RT5668_JD_TOP_VC_VTRL: + case RT5668_DRC1_CTRL_0: + case RT5668_DRC1_CTRL_1: + case RT5668_DRC1_CTRL_2: + case RT5668_DRC1_CTRL_3: + case RT5668_DRC1_CTRL_4: + case RT5668_DRC1_CTRL_5: + case RT5668_DRC1_CTRL_6: + case RT5668_DRC1_HARD_LMT_CTRL_1: + case RT5668_DRC1_HARD_LMT_CTRL_2: + case RT5668_DRC1_PRIV_1: + case RT5668_DRC1_PRIV_2: + case RT5668_DRC1_PRIV_3: + case RT5668_DRC1_PRIV_4: + case RT5668_DRC1_PRIV_5: + case RT5668_DRC1_PRIV_6: + case RT5668_DRC1_PRIV_7: + case RT5668_DRC1_PRIV_8: + case RT5668_EQ_AUTO_RCV_CTRL1: + case RT5668_EQ_AUTO_RCV_CTRL2: + case RT5668_EQ_AUTO_RCV_CTRL3: + case RT5668_EQ_AUTO_RCV_CTRL4: + case RT5668_EQ_AUTO_RCV_CTRL5: + case RT5668_EQ_AUTO_RCV_CTRL6: + case RT5668_EQ_AUTO_RCV_CTRL7: + case RT5668_EQ_AUTO_RCV_CTRL8: + case RT5668_EQ_AUTO_RCV_CTRL9: + case RT5668_EQ_AUTO_RCV_CTRL10: + case RT5668_EQ_AUTO_RCV_CTRL11: + case RT5668_EQ_AUTO_RCV_CTRL12: + case RT5668_EQ_AUTO_RCV_CTRL13: + case RT5668_ADC_L_EQ_LPF1_A1: + case RT5668_R_EQ_LPF1_A1: + case RT5668_L_EQ_LPF1_H0: + case RT5668_R_EQ_LPF1_H0: + case RT5668_L_EQ_BPF1_A1: + case RT5668_R_EQ_BPF1_A1: + case RT5668_L_EQ_BPF1_A2: + case RT5668_R_EQ_BPF1_A2: + case RT5668_L_EQ_BPF1_H0: + case RT5668_R_EQ_BPF1_H0: + case RT5668_L_EQ_BPF2_A1: + case RT5668_R_EQ_BPF2_A1: + case RT5668_L_EQ_BPF2_A2: + case RT5668_R_EQ_BPF2_A2: + case RT5668_L_EQ_BPF2_H0: + case RT5668_R_EQ_BPF2_H0: + case RT5668_L_EQ_BPF3_A1: + case RT5668_R_EQ_BPF3_A1: + case RT5668_L_EQ_BPF3_A2: + case RT5668_R_EQ_BPF3_A2: + case RT5668_L_EQ_BPF3_H0: + case RT5668_R_EQ_BPF3_H0: + case RT5668_L_EQ_BPF4_A1: + case RT5668_R_EQ_BPF4_A1: + case RT5668_L_EQ_BPF4_A2: + case RT5668_R_EQ_BPF4_A2: + case RT5668_L_EQ_BPF4_H0: + case RT5668_R_EQ_BPF4_H0: + case RT5668_L_EQ_HPF1_A1: + case RT5668_R_EQ_HPF1_A1: + case RT5668_L_EQ_HPF1_H0: + case RT5668_R_EQ_HPF1_H0: + case RT5668_L_EQ_PRE_VOL: + case RT5668_R_EQ_PRE_VOL: + case RT5668_L_EQ_POST_VOL: + case RT5668_R_EQ_POST_VOL: + case RT5668_I2C_MODE: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5668_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5668_if2_adc_enum, + RT5668_DIG_INF2_DATA, RT5668_IF2_ADC_SEL_SFT, rt5668_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5668_if1_01_adc_enum, + RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC1_SEL_SFT, rt5668_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5668_if1_23_adc_enum, + RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC2_SEL_SFT, rt5668_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5668_if1_45_adc_enum, + RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC3_SEL_SFT, rt5668_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5668_if1_67_adc_enum, + RT5668_TDM_ADDA_CTRL_1, RT5668_IF1_ADC4_SEL_SFT, rt5668_data_select); + +static const struct snd_kcontrol_new rt5668_if2_adc_swap_mux = + SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5668_if2_adc_enum); + +static const struct snd_kcontrol_new rt5668_if1_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5668_if1_01_adc_enum); + +static const struct snd_kcontrol_new rt5668_if1_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5668_if1_23_adc_enum); + +static const struct snd_kcontrol_new rt5668_if1_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5668_if1_45_adc_enum); + +static const struct snd_kcontrol_new rt5668_if1_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5668_if1_67_adc_enum); + +static void rt5668_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT5668_RESET, 0); + regmap_write(regmap, RT5668_I2C_MODE, 1); +} +/** + * rt5668_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5668 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the component driver will turn on + * ASRC for these filters if ASRC is selected as their clock source. + */ +int rt5668_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + + switch (clk_src) { + case RT5668_CLK_SEL_SYS: + case RT5668_CLK_SEL_I2S1_ASRC: + case RT5668_CLK_SEL_I2S2_ASRC: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5668_DA_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5668_PLL_TRACK_2, + RT5668_FILTER_CLK_SEL_MASK, + clk_src << RT5668_FILTER_CLK_SEL_SFT); + } + + if (filter_mask & RT5668_AD_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5668_PLL_TRACK_3, + RT5668_FILTER_CLK_SEL_MASK, + clk_src << RT5668_FILTER_CLK_SEL_SFT); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5668_sel_asrc_clk_src); + +static int rt5668_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read(component, RT5668_4BTN_IL_CMD_1); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5668_4BTN_IL_CMD_1, val); + pr_debug("%s btn_type=%x\n", __func__, btn_type); + + return btn_type; +} + +static void rt5668_enable_push_button_irq(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1, + RT5668_SAR_BUTT_DET_MASK, RT5668_SAR_BUTT_DET_EN); + snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_13, + RT5668_SAR_SOUR_MASK, RT5668_SAR_SOUR_BTN); + snd_soc_component_write(component, RT5668_IL_CMD_1, 0x0040); + snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2, + RT5668_4BTN_IL_MASK | RT5668_4BTN_IL_RST_MASK, + RT5668_4BTN_IL_EN | RT5668_4BTN_IL_NOR); + snd_soc_component_update_bits(component, RT5668_IRQ_CTRL_3, + RT5668_IL_IRQ_MASK, RT5668_IL_IRQ_EN); + } else { + snd_soc_component_update_bits(component, RT5668_IRQ_CTRL_3, + RT5668_IL_IRQ_MASK, RT5668_IL_IRQ_DIS); + snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1, + RT5668_SAR_BUTT_DET_MASK, RT5668_SAR_BUTT_DET_DIS); + snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2, + RT5668_4BTN_IL_MASK, RT5668_4BTN_IL_DIS); + snd_soc_component_update_bits(component, RT5668_4BTN_IL_CMD_2, + RT5668_4BTN_IL_RST_MASK, RT5668_4BTN_IL_RST); + snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_13, + RT5668_SAR_SOUR_MASK, RT5668_SAR_SOUR_TYPE); + } +} + +/** + * rt5668_headset_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5668_headset_detect(struct snd_soc_component *component, + int jack_insert) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + unsigned int val, count; + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_1, + RT5668_TRIG_JD_MASK, RT5668_TRIG_JD_HIGH); + + count = 0; + val = snd_soc_component_read(component, RT5668_CBJ_CTRL_2) + & RT5668_JACK_TYPE_MASK; + while (val == 0 && count < 50) { + usleep_range(10000, 15000); + val = snd_soc_component_read(component, + RT5668_CBJ_CTRL_2) & RT5668_JACK_TYPE_MASK; + count++; + } + + switch (val) { + case 0x1: + case 0x2: + rt5668->jack_type = SND_JACK_HEADSET; + rt5668_enable_push_button_irq(component, true); + break; + default: + rt5668->jack_type = SND_JACK_HEADPHONE; + } + + } else { + rt5668_enable_push_button_irq(component, false); + snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_1, + RT5668_TRIG_JD_MASK, RT5668_TRIG_JD_LOW); + snd_soc_dapm_disable_pin(dapm, "CBJ Power"); + snd_soc_dapm_sync(dapm); + + rt5668->jack_type = 0; + } + + dev_dbg(component->dev, "jack_type = %d\n", rt5668->jack_type); + return rt5668->jack_type; +} + +static irqreturn_t rt5668_irq(int irq, void *data) +{ + struct rt5668_priv *rt5668 = data; + + mod_delayed_work(system_power_efficient_wq, + &rt5668->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static void rt5668_jd_check_handler(struct work_struct *work) +{ + struct rt5668_priv *rt5668 = container_of(work, struct rt5668_priv, + jd_check_work.work); + + if (snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL) + & RT5668_JDH_RS_MASK) { + /* jack out */ + rt5668->jack_type = rt5668_headset_detect(rt5668->component, 0); + + snd_soc_jack_report(rt5668->hs_jack, rt5668->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else { + schedule_delayed_work(&rt5668->jd_check_work, 500); + } +} + +static int rt5668_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + switch (rt5668->pdata.jd_src) { + case RT5668_JD1: + snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_2, + RT5668_EXT_JD_SRC, RT5668_EXT_JD_SRC_MANUAL); + snd_soc_component_write(component, RT5668_CBJ_CTRL_1, 0xd002); + snd_soc_component_update_bits(component, RT5668_CBJ_CTRL_3, + RT5668_CBJ_IN_BUF_EN, RT5668_CBJ_IN_BUF_EN); + snd_soc_component_update_bits(component, RT5668_SAR_IL_CMD_1, + RT5668_SAR_POW_MASK, RT5668_SAR_POW_EN); + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP1_PIN_MASK, RT5668_GP1_PIN_IRQ); + regmap_update_bits(rt5668->regmap, RT5668_RC_CLK_CTRL, + RT5668_POW_IRQ | RT5668_POW_JDH | + RT5668_POW_ANA, RT5668_POW_IRQ | + RT5668_POW_JDH | RT5668_POW_ANA); + regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_2, + RT5668_PWR_JDH | RT5668_PWR_JDL, + RT5668_PWR_JDH | RT5668_PWR_JDL); + regmap_update_bits(rt5668->regmap, RT5668_IRQ_CTRL_2, + RT5668_JD1_EN_MASK | RT5668_JD1_POL_MASK, + RT5668_JD1_EN | RT5668_JD1_POL_NOR); + mod_delayed_work(system_power_efficient_wq, + &rt5668->jack_detect_work, msecs_to_jiffies(250)); + break; + + case RT5668_JD_NULL: + regmap_update_bits(rt5668->regmap, RT5668_IRQ_CTRL_2, + RT5668_JD1_EN_MASK, RT5668_JD1_DIS); + regmap_update_bits(rt5668->regmap, RT5668_RC_CLK_CTRL, + RT5668_POW_JDH | RT5668_POW_JDL, 0); + break; + + default: + dev_warn(component->dev, "Wrong JD source\n"); + break; + } + + rt5668->hs_jack = hs_jack; + + return 0; +} + +static void rt5668_jack_detect_handler(struct work_struct *work) +{ + struct rt5668_priv *rt5668 = + container_of(work, struct rt5668_priv, jack_detect_work.work); + int val, btn_type; + + if (!rt5668->component || + !snd_soc_card_is_instantiated(rt5668->component->card)) { + /* card not yet ready, try later */ + mod_delayed_work(system_power_efficient_wq, + &rt5668->jack_detect_work, msecs_to_jiffies(15)); + return; + } + + mutex_lock(&rt5668->calibrate_mutex); + + val = snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL) + & RT5668_JDH_RS_MASK; + if (!val) { + /* jack in */ + if (rt5668->jack_type == 0) { + /* jack was out, report jack type */ + rt5668->jack_type = + rt5668_headset_detect(rt5668->component, 1); + } else { + /* jack is already in, report button event */ + rt5668->jack_type = SND_JACK_HEADSET; + btn_type = rt5668_button_detect(rt5668->component); + /** + * rt5668 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + rt5668->jack_type |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + rt5668->jack_type |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + rt5668->jack_type |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + rt5668->jack_type |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + btn_type = 0; + dev_err(rt5668->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + } else { + /* jack out */ + rt5668->jack_type = rt5668_headset_detect(rt5668->component, 0); + } + + snd_soc_jack_report(rt5668->hs_jack, rt5668->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (rt5668->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5668->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5668->jd_check_work); + + mutex_unlock(&rt5668->calibrate_mutex); +} + +static const struct snd_kcontrol_new rt5668_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5668_HPL_GAIN, + RT5668_HPR_GAIN, RT5668_G_HP_SFT, 15, 1, hp_vol_tlv), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5668_DAC1_DIG_VOL, + RT5668_L_VOL_SFT, RT5668_R_VOL_SFT, 175, 0, dac_vol_tlv), + + /* IN Boost Volume */ + SOC_SINGLE_TLV("CBJ Boost Volume", RT5668_CBJ_BST_CTRL, + RT5668_BST_CBJ_SFT, 8, 0, bst_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("STO1 ADC Capture Switch", RT5668_STO1_ADC_DIG_VOL, + RT5668_L_MUTE_SFT, RT5668_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5668_STO1_ADC_DIG_VOL, + RT5668_L_VOL_SFT, RT5668_R_VOL_SFT, 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5668_STO1_ADC_BOOST, + RT5668_STO1_ADC_L_BST_SFT, RT5668_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), +}; + + +static int rt5668_div_sel(struct rt5668_priv *rt5668, + int target, const int div[], int size) +{ + int i; + + if (rt5668->sysclk < target) { + pr_err("sysclk rate %d is too low\n", + rt5668->sysclk); + return 0; + } + + for (i = 0; i < size - 1; i++) { + pr_info("div[%d]=%d\n", i, div[i]); + if (target * div[i] == rt5668->sysclk) + return i; + if (target * div[i + 1] > rt5668->sysclk) { + pr_err("can't find div for sysclk %d\n", + rt5668->sysclk); + return i; + } + } + + if (target * div[i] < rt5668->sysclk) + pr_err("sysclk rate %d is too high\n", + rt5668->sysclk); + + return size - 1; + +} + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + int idx; + static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128}; + + idx = rt5668_div_sel(rt5668, 1500000, div, ARRAY_SIZE(div)); + + snd_soc_component_update_bits(component, RT5668_DMIC_CTRL_1, + RT5668_DMIC_CLK_MASK, idx << RT5668_DMIC_CLK_SFT); + + return 0; +} + +static int set_filter_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + int ref, val, reg, idx; + static const int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48}; + + val = snd_soc_component_read(component, RT5668_GPIO_CTRL_1) & + RT5668_GP4_PIN_MASK; + if (w->shift == RT5668_PWR_ADC_S1F_BIT && + val == RT5668_GP4_PIN_ADCDAT2) + ref = 256 * rt5668->lrck[RT5668_AIF2]; + else + ref = 256 * rt5668->lrck[RT5668_AIF1]; + + idx = rt5668_div_sel(rt5668, ref, div, ARRAY_SIZE(div)); + + if (w->shift == RT5668_PWR_ADC_S1F_BIT) + reg = RT5668_PLL_TRACK_3; + else + reg = RT5668_PLL_TRACK_2; + + snd_soc_component_update_bits(component, reg, + RT5668_FILTER_CLK_SEL_MASK, idx << RT5668_FILTER_CLK_SEL_SFT); + + return 0; +} + +static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read(component, RT5668_GLB_CLK); + val &= RT5668_SCLK_SRC_MASK; + if (val == RT5668_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case RT5668_ADC_STO1_ASRC_SFT: + reg = RT5668_PLL_TRACK_3; + shift = RT5668_FILTER_CLK_SEL_SFT; + break; + case RT5668_DAC_STO1_ASRC_SFT: + reg = RT5668_PLL_TRACK_2; + shift = RT5668_FILTER_CLK_SEL_SFT; + break; + default: + return 0; + } + + val = (snd_soc_component_read(component, reg) >> shift) & 0xf; + switch (val) { + case RT5668_CLK_SEL_I2S1_ASRC: + case RT5668_CLK_SEL_I2S2_ASRC: + return 1; + default: + return 0; + } + +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5668_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5668_STO1_ADC_MIXER, + RT5668_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5668_STO1_ADC_MIXER, + RT5668_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5668_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5668_STO1_ADC_MIXER, + RT5668_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5668_STO1_ADC_MIXER, + RT5668_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5668_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5668_AD_DA_MIXER, + RT5668_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5668_AD_DA_MIXER, + RT5668_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5668_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5668_AD_DA_MIXER, + RT5668_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5668_AD_DA_MIXER, + RT5668_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5668_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5668_STO1_DAC_MIXER, + RT5668_M_DAC_L1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5668_STO1_DAC_MIXER, + RT5668_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5668_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5668_STO1_DAC_MIXER, + RT5668_M_DAC_L1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5668_STO1_DAC_MIXER, + RT5668_M_DAC_R1_STO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5668_rec1_l_mix[] = { + SOC_DAPM_SINGLE("CBJ Switch", RT5668_REC_MIXER, + RT5668_M_CBJ_RM1_L_SFT, 1, 1), +}; + +/* STO1 ADC1 Source */ +/* MX-26 [13] [5] */ +static const char * const rt5668_sto1_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adc1l_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADC1L_SRC_SFT, rt5668_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5668_sto1_adc1l_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5668_sto1_adc1l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adc1r_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADC1R_SRC_SFT, rt5668_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5668_sto1_adc1r_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5668_sto1_adc1r_enum); + +/* STO1 ADC Source */ +/* MX-26 [11:10] [3:2] */ +static const char * const rt5668_sto1_adc_src[] = { + "ADC1 L", "ADC1 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adcl_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADCL_SRC_SFT, rt5668_sto1_adc_src); + +static const struct snd_kcontrol_new rt5668_sto1_adcl_mux = + SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5668_sto1_adcl_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adcr_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADCR_SRC_SFT, rt5668_sto1_adc_src); + +static const struct snd_kcontrol_new rt5668_sto1_adcr_mux = + SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5668_sto1_adcr_enum); + +/* STO1 ADC2 Source */ +/* MX-26 [12] [4] */ +static const char * const rt5668_sto1_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adc2l_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADC2L_SRC_SFT, rt5668_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5668_sto1_adc2l_mux = + SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5668_sto1_adc2l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5668_sto1_adc2r_enum, RT5668_STO1_ADC_MIXER, + RT5668_STO1_ADC2R_SRC_SFT, rt5668_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5668_sto1_adc2r_mux = + SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5668_sto1_adc2r_enum); + +/* MX-79 [6:4] I2S1 ADC data location */ +static const unsigned int rt5668_if1_adc_slot_values[] = { + 0, + 2, + 4, + 6, +}; + +static const char * const rt5668_if1_adc_slot_src[] = { + "Slot 0", "Slot 2", "Slot 4", "Slot 6" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5668_if1_adc_slot_enum, + RT5668_TDM_CTRL, RT5668_TDM_ADC_LCA_SFT, RT5668_TDM_ADC_LCA_MASK, + rt5668_if1_adc_slot_src, rt5668_if1_adc_slot_values); + +static const struct snd_kcontrol_new rt5668_if1_adc_slot_mux = + SOC_DAPM_ENUM("IF1 ADC Slot location", rt5668_if1_adc_slot_enum); + +/* Analog DAC L1 Source, Analog DAC R1 Source*/ +/* MX-2B [4], MX-2B [0]*/ +static const char * const rt5668_alg_dac1_src[] = { + "Stereo1 DAC Mixer", "DAC1" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5668_alg_dac_l1_enum, RT5668_A_DAC1_MUX, + RT5668_A_DACL1_SFT, rt5668_alg_dac1_src); + +static const struct snd_kcontrol_new rt5668_alg_dac_l1_mux = + SOC_DAPM_ENUM("Analog DAC L1 Source", rt5668_alg_dac_l1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5668_alg_dac_r1_enum, RT5668_A_DAC1_MUX, + RT5668_A_DACR1_SFT, rt5668_alg_dac1_src); + +static const struct snd_kcontrol_new rt5668_alg_dac_r1_mux = + SOC_DAPM_ENUM("Analog DAC R1 Source", rt5668_alg_dac_r1_enum); + +/* Out Switch */ +static const struct snd_kcontrol_new hpol_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5668_HP_CTRL_1, + RT5668_L_MUTE_SFT, 1, 1); +static const struct snd_kcontrol_new hpor_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5668_HP_CTRL_1, + RT5668_R_MUTE_SFT, 1, 1); + +static int rt5668_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write(component, + RT5668_HP_LOGIC_CTRL_2, 0x0012); + snd_soc_component_write(component, + RT5668_HP_CTRL_2, 0x6000); + snd_soc_component_update_bits(component, RT5668_STO_NG2_CTRL_1, + RT5668_NG2_EN_MASK, RT5668_NG2_EN); + snd_soc_component_update_bits(component, + RT5668_DEPOP_1, 0x60, 0x60); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + RT5668_DEPOP_1, 0x60, 0x0); + snd_soc_component_write(component, + RT5668_HP_CTRL_2, 0x0000); + break; + + default: + return 0; + } + + return 0; + +} + +static int set_dmic_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /*Add delay to avoid pop noise*/ + msleep(150); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5655_set_verf(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case RT5668_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5668_PWR_ANLG_1, RT5668_PWR_FV1, 0); + break; + + case RT5668_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5668_PWR_ANLG_1, RT5668_PWR_FV2, 0); + break; + + default: + break; + } + break; + + case SND_SOC_DAPM_POST_PMU: + usleep_range(15000, 20000); + switch (w->shift) { + case RT5668_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5668_PWR_ANLG_1, RT5668_PWR_FV1, + RT5668_PWR_FV1); + break; + + case RT5668_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5668_PWR_ANLG_1, RT5668_PWR_FV2, + RT5668_PWR_FV2); + break; + + default: + break; + } + break; + + default: + return 0; + } + + return 0; +} + +static const unsigned int rt5668_adcdat_pin_values[] = { + 1, + 3, +}; + +static const char * const rt5668_adcdat_pin_select[] = { + "ADCDAT1", + "ADCDAT2", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5668_adcdat_pin_enum, + RT5668_GPIO_CTRL_1, RT5668_GP4_PIN_SFT, RT5668_GP4_PIN_MASK, + rt5668_adcdat_pin_select, rt5668_adcdat_pin_values); + +static const struct snd_kcontrol_new rt5668_adcdat_pin_ctrl = + SOC_DAPM_ENUM("ADCDAT", rt5668_adcdat_pin_enum); + +static const struct snd_soc_dapm_widget rt5668_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5668_PWR_ANLG_3, RT5668_PWR_LDO2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5668_PWR_ANLG_3, RT5668_PWR_PLL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B", RT5668_PWR_ANLG_3, RT5668_PWR_PLL2B_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F", RT5668_PWR_ANLG_3, RT5668_PWR_PLL2F_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Vref1", RT5668_PWR_ANLG_1, RT5668_PWR_VREF1_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref2", RT5668_PWR_ANLG_1, RT5668_PWR_VREF2_BIT, 0, + rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5668_PLL_TRACK_1, + RT5668_DAC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5668_PLL_TRACK_1, + RT5668_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5668_PLL_TRACK_1, + RT5668_AD_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5668_PLL_TRACK_1, + RT5668_DA_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5668_PLL_TRACK_1, + RT5668_DMIC_ASRC_SFT, 0, NULL, 0), + + /* Input Side */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5668_PWR_ANLG_2, RT5668_PWR_MB1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5668_PWR_ANLG_2, RT5668_PWR_MB2_BIT, + 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + + SND_SOC_DAPM_INPUT("IN1P"), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5668_DMIC_CTRL_1, + RT5668_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, + 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("CBJ Power", RT5668_PWR_ANLG_3, + RT5668_PWR_CBJ_BIT, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5668_rec1_l_mix, + ARRAY_SIZE(rt5668_rec1_l_mix)), + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5668_PWR_ANLG_2, + RT5668_PWR_RM1_L_BIT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5668_PWR_DIG_1, + RT5668_PWR_ADC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5668_PWR_DIG_1, + RT5668_PWR_ADC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5668_CHOP_ADC, + RT5668_CKGEN_ADC1_SFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adcl_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5668_sto1_adcr_mux), + SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if1_adc_slot_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5668_PWR_DIG_2, + RT5668_PWR_ADC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5668_STO1_ADC_DIG_VOL, + RT5668_L_MUTE_SFT, 1, rt5668_sto1_adc_l_mix, + ARRAY_SIZE(rt5668_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5668_STO1_ADC_DIG_VOL, + RT5668_R_MUTE_SFT, 1, rt5668_sto1_adc_r_mix, + ARRAY_SIZE(rt5668_sto1_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5668_PWR_DIG_1, RT5668_PWR_I2S1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5668_PWR_DIG_1, RT5668_PWR_I2S2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5668_if2_adc_swap_mux), + + SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, + &rt5668_adcdat_pin_ctrl), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, + RT5668_I2S1_SDP, RT5668_SEL_ADCDAT_SFT, 1), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, + RT5668_I2S2_SDP, RT5668_I2S2_PIN_CFG_SFT, 1), + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5668_dac_l_mix, ARRAY_SIZE(rt5668_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5668_dac_r_mix, ARRAY_SIZE(rt5668_dac_r_mix)), + + /* DAC channel Mux */ + SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, + &rt5668_alg_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, + &rt5668_alg_dac_r1_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5668_PWR_DIG_2, + RT5668_PWR_DAC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5668_sto1_dac_l_mix, ARRAY_SIZE(rt5668_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5668_sto1_dac_r_mix, ARRAY_SIZE(rt5668_sto1_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5668_PWR_DIG_1, + RT5668_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5668_PWR_DIG_1, + RT5668_PWR_DAC_R1_BIT, 0), + SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 3, RT5668_CHOP_DAC, + RT5668_CKGEN_DAC1_SFT, 0, NULL, 0), + + /* HPO */ + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5668_hp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("HP Amp L", RT5668_PWR_ANLG_1, + RT5668_PWR_HA_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP Amp R", RT5668_PWR_ANLG_1, + RT5668_PWR_HA_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5668_DEPOP_1, + RT5668_PUMP_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5668_DEPOP_1, + RT5668_CAPLESS_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("HPOL Playback", SND_SOC_NOPM, 0, 0, + &hpol_switch), + SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0, + &hpor_switch), + + /* CLK DET */ + SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5668_CLK_DET, + RT5668_SYS_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5668_CLK_DET, + RT5668_PLL1_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL2", RT5668_CLK_DET, + RT5668_PLL2_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET", RT5668_CLK_DET, + RT5668_POW_CLK_DET_SFT, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + +}; + +static const struct snd_soc_dapm_route rt5668_dapm_routes[] = { + /*PLL*/ + {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + + /*ASRC*/ + {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, + {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, + {"ADC STO1 ASRC", NULL, "AD ASRC"}, + {"DAC STO1 ASRC", NULL, "DA ASRC"}, + + /*Vref*/ + {"MICBIAS1", NULL, "Vref1"}, + {"MICBIAS1", NULL, "Vref2"}, + {"MICBIAS2", NULL, "Vref1"}, + {"MICBIAS2", NULL, "Vref2"}, + + {"CLKDET SYS", NULL, "CLKDET"}, + + {"IN1P", NULL, "LDO2"}, + + {"BST1 CBJ", NULL, "IN1P"}, + {"BST1 CBJ", NULL, "CBJ Power"}, + {"CBJ Power", NULL, "Vref2"}, + + {"RECMIX1L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX1L", NULL, "RECMIX1L Power"}, + + {"ADC1 L", NULL, "RECMIX1L"}, + {"ADC1 L", NULL, "ADC1 L Power"}, + {"ADC1 L", NULL, "ADC1 clock"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC CLK", NULL, "DMIC ASRC"}, + + {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"}, + + {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"}, + {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"}, + {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"}, + {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"}, + {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, + + {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + + {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"}, + {"IF1_ADC Mux", NULL, "I2S1"}, + {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"}, + {"AIF1TX", NULL, "ADCDAT Mux"}, + {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"}, + {"AIF2TX", NULL, "ADCDAT Mux"}, + + {"IF1 DAC1 L", NULL, "AIF1RX"}, + {"IF1 DAC1 L", NULL, "I2S1"}, + {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"}, + {"IF1 DAC1 R", NULL, "AIF1RX"}, + {"IF1 DAC1 R", NULL, "I2S1"}, + {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"}, + + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"}, + {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"}, + + {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + + {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + + {"DAC L1 Source", "DAC1", "DAC1 MIXL"}, + {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC R1 Source", "DAC1", "DAC1 MIXR"}, + {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + + {"DAC L1", NULL, "DAC L1 Source"}, + {"DAC R1", NULL, "DAC R1 Source"}, + + {"DAC L1", NULL, "DAC 1 Clock"}, + {"DAC R1", NULL, "DAC 1 Clock"}, + + {"HP Amp", NULL, "DAC L1"}, + {"HP Amp", NULL, "DAC R1"}, + {"HP Amp", NULL, "HP Amp L"}, + {"HP Amp", NULL, "HP Amp R"}, + {"HP Amp", NULL, "Capless"}, + {"HP Amp", NULL, "Charge Pump"}, + {"HP Amp", NULL, "CLKDET SYS"}, + {"HP Amp", NULL, "CBJ Power"}, + {"HP Amp", NULL, "Vref2"}, + {"HPOL Playback", "Switch", "HP Amp"}, + {"HPOR Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HPOL Playback"}, + {"HPOR", NULL, "HPOR Playback"}, +}; + +static int rt5668_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int val = 0; + + switch (slots) { + case 4: + val |= RT5668_TDM_TX_CH_4; + val |= RT5668_TDM_RX_CH_4; + break; + case 6: + val |= RT5668_TDM_TX_CH_6; + val |= RT5668_TDM_RX_CH_6; + break; + case 8: + val |= RT5668_TDM_TX_CH_8; + val |= RT5668_TDM_RX_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5668_TDM_CTRL, + RT5668_TDM_TX_CH_MASK | RT5668_TDM_RX_CH_MASK, val); + + switch (slot_width) { + case 16: + val = RT5668_TDM_CL_16; + break; + case 20: + val = RT5668_TDM_CL_20; + break; + case 24: + val = RT5668_TDM_CL_24; + break; + case 32: + val = RT5668_TDM_CL_32; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5668_TDM_TCON_CTRL, + RT5668_TDM_CL_MASK, val); + + return 0; +} + + +static int rt5668_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + unsigned int len_1 = 0, len_2 = 0; + int pre_div, frame_size; + + rt5668->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5668->sysclk, rt5668->lrck[dai->id]); + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt5668->lrck[dai->id], pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + len_1 |= RT5668_I2S1_DL_20; + len_2 |= RT5668_I2S2_DL_20; + break; + case 24: + len_1 |= RT5668_I2S1_DL_24; + len_2 |= RT5668_I2S2_DL_24; + break; + case 32: + len_1 |= RT5668_I2S1_DL_32; + len_2 |= RT5668_I2S2_DL_24; + break; + case 8: + len_1 |= RT5668_I2S2_DL_8; + len_2 |= RT5668_I2S2_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5668_AIF1: + snd_soc_component_update_bits(component, RT5668_I2S1_SDP, + RT5668_I2S1_DL_MASK, len_1); + if (rt5668->master[RT5668_AIF1]) { + snd_soc_component_update_bits(component, + RT5668_ADDA_CLK_1, RT5668_I2S_M_DIV_MASK, + pre_div << RT5668_I2S_M_DIV_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5668_I2S1_SDP, RT5668_I2S1_MONO_MASK, + RT5668_I2S1_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5668_I2S1_SDP, RT5668_I2S1_MONO_MASK, + RT5668_I2S1_MONO_DIS); + break; + case RT5668_AIF2: + snd_soc_component_update_bits(component, RT5668_I2S2_SDP, + RT5668_I2S2_DL_MASK, len_2); + if (rt5668->master[RT5668_AIF2]) { + snd_soc_component_update_bits(component, + RT5668_I2S_M_CLK_CTRL_1, RT5668_I2S2_M_PD_MASK, + pre_div << RT5668_I2S2_M_PD_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5668_I2S2_SDP, RT5668_I2S2_MONO_MASK, + RT5668_I2S2_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5668_I2S2_SDP, RT5668_I2S2_MONO_MASK, + RT5668_I2S2_MONO_DIS); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5668_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, tdm_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5668->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rt5668->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5668_I2S_BP_INV; + tdm_ctrl |= RT5668_TDM_S_BP_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + if (dai->id == RT5668_AIF1) + tdm_ctrl |= RT5668_TDM_S_LP_INV | RT5668_TDM_M_BP_INV; + else + return -EINVAL; + break; + case SND_SOC_DAIFMT_IB_IF: + if (dai->id == RT5668_AIF1) + tdm_ctrl |= RT5668_TDM_S_BP_INV | RT5668_TDM_S_LP_INV | + RT5668_TDM_M_BP_INV | RT5668_TDM_M_LP_INV; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5668_I2S_DF_LEFT; + tdm_ctrl |= RT5668_TDM_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5668_I2S_DF_PCM_A; + tdm_ctrl |= RT5668_TDM_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5668_I2S_DF_PCM_B; + tdm_ctrl |= RT5668_TDM_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5668_AIF1: + snd_soc_component_update_bits(component, RT5668_I2S1_SDP, + RT5668_I2S_DF_MASK, reg_val); + snd_soc_component_update_bits(component, RT5668_TDM_TCON_CTRL, + RT5668_TDM_MS_MASK | RT5668_TDM_S_BP_MASK | + RT5668_TDM_DF_MASK | RT5668_TDM_M_BP_MASK | + RT5668_TDM_M_LP_MASK | RT5668_TDM_S_LP_MASK, + tdm_ctrl | rt5668->master[dai->id]); + break; + case RT5668_AIF2: + if (rt5668->master[dai->id] == 0) + reg_val |= RT5668_I2S2_MS_S; + snd_soc_component_update_bits(component, RT5668_I2S2_SDP, + RT5668_I2S2_MS_MASK | RT5668_I2S_BP_MASK | + RT5668_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5668_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, src = 0; + + if (freq == rt5668->sysclk && clk_id == rt5668->sysclk_src) + return 0; + + switch (clk_id) { + case RT5668_SCLK_S_MCLK: + reg_val |= RT5668_SCLK_SRC_MCLK; + src = RT5668_CLK_SRC_MCLK; + break; + case RT5668_SCLK_S_PLL1: + reg_val |= RT5668_SCLK_SRC_PLL1; + src = RT5668_CLK_SRC_PLL1; + break; + case RT5668_SCLK_S_PLL2: + reg_val |= RT5668_SCLK_SRC_PLL2; + src = RT5668_CLK_SRC_PLL2; + break; + case RT5668_SCLK_S_RCCLK: + reg_val |= RT5668_SCLK_SRC_RCCLK; + src = RT5668_CLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5668_GLB_CLK, + RT5668_SCLK_SRC_MASK, reg_val); + + if (rt5668->master[RT5668_AIF2]) { + snd_soc_component_update_bits(component, + RT5668_I2S_M_CLK_CTRL_1, RT5668_I2S2_SRC_MASK, + src << RT5668_I2S2_SRC_SFT); + } + + rt5668->sysclk = freq; + rt5668->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt5668_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5668->pll_src && freq_in == rt5668->pll_in && + freq_out == rt5668->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5668->pll_in = 0; + rt5668->pll_out = 0; + snd_soc_component_update_bits(component, RT5668_GLB_CLK, + RT5668_SCLK_SRC_MASK, RT5668_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5668_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5668_GLB_CLK, + RT5668_PLL1_SRC_MASK, RT5668_PLL1_SRC_MCLK); + break; + case RT5668_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, RT5668_GLB_CLK, + RT5668_PLL1_SRC_MASK, RT5668_PLL1_SRC_BCLK1); + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5668_PLL_CTRL_1, + pll_code.n_code << RT5668_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5668_PLL_CTRL_2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5668_PLL_M_SFT) | + (pll_code.m_bp << RT5668_PLL_M_BP_SFT)); + + rt5668->pll_in = freq_in; + rt5668->pll_out = freq_out; + rt5668->pll_src = source; + + return 0; +} + +static int rt5668_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + rt5668->bclk[dai->id] = ratio; + + switch (ratio) { + case 64: + snd_soc_component_update_bits(component, RT5668_ADDA_CLK_2, + RT5668_I2S2_BCLK_MS2_MASK, + RT5668_I2S2_BCLK_MS2_64); + break; + case 32: + snd_soc_component_update_bits(component, RT5668_ADDA_CLK_2, + RT5668_I2S2_BCLK_MS2_MASK, + RT5668_I2S2_BCLK_MS2_32); + break; + default: + dev_err(dai->dev, "Invalid bclk ratio %d\n", ratio); + return -EINVAL; + } + + return 0; +} + +static int rt5668_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1, + RT5668_PWR_MB | RT5668_PWR_BG, + RT5668_PWR_MB | RT5668_PWR_BG); + regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1, + RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO, + RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO); + break; + + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1, + RT5668_PWR_MB, RT5668_PWR_MB); + regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1, + RT5668_DIG_GATE_CTRL, RT5668_DIG_GATE_CTRL); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(rt5668->regmap, RT5668_PWR_DIG_1, + RT5668_DIG_GATE_CTRL | RT5668_PWR_LDO, 0); + regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1, + RT5668_PWR_MB | RT5668_PWR_BG, 0); + break; + + default: + break; + } + + return 0; +} + +static int rt5668_probe(struct snd_soc_component *component) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + rt5668->component = component; + + return 0; +} + +static void rt5668_remove(struct snd_soc_component *component) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + rt5668_reset(rt5668->regmap); +} + +#ifdef CONFIG_PM +static int rt5668_suspend(struct snd_soc_component *component) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5668->regmap, true); + regcache_mark_dirty(rt5668->regmap); + return 0; +} + +static int rt5668_resume(struct snd_soc_component *component) +{ + struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5668->regmap, false); + regcache_sync(rt5668->regmap); + + return 0; +} +#else +#define rt5668_suspend NULL +#define rt5668_resume NULL +#endif + +#define RT5668_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5668_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5668_aif1_dai_ops = { + .hw_params = rt5668_hw_params, + .set_fmt = rt5668_set_dai_fmt, + .set_tdm_slot = rt5668_set_tdm_slot, +}; + +static const struct snd_soc_dai_ops rt5668_aif2_dai_ops = { + .hw_params = rt5668_hw_params, + .set_fmt = rt5668_set_dai_fmt, + .set_bclk_ratio = rt5668_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5668_dai[] = { + { + .name = "rt5668-aif1", + .id = RT5668_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5668_STEREO_RATES, + .formats = RT5668_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5668_STEREO_RATES, + .formats = RT5668_FORMATS, + }, + .ops = &rt5668_aif1_dai_ops, + }, + { + .name = "rt5668-aif2", + .id = RT5668_AIF2, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5668_STEREO_RATES, + .formats = RT5668_FORMATS, + }, + .ops = &rt5668_aif2_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5668 = { + .probe = rt5668_probe, + .remove = rt5668_remove, + .suspend = rt5668_suspend, + .resume = rt5668_resume, + .set_bias_level = rt5668_set_bias_level, + .controls = rt5668_snd_controls, + .num_controls = ARRAY_SIZE(rt5668_snd_controls), + .dapm_widgets = rt5668_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5668_dapm_widgets), + .dapm_routes = rt5668_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5668_dapm_routes), + .set_sysclk = rt5668_set_component_sysclk, + .set_pll = rt5668_set_component_pll, + .set_jack = rt5668_set_jack_detect, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5668_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT5668_I2C_MODE, + .volatile_reg = rt5668_volatile_register, + .readable_reg = rt5668_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5668_reg, + .num_reg_defaults = ARRAY_SIZE(rt5668_reg), + .use_single_read = true, + .use_single_write = true, +}; + +static const struct i2c_device_id rt5668_i2c_id[] = { + {"rt5668b", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5668_i2c_id); + +static int rt5668_parse_dt(struct rt5668_priv *rt5668, struct device *dev) +{ + + of_property_read_u32(dev->of_node, "realtek,dmic1-data-pin", + &rt5668->pdata.dmic1_data_pin); + of_property_read_u32(dev->of_node, "realtek,dmic1-clk-pin", + &rt5668->pdata.dmic1_clk_pin); + of_property_read_u32(dev->of_node, "realtek,jd-src", + &rt5668->pdata.jd_src); + + return 0; +} + +static void rt5668_calibrate(struct rt5668_priv *rt5668) +{ + int value, count; + + mutex_lock(&rt5668->calibrate_mutex); + + rt5668_reset(rt5668->regmap); + regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xa2bf); + usleep_range(15000, 20000); + regmap_write(rt5668->regmap, RT5668_PWR_ANLG_1, 0xf2bf); + regmap_write(rt5668->regmap, RT5668_MICBIAS_2, 0x0380); + regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x8001); + regmap_write(rt5668->regmap, RT5668_TEST_MODE_CTRL_1, 0x0000); + regmap_write(rt5668->regmap, RT5668_STO1_DAC_MIXER, 0x2080); + regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0x4040); + regmap_write(rt5668->regmap, RT5668_DEPOP_1, 0x0069); + regmap_write(rt5668->regmap, RT5668_CHOP_DAC, 0x3000); + regmap_write(rt5668->regmap, RT5668_HP_CTRL_2, 0x6000); + regmap_write(rt5668->regmap, RT5668_HP_CHARGE_PUMP_1, 0x0f26); + regmap_write(rt5668->regmap, RT5668_CALIB_ADC_CTRL, 0x7f05); + regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0x686c); + regmap_write(rt5668->regmap, RT5668_CAL_REC, 0x0d0d); + regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_9, 0x000f); + regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x8d01); + regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_2, 0x0321); + regmap_write(rt5668->regmap, RT5668_HP_LOGIC_CTRL_2, 0x0004); + regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_1, 0x7c00); + regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_3, 0x06a1); + regmap_write(rt5668->regmap, RT5668_A_DAC1_MUX, 0x0311); + regmap_write(rt5668->regmap, RT5668_RESET_HPF_CTRL, 0x0000); + regmap_write(rt5668->regmap, RT5668_ADC_STO1_HP_CTRL_1, 0x3320); + + regmap_write(rt5668->regmap, RT5668_HP_CALIB_CTRL_1, 0xfc00); + + for (count = 0; count < 60; count++) { + regmap_read(rt5668->regmap, RT5668_HP_CALIB_STA_1, &value); + if (!(value & 0x8000)) + break; + + usleep_range(10000, 10005); + } + + if (count >= 60) + pr_err("HP Calibration Failure\n"); + + /* restore settings */ + regmap_write(rt5668->regmap, RT5668_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5668->regmap, RT5668_PWR_DIG_1, 0x0000); + + mutex_unlock(&rt5668->calibrate_mutex); + +} + +static int rt5668_i2c_probe(struct i2c_client *i2c) +{ + struct rt5668_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5668_priv *rt5668; + int i, ret; + unsigned int val; + + rt5668 = devm_kzalloc(&i2c->dev, sizeof(struct rt5668_priv), + GFP_KERNEL); + + if (rt5668 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5668); + + if (pdata) + rt5668->pdata = *pdata; + else + rt5668_parse_dt(rt5668, &i2c->dev); + + rt5668->regmap = devm_regmap_init_i2c(i2c, &rt5668_regmap); + if (IS_ERR(rt5668->regmap)) { + ret = PTR_ERR(rt5668->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(rt5668->supplies); i++) + rt5668->supplies[i].supply = rt5668_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5668->supplies), + rt5668->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5668->supplies), + rt5668->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + rt5668->ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5668->ldo1_en)) { + dev_err(&i2c->dev, "Fail gpio request ldo1_en\n"); + return PTR_ERR(rt5668->ldo1_en); + } + + /* Sleep for 300 ms miniumum */ + usleep_range(300000, 350000); + + regmap_write(rt5668->regmap, RT5668_I2C_MODE, 0x1); + usleep_range(10000, 15000); + + regmap_read(rt5668->regmap, RT5668_DEVICE_ID, &val); + if (val != DEVICE_ID) { + pr_err("Device with ID register %x is not rt5668\n", val); + return -ENODEV; + } + + rt5668_reset(rt5668->regmap); + + rt5668_calibrate(rt5668); + + regmap_write(rt5668->regmap, RT5668_DEPOP_1, 0x0000); + + /* DMIC pin*/ + if (rt5668->pdata.dmic1_data_pin != RT5668_DMIC1_NULL) { + switch (rt5668->pdata.dmic1_data_pin) { + case RT5668_DMIC1_DATA_GPIO2: /* share with LRCK2 */ + regmap_update_bits(rt5668->regmap, RT5668_DMIC_CTRL_1, + RT5668_DMIC_1_DP_MASK, RT5668_DMIC_1_DP_GPIO2); + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP2_PIN_MASK, RT5668_GP2_PIN_DMIC_SDA); + break; + + case RT5668_DMIC1_DATA_GPIO5: /* share with DACDAT1 */ + regmap_update_bits(rt5668->regmap, RT5668_DMIC_CTRL_1, + RT5668_DMIC_1_DP_MASK, RT5668_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP5_PIN_MASK, RT5668_GP5_PIN_DMIC_SDA); + break; + + default: + dev_dbg(&i2c->dev, "invalid DMIC_DAT pin\n"); + break; + } + + switch (rt5668->pdata.dmic1_clk_pin) { + case RT5668_DMIC1_CLK_GPIO1: /* share with IRQ */ + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP1_PIN_MASK, RT5668_GP1_PIN_DMIC_CLK); + break; + + case RT5668_DMIC1_CLK_GPIO3: /* share with BCLK2 */ + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP3_PIN_MASK, RT5668_GP3_PIN_DMIC_CLK); + break; + + default: + dev_dbg(&i2c->dev, "invalid DMIC_CLK pin\n"); + break; + } + } + + regmap_update_bits(rt5668->regmap, RT5668_PWR_ANLG_1, + RT5668_LDO1_DVO_MASK | RT5668_HP_DRIVER_MASK, + RT5668_LDO1_DVO_14 | RT5668_HP_DRIVER_5X); + regmap_write(rt5668->regmap, RT5668_MICBIAS_2, 0x0380); + regmap_update_bits(rt5668->regmap, RT5668_GPIO_CTRL_1, + RT5668_GP4_PIN_MASK | RT5668_GP5_PIN_MASK, + RT5668_GP4_PIN_ADCDAT1 | RT5668_GP5_PIN_DACDAT1); + regmap_write(rt5668->regmap, RT5668_TEST_MODE_CTRL_1, 0x0000); + + INIT_DELAYED_WORK(&rt5668->jack_detect_work, + rt5668_jack_detect_handler); + INIT_DELAYED_WORK(&rt5668->jd_check_work, + rt5668_jd_check_handler); + + mutex_init(&rt5668->calibrate_mutex); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5668_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5668", rt5668); + if (ret) + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + + } + + return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668, + rt5668_dai, ARRAY_SIZE(rt5668_dai)); +} + +static void rt5668_i2c_shutdown(struct i2c_client *client) +{ + struct rt5668_priv *rt5668 = i2c_get_clientdata(client); + + rt5668_reset(rt5668->regmap); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt5668_of_match[] = { + {.compatible = "realtek,rt5668b"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5668_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5668_acpi_match[] = { + {"10EC5668", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5668_acpi_match); +#endif + +static struct i2c_driver rt5668_i2c_driver = { + .driver = { + .name = "rt5668b", + .of_match_table = of_match_ptr(rt5668_of_match), + .acpi_match_table = ACPI_PTR(rt5668_acpi_match), + }, + .probe = rt5668_i2c_probe, + .shutdown = rt5668_i2c_shutdown, + .id_table = rt5668_i2c_id, +}; +module_i2c_driver(rt5668_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5668B driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5668.h b/sound/soc/codecs/rt5668.h new file mode 100644 index 0000000000..6b851ddcc5 --- /dev/null +++ b/sound/soc/codecs/rt5668.h @@ -0,0 +1,1315 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5668.h -- RT5668/RT5658 ALSA SoC audio driver + * + * Copyright 2018 Realtek Microelectronics + * Author: Bard Liao + */ + +#ifndef __RT5668_H__ +#define __RT5668_H__ + +#include + +#define DEVICE_ID 0x6530 + +/* Info */ +#define RT5668_RESET 0x0000 +#define RT5668_VERSION_ID 0x00fd +#define RT5668_VENDOR_ID 0x00fe +#define RT5668_DEVICE_ID 0x00ff +/* I/O - Output */ +#define RT5668_HP_CTRL_1 0x0002 +#define RT5668_HP_CTRL_2 0x0003 +#define RT5668_HPL_GAIN 0x0005 +#define RT5668_HPR_GAIN 0x0006 + +#define RT5668_I2C_CTRL 0x0008 + +/* I/O - Input */ +#define RT5668_CBJ_BST_CTRL 0x000b +#define RT5668_CBJ_CTRL_1 0x0010 +#define RT5668_CBJ_CTRL_2 0x0011 +#define RT5668_CBJ_CTRL_3 0x0012 +#define RT5668_CBJ_CTRL_4 0x0013 +#define RT5668_CBJ_CTRL_5 0x0014 +#define RT5668_CBJ_CTRL_6 0x0015 +#define RT5668_CBJ_CTRL_7 0x0016 +/* I/O - ADC/DAC/DMIC */ +#define RT5668_DAC1_DIG_VOL 0x0019 +#define RT5668_STO1_ADC_DIG_VOL 0x001c +#define RT5668_STO1_ADC_BOOST 0x001f +#define RT5668_HP_IMP_GAIN_1 0x0022 +#define RT5668_HP_IMP_GAIN_2 0x0023 +/* Mixer - D-D */ +#define RT5668_SIDETONE_CTRL 0x0024 +#define RT5668_STO1_ADC_MIXER 0x0026 +#define RT5668_AD_DA_MIXER 0x0029 +#define RT5668_STO1_DAC_MIXER 0x002a +#define RT5668_A_DAC1_MUX 0x002b +#define RT5668_DIG_INF2_DATA 0x0030 +/* Mixer - ADC */ +#define RT5668_REC_MIXER 0x003c +#define RT5668_CAL_REC 0x0044 +#define RT5668_ALC_BACK_GAIN 0x0049 +/* Power */ +#define RT5668_PWR_DIG_1 0x0061 +#define RT5668_PWR_DIG_2 0x0062 +#define RT5668_PWR_ANLG_1 0x0063 +#define RT5668_PWR_ANLG_2 0x0064 +#define RT5668_PWR_ANLG_3 0x0065 +#define RT5668_PWR_MIXER 0x0066 +#define RT5668_PWR_VOL 0x0067 +/* Clock Detect */ +#define RT5668_CLK_DET 0x006b +/* Filter Auto Reset */ +#define RT5668_RESET_LPF_CTRL 0x006c +#define RT5668_RESET_HPF_CTRL 0x006d +/* DMIC */ +#define RT5668_DMIC_CTRL_1 0x006e +/* Format - ADC/DAC */ +#define RT5668_I2S1_SDP 0x0070 +#define RT5668_I2S2_SDP 0x0071 +#define RT5668_ADDA_CLK_1 0x0073 +#define RT5668_ADDA_CLK_2 0x0074 +#define RT5668_I2S1_F_DIV_CTRL_1 0x0075 +#define RT5668_I2S1_F_DIV_CTRL_2 0x0076 +/* Format - TDM Control */ +#define RT5668_TDM_CTRL 0x0079 +#define RT5668_TDM_ADDA_CTRL_1 0x007a +#define RT5668_TDM_ADDA_CTRL_2 0x007b +#define RT5668_DATA_SEL_CTRL_1 0x007c +#define RT5668_TDM_TCON_CTRL 0x007e +/* Function - Analog */ +#define RT5668_GLB_CLK 0x0080 +#define RT5668_PLL_CTRL_1 0x0081 +#define RT5668_PLL_CTRL_2 0x0082 +#define RT5668_PLL_TRACK_1 0x0083 +#define RT5668_PLL_TRACK_2 0x0084 +#define RT5668_PLL_TRACK_3 0x0085 +#define RT5668_PLL_TRACK_4 0x0086 +#define RT5668_PLL_TRACK_5 0x0087 +#define RT5668_PLL_TRACK_6 0x0088 +#define RT5668_PLL_TRACK_11 0x008c +#define RT5668_SDW_REF_CLK 0x008d +#define RT5668_DEPOP_1 0x008e +#define RT5668_DEPOP_2 0x008f +#define RT5668_HP_CHARGE_PUMP_1 0x0091 +#define RT5668_HP_CHARGE_PUMP_2 0x0092 +#define RT5668_MICBIAS_1 0x0093 +#define RT5668_MICBIAS_2 0x0094 +#define RT5668_PLL_TRACK_12 0x0098 +#define RT5668_PLL_TRACK_14 0x009a +#define RT5668_PLL2_CTRL_1 0x009b +#define RT5668_PLL2_CTRL_2 0x009c +#define RT5668_PLL2_CTRL_3 0x009d +#define RT5668_PLL2_CTRL_4 0x009e +#define RT5668_RC_CLK_CTRL 0x009f +#define RT5668_I2S_M_CLK_CTRL_1 0x00a0 +#define RT5668_I2S2_F_DIV_CTRL_1 0x00a3 +#define RT5668_I2S2_F_DIV_CTRL_2 0x00a4 +/* Function - Digital */ +#define RT5668_EQ_CTRL_1 0x00ae +#define RT5668_EQ_CTRL_2 0x00af +#define RT5668_IRQ_CTRL_1 0x00b6 +#define RT5668_IRQ_CTRL_2 0x00b7 +#define RT5668_IRQ_CTRL_3 0x00b8 +#define RT5668_IRQ_CTRL_4 0x00b9 +#define RT5668_INT_ST_1 0x00be +#define RT5668_GPIO_CTRL_1 0x00c0 +#define RT5668_GPIO_CTRL_2 0x00c1 +#define RT5668_GPIO_CTRL_3 0x00c2 +#define RT5668_HP_AMP_DET_CTRL_1 0x00d0 +#define RT5668_HP_AMP_DET_CTRL_2 0x00d1 +#define RT5668_MID_HP_AMP_DET 0x00d2 +#define RT5668_LOW_HP_AMP_DET 0x00d3 +#define RT5668_DELAY_BUF_CTRL 0x00d4 +#define RT5668_SV_ZCD_1 0x00d9 +#define RT5668_SV_ZCD_2 0x00da +#define RT5668_IL_CMD_1 0x00db +#define RT5668_IL_CMD_2 0x00dc +#define RT5668_IL_CMD_3 0x00dd +#define RT5668_IL_CMD_4 0x00de +#define RT5668_IL_CMD_5 0x00df +#define RT5668_IL_CMD_6 0x00e0 +#define RT5668_4BTN_IL_CMD_1 0x00e2 +#define RT5668_4BTN_IL_CMD_2 0x00e3 +#define RT5668_4BTN_IL_CMD_3 0x00e4 +#define RT5668_4BTN_IL_CMD_4 0x00e5 +#define RT5668_4BTN_IL_CMD_5 0x00e6 +#define RT5668_4BTN_IL_CMD_6 0x00e7 +#define RT5668_4BTN_IL_CMD_7 0x00e8 + +#define RT5668_ADC_STO1_HP_CTRL_1 0x00ea +#define RT5668_ADC_STO1_HP_CTRL_2 0x00eb +#define RT5668_AJD1_CTRL 0x00f0 +#define RT5668_JD1_THD 0x00f1 +#define RT5668_JD2_THD 0x00f2 +#define RT5668_JD_CTRL_1 0x00f6 +/* General Control */ +#define RT5668_DUMMY_1 0x00fa +#define RT5668_DUMMY_2 0x00fb +#define RT5668_DUMMY_3 0x00fc + +#define RT5668_DAC_ADC_DIG_VOL1 0x0100 +#define RT5668_BIAS_CUR_CTRL_2 0x010b +#define RT5668_BIAS_CUR_CTRL_3 0x010c +#define RT5668_BIAS_CUR_CTRL_4 0x010d +#define RT5668_BIAS_CUR_CTRL_5 0x010e +#define RT5668_BIAS_CUR_CTRL_6 0x010f +#define RT5668_BIAS_CUR_CTRL_7 0x0110 +#define RT5668_BIAS_CUR_CTRL_8 0x0111 +#define RT5668_BIAS_CUR_CTRL_9 0x0112 +#define RT5668_BIAS_CUR_CTRL_10 0x0113 +#define RT5668_VREF_REC_OP_FB_CAP_CTRL 0x0117 +#define RT5668_CHARGE_PUMP_1 0x0125 +#define RT5668_DIG_IN_CTRL_1 0x0132 +#define RT5668_PAD_DRIVING_CTRL 0x0136 +#define RT5668_SOFT_RAMP_DEPOP 0x0138 +#define RT5668_CHOP_DAC 0x013a +#define RT5668_CHOP_ADC 0x013b +#define RT5668_CALIB_ADC_CTRL 0x013c +#define RT5668_VOL_TEST 0x013f +#define RT5668_SPKVDD_DET_STA 0x0142 +#define RT5668_TEST_MODE_CTRL_1 0x0145 +#define RT5668_TEST_MODE_CTRL_2 0x0146 +#define RT5668_TEST_MODE_CTRL_3 0x0147 +#define RT5668_TEST_MODE_CTRL_4 0x0148 +#define RT5668_TEST_MODE_CTRL_5 0x0149 +#define RT5668_PLL1_INTERNAL 0x0150 +#define RT5668_PLL2_INTERNAL 0x0151 +#define RT5668_STO_NG2_CTRL_1 0x0160 +#define RT5668_STO_NG2_CTRL_2 0x0161 +#define RT5668_STO_NG2_CTRL_3 0x0162 +#define RT5668_STO_NG2_CTRL_4 0x0163 +#define RT5668_STO_NG2_CTRL_5 0x0164 +#define RT5668_STO_NG2_CTRL_6 0x0165 +#define RT5668_STO_NG2_CTRL_7 0x0166 +#define RT5668_STO_NG2_CTRL_8 0x0167 +#define RT5668_STO_NG2_CTRL_9 0x0168 +#define RT5668_STO_NG2_CTRL_10 0x0169 +#define RT5668_STO1_DAC_SIL_DET 0x0190 +#define RT5668_SIL_PSV_CTRL1 0x0194 +#define RT5668_SIL_PSV_CTRL2 0x0195 +#define RT5668_SIL_PSV_CTRL3 0x0197 +#define RT5668_SIL_PSV_CTRL4 0x0198 +#define RT5668_SIL_PSV_CTRL5 0x0199 +#define RT5668_HP_IMP_SENS_CTRL_01 0x01af +#define RT5668_HP_IMP_SENS_CTRL_02 0x01b0 +#define RT5668_HP_IMP_SENS_CTRL_03 0x01b1 +#define RT5668_HP_IMP_SENS_CTRL_04 0x01b2 +#define RT5668_HP_IMP_SENS_CTRL_05 0x01b3 +#define RT5668_HP_IMP_SENS_CTRL_06 0x01b4 +#define RT5668_HP_IMP_SENS_CTRL_07 0x01b5 +#define RT5668_HP_IMP_SENS_CTRL_08 0x01b6 +#define RT5668_HP_IMP_SENS_CTRL_09 0x01b7 +#define RT5668_HP_IMP_SENS_CTRL_10 0x01b8 +#define RT5668_HP_IMP_SENS_CTRL_11 0x01b9 +#define RT5668_HP_IMP_SENS_CTRL_12 0x01ba +#define RT5668_HP_IMP_SENS_CTRL_13 0x01bb +#define RT5668_HP_IMP_SENS_CTRL_14 0x01bc +#define RT5668_HP_IMP_SENS_CTRL_15 0x01bd +#define RT5668_HP_IMP_SENS_CTRL_16 0x01be +#define RT5668_HP_IMP_SENS_CTRL_17 0x01bf +#define RT5668_HP_IMP_SENS_CTRL_18 0x01c0 +#define RT5668_HP_IMP_SENS_CTRL_19 0x01c1 +#define RT5668_HP_IMP_SENS_CTRL_20 0x01c2 +#define RT5668_HP_IMP_SENS_CTRL_21 0x01c3 +#define RT5668_HP_IMP_SENS_CTRL_22 0x01c4 +#define RT5668_HP_IMP_SENS_CTRL_23 0x01c5 +#define RT5668_HP_IMP_SENS_CTRL_24 0x01c6 +#define RT5668_HP_IMP_SENS_CTRL_25 0x01c7 +#define RT5668_HP_IMP_SENS_CTRL_26 0x01c8 +#define RT5668_HP_IMP_SENS_CTRL_27 0x01c9 +#define RT5668_HP_IMP_SENS_CTRL_28 0x01ca +#define RT5668_HP_IMP_SENS_CTRL_29 0x01cb +#define RT5668_HP_IMP_SENS_CTRL_30 0x01cc +#define RT5668_HP_IMP_SENS_CTRL_31 0x01cd +#define RT5668_HP_IMP_SENS_CTRL_32 0x01ce +#define RT5668_HP_IMP_SENS_CTRL_33 0x01cf +#define RT5668_HP_IMP_SENS_CTRL_34 0x01d0 +#define RT5668_HP_IMP_SENS_CTRL_35 0x01d1 +#define RT5668_HP_IMP_SENS_CTRL_36 0x01d2 +#define RT5668_HP_IMP_SENS_CTRL_37 0x01d3 +#define RT5668_HP_IMP_SENS_CTRL_38 0x01d4 +#define RT5668_HP_IMP_SENS_CTRL_39 0x01d5 +#define RT5668_HP_IMP_SENS_CTRL_40 0x01d6 +#define RT5668_HP_IMP_SENS_CTRL_41 0x01d7 +#define RT5668_HP_IMP_SENS_CTRL_42 0x01d8 +#define RT5668_HP_IMP_SENS_CTRL_43 0x01d9 +#define RT5668_HP_LOGIC_CTRL_1 0x01da +#define RT5668_HP_LOGIC_CTRL_2 0x01db +#define RT5668_HP_LOGIC_CTRL_3 0x01dc +#define RT5668_HP_CALIB_CTRL_1 0x01de +#define RT5668_HP_CALIB_CTRL_2 0x01df +#define RT5668_HP_CALIB_CTRL_3 0x01e0 +#define RT5668_HP_CALIB_CTRL_4 0x01e1 +#define RT5668_HP_CALIB_CTRL_5 0x01e2 +#define RT5668_HP_CALIB_CTRL_6 0x01e3 +#define RT5668_HP_CALIB_CTRL_7 0x01e4 +#define RT5668_HP_CALIB_CTRL_9 0x01e6 +#define RT5668_HP_CALIB_CTRL_10 0x01e7 +#define RT5668_HP_CALIB_CTRL_11 0x01e8 +#define RT5668_HP_CALIB_STA_1 0x01ea +#define RT5668_HP_CALIB_STA_2 0x01eb +#define RT5668_HP_CALIB_STA_3 0x01ec +#define RT5668_HP_CALIB_STA_4 0x01ed +#define RT5668_HP_CALIB_STA_5 0x01ee +#define RT5668_HP_CALIB_STA_6 0x01ef +#define RT5668_HP_CALIB_STA_7 0x01f0 +#define RT5668_HP_CALIB_STA_8 0x01f1 +#define RT5668_HP_CALIB_STA_9 0x01f2 +#define RT5668_HP_CALIB_STA_10 0x01f3 +#define RT5668_HP_CALIB_STA_11 0x01f4 +#define RT5668_SAR_IL_CMD_1 0x0210 +#define RT5668_SAR_IL_CMD_2 0x0211 +#define RT5668_SAR_IL_CMD_3 0x0212 +#define RT5668_SAR_IL_CMD_4 0x0213 +#define RT5668_SAR_IL_CMD_5 0x0214 +#define RT5668_SAR_IL_CMD_6 0x0215 +#define RT5668_SAR_IL_CMD_7 0x0216 +#define RT5668_SAR_IL_CMD_8 0x0217 +#define RT5668_SAR_IL_CMD_9 0x0218 +#define RT5668_SAR_IL_CMD_10 0x0219 +#define RT5668_SAR_IL_CMD_11 0x021a +#define RT5668_SAR_IL_CMD_12 0x021b +#define RT5668_SAR_IL_CMD_13 0x021c +#define RT5668_EFUSE_CTRL_1 0x0250 +#define RT5668_EFUSE_CTRL_2 0x0251 +#define RT5668_EFUSE_CTRL_3 0x0252 +#define RT5668_EFUSE_CTRL_4 0x0253 +#define RT5668_EFUSE_CTRL_5 0x0254 +#define RT5668_EFUSE_CTRL_6 0x0255 +#define RT5668_EFUSE_CTRL_7 0x0256 +#define RT5668_EFUSE_CTRL_8 0x0257 +#define RT5668_EFUSE_CTRL_9 0x0258 +#define RT5668_EFUSE_CTRL_10 0x0259 +#define RT5668_EFUSE_CTRL_11 0x025a +#define RT5668_JD_TOP_VC_VTRL 0x0270 +#define RT5668_DRC1_CTRL_0 0x02ff +#define RT5668_DRC1_CTRL_1 0x0300 +#define RT5668_DRC1_CTRL_2 0x0301 +#define RT5668_DRC1_CTRL_3 0x0302 +#define RT5668_DRC1_CTRL_4 0x0303 +#define RT5668_DRC1_CTRL_5 0x0304 +#define RT5668_DRC1_CTRL_6 0x0305 +#define RT5668_DRC1_HARD_LMT_CTRL_1 0x0306 +#define RT5668_DRC1_HARD_LMT_CTRL_2 0x0307 +#define RT5668_DRC1_PRIV_1 0x0310 +#define RT5668_DRC1_PRIV_2 0x0311 +#define RT5668_DRC1_PRIV_3 0x0312 +#define RT5668_DRC1_PRIV_4 0x0313 +#define RT5668_DRC1_PRIV_5 0x0314 +#define RT5668_DRC1_PRIV_6 0x0315 +#define RT5668_DRC1_PRIV_7 0x0316 +#define RT5668_DRC1_PRIV_8 0x0317 +#define RT5668_EQ_AUTO_RCV_CTRL1 0x03c0 +#define RT5668_EQ_AUTO_RCV_CTRL2 0x03c1 +#define RT5668_EQ_AUTO_RCV_CTRL3 0x03c2 +#define RT5668_EQ_AUTO_RCV_CTRL4 0x03c3 +#define RT5668_EQ_AUTO_RCV_CTRL5 0x03c4 +#define RT5668_EQ_AUTO_RCV_CTRL6 0x03c5 +#define RT5668_EQ_AUTO_RCV_CTRL7 0x03c6 +#define RT5668_EQ_AUTO_RCV_CTRL8 0x03c7 +#define RT5668_EQ_AUTO_RCV_CTRL9 0x03c8 +#define RT5668_EQ_AUTO_RCV_CTRL10 0x03c9 +#define RT5668_EQ_AUTO_RCV_CTRL11 0x03ca +#define RT5668_EQ_AUTO_RCV_CTRL12 0x03cb +#define RT5668_EQ_AUTO_RCV_CTRL13 0x03cc +#define RT5668_ADC_L_EQ_LPF1_A1 0x03d0 +#define RT5668_R_EQ_LPF1_A1 0x03d1 +#define RT5668_L_EQ_LPF1_H0 0x03d2 +#define RT5668_R_EQ_LPF1_H0 0x03d3 +#define RT5668_L_EQ_BPF1_A1 0x03d4 +#define RT5668_R_EQ_BPF1_A1 0x03d5 +#define RT5668_L_EQ_BPF1_A2 0x03d6 +#define RT5668_R_EQ_BPF1_A2 0x03d7 +#define RT5668_L_EQ_BPF1_H0 0x03d8 +#define RT5668_R_EQ_BPF1_H0 0x03d9 +#define RT5668_L_EQ_BPF2_A1 0x03da +#define RT5668_R_EQ_BPF2_A1 0x03db +#define RT5668_L_EQ_BPF2_A2 0x03dc +#define RT5668_R_EQ_BPF2_A2 0x03dd +#define RT5668_L_EQ_BPF2_H0 0x03de +#define RT5668_R_EQ_BPF2_H0 0x03df +#define RT5668_L_EQ_BPF3_A1 0x03e0 +#define RT5668_R_EQ_BPF3_A1 0x03e1 +#define RT5668_L_EQ_BPF3_A2 0x03e2 +#define RT5668_R_EQ_BPF3_A2 0x03e3 +#define RT5668_L_EQ_BPF3_H0 0x03e4 +#define RT5668_R_EQ_BPF3_H0 0x03e5 +#define RT5668_L_EQ_BPF4_A1 0x03e6 +#define RT5668_R_EQ_BPF4_A1 0x03e7 +#define RT5668_L_EQ_BPF4_A2 0x03e8 +#define RT5668_R_EQ_BPF4_A2 0x03e9 +#define RT5668_L_EQ_BPF4_H0 0x03ea +#define RT5668_R_EQ_BPF4_H0 0x03eb +#define RT5668_L_EQ_HPF1_A1 0x03ec +#define RT5668_R_EQ_HPF1_A1 0x03ed +#define RT5668_L_EQ_HPF1_H0 0x03ee +#define RT5668_R_EQ_HPF1_H0 0x03ef +#define RT5668_L_EQ_PRE_VOL 0x03f0 +#define RT5668_R_EQ_PRE_VOL 0x03f1 +#define RT5668_L_EQ_POST_VOL 0x03f2 +#define RT5668_R_EQ_POST_VOL 0x03f3 +#define RT5668_I2C_MODE 0xffff + + +/* global definition */ +#define RT5668_L_MUTE (0x1 << 15) +#define RT5668_L_MUTE_SFT 15 +#define RT5668_VOL_L_MUTE (0x1 << 14) +#define RT5668_VOL_L_SFT 14 +#define RT5668_R_MUTE (0x1 << 7) +#define RT5668_R_MUTE_SFT 7 +#define RT5668_VOL_R_MUTE (0x1 << 6) +#define RT5668_VOL_R_SFT 6 +#define RT5668_L_VOL_MASK (0x3f << 8) +#define RT5668_L_VOL_SFT 8 +#define RT5668_R_VOL_MASK (0x3f) +#define RT5668_R_VOL_SFT 0 + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5668_G_HP (0xf << 8) +#define RT5668_G_HP_SFT 8 +#define RT5668_G_STO_DA_DMIX (0xf) +#define RT5668_G_STO_DA_SFT 0 + +/* CBJ Control (0x000b) */ +#define RT5668_BST_CBJ_MASK (0xf << 8) +#define RT5668_BST_CBJ_SFT 8 + +/* Embeeded Jack and Type Detection Control 1 (0x0010) */ +#define RT5668_EMB_JD_EN (0x1 << 15) +#define RT5668_EMB_JD_EN_SFT 15 +#define RT5668_EMB_JD_RST (0x1 << 14) +#define RT5668_JD_MODE (0x1 << 13) +#define RT5668_JD_MODE_SFT 13 +#define RT5668_DET_TYPE (0x1 << 12) +#define RT5668_DET_TYPE_SFT 12 +#define RT5668_POLA_EXT_JD_MASK (0x1 << 11) +#define RT5668_POLA_EXT_JD_LOW (0x1 << 11) +#define RT5668_POLA_EXT_JD_HIGH (0x0 << 11) +#define RT5668_EXT_JD_DIG (0x1 << 9) +#define RT5668_POL_FAST_OFF_MASK (0x1 << 8) +#define RT5668_POL_FAST_OFF_HIGH (0x1 << 8) +#define RT5668_POL_FAST_OFF_LOW (0x0 << 8) +#define RT5668_FAST_OFF_MASK (0x1 << 7) +#define RT5668_FAST_OFF_EN (0x1 << 7) +#define RT5668_FAST_OFF_DIS (0x0 << 7) +#define RT5668_VREF_POW_MASK (0x1 << 6) +#define RT5668_VREF_POW_FSM (0x0 << 6) +#define RT5668_VREF_POW_REG (0x1 << 6) +#define RT5668_MB1_PATH_MASK (0x1 << 5) +#define RT5668_CTRL_MB1_REG (0x1 << 5) +#define RT5668_CTRL_MB1_FSM (0x0 << 5) +#define RT5668_MB2_PATH_MASK (0x1 << 4) +#define RT5668_CTRL_MB2_REG (0x1 << 4) +#define RT5668_CTRL_MB2_FSM (0x0 << 4) +#define RT5668_TRIG_JD_MASK (0x1 << 3) +#define RT5668_TRIG_JD_HIGH (0x1 << 3) +#define RT5668_TRIG_JD_LOW (0x0 << 3) +#define RT5668_MIC_CAP_MASK (0x1 << 1) +#define RT5668_MIC_CAP_HS (0x1 << 1) +#define RT5668_MIC_CAP_HP (0x0 << 1) +#define RT5668_MIC_CAP_SRC_MASK (0x1) +#define RT5668_MIC_CAP_SRC_REG (0x1) +#define RT5668_MIC_CAP_SRC_ANA (0x0) + +/* Embeeded Jack and Type Detection Control 2 (0x0011) */ +#define RT5668_EXT_JD_SRC (0x7 << 4) +#define RT5668_EXT_JD_SRC_SFT 4 +#define RT5668_EXT_JD_SRC_GPIO_JD1 (0x0 << 4) +#define RT5668_EXT_JD_SRC_GPIO_JD2 (0x1 << 4) +#define RT5668_EXT_JD_SRC_JDH (0x2 << 4) +#define RT5668_EXT_JD_SRC_JDL (0x3 << 4) +#define RT5668_EXT_JD_SRC_MANUAL (0x4 << 4) +#define RT5668_JACK_TYPE_MASK (0x3) + +/* Combo Jack and Type Detection Control 3 (0x0012) */ +#define RT5668_CBJ_IN_BUF_EN (0x1 << 7) + +/* Combo Jack and Type Detection Control 4 (0x0013) */ +#define RT5668_SEL_SHT_MID_TON_MASK (0x3 << 12) +#define RT5668_SEL_SHT_MID_TON_2 (0x0 << 12) +#define RT5668_SEL_SHT_MID_TON_3 (0x1 << 12) +#define RT5668_CBJ_JD_TEST_MASK (0x1 << 6) +#define RT5668_CBJ_JD_TEST_NORM (0x0 << 6) +#define RT5668_CBJ_JD_TEST_MODE (0x1 << 6) + +/* DAC1 Digital Volume (0x0019) */ +#define RT5668_DAC_L1_VOL_MASK (0xff << 8) +#define RT5668_DAC_L1_VOL_SFT 8 +#define RT5668_DAC_R1_VOL_MASK (0xff) +#define RT5668_DAC_R1_VOL_SFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5668_ADC_L_VOL_MASK (0x7f << 8) +#define RT5668_ADC_L_VOL_SFT 8 +#define RT5668_ADC_R_VOL_MASK (0x7f) +#define RT5668_ADC_R_VOL_SFT 0 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5668_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5668_STO1_ADC_L_BST_SFT 14 +#define RT5668_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5668_STO1_ADC_R_BST_SFT 12 + +/* Sidetone Control (0x0024) */ +#define RT5668_ST_SRC_SEL (0x1 << 8) +#define RT5668_ST_SRC_SFT 8 +#define RT5668_ST_EN_MASK (0x1 << 6) +#define RT5668_ST_DIS (0x0 << 6) +#define RT5668_ST_EN (0x1 << 6) +#define RT5668_ST_EN_SFT 6 + +/* Stereo1 ADC Mixer Control (0x0026) */ +#define RT5668_M_STO1_ADC_L1 (0x1 << 15) +#define RT5668_M_STO1_ADC_L1_SFT 15 +#define RT5668_M_STO1_ADC_L2 (0x1 << 14) +#define RT5668_M_STO1_ADC_L2_SFT 14 +#define RT5668_STO1_ADC1L_SRC_MASK (0x1 << 13) +#define RT5668_STO1_ADC1L_SRC_SFT 13 +#define RT5668_STO1_ADC1_SRC_ADC (0x1 << 13) +#define RT5668_STO1_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5668_STO1_ADC2L_SRC_MASK (0x1 << 12) +#define RT5668_STO1_ADC2L_SRC_SFT 12 +#define RT5668_STO1_ADCL_SRC_MASK (0x3 << 10) +#define RT5668_STO1_ADCL_SRC_SFT 10 +#define RT5668_STO1_DD_L_SRC_MASK (0x1 << 9) +#define RT5668_STO1_DD_L_SRC_SFT 9 +#define RT5668_STO1_DMIC_SRC_MASK (0x1 << 8) +#define RT5668_STO1_DMIC_SRC_SFT 8 +#define RT5668_STO1_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5668_STO1_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5668_M_STO1_ADC_R1 (0x1 << 7) +#define RT5668_M_STO1_ADC_R1_SFT 7 +#define RT5668_M_STO1_ADC_R2 (0x1 << 6) +#define RT5668_M_STO1_ADC_R2_SFT 6 +#define RT5668_STO1_ADC1R_SRC_MASK (0x1 << 5) +#define RT5668_STO1_ADC1R_SRC_SFT 5 +#define RT5668_STO1_ADC2R_SRC_MASK (0x1 << 4) +#define RT5668_STO1_ADC2R_SRC_SFT 4 +#define RT5668_STO1_ADCR_SRC_MASK (0x3 << 2) +#define RT5668_STO1_ADCR_SRC_SFT 2 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5668_M_ADCMIX_L (0x1 << 15) +#define RT5668_M_ADCMIX_L_SFT 15 +#define RT5668_M_DAC1_L (0x1 << 14) +#define RT5668_M_DAC1_L_SFT 14 +#define RT5668_DAC1_R_SEL_MASK (0x1 << 10) +#define RT5668_DAC1_R_SEL_SFT 10 +#define RT5668_DAC1_L_SEL_MASK (0x1 << 8) +#define RT5668_DAC1_L_SEL_SFT 8 +#define RT5668_M_ADCMIX_R (0x1 << 7) +#define RT5668_M_ADCMIX_R_SFT 7 +#define RT5668_M_DAC1_R (0x1 << 6) +#define RT5668_M_DAC1_R_SFT 6 + +/* Stereo1 DAC Mixer Control (0x002a) */ +#define RT5668_M_DAC_L1_STO_L (0x1 << 15) +#define RT5668_M_DAC_L1_STO_L_SFT 15 +#define RT5668_G_DAC_L1_STO_L_MASK (0x1 << 14) +#define RT5668_G_DAC_L1_STO_L_SFT 14 +#define RT5668_M_DAC_R1_STO_L (0x1 << 13) +#define RT5668_M_DAC_R1_STO_L_SFT 13 +#define RT5668_G_DAC_R1_STO_L_MASK (0x1 << 12) +#define RT5668_G_DAC_R1_STO_L_SFT 12 +#define RT5668_M_DAC_L1_STO_R (0x1 << 7) +#define RT5668_M_DAC_L1_STO_R_SFT 7 +#define RT5668_G_DAC_L1_STO_R_MASK (0x1 << 6) +#define RT5668_G_DAC_L1_STO_R_SFT 6 +#define RT5668_M_DAC_R1_STO_R (0x1 << 5) +#define RT5668_M_DAC_R1_STO_R_SFT 5 +#define RT5668_G_DAC_R1_STO_R_MASK (0x1 << 4) +#define RT5668_G_DAC_R1_STO_R_SFT 4 + +/* Analog DAC1 Input Source Control (0x002b) */ +#define RT5668_M_ST_STO_L (0x1 << 9) +#define RT5668_M_ST_STO_L_SFT 9 +#define RT5668_M_ST_STO_R (0x1 << 8) +#define RT5668_M_ST_STO_R_SFT 8 +#define RT5668_DAC_L1_SRC_MASK (0x3 << 4) +#define RT5668_A_DACL1_SFT 4 +#define RT5668_DAC_R1_SRC_MASK (0x3) +#define RT5668_A_DACR1_SFT 0 + +/* Digital Interface Data Control (0x0030) */ +#define RT5668_IF2_ADC_SEL_MASK (0x3 << 0) +#define RT5668_IF2_ADC_SEL_SFT 0 + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5668_G_CBJ_RM1_L (0x7 << 10) +#define RT5668_G_CBJ_RM1_L_SFT 10 +#define RT5668_M_CBJ_RM1_L (0x1 << 7) +#define RT5668_M_CBJ_RM1_L_SFT 7 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5668_PWR_I2S1 (0x1 << 15) +#define RT5668_PWR_I2S1_BIT 15 +#define RT5668_PWR_I2S2 (0x1 << 14) +#define RT5668_PWR_I2S2_BIT 14 +#define RT5668_PWR_DAC_L1 (0x1 << 11) +#define RT5668_PWR_DAC_L1_BIT 11 +#define RT5668_PWR_DAC_R1 (0x1 << 10) +#define RT5668_PWR_DAC_R1_BIT 10 +#define RT5668_PWR_LDO (0x1 << 8) +#define RT5668_PWR_LDO_BIT 8 +#define RT5668_PWR_ADC_L1 (0x1 << 4) +#define RT5668_PWR_ADC_L1_BIT 4 +#define RT5668_PWR_ADC_R1 (0x1 << 3) +#define RT5668_PWR_ADC_R1_BIT 3 +#define RT5668_DIG_GATE_CTRL (0x1 << 0) +#define RT5668_DIG_GATE_CTRL_SFT 0 + + +/* Power Management for Digital 2 (0x0062) */ +#define RT5668_PWR_ADC_S1F (0x1 << 15) +#define RT5668_PWR_ADC_S1F_BIT 15 +#define RT5668_PWR_DAC_S1F (0x1 << 10) +#define RT5668_PWR_DAC_S1F_BIT 10 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5668_PWR_VREF1 (0x1 << 15) +#define RT5668_PWR_VREF1_BIT 15 +#define RT5668_PWR_FV1 (0x1 << 14) +#define RT5668_PWR_FV1_BIT 14 +#define RT5668_PWR_VREF2 (0x1 << 13) +#define RT5668_PWR_VREF2_BIT 13 +#define RT5668_PWR_FV2 (0x1 << 12) +#define RT5668_PWR_FV2_BIT 12 +#define RT5668_LDO1_DBG_MASK (0x3 << 10) +#define RT5668_PWR_MB (0x1 << 9) +#define RT5668_PWR_MB_BIT 9 +#define RT5668_PWR_BG (0x1 << 7) +#define RT5668_PWR_BG_BIT 7 +#define RT5668_LDO1_BYPASS_MASK (0x1 << 6) +#define RT5668_LDO1_BYPASS (0x1 << 6) +#define RT5668_LDO1_NOT_BYPASS (0x0 << 6) +#define RT5668_PWR_MA_BIT 6 +#define RT5668_LDO1_DVO_MASK (0x3 << 4) +#define RT5668_LDO1_DVO_09 (0x0 << 4) +#define RT5668_LDO1_DVO_10 (0x1 << 4) +#define RT5668_LDO1_DVO_12 (0x2 << 4) +#define RT5668_LDO1_DVO_14 (0x3 << 4) +#define RT5668_HP_DRIVER_MASK (0x3 << 2) +#define RT5668_HP_DRIVER_1X (0x0 << 2) +#define RT5668_HP_DRIVER_3X (0x1 << 2) +#define RT5668_HP_DRIVER_5X (0x3 << 2) +#define RT5668_PWR_HA_L (0x1 << 1) +#define RT5668_PWR_HA_L_BIT 1 +#define RT5668_PWR_HA_R (0x1 << 0) +#define RT5668_PWR_HA_R_BIT 0 + +/* Power Management for Analog 2 (0x0064) */ +#define RT5668_PWR_MB1 (0x1 << 11) +#define RT5668_PWR_MB1_PWR_DOWN (0x0 << 11) +#define RT5668_PWR_MB1_BIT 11 +#define RT5668_PWR_MB2 (0x1 << 10) +#define RT5668_PWR_MB2_PWR_DOWN (0x0 << 10) +#define RT5668_PWR_MB2_BIT 10 +#define RT5668_PWR_JDH (0x1 << 3) +#define RT5668_PWR_JDH_BIT 3 +#define RT5668_PWR_JDL (0x1 << 2) +#define RT5668_PWR_JDL_BIT 2 +#define RT5668_PWR_RM1_L (0x1 << 1) +#define RT5668_PWR_RM1_L_BIT 1 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5668_PWR_CBJ (0x1 << 9) +#define RT5668_PWR_CBJ_BIT 9 +#define RT5668_PWR_PLL (0x1 << 6) +#define RT5668_PWR_PLL_BIT 6 +#define RT5668_PWR_PLL2B (0x1 << 5) +#define RT5668_PWR_PLL2B_BIT 5 +#define RT5668_PWR_PLL2F (0x1 << 4) +#define RT5668_PWR_PLL2F_BIT 4 +#define RT5668_PWR_LDO2 (0x1 << 2) +#define RT5668_PWR_LDO2_BIT 2 +#define RT5668_PWR_DET_SPKVDD (0x1 << 1) +#define RT5668_PWR_DET_SPKVDD_BIT 1 + +/* Power Management for Mixer (0x0066) */ +#define RT5668_PWR_STO1_DAC_L (0x1 << 5) +#define RT5668_PWR_STO1_DAC_L_BIT 5 +#define RT5668_PWR_STO1_DAC_R (0x1 << 4) +#define RT5668_PWR_STO1_DAC_R_BIT 4 + +/* MCLK and System Clock Detection Control (0x006b) */ +#define RT5668_SYS_CLK_DET (0x1 << 15) +#define RT5668_SYS_CLK_DET_SFT 15 +#define RT5668_PLL1_CLK_DET (0x1 << 14) +#define RT5668_PLL1_CLK_DET_SFT 14 +#define RT5668_PLL2_CLK_DET (0x1 << 13) +#define RT5668_PLL2_CLK_DET_SFT 13 +#define RT5668_POW_CLK_DET2_SFT 8 +#define RT5668_POW_CLK_DET_SFT 0 + +/* Digital Microphone Control 1 (0x006e) */ +#define RT5668_DMIC_1_EN_MASK (0x1 << 15) +#define RT5668_DMIC_1_EN_SFT 15 +#define RT5668_DMIC_1_DIS (0x0 << 15) +#define RT5668_DMIC_1_EN (0x1 << 15) +#define RT5668_DMIC_1_DP_MASK (0x3 << 4) +#define RT5668_DMIC_1_DP_SFT 4 +#define RT5668_DMIC_1_DP_GPIO2 (0x0 << 4) +#define RT5668_DMIC_1_DP_GPIO5 (0x1 << 4) +#define RT5668_DMIC_CLK_MASK (0xf << 0) +#define RT5668_DMIC_CLK_SFT 0 + +/* I2S1 Audio Serial Data Port Control (0x0070) */ +#define RT5668_SEL_ADCDAT_MASK (0x1 << 15) +#define RT5668_SEL_ADCDAT_OUT (0x0 << 15) +#define RT5668_SEL_ADCDAT_IN (0x1 << 15) +#define RT5668_SEL_ADCDAT_SFT 15 +#define RT5668_I2S1_TX_CHL_MASK (0x7 << 12) +#define RT5668_I2S1_TX_CHL_SFT 12 +#define RT5668_I2S1_TX_CHL_16 (0x0 << 12) +#define RT5668_I2S1_TX_CHL_20 (0x1 << 12) +#define RT5668_I2S1_TX_CHL_24 (0x2 << 12) +#define RT5668_I2S1_TX_CHL_32 (0x3 << 12) +#define RT5668_I2S1_TX_CHL_8 (0x4 << 12) +#define RT5668_I2S1_RX_CHL_MASK (0x7 << 8) +#define RT5668_I2S1_RX_CHL_SFT 8 +#define RT5668_I2S1_RX_CHL_16 (0x0 << 8) +#define RT5668_I2S1_RX_CHL_20 (0x1 << 8) +#define RT5668_I2S1_RX_CHL_24 (0x2 << 8) +#define RT5668_I2S1_RX_CHL_32 (0x3 << 8) +#define RT5668_I2S1_RX_CHL_8 (0x4 << 8) +#define RT5668_I2S1_MONO_MASK (0x1 << 7) +#define RT5668_I2S1_MONO_EN (0x1 << 7) +#define RT5668_I2S1_MONO_DIS (0x0 << 7) +#define RT5668_I2S2_MONO_MASK (0x1 << 6) +#define RT5668_I2S2_MONO_EN (0x1 << 6) +#define RT5668_I2S2_MONO_DIS (0x0 << 6) +#define RT5668_I2S1_DL_MASK (0x7 << 4) +#define RT5668_I2S1_DL_SFT 4 +#define RT5668_I2S1_DL_16 (0x0 << 4) +#define RT5668_I2S1_DL_20 (0x1 << 4) +#define RT5668_I2S1_DL_24 (0x2 << 4) +#define RT5668_I2S1_DL_32 (0x3 << 4) +#define RT5668_I2S1_DL_8 (0x4 << 4) + +/* I2S1/2 Audio Serial Data Port Control (0x0070)(0x0071) */ +#define RT5668_I2S2_MS_MASK (0x1 << 15) +#define RT5668_I2S2_MS_SFT 15 +#define RT5668_I2S2_MS_M (0x0 << 15) +#define RT5668_I2S2_MS_S (0x1 << 15) +#define RT5668_I2S2_PIN_CFG_MASK (0x1 << 14) +#define RT5668_I2S2_PIN_CFG_SFT 14 +#define RT5668_I2S2_CLK_SEL_MASK (0x1 << 11) +#define RT5668_I2S2_CLK_SEL_SFT 11 +#define RT5668_I2S2_OUT_MASK (0x1 << 9) +#define RT5668_I2S2_OUT_SFT 9 +#define RT5668_I2S2_OUT_UM (0x0 << 9) +#define RT5668_I2S2_OUT_M (0x1 << 9) +#define RT5668_I2S_BP_MASK (0x1 << 8) +#define RT5668_I2S_BP_SFT 8 +#define RT5668_I2S_BP_NOR (0x0 << 8) +#define RT5668_I2S_BP_INV (0x1 << 8) +#define RT5668_I2S2_MONO_EN (0x1 << 6) +#define RT5668_I2S2_MONO_DIS (0x0 << 6) +#define RT5668_I2S2_DL_MASK (0x3 << 4) +#define RT5668_I2S2_DL_SFT 4 +#define RT5668_I2S2_DL_16 (0x0 << 4) +#define RT5668_I2S2_DL_20 (0x1 << 4) +#define RT5668_I2S2_DL_24 (0x2 << 4) +#define RT5668_I2S2_DL_8 (0x3 << 4) +#define RT5668_I2S_DF_MASK (0x7) +#define RT5668_I2S_DF_SFT 0 +#define RT5668_I2S_DF_I2S (0x0) +#define RT5668_I2S_DF_LEFT (0x1) +#define RT5668_I2S_DF_PCM_A (0x2) +#define RT5668_I2S_DF_PCM_B (0x3) +#define RT5668_I2S_DF_PCM_A_N (0x6) +#define RT5668_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5668_ADC_OSR_MASK (0xf << 12) +#define RT5668_ADC_OSR_SFT 12 +#define RT5668_ADC_OSR_D_1 (0x0 << 12) +#define RT5668_ADC_OSR_D_2 (0x1 << 12) +#define RT5668_ADC_OSR_D_4 (0x2 << 12) +#define RT5668_ADC_OSR_D_6 (0x3 << 12) +#define RT5668_ADC_OSR_D_8 (0x4 << 12) +#define RT5668_ADC_OSR_D_12 (0x5 << 12) +#define RT5668_ADC_OSR_D_16 (0x6 << 12) +#define RT5668_ADC_OSR_D_24 (0x7 << 12) +#define RT5668_ADC_OSR_D_32 (0x8 << 12) +#define RT5668_ADC_OSR_D_48 (0x9 << 12) +#define RT5668_I2S_M_DIV_MASK (0xf << 12) +#define RT5668_I2S_M_DIV_SFT 8 +#define RT5668_I2S_M_D_1 (0x0 << 8) +#define RT5668_I2S_M_D_2 (0x1 << 8) +#define RT5668_I2S_M_D_3 (0x2 << 8) +#define RT5668_I2S_M_D_4 (0x3 << 8) +#define RT5668_I2S_M_D_6 (0x4 << 8) +#define RT5668_I2S_M_D_8 (0x5 << 8) +#define RT5668_I2S_M_D_12 (0x6 << 8) +#define RT5668_I2S_M_D_16 (0x7 << 8) +#define RT5668_I2S_M_D_24 (0x8 << 8) +#define RT5668_I2S_M_D_32 (0x9 << 8) +#define RT5668_I2S_M_D_48 (0x10 << 8) +#define RT5668_I2S_CLK_SRC_MASK (0x7 << 4) +#define RT5668_I2S_CLK_SRC_SFT 4 +#define RT5668_I2S_CLK_SRC_MCLK (0x0 << 4) +#define RT5668_I2S_CLK_SRC_PLL1 (0x1 << 4) +#define RT5668_I2S_CLK_SRC_PLL2 (0x2 << 4) +#define RT5668_I2S_CLK_SRC_SDW (0x3 << 4) +#define RT5668_I2S_CLK_SRC_RCCLK (0x4 << 4) /* 25M */ +#define RT5668_DAC_OSR_MASK (0xf << 0) +#define RT5668_DAC_OSR_SFT 0 +#define RT5668_DAC_OSR_D_1 (0x0 << 0) +#define RT5668_DAC_OSR_D_2 (0x1 << 0) +#define RT5668_DAC_OSR_D_4 (0x2 << 0) +#define RT5668_DAC_OSR_D_6 (0x3 << 0) +#define RT5668_DAC_OSR_D_8 (0x4 << 0) +#define RT5668_DAC_OSR_D_12 (0x5 << 0) +#define RT5668_DAC_OSR_D_16 (0x6 << 0) +#define RT5668_DAC_OSR_D_24 (0x7 << 0) +#define RT5668_DAC_OSR_D_32 (0x8 << 0) +#define RT5668_DAC_OSR_D_48 (0x9 << 0) + +/* ADC/DAC Clock Control 2 (0x0074) */ +#define RT5668_I2S2_BCLK_MS2_MASK (0x1 << 11) +#define RT5668_I2S2_BCLK_MS2_SFT 11 +#define RT5668_I2S2_BCLK_MS2_32 (0x0 << 11) +#define RT5668_I2S2_BCLK_MS2_64 (0x1 << 11) + + +/* TDM control 1 (0x0079) */ +#define RT5668_TDM_TX_CH_MASK (0x3 << 12) +#define RT5668_TDM_TX_CH_2 (0x0 << 12) +#define RT5668_TDM_TX_CH_4 (0x1 << 12) +#define RT5668_TDM_TX_CH_6 (0x2 << 12) +#define RT5668_TDM_TX_CH_8 (0x3 << 12) +#define RT5668_TDM_RX_CH_MASK (0x3 << 8) +#define RT5668_TDM_RX_CH_2 (0x0 << 8) +#define RT5668_TDM_RX_CH_4 (0x1 << 8) +#define RT5668_TDM_RX_CH_6 (0x2 << 8) +#define RT5668_TDM_RX_CH_8 (0x3 << 8) +#define RT5668_TDM_ADC_LCA_MASK (0xf << 4) +#define RT5668_TDM_ADC_LCA_SFT 4 +#define RT5668_TDM_ADC_DL_SFT 0 + +/* TDM control 3 (0x007a) */ +#define RT5668_IF1_ADC1_SEL_SFT 14 +#define RT5668_IF1_ADC2_SEL_SFT 12 +#define RT5668_IF1_ADC3_SEL_SFT 10 +#define RT5668_IF1_ADC4_SEL_SFT 8 +#define RT5668_TDM_ADC_SEL_SFT 4 + +/* TDM/I2S control (0x007e) */ +#define RT5668_TDM_S_BP_MASK (0x1 << 15) +#define RT5668_TDM_S_BP_SFT 15 +#define RT5668_TDM_S_BP_NOR (0x0 << 15) +#define RT5668_TDM_S_BP_INV (0x1 << 15) +#define RT5668_TDM_S_LP_MASK (0x1 << 14) +#define RT5668_TDM_S_LP_SFT 14 +#define RT5668_TDM_S_LP_NOR (0x0 << 14) +#define RT5668_TDM_S_LP_INV (0x1 << 14) +#define RT5668_TDM_DF_MASK (0x7 << 11) +#define RT5668_TDM_DF_SFT 11 +#define RT5668_TDM_DF_I2S (0x0 << 11) +#define RT5668_TDM_DF_LEFT (0x1 << 11) +#define RT5668_TDM_DF_PCM_A (0x2 << 11) +#define RT5668_TDM_DF_PCM_B (0x3 << 11) +#define RT5668_TDM_DF_PCM_A_N (0x6 << 11) +#define RT5668_TDM_DF_PCM_B_N (0x7 << 11) +#define RT5668_TDM_CL_MASK (0x3 << 4) +#define RT5668_TDM_CL_16 (0x0 << 4) +#define RT5668_TDM_CL_20 (0x1 << 4) +#define RT5668_TDM_CL_24 (0x2 << 4) +#define RT5668_TDM_CL_32 (0x3 << 4) +#define RT5668_TDM_M_BP_MASK (0x1 << 2) +#define RT5668_TDM_M_BP_SFT 2 +#define RT5668_TDM_M_BP_NOR (0x0 << 2) +#define RT5668_TDM_M_BP_INV (0x1 << 2) +#define RT5668_TDM_M_LP_MASK (0x1 << 1) +#define RT5668_TDM_M_LP_SFT 1 +#define RT5668_TDM_M_LP_NOR (0x0 << 1) +#define RT5668_TDM_M_LP_INV (0x1 << 1) +#define RT5668_TDM_MS_MASK (0x1 << 0) +#define RT5668_TDM_MS_SFT 0 +#define RT5668_TDM_MS_M (0x0 << 0) +#define RT5668_TDM_MS_S (0x1 << 0) + +/* Global Clock Control (0x0080) */ +#define RT5668_SCLK_SRC_MASK (0x7 << 13) +#define RT5668_SCLK_SRC_SFT 13 +#define RT5668_SCLK_SRC_MCLK (0x0 << 13) +#define RT5668_SCLK_SRC_PLL1 (0x1 << 13) +#define RT5668_SCLK_SRC_PLL2 (0x2 << 13) +#define RT5668_SCLK_SRC_SDW (0x3 << 13) +#define RT5668_SCLK_SRC_RCCLK (0x4 << 13) +#define RT5668_PLL1_SRC_MASK (0x3 << 10) +#define RT5668_PLL1_SRC_SFT 10 +#define RT5668_PLL1_SRC_MCLK (0x0 << 10) +#define RT5668_PLL1_SRC_BCLK1 (0x1 << 10) +#define RT5668_PLL1_SRC_SDW (0x2 << 10) +#define RT5668_PLL1_SRC_RC (0x3 << 10) +#define RT5668_PLL2_SRC_MASK (0x3 << 8) +#define RT5668_PLL2_SRC_SFT 8 +#define RT5668_PLL2_SRC_MCLK (0x0 << 8) +#define RT5668_PLL2_SRC_BCLK1 (0x1 << 8) +#define RT5668_PLL2_SRC_SDW (0x2 << 8) +#define RT5668_PLL2_SRC_RC (0x3 << 8) + + + +#define RT5668_PLL_INP_MAX 40000000 +#define RT5668_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5668_PLL_N_MAX 0x001ff +#define RT5668_PLL_N_MASK (RT5668_PLL_N_MAX << 7) +#define RT5668_PLL_N_SFT 7 +#define RT5668_PLL_K_MAX 0x001f +#define RT5668_PLL_K_MASK (RT5668_PLL_K_MAX) +#define RT5668_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5668_PLL_M_MAX 0x00f +#define RT5668_PLL_M_MASK (RT5668_PLL_M_MAX << 12) +#define RT5668_PLL_M_SFT 12 +#define RT5668_PLL_M_BP (0x1 << 11) +#define RT5668_PLL_M_BP_SFT 11 +#define RT5668_PLL_K_BP (0x1 << 10) +#define RT5668_PLL_K_BP_SFT 10 + +/* PLL tracking mode 1 (0x0083) */ +#define RT5668_DA_ASRC_MASK (0x1 << 13) +#define RT5668_DA_ASRC_SFT 13 +#define RT5668_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5668_DAC_STO1_ASRC_SFT 12 +#define RT5668_AD_ASRC_MASK (0x1 << 8) +#define RT5668_AD_ASRC_SFT 8 +#define RT5668_AD_ASRC_SEL_MASK (0x1 << 4) +#define RT5668_AD_ASRC_SEL_SFT 4 +#define RT5668_DMIC_ASRC_MASK (0x1 << 3) +#define RT5668_DMIC_ASRC_SFT 3 +#define RT5668_ADC_STO1_ASRC_MASK (0x1 << 2) +#define RT5668_ADC_STO1_ASRC_SFT 2 +#define RT5668_DA_ASRC_SEL_MASK (0x1 << 0) +#define RT5668_DA_ASRC_SEL_SFT 0 + +/* PLL tracking mode 2 3 (0x0084)(0x0085)*/ +#define RT5668_FILTER_CLK_SEL_MASK (0x7 << 12) +#define RT5668_FILTER_CLK_SEL_SFT 12 + +/* ASRC Control 4 (0x0086) */ +#define RT5668_ASRCIN_FTK_N1_MASK (0x3 << 14) +#define RT5668_ASRCIN_FTK_N1_SFT 14 +#define RT5668_ASRCIN_FTK_N2_MASK (0x3 << 12) +#define RT5668_ASRCIN_FTK_N2_SFT 12 +#define RT5668_ASRCIN_FTK_M1_MASK (0x7 << 8) +#define RT5668_ASRCIN_FTK_M1_SFT 8 +#define RT5668_ASRCIN_FTK_M2_MASK (0x7 << 4) +#define RT5668_ASRCIN_FTK_M2_SFT 4 + +/* SoundWire reference clk (0x008d) */ +#define RT5668_PLL2_OUT_MASK (0x1 << 8) +#define RT5668_PLL2_OUT_98M (0x0 << 8) +#define RT5668_PLL2_OUT_49M (0x1 << 8) +#define RT5668_SDW_REF_2_MASK (0xf << 4) +#define RT5668_SDW_REF_2_SFT 4 +#define RT5668_SDW_REF_2_48K (0x0 << 4) +#define RT5668_SDW_REF_2_96K (0x1 << 4) +#define RT5668_SDW_REF_2_192K (0x2 << 4) +#define RT5668_SDW_REF_2_32K (0x3 << 4) +#define RT5668_SDW_REF_2_24K (0x4 << 4) +#define RT5668_SDW_REF_2_16K (0x5 << 4) +#define RT5668_SDW_REF_2_12K (0x6 << 4) +#define RT5668_SDW_REF_2_8K (0x7 << 4) +#define RT5668_SDW_REF_2_44K (0x8 << 4) +#define RT5668_SDW_REF_2_88K (0x9 << 4) +#define RT5668_SDW_REF_2_176K (0xa << 4) +#define RT5668_SDW_REF_2_353K (0xb << 4) +#define RT5668_SDW_REF_2_22K (0xc << 4) +#define RT5668_SDW_REF_2_384K (0xd << 4) +#define RT5668_SDW_REF_2_11K (0xe << 4) +#define RT5668_SDW_REF_1_MASK (0xf << 0) +#define RT5668_SDW_REF_1_SFT 0 +#define RT5668_SDW_REF_1_48K (0x0 << 0) +#define RT5668_SDW_REF_1_96K (0x1 << 0) +#define RT5668_SDW_REF_1_192K (0x2 << 0) +#define RT5668_SDW_REF_1_32K (0x3 << 0) +#define RT5668_SDW_REF_1_24K (0x4 << 0) +#define RT5668_SDW_REF_1_16K (0x5 << 0) +#define RT5668_SDW_REF_1_12K (0x6 << 0) +#define RT5668_SDW_REF_1_8K (0x7 << 0) +#define RT5668_SDW_REF_1_44K (0x8 << 0) +#define RT5668_SDW_REF_1_88K (0x9 << 0) +#define RT5668_SDW_REF_1_176K (0xa << 0) +#define RT5668_SDW_REF_1_353K (0xb << 0) +#define RT5668_SDW_REF_1_22K (0xc << 0) +#define RT5668_SDW_REF_1_384K (0xd << 0) +#define RT5668_SDW_REF_1_11K (0xe << 0) + +/* Depop Mode Control 1 (0x008e) */ +#define RT5668_PUMP_EN (0x1 << 3) +#define RT5668_PUMP_EN_SFT 3 +#define RT5668_CAPLESS_EN (0x1 << 0) +#define RT5668_CAPLESS_EN_SFT 0 + +/* Depop Mode Control 2 (0x8f) */ +#define RT5668_RAMP_MASK (0x1 << 12) +#define RT5668_RAMP_SFT 12 +#define RT5668_RAMP_DIS (0x0 << 12) +#define RT5668_RAMP_EN (0x1 << 12) +#define RT5668_BPS_MASK (0x1 << 11) +#define RT5668_BPS_SFT 11 +#define RT5668_BPS_DIS (0x0 << 11) +#define RT5668_BPS_EN (0x1 << 11) +#define RT5668_FAST_UPDN_MASK (0x1 << 10) +#define RT5668_FAST_UPDN_SFT 10 +#define RT5668_FAST_UPDN_DIS (0x0 << 10) +#define RT5668_FAST_UPDN_EN (0x1 << 10) +#define RT5668_VLO_MASK (0x1 << 7) +#define RT5668_VLO_SFT 7 +#define RT5668_VLO_3V (0x0 << 7) +#define RT5668_VLO_33V (0x1 << 7) + +/* HPOUT charge pump 1 (0x0091) */ +#define RT5668_OSW_L_MASK (0x1 << 11) +#define RT5668_OSW_L_SFT 11 +#define RT5668_OSW_L_DIS (0x0 << 11) +#define RT5668_OSW_L_EN (0x1 << 11) +#define RT5668_OSW_R_MASK (0x1 << 10) +#define RT5668_OSW_R_SFT 10 +#define RT5668_OSW_R_DIS (0x0 << 10) +#define RT5668_OSW_R_EN (0x1 << 10) +#define RT5668_PM_HP_MASK (0x3 << 8) +#define RT5668_PM_HP_SFT 8 +#define RT5668_PM_HP_LV (0x0 << 8) +#define RT5668_PM_HP_MV (0x1 << 8) +#define RT5668_PM_HP_HV (0x2 << 8) +#define RT5668_IB_HP_MASK (0x3 << 6) +#define RT5668_IB_HP_SFT 6 +#define RT5668_IB_HP_125IL (0x0 << 6) +#define RT5668_IB_HP_25IL (0x1 << 6) +#define RT5668_IB_HP_5IL (0x2 << 6) +#define RT5668_IB_HP_1IL (0x3 << 6) + +/* Micbias Control1 (0x93) */ +#define RT5668_MIC1_OV_MASK (0x3 << 14) +#define RT5668_MIC1_OV_SFT 14 +#define RT5668_MIC1_OV_2V7 (0x0 << 14) +#define RT5668_MIC1_OV_2V4 (0x1 << 14) +#define RT5668_MIC1_OV_2V25 (0x3 << 14) +#define RT5668_MIC1_OV_1V8 (0x4 << 14) +#define RT5668_MIC1_CLK_MASK (0x1 << 13) +#define RT5668_MIC1_CLK_SFT 13 +#define RT5668_MIC1_CLK_DIS (0x0 << 13) +#define RT5668_MIC1_CLK_EN (0x1 << 13) +#define RT5668_MIC1_OVCD_MASK (0x1 << 12) +#define RT5668_MIC1_OVCD_SFT 12 +#define RT5668_MIC1_OVCD_DIS (0x0 << 12) +#define RT5668_MIC1_OVCD_EN (0x1 << 12) +#define RT5668_MIC1_OVTH_MASK (0x3 << 10) +#define RT5668_MIC1_OVTH_SFT 10 +#define RT5668_MIC1_OVTH_768UA (0x0 << 10) +#define RT5668_MIC1_OVTH_960UA (0x1 << 10) +#define RT5668_MIC1_OVTH_1152UA (0x2 << 10) +#define RT5668_MIC1_OVTH_1960UA (0x3 << 10) +#define RT5668_MIC2_OV_MASK (0x3 << 8) +#define RT5668_MIC2_OV_SFT 8 +#define RT5668_MIC2_OV_2V7 (0x0 << 8) +#define RT5668_MIC2_OV_2V4 (0x1 << 8) +#define RT5668_MIC2_OV_2V25 (0x3 << 8) +#define RT5668_MIC2_OV_1V8 (0x4 << 8) +#define RT5668_MIC2_CLK_MASK (0x1 << 7) +#define RT5668_MIC2_CLK_SFT 7 +#define RT5668_MIC2_CLK_DIS (0x0 << 7) +#define RT5668_MIC2_CLK_EN (0x1 << 7) +#define RT5668_MIC2_OVTH_MASK (0x3 << 4) +#define RT5668_MIC2_OVTH_SFT 4 +#define RT5668_MIC2_OVTH_768UA (0x0 << 4) +#define RT5668_MIC2_OVTH_960UA (0x1 << 4) +#define RT5668_MIC2_OVTH_1152UA (0x2 << 4) +#define RT5668_MIC2_OVTH_1960UA (0x3 << 4) +#define RT5668_PWR_MB_MASK (0x1 << 3) +#define RT5668_PWR_MB_SFT 3 +#define RT5668_PWR_MB_PD (0x0 << 3) +#define RT5668_PWR_MB_PU (0x1 << 3) + +/* Micbias Control2 (0x0094) */ +#define RT5668_PWR_CLK25M_MASK (0x1 << 9) +#define RT5668_PWR_CLK25M_SFT 9 +#define RT5668_PWR_CLK25M_PD (0x0 << 9) +#define RT5668_PWR_CLK25M_PU (0x1 << 9) +#define RT5668_PWR_CLK1M_MASK (0x1 << 8) +#define RT5668_PWR_CLK1M_SFT 8 +#define RT5668_PWR_CLK1M_PD (0x0 << 8) +#define RT5668_PWR_CLK1M_PU (0x1 << 8) + +/* RC Clock Control (0x009f) */ +#define RT5668_POW_IRQ (0x1 << 15) +#define RT5668_POW_JDH (0x1 << 14) +#define RT5668_POW_JDL (0x1 << 13) +#define RT5668_POW_ANA (0x1 << 12) + +/* I2S Master Mode Clock Control 1 (0x00a0) */ +#define RT5668_CLK_SRC_MCLK (0x0) +#define RT5668_CLK_SRC_PLL1 (0x1) +#define RT5668_CLK_SRC_PLL2 (0x2) +#define RT5668_CLK_SRC_SDW (0x3) +#define RT5668_CLK_SRC_RCCLK (0x4) +#define RT5668_I2S_PD_1 (0x0) +#define RT5668_I2S_PD_2 (0x1) +#define RT5668_I2S_PD_3 (0x2) +#define RT5668_I2S_PD_4 (0x3) +#define RT5668_I2S_PD_6 (0x4) +#define RT5668_I2S_PD_8 (0x5) +#define RT5668_I2S_PD_12 (0x6) +#define RT5668_I2S_PD_16 (0x7) +#define RT5668_I2S_PD_24 (0x8) +#define RT5668_I2S_PD_32 (0x9) +#define RT5668_I2S_PD_48 (0xa) +#define RT5668_I2S2_SRC_MASK (0x3 << 4) +#define RT5668_I2S2_SRC_SFT 4 +#define RT5668_I2S2_M_PD_MASK (0xf << 0) +#define RT5668_I2S2_M_PD_SFT 0 + +/* IRQ Control 1 (0x00b6) */ +#define RT5668_JD1_PULSE_EN_MASK (0x1 << 10) +#define RT5668_JD1_PULSE_EN_SFT 10 +#define RT5668_JD1_PULSE_DIS (0x0 << 10) +#define RT5668_JD1_PULSE_EN (0x1 << 10) + +/* IRQ Control 2 (0x00b7) */ +#define RT5668_JD1_EN_MASK (0x1 << 15) +#define RT5668_JD1_EN_SFT 15 +#define RT5668_JD1_DIS (0x0 << 15) +#define RT5668_JD1_EN (0x1 << 15) +#define RT5668_JD1_POL_MASK (0x1 << 13) +#define RT5668_JD1_POL_NOR (0x0 << 13) +#define RT5668_JD1_POL_INV (0x1 << 13) + +/* IRQ Control 3 (0x00b8) */ +#define RT5668_IL_IRQ_MASK (0x1 << 7) +#define RT5668_IL_IRQ_DIS (0x0 << 7) +#define RT5668_IL_IRQ_EN (0x1 << 7) + +/* GPIO Control 1 (0x00c0) */ +#define RT5668_GP1_PIN_MASK (0x3 << 14) +#define RT5668_GP1_PIN_SFT 14 +#define RT5668_GP1_PIN_GPIO1 (0x0 << 14) +#define RT5668_GP1_PIN_IRQ (0x1 << 14) +#define RT5668_GP1_PIN_DMIC_CLK (0x2 << 14) +#define RT5668_GP2_PIN_MASK (0x3 << 12) +#define RT5668_GP2_PIN_SFT 12 +#define RT5668_GP2_PIN_GPIO2 (0x0 << 12) +#define RT5668_GP2_PIN_LRCK2 (0x1 << 12) +#define RT5668_GP2_PIN_DMIC_SDA (0x2 << 12) +#define RT5668_GP3_PIN_MASK (0x3 << 10) +#define RT5668_GP3_PIN_SFT 10 +#define RT5668_GP3_PIN_GPIO3 (0x0 << 10) +#define RT5668_GP3_PIN_BCLK2 (0x1 << 10) +#define RT5668_GP3_PIN_DMIC_CLK (0x2 << 10) +#define RT5668_GP4_PIN_MASK (0x3 << 8) +#define RT5668_GP4_PIN_SFT 8 +#define RT5668_GP4_PIN_GPIO4 (0x0 << 8) +#define RT5668_GP4_PIN_ADCDAT1 (0x1 << 8) +#define RT5668_GP4_PIN_DMIC_CLK (0x2 << 8) +#define RT5668_GP4_PIN_ADCDAT2 (0x3 << 8) +#define RT5668_GP5_PIN_MASK (0x3 << 6) +#define RT5668_GP5_PIN_SFT 6 +#define RT5668_GP5_PIN_GPIO5 (0x0 << 6) +#define RT5668_GP5_PIN_DACDAT1 (0x1 << 6) +#define RT5668_GP5_PIN_DMIC_SDA (0x2 << 6) +#define RT5668_GP6_PIN_MASK (0x1 << 5) +#define RT5668_GP6_PIN_SFT 5 +#define RT5668_GP6_PIN_GPIO6 (0x0 << 5) +#define RT5668_GP6_PIN_LRCK1 (0x1 << 5) + +/* GPIO Control 2 (0x00c1)*/ +#define RT5668_GP1_PF_MASK (0x1 << 15) +#define RT5668_GP1_PF_IN (0x0 << 15) +#define RT5668_GP1_PF_OUT (0x1 << 15) +#define RT5668_GP1_OUT_MASK (0x1 << 14) +#define RT5668_GP1_OUT_L (0x0 << 14) +#define RT5668_GP1_OUT_H (0x1 << 14) +#define RT5668_GP2_PF_MASK (0x1 << 13) +#define RT5668_GP2_PF_IN (0x0 << 13) +#define RT5668_GP2_PF_OUT (0x1 << 13) +#define RT5668_GP2_OUT_MASK (0x1 << 12) +#define RT5668_GP2_OUT_L (0x0 << 12) +#define RT5668_GP2_OUT_H (0x1 << 12) +#define RT5668_GP3_PF_MASK (0x1 << 11) +#define RT5668_GP3_PF_IN (0x0 << 11) +#define RT5668_GP3_PF_OUT (0x1 << 11) +#define RT5668_GP3_OUT_MASK (0x1 << 10) +#define RT5668_GP3_OUT_L (0x0 << 10) +#define RT5668_GP3_OUT_H (0x1 << 10) +#define RT5668_GP4_PF_MASK (0x1 << 9) +#define RT5668_GP4_PF_IN (0x0 << 9) +#define RT5668_GP4_PF_OUT (0x1 << 9) +#define RT5668_GP4_OUT_MASK (0x1 << 8) +#define RT5668_GP4_OUT_L (0x0 << 8) +#define RT5668_GP4_OUT_H (0x1 << 8) +#define RT5668_GP5_PF_MASK (0x1 << 7) +#define RT5668_GP5_PF_IN (0x0 << 7) +#define RT5668_GP5_PF_OUT (0x1 << 7) +#define RT5668_GP5_OUT_MASK (0x1 << 6) +#define RT5668_GP5_OUT_L (0x0 << 6) +#define RT5668_GP5_OUT_H (0x1 << 6) +#define RT5668_GP6_PF_MASK (0x1 << 5) +#define RT5668_GP6_PF_IN (0x0 << 5) +#define RT5668_GP6_PF_OUT (0x1 << 5) +#define RT5668_GP6_OUT_MASK (0x1 << 4) +#define RT5668_GP6_OUT_L (0x0 << 4) +#define RT5668_GP6_OUT_H (0x1 << 4) + + +/* GPIO Status (0x00c2) */ +#define RT5668_GP6_STA (0x1 << 6) +#define RT5668_GP5_STA (0x1 << 5) +#define RT5668_GP4_STA (0x1 << 4) +#define RT5668_GP3_STA (0x1 << 3) +#define RT5668_GP2_STA (0x1 << 2) +#define RT5668_GP1_STA (0x1 << 1) + +/* Soft volume and zero cross control 1 (0x00d9) */ +#define RT5668_SV_MASK (0x1 << 15) +#define RT5668_SV_SFT 15 +#define RT5668_SV_DIS (0x0 << 15) +#define RT5668_SV_EN (0x1 << 15) +#define RT5668_ZCD_MASK (0x1 << 10) +#define RT5668_ZCD_SFT 10 +#define RT5668_ZCD_PD (0x0 << 10) +#define RT5668_ZCD_PU (0x1 << 10) +#define RT5668_SV_DLY_MASK (0xf) +#define RT5668_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0x00da) */ +#define RT5668_ZCD_BST1_CBJ_MASK (0x1 << 7) +#define RT5668_ZCD_BST1_CBJ_SFT 7 +#define RT5668_ZCD_BST1_CBJ_DIS (0x0 << 7) +#define RT5668_ZCD_BST1_CBJ_EN (0x1 << 7) +#define RT5668_ZCD_RECMIX_MASK (0x1) +#define RT5668_ZCD_RECMIX_SFT 0 +#define RT5668_ZCD_RECMIX_DIS (0x0) +#define RT5668_ZCD_RECMIX_EN (0x1) + +/* 4 Button Inline Command Control 2 (0x00e3) */ +#define RT5668_4BTN_IL_MASK (0x1 << 15) +#define RT5668_4BTN_IL_EN (0x1 << 15) +#define RT5668_4BTN_IL_DIS (0x0 << 15) +#define RT5668_4BTN_IL_RST_MASK (0x1 << 14) +#define RT5668_4BTN_IL_NOR (0x1 << 14) +#define RT5668_4BTN_IL_RST (0x0 << 14) + +/* Analog JD Control (0x00f0) */ +#define RT5668_JDH_RS_MASK (0x1 << 4) +#define RT5668_JDH_NO_PLUG (0x1 << 4) +#define RT5668_JDH_PLUG (0x0 << 4) + +/* Chopper and Clock control for DAC (0x013a)*/ +#define RT5668_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5668_CKXEN_DAC1_SFT 13 +#define RT5668_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5668_CKGEN_DAC1_SFT 12 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5668_CKXEN_ADC1_MASK (0x1 << 13) +#define RT5668_CKXEN_ADC1_SFT 13 +#define RT5668_CKGEN_ADC1_MASK (0x1 << 12) +#define RT5668_CKGEN_ADC1_SFT 12 + +/* Volume test (0x013f)*/ +#define RT5668_SEL_CLK_VOL_MASK (0x1 << 15) +#define RT5668_SEL_CLK_VOL_EN (0x1 << 15) +#define RT5668_SEL_CLK_VOL_DIS (0x0 << 15) + +/* Test Mode Control 1 (0x0145) */ +#define RT5668_AD2DA_LB_MASK (0x1 << 10) +#define RT5668_AD2DA_LB_SFT 10 + +/* Stereo Noise Gate Control 1 (0x0160) */ +#define RT5668_NG2_EN_MASK (0x1 << 15) +#define RT5668_NG2_EN (0x1 << 15) +#define RT5668_NG2_DIS (0x0 << 15) + +/* Stereo1 DAC Silence Detection Control (0x0190) */ +#define RT5668_DEB_STO_DAC_MASK (0x7 << 4) +#define RT5668_DEB_80_MS (0x0 << 4) + +/* SAR ADC Inline Command Control 1 (0x0210) */ +#define RT5668_SAR_BUTT_DET_MASK (0x1 << 15) +#define RT5668_SAR_BUTT_DET_EN (0x1 << 15) +#define RT5668_SAR_BUTT_DET_DIS (0x0 << 15) +#define RT5668_SAR_BUTDET_MODE_MASK (0x1 << 14) +#define RT5668_SAR_BUTDET_POW_SAV (0x1 << 14) +#define RT5668_SAR_BUTDET_POW_NORM (0x0 << 14) +#define RT5668_SAR_BUTDET_RST_MASK (0x1 << 13) +#define RT5668_SAR_BUTDET_RST_NORMAL (0x1 << 13) +#define RT5668_SAR_BUTDET_RST (0x0 << 13) +#define RT5668_SAR_POW_MASK (0x1 << 12) +#define RT5668_SAR_POW_EN (0x1 << 12) +#define RT5668_SAR_POW_DIS (0x0 << 12) +#define RT5668_SAR_RST_MASK (0x1 << 11) +#define RT5668_SAR_RST_NORMAL (0x1 << 11) +#define RT5668_SAR_RST (0x0 << 11) +#define RT5668_SAR_BYPASS_MASK (0x1 << 10) +#define RT5668_SAR_BYPASS_EN (0x1 << 10) +#define RT5668_SAR_BYPASS_DIS (0x0 << 10) +#define RT5668_SAR_SEL_MB1_MASK (0x1 << 9) +#define RT5668_SAR_SEL_MB1_SEL (0x1 << 9) +#define RT5668_SAR_SEL_MB1_NOSEL (0x0 << 9) +#define RT5668_SAR_SEL_MB2_MASK (0x1 << 8) +#define RT5668_SAR_SEL_MB2_SEL (0x1 << 8) +#define RT5668_SAR_SEL_MB2_NOSEL (0x0 << 8) +#define RT5668_SAR_SEL_MODE_MASK (0x1 << 7) +#define RT5668_SAR_SEL_MODE_CMP (0x1 << 7) +#define RT5668_SAR_SEL_MODE_ADC (0x0 << 7) +#define RT5668_SAR_SEL_MB1_MB2_MASK (0x1 << 5) +#define RT5668_SAR_SEL_MB1_MB2_AUTO (0x1 << 5) +#define RT5668_SAR_SEL_MB1_MB2_MANU (0x0 << 5) +#define RT5668_SAR_SEL_SIGNAL_MASK (0x1 << 4) +#define RT5668_SAR_SEL_SIGNAL_AUTO (0x1 << 4) +#define RT5668_SAR_SEL_SIGNAL_MANU (0x0 << 4) + +/* SAR ADC Inline Command Control 13 (0x021c) */ +#define RT5668_SAR_SOUR_MASK (0x3f) +#define RT5668_SAR_SOUR_BTN (0x3f) +#define RT5668_SAR_SOUR_TYPE (0x0) + + +/* System Clock Source */ +enum { + RT5668_SCLK_S_MCLK, + RT5668_SCLK_S_PLL1, + RT5668_SCLK_S_PLL2, + RT5668_SCLK_S_RCCLK, +}; + +/* PLL Source */ +enum { + RT5668_PLL1_S_MCLK, + RT5668_PLL1_S_BCLK1, + RT5668_PLL1_S_RCCLK, +}; + +enum { + RT5668_AIF1, + RT5668_AIF2, + RT5668_AIFS +}; + +/* filter mask */ +enum { + RT5668_DA_STEREO1_FILTER = 0x1, + RT5668_AD_STEREO1_FILTER = (0x1 << 1), +}; + +enum { + RT5668_CLK_SEL_SYS, + RT5668_CLK_SEL_I2S1_ASRC, + RT5668_CLK_SEL_I2S2_ASRC, +}; + +int rt5668_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +#endif /* __RT5668_H__ */ diff --git a/sound/soc/codecs/rt5670-dsp.h b/sound/soc/codecs/rt5670-dsp.h new file mode 100644 index 0000000000..a07b7dfcf5 --- /dev/null +++ b/sound/soc/codecs/rt5670-dsp.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5670-dsp.h -- RT5670 ALSA SoC DSP driver + * + * Copyright 2014 Realtek Microelectronics + * Author: Bard Liao + */ + +#ifndef __RT5670_DSP_H__ +#define __RT5670_DSP_H__ + +#define RT5670_DSP_CTRL1 0xe0 +#define RT5670_DSP_CTRL2 0xe1 +#define RT5670_DSP_CTRL3 0xe2 +#define RT5670_DSP_CTRL4 0xe3 +#define RT5670_DSP_CTRL5 0xe4 + +/* DSP Control 1 (0xe0) */ +#define RT5670_DSP_CMD_MASK (0xff << 8) +#define RT5670_DSP_CMD_PE (0x0d << 8) /* Patch Entry */ +#define RT5670_DSP_CMD_MW (0x3b << 8) /* Memory Write */ +#define RT5670_DSP_CMD_MR (0x37 << 8) /* Memory Read */ +#define RT5670_DSP_CMD_RR (0x60 << 8) /* Register Read */ +#define RT5670_DSP_CMD_RW (0x68 << 8) /* Register Write */ +#define RT5670_DSP_REG_DATHI (0x26 << 8) /* High Data Addr */ +#define RT5670_DSP_REG_DATLO (0x25 << 8) /* Low Data Addr */ +#define RT5670_DSP_CLK_MASK (0x3 << 6) +#define RT5670_DSP_CLK_SFT 6 +#define RT5670_DSP_CLK_768K (0x0 << 6) +#define RT5670_DSP_CLK_384K (0x1 << 6) +#define RT5670_DSP_CLK_192K (0x2 << 6) +#define RT5670_DSP_CLK_96K (0x3 << 6) +#define RT5670_DSP_BUSY_MASK (0x1 << 5) +#define RT5670_DSP_RW_MASK (0x1 << 4) +#define RT5670_DSP_DL_MASK (0x3 << 2) +#define RT5670_DSP_DL_0 (0x0 << 2) +#define RT5670_DSP_DL_1 (0x1 << 2) +#define RT5670_DSP_DL_2 (0x2 << 2) +#define RT5670_DSP_DL_3 (0x3 << 2) +#define RT5670_DSP_I2C_AL_16 (0x1 << 1) +#define RT5670_DSP_CMD_EN (0x1) + +struct rt5670_dsp_param { + u16 cmd_fmt; + u16 addr; + u16 data; + u8 cmd; +}; + +#endif /* __RT5670_DSP_H__ */ + diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c new file mode 100644 index 0000000000..0e34293f33 --- /dev/null +++ b/sound/soc/codecs/rt5670.c @@ -0,0 +1,3340 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5670.c -- RT5670 ALSA SoC audio codec driver + * + * Copyright 2014 Realtek Semiconductor Corp. + * Author: Bard Liao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5670.h" +#include "rt5670-dsp.h" + +#define RT5670_GPIO1_IS_IRQ BIT(0) +#define RT5670_IN2_DIFF BIT(1) +#define RT5670_DMIC_EN BIT(2) +#define RT5670_DMIC1_IN2P BIT(3) +#define RT5670_DMIC1_GPIO6 BIT(4) +#define RT5670_DMIC1_GPIO7 BIT(5) +#define RT5670_DMIC2_INR BIT(6) +#define RT5670_DMIC2_GPIO8 BIT(7) +#define RT5670_DMIC3_GPIO5 BIT(8) +#define RT5670_JD_MODE1 BIT(9) +#define RT5670_JD_MODE2 BIT(10) +#define RT5670_JD_MODE3 BIT(11) +#define RT5670_GPIO1_IS_EXT_SPK_EN BIT(12) + +static unsigned long rt5670_quirk; +static unsigned int quirk_override; +module_param_named(quirk, quirk_override, uint, 0444); +MODULE_PARM_DESC(quirk, "Board-specific quirk override"); + +#define RT5670_DEVICE_ID 0x6271 + +#define RT5670_PR_RANGE_BASE (0xff + 1) +#define RT5670_PR_SPACING 0x100 + +#define RT5670_PR_BASE (RT5670_PR_RANGE_BASE + (0 * RT5670_PR_SPACING)) + +static const struct regmap_range_cfg rt5670_ranges[] = { + { .name = "PR", .range_min = RT5670_PR_BASE, + .range_max = RT5670_PR_BASE + 0xf8, + .selector_reg = RT5670_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5670_PRIV_DATA, + .window_len = 0x1, }, +}; + +static const struct reg_sequence init_list[] = { + { RT5670_PR_BASE + 0x14, 0x9a8a }, + { RT5670_PR_BASE + 0x38, 0x1fe1 }, + { RT5670_PR_BASE + 0x3d, 0x3640 }, + { 0x8a, 0x0123 }, +}; + +static const struct reg_default rt5670_reg[] = { + { 0x00, 0x0000 }, + { 0x02, 0x8888 }, + { 0x03, 0x8888 }, + { 0x0a, 0x0001 }, + { 0x0b, 0x0827 }, + { 0x0c, 0x0000 }, + { 0x0d, 0x0008 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0011 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x1f, 0x2f2f }, + { 0x20, 0x0000 }, + { 0x26, 0x7860 }, + { 0x27, 0x7860 }, + { 0x28, 0x7871 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5656 }, + { 0x2b, 0x5454 }, + { 0x2c, 0xaaa0 }, + { 0x2d, 0x0000 }, + { 0x2e, 0x2f2f }, + { 0x2f, 0x1002 }, + { 0x30, 0x0000 }, + { 0x31, 0x5f00 }, + { 0x32, 0x0000 }, + { 0x33, 0x0000 }, + { 0x34, 0x0000 }, + { 0x35, 0x0000 }, + { 0x36, 0x0000 }, + { 0x37, 0x0000 }, + { 0x38, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x45, 0xe00f }, + { 0x4c, 0x5380 }, + { 0x4f, 0x0073 }, + { 0x52, 0x00d3 }, + { 0x53, 0xf000 }, + { 0x61, 0x0000 }, + { 0x62, 0x0001 }, + { 0x63, 0x00c3 }, + { 0x64, 0x0000 }, + { 0x65, 0x0001 }, + { 0x66, 0x0000 }, + { 0x6f, 0x8000 }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x72, 0x8000 }, + { 0x73, 0x7770 }, + { 0x74, 0x0e00 }, + { 0x75, 0x1505 }, + { 0x76, 0x0015 }, + { 0x77, 0x0c00 }, + { 0x78, 0x4000 }, + { 0x79, 0x0123 }, + { 0x7f, 0x1100 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0000 }, + { 0x84, 0x0000 }, + { 0x85, 0x0000 }, + { 0x86, 0x0004 }, + { 0x87, 0x0000 }, + { 0x88, 0x0000 }, + { 0x89, 0x0000 }, + { 0x8a, 0x0123 }, + { 0x8b, 0x0000 }, + { 0x8c, 0x0003 }, + { 0x8d, 0x0000 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0646 }, + { 0x91, 0x0c06 }, + { 0x93, 0x0000 }, + { 0x94, 0x1270 }, + { 0x95, 0x1000 }, + { 0x97, 0x0000 }, + { 0x98, 0x0000 }, + { 0x99, 0x0000 }, + { 0x9a, 0x2184 }, + { 0x9b, 0x010a }, + { 0x9c, 0x0aea }, + { 0x9d, 0x000c }, + { 0x9e, 0x0400 }, + { 0xae, 0x7000 }, + { 0xaf, 0x0000 }, + { 0xb0, 0x7000 }, + { 0xb1, 0x0000 }, + { 0xb2, 0x0000 }, + { 0xb3, 0x001f }, + { 0xb4, 0x220c }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xb7, 0x0000 }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x0000 }, + { 0xc0, 0x0000 }, + { 0xc1, 0x0000 }, + { 0xc2, 0x0000 }, + { 0xcd, 0x0000 }, + { 0xce, 0x0000 }, + { 0xcf, 0x1813 }, + { 0xd0, 0x0690 }, + { 0xd1, 0x1c17 }, + { 0xd3, 0xa220 }, + { 0xd4, 0x0000 }, + { 0xd6, 0x0400 }, + { 0xd9, 0x0809 }, + { 0xda, 0x0000 }, + { 0xdb, 0x0001 }, + { 0xdc, 0x0049 }, + { 0xdd, 0x0024 }, + { 0xe6, 0x8000 }, + { 0xe7, 0x0000 }, + { 0xec, 0xa200 }, + { 0xed, 0x0000 }, + { 0xee, 0xa200 }, + { 0xef, 0x0000 }, + { 0xf8, 0x0000 }, + { 0xf9, 0x0000 }, + { 0xfa, 0x8010 }, + { 0xfb, 0x0033 }, + { 0xfc, 0x0100 }, +}; + +static bool rt5670_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5670_ranges); i++) { + if ((reg >= rt5670_ranges[i].window_start && + reg <= rt5670_ranges[i].window_start + + rt5670_ranges[i].window_len) || + (reg >= rt5670_ranges[i].range_min && + reg <= rt5670_ranges[i].range_max)) { + return true; + } + } + + switch (reg) { + case RT5670_RESET: + case RT5670_PDM_DATA_CTRL1: + case RT5670_PDM1_DATA_CTRL4: + case RT5670_PDM2_DATA_CTRL4: + case RT5670_PRIV_DATA: + case RT5670_ASRC_5: + case RT5670_CJ_CTRL1: + case RT5670_CJ_CTRL2: + case RT5670_CJ_CTRL3: + case RT5670_A_JD_CTRL1: + case RT5670_A_JD_CTRL2: + case RT5670_VAD_CTRL5: + case RT5670_ADC_EQ_CTRL1: + case RT5670_EQ_CTRL1: + case RT5670_ALC_CTRL_1: + case RT5670_IRQ_CTRL2: + case RT5670_INT_IRQ_ST: + case RT5670_IL_CMD: + case RT5670_DSP_CTRL1: + case RT5670_DSP_CTRL2: + case RT5670_DSP_CTRL3: + case RT5670_DSP_CTRL4: + case RT5670_DSP_CTRL5: + case RT5670_VENDOR_ID: + case RT5670_VENDOR_ID1: + case RT5670_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool rt5670_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5670_ranges); i++) { + if ((reg >= rt5670_ranges[i].window_start && + reg <= rt5670_ranges[i].window_start + + rt5670_ranges[i].window_len) || + (reg >= rt5670_ranges[i].range_min && + reg <= rt5670_ranges[i].range_max)) { + return true; + } + } + + switch (reg) { + case RT5670_RESET: + case RT5670_HP_VOL: + case RT5670_LOUT1: + case RT5670_CJ_CTRL1: + case RT5670_CJ_CTRL2: + case RT5670_CJ_CTRL3: + case RT5670_IN2: + case RT5670_INL1_INR1_VOL: + case RT5670_DAC1_DIG_VOL: + case RT5670_DAC2_DIG_VOL: + case RT5670_DAC_CTRL: + case RT5670_STO1_ADC_DIG_VOL: + case RT5670_MONO_ADC_DIG_VOL: + case RT5670_STO2_ADC_DIG_VOL: + case RT5670_ADC_BST_VOL1: + case RT5670_ADC_BST_VOL2: + case RT5670_STO2_ADC_MIXER: + case RT5670_STO1_ADC_MIXER: + case RT5670_MONO_ADC_MIXER: + case RT5670_AD_DA_MIXER: + case RT5670_STO_DAC_MIXER: + case RT5670_DD_MIXER: + case RT5670_DIG_MIXER: + case RT5670_DSP_PATH1: + case RT5670_DSP_PATH2: + case RT5670_DIG_INF1_DATA: + case RT5670_DIG_INF2_DATA: + case RT5670_PDM_OUT_CTRL: + case RT5670_PDM_DATA_CTRL1: + case RT5670_PDM1_DATA_CTRL2: + case RT5670_PDM1_DATA_CTRL3: + case RT5670_PDM1_DATA_CTRL4: + case RT5670_PDM2_DATA_CTRL2: + case RT5670_PDM2_DATA_CTRL3: + case RT5670_PDM2_DATA_CTRL4: + case RT5670_REC_L1_MIXER: + case RT5670_REC_L2_MIXER: + case RT5670_REC_R1_MIXER: + case RT5670_REC_R2_MIXER: + case RT5670_HPO_MIXER: + case RT5670_MONO_MIXER: + case RT5670_OUT_L1_MIXER: + case RT5670_OUT_R1_MIXER: + case RT5670_LOUT_MIXER: + case RT5670_PWR_DIG1: + case RT5670_PWR_DIG2: + case RT5670_PWR_ANLG1: + case RT5670_PWR_ANLG2: + case RT5670_PWR_MIXER: + case RT5670_PWR_VOL: + case RT5670_PRIV_INDEX: + case RT5670_PRIV_DATA: + case RT5670_I2S4_SDP: + case RT5670_I2S1_SDP: + case RT5670_I2S2_SDP: + case RT5670_I2S3_SDP: + case RT5670_ADDA_CLK1: + case RT5670_ADDA_CLK2: + case RT5670_DMIC_CTRL1: + case RT5670_DMIC_CTRL2: + case RT5670_TDM_CTRL_1: + case RT5670_TDM_CTRL_2: + case RT5670_TDM_CTRL_3: + case RT5670_DSP_CLK: + case RT5670_GLB_CLK: + case RT5670_PLL_CTRL1: + case RT5670_PLL_CTRL2: + case RT5670_ASRC_1: + case RT5670_ASRC_2: + case RT5670_ASRC_3: + case RT5670_ASRC_4: + case RT5670_ASRC_5: + case RT5670_ASRC_7: + case RT5670_ASRC_8: + case RT5670_ASRC_9: + case RT5670_ASRC_10: + case RT5670_ASRC_11: + case RT5670_ASRC_12: + case RT5670_ASRC_13: + case RT5670_ASRC_14: + case RT5670_DEPOP_M1: + case RT5670_DEPOP_M2: + case RT5670_DEPOP_M3: + case RT5670_CHARGE_PUMP: + case RT5670_MICBIAS: + case RT5670_A_JD_CTRL1: + case RT5670_A_JD_CTRL2: + case RT5670_VAD_CTRL1: + case RT5670_VAD_CTRL2: + case RT5670_VAD_CTRL3: + case RT5670_VAD_CTRL4: + case RT5670_VAD_CTRL5: + case RT5670_ADC_EQ_CTRL1: + case RT5670_ADC_EQ_CTRL2: + case RT5670_EQ_CTRL1: + case RT5670_EQ_CTRL2: + case RT5670_ALC_DRC_CTRL1: + case RT5670_ALC_DRC_CTRL2: + case RT5670_ALC_CTRL_1: + case RT5670_ALC_CTRL_2: + case RT5670_ALC_CTRL_3: + case RT5670_JD_CTRL: + case RT5670_IRQ_CTRL1: + case RT5670_IRQ_CTRL2: + case RT5670_INT_IRQ_ST: + case RT5670_GPIO_CTRL1: + case RT5670_GPIO_CTRL2: + case RT5670_GPIO_CTRL3: + case RT5670_SCRABBLE_FUN: + case RT5670_SCRABBLE_CTRL: + case RT5670_BASE_BACK: + case RT5670_MP3_PLUS1: + case RT5670_MP3_PLUS2: + case RT5670_ADJ_HPF1: + case RT5670_ADJ_HPF2: + case RT5670_HP_CALIB_AMP_DET: + case RT5670_SV_ZCD1: + case RT5670_SV_ZCD2: + case RT5670_IL_CMD: + case RT5670_IL_CMD2: + case RT5670_IL_CMD3: + case RT5670_DRC_HL_CTRL1: + case RT5670_DRC_HL_CTRL2: + case RT5670_ADC_MONO_HP_CTRL1: + case RT5670_ADC_MONO_HP_CTRL2: + case RT5670_ADC_STO2_HP_CTRL1: + case RT5670_ADC_STO2_HP_CTRL2: + case RT5670_JD_CTRL3: + case RT5670_JD_CTRL4: + case RT5670_DIG_MISC: + case RT5670_DSP_CTRL1: + case RT5670_DSP_CTRL2: + case RT5670_DSP_CTRL3: + case RT5670_DSP_CTRL4: + case RT5670_DSP_CTRL5: + case RT5670_GEN_CTRL2: + case RT5670_GEN_CTRL3: + case RT5670_VENDOR_ID: + case RT5670_VENDOR_ID1: + case RT5670_VENDOR_ID2: + return true; + default: + return false; + } +} + +/** + * rt5670_headset_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ + +static int rt5670_headset_detect(struct snd_soc_component *component, int jack_insert) +{ + int val; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); + snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, 0x4, 0x0); + snd_soc_component_update_bits(component, RT5670_CJ_CTRL2, + RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD, + RT5670_CBJ_MN_JD); + snd_soc_component_write(component, RT5670_GPIO_CTRL2, 0x0004); + snd_soc_component_update_bits(component, RT5670_GPIO_CTRL1, + RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); + snd_soc_component_update_bits(component, RT5670_CJ_CTRL1, + RT5670_CBJ_BST1_EN, RT5670_CBJ_BST1_EN); + snd_soc_component_write(component, RT5670_JD_CTRL3, 0x00f0); + snd_soc_component_update_bits(component, RT5670_CJ_CTRL2, + RT5670_CBJ_MN_JD, RT5670_CBJ_MN_JD); + snd_soc_component_update_bits(component, RT5670_CJ_CTRL2, + RT5670_CBJ_MN_JD, 0); + msleep(300); + val = snd_soc_component_read(component, RT5670_CJ_CTRL3) & 0x7; + if (val == 0x1 || val == 0x2) { + rt5670->jack_type = SND_JACK_HEADSET; + /* for push button */ + snd_soc_component_update_bits(component, RT5670_INT_IRQ_ST, 0x8, 0x8); + snd_soc_component_update_bits(component, RT5670_IL_CMD, 0x40, 0x40); + snd_soc_component_read(component, RT5670_IL_CMD); + } else { + snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, 0x4, 0x4); + rt5670->jack_type = SND_JACK_HEADPHONE; + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); + } + } else { + snd_soc_component_update_bits(component, RT5670_INT_IRQ_ST, 0x8, 0x0); + snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, 0x4, 0x4); + rt5670->jack_type = 0; + snd_soc_dapm_disable_pin(dapm, "Mic Det Power"); + snd_soc_dapm_sync(dapm); + } + + return rt5670->jack_type; +} + +void rt5670_jack_suspend(struct snd_soc_component *component) +{ + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + rt5670->jack_type_saved = rt5670->jack_type; + rt5670_headset_detect(component, 0); +} +EXPORT_SYMBOL_GPL(rt5670_jack_suspend); + +void rt5670_jack_resume(struct snd_soc_component *component) +{ + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + if (rt5670->jack_type_saved) + rt5670_headset_detect(component, 1); +} +EXPORT_SYMBOL_GPL(rt5670_jack_resume); + +static int rt5670_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read(component, RT5670_IL_CMD); + btn_type = val & 0xff80; + snd_soc_component_write(component, RT5670_IL_CMD, val); + if (btn_type != 0) { + msleep(20); + val = snd_soc_component_read(component, RT5670_IL_CMD); + snd_soc_component_write(component, RT5670_IL_CMD, val); + } + + return btn_type; +} + +static int rt5670_irq_detection(void *data) +{ + struct rt5670_priv *rt5670 = (struct rt5670_priv *)data; + struct snd_soc_jack_gpio *gpio = &rt5670->hp_gpio; + struct snd_soc_jack *jack = rt5670->jack; + int val, btn_type, report = jack->status; + + if (rt5670->jd_mode == 1) /* 2 port */ + val = snd_soc_component_read(rt5670->component, RT5670_A_JD_CTRL1) & 0x0070; + else + val = snd_soc_component_read(rt5670->component, RT5670_A_JD_CTRL1) & 0x0020; + + switch (val) { + /* jack in */ + case 0x30: /* 2 port */ + case 0x0: /* 1 port or 2 port */ + if (rt5670->jack_type == 0) { + report = rt5670_headset_detect(rt5670->component, 1); + /* for push button and jack out */ + gpio->debounce_time = 25; + break; + } + btn_type = 0; + if (snd_soc_component_read(rt5670->component, RT5670_INT_IRQ_ST) & 0x4) { + /* button pressed */ + report = SND_JACK_HEADSET; + btn_type = rt5670_button_detect(rt5670->component); + switch (btn_type) { + case 0x2000: /* up */ + report |= SND_JACK_BTN_1; + break; + case 0x0400: /* center */ + report |= SND_JACK_BTN_0; + break; + case 0x0080: /* down */ + report |= SND_JACK_BTN_2; + break; + default: + dev_err(rt5670->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + if (btn_type == 0)/* button release */ + report = rt5670->jack_type; + + break; + /* jack out */ + case 0x70: /* 2 port */ + case 0x10: /* 2 port */ + case 0x20: /* 1 port */ + report = 0; + snd_soc_component_update_bits(rt5670->component, RT5670_INT_IRQ_ST, 0x1, 0x0); + rt5670_headset_detect(rt5670->component, 0); + gpio->debounce_time = 150; /* for jack in */ + break; + default: + break; + } + + return report; +} + +int rt5670_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + int ret; + + rt5670->jack = jack; + rt5670->hp_gpio.gpiod_dev = component->dev; + rt5670->hp_gpio.name = "headset"; + rt5670->hp_gpio.report = SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2; + rt5670->hp_gpio.debounce_time = 150; + rt5670->hp_gpio.wake = true; + rt5670->hp_gpio.data = (struct rt5670_priv *)rt5670; + rt5670->hp_gpio.jack_status_check = rt5670_irq_detection; + + ret = snd_soc_jack_add_gpios(rt5670->jack, 1, + &rt5670->hp_gpio); + if (ret) { + dev_err(component->dev, "Adding jack GPIO failed\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5670_set_jack_detect); + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -6562, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -1762, 3000); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5670_data_select[] = { + "Normal", "Swap", "left copy to right", "right copy to left" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_if2_dac_enum, RT5670_DIG_INF1_DATA, + RT5670_IF2_DAC_SEL_SFT, rt5670_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_enum, RT5670_DIG_INF1_DATA, + RT5670_IF2_ADC_SEL_SFT, rt5670_data_select); + +/* + * For reliable output-mute LED control we need a "DAC1 Playback Switch" control. + * We emulate this by only clearing the RT5670_M_DAC1_L/_R AD_DA_MIXER register + * bits when both our emulated DAC1 Playback Switch control and the DAC1 MIXL/R + * DAPM-mixer DAC1 input are enabled. + */ +static void rt5670_update_ad_da_mixer_dac1_m_bits(struct rt5670_priv *rt5670) +{ + int val = RT5670_M_DAC1_L | RT5670_M_DAC1_R; + + if (rt5670->dac1_mixl_dac1_switch && rt5670->dac1_playback_switch_l) + val &= ~RT5670_M_DAC1_L; + + if (rt5670->dac1_mixr_dac1_switch && rt5670->dac1_playback_switch_r) + val &= ~RT5670_M_DAC1_R; + + regmap_update_bits(rt5670->regmap, RT5670_AD_DA_MIXER, + RT5670_M_DAC1_L | RT5670_M_DAC1_R, val); +} + +static int rt5670_dac1_playback_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt5670->dac1_playback_switch_l; + ucontrol->value.integer.value[1] = rt5670->dac1_playback_switch_r; + + return 0; +} + +static int rt5670_dac1_playback_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + if (rt5670->dac1_playback_switch_l == ucontrol->value.integer.value[0] && + rt5670->dac1_playback_switch_r == ucontrol->value.integer.value[1]) + return 0; + + rt5670->dac1_playback_switch_l = ucontrol->value.integer.value[0]; + rt5670->dac1_playback_switch_r = ucontrol->value.integer.value[1]; + + rt5670_update_ad_da_mixer_dac1_m_bits(rt5670); + + return 1; +} + +static const struct snd_kcontrol_new rt5670_snd_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_TLV("HP Playback Volume", RT5670_HP_VOL, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, + 39, 1, out_vol_tlv), + /* OUTPUT Control */ + SOC_DOUBLE_TLV("OUT Playback Volume", RT5670_LOUT1, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, 39, 1, out_vol_tlv), + /* DAC Digital Volume */ + SOC_DOUBLE("DAC2 Playback Switch", RT5670_DAC_CTRL, + RT5670_M_DAC_L2_VOL_SFT, RT5670_M_DAC_R2_VOL_SFT, 1, 1), + SOC_DOUBLE_EXT("DAC1 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt5670_dac1_playback_switch_get, rt5670_dac1_playback_switch_put), + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5670_DAC1_DIG_VOL, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, + 175, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5670_DAC2_DIG_VOL, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, + 175, 0, dac_vol_tlv), + /* IN1/IN2 Control */ + SOC_SINGLE_TLV("IN1 Boost Volume", RT5670_CJ_CTRL1, + RT5670_BST_SFT1, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN2 Boost Volume", RT5670_IN2, + RT5670_BST_SFT1, 8, 0, bst_tlv), + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5670_INL1_INR1_VOL, + RT5670_INL_VOL_SFT, RT5670_INR_VOL_SFT, + 31, 1, in_vol_tlv), + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5670_STO1_ADC_DIG_VOL, + RT5670_L_MUTE_SFT, RT5670_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5670_STO1_ADC_DIG_VOL, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, + 127, 0, adc_vol_tlv), + + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5670_MONO_ADC_DIG_VOL, + RT5670_L_VOL_SFT, RT5670_R_VOL_SFT, + 127, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5670_ADC_BST_VOL1, + RT5670_STO1_ADC_L_BST_SFT, RT5670_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_DOUBLE_TLV("STO2 ADC Boost Gain Volume", RT5670_ADC_BST_VOL1, + RT5670_STO2_ADC_L_BST_SFT, RT5670_STO2_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + + SOC_ENUM("ADC IF2 Data Switch", rt5670_if2_adc_enum), + SOC_ENUM("DAC IF2 Data Switch", rt5670_if2_dac_enum), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + int idx, rate; + + rate = rt5670->sysclk / rl6231_get_pre_div(rt5670->regmap, + RT5670_ADDA_CLK1, RT5670_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); + if (idx < 0) + dev_err(component->dev, "Failed to set DMIC clock\n"); + else + snd_soc_component_update_bits(component, RT5670_DMIC_CTRL1, + RT5670_DMIC_CLK_MASK, idx << RT5670_DMIC_CLK_SFT); + return idx; +} + +static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int reg, shift, val; + + switch (source->shift) { + case 0: + reg = RT5670_ASRC_3; + shift = 0; + break; + case 1: + reg = RT5670_ASRC_3; + shift = 4; + break; + case 2: + reg = RT5670_ASRC_5; + shift = 12; + break; + case 3: + reg = RT5670_ASRC_2; + shift = 0; + break; + case 8: + reg = RT5670_ASRC_2; + shift = 4; + break; + case 9: + reg = RT5670_ASRC_2; + shift = 8; + break; + case 10: + reg = RT5670_ASRC_2; + shift = 12; + break; + default: + return 0; + } + + val = (snd_soc_component_read(component, reg) >> shift) & 0xf; + switch (val) { + case 1: + case 2: + case 3: + case 4: + return 1; + default: + return 0; + } + +} + +static int can_use_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + if (rt5670->sysclk > rt5670->lrck[RT5670_AIF1] * 384) + return 1; + + return 0; +} + + +/** + * rt5670_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5670 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the codec driver will turn on ASRC + * for these filters if ASRC is selected as their clock source. + */ +int rt5670_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + unsigned int asrc2_mask = 0, asrc2_value = 0; + unsigned int asrc3_mask = 0, asrc3_value = 0; + + if (clk_src > RT5670_CLK_SEL_SYS3) + return -EINVAL; + + if (filter_mask & RT5670_DA_STEREO_FILTER) { + asrc2_mask |= RT5670_DA_STO_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5670_DA_STO_CLK_SEL_MASK) + | (clk_src << RT5670_DA_STO_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_DA_MONO_L_FILTER) { + asrc2_mask |= RT5670_DA_MONOL_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5670_DA_MONOL_CLK_SEL_MASK) + | (clk_src << RT5670_DA_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_DA_MONO_R_FILTER) { + asrc2_mask |= RT5670_DA_MONOR_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5670_DA_MONOR_CLK_SEL_MASK) + | (clk_src << RT5670_DA_MONOR_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_AD_STEREO_FILTER) { + asrc2_mask |= RT5670_AD_STO1_CLK_SEL_MASK; + asrc2_value = (asrc2_value & ~RT5670_AD_STO1_CLK_SEL_MASK) + | (clk_src << RT5670_AD_STO1_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_AD_MONO_L_FILTER) { + asrc3_mask |= RT5670_AD_MONOL_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5670_AD_MONOL_CLK_SEL_MASK) + | (clk_src << RT5670_AD_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_AD_MONO_R_FILTER) { + asrc3_mask |= RT5670_AD_MONOR_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5670_AD_MONOR_CLK_SEL_MASK) + | (clk_src << RT5670_AD_MONOR_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_UP_RATE_FILTER) { + asrc3_mask |= RT5670_UP_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5670_UP_CLK_SEL_MASK) + | (clk_src << RT5670_UP_CLK_SEL_SFT); + } + + if (filter_mask & RT5670_DOWN_RATE_FILTER) { + asrc3_mask |= RT5670_DOWN_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5670_DOWN_CLK_SEL_MASK) + | (clk_src << RT5670_DOWN_CLK_SEL_SFT); + } + + if (asrc2_mask) + snd_soc_component_update_bits(component, RT5670_ASRC_2, + asrc2_mask, asrc2_value); + + if (asrc3_mask) + snd_soc_component_update_bits(component, RT5670_ASRC_3, + asrc3_mask, asrc3_value); + return 0; +} +EXPORT_SYMBOL_GPL(rt5670_sel_asrc_clk_src); + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER, + RT5670_M_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO1_ADC_MIXER, + RT5670_M_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER, + RT5670_M_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO1_ADC_MIXER, + RT5670_M_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO2_ADC_MIXER, + RT5670_M_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO2_ADC_MIXER, + RT5670_M_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO2_ADC_MIXER, + RT5670_M_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_STO2_ADC_MIXER, + RT5670_M_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_mono_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_MONO_ADC_MIXER, + RT5670_M_MONO_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_MONO_ADC_MIXER, + RT5670_M_MONO_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_mono_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5670_MONO_ADC_MIXER, + RT5670_M_MONO_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5670_MONO_ADC_MIXER, + RT5670_M_MONO_ADC_R2_SFT, 1, 1), +}; + +/* See comment above rt5670_update_ad_da_mixer_dac1_m_bits() */ +static int rt5670_put_dac1_mix_dac1_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + int ret; + + if (mc->shift == 0) + rt5670->dac1_mixl_dac1_switch = ucontrol->value.integer.value[0]; + else + rt5670->dac1_mixr_dac1_switch = ucontrol->value.integer.value[0]; + + /* Apply the update (if any) */ + ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); + if (ret == 0) + return 0; + + rt5670_update_ad_da_mixer_dac1_m_bits(rt5670); + + return 1; +} + +#define SOC_DAPM_SINGLE_RT5670_DAC1_SW(name, shift) \ + SOC_SINGLE_EXT(name, SND_SOC_NOPM, shift, 1, 0, \ + snd_soc_dapm_get_volsw, rt5670_put_dac1_mix_dac1_switch) + +static const struct snd_kcontrol_new rt5670_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5670_AD_DA_MIXER, + RT5670_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE_RT5670_DAC1_SW("DAC1 Switch", 0), +}; + +static const struct snd_kcontrol_new rt5670_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5670_AD_DA_MIXER, + RT5670_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE_RT5670_DAC1_SW("DAC1 Switch", 1), +}; + +static const struct snd_kcontrol_new rt5670_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_L2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_R2_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_STO_DAC_MIXER, + RT5670_M_DAC_L1_STO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_mono_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_L1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_L2_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_R2_MONO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_mono_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_R1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_R2_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DD_MIXER, + RT5670_M_DAC_L2_MONO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_dig_l_mix[] = { + SOC_DAPM_SINGLE("Sto DAC Mix L Switch", RT5670_DIG_MIXER, + RT5670_M_STO_L_DAC_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DIG_MIXER, + RT5670_M_DAC_L2_DAC_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DIG_MIXER, + RT5670_M_DAC_R2_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_dig_r_mix[] = { + SOC_DAPM_SINGLE("Sto DAC Mix R Switch", RT5670_DIG_MIXER, + RT5670_M_STO_R_DAC_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_DIG_MIXER, + RT5670_M_DAC_R2_DAC_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_DIG_MIXER, + RT5670_M_DAC_L2_DAC_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5670_rec_l_mix[] = { + SOC_DAPM_SINGLE("INL Switch", RT5670_REC_L2_MIXER, + RT5670_M_IN_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5670_REC_L2_MIXER, + RT5670_M_BST2_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5670_REC_L2_MIXER, + RT5670_M_BST1_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_rec_r_mix[] = { + SOC_DAPM_SINGLE("INR Switch", RT5670_REC_R2_MIXER, + RT5670_M_IN_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5670_REC_R2_MIXER, + RT5670_M_BST2_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5670_REC_R2_MIXER, + RT5670_M_BST1_RM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_out_l_mix[] = { + SOC_DAPM_SINGLE("BST1 Switch", RT5670_OUT_L1_MIXER, + RT5670_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5670_OUT_L1_MIXER, + RT5670_M_IN_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5670_OUT_L1_MIXER, + RT5670_M_DAC_L2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_OUT_L1_MIXER, + RT5670_M_DAC_L1_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_out_r_mix[] = { + SOC_DAPM_SINGLE("BST2 Switch", RT5670_OUT_R1_MIXER, + RT5670_M_BST2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5670_OUT_R1_MIXER, + RT5670_M_IN_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5670_OUT_R1_MIXER, + RT5670_M_DAC_R2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_OUT_R1_MIXER, + RT5670_M_DAC_R1_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_hpo_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5670_HPO_MIXER, + RT5670_M_DAC1_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPVOL Switch", RT5670_HPO_MIXER, + RT5670_M_HPVOL_HM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_hpvoll_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5670_HPO_MIXER, + RT5670_M_DACL1_HML_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5670_HPO_MIXER, + RT5670_M_INL1_HML_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_hpvolr_mix[] = { + SOC_DAPM_SINGLE("DAC1 Switch", RT5670_HPO_MIXER, + RT5670_M_DACR1_HMR_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5670_HPO_MIXER, + RT5670_M_INR1_HMR_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5670_lout_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_LOUT_MIXER, + RT5670_M_DAC_L1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_LOUT_MIXER, + RT5670_M_DAC_R1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIX L Switch", RT5670_LOUT_MIXER, + RT5670_M_OV_L_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTMIX R Switch", RT5670_LOUT_MIXER, + RT5670_M_OV_R_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new lout_l_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5670_LOUT1, + RT5670_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new lout_r_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5670_LOUT1, + RT5670_R_MUTE_SFT, 1, 1); + +/* DAC1 L/R source */ /* MX-29 [9:8] [11:10] */ +static const char * const rt5670_dac1_src[] = { + "IF1 DAC", "IF2 DAC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_dac1l_enum, RT5670_AD_DA_MIXER, + RT5670_DAC1_L_SEL_SFT, rt5670_dac1_src); + +static const struct snd_kcontrol_new rt5670_dac1l_mux = + SOC_DAPM_ENUM("DAC1 L source", rt5670_dac1l_enum); + +static SOC_ENUM_SINGLE_DECL(rt5670_dac1r_enum, RT5670_AD_DA_MIXER, + RT5670_DAC1_R_SEL_SFT, rt5670_dac1_src); + +static const struct snd_kcontrol_new rt5670_dac1r_mux = + SOC_DAPM_ENUM("DAC1 R source", rt5670_dac1r_enum); + +/*DAC2 L/R source*/ /* MX-1B [6:4] [2:0] */ +/* TODO Use SOC_VALUE_ENUM_SINGLE_DECL */ +static const char * const rt5670_dac12_src[] = { + "IF1 DAC", "IF2 DAC", "IF3 DAC", "TxDC DAC", + "Bass", "VAD_ADC", "IF4 DAC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_dac2l_enum, RT5670_DAC_CTRL, + RT5670_DAC2_L_SEL_SFT, rt5670_dac12_src); + +static const struct snd_kcontrol_new rt5670_dac_l2_mux = + SOC_DAPM_ENUM("DAC2 L source", rt5670_dac2l_enum); + +static const char * const rt5670_dacr2_src[] = { + "IF1 DAC", "IF2 DAC", "IF3 DAC", "TxDC DAC", "TxDP ADC", "IF4 DAC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_dac2r_enum, RT5670_DAC_CTRL, + RT5670_DAC2_R_SEL_SFT, rt5670_dacr2_src); + +static const struct snd_kcontrol_new rt5670_dac_r2_mux = + SOC_DAPM_ENUM("DAC2 R source", rt5670_dac2r_enum); + +/*RxDP source*/ /* MX-2D [15:13] */ +static const char * const rt5670_rxdp_src[] = { + "IF2 DAC", "IF1 DAC", "STO1 ADC Mixer", "STO2 ADC Mixer", + "Mono ADC Mixer L", "Mono ADC Mixer R", "DAC1" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_rxdp_enum, RT5670_DSP_PATH1, + RT5670_RXDP_SEL_SFT, rt5670_rxdp_src); + +static const struct snd_kcontrol_new rt5670_rxdp_mux = + SOC_DAPM_ENUM("DAC2 L source", rt5670_rxdp_enum); + +/* MX-2D [1] [0] */ +static const char * const rt5670_dsp_bypass_src[] = { + "DSP", "Bypass" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_dsp_ul_enum, RT5670_DSP_PATH1, + RT5670_DSP_UL_SFT, rt5670_dsp_bypass_src); + +static const struct snd_kcontrol_new rt5670_dsp_ul_mux = + SOC_DAPM_ENUM("DSP UL source", rt5670_dsp_ul_enum); + +static SOC_ENUM_SINGLE_DECL(rt5670_dsp_dl_enum, RT5670_DSP_PATH1, + RT5670_DSP_DL_SFT, rt5670_dsp_bypass_src); + +static const struct snd_kcontrol_new rt5670_dsp_dl_mux = + SOC_DAPM_ENUM("DSP DL source", rt5670_dsp_dl_enum); + +/* Stereo2 ADC source */ +/* MX-26 [15] */ +static const char * const rt5670_stereo2_adc_lr_src[] = { + "L", "LR" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc_lr_enum, RT5670_STO2_ADC_MIXER, + RT5670_STO2_ADC_SRC_SFT, rt5670_stereo2_adc_lr_src); + +static const struct snd_kcontrol_new rt5670_sto2_adc_lr_mux = + SOC_DAPM_ENUM("Stereo2 ADC LR source", rt5670_stereo2_adc_lr_enum); + +/* Stereo1 ADC source */ +/* MX-27 MX-26 [12] */ +static const char * const rt5670_stereo_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_adc1_enum, RT5670_STO1_ADC_MIXER, + RT5670_ADC_1_SRC_SFT, rt5670_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5670_sto_adc_1_mux = + SOC_DAPM_ENUM("Stereo1 ADC 1 Mux", rt5670_stereo1_adc1_enum); + +static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc1_enum, RT5670_STO2_ADC_MIXER, + RT5670_ADC_1_SRC_SFT, rt5670_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5670_sto2_adc_1_mux = + SOC_DAPM_ENUM("Stereo2 ADC 1 Mux", rt5670_stereo2_adc1_enum); + + +/* MX-27 MX-26 [11] */ +static const char * const rt5670_stereo_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_adc2_enum, RT5670_STO1_ADC_MIXER, + RT5670_ADC_2_SRC_SFT, rt5670_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5670_sto_adc_2_mux = + SOC_DAPM_ENUM("Stereo1 ADC 2 Mux", rt5670_stereo1_adc2_enum); + +static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc2_enum, RT5670_STO2_ADC_MIXER, + RT5670_ADC_2_SRC_SFT, rt5670_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5670_sto2_adc_2_mux = + SOC_DAPM_ENUM("Stereo2 ADC 2 Mux", rt5670_stereo2_adc2_enum); + +/* MX-27 MX-26 [9:8] */ +static const char * const rt5670_stereo_dmic_src[] = { + "DMIC1", "DMIC2", "DMIC3" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_dmic_enum, RT5670_STO1_ADC_MIXER, + RT5670_DMIC_SRC_SFT, rt5670_stereo_dmic_src); + +static const struct snd_kcontrol_new rt5670_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC source", rt5670_stereo1_dmic_enum); + +static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_dmic_enum, RT5670_STO2_ADC_MIXER, + RT5670_DMIC_SRC_SFT, rt5670_stereo_dmic_src); + +static const struct snd_kcontrol_new rt5670_sto2_dmic_mux = + SOC_DAPM_ENUM("Stereo2 DMIC source", rt5670_stereo2_dmic_enum); + +/* Mono ADC source */ +/* MX-28 [12] */ +static const char * const rt5670_mono_adc_l1_src[] = { + "Mono DAC MIXL", "ADC1" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_mono_adc_l1_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_ADC_L1_SRC_SFT, rt5670_mono_adc_l1_src); + +static const struct snd_kcontrol_new rt5670_mono_adc_l1_mux = + SOC_DAPM_ENUM("Mono ADC1 left source", rt5670_mono_adc_l1_enum); +/* MX-28 [11] */ +static const char * const rt5670_mono_adc_l2_src[] = { + "Mono DAC MIXL", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_mono_adc_l2_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_ADC_L2_SRC_SFT, rt5670_mono_adc_l2_src); + +static const struct snd_kcontrol_new rt5670_mono_adc_l2_mux = + SOC_DAPM_ENUM("Mono ADC2 left source", rt5670_mono_adc_l2_enum); + +/* MX-28 [9:8] */ +static const char * const rt5670_mono_dmic_src[] = { + "DMIC1", "DMIC2", "DMIC3" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_mono_dmic_l_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_DMIC_L_SRC_SFT, rt5670_mono_dmic_src); + +static const struct snd_kcontrol_new rt5670_mono_dmic_l_mux = + SOC_DAPM_ENUM("Mono DMIC left source", rt5670_mono_dmic_l_enum); +/* MX-28 [1:0] */ +static SOC_ENUM_SINGLE_DECL(rt5670_mono_dmic_r_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_DMIC_R_SRC_SFT, rt5670_mono_dmic_src); + +static const struct snd_kcontrol_new rt5670_mono_dmic_r_mux = + SOC_DAPM_ENUM("Mono DMIC Right source", rt5670_mono_dmic_r_enum); +/* MX-28 [4] */ +static const char * const rt5670_mono_adc_r1_src[] = { + "Mono DAC MIXR", "ADC2" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_mono_adc_r1_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_ADC_R1_SRC_SFT, rt5670_mono_adc_r1_src); + +static const struct snd_kcontrol_new rt5670_mono_adc_r1_mux = + SOC_DAPM_ENUM("Mono ADC1 right source", rt5670_mono_adc_r1_enum); +/* MX-28 [3] */ +static const char * const rt5670_mono_adc_r2_src[] = { + "Mono DAC MIXR", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_mono_adc_r2_enum, RT5670_MONO_ADC_MIXER, + RT5670_MONO_ADC_R2_SRC_SFT, rt5670_mono_adc_r2_src); + +static const struct snd_kcontrol_new rt5670_mono_adc_r2_mux = + SOC_DAPM_ENUM("Mono ADC2 right source", rt5670_mono_adc_r2_enum); + +/* MX-2D [3:2] */ +static const char * const rt5670_txdp_slot_src[] = { + "Slot 0-1", "Slot 2-3", "Slot 4-5", "Slot 6-7" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_txdp_slot_enum, RT5670_DSP_PATH1, + RT5670_TXDP_SLOT_SEL_SFT, rt5670_txdp_slot_src); + +static const struct snd_kcontrol_new rt5670_txdp_slot_mux = + SOC_DAPM_ENUM("TxDP Slot source", rt5670_txdp_slot_enum); + +/* MX-2F [15] */ +static const char * const rt5670_if1_adc2_in_src[] = { + "IF_ADC2", "VAD_ADC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_if1_adc2_in_enum, RT5670_DIG_INF1_DATA, + RT5670_IF1_ADC2_IN_SFT, rt5670_if1_adc2_in_src); + +static const struct snd_kcontrol_new rt5670_if1_adc2_in_mux = + SOC_DAPM_ENUM("IF1 ADC2 IN source", rt5670_if1_adc2_in_enum); + +/* MX-2F [14:12] */ +static const char * const rt5670_if2_adc_in_src[] = { + "IF_ADC1", "IF_ADC2", "IF_ADC3", "TxDC_DAC", "TxDP_ADC", "VAD_ADC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_in_enum, RT5670_DIG_INF1_DATA, + RT5670_IF2_ADC_IN_SFT, rt5670_if2_adc_in_src); + +static const struct snd_kcontrol_new rt5670_if2_adc_in_mux = + SOC_DAPM_ENUM("IF2 ADC IN source", rt5670_if2_adc_in_enum); + +/* MX-31 [15] [13] [11] [9] */ +static const char * const rt5670_pdm_src[] = { + "Mono DAC", "Stereo DAC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_pdm1_l_enum, RT5670_PDM_OUT_CTRL, + RT5670_PDM1_L_SFT, rt5670_pdm_src); + +static const struct snd_kcontrol_new rt5670_pdm1_l_mux = + SOC_DAPM_ENUM("PDM1 L source", rt5670_pdm1_l_enum); + +static SOC_ENUM_SINGLE_DECL(rt5670_pdm1_r_enum, RT5670_PDM_OUT_CTRL, + RT5670_PDM1_R_SFT, rt5670_pdm_src); + +static const struct snd_kcontrol_new rt5670_pdm1_r_mux = + SOC_DAPM_ENUM("PDM1 R source", rt5670_pdm1_r_enum); + +static SOC_ENUM_SINGLE_DECL(rt5670_pdm2_l_enum, RT5670_PDM_OUT_CTRL, + RT5670_PDM2_L_SFT, rt5670_pdm_src); + +static const struct snd_kcontrol_new rt5670_pdm2_l_mux = + SOC_DAPM_ENUM("PDM2 L source", rt5670_pdm2_l_enum); + +static SOC_ENUM_SINGLE_DECL(rt5670_pdm2_r_enum, RT5670_PDM_OUT_CTRL, + RT5670_PDM2_R_SFT, rt5670_pdm_src); + +static const struct snd_kcontrol_new rt5670_pdm2_r_mux = + SOC_DAPM_ENUM("PDM2 R source", rt5670_pdm2_r_enum); + +/* MX-FA [12] */ +static const char * const rt5670_if1_adc1_in1_src[] = { + "IF_ADC1", "IF1_ADC3" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_if1_adc1_in1_enum, RT5670_DIG_MISC, + RT5670_IF1_ADC1_IN1_SFT, rt5670_if1_adc1_in1_src); + +static const struct snd_kcontrol_new rt5670_if1_adc1_in1_mux = + SOC_DAPM_ENUM("IF1 ADC1 IN1 source", rt5670_if1_adc1_in1_enum); + +/* MX-FA [11] */ +static const char * const rt5670_if1_adc1_in2_src[] = { + "IF1_ADC1_IN1", "IF1_ADC4" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_if1_adc1_in2_enum, RT5670_DIG_MISC, + RT5670_IF1_ADC1_IN2_SFT, rt5670_if1_adc1_in2_src); + +static const struct snd_kcontrol_new rt5670_if1_adc1_in2_mux = + SOC_DAPM_ENUM("IF1 ADC1 IN2 source", rt5670_if1_adc1_in2_enum); + +/* MX-FA [10] */ +static const char * const rt5670_if1_adc2_in1_src[] = { + "IF1_ADC2_IN", "IF1_ADC4" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_if1_adc2_in1_enum, RT5670_DIG_MISC, + RT5670_IF1_ADC2_IN1_SFT, rt5670_if1_adc2_in1_src); + +static const struct snd_kcontrol_new rt5670_if1_adc2_in1_mux = + SOC_DAPM_ENUM("IF1 ADC2 IN1 source", rt5670_if1_adc2_in1_enum); + +/* MX-9D [9:8] */ +static const char * const rt5670_vad_adc_src[] = { + "Sto1 ADC L", "Mono ADC L", "Mono ADC R", "Sto2 ADC L" +}; + +static SOC_ENUM_SINGLE_DECL(rt5670_vad_adc_enum, RT5670_VAD_CTRL4, + RT5670_VAD_SEL_SFT, rt5670_vad_adc_src); + +static const struct snd_kcontrol_new rt5670_vad_adc_mux = + SOC_DAPM_ENUM("VAD ADC source", rt5670_vad_adc_enum); + +static int rt5670_hp_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt5670->regmap, RT5670_CHARGE_PUMP, + RT5670_PM_HP_MASK, RT5670_PM_HP_HV); + regmap_update_bits(rt5670->regmap, RT5670_GEN_CTRL2, + 0x0400, 0x0400); + /* headphone amp power on */ + regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1, + RT5670_PWR_HA | RT5670_PWR_FV1 | + RT5670_PWR_FV2, RT5670_PWR_HA | + RT5670_PWR_FV1 | RT5670_PWR_FV2); + /* depop parameters */ + regmap_write(rt5670->regmap, RT5670_DEPOP_M2, 0x3100); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8009); + regmap_write(rt5670->regmap, RT5670_PR_BASE + + RT5670_HP_DCC_INT1, 0x9f00); + mdelay(20); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8019); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x0004); + msleep(30); + break; + default: + return 0; + } + + return 0; +} + +static int rt5670_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* headphone unmute sequence */ + regmap_write(rt5670->regmap, RT5670_PR_BASE + + RT5670_MAMP_INT_REG2, 0xb400); + regmap_write(rt5670->regmap, RT5670_DEPOP_M3, 0x0772); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x805d); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x831d); + regmap_update_bits(rt5670->regmap, RT5670_GEN_CTRL2, + 0x0300, 0x0300); + regmap_update_bits(rt5670->regmap, RT5670_HP_VOL, + RT5670_L_MUTE | RT5670_R_MUTE, 0); + msleep(80); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8019); + break; + + case SND_SOC_DAPM_PRE_PMD: + /* headphone mute sequence */ + regmap_write(rt5670->regmap, RT5670_PR_BASE + + RT5670_MAMP_INT_REG2, 0xb400); + regmap_write(rt5670->regmap, RT5670_DEPOP_M3, 0x0772); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x803d); + mdelay(10); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x831d); + mdelay(10); + regmap_update_bits(rt5670->regmap, RT5670_HP_VOL, + RT5670_L_MUTE | RT5670_R_MUTE, + RT5670_L_MUTE | RT5670_R_MUTE); + msleep(20); + regmap_update_bits(rt5670->regmap, + RT5670_GEN_CTRL2, 0x0300, 0x0); + regmap_write(rt5670->regmap, RT5670_DEPOP_M1, 0x8019); + regmap_write(rt5670->regmap, RT5670_DEPOP_M3, 0x0707); + regmap_write(rt5670->regmap, RT5670_PR_BASE + + RT5670_MAMP_INT_REG2, 0xfc00); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5670_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + if (!rt5670->gpio1_is_ext_spk_en) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2, + RT5670_GP1_OUT_MASK, RT5670_GP1_OUT_HI); + break; + + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2, + RT5670_GP1_OUT_MASK, RT5670_GP1_OUT_LO); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5670_bst1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5670_PWR_ANLG2, + RT5670_PWR_BST1_P, RT5670_PWR_BST1_P); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5670_PWR_ANLG2, + RT5670_PWR_BST1_P, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5670_bst2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5670_PWR_ANLG2, + RT5670_PWR_BST2_P, RT5670_PWR_BST2_P); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5670_PWR_ANLG2, + RT5670_PWR_BST2_P, 0); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL1", RT5670_PWR_ANLG2, + RT5670_PWR_PLL_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S DSP", RT5670_PWR_DIG2, + RT5670_PWR_I2S_DSP_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5670_PWR_VOL, + RT5670_PWR_MIC_DET_BIT, 0, NULL, 0), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5670_ASRC_1, + 11, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5670_ASRC_1, + 12, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5670_ASRC_1, + 10, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5670_ASRC_1, + 9, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5670_ASRC_1, + 8, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5670_ASRC_1, + 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5670_ASRC_1, + 6, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5670_ASRC_1, + 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5670_ASRC_1, + 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5670_ASRC_1, + 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5670_ASRC_1, + 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5670_ASRC_1, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5670_ASRC_1, + 0, 0, NULL, 0), + + /* Input Side */ + /* micbias */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5670_PWR_ANLG2, + RT5670_PWR_MB1_BIT, 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + SND_SOC_DAPM_INPUT("DMIC L2"), + SND_SOC_DAPM_INPUT("DMIC R2"), + SND_SOC_DAPM_INPUT("DMIC L3"), + SND_SOC_DAPM_INPUT("DMIC R3"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5670_DMIC_CTRL1, + RT5670_DMIC_1_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5670_DMIC_CTRL1, + RT5670_DMIC_2_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC3 Power", RT5670_DMIC_CTRL1, + RT5670_DMIC_3_EN_SFT, 0, NULL, 0), + /* Boost */ + SND_SOC_DAPM_PGA_E("BST1", RT5670_PWR_ANLG2, RT5670_PWR_BST1_BIT, + 0, NULL, 0, rt5670_bst1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("BST2", RT5670_PWR_ANLG2, RT5670_PWR_BST2_BIT, + 0, NULL, 0, rt5670_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + /* Input Volume */ + SND_SOC_DAPM_PGA("INL VOL", RT5670_PWR_VOL, + RT5670_PWR_IN_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR VOL", RT5670_PWR_VOL, + RT5670_PWR_IN_R_BIT, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5670_PWR_MIXER, RT5670_PWR_RM_L_BIT, 0, + rt5670_rec_l_mix, ARRAY_SIZE(rt5670_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5670_PWR_MIXER, RT5670_PWR_RM_R_BIT, 0, + rt5670_rec_r_mix, ARRAY_SIZE(rt5670_rec_r_mix)), + /* ADCs */ + SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC 2", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_PGA("ADC 1_2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("ADC 1 power", RT5670_PWR_DIG1, + RT5670_PWR_ADC_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC 2 power", RT5670_PWR_DIG1, + RT5670_PWR_ADC_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC clock", RT5670_PR_BASE + + RT5670_CHOP_DAC_ADC, 12, 0, NULL, 0), + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto_adc_2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto_adc_2_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto_adc_1_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto_adc_1_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_adc_2_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_adc_2_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_adc_1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_adc_1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC LR Mux", SND_SOC_NOPM, 0, 0, + &rt5670_sto2_adc_lr_mux), + SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_dmic_l_mux), + SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_dmic_r_mux), + SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_adc_l2_mux), + SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_adc_l1_mux), + SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_adc_r1_mux), + SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_mono_adc_r2_mux), + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5670_PWR_DIG2, + RT5670_PWR_ADC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC Stereo2 Filter", RT5670_PWR_DIG2, + RT5670_PWR_ADC_S2F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5670_sto1_adc_l_mix, ARRAY_SIZE(rt5670_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5670_sto1_adc_r_mix, ARRAY_SIZE(rt5670_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5670_sto2_adc_l_mix, + ARRAY_SIZE(rt5670_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5670_sto2_adc_r_mix, + ARRAY_SIZE(rt5670_sto2_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("ADC Mono Left Filter", RT5670_PWR_DIG2, + RT5670_PWR_ADC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXL", RT5670_MONO_ADC_DIG_VOL, + RT5670_L_MUTE_SFT, 1, rt5670_mono_adc_l_mix, + ARRAY_SIZE(rt5670_mono_adc_l_mix)), + SND_SOC_DAPM_SUPPLY("ADC Mono Right Filter", RT5670_PWR_DIG2, + RT5670_PWR_ADC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXR", RT5670_MONO_ADC_DIG_VOL, + RT5670_R_MUTE_SFT, 1, rt5670_mono_adc_r_mix, + ARRAY_SIZE(rt5670_mono_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("VAD_ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DSP */ + SND_SOC_DAPM_PGA("TxDP_ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("TxDP_ADC_L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("TxDP_ADC_R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("TxDC_DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("TDM Data Mux", SND_SOC_NOPM, 0, 0, + &rt5670_txdp_slot_mux), + + SND_SOC_DAPM_MUX("DSP UL Mux", SND_SOC_NOPM, 0, 0, + &rt5670_dsp_ul_mux), + SND_SOC_DAPM_MUX("DSP DL Mux", SND_SOC_NOPM, 0, 0, + &rt5670_dsp_dl_mux), + + SND_SOC_DAPM_MUX("RxDP Mux", SND_SOC_NOPM, 0, 0, + &rt5670_rxdp_mux), + + /* IF2 Mux */ + SND_SOC_DAPM_MUX("IF2 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5670_if2_adc_in_mux), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5670_PWR_DIG1, + RT5670_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5670_PWR_DIG1, + RT5670_PWR_I2S2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1 ADC1 IN1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_if1_adc1_in1_mux), + SND_SOC_DAPM_MUX("IF1 ADC1 IN2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_if1_adc1_in2_mux), + SND_SOC_DAPM_MUX("IF1 ADC2 IN Mux", SND_SOC_NOPM, 0, 0, + &rt5670_if1_adc2_in_mux), + SND_SOC_DAPM_MUX("IF1 ADC2 IN1 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_if1_adc2_in1_mux), + SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5670_vad_adc_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, + RT5670_GPIO_CTRL1, RT5670_I2S2_PIN_SFT, 1), + + /* Audio DSP */ + SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5670_dac_l_mix, ARRAY_SIZE(rt5670_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5670_dac_r_mix, ARRAY_SIZE(rt5670_dac_r_mix)), + SND_SOC_DAPM_PGA("DAC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DAC2 channel Mux */ + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5670_dac_r2_mux), + SND_SOC_DAPM_PGA("DAC L2 Volume", RT5670_PWR_DIG1, + RT5670_PWR_DAC_L2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC R2 Volume", RT5670_PWR_DIG1, + RT5670_PWR_DAC_R2_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DAC1 L Mux", SND_SOC_NOPM, 0, 0, &rt5670_dac1l_mux), + SND_SOC_DAPM_MUX("DAC1 R Mux", SND_SOC_NOPM, 0, 0, &rt5670_dac1r_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5670_PWR_DIG2, + RT5670_PWR_DAC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mono Left Filter", RT5670_PWR_DIG2, + RT5670_PWR_DAC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC Mono Right Filter", RT5670_PWR_DIG2, + RT5670_PWR_DAC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5670_sto_dac_l_mix, + ARRAY_SIZE(rt5670_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5670_sto_dac_r_mix, + ARRAY_SIZE(rt5670_sto_dac_r_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5670_mono_dac_l_mix, + ARRAY_SIZE(rt5670_mono_dac_l_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5670_mono_dac_r_mix, + ARRAY_SIZE(rt5670_mono_dac_r_mix)), + SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5670_dig_l_mix, + ARRAY_SIZE(rt5670_dig_l_mix)), + SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5670_dig_r_mix, + ARRAY_SIZE(rt5670_dig_r_mix)), + + /* DACs */ + SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5670_PWR_DIG1, + RT5670_PWR_DAC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5670_PWR_DIG1, + RT5670_PWR_DAC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC L2", NULL, RT5670_PWR_DIG1, + RT5670_PWR_DAC_L2_BIT, 0), + + SND_SOC_DAPM_DAC("DAC R2", NULL, RT5670_PWR_DIG1, + RT5670_PWR_DAC_R2_BIT, 0), + /* OUT Mixer */ + + SND_SOC_DAPM_MIXER("OUT MIXL", RT5670_PWR_MIXER, RT5670_PWR_OM_L_BIT, + 0, rt5670_out_l_mix, ARRAY_SIZE(rt5670_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5670_PWR_MIXER, RT5670_PWR_OM_R_BIT, + 0, rt5670_out_r_mix, ARRAY_SIZE(rt5670_out_r_mix)), + /* Ouput Volume */ + SND_SOC_DAPM_MIXER("HPOVOL MIXL", RT5670_PWR_VOL, + RT5670_PWR_HV_L_BIT, 0, + rt5670_hpvoll_mix, ARRAY_SIZE(rt5670_hpvoll_mix)), + SND_SOC_DAPM_MIXER("HPOVOL MIXR", RT5670_PWR_VOL, + RT5670_PWR_HV_R_BIT, 0, + rt5670_hpvolr_mix, ARRAY_SIZE(rt5670_hpvolr_mix)), + SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0, + rt5670_hpo_mix, ARRAY_SIZE(rt5670_hpo_mix)), + SND_SOC_DAPM_MIXER("LOUT MIX", RT5670_PWR_ANLG1, RT5670_PWR_LM_BIT, + 0, rt5670_lout_mix, ARRAY_SIZE(rt5670_lout_mix)), + SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM, 0, 0, + rt5670_hp_power_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("HP L Amp", RT5670_PWR_ANLG1, + RT5670_PWR_HP_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP R Amp", RT5670_PWR_ANLG1, + RT5670_PWR_HP_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, + rt5670_hp_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SWITCH("LOUT L Playback", SND_SOC_NOPM, 0, 0, + &lout_l_enable_control), + SND_SOC_DAPM_SWITCH("LOUT R Playback", SND_SOC_NOPM, 0, 0, + &lout_r_enable_control), + SND_SOC_DAPM_PGA("LOUT Amp", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* PDM */ + SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5670_PWR_DIG2, + RT5670_PWR_PDM1_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MUX("PDM1 L Mux", RT5670_PDM_OUT_CTRL, + RT5670_M_PDM1_L_SFT, 1, &rt5670_pdm1_l_mux), + SND_SOC_DAPM_MUX("PDM1 R Mux", RT5670_PDM_OUT_CTRL, + RT5670_M_PDM1_R_SFT, 1, &rt5670_pdm1_r_mux), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), +}; + +static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2, + RT5670_PWR_PDM2_BIT, 0, NULL, 0), + SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL, + RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux), + SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL, + RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux), + SND_SOC_DAPM_OUTPUT("PDM1L"), + SND_SOC_DAPM_OUTPUT("PDM1R"), + SND_SOC_DAPM_OUTPUT("PDM2L"), + SND_SOC_DAPM_OUTPUT("PDM2R"), +}; + +static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = { + SND_SOC_DAPM_PGA_E("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0, + rt5670_spk_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOLP"), + SND_SOC_DAPM_OUTPUT("SPOLN"), + SND_SOC_DAPM_OUTPUT("SPORP"), + SND_SOC_DAPM_OUTPUT("SPORN"), +}; + +static const struct snd_soc_dapm_route rt5670_dapm_routes[] = { + { "ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc }, + { "ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc }, + { "ADC Mono Left Filter", NULL, "ADC MONO L ASRC", is_using_asrc }, + { "ADC Mono Right Filter", NULL, "ADC MONO R ASRC", is_using_asrc }, + { "DAC Mono Left Filter", NULL, "DAC MONO L ASRC", is_using_asrc }, + { "DAC Mono Right Filter", NULL, "DAC MONO R ASRC", is_using_asrc }, + { "DAC Stereo1 Filter", NULL, "DAC STO ASRC", is_using_asrc }, + { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc }, + { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc }, + { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc }, + { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc }, + + { "I2S1", NULL, "I2S1 ASRC", can_use_asrc}, + { "I2S2", NULL, "I2S2 ASRC", can_use_asrc}, + + { "DMIC1", NULL, "DMIC L1" }, + { "DMIC1", NULL, "DMIC R1" }, + { "DMIC2", NULL, "DMIC L2" }, + { "DMIC2", NULL, "DMIC R2" }, + { "DMIC3", NULL, "DMIC L3" }, + { "DMIC3", NULL, "DMIC R3" }, + + { "BST1", NULL, "IN1P" }, + { "BST1", NULL, "IN1N" }, + { "BST1", NULL, "Mic Det Power" }, + { "BST2", NULL, "IN2P" }, + { "BST2", NULL, "IN2N" }, + + { "INL VOL", NULL, "IN2P" }, + { "INR VOL", NULL, "IN2N" }, + + { "RECMIXL", "INL Switch", "INL VOL" }, + { "RECMIXL", "BST2 Switch", "BST2" }, + { "RECMIXL", "BST1 Switch", "BST1" }, + + { "RECMIXR", "INR Switch", "INR VOL" }, + { "RECMIXR", "BST2 Switch", "BST2" }, + { "RECMIXR", "BST1 Switch", "BST1" }, + + { "ADC 1", NULL, "RECMIXL" }, + { "ADC 1", NULL, "ADC 1 power" }, + { "ADC 1", NULL, "ADC clock" }, + { "ADC 2", NULL, "RECMIXR" }, + { "ADC 2", NULL, "ADC 2 power" }, + { "ADC 2", NULL, "ADC clock" }, + + { "DMIC L1", NULL, "DMIC CLK" }, + { "DMIC L1", NULL, "DMIC1 Power" }, + { "DMIC R1", NULL, "DMIC CLK" }, + { "DMIC R1", NULL, "DMIC1 Power" }, + { "DMIC L2", NULL, "DMIC CLK" }, + { "DMIC L2", NULL, "DMIC2 Power" }, + { "DMIC R2", NULL, "DMIC CLK" }, + { "DMIC R2", NULL, "DMIC2 Power" }, + { "DMIC L3", NULL, "DMIC CLK" }, + { "DMIC L3", NULL, "DMIC3 Power" }, + { "DMIC R3", NULL, "DMIC CLK" }, + { "DMIC R3", NULL, "DMIC3 Power" }, + + { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" }, + { "Stereo1 DMIC Mux", "DMIC3", "DMIC3" }, + + { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" }, + { "Stereo2 DMIC Mux", "DMIC3", "DMIC3" }, + + { "Mono DMIC L Mux", "DMIC1", "DMIC L1" }, + { "Mono DMIC L Mux", "DMIC2", "DMIC L2" }, + { "Mono DMIC L Mux", "DMIC3", "DMIC L3" }, + + { "Mono DMIC R Mux", "DMIC1", "DMIC R1" }, + { "Mono DMIC R Mux", "DMIC2", "DMIC R2" }, + { "Mono DMIC R Mux", "DMIC3", "DMIC R3" }, + + { "ADC 1_2", NULL, "ADC 1" }, + { "ADC 1_2", NULL, "ADC 2" }, + + { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" }, + { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" }, + { "Stereo1 ADC L1 Mux", "ADC", "ADC 1_2" }, + { "Stereo1 ADC L1 Mux", "DAC MIX", "DAC MIXL" }, + + { "Stereo1 ADC R1 Mux", "ADC", "ADC 1_2" }, + { "Stereo1 ADC R1 Mux", "DAC MIX", "DAC MIXR" }, + { "Stereo1 ADC R2 Mux", "DMIC", "Stereo1 DMIC Mux" }, + { "Stereo1 ADC R2 Mux", "DAC MIX", "DAC MIXR" }, + + { "Mono ADC L2 Mux", "DMIC", "Mono DMIC L Mux" }, + { "Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL" }, + { "Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL" }, + { "Mono ADC L1 Mux", "ADC1", "ADC 1" }, + + { "Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR" }, + { "Mono ADC R1 Mux", "ADC2", "ADC 2" }, + { "Mono ADC R2 Mux", "DMIC", "Mono DMIC R Mux" }, + { "Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR" }, + + { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux" }, + { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux" }, + { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux" }, + { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter" }, + + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + { "Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter" }, + { "ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux" }, + { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux" }, + { "Mono ADC MIXL", NULL, "ADC Mono Left Filter" }, + { "ADC Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux" }, + { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux" }, + { "Mono ADC MIXR", NULL, "ADC Mono Right Filter" }, + { "ADC Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo2 ADC L2 Mux", "DMIC", "Stereo2 DMIC Mux" }, + { "Stereo2 ADC L2 Mux", "DAC MIX", "DAC MIXL" }, + { "Stereo2 ADC L1 Mux", "ADC", "ADC 1_2" }, + { "Stereo2 ADC L1 Mux", "DAC MIX", "DAC MIXL" }, + + { "Stereo2 ADC R1 Mux", "ADC", "ADC 1_2" }, + { "Stereo2 ADC R1 Mux", "DAC MIX", "DAC MIXR" }, + { "Stereo2 ADC R2 Mux", "DMIC", "Stereo2 DMIC Mux" }, + { "Stereo2 ADC R2 Mux", "DAC MIX", "DAC MIXR" }, + + { "Sto2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC L1 Mux" }, + { "Sto2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC L2 Mux" }, + { "Sto2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux" }, + { "Sto2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC R2 Mux" }, + + { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXL" }, + { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXR" }, + + { "Stereo2 ADC LR Mux", "L", "Sto2 ADC MIXL" }, + { "Stereo2 ADC LR Mux", "LR", "Sto2 ADC LR MIX" }, + + { "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" }, + { "Stereo2 ADC MIXL", NULL, "ADC Stereo2 Filter" }, + + { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" }, + { "Stereo2 ADC MIXR", NULL, "ADC Stereo2 Filter" }, + { "ADC Stereo2 Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "VAD ADC Mux", "Sto1 ADC L", "Stereo1 ADC MIXL" }, + { "VAD ADC Mux", "Mono ADC L", "Mono ADC MIXL" }, + { "VAD ADC Mux", "Mono ADC R", "Mono ADC MIXR" }, + { "VAD ADC Mux", "Sto2 ADC L", "Sto2 ADC MIXL" }, + + { "VAD_ADC", NULL, "VAD ADC Mux" }, + + { "IF_ADC1", NULL, "Stereo1 ADC MIXL" }, + { "IF_ADC1", NULL, "Stereo1 ADC MIXR" }, + { "IF_ADC2", NULL, "Mono ADC MIXL" }, + { "IF_ADC2", NULL, "Mono ADC MIXR" }, + { "IF_ADC3", NULL, "Stereo2 ADC MIXL" }, + { "IF_ADC3", NULL, "Stereo2 ADC MIXR" }, + + { "IF1 ADC1 IN1 Mux", "IF_ADC1", "IF_ADC1" }, + { "IF1 ADC1 IN1 Mux", "IF1_ADC3", "IF1_ADC3" }, + + { "IF1 ADC1 IN2 Mux", "IF1_ADC1_IN1", "IF1 ADC1 IN1 Mux" }, + { "IF1 ADC1 IN2 Mux", "IF1_ADC4", "TxDP_ADC" }, + + { "IF1 ADC2 IN Mux", "IF_ADC2", "IF_ADC2" }, + { "IF1 ADC2 IN Mux", "VAD_ADC", "VAD_ADC" }, + + { "IF1 ADC2 IN1 Mux", "IF1_ADC2_IN", "IF1 ADC2 IN Mux" }, + { "IF1 ADC2 IN1 Mux", "IF1_ADC4", "TxDP_ADC" }, + + { "IF1_ADC1" , NULL, "IF1 ADC1 IN2 Mux" }, + { "IF1_ADC2" , NULL, "IF1 ADC2 IN1 Mux" }, + + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" }, + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" }, + { "Stereo2 ADC MIX", NULL, "Sto2 ADC MIXL" }, + { "Stereo2 ADC MIX", NULL, "Sto2 ADC MIXR" }, + { "Mono ADC MIX", NULL, "Mono ADC MIXL" }, + { "Mono ADC MIX", NULL, "Mono ADC MIXR" }, + + { "RxDP Mux", "IF2 DAC", "IF2 DAC" }, + { "RxDP Mux", "IF1 DAC", "IF1 DAC2" }, + { "RxDP Mux", "STO1 ADC Mixer", "Stereo1 ADC MIX" }, + { "RxDP Mux", "STO2 ADC Mixer", "Stereo2 ADC MIX" }, + { "RxDP Mux", "Mono ADC Mixer L", "Mono ADC MIXL" }, + { "RxDP Mux", "Mono ADC Mixer R", "Mono ADC MIXR" }, + { "RxDP Mux", "DAC1", "DAC MIX" }, + + { "TDM Data Mux", "Slot 0-1", "Stereo1 ADC MIX" }, + { "TDM Data Mux", "Slot 2-3", "Mono ADC MIX" }, + { "TDM Data Mux", "Slot 4-5", "Stereo2 ADC MIX" }, + { "TDM Data Mux", "Slot 6-7", "IF2 DAC" }, + + { "DSP UL Mux", "Bypass", "TDM Data Mux" }, + { "DSP UL Mux", NULL, "I2S DSP" }, + { "DSP DL Mux", "Bypass", "RxDP Mux" }, + { "DSP DL Mux", NULL, "I2S DSP" }, + + { "TxDP_ADC_L", NULL, "DSP UL Mux" }, + { "TxDP_ADC_R", NULL, "DSP UL Mux" }, + { "TxDC_DAC", NULL, "DSP DL Mux" }, + + { "TxDP_ADC", NULL, "TxDP_ADC_L" }, + { "TxDP_ADC", NULL, "TxDP_ADC_R" }, + + { "IF1 ADC", NULL, "I2S1" }, + { "IF1 ADC", NULL, "IF1_ADC1" }, + { "IF1 ADC", NULL, "IF1_ADC2" }, + { "IF1 ADC", NULL, "IF_ADC3" }, + { "IF1 ADC", NULL, "TxDP_ADC" }, + + { "IF2 ADC Mux", "IF_ADC1", "IF_ADC1" }, + { "IF2 ADC Mux", "IF_ADC2", "IF_ADC2" }, + { "IF2 ADC Mux", "IF_ADC3", "IF_ADC3" }, + { "IF2 ADC Mux", "TxDC_DAC", "TxDC_DAC" }, + { "IF2 ADC Mux", "TxDP_ADC", "TxDP_ADC" }, + { "IF2 ADC Mux", "VAD_ADC", "VAD_ADC" }, + + { "IF2 ADC L", NULL, "IF2 ADC Mux" }, + { "IF2 ADC R", NULL, "IF2 ADC Mux" }, + + { "IF2 ADC", NULL, "I2S2" }, + { "IF2 ADC", NULL, "IF2 ADC L" }, + { "IF2 ADC", NULL, "IF2 ADC R" }, + + { "AIF1TX", NULL, "IF1 ADC" }, + { "AIF2TX", NULL, "IF2 ADC" }, + + { "IF1 DAC1", NULL, "AIF1RX" }, + { "IF1 DAC2", NULL, "AIF1RX" }, + { "IF2 DAC", NULL, "AIF2RX" }, + + { "IF1 DAC1", NULL, "I2S1" }, + { "IF1 DAC2", NULL, "I2S1" }, + { "IF2 DAC", NULL, "I2S2" }, + + { "IF1 DAC2 L", NULL, "IF1 DAC2" }, + { "IF1 DAC2 R", NULL, "IF1 DAC2" }, + { "IF1 DAC1 L", NULL, "IF1 DAC1" }, + { "IF1 DAC1 R", NULL, "IF1 DAC1" }, + { "IF2 DAC L", NULL, "IF2 DAC" }, + { "IF2 DAC R", NULL, "IF2 DAC" }, + + { "DAC1 L Mux", "IF1 DAC", "IF1 DAC1 L" }, + { "DAC1 L Mux", "IF2 DAC", "IF2 DAC L" }, + + { "DAC1 R Mux", "IF1 DAC", "IF1 DAC1 R" }, + { "DAC1 R Mux", "IF2 DAC", "IF2 DAC R" }, + + { "DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL" }, + { "DAC1 MIXL", "DAC1 Switch", "DAC1 L Mux" }, + { "DAC1 MIXL", NULL, "DAC Stereo1 Filter" }, + { "DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR" }, + { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" }, + { "DAC1 MIXR", NULL, "DAC Stereo1 Filter" }, + + { "DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll }, + { "DAC Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "DAC MIX", NULL, "DAC1 MIXL" }, + { "DAC MIX", NULL, "DAC1 MIXR" }, + + { "Audio DSP", NULL, "DAC1 MIXL" }, + { "Audio DSP", NULL, "DAC1 MIXR" }, + + { "DAC L2 Mux", "IF1 DAC", "IF1 DAC2 L" }, + { "DAC L2 Mux", "IF2 DAC", "IF2 DAC L" }, + { "DAC L2 Mux", "TxDC DAC", "TxDC_DAC" }, + { "DAC L2 Mux", "VAD_ADC", "VAD_ADC" }, + { "DAC L2 Volume", NULL, "DAC L2 Mux" }, + { "DAC L2 Volume", NULL, "DAC Mono Left Filter" }, + + { "DAC R2 Mux", "IF1 DAC", "IF1 DAC2 R" }, + { "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" }, + { "DAC R2 Mux", "TxDC DAC", "TxDC_DAC" }, + { "DAC R2 Mux", "TxDP ADC", "TxDP_ADC" }, + { "DAC R2 Volume", NULL, "DAC R2 Mux" }, + { "DAC R2 Volume", NULL, "DAC Mono Right Filter" }, + + { "Stereo DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXL", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "Stereo DAC MIXL", NULL, "DAC Stereo1 Filter" }, + { "Stereo DAC MIXL", NULL, "DAC L1 Power" }, + { "Stereo DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXR", "DAC L1 Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "Stereo DAC MIXR", NULL, "DAC Stereo1 Filter" }, + { "Stereo DAC MIXR", NULL, "DAC R1 Power" }, + + { "Mono DAC MIXL", "DAC L1 Switch", "DAC1 MIXL" }, + { "Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" }, + { "Mono DAC MIXL", NULL, "DAC Mono Left Filter" }, + { "Mono DAC MIXR", "DAC R1 Switch", "DAC1 MIXR" }, + { "Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" }, + { "Mono DAC MIXR", NULL, "DAC Mono Right Filter" }, + + { "DAC MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" }, + { "DAC MIXL", "DAC L2 Switch", "DAC L2 Volume" }, + { "DAC MIXL", "DAC R2 Switch", "DAC R2 Volume" }, + { "DAC MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" }, + { "DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" }, + { "DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" }, + + { "DAC L1", NULL, "DAC L1 Power" }, + { "DAC L1", NULL, "Stereo DAC MIXL" }, + { "DAC R1", NULL, "DAC R1 Power" }, + { "DAC R1", NULL, "Stereo DAC MIXR" }, + { "DAC L2", NULL, "Mono DAC MIXL" }, + { "DAC R2", NULL, "Mono DAC MIXR" }, + + { "OUT MIXL", "BST1 Switch", "BST1" }, + { "OUT MIXL", "INL Switch", "INL VOL" }, + { "OUT MIXL", "DAC L2 Switch", "DAC L2" }, + { "OUT MIXL", "DAC L1 Switch", "DAC L1" }, + + { "OUT MIXR", "BST2 Switch", "BST2" }, + { "OUT MIXR", "INR Switch", "INR VOL" }, + { "OUT MIXR", "DAC R2 Switch", "DAC R2" }, + { "OUT MIXR", "DAC R1 Switch", "DAC R1" }, + + { "HPOVOL MIXL", "DAC1 Switch", "DAC L1" }, + { "HPOVOL MIXL", "INL Switch", "INL VOL" }, + { "HPOVOL MIXR", "DAC1 Switch", "DAC R1" }, + { "HPOVOL MIXR", "INR Switch", "INR VOL" }, + + { "DAC 2", NULL, "DAC L2" }, + { "DAC 2", NULL, "DAC R2" }, + { "DAC 1", NULL, "DAC L1" }, + { "DAC 1", NULL, "DAC R1" }, + { "HPOVOL", NULL, "HPOVOL MIXL" }, + { "HPOVOL", NULL, "HPOVOL MIXR" }, + { "HPO MIX", "DAC1 Switch", "DAC 1" }, + { "HPO MIX", "HPVOL Switch", "HPOVOL" }, + + { "LOUT MIX", "DAC L1 Switch", "DAC L1" }, + { "LOUT MIX", "DAC R1 Switch", "DAC R1" }, + { "LOUT MIX", "OUTMIX L Switch", "OUT MIXL" }, + { "LOUT MIX", "OUTMIX R Switch", "OUT MIXR" }, + + { "PDM1 L Mux", "Stereo DAC", "Stereo DAC MIXL" }, + { "PDM1 L Mux", "Mono DAC", "Mono DAC MIXL" }, + { "PDM1 L Mux", NULL, "PDM1 Power" }, + { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" }, + { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" }, + { "PDM1 R Mux", NULL, "PDM1 Power" }, + + { "HP Amp", NULL, "HPO MIX" }, + { "HP Amp", NULL, "Mic Det Power" }, + { "HPOL", NULL, "HP Amp" }, + { "HPOL", NULL, "HP L Amp" }, + { "HPOL", NULL, "Improve HP Amp Drv" }, + { "HPOR", NULL, "HP Amp" }, + { "HPOR", NULL, "HP R Amp" }, + { "HPOR", NULL, "Improve HP Amp Drv" }, + + { "LOUT Amp", NULL, "LOUT MIX" }, + { "LOUT L Playback", "Switch", "LOUT Amp" }, + { "LOUT R Playback", "Switch", "LOUT Amp" }, + { "LOUTL", NULL, "LOUT L Playback" }, + { "LOUTR", NULL, "LOUT R Playback" }, + { "LOUTL", NULL, "Improve HP Amp Drv" }, + { "LOUTR", NULL, "Improve HP Amp Drv" }, +}; + +static const struct snd_soc_dapm_route rt5670_specific_dapm_routes[] = { + { "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" }, + { "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" }, + { "PDM2 L Mux", NULL, "PDM2 Power" }, + { "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" }, + { "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" }, + { "PDM2 R Mux", NULL, "PDM2 Power" }, + { "PDM1L", NULL, "PDM1 L Mux" }, + { "PDM1R", NULL, "PDM1 R Mux" }, + { "PDM2L", NULL, "PDM2 L Mux" }, + { "PDM2R", NULL, "PDM2 R Mux" }, +}; + +static const struct snd_soc_dapm_route rt5672_specific_dapm_routes[] = { + { "SPO Amp", NULL, "PDM1 L Mux" }, + { "SPO Amp", NULL, "PDM1 R Mux" }, + { "SPOLP", NULL, "SPO Amp" }, + { "SPOLN", NULL, "SPO Amp" }, + { "SPORP", NULL, "SPO Amp" }, + { "SPORN", NULL, "SPO Amp" }, +}; + +static int rt5670_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt5670->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5670->sysclk, rt5670->lrck[dai->id]); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting %d for DAI %d\n", + rt5670->lrck[dai->id], dai->id); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + bclk_ms = frame_size > 32; + rt5670->bclk[dai->id] = rt5670->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5670->bclk[dai->id], rt5670->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= RT5670_I2S_DL_20; + break; + case 24: + val_len |= RT5670_I2S_DL_24; + break; + case 8: + val_len |= RT5670_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5670_AIF1: + mask_clk = RT5670_I2S_BCLK_MS1_MASK | RT5670_I2S_PD1_MASK; + val_clk = bclk_ms << RT5670_I2S_BCLK_MS1_SFT | + pre_div << RT5670_I2S_PD1_SFT; + snd_soc_component_update_bits(component, RT5670_I2S1_SDP, + RT5670_I2S_DL_MASK, val_len); + snd_soc_component_update_bits(component, RT5670_ADDA_CLK1, mask_clk, val_clk); + break; + case RT5670_AIF2: + mask_clk = RT5670_I2S_BCLK_MS2_MASK | RT5670_I2S_PD2_MASK; + val_clk = bclk_ms << RT5670_I2S_BCLK_MS2_SFT | + pre_div << RT5670_I2S_PD2_SFT; + snd_soc_component_update_bits(component, RT5670_I2S2_SDP, + RT5670_I2S_DL_MASK, val_len); + snd_soc_component_update_bits(component, RT5670_ADDA_CLK1, mask_clk, val_clk); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5670->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5670_I2S_MS_S; + rt5670->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5670_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5670_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5670_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5670_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5670_AIF1: + snd_soc_component_update_bits(component, RT5670_I2S1_SDP, + RT5670_I2S_MS_MASK | RT5670_I2S_BP_MASK | + RT5670_I2S_DF_MASK, reg_val); + break; + case RT5670_AIF2: + snd_soc_component_update_bits(component, RT5670_I2S2_SDP, + RT5670_I2S_MS_MASK | RT5670_I2S_BP_MASK | + RT5670_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5670_set_codec_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + switch (clk_id) { + case RT5670_SCLK_S_MCLK: + reg_val |= RT5670_SCLK_SRC_MCLK; + break; + case RT5670_SCLK_S_PLL1: + reg_val |= RT5670_SCLK_SRC_PLL1; + break; + case RT5670_SCLK_S_RCCLK: + reg_val |= RT5670_SCLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5670_GLB_CLK, + RT5670_SCLK_SRC_MASK, reg_val); + rt5670->sysclk = freq; + if (clk_id != RT5670_SCLK_S_RCCLK) + rt5670->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk : %dHz clock id : %d\n", freq, clk_id); + + return 0; +} + +static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5670->pll_src && freq_in == rt5670->pll_in && + freq_out == rt5670->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5670->pll_in = 0; + rt5670->pll_out = 0; + snd_soc_component_update_bits(component, RT5670_GLB_CLK, + RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5670_PLL1_S_MCLK: + snd_soc_component_update_bits(component, RT5670_GLB_CLK, + RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_MCLK); + break; + case RT5670_PLL1_S_BCLK1: + case RT5670_PLL1_S_BCLK2: + case RT5670_PLL1_S_BCLK3: + case RT5670_PLL1_S_BCLK4: + switch (dai->id) { + case RT5670_AIF1: + snd_soc_component_update_bits(component, RT5670_GLB_CLK, + RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK1); + break; + case RT5670_AIF2: + snd_soc_component_update_bits(component, RT5670_GLB_CLK, + RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK2); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5670_PLL_CTRL1, + pll_code.n_code << RT5670_PLL_N_SFT | pll_code.k_code); + snd_soc_component_write(component, RT5670_PLL_CTRL2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5670_PLL_M_SFT) | + (pll_code.m_bp << RT5670_PLL_M_BP_SFT)); + + rt5670->pll_in = freq_in; + rt5670->pll_out = freq_out; + rt5670->pll_src = source; + + return 0; +} + +static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int val = 0; + + if (rx_mask || tx_mask) + val |= (1 << 14); + + switch (slots) { + case 4: + val |= (1 << 12); + break; + case 6: + val |= (2 << 12); + break; + case 8: + val |= (3 << 12); + break; + case 2: + break; + default: + return -EINVAL; + } + + switch (slot_width) { + case 20: + val |= (1 << 10); + break; + case 24: + val |= (2 << 10); + break; + case 32: + val |= (3 << 10); + break; + case 16: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5670_TDM_CTRL_1, 0x7c00, val); + + return 0; +} + +static int rt5670_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + + dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio); + if (dai->id != RT5670_AIF1) + return 0; + + if ((ratio % 50) == 0) + snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, + RT5670_TDM_DATA_MODE_SEL, RT5670_TDM_DATA_MODE_50FS); + else + snd_soc_component_update_bits(component, RT5670_GEN_CTRL3, + RT5670_TDM_DATA_MODE_SEL, RT5670_TDM_DATA_MODE_NOR); + + return 0; +} + +static int rt5670_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (SND_SOC_BIAS_STANDBY == snd_soc_component_get_bias_level(component)) { + snd_soc_component_update_bits(component, RT5670_PWR_ANLG1, + RT5670_PWR_VREF1 | RT5670_PWR_MB | + RT5670_PWR_BG | RT5670_PWR_VREF2, + RT5670_PWR_VREF1 | RT5670_PWR_MB | + RT5670_PWR_BG | RT5670_PWR_VREF2); + mdelay(10); + snd_soc_component_update_bits(component, RT5670_PWR_ANLG1, + RT5670_PWR_FV1 | RT5670_PWR_FV2, + RT5670_PWR_FV1 | RT5670_PWR_FV2); + snd_soc_component_update_bits(component, RT5670_CHARGE_PUMP, + RT5670_OSW_L_MASK | RT5670_OSW_R_MASK, + RT5670_OSW_L_DIS | RT5670_OSW_R_DIS); + snd_soc_component_update_bits(component, RT5670_DIG_MISC, 0x1, 0x1); + snd_soc_component_update_bits(component, RT5670_PWR_ANLG1, + RT5670_LDO_SEL_MASK, 0x5); + } + break; + case SND_SOC_BIAS_STANDBY: + snd_soc_component_update_bits(component, RT5670_PWR_ANLG1, + RT5670_PWR_VREF1 | RT5670_PWR_VREF2 | + RT5670_PWR_FV1 | RT5670_PWR_FV2, 0); + snd_soc_component_update_bits(component, RT5670_PWR_ANLG1, + RT5670_LDO_SEL_MASK, 0x3); + break; + case SND_SOC_BIAS_OFF: + if (rt5670->jd_mode) + snd_soc_component_update_bits(component, RT5670_PWR_ANLG1, + RT5670_PWR_VREF1 | RT5670_PWR_MB | + RT5670_PWR_BG | RT5670_PWR_VREF2 | + RT5670_PWR_FV1 | RT5670_PWR_FV2, + RT5670_PWR_MB | RT5670_PWR_BG); + else + snd_soc_component_update_bits(component, RT5670_PWR_ANLG1, + RT5670_PWR_VREF1 | RT5670_PWR_MB | + RT5670_PWR_BG | RT5670_PWR_VREF2 | + RT5670_PWR_FV1 | RT5670_PWR_FV2, 0); + + snd_soc_component_update_bits(component, RT5670_DIG_MISC, 0x1, 0x0); + break; + + default: + break; + } + + return 0; +} + +static int rt5670_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + switch (snd_soc_component_read(component, RT5670_RESET) & RT5670_ID_MASK) { + case RT5670_ID_5670: + case RT5670_ID_5671: + snd_soc_dapm_new_controls(dapm, + rt5670_specific_dapm_widgets, + ARRAY_SIZE(rt5670_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt5670_specific_dapm_routes, + ARRAY_SIZE(rt5670_specific_dapm_routes)); + break; + case RT5670_ID_5672: + snd_soc_dapm_new_controls(dapm, + rt5672_specific_dapm_widgets, + ARRAY_SIZE(rt5672_specific_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt5672_specific_dapm_routes, + ARRAY_SIZE(rt5672_specific_dapm_routes)); + break; + default: + dev_err(component->dev, + "The driver is for RT5670 RT5671 or RT5672 only\n"); + return -ENODEV; + } + rt5670->component = component; + + return 0; +} + +static void rt5670_remove(struct snd_soc_component *component) +{ + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + regmap_write(rt5670->regmap, RT5670_RESET, 0); + snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio); +} + +#ifdef CONFIG_PM +static int rt5670_suspend(struct snd_soc_component *component) +{ + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5670->regmap, true); + regcache_mark_dirty(rt5670->regmap); + return 0; +} + +static int rt5670_resume(struct snd_soc_component *component) +{ + struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5670->regmap, false); + regcache_sync(rt5670->regmap); + + return 0; +} +#else +#define rt5670_suspend NULL +#define rt5670_resume NULL +#endif + +#define RT5670_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5670_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5670_aif_dai_ops = { + .hw_params = rt5670_hw_params, + .set_fmt = rt5670_set_dai_fmt, + .set_tdm_slot = rt5670_set_tdm_slot, + .set_pll = rt5670_set_dai_pll, + .set_bclk_ratio = rt5670_set_bclk_ratio, +}; + +static struct snd_soc_dai_driver rt5670_dai[] = { + { + .name = "rt5670-aif1", + .id = RT5670_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5670_STEREO_RATES, + .formats = RT5670_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5670_STEREO_RATES, + .formats = RT5670_FORMATS, + }, + .ops = &rt5670_aif_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "rt5670-aif2", + .id = RT5670_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5670_STEREO_RATES, + .formats = RT5670_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5670_STEREO_RATES, + .formats = RT5670_FORMATS, + }, + .ops = &rt5670_aif_dai_ops, + .symmetric_rate = 1, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5670 = { + .probe = rt5670_probe, + .remove = rt5670_remove, + .suspend = rt5670_suspend, + .resume = rt5670_resume, + .set_bias_level = rt5670_set_bias_level, + .set_sysclk = rt5670_set_codec_sysclk, + .controls = rt5670_snd_controls, + .num_controls = ARRAY_SIZE(rt5670_snd_controls), + .dapm_widgets = rt5670_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5670_dapm_widgets), + .dapm_routes = rt5670_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5670_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5670_regmap = { + .reg_bits = 8, + .val_bits = 16, + .use_single_read = true, + .use_single_write = true, + .max_register = RT5670_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5670_ranges) * + RT5670_PR_SPACING), + .volatile_reg = rt5670_volatile_register, + .readable_reg = rt5670_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5670_reg, + .num_reg_defaults = ARRAY_SIZE(rt5670_reg), + .ranges = rt5670_ranges, + .num_ranges = ARRAY_SIZE(rt5670_ranges), +}; + +static const struct i2c_device_id rt5670_i2c_id[] = { + { "rt5670", 0 }, + { "rt5671", 0 }, + { "rt5672", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5670_acpi_match[] = { + { "10EC5670", 0}, + { "10EC5672", 0}, + { "10EC5640", 0}, /* quirk */ + { }, +}; +MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match); +#endif + +static int rt5670_quirk_cb(const struct dmi_system_id *id) +{ + rt5670_quirk = (unsigned long)id->driver_data; + return 1; +} + +static const struct dmi_system_id dmi_platform_intel_quirks[] = { + { + .callback = rt5670_quirk_cb, + .ident = "Intel Braswell", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Braswell CRB"), + }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_GPIO1_IS_IRQ | + RT5670_JD_MODE1), + }, + { + .callback = rt5670_quirk_cb, + .ident = "Dell Wyse 3040", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Wyse 3040"), + }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_GPIO1_IS_IRQ | + RT5670_JD_MODE1), + }, + { + .callback = rt5670_quirk_cb, + .ident = "Lenovo Thinkpad Tablet 8", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"), + }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC2_INR | + RT5670_GPIO1_IS_IRQ | + RT5670_JD_MODE1), + }, + { + .callback = rt5670_quirk_cb, + .ident = "Lenovo Thinkpad Tablet 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), + }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_GPIO1_IS_IRQ | + RT5670_JD_MODE1), + }, + { + .callback = rt5670_quirk_cb, + .ident = "Lenovo Thinkpad Tablet 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), + }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_GPIO1_IS_IRQ | + RT5670_JD_MODE1), + }, + { + .callback = rt5670_quirk_cb, + .ident = "Lenovo Miix 2 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), + }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_GPIO1_IS_EXT_SPK_EN | + RT5670_JD_MODE2), + }, + { + .callback = rt5670_quirk_cb, + .ident = "Dell Venue 8 Pro 5855", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5855"), + }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC2_INR | + RT5670_GPIO1_IS_IRQ | + RT5670_JD_MODE3), + }, + { + .callback = rt5670_quirk_cb, + .ident = "Dell Venue 10 Pro 5055", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"), + }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC2_INR | + RT5670_GPIO1_IS_IRQ | + RT5670_JD_MODE1), + }, + { + .callback = rt5670_quirk_cb, + .ident = "Aegex 10 tablet (RU2)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "AEGEX"), + DMI_MATCH(DMI_PRODUCT_VERSION, "RU2"), + }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC2_INR | + RT5670_GPIO1_IS_IRQ | + RT5670_JD_MODE3), + }, + {} +}; + +const char *rt5670_components(void) +{ + unsigned long quirk; + bool dmic1 = false; + bool dmic2 = false; + bool dmic3 = false; + + if (quirk_override) { + quirk = quirk_override; + } else { + dmi_check_system(dmi_platform_intel_quirks); + quirk = rt5670_quirk; + } + + if ((quirk & RT5670_DMIC1_IN2P) || + (quirk & RT5670_DMIC1_GPIO6) || + (quirk & RT5670_DMIC1_GPIO7)) + dmic1 = true; + + if ((quirk & RT5670_DMIC2_INR) || + (quirk & RT5670_DMIC2_GPIO8)) + dmic2 = true; + + if (quirk & RT5670_DMIC3_GPIO5) + dmic3 = true; + + if (dmic1 && dmic2) + return "cfg-spk:2 cfg-mic:dmics12"; + else if (dmic1) + return "cfg-spk:2 cfg-mic:dmic1"; + else if (dmic2) + return "cfg-spk:2 cfg-mic:dmic2"; + else if (dmic3) + return "cfg-spk:2 cfg-mic:dmic3"; + + return NULL; +} +EXPORT_SYMBOL_GPL(rt5670_components); + +static int rt5670_i2c_probe(struct i2c_client *i2c) +{ + struct rt5670_priv *rt5670; + int ret; + unsigned int val; + + rt5670 = devm_kzalloc(&i2c->dev, + sizeof(struct rt5670_priv), + GFP_KERNEL); + if (NULL == rt5670) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5670); + + dmi_check_system(dmi_platform_intel_quirks); + if (quirk_override) { + dev_info(&i2c->dev, "Overriding quirk 0x%x => 0x%x\n", + (unsigned int)rt5670_quirk, quirk_override); + rt5670_quirk = quirk_override; + } + + if (rt5670_quirk & RT5670_GPIO1_IS_IRQ) { + rt5670->gpio1_is_irq = true; + dev_info(&i2c->dev, "quirk GPIO1 is IRQ\n"); + } + if (rt5670_quirk & RT5670_GPIO1_IS_EXT_SPK_EN) { + rt5670->gpio1_is_ext_spk_en = true; + dev_info(&i2c->dev, "quirk GPIO1 is external speaker enable\n"); + } + if (rt5670_quirk & RT5670_IN2_DIFF) { + rt5670->in2_diff = true; + dev_info(&i2c->dev, "quirk IN2_DIFF\n"); + } + if (rt5670_quirk & RT5670_DMIC_EN) { + rt5670->dmic_en = true; + dev_info(&i2c->dev, "quirk DMIC enabled\n"); + } + if (rt5670_quirk & RT5670_DMIC1_IN2P) { + rt5670->dmic1_data_pin = RT5670_DMIC_DATA_IN2P; + dev_info(&i2c->dev, "quirk DMIC1 on IN2P pin\n"); + } + if (rt5670_quirk & RT5670_DMIC1_GPIO6) { + rt5670->dmic1_data_pin = RT5670_DMIC_DATA_GPIO6; + dev_info(&i2c->dev, "quirk DMIC1 on GPIO6 pin\n"); + } + if (rt5670_quirk & RT5670_DMIC1_GPIO7) { + rt5670->dmic1_data_pin = RT5670_DMIC_DATA_GPIO7; + dev_info(&i2c->dev, "quirk DMIC1 on GPIO7 pin\n"); + } + if (rt5670_quirk & RT5670_DMIC2_INR) { + rt5670->dmic2_data_pin = RT5670_DMIC_DATA_IN3N; + dev_info(&i2c->dev, "quirk DMIC2 on INR pin\n"); + } + if (rt5670_quirk & RT5670_DMIC2_GPIO8) { + rt5670->dmic2_data_pin = RT5670_DMIC_DATA_GPIO8; + dev_info(&i2c->dev, "quirk DMIC2 on GPIO8 pin\n"); + } + if (rt5670_quirk & RT5670_DMIC3_GPIO5) { + rt5670->dmic3_data_pin = RT5670_DMIC_DATA_GPIO5; + dev_info(&i2c->dev, "quirk DMIC3 on GPIO5 pin\n"); + } + + if (rt5670_quirk & RT5670_JD_MODE1) { + rt5670->jd_mode = 1; + dev_info(&i2c->dev, "quirk JD mode 1\n"); + } + if (rt5670_quirk & RT5670_JD_MODE2) { + rt5670->jd_mode = 2; + dev_info(&i2c->dev, "quirk JD mode 2\n"); + } + if (rt5670_quirk & RT5670_JD_MODE3) { + rt5670->jd_mode = 3; + dev_info(&i2c->dev, "quirk JD mode 3\n"); + } + + /* + * Enable the emulated "DAC1 Playback Switch" by default to avoid + * muting the output with older UCM profiles. + */ + rt5670->dac1_playback_switch_l = true; + rt5670->dac1_playback_switch_r = true; + /* The Power-On-Reset values for the DAC1 mixer have the DAC1 input enabled. */ + rt5670->dac1_mixl_dac1_switch = true; + rt5670->dac1_mixr_dac1_switch = true; + + rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap); + if (IS_ERR(rt5670->regmap)) { + ret = PTR_ERR(rt5670->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5670->regmap, RT5670_VENDOR_ID2, &val); + if (val != RT5670_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5670/72\n", val); + return -ENODEV; + } + + regmap_write(rt5670->regmap, RT5670_RESET, 0); + regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1, + RT5670_PWR_HP_L | RT5670_PWR_HP_R | + RT5670_PWR_VREF2, RT5670_PWR_VREF2); + msleep(100); + + regmap_write(rt5670->regmap, RT5670_RESET, 0); + + regmap_read(rt5670->regmap, RT5670_VENDOR_ID, &val); + if (val >= 4) + regmap_write(rt5670->regmap, RT5670_GPIO_CTRL3, 0x0980); + else + regmap_write(rt5670->regmap, RT5670_GPIO_CTRL3, 0x0d00); + + ret = regmap_register_patch(rt5670->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, + RT5670_MCLK_DET, RT5670_MCLK_DET); + + if (rt5670->in2_diff) + regmap_update_bits(rt5670->regmap, RT5670_IN2, + RT5670_IN_DF2, RT5670_IN_DF2); + + if (rt5670->gpio1_is_irq) { + /* for push button */ + regmap_write(rt5670->regmap, RT5670_IL_CMD, 0x0000); + regmap_write(rt5670->regmap, RT5670_IL_CMD2, 0x0010); + regmap_write(rt5670->regmap, RT5670_IL_CMD3, 0x0014); + /* for irq */ + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2, + RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT); + } + + if (rt5670->gpio1_is_ext_spk_en) { + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_GPIO1); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2, + RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT); + } + + if (rt5670->jd_mode) { + regmap_update_bits(rt5670->regmap, RT5670_GLB_CLK, + RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_RCCLK); + rt5670->sysclk = 0; + rt5670->sysclk_src = RT5670_SCLK_S_RCCLK; + regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG1, + RT5670_PWR_MB, RT5670_PWR_MB); + regmap_update_bits(rt5670->regmap, RT5670_PWR_ANLG2, + RT5670_PWR_JD1, RT5670_PWR_JD1); + regmap_update_bits(rt5670->regmap, RT5670_IRQ_CTRL1, + RT5670_JD1_1_EN_MASK, RT5670_JD1_1_EN); + regmap_update_bits(rt5670->regmap, RT5670_JD_CTRL3, + RT5670_JD_TRI_CBJ_SEL_MASK | + RT5670_JD_TRI_HPO_SEL_MASK, + RT5670_JD_CBJ_JD1_1 | RT5670_JD_HPO_JD1_1); + switch (rt5670->jd_mode) { + case 1: + regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1, + RT5670_JD1_MODE_MASK, + RT5670_JD1_MODE_0); + break; + case 2: + regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1, + RT5670_JD1_MODE_MASK, + RT5670_JD1_MODE_1); + break; + case 3: + regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1, + RT5670_JD1_MODE_MASK, + RT5670_JD1_MODE_2); + break; + default: + break; + } + } + + if (rt5670->dmic_en) { + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP2_PIN_MASK, + RT5670_GP2_PIN_DMIC1_SCL); + + switch (rt5670->dmic1_data_pin) { + case RT5670_DMIC_DATA_IN2P: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1, + RT5670_DMIC_1_DP_MASK, + RT5670_DMIC_1_DP_IN2P); + break; + + case RT5670_DMIC_DATA_GPIO6: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1, + RT5670_DMIC_1_DP_MASK, + RT5670_DMIC_1_DP_GPIO6); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP6_PIN_MASK, + RT5670_GP6_PIN_DMIC1_SDA); + break; + + case RT5670_DMIC_DATA_GPIO7: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1, + RT5670_DMIC_1_DP_MASK, + RT5670_DMIC_1_DP_GPIO7); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP7_PIN_MASK, + RT5670_GP7_PIN_DMIC1_SDA); + break; + + default: + break; + } + + switch (rt5670->dmic2_data_pin) { + case RT5670_DMIC_DATA_IN3N: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1, + RT5670_DMIC_2_DP_MASK, + RT5670_DMIC_2_DP_IN3N); + break; + + case RT5670_DMIC_DATA_GPIO8: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1, + RT5670_DMIC_2_DP_MASK, + RT5670_DMIC_2_DP_GPIO8); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP8_PIN_MASK, + RT5670_GP8_PIN_DMIC2_SDA); + break; + + default: + break; + } + + switch (rt5670->dmic3_data_pin) { + case RT5670_DMIC_DATA_GPIO5: + regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL2, + RT5670_DMIC_3_DP_MASK, + RT5670_DMIC_3_DP_GPIO5); + regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, + RT5670_GP5_PIN_MASK, + RT5670_GP5_PIN_DMIC3_SDA); + break; + + case RT5670_DMIC_DATA_GPIO9: + case RT5670_DMIC_DATA_GPIO10: + dev_err(&i2c->dev, + "Always use GPIO5 as DMIC3 data pin\n"); + break; + + default: + break; + } + + } + + pm_runtime_enable(&i2c->dev); + pm_request_idle(&i2c->dev); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5670, + rt5670_dai, ARRAY_SIZE(rt5670_dai)); + if (ret < 0) + goto err; + + return 0; +err: + pm_runtime_disable(&i2c->dev); + + return ret; +} + +static void rt5670_i2c_remove(struct i2c_client *i2c) +{ + pm_runtime_disable(&i2c->dev); +} + +static struct i2c_driver rt5670_i2c_driver = { + .driver = { + .name = "rt5670", + .acpi_match_table = ACPI_PTR(rt5670_acpi_match), + }, + .probe = rt5670_i2c_probe, + .remove = rt5670_i2c_remove, + .id_table = rt5670_i2c_id, +}; + +module_i2c_driver(rt5670_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5670 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h new file mode 100644 index 0000000000..5b230897f6 --- /dev/null +++ b/sound/soc/codecs/rt5670.h @@ -0,0 +1,2029 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5670.h -- RT5670 ALSA SoC audio driver + * + * Copyright 2014 Realtek Microelectronics + * Author: Bard Liao + */ + +#ifndef __RT5670_H__ +#define __RT5670_H__ + +/* Info */ +#define RT5670_RESET 0x00 +#define RT5670_VENDOR_ID 0xfd +#define RT5670_VENDOR_ID1 0xfe +#define RT5670_VENDOR_ID2 0xff +/* I/O - Output */ +#define RT5670_HP_VOL 0x02 +#define RT5670_LOUT1 0x03 +/* I/O - Input */ +#define RT5670_CJ_CTRL1 0x0a +#define RT5670_CJ_CTRL2 0x0b +#define RT5670_CJ_CTRL3 0x0c +#define RT5670_IN2 0x0e +#define RT5670_INL1_INR1_VOL 0x0f +/* I/O - ADC/DAC/DMIC */ +#define RT5670_DAC1_DIG_VOL 0x19 +#define RT5670_DAC2_DIG_VOL 0x1a +#define RT5670_DAC_CTRL 0x1b +#define RT5670_STO1_ADC_DIG_VOL 0x1c +#define RT5670_MONO_ADC_DIG_VOL 0x1d +#define RT5670_ADC_BST_VOL1 0x1e +#define RT5670_STO2_ADC_DIG_VOL 0x1f +/* Mixer - D-D */ +#define RT5670_ADC_BST_VOL2 0x20 +#define RT5670_STO2_ADC_MIXER 0x26 +#define RT5670_STO1_ADC_MIXER 0x27 +#define RT5670_MONO_ADC_MIXER 0x28 +#define RT5670_AD_DA_MIXER 0x29 +#define RT5670_STO_DAC_MIXER 0x2a +#define RT5670_DD_MIXER 0x2b +#define RT5670_DIG_MIXER 0x2c +#define RT5670_DSP_PATH1 0x2d +#define RT5670_DSP_PATH2 0x2e +#define RT5670_DIG_INF1_DATA 0x2f +#define RT5670_DIG_INF2_DATA 0x30 +/* Mixer - PDM */ +#define RT5670_PDM_OUT_CTRL 0x31 +#define RT5670_PDM_DATA_CTRL1 0x32 +#define RT5670_PDM1_DATA_CTRL2 0x33 +#define RT5670_PDM1_DATA_CTRL3 0x34 +#define RT5670_PDM1_DATA_CTRL4 0x35 +#define RT5670_PDM2_DATA_CTRL2 0x36 +#define RT5670_PDM2_DATA_CTRL3 0x37 +#define RT5670_PDM2_DATA_CTRL4 0x38 +/* Mixer - ADC */ +#define RT5670_REC_L1_MIXER 0x3b +#define RT5670_REC_L2_MIXER 0x3c +#define RT5670_REC_R1_MIXER 0x3d +#define RT5670_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5670_HPO_MIXER 0x45 +#define RT5670_MONO_MIXER 0x4c +#define RT5670_OUT_L1_MIXER 0x4f +#define RT5670_OUT_R1_MIXER 0x52 +#define RT5670_LOUT_MIXER 0x53 +/* Power */ +#define RT5670_PWR_DIG1 0x61 +#define RT5670_PWR_DIG2 0x62 +#define RT5670_PWR_ANLG1 0x63 +#define RT5670_PWR_ANLG2 0x64 +#define RT5670_PWR_MIXER 0x65 +#define RT5670_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5670_PRIV_INDEX 0x6a +#define RT5670_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5670_I2S4_SDP 0x6f +#define RT5670_I2S1_SDP 0x70 +#define RT5670_I2S2_SDP 0x71 +#define RT5670_I2S3_SDP 0x72 +#define RT5670_ADDA_CLK1 0x73 +#define RT5670_ADDA_CLK2 0x74 +#define RT5670_DMIC_CTRL1 0x75 +#define RT5670_DMIC_CTRL2 0x76 +/* Format - TDM Control */ +#define RT5670_TDM_CTRL_1 0x77 +#define RT5670_TDM_CTRL_2 0x78 +#define RT5670_TDM_CTRL_3 0x79 + +/* Function - Analog */ +#define RT5670_DSP_CLK 0x7f +#define RT5670_GLB_CLK 0x80 +#define RT5670_PLL_CTRL1 0x81 +#define RT5670_PLL_CTRL2 0x82 +#define RT5670_ASRC_1 0x83 +#define RT5670_ASRC_2 0x84 +#define RT5670_ASRC_3 0x85 +#define RT5670_ASRC_4 0x86 +#define RT5670_ASRC_5 0x87 +#define RT5670_ASRC_7 0x89 +#define RT5670_ASRC_8 0x8a +#define RT5670_ASRC_9 0x8b +#define RT5670_ASRC_10 0x8c +#define RT5670_ASRC_11 0x8d +#define RT5670_DEPOP_M1 0x8e +#define RT5670_DEPOP_M2 0x8f +#define RT5670_DEPOP_M3 0x90 +#define RT5670_CHARGE_PUMP 0x91 +#define RT5670_MICBIAS 0x93 +#define RT5670_A_JD_CTRL1 0x94 +#define RT5670_A_JD_CTRL2 0x95 +#define RT5670_ASRC_12 0x97 +#define RT5670_ASRC_13 0x98 +#define RT5670_ASRC_14 0x99 +#define RT5670_VAD_CTRL1 0x9a +#define RT5670_VAD_CTRL2 0x9b +#define RT5670_VAD_CTRL3 0x9c +#define RT5670_VAD_CTRL4 0x9d +#define RT5670_VAD_CTRL5 0x9e +/* Function - Digital */ +#define RT5670_ADC_EQ_CTRL1 0xae +#define RT5670_ADC_EQ_CTRL2 0xaf +#define RT5670_EQ_CTRL1 0xb0 +#define RT5670_EQ_CTRL2 0xb1 +#define RT5670_ALC_DRC_CTRL1 0xb2 +#define RT5670_ALC_DRC_CTRL2 0xb3 +#define RT5670_ALC_CTRL_1 0xb4 +#define RT5670_ALC_CTRL_2 0xb5 +#define RT5670_ALC_CTRL_3 0xb6 +#define RT5670_ALC_CTRL_4 0xb7 +#define RT5670_JD_CTRL 0xbb +#define RT5670_IRQ_CTRL1 0xbd +#define RT5670_IRQ_CTRL2 0xbe +#define RT5670_INT_IRQ_ST 0xbf +#define RT5670_GPIO_CTRL1 0xc0 +#define RT5670_GPIO_CTRL2 0xc1 +#define RT5670_GPIO_CTRL3 0xc2 +#define RT5670_SCRABBLE_FUN 0xcd +#define RT5670_SCRABBLE_CTRL 0xce +#define RT5670_BASE_BACK 0xcf +#define RT5670_MP3_PLUS1 0xd0 +#define RT5670_MP3_PLUS2 0xd1 +#define RT5670_ADJ_HPF1 0xd3 +#define RT5670_ADJ_HPF2 0xd4 +#define RT5670_HP_CALIB_AMP_DET 0xd6 +#define RT5670_SV_ZCD1 0xd9 +#define RT5670_SV_ZCD2 0xda +#define RT5670_IL_CMD 0xdb +#define RT5670_IL_CMD2 0xdc +#define RT5670_IL_CMD3 0xdd +#define RT5670_DRC_HL_CTRL1 0xe6 +#define RT5670_DRC_HL_CTRL2 0xe7 +#define RT5670_ADC_MONO_HP_CTRL1 0xec +#define RT5670_ADC_MONO_HP_CTRL2 0xed +#define RT5670_ADC_STO2_HP_CTRL1 0xee +#define RT5670_ADC_STO2_HP_CTRL2 0xef +#define RT5670_JD_CTRL3 0xf8 +#define RT5670_JD_CTRL4 0xf9 +/* General Control */ +#define RT5670_DIG_MISC 0xfa +#define RT5670_GEN_CTRL2 0xfb +#define RT5670_GEN_CTRL3 0xfc + + +/* Index of Codec Private Register definition */ +#define RT5670_DIG_VOL 0x00 +#define RT5670_PR_ALC_CTRL_1 0x01 +#define RT5670_PR_ALC_CTRL_2 0x02 +#define RT5670_PR_ALC_CTRL_3 0x03 +#define RT5670_PR_ALC_CTRL_4 0x04 +#define RT5670_PR_ALC_CTRL_5 0x05 +#define RT5670_PR_ALC_CTRL_6 0x06 +#define RT5670_BIAS_CUR1 0x12 +#define RT5670_BIAS_CUR3 0x14 +#define RT5670_CLSD_INT_REG1 0x1c +#define RT5670_MAMP_INT_REG2 0x37 +#define RT5670_CHOP_DAC_ADC 0x3d +#define RT5670_MIXER_INT_REG 0x3f +#define RT5670_3D_SPK 0x63 +#define RT5670_WND_1 0x6c +#define RT5670_WND_2 0x6d +#define RT5670_WND_3 0x6e +#define RT5670_WND_4 0x6f +#define RT5670_WND_5 0x70 +#define RT5670_WND_8 0x73 +#define RT5670_DIP_SPK_INF 0x75 +#define RT5670_HP_DCC_INT1 0x77 +#define RT5670_EQ_BW_LOP 0xa0 +#define RT5670_EQ_GN_LOP 0xa1 +#define RT5670_EQ_FC_BP1 0xa2 +#define RT5670_EQ_BW_BP1 0xa3 +#define RT5670_EQ_GN_BP1 0xa4 +#define RT5670_EQ_FC_BP2 0xa5 +#define RT5670_EQ_BW_BP2 0xa6 +#define RT5670_EQ_GN_BP2 0xa7 +#define RT5670_EQ_FC_BP3 0xa8 +#define RT5670_EQ_BW_BP3 0xa9 +#define RT5670_EQ_GN_BP3 0xaa +#define RT5670_EQ_FC_BP4 0xab +#define RT5670_EQ_BW_BP4 0xac +#define RT5670_EQ_GN_BP4 0xad +#define RT5670_EQ_FC_HIP1 0xae +#define RT5670_EQ_GN_HIP1 0xaf +#define RT5670_EQ_FC_HIP2 0xb0 +#define RT5670_EQ_BW_HIP2 0xb1 +#define RT5670_EQ_GN_HIP2 0xb2 +#define RT5670_EQ_PRE_VOL 0xb3 +#define RT5670_EQ_PST_VOL 0xb4 + + +/* global definition */ +#define RT5670_L_MUTE (0x1 << 15) +#define RT5670_L_MUTE_SFT 15 +#define RT5670_R_MUTE (0x1 << 7) +#define RT5670_R_MUTE_SFT 7 +#define RT5670_L_VOL_MASK (0x3f << 8) +#define RT5670_L_VOL_SFT 8 +#define RT5670_R_VOL_MASK (0x3f) +#define RT5670_R_VOL_SFT 0 + +/* SW Reset & Device ID (0x00) */ +#define RT5670_ID_MASK (0x3 << 1) +#define RT5670_ID_5670 (0x0 << 1) +#define RT5670_ID_5672 (0x1 << 1) +#define RT5670_ID_5671 (0x2 << 1) + +/* Combo Jack Control 1 (0x0a) */ +#define RT5670_CBJ_BST1_MASK (0xf << 12) +#define RT5670_CBJ_BST1_SFT (12) +#define RT5670_CBJ_JD_HP_EN (0x1 << 9) +#define RT5670_CBJ_JD_MIC_EN (0x1 << 8) +#define RT5670_CBJ_BST1_EN (0x1 << 2) + +/* Combo Jack Control 1 (0x0b) */ +#define RT5670_CBJ_MN_JD (0x1 << 12) +#define RT5670_CAPLESS_EN (0x1 << 11) +#define RT5670_CBJ_DET_MODE (0x1 << 7) + +/* IN2 Control (0x0e) */ +#define RT5670_BST_MASK1 (0xf<<12) +#define RT5670_BST_SFT1 12 +#define RT5670_BST_MASK2 (0xf<<8) +#define RT5670_BST_SFT2 8 +#define RT5670_IN_DF1 (0x1 << 7) +#define RT5670_IN_SFT1 7 +#define RT5670_IN_DF2 (0x1 << 6) +#define RT5670_IN_SFT2 6 + +/* INL and INR Volume Control (0x0f) */ +#define RT5670_INL_SEL_MASK (0x1 << 15) +#define RT5670_INL_SEL_SFT 15 +#define RT5670_INL_SEL_IN4P (0x0 << 15) +#define RT5670_INL_SEL_MONOP (0x1 << 15) +#define RT5670_INL_VOL_MASK (0x1f << 8) +#define RT5670_INL_VOL_SFT 8 +#define RT5670_INR_SEL_MASK (0x1 << 7) +#define RT5670_INR_SEL_SFT 7 +#define RT5670_INR_SEL_IN4N (0x0 << 7) +#define RT5670_INR_SEL_MONON (0x1 << 7) +#define RT5670_INR_VOL_MASK (0x1f) +#define RT5670_INR_VOL_SFT 0 + +/* Sidetone Control (0x18) */ +#define RT5670_ST_SEL_MASK (0x7 << 9) +#define RT5670_ST_SEL_SFT 9 +#define RT5670_M_ST_DACR2 (0x1 << 8) +#define RT5670_M_ST_DACR2_SFT 8 +#define RT5670_M_ST_DACL2 (0x1 << 7) +#define RT5670_M_ST_DACL2_SFT 7 +#define RT5670_ST_EN (0x1 << 6) +#define RT5670_ST_EN_SFT 6 + +/* DAC1 Digital Volume (0x19) */ +#define RT5670_DAC_L1_VOL_MASK (0xff << 8) +#define RT5670_DAC_L1_VOL_SFT 8 +#define RT5670_DAC_R1_VOL_MASK (0xff) +#define RT5670_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x1a) */ +#define RT5670_DAC_L2_VOL_MASK (0xff << 8) +#define RT5670_DAC_L2_VOL_SFT 8 +#define RT5670_DAC_R2_VOL_MASK (0xff) +#define RT5670_DAC_R2_VOL_SFT 0 + +/* DAC2 Control (0x1b) */ +#define RT5670_M_DAC_L2_VOL (0x1 << 13) +#define RT5670_M_DAC_L2_VOL_SFT 13 +#define RT5670_M_DAC_R2_VOL (0x1 << 12) +#define RT5670_M_DAC_R2_VOL_SFT 12 +#define RT5670_DAC2_L_SEL_MASK (0x7 << 4) +#define RT5670_DAC2_L_SEL_SFT 4 +#define RT5670_DAC2_R_SEL_MASK (0x7 << 0) +#define RT5670_DAC2_R_SEL_SFT 0 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5670_ADC_L_VOL_MASK (0x7f << 8) +#define RT5670_ADC_L_VOL_SFT 8 +#define RT5670_ADC_R_VOL_MASK (0x7f) +#define RT5670_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x1d) */ +#define RT5670_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5670_MONO_ADC_L_VOL_SFT 8 +#define RT5670_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5670_MONO_ADC_R_VOL_SFT 0 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5670_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5670_STO1_ADC_L_BST_SFT 14 +#define RT5670_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5670_STO1_ADC_R_BST_SFT 12 +#define RT5670_STO1_ADC_COMP_MASK (0x3 << 10) +#define RT5670_STO1_ADC_COMP_SFT 10 +#define RT5670_STO2_ADC_L_BST_MASK (0x3 << 8) +#define RT5670_STO2_ADC_L_BST_SFT 8 +#define RT5670_STO2_ADC_R_BST_MASK (0x3 << 6) +#define RT5670_STO2_ADC_R_BST_SFT 6 +#define RT5670_STO2_ADC_COMP_MASK (0x3 << 4) +#define RT5670_STO2_ADC_COMP_SFT 4 + +/* Stereo2 ADC Mixer Control (0x26) */ +#define RT5670_STO2_ADC_SRC_MASK (0x1 << 15) +#define RT5670_STO2_ADC_SRC_SFT 15 + +/* Stereo ADC Mixer Control (0x26 0x27) */ +#define RT5670_M_ADC_L1 (0x1 << 14) +#define RT5670_M_ADC_L1_SFT 14 +#define RT5670_M_ADC_L2 (0x1 << 13) +#define RT5670_M_ADC_L2_SFT 13 +#define RT5670_ADC_1_SRC_MASK (0x1 << 12) +#define RT5670_ADC_1_SRC_SFT 12 +#define RT5670_ADC_1_SRC_ADC (0x1 << 12) +#define RT5670_ADC_1_SRC_DACMIX (0x0 << 12) +#define RT5670_ADC_2_SRC_MASK (0x1 << 11) +#define RT5670_ADC_2_SRC_SFT 11 +#define RT5670_ADC_SRC_MASK (0x1 << 10) +#define RT5670_ADC_SRC_SFT 10 +#define RT5670_DMIC_SRC_MASK (0x3 << 8) +#define RT5670_DMIC_SRC_SFT 8 +#define RT5670_M_ADC_R1 (0x1 << 6) +#define RT5670_M_ADC_R1_SFT 6 +#define RT5670_M_ADC_R2 (0x1 << 5) +#define RT5670_M_ADC_R2_SFT 5 +#define RT5670_DMIC3_SRC_MASK (0x1 << 1) +#define RT5670_DMIC3_SRC_SFT 0 + +/* Mono ADC Mixer Control (0x28) */ +#define RT5670_M_MONO_ADC_L1 (0x1 << 14) +#define RT5670_M_MONO_ADC_L1_SFT 14 +#define RT5670_M_MONO_ADC_L2 (0x1 << 13) +#define RT5670_M_MONO_ADC_L2_SFT 13 +#define RT5670_MONO_ADC_L1_SRC_MASK (0x1 << 12) +#define RT5670_MONO_ADC_L1_SRC_SFT 12 +#define RT5670_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12) +#define RT5670_MONO_ADC_L1_SRC_ADCL (0x1 << 12) +#define RT5670_MONO_ADC_L2_SRC_MASK (0x1 << 11) +#define RT5670_MONO_ADC_L2_SRC_SFT 11 +#define RT5670_MONO_ADC_L_SRC_MASK (0x1 << 10) +#define RT5670_MONO_ADC_L_SRC_SFT 10 +#define RT5670_MONO_DMIC_L_SRC_MASK (0x3 << 8) +#define RT5670_MONO_DMIC_L_SRC_SFT 8 +#define RT5670_M_MONO_ADC_R1 (0x1 << 6) +#define RT5670_M_MONO_ADC_R1_SFT 6 +#define RT5670_M_MONO_ADC_R2 (0x1 << 5) +#define RT5670_M_MONO_ADC_R2_SFT 5 +#define RT5670_MONO_ADC_R1_SRC_MASK (0x1 << 4) +#define RT5670_MONO_ADC_R1_SRC_SFT 4 +#define RT5670_MONO_ADC_R1_SRC_ADCR (0x1 << 4) +#define RT5670_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4) +#define RT5670_MONO_ADC_R2_SRC_MASK (0x1 << 3) +#define RT5670_MONO_ADC_R2_SRC_SFT 3 +#define RT5670_MONO_DMIC_R_SRC_MASK (0x3) +#define RT5670_MONO_DMIC_R_SRC_SFT 0 + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5670_M_ADCMIX_L (0x1 << 15) +#define RT5670_M_ADCMIX_L_SFT 15 +#define RT5670_M_DAC1_L (0x1 << 14) +#define RT5670_M_DAC1_L_SFT 14 +#define RT5670_DAC1_R_SEL_MASK (0x3 << 10) +#define RT5670_DAC1_R_SEL_SFT 10 +#define RT5670_DAC1_R_SEL_IF1 (0x0 << 10) +#define RT5670_DAC1_R_SEL_IF2 (0x1 << 10) +#define RT5670_DAC1_R_SEL_IF3 (0x2 << 10) +#define RT5670_DAC1_R_SEL_IF4 (0x3 << 10) +#define RT5670_DAC1_L_SEL_MASK (0x3 << 8) +#define RT5670_DAC1_L_SEL_SFT 8 +#define RT5670_DAC1_L_SEL_IF1 (0x0 << 8) +#define RT5670_DAC1_L_SEL_IF2 (0x1 << 8) +#define RT5670_DAC1_L_SEL_IF3 (0x2 << 8) +#define RT5670_DAC1_L_SEL_IF4 (0x3 << 8) +#define RT5670_M_ADCMIX_R (0x1 << 7) +#define RT5670_M_ADCMIX_R_SFT 7 +#define RT5670_M_DAC1_R (0x1 << 6) +#define RT5670_M_DAC1_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5670_M_DAC_L1 (0x1 << 14) +#define RT5670_M_DAC_L1_SFT 14 +#define RT5670_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5670_DAC_L1_STO_L_VOL_SFT 13 +#define RT5670_M_DAC_L2 (0x1 << 12) +#define RT5670_M_DAC_L2_SFT 12 +#define RT5670_DAC_L2_STO_L_VOL_MASK (0x1 << 11) +#define RT5670_DAC_L2_STO_L_VOL_SFT 11 +#define RT5670_M_DAC_R1_STO_L (0x1 << 9) +#define RT5670_M_DAC_R1_STO_L_SFT 9 +#define RT5670_DAC_R1_STO_L_VOL_MASK (0x1 << 8) +#define RT5670_DAC_R1_STO_L_VOL_SFT 8 +#define RT5670_M_DAC_R1 (0x1 << 6) +#define RT5670_M_DAC_R1_SFT 6 +#define RT5670_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5670_DAC_R1_STO_R_VOL_SFT 5 +#define RT5670_M_DAC_R2 (0x1 << 4) +#define RT5670_M_DAC_R2_SFT 4 +#define RT5670_DAC_R2_STO_R_VOL_MASK (0x1 << 3) +#define RT5670_DAC_R2_STO_R_VOL_SFT 3 +#define RT5670_M_DAC_L1_STO_R (0x1 << 1) +#define RT5670_M_DAC_L1_STO_R_SFT 1 +#define RT5670_DAC_L1_STO_R_VOL_MASK (0x1) +#define RT5670_DAC_L1_STO_R_VOL_SFT 0 + +/* Mono DAC Mixer Control (0x2b) */ +#define RT5670_M_DAC_L1_MONO_L (0x1 << 14) +#define RT5670_M_DAC_L1_MONO_L_SFT 14 +#define RT5670_DAC_L1_MONO_L_VOL_MASK (0x1 << 13) +#define RT5670_DAC_L1_MONO_L_VOL_SFT 13 +#define RT5670_M_DAC_L2_MONO_L (0x1 << 12) +#define RT5670_M_DAC_L2_MONO_L_SFT 12 +#define RT5670_DAC_L2_MONO_L_VOL_MASK (0x1 << 11) +#define RT5670_DAC_L2_MONO_L_VOL_SFT 11 +#define RT5670_M_DAC_R2_MONO_L (0x1 << 10) +#define RT5670_M_DAC_R2_MONO_L_SFT 10 +#define RT5670_DAC_R2_MONO_L_VOL_MASK (0x1 << 9) +#define RT5670_DAC_R2_MONO_L_VOL_SFT 9 +#define RT5670_M_DAC_R1_MONO_R (0x1 << 6) +#define RT5670_M_DAC_R1_MONO_R_SFT 6 +#define RT5670_DAC_R1_MONO_R_VOL_MASK (0x1 << 5) +#define RT5670_DAC_R1_MONO_R_VOL_SFT 5 +#define RT5670_M_DAC_R2_MONO_R (0x1 << 4) +#define RT5670_M_DAC_R2_MONO_R_SFT 4 +#define RT5670_DAC_R2_MONO_R_VOL_MASK (0x1 << 3) +#define RT5670_DAC_R2_MONO_R_VOL_SFT 3 +#define RT5670_M_DAC_L2_MONO_R (0x1 << 2) +#define RT5670_M_DAC_L2_MONO_R_SFT 2 +#define RT5670_DAC_L2_MONO_R_VOL_MASK (0x1 << 1) +#define RT5670_DAC_L2_MONO_R_VOL_SFT 1 + +/* Digital Mixer Control (0x2c) */ +#define RT5670_M_STO_L_DAC_L (0x1 << 15) +#define RT5670_M_STO_L_DAC_L_SFT 15 +#define RT5670_STO_L_DAC_L_VOL_MASK (0x1 << 14) +#define RT5670_STO_L_DAC_L_VOL_SFT 14 +#define RT5670_M_DAC_L2_DAC_L (0x1 << 13) +#define RT5670_M_DAC_L2_DAC_L_SFT 13 +#define RT5670_DAC_L2_DAC_L_VOL_MASK (0x1 << 12) +#define RT5670_DAC_L2_DAC_L_VOL_SFT 12 +#define RT5670_M_STO_R_DAC_R (0x1 << 11) +#define RT5670_M_STO_R_DAC_R_SFT 11 +#define RT5670_STO_R_DAC_R_VOL_MASK (0x1 << 10) +#define RT5670_STO_R_DAC_R_VOL_SFT 10 +#define RT5670_M_DAC_R2_DAC_R (0x1 << 9) +#define RT5670_M_DAC_R2_DAC_R_SFT 9 +#define RT5670_DAC_R2_DAC_R_VOL_MASK (0x1 << 8) +#define RT5670_DAC_R2_DAC_R_VOL_SFT 8 +#define RT5670_M_DAC_R2_DAC_L (0x1 << 7) +#define RT5670_M_DAC_R2_DAC_L_SFT 7 +#define RT5670_DAC_R2_DAC_L_VOL_MASK (0x1 << 6) +#define RT5670_DAC_R2_DAC_L_VOL_SFT 6 +#define RT5670_M_DAC_L2_DAC_R (0x1 << 5) +#define RT5670_M_DAC_L2_DAC_R_SFT 5 +#define RT5670_DAC_L2_DAC_R_VOL_MASK (0x1 << 4) +#define RT5670_DAC_L2_DAC_R_VOL_SFT 4 + +/* DSP Path Control 1 (0x2d) */ +#define RT5670_RXDP_SEL_MASK (0x7 << 13) +#define RT5670_RXDP_SEL_SFT 13 +#define RT5670_RXDP_SRC_MASK (0x3 << 11) +#define RT5670_RXDP_SRC_SFT 11 +#define RT5670_RXDP_SRC_NOR (0x0 << 11) +#define RT5670_RXDP_SRC_DIV2 (0x1 << 11) +#define RT5670_RXDP_SRC_DIV3 (0x2 << 11) +#define RT5670_TXDP_SRC_MASK (0x3 << 4) +#define RT5670_TXDP_SRC_SFT 4 +#define RT5670_TXDP_SRC_NOR (0x0 << 4) +#define RT5670_TXDP_SRC_DIV2 (0x1 << 4) +#define RT5670_TXDP_SRC_DIV3 (0x2 << 4) +#define RT5670_TXDP_SLOT_SEL_MASK (0x3 << 2) +#define RT5670_TXDP_SLOT_SEL_SFT 2 +#define RT5670_DSP_UL_SEL (0x1 << 1) +#define RT5670_DSP_UL_SFT 1 +#define RT5670_DSP_DL_SEL 0x1 +#define RT5670_DSP_DL_SFT 0 + +/* DSP Path Control 2 (0x2e) */ +#define RT5670_TXDP_L_VOL_MASK (0x7f << 8) +#define RT5670_TXDP_L_VOL_SFT 8 +#define RT5670_TXDP_R_VOL_MASK (0x7f) +#define RT5670_TXDP_R_VOL_SFT 0 + +/* Digital Interface Data Control (0x2f) */ +#define RT5670_IF1_ADC2_IN_SEL (0x1 << 15) +#define RT5670_IF1_ADC2_IN_SFT 15 +#define RT5670_IF2_ADC_IN_MASK (0x7 << 12) +#define RT5670_IF2_ADC_IN_SFT 12 +#define RT5670_IF2_DAC_SEL_MASK (0x3 << 10) +#define RT5670_IF2_DAC_SEL_SFT 10 +#define RT5670_IF2_ADC_SEL_MASK (0x3 << 8) +#define RT5670_IF2_ADC_SEL_SFT 8 + +/* Digital Interface Data Control (0x30) */ +#define RT5670_IF4_ADC_IN_MASK (0x3 << 4) +#define RT5670_IF4_ADC_IN_SFT 4 + +/* PDM Output Control (0x31) */ +#define RT5670_PDM1_L_MASK (0x1 << 15) +#define RT5670_PDM1_L_SFT 15 +#define RT5670_M_PDM1_L (0x1 << 14) +#define RT5670_M_PDM1_L_SFT 14 +#define RT5670_PDM1_R_MASK (0x1 << 13) +#define RT5670_PDM1_R_SFT 13 +#define RT5670_M_PDM1_R (0x1 << 12) +#define RT5670_M_PDM1_R_SFT 12 +#define RT5670_PDM2_L_MASK (0x1 << 11) +#define RT5670_PDM2_L_SFT 11 +#define RT5670_M_PDM2_L (0x1 << 10) +#define RT5670_M_PDM2_L_SFT 10 +#define RT5670_PDM2_R_MASK (0x1 << 9) +#define RT5670_PDM2_R_SFT 9 +#define RT5670_M_PDM2_R (0x1 << 8) +#define RT5670_M_PDM2_R_SFT 8 +#define RT5670_PDM2_BUSY (0x1 << 7) +#define RT5670_PDM1_BUSY (0x1 << 6) +#define RT5670_PDM_PATTERN (0x1 << 5) +#define RT5670_PDM_GAIN (0x1 << 4) +#define RT5670_PDM_DIV_MASK (0x3) + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5670_G_HP_L_RM_L_MASK (0x7 << 13) +#define RT5670_G_HP_L_RM_L_SFT 13 +#define RT5670_G_IN_L_RM_L_MASK (0x7 << 10) +#define RT5670_G_IN_L_RM_L_SFT 10 +#define RT5670_G_BST4_RM_L_MASK (0x7 << 7) +#define RT5670_G_BST4_RM_L_SFT 7 +#define RT5670_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5670_G_BST3_RM_L_SFT 4 +#define RT5670_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5670_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5670_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5670_G_BST1_RM_L_SFT 13 +#define RT5670_M_IN_L_RM_L (0x1 << 5) +#define RT5670_M_IN_L_RM_L_SFT 5 +#define RT5670_M_BST2_RM_L (0x1 << 3) +#define RT5670_M_BST2_RM_L_SFT 3 +#define RT5670_M_BST1_RM_L (0x1 << 1) +#define RT5670_M_BST1_RM_L_SFT 1 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5670_G_HP_R_RM_R_MASK (0x7 << 13) +#define RT5670_G_HP_R_RM_R_SFT 13 +#define RT5670_G_IN_R_RM_R_MASK (0x7 << 10) +#define RT5670_G_IN_R_RM_R_SFT 10 +#define RT5670_G_BST4_RM_R_MASK (0x7 << 7) +#define RT5670_G_BST4_RM_R_SFT 7 +#define RT5670_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5670_G_BST3_RM_R_SFT 4 +#define RT5670_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5670_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5670_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5670_G_BST1_RM_R_SFT 13 +#define RT5670_M_IN_R_RM_R (0x1 << 5) +#define RT5670_M_IN_R_RM_R_SFT 5 +#define RT5670_M_BST2_RM_R (0x1 << 3) +#define RT5670_M_BST2_RM_R_SFT 3 +#define RT5670_M_BST1_RM_R (0x1 << 1) +#define RT5670_M_BST1_RM_R_SFT 1 + +/* HPMIX Control (0x45) */ +#define RT5670_M_DAC2_HM (0x1 << 15) +#define RT5670_M_DAC2_HM_SFT 15 +#define RT5670_M_HPVOL_HM (0x1 << 14) +#define RT5670_M_HPVOL_HM_SFT 14 +#define RT5670_M_DAC1_HM (0x1 << 13) +#define RT5670_M_DAC1_HM_SFT 13 +#define RT5670_G_HPOMIX_MASK (0x1 << 12) +#define RT5670_G_HPOMIX_SFT 12 +#define RT5670_M_INR1_HMR (0x1 << 3) +#define RT5670_M_INR1_HMR_SFT 3 +#define RT5670_M_DACR1_HMR (0x1 << 2) +#define RT5670_M_DACR1_HMR_SFT 2 +#define RT5670_M_INL1_HML (0x1 << 1) +#define RT5670_M_INL1_HML_SFT 1 +#define RT5670_M_DACL1_HML (0x1) +#define RT5670_M_DACL1_HML_SFT 0 + +/* Mono Output Mixer Control (0x4c) */ +#define RT5670_M_DAC_R2_MA (0x1 << 15) +#define RT5670_M_DAC_R2_MA_SFT 15 +#define RT5670_M_DAC_L2_MA (0x1 << 14) +#define RT5670_M_DAC_L2_MA_SFT 14 +#define RT5670_M_OV_R_MM (0x1 << 13) +#define RT5670_M_OV_R_MM_SFT 13 +#define RT5670_M_OV_L_MM (0x1 << 12) +#define RT5670_M_OV_L_MM_SFT 12 +#define RT5670_G_MONOMIX_MASK (0x1 << 10) +#define RT5670_G_MONOMIX_SFT 10 +#define RT5670_M_DAC_R2_MM (0x1 << 9) +#define RT5670_M_DAC_R2_MM_SFT 9 +#define RT5670_M_DAC_L2_MM (0x1 << 8) +#define RT5670_M_DAC_L2_MM_SFT 8 +#define RT5670_M_BST4_MM (0x1 << 7) +#define RT5670_M_BST4_MM_SFT 7 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5670_G_BST3_OM_L_MASK (0x7 << 13) +#define RT5670_G_BST3_OM_L_SFT 13 +#define RT5670_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5670_G_BST2_OM_L_SFT 10 +#define RT5670_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5670_G_BST1_OM_L_SFT 7 +#define RT5670_G_IN_L_OM_L_MASK (0x7 << 4) +#define RT5670_G_IN_L_OM_L_SFT 4 +#define RT5670_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5670_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5670_G_DAC_R2_OM_L_MASK (0x7 << 13) +#define RT5670_G_DAC_R2_OM_L_SFT 13 +#define RT5670_G_DAC_L2_OM_L_MASK (0x7 << 10) +#define RT5670_G_DAC_L2_OM_L_SFT 10 +#define RT5670_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5670_G_DAC_L1_OM_L_SFT 7 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5670_M_BST1_OM_L (0x1 << 5) +#define RT5670_M_BST1_OM_L_SFT 5 +#define RT5670_M_IN_L_OM_L (0x1 << 4) +#define RT5670_M_IN_L_OM_L_SFT 4 +#define RT5670_M_DAC_L2_OM_L (0x1 << 1) +#define RT5670_M_DAC_L2_OM_L_SFT 1 +#define RT5670_M_DAC_L1_OM_L (0x1) +#define RT5670_M_DAC_L1_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5670_G_BST4_OM_R_MASK (0x7 << 13) +#define RT5670_G_BST4_OM_R_SFT 13 +#define RT5670_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5670_G_BST2_OM_R_SFT 10 +#define RT5670_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5670_G_BST1_OM_R_SFT 7 +#define RT5670_G_IN_R_OM_R_MASK (0x7 << 4) +#define RT5670_G_IN_R_OM_R_SFT 4 +#define RT5670_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5670_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5670_G_DAC_L2_OM_R_MASK (0x7 << 13) +#define RT5670_G_DAC_L2_OM_R_SFT 13 +#define RT5670_G_DAC_R2_OM_R_MASK (0x7 << 10) +#define RT5670_G_DAC_R2_OM_R_SFT 10 +#define RT5670_G_DAC_R1_OM_R_MASK (0x7 << 7) +#define RT5670_G_DAC_R1_OM_R_SFT 7 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5670_M_BST2_OM_R (0x1 << 6) +#define RT5670_M_BST2_OM_R_SFT 6 +#define RT5670_M_IN_R_OM_R (0x1 << 4) +#define RT5670_M_IN_R_OM_R_SFT 4 +#define RT5670_M_DAC_R2_OM_R (0x1 << 1) +#define RT5670_M_DAC_R2_OM_R_SFT 1 +#define RT5670_M_DAC_R1_OM_R (0x1) +#define RT5670_M_DAC_R1_OM_R_SFT 0 + +/* LOUT Mixer Control (0x53) */ +#define RT5670_M_DAC_L1_LM (0x1 << 15) +#define RT5670_M_DAC_L1_LM_SFT 15 +#define RT5670_M_DAC_R1_LM (0x1 << 14) +#define RT5670_M_DAC_R1_LM_SFT 14 +#define RT5670_M_OV_L_LM (0x1 << 13) +#define RT5670_M_OV_L_LM_SFT 13 +#define RT5670_M_OV_R_LM (0x1 << 12) +#define RT5670_M_OV_R_LM_SFT 12 +#define RT5670_G_LOUTMIX_MASK (0x1 << 11) +#define RT5670_G_LOUTMIX_SFT 11 + +/* Power Management for Digital 1 (0x61) */ +#define RT5670_PWR_I2S1 (0x1 << 15) +#define RT5670_PWR_I2S1_BIT 15 +#define RT5670_PWR_I2S2 (0x1 << 14) +#define RT5670_PWR_I2S2_BIT 14 +#define RT5670_PWR_DAC_L1 (0x1 << 12) +#define RT5670_PWR_DAC_L1_BIT 12 +#define RT5670_PWR_DAC_R1 (0x1 << 11) +#define RT5670_PWR_DAC_R1_BIT 11 +#define RT5670_PWR_DAC_L2 (0x1 << 7) +#define RT5670_PWR_DAC_L2_BIT 7 +#define RT5670_PWR_DAC_R2 (0x1 << 6) +#define RT5670_PWR_DAC_R2_BIT 6 +#define RT5670_PWR_ADC_L (0x1 << 2) +#define RT5670_PWR_ADC_L_BIT 2 +#define RT5670_PWR_ADC_R (0x1 << 1) +#define RT5670_PWR_ADC_R_BIT 1 +#define RT5670_PWR_CLS_D (0x1) +#define RT5670_PWR_CLS_D_BIT 0 + +/* Power Management for Digital 2 (0x62) */ +#define RT5670_PWR_ADC_S1F (0x1 << 15) +#define RT5670_PWR_ADC_S1F_BIT 15 +#define RT5670_PWR_ADC_MF_L (0x1 << 14) +#define RT5670_PWR_ADC_MF_L_BIT 14 +#define RT5670_PWR_ADC_MF_R (0x1 << 13) +#define RT5670_PWR_ADC_MF_R_BIT 13 +#define RT5670_PWR_I2S_DSP (0x1 << 12) +#define RT5670_PWR_I2S_DSP_BIT 12 +#define RT5670_PWR_DAC_S1F (0x1 << 11) +#define RT5670_PWR_DAC_S1F_BIT 11 +#define RT5670_PWR_DAC_MF_L (0x1 << 10) +#define RT5670_PWR_DAC_MF_L_BIT 10 +#define RT5670_PWR_DAC_MF_R (0x1 << 9) +#define RT5670_PWR_DAC_MF_R_BIT 9 +#define RT5670_PWR_ADC_S2F (0x1 << 8) +#define RT5670_PWR_ADC_S2F_BIT 8 +#define RT5670_PWR_PDM1 (0x1 << 7) +#define RT5670_PWR_PDM1_BIT 7 +#define RT5670_PWR_PDM2 (0x1 << 6) +#define RT5670_PWR_PDM2_BIT 6 + +/* Power Management for Analog 1 (0x63) */ +#define RT5670_PWR_VREF1 (0x1 << 15) +#define RT5670_PWR_VREF1_BIT 15 +#define RT5670_PWR_FV1 (0x1 << 14) +#define RT5670_PWR_FV1_BIT 14 +#define RT5670_PWR_MB (0x1 << 13) +#define RT5670_PWR_MB_BIT 13 +#define RT5670_PWR_LM (0x1 << 12) +#define RT5670_PWR_LM_BIT 12 +#define RT5670_PWR_BG (0x1 << 11) +#define RT5670_PWR_BG_BIT 11 +#define RT5670_PWR_HP_L (0x1 << 7) +#define RT5670_PWR_HP_L_BIT 7 +#define RT5670_PWR_HP_R (0x1 << 6) +#define RT5670_PWR_HP_R_BIT 6 +#define RT5670_PWR_HA (0x1 << 5) +#define RT5670_PWR_HA_BIT 5 +#define RT5670_PWR_VREF2 (0x1 << 4) +#define RT5670_PWR_VREF2_BIT 4 +#define RT5670_PWR_FV2 (0x1 << 3) +#define RT5670_PWR_FV2_BIT 3 +#define RT5670_LDO_SEL_MASK (0x7) +#define RT5670_LDO_SEL_SFT 0 + +/* Power Management for Analog 2 (0x64) */ +#define RT5670_PWR_BST1 (0x1 << 15) +#define RT5670_PWR_BST1_BIT 15 +#define RT5670_PWR_BST2 (0x1 << 13) +#define RT5670_PWR_BST2_BIT 13 +#define RT5670_PWR_MB1 (0x1 << 11) +#define RT5670_PWR_MB1_BIT 11 +#define RT5670_PWR_MB2 (0x1 << 10) +#define RT5670_PWR_MB2_BIT 10 +#define RT5670_PWR_PLL (0x1 << 9) +#define RT5670_PWR_PLL_BIT 9 +#define RT5670_PWR_BST1_P (0x1 << 6) +#define RT5670_PWR_BST1_P_BIT 6 +#define RT5670_PWR_BST2_P (0x1 << 4) +#define RT5670_PWR_BST2_P_BIT 4 +#define RT5670_PWR_JD1 (0x1 << 2) +#define RT5670_PWR_JD1_BIT 2 +#define RT5670_PWR_JD (0x1 << 1) +#define RT5670_PWR_JD_BIT 1 + +/* Power Management for Mixer (0x65) */ +#define RT5670_PWR_OM_L (0x1 << 15) +#define RT5670_PWR_OM_L_BIT 15 +#define RT5670_PWR_OM_R (0x1 << 14) +#define RT5670_PWR_OM_R_BIT 14 +#define RT5670_PWR_RM_L (0x1 << 11) +#define RT5670_PWR_RM_L_BIT 11 +#define RT5670_PWR_RM_R (0x1 << 10) +#define RT5670_PWR_RM_R_BIT 10 + +/* Power Management for Volume (0x66) */ +#define RT5670_PWR_HV_L (0x1 << 11) +#define RT5670_PWR_HV_L_BIT 11 +#define RT5670_PWR_HV_R (0x1 << 10) +#define RT5670_PWR_HV_R_BIT 10 +#define RT5670_PWR_IN_L (0x1 << 9) +#define RT5670_PWR_IN_L_BIT 9 +#define RT5670_PWR_IN_R (0x1 << 8) +#define RT5670_PWR_IN_R_BIT 8 +#define RT5670_PWR_MIC_DET (0x1 << 5) +#define RT5670_PWR_MIC_DET_BIT 5 + +/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71 0x72) */ +#define RT5670_I2S_MS_MASK (0x1 << 15) +#define RT5670_I2S_MS_SFT 15 +#define RT5670_I2S_MS_M (0x0 << 15) +#define RT5670_I2S_MS_S (0x1 << 15) +#define RT5670_I2S_IF_MASK (0x7 << 12) +#define RT5670_I2S_IF_SFT 12 +#define RT5670_I2S_O_CP_MASK (0x3 << 10) +#define RT5670_I2S_O_CP_SFT 10 +#define RT5670_I2S_O_CP_OFF (0x0 << 10) +#define RT5670_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5670_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5670_I2S_I_CP_MASK (0x3 << 8) +#define RT5670_I2S_I_CP_SFT 8 +#define RT5670_I2S_I_CP_OFF (0x0 << 8) +#define RT5670_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5670_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5670_I2S_BP_MASK (0x1 << 7) +#define RT5670_I2S_BP_SFT 7 +#define RT5670_I2S_BP_NOR (0x0 << 7) +#define RT5670_I2S_BP_INV (0x1 << 7) +#define RT5670_I2S_DL_MASK (0x3 << 2) +#define RT5670_I2S_DL_SFT 2 +#define RT5670_I2S_DL_16 (0x0 << 2) +#define RT5670_I2S_DL_20 (0x1 << 2) +#define RT5670_I2S_DL_24 (0x2 << 2) +#define RT5670_I2S_DL_8 (0x3 << 2) +#define RT5670_I2S_DF_MASK (0x3) +#define RT5670_I2S_DF_SFT 0 +#define RT5670_I2S_DF_I2S (0x0) +#define RT5670_I2S_DF_LEFT (0x1) +#define RT5670_I2S_DF_PCM_A (0x2) +#define RT5670_I2S_DF_PCM_B (0x3) + +/* I2S2 Audio Serial Data Port Control (0x71) */ +#define RT5670_I2S2_SDI_MASK (0x1 << 6) +#define RT5670_I2S2_SDI_SFT 6 +#define RT5670_I2S2_SDI_I2S1 (0x0 << 6) +#define RT5670_I2S2_SDI_I2S2 (0x1 << 6) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5670_I2S_BCLK_MS1_MASK (0x1 << 15) +#define RT5670_I2S_BCLK_MS1_SFT 15 +#define RT5670_I2S_BCLK_MS1_32 (0x0 << 15) +#define RT5670_I2S_BCLK_MS1_64 (0x1 << 15) +#define RT5670_I2S_PD1_MASK (0x7 << 12) +#define RT5670_I2S_PD1_SFT 12 +#define RT5670_I2S_PD1_1 (0x0 << 12) +#define RT5670_I2S_PD1_2 (0x1 << 12) +#define RT5670_I2S_PD1_3 (0x2 << 12) +#define RT5670_I2S_PD1_4 (0x3 << 12) +#define RT5670_I2S_PD1_6 (0x4 << 12) +#define RT5670_I2S_PD1_8 (0x5 << 12) +#define RT5670_I2S_PD1_12 (0x6 << 12) +#define RT5670_I2S_PD1_16 (0x7 << 12) +#define RT5670_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5670_I2S_BCLK_MS2_SFT 11 +#define RT5670_I2S_BCLK_MS2_32 (0x0 << 11) +#define RT5670_I2S_BCLK_MS2_64 (0x1 << 11) +#define RT5670_I2S_PD2_MASK (0x7 << 8) +#define RT5670_I2S_PD2_SFT 8 +#define RT5670_I2S_PD2_1 (0x0 << 8) +#define RT5670_I2S_PD2_2 (0x1 << 8) +#define RT5670_I2S_PD2_3 (0x2 << 8) +#define RT5670_I2S_PD2_4 (0x3 << 8) +#define RT5670_I2S_PD2_6 (0x4 << 8) +#define RT5670_I2S_PD2_8 (0x5 << 8) +#define RT5670_I2S_PD2_12 (0x6 << 8) +#define RT5670_I2S_PD2_16 (0x7 << 8) +#define RT5670_I2S_BCLK_MS3_MASK (0x1 << 7) +#define RT5670_I2S_BCLK_MS3_SFT 7 +#define RT5670_I2S_BCLK_MS3_32 (0x0 << 7) +#define RT5670_I2S_BCLK_MS3_64 (0x1 << 7) +#define RT5670_I2S_PD3_MASK (0x7 << 4) +#define RT5670_I2S_PD3_SFT 4 +#define RT5670_I2S_PD3_1 (0x0 << 4) +#define RT5670_I2S_PD3_2 (0x1 << 4) +#define RT5670_I2S_PD3_3 (0x2 << 4) +#define RT5670_I2S_PD3_4 (0x3 << 4) +#define RT5670_I2S_PD3_6 (0x4 << 4) +#define RT5670_I2S_PD3_8 (0x5 << 4) +#define RT5670_I2S_PD3_12 (0x6 << 4) +#define RT5670_I2S_PD3_16 (0x7 << 4) +#define RT5670_DAC_OSR_MASK (0x3 << 2) +#define RT5670_DAC_OSR_SFT 2 +#define RT5670_DAC_OSR_128 (0x0 << 2) +#define RT5670_DAC_OSR_64 (0x1 << 2) +#define RT5670_DAC_OSR_32 (0x2 << 2) +#define RT5670_DAC_OSR_16 (0x3 << 2) +#define RT5670_ADC_OSR_MASK (0x3) +#define RT5670_ADC_OSR_SFT 0 +#define RT5670_ADC_OSR_128 (0x0) +#define RT5670_ADC_OSR_64 (0x1) +#define RT5670_ADC_OSR_32 (0x2) +#define RT5670_ADC_OSR_16 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5670_DAC_L_OSR_MASK (0x3 << 14) +#define RT5670_DAC_L_OSR_SFT 14 +#define RT5670_DAC_L_OSR_128 (0x0 << 14) +#define RT5670_DAC_L_OSR_64 (0x1 << 14) +#define RT5670_DAC_L_OSR_32 (0x2 << 14) +#define RT5670_DAC_L_OSR_16 (0x3 << 14) +#define RT5670_ADC_R_OSR_MASK (0x3 << 12) +#define RT5670_ADC_R_OSR_SFT 12 +#define RT5670_ADC_R_OSR_128 (0x0 << 12) +#define RT5670_ADC_R_OSR_64 (0x1 << 12) +#define RT5670_ADC_R_OSR_32 (0x2 << 12) +#define RT5670_ADC_R_OSR_16 (0x3 << 12) +#define RT5670_DAHPF_EN (0x1 << 11) +#define RT5670_DAHPF_EN_SFT 11 +#define RT5670_ADHPF_EN (0x1 << 10) +#define RT5670_ADHPF_EN_SFT 10 + +/* Digital Microphone Control (0x75) */ +#define RT5670_DMIC_1_EN_MASK (0x1 << 15) +#define RT5670_DMIC_1_EN_SFT 15 +#define RT5670_DMIC_1_DIS (0x0 << 15) +#define RT5670_DMIC_1_EN (0x1 << 15) +#define RT5670_DMIC_2_EN_MASK (0x1 << 14) +#define RT5670_DMIC_2_EN_SFT 14 +#define RT5670_DMIC_2_DIS (0x0 << 14) +#define RT5670_DMIC_2_EN (0x1 << 14) +#define RT5670_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5670_DMIC_1L_LH_SFT 13 +#define RT5670_DMIC_1L_LH_FALLING (0x0 << 13) +#define RT5670_DMIC_1L_LH_RISING (0x1 << 13) +#define RT5670_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5670_DMIC_1R_LH_SFT 12 +#define RT5670_DMIC_1R_LH_FALLING (0x0 << 12) +#define RT5670_DMIC_1R_LH_RISING (0x1 << 12) +#define RT5670_DMIC_2_DP_MASK (0x1 << 10) +#define RT5670_DMIC_2_DP_SFT 10 +#define RT5670_DMIC_2_DP_GPIO8 (0x0 << 10) +#define RT5670_DMIC_2_DP_IN3N (0x1 << 10) +#define RT5670_DMIC_2L_LH_MASK (0x1 << 9) +#define RT5670_DMIC_2L_LH_SFT 9 +#define RT5670_DMIC_2L_LH_FALLING (0x0 << 9) +#define RT5670_DMIC_2L_LH_RISING (0x1 << 9) +#define RT5670_DMIC_2R_LH_MASK (0x1 << 8) +#define RT5670_DMIC_2R_LH_SFT 8 +#define RT5670_DMIC_2R_LH_FALLING (0x0 << 8) +#define RT5670_DMIC_2R_LH_RISING (0x1 << 8) +#define RT5670_DMIC_CLK_MASK (0x7 << 5) +#define RT5670_DMIC_CLK_SFT 5 +#define RT5670_DMIC_3_EN_MASK (0x1 << 4) +#define RT5670_DMIC_3_EN_SFT 4 +#define RT5670_DMIC_3_DIS (0x0 << 4) +#define RT5670_DMIC_3_EN (0x1 << 4) +#define RT5670_DMIC_1_DP_MASK (0x3 << 0) +#define RT5670_DMIC_1_DP_SFT 0 +#define RT5670_DMIC_1_DP_GPIO6 (0x0 << 0) +#define RT5670_DMIC_1_DP_IN2P (0x1 << 0) +#define RT5670_DMIC_1_DP_GPIO7 (0x2 << 0) + +/* Digital Microphone Control2 (0x76) */ +#define RT5670_DMIC_3_DP_MASK (0x3 << 6) +#define RT5670_DMIC_3_DP_SFT 6 +#define RT5670_DMIC_3_DP_GPIO9 (0x0 << 6) +#define RT5670_DMIC_3_DP_GPIO10 (0x1 << 6) +#define RT5670_DMIC_3_DP_GPIO5 (0x2 << 6) + +/* Global Clock Control (0x80) */ +#define RT5670_SCLK_SRC_MASK (0x3 << 14) +#define RT5670_SCLK_SRC_SFT 14 +#define RT5670_SCLK_SRC_MCLK (0x0 << 14) +#define RT5670_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5670_SCLK_SRC_RCCLK (0x2 << 14) /* 15MHz */ +#define RT5670_PLL1_SRC_MASK (0x7 << 11) +#define RT5670_PLL1_SRC_SFT 11 +#define RT5670_PLL1_SRC_MCLK (0x0 << 11) +#define RT5670_PLL1_SRC_BCLK1 (0x1 << 11) +#define RT5670_PLL1_SRC_BCLK2 (0x2 << 11) +#define RT5670_PLL1_SRC_BCLK3 (0x3 << 11) +#define RT5670_PLL1_PD_MASK (0x1 << 3) +#define RT5670_PLL1_PD_SFT 3 +#define RT5670_PLL1_PD_1 (0x0 << 3) +#define RT5670_PLL1_PD_2 (0x1 << 3) + +#define RT5670_PLL_INP_MAX 40000000 +#define RT5670_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5670_PLL_N_MAX 0x1ff +#define RT5670_PLL_N_MASK (RT5670_PLL_N_MAX << 7) +#define RT5670_PLL_N_SFT 7 +#define RT5670_PLL_K_MAX 0x1f +#define RT5670_PLL_K_MASK (RT5670_PLL_K_MAX) +#define RT5670_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5670_PLL_M_MAX 0xf +#define RT5670_PLL_M_MASK (RT5670_PLL_M_MAX << 12) +#define RT5670_PLL_M_SFT 12 +#define RT5670_PLL_M_BP (0x1 << 11) +#define RT5670_PLL_M_BP_SFT 11 + +/* ASRC Control 1 (0x83) */ +#define RT5670_STO_T_MASK (0x1 << 15) +#define RT5670_STO_T_SFT 15 +#define RT5670_STO_T_SCLK (0x0 << 15) +#define RT5670_STO_T_LRCK1 (0x1 << 15) +#define RT5670_M1_T_MASK (0x1 << 14) +#define RT5670_M1_T_SFT 14 +#define RT5670_M1_T_I2S2 (0x0 << 14) +#define RT5670_M1_T_I2S2_D3 (0x1 << 14) +#define RT5670_I2S2_F_MASK (0x1 << 12) +#define RT5670_I2S2_F_SFT 12 +#define RT5670_I2S2_F_I2S2_D2 (0x0 << 12) +#define RT5670_I2S2_F_I2S1_TCLK (0x1 << 12) +#define RT5670_DMIC_1_M_MASK (0x1 << 9) +#define RT5670_DMIC_1_M_SFT 9 +#define RT5670_DMIC_1_M_NOR (0x0 << 9) +#define RT5670_DMIC_1_M_ASYN (0x1 << 9) +#define RT5670_DMIC_2_M_MASK (0x1 << 8) +#define RT5670_DMIC_2_M_SFT 8 +#define RT5670_DMIC_2_M_NOR (0x0 << 8) +#define RT5670_DMIC_2_M_ASYN (0x1 << 8) + +/* ASRC clock source selection (0x84, 0x85) */ +#define RT5670_CLK_SEL_SYS (0x0) +#define RT5670_CLK_SEL_I2S1_ASRC (0x1) +#define RT5670_CLK_SEL_I2S2_ASRC (0x2) +#define RT5670_CLK_SEL_I2S3_ASRC (0x3) +#define RT5670_CLK_SEL_SYS2 (0x5) +#define RT5670_CLK_SEL_SYS3 (0x6) + +/* ASRC Control 2 (0x84) */ +#define RT5670_DA_STO_CLK_SEL_MASK (0xf << 12) +#define RT5670_DA_STO_CLK_SEL_SFT 12 +#define RT5670_DA_MONOL_CLK_SEL_MASK (0xf << 8) +#define RT5670_DA_MONOL_CLK_SEL_SFT 8 +#define RT5670_DA_MONOR_CLK_SEL_MASK (0xf << 4) +#define RT5670_DA_MONOR_CLK_SEL_SFT 4 +#define RT5670_AD_STO1_CLK_SEL_MASK (0xf << 0) +#define RT5670_AD_STO1_CLK_SEL_SFT 0 + +/* ASRC Control 3 (0x85) */ +#define RT5670_UP_CLK_SEL_MASK (0xf << 12) +#define RT5670_UP_CLK_SEL_SFT 12 +#define RT5670_DOWN_CLK_SEL_MASK (0xf << 8) +#define RT5670_DOWN_CLK_SEL_SFT 8 +#define RT5670_AD_MONOL_CLK_SEL_MASK (0xf << 4) +#define RT5670_AD_MONOL_CLK_SEL_SFT 4 +#define RT5670_AD_MONOR_CLK_SEL_MASK (0xf << 0) +#define RT5670_AD_MONOR_CLK_SEL_SFT 0 + +/* ASRC Control 4 (0x89) */ +#define RT5670_I2S1_PD_MASK (0x7 << 12) +#define RT5670_I2S1_PD_SFT 12 +#define RT5670_I2S2_PD_MASK (0x7 << 8) +#define RT5670_I2S2_PD_SFT 8 + +/* HPOUT Over Current Detection (0x8b) */ +#define RT5670_HP_OVCD_MASK (0x1 << 10) +#define RT5670_HP_OVCD_SFT 10 +#define RT5670_HP_OVCD_DIS (0x0 << 10) +#define RT5670_HP_OVCD_EN (0x1 << 10) +#define RT5670_HP_OC_TH_MASK (0x3 << 8) +#define RT5670_HP_OC_TH_SFT 8 +#define RT5670_HP_OC_TH_90 (0x0 << 8) +#define RT5670_HP_OC_TH_105 (0x1 << 8) +#define RT5670_HP_OC_TH_120 (0x2 << 8) +#define RT5670_HP_OC_TH_135 (0x3 << 8) + +/* Class D Over Current Control (0x8c) */ +#define RT5670_CLSD_OC_MASK (0x1 << 9) +#define RT5670_CLSD_OC_SFT 9 +#define RT5670_CLSD_OC_PU (0x0 << 9) +#define RT5670_CLSD_OC_PD (0x1 << 9) +#define RT5670_AUTO_PD_MASK (0x1 << 8) +#define RT5670_AUTO_PD_SFT 8 +#define RT5670_AUTO_PD_DIS (0x0 << 8) +#define RT5670_AUTO_PD_EN (0x1 << 8) +#define RT5670_CLSD_OC_TH_MASK (0x3f) +#define RT5670_CLSD_OC_TH_SFT 0 + +/* Class D Output Control (0x8d) */ +#define RT5670_CLSD_RATIO_MASK (0xf << 12) +#define RT5670_CLSD_RATIO_SFT 12 +#define RT5670_CLSD_OM_MASK (0x1 << 11) +#define RT5670_CLSD_OM_SFT 11 +#define RT5670_CLSD_OM_MONO (0x0 << 11) +#define RT5670_CLSD_OM_STO (0x1 << 11) +#define RT5670_CLSD_SCH_MASK (0x1 << 10) +#define RT5670_CLSD_SCH_SFT 10 +#define RT5670_CLSD_SCH_L (0x0 << 10) +#define RT5670_CLSD_SCH_S (0x1 << 10) + +/* Depop Mode Control 1 (0x8e) */ +#define RT5670_SMT_TRIG_MASK (0x1 << 15) +#define RT5670_SMT_TRIG_SFT 15 +#define RT5670_SMT_TRIG_DIS (0x0 << 15) +#define RT5670_SMT_TRIG_EN (0x1 << 15) +#define RT5670_HP_L_SMT_MASK (0x1 << 9) +#define RT5670_HP_L_SMT_SFT 9 +#define RT5670_HP_L_SMT_DIS (0x0 << 9) +#define RT5670_HP_L_SMT_EN (0x1 << 9) +#define RT5670_HP_R_SMT_MASK (0x1 << 8) +#define RT5670_HP_R_SMT_SFT 8 +#define RT5670_HP_R_SMT_DIS (0x0 << 8) +#define RT5670_HP_R_SMT_EN (0x1 << 8) +#define RT5670_HP_CD_PD_MASK (0x1 << 7) +#define RT5670_HP_CD_PD_SFT 7 +#define RT5670_HP_CD_PD_DIS (0x0 << 7) +#define RT5670_HP_CD_PD_EN (0x1 << 7) +#define RT5670_RSTN_MASK (0x1 << 6) +#define RT5670_RSTN_SFT 6 +#define RT5670_RSTN_DIS (0x0 << 6) +#define RT5670_RSTN_EN (0x1 << 6) +#define RT5670_RSTP_MASK (0x1 << 5) +#define RT5670_RSTP_SFT 5 +#define RT5670_RSTP_DIS (0x0 << 5) +#define RT5670_RSTP_EN (0x1 << 5) +#define RT5670_HP_CO_MASK (0x1 << 4) +#define RT5670_HP_CO_SFT 4 +#define RT5670_HP_CO_DIS (0x0 << 4) +#define RT5670_HP_CO_EN (0x1 << 4) +#define RT5670_HP_CP_MASK (0x1 << 3) +#define RT5670_HP_CP_SFT 3 +#define RT5670_HP_CP_PD (0x0 << 3) +#define RT5670_HP_CP_PU (0x1 << 3) +#define RT5670_HP_SG_MASK (0x1 << 2) +#define RT5670_HP_SG_SFT 2 +#define RT5670_HP_SG_DIS (0x0 << 2) +#define RT5670_HP_SG_EN (0x1 << 2) +#define RT5670_HP_DP_MASK (0x1 << 1) +#define RT5670_HP_DP_SFT 1 +#define RT5670_HP_DP_PD (0x0 << 1) +#define RT5670_HP_DP_PU (0x1 << 1) +#define RT5670_HP_CB_MASK (0x1) +#define RT5670_HP_CB_SFT 0 +#define RT5670_HP_CB_PD (0x0) +#define RT5670_HP_CB_PU (0x1) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5670_DEPOP_MASK (0x1 << 13) +#define RT5670_DEPOP_SFT 13 +#define RT5670_DEPOP_AUTO (0x0 << 13) +#define RT5670_DEPOP_MAN (0x1 << 13) +#define RT5670_RAMP_MASK (0x1 << 12) +#define RT5670_RAMP_SFT 12 +#define RT5670_RAMP_DIS (0x0 << 12) +#define RT5670_RAMP_EN (0x1 << 12) +#define RT5670_BPS_MASK (0x1 << 11) +#define RT5670_BPS_SFT 11 +#define RT5670_BPS_DIS (0x0 << 11) +#define RT5670_BPS_EN (0x1 << 11) +#define RT5670_FAST_UPDN_MASK (0x1 << 10) +#define RT5670_FAST_UPDN_SFT 10 +#define RT5670_FAST_UPDN_DIS (0x0 << 10) +#define RT5670_FAST_UPDN_EN (0x1 << 10) +#define RT5670_MRES_MASK (0x3 << 8) +#define RT5670_MRES_SFT 8 +#define RT5670_MRES_15MO (0x0 << 8) +#define RT5670_MRES_25MO (0x1 << 8) +#define RT5670_MRES_35MO (0x2 << 8) +#define RT5670_MRES_45MO (0x3 << 8) +#define RT5670_VLO_MASK (0x1 << 7) +#define RT5670_VLO_SFT 7 +#define RT5670_VLO_3V (0x0 << 7) +#define RT5670_VLO_32V (0x1 << 7) +#define RT5670_DIG_DP_MASK (0x1 << 6) +#define RT5670_DIG_DP_SFT 6 +#define RT5670_DIG_DP_DIS (0x0 << 6) +#define RT5670_DIG_DP_EN (0x1 << 6) +#define RT5670_DP_TH_MASK (0x3 << 4) +#define RT5670_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5670_CP_SYS_MASK (0x7 << 12) +#define RT5670_CP_SYS_SFT 12 +#define RT5670_CP_FQ1_MASK (0x7 << 8) +#define RT5670_CP_FQ1_SFT 8 +#define RT5670_CP_FQ2_MASK (0x7 << 4) +#define RT5670_CP_FQ2_SFT 4 +#define RT5670_CP_FQ3_MASK (0x7) +#define RT5670_CP_FQ3_SFT 0 +#define RT5670_CP_FQ_1_5_KHZ 0 +#define RT5670_CP_FQ_3_KHZ 1 +#define RT5670_CP_FQ_6_KHZ 2 +#define RT5670_CP_FQ_12_KHZ 3 +#define RT5670_CP_FQ_24_KHZ 4 +#define RT5670_CP_FQ_48_KHZ 5 +#define RT5670_CP_FQ_96_KHZ 6 +#define RT5670_CP_FQ_192_KHZ 7 + +/* HPOUT charge pump (0x91) */ +#define RT5670_OSW_L_MASK (0x1 << 11) +#define RT5670_OSW_L_SFT 11 +#define RT5670_OSW_L_DIS (0x0 << 11) +#define RT5670_OSW_L_EN (0x1 << 11) +#define RT5670_OSW_R_MASK (0x1 << 10) +#define RT5670_OSW_R_SFT 10 +#define RT5670_OSW_R_DIS (0x0 << 10) +#define RT5670_OSW_R_EN (0x1 << 10) +#define RT5670_PM_HP_MASK (0x3 << 8) +#define RT5670_PM_HP_SFT 8 +#define RT5670_PM_HP_LV (0x0 << 8) +#define RT5670_PM_HP_MV (0x1 << 8) +#define RT5670_PM_HP_HV (0x2 << 8) +#define RT5670_IB_HP_MASK (0x3 << 6) +#define RT5670_IB_HP_SFT 6 +#define RT5670_IB_HP_125IL (0x0 << 6) +#define RT5670_IB_HP_25IL (0x1 << 6) +#define RT5670_IB_HP_5IL (0x2 << 6) +#define RT5670_IB_HP_1IL (0x3 << 6) + +/* PV detection and SPK gain control (0x92) */ +#define RT5670_PVDD_DET_MASK (0x1 << 15) +#define RT5670_PVDD_DET_SFT 15 +#define RT5670_PVDD_DET_DIS (0x0 << 15) +#define RT5670_PVDD_DET_EN (0x1 << 15) +#define RT5670_SPK_AG_MASK (0x1 << 14) +#define RT5670_SPK_AG_SFT 14 +#define RT5670_SPK_AG_DIS (0x0 << 14) +#define RT5670_SPK_AG_EN (0x1 << 14) + +/* Micbias Control (0x93) */ +#define RT5670_MIC1_BS_MASK (0x1 << 15) +#define RT5670_MIC1_BS_SFT 15 +#define RT5670_MIC1_BS_9AV (0x0 << 15) +#define RT5670_MIC1_BS_75AV (0x1 << 15) +#define RT5670_MIC2_BS_MASK (0x1 << 14) +#define RT5670_MIC2_BS_SFT 14 +#define RT5670_MIC2_BS_9AV (0x0 << 14) +#define RT5670_MIC2_BS_75AV (0x1 << 14) +#define RT5670_MIC1_CLK_MASK (0x1 << 13) +#define RT5670_MIC1_CLK_SFT 13 +#define RT5670_MIC1_CLK_DIS (0x0 << 13) +#define RT5670_MIC1_CLK_EN (0x1 << 13) +#define RT5670_MIC2_CLK_MASK (0x1 << 12) +#define RT5670_MIC2_CLK_SFT 12 +#define RT5670_MIC2_CLK_DIS (0x0 << 12) +#define RT5670_MIC2_CLK_EN (0x1 << 12) +#define RT5670_MIC1_OVCD_MASK (0x1 << 11) +#define RT5670_MIC1_OVCD_SFT 11 +#define RT5670_MIC1_OVCD_DIS (0x0 << 11) +#define RT5670_MIC1_OVCD_EN (0x1 << 11) +#define RT5670_MIC1_OVTH_MASK (0x3 << 9) +#define RT5670_MIC1_OVTH_SFT 9 +#define RT5670_MIC1_OVTH_600UA (0x0 << 9) +#define RT5670_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5670_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5670_MIC2_OVCD_MASK (0x1 << 8) +#define RT5670_MIC2_OVCD_SFT 8 +#define RT5670_MIC2_OVCD_DIS (0x0 << 8) +#define RT5670_MIC2_OVCD_EN (0x1 << 8) +#define RT5670_MIC2_OVTH_MASK (0x3 << 6) +#define RT5670_MIC2_OVTH_SFT 6 +#define RT5670_MIC2_OVTH_600UA (0x0 << 6) +#define RT5670_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5670_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5670_PWR_MB_MASK (0x1 << 5) +#define RT5670_PWR_MB_SFT 5 +#define RT5670_PWR_MB_PD (0x0 << 5) +#define RT5670_PWR_MB_PU (0x1 << 5) +#define RT5670_PWR_CLK25M_MASK (0x1 << 4) +#define RT5670_PWR_CLK25M_SFT 4 +#define RT5670_PWR_CLK25M_PD (0x0 << 4) +#define RT5670_PWR_CLK25M_PU (0x1 << 4) + +/* Analog JD Control 1 (0x94) */ +#define RT5670_JD1_MODE_MASK (0x3 << 0) +#define RT5670_JD1_MODE_0 (0x0 << 0) +#define RT5670_JD1_MODE_1 (0x1 << 0) +#define RT5670_JD1_MODE_2 (0x2 << 0) + +/* VAD Control 4 (0x9d) */ +#define RT5670_VAD_SEL_MASK (0x3 << 8) +#define RT5670_VAD_SEL_SFT 8 + +/* EQ Control 1 (0xb0) */ +#define RT5670_EQ_SRC_MASK (0x1 << 15) +#define RT5670_EQ_SRC_SFT 15 +#define RT5670_EQ_SRC_DAC (0x0 << 15) +#define RT5670_EQ_SRC_ADC (0x1 << 15) +#define RT5670_EQ_UPD (0x1 << 14) +#define RT5670_EQ_UPD_BIT 14 +#define RT5670_EQ_CD_MASK (0x1 << 13) +#define RT5670_EQ_CD_SFT 13 +#define RT5670_EQ_CD_DIS (0x0 << 13) +#define RT5670_EQ_CD_EN (0x1 << 13) +#define RT5670_EQ_DITH_MASK (0x3 << 8) +#define RT5670_EQ_DITH_SFT 8 +#define RT5670_EQ_DITH_NOR (0x0 << 8) +#define RT5670_EQ_DITH_LSB (0x1 << 8) +#define RT5670_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5670_EQ_DITH_LSB_2 (0x3 << 8) + +/* EQ Control 2 (0xb1) */ +#define RT5670_EQ_HPF1_M_MASK (0x1 << 8) +#define RT5670_EQ_HPF1_M_SFT 8 +#define RT5670_EQ_HPF1_M_HI (0x0 << 8) +#define RT5670_EQ_HPF1_M_1ST (0x1 << 8) +#define RT5670_EQ_LPF1_M_MASK (0x1 << 7) +#define RT5670_EQ_LPF1_M_SFT 7 +#define RT5670_EQ_LPF1_M_LO (0x0 << 7) +#define RT5670_EQ_LPF1_M_1ST (0x1 << 7) +#define RT5670_EQ_HPF2_MASK (0x1 << 6) +#define RT5670_EQ_HPF2_SFT 6 +#define RT5670_EQ_HPF2_DIS (0x0 << 6) +#define RT5670_EQ_HPF2_EN (0x1 << 6) +#define RT5670_EQ_HPF1_MASK (0x1 << 5) +#define RT5670_EQ_HPF1_SFT 5 +#define RT5670_EQ_HPF1_DIS (0x0 << 5) +#define RT5670_EQ_HPF1_EN (0x1 << 5) +#define RT5670_EQ_BPF4_MASK (0x1 << 4) +#define RT5670_EQ_BPF4_SFT 4 +#define RT5670_EQ_BPF4_DIS (0x0 << 4) +#define RT5670_EQ_BPF4_EN (0x1 << 4) +#define RT5670_EQ_BPF3_MASK (0x1 << 3) +#define RT5670_EQ_BPF3_SFT 3 +#define RT5670_EQ_BPF3_DIS (0x0 << 3) +#define RT5670_EQ_BPF3_EN (0x1 << 3) +#define RT5670_EQ_BPF2_MASK (0x1 << 2) +#define RT5670_EQ_BPF2_SFT 2 +#define RT5670_EQ_BPF2_DIS (0x0 << 2) +#define RT5670_EQ_BPF2_EN (0x1 << 2) +#define RT5670_EQ_BPF1_MASK (0x1 << 1) +#define RT5670_EQ_BPF1_SFT 1 +#define RT5670_EQ_BPF1_DIS (0x0 << 1) +#define RT5670_EQ_BPF1_EN (0x1 << 1) +#define RT5670_EQ_LPF_MASK (0x1) +#define RT5670_EQ_LPF_SFT 0 +#define RT5670_EQ_LPF_DIS (0x0) +#define RT5670_EQ_LPF_EN (0x1) +#define RT5670_EQ_CTRL_MASK (0x7f) + +/* Memory Test (0xb2) */ +#define RT5670_MT_MASK (0x1 << 15) +#define RT5670_MT_SFT 15 +#define RT5670_MT_DIS (0x0 << 15) +#define RT5670_MT_EN (0x1 << 15) + +/* DRC/AGC Control 1 (0xb4) */ +#define RT5670_DRC_AGC_P_MASK (0x1 << 15) +#define RT5670_DRC_AGC_P_SFT 15 +#define RT5670_DRC_AGC_P_DAC (0x0 << 15) +#define RT5670_DRC_AGC_P_ADC (0x1 << 15) +#define RT5670_DRC_AGC_MASK (0x1 << 14) +#define RT5670_DRC_AGC_SFT 14 +#define RT5670_DRC_AGC_DIS (0x0 << 14) +#define RT5670_DRC_AGC_EN (0x1 << 14) +#define RT5670_DRC_AGC_UPD (0x1 << 13) +#define RT5670_DRC_AGC_UPD_BIT 13 +#define RT5670_DRC_AGC_AR_MASK (0x1f << 8) +#define RT5670_DRC_AGC_AR_SFT 8 +#define RT5670_DRC_AGC_R_MASK (0x7 << 5) +#define RT5670_DRC_AGC_R_SFT 5 +#define RT5670_DRC_AGC_R_48K (0x1 << 5) +#define RT5670_DRC_AGC_R_96K (0x2 << 5) +#define RT5670_DRC_AGC_R_192K (0x3 << 5) +#define RT5670_DRC_AGC_R_441K (0x5 << 5) +#define RT5670_DRC_AGC_R_882K (0x6 << 5) +#define RT5670_DRC_AGC_R_1764K (0x7 << 5) +#define RT5670_DRC_AGC_RC_MASK (0x1f) +#define RT5670_DRC_AGC_RC_SFT 0 + +/* DRC/AGC Control 2 (0xb5) */ +#define RT5670_DRC_AGC_POB_MASK (0x3f << 8) +#define RT5670_DRC_AGC_POB_SFT 8 +#define RT5670_DRC_AGC_CP_MASK (0x1 << 7) +#define RT5670_DRC_AGC_CP_SFT 7 +#define RT5670_DRC_AGC_CP_DIS (0x0 << 7) +#define RT5670_DRC_AGC_CP_EN (0x1 << 7) +#define RT5670_DRC_AGC_CPR_MASK (0x3 << 5) +#define RT5670_DRC_AGC_CPR_SFT 5 +#define RT5670_DRC_AGC_CPR_1_1 (0x0 << 5) +#define RT5670_DRC_AGC_CPR_1_2 (0x1 << 5) +#define RT5670_DRC_AGC_CPR_1_3 (0x2 << 5) +#define RT5670_DRC_AGC_CPR_1_4 (0x3 << 5) +#define RT5670_DRC_AGC_PRB_MASK (0x1f) +#define RT5670_DRC_AGC_PRB_SFT 0 + +/* DRC/AGC Control 3 (0xb6) */ +#define RT5670_DRC_AGC_NGB_MASK (0xf << 12) +#define RT5670_DRC_AGC_NGB_SFT 12 +#define RT5670_DRC_AGC_TAR_MASK (0x1f << 7) +#define RT5670_DRC_AGC_TAR_SFT 7 +#define RT5670_DRC_AGC_NG_MASK (0x1 << 6) +#define RT5670_DRC_AGC_NG_SFT 6 +#define RT5670_DRC_AGC_NG_DIS (0x0 << 6) +#define RT5670_DRC_AGC_NG_EN (0x1 << 6) +#define RT5670_DRC_AGC_NGH_MASK (0x1 << 5) +#define RT5670_DRC_AGC_NGH_SFT 5 +#define RT5670_DRC_AGC_NGH_DIS (0x0 << 5) +#define RT5670_DRC_AGC_NGH_EN (0x1 << 5) +#define RT5670_DRC_AGC_NGT_MASK (0x1f) +#define RT5670_DRC_AGC_NGT_SFT 0 + +/* Jack Detect Control (0xbb) */ +#define RT5670_JD_MASK (0x7 << 13) +#define RT5670_JD_SFT 13 +#define RT5670_JD_DIS (0x0 << 13) +#define RT5670_JD_GPIO1 (0x1 << 13) +#define RT5670_JD_JD1_IN4P (0x2 << 13) +#define RT5670_JD_JD2_IN4N (0x3 << 13) +#define RT5670_JD_GPIO2 (0x4 << 13) +#define RT5670_JD_GPIO3 (0x5 << 13) +#define RT5670_JD_GPIO4 (0x6 << 13) +#define RT5670_JD_HP_MASK (0x1 << 11) +#define RT5670_JD_HP_SFT 11 +#define RT5670_JD_HP_DIS (0x0 << 11) +#define RT5670_JD_HP_EN (0x1 << 11) +#define RT5670_JD_HP_TRG_MASK (0x1 << 10) +#define RT5670_JD_HP_TRG_SFT 10 +#define RT5670_JD_HP_TRG_LO (0x0 << 10) +#define RT5670_JD_HP_TRG_HI (0x1 << 10) +#define RT5670_JD_SPL_MASK (0x1 << 9) +#define RT5670_JD_SPL_SFT 9 +#define RT5670_JD_SPL_DIS (0x0 << 9) +#define RT5670_JD_SPL_EN (0x1 << 9) +#define RT5670_JD_SPL_TRG_MASK (0x1 << 8) +#define RT5670_JD_SPL_TRG_SFT 8 +#define RT5670_JD_SPL_TRG_LO (0x0 << 8) +#define RT5670_JD_SPL_TRG_HI (0x1 << 8) +#define RT5670_JD_SPR_MASK (0x1 << 7) +#define RT5670_JD_SPR_SFT 7 +#define RT5670_JD_SPR_DIS (0x0 << 7) +#define RT5670_JD_SPR_EN (0x1 << 7) +#define RT5670_JD_SPR_TRG_MASK (0x1 << 6) +#define RT5670_JD_SPR_TRG_SFT 6 +#define RT5670_JD_SPR_TRG_LO (0x0 << 6) +#define RT5670_JD_SPR_TRG_HI (0x1 << 6) +#define RT5670_JD_MO_MASK (0x1 << 5) +#define RT5670_JD_MO_SFT 5 +#define RT5670_JD_MO_DIS (0x0 << 5) +#define RT5670_JD_MO_EN (0x1 << 5) +#define RT5670_JD_MO_TRG_MASK (0x1 << 4) +#define RT5670_JD_MO_TRG_SFT 4 +#define RT5670_JD_MO_TRG_LO (0x0 << 4) +#define RT5670_JD_MO_TRG_HI (0x1 << 4) +#define RT5670_JD_LO_MASK (0x1 << 3) +#define RT5670_JD_LO_SFT 3 +#define RT5670_JD_LO_DIS (0x0 << 3) +#define RT5670_JD_LO_EN (0x1 << 3) +#define RT5670_JD_LO_TRG_MASK (0x1 << 2) +#define RT5670_JD_LO_TRG_SFT 2 +#define RT5670_JD_LO_TRG_LO (0x0 << 2) +#define RT5670_JD_LO_TRG_HI (0x1 << 2) +#define RT5670_JD1_IN4P_MASK (0x1 << 1) +#define RT5670_JD1_IN4P_SFT 1 +#define RT5670_JD1_IN4P_DIS (0x0 << 1) +#define RT5670_JD1_IN4P_EN (0x1 << 1) +#define RT5670_JD2_IN4N_MASK (0x1) +#define RT5670_JD2_IN4N_SFT 0 +#define RT5670_JD2_IN4N_DIS (0x0) +#define RT5670_JD2_IN4N_EN (0x1) + +/* IRQ Control 1 (0xbd) */ +#define RT5670_IRQ_JD_MASK (0x1 << 15) +#define RT5670_IRQ_JD_SFT 15 +#define RT5670_IRQ_JD_BP (0x0 << 15) +#define RT5670_IRQ_JD_NOR (0x1 << 15) +#define RT5670_IRQ_OT_MASK (0x1 << 14) +#define RT5670_IRQ_OT_SFT 14 +#define RT5670_IRQ_OT_BP (0x0 << 14) +#define RT5670_IRQ_OT_NOR (0x1 << 14) +#define RT5670_JD_STKY_MASK (0x1 << 13) +#define RT5670_JD_STKY_SFT 13 +#define RT5670_JD_STKY_DIS (0x0 << 13) +#define RT5670_JD_STKY_EN (0x1 << 13) +#define RT5670_OT_STKY_MASK (0x1 << 12) +#define RT5670_OT_STKY_SFT 12 +#define RT5670_OT_STKY_DIS (0x0 << 12) +#define RT5670_OT_STKY_EN (0x1 << 12) +#define RT5670_JD_P_MASK (0x1 << 11) +#define RT5670_JD_P_SFT 11 +#define RT5670_JD_P_NOR (0x0 << 11) +#define RT5670_JD_P_INV (0x1 << 11) +#define RT5670_OT_P_MASK (0x1 << 10) +#define RT5670_OT_P_SFT 10 +#define RT5670_OT_P_NOR (0x0 << 10) +#define RT5670_OT_P_INV (0x1 << 10) +#define RT5670_JD1_1_EN_MASK (0x1 << 9) +#define RT5670_JD1_1_EN_SFT 9 +#define RT5670_JD1_1_DIS (0x0 << 9) +#define RT5670_JD1_1_EN (0x1 << 9) + +/* IRQ Control 2 (0xbe) */ +#define RT5670_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5670_IRQ_MB1_OC_SFT 15 +#define RT5670_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5670_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5670_IRQ_MB2_OC_MASK (0x1 << 14) +#define RT5670_IRQ_MB2_OC_SFT 14 +#define RT5670_IRQ_MB2_OC_BP (0x0 << 14) +#define RT5670_IRQ_MB2_OC_NOR (0x1 << 14) +#define RT5670_MB1_OC_STKY_MASK (0x1 << 11) +#define RT5670_MB1_OC_STKY_SFT 11 +#define RT5670_MB1_OC_STKY_DIS (0x0 << 11) +#define RT5670_MB1_OC_STKY_EN (0x1 << 11) +#define RT5670_MB2_OC_STKY_MASK (0x1 << 10) +#define RT5670_MB2_OC_STKY_SFT 10 +#define RT5670_MB2_OC_STKY_DIS (0x0 << 10) +#define RT5670_MB2_OC_STKY_EN (0x1 << 10) +#define RT5670_MB1_OC_P_MASK (0x1 << 7) +#define RT5670_MB1_OC_P_SFT 7 +#define RT5670_MB1_OC_P_NOR (0x0 << 7) +#define RT5670_MB1_OC_P_INV (0x1 << 7) +#define RT5670_MB2_OC_P_MASK (0x1 << 6) +#define RT5670_MB2_OC_P_SFT 6 +#define RT5670_MB2_OC_P_NOR (0x0 << 6) +#define RT5670_MB2_OC_P_INV (0x1 << 6) +#define RT5670_MB1_OC_CLR (0x1 << 3) +#define RT5670_MB1_OC_CLR_SFT 3 +#define RT5670_MB2_OC_CLR (0x1 << 2) +#define RT5670_MB2_OC_CLR_SFT 2 + +/* GPIO Control 1 (0xc0) */ +#define RT5670_GP1_PIN_MASK (0x1 << 15) +#define RT5670_GP1_PIN_SFT 15 +#define RT5670_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5670_GP1_PIN_IRQ (0x1 << 15) +#define RT5670_GP2_PIN_MASK (0x1 << 14) +#define RT5670_GP2_PIN_SFT 14 +#define RT5670_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5670_GP2_PIN_DMIC1_SCL (0x1 << 14) +#define RT5670_GP3_PIN_MASK (0x3 << 12) +#define RT5670_GP3_PIN_SFT 12 +#define RT5670_GP3_PIN_GPIO3 (0x0 << 12) +#define RT5670_GP3_PIN_DMIC1_SDA (0x1 << 12) +#define RT5670_GP3_PIN_IRQ (0x2 << 12) +#define RT5670_GP4_PIN_MASK (0x1 << 11) +#define RT5670_GP4_PIN_SFT 11 +#define RT5670_GP4_PIN_GPIO4 (0x0 << 11) +#define RT5670_GP4_PIN_DMIC2_SDA (0x1 << 11) +#define RT5670_DP_SIG_MASK (0x1 << 10) +#define RT5670_DP_SIG_SFT 10 +#define RT5670_DP_SIG_TEST (0x0 << 10) +#define RT5670_DP_SIG_AP (0x1 << 10) +#define RT5670_GPIO_M_MASK (0x1 << 9) +#define RT5670_GPIO_M_SFT 9 +#define RT5670_GPIO_M_FLT (0x0 << 9) +#define RT5670_GPIO_M_PH (0x1 << 9) +#define RT5670_I2S2_PIN_MASK (0x1 << 8) +#define RT5670_I2S2_PIN_SFT 8 +#define RT5670_I2S2_PIN_I2S (0x0 << 8) +#define RT5670_I2S2_PIN_GPIO (0x1 << 8) +#define RT5670_GP5_PIN_MASK (0x1 << 7) +#define RT5670_GP5_PIN_SFT 7 +#define RT5670_GP5_PIN_GPIO5 (0x0 << 7) +#define RT5670_GP5_PIN_DMIC3_SDA (0x1 << 7) +#define RT5670_GP6_PIN_MASK (0x1 << 6) +#define RT5670_GP6_PIN_SFT 6 +#define RT5670_GP6_PIN_GPIO6 (0x0 << 6) +#define RT5670_GP6_PIN_DMIC1_SDA (0x1 << 6) +#define RT5670_GP7_PIN_MASK (0x3 << 4) +#define RT5670_GP7_PIN_SFT 4 +#define RT5670_GP7_PIN_GPIO7 (0x0 << 4) +#define RT5670_GP7_PIN_DMIC1_SDA (0x1 << 4) +#define RT5670_GP7_PIN_PDM_SCL2 (0x2 << 4) +#define RT5670_GP8_PIN_MASK (0x1 << 3) +#define RT5670_GP8_PIN_SFT 3 +#define RT5670_GP8_PIN_GPIO8 (0x0 << 3) +#define RT5670_GP8_PIN_DMIC2_SDA (0x1 << 3) +#define RT5670_GP9_PIN_MASK (0x1 << 2) +#define RT5670_GP9_PIN_SFT 2 +#define RT5670_GP9_PIN_GPIO9 (0x0 << 2) +#define RT5670_GP9_PIN_DMIC3_SDA (0x1 << 2) +#define RT5670_GP10_PIN_MASK (0x3) +#define RT5670_GP10_PIN_SFT 0 +#define RT5670_GP10_PIN_GPIO9 (0x0) +#define RT5670_GP10_PIN_DMIC3_SDA (0x1) +#define RT5670_GP10_PIN_PDM_ADT2 (0x2) + +/* GPIO Control 2 (0xc1) */ +#define RT5670_GP4_PF_MASK (0x1 << 11) +#define RT5670_GP4_PF_SFT 11 +#define RT5670_GP4_PF_IN (0x0 << 11) +#define RT5670_GP4_PF_OUT (0x1 << 11) +#define RT5670_GP4_OUT_MASK (0x1 << 10) +#define RT5670_GP4_OUT_SFT 10 +#define RT5670_GP4_OUT_LO (0x0 << 10) +#define RT5670_GP4_OUT_HI (0x1 << 10) +#define RT5670_GP4_P_MASK (0x1 << 9) +#define RT5670_GP4_P_SFT 9 +#define RT5670_GP4_P_NOR (0x0 << 9) +#define RT5670_GP4_P_INV (0x1 << 9) +#define RT5670_GP3_PF_MASK (0x1 << 8) +#define RT5670_GP3_PF_SFT 8 +#define RT5670_GP3_PF_IN (0x0 << 8) +#define RT5670_GP3_PF_OUT (0x1 << 8) +#define RT5670_GP3_OUT_MASK (0x1 << 7) +#define RT5670_GP3_OUT_SFT 7 +#define RT5670_GP3_OUT_LO (0x0 << 7) +#define RT5670_GP3_OUT_HI (0x1 << 7) +#define RT5670_GP3_P_MASK (0x1 << 6) +#define RT5670_GP3_P_SFT 6 +#define RT5670_GP3_P_NOR (0x0 << 6) +#define RT5670_GP3_P_INV (0x1 << 6) +#define RT5670_GP2_PF_MASK (0x1 << 5) +#define RT5670_GP2_PF_SFT 5 +#define RT5670_GP2_PF_IN (0x0 << 5) +#define RT5670_GP2_PF_OUT (0x1 << 5) +#define RT5670_GP2_OUT_MASK (0x1 << 4) +#define RT5670_GP2_OUT_SFT 4 +#define RT5670_GP2_OUT_LO (0x0 << 4) +#define RT5670_GP2_OUT_HI (0x1 << 4) +#define RT5670_GP2_P_MASK (0x1 << 3) +#define RT5670_GP2_P_SFT 3 +#define RT5670_GP2_P_NOR (0x0 << 3) +#define RT5670_GP2_P_INV (0x1 << 3) +#define RT5670_GP1_PF_MASK (0x1 << 2) +#define RT5670_GP1_PF_SFT 2 +#define RT5670_GP1_PF_IN (0x0 << 2) +#define RT5670_GP1_PF_OUT (0x1 << 2) +#define RT5670_GP1_OUT_MASK (0x1 << 1) +#define RT5670_GP1_OUT_SFT 1 +#define RT5670_GP1_OUT_LO (0x0 << 1) +#define RT5670_GP1_OUT_HI (0x1 << 1) +#define RT5670_GP1_P_MASK (0x1) +#define RT5670_GP1_P_SFT 0 +#define RT5670_GP1_P_NOR (0x0) +#define RT5670_GP1_P_INV (0x1) + +/* Scramble Function (0xcd) */ +#define RT5670_SCB_KEY_MASK (0xff) +#define RT5670_SCB_KEY_SFT 0 + +/* Scramble Control (0xce) */ +#define RT5670_SCB_SWAP_MASK (0x1 << 15) +#define RT5670_SCB_SWAP_SFT 15 +#define RT5670_SCB_SWAP_DIS (0x0 << 15) +#define RT5670_SCB_SWAP_EN (0x1 << 15) +#define RT5670_SCB_MASK (0x1 << 14) +#define RT5670_SCB_SFT 14 +#define RT5670_SCB_DIS (0x0 << 14) +#define RT5670_SCB_EN (0x1 << 14) + +/* Baseback Control (0xcf) */ +#define RT5670_BB_MASK (0x1 << 15) +#define RT5670_BB_SFT 15 +#define RT5670_BB_DIS (0x0 << 15) +#define RT5670_BB_EN (0x1 << 15) +#define RT5670_BB_CT_MASK (0x7 << 12) +#define RT5670_BB_CT_SFT 12 +#define RT5670_BB_CT_A (0x0 << 12) +#define RT5670_BB_CT_B (0x1 << 12) +#define RT5670_BB_CT_C (0x2 << 12) +#define RT5670_BB_CT_D (0x3 << 12) +#define RT5670_M_BB_L_MASK (0x1 << 9) +#define RT5670_M_BB_L_SFT 9 +#define RT5670_M_BB_R_MASK (0x1 << 8) +#define RT5670_M_BB_R_SFT 8 +#define RT5670_M_BB_HPF_L_MASK (0x1 << 7) +#define RT5670_M_BB_HPF_L_SFT 7 +#define RT5670_M_BB_HPF_R_MASK (0x1 << 6) +#define RT5670_M_BB_HPF_R_SFT 6 +#define RT5670_G_BB_BST_MASK (0x3f) +#define RT5670_G_BB_BST_SFT 0 + +/* MP3 Plus Control 1 (0xd0) */ +#define RT5670_M_MP3_L_MASK (0x1 << 15) +#define RT5670_M_MP3_L_SFT 15 +#define RT5670_M_MP3_R_MASK (0x1 << 14) +#define RT5670_M_MP3_R_SFT 14 +#define RT5670_M_MP3_MASK (0x1 << 13) +#define RT5670_M_MP3_SFT 13 +#define RT5670_M_MP3_DIS (0x0 << 13) +#define RT5670_M_MP3_EN (0x1 << 13) +#define RT5670_EG_MP3_MASK (0x1f << 8) +#define RT5670_EG_MP3_SFT 8 +#define RT5670_MP3_HLP_MASK (0x1 << 7) +#define RT5670_MP3_HLP_SFT 7 +#define RT5670_MP3_HLP_DIS (0x0 << 7) +#define RT5670_MP3_HLP_EN (0x1 << 7) +#define RT5670_M_MP3_ORG_L_MASK (0x1 << 6) +#define RT5670_M_MP3_ORG_L_SFT 6 +#define RT5670_M_MP3_ORG_R_MASK (0x1 << 5) +#define RT5670_M_MP3_ORG_R_SFT 5 + +/* MP3 Plus Control 2 (0xd1) */ +#define RT5670_MP3_WT_MASK (0x1 << 13) +#define RT5670_MP3_WT_SFT 13 +#define RT5670_MP3_WT_1_4 (0x0 << 13) +#define RT5670_MP3_WT_1_2 (0x1 << 13) +#define RT5670_OG_MP3_MASK (0x1f << 8) +#define RT5670_OG_MP3_SFT 8 +#define RT5670_HG_MP3_MASK (0x3f) +#define RT5670_HG_MP3_SFT 0 + +/* 3D HP Control 1 (0xd2) */ +#define RT5670_3D_CF_MASK (0x1 << 15) +#define RT5670_3D_CF_SFT 15 +#define RT5670_3D_CF_DIS (0x0 << 15) +#define RT5670_3D_CF_EN (0x1 << 15) +#define RT5670_3D_HP_MASK (0x1 << 14) +#define RT5670_3D_HP_SFT 14 +#define RT5670_3D_HP_DIS (0x0 << 14) +#define RT5670_3D_HP_EN (0x1 << 14) +#define RT5670_3D_BT_MASK (0x1 << 13) +#define RT5670_3D_BT_SFT 13 +#define RT5670_3D_BT_DIS (0x0 << 13) +#define RT5670_3D_BT_EN (0x1 << 13) +#define RT5670_3D_1F_MIX_MASK (0x3 << 11) +#define RT5670_3D_1F_MIX_SFT 11 +#define RT5670_3D_HP_M_MASK (0x1 << 10) +#define RT5670_3D_HP_M_SFT 10 +#define RT5670_3D_HP_M_SUR (0x0 << 10) +#define RT5670_3D_HP_M_FRO (0x1 << 10) +#define RT5670_M_3D_HRTF_MASK (0x1 << 9) +#define RT5670_M_3D_HRTF_SFT 9 +#define RT5670_M_3D_D2H_MASK (0x1 << 8) +#define RT5670_M_3D_D2H_SFT 8 +#define RT5670_M_3D_D2R_MASK (0x1 << 7) +#define RT5670_M_3D_D2R_SFT 7 +#define RT5670_M_3D_REVB_MASK (0x1 << 6) +#define RT5670_M_3D_REVB_SFT 6 + +/* Adjustable high pass filter control 1 (0xd3) */ +#define RT5670_2ND_HPF_MASK (0x1 << 15) +#define RT5670_2ND_HPF_SFT 15 +#define RT5670_2ND_HPF_DIS (0x0 << 15) +#define RT5670_2ND_HPF_EN (0x1 << 15) +#define RT5670_HPF_CF_L_MASK (0x7 << 12) +#define RT5670_HPF_CF_L_SFT 12 +#define RT5670_1ST_HPF_MASK (0x1 << 11) +#define RT5670_1ST_HPF_SFT 11 +#define RT5670_1ST_HPF_DIS (0x0 << 11) +#define RT5670_1ST_HPF_EN (0x1 << 11) +#define RT5670_HPF_CF_R_MASK (0x7 << 8) +#define RT5670_HPF_CF_R_SFT 8 +#define RT5670_ZD_T_MASK (0x3 << 6) +#define RT5670_ZD_T_SFT 6 +#define RT5670_ZD_F_MASK (0x3 << 4) +#define RT5670_ZD_F_SFT 4 +#define RT5670_ZD_F_IM (0x0 << 4) +#define RT5670_ZD_F_ZC_IM (0x1 << 4) +#define RT5670_ZD_F_ZC_IOD (0x2 << 4) +#define RT5670_ZD_F_UN (0x3 << 4) + +/* HP calibration control and Amp detection (0xd6) */ +#define RT5670_SI_DAC_MASK (0x1 << 11) +#define RT5670_SI_DAC_SFT 11 +#define RT5670_SI_DAC_AUTO (0x0 << 11) +#define RT5670_SI_DAC_TEST (0x1 << 11) +#define RT5670_DC_CAL_M_MASK (0x1 << 10) +#define RT5670_DC_CAL_M_SFT 10 +#define RT5670_DC_CAL_M_CAL (0x0 << 10) +#define RT5670_DC_CAL_M_NOR (0x1 << 10) +#define RT5670_DC_CAL_MASK (0x1 << 9) +#define RT5670_DC_CAL_SFT 9 +#define RT5670_DC_CAL_DIS (0x0 << 9) +#define RT5670_DC_CAL_EN (0x1 << 9) +#define RT5670_HPD_RCV_MASK (0x7 << 6) +#define RT5670_HPD_RCV_SFT 6 +#define RT5670_HPD_PS_MASK (0x1 << 5) +#define RT5670_HPD_PS_SFT 5 +#define RT5670_HPD_PS_DIS (0x0 << 5) +#define RT5670_HPD_PS_EN (0x1 << 5) +#define RT5670_CAL_M_MASK (0x1 << 4) +#define RT5670_CAL_M_SFT 4 +#define RT5670_CAL_M_DEP (0x0 << 4) +#define RT5670_CAL_M_CAL (0x1 << 4) +#define RT5670_CAL_MASK (0x1 << 3) +#define RT5670_CAL_SFT 3 +#define RT5670_CAL_DIS (0x0 << 3) +#define RT5670_CAL_EN (0x1 << 3) +#define RT5670_CAL_TEST_MASK (0x1 << 2) +#define RT5670_CAL_TEST_SFT 2 +#define RT5670_CAL_TEST_DIS (0x0 << 2) +#define RT5670_CAL_TEST_EN (0x1 << 2) +#define RT5670_CAL_P_MASK (0x3) +#define RT5670_CAL_P_SFT 0 +#define RT5670_CAL_P_NONE (0x0) +#define RT5670_CAL_P_CAL (0x1) +#define RT5670_CAL_P_DAC_CAL (0x2) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5670_SV_MASK (0x1 << 15) +#define RT5670_SV_SFT 15 +#define RT5670_SV_DIS (0x0 << 15) +#define RT5670_SV_EN (0x1 << 15) +#define RT5670_SPO_SV_MASK (0x1 << 14) +#define RT5670_SPO_SV_SFT 14 +#define RT5670_SPO_SV_DIS (0x0 << 14) +#define RT5670_SPO_SV_EN (0x1 << 14) +#define RT5670_OUT_SV_MASK (0x1 << 13) +#define RT5670_OUT_SV_SFT 13 +#define RT5670_OUT_SV_DIS (0x0 << 13) +#define RT5670_OUT_SV_EN (0x1 << 13) +#define RT5670_HP_SV_MASK (0x1 << 12) +#define RT5670_HP_SV_SFT 12 +#define RT5670_HP_SV_DIS (0x0 << 12) +#define RT5670_HP_SV_EN (0x1 << 12) +#define RT5670_ZCD_DIG_MASK (0x1 << 11) +#define RT5670_ZCD_DIG_SFT 11 +#define RT5670_ZCD_DIG_DIS (0x0 << 11) +#define RT5670_ZCD_DIG_EN (0x1 << 11) +#define RT5670_ZCD_MASK (0x1 << 10) +#define RT5670_ZCD_SFT 10 +#define RT5670_ZCD_PD (0x0 << 10) +#define RT5670_ZCD_PU (0x1 << 10) +#define RT5670_M_ZCD_MASK (0x3f << 4) +#define RT5670_M_ZCD_SFT 4 +#define RT5670_M_ZCD_RM_L (0x1 << 9) +#define RT5670_M_ZCD_RM_R (0x1 << 8) +#define RT5670_M_ZCD_SM_L (0x1 << 7) +#define RT5670_M_ZCD_SM_R (0x1 << 6) +#define RT5670_M_ZCD_OM_L (0x1 << 5) +#define RT5670_M_ZCD_OM_R (0x1 << 4) +#define RT5670_SV_DLY_MASK (0xf) +#define RT5670_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5670_ZCD_HP_MASK (0x1 << 15) +#define RT5670_ZCD_HP_SFT 15 +#define RT5670_ZCD_HP_DIS (0x0 << 15) +#define RT5670_ZCD_HP_EN (0x1 << 15) + +/* General Control 3 (0xfc) */ +#define RT5670_TDM_DATA_MODE_SEL (0x1 << 11) +#define RT5670_TDM_DATA_MODE_NOR (0x0 << 11) +#define RT5670_TDM_DATA_MODE_50FS (0x1 << 11) + +/* Codec Private Register definition */ +/* 3D Speaker Control (0x63) */ +#define RT5670_3D_SPK_MASK (0x1 << 15) +#define RT5670_3D_SPK_SFT 15 +#define RT5670_3D_SPK_DIS (0x0 << 15) +#define RT5670_3D_SPK_EN (0x1 << 15) +#define RT5670_3D_SPK_M_MASK (0x3 << 13) +#define RT5670_3D_SPK_M_SFT 13 +#define RT5670_3D_SPK_CG_MASK (0x1f << 8) +#define RT5670_3D_SPK_CG_SFT 8 +#define RT5670_3D_SPK_SG_MASK (0x1f) +#define RT5670_3D_SPK_SG_SFT 0 + +/* Wind Noise Detection Control 1 (0x6c) */ +#define RT5670_WND_MASK (0x1 << 15) +#define RT5670_WND_SFT 15 +#define RT5670_WND_DIS (0x0 << 15) +#define RT5670_WND_EN (0x1 << 15) + +/* Wind Noise Detection Control 2 (0x6d) */ +#define RT5670_WND_FC_NW_MASK (0x3f << 10) +#define RT5670_WND_FC_NW_SFT 10 +#define RT5670_WND_FC_WK_MASK (0x3f << 4) +#define RT5670_WND_FC_WK_SFT 4 + +/* Wind Noise Detection Control 3 (0x6e) */ +#define RT5670_HPF_FC_MASK (0x3f << 6) +#define RT5670_HPF_FC_SFT 6 +#define RT5670_WND_FC_ST_MASK (0x3f) +#define RT5670_WND_FC_ST_SFT 0 + +/* Wind Noise Detection Control 4 (0x6f) */ +#define RT5670_WND_TH_LO_MASK (0x3ff) +#define RT5670_WND_TH_LO_SFT 0 + +/* Wind Noise Detection Control 5 (0x70) */ +#define RT5670_WND_TH_HI_MASK (0x3ff) +#define RT5670_WND_TH_HI_SFT 0 + +/* Wind Noise Detection Control 8 (0x73) */ +#define RT5670_WND_WIND_MASK (0x1 << 13) /* Read-Only */ +#define RT5670_WND_WIND_SFT 13 +#define RT5670_WND_STRONG_MASK (0x1 << 12) /* Read-Only */ +#define RT5670_WND_STRONG_SFT 12 +enum { + RT5670_NO_WIND, + RT5670_BREEZE, + RT5670_STORM, +}; + +/* Dipole Speaker Interface (0x75) */ +#define RT5670_DP_ATT_MASK (0x3 << 14) +#define RT5670_DP_ATT_SFT 14 +#define RT5670_DP_SPK_MASK (0x1 << 10) +#define RT5670_DP_SPK_SFT 10 +#define RT5670_DP_SPK_DIS (0x0 << 10) +#define RT5670_DP_SPK_EN (0x1 << 10) + +/* EQ Pre Volume Control (0xb3) */ +#define RT5670_EQ_PRE_VOL_MASK (0xffff) +#define RT5670_EQ_PRE_VOL_SFT 0 + +/* EQ Post Volume Control (0xb4) */ +#define RT5670_EQ_PST_VOL_MASK (0xffff) +#define RT5670_EQ_PST_VOL_SFT 0 + +/* Jack Detect Control 3 (0xf8) */ +#define RT5670_CMP_MIC_IN_DET_MASK (0x7 << 12) +#define RT5670_JD_CBJ_EN (0x1 << 7) +#define RT5670_JD_CBJ_POL (0x1 << 6) +#define RT5670_JD_TRI_CBJ_SEL_MASK (0x7 << 3) +#define RT5670_JD_TRI_CBJ_SEL_SFT (3) +#define RT5670_JD_CBJ_GPIO_JD1 (0x0 << 3) +#define RT5670_JD_CBJ_JD1_1 (0x1 << 3) +#define RT5670_JD_CBJ_JD1_2 (0x2 << 3) +#define RT5670_JD_CBJ_JD2 (0x3 << 3) +#define RT5670_JD_CBJ_JD3 (0x4 << 3) +#define RT5670_JD_CBJ_GPIO_JD2 (0x5 << 3) +#define RT5670_JD_CBJ_MX0B_12 (0x6 << 3) +#define RT5670_JD_TRI_HPO_SEL_MASK (0x7 << 3) +#define RT5670_JD_TRI_HPO_SEL_SFT (0) +#define RT5670_JD_HPO_GPIO_JD1 (0x0) +#define RT5670_JD_HPO_JD1_1 (0x1) +#define RT5670_JD_HPO_JD1_2 (0x2) +#define RT5670_JD_HPO_JD2 (0x3) +#define RT5670_JD_HPO_JD3 (0x4) +#define RT5670_JD_HPO_GPIO_JD2 (0x5) +#define RT5670_JD_HPO_MX0B_12 (0x6) + +/* Digital Misc Control (0xfa) */ +#define RT5670_RST_DSP (0x1 << 13) +#define RT5670_IF1_ADC1_IN1_SEL (0x1 << 12) +#define RT5670_IF1_ADC1_IN1_SFT 12 +#define RT5670_IF1_ADC1_IN2_SEL (0x1 << 11) +#define RT5670_IF1_ADC1_IN2_SFT 11 +#define RT5670_IF1_ADC2_IN1_SEL (0x1 << 10) +#define RT5670_IF1_ADC2_IN1_SFT 10 +#define RT5670_MCLK_DET (0x1 << 3) + +/* General Control2 (0xfb) */ +#define RT5670_RXDC_SRC_MASK (0x1 << 7) +#define RT5670_RXDC_SRC_STO (0x0 << 7) +#define RT5670_RXDC_SRC_MONO (0x1 << 7) +#define RT5670_RXDC_SRC_SFT (7) +#define RT5670_RXDP2_SEL_MASK (0x1 << 3) +#define RT5670_RXDP2_SEL_IF2 (0x0 << 3) +#define RT5670_RXDP2_SEL_ADC (0x1 << 3) +#define RT5670_RXDP2_SEL_SFT (3) + +/* System Clock Source */ +enum { + RT5670_SCLK_S_MCLK, + RT5670_SCLK_S_PLL1, + RT5670_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5670_PLL1_S_MCLK, + RT5670_PLL1_S_BCLK1, + RT5670_PLL1_S_BCLK2, + RT5670_PLL1_S_BCLK3, + RT5670_PLL1_S_BCLK4, +}; + +enum { + RT5670_AIF1, + RT5670_AIF2, + RT5670_AIF3, + RT5670_AIF4, + RT5670_AIFS, +}; + +enum { + RT5670_DMIC1_DISABLED, + RT5670_DMIC_DATA_GPIO6, + RT5670_DMIC_DATA_IN2P, + RT5670_DMIC_DATA_GPIO7, +}; + +enum { + RT5670_DMIC2_DISABLED, + RT5670_DMIC_DATA_GPIO8, + RT5670_DMIC_DATA_IN3N, +}; + +enum { + RT5670_DMIC3_DISABLED, + RT5670_DMIC_DATA_GPIO9, + RT5670_DMIC_DATA_GPIO10, + RT5670_DMIC_DATA_GPIO5, +}; + +/* filter mask */ +enum { + RT5670_DA_STEREO_FILTER = 0x1, + RT5670_DA_MONO_L_FILTER = (0x1 << 1), + RT5670_DA_MONO_R_FILTER = (0x1 << 2), + RT5670_AD_STEREO_FILTER = (0x1 << 3), + RT5670_AD_MONO_L_FILTER = (0x1 << 4), + RT5670_AD_MONO_R_FILTER = (0x1 << 5), + RT5670_UP_RATE_FILTER = (0x1 << 6), + RT5670_DOWN_RATE_FILTER = (0x1 << 7), +}; + +int rt5670_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +struct rt5670_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct snd_soc_jack *jack; + struct snd_soc_jack_gpio hp_gpio; + + int jd_mode; + bool in2_diff; + bool gpio1_is_irq; + bool gpio1_is_ext_spk_en; + + bool dmic_en; + unsigned int dmic1_data_pin; + /* 0 = GPIO6; 1 = IN2P; 3 = GPIO7*/ + unsigned int dmic2_data_pin; + /* 0 = GPIO8; 1 = IN3N; */ + unsigned int dmic3_data_pin; + /* 0 = GPIO9; 1 = GPIO10; 2 = GPIO5*/ + + int sysclk; + int sysclk_src; + int lrck[RT5670_AIFS]; + int bclk[RT5670_AIFS]; + int master[RT5670_AIFS]; + + int pll_src; + int pll_in; + int pll_out; + + int dsp_sw; /* expected parameter setting */ + int dsp_rate; + int jack_type; + int jack_type_saved; + + bool dac1_mixl_dac1_switch; + bool dac1_mixr_dac1_switch; + bool dac1_playback_switch_l; + bool dac1_playback_switch_r; +}; + +void rt5670_jack_suspend(struct snd_soc_component *component); +void rt5670_jack_resume(struct snd_soc_component *component); +int rt5670_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack); +const char *rt5670_components(void); + +#endif /* __RT5670_H__ */ diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c new file mode 100644 index 0000000000..d25703dd74 --- /dev/null +++ b/sound/soc/codecs/rt5677-spi.c @@ -0,0 +1,637 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5677-spi.c -- RT5677 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rt5677.h" +#include "rt5677-spi.h" + +#define DRV_NAME "rt5677spi" + +#define RT5677_SPI_BURST_LEN 240 +#define RT5677_SPI_HEADER 5 +#define RT5677_SPI_FREQ 6000000 + +/* The AddressPhase and DataPhase of SPI commands are MSB first on the wire. + * DataPhase word size of 16-bit commands is 2 bytes. + * DataPhase word size of 32-bit commands is 4 bytes. + * DataPhase word size of burst commands is 8 bytes. + * The DSP CPU is little-endian. + */ +#define RT5677_SPI_WRITE_BURST 0x5 +#define RT5677_SPI_READ_BURST 0x4 +#define RT5677_SPI_WRITE_32 0x3 +#define RT5677_SPI_READ_32 0x2 +#define RT5677_SPI_WRITE_16 0x1 +#define RT5677_SPI_READ_16 0x0 + +#define RT5677_BUF_BYTES_TOTAL 0x20000 +#define RT5677_MIC_BUF_ADDR 0x60030000 +#define RT5677_MODEL_ADDR 0x5FFC9800 +#define RT5677_MIC_BUF_BYTES ((u32)(RT5677_BUF_BYTES_TOTAL - \ + sizeof(u32))) +#define RT5677_MIC_BUF_FIRST_READ_SIZE 0x10000 + +static struct spi_device *g_spi; +static DEFINE_MUTEX(spi_mutex); + +struct rt5677_dsp { + struct device *dev; + struct delayed_work copy_work; + struct mutex dma_lock; + struct snd_pcm_substream *substream; + size_t dma_offset; /* zero-based offset into runtime->dma_area */ + size_t avail_bytes; /* number of new bytes since last period */ + u32 mic_read_offset; /* zero-based offset into DSP's mic buffer */ + bool new_hotword; /* a new hotword is fired */ +}; + +static const struct snd_pcm_hardware rt5677_spi_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .period_bytes_min = PAGE_SIZE, + .period_bytes_max = RT5677_BUF_BYTES_TOTAL / 8, + .periods_min = 8, + .periods_max = 8, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = RT5677_BUF_BYTES_TOTAL, +}; + +static struct snd_soc_dai_driver rt5677_spi_dai = { + /* The DAI name "rt5677-dsp-cpu-dai" is not used. The actual DAI name + * registered with ASoC is the name of the device "spi-RT5677AA:00", + * because we only have one DAI. See snd_soc_register_dais(). + */ + .name = "rt5677-dsp-cpu-dai", + .id = 0, + .capture = { + .stream_name = "DSP Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +/* PCM for streaming audio from the DSP buffer */ +static int rt5677_spi_pcm_open( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + snd_soc_set_runtime_hwparams(substream, &rt5677_spi_pcm_hardware); + return 0; +} + +static int rt5677_spi_pcm_close( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_component *codec_component = + snd_soc_rtdcom_lookup(rtd, "rt5677"); + struct rt5677_priv *rt5677 = + snd_soc_component_get_drvdata(codec_component); + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&rt5677_dsp->copy_work); + rt5677->set_dsp_vad(codec_component, false); + return 0; +} + +static int rt5677_spi_hw_params( + struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + + mutex_lock(&rt5677_dsp->dma_lock); + rt5677_dsp->substream = substream; + mutex_unlock(&rt5677_dsp->dma_lock); + + return 0; +} + +static int rt5677_spi_hw_free( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + + mutex_lock(&rt5677_dsp->dma_lock); + rt5677_dsp->substream = NULL; + mutex_unlock(&rt5677_dsp->dma_lock); + + return 0; +} + +static int rt5677_spi_prepare( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_component *rt5677_component = + snd_soc_rtdcom_lookup(rtd, "rt5677"); + struct rt5677_priv *rt5677 = + snd_soc_component_get_drvdata(rt5677_component); + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + + rt5677->set_dsp_vad(rt5677_component, true); + rt5677_dsp->dma_offset = 0; + rt5677_dsp->avail_bytes = 0; + return 0; +} + +static snd_pcm_uframes_t rt5677_spi_pcm_pointer( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct rt5677_dsp *rt5677_dsp = + snd_soc_component_get_drvdata(component); + + return bytes_to_frames(runtime, rt5677_dsp->dma_offset); +} + +static int rt5677_spi_mic_write_offset(u32 *mic_write_offset) +{ + int ret; + /* Grab the first 4 bytes that hold the write pointer on the + * dsp, and check to make sure that it points somewhere inside the + * buffer. + */ + ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR, mic_write_offset, + sizeof(u32)); + if (ret) + return ret; + /* Adjust the offset so that it's zero-based */ + *mic_write_offset = *mic_write_offset - sizeof(u32); + return *mic_write_offset < RT5677_MIC_BUF_BYTES ? 0 : -EFAULT; +} + +/* + * Copy one contiguous block of audio samples from the DSP mic buffer to the + * dma_area of the pcm runtime. The receiving buffer may wrap around. + * @begin: start offset of the block to copy, in bytes. + * @end: offset of the first byte after the block to copy, must be greater + * than or equal to begin. + * + * Return: Zero if successful, or a negative error code on failure. + */ +static int rt5677_spi_copy_block(struct rt5677_dsp *rt5677_dsp, + u32 begin, u32 end) +{ + struct snd_pcm_runtime *runtime = rt5677_dsp->substream->runtime; + size_t bytes_per_frame = frames_to_bytes(runtime, 1); + size_t first_chunk_len, second_chunk_len; + int ret; + + if (begin > end || runtime->dma_bytes < 2 * bytes_per_frame) { + dev_err(rt5677_dsp->dev, + "Invalid copy from (%u, %u), dma_area size %zu\n", + begin, end, runtime->dma_bytes); + return -EINVAL; + } + + /* The block to copy is empty */ + if (begin == end) + return 0; + + /* If the incoming chunk is too big for the receiving buffer, only the + * last "receiving buffer size - one frame" bytes are copied. + */ + if (end - begin > runtime->dma_bytes - bytes_per_frame) + begin = end - (runtime->dma_bytes - bytes_per_frame); + + /* May need to split to two chunks, calculate the size of each */ + first_chunk_len = end - begin; + second_chunk_len = 0; + if (rt5677_dsp->dma_offset + first_chunk_len > runtime->dma_bytes) { + /* Receiving buffer wrapped around */ + second_chunk_len = first_chunk_len; + first_chunk_len = runtime->dma_bytes - rt5677_dsp->dma_offset; + second_chunk_len -= first_chunk_len; + } + + /* Copy first chunk */ + ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + begin, + runtime->dma_area + rt5677_dsp->dma_offset, + first_chunk_len); + if (ret) + return ret; + rt5677_dsp->dma_offset += first_chunk_len; + if (rt5677_dsp->dma_offset == runtime->dma_bytes) + rt5677_dsp->dma_offset = 0; + + /* Copy second chunk */ + if (second_chunk_len) { + ret = rt5677_spi_read(RT5677_MIC_BUF_ADDR + sizeof(u32) + + begin + first_chunk_len, runtime->dma_area, + second_chunk_len); + if (!ret) + rt5677_dsp->dma_offset = second_chunk_len; + } + return ret; +} + +/* + * Copy a given amount of audio samples from the DSP mic buffer starting at + * mic_read_offset, to the dma_area of the pcm runtime. The source buffer may + * wrap around. mic_read_offset is updated after successful copy. + * @amount: amount of samples to copy, in bytes. + * + * Return: Zero if successful, or a negative error code on failure. + */ +static int rt5677_spi_copy(struct rt5677_dsp *rt5677_dsp, u32 amount) +{ + int ret = 0; + u32 target; + + if (amount == 0) + return ret; + + target = rt5677_dsp->mic_read_offset + amount; + /* Copy the first chunk in DSP's mic buffer */ + ret |= rt5677_spi_copy_block(rt5677_dsp, rt5677_dsp->mic_read_offset, + min(target, RT5677_MIC_BUF_BYTES)); + + if (target >= RT5677_MIC_BUF_BYTES) { + /* Wrap around, copy the second chunk */ + target -= RT5677_MIC_BUF_BYTES; + ret |= rt5677_spi_copy_block(rt5677_dsp, 0, target); + } + + if (!ret) + rt5677_dsp->mic_read_offset = target; + return ret; +} + +/* + * A delayed work that streams audio samples from the DSP mic buffer to the + * dma_area of the pcm runtime via SPI. + */ +static void rt5677_spi_copy_work(struct work_struct *work) +{ + struct rt5677_dsp *rt5677_dsp = + container_of(work, struct rt5677_dsp, copy_work.work); + struct snd_pcm_runtime *runtime; + u32 mic_write_offset; + size_t new_bytes, copy_bytes, period_bytes; + unsigned int delay; + int ret = 0; + + /* Ensure runtime->dma_area buffer does not go away while copying. */ + mutex_lock(&rt5677_dsp->dma_lock); + if (!rt5677_dsp->substream) { + dev_err(rt5677_dsp->dev, "No pcm substream\n"); + goto done; + } + + runtime = rt5677_dsp->substream->runtime; + + if (rt5677_spi_mic_write_offset(&mic_write_offset)) { + dev_err(rt5677_dsp->dev, "No mic_write_offset\n"); + goto done; + } + + /* If this is the first time that we've asked for streaming data after + * a hotword is fired, we should start reading from the previous 2 + * seconds of audio from wherever the mic_write_offset is currently. + */ + if (rt5677_dsp->new_hotword) { + rt5677_dsp->new_hotword = false; + /* See if buffer wraparound happens */ + if (mic_write_offset < RT5677_MIC_BUF_FIRST_READ_SIZE) + rt5677_dsp->mic_read_offset = RT5677_MIC_BUF_BYTES - + (RT5677_MIC_BUF_FIRST_READ_SIZE - + mic_write_offset); + else + rt5677_dsp->mic_read_offset = mic_write_offset - + RT5677_MIC_BUF_FIRST_READ_SIZE; + } + + /* Calculate the amount of new samples in bytes */ + if (rt5677_dsp->mic_read_offset <= mic_write_offset) + new_bytes = mic_write_offset - rt5677_dsp->mic_read_offset; + else + new_bytes = RT5677_MIC_BUF_BYTES + mic_write_offset + - rt5677_dsp->mic_read_offset; + + /* Copy all new samples from DSP mic buffer, one period at a time */ + period_bytes = snd_pcm_lib_period_bytes(rt5677_dsp->substream); + while (new_bytes) { + copy_bytes = min(new_bytes, period_bytes + - rt5677_dsp->avail_bytes); + ret = rt5677_spi_copy(rt5677_dsp, copy_bytes); + if (ret) { + dev_err(rt5677_dsp->dev, "Copy failed %d\n", ret); + goto done; + } + rt5677_dsp->avail_bytes += copy_bytes; + if (rt5677_dsp->avail_bytes >= period_bytes) { + snd_pcm_period_elapsed(rt5677_dsp->substream); + rt5677_dsp->avail_bytes = 0; + } + new_bytes -= copy_bytes; + } + + delay = bytes_to_frames(runtime, period_bytes) / (runtime->rate / 1000); + schedule_delayed_work(&rt5677_dsp->copy_work, msecs_to_jiffies(delay)); +done: + mutex_unlock(&rt5677_dsp->dma_lock); +} + +static int rt5677_spi_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_VMALLOC, + NULL, 0, 0); + return 0; +} + +static int rt5677_spi_pcm_probe(struct snd_soc_component *component) +{ + struct rt5677_dsp *rt5677_dsp; + + rt5677_dsp = devm_kzalloc(component->dev, sizeof(*rt5677_dsp), + GFP_KERNEL); + if (!rt5677_dsp) + return -ENOMEM; + rt5677_dsp->dev = &g_spi->dev; + mutex_init(&rt5677_dsp->dma_lock); + INIT_DELAYED_WORK(&rt5677_dsp->copy_work, rt5677_spi_copy_work); + + snd_soc_component_set_drvdata(component, rt5677_dsp); + return 0; +} + +static const struct snd_soc_component_driver rt5677_spi_dai_component = { + .name = DRV_NAME, + .probe = rt5677_spi_pcm_probe, + .open = rt5677_spi_pcm_open, + .close = rt5677_spi_pcm_close, + .hw_params = rt5677_spi_hw_params, + .hw_free = rt5677_spi_hw_free, + .prepare = rt5677_spi_prepare, + .pointer = rt5677_spi_pcm_pointer, + .pcm_construct = rt5677_spi_pcm_new, + .legacy_dai_naming = 1, +}; + +/* Select a suitable transfer command for the next transfer to ensure + * the transfer address is always naturally aligned while minimizing + * the total number of transfers required. + * + * 3 transfer commands are available: + * RT5677_SPI_READ/WRITE_16: Transfer 2 bytes + * RT5677_SPI_READ/WRITE_32: Transfer 4 bytes + * RT5677_SPI_READ/WRITE_BURST: Transfer any multiples of 8 bytes + * + * Note: + * 16 Bit writes and reads are restricted to the address range + * 0x18020000 ~ 0x18021000 + * + * For example, reading 256 bytes at 0x60030004 uses the following commands: + * 0x60030004 RT5677_SPI_READ_32 4 bytes + * 0x60030008 RT5677_SPI_READ_BURST 240 bytes + * 0x600300F8 RT5677_SPI_READ_BURST 8 bytes + * 0x60030100 RT5677_SPI_READ_32 4 bytes + * + * Input: + * @read: true for read commands; false for write commands + * @align: alignment of the next transfer address + * @remain: number of bytes remaining to transfer + * + * Output: + * @len: number of bytes to transfer with the selected command + * Returns the selected command + */ +static u8 rt5677_spi_select_cmd(bool read, u32 align, u32 remain, u32 *len) +{ + u8 cmd; + + if (align == 4 || remain <= 4) { + cmd = RT5677_SPI_READ_32; + *len = 4; + } else { + cmd = RT5677_SPI_READ_BURST; + *len = (((remain - 1) >> 3) + 1) << 3; + *len = min_t(u32, *len, RT5677_SPI_BURST_LEN); + } + return read ? cmd : cmd + 1; +} + +/* Copy dstlen bytes from src to dst, while reversing byte order for each word. + * If srclen < dstlen, zeros are padded. + */ +static void rt5677_spi_reverse(u8 *dst, u32 dstlen, const u8 *src, u32 srclen) +{ + u32 w, i, si; + u32 word_size = min_t(u32, dstlen, 8); + + for (w = 0; w < dstlen; w += word_size) { + for (i = 0; i < word_size && i + w < dstlen; i++) { + si = w + word_size - i - 1; + dst[w + i] = si < srclen ? src[si] : 0; + } + } +} + +/* Read DSP address space using SPI. addr and len have to be 4-byte aligned. */ +int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) +{ + u32 offset; + int status = 0; + struct spi_transfer t[2]; + struct spi_message m; + /* +4 bytes is for the DummyPhase following the AddressPhase */ + u8 header[RT5677_SPI_HEADER + 4]; + u8 body[RT5677_SPI_BURST_LEN]; + u8 spi_cmd; + u8 *cb = rxbuf; + + if (!g_spi) + return -ENODEV; + + if ((addr & 3) || (len & 3)) { + dev_err(&g_spi->dev, "Bad read align 0x%x(%zu)\n", addr, len); + return -EACCES; + } + + memset(t, 0, sizeof(t)); + t[0].tx_buf = header; + t[0].len = sizeof(header); + t[0].speed_hz = RT5677_SPI_FREQ; + t[1].rx_buf = body; + t[1].speed_hz = RT5677_SPI_FREQ; + spi_message_init_with_transfers(&m, t, ARRAY_SIZE(t)); + + for (offset = 0; offset < len; offset += t[1].len) { + spi_cmd = rt5677_spi_select_cmd(true, (addr + offset) & 7, + len - offset, &t[1].len); + + /* Construct SPI message header */ + header[0] = spi_cmd; + header[1] = ((addr + offset) & 0xff000000) >> 24; + header[2] = ((addr + offset) & 0x00ff0000) >> 16; + header[3] = ((addr + offset) & 0x0000ff00) >> 8; + header[4] = ((addr + offset) & 0x000000ff) >> 0; + + mutex_lock(&spi_mutex); + status |= spi_sync(g_spi, &m); + mutex_unlock(&spi_mutex); + + + /* Copy data back to caller buffer */ + rt5677_spi_reverse(cb + offset, len - offset, body, t[1].len); + } + return status; +} +EXPORT_SYMBOL_GPL(rt5677_spi_read); + +/* Write DSP address space using SPI. addr has to be 4-byte aligned. + * If len is not 4-byte aligned, then extra zeros are written at the end + * as padding. + */ +int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) +{ + u32 offset; + int status = 0; + struct spi_transfer t; + struct spi_message m; + /* +1 byte is for the DummyPhase following the DataPhase */ + u8 buf[RT5677_SPI_HEADER + RT5677_SPI_BURST_LEN + 1]; + u8 *body = buf + RT5677_SPI_HEADER; + u8 spi_cmd; + const u8 *cb = txbuf; + + if (!g_spi) + return -ENODEV; + + if (addr & 3) { + dev_err(&g_spi->dev, "Bad write align 0x%x(%zu)\n", addr, len); + return -EACCES; + } + + memset(&t, 0, sizeof(t)); + t.tx_buf = buf; + t.speed_hz = RT5677_SPI_FREQ; + spi_message_init_with_transfers(&m, &t, 1); + + for (offset = 0; offset < len;) { + spi_cmd = rt5677_spi_select_cmd(false, (addr + offset) & 7, + len - offset, &t.len); + + /* Construct SPI message header */ + buf[0] = spi_cmd; + buf[1] = ((addr + offset) & 0xff000000) >> 24; + buf[2] = ((addr + offset) & 0x00ff0000) >> 16; + buf[3] = ((addr + offset) & 0x0000ff00) >> 8; + buf[4] = ((addr + offset) & 0x000000ff) >> 0; + + /* Fetch data from caller buffer */ + rt5677_spi_reverse(body, t.len, cb + offset, len - offset); + offset += t.len; + t.len += RT5677_SPI_HEADER + 1; + + mutex_lock(&spi_mutex); + status |= spi_sync(g_spi, &m); + mutex_unlock(&spi_mutex); + } + return status; +} +EXPORT_SYMBOL_GPL(rt5677_spi_write); + +int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw) +{ + return rt5677_spi_write(addr, fw->data, fw->size); +} +EXPORT_SYMBOL_GPL(rt5677_spi_write_firmware); + +void rt5677_spi_hotword_detected(void) +{ + struct rt5677_dsp *rt5677_dsp; + + if (!g_spi) + return; + + rt5677_dsp = dev_get_drvdata(&g_spi->dev); + if (!rt5677_dsp) { + dev_err(&g_spi->dev, "Can't get rt5677_dsp\n"); + return; + } + + mutex_lock(&rt5677_dsp->dma_lock); + dev_info(rt5677_dsp->dev, "Hotword detected\n"); + rt5677_dsp->new_hotword = true; + mutex_unlock(&rt5677_dsp->dma_lock); + + schedule_delayed_work(&rt5677_dsp->copy_work, 0); +} +EXPORT_SYMBOL_GPL(rt5677_spi_hotword_detected); + +static int rt5677_spi_probe(struct spi_device *spi) +{ + int ret; + + g_spi = spi; + + ret = devm_snd_soc_register_component(&spi->dev, + &rt5677_spi_dai_component, + &rt5677_spi_dai, 1); + if (ret < 0) + dev_err(&spi->dev, "Failed to register component.\n"); + + return ret; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id rt5677_spi_acpi_id[] = { + { "RT5677AA", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id); +#endif + +static struct spi_driver rt5677_spi_driver = { + .driver = { + .name = DRV_NAME, + .acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id), + }, + .probe = rt5677_spi_probe, +}; +module_spi_driver(rt5677_spi_driver); + +MODULE_DESCRIPTION("ASoC RT5677 SPI driver"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h new file mode 100644 index 0000000000..088b779317 --- /dev/null +++ b/sound/soc/codecs/rt5677-spi.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5677-spi.h -- RT5677 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#ifndef __RT5677_SPI_H__ +#define __RT5677_SPI_H__ + +#if IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI) +int rt5677_spi_read(u32 addr, void *rxbuf, size_t len); +int rt5677_spi_write(u32 addr, const void *txbuf, size_t len); +int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw); +void rt5677_spi_hotword_detected(void); +#else +static inline int rt5677_spi_read(u32 addr, void *rxbuf, size_t len) +{ + return -EINVAL; +} +static inline int rt5677_spi_write(u32 addr, const void *txbuf, size_t len) +{ + return -EINVAL; +} +static inline int rt5677_spi_write_firmware(u32 addr, const struct firmware *fw) +{ + return -EINVAL; +} +static inline void rt5677_spi_hotword_detected(void){} +#endif + +#endif /* __RT5677_SPI_H__ */ diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c new file mode 100644 index 0000000000..0e70a3ab42 --- /dev/null +++ b/sound/soc/codecs/rt5677.c @@ -0,0 +1,5675 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rt5677.c -- RT5677 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5677.h" +#include "rt5677-spi.h" + +#define RT5677_DEVICE_ID 0x6327 + +/* Register controlling boot vector */ +#define RT5677_DSP_BOOT_VECTOR 0x1801f090 +#define RT5677_MODEL_ADDR 0x5FFC9800 + +#define RT5677_PR_RANGE_BASE (0xff + 1) +#define RT5677_PR_SPACING 0x100 + +#define RT5677_PR_BASE (RT5677_PR_RANGE_BASE + (0 * RT5677_PR_SPACING)) + +static const struct regmap_range_cfg rt5677_ranges[] = { + { + .name = "PR", + .range_min = RT5677_PR_BASE, + .range_max = RT5677_PR_BASE + 0xfd, + .selector_reg = RT5677_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5677_PRIV_DATA, + .window_len = 0x1, + }, +}; + +static const struct reg_sequence init_list[] = { + {RT5677_ASRC_12, 0x0018}, + {RT5677_PR_BASE + 0x3d, 0x364d}, + {RT5677_PR_BASE + 0x17, 0x4fc0}, + {RT5677_PR_BASE + 0x13, 0x0312}, + {RT5677_PR_BASE + 0x1e, 0x0000}, + {RT5677_PR_BASE + 0x12, 0x0eaa}, + {RT5677_PR_BASE + 0x14, 0x018a}, + {RT5677_PR_BASE + 0x15, 0x0490}, + {RT5677_PR_BASE + 0x38, 0x0f71}, + {RT5677_PR_BASE + 0x39, 0x0f71}, +}; +#define RT5677_INIT_REG_LEN ARRAY_SIZE(init_list) + +static const struct reg_default rt5677_reg[] = { + {RT5677_RESET , 0x0000}, + {RT5677_LOUT1 , 0xa800}, + {RT5677_IN1 , 0x0000}, + {RT5677_MICBIAS , 0x0000}, + {RT5677_SLIMBUS_PARAM , 0x0000}, + {RT5677_SLIMBUS_RX , 0x0000}, + {RT5677_SLIMBUS_CTRL , 0x0000}, + {RT5677_SIDETONE_CTRL , 0x000b}, + {RT5677_ANA_DAC1_2_3_SRC , 0x0000}, + {RT5677_IF_DSP_DAC3_4_MIXER , 0x1111}, + {RT5677_DAC4_DIG_VOL , 0xafaf}, + {RT5677_DAC3_DIG_VOL , 0xafaf}, + {RT5677_DAC1_DIG_VOL , 0xafaf}, + {RT5677_DAC2_DIG_VOL , 0xafaf}, + {RT5677_IF_DSP_DAC2_MIXER , 0x0011}, + {RT5677_STO1_ADC_DIG_VOL , 0x2f2f}, + {RT5677_MONO_ADC_DIG_VOL , 0x2f2f}, + {RT5677_STO1_2_ADC_BST , 0x0000}, + {RT5677_STO2_ADC_DIG_VOL , 0x2f2f}, + {RT5677_ADC_BST_CTRL2 , 0x0000}, + {RT5677_STO3_4_ADC_BST , 0x0000}, + {RT5677_STO3_ADC_DIG_VOL , 0x2f2f}, + {RT5677_STO4_ADC_DIG_VOL , 0x2f2f}, + {RT5677_STO4_ADC_MIXER , 0xd4c0}, + {RT5677_STO3_ADC_MIXER , 0xd4c0}, + {RT5677_STO2_ADC_MIXER , 0xd4c0}, + {RT5677_STO1_ADC_MIXER , 0xd4c0}, + {RT5677_MONO_ADC_MIXER , 0xd4d1}, + {RT5677_ADC_IF_DSP_DAC1_MIXER , 0x8080}, + {RT5677_STO1_DAC_MIXER , 0xaaaa}, + {RT5677_MONO_DAC_MIXER , 0xaaaa}, + {RT5677_DD1_MIXER , 0xaaaa}, + {RT5677_DD2_MIXER , 0xaaaa}, + {RT5677_IF3_DATA , 0x0000}, + {RT5677_IF4_DATA , 0x0000}, + {RT5677_PDM_OUT_CTRL , 0x8888}, + {RT5677_PDM_DATA_CTRL1 , 0x0000}, + {RT5677_PDM_DATA_CTRL2 , 0x0000}, + {RT5677_PDM1_DATA_CTRL2 , 0x0000}, + {RT5677_PDM1_DATA_CTRL3 , 0x0000}, + {RT5677_PDM1_DATA_CTRL4 , 0x0000}, + {RT5677_PDM2_DATA_CTRL2 , 0x0000}, + {RT5677_PDM2_DATA_CTRL3 , 0x0000}, + {RT5677_PDM2_DATA_CTRL4 , 0x0000}, + {RT5677_TDM1_CTRL1 , 0x0300}, + {RT5677_TDM1_CTRL2 , 0x0000}, + {RT5677_TDM1_CTRL3 , 0x4000}, + {RT5677_TDM1_CTRL4 , 0x0123}, + {RT5677_TDM1_CTRL5 , 0x4567}, + {RT5677_TDM2_CTRL1 , 0x0300}, + {RT5677_TDM2_CTRL2 , 0x0000}, + {RT5677_TDM2_CTRL3 , 0x4000}, + {RT5677_TDM2_CTRL4 , 0x0123}, + {RT5677_TDM2_CTRL5 , 0x4567}, + {RT5677_I2C_MASTER_CTRL1 , 0x0001}, + {RT5677_I2C_MASTER_CTRL2 , 0x0000}, + {RT5677_I2C_MASTER_CTRL3 , 0x0000}, + {RT5677_I2C_MASTER_CTRL4 , 0x0000}, + {RT5677_I2C_MASTER_CTRL5 , 0x0000}, + {RT5677_I2C_MASTER_CTRL6 , 0x0000}, + {RT5677_I2C_MASTER_CTRL7 , 0x0000}, + {RT5677_I2C_MASTER_CTRL8 , 0x0000}, + {RT5677_DMIC_CTRL1 , 0x1505}, + {RT5677_DMIC_CTRL2 , 0x0055}, + {RT5677_HAP_GENE_CTRL1 , 0x0111}, + {RT5677_HAP_GENE_CTRL2 , 0x0064}, + {RT5677_HAP_GENE_CTRL3 , 0xef0e}, + {RT5677_HAP_GENE_CTRL4 , 0xf0f0}, + {RT5677_HAP_GENE_CTRL5 , 0xef0e}, + {RT5677_HAP_GENE_CTRL6 , 0xf0f0}, + {RT5677_HAP_GENE_CTRL7 , 0xef0e}, + {RT5677_HAP_GENE_CTRL8 , 0xf0f0}, + {RT5677_HAP_GENE_CTRL9 , 0xf000}, + {RT5677_HAP_GENE_CTRL10 , 0x0000}, + {RT5677_PWR_DIG1 , 0x0000}, + {RT5677_PWR_DIG2 , 0x0000}, + {RT5677_PWR_ANLG1 , 0x0055}, + {RT5677_PWR_ANLG2 , 0x0000}, + {RT5677_PWR_DSP1 , 0x0001}, + {RT5677_PWR_DSP_ST , 0x0000}, + {RT5677_PWR_DSP2 , 0x0000}, + {RT5677_ADC_DAC_HPF_CTRL1 , 0x0e00}, + {RT5677_PRIV_INDEX , 0x0000}, + {RT5677_PRIV_DATA , 0x0000}, + {RT5677_I2S4_SDP , 0x8000}, + {RT5677_I2S1_SDP , 0x8000}, + {RT5677_I2S2_SDP , 0x8000}, + {RT5677_I2S3_SDP , 0x8000}, + {RT5677_CLK_TREE_CTRL1 , 0x1111}, + {RT5677_CLK_TREE_CTRL2 , 0x1111}, + {RT5677_CLK_TREE_CTRL3 , 0x0000}, + {RT5677_PLL1_CTRL1 , 0x0000}, + {RT5677_PLL1_CTRL2 , 0x0000}, + {RT5677_PLL2_CTRL1 , 0x0c60}, + {RT5677_PLL2_CTRL2 , 0x2000}, + {RT5677_GLB_CLK1 , 0x0000}, + {RT5677_GLB_CLK2 , 0x0000}, + {RT5677_ASRC_1 , 0x0000}, + {RT5677_ASRC_2 , 0x0000}, + {RT5677_ASRC_3 , 0x0000}, + {RT5677_ASRC_4 , 0x0000}, + {RT5677_ASRC_5 , 0x0000}, + {RT5677_ASRC_6 , 0x0000}, + {RT5677_ASRC_7 , 0x0000}, + {RT5677_ASRC_8 , 0x0000}, + {RT5677_ASRC_9 , 0x0000}, + {RT5677_ASRC_10 , 0x0000}, + {RT5677_ASRC_11 , 0x0000}, + {RT5677_ASRC_12 , 0x0018}, + {RT5677_ASRC_13 , 0x0000}, + {RT5677_ASRC_14 , 0x0000}, + {RT5677_ASRC_15 , 0x0000}, + {RT5677_ASRC_16 , 0x0000}, + {RT5677_ASRC_17 , 0x0000}, + {RT5677_ASRC_18 , 0x0000}, + {RT5677_ASRC_19 , 0x0000}, + {RT5677_ASRC_20 , 0x0000}, + {RT5677_ASRC_21 , 0x000c}, + {RT5677_ASRC_22 , 0x0000}, + {RT5677_ASRC_23 , 0x0000}, + {RT5677_VAD_CTRL1 , 0x2184}, + {RT5677_VAD_CTRL2 , 0x010a}, + {RT5677_VAD_CTRL3 , 0x0aea}, + {RT5677_VAD_CTRL4 , 0x000c}, + {RT5677_VAD_CTRL5 , 0x0000}, + {RT5677_DSP_INB_CTRL1 , 0x0000}, + {RT5677_DSP_INB_CTRL2 , 0x0000}, + {RT5677_DSP_IN_OUTB_CTRL , 0x0000}, + {RT5677_DSP_OUTB0_1_DIG_VOL , 0x2f2f}, + {RT5677_DSP_OUTB2_3_DIG_VOL , 0x2f2f}, + {RT5677_DSP_OUTB4_5_DIG_VOL , 0x2f2f}, + {RT5677_DSP_OUTB6_7_DIG_VOL , 0x2f2f}, + {RT5677_ADC_EQ_CTRL1 , 0x6000}, + {RT5677_ADC_EQ_CTRL2 , 0x0000}, + {RT5677_EQ_CTRL1 , 0xc000}, + {RT5677_EQ_CTRL2 , 0x0000}, + {RT5677_EQ_CTRL3 , 0x0000}, + {RT5677_SOFT_VOL_ZERO_CROSS1 , 0x0009}, + {RT5677_JD_CTRL1 , 0x0000}, + {RT5677_JD_CTRL2 , 0x0000}, + {RT5677_JD_CTRL3 , 0x0000}, + {RT5677_IRQ_CTRL1 , 0x0000}, + {RT5677_IRQ_CTRL2 , 0x0000}, + {RT5677_GPIO_ST , 0x0000}, + {RT5677_GPIO_CTRL1 , 0x0000}, + {RT5677_GPIO_CTRL2 , 0x0000}, + {RT5677_GPIO_CTRL3 , 0x0000}, + {RT5677_STO1_ADC_HI_FILTER1 , 0xb320}, + {RT5677_STO1_ADC_HI_FILTER2 , 0x0000}, + {RT5677_MONO_ADC_HI_FILTER1 , 0xb300}, + {RT5677_MONO_ADC_HI_FILTER2 , 0x0000}, + {RT5677_STO2_ADC_HI_FILTER1 , 0xb300}, + {RT5677_STO2_ADC_HI_FILTER2 , 0x0000}, + {RT5677_STO3_ADC_HI_FILTER1 , 0xb300}, + {RT5677_STO3_ADC_HI_FILTER2 , 0x0000}, + {RT5677_STO4_ADC_HI_FILTER1 , 0xb300}, + {RT5677_STO4_ADC_HI_FILTER2 , 0x0000}, + {RT5677_MB_DRC_CTRL1 , 0x0f20}, + {RT5677_DRC1_CTRL1 , 0x001f}, + {RT5677_DRC1_CTRL2 , 0x020c}, + {RT5677_DRC1_CTRL3 , 0x1f00}, + {RT5677_DRC1_CTRL4 , 0x0000}, + {RT5677_DRC1_CTRL5 , 0x0000}, + {RT5677_DRC1_CTRL6 , 0x0029}, + {RT5677_DRC2_CTRL1 , 0x001f}, + {RT5677_DRC2_CTRL2 , 0x020c}, + {RT5677_DRC2_CTRL3 , 0x1f00}, + {RT5677_DRC2_CTRL4 , 0x0000}, + {RT5677_DRC2_CTRL5 , 0x0000}, + {RT5677_DRC2_CTRL6 , 0x0029}, + {RT5677_DRC1_HL_CTRL1 , 0x8000}, + {RT5677_DRC1_HL_CTRL2 , 0x0200}, + {RT5677_DRC2_HL_CTRL1 , 0x8000}, + {RT5677_DRC2_HL_CTRL2 , 0x0200}, + {RT5677_DSP_INB1_SRC_CTRL1 , 0x5800}, + {RT5677_DSP_INB1_SRC_CTRL2 , 0x0000}, + {RT5677_DSP_INB1_SRC_CTRL3 , 0x0000}, + {RT5677_DSP_INB1_SRC_CTRL4 , 0x0800}, + {RT5677_DSP_INB2_SRC_CTRL1 , 0x5800}, + {RT5677_DSP_INB2_SRC_CTRL2 , 0x0000}, + {RT5677_DSP_INB2_SRC_CTRL3 , 0x0000}, + {RT5677_DSP_INB2_SRC_CTRL4 , 0x0800}, + {RT5677_DSP_INB3_SRC_CTRL1 , 0x5800}, + {RT5677_DSP_INB3_SRC_CTRL2 , 0x0000}, + {RT5677_DSP_INB3_SRC_CTRL3 , 0x0000}, + {RT5677_DSP_INB3_SRC_CTRL4 , 0x0800}, + {RT5677_DSP_OUTB1_SRC_CTRL1 , 0x5800}, + {RT5677_DSP_OUTB1_SRC_CTRL2 , 0x0000}, + {RT5677_DSP_OUTB1_SRC_CTRL3 , 0x0000}, + {RT5677_DSP_OUTB1_SRC_CTRL4 , 0x0800}, + {RT5677_DSP_OUTB2_SRC_CTRL1 , 0x5800}, + {RT5677_DSP_OUTB2_SRC_CTRL2 , 0x0000}, + {RT5677_DSP_OUTB2_SRC_CTRL3 , 0x0000}, + {RT5677_DSP_OUTB2_SRC_CTRL4 , 0x0800}, + {RT5677_DSP_OUTB_0123_MIXER_CTRL, 0xfefe}, + {RT5677_DSP_OUTB_45_MIXER_CTRL , 0xfefe}, + {RT5677_DSP_OUTB_67_MIXER_CTRL , 0xfefe}, + {RT5677_DIG_MISC , 0x0000}, + {RT5677_GEN_CTRL1 , 0x0000}, + {RT5677_GEN_CTRL2 , 0x0000}, + {RT5677_VENDOR_ID , 0x0000}, + {RT5677_VENDOR_ID1 , 0x10ec}, + {RT5677_VENDOR_ID2 , 0x6327}, +}; + +static bool rt5677_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5677_ranges); i++) { + if (reg >= rt5677_ranges[i].range_min && + reg <= rt5677_ranges[i].range_max) { + return true; + } + } + + switch (reg) { + case RT5677_RESET: + case RT5677_SLIMBUS_PARAM: + case RT5677_PDM_DATA_CTRL1: + case RT5677_PDM_DATA_CTRL2: + case RT5677_PDM1_DATA_CTRL4: + case RT5677_PDM2_DATA_CTRL4: + case RT5677_I2C_MASTER_CTRL1: + case RT5677_I2C_MASTER_CTRL7: + case RT5677_I2C_MASTER_CTRL8: + case RT5677_HAP_GENE_CTRL2: + case RT5677_PWR_ANLG2: /* Modified by DSP firmware */ + case RT5677_PWR_DSP_ST: + case RT5677_PRIV_DATA: + case RT5677_ASRC_22: + case RT5677_ASRC_23: + case RT5677_VAD_CTRL5: + case RT5677_ADC_EQ_CTRL1: + case RT5677_EQ_CTRL1: + case RT5677_IRQ_CTRL1: + case RT5677_IRQ_CTRL2: + case RT5677_GPIO_ST: + case RT5677_GPIO_CTRL1: /* Modified by DSP firmware */ + case RT5677_GPIO_CTRL2: /* Modified by DSP firmware */ + case RT5677_DSP_INB1_SRC_CTRL4: + case RT5677_DSP_INB2_SRC_CTRL4: + case RT5677_DSP_INB3_SRC_CTRL4: + case RT5677_DSP_OUTB1_SRC_CTRL4: + case RT5677_DSP_OUTB2_SRC_CTRL4: + case RT5677_VENDOR_ID: + case RT5677_VENDOR_ID1: + case RT5677_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool rt5677_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5677_ranges); i++) { + if (reg >= rt5677_ranges[i].range_min && + reg <= rt5677_ranges[i].range_max) { + return true; + } + } + + switch (reg) { + case RT5677_RESET: + case RT5677_LOUT1: + case RT5677_IN1: + case RT5677_MICBIAS: + case RT5677_SLIMBUS_PARAM: + case RT5677_SLIMBUS_RX: + case RT5677_SLIMBUS_CTRL: + case RT5677_SIDETONE_CTRL: + case RT5677_ANA_DAC1_2_3_SRC: + case RT5677_IF_DSP_DAC3_4_MIXER: + case RT5677_DAC4_DIG_VOL: + case RT5677_DAC3_DIG_VOL: + case RT5677_DAC1_DIG_VOL: + case RT5677_DAC2_DIG_VOL: + case RT5677_IF_DSP_DAC2_MIXER: + case RT5677_STO1_ADC_DIG_VOL: + case RT5677_MONO_ADC_DIG_VOL: + case RT5677_STO1_2_ADC_BST: + case RT5677_STO2_ADC_DIG_VOL: + case RT5677_ADC_BST_CTRL2: + case RT5677_STO3_4_ADC_BST: + case RT5677_STO3_ADC_DIG_VOL: + case RT5677_STO4_ADC_DIG_VOL: + case RT5677_STO4_ADC_MIXER: + case RT5677_STO3_ADC_MIXER: + case RT5677_STO2_ADC_MIXER: + case RT5677_STO1_ADC_MIXER: + case RT5677_MONO_ADC_MIXER: + case RT5677_ADC_IF_DSP_DAC1_MIXER: + case RT5677_STO1_DAC_MIXER: + case RT5677_MONO_DAC_MIXER: + case RT5677_DD1_MIXER: + case RT5677_DD2_MIXER: + case RT5677_IF3_DATA: + case RT5677_IF4_DATA: + case RT5677_PDM_OUT_CTRL: + case RT5677_PDM_DATA_CTRL1: + case RT5677_PDM_DATA_CTRL2: + case RT5677_PDM1_DATA_CTRL2: + case RT5677_PDM1_DATA_CTRL3: + case RT5677_PDM1_DATA_CTRL4: + case RT5677_PDM2_DATA_CTRL2: + case RT5677_PDM2_DATA_CTRL3: + case RT5677_PDM2_DATA_CTRL4: + case RT5677_TDM1_CTRL1: + case RT5677_TDM1_CTRL2: + case RT5677_TDM1_CTRL3: + case RT5677_TDM1_CTRL4: + case RT5677_TDM1_CTRL5: + case RT5677_TDM2_CTRL1: + case RT5677_TDM2_CTRL2: + case RT5677_TDM2_CTRL3: + case RT5677_TDM2_CTRL4: + case RT5677_TDM2_CTRL5: + case RT5677_I2C_MASTER_CTRL1: + case RT5677_I2C_MASTER_CTRL2: + case RT5677_I2C_MASTER_CTRL3: + case RT5677_I2C_MASTER_CTRL4: + case RT5677_I2C_MASTER_CTRL5: + case RT5677_I2C_MASTER_CTRL6: + case RT5677_I2C_MASTER_CTRL7: + case RT5677_I2C_MASTER_CTRL8: + case RT5677_DMIC_CTRL1: + case RT5677_DMIC_CTRL2: + case RT5677_HAP_GENE_CTRL1: + case RT5677_HAP_GENE_CTRL2: + case RT5677_HAP_GENE_CTRL3: + case RT5677_HAP_GENE_CTRL4: + case RT5677_HAP_GENE_CTRL5: + case RT5677_HAP_GENE_CTRL6: + case RT5677_HAP_GENE_CTRL7: + case RT5677_HAP_GENE_CTRL8: + case RT5677_HAP_GENE_CTRL9: + case RT5677_HAP_GENE_CTRL10: + case RT5677_PWR_DIG1: + case RT5677_PWR_DIG2: + case RT5677_PWR_ANLG1: + case RT5677_PWR_ANLG2: + case RT5677_PWR_DSP1: + case RT5677_PWR_DSP_ST: + case RT5677_PWR_DSP2: + case RT5677_ADC_DAC_HPF_CTRL1: + case RT5677_PRIV_INDEX: + case RT5677_PRIV_DATA: + case RT5677_I2S4_SDP: + case RT5677_I2S1_SDP: + case RT5677_I2S2_SDP: + case RT5677_I2S3_SDP: + case RT5677_CLK_TREE_CTRL1: + case RT5677_CLK_TREE_CTRL2: + case RT5677_CLK_TREE_CTRL3: + case RT5677_PLL1_CTRL1: + case RT5677_PLL1_CTRL2: + case RT5677_PLL2_CTRL1: + case RT5677_PLL2_CTRL2: + case RT5677_GLB_CLK1: + case RT5677_GLB_CLK2: + case RT5677_ASRC_1: + case RT5677_ASRC_2: + case RT5677_ASRC_3: + case RT5677_ASRC_4: + case RT5677_ASRC_5: + case RT5677_ASRC_6: + case RT5677_ASRC_7: + case RT5677_ASRC_8: + case RT5677_ASRC_9: + case RT5677_ASRC_10: + case RT5677_ASRC_11: + case RT5677_ASRC_12: + case RT5677_ASRC_13: + case RT5677_ASRC_14: + case RT5677_ASRC_15: + case RT5677_ASRC_16: + case RT5677_ASRC_17: + case RT5677_ASRC_18: + case RT5677_ASRC_19: + case RT5677_ASRC_20: + case RT5677_ASRC_21: + case RT5677_ASRC_22: + case RT5677_ASRC_23: + case RT5677_VAD_CTRL1: + case RT5677_VAD_CTRL2: + case RT5677_VAD_CTRL3: + case RT5677_VAD_CTRL4: + case RT5677_VAD_CTRL5: + case RT5677_DSP_INB_CTRL1: + case RT5677_DSP_INB_CTRL2: + case RT5677_DSP_IN_OUTB_CTRL: + case RT5677_DSP_OUTB0_1_DIG_VOL: + case RT5677_DSP_OUTB2_3_DIG_VOL: + case RT5677_DSP_OUTB4_5_DIG_VOL: + case RT5677_DSP_OUTB6_7_DIG_VOL: + case RT5677_ADC_EQ_CTRL1: + case RT5677_ADC_EQ_CTRL2: + case RT5677_EQ_CTRL1: + case RT5677_EQ_CTRL2: + case RT5677_EQ_CTRL3: + case RT5677_SOFT_VOL_ZERO_CROSS1: + case RT5677_JD_CTRL1: + case RT5677_JD_CTRL2: + case RT5677_JD_CTRL3: + case RT5677_IRQ_CTRL1: + case RT5677_IRQ_CTRL2: + case RT5677_GPIO_ST: + case RT5677_GPIO_CTRL1: + case RT5677_GPIO_CTRL2: + case RT5677_GPIO_CTRL3: + case RT5677_STO1_ADC_HI_FILTER1: + case RT5677_STO1_ADC_HI_FILTER2: + case RT5677_MONO_ADC_HI_FILTER1: + case RT5677_MONO_ADC_HI_FILTER2: + case RT5677_STO2_ADC_HI_FILTER1: + case RT5677_STO2_ADC_HI_FILTER2: + case RT5677_STO3_ADC_HI_FILTER1: + case RT5677_STO3_ADC_HI_FILTER2: + case RT5677_STO4_ADC_HI_FILTER1: + case RT5677_STO4_ADC_HI_FILTER2: + case RT5677_MB_DRC_CTRL1: + case RT5677_DRC1_CTRL1: + case RT5677_DRC1_CTRL2: + case RT5677_DRC1_CTRL3: + case RT5677_DRC1_CTRL4: + case RT5677_DRC1_CTRL5: + case RT5677_DRC1_CTRL6: + case RT5677_DRC2_CTRL1: + case RT5677_DRC2_CTRL2: + case RT5677_DRC2_CTRL3: + case RT5677_DRC2_CTRL4: + case RT5677_DRC2_CTRL5: + case RT5677_DRC2_CTRL6: + case RT5677_DRC1_HL_CTRL1: + case RT5677_DRC1_HL_CTRL2: + case RT5677_DRC2_HL_CTRL1: + case RT5677_DRC2_HL_CTRL2: + case RT5677_DSP_INB1_SRC_CTRL1: + case RT5677_DSP_INB1_SRC_CTRL2: + case RT5677_DSP_INB1_SRC_CTRL3: + case RT5677_DSP_INB1_SRC_CTRL4: + case RT5677_DSP_INB2_SRC_CTRL1: + case RT5677_DSP_INB2_SRC_CTRL2: + case RT5677_DSP_INB2_SRC_CTRL3: + case RT5677_DSP_INB2_SRC_CTRL4: + case RT5677_DSP_INB3_SRC_CTRL1: + case RT5677_DSP_INB3_SRC_CTRL2: + case RT5677_DSP_INB3_SRC_CTRL3: + case RT5677_DSP_INB3_SRC_CTRL4: + case RT5677_DSP_OUTB1_SRC_CTRL1: + case RT5677_DSP_OUTB1_SRC_CTRL2: + case RT5677_DSP_OUTB1_SRC_CTRL3: + case RT5677_DSP_OUTB1_SRC_CTRL4: + case RT5677_DSP_OUTB2_SRC_CTRL1: + case RT5677_DSP_OUTB2_SRC_CTRL2: + case RT5677_DSP_OUTB2_SRC_CTRL3: + case RT5677_DSP_OUTB2_SRC_CTRL4: + case RT5677_DSP_OUTB_0123_MIXER_CTRL: + case RT5677_DSP_OUTB_45_MIXER_CTRL: + case RT5677_DSP_OUTB_67_MIXER_CTRL: + case RT5677_DIG_MISC: + case RT5677_GEN_CTRL1: + case RT5677_GEN_CTRL2: + case RT5677_VENDOR_ID: + case RT5677_VENDOR_ID1: + case RT5677_VENDOR_ID2: + return true; + default: + return false; + } +} + +/** + * rt5677_dsp_mode_i2c_write_addr - Write value to address on DSP mode. + * @rt5677: Private Data. + * @addr: Address index. + * @value: Address data. + * @opcode: opcode value + * + * Returns 0 for success or negative error code. + */ +static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677, + unsigned int addr, unsigned int value, unsigned int opcode) +{ + struct snd_soc_component *component = rt5677->component; + int ret; + + mutex_lock(&rt5677->dsp_cmd_lock); + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, + addr >> 16); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, + addr & 0xffff); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, + value >> 16); + if (ret < 0) { + dev_err(component->dev, "Failed to set data msb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, + value & 0xffff); + if (ret < 0) { + dev_err(component->dev, "Failed to set data lsb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, + opcode); + if (ret < 0) { + dev_err(component->dev, "Failed to set op code value: %d\n", ret); + goto err; + } + +err: + mutex_unlock(&rt5677->dsp_cmd_lock); + + return ret; +} + +/** + * rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode. + * @rt5677: Private Data. + * @addr: Address index. + * @value: Address data. + * + * + * Returns 0 for success or negative error code. + */ +static int rt5677_dsp_mode_i2c_read_addr( + struct rt5677_priv *rt5677, unsigned int addr, unsigned int *value) +{ + struct snd_soc_component *component = rt5677->component; + int ret; + unsigned int msb, lsb; + + mutex_lock(&rt5677->dsp_cmd_lock); + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB, + addr >> 16); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr msb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB, + addr & 0xffff); + if (ret < 0) { + dev_err(component->dev, "Failed to set addr lsb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE, + 0x0002); + if (ret < 0) { + dev_err(component->dev, "Failed to set op code value: %d\n", ret); + goto err; + } + + regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb); + regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb); + *value = (msb << 16) | lsb; + +err: + mutex_unlock(&rt5677->dsp_cmd_lock); + + return ret; +} + +/** + * rt5677_dsp_mode_i2c_write - Write register on DSP mode. + * @rt5677: Private Data. + * @reg: Register index. + * @value: Register data. + * + * + * Returns 0 for success or negative error code. + */ +static int rt5677_dsp_mode_i2c_write(struct rt5677_priv *rt5677, + unsigned int reg, unsigned int value) +{ + return rt5677_dsp_mode_i2c_write_addr(rt5677, 0x18020000 + reg * 2, + value, 0x0001); +} + +/** + * rt5677_dsp_mode_i2c_read - Read register on DSP mode. + * @rt5677: Private Data + * @reg: Register index. + * @value: Register data. + * + * + * Returns 0 for success or negative error code. + */ +static int rt5677_dsp_mode_i2c_read( + struct rt5677_priv *rt5677, unsigned int reg, unsigned int *value) +{ + int ret = rt5677_dsp_mode_i2c_read_addr(rt5677, 0x18020000 + reg * 2, + value); + + *value &= 0xffff; + + return ret; +} + +static void rt5677_set_dsp_mode(struct rt5677_priv *rt5677, bool on) +{ + if (on) { + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, + RT5677_PWR_DSP, RT5677_PWR_DSP); + rt5677->is_dsp_mode = true; + } else { + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, + RT5677_PWR_DSP, 0x0); + rt5677->is_dsp_mode = false; + } +} + +static unsigned int rt5677_set_vad_source(struct rt5677_priv *rt5677) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(rt5677->component); + /* Force dapm to sync before we enable the + * DSP to prevent write corruption + */ + snd_soc_dapm_sync(dapm); + + /* DMIC1 power = enabled + * DMIC CLK = 256 * fs / 12 + */ + regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1, + RT5677_DMIC_CLK_MASK, 5 << RT5677_DMIC_CLK_SFT); + + /* I2S pre divide 2 = /6 (clk_sys2) */ + regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1, + RT5677_I2S_PD2_MASK, RT5677_I2S_PD2_6); + + /* DSP Clock = MCLK1 (bypassed PLL2) */ + regmap_write(rt5677->regmap, RT5677_GLB_CLK2, + RT5677_DSP_CLK_SRC_BYPASS); + + /* SAD Threshold1 */ + regmap_write(rt5677->regmap, RT5677_VAD_CTRL2, 0x013f); + /* SAD Threshold2 */ + regmap_write(rt5677->regmap, RT5677_VAD_CTRL3, 0x0ae5); + /* SAD Sample Rate Converter = Up 6 (8K to 48K) + * SAD Output Sample Rate = Same as I2S + * SAD Threshold3 + */ + regmap_update_bits(rt5677->regmap, RT5677_VAD_CTRL4, + RT5677_VAD_OUT_SRC_RATE_MASK | RT5677_VAD_OUT_SRC_MASK | + RT5677_VAD_LV_DIFF_MASK, 0x7f << RT5677_VAD_LV_DIFF_SFT); + /* Minimum frame level within a pre-determined duration = 32 frames + * Bypass ADPCM Encoder/Decoder = Bypass ADPCM + * Automatic Push Data to SAD Buffer Once SAD Flag is triggered = enable + * SAD Buffer Over-Writing = enable + * SAD Buffer Pop Mode Control = disable + * SAD Buffer Push Mode Control = enable + * SAD Detector Control = enable + * SAD Function Control = enable + * SAD Function Reset = normal + */ + regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, + RT5677_VAD_FUNC_RESET | RT5677_VAD_FUNC_ENABLE | + RT5677_VAD_DET_ENABLE | RT5677_VAD_BUF_PUSH | + RT5677_VAD_BUF_OW | RT5677_VAD_FG2ENC | + RT5677_VAD_ADPCM_BYPASS | 1 << RT5677_VAD_MIN_DUR_SFT); + + /* VAD/SAD is not routed to the IRQ output (i.e. MX-BE[14] = 0), but it + * is routed to DSP_IRQ_0, so DSP firmware may use it to sleep and save + * power. See ALC5677 datasheet section 9.17 "GPIO, Interrupt and Jack + * Detection" for more info. + */ + + /* Private register, no doc */ + regmap_update_bits(rt5677->regmap, RT5677_PR_BASE + RT5677_BIAS_CUR4, + 0x0f00, 0x0100); + + /* LDO2 output = 1.2V + * LDO1 output = 1.2V (LDO_IN = 1.8V) + */ + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, + RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK, + 5 << RT5677_LDO1_SEL_SFT | 5 << RT5677_LDO2_SEL_SFT); + + /* Codec core power = power on + * LDO1 power = power on + */ + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_CORE | RT5677_PWR_LDO1, + RT5677_PWR_CORE | RT5677_PWR_LDO1); + + /* Isolation for DCVDD4 = normal (set during probe) + * Isolation for DCVDD2 = normal (set during probe) + * Isolation for DSP = normal + * Isolation for Band 0~7 = disable + * Isolation for InBound 4~10 and OutBound 4~10 = disable + */ + regmap_write(rt5677->regmap, RT5677_PWR_DSP2, + RT5677_PWR_CORE_ISO | RT5677_PWR_DSP_ISO | + RT5677_PWR_SR7_ISO | RT5677_PWR_SR6_ISO | + RT5677_PWR_SR5_ISO | RT5677_PWR_SR4_ISO | + RT5677_PWR_SR3_ISO | RT5677_PWR_SR2_ISO | + RT5677_PWR_SR1_ISO | RT5677_PWR_SR0_ISO | + RT5677_PWR_MLT_ISO); + + /* System Band 0~7 = power on + * InBound 4~10 and OutBound 4~10 = power on + * DSP = power on + * DSP CPU = stop (will be set to "run" after firmware loaded) + */ + regmap_write(rt5677->regmap, RT5677_PWR_DSP1, + RT5677_PWR_SR7 | RT5677_PWR_SR6 | + RT5677_PWR_SR5 | RT5677_PWR_SR4 | + RT5677_PWR_SR3 | RT5677_PWR_SR2 | + RT5677_PWR_SR1 | RT5677_PWR_SR0 | + RT5677_PWR_MLT | RT5677_PWR_DSP | + RT5677_PWR_DSP_CPU); + + return 0; +} + +static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf, + unsigned int len) +{ + struct snd_soc_component *component = rt5677->component; + Elf32_Ehdr *elf_hdr; + Elf32_Phdr *pr_hdr; + Elf32_Half i; + int ret = 0; + + if (!buf || (len < sizeof(Elf32_Ehdr))) + return -ENOMEM; + + elf_hdr = (Elf32_Ehdr *)buf; +#ifndef EM_XTENSA +#define EM_XTENSA 94 +#endif + if (strncmp(elf_hdr->e_ident, ELFMAG, sizeof(ELFMAG) - 1)) + dev_err(component->dev, "Wrong ELF header prefix\n"); + if (elf_hdr->e_ehsize != sizeof(Elf32_Ehdr)) + dev_err(component->dev, "Wrong ELF header size\n"); + if (elf_hdr->e_machine != EM_XTENSA) + dev_err(component->dev, "Wrong DSP code file\n"); + + if (len < elf_hdr->e_phoff) + return -ENOMEM; + pr_hdr = (Elf32_Phdr *)(buf + elf_hdr->e_phoff); + for (i = 0; i < elf_hdr->e_phnum; i++) { + /* TODO: handle p_memsz != p_filesz */ + if (pr_hdr->p_paddr && pr_hdr->p_filesz) { + dev_info(component->dev, "Load 0x%x bytes to 0x%x\n", + pr_hdr->p_filesz, pr_hdr->p_paddr); + + ret = rt5677_spi_write(pr_hdr->p_paddr, + buf + pr_hdr->p_offset, + pr_hdr->p_filesz); + if (ret) + dev_err(component->dev, "Load firmware failed %d\n", + ret); + } + pr_hdr++; + } + return ret; +} + +static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677) +{ + const struct firmware *fwp; + struct device *dev = rt5677->component->dev; + int ret = 0; + + /* Load dsp firmware from rt5677_elf_vad file */ + ret = request_firmware(&fwp, "rt5677_elf_vad", dev); + if (ret) { + dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret); + return ret; + } + dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size); + + ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size); + release_firmware(fwp); + return ret; +} + +static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on) +{ + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + rt5677->dsp_vad_en_request = on; + rt5677->dsp_vad_en = on; + + if (!IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI)) + return -ENXIO; + + schedule_delayed_work(&rt5677->dsp_work, 0); + return 0; +} + +static void rt5677_dsp_work(struct work_struct *work) +{ + struct rt5677_priv *rt5677 = + container_of(work, struct rt5677_priv, dsp_work.work); + static bool activity; + bool enable = rt5677->dsp_vad_en; + int i, val; + + + dev_info(rt5677->component->dev, "DSP VAD: enable=%d, activity=%d\n", + enable, activity); + + if (enable && !activity) { + activity = true; + + /* Before a hotword is detected, GPIO1 pin is configured as IRQ + * output so that jack detect works. When a hotword is detected, + * the DSP firmware configures the GPIO1 pin as GPIO1 and + * drives a 1. rt5677_irq() is called after a rising edge on + * the GPIO1 pin, due to either jack detect event or hotword + * event, or both. All possible events are checked and handled + * in rt5677_irq() where GPIO1 pin is configured back to IRQ + * output if a hotword is detected. + */ + + rt5677_set_vad_source(rt5677); + rt5677_set_dsp_mode(rt5677, true); + +#define RT5677_BOOT_RETRY 20 + for (i = 0; i < RT5677_BOOT_RETRY; i++) { + regmap_read(rt5677->regmap, RT5677_PWR_DSP_ST, &val); + if (val == 0x3ff) + break; + udelay(500); + } + if (i == RT5677_BOOT_RETRY && val != 0x3ff) { + dev_err(rt5677->component->dev, "DSP Boot Timed Out!"); + return; + } + + /* Boot the firmware from IRAM instead of SRAM0. */ + rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR, + 0x0009, 0x0003); + rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR, + 0x0019, 0x0003); + rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR, + 0x0009, 0x0003); + + rt5677_load_dsp_from_file(rt5677); + + /* Set DSP CPU to Run */ + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, + RT5677_PWR_DSP_CPU, 0x0); + } else if (!enable && activity) { + activity = false; + + /* Don't turn off the DSP while handling irqs */ + mutex_lock(&rt5677->irq_lock); + /* Set DSP CPU to Stop */ + regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, + RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU); + + rt5677_set_dsp_mode(rt5677, false); + + /* Disable and clear VAD interrupt */ + regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184); + + /* Set GPIO1 pin back to be IRQ output for jack detect */ + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, + RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); + + mutex_unlock(&rt5677->irq_lock); + } +} + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); +static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt5677->dsp_vad_en_request; + + return 0; +} + +static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + + rt5677_set_dsp_vad(component, !!ucontrol->value.integer.value[0]); + + return 0; +} + +static const struct snd_kcontrol_new rt5677_snd_controls[] = { + /* OUTPUT Control */ + SOC_SINGLE("OUT1 Playback Switch", RT5677_LOUT1, + RT5677_LOUT1_L_MUTE_SFT, 1, 1), + SOC_SINGLE("OUT2 Playback Switch", RT5677_LOUT1, + RT5677_LOUT2_L_MUTE_SFT, 1, 1), + SOC_SINGLE("OUT3 Playback Switch", RT5677_LOUT1, + RT5677_LOUT3_L_MUTE_SFT, 1, 1), + + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5677_DAC1_DIG_VOL, + RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5677_DAC2_DIG_VOL, + RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("DAC3 Playback Volume", RT5677_DAC3_DIG_VOL, + RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("DAC4 Playback Volume", RT5677_DAC4_DIG_VOL, + RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 127, 0, dac_vol_tlv), + + /* IN1/IN2 Control */ + SOC_SINGLE_TLV("IN1 Boost", RT5677_IN1, RT5677_BST_SFT1, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN2 Boost", RT5677_IN1, RT5677_BST_SFT2, 8, 0, bst_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC1 Capture Switch", RT5677_STO1_ADC_DIG_VOL, + RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("ADC2 Capture Switch", RT5677_STO2_ADC_DIG_VOL, + RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("ADC3 Capture Switch", RT5677_STO3_ADC_DIG_VOL, + RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("ADC4 Capture Switch", RT5677_STO4_ADC_DIG_VOL, + RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("Mono ADC Capture Switch", RT5677_MONO_ADC_DIG_VOL, + RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1), + + SOC_DOUBLE_TLV("ADC1 Capture Volume", RT5677_STO1_ADC_DIG_VOL, + RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0, + adc_vol_tlv), + SOC_DOUBLE_TLV("ADC2 Capture Volume", RT5677_STO2_ADC_DIG_VOL, + RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0, + adc_vol_tlv), + SOC_DOUBLE_TLV("ADC3 Capture Volume", RT5677_STO3_ADC_DIG_VOL, + RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0, + adc_vol_tlv), + SOC_DOUBLE_TLV("ADC4 Capture Volume", RT5677_STO4_ADC_DIG_VOL, + RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0, + adc_vol_tlv), + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5677_MONO_ADC_DIG_VOL, + RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 63, 0, + adc_vol_tlv), + + /* Sidetone Control */ + SOC_SINGLE_TLV("Sidetone Volume", RT5677_SIDETONE_CTRL, + RT5677_ST_VOL_SFT, 31, 0, st_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Volume", RT5677_STO1_2_ADC_BST, + RT5677_STO1_ADC_L_BST_SFT, RT5677_STO1_ADC_R_BST_SFT, 3, 0, + adc_bst_tlv), + SOC_DOUBLE_TLV("STO2 ADC Boost Volume", RT5677_STO1_2_ADC_BST, + RT5677_STO2_ADC_L_BST_SFT, RT5677_STO2_ADC_R_BST_SFT, 3, 0, + adc_bst_tlv), + SOC_DOUBLE_TLV("STO3 ADC Boost Volume", RT5677_STO3_4_ADC_BST, + RT5677_STO3_ADC_L_BST_SFT, RT5677_STO3_ADC_R_BST_SFT, 3, 0, + adc_bst_tlv), + SOC_DOUBLE_TLV("STO4 ADC Boost Volume", RT5677_STO3_4_ADC_BST, + RT5677_STO4_ADC_L_BST_SFT, RT5677_STO4_ADC_R_BST_SFT, 3, 0, + adc_bst_tlv), + SOC_DOUBLE_TLV("Mono ADC Boost Volume", RT5677_ADC_BST_CTRL2, + RT5677_MONO_ADC_L_BST_SFT, RT5677_MONO_ADC_R_BST_SFT, 3, 0, + adc_bst_tlv), + + SOC_SINGLE_EXT("DSP VAD Switch", SND_SOC_NOPM, 0, 1, 0, + rt5677_dsp_vad_get, rt5677_dsp_vad_put), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + int idx, rate; + + rate = rt5677->sysclk / rl6231_get_pre_div(rt5677->regmap, + RT5677_CLK_TREE_CTRL1, RT5677_I2S_PD1_SFT); + idx = rl6231_calc_dmic_clk(rate); + if (idx < 0) + dev_err(component->dev, "Failed to set DMIC clock\n"); + else + regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1, + RT5677_DMIC_CLK_MASK, idx << RT5677_DMIC_CLK_SFT); + return idx; +} + +static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + unsigned int val; + + regmap_read(rt5677->regmap, RT5677_GLB_CLK1, &val); + val &= RT5677_SCLK_SRC_MASK; + if (val == RT5677_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + unsigned int reg, shift, val; + + if (source->reg == RT5677_ASRC_1) { + switch (source->shift) { + case 12: + reg = RT5677_ASRC_4; + shift = 0; + break; + case 13: + reg = RT5677_ASRC_4; + shift = 4; + break; + case 14: + reg = RT5677_ASRC_4; + shift = 8; + break; + case 15: + reg = RT5677_ASRC_4; + shift = 12; + break; + default: + return 0; + } + } else { + switch (source->shift) { + case 0: + reg = RT5677_ASRC_6; + shift = 8; + break; + case 1: + reg = RT5677_ASRC_6; + shift = 12; + break; + case 2: + reg = RT5677_ASRC_5; + shift = 0; + break; + case 3: + reg = RT5677_ASRC_5; + shift = 4; + break; + case 4: + reg = RT5677_ASRC_5; + shift = 8; + break; + case 5: + reg = RT5677_ASRC_5; + shift = 12; + break; + case 12: + reg = RT5677_ASRC_3; + shift = 0; + break; + case 13: + reg = RT5677_ASRC_3; + shift = 4; + break; + case 14: + reg = RT5677_ASRC_3; + shift = 12; + break; + default: + return 0; + } + } + + regmap_read(rt5677->regmap, reg, &val); + val = (val >> shift) & 0xf; + + switch (val) { + case 1 ... 6: + return 1; + default: + return 0; + } + +} + +static int can_use_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + if (rt5677->sysclk > rt5677->lrck[RT5677_AIF1] * 384) + return 1; + + return 0; +} + +/** + * rt5677_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5677 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the codec driver will turn on ASRC + * for these filters if ASRC is selected as their clock source. + */ +int rt5677_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + unsigned int asrc3_mask = 0, asrc3_value = 0; + unsigned int asrc4_mask = 0, asrc4_value = 0; + unsigned int asrc5_mask = 0, asrc5_value = 0; + unsigned int asrc6_mask = 0, asrc6_value = 0; + unsigned int asrc7_mask = 0, asrc7_value = 0; + unsigned int asrc8_mask = 0, asrc8_value = 0; + + switch (clk_src) { + case RT5677_CLK_SEL_SYS: + case RT5677_CLK_SEL_I2S1_ASRC: + case RT5677_CLK_SEL_I2S2_ASRC: + case RT5677_CLK_SEL_I2S3_ASRC: + case RT5677_CLK_SEL_I2S4_ASRC: + case RT5677_CLK_SEL_I2S5_ASRC: + case RT5677_CLK_SEL_I2S6_ASRC: + case RT5677_CLK_SEL_SYS2: + case RT5677_CLK_SEL_SYS3: + case RT5677_CLK_SEL_SYS4: + case RT5677_CLK_SEL_SYS5: + case RT5677_CLK_SEL_SYS6: + case RT5677_CLK_SEL_SYS7: + break; + + default: + return -EINVAL; + } + + /* ASRC 3 */ + if (filter_mask & RT5677_DA_STEREO_FILTER) { + asrc3_mask |= RT5677_DA_STO_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5677_DA_STO_CLK_SEL_MASK) + | (clk_src << RT5677_DA_STO_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO2_L_FILTER) { + asrc3_mask |= RT5677_DA_MONO2L_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5677_DA_MONO2L_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO2L_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO2_R_FILTER) { + asrc3_mask |= RT5677_DA_MONO2R_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5677_DA_MONO2R_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO2R_CLK_SEL_SFT); + } + + if (asrc3_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_3, asrc3_mask, + asrc3_value); + + /* ASRC 4 */ + if (filter_mask & RT5677_DA_MONO3_L_FILTER) { + asrc4_mask |= RT5677_DA_MONO3L_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO3L_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO3L_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO3_R_FILTER) { + asrc4_mask |= RT5677_DA_MONO3R_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO3R_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO3R_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO4_L_FILTER) { + asrc4_mask |= RT5677_DA_MONO4L_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO4L_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO4L_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO4_R_FILTER) { + asrc4_mask |= RT5677_DA_MONO4R_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO4R_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO4R_CLK_SEL_SFT); + } + + if (asrc4_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_4, asrc4_mask, + asrc4_value); + + /* ASRC 5 */ + if (filter_mask & RT5677_AD_STEREO1_FILTER) { + asrc5_mask |= RT5677_AD_STO1_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO1_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO1_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_STEREO2_FILTER) { + asrc5_mask |= RT5677_AD_STO2_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO2_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO2_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_STEREO3_FILTER) { + asrc5_mask |= RT5677_AD_STO3_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO3_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO3_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_STEREO4_FILTER) { + asrc5_mask |= RT5677_AD_STO4_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO4_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO4_CLK_SEL_SFT); + } + + if (asrc5_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_5, asrc5_mask, + asrc5_value); + + /* ASRC 6 */ + if (filter_mask & RT5677_AD_MONO_L_FILTER) { + asrc6_mask |= RT5677_AD_MONOL_CLK_SEL_MASK; + asrc6_value = (asrc6_value & ~RT5677_AD_MONOL_CLK_SEL_MASK) + | (clk_src << RT5677_AD_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_MONO_R_FILTER) { + asrc6_mask |= RT5677_AD_MONOR_CLK_SEL_MASK; + asrc6_value = (asrc6_value & ~RT5677_AD_MONOR_CLK_SEL_MASK) + | (clk_src << RT5677_AD_MONOR_CLK_SEL_SFT); + } + + if (asrc6_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_6, asrc6_mask, + asrc6_value); + + /* ASRC 7 */ + if (filter_mask & RT5677_DSP_OB_0_3_FILTER) { + asrc7_mask |= RT5677_DSP_OB_0_3_CLK_SEL_MASK; + asrc7_value = (asrc7_value & ~RT5677_DSP_OB_0_3_CLK_SEL_MASK) + | (clk_src << RT5677_DSP_OB_0_3_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DSP_OB_4_7_FILTER) { + asrc7_mask |= RT5677_DSP_OB_4_7_CLK_SEL_MASK; + asrc7_value = (asrc7_value & ~RT5677_DSP_OB_4_7_CLK_SEL_MASK) + | (clk_src << RT5677_DSP_OB_4_7_CLK_SEL_SFT); + } + + if (asrc7_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_7, asrc7_mask, + asrc7_value); + + /* ASRC 8 */ + if (filter_mask & RT5677_I2S1_SOURCE) { + asrc8_mask |= RT5677_I2S1_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S1_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S1_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_I2S2_SOURCE) { + asrc8_mask |= RT5677_I2S2_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S2_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S2_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_I2S3_SOURCE) { + asrc8_mask |= RT5677_I2S3_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S3_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S3_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_I2S4_SOURCE) { + asrc8_mask |= RT5677_I2S4_CLK_SEL_MASK; + asrc8_value = (asrc8_value & ~RT5677_I2S4_CLK_SEL_MASK) + | ((clk_src - 1) << RT5677_I2S4_CLK_SEL_SFT); + } + + if (asrc8_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_8, asrc8_mask, + asrc8_value); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5677_sel_asrc_clk_src); + +static int rt5677_dmic_use_asrc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + unsigned int asrc_setting; + + switch (source->shift) { + case 11: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO1_CLK_SEL_MASK) >> + RT5677_AD_STO1_CLK_SEL_SFT; + break; + + case 10: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO2_CLK_SEL_MASK) >> + RT5677_AD_STO2_CLK_SEL_SFT; + break; + + case 9: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO3_CLK_SEL_MASK) >> + RT5677_AD_STO3_CLK_SEL_SFT; + break; + + case 8: + regmap_read(rt5677->regmap, RT5677_ASRC_5, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_STO4_CLK_SEL_MASK) >> + RT5677_AD_STO4_CLK_SEL_SFT; + break; + + case 7: + regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_MONOL_CLK_SEL_MASK) >> + RT5677_AD_MONOL_CLK_SEL_SFT; + break; + + case 6: + regmap_read(rt5677->regmap, RT5677_ASRC_6, &asrc_setting); + asrc_setting = (asrc_setting & RT5677_AD_MONOR_CLK_SEL_MASK) >> + RT5677_AD_MONOR_CLK_SEL_SFT; + break; + + default: + return 0; + } + + if (asrc_setting >= RT5677_CLK_SEL_I2S1_ASRC && + asrc_setting <= RT5677_CLK_SEL_I2S6_ASRC) + return 1; + + return 0; +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5677_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER, + RT5677_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO1_ADC_MIXER, + RT5677_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER, + RT5677_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO1_ADC_MIXER, + RT5677_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_sto2_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO2_ADC_MIXER, + RT5677_M_STO2_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO2_ADC_MIXER, + RT5677_M_STO2_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_sto2_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO2_ADC_MIXER, + RT5677_M_STO2_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO2_ADC_MIXER, + RT5677_M_STO2_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_sto3_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO3_ADC_MIXER, + RT5677_M_STO3_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO3_ADC_MIXER, + RT5677_M_STO3_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_sto3_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO3_ADC_MIXER, + RT5677_M_STO3_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO3_ADC_MIXER, + RT5677_M_STO3_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_sto4_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO4_ADC_MIXER, + RT5677_M_STO4_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO4_ADC_MIXER, + RT5677_M_STO4_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_sto4_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO4_ADC_MIXER, + RT5677_M_STO4_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5677_STO4_ADC_MIXER, + RT5677_M_STO4_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_mono_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5677_MONO_ADC_MIXER, + RT5677_M_MONO_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5677_MONO_ADC_MIXER, + RT5677_M_MONO_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_mono_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5677_MONO_ADC_MIXER, + RT5677_M_MONO_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5677_MONO_ADC_MIXER, + RT5677_M_MONO_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5677_ADC_IF_DSP_DAC1_MIXER, + RT5677_M_ADDA_MIXER1_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5677_ADC_IF_DSP_DAC1_MIXER, + RT5677_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5677_ADC_IF_DSP_DAC1_MIXER, + RT5677_M_ADDA_MIXER1_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5677_ADC_IF_DSP_DAC1_MIXER, + RT5677_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_STO1_DAC_MIXER, + RT5677_M_ST_DAC1_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER, + RT5677_M_DAC1_L_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER, + RT5677_M_DAC2_L_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER, + RT5677_M_DAC1_R_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_STO1_DAC_MIXER, + RT5677_M_ST_DAC1_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER, + RT5677_M_DAC1_R_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER, + RT5677_M_DAC2_R_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER, + RT5677_M_DAC1_L_STO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_mono_dac_l_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_MONO_DAC_MIXER, + RT5677_M_ST_DAC2_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER, + RT5677_M_DAC1_L_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER, + RT5677_M_DAC2_L_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER, + RT5677_M_DAC2_R_MONO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_mono_dac_r_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_MONO_DAC_MIXER, + RT5677_M_ST_DAC2_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER, + RT5677_M_DAC1_R_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER, + RT5677_M_DAC2_R_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER, + RT5677_M_DAC2_L_MONO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_dd1_l_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER, + RT5677_M_STO_L_DD1_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER, + RT5677_M_MONO_L_DD1_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER, + RT5677_M_DAC3_L_DD1_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER, + RT5677_M_DAC3_R_DD1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_dd1_r_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER, + RT5677_M_STO_R_DD1_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER, + RT5677_M_MONO_R_DD1_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER, + RT5677_M_DAC3_R_DD1_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER, + RT5677_M_DAC3_L_DD1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_dd2_l_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER, + RT5677_M_STO_L_DD2_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER, + RT5677_M_MONO_L_DD2_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER, + RT5677_M_DAC4_L_DD2_L_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER, + RT5677_M_DAC4_R_DD2_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_dd2_r_mix[] = { + SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER, + RT5677_M_STO_R_DD2_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER, + RT5677_M_MONO_R_DD2_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER, + RT5677_M_DAC4_R_DD2_R_SFT, 1, 1), + SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER, + RT5677_M_DAC4_L_DD2_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_ob_01_mix[] = { + SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_01_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_23_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_45_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_6_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_7_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_8_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_9_H_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_ob_23_mix[] = { + SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_01_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_23_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_45_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_6_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_7_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_8_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_0123_MIXER_CTRL, + RT5677_DSP_IB_9_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_ob_4_mix[] = { + SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_01_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_23_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_45_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_6_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_7_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_8_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_9_H_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_ob_5_mix[] = { + SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_01_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_23_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_45_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_6_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_7_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_8_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_45_MIXER_CTRL, + RT5677_DSP_IB_9_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_ob_6_mix[] = { + SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_01_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_23_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_45_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_6_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_7_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_8_H_SFT, 1, 1), + SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_9_H_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5677_ob_7_mix[] = { + SOC_DAPM_SINGLE("IB01 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_01_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB23 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_23_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB45 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_45_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB6 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_6_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB7 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_7_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB8 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_8_L_SFT, 1, 1), + SOC_DAPM_SINGLE("IB9 Switch", RT5677_DSP_OUTB_67_MIXER_CTRL, + RT5677_DSP_IB_9_L_SFT, 1, 1), +}; + + +/* Mux */ +/* DAC1 L/R Source */ /* MX-29 [10:8] */ +static const char * const rt5677_dac1_src[] = { + "IF1 DAC 01", "IF2 DAC 01", "IF3 DAC LR", "IF4 DAC LR", "SLB DAC 01", + "OB 01" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_dac1_enum, RT5677_ADC_IF_DSP_DAC1_MIXER, + RT5677_DAC1_L_SEL_SFT, rt5677_dac1_src); + +static const struct snd_kcontrol_new rt5677_dac1_mux = + SOC_DAPM_ENUM("DAC1 Source", rt5677_dac1_enum); + +/* ADDA1 L/R Source */ /* MX-29 [1:0] */ +static const char * const rt5677_adda1_src[] = { + "STO1 ADC MIX", "STO2 ADC MIX", "OB 67", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_adda1_enum, RT5677_ADC_IF_DSP_DAC1_MIXER, + RT5677_ADDA1_SEL_SFT, rt5677_adda1_src); + +static const struct snd_kcontrol_new rt5677_adda1_mux = + SOC_DAPM_ENUM("ADDA1 Source", rt5677_adda1_enum); + + +/*DAC2 L/R Source*/ /* MX-1B [6:4] [2:0] */ +static const char * const rt5677_dac2l_src[] = { + "IF1 DAC 2", "IF2 DAC 2", "IF3 DAC L", "IF4 DAC L", "SLB DAC 2", + "OB 2", +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_dac2l_enum, RT5677_IF_DSP_DAC2_MIXER, + RT5677_SEL_DAC2_L_SRC_SFT, rt5677_dac2l_src); + +static const struct snd_kcontrol_new rt5677_dac2_l_mux = + SOC_DAPM_ENUM("DAC2 L Source", rt5677_dac2l_enum); + +static const char * const rt5677_dac2r_src[] = { + "IF1 DAC 3", "IF2 DAC 3", "IF3 DAC R", "IF4 DAC R", "SLB DAC 3", + "OB 3", "Haptic Generator", "VAD ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_dac2r_enum, RT5677_IF_DSP_DAC2_MIXER, + RT5677_SEL_DAC2_R_SRC_SFT, rt5677_dac2r_src); + +static const struct snd_kcontrol_new rt5677_dac2_r_mux = + SOC_DAPM_ENUM("DAC2 R Source", rt5677_dac2r_enum); + +/*DAC3 L/R Source*/ /* MX-16 [6:4] [2:0] */ +static const char * const rt5677_dac3l_src[] = { + "IF1 DAC 4", "IF2 DAC 4", "IF3 DAC L", "IF4 DAC L", + "SLB DAC 4", "OB 4" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_dac3l_enum, RT5677_IF_DSP_DAC3_4_MIXER, + RT5677_SEL_DAC3_L_SRC_SFT, rt5677_dac3l_src); + +static const struct snd_kcontrol_new rt5677_dac3_l_mux = + SOC_DAPM_ENUM("DAC3 L Source", rt5677_dac3l_enum); + +static const char * const rt5677_dac3r_src[] = { + "IF1 DAC 5", "IF2 DAC 5", "IF3 DAC R", "IF4 DAC R", + "SLB DAC 5", "OB 5" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_dac3r_enum, RT5677_IF_DSP_DAC3_4_MIXER, + RT5677_SEL_DAC3_R_SRC_SFT, rt5677_dac3r_src); + +static const struct snd_kcontrol_new rt5677_dac3_r_mux = + SOC_DAPM_ENUM("DAC3 R Source", rt5677_dac3r_enum); + +/*DAC4 L/R Source*/ /* MX-16 [14:12] [10:8] */ +static const char * const rt5677_dac4l_src[] = { + "IF1 DAC 6", "IF2 DAC 6", "IF3 DAC L", "IF4 DAC L", + "SLB DAC 6", "OB 6" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_dac4l_enum, RT5677_IF_DSP_DAC3_4_MIXER, + RT5677_SEL_DAC4_L_SRC_SFT, rt5677_dac4l_src); + +static const struct snd_kcontrol_new rt5677_dac4_l_mux = + SOC_DAPM_ENUM("DAC4 L Source", rt5677_dac4l_enum); + +static const char * const rt5677_dac4r_src[] = { + "IF1 DAC 7", "IF2 DAC 7", "IF3 DAC R", "IF4 DAC R", + "SLB DAC 7", "OB 7" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_dac4r_enum, RT5677_IF_DSP_DAC3_4_MIXER, + RT5677_SEL_DAC4_R_SRC_SFT, rt5677_dac4r_src); + +static const struct snd_kcontrol_new rt5677_dac4_r_mux = + SOC_DAPM_ENUM("DAC4 R Source", rt5677_dac4r_enum); + +/* In/OutBound Source Pass SRC */ /* MX-A5 [3] [4] [0] [1] [2] */ +static const char * const rt5677_iob_bypass_src[] = { + "Bypass", "Pass SRC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_ob01_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL, + RT5677_SEL_SRC_OB01_SFT, rt5677_iob_bypass_src); + +static const struct snd_kcontrol_new rt5677_ob01_bypass_src_mux = + SOC_DAPM_ENUM("OB01 Bypass Source", rt5677_ob01_bypass_src_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_ob23_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL, + RT5677_SEL_SRC_OB23_SFT, rt5677_iob_bypass_src); + +static const struct snd_kcontrol_new rt5677_ob23_bypass_src_mux = + SOC_DAPM_ENUM("OB23 Bypass Source", rt5677_ob23_bypass_src_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_ib01_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL, + RT5677_SEL_SRC_IB01_SFT, rt5677_iob_bypass_src); + +static const struct snd_kcontrol_new rt5677_ib01_bypass_src_mux = + SOC_DAPM_ENUM("IB01 Bypass Source", rt5677_ib01_bypass_src_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_ib23_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL, + RT5677_SEL_SRC_IB23_SFT, rt5677_iob_bypass_src); + +static const struct snd_kcontrol_new rt5677_ib23_bypass_src_mux = + SOC_DAPM_ENUM("IB23 Bypass Source", rt5677_ib23_bypass_src_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_ib45_bypass_src_enum, RT5677_DSP_IN_OUTB_CTRL, + RT5677_SEL_SRC_IB45_SFT, rt5677_iob_bypass_src); + +static const struct snd_kcontrol_new rt5677_ib45_bypass_src_mux = + SOC_DAPM_ENUM("IB45 Bypass Source", rt5677_ib45_bypass_src_enum); + +/* Stereo ADC Source 2 */ /* MX-27 MX26 MX25 [11:10] */ +static const char * const rt5677_stereo_adc2_src[] = { + "DD MIX1", "DMIC", "Stereo DAC MIX" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo1_adc2_enum, RT5677_STO1_ADC_MIXER, + RT5677_SEL_STO1_ADC2_SFT, rt5677_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5677_sto1_adc2_mux = + SOC_DAPM_ENUM("Stereo1 ADC2 Source", rt5677_stereo1_adc2_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo2_adc2_enum, RT5677_STO2_ADC_MIXER, + RT5677_SEL_STO2_ADC2_SFT, rt5677_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5677_sto2_adc2_mux = + SOC_DAPM_ENUM("Stereo2 ADC2 Source", rt5677_stereo2_adc2_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo3_adc2_enum, RT5677_STO3_ADC_MIXER, + RT5677_SEL_STO3_ADC2_SFT, rt5677_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5677_sto3_adc2_mux = + SOC_DAPM_ENUM("Stereo3 ADC2 Source", rt5677_stereo3_adc2_enum); + +/* DMIC Source */ /* MX-28 [9:8][1:0] MX-27 MX-26 MX-25 MX-24 [9:8] */ +static const char * const rt5677_dmic_src[] = { + "DMIC1", "DMIC2", "DMIC3", "DMIC4" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_mono_dmic_l_enum, RT5677_MONO_ADC_MIXER, + RT5677_SEL_MONO_DMIC_L_SFT, rt5677_dmic_src); + +static const struct snd_kcontrol_new rt5677_mono_dmic_l_mux = + SOC_DAPM_ENUM("Mono DMIC L Source", rt5677_mono_dmic_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_mono_dmic_r_enum, RT5677_MONO_ADC_MIXER, + RT5677_SEL_MONO_DMIC_R_SFT, rt5677_dmic_src); + +static const struct snd_kcontrol_new rt5677_mono_dmic_r_mux = + SOC_DAPM_ENUM("Mono DMIC R Source", rt5677_mono_dmic_r_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo1_dmic_enum, RT5677_STO1_ADC_MIXER, + RT5677_SEL_STO1_DMIC_SFT, rt5677_dmic_src); + +static const struct snd_kcontrol_new rt5677_sto1_dmic_mux = + SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5677_stereo1_dmic_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo2_dmic_enum, RT5677_STO2_ADC_MIXER, + RT5677_SEL_STO2_DMIC_SFT, rt5677_dmic_src); + +static const struct snd_kcontrol_new rt5677_sto2_dmic_mux = + SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5677_stereo2_dmic_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo3_dmic_enum, RT5677_STO3_ADC_MIXER, + RT5677_SEL_STO3_DMIC_SFT, rt5677_dmic_src); + +static const struct snd_kcontrol_new rt5677_sto3_dmic_mux = + SOC_DAPM_ENUM("Stereo3 DMIC Source", rt5677_stereo3_dmic_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo4_dmic_enum, RT5677_STO4_ADC_MIXER, + RT5677_SEL_STO4_DMIC_SFT, rt5677_dmic_src); + +static const struct snd_kcontrol_new rt5677_sto4_dmic_mux = + SOC_DAPM_ENUM("Stereo4 DMIC Source", rt5677_stereo4_dmic_enum); + +/* Stereo2 ADC Source */ /* MX-26 [0] */ +static const char * const rt5677_stereo2_adc_lr_src[] = { + "L", "LR" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo2_adc_lr_enum, RT5677_STO2_ADC_MIXER, + RT5677_SEL_STO2_LR_MIX_SFT, rt5677_stereo2_adc_lr_src); + +static const struct snd_kcontrol_new rt5677_sto2_adc_lr_mux = + SOC_DAPM_ENUM("Stereo2 ADC LR Source", rt5677_stereo2_adc_lr_enum); + +/* Stereo1 ADC Source 1 */ /* MX-27 MX26 MX25 [13:12] */ +static const char * const rt5677_stereo_adc1_src[] = { + "DD MIX1", "ADC1/2", "Stereo DAC MIX" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo1_adc1_enum, RT5677_STO1_ADC_MIXER, + RT5677_SEL_STO1_ADC1_SFT, rt5677_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5677_sto1_adc1_mux = + SOC_DAPM_ENUM("Stereo1 ADC1 Source", rt5677_stereo1_adc1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo2_adc1_enum, RT5677_STO2_ADC_MIXER, + RT5677_SEL_STO2_ADC1_SFT, rt5677_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5677_sto2_adc1_mux = + SOC_DAPM_ENUM("Stereo2 ADC1 Source", rt5677_stereo2_adc1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo3_adc1_enum, RT5677_STO3_ADC_MIXER, + RT5677_SEL_STO3_ADC1_SFT, rt5677_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5677_sto3_adc1_mux = + SOC_DAPM_ENUM("Stereo3 ADC1 Source", rt5677_stereo3_adc1_enum); + +/* Mono ADC Left Source 2 */ /* MX-28 [11:10] */ +static const char * const rt5677_mono_adc2_l_src[] = { + "DD MIX1L", "DMIC", "MONO DAC MIXL" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_mono_adc2_l_enum, RT5677_MONO_ADC_MIXER, + RT5677_SEL_MONO_ADC_L2_SFT, rt5677_mono_adc2_l_src); + +static const struct snd_kcontrol_new rt5677_mono_adc2_l_mux = + SOC_DAPM_ENUM("Mono ADC2 L Source", rt5677_mono_adc2_l_enum); + +/* Mono ADC Left Source 1 */ /* MX-28 [13:12] */ +static const char * const rt5677_mono_adc1_l_src[] = { + "DD MIX1L", "ADC1", "MONO DAC MIXL" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_mono_adc1_l_enum, RT5677_MONO_ADC_MIXER, + RT5677_SEL_MONO_ADC_L1_SFT, rt5677_mono_adc1_l_src); + +static const struct snd_kcontrol_new rt5677_mono_adc1_l_mux = + SOC_DAPM_ENUM("Mono ADC1 L Source", rt5677_mono_adc1_l_enum); + +/* Mono ADC Right Source 2 */ /* MX-28 [3:2] */ +static const char * const rt5677_mono_adc2_r_src[] = { + "DD MIX1R", "DMIC", "MONO DAC MIXR" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_mono_adc2_r_enum, RT5677_MONO_ADC_MIXER, + RT5677_SEL_MONO_ADC_R2_SFT, rt5677_mono_adc2_r_src); + +static const struct snd_kcontrol_new rt5677_mono_adc2_r_mux = + SOC_DAPM_ENUM("Mono ADC2 R Source", rt5677_mono_adc2_r_enum); + +/* Mono ADC Right Source 1 */ /* MX-28 [5:4] */ +static const char * const rt5677_mono_adc1_r_src[] = { + "DD MIX1R", "ADC2", "MONO DAC MIXR" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_mono_adc1_r_enum, RT5677_MONO_ADC_MIXER, + RT5677_SEL_MONO_ADC_R1_SFT, rt5677_mono_adc1_r_src); + +static const struct snd_kcontrol_new rt5677_mono_adc1_r_mux = + SOC_DAPM_ENUM("Mono ADC1 R Source", rt5677_mono_adc1_r_enum); + +/* Stereo4 ADC Source 2 */ /* MX-24 [11:10] */ +static const char * const rt5677_stereo4_adc2_src[] = { + "DD MIX1", "DMIC", "DD MIX2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo4_adc2_enum, RT5677_STO4_ADC_MIXER, + RT5677_SEL_STO4_ADC2_SFT, rt5677_stereo4_adc2_src); + +static const struct snd_kcontrol_new rt5677_sto4_adc2_mux = + SOC_DAPM_ENUM("Stereo4 ADC2 Source", rt5677_stereo4_adc2_enum); + + +/* Stereo4 ADC Source 1 */ /* MX-24 [13:12] */ +static const char * const rt5677_stereo4_adc1_src[] = { + "DD MIX1", "ADC1/2", "DD MIX2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_stereo4_adc1_enum, RT5677_STO4_ADC_MIXER, + RT5677_SEL_STO4_ADC1_SFT, rt5677_stereo4_adc1_src); + +static const struct snd_kcontrol_new rt5677_sto4_adc1_mux = + SOC_DAPM_ENUM("Stereo4 ADC1 Source", rt5677_stereo4_adc1_enum); + +/* InBound0/1 Source */ /* MX-A3 [14:12] */ +static const char * const rt5677_inbound01_src[] = { + "IF1 DAC 01", "IF2 DAC 01", "SLB DAC 01", "STO1 ADC MIX", + "VAD ADC/DAC1 FS" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_inbound01_enum, RT5677_DSP_INB_CTRL1, + RT5677_IB01_SRC_SFT, rt5677_inbound01_src); + +static const struct snd_kcontrol_new rt5677_ib01_src_mux = + SOC_DAPM_ENUM("InBound0/1 Source", rt5677_inbound01_enum); + +/* InBound2/3 Source */ /* MX-A3 [10:8] */ +static const char * const rt5677_inbound23_src[] = { + "IF1 DAC 23", "IF2 DAC 23", "SLB DAC 23", "STO2 ADC MIX", + "DAC1 FS", "IF4 DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_inbound23_enum, RT5677_DSP_INB_CTRL1, + RT5677_IB23_SRC_SFT, rt5677_inbound23_src); + +static const struct snd_kcontrol_new rt5677_ib23_src_mux = + SOC_DAPM_ENUM("InBound2/3 Source", rt5677_inbound23_enum); + +/* InBound4/5 Source */ /* MX-A3 [6:4] */ +static const char * const rt5677_inbound45_src[] = { + "IF1 DAC 45", "IF2 DAC 45", "SLB DAC 45", "STO3 ADC MIX", + "IF3 DAC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_inbound45_enum, RT5677_DSP_INB_CTRL1, + RT5677_IB45_SRC_SFT, rt5677_inbound45_src); + +static const struct snd_kcontrol_new rt5677_ib45_src_mux = + SOC_DAPM_ENUM("InBound4/5 Source", rt5677_inbound45_enum); + +/* InBound6 Source */ /* MX-A3 [2:0] */ +static const char * const rt5677_inbound6_src[] = { + "IF1 DAC 6", "IF2 DAC 6", "SLB DAC 6", "STO4 ADC MIX L", + "IF4 DAC L", "STO1 ADC MIX L", "STO2 ADC MIX L", "STO3 ADC MIX L" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_inbound6_enum, RT5677_DSP_INB_CTRL1, + RT5677_IB6_SRC_SFT, rt5677_inbound6_src); + +static const struct snd_kcontrol_new rt5677_ib6_src_mux = + SOC_DAPM_ENUM("InBound6 Source", rt5677_inbound6_enum); + +/* InBound7 Source */ /* MX-A4 [14:12] */ +static const char * const rt5677_inbound7_src[] = { + "IF1 DAC 7", "IF2 DAC 7", "SLB DAC 7", "STO4 ADC MIX R", + "IF4 DAC R", "STO1 ADC MIX R", "STO2 ADC MIX R", "STO3 ADC MIX R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_inbound7_enum, RT5677_DSP_INB_CTRL2, + RT5677_IB7_SRC_SFT, rt5677_inbound7_src); + +static const struct snd_kcontrol_new rt5677_ib7_src_mux = + SOC_DAPM_ENUM("InBound7 Source", rt5677_inbound7_enum); + +/* InBound8 Source */ /* MX-A4 [10:8] */ +static const char * const rt5677_inbound8_src[] = { + "STO1 ADC MIX L", "STO2 ADC MIX L", "STO3 ADC MIX L", "STO4 ADC MIX L", + "MONO ADC MIX L", "DACL1 FS" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_inbound8_enum, RT5677_DSP_INB_CTRL2, + RT5677_IB8_SRC_SFT, rt5677_inbound8_src); + +static const struct snd_kcontrol_new rt5677_ib8_src_mux = + SOC_DAPM_ENUM("InBound8 Source", rt5677_inbound8_enum); + +/* InBound9 Source */ /* MX-A4 [6:4] */ +static const char * const rt5677_inbound9_src[] = { + "STO1 ADC MIX R", "STO2 ADC MIX R", "STO3 ADC MIX R", "STO4 ADC MIX R", + "MONO ADC MIX R", "DACR1 FS", "DAC1 FS" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_inbound9_enum, RT5677_DSP_INB_CTRL2, + RT5677_IB9_SRC_SFT, rt5677_inbound9_src); + +static const struct snd_kcontrol_new rt5677_ib9_src_mux = + SOC_DAPM_ENUM("InBound9 Source", rt5677_inbound9_enum); + +/* VAD Source */ /* MX-9F [6:4] */ +static const char * const rt5677_vad_src[] = { + "STO1 ADC MIX L", "MONO ADC MIX L", "MONO ADC MIX R", "STO2 ADC MIX L", + "STO3 ADC MIX L" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_vad_enum, RT5677_VAD_CTRL4, + RT5677_VAD_SRC_SFT, rt5677_vad_src); + +static const struct snd_kcontrol_new rt5677_vad_src_mux = + SOC_DAPM_ENUM("VAD Source", rt5677_vad_enum); + +/* Sidetone Source */ /* MX-13 [11:9] */ +static const char * const rt5677_sidetone_src[] = { + "DMIC1 L", "DMIC2 L", "DMIC3 L", "DMIC4 L", "ADC1", "ADC2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_sidetone_enum, RT5677_SIDETONE_CTRL, + RT5677_ST_SEL_SFT, rt5677_sidetone_src); + +static const struct snd_kcontrol_new rt5677_sidetone_mux = + SOC_DAPM_ENUM("Sidetone Source", rt5677_sidetone_enum); + +/* DAC1/2 Source */ /* MX-15 [1:0] */ +static const char * const rt5677_dac12_src[] = { + "STO1 DAC MIX", "MONO DAC MIX", "DD MIX1", "DD MIX2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_dac12_enum, RT5677_ANA_DAC1_2_3_SRC, + RT5677_ANA_DAC1_2_SRC_SEL_SFT, rt5677_dac12_src); + +static const struct snd_kcontrol_new rt5677_dac12_mux = + SOC_DAPM_ENUM("Analog DAC1/2 Source", rt5677_dac12_enum); + +/* DAC3 Source */ /* MX-15 [5:4] */ +static const char * const rt5677_dac3_src[] = { + "MONO DAC MIXL", "MONO DAC MIXR", "DD MIX1L", "DD MIX2L" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_dac3_enum, RT5677_ANA_DAC1_2_3_SRC, + RT5677_ANA_DAC3_SRC_SEL_SFT, rt5677_dac3_src); + +static const struct snd_kcontrol_new rt5677_dac3_mux = + SOC_DAPM_ENUM("Analog DAC3 Source", rt5677_dac3_enum); + +/* PDM channel Source */ /* MX-31 [13:12][9:8][5:4][1:0] */ +static const char * const rt5677_pdm_src[] = { + "STO1 DAC MIX", "MONO DAC MIX", "DD MIX1", "DD MIX2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_pdm1_l_enum, RT5677_PDM_OUT_CTRL, + RT5677_SEL_PDM1_L_SFT, rt5677_pdm_src); + +static const struct snd_kcontrol_new rt5677_pdm1_l_mux = + SOC_DAPM_ENUM("PDM1 Source", rt5677_pdm1_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_pdm2_l_enum, RT5677_PDM_OUT_CTRL, + RT5677_SEL_PDM2_L_SFT, rt5677_pdm_src); + +static const struct snd_kcontrol_new rt5677_pdm2_l_mux = + SOC_DAPM_ENUM("PDM2 Source", rt5677_pdm2_l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_pdm1_r_enum, RT5677_PDM_OUT_CTRL, + RT5677_SEL_PDM1_R_SFT, rt5677_pdm_src); + +static const struct snd_kcontrol_new rt5677_pdm1_r_mux = + SOC_DAPM_ENUM("PDM1 Source", rt5677_pdm1_r_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_pdm2_r_enum, RT5677_PDM_OUT_CTRL, + RT5677_SEL_PDM2_R_SFT, rt5677_pdm_src); + +static const struct snd_kcontrol_new rt5677_pdm2_r_mux = + SOC_DAPM_ENUM("PDM2 Source", rt5677_pdm2_r_enum); + +/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0] */ +static const char * const rt5677_if12_adc1_src[] = { + "STO1 ADC MIX", "OB01", "VAD ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc1_enum, RT5677_TDM1_CTRL2, + RT5677_IF1_ADC1_SFT, rt5677_if12_adc1_src); + +static const struct snd_kcontrol_new rt5677_if1_adc1_mux = + SOC_DAPM_ENUM("IF1 ADC1 Source", rt5677_if1_adc1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc1_enum, RT5677_TDM2_CTRL2, + RT5677_IF2_ADC1_SFT, rt5677_if12_adc1_src); + +static const struct snd_kcontrol_new rt5677_if2_adc1_mux = + SOC_DAPM_ENUM("IF2 ADC1 Source", rt5677_if2_adc1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_slb_adc1_enum, RT5677_SLIMBUS_RX, + RT5677_SLB_ADC1_SFT, rt5677_if12_adc1_src); + +static const struct snd_kcontrol_new rt5677_slb_adc1_mux = + SOC_DAPM_ENUM("SLB ADC1 Source", rt5677_slb_adc1_enum); + +/* TDM IF1/2 SLB ADC2 Data Selection */ /* MX-3C MX-41 [7:6] MX-08 [3:2] */ +static const char * const rt5677_if12_adc2_src[] = { + "STO2 ADC MIX", "OB23" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc2_enum, RT5677_TDM1_CTRL2, + RT5677_IF1_ADC2_SFT, rt5677_if12_adc2_src); + +static const struct snd_kcontrol_new rt5677_if1_adc2_mux = + SOC_DAPM_ENUM("IF1 ADC2 Source", rt5677_if1_adc2_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc2_enum, RT5677_TDM2_CTRL2, + RT5677_IF2_ADC2_SFT, rt5677_if12_adc2_src); + +static const struct snd_kcontrol_new rt5677_if2_adc2_mux = + SOC_DAPM_ENUM("IF2 ADC2 Source", rt5677_if2_adc2_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_slb_adc2_enum, RT5677_SLIMBUS_RX, + RT5677_SLB_ADC2_SFT, rt5677_if12_adc2_src); + +static const struct snd_kcontrol_new rt5677_slb_adc2_mux = + SOC_DAPM_ENUM("SLB ADC2 Source", rt5677_slb_adc2_enum); + +/* TDM IF1/2 SLB ADC3 Data Selection */ /* MX-3C MX-41 [9:8] MX-08 [5:4] */ +static const char * const rt5677_if12_adc3_src[] = { + "STO3 ADC MIX", "MONO ADC MIX", "OB45" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc3_enum, RT5677_TDM1_CTRL2, + RT5677_IF1_ADC3_SFT, rt5677_if12_adc3_src); + +static const struct snd_kcontrol_new rt5677_if1_adc3_mux = + SOC_DAPM_ENUM("IF1 ADC3 Source", rt5677_if1_adc3_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc3_enum, RT5677_TDM2_CTRL2, + RT5677_IF2_ADC3_SFT, rt5677_if12_adc3_src); + +static const struct snd_kcontrol_new rt5677_if2_adc3_mux = + SOC_DAPM_ENUM("IF2 ADC3 Source", rt5677_if2_adc3_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_slb_adc3_enum, RT5677_SLIMBUS_RX, + RT5677_SLB_ADC3_SFT, rt5677_if12_adc3_src); + +static const struct snd_kcontrol_new rt5677_slb_adc3_mux = + SOC_DAPM_ENUM("SLB ADC3 Source", rt5677_slb_adc3_enum); + +/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10] MX-08 [7:6] */ +static const char * const rt5677_if12_adc4_src[] = { + "STO4 ADC MIX", "OB67", "OB01" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc4_enum, RT5677_TDM1_CTRL2, + RT5677_IF1_ADC4_SFT, rt5677_if12_adc4_src); + +static const struct snd_kcontrol_new rt5677_if1_adc4_mux = + SOC_DAPM_ENUM("IF1 ADC4 Source", rt5677_if1_adc4_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc4_enum, RT5677_TDM2_CTRL2, + RT5677_IF2_ADC4_SFT, rt5677_if12_adc4_src); + +static const struct snd_kcontrol_new rt5677_if2_adc4_mux = + SOC_DAPM_ENUM("IF2 ADC4 Source", rt5677_if2_adc4_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_slb_adc4_enum, RT5677_SLIMBUS_RX, + RT5677_SLB_ADC4_SFT, rt5677_if12_adc4_src); + +static const struct snd_kcontrol_new rt5677_slb_adc4_mux = + SOC_DAPM_ENUM("SLB ADC4 Source", rt5677_slb_adc4_enum); + +/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4] */ +static const char * const rt5677_if34_adc_src[] = { + "STO1 ADC MIX", "STO2 ADC MIX", "STO3 ADC MIX", "STO4 ADC MIX", + "MONO ADC MIX", "OB01", "OB23", "VAD ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if3_adc_enum, RT5677_IF3_DATA, + RT5677_IF3_ADC_IN_SFT, rt5677_if34_adc_src); + +static const struct snd_kcontrol_new rt5677_if3_adc_mux = + SOC_DAPM_ENUM("IF3 ADC Source", rt5677_if3_adc_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if4_adc_enum, RT5677_IF4_DATA, + RT5677_IF4_ADC_IN_SFT, rt5677_if34_adc_src); + +static const struct snd_kcontrol_new rt5677_if4_adc_mux = + SOC_DAPM_ENUM("IF4 ADC Source", rt5677_if4_adc_enum); + +/* TDM IF1/2 ADC Data Selection */ /* MX-3B MX-40 [7:6][5:4][3:2][1:0] */ +static const char * const rt5677_if12_adc_swap_src[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc1_swap_enum, RT5677_TDM1_CTRL1, + RT5677_IF1_ADC1_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if1_adc1_swap_mux = + SOC_DAPM_ENUM("IF1 ADC1 Swap Source", rt5677_if1_adc1_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc2_swap_enum, RT5677_TDM1_CTRL1, + RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if1_adc2_swap_mux = + SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if1_adc2_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc3_swap_enum, RT5677_TDM1_CTRL1, + RT5677_IF1_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if1_adc3_swap_mux = + SOC_DAPM_ENUM("IF1 ADC3 Swap Source", rt5677_if1_adc3_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc4_swap_enum, RT5677_TDM1_CTRL1, + RT5677_IF1_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if1_adc4_swap_mux = + SOC_DAPM_ENUM("IF1 ADC4 Swap Source", rt5677_if1_adc4_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc1_swap_enum, RT5677_TDM2_CTRL1, + RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if2_adc1_swap_mux = + SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if2_adc1_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc2_swap_enum, RT5677_TDM2_CTRL1, + RT5677_IF2_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if2_adc2_swap_mux = + SOC_DAPM_ENUM("IF2 ADC2 Swap Source", rt5677_if2_adc2_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc3_swap_enum, RT5677_TDM2_CTRL1, + RT5677_IF2_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if2_adc3_swap_mux = + SOC_DAPM_ENUM("IF2 ADC3 Swap Source", rt5677_if2_adc3_swap_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc4_swap_enum, RT5677_TDM2_CTRL1, + RT5677_IF2_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src); + +static const struct snd_kcontrol_new rt5677_if2_adc4_swap_mux = + SOC_DAPM_ENUM("IF2 ADC4 Swap Source", rt5677_if2_adc4_swap_enum); + +/* TDM IF1 ADC Data Selection */ /* MX-3C [2:0] */ +static const char * const rt5677_if1_adc_tdm_swap_src[] = { + "1/2/3/4", "2/1/3/4", "2/3/1/4", "4/1/2/3", "1/3/2/4", "1/4/2/3", + "3/1/2/4", "3/4/1/2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_adc_tdm_swap_enum, RT5677_TDM1_CTRL2, + RT5677_IF1_ADC_CTRL_SFT, rt5677_if1_adc_tdm_swap_src); + +static const struct snd_kcontrol_new rt5677_if1_adc_tdm_swap_mux = + SOC_DAPM_ENUM("IF1 ADC TDM Swap Source", rt5677_if1_adc_tdm_swap_enum); + +/* TDM IF2 ADC Data Selection */ /* MX-41[2:0] */ +static const char * const rt5677_if2_adc_tdm_swap_src[] = { + "1/2/3/4", "2/1/3/4", "3/1/2/4", "4/1/2/3", "1/3/2/4", "1/4/2/3", + "2/3/1/4", "3/4/1/2" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_adc_tdm_swap_enum, RT5677_TDM2_CTRL2, + RT5677_IF2_ADC_CTRL_SFT, rt5677_if2_adc_tdm_swap_src); + +static const struct snd_kcontrol_new rt5677_if2_adc_tdm_swap_mux = + SOC_DAPM_ENUM("IF2 ADC TDM Swap Source", rt5677_if2_adc_tdm_swap_enum); + +/* TDM IF1/2 DAC Data Selection */ /* MX-3E[14:12][10:8][6:4][2:0] + MX-3F[14:12][10:8][6:4][2:0] + MX-43[14:12][10:8][6:4][2:0] + MX-44[14:12][10:8][6:4][2:0] */ +static const char * const rt5677_if12_dac_tdm_sel_src[] = { + "Slot0", "Slot1", "Slot2", "Slot3", "Slot4", "Slot5", "Slot6", "Slot7" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac0_tdm_sel_enum, RT5677_TDM1_CTRL4, + RT5677_IF1_DAC0_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac0_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC0 TDM Source", rt5677_if1_dac0_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac1_tdm_sel_enum, RT5677_TDM1_CTRL4, + RT5677_IF1_DAC1_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac1_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC1 TDM Source", rt5677_if1_dac1_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac2_tdm_sel_enum, RT5677_TDM1_CTRL4, + RT5677_IF1_DAC2_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac2_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC2 TDM Source", rt5677_if1_dac2_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac3_tdm_sel_enum, RT5677_TDM1_CTRL4, + RT5677_IF1_DAC3_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac3_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC3 TDM Source", rt5677_if1_dac3_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac4_tdm_sel_enum, RT5677_TDM1_CTRL5, + RT5677_IF1_DAC4_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac4_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC4 TDM Source", rt5677_if1_dac4_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac5_tdm_sel_enum, RT5677_TDM1_CTRL5, + RT5677_IF1_DAC5_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac5_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC5 TDM Source", rt5677_if1_dac5_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac6_tdm_sel_enum, RT5677_TDM1_CTRL5, + RT5677_IF1_DAC6_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac6_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC6 TDM Source", rt5677_if1_dac6_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if1_dac7_tdm_sel_enum, RT5677_TDM1_CTRL5, + RT5677_IF1_DAC7_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if1_dac7_tdm_sel_mux = + SOC_DAPM_ENUM("IF1 DAC7 TDM Source", rt5677_if1_dac7_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac0_tdm_sel_enum, RT5677_TDM2_CTRL4, + RT5677_IF2_DAC0_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac0_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC0 TDM Source", rt5677_if2_dac0_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac1_tdm_sel_enum, RT5677_TDM2_CTRL4, + RT5677_IF2_DAC1_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac1_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC1 TDM Source", rt5677_if2_dac1_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac2_tdm_sel_enum, RT5677_TDM2_CTRL4, + RT5677_IF2_DAC2_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac2_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC2 TDM Source", rt5677_if2_dac2_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac3_tdm_sel_enum, RT5677_TDM2_CTRL4, + RT5677_IF2_DAC3_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac3_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC3 TDM Source", rt5677_if2_dac3_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac4_tdm_sel_enum, RT5677_TDM2_CTRL5, + RT5677_IF2_DAC4_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac4_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC4 TDM Source", rt5677_if2_dac4_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac5_tdm_sel_enum, RT5677_TDM2_CTRL5, + RT5677_IF2_DAC5_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac5_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC5 TDM Source", rt5677_if2_dac5_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac6_tdm_sel_enum, RT5677_TDM2_CTRL5, + RT5677_IF2_DAC6_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac6_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC6 TDM Source", rt5677_if2_dac6_tdm_sel_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5677_if2_dac7_tdm_sel_enum, RT5677_TDM2_CTRL5, + RT5677_IF2_DAC7_SFT, rt5677_if12_dac_tdm_sel_src); + +static const struct snd_kcontrol_new rt5677_if2_dac7_tdm_sel_mux = + SOC_DAPM_ENUM("IF2 DAC7 TDM Source", rt5677_if2_dac7_tdm_sel_enum); + +static int rt5677_bst1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_BST1_P, RT5677_PWR_BST1_P); + break; + + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_BST1_P, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5677_bst2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_BST2_P, RT5677_PWR_BST2_P); + break; + + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_BST2_P, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5677_set_pll1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(rt5677->regmap, RT5677_PLL1_CTRL2, 0x2, 0x2); + break; + + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt5677->regmap, RT5677_PLL1_CTRL2, 0x2, 0x0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5677_set_pll2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(rt5677->regmap, RT5677_PLL2_CTRL2, 0x2, 0x2); + break; + + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt5677->regmap, RT5677_PLL2_CTRL2, 0x2, 0x0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_CLK_MB1 | RT5677_PWR_PP_MB1 | + RT5677_PWR_CLK_MB, RT5677_PWR_CLK_MB1 | + RT5677_PWR_PP_MB1 | RT5677_PWR_CLK_MB); + break; + + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_CLK_MB1 | RT5677_PWR_PP_MB1 | + RT5677_PWR_CLK_MB, 0); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + unsigned int value; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_read(rt5677->regmap, RT5677_TDM1_CTRL2, &value); + if (value & RT5677_IF1_ADC_CTRL_MASK) + regmap_update_bits(rt5677->regmap, RT5677_TDM1_CTRL1, + RT5677_IF1_ADC_MODE_MASK, + RT5677_IF1_ADC_MODE_TDM); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + unsigned int value; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_read(rt5677->regmap, RT5677_TDM2_CTRL2, &value); + if (value & RT5677_IF2_ADC_CTRL_MASK) + regmap_update_bits(rt5677->regmap, RT5677_TDM2_CTRL1, + RT5677_IF2_ADC_MODE_MASK, + RT5677_IF2_ADC_MODE_TDM); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5677_vref_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON && + !rt5677->is_vref_slow) { + mdelay(20); + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, + RT5677_PWR_FV1 | RT5677_PWR_FV2, + RT5677_PWR_FV1 | RT5677_PWR_FV2); + rt5677->is_vref_slow = true; + } + break; + + default: + return 0; + } + + return 0; +} + +static int rt5677_filter_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(50); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT, + 0, rt5677_set_pll1_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("PLL2", RT5677_PWR_ANLG2, RT5677_PWR_PLL2_BIT, + 0, rt5677_set_pll2_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5677_ASRC_1, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5677_ASRC_1, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S3 ASRC", 1, RT5677_ASRC_1, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("I2S4 ASRC", 1, RT5677_ASRC_1, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5677_ASRC_2, 14, 0, + rt5677_filter_power_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY_S("DAC MONO2 L ASRC", 1, RT5677_ASRC_2, 13, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO2 R ASRC", 1, RT5677_ASRC_2, 12, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO3 L ASRC", 1, RT5677_ASRC_1, 15, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO3 R ASRC", 1, RT5677_ASRC_1, 14, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO4 L ASRC", 1, RT5677_ASRC_1, 13, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DAC MONO4 R ASRC", 1, RT5677_ASRC_1, 12, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5677_ASRC_2, 11, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5677_ASRC_2, 10, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO3 ASRC", 1, RT5677_ASRC_2, 9, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC STO4 ASRC", 1, RT5677_ASRC_2, 8, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5677_ASRC_2, 7, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5677_ASRC_2, 6, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5677_ASRC_2, 5, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5677_ASRC_2, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO3 ASRC", 1, RT5677_ASRC_2, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO4 ASRC", 1, RT5677_ASRC_2, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5677_ASRC_2, 1, 0, NULL, + 0), + SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5677_ASRC_2, 0, 0, NULL, + 0), + + /* Input Side */ + /* micbias */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5677_PWR_ANLG2, RT5677_PWR_MB1_BIT, + 0, rt5677_set_micbias1_event, SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMU), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + SND_SOC_DAPM_INPUT("DMIC L2"), + SND_SOC_DAPM_INPUT("DMIC R2"), + SND_SOC_DAPM_INPUT("DMIC L3"), + SND_SOC_DAPM_INPUT("DMIC R3"), + SND_SOC_DAPM_INPUT("DMIC L4"), + SND_SOC_DAPM_INPUT("DMIC R4"), + + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + + SND_SOC_DAPM_INPUT("Haptic Generator"), + + SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC4", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC1 power", RT5677_DMIC_CTRL1, + RT5677_DMIC_1_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC2 power", RT5677_DMIC_CTRL1, + RT5677_DMIC_2_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC3 power", RT5677_DMIC_CTRL1, + RT5677_DMIC_3_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC4 power", RT5677_DMIC_CTRL2, + RT5677_DMIC_4_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + + /* Boost */ + SND_SOC_DAPM_PGA_E("BST1", RT5677_PWR_ANLG2, + RT5677_PWR_BST1_BIT, 0, NULL, 0, rt5677_bst1_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("BST2", RT5677_PWR_ANLG2, + RT5677_PWR_BST2_BIT, 0, NULL, 0, rt5677_bst2_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_ADC("ADC 2", NULL, SND_SOC_NOPM, + 0, 0), + SND_SOC_DAPM_PGA("ADC 1_2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("ADC 1 power", RT5677_PWR_DIG1, + RT5677_PWR_ADC_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC 2 power", RT5677_PWR_DIG1, + RT5677_PWR_ADC_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5677_PWR_DIG1, + RT5677_PWR_ADCFED1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC2 clock", RT5677_PWR_DIG1, + RT5677_PWR_ADCFED2_BIT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto1_dmic_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto1_adc1_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto1_adc2_mux), + SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto2_dmic_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto2_adc1_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto2_adc2_mux), + SND_SOC_DAPM_MUX("Stereo2 ADC LR Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto2_adc_lr_mux), + SND_SOC_DAPM_MUX("Stereo3 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto3_dmic_mux), + SND_SOC_DAPM_MUX("Stereo3 ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto3_adc1_mux), + SND_SOC_DAPM_MUX("Stereo3 ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto3_adc2_mux), + SND_SOC_DAPM_MUX("Stereo4 DMIC Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto4_dmic_mux), + SND_SOC_DAPM_MUX("Stereo4 ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto4_adc1_mux), + SND_SOC_DAPM_MUX("Stereo4 ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sto4_adc2_mux), + SND_SOC_DAPM_MUX("Mono DMIC L Mux", SND_SOC_NOPM, 0, 0, + &rt5677_mono_dmic_l_mux), + SND_SOC_DAPM_MUX("Mono DMIC R Mux", SND_SOC_NOPM, 0, 0, + &rt5677_mono_dmic_r_mux), + SND_SOC_DAPM_MUX("Mono ADC2 L Mux", SND_SOC_NOPM, 0, 0, + &rt5677_mono_adc2_l_mux), + SND_SOC_DAPM_MUX("Mono ADC1 L Mux", SND_SOC_NOPM, 0, 0, + &rt5677_mono_adc1_l_mux), + SND_SOC_DAPM_MUX("Mono ADC1 R Mux", SND_SOC_NOPM, 0, 0, + &rt5677_mono_adc1_r_mux), + SND_SOC_DAPM_MUX("Mono ADC2 R Mux", SND_SOC_NOPM, 0, 0, + &rt5677_mono_adc2_r_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5677_PWR_DIG2, + RT5677_PWR_ADC_S1F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5677_PWR_DIG2, + RT5677_PWR_ADC_S2F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("adc stereo3 filter", RT5677_PWR_DIG2, + RT5677_PWR_ADC_S3F_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("adc stereo4 filter", RT5677_PWR_DIG2, + RT5677_PWR_ADC_S4F_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5677_sto1_adc_l_mix, ARRAY_SIZE(rt5677_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5677_sto1_adc_r_mix, ARRAY_SIZE(rt5677_sto1_adc_r_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5677_sto2_adc_l_mix, ARRAY_SIZE(rt5677_sto2_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5677_sto2_adc_r_mix, ARRAY_SIZE(rt5677_sto2_adc_r_mix)), + SND_SOC_DAPM_MIXER("Sto3 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5677_sto3_adc_l_mix, ARRAY_SIZE(rt5677_sto3_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto3 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5677_sto3_adc_r_mix, ARRAY_SIZE(rt5677_sto3_adc_r_mix)), + SND_SOC_DAPM_MIXER("Sto4 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5677_sto4_adc_l_mix, ARRAY_SIZE(rt5677_sto4_adc_l_mix)), + SND_SOC_DAPM_MIXER("Sto4 ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5677_sto4_adc_r_mix, ARRAY_SIZE(rt5677_sto4_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("adc mono left filter", RT5677_PWR_DIG2, + RT5677_PWR_ADC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5677_mono_adc_l_mix, ARRAY_SIZE(rt5677_mono_adc_l_mix)), + SND_SOC_DAPM_SUPPLY("adc mono right filter", RT5677_PWR_DIG2, + RT5677_PWR_ADC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5677_mono_adc_r_mix, ARRAY_SIZE(rt5677_mono_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo3 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo3 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo3 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo4 ADC MIXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo4 ADC MIXR", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Stereo4 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DSP */ + SND_SOC_DAPM_MUX("IB9 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ib9_src_mux), + SND_SOC_DAPM_MUX("IB8 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ib8_src_mux), + SND_SOC_DAPM_MUX("IB7 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ib7_src_mux), + SND_SOC_DAPM_MUX("IB6 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ib6_src_mux), + SND_SOC_DAPM_MUX("IB45 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ib45_src_mux), + SND_SOC_DAPM_MUX("IB23 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ib23_src_mux), + SND_SOC_DAPM_MUX("IB01 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ib01_src_mux), + SND_SOC_DAPM_MUX("IB45 Bypass Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ib45_bypass_src_mux), + SND_SOC_DAPM_MUX("IB23 Bypass Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ib23_bypass_src_mux), + SND_SOC_DAPM_MUX("IB01 Bypass Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ib01_bypass_src_mux), + SND_SOC_DAPM_MUX("OB23 Bypass Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ob23_bypass_src_mux), + SND_SOC_DAPM_MUX("OB01 Bypass Mux", SND_SOC_NOPM, 0, 0, + &rt5677_ob01_bypass_src_mux), + + SND_SOC_DAPM_PGA("OB45", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("OB67", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("OutBound2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("OutBound3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("OutBound4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("OutBound5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("OutBound6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("OutBound7", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5677_PWR_DIG1, + RT5677_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC01", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC23", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC45", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC67", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("I2S2", RT5677_PWR_DIG1, + RT5677_PWR_I2S2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC01", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC23", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC45", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC67", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("I2S3", RT5677_PWR_DIG1, + RT5677_PWR_I2S3_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF3 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("I2S4", RT5677_PWR_DIG1, + RT5677_PWR_I2S4_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF4 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF4 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF4 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF4 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF4 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF4 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("SLB", RT5677_PWR_DIG1, + RT5677_PWR_SLB_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC01", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC23", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC45", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB DAC67", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB ADC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SLB ADC4", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1 ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc1_mux), + SND_SOC_DAPM_MUX("IF1 ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc2_mux), + SND_SOC_DAPM_MUX("IF1 ADC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc3_mux), + SND_SOC_DAPM_MUX("IF1 ADC4 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc4_mux), + SND_SOC_DAPM_MUX("IF1 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc1_swap_mux), + SND_SOC_DAPM_MUX("IF1 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc2_swap_mux), + SND_SOC_DAPM_MUX("IF1 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc3_swap_mux), + SND_SOC_DAPM_MUX("IF1 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc4_swap_mux), + SND_SOC_DAPM_MUX_E("IF1 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_adc_tdm_swap_mux, rt5677_if1_adc_tdm_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MUX("IF2 ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc1_mux), + SND_SOC_DAPM_MUX("IF2 ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc2_mux), + SND_SOC_DAPM_MUX("IF2 ADC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc3_mux), + SND_SOC_DAPM_MUX("IF2 ADC4 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc4_mux), + SND_SOC_DAPM_MUX("IF2 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc1_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc2_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc3_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc4_swap_mux), + SND_SOC_DAPM_MUX_E("IF2 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_adc_tdm_swap_mux, rt5677_if2_adc_tdm_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if3_adc_mux), + SND_SOC_DAPM_MUX("IF4 ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if4_adc_mux), + SND_SOC_DAPM_MUX("SLB ADC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_slb_adc1_mux), + SND_SOC_DAPM_MUX("SLB ADC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_slb_adc2_mux), + SND_SOC_DAPM_MUX("SLB ADC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_slb_adc3_mux), + SND_SOC_DAPM_MUX("SLB ADC4 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_slb_adc4_mux), + + SND_SOC_DAPM_MUX("IF1 DAC0 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac0_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac1_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac2_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac3_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC4 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac4_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC5 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac5_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC6 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac6_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF1 DAC7 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if1_dac7_tdm_sel_mux), + + SND_SOC_DAPM_MUX("IF2 DAC0 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac0_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac1_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC2 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac2_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC3 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac3_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC4 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac4_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC5 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac5_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC6 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac6_tdm_sel_mux), + SND_SOC_DAPM_MUX("IF2 DAC7 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_if2_dac7_tdm_sel_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF3RX", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF3TX", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF4RX", "AIF4 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SLBRX", "SLIMBus Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLBTX", "SLIMBus Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DSPTX", "DSP Buffer", 0, SND_SOC_NOPM, 0, 0), + + /* Sidetone Mux */ + SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, + &rt5677_sidetone_mux), + SND_SOC_DAPM_SUPPLY("Sidetone Power", RT5677_SIDETONE_CTRL, + RT5677_ST_EN_SFT, 0, NULL, 0), + + /* VAD Mux*/ + SND_SOC_DAPM_MUX("VAD ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5677_vad_src_mux), + + /* Tensilica DSP */ + SND_SOC_DAPM_PGA("Tensilica DSP", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("OB01 MIX", SND_SOC_NOPM, 0, 0, + rt5677_ob_01_mix, ARRAY_SIZE(rt5677_ob_01_mix)), + SND_SOC_DAPM_MIXER("OB23 MIX", SND_SOC_NOPM, 0, 0, + rt5677_ob_23_mix, ARRAY_SIZE(rt5677_ob_23_mix)), + SND_SOC_DAPM_MIXER("OB4 MIX", SND_SOC_NOPM, 0, 0, + rt5677_ob_4_mix, ARRAY_SIZE(rt5677_ob_4_mix)), + SND_SOC_DAPM_MIXER("OB5 MIX", SND_SOC_NOPM, 0, 0, + rt5677_ob_5_mix, ARRAY_SIZE(rt5677_ob_5_mix)), + SND_SOC_DAPM_MIXER("OB6 MIX", SND_SOC_NOPM, 0, 0, + rt5677_ob_6_mix, ARRAY_SIZE(rt5677_ob_6_mix)), + SND_SOC_DAPM_MIXER("OB7 MIX", SND_SOC_NOPM, 0, 0, + rt5677_ob_7_mix, ARRAY_SIZE(rt5677_ob_7_mix)), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5677_dac_l_mix, ARRAY_SIZE(rt5677_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5677_dac_r_mix, ARRAY_SIZE(rt5677_dac_r_mix)), + SND_SOC_DAPM_PGA("DAC1 FS", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DAC Mux */ + SND_SOC_DAPM_MUX("DAC1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_dac1_mux), + SND_SOC_DAPM_MUX("ADDA1 Mux", SND_SOC_NOPM, 0, 0, + &rt5677_adda1_mux), + SND_SOC_DAPM_MUX("DAC12 SRC Mux", SND_SOC_NOPM, 0, 0, + &rt5677_dac12_mux), + SND_SOC_DAPM_MUX("DAC3 SRC Mux", SND_SOC_NOPM, 0, 0, + &rt5677_dac3_mux), + + /* DAC2 channel Mux */ + SND_SOC_DAPM_MUX("DAC2 L Mux", SND_SOC_NOPM, 0, 0, + &rt5677_dac2_l_mux), + SND_SOC_DAPM_MUX("DAC2 R Mux", SND_SOC_NOPM, 0, 0, + &rt5677_dac2_r_mux), + + /* DAC3 channel Mux */ + SND_SOC_DAPM_MUX("DAC3 L Mux", SND_SOC_NOPM, 0, 0, + &rt5677_dac3_l_mux), + SND_SOC_DAPM_MUX("DAC3 R Mux", SND_SOC_NOPM, 0, 0, + &rt5677_dac3_r_mux), + + /* DAC4 channel Mux */ + SND_SOC_DAPM_MUX("DAC4 L Mux", SND_SOC_NOPM, 0, 0, + &rt5677_dac4_l_mux), + SND_SOC_DAPM_MUX("DAC4 R Mux", SND_SOC_NOPM, 0, 0, + &rt5677_dac4_r_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_S1F_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("dac mono2 left filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_M2F_L_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("dac mono2 right filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_M2F_R_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("dac mono3 left filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_M3F_L_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("dac mono3 right filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_M3F_R_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("dac mono4 left filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_M4F_L_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("dac mono4 right filter", RT5677_PWR_DIG2, + RT5677_PWR_DAC_M4F_R_BIT, 0, rt5677_filter_power_event, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5677_sto1_dac_l_mix, ARRAY_SIZE(rt5677_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5677_sto1_dac_r_mix, ARRAY_SIZE(rt5677_sto1_dac_r_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5677_mono_dac_l_mix, ARRAY_SIZE(rt5677_mono_dac_l_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5677_mono_dac_r_mix, ARRAY_SIZE(rt5677_mono_dac_r_mix)), + SND_SOC_DAPM_MIXER("DD1 MIXL", SND_SOC_NOPM, 0, 0, + rt5677_dd1_l_mix, ARRAY_SIZE(rt5677_dd1_l_mix)), + SND_SOC_DAPM_MIXER("DD1 MIXR", SND_SOC_NOPM, 0, 0, + rt5677_dd1_r_mix, ARRAY_SIZE(rt5677_dd1_r_mix)), + SND_SOC_DAPM_MIXER("DD2 MIXL", SND_SOC_NOPM, 0, 0, + rt5677_dd2_l_mix, ARRAY_SIZE(rt5677_dd2_l_mix)), + SND_SOC_DAPM_MIXER("DD2 MIXR", SND_SOC_NOPM, 0, 0, + rt5677_dd2_r_mix, ARRAY_SIZE(rt5677_dd2_r_mix)), + SND_SOC_DAPM_PGA("Stereo DAC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono DAC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DD1 MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DD2 MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC 1", NULL, RT5677_PWR_DIG1, + RT5677_PWR_DAC1_BIT, 0), + SND_SOC_DAPM_DAC("DAC 2", NULL, RT5677_PWR_DIG1, + RT5677_PWR_DAC2_BIT, 0), + SND_SOC_DAPM_DAC("DAC 3", NULL, RT5677_PWR_DIG1, + RT5677_PWR_DAC3_BIT, 0), + + /* PDM */ + SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5677_PWR_DIG2, + RT5677_PWR_PDM1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5677_PWR_DIG2, + RT5677_PWR_PDM2_BIT, 0, NULL, 0), + + SND_SOC_DAPM_MUX("PDM1 L Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM1_L_SFT, + 1, &rt5677_pdm1_l_mux), + SND_SOC_DAPM_MUX("PDM1 R Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM1_R_SFT, + 1, &rt5677_pdm1_r_mux), + SND_SOC_DAPM_MUX("PDM2 L Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM2_L_SFT, + 1, &rt5677_pdm2_l_mux), + SND_SOC_DAPM_MUX("PDM2 R Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM2_R_SFT, + 1, &rt5677_pdm2_r_mux), + + SND_SOC_DAPM_PGA_S("LOUT1 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA_S("LOUT2 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA_S("LOUT3 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT, + 0, NULL, 0), + + SND_SOC_DAPM_PGA_S("LOUT1 vref", 1, SND_SOC_NOPM, 0, 0, + rt5677_vref_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("LOUT2 vref", 1, SND_SOC_NOPM, 0, 0, + rt5677_vref_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("LOUT3 vref", 1, SND_SOC_NOPM, 0, 0, + rt5677_vref_event, SND_SOC_DAPM_POST_PMU), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("LOUT3"), + SND_SOC_DAPM_OUTPUT("PDM1L"), + SND_SOC_DAPM_OUTPUT("PDM1R"), + SND_SOC_DAPM_OUTPUT("PDM2L"), + SND_SOC_DAPM_OUTPUT("PDM2R"), + + SND_SOC_DAPM_POST("vref", rt5677_vref_event), +}; + +static const struct snd_soc_dapm_route rt5677_dapm_routes[] = { + { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", rt5677_dmic_use_asrc }, + { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", rt5677_dmic_use_asrc }, + { "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", rt5677_dmic_use_asrc }, + { "Stereo4 DMIC Mux", NULL, "DMIC STO4 ASRC", rt5677_dmic_use_asrc }, + { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", rt5677_dmic_use_asrc }, + { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", rt5677_dmic_use_asrc }, + { "I2S1", NULL, "I2S1 ASRC", can_use_asrc}, + { "I2S2", NULL, "I2S2 ASRC", can_use_asrc}, + { "I2S3", NULL, "I2S3 ASRC", can_use_asrc}, + { "I2S4", NULL, "I2S4 ASRC", can_use_asrc}, + + { "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc }, + { "dac mono2 left filter", NULL, "DAC MONO2 L ASRC", is_using_asrc }, + { "dac mono2 right filter", NULL, "DAC MONO2 R ASRC", is_using_asrc }, + { "dac mono3 left filter", NULL, "DAC MONO3 L ASRC", is_using_asrc }, + { "dac mono3 right filter", NULL, "DAC MONO3 R ASRC", is_using_asrc }, + { "dac mono4 left filter", NULL, "DAC MONO4 L ASRC", is_using_asrc }, + { "dac mono4 right filter", NULL, "DAC MONO4 R ASRC", is_using_asrc }, + { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc }, + { "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc }, + { "adc stereo3 filter", NULL, "ADC STO3 ASRC", is_using_asrc }, + { "adc stereo4 filter", NULL, "ADC STO4 ASRC", is_using_asrc }, + { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc }, + { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc }, + + { "DMIC1", NULL, "DMIC L1" }, + { "DMIC1", NULL, "DMIC R1" }, + { "DMIC2", NULL, "DMIC L2" }, + { "DMIC2", NULL, "DMIC R2" }, + { "DMIC3", NULL, "DMIC L3" }, + { "DMIC3", NULL, "DMIC R3" }, + { "DMIC4", NULL, "DMIC L4" }, + { "DMIC4", NULL, "DMIC R4" }, + + { "DMIC L1", NULL, "DMIC CLK" }, + { "DMIC R1", NULL, "DMIC CLK" }, + { "DMIC L2", NULL, "DMIC CLK" }, + { "DMIC R2", NULL, "DMIC CLK" }, + { "DMIC L3", NULL, "DMIC CLK" }, + { "DMIC R3", NULL, "DMIC CLK" }, + { "DMIC L4", NULL, "DMIC CLK" }, + { "DMIC R4", NULL, "DMIC CLK" }, + + { "DMIC L1", NULL, "DMIC1 power" }, + { "DMIC R1", NULL, "DMIC1 power" }, + { "DMIC L3", NULL, "DMIC3 power" }, + { "DMIC R3", NULL, "DMIC3 power" }, + { "DMIC L4", NULL, "DMIC4 power" }, + { "DMIC R4", NULL, "DMIC4 power" }, + + { "BST1", NULL, "IN1P" }, + { "BST1", NULL, "IN1N" }, + { "BST2", NULL, "IN2P" }, + { "BST2", NULL, "IN2N" }, + + { "IN1P", NULL, "MICBIAS1" }, + { "IN1N", NULL, "MICBIAS1" }, + { "IN2P", NULL, "MICBIAS1" }, + { "IN2N", NULL, "MICBIAS1" }, + + { "ADC 1", NULL, "BST1" }, + { "ADC 1", NULL, "ADC 1 power" }, + { "ADC 1", NULL, "ADC1 clock" }, + { "ADC 2", NULL, "BST2" }, + { "ADC 2", NULL, "ADC 2 power" }, + { "ADC 2", NULL, "ADC2 clock" }, + + { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" }, + { "Stereo1 DMIC Mux", "DMIC3", "DMIC3" }, + { "Stereo1 DMIC Mux", "DMIC4", "DMIC4" }, + + { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" }, + { "Stereo2 DMIC Mux", "DMIC3", "DMIC3" }, + { "Stereo2 DMIC Mux", "DMIC4", "DMIC4" }, + + { "Stereo3 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo3 DMIC Mux", "DMIC2", "DMIC2" }, + { "Stereo3 DMIC Mux", "DMIC3", "DMIC3" }, + { "Stereo3 DMIC Mux", "DMIC4", "DMIC4" }, + + { "Stereo4 DMIC Mux", "DMIC1", "DMIC1" }, + { "Stereo4 DMIC Mux", "DMIC2", "DMIC2" }, + { "Stereo4 DMIC Mux", "DMIC3", "DMIC3" }, + { "Stereo4 DMIC Mux", "DMIC4", "DMIC4" }, + + { "Mono DMIC L Mux", "DMIC1", "DMIC1" }, + { "Mono DMIC L Mux", "DMIC2", "DMIC2" }, + { "Mono DMIC L Mux", "DMIC3", "DMIC3" }, + { "Mono DMIC L Mux", "DMIC4", "DMIC4" }, + + { "Mono DMIC R Mux", "DMIC1", "DMIC1" }, + { "Mono DMIC R Mux", "DMIC2", "DMIC2" }, + { "Mono DMIC R Mux", "DMIC3", "DMIC3" }, + { "Mono DMIC R Mux", "DMIC4", "DMIC4" }, + + { "ADC 1_2", NULL, "ADC 1" }, + { "ADC 1_2", NULL, "ADC 2" }, + + { "Stereo1 ADC1 Mux", "DD MIX1", "DD1 MIX" }, + { "Stereo1 ADC1 Mux", "ADC1/2", "ADC 1_2" }, + { "Stereo1 ADC1 Mux", "Stereo DAC MIX", "Stereo DAC MIX" }, + + { "Stereo1 ADC2 Mux", "DD MIX1", "DD1 MIX" }, + { "Stereo1 ADC2 Mux", "DMIC", "Stereo1 DMIC Mux" }, + { "Stereo1 ADC2 Mux", "Stereo DAC MIX", "Stereo DAC MIX" }, + + { "Stereo2 ADC1 Mux", "DD MIX1", "DD1 MIX" }, + { "Stereo2 ADC1 Mux", "ADC1/2", "ADC 1_2" }, + { "Stereo2 ADC1 Mux", "Stereo DAC MIX", "Stereo DAC MIX" }, + + { "Stereo2 ADC2 Mux", "DD MIX1", "DD1 MIX" }, + { "Stereo2 ADC2 Mux", "DMIC", "Stereo2 DMIC Mux" }, + { "Stereo2 ADC2 Mux", "Stereo DAC MIX", "Stereo DAC MIX" }, + + { "Stereo3 ADC1 Mux", "DD MIX1", "DD1 MIX" }, + { "Stereo3 ADC1 Mux", "ADC1/2", "ADC 1_2" }, + { "Stereo3 ADC1 Mux", "Stereo DAC MIX", "Stereo DAC MIX" }, + + { "Stereo3 ADC2 Mux", "DD MIX1", "DD1 MIX" }, + { "Stereo3 ADC2 Mux", "DMIC", "Stereo3 DMIC Mux" }, + { "Stereo3 ADC2 Mux", "Stereo DAC MIX", "Stereo DAC MIX" }, + + { "Stereo4 ADC1 Mux", "DD MIX1", "DD1 MIX" }, + { "Stereo4 ADC1 Mux", "ADC1/2", "ADC 1_2" }, + { "Stereo4 ADC1 Mux", "DD MIX2", "DD2 MIX" }, + + { "Stereo4 ADC2 Mux", "DD MIX1", "DD1 MIX" }, + { "Stereo4 ADC2 Mux", "DMIC", "Stereo3 DMIC Mux" }, + { "Stereo4 ADC2 Mux", "DD MIX2", "DD2 MIX" }, + + { "Mono ADC2 L Mux", "DD MIX1L", "DD1 MIXL" }, + { "Mono ADC2 L Mux", "DMIC", "Mono DMIC L Mux" }, + { "Mono ADC2 L Mux", "MONO DAC MIXL", "Mono DAC MIXL" }, + + { "Mono ADC1 L Mux", "DD MIX1L", "DD1 MIXL" }, + { "Mono ADC1 L Mux", "ADC1", "ADC 1" }, + { "Mono ADC1 L Mux", "MONO DAC MIXL", "Mono DAC MIXL" }, + + { "Mono ADC1 R Mux", "DD MIX1R", "DD1 MIXR" }, + { "Mono ADC1 R Mux", "ADC2", "ADC 2" }, + { "Mono ADC1 R Mux", "MONO DAC MIXR", "Mono DAC MIXR" }, + + { "Mono ADC2 R Mux", "DD MIX1R", "DD1 MIXR" }, + { "Mono ADC2 R Mux", "DMIC", "Mono DMIC R Mux" }, + { "Mono ADC2 R Mux", "MONO DAC MIXR", "Mono DAC MIXR" }, + + { "Sto1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC1 Mux" }, + { "Sto1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC2 Mux" }, + { "Sto1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC1 Mux" }, + { "Sto1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC2 Mux" }, + + { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" }, + { "Stereo1 ADC MIXL", NULL, "adc stereo1 filter" }, + { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" }, + { "Stereo1 ADC MIXR", NULL, "adc stereo1 filter" }, + { "adc stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" }, + { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" }, + + { "Sto2 ADC MIXL", "ADC1 Switch", "Stereo2 ADC1 Mux" }, + { "Sto2 ADC MIXL", "ADC2 Switch", "Stereo2 ADC2 Mux" }, + { "Sto2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC1 Mux" }, + { "Sto2 ADC MIXR", "ADC2 Switch", "Stereo2 ADC2 Mux" }, + + { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXL" }, + { "Sto2 ADC LR MIX", NULL, "Sto2 ADC MIXR" }, + + { "Stereo2 ADC LR Mux", "L", "Sto2 ADC MIXL" }, + { "Stereo2 ADC LR Mux", "LR", "Sto2 ADC LR MIX" }, + + { "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" }, + { "Stereo2 ADC MIXL", NULL, "adc stereo2 filter" }, + { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" }, + { "Stereo2 ADC MIXR", NULL, "adc stereo2 filter" }, + { "adc stereo2 filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" }, + { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" }, + + { "Sto3 ADC MIXL", "ADC1 Switch", "Stereo3 ADC1 Mux" }, + { "Sto3 ADC MIXL", "ADC2 Switch", "Stereo3 ADC2 Mux" }, + { "Sto3 ADC MIXR", "ADC1 Switch", "Stereo3 ADC1 Mux" }, + { "Sto3 ADC MIXR", "ADC2 Switch", "Stereo3 ADC2 Mux" }, + + { "Stereo3 ADC MIXL", NULL, "Sto3 ADC MIXL" }, + { "Stereo3 ADC MIXL", NULL, "adc stereo3 filter" }, + { "Stereo3 ADC MIXR", NULL, "Sto3 ADC MIXR" }, + { "Stereo3 ADC MIXR", NULL, "adc stereo3 filter" }, + { "adc stereo3 filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo3 ADC MIX", NULL, "Stereo3 ADC MIXL" }, + { "Stereo3 ADC MIX", NULL, "Stereo3 ADC MIXR" }, + + { "Sto4 ADC MIXL", "ADC1 Switch", "Stereo4 ADC1 Mux" }, + { "Sto4 ADC MIXL", "ADC2 Switch", "Stereo4 ADC2 Mux" }, + { "Sto4 ADC MIXR", "ADC1 Switch", "Stereo4 ADC1 Mux" }, + { "Sto4 ADC MIXR", "ADC2 Switch", "Stereo4 ADC2 Mux" }, + + { "Stereo4 ADC MIXL", NULL, "Sto4 ADC MIXL" }, + { "Stereo4 ADC MIXL", NULL, "adc stereo4 filter" }, + { "Stereo4 ADC MIXR", NULL, "Sto4 ADC MIXR" }, + { "Stereo4 ADC MIXR", NULL, "adc stereo4 filter" }, + { "adc stereo4 filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo4 ADC MIX", NULL, "Stereo4 ADC MIXL" }, + { "Stereo4 ADC MIX", NULL, "Stereo4 ADC MIXR" }, + + { "Mono ADC MIXL", "ADC1 Switch", "Mono ADC1 L Mux" }, + { "Mono ADC MIXL", "ADC2 Switch", "Mono ADC2 L Mux" }, + { "Mono ADC MIXL", NULL, "adc mono left filter" }, + { "adc mono left filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono ADC MIXR", "ADC1 Switch", "Mono ADC1 R Mux" }, + { "Mono ADC MIXR", "ADC2 Switch", "Mono ADC2 R Mux" }, + { "Mono ADC MIXR", NULL, "adc mono right filter" }, + { "adc mono right filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono ADC MIX", NULL, "Mono ADC MIXL" }, + { "Mono ADC MIX", NULL, "Mono ADC MIXR" }, + + { "VAD ADC Mux", "STO1 ADC MIX L", "Stereo1 ADC MIXL" }, + { "VAD ADC Mux", "MONO ADC MIX L", "Mono ADC MIXL" }, + { "VAD ADC Mux", "MONO ADC MIX R", "Mono ADC MIXR" }, + { "VAD ADC Mux", "STO2 ADC MIX L", "Stereo2 ADC MIXL" }, + { "VAD ADC Mux", "STO3 ADC MIX L", "Stereo3 ADC MIXL" }, + + { "IF1 ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" }, + { "IF1 ADC1 Mux", "OB01", "OB01 Bypass Mux" }, + { "IF1 ADC1 Mux", "VAD ADC", "VAD ADC Mux" }, + + { "IF1 ADC2 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" }, + { "IF1 ADC2 Mux", "OB23", "OB23 Bypass Mux" }, + + { "IF1 ADC3 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" }, + { "IF1 ADC3 Mux", "MONO ADC MIX", "Mono ADC MIX" }, + { "IF1 ADC3 Mux", "OB45", "OB45" }, + + { "IF1 ADC4 Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" }, + { "IF1 ADC4 Mux", "OB67", "OB67" }, + { "IF1 ADC4 Mux", "OB01", "OB01 Bypass Mux" }, + + { "IF1 ADC1 Swap Mux", "L/R", "IF1 ADC1 Mux" }, + { "IF1 ADC1 Swap Mux", "R/L", "IF1 ADC1 Mux" }, + { "IF1 ADC1 Swap Mux", "L/L", "IF1 ADC1 Mux" }, + { "IF1 ADC1 Swap Mux", "R/R", "IF1 ADC1 Mux" }, + + { "IF1 ADC2 Swap Mux", "L/R", "IF1 ADC2 Mux" }, + { "IF1 ADC2 Swap Mux", "R/L", "IF1 ADC2 Mux" }, + { "IF1 ADC2 Swap Mux", "L/L", "IF1 ADC2 Mux" }, + { "IF1 ADC2 Swap Mux", "R/R", "IF1 ADC2 Mux" }, + + { "IF1 ADC3 Swap Mux", "L/R", "IF1 ADC3 Mux" }, + { "IF1 ADC3 Swap Mux", "R/L", "IF1 ADC3 Mux" }, + { "IF1 ADC3 Swap Mux", "L/L", "IF1 ADC3 Mux" }, + { "IF1 ADC3 Swap Mux", "R/R", "IF1 ADC3 Mux" }, + + { "IF1 ADC4 Swap Mux", "L/R", "IF1 ADC4 Mux" }, + { "IF1 ADC4 Swap Mux", "R/L", "IF1 ADC4 Mux" }, + { "IF1 ADC4 Swap Mux", "L/L", "IF1 ADC4 Mux" }, + { "IF1 ADC4 Swap Mux", "R/R", "IF1 ADC4 Mux" }, + + { "IF1 ADC", NULL, "IF1 ADC1 Swap Mux" }, + { "IF1 ADC", NULL, "IF1 ADC2 Swap Mux" }, + { "IF1 ADC", NULL, "IF1 ADC3 Swap Mux" }, + { "IF1 ADC", NULL, "IF1 ADC4 Swap Mux" }, + + { "IF1 ADC TDM Swap Mux", "1/2/3/4", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "2/1/3/4", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "2/3/1/4", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "4/1/2/3", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "1/3/2/4", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "1/4/2/3", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "3/1/2/4", "IF1 ADC" }, + { "IF1 ADC TDM Swap Mux", "3/4/1/2", "IF1 ADC" }, + + { "AIF1TX", NULL, "I2S1" }, + { "AIF1TX", NULL, "IF1 ADC TDM Swap Mux" }, + + { "IF2 ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" }, + { "IF2 ADC1 Mux", "OB01", "OB01 Bypass Mux" }, + { "IF2 ADC1 Mux", "VAD ADC", "VAD ADC Mux" }, + + { "IF2 ADC2 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" }, + { "IF2 ADC2 Mux", "OB23", "OB23 Bypass Mux" }, + + { "IF2 ADC3 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" }, + { "IF2 ADC3 Mux", "MONO ADC MIX", "Mono ADC MIX" }, + { "IF2 ADC3 Mux", "OB45", "OB45" }, + + { "IF2 ADC4 Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" }, + { "IF2 ADC4 Mux", "OB67", "OB67" }, + { "IF2 ADC4 Mux", "OB01", "OB01 Bypass Mux" }, + + { "IF2 ADC1 Swap Mux", "L/R", "IF2 ADC1 Mux" }, + { "IF2 ADC1 Swap Mux", "R/L", "IF2 ADC1 Mux" }, + { "IF2 ADC1 Swap Mux", "L/L", "IF2 ADC1 Mux" }, + { "IF2 ADC1 Swap Mux", "R/R", "IF2 ADC1 Mux" }, + + { "IF2 ADC2 Swap Mux", "L/R", "IF2 ADC2 Mux" }, + { "IF2 ADC2 Swap Mux", "R/L", "IF2 ADC2 Mux" }, + { "IF2 ADC2 Swap Mux", "L/L", "IF2 ADC2 Mux" }, + { "IF2 ADC2 Swap Mux", "R/R", "IF2 ADC2 Mux" }, + + { "IF2 ADC3 Swap Mux", "L/R", "IF2 ADC3 Mux" }, + { "IF2 ADC3 Swap Mux", "R/L", "IF2 ADC3 Mux" }, + { "IF2 ADC3 Swap Mux", "L/L", "IF2 ADC3 Mux" }, + { "IF2 ADC3 Swap Mux", "R/R", "IF2 ADC3 Mux" }, + + { "IF2 ADC4 Swap Mux", "L/R", "IF2 ADC4 Mux" }, + { "IF2 ADC4 Swap Mux", "R/L", "IF2 ADC4 Mux" }, + { "IF2 ADC4 Swap Mux", "L/L", "IF2 ADC4 Mux" }, + { "IF2 ADC4 Swap Mux", "R/R", "IF2 ADC4 Mux" }, + + { "IF2 ADC", NULL, "IF2 ADC1 Swap Mux" }, + { "IF2 ADC", NULL, "IF2 ADC2 Swap Mux" }, + { "IF2 ADC", NULL, "IF2 ADC3 Swap Mux" }, + { "IF2 ADC", NULL, "IF2 ADC4 Swap Mux" }, + + { "IF2 ADC TDM Swap Mux", "1/2/3/4", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "2/1/3/4", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "3/1/2/4", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "4/1/2/3", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "1/3/2/4", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "1/4/2/3", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "2/3/1/4", "IF2 ADC" }, + { "IF2 ADC TDM Swap Mux", "3/4/1/2", "IF2 ADC" }, + + { "AIF2TX", NULL, "I2S2" }, + { "AIF2TX", NULL, "IF2 ADC TDM Swap Mux" }, + + { "IF3 ADC Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" }, + { "IF3 ADC Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" }, + { "IF3 ADC Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" }, + { "IF3 ADC Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" }, + { "IF3 ADC Mux", "MONO ADC MIX", "Mono ADC MIX" }, + { "IF3 ADC Mux", "OB01", "OB01 Bypass Mux" }, + { "IF3 ADC Mux", "OB23", "OB23 Bypass Mux" }, + { "IF3 ADC Mux", "VAD ADC", "VAD ADC Mux" }, + + { "AIF3TX", NULL, "I2S3" }, + { "AIF3TX", NULL, "IF3 ADC Mux" }, + + { "IF4 ADC Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" }, + { "IF4 ADC Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" }, + { "IF4 ADC Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" }, + { "IF4 ADC Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" }, + { "IF4 ADC Mux", "MONO ADC MIX", "Mono ADC MIX" }, + { "IF4 ADC Mux", "OB01", "OB01 Bypass Mux" }, + { "IF4 ADC Mux", "OB23", "OB23 Bypass Mux" }, + { "IF4 ADC Mux", "VAD ADC", "VAD ADC Mux" }, + + { "AIF4TX", NULL, "I2S4" }, + { "AIF4TX", NULL, "IF4 ADC Mux" }, + + { "SLB ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" }, + { "SLB ADC1 Mux", "OB01", "OB01 Bypass Mux" }, + { "SLB ADC1 Mux", "VAD ADC", "VAD ADC Mux" }, + + { "SLB ADC2 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" }, + { "SLB ADC2 Mux", "OB23", "OB23 Bypass Mux" }, + + { "SLB ADC3 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" }, + { "SLB ADC3 Mux", "MONO ADC MIX", "Mono ADC MIX" }, + { "SLB ADC3 Mux", "OB45", "OB45" }, + + { "SLB ADC4 Mux", "STO4 ADC MIX", "Stereo4 ADC MIX" }, + { "SLB ADC4 Mux", "OB67", "OB67" }, + { "SLB ADC4 Mux", "OB01", "OB01 Bypass Mux" }, + + { "SLBTX", NULL, "SLB" }, + { "SLBTX", NULL, "SLB ADC1 Mux" }, + { "SLBTX", NULL, "SLB ADC2 Mux" }, + { "SLBTX", NULL, "SLB ADC3 Mux" }, + { "SLBTX", NULL, "SLB ADC4 Mux" }, + + { "DSPTX", NULL, "IB01 Bypass Mux" }, + + { "IB01 Mux", "IF1 DAC 01", "IF1 DAC01" }, + { "IB01 Mux", "IF2 DAC 01", "IF2 DAC01" }, + { "IB01 Mux", "SLB DAC 01", "SLB DAC01" }, + { "IB01 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" }, + /* The IB01 Mux controls the source for InBound0 and InBound1. + * When the mux option "VAD ADC/DAC1 FS" is selected, "VAD ADC" goes to + * InBound0 and "DAC1 FS" goes to InBound1. "VAD ADC" is used for + * hotwording. "DAC1 FS" is not used currently. + * + * Creating a common widget node for "VAD ADC" + "DAC1 FS" and + * connecting the common widget to IB01 Mux causes the issue where + * there is an active path going from system playback -> "DAC1 FS" -> + * IB01 Mux -> DSP Buffer -> hotword stream. This wrong path confuses + * DAPM. Therefore "DAC1 FS" is ignored for now. + */ + { "IB01 Mux", "VAD ADC/DAC1 FS", "VAD ADC Mux" }, + + { "IB01 Bypass Mux", "Bypass", "IB01 Mux" }, + { "IB01 Bypass Mux", "Pass SRC", "IB01 Mux" }, + + { "IB23 Mux", "IF1 DAC 23", "IF1 DAC23" }, + { "IB23 Mux", "IF2 DAC 23", "IF2 DAC23" }, + { "IB23 Mux", "SLB DAC 23", "SLB DAC23" }, + { "IB23 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" }, + { "IB23 Mux", "DAC1 FS", "DAC1 FS" }, + { "IB23 Mux", "IF4 DAC", "IF4 DAC" }, + + { "IB23 Bypass Mux", "Bypass", "IB23 Mux" }, + { "IB23 Bypass Mux", "Pass SRC", "IB23 Mux" }, + + { "IB45 Mux", "IF1 DAC 45", "IF1 DAC45" }, + { "IB45 Mux", "IF2 DAC 45", "IF2 DAC45" }, + { "IB45 Mux", "SLB DAC 45", "SLB DAC45" }, + { "IB45 Mux", "STO3 ADC MIX", "Stereo3 ADC MIX" }, + { "IB45 Mux", "IF3 DAC", "IF3 DAC" }, + + { "IB45 Bypass Mux", "Bypass", "IB45 Mux" }, + { "IB45 Bypass Mux", "Pass SRC", "IB45 Mux" }, + + { "IB6 Mux", "IF1 DAC 6", "IF1 DAC6 Mux" }, + { "IB6 Mux", "IF2 DAC 6", "IF2 DAC6 Mux" }, + { "IB6 Mux", "SLB DAC 6", "SLB DAC6" }, + { "IB6 Mux", "STO4 ADC MIX L", "Stereo4 ADC MIXL" }, + { "IB6 Mux", "IF4 DAC L", "IF4 DAC L" }, + { "IB6 Mux", "STO1 ADC MIX L", "Stereo1 ADC MIXL" }, + { "IB6 Mux", "STO2 ADC MIX L", "Stereo2 ADC MIXL" }, + { "IB6 Mux", "STO3 ADC MIX L", "Stereo3 ADC MIXL" }, + + { "IB7 Mux", "IF1 DAC 7", "IF1 DAC7 Mux" }, + { "IB7 Mux", "IF2 DAC 7", "IF2 DAC7 Mux" }, + { "IB7 Mux", "SLB DAC 7", "SLB DAC7" }, + { "IB7 Mux", "STO4 ADC MIX R", "Stereo4 ADC MIXR" }, + { "IB7 Mux", "IF4 DAC R", "IF4 DAC R" }, + { "IB7 Mux", "STO1 ADC MIX R", "Stereo1 ADC MIXR" }, + { "IB7 Mux", "STO2 ADC MIX R", "Stereo2 ADC MIXR" }, + { "IB7 Mux", "STO3 ADC MIX R", "Stereo3 ADC MIXR" }, + + { "IB8 Mux", "STO1 ADC MIX L", "Stereo1 ADC MIXL" }, + { "IB8 Mux", "STO2 ADC MIX L", "Stereo2 ADC MIXL" }, + { "IB8 Mux", "STO3 ADC MIX L", "Stereo3 ADC MIXL" }, + { "IB8 Mux", "STO4 ADC MIX L", "Stereo4 ADC MIXL" }, + { "IB8 Mux", "MONO ADC MIX L", "Mono ADC MIXL" }, + { "IB8 Mux", "DACL1 FS", "DAC1 MIXL" }, + + { "IB9 Mux", "STO1 ADC MIX R", "Stereo1 ADC MIXR" }, + { "IB9 Mux", "STO2 ADC MIX R", "Stereo2 ADC MIXR" }, + { "IB9 Mux", "STO3 ADC MIX R", "Stereo3 ADC MIXR" }, + { "IB9 Mux", "STO4 ADC MIX R", "Stereo4 ADC MIXR" }, + { "IB9 Mux", "MONO ADC MIX R", "Mono ADC MIXR" }, + { "IB9 Mux", "DACR1 FS", "DAC1 MIXR" }, + { "IB9 Mux", "DAC1 FS", "DAC1 FS" }, + + { "OB01 MIX", "IB01 Switch", "IB01 Bypass Mux" }, + { "OB01 MIX", "IB23 Switch", "IB23 Bypass Mux" }, + { "OB01 MIX", "IB45 Switch", "IB45 Bypass Mux" }, + { "OB01 MIX", "IB6 Switch", "IB6 Mux" }, + { "OB01 MIX", "IB7 Switch", "IB7 Mux" }, + { "OB01 MIX", "IB8 Switch", "IB8 Mux" }, + { "OB01 MIX", "IB9 Switch", "IB9 Mux" }, + + { "OB23 MIX", "IB01 Switch", "IB01 Bypass Mux" }, + { "OB23 MIX", "IB23 Switch", "IB23 Bypass Mux" }, + { "OB23 MIX", "IB45 Switch", "IB45 Bypass Mux" }, + { "OB23 MIX", "IB6 Switch", "IB6 Mux" }, + { "OB23 MIX", "IB7 Switch", "IB7 Mux" }, + { "OB23 MIX", "IB8 Switch", "IB8 Mux" }, + { "OB23 MIX", "IB9 Switch", "IB9 Mux" }, + + { "OB4 MIX", "IB01 Switch", "IB01 Bypass Mux" }, + { "OB4 MIX", "IB23 Switch", "IB23 Bypass Mux" }, + { "OB4 MIX", "IB45 Switch", "IB45 Bypass Mux" }, + { "OB4 MIX", "IB6 Switch", "IB6 Mux" }, + { "OB4 MIX", "IB7 Switch", "IB7 Mux" }, + { "OB4 MIX", "IB8 Switch", "IB8 Mux" }, + { "OB4 MIX", "IB9 Switch", "IB9 Mux" }, + + { "OB5 MIX", "IB01 Switch", "IB01 Bypass Mux" }, + { "OB5 MIX", "IB23 Switch", "IB23 Bypass Mux" }, + { "OB5 MIX", "IB45 Switch", "IB45 Bypass Mux" }, + { "OB5 MIX", "IB6 Switch", "IB6 Mux" }, + { "OB5 MIX", "IB7 Switch", "IB7 Mux" }, + { "OB5 MIX", "IB8 Switch", "IB8 Mux" }, + { "OB5 MIX", "IB9 Switch", "IB9 Mux" }, + + { "OB6 MIX", "IB01 Switch", "IB01 Bypass Mux" }, + { "OB6 MIX", "IB23 Switch", "IB23 Bypass Mux" }, + { "OB6 MIX", "IB45 Switch", "IB45 Bypass Mux" }, + { "OB6 MIX", "IB6 Switch", "IB6 Mux" }, + { "OB6 MIX", "IB7 Switch", "IB7 Mux" }, + { "OB6 MIX", "IB8 Switch", "IB8 Mux" }, + { "OB6 MIX", "IB9 Switch", "IB9 Mux" }, + + { "OB7 MIX", "IB01 Switch", "IB01 Bypass Mux" }, + { "OB7 MIX", "IB23 Switch", "IB23 Bypass Mux" }, + { "OB7 MIX", "IB45 Switch", "IB45 Bypass Mux" }, + { "OB7 MIX", "IB6 Switch", "IB6 Mux" }, + { "OB7 MIX", "IB7 Switch", "IB7 Mux" }, + { "OB7 MIX", "IB8 Switch", "IB8 Mux" }, + { "OB7 MIX", "IB9 Switch", "IB9 Mux" }, + + { "OB01 Bypass Mux", "Bypass", "OB01 MIX" }, + { "OB01 Bypass Mux", "Pass SRC", "OB01 MIX" }, + { "OB23 Bypass Mux", "Bypass", "OB23 MIX" }, + { "OB23 Bypass Mux", "Pass SRC", "OB23 MIX" }, + + { "OutBound2", NULL, "OB23 Bypass Mux" }, + { "OutBound3", NULL, "OB23 Bypass Mux" }, + { "OutBound4", NULL, "OB4 MIX" }, + { "OutBound5", NULL, "OB5 MIX" }, + { "OutBound6", NULL, "OB6 MIX" }, + { "OutBound7", NULL, "OB7 MIX" }, + + { "OB45", NULL, "OutBound4" }, + { "OB45", NULL, "OutBound5" }, + { "OB67", NULL, "OutBound6" }, + { "OB67", NULL, "OutBound7" }, + + { "IF1 DAC0", NULL, "AIF1RX" }, + { "IF1 DAC1", NULL, "AIF1RX" }, + { "IF1 DAC2", NULL, "AIF1RX" }, + { "IF1 DAC3", NULL, "AIF1RX" }, + { "IF1 DAC4", NULL, "AIF1RX" }, + { "IF1 DAC5", NULL, "AIF1RX" }, + { "IF1 DAC6", NULL, "AIF1RX" }, + { "IF1 DAC7", NULL, "AIF1RX" }, + { "IF1 DAC0", NULL, "I2S1" }, + { "IF1 DAC1", NULL, "I2S1" }, + { "IF1 DAC2", NULL, "I2S1" }, + { "IF1 DAC3", NULL, "I2S1" }, + { "IF1 DAC4", NULL, "I2S1" }, + { "IF1 DAC5", NULL, "I2S1" }, + { "IF1 DAC6", NULL, "I2S1" }, + { "IF1 DAC7", NULL, "I2S1" }, + + { "IF1 DAC0 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC0 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC0 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC0 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC0 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC0 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC0 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC0 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC1 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC1 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC1 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC1 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC1 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC1 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC1 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC1 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC2 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC2 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC2 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC2 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC2 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC2 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC2 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC2 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC3 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC3 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC3 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC3 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC3 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC3 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC3 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC3 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC4 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC4 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC4 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC4 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC4 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC4 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC4 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC4 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC5 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC5 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC5 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC5 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC5 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC5 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC5 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC5 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC6 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC6 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC6 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC6 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC6 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC6 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC6 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC6 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC7 Mux", "Slot0", "IF1 DAC0" }, + { "IF1 DAC7 Mux", "Slot1", "IF1 DAC1" }, + { "IF1 DAC7 Mux", "Slot2", "IF1 DAC2" }, + { "IF1 DAC7 Mux", "Slot3", "IF1 DAC3" }, + { "IF1 DAC7 Mux", "Slot4", "IF1 DAC4" }, + { "IF1 DAC7 Mux", "Slot5", "IF1 DAC5" }, + { "IF1 DAC7 Mux", "Slot6", "IF1 DAC6" }, + { "IF1 DAC7 Mux", "Slot7", "IF1 DAC7" }, + + { "IF1 DAC01", NULL, "IF1 DAC0 Mux" }, + { "IF1 DAC01", NULL, "IF1 DAC1 Mux" }, + { "IF1 DAC23", NULL, "IF1 DAC2 Mux" }, + { "IF1 DAC23", NULL, "IF1 DAC3 Mux" }, + { "IF1 DAC45", NULL, "IF1 DAC4 Mux" }, + { "IF1 DAC45", NULL, "IF1 DAC5 Mux" }, + { "IF1 DAC67", NULL, "IF1 DAC6 Mux" }, + { "IF1 DAC67", NULL, "IF1 DAC7 Mux" }, + + { "IF2 DAC0", NULL, "AIF2RX" }, + { "IF2 DAC1", NULL, "AIF2RX" }, + { "IF2 DAC2", NULL, "AIF2RX" }, + { "IF2 DAC3", NULL, "AIF2RX" }, + { "IF2 DAC4", NULL, "AIF2RX" }, + { "IF2 DAC5", NULL, "AIF2RX" }, + { "IF2 DAC6", NULL, "AIF2RX" }, + { "IF2 DAC7", NULL, "AIF2RX" }, + { "IF2 DAC0", NULL, "I2S2" }, + { "IF2 DAC1", NULL, "I2S2" }, + { "IF2 DAC2", NULL, "I2S2" }, + { "IF2 DAC3", NULL, "I2S2" }, + { "IF2 DAC4", NULL, "I2S2" }, + { "IF2 DAC5", NULL, "I2S2" }, + { "IF2 DAC6", NULL, "I2S2" }, + { "IF2 DAC7", NULL, "I2S2" }, + + { "IF2 DAC0 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC0 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC0 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC0 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC0 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC0 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC0 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC0 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC1 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC1 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC1 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC1 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC1 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC1 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC1 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC1 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC2 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC2 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC2 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC2 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC2 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC2 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC2 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC2 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC3 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC3 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC3 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC3 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC3 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC3 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC3 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC3 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC4 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC4 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC4 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC4 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC4 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC4 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC4 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC4 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC5 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC5 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC5 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC5 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC5 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC5 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC5 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC5 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC6 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC6 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC6 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC6 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC6 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC6 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC6 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC6 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC7 Mux", "Slot0", "IF2 DAC0" }, + { "IF2 DAC7 Mux", "Slot1", "IF2 DAC1" }, + { "IF2 DAC7 Mux", "Slot2", "IF2 DAC2" }, + { "IF2 DAC7 Mux", "Slot3", "IF2 DAC3" }, + { "IF2 DAC7 Mux", "Slot4", "IF2 DAC4" }, + { "IF2 DAC7 Mux", "Slot5", "IF2 DAC5" }, + { "IF2 DAC7 Mux", "Slot6", "IF2 DAC6" }, + { "IF2 DAC7 Mux", "Slot7", "IF2 DAC7" }, + + { "IF2 DAC01", NULL, "IF2 DAC0 Mux" }, + { "IF2 DAC01", NULL, "IF2 DAC1 Mux" }, + { "IF2 DAC23", NULL, "IF2 DAC2 Mux" }, + { "IF2 DAC23", NULL, "IF2 DAC3 Mux" }, + { "IF2 DAC45", NULL, "IF2 DAC4 Mux" }, + { "IF2 DAC45", NULL, "IF2 DAC5 Mux" }, + { "IF2 DAC67", NULL, "IF2 DAC6 Mux" }, + { "IF2 DAC67", NULL, "IF2 DAC7 Mux" }, + + { "IF3 DAC", NULL, "AIF3RX" }, + { "IF3 DAC", NULL, "I2S3" }, + + { "IF4 DAC", NULL, "AIF4RX" }, + { "IF4 DAC", NULL, "I2S4" }, + + { "IF3 DAC L", NULL, "IF3 DAC" }, + { "IF3 DAC R", NULL, "IF3 DAC" }, + + { "IF4 DAC L", NULL, "IF4 DAC" }, + { "IF4 DAC R", NULL, "IF4 DAC" }, + + { "SLB DAC0", NULL, "SLBRX" }, + { "SLB DAC1", NULL, "SLBRX" }, + { "SLB DAC2", NULL, "SLBRX" }, + { "SLB DAC3", NULL, "SLBRX" }, + { "SLB DAC4", NULL, "SLBRX" }, + { "SLB DAC5", NULL, "SLBRX" }, + { "SLB DAC6", NULL, "SLBRX" }, + { "SLB DAC7", NULL, "SLBRX" }, + { "SLB DAC0", NULL, "SLB" }, + { "SLB DAC1", NULL, "SLB" }, + { "SLB DAC2", NULL, "SLB" }, + { "SLB DAC3", NULL, "SLB" }, + { "SLB DAC4", NULL, "SLB" }, + { "SLB DAC5", NULL, "SLB" }, + { "SLB DAC6", NULL, "SLB" }, + { "SLB DAC7", NULL, "SLB" }, + + { "SLB DAC01", NULL, "SLB DAC0" }, + { "SLB DAC01", NULL, "SLB DAC1" }, + { "SLB DAC23", NULL, "SLB DAC2" }, + { "SLB DAC23", NULL, "SLB DAC3" }, + { "SLB DAC45", NULL, "SLB DAC4" }, + { "SLB DAC45", NULL, "SLB DAC5" }, + { "SLB DAC67", NULL, "SLB DAC6" }, + { "SLB DAC67", NULL, "SLB DAC7" }, + + { "ADDA1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" }, + { "ADDA1 Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" }, + { "ADDA1 Mux", "OB 67", "OB67" }, + + { "DAC1 Mux", "IF1 DAC 01", "IF1 DAC01" }, + { "DAC1 Mux", "IF2 DAC 01", "IF2 DAC01" }, + { "DAC1 Mux", "IF3 DAC LR", "IF3 DAC" }, + { "DAC1 Mux", "IF4 DAC LR", "IF4 DAC" }, + { "DAC1 Mux", "SLB DAC 01", "SLB DAC01" }, + { "DAC1 Mux", "OB 01", "OB01 Bypass Mux" }, + + { "DAC1 MIXL", "Stereo ADC Switch", "ADDA1 Mux" }, + { "DAC1 MIXL", "DAC1 Switch", "DAC1 Mux" }, + { "DAC1 MIXR", "Stereo ADC Switch", "ADDA1 Mux" }, + { "DAC1 MIXR", "DAC1 Switch", "DAC1 Mux" }, + + { "DAC1 FS", NULL, "DAC1 MIXL" }, + { "DAC1 FS", NULL, "DAC1 MIXR" }, + + { "DAC2 L Mux", "IF1 DAC 2", "IF1 DAC2 Mux" }, + { "DAC2 L Mux", "IF2 DAC 2", "IF2 DAC2 Mux" }, + { "DAC2 L Mux", "IF3 DAC L", "IF3 DAC L" }, + { "DAC2 L Mux", "IF4 DAC L", "IF4 DAC L" }, + { "DAC2 L Mux", "SLB DAC 2", "SLB DAC2" }, + { "DAC2 L Mux", "OB 2", "OutBound2" }, + + { "DAC2 R Mux", "IF1 DAC 3", "IF1 DAC3 Mux" }, + { "DAC2 R Mux", "IF2 DAC 3", "IF2 DAC3 Mux" }, + { "DAC2 R Mux", "IF3 DAC R", "IF3 DAC R" }, + { "DAC2 R Mux", "IF4 DAC R", "IF4 DAC R" }, + { "DAC2 R Mux", "SLB DAC 3", "SLB DAC3" }, + { "DAC2 R Mux", "OB 3", "OutBound3" }, + { "DAC2 R Mux", "Haptic Generator", "Haptic Generator" }, + { "DAC2 R Mux", "VAD ADC", "VAD ADC Mux" }, + + { "DAC3 L Mux", "IF1 DAC 4", "IF1 DAC4 Mux" }, + { "DAC3 L Mux", "IF2 DAC 4", "IF2 DAC4 Mux" }, + { "DAC3 L Mux", "IF3 DAC L", "IF3 DAC L" }, + { "DAC3 L Mux", "IF4 DAC L", "IF4 DAC L" }, + { "DAC3 L Mux", "SLB DAC 4", "SLB DAC4" }, + { "DAC3 L Mux", "OB 4", "OutBound4" }, + + { "DAC3 R Mux", "IF1 DAC 5", "IF1 DAC5 Mux" }, + { "DAC3 R Mux", "IF2 DAC 5", "IF2 DAC5 Mux" }, + { "DAC3 R Mux", "IF3 DAC R", "IF3 DAC R" }, + { "DAC3 R Mux", "IF4 DAC R", "IF4 DAC R" }, + { "DAC3 R Mux", "SLB DAC 5", "SLB DAC5" }, + { "DAC3 R Mux", "OB 5", "OutBound5" }, + + { "DAC4 L Mux", "IF1 DAC 6", "IF1 DAC6 Mux" }, + { "DAC4 L Mux", "IF2 DAC 6", "IF2 DAC6 Mux" }, + { "DAC4 L Mux", "IF3 DAC L", "IF3 DAC L" }, + { "DAC4 L Mux", "IF4 DAC L", "IF4 DAC L" }, + { "DAC4 L Mux", "SLB DAC 6", "SLB DAC6" }, + { "DAC4 L Mux", "OB 6", "OutBound6" }, + + { "DAC4 R Mux", "IF1 DAC 7", "IF1 DAC7 Mux" }, + { "DAC4 R Mux", "IF2 DAC 7", "IF2 DAC7 Mux" }, + { "DAC4 R Mux", "IF3 DAC R", "IF3 DAC R" }, + { "DAC4 R Mux", "IF4 DAC R", "IF4 DAC R" }, + { "DAC4 R Mux", "SLB DAC 7", "SLB DAC7" }, + { "DAC4 R Mux", "OB 7", "OutBound7" }, + + { "Sidetone Mux", "DMIC1 L", "DMIC L1" }, + { "Sidetone Mux", "DMIC2 L", "DMIC L2" }, + { "Sidetone Mux", "DMIC3 L", "DMIC L3" }, + { "Sidetone Mux", "DMIC4 L", "DMIC L4" }, + { "Sidetone Mux", "ADC1", "ADC 1" }, + { "Sidetone Mux", "ADC2", "ADC 2" }, + { "Sidetone Mux", NULL, "Sidetone Power" }, + + { "Stereo DAC MIXL", "ST L Switch", "Sidetone Mux" }, + { "Stereo DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXL", "DAC2 L Switch", "DAC2 L Mux" }, + { "Stereo DAC MIXL", "DAC1 R Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXL", NULL, "dac stereo1 filter" }, + { "Stereo DAC MIXR", "ST R Switch", "Sidetone Mux" }, + { "Stereo DAC MIXR", "DAC1 R Switch", "DAC1 MIXR" }, + { "Stereo DAC MIXR", "DAC2 R Switch", "DAC2 R Mux" }, + { "Stereo DAC MIXR", "DAC1 L Switch", "DAC1 MIXL" }, + { "Stereo DAC MIXR", NULL, "dac stereo1 filter" }, + { "dac stereo1 filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Mono DAC MIXL", "ST L Switch", "Sidetone Mux" }, + { "Mono DAC MIXL", "DAC1 L Switch", "DAC1 MIXL" }, + { "Mono DAC MIXL", "DAC2 L Switch", "DAC2 L Mux" }, + { "Mono DAC MIXL", "DAC2 R Switch", "DAC2 R Mux" }, + { "Mono DAC MIXL", NULL, "dac mono2 left filter" }, + { "dac mono2 left filter", NULL, "PLL1", is_sys_clk_from_pll }, + { "Mono DAC MIXR", "ST R Switch", "Sidetone Mux" }, + { "Mono DAC MIXR", "DAC1 R Switch", "DAC1 MIXR" }, + { "Mono DAC MIXR", "DAC2 R Switch", "DAC2 R Mux" }, + { "Mono DAC MIXR", "DAC2 L Switch", "DAC2 L Mux" }, + { "Mono DAC MIXR", NULL, "dac mono2 right filter" }, + { "dac mono2 right filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "DD1 MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" }, + { "DD1 MIXL", "Mono DAC Mix L Switch", "Mono DAC MIXL" }, + { "DD1 MIXL", "DAC3 L Switch", "DAC3 L Mux" }, + { "DD1 MIXL", "DAC3 R Switch", "DAC3 R Mux" }, + { "DD1 MIXL", NULL, "dac mono3 left filter" }, + { "dac mono3 left filter", NULL, "PLL1", is_sys_clk_from_pll }, + { "DD1 MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" }, + { "DD1 MIXR", "Mono DAC Mix R Switch", "Mono DAC MIXR" }, + { "DD1 MIXR", "DAC3 L Switch", "DAC3 L Mux" }, + { "DD1 MIXR", "DAC3 R Switch", "DAC3 R Mux" }, + { "DD1 MIXR", NULL, "dac mono3 right filter" }, + { "dac mono3 right filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "DD2 MIXL", "Sto DAC Mix L Switch", "Stereo DAC MIXL" }, + { "DD2 MIXL", "Mono DAC Mix L Switch", "Mono DAC MIXL" }, + { "DD2 MIXL", "DAC4 L Switch", "DAC4 L Mux" }, + { "DD2 MIXL", "DAC4 R Switch", "DAC4 R Mux" }, + { "DD2 MIXL", NULL, "dac mono4 left filter" }, + { "dac mono4 left filter", NULL, "PLL1", is_sys_clk_from_pll }, + { "DD2 MIXR", "Sto DAC Mix R Switch", "Stereo DAC MIXR" }, + { "DD2 MIXR", "Mono DAC Mix R Switch", "Mono DAC MIXR" }, + { "DD2 MIXR", "DAC4 L Switch", "DAC4 L Mux" }, + { "DD2 MIXR", "DAC4 R Switch", "DAC4 R Mux" }, + { "DD2 MIXR", NULL, "dac mono4 right filter" }, + { "dac mono4 right filter", NULL, "PLL1", is_sys_clk_from_pll }, + + { "Stereo DAC MIX", NULL, "Stereo DAC MIXL" }, + { "Stereo DAC MIX", NULL, "Stereo DAC MIXR" }, + { "Mono DAC MIX", NULL, "Mono DAC MIXL" }, + { "Mono DAC MIX", NULL, "Mono DAC MIXR" }, + { "DD1 MIX", NULL, "DD1 MIXL" }, + { "DD1 MIX", NULL, "DD1 MIXR" }, + { "DD2 MIX", NULL, "DD2 MIXL" }, + { "DD2 MIX", NULL, "DD2 MIXR" }, + + { "DAC12 SRC Mux", "STO1 DAC MIX", "Stereo DAC MIX" }, + { "DAC12 SRC Mux", "MONO DAC MIX", "Mono DAC MIX" }, + { "DAC12 SRC Mux", "DD MIX1", "DD1 MIX" }, + { "DAC12 SRC Mux", "DD MIX2", "DD2 MIX" }, + + { "DAC3 SRC Mux", "MONO DAC MIXL", "Mono DAC MIXL" }, + { "DAC3 SRC Mux", "MONO DAC MIXR", "Mono DAC MIXR" }, + { "DAC3 SRC Mux", "DD MIX1L", "DD1 MIXL" }, + { "DAC3 SRC Mux", "DD MIX2L", "DD2 MIXL" }, + + { "DAC 1", NULL, "DAC12 SRC Mux" }, + { "DAC 2", NULL, "DAC12 SRC Mux" }, + { "DAC 3", NULL, "DAC3 SRC Mux" }, + + { "PDM1 L Mux", "STO1 DAC MIX", "Stereo DAC MIXL" }, + { "PDM1 L Mux", "MONO DAC MIX", "Mono DAC MIXL" }, + { "PDM1 L Mux", "DD MIX1", "DD1 MIXL" }, + { "PDM1 L Mux", "DD MIX2", "DD2 MIXL" }, + { "PDM1 L Mux", NULL, "PDM1 Power" }, + { "PDM1 R Mux", "STO1 DAC MIX", "Stereo DAC MIXR" }, + { "PDM1 R Mux", "MONO DAC MIX", "Mono DAC MIXR" }, + { "PDM1 R Mux", "DD MIX1", "DD1 MIXR" }, + { "PDM1 R Mux", "DD MIX2", "DD2 MIXR" }, + { "PDM1 R Mux", NULL, "PDM1 Power" }, + { "PDM2 L Mux", "STO1 DAC MIX", "Stereo DAC MIXL" }, + { "PDM2 L Mux", "MONO DAC MIX", "Mono DAC MIXL" }, + { "PDM2 L Mux", "DD MIX1", "DD1 MIXL" }, + { "PDM2 L Mux", "DD MIX2", "DD2 MIXL" }, + { "PDM2 L Mux", NULL, "PDM2 Power" }, + { "PDM2 R Mux", "STO1 DAC MIX", "Stereo DAC MIXR" }, + { "PDM2 R Mux", "MONO DAC MIX", "Mono DAC MIXR" }, + { "PDM2 R Mux", "DD MIX1", "DD1 MIXR" }, + { "PDM2 R Mux", "DD MIX1", "DD2 MIXR" }, + { "PDM2 R Mux", NULL, "PDM2 Power" }, + + { "LOUT1 amp", NULL, "DAC 1" }, + { "LOUT2 amp", NULL, "DAC 2" }, + { "LOUT3 amp", NULL, "DAC 3" }, + + { "LOUT1 vref", NULL, "LOUT1 amp" }, + { "LOUT2 vref", NULL, "LOUT2 amp" }, + { "LOUT3 vref", NULL, "LOUT3 amp" }, + + { "LOUT1", NULL, "LOUT1 vref" }, + { "LOUT2", NULL, "LOUT2 vref" }, + { "LOUT3", NULL, "LOUT3 vref" }, + + { "PDM1L", NULL, "PDM1 L Mux" }, + { "PDM1R", NULL, "PDM1 R Mux" }, + { "PDM2L", NULL, "PDM2 L Mux" }, + { "PDM2R", NULL, "PDM2 R Mux" }, +}; + +static const struct snd_soc_dapm_route rt5677_dmic2_clk_1[] = { + { "DMIC L2", NULL, "DMIC1 power" }, + { "DMIC R2", NULL, "DMIC1 power" }, +}; + +static const struct snd_soc_dapm_route rt5677_dmic2_clk_2[] = { + { "DMIC L2", NULL, "DMIC2 power" }, + { "DMIC R2", NULL, "DMIC2 power" }, +}; + +static int rt5677_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + unsigned int val_len = 0, val_clk, mask_clk; + int pre_div, bclk_ms, frame_size; + + rt5677->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5677->sysclk, rt5677->lrck[dai->id]); + if (pre_div < 0) { + dev_err(component->dev, "Unsupported clock setting: sysclk=%dHz lrck=%dHz\n", + rt5677->sysclk, rt5677->lrck[dai->id]); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + bclk_ms = frame_size > 32; + rt5677->bclk[dai->id] = rt5677->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5677->bclk[dai->id], rt5677->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + val_len |= RT5677_I2S_DL_20; + break; + case 24: + val_len |= RT5677_I2S_DL_24; + break; + case 8: + val_len |= RT5677_I2S_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5677_AIF1: + mask_clk = RT5677_I2S_PD1_MASK; + val_clk = pre_div << RT5677_I2S_PD1_SFT; + regmap_update_bits(rt5677->regmap, RT5677_I2S1_SDP, + RT5677_I2S_DL_MASK, val_len); + regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1, + mask_clk, val_clk); + break; + case RT5677_AIF2: + mask_clk = RT5677_I2S_PD2_MASK; + val_clk = pre_div << RT5677_I2S_PD2_SFT; + regmap_update_bits(rt5677->regmap, RT5677_I2S2_SDP, + RT5677_I2S_DL_MASK, val_len); + regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1, + mask_clk, val_clk); + break; + case RT5677_AIF3: + mask_clk = RT5677_I2S_BCLK_MS3_MASK | RT5677_I2S_PD3_MASK; + val_clk = bclk_ms << RT5677_I2S_BCLK_MS3_SFT | + pre_div << RT5677_I2S_PD3_SFT; + regmap_update_bits(rt5677->regmap, RT5677_I2S3_SDP, + RT5677_I2S_DL_MASK, val_len); + regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1, + mask_clk, val_clk); + break; + case RT5677_AIF4: + mask_clk = RT5677_I2S_BCLK_MS4_MASK | RT5677_I2S_PD4_MASK; + val_clk = bclk_ms << RT5677_I2S_BCLK_MS4_SFT | + pre_div << RT5677_I2S_PD4_SFT; + regmap_update_bits(rt5677->regmap, RT5677_I2S4_SDP, + RT5677_I2S_DL_MASK, val_len); + regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1, + mask_clk, val_clk); + break; + default: + break; + } + + return 0; +} + +static int rt5677_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5677->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5677_I2S_MS_S; + rt5677->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5677_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5677_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5677_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5677_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5677_AIF1: + regmap_update_bits(rt5677->regmap, RT5677_I2S1_SDP, + RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK | + RT5677_I2S_DF_MASK, reg_val); + break; + case RT5677_AIF2: + regmap_update_bits(rt5677->regmap, RT5677_I2S2_SDP, + RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK | + RT5677_I2S_DF_MASK, reg_val); + break; + case RT5677_AIF3: + regmap_update_bits(rt5677->regmap, RT5677_I2S3_SDP, + RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK | + RT5677_I2S_DF_MASK, reg_val); + break; + case RT5677_AIF4: + regmap_update_bits(rt5677->regmap, RT5677_I2S4_SDP, + RT5677_I2S_MS_MASK | RT5677_I2S_BP_MASK | + RT5677_I2S_DF_MASK, reg_val); + break; + default: + break; + } + + + return 0; +} + +static int rt5677_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0; + + if (freq == rt5677->sysclk && clk_id == rt5677->sysclk_src) + return 0; + + switch (clk_id) { + case RT5677_SCLK_S_MCLK: + reg_val |= RT5677_SCLK_SRC_MCLK; + break; + case RT5677_SCLK_S_PLL1: + reg_val |= RT5677_SCLK_SRC_PLL1; + break; + case RT5677_SCLK_S_RCCLK: + reg_val |= RT5677_SCLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, + RT5677_SCLK_SRC_MASK, reg_val); + rt5677->sysclk = freq; + rt5677->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + + return 0; +} + +/** + * rt5677_pll_calc - Calcualte PLL M/N/K code. + * @freq_in: external clock provided to codec. + * @freq_out: target clock which codec works on. + * @pll_code: Pointer to structure with M, N, K, bypass K and bypass M flag. + * + * Calcualte M/N/K code and bypass K/M flag to configure PLL for codec. + * + * Returns 0 for success or negative error code. + */ +static int rt5677_pll_calc(const unsigned int freq_in, + const unsigned int freq_out, struct rl6231_pll_code *pll_code) +{ + if (RT5677_PLL_INP_MIN > freq_in) + return -EINVAL; + + return rl6231_pll_calc(freq_in, freq_out, pll_code); +} + +static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code; + int ret; + + if (source == rt5677->pll_src && freq_in == rt5677->pll_in && + freq_out == rt5677->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5677->pll_in = 0; + rt5677->pll_out = 0; + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, + RT5677_SCLK_SRC_MASK, RT5677_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5677_PLL1_S_MCLK: + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, + RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_MCLK); + break; + case RT5677_PLL1_S_BCLK1: + case RT5677_PLL1_S_BCLK2: + case RT5677_PLL1_S_BCLK3: + case RT5677_PLL1_S_BCLK4: + switch (dai->id) { + case RT5677_AIF1: + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, + RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK1); + break; + case RT5677_AIF2: + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, + RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK2); + break; + case RT5677_AIF3: + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, + RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK3); + break; + case RT5677_AIF4: + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, + RT5677_PLL1_SRC_MASK, RT5677_PLL1_SRC_BCLK4); + break; + default: + break; + } + break; + default: + dev_err(component->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rt5677_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + + dev_dbg(component->dev, "m_bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + regmap_write(rt5677->regmap, RT5677_PLL1_CTRL1, + pll_code.n_code << RT5677_PLL_N_SFT | pll_code.k_code); + regmap_write(rt5677->regmap, RT5677_PLL1_CTRL2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5677_PLL_M_SFT) | + (pll_code.m_bp << RT5677_PLL_M_BP_SFT)); + + rt5677->pll_in = freq_in; + rt5677->pll_out = freq_out; + rt5677->pll_src = source; + + return 0; +} + +static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, slot_width_25 = 0; + + if (rx_mask || tx_mask) + val |= (1 << 12); + + switch (slots) { + case 4: + val |= (1 << 10); + break; + case 6: + val |= (2 << 10); + break; + case 8: + val |= (3 << 10); + break; + case 2: + default: + break; + } + + switch (slot_width) { + case 20: + val |= (1 << 8); + break; + case 25: + slot_width_25 = 0x8080; + fallthrough; + case 24: + val |= (2 << 8); + break; + case 32: + val |= (3 << 8); + break; + case 16: + default: + break; + } + + switch (dai->id) { + case RT5677_AIF1: + regmap_update_bits(rt5677->regmap, RT5677_TDM1_CTRL1, 0x1f00, + val); + regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x8000, + slot_width_25); + break; + case RT5677_AIF2: + regmap_update_bits(rt5677->regmap, RT5677_TDM2_CTRL1, 0x1f00, + val); + regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x80, + slot_width_25); + break; + default: + break; + } + + return 0; +} + +static int rt5677_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + enum snd_soc_bias_level prev_bias = + snd_soc_component_get_bias_level(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + if (prev_bias == SND_SOC_BIAS_STANDBY) { + + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, + RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK, + 5 << RT5677_LDO1_SEL_SFT | + 5 << RT5677_LDO2_SEL_SFT); + regmap_update_bits(rt5677->regmap, + RT5677_PR_BASE + RT5677_BIAS_CUR4, + 0x0f00, 0x0f00); + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, + RT5677_PWR_FV1 | RT5677_PWR_FV2 | + RT5677_PWR_VREF1 | RT5677_PWR_MB | + RT5677_PWR_BG | RT5677_PWR_VREF2, + RT5677_PWR_VREF1 | RT5677_PWR_MB | + RT5677_PWR_BG | RT5677_PWR_VREF2); + rt5677->is_vref_slow = false; + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_CORE, RT5677_PWR_CORE); + regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, + 0x1, 0x1); + } + break; + + case SND_SOC_BIAS_STANDBY: + if (prev_bias == SND_SOC_BIAS_OFF && + rt5677->dsp_vad_en_request) { + /* Re-enable the DSP if it was turned off at suspend */ + rt5677->dsp_vad_en = true; + /* The delay is to wait for MCLK */ + schedule_delayed_work(&rt5677->dsp_work, + msecs_to_jiffies(1000)); + } + break; + + case SND_SOC_BIAS_OFF: + flush_delayed_work(&rt5677->dsp_work); + if (rt5677->is_dsp_mode) { + /* Turn off the DSP before suspend */ + rt5677->dsp_vad_en = false; + schedule_delayed_work(&rt5677->dsp_work, 0); + flush_delayed_work(&rt5677->dsp_work); + } + + regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x0); + regmap_write(rt5677->regmap, RT5677_PWR_DIG1, 0x0000); + regmap_write(rt5677->regmap, RT5677_PWR_ANLG1, + 2 << RT5677_LDO1_SEL_SFT | + 2 << RT5677_LDO2_SEL_SFT); + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_CORE, 0); + regmap_update_bits(rt5677->regmap, + RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0000); + + if (rt5677->dsp_vad_en) + rt5677_set_dsp_vad(component, true); + break; + + default: + break; + } + + return 0; +} + +static int rt5677_update_gpio_bits(struct rt5677_priv *rt5677, unsigned offset, int m, int v) +{ + unsigned int bank = offset / 5; + unsigned int shift = (offset % 5) * 3; + unsigned int reg = bank ? RT5677_GPIO_CTRL3 : RT5677_GPIO_CTRL2; + + return regmap_update_bits(rt5677->regmap, reg, m << shift, v << shift); +} + +#ifdef CONFIG_GPIOLIB +static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO; + int m = RT5677_GPIOx_OUT_MASK; + + rt5677_update_gpio_bits(rt5677, offset, m, level); +} + +static int rt5677_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO; + int m = RT5677_GPIOx_DIR_MASK | RT5677_GPIOx_OUT_MASK; + int v = RT5677_GPIOx_DIR_OUT | level; + + return rt5677_update_gpio_bits(rt5677, offset, m, v); +} + +static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + int value, ret; + + ret = regmap_read(rt5677->regmap, RT5677_GPIO_ST, &value); + if (ret < 0) + return ret; + + return (value & (0x1 << offset)) >> offset; +} + +static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + int m = RT5677_GPIOx_DIR_MASK; + int v = RT5677_GPIOx_DIR_IN; + + return rt5677_update_gpio_bits(rt5677, offset, m, v); +} + +/* + * Configures the GPIO as + * 0 - floating + * 1 - pull down + * 2 - pull up + */ +static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset, + int value) +{ + int shift; + + switch (offset) { + case RT5677_GPIO1 ... RT5677_GPIO2: + shift = 2 * (1 - offset); + regmap_update_bits(rt5677->regmap, + RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL2, + 0x3 << shift, + (value & 0x3) << shift); + break; + + case RT5677_GPIO3 ... RT5677_GPIO6: + shift = 2 * (9 - offset); + regmap_update_bits(rt5677->regmap, + RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL3, + 0x3 << shift, + (value & 0x3) << shift); + break; + + default: + break; + } +} + +static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct rt5677_priv *rt5677 = gpiochip_get_data(chip); + int irq; + + if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) || + (rt5677->pdata.jd1_gpio == 2 && + offset == RT5677_GPIO2) || + (rt5677->pdata.jd1_gpio == 3 && + offset == RT5677_GPIO3)) { + irq = RT5677_IRQ_JD1; + } else if ((rt5677->pdata.jd2_gpio == 1 && offset == RT5677_GPIO4) || + (rt5677->pdata.jd2_gpio == 2 && + offset == RT5677_GPIO5) || + (rt5677->pdata.jd2_gpio == 3 && + offset == RT5677_GPIO6)) { + irq = RT5677_IRQ_JD2; + } else if ((rt5677->pdata.jd3_gpio == 1 && + offset == RT5677_GPIO4) || + (rt5677->pdata.jd3_gpio == 2 && + offset == RT5677_GPIO5) || + (rt5677->pdata.jd3_gpio == 3 && + offset == RT5677_GPIO6)) { + irq = RT5677_IRQ_JD3; + } else { + return -ENXIO; + } + + return irq_create_mapping(rt5677->domain, irq); +} + +static const struct gpio_chip rt5677_template_chip = { + .label = RT5677_DRV_NAME, + .owner = THIS_MODULE, + .direction_output = rt5677_gpio_direction_out, + .set = rt5677_gpio_set, + .direction_input = rt5677_gpio_direction_in, + .get = rt5677_gpio_get, + .to_irq = rt5677_to_irq, + .can_sleep = 1, +}; + +static void rt5677_init_gpio(struct i2c_client *i2c) +{ + struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); + int ret; + + rt5677->gpio_chip = rt5677_template_chip; + rt5677->gpio_chip.ngpio = RT5677_GPIO_NUM; + rt5677->gpio_chip.parent = &i2c->dev; + rt5677->gpio_chip.base = -1; + + ret = gpiochip_add_data(&rt5677->gpio_chip, rt5677); + if (ret != 0) + dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void rt5677_free_gpio(struct i2c_client *i2c) +{ + struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); + + gpiochip_remove(&rt5677->gpio_chip); +} +#else +static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset, + int value) +{ +} + +static void rt5677_init_gpio(struct i2c_client *i2c) +{ +} + +static void rt5677_free_gpio(struct i2c_client *i2c) +{ +} +#endif + +static int rt5677_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + int i; + + rt5677->component = component; + + if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) { + snd_soc_dapm_add_routes(dapm, + rt5677_dmic2_clk_2, + ARRAY_SIZE(rt5677_dmic2_clk_2)); + } else { /*use dmic1 clock by default*/ + snd_soc_dapm_add_routes(dapm, + rt5677_dmic2_clk_1, + ARRAY_SIZE(rt5677_dmic2_clk_1)); + } + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, + ~RT5677_IRQ_DEBOUNCE_SEL_MASK, 0x0020); + regmap_write(rt5677->regmap, RT5677_PWR_DSP2, + RT5677_PWR_SLIM_ISO | RT5677_PWR_CORE_ISO); + + for (i = 0; i < RT5677_GPIO_NUM; i++) + rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]); + + mutex_init(&rt5677->dsp_cmd_lock); + mutex_init(&rt5677->dsp_pri_lock); + + return 0; +} + +static void rt5677_remove(struct snd_soc_component *component) +{ + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&rt5677->dsp_work); + + regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); + gpiod_set_value_cansleep(rt5677->pow_ldo2, 0); + gpiod_set_value_cansleep(rt5677->reset_pin, 1); +} + +#ifdef CONFIG_PM +static int rt5677_suspend(struct snd_soc_component *component) +{ + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + if (rt5677->irq) { + cancel_delayed_work_sync(&rt5677->resume_irq_check); + disable_irq(rt5677->irq); + } + + if (!rt5677->dsp_vad_en) { + regcache_cache_only(rt5677->regmap, true); + regcache_mark_dirty(rt5677->regmap); + + gpiod_set_value_cansleep(rt5677->pow_ldo2, 0); + gpiod_set_value_cansleep(rt5677->reset_pin, 1); + } + + return 0; +} + +static int rt5677_resume(struct snd_soc_component *component) +{ + struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component); + + if (!rt5677->dsp_vad_en) { + rt5677->pll_src = 0; + rt5677->pll_in = 0; + rt5677->pll_out = 0; + gpiod_set_value_cansleep(rt5677->pow_ldo2, 1); + gpiod_set_value_cansleep(rt5677->reset_pin, 0); + if (rt5677->pow_ldo2 || rt5677->reset_pin) + msleep(10); + + regcache_cache_only(rt5677->regmap, false); + regcache_sync(rt5677->regmap); + } + + if (rt5677->irq) { + enable_irq(rt5677->irq); + schedule_delayed_work(&rt5677->resume_irq_check, 0); + } + + return 0; +} +#else +#define rt5677_suspend NULL +#define rt5677_resume NULL +#endif + +static int rt5677_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + struct rt5677_priv *rt5677 = i2c_get_clientdata(client); + + if (rt5677->is_dsp_mode) { + if (reg > 0xff) { + mutex_lock(&rt5677->dsp_pri_lock); + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, + reg & 0xff); + rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val); + mutex_unlock(&rt5677->dsp_pri_lock); + } else { + rt5677_dsp_mode_i2c_read(rt5677, reg, val); + } + } else { + regmap_read(rt5677->regmap_physical, reg, val); + } + + return 0; +} + +static int rt5677_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + struct rt5677_priv *rt5677 = i2c_get_clientdata(client); + + if (rt5677->is_dsp_mode) { + if (reg > 0xff) { + mutex_lock(&rt5677->dsp_pri_lock); + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX, + reg & 0xff); + rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA, + val); + mutex_unlock(&rt5677->dsp_pri_lock); + } else { + rt5677_dsp_mode_i2c_write(rt5677, reg, val); + } + } else { + regmap_write(rt5677->regmap_physical, reg, val); + } + + return 0; +} + +#define RT5677_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt5677_aif_dai_ops = { + .hw_params = rt5677_hw_params, + .set_fmt = rt5677_set_dai_fmt, + .set_sysclk = rt5677_set_dai_sysclk, + .set_pll = rt5677_set_dai_pll, + .set_tdm_slot = rt5677_set_tdm_slot, +}; + +static const struct snd_soc_dai_ops rt5677_dsp_dai_ops = { + .set_sysclk = rt5677_set_dai_sysclk, + .set_pll = rt5677_set_dai_pll, +}; + +static struct snd_soc_dai_driver rt5677_dai[] = { + { + .name = "rt5677-aif1", + .id = RT5677_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5677_STEREO_RATES, + .formats = RT5677_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5677_STEREO_RATES, + .formats = RT5677_FORMATS, + }, + .ops = &rt5677_aif_dai_ops, + }, + { + .name = "rt5677-aif2", + .id = RT5677_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5677_STEREO_RATES, + .formats = RT5677_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5677_STEREO_RATES, + .formats = RT5677_FORMATS, + }, + .ops = &rt5677_aif_dai_ops, + }, + { + .name = "rt5677-aif3", + .id = RT5677_AIF3, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5677_STEREO_RATES, + .formats = RT5677_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5677_STEREO_RATES, + .formats = RT5677_FORMATS, + }, + .ops = &rt5677_aif_dai_ops, + }, + { + .name = "rt5677-aif4", + .id = RT5677_AIF4, + .playback = { + .stream_name = "AIF4 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5677_STEREO_RATES, + .formats = RT5677_FORMATS, + }, + .capture = { + .stream_name = "AIF4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5677_STEREO_RATES, + .formats = RT5677_FORMATS, + }, + .ops = &rt5677_aif_dai_ops, + }, + { + .name = "rt5677-slimbus", + .id = RT5677_AIF5, + .playback = { + .stream_name = "SLIMBus Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5677_STEREO_RATES, + .formats = RT5677_FORMATS, + }, + .capture = { + .stream_name = "SLIMBus Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5677_STEREO_RATES, + .formats = RT5677_FORMATS, + }, + .ops = &rt5677_aif_dai_ops, + }, + { + .name = "rt5677-dspbuffer", + .id = RT5677_DSPBUFF, + .capture = { + .stream_name = "DSP Buffer", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &rt5677_dsp_dai_ops, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_rt5677 = { + .name = RT5677_DRV_NAME, + .probe = rt5677_probe, + .remove = rt5677_remove, + .suspend = rt5677_suspend, + .resume = rt5677_resume, + .set_bias_level = rt5677_set_bias_level, + .controls = rt5677_snd_controls, + .num_controls = ARRAY_SIZE(rt5677_snd_controls), + .dapm_widgets = rt5677_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5677_dapm_widgets), + .dapm_routes = rt5677_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config rt5677_regmap_physical = { + .name = "physical", + .reg_bits = 8, + .val_bits = 16, + + .max_register = RT5677_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5677_ranges) * + RT5677_PR_SPACING), + .readable_reg = rt5677_readable_register, + + .cache_type = REGCACHE_NONE, + .ranges = rt5677_ranges, + .num_ranges = ARRAY_SIZE(rt5677_ranges), +}; + +static const struct regmap_config rt5677_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = RT5677_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5677_ranges) * + RT5677_PR_SPACING), + + .volatile_reg = rt5677_volatile_register, + .readable_reg = rt5677_readable_register, + .reg_read = rt5677_read, + .reg_write = rt5677_write, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5677_reg, + .num_reg_defaults = ARRAY_SIZE(rt5677_reg), + .ranges = rt5677_ranges, + .num_ranges = ARRAY_SIZE(rt5677_ranges), +}; + +static const struct of_device_id rt5677_of_match[] = { + { .compatible = "realtek,rt5677", .data = (const void *)RT5677 }, + { } +}; +MODULE_DEVICE_TABLE(of, rt5677_of_match); + +static const struct acpi_device_id rt5677_acpi_match[] = { + { "RT5677CE", RT5677 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match); + +static void rt5677_read_device_properties(struct rt5677_priv *rt5677, + struct device *dev) +{ + u32 val; + + rt5677->pdata.in1_diff = + device_property_read_bool(dev, "IN1") || + device_property_read_bool(dev, "realtek,in1-differential"); + + rt5677->pdata.in2_diff = + device_property_read_bool(dev, "IN2") || + device_property_read_bool(dev, "realtek,in2-differential"); + + rt5677->pdata.lout1_diff = + device_property_read_bool(dev, "OUT1") || + device_property_read_bool(dev, "realtek,lout1-differential"); + + rt5677->pdata.lout2_diff = + device_property_read_bool(dev, "OUT2") || + device_property_read_bool(dev, "realtek,lout2-differential"); + + rt5677->pdata.lout3_diff = + device_property_read_bool(dev, "OUT3") || + device_property_read_bool(dev, "realtek,lout3-differential"); + + device_property_read_u8_array(dev, "realtek,gpio-config", + rt5677->pdata.gpio_config, + RT5677_GPIO_NUM); + + if (!device_property_read_u32(dev, "DCLK", &val) || + !device_property_read_u32(dev, "realtek,dmic2_clk_pin", &val)) + rt5677->pdata.dmic2_clk_pin = val; + + if (!device_property_read_u32(dev, "JD1", &val) || + !device_property_read_u32(dev, "realtek,jd1-gpio", &val)) + rt5677->pdata.jd1_gpio = val; + + if (!device_property_read_u32(dev, "JD2", &val) || + !device_property_read_u32(dev, "realtek,jd2-gpio", &val)) + rt5677->pdata.jd2_gpio = val; + + if (!device_property_read_u32(dev, "JD3", &val) || + !device_property_read_u32(dev, "realtek,jd3-gpio", &val)) + rt5677->pdata.jd3_gpio = val; +} + +struct rt5677_irq_desc { + unsigned int enable_mask; + unsigned int status_mask; + unsigned int polarity_mask; +}; + +static const struct rt5677_irq_desc rt5677_irq_descs[] = { + [RT5677_IRQ_JD1] = { + .enable_mask = RT5677_EN_IRQ_GPIO_JD1, + .status_mask = RT5677_STA_GPIO_JD1, + .polarity_mask = RT5677_INV_GPIO_JD1, + }, + [RT5677_IRQ_JD2] = { + .enable_mask = RT5677_EN_IRQ_GPIO_JD2, + .status_mask = RT5677_STA_GPIO_JD2, + .polarity_mask = RT5677_INV_GPIO_JD2, + }, + [RT5677_IRQ_JD3] = { + .enable_mask = RT5677_EN_IRQ_GPIO_JD3, + .status_mask = RT5677_STA_GPIO_JD3, + .polarity_mask = RT5677_INV_GPIO_JD3, + }, +}; + +static bool rt5677_check_hotword(struct rt5677_priv *rt5677) +{ + int reg_gpio; + + if (!rt5677->is_dsp_mode) + return false; + + if (regmap_read(rt5677->regmap, RT5677_GPIO_CTRL1, ®_gpio)) + return false; + + /* Firmware sets GPIO1 pin to be GPIO1 after hotword is detected */ + if ((reg_gpio & RT5677_GPIO1_PIN_MASK) == RT5677_GPIO1_PIN_IRQ) + return false; + + /* Set GPIO1 pin back to be IRQ output for jack detect */ + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, + RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); + + rt5677_spi_hotword_detected(); + return true; +} + +static irqreturn_t rt5677_irq(int unused, void *data) +{ + struct rt5677_priv *rt5677 = data; + int ret, loop, i, reg_irq, virq; + bool irq_fired = false; + + mutex_lock(&rt5677->irq_lock); + + /* + * Loop to handle interrupts until the last i2c read shows no pending + * irqs. The interrupt line is shared by multiple interrupt sources. + * After the regmap_read() below, a new interrupt source line may + * become high before the regmap_write() finishes, so there isn't a + * rising edge on the shared interrupt line for the new interrupt. Thus, + * the loop is needed to avoid missing irqs. + * + * A safeguard of 20 loops is used to avoid hanging in the irq handler + * if there is something wrong with the interrupt status update. The + * interrupt sources here are audio jack plug/unplug events which + * shouldn't happen at a high frequency for a long period of time. + * Empirically, more than 3 loops have never been seen. + */ + for (loop = 0; loop < 20; loop++) { + /* Read interrupt status */ + ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, ®_irq); + if (ret) { + dev_err(rt5677->dev, "failed reading IRQ status: %d\n", + ret); + goto exit; + } + + irq_fired = false; + for (i = 0; i < RT5677_IRQ_NUM; i++) { + if (reg_irq & rt5677_irq_descs[i].status_mask) { + irq_fired = true; + virq = irq_find_mapping(rt5677->domain, i); + if (virq) + handle_nested_irq(virq); + + /* Clear the interrupt by flipping the polarity + * of the interrupt source line that fired + */ + reg_irq ^= rt5677_irq_descs[i].polarity_mask; + } + } + + /* Exit the loop only when we know for sure that GPIO1 pin + * was low at some point since irq_lock was acquired. Any event + * after that point creates a rising edge that triggers another + * call to rt5677_irq(). + */ + if (!irq_fired && !rt5677_check_hotword(rt5677)) + goto exit; + + ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq); + if (ret) { + dev_err(rt5677->dev, "failed updating IRQ status: %d\n", + ret); + goto exit; + } + } +exit: + WARN_ON_ONCE(loop == 20); + mutex_unlock(&rt5677->irq_lock); + if (irq_fired) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static void rt5677_resume_irq_check(struct work_struct *work) +{ + int i, virq; + struct rt5677_priv *rt5677 = + container_of(work, struct rt5677_priv, resume_irq_check.work); + + /* This is needed to check and clear the interrupt status register + * at resume. If the headset is plugged/unplugged when the device is + * fully suspended, there won't be a rising edge at resume to trigger + * the interrupt. Without this, we miss the next unplug/plug event. + */ + rt5677_irq(0, rt5677); + + /* Call all enabled jack detect irq handlers again. This is needed in + * addition to the above check for a corner case caused by jack gpio + * debounce. After codec irq is disabled at suspend, the delayed work + * scheduled by soc-jack may run and read wrong jack gpio values, since + * the regmap is in cache only mode. At resume, there is no irq because + * rt5677_irq has already ran and cleared the irq status at suspend. + * Without this explicit check, unplug the headset right after suspend + * starts, then after resume the headset is still shown as plugged in. + */ + mutex_lock(&rt5677->irq_lock); + for (i = 0; i < RT5677_IRQ_NUM; i++) { + if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) { + virq = irq_find_mapping(rt5677->domain, i); + if (virq) + handle_nested_irq(virq); + } + } + mutex_unlock(&rt5677->irq_lock); +} + +static void rt5677_irq_bus_lock(struct irq_data *data) +{ + struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data); + + mutex_lock(&rt5677->irq_lock); +} + +static void rt5677_irq_bus_sync_unlock(struct irq_data *data) +{ + struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data); + + // Set the enable/disable bits for the jack detect IRQs. + regmap_update_bits(rt5677->regmap, RT5677_IRQ_CTRL1, + RT5677_EN_IRQ_GPIO_JD1 | RT5677_EN_IRQ_GPIO_JD2 | + RT5677_EN_IRQ_GPIO_JD3, rt5677->irq_en); + mutex_unlock(&rt5677->irq_lock); +} + +static void rt5677_irq_enable(struct irq_data *data) +{ + struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data); + + rt5677->irq_en |= rt5677_irq_descs[data->hwirq].enable_mask; +} + +static void rt5677_irq_disable(struct irq_data *data) +{ + struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data); + + rt5677->irq_en &= ~rt5677_irq_descs[data->hwirq].enable_mask; +} + +static struct irq_chip rt5677_irq_chip = { + .name = "rt5677_irq_chip", + .irq_bus_lock = rt5677_irq_bus_lock, + .irq_bus_sync_unlock = rt5677_irq_bus_sync_unlock, + .irq_disable = rt5677_irq_disable, + .irq_enable = rt5677_irq_enable, +}; + +static int rt5677_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct rt5677_priv *rt5677 = h->host_data; + + irq_set_chip_data(virq, rt5677); + irq_set_chip(virq, &rt5677_irq_chip); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); + return 0; +} + + +static const struct irq_domain_ops rt5677_domain_ops = { + .map = rt5677_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int rt5677_init_irq(struct i2c_client *i2c) +{ + int ret; + struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c); + unsigned int jd_mask = 0, jd_val = 0; + + if (!rt5677->pdata.jd1_gpio && + !rt5677->pdata.jd2_gpio && + !rt5677->pdata.jd3_gpio) + return 0; + + if (!i2c->irq) { + dev_err(&i2c->dev, "No interrupt specified\n"); + return -EINVAL; + } + + mutex_init(&rt5677->irq_lock); + INIT_DELAYED_WORK(&rt5677->resume_irq_check, rt5677_resume_irq_check); + + /* + * Select RC as the debounce clock so that GPIO works even when + * MCLK is gated which happens when there is no audio stream + * (SND_SOC_BIAS_OFF). + */ + regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, + RT5677_IRQ_DEBOUNCE_SEL_MASK, + RT5677_IRQ_DEBOUNCE_SEL_RC); + /* Enable auto power on RC when GPIO states are changed */ + regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL1, 0xff, 0xff); + + /* Select and enable jack detection sources per platform data */ + if (rt5677->pdata.jd1_gpio) { + jd_mask |= RT5677_SEL_GPIO_JD1_MASK; + jd_val |= rt5677->pdata.jd1_gpio << RT5677_SEL_GPIO_JD1_SFT; + } + if (rt5677->pdata.jd2_gpio) { + jd_mask |= RT5677_SEL_GPIO_JD2_MASK; + jd_val |= rt5677->pdata.jd2_gpio << RT5677_SEL_GPIO_JD2_SFT; + } + if (rt5677->pdata.jd3_gpio) { + jd_mask |= RT5677_SEL_GPIO_JD3_MASK; + jd_val |= rt5677->pdata.jd3_gpio << RT5677_SEL_GPIO_JD3_SFT; + } + regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1, jd_mask, jd_val); + + /* Set GPIO1 pin to be IRQ output */ + regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, + RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ); + + /* Ready to listen for interrupts */ + rt5677->domain = irq_domain_create_linear(dev_fwnode(&i2c->dev), + RT5677_IRQ_NUM, &rt5677_domain_ops, rt5677); + if (!rt5677->domain) { + dev_err(&i2c->dev, "Failed to create IRQ domain\n"); + return -ENOMEM; + } + + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5677_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "rt5677", rt5677); + if (ret) + dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret); + + rt5677->irq = i2c->irq; + + return ret; +} + +static int rt5677_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct rt5677_priv *rt5677; + int ret; + unsigned int val; + + rt5677 = devm_kzalloc(&i2c->dev, sizeof(struct rt5677_priv), + GFP_KERNEL); + if (rt5677 == NULL) + return -ENOMEM; + + rt5677->dev = &i2c->dev; + rt5677->set_dsp_vad = rt5677_set_dsp_vad; + INIT_DELAYED_WORK(&rt5677->dsp_work, rt5677_dsp_work); + i2c_set_clientdata(i2c, rt5677); + + rt5677->type = (enum rt5677_type)(uintptr_t)device_get_match_data(dev); + if (rt5677->type == 0) + return -EINVAL; + + rt5677_read_device_properties(rt5677, &i2c->dev); + + /* pow-ldo2 and reset are optional. The codec pins may be statically + * connected on the board without gpios. If the gpio device property + * isn't specified, devm_gpiod_get_optional returns NULL. + */ + rt5677->pow_ldo2 = devm_gpiod_get_optional(&i2c->dev, + "realtek,pow-ldo2", GPIOD_OUT_HIGH); + if (IS_ERR(rt5677->pow_ldo2)) { + ret = PTR_ERR(rt5677->pow_ldo2); + dev_err(&i2c->dev, "Failed to request POW_LDO2: %d\n", ret); + return ret; + } + rt5677->reset_pin = devm_gpiod_get_optional(&i2c->dev, + "realtek,reset", GPIOD_OUT_LOW); + if (IS_ERR(rt5677->reset_pin)) { + ret = PTR_ERR(rt5677->reset_pin); + dev_err(&i2c->dev, "Failed to request RESET: %d\n", ret); + return ret; + } + + if (rt5677->pow_ldo2 || rt5677->reset_pin) { + /* Wait a while until I2C bus becomes available. The datasheet + * does not specify the exact we should wait but startup + * sequence mentiones at least a few milliseconds. + */ + msleep(10); + } + + rt5677->regmap_physical = devm_regmap_init_i2c(i2c, + &rt5677_regmap_physical); + if (IS_ERR(rt5677->regmap_physical)) { + ret = PTR_ERR(rt5677->regmap_physical); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + rt5677->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5677_regmap); + if (IS_ERR(rt5677->regmap)) { + ret = PTR_ERR(rt5677->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + regmap_read(rt5677->regmap, RT5677_VENDOR_ID2, &val); + if (val != RT5677_DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5677\n", val); + return -ENODEV; + } + + regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec); + + ret = regmap_register_patch(rt5677->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + if (rt5677->pdata.in1_diff) + regmap_update_bits(rt5677->regmap, RT5677_IN1, + RT5677_IN_DF1, RT5677_IN_DF1); + + if (rt5677->pdata.in2_diff) + regmap_update_bits(rt5677->regmap, RT5677_IN1, + RT5677_IN_DF2, RT5677_IN_DF2); + + if (rt5677->pdata.lout1_diff) + regmap_update_bits(rt5677->regmap, RT5677_LOUT1, + RT5677_LOUT1_L_DF, RT5677_LOUT1_L_DF); + + if (rt5677->pdata.lout2_diff) + regmap_update_bits(rt5677->regmap, RT5677_LOUT1, + RT5677_LOUT2_L_DF, RT5677_LOUT2_L_DF); + + if (rt5677->pdata.lout3_diff) + regmap_update_bits(rt5677->regmap, RT5677_LOUT1, + RT5677_LOUT3_L_DF, RT5677_LOUT3_L_DF); + + if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) { + regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2, + RT5677_GPIO5_FUNC_MASK, + RT5677_GPIO5_FUNC_DMIC); + rt5677_update_gpio_bits(rt5677, RT5677_GPIO5, + RT5677_GPIOx_DIR_MASK, + RT5677_GPIOx_DIR_OUT); + } + + if (rt5677->pdata.micbias1_vdd_3v3) + regmap_update_bits(rt5677->regmap, RT5677_MICBIAS, + RT5677_MICBIAS1_CTRL_VDD_MASK, + RT5677_MICBIAS1_CTRL_VDD_3_3V); + + rt5677_init_gpio(i2c); + ret = rt5677_init_irq(i2c); + if (ret) + dev_err(&i2c->dev, "Failed to initialize irq: %d\n", ret); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_rt5677, + rt5677_dai, ARRAY_SIZE(rt5677_dai)); +} + +static void rt5677_i2c_remove(struct i2c_client *i2c) +{ + rt5677_free_gpio(i2c); +} + +static struct i2c_driver rt5677_i2c_driver = { + .driver = { + .name = RT5677_DRV_NAME, + .of_match_table = rt5677_of_match, + .acpi_match_table = rt5677_acpi_match, + }, + .probe = rt5677_i2c_probe, + .remove = rt5677_i2c_remove, +}; +module_i2c_driver(rt5677_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5677 driver"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); + +MODULE_FIRMWARE("rt5677_elf_vad"); diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h new file mode 100644 index 0000000000..d67ebae067 --- /dev/null +++ b/sound/soc/codecs/rt5677.h @@ -0,0 +1,1808 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5677.h -- RT5677 ALSA SoC audio driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Oder Chiou + */ + +#ifndef __RT5677_H__ +#define __RT5677_H__ + +#include +#include + +/* Info */ +#define RT5677_RESET 0x00 +#define RT5677_VENDOR_ID 0xfd +#define RT5677_VENDOR_ID1 0xfe +#define RT5677_VENDOR_ID2 0xff +/* I/O - Output */ +#define RT5677_LOUT1 0x01 +/* I/O - Input */ +#define RT5677_IN1 0x03 +#define RT5677_MICBIAS 0x04 +/* I/O - SLIMBus */ +#define RT5677_SLIMBUS_PARAM 0x07 +#define RT5677_SLIMBUS_RX 0x08 +#define RT5677_SLIMBUS_CTRL 0x09 +/* I/O */ +#define RT5677_SIDETONE_CTRL 0x13 +/* I/O - ADC/DAC */ +#define RT5677_ANA_DAC1_2_3_SRC 0x15 +#define RT5677_IF_DSP_DAC3_4_MIXER 0x16 +#define RT5677_DAC4_DIG_VOL 0x17 +#define RT5677_DAC3_DIG_VOL 0x18 +#define RT5677_DAC1_DIG_VOL 0x19 +#define RT5677_DAC2_DIG_VOL 0x1a +#define RT5677_IF_DSP_DAC2_MIXER 0x1b +#define RT5677_STO1_ADC_DIG_VOL 0x1c +#define RT5677_MONO_ADC_DIG_VOL 0x1d +#define RT5677_STO1_2_ADC_BST 0x1e +#define RT5677_STO2_ADC_DIG_VOL 0x1f +/* Mixer - D-D */ +#define RT5677_ADC_BST_CTRL2 0x20 +#define RT5677_STO3_4_ADC_BST 0x21 +#define RT5677_STO3_ADC_DIG_VOL 0x22 +#define RT5677_STO4_ADC_DIG_VOL 0x23 +#define RT5677_STO4_ADC_MIXER 0x24 +#define RT5677_STO3_ADC_MIXER 0x25 +#define RT5677_STO2_ADC_MIXER 0x26 +#define RT5677_STO1_ADC_MIXER 0x27 +#define RT5677_MONO_ADC_MIXER 0x28 +#define RT5677_ADC_IF_DSP_DAC1_MIXER 0x29 +#define RT5677_STO1_DAC_MIXER 0x2a +#define RT5677_MONO_DAC_MIXER 0x2b +#define RT5677_DD1_MIXER 0x2c +#define RT5677_DD2_MIXER 0x2d +#define RT5677_IF3_DATA 0x2f +#define RT5677_IF4_DATA 0x30 +/* Mixer - PDM */ +#define RT5677_PDM_OUT_CTRL 0x31 +#define RT5677_PDM_DATA_CTRL1 0x32 +#define RT5677_PDM_DATA_CTRL2 0x33 +#define RT5677_PDM1_DATA_CTRL2 0x34 +#define RT5677_PDM1_DATA_CTRL3 0x35 +#define RT5677_PDM1_DATA_CTRL4 0x36 +#define RT5677_PDM2_DATA_CTRL2 0x37 +#define RT5677_PDM2_DATA_CTRL3 0x38 +#define RT5677_PDM2_DATA_CTRL4 0x39 +/* TDM */ +#define RT5677_TDM1_CTRL1 0x3b +#define RT5677_TDM1_CTRL2 0x3c +#define RT5677_TDM1_CTRL3 0x3d +#define RT5677_TDM1_CTRL4 0x3e +#define RT5677_TDM1_CTRL5 0x3f +#define RT5677_TDM2_CTRL1 0x40 +#define RT5677_TDM2_CTRL2 0x41 +#define RT5677_TDM2_CTRL3 0x42 +#define RT5677_TDM2_CTRL4 0x43 +#define RT5677_TDM2_CTRL5 0x44 +/* I2C_MASTER_CTRL */ +#define RT5677_I2C_MASTER_CTRL1 0x47 +#define RT5677_I2C_MASTER_CTRL2 0x48 +#define RT5677_I2C_MASTER_CTRL3 0x49 +#define RT5677_I2C_MASTER_CTRL4 0x4a +#define RT5677_I2C_MASTER_CTRL5 0x4b +#define RT5677_I2C_MASTER_CTRL6 0x4c +#define RT5677_I2C_MASTER_CTRL7 0x4d +#define RT5677_I2C_MASTER_CTRL8 0x4e +/* DMIC */ +#define RT5677_DMIC_CTRL1 0x50 +#define RT5677_DMIC_CTRL2 0x51 +/* Haptic Generator */ +#define RT5677_HAP_GENE_CTRL1 0x56 +#define RT5677_HAP_GENE_CTRL2 0x57 +#define RT5677_HAP_GENE_CTRL3 0x58 +#define RT5677_HAP_GENE_CTRL4 0x59 +#define RT5677_HAP_GENE_CTRL5 0x5a +#define RT5677_HAP_GENE_CTRL6 0x5b +#define RT5677_HAP_GENE_CTRL7 0x5c +#define RT5677_HAP_GENE_CTRL8 0x5d +#define RT5677_HAP_GENE_CTRL9 0x5e +#define RT5677_HAP_GENE_CTRL10 0x5f +/* Power */ +#define RT5677_PWR_DIG1 0x61 +#define RT5677_PWR_DIG2 0x62 +#define RT5677_PWR_ANLG1 0x63 +#define RT5677_PWR_ANLG2 0x64 +#define RT5677_PWR_DSP1 0x65 +#define RT5677_PWR_DSP_ST 0x66 +#define RT5677_PWR_DSP2 0x67 +#define RT5677_ADC_DAC_HPF_CTRL1 0x68 +/* Private Register Control */ +#define RT5677_PRIV_INDEX 0x6a +#define RT5677_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5677_I2S4_SDP 0x6f +#define RT5677_I2S1_SDP 0x70 +#define RT5677_I2S2_SDP 0x71 +#define RT5677_I2S3_SDP 0x72 +#define RT5677_CLK_TREE_CTRL1 0x73 +#define RT5677_CLK_TREE_CTRL2 0x74 +#define RT5677_CLK_TREE_CTRL3 0x75 +/* Function - Analog */ +#define RT5677_PLL1_CTRL1 0x7a +#define RT5677_PLL1_CTRL2 0x7b +#define RT5677_PLL2_CTRL1 0x7c +#define RT5677_PLL2_CTRL2 0x7d +#define RT5677_GLB_CLK1 0x80 +#define RT5677_GLB_CLK2 0x81 +#define RT5677_ASRC_1 0x83 +#define RT5677_ASRC_2 0x84 +#define RT5677_ASRC_3 0x85 +#define RT5677_ASRC_4 0x86 +#define RT5677_ASRC_5 0x87 +#define RT5677_ASRC_6 0x88 +#define RT5677_ASRC_7 0x89 +#define RT5677_ASRC_8 0x8a +#define RT5677_ASRC_9 0x8b +#define RT5677_ASRC_10 0x8c +#define RT5677_ASRC_11 0x8d +#define RT5677_ASRC_12 0x8e +#define RT5677_ASRC_13 0x8f +#define RT5677_ASRC_14 0x90 +#define RT5677_ASRC_15 0x91 +#define RT5677_ASRC_16 0x92 +#define RT5677_ASRC_17 0x93 +#define RT5677_ASRC_18 0x94 +#define RT5677_ASRC_19 0x95 +#define RT5677_ASRC_20 0x97 +#define RT5677_ASRC_21 0x98 +#define RT5677_ASRC_22 0x99 +#define RT5677_ASRC_23 0x9a +#define RT5677_VAD_CTRL1 0x9c +#define RT5677_VAD_CTRL2 0x9d +#define RT5677_VAD_CTRL3 0x9e +#define RT5677_VAD_CTRL4 0x9f +#define RT5677_VAD_CTRL5 0xa0 +/* Function - Digital */ +#define RT5677_DSP_INB_CTRL1 0xa3 +#define RT5677_DSP_INB_CTRL2 0xa4 +#define RT5677_DSP_IN_OUTB_CTRL 0xa5 +#define RT5677_DSP_OUTB0_1_DIG_VOL 0xa6 +#define RT5677_DSP_OUTB2_3_DIG_VOL 0xa7 +#define RT5677_DSP_OUTB4_5_DIG_VOL 0xa8 +#define RT5677_DSP_OUTB6_7_DIG_VOL 0xa9 +#define RT5677_ADC_EQ_CTRL1 0xae +#define RT5677_ADC_EQ_CTRL2 0xaf +#define RT5677_EQ_CTRL1 0xb0 +#define RT5677_EQ_CTRL2 0xb1 +#define RT5677_EQ_CTRL3 0xb2 +#define RT5677_SOFT_VOL_ZERO_CROSS1 0xb3 +#define RT5677_JD_CTRL1 0xb5 +#define RT5677_JD_CTRL2 0xb6 +#define RT5677_JD_CTRL3 0xb8 +#define RT5677_IRQ_CTRL1 0xbd +#define RT5677_IRQ_CTRL2 0xbe +#define RT5677_GPIO_ST 0xbf +#define RT5677_GPIO_CTRL1 0xc0 +#define RT5677_GPIO_CTRL2 0xc1 +#define RT5677_GPIO_CTRL3 0xc2 +#define RT5677_STO1_ADC_HI_FILTER1 0xc5 +#define RT5677_STO1_ADC_HI_FILTER2 0xc6 +#define RT5677_MONO_ADC_HI_FILTER1 0xc7 +#define RT5677_MONO_ADC_HI_FILTER2 0xc8 +#define RT5677_STO2_ADC_HI_FILTER1 0xc9 +#define RT5677_STO2_ADC_HI_FILTER2 0xca +#define RT5677_STO3_ADC_HI_FILTER1 0xcb +#define RT5677_STO3_ADC_HI_FILTER2 0xcc +#define RT5677_STO4_ADC_HI_FILTER1 0xcd +#define RT5677_STO4_ADC_HI_FILTER2 0xce +#define RT5677_MB_DRC_CTRL1 0xd0 +#define RT5677_DRC1_CTRL1 0xd2 +#define RT5677_DRC1_CTRL2 0xd3 +#define RT5677_DRC1_CTRL3 0xd4 +#define RT5677_DRC1_CTRL4 0xd5 +#define RT5677_DRC1_CTRL5 0xd6 +#define RT5677_DRC1_CTRL6 0xd7 +#define RT5677_DRC2_CTRL1 0xd8 +#define RT5677_DRC2_CTRL2 0xd9 +#define RT5677_DRC2_CTRL3 0xda +#define RT5677_DRC2_CTRL4 0xdb +#define RT5677_DRC2_CTRL5 0xdc +#define RT5677_DRC2_CTRL6 0xdd +#define RT5677_DRC1_HL_CTRL1 0xde +#define RT5677_DRC1_HL_CTRL2 0xdf +#define RT5677_DRC2_HL_CTRL1 0xe0 +#define RT5677_DRC2_HL_CTRL2 0xe1 +#define RT5677_DSP_INB1_SRC_CTRL1 0xe3 +#define RT5677_DSP_INB1_SRC_CTRL2 0xe4 +#define RT5677_DSP_INB1_SRC_CTRL3 0xe5 +#define RT5677_DSP_INB1_SRC_CTRL4 0xe6 +#define RT5677_DSP_INB2_SRC_CTRL1 0xe7 +#define RT5677_DSP_INB2_SRC_CTRL2 0xe8 +#define RT5677_DSP_INB2_SRC_CTRL3 0xe9 +#define RT5677_DSP_INB2_SRC_CTRL4 0xea +#define RT5677_DSP_INB3_SRC_CTRL1 0xeb +#define RT5677_DSP_INB3_SRC_CTRL2 0xec +#define RT5677_DSP_INB3_SRC_CTRL3 0xed +#define RT5677_DSP_INB3_SRC_CTRL4 0xee +#define RT5677_DSP_OUTB1_SRC_CTRL1 0xef +#define RT5677_DSP_OUTB1_SRC_CTRL2 0xf0 +#define RT5677_DSP_OUTB1_SRC_CTRL3 0xf1 +#define RT5677_DSP_OUTB1_SRC_CTRL4 0xf2 +#define RT5677_DSP_OUTB2_SRC_CTRL1 0xf3 +#define RT5677_DSP_OUTB2_SRC_CTRL2 0xf4 +#define RT5677_DSP_OUTB2_SRC_CTRL3 0xf5 +#define RT5677_DSP_OUTB2_SRC_CTRL4 0xf6 + +/* Virtual DSP Mixer Control */ +#define RT5677_DSP_OUTB_0123_MIXER_CTRL 0xf7 +#define RT5677_DSP_OUTB_45_MIXER_CTRL 0xf8 +#define RT5677_DSP_OUTB_67_MIXER_CTRL 0xf9 + +/* General Control */ +#define RT5677_DIG_MISC 0xfa +#define RT5677_GEN_CTRL1 0xfb +#define RT5677_GEN_CTRL2 0xfc + +/* DSP Mode I2C Control*/ +#define RT5677_DSP_I2C_OP_CODE 0x00 +#define RT5677_DSP_I2C_ADDR_LSB 0x01 +#define RT5677_DSP_I2C_ADDR_MSB 0x02 +#define RT5677_DSP_I2C_DATA_LSB 0x03 +#define RT5677_DSP_I2C_DATA_MSB 0x04 + +/* Index of Codec Private Register definition */ +#define RT5677_PR_DRC1_CTRL_1 0x01 +#define RT5677_PR_DRC1_CTRL_2 0x02 +#define RT5677_PR_DRC1_CTRL_3 0x03 +#define RT5677_PR_DRC1_CTRL_4 0x04 +#define RT5677_PR_DRC1_CTRL_5 0x05 +#define RT5677_PR_DRC1_CTRL_6 0x06 +#define RT5677_PR_DRC1_CTRL_7 0x07 +#define RT5677_PR_DRC2_CTRL_1 0x08 +#define RT5677_PR_DRC2_CTRL_2 0x09 +#define RT5677_PR_DRC2_CTRL_3 0x0a +#define RT5677_PR_DRC2_CTRL_4 0x0b +#define RT5677_PR_DRC2_CTRL_5 0x0c +#define RT5677_PR_DRC2_CTRL_6 0x0d +#define RT5677_PR_DRC2_CTRL_7 0x0e +#define RT5677_BIAS_CUR1 0x10 +#define RT5677_BIAS_CUR2 0x12 +#define RT5677_BIAS_CUR3 0x13 +#define RT5677_BIAS_CUR4 0x14 +#define RT5677_BIAS_CUR5 0x15 +#define RT5677_VREF_LOUT_CTRL 0x17 +#define RT5677_DIG_VOL_CTRL1 0x1a +#define RT5677_DIG_VOL_CTRL2 0x1b +#define RT5677_ANA_ADC_GAIN_CTRL 0x1e +#define RT5677_VAD_SRAM_TEST1 0x20 +#define RT5677_VAD_SRAM_TEST2 0x21 +#define RT5677_VAD_SRAM_TEST3 0x22 +#define RT5677_VAD_SRAM_TEST4 0x23 +#define RT5677_PAD_DRV_CTRL 0x26 +#define RT5677_DIG_IN_PIN_ST_CTRL1 0x29 +#define RT5677_DIG_IN_PIN_ST_CTRL2 0x2a +#define RT5677_DIG_IN_PIN_ST_CTRL3 0x2b +#define RT5677_PLL1_INT 0x38 +#define RT5677_PLL2_INT 0x39 +#define RT5677_TEST_CTRL1 0x3a +#define RT5677_TEST_CTRL2 0x3b +#define RT5677_TEST_CTRL3 0x3c +#define RT5677_CHOP_DAC_ADC 0x3d +#define RT5677_SOFT_DEPOP_DAC_CLK_CTRL 0x3e +#define RT5677_CROSS_OVER_FILTER1 0x90 +#define RT5677_CROSS_OVER_FILTER2 0x91 +#define RT5677_CROSS_OVER_FILTER3 0x92 +#define RT5677_CROSS_OVER_FILTER4 0x93 +#define RT5677_CROSS_OVER_FILTER5 0x94 +#define RT5677_CROSS_OVER_FILTER6 0x95 +#define RT5677_CROSS_OVER_FILTER7 0x96 +#define RT5677_CROSS_OVER_FILTER8 0x97 +#define RT5677_CROSS_OVER_FILTER9 0x98 +#define RT5677_CROSS_OVER_FILTER10 0x99 + +/* global definition */ +#define RT5677_L_MUTE (0x1 << 15) +#define RT5677_L_MUTE_SFT 15 +#define RT5677_VOL_L_MUTE (0x1 << 14) +#define RT5677_VOL_L_SFT 14 +#define RT5677_R_MUTE (0x1 << 7) +#define RT5677_R_MUTE_SFT 7 +#define RT5677_VOL_R_MUTE (0x1 << 6) +#define RT5677_VOL_R_SFT 6 +#define RT5677_L_VOL_MASK (0x7f << 9) +#define RT5677_L_VOL_SFT 9 +#define RT5677_R_VOL_MASK (0x7f << 1) +#define RT5677_R_VOL_SFT 1 + +/* LOUT1 Control (0x01) */ +#define RT5677_LOUT1_L_MUTE (0x1 << 15) +#define RT5677_LOUT1_L_MUTE_SFT (15) +#define RT5677_LOUT1_L_DF (0x1 << 14) +#define RT5677_LOUT1_L_DF_SFT (14) +#define RT5677_LOUT2_L_MUTE (0x1 << 13) +#define RT5677_LOUT2_L_MUTE_SFT (13) +#define RT5677_LOUT2_L_DF (0x1 << 12) +#define RT5677_LOUT2_L_DF_SFT (12) +#define RT5677_LOUT3_L_MUTE (0x1 << 11) +#define RT5677_LOUT3_L_MUTE_SFT (11) +#define RT5677_LOUT3_L_DF (0x1 << 10) +#define RT5677_LOUT3_L_DF_SFT (10) +#define RT5677_LOUT1_ENH_DRV (0x1 << 9) +#define RT5677_LOUT1_ENH_DRV_SFT (9) +#define RT5677_LOUT2_ENH_DRV (0x1 << 8) +#define RT5677_LOUT2_ENH_DRV_SFT (8) +#define RT5677_LOUT3_ENH_DRV (0x1 << 7) +#define RT5677_LOUT3_ENH_DRV_SFT (7) + +/* IN1 Control (0x03) */ +#define RT5677_BST_MASK1 (0xf << 12) +#define RT5677_BST_SFT1 12 +#define RT5677_BST_MASK2 (0xf << 8) +#define RT5677_BST_SFT2 8 +#define RT5677_IN_DF1 (0x1 << 7) +#define RT5677_IN_DF1_SFT 7 +#define RT5677_IN_DF2 (0x1 << 6) +#define RT5677_IN_DF2_SFT 6 + +/* Micbias Control (0x04) */ +#define RT5677_MICBIAS1_OUTVOLT_MASK (0x1 << 15) +#define RT5677_MICBIAS1_OUTVOLT_SFT (15) +#define RT5677_MICBIAS1_OUTVOLT_2_7V (0x0 << 15) +#define RT5677_MICBIAS1_OUTVOLT_2_25V (0x1 << 15) +#define RT5677_MICBIAS1_CTRL_VDD_MASK (0x1 << 14) +#define RT5677_MICBIAS1_CTRL_VDD_SFT (14) +#define RT5677_MICBIAS1_CTRL_VDD_1_8V (0x0 << 14) +#define RT5677_MICBIAS1_CTRL_VDD_3_3V (0x1 << 14) +#define RT5677_MICBIAS1_OVCD_MASK (0x1 << 11) +#define RT5677_MICBIAS1_OVCD_SHIFT (11) +#define RT5677_MICBIAS1_OVCD_DIS (0x0 << 11) +#define RT5677_MICBIAS1_OVCD_EN (0x1 << 11) +#define RT5677_MICBIAS1_OVTH_MASK (0x3 << 9) +#define RT5677_MICBIAS1_OVTH_SFT 9 +#define RT5677_MICBIAS1_OVTH_640UA (0x0 << 9) +#define RT5677_MICBIAS1_OVTH_1280UA (0x1 << 9) +#define RT5677_MICBIAS1_OVTH_1920UA (0x2 << 9) + +/* SLIMbus Parameter (0x07) */ + +/* SLIMbus Rx (0x08) */ +#define RT5677_SLB_ADC4_MASK (0x3 << 6) +#define RT5677_SLB_ADC4_SFT 6 +#define RT5677_SLB_ADC3_MASK (0x3 << 4) +#define RT5677_SLB_ADC3_SFT 4 +#define RT5677_SLB_ADC2_MASK (0x3 << 2) +#define RT5677_SLB_ADC2_SFT 2 +#define RT5677_SLB_ADC1_MASK (0x3 << 0) +#define RT5677_SLB_ADC1_SFT 0 + +/* SLIMBus control (0x09) */ + +/* Sidetone Control (0x13) */ +#define RT5677_ST_HPF_SEL_MASK (0x7 << 13) +#define RT5677_ST_HPF_SEL_SFT 13 +#define RT5677_ST_HPF_PATH (0x1 << 12) +#define RT5677_ST_HPF_PATH_SFT 12 +#define RT5677_ST_SEL_MASK (0x7 << 9) +#define RT5677_ST_SEL_SFT 9 +#define RT5677_ST_EN (0x1 << 6) +#define RT5677_ST_EN_SFT 6 +#define RT5677_ST_GAIN (0x1 << 5) +#define RT5677_ST_GAIN_SFT 5 +#define RT5677_ST_VOL_MASK (0x1f << 0) +#define RT5677_ST_VOL_SFT 0 + +/* Analog DAC1/2/3 Source Control (0x15) */ +#define RT5677_ANA_DAC3_SRC_SEL_MASK (0x3 << 4) +#define RT5677_ANA_DAC3_SRC_SEL_SFT 4 +#define RT5677_ANA_DAC1_2_SRC_SEL_MASK (0x3 << 0) +#define RT5677_ANA_DAC1_2_SRC_SEL_SFT 0 + +/* IF/DSP to DAC3/4 Mixer Control (0x16) */ +#define RT5677_M_DAC4_L_VOL (0x1 << 15) +#define RT5677_M_DAC4_L_VOL_SFT 15 +#define RT5677_SEL_DAC4_L_SRC_MASK (0x7 << 12) +#define RT5677_SEL_DAC4_L_SRC_SFT 12 +#define RT5677_M_DAC4_R_VOL (0x1 << 11) +#define RT5677_M_DAC4_R_VOL_SFT 11 +#define RT5677_SEL_DAC4_R_SRC_MASK (0x7 << 8) +#define RT5677_SEL_DAC4_R_SRC_SFT 8 +#define RT5677_M_DAC3_L_VOL (0x1 << 7) +#define RT5677_M_DAC3_L_VOL_SFT 7 +#define RT5677_SEL_DAC3_L_SRC_MASK (0x7 << 4) +#define RT5677_SEL_DAC3_L_SRC_SFT 4 +#define RT5677_M_DAC3_R_VOL (0x1 << 3) +#define RT5677_M_DAC3_R_VOL_SFT 3 +#define RT5677_SEL_DAC3_R_SRC_MASK (0x7 << 0) +#define RT5677_SEL_DAC3_R_SRC_SFT 0 + +/* DAC4 Digital Volume (0x17) */ +#define RT5677_DAC4_L_VOL_MASK (0xff << 8) +#define RT5677_DAC4_L_VOL_SFT 8 +#define RT5677_DAC4_R_VOL_MASK (0xff) +#define RT5677_DAC4_R_VOL_SFT 0 + +/* DAC3 Digital Volume (0x18) */ +#define RT5677_DAC3_L_VOL_MASK (0xff << 8) +#define RT5677_DAC3_L_VOL_SFT 8 +#define RT5677_DAC3_R_VOL_MASK (0xff) +#define RT5677_DAC3_R_VOL_SFT 0 + +/* DAC3 Digital Volume (0x19) */ +#define RT5677_DAC1_L_VOL_MASK (0xff << 8) +#define RT5677_DAC1_L_VOL_SFT 8 +#define RT5677_DAC1_R_VOL_MASK (0xff) +#define RT5677_DAC1_R_VOL_SFT 0 + +/* DAC2 Digital Volume (0x1a) */ +#define RT5677_DAC2_L_VOL_MASK (0xff << 8) +#define RT5677_DAC2_L_VOL_SFT 8 +#define RT5677_DAC2_R_VOL_MASK (0xff) +#define RT5677_DAC2_R_VOL_SFT 0 + +/* IF/DSP to DAC2 Mixer Control (0x1b) */ +#define RT5677_M_DAC2_L_VOL (0x1 << 7) +#define RT5677_M_DAC2_L_VOL_SFT 7 +#define RT5677_SEL_DAC2_L_SRC_MASK (0x7 << 4) +#define RT5677_SEL_DAC2_L_SRC_SFT 4 +#define RT5677_M_DAC2_R_VOL (0x1 << 3) +#define RT5677_M_DAC2_R_VOL_SFT 3 +#define RT5677_SEL_DAC2_R_SRC_MASK (0x7 << 0) +#define RT5677_SEL_DAC2_R_SRC_SFT 0 + +/* Stereo1 ADC Digital Volume Control (0x1c) */ +#define RT5677_STO1_ADC_L_VOL_MASK (0x3f << 9) +#define RT5677_STO1_ADC_L_VOL_SFT 9 +#define RT5677_STO1_ADC_R_VOL_MASK (0x3f << 1) +#define RT5677_STO1_ADC_R_VOL_SFT 1 + +/* Mono ADC Digital Volume Control (0x1d) */ +#define RT5677_MONO_ADC_L_VOL_MASK (0x3f << 9) +#define RT5677_MONO_ADC_L_VOL_SFT 9 +#define RT5677_MONO_ADC_R_VOL_MASK (0x3f << 1) +#define RT5677_MONO_ADC_R_VOL_SFT 1 + +/* Stereo 1/2 ADC Boost Gain Control (0x1e) */ +#define RT5677_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5677_STO1_ADC_L_BST_SFT 14 +#define RT5677_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5677_STO1_ADC_R_BST_SFT 12 +#define RT5677_STO1_ADC_COMP_MASK (0x3 << 10) +#define RT5677_STO1_ADC_COMP_SFT 10 +#define RT5677_STO2_ADC_L_BST_MASK (0x3 << 8) +#define RT5677_STO2_ADC_L_BST_SFT 8 +#define RT5677_STO2_ADC_R_BST_MASK (0x3 << 6) +#define RT5677_STO2_ADC_R_BST_SFT 6 +#define RT5677_STO2_ADC_COMP_MASK (0x3 << 4) +#define RT5677_STO2_ADC_COMP_SFT 4 + +/* Stereo2 ADC Digital Volume Control (0x1f) */ +#define RT5677_STO2_ADC_L_VOL_MASK (0x7f << 8) +#define RT5677_STO2_ADC_L_VOL_SFT 8 +#define RT5677_STO2_ADC_R_VOL_MASK (0x7f) +#define RT5677_STO2_ADC_R_VOL_SFT 0 + +/* ADC Boost Gain Control 2 (0x20) */ +#define RT5677_MONO_ADC_L_BST_MASK (0x3 << 14) +#define RT5677_MONO_ADC_L_BST_SFT 14 +#define RT5677_MONO_ADC_R_BST_MASK (0x3 << 12) +#define RT5677_MONO_ADC_R_BST_SFT 12 +#define RT5677_MONO_ADC_COMP_MASK (0x3 << 10) +#define RT5677_MONO_ADC_COMP_SFT 10 + +/* Stereo 3/4 ADC Boost Gain Control (0x21) */ +#define RT5677_STO3_ADC_L_BST_MASK (0x3 << 14) +#define RT5677_STO3_ADC_L_BST_SFT 14 +#define RT5677_STO3_ADC_R_BST_MASK (0x3 << 12) +#define RT5677_STO3_ADC_R_BST_SFT 12 +#define RT5677_STO3_ADC_COMP_MASK (0x3 << 10) +#define RT5677_STO3_ADC_COMP_SFT 10 +#define RT5677_STO4_ADC_L_BST_MASK (0x3 << 8) +#define RT5677_STO4_ADC_L_BST_SFT 8 +#define RT5677_STO4_ADC_R_BST_MASK (0x3 << 6) +#define RT5677_STO4_ADC_R_BST_SFT 6 +#define RT5677_STO4_ADC_COMP_MASK (0x3 << 4) +#define RT5677_STO4_ADC_COMP_SFT 4 + +/* Stereo3 ADC Digital Volume Control (0x22) */ +#define RT5677_STO3_ADC_L_VOL_MASK (0x7f << 8) +#define RT5677_STO3_ADC_L_VOL_SFT 8 +#define RT5677_STO3_ADC_R_VOL_MASK (0x7f) +#define RT5677_STO3_ADC_R_VOL_SFT 0 + +/* Stereo4 ADC Digital Volume Control (0x23) */ +#define RT5677_STO4_ADC_L_VOL_MASK (0x7f << 8) +#define RT5677_STO4_ADC_L_VOL_SFT 8 +#define RT5677_STO4_ADC_R_VOL_MASK (0x7f) +#define RT5677_STO4_ADC_R_VOL_SFT 0 + +/* Stereo4 ADC Mixer control (0x24) */ +#define RT5677_M_STO4_ADC_L2 (0x1 << 15) +#define RT5677_M_STO4_ADC_L2_SFT 15 +#define RT5677_M_STO4_ADC_L1 (0x1 << 14) +#define RT5677_M_STO4_ADC_L1_SFT 14 +#define RT5677_SEL_STO4_ADC1_MASK (0x3 << 12) +#define RT5677_SEL_STO4_ADC1_SFT 12 +#define RT5677_SEL_STO4_ADC2_MASK (0x3 << 10) +#define RT5677_SEL_STO4_ADC2_SFT 10 +#define RT5677_SEL_STO4_DMIC_MASK (0x3 << 8) +#define RT5677_SEL_STO4_DMIC_SFT 8 +#define RT5677_M_STO4_ADC_R1 (0x1 << 7) +#define RT5677_M_STO4_ADC_R1_SFT 7 +#define RT5677_M_STO4_ADC_R2 (0x1 << 6) +#define RT5677_M_STO4_ADC_R2_SFT 6 + +/* Stereo3 ADC Mixer control (0x25) */ +#define RT5677_M_STO3_ADC_L2 (0x1 << 15) +#define RT5677_M_STO3_ADC_L2_SFT 15 +#define RT5677_M_STO3_ADC_L1 (0x1 << 14) +#define RT5677_M_STO3_ADC_L1_SFT 14 +#define RT5677_SEL_STO3_ADC1_MASK (0x3 << 12) +#define RT5677_SEL_STO3_ADC1_SFT 12 +#define RT5677_SEL_STO3_ADC2_MASK (0x3 << 10) +#define RT5677_SEL_STO3_ADC2_SFT 10 +#define RT5677_SEL_STO3_DMIC_MASK (0x3 << 8) +#define RT5677_SEL_STO3_DMIC_SFT 8 +#define RT5677_M_STO3_ADC_R1 (0x1 << 7) +#define RT5677_M_STO3_ADC_R1_SFT 7 +#define RT5677_M_STO3_ADC_R2 (0x1 << 6) +#define RT5677_M_STO3_ADC_R2_SFT 6 + +/* Stereo2 ADC Mixer Control (0x26) */ +#define RT5677_M_STO2_ADC_L2 (0x1 << 15) +#define RT5677_M_STO2_ADC_L2_SFT 15 +#define RT5677_M_STO2_ADC_L1 (0x1 << 14) +#define RT5677_M_STO2_ADC_L1_SFT 14 +#define RT5677_SEL_STO2_ADC1_MASK (0x3 << 12) +#define RT5677_SEL_STO2_ADC1_SFT 12 +#define RT5677_SEL_STO2_ADC2_MASK (0x3 << 10) +#define RT5677_SEL_STO2_ADC2_SFT 10 +#define RT5677_SEL_STO2_DMIC_MASK (0x3 << 8) +#define RT5677_SEL_STO2_DMIC_SFT 8 +#define RT5677_M_STO2_ADC_R1 (0x1 << 7) +#define RT5677_M_STO2_ADC_R1_SFT 7 +#define RT5677_M_STO2_ADC_R2 (0x1 << 6) +#define RT5677_M_STO2_ADC_R2_SFT 6 +#define RT5677_SEL_STO2_LR_MIX_MASK (0x1 << 0) +#define RT5677_SEL_STO2_LR_MIX_SFT 0 +#define RT5677_SEL_STO2_LR_MIX_L (0x0 << 0) +#define RT5677_SEL_STO2_LR_MIX_LR (0x1 << 0) + +/* Stereo1 ADC Mixer control (0x27) */ +#define RT5677_M_STO1_ADC_L2 (0x1 << 15) +#define RT5677_M_STO1_ADC_L2_SFT 15 +#define RT5677_M_STO1_ADC_L1 (0x1 << 14) +#define RT5677_M_STO1_ADC_L1_SFT 14 +#define RT5677_SEL_STO1_ADC1_MASK (0x3 << 12) +#define RT5677_SEL_STO1_ADC1_SFT 12 +#define RT5677_SEL_STO1_ADC2_MASK (0x3 << 10) +#define RT5677_SEL_STO1_ADC2_SFT 10 +#define RT5677_SEL_STO1_DMIC_MASK (0x3 << 8) +#define RT5677_SEL_STO1_DMIC_SFT 8 +#define RT5677_M_STO1_ADC_R1 (0x1 << 7) +#define RT5677_M_STO1_ADC_R1_SFT 7 +#define RT5677_M_STO1_ADC_R2 (0x1 << 6) +#define RT5677_M_STO1_ADC_R2_SFT 6 + +/* Mono ADC Mixer control (0x28) */ +#define RT5677_M_MONO_ADC_L2 (0x1 << 15) +#define RT5677_M_MONO_ADC_L2_SFT 15 +#define RT5677_M_MONO_ADC_L1 (0x1 << 14) +#define RT5677_M_MONO_ADC_L1_SFT 14 +#define RT5677_SEL_MONO_ADC_L1_MASK (0x3 << 12) +#define RT5677_SEL_MONO_ADC_L1_SFT 12 +#define RT5677_SEL_MONO_ADC_L2_MASK (0x3 << 10) +#define RT5677_SEL_MONO_ADC_L2_SFT 10 +#define RT5677_SEL_MONO_DMIC_L_MASK (0x3 << 8) +#define RT5677_SEL_MONO_DMIC_L_SFT 8 +#define RT5677_M_MONO_ADC_R1 (0x1 << 7) +#define RT5677_M_MONO_ADC_R1_SFT 7 +#define RT5677_M_MONO_ADC_R2 (0x1 << 6) +#define RT5677_M_MONO_ADC_R2_SFT 6 +#define RT5677_SEL_MONO_ADC_R1_MASK (0x3 << 4) +#define RT5677_SEL_MONO_ADC_R1_SFT 4 +#define RT5677_SEL_MONO_ADC_R2_MASK (0x3 << 2) +#define RT5677_SEL_MONO_ADC_R2_SFT 2 +#define RT5677_SEL_MONO_DMIC_R_MASK (0x3 << 0) +#define RT5677_SEL_MONO_DMIC_R_SFT 0 + +/* ADC/IF/DSP to DAC1 Mixer control (0x29) */ +#define RT5677_M_ADDA_MIXER1_L (0x1 << 15) +#define RT5677_M_ADDA_MIXER1_L_SFT 15 +#define RT5677_M_DAC1_L (0x1 << 14) +#define RT5677_M_DAC1_L_SFT 14 +#define RT5677_DAC1_L_SEL_MASK (0x7 << 8) +#define RT5677_DAC1_L_SEL_SFT 8 +#define RT5677_M_ADDA_MIXER1_R (0x1 << 7) +#define RT5677_M_ADDA_MIXER1_R_SFT 7 +#define RT5677_M_DAC1_R (0x1 << 6) +#define RT5677_M_DAC1_R_SFT 6 +#define RT5677_ADDA1_SEL_MASK (0x3 << 0) +#define RT5677_ADDA1_SEL_SFT 0 + +/* Stereo1 DAC Mixer L/R Control (0x2a) */ +#define RT5677_M_ST_DAC1_L (0x1 << 15) +#define RT5677_M_ST_DAC1_L_SFT 15 +#define RT5677_M_DAC1_L_STO_L (0x1 << 13) +#define RT5677_M_DAC1_L_STO_L_SFT 13 +#define RT5677_DAC1_L_STO_L_VOL_MASK (0x1 << 12) +#define RT5677_DAC1_L_STO_L_VOL_SFT 12 +#define RT5677_M_DAC2_L_STO_L (0x1 << 11) +#define RT5677_M_DAC2_L_STO_L_SFT 11 +#define RT5677_DAC2_L_STO_L_VOL_MASK (0x1 << 10) +#define RT5677_DAC2_L_STO_L_VOL_SFT 10 +#define RT5677_M_DAC1_R_STO_L (0x1 << 9) +#define RT5677_M_DAC1_R_STO_L_SFT 9 +#define RT5677_DAC1_R_STO_L_VOL_MASK (0x1 << 8) +#define RT5677_DAC1_R_STO_L_VOL_SFT 8 +#define RT5677_M_ST_DAC1_R (0x1 << 7) +#define RT5677_M_ST_DAC1_R_SFT 7 +#define RT5677_M_DAC1_R_STO_R (0x1 << 5) +#define RT5677_M_DAC1_R_STO_R_SFT 5 +#define RT5677_DAC1_R_STO_R_VOL_MASK (0x1 << 4) +#define RT5677_DAC1_R_STO_R_VOL_SFT 4 +#define RT5677_M_DAC2_R_STO_R (0x1 << 3) +#define RT5677_M_DAC2_R_STO_R_SFT 3 +#define RT5677_DAC2_R_STO_R_VOL_MASK (0x1 << 2) +#define RT5677_DAC2_R_STO_R_VOL_SFT 2 +#define RT5677_M_DAC1_L_STO_R (0x1 << 1) +#define RT5677_M_DAC1_L_STO_R_SFT 1 +#define RT5677_DAC1_L_STO_R_VOL_MASK (0x1 << 0) +#define RT5677_DAC1_L_STO_R_VOL_SFT 0 + +/* Mono DAC Mixer L/R Control (0x2b) */ +#define RT5677_M_ST_DAC2_L (0x1 << 15) +#define RT5677_M_ST_DAC2_L_SFT 15 +#define RT5677_M_DAC2_L_MONO_L (0x1 << 13) +#define RT5677_M_DAC2_L_MONO_L_SFT 13 +#define RT5677_DAC2_L_MONO_L_VOL_MASK (0x1 << 12) +#define RT5677_DAC2_L_MONO_L_VOL_SFT 12 +#define RT5677_M_DAC2_R_MONO_L (0x1 << 11) +#define RT5677_M_DAC2_R_MONO_L_SFT 11 +#define RT5677_DAC2_R_MONO_L_VOL_MASK (0x1 << 10) +#define RT5677_DAC2_R_MONO_L_VOL_SFT 10 +#define RT5677_M_DAC1_L_MONO_L (0x1 << 9) +#define RT5677_M_DAC1_L_MONO_L_SFT 9 +#define RT5677_DAC1_L_MONO_L_VOL_MASK (0x1 << 8) +#define RT5677_DAC1_L_MONO_L_VOL_SFT 8 +#define RT5677_M_ST_DAC2_R (0x1 << 7) +#define RT5677_M_ST_DAC2_R_SFT 7 +#define RT5677_M_DAC2_R_MONO_R (0x1 << 5) +#define RT5677_M_DAC2_R_MONO_R_SFT 5 +#define RT5677_DAC2_R_MONO_R_VOL_MASK (0x1 << 4) +#define RT5677_DAC2_R_MONO_R_VOL_SFT 4 +#define RT5677_M_DAC1_R_MONO_R (0x1 << 3) +#define RT5677_M_DAC1_R_MONO_R_SFT 3 +#define RT5677_DAC1_R_MONO_R_VOL_MASK (0x1 << 2) +#define RT5677_DAC1_R_MONO_R_VOL_SFT 2 +#define RT5677_M_DAC2_L_MONO_R (0x1 << 1) +#define RT5677_M_DAC2_L_MONO_R_SFT 1 +#define RT5677_DAC2_L_MONO_R_VOL_MASK (0x1 << 0) +#define RT5677_DAC2_L_MONO_R_VOL_SFT 0 + +/* DD Mixer 1 Control (0x2c) */ +#define RT5677_M_STO_L_DD1_L (0x1 << 15) +#define RT5677_M_STO_L_DD1_L_SFT 15 +#define RT5677_STO_L_DD1_L_VOL_MASK (0x1 << 14) +#define RT5677_STO_L_DD1_L_VOL_SFT 14 +#define RT5677_M_MONO_L_DD1_L (0x1 << 13) +#define RT5677_M_MONO_L_DD1_L_SFT 13 +#define RT5677_MONO_L_DD1_L_VOL_MASK (0x1 << 12) +#define RT5677_MONO_L_DD1_L_VOL_SFT 12 +#define RT5677_M_DAC3_L_DD1_L (0x1 << 11) +#define RT5677_M_DAC3_L_DD1_L_SFT 11 +#define RT5677_DAC3_L_DD1_L_VOL_MASK (0x1 << 10) +#define RT5677_DAC3_L_DD1_L_VOL_SFT 10 +#define RT5677_M_DAC3_R_DD1_L (0x1 << 9) +#define RT5677_M_DAC3_R_DD1_L_SFT 9 +#define RT5677_DAC3_R_DD1_L_VOL_MASK (0x1 << 8) +#define RT5677_DAC3_R_DD1_L_VOL_SFT 8 +#define RT5677_M_STO_R_DD1_R (0x1 << 7) +#define RT5677_M_STO_R_DD1_R_SFT 7 +#define RT5677_STO_R_DD1_R_VOL_MASK (0x1 << 6) +#define RT5677_STO_R_DD1_R_VOL_SFT 6 +#define RT5677_M_MONO_R_DD1_R (0x1 << 5) +#define RT5677_M_MONO_R_DD1_R_SFT 5 +#define RT5677_MONO_R_DD1_R_VOL_MASK (0x1 << 4) +#define RT5677_MONO_R_DD1_R_VOL_SFT 4 +#define RT5677_M_DAC3_R_DD1_R (0x1 << 3) +#define RT5677_M_DAC3_R_DD1_R_SFT 3 +#define RT5677_DAC3_R_DD1_R_VOL_MASK (0x1 << 2) +#define RT5677_DAC3_R_DD1_R_VOL_SFT 2 +#define RT5677_M_DAC3_L_DD1_R (0x1 << 1) +#define RT5677_M_DAC3_L_DD1_R_SFT 1 +#define RT5677_DAC3_L_DD1_R_VOL_MASK (0x1 << 0) +#define RT5677_DAC3_L_DD1_R_VOL_SFT 0 + +/* DD Mixer 2 Control (0x2d) */ +#define RT5677_M_STO_L_DD2_L (0x1 << 15) +#define RT5677_M_STO_L_DD2_L_SFT 15 +#define RT5677_STO_L_DD2_L_VOL_MASK (0x1 << 14) +#define RT5677_STO_L_DD2_L_VOL_SFT 14 +#define RT5677_M_MONO_L_DD2_L (0x1 << 13) +#define RT5677_M_MONO_L_DD2_L_SFT 13 +#define RT5677_MONO_L_DD2_L_VOL_MASK (0x1 << 12) +#define RT5677_MONO_L_DD2_L_VOL_SFT 12 +#define RT5677_M_DAC4_L_DD2_L (0x1 << 11) +#define RT5677_M_DAC4_L_DD2_L_SFT 11 +#define RT5677_DAC4_L_DD2_L_VOL_MASK (0x1 << 10) +#define RT5677_DAC4_L_DD2_L_VOL_SFT 10 +#define RT5677_M_DAC4_R_DD2_L (0x1 << 9) +#define RT5677_M_DAC4_R_DD2_L_SFT 9 +#define RT5677_DAC4_R_DD2_L_VOL_MASK (0x1 << 8) +#define RT5677_DAC4_R_DD2_L_VOL_SFT 8 +#define RT5677_M_STO_R_DD2_R (0x1 << 7) +#define RT5677_M_STO_R_DD2_R_SFT 7 +#define RT5677_STO_R_DD2_R_VOL_MASK (0x1 << 6) +#define RT5677_STO_R_DD2_R_VOL_SFT 6 +#define RT5677_M_MONO_R_DD2_R (0x1 << 5) +#define RT5677_M_MONO_R_DD2_R_SFT 5 +#define RT5677_MONO_R_DD2_R_VOL_MASK (0x1 << 4) +#define RT5677_MONO_R_DD2_R_VOL_SFT 4 +#define RT5677_M_DAC4_R_DD2_R (0x1 << 3) +#define RT5677_M_DAC4_R_DD2_R_SFT 3 +#define RT5677_DAC4_R_DD2_R_VOL_MASK (0x1 << 2) +#define RT5677_DAC4_R_DD2_R_VOL_SFT 2 +#define RT5677_M_DAC4_L_DD2_R (0x1 << 1) +#define RT5677_M_DAC4_L_DD2_R_SFT 1 +#define RT5677_DAC4_L_DD2_R_VOL_MASK (0x1 << 0) +#define RT5677_DAC4_L_DD2_R_VOL_SFT 0 + +/* IF3 data control (0x2f) */ +#define RT5677_IF3_DAC_SEL_MASK (0x3 << 6) +#define RT5677_IF3_DAC_SEL_SFT 6 +#define RT5677_IF3_ADC_SEL_MASK (0x3 << 4) +#define RT5677_IF3_ADC_SEL_SFT 4 +#define RT5677_IF3_ADC_IN_MASK (0xf << 0) +#define RT5677_IF3_ADC_IN_SFT 0 + +/* IF4 data control (0x30) */ +#define RT5677_IF4_ADC_IN_MASK (0xf << 4) +#define RT5677_IF4_ADC_IN_SFT 4 +#define RT5677_IF4_DAC_SEL_MASK (0x3 << 2) +#define RT5677_IF4_DAC_SEL_SFT 2 +#define RT5677_IF4_ADC_SEL_MASK (0x3 << 0) +#define RT5677_IF4_ADC_SEL_SFT 0 + +/* PDM Output Control (0x31) */ +#define RT5677_M_PDM1_L (0x1 << 15) +#define RT5677_M_PDM1_L_SFT 15 +#define RT5677_SEL_PDM1_L_MASK (0x3 << 12) +#define RT5677_SEL_PDM1_L_SFT 12 +#define RT5677_M_PDM1_R (0x1 << 11) +#define RT5677_M_PDM1_R_SFT 11 +#define RT5677_SEL_PDM1_R_MASK (0x3 << 8) +#define RT5677_SEL_PDM1_R_SFT 8 +#define RT5677_M_PDM2_L (0x1 << 7) +#define RT5677_M_PDM2_L_SFT 7 +#define RT5677_SEL_PDM2_L_MASK (0x3 << 4) +#define RT5677_SEL_PDM2_L_SFT 4 +#define RT5677_M_PDM2_R (0x1 << 3) +#define RT5677_M_PDM2_R_SFT 3 +#define RT5677_SEL_PDM2_R_MASK (0x3 << 0) +#define RT5677_SEL_PDM2_R_SFT 0 + +/* PDM I2C / Data Control 1 (0x32) */ +#define RT5677_PDM2_PW_DOWN (0x1 << 7) +#define RT5677_PDM1_PW_DOWN (0x1 << 6) +#define RT5677_PDM2_BUSY (0x1 << 5) +#define RT5677_PDM1_BUSY (0x1 << 4) +#define RT5677_PDM_PATTERN (0x1 << 3) +#define RT5677_PDM_GAIN (0x1 << 2) +#define RT5677_PDM_DIV_MASK (0x3 << 0) + +/* PDM I2C / Data Control 2 (0x33) */ +#define RT5677_PDM1_I2C_ID (0xf << 12) +#define RT5677_PDM1_EXE (0x1 << 11) +#define RT5677_PDM1_I2C_CMD (0x1 << 10) +#define RT5677_PDM1_I2C_EXE (0x1 << 9) +#define RT5677_PDM1_I2C_BUSY (0x1 << 8) +#define RT5677_PDM2_I2C_ID (0xf << 4) +#define RT5677_PDM2_EXE (0x1 << 3) +#define RT5677_PDM2_I2C_CMD (0x1 << 2) +#define RT5677_PDM2_I2C_EXE (0x1 << 1) +#define RT5677_PDM2_I2C_BUSY (0x1 << 0) + +/* TDM1 control 1 (0x3b) */ +#define RT5677_IF1_ADC_MODE_MASK (0x1 << 12) +#define RT5677_IF1_ADC_MODE_SFT 12 +#define RT5677_IF1_ADC_MODE_I2S (0x0 << 12) +#define RT5677_IF1_ADC_MODE_TDM (0x1 << 12) +#define RT5677_IF1_ADC1_SWAP_MASK (0x3 << 6) +#define RT5677_IF1_ADC1_SWAP_SFT 6 +#define RT5677_IF1_ADC2_SWAP_MASK (0x3 << 4) +#define RT5677_IF1_ADC2_SWAP_SFT 4 +#define RT5677_IF1_ADC3_SWAP_MASK (0x3 << 2) +#define RT5677_IF1_ADC3_SWAP_SFT 2 +#define RT5677_IF1_ADC4_SWAP_MASK (0x3 << 0) +#define RT5677_IF1_ADC4_SWAP_SFT 0 + +/* TDM1 control 2 (0x3c) */ +#define RT5677_IF1_ADC4_MASK (0x3 << 10) +#define RT5677_IF1_ADC4_SFT 10 +#define RT5677_IF1_ADC3_MASK (0x3 << 8) +#define RT5677_IF1_ADC3_SFT 8 +#define RT5677_IF1_ADC2_MASK (0x3 << 6) +#define RT5677_IF1_ADC2_SFT 6 +#define RT5677_IF1_ADC1_MASK (0x3 << 4) +#define RT5677_IF1_ADC1_SFT 4 +#define RT5677_IF1_ADC_CTRL_MASK (0x7 << 0) +#define RT5677_IF1_ADC_CTRL_SFT 0 + +/* TDM1 control 4 (0x3e) */ +#define RT5677_IF1_DAC0_MASK (0x7 << 12) +#define RT5677_IF1_DAC0_SFT 12 +#define RT5677_IF1_DAC1_MASK (0x7 << 8) +#define RT5677_IF1_DAC1_SFT 8 +#define RT5677_IF1_DAC2_MASK (0x7 << 4) +#define RT5677_IF1_DAC2_SFT 4 +#define RT5677_IF1_DAC3_MASK (0x7 << 0) +#define RT5677_IF1_DAC3_SFT 0 + +/* TDM1 control 5 (0x3f) */ +#define RT5677_IF1_DAC4_MASK (0x7 << 12) +#define RT5677_IF1_DAC4_SFT 12 +#define RT5677_IF1_DAC5_MASK (0x7 << 8) +#define RT5677_IF1_DAC5_SFT 8 +#define RT5677_IF1_DAC6_MASK (0x7 << 4) +#define RT5677_IF1_DAC6_SFT 4 +#define RT5677_IF1_DAC7_MASK (0x7 << 0) +#define RT5677_IF1_DAC7_SFT 0 + +/* TDM2 control 1 (0x40) */ +#define RT5677_IF2_ADC_MODE_MASK (0x1 << 12) +#define RT5677_IF2_ADC_MODE_SFT 12 +#define RT5677_IF2_ADC_MODE_I2S (0x0 << 12) +#define RT5677_IF2_ADC_MODE_TDM (0x1 << 12) +#define RT5677_IF2_ADC1_SWAP_MASK (0x3 << 6) +#define RT5677_IF2_ADC1_SWAP_SFT 6 +#define RT5677_IF2_ADC2_SWAP_MASK (0x3 << 4) +#define RT5677_IF2_ADC2_SWAP_SFT 4 +#define RT5677_IF2_ADC3_SWAP_MASK (0x3 << 2) +#define RT5677_IF2_ADC3_SWAP_SFT 2 +#define RT5677_IF2_ADC4_SWAP_MASK (0x3 << 0) +#define RT5677_IF2_ADC4_SWAP_SFT 0 + +/* TDM2 control 2 (0x41) */ +#define RT5677_IF2_ADC4_MASK (0x3 << 10) +#define RT5677_IF2_ADC4_SFT 10 +#define RT5677_IF2_ADC3_MASK (0x3 << 8) +#define RT5677_IF2_ADC3_SFT 8 +#define RT5677_IF2_ADC2_MASK (0x3 << 6) +#define RT5677_IF2_ADC2_SFT 6 +#define RT5677_IF2_ADC1_MASK (0x3 << 4) +#define RT5677_IF2_ADC1_SFT 4 +#define RT5677_IF2_ADC_CTRL_MASK (0x7 << 0) +#define RT5677_IF2_ADC_CTRL_SFT 0 + +/* TDM2 control 4 (0x43) */ +#define RT5677_IF2_DAC0_MASK (0x7 << 12) +#define RT5677_IF2_DAC0_SFT 12 +#define RT5677_IF2_DAC1_MASK (0x7 << 8) +#define RT5677_IF2_DAC1_SFT 8 +#define RT5677_IF2_DAC2_MASK (0x7 << 4) +#define RT5677_IF2_DAC2_SFT 4 +#define RT5677_IF2_DAC3_MASK (0x7 << 0) +#define RT5677_IF2_DAC3_SFT 0 + +/* TDM2 control 5 (0x44) */ +#define RT5677_IF2_DAC4_MASK (0x7 << 12) +#define RT5677_IF2_DAC4_SFT 12 +#define RT5677_IF2_DAC5_MASK (0x7 << 8) +#define RT5677_IF2_DAC5_SFT 8 +#define RT5677_IF2_DAC6_MASK (0x7 << 4) +#define RT5677_IF2_DAC6_SFT 4 +#define RT5677_IF2_DAC7_MASK (0x7 << 0) +#define RT5677_IF2_DAC7_SFT 0 + +/* Digital Microphone Control 1 (0x50) */ +#define RT5677_DMIC_1_EN_MASK (0x1 << 15) +#define RT5677_DMIC_1_EN_SFT 15 +#define RT5677_DMIC_1_DIS (0x0 << 15) +#define RT5677_DMIC_1_EN (0x1 << 15) +#define RT5677_DMIC_2_EN_MASK (0x1 << 14) +#define RT5677_DMIC_2_EN_SFT 14 +#define RT5677_DMIC_2_DIS (0x0 << 14) +#define RT5677_DMIC_2_EN (0x1 << 14) +#define RT5677_DMIC_L_STO1_LH_MASK (0x1 << 13) +#define RT5677_DMIC_L_STO1_LH_SFT 13 +#define RT5677_DMIC_L_STO1_LH_FALLING (0x0 << 13) +#define RT5677_DMIC_L_STO1_LH_RISING (0x1 << 13) +#define RT5677_DMIC_R_STO1_LH_MASK (0x1 << 12) +#define RT5677_DMIC_R_STO1_LH_SFT 12 +#define RT5677_DMIC_R_STO1_LH_FALLING (0x0 << 12) +#define RT5677_DMIC_R_STO1_LH_RISING (0x1 << 12) +#define RT5677_DMIC_L_STO3_LH_MASK (0x1 << 11) +#define RT5677_DMIC_L_STO3_LH_SFT 11 +#define RT5677_DMIC_L_STO3_LH_FALLING (0x0 << 11) +#define RT5677_DMIC_L_STO3_LH_RISING (0x1 << 11) +#define RT5677_DMIC_R_STO3_LH_MASK (0x1 << 10) +#define RT5677_DMIC_R_STO3_LH_SFT 10 +#define RT5677_DMIC_R_STO3_LH_FALLING (0x0 << 10) +#define RT5677_DMIC_R_STO3_LH_RISING (0x1 << 10) +#define RT5677_DMIC_L_STO2_LH_MASK (0x1 << 9) +#define RT5677_DMIC_L_STO2_LH_SFT 9 +#define RT5677_DMIC_L_STO2_LH_FALLING (0x0 << 9) +#define RT5677_DMIC_L_STO2_LH_RISING (0x1 << 9) +#define RT5677_DMIC_R_STO2_LH_MASK (0x1 << 8) +#define RT5677_DMIC_R_STO2_LH_SFT 8 +#define RT5677_DMIC_R_STO2_LH_FALLING (0x0 << 8) +#define RT5677_DMIC_R_STO2_LH_RISING (0x1 << 8) +#define RT5677_DMIC_CLK_MASK (0x7 << 5) +#define RT5677_DMIC_CLK_SFT 5 +#define RT5677_DMIC_3_EN_MASK (0x1 << 4) +#define RT5677_DMIC_3_EN_SFT 4 +#define RT5677_DMIC_3_DIS (0x0 << 4) +#define RT5677_DMIC_3_EN (0x1 << 4) +#define RT5677_DMIC_R_MONO_LH_MASK (0x1 << 2) +#define RT5677_DMIC_R_MONO_LH_SFT 2 +#define RT5677_DMIC_R_MONO_LH_FALLING (0x0 << 2) +#define RT5677_DMIC_R_MONO_LH_RISING (0x1 << 2) +#define RT5677_DMIC_L_STO4_LH_MASK (0x1 << 1) +#define RT5677_DMIC_L_STO4_LH_SFT 1 +#define RT5677_DMIC_L_STO4_LH_FALLING (0x0 << 1) +#define RT5677_DMIC_L_STO4_LH_RISING (0x1 << 1) +#define RT5677_DMIC_R_STO4_LH_MASK (0x1 << 0) +#define RT5677_DMIC_R_STO4_LH_SFT 0 +#define RT5677_DMIC_R_STO4_LH_FALLING (0x0 << 0) +#define RT5677_DMIC_R_STO4_LH_RISING (0x1 << 0) + +/* Digital Microphone Control 2 (0x51) */ +#define RT5677_DMIC_4_EN_MASK (0x1 << 15) +#define RT5677_DMIC_4_EN_SFT 15 +#define RT5677_DMIC_4_DIS (0x0 << 15) +#define RT5677_DMIC_4_EN (0x1 << 15) +#define RT5677_DMIC_4L_LH_MASK (0x1 << 7) +#define RT5677_DMIC_4L_LH_SFT 7 +#define RT5677_DMIC_4L_LH_FALLING (0x0 << 7) +#define RT5677_DMIC_4L_LH_RISING (0x1 << 7) +#define RT5677_DMIC_4R_LH_MASK (0x1 << 6) +#define RT5677_DMIC_4R_LH_SFT 6 +#define RT5677_DMIC_4R_LH_FALLING (0x0 << 6) +#define RT5677_DMIC_4R_LH_RISING (0x1 << 6) +#define RT5677_DMIC_3L_LH_MASK (0x1 << 5) +#define RT5677_DMIC_3L_LH_SFT 5 +#define RT5677_DMIC_3L_LH_FALLING (0x0 << 5) +#define RT5677_DMIC_3L_LH_RISING (0x1 << 5) +#define RT5677_DMIC_3R_LH_MASK (0x1 << 4) +#define RT5677_DMIC_3R_LH_SFT 4 +#define RT5677_DMIC_3R_LH_FALLING (0x0 << 4) +#define RT5677_DMIC_3R_LH_RISING (0x1 << 4) +#define RT5677_DMIC_2L_LH_MASK (0x1 << 3) +#define RT5677_DMIC_2L_LH_SFT 3 +#define RT5677_DMIC_2L_LH_FALLING (0x0 << 3) +#define RT5677_DMIC_2L_LH_RISING (0x1 << 3) +#define RT5677_DMIC_2R_LH_MASK (0x1 << 2) +#define RT5677_DMIC_2R_LH_SFT 2 +#define RT5677_DMIC_2R_LH_FALLING (0x0 << 2) +#define RT5677_DMIC_2R_LH_RISING (0x1 << 2) +#define RT5677_DMIC_1L_LH_MASK (0x1 << 1) +#define RT5677_DMIC_1L_LH_SFT 1 +#define RT5677_DMIC_1L_LH_FALLING (0x0 << 1) +#define RT5677_DMIC_1L_LH_RISING (0x1 << 1) +#define RT5677_DMIC_1R_LH_MASK (0x1 << 0) +#define RT5677_DMIC_1R_LH_SFT 0 +#define RT5677_DMIC_1R_LH_FALLING (0x0 << 0) +#define RT5677_DMIC_1R_LH_RISING (0x1 << 0) + +/* Power Management for Digital 1 (0x61) */ +#define RT5677_PWR_I2S1 (0x1 << 15) +#define RT5677_PWR_I2S1_BIT 15 +#define RT5677_PWR_I2S2 (0x1 << 14) +#define RT5677_PWR_I2S2_BIT 14 +#define RT5677_PWR_I2S3 (0x1 << 13) +#define RT5677_PWR_I2S3_BIT 13 +#define RT5677_PWR_DAC1 (0x1 << 12) +#define RT5677_PWR_DAC1_BIT 12 +#define RT5677_PWR_DAC2 (0x1 << 11) +#define RT5677_PWR_DAC2_BIT 11 +#define RT5677_PWR_I2S4 (0x1 << 10) +#define RT5677_PWR_I2S4_BIT 10 +#define RT5677_PWR_SLB (0x1 << 9) +#define RT5677_PWR_SLB_BIT 9 +#define RT5677_PWR_DAC3 (0x1 << 7) +#define RT5677_PWR_DAC3_BIT 7 +#define RT5677_PWR_ADCFED2 (0x1 << 4) +#define RT5677_PWR_ADCFED2_BIT 4 +#define RT5677_PWR_ADCFED1 (0x1 << 3) +#define RT5677_PWR_ADCFED1_BIT 3 +#define RT5677_PWR_ADC_L (0x1 << 2) +#define RT5677_PWR_ADC_L_BIT 2 +#define RT5677_PWR_ADC_R (0x1 << 1) +#define RT5677_PWR_ADC_R_BIT 1 +#define RT5677_PWR_I2C_MASTER (0x1 << 0) +#define RT5677_PWR_I2C_MASTER_BIT 0 + +/* Power Management for Digital 2 (0x62) */ +#define RT5677_PWR_ADC_S1F (0x1 << 15) +#define RT5677_PWR_ADC_S1F_BIT 15 +#define RT5677_PWR_ADC_MF_L (0x1 << 14) +#define RT5677_PWR_ADC_MF_L_BIT 14 +#define RT5677_PWR_ADC_MF_R (0x1 << 13) +#define RT5677_PWR_ADC_MF_R_BIT 13 +#define RT5677_PWR_DAC_S1F (0x1 << 12) +#define RT5677_PWR_DAC_S1F_BIT 12 +#define RT5677_PWR_DAC_M2F_L (0x1 << 11) +#define RT5677_PWR_DAC_M2F_L_BIT 11 +#define RT5677_PWR_DAC_M2F_R (0x1 << 10) +#define RT5677_PWR_DAC_M2F_R_BIT 10 +#define RT5677_PWR_DAC_M3F_L (0x1 << 9) +#define RT5677_PWR_DAC_M3F_L_BIT 9 +#define RT5677_PWR_DAC_M3F_R (0x1 << 8) +#define RT5677_PWR_DAC_M3F_R_BIT 8 +#define RT5677_PWR_DAC_M4F_L (0x1 << 7) +#define RT5677_PWR_DAC_M4F_L_BIT 7 +#define RT5677_PWR_DAC_M4F_R (0x1 << 6) +#define RT5677_PWR_DAC_M4F_R_BIT 6 +#define RT5677_PWR_ADC_S2F (0x1 << 5) +#define RT5677_PWR_ADC_S2F_BIT 5 +#define RT5677_PWR_ADC_S3F (0x1 << 4) +#define RT5677_PWR_ADC_S3F_BIT 4 +#define RT5677_PWR_ADC_S4F (0x1 << 3) +#define RT5677_PWR_ADC_S4F_BIT 3 +#define RT5677_PWR_PDM1 (0x1 << 2) +#define RT5677_PWR_PDM1_BIT 2 +#define RT5677_PWR_PDM2 (0x1 << 1) +#define RT5677_PWR_PDM2_BIT 1 + +/* Power Management for Analog 1 (0x63) */ +#define RT5677_PWR_VREF1 (0x1 << 15) +#define RT5677_PWR_VREF1_BIT 15 +#define RT5677_PWR_FV1 (0x1 << 14) +#define RT5677_PWR_FV1_BIT 14 +#define RT5677_PWR_MB (0x1 << 13) +#define RT5677_PWR_MB_BIT 13 +#define RT5677_PWR_LO1 (0x1 << 12) +#define RT5677_PWR_LO1_BIT 12 +#define RT5677_PWR_BG (0x1 << 11) +#define RT5677_PWR_BG_BIT 11 +#define RT5677_PWR_LO2 (0x1 << 10) +#define RT5677_PWR_LO2_BIT 10 +#define RT5677_PWR_LO3 (0x1 << 9) +#define RT5677_PWR_LO3_BIT 9 +#define RT5677_PWR_VREF2 (0x1 << 8) +#define RT5677_PWR_VREF2_BIT 8 +#define RT5677_PWR_FV2 (0x1 << 7) +#define RT5677_PWR_FV2_BIT 7 +#define RT5677_LDO2_SEL_MASK (0x7 << 4) +#define RT5677_LDO2_SEL_SFT 4 +#define RT5677_LDO1_SEL_MASK (0x7 << 0) +#define RT5677_LDO1_SEL_SFT 0 + +/* Power Management for Analog 2 (0x64) */ +#define RT5677_PWR_BST1 (0x1 << 15) +#define RT5677_PWR_BST1_BIT 15 +#define RT5677_PWR_BST2 (0x1 << 14) +#define RT5677_PWR_BST2_BIT 14 +#define RT5677_PWR_CLK_MB1 (0x1 << 13) +#define RT5677_PWR_CLK_MB1_BIT 13 +#define RT5677_PWR_SLIM (0x1 << 12) +#define RT5677_PWR_SLIM_BIT 12 +#define RT5677_PWR_MB1 (0x1 << 11) +#define RT5677_PWR_MB1_BIT 11 +#define RT5677_PWR_PP_MB1 (0x1 << 10) +#define RT5677_PWR_PP_MB1_BIT 10 +#define RT5677_PWR_PLL1 (0x1 << 9) +#define RT5677_PWR_PLL1_BIT 9 +#define RT5677_PWR_PLL2 (0x1 << 8) +#define RT5677_PWR_PLL2_BIT 8 +#define RT5677_PWR_CORE (0x1 << 7) +#define RT5677_PWR_CORE_BIT 7 +#define RT5677_PWR_CLK_MB (0x1 << 6) +#define RT5677_PWR_CLK_MB_BIT 6 +#define RT5677_PWR_BST1_P (0x1 << 5) +#define RT5677_PWR_BST1_P_BIT 5 +#define RT5677_PWR_BST2_P (0x1 << 4) +#define RT5677_PWR_BST2_P_BIT 4 +#define RT5677_PWR_IPTV (0x1 << 3) +#define RT5677_PWR_IPTV_BIT 3 +#define RT5677_PWR_25M_CLK (0x1 << 1) +#define RT5677_PWR_25M_CLK_BIT 1 +#define RT5677_PWR_LDO1 (0x1 << 0) +#define RT5677_PWR_LDO1_BIT 0 + +/* Power Management for DSP (0x65) */ +#define RT5677_PWR_SR7 (0x1 << 10) +#define RT5677_PWR_SR7_BIT 10 +#define RT5677_PWR_SR6 (0x1 << 9) +#define RT5677_PWR_SR6_BIT 9 +#define RT5677_PWR_SR5 (0x1 << 8) +#define RT5677_PWR_SR5_BIT 8 +#define RT5677_PWR_SR4 (0x1 << 7) +#define RT5677_PWR_SR4_BIT 7 +#define RT5677_PWR_SR3 (0x1 << 6) +#define RT5677_PWR_SR3_BIT 6 +#define RT5677_PWR_SR2 (0x1 << 5) +#define RT5677_PWR_SR2_BIT 5 +#define RT5677_PWR_SR1 (0x1 << 4) +#define RT5677_PWR_SR1_BIT 4 +#define RT5677_PWR_SR0 (0x1 << 3) +#define RT5677_PWR_SR0_BIT 3 +#define RT5677_PWR_MLT (0x1 << 2) +#define RT5677_PWR_MLT_BIT 2 +#define RT5677_PWR_DSP (0x1 << 1) +#define RT5677_PWR_DSP_BIT 1 +#define RT5677_PWR_DSP_CPU (0x1 << 0) +#define RT5677_PWR_DSP_CPU_BIT 0 + +/* Power Status for DSP (0x66) */ +#define RT5677_PWR_SR7_RDY (0x1 << 9) +#define RT5677_PWR_SR7_RDY_BIT 9 +#define RT5677_PWR_SR6_RDY (0x1 << 8) +#define RT5677_PWR_SR6_RDY_BIT 8 +#define RT5677_PWR_SR5_RDY (0x1 << 7) +#define RT5677_PWR_SR5_RDY_BIT 7 +#define RT5677_PWR_SR4_RDY (0x1 << 6) +#define RT5677_PWR_SR4_RDY_BIT 6 +#define RT5677_PWR_SR3_RDY (0x1 << 5) +#define RT5677_PWR_SR3_RDY_BIT 5 +#define RT5677_PWR_SR2_RDY (0x1 << 4) +#define RT5677_PWR_SR2_RDY_BIT 4 +#define RT5677_PWR_SR1_RDY (0x1 << 3) +#define RT5677_PWR_SR1_RDY_BIT 3 +#define RT5677_PWR_SR0_RDY (0x1 << 2) +#define RT5677_PWR_SR0_RDY_BIT 2 +#define RT5677_PWR_MLT_RDY (0x1 << 1) +#define RT5677_PWR_MLT_RDY_BIT 1 +#define RT5677_PWR_DSP_RDY (0x1 << 0) +#define RT5677_PWR_DSP_RDY_BIT 0 + +/* Power Management for DSP (0x67) */ +#define RT5677_PWR_SLIM_ISO (0x1 << 11) +#define RT5677_PWR_SLIM_ISO_BIT 11 +#define RT5677_PWR_CORE_ISO (0x1 << 10) +#define RT5677_PWR_CORE_ISO_BIT 10 +#define RT5677_PWR_DSP_ISO (0x1 << 9) +#define RT5677_PWR_DSP_ISO_BIT 9 +#define RT5677_PWR_SR7_ISO (0x1 << 8) +#define RT5677_PWR_SR7_ISO_BIT 8 +#define RT5677_PWR_SR6_ISO (0x1 << 7) +#define RT5677_PWR_SR6_ISO_BIT 7 +#define RT5677_PWR_SR5_ISO (0x1 << 6) +#define RT5677_PWR_SR5_ISO_BIT 6 +#define RT5677_PWR_SR4_ISO (0x1 << 5) +#define RT5677_PWR_SR4_ISO_BIT 5 +#define RT5677_PWR_SR3_ISO (0x1 << 4) +#define RT5677_PWR_SR3_ISO_BIT 4 +#define RT5677_PWR_SR2_ISO (0x1 << 3) +#define RT5677_PWR_SR2_ISO_BIT 3 +#define RT5677_PWR_SR1_ISO (0x1 << 2) +#define RT5677_PWR_SR1_ISO_BIT 2 +#define RT5677_PWR_SR0_ISO (0x1 << 1) +#define RT5677_PWR_SR0_ISO_BIT 1 +#define RT5677_PWR_MLT_ISO (0x1 << 0) +#define RT5677_PWR_MLT_ISO_BIT 0 + +/* I2S1/2/3/4 Audio Serial Data Port Control (0x6f 0x70 0x71 0x72) */ +#define RT5677_I2S_MS_MASK (0x1 << 15) +#define RT5677_I2S_MS_SFT 15 +#define RT5677_I2S_MS_M (0x0 << 15) +#define RT5677_I2S_MS_S (0x1 << 15) +#define RT5677_I2S_O_CP_MASK (0x3 << 10) +#define RT5677_I2S_O_CP_SFT 10 +#define RT5677_I2S_O_CP_OFF (0x0 << 10) +#define RT5677_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5677_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5677_I2S_I_CP_MASK (0x3 << 8) +#define RT5677_I2S_I_CP_SFT 8 +#define RT5677_I2S_I_CP_OFF (0x0 << 8) +#define RT5677_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5677_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5677_I2S_BP_MASK (0x1 << 7) +#define RT5677_I2S_BP_SFT 7 +#define RT5677_I2S_BP_NOR (0x0 << 7) +#define RT5677_I2S_BP_INV (0x1 << 7) +#define RT5677_I2S_DL_MASK (0x3 << 2) +#define RT5677_I2S_DL_SFT 2 +#define RT5677_I2S_DL_16 (0x0 << 2) +#define RT5677_I2S_DL_20 (0x1 << 2) +#define RT5677_I2S_DL_24 (0x2 << 2) +#define RT5677_I2S_DL_8 (0x3 << 2) +#define RT5677_I2S_DF_MASK (0x3 << 0) +#define RT5677_I2S_DF_SFT 0 +#define RT5677_I2S_DF_I2S (0x0 << 0) +#define RT5677_I2S_DF_LEFT (0x1 << 0) +#define RT5677_I2S_DF_PCM_A (0x2 << 0) +#define RT5677_I2S_DF_PCM_B (0x3 << 0) + +/* Clock Tree Control 1 (0x73) */ +#define RT5677_I2S_PD1_MASK (0x7 << 12) +#define RT5677_I2S_PD1_SFT 12 +#define RT5677_I2S_PD1_1 (0x0 << 12) +#define RT5677_I2S_PD1_2 (0x1 << 12) +#define RT5677_I2S_PD1_3 (0x2 << 12) +#define RT5677_I2S_PD1_4 (0x3 << 12) +#define RT5677_I2S_PD1_6 (0x4 << 12) +#define RT5677_I2S_PD1_8 (0x5 << 12) +#define RT5677_I2S_PD1_12 (0x6 << 12) +#define RT5677_I2S_PD1_16 (0x7 << 12) +#define RT5677_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5677_I2S_BCLK_MS2_SFT 11 +#define RT5677_I2S_BCLK_MS2_32 (0x0 << 11) +#define RT5677_I2S_BCLK_MS2_64 (0x1 << 11) +#define RT5677_I2S_PD2_MASK (0x7 << 8) +#define RT5677_I2S_PD2_SFT 8 +#define RT5677_I2S_PD2_1 (0x0 << 8) +#define RT5677_I2S_PD2_2 (0x1 << 8) +#define RT5677_I2S_PD2_3 (0x2 << 8) +#define RT5677_I2S_PD2_4 (0x3 << 8) +#define RT5677_I2S_PD2_6 (0x4 << 8) +#define RT5677_I2S_PD2_8 (0x5 << 8) +#define RT5677_I2S_PD2_12 (0x6 << 8) +#define RT5677_I2S_PD2_16 (0x7 << 8) +#define RT5677_I2S_BCLK_MS3_MASK (0x1 << 7) +#define RT5677_I2S_BCLK_MS3_SFT 7 +#define RT5677_I2S_BCLK_MS3_32 (0x0 << 7) +#define RT5677_I2S_BCLK_MS3_64 (0x1 << 7) +#define RT5677_I2S_PD3_MASK (0x7 << 4) +#define RT5677_I2S_PD3_SFT 4 +#define RT5677_I2S_PD3_1 (0x0 << 4) +#define RT5677_I2S_PD3_2 (0x1 << 4) +#define RT5677_I2S_PD3_3 (0x2 << 4) +#define RT5677_I2S_PD3_4 (0x3 << 4) +#define RT5677_I2S_PD3_6 (0x4 << 4) +#define RT5677_I2S_PD3_8 (0x5 << 4) +#define RT5677_I2S_PD3_12 (0x6 << 4) +#define RT5677_I2S_PD3_16 (0x7 << 4) +#define RT5677_I2S_BCLK_MS4_MASK (0x1 << 3) +#define RT5677_I2S_BCLK_MS4_SFT 3 +#define RT5677_I2S_BCLK_MS4_32 (0x0 << 3) +#define RT5677_I2S_BCLK_MS4_64 (0x1 << 3) +#define RT5677_I2S_PD4_MASK (0x7 << 0) +#define RT5677_I2S_PD4_SFT 0 +#define RT5677_I2S_PD4_1 (0x0 << 0) +#define RT5677_I2S_PD4_2 (0x1 << 0) +#define RT5677_I2S_PD4_3 (0x2 << 0) +#define RT5677_I2S_PD4_4 (0x3 << 0) +#define RT5677_I2S_PD4_6 (0x4 << 0) +#define RT5677_I2S_PD4_8 (0x5 << 0) +#define RT5677_I2S_PD4_12 (0x6 << 0) +#define RT5677_I2S_PD4_16 (0x7 << 0) + +/* Clock Tree Control 2 (0x74) */ +#define RT5677_I2S_PD5_MASK (0x7 << 12) +#define RT5677_I2S_PD5_SFT 12 +#define RT5677_I2S_PD5_1 (0x0 << 12) +#define RT5677_I2S_PD5_2 (0x1 << 12) +#define RT5677_I2S_PD5_3 (0x2 << 12) +#define RT5677_I2S_PD5_4 (0x3 << 12) +#define RT5677_I2S_PD5_6 (0x4 << 12) +#define RT5677_I2S_PD5_8 (0x5 << 12) +#define RT5677_I2S_PD5_12 (0x6 << 12) +#define RT5677_I2S_PD5_16 (0x7 << 12) +#define RT5677_I2S_PD6_MASK (0x7 << 8) +#define RT5677_I2S_PD6_SFT 8 +#define RT5677_I2S_PD6_1 (0x0 << 8) +#define RT5677_I2S_PD6_2 (0x1 << 8) +#define RT5677_I2S_PD6_3 (0x2 << 8) +#define RT5677_I2S_PD6_4 (0x3 << 8) +#define RT5677_I2S_PD6_6 (0x4 << 8) +#define RT5677_I2S_PD6_8 (0x5 << 8) +#define RT5677_I2S_PD6_12 (0x6 << 8) +#define RT5677_I2S_PD6_16 (0x7 << 8) +#define RT5677_I2S_PD7_MASK (0x7 << 4) +#define RT5677_I2S_PD7_SFT 4 +#define RT5677_I2S_PD7_1 (0x0 << 4) +#define RT5677_I2S_PD7_2 (0x1 << 4) +#define RT5677_I2S_PD7_3 (0x2 << 4) +#define RT5677_I2S_PD7_4 (0x3 << 4) +#define RT5677_I2S_PD7_6 (0x4 << 4) +#define RT5677_I2S_PD7_8 (0x5 << 4) +#define RT5677_I2S_PD7_12 (0x6 << 4) +#define RT5677_I2S_PD7_16 (0x7 << 4) +#define RT5677_I2S_PD8_MASK (0x7 << 0) +#define RT5677_I2S_PD8_SFT 0 +#define RT5677_I2S_PD8_1 (0x0 << 0) +#define RT5677_I2S_PD8_2 (0x1 << 0) +#define RT5677_I2S_PD8_3 (0x2 << 0) +#define RT5677_I2S_PD8_4 (0x3 << 0) +#define RT5677_I2S_PD8_6 (0x4 << 0) +#define RT5677_I2S_PD8_8 (0x5 << 0) +#define RT5677_I2S_PD8_12 (0x6 << 0) +#define RT5677_I2S_PD8_16 (0x7 << 0) + +/* Clock Tree Control 3 (0x75) */ +#define RT5677_DSP_ASRC_O_MASK (0x3 << 6) +#define RT5677_DSP_ASRC_O_SFT 6 +#define RT5677_DSP_ASRC_O_1_0 (0x0 << 6) +#define RT5677_DSP_ASRC_O_1_5 (0x1 << 6) +#define RT5677_DSP_ASRC_O_2_0 (0x2 << 6) +#define RT5677_DSP_ASRC_O_3_0 (0x3 << 6) +#define RT5677_DSP_ASRC_I_MASK (0x3 << 4) +#define RT5677_DSP_ASRC_I_SFT 4 +#define RT5677_DSP_ASRC_I_1_0 (0x0 << 4) +#define RT5677_DSP_ASRC_I_1_5 (0x1 << 4) +#define RT5677_DSP_ASRC_I_2_0 (0x2 << 4) +#define RT5677_DSP_ASRC_I_3_0 (0x3 << 4) +#define RT5677_DSP_BUS_PD_MASK (0x7 << 0) +#define RT5677_DSP_BUS_PD_SFT 0 +#define RT5677_DSP_BUS_PD_1 (0x0 << 0) +#define RT5677_DSP_BUS_PD_2 (0x1 << 0) +#define RT5677_DSP_BUS_PD_3 (0x2 << 0) +#define RT5677_DSP_BUS_PD_4 (0x3 << 0) +#define RT5677_DSP_BUS_PD_6 (0x4 << 0) +#define RT5677_DSP_BUS_PD_8 (0x5 << 0) +#define RT5677_DSP_BUS_PD_12 (0x6 << 0) +#define RT5677_DSP_BUS_PD_16 (0x7 << 0) + +#define RT5677_PLL_INP_MAX 40000000 +#define RT5677_PLL_INP_MIN 2048000 +/* PLL M/N/K Code Control 1 (0x7a 0x7c) */ +#define RT5677_PLL_N_MAX 0x1ff +#define RT5677_PLL_N_MASK (RT5677_PLL_N_MAX << 7) +#define RT5677_PLL_N_SFT 7 +#define RT5677_PLL_K_BP (0x1 << 5) +#define RT5677_PLL_K_BP_SFT 5 +#define RT5677_PLL_K_MAX 0x1f +#define RT5677_PLL_K_MASK (RT5677_PLL_K_MAX) +#define RT5677_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x7b 0x7d) */ +#define RT5677_PLL_M_MAX 0xf +#define RT5677_PLL_M_MASK (RT5677_PLL_M_MAX << 12) +#define RT5677_PLL_M_SFT 12 +#define RT5677_PLL_M_BP (0x1 << 11) +#define RT5677_PLL_M_BP_SFT 11 +#define RT5677_PLL_UPDATE_PLL1 (0x1 << 1) +#define RT5677_PLL_UPDATE_PLL1_SFT 1 + +/* Global Clock Control 1 (0x80) */ +#define RT5677_SCLK_SRC_MASK (0x3 << 14) +#define RT5677_SCLK_SRC_SFT 14 +#define RT5677_SCLK_SRC_MCLK (0x0 << 14) +#define RT5677_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5677_SCLK_SRC_RCCLK (0x2 << 14) /* 25MHz */ +#define RT5677_SCLK_SRC_SLIM (0x3 << 14) +#define RT5677_PLL1_SRC_MASK (0x7 << 11) +#define RT5677_PLL1_SRC_SFT 11 +#define RT5677_PLL1_SRC_MCLK (0x0 << 11) +#define RT5677_PLL1_SRC_BCLK1 (0x1 << 11) +#define RT5677_PLL1_SRC_BCLK2 (0x2 << 11) +#define RT5677_PLL1_SRC_BCLK3 (0x3 << 11) +#define RT5677_PLL1_SRC_BCLK4 (0x4 << 11) +#define RT5677_PLL1_SRC_RCCLK (0x5 << 11) +#define RT5677_PLL1_SRC_SLIM (0x6 << 11) +#define RT5677_MCLK_SRC_MASK (0x1 << 10) +#define RT5677_MCLK_SRC_SFT 10 +#define RT5677_MCLK1_SRC (0x0 << 10) +#define RT5677_MCLK2_SRC (0x1 << 10) +#define RT5677_PLL1_PD_MASK (0x1 << 8) +#define RT5677_PLL1_PD_SFT 8 +#define RT5677_PLL1_PD_1 (0x0 << 8) +#define RT5677_PLL1_PD_2 (0x1 << 8) +#define RT5677_DAC_OSR_MASK (0x3 << 6) +#define RT5677_DAC_OSR_SFT 6 +#define RT5677_DAC_OSR_128 (0x0 << 6) +#define RT5677_DAC_OSR_64 (0x1 << 6) +#define RT5677_DAC_OSR_32 (0x2 << 6) +#define RT5677_ADC_OSR_MASK (0x3 << 4) +#define RT5677_ADC_OSR_SFT 4 +#define RT5677_ADC_OSR_128 (0x0 << 4) +#define RT5677_ADC_OSR_64 (0x1 << 4) +#define RT5677_ADC_OSR_32 (0x2 << 4) + +/* Global Clock Control 2 (0x81) */ +#define RT5677_PLL2_PR_SRC_MASK (0x1 << 15) +#define RT5677_PLL2_PR_SRC_SFT 15 +#define RT5677_PLL2_PR_SRC_MCLK1 (0x0 << 15) +#define RT5677_PLL2_PR_SRC_MCLK2 (0x1 << 15) +#define RT5677_PLL2_SRC_MASK (0x7 << 12) +#define RT5677_PLL2_SRC_SFT 12 +#define RT5677_PLL2_SRC_MCLK (0x0 << 12) +#define RT5677_PLL2_SRC_BCLK1 (0x1 << 12) +#define RT5677_PLL2_SRC_BCLK2 (0x2 << 12) +#define RT5677_PLL2_SRC_BCLK3 (0x3 << 12) +#define RT5677_PLL2_SRC_BCLK4 (0x4 << 12) +#define RT5677_PLL2_SRC_RCCLK (0x5 << 12) +#define RT5677_PLL2_SRC_SLIM (0x6 << 12) +#define RT5677_DSP_ASRC_O_SRC (0x3 << 10) +#define RT5677_DSP_ASRC_O_SRC_SFT 10 +#define RT5677_DSP_ASRC_O_MCLK (0x0 << 10) +#define RT5677_DSP_ASRC_O_PLL1 (0x1 << 10) +#define RT5677_DSP_ASRC_O_SLIM (0x2 << 10) +#define RT5677_DSP_ASRC_O_RCCLK (0x3 << 10) +#define RT5677_DSP_ASRC_I_SRC (0x3 << 8) +#define RT5677_DSP_ASRC_I_SRC_SFT 8 +#define RT5677_DSP_ASRC_I_MCLK (0x0 << 8) +#define RT5677_DSP_ASRC_I_PLL1 (0x1 << 8) +#define RT5677_DSP_ASRC_I_SLIM (0x2 << 8) +#define RT5677_DSP_ASRC_I_RCCLK (0x3 << 8) +#define RT5677_DSP_CLK_SRC_MASK (0x1 << 7) +#define RT5677_DSP_CLK_SRC_SFT 7 +#define RT5677_DSP_CLK_SRC_PLL2 (0x0 << 7) +#define RT5677_DSP_CLK_SRC_BYPASS (0x1 << 7) + +/* ASRC Control 3 (0x85) */ +#define RT5677_DA_STO_CLK_SEL_MASK (0xf << 12) +#define RT5677_DA_STO_CLK_SEL_SFT 12 +#define RT5677_DA_MONO2L_CLK_SEL_MASK (0xf << 4) +#define RT5677_DA_MONO2L_CLK_SEL_SFT 4 +#define RT5677_DA_MONO2R_CLK_SEL_MASK (0xf << 0) +#define RT5677_DA_MONO2R_CLK_SEL_SFT 0 + +/* ASRC Control 4 (0x86) */ +#define RT5677_DA_MONO3L_CLK_SEL_MASK (0xf << 12) +#define RT5677_DA_MONO3L_CLK_SEL_SFT 12 +#define RT5677_DA_MONO3R_CLK_SEL_MASK (0xf << 8) +#define RT5677_DA_MONO3R_CLK_SEL_SFT 8 +#define RT5677_DA_MONO4L_CLK_SEL_MASK (0xf << 4) +#define RT5677_DA_MONO4L_CLK_SEL_SFT 4 +#define RT5677_DA_MONO4R_CLK_SEL_MASK (0xf << 0) +#define RT5677_DA_MONO4R_CLK_SEL_SFT 0 + +/* ASRC Control 5 (0x87) */ +#define RT5677_AD_STO1_CLK_SEL_MASK (0xf << 12) +#define RT5677_AD_STO1_CLK_SEL_SFT 12 +#define RT5677_AD_STO2_CLK_SEL_MASK (0xf << 8) +#define RT5677_AD_STO2_CLK_SEL_SFT 8 +#define RT5677_AD_STO3_CLK_SEL_MASK (0xf << 4) +#define RT5677_AD_STO3_CLK_SEL_SFT 4 +#define RT5677_AD_STO4_CLK_SEL_MASK (0xf << 0) +#define RT5677_AD_STO4_CLK_SEL_SFT 0 + +/* ASRC Control 6 (0x88) */ +#define RT5677_AD_MONOL_CLK_SEL_MASK (0xf << 12) +#define RT5677_AD_MONOL_CLK_SEL_SFT 12 +#define RT5677_AD_MONOR_CLK_SEL_MASK (0xf << 8) +#define RT5677_AD_MONOR_CLK_SEL_SFT 8 + +/* ASRC Control 7 (0x89) */ +#define RT5677_DSP_OB_0_3_CLK_SEL_MASK (0xf << 12) +#define RT5677_DSP_OB_0_3_CLK_SEL_SFT 12 +#define RT5677_DSP_OB_4_7_CLK_SEL_MASK (0xf << 8) +#define RT5677_DSP_OB_4_7_CLK_SEL_SFT 8 + +/* ASRC Control 8 (0x8a) */ +#define RT5677_I2S1_CLK_SEL_MASK (0xf << 12) +#define RT5677_I2S1_CLK_SEL_SFT 12 +#define RT5677_I2S2_CLK_SEL_MASK (0xf << 8) +#define RT5677_I2S2_CLK_SEL_SFT 8 +#define RT5677_I2S3_CLK_SEL_MASK (0xf << 4) +#define RT5677_I2S3_CLK_SEL_SFT 4 +#define RT5677_I2S4_CLK_SEL_MASK (0xf) +#define RT5677_I2S4_CLK_SEL_SFT 0 + +/* VAD Function Control 1 (0x9c) */ +#define RT5677_VAD_MIN_DUR_MASK (0x3 << 13) +#define RT5677_VAD_MIN_DUR_SFT 13 +#define RT5677_VAD_ADPCM_BYPASS (1 << 10) +#define RT5677_VAD_ADPCM_BYPASS_BIT 10 +#define RT5677_VAD_FG2ENC (1 << 9) +#define RT5677_VAD_FG2ENC_BIT 9 +#define RT5677_VAD_BUF_OW (1 << 8) +#define RT5677_VAD_BUF_OW_BIT 8 +#define RT5677_VAD_CLR_FLAG (1 << 7) +#define RT5677_VAD_CLR_FLAG_BIT 7 +#define RT5677_VAD_BUF_POP (1 << 6) +#define RT5677_VAD_BUF_POP_BIT 6 +#define RT5677_VAD_BUF_PUSH (1 << 5) +#define RT5677_VAD_BUF_PUSH_BIT 5 +#define RT5677_VAD_DET_ENABLE (1 << 4) +#define RT5677_VAD_DET_ENABLE_BIT 4 +#define RT5677_VAD_FUNC_ENABLE (1 << 3) +#define RT5677_VAD_FUNC_ENABLE_BIT 3 +#define RT5677_VAD_FUNC_RESET (1 << 2) +#define RT5677_VAD_FUNC_RESET_BIT 2 + +/* VAD Function Control 4 (0x9f) */ +#define RT5677_VAD_OUT_SRC_RATE_MASK (0x1 << 11) +#define RT5677_VAD_OUT_SRC_RATE_SFT 11 +#define RT5677_VAD_OUT_SRC_MASK (0x1 << 10) +#define RT5677_VAD_OUT_SRC_SFT 10 +#define RT5677_VAD_SRC_MASK (0x3 << 8) +#define RT5677_VAD_SRC_SFT 8 +#define RT5677_VAD_LV_DIFF_MASK (0xff << 0) +#define RT5677_VAD_LV_DIFF_SFT 0 + +/* DSP InBound Control (0xa3) */ +#define RT5677_IB01_SRC_MASK (0x7 << 12) +#define RT5677_IB01_SRC_SFT 12 +#define RT5677_IB23_SRC_MASK (0x7 << 8) +#define RT5677_IB23_SRC_SFT 8 +#define RT5677_IB45_SRC_MASK (0x7 << 4) +#define RT5677_IB45_SRC_SFT 4 +#define RT5677_IB6_SRC_MASK (0x7 << 0) +#define RT5677_IB6_SRC_SFT 0 + +/* DSP InBound Control (0xa4) */ +#define RT5677_IB7_SRC_MASK (0x7 << 12) +#define RT5677_IB7_SRC_SFT 12 +#define RT5677_IB8_SRC_MASK (0x7 << 8) +#define RT5677_IB8_SRC_SFT 8 +#define RT5677_IB9_SRC_MASK (0x7 << 4) +#define RT5677_IB9_SRC_SFT 4 + +/* DSP In/OutBound Control (0xa5) */ +#define RT5677_SEL_SRC_OB23 (0x1 << 4) +#define RT5677_SEL_SRC_OB23_SFT 4 +#define RT5677_SEL_SRC_OB01 (0x1 << 3) +#define RT5677_SEL_SRC_OB01_SFT 3 +#define RT5677_SEL_SRC_IB45 (0x1 << 2) +#define RT5677_SEL_SRC_IB45_SFT 2 +#define RT5677_SEL_SRC_IB23 (0x1 << 1) +#define RT5677_SEL_SRC_IB23_SFT 1 +#define RT5677_SEL_SRC_IB01 (0x1 << 0) +#define RT5677_SEL_SRC_IB01_SFT 0 + +/* Jack Detect Control 1 (0xb5) */ +#define RT5677_SEL_GPIO_JD1_MASK (0x3 << 14) +#define RT5677_SEL_GPIO_JD1_SFT 14 +#define RT5677_SEL_GPIO_JD2_MASK (0x3 << 12) +#define RT5677_SEL_GPIO_JD2_SFT 12 +#define RT5677_SEL_GPIO_JD3_MASK (0x3 << 10) +#define RT5677_SEL_GPIO_JD3_SFT 10 + +/* IRQ Control 1 (0xbd) */ +#define RT5677_STA_GPIO_JD1 (0x1 << 15) +#define RT5677_STA_GPIO_JD1_SFT 15 +#define RT5677_EN_IRQ_GPIO_JD1 (0x1 << 14) +#define RT5677_EN_IRQ_GPIO_JD1_SFT 14 +#define RT5677_EN_GPIO_JD1_STICKY (0x1 << 13) +#define RT5677_EN_GPIO_JD1_STICKY_SFT 13 +#define RT5677_INV_GPIO_JD1 (0x1 << 12) +#define RT5677_INV_GPIO_JD1_SFT 12 +#define RT5677_STA_GPIO_JD2 (0x1 << 11) +#define RT5677_STA_GPIO_JD2_SFT 11 +#define RT5677_EN_IRQ_GPIO_JD2 (0x1 << 10) +#define RT5677_EN_IRQ_GPIO_JD2_SFT 10 +#define RT5677_EN_GPIO_JD2_STICKY (0x1 << 9) +#define RT5677_EN_GPIO_JD2_STICKY_SFT 9 +#define RT5677_INV_GPIO_JD2 (0x1 << 8) +#define RT5677_INV_GPIO_JD2_SFT 8 +#define RT5677_STA_MICBIAS1_OVCD (0x1 << 7) +#define RT5677_STA_MICBIAS1_OVCD_SFT 7 +#define RT5677_EN_IRQ_MICBIAS1_OVCD (0x1 << 6) +#define RT5677_EN_IRQ_MICBIAS1_OVCD_SFT 6 +#define RT5677_EN_MICBIAS1_OVCD_STICKY (0x1 << 5) +#define RT5677_EN_MICBIAS1_OVCD_STICKY_SFT 5 +#define RT5677_INV_MICBIAS1_OVCD (0x1 << 4) +#define RT5677_INV_MICBIAS1_OVCD_SFT 4 +#define RT5677_STA_GPIO_JD3 (0x1 << 3) +#define RT5677_STA_GPIO_JD3_SFT 3 +#define RT5677_EN_IRQ_GPIO_JD3 (0x1 << 2) +#define RT5677_EN_IRQ_GPIO_JD3_SFT 2 +#define RT5677_EN_GPIO_JD3_STICKY (0x1 << 1) +#define RT5677_EN_GPIO_JD3_STICKY_SFT 1 +#define RT5677_INV_GPIO_JD3 (0x1 << 0) +#define RT5677_INV_GPIO_JD3_SFT 0 + +/* GPIO status (0xbf) */ +#define RT5677_GPIO6_STATUS_MASK (0x1 << 5) +#define RT5677_GPIO6_STATUS_SFT 5 +#define RT5677_GPIO5_STATUS_MASK (0x1 << 4) +#define RT5677_GPIO5_STATUS_SFT 4 +#define RT5677_GPIO4_STATUS_MASK (0x1 << 3) +#define RT5677_GPIO4_STATUS_SFT 3 +#define RT5677_GPIO3_STATUS_MASK (0x1 << 2) +#define RT5677_GPIO3_STATUS_SFT 2 +#define RT5677_GPIO2_STATUS_MASK (0x1 << 1) +#define RT5677_GPIO2_STATUS_SFT 1 +#define RT5677_GPIO1_STATUS_MASK (0x1 << 0) +#define RT5677_GPIO1_STATUS_SFT 0 + +/* GPIO Control 1 (0xc0) */ +#define RT5677_GPIO1_PIN_MASK (0x1 << 15) +#define RT5677_GPIO1_PIN_SFT 15 +#define RT5677_GPIO1_PIN_GPIO1 (0x0 << 15) +#define RT5677_GPIO1_PIN_IRQ (0x1 << 15) +#define RT5677_IPTV_MODE_MASK (0x1 << 14) +#define RT5677_IPTV_MODE_SFT 14 +#define RT5677_IPTV_MODE_GPIO (0x0 << 14) +#define RT5677_IPTV_MODE_IPTV (0x1 << 14) +#define RT5677_FUNC_MODE_MASK (0x1 << 13) +#define RT5677_FUNC_MODE_SFT 13 +#define RT5677_FUNC_MODE_DMIC_GPIO (0x0 << 13) +#define RT5677_FUNC_MODE_JTAG (0x1 << 13) + +/* GPIO Control 2 (0xc1) & 3 (0xc2) common bits */ +#define RT5677_GPIOx_DIR_MASK (0x1 << 2) +#define RT5677_GPIOx_DIR_SFT 2 +#define RT5677_GPIOx_DIR_IN (0x0 << 2) +#define RT5677_GPIOx_DIR_OUT (0x1 << 2) +#define RT5677_GPIOx_OUT_MASK (0x1 << 1) +#define RT5677_GPIOx_OUT_SFT 1 +#define RT5677_GPIOx_OUT_LO (0x0 << 1) +#define RT5677_GPIOx_OUT_HI (0x1 << 1) +#define RT5677_GPIOx_P_MASK (0x1 << 0) +#define RT5677_GPIOx_P_SFT 0 +#define RT5677_GPIOx_P_NOR (0x0 << 0) +#define RT5677_GPIOx_P_INV (0x1 << 0) + +/* General Control (0xfa) */ +#define RT5677_IRQ_DEBOUNCE_SEL_MASK (0x3 << 3) +#define RT5677_IRQ_DEBOUNCE_SEL_MCLK (0x0 << 3) +#define RT5677_IRQ_DEBOUNCE_SEL_RC (0x1 << 3) +#define RT5677_IRQ_DEBOUNCE_SEL_SLIM (0x2 << 3) + +/* Virtual DSP Mixer Control (0xf7 0xf8 0xf9) */ +#define RT5677_DSP_IB_01_H (0x1 << 15) +#define RT5677_DSP_IB_01_H_SFT 15 +#define RT5677_DSP_IB_23_H (0x1 << 14) +#define RT5677_DSP_IB_23_H_SFT 14 +#define RT5677_DSP_IB_45_H (0x1 << 13) +#define RT5677_DSP_IB_45_H_SFT 13 +#define RT5677_DSP_IB_6_H (0x1 << 12) +#define RT5677_DSP_IB_6_H_SFT 12 +#define RT5677_DSP_IB_7_H (0x1 << 11) +#define RT5677_DSP_IB_7_H_SFT 11 +#define RT5677_DSP_IB_8_H (0x1 << 10) +#define RT5677_DSP_IB_8_H_SFT 10 +#define RT5677_DSP_IB_9_H (0x1 << 9) +#define RT5677_DSP_IB_9_H_SFT 9 +#define RT5677_DSP_IB_01_L (0x1 << 7) +#define RT5677_DSP_IB_01_L_SFT 7 +#define RT5677_DSP_IB_23_L (0x1 << 6) +#define RT5677_DSP_IB_23_L_SFT 6 +#define RT5677_DSP_IB_45_L (0x1 << 5) +#define RT5677_DSP_IB_45_L_SFT 5 +#define RT5677_DSP_IB_6_L (0x1 << 4) +#define RT5677_DSP_IB_6_L_SFT 4 +#define RT5677_DSP_IB_7_L (0x1 << 3) +#define RT5677_DSP_IB_7_L_SFT 3 +#define RT5677_DSP_IB_8_L (0x1 << 2) +#define RT5677_DSP_IB_8_L_SFT 2 +#define RT5677_DSP_IB_9_L (0x1 << 1) +#define RT5677_DSP_IB_9_L_SFT 1 + +/* General Control2 (0xfc)*/ +#define RT5677_GPIO5_FUNC_MASK (0x1 << 9) +#define RT5677_GPIO5_FUNC_GPIO (0x0 << 9) +#define RT5677_GPIO5_FUNC_DMIC (0x1 << 9) + +#define RT5677_FIRMWARE1 "rt5677_dsp_fw1.bin" +#define RT5677_FIRMWARE2 "rt5677_dsp_fw2.bin" + +#define RT5677_DRV_NAME "rt5677" + +/* System Clock Source */ +enum { + RT5677_SCLK_S_MCLK, + RT5677_SCLK_S_PLL1, + RT5677_SCLK_S_RCCLK, +}; + +/* PLL1 Source */ +enum { + RT5677_PLL1_S_MCLK, + RT5677_PLL1_S_BCLK1, + RT5677_PLL1_S_BCLK2, + RT5677_PLL1_S_BCLK3, + RT5677_PLL1_S_BCLK4, +}; + +enum { + RT5677_AIF1, + RT5677_AIF2, + RT5677_AIF3, + RT5677_AIF4, + RT5677_AIF5, + RT5677_AIFS, + RT5677_DSPBUFF, +}; + +enum { + RT5677_GPIO1, + RT5677_GPIO2, + RT5677_GPIO3, + RT5677_GPIO4, + RT5677_GPIO5, + RT5677_GPIO6, + RT5677_GPIO_NUM, +}; + +enum { + RT5677_IRQ_JD1, + RT5677_IRQ_JD2, + RT5677_IRQ_JD3, + RT5677_IRQ_NUM, +}; + +enum rt5677_type { + RT5677 = 1, + RT5676 = 2, +}; + +/* ASRC clock source selection */ +enum { + RT5677_CLK_SEL_SYS, + RT5677_CLK_SEL_I2S1_ASRC, + RT5677_CLK_SEL_I2S2_ASRC, + RT5677_CLK_SEL_I2S3_ASRC, + RT5677_CLK_SEL_I2S4_ASRC, + RT5677_CLK_SEL_I2S5_ASRC, + RT5677_CLK_SEL_I2S6_ASRC, + RT5677_CLK_SEL_SYS2, + RT5677_CLK_SEL_SYS3, + RT5677_CLK_SEL_SYS4, + RT5677_CLK_SEL_SYS5, + RT5677_CLK_SEL_SYS6, + RT5677_CLK_SEL_SYS7, +}; + +/* filter mask */ +enum { + RT5677_DA_STEREO_FILTER = 0x1, + RT5677_DA_MONO2_L_FILTER = (0x1 << 1), + RT5677_DA_MONO2_R_FILTER = (0x1 << 2), + RT5677_DA_MONO3_L_FILTER = (0x1 << 3), + RT5677_DA_MONO3_R_FILTER = (0x1 << 4), + RT5677_DA_MONO4_L_FILTER = (0x1 << 5), + RT5677_DA_MONO4_R_FILTER = (0x1 << 6), + RT5677_AD_STEREO1_FILTER = (0x1 << 7), + RT5677_AD_STEREO2_FILTER = (0x1 << 8), + RT5677_AD_STEREO3_FILTER = (0x1 << 9), + RT5677_AD_STEREO4_FILTER = (0x1 << 10), + RT5677_AD_MONO_L_FILTER = (0x1 << 11), + RT5677_AD_MONO_R_FILTER = (0x1 << 12), + RT5677_DSP_OB_0_3_FILTER = (0x1 << 13), + RT5677_DSP_OB_4_7_FILTER = (0x1 << 14), + RT5677_I2S1_SOURCE = (0x1 << 15), + RT5677_I2S2_SOURCE = (0x1 << 16), + RT5677_I2S3_SOURCE = (0x1 << 17), + RT5677_I2S4_SOURCE = (0x1 << 18), +}; + +enum rt5677_dmic2_clk { + RT5677_DMIC_CLK1 = 0, + RT5677_DMIC_CLK2 = 1, +}; + +struct rt5677_platform_data { + /* IN1/IN2/LOUT1/LOUT2/LOUT3 can optionally be differential */ + bool in1_diff; + bool in2_diff; + bool lout1_diff; + bool lout2_diff; + bool lout3_diff; + /* DMIC2 clock source selection */ + enum rt5677_dmic2_clk dmic2_clk_pin; + + /* configures GPIO, 0 - floating, 1 - pulldown, 2 - pullup */ + u8 gpio_config[6]; + + /* jd1 can select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively */ + unsigned int jd1_gpio; + /* jd2 and jd3 can select 0 ~ 3 as + OFF, GPIO4, GPIO5 and GPIO6 respectively */ + unsigned int jd2_gpio; + unsigned int jd3_gpio; + + /* Set MICBIAS1 VDD 1v8 or 3v3 */ + bool micbias1_vdd_3v3; +}; + +struct rt5677_priv { + struct snd_soc_component *component; + struct device *dev; + struct rt5677_platform_data pdata; + struct regmap *regmap, *regmap_physical; + const struct firmware *fw1, *fw2; + struct mutex dsp_cmd_lock, dsp_pri_lock; + + int sysclk; + int sysclk_src; + int lrck[RT5677_AIFS]; + int bclk[RT5677_AIFS]; + int master[RT5677_AIFS]; + int pll_src; + int pll_in; + int pll_out; + struct gpio_desc *pow_ldo2; /* POW_LDO2 pin */ + struct gpio_desc *reset_pin; /* RESET pin */ + enum rt5677_type type; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif + bool dsp_vad_en_request; /* DSP VAD enable/disable request */ + bool dsp_vad_en; /* dsp_work parameter */ + bool is_dsp_mode; + bool is_vref_slow; + struct delayed_work dsp_work; + + /* Interrupt handling */ + struct irq_domain *domain; + struct mutex irq_lock; + unsigned int irq_en; + struct delayed_work resume_irq_check; + int irq; + + int (*set_dsp_vad)(struct snd_soc_component *component, bool on); +}; + +int rt5677_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +#endif /* __RT5677_H__ */ diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c new file mode 100644 index 0000000000..fbad1ed066 --- /dev/null +++ b/sound/soc/codecs/rt5682-i2c.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt5682.c -- RT5682 ALSA SoC audio component driver +// +// Copyright 2018 Realtek Semiconductor Corp. +// Author: Bard Liao +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5682.h" + +static const struct rt5682_platform_data i2s_default_platform_data = { + .dmic1_data_pin = RT5682_DMIC1_DATA_GPIO2, + .dmic1_clk_pin = RT5682_DMIC1_CLK_GPIO3, + .jd_src = RT5682_JD1, + .btndet_delay = 16, + .dai_clk_names[RT5682_DAI_WCLK_IDX] = "rt5682-dai-wclk", + .dai_clk_names[RT5682_DAI_BCLK_IDX] = "rt5682-dai-bclk", +}; + +static const struct regmap_config rt5682_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT5682_I2C_MODE, + .volatile_reg = rt5682_volatile_register, + .readable_reg = rt5682_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5682_reg, + .num_reg_defaults = RT5682_REG_NUM, + .use_single_read = true, + .use_single_write = true, +}; + +static void rt5682_jd_check_handler(struct work_struct *work) +{ + struct rt5682_priv *rt5682 = container_of(work, struct rt5682_priv, + jd_check_work.work); + + if (snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL) & RT5682_JDH_RS_MASK) + /* jack out */ + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, 0); + else + schedule_delayed_work(&rt5682->jd_check_work, 500); +} + +static irqreturn_t rt5682_irq(int irq, void *data) +{ + struct rt5682_priv *rt5682 = data; + + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time)); + + return IRQ_HANDLED; +} + +static struct snd_soc_dai_driver rt5682_dai[] = { + { + .name = "rt5682-aif1", + .id = RT5682_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_aif1_dai_ops, + }, + { + .name = "rt5682-aif2", + .id = RT5682_AIF2, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_aif2_dai_ops, + }, +}; + +static void rt5682_i2c_disable_regulators(void *data) +{ + struct rt5682_priv *rt5682 = data; + + regulator_bulk_disable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies); +} + +static int rt5682_i2c_probe(struct i2c_client *i2c) +{ + struct rt5682_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5682_priv *rt5682; + int i, ret; + unsigned int val; + + rt5682 = devm_kzalloc(&i2c->dev, sizeof(struct rt5682_priv), + GFP_KERNEL); + if (!rt5682) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5682); + + rt5682->i2c_dev = &i2c->dev; + + rt5682->pdata = i2s_default_platform_data; + + if (pdata) + rt5682->pdata = *pdata; + else + rt5682_parse_dt(rt5682, &i2c->dev); + + rt5682->regmap = devm_regmap_init_i2c(i2c, &rt5682_regmap); + if (IS_ERR(rt5682->regmap)) { + ret = PTR_ERR(rt5682->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(rt5682->supplies); i++) + rt5682->supplies[i].supply = rt5682_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(rt5682->supplies), + rt5682->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5682->supplies), + rt5682->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(&i2c->dev, rt5682_i2c_disable_regulators, + rt5682); + if (ret) + return ret; + + ret = rt5682_get_ldo1(rt5682, &i2c->dev); + if (ret) + return ret; + + /* Sleep for 300 ms miniumum */ + usleep_range(300000, 350000); + + regmap_write(rt5682->regmap, RT5682_I2C_MODE, 0x1); + usleep_range(10000, 15000); + + regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val); + if (val != DEVICE_ID) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5682\n", val); + return -ENODEV; + } + + mutex_init(&rt5682->calibrate_mutex); + rt5682_calibrate(rt5682); + + rt5682_apply_patch_list(rt5682, &i2c->dev); + + regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000); + + /* DMIC pin*/ + if (rt5682->pdata.dmic1_data_pin != RT5682_DMIC1_NULL) { + switch (rt5682->pdata.dmic1_data_pin) { + case RT5682_DMIC1_DATA_GPIO2: /* share with LRCK2 */ + regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO2); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP2_PIN_MASK, RT5682_GP2_PIN_DMIC_SDA); + break; + + case RT5682_DMIC1_DATA_GPIO5: /* share with DACDAT1 */ + regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_DP_MASK, RT5682_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP5_PIN_MASK, RT5682_GP5_PIN_DMIC_SDA); + break; + + default: + dev_warn(&i2c->dev, "invalid DMIC_DAT pin\n"); + break; + } + + switch (rt5682->pdata.dmic1_clk_pin) { + case RT5682_DMIC1_CLK_GPIO1: /* share with IRQ */ + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_DMIC_CLK); + break; + + case RT5682_DMIC1_CLK_GPIO3: /* share with BCLK2 */ + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP3_PIN_MASK, RT5682_GP3_PIN_DMIC_CLK); + if (rt5682->pdata.dmic_clk_driving_high) + regmap_update_bits(rt5682->regmap, + RT5682_PAD_DRIVING_CTRL, + RT5682_PAD_DRV_GP3_MASK, + 2 << RT5682_PAD_DRV_GP3_SFT); + break; + + default: + dev_warn(&i2c->dev, "invalid DMIC_CLK pin\n"); + break; + } + } + + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK, + RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP4_PIN_MASK | RT5682_GP5_PIN_MASK, + RT5682_GP4_PIN_ADCDAT1 | RT5682_GP5_PIN_DACDAT1); + regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8, + RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA); + regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1, + RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ); + regmap_update_bits(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1, + RT5682_PM_HP_MASK, RT5682_PM_HP_HV); + regmap_update_bits(rt5682->regmap, RT5682_DMIC_CTRL_1, + RT5682_FIFO_CLK_DIV_MASK, RT5682_FIFO_CLK_DIV_2); + + INIT_DELAYED_WORK(&rt5682->jack_detect_work, + rt5682_jack_detect_handler); + INIT_DELAYED_WORK(&rt5682->jd_check_work, + rt5682_jd_check_handler); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5682_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_ONESHOT, "rt5682", rt5682); + if (!ret) + rt5682->irq = i2c->irq; + else + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + } + +#ifdef CONFIG_COMMON_CLK + /* Check if MCLK provided */ + rt5682->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(rt5682->mclk)) + return PTR_ERR(rt5682->mclk); + + /* Register CCF DAI clock control */ + ret = rt5682_register_dai_clks(rt5682); + if (ret) + return ret; + + /* Initial setup for CCF */ + rt5682->lrck[RT5682_AIF1] = 48000; +#endif + + return devm_snd_soc_register_component(&i2c->dev, + &rt5682_soc_component_dev, + rt5682_dai, ARRAY_SIZE(rt5682_dai)); +} + +static void rt5682_i2c_shutdown(struct i2c_client *client) +{ + struct rt5682_priv *rt5682 = i2c_get_clientdata(client); + + disable_irq(client->irq); + cancel_delayed_work_sync(&rt5682->jack_detect_work); + cancel_delayed_work_sync(&rt5682->jd_check_work); + + rt5682_reset(rt5682); +} + +static void rt5682_i2c_remove(struct i2c_client *client) +{ + rt5682_i2c_shutdown(client); +} + +static const struct of_device_id rt5682_of_match[] = { + {.compatible = "realtek,rt5682i"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5682_of_match); + +static const struct acpi_device_id rt5682_acpi_match[] = { + {"10EC5682", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5682_acpi_match); + +static const struct i2c_device_id rt5682_i2c_id[] = { + {"rt5682", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5682_i2c_id); + +static struct i2c_driver rt5682_i2c_driver = { + .driver = { + .name = "rt5682", + .of_match_table = rt5682_of_match, + .acpi_match_table = rt5682_acpi_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = rt5682_i2c_probe, + .remove = rt5682_i2c_remove, + .shutdown = rt5682_i2c_shutdown, + .id_table = rt5682_i2c_id, +}; +module_i2c_driver(rt5682_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5682 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c new file mode 100644 index 0000000000..e67c2e19cb --- /dev/null +++ b/sound/soc/codecs/rt5682-sdw.c @@ -0,0 +1,813 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt5682-sdw.c -- RT5682 ALSA SoC audio component driver +// +// Copyright 2019 Realtek Semiconductor Corp. +// Author: Oder Chiou +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5682.h" + +#define RT5682_SDW_ADDR_L 0x3000 +#define RT5682_SDW_ADDR_H 0x3001 +#define RT5682_SDW_DATA_L 0x3004 +#define RT5682_SDW_DATA_H 0x3005 +#define RT5682_SDW_CMD 0x3008 + +static int rt5682_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + unsigned int data_l, data_h; + + regmap_write(rt5682->sdw_regmap, RT5682_SDW_CMD, 0); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_H, (reg >> 8) & 0xff); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_L, (reg & 0xff)); + regmap_read(rt5682->sdw_regmap, RT5682_SDW_DATA_H, &data_h); + regmap_read(rt5682->sdw_regmap, RT5682_SDW_DATA_L, &data_l); + + *val = (data_h << 8) | data_l; + + dev_vdbg(dev, "[%s] %04x => %04x\n", __func__, reg, *val); + + return 0; +} + +static int rt5682_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + + regmap_write(rt5682->sdw_regmap, RT5682_SDW_CMD, 1); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_H, (reg >> 8) & 0xff); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_ADDR_L, (reg & 0xff)); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_DATA_H, (val >> 8) & 0xff); + regmap_write(rt5682->sdw_regmap, RT5682_SDW_DATA_L, (val & 0xff)); + + dev_vdbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + + return 0; +} + +static const struct regmap_config rt5682_sdw_indirect_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT5682_I2C_MODE, + .volatile_reg = rt5682_volatile_register, + .readable_reg = rt5682_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5682_reg, + .num_reg_defaults = RT5682_REG_NUM, + .use_single_read = true, + .use_single_write = true, + .reg_read = rt5682_sdw_read, + .reg_write = rt5682_sdw_write, +}; + +static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt5682_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt5682_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream; + int retval; + unsigned int val_p = 0, val_c = 0, osr_p = 0, osr_c = 0; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + if (!sdw_stream) + return -ENOMEM; + + if (!rt5682->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_config.num = 1; + else + port_config.num = 2; + + retval = sdw_stream_add_slave(rt5682->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + switch (params_rate(params)) { + case 48000: + val_p = RT5682_SDW_REF_1_48K; + val_c = RT5682_SDW_REF_2_48K; + break; + case 96000: + val_p = RT5682_SDW_REF_1_96K; + val_c = RT5682_SDW_REF_2_96K; + break; + case 192000: + val_p = RT5682_SDW_REF_1_192K; + val_c = RT5682_SDW_REF_2_192K; + break; + case 32000: + val_p = RT5682_SDW_REF_1_32K; + val_c = RT5682_SDW_REF_2_32K; + break; + case 24000: + val_p = RT5682_SDW_REF_1_24K; + val_c = RT5682_SDW_REF_2_24K; + break; + case 16000: + val_p = RT5682_SDW_REF_1_16K; + val_c = RT5682_SDW_REF_2_16K; + break; + case 12000: + val_p = RT5682_SDW_REF_1_12K; + val_c = RT5682_SDW_REF_2_12K; + break; + case 8000: + val_p = RT5682_SDW_REF_1_8K; + val_c = RT5682_SDW_REF_2_8K; + break; + case 44100: + val_p = RT5682_SDW_REF_1_44K; + val_c = RT5682_SDW_REF_2_44K; + break; + case 88200: + val_p = RT5682_SDW_REF_1_88K; + val_c = RT5682_SDW_REF_2_88K; + break; + case 176400: + val_p = RT5682_SDW_REF_1_176K; + val_c = RT5682_SDW_REF_2_176K; + break; + case 22050: + val_p = RT5682_SDW_REF_1_22K; + val_c = RT5682_SDW_REF_2_22K; + break; + case 11025: + val_p = RT5682_SDW_REF_1_11K; + val_c = RT5682_SDW_REF_2_11K; + break; + default: + return -EINVAL; + } + + if (params_rate(params) <= 48000) { + osr_p = RT5682_DAC_OSR_D_8; + osr_c = RT5682_ADC_OSR_D_8; + } else if (params_rate(params) <= 96000) { + osr_p = RT5682_DAC_OSR_D_4; + osr_c = RT5682_ADC_OSR_D_4; + } else { + osr_p = RT5682_DAC_OSR_D_2; + osr_c = RT5682_ADC_OSR_D_2; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(rt5682->regmap, RT5682_SDW_REF_CLK, + RT5682_SDW_REF_1_MASK, val_p); + regmap_update_bits(rt5682->regmap, RT5682_ADDA_CLK_1, + RT5682_DAC_OSR_MASK, osr_p); + } else { + regmap_update_bits(rt5682->regmap, RT5682_SDW_REF_CLK, + RT5682_SDW_REF_2_MASK, val_c); + regmap_update_bits(rt5682->regmap, RT5682_ADDA_CLK_1, + RT5682_ADC_OSR_MASK, osr_c); + } + + return retval; +} + +static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt5682->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt5682->slave, sdw_stream); + return 0; +} + +static const struct snd_soc_dai_ops rt5682_sdw_ops = { + .hw_params = rt5682_sdw_hw_params, + .hw_free = rt5682_sdw_hw_free, + .set_stream = rt5682_set_sdw_stream, + .shutdown = rt5682_sdw_shutdown, +}; + +static struct snd_soc_dai_driver rt5682_dai[] = { + { + .name = "rt5682-aif1", + .id = RT5682_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_aif1_dai_ops, + }, + { + .name = "rt5682-aif2", + .id = RT5682_AIF2, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_aif2_dai_ops, + }, + { + .name = "rt5682-sdw", + .id = RT5682_SDW, + .playback = { + .stream_name = "SDW Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .capture = { + .stream_name = "SDW Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682_STEREO_RATES, + .formats = RT5682_FORMATS, + }, + .ops = &rt5682_sdw_ops, + }, +}; + +static int rt5682_sdw_init(struct device *dev, struct regmap *regmap, + struct sdw_slave *slave) +{ + struct rt5682_priv *rt5682; + int ret; + + rt5682 = devm_kzalloc(dev, sizeof(*rt5682), GFP_KERNEL); + if (!rt5682) + return -ENOMEM; + + dev_set_drvdata(dev, rt5682); + rt5682->slave = slave; + rt5682->sdw_regmap = regmap; + rt5682->is_sdw = true; + + mutex_init(&rt5682->disable_irq_lock); + + rt5682->regmap = devm_regmap_init(dev, NULL, dev, + &rt5682_sdw_indirect_regmap); + if (IS_ERR(rt5682->regmap)) { + ret = PTR_ERR(rt5682->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + + ret = rt5682_get_ldo1(rt5682, dev); + if (ret) + return ret; + + regcache_cache_only(rt5682->sdw_regmap, true); + regcache_cache_only(rt5682->regmap, true); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt5682->hw_init = false; + rt5682->first_hw_init = false; + + mutex_init(&rt5682->calibrate_mutex); + INIT_DELAYED_WORK(&rt5682->jack_detect_work, + rt5682_jack_detect_handler); + + ret = devm_snd_soc_register_component(dev, + &rt5682_soc_component_dev, + rt5682_dai, ARRAY_SIZE(rt5682_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return ret; +} + +static int rt5682_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + int ret = 0, loop = 10; + unsigned int val; + + rt5682->disable_irq = false; + + if (rt5682->hw_init) + return 0; + + regcache_cache_only(rt5682->sdw_regmap, false); + regcache_cache_only(rt5682->regmap, false); + if (rt5682->first_hw_init) + regcache_cache_bypass(rt5682->regmap, true); + + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + if (!rt5682->first_hw_init) + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + pm_runtime_get_noresume(&slave->dev); + + while (loop > 0) { + regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val); + if (val == DEVICE_ID) + break; + dev_warn(dev, "Device with ID register %x is not rt5682\n", val); + usleep_range(30000, 30005); + loop--; + } + + if (val != DEVICE_ID) { + dev_err(dev, "Device with ID register %x is not rt5682\n", val); + ret = -ENODEV; + goto err_nodev; + } + + rt5682_calibrate(rt5682); + + if (rt5682->first_hw_init) { + regcache_cache_bypass(rt5682->regmap, false); + regcache_mark_dirty(rt5682->regmap); + regcache_sync(rt5682->regmap); + + /* volatile registers */ + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, + RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); + + goto reinit; + } + + rt5682_apply_patch_list(rt5682, dev); + + regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0000); + + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_LDO1_DVO_MASK | RT5682_HP_DRIVER_MASK, + RT5682_LDO1_DVO_12 | RT5682_HP_DRIVER_5X); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080); + regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8, + RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA); + regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1, + RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ); + regmap_update_bits(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1, + RT5682_PM_HP_MASK, RT5682_PM_HP_HV); + + /* Soundwire */ + regmap_write(rt5682->regmap, RT5682_PLL2_INTERNAL, 0xa266); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_1, 0x1700); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_2, 0x0006); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_3, 0x2600); + regmap_write(rt5682->regmap, RT5682_PLL2_CTRL_4, 0x0c8f); + regmap_write(rt5682->regmap, RT5682_PLL_TRACK_2, 0x3000); + regmap_write(rt5682->regmap, RT5682_PLL_TRACK_3, 0x4000); + regmap_update_bits(rt5682->regmap, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK | RT5682_PLL2_SRC_MASK, + RT5682_SCLK_SRC_PLL2 | RT5682_PLL2_SRC_SDW); + + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_2, + RT5682_EXT_JD_SRC, RT5682_EXT_JD_SRC_MANUAL); + regmap_write(rt5682->regmap, RT5682_CBJ_CTRL_1, 0xd142); + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_5, 0x0700, 0x0600); + regmap_update_bits(rt5682->regmap, RT5682_CBJ_CTRL_3, + RT5682_CBJ_IN_BUF_EN, RT5682_CBJ_IN_BUF_EN); + regmap_update_bits(rt5682->regmap, RT5682_SAR_IL_CMD_1, + RT5682_SAR_POW_MASK, RT5682_SAR_POW_EN); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_IRQ | RT5682_POW_JDH | + RT5682_POW_ANA, RT5682_POW_IRQ | + RT5682_POW_JDH | RT5682_POW_ANA); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, + RT5682_PWR_JDH, RT5682_PWR_JDH); + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK | RT5682_JD1_IRQ_MASK, + RT5682_JD1_EN | RT5682_JD1_IRQ_PUL); + +reinit: + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(250)); + + /* Mark Slave initialization complete */ + rt5682->hw_init = true; + rt5682->first_hw_init = true; + +err_nodev: + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret); + + return ret; +} + +static bool rt5682_sdw_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x3000: + case 0x3001: + case 0x3004: + case 0x3005: + case 0x3008: + return true; + default: + return false; + } +} + +static const struct regmap_config rt5682_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, + .val_bits = 8, + .max_register = RT5682_I2C_MODE, + .readable_reg = rt5682_sdw_readable_register, + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt5682_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt5682->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt5682->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt5682_io_init(&slave->dev, slave); +} + +static int rt5682_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH | + SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x4; /* BITMAP: 00000100 */ + prop->sink_ports = 0x2; /* BITMAP: 00000010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +/* Bus clock frequency */ +#define RT5682_CLK_FREQ_9600000HZ 9600000 +#define RT5682_CLK_FREQ_12000000HZ 12000000 +#define RT5682_CLK_FREQ_6000000HZ 6000000 +#define RT5682_CLK_FREQ_4800000HZ 4800000 +#define RT5682_CLK_FREQ_2400000HZ 2400000 +#define RT5682_CLK_FREQ_12288000HZ 12288000 + +static int rt5682_clock_config(struct device *dev) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt5682->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT5682_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT5682_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT5682_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT5682_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT5682_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT5682_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt5682->sdw_regmap, 0xe0, value); + regmap_write(rt5682->sdw_regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +static int rt5682_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt5682->params, params, sizeof(*params)); + + ret = rt5682_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt5682_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + mutex_lock(&rt5682->disable_irq_lock); + if (status->control_port & 0x4 && !rt5682->disable_irq) { + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(rt5682->irq_work_delay_time)); + } + mutex_unlock(&rt5682->disable_irq_lock); + + return 0; +} + +static const struct sdw_slave_ops rt5682_slave_ops = { + .read_prop = rt5682_read_prop, + .interrupt_callback = rt5682_interrupt_callback, + .update_status = rt5682_update_status, + .bus_config = rt5682_bus_config, +}; + +static int rt5682_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + /* Regmap Initialization */ + regmap = devm_regmap_init_sdw(slave, &rt5682_sdw_regmap); + if (IS_ERR(regmap)) + return -EINVAL; + + return rt5682_sdw_init(&slave->dev, regmap, slave); +} + +static int rt5682_sdw_remove(struct sdw_slave *slave) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); + + if (rt5682->hw_init) + cancel_delayed_work_sync(&rt5682->jack_detect_work); + + pm_runtime_disable(&slave->dev); + + return 0; +} + +static const struct sdw_device_id rt5682_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x5682, 0x2, 0, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt5682_id); + +static int __maybe_unused rt5682_dev_suspend(struct device *dev) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + + if (!rt5682->hw_init) + return 0; + + cancel_delayed_work_sync(&rt5682->jack_detect_work); + + regcache_cache_only(rt5682->sdw_regmap, true); + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); + + return 0; +} + +static int __maybe_unused rt5682_dev_system_suspend(struct device *dev) +{ + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret; + + if (!rt5682->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt5682->disable_irq_lock); + rt5682->disable_irq = true; + ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, + SDW_SCP_INT1_IMPL_DEF, 0); + mutex_unlock(&rt5682->disable_irq_lock); + + if (ret < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:", __func__); + } + + return rt5682_dev_suspend(dev); +} + +static int __maybe_unused rt5682_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt5682_priv *rt5682 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt5682->first_hw_init) + return 0; + + if (!slave->unattach_request) { + if (rt5682->disable_irq == true) { + mutex_lock(&rt5682->disable_irq_lock); + sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); + rt5682->disable_irq = false; + mutex_unlock(&rt5682->disable_irq_lock); + } + goto regmap_sync; + } + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT5682_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt5682->sdw_regmap, false); + regcache_cache_only(rt5682->regmap, false); + regcache_sync(rt5682->regmap); + + return 0; +} + +static const struct dev_pm_ops rt5682_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt5682_dev_system_suspend, rt5682_dev_resume) + SET_RUNTIME_PM_OPS(rt5682_dev_suspend, rt5682_dev_resume, NULL) +}; + +static struct sdw_driver rt5682_sdw_driver = { + .driver = { + .name = "rt5682", + .owner = THIS_MODULE, + .pm = &rt5682_pm, + }, + .probe = rt5682_sdw_probe, + .remove = rt5682_sdw_remove, + .ops = &rt5682_slave_ops, + .id_table = rt5682_id, +}; +module_sdw_driver(rt5682_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT5682 driver SDW"); +MODULE_AUTHOR("Oder Chiou "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c new file mode 100644 index 0000000000..e3aca9c785 --- /dev/null +++ b/sound/soc/codecs/rt5682.c @@ -0,0 +1,3179 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt5682.c -- RT5682 ALSA SoC audio component driver +// +// Copyright 2018 Realtek Semiconductor Corp. +// Author: Bard Liao +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rl6231.h" +#include "rt5682.h" + +const char *rt5682_supply_names[RT5682_NUM_SUPPLIES] = { + "AVDD", + "MICVDD", + "VBAT", + "DBVDD", + "LDO1-IN", +}; +EXPORT_SYMBOL_GPL(rt5682_supply_names); + +static const struct reg_sequence patch_list[] = { + {RT5682_HP_IMP_SENS_CTRL_19, 0x1000}, + {RT5682_DAC_ADC_DIG_VOL1, 0xa020}, + {RT5682_I2C_CTRL, 0x000f}, + {RT5682_PLL2_INTERNAL, 0x8266}, + {RT5682_SAR_IL_CMD_1, 0x22b7}, + {RT5682_SAR_IL_CMD_3, 0x0365}, + {RT5682_SAR_IL_CMD_6, 0x0110}, + {RT5682_CHARGE_PUMP_1, 0x0210}, + {RT5682_HP_LOGIC_CTRL_2, 0x0007}, + {RT5682_SAR_IL_CMD_2, 0xac00}, + {RT5682_CBJ_CTRL_7, 0x0104}, +}; + +void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev) +{ + int ret; + + ret = regmap_multi_reg_write(rt5682->regmap, patch_list, + ARRAY_SIZE(patch_list)); + if (ret) + dev_warn(dev, "Failed to apply regmap patch: %d\n", ret); +} +EXPORT_SYMBOL_GPL(rt5682_apply_patch_list); + +const struct reg_default rt5682_reg[RT5682_REG_NUM] = { + {0x0002, 0x8080}, + {0x0003, 0x8000}, + {0x0005, 0x0000}, + {0x0006, 0x0000}, + {0x0008, 0x800f}, + {0x000b, 0x0000}, + {0x0010, 0x4040}, + {0x0011, 0x0000}, + {0x0012, 0x1404}, + {0x0013, 0x1000}, + {0x0014, 0xa00a}, + {0x0015, 0x0404}, + {0x0016, 0x0404}, + {0x0019, 0xafaf}, + {0x001c, 0x2f2f}, + {0x001f, 0x0000}, + {0x0022, 0x5757}, + {0x0023, 0x0039}, + {0x0024, 0x000b}, + {0x0026, 0xc0c4}, + {0x0029, 0x8080}, + {0x002a, 0xa0a0}, + {0x002b, 0x0300}, + {0x0030, 0x0000}, + {0x003c, 0x0080}, + {0x0044, 0x0c0c}, + {0x0049, 0x0000}, + {0x0061, 0x0000}, + {0x0062, 0x0000}, + {0x0063, 0x003f}, + {0x0064, 0x0000}, + {0x0065, 0x0000}, + {0x0066, 0x0030}, + {0x0067, 0x0000}, + {0x006b, 0x0000}, + {0x006c, 0x0000}, + {0x006d, 0x2200}, + {0x006e, 0x0a10}, + {0x0070, 0x8000}, + {0x0071, 0x8000}, + {0x0073, 0x0000}, + {0x0074, 0x0000}, + {0x0075, 0x0002}, + {0x0076, 0x0001}, + {0x0079, 0x0000}, + {0x007a, 0x0000}, + {0x007b, 0x0000}, + {0x007c, 0x0100}, + {0x007e, 0x0000}, + {0x0080, 0x0000}, + {0x0081, 0x0000}, + {0x0082, 0x0000}, + {0x0083, 0x0000}, + {0x0084, 0x0000}, + {0x0085, 0x0000}, + {0x0086, 0x0005}, + {0x0087, 0x0000}, + {0x0088, 0x0000}, + {0x008c, 0x0003}, + {0x008d, 0x0000}, + {0x008e, 0x0060}, + {0x008f, 0x1000}, + {0x0091, 0x0c26}, + {0x0092, 0x0073}, + {0x0093, 0x0000}, + {0x0094, 0x0080}, + {0x0098, 0x0000}, + {0x009a, 0x0000}, + {0x009b, 0x0000}, + {0x009c, 0x0000}, + {0x009d, 0x0000}, + {0x009e, 0x100c}, + {0x009f, 0x0000}, + {0x00a0, 0x0000}, + {0x00a3, 0x0002}, + {0x00a4, 0x0001}, + {0x00ae, 0x2040}, + {0x00af, 0x0000}, + {0x00b6, 0x0000}, + {0x00b7, 0x0000}, + {0x00b8, 0x0000}, + {0x00b9, 0x0002}, + {0x00be, 0x0000}, + {0x00c0, 0x0160}, + {0x00c1, 0x82a0}, + {0x00c2, 0x0000}, + {0x00d0, 0x0000}, + {0x00d1, 0x2244}, + {0x00d2, 0x3300}, + {0x00d3, 0x2200}, + {0x00d4, 0x0000}, + {0x00d9, 0x0009}, + {0x00da, 0x0000}, + {0x00db, 0x0000}, + {0x00dc, 0x00c0}, + {0x00dd, 0x2220}, + {0x00de, 0x3131}, + {0x00df, 0x3131}, + {0x00e0, 0x3131}, + {0x00e2, 0x0000}, + {0x00e3, 0x4000}, + {0x00e4, 0x0aa0}, + {0x00e5, 0x3131}, + {0x00e6, 0x3131}, + {0x00e7, 0x3131}, + {0x00e8, 0x3131}, + {0x00ea, 0xb320}, + {0x00eb, 0x0000}, + {0x00f0, 0x0000}, + {0x00f1, 0x00d0}, + {0x00f2, 0x00d0}, + {0x00f6, 0x0000}, + {0x00fa, 0x0000}, + {0x00fb, 0x0000}, + {0x00fc, 0x0000}, + {0x00fd, 0x0000}, + {0x00fe, 0x10ec}, + {0x00ff, 0x6530}, + {0x0100, 0xa0a0}, + {0x010b, 0x0000}, + {0x010c, 0xae00}, + {0x010d, 0xaaa0}, + {0x010e, 0x8aa2}, + {0x010f, 0x02a2}, + {0x0110, 0xc000}, + {0x0111, 0x04a2}, + {0x0112, 0x2800}, + {0x0113, 0x0000}, + {0x0117, 0x0100}, + {0x0125, 0x0410}, + {0x0132, 0x6026}, + {0x0136, 0x5555}, + {0x0138, 0x3700}, + {0x013a, 0x2000}, + {0x013b, 0x2000}, + {0x013c, 0x2005}, + {0x013f, 0x0000}, + {0x0142, 0x0000}, + {0x0145, 0x0002}, + {0x0146, 0x0000}, + {0x0147, 0x0000}, + {0x0148, 0x0000}, + {0x0149, 0x0000}, + {0x0150, 0x79a1}, + {0x0156, 0xaaaa}, + {0x0160, 0x4ec0}, + {0x0161, 0x0080}, + {0x0162, 0x0200}, + {0x0163, 0x0800}, + {0x0164, 0x0000}, + {0x0165, 0x0000}, + {0x0166, 0x0000}, + {0x0167, 0x000f}, + {0x0168, 0x000f}, + {0x0169, 0x0021}, + {0x0190, 0x413d}, + {0x0194, 0x0000}, + {0x0195, 0x0000}, + {0x0197, 0x0022}, + {0x0198, 0x0000}, + {0x0199, 0x0000}, + {0x01af, 0x0000}, + {0x01b0, 0x0400}, + {0x01b1, 0x0000}, + {0x01b2, 0x0000}, + {0x01b3, 0x0000}, + {0x01b4, 0x0000}, + {0x01b5, 0x0000}, + {0x01b6, 0x01c3}, + {0x01b7, 0x02a0}, + {0x01b8, 0x03e9}, + {0x01b9, 0x1389}, + {0x01ba, 0xc351}, + {0x01bb, 0x0009}, + {0x01bc, 0x0018}, + {0x01bd, 0x002a}, + {0x01be, 0x004c}, + {0x01bf, 0x0097}, + {0x01c0, 0x433d}, + {0x01c2, 0x0000}, + {0x01c3, 0x0000}, + {0x01c4, 0x0000}, + {0x01c5, 0x0000}, + {0x01c6, 0x0000}, + {0x01c7, 0x0000}, + {0x01c8, 0x40af}, + {0x01c9, 0x0702}, + {0x01ca, 0x0000}, + {0x01cb, 0x0000}, + {0x01cc, 0x5757}, + {0x01cd, 0x5757}, + {0x01ce, 0x5757}, + {0x01cf, 0x5757}, + {0x01d0, 0x5757}, + {0x01d1, 0x5757}, + {0x01d2, 0x5757}, + {0x01d3, 0x5757}, + {0x01d4, 0x5757}, + {0x01d5, 0x5757}, + {0x01d6, 0x0000}, + {0x01d7, 0x0008}, + {0x01d8, 0x0029}, + {0x01d9, 0x3333}, + {0x01da, 0x0000}, + {0x01db, 0x0004}, + {0x01dc, 0x0000}, + {0x01de, 0x7c00}, + {0x01df, 0x0320}, + {0x01e0, 0x06a1}, + {0x01e1, 0x0000}, + {0x01e2, 0x0000}, + {0x01e3, 0x0000}, + {0x01e4, 0x0000}, + {0x01e6, 0x0001}, + {0x01e7, 0x0000}, + {0x01e8, 0x0000}, + {0x01ea, 0x0000}, + {0x01eb, 0x0000}, + {0x01ec, 0x0000}, + {0x01ed, 0x0000}, + {0x01ee, 0x0000}, + {0x01ef, 0x0000}, + {0x01f0, 0x0000}, + {0x01f1, 0x0000}, + {0x01f2, 0x0000}, + {0x01f3, 0x0000}, + {0x01f4, 0x0000}, + {0x0210, 0x6297}, + {0x0211, 0xa005}, + {0x0212, 0x824c}, + {0x0213, 0xf7ff}, + {0x0214, 0xf24c}, + {0x0215, 0x0102}, + {0x0216, 0x00a3}, + {0x0217, 0x0048}, + {0x0218, 0xa2c0}, + {0x0219, 0x0400}, + {0x021a, 0x00c8}, + {0x021b, 0x00c0}, + {0x021c, 0x0000}, + {0x0250, 0x4500}, + {0x0251, 0x40b3}, + {0x0252, 0x0000}, + {0x0253, 0x0000}, + {0x0254, 0x0000}, + {0x0255, 0x0000}, + {0x0256, 0x0000}, + {0x0257, 0x0000}, + {0x0258, 0x0000}, + {0x0259, 0x0000}, + {0x025a, 0x0005}, + {0x0270, 0x0000}, + {0x02ff, 0x0110}, + {0x0300, 0x001f}, + {0x0301, 0x032c}, + {0x0302, 0x5f21}, + {0x0303, 0x4000}, + {0x0304, 0x4000}, + {0x0305, 0x06d5}, + {0x0306, 0x8000}, + {0x0307, 0x0700}, + {0x0310, 0x4560}, + {0x0311, 0xa4a8}, + {0x0312, 0x7418}, + {0x0313, 0x0000}, + {0x0314, 0x0006}, + {0x0315, 0xffff}, + {0x0316, 0xc400}, + {0x0317, 0x0000}, + {0x03c0, 0x7e00}, + {0x03c1, 0x8000}, + {0x03c2, 0x8000}, + {0x03c3, 0x8000}, + {0x03c4, 0x8000}, + {0x03c5, 0x8000}, + {0x03c6, 0x8000}, + {0x03c7, 0x8000}, + {0x03c8, 0x8000}, + {0x03c9, 0x8000}, + {0x03ca, 0x8000}, + {0x03cb, 0x8000}, + {0x03cc, 0x8000}, + {0x03d0, 0x0000}, + {0x03d1, 0x0000}, + {0x03d2, 0x0000}, + {0x03d3, 0x0000}, + {0x03d4, 0x2000}, + {0x03d5, 0x2000}, + {0x03d6, 0x0000}, + {0x03d7, 0x0000}, + {0x03d8, 0x2000}, + {0x03d9, 0x2000}, + {0x03da, 0x2000}, + {0x03db, 0x2000}, + {0x03dc, 0x0000}, + {0x03dd, 0x0000}, + {0x03de, 0x0000}, + {0x03df, 0x2000}, + {0x03e0, 0x0000}, + {0x03e1, 0x0000}, + {0x03e2, 0x0000}, + {0x03e3, 0x0000}, + {0x03e4, 0x0000}, + {0x03e5, 0x0000}, + {0x03e6, 0x0000}, + {0x03e7, 0x0000}, + {0x03e8, 0x0000}, + {0x03e9, 0x0000}, + {0x03ea, 0x0000}, + {0x03eb, 0x0000}, + {0x03ec, 0x0000}, + {0x03ed, 0x0000}, + {0x03ee, 0x0000}, + {0x03ef, 0x0000}, + {0x03f0, 0x0800}, + {0x03f1, 0x0800}, + {0x03f2, 0x0800}, + {0x03f3, 0x0800}, +}; +EXPORT_SYMBOL_GPL(rt5682_reg); + +bool rt5682_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5682_RESET: + case RT5682_CBJ_CTRL_2: + case RT5682_INT_ST_1: + case RT5682_4BTN_IL_CMD_1: + case RT5682_AJD1_CTRL: + case RT5682_HP_CALIB_CTRL_1: + case RT5682_DEVICE_ID: + case RT5682_I2C_MODE: + case RT5682_HP_CALIB_CTRL_10: + case RT5682_EFUSE_CTRL_2: + case RT5682_JD_TOP_VC_VTRL: + case RT5682_HP_IMP_SENS_CTRL_19: + case RT5682_IL_CMD_1: + case RT5682_SAR_IL_CMD_2: + case RT5682_SAR_IL_CMD_4: + case RT5682_SAR_IL_CMD_10: + case RT5682_SAR_IL_CMD_11: + case RT5682_EFUSE_CTRL_6...RT5682_EFUSE_CTRL_11: + case RT5682_HP_CALIB_STA_1...RT5682_HP_CALIB_STA_11: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_GPL(rt5682_volatile_register); + +bool rt5682_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5682_RESET: + case RT5682_VERSION_ID: + case RT5682_VENDOR_ID: + case RT5682_DEVICE_ID: + case RT5682_HP_CTRL_1: + case RT5682_HP_CTRL_2: + case RT5682_HPL_GAIN: + case RT5682_HPR_GAIN: + case RT5682_I2C_CTRL: + case RT5682_CBJ_BST_CTRL: + case RT5682_CBJ_CTRL_1: + case RT5682_CBJ_CTRL_2: + case RT5682_CBJ_CTRL_3: + case RT5682_CBJ_CTRL_4: + case RT5682_CBJ_CTRL_5: + case RT5682_CBJ_CTRL_6: + case RT5682_CBJ_CTRL_7: + case RT5682_DAC1_DIG_VOL: + case RT5682_STO1_ADC_DIG_VOL: + case RT5682_STO1_ADC_BOOST: + case RT5682_HP_IMP_GAIN_1: + case RT5682_HP_IMP_GAIN_2: + case RT5682_SIDETONE_CTRL: + case RT5682_STO1_ADC_MIXER: + case RT5682_AD_DA_MIXER: + case RT5682_STO1_DAC_MIXER: + case RT5682_A_DAC1_MUX: + case RT5682_DIG_INF2_DATA: + case RT5682_REC_MIXER: + case RT5682_CAL_REC: + case RT5682_ALC_BACK_GAIN: + case RT5682_PWR_DIG_1: + case RT5682_PWR_DIG_2: + case RT5682_PWR_ANLG_1: + case RT5682_PWR_ANLG_2: + case RT5682_PWR_ANLG_3: + case RT5682_PWR_MIXER: + case RT5682_PWR_VOL: + case RT5682_CLK_DET: + case RT5682_RESET_LPF_CTRL: + case RT5682_RESET_HPF_CTRL: + case RT5682_DMIC_CTRL_1: + case RT5682_I2S1_SDP: + case RT5682_I2S2_SDP: + case RT5682_ADDA_CLK_1: + case RT5682_ADDA_CLK_2: + case RT5682_I2S1_F_DIV_CTRL_1: + case RT5682_I2S1_F_DIV_CTRL_2: + case RT5682_TDM_CTRL: + case RT5682_TDM_ADDA_CTRL_1: + case RT5682_TDM_ADDA_CTRL_2: + case RT5682_DATA_SEL_CTRL_1: + case RT5682_TDM_TCON_CTRL: + case RT5682_GLB_CLK: + case RT5682_PLL_CTRL_1: + case RT5682_PLL_CTRL_2: + case RT5682_PLL_TRACK_1: + case RT5682_PLL_TRACK_2: + case RT5682_PLL_TRACK_3: + case RT5682_PLL_TRACK_4: + case RT5682_PLL_TRACK_5: + case RT5682_PLL_TRACK_6: + case RT5682_PLL_TRACK_11: + case RT5682_SDW_REF_CLK: + case RT5682_DEPOP_1: + case RT5682_DEPOP_2: + case RT5682_HP_CHARGE_PUMP_1: + case RT5682_HP_CHARGE_PUMP_2: + case RT5682_MICBIAS_1: + case RT5682_MICBIAS_2: + case RT5682_PLL_TRACK_12: + case RT5682_PLL_TRACK_14: + case RT5682_PLL2_CTRL_1: + case RT5682_PLL2_CTRL_2: + case RT5682_PLL2_CTRL_3: + case RT5682_PLL2_CTRL_4: + case RT5682_RC_CLK_CTRL: + case RT5682_I2S_M_CLK_CTRL_1: + case RT5682_I2S2_F_DIV_CTRL_1: + case RT5682_I2S2_F_DIV_CTRL_2: + case RT5682_EQ_CTRL_1: + case RT5682_EQ_CTRL_2: + case RT5682_IRQ_CTRL_1: + case RT5682_IRQ_CTRL_2: + case RT5682_IRQ_CTRL_3: + case RT5682_IRQ_CTRL_4: + case RT5682_INT_ST_1: + case RT5682_GPIO_CTRL_1: + case RT5682_GPIO_CTRL_2: + case RT5682_GPIO_CTRL_3: + case RT5682_HP_AMP_DET_CTRL_1: + case RT5682_HP_AMP_DET_CTRL_2: + case RT5682_MID_HP_AMP_DET: + case RT5682_LOW_HP_AMP_DET: + case RT5682_DELAY_BUF_CTRL: + case RT5682_SV_ZCD_1: + case RT5682_SV_ZCD_2: + case RT5682_IL_CMD_1: + case RT5682_IL_CMD_2: + case RT5682_IL_CMD_3: + case RT5682_IL_CMD_4: + case RT5682_IL_CMD_5: + case RT5682_IL_CMD_6: + case RT5682_4BTN_IL_CMD_1: + case RT5682_4BTN_IL_CMD_2: + case RT5682_4BTN_IL_CMD_3: + case RT5682_4BTN_IL_CMD_4: + case RT5682_4BTN_IL_CMD_5: + case RT5682_4BTN_IL_CMD_6: + case RT5682_4BTN_IL_CMD_7: + case RT5682_ADC_STO1_HP_CTRL_1: + case RT5682_ADC_STO1_HP_CTRL_2: + case RT5682_AJD1_CTRL: + case RT5682_JD1_THD: + case RT5682_JD2_THD: + case RT5682_JD_CTRL_1: + case RT5682_DUMMY_1: + case RT5682_DUMMY_2: + case RT5682_DUMMY_3: + case RT5682_DAC_ADC_DIG_VOL1: + case RT5682_BIAS_CUR_CTRL_2: + case RT5682_BIAS_CUR_CTRL_3: + case RT5682_BIAS_CUR_CTRL_4: + case RT5682_BIAS_CUR_CTRL_5: + case RT5682_BIAS_CUR_CTRL_6: + case RT5682_BIAS_CUR_CTRL_7: + case RT5682_BIAS_CUR_CTRL_8: + case RT5682_BIAS_CUR_CTRL_9: + case RT5682_BIAS_CUR_CTRL_10: + case RT5682_VREF_REC_OP_FB_CAP_CTRL: + case RT5682_CHARGE_PUMP_1: + case RT5682_DIG_IN_CTRL_1: + case RT5682_PAD_DRIVING_CTRL: + case RT5682_SOFT_RAMP_DEPOP: + case RT5682_CHOP_DAC: + case RT5682_CHOP_ADC: + case RT5682_CALIB_ADC_CTRL: + case RT5682_VOL_TEST: + case RT5682_SPKVDD_DET_STA: + case RT5682_TEST_MODE_CTRL_1: + case RT5682_TEST_MODE_CTRL_2: + case RT5682_TEST_MODE_CTRL_3: + case RT5682_TEST_MODE_CTRL_4: + case RT5682_TEST_MODE_CTRL_5: + case RT5682_PLL1_INTERNAL: + case RT5682_PLL2_INTERNAL: + case RT5682_STO_NG2_CTRL_1: + case RT5682_STO_NG2_CTRL_2: + case RT5682_STO_NG2_CTRL_3: + case RT5682_STO_NG2_CTRL_4: + case RT5682_STO_NG2_CTRL_5: + case RT5682_STO_NG2_CTRL_6: + case RT5682_STO_NG2_CTRL_7: + case RT5682_STO_NG2_CTRL_8: + case RT5682_STO_NG2_CTRL_9: + case RT5682_STO_NG2_CTRL_10: + case RT5682_STO1_DAC_SIL_DET: + case RT5682_SIL_PSV_CTRL1: + case RT5682_SIL_PSV_CTRL2: + case RT5682_SIL_PSV_CTRL3: + case RT5682_SIL_PSV_CTRL4: + case RT5682_SIL_PSV_CTRL5: + case RT5682_HP_IMP_SENS_CTRL_01: + case RT5682_HP_IMP_SENS_CTRL_02: + case RT5682_HP_IMP_SENS_CTRL_03: + case RT5682_HP_IMP_SENS_CTRL_04: + case RT5682_HP_IMP_SENS_CTRL_05: + case RT5682_HP_IMP_SENS_CTRL_06: + case RT5682_HP_IMP_SENS_CTRL_07: + case RT5682_HP_IMP_SENS_CTRL_08: + case RT5682_HP_IMP_SENS_CTRL_09: + case RT5682_HP_IMP_SENS_CTRL_10: + case RT5682_HP_IMP_SENS_CTRL_11: + case RT5682_HP_IMP_SENS_CTRL_12: + case RT5682_HP_IMP_SENS_CTRL_13: + case RT5682_HP_IMP_SENS_CTRL_14: + case RT5682_HP_IMP_SENS_CTRL_15: + case RT5682_HP_IMP_SENS_CTRL_16: + case RT5682_HP_IMP_SENS_CTRL_17: + case RT5682_HP_IMP_SENS_CTRL_18: + case RT5682_HP_IMP_SENS_CTRL_19: + case RT5682_HP_IMP_SENS_CTRL_20: + case RT5682_HP_IMP_SENS_CTRL_21: + case RT5682_HP_IMP_SENS_CTRL_22: + case RT5682_HP_IMP_SENS_CTRL_23: + case RT5682_HP_IMP_SENS_CTRL_24: + case RT5682_HP_IMP_SENS_CTRL_25: + case RT5682_HP_IMP_SENS_CTRL_26: + case RT5682_HP_IMP_SENS_CTRL_27: + case RT5682_HP_IMP_SENS_CTRL_28: + case RT5682_HP_IMP_SENS_CTRL_29: + case RT5682_HP_IMP_SENS_CTRL_30: + case RT5682_HP_IMP_SENS_CTRL_31: + case RT5682_HP_IMP_SENS_CTRL_32: + case RT5682_HP_IMP_SENS_CTRL_33: + case RT5682_HP_IMP_SENS_CTRL_34: + case RT5682_HP_IMP_SENS_CTRL_35: + case RT5682_HP_IMP_SENS_CTRL_36: + case RT5682_HP_IMP_SENS_CTRL_37: + case RT5682_HP_IMP_SENS_CTRL_38: + case RT5682_HP_IMP_SENS_CTRL_39: + case RT5682_HP_IMP_SENS_CTRL_40: + case RT5682_HP_IMP_SENS_CTRL_41: + case RT5682_HP_IMP_SENS_CTRL_42: + case RT5682_HP_IMP_SENS_CTRL_43: + case RT5682_HP_LOGIC_CTRL_1: + case RT5682_HP_LOGIC_CTRL_2: + case RT5682_HP_LOGIC_CTRL_3: + case RT5682_HP_CALIB_CTRL_1: + case RT5682_HP_CALIB_CTRL_2: + case RT5682_HP_CALIB_CTRL_3: + case RT5682_HP_CALIB_CTRL_4: + case RT5682_HP_CALIB_CTRL_5: + case RT5682_HP_CALIB_CTRL_6: + case RT5682_HP_CALIB_CTRL_7: + case RT5682_HP_CALIB_CTRL_9: + case RT5682_HP_CALIB_CTRL_10: + case RT5682_HP_CALIB_CTRL_11: + case RT5682_HP_CALIB_STA_1: + case RT5682_HP_CALIB_STA_2: + case RT5682_HP_CALIB_STA_3: + case RT5682_HP_CALIB_STA_4: + case RT5682_HP_CALIB_STA_5: + case RT5682_HP_CALIB_STA_6: + case RT5682_HP_CALIB_STA_7: + case RT5682_HP_CALIB_STA_8: + case RT5682_HP_CALIB_STA_9: + case RT5682_HP_CALIB_STA_10: + case RT5682_HP_CALIB_STA_11: + case RT5682_SAR_IL_CMD_1: + case RT5682_SAR_IL_CMD_2: + case RT5682_SAR_IL_CMD_3: + case RT5682_SAR_IL_CMD_4: + case RT5682_SAR_IL_CMD_5: + case RT5682_SAR_IL_CMD_6: + case RT5682_SAR_IL_CMD_7: + case RT5682_SAR_IL_CMD_8: + case RT5682_SAR_IL_CMD_9: + case RT5682_SAR_IL_CMD_10: + case RT5682_SAR_IL_CMD_11: + case RT5682_SAR_IL_CMD_12: + case RT5682_SAR_IL_CMD_13: + case RT5682_EFUSE_CTRL_1: + case RT5682_EFUSE_CTRL_2: + case RT5682_EFUSE_CTRL_3: + case RT5682_EFUSE_CTRL_4: + case RT5682_EFUSE_CTRL_5: + case RT5682_EFUSE_CTRL_6: + case RT5682_EFUSE_CTRL_7: + case RT5682_EFUSE_CTRL_8: + case RT5682_EFUSE_CTRL_9: + case RT5682_EFUSE_CTRL_10: + case RT5682_EFUSE_CTRL_11: + case RT5682_JD_TOP_VC_VTRL: + case RT5682_DRC1_CTRL_0: + case RT5682_DRC1_CTRL_1: + case RT5682_DRC1_CTRL_2: + case RT5682_DRC1_CTRL_3: + case RT5682_DRC1_CTRL_4: + case RT5682_DRC1_CTRL_5: + case RT5682_DRC1_CTRL_6: + case RT5682_DRC1_HARD_LMT_CTRL_1: + case RT5682_DRC1_HARD_LMT_CTRL_2: + case RT5682_DRC1_PRIV_1: + case RT5682_DRC1_PRIV_2: + case RT5682_DRC1_PRIV_3: + case RT5682_DRC1_PRIV_4: + case RT5682_DRC1_PRIV_5: + case RT5682_DRC1_PRIV_6: + case RT5682_DRC1_PRIV_7: + case RT5682_DRC1_PRIV_8: + case RT5682_EQ_AUTO_RCV_CTRL1: + case RT5682_EQ_AUTO_RCV_CTRL2: + case RT5682_EQ_AUTO_RCV_CTRL3: + case RT5682_EQ_AUTO_RCV_CTRL4: + case RT5682_EQ_AUTO_RCV_CTRL5: + case RT5682_EQ_AUTO_RCV_CTRL6: + case RT5682_EQ_AUTO_RCV_CTRL7: + case RT5682_EQ_AUTO_RCV_CTRL8: + case RT5682_EQ_AUTO_RCV_CTRL9: + case RT5682_EQ_AUTO_RCV_CTRL10: + case RT5682_EQ_AUTO_RCV_CTRL11: + case RT5682_EQ_AUTO_RCV_CTRL12: + case RT5682_EQ_AUTO_RCV_CTRL13: + case RT5682_ADC_L_EQ_LPF1_A1: + case RT5682_R_EQ_LPF1_A1: + case RT5682_L_EQ_LPF1_H0: + case RT5682_R_EQ_LPF1_H0: + case RT5682_L_EQ_BPF1_A1: + case RT5682_R_EQ_BPF1_A1: + case RT5682_L_EQ_BPF1_A2: + case RT5682_R_EQ_BPF1_A2: + case RT5682_L_EQ_BPF1_H0: + case RT5682_R_EQ_BPF1_H0: + case RT5682_L_EQ_BPF2_A1: + case RT5682_R_EQ_BPF2_A1: + case RT5682_L_EQ_BPF2_A2: + case RT5682_R_EQ_BPF2_A2: + case RT5682_L_EQ_BPF2_H0: + case RT5682_R_EQ_BPF2_H0: + case RT5682_L_EQ_BPF3_A1: + case RT5682_R_EQ_BPF3_A1: + case RT5682_L_EQ_BPF3_A2: + case RT5682_R_EQ_BPF3_A2: + case RT5682_L_EQ_BPF3_H0: + case RT5682_R_EQ_BPF3_H0: + case RT5682_L_EQ_BPF4_A1: + case RT5682_R_EQ_BPF4_A1: + case RT5682_L_EQ_BPF4_A2: + case RT5682_R_EQ_BPF4_A2: + case RT5682_L_EQ_BPF4_H0: + case RT5682_R_EQ_BPF4_H0: + case RT5682_L_EQ_HPF1_A1: + case RT5682_R_EQ_HPF1_A1: + case RT5682_L_EQ_HPF1_H0: + case RT5682_R_EQ_HPF1_H0: + case RT5682_L_EQ_PRE_VOL: + case RT5682_R_EQ_PRE_VOL: + case RT5682_L_EQ_POST_VOL: + case RT5682_R_EQ_POST_VOL: + case RT5682_I2C_MODE: + return true; + default: + return false; + } +} +EXPORT_SYMBOL_GPL(rt5682_readable_register); + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static const DECLARE_TLV_DB_RANGE(bst_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0) +); + +/* Interface data select */ +static const char * const rt5682_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682_if2_adc_enum, + RT5682_DIG_INF2_DATA, RT5682_IF2_ADC_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_01_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC1_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_23_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC2_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_45_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC3_SEL_SFT, rt5682_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682_if1_67_adc_enum, + RT5682_TDM_ADDA_CTRL_1, RT5682_IF1_ADC4_SEL_SFT, rt5682_data_select); + +static const struct snd_kcontrol_new rt5682_if2_adc_swap_mux = + SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5682_if2_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5682_if1_01_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5682_if1_23_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5682_if1_45_adc_enum); + +static const struct snd_kcontrol_new rt5682_if1_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682_if1_67_adc_enum); + +static const char * const rt5682_dac_select[] = { + "IF1", "SOUND" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682_dacl_enum, + RT5682_AD_DA_MIXER, RT5682_DAC1_L_SEL_SFT, rt5682_dac_select); + +static const struct snd_kcontrol_new rt5682_dac_l_mux = + SOC_DAPM_ENUM("DAC L Mux", rt5682_dacl_enum); + +static SOC_ENUM_SINGLE_DECL(rt5682_dacr_enum, + RT5682_AD_DA_MIXER, RT5682_DAC1_R_SEL_SFT, rt5682_dac_select); + +static const struct snd_kcontrol_new rt5682_dac_r_mux = + SOC_DAPM_ENUM("DAC R Mux", rt5682_dacr_enum); + +void rt5682_reset(struct rt5682_priv *rt5682) +{ + regmap_write(rt5682->regmap, RT5682_RESET, 0); + if (!rt5682->is_sdw) + regmap_write(rt5682->regmap, RT5682_I2C_MODE, 1); +} +EXPORT_SYMBOL_GPL(rt5682_reset); + +/** + * rt5682_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5682 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the component driver will turn on + * ASRC for these filters if ASRC is selected as their clock source. + */ +int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + switch (clk_src) { + case RT5682_CLK_SEL_SYS: + case RT5682_CLK_SEL_I2S1_ASRC: + case RT5682_CLK_SEL_I2S2_ASRC: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5682_DA_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5682_PLL_TRACK_2, + RT5682_FILTER_CLK_SEL_MASK, + clk_src << RT5682_FILTER_CLK_SEL_SFT); + } + + if (filter_mask & RT5682_AD_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5682_PLL_TRACK_3, + RT5682_FILTER_CLK_SEL_MASK, + clk_src << RT5682_FILTER_CLK_SEL_SFT); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5682_sel_asrc_clk_src); + +static int rt5682_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read(component, RT5682_4BTN_IL_CMD_1); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5682_4BTN_IL_CMD_1, val); + dev_dbg(component->dev, "%s btn_type=%x\n", __func__, btn_type); + snd_soc_component_update_bits(component, + RT5682_SAR_IL_CMD_2, 0x10, 0x10); + + return btn_type; +} + +static void rt5682_enable_push_button_irq(struct snd_soc_component *component, + bool enable) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + if (enable) { + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_EN); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13, + RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_BTN); + snd_soc_component_write(component, RT5682_IL_CMD_1, 0x0040); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_MASK | RT5682_4BTN_IL_RST_MASK, + RT5682_4BTN_IL_EN | RT5682_4BTN_IL_NOR); + if (rt5682->is_sdw) + snd_soc_component_update_bits(component, + RT5682_IRQ_CTRL_3, + RT5682_IL_IRQ_MASK | RT5682_IL_IRQ_TYPE_MASK, + RT5682_IL_IRQ_EN | RT5682_IL_IRQ_PUL); + else + snd_soc_component_update_bits(component, + RT5682_IRQ_CTRL_3, RT5682_IL_IRQ_MASK, + RT5682_IL_IRQ_EN); + } else { + snd_soc_component_update_bits(component, RT5682_IRQ_CTRL_3, + RT5682_IL_IRQ_MASK, RT5682_IL_IRQ_DIS); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK, RT5682_SAR_BUTT_DET_DIS); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_MASK, RT5682_4BTN_IL_DIS); + snd_soc_component_update_bits(component, RT5682_4BTN_IL_CMD_2, + RT5682_4BTN_IL_RST_MASK, RT5682_4BTN_IL_RST); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_13, + RT5682_SAR_SOUR_MASK, RT5682_SAR_SOUR_TYPE); + } +} + +/** + * rt5682_headset_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = &component->dapm; + unsigned int val, count; + + if (jack_insert) { + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, + RT5682_PWR_VREF2 | RT5682_PWR_MB, + RT5682_PWR_VREF2 | RT5682_PWR_MB); + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0); + usleep_range(15000, 20000); + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, RT5682_PWR_FV2); + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3, + RT5682_PWR_CBJ, RT5682_PWR_CBJ); + snd_soc_component_update_bits(component, + RT5682_HP_CHARGE_PUMP_1, + RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0); + rt5682_enable_push_button_irq(component, false); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW); + usleep_range(55000, 60000); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_HIGH); + + count = 0; + val = snd_soc_component_read(component, RT5682_CBJ_CTRL_2) + & RT5682_JACK_TYPE_MASK; + while (val == 0 && count < 50) { + usleep_range(10000, 15000); + val = snd_soc_component_read(component, + RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK; + count++; + } + + switch (val) { + case 0x1: + case 0x2: + rt5682->jack_type = SND_JACK_HEADSET; + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_FAST_OFF_MASK, RT5682_FAST_OFF_EN); + rt5682_enable_push_button_irq(component, true); + break; + default: + rt5682->jack_type = SND_JACK_HEADPHONE; + break; + } + + snd_soc_component_update_bits(component, + RT5682_HP_CHARGE_PUMP_1, + RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, + RT5682_OSW_L_EN | RT5682_OSW_R_EN); + snd_soc_component_update_bits(component, RT5682_MICBIAS_2, + RT5682_PWR_CLK25M_MASK | RT5682_PWR_CLK1M_MASK, + RT5682_PWR_CLK25M_PU | RT5682_PWR_CLK1M_PU); + } else { + rt5682_enable_push_button_irq(component, false); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW); + if (!snd_soc_dapm_get_pin_status(dapm, "MICBIAS") && + !snd_soc_dapm_get_pin_status(dapm, "PLL1") && + !snd_soc_dapm_get_pin_status(dapm, "PLL2B")) + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_MB, 0); + if (!snd_soc_dapm_get_pin_status(dapm, "Vref2") && + !snd_soc_dapm_get_pin_status(dapm, "PLL1") && + !snd_soc_dapm_get_pin_status(dapm, "PLL2B")) + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_VREF2, 0); + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3, + RT5682_PWR_CBJ, 0); + snd_soc_component_update_bits(component, RT5682_MICBIAS_2, + RT5682_PWR_CLK25M_MASK | RT5682_PWR_CLK1M_MASK, + RT5682_PWR_CLK25M_PD | RT5682_PWR_CLK1M_PD); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_FAST_OFF_MASK, RT5682_FAST_OFF_DIS); + + rt5682->jack_type = 0; + } + + dev_dbg(component->dev, "jack_type = %d\n", rt5682->jack_type); + return rt5682->jack_type; +} + +static int rt5682_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682->hs_jack = hs_jack; + + if (rt5682->is_sdw && !rt5682->first_hw_init) + return 0; + + if (!hs_jack) { + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK, RT5682_JD1_DIS); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_JDH | RT5682_POW_JDL, 0); + cancel_delayed_work_sync(&rt5682->jack_detect_work); + + return 0; + } + + if (!rt5682->is_sdw) { + switch (rt5682->pdata.jd_src) { + case RT5682_JD1: + snd_soc_component_update_bits(component, + RT5682_CBJ_CTRL_5, 0x0700, 0x0600); + snd_soc_component_update_bits(component, + RT5682_CBJ_CTRL_2, RT5682_EXT_JD_SRC, + RT5682_EXT_JD_SRC_MANUAL); + snd_soc_component_write(component, RT5682_CBJ_CTRL_1, + 0xd142); + snd_soc_component_update_bits(component, + RT5682_CBJ_CTRL_3, RT5682_CBJ_IN_BUF_EN, + RT5682_CBJ_IN_BUF_EN); + snd_soc_component_update_bits(component, + RT5682_SAR_IL_CMD_1, RT5682_SAR_POW_MASK, + RT5682_SAR_POW_EN); + regmap_update_bits(rt5682->regmap, RT5682_GPIO_CTRL_1, + RT5682_GP1_PIN_MASK, RT5682_GP1_PIN_IRQ); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_IRQ | RT5682_POW_JDH | + RT5682_POW_ANA, RT5682_POW_IRQ | + RT5682_POW_JDH | RT5682_POW_ANA); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_2, + RT5682_PWR_JDH, RT5682_PWR_JDH); + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK | RT5682_JD1_POL_MASK, + RT5682_JD1_EN | RT5682_JD1_POL_NOR); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_4, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_5, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_6, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + regmap_update_bits(rt5682->regmap, RT5682_4BTN_IL_CMD_7, + 0x7f7f, (rt5682->pdata.btndet_delay << 8 | + rt5682->pdata.btndet_delay)); + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, + msecs_to_jiffies(250)); + break; + + case RT5682_JD_NULL: + regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2, + RT5682_JD1_EN_MASK, RT5682_JD1_DIS); + regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL, + RT5682_POW_JDH | RT5682_POW_JDL, 0); + break; + + default: + dev_warn(component->dev, "Wrong JD source\n"); + break; + } + } + + return 0; +} + +void rt5682_jack_detect_handler(struct work_struct *work) +{ + struct rt5682_priv *rt5682 = + container_of(work, struct rt5682_priv, jack_detect_work.work); + struct snd_soc_dapm_context *dapm; + int val, btn_type; + + if (!rt5682->component || + !snd_soc_card_is_instantiated(rt5682->component->card)) { + /* card not yet ready, try later */ + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(15)); + return; + } + + if (rt5682->is_sdw) { + if (pm_runtime_status_suspended(rt5682->slave->dev.parent)) { + dev_dbg(&rt5682->slave->dev, + "%s: parent device is pm_runtime_status_suspended, skipping jack detection\n", + __func__); + return; + } + } + + dapm = snd_soc_component_get_dapm(rt5682->component); + + snd_soc_dapm_mutex_lock(dapm); + mutex_lock(&rt5682->calibrate_mutex); + + val = snd_soc_component_read(rt5682->component, RT5682_AJD1_CTRL) + & RT5682_JDH_RS_MASK; + if (!val) { + /* jack in */ + if (rt5682->jack_type == 0) { + /* jack was out, report jack type */ + rt5682->jack_type = + rt5682_headset_detect(rt5682->component, 1); + rt5682->irq_work_delay_time = 0; + } else if ((rt5682->jack_type & SND_JACK_HEADSET) == + SND_JACK_HEADSET) { + /* jack is already in, report button event */ + rt5682->jack_type = SND_JACK_HEADSET; + btn_type = rt5682_button_detect(rt5682->component); + /** + * rt5682 can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + rt5682->jack_type |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + rt5682->jack_type |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + rt5682->jack_type |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + rt5682->jack_type |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + dev_err(rt5682->component->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + } else { + /* jack out */ + rt5682->jack_type = rt5682_headset_detect(rt5682->component, 0); + rt5682->irq_work_delay_time = 50; + } + + mutex_unlock(&rt5682->calibrate_mutex); + snd_soc_dapm_mutex_unlock(dapm); + + snd_soc_jack_report(rt5682->hs_jack, rt5682->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (!rt5682->is_sdw) { + if (rt5682->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5682->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5682->jd_check_work); + } +} +EXPORT_SYMBOL_GPL(rt5682_jack_detect_handler); + +static const struct snd_kcontrol_new rt5682_snd_controls[] = { + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682_DAC1_DIG_VOL, + RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 87, 0, dac_vol_tlv), + + /* IN Boost Volume */ + SOC_SINGLE_TLV("CBJ Boost Volume", RT5682_CBJ_BST_CTRL, + RT5682_BST_CBJ_SFT, 8, 0, bst_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("STO1 ADC Capture Switch", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_MUTE_SFT, RT5682_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 63, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682_STO1_ADC_BOOST, + RT5682_STO1_ADC_L_BST_SFT, RT5682_STO1_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), +}; + +static int rt5682_div_sel(struct rt5682_priv *rt5682, + int target, const int div[], int size) +{ + int i; + + if (rt5682->sysclk < target) { + dev_err(rt5682->component->dev, + "sysclk rate %d is too low\n", rt5682->sysclk); + return 0; + } + + for (i = 0; i < size - 1; i++) { + dev_dbg(rt5682->component->dev, "div[%d]=%d\n", i, div[i]); + if (target * div[i] == rt5682->sysclk) + return i; + if (target * div[i + 1] > rt5682->sysclk) { + dev_dbg(rt5682->component->dev, + "can't find div for sysclk %d\n", + rt5682->sysclk); + return i; + } + } + + if (target * div[i] < rt5682->sysclk) + dev_err(rt5682->component->dev, + "sysclk rate %d is too high\n", rt5682->sysclk); + + return size - 1; +} + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + int idx, dmic_clk_rate = 3072000; + static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128}; + + if (rt5682->pdata.dmic_clk_rate) + dmic_clk_rate = rt5682->pdata.dmic_clk_rate; + + idx = rt5682_div_sel(rt5682, dmic_clk_rate, div, ARRAY_SIZE(div)); + + snd_soc_component_update_bits(component, RT5682_DMIC_CTRL_1, + RT5682_DMIC_CLK_MASK, idx << RT5682_DMIC_CLK_SFT); + + return 0; +} + +static int set_filter_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + int ref, val, reg, idx; + static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48}; + static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48}; + + if (rt5682->is_sdw) + return 0; + + val = snd_soc_component_read(component, RT5682_GPIO_CTRL_1) & + RT5682_GP4_PIN_MASK; + if (w->shift == RT5682_PWR_ADC_S1F_BIT && + val == RT5682_GP4_PIN_ADCDAT2) + ref = 256 * rt5682->lrck[RT5682_AIF2]; + else + ref = 256 * rt5682->lrck[RT5682_AIF1]; + + idx = rt5682_div_sel(rt5682, ref, div_f, ARRAY_SIZE(div_f)); + + if (w->shift == RT5682_PWR_ADC_S1F_BIT) + reg = RT5682_PLL_TRACK_3; + else + reg = RT5682_PLL_TRACK_2; + + snd_soc_component_update_bits(component, reg, + RT5682_FILTER_CLK_DIV_MASK, idx << RT5682_FILTER_CLK_DIV_SFT); + + /* select over sample rate */ + for (idx = 0; idx < ARRAY_SIZE(div_o); idx++) { + if (rt5682->sysclk <= 12288000 * div_o[idx]) + break; + } + + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1, + RT5682_ADC_OSR_MASK | RT5682_DAC_OSR_MASK, + (idx << RT5682_ADC_OSR_SFT) | (idx << RT5682_DAC_OSR_SFT)); + + return 0; +} + +static int is_sys_clk_from_pll1(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read(component, RT5682_GLB_CLK); + val &= RT5682_SCLK_SRC_MASK; + if (val == RT5682_SCLK_SRC_PLL1) + return 1; + else + return 0; +} + +static int is_sys_clk_from_pll2(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + val = snd_soc_component_read(component, RT5682_GLB_CLK); + val &= RT5682_SCLK_SRC_MASK; + if (val == RT5682_SCLK_SRC_PLL2) + return 1; + else + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, shift, val; + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case RT5682_ADC_STO1_ASRC_SFT: + reg = RT5682_PLL_TRACK_3; + shift = RT5682_FILTER_CLK_SEL_SFT; + break; + case RT5682_DAC_STO1_ASRC_SFT: + reg = RT5682_PLL_TRACK_2; + shift = RT5682_FILTER_CLK_SEL_SFT; + break; + default: + return 0; + } + + val = (snd_soc_component_read(component, reg) >> shift) & 0xf; + switch (val) { + case RT5682_CLK_SEL_I2S1_ASRC: + case RT5682_CLK_SEL_I2S2_ASRC: + return 1; + default: + return 0; + } +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5682_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5682_STO1_ADC_MIXER, + RT5682_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER, + RT5682_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER, + RT5682_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682_AD_DA_MIXER, + RT5682_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5682_AD_DA_MIXER, + RT5682_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_L1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_L1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5682_STO1_DAC_MIXER, + RT5682_M_DAC_R1_STO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5682_rec1_l_mix[] = { + SOC_DAPM_SINGLE("CBJ Switch", RT5682_REC_MIXER, + RT5682_M_CBJ_RM1_L_SFT, 1, 1), +}; + +/* STO1 ADC1 Source */ +/* MX-26 [13] [5] */ +static const char * const rt5682_sto1_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc1l_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC1L_SRC_SFT, rt5682_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc1l_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc1r_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC1R_SRC_SFT, rt5682_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc1r_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682_sto1_adc1r_enum); + +/* STO1 ADC Source */ +/* MX-26 [11:10] [3:2] */ +static const char * const rt5682_sto1_adc_src[] = { + "ADC1 L", "ADC1 R" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adcl_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADCL_SRC_SFT, rt5682_sto1_adc_src); + +static const struct snd_kcontrol_new rt5682_sto1_adcl_mux = + SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5682_sto1_adcl_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adcr_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADCR_SRC_SFT, rt5682_sto1_adc_src); + +static const struct snd_kcontrol_new rt5682_sto1_adcr_mux = + SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5682_sto1_adcr_enum); + +/* STO1 ADC2 Source */ +/* MX-26 [12] [4] */ +static const char * const rt5682_sto1_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc2l_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC2L_SRC_SFT, rt5682_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc2l_mux = + SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5682_sto1_adc2l_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_sto1_adc2r_enum, RT5682_STO1_ADC_MIXER, + RT5682_STO1_ADC2R_SRC_SFT, rt5682_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5682_sto1_adc2r_mux = + SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5682_sto1_adc2r_enum); + +/* MX-79 [6:4] I2S1 ADC data location */ +static const unsigned int rt5682_if1_adc_slot_values[] = { + 0, + 2, + 4, + 6, +}; + +static const char * const rt5682_if1_adc_slot_src[] = { + "Slot 0", "Slot 2", "Slot 4", "Slot 6" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_if1_adc_slot_enum, + RT5682_TDM_CTRL, RT5682_TDM_ADC_LCA_SFT, RT5682_TDM_ADC_LCA_MASK, + rt5682_if1_adc_slot_src, rt5682_if1_adc_slot_values); + +static const struct snd_kcontrol_new rt5682_if1_adc_slot_mux = + SOC_DAPM_ENUM("IF1 ADC Slot location", rt5682_if1_adc_slot_enum); + +/* Analog DAC L1 Source, Analog DAC R1 Source*/ +/* MX-2B [4], MX-2B [0]*/ +static const char * const rt5682_alg_dac1_src[] = { + "Stereo1 DAC Mixer", "DAC1" +}; + +static SOC_ENUM_SINGLE_DECL( + rt5682_alg_dac_l1_enum, RT5682_A_DAC1_MUX, + RT5682_A_DACL1_SFT, rt5682_alg_dac1_src); + +static const struct snd_kcontrol_new rt5682_alg_dac_l1_mux = + SOC_DAPM_ENUM("Analog DAC L1 Source", rt5682_alg_dac_l1_enum); + +static SOC_ENUM_SINGLE_DECL( + rt5682_alg_dac_r1_enum, RT5682_A_DAC1_MUX, + RT5682_A_DACR1_SFT, rt5682_alg_dac1_src); + +static const struct snd_kcontrol_new rt5682_alg_dac_r1_mux = + SOC_DAPM_ENUM("Analog DAC R1 Source", rt5682_alg_dac_r1_enum); + +/* Out Switch */ +static const struct snd_kcontrol_new hpol_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1, + RT5682_L_MUTE_SFT, 1, 1); +static const struct snd_kcontrol_new hpor_switch = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1, + RT5682_R_MUTE_SFT, 1, 1); + +static int rt5682_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, RT5682_HP_CTRL_2, + RT5682_HP_C2_DAC_AMP_MUTE, 0); + snd_soc_component_update_bits(component, RT5682_HP_LOGIC_CTRL_2, + RT5682_HP_LC2_SIG_SOUR2_MASK, RT5682_HP_LC2_SIG_SOUR2_REG); + snd_soc_component_update_bits(component, + RT5682_DEPOP_1, 0x60, 0x60); + snd_soc_component_update_bits(component, + RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080); + snd_soc_component_update_bits(component, RT5682_HP_CTRL_2, + RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN, + RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN); + usleep_range(5000, 10000); + snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1, + RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_L); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, RT5682_HP_CTRL_2, + RT5682_HP_C2_DAC_L_EN | RT5682_HP_C2_DAC_R_EN, 0); + snd_soc_component_update_bits(component, RT5682_CHARGE_PUMP_1, + RT5682_CP_SW_SIZE_MASK, RT5682_CP_SW_SIZE_M); + snd_soc_component_update_bits(component, + RT5682_DEPOP_1, 0x60, 0x0); + snd_soc_component_update_bits(component, + RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0000); + break; + } + + return 0; +} + +static int set_dmic_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int delay = 50, val; + + if (rt5682->pdata.dmic_delay) + delay = rt5682->pdata.dmic_delay; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read(component, RT5682_GLB_CLK); + val &= RT5682_SCLK_SRC_MASK; + if (val == RT5682_SCLK_SRC_PLL1 || val == RT5682_SCLK_SRC_PLL2) + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, + RT5682_PWR_VREF2 | RT5682_PWR_MB, + RT5682_PWR_VREF2 | RT5682_PWR_MB); + + /*Add delay to avoid pop noise*/ + msleep(delay); + break; + + case SND_SOC_DAPM_POST_PMD: + if (!rt5682->jack_type) { + if (!snd_soc_dapm_get_pin_status(w->dapm, "MICBIAS")) + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_MB, 0); + if (!snd_soc_dapm_get_pin_status(w->dapm, "Vref2")) + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_VREF2, 0); + } + break; + } + + return 0; +} + +static int rt5682_set_verf(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (w->shift) { + case RT5682_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV1, 0); + break; + + case RT5682_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, 0); + break; + } + break; + + case SND_SOC_DAPM_POST_PMU: + usleep_range(15000, 20000); + switch (w->shift) { + case RT5682_PWR_VREF1_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV1, + RT5682_PWR_FV1); + break; + + case RT5682_PWR_VREF2_BIT: + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_FV2, + RT5682_PWR_FV2); + break; + } + break; + } + + return 0; +} + +static const unsigned int rt5682_adcdat_pin_values[] = { + 1, + 3, +}; + +static const char * const rt5682_adcdat_pin_select[] = { + "ADCDAT1", + "ADCDAT2", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_adcdat_pin_enum, + RT5682_GPIO_CTRL_1, RT5682_GP4_PIN_SFT, RT5682_GP4_PIN_MASK, + rt5682_adcdat_pin_select, rt5682_adcdat_pin_values); + +static const struct snd_kcontrol_new rt5682_adcdat_pin_ctrl = + SOC_DAPM_ENUM("ADCDAT", rt5682_adcdat_pin_enum); + +static const unsigned int rt5682_hpo_sig_out_values[] = { + 2, + 7, +}; + +static const char * const rt5682_hpo_sig_out_mode[] = { + "Legacy", + "OneBit", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682_hpo_sig_out_enum, + RT5682_HP_LOGIC_CTRL_2, 0, RT5682_HP_LC2_SIG_SOUR1_MASK, + rt5682_hpo_sig_out_mode, rt5682_hpo_sig_out_values); + +static const struct snd_kcontrol_new rt5682_hpo_sig_demux = + SOC_DAPM_ENUM("HPO Signal Demux", rt5682_hpo_sig_out_enum); + +static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO2", RT5682_PWR_ANLG_3, RT5682_PWR_LDO2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL1", RT5682_PWR_ANLG_3, RT5682_PWR_PLL_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2B", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2B_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL2F", RT5682_PWR_ANLG_3, RT5682_PWR_PLL2F_BIT, + 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("Vref1", RT5682_PWR_ANLG_1, RT5682_PWR_VREF1_BIT, 0, + rt5682_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("Vref2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DAC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_AD_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DA_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5682_PLL_TRACK_1, + RT5682_DMIC_ASRC_SFT, 0, NULL, 0), + + /* Input Side */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5682_PWR_ANLG_2, RT5682_PWR_MB1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5682_PWR_ANLG_2, RT5682_PWR_MB2_BIT, + 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + + SND_SOC_DAPM_INPUT("IN1P"), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5682_DMIC_CTRL_1, + RT5682_DMIC_1_EN_SFT, 0, set_dmic_power, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682_rec1_l_mix, + ARRAY_SIZE(rt5682_rec1_l_mix)), + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5682_PWR_ANLG_2, + RT5682_PWR_RM1_L_BIT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5682_PWR_DIG_1, + RT5682_PWR_ADC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5682_PWR_DIG_1, + RT5682_PWR_ADC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5682_CHOP_ADC, + RT5682_CKGEN_ADC1_SFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adcl_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5682_sto1_adcr_mux), + SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_adc_slot_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682_PWR_DIG_2, + RT5682_PWR_ADC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", RT5682_STO1_ADC_DIG_VOL, + RT5682_L_MUTE_SFT, 1, rt5682_sto1_adc_l_mix, + ARRAY_SIZE(rt5682_sto1_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682_STO1_ADC_DIG_VOL, + RT5682_R_MUTE_SFT, 1, rt5682_sto1_adc_r_mix, + ARRAY_SIZE(rt5682_sto1_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5682_PWR_DIG_1, RT5682_PWR_I2S1_BIT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5682_PWR_DIG_1, RT5682_PWR_I2S2_BIT, + 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SOUND DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SOUND DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682_if2_adc_swap_mux), + + SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, + &rt5682_adcdat_pin_ctrl), + + SND_SOC_DAPM_MUX("DAC L Mux", SND_SOC_NOPM, 0, 0, + &rt5682_dac_l_mux), + SND_SOC_DAPM_MUX("DAC R Mux", SND_SOC_NOPM, 0, 0, + &rt5682_dac_r_mux), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, + RT5682_I2S1_SDP, RT5682_SEL_ADCDAT_SFT, 1), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, + RT5682_I2S2_SDP, RT5682_I2S2_PIN_CFG_SFT, 1), + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDWRX", "SDW Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SDWTX", "SDW Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5682_dac_l_mix, ARRAY_SIZE(rt5682_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5682_dac_r_mix, ARRAY_SIZE(rt5682_dac_r_mix)), + + /* DAC channel Mux */ + SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, + &rt5682_alg_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, + &rt5682_alg_dac_r1_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5682_PWR_DIG_2, + RT5682_PWR_DAC_S1F_BIT, 0, set_filter_clk, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5682_sto1_dac_l_mix, ARRAY_SIZE(rt5682_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5682_sto1_dac_r_mix, ARRAY_SIZE(rt5682_sto1_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5682_PWR_DIG_1, + RT5682_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5682_PWR_DIG_1, + RT5682_PWR_DAC_R1_BIT, 0), + SND_SOC_DAPM_SUPPLY_S("DAC 1 Clock", 3, RT5682_CHOP_DAC, + RT5682_CKGEN_DAC1_SFT, 0, NULL, 0), + + /* HPO */ + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5682_hp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("HP Amp L", RT5682_PWR_ANLG_1, + RT5682_PWR_HA_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1, + RT5682_PWR_HA_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1, + RT5682_PUMP_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1, + RT5682_CAPLESS_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("HPOL Playback", SND_SOC_NOPM, 0, 0, + &hpol_switch), + SND_SOC_DAPM_SWITCH("HPOR Playback", SND_SOC_NOPM, 0, 0, + &hpor_switch), + + SND_SOC_DAPM_OUT_DRV("HPO Legacy", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HPO OneBit", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_DEMUX("HPO Signal Demux", SND_SOC_NOPM, 0, 0, &rt5682_hpo_sig_demux), + + /* CLK DET */ + SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682_CLK_DET, + RT5682_SYS_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5682_CLK_DET, + RT5682_PLL1_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL2", RT5682_CLK_DET, + RT5682_PLL2_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET", RT5682_CLK_DET, + RT5682_POW_CLK_DET_SFT, 0, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { + /*PLL*/ + {"ADC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + {"ADC Stereo1 Filter", NULL, "PLL2B", is_sys_clk_from_pll2}, + {"ADC Stereo1 Filter", NULL, "PLL2F", is_sys_clk_from_pll2}, + {"DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll1}, + {"DAC Stereo1 Filter", NULL, "PLL2B", is_sys_clk_from_pll2}, + {"DAC Stereo1 Filter", NULL, "PLL2F", is_sys_clk_from_pll2}, + + /*ASRC*/ + {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, + {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, + {"ADC STO1 ASRC", NULL, "AD ASRC"}, + {"ADC STO1 ASRC", NULL, "DA ASRC"}, + {"ADC STO1 ASRC", NULL, "CLKDET"}, + {"DAC STO1 ASRC", NULL, "AD ASRC"}, + {"DAC STO1 ASRC", NULL, "DA ASRC"}, + {"DAC STO1 ASRC", NULL, "CLKDET"}, + + /*Vref*/ + {"MICBIAS1", NULL, "Vref1"}, + {"MICBIAS2", NULL, "Vref1"}, + + {"CLKDET SYS", NULL, "CLKDET"}, + + {"BST1 CBJ", NULL, "IN1P"}, + + {"RECMIX1L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX1L", NULL, "RECMIX1L Power"}, + + {"ADC1 L", NULL, "RECMIX1L"}, + {"ADC1 L", NULL, "ADC1 L Power"}, + {"ADC1 L", NULL, "ADC1 clock"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC CLK", NULL, "DMIC ASRC"}, + + {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"}, + + {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"}, + {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"}, + {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"}, + {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"}, + {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, + + {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + + {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"}, + {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"}, + {"AIF1TX", NULL, "I2S1"}, + {"AIF1TX", NULL, "ADCDAT Mux"}, + {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"}, + {"AIF2TX", NULL, "ADCDAT Mux"}, + + {"SDWTX", NULL, "PLL2B"}, + {"SDWTX", NULL, "PLL2F"}, + {"SDWTX", NULL, "ADCDAT Mux"}, + + {"IF1 DAC1 L", NULL, "AIF1RX"}, + {"IF1 DAC1 L", NULL, "I2S1"}, + {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"}, + {"IF1 DAC1 R", NULL, "AIF1RX"}, + {"IF1 DAC1 R", NULL, "I2S1"}, + {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"}, + + {"SOUND DAC L", NULL, "SDWRX"}, + {"SOUND DAC L", NULL, "DAC Stereo1 Filter"}, + {"SOUND DAC L", NULL, "PLL2B"}, + {"SOUND DAC L", NULL, "PLL2F"}, + {"SOUND DAC R", NULL, "SDWRX"}, + {"SOUND DAC R", NULL, "DAC Stereo1 Filter"}, + {"SOUND DAC R", NULL, "PLL2B"}, + {"SOUND DAC R", NULL, "PLL2F"}, + + {"DAC L Mux", "IF1", "IF1 DAC1 L"}, + {"DAC L Mux", "SOUND", "SOUND DAC L"}, + {"DAC R Mux", "IF1", "IF1 DAC1 R"}, + {"DAC R Mux", "SOUND", "SOUND DAC R"}, + + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC1 MIXL", "DAC1 Switch", "DAC L Mux"}, + {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC1 MIXR", "DAC1 Switch", "DAC R Mux"}, + + {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + + {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + + {"DAC L1 Source", "DAC1", "DAC1 MIXL"}, + {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC R1 Source", "DAC1", "DAC1 MIXR"}, + {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + + {"DAC L1", NULL, "DAC L1 Source"}, + {"DAC R1", NULL, "DAC R1 Source"}, + + {"DAC L1", NULL, "DAC 1 Clock"}, + {"DAC R1", NULL, "DAC 1 Clock"}, + + {"HP Amp", NULL, "DAC L1"}, + {"HP Amp", NULL, "DAC R1"}, + {"HP Amp", NULL, "HP Amp L"}, + {"HP Amp", NULL, "HP Amp R"}, + {"HP Amp", NULL, "Capless"}, + {"HP Amp", NULL, "Charge Pump"}, + {"HP Amp", NULL, "CLKDET SYS"}, + {"HP Amp", NULL, "Vref1"}, + + {"HPO Signal Demux", NULL, "HP Amp"}, + + {"HPO Legacy", "Legacy", "HPO Signal Demux"}, + {"HPO OneBit", "OneBit", "HPO Signal Demux"}, + + {"HPOL Playback", "Switch", "HPO Legacy"}, + {"HPOR Playback", "Switch", "HPO Legacy"}, + + {"HPOL", NULL, "HPOL Playback"}, + {"HPOR", NULL, "HPOR Playback"}, + {"HPOL", NULL, "HPO OneBit"}, + {"HPOR", NULL, "HPO OneBit"}, +}; + +static int rt5682_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int cl, val = 0; + + if (tx_mask || rx_mask) + snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2, + RT5682_TDM_EN, RT5682_TDM_EN); + else + snd_soc_component_update_bits(component, RT5682_TDM_ADDA_CTRL_2, + RT5682_TDM_EN, 0); + + switch (slots) { + case 4: + val |= RT5682_TDM_TX_CH_4; + val |= RT5682_TDM_RX_CH_4; + break; + case 6: + val |= RT5682_TDM_TX_CH_6; + val |= RT5682_TDM_RX_CH_6; + break; + case 8: + val |= RT5682_TDM_TX_CH_8; + val |= RT5682_TDM_RX_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682_TDM_CTRL, + RT5682_TDM_TX_CH_MASK | RT5682_TDM_RX_CH_MASK, val); + + switch (slot_width) { + case 8: + if (tx_mask || rx_mask) + return -EINVAL; + cl = RT5682_I2S1_TX_CHL_8 | RT5682_I2S1_RX_CHL_8; + break; + case 16: + val = RT5682_TDM_CL_16; + cl = RT5682_I2S1_TX_CHL_16 | RT5682_I2S1_RX_CHL_16; + break; + case 20: + val = RT5682_TDM_CL_20; + cl = RT5682_I2S1_TX_CHL_20 | RT5682_I2S1_RX_CHL_20; + break; + case 24: + val = RT5682_TDM_CL_24; + cl = RT5682_I2S1_TX_CHL_24 | RT5682_I2S1_RX_CHL_24; + break; + case 32: + val = RT5682_TDM_CL_32; + cl = RT5682_I2S1_TX_CHL_32 | RT5682_I2S1_RX_CHL_32; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_CL_MASK, val); + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S1_TX_CHL_MASK | RT5682_I2S1_RX_CHL_MASK, cl); + + return 0; +} + +static int rt5682_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int len_1 = 0, len_2 = 0; + int pre_div, frame_size; + + rt5682->lrck[dai->id] = params_rate(params); + pre_div = rl6231_get_clk_info(rt5682->sysclk, rt5682->lrck[dai->id]); + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", + frame_size); + return -EINVAL; + } + + dev_dbg(dai->dev, "lrck is %dHz and pre_div is %d for iis %d\n", + rt5682->lrck[dai->id], pre_div, dai->id); + + switch (params_width(params)) { + case 16: + break; + case 20: + len_1 |= RT5682_I2S1_DL_20; + len_2 |= RT5682_I2S2_DL_20; + break; + case 24: + len_1 |= RT5682_I2S1_DL_24; + len_2 |= RT5682_I2S2_DL_24; + break; + case 32: + len_1 |= RT5682_I2S1_DL_32; + len_2 |= RT5682_I2S2_DL_24; + break; + case 8: + len_1 |= RT5682_I2S2_DL_8; + len_2 |= RT5682_I2S2_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5682_AIF1: + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S1_DL_MASK, len_1); + if (rt5682->master[RT5682_AIF1]) { + snd_soc_component_update_bits(component, + RT5682_ADDA_CLK_1, RT5682_I2S_M_DIV_MASK | + RT5682_I2S_CLK_SRC_MASK, + pre_div << RT5682_I2S_M_DIV_SFT | + (rt5682->sysclk_src) << RT5682_I2S_CLK_SRC_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK, + RT5682_I2S1_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5682_I2S1_SDP, RT5682_I2S1_MONO_MASK, + RT5682_I2S1_MONO_DIS); + break; + case RT5682_AIF2: + snd_soc_component_update_bits(component, RT5682_I2S2_SDP, + RT5682_I2S2_DL_MASK, len_2); + if (rt5682->master[RT5682_AIF2]) { + snd_soc_component_update_bits(component, + RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_M_PD_MASK, + pre_div << RT5682_I2S2_M_PD_SFT); + } + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, + RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK, + RT5682_I2S2_MONO_EN); + else + snd_soc_component_update_bits(component, + RT5682_I2S2_SDP, RT5682_I2S2_MONO_MASK, + RT5682_I2S2_MONO_DIS); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5682_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, tdm_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5682->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rt5682->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5682_I2S_BP_INV; + tdm_ctrl |= RT5682_TDM_S_BP_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + if (dai->id == RT5682_AIF1) + tdm_ctrl |= RT5682_TDM_S_LP_INV | RT5682_TDM_M_BP_INV; + else + return -EINVAL; + break; + case SND_SOC_DAIFMT_IB_IF: + if (dai->id == RT5682_AIF1) + tdm_ctrl |= RT5682_TDM_S_BP_INV | RT5682_TDM_S_LP_INV | + RT5682_TDM_M_BP_INV | RT5682_TDM_M_LP_INV; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5682_I2S_DF_LEFT; + tdm_ctrl |= RT5682_TDM_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5682_I2S_DF_PCM_A; + tdm_ctrl |= RT5682_TDM_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5682_I2S_DF_PCM_B; + tdm_ctrl |= RT5682_TDM_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5682_AIF1: + snd_soc_component_update_bits(component, RT5682_I2S1_SDP, + RT5682_I2S_DF_MASK, reg_val); + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_MS_MASK | RT5682_TDM_S_BP_MASK | + RT5682_TDM_DF_MASK | RT5682_TDM_M_BP_MASK | + RT5682_TDM_M_LP_MASK | RT5682_TDM_S_LP_MASK, + tdm_ctrl | rt5682->master[dai->id]); + break; + case RT5682_AIF2: + if (rt5682->master[dai->id] == 0) + reg_val |= RT5682_I2S2_MS_S; + snd_soc_component_update_bits(component, RT5682_I2S2_SDP, + RT5682_I2S2_MS_MASK | RT5682_I2S_BP_MASK | + RT5682_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5682_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, src = 0; + + if (freq == rt5682->sysclk && clk_id == rt5682->sysclk_src) + return 0; + + switch (clk_id) { + case RT5682_SCLK_S_MCLK: + reg_val |= RT5682_SCLK_SRC_MCLK; + src = RT5682_CLK_SRC_MCLK; + break; + case RT5682_SCLK_S_PLL1: + reg_val |= RT5682_SCLK_SRC_PLL1; + src = RT5682_CLK_SRC_PLL1; + break; + case RT5682_SCLK_S_PLL2: + reg_val |= RT5682_SCLK_SRC_PLL2; + src = RT5682_CLK_SRC_PLL2; + break; + case RT5682_SCLK_S_RCCLK: + reg_val |= RT5682_SCLK_SRC_RCCLK; + src = RT5682_CLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK, reg_val); + + if (rt5682->master[RT5682_AIF2]) { + snd_soc_component_update_bits(component, + RT5682_I2S_M_CLK_CTRL_1, RT5682_I2S2_SRC_MASK, + src << RT5682_I2S2_SRC_SFT); + } + + rt5682->sysclk = freq; + rt5682->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static int rt5682_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct rl6231_pll_code pll_code, pll2f_code, pll2b_code; + unsigned int pll2_fout1, pll2_ps_val; + int ret; + + if (source == rt5682->pll_src[pll_id] && + freq_in == rt5682->pll_in[pll_id] && + freq_out == rt5682->pll_out[pll_id]) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + + rt5682->pll_in[pll_id] = 0; + rt5682->pll_out[pll_id] = 0; + snd_soc_component_update_bits(component, RT5682_GLB_CLK, + RT5682_SCLK_SRC_MASK, RT5682_SCLK_SRC_MCLK); + return 0; + } + + if (pll_id == RT5682_PLL2) { + switch (source) { + case RT5682_PLL2_S_MCLK: + snd_soc_component_update_bits(component, + RT5682_GLB_CLK, RT5682_PLL2_SRC_MASK, + RT5682_PLL2_SRC_MCLK); + break; + default: + dev_err(component->dev, "Unknown PLL2 Source %d\n", + source); + return -EINVAL; + } + + /** + * PLL2 concatenates 2 PLL units. + * We suggest the Fout of the front PLL is 3.84MHz. + */ + pll2_fout1 = 3840000; + ret = rl6231_pll_calc(freq_in, pll2_fout1, &pll2f_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", + freq_in); + return ret; + } + dev_dbg(component->dev, "PLL2F: fin=%d fout=%d bypass=%d m=%d n=%d k=%d\n", + freq_in, pll2_fout1, + pll2f_code.m_bp, + (pll2f_code.m_bp ? 0 : pll2f_code.m_code), + pll2f_code.n_code, pll2f_code.k_code); + + ret = rl6231_pll_calc(pll2_fout1, freq_out, &pll2b_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", + pll2_fout1); + return ret; + } + dev_dbg(component->dev, "PLL2B: fin=%d fout=%d bypass=%d m=%d n=%d k=%d\n", + pll2_fout1, freq_out, + pll2b_code.m_bp, + (pll2b_code.m_bp ? 0 : pll2b_code.m_code), + pll2b_code.n_code, pll2b_code.k_code); + + snd_soc_component_write(component, RT5682_PLL2_CTRL_1, + pll2f_code.k_code << RT5682_PLL2F_K_SFT | + pll2b_code.k_code << RT5682_PLL2B_K_SFT | + pll2b_code.m_code); + snd_soc_component_write(component, RT5682_PLL2_CTRL_2, + pll2f_code.m_code << RT5682_PLL2F_M_SFT | + pll2b_code.n_code); + snd_soc_component_write(component, RT5682_PLL2_CTRL_3, + pll2f_code.n_code << RT5682_PLL2F_N_SFT); + + if (freq_out == 22579200) + pll2_ps_val = 1 << RT5682_PLL2B_SEL_PS_SFT; + else + pll2_ps_val = 1 << RT5682_PLL2B_PS_BYP_SFT; + snd_soc_component_update_bits(component, RT5682_PLL2_CTRL_4, + RT5682_PLL2B_SEL_PS_MASK | RT5682_PLL2B_PS_BYP_MASK | + RT5682_PLL2B_M_BP_MASK | RT5682_PLL2F_M_BP_MASK | 0xf, + pll2_ps_val | + (pll2b_code.m_bp ? 1 : 0) << RT5682_PLL2B_M_BP_SFT | + (pll2f_code.m_bp ? 1 : 0) << RT5682_PLL2F_M_BP_SFT | + 0xf); + } else { + switch (source) { + case RT5682_PLL1_S_MCLK: + snd_soc_component_update_bits(component, + RT5682_GLB_CLK, RT5682_PLL1_SRC_MASK, + RT5682_PLL1_SRC_MCLK); + break; + case RT5682_PLL1_S_BCLK1: + snd_soc_component_update_bits(component, + RT5682_GLB_CLK, RT5682_PLL1_SRC_MASK, + RT5682_PLL1_SRC_BCLK1); + break; + default: + dev_err(component->dev, "Unknown PLL1 Source %d\n", + source); + return -EINVAL; + } + + ret = rl6231_pll_calc(freq_in, freq_out, &pll_code); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", + freq_in); + return ret; + } + + dev_dbg(component->dev, "bypass=%d m=%d n=%d k=%d\n", + pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code), + pll_code.n_code, pll_code.k_code); + + snd_soc_component_write(component, RT5682_PLL_CTRL_1, + (pll_code.n_code << RT5682_PLL_N_SFT) | pll_code.k_code); + snd_soc_component_write(component, RT5682_PLL_CTRL_2, + ((pll_code.m_bp ? 0 : pll_code.m_code) << RT5682_PLL_M_SFT) | + ((pll_code.m_bp << RT5682_PLL_M_BP_SFT) | RT5682_PLL_RST)); + } + + rt5682->pll_in[pll_id] = freq_in; + rt5682->pll_out[pll_id] = freq_out; + rt5682->pll_src[pll_id] = source; + + return 0; +} + +static int rt5682_set_bclk1_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682->bclk[dai->id] = ratio; + + switch (ratio) { + case 256: + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_256); + break; + case 128: + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_128); + break; + case 64: + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_64); + break; + case 32: + snd_soc_component_update_bits(component, RT5682_TDM_TCON_CTRL, + RT5682_TDM_BCLK_MS1_MASK, RT5682_TDM_BCLK_MS1_32); + break; + default: + dev_err(dai->dev, "Invalid bclk1 ratio %d\n", ratio); + return -EINVAL; + } + + return 0; +} + +static int rt5682_set_bclk2_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682->bclk[dai->id] = ratio; + + switch (ratio) { + case 64: + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2, + RT5682_I2S2_BCLK_MS2_MASK, + RT5682_I2S2_BCLK_MS2_64); + break; + case 32: + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_2, + RT5682_I2S2_BCLK_MS2_MASK, + RT5682_I2S2_BCLK_MS2_32); + break; + default: + dev_err(dai->dev, "Invalid bclk2 ratio %d\n", ratio); + return -EINVAL; + } + + return 0; +} + +static int rt5682_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_BG, RT5682_PWR_BG); + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO); + break; + + case SND_SOC_BIAS_STANDBY: + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL, RT5682_DIG_GATE_CTRL); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(rt5682->regmap, RT5682_PWR_DIG_1, + RT5682_DIG_GATE_CTRL | RT5682_PWR_LDO, 0); + regmap_update_bits(rt5682->regmap, RT5682_PWR_ANLG_1, + RT5682_PWR_BG, 0); + break; + case SND_SOC_BIAS_ON: + break; + } + + return 0; +} + +#ifdef CONFIG_COMMON_CLK +#define CLK_PLL2_FIN 48000000 +#define CLK_48 48000 +#define CLK_44 44100 + +static bool rt5682_clk_check(struct rt5682_priv *rt5682) +{ + if (!rt5682->master[RT5682_AIF1]) { + dev_dbg(rt5682->i2c_dev, "sysclk/dai not set correctly\n"); + return false; + } + return true; +} + +static int rt5682_wclk_prepare(struct clk_hw *hw) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + struct snd_soc_component *component; + struct snd_soc_dapm_context *dapm; + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + + component = rt5682->component; + dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS"); + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, + RT5682_PWR_MB, RT5682_PWR_MB); + + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Vref2"); + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, + RT5682_PWR_VREF2 | RT5682_PWR_FV2, + RT5682_PWR_VREF2); + usleep_range(55000, 60000); + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, + RT5682_PWR_FV2, RT5682_PWR_FV2); + + snd_soc_dapm_force_enable_pin_unlocked(dapm, "I2S1"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2F"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2B"); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + +static void rt5682_wclk_unprepare(struct clk_hw *hw) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + struct snd_soc_component *component; + struct snd_soc_dapm_context *dapm; + + if (!rt5682_clk_check(rt5682)) + return; + + component = rt5682->component; + dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Vref2"); + if (!rt5682->jack_type) + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, + RT5682_PWR_VREF2 | RT5682_PWR_FV2 | + RT5682_PWR_MB, 0); + + snd_soc_dapm_disable_pin_unlocked(dapm, "I2S1"); + snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2F"); + snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2B"); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); +} + +static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + const char * const clk_name = clk_hw_get_name(hw); + + if (!rt5682_clk_check(rt5682)) + return 0; + /* + * Only accept to set wclk rate to 44.1k or 48kHz. + */ + if (rt5682->lrck[RT5682_AIF1] != CLK_48 && + rt5682->lrck[RT5682_AIF1] != CLK_44) { + dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n", + __func__, clk_name, CLK_44, CLK_48); + return 0; + } + + return rt5682->lrck[RT5682_AIF1]; +} + +static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + const char * const clk_name = clk_hw_get_name(hw); + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + /* + * Only accept to set wclk rate to 44.1k or 48kHz. + * It will force to 48kHz if not both. + */ + if (rate != CLK_48 && rate != CLK_44) { + dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n", + __func__, clk_name, CLK_44, CLK_48); + rate = CLK_48; + } + + return rate; +} + +static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + struct snd_soc_component *component; + struct clk_hw *parent_hw; + const char * const clk_name = clk_hw_get_name(hw); + int pre_div; + unsigned int clk_pll2_out; + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + + component = rt5682->component; + + /* + * Whether the wclk's parent clk (mclk) exists or not, please ensure + * it is fixed or set to 48MHz before setting wclk rate. It's a + * temporary limitation. Only accept 48MHz clk as the clk provider. + * + * It will set the codec anyway by assuming mclk is 48MHz. + */ + parent_hw = clk_hw_get_parent(hw); + if (!parent_hw) + dev_warn(rt5682->i2c_dev, + "Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n", + CLK_PLL2_FIN); + + if (parent_rate != CLK_PLL2_FIN) + dev_warn(rt5682->i2c_dev, "clk %s only support %d Hz input\n", + clk_name, CLK_PLL2_FIN); + + /* + * To achieve the rate conversion from 48MHz to 44.1k or 48kHz, + * PLL2 is needed. + */ + clk_pll2_out = rate * 512; + rt5682_set_component_pll(component, RT5682_PLL2, RT5682_PLL2_S_MCLK, + CLK_PLL2_FIN, clk_pll2_out); + + rt5682_set_component_sysclk(component, RT5682_SCLK_S_PLL2, 0, + clk_pll2_out, SND_SOC_CLOCK_IN); + + rt5682->lrck[RT5682_AIF1] = rate; + + pre_div = rl6231_get_clk_info(rt5682->sysclk, rate); + + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1, + RT5682_I2S_M_DIV_MASK | RT5682_I2S_CLK_SRC_MASK, + pre_div << RT5682_I2S_M_DIV_SFT | + (rt5682->sysclk_src) << RT5682_I2S_CLK_SRC_SFT); + + return 0; +} + +static unsigned long rt5682_bclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_BCLK_IDX]); + unsigned int bclks_per_wclk; + + regmap_read(rt5682->regmap, RT5682_TDM_TCON_CTRL, &bclks_per_wclk); + + switch (bclks_per_wclk & RT5682_TDM_BCLK_MS1_MASK) { + case RT5682_TDM_BCLK_MS1_256: + return parent_rate * 256; + case RT5682_TDM_BCLK_MS1_128: + return parent_rate * 128; + case RT5682_TDM_BCLK_MS1_64: + return parent_rate * 64; + case RT5682_TDM_BCLK_MS1_32: + return parent_rate * 32; + default: + return 0; + } +} + +static unsigned long rt5682_bclk_get_factor(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long factor; + + factor = rate / parent_rate; + if (factor < 64) + return 32; + else if (factor < 128) + return 64; + else if (factor < 256) + return 128; + else + return 256; +} + +static long rt5682_bclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_BCLK_IDX]); + unsigned long factor; + + if (!*parent_rate || !rt5682_clk_check(rt5682)) + return -EINVAL; + + /* + * BCLK rates are set as a multiplier of WCLK in HW. + * We don't allow changing the parent WCLK. We just do + * some rounding down based on the parent WCLK rate + * and find the appropriate multiplier of BCLK to + * get the rounded down BCLK value. + */ + factor = rt5682_bclk_get_factor(rate, *parent_rate); + + return *parent_rate * factor; +} + +static int rt5682_bclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_BCLK_IDX]); + struct snd_soc_component *component; + struct snd_soc_dai *dai; + unsigned long factor; + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + + component = rt5682->component; + + factor = rt5682_bclk_get_factor(rate, parent_rate); + + for_each_component_dais(component, dai) + if (dai->id == RT5682_AIF1) + return rt5682_set_bclk1_ratio(dai, factor); + + dev_err(rt5682->i2c_dev, "dai %d not found in component\n", + RT5682_AIF1); + return -ENODEV; +} + +static const struct clk_ops rt5682_dai_clk_ops[RT5682_DAI_NUM_CLKS] = { + [RT5682_DAI_WCLK_IDX] = { + .prepare = rt5682_wclk_prepare, + .unprepare = rt5682_wclk_unprepare, + .recalc_rate = rt5682_wclk_recalc_rate, + .round_rate = rt5682_wclk_round_rate, + .set_rate = rt5682_wclk_set_rate, + }, + [RT5682_DAI_BCLK_IDX] = { + .recalc_rate = rt5682_bclk_recalc_rate, + .round_rate = rt5682_bclk_round_rate, + .set_rate = rt5682_bclk_set_rate, + }, +}; + +int rt5682_register_dai_clks(struct rt5682_priv *rt5682) +{ + struct device *dev = rt5682->i2c_dev; + struct rt5682_platform_data *pdata = &rt5682->pdata; + struct clk_hw *dai_clk_hw; + int i, ret; + + for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) { + struct clk_init_data init = { }; + const struct clk_hw *parent; + + dai_clk_hw = &rt5682->dai_clks_hw[i]; + + switch (i) { + case RT5682_DAI_WCLK_IDX: + /* Make MCLK the parent of WCLK */ + if (rt5682->mclk) { + parent = __clk_get_hw(rt5682->mclk); + init.parent_hws = &parent; + init.num_parents = 1; + } + break; + case RT5682_DAI_BCLK_IDX: + /* Make WCLK the parent of BCLK */ + parent = &rt5682->dai_clks_hw[RT5682_DAI_WCLK_IDX]; + init.parent_hws = &parent; + init.num_parents = 1; + break; + default: + dev_err(dev, "Invalid clock index\n"); + return -EINVAL; + } + + init.name = pdata->dai_clk_names[i]; + init.ops = &rt5682_dai_clk_ops[i]; + init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; + dai_clk_hw->init = &init; + + ret = devm_clk_hw_register(dev, dai_clk_hw); + if (ret) { + dev_warn(dev, "Failed to register %s: %d\n", + init.name, ret); + return ret; + } + + if (dev->of_node) { + devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + dai_clk_hw); + } else { + ret = devm_clk_hw_register_clkdev(dev, dai_clk_hw, + init.name, + dev_name(dev)); + if (ret) + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5682_register_dai_clks); +#endif /* CONFIG_COMMON_CLK */ + +static int rt5682_probe(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct sdw_slave *slave; + unsigned long time; + struct snd_soc_dapm_context *dapm = &component->dapm; + + rt5682->component = component; + + if (rt5682->is_sdw) { + slave = rt5682->slave; + time = wait_for_completion_timeout( + &slave->initialization_complete, + msecs_to_jiffies(RT5682_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + } + + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + snd_soc_dapm_disable_pin(dapm, "Vref2"); + snd_soc_dapm_sync(dapm); + return 0; +} + +static void rt5682_remove(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + rt5682_reset(rt5682); +} + +#ifdef CONFIG_PM +static int rt5682_suspend(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (rt5682->is_sdw) + return 0; + + if (rt5682->irq) + disable_irq(rt5682->irq); + + cancel_delayed_work_sync(&rt5682->jack_detect_work); + cancel_delayed_work_sync(&rt5682->jd_check_work); + if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) { + val = snd_soc_component_read(component, + RT5682_CBJ_CTRL_2) & RT5682_JACK_TYPE_MASK; + + switch (val) { + case 0x1: + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_SEL_MB1_MASK | RT5682_SAR_SEL_MB2_MASK, + RT5682_SAR_SEL_MB1_NOSEL | RT5682_SAR_SEL_MB2_SEL); + break; + case 0x2: + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_SEL_MB1_MASK | RT5682_SAR_SEL_MB2_MASK, + RT5682_SAR_SEL_MB1_SEL | RT5682_SAR_SEL_MB2_NOSEL); + break; + default: + break; + } + + /* enter SAR ADC power saving mode */ + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK | + RT5682_SAR_SEL_MB1_MB2_MASK, 0); + usleep_range(5000, 6000); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK, + RT5682_CTRL_MB1_REG | RT5682_CTRL_MB2_REG); + usleep_range(10000, 12000); + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTT_DET_MASK | RT5682_SAR_BUTDET_MODE_MASK, + RT5682_SAR_BUTT_DET_EN | RT5682_SAR_BUTDET_POW_SAV); + snd_soc_component_update_bits(component, RT5682_HP_CHARGE_PUMP_1, + RT5682_OSW_L_MASK | RT5682_OSW_R_MASK, 0); + } + + regcache_cache_only(rt5682->regmap, true); + regcache_mark_dirty(rt5682->regmap); + return 0; +} + +static int rt5682_resume(struct snd_soc_component *component) +{ + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + + if (rt5682->is_sdw) + return 0; + + regcache_cache_only(rt5682->regmap, false); + regcache_sync(rt5682->regmap); + + if (rt5682->hs_jack && (rt5682->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) { + snd_soc_component_update_bits(component, RT5682_SAR_IL_CMD_1, + RT5682_SAR_BUTDET_MODE_MASK | RT5682_SAR_SEL_MB1_MB2_MASK, + RT5682_SAR_BUTDET_POW_NORM | RT5682_SAR_SEL_MB1_MB2_AUTO); + usleep_range(5000, 6000); + snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, + RT5682_MB1_PATH_MASK | RT5682_MB2_PATH_MASK, + RT5682_CTRL_MB1_FSM | RT5682_CTRL_MB2_FSM); + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3, + RT5682_PWR_CBJ, RT5682_PWR_CBJ); + } + + rt5682->jack_type = 0; + mod_delayed_work(system_power_efficient_wq, + &rt5682->jack_detect_work, msecs_to_jiffies(0)); + + if (rt5682->irq) + enable_irq(rt5682->irq); + + return 0; +} +#else +#define rt5682_suspend NULL +#define rt5682_resume NULL +#endif + +const struct snd_soc_dai_ops rt5682_aif1_dai_ops = { + .hw_params = rt5682_hw_params, + .set_fmt = rt5682_set_dai_fmt, + .set_tdm_slot = rt5682_set_tdm_slot, + .set_bclk_ratio = rt5682_set_bclk1_ratio, +}; +EXPORT_SYMBOL_GPL(rt5682_aif1_dai_ops); + +const struct snd_soc_dai_ops rt5682_aif2_dai_ops = { + .hw_params = rt5682_hw_params, + .set_fmt = rt5682_set_dai_fmt, + .set_bclk_ratio = rt5682_set_bclk2_ratio, +}; +EXPORT_SYMBOL_GPL(rt5682_aif2_dai_ops); + +const struct snd_soc_component_driver rt5682_soc_component_dev = { + .probe = rt5682_probe, + .remove = rt5682_remove, + .suspend = rt5682_suspend, + .resume = rt5682_resume, + .set_bias_level = rt5682_set_bias_level, + .controls = rt5682_snd_controls, + .num_controls = ARRAY_SIZE(rt5682_snd_controls), + .dapm_widgets = rt5682_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5682_dapm_widgets), + .dapm_routes = rt5682_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5682_dapm_routes), + .set_sysclk = rt5682_set_component_sysclk, + .set_pll = rt5682_set_component_pll, + .set_jack = rt5682_set_jack_detect, + .use_pmdown_time = 1, + .endianness = 1, +}; +EXPORT_SYMBOL_GPL(rt5682_soc_component_dev); + +int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) +{ + + device_property_read_u32(dev, "realtek,dmic1-data-pin", + &rt5682->pdata.dmic1_data_pin); + device_property_read_u32(dev, "realtek,dmic1-clk-pin", + &rt5682->pdata.dmic1_clk_pin); + device_property_read_u32(dev, "realtek,jd-src", + &rt5682->pdata.jd_src); + device_property_read_u32(dev, "realtek,btndet-delay", + &rt5682->pdata.btndet_delay); + device_property_read_u32(dev, "realtek,dmic-clk-rate-hz", + &rt5682->pdata.dmic_clk_rate); + device_property_read_u32(dev, "realtek,dmic-delay-ms", + &rt5682->pdata.dmic_delay); + + if (device_property_read_string_array(dev, "clock-output-names", + rt5682->pdata.dai_clk_names, + RT5682_DAI_NUM_CLKS) < 0) + dev_warn(dev, "Using default DAI clk names: %s, %s\n", + rt5682->pdata.dai_clk_names[RT5682_DAI_WCLK_IDX], + rt5682->pdata.dai_clk_names[RT5682_DAI_BCLK_IDX]); + + rt5682->pdata.dmic_clk_driving_high = device_property_read_bool(dev, + "realtek,dmic-clk-driving-high"); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5682_parse_dt); + +int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev) +{ + rt5682->ldo1_en = devm_gpiod_get_optional(dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5682->ldo1_en)) { + dev_err(dev, "Fail gpio request ldo1_en\n"); + return PTR_ERR(rt5682->ldo1_en); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5682_get_ldo1); + +void rt5682_calibrate(struct rt5682_priv *rt5682) +{ + int value, count; + + mutex_lock(&rt5682->calibrate_mutex); + + rt5682_reset(rt5682); + regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af); + usleep_range(15000, 20000); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0300); + regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x8000); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0100); + regmap_write(rt5682->regmap, RT5682_HP_IMP_SENS_CTRL_19, 0x3800); + regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000); + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7005); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c); + regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321); + regmap_write(rt5682->regmap, RT5682_HP_LOGIC_CTRL_2, 0x0004); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_3, 0x06a1); + regmap_write(rt5682->regmap, RT5682_A_DAC1_MUX, 0x0311); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); + + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0xfc00); + + for (count = 0; count < 60; count++) { + regmap_read(rt5682->regmap, RT5682_HP_CALIB_STA_1, &value); + if (!(value & 0x8000)) + break; + + usleep_range(10000, 10005); + } + + if (count >= 60) + dev_err(rt5682->component->dev, "HP Calibration Failure\n"); + + /* restore settings */ + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0x002f); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080); + regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x0000); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000); + regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000); + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); + regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c); + + mutex_unlock(&rt5682->calibrate_mutex); +} +EXPORT_SYMBOL_GPL(rt5682_calibrate); + +MODULE_DESCRIPTION("ASoC RT5682 driver"); +MODULE_AUTHOR("Bard Liao "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h new file mode 100644 index 0000000000..b2d9e87af2 --- /dev/null +++ b/sound/soc/codecs/rt5682.h @@ -0,0 +1,1497 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5682.h -- RT5682/RT5658 ALSA SoC audio driver + * + * Copyright 2018 Realtek Microelectronics + * Author: Bard Liao + */ + +#ifndef __RT5682_H__ +#define __RT5682_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_ID 0x6530 + +/* Info */ +#define RT5682_RESET 0x0000 +#define RT5682_VERSION_ID 0x00fd +#define RT5682_VENDOR_ID 0x00fe +#define RT5682_DEVICE_ID 0x00ff +/* I/O - Output */ +#define RT5682_HP_CTRL_1 0x0002 +#define RT5682_HP_CTRL_2 0x0003 +#define RT5682_HPL_GAIN 0x0005 +#define RT5682_HPR_GAIN 0x0006 + +#define RT5682_I2C_CTRL 0x0008 + +/* I/O - Input */ +#define RT5682_CBJ_BST_CTRL 0x000b +#define RT5682_CBJ_CTRL_1 0x0010 +#define RT5682_CBJ_CTRL_2 0x0011 +#define RT5682_CBJ_CTRL_3 0x0012 +#define RT5682_CBJ_CTRL_4 0x0013 +#define RT5682_CBJ_CTRL_5 0x0014 +#define RT5682_CBJ_CTRL_6 0x0015 +#define RT5682_CBJ_CTRL_7 0x0016 +/* I/O - ADC/DAC/DMIC */ +#define RT5682_DAC1_DIG_VOL 0x0019 +#define RT5682_STO1_ADC_DIG_VOL 0x001c +#define RT5682_STO1_ADC_BOOST 0x001f +#define RT5682_HP_IMP_GAIN_1 0x0022 +#define RT5682_HP_IMP_GAIN_2 0x0023 +/* Mixer - D-D */ +#define RT5682_SIDETONE_CTRL 0x0024 +#define RT5682_STO1_ADC_MIXER 0x0026 +#define RT5682_AD_DA_MIXER 0x0029 +#define RT5682_STO1_DAC_MIXER 0x002a +#define RT5682_A_DAC1_MUX 0x002b +#define RT5682_DIG_INF2_DATA 0x0030 +/* Mixer - ADC */ +#define RT5682_REC_MIXER 0x003c +#define RT5682_CAL_REC 0x0044 +#define RT5682_ALC_BACK_GAIN 0x0049 +/* Power */ +#define RT5682_PWR_DIG_1 0x0061 +#define RT5682_PWR_DIG_2 0x0062 +#define RT5682_PWR_ANLG_1 0x0063 +#define RT5682_PWR_ANLG_2 0x0064 +#define RT5682_PWR_ANLG_3 0x0065 +#define RT5682_PWR_MIXER 0x0066 +#define RT5682_PWR_VOL 0x0067 +/* Clock Detect */ +#define RT5682_CLK_DET 0x006b +/* Filter Auto Reset */ +#define RT5682_RESET_LPF_CTRL 0x006c +#define RT5682_RESET_HPF_CTRL 0x006d +/* DMIC */ +#define RT5682_DMIC_CTRL_1 0x006e +/* Format - ADC/DAC */ +#define RT5682_I2S1_SDP 0x0070 +#define RT5682_I2S2_SDP 0x0071 +#define RT5682_ADDA_CLK_1 0x0073 +#define RT5682_ADDA_CLK_2 0x0074 +#define RT5682_I2S1_F_DIV_CTRL_1 0x0075 +#define RT5682_I2S1_F_DIV_CTRL_2 0x0076 +/* Format - TDM Control */ +#define RT5682_TDM_CTRL 0x0079 +#define RT5682_TDM_ADDA_CTRL_1 0x007a +#define RT5682_TDM_ADDA_CTRL_2 0x007b +#define RT5682_DATA_SEL_CTRL_1 0x007c +#define RT5682_TDM_TCON_CTRL 0x007e +/* Function - Analog */ +#define RT5682_GLB_CLK 0x0080 +#define RT5682_PLL_CTRL_1 0x0081 +#define RT5682_PLL_CTRL_2 0x0082 +#define RT5682_PLL_TRACK_1 0x0083 +#define RT5682_PLL_TRACK_2 0x0084 +#define RT5682_PLL_TRACK_3 0x0085 +#define RT5682_PLL_TRACK_4 0x0086 +#define RT5682_PLL_TRACK_5 0x0087 +#define RT5682_PLL_TRACK_6 0x0088 +#define RT5682_PLL_TRACK_11 0x008c +#define RT5682_SDW_REF_CLK 0x008d +#define RT5682_DEPOP_1 0x008e +#define RT5682_DEPOP_2 0x008f +#define RT5682_HP_CHARGE_PUMP_1 0x0091 +#define RT5682_HP_CHARGE_PUMP_2 0x0092 +#define RT5682_MICBIAS_1 0x0093 +#define RT5682_MICBIAS_2 0x0094 +#define RT5682_PLL_TRACK_12 0x0098 +#define RT5682_PLL_TRACK_14 0x009a +#define RT5682_PLL2_CTRL_1 0x009b +#define RT5682_PLL2_CTRL_2 0x009c +#define RT5682_PLL2_CTRL_3 0x009d +#define RT5682_PLL2_CTRL_4 0x009e +#define RT5682_RC_CLK_CTRL 0x009f +#define RT5682_I2S_M_CLK_CTRL_1 0x00a0 +#define RT5682_I2S2_F_DIV_CTRL_1 0x00a3 +#define RT5682_I2S2_F_DIV_CTRL_2 0x00a4 +/* Function - Digital */ +#define RT5682_EQ_CTRL_1 0x00ae +#define RT5682_EQ_CTRL_2 0x00af +#define RT5682_IRQ_CTRL_1 0x00b6 +#define RT5682_IRQ_CTRL_2 0x00b7 +#define RT5682_IRQ_CTRL_3 0x00b8 +#define RT5682_IRQ_CTRL_4 0x00b9 +#define RT5682_INT_ST_1 0x00be +#define RT5682_GPIO_CTRL_1 0x00c0 +#define RT5682_GPIO_CTRL_2 0x00c1 +#define RT5682_GPIO_CTRL_3 0x00c2 +#define RT5682_HP_AMP_DET_CTRL_1 0x00d0 +#define RT5682_HP_AMP_DET_CTRL_2 0x00d1 +#define RT5682_MID_HP_AMP_DET 0x00d2 +#define RT5682_LOW_HP_AMP_DET 0x00d3 +#define RT5682_DELAY_BUF_CTRL 0x00d4 +#define RT5682_SV_ZCD_1 0x00d9 +#define RT5682_SV_ZCD_2 0x00da +#define RT5682_IL_CMD_1 0x00db +#define RT5682_IL_CMD_2 0x00dc +#define RT5682_IL_CMD_3 0x00dd +#define RT5682_IL_CMD_4 0x00de +#define RT5682_IL_CMD_5 0x00df +#define RT5682_IL_CMD_6 0x00e0 +#define RT5682_4BTN_IL_CMD_1 0x00e2 +#define RT5682_4BTN_IL_CMD_2 0x00e3 +#define RT5682_4BTN_IL_CMD_3 0x00e4 +#define RT5682_4BTN_IL_CMD_4 0x00e5 +#define RT5682_4BTN_IL_CMD_5 0x00e6 +#define RT5682_4BTN_IL_CMD_6 0x00e7 +#define RT5682_4BTN_IL_CMD_7 0x00e8 + +#define RT5682_ADC_STO1_HP_CTRL_1 0x00ea +#define RT5682_ADC_STO1_HP_CTRL_2 0x00eb +#define RT5682_AJD1_CTRL 0x00f0 +#define RT5682_JD1_THD 0x00f1 +#define RT5682_JD2_THD 0x00f2 +#define RT5682_JD_CTRL_1 0x00f6 +/* General Control */ +#define RT5682_DUMMY_1 0x00fa +#define RT5682_DUMMY_2 0x00fb +#define RT5682_DUMMY_3 0x00fc + +#define RT5682_DAC_ADC_DIG_VOL1 0x0100 +#define RT5682_BIAS_CUR_CTRL_2 0x010b +#define RT5682_BIAS_CUR_CTRL_3 0x010c +#define RT5682_BIAS_CUR_CTRL_4 0x010d +#define RT5682_BIAS_CUR_CTRL_5 0x010e +#define RT5682_BIAS_CUR_CTRL_6 0x010f +#define RT5682_BIAS_CUR_CTRL_7 0x0110 +#define RT5682_BIAS_CUR_CTRL_8 0x0111 +#define RT5682_BIAS_CUR_CTRL_9 0x0112 +#define RT5682_BIAS_CUR_CTRL_10 0x0113 +#define RT5682_VREF_REC_OP_FB_CAP_CTRL 0x0117 +#define RT5682_CHARGE_PUMP_1 0x0125 +#define RT5682_DIG_IN_CTRL_1 0x0132 +#define RT5682_PAD_DRIVING_CTRL 0x0136 +#define RT5682_SOFT_RAMP_DEPOP 0x0138 +#define RT5682_CHOP_DAC 0x013a +#define RT5682_CHOP_ADC 0x013b +#define RT5682_CALIB_ADC_CTRL 0x013c +#define RT5682_VOL_TEST 0x013f +#define RT5682_SPKVDD_DET_STA 0x0142 +#define RT5682_TEST_MODE_CTRL_1 0x0145 +#define RT5682_TEST_MODE_CTRL_2 0x0146 +#define RT5682_TEST_MODE_CTRL_3 0x0147 +#define RT5682_TEST_MODE_CTRL_4 0x0148 +#define RT5682_TEST_MODE_CTRL_5 0x0149 +#define RT5682_PLL1_INTERNAL 0x0150 +#define RT5682_PLL2_INTERNAL 0x0156 +#define RT5682_STO_NG2_CTRL_1 0x0160 +#define RT5682_STO_NG2_CTRL_2 0x0161 +#define RT5682_STO_NG2_CTRL_3 0x0162 +#define RT5682_STO_NG2_CTRL_4 0x0163 +#define RT5682_STO_NG2_CTRL_5 0x0164 +#define RT5682_STO_NG2_CTRL_6 0x0165 +#define RT5682_STO_NG2_CTRL_7 0x0166 +#define RT5682_STO_NG2_CTRL_8 0x0167 +#define RT5682_STO_NG2_CTRL_9 0x0168 +#define RT5682_STO_NG2_CTRL_10 0x0169 +#define RT5682_STO1_DAC_SIL_DET 0x0190 +#define RT5682_SIL_PSV_CTRL1 0x0194 +#define RT5682_SIL_PSV_CTRL2 0x0195 +#define RT5682_SIL_PSV_CTRL3 0x0197 +#define RT5682_SIL_PSV_CTRL4 0x0198 +#define RT5682_SIL_PSV_CTRL5 0x0199 +#define RT5682_HP_IMP_SENS_CTRL_01 0x01af +#define RT5682_HP_IMP_SENS_CTRL_02 0x01b0 +#define RT5682_HP_IMP_SENS_CTRL_03 0x01b1 +#define RT5682_HP_IMP_SENS_CTRL_04 0x01b2 +#define RT5682_HP_IMP_SENS_CTRL_05 0x01b3 +#define RT5682_HP_IMP_SENS_CTRL_06 0x01b4 +#define RT5682_HP_IMP_SENS_CTRL_07 0x01b5 +#define RT5682_HP_IMP_SENS_CTRL_08 0x01b6 +#define RT5682_HP_IMP_SENS_CTRL_09 0x01b7 +#define RT5682_HP_IMP_SENS_CTRL_10 0x01b8 +#define RT5682_HP_IMP_SENS_CTRL_11 0x01b9 +#define RT5682_HP_IMP_SENS_CTRL_12 0x01ba +#define RT5682_HP_IMP_SENS_CTRL_13 0x01bb +#define RT5682_HP_IMP_SENS_CTRL_14 0x01bc +#define RT5682_HP_IMP_SENS_CTRL_15 0x01bd +#define RT5682_HP_IMP_SENS_CTRL_16 0x01be +#define RT5682_HP_IMP_SENS_CTRL_17 0x01bf +#define RT5682_HP_IMP_SENS_CTRL_18 0x01c0 +#define RT5682_HP_IMP_SENS_CTRL_19 0x01c1 +#define RT5682_HP_IMP_SENS_CTRL_20 0x01c2 +#define RT5682_HP_IMP_SENS_CTRL_21 0x01c3 +#define RT5682_HP_IMP_SENS_CTRL_22 0x01c4 +#define RT5682_HP_IMP_SENS_CTRL_23 0x01c5 +#define RT5682_HP_IMP_SENS_CTRL_24 0x01c6 +#define RT5682_HP_IMP_SENS_CTRL_25 0x01c7 +#define RT5682_HP_IMP_SENS_CTRL_26 0x01c8 +#define RT5682_HP_IMP_SENS_CTRL_27 0x01c9 +#define RT5682_HP_IMP_SENS_CTRL_28 0x01ca +#define RT5682_HP_IMP_SENS_CTRL_29 0x01cb +#define RT5682_HP_IMP_SENS_CTRL_30 0x01cc +#define RT5682_HP_IMP_SENS_CTRL_31 0x01cd +#define RT5682_HP_IMP_SENS_CTRL_32 0x01ce +#define RT5682_HP_IMP_SENS_CTRL_33 0x01cf +#define RT5682_HP_IMP_SENS_CTRL_34 0x01d0 +#define RT5682_HP_IMP_SENS_CTRL_35 0x01d1 +#define RT5682_HP_IMP_SENS_CTRL_36 0x01d2 +#define RT5682_HP_IMP_SENS_CTRL_37 0x01d3 +#define RT5682_HP_IMP_SENS_CTRL_38 0x01d4 +#define RT5682_HP_IMP_SENS_CTRL_39 0x01d5 +#define RT5682_HP_IMP_SENS_CTRL_40 0x01d6 +#define RT5682_HP_IMP_SENS_CTRL_41 0x01d7 +#define RT5682_HP_IMP_SENS_CTRL_42 0x01d8 +#define RT5682_HP_IMP_SENS_CTRL_43 0x01d9 +#define RT5682_HP_LOGIC_CTRL_1 0x01da +#define RT5682_HP_LOGIC_CTRL_2 0x01db +#define RT5682_HP_LOGIC_CTRL_3 0x01dc +#define RT5682_HP_CALIB_CTRL_1 0x01de +#define RT5682_HP_CALIB_CTRL_2 0x01df +#define RT5682_HP_CALIB_CTRL_3 0x01e0 +#define RT5682_HP_CALIB_CTRL_4 0x01e1 +#define RT5682_HP_CALIB_CTRL_5 0x01e2 +#define RT5682_HP_CALIB_CTRL_6 0x01e3 +#define RT5682_HP_CALIB_CTRL_7 0x01e4 +#define RT5682_HP_CALIB_CTRL_9 0x01e6 +#define RT5682_HP_CALIB_CTRL_10 0x01e7 +#define RT5682_HP_CALIB_CTRL_11 0x01e8 +#define RT5682_HP_CALIB_STA_1 0x01ea +#define RT5682_HP_CALIB_STA_2 0x01eb +#define RT5682_HP_CALIB_STA_3 0x01ec +#define RT5682_HP_CALIB_STA_4 0x01ed +#define RT5682_HP_CALIB_STA_5 0x01ee +#define RT5682_HP_CALIB_STA_6 0x01ef +#define RT5682_HP_CALIB_STA_7 0x01f0 +#define RT5682_HP_CALIB_STA_8 0x01f1 +#define RT5682_HP_CALIB_STA_9 0x01f2 +#define RT5682_HP_CALIB_STA_10 0x01f3 +#define RT5682_HP_CALIB_STA_11 0x01f4 +#define RT5682_SAR_IL_CMD_1 0x0210 +#define RT5682_SAR_IL_CMD_2 0x0211 +#define RT5682_SAR_IL_CMD_3 0x0212 +#define RT5682_SAR_IL_CMD_4 0x0213 +#define RT5682_SAR_IL_CMD_5 0x0214 +#define RT5682_SAR_IL_CMD_6 0x0215 +#define RT5682_SAR_IL_CMD_7 0x0216 +#define RT5682_SAR_IL_CMD_8 0x0217 +#define RT5682_SAR_IL_CMD_9 0x0218 +#define RT5682_SAR_IL_CMD_10 0x0219 +#define RT5682_SAR_IL_CMD_11 0x021a +#define RT5682_SAR_IL_CMD_12 0x021b +#define RT5682_SAR_IL_CMD_13 0x021c +#define RT5682_EFUSE_CTRL_1 0x0250 +#define RT5682_EFUSE_CTRL_2 0x0251 +#define RT5682_EFUSE_CTRL_3 0x0252 +#define RT5682_EFUSE_CTRL_4 0x0253 +#define RT5682_EFUSE_CTRL_5 0x0254 +#define RT5682_EFUSE_CTRL_6 0x0255 +#define RT5682_EFUSE_CTRL_7 0x0256 +#define RT5682_EFUSE_CTRL_8 0x0257 +#define RT5682_EFUSE_CTRL_9 0x0258 +#define RT5682_EFUSE_CTRL_10 0x0259 +#define RT5682_EFUSE_CTRL_11 0x025a +#define RT5682_JD_TOP_VC_VTRL 0x0270 +#define RT5682_DRC1_CTRL_0 0x02ff +#define RT5682_DRC1_CTRL_1 0x0300 +#define RT5682_DRC1_CTRL_2 0x0301 +#define RT5682_DRC1_CTRL_3 0x0302 +#define RT5682_DRC1_CTRL_4 0x0303 +#define RT5682_DRC1_CTRL_5 0x0304 +#define RT5682_DRC1_CTRL_6 0x0305 +#define RT5682_DRC1_HARD_LMT_CTRL_1 0x0306 +#define RT5682_DRC1_HARD_LMT_CTRL_2 0x0307 +#define RT5682_DRC1_PRIV_1 0x0310 +#define RT5682_DRC1_PRIV_2 0x0311 +#define RT5682_DRC1_PRIV_3 0x0312 +#define RT5682_DRC1_PRIV_4 0x0313 +#define RT5682_DRC1_PRIV_5 0x0314 +#define RT5682_DRC1_PRIV_6 0x0315 +#define RT5682_DRC1_PRIV_7 0x0316 +#define RT5682_DRC1_PRIV_8 0x0317 +#define RT5682_EQ_AUTO_RCV_CTRL1 0x03c0 +#define RT5682_EQ_AUTO_RCV_CTRL2 0x03c1 +#define RT5682_EQ_AUTO_RCV_CTRL3 0x03c2 +#define RT5682_EQ_AUTO_RCV_CTRL4 0x03c3 +#define RT5682_EQ_AUTO_RCV_CTRL5 0x03c4 +#define RT5682_EQ_AUTO_RCV_CTRL6 0x03c5 +#define RT5682_EQ_AUTO_RCV_CTRL7 0x03c6 +#define RT5682_EQ_AUTO_RCV_CTRL8 0x03c7 +#define RT5682_EQ_AUTO_RCV_CTRL9 0x03c8 +#define RT5682_EQ_AUTO_RCV_CTRL10 0x03c9 +#define RT5682_EQ_AUTO_RCV_CTRL11 0x03ca +#define RT5682_EQ_AUTO_RCV_CTRL12 0x03cb +#define RT5682_EQ_AUTO_RCV_CTRL13 0x03cc +#define RT5682_ADC_L_EQ_LPF1_A1 0x03d0 +#define RT5682_R_EQ_LPF1_A1 0x03d1 +#define RT5682_L_EQ_LPF1_H0 0x03d2 +#define RT5682_R_EQ_LPF1_H0 0x03d3 +#define RT5682_L_EQ_BPF1_A1 0x03d4 +#define RT5682_R_EQ_BPF1_A1 0x03d5 +#define RT5682_L_EQ_BPF1_A2 0x03d6 +#define RT5682_R_EQ_BPF1_A2 0x03d7 +#define RT5682_L_EQ_BPF1_H0 0x03d8 +#define RT5682_R_EQ_BPF1_H0 0x03d9 +#define RT5682_L_EQ_BPF2_A1 0x03da +#define RT5682_R_EQ_BPF2_A1 0x03db +#define RT5682_L_EQ_BPF2_A2 0x03dc +#define RT5682_R_EQ_BPF2_A2 0x03dd +#define RT5682_L_EQ_BPF2_H0 0x03de +#define RT5682_R_EQ_BPF2_H0 0x03df +#define RT5682_L_EQ_BPF3_A1 0x03e0 +#define RT5682_R_EQ_BPF3_A1 0x03e1 +#define RT5682_L_EQ_BPF3_A2 0x03e2 +#define RT5682_R_EQ_BPF3_A2 0x03e3 +#define RT5682_L_EQ_BPF3_H0 0x03e4 +#define RT5682_R_EQ_BPF3_H0 0x03e5 +#define RT5682_L_EQ_BPF4_A1 0x03e6 +#define RT5682_R_EQ_BPF4_A1 0x03e7 +#define RT5682_L_EQ_BPF4_A2 0x03e8 +#define RT5682_R_EQ_BPF4_A2 0x03e9 +#define RT5682_L_EQ_BPF4_H0 0x03ea +#define RT5682_R_EQ_BPF4_H0 0x03eb +#define RT5682_L_EQ_HPF1_A1 0x03ec +#define RT5682_R_EQ_HPF1_A1 0x03ed +#define RT5682_L_EQ_HPF1_H0 0x03ee +#define RT5682_R_EQ_HPF1_H0 0x03ef +#define RT5682_L_EQ_PRE_VOL 0x03f0 +#define RT5682_R_EQ_PRE_VOL 0x03f1 +#define RT5682_L_EQ_POST_VOL 0x03f2 +#define RT5682_R_EQ_POST_VOL 0x03f3 +#define RT5682_I2C_MODE 0xffff + + +/* global definition */ +#define RT5682_L_MUTE (0x1 << 15) +#define RT5682_L_MUTE_SFT 15 +#define RT5682_VOL_L_MUTE (0x1 << 14) +#define RT5682_VOL_L_SFT 14 +#define RT5682_R_MUTE (0x1 << 7) +#define RT5682_R_MUTE_SFT 7 +#define RT5682_VOL_R_MUTE (0x1 << 6) +#define RT5682_VOL_R_SFT 6 +#define RT5682_L_VOL_MASK (0x3f << 8) +#define RT5682_L_VOL_SFT 8 +#define RT5682_R_VOL_MASK (0x3f) +#define RT5682_R_VOL_SFT 0 + +/* Headphone Amp Control 2 (0x0003) */ +#define RT5682_HP_C2_DAC_AMP_MUTE_SFT 15 +#define RT5682_HP_C2_DAC_AMP_MUTE (0x1 << 15) +#define RT5682_HP_C2_DAC_L_EN_SFT 14 +#define RT5682_HP_C2_DAC_L_EN (0x1 << 14) +#define RT5682_HP_C2_DAC_R_EN_SFT 13 +#define RT5682_HP_C2_DAC_R_EN (0x1 << 13) + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5682_G_HP (0xf << 8) +#define RT5682_G_HP_SFT 8 +#define RT5682_G_STO_DA_DMIX (0xf) +#define RT5682_G_STO_DA_SFT 0 + +/* CBJ Control (0x000b) */ +#define RT5682_BST_CBJ_MASK (0xf << 8) +#define RT5682_BST_CBJ_SFT 8 + +/* Embeeded Jack and Type Detection Control 1 (0x0010) */ +#define RT5682_EMB_JD_EN (0x1 << 15) +#define RT5682_EMB_JD_EN_SFT 15 +#define RT5682_EMB_JD_RST (0x1 << 14) +#define RT5682_JD_MODE (0x1 << 13) +#define RT5682_JD_MODE_SFT 13 +#define RT5682_DET_TYPE (0x1 << 12) +#define RT5682_DET_TYPE_SFT 12 +#define RT5682_POLA_EXT_JD_MASK (0x1 << 11) +#define RT5682_POLA_EXT_JD_LOW (0x1 << 11) +#define RT5682_POLA_EXT_JD_HIGH (0x0 << 11) +#define RT5682_EXT_JD_DIG (0x1 << 9) +#define RT5682_POL_FAST_OFF_MASK (0x1 << 8) +#define RT5682_POL_FAST_OFF_HIGH (0x1 << 8) +#define RT5682_POL_FAST_OFF_LOW (0x0 << 8) +#define RT5682_FAST_OFF_MASK (0x1 << 7) +#define RT5682_FAST_OFF_EN (0x1 << 7) +#define RT5682_FAST_OFF_DIS (0x0 << 7) +#define RT5682_VREF_POW_MASK (0x1 << 6) +#define RT5682_VREF_POW_FSM (0x0 << 6) +#define RT5682_VREF_POW_REG (0x1 << 6) +#define RT5682_MB1_PATH_MASK (0x1 << 5) +#define RT5682_CTRL_MB1_REG (0x1 << 5) +#define RT5682_CTRL_MB1_FSM (0x0 << 5) +#define RT5682_MB2_PATH_MASK (0x1 << 4) +#define RT5682_CTRL_MB2_REG (0x1 << 4) +#define RT5682_CTRL_MB2_FSM (0x0 << 4) +#define RT5682_TRIG_JD_MASK (0x1 << 3) +#define RT5682_TRIG_JD_HIGH (0x1 << 3) +#define RT5682_TRIG_JD_LOW (0x0 << 3) +#define RT5682_MIC_CAP_MASK (0x1 << 1) +#define RT5682_MIC_CAP_HS (0x1 << 1) +#define RT5682_MIC_CAP_HP (0x0 << 1) +#define RT5682_MIC_CAP_SRC_MASK (0x1) +#define RT5682_MIC_CAP_SRC_REG (0x1) +#define RT5682_MIC_CAP_SRC_ANA (0x0) + +/* Embeeded Jack and Type Detection Control 2 (0x0011) */ +#define RT5682_EXT_JD_SRC (0x7 << 4) +#define RT5682_EXT_JD_SRC_SFT 4 +#define RT5682_EXT_JD_SRC_GPIO_JD1 (0x0 << 4) +#define RT5682_EXT_JD_SRC_GPIO_JD2 (0x1 << 4) +#define RT5682_EXT_JD_SRC_JDH (0x2 << 4) +#define RT5682_EXT_JD_SRC_JDL (0x3 << 4) +#define RT5682_EXT_JD_SRC_MANUAL (0x4 << 4) +#define RT5682_JACK_TYPE_MASK (0x3) + +/* Combo Jack and Type Detection Control 3 (0x0012) */ +#define RT5682_CBJ_IN_BUF_EN (0x1 << 7) + +/* Combo Jack and Type Detection Control 4 (0x0013) */ +#define RT5682_SEL_SHT_MID_TON_MASK (0x3 << 12) +#define RT5682_SEL_SHT_MID_TON_2 (0x0 << 12) +#define RT5682_SEL_SHT_MID_TON_3 (0x1 << 12) +#define RT5682_CBJ_JD_TEST_MASK (0x1 << 6) +#define RT5682_CBJ_JD_TEST_NORM (0x0 << 6) +#define RT5682_CBJ_JD_TEST_MODE (0x1 << 6) + +/* DAC1 Digital Volume (0x0019) */ +#define RT5682_DAC_L1_VOL_MASK (0xff << 8) +#define RT5682_DAC_L1_VOL_SFT 8 +#define RT5682_DAC_R1_VOL_MASK (0xff) +#define RT5682_DAC_R1_VOL_SFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5682_ADC_L_VOL_MASK (0x7f << 8) +#define RT5682_ADC_L_VOL_SFT 8 +#define RT5682_ADC_R_VOL_MASK (0x7f) +#define RT5682_ADC_R_VOL_SFT 0 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5682_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5682_STO1_ADC_L_BST_SFT 14 +#define RT5682_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5682_STO1_ADC_R_BST_SFT 12 + +/* Sidetone Control (0x0024) */ +#define RT5682_ST_SRC_SEL (0x1 << 8) +#define RT5682_ST_SRC_SFT 8 +#define RT5682_ST_EN_MASK (0x1 << 6) +#define RT5682_ST_DIS (0x0 << 6) +#define RT5682_ST_EN (0x1 << 6) +#define RT5682_ST_EN_SFT 6 + +/* Stereo1 ADC Mixer Control (0x0026) */ +#define RT5682_M_STO1_ADC_L1 (0x1 << 15) +#define RT5682_M_STO1_ADC_L1_SFT 15 +#define RT5682_M_STO1_ADC_L2 (0x1 << 14) +#define RT5682_M_STO1_ADC_L2_SFT 14 +#define RT5682_STO1_ADC1L_SRC_MASK (0x1 << 13) +#define RT5682_STO1_ADC1L_SRC_SFT 13 +#define RT5682_STO1_ADC1_SRC_ADC (0x1 << 13) +#define RT5682_STO1_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5682_STO1_ADC2L_SRC_MASK (0x1 << 12) +#define RT5682_STO1_ADC2L_SRC_SFT 12 +#define RT5682_STO1_ADCL_SRC_MASK (0x3 << 10) +#define RT5682_STO1_ADCL_SRC_SFT 10 +#define RT5682_STO1_DD_L_SRC_MASK (0x1 << 9) +#define RT5682_STO1_DD_L_SRC_SFT 9 +#define RT5682_STO1_DMIC_SRC_MASK (0x1 << 8) +#define RT5682_STO1_DMIC_SRC_SFT 8 +#define RT5682_STO1_DMIC_SRC_DMIC2 (0x1 << 8) +#define RT5682_STO1_DMIC_SRC_DMIC1 (0x0 << 8) +#define RT5682_M_STO1_ADC_R1 (0x1 << 7) +#define RT5682_M_STO1_ADC_R1_SFT 7 +#define RT5682_M_STO1_ADC_R2 (0x1 << 6) +#define RT5682_M_STO1_ADC_R2_SFT 6 +#define RT5682_STO1_ADC1R_SRC_MASK (0x1 << 5) +#define RT5682_STO1_ADC1R_SRC_SFT 5 +#define RT5682_STO1_ADC2R_SRC_MASK (0x1 << 4) +#define RT5682_STO1_ADC2R_SRC_SFT 4 +#define RT5682_STO1_ADCR_SRC_MASK (0x3 << 2) +#define RT5682_STO1_ADCR_SRC_SFT 2 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5682_M_ADCMIX_L (0x1 << 15) +#define RT5682_M_ADCMIX_L_SFT 15 +#define RT5682_M_DAC1_L (0x1 << 14) +#define RT5682_M_DAC1_L_SFT 14 +#define RT5682_DAC1_R_SEL_MASK (0x1 << 10) +#define RT5682_DAC1_R_SEL_SFT 10 +#define RT5682_DAC1_L_SEL_MASK (0x1 << 8) +#define RT5682_DAC1_L_SEL_SFT 8 +#define RT5682_M_ADCMIX_R (0x1 << 7) +#define RT5682_M_ADCMIX_R_SFT 7 +#define RT5682_M_DAC1_R (0x1 << 6) +#define RT5682_M_DAC1_R_SFT 6 + +/* Stereo1 DAC Mixer Control (0x002a) */ +#define RT5682_M_DAC_L1_STO_L (0x1 << 15) +#define RT5682_M_DAC_L1_STO_L_SFT 15 +#define RT5682_G_DAC_L1_STO_L_MASK (0x1 << 14) +#define RT5682_G_DAC_L1_STO_L_SFT 14 +#define RT5682_M_DAC_R1_STO_L (0x1 << 13) +#define RT5682_M_DAC_R1_STO_L_SFT 13 +#define RT5682_G_DAC_R1_STO_L_MASK (0x1 << 12) +#define RT5682_G_DAC_R1_STO_L_SFT 12 +#define RT5682_M_DAC_L1_STO_R (0x1 << 7) +#define RT5682_M_DAC_L1_STO_R_SFT 7 +#define RT5682_G_DAC_L1_STO_R_MASK (0x1 << 6) +#define RT5682_G_DAC_L1_STO_R_SFT 6 +#define RT5682_M_DAC_R1_STO_R (0x1 << 5) +#define RT5682_M_DAC_R1_STO_R_SFT 5 +#define RT5682_G_DAC_R1_STO_R_MASK (0x1 << 4) +#define RT5682_G_DAC_R1_STO_R_SFT 4 + +/* Analog DAC1 Input Source Control (0x002b) */ +#define RT5682_M_ST_STO_L (0x1 << 9) +#define RT5682_M_ST_STO_L_SFT 9 +#define RT5682_M_ST_STO_R (0x1 << 8) +#define RT5682_M_ST_STO_R_SFT 8 +#define RT5682_DAC_L1_SRC_MASK (0x3 << 4) +#define RT5682_A_DACL1_SFT 4 +#define RT5682_DAC_R1_SRC_MASK (0x3) +#define RT5682_A_DACR1_SFT 0 + +/* Digital Interface Data Control (0x0030) */ +#define RT5682_IF2_ADC_SEL_MASK (0x3 << 0) +#define RT5682_IF2_ADC_SEL_SFT 0 + +/* REC Left Mixer Control 2 (0x003c) */ +#define RT5682_G_CBJ_RM1_L (0x7 << 10) +#define RT5682_G_CBJ_RM1_L_SFT 10 +#define RT5682_M_CBJ_RM1_L (0x1 << 7) +#define RT5682_M_CBJ_RM1_L_SFT 7 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5682_PWR_I2S1 (0x1 << 15) +#define RT5682_PWR_I2S1_BIT 15 +#define RT5682_PWR_I2S2 (0x1 << 14) +#define RT5682_PWR_I2S2_BIT 14 +#define RT5682_PWR_DAC_L1 (0x1 << 11) +#define RT5682_PWR_DAC_L1_BIT 11 +#define RT5682_PWR_DAC_R1 (0x1 << 10) +#define RT5682_PWR_DAC_R1_BIT 10 +#define RT5682_PWR_LDO (0x1 << 8) +#define RT5682_PWR_LDO_BIT 8 +#define RT5682_PWR_ADC_L1 (0x1 << 4) +#define RT5682_PWR_ADC_L1_BIT 4 +#define RT5682_PWR_ADC_R1 (0x1 << 3) +#define RT5682_PWR_ADC_R1_BIT 3 +#define RT5682_DIG_GATE_CTRL (0x1 << 0) +#define RT5682_DIG_GATE_CTRL_SFT 0 + + +/* Power Management for Digital 2 (0x0062) */ +#define RT5682_PWR_ADC_S1F (0x1 << 15) +#define RT5682_PWR_ADC_S1F_BIT 15 +#define RT5682_PWR_DAC_S1F (0x1 << 10) +#define RT5682_PWR_DAC_S1F_BIT 10 + +/* Power Management for Analog 1 (0x0063) */ +#define RT5682_PWR_VREF1 (0x1 << 15) +#define RT5682_PWR_VREF1_BIT 15 +#define RT5682_PWR_FV1 (0x1 << 14) +#define RT5682_PWR_FV1_BIT 14 +#define RT5682_PWR_VREF2 (0x1 << 13) +#define RT5682_PWR_VREF2_BIT 13 +#define RT5682_PWR_FV2 (0x1 << 12) +#define RT5682_PWR_FV2_BIT 12 +#define RT5682_LDO1_DBG_MASK (0x3 << 10) +#define RT5682_PWR_MB (0x1 << 9) +#define RT5682_PWR_MB_BIT 9 +#define RT5682_PWR_BG (0x1 << 7) +#define RT5682_PWR_BG_BIT 7 +#define RT5682_LDO1_BYPASS_MASK (0x1 << 6) +#define RT5682_LDO1_BYPASS (0x1 << 6) +#define RT5682_LDO1_NOT_BYPASS (0x0 << 6) +#define RT5682_PWR_MA_BIT 6 +#define RT5682_LDO1_DVO_MASK (0x3 << 4) +#define RT5682_LDO1_DVO_09 (0x0 << 4) +#define RT5682_LDO1_DVO_10 (0x1 << 4) +#define RT5682_LDO1_DVO_12 (0x2 << 4) +#define RT5682_LDO1_DVO_14 (0x3 << 4) +#define RT5682_HP_DRIVER_MASK (0x3 << 2) +#define RT5682_HP_DRIVER_1X (0x0 << 2) +#define RT5682_HP_DRIVER_3X (0x1 << 2) +#define RT5682_HP_DRIVER_5X (0x3 << 2) +#define RT5682_PWR_HA_L (0x1 << 1) +#define RT5682_PWR_HA_L_BIT 1 +#define RT5682_PWR_HA_R (0x1 << 0) +#define RT5682_PWR_HA_R_BIT 0 + +/* Power Management for Analog 2 (0x0064) */ +#define RT5682_PWR_MB1 (0x1 << 11) +#define RT5682_PWR_MB1_PWR_DOWN (0x0 << 11) +#define RT5682_PWR_MB1_BIT 11 +#define RT5682_PWR_MB2 (0x1 << 10) +#define RT5682_PWR_MB2_PWR_DOWN (0x0 << 10) +#define RT5682_PWR_MB2_BIT 10 +#define RT5682_PWR_JDH (0x1 << 3) +#define RT5682_PWR_JDH_BIT 3 +#define RT5682_PWR_JDL (0x1 << 2) +#define RT5682_PWR_JDL_BIT 2 +#define RT5682_PWR_RM1_L (0x1 << 1) +#define RT5682_PWR_RM1_L_BIT 1 + +/* Power Management for Analog 3 (0x0065) */ +#define RT5682_PWR_CBJ (0x1 << 9) +#define RT5682_PWR_CBJ_BIT 9 +#define RT5682_PWR_PLL (0x1 << 6) +#define RT5682_PWR_PLL_BIT 6 +#define RT5682_PWR_PLL2B (0x1 << 5) +#define RT5682_PWR_PLL2B_BIT 5 +#define RT5682_PWR_PLL2F (0x1 << 4) +#define RT5682_PWR_PLL2F_BIT 4 +#define RT5682_PWR_LDO2 (0x1 << 2) +#define RT5682_PWR_LDO2_BIT 2 +#define RT5682_PWR_DET_SPKVDD (0x1 << 1) +#define RT5682_PWR_DET_SPKVDD_BIT 1 + +/* Power Management for Mixer (0x0066) */ +#define RT5682_PWR_STO1_DAC_L (0x1 << 5) +#define RT5682_PWR_STO1_DAC_L_BIT 5 +#define RT5682_PWR_STO1_DAC_R (0x1 << 4) +#define RT5682_PWR_STO1_DAC_R_BIT 4 + +/* MCLK and System Clock Detection Control (0x006b) */ +#define RT5682_SYS_CLK_DET (0x1 << 15) +#define RT5682_SYS_CLK_DET_SFT 15 +#define RT5682_PLL1_CLK_DET (0x1 << 14) +#define RT5682_PLL1_CLK_DET_SFT 14 +#define RT5682_PLL2_CLK_DET (0x1 << 13) +#define RT5682_PLL2_CLK_DET_SFT 13 +#define RT5682_POW_CLK_DET2_SFT 8 +#define RT5682_POW_CLK_DET_SFT 0 + +/* Digital Microphone Control 1 (0x006e) */ +#define RT5682_DMIC_1_EN_MASK (0x1 << 15) +#define RT5682_DMIC_1_EN_SFT 15 +#define RT5682_DMIC_1_DIS (0x0 << 15) +#define RT5682_DMIC_1_EN (0x1 << 15) +#define RT5682_FIFO_CLK_DIV_MASK (0x7 << 12) +#define RT5682_FIFO_CLK_DIV_2 (0x1 << 12) +#define RT5682_DMIC_1_DP_MASK (0x3 << 4) +#define RT5682_DMIC_1_DP_SFT 4 +#define RT5682_DMIC_1_DP_GPIO2 (0x0 << 4) +#define RT5682_DMIC_1_DP_GPIO5 (0x1 << 4) +#define RT5682_DMIC_CLK_MASK (0xf << 0) +#define RT5682_DMIC_CLK_SFT 0 + +/* I2S1 Audio Serial Data Port Control (0x0070) */ +#define RT5682_SEL_ADCDAT_MASK (0x1 << 15) +#define RT5682_SEL_ADCDAT_OUT (0x0 << 15) +#define RT5682_SEL_ADCDAT_IN (0x1 << 15) +#define RT5682_SEL_ADCDAT_SFT 15 +#define RT5682_I2S1_TX_CHL_MASK (0x7 << 12) +#define RT5682_I2S1_TX_CHL_SFT 12 +#define RT5682_I2S1_TX_CHL_16 (0x0 << 12) +#define RT5682_I2S1_TX_CHL_20 (0x1 << 12) +#define RT5682_I2S1_TX_CHL_24 (0x2 << 12) +#define RT5682_I2S1_TX_CHL_32 (0x3 << 12) +#define RT5682_I2S1_TX_CHL_8 (0x4 << 12) +#define RT5682_I2S1_RX_CHL_MASK (0x7 << 8) +#define RT5682_I2S1_RX_CHL_SFT 8 +#define RT5682_I2S1_RX_CHL_16 (0x0 << 8) +#define RT5682_I2S1_RX_CHL_20 (0x1 << 8) +#define RT5682_I2S1_RX_CHL_24 (0x2 << 8) +#define RT5682_I2S1_RX_CHL_32 (0x3 << 8) +#define RT5682_I2S1_RX_CHL_8 (0x4 << 8) +#define RT5682_I2S1_MONO_MASK (0x1 << 7) +#define RT5682_I2S1_MONO_EN (0x1 << 7) +#define RT5682_I2S1_MONO_DIS (0x0 << 7) +#define RT5682_I2S2_MONO_MASK (0x1 << 6) +#define RT5682_I2S2_MONO_EN (0x1 << 6) +#define RT5682_I2S2_MONO_DIS (0x0 << 6) +#define RT5682_I2S1_DL_MASK (0x7 << 4) +#define RT5682_I2S1_DL_SFT 4 +#define RT5682_I2S1_DL_16 (0x0 << 4) +#define RT5682_I2S1_DL_20 (0x1 << 4) +#define RT5682_I2S1_DL_24 (0x2 << 4) +#define RT5682_I2S1_DL_32 (0x3 << 4) +#define RT5682_I2S1_DL_8 (0x4 << 4) + +/* I2S1/2 Audio Serial Data Port Control (0x0070)(0x0071) */ +#define RT5682_I2S2_MS_MASK (0x1 << 15) +#define RT5682_I2S2_MS_SFT 15 +#define RT5682_I2S2_MS_M (0x0 << 15) +#define RT5682_I2S2_MS_S (0x1 << 15) +#define RT5682_I2S2_PIN_CFG_MASK (0x1 << 14) +#define RT5682_I2S2_PIN_CFG_SFT 14 +#define RT5682_I2S2_CLK_SEL_MASK (0x1 << 11) +#define RT5682_I2S2_CLK_SEL_SFT 11 +#define RT5682_I2S2_OUT_MASK (0x1 << 9) +#define RT5682_I2S2_OUT_SFT 9 +#define RT5682_I2S2_OUT_UM (0x0 << 9) +#define RT5682_I2S2_OUT_M (0x1 << 9) +#define RT5682_I2S_BP_MASK (0x1 << 8) +#define RT5682_I2S_BP_SFT 8 +#define RT5682_I2S_BP_NOR (0x0 << 8) +#define RT5682_I2S_BP_INV (0x1 << 8) +#define RT5682_I2S2_MONO_EN (0x1 << 6) +#define RT5682_I2S2_MONO_DIS (0x0 << 6) +#define RT5682_I2S2_DL_MASK (0x3 << 4) +#define RT5682_I2S2_DL_SFT 4 +#define RT5682_I2S2_DL_16 (0x0 << 4) +#define RT5682_I2S2_DL_20 (0x1 << 4) +#define RT5682_I2S2_DL_24 (0x2 << 4) +#define RT5682_I2S2_DL_8 (0x3 << 4) +#define RT5682_I2S_DF_MASK (0x7) +#define RT5682_I2S_DF_SFT 0 +#define RT5682_I2S_DF_I2S (0x0) +#define RT5682_I2S_DF_LEFT (0x1) +#define RT5682_I2S_DF_PCM_A (0x2) +#define RT5682_I2S_DF_PCM_B (0x3) +#define RT5682_I2S_DF_PCM_A_N (0x6) +#define RT5682_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5682_ADC_OSR_MASK (0xf << 12) +#define RT5682_ADC_OSR_SFT 12 +#define RT5682_ADC_OSR_D_1 (0x0 << 12) +#define RT5682_ADC_OSR_D_2 (0x1 << 12) +#define RT5682_ADC_OSR_D_4 (0x2 << 12) +#define RT5682_ADC_OSR_D_6 (0x3 << 12) +#define RT5682_ADC_OSR_D_8 (0x4 << 12) +#define RT5682_ADC_OSR_D_12 (0x5 << 12) +#define RT5682_ADC_OSR_D_16 (0x6 << 12) +#define RT5682_ADC_OSR_D_24 (0x7 << 12) +#define RT5682_ADC_OSR_D_32 (0x8 << 12) +#define RT5682_ADC_OSR_D_48 (0x9 << 12) +#define RT5682_I2S_M_DIV_MASK (0xf << 8) +#define RT5682_I2S_M_DIV_SFT 8 +#define RT5682_I2S_M_D_1 (0x0 << 8) +#define RT5682_I2S_M_D_2 (0x1 << 8) +#define RT5682_I2S_M_D_3 (0x2 << 8) +#define RT5682_I2S_M_D_4 (0x3 << 8) +#define RT5682_I2S_M_D_6 (0x4 << 8) +#define RT5682_I2S_M_D_8 (0x5 << 8) +#define RT5682_I2S_M_D_12 (0x6 << 8) +#define RT5682_I2S_M_D_16 (0x7 << 8) +#define RT5682_I2S_M_D_24 (0x8 << 8) +#define RT5682_I2S_M_D_32 (0x9 << 8) +#define RT5682_I2S_M_D_48 (0x10 << 8) +#define RT5682_I2S_CLK_SRC_MASK (0x7 << 4) +#define RT5682_I2S_CLK_SRC_SFT 4 +#define RT5682_I2S_CLK_SRC_MCLK (0x0 << 4) +#define RT5682_I2S_CLK_SRC_PLL1 (0x1 << 4) +#define RT5682_I2S_CLK_SRC_PLL2 (0x2 << 4) +#define RT5682_I2S_CLK_SRC_SDW (0x3 << 4) +#define RT5682_I2S_CLK_SRC_RCCLK (0x4 << 4) /* 25M */ +#define RT5682_DAC_OSR_MASK (0xf << 0) +#define RT5682_DAC_OSR_SFT 0 +#define RT5682_DAC_OSR_D_1 (0x0 << 0) +#define RT5682_DAC_OSR_D_2 (0x1 << 0) +#define RT5682_DAC_OSR_D_4 (0x2 << 0) +#define RT5682_DAC_OSR_D_6 (0x3 << 0) +#define RT5682_DAC_OSR_D_8 (0x4 << 0) +#define RT5682_DAC_OSR_D_12 (0x5 << 0) +#define RT5682_DAC_OSR_D_16 (0x6 << 0) +#define RT5682_DAC_OSR_D_24 (0x7 << 0) +#define RT5682_DAC_OSR_D_32 (0x8 << 0) +#define RT5682_DAC_OSR_D_48 (0x9 << 0) + +/* ADC/DAC Clock Control 2 (0x0074) */ +#define RT5682_I2S2_BCLK_MS2_MASK (0x1 << 11) +#define RT5682_I2S2_BCLK_MS2_SFT 11 +#define RT5682_I2S2_BCLK_MS2_32 (0x0 << 11) +#define RT5682_I2S2_BCLK_MS2_64 (0x1 << 11) + + +/* TDM control 1 (0x0079) */ +#define RT5682_TDM_TX_CH_MASK (0x3 << 12) +#define RT5682_TDM_TX_CH_2 (0x0 << 12) +#define RT5682_TDM_TX_CH_4 (0x1 << 12) +#define RT5682_TDM_TX_CH_6 (0x2 << 12) +#define RT5682_TDM_TX_CH_8 (0x3 << 12) +#define RT5682_TDM_RX_CH_MASK (0x3 << 8) +#define RT5682_TDM_RX_CH_2 (0x0 << 8) +#define RT5682_TDM_RX_CH_4 (0x1 << 8) +#define RT5682_TDM_RX_CH_6 (0x2 << 8) +#define RT5682_TDM_RX_CH_8 (0x3 << 8) +#define RT5682_TDM_ADC_LCA_MASK (0xf << 4) +#define RT5682_TDM_ADC_LCA_SFT 4 +#define RT5682_TDM_ADC_DL_SFT 0 + +/* TDM control 2 (0x007a) */ +#define RT5682_IF1_ADC1_SEL_SFT 14 +#define RT5682_IF1_ADC2_SEL_SFT 12 +#define RT5682_IF1_ADC3_SEL_SFT 10 +#define RT5682_IF1_ADC4_SEL_SFT 8 +#define RT5682_TDM_ADC_SEL_SFT 4 + +/* TDM control 3 (0x007b) */ +#define RT5682_TDM_EN (0x1 << 7) + +/* TDM/I2S control (0x007e) */ +#define RT5682_TDM_S_BP_MASK (0x1 << 15) +#define RT5682_TDM_S_BP_SFT 15 +#define RT5682_TDM_S_BP_NOR (0x0 << 15) +#define RT5682_TDM_S_BP_INV (0x1 << 15) +#define RT5682_TDM_S_LP_MASK (0x1 << 14) +#define RT5682_TDM_S_LP_SFT 14 +#define RT5682_TDM_S_LP_NOR (0x0 << 14) +#define RT5682_TDM_S_LP_INV (0x1 << 14) +#define RT5682_TDM_DF_MASK (0x7 << 11) +#define RT5682_TDM_DF_SFT 11 +#define RT5682_TDM_DF_I2S (0x0 << 11) +#define RT5682_TDM_DF_LEFT (0x1 << 11) +#define RT5682_TDM_DF_PCM_A (0x2 << 11) +#define RT5682_TDM_DF_PCM_B (0x3 << 11) +#define RT5682_TDM_DF_PCM_A_N (0x6 << 11) +#define RT5682_TDM_DF_PCM_B_N (0x7 << 11) +#define RT5682_TDM_BCLK_MS1_MASK (0x3 << 9) +#define RT5682_TDM_BCLK_MS1_SFT 9 +#define RT5682_TDM_BCLK_MS1_32 (0x0 << 9) +#define RT5682_TDM_BCLK_MS1_64 (0x1 << 9) +#define RT5682_TDM_BCLK_MS1_128 (0x2 << 9) +#define RT5682_TDM_BCLK_MS1_256 (0x3 << 9) +#define RT5682_TDM_CL_MASK (0x3 << 4) +#define RT5682_TDM_CL_16 (0x0 << 4) +#define RT5682_TDM_CL_20 (0x1 << 4) +#define RT5682_TDM_CL_24 (0x2 << 4) +#define RT5682_TDM_CL_32 (0x3 << 4) +#define RT5682_TDM_M_BP_MASK (0x1 << 2) +#define RT5682_TDM_M_BP_SFT 2 +#define RT5682_TDM_M_BP_NOR (0x0 << 2) +#define RT5682_TDM_M_BP_INV (0x1 << 2) +#define RT5682_TDM_M_LP_MASK (0x1 << 1) +#define RT5682_TDM_M_LP_SFT 1 +#define RT5682_TDM_M_LP_NOR (0x0 << 1) +#define RT5682_TDM_M_LP_INV (0x1 << 1) +#define RT5682_TDM_MS_MASK (0x1 << 0) +#define RT5682_TDM_MS_SFT 0 +#define RT5682_TDM_MS_S (0x0 << 0) +#define RT5682_TDM_MS_M (0x1 << 0) + +/* Global Clock Control (0x0080) */ +#define RT5682_SCLK_SRC_MASK (0x7 << 13) +#define RT5682_SCLK_SRC_SFT 13 +#define RT5682_SCLK_SRC_MCLK (0x0 << 13) +#define RT5682_SCLK_SRC_PLL1 (0x1 << 13) +#define RT5682_SCLK_SRC_PLL2 (0x2 << 13) +#define RT5682_SCLK_SRC_SDW (0x3 << 13) +#define RT5682_SCLK_SRC_RCCLK (0x4 << 13) +#define RT5682_PLL2_SRC_MASK (0x3 << 10) +#define RT5682_PLL2_SRC_SFT 10 +#define RT5682_PLL2_SRC_MCLK (0x0 << 10) +#define RT5682_PLL2_SRC_BCLK1 (0x1 << 10) +#define RT5682_PLL2_SRC_SDW (0x2 << 10) +#define RT5682_PLL2_SRC_RC (0x3 << 10) +#define RT5682_PLL1_SRC_MASK (0x3 << 8) +#define RT5682_PLL1_SRC_SFT 8 +#define RT5682_PLL1_SRC_MCLK (0x0 << 8) +#define RT5682_PLL1_SRC_BCLK1 (0x1 << 8) +#define RT5682_PLL1_SRC_SDW (0x2 << 8) +#define RT5682_PLL1_SRC_RC (0x3 << 8) + + + +#define RT5682_PLL_INP_MAX 40000000 +#define RT5682_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x0081) */ +#define RT5682_PLL_N_MAX 0x001ff +#define RT5682_PLL_N_MASK (RT5682_PLL_N_MAX << 7) +#define RT5682_PLL_N_SFT 7 +#define RT5682_PLL_K_MAX 0x001f +#define RT5682_PLL_K_MASK (RT5682_PLL_K_MAX) +#define RT5682_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x0082) */ +#define RT5682_PLL_M_MAX 0x00f +#define RT5682_PLL_M_MASK (RT5682_PLL_M_MAX << 12) +#define RT5682_PLL_M_SFT 12 +#define RT5682_PLL_M_BP (0x1 << 11) +#define RT5682_PLL_M_BP_SFT 11 +#define RT5682_PLL_K_BP (0x1 << 10) +#define RT5682_PLL_K_BP_SFT 10 +#define RT5682_PLL_RST (0x1 << 1) + +/* PLL tracking mode 1 (0x0083) */ +#define RT5682_DA_ASRC_MASK (0x1 << 13) +#define RT5682_DA_ASRC_SFT 13 +#define RT5682_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5682_DAC_STO1_ASRC_SFT 12 +#define RT5682_AD_ASRC_MASK (0x1 << 8) +#define RT5682_AD_ASRC_SFT 8 +#define RT5682_AD_ASRC_SEL_MASK (0x1 << 4) +#define RT5682_AD_ASRC_SEL_SFT 4 +#define RT5682_DMIC_ASRC_MASK (0x1 << 3) +#define RT5682_DMIC_ASRC_SFT 3 +#define RT5682_ADC_STO1_ASRC_MASK (0x1 << 2) +#define RT5682_ADC_STO1_ASRC_SFT 2 +#define RT5682_DA_ASRC_SEL_MASK (0x1 << 0) +#define RT5682_DA_ASRC_SEL_SFT 0 + +/* PLL tracking mode 2 3 (0x0084)(0x0085)*/ +#define RT5682_FILTER_CLK_SEL_MASK (0x7 << 12) +#define RT5682_FILTER_CLK_SEL_SFT 12 +#define RT5682_FILTER_CLK_DIV_MASK (0xf << 8) +#define RT5682_FILTER_CLK_DIV_SFT 8 + +/* ASRC Control 4 (0x0086) */ +#define RT5682_ASRCIN_FTK_N1_MASK (0x3 << 14) +#define RT5682_ASRCIN_FTK_N1_SFT 14 +#define RT5682_ASRCIN_FTK_N2_MASK (0x3 << 12) +#define RT5682_ASRCIN_FTK_N2_SFT 12 +#define RT5682_ASRCIN_FTK_M1_MASK (0x7 << 8) +#define RT5682_ASRCIN_FTK_M1_SFT 8 +#define RT5682_ASRCIN_FTK_M2_MASK (0x7 << 4) +#define RT5682_ASRCIN_FTK_M2_SFT 4 + +/* SoundWire reference clk (0x008d) */ +#define RT5682_PLL2_OUT_MASK (0x1 << 8) +#define RT5682_PLL2_OUT_98M (0x0 << 8) +#define RT5682_PLL2_OUT_49M (0x1 << 8) +#define RT5682_SDW_REF_2_MASK (0xf << 4) +#define RT5682_SDW_REF_2_SFT 4 +#define RT5682_SDW_REF_2_48K (0x0 << 4) +#define RT5682_SDW_REF_2_96K (0x1 << 4) +#define RT5682_SDW_REF_2_192K (0x2 << 4) +#define RT5682_SDW_REF_2_32K (0x3 << 4) +#define RT5682_SDW_REF_2_24K (0x4 << 4) +#define RT5682_SDW_REF_2_16K (0x5 << 4) +#define RT5682_SDW_REF_2_12K (0x6 << 4) +#define RT5682_SDW_REF_2_8K (0x7 << 4) +#define RT5682_SDW_REF_2_44K (0x8 << 4) +#define RT5682_SDW_REF_2_88K (0x9 << 4) +#define RT5682_SDW_REF_2_176K (0xa << 4) +#define RT5682_SDW_REF_2_353K (0xb << 4) +#define RT5682_SDW_REF_2_22K (0xc << 4) +#define RT5682_SDW_REF_2_384K (0xd << 4) +#define RT5682_SDW_REF_2_11K (0xe << 4) +#define RT5682_SDW_REF_1_MASK (0xf << 0) +#define RT5682_SDW_REF_1_SFT 0 +#define RT5682_SDW_REF_1_48K (0x0 << 0) +#define RT5682_SDW_REF_1_96K (0x1 << 0) +#define RT5682_SDW_REF_1_192K (0x2 << 0) +#define RT5682_SDW_REF_1_32K (0x3 << 0) +#define RT5682_SDW_REF_1_24K (0x4 << 0) +#define RT5682_SDW_REF_1_16K (0x5 << 0) +#define RT5682_SDW_REF_1_12K (0x6 << 0) +#define RT5682_SDW_REF_1_8K (0x7 << 0) +#define RT5682_SDW_REF_1_44K (0x8 << 0) +#define RT5682_SDW_REF_1_88K (0x9 << 0) +#define RT5682_SDW_REF_1_176K (0xa << 0) +#define RT5682_SDW_REF_1_353K (0xb << 0) +#define RT5682_SDW_REF_1_22K (0xc << 0) +#define RT5682_SDW_REF_1_384K (0xd << 0) +#define RT5682_SDW_REF_1_11K (0xe << 0) + +/* Depop Mode Control 1 (0x008e) */ +#define RT5682_PUMP_EN (0x1 << 3) +#define RT5682_PUMP_EN_SFT 3 +#define RT5682_CAPLESS_EN (0x1 << 0) +#define RT5682_CAPLESS_EN_SFT 0 + +/* Depop Mode Control 2 (0x8f) */ +#define RT5682_RAMP_MASK (0x1 << 12) +#define RT5682_RAMP_SFT 12 +#define RT5682_RAMP_DIS (0x0 << 12) +#define RT5682_RAMP_EN (0x1 << 12) +#define RT5682_BPS_MASK (0x1 << 11) +#define RT5682_BPS_SFT 11 +#define RT5682_BPS_DIS (0x0 << 11) +#define RT5682_BPS_EN (0x1 << 11) +#define RT5682_FAST_UPDN_MASK (0x1 << 10) +#define RT5682_FAST_UPDN_SFT 10 +#define RT5682_FAST_UPDN_DIS (0x0 << 10) +#define RT5682_FAST_UPDN_EN (0x1 << 10) +#define RT5682_VLO_MASK (0x1 << 7) +#define RT5682_VLO_SFT 7 +#define RT5682_VLO_3V (0x0 << 7) +#define RT5682_VLO_33V (0x1 << 7) + +/* HPOUT charge pump 1 (0x0091) */ +#define RT5682_OSW_L_MASK (0x1 << 11) +#define RT5682_OSW_L_SFT 11 +#define RT5682_OSW_L_DIS (0x0 << 11) +#define RT5682_OSW_L_EN (0x1 << 11) +#define RT5682_OSW_R_MASK (0x1 << 10) +#define RT5682_OSW_R_SFT 10 +#define RT5682_OSW_R_DIS (0x0 << 10) +#define RT5682_OSW_R_EN (0x1 << 10) +#define RT5682_PM_HP_MASK (0x3 << 8) +#define RT5682_PM_HP_SFT 8 +#define RT5682_PM_HP_LV (0x0 << 8) +#define RT5682_PM_HP_MV (0x1 << 8) +#define RT5682_PM_HP_HV (0x2 << 8) +#define RT5682_IB_HP_MASK (0x3 << 6) +#define RT5682_IB_HP_SFT 6 +#define RT5682_IB_HP_125IL (0x0 << 6) +#define RT5682_IB_HP_25IL (0x1 << 6) +#define RT5682_IB_HP_5IL (0x2 << 6) +#define RT5682_IB_HP_1IL (0x3 << 6) + +/* Micbias Control1 (0x93) */ +#define RT5682_MIC1_OV_MASK (0x3 << 14) +#define RT5682_MIC1_OV_SFT 14 +#define RT5682_MIC1_OV_2V7 (0x0 << 14) +#define RT5682_MIC1_OV_2V4 (0x1 << 14) +#define RT5682_MIC1_OV_2V25 (0x3 << 14) +#define RT5682_MIC1_OV_1V8 (0x4 << 14) +#define RT5682_MIC1_CLK_MASK (0x1 << 13) +#define RT5682_MIC1_CLK_SFT 13 +#define RT5682_MIC1_CLK_DIS (0x0 << 13) +#define RT5682_MIC1_CLK_EN (0x1 << 13) +#define RT5682_MIC1_OVCD_MASK (0x1 << 12) +#define RT5682_MIC1_OVCD_SFT 12 +#define RT5682_MIC1_OVCD_DIS (0x0 << 12) +#define RT5682_MIC1_OVCD_EN (0x1 << 12) +#define RT5682_MIC1_OVTH_MASK (0x3 << 10) +#define RT5682_MIC1_OVTH_SFT 10 +#define RT5682_MIC1_OVTH_768UA (0x0 << 10) +#define RT5682_MIC1_OVTH_960UA (0x1 << 10) +#define RT5682_MIC1_OVTH_1152UA (0x2 << 10) +#define RT5682_MIC1_OVTH_1960UA (0x3 << 10) +#define RT5682_MIC2_OV_MASK (0x3 << 8) +#define RT5682_MIC2_OV_SFT 8 +#define RT5682_MIC2_OV_2V7 (0x0 << 8) +#define RT5682_MIC2_OV_2V4 (0x1 << 8) +#define RT5682_MIC2_OV_2V25 (0x3 << 8) +#define RT5682_MIC2_OV_1V8 (0x4 << 8) +#define RT5682_MIC2_CLK_MASK (0x1 << 7) +#define RT5682_MIC2_CLK_SFT 7 +#define RT5682_MIC2_CLK_DIS (0x0 << 7) +#define RT5682_MIC2_CLK_EN (0x1 << 7) +#define RT5682_MIC2_OVTH_MASK (0x3 << 4) +#define RT5682_MIC2_OVTH_SFT 4 +#define RT5682_MIC2_OVTH_768UA (0x0 << 4) +#define RT5682_MIC2_OVTH_960UA (0x1 << 4) +#define RT5682_MIC2_OVTH_1152UA (0x2 << 4) +#define RT5682_MIC2_OVTH_1960UA (0x3 << 4) +#define RT5682_PWR_MB_MASK (0x1 << 3) +#define RT5682_PWR_MB_SFT 3 +#define RT5682_PWR_MB_PD (0x0 << 3) +#define RT5682_PWR_MB_PU (0x1 << 3) + +/* Micbias Control2 (0x0094) */ +#define RT5682_PWR_CLK25M_MASK (0x1 << 9) +#define RT5682_PWR_CLK25M_SFT 9 +#define RT5682_PWR_CLK25M_PD (0x0 << 9) +#define RT5682_PWR_CLK25M_PU (0x1 << 9) +#define RT5682_PWR_CLK1M_MASK (0x1 << 8) +#define RT5682_PWR_CLK1M_SFT 8 +#define RT5682_PWR_CLK1M_PD (0x0 << 8) +#define RT5682_PWR_CLK1M_PU (0x1 << 8) + +/* PLL2 M/N/K Code Control 1 (0x009b) */ +#define RT5682_PLL2F_K_MASK (0x1f << 8) +#define RT5682_PLL2F_K_SFT 8 +#define RT5682_PLL2B_K_MASK (0xf << 4) +#define RT5682_PLL2B_K_SFT 4 +#define RT5682_PLL2B_M_MASK (0xf << 0) + +/* PLL2 M/N/K Code Control 2 (0x009c) */ +#define RT5682_PLL2F_M_MASK (0x3f << 8) +#define RT5682_PLL2F_M_SFT 8 +#define RT5682_PLL2B_N_MASK (0x3f << 0) + +/* PLL2 M/N/K Code Control 2 (0x009d) */ +#define RT5682_PLL2F_N_MASK (0x7f << 8) +#define RT5682_PLL2F_N_SFT 8 + +/* PLL2 M/N/K Code Control 2 (0x009e) */ +#define RT5682_PLL2B_SEL_PS_MASK (0x1 << 13) +#define RT5682_PLL2B_SEL_PS_SFT 13 +#define RT5682_PLL2B_PS_BYP_MASK (0x1 << 12) +#define RT5682_PLL2B_PS_BYP_SFT 12 +#define RT5682_PLL2B_M_BP_MASK (0x1 << 11) +#define RT5682_PLL2B_M_BP_SFT 11 +#define RT5682_PLL2F_M_BP_MASK (0x1 << 7) +#define RT5682_PLL2F_M_BP_SFT 7 + +/* RC Clock Control (0x009f) */ +#define RT5682_POW_IRQ (0x1 << 15) +#define RT5682_POW_JDH (0x1 << 14) +#define RT5682_POW_JDL (0x1 << 13) +#define RT5682_POW_ANA (0x1 << 12) + +/* I2S Master Mode Clock Control 1 (0x00a0) */ +#define RT5682_CLK_SRC_MCLK (0x0) +#define RT5682_CLK_SRC_PLL1 (0x1) +#define RT5682_CLK_SRC_PLL2 (0x2) +#define RT5682_CLK_SRC_SDW (0x3) +#define RT5682_CLK_SRC_RCCLK (0x4) +#define RT5682_I2S_PD_1 (0x0) +#define RT5682_I2S_PD_2 (0x1) +#define RT5682_I2S_PD_3 (0x2) +#define RT5682_I2S_PD_4 (0x3) +#define RT5682_I2S_PD_6 (0x4) +#define RT5682_I2S_PD_8 (0x5) +#define RT5682_I2S_PD_12 (0x6) +#define RT5682_I2S_PD_16 (0x7) +#define RT5682_I2S_PD_24 (0x8) +#define RT5682_I2S_PD_32 (0x9) +#define RT5682_I2S_PD_48 (0xa) +#define RT5682_I2S2_SRC_MASK (0x3 << 4) +#define RT5682_I2S2_SRC_SFT 4 +#define RT5682_I2S2_M_PD_MASK (0xf << 0) +#define RT5682_I2S2_M_PD_SFT 0 + +/* IRQ Control 1 (0x00b6) */ +#define RT5682_JD1_PULSE_EN_MASK (0x1 << 10) +#define RT5682_JD1_PULSE_EN_SFT 10 +#define RT5682_JD1_PULSE_DIS (0x0 << 10) +#define RT5682_JD1_PULSE_EN (0x1 << 10) + +/* IRQ Control 2 (0x00b7) */ +#define RT5682_JD1_EN_MASK (0x1 << 15) +#define RT5682_JD1_EN_SFT 15 +#define RT5682_JD1_DIS (0x0 << 15) +#define RT5682_JD1_EN (0x1 << 15) +#define RT5682_JD1_POL_MASK (0x1 << 13) +#define RT5682_JD1_POL_NOR (0x0 << 13) +#define RT5682_JD1_POL_INV (0x1 << 13) +#define RT5682_JD1_IRQ_MASK (0x1 << 10) +#define RT5682_JD1_IRQ_LEV (0x0 << 10) +#define RT5682_JD1_IRQ_PUL (0x1 << 10) + +/* IRQ Control 3 (0x00b8) */ +#define RT5682_IL_IRQ_MASK (0x1 << 7) +#define RT5682_IL_IRQ_DIS (0x0 << 7) +#define RT5682_IL_IRQ_EN (0x1 << 7) +#define RT5682_IL_IRQ_TYPE_MASK (0x1 << 4) +#define RT5682_IL_IRQ_LEV (0x0 << 4) +#define RT5682_IL_IRQ_PUL (0x1 << 4) + +/* GPIO Control 1 (0x00c0) */ +#define RT5682_GP1_PIN_MASK (0x3 << 14) +#define RT5682_GP1_PIN_SFT 14 +#define RT5682_GP1_PIN_GPIO1 (0x0 << 14) +#define RT5682_GP1_PIN_IRQ (0x1 << 14) +#define RT5682_GP1_PIN_DMIC_CLK (0x2 << 14) +#define RT5682_GP2_PIN_MASK (0x3 << 12) +#define RT5682_GP2_PIN_SFT 12 +#define RT5682_GP2_PIN_GPIO2 (0x0 << 12) +#define RT5682_GP2_PIN_LRCK2 (0x1 << 12) +#define RT5682_GP2_PIN_DMIC_SDA (0x2 << 12) +#define RT5682_GP3_PIN_MASK (0x3 << 10) +#define RT5682_GP3_PIN_SFT 10 +#define RT5682_GP3_PIN_GPIO3 (0x0 << 10) +#define RT5682_GP3_PIN_BCLK2 (0x1 << 10) +#define RT5682_GP3_PIN_DMIC_CLK (0x2 << 10) +#define RT5682_GP4_PIN_MASK (0x3 << 8) +#define RT5682_GP4_PIN_SFT 8 +#define RT5682_GP4_PIN_GPIO4 (0x0 << 8) +#define RT5682_GP4_PIN_ADCDAT1 (0x1 << 8) +#define RT5682_GP4_PIN_DMIC_CLK (0x2 << 8) +#define RT5682_GP4_PIN_ADCDAT2 (0x3 << 8) +#define RT5682_GP5_PIN_MASK (0x3 << 6) +#define RT5682_GP5_PIN_SFT 6 +#define RT5682_GP5_PIN_GPIO5 (0x0 << 6) +#define RT5682_GP5_PIN_DACDAT1 (0x1 << 6) +#define RT5682_GP5_PIN_DMIC_SDA (0x2 << 6) +#define RT5682_GP6_PIN_MASK (0x1 << 5) +#define RT5682_GP6_PIN_SFT 5 +#define RT5682_GP6_PIN_GPIO6 (0x0 << 5) +#define RT5682_GP6_PIN_LRCK1 (0x1 << 5) + +/* GPIO Control 2 (0x00c1)*/ +#define RT5682_GP1_PF_MASK (0x1 << 15) +#define RT5682_GP1_PF_IN (0x0 << 15) +#define RT5682_GP1_PF_OUT (0x1 << 15) +#define RT5682_GP1_OUT_MASK (0x1 << 14) +#define RT5682_GP1_OUT_L (0x0 << 14) +#define RT5682_GP1_OUT_H (0x1 << 14) +#define RT5682_GP2_PF_MASK (0x1 << 13) +#define RT5682_GP2_PF_IN (0x0 << 13) +#define RT5682_GP2_PF_OUT (0x1 << 13) +#define RT5682_GP2_OUT_MASK (0x1 << 12) +#define RT5682_GP2_OUT_L (0x0 << 12) +#define RT5682_GP2_OUT_H (0x1 << 12) +#define RT5682_GP3_PF_MASK (0x1 << 11) +#define RT5682_GP3_PF_IN (0x0 << 11) +#define RT5682_GP3_PF_OUT (0x1 << 11) +#define RT5682_GP3_OUT_MASK (0x1 << 10) +#define RT5682_GP3_OUT_L (0x0 << 10) +#define RT5682_GP3_OUT_H (0x1 << 10) +#define RT5682_GP4_PF_MASK (0x1 << 9) +#define RT5682_GP4_PF_IN (0x0 << 9) +#define RT5682_GP4_PF_OUT (0x1 << 9) +#define RT5682_GP4_OUT_MASK (0x1 << 8) +#define RT5682_GP4_OUT_L (0x0 << 8) +#define RT5682_GP4_OUT_H (0x1 << 8) +#define RT5682_GP5_PF_MASK (0x1 << 7) +#define RT5682_GP5_PF_IN (0x0 << 7) +#define RT5682_GP5_PF_OUT (0x1 << 7) +#define RT5682_GP5_OUT_MASK (0x1 << 6) +#define RT5682_GP5_OUT_L (0x0 << 6) +#define RT5682_GP5_OUT_H (0x1 << 6) +#define RT5682_GP6_PF_MASK (0x1 << 5) +#define RT5682_GP6_PF_IN (0x0 << 5) +#define RT5682_GP6_PF_OUT (0x1 << 5) +#define RT5682_GP6_OUT_MASK (0x1 << 4) +#define RT5682_GP6_OUT_L (0x0 << 4) +#define RT5682_GP6_OUT_H (0x1 << 4) + + +/* GPIO Status (0x00c2) */ +#define RT5682_GP6_STA (0x1 << 6) +#define RT5682_GP5_STA (0x1 << 5) +#define RT5682_GP4_STA (0x1 << 4) +#define RT5682_GP3_STA (0x1 << 3) +#define RT5682_GP2_STA (0x1 << 2) +#define RT5682_GP1_STA (0x1 << 1) + +/* Soft volume and zero cross control 1 (0x00d9) */ +#define RT5682_SV_MASK (0x1 << 15) +#define RT5682_SV_SFT 15 +#define RT5682_SV_DIS (0x0 << 15) +#define RT5682_SV_EN (0x1 << 15) +#define RT5682_ZCD_MASK (0x1 << 10) +#define RT5682_ZCD_SFT 10 +#define RT5682_ZCD_PD (0x0 << 10) +#define RT5682_ZCD_PU (0x1 << 10) +#define RT5682_SV_DLY_MASK (0xf) +#define RT5682_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0x00da) */ +#define RT5682_ZCD_BST1_CBJ_MASK (0x1 << 7) +#define RT5682_ZCD_BST1_CBJ_SFT 7 +#define RT5682_ZCD_BST1_CBJ_DIS (0x0 << 7) +#define RT5682_ZCD_BST1_CBJ_EN (0x1 << 7) +#define RT5682_ZCD_RECMIX_MASK (0x1) +#define RT5682_ZCD_RECMIX_SFT 0 +#define RT5682_ZCD_RECMIX_DIS (0x0) +#define RT5682_ZCD_RECMIX_EN (0x1) + +/* 4 Button Inline Command Control 2 (0x00e3) */ +#define RT5682_4BTN_IL_MASK (0x1 << 15) +#define RT5682_4BTN_IL_EN (0x1 << 15) +#define RT5682_4BTN_IL_DIS (0x0 << 15) +#define RT5682_4BTN_IL_RST_MASK (0x1 << 14) +#define RT5682_4BTN_IL_NOR (0x1 << 14) +#define RT5682_4BTN_IL_RST (0x0 << 14) + +/* Analog JD Control (0x00f0) */ +#define RT5682_JDH_RS_MASK (0x1 << 4) +#define RT5682_JDH_NO_PLUG (0x1 << 4) +#define RT5682_JDH_PLUG (0x0 << 4) + +/* Bias current control 8 (0x0111) */ +#define RT5682_HPA_CP_BIAS_CTRL_MASK (0x3 << 2) +#define RT5682_HPA_CP_BIAS_2UA (0x0 << 2) +#define RT5682_HPA_CP_BIAS_3UA (0x1 << 2) +#define RT5682_HPA_CP_BIAS_4UA (0x2 << 2) +#define RT5682_HPA_CP_BIAS_6UA (0x3 << 2) + +/* Charge Pump Internal Register1 (0x0125) */ +#define RT5682_CP_SW_SIZE_MASK (0x7 << 8) +#define RT5682_CP_SW_SIZE_L (0x4 << 8) +#define RT5682_CP_SW_SIZE_M (0x2 << 8) +#define RT5682_CP_SW_SIZE_S (0x1 << 8) +#define RT5682_CP_CLK_HP_MASK (0x3 << 4) +#define RT5682_CP_CLK_HP_100KHZ (0x0 << 4) +#define RT5682_CP_CLK_HP_200KHZ (0x1 << 4) +#define RT5682_CP_CLK_HP_300KHZ (0x2 << 4) +#define RT5682_CP_CLK_HP_600KHZ (0x3 << 4) + +/* Pad Driving Control (0x0136) */ +#define RT5682_PAD_DRV_GP1_MASK (0x3 << 14) +#define RT5682_PAD_DRV_GP1_SFT 14 +#define RT5682_PAD_DRV_GP2_MASK (0x3 << 12) +#define RT5682_PAD_DRV_GP2_SFT 12 +#define RT5682_PAD_DRV_GP3_MASK (0x3 << 10) +#define RT5682_PAD_DRV_GP3_SFT 10 +#define RT5682_PAD_DRV_GP4_MASK (0x3 << 8) +#define RT5682_PAD_DRV_GP4_SFT 8 +#define RT5682_PAD_DRV_GP5_MASK (0x3 << 6) +#define RT5682_PAD_DRV_GP5_SFT 6 +#define RT5682_PAD_DRV_GP6_MASK (0x3 << 4) +#define RT5682_PAD_DRV_GP6_SFT 4 + +/* Chopper and Clock control for DAC (0x013a)*/ +#define RT5682_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5682_CKXEN_DAC1_SFT 13 +#define RT5682_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5682_CKGEN_DAC1_SFT 12 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5682_CKXEN_ADC1_MASK (0x1 << 13) +#define RT5682_CKXEN_ADC1_SFT 13 +#define RT5682_CKGEN_ADC1_MASK (0x1 << 12) +#define RT5682_CKGEN_ADC1_SFT 12 + +/* Volume test (0x013f)*/ +#define RT5682_SEL_CLK_VOL_MASK (0x1 << 15) +#define RT5682_SEL_CLK_VOL_EN (0x1 << 15) +#define RT5682_SEL_CLK_VOL_DIS (0x0 << 15) + +/* Test Mode Control 1 (0x0145) */ +#define RT5682_AD2DA_LB_MASK (0x1 << 10) +#define RT5682_AD2DA_LB_SFT 10 + +/* Stereo Noise Gate Control 1 (0x0160) */ +#define RT5682_NG2_EN_MASK (0x1 << 15) +#define RT5682_NG2_EN (0x1 << 15) +#define RT5682_NG2_DIS (0x0 << 15) + +/* Stereo1 DAC Silence Detection Control (0x0190) */ +#define RT5682_DEB_STO_DAC_MASK (0x7 << 4) +#define RT5682_DEB_80_MS (0x0 << 4) + +/* HP Behavior Logic Control 2 (0x01db) */ +#define RT5682_HP_LC2_SIG_SOUR2_MASK (0x1 << 4) +#define RT5682_HP_LC2_SIG_SOUR2_REG (0x1 << 4) +#define RT5682_HP_LC2_SIG_SOUR2_DC_CAL (0x0 << 4) +#define RT5682_HP_LC2_SIG_SOUR1_MASK (0x7) +#define RT5682_HP_LC2_SIG_SOUR1_1BIT (0x7) +#define RT5682_HP_LC2_SIG_SOUR1_LEGA (0x2) + +/* SAR ADC Inline Command Control 1 (0x0210) */ +#define RT5682_SAR_BUTT_DET_MASK (0x1 << 15) +#define RT5682_SAR_BUTT_DET_EN (0x1 << 15) +#define RT5682_SAR_BUTT_DET_DIS (0x0 << 15) +#define RT5682_SAR_BUTDET_MODE_MASK (0x1 << 14) +#define RT5682_SAR_BUTDET_POW_SAV (0x1 << 14) +#define RT5682_SAR_BUTDET_POW_NORM (0x0 << 14) +#define RT5682_SAR_BUTDET_RST_MASK (0x1 << 13) +#define RT5682_SAR_BUTDET_RST_NORMAL (0x1 << 13) +#define RT5682_SAR_BUTDET_RST (0x0 << 13) +#define RT5682_SAR_POW_MASK (0x1 << 12) +#define RT5682_SAR_POW_EN (0x1 << 12) +#define RT5682_SAR_POW_DIS (0x0 << 12) +#define RT5682_SAR_RST_MASK (0x1 << 11) +#define RT5682_SAR_RST_NORMAL (0x1 << 11) +#define RT5682_SAR_RST (0x0 << 11) +#define RT5682_SAR_BYPASS_MASK (0x1 << 10) +#define RT5682_SAR_BYPASS_EN (0x1 << 10) +#define RT5682_SAR_BYPASS_DIS (0x0 << 10) +#define RT5682_SAR_SEL_MB1_MASK (0x1 << 9) +#define RT5682_SAR_SEL_MB1_SEL (0x1 << 9) +#define RT5682_SAR_SEL_MB1_NOSEL (0x0 << 9) +#define RT5682_SAR_SEL_MB2_MASK (0x1 << 8) +#define RT5682_SAR_SEL_MB2_SEL (0x1 << 8) +#define RT5682_SAR_SEL_MB2_NOSEL (0x0 << 8) +#define RT5682_SAR_SEL_MODE_MASK (0x1 << 7) +#define RT5682_SAR_SEL_MODE_CMP (0x1 << 7) +#define RT5682_SAR_SEL_MODE_ADC (0x0 << 7) +#define RT5682_SAR_SEL_MB1_MB2_MASK (0x1 << 5) +#define RT5682_SAR_SEL_MB1_MB2_AUTO (0x1 << 5) +#define RT5682_SAR_SEL_MB1_MB2_MANU (0x0 << 5) +#define RT5682_SAR_SEL_SIGNAL_MASK (0x1 << 4) +#define RT5682_SAR_SEL_SIGNAL_AUTO (0x1 << 4) +#define RT5682_SAR_SEL_SIGNAL_MANU (0x0 << 4) + +/* SAR ADC Inline Command Control 13 (0x021c) */ +#define RT5682_SAR_SOUR_MASK (0x3f) +#define RT5682_SAR_SOUR_BTN (0x3f) +#define RT5682_SAR_SOUR_TYPE (0x0) + +/* soundwire timeout */ +#define RT5682_PROBE_TIMEOUT 5000 + + +#define RT5682_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5682_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +/* System Clock Source */ +enum { + RT5682_SCLK_S_MCLK, + RT5682_SCLK_S_PLL1, + RT5682_SCLK_S_PLL2, + RT5682_SCLK_S_RCCLK, +}; + +/* PLL Source */ +enum { + RT5682_PLL1_S_MCLK, + RT5682_PLL1_S_BCLK1, + RT5682_PLL1_S_RCCLK, + RT5682_PLL2_S_MCLK, +}; + +enum { + RT5682_PLL1, + RT5682_PLL2, + RT5682_PLLS, +}; + +enum { + RT5682_AIF1, + RT5682_AIF2, + RT5682_SDW, + RT5682_AIFS +}; + +/* filter mask */ +enum { + RT5682_DA_STEREO1_FILTER = 0x1, + RT5682_AD_STEREO1_FILTER = (0x1 << 1), +}; + +enum { + RT5682_CLK_SEL_SYS, + RT5682_CLK_SEL_I2S1_ASRC, + RT5682_CLK_SEL_I2S2_ASRC, +}; + +#define RT5682_NUM_SUPPLIES 5 + +struct rt5682_priv { + struct snd_soc_component *component; + struct device *i2c_dev; + struct rt5682_platform_data pdata; + struct gpio_desc *ldo1_en; + struct regmap *regmap; + struct regmap *sdw_regmap; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5682_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work jd_check_work; + struct mutex disable_irq_lock; /* imp-def irq lock protection */ + bool disable_irq; + struct mutex calibrate_mutex; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + bool is_sdw; + +#ifdef CONFIG_COMMON_CLK + struct clk_hw dai_clks_hw[RT5682_DAI_NUM_CLKS]; + struct clk *mclk; +#endif + + int sysclk; + int sysclk_src; + int lrck[RT5682_AIFS]; + int bclk[RT5682_AIFS]; + int master[RT5682_AIFS]; + + int pll_src[RT5682_PLLS]; + int pll_in[RT5682_PLLS]; + int pll_out[RT5682_PLLS]; + + int jack_type; + int irq; + int irq_work_delay_time; +}; + +extern const char *rt5682_supply_names[RT5682_NUM_SUPPLIES]; + +int rt5682_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +void rt5682_apply_patch_list(struct rt5682_priv *rt5682, struct device *dev); + +void rt5682_jack_detect_handler(struct work_struct *work); + +bool rt5682_volatile_register(struct device *dev, unsigned int reg); +bool rt5682_readable_register(struct device *dev, unsigned int reg); + +int rt5682_register_component(struct device *dev); +void rt5682_calibrate(struct rt5682_priv *rt5682); +void rt5682_reset(struct rt5682_priv *rt5682); +int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev); +int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev); + +int rt5682_register_dai_clks(struct rt5682_priv *rt5682); + +#define RT5682_REG_NUM 318 +extern const struct reg_default rt5682_reg[RT5682_REG_NUM]; + +extern const struct snd_soc_dai_ops rt5682_aif1_dai_ops; +extern const struct snd_soc_dai_ops rt5682_aif2_dai_ops; +extern const struct snd_soc_component_driver rt5682_soc_component_dev; + +#endif /* __RT5682_H__ */ diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c new file mode 100644 index 0000000000..68ac5ea503 --- /dev/null +++ b/sound/soc/codecs/rt5682s.c @@ -0,0 +1,3320 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt5682s.c -- RT5682I-VS ALSA SoC audio component driver +// +// Copyright 2021 Realtek Semiconductor Corp. +// Author: Derek Fang +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5682s.h" + +#define DEVICE_ID 0x6749 + +static const struct rt5682s_platform_data i2s_default_platform_data = { + .dmic1_data_pin = RT5682S_DMIC1_DATA_GPIO2, + .dmic1_clk_pin = RT5682S_DMIC1_CLK_GPIO3, + .jd_src = RT5682S_JD1, + .dai_clk_names[RT5682S_DAI_WCLK_IDX] = "rt5682-dai-wclk", + .dai_clk_names[RT5682S_DAI_BCLK_IDX] = "rt5682-dai-bclk", +}; + +static const char *rt5682s_supply_names[RT5682S_NUM_SUPPLIES] = { + [RT5682S_SUPPLY_AVDD] = "AVDD", + [RT5682S_SUPPLY_MICVDD] = "MICVDD", + [RT5682S_SUPPLY_DBVDD] = "DBVDD", + [RT5682S_SUPPLY_LDO1_IN] = "LDO1-IN", +}; + +static const struct reg_sequence patch_list[] = { + {RT5682S_I2C_CTRL, 0x0007}, + {RT5682S_DIG_IN_CTRL_1, 0x0000}, + {RT5682S_CHOP_DAC_2, 0x2020}, + {RT5682S_VREF_REC_OP_FB_CAP_CTRL_2, 0x0101}, + {RT5682S_VREF_REC_OP_FB_CAP_CTRL_1, 0x80c0}, + {RT5682S_HP_CALIB_CTRL_9, 0x0002}, + {RT5682S_DEPOP_1, 0x0000}, + {RT5682S_HP_CHARGE_PUMP_2, 0x3c15}, + {RT5682S_DAC1_DIG_VOL, 0xfefe}, + {RT5682S_SAR_IL_CMD_2, 0xac00}, + {RT5682S_SAR_IL_CMD_3, 0x024c}, + {RT5682S_CBJ_CTRL_6, 0x0804}, +}; + +static void rt5682s_apply_patch_list(struct rt5682s_priv *rt5682s, + struct device *dev) +{ + int ret; + + ret = regmap_multi_reg_write(rt5682s->regmap, patch_list, ARRAY_SIZE(patch_list)); + if (ret) + dev_warn(dev, "Failed to apply regmap patch: %d\n", ret); +} + +static const struct reg_default rt5682s_reg[] = { + {0x0002, 0x8080}, + {0x0003, 0x0001}, + {0x0005, 0x0000}, + {0x0006, 0x0000}, + {0x0008, 0x8007}, + {0x000b, 0x0000}, + {0x000f, 0x4000}, + {0x0010, 0x4040}, + {0x0011, 0x0000}, + {0x0012, 0x0000}, + {0x0013, 0x1200}, + {0x0014, 0x200a}, + {0x0015, 0x0404}, + {0x0016, 0x0404}, + {0x0017, 0x05a4}, + {0x0019, 0xffff}, + {0x001c, 0x2f2f}, + {0x001f, 0x0000}, + {0x0022, 0x5757}, + {0x0023, 0x0039}, + {0x0024, 0x000b}, + {0x0026, 0xc0c4}, + {0x0029, 0x8080}, + {0x002a, 0xa0a0}, + {0x002b, 0x0300}, + {0x0030, 0x0000}, + {0x003c, 0x08c0}, + {0x0044, 0x1818}, + {0x004b, 0x00c0}, + {0x004c, 0x0000}, + {0x004d, 0x0000}, + {0x0061, 0x00c0}, + {0x0062, 0x008a}, + {0x0063, 0x0800}, + {0x0064, 0x0000}, + {0x0065, 0x0000}, + {0x0066, 0x0030}, + {0x0067, 0x000c}, + {0x0068, 0x0000}, + {0x0069, 0x0000}, + {0x006a, 0x0000}, + {0x006b, 0x0000}, + {0x006c, 0x0000}, + {0x006d, 0x2200}, + {0x006e, 0x0810}, + {0x006f, 0xe4de}, + {0x0070, 0x3320}, + {0x0071, 0x0000}, + {0x0073, 0x0000}, + {0x0074, 0x0000}, + {0x0075, 0x0002}, + {0x0076, 0x0001}, + {0x0079, 0x0000}, + {0x007a, 0x0000}, + {0x007b, 0x0000}, + {0x007c, 0x0100}, + {0x007e, 0x0000}, + {0x007f, 0x0000}, + {0x0080, 0x0000}, + {0x0083, 0x0000}, + {0x0084, 0x0000}, + {0x0085, 0x0000}, + {0x0086, 0x0005}, + {0x0087, 0x0000}, + {0x0088, 0x0000}, + {0x008c, 0x0003}, + {0x008e, 0x0060}, + {0x008f, 0x4da1}, + {0x0091, 0x1c15}, + {0x0092, 0x0425}, + {0x0093, 0x0000}, + {0x0094, 0x0080}, + {0x0095, 0x008f}, + {0x0096, 0x0000}, + {0x0097, 0x0000}, + {0x0098, 0x0000}, + {0x0099, 0x0000}, + {0x009a, 0x0000}, + {0x009b, 0x0000}, + {0x009c, 0x0000}, + {0x009d, 0x0000}, + {0x009e, 0x0000}, + {0x009f, 0x0009}, + {0x00a0, 0x0000}, + {0x00a3, 0x0002}, + {0x00a4, 0x0001}, + {0x00b6, 0x0000}, + {0x00b7, 0x0000}, + {0x00b8, 0x0000}, + {0x00b9, 0x0002}, + {0x00be, 0x0000}, + {0x00c0, 0x0160}, + {0x00c1, 0x82a0}, + {0x00c2, 0x0000}, + {0x00d0, 0x0000}, + {0x00d2, 0x3300}, + {0x00d3, 0x2200}, + {0x00d4, 0x0000}, + {0x00d9, 0x0000}, + {0x00da, 0x0000}, + {0x00db, 0x0000}, + {0x00dc, 0x00c0}, + {0x00dd, 0x2220}, + {0x00de, 0x3131}, + {0x00df, 0x3131}, + {0x00e0, 0x3131}, + {0x00e2, 0x0000}, + {0x00e3, 0x4000}, + {0x00e4, 0x0aa0}, + {0x00e5, 0x3131}, + {0x00e6, 0x3131}, + {0x00e7, 0x3131}, + {0x00e8, 0x3131}, + {0x00ea, 0xb320}, + {0x00eb, 0x0000}, + {0x00f0, 0x0000}, + {0x00f6, 0x0000}, + {0x00fa, 0x0000}, + {0x00fb, 0x0000}, + {0x00fc, 0x0000}, + {0x00fd, 0x0000}, + {0x00fe, 0x10ec}, + {0x00ff, 0x6749}, + {0x0100, 0xa000}, + {0x010b, 0x0066}, + {0x010c, 0x6666}, + {0x010d, 0x2202}, + {0x010e, 0x6666}, + {0x010f, 0xa800}, + {0x0110, 0x0006}, + {0x0111, 0x0460}, + {0x0112, 0x2000}, + {0x0113, 0x0200}, + {0x0117, 0x8000}, + {0x0118, 0x0303}, + {0x0125, 0x0020}, + {0x0132, 0x5026}, + {0x0136, 0x8000}, + {0x0139, 0x0005}, + {0x013a, 0x3030}, + {0x013b, 0xa000}, + {0x013c, 0x4110}, + {0x013f, 0x0000}, + {0x0145, 0x0022}, + {0x0146, 0x0000}, + {0x0147, 0x0000}, + {0x0148, 0x0000}, + {0x0156, 0x0022}, + {0x0157, 0x0303}, + {0x0158, 0x2222}, + {0x0159, 0x0000}, + {0x0160, 0x4ec0}, + {0x0161, 0x0080}, + {0x0162, 0x0200}, + {0x0163, 0x0800}, + {0x0164, 0x0000}, + {0x0165, 0x0000}, + {0x0166, 0x0000}, + {0x0167, 0x000f}, + {0x0168, 0x000f}, + {0x0169, 0x0001}, + {0x0190, 0x4131}, + {0x0194, 0x0000}, + {0x0195, 0x0000}, + {0x0197, 0x0022}, + {0x0198, 0x0000}, + {0x0199, 0x0000}, + {0x01ac, 0x0000}, + {0x01ad, 0x0000}, + {0x01ae, 0x0000}, + {0x01af, 0x2000}, + {0x01b0, 0x0000}, + {0x01b1, 0x0000}, + {0x01b2, 0x0000}, + {0x01b3, 0x0017}, + {0x01b4, 0x004b}, + {0x01b5, 0x0000}, + {0x01b6, 0x03e8}, + {0x01b7, 0x0000}, + {0x01b8, 0x0000}, + {0x01b9, 0x0400}, + {0x01ba, 0xb5b6}, + {0x01bb, 0x9124}, + {0x01bc, 0x4924}, + {0x01bd, 0x0009}, + {0x01be, 0x0018}, + {0x01bf, 0x002a}, + {0x01c0, 0x004c}, + {0x01c1, 0x0097}, + {0x01c2, 0x01c3}, + {0x01c3, 0x03e9}, + {0x01c4, 0x1389}, + {0x01c5, 0xc351}, + {0x01c6, 0x02a0}, + {0x01c7, 0x0b0f}, + {0x01c8, 0x402f}, + {0x01c9, 0x0702}, + {0x01ca, 0x0000}, + {0x01cb, 0x0000}, + {0x01cc, 0x5757}, + {0x01cd, 0x5757}, + {0x01ce, 0x5757}, + {0x01cf, 0x5757}, + {0x01d0, 0x5757}, + {0x01d1, 0x5757}, + {0x01d2, 0x5757}, + {0x01d3, 0x5757}, + {0x01d4, 0x5757}, + {0x01d5, 0x5757}, + {0x01d6, 0x0000}, + {0x01d7, 0x0000}, + {0x01d8, 0x0162}, + {0x01d9, 0x0007}, + {0x01da, 0x0000}, + {0x01db, 0x0004}, + {0x01dc, 0x0000}, + {0x01de, 0x7c00}, + {0x01df, 0x0020}, + {0x01e0, 0x04c1}, + {0x01e1, 0x0000}, + {0x01e2, 0x0000}, + {0x01e3, 0x0000}, + {0x01e4, 0x0000}, + {0x01e5, 0x0000}, + {0x01e6, 0x0001}, + {0x01e7, 0x0000}, + {0x01e8, 0x0000}, + {0x01eb, 0x0000}, + {0x01ec, 0x0000}, + {0x01ed, 0x0000}, + {0x01ee, 0x0000}, + {0x01ef, 0x0000}, + {0x01f0, 0x0000}, + {0x01f1, 0x0000}, + {0x01f2, 0x0000}, + {0x01f3, 0x0000}, + {0x01f4, 0x0000}, + {0x0210, 0x6297}, + {0x0211, 0xa004}, + {0x0212, 0x0365}, + {0x0213, 0xf7ff}, + {0x0214, 0xf24c}, + {0x0215, 0x0102}, + {0x0216, 0x00a3}, + {0x0217, 0x0048}, + {0x0218, 0xa2c0}, + {0x0219, 0x0400}, + {0x021a, 0x00c8}, + {0x021b, 0x00c0}, + {0x021c, 0x0000}, + {0x021d, 0x024c}, + {0x02fa, 0x0000}, + {0x02fb, 0x0000}, + {0x02fc, 0x0000}, + {0x03fe, 0x0000}, + {0x03ff, 0x0000}, + {0x0500, 0x0000}, + {0x0600, 0x0000}, + {0x0610, 0x6666}, + {0x0611, 0xa9aa}, + {0x0620, 0x6666}, + {0x0621, 0xa9aa}, + {0x0630, 0x6666}, + {0x0631, 0xa9aa}, + {0x0640, 0x6666}, + {0x0641, 0xa9aa}, + {0x07fa, 0x0000}, + {0x08fa, 0x0000}, + {0x08fb, 0x0000}, + {0x0d00, 0x0000}, + {0x1100, 0x0000}, + {0x1101, 0x0000}, + {0x1102, 0x0000}, + {0x1103, 0x0000}, + {0x1104, 0x0000}, + {0x1105, 0x0000}, + {0x1106, 0x0000}, + {0x1107, 0x0000}, + {0x1108, 0x0000}, + {0x1109, 0x0000}, + {0x110a, 0x0000}, + {0x110b, 0x0000}, + {0x110c, 0x0000}, + {0x1111, 0x0000}, + {0x1112, 0x0000}, + {0x1113, 0x0000}, + {0x1114, 0x0000}, + {0x1115, 0x0000}, + {0x1116, 0x0000}, + {0x1117, 0x0000}, + {0x1118, 0x0000}, + {0x1119, 0x0000}, + {0x111a, 0x0000}, + {0x111b, 0x0000}, + {0x111c, 0x0000}, + {0x1401, 0x0404}, + {0x1402, 0x0007}, + {0x1403, 0x0365}, + {0x1404, 0x0210}, + {0x1405, 0x0365}, + {0x1406, 0x0210}, + {0x1407, 0x0000}, + {0x1408, 0x0000}, + {0x1409, 0x0000}, + {0x140a, 0x0000}, + {0x140b, 0x0000}, + {0x140c, 0x0000}, + {0x140d, 0x0000}, + {0x140e, 0x0000}, + {0x140f, 0x0000}, + {0x1410, 0x0000}, + {0x1411, 0x0000}, + {0x1801, 0x0004}, + {0x1802, 0x0000}, + {0x1803, 0x0000}, + {0x1804, 0x0000}, + {0x1805, 0x00ff}, + {0x2c00, 0x0000}, + {0x3400, 0x0200}, + {0x3404, 0x0000}, + {0x3405, 0x0000}, + {0x3406, 0x0000}, + {0x3407, 0x0000}, + {0x3408, 0x0000}, + {0x3409, 0x0000}, + {0x340a, 0x0000}, + {0x340b, 0x0000}, + {0x340c, 0x0000}, + {0x340d, 0x0000}, + {0x340e, 0x0000}, + {0x340f, 0x0000}, + {0x3410, 0x0000}, + {0x3411, 0x0000}, + {0x3412, 0x0000}, + {0x3413, 0x0000}, + {0x3414, 0x0000}, + {0x3415, 0x0000}, + {0x3424, 0x0000}, + {0x3425, 0x0000}, + {0x3426, 0x0000}, + {0x3427, 0x0000}, + {0x3428, 0x0000}, + {0x3429, 0x0000}, + {0x342a, 0x0000}, + {0x342b, 0x0000}, + {0x342c, 0x0000}, + {0x342d, 0x0000}, + {0x342e, 0x0000}, + {0x342f, 0x0000}, + {0x3430, 0x0000}, + {0x3431, 0x0000}, + {0x3432, 0x0000}, + {0x3433, 0x0000}, + {0x3434, 0x0000}, + {0x3435, 0x0000}, + {0x3440, 0x6319}, + {0x3441, 0x3771}, + {0x3500, 0x0002}, + {0x3501, 0x5728}, + {0x3b00, 0x3010}, + {0x3b01, 0x3300}, + {0x3b02, 0x2200}, + {0x3b03, 0x0100}, +}; + +static bool rt5682s_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5682S_RESET: + case RT5682S_CBJ_CTRL_2: + case RT5682S_I2S1_F_DIV_CTRL_2: + case RT5682S_I2S2_F_DIV_CTRL_2: + case RT5682S_INT_ST_1: + case RT5682S_GPIO_ST: + case RT5682S_IL_CMD_1: + case RT5682S_4BTN_IL_CMD_1: + case RT5682S_AJD1_CTRL: + case RT5682S_VERSION_ID...RT5682S_DEVICE_ID: + case RT5682S_STO_NG2_CTRL_1: + case RT5682S_STO_NG2_CTRL_5...RT5682S_STO_NG2_CTRL_7: + case RT5682S_STO1_DAC_SIL_DET: + case RT5682S_HP_IMP_SENS_CTRL_1...RT5682S_HP_IMP_SENS_CTRL_4: + case RT5682S_HP_IMP_SENS_CTRL_13: + case RT5682S_HP_IMP_SENS_CTRL_14: + case RT5682S_HP_IMP_SENS_CTRL_43...RT5682S_HP_IMP_SENS_CTRL_46: + case RT5682S_HP_CALIB_CTRL_1: + case RT5682S_HP_CALIB_CTRL_10: + case RT5682S_HP_CALIB_ST_1...RT5682S_HP_CALIB_ST_11: + case RT5682S_SAR_IL_CMD_2...RT5682S_SAR_IL_CMD_5: + case RT5682S_SAR_IL_CMD_10: + case RT5682S_SAR_IL_CMD_11: + case RT5682S_VERSION_ID_HIDE: + case RT5682S_VERSION_ID_CUS: + case RT5682S_I2C_TRANS_CTRL: + case RT5682S_DMIC_FLOAT_DET: + case RT5682S_HA_CMP_OP_1: + case RT5682S_NEW_CBJ_DET_CTL_10...RT5682S_NEW_CBJ_DET_CTL_16: + case RT5682S_CLK_SW_TEST_1: + case RT5682S_CLK_SW_TEST_2: + case RT5682S_EFUSE_READ_1...RT5682S_EFUSE_READ_18: + case RT5682S_PILOT_DIG_CTL_1: + return true; + default: + return false; + } +} + +static bool rt5682s_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RT5682S_RESET: + case RT5682S_VERSION_ID: + case RT5682S_VENDOR_ID: + case RT5682S_DEVICE_ID: + case RT5682S_HP_CTRL_1: + case RT5682S_HP_CTRL_2: + case RT5682S_HPL_GAIN: + case RT5682S_HPR_GAIN: + case RT5682S_I2C_CTRL: + case RT5682S_CBJ_BST_CTRL: + case RT5682S_CBJ_DET_CTRL: + case RT5682S_CBJ_CTRL_1...RT5682S_CBJ_CTRL_8: + case RT5682S_DAC1_DIG_VOL: + case RT5682S_STO1_ADC_DIG_VOL: + case RT5682S_STO1_ADC_BOOST: + case RT5682S_HP_IMP_GAIN_1: + case RT5682S_HP_IMP_GAIN_2: + case RT5682S_SIDETONE_CTRL: + case RT5682S_STO1_ADC_MIXER: + case RT5682S_AD_DA_MIXER: + case RT5682S_STO1_DAC_MIXER: + case RT5682S_A_DAC1_MUX: + case RT5682S_DIG_INF2_DATA: + case RT5682S_REC_MIXER: + case RT5682S_CAL_REC: + case RT5682S_HP_ANA_OST_CTRL_1...RT5682S_HP_ANA_OST_CTRL_3: + case RT5682S_PWR_DIG_1...RT5682S_PWR_MIXER: + case RT5682S_MB_CTRL: + case RT5682S_CLK_GATE_TCON_1...RT5682S_CLK_GATE_TCON_3: + case RT5682S_CLK_DET...RT5682S_LPF_AD_DMIC: + case RT5682S_I2S1_SDP: + case RT5682S_I2S2_SDP: + case RT5682S_ADDA_CLK_1: + case RT5682S_ADDA_CLK_2: + case RT5682S_I2S1_F_DIV_CTRL_1: + case RT5682S_I2S1_F_DIV_CTRL_2: + case RT5682S_TDM_CTRL: + case RT5682S_TDM_ADDA_CTRL_1: + case RT5682S_TDM_ADDA_CTRL_2: + case RT5682S_DATA_SEL_CTRL_1: + case RT5682S_TDM_TCON_CTRL_1: + case RT5682S_TDM_TCON_CTRL_2: + case RT5682S_GLB_CLK: + case RT5682S_PLL_TRACK_1...RT5682S_PLL_TRACK_6: + case RT5682S_PLL_TRACK_11: + case RT5682S_DEPOP_1: + case RT5682S_HP_CHARGE_PUMP_1: + case RT5682S_HP_CHARGE_PUMP_2: + case RT5682S_HP_CHARGE_PUMP_3: + case RT5682S_MICBIAS_1...RT5682S_MICBIAS_3: + case RT5682S_PLL_TRACK_12...RT5682S_PLL_CTRL_7: + case RT5682S_RC_CLK_CTRL: + case RT5682S_I2S2_M_CLK_CTRL_1: + case RT5682S_I2S2_F_DIV_CTRL_1: + case RT5682S_I2S2_F_DIV_CTRL_2: + case RT5682S_IRQ_CTRL_1...RT5682S_IRQ_CTRL_4: + case RT5682S_INT_ST_1: + case RT5682S_GPIO_CTRL_1: + case RT5682S_GPIO_CTRL_2: + case RT5682S_GPIO_ST: + case RT5682S_HP_AMP_DET_CTRL_1: + case RT5682S_MID_HP_AMP_DET: + case RT5682S_LOW_HP_AMP_DET: + case RT5682S_DELAY_BUF_CTRL: + case RT5682S_SV_ZCD_1: + case RT5682S_SV_ZCD_2: + case RT5682S_IL_CMD_1...RT5682S_IL_CMD_6: + case RT5682S_4BTN_IL_CMD_1...RT5682S_4BTN_IL_CMD_7: + case RT5682S_ADC_STO1_HP_CTRL_1: + case RT5682S_ADC_STO1_HP_CTRL_2: + case RT5682S_AJD1_CTRL: + case RT5682S_JD_CTRL_1: + case RT5682S_DUMMY_1...RT5682S_DUMMY_3: + case RT5682S_DAC_ADC_DIG_VOL1: + case RT5682S_BIAS_CUR_CTRL_2...RT5682S_BIAS_CUR_CTRL_10: + case RT5682S_VREF_REC_OP_FB_CAP_CTRL_1: + case RT5682S_VREF_REC_OP_FB_CAP_CTRL_2: + case RT5682S_CHARGE_PUMP_1: + case RT5682S_DIG_IN_CTRL_1: + case RT5682S_PAD_DRIVING_CTRL: + case RT5682S_CHOP_DAC_1: + case RT5682S_CHOP_DAC_2: + case RT5682S_CHOP_ADC: + case RT5682S_CALIB_ADC_CTRL: + case RT5682S_VOL_TEST: + case RT5682S_SPKVDD_DET_ST: + case RT5682S_TEST_MODE_CTRL_1...RT5682S_TEST_MODE_CTRL_4: + case RT5682S_PLL_INTERNAL_1...RT5682S_PLL_INTERNAL_4: + case RT5682S_STO_NG2_CTRL_1...RT5682S_STO_NG2_CTRL_10: + case RT5682S_STO1_DAC_SIL_DET: + case RT5682S_SIL_PSV_CTRL1: + case RT5682S_SIL_PSV_CTRL2: + case RT5682S_SIL_PSV_CTRL3: + case RT5682S_SIL_PSV_CTRL4: + case RT5682S_SIL_PSV_CTRL5: + case RT5682S_HP_IMP_SENS_CTRL_1...RT5682S_HP_IMP_SENS_CTRL_46: + case RT5682S_HP_LOGIC_CTRL_1...RT5682S_HP_LOGIC_CTRL_3: + case RT5682S_HP_CALIB_CTRL_1...RT5682S_HP_CALIB_CTRL_11: + case RT5682S_HP_CALIB_ST_1...RT5682S_HP_CALIB_ST_11: + case RT5682S_SAR_IL_CMD_1...RT5682S_SAR_IL_CMD_14: + case RT5682S_DUMMY_4...RT5682S_DUMMY_6: + case RT5682S_VERSION_ID_HIDE: + case RT5682S_VERSION_ID_CUS: + case RT5682S_SCAN_CTL: + case RT5682S_HP_AMP_DET: + case RT5682S_BIAS_CUR_CTRL_11: + case RT5682S_BIAS_CUR_CTRL_12: + case RT5682S_BIAS_CUR_CTRL_13: + case RT5682S_BIAS_CUR_CTRL_14: + case RT5682S_BIAS_CUR_CTRL_15: + case RT5682S_BIAS_CUR_CTRL_16: + case RT5682S_BIAS_CUR_CTRL_17: + case RT5682S_BIAS_CUR_CTRL_18: + case RT5682S_I2C_TRANS_CTRL: + case RT5682S_DUMMY_7: + case RT5682S_DUMMY_8: + case RT5682S_DMIC_FLOAT_DET: + case RT5682S_HA_CMP_OP_1...RT5682S_HA_CMP_OP_13: + case RT5682S_HA_CMP_OP_14...RT5682S_HA_CMP_OP_25: + case RT5682S_NEW_CBJ_DET_CTL_1...RT5682S_NEW_CBJ_DET_CTL_16: + case RT5682S_DA_FILTER_1...RT5682S_DA_FILTER_5: + case RT5682S_CLK_SW_TEST_1: + case RT5682S_CLK_SW_TEST_2: + case RT5682S_CLK_SW_TEST_3...RT5682S_CLK_SW_TEST_14: + case RT5682S_EFUSE_MANU_WRITE_1...RT5682S_EFUSE_MANU_WRITE_6: + case RT5682S_EFUSE_READ_1...RT5682S_EFUSE_READ_18: + case RT5682S_EFUSE_TIMING_CTL_1: + case RT5682S_EFUSE_TIMING_CTL_2: + case RT5682S_PILOT_DIG_CTL_1: + case RT5682S_PILOT_DIG_CTL_2: + case RT5682S_HP_AMP_DET_CTL_1...RT5682S_HP_AMP_DET_CTL_4: + return true; + default: + return false; + } +} + +static void rt5682s_reset(struct rt5682s_priv *rt5682s) +{ + regmap_write(rt5682s->regmap, RT5682S_RESET, 0); +} + +static int rt5682s_button_detect(struct snd_soc_component *component) +{ + int btn_type, val; + + val = snd_soc_component_read(component, RT5682S_4BTN_IL_CMD_1); + btn_type = val & 0xfff0; + snd_soc_component_write(component, RT5682S_4BTN_IL_CMD_1, val); + dev_dbg(component->dev, "%s btn_type=%x\n", __func__, btn_type); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_2, + RT5682S_SAR_ADC_PSV_MASK, RT5682S_SAR_ADC_PSV_ENTRY); + + return btn_type; +} + +enum { + SAR_PWR_OFF, + SAR_PWR_NORMAL, + SAR_PWR_SAVING, +}; + +static void rt5682s_sar_power_mode(struct snd_soc_component *component, int mode) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + mutex_lock(&rt5682s->sar_mutex); + + switch (mode) { + case SAR_PWR_SAVING: + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3, + RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_DIS); + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, + RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK, + RT5682S_CTRL_MB1_REG | RT5682S_CTRL_MB2_REG); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK | + RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS | + RT5682S_SAR_BUTDET_POW_SAV | RT5682S_SAR_SEL_MB1_2_MANU); + usleep_range(5000, 5500); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_BUTDET_MASK, RT5682S_SAR_BUTDET_EN); + usleep_range(5000, 5500); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_2, + RT5682S_SAR_ADC_PSV_MASK, RT5682S_SAR_ADC_PSV_ENTRY); + break; + case SAR_PWR_NORMAL: + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3, + RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_EN); + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, + RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK, + RT5682S_CTRL_MB1_FSM | RT5682S_CTRL_MB2_FSM); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_SEL_MB1_2_AUTO); + usleep_range(5000, 5500); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK, + RT5682S_SAR_BUTDET_EN | RT5682S_SAR_BUTDET_POW_NORM); + break; + case SAR_PWR_OFF: + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, + RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK, + RT5682S_CTRL_MB1_FSM | RT5682S_CTRL_MB2_FSM); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK | + RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS | + RT5682S_SAR_BUTDET_POW_SAV | RT5682S_SAR_SEL_MB1_2_MANU); + break; + default: + dev_err(component->dev, "Invalid SAR Power mode: %d\n", mode); + break; + } + + mutex_unlock(&rt5682s->sar_mutex); +} + +static void rt5682s_enable_push_button_irq(struct snd_soc_component *component) +{ + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13, + RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_BTN); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK | + RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_EN | + RT5682S_SAR_BUTDET_POW_NORM | RT5682S_SAR_SEL_MB1_2_AUTO); + snd_soc_component_write(component, RT5682S_IL_CMD_1, 0x0040); + snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2, + RT5682S_4BTN_IL_MASK | RT5682S_4BTN_IL_RST_MASK, + RT5682S_4BTN_IL_EN | RT5682S_4BTN_IL_NOR); + snd_soc_component_update_bits(component, RT5682S_IRQ_CTRL_3, + RT5682S_IL_IRQ_MASK, RT5682S_IL_IRQ_EN); +} + +static void rt5682s_disable_push_button_irq(struct snd_soc_component *component) +{ + snd_soc_component_update_bits(component, RT5682S_IRQ_CTRL_3, + RT5682S_IL_IRQ_MASK, RT5682S_IL_IRQ_DIS); + snd_soc_component_update_bits(component, RT5682S_4BTN_IL_CMD_2, + RT5682S_4BTN_IL_MASK, RT5682S_4BTN_IL_DIS); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13, + RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_TYPE); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_BUTDET_MASK | RT5682S_SAR_BUTDET_POW_MASK | + RT5682S_SAR_SEL_MB1_2_CTL_MASK, RT5682S_SAR_BUTDET_DIS | + RT5682S_SAR_BUTDET_POW_SAV | RT5682S_SAR_SEL_MB1_2_MANU); +} + +/** + * rt5682s_headset_detect - Detect headset. + * @component: SoC audio component device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ +static int rt5682s_headset_detect(struct snd_soc_component *component, int jack_insert) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + unsigned int val, count; + int jack_type = 0; + + if (jack_insert) { + rt5682s_disable_push_button_irq(component); + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, + RT5682S_PWR_VREF1 | RT5682S_PWR_VREF2 | RT5682S_PWR_MB, + RT5682S_PWR_VREF1 | RT5682S_PWR_VREF2 | RT5682S_PWR_MB); + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, + RT5682S_PWR_FV1 | RT5682S_PWR_FV2, 0); + usleep_range(15000, 20000); + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, + RT5682S_PWR_FV1 | RT5682S_PWR_FV2, + RT5682S_PWR_FV1 | RT5682S_PWR_FV2); + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3, + RT5682S_PWR_CBJ, RT5682S_PWR_CBJ); + snd_soc_component_write(component, RT5682S_SAR_IL_CMD_3, 0x0365); + snd_soc_component_update_bits(component, RT5682S_HP_CHARGE_PUMP_2, + RT5682S_OSW_L_MASK | RT5682S_OSW_R_MASK, + RT5682S_OSW_L_DIS | RT5682S_OSW_R_DIS); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_13, + RT5682S_SAR_SOUR_MASK, RT5682S_SAR_SOUR_TYPE); + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3, + RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_EN); + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, + RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_LOW); + usleep_range(45000, 50000); + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, + RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_HIGH); + + count = 0; + do { + usleep_range(10000, 15000); + val = snd_soc_component_read(component, RT5682S_CBJ_CTRL_2) + & RT5682S_JACK_TYPE_MASK; + count++; + } while (val == 0 && count < 50); + + dev_dbg(component->dev, "%s, val=%d, count=%d\n", __func__, val, count); + + switch (val) { + case 0x1: + case 0x2: + jack_type = SND_JACK_HEADSET; + snd_soc_component_write(component, RT5682S_SAR_IL_CMD_3, 0x024c); + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, + RT5682S_FAST_OFF_MASK, RT5682S_FAST_OFF_EN); + snd_soc_component_update_bits(component, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_SEL_MB1_2_MASK, val << RT5682S_SAR_SEL_MB1_2_SFT); + rt5682s_enable_push_button_irq(component); + rt5682s_sar_power_mode(component, SAR_PWR_SAVING); + break; + default: + jack_type = SND_JACK_HEADPHONE; + break; + } + snd_soc_component_update_bits(component, RT5682S_HP_CHARGE_PUMP_2, + RT5682S_OSW_L_MASK | RT5682S_OSW_R_MASK, + RT5682S_OSW_L_EN | RT5682S_OSW_R_EN); + usleep_range(35000, 40000); + } else { + rt5682s_sar_power_mode(component, SAR_PWR_OFF); + rt5682s_disable_push_button_irq(component); + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, + RT5682S_TRIG_JD_MASK, RT5682S_TRIG_JD_LOW); + + if (!rt5682s->wclk_enabled) { + snd_soc_component_update_bits(component, + RT5682S_PWR_ANLG_1, RT5682S_PWR_VREF2 | RT5682S_PWR_MB, 0); + } + + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3, + RT5682S_PWR_CBJ, 0); + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1, + RT5682S_FAST_OFF_MASK, RT5682S_FAST_OFF_DIS); + snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_3, + RT5682S_CBJ_IN_BUF_MASK, RT5682S_CBJ_IN_BUF_DIS); + jack_type = 0; + } + + dev_dbg(component->dev, "jack_type = %d\n", jack_type); + + return jack_type; +} + +static void rt5682s_jack_detect_handler(struct work_struct *work) +{ + struct rt5682s_priv *rt5682s = + container_of(work, struct rt5682s_priv, jack_detect_work.work); + struct snd_soc_dapm_context *dapm; + int val, btn_type; + + if (!rt5682s->component || + !snd_soc_card_is_instantiated(rt5682s->component->card)) { + /* card not yet ready, try later */ + mod_delayed_work(system_power_efficient_wq, + &rt5682s->jack_detect_work, msecs_to_jiffies(15)); + return; + } + + dapm = snd_soc_component_get_dapm(rt5682s->component); + + snd_soc_dapm_mutex_lock(dapm); + mutex_lock(&rt5682s->calibrate_mutex); + mutex_lock(&rt5682s->wclk_mutex); + + val = snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL) + & RT5682S_JDH_RS_MASK; + if (!val) { + /* jack in */ + if (rt5682s->jack_type == 0) { + /* jack was out, report jack type */ + rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 1); + rt5682s->irq_work_delay_time = 0; + } else if ((rt5682s->jack_type & SND_JACK_HEADSET) == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + rt5682s->jack_type = SND_JACK_HEADSET; + btn_type = rt5682s_button_detect(rt5682s->component); + /** + * rt5682s can report three kinds of button behavior, + * one click, double click and hold. However, + * currently we will report button pressed/released + * event. So all the three button behaviors are + * treated as button pressed. + */ + switch (btn_type) { + case 0x8000: + case 0x4000: + case 0x2000: + rt5682s->jack_type |= SND_JACK_BTN_0; + break; + case 0x1000: + case 0x0800: + case 0x0400: + rt5682s->jack_type |= SND_JACK_BTN_1; + break; + case 0x0200: + case 0x0100: + case 0x0080: + rt5682s->jack_type |= SND_JACK_BTN_2; + break; + case 0x0040: + case 0x0020: + case 0x0010: + rt5682s->jack_type |= SND_JACK_BTN_3; + break; + case 0x0000: /* unpressed */ + break; + default: + dev_err(rt5682s->component->dev, + "Unexpected button code 0x%04x\n", btn_type); + break; + } + } + } else { + /* jack out */ + rt5682s->jack_type = rt5682s_headset_detect(rt5682s->component, 0); + rt5682s->irq_work_delay_time = 50; + } + + mutex_unlock(&rt5682s->wclk_mutex); + mutex_unlock(&rt5682s->calibrate_mutex); + snd_soc_dapm_mutex_unlock(dapm); + + snd_soc_jack_report(rt5682s->hs_jack, rt5682s->jack_type, + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (rt5682s->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + schedule_delayed_work(&rt5682s->jd_check_work, 0); + else + cancel_delayed_work_sync(&rt5682s->jd_check_work); +} + +static void rt5682s_jd_check_handler(struct work_struct *work) +{ + struct rt5682s_priv *rt5682s = + container_of(work, struct rt5682s_priv, jd_check_work.work); + + if (snd_soc_component_read(rt5682s->component, RT5682S_AJD1_CTRL) & RT5682S_JDH_RS_MASK) { + /* jack out */ + schedule_delayed_work(&rt5682s->jack_detect_work, 0); + } else { + schedule_delayed_work(&rt5682s->jd_check_work, 500); + } +} + +static irqreturn_t rt5682s_irq(int irq, void *data) +{ + struct rt5682s_priv *rt5682s = data; + + mod_delayed_work(system_power_efficient_wq, &rt5682s->jack_detect_work, + msecs_to_jiffies(rt5682s->irq_work_delay_time)); + + return IRQ_HANDLED; +} + +static int rt5682s_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + int btndet_delay = 16; + + rt5682s->hs_jack = hs_jack; + + if (!hs_jack) { + regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2, + RT5682S_JD1_EN_MASK, RT5682S_JD1_DIS); + regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL, + RT5682S_POW_JDH, 0); + cancel_delayed_work_sync(&rt5682s->jack_detect_work); + + return 0; + } + + switch (rt5682s->pdata.jd_src) { + case RT5682S_JD1: + regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_5, + RT5682S_JD_FAST_OFF_SRC_MASK, RT5682S_JD_FAST_OFF_SRC_JDH); + regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_2, + RT5682S_EXT_JD_SRC, RT5682S_EXT_JD_SRC_MANUAL); + regmap_update_bits(rt5682s->regmap, RT5682S_CBJ_CTRL_1, + RT5682S_EMB_JD_MASK | RT5682S_DET_TYPE | + RT5682S_POL_FAST_OFF_MASK | RT5682S_MIC_CAP_MASK, + RT5682S_EMB_JD_EN | RT5682S_DET_TYPE | + RT5682S_POL_FAST_OFF_HIGH | RT5682S_MIC_CAP_HS); + regmap_update_bits(rt5682s->regmap, RT5682S_SAR_IL_CMD_1, + RT5682S_SAR_POW_MASK, RT5682S_SAR_POW_EN); + regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1, + RT5682S_GP1_PIN_MASK, RT5682S_GP1_PIN_IRQ); + regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_3, + RT5682S_PWR_BGLDO, RT5682S_PWR_BGLDO); + regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_2, + RT5682S_PWR_JD_MASK, RT5682S_PWR_JD_ENABLE); + regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL, + RT5682S_POW_IRQ | RT5682S_POW_JDH, RT5682S_POW_IRQ | RT5682S_POW_JDH); + regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2, + RT5682S_JD1_EN_MASK | RT5682S_JD1_POL_MASK, + RT5682S_JD1_EN | RT5682S_JD1_POL_NOR); + regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_4, + RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK, + (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay)); + regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_5, + RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK, + (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay)); + regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_6, + RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK, + (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay)); + regmap_update_bits(rt5682s->regmap, RT5682S_4BTN_IL_CMD_7, + RT5682S_4BTN_IL_HOLD_WIN_MASK | RT5682S_4BTN_IL_CLICK_WIN_MASK, + (btndet_delay << RT5682S_4BTN_IL_HOLD_WIN_SFT | btndet_delay)); + + mod_delayed_work(system_power_efficient_wq, + &rt5682s->jack_detect_work, msecs_to_jiffies(250)); + break; + + case RT5682S_JD_NULL: + regmap_update_bits(rt5682s->regmap, RT5682S_IRQ_CTRL_2, + RT5682S_JD1_EN_MASK, RT5682S_JD1_DIS); + regmap_update_bits(rt5682s->regmap, RT5682S_RC_CLK_CTRL, + RT5682S_POW_JDH, 0); + break; + + default: + dev_warn(component->dev, "Wrong JD source\n"); + break; + } + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9562, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); +static const DECLARE_TLV_DB_SCALE(cbj_bst_tlv, -1200, 150, 0); + +static const struct snd_kcontrol_new rt5682s_snd_controls[] = { + /* DAC Digital Volume */ + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682S_DAC1_DIG_VOL, + RT5682S_L_VOL_SFT + 1, RT5682S_R_VOL_SFT + 1, 127, 0, dac_vol_tlv), + + /* CBJ Boost Volume */ + SOC_SINGLE_TLV("CBJ Boost Volume", RT5682S_REC_MIXER, + RT5682S_BST_CBJ_SFT, 35, 0, cbj_bst_tlv), + + /* ADC Digital Volume Control */ + SOC_DOUBLE("STO1 ADC Capture Switch", RT5682S_STO1_ADC_DIG_VOL, + RT5682S_L_MUTE_SFT, RT5682S_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("STO1 ADC Capture Volume", RT5682S_STO1_ADC_DIG_VOL, + RT5682S_L_VOL_SFT + 1, RT5682S_R_VOL_SFT + 1, 63, 0, adc_vol_tlv), + + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("STO1 ADC Boost Gain Volume", RT5682S_STO1_ADC_BOOST, + RT5682S_STO1_ADC_L_BST_SFT, RT5682S_STO1_ADC_R_BST_SFT, 3, 0, adc_bst_tlv), +}; + +/** + * rt5682s_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @component: SoC audio component device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5682S can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the component driver will turn on + * ASRC for these filters if ASRC is selected as their clock source. + */ +int rt5682s_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src) +{ + switch (clk_src) { + case RT5682S_CLK_SEL_SYS: + case RT5682S_CLK_SEL_I2S1_ASRC: + case RT5682S_CLK_SEL_I2S2_ASRC: + break; + + default: + return -EINVAL; + } + + if (filter_mask & RT5682S_DA_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_2, + RT5682S_FILTER_CLK_SEL_MASK, clk_src << RT5682S_FILTER_CLK_SEL_SFT); + } + + if (filter_mask & RT5682S_AD_STEREO1_FILTER) { + snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_3, + RT5682S_FILTER_CLK_SEL_MASK, clk_src << RT5682S_FILTER_CLK_SEL_SFT); + } + + snd_soc_component_update_bits(component, RT5682S_PLL_TRACK_11, + RT5682S_ASRCIN_AUTO_CLKOUT_MASK, RT5682S_ASRCIN_AUTO_CLKOUT_EN); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5682s_sel_asrc_clk_src); + +static int rt5682s_div_sel(struct rt5682s_priv *rt5682s, + int target, const int div[], int size) +{ + int i; + + if (rt5682s->sysclk < target) { + dev_err(rt5682s->component->dev, + "sysclk rate %d is too low\n", rt5682s->sysclk); + return 0; + } + + for (i = 0; i < size - 1; i++) { + dev_dbg(rt5682s->component->dev, "div[%d]=%d\n", i, div[i]); + if (target * div[i] == rt5682s->sysclk) + return i; + if (target * div[i + 1] > rt5682s->sysclk) { + dev_dbg(rt5682s->component->dev, + "can't find div for sysclk %d\n", rt5682s->sysclk); + return i; + } + } + + if (target * div[i] < rt5682s->sysclk) + dev_err(rt5682s->component->dev, + "sysclk rate %d is too high\n", rt5682s->sysclk); + + return size - 1; +} + +static int get_clk_info(int sclk, int rate) +{ + int i; + static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + int idx, dmic_clk_rate = 3072000; + static const int div[] = {2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128}; + + if (rt5682s->pdata.dmic_clk_rate) + dmic_clk_rate = rt5682s->pdata.dmic_clk_rate; + + idx = rt5682s_div_sel(rt5682s, dmic_clk_rate, div, ARRAY_SIZE(div)); + + snd_soc_component_update_bits(component, RT5682S_DMIC_CTRL_1, + RT5682S_DMIC_CLK_MASK, idx << RT5682S_DMIC_CLK_SFT); + + return 0; +} + + +static int rt5682s_set_pllb_power(struct rt5682s_priv *rt5682s, int on) +{ + struct snd_soc_component *component = rt5682s->component; + + if (on) { + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3, + RT5682S_PWR_LDO_PLLB | RT5682S_PWR_BIAS_PLLB | RT5682S_PWR_PLLB, + RT5682S_PWR_LDO_PLLB | RT5682S_PWR_BIAS_PLLB | RT5682S_PWR_PLLB); + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3, + RT5682S_RSTB_PLLB, RT5682S_RSTB_PLLB); + } else { + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_3, + RT5682S_PWR_LDO_PLLB | RT5682S_PWR_BIAS_PLLB | + RT5682S_RSTB_PLLB | RT5682S_PWR_PLLB, 0); + } + + return 0; +} + +static int set_pllb_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + int on = 0; + + if (rt5682s->wclk_enabled) + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) + on = 1; + + rt5682s_set_pllb_power(rt5682s, on); + + return 0; +} + +static void rt5682s_set_filter_clk(struct rt5682s_priv *rt5682s, int reg, int ref) +{ + struct snd_soc_component *component = rt5682s->component; + int idx; + static const int div_f[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48}; + static const int div_o[] = {1, 2, 4, 6, 8, 12, 16, 24, 32, 48}; + + idx = rt5682s_div_sel(rt5682s, ref, div_f, ARRAY_SIZE(div_f)); + + snd_soc_component_update_bits(component, reg, + RT5682S_FILTER_CLK_DIV_MASK, idx << RT5682S_FILTER_CLK_DIV_SFT); + + /* select over sample rate */ + for (idx = 0; idx < ARRAY_SIZE(div_o); idx++) { + if (rt5682s->sysclk <= 12288000 * div_o[idx]) + break; + } + + snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_1, + RT5682S_ADC_OSR_MASK | RT5682S_DAC_OSR_MASK, + (idx << RT5682S_ADC_OSR_SFT) | (idx << RT5682S_DAC_OSR_SFT)); +} + +static int set_filter_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + int ref, reg, val; + + val = snd_soc_component_read(component, RT5682S_GPIO_CTRL_1) + & RT5682S_GP4_PIN_MASK; + + if (w->shift == RT5682S_PWR_ADC_S1F_BIT && val == RT5682S_GP4_PIN_ADCDAT2) + ref = 256 * rt5682s->lrck[RT5682S_AIF2]; + else + ref = 256 * rt5682s->lrck[RT5682S_AIF1]; + + if (w->shift == RT5682S_PWR_ADC_S1F_BIT) + reg = RT5682S_PLL_TRACK_3; + else + reg = RT5682S_PLL_TRACK_2; + + rt5682s_set_filter_clk(rt5682s, reg, ref); + + return 0; +} + +static int set_dmic_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + unsigned int delay = 50, val; + + if (rt5682s->pdata.dmic_delay) + delay = rt5682s->pdata.dmic_delay; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = (snd_soc_component_read(component, RT5682S_GLB_CLK) + & RT5682S_SCLK_SRC_MASK) >> RT5682S_SCLK_SRC_SFT; + if (val == RT5682S_CLK_SRC_PLL1 || val == RT5682S_CLK_SRC_PLL2) + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, + RT5682S_PWR_VREF2 | RT5682S_PWR_MB, + RT5682S_PWR_VREF2 | RT5682S_PWR_MB); + + /*Add delay to avoid pop noise*/ + msleep(delay); + break; + + case SND_SOC_DAPM_POST_PMD: + if (!rt5682s->jack_type && !rt5682s->wclk_enabled) { + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, + RT5682S_PWR_VREF2 | RT5682S_PWR_MB, 0); + } + break; + } + + return 0; +} + +static void rt5682s_set_i2s(struct rt5682s_priv *rt5682s, int id, int on) +{ + struct snd_soc_component *component = rt5682s->component; + int pre_div; + unsigned int p_reg, p_mask, p_sft; + unsigned int c_reg, c_mask, c_sft; + + if (id == RT5682S_AIF1) { + c_reg = RT5682S_ADDA_CLK_1; + c_mask = RT5682S_I2S_M_D_MASK; + c_sft = RT5682S_I2S_M_D_SFT; + p_reg = RT5682S_PWR_DIG_1; + p_mask = RT5682S_PWR_I2S1; + p_sft = RT5682S_PWR_I2S1_BIT; + } else { + c_reg = RT5682S_I2S2_M_CLK_CTRL_1; + c_mask = RT5682S_I2S2_M_D_MASK; + c_sft = RT5682S_I2S2_M_D_SFT; + p_reg = RT5682S_PWR_DIG_1; + p_mask = RT5682S_PWR_I2S2; + p_sft = RT5682S_PWR_I2S2_BIT; + } + + if (on && rt5682s->master[id]) { + pre_div = get_clk_info(rt5682s->sysclk, rt5682s->lrck[id]); + if (pre_div < 0) { + dev_err(component->dev, "get pre_div failed\n"); + return; + } + + dev_dbg(component->dev, "lrck is %dHz and pre_div is %d for iis %d master\n", + rt5682s->lrck[id], pre_div, id); + snd_soc_component_update_bits(component, c_reg, c_mask, pre_div << c_sft); + } + + snd_soc_component_update_bits(component, p_reg, p_mask, on << p_sft); +} + +static int set_i2s_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + int on = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) + on = 1; + + if (!strcmp(w->name, "I2S1") && !rt5682s->wclk_enabled) + rt5682s_set_i2s(rt5682s, RT5682S_AIF1, on); + else if (!strcmp(w->name, "I2S2")) + rt5682s_set_i2s(rt5682s, RT5682S_AIF2, on); + + return 0; +} + +static int is_sys_clk_from_plla(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + if ((rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL1) || + (rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL2 && rt5682s->pll_comb == USE_PLLAB)) + return 1; + + return 0; +} + +static int is_sys_clk_from_pllb(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + if (rt5682s->sysclk_src == RT5682S_CLK_SRC_PLL2) + return 1; + + return 0; +} + +static int is_using_asrc(struct snd_soc_dapm_widget *w, + struct snd_soc_dapm_widget *sink) +{ + unsigned int reg, sft, val; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (w->shift) { + case RT5682S_ADC_STO1_ASRC_SFT: + reg = RT5682S_PLL_TRACK_3; + sft = RT5682S_FILTER_CLK_SEL_SFT; + break; + case RT5682S_DAC_STO1_ASRC_SFT: + reg = RT5682S_PLL_TRACK_2; + sft = RT5682S_FILTER_CLK_SEL_SFT; + break; + default: + return 0; + } + + val = (snd_soc_component_read(component, reg) >> sft) & 0xf; + switch (val) { + case RT5682S_CLK_SEL_I2S1_ASRC: + case RT5682S_CLK_SEL_I2S2_ASRC: + return 1; + default: + return 0; + } +} + +static int rt5682s_hp_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, RT5682S_DEPOP_1, + RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN, + RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN); + usleep_range(15000, 20000); + snd_soc_component_update_bits(component, RT5682S_DEPOP_1, + RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN | + RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN, + RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN | + RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN); + snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_11, 0x6666); + snd_soc_component_write(component, RT5682S_BIAS_CUR_CTRL_12, 0xa82a); + + snd_soc_component_update_bits(component, RT5682S_HP_CTRL_2, + RT5682S_HPO_L_PATH_MASK | RT5682S_HPO_R_PATH_MASK | + RT5682S_HPO_SEL_IP_EN_SW, RT5682S_HPO_L_PATH_EN | + RT5682S_HPO_R_PATH_EN | RT5682S_HPO_IP_EN_GATING); + usleep_range(5000, 10000); + snd_soc_component_update_bits(component, RT5682S_HP_AMP_DET_CTL_1, + RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_L | RT5682S_CP_SW_SIZE_S); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, RT5682S_HP_CTRL_2, + RT5682S_HPO_L_PATH_MASK | RT5682S_HPO_R_PATH_MASK | + RT5682S_HPO_SEL_IP_EN_SW, 0); + snd_soc_component_update_bits(component, RT5682S_HP_AMP_DET_CTL_1, + RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_M); + snd_soc_component_update_bits(component, RT5682S_DEPOP_1, + RT5682S_LDO_PUMP_EN | RT5682S_PUMP_EN | + RT5682S_CAPLESS_L_EN | RT5682S_CAPLESS_R_EN, 0); + snd_soc_component_update_bits(component, RT5682S_DEPOP_1, + RT5682S_OUT_HP_L_EN | RT5682S_OUT_HP_R_EN, 0); + break; + } + + return 0; +} + +static int rt5682s_stereo1_adc_mixl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + unsigned int delay = 0; + + if (rt5682s->pdata.amic_delay) + delay = rt5682s->pdata.amic_delay; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(delay); + snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL, + RT5682S_L_MUTE, 0); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, RT5682S_STO1_ADC_DIG_VOL, + RT5682S_L_MUTE, RT5682S_L_MUTE); + break; + } + + return 0; +} + +static int sar_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + if ((rt5682s->jack_type & SND_JACK_HEADSET) != SND_JACK_HEADSET) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + rt5682s_sar_power_mode(component, SAR_PWR_NORMAL); + break; + case SND_SOC_DAPM_POST_PMD: + rt5682s_sar_power_mode(component, SAR_PWR_SAVING); + break; + } + + return 0; +} + +/* Interface data select */ +static const char * const rt5682s_data_select[] = { + "L/R", "R/L", "L/L", "R/R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682s_if2_adc_enum, RT5682S_DIG_INF2_DATA, + RT5682S_IF2_ADC_SEL_SFT, rt5682s_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682s_if1_01_adc_enum, RT5682S_TDM_ADDA_CTRL_1, + RT5682S_IF1_ADC1_SEL_SFT, rt5682s_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682s_if1_23_adc_enum, RT5682S_TDM_ADDA_CTRL_1, + RT5682S_IF1_ADC2_SEL_SFT, rt5682s_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682s_if1_45_adc_enum, RT5682S_TDM_ADDA_CTRL_1, + RT5682S_IF1_ADC3_SEL_SFT, rt5682s_data_select); + +static SOC_ENUM_SINGLE_DECL(rt5682s_if1_67_adc_enum, RT5682S_TDM_ADDA_CTRL_1, + RT5682S_IF1_ADC4_SEL_SFT, rt5682s_data_select); + +static const struct snd_kcontrol_new rt5682s_if2_adc_swap_mux = + SOC_DAPM_ENUM("IF2 ADC Swap Mux", rt5682s_if2_adc_enum); + +static const struct snd_kcontrol_new rt5682s_if1_01_adc_swap_mux = + SOC_DAPM_ENUM("IF1 01 ADC Swap Mux", rt5682s_if1_01_adc_enum); + +static const struct snd_kcontrol_new rt5682s_if1_23_adc_swap_mux = + SOC_DAPM_ENUM("IF1 23 ADC Swap Mux", rt5682s_if1_23_adc_enum); + +static const struct snd_kcontrol_new rt5682s_if1_45_adc_swap_mux = + SOC_DAPM_ENUM("IF1 45 ADC Swap Mux", rt5682s_if1_45_adc_enum); + +static const struct snd_kcontrol_new rt5682s_if1_67_adc_swap_mux = + SOC_DAPM_ENUM("IF1 67 ADC Swap Mux", rt5682s_if1_67_adc_enum); + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5682s_sto1_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5682S_STO1_ADC_MIXER, + RT5682S_M_STO1_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5682S_STO1_ADC_MIXER, + RT5682S_M_STO1_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682s_sto1_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5682S_STO1_ADC_MIXER, + RT5682S_M_STO1_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5682S_STO1_ADC_MIXER, + RT5682S_M_STO1_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682s_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682S_AD_DA_MIXER, + RT5682S_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5682S_AD_DA_MIXER, + RT5682S_M_DAC1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682s_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5682S_AD_DA_MIXER, + RT5682S_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC1 Switch", RT5682S_AD_DA_MIXER, + RT5682S_M_DAC1_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682s_sto1_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5682S_STO1_DAC_MIXER, + RT5682S_M_DAC_L1_STO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5682S_STO1_DAC_MIXER, + RT5682S_M_DAC_R1_STO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682s_sto1_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5682S_STO1_DAC_MIXER, + RT5682S_M_DAC_L1_STO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5682S_STO1_DAC_MIXER, + RT5682S_M_DAC_R1_STO_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5682s_rec1_l_mix[] = { + SOC_DAPM_SINGLE("CBJ Switch", RT5682S_REC_MIXER, + RT5682S_M_CBJ_RM1_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5682s_rec1_r_mix[] = { + SOC_DAPM_SINGLE("CBJ Switch", RT5682S_REC_MIXER, + RT5682S_M_CBJ_RM1_R_SFT, 1, 1), +}; + +/* STO1 ADC1 Source */ +/* MX-26 [13] [5] */ +static const char * const rt5682s_sto1_adc1_src[] = { + "DAC MIX", "ADC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc1l_enum, RT5682S_STO1_ADC_MIXER, + RT5682S_STO1_ADC1L_SRC_SFT, rt5682s_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5682s_sto1_adc1l_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682s_sto1_adc1l_enum); + +static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc1r_enum, RT5682S_STO1_ADC_MIXER, + RT5682S_STO1_ADC1R_SRC_SFT, rt5682s_sto1_adc1_src); + +static const struct snd_kcontrol_new rt5682s_sto1_adc1r_mux = + SOC_DAPM_ENUM("Stereo1 ADC1L Source", rt5682s_sto1_adc1r_enum); + +/* STO1 ADC Source */ +/* MX-26 [11:10] [3:2] */ +static const char * const rt5682s_sto1_adc_src[] = { + "ADC1 L", "ADC1 R" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adcl_enum, RT5682S_STO1_ADC_MIXER, + RT5682S_STO1_ADCL_SRC_SFT, rt5682s_sto1_adc_src); + +static const struct snd_kcontrol_new rt5682s_sto1_adcl_mux = + SOC_DAPM_ENUM("Stereo1 ADCL Source", rt5682s_sto1_adcl_enum); + +static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adcr_enum, RT5682S_STO1_ADC_MIXER, + RT5682S_STO1_ADCR_SRC_SFT, rt5682s_sto1_adc_src); + +static const struct snd_kcontrol_new rt5682s_sto1_adcr_mux = + SOC_DAPM_ENUM("Stereo1 ADCR Source", rt5682s_sto1_adcr_enum); + +/* STO1 ADC2 Source */ +/* MX-26 [12] [4] */ +static const char * const rt5682s_sto1_adc2_src[] = { + "DAC MIX", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc2l_enum, RT5682S_STO1_ADC_MIXER, + RT5682S_STO1_ADC2L_SRC_SFT, rt5682s_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5682s_sto1_adc2l_mux = + SOC_DAPM_ENUM("Stereo1 ADC2L Source", rt5682s_sto1_adc2l_enum); + +static SOC_ENUM_SINGLE_DECL(rt5682s_sto1_adc2r_enum, RT5682S_STO1_ADC_MIXER, + RT5682S_STO1_ADC2R_SRC_SFT, rt5682s_sto1_adc2_src); + +static const struct snd_kcontrol_new rt5682s_sto1_adc2r_mux = + SOC_DAPM_ENUM("Stereo1 ADC2R Source", rt5682s_sto1_adc2r_enum); + +/* MX-79 [6:4] I2S1 ADC data location */ +static const unsigned int rt5682s_if1_adc_slot_values[] = { + 0, 2, 4, 6, +}; + +static const char * const rt5682s_if1_adc_slot_src[] = { + "Slot 0", "Slot 2", "Slot 4", "Slot 6" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682s_if1_adc_slot_enum, + RT5682S_TDM_CTRL, RT5682S_TDM_ADC_LCA_SFT, RT5682S_TDM_ADC_LCA_MASK, + rt5682s_if1_adc_slot_src, rt5682s_if1_adc_slot_values); + +static const struct snd_kcontrol_new rt5682s_if1_adc_slot_mux = + SOC_DAPM_ENUM("IF1 ADC Slot location", rt5682s_if1_adc_slot_enum); + +/* Analog DAC L1 Source, Analog DAC R1 Source*/ +/* MX-2B [4], MX-2B [0]*/ +static const char * const rt5682s_alg_dac1_src[] = { + "Stereo1 DAC Mixer", "DAC1" +}; + +static SOC_ENUM_SINGLE_DECL(rt5682s_alg_dac_l1_enum, RT5682S_A_DAC1_MUX, + RT5682S_A_DACL1_SFT, rt5682s_alg_dac1_src); + +static const struct snd_kcontrol_new rt5682s_alg_dac_l1_mux = + SOC_DAPM_ENUM("Analog DAC L1 Source", rt5682s_alg_dac_l1_enum); + +static SOC_ENUM_SINGLE_DECL(rt5682s_alg_dac_r1_enum, RT5682S_A_DAC1_MUX, + RT5682S_A_DACR1_SFT, rt5682s_alg_dac1_src); + +static const struct snd_kcontrol_new rt5682s_alg_dac_r1_mux = + SOC_DAPM_ENUM("Analog DAC R1 Source", rt5682s_alg_dac_r1_enum); + +static const unsigned int rt5682s_adcdat_pin_values[] = { + 1, 3, +}; + +static const char * const rt5682s_adcdat_pin_select[] = { + "ADCDAT1", "ADCDAT2", +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(rt5682s_adcdat_pin_enum, + RT5682S_GPIO_CTRL_1, RT5682S_GP4_PIN_SFT, RT5682S_GP4_PIN_MASK, + rt5682s_adcdat_pin_select, rt5682s_adcdat_pin_values); + +static const struct snd_kcontrol_new rt5682s_adcdat_pin_ctrl = + SOC_DAPM_ENUM("ADCDAT", rt5682s_adcdat_pin_enum); + +static const struct snd_soc_dapm_widget rt5682s_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("LDO MB1", RT5682S_PWR_ANLG_3, + RT5682S_PWR_LDO_MB1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO MB2", RT5682S_PWR_ANLG_3, + RT5682S_PWR_LDO_MB2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("LDO", RT5682S_PWR_ANLG_3, + RT5682S_PWR_LDO_BIT, 0, NULL, 0), + + /* PLL Powers */ + SND_SOC_DAPM_SUPPLY_S("PLLA_LDO", 0, RT5682S_PWR_ANLG_3, + RT5682S_PWR_LDO_PLLA_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("PLLA_BIAS", 0, RT5682S_PWR_ANLG_3, + RT5682S_PWR_BIAS_PLLA_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("PLLA", 0, RT5682S_PWR_ANLG_3, + RT5682S_PWR_PLLA_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("PLLA_RST", 1, RT5682S_PWR_ANLG_3, + RT5682S_RSTB_PLLA_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLLB", SND_SOC_NOPM, 0, 0, + set_pllb_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* ASRC */ + SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682S_PLL_TRACK_1, + RT5682S_DAC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5682S_PLL_TRACK_1, + RT5682S_ADC_STO1_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AD ASRC", 1, RT5682S_PLL_TRACK_1, + RT5682S_AD_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DA ASRC", 1, RT5682S_PLL_TRACK_1, + RT5682S_DA_ASRC_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC ASRC", 1, RT5682S_PLL_TRACK_1, + RT5682S_DMIC_ASRC_SFT, 0, NULL, 0), + + /* Input Side */ + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5682S_PWR_ANLG_2, + RT5682S_PWR_MB1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", RT5682S_PWR_ANLG_2, + RT5682S_PWR_MB2_BIT, 0, NULL, 0), + + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC L1"), + SND_SOC_DAPM_INPUT("DMIC R1"), + + SND_SOC_DAPM_INPUT("IN1P"), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5682S_DMIC_CTRL_1, RT5682S_DMIC_1_EN_SFT, 0, + set_dmic_power, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Boost */ + SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5682s_rec1_l_mix, + ARRAY_SIZE(rt5682s_rec1_l_mix)), + SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5682s_rec1_r_mix, + ARRAY_SIZE(rt5682s_rec1_r_mix)), + SND_SOC_DAPM_SUPPLY("RECMIX1L Power", RT5682S_CAL_REC, + RT5682S_PWR_RM1_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5682S_CAL_REC, + RT5682S_PWR_RM1_R_BIT, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC1 L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC1 R", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("ADC1 L Power", RT5682S_PWR_DIG_1, + RT5682S_PWR_ADC_L1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 R Power", RT5682S_PWR_DIG_1, + RT5682S_PWR_ADC_R1_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC1 clock", RT5682S_CHOP_ADC, + RT5682S_CKGEN_ADC1_SFT, 0, NULL, 0), + + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_sto1_adc1l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_sto1_adc1r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_sto1_adc2l_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_sto1_adc2r_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC L Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_sto1_adcl_mux), + SND_SOC_DAPM_MUX("Stereo1 ADC R Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_sto1_adcr_mux), + SND_SOC_DAPM_MUX("IF1_ADC Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_if1_adc_slot_mux), + + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("ADC Stereo1 Filter", RT5682S_PWR_DIG_2, + RT5682S_PWR_ADC_S1F_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER_E("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5682s_sto1_adc_l_mix, ARRAY_SIZE(rt5682s_sto1_adc_l_mix), + rt5682s_stereo1_adc_mixl_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", RT5682S_STO1_ADC_DIG_VOL, + RT5682S_R_MUTE_SFT, 1, rt5682s_sto1_adc_r_mix, + ARRAY_SIZE(rt5682s_sto1_adc_r_mix)), + + /* ADC PGA */ + SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", SND_SOC_NOPM, 0, 0, + set_i2s_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("I2S2", SND_SOC_NOPM, 0, 0, + set_i2s_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("IF1 01 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_if1_01_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 23 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_if1_23_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 45 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_if1_45_adc_swap_mux), + SND_SOC_DAPM_MUX("IF1 67 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_if1_67_adc_swap_mux), + SND_SOC_DAPM_MUX("IF2 ADC Swap Mux", SND_SOC_NOPM, 0, 0, + &rt5682s_if2_adc_swap_mux), + + SND_SOC_DAPM_MUX("ADCDAT Mux", SND_SOC_NOPM, 0, 0, &rt5682s_adcdat_pin_ctrl), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, RT5682S_I2S1_SDP, + RT5682S_SEL_ADCDAT_SFT, 1), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, RT5682S_I2S2_SDP, + RT5682S_I2S2_PIN_CFG_SFT, 1), + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0, + rt5682s_dac_l_mix, ARRAY_SIZE(rt5682s_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0, + rt5682s_dac_r_mix, ARRAY_SIZE(rt5682s_dac_r_mix)), + + /* DAC channel Mux */ + SND_SOC_DAPM_MUX("DAC L1 Source", SND_SOC_NOPM, 0, 0, &rt5682s_alg_dac_l1_mux), + SND_SOC_DAPM_MUX("DAC R1 Source", SND_SOC_NOPM, 0, 0, &rt5682s_alg_dac_r1_mux), + + /* DAC Mixer */ + SND_SOC_DAPM_SUPPLY("DAC Stereo1 Filter", RT5682S_PWR_DIG_2, + RT5682S_PWR_DAC_S1F_BIT, 0, set_filter_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5682s_sto1_dac_l_mix, ARRAY_SIZE(rt5682s_sto1_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo1 DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5682s_sto1_dac_r_mix, ARRAY_SIZE(rt5682s_sto1_dac_r_mix)), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5682S_PWR_DIG_1, RT5682S_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5682S_PWR_DIG_1, RT5682S_PWR_DAC_R1_BIT, 0), + + /* HPO */ + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, rt5682s_hp_amp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + + /* CLK DET */ + SND_SOC_DAPM_SUPPLY("CLKDET SYS", RT5682S_CLK_DET, + RT5682S_SYS_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLKDET PLL1", RT5682S_CLK_DET, + RT5682S_PLL1_CLK_DET_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MCLK0 DET PWR", RT5682S_PWR_ANLG_2, + RT5682S_PWR_MCLK0_WD_BIT, 0, NULL, 0), + + /* SAR */ + SND_SOC_DAPM_SUPPLY("SAR", SND_SOC_NOPM, 0, 0, sar_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route rt5682s_dapm_routes[] = { + /*PLL*/ + {"ADC Stereo1 Filter", NULL, "PLLA", is_sys_clk_from_plla}, + {"ADC Stereo1 Filter", NULL, "PLLB", is_sys_clk_from_pllb}, + {"DAC Stereo1 Filter", NULL, "PLLA", is_sys_clk_from_plla}, + {"DAC Stereo1 Filter", NULL, "PLLB", is_sys_clk_from_pllb}, + {"PLLA", NULL, "PLLA_LDO"}, + {"PLLA", NULL, "PLLA_BIAS"}, + {"PLLA", NULL, "PLLA_RST"}, + + /*ASRC*/ + {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, + {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, + {"ADC STO1 ASRC", NULL, "AD ASRC"}, + {"ADC STO1 ASRC", NULL, "DA ASRC"}, + {"DAC STO1 ASRC", NULL, "AD ASRC"}, + {"DAC STO1 ASRC", NULL, "DA ASRC"}, + + {"CLKDET SYS", NULL, "MCLK0 DET PWR"}, + + {"BST1 CBJ", NULL, "IN1P"}, + {"BST1 CBJ", NULL, "SAR"}, + + {"RECMIX1L", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX1L", NULL, "RECMIX1L Power"}, + {"RECMIX1R", "CBJ Switch", "BST1 CBJ"}, + {"RECMIX1R", NULL, "RECMIX1R Power"}, + + {"ADC1 L", NULL, "RECMIX1L"}, + {"ADC1 L", NULL, "ADC1 L Power"}, + {"ADC1 L", NULL, "ADC1 clock"}, + {"ADC1 R", NULL, "RECMIX1R"}, + {"ADC1 R", NULL, "ADC1 R Power"}, + {"ADC1 R", NULL, "ADC1 clock"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC CLK", NULL, "DMIC ASRC"}, + + {"Stereo1 ADC L Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC L Mux", "ADC1 R", "ADC1 R"}, + {"Stereo1 ADC R Mux", "ADC1 L", "ADC1 L"}, + {"Stereo1 ADC R Mux", "ADC1 R", "ADC1 R"}, + + {"Stereo1 ADC L1 Mux", "ADC", "Stereo1 ADC L Mux"}, + {"Stereo1 ADC L1 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + {"Stereo1 ADC L2 Mux", "DMIC", "DMIC L1"}, + {"Stereo1 ADC L2 Mux", "DAC MIX", "Stereo1 DAC MIXL"}, + + {"Stereo1 ADC R1 Mux", "ADC", "Stereo1 ADC R Mux"}, + {"Stereo1 ADC R1 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + {"Stereo1 ADC R2 Mux", "DMIC", "DMIC R1"}, + {"Stereo1 ADC R2 Mux", "DAC MIX", "Stereo1 DAC MIXR"}, + + {"Stereo1 ADC MIXL", "ADC1 Switch", "Stereo1 ADC L1 Mux"}, + {"Stereo1 ADC MIXL", "ADC2 Switch", "Stereo1 ADC L2 Mux"}, + {"Stereo1 ADC MIXL", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"}, + {"Stereo1 ADC MIXR", "ADC2 Switch", "Stereo1 ADC R2 Mux"}, + {"Stereo1 ADC MIXR", NULL, "ADC Stereo1 Filter"}, + + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL"}, + {"Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR"}, + + {"IF1 01 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 01 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 23 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 45 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF1 67 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + + {"IF1_ADC Mux", "Slot 0", "IF1 01 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 2", "IF1 23 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 4", "IF1 45 ADC Swap Mux"}, + {"IF1_ADC Mux", "Slot 6", "IF1 67 ADC Swap Mux"}, + {"ADCDAT Mux", "ADCDAT1", "IF1_ADC Mux"}, + {"AIF1TX", NULL, "I2S1"}, + {"AIF1TX", NULL, "ADCDAT Mux"}, + {"IF2 ADC Swap Mux", "L/R", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "L/L", "Stereo1 ADC MIX"}, + {"IF2 ADC Swap Mux", "R/R", "Stereo1 ADC MIX"}, + {"ADCDAT Mux", "ADCDAT2", "IF2 ADC Swap Mux"}, + {"AIF2TX", NULL, "ADCDAT Mux"}, + + {"IF1 DAC1 L", NULL, "AIF1RX"}, + {"IF1 DAC1 L", NULL, "I2S1"}, + {"IF1 DAC1 L", NULL, "DAC Stereo1 Filter"}, + {"IF1 DAC1 R", NULL, "AIF1RX"}, + {"IF1 DAC1 R", NULL, "I2S1"}, + {"IF1 DAC1 R", NULL, "DAC Stereo1 Filter"}, + + {"DAC1 MIXL", "Stereo ADC Switch", "Stereo1 ADC MIXL"}, + {"DAC1 MIXL", "DAC1 Switch", "IF1 DAC1 L"}, + {"DAC1 MIXR", "Stereo ADC Switch", "Stereo1 ADC MIXR"}, + {"DAC1 MIXR", "DAC1 Switch", "IF1 DAC1 R"}, + + {"Stereo1 DAC MIXL", "DAC L1 Switch", "DAC1 MIXL"}, + {"Stereo1 DAC MIXL", "DAC R1 Switch", "DAC1 MIXR"}, + + {"Stereo1 DAC MIXR", "DAC R1 Switch", "DAC1 MIXR"}, + {"Stereo1 DAC MIXR", "DAC L1 Switch", "DAC1 MIXL"}, + + {"DAC L1 Source", "DAC1", "DAC1 MIXL"}, + {"DAC L1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXL"}, + {"DAC R1 Source", "DAC1", "DAC1 MIXR"}, + {"DAC R1 Source", "Stereo1 DAC Mixer", "Stereo1 DAC MIXR"}, + + {"DAC L1", NULL, "DAC L1 Source"}, + {"DAC R1", NULL, "DAC R1 Source"}, + + {"HP Amp", NULL, "DAC L1"}, + {"HP Amp", NULL, "DAC R1"}, + {"HP Amp", NULL, "CLKDET SYS"}, + {"HP Amp", NULL, "SAR"}, + + {"HPOL", NULL, "HP Amp"}, + {"HPOR", NULL, "HP Amp"}, +}; + +static int rt5682s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int cl, val = 0, tx_slotnum; + + if (tx_mask || rx_mask) + snd_soc_component_update_bits(component, + RT5682S_TDM_ADDA_CTRL_2, RT5682S_TDM_EN, RT5682S_TDM_EN); + else + snd_soc_component_update_bits(component, + RT5682S_TDM_ADDA_CTRL_2, RT5682S_TDM_EN, 0); + + /* Tx slot configuration */ + tx_slotnum = hweight_long(tx_mask); + if (tx_slotnum) { + if (tx_slotnum > slots) { + dev_err(component->dev, "Invalid or oversized Tx slots.\n"); + return -EINVAL; + } + val |= (tx_slotnum - 1) << RT5682S_TDM_ADC_DL_SFT; + } + + switch (slots) { + case 4: + val |= RT5682S_TDM_TX_CH_4; + val |= RT5682S_TDM_RX_CH_4; + break; + case 6: + val |= RT5682S_TDM_TX_CH_6; + val |= RT5682S_TDM_RX_CH_6; + break; + case 8: + val |= RT5682S_TDM_TX_CH_8; + val |= RT5682S_TDM_RX_CH_8; + break; + case 2: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682S_TDM_CTRL, + RT5682S_TDM_TX_CH_MASK | RT5682S_TDM_RX_CH_MASK | + RT5682S_TDM_ADC_DL_MASK, val); + + switch (slot_width) { + case 8: + if (tx_mask || rx_mask) + return -EINVAL; + cl = RT5682S_I2S1_TX_CHL_8 | RT5682S_I2S1_RX_CHL_8; + break; + case 16: + val = RT5682S_TDM_CL_16; + cl = RT5682S_I2S1_TX_CHL_16 | RT5682S_I2S1_RX_CHL_16; + break; + case 20: + val = RT5682S_TDM_CL_20; + cl = RT5682S_I2S1_TX_CHL_20 | RT5682S_I2S1_RX_CHL_20; + break; + case 24: + val = RT5682S_TDM_CL_24; + cl = RT5682S_I2S1_TX_CHL_24 | RT5682S_I2S1_RX_CHL_24; + break; + case 32: + val = RT5682S_TDM_CL_32; + cl = RT5682S_I2S1_TX_CHL_32 | RT5682S_I2S1_RX_CHL_32; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1, + RT5682S_TDM_CL_MASK, val); + snd_soc_component_update_bits(component, RT5682S_I2S1_SDP, + RT5682S_I2S1_TX_CHL_MASK | RT5682S_I2S1_RX_CHL_MASK, cl); + + return 0; +} + +static int rt5682s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + unsigned int len_1 = 0, len_2 = 0; + int frame_size; + + rt5682s->lrck[dai->id] = params_rate(params); + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(component->dev, "Unsupported frame size: %d\n", frame_size); + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + break; + case 20: + len_1 |= RT5682S_I2S1_DL_20; + len_2 |= RT5682S_I2S2_DL_20; + break; + case 24: + len_1 |= RT5682S_I2S1_DL_24; + len_2 |= RT5682S_I2S2_DL_24; + break; + case 32: + len_1 |= RT5682S_I2S1_DL_32; + len_2 |= RT5682S_I2S2_DL_24; + break; + case 8: + len_1 |= RT5682S_I2S2_DL_8; + len_2 |= RT5682S_I2S2_DL_8; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5682S_AIF1: + snd_soc_component_update_bits(component, RT5682S_I2S1_SDP, + RT5682S_I2S1_DL_MASK, len_1); + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, RT5682S_I2S1_SDP, + RT5682S_I2S1_MONO_MASK, RT5682S_I2S1_MONO_EN); + else + snd_soc_component_update_bits(component, RT5682S_I2S1_SDP, + RT5682S_I2S1_MONO_MASK, RT5682S_I2S1_MONO_DIS); + break; + case RT5682S_AIF2: + snd_soc_component_update_bits(component, RT5682S_I2S2_SDP, + RT5682S_I2S2_DL_MASK, len_2); + if (params_channels(params) == 1) /* mono mode */ + snd_soc_component_update_bits(component, RT5682S_I2S2_SDP, + RT5682S_I2S2_MONO_MASK, RT5682S_I2S2_MONO_EN); + else + snd_soc_component_update_bits(component, RT5682S_I2S2_SDP, + RT5682S_I2S2_MONO_MASK, RT5682S_I2S2_MONO_DIS); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + + return 0; +} + +static int rt5682s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + unsigned int reg_val = 0, tdm_ctrl = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5682s->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rt5682s->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5682S_I2S_BP_INV; + tdm_ctrl |= RT5682S_TDM_S_BP_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + if (dai->id == RT5682S_AIF1) + tdm_ctrl |= RT5682S_TDM_S_LP_INV | RT5682S_TDM_M_BP_INV; + else + return -EINVAL; + break; + case SND_SOC_DAIFMT_IB_IF: + if (dai->id == RT5682S_AIF1) + tdm_ctrl |= RT5682S_TDM_S_BP_INV | RT5682S_TDM_S_LP_INV | + RT5682S_TDM_M_BP_INV | RT5682S_TDM_M_LP_INV; + else + return -EINVAL; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5682S_I2S_DF_LEFT; + tdm_ctrl |= RT5682S_TDM_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5682S_I2S_DF_PCM_A; + tdm_ctrl |= RT5682S_TDM_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5682S_I2S_DF_PCM_B; + tdm_ctrl |= RT5682S_TDM_DF_PCM_B; + break; + default: + return -EINVAL; + } + + switch (dai->id) { + case RT5682S_AIF1: + snd_soc_component_update_bits(component, RT5682S_I2S1_SDP, + RT5682S_I2S_DF_MASK, reg_val); + snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1, + RT5682S_TDM_MS_MASK | RT5682S_TDM_S_BP_MASK | + RT5682S_TDM_DF_MASK | RT5682S_TDM_M_BP_MASK | + RT5682S_TDM_M_LP_MASK | RT5682S_TDM_S_LP_MASK, + tdm_ctrl | rt5682s->master[dai->id]); + break; + case RT5682S_AIF2: + if (rt5682s->master[dai->id] == 0) + reg_val |= RT5682S_I2S2_MS_S; + snd_soc_component_update_bits(component, RT5682S_I2S2_SDP, + RT5682S_I2S2_MS_MASK | RT5682S_I2S_BP_MASK | + RT5682S_I2S_DF_MASK, reg_val); + break; + default: + dev_err(component->dev, "Invalid dai->id: %d\n", dai->id); + return -EINVAL; + } + return 0; +} + +static int rt5682s_set_component_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, int dir) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + unsigned int src = 0; + + if (freq == rt5682s->sysclk && clk_id == rt5682s->sysclk_src) + return 0; + + switch (clk_id) { + case RT5682S_SCLK_S_MCLK: + src = RT5682S_CLK_SRC_MCLK; + break; + case RT5682S_SCLK_S_PLL1: + src = RT5682S_CLK_SRC_PLL1; + break; + case RT5682S_SCLK_S_PLL2: + src = RT5682S_CLK_SRC_PLL2; + break; + case RT5682S_SCLK_S_RCCLK: + src = RT5682S_CLK_SRC_RCCLK; + break; + default: + dev_err(component->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, RT5682S_GLB_CLK, + RT5682S_SCLK_SRC_MASK, src << RT5682S_SCLK_SRC_SFT); + snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_1, + RT5682S_I2S_M_CLK_SRC_MASK, src << RT5682S_I2S_M_CLK_SRC_SFT); + snd_soc_component_update_bits(component, RT5682S_I2S2_M_CLK_CTRL_1, + RT5682S_I2S2_M_CLK_SRC_MASK, src << RT5682S_I2S2_M_CLK_SRC_SFT); + + rt5682s->sysclk = freq; + rt5682s->sysclk_src = clk_id; + + dev_dbg(component->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); + + return 0; +} + +static const struct pll_calc_map plla_table[] = { + {2048000, 24576000, 0, 46, 2, true, false, false, false}, + {256000, 24576000, 0, 382, 2, true, false, false, false}, + {512000, 24576000, 0, 190, 2, true, false, false, false}, + {4096000, 24576000, 0, 22, 2, true, false, false, false}, + {1024000, 24576000, 0, 94, 2, true, false, false, false}, + {11289600, 22579200, 1, 22, 2, false, false, false, false}, + {1411200, 22579200, 0, 62, 2, true, false, false, false}, + {2822400, 22579200, 0, 30, 2, true, false, false, false}, + {12288000, 24576000, 1, 22, 2, false, false, false, false}, + {1536000, 24576000, 0, 62, 2, true, false, false, false}, + {3072000, 24576000, 0, 30, 2, true, false, false, false}, + {24576000, 49152000, 4, 22, 0, false, false, false, false}, + {3072000, 49152000, 0, 30, 0, true, false, false, false}, + {6144000, 49152000, 0, 30, 0, false, false, false, false}, + {49152000, 98304000, 10, 22, 0, false, true, false, false}, + {6144000, 98304000, 0, 30, 0, false, true, false, false}, + {12288000, 98304000, 1, 22, 0, false, true, false, false}, + {48000000, 3840000, 10, 22, 23, false, false, false, false}, + {24000000, 3840000, 4, 22, 23, false, false, false, false}, + {19200000, 3840000, 3, 23, 23, false, false, false, false}, + {38400000, 3840000, 8, 23, 23, false, false, false, false}, +}; + +static const struct pll_calc_map pllb_table[] = { + {48000000, 24576000, 8, 6, 3, false, false, false, false}, + {48000000, 22579200, 23, 12, 3, false, false, false, true}, + {24000000, 24576000, 3, 6, 3, false, false, false, false}, + {24000000, 22579200, 23, 26, 3, false, false, false, true}, + {19200000, 24576000, 2, 6, 3, false, false, false, false}, + {19200000, 22579200, 3, 5, 3, false, false, false, true}, + {38400000, 24576000, 6, 6, 3, false, false, false, false}, + {38400000, 22579200, 8, 5, 3, false, false, false, true}, + {3840000, 49152000, 0, 6, 0, true, false, false, false}, +}; + +static int find_pll_inter_combination(unsigned int f_in, unsigned int f_out, + struct pll_calc_map *a, struct pll_calc_map *b) +{ + int i, j; + + /* Look at PLLA table */ + for (i = 0; i < ARRAY_SIZE(plla_table); i++) { + if (plla_table[i].freq_in == f_in && plla_table[i].freq_out == f_out) { + memcpy(a, plla_table + i, sizeof(*a)); + return USE_PLLA; + } + } + + /* Look at PLLB table */ + for (i = 0; i < ARRAY_SIZE(pllb_table); i++) { + if (pllb_table[i].freq_in == f_in && pllb_table[i].freq_out == f_out) { + memcpy(b, pllb_table + i, sizeof(*b)); + return USE_PLLB; + } + } + + /* Find a combination of PLLA & PLLB */ + for (i = ARRAY_SIZE(plla_table) - 1; i >= 0; i--) { + if (plla_table[i].freq_in == f_in && plla_table[i].freq_out == 3840000) { + for (j = ARRAY_SIZE(pllb_table) - 1; j >= 0; j--) { + if (pllb_table[j].freq_in == 3840000 && + pllb_table[j].freq_out == f_out) { + memcpy(a, plla_table + i, sizeof(*a)); + memcpy(b, pllb_table + j, sizeof(*b)); + return USE_PLLAB; + } + } + } + } + + return -EINVAL; +} + +static int rt5682s_set_component_pll(struct snd_soc_component *component, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + struct pll_calc_map a_map, b_map; + + if (source == rt5682s->pll_src[pll_id] && freq_in == rt5682s->pll_in[pll_id] && + freq_out == rt5682s->pll_out[pll_id]) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(component->dev, "PLL disabled\n"); + rt5682s->pll_in[pll_id] = 0; + rt5682s->pll_out[pll_id] = 0; + snd_soc_component_update_bits(component, RT5682S_GLB_CLK, + RT5682S_SCLK_SRC_MASK, RT5682S_CLK_SRC_MCLK << RT5682S_SCLK_SRC_SFT); + return 0; + } + + switch (source) { + case RT5682S_PLL_S_MCLK: + snd_soc_component_update_bits(component, RT5682S_GLB_CLK, + RT5682S_PLL_SRC_MASK, RT5682S_PLL_SRC_MCLK); + break; + case RT5682S_PLL_S_BCLK1: + snd_soc_component_update_bits(component, RT5682S_GLB_CLK, + RT5682S_PLL_SRC_MASK, RT5682S_PLL_SRC_BCLK1); + break; + default: + dev_err(component->dev, "Unknown PLL Source %d\n", source); + return -EINVAL; + } + + rt5682s->pll_comb = find_pll_inter_combination(freq_in, freq_out, + &a_map, &b_map); + + if ((pll_id == RT5682S_PLL1 && rt5682s->pll_comb == USE_PLLA) || + (pll_id == RT5682S_PLL2 && (rt5682s->pll_comb == USE_PLLB || + rt5682s->pll_comb == USE_PLLAB))) { + dev_dbg(component->dev, + "Supported freq conversion for PLL%d:(%d->%d): %d\n", + pll_id + 1, freq_in, freq_out, rt5682s->pll_comb); + } else { + dev_err(component->dev, + "Unsupported freq conversion for PLL%d:(%d->%d): %d\n", + pll_id + 1, freq_in, freq_out, rt5682s->pll_comb); + return -EINVAL; + } + + if (rt5682s->pll_comb == USE_PLLA || rt5682s->pll_comb == USE_PLLAB) { + dev_dbg(component->dev, + "PLLA: fin=%d fout=%d m_bp=%d k_bp=%d m=%d n=%d k=%d\n", + a_map.freq_in, a_map.freq_out, a_map.m_bp, a_map.k_bp, + (a_map.m_bp ? 0 : a_map.m), a_map.n, (a_map.k_bp ? 0 : a_map.k)); + snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_1, + RT5682S_PLLA_N_MASK, a_map.n); + snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_2, + RT5682S_PLLA_M_MASK | RT5682S_PLLA_K_MASK, + a_map.m << RT5682S_PLLA_M_SFT | a_map.k); + snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_6, + RT5682S_PLLA_M_BP_MASK | RT5682S_PLLA_K_BP_MASK, + a_map.m_bp << RT5682S_PLLA_M_BP_SFT | + a_map.k_bp << RT5682S_PLLA_K_BP_SFT); + } + + if (rt5682s->pll_comb == USE_PLLB || rt5682s->pll_comb == USE_PLLAB) { + dev_dbg(component->dev, + "PLLB: fin=%d fout=%d m_bp=%d k_bp=%d m=%d n=%d k=%d byp_ps=%d sel_ps=%d\n", + b_map.freq_in, b_map.freq_out, b_map.m_bp, b_map.k_bp, + (b_map.m_bp ? 0 : b_map.m), b_map.n, (b_map.k_bp ? 0 : b_map.k), + b_map.byp_ps, b_map.sel_ps); + snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_3, + RT5682S_PLLB_N_MASK, b_map.n); + snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_4, + RT5682S_PLLB_M_MASK | RT5682S_PLLB_K_MASK, + b_map.m << RT5682S_PLLB_M_SFT | b_map.k); + snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_6, + RT5682S_PLLB_SEL_PS_MASK | RT5682S_PLLB_BYP_PS_MASK | + RT5682S_PLLB_M_BP_MASK | RT5682S_PLLB_K_BP_MASK, + b_map.sel_ps << RT5682S_PLLB_SEL_PS_SFT | + b_map.byp_ps << RT5682S_PLLB_BYP_PS_SFT | + b_map.m_bp << RT5682S_PLLB_M_BP_SFT | + b_map.k_bp << RT5682S_PLLB_K_BP_SFT); + } + + if (rt5682s->pll_comb == USE_PLLB) + snd_soc_component_update_bits(component, RT5682S_PLL_CTRL_7, + RT5682S_PLLB_SRC_MASK, RT5682S_PLLB_SRC_DFIN); + + rt5682s->pll_in[pll_id] = freq_in; + rt5682s->pll_out[pll_id] = freq_out; + rt5682s->pll_src[pll_id] = source; + + return 0; +} + +static int rt5682s_set_bclk1_ratio(struct snd_soc_dai *dai, + unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + rt5682s->bclk[dai->id] = ratio; + + switch (ratio) { + case 256: + snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1, + RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_256); + break; + case 128: + snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1, + RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_128); + break; + case 64: + snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1, + RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_64); + break; + case 32: + snd_soc_component_update_bits(component, RT5682S_TDM_TCON_CTRL_1, + RT5682S_TDM_BCLK_MS1_MASK, RT5682S_TDM_BCLK_MS1_32); + break; + default: + dev_err(dai->dev, "Invalid bclk1 ratio %d\n", ratio); + return -EINVAL; + } + + return 0; +} + +static int rt5682s_set_bclk2_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_component *component = dai->component; + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + rt5682s->bclk[dai->id] = ratio; + + switch (ratio) { + case 64: + snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_2, + RT5682S_I2S2_BCLK_MS2_MASK, RT5682S_I2S2_BCLK_MS2_64); + break; + case 32: + snd_soc_component_update_bits(component, RT5682S_ADDA_CLK_2, + RT5682S_I2S2_BCLK_MS2_MASK, RT5682S_I2S2_BCLK_MS2_32); + break; + default: + dev_err(dai->dev, "Invalid bclk2 ratio %d\n", ratio); + return -EINVAL; + } + + return 0; +} + +static int rt5682s_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1, + RT5682S_PWR_LDO, RT5682S_PWR_LDO); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1, + RT5682S_DIG_GATE_CTRL, RT5682S_DIG_GATE_CTRL); + break; + case SND_SOC_BIAS_OFF: + regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1, RT5682S_PWR_LDO, 0); + if (!rt5682s->wclk_enabled) + regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_1, + RT5682S_DIG_GATE_CTRL, 0); + break; + case SND_SOC_BIAS_ON: + break; + } + + return 0; +} + +#ifdef CONFIG_COMMON_CLK +#define CLK_PLL2_FIN 48000000 +#define CLK_48 48000 +#define CLK_44 44100 + +static bool rt5682s_clk_check(struct rt5682s_priv *rt5682s) +{ + if (!rt5682s->master[RT5682S_AIF1]) { + dev_dbg(rt5682s->component->dev, "dai clk fmt not set correctly\n"); + return false; + } + return true; +} + +static int rt5682s_wclk_prepare(struct clk_hw *hw) +{ + struct rt5682s_priv *rt5682s = + container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682s->component; + int ref, reg; + + if (!rt5682s_clk_check(rt5682s)) + return -EINVAL; + + mutex_lock(&rt5682s->wclk_mutex); + + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, + RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, + RT5682S_PWR_VREF2 | RT5682S_PWR_MB); + usleep_range(15000, 20000); + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, + RT5682S_PWR_FV2, RT5682S_PWR_FV2); + + /* Set and power on I2S1 */ + snd_soc_component_update_bits(component, RT5682S_PWR_DIG_1, + RT5682S_DIG_GATE_CTRL, RT5682S_DIG_GATE_CTRL); + rt5682s_set_i2s(rt5682s, RT5682S_AIF1, 1); + + /* Only need to power on PLLB due to the rate set restriction */ + reg = RT5682S_PLL_TRACK_2; + ref = 256 * rt5682s->lrck[RT5682S_AIF1]; + rt5682s_set_filter_clk(rt5682s, reg, ref); + rt5682s_set_pllb_power(rt5682s, 1); + + rt5682s->wclk_enabled = 1; + + mutex_unlock(&rt5682s->wclk_mutex); + + return 0; +} + +static void rt5682s_wclk_unprepare(struct clk_hw *hw) +{ + struct rt5682s_priv *rt5682s = + container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682s->component; + + if (!rt5682s_clk_check(rt5682s)) + return; + + mutex_lock(&rt5682s->wclk_mutex); + + if (!rt5682s->jack_type) + snd_soc_component_update_bits(component, RT5682S_PWR_ANLG_1, + RT5682S_PWR_VREF2 | RT5682S_PWR_FV2 | RT5682S_PWR_MB, 0); + + /* Power down I2S1 */ + rt5682s_set_i2s(rt5682s, RT5682S_AIF1, 0); + snd_soc_component_update_bits(component, RT5682S_PWR_DIG_1, + RT5682S_DIG_GATE_CTRL, 0); + + /* Power down PLLB */ + rt5682s_set_pllb_power(rt5682s, 0); + + rt5682s->wclk_enabled = 0; + + mutex_unlock(&rt5682s->wclk_mutex); +} + +static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rt5682s_priv *rt5682s = + container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682s->component; + const char * const clk_name = clk_hw_get_name(hw); + + if (!rt5682s_clk_check(rt5682s)) + return 0; + /* + * Only accept to set wclk rate to 44.1k or 48kHz. + */ + if (rt5682s->lrck[RT5682S_AIF1] != CLK_48 && + rt5682s->lrck[RT5682S_AIF1] != CLK_44) { + dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n", + __func__, clk_name, CLK_44, CLK_48); + return 0; + } + + return rt5682s->lrck[RT5682S_AIF1]; +} + +static long rt5682s_wclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rt5682s_priv *rt5682s = + container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682s->component; + const char * const clk_name = clk_hw_get_name(hw); + + if (!rt5682s_clk_check(rt5682s)) + return -EINVAL; + /* + * Only accept to set wclk rate to 44.1k or 48kHz. + * It will force to 48kHz if not both. + */ + if (rate != CLK_48 && rate != CLK_44) { + dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n", + __func__, clk_name, CLK_44, CLK_48); + rate = CLK_48; + } + + return rate; +} + +static int rt5682s_wclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct rt5682s_priv *rt5682s = + container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682s->component; + struct clk *parent_clk; + const char * const clk_name = clk_hw_get_name(hw); + unsigned int clk_pll2_fout; + + if (!rt5682s_clk_check(rt5682s)) + return -EINVAL; + + /* + * Whether the wclk's parent clk (mclk) exists or not, please ensure + * it is fixed or set to 48MHz before setting wclk rate. It's a + * temporary limitation. Only accept 48MHz clk as the clk provider. + * + * It will set the codec anyway by assuming mclk is 48MHz. + */ + parent_clk = clk_get_parent(hw->clk); + if (!parent_clk) + dev_warn(component->dev, + "Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n", + CLK_PLL2_FIN); + + if (parent_rate != CLK_PLL2_FIN) + dev_warn(component->dev, "clk %s only support %d Hz input\n", + clk_name, CLK_PLL2_FIN); + + /* + * To achieve the rate conversion from 48MHz to 44.1k or 48kHz, + * PLL2 is needed. + */ + clk_pll2_fout = rate * 512; + rt5682s_set_component_pll(component, RT5682S_PLL2, RT5682S_PLL_S_MCLK, + CLK_PLL2_FIN, clk_pll2_fout); + + rt5682s_set_component_sysclk(component, RT5682S_SCLK_S_PLL2, 0, + clk_pll2_fout, SND_SOC_CLOCK_IN); + + rt5682s->lrck[RT5682S_AIF1] = rate; + + return 0; +} + +static unsigned long rt5682s_bclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rt5682s_priv *rt5682s = + container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]); + struct snd_soc_component *component = rt5682s->component; + unsigned int bclks_per_wclk; + + bclks_per_wclk = snd_soc_component_read(component, RT5682S_TDM_TCON_CTRL_1); + + switch (bclks_per_wclk & RT5682S_TDM_BCLK_MS1_MASK) { + case RT5682S_TDM_BCLK_MS1_256: + return parent_rate * 256; + case RT5682S_TDM_BCLK_MS1_128: + return parent_rate * 128; + case RT5682S_TDM_BCLK_MS1_64: + return parent_rate * 64; + case RT5682S_TDM_BCLK_MS1_32: + return parent_rate * 32; + default: + return 0; + } +} + +static unsigned long rt5682s_bclk_get_factor(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long factor; + + factor = rate / parent_rate; + if (factor < 64) + return 32; + else if (factor < 128) + return 64; + else if (factor < 256) + return 128; + else + return 256; +} + +static long rt5682s_bclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rt5682s_priv *rt5682s = + container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]); + unsigned long factor; + + if (!*parent_rate || !rt5682s_clk_check(rt5682s)) + return -EINVAL; + + /* + * BCLK rates are set as a multiplier of WCLK in HW. + * We don't allow changing the parent WCLK. We just do + * some rounding down based on the parent WCLK rate + * and find the appropriate multiplier of BCLK to + * get the rounded down BCLK value. + */ + factor = rt5682s_bclk_get_factor(rate, *parent_rate); + + return *parent_rate * factor; +} + +static int rt5682s_bclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct rt5682s_priv *rt5682s = + container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]); + struct snd_soc_component *component = rt5682s->component; + struct snd_soc_dai *dai; + unsigned long factor; + + if (!rt5682s_clk_check(rt5682s)) + return -EINVAL; + + factor = rt5682s_bclk_get_factor(rate, parent_rate); + + for_each_component_dais(component, dai) + if (dai->id == RT5682S_AIF1) + return rt5682s_set_bclk1_ratio(dai, factor); + + dev_err(component->dev, "dai %d not found in component\n", + RT5682S_AIF1); + return -ENODEV; +} + +static const struct clk_ops rt5682s_dai_clk_ops[RT5682S_DAI_NUM_CLKS] = { + [RT5682S_DAI_WCLK_IDX] = { + .prepare = rt5682s_wclk_prepare, + .unprepare = rt5682s_wclk_unprepare, + .recalc_rate = rt5682s_wclk_recalc_rate, + .round_rate = rt5682s_wclk_round_rate, + .set_rate = rt5682s_wclk_set_rate, + }, + [RT5682S_DAI_BCLK_IDX] = { + .recalc_rate = rt5682s_bclk_recalc_rate, + .round_rate = rt5682s_bclk_round_rate, + .set_rate = rt5682s_bclk_set_rate, + }, +}; + +static int rt5682s_register_dai_clks(struct snd_soc_component *component) +{ + struct device *dev = component->dev; + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + struct rt5682s_platform_data *pdata = &rt5682s->pdata; + struct clk_hw *dai_clk_hw; + int i, ret; + + for (i = 0; i < RT5682S_DAI_NUM_CLKS; ++i) { + struct clk_init_data init = { }; + struct clk_parent_data parent_data; + const struct clk_hw *parent; + + dai_clk_hw = &rt5682s->dai_clks_hw[i]; + + switch (i) { + case RT5682S_DAI_WCLK_IDX: + /* Make MCLK the parent of WCLK */ + if (rt5682s->mclk) { + parent_data = (struct clk_parent_data){ + .fw_name = "mclk", + }; + init.parent_data = &parent_data; + init.num_parents = 1; + } + break; + case RT5682S_DAI_BCLK_IDX: + /* Make WCLK the parent of BCLK */ + parent = &rt5682s->dai_clks_hw[RT5682S_DAI_WCLK_IDX]; + init.parent_hws = &parent; + init.num_parents = 1; + break; + default: + dev_err(dev, "Invalid clock index\n"); + return -EINVAL; + } + + init.name = pdata->dai_clk_names[i]; + init.ops = &rt5682s_dai_clk_ops[i]; + init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; + dai_clk_hw->init = &init; + + ret = devm_clk_hw_register(dev, dai_clk_hw); + if (ret) { + dev_warn(dev, "Failed to register %s: %d\n", init.name, ret); + return ret; + } + + if (dev->of_node) { + devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, dai_clk_hw); + } else { + ret = devm_clk_hw_register_clkdev(dev, dai_clk_hw, + init.name, dev_name(dev)); + if (ret) + return ret; + } + } + + return 0; +} + +static int rt5682s_dai_probe_clks(struct snd_soc_component *component) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + int ret; + + /* Check if MCLK provided */ + rt5682s->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5682s->mclk)) + return PTR_ERR(rt5682s->mclk); + + /* Register CCF DAI clock control */ + ret = rt5682s_register_dai_clks(component); + if (ret) + return ret; + + /* Initial setup for CCF */ + rt5682s->lrck[RT5682S_AIF1] = CLK_48; + + return 0; +} +#else +static inline int rt5682s_dai_probe_clks(struct snd_soc_component *component) +{ + return 0; +} +#endif /* CONFIG_COMMON_CLK */ + +static int rt5682s_probe(struct snd_soc_component *component) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + rt5682s->component = component; + + return rt5682s_dai_probe_clks(component); +} + +static void rt5682s_remove(struct snd_soc_component *component) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + rt5682s_reset(rt5682s); +} + +#ifdef CONFIG_PM +static int rt5682s_suspend(struct snd_soc_component *component) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + if (rt5682s->irq) + disable_irq(rt5682s->irq); + + cancel_delayed_work_sync(&rt5682s->jack_detect_work); + cancel_delayed_work_sync(&rt5682s->jd_check_work); + + if (rt5682s->hs_jack) + rt5682s->jack_type = rt5682s_headset_detect(component, 0); + + regcache_cache_only(rt5682s->regmap, true); + regcache_mark_dirty(rt5682s->regmap); + + return 0; +} + +static int rt5682s_resume(struct snd_soc_component *component) +{ + struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component); + + regcache_cache_only(rt5682s->regmap, false); + regcache_sync(rt5682s->regmap); + + if (rt5682s->hs_jack) { + mod_delayed_work(system_power_efficient_wq, + &rt5682s->jack_detect_work, msecs_to_jiffies(0)); + } + + if (rt5682s->irq) + enable_irq(rt5682s->irq); + + return 0; +} +#else +#define rt5682s_suspend NULL +#define rt5682s_resume NULL +#endif + +static const struct snd_soc_dai_ops rt5682s_aif1_dai_ops = { + .hw_params = rt5682s_hw_params, + .set_fmt = rt5682s_set_dai_fmt, + .set_tdm_slot = rt5682s_set_tdm_slot, + .set_bclk_ratio = rt5682s_set_bclk1_ratio, +}; + +static const struct snd_soc_dai_ops rt5682s_aif2_dai_ops = { + .hw_params = rt5682s_hw_params, + .set_fmt = rt5682s_set_dai_fmt, + .set_bclk_ratio = rt5682s_set_bclk2_ratio, +}; + +static const struct snd_soc_component_driver rt5682s_soc_component_dev = { + .probe = rt5682s_probe, + .remove = rt5682s_remove, + .suspend = rt5682s_suspend, + .resume = rt5682s_resume, + .set_bias_level = rt5682s_set_bias_level, + .controls = rt5682s_snd_controls, + .num_controls = ARRAY_SIZE(rt5682s_snd_controls), + .dapm_widgets = rt5682s_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5682s_dapm_widgets), + .dapm_routes = rt5682s_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5682s_dapm_routes), + .set_sysclk = rt5682s_set_component_sysclk, + .set_pll = rt5682s_set_component_pll, + .set_jack = rt5682s_set_jack_detect, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev) +{ + device_property_read_u32(dev, "realtek,dmic1-data-pin", + &rt5682s->pdata.dmic1_data_pin); + device_property_read_u32(dev, "realtek,dmic1-clk-pin", + &rt5682s->pdata.dmic1_clk_pin); + device_property_read_u32(dev, "realtek,jd-src", + &rt5682s->pdata.jd_src); + device_property_read_u32(dev, "realtek,dmic-clk-rate-hz", + &rt5682s->pdata.dmic_clk_rate); + device_property_read_u32(dev, "realtek,dmic-delay-ms", + &rt5682s->pdata.dmic_delay); + device_property_read_u32(dev, "realtek,amic-delay-ms", + &rt5682s->pdata.amic_delay); + + if (device_property_read_string_array(dev, "clock-output-names", + rt5682s->pdata.dai_clk_names, + RT5682S_DAI_NUM_CLKS) < 0) + dev_warn(dev, "Using default DAI clk names: %s, %s\n", + rt5682s->pdata.dai_clk_names[RT5682S_DAI_WCLK_IDX], + rt5682s->pdata.dai_clk_names[RT5682S_DAI_BCLK_IDX]); + + rt5682s->pdata.dmic_clk_driving_high = device_property_read_bool(dev, + "realtek,dmic-clk-driving-high"); + + return 0; +} + +static void rt5682s_calibrate(struct rt5682s_priv *rt5682s) +{ + unsigned int count, value; + + mutex_lock(&rt5682s->calibrate_mutex); + + regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xaa80); + usleep_range(15000, 20000); + regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0xfa80); + regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x01c0); + regmap_write(rt5682s->regmap, RT5682S_MICBIAS_2, 0x0380); + regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x8000); + regmap_write(rt5682s->regmap, RT5682S_ADDA_CLK_1, 0x1001); + regmap_write(rt5682s->regmap, RT5682S_CHOP_DAC_2, 0x3030); + regmap_write(rt5682s->regmap, RT5682S_CHOP_ADC, 0xb000); + regmap_write(rt5682s->regmap, RT5682S_STO1_ADC_MIXER, 0x686c); + regmap_write(rt5682s->regmap, RT5682S_CAL_REC, 0x5151); + regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_2, 0x0321); + regmap_write(rt5682s->regmap, RT5682S_HP_LOGIC_CTRL_2, 0x0004); + regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_1, 0x7c00); + regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_1, 0xfc00); + + for (count = 0; count < 60; count++) { + regmap_read(rt5682s->regmap, RT5682S_HP_CALIB_ST_1, &value); + if (!(value & 0x8000)) + break; + + usleep_range(10000, 10005); + } + + if (count >= 60) + dev_err(rt5682s->component->dev, "HP Calibration Failure\n"); + + /* restore settings */ + regmap_write(rt5682s->regmap, RT5682S_MICBIAS_2, 0x0180); + regmap_write(rt5682s->regmap, RT5682S_CAL_REC, 0x5858); + regmap_write(rt5682s->regmap, RT5682S_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5682s->regmap, RT5682S_HP_CALIB_CTRL_2, 0x0320); + regmap_write(rt5682s->regmap, RT5682S_PWR_DIG_1, 0x00c0); + regmap_write(rt5682s->regmap, RT5682S_PWR_ANLG_1, 0x0800); + regmap_write(rt5682s->regmap, RT5682S_GLB_CLK, 0x0000); + + mutex_unlock(&rt5682s->calibrate_mutex); +} + +static const struct regmap_config rt5682s_regmap = { + .reg_bits = 16, + .val_bits = 16, + .max_register = RT5682S_MAX_REG, + .volatile_reg = rt5682s_volatile_register, + .readable_reg = rt5682s_readable_register, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = rt5682s_reg, + .num_reg_defaults = ARRAY_SIZE(rt5682s_reg), + .use_single_read = true, + .use_single_write = true, +}; + +static struct snd_soc_dai_driver rt5682s_dai[] = { + { + .name = "rt5682s-aif1", + .id = RT5682S_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682S_STEREO_RATES, + .formats = RT5682S_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682S_STEREO_RATES, + .formats = RT5682S_FORMATS, + }, + .ops = &rt5682s_aif1_dai_ops, + }, + { + .name = "rt5682s-aif2", + .id = RT5682S_AIF2, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5682S_STEREO_RATES, + .formats = RT5682S_FORMATS, + }, + .ops = &rt5682s_aif2_dai_ops, + }, +}; + +static void rt5682s_i2c_disable_regulators(void *data) +{ + struct rt5682s_priv *rt5682s = data; + struct device *dev = regmap_get_device(rt5682s->regmap); + int ret; + + ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer); + if (ret) + dev_err(dev, "Failed to disable supply AVDD: %d\n", ret); + + ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_DBVDD].consumer); + if (ret) + dev_err(dev, "Failed to disable supply DBVDD: %d\n", ret); + + ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_LDO1_IN].consumer); + if (ret) + dev_err(dev, "Failed to disable supply LDO1-IN: %d\n", ret); + + usleep_range(1000, 1500); + + ret = regulator_disable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer); + if (ret) + dev_err(dev, "Failed to disable supply MICVDD: %d\n", ret); +} + +static int rt5682s_i2c_probe(struct i2c_client *i2c) +{ + struct rt5682s_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5682s_priv *rt5682s; + int i, ret; + unsigned int val; + + rt5682s = devm_kzalloc(&i2c->dev, sizeof(struct rt5682s_priv), GFP_KERNEL); + if (!rt5682s) + return -ENOMEM; + + i2c_set_clientdata(i2c, rt5682s); + + rt5682s->pdata = i2s_default_platform_data; + + if (pdata) + rt5682s->pdata = *pdata; + else + rt5682s_parse_dt(rt5682s, &i2c->dev); + + rt5682s->regmap = devm_regmap_init_i2c(i2c, &rt5682s_regmap); + if (IS_ERR(rt5682s->regmap)) { + ret = PTR_ERR(rt5682s->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(rt5682s->supplies); i++) + rt5682s->supplies[i].supply = rt5682s_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, + ARRAY_SIZE(rt5682s->supplies), rt5682s->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(&i2c->dev, rt5682s_i2c_disable_regulators, rt5682s); + if (ret) + return ret; + + ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_MICVDD].consumer); + if (ret) { + dev_err(&i2c->dev, "Failed to enable supply MICVDD: %d\n", ret); + return ret; + } + usleep_range(1000, 1500); + + ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_AVDD].consumer); + if (ret) { + dev_err(&i2c->dev, "Failed to enable supply AVDD: %d\n", ret); + return ret; + } + + ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_DBVDD].consumer); + if (ret) { + dev_err(&i2c->dev, "Failed to enable supply DBVDD: %d\n", ret); + return ret; + } + + ret = regulator_enable(rt5682s->supplies[RT5682S_SUPPLY_LDO1_IN].consumer); + if (ret) { + dev_err(&i2c->dev, "Failed to enable supply LDO1-IN: %d\n", ret); + return ret; + } + + rt5682s->ldo1_en = devm_gpiod_get_optional(&i2c->dev, + "realtek,ldo1-en", + GPIOD_OUT_HIGH); + if (IS_ERR(rt5682s->ldo1_en)) { + dev_err(&i2c->dev, "Fail gpio request ldo1_en\n"); + return PTR_ERR(rt5682s->ldo1_en); + } + + /* Sleep for 50 ms minimum */ + usleep_range(50000, 55000); + + regmap_read(rt5682s->regmap, RT5682S_DEVICE_ID, &val); + if (val != DEVICE_ID) { + dev_err(&i2c->dev, "Device with ID register %x is not rt5682s\n", val); + return -ENODEV; + } + + rt5682s_reset(rt5682s); + rt5682s_apply_patch_list(rt5682s, &i2c->dev); + + regmap_update_bits(rt5682s->regmap, RT5682S_PWR_DIG_2, + RT5682S_DLDO_I_LIMIT_MASK, RT5682S_DLDO_I_LIMIT_DIS); + usleep_range(20000, 25000); + + mutex_init(&rt5682s->calibrate_mutex); + mutex_init(&rt5682s->sar_mutex); + mutex_init(&rt5682s->wclk_mutex); + rt5682s_calibrate(rt5682s); + + regmap_update_bits(rt5682s->regmap, RT5682S_MICBIAS_2, + RT5682S_PWR_CLK25M_MASK | RT5682S_PWR_CLK1M_MASK, + RT5682S_PWR_CLK25M_PD | RT5682S_PWR_CLK1M_PU); + regmap_update_bits(rt5682s->regmap, RT5682S_PWR_ANLG_1, + RT5682S_PWR_BG, RT5682S_PWR_BG); + regmap_update_bits(rt5682s->regmap, RT5682S_HP_LOGIC_CTRL_2, + RT5682S_HP_SIG_SRC_MASK, RT5682S_HP_SIG_SRC_1BIT_CTL); + regmap_update_bits(rt5682s->regmap, RT5682S_HP_CHARGE_PUMP_2, + RT5682S_PM_HP_MASK, RT5682S_PM_HP_HV); + regmap_update_bits(rt5682s->regmap, RT5682S_HP_AMP_DET_CTL_1, + RT5682S_CP_SW_SIZE_MASK, RT5682S_CP_SW_SIZE_M); + + /* DMIC data pin */ + switch (rt5682s->pdata.dmic1_data_pin) { + case RT5682S_DMIC1_DATA_NULL: + break; + case RT5682S_DMIC1_DATA_GPIO2: /* share with LRCK2 */ + regmap_update_bits(rt5682s->regmap, RT5682S_DMIC_CTRL_1, + RT5682S_DMIC_1_DP_MASK, RT5682S_DMIC_1_DP_GPIO2); + regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1, + RT5682S_GP2_PIN_MASK, RT5682S_GP2_PIN_DMIC_SDA); + break; + case RT5682S_DMIC1_DATA_GPIO5: /* share with DACDAT1 */ + regmap_update_bits(rt5682s->regmap, RT5682S_DMIC_CTRL_1, + RT5682S_DMIC_1_DP_MASK, RT5682S_DMIC_1_DP_GPIO5); + regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1, + RT5682S_GP5_PIN_MASK, RT5682S_GP5_PIN_DMIC_SDA); + break; + default: + dev_warn(&i2c->dev, "invalid DMIC_DAT pin\n"); + break; + } + + /* DMIC clk pin */ + switch (rt5682s->pdata.dmic1_clk_pin) { + case RT5682S_DMIC1_CLK_NULL: + break; + case RT5682S_DMIC1_CLK_GPIO1: /* share with IRQ */ + regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1, + RT5682S_GP1_PIN_MASK, RT5682S_GP1_PIN_DMIC_CLK); + break; + case RT5682S_DMIC1_CLK_GPIO3: /* share with BCLK2 */ + regmap_update_bits(rt5682s->regmap, RT5682S_GPIO_CTRL_1, + RT5682S_GP3_PIN_MASK, RT5682S_GP3_PIN_DMIC_CLK); + if (rt5682s->pdata.dmic_clk_driving_high) + regmap_update_bits(rt5682s->regmap, RT5682S_PAD_DRIVING_CTRL, + RT5682S_PAD_DRV_GP3_MASK, RT5682S_PAD_DRV_GP3_HIGH); + break; + default: + dev_warn(&i2c->dev, "invalid DMIC_CLK pin\n"); + break; + } + + INIT_DELAYED_WORK(&rt5682s->jack_detect_work, rt5682s_jack_detect_handler); + INIT_DELAYED_WORK(&rt5682s->jd_check_work, rt5682s_jd_check_handler); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5682s_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "rt5682s", rt5682s); + if (!ret) + rt5682s->irq = i2c->irq; + else + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + } + + return devm_snd_soc_register_component(&i2c->dev, &rt5682s_soc_component_dev, + rt5682s_dai, ARRAY_SIZE(rt5682s_dai)); +} + +static void rt5682s_i2c_shutdown(struct i2c_client *client) +{ + struct rt5682s_priv *rt5682s = i2c_get_clientdata(client); + + disable_irq(client->irq); + cancel_delayed_work_sync(&rt5682s->jack_detect_work); + cancel_delayed_work_sync(&rt5682s->jd_check_work); + + rt5682s_reset(rt5682s); +} + +static void rt5682s_i2c_remove(struct i2c_client *client) +{ + rt5682s_i2c_shutdown(client); +} + +static const struct of_device_id rt5682s_of_match[] = { + {.compatible = "realtek,rt5682s"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rt5682s_of_match); + +static const struct acpi_device_id rt5682s_acpi_match[] = { + {"RTL5682", 0,}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, rt5682s_acpi_match); + +static const struct i2c_device_id rt5682s_i2c_id[] = { + {"rt5682s", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt5682s_i2c_id); + +static struct i2c_driver rt5682s_i2c_driver = { + .driver = { + .name = "rt5682s", + .of_match_table = rt5682s_of_match, + .acpi_match_table = rt5682s_acpi_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = rt5682s_i2c_probe, + .remove = rt5682s_i2c_remove, + .shutdown = rt5682s_i2c_shutdown, + .id_table = rt5682s_i2c_id, +}; +module_i2c_driver(rt5682s_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5682I-VS driver"); +MODULE_AUTHOR("Derek Fang "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h new file mode 100644 index 0000000000..1d79d432d0 --- /dev/null +++ b/sound/soc/codecs/rt5682s.h @@ -0,0 +1,1485 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt5682s.h -- RT5682I-VS ALSA SoC audio driver + * + * Copyright 2021 Realtek Microelectronics + * Author: Derek Fang + */ + +#ifndef __RT5682S_H__ +#define __RT5682S_H__ + +#include +#include +#include +#include +#include +#include + + +/* Info */ +#define RT5682S_RESET 0x0000 +#define RT5682S_VERSION_ID 0x00fd +#define RT5682S_VENDOR_ID 0x00fe +#define RT5682S_DEVICE_ID 0x00ff +/* I/O - Output */ +#define RT5682S_HP_CTRL_1 0x0002 +#define RT5682S_HP_CTRL_2 0x0003 +#define RT5682S_HPL_GAIN 0x0005 +#define RT5682S_HPR_GAIN 0x0006 + +#define RT5682S_I2C_CTRL 0x0008 + +/* I/O - Input */ +#define RT5682S_CBJ_BST_CTRL 0x000b +#define RT5682S_CBJ_DET_CTRL 0x000f +#define RT5682S_CBJ_CTRL_1 0x0010 +#define RT5682S_CBJ_CTRL_2 0x0011 +#define RT5682S_CBJ_CTRL_3 0x0012 +#define RT5682S_CBJ_CTRL_4 0x0013 +#define RT5682S_CBJ_CTRL_5 0x0014 +#define RT5682S_CBJ_CTRL_6 0x0015 +#define RT5682S_CBJ_CTRL_7 0x0016 +#define RT5682S_CBJ_CTRL_8 0x0017 +/* I/O - ADC/DAC/DMIC */ +#define RT5682S_DAC1_DIG_VOL 0x0019 +#define RT5682S_STO1_ADC_DIG_VOL 0x001c +#define RT5682S_STO1_ADC_BOOST 0x001f +#define RT5682S_HP_IMP_GAIN_1 0x0022 +#define RT5682S_HP_IMP_GAIN_2 0x0023 +/* Mixer - D-D */ +#define RT5682S_SIDETONE_CTRL 0x0024 +#define RT5682S_STO1_ADC_MIXER 0x0026 +#define RT5682S_AD_DA_MIXER 0x0029 +#define RT5682S_STO1_DAC_MIXER 0x002a +#define RT5682S_A_DAC1_MUX 0x002b +#define RT5682S_DIG_INF2_DATA 0x0030 +/* Mixer - ADC */ +#define RT5682S_REC_MIXER 0x003c +#define RT5682S_CAL_REC 0x0044 +/* HP Analog Offset Control */ +#define RT5682S_HP_ANA_OST_CTRL_1 0x004b +#define RT5682S_HP_ANA_OST_CTRL_2 0x004c +#define RT5682S_HP_ANA_OST_CTRL_3 0x004d +/* Power */ +#define RT5682S_PWR_DIG_1 0x0061 +#define RT5682S_PWR_DIG_2 0x0062 +#define RT5682S_PWR_ANLG_1 0x0063 +#define RT5682S_PWR_ANLG_2 0x0064 +#define RT5682S_PWR_ANLG_3 0x0065 +#define RT5682S_PWR_MIXER 0x0066 + +#define RT5682S_MB_CTRL 0x0067 +#define RT5682S_CLK_GATE_TCON_1 0x0068 +#define RT5682S_CLK_GATE_TCON_2 0x0069 +#define RT5682S_CLK_GATE_TCON_3 0x006a +/* Clock Detect */ +#define RT5682S_CLK_DET 0x006b +/* Filter Auto Reset */ +#define RT5682S_RESET_LPF_CTRL 0x006c +#define RT5682S_RESET_HPF_CTRL 0x006d +/* DMIC */ +#define RT5682S_DMIC_CTRL_1 0x006e +#define RT5682S_LPF_AD_DMIC 0x006f +/* Format - ADC/DAC */ +#define RT5682S_I2S1_SDP 0x0070 +#define RT5682S_I2S2_SDP 0x0071 +#define RT5682S_ADDA_CLK_1 0x0073 +#define RT5682S_ADDA_CLK_2 0x0074 +#define RT5682S_I2S1_F_DIV_CTRL_1 0x0075 +#define RT5682S_I2S1_F_DIV_CTRL_2 0x0076 +/* Format - TDM Control */ +#define RT5682S_TDM_CTRL 0x0079 +#define RT5682S_TDM_ADDA_CTRL_1 0x007a +#define RT5682S_TDM_ADDA_CTRL_2 0x007b +#define RT5682S_DATA_SEL_CTRL_1 0x007c +#define RT5682S_TDM_TCON_CTRL_1 0x007e +#define RT5682S_TDM_TCON_CTRL_2 0x007f +/* Function - Analog */ +#define RT5682S_GLB_CLK 0x0080 +#define RT5682S_PLL_TRACK_1 0x0083 +#define RT5682S_PLL_TRACK_2 0x0084 +#define RT5682S_PLL_TRACK_3 0x0085 +#define RT5682S_PLL_TRACK_4 0x0086 +#define RT5682S_PLL_TRACK_5 0x0087 +#define RT5682S_PLL_TRACK_6 0x0088 +#define RT5682S_PLL_TRACK_11 0x008c +#define RT5682S_DEPOP_1 0x008e +#define RT5682S_HP_CHARGE_PUMP_1 0x008f +#define RT5682S_HP_CHARGE_PUMP_2 0x0091 +#define RT5682S_HP_CHARGE_PUMP_3 0x0092 +#define RT5682S_MICBIAS_1 0x0093 +#define RT5682S_MICBIAS_2 0x0094 +#define RT5682S_MICBIAS_3 0x0095 + +#define RT5682S_PLL_TRACK_12 0x0096 +#define RT5682S_PLL_TRACK_14 0x0097 +#define RT5682S_PLL_CTRL_1 0x0098 +#define RT5682S_PLL_CTRL_2 0x0099 +#define RT5682S_PLL_CTRL_3 0x009a +#define RT5682S_PLL_CTRL_4 0x009b +#define RT5682S_PLL_CTRL_5 0x009c +#define RT5682S_PLL_CTRL_6 0x009d +#define RT5682S_PLL_CTRL_7 0x009e + +#define RT5682S_RC_CLK_CTRL 0x009f +#define RT5682S_I2S2_M_CLK_CTRL_1 0x00a0 +#define RT5682S_I2S2_F_DIV_CTRL_1 0x00a3 +#define RT5682S_I2S2_F_DIV_CTRL_2 0x00a4 + +#define RT5682S_IRQ_CTRL_1 0x00b6 +#define RT5682S_IRQ_CTRL_2 0x00b7 +#define RT5682S_IRQ_CTRL_3 0x00b8 +#define RT5682S_IRQ_CTRL_4 0x00b9 +#define RT5682S_INT_ST_1 0x00be +#define RT5682S_GPIO_CTRL_1 0x00c0 +#define RT5682S_GPIO_CTRL_2 0x00c1 +#define RT5682S_GPIO_ST 0x00c2 +#define RT5682S_HP_AMP_DET_CTRL_1 0x00d0 +#define RT5682S_MID_HP_AMP_DET 0x00d2 +#define RT5682S_LOW_HP_AMP_DET 0x00d3 +#define RT5682S_DELAY_BUF_CTRL 0x00d4 +#define RT5682S_SV_ZCD_1 0x00d9 +#define RT5682S_SV_ZCD_2 0x00da +#define RT5682S_IL_CMD_1 0x00db +#define RT5682S_IL_CMD_2 0x00dc +#define RT5682S_IL_CMD_3 0x00dd +#define RT5682S_IL_CMD_4 0x00de +#define RT5682S_IL_CMD_5 0x00df +#define RT5682S_IL_CMD_6 0x00e0 +#define RT5682S_4BTN_IL_CMD_1 0x00e2 +#define RT5682S_4BTN_IL_CMD_2 0x00e3 +#define RT5682S_4BTN_IL_CMD_3 0x00e4 +#define RT5682S_4BTN_IL_CMD_4 0x00e5 +#define RT5682S_4BTN_IL_CMD_5 0x00e6 +#define RT5682S_4BTN_IL_CMD_6 0x00e7 +#define RT5682S_4BTN_IL_CMD_7 0x00e8 + +#define RT5682S_ADC_STO1_HP_CTRL_1 0x00ea +#define RT5682S_ADC_STO1_HP_CTRL_2 0x00eb +#define RT5682S_AJD1_CTRL 0x00f0 +#define RT5682S_JD_CTRL_1 0x00f6 +/* General Control */ +#define RT5682S_DUMMY_1 0x00fa +#define RT5682S_DUMMY_2 0x00fb +#define RT5682S_DUMMY_3 0x00fc + +#define RT5682S_DAC_ADC_DIG_VOL1 0x0100 +#define RT5682S_BIAS_CUR_CTRL_2 0x010b +#define RT5682S_BIAS_CUR_CTRL_3 0x010c +#define RT5682S_BIAS_CUR_CTRL_4 0x010d +#define RT5682S_BIAS_CUR_CTRL_5 0x010e +#define RT5682S_BIAS_CUR_CTRL_6 0x010f +#define RT5682S_BIAS_CUR_CTRL_7 0x0110 +#define RT5682S_BIAS_CUR_CTRL_8 0x0111 +#define RT5682S_BIAS_CUR_CTRL_9 0x0112 +#define RT5682S_BIAS_CUR_CTRL_10 0x0113 +#define RT5682S_VREF_REC_OP_FB_CAP_CTRL_1 0x0117 +#define RT5682S_VREF_REC_OP_FB_CAP_CTRL_2 0x0118 +#define RT5682S_CHARGE_PUMP_1 0x0125 +#define RT5682S_DIG_IN_CTRL_1 0x0132 +#define RT5682S_PAD_DRIVING_CTRL 0x0136 +#define RT5682S_CHOP_DAC_1 0x0139 +#define RT5682S_CHOP_DAC_2 0x013a +#define RT5682S_CHOP_ADC 0x013b +#define RT5682S_CALIB_ADC_CTRL 0x013c +#define RT5682S_VOL_TEST 0x013f +#define RT5682S_SPKVDD_DET_ST 0x0142 +#define RT5682S_TEST_MODE_CTRL_1 0x0145 +#define RT5682S_TEST_MODE_CTRL_2 0x0146 +#define RT5682S_TEST_MODE_CTRL_3 0x0147 +#define RT5682S_TEST_MODE_CTRL_4 0x0148 +#define RT5682S_PLL_INTERNAL_1 0x0156 +#define RT5682S_PLL_INTERNAL_2 0x0157 +#define RT5682S_PLL_INTERNAL_3 0x0158 +#define RT5682S_PLL_INTERNAL_4 0x0159 +#define RT5682S_STO_NG2_CTRL_1 0x0160 +#define RT5682S_STO_NG2_CTRL_2 0x0161 +#define RT5682S_STO_NG2_CTRL_3 0x0162 +#define RT5682S_STO_NG2_CTRL_4 0x0163 +#define RT5682S_STO_NG2_CTRL_5 0x0164 +#define RT5682S_STO_NG2_CTRL_6 0x0165 +#define RT5682S_STO_NG2_CTRL_7 0x0166 +#define RT5682S_STO_NG2_CTRL_8 0x0167 +#define RT5682S_STO_NG2_CTRL_9 0x0168 +#define RT5682S_STO_NG2_CTRL_10 0x0169 +#define RT5682S_STO1_DAC_SIL_DET 0x0190 +#define RT5682S_SIL_PSV_CTRL1 0x0194 +#define RT5682S_SIL_PSV_CTRL2 0x0195 +#define RT5682S_SIL_PSV_CTRL3 0x0197 +#define RT5682S_SIL_PSV_CTRL4 0x0198 +#define RT5682S_SIL_PSV_CTRL5 0x0199 +#define RT5682S_HP_IMP_SENS_CTRL_1 0x01ac +#define RT5682S_HP_IMP_SENS_CTRL_2 0x01ad +#define RT5682S_HP_IMP_SENS_CTRL_3 0x01ae +#define RT5682S_HP_IMP_SENS_CTRL_4 0x01af +#define RT5682S_HP_IMP_SENS_CTRL_5 0x01b0 +#define RT5682S_HP_IMP_SENS_CTRL_6 0x01b1 +#define RT5682S_HP_IMP_SENS_CTRL_7 0x01b2 +#define RT5682S_HP_IMP_SENS_CTRL_8 0x01b3 +#define RT5682S_HP_IMP_SENS_CTRL_9 0x01b4 +#define RT5682S_HP_IMP_SENS_CTRL_10 0x01b5 +#define RT5682S_HP_IMP_SENS_CTRL_11 0x01b6 +#define RT5682S_HP_IMP_SENS_CTRL_12 0x01b7 +#define RT5682S_HP_IMP_SENS_CTRL_13 0x01b8 +#define RT5682S_HP_IMP_SENS_CTRL_14 0x01b9 +#define RT5682S_HP_IMP_SENS_CTRL_15 0x01ba +#define RT5682S_HP_IMP_SENS_CTRL_16 0x01bb +#define RT5682S_HP_IMP_SENS_CTRL_17 0x01bc +#define RT5682S_HP_IMP_SENS_CTRL_18 0x01bd +#define RT5682S_HP_IMP_SENS_CTRL_19 0x01be +#define RT5682S_HP_IMP_SENS_CTRL_20 0x01bf +#define RT5682S_HP_IMP_SENS_CTRL_21 0x01c0 +#define RT5682S_HP_IMP_SENS_CTRL_22 0x01c1 +#define RT5682S_HP_IMP_SENS_CTRL_23 0x01c2 +#define RT5682S_HP_IMP_SENS_CTRL_24 0x01c3 +#define RT5682S_HP_IMP_SENS_CTRL_25 0x01c4 +#define RT5682S_HP_IMP_SENS_CTRL_26 0x01c5 +#define RT5682S_HP_IMP_SENS_CTRL_27 0x01c6 +#define RT5682S_HP_IMP_SENS_CTRL_28 0x01c7 +#define RT5682S_HP_IMP_SENS_CTRL_29 0x01c8 +#define RT5682S_HP_IMP_SENS_CTRL_30 0x01c9 +#define RT5682S_HP_IMP_SENS_CTRL_31 0x01ca +#define RT5682S_HP_IMP_SENS_CTRL_32 0x01cb +#define RT5682S_HP_IMP_SENS_CTRL_33 0x01cc +#define RT5682S_HP_IMP_SENS_CTRL_34 0x01cd +#define RT5682S_HP_IMP_SENS_CTRL_35 0x01ce +#define RT5682S_HP_IMP_SENS_CTRL_36 0x01cf +#define RT5682S_HP_IMP_SENS_CTRL_37 0x01d0 +#define RT5682S_HP_IMP_SENS_CTRL_38 0x01d1 +#define RT5682S_HP_IMP_SENS_CTRL_39 0x01d2 +#define RT5682S_HP_IMP_SENS_CTRL_40 0x01d3 +#define RT5682S_HP_IMP_SENS_CTRL_41 0x01d4 +#define RT5682S_HP_IMP_SENS_CTRL_42 0x01d5 +#define RT5682S_HP_IMP_SENS_CTRL_43 0x01d6 +#define RT5682S_HP_IMP_SENS_CTRL_44 0x01d7 +#define RT5682S_HP_IMP_SENS_CTRL_45 0x01d8 +#define RT5682S_HP_IMP_SENS_CTRL_46 0x01d9 +#define RT5682S_HP_LOGIC_CTRL_1 0x01da +#define RT5682S_HP_LOGIC_CTRL_2 0x01db +#define RT5682S_HP_LOGIC_CTRL_3 0x01dc +#define RT5682S_HP_CALIB_CTRL_1 0x01de +#define RT5682S_HP_CALIB_CTRL_2 0x01df +#define RT5682S_HP_CALIB_CTRL_3 0x01e0 +#define RT5682S_HP_CALIB_CTRL_4 0x01e1 +#define RT5682S_HP_CALIB_CTRL_5 0x01e2 +#define RT5682S_HP_CALIB_CTRL_6 0x01e3 +#define RT5682S_HP_CALIB_CTRL_7 0x01e4 +#define RT5682S_HP_CALIB_CTRL_8 0x01e5 +#define RT5682S_HP_CALIB_CTRL_9 0x01e6 +#define RT5682S_HP_CALIB_CTRL_10 0x01e7 +#define RT5682S_HP_CALIB_CTRL_11 0x01e8 +#define RT5682S_HP_CALIB_ST_1 0x01ea +#define RT5682S_HP_CALIB_ST_2 0x01eb +#define RT5682S_HP_CALIB_ST_3 0x01ec +#define RT5682S_HP_CALIB_ST_4 0x01ed +#define RT5682S_HP_CALIB_ST_5 0x01ee +#define RT5682S_HP_CALIB_ST_6 0x01ef +#define RT5682S_HP_CALIB_ST_7 0x01f0 +#define RT5682S_HP_CALIB_ST_8 0x01f1 +#define RT5682S_HP_CALIB_ST_9 0x01f2 +#define RT5682S_HP_CALIB_ST_10 0x01f3 +#define RT5682S_HP_CALIB_ST_11 0x01f4 +#define RT5682S_SAR_IL_CMD_1 0x0210 +#define RT5682S_SAR_IL_CMD_2 0x0211 +#define RT5682S_SAR_IL_CMD_3 0x0212 +#define RT5682S_SAR_IL_CMD_4 0x0213 +#define RT5682S_SAR_IL_CMD_5 0x0214 +#define RT5682S_SAR_IL_CMD_6 0x0215 +#define RT5682S_SAR_IL_CMD_7 0x0216 +#define RT5682S_SAR_IL_CMD_8 0x0217 +#define RT5682S_SAR_IL_CMD_9 0x0218 +#define RT5682S_SAR_IL_CMD_10 0x0219 +#define RT5682S_SAR_IL_CMD_11 0x021a +#define RT5682S_SAR_IL_CMD_12 0x021b +#define RT5682S_SAR_IL_CMD_13 0x021c +#define RT5682S_SAR_IL_CMD_14 0x021d +#define RT5682S_DUMMY_4 0x02fa +#define RT5682S_DUMMY_5 0x02fb +#define RT5682S_DUMMY_6 0x02fc +#define RT5682S_VERSION_ID_HIDE 0x03fe +#define RT5682S_VERSION_ID_CUS 0x03ff +#define RT5682S_SCAN_CTL 0x0500 +#define RT5682S_HP_AMP_DET 0x0600 +#define RT5682S_BIAS_CUR_CTRL_11 0x0610 +#define RT5682S_BIAS_CUR_CTRL_12 0x0611 +#define RT5682S_BIAS_CUR_CTRL_13 0x0620 +#define RT5682S_BIAS_CUR_CTRL_14 0x0621 +#define RT5682S_BIAS_CUR_CTRL_15 0x0630 +#define RT5682S_BIAS_CUR_CTRL_16 0x0631 +#define RT5682S_BIAS_CUR_CTRL_17 0x0640 +#define RT5682S_BIAS_CUR_CTRL_18 0x0641 +#define RT5682S_I2C_TRANS_CTRL 0x07fa +#define RT5682S_DUMMY_7 0x08fa +#define RT5682S_DUMMY_8 0x08fb +#define RT5682S_DMIC_FLOAT_DET 0x0d00 +#define RT5682S_HA_CMP_OP_1 0x1100 +#define RT5682S_HA_CMP_OP_2 0x1101 +#define RT5682S_HA_CMP_OP_3 0x1102 +#define RT5682S_HA_CMP_OP_4 0x1103 +#define RT5682S_HA_CMP_OP_5 0x1104 +#define RT5682S_HA_CMP_OP_6 0x1105 +#define RT5682S_HA_CMP_OP_7 0x1106 +#define RT5682S_HA_CMP_OP_8 0x1107 +#define RT5682S_HA_CMP_OP_9 0x1108 +#define RT5682S_HA_CMP_OP_10 0x1109 +#define RT5682S_HA_CMP_OP_11 0x110a +#define RT5682S_HA_CMP_OP_12 0x110b +#define RT5682S_HA_CMP_OP_13 0x110c +#define RT5682S_HA_CMP_OP_14 0x1111 +#define RT5682S_HA_CMP_OP_15 0x1112 +#define RT5682S_HA_CMP_OP_16 0x1113 +#define RT5682S_HA_CMP_OP_17 0x1114 +#define RT5682S_HA_CMP_OP_18 0x1115 +#define RT5682S_HA_CMP_OP_19 0x1116 +#define RT5682S_HA_CMP_OP_20 0x1117 +#define RT5682S_HA_CMP_OP_21 0x1118 +#define RT5682S_HA_CMP_OP_22 0x1119 +#define RT5682S_HA_CMP_OP_23 0x111a +#define RT5682S_HA_CMP_OP_24 0x111b +#define RT5682S_HA_CMP_OP_25 0x111c +#define RT5682S_NEW_CBJ_DET_CTL_1 0x1401 +#define RT5682S_NEW_CBJ_DET_CTL_2 0x1402 +#define RT5682S_NEW_CBJ_DET_CTL_3 0x1403 +#define RT5682S_NEW_CBJ_DET_CTL_4 0x1404 +#define RT5682S_NEW_CBJ_DET_CTL_5 0x1406 +#define RT5682S_NEW_CBJ_DET_CTL_6 0x1407 +#define RT5682S_NEW_CBJ_DET_CTL_7 0x1408 +#define RT5682S_NEW_CBJ_DET_CTL_8 0x1409 +#define RT5682S_NEW_CBJ_DET_CTL_9 0x140a +#define RT5682S_NEW_CBJ_DET_CTL_10 0x140b +#define RT5682S_NEW_CBJ_DET_CTL_11 0x140c +#define RT5682S_NEW_CBJ_DET_CTL_12 0x140d +#define RT5682S_NEW_CBJ_DET_CTL_13 0x140e +#define RT5682S_NEW_CBJ_DET_CTL_14 0x140f +#define RT5682S_NEW_CBJ_DET_CTL_15 0x1410 +#define RT5682S_NEW_CBJ_DET_CTL_16 0x1411 +#define RT5682S_DA_FILTER_1 0x1801 +#define RT5682S_DA_FILTER_2 0x1802 +#define RT5682S_DA_FILTER_3 0x1803 +#define RT5682S_DA_FILTER_4 0x1804 +#define RT5682S_DA_FILTER_5 0x1805 +#define RT5682S_CLK_SW_TEST_1 0x2c00 +#define RT5682S_CLK_SW_TEST_2 0x3400 +#define RT5682S_CLK_SW_TEST_3 0x3404 +#define RT5682S_CLK_SW_TEST_4 0x3405 +#define RT5682S_CLK_SW_TEST_5 0x3406 +#define RT5682S_CLK_SW_TEST_6 0x3407 +#define RT5682S_CLK_SW_TEST_7 0x3408 +#define RT5682S_CLK_SW_TEST_8 0x3409 +#define RT5682S_CLK_SW_TEST_9 0x340a +#define RT5682S_CLK_SW_TEST_10 0x340b +#define RT5682S_CLK_SW_TEST_11 0x340c +#define RT5682S_CLK_SW_TEST_12 0x340d +#define RT5682S_CLK_SW_TEST_13 0x340e +#define RT5682S_CLK_SW_TEST_14 0x340f +#define RT5682S_EFUSE_MANU_WRITE_1 0x3410 +#define RT5682S_EFUSE_MANU_WRITE_2 0x3411 +#define RT5682S_EFUSE_MANU_WRITE_3 0x3412 +#define RT5682S_EFUSE_MANU_WRITE_4 0x3413 +#define RT5682S_EFUSE_MANU_WRITE_5 0x3414 +#define RT5682S_EFUSE_MANU_WRITE_6 0x3415 +#define RT5682S_EFUSE_READ_1 0x3424 +#define RT5682S_EFUSE_READ_2 0x3425 +#define RT5682S_EFUSE_READ_3 0x3426 +#define RT5682S_EFUSE_READ_4 0x3427 +#define RT5682S_EFUSE_READ_5 0x3428 +#define RT5682S_EFUSE_READ_6 0x3429 +#define RT5682S_EFUSE_READ_7 0x342a +#define RT5682S_EFUSE_READ_8 0x342b +#define RT5682S_EFUSE_READ_9 0x342c +#define RT5682S_EFUSE_READ_10 0x342d +#define RT5682S_EFUSE_READ_11 0x342e +#define RT5682S_EFUSE_READ_12 0x342f +#define RT5682S_EFUSE_READ_13 0x3430 +#define RT5682S_EFUSE_READ_14 0x3431 +#define RT5682S_EFUSE_READ_15 0x3432 +#define RT5682S_EFUSE_READ_16 0x3433 +#define RT5682S_EFUSE_READ_17 0x3434 +#define RT5682S_EFUSE_READ_18 0x3435 +#define RT5682S_EFUSE_TIMING_CTL_1 0x3440 +#define RT5682S_EFUSE_TIMING_CTL_2 0x3441 +#define RT5682S_PILOT_DIG_CTL_1 0x3500 +#define RT5682S_PILOT_DIG_CTL_2 0x3501 +#define RT5682S_HP_AMP_DET_CTL_1 0x3b00 +#define RT5682S_HP_AMP_DET_CTL_2 0x3b01 +#define RT5682S_HP_AMP_DET_CTL_3 0x3b02 +#define RT5682S_HP_AMP_DET_CTL_4 0x3b03 + +#define RT5682S_MAX_REG (RT5682S_HP_AMP_DET_CTL_4) + +/* global definition */ +#define RT5682S_L_MUTE (0x1 << 15) +#define RT5682S_L_MUTE_SFT 15 +#define RT5682S_R_MUTE (0x1 << 7) +#define RT5682S_R_MUTE_SFT 7 +#define RT5682S_L_VOL_SFT 8 +#define RT5682S_R_VOL_SFT 0 +#define RT5682S_CLK_SRC_MCLK (0x0) +#define RT5682S_CLK_SRC_PLL1 (0x1) +#define RT5682S_CLK_SRC_PLL2 (0x2) +#define RT5682S_CLK_SRC_RCCLK (0x4) /* 25M */ + + +/* Headphone Amp Control 2 (0x0003) */ +#define RT5682S_HPO_L_PATH_MASK (0x1 << 14) +#define RT5682S_HPO_L_PATH_EN (0x1 << 14) +#define RT5682S_HPO_L_PATH_DIS (0x0 << 14) +#define RT5682S_HPO_R_PATH_MASK (0x1 << 13) +#define RT5682S_HPO_R_PATH_EN (0x1 << 13) +#define RT5682S_HPO_R_PATH_DIS (0x0 << 13) +#define RT5682S_HPO_SEL_IP_EN_SW (0x1) +#define RT5682S_HPO_IP_EN_GATING (0x1) +#define RT5682S_HPO_IP_NO_GATING (0x0) + +/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/ +#define RT5682S_G_HP (0xf << 8) +#define RT5682S_G_HP_SFT 8 +#define RT5682S_G_STO_DA_DMIX (0xf) +#define RT5682S_G_STO_DA_SFT 0 + +/* Embeeded Jack and Type Detection Control 2 (0x0010) */ +#define RT5682S_EMB_JD_MASK (0x1 << 15) +#define RT5682S_EMB_JD_EN (0x1 << 15) +#define RT5682S_EMB_JD_EN_SFT 15 +#define RT5682S_EMB_JD_RST (0x1 << 14) +#define RT5682S_JD_MODE (0x1 << 13) +#define RT5682S_JD_MODE_SFT 13 +#define RT5682S_DET_TYPE (0x1 << 12) +#define RT5682S_DET_TYPE_SFT 12 +#define RT5682S_POLA_EXT_JD_MASK (0x1 << 11) +#define RT5682S_POLA_EXT_JD_LOW (0x1 << 11) +#define RT5682S_POLA_EXT_JD_HIGH (0x0 << 11) +#define RT5682S_SEL_FAST_OFF_MASK (0x3 << 9) +#define RT5682S_SEL_FAST_OFF_SFT 9 +#define RT5682S_POL_FAST_OFF_MASK (0x1 << 8) +#define RT5682S_POL_FAST_OFF_HIGH (0x1 << 8) +#define RT5682S_POL_FAST_OFF_LOW (0x0 << 8) +#define RT5682S_FAST_OFF_MASK (0x1 << 7) +#define RT5682S_FAST_OFF_EN (0x1 << 7) +#define RT5682S_FAST_OFF_DIS (0x0 << 7) +#define RT5682S_VREF_POW_MASK (0x1 << 6) +#define RT5682S_VREF_POW_FSM (0x0 << 6) +#define RT5682S_VREF_POW_REG (0x1 << 6) +#define RT5682S_MB1_PATH_BIT 5 +#define RT5682S_MB1_PATH_MASK (0x1 << 5) +#define RT5682S_CTRL_MB1_REG (0x1 << 5) +#define RT5682S_CTRL_MB1_FSM (0x0 << 5) +#define RT5682S_MB2_PATH_BIT 4 +#define RT5682S_MB2_PATH_MASK (0x1 << 4) +#define RT5682S_CTRL_MB2_REG (0x1 << 4) +#define RT5682S_CTRL_MB2_FSM (0x0 << 4) +#define RT5682S_TRIG_JD_MASK (0x1 << 3) +#define RT5682S_TRIG_JD_HIGH (0x1 << 3) +#define RT5682S_TRIG_JD_LOW (0x0 << 3) +#define RT5682S_MIC_CAP_MASK (0x1 << 1) +#define RT5682S_MIC_CAP_HS (0x1 << 1) +#define RT5682S_MIC_CAP_HP (0x0 << 1) +#define RT5682S_MIC_CAP_SRC_MASK (0x1) +#define RT5682S_MIC_CAP_SRC_REG (0x1) +#define RT5682S_MIC_CAP_SRC_ANA (0x0) + +/* Embeeded Jack and Type Detection Control 3 (0x0011) */ +#define RT5682S_SEL_CBJ_TYPE_SLOW (0x1 << 15) +#define RT5682S_SEL_CBJ_TYPE_NORM (0x0 << 15) +#define RT5682S_SEL_CBJ_TYPE_MASK (0x1 << 15) +#define RT5682S_POW_BG_MB1_MASK (0x1 << 13) +#define RT5682S_POW_BG_MB1_REG (0x1 << 13) +#define RT5682S_POW_BG_MB1_FSM (0x0 << 13) +#define RT5682S_POW_BG_MB2_MASK (0x1 << 12) +#define RT5682S_POW_BG_MB2_REG (0x1 << 12) +#define RT5682S_POW_BG_MB2_FSM (0x0 << 12) +#define RT5682S_EXT_JD_SRC (0x7 << 4) +#define RT5682S_EXT_JD_SRC_SFT 4 +#define RT5682S_EXT_JD_SRC_GPIO_JD1 (0x0 << 4) +#define RT5682S_EXT_JD_SRC_GPIO_JD2 (0x1 << 4) +#define RT5682S_EXT_JD_SRC_JDH (0x2 << 4) +#define RT5682S_EXT_JD_SRC_JDL (0x3 << 4) +#define RT5682S_EXT_JD_SRC_MANUAL (0x4 << 4) +#define RT5682S_JACK_TYPE_MASK (0x3) + +/* Combo Jack and Type Detection Control 4 (0x0012) */ +#define RT5682S_CBJ_IN_BUF_MASK (0x1 << 7) +#define RT5682S_CBJ_IN_BUF_EN (0x1 << 7) +#define RT5682S_CBJ_IN_BUF_DIS (0x0 << 7) +#define RT5682S_CBJ_IN_BUF_BIT 7 + +/* Combo Jack and Type Detection Control 5 (0x0013) */ +#define RT5682S_SEL_SHT_MID_TON_MASK (0x3 << 12) +#define RT5682S_SEL_SHT_MID_TON_2 (0x0 << 12) +#define RT5682S_SEL_SHT_MID_TON_3 (0x1 << 12) +#define RT5682S_CBJ_JD_TEST_MASK (0x1 << 6) +#define RT5682S_CBJ_JD_TEST_NORM (0x0 << 6) +#define RT5682S_CBJ_JD_TEST_MODE (0x1 << 6) + +/* Combo Jack and Type Detection Control 6 (0x0014) */ +#define RT5682S_JD_FAST_OFF_SRC_MASK (0x7 << 8) +#define RT5682S_JD_FAST_OFF_SRC_JDH (0x6 << 8) +#define RT5682S_JD_FAST_OFF_SRC_GPIO6 (0x5 << 8) +#define RT5682S_JD_FAST_OFF_SRC_GPIO5 (0x4 << 8) +#define RT5682S_JD_FAST_OFF_SRC_GPIO4 (0x3 << 8) +#define RT5682S_JD_FAST_OFF_SRC_GPIO3 (0x2 << 8) +#define RT5682S_JD_FAST_OFF_SRC_GPIO2 (0x1 << 8) +#define RT5682S_JD_FAST_OFF_SRC_GPIO1 (0x0 << 8) + +/* DAC1 Digital Volume (0x0019) */ +#define RT5682S_DAC_L1_VOL_MASK (0xff << 8) +#define RT5682S_DAC_L1_VOL_SFT 8 +#define RT5682S_DAC_R1_VOL_MASK (0xff) +#define RT5682S_DAC_R1_VOL_SFT 0 + +/* ADC Digital Volume Control (0x001c) */ +#define RT5682S_ADC_L_VOL_MASK (0x7f << 8) +#define RT5682S_ADC_L_VOL_SFT 8 +#define RT5682S_ADC_R_VOL_MASK (0x7f) +#define RT5682S_ADC_R_VOL_SFT 0 + +/* Stereo1 ADC Boost Gain Control (0x001f) */ +#define RT5682S_STO1_ADC_L_BST_MASK (0x3 << 14) +#define RT5682S_STO1_ADC_L_BST_SFT 14 +#define RT5682S_STO1_ADC_R_BST_MASK (0x3 << 12) +#define RT5682S_STO1_ADC_R_BST_SFT 12 + +/* Sidetone Control (0x0024) */ +#define RT5682S_ST_SRC_SEL (0x1 << 8) +#define RT5682S_ST_SRC_SFT 8 +#define RT5682S_ST_EN_MASK (0x1 << 6) +#define RT5682S_ST_DIS (0x0 << 6) +#define RT5682S_ST_EN (0x1 << 6) +#define RT5682S_ST_EN_SFT 6 + +/* Stereo1 ADC Mixer Control (0x0026) */ +#define RT5682S_M_STO1_ADC_L1 (0x1 << 15) +#define RT5682S_M_STO1_ADC_L1_SFT 15 +#define RT5682S_M_STO1_ADC_L2 (0x1 << 14) +#define RT5682S_M_STO1_ADC_L2_SFT 14 +#define RT5682S_STO1_ADC1L_SRC_MASK (0x1 << 13) +#define RT5682S_STO1_ADC1L_SRC_SFT 13 +#define RT5682S_STO1_ADC1_SRC_ADC (0x1 << 13) +#define RT5682S_STO1_ADC1_SRC_DACMIX (0x0 << 13) +#define RT5682S_STO1_ADC2L_SRC_MASK (0x1 << 12) +#define RT5682S_STO1_ADC2L_SRC_SFT 12 +#define RT5682S_STO1_ADCL_SRC_MASK (0x3 << 10) +#define RT5682S_STO1_ADCL_SRC_SFT 10 +#define RT5682S_M_STO1_ADC_R1 (0x1 << 7) +#define RT5682S_M_STO1_ADC_R1_SFT 7 +#define RT5682S_M_STO1_ADC_R2 (0x1 << 6) +#define RT5682S_M_STO1_ADC_R2_SFT 6 +#define RT5682S_STO1_ADC1R_SRC_MASK (0x1 << 5) +#define RT5682S_STO1_ADC1R_SRC_SFT 5 +#define RT5682S_STO1_ADC2R_SRC_MASK (0x1 << 4) +#define RT5682S_STO1_ADC2R_SRC_SFT 4 +#define RT5682S_STO1_ADCR_SRC_MASK (0x3 << 2) +#define RT5682S_STO1_ADCR_SRC_SFT 2 + +/* ADC Mixer to DAC Mixer Control (0x0029) */ +#define RT5682S_M_ADCMIX_L (0x1 << 15) +#define RT5682S_M_ADCMIX_L_SFT 15 +#define RT5682S_M_DAC1_L (0x1 << 14) +#define RT5682S_M_DAC1_L_SFT 14 +#define RT5682S_M_ADCMIX_R (0x1 << 7) +#define RT5682S_M_ADCMIX_R_SFT 7 +#define RT5682S_M_DAC1_R (0x1 << 6) +#define RT5682S_M_DAC1_R_SFT 6 + +/* Stereo1 DAC Mixer Control (0x002a) */ +#define RT5682S_M_DAC_L1_STO_L (0x1 << 15) +#define RT5682S_M_DAC_L1_STO_L_SFT 15 +#define RT5682S_G_DAC_L1_STO_L_MASK (0x1 << 14) +#define RT5682S_G_DAC_L1_STO_L_SFT 14 +#define RT5682S_M_DAC_R1_STO_L (0x1 << 13) +#define RT5682S_M_DAC_R1_STO_L_SFT 13 +#define RT5682S_G_DAC_R1_STO_L_MASK (0x1 << 12) +#define RT5682S_G_DAC_R1_STO_L_SFT 12 +#define RT5682S_M_DAC_L1_STO_R (0x1 << 7) +#define RT5682S_M_DAC_L1_STO_R_SFT 7 +#define RT5682S_G_DAC_L1_STO_R_MASK (0x1 << 6) +#define RT5682S_G_DAC_L1_STO_R_SFT 6 +#define RT5682S_M_DAC_R1_STO_R (0x1 << 5) +#define RT5682S_M_DAC_R1_STO_R_SFT 5 +#define RT5682S_G_DAC_R1_STO_R_MASK (0x1 << 4) +#define RT5682S_G_DAC_R1_STO_R_SFT 4 + +/* Analog DAC1 Input Source Control (0x002b) */ +#define RT5682S_M_ST_STO_L (0x1 << 9) +#define RT5682S_M_ST_STO_L_SFT 9 +#define RT5682S_M_ST_STO_R (0x1 << 8) +#define RT5682S_M_ST_STO_R_SFT 8 +#define RT5682S_DAC_L1_SRC_MASK (0x1 << 4) +#define RT5682S_A_DACL1_SFT 4 +#define RT5682S_DAC_R1_SRC_MASK (0x1) +#define RT5682S_A_DACR1_SFT 0 + +/* Digital Interface Data Control (0x0030) */ +#define RT5682S_IF2_DAC_SEL_MASK (0x3 << 2) +#define RT5682S_IF2_DAC_SEL_SFT 2 +#define RT5682S_IF2_ADC_SEL_MASK (0x3 << 0) +#define RT5682S_IF2_ADC_SEL_SFT 0 + +/* REC Left/Right Mixer Control 2 (0x003c) */ +#define RT5682S_BST_CBJ_MASK (0x3f << 8) +#define RT5682S_BST_CBJ_SFT 8 +#define RT5682S_M_CBJ_RM1_L (0x1 << 7) +#define RT5682S_M_CBJ_RM1_L_SFT 7 +#define RT5682S_M_CBJ_RM1_R (0x1 << 6) +#define RT5682S_M_CBJ_RM1_R_SFT 6 + +/* REC Left/Right Mixer Calibration Control(0x0044) */ +#define RT5682S_PWR_RM1_R_BIT 8 +#define RT5682S_PWR_RM1_L_BIT 0 + +/* Power Management for Digital 1 (0x0061) */ +#define RT5682S_PWR_I2S1 (0x1 << 15) +#define RT5682S_PWR_I2S1_BIT 15 +#define RT5682S_PWR_I2S2 (0x1 << 14) +#define RT5682S_PWR_I2S2_BIT 14 +#define RT5682S_PRE_CHR_DAC_L1 (0x1 << 13) +#define RT5682S_PRE_CHR_DAC_L1_BIT 13 +#define RT5682S_PRE_CHR_DAC_R1 (0x1 << 12) +#define RT5682S_PRE_CHR_DAC_R1_BIT 12 +#define RT5682S_PWR_DAC_L1 (0x1 << 11) +#define RT5682S_PWR_DAC_L1_BIT 11 +#define RT5682S_PWR_DAC_R1 (0x1 << 10) +#define RT5682S_PWR_DAC_R1_BIT 10 +#define RT5682S_PWR_LDO (0x1 << 8) +#define RT5682S_PWR_LDO_BIT 8 +#define RT5682S_PWR_D2S_L (0x1 << 7) +#define RT5682S_PWR_D2S_L_BIT 7 +#define RT5682S_PWR_D2S_R (0x1 << 6) +#define RT5682S_PWR_D2S_R_BIT 6 +#define RT5682S_PWR_ADC_L1 (0x1 << 4) +#define RT5682S_PWR_ADC_L1_BIT 4 +#define RT5682S_PWR_ADC_R1 (0x1 << 3) +#define RT5682S_PWR_ADC_R1_BIT 3 +#define RT5682S_EFUSE_SW_EN (0x1 << 2) +#define RT5682S_EFUSE_SW_DIS (0x0 << 2) +#define RT5682S_PWR_EFUSE (0x1 << 1) +#define RT5682S_PWR_EFUSE_BIT 1 +#define RT5682S_DIG_GATE_CTRL (0x1 << 0) +#define RT5682S_DIG_GATE_CTRL_SFT 0 + +/* Power Management for Digital 2 (0x0062) */ +#define RT5682S_PWR_ADC_S1F (0x1 << 15) +#define RT5682S_PWR_ADC_S1F_BIT 15 +#define RT5682S_PWR_DAC_S1F (0x1 << 10) +#define RT5682S_PWR_DAC_S1F_BIT 10 +#define RT5682S_DLDO_I_LIMIT_MASK (0x1 << 7) +#define RT5682S_DLDO_I_LIMIT_EN (0x1 << 7) +#define RT5682S_DLDO_I_LIMIT_DIS (0x0 << 7) +#define RT5682S_DLDO_I_BIAS_SEL_4 (0x1 << 6) +#define RT5682S_DLDO_I_BIAS_SEL_0 (0x0 << 6) +#define RT5682S_DLDO_REG_TEST_1 (0x1 << 5) +#define RT5682S_DLDO_REG_TEST_0 (0x0 << 5) +#define RT5682S_DLDO_SRC_REG (0x1 << 4) +#define RT5682S_DLDO_SRC_EFUSE (0x0 << 4) + +/* Power Management for Analog 1 (0x0063) */ +#define RT5682S_PWR_VREF1 (0x1 << 15) +#define RT5682S_PWR_VREF1_BIT 15 +#define RT5682S_PWR_FV1 (0x1 << 14) +#define RT5682S_PWR_FV1_BIT 14 +#define RT5682S_PWR_VREF2 (0x1 << 13) +#define RT5682S_PWR_VREF2_BIT 13 +#define RT5682S_PWR_FV2 (0x1 << 12) +#define RT5682S_PWR_FV2_BIT 12 +#define RT5682S_LDO1_DBG_MASK (0x3 << 10) +#define RT5682S_PWR_MB (0x1 << 9) +#define RT5682S_PWR_MB_BIT 9 +#define RT5682S_PWR_BG (0x1 << 7) +#define RT5682S_PWR_BG_BIT 7 +#define RT5682S_LDO1_BYPASS_MASK (0x1 << 6) +#define RT5682S_LDO1_BYPASS (0x1 << 6) +#define RT5682S_LDO1_NOT_BYPASS (0x0 << 6) + +/* Power Management for Analog 2 (0x0064) */ +#define RT5682S_PWR_MCLK0_WD (0x1 << 15) +#define RT5682S_PWR_MCLK0_WD_BIT 15 +#define RT5682S_PWR_MCLK1_WD (0x1 << 14) +#define RT5682S_PWR_MCLK1_WD_BIT 14 +#define RT5682S_RST_MCLK0 (0x1 << 13) +#define RT5682S_RST_MCLK0_BIT 13 +#define RT5682S_RST_MCLK1 (0x1 << 12) +#define RT5682S_RST_MCLK1_BIT 12 +#define RT5682S_PWR_MB1 (0x1 << 11) +#define RT5682S_PWR_MB1_PWR_DOWN (0x0 << 11) +#define RT5682S_PWR_MB1_BIT 11 +#define RT5682S_PWR_MB2 (0x1 << 10) +#define RT5682S_PWR_MB2_PWR_DOWN (0x0 << 10) +#define RT5682S_PWR_MB2_BIT 10 +#define RT5682S_PWR_JD_MASK (0x1 << 0) +#define RT5682S_PWR_JD_ENABLE (0x1 << 0) +#define RT5682S_PWR_JD_DISABLE (0x0 << 0) + +/* Power Management for Analog 3 (0x0065) */ +#define RT5682S_PWR_LDO_PLLA (0x1 << 15) +#define RT5682S_PWR_LDO_PLLA_BIT 15 +#define RT5682S_PWR_LDO_PLLB (0x1 << 14) +#define RT5682S_PWR_LDO_PLLB_BIT 14 +#define RT5682S_PWR_BIAS_PLLA (0x1 << 13) +#define RT5682S_PWR_BIAS_PLLA_BIT 13 +#define RT5682S_PWR_BIAS_PLLB (0x1 << 12) +#define RT5682S_PWR_BIAS_PLLB_BIT 12 +#define RT5682S_PWR_CBJ (0x1 << 9) +#define RT5682S_PWR_CBJ_BIT 9 +#define RT5682S_RSTB_PLLB (0x1 << 7) +#define RT5682S_RSTB_PLLB_BIT 7 +#define RT5682S_RSTB_PLLA (0x1 << 6) +#define RT5682S_RSTB_PLLA_BIT 6 +#define RT5682S_PWR_PLLB (0x1 << 5) +#define RT5682S_PWR_PLLB_BIT 5 +#define RT5682S_PWR_PLLA (0x1 << 4) +#define RT5682S_PWR_PLLA_BIT 4 +#define RT5682S_PWR_LDO_MB2 (0x1 << 2) +#define RT5682S_PWR_LDO_MB2_BIT 2 +#define RT5682S_PWR_LDO_MB1 (0x1 << 1) +#define RT5682S_PWR_LDO_MB1_BIT 1 +#define RT5682S_PWR_BGLDO (0x1 << 0) +#define RT5682S_PWR_BGLDO_BIT 0 + +/* Power Management for Mixer (0x0066) */ +#define RT5682S_PWR_CLK_COMP_8FS (0x1 << 15) +#define RT5682S_PWR_CLK_COMP_8FS_BIT 15 +#define RT5682S_DBG_BGLDO_MASK (0x3 << 12) +#define RT5682S_DBG_BGLDO_SFT 12 +#define RT5682S_DBG_BGLDO_MB1_MASK (0x3 << 10) +#define RT5682S_DBG_BGLDO_MB1_SFT 10 +#define RT5682S_DBG_BGLDO_MB2_MASK (0x3 << 8) +#define RT5682S_DBG_BGLDO_MB2_SFT 8 +#define RT5682S_DLDO_BGLDO_MASK (0x3 << 6) +#define RT5682S_DLDO_BGLDO_MB2_SFT 6 +#define RT5682S_PWR_STO1_DAC_L (0x1 << 5) +#define RT5682S_PWR_STO1_DAC_L_BIT 5 +#define RT5682S_PWR_STO1_DAC_R (0x1 << 4) +#define RT5682S_PWR_STO1_DAC_R_BIT 4 +#define RT5682S_DVO_BGLDO_MB1_MASK (0x3 << 2) +#define RT5682S_DVO_BGLDO_MB1_SFT 2 +#define RT5682S_DVO_BGLDO_MB2_MASK (0x3 << 0) + +/* MCLK and System Clock Detection Control (0x006b) */ +#define RT5682S_SYS_CLK_DET (0x1 << 15) +#define RT5682S_SYS_CLK_DET_SFT 15 +#define RT5682S_PLL1_CLK_DET (0x1 << 14) +#define RT5682S_PLL1_CLK_DET_SFT 14 + +/* Digital Microphone Control 1 (0x006e) */ +#define RT5682S_DMIC_1_EN_MASK (0x1 << 15) +#define RT5682S_DMIC_1_EN_SFT 15 +#define RT5682S_DMIC_1_DIS (0x0 << 15) +#define RT5682S_DMIC_1_EN (0x1 << 15) +#define RT5682S_FIFO_CLK_DIV_MASK (0x7 << 12) +#define RT5682S_FIFO_CLK_DIV_2 (0x1 << 12) +#define RT5682S_DMIC_1_DP_MASK (0x3 << 4) +#define RT5682S_DMIC_1_DP_SFT 4 +#define RT5682S_DMIC_1_DP_GPIO2 (0x0 << 4) +#define RT5682S_DMIC_1_DP_GPIO5 (0x1 << 4) +#define RT5682S_DMIC_CLK_MASK (0xf << 0) +#define RT5682S_DMIC_CLK_SFT 0 + +/* I2S1 Audio Serial Data Port Control (0x0070) */ +#define RT5682S_SEL_ADCDAT_MASK (0x1 << 15) +#define RT5682S_SEL_ADCDAT_OUT (0x0 << 15) +#define RT5682S_SEL_ADCDAT_IN (0x1 << 15) +#define RT5682S_SEL_ADCDAT_SFT 15 +#define RT5682S_I2S1_TX_CHL_MASK (0x7 << 12) +#define RT5682S_I2S1_TX_CHL_SFT 12 +#define RT5682S_I2S1_TX_CHL_16 (0x0 << 12) +#define RT5682S_I2S1_TX_CHL_20 (0x1 << 12) +#define RT5682S_I2S1_TX_CHL_24 (0x2 << 12) +#define RT5682S_I2S1_TX_CHL_32 (0x3 << 12) +#define RT5682S_I2S1_TX_CHL_8 (0x4 << 12) +#define RT5682S_I2S1_RX_CHL_MASK (0x7 << 8) +#define RT5682S_I2S1_RX_CHL_SFT 8 +#define RT5682S_I2S1_RX_CHL_16 (0x0 << 8) +#define RT5682S_I2S1_RX_CHL_20 (0x1 << 8) +#define RT5682S_I2S1_RX_CHL_24 (0x2 << 8) +#define RT5682S_I2S1_RX_CHL_32 (0x3 << 8) +#define RT5682S_I2S1_RX_CHL_8 (0x4 << 8) +#define RT5682S_I2S1_MONO_MASK (0x1 << 7) +#define RT5682S_I2S1_MONO_EN (0x1 << 7) +#define RT5682S_I2S1_MONO_DIS (0x0 << 7) +#define RT5682S_I2S1_DL_MASK (0x7 << 4) +#define RT5682S_I2S1_DL_SFT 4 +#define RT5682S_I2S1_DL_16 (0x0 << 4) +#define RT5682S_I2S1_DL_20 (0x1 << 4) +#define RT5682S_I2S1_DL_24 (0x2 << 4) +#define RT5682S_I2S1_DL_32 (0x3 << 4) +#define RT5682S_I2S1_DL_8 (0x4 << 4) + +/* I2S1/2 Audio Serial Data Port Control (0x0071) */ +#define RT5682S_I2S2_MS_MASK (0x1 << 15) +#define RT5682S_I2S2_MS_SFT 15 +#define RT5682S_I2S2_MS_M (0x0 << 15) +#define RT5682S_I2S2_MS_S (0x1 << 15) +#define RT5682S_I2S2_PIN_CFG_MASK (0x1 << 14) +#define RT5682S_I2S2_PIN_CFG_SFT 14 +#define RT5682S_I2S2_OUT_MASK (0x1 << 9) +#define RT5682S_I2S2_OUT_SFT 9 +#define RT5682S_I2S2_OUT_UM (0x0 << 9) +#define RT5682S_I2S2_OUT_M (0x1 << 9) +#define RT5682S_I2S_BP_MASK (0x1 << 8) +#define RT5682S_I2S_BP_SFT 8 +#define RT5682S_I2S_BP_NOR (0x0 << 8) +#define RT5682S_I2S_BP_INV (0x1 << 8) +#define RT5682S_I2S2_MONO_MASK (0x1 << 7) +#define RT5682S_I2S2_MONO_EN (0x1 << 7) +#define RT5682S_I2S2_MONO_DIS (0x0 << 7) +#define RT5682S_I2S2_DL_MASK (0x7 << 4) +#define RT5682S_I2S2_DL_SFT 4 +#define RT5682S_I2S2_DL_8 (0x0 << 4) +#define RT5682S_I2S2_DL_16 (0x1 << 4) +#define RT5682S_I2S2_DL_20 (0x2 << 4) +#define RT5682S_I2S2_DL_24 (0x3 << 4) +#define RT5682S_I2S2_DL_32 (0x4 << 4) +#define RT5682S_I2S_DF_MASK (0x7) +#define RT5682S_I2S_DF_SFT 0 +#define RT5682S_I2S_DF_I2S (0x0) +#define RT5682S_I2S_DF_LEFT (0x1) +#define RT5682S_I2S_DF_PCM_A (0x2) +#define RT5682S_I2S_DF_PCM_B (0x3) +#define RT5682S_I2S_DF_PCM_A_N (0x6) +#define RT5682S_I2S_DF_PCM_B_N (0x7) + +/* ADC/DAC Clock Control 1 (0x0073) */ +#define RT5682S_ADC_OSR_MASK (0xf << 12) +#define RT5682S_ADC_OSR_SFT 12 +#define RT5682S_ADC_OSR_D_1 (0x0 << 12) +#define RT5682S_ADC_OSR_D_2 (0x1 << 12) +#define RT5682S_ADC_OSR_D_4 (0x2 << 12) +#define RT5682S_ADC_OSR_D_6 (0x3 << 12) +#define RT5682S_ADC_OSR_D_8 (0x4 << 12) +#define RT5682S_ADC_OSR_D_12 (0x5 << 12) +#define RT5682S_ADC_OSR_D_16 (0x6 << 12) +#define RT5682S_ADC_OSR_D_24 (0x7 << 12) +#define RT5682S_ADC_OSR_D_32 (0x8 << 12) +#define RT5682S_ADC_OSR_D_48 (0x9 << 12) +#define RT5682S_I2S_M_D_MASK (0xf << 8) +#define RT5682S_I2S_M_D_SFT 8 +#define RT5682S_I2S_M_D_1 (0x0 << 8) +#define RT5682S_I2S_M_D_2 (0x1 << 8) +#define RT5682S_I2S_M_D_3 (0x2 << 8) +#define RT5682S_I2S_M_D_4 (0x3 << 8) +#define RT5682S_I2S_M_D_6 (0x4 << 8) +#define RT5682S_I2S_M_D_8 (0x5 << 8) +#define RT5682S_I2S_M_D_12 (0x6 << 8) +#define RT5682S_I2S_M_D_16 (0x7 << 8) +#define RT5682S_I2S_M_D_24 (0x8 << 8) +#define RT5682S_I2S_M_D_32 (0x9 << 8) +#define RT5682S_I2S_M_D_48 (0x10 << 8) +#define RT5682S_I2S_M_CLK_SRC_MASK (0x7 << 4) +#define RT5682S_I2S_M_CLK_SRC_SFT 4 +#define RT5682S_DAC_OSR_MASK (0xf << 0) +#define RT5682S_DAC_OSR_SFT 0 +#define RT5682S_DAC_OSR_D_1 (0x0 << 0) +#define RT5682S_DAC_OSR_D_2 (0x1 << 0) +#define RT5682S_DAC_OSR_D_4 (0x2 << 0) +#define RT5682S_DAC_OSR_D_6 (0x3 << 0) +#define RT5682S_DAC_OSR_D_8 (0x4 << 0) +#define RT5682S_DAC_OSR_D_12 (0x5 << 0) +#define RT5682S_DAC_OSR_D_16 (0x6 << 0) +#define RT5682S_DAC_OSR_D_24 (0x7 << 0) +#define RT5682S_DAC_OSR_D_32 (0x8 << 0) +#define RT5682S_DAC_OSR_D_48 (0x9 << 0) + +/* ADC/DAC Clock Control 2 (0x0074) */ +#define RT5682S_I2S2_BCLK_MS2_MASK (0x1 << 11) +#define RT5682S_I2S2_BCLK_MS2_SFT 11 +#define RT5682S_I2S2_BCLK_MS2_32 (0x0 << 11) +#define RT5682S_I2S2_BCLK_MS2_64 (0x1 << 11) + + +/* TDM control 1 (0x0079) */ +#define RT5682S_TDM_TX_CH_MASK (0x3 << 12) +#define RT5682S_TDM_TX_CH_2 (0x0 << 12) +#define RT5682S_TDM_TX_CH_4 (0x1 << 12) +#define RT5682S_TDM_TX_CH_6 (0x2 << 12) +#define RT5682S_TDM_TX_CH_8 (0x3 << 12) +#define RT5682S_TDM_RX_CH_MASK (0x3 << 8) +#define RT5682S_TDM_RX_CH_2 (0x0 << 8) +#define RT5682S_TDM_RX_CH_4 (0x1 << 8) +#define RT5682S_TDM_RX_CH_6 (0x2 << 8) +#define RT5682S_TDM_RX_CH_8 (0x3 << 8) +#define RT5682S_TDM_ADC_LCA_MASK (0x7 << 4) +#define RT5682S_TDM_ADC_LCA_SFT 4 +#define RT5682S_TDM_ADC_DL_MASK (0x3 << 0) +#define RT5682S_TDM_ADC_DL_SFT 0 + +/* TDM control 2 (0x007a) */ +#define RT5682S_IF1_ADC1_SEL_SFT 14 +#define RT5682S_IF1_ADC2_SEL_SFT 12 +#define RT5682S_IF1_ADC3_SEL_SFT 10 +#define RT5682S_IF1_ADC4_SEL_SFT 8 +#define RT5682S_TDM_ADC_SEL_SFT 3 + +/* TDM control 3 (0x007b) */ +#define RT5682S_TDM_EN (0x1 << 7) + +/* TDM/I2S control (0x007e) */ +#define RT5682S_TDM_S_BP_MASK (0x1 << 15) +#define RT5682S_TDM_S_BP_SFT 15 +#define RT5682S_TDM_S_BP_NOR (0x0 << 15) +#define RT5682S_TDM_S_BP_INV (0x1 << 15) +#define RT5682S_TDM_S_LP_MASK (0x1 << 14) +#define RT5682S_TDM_S_LP_SFT 14 +#define RT5682S_TDM_S_LP_NOR (0x0 << 14) +#define RT5682S_TDM_S_LP_INV (0x1 << 14) +#define RT5682S_TDM_DF_MASK (0x7 << 11) +#define RT5682S_TDM_DF_SFT 11 +#define RT5682S_TDM_DF_I2S (0x0 << 11) +#define RT5682S_TDM_DF_LEFT (0x1 << 11) +#define RT5682S_TDM_DF_PCM_A (0x2 << 11) +#define RT5682S_TDM_DF_PCM_B (0x3 << 11) +#define RT5682S_TDM_DF_PCM_A_N (0x6 << 11) +#define RT5682S_TDM_DF_PCM_B_N (0x7 << 11) +#define RT5682S_TDM_BCLK_MS1_MASK (0x3 << 8) +#define RT5682S_TDM_BCLK_MS1_SFT 8 +#define RT5682S_TDM_BCLK_MS1_32 (0x0 << 8) +#define RT5682S_TDM_BCLK_MS1_64 (0x1 << 8) +#define RT5682S_TDM_BCLK_MS1_128 (0x2 << 8) +#define RT5682S_TDM_BCLK_MS1_256 (0x3 << 8) +#define RT5682S_TDM_BCLK_MS1_16 (0x4 << 8) +#define RT5682S_TDM_CL_MASK (0x3 << 4) +#define RT5682S_TDM_CL_16 (0x0 << 4) +#define RT5682S_TDM_CL_20 (0x1 << 4) +#define RT5682S_TDM_CL_24 (0x2 << 4) +#define RT5682S_TDM_CL_32 (0x3 << 4) +#define RT5682S_TDM_M_BP_MASK (0x1 << 2) +#define RT5682S_TDM_M_BP_SFT 2 +#define RT5682S_TDM_M_BP_NOR (0x0 << 2) +#define RT5682S_TDM_M_BP_INV (0x1 << 2) +#define RT5682S_TDM_M_LP_MASK (0x1 << 1) +#define RT5682S_TDM_M_LP_SFT 1 +#define RT5682S_TDM_M_LP_NOR (0x0 << 1) +#define RT5682S_TDM_M_LP_INV (0x1 << 1) +#define RT5682S_TDM_MS_MASK (0x1 << 0) +#define RT5682S_TDM_MS_SFT 0 +#define RT5682S_TDM_MS_S (0x0 << 0) +#define RT5682S_TDM_MS_M (0x1 << 0) + +/* Global Clock Control (0x0080) */ +#define RT5682S_SCLK_SRC_MASK (0x7 << 13) +#define RT5682S_SCLK_SRC_SFT 13 +#define RT5682S_PLL_SRC_MASK (0x3 << 8) +#define RT5682S_PLL_SRC_SFT 8 +#define RT5682S_PLL_SRC_MCLK (0x0 << 8) +#define RT5682S_PLL_SRC_BCLK1 (0x1 << 8) +#define RT5682S_PLL_SRC_RC (0x3 << 8) + +/* PLL tracking mode 1 (0x0083) */ +#define RT5682S_DA_ASRC_MASK (0x1 << 13) +#define RT5682S_DA_ASRC_SFT 13 +#define RT5682S_DAC_STO1_ASRC_MASK (0x1 << 12) +#define RT5682S_DAC_STO1_ASRC_SFT 12 +#define RT5682S_AD_ASRC_MASK (0x1 << 8) +#define RT5682S_AD_ASRC_SFT 8 +#define RT5682S_AD_ASRC_SEL_MASK (0x1 << 4) +#define RT5682S_AD_ASRC_SEL_SFT 4 +#define RT5682S_DMIC_ASRC_MASK (0x1 << 3) +#define RT5682S_DMIC_ASRC_SFT 3 +#define RT5682S_ADC_STO1_ASRC_MASK (0x1 << 2) +#define RT5682S_ADC_STO1_ASRC_SFT 2 +#define RT5682S_DA_ASRC_SEL_MASK (0x1 << 0) +#define RT5682S_DA_ASRC_SEL_SFT 0 + +/* PLL tracking mode 2 3 (0x0084)(0x0085)*/ +#define RT5682S_FILTER_CLK_SEL_MASK (0x7 << 12) +#define RT5682S_FILTER_CLK_SEL_SFT 12 +#define RT5682S_FILTER_CLK_DIV_MASK (0xf << 8) +#define RT5682S_FILTER_CLK_DIV_SFT 8 + +/* ASRC Control 4 (0x0086) */ +#define RT5682S_ASRCIN_FTK_N1_MASK (0x3 << 14) +#define RT5682S_ASRCIN_FTK_N1_SFT 14 +#define RT5682S_ASRCIN_FTK_N2_MASK (0x3 << 12) +#define RT5682S_ASRCIN_FTK_N2_SFT 12 +#define RT5682S_ASRCIN_FTK_M1_MASK (0x7 << 8) +#define RT5682S_ASRCIN_FTK_M1_SFT 8 +#define RT5682S_ASRCIN_FTK_M2_MASK (0x7 << 4) +#define RT5682S_ASRCIN_FTK_M2_SFT 4 + +/* ASRC Control 11 (0x008c) */ +#define RT5682S_ASRCIN_AUTO_CLKOUT_MASK (0x1 << 5) +#define RT5682S_ASRCIN_AUTO_CLKOUT_EN (0x1 << 5) +#define RT5682S_ASRCIN_AUTO_CLKOUT_DIS (0x0 << 5) +#define RT5682S_ASRCIN_AUTO_RST_MASK (0x1 << 4) +#define RT5682S_ASRCIN_AUTO_RST_EN (0x1 << 4) +#define RT5682S_ASRCIN_AUTO_RST_DIS (0x0 << 4) +#define RT5682S_SEL_LRCK_DET_MASK (0x3) +#define RT5682S_SEL_LRCK_DET_DIV8 (0x3) +#define RT5682S_SEL_LRCK_DET_DIV4 (0x2) +#define RT5682S_SEL_LRCK_DET_DIV2 (0x1) +#define RT5682S_SEL_LRCK_DET_DIV1 (0x0) + +/* Depop Mode Control 1 (0x008e) */ +#define RT5682S_OUT_HP_L_EN (0x1 << 6) +#define RT5682S_OUT_HP_R_EN (0x1 << 5) +#define RT5682S_LDO_PUMP_EN (0x1 << 4) +#define RT5682S_LDO_PUMP_EN_SFT 4 +#define RT5682S_PUMP_EN (0x1 << 3) +#define RT5682S_PUMP_EN_SFT 3 +#define RT5682S_CAPLESS_L_EN (0x1 << 1) +#define RT5682S_CAPLESS_L_EN_SFT 1 +#define RT5682S_CAPLESS_R_EN (0x1 << 0) +#define RT5682S_CAPLESS_R_EN_SFT 0 + +/* Depop Mode Control 2 (0x8f) */ +#define RT5682S_RAMP_MASK (0x1 << 12) +#define RT5682S_RAMP_SFT 12 +#define RT5682S_RAMP_DIS (0x0 << 12) +#define RT5682S_RAMP_EN (0x1 << 12) +#define RT5682S_BPS_MASK (0x1 << 11) +#define RT5682S_BPS_SFT 11 +#define RT5682S_BPS_DIS (0x0 << 11) +#define RT5682S_BPS_EN (0x1 << 11) +#define RT5682S_FAST_UPDN_MASK (0x1 << 10) +#define RT5682S_FAST_UPDN_SFT 10 +#define RT5682S_FAST_UPDN_DIS (0x0 << 10) +#define RT5682S_FAST_UPDN_EN (0x1 << 10) +#define RT5682S_VLO_MASK (0x1 << 7) +#define RT5682S_VLO_SFT 7 +#define RT5682S_VLO_3V (0x0 << 7) +#define RT5682S_VLO_33V (0x1 << 7) + +/* HPOUT charge pump 1 (0x0091) */ +#define RT5682S_OSW_L_MASK (0x1 << 11) +#define RT5682S_OSW_L_SFT 11 +#define RT5682S_OSW_L_DIS (0x0 << 11) +#define RT5682S_OSW_L_EN (0x1 << 11) +#define RT5682S_OSW_R_MASK (0x1 << 10) +#define RT5682S_OSW_R_SFT 10 +#define RT5682S_OSW_R_DIS (0x0 << 10) +#define RT5682S_OSW_R_EN (0x1 << 10) +#define RT5682S_PM_HP_MASK (0x3 << 8) +#define RT5682S_PM_HP_SFT 8 +#define RT5682S_PM_HP_LV (0x0 << 8) +#define RT5682S_PM_HP_MV (0x1 << 8) +#define RT5682S_PM_HP_HV (0x2 << 8) + +/* Micbias Control1 (0x93) */ +#define RT5682S_MIC1_OV_MASK (0x3 << 14) +#define RT5682S_MIC1_OV_SFT 14 +#define RT5682S_MIC1_OV_2V7 (0x0 << 14) +#define RT5682S_MIC1_OV_2V4 (0x1 << 14) +#define RT5682S_MIC1_OV_2V25 (0x3 << 14) +#define RT5682S_MIC1_OV_1V8 (0x4 << 14) +#define RT5682S_MIC2_OV_MASK (0x3 << 8) +#define RT5682S_MIC2_OV_SFT 8 +#define RT5682S_MIC2_OV_2V7 (0x0 << 8) +#define RT5682S_MIC2_OV_2V4 (0x1 << 8) +#define RT5682S_MIC2_OV_2V25 (0x3 << 8) +#define RT5682S_MIC2_OV_1V8 (0x4 << 8) + +/* Micbias Control2 (0x0094) */ +#define RT5682S_PWR_CLK25M_MASK (0x1 << 9) +#define RT5682S_PWR_CLK25M_SFT 9 +#define RT5682S_PWR_CLK25M_PD (0x0 << 9) +#define RT5682S_PWR_CLK25M_PU (0x1 << 9) +#define RT5682S_PWR_CLK1M_MASK (0x1 << 8) +#define RT5682S_PWR_CLK1M_SFT 8 +#define RT5682S_PWR_CLK1M_PD (0x0 << 8) +#define RT5682S_PWR_CLK1M_PU (0x1 << 8) + +/* PLL M/N/K Code Control 1 (0x0098) */ +#define RT5682S_PLLA_N_MASK (0x1ff << 0) + +/* PLL M/N/K Code Control 2 (0x0099) */ +#define RT5682S_PLLA_M_MASK (0x1f << 8) +#define RT5682S_PLLA_M_SFT 8 +#define RT5682S_PLLA_K_MASK (0x1f << 0) + +/* PLL M/N/K Code Control 3 (0x009a) */ +#define RT5682S_PLLB_N_MASK (0x3ff << 0) + +/* PLL M/N/K Code Control 4 (0x009b) */ +#define RT5682S_PLLB_M_MASK (0x1f << 8) +#define RT5682S_PLLB_M_SFT 8 +#define RT5682S_PLLB_K_MASK (0x1f << 0) + +/* PLL M/N/K Code Control 6 (0x009d) */ +#define RT5682S_PLLB_SEL_PS_MASK (0x1 << 13) +#define RT5682S_PLLB_SEL_PS_SFT 13 +#define RT5682S_PLLB_BYP_PS_MASK (0x1 << 12) +#define RT5682S_PLLB_BYP_PS_SFT 12 +#define RT5682S_PLLB_M_BP_MASK (0x1 << 11) +#define RT5682S_PLLB_M_BP_SFT 11 +#define RT5682S_PLLB_K_BP_MASK (0x1 << 10) +#define RT5682S_PLLB_K_BP_SFT 10 +#define RT5682S_PLLA_M_BP_MASK (0x1 << 7) +#define RT5682S_PLLA_M_BP_SFT 7 +#define RT5682S_PLLA_K_BP_MASK (0x1 << 6) +#define RT5682S_PLLA_K_BP_SFT 6 + +/* PLL M/N/K Code Control 7 (0x009e) */ +#define RT5682S_PLLB_SRC_MASK (0x1) +#define RT5682S_PLLB_SRC_DFIN (0x1) +#define RT5682S_PLLB_SRC_PLLA (0x0) + +/* RC Clock Control (0x009f) */ +#define RT5682S_POW_IRQ (0x1 << 15) +#define RT5682S_POW_JDH (0x1 << 14) + +/* I2S2 Master Mode Clock Control 1 (0x00a0) */ +#define RT5682S_I2S2_M_CLK_SRC_MASK (0x7 << 4) +#define RT5682S_I2S2_M_CLK_SRC_SFT 4 +#define RT5682S_I2S2_M_D_MASK (0xf << 0) +#define RT5682S_I2S2_M_D_1 (0x0) +#define RT5682S_I2S2_M_D_2 (0x1) +#define RT5682S_I2S2_M_D_3 (0x2) +#define RT5682S_I2S2_M_D_4 (0x3) +#define RT5682S_I2S2_M_D_6 (0x4) +#define RT5682S_I2S2_M_D_8 (0x5) +#define RT5682S_I2S2_M_D_12 (0x6) +#define RT5682S_I2S2_M_D_16 (0x7) +#define RT5682S_I2S2_M_D_24 (0x8) +#define RT5682S_I2S2_M_D_32 (0x9) +#define RT5682S_I2S2_M_D_48 (0xa) +#define RT5682S_I2S2_M_D_SFT 0 + +/* IRQ Control 1 (0x00b6) */ +#define RT5682S_JD1_PULSE_EN_MASK (0x1 << 10) +#define RT5682S_JD1_PULSE_EN_SFT 10 +#define RT5682S_JD1_PULSE_DIS (0x0 << 10) +#define RT5682S_JD1_PULSE_EN (0x1 << 10) + +/* IRQ Control 2 (0x00b7) */ +#define RT5682S_JD1_EN_MASK (0x1 << 15) +#define RT5682S_JD1_EN_SFT 15 +#define RT5682S_JD1_DIS (0x0 << 15) +#define RT5682S_JD1_EN (0x1 << 15) +#define RT5682S_JD1_POL_MASK (0x1 << 13) +#define RT5682S_JD1_POL_NOR (0x0 << 13) +#define RT5682S_JD1_POL_INV (0x1 << 13) +#define RT5682S_JD1_IRQ_MASK (0x1 << 10) +#define RT5682S_JD1_IRQ_LEV (0x0 << 10) +#define RT5682S_JD1_IRQ_PUL (0x1 << 10) + +/* IRQ Control 3 (0x00b8) */ +#define RT5682S_IL_IRQ_MASK (0x1 << 7) +#define RT5682S_IL_IRQ_DIS (0x0 << 7) +#define RT5682S_IL_IRQ_EN (0x1 << 7) +#define RT5682S_IL_IRQ_TYPE_MASK (0x1 << 4) +#define RT5682S_IL_IRQ_LEV (0x0 << 4) +#define RT5682S_IL_IRQ_PUL (0x1 << 4) + +/* GPIO Control 1 (0x00c0) */ +#define RT5682S_GP1_PIN_MASK (0x3 << 14) +#define RT5682S_GP1_PIN_SFT 14 +#define RT5682S_GP1_PIN_GPIO1 (0x0 << 14) +#define RT5682S_GP1_PIN_IRQ (0x1 << 14) +#define RT5682S_GP1_PIN_DMIC_CLK (0x2 << 14) +#define RT5682S_GP2_PIN_MASK (0x3 << 12) +#define RT5682S_GP2_PIN_SFT 12 +#define RT5682S_GP2_PIN_GPIO2 (0x0 << 12) +#define RT5682S_GP2_PIN_LRCK2 (0x1 << 12) +#define RT5682S_GP2_PIN_DMIC_SDA (0x2 << 12) +#define RT5682S_GP3_PIN_MASK (0x3 << 10) +#define RT5682S_GP3_PIN_SFT 10 +#define RT5682S_GP3_PIN_GPIO3 (0x0 << 10) +#define RT5682S_GP3_PIN_BCLK2 (0x1 << 10) +#define RT5682S_GP3_PIN_DMIC_CLK (0x2 << 10) +#define RT5682S_GP4_PIN_MASK (0x3 << 8) +#define RT5682S_GP4_PIN_SFT 8 +#define RT5682S_GP4_PIN_GPIO4 (0x0 << 8) +#define RT5682S_GP4_PIN_ADCDAT1 (0x1 << 8) +#define RT5682S_GP4_PIN_DMIC_CLK (0x2 << 8) +#define RT5682S_GP4_PIN_ADCDAT2 (0x3 << 8) +#define RT5682S_GP5_PIN_MASK (0x3 << 6) +#define RT5682S_GP5_PIN_SFT 6 +#define RT5682S_GP5_PIN_GPIO5 (0x0 << 6) +#define RT5682S_GP5_PIN_DACDAT1 (0x1 << 6) +#define RT5682S_GP5_PIN_DMIC_SDA (0x2 << 6) +#define RT5682S_GP6_PIN_MASK (0x1 << 5) +#define RT5682S_GP6_PIN_SFT 5 +#define RT5682S_GP6_PIN_GPIO6 (0x0 << 5) +#define RT5682S_GP6_PIN_LRCK1 (0x1 << 5) + +/* GPIO Control 2 (0x00c1)*/ +#define RT5682S_GP1_PF_MASK (0x1 << 15) +#define RT5682S_GP1_PF_IN (0x0 << 15) +#define RT5682S_GP1_PF_OUT (0x1 << 15) +#define RT5682S_GP1_OUT_MASK (0x1 << 14) +#define RT5682S_GP1_OUT_L (0x0 << 14) +#define RT5682S_GP1_OUT_H (0x1 << 14) +#define RT5682S_GP2_PF_MASK (0x1 << 13) +#define RT5682S_GP2_PF_IN (0x0 << 13) +#define RT5682S_GP2_PF_OUT (0x1 << 13) +#define RT5682S_GP2_OUT_MASK (0x1 << 12) +#define RT5682S_GP2_OUT_L (0x0 << 12) +#define RT5682S_GP2_OUT_H (0x1 << 12) +#define RT5682S_GP3_PF_MASK (0x1 << 11) +#define RT5682S_GP3_PF_IN (0x0 << 11) +#define RT5682S_GP3_PF_OUT (0x1 << 11) +#define RT5682S_GP3_OUT_MASK (0x1 << 10) +#define RT5682S_GP3_OUT_L (0x0 << 10) +#define RT5682S_GP3_OUT_H (0x1 << 10) +#define RT5682S_GP4_PF_MASK (0x1 << 9) +#define RT5682S_GP4_PF_IN (0x0 << 9) +#define RT5682S_GP4_PF_OUT (0x1 << 9) +#define RT5682S_GP4_OUT_MASK (0x1 << 8) +#define RT5682S_GP4_OUT_L (0x0 << 8) +#define RT5682S_GP4_OUT_H (0x1 << 8) +#define RT5682S_GP5_PF_MASK (0x1 << 7) +#define RT5682S_GP5_PF_IN (0x0 << 7) +#define RT5682S_GP5_PF_OUT (0x1 << 7) +#define RT5682S_GP5_OUT_MASK (0x1 << 6) +#define RT5682S_GP5_OUT_L (0x0 << 6) +#define RT5682S_GP5_OUT_H (0x1 << 6) +#define RT5682S_GP6_PF_MASK (0x1 << 5) +#define RT5682S_GP6_PF_IN (0x0 << 5) +#define RT5682S_GP6_PF_OUT (0x1 << 5) +#define RT5682S_GP6_OUT_MASK (0x1 << 4) +#define RT5682S_GP6_OUT_L (0x0 << 4) +#define RT5682S_GP6_OUT_H (0x1 << 4) + +/* GPIO Status (0x00c2) */ +#define RT5682S_GP6_ST (0x1 << 6) +#define RT5682S_GP5_ST (0x1 << 5) +#define RT5682S_GP4_ST (0x1 << 4) +#define RT5682S_GP3_ST (0x1 << 3) +#define RT5682S_GP2_ST (0x1 << 2) +#define RT5682S_GP1_ST (0x1 << 1) + +/* Soft volume and zero cross control 1 (0x00d9) */ +#define RT5682S_ZCD_MASK (0x1 << 10) +#define RT5682S_ZCD_SFT 10 +#define RT5682S_ZCD_PD (0x0 << 10) +#define RT5682S_ZCD_PU (0x1 << 10) + +/* 4 Button Inline Command Control 2 (0x00e3) */ +#define RT5682S_4BTN_IL_MASK (0x1 << 15) +#define RT5682S_4BTN_IL_EN (0x1 << 15) +#define RT5682S_4BTN_IL_DIS (0x0 << 15) +#define RT5682S_4BTN_IL_RST_MASK (0x1 << 14) +#define RT5682S_4BTN_IL_NOR (0x1 << 14) +#define RT5682S_4BTN_IL_RST (0x0 << 14) + +/* 4 Button Inline Command Control 3~6 (0x00e5~0x00e8) */ +#define RT5682S_4BTN_IL_HOLD_WIN_MASK (0x7f << 8) +#define RT5682S_4BTN_IL_HOLD_WIN_SFT 8 +#define RT5682S_4BTN_IL_CLICK_WIN_MASK (0x7f) +#define RT5682S_4BTN_IL_CLICK_WIN_SFT 0 + +/* Analog JD Control (0x00f0) */ +#define RT5682S_JDH_RS_MASK (0x1 << 4) +#define RT5682S_JDH_NO_PLUG (0x1 << 4) +#define RT5682S_JDH_PLUG (0x0 << 4) + +/* Charge Pump Internal Register1 (0x0125) */ +#define RT5682S_CP_CLK_HP_MASK (0x3 << 4) +#define RT5682S_CP_CLK_HP_100KHZ (0x0 << 4) +#define RT5682S_CP_CLK_HP_200KHZ (0x1 << 4) +#define RT5682S_CP_CLK_HP_300KHZ (0x2 << 4) +#define RT5682S_CP_CLK_HP_600KHZ (0x3 << 4) + +/* Pad Driving Control (0x0136) */ +#define RT5682S_PAD_DRV_GP1_MASK (0x1 << 14) +#define RT5682S_PAD_DRV_GP1_HIGH (0x1 << 14) +#define RT5682S_PAD_DRV_GP1_LOW (0x0 << 14) +#define RT5682S_PAD_DRV_GP2_MASK (0x1 << 12) +#define RT5682S_PAD_DRV_GP2_HIGH (0x1 << 12) +#define RT5682S_PAD_DRV_GP2_LOW (0x0 << 12) +#define RT5682S_PAD_DRV_GP3_MASK (0x1 << 10) +#define RT5682S_PAD_DRV_GP3_HIGH (0x1 << 10) +#define RT5682S_PAD_DRV_GP3_LOW (0x0 << 10) +#define RT5682S_PAD_DRV_GP4_MASK (0x1 << 8) +#define RT5682S_PAD_DRV_GP4_HIGH (0x1 << 8) +#define RT5682S_PAD_DRV_GP4_LOW (0x0 << 8) +#define RT5682S_PAD_DRV_GP5_MASK (0x1 << 6) +#define RT5682S_PAD_DRV_GP5_HIGH (0x1 << 6) +#define RT5682S_PAD_DRV_GP5_LOW (0x0 << 6) +#define RT5682S_PAD_DRV_GP6_MASK (0x1 << 4) +#define RT5682S_PAD_DRV_GP6_HIGH (0x1 << 4) +#define RT5682S_PAD_DRV_GP6_LOW (0x0 << 4) + +/* Chopper and Clock control for DAC (0x013a)*/ +#define RT5682S_CKXEN_DAC1_MASK (0x1 << 13) +#define RT5682S_CKXEN_DAC1_SFT 13 +#define RT5682S_CKGEN_DAC1_MASK (0x1 << 12) +#define RT5682S_CKGEN_DAC1_SFT 12 + +/* Chopper and Clock control for ADC (0x013b)*/ +#define RT5682S_CKXEN_ADC1_MASK (0x1 << 13) +#define RT5682S_CKXEN_ADC1_SFT 13 +#define RT5682S_CKGEN_ADC1_MASK (0x1 << 12) +#define RT5682S_CKGEN_ADC1_SFT 12 + +/* Volume test (0x013f)*/ +#define RT5682S_SEL_CLK_VOL_MASK (0x1 << 15) +#define RT5682S_SEL_CLK_VOL_EN (0x1 << 15) +#define RT5682S_SEL_CLK_VOL_DIS (0x0 << 15) + +/* Test Mode Control 1 (0x0145) */ +#define RT5682S_AD2DA_LB_MASK (0x1 << 10) +#define RT5682S_AD2DA_LB_SFT 10 + +/* Stereo Noise Gate Control 1 (0x0160) */ +#define RT5682S_NG2_EN_MASK (0x1 << 15) +#define RT5682S_NG2_EN (0x1 << 15) +#define RT5682S_NG2_DIS (0x0 << 15) + +/* Stereo1 DAC Silence Detection Control (0x0190) */ +#define RT5682S_DEB_STO_DAC_MASK (0x7 << 4) +#define RT5682S_DEB_80_MS (0x0 << 4) + +/* HP Behavior Logic Control 2 (0x01db) */ +#define RT5682S_HP_SIG_SRC_MASK (0x3) +#define RT5682S_HP_SIG_SRC_1BIT_CTL (0x3) +#define RT5682S_HP_SIG_SRC_REG (0x2) +#define RT5682S_HP_SIG_SRC_IMPE_REG (0x1) +#define RT5682S_HP_SIG_SRC_DC_CALI (0x0) + +/* SAR ADC Inline Command Control 1 (0x0210) */ +#define RT5682S_SAR_BUTDET_MASK (0x1 << 15) +#define RT5682S_SAR_BUTDET_EN (0x1 << 15) +#define RT5682S_SAR_BUTDET_DIS (0x0 << 15) +#define RT5682S_SAR_BUTDET_POW_MASK (0x1 << 14) +#define RT5682S_SAR_BUTDET_POW_SAV (0x1 << 14) +#define RT5682S_SAR_BUTDET_POW_NORM (0x0 << 14) +#define RT5682S_SAR_BUTDET_RST_MASK (0x1 << 13) +#define RT5682S_SAR_BUTDET_RST_NORM (0x1 << 13) +#define RT5682S_SAR_BUTDET_RST (0x0 << 13) +#define RT5682S_SAR_POW_MASK (0x1 << 12) +#define RT5682S_SAR_POW_EN (0x1 << 12) +#define RT5682S_SAR_POW_DIS (0x0 << 12) +#define RT5682S_SAR_RST_MASK (0x1 << 11) +#define RT5682S_SAR_RST_NORMAL (0x1 << 11) +#define RT5682S_SAR_RST (0x0 << 11) +#define RT5682S_SAR_BYPASS_MASK (0x1 << 10) +#define RT5682S_SAR_BYPASS_EN (0x1 << 10) +#define RT5682S_SAR_BYPASS_DIS (0x0 << 10) +#define RT5682S_SAR_SEL_MB1_2_MASK (0x3 << 8) +#define RT5682S_SAR_SEL_MB1_2_SFT 8 +#define RT5682S_SAR_SEL_MODE_MASK (0x1 << 7) +#define RT5682S_SAR_SEL_MODE_CMP (0x1 << 7) +#define RT5682S_SAR_SEL_MODE_ADC (0x0 << 7) +#define RT5682S_SAR_SEL_MB1_2_CTL_MASK (0x1 << 5) +#define RT5682S_SAR_SEL_MB1_2_AUTO (0x1 << 5) +#define RT5682S_SAR_SEL_MB1_2_MANU (0x0 << 5) +#define RT5682S_SAR_SEL_SIGNAL_MASK (0x1 << 4) +#define RT5682S_SAR_SEL_SIGNAL_AUTO (0x1 << 4) +#define RT5682S_SAR_SEL_SIGNAL_MANU (0x0 << 4) + +/* SAR ADC Inline Command Control 2 (0x0211) */ +#define RT5682S_SAR_ADC_PSV_MASK (0x1 << 4) +#define RT5682S_SAR_ADC_PSV_ENTRY (0x1 << 4) + + +/* SAR ADC Inline Command Control 13 (0x021c) */ +#define RT5682S_SAR_SOUR_MASK (0x3f) +#define RT5682S_SAR_SOUR_BTN (0x3f) +#define RT5682S_SAR_SOUR_TYPE (0x0) + +/* Headphone Amp Detection Control 1 (0x3b00) */ +#define RT5682S_CP_SW_SIZE_MASK (0x7 << 4) +#define RT5682S_CP_SW_SIZE_L (0x4 << 4) +#define RT5682S_CP_SW_SIZE_M (0x2 << 4) +#define RT5682S_CP_SW_SIZE_S (0x1 << 4) + +#define RT5682S_STEREO_RATES SNDRV_PCM_RATE_8000_192000 +#define RT5682S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +/* System Clock Source */ +enum { + RT5682S_SCLK_S_MCLK, + RT5682S_SCLK_S_PLL1, + RT5682S_SCLK_S_PLL2, + RT5682S_SCLK_S_RCCLK, +}; + +/* PLL Source */ +enum { + RT5682S_PLL_S_MCLK, + RT5682S_PLL_S_BCLK1, + RT5682S_PLL_S_BCLK2, + RT5682S_PLL_S_RCCLK, +}; + +enum { + RT5682S_PLL1, + RT5682S_PLL2, + RT5682S_PLLS, +}; + +enum { + RT5682S_AIF1, + RT5682S_AIF2, + RT5682S_AIFS +}; + +/* filter mask */ +enum { + RT5682S_DA_STEREO1_FILTER = 0x1, + RT5682S_AD_STEREO1_FILTER = (0x1 << 1), +}; + +enum { + RT5682S_CLK_SEL_SYS, + RT5682S_CLK_SEL_I2S1_ASRC, + RT5682S_CLK_SEL_I2S2_ASRC, +}; + +enum { + USE_PLLA, + USE_PLLB, + USE_PLLAB, +}; + +struct pll_calc_map { + unsigned int freq_in; + unsigned int freq_out; + int m; + int n; + int k; + bool m_bp; + bool k_bp; + bool byp_ps; + bool sel_ps; +}; + +enum { + RT5682S_SUPPLY_AVDD, + RT5682S_SUPPLY_MICVDD, + RT5682S_SUPPLY_DBVDD, + RT5682S_SUPPLY_LDO1_IN, + RT5682S_NUM_SUPPLIES, +}; + +struct rt5682s_priv { + struct snd_soc_component *component; + struct rt5682s_platform_data pdata; + struct gpio_desc *ldo1_en; + struct regmap *regmap; + struct snd_soc_jack *hs_jack; + struct regulator_bulk_data supplies[RT5682S_NUM_SUPPLIES]; + struct delayed_work jack_detect_work; + struct delayed_work jd_check_work; + struct mutex calibrate_mutex; + struct mutex sar_mutex; + struct mutex wclk_mutex; + +#ifdef CONFIG_COMMON_CLK + struct clk_hw dai_clks_hw[RT5682S_DAI_NUM_CLKS]; + struct clk *mclk; +#endif + + int sysclk; + int sysclk_src; + int lrck[RT5682S_AIFS]; + int bclk[RT5682S_AIFS]; + int master[RT5682S_AIFS]; + + int pll_src[RT5682S_PLLS]; + int pll_in[RT5682S_PLLS]; + int pll_out[RT5682S_PLLS]; + int pll_comb; + + int jack_type; + unsigned int irq; + int irq_work_delay_time; + int wclk_enabled; +}; + +int rt5682s_sel_asrc_clk_src(struct snd_soc_component *component, + unsigned int filter_mask, unsigned int clk_src); + +#endif /* __RT5682S_H__ */ diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c new file mode 100644 index 0000000000..52c33d56b1 --- /dev/null +++ b/sound/soc/codecs/rt700-sdw.c @@ -0,0 +1,573 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt700-sdw.c -- rt700 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt700.h" +#include "rt700-sdw.h" + +static bool rt700_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x2000 ... 0x200e: + case 0x2012 ... 0x2016: + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2231: + case 0x3000 ... 0x3fff: + case 0x7000 ... 0x7fff: + case 0x8300 ... 0x83ff: + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0x75201a: + case 0x752045: + case 0x752046: + case 0x752048: + case 0x75204a: + case 0x75206b: + case 0x752080: + case 0x752081: + return true; + default: + return false; + } +} + +static bool rt700_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2009: + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x200b ... 0x200e: /* i2c read */ + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0xff01: + case 0x75201a: + case 0x752046: + case 0x752080: + case 0x752081: + return true; + default: + return false; + } +} + +static int rt700_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; + unsigned int is_hda_reg = 1, is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt700->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT700_PRIV_DATA_R_H | nid; + ret = regmap_write(rt700->sdw_regmap, + reg3, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg4, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x3000) { + reg += 0x8000; + ret = regmap_write(rt700->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + reg += 0x2000; + reg |= 0x800; + ret = regmap_write(rt700->sdw_regmap, + reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt700->sdw_regmap, + reg2, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt700->sdw_regmap, reg, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x9000) { + ret = regmap_write(rt700->sdw_regmap, + reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0xb000) { + ret = regmap_write(rt700->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else { + ret = regmap_read(rt700->sdw_regmap, reg, val); + if (ret < 0) + return ret; + is_hda_reg = 0; + } + + if (is_hda_reg || is_index_reg) { + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_3, &sdw_data_3); + if (ret < 0) + return ret; + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_2, &sdw_data_2); + if (ret < 0) + return ret; + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_1, &sdw_data_1); + if (ret < 0) + return ret; + ret = regmap_read(rt700->sdw_regmap, + RT700_READ_HDA_0, &sdw_data_0); + if (ret < 0) + return ret; + *val = ((sdw_data_3 & 0xff) << 24) | + ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + } + + if (is_hda_reg == 0) + dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", + __func__, reg, reg2, reg3, reg4, *val); + else + dev_dbg(dev, "[%s] %04x %04x => %08x\n", + __func__, reg, reg2, *val); + + return 0; +} + +static int rt700_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; + unsigned int is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt700->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT700_PRIV_DATA_W_H | nid; + ret = regmap_write(rt700->sdw_regmap, + reg3, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg4, (val & 0xff)); + if (ret < 0) + return ret; + is_index_reg = 1; + } else if (reg < 0x4fff) { + ret = regmap_write(rt700->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (reg == 0xff01) { + ret = regmap_write(rt700->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + ret = regmap_write(rt700->sdw_regmap, + reg, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt700->sdw_regmap, reg2, (val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt700->sdw_regmap, + reg2, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt700->sdw_regmap, reg, (val & 0xff)); + if (ret < 0) + return ret; + } + + if (reg2 == 0) + dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", + __func__, reg, reg2, reg3, reg4, val2, val); + else + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", + __func__, reg, reg2, val); + + return 0; +} + +static const struct regmap_config rt700_regmap = { + .reg_bits = 24, + .val_bits = 32, + .readable_reg = rt700_readable_register, + .volatile_reg = rt700_volatile_register, + .max_register = 0x755800, + .reg_defaults = rt700_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, + .reg_read = rt700_sdw_read, + .reg_write = rt700_sdw_write, +}; + +static const struct regmap_config rt700_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt700_readable_register, + .max_register = 0xff01, + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt700_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt700->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt700->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt700_io_init(&slave->dev, slave); +} + +static int rt700_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH | + SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x14; /* BITMAP: 00010100 */ + prop->sink_ports = 0xA; /* BITMAP: 00001010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt700_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt700->params, params, sizeof(*params)); + + ret = rt700_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt700_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + mutex_lock(&rt700->disable_irq_lock); + if (status->control_port & 0x4 && !rt700->disable_irq) { + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_detect_work, msecs_to_jiffies(250)); + } + mutex_unlock(&rt700->disable_irq_lock); + + return 0; +} + +/* + * slave_ops: callbacks for get_clock_stop_mode, clock_stop and + * port_prep are not defined for now + */ +static const struct sdw_slave_ops rt700_slave_ops = { + .read_prop = rt700_read_prop, + .interrupt_callback = rt700_interrupt_callback, + .update_status = rt700_update_status, + .bus_config = rt700_bus_config, +}; + +static int rt700_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *sdw_regmap, *regmap; + + /* Regmap Initialization */ + sdw_regmap = devm_regmap_init_sdw(slave, &rt700_sdw_regmap); + if (IS_ERR(sdw_regmap)) + return PTR_ERR(sdw_regmap); + + regmap = devm_regmap_init(&slave->dev, NULL, + &slave->dev, &rt700_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt700_init(&slave->dev, sdw_regmap, regmap, slave); +} + +static int rt700_sdw_remove(struct sdw_slave *slave) +{ + struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); + + if (rt700->hw_init) { + cancel_delayed_work_sync(&rt700->jack_detect_work); + cancel_delayed_work_sync(&rt700->jack_btn_check_work); + } + + pm_runtime_disable(&slave->dev); + + return 0; +} + +static const struct sdw_device_id rt700_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x700, 0x1, 0, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt700_id); + +static int __maybe_unused rt700_dev_suspend(struct device *dev) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + + if (!rt700->hw_init) + return 0; + + cancel_delayed_work_sync(&rt700->jack_detect_work); + cancel_delayed_work_sync(&rt700->jack_btn_check_work); + + regcache_cache_only(rt700->regmap, true); + + return 0; +} + +static int __maybe_unused rt700_dev_system_suspend(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt700_priv *rt700 = dev_get_drvdata(dev); + int ret; + + if (!rt700->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt700->disable_irq_lock); + rt700->disable_irq = true; + ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, + SDW_SCP_INT1_IMPL_DEF, 0); + mutex_unlock(&rt700->disable_irq_lock); + + if (ret < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:", __func__); + } + + return rt700_dev_suspend(dev); +} + +#define RT700_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt700_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt700->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT700_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt700->regmap, false); + regcache_sync_region(rt700->regmap, 0x3000, 0x8fff); + regcache_sync_region(rt700->regmap, 0x752010, 0x75206b); + + return 0; +} + +static const struct dev_pm_ops rt700_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt700_dev_system_suspend, rt700_dev_resume) + SET_RUNTIME_PM_OPS(rt700_dev_suspend, rt700_dev_resume, NULL) +}; + +static struct sdw_driver rt700_sdw_driver = { + .driver = { + .name = "rt700", + .owner = THIS_MODULE, + .pm = &rt700_pm, + }, + .probe = rt700_sdw_probe, + .remove = rt700_sdw_remove, + .ops = &rt700_slave_ops, + .id_table = rt700_id, +}; +module_sdw_driver(rt700_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT700 driver SDW"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt700-sdw.h b/sound/soc/codecs/rt700-sdw.h new file mode 100644 index 0000000000..4ad0dcfd16 --- /dev/null +++ b/sound/soc/codecs/rt700-sdw.h @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt700-sdw.h -- RT700 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT700_SDW_H__ +#define __RT700_SDW_H__ + +static const struct reg_default rt700_reg_defaults[] = { + { 0x0000, 0x0000 }, + { 0x0001, 0x0000 }, + { 0x0002, 0x0000 }, + { 0x0003, 0x0000 }, + { 0x0004, 0x0000 }, + { 0x0005, 0x0001 }, + { 0x0020, 0x0000 }, + { 0x0022, 0x0000 }, + { 0x0023, 0x0000 }, + { 0x0024, 0x0000 }, + { 0x0025, 0x0000 }, + { 0x0026, 0x0000 }, + { 0x0030, 0x0000 }, + { 0x0032, 0x0000 }, + { 0x0033, 0x0000 }, + { 0x0034, 0x0000 }, + { 0x0035, 0x0000 }, + { 0x0036, 0x0000 }, + { 0x0040, 0x0000 }, + { 0x0041, 0x0000 }, + { 0x0042, 0x0000 }, + { 0x0043, 0x0000 }, + { 0x0044, 0x0020 }, + { 0x0045, 0x0001 }, + { 0x0046, 0x0000 }, + { 0x0050, 0x0000 }, + { 0x0051, 0x0000 }, + { 0x0052, 0x0000 }, + { 0x0053, 0x0000 }, + { 0x0054, 0x0000 }, + { 0x0055, 0x0000 }, + { 0x0060, 0x0000 }, + { 0x0070, 0x0000 }, + { 0x00e0, 0x0000 }, + { 0x00f0, 0x0000 }, + { 0x0100, 0x0000 }, + { 0x0101, 0x0000 }, + { 0x0102, 0x0000 }, + { 0x0103, 0x0000 }, + { 0x0104, 0x0000 }, + { 0x0105, 0x0000 }, + { 0x0120, 0x0000 }, + { 0x0121, 0x0000 }, + { 0x0122, 0x0000 }, + { 0x0123, 0x0000 }, + { 0x0124, 0x0000 }, + { 0x0125, 0x0000 }, + { 0x0126, 0x0000 }, + { 0x0127, 0x0000 }, + { 0x0130, 0x0000 }, + { 0x0131, 0x0000 }, + { 0x0132, 0x0000 }, + { 0x0133, 0x0000 }, + { 0x0134, 0x0000 }, + { 0x0135, 0x0000 }, + { 0x0136, 0x0000 }, + { 0x0137, 0x0000 }, + { 0x0200, 0x0000 }, + { 0x0201, 0x0000 }, + { 0x0202, 0x0000 }, + { 0x0203, 0x0000 }, + { 0x0204, 0x0000 }, + { 0x0205, 0x0000 }, + { 0x0220, 0x0000 }, + { 0x0221, 0x0000 }, + { 0x0222, 0x0000 }, + { 0x0223, 0x0000 }, + { 0x0224, 0x0000 }, + { 0x0225, 0x0000 }, + { 0x0226, 0x0000 }, + { 0x0227, 0x0000 }, + { 0x0230, 0x0000 }, + { 0x0231, 0x0000 }, + { 0x0232, 0x0000 }, + { 0x0233, 0x0000 }, + { 0x0234, 0x0000 }, + { 0x0235, 0x0000 }, + { 0x0236, 0x0000 }, + { 0x0237, 0x0000 }, + { 0x0300, 0x0000 }, + { 0x0301, 0x0000 }, + { 0x0302, 0x0000 }, + { 0x0303, 0x0000 }, + { 0x0304, 0x0000 }, + { 0x0305, 0x0000 }, + { 0x0320, 0x0000 }, + { 0x0321, 0x0000 }, + { 0x0322, 0x0000 }, + { 0x0323, 0x0000 }, + { 0x0324, 0x0000 }, + { 0x0325, 0x0000 }, + { 0x0326, 0x0000 }, + { 0x0327, 0x0000 }, + { 0x0330, 0x0000 }, + { 0x0331, 0x0000 }, + { 0x0332, 0x0000 }, + { 0x0333, 0x0000 }, + { 0x0334, 0x0000 }, + { 0x0335, 0x0000 }, + { 0x0336, 0x0000 }, + { 0x0337, 0x0000 }, + { 0x0400, 0x0000 }, + { 0x0401, 0x0000 }, + { 0x0402, 0x0000 }, + { 0x0403, 0x0000 }, + { 0x0404, 0x0000 }, + { 0x0405, 0x0000 }, + { 0x0420, 0x0000 }, + { 0x0421, 0x0000 }, + { 0x0422, 0x0000 }, + { 0x0423, 0x0000 }, + { 0x0424, 0x0000 }, + { 0x0425, 0x0000 }, + { 0x0426, 0x0000 }, + { 0x0427, 0x0000 }, + { 0x0430, 0x0000 }, + { 0x0431, 0x0000 }, + { 0x0432, 0x0000 }, + { 0x0433, 0x0000 }, + { 0x0434, 0x0000 }, + { 0x0435, 0x0000 }, + { 0x0436, 0x0000 }, + { 0x0437, 0x0000 }, + { 0x0500, 0x0000 }, + { 0x0501, 0x0000 }, + { 0x0502, 0x0000 }, + { 0x0503, 0x0000 }, + { 0x0504, 0x0000 }, + { 0x0505, 0x0000 }, + { 0x0520, 0x0000 }, + { 0x0521, 0x0000 }, + { 0x0522, 0x0000 }, + { 0x0523, 0x0000 }, + { 0x0524, 0x0000 }, + { 0x0525, 0x0000 }, + { 0x0526, 0x0000 }, + { 0x0527, 0x0000 }, + { 0x0530, 0x0000 }, + { 0x0531, 0x0000 }, + { 0x0532, 0x0000 }, + { 0x0533, 0x0000 }, + { 0x0534, 0x0000 }, + { 0x0535, 0x0000 }, + { 0x0536, 0x0000 }, + { 0x0537, 0x0000 }, + { 0x0600, 0x0000 }, + { 0x0601, 0x0000 }, + { 0x0602, 0x0000 }, + { 0x0603, 0x0000 }, + { 0x0604, 0x0000 }, + { 0x0605, 0x0000 }, + { 0x0620, 0x0000 }, + { 0x0621, 0x0000 }, + { 0x0622, 0x0000 }, + { 0x0623, 0x0000 }, + { 0x0624, 0x0000 }, + { 0x0625, 0x0000 }, + { 0x0626, 0x0000 }, + { 0x0627, 0x0000 }, + { 0x0630, 0x0000 }, + { 0x0631, 0x0000 }, + { 0x0632, 0x0000 }, + { 0x0633, 0x0000 }, + { 0x0634, 0x0000 }, + { 0x0635, 0x0000 }, + { 0x0636, 0x0000 }, + { 0x0637, 0x0000 }, + { 0x0700, 0x0000 }, + { 0x0701, 0x0000 }, + { 0x0702, 0x0000 }, + { 0x0703, 0x0000 }, + { 0x0704, 0x0000 }, + { 0x0705, 0x0000 }, + { 0x0720, 0x0000 }, + { 0x0721, 0x0000 }, + { 0x0722, 0x0000 }, + { 0x0723, 0x0000 }, + { 0x0724, 0x0000 }, + { 0x0725, 0x0000 }, + { 0x0726, 0x0000 }, + { 0x0727, 0x0000 }, + { 0x0730, 0x0000 }, + { 0x0731, 0x0000 }, + { 0x0732, 0x0000 }, + { 0x0733, 0x0000 }, + { 0x0734, 0x0000 }, + { 0x0735, 0x0000 }, + { 0x0736, 0x0000 }, + { 0x0737, 0x0000 }, + { 0x0800, 0x0000 }, + { 0x0801, 0x0000 }, + { 0x0802, 0x0000 }, + { 0x0803, 0x0000 }, + { 0x0804, 0x0000 }, + { 0x0805, 0x0000 }, + { 0x0820, 0x0000 }, + { 0x0821, 0x0000 }, + { 0x0822, 0x0000 }, + { 0x0823, 0x0000 }, + { 0x0824, 0x0000 }, + { 0x0825, 0x0000 }, + { 0x0826, 0x0000 }, + { 0x0827, 0x0000 }, + { 0x0830, 0x0000 }, + { 0x0831, 0x0000 }, + { 0x0832, 0x0000 }, + { 0x0833, 0x0000 }, + { 0x0834, 0x0000 }, + { 0x0835, 0x0000 }, + { 0x0836, 0x0000 }, + { 0x0837, 0x0000 }, + { 0x0f00, 0x0000 }, + { 0x0f01, 0x0000 }, + { 0x0f02, 0x0000 }, + { 0x0f03, 0x0000 }, + { 0x0f04, 0x0000 }, + { 0x0f05, 0x0000 }, + { 0x0f20, 0x0000 }, + { 0x0f21, 0x0000 }, + { 0x0f22, 0x0000 }, + { 0x0f23, 0x0000 }, + { 0x0f24, 0x0000 }, + { 0x0f25, 0x0000 }, + { 0x0f26, 0x0000 }, + { 0x0f27, 0x0000 }, + { 0x0f30, 0x0000 }, + { 0x0f31, 0x0000 }, + { 0x0f32, 0x0000 }, + { 0x0f33, 0x0000 }, + { 0x0f34, 0x0000 }, + { 0x0f35, 0x0000 }, + { 0x0f36, 0x0000 }, + { 0x0f37, 0x0000 }, + { 0x2000, 0x0000 }, + { 0x2001, 0x0000 }, + { 0x2002, 0x0000 }, + { 0x2003, 0x0000 }, + { 0x2004, 0x0000 }, + { 0x2005, 0x0000 }, + { 0x2006, 0x0000 }, + { 0x2007, 0x0000 }, + { 0x2008, 0x0000 }, + { 0x2009, 0x0003 }, + { 0x200a, 0x0003 }, + { 0x200b, 0x0000 }, + { 0x200c, 0x0000 }, + { 0x200d, 0x0000 }, + { 0x200e, 0x0000 }, + { 0x2012, 0x0000 }, + { 0x2013, 0x0000 }, + { 0x2014, 0x0000 }, + { 0x2015, 0x0000 }, + { 0x2016, 0x0000 }, + { 0x201a, 0x0000 }, + { 0x201b, 0x0000 }, + { 0x201c, 0x0000 }, + { 0x201d, 0x0000 }, + { 0x201e, 0x0000 }, + { 0x201f, 0x0000 }, + { 0x2020, 0x0000 }, + { 0x2021, 0x0000 }, + { 0x2022, 0x0000 }, + { 0x2023, 0x0000 }, + { 0x2024, 0x0000 }, + { 0x2025, 0x0002 }, + { 0x2026, 0x0000 }, + { 0x2027, 0x0000 }, + { 0x2029, 0x0000 }, + { 0x202a, 0x0000 }, + { 0x202d, 0x0000 }, + { 0x202e, 0x0000 }, + { 0x202f, 0x0000 }, + { 0x2030, 0x0000 }, + { 0x2031, 0x0000 }, + { 0x2032, 0x0000 }, + { 0x2033, 0x0000 }, + { 0x2034, 0x0000 }, + { 0x2200, 0x0000 }, + { 0x2201, 0x0000 }, + { 0x2202, 0x0000 }, + { 0x2203, 0x0000 }, + { 0x2204, 0x0000 }, + { 0x2206, 0x0000 }, + { 0x2207, 0x0000 }, + { 0x2208, 0x0000 }, + { 0x2209, 0x0000 }, + { 0x220a, 0x0000 }, + { 0x220b, 0x0000 }, + { 0x220c, 0x0000 }, + { 0x220d, 0x0000 }, + { 0x220e, 0x0000 }, + { 0x220f, 0x0000 }, + { 0x2211, 0x0000 }, + { 0x2212, 0x0000 }, + { 0x2220, 0x0000 }, + { 0x2221, 0x0000 }, + { 0x2222, 0x0000 }, + { 0x2223, 0x0000 }, + { 0x2230, 0x0000 }, + { 0x2231, 0x0000 }, + { 0x3121, 0x0001 }, + { 0x3122, 0x0000 }, + { 0x3123, 0x0000 }, + { 0x7303, 0x0057 }, + { 0x7303, 0x0057 }, + { 0x8383, 0x0057 }, + { 0x7308, 0x0097 }, + { 0x8388, 0x0097 }, + { 0x7309, 0x0097 }, + { 0x8389, 0x0097 }, + { 0x7312, 0x0000 }, + { 0x8392, 0x0000 }, + { 0x7313, 0x0000 }, + { 0x8393, 0x0000 }, + { 0x7319, 0x0000 }, + { 0x8399, 0x0000 }, + { 0x75201a, 0x8003 }, + { 0x752045, 0x5289 }, + { 0x752048, 0xd049 }, + { 0x75204a, 0xa83b }, + { 0x75206b, 0x5064 }, +}; + +#endif /* __RT700_H__ */ diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c new file mode 100644 index 0000000000..0ebf344a1b --- /dev/null +++ b/sound/soc/codecs/rt700.c @@ -0,0 +1,1243 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt700.c -- rt700 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt700.h" + +static int rt700_index_write(struct regmap *regmap, + unsigned int reg, unsigned int value) +{ + int ret; + unsigned int addr = (RT700_PRIV_INDEX_W_H << 8) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + pr_err("Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt700_index_read(struct regmap *regmap, + unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int addr = (RT700_PRIV_INDEX_W_H << 8) | reg; + + *value = 0; + ret = regmap_read(regmap, addr, value); + if (ret < 0) + pr_err("Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static unsigned int rt700_button_detect(struct rt700_priv *rt700) +{ + unsigned int btn_type = 0, val80, val81; + int ret; + + ret = rt700_index_read(rt700->regmap, RT700_IRQ_FLAG_TABLE1, &val80); + if (ret < 0) + goto read_error; + ret = rt700_index_read(rt700->regmap, RT700_IRQ_FLAG_TABLE2, &val81); + if (ret < 0) + goto read_error; + + val80 &= 0x0381; + val81 &= 0xff00; + + switch (val80) { + case 0x0200: + case 0x0100: + case 0x0080: + btn_type |= SND_JACK_BTN_0; + break; + case 0x0001: + btn_type |= SND_JACK_BTN_3; + break; + } + switch (val81) { + case 0x8000: + case 0x4000: + case 0x2000: + btn_type |= SND_JACK_BTN_1; + break; + case 0x1000: + case 0x0800: + case 0x0400: + btn_type |= SND_JACK_BTN_2; + break; + case 0x0200: + case 0x0100: + btn_type |= SND_JACK_BTN_3; + break; + } +read_error: + return btn_type; +} + +static int rt700_headset_detect(struct rt700_priv *rt700) +{ + unsigned int buf, loop = 0; + int ret; + unsigned int jack_status = 0, reg; + + ret = rt700_index_read(rt700->regmap, + RT700_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + + while (loop < 500 && + (buf & RT700_COMBOJACK_AUTO_DET_STATUS) == 0) { + loop++; + + usleep_range(9000, 10000); + ret = rt700_index_read(rt700->regmap, + RT700_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + + reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; + ret = regmap_read(rt700->regmap, reg, &jack_status); + if ((jack_status & (1 << 31)) == 0) + goto remove_error; + } + + if (loop >= 500) + goto to_error; + + if (buf & RT700_COMBOJACK_AUTO_DET_TRS) + rt700->jack_type = SND_JACK_HEADPHONE; + else if ((buf & RT700_COMBOJACK_AUTO_DET_CTIA) || + (buf & RT700_COMBOJACK_AUTO_DET_OMTP)) + rt700->jack_type = SND_JACK_HEADSET; + + return 0; + +to_error: + ret = -ETIMEDOUT; + pr_err_ratelimited("Time-out error in %s\n", __func__); + return ret; +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +remove_error: + pr_err_ratelimited("Jack removal in %s\n", __func__); + return -ENODEV; +} + +static void rt700_jack_detect_handler(struct work_struct *work) +{ + struct rt700_priv *rt700 = + container_of(work, struct rt700_priv, jack_detect_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + if (!rt700->hs_jack) + return; + + if (!snd_soc_card_is_instantiated(rt700->component->card)) + return; + + reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; + ret = regmap_read(rt700->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + /* jack in */ + if (rt700->jack_type == 0) { + ret = rt700_headset_detect(rt700); + if (ret < 0) + return; + if (rt700->jack_type == SND_JACK_HEADSET) + btn_type = rt700_button_detect(rt700); + } else if (rt700->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt700_button_detect(rt700); + } + } else { + /* jack out */ + rt700->jack_type = 0; + } + + dev_dbg(&rt700->slave->dev, + "in %s, jack_type=0x%x\n", __func__, rt700->jack_type); + dev_dbg(&rt700->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt700_btn_check_handler(struct work_struct *work) +{ + struct rt700_priv *rt700 = container_of(work, struct rt700_priv, + jack_btn_check_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; + ret = regmap_read(rt700->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + if (rt700->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt700_button_detect(rt700); + } + } else { + rt700->jack_type = 0; + } + + /* cbj comparator */ + ret = rt700_index_read(rt700->regmap, RT700_COMBO_JACK_AUTO_CTL2, ®); + if (ret < 0) + goto io_error; + + if ((reg & 0xf0) == 0xf0) + btn_type = 0; + + dev_dbg(&rt700->slave->dev, + "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt700->hs_jack, rt700->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt700_jack_init(struct rt700_priv *rt700) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(rt700->component); + + /* power on */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + if (rt700->hs_jack) { + /* Enable Jack Detection */ + regmap_write(rt700->regmap, + RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt700->regmap, + RT700_SET_HP_UNSOLICITED_ENABLE, 0x81); + regmap_write(rt700->regmap, + RT700_SET_INLINE_UNSOLICITED_ENABLE, 0x83); + rt700_index_write(rt700->regmap, 0x10, 0x2420); + rt700_index_write(rt700->regmap, 0x19, 0x2e11); + + dev_dbg(&rt700->slave->dev, "in %s enable\n", __func__); + + mod_delayed_work(system_power_efficient_wq, + &rt700->jack_detect_work, msecs_to_jiffies(250)); + } else { + regmap_write(rt700->regmap, + RT700_SET_MIC2_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt700->regmap, + RT700_SET_HP_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt700->regmap, + RT700_SET_INLINE_UNSOLICITED_ENABLE, 0x00); + + dev_dbg(&rt700->slave->dev, "in %s disable\n", __func__); + } + + /* power off */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); +} + +static int rt700_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + int ret; + + rt700->hs_jack = hs_jack; + + /* we can only resume if the device was initialized at least once */ + if (!rt700->first_hw_init) + return 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + rt700_jack_init(rt700); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +static void rt700_get_gain(struct rt700_priv *rt700, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + /* R Channel */ + *r_val = (val_h << 8); + regmap_read(rt700->regmap, addr_l, r_val); + + /* L Channel */ + val_h |= 0x20; + *l_val = (val_h << 8); + regmap_read(rt700->regmap, addr_h, l_val); +} + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt700_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; + unsigned int read_ll, read_rl; + int i; + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); + + /* L Channel */ + if (mc->invert) { + /* for mute */ + val_ll = (mc->max - ucontrol->value.integer.value[0]) << 7; + /* keep gain */ + read_ll = read_ll & 0x7f; + val_ll |= read_ll; + } else { + /* for gain */ + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; + /* keep mute status */ + read_ll = read_ll & 0x80; + val_ll |= read_ll; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* R Channel */ + if (mc->invert) { + /* for mute */ + val_lr = (mc->max - ucontrol->value.integer.value[1]) << 7; + /* keep gain */ + read_rl = read_rl & 0x7f; + val_lr |= read_rl; + } else { + /* for gain */ + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; + /* keep mute status */ + read_rl = read_rl & 0x80; + val_lr |= read_rl; + } + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt700->regmap, + addr_h, (val_h << 8 | val_ll)); + regmap_write(rt700->regmap, + addr_l, (val_h << 8 | val_ll)); + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt700->regmap, + addr_h, (val_h << 8 | val_ll)); + + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt700->regmap, + addr_l, (val_h << 8 | val_lr)); + } + /* check result */ + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + return 0; +} + +static int rt700_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT700_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt700_get_gain(rt700, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (mc->invert) { + /* for mute status */ + read_ll = !((read_ll & 0x80) >> RT700_MUTE_SFT); + read_rl = !((read_rl & 0x80) >> RT700_MUTE_SFT); + } else { + /* for gain */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt700_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Front Playback Volume", + RT700_SET_GAIN_DAC1_H, RT700_SET_GAIN_DAC1_L, + RT700_DIR_OUT_SFT, 0x57, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", + RT700_SET_GAIN_ADC2_H, RT700_SET_GAIN_ADC2_L, + RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", + RT700_SET_GAIN_ADC1_H, RT700_SET_GAIN_ADC1_L, + RT700_DIR_IN_SFT, 1, 1, + rt700_set_amp_gain_get, rt700_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", + RT700_SET_GAIN_ADC2_H, RT700_SET_GAIN_ADC2_L, + RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", + RT700_SET_GAIN_ADC1_H, RT700_SET_GAIN_ADC1_L, + RT700_DIR_IN_SFT, 0x3f, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC Volume", + RT700_SET_GAIN_AMIC_H, RT700_SET_GAIN_AMIC_L, + RT700_DIR_IN_SFT, 3, 0, + rt700_set_amp_gain_get, rt700_set_amp_gain_put, mic_vol_tlv), +}; + +static int rt700_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int reg, val = 0, nid; + int ret; + + if (strstr(ucontrol->id.name, "HPO Mux")) + nid = RT700_HP_OUT; + else if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT700_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT700_MIXER_IN2; + else + return -EINVAL; + + /* vid = 0xf01 */ + reg = RT700_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt700->regmap, reg, &val); + if (ret < 0) + return ret; + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt700_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, reg, nid; + int ret; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "HPO Mux")) + nid = RT700_HP_OUT; + else if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT700_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT700_MIXER_IN2; + else + return -EINVAL; + + /* Verb ID = 0x701h */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + reg = RT700_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt700->regmap, reg, &val2); + if (ret < 0) + return ret; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) { + reg = RT700_VERB_SET_CONNECT_SEL | nid; + regmap_write(rt700->regmap, reg, val); + } + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", + "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt700_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt700_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt700_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt700_adc22_enum, + rt700_mux_get, rt700_mux_put); + +static const struct snd_kcontrol_new rt700_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt700_adc23_enum, + rt700_mux_get, rt700_mux_put); + +static const char * const out_mux_text[] = { + "Front", + "Surround", +}; + +static SOC_ENUM_SINGLE_DECL( + rt700_hp_enum, SND_SOC_NOPM, 0, out_mux_text); + +static const struct snd_kcontrol_new rt700_hp_mux = + SOC_DAPM_ENUM_EXT("HP Mux", rt700_hp_enum, + rt700_mux_get, rt700_mux_put); + +static int rt700_dac_front_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC1, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC1, 0x00); + break; + } + return 0; +} + +static int rt700_dac_surround_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC2, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_DAC2, 0x00); + break; + } + return 0; +} + +static int rt700_adc_09_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC1, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC1, 0x00); + break; + } + return 0; +} + +static int rt700_adc_08_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC2, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt700->regmap, + RT700_SET_STREAMID_ADC2, 0x00); + break; + } + return 0; +} + +static int rt700_hpo_mux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int val_h = (1 << RT700_DIR_OUT_SFT) | (0x3 << 4); + unsigned int val_l; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val_l = 0x00; + regmap_write(rt700->regmap, + RT700_SET_GAIN_HP_H, (val_h << 8 | val_l)); + break; + case SND_SOC_DAPM_PRE_PMD: + val_l = (1 << RT700_MUTE_SFT); + regmap_write(rt700->regmap, + RT700_SET_GAIN_HP_H, (val_h << 8 | val_l)); + usleep_range(50000, 55000); + break; + } + return 0; +} + +static int rt700_spk_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + unsigned int val_h = (1 << RT700_DIR_OUT_SFT) | (0x3 << 4); + unsigned int val_l; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val_l = 0x00; + regmap_write(rt700->regmap, + RT700_SET_GAIN_SPK_H, (val_h << 8 | val_l)); + break; + case SND_SOC_DAPM_PRE_PMD: + val_l = (1 << RT700_MUTE_SFT); + regmap_write(rt700->regmap, + RT700_SET_GAIN_SPK_H, (val_h << 8 | val_l)); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt700_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_DAC_E("DAC Front", NULL, SND_SOC_NOPM, 0, 0, + rt700_dac_front_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("DAC Surround", NULL, SND_SOC_NOPM, 0, 0, + rt700_dac_surround_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX_E("HPO Mux", SND_SOC_NOPM, 0, 0, &rt700_hp_mux, + rt700_hpo_mux_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("SPK PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + rt700_spk_pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 09", NULL, SND_SOC_NOPM, 0, 0, + rt700_adc_09_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 08", NULL, SND_SOC_NOPM, 0, 0, + rt700_adc_08_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt700_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt700_adc23_mux), + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt700_audio_map[] = { + {"DAC Front", NULL, "DP1RX"}, + {"DAC Surround", NULL, "DP3RX"}, + {"DP2TX", NULL, "ADC 09"}, + {"DP4TX", NULL, "ADC 08"}, + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 22 Mux", "DMIC", "DMIC1"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "DMIC", "DMIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + {"HPO Mux", "Front", "DAC Front"}, + {"HPO Mux", "Surround", "DAC Surround"}, + {"HP", NULL, "HPO Mux"}, + {"SPK PGA", NULL, "DAC Front"}, + {"SPK", NULL, "SPK PGA"}, +}; + +static int rt700_probe(struct snd_soc_component *component) +{ + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + int ret; + + rt700->component = component; + + if (!rt700->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static int rt700_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, + AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + regmap_write(rt700->regmap, + RT700_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + break; + + default: + break; + } + dapm->bias_level = level; + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_rt700 = { + .probe = rt700_probe, + .set_bias_level = rt700_set_bias_level, + .controls = rt700_snd_controls, + .num_controls = ARRAY_SIZE(rt700_snd_controls), + .dapm_widgets = rt700_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt700_dapm_widgets), + .dapm_routes = rt700_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt700_audio_map), + .set_jack = rt700_set_jack_detect, + .endianness = 1, +}; + +static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt700_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt700_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream; + int retval; + unsigned int val = 0; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt700->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + /* This code assumes port 1 for playback and port 2 for capture */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_config.num = 1; + else + port_config.num = 2; + + switch (dai->id) { + case RT700_AIF1: + break; + case RT700_AIF2: + port_config.num += 2; + break; + default: + dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + return -EINVAL; + } + + retval = sdw_stream_add_slave(rt700->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + /* 48Khz */ + regmap_write(rt700->regmap, RT700_DAC_FORMAT_H, val); + regmap_write(rt700->regmap, RT700_ADC_FORMAT_H, val); + + return retval; +} + +static int rt700_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt700->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt700->slave, sdw_stream); + return 0; +} + +#define RT700_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT700_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt700_ops = { + .hw_params = rt700_pcm_hw_params, + .hw_free = rt700_pcm_hw_free, + .set_stream = rt700_set_sdw_stream, + .shutdown = rt700_shutdown, +}; + +static struct snd_soc_dai_driver rt700_dai[] = { + { + .name = "rt700-aif1", + .id = RT700_AIF1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, + { + .name = "rt700-aif2", + .id = RT700_AIF2, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT700_STEREO_RATES, + .formats = RT700_FORMATS, + }, + .ops = &rt700_ops, + }, +}; + +/* Bus clock frequency */ +#define RT700_CLK_FREQ_9600000HZ 9600000 +#define RT700_CLK_FREQ_12000000HZ 12000000 +#define RT700_CLK_FREQ_6000000HZ 6000000 +#define RT700_CLK_FREQ_4800000HZ 4800000 +#define RT700_CLK_FREQ_2400000HZ 2400000 +#define RT700_CLK_FREQ_12288000HZ 12288000 + +int rt700_clock_config(struct device *dev) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt700->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT700_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT700_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT700_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT700_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT700_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT700_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt700->regmap, 0xe0, value); + regmap_write(rt700->regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +int rt700_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave) + +{ + struct rt700_priv *rt700; + int ret; + + rt700 = devm_kzalloc(dev, sizeof(*rt700), GFP_KERNEL); + if (!rt700) + return -ENOMEM; + + dev_set_drvdata(dev, rt700); + rt700->slave = slave; + rt700->sdw_regmap = sdw_regmap; + rt700->regmap = regmap; + + regcache_cache_only(rt700->regmap, true); + + mutex_init(&rt700->disable_irq_lock); + + INIT_DELAYED_WORK(&rt700->jack_detect_work, + rt700_jack_detect_handler); + INIT_DELAYED_WORK(&rt700->jack_btn_check_work, + rt700_btn_check_handler); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt700->hw_init = false; + rt700->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt700, + rt700_dai, + ARRAY_SIZE(rt700_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + dev_dbg(&slave->dev, "%s\n", __func__); + + return 0; +} + +int rt700_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt700_priv *rt700 = dev_get_drvdata(dev); + + rt700->disable_irq = false; + + if (rt700->hw_init) + return 0; + + regcache_cache_only(rt700->regmap, false); + if (rt700->first_hw_init) + regcache_cache_bypass(rt700->regmap, true); + + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + if (!rt700->first_hw_init) + /* PM runtime status is marked as 'active' only when a Slave reports as Attached */ + pm_runtime_set_active(&slave->dev); + + pm_runtime_get_noresume(&slave->dev); + + /* reset */ + regmap_write(rt700->regmap, 0xff01, 0x0000); + regmap_write(rt700->regmap, 0x7520, 0x001a); + regmap_write(rt700->regmap, 0x7420, 0xc003); + + /* power on */ + regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + /* Set Pin Widget */ + regmap_write(rt700->regmap, RT700_SET_PIN_HP, 0x40); + regmap_write(rt700->regmap, RT700_SET_PIN_SPK, 0x40); + regmap_write(rt700->regmap, RT700_SET_EAPD_SPK, RT700_EAPD_HIGH); + regmap_write(rt700->regmap, RT700_SET_PIN_DMIC1, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_DMIC2, 0x20); + regmap_write(rt700->regmap, RT700_SET_PIN_MIC2, 0x20); + + /* Set Configuration Default */ + regmap_write(rt700->regmap, 0x4f12, 0x91); + regmap_write(rt700->regmap, 0x4e12, 0xd6); + regmap_write(rt700->regmap, 0x4d12, 0x11); + regmap_write(rt700->regmap, 0x4c12, 0x20); + regmap_write(rt700->regmap, 0x4f13, 0x91); + regmap_write(rt700->regmap, 0x4e13, 0xd6); + regmap_write(rt700->regmap, 0x4d13, 0x11); + regmap_write(rt700->regmap, 0x4c13, 0x21); + + regmap_write(rt700->regmap, 0x4f19, 0x02); + regmap_write(rt700->regmap, 0x4e19, 0xa1); + regmap_write(rt700->regmap, 0x4d19, 0x90); + regmap_write(rt700->regmap, 0x4c19, 0x80); + + /* Enable Line2 */ + regmap_write(rt700->regmap, 0x371b, 0x40); + regmap_write(rt700->regmap, 0x731b, 0xb0); + regmap_write(rt700->regmap, 0x839b, 0x00); + + /* Set index */ + rt700_index_write(rt700->regmap, 0x4a, 0x201b); + rt700_index_write(rt700->regmap, 0x45, 0x5089); + rt700_index_write(rt700->regmap, 0x6b, 0x5064); + rt700_index_write(rt700->regmap, 0x48, 0xd249); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt700->regmap, RT700_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + /* + * if set_jack callback occurred early than io_init, + * we set up the jack detection function now + */ + if (rt700->hs_jack) + rt700_jack_init(rt700); + + if (rt700->first_hw_init) { + regcache_cache_bypass(rt700->regmap, false); + regcache_mark_dirty(rt700->regmap); + } else + rt700->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt700->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + + return 0; +} + +MODULE_DESCRIPTION("ASoC RT700 driver SDW"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h new file mode 100644 index 0000000000..491774d207 --- /dev/null +++ b/sound/soc/codecs/rt700.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt700.h -- RT700 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT700_H__ +#define __RT700_H__ + +extern const struct dev_pm_ops rt700_runtime_pm; + +struct rt700_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct regmap *sdw_regmap; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + int jack_type; + struct mutex disable_irq_lock; /* imp-def irq lock protection */ + bool disable_irq; +}; + +/* NID */ +#define RT700_AUDIO_FUNCTION_GROUP 0x01 +#define RT700_DAC_OUT1 0x02 +#define RT700_DAC_OUT2 0x03 +#define RT700_ADC_IN1 0x09 +#define RT700_ADC_IN2 0x08 +#define RT700_DMIC1 0x12 +#define RT700_DMIC2 0x13 +#define RT700_SPK_OUT 0x14 +#define RT700_MIC2 0x19 +#define RT700_LINE1 0x1a +#define RT700_LINE2 0x1b +#define RT700_BEEP 0x1d +#define RT700_SPDIF 0x1e +#define RT700_VENDOR_REGISTERS 0x20 +#define RT700_HP_OUT 0x21 +#define RT700_MIXER_IN1 0x22 +#define RT700_MIXER_IN2 0x23 +#define RT700_INLINE_CMD 0x55 + +/* Index (NID:20h) */ +#define RT700_DAC_DC_CALI_CTL1 0x00 +#define RT700_PARA_VERB_CTL 0x1a +#define RT700_COMBO_JACK_AUTO_CTL1 0x45 +#define RT700_COMBO_JACK_AUTO_CTL2 0x46 +#define RT700_INLINE_CMD_CTL 0x48 +#define RT700_DIGITAL_MISC_CTRL4 0x4a +#define RT700_VREFOUT_CTL 0x6b +#define RT700_FSM_CTL 0x6f +#define RT700_IRQ_FLAG_TABLE1 0x80 +#define RT700_IRQ_FLAG_TABLE2 0x81 +#define RT700_IRQ_FLAG_TABLE3 0x82 + +/* Verb */ +#define RT700_VERB_SET_CONNECT_SEL 0x3100 +#define RT700_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT700_VERB_GET_CONNECT_SEL 0xb100 +#define RT700_VERB_SET_POWER_STATE 0x3500 +#define RT700_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT700_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT700_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT700_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT700_SET_AMP_GAIN_MUTE_L 0x8380 +#define RT700_VERB_GET_PIN_SENSE 0xb900 + +#define RT700_READ_HDA_3 0x2012 +#define RT700_READ_HDA_2 0x2013 +#define RT700_READ_HDA_1 0x2014 +#define RT700_READ_HDA_0 0x2015 +#define RT700_PRIV_INDEX_W_H 0x7520 +#define RT700_PRIV_INDEX_W_L 0x85a0 +#define RT700_PRIV_DATA_W_H 0x7420 +#define RT700_PRIV_DATA_W_L 0x84a0 +#define RT700_PRIV_INDEX_R_H 0x9d20 +#define RT700_PRIV_INDEX_R_L 0xada0 +#define RT700_PRIV_DATA_R_H 0x9c20 +#define RT700_PRIV_DATA_R_L 0xaca0 +#define RT700_DAC_FORMAT_H 0x7203 +#define RT700_DAC_FORMAT_L 0x8283 +#define RT700_ADC_FORMAT_H 0x7209 +#define RT700_ADC_FORMAT_L 0x8289 +#define RT700_SET_AUDIO_POWER_STATE\ + (RT700_VERB_SET_POWER_STATE | RT700_AUDIO_FUNCTION_GROUP) +#define RT700_SET_PIN_DMIC1\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC1) +#define RT700_SET_PIN_DMIC2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_DMIC2) +#define RT700_SET_PIN_SPK\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_SPK_OUT) +#define RT700_SET_PIN_HP\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_HP_OUT) +#define RT700_SET_PIN_MIC2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_MIC2) +#define RT700_SET_PIN_LINE1\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE1) +#define RT700_SET_PIN_LINE2\ + (RT700_VERB_SET_PIN_WIDGET_CONTROL | RT700_LINE2) +#define RT700_SET_MIC2_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_MIC2) +#define RT700_SET_HP_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_HP_OUT) +#define RT700_SET_INLINE_UNSOLICITED_ENABLE\ + (RT700_VERB_SET_UNSOLICITED_ENABLE | RT700_INLINE_CMD) +#define RT700_SET_STREAMID_DAC1\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT1) +#define RT700_SET_STREAMID_DAC2\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_DAC_OUT2) +#define RT700_SET_STREAMID_ADC1\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN1) +#define RT700_SET_STREAMID_ADC2\ + (RT700_VERB_SET_CHANNEL_STREAMID | RT700_ADC_IN2) +#define RT700_SET_GAIN_DAC1_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_DAC_OUT1) +#define RT700_SET_GAIN_DAC1_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_DAC_OUT1) +#define RT700_SET_GAIN_ADC1_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN1) +#define RT700_SET_GAIN_ADC1_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN1) +#define RT700_SET_GAIN_ADC2_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_ADC_IN2) +#define RT700_SET_GAIN_ADC2_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_ADC_IN2) +#define RT700_SET_GAIN_AMIC_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_MIC2) +#define RT700_SET_GAIN_AMIC_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_MIC2) +#define RT700_SET_GAIN_HP_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_HP_OUT) +#define RT700_SET_GAIN_HP_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_HP_OUT) +#define RT700_SET_GAIN_SPK_L\ + (RT700_SET_AMP_GAIN_MUTE_L | RT700_SPK_OUT) +#define RT700_SET_GAIN_SPK_H\ + (RT700_SET_AMP_GAIN_MUTE_H | RT700_SPK_OUT) +#define RT700_SET_EAPD_SPK\ + (RT700_VERB_SET_EAPD_BTLENABLE | RT700_SPK_OUT) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT700_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT700_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT700_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT700_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +#define RT700_EAPD_HIGH 0x2 +#define RT700_EAPD_LOW 0x0 +#define RT700_MUTE_SFT 7 +#define RT700_DIR_IN_SFT 6 +#define RT700_DIR_OUT_SFT 7 + +enum { + RT700_AIF1, + RT700_AIF2, + RT700_AIFS, +}; + +int rt700_io_init(struct device *dev, struct sdw_slave *slave); +int rt700_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +int rt700_jack_detect(struct rt700_priv *rt700, bool *hp, bool *mic); +int rt700_clock_config(struct device *dev); +#endif /* __RT700_H__ */ diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c new file mode 100644 index 0000000000..935e597022 --- /dev/null +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt711-sdw-sdca.c -- rt711 SDCA ALSA SoC audio driver +// +// Copyright(c) 2021 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include + +#include "rt711-sdca.h" +#include "rt711-sdca-sdw.h" + +static bool rt711_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2239: + case 0x2f01 ... 0x2f0f: + case 0x2f30 ... 0x2f36: + case 0x2f50 ... 0x2f5a: + case 0x2f60: + case 0x3200 ... 0x3212: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_SELECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... + SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT711_BUF_ADDR_HID1 ... RT711_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt711_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x202d ... 0x202f: /* BRA */ + case 0x2200 ... 0x2212: /* i2c debug */ + case RT711_RC_CAL_STATUS: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... + SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT711_BUF_ADDR_HID1 ... RT711_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt711_sdca_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000 ... 0x20000ff: + case 0x5600000 ... 0x56000ff: + case 0x5700000 ... 0x57000ff: + case 0x5800000 ... 0x58000ff: + case 0x5900000 ... 0x59000ff: + case 0x5b00000 ... 0x5b000ff: + case 0x5f00000 ... 0x5f000ff: + case 0x6100000 ... 0x61000ff: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R): + return true; + default: + return false; + } +} + +static bool rt711_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + case 0x200001a: + case 0x2000046: + case 0x2000080: + case 0x2000081: + case 0x2000083: + case 0x5800000: + case 0x5800001: + case 0x5f00001: + case 0x6100008: + return true; + default: + return false; + } +} + +static const struct regmap_config rt711_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt711_sdca_readable_register, + .volatile_reg = rt711_sdca_volatile_register, + .max_register = 0x44ffffff, + .reg_defaults = rt711_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt711_sdca_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt711_sdca_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt711_sdca_mbq_readable_register, + .volatile_reg = rt711_sdca_mbq_volatile_register, + .max_register = 0x40800f12, + .reg_defaults = rt711_sdca_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt711_sdca_mbq_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt711_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt711->hw_init = false; + + if (status == SDW_SLAVE_ATTACHED) { + if (rt711->hs_jack) { + /* + * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then + * if the device attached again, we will need to set the setting back. + * It could avoid losing the jack detection interrupt. + * This also could sync with the cache value as the rt711_sdca_jack_init set. + */ + sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + } + } + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt711->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt711_sdca_io_init(&slave->dev, slave); +} + +static int rt711_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x14; /* BITMAP: 00010100 */ + prop->sink_ports = 0x8; /* BITMAP: 00001000 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 700; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt711_sdca_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev); + int ret, stat; + int count = 0, retry = 3; + unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0; + + dev_dbg(&slave->dev, + "%s control_port_stat=%x, sdca_cascade=%x", __func__, + status->control_port, status->sdca_cascade); + + if (cancel_delayed_work_sync(&rt711->jack_detect_work)) { + dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__); + /* avoid the HID owner doesn't change to device */ + if (rt711->scp_sdca_stat2) + scp_sdca_stat2 = rt711->scp_sdca_stat2; + } + + /* + * The critical section below intentionally protects a rather large piece of code. + * We don't want to allow the system suspend to disable an interrupt while we are + * processing it, which could be problematic given the quirky SoundWire interrupt + * scheme. We do want however to prevent new workqueues from being scheduled if + * the disable_irq flag was set during system suspend. + */ + mutex_lock(&rt711->disable_irq_lock); + + ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + rt711->scp_sdca_stat1 = ret; + ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + rt711->scp_sdca_stat2 = ret; + if (scp_sdca_stat2) + rt711->scp_sdca_stat2 |= scp_sdca_stat2; + + do { + /* clear flag */ + ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) { + ret = sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + if (ret < 0) + goto io_error; + } + ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) { + ret = sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INT2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + if (ret < 0) + goto io_error; + } + + /* check if flag clear or not */ + ret = sdw_read_no_pm(rt711->slave, SDW_DP0_INT); + if (ret < 0) + goto io_error; + sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; + + ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0; + + ret = sdw_read_no_pm(rt711->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8; + + stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade; + + count++; + } while (stat != 0 && count < retry); + + if (stat) + dev_warn(&slave->dev, + "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt711->scp_sdca_stat1, rt711->scp_sdca_stat2); + + if (status->sdca_cascade && !rt711->disable_irq) + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(30)); + + mutex_unlock(&rt711->disable_irq_lock); + + return 0; + +io_error: + mutex_unlock(&rt711->disable_irq_lock); + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static const struct sdw_slave_ops rt711_sdca_slave_ops = { + .read_prop = rt711_sdca_read_prop, + .interrupt_callback = rt711_sdca_interrupt_callback, + .update_status = rt711_sdca_update_status, +}; + +static int rt711_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt711_sdca_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt711_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt711_sdca_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt711_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev); + + if (rt711->hw_init) { + cancel_delayed_work_sync(&rt711->jack_detect_work); + cancel_delayed_work_sync(&rt711->jack_btn_check_work); + } + + pm_runtime_disable(&slave->dev); + + mutex_destroy(&rt711->calibrate_mutex); + mutex_destroy(&rt711->disable_irq_lock); + + return 0; +} + +static const struct sdw_device_id rt711_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x711, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt711_sdca_id); + +static int __maybe_unused rt711_sdca_dev_suspend(struct device *dev) +{ + struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev); + + if (!rt711->hw_init) + return 0; + + cancel_delayed_work_sync(&rt711->jack_detect_work); + cancel_delayed_work_sync(&rt711->jack_btn_check_work); + + regcache_cache_only(rt711->regmap, true); + regcache_cache_only(rt711->mbq_regmap, true); + + return 0; +} + +static int __maybe_unused rt711_sdca_dev_system_suspend(struct device *dev) +{ + struct rt711_sdca_priv *rt711_sdca = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt711_sdca->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt711_sdca->disable_irq_lock); + rt711_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + mutex_unlock(&rt711_sdca->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt711_sdca_dev_suspend(dev); +} + +#define RT711_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt711_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt711->first_hw_init) + return 0; + + if (!slave->unattach_request) { + if (rt711->disable_irq == true) { + mutex_lock(&rt711->disable_irq_lock); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); + rt711->disable_irq = false; + mutex_unlock(&rt711->disable_irq_lock); + } + goto regmap_sync; + } + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT711_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt711->regmap, false); + regcache_sync(rt711->regmap); + regcache_cache_only(rt711->mbq_regmap, false); + regcache_sync(rt711->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt711_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt711_sdca_dev_system_suspend, rt711_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt711_sdca_dev_suspend, rt711_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt711_sdca_sdw_driver = { + .driver = { + .name = "rt711-sdca", + .owner = THIS_MODULE, + .pm = &rt711_sdca_pm, + }, + .probe = rt711_sdca_sdw_probe, + .remove = rt711_sdca_sdw_remove, + .ops = &rt711_sdca_slave_ops, + .id_table = rt711_sdca_id, +}; +module_sdw_driver(rt711_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT711 SDCA SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt711-sdca-sdw.h b/sound/soc/codecs/rt711-sdca-sdw.h new file mode 100644 index 0000000000..0d774e473a --- /dev/null +++ b/sound/soc/codecs/rt711-sdca-sdw.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt711-sdw-sdca.h -- RT711 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2021 Realtek Semiconductor Corp. + */ + +#ifndef __RT711_SDW_SDCA_H__ +#define __RT711_SDW_SDCA_H__ + +#include +#include + +static const struct reg_default rt711_sdca_reg_defaults[] = { + { 0x201a, 0x00 }, + { 0x201e, 0x00 }, + { 0x201f, 0x00 }, + { 0x2020, 0x00 }, + { 0x2021, 0x00 }, + { 0x2022, 0x00 }, + { 0x2023, 0x00 }, + { 0x2024, 0x00 }, + { 0x2025, 0x01 }, + { 0x2026, 0x00 }, + { 0x2027, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x2f }, + { 0x2232, 0x80 }, + { 0x2233, 0x00 }, + { 0x2234, 0x00 }, + { 0x2235, 0x00 }, + { 0x2236, 0x00 }, + { 0x2237, 0x00 }, + { 0x2238, 0x00 }, + { 0x2239, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f0b, 0x00 }, + { 0x2f0c, 0x00 }, + { 0x2f0d, 0x00 }, + { 0x2f0e, 0x14 }, + { 0x2f0f, 0x00 }, + { 0x2f50, 0x03 }, + { 0x2f5a, 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28, RT711_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, +}; + +static const struct reg_default rt711_sdca_mbq_defaults[] = { + { 0x2000009, 0x1029 }, + { 0x2000011, 0x007a }, + { 0x200001a, 0x8003 }, + { 0x2000045, 0x5289 }, + { 0x2000048, 0x8049 }, + { 0x200004a, 0xa83b }, + { 0x200006b, 0x5064 }, + { 0x200006f, 0x058b }, + { 0x5800000, 0x0008 }, + { 0x5800001, 0x0000 }, + { 0x5f00001, 0x000a }, + { 0x6100000, 0x6100 }, + { 0x6100035, 0x0060 }, + { 0x6100036, 0x0029 }, + { 0x610003f, 0xff12 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 }, +}; + +#endif /* __RT711_SDW_SDCA_H__ */ diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c new file mode 100644 index 0000000000..447154cb60 --- /dev/null +++ b/sound/soc/codecs/rt711-sdca.c @@ -0,0 +1,1602 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt711-sdca.c -- rt711 SDCA ALSA SoC audio driver +// +// Copyright(c) 2021 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt711-sdca.h" + +static int rt711_sdca_index_write(struct rt711_sdca_priv *rt711, + unsigned int nid, unsigned int reg, unsigned int value) +{ + int ret; + struct regmap *regmap = rt711->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt711->slave->dev, + "Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt711_sdca_index_read(struct rt711_sdca_priv *rt711, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + struct regmap *regmap = rt711->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt711->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt711_sdca_index_update_bits(struct rt711_sdca_priv *rt711, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt711_sdca_index_read(rt711, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + return rt711_sdca_index_write(rt711, nid, reg, tmp); +} + +static void rt711_sdca_reset(struct rt711_sdca_priv *rt711) +{ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_PARA_VERB_CTL, RT711_HIDDEN_REG_SW_RESET, + RT711_HIDDEN_REG_SW_RESET); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, + RT711_HDA_LEGACY_RESET_CTL, 0x1, 0x1); +} + +static int rt711_sdca_calibration(struct rt711_sdca_priv *rt711) +{ + unsigned int val, loop_rc = 0, loop_dc = 0; + struct device *dev; + struct regmap *regmap = rt711->regmap; + int chk_cnt = 100; + int ret = 0; + + mutex_lock(&rt711->calibrate_mutex); + dev = regmap_get_device(regmap); + + regmap_read(rt711->regmap, RT711_RC_CAL_STATUS, &val); + /* RC calibration */ + if (!(val & 0x40)) + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_ANALOG_CTL, + RT711_MISC_POWER_CTL0, 0x0010, 0x0010); + + for (loop_rc = 0; loop_rc < chk_cnt && !(val & 0x40); loop_rc++) { + usleep_range(10000, 11000); + ret = regmap_read(rt711->regmap, RT711_RC_CAL_STATUS, &val); + if (ret < 0) + goto _cali_fail_; + } + if (loop_rc == chk_cnt) + dev_err(dev, "%s, RC calibration time-out!\n", __func__); + + /* HP calibration by manual mode setting */ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_FSM_CTL, 0x2000, 0x2000); + + /* Calibration manual mode */ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_FSM_CTL, 0xf, RT711_CALI_CTL); + + /* reset HP calibration */ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_FORCE_CALI_RST, 0x00); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_FORCE_CALI_RST, + RT711_DAC_DC_FORCE_CALI_RST); + + /* cal_clk_en_reg */ + if (rt711->hw_ver == RT711_VER_VD0) + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_CLK_EN, + RT711_DAC_DC_CALI_CLK_EN); + + /* trigger */ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, + RT711_DAC_DC_CALI_TRIGGER); + + /* wait for calibration process */ + rt711_sdca_index_read(rt711, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, &val); + + for (loop_dc = 0; loop_dc < chk_cnt && + (val & RT711_DAC_DC_CALI_TRIGGER); loop_dc++) { + usleep_range(10000, 11000); + ret = rt711_sdca_index_read(rt711, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, &val); + if (ret < 0) + goto _cali_fail_; + } + if (loop_dc == chk_cnt) + dev_err(dev, "%s, calibration time-out!\n", __func__); + + if (loop_dc == chk_cnt || loop_rc == chk_cnt) + ret = -ETIMEDOUT; + +_cali_fail_: + /* enable impedance sense */ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_FSM_CTL, RT711_FSM_IMP_EN, RT711_FSM_IMP_EN); + + /* release HP-JD and trigger FSM */ + rt711_sdca_index_write(rt711, RT711_VENDOR_REG, + RT711_DIGITAL_MISC_CTRL4, 0x201b); + + mutex_unlock(&rt711->calibrate_mutex); + dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); + return ret; +} + +static unsigned int rt711_sdca_button_detect(struct rt711_sdca_priv *rt711) +{ + unsigned int btn_type = 0, offset, idx, val, owner; + int ret; + unsigned char buf[3]; + + /* get current UMP message owner */ + ret = regmap_read(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), + &owner); + if (ret < 0) + return 0; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + /* read UMP message offset */ + ret = regmap_read(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), + &offset); + if (ret < 0) + goto _end_btn_det_; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt711->regmap, + RT711_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) { + switch (buf[1] & 0xf0) { + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + switch (buf[2]) { + case 0x01: + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x02: + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x04: + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x08: + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + } + +_end_btn_det_: + /* Host is owner, so set back to device */ + if (owner == 0) + /* set owner to device */ + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, + RT711_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01); + + return btn_type; +} + +static int rt711_sdca_headset_detect(struct rt711_sdca_priv *rt711) +{ + unsigned int det_mode; + int ret; + + /* get detected_mode */ + ret = regmap_read(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0), + &det_mode); + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x00: + rt711->jack_type = 0; + break; + case 0x03: + rt711->jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + rt711->jack_type = SND_JACK_HEADSET; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_SELECTED_MODE, 0), + det_mode); + if (ret < 0) + goto io_error; + } + + dev_dbg(&rt711->slave->dev, + "%s, detected_mode=0x%x\n", __func__, det_mode); + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void rt711_sdca_jack_detect_handler(struct work_struct *work) +{ + struct rt711_sdca_priv *rt711 = + container_of(work, struct rt711_sdca_priv, jack_detect_work.work); + int btn_type = 0, ret; + + if (!rt711->hs_jack) + return; + + if (!snd_soc_card_is_instantiated(rt711->component->card)) + return; + + /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */ + if (rt711->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) { + ret = rt711_sdca_headset_detect(rt711); + if (ret < 0) + return; + } + + /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */ + if (rt711->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8) + btn_type = rt711_sdca_button_detect(rt711); + + if (rt711->jack_type == 0) + btn_type = 0; + + dev_dbg(&rt711->slave->dev, + "in %s, jack_type=0x%x\n", __func__, rt711->jack_type); + dev_dbg(&rt711->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + dev_dbg(&rt711->slave->dev, + "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt711->scp_sdca_stat1, rt711->scp_sdca_stat2); + + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_btn_check_work, msecs_to_jiffies(200)); + } +} + +static void rt711_sdca_btn_check_handler(struct work_struct *work) +{ + struct rt711_sdca_priv *rt711 = + container_of(work, struct rt711_sdca_priv, jack_btn_check_work.work); + int btn_type = 0, ret, idx; + unsigned int det_mode, offset, val; + unsigned char buf[3]; + + ret = regmap_read(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0), + &det_mode); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (det_mode) { + /* read UMP message offset */ + ret = regmap_read(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), + &offset); + if (ret < 0) + goto io_error; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt711->regmap, + RT711_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto io_error; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) { + switch (buf[1] & 0xf0) { + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + switch (buf[2]) { + case 0x01: + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x02: + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x04: + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x08: + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + } + } else + rt711->jack_type = 0; + + dev_dbg(&rt711->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711) +{ + mutex_lock(&rt711->calibrate_mutex); + + if (rt711->hs_jack) { + /* Enable HID1 event & set button RTC mode */ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, + RT711_PUSH_BTN_INT_CTL6, 0x80f0, 0x8000); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, + RT711_PUSH_BTN_INT_CTL2, 0x11dd, 0x11dd); + rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL, + RT711_PUSH_BTN_INT_CTL7, 0xffff); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, + RT711_PUSH_BTN_INT_CTL9, 0xf000, 0x0000); + + /* GE_mode_change_event_en & Hid1_push_button_event_en */ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, + RT711_GE_MODE_RELATED_CTL, 0x0c00, 0x0c00); + + switch (rt711->jd_src) { + case RT711_JD1: + /* default settings was already for JD1 */ + break; + case RT711_JD2: + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_JD_CTL1, RT711_JD2_DIGITAL_MODE_SEL, + RT711_JD2_DIGITAL_MODE_SEL); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP | RT711_HP_JD_SEL_JD2, + RT711_JD2_2PORT_200K_DECODE_HP | RT711_HP_JD_SEL_JD2); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_CC_DET1, + RT711_HP_JD_FINAL_RESULT_CTL_JD12, + RT711_HP_JD_FINAL_RESULT_CTL_JD12); + break; + case RT711_JD2_100K: + rt711_sdca_index_write(rt711, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL3, 0xa47e); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_JD_CTL1, RT711_JD2_DIGITAL_MODE_SEL, + RT711_JD2_DIGITAL_MODE_SEL); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP | + RT711_JD2_2PORT_100K_DECODE_MASK | RT711_HP_JD_SEL_JD2, + RT711_JD2_2PORT_100K_DECODE_HP | RT711_HP_JD_SEL_JD2); + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_CC_DET1, + RT711_HP_JD_FINAL_RESULT_CTL_JD12 | RT711_POW_CC1_AGPI, + RT711_HP_JD_FINAL_RESULT_CTL_JD12 | RT711_POW_CC1_AGPI_OFF); + break; + default: + dev_warn(rt711->component->dev, "Wrong JD source\n"); + break; + } + + /* set SCP_SDCA_IntMask1[0]=1 */ + sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); + /* set SCP_SDCA_IntMask2[0]=1 */ + sdw_write_no_pm(rt711->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); + dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__); + } else { + /* disable HID 1/2 event */ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, + RT711_GE_MODE_RELATED_CTL, 0x0c00, 0x0000); + + dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__); + } + + mutex_unlock(&rt711->calibrate_mutex); +} + +static int rt711_sdca_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + int ret; + + rt711->hs_jack = hs_jack; + + /* we can only resume if the device was initialized at least once */ + if (!rt711->first_hw_init) + return 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + rt711_sdca_jack_init(rt711); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +/* For SDCA control DAC/ADC Gain */ +static int rt711_sdca_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int read_l, read_r, gain_l_val, gain_r_val; + unsigned int i, adc_vol_flag = 0, changed = 0; + unsigned int lvalue, rvalue; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt711->mbq_regmap, mc->reg, &lvalue); + regmap_read(rt711->mbq_regmap, mc->rreg, &rvalue); + + /* control value to 2's complement value */ + /* L Channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + read_l = gain_l_val; + + if (mc->shift == 8) /* boost gain */ + gain_l_val = (gain_l_val * 10) << mc->shift; + else { /* ADC/DAC gain */ + if (adc_vol_flag && gain_l_val > mc->shift) + gain_l_val = (gain_l_val - mc->shift) * 75; + else + gain_l_val = (mc->shift - gain_l_val) * 75; + gain_l_val <<= 8; + gain_l_val /= 100; + if (!(adc_vol_flag && read_l > mc->shift)) { + gain_l_val = ~gain_l_val; + gain_l_val += 1; + } + gain_l_val &= 0xffff; + } + + /* R Channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + read_r = gain_r_val; + + if (mc->shift == 8) /* boost gain */ + gain_r_val = (gain_r_val * 10) << mc->shift; + else { /* ADC/DAC gain */ + if (adc_vol_flag && gain_r_val > mc->shift) + gain_r_val = (gain_r_val - mc->shift) * 75; + else + gain_r_val = (mc->shift - gain_r_val) * 75; + gain_r_val <<= 8; + gain_r_val /= 100; + if (!(adc_vol_flag && read_r > mc->shift)) { + gain_r_val = ~gain_r_val; + gain_r_val += 1; + } + gain_r_val &= 0xffff; + } + + if (lvalue != gain_l_val || rvalue != gain_r_val) + changed = 1; + else + return 0; + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + /* Lch*/ + regmap_write(rt711->mbq_regmap, mc->reg, gain_l_val); + + /* Rch */ + regmap_write(rt711->mbq_regmap, mc->rreg, gain_r_val); + + regmap_read(rt711->mbq_regmap, mc->reg, &read_l); + regmap_read(rt711->mbq_regmap, mc->rreg, &read_r); + if (read_r == gain_r_val && read_l == gain_l_val) + break; + } + + return i == 3 ? -EIO : changed; +} + +static int rt711_sdca_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; + unsigned int adc_vol_flag = 0, neg_flag = 0; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt711->mbq_regmap, mc->reg, &read_l); + regmap_read(rt711->mbq_regmap, mc->rreg, &read_r); + + /* 2's complement value to control value */ + if (mc->shift == 8) /* boost gain */ + ctl_l = (read_l >> mc->shift) / 10; + else { /* ADC/DAC gain */ + ctl_l = read_l; + if (read_l & BIT(15)) { + ctl_l = 0xffff & ~(read_l - 1); + neg_flag = 1; + } + ctl_l *= 100; + ctl_l >>= 8; + if (adc_vol_flag) { + if (neg_flag) + ctl_l = mc->shift - (ctl_l / 75); + else + ctl_l = mc->shift + (ctl_l / 75); + } else + ctl_l = mc->max - (ctl_l / 75); + } + + neg_flag = 0; + if (read_l != read_r) { + if (mc->shift == 8) /* boost gain */ + ctl_r = (read_r >> mc->shift) / 10; + else { /* ADC/DAC gain */ + ctl_r = read_r; + if (read_r & BIT(15)) { + ctl_r = 0xffff & ~(read_r - 1); + neg_flag = 1; + } + ctl_r *= 100; + ctl_r >>= 8; + if (adc_vol_flag) { + if (neg_flag) + ctl_r = mc->shift - (ctl_r / 75); + else + ctl_r = mc->shift + (ctl_r / 75); + } else + ctl_r = mc->max - (ctl_r / 75); + } + } else + ctl_r = ctl_l; + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + + return 0; +} + +static int rt711_sdca_set_fu0f_capture_ctl(struct rt711_sdca_priv *rt711) +{ + int err; + unsigned int ch_l, ch_r; + + ch_l = (rt711->fu0f_dapm_mute || rt711->fu0f_mixer_l_mute) ? 0x01 : 0x00; + ch_r = (rt711->fu0f_dapm_mute || rt711->fu0f_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, + RT711_SDCA_CTL_FU_MUTE, CH_L), ch_l); + if (err < 0) + return err; + + err = regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, + RT711_SDCA_CTL_FU_MUTE, CH_R), ch_r); + if (err < 0) + return err; + + return 0; +} + +static int rt711_sdca_set_fu1e_capture_ctl(struct rt711_sdca_priv *rt711) +{ + int err; + unsigned int ch_l, ch_r; + + ch_l = (rt711->fu1e_dapm_mute || rt711->fu1e_mixer_l_mute) ? 0x01 : 0x00; + ch_r = (rt711->fu1e_dapm_mute || rt711->fu1e_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, + RT711_SDCA_CTL_FU_MUTE, CH_L), ch_l); + if (err < 0) + return err; + + err = regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, + RT711_SDCA_CTL_FU_MUTE, CH_R), ch_r); + if (err < 0) + return err; + + return 0; +} + +static int rt711_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt711->fu1e_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt711->fu1e_mixer_r_mute; + return 0; +} + +static int rt711_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + int err, changed = 0; + + if (rt711->fu1e_mixer_l_mute != !ucontrol->value.integer.value[0] || + rt711->fu1e_mixer_r_mute != !ucontrol->value.integer.value[1]) + changed = 1; + + rt711->fu1e_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt711->fu1e_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt711_sdca_set_fu1e_capture_ctl(rt711); + if (err < 0) + return err; + + return changed; +} + +static int rt711_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt711->fu0f_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt711->fu0f_mixer_r_mute; + return 0; +} + +static int rt711_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + int err, changed = 0; + + if (rt711->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] || + rt711->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1]) + changed = 1; + + rt711->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt711->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt711_sdca_set_fu0f_capture_ctl(rt711); + if (err < 0) + return err; + + return changed; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt711_sdca_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R), + 0x57, 0x57, 0, + rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, out_vol_tlv), + SOC_DOUBLE_EXT("FU1E Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt711_sdca_fu1e_capture_get, rt711_sdca_fu1e_capture_put), + SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt711_sdca_fu0f_capture_get, rt711_sdca_fu0f_capture_put), + SOC_DOUBLE_R_EXT_TLV("FU1E Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), + 0x17, 0x3f, 0, + rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R), + 0x17, 0x3f, 0, + rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU44 Gain Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), + 8, 3, 0, + rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU15 Gain Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), + 8, 3, 0, + rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, mic_vol_tlv), +}; + +static int rt711_sdca_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask_sft; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 10; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + mask_sft = 13; + else + return -EINVAL; + + rt711_sdca_index_read(rt711, RT711_VENDOR_HDA_CTL, + RT711_HDA_LEGACY_MUX_CTL1, &val); + + ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7; + + return 0; +} + +static int rt711_sdca_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 10; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + mask_sft = 13; + else + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + rt711_sdca_index_read(rt711, RT711_VENDOR_HDA_CTL, + RT711_HDA_LEGACY_MUX_CTL1, &val2); + val2 = (val2 >> mask_sft) & 0x7; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, + RT711_HDA_LEGACY_MUX_CTL1, 0x7 << mask_sft, + val << mask_sft); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", + "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt711_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt711_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt711_sdca_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt711_adc22_enum, + rt711_sdca_mux_get, rt711_sdca_mux_put); + +static const struct snd_kcontrol_new rt711_sdca_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt711_adc23_enum, + rt711_sdca_mux_get, rt711_sdca_mux_put); + +static int rt711_sdca_fu05_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, + RT711_SDCA_CTL_FU_MUTE, CH_L), + unmute); + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, + RT711_SDCA_CTL_FU_MUTE, CH_R), + unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, + RT711_SDCA_CTL_FU_MUTE, CH_L), + mute); + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, + RT711_SDCA_CTL_FU_MUTE, CH_R), + mute); + break; + } + return 0; +} + +static int rt711_sdca_fu0f_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt711->fu0f_dapm_mute = false; + rt711_sdca_set_fu0f_capture_ctl(rt711); + break; + case SND_SOC_DAPM_PRE_PMD: + rt711->fu0f_dapm_mute = true; + rt711_sdca_set_fu0f_capture_ctl(rt711); + break; + } + return 0; +} + +static int rt711_sdca_fu1e_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt711->fu1e_dapm_mute = false; + rt711_sdca_set_fu1e_capture_ctl(rt711); + break; + case SND_SOC_DAPM_PRE_PMD: + rt711->fu1e_dapm_mute = true; + rt711_sdca_set_fu1e_capture_ctl(rt711); + break; + } + return 0; +} + +static int rt711_sdca_pde28_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28, + RT711_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE28, + RT711_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static int rt711_sdca_pde29_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE29, + RT711_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDE29, + RT711_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static int rt711_sdca_pde2a_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PDE2A, + RT711_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_PDE2A, + RT711_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static int rt711_sdca_line1_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + static unsigned int sel_mode = 0xffff; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_read(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, + RT711_SDCA_CTL_SELECTED_MODE, 0), + &sel_mode); + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE1, + RT711_SDCA_CTL_VENDOR_DEF, 0), + 0x1); + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, + RT711_SDCA_CTL_SELECTED_MODE, 0), + 0x7); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE1, + RT711_SDCA_CTL_VENDOR_DEF, 0), + 0x0); + if (sel_mode != 0xffff) + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_GE49, + RT711_SDCA_CTL_SELECTED_MODE, 0), + sel_mode); + break; + } + + return 0; +} + +static int rt711_sdca_line2_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDELINE2, + RT711_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE2, + RT711_SDCA_CTL_VENDOR_DEF, 0), + 0x1); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_LINE2, + RT711_SDCA_CTL_VENDOR_DEF, 0), + 0x0); + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_PDELINE2, + RT711_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt711_sdca_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + + SND_SOC_DAPM_PGA_E("LINE1 Power", SND_SOC_NOPM, + 0, 0, NULL, 0, rt711_sdca_line1_power_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("LINE2 Power", SND_SOC_NOPM, + 0, 0, NULL, 0, rt711_sdca_line2_power_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("PDE 28", SND_SOC_NOPM, 0, 0, + rt711_sdca_pde28_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 29", SND_SOC_NOPM, 0, 0, + rt711_sdca_pde29_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 2A", SND_SOC_NOPM, 0, 0, + rt711_sdca_pde2a_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_DAC_E("FU 05", NULL, SND_SOC_NOPM, 0, 0, + rt711_sdca_fu05_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 0F", NULL, SND_SOC_NOPM, 0, 0, + rt711_sdca_fu0f_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0, + rt711_sdca_fu1e_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt711_sdca_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt711_sdca_adc23_mux), + + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt711_sdca_audio_map[] = { + {"FU 05", NULL, "DP3RX"}, + {"DP2TX", NULL, "FU 0F"}, + {"DP4TX", NULL, "FU 1E"}, + + {"LINE1 Power", NULL, "LINE1"}, + {"LINE2 Power", NULL, "LINE2"}, + {"HP", NULL, "PDE 28"}, + {"FU 0F", NULL, "PDE 29"}, + {"FU 1E", NULL, "PDE 2A"}, + + {"FU 0F", NULL, "ADC 22 Mux"}, + {"FU 1E", NULL, "ADC 23 Mux"}, + {"ADC 22 Mux", "DMIC", "DMIC1"}, + {"ADC 22 Mux", "LINE1", "LINE1 Power"}, + {"ADC 22 Mux", "LINE2", "LINE2 Power"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "DMIC", "DMIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1 Power"}, + {"ADC 23 Mux", "LINE2", "LINE2 Power"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + + {"HP", NULL, "FU 05"}, +}; + +static int rt711_sdca_parse_dt(struct rt711_sdca_priv *rt711, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", &rt711->jd_src); + + return 0; +} + +static int rt711_sdca_probe(struct snd_soc_component *component) +{ + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + int ret; + + rt711_sdca_parse_dt(rt711, &rt711->slave->dev); + rt711->component = component; + + if (!rt711->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt711 = { + .probe = rt711_sdca_probe, + .controls = rt711_sdca_snd_controls, + .num_controls = ARRAY_SIZE(rt711_sdca_snd_controls), + .dapm_widgets = rt711_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt711_sdca_dapm_widgets), + .dapm_routes = rt711_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt711_sdca_audio_map), + .set_jack = rt711_sdca_set_jack_detect, + .endianness = 1, +}; + +static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt711_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt711_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream; + int retval; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt711->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + port_config.num = 3; + } else { + if (dai->id == RT711_AIF1) + port_config.num = 2; + else if (dai->id == RT711_AIF2) + port_config.num = 4; + else + return -EINVAL; + } + + retval = sdw_stream_add_slave(rt711->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT711_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT711_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT711_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT711_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_CS11, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT711_SDCA_ENT_CS1F, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + + return 0; +} + +static int rt711_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt711->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt711->slave, sdw_stream); + return 0; +} + +#define RT711_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) +#define RT711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt711_sdca_ops = { + .hw_params = rt711_sdca_pcm_hw_params, + .hw_free = rt711_sdca_pcm_hw_free, + .set_stream = rt711_sdca_set_sdw_stream, + .shutdown = rt711_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt711_sdca_dai[] = { + { + .name = "rt711-sdca-aif1", + .id = RT711_AIF1, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .ops = &rt711_sdca_ops, + }, + { + .name = "rt711-sdca-aif2", + .id = RT711_AIF2, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .ops = &rt711_sdca_ops, + } +}; + +int rt711_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt711_sdca_priv *rt711; + int ret; + + rt711 = devm_kzalloc(dev, sizeof(*rt711), GFP_KERNEL); + if (!rt711) + return -ENOMEM; + + dev_set_drvdata(dev, rt711); + rt711->slave = slave; + rt711->regmap = regmap; + rt711->mbq_regmap = mbq_regmap; + + regcache_cache_only(rt711->regmap, true); + regcache_cache_only(rt711->mbq_regmap, true); + + mutex_init(&rt711->calibrate_mutex); + mutex_init(&rt711->disable_irq_lock); + + INIT_DELAYED_WORK(&rt711->jack_detect_work, rt711_sdca_jack_detect_handler); + INIT_DELAYED_WORK(&rt711->jack_btn_check_work, rt711_sdca_btn_check_handler); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt711->hw_init = false; + rt711->first_hw_init = false; + rt711->fu0f_dapm_mute = true; + rt711->fu1e_dapm_mute = true; + rt711->fu0f_mixer_l_mute = rt711->fu0f_mixer_r_mute = true; + rt711->fu1e_mixer_l_mute = rt711->fu1e_mixer_r_mute = true; + + /* JD source uses JD2 in default */ + rt711->jd_src = RT711_JD2; + + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt711, + rt711_sdca_dai, + ARRAY_SIZE(rt711_sdca_dai)); + + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +static void rt711_sdca_vd0_io_init(struct rt711_sdca_priv *rt711) +{ + rt711_sdca_index_write(rt711, RT711_VENDOR_REG, + RT711_GPIO_TEST_MODE_CTL2, 0x0e00); + rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL, + RT711_HDA_LEGACY_GPIO_CTL, 0x0008); + + regmap_write(rt711->regmap, 0x2f5a, 0x01); + + rt711_sdca_index_write(rt711, RT711_VENDOR_REG, + RT711_ADC27_VOL_SET, 0x8728); + + rt711_sdca_index_write(rt711, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL3, 0xa472); + + regmap_write(rt711->regmap, 0x2f50, 0x02); + + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_ANALOG_CTL, + RT711_MISC_POWER_CTL4, 0x6000, 0x6000); + + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL3, 0x000c, 0x000c); + + rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL, + RT711_HDA_LEGACY_CONFIG_CTL, 0x0000); + + rt711_sdca_index_write(rt711, RT711_VENDOR_VAD, + RT711_VAD_SRAM_CTL1, 0x0050); +} + +static void rt711_sdca_vd1_io_init(struct rt711_sdca_priv *rt711) +{ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, + RT711_HDA_LEGACY_UNSOLICITED_CTL, 0x0300, 0x0000); + + rt711_sdca_index_write(rt711, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL3, 0xa43e); + + regmap_write(rt711->regmap, 0x2f5a, 0x05); + + rt711_sdca_index_write(rt711, RT711_VENDOR_REG, + RT711_JD_CTRL6, 0x0500); + + rt711_sdca_index_write(rt711, RT711_VENDOR_REG, + RT711_DMIC_CTL1, 0x6173); + + rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL, + RT711_HDA_LEGACY_CONFIG_CTL, 0x0000); + + rt711_sdca_index_write(rt711, RT711_VENDOR_VAD, + RT711_VAD_SRAM_CTL1, 0x0050); +} + +int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev); + int ret = 0; + unsigned int val; + + rt711->disable_irq = false; + + if (rt711->hw_init) + return 0; + + regcache_cache_only(rt711->regmap, false); + regcache_cache_only(rt711->mbq_regmap, false); + + if (rt711->first_hw_init) { + regcache_cache_bypass(rt711->regmap, true); + regcache_cache_bypass(rt711->mbq_regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt711_sdca_reset(rt711); + + rt711_sdca_index_read(rt711, RT711_VENDOR_REG, RT711_JD_PRODUCT_NUM, &val); + rt711->hw_ver = val & 0xf; + + if (rt711->hw_ver == RT711_VER_VD0) + rt711_sdca_vd0_io_init(rt711); + else + rt711_sdca_vd1_io_init(rt711); + + /* DP4 mux select from 08_filter_Out_pri */ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, + RT711_FILTER_SRC_SEL, 0x1800, 0x0800); + + /* ge_exclusive_inbox_en disable */ + rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, + RT711_PUSH_BTN_INT_CTL0, 0x20, 0x00); + + /* calibration */ + ret = rt711_sdca_calibration(rt711); + if (ret < 0) + dev_err(dev, "%s, calibration failed!\n", __func__); + + /* HP output enable */ + regmap_write(rt711->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT711_SDCA_ENT_OT1, RT711_SDCA_CTL_VENDOR_DEF, 0), 0x4); + + /* + * if set_jack callback occurred early than io_init, + * we set up the jack detection function now + */ + if (rt711->hs_jack) + rt711_sdca_jack_init(rt711); + + if (rt711->first_hw_init) { + regcache_cache_bypass(rt711->regmap, false); + regcache_mark_dirty(rt711->regmap); + regcache_cache_bypass(rt711->mbq_regmap, false); + regcache_mark_dirty(rt711->mbq_regmap); + } else + rt711->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt711->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT711 SDCA SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h new file mode 100644 index 0000000000..11d421e8ab --- /dev/null +++ b/sound/soc/codecs/rt711-sdca.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt711-sdca.h -- RT711 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2021 Realtek Semiconductor Corp. + */ + +#ifndef __RT711_SDCA_H__ +#define __RT711_SDCA_H__ + +#include +#include +#include +#include +#include +#include + +struct rt711_sdca_priv { + struct regmap *regmap, *mbq_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + struct mutex calibrate_mutex; /* for headset calibration */ + struct mutex disable_irq_lock; /* SDCA irq lock protection */ + bool disable_irq; + int jack_type, jd_src; + unsigned int scp_sdca_stat1, scp_sdca_stat2; + int hw_ver; + bool fu0f_dapm_mute, fu0f_mixer_l_mute, fu0f_mixer_r_mute; + bool fu1e_dapm_mute, fu1e_mixer_l_mute, fu1e_mixer_r_mute; +}; + +/* NID */ +#define RT711_AUDIO_FUNCTION_GROUP 0x01 +#define RT711_DAC_OUT2 0x03 +#define RT711_ADC_IN1 0x09 +#define RT711_ADC_IN2 0x08 +#define RT711_DMIC1 0x12 +#define RT711_DMIC2 0x13 +#define RT711_MIC2 0x19 +#define RT711_LINE1 0x1a +#define RT711_LINE2 0x1b +#define RT711_BEEP 0x1d +#define RT711_VENDOR_REG 0x20 +#define RT711_HP_OUT 0x21 +#define RT711_MIXER_IN1 0x22 +#define RT711_MIXER_IN2 0x23 +#define RT711_INLINE_CMD 0x55 +#define RT711_VENDOR_CALI 0x58 +#define RT711_VENDOR_IMS_DRE 0x5b +#define RT711_VENDOR_VAD 0x5e +#define RT711_VENDOR_ANALOG_CTL 0x5f +#define RT711_VENDOR_HDA_CTL 0x61 + +/* Index (NID:20h) */ +#define RT711_JD_PRODUCT_NUM 0x00 +#define RT711_DMIC_CTL1 0x06 +#define RT711_JD_CTL1 0x08 +#define RT711_JD_CTL2 0x09 +#define RT711_CC_DET1 0x11 +#define RT711_PARA_VERB_CTL 0x1a +#define RT711_COMBO_JACK_AUTO_CTL1 0x45 +#define RT711_COMBO_JACK_AUTO_CTL2 0x46 +#define RT711_COMBO_JACK_AUTO_CTL3 0x47 +#define RT711_INLINE_CMD_CTL 0x48 +#define RT711_DIGITAL_MISC_CTRL4 0x4a +#define RT711_JD_CTRL6 0x6a +#define RT711_VREFOUT_CTL 0x6b +#define RT711_GPIO_TEST_MODE_CTL2 0x6d +#define RT711_FSM_CTL 0x6f +#define RT711_IRQ_FLAG_TABLE1 0x80 +#define RT711_IRQ_FLAG_TABLE2 0x81 +#define RT711_IRQ_FLAG_TABLE3 0x82 +#define RT711_HP_FSM_CTL 0x83 +#define RT711_TX_RX_MUX_CTL 0x91 +#define RT711_FILTER_SRC_SEL 0xb0 +#define RT711_ADC27_VOL_SET 0xb7 + +/* Index (NID:58h) */ +#define RT711_DAC_DC_CALI_CTL1 0x00 +#define RT711_DAC_DC_CALI_CTL2 0x01 + +/* Index (NID:5bh) */ +#define RT711_IMS_DIGITAL_CTL1 0x00 +#define RT711_HP_IMS_RESULT_L 0x20 +#define RT711_HP_IMS_RESULT_R 0x21 + +/* Index (NID:5eh) */ +#define RT711_VAD_SRAM_CTL1 0x10 + +/* Index (NID:5fh) */ +#define RT711_MISC_POWER_CTL0 0x01 +#define RT711_MISC_POWER_CTL4 0x05 + +/* Index (NID:61h) */ +#define RT711_HDA_LEGACY_MUX_CTL1 0x00 +#define RT711_HDA_LEGACY_UNSOLICITED_CTL 0x03 +#define RT711_HDA_LEGACY_CONFIG_CTL 0x06 +#define RT711_HDA_LEGACY_RESET_CTL 0x08 +#define RT711_HDA_LEGACY_GPIO_CTL 0x0a +#define RT711_ADC08_09_PDE_CTL 0x24 +#define RT711_GE_MODE_RELATED_CTL 0x35 +#define RT711_PUSH_BTN_INT_CTL0 0x36 +#define RT711_PUSH_BTN_INT_CTL1 0x37 +#define RT711_PUSH_BTN_INT_CTL2 0x38 +#define RT711_PUSH_BTN_INT_CTL6 0x3c +#define RT711_PUSH_BTN_INT_CTL7 0x3d +#define RT711_PUSH_BTN_INT_CTL9 0x3f + +/* DAC DC offset calibration control-1 (0x00)(NID:20h) */ +#define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15) +#define RT711_DAC_DC_CALI_CLK_EN (0x1 << 14) +#define RT711_DAC_DC_FORCE_CALI_RST (0x1 << 3) + +/* jack detect control 1 (0x08)(NID:20h) */ +#define RT711_JD2_DIGITAL_MODE_SEL (0x1 << 1) + +/* jack detect control 2 (0x09)(NID:20h) */ +#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13) +#define RT711_JD2_2PORT_100K_DECODE_MASK (0x1 << 12) +#define RT711_JD2_2PORT_100K_DECODE_HP (0x0 << 12) +#define RT711_HP_JD_SEL_JD1 (0x0 << 1) +#define RT711_HP_JD_SEL_JD2 (0x1 << 1) + +/* CC DET1 (0x11)(NID:20h) */ +#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10) +#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10) +#define RT711_POW_CC1_AGPI (0x1 << 5) +#define RT711_POW_CC1_AGPI_ON (0x1 << 5) +#define RT711_POW_CC1_AGPI_OFF (0x0 << 5) + +/* Parameter & Verb control (0x1a)(NID:20h) */ +#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT711_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT711_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT711_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT711_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +/* FSM control (0x6f)(NID:20h) */ +#define RT711_CALI_CTL (0x0 << 0) +#define RT711_COMBOJACK_CTL (0x1 << 0) +#define RT711_IMS_CTL (0x2 << 0) +#define RT711_DEPOP_CTL (0x3 << 0) +#define RT711_FSM_IMP_EN (0x1 << 6) + +/* Impedance Sense Digital Control 1 (0x00)(NID:5bh) */ +#define RT711_TRIGGER_IMS (0x1 << 15) +#define RT711_IMS_EN (0x1 << 6) + +#define RT711_EAPD_HIGH 0x2 +#define RT711_EAPD_LOW 0x0 +#define RT711_MUTE_SFT 7 +/* set input/output mapping to payload[14][15] separately */ +#define RT711_DIR_IN_SFT 6 +#define RT711_DIR_OUT_SFT 7 + +/* RC Calibration register */ +#define RT711_RC_CAL_STATUS 0x320c + +/* Buffer address for HID */ +#define RT711_BUF_ADDR_HID1 0x44030000 +#define RT711_BUF_ADDR_HID2 0x44030020 + +/* RT711 SDCA Control - function number */ +#define FUNC_NUM_JACK_CODEC 0x01 +#define FUNC_NUM_MIC_ARRAY 0x02 +#define FUNC_NUM_HID 0x03 + +/* RT711 SDCA entity */ +#define RT711_SDCA_ENT_HID01 0x01 +#define RT711_SDCA_ENT_GE49 0x49 +#define RT711_SDCA_ENT_USER_FU05 0x05 +#define RT711_SDCA_ENT_USER_FU0F 0x0f +#define RT711_SDCA_ENT_USER_FU1E 0x1e +#define RT711_SDCA_ENT_PLATFORM_FU15 0x15 +#define RT711_SDCA_ENT_PLATFORM_FU44 0x44 +#define RT711_SDCA_ENT_PDE28 0x28 +#define RT711_SDCA_ENT_PDE29 0x29 +#define RT711_SDCA_ENT_PDE2A 0x2a +#define RT711_SDCA_ENT_CS01 0x01 +#define RT711_SDCA_ENT_CS11 0x11 +#define RT711_SDCA_ENT_CS1F 0x1f +#define RT711_SDCA_ENT_OT1 0x06 +#define RT711_SDCA_ENT_LINE1 0x09 +#define RT711_SDCA_ENT_LINE2 0x31 +#define RT711_SDCA_ENT_PDELINE2 0x36 +#define RT711_SDCA_ENT_USER_FU9 0x41 + +/* RT711 SDCA control */ +#define RT711_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT711_SDCA_CTL_FU_CH_GAIN 0x0b +#define RT711_SDCA_CTL_FU_MUTE 0x01 +#define RT711_SDCA_CTL_FU_VOLUME 0x02 +#define RT711_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10 +#define RT711_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11 +#define RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12 +#define RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13 +#define RT711_SDCA_CTL_SELECTED_MODE 0x01 +#define RT711_SDCA_CTL_DETECTED_MODE 0x02 +#define RT711_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT711_SDCA_CTL_VENDOR_DEF 0x30 + +/* RT711 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 + +/* sample frequency index */ +#define RT711_SDCA_RATE_44100HZ 0x08 +#define RT711_SDCA_RATE_48000HZ 0x09 +#define RT711_SDCA_RATE_96000HZ 0x0b +#define RT711_SDCA_RATE_192000HZ 0x0d + +enum { + RT711_AIF1, + RT711_AIF2, + RT711_AIFS, +}; + +enum rt711_sdca_jd_src { + RT711_JD_NULL, + RT711_JD1, + RT711_JD2, + RT711_JD2_100K +}; + +enum rt711_sdca_ver { + RT711_VER_VD0, + RT711_VER_VD1 +}; + +int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt711_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave); + +int rt711_sdca_jack_detect(struct rt711_sdca_priv *rt711, bool *hp, bool *mic); +#endif /* __RT711_SDCA_H__ */ diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c new file mode 100644 index 0000000000..3f5773310a --- /dev/null +++ b/sound/soc/codecs/rt711-sdw.c @@ -0,0 +1,584 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt711-sdw.c -- rt711 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt711.h" +#include "rt711-sdw.h" + +static bool rt711_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0: + case 0x00f0: + case 0x2012 ... 0x2016: + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2201 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2239: + case 0x2f01 ... 0x2f0f: + case 0x3000 ... 0x3fff: + case 0x7000 ... 0x7fff: + case 0x8300 ... 0x83ff: + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0x752009: + case 0x752011: + case 0x75201a: + case 0x752045: + case 0x752046: + case 0x752048: + case 0x75204a: + case 0x75206b: + case 0x75206f: + case 0x752080: + case 0x752081: + case 0x752091: + case 0x755800: + return true; + default: + return false; + } +} + +static bool rt711_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + case 0x9c00 ... 0x9cff: + case 0xb900 ... 0xb9ff: + case 0xff01: + case 0x75201a: + case 0x752046: + case 0x752080: + case 0x752081: + case 0x755800: + return true; + default: + return false; + } +} + +static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; + unsigned int is_hda_reg = 1, is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt711->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT711_PRIV_DATA_R_H | nid; + ret = regmap_write(rt711->sdw_regmap, + reg3, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg4, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x3000) { + reg += 0x8000; + ret = regmap_write(rt711->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + reg += 0x2000; + reg |= 0x800; + ret = regmap_write(rt711->sdw_regmap, + reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt711->sdw_regmap, + reg2, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt711->sdw_regmap, reg, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x9000) { + ret = regmap_write(rt711->sdw_regmap, + reg, ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0xb000) { + ret = regmap_write(rt711->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else { + ret = regmap_read(rt711->sdw_regmap, reg, val); + if (ret < 0) + return ret; + is_hda_reg = 0; + } + + if (is_hda_reg || is_index_reg) { + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_3, &sdw_data_3); + if (ret < 0) + return ret; + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_2, &sdw_data_2); + if (ret < 0) + return ret; + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_1, &sdw_data_1); + if (ret < 0) + return ret; + ret = regmap_read(rt711->sdw_regmap, + RT711_READ_HDA_0, &sdw_data_0); + if (ret < 0) + return ret; + *val = ((sdw_data_3 & 0xff) << 24) | + ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + } + + if (is_hda_reg == 0) + dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", + __func__, reg, reg2, reg3, reg4, *val); + else + dev_dbg(dev, "[%s] %04x %04x => %08x\n", + __func__, reg, reg2, *val); + + return 0; +} + +static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; + unsigned int is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt711->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT711_PRIV_DATA_W_H | nid; + ret = regmap_write(rt711->sdw_regmap, + reg3, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg4, (val & 0xff)); + if (ret < 0) + return ret; + is_index_reg = 1; + } else if (reg < 0x4fff) { + ret = regmap_write(rt711->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (reg == RT711_FUNC_RESET) { + ret = regmap_write(rt711->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + ret = regmap_write(rt711->sdw_regmap, + reg, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt711->sdw_regmap, reg2, (val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt711->sdw_regmap, + reg2, ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt711->sdw_regmap, reg, (val & 0xff)); + if (ret < 0) + return ret; + } + + if (reg2 == 0) + dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", + __func__, reg, reg2, reg3, reg4, val2, val); + else + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", + __func__, reg, reg2, val); + + return 0; +} + +static const struct regmap_config rt711_regmap = { + .reg_bits = 24, + .val_bits = 32, + .readable_reg = rt711_readable_register, + .volatile_reg = rt711_volatile_register, + .max_register = 0x755800, + .reg_defaults = rt711_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, + .reg_read = rt711_sdw_read, + .reg_write = rt711_sdw_write, +}; + +static const struct regmap_config rt711_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt711_readable_register, + .max_register = 0xff01, + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt711_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt711->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt711->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt711_io_init(&slave->dev, slave); +} + +static int rt711_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH | + SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x14; /* BITMAP: 00010100 */ + prop->sink_ports = 0x8; /* BITMAP: 00001000 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt711_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt711->params, params, sizeof(*params)); + + ret = rt711_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return ret; +} + +static int rt711_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + + dev_dbg(&slave->dev, + "%s control_port_stat=%x", __func__, status->control_port); + + mutex_lock(&rt711->disable_irq_lock); + if (status->control_port & 0x4 && !rt711->disable_irq) { + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(250)); + } + mutex_unlock(&rt711->disable_irq_lock); + + return 0; +} + +static const struct sdw_slave_ops rt711_slave_ops = { + .read_prop = rt711_read_prop, + .interrupt_callback = rt711_interrupt_callback, + .update_status = rt711_update_status, + .bus_config = rt711_bus_config, +}; + +static int rt711_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *sdw_regmap, *regmap; + + /* Regmap Initialization */ + sdw_regmap = devm_regmap_init_sdw(slave, &rt711_sdw_regmap); + if (IS_ERR(sdw_regmap)) + return PTR_ERR(sdw_regmap); + + regmap = devm_regmap_init(&slave->dev, NULL, + &slave->dev, &rt711_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt711_init(&slave->dev, sdw_regmap, regmap, slave); +} + +static int rt711_sdw_remove(struct sdw_slave *slave) +{ + struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); + + if (rt711->hw_init) { + cancel_delayed_work_sync(&rt711->jack_detect_work); + cancel_delayed_work_sync(&rt711->jack_btn_check_work); + cancel_work_sync(&rt711->calibration_work); + } + + pm_runtime_disable(&slave->dev); + + mutex_destroy(&rt711->calibrate_mutex); + mutex_destroy(&rt711->disable_irq_lock); + + return 0; +} + +static const struct sdw_device_id rt711_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x711, 0x2, 0, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt711_id); + +static int __maybe_unused rt711_dev_suspend(struct device *dev) +{ + struct rt711_priv *rt711 = dev_get_drvdata(dev); + + if (!rt711->hw_init) + return 0; + + cancel_delayed_work_sync(&rt711->jack_detect_work); + cancel_delayed_work_sync(&rt711->jack_btn_check_work); + cancel_work_sync(&rt711->calibration_work); + + regcache_cache_only(rt711->regmap, true); + + return 0; +} + +static int __maybe_unused rt711_dev_system_suspend(struct device *dev) +{ + struct rt711_priv *rt711 = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret; + + if (!rt711->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt711->disable_irq_lock); + rt711->disable_irq = true; + ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, + SDW_SCP_INT1_IMPL_DEF, 0); + mutex_unlock(&rt711->disable_irq_lock); + + if (ret < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:", __func__); + } + + return rt711_dev_suspend(dev); +} + +#define RT711_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt711_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt711->first_hw_init) + return 0; + + if (!slave->unattach_request) { + if (rt711->disable_irq == true) { + mutex_lock(&rt711->disable_irq_lock); + sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); + rt711->disable_irq = false; + mutex_unlock(&rt711->disable_irq_lock); + } + goto regmap_sync; + } + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT711_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt711->regmap, false); + regcache_sync_region(rt711->regmap, 0x3000, 0x8fff); + regcache_sync_region(rt711->regmap, 0x752009, 0x752091); + + return 0; +} + +static const struct dev_pm_ops rt711_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt711_dev_system_suspend, rt711_dev_resume) + SET_RUNTIME_PM_OPS(rt711_dev_suspend, rt711_dev_resume, NULL) +}; + +static struct sdw_driver rt711_sdw_driver = { + .driver = { + .name = "rt711", + .owner = THIS_MODULE, + .pm = &rt711_pm, + }, + .probe = rt711_sdw_probe, + .remove = rt711_sdw_remove, + .ops = &rt711_slave_ops, + .id_table = rt711_id, +}; +module_sdw_driver(rt711_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT711 SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h new file mode 100644 index 0000000000..6acf985833 --- /dev/null +++ b/sound/soc/codecs/rt711-sdw.h @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt711-sdw.h -- RT711 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT711_SDW_H__ +#define __RT711_SDW_H__ + +static const struct reg_default rt711_reg_defaults[] = { + { 0x0000, 0x00 }, + { 0x0001, 0x00 }, + { 0x0002, 0x00 }, + { 0x0003, 0x00 }, + { 0x0004, 0x00 }, + { 0x0005, 0x01 }, + { 0x0020, 0x00 }, + { 0x0022, 0x00 }, + { 0x0023, 0x00 }, + { 0x0024, 0x00 }, + { 0x0025, 0x00 }, + { 0x0026, 0x00 }, + { 0x0030, 0x00 }, + { 0x0032, 0x00 }, + { 0x0033, 0x00 }, + { 0x0034, 0x00 }, + { 0x0035, 0x00 }, + { 0x0036, 0x00 }, + { 0x0040, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0043, 0x00 }, + { 0x0044, 0x20 }, + { 0x0045, 0x01 }, + { 0x0046, 0x01 }, + { 0x0050, 0x20 }, + { 0x0051, 0x02 }, + { 0x0052, 0x5d }, + { 0x0053, 0x07 }, + { 0x0054, 0x11 }, + { 0x0055, 0x00 }, + { 0x0060, 0x00 }, + { 0x0070, 0x00 }, + { 0x0080, 0xc0 }, + { 0x0088, 0x00 }, + { 0x00e0, 0x00 }, + { 0x00e1, 0x00 }, + { 0x00e2, 0x00 }, + { 0x00e3, 0x00 }, + { 0x00e5, 0x00 }, + { 0x00ee, 0x00 }, + { 0x00ef, 0x00 }, + { 0x00f0, 0x00 }, + { 0x00f1, 0x00 }, + { 0x00f2, 0x00 }, + { 0x00f3, 0x00 }, + { 0x00f4, 0x00 }, + { 0x00f5, 0x00 }, + { 0x00fe, 0x00 }, + { 0x00ff, 0x00 }, + { 0x0100, 0x00 }, + { 0x0101, 0x00 }, + { 0x0102, 0x00 }, + { 0x0103, 0x00 }, + { 0x0104, 0x00 }, + { 0x0105, 0x00 }, + { 0x0120, 0x00 }, + { 0x0122, 0x00 }, + { 0x0123, 0x00 }, + { 0x0124, 0x00 }, + { 0x0125, 0x00 }, + { 0x0126, 0x00 }, + { 0x0127, 0x00 }, + { 0x0130, 0x00 }, + { 0x0132, 0x00 }, + { 0x0133, 0x00 }, + { 0x0134, 0x00 }, + { 0x0135, 0x00 }, + { 0x0136, 0x00 }, + { 0x0137, 0x00 }, + { 0x0200, 0x00 }, + { 0x0201, 0x00 }, + { 0x0202, 0x00 }, + { 0x0203, 0x00 }, + { 0x0204, 0x00 }, + { 0x0205, 0x03 }, + { 0x0220, 0x00 }, + { 0x0222, 0x00 }, + { 0x0223, 0x00 }, + { 0x0224, 0x00 }, + { 0x0225, 0x00 }, + { 0x0226, 0x00 }, + { 0x0227, 0x00 }, + { 0x0230, 0x00 }, + { 0x0232, 0x00 }, + { 0x0233, 0x00 }, + { 0x0234, 0x00 }, + { 0x0235, 0x00 }, + { 0x0236, 0x00 }, + { 0x0237, 0x00 }, + { 0x0300, 0x00 }, + { 0x0301, 0x00 }, + { 0x0302, 0x20 }, + { 0x0303, 0x00 }, + { 0x0304, 0x00 }, + { 0x0305, 0x03 }, + { 0x0320, 0x00 }, + { 0x0322, 0x00 }, + { 0x0323, 0x00 }, + { 0x0324, 0x00 }, + { 0x0325, 0x00 }, + { 0x0326, 0x00 }, + { 0x0327, 0x00 }, + { 0x0330, 0x00 }, + { 0x0332, 0x00 }, + { 0x0333, 0x00 }, + { 0x0334, 0x00 }, + { 0x0335, 0x00 }, + { 0x0336, 0x00 }, + { 0x0337, 0x00 }, + { 0x0400, 0x00 }, + { 0x0401, 0x00 }, + { 0x0402, 0x00 }, + { 0x0403, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x03 }, + { 0x0420, 0x00 }, + { 0x0422, 0x00 }, + { 0x0423, 0x00 }, + { 0x0424, 0x00 }, + { 0x0425, 0x00 }, + { 0x0426, 0x00 }, + { 0x0427, 0x00 }, + { 0x0430, 0x00 }, + { 0x0432, 0x00 }, + { 0x0433, 0x00 }, + { 0x0434, 0x00 }, + { 0x0435, 0x00 }, + { 0x0436, 0x00 }, + { 0x0437, 0x00 }, + { 0x0f00, 0x00 }, + { 0x0f01, 0x00 }, + { 0x0f02, 0x20 }, + { 0x0f03, 0x00 }, + { 0x0f04, 0x00 }, + { 0x0f05, 0x03 }, + { 0x0f06, 0x00 }, + { 0x0f07, 0x00 }, + { 0x0f08, 0x00 }, + { 0x0f09, 0x00 }, + { 0x0f10, 0x00 }, + { 0x0f11, 0x00 }, + { 0x0f12, 0x00 }, + { 0x0f13, 0x00 }, + { 0x0f14, 0x00 }, + { 0x0f15, 0x00 }, + { 0x0f16, 0x00 }, + { 0x0f17, 0x00 }, + { 0x0f18, 0x00 }, + { 0x0f19, 0x00 }, + { 0x0f1a, 0x00 }, + { 0x0f1b, 0x00 }, + { 0x0f1c, 0x00 }, + { 0x0f1d, 0x00 }, + { 0x0f1e, 0x00 }, + { 0x0f1f, 0x00 }, + { 0x0f20, 0x00 }, + { 0x0f22, 0x00 }, + { 0x0f23, 0x00 }, + { 0x0f24, 0x00 }, + { 0x0f25, 0x00 }, + { 0x0f26, 0x00 }, + { 0x0f27, 0x00 }, + { 0x0f30, 0x00 }, + { 0x0f32, 0x00 }, + { 0x0f33, 0x00 }, + { 0x0f34, 0x00 }, + { 0x0f35, 0x00 }, + { 0x0f36, 0x00 }, + { 0x0f37, 0x00 }, + { 0x2012, 0x00 }, + { 0x2013, 0x00 }, + { 0x2014, 0x00 }, + { 0x2015, 0x00 }, + { 0x2016, 0x00 }, + { 0x201a, 0x00 }, + { 0x201b, 0x00 }, + { 0x201c, 0x0c }, + { 0x201d, 0x00 }, + { 0x201e, 0x00 }, + { 0x201f, 0x00 }, + { 0x2020, 0x00 }, + { 0x2021, 0x00 }, + { 0x2022, 0x00 }, + { 0x2023, 0x00 }, + { 0x2024, 0x00 }, + { 0x2025, 0x01 }, + { 0x2026, 0x00 }, + { 0x2027, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2201, 0xc7 }, + { 0x2202, 0x0c }, + { 0x2203, 0x22 }, + { 0x2204, 0x04 }, + { 0x2206, 0x00 }, + { 0x2207, 0x00 }, + { 0x2208, 0x00 }, + { 0x2209, 0x00 }, + { 0x220a, 0x00 }, + { 0x220b, 0x00 }, + { 0x220c, 0x00 }, + { 0x220d, 0x04 }, + { 0x220e, 0x00 }, + { 0x220f, 0x00 }, + { 0x2211, 0x01 }, + { 0x2212, 0x00 }, + { 0x2220, 0x00 }, + { 0x2221, 0x00 }, + { 0x2222, 0x00 }, + { 0x2223, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x2f }, + { 0x2232, 0x80 }, + { 0x2233, 0x00 }, + { 0x2234, 0x00 }, + { 0x2235, 0x00 }, + { 0x2236, 0x00 }, + { 0x2237, 0x00 }, + { 0x2238, 0x00 }, + { 0x2239, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f07, 0xcf }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f0b, 0x00 }, + { 0x2f0c, 0x00 }, + { 0x2f0d, 0x00 }, + { 0x2f0e, 0x00 }, + { 0x2f0f, 0x00 }, + { 0x3122, 0x00 }, + { 0x3123, 0x00 }, + { 0x7303, 0x57 }, + { 0x8383, 0x57 }, + { 0x7308, 0x97 }, + { 0x8388, 0x97 }, + { 0x7309, 0x97 }, + { 0x8389, 0x97 }, + { 0x7312, 0x00 }, + { 0x8392, 0x00 }, + { 0x7313, 0x00 }, + { 0x8393, 0x00 }, + { 0x7319, 0x00 }, + { 0x8399, 0x00 }, + { 0x752008, 0xa807 }, + { 0x752009, 0x1029 }, + { 0x75200b, 0x7770 }, + { 0x752011, 0x007a }, + { 0x75201a, 0x8003 }, + { 0x752045, 0x5289 }, + { 0x752048, 0xd049 }, + { 0x75204a, 0xa83b }, + { 0x75206b, 0x5064 }, + { 0x75206f, 0x058b }, + { 0x752091, 0x0000 }, +}; + +#endif /* __RT711_SDW_H__ */ diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c new file mode 100644 index 0000000000..66eaed13b0 --- /dev/null +++ b/sound/soc/codecs/rt711.c @@ -0,0 +1,1343 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rt711.c -- rt711 ALSA SoC audio driver +// +// Copyright(c) 2019 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt711.h" + +static int rt711_index_write(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int value) +{ + int ret; + unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 8) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + pr_err("Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt711_index_read(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int addr = ((RT711_PRIV_INDEX_W_H | nid) << 8) | reg; + + *value = 0; + ret = regmap_read(regmap, addr, value); + if (ret < 0) + pr_err("Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt711_index_update_bits(struct regmap *regmap, unsigned int nid, + unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp, orig; + int ret; + + ret = rt711_index_read(regmap, nid, reg, &orig); + if (ret < 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + + return rt711_index_write(regmap, nid, reg, tmp); +} + +static void rt711_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT711_FUNC_RESET, 0); + rt711_index_update_bits(regmap, RT711_VENDOR_REG, + RT711_PARA_VERB_CTL, RT711_HIDDEN_REG_SW_RESET, + RT711_HIDDEN_REG_SW_RESET); +} + +static int rt711_calibration(struct rt711_priv *rt711) +{ + unsigned int val, loop = 0; + struct device *dev; + struct regmap *regmap = rt711->regmap; + int ret = 0; + + mutex_lock(&rt711->calibrate_mutex); + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + dev = regmap_get_device(regmap); + + /* Calibration manual mode */ + rt711_index_update_bits(regmap, RT711_VENDOR_REG, RT711_FSM_CTL, + 0xf, 0x0); + + /* trigger */ + rt711_index_update_bits(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER, + RT711_DAC_DC_CALI_TRIGGER); + + /* wait for calibration process */ + rt711_index_read(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, &val); + + while (val & RT711_DAC_DC_CALI_TRIGGER) { + if (loop >= 500) { + pr_err("%s, calibration time-out!\n", + __func__); + ret = -ETIMEDOUT; + break; + } + loop++; + + usleep_range(10000, 11000); + rt711_index_read(regmap, RT711_VENDOR_CALI, + RT711_DAC_DC_CALI_CTL1, &val); + } + + /* depop mode */ + rt711_index_update_bits(regmap, RT711_VENDOR_REG, + RT711_FSM_CTL, 0xf, RT711_DEPOP_CTL); + + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + mutex_unlock(&rt711->calibrate_mutex); + + dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); + return ret; +} + +static unsigned int rt711_button_detect(struct rt711_priv *rt711) +{ + unsigned int btn_type = 0, val80, val81; + int ret; + + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_IRQ_FLAG_TABLE1, &val80); + if (ret < 0) + goto read_error; + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_IRQ_FLAG_TABLE2, &val81); + if (ret < 0) + goto read_error; + + val80 &= 0x0381; + val81 &= 0xff00; + + switch (val80) { + case 0x0200: + case 0x0100: + case 0x0080: + btn_type |= SND_JACK_BTN_0; + break; + case 0x0001: + btn_type |= SND_JACK_BTN_3; + break; + } + switch (val81) { + case 0x8000: + case 0x4000: + case 0x2000: + btn_type |= SND_JACK_BTN_1; + break; + case 0x1000: + case 0x0800: + case 0x0400: + btn_type |= SND_JACK_BTN_2; + break; + case 0x0200: + case 0x0100: + btn_type |= SND_JACK_BTN_3; + break; + } +read_error: + return btn_type; +} + +static int rt711_headset_detect(struct rt711_priv *rt711) +{ + unsigned int buf, loop = 0; + int ret; + unsigned int jack_status = 0, reg; + + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + + while (loop < 500 && + (buf & RT711_COMBOJACK_AUTO_DET_STATUS) == 0) { + loop++; + + usleep_range(9000, 10000); + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL2, &buf); + if (ret < 0) + goto io_error; + + reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; + ret = regmap_read(rt711->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + if ((jack_status & (1 << 31)) == 0) + goto remove_error; + } + + if (loop >= 500) + goto to_error; + + if (buf & RT711_COMBOJACK_AUTO_DET_TRS) + rt711->jack_type = SND_JACK_HEADPHONE; + else if ((buf & RT711_COMBOJACK_AUTO_DET_CTIA) || + (buf & RT711_COMBOJACK_AUTO_DET_OMTP)) + rt711->jack_type = SND_JACK_HEADSET; + + return 0; + +to_error: + ret = -ETIMEDOUT; + pr_err_ratelimited("Time-out error in %s\n", __func__); + return ret; +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +remove_error: + pr_err_ratelimited("Jack removal in %s\n", __func__); + return -ENODEV; +} + +static void rt711_jack_detect_handler(struct work_struct *work) +{ + struct rt711_priv *rt711 = + container_of(work, struct rt711_priv, jack_detect_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + if (!rt711->hs_jack) + return; + + if (!snd_soc_card_is_instantiated(rt711->component->card)) + return; + + if (pm_runtime_status_suspended(rt711->slave->dev.parent)) { + dev_dbg(&rt711->slave->dev, + "%s: parent device is pm_runtime_status_suspended, skipping jack detection\n", + __func__); + return; + } + + reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; + ret = regmap_read(rt711->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + /* jack in */ + if (rt711->jack_type == 0) { + ret = rt711_headset_detect(rt711); + if (ret < 0) + return; + if (rt711->jack_type == SND_JACK_HEADSET) + btn_type = rt711_button_detect(rt711); + } else if (rt711->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt711_button_detect(rt711); + } + } else { + /* jack out */ + rt711->jack_type = 0; + } + + dev_dbg(&rt711->slave->dev, + "in %s, jack_type=0x%x\n", __func__, rt711->jack_type); + dev_dbg(&rt711->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt711_btn_check_handler(struct work_struct *work) +{ + struct rt711_priv *rt711 = container_of(work, struct rt711_priv, + jack_btn_check_work.work); + int btn_type = 0, ret; + unsigned int jack_status = 0, reg; + + reg = RT711_VERB_GET_PIN_SENSE | RT711_HP_OUT; + ret = regmap_read(rt711->regmap, reg, &jack_status); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (jack_status & (1 << 31)) { + if (rt711->jack_type == SND_JACK_HEADSET) { + /* jack is already in, report button event */ + btn_type = rt711_button_detect(rt711); + } + } else { + rt711->jack_type = 0; + } + + /* cbj comparator */ + ret = rt711_index_read(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL2, ®); + if (ret < 0) + goto io_error; + + if ((reg & 0xf0) == 0xf0) + btn_type = 0; + + dev_dbg(&rt711->slave->dev, + "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt711->hs_jack, rt711->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt711_jack_init(struct rt711_priv *rt711) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(rt711->component); + + mutex_lock(&rt711->calibrate_mutex); + /* power on */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + if (rt711->hs_jack) { + /* unsolicited response & IRQ control */ + regmap_write(rt711->regmap, + RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x82); + regmap_write(rt711->regmap, + RT711_SET_HP_UNSOLICITED_ENABLE, 0x81); + regmap_write(rt711->regmap, + RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x83); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + 0x10, 0x2420); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + 0x19, 0x2e11); + + switch (rt711->jd_src) { + case RT711_JD1: + /* default settings was already for JD1 */ + break; + case RT711_JD2: + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP | + RT711_HP_JD_SEL_JD2, + RT711_JD2_2PORT_200K_DECODE_HP | + RT711_HP_JD_SEL_JD2); + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_CC_DET1, + RT711_HP_JD_FINAL_RESULT_CTL_JD12, + RT711_HP_JD_FINAL_RESULT_CTL_JD12); + break; + case RT711_JD2_100K: + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_JD_CTL2, RT711_JD2_2PORT_100K_DECODE | RT711_JD2_1PORT_TYPE_DECODE | + RT711_HP_JD_SEL_JD2 | RT711_JD1_2PORT_TYPE_100K_DECODE, + RT711_JD2_2PORT_100K_DECODE_HP | RT711_JD2_1PORT_JD_HP | + RT711_HP_JD_SEL_JD2 | RT711_JD1_2PORT_JD_RESERVED); + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_CC_DET1, + RT711_HP_JD_FINAL_RESULT_CTL_JD12, + RT711_HP_JD_FINAL_RESULT_CTL_JD12); + break; + case RT711_JD2_1P8V_1PORT: + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_JD_CTL1, RT711_JD2_DIGITAL_JD_MODE_SEL, + RT711_JD2_1_JD_MODE); + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_JD_CTL2, RT711_JD2_1PORT_TYPE_DECODE | + RT711_HP_JD_SEL_JD2, + RT711_JD2_1PORT_JD_HP | + RT711_HP_JD_SEL_JD2); + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_JD_CTL4, RT711_JD2_PAD_PULL_UP_MASK | + RT711_JD2_MODE_SEL_MASK, + RT711_JD2_PAD_PULL_UP | + RT711_JD2_MODE2_1P8V_1PORT); + rt711_index_update_bits(rt711->regmap, RT711_VENDOR_REG, + RT711_CC_DET1, + RT711_HP_JD_FINAL_RESULT_CTL_JD12, + RT711_HP_JD_FINAL_RESULT_CTL_JD12); + break; + default: + dev_warn(rt711->component->dev, "Wrong JD source\n"); + break; + } + + dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__); + + mod_delayed_work(system_power_efficient_wq, + &rt711->jack_detect_work, msecs_to_jiffies(250)); + } else { + regmap_write(rt711->regmap, + RT711_SET_MIC2_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt711->regmap, + RT711_SET_HP_UNSOLICITED_ENABLE, 0x00); + regmap_write(rt711->regmap, + RT711_SET_INLINE_UNSOLICITED_ENABLE, 0x00); + + dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__); + } + + /* power off */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + mutex_unlock(&rt711->calibrate_mutex); +} + +static int rt711_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + int ret; + + rt711->hs_jack = hs_jack; + + /* we can only resume if the device was initialized at least once */ + if (!rt711->first_hw_init) + return 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + rt711_jack_init(rt711); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +static void rt711_get_gain(struct rt711_priv *rt711, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + /* R Channel */ + *r_val = (val_h << 8); + regmap_read(rt711->regmap, addr_l, r_val); + + /* L Channel */ + val_h |= 0x20; + *l_val = (val_h << 8); + regmap_read(rt711->regmap, addr_h, l_val); +} + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; + unsigned int read_ll, read_rl; + int i; + + mutex_lock(&rt711->calibrate_mutex); + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT711_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt711_get_gain(rt711, addr_h, addr_l, val_h, &read_rl, &read_ll); + + /* L Channel */ + if (mc->invert) { + /* for mute/unmute */ + val_ll = (mc->max - ucontrol->value.integer.value[0]) + << RT711_MUTE_SFT; + /* keep gain */ + read_ll = read_ll & 0x7f; + val_ll |= read_ll; + } else { + /* for gain */ + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; + /* keep mute status */ + read_ll = read_ll & (1 << RT711_MUTE_SFT); + val_ll |= read_ll; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* R Channel */ + if (mc->invert) { + /* for mute/unmute */ + val_lr = (mc->max - ucontrol->value.integer.value[1]) + << RT711_MUTE_SFT; + /* keep gain */ + read_rl = read_rl & 0x7f; + val_lr |= read_rl; + } else { + /* for gain */ + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; + /* keep mute status */ + read_rl = read_rl & (1 << RT711_MUTE_SFT); + val_lr |= read_rl; + } + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt711->regmap, + addr_h, (val_h << 8 | val_ll)); + regmap_write(rt711->regmap, + addr_l, (val_h << 8 | val_ll)); + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt711->regmap, + addr_h, (val_h << 8 | val_ll)); + + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt711->regmap, + addr_l, (val_h << 8 | val_lr)); + } + /* check result */ + if (mc->shift == RT711_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt711_get_gain(rt711, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + mutex_unlock(&rt711->calibrate_mutex); + return 0; +} + +static int rt711_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + /* switch to get command */ + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT711_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt711_get_gain(rt711, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (mc->invert) { + /* mute/unmute for switch controls */ + read_ll = !((read_ll & 0x80) >> RT711_MUTE_SFT); + read_rl = !((read_rl & 0x80) >> RT711_MUTE_SFT); + } else { + /* for gain volume controls */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt711_snd_controls[] = { + SOC_DOUBLE_R_EXT_TLV("DAC Surr Playback Volume", + RT711_SET_GAIN_DAC2_H, RT711_SET_GAIN_DAC2_L, + RT711_DIR_OUT_SFT, 0x57, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, out_vol_tlv), + SOC_DOUBLE_R_EXT("ADC 08 Capture Switch", + RT711_SET_GAIN_ADC2_H, RT711_SET_GAIN_ADC2_L, + RT711_DIR_IN_SFT, 1, 1, + rt711_set_amp_gain_get, rt711_set_amp_gain_put), + SOC_DOUBLE_R_EXT("ADC 09 Capture Switch", + RT711_SET_GAIN_ADC1_H, RT711_SET_GAIN_ADC1_L, + RT711_DIR_IN_SFT, 1, 1, + rt711_set_amp_gain_get, rt711_set_amp_gain_put), + SOC_DOUBLE_R_EXT_TLV("ADC 08 Capture Volume", + RT711_SET_GAIN_ADC2_H, RT711_SET_GAIN_ADC2_L, + RT711_DIR_IN_SFT, 0x3f, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("ADC 09 Capture Volume", + RT711_SET_GAIN_ADC1_H, RT711_SET_GAIN_ADC1_L, + RT711_DIR_IN_SFT, 0x3f, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, in_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("AMIC Volume", + RT711_SET_GAIN_AMIC_H, RT711_SET_GAIN_AMIC_L, + RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC1 Volume", + RT711_SET_GAIN_DMIC1_H, RT711_SET_GAIN_DMIC1_L, + RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC2 Volume", + RT711_SET_GAIN_DMIC2_H, RT711_SET_GAIN_DMIC2_L, + RT711_DIR_IN_SFT, 3, 0, + rt711_set_amp_gain_get, rt711_set_amp_gain_put, mic_vol_tlv), +}; + +static int rt711_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int reg, val = 0, nid; + int ret; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT711_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT711_MIXER_IN2; + else + return -EINVAL; + + /* vid = 0xf01 */ + reg = RT711_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt711->regmap, reg, &val); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); + return ret; + } + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt711_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, reg, nid; + int ret; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + nid = RT711_MIXER_IN1; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + nid = RT711_MIXER_IN2; + else + return -EINVAL; + + /* Verb ID = 0x701h */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + reg = RT711_VERB_SET_CONNECT_SEL | nid; + ret = regmap_read(rt711->regmap, reg, &val2); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); + return ret; + } + + if (val == val2) + change = 0; + else + change = 1; + + if (change) { + reg = RT711_VERB_SET_CONNECT_SEL | nid; + regmap_write(rt711->regmap, reg, val); + } + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", + "DMIC", +}; + +static SOC_ENUM_SINGLE_DECL( + rt711_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt711_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt711_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt711_adc22_enum, + rt711_mux_get, rt711_mux_put); + +static const struct snd_kcontrol_new rt711_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt711_adc23_enum, + rt711_mux_get, rt711_mux_put); + +static int rt711_dac_surround_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + unsigned int val_h = (1 << RT711_DIR_OUT_SFT) | (0x3 << 4); + unsigned int val_l; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_DAC2, 0x10); + + val_l = 0x00; + regmap_write(rt711->regmap, + RT711_SET_GAIN_HP_H, (val_h << 8 | val_l)); + break; + case SND_SOC_DAPM_PRE_PMD: + val_l = (1 << RT711_MUTE_SFT); + regmap_write(rt711->regmap, + RT711_SET_GAIN_HP_H, (val_h << 8 | val_l)); + usleep_range(50000, 55000); + + regmap_write(rt711->regmap, + RT711_SET_STREAMID_DAC2, 0x00); + break; + } + return 0; +} + +static int rt711_adc_09_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC1, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC1, 0x00); + break; + } + return 0; +} + +static int rt711_adc_08_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC2, 0x10); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt711->regmap, + RT711_SET_STREAMID_ADC2, 0x00); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt711_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + + SND_SOC_DAPM_DAC_E("DAC Surround", NULL, SND_SOC_NOPM, 0, 0, + rt711_dac_surround_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 09", NULL, SND_SOC_NOPM, 0, 0, + rt711_adc_09_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("ADC 08", NULL, SND_SOC_NOPM, 0, 0, + rt711_adc_08_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt711_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt711_adc23_mux), + + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt711_audio_map[] = { + {"DAC Surround", NULL, "DP3RX"}, + {"DP2TX", NULL, "ADC 09"}, + {"DP4TX", NULL, "ADC 08"}, + + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 22 Mux", "DMIC", "DMIC1"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "DMIC", "DMIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + + {"HP", NULL, "DAC Surround"}, +}; + +static int rt711_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, + AC_PWRST_D0); + } + break; + + case SND_SOC_BIAS_STANDBY: + mutex_lock(&rt711->calibrate_mutex); + regmap_write(rt711->regmap, + RT711_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + mutex_unlock(&rt711->calibrate_mutex); + break; + + default: + break; + } + + return 0; +} + +static int rt711_parse_dt(struct rt711_priv *rt711, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", + &rt711->jd_src); + + return 0; +} + +static int rt711_probe(struct snd_soc_component *component) +{ + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + int ret; + + rt711_parse_dt(rt711, &rt711->slave->dev); + rt711->component = component; + + if (!rt711->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_rt711 = { + .probe = rt711_probe, + .set_bias_level = rt711_set_bias_level, + .controls = rt711_snd_controls, + .num_controls = ARRAY_SIZE(rt711_snd_controls), + .dapm_widgets = rt711_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt711_dapm_widgets), + .dapm_routes = rt711_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt711_audio_map), + .set_jack = rt711_set_jack_detect, + .endianness = 1, +}; + +static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt711_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt711_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream; + int retval; + unsigned int val = 0; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt711->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + port_config.num = 3; + } else { + if (dai->id == RT711_AIF1) + port_config.num = 4; + else if (dai->id == RT711_AIF2) + port_config.num = 2; + else + return -EINVAL; + } + + retval = sdw_stream_add_slave(rt711->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + /* 48Khz */ + regmap_write(rt711->regmap, RT711_DAC_FORMAT_H, val); + regmap_write(rt711->regmap, RT711_ADC1_FORMAT_H, val); + regmap_write(rt711->regmap, RT711_ADC2_FORMAT_H, val); + + return retval; +} + +static int rt711_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt711->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt711->slave, sdw_stream); + return 0; +} + +#define RT711_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt711_ops = { + .hw_params = rt711_pcm_hw_params, + .hw_free = rt711_pcm_hw_free, + .set_stream = rt711_set_sdw_stream, + .shutdown = rt711_shutdown, +}; + +static struct snd_soc_dai_driver rt711_dai[] = { + { + .name = "rt711-aif1", + .id = RT711_AIF1, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .ops = &rt711_ops, + }, + { + .name = "rt711-aif2", + .id = RT711_AIF2, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT711_STEREO_RATES, + .formats = RT711_FORMATS, + }, + .ops = &rt711_ops, + } +}; + +/* Bus clock frequency */ +#define RT711_CLK_FREQ_9600000HZ 9600000 +#define RT711_CLK_FREQ_12000000HZ 12000000 +#define RT711_CLK_FREQ_6000000HZ 6000000 +#define RT711_CLK_FREQ_4800000HZ 4800000 +#define RT711_CLK_FREQ_2400000HZ 2400000 +#define RT711_CLK_FREQ_12288000HZ 12288000 + +int rt711_clock_config(struct device *dev) +{ + struct rt711_priv *rt711 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt711->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT711_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT711_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT711_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT711_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT711_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT711_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt711->regmap, 0xe0, value); + regmap_write(rt711->regmap, 0xf0, value); + + dev_dbg(dev, "%s complete, clk_freq=%d\n", __func__, clk_freq); + + return 0; +} + +static void rt711_calibration_work(struct work_struct *work) +{ + struct rt711_priv *rt711 = + container_of(work, struct rt711_priv, calibration_work); + + rt711_calibration(rt711); +} + +int rt711_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave) +{ + struct rt711_priv *rt711; + int ret; + + rt711 = devm_kzalloc(dev, sizeof(*rt711), GFP_KERNEL); + if (!rt711) + return -ENOMEM; + + dev_set_drvdata(dev, rt711); + rt711->slave = slave; + rt711->sdw_regmap = sdw_regmap; + rt711->regmap = regmap; + + regcache_cache_only(rt711->regmap, true); + + mutex_init(&rt711->calibrate_mutex); + mutex_init(&rt711->disable_irq_lock); + + INIT_DELAYED_WORK(&rt711->jack_detect_work, rt711_jack_detect_handler); + INIT_DELAYED_WORK(&rt711->jack_btn_check_work, rt711_btn_check_handler); + INIT_WORK(&rt711->calibration_work, rt711_calibration_work); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt711->hw_init = false; + rt711->first_hw_init = false; + + /* JD source uses JD2 in default */ + rt711->jd_src = RT711_JD2; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt711, + rt711_dai, + ARRAY_SIZE(rt711_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return ret; +} + +int rt711_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt711_priv *rt711 = dev_get_drvdata(dev); + + rt711->disable_irq = false; + + if (rt711->hw_init) + return 0; + + regcache_cache_only(rt711->regmap, false); + if (rt711->first_hw_init) + regcache_cache_bypass(rt711->regmap, true); + + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + if (!rt711->first_hw_init) + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + pm_runtime_get_noresume(&slave->dev); + + rt711_reset(rt711->regmap); + + /* power on */ + regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* Set Pin Widget */ + regmap_write(rt711->regmap, RT711_SET_PIN_MIC2, 0x25); + regmap_write(rt711->regmap, RT711_SET_PIN_HP, 0xc0); + regmap_write(rt711->regmap, RT711_SET_PIN_DMIC1, 0x20); + regmap_write(rt711->regmap, RT711_SET_PIN_DMIC2, 0x20); + regmap_write(rt711->regmap, RT711_SET_PIN_LINE1, 0x20); + regmap_write(rt711->regmap, RT711_SET_PIN_LINE2, 0x20); + + /* Mute HP/ADC1/ADC2 */ + regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, 0xa080); + regmap_write(rt711->regmap, RT711_SET_GAIN_HP_H, 0x9080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC2_H, 0x6080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC2_H, 0x5080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC1_H, 0x6080); + regmap_write(rt711->regmap, RT711_SET_GAIN_ADC1_H, 0x5080); + + /* Set Configuration Default */ + regmap_write(rt711->regmap, 0x4f12, 0x91); + regmap_write(rt711->regmap, 0x4e12, 0xd6); + regmap_write(rt711->regmap, 0x4d12, 0x11); + regmap_write(rt711->regmap, 0x4c12, 0x20); + regmap_write(rt711->regmap, 0x4f13, 0x91); + regmap_write(rt711->regmap, 0x4e13, 0xd6); + regmap_write(rt711->regmap, 0x4d13, 0x11); + regmap_write(rt711->regmap, 0x4c13, 0x21); + regmap_write(rt711->regmap, 0x4c21, 0xf0); + regmap_write(rt711->regmap, 0x4d21, 0x11); + regmap_write(rt711->regmap, 0x4e21, 0x11); + regmap_write(rt711->regmap, 0x4f21, 0x01); + + /* Data port arrangement */ + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_TX_RX_MUX_CTL, 0x0154); + + /* Set index */ + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_DIGITAL_MISC_CTRL4, 0x201b); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_COMBO_JACK_AUTO_CTL1, 0x5089); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_VREFOUT_CTL, 0x5064); + rt711_index_write(rt711->regmap, RT711_VENDOR_REG, + RT711_INLINE_CMD_CTL, 0xd249); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt711->regmap, RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + if (rt711->first_hw_init) + rt711_calibration(rt711); + else + schedule_work(&rt711->calibration_work); + + /* + * if set_jack callback occurred early than io_init, + * we set up the jack detection function now + */ + if (rt711->hs_jack) + rt711_jack_init(rt711); + + if (rt711->first_hw_init) { + regcache_cache_bypass(rt711->regmap, false); + regcache_mark_dirty(rt711->regmap); + } else + rt711->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt711->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT711 SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h new file mode 100644 index 0000000000..491e357191 --- /dev/null +++ b/sound/soc/codecs/rt711.h @@ -0,0 +1,251 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt711.h -- RT711 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT711_H__ +#define __RT711_H__ + +extern const struct dev_pm_ops rt711_runtime_pm; + +struct rt711_priv { + struct regmap *regmap; + struct regmap *sdw_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + struct work_struct calibration_work; + struct mutex calibrate_mutex; /* for headset calibration */ + int jack_type, jd_src; + struct mutex disable_irq_lock; /* imp-def irq lock protection */ + bool disable_irq; +}; + +/* NID */ +#define RT711_AUDIO_FUNCTION_GROUP 0x01 +#define RT711_DAC_OUT2 0x03 +#define RT711_ADC_IN1 0x09 +#define RT711_ADC_IN2 0x08 +#define RT711_DMIC1 0x12 +#define RT711_DMIC2 0x13 +#define RT711_MIC2 0x19 +#define RT711_LINE1 0x1a +#define RT711_LINE2 0x1b +#define RT711_BEEP 0x1d +#define RT711_VENDOR_REG 0x20 +#define RT711_HP_OUT 0x21 +#define RT711_MIXER_IN1 0x22 +#define RT711_MIXER_IN2 0x23 +#define RT711_INLINE_CMD 0x55 +#define RT711_VENDOR_CALI 0x58 +#define RT711_VENDOR_IMS_DRE 0x5b + +/* Index (NID:20h) */ +#define RT711_DAC_DC_CALI_CTL1 0x00 +#define RT711_JD_CTL1 0x08 +#define RT711_JD_CTL2 0x09 +#define RT711_JD_CTL4 0x0b +#define RT711_CC_DET1 0x11 +#define RT711_PARA_VERB_CTL 0x1a +#define RT711_COMBO_JACK_AUTO_CTL1 0x45 +#define RT711_COMBO_JACK_AUTO_CTL2 0x46 +#define RT711_INLINE_CMD_CTL 0x48 +#define RT711_DIGITAL_MISC_CTRL4 0x4a +#define RT711_VREFOUT_CTL 0x6b +#define RT711_FSM_CTL 0x6f +#define RT711_IRQ_FLAG_TABLE1 0x80 +#define RT711_IRQ_FLAG_TABLE2 0x81 +#define RT711_IRQ_FLAG_TABLE3 0x82 +#define RT711_TX_RX_MUX_CTL 0x91 + +/* Index (NID:5bh) */ +#define RT711_IMS_DIGITAL_CTL1 0x00 +#define RT711_HP_IMS_RESULT_L 0x20 +#define RT711_HP_IMS_RESULT_R 0x21 + +/* Verb */ +#define RT711_VERB_SET_CONNECT_SEL 0x3100 +#define RT711_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT711_VERB_GET_CONNECT_SEL 0xb100 +#define RT711_VERB_SET_POWER_STATE 0x3500 +#define RT711_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT711_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT711_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT711_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT711_SET_AMP_GAIN_MUTE_L 0x8380 +#define RT711_VERB_GET_POWER_STATE 0xb500 +#define RT711_VERB_GET_CHANNEL_STREAMID 0xb600 +#define RT711_VERB_GET_PIN_SENSE 0xb900 +#define RT711_FUNC_RESET 0xff01 + +#define RT711_READ_HDA_3 0x2012 +#define RT711_READ_HDA_2 0x2013 +#define RT711_READ_HDA_1 0x2014 +#define RT711_READ_HDA_0 0x2015 +#define RT711_PRIV_INDEX_W_H 0x7500 +#define RT711_PRIV_INDEX_W_L 0x8580 +#define RT711_PRIV_DATA_W_H 0x7400 +#define RT711_PRIV_DATA_W_L 0x8480 +#define RT711_PRIV_INDEX_R_H 0x9d00 +#define RT711_PRIV_INDEX_R_L 0xad80 +#define RT711_PRIV_DATA_R_H 0x9c00 +#define RT711_PRIV_DATA_R_L 0xac80 +#define RT711_DAC_FORMAT_H 0x7203 +#define RT711_DAC_FORMAT_L 0x8283 +#define RT711_ADC1_FORMAT_H 0x7209 +#define RT711_ADC1_FORMAT_L 0x8289 +#define RT711_ADC2_FORMAT_H 0x7208 +#define RT711_ADC2_FORMAT_L 0x8288 + +#define RT711_SET_AUDIO_POWER_STATE\ + (RT711_VERB_SET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP) +#define RT711_GET_AUDIO_POWER_STATE\ + (RT711_VERB_GET_POWER_STATE | RT711_AUDIO_FUNCTION_GROUP) +#define RT711_SET_PIN_DMIC1\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC1) +#define RT711_SET_PIN_DMIC2\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_DMIC2) +#define RT711_SET_PIN_HP\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_HP_OUT) +#define RT711_SET_PIN_MIC2\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_MIC2) +#define RT711_SET_PIN_LINE1\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE1) +#define RT711_SET_PIN_LINE2\ + (RT711_VERB_SET_PIN_WIDGET_CONTROL | RT711_LINE2) +#define RT711_SET_MIC2_UNSOLICITED_ENABLE\ + (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_MIC2) +#define RT711_SET_HP_UNSOLICITED_ENABLE\ + (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_HP_OUT) +#define RT711_SET_INLINE_UNSOLICITED_ENABLE\ + (RT711_VERB_SET_UNSOLICITED_ENABLE | RT711_INLINE_CMD) +#define RT711_SET_STREAMID_DAC2\ + (RT711_VERB_SET_CHANNEL_STREAMID | RT711_DAC_OUT2) +#define RT711_SET_STREAMID_ADC1\ + (RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN1) +#define RT711_SET_STREAMID_ADC2\ + (RT711_VERB_SET_CHANNEL_STREAMID | RT711_ADC_IN2) +#define RT711_GET_STREAMID_DAC2\ + (RT711_VERB_GET_CHANNEL_STREAMID | RT711_DAC_OUT2) +#define RT711_GET_STREAMID_ADC1\ + (RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN1) +#define RT711_GET_STREAMID_ADC2\ + (RT711_VERB_GET_CHANNEL_STREAMID | RT711_ADC_IN2) +#define RT711_SET_GAIN_DAC2_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_DAC_OUT2) +#define RT711_SET_GAIN_DAC2_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_DAC_OUT2) +#define RT711_SET_GAIN_ADC1_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN1) +#define RT711_SET_GAIN_ADC1_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN1) +#define RT711_SET_GAIN_ADC2_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_ADC_IN2) +#define RT711_SET_GAIN_ADC2_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_ADC_IN2) +#define RT711_SET_GAIN_AMIC_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_MIC2) +#define RT711_SET_GAIN_AMIC_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_MIC2) +#define RT711_SET_GAIN_DMIC1_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC1) +#define RT711_SET_GAIN_DMIC1_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC1) +#define RT711_SET_GAIN_DMIC2_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_DMIC2) +#define RT711_SET_GAIN_DMIC2_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_DMIC2) +#define RT711_SET_GAIN_HP_L\ + (RT711_SET_AMP_GAIN_MUTE_L | RT711_HP_OUT) +#define RT711_SET_GAIN_HP_H\ + (RT711_SET_AMP_GAIN_MUTE_H | RT711_HP_OUT) + +/* DAC DC offset calibration control-1 (0x00)(NID:20h) */ +#define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15) + +/* jack detect control 1 (0x08)(NID:20h) */ +#define RT711_JD2_DIGITAL_JD_MODE_SEL (0x1 << 1) +#define RT711_JD2_1_JD_MODE (0x0 << 1) +#define RT711_JD2_2_JD_MODE (0x1 << 1) + +/* jack detect control 2 (0x09)(NID:20h) */ +#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13) +#define RT711_JD2_2PORT_100K_DECODE (0x1 << 12) +#define RT711_JD2_2PORT_100K_DECODE_HP (0x0 << 12) +#define RT711_HP_JD_SEL_JD1 (0x0 << 1) +#define RT711_HP_JD_SEL_JD2 (0x1 << 1) +#define RT711_JD2_1PORT_TYPE_DECODE (0x3 << 10) +#define RT711_JD2_1PORT_JD_LINE2 (0x0 << 10) +#define RT711_JD2_1PORT_JD_HP (0x1 << 10) +#define RT711_JD2_1PORT_JD_LINE1 (0x2 << 10) +#define RT711_JD1_2PORT_TYPE_100K_DECODE (0x1 << 0) +#define RT711_JD1_2PORT_JD_RESERVED (0x0 << 0) +#define RT711_JD1_2PORT_JD_LINE1 (0x1 << 0) + +/* jack detect control 4 (0x0b)(NID:20h) */ +#define RT711_JD2_PAD_PULL_UP_MASK (0x1 << 3) +#define RT711_JD2_PAD_NOT_PULL_UP (0x0 << 3) +#define RT711_JD2_PAD_PULL_UP (0x1 << 3) +#define RT711_JD2_MODE_SEL_MASK (0x3 << 0) +#define RT711_JD2_MODE0_2PORT (0x0 << 0) +#define RT711_JD2_MODE1_3P3V_1PORT (0x1 << 0) +#define RT711_JD2_MODE2_1P8V_1PORT (0x2 << 0) + +/* CC DET1 (0x11)(NID:20h) */ +#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10) +#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10) + +/* Parameter & Verb control (0x1a)(NID:20h) */ +#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT711_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT711_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT711_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT711_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +/* FSM control (0x6f)(NID:20h) */ +#define RT711_CALI_CTL (0x0 << 0) +#define RT711_COMBOJACK_CTL (0x1 << 0) +#define RT711_IMS_CTL (0x2 << 0) +#define RT711_DEPOP_CTL (0x3 << 0) + +/* Impedance Sense Digital Control 1 (0x00)(NID:5bh) */ +#define RT711_TRIGGER_IMS (0x1 << 15) +#define RT711_IMS_EN (0x1 << 6) + +#define RT711_EAPD_HIGH 0x2 +#define RT711_EAPD_LOW 0x0 +#define RT711_MUTE_SFT 7 +/* set input/output mapping to payload[14][15] separately */ +#define RT711_DIR_IN_SFT 6 +#define RT711_DIR_OUT_SFT 7 + +enum { + RT711_AIF1, + RT711_AIF2, + RT711_AIFS, +}; + +enum rt711_jd_src { + RT711_JD_NULL, + RT711_JD1, + RT711_JD2, + RT711_JD2_100K, + RT711_JD2_1P8V_1PORT +}; + +int rt711_io_init(struct device *dev, struct sdw_slave *slave); +int rt711_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +int rt711_jack_detect(struct rt711_priv *rt711, bool *hp, bool *mic); +int rt711_clock_config(struct device *dev); +#endif /* __RT711_H__ */ diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c new file mode 100644 index 0000000000..ba08d03e71 --- /dev/null +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -0,0 +1,991 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt712-sdca-dmic.c -- rt712 SDCA DMIC ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt712-sdca.h" +#include "rt712-sdca-dmic.h" + +static bool rt712_sdca_dmic_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201a ... 0x201f: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2230 ... 0x2232: + case 0x2f01 ... 0x2f0a: + case 0x2f35 ... 0x2f36: + case 0x2f52: + case 0x2f58 ... 0x2f59: + case 0x3201: + case 0x320c: + return true; + default: + return false; + } +} + +static bool rt712_sdca_dmic_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x202d ... 0x202f: + case 0x2230: + case 0x2f01: + case 0x2f35: + case 0x320c: + return true; + default: + return false; + } +} + +static bool rt712_sdca_dmic_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000 ... 0x200008e: + case 0x5300000 ... 0x530000e: + case 0x5400000 ... 0x540000e: + case 0x5600000 ... 0x5600008: + case 0x5700000 ... 0x570000d: + case 0x5800000 ... 0x5800021: + case 0x5900000 ... 0x5900028: + case 0x5a00000 ... 0x5a00009: + case 0x5b00000 ... 0x5b00051: + case 0x5c00000 ... 0x5c0009a: + case 0x5d00000 ... 0x5d00009: + case 0x5f00000 ... 0x5f00030: + case 0x6100000 ... 0x6100068: + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04): + return true; + default: + return false; + } +} + +static bool rt712_sdca_dmic_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + case 0x200001a: + case 0x2000024: + case 0x2000046: + case 0x200008a: + case 0x5800000: + case 0x5800001: + case 0x6100008: + return true; + default: + return false; + } +} + +static const struct regmap_config rt712_sdca_dmic_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt712_sdca_dmic_readable_register, + .volatile_reg = rt712_sdca_dmic_volatile_register, + .max_register = 0x40981300, + .reg_defaults = rt712_sdca_dmic_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt712_sdca_dmic_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt712_sdca_dmic_mbq_readable_register, + .volatile_reg = rt712_sdca_dmic_mbq_volatile_register, + .max_register = 0x40800f14, + .reg_defaults = rt712_sdca_dmic_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_mbq_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt712_sdca_dmic_index_write(struct rt712_sdca_dmic_priv *rt712, + unsigned int nid, unsigned int reg, unsigned int value) +{ + int ret; + struct regmap *regmap = rt712->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt712->slave->dev, + "Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt712_sdca_dmic_index_read(struct rt712_sdca_dmic_priv *rt712, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + struct regmap *regmap = rt712->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt712->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt712_sdca_dmic_index_update_bits(struct rt712_sdca_dmic_priv *rt712, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt712_sdca_dmic_index_read(rt712, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + return rt712_sdca_dmic_index_write(rt712, nid, reg, tmp); +} + +static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev); + + if (rt712->hw_init) + return 0; + + regcache_cache_only(rt712->regmap, false); + regcache_cache_only(rt712->mbq_regmap, false); + if (rt712->first_hw_init) { + regcache_cache_bypass(rt712->regmap, true); + regcache_cache_bypass(rt712->mbq_regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_ADC0A_08_PDE_FLOAT_CTL, 0x1112); + rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_ADC0B_11_PDE_FLOAT_CTL, 0x1111); + rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_DMIC1_2_PDE_FLOAT_CTL, 0x1111); + rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_I2S_IN_OUT_PDE_FLOAT_CTL, 0x1155); + rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_DMIC_ENT_FLOAT_CTL, 0x2626); + rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_ADC_ENT_FLOAT_CTL, 0x1e19); + rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515); + rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_ADC_VOL_CH_FLOAT_CTL2, 0x0304); + rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304); + rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_CONFIG_CTL0, 0x0050); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01); + rt712_sdca_dmic_index_write(rt712, RT712_ULTRA_SOUND_DET, + RT712_ULTRA_SOUND_DETECTOR6, 0x3200); + regmap_write(rt712->regmap, RT712_RC_CAL, 0x23); + regmap_write(rt712->regmap, 0x2f52, 0x00); + + if (rt712->first_hw_init) { + regcache_cache_bypass(rt712->regmap, false); + regcache_mark_dirty(rt712->regmap); + regcache_cache_bypass(rt712->mbq_regmap, false); + regcache_mark_dirty(rt712->mbq_regmap); + } else + rt712->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt712->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct rt712_sdca_dmic_kctrl_priv *p = + (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int regvalue, ctl, i; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt712->mbq_regmap, p->reg_base + i, ®value); + + if (!adc_vol_flag) /* boost gain */ + ctl = regvalue / 0x0a00; + else { /* ADC gain */ + if (adc_vol_flag) + ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset); + else + ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset); + } + + ucontrol->value.integer.value[i] = ctl; + } + + return 0; +} + +static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_dmic_kctrl_priv *p = + (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned int gain_val[4]; + unsigned int i, adc_vol_flag = 0, changed = 0; + unsigned int regvalue[4]; + const unsigned int interval_offset = 0xc0; + int err; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt712->mbq_regmap, p->reg_base + i, ®value[i]); + + gain_val[i] = ucontrol->value.integer.value[i]; + if (gain_val[i] > p->max) + gain_val[i] = p->max; + + if (!adc_vol_flag) /* boost gain */ + gain_val[i] = gain_val[i] * 0x0a00; + else { /* ADC gain */ + gain_val[i] = 0x1e00 - ((p->max - gain_val[i]) * interval_offset); + gain_val[i] &= 0xffff; + } + + if (regvalue[i] != gain_val[i]) + changed = 1; + } + + if (!changed) + return 0; + + for (i = 0; i < p->count; i++) { + err = regmap_write(rt712->mbq_regmap, p->reg_base + i, gain_val[i]); + if (err < 0) + dev_err(&rt712->slave->dev, "0x%08x can't be set\n", p->reg_base + i); + } + + return changed; +} + +static int rt712_sdca_set_fu1e_capture_ctl(struct rt712_sdca_dmic_priv *rt712) +{ + int err, i; + unsigned int ch_mute; + + for (i = 0; i < ARRAY_SIZE(rt712->fu1e_mixer_mute); i++) { + ch_mute = (rt712->fu1e_dapm_mute || rt712->fu1e_mixer_mute[i]) ? 0x01 : 0x00; + err = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, + RT712_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute); + if (err < 0) + return err; + } + + return 0; +} + +static int rt712_sdca_dmic_fu1e_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); + struct rt712_sdca_dmic_kctrl_priv *p = + (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int i; + + for (i = 0; i < p->count; i++) + ucontrol->value.integer.value[i] = !rt712->fu1e_mixer_mute[i]; + + return 0; +} + +static int rt712_sdca_dmic_fu1e_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); + struct rt712_sdca_dmic_kctrl_priv *p = + (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value; + int err, changed = 0, i; + + for (i = 0; i < p->count; i++) { + if (rt712->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i]) + changed = 1; + rt712->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i]; + } + + err = rt712_sdca_set_fu1e_capture_ctl(rt712); + if (err < 0) + return err; + + return changed; +} + +static int rt712_sdca_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt712_sdca_dmic_kctrl_priv *p = + (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +#define RT712_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \ + ((unsigned long)&(struct rt712_sdca_dmic_kctrl_priv) \ + {.reg_base = xreg_base, .count = xcount, .max = xmax, \ + .invert = xinvert}) + +#define RT712_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = rt712_sdca_fu_info, \ + .get = rt712_sdca_dmic_fu1e_capture_get, \ + .put = rt712_sdca_dmic_fu1e_capture_put, \ + .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)} + +#define RT712_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\ + xhandler_put, xcount, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt712_sdca_fu_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) } + +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt712_sdca_dmic_snd_controls[] = { + RT712_SDCA_FU_CTRL("FU1E Capture Switch", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), + 1, 1, 4), + RT712_SDCA_EXT_TLV("FU1E Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), + rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 0x3f, in_vol_tlv), + RT712_SDCA_EXT_TLV("FU15 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), + rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 3, mic_vol_tlv), +}; + +static int rt712_sdca_dmic_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask_sft; + + if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 8; + else if (strstr(ucontrol->id.name, "ADC 26 Mux")) + mask_sft = 4; + else + return -EINVAL; + + rt712_sdca_dmic_index_read(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_MUX_CTL0, &val); + + ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7; + + return 0; +} + +static int rt712_sdca_dmic_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 8; + else if (strstr(ucontrol->id.name, "ADC 26 Mux")) + mask_sft = 4; + else + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + rt712_sdca_dmic_index_read(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_MUX_CTL0, &val2); + val2 = (0x7 << mask_sft) & val2; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) + rt712_sdca_dmic_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_MUX_CTL0, 0x7 << mask_sft, + val << mask_sft); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_mux_text[] = { + "DMIC1", + "DMIC2", +}; + +static SOC_ENUM_SINGLE_DECL( + rt712_adc25_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt712_adc26_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt712_sdca_dmic_adc25_mux = + SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt712_adc25_enum, + rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put); + +static const struct snd_kcontrol_new rt712_sdca_dmic_adc26_mux = + SOC_DAPM_ENUM_EXT("ADC 26 Mux", rt712_adc26_enum, + rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put); + +static int rt712_sdca_dmic_fu1e_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt712->fu1e_dapm_mute = false; + rt712_sdca_set_fu1e_capture_ctl(rt712); + break; + case SND_SOC_DAPM_PRE_PMD: + rt712->fu1e_dapm_mute = true; + rt712_sdca_set_fu1e_capture_ctl(rt712); + break; + } + return 0; +} + +static int rt712_sdca_dmic_pde11_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt712_sdca_dmic_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + + SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, + rt712_sdca_dmic_pde11_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0, + rt712_sdca_dmic_fu1e_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, + &rt712_sdca_dmic_adc25_mux), + SND_SOC_DAPM_MUX("ADC 26 Mux", SND_SOC_NOPM, 0, 0, + &rt712_sdca_dmic_adc26_mux), + + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt712_sdca_dmic_audio_map[] = { + {"DP2TX", NULL, "FU 1E"}, + + {"FU 1E", NULL, "PDE 11"}, + {"FU 1E", NULL, "ADC 25 Mux"}, + {"FU 1E", NULL, "ADC 26 Mux"}, + {"ADC 25 Mux", "DMIC1", "DMIC1"}, + {"ADC 25 Mux", "DMIC2", "DMIC2"}, + {"ADC 26 Mux", "DMIC1", "DMIC1"}, + {"ADC 26 Mux", "DMIC2", "DMIC2"}, +}; + +static int rt712_sdca_dmic_probe(struct snd_soc_component *component) +{ + struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); + int ret; + + rt712->component = component; + + if (!rt712->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt712_dmic = { + .probe = rt712_sdca_dmic_probe, + .controls = rt712_sdca_dmic_snd_controls, + .num_controls = ARRAY_SIZE(rt712_sdca_dmic_snd_controls), + .dapm_widgets = rt712_sdca_dmic_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dmic_dapm_widgets), + .dapm_routes = rt712_sdca_dmic_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt712_sdca_dmic_audio_map), + .endianness = 1, +}; + +static int rt712_sdca_dmic_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt712_sdca_dmic_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt712_sdca_dmic_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + struct sdw_stream_runtime *sdw_stream; + int retval, num_channels; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt712->slave) + return -EINVAL; + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = SDW_DATA_DIR_TX; + + num_channels = params_channels(params); + port_config.ch_mask = GENMASK(num_channels - 1, 0); + port_config.num = 2; + + retval = sdw_stream_add_slave(rt712->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) > 4) { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 16000: + sampling_rate = RT712_SDCA_RATE_16000HZ; + break; + case 32000: + sampling_rate = RT712_SDCA_RATE_32000HZ; + break; + case 44100: + sampling_rate = RT712_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT712_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT712_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT712_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + + return 0; +} + +static int rt712_sdca_dmic_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt712->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt712->slave, sdw_stream); + return 0; +} + +#define RT712_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT712_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt712_sdca_dmic_ops = { + .hw_params = rt712_sdca_dmic_hw_params, + .hw_free = rt712_sdca_dmic_hw_free, + .set_stream = rt712_sdca_dmic_set_sdw_stream, + .shutdown = rt712_sdca_dmic_shutdown, +}; + +static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = { + { + .name = "rt712-sdca-dmic-aif1", + .id = RT712_AIF1, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = RT712_STEREO_RATES, + .formats = RT712_FORMATS, + }, + .ops = &rt712_sdca_dmic_ops, + }, +}; + +static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt712_sdca_dmic_priv *rt712; + int ret; + + rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL); + if (!rt712) + return -ENOMEM; + + dev_set_drvdata(dev, rt712); + rt712->slave = slave; + rt712->regmap = regmap; + rt712->mbq_regmap = mbq_regmap; + + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt712->hw_init = false; + rt712->first_hw_init = false; + rt712->fu1e_dapm_mute = true; + rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] = + rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true; + + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt712_dmic, + rt712_sdca_dmic_dai, + ARRAY_SIZE(rt712_sdca_dmic_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + + +static int rt712_sdca_dmic_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt712->hw_init = false; + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt712->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt712_sdca_dmic_io_init(&slave->dev, slave); +} + +static int rt712_sdca_dmic_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = BIT(2); /* BITMAP: 00000100 */ + prop->sink_ports = 0; + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 200; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static const struct sdw_device_id rt712_sdca_dmic_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1712, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1713, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1716, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1717, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt712_sdca_dmic_id); + +static int __maybe_unused rt712_sdca_dmic_dev_suspend(struct device *dev) +{ + struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev); + + if (!rt712->hw_init) + return 0; + + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + + return 0; +} + +static int __maybe_unused rt712_sdca_dmic_dev_system_suspend(struct device *dev) +{ + struct rt712_sdca_dmic_priv *rt712_sdca = dev_get_drvdata(dev); + + if (!rt712_sdca->hw_init) + return 0; + + return rt712_sdca_dmic_dev_suspend(dev); +} + +#define RT712_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt712_sdca_dmic_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt712->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT712_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt712->regmap, false); + regcache_sync(rt712->regmap); + regcache_cache_only(rt712->mbq_regmap, false); + regcache_sync(rt712->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt712_sdca_dmic_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt712_sdca_dmic_dev_system_suspend, rt712_sdca_dmic_dev_resume) + SET_RUNTIME_PM_OPS(rt712_sdca_dmic_dev_suspend, rt712_sdca_dmic_dev_resume, NULL) +}; + + +static struct sdw_slave_ops rt712_sdca_dmic_slave_ops = { + .read_prop = rt712_sdca_dmic_read_prop, + .update_status = rt712_sdca_dmic_update_status, +}; + +static int rt712_sdca_dmic_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt712_sdca_dmic_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt712_sdca_dmic_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt712_sdca_dmic_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt712_sdca_dmic_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); + + return 0; +} + +static struct sdw_driver rt712_sdca_dmic_sdw_driver = { + .driver = { + .name = "rt712-sdca-dmic", + .owner = THIS_MODULE, + .pm = &rt712_sdca_dmic_pm, + }, + .probe = rt712_sdca_dmic_sdw_probe, + .remove = rt712_sdca_dmic_sdw_remove, + .ops = &rt712_sdca_dmic_slave_ops, + .id_table = rt712_sdca_dmic_id, +}; +module_sdw_driver(rt712_sdca_dmic_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT712 SDCA DMIC SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h new file mode 100644 index 0000000000..110154e74e --- /dev/null +++ b/sound/soc/codecs/rt712-sdca-dmic.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt712-sdca-dmic.h -- RT712 SDCA DMIC ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT712_SDW_DMIC_H__ +#define __RT712_SDW_DMIC_H__ + +#include +#include + +struct rt712_sdca_dmic_priv { + struct regmap *regmap; + struct regmap *mbq_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + bool fu1e_dapm_mute; + bool fu1e_mixer_mute[4]; +}; + +struct rt712_sdca_dmic_kctrl_priv { + unsigned int reg_base; + unsigned int count; + unsigned int max; + unsigned int invert; +}; + +/* SDCA (Channel) */ +#define CH_01 0x01 +#define CH_02 0x02 +#define CH_03 0x03 +#define CH_04 0x04 + +static const struct reg_default rt712_sdca_dmic_reg_defaults[] = { + { 0x201a, 0x00 }, + { 0x201b, 0x00 }, + { 0x201c, 0x00 }, + { 0x201d, 0x00 }, + { 0x201e, 0x00 }, + { 0x201f, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x2f }, + { 0x2232, 0x80 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x01 }, + { 0x2f35, 0x02 }, + { 0x2f36, 0xcf }, + { 0x2f52, 0x08 }, + { 0x2f58, 0x07 }, + { 0x2f59, 0x07 }, + { 0x3201, 0x01 }, + { 0x320c, 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, +}; + +static const struct reg_default rt712_sdca_dmic_mbq_defaults[] = { + { 0x0590001e, 0x0020 }, + { 0x06100000, 0x0010 }, + { 0x06100006, 0x0055 }, + { 0x06100010, 0x2630 }, + { 0x06100011, 0x152f }, + { 0x06100013, 0x0102 }, + { 0x06100015, 0x2219 }, + { 0x06100018, 0x0102 }, + { 0x06100026, 0x2c29 }, + { 0x06100027, 0x2d2b }, + { 0x0610002b, 0x2a32 }, + { 0x0610002f, 0x3355 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04), 0x0000 }, +}; + +#endif /* __RT712_SDW_DMIC_H__ */ diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c new file mode 100644 index 0000000000..6b644a89c5 --- /dev/null +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt712-sdca-sdw.c -- rt712 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include "rt712-sdca.h" +#include "rt712-sdca-sdw.h" + +static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201a ... 0x201f: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2230 ... 0x2232: + case 0x2f01 ... 0x2f0a: + case 0x2f35 ... 0x2f36: + case 0x2f50: + case 0x2f54: + case 0x2f58 ... 0x2f5d: + case 0x3201: + case 0x320c: + case 0x3301 ... 0x3303: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x202d ... 0x202f: + case 0x2230: + case 0x2f01: + case 0x2f35: + case 0x320c: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt712_sdca_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000 ... 0x200008e: + case 0x5300000 ... 0x530000e: + case 0x5400000 ... 0x540000e: + case 0x5600000 ... 0x5600008: + case 0x5700000 ... 0x570000d: + case 0x5800000 ... 0x5800021: + case 0x5900000 ... 0x5900028: + case 0x5a00000 ... 0x5a00009: + case 0x5b00000 ... 0x5b00051: + case 0x5c00000 ... 0x5c0009a: + case 0x5d00000 ... 0x5d00009: + case 0x5f00000 ... 0x5f00030: + case 0x6100000 ... 0x6100068: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R): + return true; + default: + return false; + } +} + +static bool rt712_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + case 0x200001a: + case 0x2000024: + case 0x2000046: + case 0x200008a: + case 0x5800000: + case 0x5800001: + case 0x6100008: + return true; + default: + return false; + } +} + +static const struct regmap_config rt712_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt712_sdca_readable_register, + .volatile_reg = rt712_sdca_volatile_register, + .max_register = 0x44ffffff, + .reg_defaults = rt712_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt712_sdca_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt712_sdca_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt712_sdca_mbq_readable_register, + .volatile_reg = rt712_sdca_mbq_volatile_register, + .max_register = 0x41000312, + .reg_defaults = rt712_sdca_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt712_sdca_mbq_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt712_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt712->hw_init = false; + + if (status == SDW_SLAVE_ATTACHED) { + if (rt712->hs_jack) { + /* + * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then + * if the device attached again, we will need to set the setting back. + * It could avoid losing the jack detection interrupt. + * This also could sync with the cache value as the rt712_sdca_jack_init set. + */ + sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + } + } + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt712->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt712_sdca_io_init(&slave->dev, slave); +} + +static int rt712_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = BIT(4); /* BITMAP: 00010000 */ + prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 1380; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt712_sdca_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); + int ret, stat; + int count = 0, retry = 3; + unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0; + + dev_dbg(&slave->dev, + "%s control_port_stat=%x, sdca_cascade=%x", __func__, + status->control_port, status->sdca_cascade); + + if (cancel_delayed_work_sync(&rt712->jack_detect_work)) { + dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__); + /* avoid the HID owner doesn't change to device */ + if (rt712->scp_sdca_stat2) + scp_sdca_stat2 = rt712->scp_sdca_stat2; + } + + /* + * The critical section below intentionally protects a rather large piece of code. + * We don't want to allow the system suspend to disable an interrupt while we are + * processing it, which could be problematic given the quirky SoundWire interrupt + * scheme. We do want however to prevent new workqueues from being scheduled if + * the disable_irq flag was set during system suspend. + */ + mutex_lock(&rt712->disable_irq_lock); + + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + rt712->scp_sdca_stat1 = ret; + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + rt712->scp_sdca_stat2 = ret; + if (scp_sdca_stat2) + rt712->scp_sdca_stat2 |= scp_sdca_stat2; + + do { + /* clear flag */ + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) { + ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INTMASK_SDCA_0); + if (ret < 0) + goto io_error; + } + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) { + ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + if (ret < 0) + goto io_error; + } + + /* check if flag clear or not */ + ret = sdw_read_no_pm(rt712->slave, SDW_DP0_INT); + if (ret < 0) + goto io_error; + sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; + + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0; + + ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8; + + stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade; + + count++; + } while (stat != 0 && count < retry); + + if (stat) + dev_warn(&slave->dev, + "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt712->scp_sdca_stat1, rt712->scp_sdca_stat2); + + if (status->sdca_cascade && !rt712->disable_irq) + mod_delayed_work(system_power_efficient_wq, + &rt712->jack_detect_work, msecs_to_jiffies(30)); + + mutex_unlock(&rt712->disable_irq_lock); + + return 0; + +io_error: + mutex_unlock(&rt712->disable_irq_lock); + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static struct sdw_slave_ops rt712_sdca_slave_ops = { + .read_prop = rt712_sdca_read_prop, + .interrupt_callback = rt712_sdca_interrupt_callback, + .update_status = rt712_sdca_update_status, +}; + +static int rt712_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt712_sdca_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt712_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt712_sdca_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt712_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); + + if (rt712->hw_init) { + cancel_delayed_work_sync(&rt712->jack_detect_work); + cancel_delayed_work_sync(&rt712->jack_btn_check_work); + } + + pm_runtime_disable(&slave->dev); + + mutex_destroy(&rt712->calibrate_mutex); + mutex_destroy(&rt712->disable_irq_lock); + + return 0; +} + +static const struct sdw_device_id rt712_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x712, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x713, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x716, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x717, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt712_sdca_id); + +static int __maybe_unused rt712_sdca_dev_suspend(struct device *dev) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); + + if (!rt712->hw_init) + return 0; + + cancel_delayed_work_sync(&rt712->jack_detect_work); + cancel_delayed_work_sync(&rt712->jack_btn_check_work); + + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + + return 0; +} + +static int __maybe_unused rt712_sdca_dev_system_suspend(struct device *dev) +{ + struct rt712_sdca_priv *rt712_sdca = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt712_sdca->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt712_sdca->disable_irq_lock); + rt712_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + mutex_unlock(&rt712_sdca->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt712_sdca_dev_suspend(dev); +} + +#define RT712_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt712_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt712->first_hw_init) + return 0; + + if (!slave->unattach_request) { + if (rt712->disable_irq == true) { + mutex_lock(&rt712->disable_irq_lock); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); + rt712->disable_irq = false; + mutex_unlock(&rt712->disable_irq_lock); + } + goto regmap_sync; + } + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT712_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt712->regmap, false); + regcache_sync(rt712->regmap); + regcache_cache_only(rt712->mbq_regmap, false); + regcache_sync(rt712->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt712_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt712_sdca_dev_system_suspend, rt712_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt712_sdca_dev_suspend, rt712_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt712_sdca_sdw_driver = { + .driver = { + .name = "rt712-sdca", + .owner = THIS_MODULE, + .pm = &rt712_sdca_pm, + }, + .probe = rt712_sdca_sdw_probe, + .remove = rt712_sdca_sdw_remove, + .ops = &rt712_sdca_slave_ops, + .id_table = rt712_sdca_id, +}; +module_sdw_driver(rt712_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h new file mode 100644 index 0000000000..4be22ccd85 --- /dev/null +++ b/sound/soc/codecs/rt712-sdca-sdw.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt712-sdca-sdw.h -- RT712 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT712_SDW_H__ +#define __RT712_SDW_H__ + +#include +#include + +static const struct reg_default rt712_sdca_reg_defaults[] = { + { 0x201a, 0x00 }, + { 0x201b, 0x00 }, + { 0x201c, 0x00 }, + { 0x201d, 0x00 }, + { 0x201e, 0x00 }, + { 0x201f, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x2f }, + { 0x2232, 0x80 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x01 }, + { 0x2f35, 0x01 }, + { 0x2f36, 0xcf }, + { 0x2f50, 0x0f }, + { 0x2f54, 0x01 }, + { 0x2f58, 0x07 }, + { 0x2f59, 0x09 }, + { 0x2f5a, 0x01 }, + { 0x2f5b, 0x07 }, + { 0x2f5c, 0x05 }, + { 0x2f5d, 0x05 }, + { 0x3201, 0x01 }, + { 0x320c, 0x00 }, + { 0x3301, 0x01 }, + { 0x3302, 0x00 }, + { 0x3303, 0x1f }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, +}; + +static const struct reg_default rt712_sdca_mbq_defaults[] = { + { 0x2000004, 0xaa01 }, + { 0x200000e, 0x21e0 }, + { 0x2000024, 0x01ba }, + { 0x200004a, 0x8830 }, + { 0x2000067, 0xf100 }, + { 0x5800000, 0x1893 }, + { 0x5b00000, 0x0407 }, + { 0x5b00005, 0x0000 }, + { 0x5b00029, 0x3fff }, + { 0x5b0002a, 0xf000 }, + { 0x5f00008, 0x7000 }, + { 0x610000e, 0x0007 }, + { 0x6100022, 0x2828 }, + { 0x6100023, 0x2929 }, + { 0x6100026, 0x2c29 }, + { 0x610002c, 0x4150 }, + { 0x6100045, 0x0860 }, + { 0x6100046, 0x0029 }, + { 0x6100053, 0x3fff }, + { 0x6100055, 0x0000 }, + { 0x6100060, 0x0000 }, + { 0x6100064, 0x8000 }, + { 0x6100065, 0x0000 }, + { 0x6100067, 0xff12 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 }, +}; + +#endif /* __RT712_SDW_H__ */ diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c new file mode 100644 index 0000000000..6954fbe7ec --- /dev/null +++ b/sound/soc/codecs/rt712-sdca.c @@ -0,0 +1,1341 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt712-sdca.c -- rt712 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt712-sdca.h" + +static int rt712_sdca_index_write(struct rt712_sdca_priv *rt712, + unsigned int nid, unsigned int reg, unsigned int value) +{ + int ret; + struct regmap *regmap = rt712->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt712->slave->dev, + "Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt712_sdca_index_read(struct rt712_sdca_priv *rt712, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + struct regmap *regmap = rt712->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt712->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt712_sdca_index_read(rt712, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + return rt712_sdca_index_write(rt712, nid, reg, tmp); +} + +static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712) +{ + unsigned int val, loop_rc = 0, loop_dc = 0; + struct device *dev; + struct regmap *regmap = rt712->regmap; + int chk_cnt = 100; + int ret = 0; + + mutex_lock(&rt712->calibrate_mutex); + dev = regmap_get_device(regmap); + + /* Set HP-JD source from JD1 */ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); + + /* FSM switch to calibration manual mode */ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100); + + /* Calibration setting */ + rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, RT712_DAC_DC_CALI_CTL1, 0x7883); + + /* W1C Trigger DC calibration (HP & Class-D) */ + rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, RT712_DAC_DC_CALI_CTL1, 0xf893); + + /* wait for calibration process */ + rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, &val); + + for (loop_dc = 0; loop_dc < chk_cnt && + (val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) { + usleep_range(10000, 11000); + ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI, + RT712_DAC_DC_CALI_CTL1, &val); + if (ret < 0) + goto _cali_fail_; + } + if (loop_dc == chk_cnt) + dev_err(dev, "%s, calibration time-out!\n", __func__); + + if (loop_dc == chk_cnt || loop_rc == chk_cnt) + ret = -ETIMEDOUT; + +_cali_fail_: + /* Enable Rldet in FSM */ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4500); + + /* Sensing Lch+Rch */ + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_IMS_DIGITAL_CTL1, 0x040f); + + /* Sine gen path control */ + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_IMS_DIGITAL_CTL5, 0x0000); + + /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_DIGITAL_MISC_CTRL4, 0x0010); + + mutex_unlock(&rt712->calibrate_mutex); + dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret); + return ret; +} + +static unsigned int rt712_sdca_button_detect(struct rt712_sdca_priv *rt712) +{ + unsigned int btn_type = 0, offset, idx, val, owner; + int ret; + unsigned char buf[3]; + + /* get current UMP message owner */ + ret = regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), + &owner); + if (ret < 0) + return 0; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + /* read UMP message offset */ + ret = regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), + &offset); + if (ret < 0) + goto _end_btn_det_; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt712->regmap, + RT712_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) { + switch (buf[1] & 0xf0) { + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + switch (buf[2]) { + case 0x01: + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x02: + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x04: + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x08: + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + } + +_end_btn_det_: + /* Host is owner, so set back to device */ + if (owner == 0) + /* set owner to device */ + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, + RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01); + + return btn_type; +} + +static int rt712_sdca_headset_detect(struct rt712_sdca_priv *rt712) +{ + unsigned int det_mode; + int ret; + + /* get detected_mode */ + ret = regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0), + &det_mode); + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x00: + rt712->jack_type = 0; + break; + case 0x03: + rt712->jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + rt712->jack_type = SND_JACK_HEADSET; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0), + det_mode); + if (ret < 0) + goto io_error; + } + + dev_dbg(&rt712->slave->dev, + "%s, detected_mode=0x%x\n", __func__, det_mode); + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void rt712_sdca_jack_detect_handler(struct work_struct *work) +{ + struct rt712_sdca_priv *rt712 = + container_of(work, struct rt712_sdca_priv, jack_detect_work.work); + int btn_type = 0, ret; + + if (!rt712->hs_jack) + return; + + if (!rt712->component->card || !rt712->component->card->instantiated) + return; + + /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */ + if (rt712->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) { + ret = rt712_sdca_headset_detect(rt712); + if (ret < 0) + return; + } + + /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */ + if (rt712->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8) + btn_type = rt712_sdca_button_detect(rt712); + + if (rt712->jack_type == 0) + btn_type = 0; + + dev_dbg(&rt712->slave->dev, + "in %s, jack_type=0x%x\n", __func__, rt712->jack_type); + dev_dbg(&rt712->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + dev_dbg(&rt712->slave->dev, + "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt712->scp_sdca_stat1, rt712->scp_sdca_stat2); + + snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt712->hs_jack, rt712->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt712->jack_btn_check_work, msecs_to_jiffies(200)); + } +} + +static void rt712_sdca_btn_check_handler(struct work_struct *work) +{ + struct rt712_sdca_priv *rt712 = + container_of(work, struct rt712_sdca_priv, jack_btn_check_work.work); + int btn_type = 0, ret, idx; + unsigned int det_mode, offset, val; + unsigned char buf[3]; + + ret = regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0), + &det_mode); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (det_mode) { + /* read UMP message offset */ + ret = regmap_read(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), + &offset); + if (ret < 0) + goto io_error; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt712->regmap, + RT712_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto io_error; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) { + switch (buf[1] & 0xf0) { + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + switch (buf[2]) { + case 0x01: + case 0x10: + btn_type |= SND_JACK_BTN_2; + break; + case 0x02: + case 0x20: + btn_type |= SND_JACK_BTN_3; + break; + case 0x04: + case 0x40: + btn_type |= SND_JACK_BTN_0; + break; + case 0x08: + case 0x80: + btn_type |= SND_JACK_BTN_1; + break; + } + } + } else { + rt712->jack_type = 0; + } + + dev_dbg(&rt712->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt712->hs_jack, rt712->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt712->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712) +{ + mutex_lock(&rt712->calibrate_mutex); + + if (rt712->hs_jack) { + /* Enable HID1 event & set button RTC mode */ + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_UMP_HID_CTL5, 0xfff0); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_UMP_HID_CTL0, 0x1100, 0x1100); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_UMP_HID_CTL7, 0xf000, 0x0000); + + /* detected_mode_change_event_en & hid1_push_button_event_en */ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_GE_RELATED_CTL1, 0x0c00, 0x0c00); + /* ge_inbox_en */ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_GE_RELATED_CTL2, 0x0020, 0x0000); + + switch (rt712->jd_src) { + case RT712_JD1: + /* Set HP-JD source from JD1 */ + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a); + break; + default: + dev_warn(rt712->component->dev, "Wrong JD source\n"); + break; + } + + /* set SCP_SDCA_IntMask1[0]=1 */ + sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0); + /* set SCP_SDCA_IntMask2[0]=1 */ + sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); + dev_dbg(&rt712->slave->dev, "in %s enable\n", __func__); + + /* trigger GE interrupt */ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_GE_RELATED_CTL1, 0x0080, 0x0080); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_GE_RELATED_CTL1, 0x0080, 0x0000); + } else { + /* disable HID1 & detected_mode_change event */ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_GE_RELATED_CTL1, 0x0c00, 0x0000); + + dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__); + } + + mutex_unlock(&rt712->calibrate_mutex); +} + +static int rt712_sdca_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + int ret; + + rt712->hs_jack = hs_jack; + + if (!rt712->first_hw_init) + return 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + rt712_sdca_jack_init(rt712); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +/* For SDCA control DAC/ADC Gain */ +static int rt712_sdca_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned int read_l, read_r, gain_l_val, gain_r_val; + unsigned int adc_vol_flag = 0; + unsigned int lvalue, rvalue; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt712->mbq_regmap, mc->reg, &lvalue); + regmap_read(rt712->mbq_regmap, mc->rreg, &rvalue); + + /* L Channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_l_val = gain_l_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset); + else + gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset); + gain_l_val &= 0xffff; + } + + /* R Channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_r_val = gain_r_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset); + else + gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset); + gain_r_val &= 0xffff; + } + + if (lvalue == gain_l_val && rvalue == gain_r_val) + return 0; + + /* Lch*/ + regmap_write(rt712->mbq_regmap, mc->reg, gain_l_val); + /* Rch */ + regmap_write(rt712->mbq_regmap, mc->rreg, gain_r_val); + + regmap_read(rt712->mbq_regmap, mc->reg, &read_l); + regmap_read(rt712->mbq_regmap, mc->rreg, &read_r); + if (read_r == gain_r_val && read_l == gain_l_val) + return 1; + + return -EIO; +} + +static int rt712_sdca_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt712->mbq_regmap, mc->reg, &read_l); + regmap_read(rt712->mbq_regmap, mc->rreg, &read_r); + + if (mc->shift == 8) /* boost gain */ + ctl_l = read_l / tendB; + else { + if (adc_vol_flag) + ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset); + else + ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset); + } + + if (read_l != read_r) { + if (mc->shift == 8) /* boost gain */ + ctl_r = read_r / tendB; + else { /* ADC/DAC gain */ + if (adc_vol_flag) + ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset); + else + ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset); + } + } else + ctl_r = ctl_l; + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + + return 0; +} + +static int rt712_sdca_set_fu0f_capture_ctl(struct rt712_sdca_priv *rt712) +{ + int err; + unsigned int ch_l, ch_r; + + ch_l = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_l_mute) ? 0x01 : 0x00; + ch_r = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, + RT712_SDCA_CTL_FU_MUTE, CH_L), ch_l); + if (err < 0) + return err; + + err = regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, + RT712_SDCA_CTL_FU_MUTE, CH_R), ch_r); + if (err < 0) + return err; + + return 0; +} + +static int rt712_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt712->fu0f_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt712->fu0f_mixer_r_mute; + return 0; +} + +static int rt712_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + int err; + + if (rt712->fu0f_mixer_l_mute == !ucontrol->value.integer.value[0] && + rt712->fu0f_mixer_r_mute == !ucontrol->value.integer.value[1]) + return 0; + + rt712->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt712->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt712_sdca_set_fu0f_capture_ctl(rt712); + if (err < 0) + return err; + + return 1; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt712_sdca_controls[] = { + SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R), + 0, 0x57, 0, + rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv), + SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt712_sdca_fu0f_capture_get, rt712_sdca_fu0f_capture_put), + SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R), + 0, 0x3f, 0, + rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU44 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_R), + 8, 3, 0, + rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, boost_vol_tlv), +}; + +static const struct snd_kcontrol_new rt712_sdca_spk_controls[] = { + SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R), + 0, 0x57, 0, + rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv), +}; + +static int rt712_sdca_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask = 0x3300; + + rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, &val); + + val = val & mask; + switch (val) { + case 0x3000: + val = 1; + break; + case 0x0300: + val = 0; + break; + } + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt712_sdca_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int mask_sft; + unsigned int val; + + if (item[0] >= e->items) + return -EINVAL; + + if (ucontrol->value.enumerated.item[0] == 0) + mask_sft = 12; + else if (ucontrol->value.enumerated.item[0] == 1) + mask_sft = 8; + else + return -EINVAL; + + rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, &val); + val = (val >> mask_sft) & 0x3; + if (!val) + return 0; + + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_MIXER_CTL1, 0x3fff); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_MIXER_CTL1, 0x3 << mask_sft, 0); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return 1; +} + +static const char * const adc_mux_text[] = { + "MIC2", + "LINE2", +}; + +static SOC_ENUM_SINGLE_DECL( + rt712_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text); + +static const struct snd_kcontrol_new rt712_sdca_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt712_adc23_enum, + rt712_sdca_mux_get, rt712_sdca_mux_put); + +static int rt712_sdca_fu05_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, + RT712_SDCA_CTL_FU_MUTE, CH_L), + unmute); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, + RT712_SDCA_CTL_FU_MUTE, CH_R), + unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, + RT712_SDCA_CTL_FU_MUTE, CH_L), + mute); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, + RT712_SDCA_CTL_FU_MUTE, CH_R), + mute); + break; + } + return 0; +} + +static int rt712_sdca_fu0f_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt712->fu0f_dapm_mute = false; + rt712_sdca_set_fu0f_capture_ctl(rt712); + break; + case SND_SOC_DAPM_PRE_PMD: + rt712->fu0f_dapm_mute = true; + rt712_sdca_set_fu0f_capture_ctl(rt712); + break; + } + return 0; +} + +static int rt712_sdca_pde40_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static int rt712_sdca_pde12_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + } + return 0; +} + +static int rt712_sdca_classd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, + RT712_SDCA_CTL_REQ_POWER_STATE, 0), + ps3); + break; + + default: + break; + } + + return 0; +} + +static const struct snd_kcontrol_new rt712_spk_sto_dac = + SOC_DAPM_DOUBLE_R("Switch", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_L), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_R), + 0, 1, 1); + +static const struct snd_soc_dapm_widget rt712_sdca_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE2"), + + SND_SOC_DAPM_SUPPLY("PDE 40", SND_SOC_NOPM, 0, 0, + rt712_sdca_pde40_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0, + rt712_sdca_pde12_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_DAC_E("FU 05", NULL, SND_SOC_NOPM, 0, 0, + rt712_sdca_fu05_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 0F", NULL, SND_SOC_NOPM, 0, 0, + rt712_sdca_fu0f_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt712_sdca_adc23_mux), + + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt712_sdca_audio_map[] = { + { "FU 05", NULL, "DP1RX" }, + { "DP4TX", NULL, "FU 0F" }, + + { "FU 0F", NULL, "PDE 12" }, + { "FU 0F", NULL, "ADC 23 Mux" }, + { "ADC 23 Mux", "LINE2", "LINE2" }, + { "ADC 23 Mux", "MIC2", "MIC2" }, + + { "HP", NULL, "PDE 40" }, + { "HP", NULL, "FU 05" }, +}; + +static const struct snd_soc_dapm_widget rt712_sdca_spk_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + + /* Digital Interface */ + SND_SOC_DAPM_SWITCH("FU06", SND_SOC_NOPM, 0, 0, &rt712_spk_sto_dac), + + /* Output */ + SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0, + rt712_sdca_classd_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("SPOL"), + SND_SOC_DAPM_OUTPUT("SPOR"), +}; + +static const struct snd_soc_dapm_route rt712_sdca_spk_dapm_routes[] = { + { "FU06", "Switch", "DP3RX" }, + { "CLASS D", NULL, "FU06" }, + { "SPOL", NULL, "CLASS D" }, + { "SPOR", NULL, "CLASS D" }, +}; + +static int rt712_sdca_parse_dt(struct rt712_sdca_priv *rt712, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", &rt712->jd_src); + + return 0; +} + +static int rt712_sdca_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + int ret; + + rt712_sdca_parse_dt(rt712, &rt712->slave->dev); + rt712->component = component; + + /* add SPK route */ + if (rt712->hw_id != RT712_DEV_ID_713) { + snd_soc_add_component_controls(component, + rt712_sdca_spk_controls, ARRAY_SIZE(rt712_sdca_spk_controls)); + snd_soc_dapm_new_controls(dapm, + rt712_sdca_spk_dapm_widgets, ARRAY_SIZE(rt712_sdca_spk_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, + rt712_sdca_spk_dapm_routes, ARRAY_SIZE(rt712_sdca_spk_dapm_routes)); + } + + if (!rt712->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt712 = { + .probe = rt712_sdca_probe, + .controls = rt712_sdca_controls, + .num_controls = ARRAY_SIZE(rt712_sdca_controls), + .dapm_widgets = rt712_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dapm_widgets), + .dapm_routes = rt712_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt712_sdca_audio_map), + .set_jack = rt712_sdca_set_jack_detect, + .endianness = 1, +}; + +static int rt712_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt712_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + int retval, port, num_channels; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt712->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + if (dai->id == RT712_AIF1) + port = 1; + else if (dai->id == RT712_AIF2) + port = 3; + else + return -EINVAL; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT712_AIF1) + port = 4; + else + return -EINVAL; + } + + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = GENMASK(num_channels - 1, 0); + port_config.num = port; + + retval = sdw_stream_add_slave(rt712->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT712_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT712_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT712_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT712_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + switch (dai->id) { + case RT712_AIF1: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + break; + case RT712_AIF2: + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + sampling_rate); + break; + default: + dev_err(component->dev, "Wrong DAI id\n"); + return -EINVAL; + } + + return 0; +} + +static int rt712_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt712->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt712->slave, sdw_stream); + return 0; +} + +#define RT712_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) +#define RT712_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt712_sdca_ops = { + .hw_params = rt712_sdca_pcm_hw_params, + .hw_free = rt712_sdca_pcm_hw_free, + .set_stream = rt712_sdca_set_sdw_stream, + .shutdown = rt712_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt712_sdca_dai[] = { + { + .name = "rt712-sdca-aif1", + .id = RT712_AIF1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT712_STEREO_RATES, + .formats = RT712_FORMATS, + }, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT712_STEREO_RATES, + .formats = RT712_FORMATS, + }, + .ops = &rt712_sdca_ops, + }, + { + .name = "rt712-sdca-aif2", + .id = RT712_AIF2, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT712_STEREO_RATES, + .formats = RT712_FORMATS, + }, + .ops = &rt712_sdca_ops, + } +}; + +int rt712_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt712_sdca_priv *rt712; + int ret; + + rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL); + if (!rt712) + return -ENOMEM; + + dev_set_drvdata(dev, rt712); + rt712->slave = slave; + rt712->regmap = regmap; + rt712->mbq_regmap = mbq_regmap; + + regcache_cache_only(rt712->regmap, true); + regcache_cache_only(rt712->mbq_regmap, true); + + mutex_init(&rt712->calibrate_mutex); + mutex_init(&rt712->disable_irq_lock); + + INIT_DELAYED_WORK(&rt712->jack_detect_work, rt712_sdca_jack_detect_handler); + INIT_DELAYED_WORK(&rt712->jack_btn_check_work, rt712_sdca_btn_check_handler); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt712->hw_init = false; + rt712->first_hw_init = false; + rt712->fu0f_dapm_mute = true; + rt712->fu0f_mixer_l_mute = rt712->fu0f_mixer_r_mute = true; + + /* JD source uses JD1 in default */ + rt712->jd_src = RT712_JD1; + + if (slave->id.part_id != RT712_PART_ID_713) + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt712, rt712_sdca_dai, ARRAY_SIZE(rt712_sdca_dai)); + else + ret = devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt712, rt712_sdca_dai, 1); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); + int ret = 0; + unsigned int val, hibernation_flag; + + rt712->disable_irq = false; + + if (rt712->hw_init) + return 0; + + regcache_cache_only(rt712->regmap, false); + regcache_cache_only(rt712->mbq_regmap, false); + if (rt712->first_hw_init) { + regcache_cache_bypass(rt712->regmap, true); + regcache_cache_bypass(rt712->mbq_regmap, true); + } else { + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val); + rt712->hw_id = (val & 0xf000) >> 12; + + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81); + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0); + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL1, 0x0000); + rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL2, 0x0000); + rt712_sdca_index_write(rt712, RT712_VENDOR_ANALOG_CTL, RT712_MISC_POWER_CTL7, 0x0000); + regmap_write(rt712->regmap, RT712_RC_CAL, 0x23); + + /* calibration */ + rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, &hibernation_flag); + if (!hibernation_flag) { + ret = rt712_sdca_calibration(rt712); + if (ret < 0) + dev_err(dev, "%s, calibration failed!\n", __func__); + } + + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_MIXER_CTL1, 0x3000, 0x0000); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_ADC0A_08_PDE_FLOAT_CTL, 0x1112); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_MIC2_LINE2_PDE_FLOAT_CTL, 0x3412); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, + RT712_DAC03_HP_PDE_FLOAT_CTL, 0x4040); + + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL, 0x0001, 0x0000); + regmap_write(rt712->regmap, 0x2f50, 0x00); + regmap_write(rt712->regmap, 0x2f54, 0x00); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_IT09, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01); + + /* add SPK settings */ + if (rt712->hw_id != RT712_DEV_ID_713) { + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_AMP_PDE_FLOAT_CTL, 0x2323); + rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_EAPD_CTL, 0x0002); + regmap_write(rt712->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04); + } + + /* + * if set_jack callback occurred early than io_init, + * we set up the jack detection function now + */ + if (rt712->hs_jack) + rt712_sdca_jack_init(rt712); + + if (!hibernation_flag) + rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, 0x0001); + + if (rt712->first_hw_init) { + regcache_cache_bypass(rt712->regmap, false); + regcache_mark_dirty(rt712->regmap); + regcache_cache_bypass(rt712->mbq_regmap, false); + regcache_mark_dirty(rt712->mbq_regmap); + } else + rt712->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt712->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver"); +MODULE_AUTHOR("Shuming Fan "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h new file mode 100644 index 0000000000..ff79e03118 --- /dev/null +++ b/sound/soc/codecs/rt712-sdca.h @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt712-sdca.h -- RT712 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT712_H__ +#define __RT712_H__ + +#include +#include +#include +#include +#include +#include + +struct rt712_sdca_priv { + struct regmap *regmap; + struct regmap *mbq_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + struct mutex calibrate_mutex; /* for headset calibration */ + struct mutex disable_irq_lock; /* SDCA irq lock protection */ + bool disable_irq; + int jack_type; + int jd_src; + unsigned int scp_sdca_stat1; + unsigned int scp_sdca_stat2; + unsigned int hw_id; + bool fu0f_dapm_mute; + bool fu0f_mixer_l_mute; + bool fu0f_mixer_r_mute; +}; + +/* NID */ +#define RT712_VENDOR_REG 0x20 +#define RT712_VENDOR_CALI 0x58 +#define RT712_ULTRA_SOUND_DET 0x59 +#define RT712_VENDOR_IMS_DRE 0x5b +#define RT712_VENDOR_ANALOG_CTL 0x5f +#define RT712_VENDOR_HDA_CTL 0x61 + +/* Index (NID:20h) */ +#define RT712_JD_PRODUCT_NUM 0x00 +#define RT712_ANALOG_BIAS_CTL3 0x04 +#define RT712_LDO2_3_CTL1 0x0e +#define RT712_PARA_VERB_CTL 0x1a +#define RT712_CC_DET1 0x24 +#define RT712_COMBO_JACK_AUTO_CTL1 0x45 +#define RT712_COMBO_JACK_AUTO_CTL2 0x46 +#define RT712_COMBO_JACK_AUTO_CTL3 0x47 +#define RT712_DIGITAL_MISC_CTRL4 0x4a +#define RT712_FSM_CTL 0x67 +#define RT712_SW_CONFIG1 0x8a +#define RT712_SW_CONFIG2 0x8b + +/* Index (NID:58h) */ +#define RT712_DAC_DC_CALI_CTL1 0x00 +#define RT712_DAC_DC_CALI_CTL2 0x01 + +/* Index (NID:59h) */ +#define RT712_ULTRA_SOUND_DETECTOR6 0x1e + +/* Index (NID:5bh) */ +#define RT712_IMS_DIGITAL_CTL1 0x00 +#define RT712_IMS_DIGITAL_CTL5 0x05 +#define RT712_HP_DETECT_RLDET_CTL1 0x29 +#define RT712_HP_DETECT_RLDET_CTL2 0x2a + +/* Index (NID:5fh) */ +#define RT712_MISC_POWER_CTL0 0x00 +#define RT712_MISC_POWER_CTL7 0x08 + +/* Index (NID:61h) */ +#define RT712_HDA_LEGACY_MUX_CTL0 0x00 +#define RT712_HDA_LEGACY_CONFIG_CTL0 0x06 +#define RT712_HDA_LEGACY_RESET_CTL 0x08 +#define RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e +#define RT712_DMIC_ENT_FLOAT_CTL 0x10 +#define RT712_DMIC_GAIN_ENT_FLOAT_CTL0 0x11 +#define RT712_DMIC_GAIN_ENT_FLOAT_CTL2 0x13 +#define RT712_ADC_ENT_FLOAT_CTL 0x15 +#define RT712_ADC_VOL_CH_FLOAT_CTL2 0x18 +#define RT712_DAC03_HP_PDE_FLOAT_CTL 0x22 +#define RT712_MIC2_LINE2_PDE_FLOAT_CTL 0x23 +#define RT712_ADC0A_08_PDE_FLOAT_CTL 0x26 +#define RT712_ADC0B_11_PDE_FLOAT_CTL 0x27 +#define RT712_DMIC1_2_PDE_FLOAT_CTL 0x2b +#define RT712_AMP_PDE_FLOAT_CTL 0x2c +#define RT712_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f +#define RT712_GE_RELATED_CTL1 0x45 +#define RT712_GE_RELATED_CTL2 0x46 +#define RT712_MIXER_CTL0 0x52 +#define RT712_MIXER_CTL1 0x53 +#define RT712_EAPD_CTL 0x55 +#define RT712_UMP_HID_CTL0 0x60 +#define RT712_UMP_HID_CTL1 0x61 +#define RT712_UMP_HID_CTL2 0x62 +#define RT712_UMP_HID_CTL3 0x63 +#define RT712_UMP_HID_CTL4 0x64 +#define RT712_UMP_HID_CTL5 0x65 +#define RT712_UMP_HID_CTL6 0x66 +#define RT712_UMP_HID_CTL7 0x67 +#define RT712_UMP_HID_CTL8 0x68 + +/* Parameter & Verb control 01 (0x1a)(NID:20h) */ +#define RT712_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT712_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT712_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT712_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT712_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +/* DAC DC offset calibration control-1 (0x00)(NID:58h) */ +#define RT712_DAC_DC_CALI_TRIGGER (0x1 << 15) + +#define RT712_EAPD_HIGH 0x2 +#define RT712_EAPD_LOW 0x0 + +/* RC Calibration register */ +#define RT712_RC_CAL 0x3201 + +/* Buffer address for HID */ +#define RT712_BUF_ADDR_HID1 0x44030000 +#define RT712_BUF_ADDR_HID2 0x44030020 + +/* RT712 SDCA Control - function number */ +#define FUNC_NUM_JACK_CODEC 0x01 +#define FUNC_NUM_MIC_ARRAY 0x02 +#define FUNC_NUM_HID 0x03 +#define FUNC_NUM_AMP 0x04 + +/* RT712 SDCA entity */ +#define RT712_SDCA_ENT_HID01 0x01 +#define RT712_SDCA_ENT_GE49 0x49 +#define RT712_SDCA_ENT_USER_FU05 0x05 +#define RT712_SDCA_ENT_USER_FU06 0x06 +#define RT712_SDCA_ENT_USER_FU0F 0x0f +#define RT712_SDCA_ENT_USER_FU10 0x19 +#define RT712_SDCA_ENT_USER_FU1E 0x1e +#define RT712_SDCA_ENT_FU15 0x15 +#define RT712_SDCA_ENT_PDE23 0x23 +#define RT712_SDCA_ENT_PDE40 0x40 +#define RT712_SDCA_ENT_PDE11 0x11 +#define RT712_SDCA_ENT_PDE12 0x12 +#define RT712_SDCA_ENT_CS01 0x01 +#define RT712_SDCA_ENT_CS11 0x11 +#define RT712_SDCA_ENT_CS1F 0x1f +#define RT712_SDCA_ENT_CS1C 0x1c +#define RT712_SDCA_ENT_CS31 0x31 +#define RT712_SDCA_ENT_OT23 0x42 +#define RT712_SDCA_ENT_IT26 0x26 +#define RT712_SDCA_ENT_IT09 0x09 +#define RT712_SDCA_ENT_PLATFORM_FU15 0x15 +#define RT712_SDCA_ENT_PLATFORM_FU44 0x44 + +/* RT712 SDCA control */ +#define RT712_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT712_SDCA_CTL_FU_MUTE 0x01 +#define RT712_SDCA_CTL_FU_VOLUME 0x02 +#define RT712_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10 +#define RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11 +#define RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12 +#define RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13 +#define RT712_SDCA_CTL_SELECTED_MODE 0x01 +#define RT712_SDCA_CTL_DETECTED_MODE 0x02 +#define RT712_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT712_SDCA_CTL_VENDOR_DEF 0x30 +#define RT712_SDCA_CTL_FU_CH_GAIN 0x0b + +/* RT712 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 + +/* sample frequency index */ +#define RT712_SDCA_RATE_16000HZ 0x04 +#define RT712_SDCA_RATE_32000HZ 0x07 +#define RT712_SDCA_RATE_44100HZ 0x08 +#define RT712_SDCA_RATE_48000HZ 0x09 +#define RT712_SDCA_RATE_96000HZ 0x0b +#define RT712_SDCA_RATE_192000HZ 0x0d + +enum { + RT712_AIF1, + RT712_AIF2, +}; + +enum rt712_sdca_jd_src { + RT712_JD_NULL, + RT712_JD1, +}; + +enum rt712_sdca_hw_id { + RT712_DEV_ID_712 = 0x7, + RT712_DEV_ID_713 = 0x6, + RT712_DEV_ID_716 = 0x5, + RT712_DEV_ID_717 = 0x4, +}; + +#define RT712_PART_ID_713 0x713 + +int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt712_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave); + +int rt712_sdca_jack_detect(struct rt712_sdca_priv *rt712, bool *hp, bool *mic); +#endif /* __RT712_H__ */ diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c new file mode 100644 index 0000000000..ab54a67a27 --- /dev/null +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt715-sdca-sdw.c -- rt715 ALSA SoC audio driver +// +// Copyright(c) 2020 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt715-sdca.h" +#include "rt715-sdca-sdw.h" + +static bool rt715_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2230 ... 0x2239: + case 0x2f5b: + case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): + return true; + default: + return false; + } +} + +static bool rt715_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2021: + case 0x2023: + case 0x2230: + case 0x202d ... 0x202f: /* BRA */ + case 0x2200 ... 0x2212: /* i2c debug */ + case 0x2f07: + case 0x2f1b ... 0x2f1e: + case 0x2f30 ... 0x2f34: + case 0x2f50 ... 0x2f51: + case 0x2f53 ... 0x2f59: + case 0x2f5c ... 0x2f5f: + case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): /* VAD Searching status */ + return true; + default: + return false; + } +} + +static bool rt715_sdca_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + case 0x200002b: + case 0x2000036: + case 0x2000037: + case 0x2000039: + case 0x2000044: + case 0x6100000: + return true; + default: + return false; + } +} + +static bool rt715_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + return true; + default: + return false; + } +} + +static const struct regmap_config rt715_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt715_sdca_readable_register, + .volatile_reg = rt715_sdca_volatile_register, + .max_register = 0x43ffffff, + .reg_defaults = rt715_reg_defaults_sdca, + .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt715_sdca_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt715_sdca_mbq_readable_register, + .volatile_reg = rt715_sdca_mbq_volatile_register, + .max_register = 0x43ffffff, + .reg_defaults = rt715_mbq_reg_defaults_sdca, + .num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt715_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev); + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt715->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt715_sdca_io_init(&slave->dev, slave); +} + +static int rt715_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = true; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x50;/* BITMAP: 01010000 */ + prop->sink_ports = 0x0; /* BITMAP: 00000000 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + dpn = prop->src_dpn_prop; + i = 0; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 200; + + return 0; +} + +static const struct sdw_slave_ops rt715_sdca_slave_ops = { + .read_prop = rt715_sdca_read_prop, + .update_status = rt715_sdca_update_status, +}; + +static int rt715_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *mbq_regmap, *regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt715_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt715_sdca_init(&slave->dev, mbq_regmap, regmap, slave); +} + +static int rt715_sdca_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); + + return 0; +} + +static const struct sdw_device_id rt715_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x3, 0x1, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt715_sdca_id); + +static int __maybe_unused rt715_dev_suspend(struct device *dev) +{ + struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); + + if (!rt715->hw_init) + return 0; + + regcache_cache_only(rt715->regmap, true); + regcache_mark_dirty(rt715->regmap); + regcache_cache_only(rt715->mbq_regmap, true); + regcache_mark_dirty(rt715->mbq_regmap); + + return 0; +} + +#define RT715_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt715_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt715->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->enumeration_complete, + msecs_to_jiffies(RT715_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Enumeration not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt715->regmap, false); + regcache_sync_region(rt715->regmap, + SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, + CH_00), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00)); + regcache_cache_only(rt715->mbq_regmap, false); + regcache_sync_region(rt715->mbq_regmap, 0x2000000, 0x61020ff); + regcache_sync_region(rt715->mbq_regmap, + SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, + CH_00), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00)); + + return 0; +} + +static const struct dev_pm_ops rt715_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) + SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) +}; + +static struct sdw_driver rt715_sdw_driver = { + .driver = { + .name = "rt715-sdca", + .owner = THIS_MODULE, + .pm = &rt715_pm, + }, + .probe = rt715_sdca_sdw_probe, + .remove = rt715_sdca_sdw_remove, + .ops = &rt715_sdca_slave_ops, + .id_table = rt715_sdca_id, +}; +module_sdw_driver(rt715_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT715 driver SDW SDCA"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h new file mode 100644 index 0000000000..0cbc14844f --- /dev/null +++ b/sound/soc/codecs/rt715-sdca-sdw.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt715-sdca-sdw.h -- RT715 ALSA SoC audio driver header + * + * Copyright(c) 2020 Realtek Semiconductor Corp. + */ + +#ifndef __RT715_SDW_SDCA_H__ +#define __RT715_SDW_SDCA_H__ + +#include + +static const struct reg_default rt715_reg_defaults_sdca[] = { + { 0x201a, 0x00 }, + { 0x201e, 0x00 }, + { 0x2020, 0x00 }, + { 0x2021, 0x00 }, + { 0x2022, 0x00 }, + { 0x2023, 0x00 }, + { 0x2024, 0x00 }, + { 0x2025, 0x01 }, + { 0x2026, 0x00 }, + { 0x2027, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x2f }, + { 0x2232, 0x80 }, + { 0x2233, 0x00 }, + { 0x2234, 0x00 }, + { 0x2235, 0x00 }, + { 0x2236, 0x00 }, + { 0x2237, 0x00 }, + { 0x2238, 0x00 }, + { 0x2239, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x0b }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0e }, + { 0x2f06, 0x01 }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f0b, 0x00 }, + { 0x2f0c, 0x00 }, + { 0x2f0d, 0x00 }, + { 0x2f0e, 0x12 }, + { 0x2f0f, 0x00 }, + { 0x2f10, 0x00 }, + { 0x2f11, 0x00 }, + { 0x2f12, 0x00 }, + { 0x2f13, 0x00 }, + { 0x2f14, 0x00 }, + { 0x2f15, 0x00 }, + { 0x2f16, 0x00 }, + { 0x2f17, 0x00 }, + { 0x2f18, 0x00 }, + { 0x2f19, 0x03 }, + { 0x2f1a, 0x00 }, + { 0x2f1f, 0x10 }, + { 0x2f20, 0x00 }, + { 0x2f21, 0x00 }, + { 0x2f22, 0x00 }, + { 0x2f23, 0x00 }, + { 0x2f24, 0x00 }, + { 0x2f25, 0x00 }, + { 0x2f52, 0x01 }, + { 0x2f5a, 0x02 }, + { 0x2f5b, 0x05 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, + RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, +}; + +static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { + { 0x200002b, 0x0420 }, + { 0x2000036, 0x0000 }, + { 0x2000037, 0x0000 }, + { 0x2000039, 0xaa81 }, + { 0x2000044, 0x0202 }, + { 0x6100000, 0x0100 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, + { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, +}; +#endif /* __RT715_SDW_SDCA_H__ */ diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c new file mode 100644 index 0000000000..9fa96fd83d --- /dev/null +++ b/sound/soc/codecs/rt715-sdca.c @@ -0,0 +1,1084 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt715-sdca.c -- rt715 ALSA SoC audio driver +// +// Copyright(c) 2020 Realtek Semiconductor Corp. +// +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt715-sdca.h" + +static int rt715_sdca_index_write(struct rt715_sdca_priv *rt715, + unsigned int nid, unsigned int reg, unsigned int value) +{ + struct regmap *regmap = rt715->mbq_regmap; + unsigned int addr; + int ret; + + addr = (nid << 20) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt715->slave->dev, + "Failed to set private value: %08x <= %04x %d\n", ret, addr, + value); + + return ret; +} + +static int rt715_sdca_index_read(struct rt715_sdca_priv *rt715, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + struct regmap *regmap = rt715->mbq_regmap; + unsigned int addr; + int ret; + + addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt715->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt715_sdca_index_update_bits(struct rt715_sdca_priv *rt715, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt715_sdca_index_read(rt715, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + + return rt715_sdca_index_write(rt715, nid, reg, tmp); +} + +static inline unsigned int rt715_sdca_vol_gain(unsigned int u_ctrl_val, + unsigned int vol_max, unsigned int vol_gain_sft) +{ + unsigned int val; + + if (u_ctrl_val > vol_max) + u_ctrl_val = vol_max; + val = u_ctrl_val; + u_ctrl_val = + ((abs(u_ctrl_val - vol_gain_sft) * RT715_SDCA_DB_STEP) << 8) / 1000; + if (val <= vol_gain_sft) { + u_ctrl_val = ~u_ctrl_val; + u_ctrl_val += 1; + } + u_ctrl_val &= 0xffff; + + return u_ctrl_val; +} + +static inline unsigned int rt715_sdca_boost_gain(unsigned int u_ctrl_val, + unsigned int b_max, unsigned int b_gain_sft) +{ + if (u_ctrl_val > b_max) + u_ctrl_val = b_max; + + return (u_ctrl_val * 10) << b_gain_sft; +} + +static inline unsigned int rt715_sdca_get_gain(unsigned int reg_val, + unsigned int gain_sft) +{ + unsigned int neg_flag = 0; + + if (reg_val & BIT(15)) { + reg_val = ~(reg_val - 1) & 0xffff; + neg_flag = 1; + } + reg_val *= 1000; + reg_val >>= 8; + if (neg_flag) + reg_val = gain_sft - reg_val / RT715_SDCA_DB_STEP; + else + reg_val = gain_sft + reg_val / RT715_SDCA_DB_STEP; + + return reg_val; +} + +/* SDCA Volume/Boost control */ +static int rt715_sdca_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + unsigned int gain_val, i, k_changed = 0; + int ret; + + for (i = 0; i < 2; i++) { + if (ucontrol->value.integer.value[i] != rt715->kctl_2ch_orig[i]) { + k_changed = 1; + break; + } + } + + for (i = 0; i < 2; i++) { + rt715->kctl_2ch_orig[i] = ucontrol->value.integer.value[i]; + gain_val = + rt715_sdca_vol_gain(ucontrol->value.integer.value[i], mc->max, + mc->shift); + ret = regmap_write(rt715->mbq_regmap, mc->reg + i, gain_val); + if (ret != 0) { + dev_err(component->dev, "Failed to write 0x%x=0x%x\n", + mc->reg + i, gain_val); + return ret; + } + } + + return k_changed; +} + +static int rt715_sdca_set_amp_gain_4ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct rt715_sdca_kcontrol_private *p = + (struct rt715_sdca_kcontrol_private *)kcontrol->private_value; + unsigned int reg_base = p->reg_base, k_changed = 0; + const unsigned int gain_sft = 0x2f; + unsigned int gain_val, i; + int ret; + + for (i = 0; i < 4; i++) { + if (ucontrol->value.integer.value[i] != rt715->kctl_4ch_orig[i]) { + k_changed = 1; + break; + } + } + + for (i = 0; i < 4; i++) { + rt715->kctl_4ch_orig[i] = ucontrol->value.integer.value[i]; + gain_val = + rt715_sdca_vol_gain(ucontrol->value.integer.value[i], p->max, + gain_sft); + ret = regmap_write(rt715->mbq_regmap, reg_base + i, + gain_val); + if (ret != 0) { + dev_err(component->dev, "Failed to write 0x%x=0x%x\n", + reg_base + i, gain_val); + return ret; + } + } + + return k_changed; +} + +static int rt715_sdca_set_amp_gain_8ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct rt715_sdca_kcontrol_private *p = + (struct rt715_sdca_kcontrol_private *)kcontrol->private_value; + unsigned int reg_base = p->reg_base, i, k_changed = 0; + const unsigned int gain_sft = 8; + unsigned int gain_val, reg; + int ret; + + for (i = 0; i < 8; i++) { + if (ucontrol->value.integer.value[i] != rt715->kctl_8ch_orig[i]) { + k_changed = 1; + break; + } + } + + for (i = 0; i < 8; i++) { + rt715->kctl_8ch_orig[i] = ucontrol->value.integer.value[i]; + gain_val = + rt715_sdca_boost_gain(ucontrol->value.integer.value[i], p->max, + gain_sft); + reg = i < 7 ? reg_base + i : (reg_base - 1) | BIT(15); + ret = regmap_write(rt715->mbq_regmap, reg, gain_val); + if (ret != 0) { + dev_err(component->dev, "Failed to write 0x%x=0x%x\n", + reg, gain_val); + return ret; + } + } + + return k_changed; +} + +static int rt715_sdca_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + unsigned int val, i; + int ret; + + for (i = 0; i < 2; i++) { + ret = regmap_read(rt715->mbq_regmap, mc->reg + i, &val); + if (ret < 0) { + dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", + mc->reg + i, ret); + return ret; + } + ucontrol->value.integer.value[i] = rt715_sdca_get_gain(val, mc->shift); + } + + return 0; +} + +static int rt715_sdca_set_amp_gain_4ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct rt715_sdca_kcontrol_private *p = + (struct rt715_sdca_kcontrol_private *)kcontrol->private_value; + unsigned int reg_base = p->reg_base, i; + const unsigned int gain_sft = 0x2f; + unsigned int val; + int ret; + + for (i = 0; i < 4; i++) { + ret = regmap_read(rt715->mbq_regmap, reg_base + i, &val); + if (ret < 0) { + dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", + reg_base + i, ret); + return ret; + } + ucontrol->value.integer.value[i] = rt715_sdca_get_gain(val, gain_sft); + } + + return 0; +} + +static int rt715_sdca_set_amp_gain_8ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct rt715_sdca_kcontrol_private *p = + (struct rt715_sdca_kcontrol_private *)kcontrol->private_value; + unsigned int reg_base = p->reg_base; + const unsigned int gain_sft = 8; + unsigned int val_l, val_r; + unsigned int i, reg; + int ret; + + for (i = 0; i < 8; i += 2) { + ret = regmap_read(rt715->mbq_regmap, reg_base + i, &val_l); + if (ret < 0) { + dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", + reg_base + i, ret); + return ret; + } + ucontrol->value.integer.value[i] = (val_l >> gain_sft) / 10; + + reg = (i == 6) ? (reg_base - 1) | BIT(15) : reg_base + 1 + i; + ret = regmap_read(rt715->mbq_regmap, reg, &val_r); + if (ret < 0) { + dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", + reg, ret); + return ret; + } + ucontrol->value.integer.value[i + 1] = (val_r >> gain_sft) / 10; + } + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static int rt715_sdca_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_sdca_kcontrol_private *p = + (struct rt715_sdca_kcontrol_private *)kcontrol->private_value; + unsigned int reg_base = p->reg_base; + unsigned int invert = p->invert, i; + int val; + + for (i = 0; i < p->count; i += 2) { + val = snd_soc_component_read(component, reg_base + i); + if (val < 0) + return -EINVAL; + ucontrol->value.integer.value[i] = invert ? p->max - val : val; + + val = snd_soc_component_read(component, reg_base + 1 + i); + if (val < 0) + return -EINVAL; + ucontrol->value.integer.value[i + 1] = + invert ? p->max - val : val; + } + + return 0; +} + +static int rt715_sdca_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct rt715_sdca_kcontrol_private *p = + (struct rt715_sdca_kcontrol_private *)kcontrol->private_value; + unsigned int val[4] = {0}, val_mask, i, k_changed = 0; + unsigned int reg = p->reg_base; + unsigned int shift = p->shift; + unsigned int max = p->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = p->invert; + int err; + + for (i = 0; i < 4; i++) { + if (ucontrol->value.integer.value[i] != rt715->kctl_switch_orig[i]) { + k_changed = 1; + break; + } + } + + for (i = 0; i < 2; i++) { + rt715->kctl_switch_orig[i * 2] = ucontrol->value.integer.value[i * 2]; + val[i * 2] = ucontrol->value.integer.value[i * 2] & mask; + if (invert) + val[i * 2] = max - val[i * 2]; + val_mask = mask << shift; + val[i * 2] <<= shift; + + rt715->kctl_switch_orig[i * 2 + 1] = + ucontrol->value.integer.value[i * 2 + 1]; + val[i * 2 + 1] = + ucontrol->value.integer.value[i * 2 + 1] & mask; + if (invert) + val[i * 2 + 1] = max - val[i * 2 + 1]; + + val[i * 2 + 1] <<= shift; + + err = snd_soc_component_update_bits(component, reg + i * 2, val_mask, + val[i * 2]); + if (err < 0) + return err; + + err = snd_soc_component_update_bits(component, reg + 1 + i * 2, + val_mask, val[i * 2 + 1]); + if (err < 0) + return err; + } + + return k_changed; +} + +static int rt715_sdca_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt715_sdca_kcontrol_private *p = + (struct rt715_sdca_kcontrol_private *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +#define RT715_SDCA_PR_VALUE(xreg_base, xcount, xmax, xshift, xinvert) \ + ((unsigned long)&(struct rt715_sdca_kcontrol_private) \ + {.reg_base = xreg_base, .count = xcount, .max = xmax, \ + .shift = xshift, .invert = xinvert}) + +#define RT715_SDCA_FU_CTRL(xname, reg_base, xshift, xmax, xinvert, xcount) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = rt715_sdca_fu_info, \ + .get = rt715_sdca_get_volsw, \ + .put = rt715_sdca_put_volsw, \ + .private_value = RT715_SDCA_PR_VALUE(reg_base, xcount, xmax, \ + xshift, xinvert)} + +#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ + xmax, xinvert) } + +#define RT715_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\ + xhandler_put, tlv_array, xcount, xmax) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt715_sdca_fu_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = RT715_SDCA_PR_VALUE(reg_base, xcount, xmax, 0, 0) } + +#define RT715_SDCA_BOOST_EXT_TLV(xname, reg_base, xhandler_get,\ + xhandler_put, tlv_array, xcount, xmax) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt715_sdca_fu_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = RT715_SDCA_PR_VALUE(reg_base, xcount, xmax, 0, 0) } + +static const struct snd_kcontrol_new rt715_sdca_snd_controls[] = { + /* Capture switch */ + SOC_DOUBLE_R("FU0A Capture Switch", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_02), + 0, 1, 1), + RT715_SDCA_FU_CTRL("FU02 Capture Switch", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), + 0, 1, 1, 4), + RT715_SDCA_FU_CTRL("FU06 Capture Switch", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_MUTE_CTRL, CH_01), + 0, 1, 1, 4), + /* Volume Control */ + SOC_DOUBLE_R_EXT_TLV("FU0A Capture Volume", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_02), + 0x2f, 0x7f, 0, + rt715_sdca_set_amp_gain_get, rt715_sdca_set_amp_gain_put, + in_vol_tlv), + RT715_SDCA_EXT_TLV("FU02 Capture Volume", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), + rt715_sdca_set_amp_gain_4ch_get, + rt715_sdca_set_amp_gain_4ch_put, + in_vol_tlv, 4, 0x7f), + RT715_SDCA_EXT_TLV("FU06 Capture Volume", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, + RT715_SDCA_FU_VOL_CTRL, CH_01), + rt715_sdca_set_amp_gain_4ch_get, + rt715_sdca_set_amp_gain_4ch_put, + in_vol_tlv, 4, 0x7f), + /* MIC Boost Control */ + RT715_SDCA_BOOST_EXT_TLV("FU0E Boost", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), + rt715_sdca_set_amp_gain_8ch_get, + rt715_sdca_set_amp_gain_8ch_put, + mic_vol_tlv, 8, 3), + RT715_SDCA_BOOST_EXT_TLV("FU0C Boost", + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, + RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), + rt715_sdca_set_amp_gain_8ch_get, + rt715_sdca_set_amp_gain_8ch_put, + mic_vol_tlv, 8, 3), +}; + +static int rt715_sdca_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + unsigned int val, mask_sft; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + mask_sft = 8; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + rt715_sdca_index_read(rt715, RT715_VENDOR_HDA_CTL, + RT715_HDA_LEGACY_MUX_CTL1, &val); + val = (val >> mask_sft) & 0xf; + + /* + * The first two indices of ADC Mux 24/25 are routed to the same + * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2. + * To have a unique set of inputs, we skip the index1 of the muxes. + */ + if ((strstr(ucontrol->id.name, "ADC 24 Mux") || + strstr(ucontrol->id.name, "ADC 25 Mux")) && val > 0) + val -= 1; + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt715_sdca_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 23 Mux")) + mask_sft = 8; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + /* Verb ID = 0x701h, nid = e->reg */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + rt715_sdca_index_read(rt715, RT715_VENDOR_HDA_CTL, + RT715_HDA_LEGACY_MUX_CTL1, &val2); + val2 = (val2 >> mask_sft) & 0xf; + + change = val != val2; + + if (change) + rt715_sdca_index_update_bits(rt715, RT715_VENDOR_HDA_CTL, + RT715_HDA_LEGACY_MUX_CTL1, 0xf << mask_sft, val << mask_sft); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL); + + return change; +} + +static const char * const adc_22_23_mux_text[] = { + "MIC1", + "MIC2", + "LINE1", + "LINE2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +/* + * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and + * 1 will be connected to the same dmic source, therefore we skip index 1 to + * avoid misunderstanding on usage of dapm routing. + */ +static int rt715_adc_24_25_values[] = { + 0, + 2, + 3, + 4, + 5, +}; + +static const char * const adc_24_mux_text[] = { + "MIC2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +static const char * const adc_25_mux_text[] = { + "MIC1", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +static SOC_ENUM_SINGLE_DECL(rt715_adc22_enum, SND_SOC_NOPM, 0, + adc_22_23_mux_text); + +static SOC_ENUM_SINGLE_DECL(rt715_adc23_enum, SND_SOC_NOPM, 0, + adc_22_23_mux_text); + +static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum, + SND_SOC_NOPM, 0, 0xf, + adc_24_mux_text, rt715_adc_24_25_values); +static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum, + SND_SOC_NOPM, 0, 0xf, + adc_25_mux_text, rt715_adc_24_25_values); + +static const struct snd_kcontrol_new rt715_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum, + rt715_sdca_mux_get, rt715_sdca_mux_put); + +static const struct snd_kcontrol_new rt715_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum, + rt715_sdca_mux_get, rt715_sdca_mux_put); + +static const struct snd_kcontrol_new rt715_adc24_mux = + SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum, + rt715_sdca_mux_get, rt715_sdca_mux_put); + +static const struct snd_kcontrol_new rt715_adc25_mux = + SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum, + rt715_sdca_mux_get, rt715_sdca_mux_put); + +static int rt715_sdca_pde23_24_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt715->regmap, + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN, + RT715_SDCA_REQ_POW_CTRL, + CH_00), 0x00); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt715->regmap, + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN, + RT715_SDCA_REQ_POW_CTRL, + CH_00), 0x03); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt715_sdca_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + + SND_SOC_DAPM_SUPPLY("PDE23_24", SND_SOC_NOPM, 0, 0, + rt715_sdca_pde23_24_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_ADC("ADC 07", NULL, SND_SOC_NOPM, 4, 0), + SND_SOC_DAPM_ADC("ADC 08", NULL, SND_SOC_NOPM, 4, 0), + SND_SOC_DAPM_ADC("ADC 09", NULL, SND_SOC_NOPM, 4, 0), + SND_SOC_DAPM_ADC("ADC 27", NULL, SND_SOC_NOPM, 4, 0), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc23_mux), + SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc24_mux), + SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc25_mux), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt715_sdca_audio_map[] = { + {"DP6TX", NULL, "ADC 09"}, + {"DP6TX", NULL, "ADC 08"}, + {"DP4TX", NULL, "ADC 07"}, + {"DP4TX", NULL, "ADC 27"}, + {"DP4TX", NULL, "ADC 09"}, + {"DP4TX", NULL, "ADC 08"}, + + {"LINE1", NULL, "PDE23_24"}, + {"LINE2", NULL, "PDE23_24"}, + {"MIC1", NULL, "PDE23_24"}, + {"MIC2", NULL, "PDE23_24"}, + {"DMIC1", NULL, "PDE23_24"}, + {"DMIC2", NULL, "PDE23_24"}, + {"DMIC3", NULL, "PDE23_24"}, + {"DMIC4", NULL, "PDE23_24"}, + + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 07", NULL, "ADC 24 Mux"}, + {"ADC 27", NULL, "ADC 25 Mux"}, + {"ADC 22 Mux", "MIC1", "MIC1"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "DMIC1", "DMIC1"}, + {"ADC 22 Mux", "DMIC2", "DMIC2"}, + {"ADC 22 Mux", "DMIC3", "DMIC3"}, + {"ADC 22 Mux", "DMIC4", "DMIC4"}, + {"ADC 23 Mux", "MIC1", "MIC1"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "DMIC1", "DMIC1"}, + {"ADC 23 Mux", "DMIC2", "DMIC2"}, + {"ADC 23 Mux", "DMIC3", "DMIC3"}, + {"ADC 23 Mux", "DMIC4", "DMIC4"}, + {"ADC 24 Mux", "MIC2", "MIC2"}, + {"ADC 24 Mux", "DMIC1", "DMIC1"}, + {"ADC 24 Mux", "DMIC2", "DMIC2"}, + {"ADC 24 Mux", "DMIC3", "DMIC3"}, + {"ADC 24 Mux", "DMIC4", "DMIC4"}, + {"ADC 25 Mux", "MIC1", "MIC1"}, + {"ADC 25 Mux", "DMIC1", "DMIC1"}, + {"ADC 25 Mux", "DMIC2", "DMIC2"}, + {"ADC 25 Mux", "DMIC3", "DMIC3"}, + {"ADC 25 Mux", "DMIC4", "DMIC4"}, +}; + +static int rt715_sdca_probe(struct snd_soc_component *component) +{ + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + int ret; + + if (!rt715->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = { + .probe = rt715_sdca_probe, + .controls = rt715_sdca_snd_controls, + .num_controls = ARRAY_SIZE(rt715_sdca_snd_controls), + .dapm_widgets = rt715_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt715_sdca_dapm_widgets), + .dapm_routes = rt715_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt715_sdca_audio_map), + .endianness = 1, +}; + +static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt715_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt715_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream; + int retval; + unsigned int val; + + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt715->slave) + return -EINVAL; + + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + switch (dai->id) { + case RT715_AIF1: + port_config.num = 6; + rt715_sdca_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL, + 0xa500); + break; + case RT715_AIF2: + port_config.num = 4; + rt715_sdca_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL, + 0xaf00); + break; + default: + dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + return -EINVAL; + } + + retval = sdw_stream_add_slave(rt715->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(component->dev, "Unable to configure port, retval:%d\n", + retval); + return retval; + } + + switch (params_rate(params)) { + case 8000: + val = 0x1; + break; + case 11025: + val = 0x2; + break; + case 12000: + val = 0x3; + break; + case 16000: + val = 0x4; + break; + case 22050: + val = 0x5; + break; + case 24000: + val = 0x6; + break; + case 32000: + val = 0x7; + break; + case 44100: + val = 0x8; + break; + case 48000: + val = 0x9; + break; + case 88200: + val = 0xa; + break; + case 96000: + val = 0xb; + break; + case 176400: + val = 0xc; + break; + case 192000: + val = 0xd; + break; + case 384000: + val = 0xe; + break; + case 768000: + val = 0xf; + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + + regmap_write(rt715->regmap, + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CS_FREQ_IND_EN, + RT715_SDCA_FREQ_IND_CTRL, CH_00), val); + + return 0; +} + +static int rt715_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt715->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt715->slave, sdw_stream); + return 0; +} + +#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt715_sdca_ops = { + .hw_params = rt715_sdca_pcm_hw_params, + .hw_free = rt715_sdca_pcm_hw_free, + .set_stream = rt715_sdca_set_sdw_stream, + .shutdown = rt715_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt715_sdca_dai[] = { + { + .name = "rt715-aif1", + .id = RT715_AIF1, + .capture = { + .stream_name = "DP6 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT715_STEREO_RATES, + .formats = RT715_FORMATS, + }, + .ops = &rt715_sdca_ops, + }, + { + .name = "rt715-aif2", + .id = RT715_AIF2, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT715_STEREO_RATES, + .formats = RT715_FORMATS, + }, + .ops = &rt715_sdca_ops, + }, +}; + +/* Bus clock frequency */ +#define RT715_CLK_FREQ_9600000HZ 9600000 +#define RT715_CLK_FREQ_12000000HZ 12000000 +#define RT715_CLK_FREQ_6000000HZ 6000000 +#define RT715_CLK_FREQ_4800000HZ 4800000 +#define RT715_CLK_FREQ_2400000HZ 2400000 +#define RT715_CLK_FREQ_12288000HZ 12288000 + +int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap, + struct regmap *regmap, struct sdw_slave *slave) +{ + struct rt715_sdca_priv *rt715; + int ret; + + rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL); + if (!rt715) + return -ENOMEM; + + dev_set_drvdata(dev, rt715); + rt715->slave = slave; + rt715->regmap = regmap; + rt715->mbq_regmap = mbq_regmap; + rt715->hw_sdw_ver = slave->id.sdw_version; + + regcache_cache_only(rt715->regmap, true); + regcache_cache_only(rt715->mbq_regmap, true); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt715->hw_init = false; + rt715->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt715_sdca, + rt715_sdca_dai, + ARRAY_SIZE(rt715_sdca_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + dev_dbg(dev, "%s\n", __func__); + + return ret; +} + +int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); + unsigned int hw_ver; + + if (rt715->hw_init) + return 0; + + regcache_cache_only(rt715->regmap, false); + regcache_cache_only(rt715->mbq_regmap, false); + + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + if (!rt715->first_hw_init) { + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + rt715->first_hw_init = true; + } + + pm_runtime_get_noresume(&slave->dev); + + rt715_sdca_index_read(rt715, RT715_VENDOR_REG, + RT715_PRODUCT_NUM, &hw_ver); + hw_ver = hw_ver & 0x000f; + + /* set clock selector = external */ + regmap_write(rt715->regmap, + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, + RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1); + /* set GPIO_4/5/6 to be 3rd/4th DMIC usage */ + if (hw_ver == 0x0) + rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG, + RT715_AD_FUNC_EN, 0x54, 0x54); + else if (hw_ver == 0x1) { + rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG, + RT715_AD_FUNC_EN, 0x55, 0x55); + rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG, + RT715_REV_1, 0x40, 0x40); + } + /* DFLL Calibration trigger */ + rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG, + RT715_DFLL_VAD, 0x1, 0x1); + /* trigger mode = VAD enable */ + regmap_write(rt715->regmap, + SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, + RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x2); + /* SMPU-1 interrupt enable mask */ + regmap_update_bits(rt715->regmap, RT715_INT_MASK, 0x1, 0x1); + + /* Mark Slave initialization complete */ + rt715->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + return 0; +} + +MODULE_DESCRIPTION("ASoC rt715 driver SDW SDCA"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h new file mode 100644 index 0000000000..e5d6928eca --- /dev/null +++ b/sound/soc/codecs/rt715-sdca.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt715-sdca.h -- RT715 ALSA SoC audio driver header + * + * Copyright(c) 2020 Realtek Semiconductor Corp. + */ + +#ifndef __RT715_SDCA_H__ +#define __RT715_SDCA_H__ + +#include +#include +#include +#include +#include +#include + +struct rt715_sdca_priv { + struct regmap *regmap; + struct regmap *mbq_regmap; + struct snd_soc_codec *codec; + struct sdw_slave *slave; + struct delayed_work adc_mute_work; + int dbg_nid; + int dbg_vid; + int dbg_payload; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + int l_is_unmute; + int r_is_unmute; + int hw_sdw_ver; + int kctl_switch_orig[4]; + int kctl_2ch_orig[2]; + int kctl_4ch_orig[4]; + int kctl_8ch_orig[8]; +}; + +struct rt715_sdca_kcontrol_private { + unsigned int reg_base; + unsigned int count; + unsigned int max; + unsigned int shift; + unsigned int invert; +}; + +/* MIPI Register */ +#define RT715_INT_CTRL 0x005a +#define RT715_INT_MASK 0x005e + +/* NID */ +#define RT715_AUDIO_FUNCTION_GROUP 0x01 +#define RT715_MIC_ADC 0x07 +#define RT715_LINE_ADC 0x08 +#define RT715_MIX_ADC 0x09 +#define RT715_DMIC1 0x12 +#define RT715_DMIC2 0x13 +#define RT715_MIC1 0x18 +#define RT715_MIC2 0x19 +#define RT715_LINE1 0x1a +#define RT715_LINE2 0x1b +#define RT715_DMIC3 0x1d +#define RT715_DMIC4 0x29 +#define RT715_VENDOR_REG 0x20 +#define RT715_MUX_IN1 0x22 +#define RT715_MUX_IN2 0x23 +#define RT715_MUX_IN3 0x24 +#define RT715_MUX_IN4 0x25 +#define RT715_MIX_ADC2 0x27 +#define RT715_INLINE_CMD 0x55 +#define RT715_VENDOR_HDA_CTL 0x61 + +/* Index (NID:20h) */ +#define RT715_PRODUCT_NUM 0x0 +#define RT715_IRQ_CTRL 0x2b +#define RT715_AD_FUNC_EN 0x36 +#define RT715_REV_1 0x37 +#define RT715_SDW_INPUT_SEL 0x39 +#define RT715_DFLL_VAD 0x44 +#define RT715_EXT_DMIC_CLK_CTRL2 0x54 + +/* Index (NID:61h) */ +#define RT715_HDA_LEGACY_MUX_CTL1 0x00 + +/* SDCA (Function) */ +#define FUN_JACK_CODEC 0x01 +#define FUN_MIC_ARRAY 0x02 +#define FUN_HID 0x03 +/* SDCA (Entity) */ +#define RT715_SDCA_ST_EN 0x00 +#define RT715_SDCA_CS_FREQ_IND_EN 0x01 +#define RT715_SDCA_FU_ADC8_9_VOL 0x02 +#define RT715_SDCA_SMPU_TRIG_ST_EN 0x05 +#define RT715_SDCA_FU_ADC10_11_VOL 0x06 +#define RT715_SDCA_FU_ADC7_27_VOL 0x0a +#define RT715_SDCA_FU_AMIC_GAIN_EN 0x0c +#define RT715_SDCA_FU_DMIC_GAIN_EN 0x0e +#define RT715_SDCA_CX_CLK_SEL_EN 0x10 +#define RT715_SDCA_CREQ_POW_EN 0x18 +/* SDCA (Control) */ +#define RT715_SDCA_ST_CTRL 0x00 +#define RT715_SDCA_CX_CLK_SEL_CTRL 0x01 +#define RT715_SDCA_REQ_POW_CTRL 0x01 +#define RT715_SDCA_FU_MUTE_CTRL 0x01 +#define RT715_SDCA_FU_VOL_CTRL 0x02 +#define RT715_SDCA_FU_DMIC_GAIN_CTRL 0x0b +#define RT715_SDCA_FREQ_IND_CTRL 0x10 +#define RT715_SDCA_SMPU_TRIG_EN_CTRL 0x10 +#define RT715_SDCA_SMPU_TRIG_ST_CTRL 0x11 +/* SDCA (Channel) */ +#define CH_00 0x00 +#define CH_01 0x01 +#define CH_02 0x02 +#define CH_03 0x03 +#define CH_04 0x04 +#define CH_05 0x05 +#define CH_06 0x06 +#define CH_07 0x07 +#define CH_08 0x08 + +#define RT715_SDCA_DB_STEP 375 + +enum { + RT715_AIF1, + RT715_AIF2, +}; + +int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +#endif /* __RT715_SDCA_H__ */ diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c new file mode 100644 index 0000000000..21f37babd1 --- /dev/null +++ b/sound/soc/codecs/rt715-sdw.c @@ -0,0 +1,592 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rt715-sdw.c -- rt715 ALSA SoC audio driver + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + * + * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rt715.h" +#include "rt715-sdw.h" + +static bool rt715_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e0 ... 0x00e5: + case 0x00ee ... 0x00ef: + case 0x00f0 ... 0x00f5: + case 0x00fe ... 0x00ff: + case 0x02e0: + case 0x02f0: + case 0x04e0: + case 0x04f0: + case 0x06e0: + case 0x06f0: + case 0x2000 ... 0x2016: + case 0x201a ... 0x2027: + case 0x2029 ... 0x202a: + case 0x202d ... 0x2034: + case 0x2200 ... 0x2204: + case 0x2206 ... 0x2212: + case 0x2220 ... 0x2223: + case 0x2230 ... 0x2239: + case 0x22f0 ... 0x22f3: + case 0x3122: + case 0x3123: + case 0x3124: + case 0x3125: + case 0x3607: + case 0x3608: + case 0x3609: + case 0x3610: + case 0x3611: + case 0x3627: + case 0x3712: + case 0x3713: + case 0x3718: + case 0x3719: + case 0x371a: + case 0x371b: + case 0x371d: + case 0x3729: + case 0x385e: + case 0x3859: + case 0x4c12: + case 0x4c13: + case 0x4c1d: + case 0x4c29: + case 0x4d12: + case 0x4d13: + case 0x4d1d: + case 0x4d29: + case 0x4e12: + case 0x4e13: + case 0x4e1d: + case 0x4e29: + case 0x4f12: + case 0x4f13: + case 0x4f1d: + case 0x4f29: + case 0x7207: + case 0x7208: + case 0x7209: + case 0x7227: + case 0x7307: + case 0x7308: + case 0x7309: + case 0x7312: + case 0x7313: + case 0x7318: + case 0x7319: + case 0x731a: + case 0x731b: + case 0x731d: + case 0x7327: + case 0x7329: + case 0x8287: + case 0x8288: + case 0x8289: + case 0x82a7: + case 0x8387: + case 0x8388: + case 0x8389: + case 0x8392: + case 0x8393: + case 0x8398: + case 0x8399: + case 0x839a: + case 0x839b: + case 0x839d: + case 0x83a7: + case 0x83a9: + case 0x752039: + return true; + default: + return false; + } +} + +static bool rt715_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00e5: + case 0x00f0: + case 0x00f3: + case 0x00f5: + case 0x2009: + case 0x2016: + case 0x201b: + case 0x201c: + case 0x201d: + case 0x201f: + case 0x2023: + case 0x2230: + case 0x200b ... 0x200e: /* i2c read */ + case 0x2012 ... 0x2015: /* HD-A read */ + case 0x202d ... 0x202f: /* BRA */ + case 0x2201 ... 0x2212: /* i2c debug */ + case 0x2220 ... 0x2223: /* decoded HD-A */ + return true; + default: + return false; + } +} + +static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val) +{ + struct device *dev = context; + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; + unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; + unsigned int is_hda_reg = 1, is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt715->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT715_PRIV_DATA_R_H | nid; + ret = regmap_write(rt715->sdw_regmap, reg3, + ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg4, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x3000) { + reg += 0x8000; + ret = regmap_write(rt715->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + reg += 0x2000; + reg |= 0x800; + ret = regmap_write(rt715->sdw_regmap, reg, + ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, + ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt715->sdw_regmap, reg, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0x9000) { + ret = regmap_write(rt715->sdw_regmap, reg, + ((*val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (*val & 0xff)); + if (ret < 0) + return ret; + } else if (mask == 0xb000) { + ret = regmap_write(rt715->sdw_regmap, reg, *val); + if (ret < 0) + return ret; + } else { + ret = regmap_read(rt715->sdw_regmap, reg, val); + if (ret < 0) + return ret; + is_hda_reg = 0; + } + + if (is_hda_reg || is_index_reg) { + sdw_data_3 = 0; + sdw_data_2 = 0; + sdw_data_1 = 0; + sdw_data_0 = 0; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_3, + &sdw_data_3); + if (ret < 0) + return ret; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_2, + &sdw_data_2); + if (ret < 0) + return ret; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_1, + &sdw_data_1); + if (ret < 0) + return ret; + ret = regmap_read(rt715->sdw_regmap, RT715_READ_HDA_0, + &sdw_data_0); + if (ret < 0) + return ret; + *val = ((sdw_data_3 & 0xff) << 24) | + ((sdw_data_2 & 0xff) << 16) | + ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); + } + + if (is_hda_reg == 0) + dev_dbg(dev, "[%s] %04x => %08x\n", __func__, reg, *val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n", __func__, + reg, reg2, reg3, reg4, *val); + else + dev_dbg(dev, "[%s] %04x %04x => %08x\n", + __func__, reg, reg2, *val); + + return 0; +} + +static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val) +{ + struct device *dev = context; + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; + unsigned int is_index_reg = 0; + int ret; + + if (reg > 0xffff) + is_index_reg = 1; + + mask = reg & 0xf000; + + if (is_index_reg) { /* index registers */ + val2 = reg & 0xff; + reg = reg >> 8; + nid = reg & 0xff; + ret = regmap_write(rt715->sdw_regmap, reg, 0); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, val2); + if (ret < 0) + return ret; + + reg3 = RT715_PRIV_DATA_W_H | nid; + ret = regmap_write(rt715->sdw_regmap, reg3, + ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg4 = reg3 + 0x1000; + reg4 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg4, (val & 0xff)); + if (ret < 0) + return ret; + is_index_reg = 1; + } else if (reg < 0x4fff) { + ret = regmap_write(rt715->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (reg == RT715_FUNC_RESET) { + ret = regmap_write(rt715->sdw_regmap, reg, val); + if (ret < 0) + return ret; + } else if (mask == 0x7000) { + ret = regmap_write(rt715->sdw_regmap, reg, + ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + reg2 = reg + 0x1000; + reg2 |= 0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, (val & 0xff)); + if (ret < 0) + return ret; + } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ + reg2 = reg - 0x1000; + reg2 &= ~0x80; + ret = regmap_write(rt715->sdw_regmap, reg2, + ((val >> 8) & 0xff)); + if (ret < 0) + return ret; + ret = regmap_write(rt715->sdw_regmap, reg, (val & 0xff)); + if (ret < 0) + return ret; + } + + if (reg2 == 0) + dev_dbg(dev, "[%s] %04x <= %04x\n", __func__, reg, val); + else if (is_index_reg) + dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n", + __func__, reg, reg2, reg3, reg4, val2, val); + else + dev_dbg(dev, "[%s] %04x %04x <= %04x\n", + __func__, reg, reg2, val); + + return 0; +} + +static const struct regmap_config rt715_regmap = { + .reg_bits = 24, + .val_bits = 32, + .readable_reg = rt715_readable_register, /* Readable registers */ + .volatile_reg = rt715_volatile_register, /* volatile register */ + .max_register = 0x752039, /* Maximum number of register */ + .reg_defaults = rt715_reg_defaults, /* Defaults */ + .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, + .reg_read = rt715_sdw_read, + .reg_write = rt715_sdw_write, +}; + +static const struct regmap_config rt715_sdw_regmap = { + .name = "sdw", + .reg_bits = 32, /* Total register space for SDW */ + .val_bits = 8, /* Total number of bits in register */ + .max_register = 0xff01, /* Maximum number of register */ + .cache_type = REGCACHE_NONE, + .use_single_read = true, + .use_single_write = true, +}; + +int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, + unsigned int *sdw_addr_h, unsigned int *sdw_data_h, + unsigned int *sdw_addr_l, unsigned int *sdw_data_l) +{ + unsigned int offset_h, offset_l, e_verb; + + if (((verb & 0xff) != 0) || verb == 0xf00) { /* 12 bits command */ + if (verb == 0x7ff) /* special case */ + offset_h = 0; + else + offset_h = 0x3000; + + if (verb & 0x800) /* get command */ + e_verb = (verb - 0xf00) | 0x80; + else /* set command */ + e_verb = (verb - 0x700); + + *sdw_data_h = payload; /* 7 bits payload */ + *sdw_addr_l = *sdw_data_l = 0; + } else { /* 4 bits command */ + if ((verb & 0x800) == 0x800) { /* read */ + offset_h = 0x9000; + offset_l = 0xa000; + } else { /* write */ + offset_h = 0x7000; + offset_l = 0x8000; + } + e_verb = verb >> 8; + *sdw_data_h = (payload >> 8); /* 16 bits payload [15:8] */ + *sdw_addr_l = (e_verb << 8) | nid | 0x80; /* 0x80: valid bit */ + *sdw_addr_l += offset_l; + *sdw_data_l = payload & 0xff; + } + + *sdw_addr_h = (e_verb << 8) | nid; + *sdw_addr_h += offset_h; + + return 0; +} +EXPORT_SYMBOL(hda_to_sdw); + +static int rt715_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt715->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt715_io_init(&slave->dev, slave); +} + +static int rt715_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval, i; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH | + SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = false; + + /* first we need to allocate memory for set bits in port lists */ + prop->source_ports = 0x50;/* BITMAP: 01010000 */ + prop->sink_ports = 0x0; /* BITMAP: 00000000 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + dpn = prop->src_dpn_prop; + i = 0; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 20; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt715_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); + int ret; + + memcpy(&rt715->params, params, sizeof(*params)); + + ret = rt715_clock_config(&slave->dev); + if (ret < 0) + dev_err(&slave->dev, "Invalid clk config"); + + return 0; +} + +static const struct sdw_slave_ops rt715_slave_ops = { + .read_prop = rt715_read_prop, + .update_status = rt715_update_status, + .bus_config = rt715_bus_config, +}; + +static int rt715_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *sdw_regmap, *regmap; + + /* Regmap Initialization */ + sdw_regmap = devm_regmap_init_sdw(slave, &rt715_sdw_regmap); + if (IS_ERR(sdw_regmap)) + return PTR_ERR(sdw_regmap); + + regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev, + &rt715_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt715_init(&slave->dev, sdw_regmap, regmap, slave); +} + +static int rt715_sdw_remove(struct sdw_slave *slave) +{ + pm_runtime_disable(&slave->dev); + + return 0; +} + +static const struct sdw_device_id rt715_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x2, 0, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x2, 0, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt715_id); + +static int __maybe_unused rt715_dev_suspend(struct device *dev) +{ + struct rt715_priv *rt715 = dev_get_drvdata(dev); + + if (!rt715->hw_init) + return 0; + + regcache_cache_only(rt715->regmap, true); + + return 0; +} + +#define RT715_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt715_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt715->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT715_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt715->regmap, false); + regcache_sync_region(rt715->regmap, 0x3000, 0x8fff); + regcache_sync_region(rt715->regmap, 0x752039, 0x752039); + + return 0; +} + +static const struct dev_pm_ops rt715_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) + SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) +}; + +static struct sdw_driver rt715_sdw_driver = { + .driver = { + .name = "rt715", + .owner = THIS_MODULE, + .pm = &rt715_pm, + }, + .probe = rt715_sdw_probe, + .remove = rt715_sdw_remove, + .ops = &rt715_slave_ops, + .id_table = rt715_id, +}; +module_sdw_driver(rt715_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT715 driver SDW"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdw.h b/sound/soc/codecs/rt715-sdw.h new file mode 100644 index 0000000000..5d7661e335 --- /dev/null +++ b/sound/soc/codecs/rt715-sdw.h @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt715-sdw.h -- RT715 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT715_SDW_H__ +#define __RT715_SDW_H__ + +static const struct reg_default rt715_reg_defaults[] = { + { 0x0000, 0x00 }, + { 0x0001, 0x00 }, + { 0x0002, 0x00 }, + { 0x0003, 0x00 }, + { 0x0004, 0x00 }, + { 0x0005, 0x01 }, + { 0x0020, 0x00 }, + { 0x0022, 0x00 }, + { 0x0023, 0x00 }, + { 0x0024, 0x00 }, + { 0x0025, 0x00 }, + { 0x0026, 0x00 }, + { 0x0030, 0x00 }, + { 0x0032, 0x00 }, + { 0x0033, 0x00 }, + { 0x0034, 0x00 }, + { 0x0035, 0x00 }, + { 0x0036, 0x00 }, + { 0x0040, 0x00 }, + { 0x0041, 0x00 }, + { 0x0042, 0x00 }, + { 0x0043, 0x00 }, + { 0x0044, 0x20 }, + { 0x0045, 0x01 }, + { 0x0046, 0x00 }, + { 0x0050, 0x20 }, + { 0x0051, 0x02 }, + { 0x0052, 0x5d }, + { 0x0053, 0x07 }, + { 0x0054, 0x15 }, + { 0x0055, 0x00 }, + { 0x0060, 0x00 }, + { 0x0070, 0x00 }, + { 0x0080, 0x00 }, + { 0x0088, 0x10 }, + { 0x00e0, 0x00 }, + { 0x00e1, 0x00 }, + { 0x00e2, 0x00 }, + { 0x00e3, 0x00 }, + { 0x00e4, 0x00 }, + { 0x00e5, 0x00 }, + { 0x00ee, 0x00 }, + { 0x00ef, 0x00 }, + { 0x00f0, 0x00 }, + { 0x00f1, 0x00 }, + { 0x00f2, 0x00 }, + { 0x00f3, 0x00 }, + { 0x00f4, 0x00 }, + { 0x00f5, 0x00 }, + { 0x00fe, 0x00 }, + { 0x00ff, 0x00 }, + { 0x0200, 0x00 }, + { 0x0201, 0x00 }, + { 0x0202, 0x20 }, + { 0x0203, 0x00 }, + { 0x0204, 0x00 }, + { 0x0205, 0x03 }, + { 0x0220, 0x00 }, + { 0x0221, 0x00 }, + { 0x0222, 0x00 }, + { 0x0223, 0x00 }, + { 0x0224, 0x00 }, + { 0x0225, 0x00 }, + { 0x0226, 0x00 }, + { 0x0227, 0x00 }, + { 0x0230, 0x00 }, + { 0x0231, 0x00 }, + { 0x0232, 0x00 }, + { 0x0233, 0x00 }, + { 0x0234, 0x00 }, + { 0x0235, 0x00 }, + { 0x0236, 0x00 }, + { 0x0237, 0x00 }, + { 0x02e0, 0x00 }, + { 0x02f0, 0x00 }, + { 0x0400, 0x00 }, + { 0x0401, 0x00 }, + { 0x0402, 0x20 }, + { 0x0403, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x0f }, + { 0x0420, 0x00 }, + { 0x0421, 0x00 }, + { 0x0422, 0x00 }, + { 0x0423, 0x00 }, + { 0x0424, 0x00 }, + { 0x0425, 0x00 }, + { 0x0426, 0x00 }, + { 0x0427, 0x00 }, + { 0x0430, 0x00 }, + { 0x0431, 0x00 }, + { 0x0432, 0x00 }, + { 0x0433, 0x00 }, + { 0x0434, 0x00 }, + { 0x0435, 0x00 }, + { 0x0436, 0x00 }, + { 0x0437, 0x00 }, + { 0x04e0, 0x00 }, + { 0x04f0, 0x00 }, + { 0x0600, 0x00 }, + { 0x0601, 0x00 }, + { 0x0602, 0x20 }, + { 0x0603, 0x00 }, + { 0x0604, 0x00 }, + { 0x0605, 0xff }, + { 0x0620, 0x00 }, + { 0x0621, 0x00 }, + { 0x0622, 0x00 }, + { 0x0623, 0x00 }, + { 0x0624, 0x00 }, + { 0x0625, 0x00 }, + { 0x0626, 0x00 }, + { 0x0627, 0x00 }, + { 0x0630, 0x00 }, + { 0x0631, 0x00 }, + { 0x0632, 0x00 }, + { 0x0633, 0x00 }, + { 0x0634, 0x00 }, + { 0x0635, 0x00 }, + { 0x0636, 0x00 }, + { 0x0637, 0x00 }, + { 0x06e0, 0x00 }, + { 0x06f0, 0x00 }, + { 0x0f00, 0x00 }, + { 0x0f01, 0x00 }, + { 0x0f02, 0x00 }, + { 0x0f03, 0x00 }, + { 0x0f04, 0x00 }, + { 0x0f05, 0xff }, + { 0x0f06, 0x00 }, + { 0x0f07, 0x00 }, + { 0x0f08, 0x00 }, + { 0x0f09, 0x00 }, + { 0x0f0a, 0x00 }, + { 0x0f0b, 0x00 }, + { 0x0f0c, 0x00 }, + { 0x0f0d, 0x00 }, + { 0x0f0e, 0x00 }, + { 0x0f0f, 0x00 }, + { 0x0f10, 0x00 }, + { 0x0f11, 0x00 }, + { 0x0f12, 0x00 }, + { 0x0f13, 0x00 }, + { 0x0f14, 0x00 }, + { 0x0f15, 0x00 }, + { 0x0f16, 0x00 }, + { 0x0f17, 0x00 }, + { 0x0f18, 0x00 }, + { 0x0f19, 0x00 }, + { 0x0f1a, 0x00 }, + { 0x0f1b, 0x00 }, + { 0x0f1c, 0x00 }, + { 0x0f1d, 0x00 }, + { 0x0f1e, 0x00 }, + { 0x0f1f, 0x00 }, + { 0x0f20, 0x00 }, + { 0x0f21, 0x00 }, + { 0x0f22, 0x00 }, + { 0x0f23, 0x00 }, + { 0x0f24, 0x00 }, + { 0x0f25, 0x00 }, + { 0x0f26, 0x00 }, + { 0x0f27, 0x00 }, + { 0x0f30, 0x00 }, + { 0x0f31, 0x00 }, + { 0x0f32, 0x00 }, + { 0x0f33, 0x00 }, + { 0x0f34, 0x00 }, + { 0x0f35, 0x00 }, + { 0x0f36, 0x00 }, + { 0x0f37, 0x00 }, + { 0x2000, 0x00 }, + { 0x2001, 0x00 }, + { 0x2002, 0x00 }, + { 0x2003, 0x00 }, + { 0x2004, 0x00 }, + { 0x2005, 0x00 }, + { 0x2006, 0x00 }, + { 0x2007, 0x00 }, + { 0x2008, 0x00 }, + { 0x2009, 0x03 }, + { 0x200a, 0x00 }, + { 0x200b, 0x00 }, + { 0x200c, 0x00 }, + { 0x200d, 0x00 }, + { 0x200e, 0x00 }, + { 0x200f, 0x10 }, + { 0x2010, 0x00 }, + { 0x2011, 0x00 }, + { 0x2012, 0x00 }, + { 0x2013, 0x00 }, + { 0x2014, 0x00 }, + { 0x2015, 0x00 }, + { 0x2016, 0x00 }, + { 0x201a, 0x00 }, + { 0x201b, 0x00 }, + { 0x201c, 0x00 }, + { 0x201d, 0x00 }, + { 0x201e, 0x00 }, + { 0x201f, 0x00 }, + { 0x2020, 0x00 }, + { 0x2021, 0x00 }, + { 0x2022, 0x00 }, + { 0x2023, 0x00 }, + { 0x2024, 0x00 }, + { 0x2025, 0x01 }, + { 0x2026, 0x00 }, + { 0x2027, 0x00 }, + { 0x2029, 0x00 }, + { 0x202a, 0x00 }, + { 0x202d, 0x00 }, + { 0x202e, 0x00 }, + { 0x202f, 0x00 }, + { 0x2030, 0x00 }, + { 0x2031, 0x00 }, + { 0x2032, 0x00 }, + { 0x2033, 0x00 }, + { 0x2034, 0x00 }, + { 0x2200, 0x00 }, + { 0x2201, 0x00 }, + { 0x2202, 0x00 }, + { 0x2203, 0x00 }, + { 0x2204, 0x00 }, + { 0x2206, 0x00 }, + { 0x2207, 0x00 }, + { 0x2208, 0x00 }, + { 0x2209, 0x00 }, + { 0x220a, 0x00 }, + { 0x220b, 0x00 }, + { 0x220c, 0x00 }, + { 0x220d, 0x00 }, + { 0x220e, 0x00 }, + { 0x220f, 0x00 }, + { 0x2210, 0x00 }, + { 0x2211, 0x00 }, + { 0x2212, 0x00 }, + { 0x2220, 0x00 }, + { 0x2221, 0x00 }, + { 0x2222, 0x00 }, + { 0x2223, 0x00 }, + { 0x2230, 0x00 }, + { 0x2231, 0x0f }, + { 0x2232, 0x00 }, + { 0x2233, 0x00 }, + { 0x2234, 0x00 }, + { 0x2235, 0x00 }, + { 0x2236, 0x00 }, + { 0x2237, 0x00 }, + { 0x2238, 0x00 }, + { 0x2239, 0x00 }, + { 0x22f0, 0x00 }, + { 0x22f1, 0x00 }, + { 0x22f2, 0x00 }, + { 0x22f3, 0x00 }, + { 0x3122, 0x02 }, + { 0x3123, 0x03 }, + { 0x3124, 0x00 }, + { 0x3125, 0x01 }, + { 0x3607, 0x00 }, + { 0x3608, 0x00 }, + { 0x3609, 0x00 }, + { 0x3610, 0x00 }, + { 0x3611, 0x00 }, + { 0x3627, 0x00 }, + { 0x3712, 0x00 }, + { 0x3713, 0x00 }, + { 0x3718, 0x00 }, + { 0x3719, 0x00 }, + { 0x371a, 0x00 }, + { 0x371b, 0x00 }, + { 0x371d, 0x00 }, + { 0x3729, 0x00 }, + { 0x385e, 0x00 }, + { 0x3859, 0x00 }, + { 0x4c12, 0x411111f0 }, + { 0x4c13, 0x411111f0 }, + { 0x4c1d, 0x411111f0 }, + { 0x4c29, 0x411111f0 }, + { 0x4d12, 0x411111f0 }, + { 0x4d13, 0x411111f0 }, + { 0x4d1d, 0x411111f0 }, + { 0x4d29, 0x411111f0 }, + { 0x4e12, 0x411111f0 }, + { 0x4e13, 0x411111f0 }, + { 0x4e1d, 0x411111f0 }, + { 0x4e29, 0x411111f0 }, + { 0x4f12, 0x411111f0 }, + { 0x4f13, 0x411111f0 }, + { 0x4f1d, 0x411111f0 }, + { 0x4f29, 0x411111f0 }, + { 0x7207, 0x00 }, + { 0x8287, 0x00 }, + { 0x7208, 0x00 }, + { 0x8288, 0x00 }, + { 0x7209, 0x00 }, + { 0x8289, 0x00 }, + { 0x7227, 0x00 }, + { 0x82a7, 0x00 }, + { 0x7307, 0x97 }, + { 0x8387, 0x97 }, + { 0x7308, 0x97 }, + { 0x8388, 0x97 }, + { 0x7309, 0x97 }, + { 0x8389, 0x97 }, + { 0x7312, 0x00 }, + { 0x8392, 0x00 }, + { 0x7313, 0x00 }, + { 0x8393, 0x00 }, + { 0x7318, 0x00 }, + { 0x8398, 0x00 }, + { 0x7319, 0x00 }, + { 0x8399, 0x00 }, + { 0x731a, 0x00 }, + { 0x839a, 0x00 }, + { 0x731b, 0x00 }, + { 0x839b, 0x00 }, + { 0x731d, 0x00 }, + { 0x839d, 0x00 }, + { 0x7327, 0x97 }, + { 0x83a7, 0x97 }, + { 0x7329, 0x00 }, + { 0x83a9, 0x00 }, + { 0x752039, 0xa500 }, +}; + +#endif /* __RT715_H__ */ diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c new file mode 100644 index 0000000000..b59230c8fd --- /dev/null +++ b/sound/soc/codecs/rt715.c @@ -0,0 +1,1151 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rt715.c -- rt715 ALSA SoC audio driver + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + * + * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt715.h" + +static int rt715_index_write(struct regmap *regmap, unsigned int reg, + unsigned int value) +{ + int ret; + unsigned int addr = ((RT715_PRIV_INDEX_W_H) << 8) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) { + pr_err("Failed to set private value: %08x <= %04x %d\n", ret, + addr, value); + } + + return ret; +} + +static int rt715_index_write_nid(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int value) +{ + int ret; + unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + pr_err("Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +static int rt715_index_read_nid(struct regmap *regmap, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg; + + *value = 0; + ret = regmap_read(regmap, addr, value); + if (ret < 0) + pr_err("Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt715_index_update_bits(struct regmap *regmap, unsigned int nid, + unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp, orig; + int ret; + + ret = rt715_index_read_nid(regmap, nid, reg, &orig); + if (ret < 0) + return ret; + + tmp = orig & ~mask; + tmp |= val & mask; + + return rt715_index_write_nid(regmap, nid, reg, tmp); +} + +static void rt715_reset(struct regmap *regmap) +{ + regmap_write(regmap, RT715_FUNC_RESET, 0); + rt715_index_update_bits(regmap, RT715_VENDOR_REGISTERS, + RT715_VD_CLEAR_CTRL, RT715_CLEAR_HIDDEN_REG, + RT715_CLEAR_HIDDEN_REG); +} + + +static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h, + unsigned int addr_l, unsigned int val_h, + unsigned int *r_val, unsigned int *l_val) +{ + int ret; + /* R Channel */ + *r_val = val_h << 8; + ret = regmap_read(rt715->regmap, addr_l, r_val); + if (ret < 0) + pr_err("Failed to get R channel gain.\n"); + + /* L Channel */ + val_h |= 0x20; + *l_val = val_h << 8; + ret = regmap_read(rt715->regmap, addr_h, l_val); + if (ret < 0) + pr_err("Failed to get L channel gain.\n"); +} + +/* For Verb-Set Amplifier Gain (Verb ID = 3h) */ +static int rt715_set_amp_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + unsigned int addr_h, addr_l, val_h, val_ll, val_lr; + unsigned int read_ll, read_rl, i; + unsigned int k_vol_changed = 0; + + for (i = 0; i < 2; i++) { + if (ucontrol->value.integer.value[i] != rt715->kctl_2ch_vol_ori[i]) { + k_vol_changed = 1; + break; + } + } + + /* Can't use update bit function, so read the original value first */ + addr_h = mc->reg; + addr_l = mc->rreg; + + if (mc->shift == RT715_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* L Channel */ + rt715->kctl_2ch_vol_ori[0] = ucontrol->value.integer.value[0]; + /* for gain */ + val_ll = ((ucontrol->value.integer.value[0]) & 0x7f); + if (val_ll > mc->max) + val_ll = mc->max; + /* keep mute status */ + val_ll |= read_ll & 0x80; + + /* R Channel */ + rt715->kctl_2ch_vol_ori[1] = ucontrol->value.integer.value[1]; + /* for gain */ + val_lr = ((ucontrol->value.integer.value[1]) & 0x7f); + if (val_lr > mc->max) + val_lr = mc->max; + /* keep mute status */ + val_lr |= read_rl & 0x80; + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << mc->shift) | (3 << 4); + regmap_write(rt715->regmap, addr_h, + (val_h << 8) | val_ll); + regmap_write(rt715->regmap, addr_l, + (val_h << 8) | val_ll); + } else { + /* Lch*/ + val_h = (1 << mc->shift) | (1 << 5); + regmap_write(rt715->regmap, addr_h, + (val_h << 8) | val_ll); + /* Rch */ + val_h = (1 << mc->shift) | (1 << 4); + regmap_write(rt715->regmap, addr_l, + (val_h << 8) | val_lr); + } + /* check result */ + if (mc->shift == RT715_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt715_get_gain(rt715, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + + /* D0:power on state, D3: power saving mode */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + return k_vol_changed; +} + +static int rt715_set_amp_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int addr_h, addr_l, val_h; + unsigned int read_ll, read_rl; + + addr_h = mc->reg; + addr_l = mc->rreg; + if (mc->shift == RT715_DIR_OUT_SFT) /* output */ + val_h = 0x80; + else /* input */ + val_h = 0x0; + + rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (mc->invert) { + /* for mute status */ + read_ll = !(read_ll & 0x80); + read_rl = !(read_rl & 0x80); + } else { + /* for gain */ + read_ll = read_ll & 0x7f; + read_rl = read_rl & 0x7f; + } + ucontrol->value.integer.value[0] = read_ll; + ucontrol->value.integer.value[1] = read_rl; + + return 0; +} + +static int rt715_set_main_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + static const unsigned int capture_reg_H[] = { + RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H, + RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H }; + static const unsigned int capture_reg_L[] = { + RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L, + RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L }; + unsigned int addr_h, addr_l, val_h = 0x0, val_ll, val_lr; + unsigned int k_shift = RT715_DIR_IN_SFT, k_changed = 0; + unsigned int read_ll, read_rl, i, j, loop_cnt = 4; + + for (i = 0; i < 8; i++) { + if (ucontrol->value.integer.value[i] != rt715->kctl_8ch_switch_ori[i]) + k_changed = 1; + } + + for (j = 0; j < loop_cnt; j++) { + /* Can't use update bit function, so read the original value first */ + addr_h = capture_reg_H[j]; + addr_l = capture_reg_L[j]; + rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* L Channel */ + /* for mute */ + rt715->kctl_8ch_switch_ori[j * 2] = + ucontrol->value.integer.value[j * 2]; + val_ll = (!ucontrol->value.integer.value[j * 2]) << 7; + /* keep gain */ + val_ll |= read_ll & 0x7f; + + /* R Channel */ + /* for mute */ + rt715->kctl_8ch_switch_ori[j * 2 + 1] = + ucontrol->value.integer.value[j * 2 + 1]; + val_lr = (!ucontrol->value.integer.value[j * 2 + 1]) << 7; + /* keep gain */ + val_lr |= read_rl & 0x7f; + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << k_shift) | (3 << 4); + regmap_write(rt715->regmap, addr_h, + (val_h << 8) | val_ll); + regmap_write(rt715->regmap, addr_l, + (val_h << 8) | val_ll); + } else { + /* Lch*/ + val_h = (1 << k_shift) | (1 << 5); + regmap_write(rt715->regmap, addr_h, + (val_h << 8) | val_ll); + /* Rch */ + val_h = (1 << k_shift) | (1 << 4); + regmap_write(rt715->regmap, addr_l, + (val_h << 8) | val_lr); + } + val_h = 0x0; + rt715_get_gain(rt715, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + } + + /* D0:power on state, D3: power saving mode */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + return k_changed; +} + +static int rt715_set_main_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + static const unsigned int capture_reg_H[] = { + RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H, + RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H }; + static const unsigned int capture_reg_L[] = { + RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L, + RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L }; + unsigned int addr_h, addr_l, val_h = 0x0, i, loop_cnt = 4; + unsigned int read_ll, read_rl; + + for (i = 0; i < loop_cnt; i++) { + addr_h = capture_reg_H[i]; + addr_l = capture_reg_L[i]; + rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll); + + ucontrol->value.integer.value[i * 2] = !(read_ll & 0x80); + ucontrol->value.integer.value[i * 2 + 1] = !(read_rl & 0x80); + } + + return 0; +} + +static int rt715_set_main_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + static const unsigned int capture_reg_H[] = { + RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H, + RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H }; + static const unsigned int capture_reg_L[] = { + RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L, + RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L}; + unsigned int addr_h, addr_l, val_h = 0x0, val_ll, val_lr; + unsigned int read_ll, read_rl, i, j, loop_cnt = 4, k_changed = 0; + unsigned int k_shift = RT715_DIR_IN_SFT, k_max = 0x3f; + + for (i = 0; i < 8; i++) { + if (ucontrol->value.integer.value[i] != rt715->kctl_8ch_vol_ori[i]) + k_changed = 1; + } + + for (j = 0; j < loop_cnt; j++) { + addr_h = capture_reg_H[j]; + addr_l = capture_reg_L[j]; + rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll); + + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + + /* L Channel */ + /* for gain */ + rt715->kctl_8ch_vol_ori[j * 2] = ucontrol->value.integer.value[j * 2]; + val_ll = ((ucontrol->value.integer.value[j * 2]) & 0x7f); + if (val_ll > k_max) + val_ll = k_max; + /* keep mute status */ + val_ll |= read_ll & 0x80; + + /* R Channel */ + /* for gain */ + rt715->kctl_8ch_vol_ori[j * 2 + 1] = + ucontrol->value.integer.value[j * 2 + 1]; + val_lr = ((ucontrol->value.integer.value[j * 2 + 1]) & 0x7f); + if (val_lr > k_max) + val_lr = k_max; + /* keep mute status */ + val_lr |= read_rl & 0x80; + + for (i = 0; i < 3; i++) { /* retry 3 times at most */ + if (val_ll == val_lr) { + /* Set both L/R channels at the same time */ + val_h = (1 << k_shift) | (3 << 4); + regmap_write(rt715->regmap, addr_h, + (val_h << 8) | val_ll); + regmap_write(rt715->regmap, addr_l, + (val_h << 8) | val_ll); + } else { + /* Lch*/ + val_h = (1 << k_shift) | (1 << 5); + regmap_write(rt715->regmap, addr_h, + (val_h << 8) | val_ll); + /* Rch */ + val_h = (1 << k_shift) | (1 << 4); + regmap_write(rt715->regmap, addr_l, + (val_h << 8) | val_lr); + } + val_h = 0x0; + rt715_get_gain(rt715, addr_h, addr_l, val_h, + &read_rl, &read_ll); + if (read_rl == val_lr && read_ll == val_ll) + break; + } + } + + /* D0:power on state, D3: power saving mode */ + if (dapm->bias_level <= SND_SOC_BIAS_STANDBY) + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + return k_changed; +} + +static int rt715_set_main_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + static const unsigned int capture_reg_H[] = { + RT715_SET_GAIN_MIC_ADC_H, RT715_SET_GAIN_LINE_ADC_H, + RT715_SET_GAIN_MIX_ADC_H, RT715_SET_GAIN_MIX_ADC2_H }; + static const unsigned int capture_reg_L[] = { + RT715_SET_GAIN_MIC_ADC_L, RT715_SET_GAIN_LINE_ADC_L, + RT715_SET_GAIN_MIX_ADC_L, RT715_SET_GAIN_MIX_ADC2_L }; + unsigned int addr_h, addr_l, val_h = 0x0, i, loop_cnt = 4; + unsigned int read_ll, read_rl; + + for (i = 0; i < loop_cnt; i++) { + addr_h = capture_reg_H[i]; + addr_l = capture_reg_L[i]; + rt715_get_gain(rt715, addr_h, addr_l, val_h, &read_rl, &read_ll); + + ucontrol->value.integer.value[i * 2] = read_ll & 0x7f; + ucontrol->value.integer.value[i * 2 + 1] = read_rl & 0x7f; + } + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); + +static int rt715_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 8; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int rt715_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 8; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x3f; + return 0; +} + +#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ + xmax, xinvert) } + +#define RT715_MAIN_SWITCH_EXT(xname, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = rt715_switch_info, \ + .get = xhandler_get, .put = xhandler_put, \ +} + +#define RT715_MAIN_VOL_EXT_TLV(xname, xhandler_get, xhandler_put, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt715_vol_info, \ + .get = xhandler_get, .put = xhandler_put, \ +} + +static const struct snd_kcontrol_new rt715_snd_controls[] = { + /* Capture switch */ + RT715_MAIN_SWITCH_EXT("Capture Switch", + rt715_set_main_switch_get, rt715_set_main_switch_put), + /* Volume Control */ + RT715_MAIN_VOL_EXT_TLV("Capture Volume", + rt715_set_main_vol_get, rt715_set_main_vol_put, in_vol_tlv), + /* MIC Boost Control */ + SOC_DOUBLE_R_EXT_TLV("DMIC1 Boost", RT715_SET_GAIN_DMIC1_H, + RT715_SET_GAIN_DMIC1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC2 Boost", RT715_SET_GAIN_DMIC2_H, + RT715_SET_GAIN_DMIC2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC3 Boost", RT715_SET_GAIN_DMIC3_H, + RT715_SET_GAIN_DMIC3_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("DMIC4 Boost", RT715_SET_GAIN_DMIC4_H, + RT715_SET_GAIN_DMIC4_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("MIC1 Boost", RT715_SET_GAIN_MIC1_H, + RT715_SET_GAIN_MIC1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("MIC2 Boost", RT715_SET_GAIN_MIC2_H, + RT715_SET_GAIN_MIC2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("LINE1 Boost", RT715_SET_GAIN_LINE1_H, + RT715_SET_GAIN_LINE1_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("LINE2 Boost", RT715_SET_GAIN_LINE2_H, + RT715_SET_GAIN_LINE2_L, RT715_DIR_IN_SFT, 3, 0, + rt715_set_amp_gain_get, rt715_set_amp_gain_put, + mic_vol_tlv), +}; + +static int rt715_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int reg, val; + int ret; + + /* nid = e->reg, vid = 0xf01 */ + reg = RT715_VERB_SET_CONNECT_SEL | e->reg; + ret = regmap_read(rt715->regmap, reg, &val); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); + return ret; + } + + /* + * The first two indices of ADC Mux 24/25 are routed to the same + * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2. + * To have a unique set of inputs, we skip the index1 of the muxes. + */ + if ((e->reg == RT715_MUX_IN3 || e->reg == RT715_MUX_IN4) && (val > 0)) + val -= 1; + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int rt715_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, reg; + int ret; + + if (item[0] >= e->items) + return -EINVAL; + + /* Verb ID = 0x701h, nid = e->reg */ + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + reg = RT715_VERB_SET_CONNECT_SEL | e->reg; + ret = regmap_read(rt715->regmap, reg, &val2); + if (ret < 0) { + dev_err(component->dev, "%s: sdw read failed: %d\n", + __func__, ret); + return ret; + } + + if (val == val2) + change = 0; + else + change = 1; + + if (change) { + reg = RT715_VERB_SET_CONNECT_SEL | e->reg; + regmap_write(rt715->regmap, reg, val); + } + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc_22_23_mux_text[] = { + "MIC1", + "MIC2", + "LINE1", + "LINE2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +/* + * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and + * 1 will be connected to the same dmic source, therefore we skip index 1 to + * avoid misunderstanding on usage of dapm routing. + */ +static const unsigned int rt715_adc_24_25_values[] = { + 0, + 2, + 3, + 4, + 5, +}; + +static const char * const adc_24_mux_text[] = { + "MIC2", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +static const char * const adc_25_mux_text[] = { + "MIC1", + "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", +}; + +static SOC_ENUM_SINGLE_DECL( + rt715_adc22_enum, RT715_MUX_IN1, 0, adc_22_23_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt715_adc23_enum, RT715_MUX_IN2, 0, adc_22_23_mux_text); + +static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum, + RT715_MUX_IN3, 0, 0xf, + adc_24_mux_text, rt715_adc_24_25_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum, + RT715_MUX_IN4, 0, 0xf, + adc_25_mux_text, rt715_adc_24_25_values); + +static const struct snd_kcontrol_new rt715_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc23_mux = + SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc24_mux = + SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_kcontrol_new rt715_adc25_mux = + SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum, + rt715_mux_get, rt715_mux_put); + +static const struct snd_soc_dapm_widget rt715_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_ADC("ADC 07", NULL, RT715_SET_STREAMID_MIC_ADC, 4, 0), + SND_SOC_DAPM_ADC("ADC 08", NULL, RT715_SET_STREAMID_LINE_ADC, 4, 0), + SND_SOC_DAPM_ADC("ADC 09", NULL, RT715_SET_STREAMID_MIX_ADC, 4, 0), + SND_SOC_DAPM_ADC("ADC 27", NULL, RT715_SET_STREAMID_MIX_ADC2, 4, 0), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc22_mux), + SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc23_mux), + SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc24_mux), + SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, + &rt715_adc25_mux), + SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt715_audio_map[] = { + {"DP6TX", NULL, "ADC 09"}, + {"DP6TX", NULL, "ADC 08"}, + {"DP4TX", NULL, "ADC 07"}, + {"DP4TX", NULL, "ADC 27"}, + {"ADC 09", NULL, "ADC 22 Mux"}, + {"ADC 08", NULL, "ADC 23 Mux"}, + {"ADC 07", NULL, "ADC 24 Mux"}, + {"ADC 27", NULL, "ADC 25 Mux"}, + {"ADC 22 Mux", "MIC1", "MIC1"}, + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 22 Mux", "DMIC1", "DMIC1"}, + {"ADC 22 Mux", "DMIC2", "DMIC2"}, + {"ADC 22 Mux", "DMIC3", "DMIC3"}, + {"ADC 22 Mux", "DMIC4", "DMIC4"}, + {"ADC 23 Mux", "MIC1", "MIC1"}, + {"ADC 23 Mux", "MIC2", "MIC2"}, + {"ADC 23 Mux", "LINE1", "LINE1"}, + {"ADC 23 Mux", "LINE2", "LINE2"}, + {"ADC 23 Mux", "DMIC1", "DMIC1"}, + {"ADC 23 Mux", "DMIC2", "DMIC2"}, + {"ADC 23 Mux", "DMIC3", "DMIC3"}, + {"ADC 23 Mux", "DMIC4", "DMIC4"}, + {"ADC 24 Mux", "MIC2", "MIC2"}, + {"ADC 24 Mux", "DMIC1", "DMIC1"}, + {"ADC 24 Mux", "DMIC2", "DMIC2"}, + {"ADC 24 Mux", "DMIC3", "DMIC3"}, + {"ADC 24 Mux", "DMIC4", "DMIC4"}, + {"ADC 25 Mux", "MIC1", "MIC1"}, + {"ADC 25 Mux", "DMIC1", "DMIC1"}, + {"ADC 25 Mux", "DMIC2", "DMIC2"}, + {"ADC 25 Mux", "DMIC3", "DMIC3"}, + {"ADC 25 Mux", "DMIC4", "DMIC4"}, +}; + +static int rt715_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, + AC_PWRST_D0); + msleep(RT715_POWER_UP_DELAY_MS); + } + break; + + case SND_SOC_BIAS_STANDBY: + regmap_write(rt715->regmap, + RT715_SET_AUDIO_POWER_STATE, + AC_PWRST_D3); + break; + + default: + break; + } + dapm->bias_level = level; + return 0; +} + +static int rt715_probe(struct snd_soc_component *component) +{ + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + int ret; + + if (!rt715->first_hw_init) + return 0; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_rt715 = { + .probe = rt715_probe, + .set_bias_level = rt715_set_bias_level, + .controls = rt715_snd_controls, + .num_controls = ARRAY_SIZE(rt715_snd_controls), + .dapm_widgets = rt715_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets), + .dapm_routes = rt715_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt715_audio_map), + .endianness = 1, +}; + +static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt715_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) + +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream; + int retval; + unsigned int val = 0; + + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt715->slave) + return -EINVAL; + + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + switch (dai->id) { + case RT715_AIF1: + port_config.num = 6; + rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa500); + break; + case RT715_AIF2: + port_config.num = 4; + rt715_index_write(rt715->regmap, RT715_SDW_INPUT_SEL, 0xa000); + break; + default: + dev_err(component->dev, "Invalid DAI id %d\n", dai->id); + return -EINVAL; + } + + retval = sdw_stream_add_slave(rt715->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + switch (params_rate(params)) { + /* bit 14 0:48K 1:44.1K */ + /* bit 15 Stream Type 0:PCM 1:Non-PCM, should always be PCM */ + case 44100: + val |= 0x40 << 8; + break; + case 48000: + val |= 0x0 << 8; + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", + params_rate(params)); + return -EINVAL; + } + + if (params_channels(params) <= 16) { + /* bit 3:0 Number of Channel */ + val |= (params_channels(params) - 1); + } else { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_width(params)) { + /* bit 6:4 Bits per Sample */ + case 8: + break; + case 16: + val |= (0x1 << 4); + break; + case 20: + val |= (0x2 << 4); + break; + case 24: + val |= (0x3 << 4); + break; + case 32: + val |= (0x4 << 4); + break; + default: + return -EINVAL; + } + + regmap_write(rt715->regmap, RT715_MIC_ADC_FORMAT_H, val); + regmap_write(rt715->regmap, RT715_MIC_LINE_FORMAT_H, val); + regmap_write(rt715->regmap, RT715_MIX_ADC_FORMAT_H, val); + regmap_write(rt715->regmap, RT715_MIX_ADC2_FORMAT_H, val); + + return retval; +} + +static int rt715_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt715->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt715->slave, sdw_stream); + return 0; +} + +#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +static const struct snd_soc_dai_ops rt715_ops = { + .hw_params = rt715_pcm_hw_params, + .hw_free = rt715_pcm_hw_free, + .set_stream = rt715_set_sdw_stream, + .shutdown = rt715_shutdown, +}; + +static struct snd_soc_dai_driver rt715_dai[] = { + { + .name = "rt715-aif1", + .id = RT715_AIF1, + .capture = { + .stream_name = "DP6 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT715_STEREO_RATES, + .formats = RT715_FORMATS, + }, + .ops = &rt715_ops, + }, + { + .name = "rt715-aif2", + .id = RT715_AIF2, + .capture = { + .stream_name = "DP4 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT715_STEREO_RATES, + .formats = RT715_FORMATS, + }, + .ops = &rt715_ops, + }, +}; + +/* Bus clock frequency */ +#define RT715_CLK_FREQ_9600000HZ 9600000 +#define RT715_CLK_FREQ_12000000HZ 12000000 +#define RT715_CLK_FREQ_6000000HZ 6000000 +#define RT715_CLK_FREQ_4800000HZ 4800000 +#define RT715_CLK_FREQ_2400000HZ 2400000 +#define RT715_CLK_FREQ_12288000HZ 12288000 + +int rt715_clock_config(struct device *dev) +{ + struct rt715_priv *rt715 = dev_get_drvdata(dev); + unsigned int clk_freq, value; + + clk_freq = (rt715->params.curr_dr_freq >> 1); + + switch (clk_freq) { + case RT715_CLK_FREQ_12000000HZ: + value = 0x0; + break; + case RT715_CLK_FREQ_6000000HZ: + value = 0x1; + break; + case RT715_CLK_FREQ_9600000HZ: + value = 0x2; + break; + case RT715_CLK_FREQ_4800000HZ: + value = 0x3; + break; + case RT715_CLK_FREQ_2400000HZ: + value = 0x4; + break; + case RT715_CLK_FREQ_12288000HZ: + value = 0x5; + break; + default: + return -EINVAL; + } + + regmap_write(rt715->regmap, 0xe0, value); + regmap_write(rt715->regmap, 0xf0, value); + + return 0; +} + +int rt715_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave) +{ + struct rt715_priv *rt715; + int ret; + + rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL); + if (!rt715) + return -ENOMEM; + + dev_set_drvdata(dev, rt715); + rt715->slave = slave; + rt715->regmap = regmap; + rt715->sdw_regmap = sdw_regmap; + + regcache_cache_only(rt715->regmap, true); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt715->hw_init = false; + rt715->first_hw_init = false; + + ret = devm_snd_soc_register_component(dev, + &soc_codec_dev_rt715, + rt715_dai, + ARRAY_SIZE(rt715_dai)); + if (ret < 0) + return ret; + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + + /* important note: the device is NOT tagged as 'active' and will remain + * 'suspended' until the hardware is enumerated/initialized. This is required + * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently + * fail with -EACCESS because of race conditions between card creation and enumeration + */ + + return 0; +} + +int rt715_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt715_priv *rt715 = dev_get_drvdata(dev); + + if (rt715->hw_init) + return 0; + + regcache_cache_only(rt715->regmap, false); + + /* + * PM runtime status is marked as 'active' only when a Slave reports as Attached + */ + if (!rt715->first_hw_init) + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + pm_runtime_get_noresume(&slave->dev); + + rt715_reset(rt715->regmap); + + /* Mute nid=08h/09h */ + regmap_write(rt715->regmap, RT715_SET_GAIN_LINE_ADC_H, 0xb080); + regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC_H, 0xb080); + /* Mute nid=07h/27h */ + regmap_write(rt715->regmap, RT715_SET_GAIN_MIC_ADC_H, 0xb080); + regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC2_H, 0xb080); + + /* Set Pin Widget */ + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC1, 0x20); + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC2, 0x20); + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC3, 0x20); + regmap_write(rt715->regmap, RT715_SET_PIN_DMIC4, 0x20); + /* Set Converter Stream */ + regmap_write(rt715->regmap, RT715_SET_STREAMID_LINE_ADC, 0x10); + regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC, 0x10); + regmap_write(rt715->regmap, RT715_SET_STREAMID_MIC_ADC, 0x10); + regmap_write(rt715->regmap, RT715_SET_STREAMID_MIX_ADC2, 0x10); + /* Set Configuration Default */ + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT1, 0xd0); + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC1_CONFIG_DEFAULT4, 0x81); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT1, 0xd1); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC2_CONFIG_DEFAULT4, 0x81); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT1, 0xd0); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC3_CONFIG_DEFAULT4, 0x81); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT1, 0xd1); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT2, 0x11); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT3, 0xa1); + regmap_write(rt715->regmap, RT715_SET_DMIC4_CONFIG_DEFAULT4, 0x81); + + /* Finish Initial Settings, set power to D3 */ + regmap_write(rt715->regmap, RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D3); + + if (rt715->first_hw_init) + regcache_mark_dirty(rt715->regmap); + else + rt715->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt715->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + return 0; +} + +MODULE_DESCRIPTION("ASoC rt715 driver"); +MODULE_DESCRIPTION("ASoC rt715 driver SDW"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h new file mode 100644 index 0000000000..6e37bf64e1 --- /dev/null +++ b/sound/soc/codecs/rt715.h @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rt715.h -- RT715 ALSA SoC audio driver header + * + * Copyright(c) 2019 Realtek Semiconductor Corp. + */ + +#ifndef __RT715_H__ +#define __RT715_H__ + +#include + +struct rt715_priv { + struct regmap *regmap; + struct regmap *sdw_regmap; + struct snd_soc_codec *codec; + struct sdw_slave *slave; + int dbg_nid; + int dbg_vid; + int dbg_payload; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + unsigned int kctl_2ch_vol_ori[2]; + unsigned int kctl_8ch_switch_ori[8]; + unsigned int kctl_8ch_vol_ori[8]; +}; + +/* NID */ +#define RT715_AUDIO_FUNCTION_GROUP 0x01 +#define RT715_MIC_ADC 0x07 +#define RT715_LINE_ADC 0x08 +#define RT715_MIX_ADC 0x09 +#define RT715_DMIC1 0x12 +#define RT715_DMIC2 0x13 +#define RT715_MIC1 0x18 +#define RT715_MIC2 0x19 +#define RT715_LINE1 0x1a +#define RT715_LINE2 0x1b +#define RT715_DMIC3 0x1d +#define RT715_DMIC4 0x29 +#define RT715_VENDOR_REGISTERS 0x20 +#define RT715_MUX_IN1 0x22 +#define RT715_MUX_IN2 0x23 +#define RT715_MUX_IN3 0x24 +#define RT715_MUX_IN4 0x25 +#define RT715_MIX_ADC2 0x27 +#define RT715_INLINE_CMD 0x55 + +/* Index (NID:20h) */ +#define RT715_VD_CLEAR_CTRL 0x01 +#define RT715_SDW_INPUT_SEL 0x39 +#define RT715_EXT_DMIC_CLK_CTRL2 0x54 + +/* Verb */ +#define RT715_VERB_SET_CONNECT_SEL 0x3100 +#define RT715_VERB_GET_CONNECT_SEL 0xb100 +#define RT715_VERB_SET_EAPD_BTLENABLE 0x3c00 +#define RT715_VERB_SET_POWER_STATE 0x3500 +#define RT715_VERB_SET_CHANNEL_STREAMID 0x3600 +#define RT715_VERB_SET_PIN_WIDGET_CONTROL 0x3700 +#define RT715_VERB_SET_CONFIG_DEFAULT1 0x4c00 +#define RT715_VERB_SET_CONFIG_DEFAULT2 0x4d00 +#define RT715_VERB_SET_CONFIG_DEFAULT3 0x4e00 +#define RT715_VERB_SET_CONFIG_DEFAULT4 0x4f00 +#define RT715_VERB_SET_UNSOLICITED_ENABLE 0x3800 +#define RT715_SET_AMP_GAIN_MUTE_H 0x7300 +#define RT715_SET_AMP_GAIN_MUTE_L 0x8380 +#define RT715_READ_HDA_3 0x2012 +#define RT715_READ_HDA_2 0x2013 +#define RT715_READ_HDA_1 0x2014 +#define RT715_READ_HDA_0 0x2015 +#define RT715_PRIV_INDEX_W_H 0x7520 +#define RT715_PRIV_INDEX_W_L 0x85a0 +#define RT715_PRIV_INDEX_W_H_2 0x7500 +#define RT715_PRIV_INDEX_W_L_2 0x8580 +#define RT715_PRIV_DATA_W_H 0x7420 +#define RT715_PRIV_DATA_W_L 0x84a0 +#define RT715_PRIV_INDEX_R_H 0x9d20 +#define RT715_PRIV_INDEX_R_L 0xada0 +#define RT715_PRIV_DATA_R_H 0x9c20 +#define RT715_PRIV_DATA_R_L 0xaca0 +#define RT715_MIC_ADC_FORMAT_H 0x7207 +#define RT715_MIC_ADC_FORMAT_L 0x8287 +#define RT715_MIC_LINE_FORMAT_H 0x7208 +#define RT715_MIC_LINE_FORMAT_L 0x8288 +#define RT715_MIX_ADC_FORMAT_H 0x7209 +#define RT715_MIX_ADC_FORMAT_L 0x8289 +#define RT715_MIX_ADC2_FORMAT_H 0x7227 +#define RT715_MIX_ADC2_FORMAT_L 0x82a7 +#define RT715_FUNC_RESET 0xff01 + +#define RT715_SET_AUDIO_POWER_STATE\ + (RT715_VERB_SET_POWER_STATE | RT715_AUDIO_FUNCTION_GROUP) +#define RT715_SET_PIN_DMIC1\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC1) +#define RT715_SET_PIN_DMIC2\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC2) +#define RT715_SET_PIN_DMIC3\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC3) +#define RT715_SET_PIN_DMIC4\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_DMIC4) +#define RT715_SET_PIN_MIC1\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC1) +#define RT715_SET_PIN_MIC2\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_MIC2) +#define RT715_SET_PIN_LINE1\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE1) +#define RT715_SET_PIN_LINE2\ + (RT715_VERB_SET_PIN_WIDGET_CONTROL | RT715_LINE2) +#define RT715_SET_MIC1_UNSOLICITED_ENABLE\ + (RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC1) +#define RT715_SET_MIC2_UNSOLICITED_ENABLE\ + (RT715_VERB_SET_UNSOLICITED_ENABLE | RT715_MIC2) +#define RT715_SET_STREAMID_MIC_ADC\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIC_ADC) +#define RT715_SET_STREAMID_LINE_ADC\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_LINE_ADC) +#define RT715_SET_STREAMID_MIX_ADC\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC) +#define RT715_SET_STREAMID_MIX_ADC2\ + (RT715_VERB_SET_CHANNEL_STREAMID | RT715_MIX_ADC2) +#define RT715_SET_GAIN_MIC_ADC_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC_ADC) +#define RT715_SET_GAIN_MIC_ADC_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC_ADC) +#define RT715_SET_GAIN_LINE_ADC_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE_ADC) +#define RT715_SET_GAIN_LINE_ADC_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE_ADC) +#define RT715_SET_GAIN_MIX_ADC_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC) +#define RT715_SET_GAIN_MIX_ADC_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC) +#define RT715_SET_GAIN_MIX_ADC2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIX_ADC2) +#define RT715_SET_GAIN_MIX_ADC2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIX_ADC2) +#define RT715_SET_GAIN_DMIC1_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC1) +#define RT715_SET_GAIN_DMIC1_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC1) +#define RT715_SET_GAIN_DMIC2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC2) +#define RT715_SET_GAIN_DMIC2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC2) +#define RT715_SET_GAIN_DMIC3_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC3) +#define RT715_SET_GAIN_DMIC3_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC3) +#define RT715_SET_GAIN_DMIC4_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_DMIC4) +#define RT715_SET_GAIN_DMIC4_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_DMIC4) +#define RT715_SET_GAIN_MIC1_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC1) +#define RT715_SET_GAIN_MIC1_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC1) +#define RT715_SET_GAIN_MIC2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_MIC2) +#define RT715_SET_GAIN_MIC2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_MIC2) +#define RT715_SET_GAIN_LINE1_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE1) +#define RT715_SET_GAIN_LINE1_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE1) +#define RT715_SET_GAIN_LINE2_L\ + (RT715_SET_AMP_GAIN_MUTE_L | RT715_LINE2) +#define RT715_SET_GAIN_LINE2_H\ + (RT715_SET_AMP_GAIN_MUTE_H | RT715_LINE2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC2) +#define RT715_SET_DMIC1_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC1) +#define RT715_SET_DMIC2_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC2) +#define RT715_SET_DMIC3_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT1\ + (RT715_VERB_SET_CONFIG_DEFAULT1 | RT715_DMIC4) +#define RT715_SET_DMIC3_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT2\ + (RT715_VERB_SET_CONFIG_DEFAULT2 | RT715_DMIC4) +#define RT715_SET_DMIC3_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT3\ + (RT715_VERB_SET_CONFIG_DEFAULT3 | RT715_DMIC4) +#define RT715_SET_DMIC3_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC3) +#define RT715_SET_DMIC4_CONFIG_DEFAULT4\ + (RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC4) + +/* vendor register clear ctrl-1 (0x01)(NID:20h) */ +#define RT715_CLEAR_HIDDEN_REG (0x1 << 15) + + +#define RT715_MUTE_SFT 7 +#define RT715_DIR_IN_SFT 6 +#define RT715_DIR_OUT_SFT 7 + +enum { + RT715_AIF1, + RT715_AIF2, +}; + +#define RT715_POWER_UP_DELAY_MS 400 + +int rt715_io_init(struct device *dev, struct sdw_slave *slave); +int rt715_init(struct device *dev, struct regmap *sdw_regmap, + struct regmap *regmap, struct sdw_slave *slave); + +int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, + unsigned int *sdw_addr_h, unsigned int *sdw_data_h, + unsigned int *sdw_addr_l, unsigned int *sdw_data_l); +int rt715_clock_config(struct device *dev); +#endif /* __RT715_H__ */ diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c new file mode 100644 index 0000000000..a38ec58622 --- /dev/null +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -0,0 +1,515 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt722-sdca-sdw.c -- rt722 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include + +#include "rt722-sdca.h" +#include "rt722-sdca-sdw.h" + +static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01 ... 0x2f0a: + case 0x2f35 ... 0x2f36: + case 0x2f50: + case 0x2f54: + case 0x2f58 ... 0x2f5d: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_SELECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, + 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01: + case 0x2f54: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, + 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000 ... 0x2000024: + case 0x2000029 ... 0x200004a: + case 0x2000051 ... 0x2000052: + case 0x200005a ... 0x200005b: + case 0x2000061 ... 0x2000069: + case 0x200006b: + case 0x2000070: + case 0x200007f: + case 0x2000082 ... 0x200008e: + case 0x2000090 ... 0x2000094: + case 0x5300000 ... 0x5300002: + case 0x5400002: + case 0x5600000 ... 0x5600007: + case 0x5700000 ... 0x5700004: + case 0x5800000 ... 0x5800004: + case 0x5b00003: + case 0x5c00011: + case 0x5d00006: + case 0x5f00000 ... 0x5f0000d: + case 0x5f00030: + case 0x6100000 ... 0x6100051: + case 0x6100055 ... 0x6100057: + case 0x6100062: + case 0x6100064 ... 0x6100065: + case 0x6100067: + case 0x6100070 ... 0x610007c: + case 0x6100080: + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_04): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_R): + return true; + default: + return false; + } +} + +static bool rt722_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + case 0x200000d: + case 0x2000019: + case 0x2000020: + case 0x2000030: + case 0x2000046: + case 0x2000067: + case 0x2000084: + case 0x2000086: + return true; + default: + return false; + } +} + +static const struct regmap_config rt722_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt722_sdca_readable_register, + .volatile_reg = rt722_sdca_volatile_register, + .max_register = 0x44ffffff, + .reg_defaults = rt722_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt722_sdca_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt722_sdca_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt722_sdca_mbq_readable_register, + .volatile_reg = rt722_sdca_mbq_volatile_register, + .max_register = 0x41000312, + .reg_defaults = rt722_sdca_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt722_sdca_mbq_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt722_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt722->hw_init = false; + + if (status == SDW_SLAVE_ATTACHED) { + if (rt722->hs_jack) { + /* + * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then + * if the device attached again, we will need to set the setting back. + * It could avoid losing the jack detection interrupt. + * This also could sync with the cache value as the rt722_sdca_jack_init set. + */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_6); + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + } + } + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt722->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt722_sdca_io_init(&slave->dev, slave); +} + +static int rt722_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* + * port = 1 for headphone playback + * port = 2 for headset-mic capture + * port = 3 for speaker playback + * port = 6 for digital-mic capture + */ + prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */ + prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 200; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt722_sdca_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + int ret, stat; + int count = 0, retry = 3; + unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0; + + if (cancel_delayed_work_sync(&rt722->jack_detect_work)) { + dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__); + /* avoid the HID owner doesn't change to device */ + if (rt722->scp_sdca_stat2) + scp_sdca_stat2 = rt722->scp_sdca_stat2; + } + + /* + * The critical section below intentionally protects a rather large piece of code. + * We don't want to allow the system suspend to disable an interrupt while we are + * processing it, which could be problematic given the quirky SoundWire interrupt + * scheme. We do want however to prevent new workqueues from being scheduled if + * the disable_irq flag was set during system suspend. + */ + mutex_lock(&rt722->disable_irq_lock); + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + rt722->scp_sdca_stat1 = ret; + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + rt722->scp_sdca_stat2 = ret; + if (scp_sdca_stat2) + rt722->scp_sdca_stat2 |= scp_sdca_stat2; + do { + /* clear flag */ + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) { + ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0); + if (ret < 0) + goto io_error; + } else if (ret & SDW_SCP_SDCA_INTMASK_SDCA_6) { + ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_6, SDW_SCP_SDCA_INT_SDCA_6); + if (ret < 0) + goto io_error; + } + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) { + ret = sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INT2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + if (ret < 0) + goto io_error; + } + + /* check if flag clear or not */ + ret = sdw_read_no_pm(rt722->slave, SDW_DP0_INT); + if (ret < 0) + goto io_error; + sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0; + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8; + + stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade; + + count++; + } while (stat != 0 && count < retry); + + if (stat) + dev_warn(&slave->dev, + "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt722->scp_sdca_stat1, rt722->scp_sdca_stat2); + + if (status->sdca_cascade && !rt722->disable_irq) + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_detect_work, msecs_to_jiffies(30)); + + mutex_unlock(&rt722->disable_irq_lock); + + return 0; + +io_error: + mutex_unlock(&rt722->disable_irq_lock); + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static struct sdw_slave_ops rt722_sdca_slave_ops = { + .read_prop = rt722_sdca_read_prop, + .interrupt_callback = rt722_sdca_interrupt_callback, + .update_status = rt722_sdca_update_status, +}; + +static int rt722_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt722_sdca_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt722_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt722_sdca_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt722_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + + if (rt722->hw_init) { + cancel_delayed_work_sync(&rt722->jack_detect_work); + cancel_delayed_work_sync(&rt722->jack_btn_check_work); + } + + if (rt722->first_hw_init) + pm_runtime_disable(&slave->dev); + + mutex_destroy(&rt722->calibrate_mutex); + mutex_destroy(&rt722->disable_irq_lock); + + return 0; +} + +static const struct sdw_device_id rt722_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x722, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt722_sdca_id); + +static int __maybe_unused rt722_sdca_dev_suspend(struct device *dev) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + + if (!rt722->hw_init) + return 0; + + cancel_delayed_work_sync(&rt722->jack_detect_work); + cancel_delayed_work_sync(&rt722->jack_btn_check_work); + + regcache_cache_only(rt722->regmap, true); + regcache_cache_only(rt722->mbq_regmap, true); + + return 0; +} + +static int __maybe_unused rt722_sdca_dev_system_suspend(struct device *dev) +{ + struct rt722_sdca_priv *rt722_sdca = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt722_sdca->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt722_sdca->disable_irq_lock); + rt722_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + mutex_unlock(&rt722_sdca->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt722_sdca_dev_suspend(dev); +} + +#define RT722_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt722_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt722->first_hw_init) + return 0; + + if (!slave->unattach_request) { + if (rt722->disable_irq == true) { + mutex_lock(&rt722->disable_irq_lock); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_6); + sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8); + rt722->disable_irq = false; + mutex_unlock(&rt722->disable_irq_lock); + } + goto regmap_sync; + } + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT722_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt722->regmap, false); + regcache_sync(rt722->regmap); + regcache_cache_only(rt722->mbq_regmap, false); + regcache_sync(rt722->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt722_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt722_sdca_dev_system_suspend, rt722_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt722_sdca_dev_suspend, rt722_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt722_sdca_sdw_driver = { + .driver = { + .name = "rt722-sdca", + .owner = THIS_MODULE, + .pm = &rt722_sdca_pm, + }, + .probe = rt722_sdca_sdw_probe, + .remove = rt722_sdca_sdw_remove, + .ops = &rt722_sdca_slave_ops, + .id_table = rt722_sdca_id, +}; +module_sdw_driver(rt722_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt722-sdca-sdw.h b/sound/soc/codecs/rt722-sdca-sdw.h new file mode 100644 index 0000000000..5b43e86f75 --- /dev/null +++ b/sound/soc/codecs/rt722-sdca-sdw.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt722-sdca-sdw.h -- RT722 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT722_SDW_H__ +#define __RT722_SDW_H__ + +#include +#include + +static const struct reg_default rt722_sdca_reg_defaults[] = { + { 0x202d, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f35, 0x00 }, + { 0x2f36, 0x00 }, + { 0x2f50, 0xf0 }, + { 0x2f58, 0x07 }, + { 0x2f59, 0x07 }, + { 0x2f5a, 0x07 }, + { 0x2f5b, 0x07 }, + { 0x2f5c, 0x27 }, + { 0x2f5d, 0x07 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE, + 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE, + 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_01), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_02), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_03), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_04), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0), + 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0), + 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0), + 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, +}; + +static const struct reg_default rt722_sdca_mbq_defaults[] = { + { 0x200003c, 0xc214 }, + { 0x2000046, 0x8004 }, + { 0x6100006, 0x0005 }, + { 0x6100010, 0x2630 }, + { 0x6100011, 0x152f }, + { 0x6100013, 0x0102 }, + { 0x6100015, 0x2200 }, + { 0x6100017, 0x0102 }, + { 0x6100025, 0x2a29 }, + { 0x6100026, 0x2a00 }, + { 0x6100028, 0x2a2a }, + { 0x6100029, 0x4141 }, + { 0x6100055, 0x0000 }, + { 0x5810000, 0x702d }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_01), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_02), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_03), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_04), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R), + 0x0000 }, +}; + +#endif /* __RT722_SDW_H__ */ diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c new file mode 100644 index 0000000000..0e1c65a203 --- /dev/null +++ b/sound/soc/codecs/rt722-sdca.c @@ -0,0 +1,1554 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt722-sdca.c -- rt722 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt722-sdca.h" + +int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int value) +{ + struct regmap *regmap = rt722->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + int ret; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt722->slave->dev, + "Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + struct regmap *regmap = rt722->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt722->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt722_sdca_index_update_bits(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt722_sdca_index_read(rt722, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + return rt722_sdca_index_write(rt722, nid, reg, tmp); +} + +static int rt722_sdca_btn_type(unsigned char *buffer) +{ + if ((*buffer & 0xf0) == 0x10 || (*buffer & 0x0f) == 0x01 || (*(buffer + 1) == 0x01) || + (*(buffer + 1) == 0x10)) + return SND_JACK_BTN_2; + else if ((*buffer & 0xf0) == 0x20 || (*buffer & 0x0f) == 0x02 || (*(buffer + 1) == 0x02) || + (*(buffer + 1) == 0x20)) + return SND_JACK_BTN_3; + else if ((*buffer & 0xf0) == 0x40 || (*buffer & 0x0f) == 0x04 || (*(buffer + 1) == 0x04) || + (*(buffer + 1) == 0x40)) + return SND_JACK_BTN_0; + else if ((*buffer & 0xf0) == 0x80 || (*buffer & 0x0f) == 0x08 || (*(buffer + 1) == 0x08) || + (*(buffer + 1) == 0x80)) + return SND_JACK_BTN_1; + + return 0; +} + +static unsigned int rt722_sdca_button_detect(struct rt722_sdca_priv *rt722) +{ + unsigned int btn_type = 0, offset, idx, val, owner; + int ret; + unsigned char buf[3]; + + /* get current UMP message owner */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner); + if (ret < 0) + return 0; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + /* read UMP message offset */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto _end_btn_det_; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt722->regmap, + RT722_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) + btn_type = rt722_sdca_btn_type(&buf[1]); + +_end_btn_det_: + /* Host is owner, so set back to device */ + if (owner == 0) + /* set owner to device */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01); + + return btn_type; +} + +static int rt722_sdca_headset_detect(struct rt722_sdca_priv *rt722) +{ + unsigned int det_mode; + int ret; + + /* get detected_mode */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x00: + rt722->jack_type = 0; + break; + case 0x03: + rt722->jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + rt722->jack_type = SND_JACK_HEADSET; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_SELECTED_MODE, 0), det_mode); + if (ret < 0) + goto io_error; + } + + dev_dbg(&rt722->slave->dev, + "%s, detected_mode=0x%x\n", __func__, det_mode); + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void rt722_sdca_jack_detect_handler(struct work_struct *work) +{ + struct rt722_sdca_priv *rt722 = + container_of(work, struct rt722_sdca_priv, jack_detect_work.work); + int btn_type = 0, ret; + + if (!rt722->hs_jack) + return; + + if (!rt722->component->card || !rt722->component->card->instantiated) + return; + + /* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */ + if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6) { + ret = rt722_sdca_headset_detect(rt722); + if (ret < 0) + return; + } + + /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */ + if (rt722->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8) + btn_type = rt722_sdca_button_detect(rt722); + + if (rt722->jack_type == 0) + btn_type = 0; + + dev_dbg(&rt722->slave->dev, + "in %s, jack_type=%d\n", __func__, rt722->jack_type); + dev_dbg(&rt722->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + dev_dbg(&rt722->slave->dev, + "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt722->scp_sdca_stat1, rt722->scp_sdca_stat2); + + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_btn_check_work, msecs_to_jiffies(200)); + } +} + +static void rt722_sdca_btn_check_handler(struct work_struct *work) +{ + struct rt722_sdca_priv *rt722 = + container_of(work, struct rt722_sdca_priv, jack_btn_check_work.work); + int btn_type = 0, ret, idx; + unsigned int det_mode, offset, val; + unsigned char buf[3]; + + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (det_mode) { + /* read UMP message offset */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto io_error; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt722->regmap, + RT722_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto io_error; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) + btn_type = rt722_sdca_btn_type(&buf[1]); + } else + rt722->jack_type = 0; + + dev_dbg(&rt722->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) +{ + mutex_lock(&rt722->calibrate_mutex); + if (rt722->hs_jack) { + /* set SCP_SDCA_IntMask1[0]=1 */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6); + /* set SCP_SDCA_IntMask2[0]=1 */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + dev_dbg(&rt722->slave->dev, "in %s enable\n", __func__); + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_UNSOL_CTL, 0x016E); + /* set XU(et03h) & XU(et0Dh) to Not bypassed */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU03, + RT722_SDCA_CTL_SELECTED_MODE, 0), 0); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D, + RT722_SDCA_CTL_SELECTED_MODE, 0), 0); + /* trigger GE interrupt */ + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_GE_RELATED_CTL2, 0x4000, 0x4000); + } + mutex_unlock(&rt722->calibrate_mutex); +} + +static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int ret; + + rt722->hs_jack = hs_jack; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + rt722_sdca_jack_init(rt722); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + 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) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int read_l, read_r, gain_l_val, gain_r_val; + unsigned int adc_vol_flag = 0, changed = 0; + unsigned int lvalue, rvalue; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt722->mbq_regmap, mc->reg, &lvalue); + regmap_read(rt722->mbq_regmap, mc->rreg, &rvalue); + + /* L Channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_l_val = gain_l_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset); + else + gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset); + gain_l_val &= 0xffff; + } + + /* R Channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_r_val = gain_r_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset); + else + gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset); + gain_r_val &= 0xffff; + } + + if (lvalue != gain_l_val || rvalue != gain_r_val) + changed = 1; + else + return 0; + + /* Lch*/ + regmap_write(rt722->mbq_regmap, mc->reg, gain_l_val); + + /* Rch */ + regmap_write(rt722->mbq_regmap, mc->rreg, gain_r_val); + + regmap_read(rt722->mbq_regmap, mc->reg, &read_l); + regmap_read(rt722->mbq_regmap, mc->rreg, &read_r); + if (read_r == gain_r_val && read_l == gain_l_val) + return changed; + + return -EIO; +} + +static int rt722_sdca_set_gain_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); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt722->mbq_regmap, mc->reg, &read_l); + regmap_read(rt722->mbq_regmap, mc->rreg, &read_r); + + if (mc->shift == 8) /* boost gain */ + ctl_l = read_l / tendB; + else { + if (adc_vol_flag) + ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset); + else + ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset); + } + + if (read_l != read_r) { + if (mc->shift == 8) /* boost gain */ + ctl_r = read_r / tendB; + else { /* ADC/DAC gain */ + if (adc_vol_flag) + ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset); + else + ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset); + } + } else { + ctl_r = ctl_l; + } + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + + return 0; +} + +static int rt722_sdca_set_fu1e_capture_ctl(struct rt722_sdca_priv *rt722) +{ + int err, i; + unsigned int ch_mute; + + for (i = 0; i < ARRAY_SIZE(rt722->fu1e_mixer_mute); i++) { + ch_mute = rt722->fu1e_dapm_mute || rt722->fu1e_mixer_mute[i]; + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute); + if (err < 0) + return err; + } + + return 0; +} + +static int rt722_sdca_fu1e_capture_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); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int i; + + for (i = 0; i < p->count; i++) + ucontrol->value.integer.value[i] = !rt722->fu1e_mixer_mute[i]; + + return 0; +} + +static int rt722_sdca_fu1e_capture_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 rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + int err, changed = 0, i; + + for (i = 0; i < p->count; i++) { + if (rt722->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i]) + changed = 1; + rt722->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i]; + } + + err = rt722_sdca_set_fu1e_capture_ctl(rt722); + if (err < 0) + return err; + + return changed; +} + +static int rt722_sdca_set_fu0f_capture_ctl(struct rt722_sdca_priv *rt722) +{ + int err; + unsigned int ch_l, ch_r; + + ch_l = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_l_mute) ? 0x01 : 0x00; + ch_r = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_MUTE, CH_L), ch_l); + if (err < 0) + return err; + + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_MUTE, CH_R), ch_r); + if (err < 0) + return err; + + return 0; +} + +static int rt722_sdca_fu0f_capture_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->fu0f_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt722->fu0f_mixer_r_mute; + return 0; +} + +static int rt722_sdca_fu0f_capture_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); + int err, changed = 0; + + if (rt722->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] || + rt722->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1]) + changed = 1; + + rt722->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt722->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt722_sdca_set_fu0f_capture_ctl(rt722); + if (err < 0) + return err; + + return changed; +} + +static int rt722_sdca_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +static int rt722_sdca_dmic_set_gain_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); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int boost_step = 0x0a00; + unsigned int vol_max = 0x1e00; + unsigned int regvalue, ctl, i; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt722->mbq_regmap, p->reg_base + i, ®value); + + if (!adc_vol_flag) /* boost gain */ + ctl = regvalue / boost_step; + else { /* ADC gain */ + if (adc_vol_flag) + ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset); + else + ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset); + } + + ucontrol->value.integer.value[i] = ctl; + } + + return 0; +} + +static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int boost_step = 0x0a00; + unsigned int vol_max = 0x1e00; + unsigned int gain_val[4]; + unsigned int i, adc_vol_flag = 0, changed = 0; + unsigned int regvalue[4]; + const unsigned int interval_offset = 0xc0; + int err; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt722->mbq_regmap, p->reg_base + i, ®value[i]); + + gain_val[i] = ucontrol->value.integer.value[i]; + if (gain_val[i] > p->max) + gain_val[i] = p->max; + + if (!adc_vol_flag) /* boost gain */ + gain_val[i] = gain_val[i] * boost_step; + else { /* ADC gain */ + gain_val[i] = vol_max - ((p->max - gain_val[i]) * interval_offset); + gain_val[i] &= 0xffff; + } + + if (regvalue[i] != gain_val[i]) + changed = 1; + } + + if (!changed) + return 0; + + for (i = 0; i < p->count; i++) { + err = regmap_write(rt722->mbq_regmap, p->reg_base + i, gain_val[i]); + if (err < 0) + dev_err(&rt722->slave->dev, "%#08x can't be set\n", p->reg_base + i); + } + + return changed; +} + +#define RT722_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \ + ((unsigned long)&(struct rt722_sdca_dmic_kctrl_priv) \ + {.reg_base = xreg_base, .count = xcount, .max = xmax, \ + .invert = xinvert}) + +#define RT722_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = rt722_sdca_fu_info, \ + .get = rt722_sdca_fu1e_capture_get, \ + .put = rt722_sdca_fu1e_capture_put, \ + .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)} + +#define RT722_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\ + xhandler_put, xcount, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt722_sdca_fu_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) } + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt722_sdca_controls[] = { + /* Headphone playback settings */ + SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv), + /* Headset mic capture settings */ + SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt722_sdca_fu0f_capture_get, rt722_sdca_fu0f_capture_put), + SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_R), 8, 3, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, boost_vol_tlv), + /* AMP playback settings */ + SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv), + /* DMIC capture settings */ + RT722_SDCA_FU_CTRL("FU1E Capture Switch", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4), + RT722_SDCA_EXT_TLV("FU1E Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_VOLUME, CH_01), + rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put, + 4, 0x3f, mic_vol_tlv), + RT722_SDCA_EXT_TLV("FU15 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, + 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), +}; + +static int rt722_sdca_adc_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask_sft; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, &val); + + ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7; + + return 0; +} + +static int rt722_sdca_adc_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, &val2); + val2 = (0x7 << mask_sft) & val2; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, 0x7 << mask_sft, + val << mask_sft); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc22_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", +}; + +static const char * const adc07_10_mux_text[] = { + "DMIC1", + "DMIC2", +}; + +static SOC_ENUM_SINGLE_DECL( + rt722_adc22_enum, SND_SOC_NOPM, 0, adc22_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt722_adc24_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt722_adc25_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); + +static const struct snd_kcontrol_new rt722_sdca_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt722_adc22_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static const struct snd_kcontrol_new rt722_sdca_adc24_mux = + SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt722_adc24_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static const struct snd_kcontrol_new rt722_sdca_adc25_mux = + SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt722_adc25_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static int rt722_sdca_fu42_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_L), unmute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_R), unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_L), mute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_R), mute); + break; + } + return 0; +} + +static int rt722_sdca_fu21_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_L), unmute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_R), unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_L), mute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_R), mute); + break; + } + return 0; +} + +static int rt722_sdca_fu113_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt722->fu1e_dapm_mute = false; + rt722_sdca_set_fu1e_capture_ctl(rt722); + break; + case SND_SOC_DAPM_PRE_PMD: + rt722->fu1e_dapm_mute = true; + rt722_sdca_set_fu1e_capture_ctl(rt722); + break; + } + return 0; +} + +static int rt722_sdca_fu36_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt722->fu0f_dapm_mute = false; + rt722_sdca_set_fu0f_capture_ctl(rt722); + break; + case SND_SOC_DAPM_PRE_PMD: + rt722->fu0f_dapm_mute = true; + rt722_sdca_set_fu0f_capture_ctl(rt722); + break; + } + return 0; +} + +static int rt722_sdca_pde47_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde11_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde12_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt722_sdca_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_INPUT("DMIC1_2"), + SND_SOC_DAPM_INPUT("DMIC3_4"), + + SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde47_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde11_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde12_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu21_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu42_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu36_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu113_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc22_mux), + SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc24_mux), + SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc25_mux), + + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Headphone Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Headset Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Speaker Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 DMic Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt722_sdca_audio_map[] = { + {"FU 42", NULL, "DP1RX"}, + {"FU 21", NULL, "DP3RX"}, + + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 24 Mux", "DMIC1", "DMIC1_2"}, + {"ADC 24 Mux", "DMIC2", "DMIC3_4"}, + {"ADC 25 Mux", "DMIC1", "DMIC1_2"}, + {"ADC 25 Mux", "DMIC2", "DMIC3_4"}, + {"FU 36", NULL, "PDE 12"}, + {"FU 36", NULL, "ADC 22 Mux"}, + {"FU 113", NULL, "PDE 11"}, + {"FU 113", NULL, "ADC 24 Mux"}, + {"FU 113", NULL, "ADC 25 Mux"}, + {"DP2TX", NULL, "FU 36"}, + {"DP6TX", NULL, "FU 113"}, + + {"HP", NULL, "PDE 47"}, + {"HP", NULL, "FU 42"}, + {"SPK", NULL, "PDE 23"}, + {"SPK", NULL, "FU 21"}, +}; + +static int rt722_sdca_parse_dt(struct rt722_sdca_priv *rt722, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", &rt722->jd_src); + + return 0; +} + +static int rt722_sdca_probe(struct snd_soc_component *component) +{ + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int ret; + + rt722_sdca_parse_dt(rt722, &rt722->slave->dev); + rt722->component = component; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt722 = { + .probe = rt722_sdca_probe, + .controls = rt722_sdca_controls, + .num_controls = ARRAY_SIZE(rt722_sdca_controls), + .dapm_widgets = rt722_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt722_sdca_dapm_widgets), + .dapm_routes = rt722_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt722_sdca_audio_map), + .set_jack = rt722_sdca_set_jack_detect, + .endianness = 1, +}; + +static int rt722_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt722_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + int retval, port, num_channels; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt722->slave) + return -EINVAL; + + /* + * RT722_AIF1 with port = 1 for headphone playback + * RT722_AIF1 with port = 2 for headset-mic capture + * RT722_AIF2 with port = 3 for speaker playback + * RT722_AIF3 with port = 6 for digital-mic capture + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + if (dai->id == RT722_AIF1) + port = 1; + else if (dai->id == RT722_AIF2) + port = 3; + else + return -EINVAL; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT722_AIF1) + port = 2; + else if (dai->id == RT722_AIF3) + port = 6; + else + return -EINVAL; + } + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = GENMASK(num_channels - 1, 0); + port_config.num = port; + + retval = sdw_stream_add_slave(rt722->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT722_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT722_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT722_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT722_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + if (dai->id == RT722_AIF1) { + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + } + + if (dai->id == RT722_AIF2) + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + + if (dai->id == RT722_AIF3) + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + + return 0; +} + +static int rt722_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt722->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt722->slave, sdw_stream); + return 0; +} + +#define RT722_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT722_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt722_sdca_ops = { + .hw_params = rt722_sdca_pcm_hw_params, + .hw_free = rt722_sdca_pcm_hw_free, + .set_stream = rt722_sdca_set_sdw_stream, + .shutdown = rt722_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt722_sdca_dai[] = { + { + .name = "rt722-sdca-aif1", + .id = RT722_AIF1, + .playback = { + .stream_name = "DP1 Headphone Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .capture = { + .stream_name = "DP2 Headset Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + }, + { + .name = "rt722-sdca-aif2", + .id = RT722_AIF2, + .playback = { + .stream_name = "DP3 Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + }, + { + .name = "rt722-sdca-aif3", + .id = RT722_AIF3, + .capture = { + .stream_name = "DP6 DMic Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + } +}; + +int rt722_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722; + + rt722 = devm_kzalloc(dev, sizeof(*rt722), GFP_KERNEL); + if (!rt722) + return -ENOMEM; + + dev_set_drvdata(dev, rt722); + rt722->slave = slave; + rt722->regmap = regmap; + rt722->mbq_regmap = mbq_regmap; + + mutex_init(&rt722->calibrate_mutex); + mutex_init(&rt722->disable_irq_lock); + + INIT_DELAYED_WORK(&rt722->jack_detect_work, rt722_sdca_jack_detect_handler); + INIT_DELAYED_WORK(&rt722->jack_btn_check_work, rt722_sdca_btn_check_handler); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt722->hw_init = false; + rt722->first_hw_init = false; + rt722->fu1e_dapm_mute = true; + rt722->fu0f_dapm_mute = true; + 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; + + return devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai)); +} + +static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722) +{ + /* Set AD07 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a29); + /* Set AD10 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC10_PDE_FLOAT_CTL, 0x2a00); + /* Set DMIC1/DMIC2 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC1_2_PDE_FLOAT_CTL, 0x2a2a); + /* Set DMIC2 IT entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_ENT_FLOAT_CTL, 0x2626); + /* Set AD10 FU entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC_ENT_FLOAT_CTL, 0x1e00); + /* Set DMIC2 FU entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515); + /* Set AD10 FU channel floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC_VOL_CH_FLOAT_CTL, 0x0304); + /* Set DMIC2 FU channel floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304); + /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_CONFIG_CTL0, 0x0000); + /* Enable vf707_r12_05/vf707_r13_05 */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, + RT722_SDCA_CTL_VENDOR_DEF, 0), 0x01); + /* Fine tune PDE2A latency */ + regmap_write(rt722->regmap, 0x2f5c, 0x25); +} + +static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722) +{ + /* Set DVQ=01 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, + 0xc215); + /* Reset dc_cal_top */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, + 0x702c); + /* W1C Trigger Calibration */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, + 0xf02d); + /* Set DAC02/ClassD power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_AMP_PDE_FLOAT_CTL, + 0x2323); + /* Set EAPD high */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_EAPD_CTL, + 0x0002); + /* Enable vf707_r14 */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, + RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04); +} + +static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) +{ + int loop_check, chk_cnt = 100, ret; + unsigned int calib_status = 0; + + /* Read eFuse */ + rt722_sdca_index_write(rt722, RT722_VENDOR_SPK_EFUSE, RT722_DC_CALIB_CTRL, + 0x4808); + /* Button A, B, C, D bypass mode */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4, + 0xcf00); + /* HID1 slot enable */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL5, + 0x000f); + /* Report ID for HID1 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL0, + 0x1100); + /* OSC/OOC for slot 2, 3 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL7, + 0x0c12); + /* Set JD de-bounce clock control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_JD_CTRL1, + 0x7002); + /* Set DVQ=01 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, + 0xc215); + /* FSM switch to calibration manual mode */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_FSM_CTL, + 0x4100); + /* W1C Trigger DC calibration (HP) */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DAC_DC_CALI_CTL3, + 0x008d); + /* check HP calibration FSM status */ + for (loop_check = 0; loop_check < chk_cnt; loop_check++) { + ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI, + RT722_DAC_DC_CALI_CTL3, &calib_status); + if (ret < 0 || loop_check == chk_cnt) + dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret); + if ((calib_status & 0x0040) == 0x0) + break; + } + /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4, + 0x0010); + /* Set ADC09 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL, + 0x2a12); + /* Set MIC2 and LINE1 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_MIC2_LINE2_PDE_FLOAT_CTL, + 0x3429); + /* Set ET41h and LINE2 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ET41_LINE2_PDE_FLOAT_CTL, + 0x4112); + /* Set DAC03 and HP power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL, + 0x4040); + /* Fine tune PDE40 latency */ + regmap_write(rt722->regmap, 0x2f58, 0x07); +} + +int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + + rt722->disable_irq = false; + + if (rt722->hw_init) + return 0; + + if (rt722->first_hw_init) { + regcache_cache_only(rt722->regmap, false); + regcache_cache_bypass(rt722->regmap, true); + regcache_cache_only(rt722->mbq_regmap, false); + regcache_cache_bypass(rt722->mbq_regmap, true); + } else { + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt722_sdca_dmic_preset(rt722); + rt722_sdca_amp_preset(rt722); + rt722_sdca_jack_preset(rt722); + + if (rt722->first_hw_init) { + regcache_cache_bypass(rt722->regmap, false); + regcache_mark_dirty(rt722->regmap); + regcache_cache_bypass(rt722->mbq_regmap, false); + regcache_mark_dirty(rt722->mbq_regmap); + } else + rt722->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt722->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver"); +MODULE_AUTHOR("Jack Yu "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h new file mode 100644 index 0000000000..44af890135 --- /dev/null +++ b/sound/soc/codecs/rt722-sdca.h @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt722-sdca.h -- RT722 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT722_H__ +#define __RT722_H__ + +#include +#include +#include +#include +#include +#include + +struct rt722_sdca_priv { + struct regmap *regmap; + struct regmap *mbq_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct mutex calibrate_mutex; + struct mutex disable_irq_lock; + bool disable_irq; + /* For Headset jack & Headphone */ + unsigned int scp_sdca_stat1; + unsigned int scp_sdca_stat2; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + int jack_type; + int jd_src; + bool fu0f_dapm_mute; + bool fu0f_mixer_l_mute; + bool fu0f_mixer_r_mute; + /* For DMIC */ + bool fu1e_dapm_mute; + bool fu1e_mixer_mute[4]; +}; + +struct rt722_sdca_dmic_kctrl_priv { + unsigned int reg_base; + unsigned int count; + unsigned int max; + unsigned int invert; +}; + +/* NID */ +#define RT722_VENDOR_REG 0x20 +#define RT722_VENDOR_CALI 0x58 +#define RT722_VENDOR_SPK_EFUSE 0x5c +#define RT722_VENDOR_IMS_DRE 0x5b +#define RT722_VENDOR_ANALOG_CTL 0x5f +#define RT722_VENDOR_HDA_CTL 0x61 + +/* Index (NID:20h) */ +#define RT722_JD_PRODUCT_NUM 0x00 +#define RT722_ANALOG_BIAS_CTL3 0x04 +#define RT722_JD_CTRL1 0x09 +#define RT722_LDO2_3_CTL1 0x0e +#define RT722_LDO1_CTL 0x1a +#define RT722_HP_JD_CTRL 0x24 +#define RT722_CLSD_CTRL6 0x3c +#define RT722_COMBO_JACK_AUTO_CTL1 0x45 +#define RT722_COMBO_JACK_AUTO_CTL2 0x46 +#define RT722_COMBO_JACK_AUTO_CTL3 0x47 +#define RT722_DIGITAL_MISC_CTRL4 0x4a +#define RT722_FSM_CTL 0x67 +#define RT722_SDCA_INTR_REC 0x82 +#define RT722_SW_CONFIG1 0x8a +#define RT722_SW_CONFIG2 0x8b + +/* Index (NID:58h) */ +#define RT722_DAC_DC_CALI_CTL0 0x00 +#define RT722_DAC_DC_CALI_CTL1 0x01 +#define RT722_DAC_DC_CALI_CTL2 0x02 +#define RT722_DAC_DC_CALI_CTL3 0x03 + +/* Index (NID:59h) */ +#define RT722_ULTRA_SOUND_DETECTOR6 0x1e + +/* Index (NID:5bh) */ +#define RT722_IMS_DIGITAL_CTL1 0x00 +#define RT722_IMS_DIGITAL_CTL5 0x05 +#define RT722_HP_DETECT_RLDET_CTL1 0x29 +#define RT722_HP_DETECT_RLDET_CTL2 0x2a + +/* Index (NID:5fh) */ +#define RT722_MISC_POWER_CTL0 0x00 +#define RT722_MISC_POWER_CTL7 0x08 + +/* Index (NID:61h) */ +#define RT722_HDA_LEGACY_MUX_CTL0 0x00 +#define RT722_HDA_LEGACY_UNSOL_CTL 0x03 +#define RT722_HDA_LEGACY_CONFIG_CTL0 0x06 +#define RT722_HDA_LEGACY_RESET_CTL 0x08 +#define RT722_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e +#define RT722_DMIC_ENT_FLOAT_CTL 0x10 +#define RT722_DMIC_GAIN_ENT_FLOAT_CTL0 0x11 +#define RT722_DMIC_GAIN_ENT_FLOAT_CTL2 0x13 +#define RT722_ADC_ENT_FLOAT_CTL 0x15 +#define RT722_ADC_VOL_CH_FLOAT_CTL 0x17 +#define RT722_ADC_SAMPLE_RATE_FLOAT 0x18 +#define RT722_DAC03_HP_PDE_FLOAT_CTL 0x22 +#define RT722_MIC2_LINE2_PDE_FLOAT_CTL 0x23 +#define RT722_ET41_LINE2_PDE_FLOAT_CTL 0x24 +#define RT722_ADC0A_08_PDE_FLOAT_CTL 0x25 +#define RT722_ADC10_PDE_FLOAT_CTL 0x26 +#define RT722_DMIC1_2_PDE_FLOAT_CTL 0x28 +#define RT722_AMP_PDE_FLOAT_CTL 0x29 +#define RT722_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f +#define RT722_GE_RELATED_CTL1 0x45 +#define RT722_GE_RELATED_CTL2 0x46 +#define RT722_MIXER_CTL0 0x52 +#define RT722_MIXER_CTL1 0x53 +#define RT722_EAPD_CTL 0x55 +#define RT722_UMP_HID_CTL0 0x60 +#define RT722_UMP_HID_CTL1 0x61 +#define RT722_UMP_HID_CTL2 0x62 +#define RT722_UMP_HID_CTL3 0x63 +#define RT722_UMP_HID_CTL4 0x64 +#define RT722_UMP_HID_CTL5 0x65 +#define RT722_UMP_HID_CTL6 0x66 +#define RT722_UMP_HID_CTL7 0x67 +#define RT722_UMP_HID_CTL8 0x68 + +/* Parameter & Verb control 01 (0x1a)(NID:20h) */ +#define RT722_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT722_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT722_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT722_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT722_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +/* DAC calibration control (0x00)(NID:58h) */ +#define RT722_DC_CALIB_CTRL (0x1 << 16) +/* DAC DC offset calibration control-1 (0x01)(NID:58h) */ +#define RT722_PDM_DC_CALIB_STATUS (0x1 << 15) + +#define RT722_EAPD_HIGH 0x2 +#define RT722_EAPD_LOW 0x0 + +/* Buffer address for HID */ +#define RT722_BUF_ADDR_HID1 0x44030000 +#define RT722_BUF_ADDR_HID2 0x44030020 + +/* RT722 SDCA Control - function number */ +#define FUNC_NUM_JACK_CODEC 0x01 +#define FUNC_NUM_MIC_ARRAY 0x02 +#define FUNC_NUM_HID 0x03 +#define FUNC_NUM_AMP 0x04 + +/* RT722 SDCA entity */ +#define RT722_SDCA_ENT_HID01 0x01 +#define RT722_SDCA_ENT_GE49 0x49 +#define RT722_SDCA_ENT_USER_FU05 0x05 +#define RT722_SDCA_ENT_USER_FU06 0x06 +#define RT722_SDCA_ENT_USER_FU0F 0x0f +#define RT722_SDCA_ENT_USER_FU10 0x19 +#define RT722_SDCA_ENT_USER_FU1E 0x1e +#define RT722_SDCA_ENT_FU15 0x15 +#define RT722_SDCA_ENT_PDE23 0x23 +#define RT722_SDCA_ENT_PDE40 0x40 +#define RT722_SDCA_ENT_PDE11 0x11 +#define RT722_SDCA_ENT_PDE12 0x12 +#define RT722_SDCA_ENT_PDE2A 0x2a +#define RT722_SDCA_ENT_CS01 0x01 +#define RT722_SDCA_ENT_CS11 0x11 +#define RT722_SDCA_ENT_CS1F 0x1f +#define RT722_SDCA_ENT_CS1C 0x1c +#define RT722_SDCA_ENT_CS31 0x31 +#define RT722_SDCA_ENT_OT23 0x42 +#define RT722_SDCA_ENT_IT26 0x26 +#define RT722_SDCA_ENT_IT09 0x09 +#define RT722_SDCA_ENT_PLATFORM_FU15 0x15 +#define RT722_SDCA_ENT_PLATFORM_FU44 0x44 +#define RT722_SDCA_ENT_XU03 0x03 +#define RT722_SDCA_ENT_XU0D 0x0d + +/* RT722 SDCA control */ +#define RT722_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT722_SDCA_CTL_FU_MUTE 0x01 +#define RT722_SDCA_CTL_FU_VOLUME 0x02 +#define RT722_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10 +#define RT722_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11 +#define RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12 +#define RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13 +#define RT722_SDCA_CTL_SELECTED_MODE 0x01 +#define RT722_SDCA_CTL_DETECTED_MODE 0x02 +#define RT722_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT722_SDCA_CTL_VENDOR_DEF 0x30 +#define RT722_SDCA_CTL_FU_CH_GAIN 0x0b + +/* RT722 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 +#define CH_01 0x01 +#define CH_02 0x02 +#define CH_03 0x03 +#define CH_04 0x04 +#define CH_08 0x08 + +/* sample frequency index */ +#define RT722_SDCA_RATE_16000HZ 0x04 +#define RT722_SDCA_RATE_32000HZ 0x07 +#define RT722_SDCA_RATE_44100HZ 0x08 +#define RT722_SDCA_RATE_48000HZ 0x09 +#define RT722_SDCA_RATE_96000HZ 0x0b +#define RT722_SDCA_RATE_192000HZ 0x0d + +enum { + RT722_AIF1, /* For headset mic and headphone */ + RT722_AIF2, /* For speaker */ + RT722_AIF3, /* For dmic */ + RT722_AIFS, +}; + +enum rt722_sdca_jd_src { + RT722_JD_NULL, + RT722_JD1, +}; + +int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt722_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave); +int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int value); +int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int *value); + +int rt722_sdca_jack_detect(struct rt722_sdca_priv *rt722, bool *hp, bool *mic); +#endif /* __RT722_H__ */ diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c new file mode 100644 index 0000000000..733a7d130a --- /dev/null +++ b/sound/soc/codecs/rt9120.c @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RT9120_REG_DEVID 0x00 +#define RT9120_REG_I2SFMT 0x02 +#define RT9120_REG_I2SWL 0x03 +#define RT9120_REG_SDIOSEL 0x04 +#define RT9120_REG_SYSCTL 0x05 +#define RT9120_REG_SPKGAIN 0x07 +#define RT9120_REG_VOLRAMP 0x0A +#define RT9120_REG_ERRRPT 0x10 +#define RT9120_REG_MSVOL 0x20 +#define RT9120_REG_SWRESET 0x40 +#define RT9120_REG_INTERCFG 0x63 +#define RT9120_REG_INTERNAL0 0x65 +#define RT9120_REG_INTERNAL1 0x69 +#define RT9120_REG_UVPOPT 0x6C +#define RT9120_REG_DIGCFG 0xF8 + +#define RT9120_VID_MASK GENMASK(15, 8) +#define RT9120_SWRST_MASK BIT(7) +#define RT9120_MUTE_MASK GENMASK(5, 4) +#define RT9120_I2SFMT_MASK GENMASK(4, 2) +#define RT9120_I2SFMT_SHIFT 2 +#define RT9120_CFG_FMT_I2S 0 +#define RT9120_CFG_FMT_LEFTJ 1 +#define RT9120_CFG_FMT_RIGHTJ 2 +#define RT9120_CFG_FMT_DSPA 3 +#define RT9120_CFG_FMT_DSPB 7 +#define RT9120_AUDBIT_MASK GENMASK(1, 0) +#define RT9120_CFG_AUDBIT_16 0 +#define RT9120_CFG_AUDBIT_20 1 +#define RT9120_CFG_AUDBIT_24 2 +#define RT9120_AUDWL_MASK GENMASK(5, 0) +#define RT9120_CFG_WORDLEN_16 16 +#define RT9120_CFG_WORDLEN_24 24 +#define RT9120_CFG_WORDLEN_32 32 +#define RT9120_DVDD_UVSEL_MASK GENMASK(5, 4) +#define RT9120_AUTOSYNC_MASK BIT(6) + +#define RT9120_VENDOR_ID 0x42 +#define RT9120S_VENDOR_ID 0x43 +#define RT9120_RESET_WAITMS 20 +#define RT9120_CHIPON_WAITMS 20 +#define RT9120_AMPON_WAITMS 50 +#define RT9120_AMPOFF_WAITMS 100 +#define RT9120_LVAPP_THRESUV 2000000 + +/* 8000 to 192000 supported , only 176400 not support */ +#define RT9120_RATES_MASK (SNDRV_PCM_RATE_8000_192000 &\ + ~SNDRV_PCM_RATE_176400) +#define RT9120_FMTS_MASK (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +enum { + CHIP_IDX_RT9120 = 0, + CHIP_IDX_RT9120S, + CHIP_IDX_MAX +}; + +struct rt9120_data { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *pwdnn_gpio; + int chip_idx; +}; + +/* 11bit [min,max,step] = [-103.9375dB, 24dB, 0.0625dB] */ +static const DECLARE_TLV_DB_SCALE(digital_tlv, -1039375, 625, 1); + +/* {6, 8, 10, 12, 13, 14, 15, 16}dB */ +static const DECLARE_TLV_DB_RANGE(classd_tlv, + 0, 3, TLV_DB_SCALE_ITEM(600, 200, 0), + 4, 7, TLV_DB_SCALE_ITEM(1300, 100, 0) +); + +static const char * const sdo_select_text[] = { + "None", "INTF", "Final", "RMS Detect" +}; + +static const struct soc_enum sdo_select_enum = + SOC_ENUM_SINGLE(RT9120_REG_SDIOSEL, 4, ARRAY_SIZE(sdo_select_text), + sdo_select_text); + +static const struct snd_kcontrol_new rt9120_snd_controls[] = { + SOC_SINGLE_TLV("MS Volume", RT9120_REG_MSVOL, 0, 2047, 1, digital_tlv), + SOC_SINGLE_TLV("SPK Gain Volume", RT9120_REG_SPKGAIN, 0, 7, 0, classd_tlv), + SOC_SINGLE("PBTL Switch", RT9120_REG_SYSCTL, 3, 1, 0), + SOC_ENUM("SDO Select", sdo_select_enum), +}; + +static int internal_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write(comp, RT9120_REG_ERRRPT, 0); + break; + case SND_SOC_DAPM_POST_PMU: + msleep(RT9120_AMPON_WAITMS); + break; + case SND_SOC_DAPM_POST_PMD: + msleep(RT9120_AMPOFF_WAITMS); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt9120_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("DMIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_DAC("LDAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("RDAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("PWND", RT9120_REG_SYSCTL, 6, 1, + internal_power_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA("SPKL PA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPKR PA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), +}; + +static const struct snd_soc_dapm_route rt9120_dapm_routes[] = { + { "DMIX", NULL, "AIF Playback" }, + /* SPKL */ + { "LDAC", NULL, "PWND" }, + { "LDAC", NULL, "DMIX" }, + { "SPKL PA", NULL, "LDAC" }, + { "SPKL", NULL, "SPKL PA" }, + /* SPKR */ + { "RDAC", NULL, "PWND" }, + { "RDAC", NULL, "DMIX" }, + { "SPKR PA", NULL, "RDAC" }, + { "SPKR", NULL, "SPKR PA" }, + /* Cap */ + { "AIF Capture", NULL, "LDAC" }, + { "AIF Capture", NULL, "RDAC" }, +}; + +static int rt9120_codec_probe(struct snd_soc_component *comp) +{ + struct rt9120_data *data = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, data->regmap); + + pm_runtime_get_sync(comp->dev); + + /* Internal setting */ + if (data->chip_idx == CHIP_IDX_RT9120S) { + snd_soc_component_write(comp, RT9120_REG_INTERCFG, 0xde); + snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x66); + } else + snd_soc_component_write(comp, RT9120_REG_INTERNAL0, 0x04); + + pm_runtime_mark_last_busy(comp->dev); + pm_runtime_put(comp->dev); + + return 0; +} + +static int rt9120_codec_suspend(struct snd_soc_component *comp) +{ + return pm_runtime_force_suspend(comp->dev); +} + +static int rt9120_codec_resume(struct snd_soc_component *comp) +{ + return pm_runtime_force_resume(comp->dev); +} + +static const struct snd_soc_component_driver rt9120_component_driver = { + .probe = rt9120_codec_probe, + .suspend = rt9120_codec_suspend, + .resume = rt9120_codec_resume, + .controls = rt9120_snd_controls, + .num_controls = ARRAY_SIZE(rt9120_snd_controls), + .dapm_widgets = rt9120_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt9120_dapm_widgets), + .dapm_routes = rt9120_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt9120_dapm_routes), + .endianness = 1, +}; + +static int rt9120_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *comp = dai->component; + unsigned int format; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = RT9120_CFG_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = RT9120_CFG_FMT_LEFTJ; + break; + case SND_SOC_DAIFMT_RIGHT_J: + format = RT9120_CFG_FMT_RIGHTJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format = RT9120_CFG_FMT_DSPA; + break; + case SND_SOC_DAIFMT_DSP_B: + format = RT9120_CFG_FMT_DSPB; + break; + default: + dev_err(dai->dev, "Unknown dai format\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(comp, RT9120_REG_I2SFMT, + RT9120_I2SFMT_MASK, + format << RT9120_I2SFMT_SHIFT); + return 0; +} + +static int rt9120_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *param, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *comp = dai->component; + unsigned int param_width, param_slot_width, auto_sync; + int width, fs; + + switch (width = params_width(param)) { + case 16: + param_width = RT9120_CFG_AUDBIT_16; + break; + case 20: + param_width = RT9120_CFG_AUDBIT_20; + break; + case 24: + case 32: + param_width = RT9120_CFG_AUDBIT_24; + break; + default: + dev_err(dai->dev, "Unsupported data width [%d]\n", width); + return -EINVAL; + } + + snd_soc_component_update_bits(comp, RT9120_REG_I2SFMT, + RT9120_AUDBIT_MASK, param_width); + + switch (width = params_physical_width(param)) { + case 16: + param_slot_width = RT9120_CFG_WORDLEN_16; + break; + case 24: + param_slot_width = RT9120_CFG_WORDLEN_24; + break; + case 32: + param_slot_width = RT9120_CFG_WORDLEN_32; + break; + default: + dev_err(dai->dev, "Unsupported slot width [%d]\n", width); + return -EINVAL; + } + + snd_soc_component_update_bits(comp, RT9120_REG_I2SWL, + RT9120_AUDWL_MASK, param_slot_width); + + fs = width * params_channels(param); + /* If fs is divided by 48, disable auto sync */ + if (fs % 48 == 0) + auto_sync = 0; + else + auto_sync = RT9120_AUTOSYNC_MASK; + + snd_soc_component_update_bits(comp, RT9120_REG_DIGCFG, + RT9120_AUTOSYNC_MASK, auto_sync); + return 0; +} + +static const struct snd_soc_dai_ops rt9120_dai_ops = { + .set_fmt = rt9120_set_fmt, + .hw_params = rt9120_hw_params, +}; + +static struct snd_soc_dai_driver rt9120_dai = { + .name = "rt9120_aif", + .playback = { + .stream_name = "AIF Playback", + .rates = RT9120_RATES_MASK, + .formats = RT9120_FMTS_MASK, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .capture = { + .stream_name = "AIF Capture", + .rates = RT9120_RATES_MASK, + .formats = RT9120_FMTS_MASK, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rt9120_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, +}; + +static const struct regmap_range rt9120_rd_yes_ranges[] = { + regmap_reg_range(0x00, 0x0C), + regmap_reg_range(0x10, 0x15), + regmap_reg_range(0x20, 0x27), + regmap_reg_range(0x30, 0x38), + regmap_reg_range(0x3A, 0x40), + regmap_reg_range(0x63, 0x63), + regmap_reg_range(0x65, 0x65), + regmap_reg_range(0x69, 0x69), + regmap_reg_range(0x6C, 0x6C), + regmap_reg_range(0xF8, 0xF8) +}; + +static const struct regmap_access_table rt9120_rd_table = { + .yes_ranges = rt9120_rd_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(rt9120_rd_yes_ranges), +}; + +static const struct regmap_range rt9120_wr_yes_ranges[] = { + regmap_reg_range(0x00, 0x00), + regmap_reg_range(0x02, 0x0A), + regmap_reg_range(0x10, 0x15), + regmap_reg_range(0x20, 0x27), + regmap_reg_range(0x30, 0x38), + regmap_reg_range(0x3A, 0x3D), + regmap_reg_range(0x40, 0x40), + regmap_reg_range(0x63, 0x63), + regmap_reg_range(0x65, 0x65), + regmap_reg_range(0x69, 0x69), + regmap_reg_range(0x6C, 0x6C), + regmap_reg_range(0xF8, 0xF8) +}; + +static const struct regmap_access_table rt9120_wr_table = { + .yes_ranges = rt9120_wr_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(rt9120_wr_yes_ranges), +}; + +static bool rt9120_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x00 ... 0x01: + case 0x10: + case 0x30 ... 0x40: + return true; + default: + return false; + } +} + +static int rt9120_get_reg_size(unsigned int reg) +{ + switch (reg) { + case 0x00: + case 0x20 ... 0x27: + return 2; + case 0x30 ... 0x3D: + return 3; + case 0x3E ... 0x3F: + return 4; + default: + return 1; + } +} + +static int rt9120_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct rt9120_data *data = context; + struct i2c_client *i2c = to_i2c_client(data->dev); + int size = rt9120_get_reg_size(reg); + u8 raw[4] = {0}; + int ret; + + ret = i2c_smbus_read_i2c_block_data(i2c, reg, size, raw); + if (ret < 0) + return ret; + else if (ret != size) + return -EIO; + + switch (size) { + case 4: + *val = be32_to_cpup((__be32 *)raw); + break; + case 3: + *val = raw[0] << 16 | raw[1] << 8 | raw[2]; + break; + case 2: + *val = be16_to_cpup((__be16 *)raw); + break; + default: + *val = raw[0]; + } + + return 0; +} + +static int rt9120_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct rt9120_data *data = context; + struct i2c_client *i2c = to_i2c_client(data->dev); + int size = rt9120_get_reg_size(reg); + __be32 be32_val; + u8 *rawp = (u8 *)&be32_val; + int offs = 4 - size; + + be32_val = cpu_to_be32(val); + return i2c_smbus_write_i2c_block_data(i2c, reg, size, rawp + offs); +} + +static const struct reg_default rt9120_reg_defaults[] = { + { .reg = 0x02, .def = 0x02 }, + { .reg = 0x03, .def = 0xf2 }, + { .reg = 0x04, .def = 0x01 }, + { .reg = 0x05, .def = 0xc0 }, + { .reg = 0x06, .def = 0x28 }, + { .reg = 0x07, .def = 0x04 }, + { .reg = 0x08, .def = 0xff }, + { .reg = 0x09, .def = 0x01 }, + { .reg = 0x0a, .def = 0x01 }, + { .reg = 0x0b, .def = 0x00 }, + { .reg = 0x0c, .def = 0x04 }, + { .reg = 0x11, .def = 0x30 }, + { .reg = 0x12, .def = 0x08 }, + { .reg = 0x13, .def = 0x12 }, + { .reg = 0x14, .def = 0x09 }, + { .reg = 0x15, .def = 0x00 }, + { .reg = 0x20, .def = 0x7ff }, + { .reg = 0x21, .def = 0x180 }, + { .reg = 0x22, .def = 0x180 }, + { .reg = 0x23, .def = 0x00 }, + { .reg = 0x24, .def = 0x80 }, + { .reg = 0x25, .def = 0x180 }, + { .reg = 0x26, .def = 0x640 }, + { .reg = 0x27, .def = 0x180 }, + { .reg = 0x63, .def = 0x5e }, + { .reg = 0x65, .def = 0x66 }, + { .reg = 0x6c, .def = 0xe0 }, + { .reg = 0xf8, .def = 0x44 }, +}; + +static const struct regmap_config rt9120_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = RT9120_REG_DIGCFG, + .reg_defaults = rt9120_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt9120_reg_defaults), + .cache_type = REGCACHE_RBTREE, + + .reg_read = rt9120_reg_read, + .reg_write = rt9120_reg_write, + + .volatile_reg = rt9120_volatile_reg, + .wr_table = &rt9120_wr_table, + .rd_table = &rt9120_rd_table, +}; + +static int rt9120_check_vendor_info(struct rt9120_data *data) +{ + unsigned int devid; + int ret; + + ret = regmap_read(data->regmap, RT9120_REG_DEVID, &devid); + if (ret) + return ret; + + devid = FIELD_GET(RT9120_VID_MASK, devid); + switch (devid) { + case RT9120_VENDOR_ID: + data->chip_idx = CHIP_IDX_RT9120; + break; + case RT9120S_VENDOR_ID: + data->chip_idx = CHIP_IDX_RT9120S; + break; + default: + dev_err(data->dev, "DEVID not correct [0x%0x]\n", devid); + return -ENODEV; + } + + return 0; +} + +static int rt9120_do_register_reset(struct rt9120_data *data) +{ + int ret; + + ret = regmap_write(data->regmap, RT9120_REG_SWRESET, + RT9120_SWRST_MASK); + if (ret) + return ret; + + msleep(RT9120_RESET_WAITMS); + return 0; +} + +static int rt9120_probe(struct i2c_client *i2c) +{ + struct rt9120_data *data; + struct regulator *dvdd_supply; + int dvdd_supply_volt, ret; + + data = devm_kzalloc(&i2c->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = &i2c->dev; + i2c_set_clientdata(i2c, data); + + data->pwdnn_gpio = devm_gpiod_get_optional(&i2c->dev, "pwdnn", + GPIOD_OUT_HIGH); + if (IS_ERR(data->pwdnn_gpio)) { + dev_err(&i2c->dev, "Failed to initialize 'pwdnn' gpio\n"); + return PTR_ERR(data->pwdnn_gpio); + } else if (data->pwdnn_gpio) { + dev_dbg(&i2c->dev, "'pwdnn' from low to high, wait chip on\n"); + msleep(RT9120_CHIPON_WAITMS); + } + + data->regmap = devm_regmap_init(&i2c->dev, NULL, data, + &rt9120_regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + dev_err(&i2c->dev, "Failed to init regmap [%d]\n", ret); + return ret; + } + + ret = rt9120_check_vendor_info(data); + if (ret) { + dev_err(&i2c->dev, "Failed to check vendor info\n"); + return ret; + } + + ret = rt9120_do_register_reset(data); + if (ret) { + dev_err(&i2c->dev, "Failed to do register reset\n"); + return ret; + } + + dvdd_supply = devm_regulator_get(&i2c->dev, "dvdd"); + if (IS_ERR(dvdd_supply)) { + dev_err(&i2c->dev, "No dvdd regulator found\n"); + return PTR_ERR(dvdd_supply); + } + + dvdd_supply_volt = regulator_get_voltage(dvdd_supply); + if (dvdd_supply_volt <= RT9120_LVAPP_THRESUV) { + dev_dbg(&i2c->dev, "dvdd low voltage design\n"); + ret = regmap_update_bits(data->regmap, RT9120_REG_UVPOPT, + RT9120_DVDD_UVSEL_MASK, 0); + if (ret) { + dev_err(&i2c->dev, "Failed to config dvdd uvsel\n"); + return ret; + } + } + + pm_runtime_set_autosuspend_delay(&i2c->dev, 1000); + pm_runtime_use_autosuspend(&i2c->dev); + pm_runtime_set_active(&i2c->dev); + pm_runtime_mark_last_busy(&i2c->dev); + pm_runtime_enable(&i2c->dev); + + return devm_snd_soc_register_component(&i2c->dev, + &rt9120_component_driver, + &rt9120_dai, 1); +} + +static void rt9120_remove(struct i2c_client *i2c) +{ + pm_runtime_disable(&i2c->dev); + pm_runtime_set_suspended(&i2c->dev); +} + +static int __maybe_unused rt9120_runtime_suspend(struct device *dev) +{ + struct rt9120_data *data = dev_get_drvdata(dev); + + if (data->pwdnn_gpio) { + regcache_cache_only(data->regmap, true); + regcache_mark_dirty(data->regmap); + gpiod_set_value(data->pwdnn_gpio, 0); + } + + return 0; +} + +static int __maybe_unused rt9120_runtime_resume(struct device *dev) +{ + struct rt9120_data *data = dev_get_drvdata(dev); + + if (data->pwdnn_gpio) { + gpiod_set_value(data->pwdnn_gpio, 1); + msleep(RT9120_CHIPON_WAITMS); + regcache_cache_only(data->regmap, false); + regcache_sync(data->regmap); + } + + return 0; +} + +static const struct dev_pm_ops rt9120_pm_ops = { + SET_RUNTIME_PM_OPS(rt9120_runtime_suspend, rt9120_runtime_resume, NULL) +}; + +static const struct of_device_id __maybe_unused rt9120_device_table[] = { + { .compatible = "richtek,rt9120", }, + { } +}; +MODULE_DEVICE_TABLE(of, rt9120_device_table); + +static struct i2c_driver rt9120_driver = { + .driver = { + .name = "rt9120", + .of_match_table = rt9120_device_table, + .pm = &rt9120_pm_ops, + }, + .probe = rt9120_probe, + .remove = rt9120_remove, +}; +module_i2c_driver(rt9120_driver); + +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_DESCRIPTION("RT9120 Audio Amplifier Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c new file mode 100644 index 0000000000..5498ff027c --- /dev/null +++ b/sound/soc/codecs/sdw-mockup.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// sdw-mockup.c -- a mockup SoundWire codec for tests where only the host +// drives the bus. +// +// Copyright(c) 2021 Intel Corporation +// +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct sdw_mockup_priv { + struct sdw_slave *slave; +}; + +static int sdw_mockup_component_probe(struct snd_soc_component *component) +{ + return 0; +} + +static void sdw_mockup_component_remove(struct snd_soc_component *component) +{ +} + +static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = { + .probe = sdw_mockup_component_probe, + .remove = sdw_mockup_component_remove, + .endianness = 1, +}; + +static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void sdw_mockup_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + int ret; + + if (!sdw_stream) + return -EINVAL; + + if (!sdw_mockup->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + port_config.num = 1; + else + port_config.num = 8; + + ret = sdw_stream_add_slave(sdw_mockup->slave, &stream_config, + &port_config, 1, sdw_stream); + if (ret) + dev_err(dai->dev, "Unable to configure port\n"); + + return ret; +} + +static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_mockup->slave) + return -EINVAL; + + sdw_stream_remove_slave(sdw_mockup->slave, sdw_stream); + return 0; +} + +static const struct snd_soc_dai_ops sdw_mockup_ops = { + .hw_params = sdw_mockup_pcm_hw_params, + .hw_free = sdw_mockup_pcm_hw_free, + .set_stream = sdw_mockup_set_sdw_stream, + .shutdown = sdw_mockup_shutdown, +}; + +static struct snd_soc_dai_driver sdw_mockup_dai[] = { + { + .name = "sdw-mockup-aif1", + .id = 1, + .playback = { + .stream_name = "DP1 Playback", + .channels_min = 1, + .channels_max = 2, + }, + .capture = { + .stream_name = "DP8 Capture", + .channels_min = 1, + .channels_max = 2, + }, + .ops = &sdw_mockup_ops, + }, +}; + +static int sdw_mockup_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + return 0; +} + +static int sdw_mockup_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = false; + + /* + * first we need to allocate memory for set bits in port lists + * the port allocation is completely arbitrary: + * DP0 is not supported + * DP1 is sink + * DP8 is source + */ + prop->source_ports = BIT(8); + prop->sink_ports = BIT(1); + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + j++; + } + + prop->simple_clk_stop_capable = true; + + /* wake-up event */ + prop->wake_capable = 0; + + return 0; +} + +static int sdw_mockup_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + return 0; +} + +static int sdw_mockup_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + return 0; +} + +static const struct sdw_slave_ops sdw_mockup_slave_ops = { + .read_prop = sdw_mockup_read_prop, + .interrupt_callback = sdw_mockup_interrupt_callback, + .update_status = sdw_mockup_update_status, + .bus_config = sdw_mockup_bus_config, +}; + +static int sdw_mockup_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct device *dev = &slave->dev; + struct sdw_mockup_priv *sdw_mockup; + int ret; + + sdw_mockup = devm_kzalloc(dev, sizeof(*sdw_mockup), GFP_KERNEL); + if (!sdw_mockup) + return -ENOMEM; + + dev_set_drvdata(dev, sdw_mockup); + sdw_mockup->slave = slave; + + slave->is_mockup_device = true; + + ret = devm_snd_soc_register_component(dev, + &snd_soc_sdw_mockup_component, + sdw_mockup_dai, + ARRAY_SIZE(sdw_mockup_dai)); + + return ret; +} + +static int sdw_mockup_sdw_remove(struct sdw_slave *slave) +{ + return 0; +} + +/* + * Intel reserved parts ID with the following mapping expected: + * 0xAAAA: generic full-duplex codec + * 0xAA55: headset codec (mock-up of RT711/RT5682) - full-duplex + * 0x55AA: amplifier (mock-up of RT1308/Maxim 98373) - playback only with + * IV feedback + * 0x5555: mic codec (mock-up of RT715) - capture-only + */ +static const struct sdw_device_id sdw_mockup_id[] = { + SDW_SLAVE_ENTRY_EXT(0x0105, 0xAAAA, 0x0, 0, 0), + SDW_SLAVE_ENTRY_EXT(0x0105, 0xAA55, 0x0, 0, 0), + SDW_SLAVE_ENTRY_EXT(0x0105, 0x55AA, 0x0, 0, 0), + SDW_SLAVE_ENTRY_EXT(0x0105, 0x5555, 0x0, 0, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, sdw_mockup_id); + +static struct sdw_driver sdw_mockup_sdw_driver = { + .driver = { + .name = "sdw-mockup", + .owner = THIS_MODULE, + }, + .probe = sdw_mockup_sdw_probe, + .remove = sdw_mockup_sdw_remove, + .ops = &sdw_mockup_slave_ops, + .id_table = sdw_mockup_id, +}; +module_sdw_driver(sdw_mockup_sdw_driver); + +MODULE_DESCRIPTION("ASoC SDW mockup codec driver"); +MODULE_AUTHOR("Pierre-Louis Bossart "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c new file mode 100644 index 0000000000..b22ba95bd0 --- /dev/null +++ b/sound/soc/codecs/sgtl5000.c @@ -0,0 +1,1839 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// sgtl5000.c -- SGTL5000 ALSA SoC Audio driver +// +// Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sgtl5000.h" + +#define SGTL5000_DAP_REG_OFFSET 0x0100 +#define SGTL5000_MAX_REG_OFFSET 0x013A + +/* Delay for the VAG ramp up */ +#define SGTL5000_VAG_POWERUP_DELAY 500 /* ms */ +/* Delay for the VAG ramp down */ +#define SGTL5000_VAG_POWERDOWN_DELAY 500 /* ms */ + +#define SGTL5000_OUTPUTS_MUTE (SGTL5000_HP_MUTE | SGTL5000_LINE_OUT_MUTE) + +/* default value of sgtl5000 registers */ +static const struct reg_default sgtl5000_reg_defaults[] = { + { SGTL5000_CHIP_DIG_POWER, 0x0000 }, + { SGTL5000_CHIP_I2S_CTRL, 0x0010 }, + { SGTL5000_CHIP_SSS_CTRL, 0x0010 }, + { SGTL5000_CHIP_ADCDAC_CTRL, 0x020c }, + { SGTL5000_CHIP_DAC_VOL, 0x3c3c }, + { SGTL5000_CHIP_PAD_STRENGTH, 0x015f }, + { SGTL5000_CHIP_ANA_ADC_CTRL, 0x0000 }, + { SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 }, + { SGTL5000_CHIP_ANA_CTRL, 0x0111 }, + { SGTL5000_CHIP_REF_CTRL, 0x0000 }, + { SGTL5000_CHIP_MIC_CTRL, 0x0000 }, + { SGTL5000_CHIP_LINE_OUT_CTRL, 0x0000 }, + { SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 }, + { SGTL5000_CHIP_PLL_CTRL, 0x5000 }, + { SGTL5000_CHIP_CLK_TOP_CTRL, 0x0000 }, + { SGTL5000_CHIP_ANA_STATUS, 0x0000 }, + { SGTL5000_CHIP_SHORT_CTRL, 0x0000 }, + { SGTL5000_CHIP_ANA_TEST2, 0x0000 }, + { SGTL5000_DAP_CTRL, 0x0000 }, + { SGTL5000_DAP_PEQ, 0x0000 }, + { SGTL5000_DAP_BASS_ENHANCE, 0x0040 }, + { SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f }, + { SGTL5000_DAP_AUDIO_EQ, 0x0000 }, + { SGTL5000_DAP_SURROUND, 0x0040 }, + { SGTL5000_DAP_EQ_BASS_BAND0, 0x002f }, + { SGTL5000_DAP_EQ_BASS_BAND1, 0x002f }, + { SGTL5000_DAP_EQ_BASS_BAND2, 0x002f }, + { SGTL5000_DAP_EQ_BASS_BAND3, 0x002f }, + { SGTL5000_DAP_EQ_BASS_BAND4, 0x002f }, + { SGTL5000_DAP_MAIN_CHAN, 0x8000 }, + { SGTL5000_DAP_MIX_CHAN, 0x0000 }, + { SGTL5000_DAP_AVC_CTRL, 0x5100 }, + { SGTL5000_DAP_AVC_THRESHOLD, 0x1473 }, + { SGTL5000_DAP_AVC_ATTACK, 0x0028 }, + { SGTL5000_DAP_AVC_DECAY, 0x0050 }, +}; + +/* AVC: Threshold dB -> register: pre-calculated values */ +static const u16 avc_thr_db2reg[97] = { + 0x5168, 0x488E, 0x40AA, 0x39A1, 0x335D, 0x2DC7, 0x28CC, 0x245D, 0x2068, + 0x1CE2, 0x19BE, 0x16F1, 0x1472, 0x1239, 0x103E, 0x0E7A, 0x0CE6, 0x0B7F, + 0x0A3F, 0x0922, 0x0824, 0x0741, 0x0677, 0x05C3, 0x0522, 0x0493, 0x0414, + 0x03A2, 0x033D, 0x02E3, 0x0293, 0x024B, 0x020B, 0x01D2, 0x019F, 0x0172, + 0x014A, 0x0126, 0x0106, 0x00E9, 0x00D0, 0x00B9, 0x00A5, 0x0093, 0x0083, + 0x0075, 0x0068, 0x005D, 0x0052, 0x0049, 0x0041, 0x003A, 0x0034, 0x002E, + 0x0029, 0x0025, 0x0021, 0x001D, 0x001A, 0x0017, 0x0014, 0x0012, 0x0010, + 0x000E, 0x000D, 0x000B, 0x000A, 0x0009, 0x0008, 0x0007, 0x0006, 0x0005, + 0x0005, 0x0004, 0x0004, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, 0x0002, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; + +/* regulator supplies for sgtl5000, VDDD is an optional external supply */ +enum sgtl5000_regulator_supplies { + VDDA, + VDDIO, + VDDD, + SGTL5000_SUPPLY_NUM +}; + +/* vddd is optional supply */ +static const char *supply_names[SGTL5000_SUPPLY_NUM] = { + "VDDA", + "VDDIO", + "VDDD" +}; + +#define LDO_VOLTAGE 1200000 +#define LINREG_VDDD ((1600 - LDO_VOLTAGE / 1000) / 50) + +enum sgtl5000_micbias_resistor { + SGTL5000_MICBIAS_OFF = 0, + SGTL5000_MICBIAS_2K = 2, + SGTL5000_MICBIAS_4K = 4, + SGTL5000_MICBIAS_8K = 8, +}; + +enum { + I2S_LRCLK_STRENGTH_DISABLE, + I2S_LRCLK_STRENGTH_LOW, + I2S_LRCLK_STRENGTH_MEDIUM, + I2S_LRCLK_STRENGTH_HIGH, +}; + +enum { + I2S_SCLK_STRENGTH_DISABLE, + I2S_SCLK_STRENGTH_LOW, + I2S_SCLK_STRENGTH_MEDIUM, + I2S_SCLK_STRENGTH_HIGH, +}; + +enum { + HP_POWER_EVENT, + DAC_POWER_EVENT, + ADC_POWER_EVENT, + LAST_POWER_EVENT = ADC_POWER_EVENT +}; + +/* sgtl5000 private structure in codec */ +struct sgtl5000_priv { + int sysclk; /* sysclk rate */ + int master; /* i2s master or not */ + int fmt; /* i2s data format */ + struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM]; + int num_supplies; + struct regmap *regmap; + struct clk *mclk; + int revision; + u8 micbias_resistor; + u8 micbias_voltage; + u8 lrclk_strength; + u8 sclk_strength; + u16 mute_state[LAST_POWER_EVENT + 1]; +}; + +static inline int hp_sel_input(struct snd_soc_component *component) +{ + return (snd_soc_component_read(component, SGTL5000_CHIP_ANA_CTRL) & + SGTL5000_HP_SEL_MASK) >> SGTL5000_HP_SEL_SHIFT; +} + +static inline u16 mute_output(struct snd_soc_component *component, + u16 mute_mask) +{ + u16 mute_reg = snd_soc_component_read(component, + SGTL5000_CHIP_ANA_CTRL); + + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL, + mute_mask, mute_mask); + return mute_reg; +} + +static inline void restore_output(struct snd_soc_component *component, + u16 mute_mask, u16 mute_reg) +{ + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL, + mute_mask, mute_reg); +} + +static void vag_power_on(struct snd_soc_component *component, u32 source) +{ + if (snd_soc_component_read(component, SGTL5000_CHIP_ANA_POWER) & + SGTL5000_VAG_POWERUP) + return; + + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP); + + /* When VAG powering on to get local loop from Line-In, the sleep + * is required to avoid loud pop. + */ + if (hp_sel_input(component) == SGTL5000_HP_SEL_LINE_IN && + source == HP_POWER_EVENT) + msleep(SGTL5000_VAG_POWERUP_DELAY); +} + +static int vag_power_consumers(struct snd_soc_component *component, + u16 ana_pwr_reg, u32 source) +{ + int consumers = 0; + + /* count dac/adc consumers unconditional */ + if (ana_pwr_reg & SGTL5000_DAC_POWERUP) + consumers++; + if (ana_pwr_reg & SGTL5000_ADC_POWERUP) + consumers++; + + /* + * If the event comes from HP and Line-In is selected, + * current action is 'DAC to be powered down'. + * As HP_POWERUP is not set when HP muxed to line-in, + * we need to keep VAG power ON. + */ + if (source == HP_POWER_EVENT) { + if (hp_sel_input(component) == SGTL5000_HP_SEL_LINE_IN) + consumers++; + } else { + if (ana_pwr_reg & SGTL5000_HP_POWERUP) + consumers++; + } + + return consumers; +} + +static void vag_power_off(struct snd_soc_component *component, u32 source) +{ + u16 ana_pwr = snd_soc_component_read(component, + SGTL5000_CHIP_ANA_POWER); + + if (!(ana_pwr & SGTL5000_VAG_POWERUP)) + return; + + /* + * This function calls when any of VAG power consumers is disappearing. + * Thus, if there is more than one consumer at the moment, as minimum + * one consumer will definitely stay after the end of the current + * event. + * Don't clear VAG_POWERUP if 2 or more consumers of VAG present: + * - LINE_IN (for HP events) / HP (for DAC/ADC events) + * - DAC + * - ADC + * (the current consumer is disappearing right now) + */ + if (vag_power_consumers(component, ana_pwr, source) >= 2) + return; + + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, 0); + /* In power down case, we need wait 400-1000 ms + * when VAG fully ramped down. + * As longer we wait, as smaller pop we've got. + */ + msleep(SGTL5000_VAG_POWERDOWN_DELAY); +} + +/* + * mic_bias power on/off share the same register bits with + * output impedance of mic bias, when power on mic bias, we + * need reclaim it to impedance value. + * 0x0 = Powered off + * 0x1 = 2Kohm + * 0x2 = 4Kohm + * 0x3 = 8Kohm + */ +static int mic_bias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* change mic bias resistor */ + snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_MASK, + sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_MASK, 0); + break; + } + return 0; +} + +static int vag_and_mute_control(struct snd_soc_component *component, + int event, int event_source) +{ + static const u16 mute_mask[] = { + /* + * Mask for HP_POWER_EVENT. + * Muxing Headphones have to be wrapped with mute/unmute + * headphones only. + */ + SGTL5000_HP_MUTE, + /* + * Masks for DAC_POWER_EVENT/ADC_POWER_EVENT. + * Muxing DAC or ADC block have to wrapped with mute/unmute + * both headphones and line-out. + */ + SGTL5000_OUTPUTS_MUTE, + SGTL5000_OUTPUTS_MUTE + }; + + struct sgtl5000_priv *sgtl5000 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + sgtl5000->mute_state[event_source] = + mute_output(component, mute_mask[event_source]); + break; + case SND_SOC_DAPM_POST_PMU: + vag_power_on(component, event_source); + restore_output(component, mute_mask[event_source], + sgtl5000->mute_state[event_source]); + break; + case SND_SOC_DAPM_PRE_PMD: + sgtl5000->mute_state[event_source] = + mute_output(component, mute_mask[event_source]); + vag_power_off(component, event_source); + break; + case SND_SOC_DAPM_POST_PMD: + restore_output(component, mute_mask[event_source], + sgtl5000->mute_state[event_source]); + break; + default: + break; + } + + return 0; +} + +/* + * Mute Headphone when power it up/down. + * Control VAG power on HP power path. + */ +static int headphone_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + return vag_and_mute_control(component, event, HP_POWER_EVENT); +} + +/* As manual describes, ADC/DAC powering up/down requires + * to mute outputs to avoid pops. + * Control VAG power on ADC/DAC power path. + */ +static int adc_updown_depop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + return vag_and_mute_control(component, event, ADC_POWER_EVENT); +} + +static int dac_updown_depop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + return vag_and_mute_control(component, event, DAC_POWER_EVENT); +} + +/* input sources for ADC */ +static const char *adc_mux_text[] = { + "MIC_IN", "LINE_IN" +}; + +static SOC_ENUM_SINGLE_DECL(adc_enum, + SGTL5000_CHIP_ANA_CTRL, 2, + adc_mux_text); + +static const struct snd_kcontrol_new adc_mux = +SOC_DAPM_ENUM("Capture Mux", adc_enum); + +/* input sources for headphone */ +static const char *hp_mux_text[] = { + "DAC", "LINE_IN" +}; + +static SOC_ENUM_SINGLE_DECL(hp_enum, + SGTL5000_CHIP_ANA_CTRL, 6, + hp_mux_text); + +static const struct snd_kcontrol_new hp_mux = +SOC_DAPM_ENUM("Headphone Mux", hp_enum); + +/* input sources for DAC */ +static const char *dac_mux_text[] = { + "ADC", "I2S", "Rsvrd", "DAP" +}; + +static SOC_ENUM_SINGLE_DECL(dac_enum, + SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAC_SEL_SHIFT, + dac_mux_text); + +static const struct snd_kcontrol_new dac_mux = +SOC_DAPM_ENUM("Digital Input Mux", dac_enum); + +/* input sources for DAP */ +static const char *dap_mux_text[] = { + "ADC", "I2S" +}; + +static SOC_ENUM_SINGLE_DECL(dap_enum, + SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAP_SEL_SHIFT, + dap_mux_text); + +static const struct snd_kcontrol_new dap_mux = +SOC_DAPM_ENUM("DAP Mux", dap_enum); + +/* input sources for DAP mix */ +static const char *dapmix_mux_text[] = { + "ADC", "I2S" +}; + +static SOC_ENUM_SINGLE_DECL(dapmix_enum, + SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAP_MIX_SEL_SHIFT, + dapmix_mux_text); + +static const struct snd_kcontrol_new dapmix_mux = +SOC_DAPM_ENUM("DAP MIX Mux", dapmix_enum); + + +static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("LINE_IN"), + SND_SOC_DAPM_INPUT("MIC_IN"), + + SND_SOC_DAPM_OUTPUT("HP_OUT"), + SND_SOC_DAPM_OUTPUT("LINE_OUT"), + + SND_SOC_DAPM_SUPPLY("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0, + mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0, + headphone_pga_event, + SND_SOC_DAPM_PRE_POST_PMU | + SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_PGA("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux), + SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &hp_mux), + SND_SOC_DAPM_MUX("Digital Input Mux", SND_SOC_NOPM, 0, 0, &dac_mux), + SND_SOC_DAPM_MUX("DAP Mux", SGTL5000_DAP_CTRL, 0, 0, &dap_mux), + SND_SOC_DAPM_MUX("DAP MIX Mux", SGTL5000_DAP_CTRL, 4, 0, &dapmix_mux), + SND_SOC_DAPM_MIXER("DAP", SGTL5000_CHIP_DIG_POWER, 4, 0, NULL, 0), + + + /* aif for i2s input */ + SND_SOC_DAPM_AIF_IN("AIFIN", "Playback", + 0, SGTL5000_CHIP_DIG_POWER, + 0, 0), + + /* aif for i2s output */ + SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture", + 0, SGTL5000_CHIP_DIG_POWER, + 1, 0), + + SND_SOC_DAPM_ADC_E("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0, + adc_updown_depop, SND_SOC_DAPM_PRE_POST_PMU | + SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0, + dac_updown_depop, SND_SOC_DAPM_PRE_POST_PMU | + SND_SOC_DAPM_PRE_POST_PMD), +}; + +/* routes for sgtl5000 */ +static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = { + {"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */ + {"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */ + + {"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */ + {"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */ + + {"DAP Mux", "ADC", "ADC"}, /* adc --> DAP mux */ + {"DAP Mux", NULL, "AIFIN"}, /* i2s --> DAP mux */ + {"DAP", NULL, "DAP Mux"}, /* DAP mux --> dap */ + + {"DAP MIX Mux", "ADC", "ADC"}, /* adc --> DAP MIX mux */ + {"DAP MIX Mux", NULL, "AIFIN"}, /* i2s --> DAP MIX mux */ + {"DAP", NULL, "DAP MIX Mux"}, /* DAP MIX mux --> dap */ + + {"Digital Input Mux", "ADC", "ADC"}, /* adc --> audio mux */ + {"Digital Input Mux", NULL, "AIFIN"}, /* i2s --> audio mux */ + {"Digital Input Mux", NULL, "DAP"}, /* dap --> audio mux */ + {"DAC", NULL, "Digital Input Mux"}, /* audio mux --> dac */ + + {"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */ + {"LO", NULL, "DAC"}, /* dac --> line_out */ + + {"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */ + {"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */ + + {"LINE_OUT", NULL, "LO"}, + {"HP_OUT", NULL, "HP"}, +}; + +/* custom function to fetch info of PCM playback volume */ +static int dac_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xfc - 0x3c; + return 0; +} + +/* + * custom function to get of PCM playback volume + * + * dac volume register + * 15-------------8-7--------------0 + * | R channel vol | L channel vol | + * ------------------------------- + * + * PCM volume with 0.5017 dB steps from 0 to -90 dB + * + * register values map to dB + * 0x3B and less = Reserved + * 0x3C = 0 dB + * 0x3D = -0.5 dB + * 0xF0 = -90 dB + * 0xFC and greater = Muted + * + * register value map to userspace value + * + * register value 0x3c(0dB) 0xf0(-90dB)0xfc + * ------------------------------ + * userspace value 0xc0 0 + */ +static int dac_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int reg; + int l; + int r; + + reg = snd_soc_component_read(component, SGTL5000_CHIP_DAC_VOL); + + /* get left channel volume */ + l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT; + + /* get right channel volume */ + r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) >> SGTL5000_DAC_VOL_RIGHT_SHIFT; + + /* make sure value fall in (0x3c,0xfc) */ + l = clamp(l, 0x3c, 0xfc); + r = clamp(r, 0x3c, 0xfc); + + /* invert it and map to userspace value */ + l = 0xfc - l; + r = 0xfc - r; + + ucontrol->value.integer.value[0] = l; + ucontrol->value.integer.value[1] = r; + + return 0; +} + +/* + * custom function to put of PCM playback volume + * + * dac volume register + * 15-------------8-7--------------0 + * | R channel vol | L channel vol | + * ------------------------------- + * + * PCM volume with 0.5017 dB steps from 0 to -90 dB + * + * register values map to dB + * 0x3B and less = Reserved + * 0x3C = 0 dB + * 0x3D = -0.5 dB + * 0xF0 = -90 dB + * 0xFC and greater = Muted + * + * userspace value map to register value + * + * userspace value 0xc0 0 + * ------------------------------ + * register value 0x3c(0dB) 0xf0(-90dB)0xfc + */ +static int dac_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int reg; + int l; + int r; + + l = ucontrol->value.integer.value[0]; + r = ucontrol->value.integer.value[1]; + + /* make sure userspace volume fall in (0, 0xfc-0x3c) */ + l = clamp(l, 0, 0xfc - 0x3c); + r = clamp(r, 0, 0xfc - 0x3c); + + /* invert it, get the value can be set to register */ + l = 0xfc - l; + r = 0xfc - r; + + /* shift to get the register value */ + reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT | + r << SGTL5000_DAC_VOL_RIGHT_SHIFT; + + snd_soc_component_write(component, SGTL5000_CHIP_DAC_VOL, reg); + + return 0; +} + +/* + * custom function to get AVC threshold + * + * The threshold dB is calculated by rearranging the calculation from the + * avc_put_threshold function: register_value = 10^(dB/20) * 0.636 * 2^15 ==> + * dB = ( fls(register_value) - 14.347 ) * 6.02 + * + * As this calculation is expensive and the threshold dB values may not exceed + * 0 to 96 we use pre-calculated values. + */ +static int avc_get_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int db, i; + u16 reg = snd_soc_component_read(component, SGTL5000_DAP_AVC_THRESHOLD); + + /* register value 0 => -96dB */ + if (!reg) { + ucontrol->value.integer.value[0] = 96; + ucontrol->value.integer.value[1] = 96; + return 0; + } + + /* get dB from register value (rounded down) */ + for (i = 0; avc_thr_db2reg[i] > reg; i++) + ; + db = i; + + ucontrol->value.integer.value[0] = db; + ucontrol->value.integer.value[1] = db; + + return 0; +} + +/* + * custom function to put AVC threshold + * + * The register value is calculated by following formula: + * register_value = 10^(dB/20) * 0.636 * 2^15 + * As this calculation is expensive and the threshold dB values may not exceed + * 0 to 96 we use pre-calculated values. + */ +static int avc_put_threshold(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int db; + u16 reg; + + db = (int)ucontrol->value.integer.value[0]; + if (db < 0 || db > 96) + return -EINVAL; + reg = avc_thr_db2reg[db]; + snd_soc_component_write(component, SGTL5000_DAP_AVC_THRESHOLD, reg); + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0); + +/* tlv for mic gain, 0db 20db 30db 40db */ +static const DECLARE_TLV_DB_RANGE(mic_gain_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0) +); + +/* tlv for DAP channels, 0% - 100% - 200% */ +static const DECLARE_TLV_DB_SCALE(dap_volume, 0, 1, 0); + +/* tlv for bass bands, -11.75db to 12.0db, step .25db */ +static const DECLARE_TLV_DB_SCALE(bass_band, -1175, 25, 0); + +/* tlv for hp volume, -51.5db to 12.0db, step .5db */ +static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0); + +/* tlv for lineout volume, 31 steps of .5db each */ +static const DECLARE_TLV_DB_SCALE(lineout_volume, -1550, 50, 0); + +/* tlv for dap avc max gain, 0db, 6db, 12db */ +static const DECLARE_TLV_DB_SCALE(avc_max_gain, 0, 600, 0); + +/* tlv for dap avc threshold, */ +static const DECLARE_TLV_DB_MINMAX(avc_threshold, 0, 9600); + +static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { + /* SOC_DOUBLE_S8_TLV with invert */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = dac_info_volsw, + .get = dac_get_volsw, + .put = dac_put_volsw, + }, + + SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0), + SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)", + SGTL5000_CHIP_ANA_ADC_CTRL, + 8, 1, 0, capture_6db_attenuate), + SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0), + SOC_SINGLE("Capture Switch", SGTL5000_CHIP_ANA_CTRL, 0, 1, 1), + + SOC_DOUBLE_TLV("Headphone Playback Volume", + SGTL5000_CHIP_ANA_HP_CTRL, + 0, 8, + 0x7f, 1, + headphone_volume), + SOC_SINGLE("Headphone Playback Switch", SGTL5000_CHIP_ANA_CTRL, + 4, 1, 1), + SOC_SINGLE("Headphone Playback ZC Switch", SGTL5000_CHIP_ANA_CTRL, + 5, 1, 0), + + SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL, + 0, 3, 0, mic_gain_tlv), + + SOC_DOUBLE_TLV("Lineout Playback Volume", + SGTL5000_CHIP_LINE_OUT_VOL, + SGTL5000_LINE_OUT_VOL_LEFT_SHIFT, + SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT, + 0x1f, 1, + lineout_volume), + SOC_SINGLE("Lineout Playback Switch", SGTL5000_CHIP_ANA_CTRL, 8, 1, 1), + + SOC_SINGLE_TLV("DAP Main channel", SGTL5000_DAP_MAIN_CHAN, + 0, 0xffff, 0, dap_volume), + + SOC_SINGLE_TLV("DAP Mix channel", SGTL5000_DAP_MIX_CHAN, + 0, 0xffff, 0, dap_volume), + /* Automatic Volume Control (DAP AVC) */ + SOC_SINGLE("AVC Switch", SGTL5000_DAP_AVC_CTRL, 0, 1, 0), + SOC_SINGLE("AVC Hard Limiter Switch", SGTL5000_DAP_AVC_CTRL, 5, 1, 0), + SOC_SINGLE_TLV("AVC Max Gain Volume", SGTL5000_DAP_AVC_CTRL, 12, 2, 0, + avc_max_gain), + SOC_SINGLE("AVC Integrator Response", SGTL5000_DAP_AVC_CTRL, 8, 3, 0), + SOC_SINGLE_EXT_TLV("AVC Threshold Volume", SGTL5000_DAP_AVC_THRESHOLD, + 0, 96, 0, avc_get_threshold, avc_put_threshold, + avc_threshold), + + SOC_SINGLE_TLV("BASS 0", SGTL5000_DAP_EQ_BASS_BAND0, + 0, 0x5F, 0, bass_band), + + SOC_SINGLE_TLV("BASS 1", SGTL5000_DAP_EQ_BASS_BAND1, + 0, 0x5F, 0, bass_band), + + SOC_SINGLE_TLV("BASS 2", SGTL5000_DAP_EQ_BASS_BAND2, + 0, 0x5F, 0, bass_band), + + SOC_SINGLE_TLV("BASS 3", SGTL5000_DAP_EQ_BASS_BAND3, + 0, 0x5F, 0, bass_band), + + SOC_SINGLE_TLV("BASS 4", SGTL5000_DAP_EQ_BASS_BAND4, + 0, 0x5F, 0, bass_band), +}; + +/* mute the codec used by alsa core */ +static int sgtl5000_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + u16 i2s_pwr = SGTL5000_I2S_IN_POWERUP; + + /* + * During 'digital mute' do not mute DAC + * because LINE_IN would be muted aswell. We want to mute + * only I2S block - this can be done by powering it off + */ + snd_soc_component_update_bits(component, SGTL5000_CHIP_DIG_POWER, + i2s_pwr, mute ? 0 : i2s_pwr); + + return 0; +} + +/* set codec format */ +static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); + u16 i2sctl = 0; + + sgtl5000->master = 0; + /* + * i2s clock and frame master setting. + * ONLY support: + * - clock and frame slave, + * - clock and frame master + */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + i2sctl |= SGTL5000_I2S_MASTER; + sgtl5000->master = 1; + break; + default: + return -EINVAL; + } + + /* setting i2s data format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + i2sctl |= SGTL5000_I2S_MODE_PCM << SGTL5000_I2S_MODE_SHIFT; + break; + case SND_SOC_DAIFMT_DSP_B: + i2sctl |= SGTL5000_I2S_MODE_PCM << SGTL5000_I2S_MODE_SHIFT; + i2sctl |= SGTL5000_I2S_LRALIGN; + break; + case SND_SOC_DAIFMT_I2S: + i2sctl |= SGTL5000_I2S_MODE_I2S_LJ << SGTL5000_I2S_MODE_SHIFT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + i2sctl |= SGTL5000_I2S_MODE_RJ << SGTL5000_I2S_MODE_SHIFT; + i2sctl |= SGTL5000_I2S_LRPOL; + break; + case SND_SOC_DAIFMT_LEFT_J: + i2sctl |= SGTL5000_I2S_MODE_I2S_LJ << SGTL5000_I2S_MODE_SHIFT; + i2sctl |= SGTL5000_I2S_LRALIGN; + break; + default: + return -EINVAL; + } + + sgtl5000->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + /* Clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + i2sctl |= SGTL5000_I2S_SCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, SGTL5000_CHIP_I2S_CTRL, i2sctl); + + return 0; +} + +/* set codec sysclk */ +static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case SGTL5000_SYSCLK: + sgtl5000->sysclk = freq; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * set clock according to i2s frame clock, + * sgtl5000 provides 2 clock sources: + * 1. sys_mclk: sample freq can only be configured to + * 1/256, 1/384, 1/512 of sys_mclk. + * 2. pll: can derive any audio clocks. + * + * clock setting rules: + * 1. in slave mode, only sys_mclk can be used + * 2. as constraint by sys_mclk, sample freq should be set to 32 kHz, 44.1 kHz + * and above. + * 3. usage of sys_mclk is preferred over pll to save power. + */ +static int sgtl5000_set_clock(struct snd_soc_component *component, int frame_rate) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); + int clk_ctl = 0; + int sys_fs; /* sample freq */ + + /* + * sample freq should be divided by frame clock, + * if frame clock is lower than 44.1 kHz, sample freq should be set to + * 32 kHz or 44.1 kHz. + */ + switch (frame_rate) { + case 8000: + case 16000: + sys_fs = 32000; + break; + case 11025: + case 22050: + sys_fs = 44100; + break; + default: + sys_fs = frame_rate; + break; + } + + /* set divided factor of frame clock */ + switch (sys_fs / frame_rate) { + case 4: + clk_ctl |= SGTL5000_RATE_MODE_DIV_4 << SGTL5000_RATE_MODE_SHIFT; + break; + case 2: + clk_ctl |= SGTL5000_RATE_MODE_DIV_2 << SGTL5000_RATE_MODE_SHIFT; + break; + case 1: + clk_ctl |= SGTL5000_RATE_MODE_DIV_1 << SGTL5000_RATE_MODE_SHIFT; + break; + default: + return -EINVAL; + } + + /* set the sys_fs according to frame rate */ + switch (sys_fs) { + case 32000: + clk_ctl |= SGTL5000_SYS_FS_32k << SGTL5000_SYS_FS_SHIFT; + break; + case 44100: + clk_ctl |= SGTL5000_SYS_FS_44_1k << SGTL5000_SYS_FS_SHIFT; + break; + case 48000: + clk_ctl |= SGTL5000_SYS_FS_48k << SGTL5000_SYS_FS_SHIFT; + break; + case 96000: + clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT; + break; + default: + dev_err(component->dev, "frame rate %d not supported\n", + frame_rate); + return -EINVAL; + } + + /* + * calculate the divider of mclk/sample_freq, + * factor of freq = 96 kHz can only be 256, since mclk is in the range + * of 8 MHz - 27 MHz + */ + switch (sgtl5000->sysclk / frame_rate) { + case 256: + clk_ctl |= SGTL5000_MCLK_FREQ_256FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + case 384: + clk_ctl |= SGTL5000_MCLK_FREQ_384FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + case 512: + clk_ctl |= SGTL5000_MCLK_FREQ_512FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + default: + /* if mclk does not satisfy the divider, use pll */ + if (sgtl5000->master) { + clk_ctl |= SGTL5000_MCLK_FREQ_PLL << + SGTL5000_MCLK_FREQ_SHIFT; + } else { + dev_err(component->dev, + "PLL not supported in slave mode\n"); + dev_err(component->dev, "%d ratio is not supported. " + "SYS_MCLK needs to be 256, 384 or 512 * fs\n", + sgtl5000->sysclk / frame_rate); + return -EINVAL; + } + } + + /* if using pll, please check manual 6.4.2 for detail */ + if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) { + u64 out, t; + int div2; + int pll_ctl; + unsigned int in, int_div, frac_div; + + if (sgtl5000->sysclk > 17000000) { + div2 = 1; + in = sgtl5000->sysclk / 2; + } else { + div2 = 0; + in = sgtl5000->sysclk; + } + if (sys_fs == 44100) + out = 180633600; + else + out = 196608000; + t = do_div(out, in); + int_div = out; + t *= 2048; + do_div(t, in); + frac_div = t; + pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT | + frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT; + + snd_soc_component_write(component, SGTL5000_CHIP_PLL_CTRL, pll_ctl); + if (div2) + snd_soc_component_update_bits(component, + SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INPUT_FREQ_DIV2, + SGTL5000_INPUT_FREQ_DIV2); + else + snd_soc_component_update_bits(component, + SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INPUT_FREQ_DIV2, + 0); + + /* power up pll */ + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP); + + /* if using pll, clk_ctrl must be set after pll power up */ + snd_soc_component_write(component, SGTL5000_CHIP_CLK_CTRL, clk_ctl); + } else { + /* otherwise, clk_ctrl must be set before pll power down */ + snd_soc_component_write(component, SGTL5000_CHIP_CLK_CTRL, clk_ctl); + + /* power down pll */ + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, + 0); + } + + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + * input: params_rate, params_fmt + */ +static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); + int channels = params_channels(params); + int i2s_ctl = 0; + int stereo; + int ret; + + /* sysclk should already set */ + if (!sgtl5000->sysclk) { + dev_err(component->dev, "%s: set sysclk first!\n", __func__); + return -EFAULT; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + stereo = SGTL5000_DAC_STEREO; + else + stereo = SGTL5000_ADC_STEREO; + + /* set mono to save power */ + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, stereo, + channels == 1 ? 0 : stereo); + + /* set codec clock base on lrclk */ + ret = sgtl5000_set_clock(component, params_rate(params)); + if (ret) + return ret; + + /* set i2s data format */ + switch (params_width(params)) { + case 16: + if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J) + return -EINVAL; + i2s_ctl |= SGTL5000_I2S_DLEN_16 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_32FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case 20: + i2s_ctl |= SGTL5000_I2S_DLEN_20 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case 24: + i2s_ctl |= SGTL5000_I2S_DLEN_24 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case 32: + if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J) + return -EINVAL; + i2s_ctl |= SGTL5000_I2S_DLEN_32 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, SGTL5000_CHIP_I2S_CTRL, + SGTL5000_I2S_DLEN_MASK | SGTL5000_I2S_SCLKFREQ_MASK, + i2s_ctl); + + return 0; +} + +/* + * set dac bias + * common state changes: + * startup: + * off --> standby --> prepare --> on + * standby --> prepare --> on + * + * stop: + * on --> prepare --> standby + */ +static int sgtl5000_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct sgtl5000_priv *sgtl = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + case SND_SOC_BIAS_STANDBY: + regcache_cache_only(sgtl->regmap, false); + ret = regcache_sync(sgtl->regmap); + if (ret) { + regcache_cache_only(sgtl->regmap, true); + return ret; + } + + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, + SGTL5000_REFTOP_POWERUP, + SGTL5000_REFTOP_POWERUP); + break; + case SND_SOC_BIAS_OFF: + regcache_cache_only(sgtl->regmap, true); + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER, + SGTL5000_REFTOP_POWERUP, 0); + break; + } + + return 0; +} + +#define SGTL5000_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops sgtl5000_ops = { + .hw_params = sgtl5000_pcm_hw_params, + .mute_stream = sgtl5000_mute_stream, + .set_fmt = sgtl5000_set_dai_fmt, + .set_sysclk = sgtl5000_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver sgtl5000_dai = { + .name = "sgtl5000", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + /* + * only support 8~48K + 96K, + * TODO modify hw_param to support more + */ + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = SGTL5000_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = SGTL5000_FORMATS, + }, + .ops = &sgtl5000_ops, + .symmetric_rate = 1, +}; + +static bool sgtl5000_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SGTL5000_CHIP_ID: + case SGTL5000_CHIP_ADCDAC_CTRL: + case SGTL5000_CHIP_ANA_STATUS: + return true; + } + + return false; +} + +static bool sgtl5000_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SGTL5000_CHIP_ID: + case SGTL5000_CHIP_DIG_POWER: + case SGTL5000_CHIP_CLK_CTRL: + case SGTL5000_CHIP_I2S_CTRL: + case SGTL5000_CHIP_SSS_CTRL: + case SGTL5000_CHIP_ADCDAC_CTRL: + case SGTL5000_CHIP_DAC_VOL: + case SGTL5000_CHIP_PAD_STRENGTH: + case SGTL5000_CHIP_ANA_ADC_CTRL: + case SGTL5000_CHIP_ANA_HP_CTRL: + case SGTL5000_CHIP_ANA_CTRL: + case SGTL5000_CHIP_LINREG_CTRL: + case SGTL5000_CHIP_REF_CTRL: + case SGTL5000_CHIP_MIC_CTRL: + case SGTL5000_CHIP_LINE_OUT_CTRL: + case SGTL5000_CHIP_LINE_OUT_VOL: + case SGTL5000_CHIP_ANA_POWER: + case SGTL5000_CHIP_PLL_CTRL: + case SGTL5000_CHIP_CLK_TOP_CTRL: + case SGTL5000_CHIP_ANA_STATUS: + case SGTL5000_CHIP_SHORT_CTRL: + case SGTL5000_CHIP_ANA_TEST2: + case SGTL5000_DAP_CTRL: + case SGTL5000_DAP_PEQ: + case SGTL5000_DAP_BASS_ENHANCE: + case SGTL5000_DAP_BASS_ENHANCE_CTRL: + case SGTL5000_DAP_AUDIO_EQ: + case SGTL5000_DAP_SURROUND: + case SGTL5000_DAP_FLT_COEF_ACCESS: + case SGTL5000_DAP_COEF_WR_B0_MSB: + case SGTL5000_DAP_COEF_WR_B0_LSB: + case SGTL5000_DAP_EQ_BASS_BAND0: + case SGTL5000_DAP_EQ_BASS_BAND1: + case SGTL5000_DAP_EQ_BASS_BAND2: + case SGTL5000_DAP_EQ_BASS_BAND3: + case SGTL5000_DAP_EQ_BASS_BAND4: + case SGTL5000_DAP_MAIN_CHAN: + case SGTL5000_DAP_MIX_CHAN: + case SGTL5000_DAP_AVC_CTRL: + case SGTL5000_DAP_AVC_THRESHOLD: + case SGTL5000_DAP_AVC_ATTACK: + case SGTL5000_DAP_AVC_DECAY: + case SGTL5000_DAP_COEF_WR_B1_MSB: + case SGTL5000_DAP_COEF_WR_B1_LSB: + case SGTL5000_DAP_COEF_WR_B2_MSB: + case SGTL5000_DAP_COEF_WR_B2_LSB: + case SGTL5000_DAP_COEF_WR_A1_MSB: + case SGTL5000_DAP_COEF_WR_A1_LSB: + case SGTL5000_DAP_COEF_WR_A2_MSB: + case SGTL5000_DAP_COEF_WR_A2_LSB: + return true; + + default: + return false; + } +} + +/* + * This precalculated table contains all (vag_val * 100 / lo_calcntrl) results + * to select an appropriate lo_vol_* in SGTL5000_CHIP_LINE_OUT_VOL + * The calculatation was done for all possible register values which + * is the array index and the following formula: 10^((idx−15)/40) * 100 + */ +static const u8 vol_quot_table[] = { + 42, 45, 47, 50, 53, 56, 60, 63, + 67, 71, 75, 79, 84, 89, 94, 100, + 106, 112, 119, 126, 133, 141, 150, 158, + 168, 178, 188, 200, 211, 224, 237, 251 +}; + +/* + * sgtl5000 has 3 internal power supplies: + * 1. VAG, normally set to vdda/2 + * 2. charge pump, set to different value + * according to voltage of vdda and vddio + * 3. line out VAG, normally set to vddio/2 + * + * and should be set according to: + * 1. vddd provided by external or not + * 2. vdda and vddio voltage value. > 3.1v or not + */ +static int sgtl5000_set_power_regs(struct snd_soc_component *component) +{ + int vddd; + int vdda; + int vddio; + u16 ana_pwr; + u16 lreg_ctrl; + int vag; + int lo_vag; + int vol_quot; + int lo_vol; + size_t i; + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); + + vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer); + vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer); + vddd = (sgtl5000->num_supplies > VDDD) + ? regulator_get_voltage(sgtl5000->supplies[VDDD].consumer) + : LDO_VOLTAGE; + + vdda = vdda / 1000; + vddio = vddio / 1000; + vddd = vddd / 1000; + + if (vdda <= 0 || vddio <= 0 || vddd < 0) { + dev_err(component->dev, "regulator voltage not set correctly\n"); + + return -EINVAL; + } + + /* according to datasheet, maximum voltage of supplies */ + if (vdda > 3600 || vddio > 3600 || vddd > 1980) { + dev_err(component->dev, + "exceed max voltage vdda %dmV vddio %dmV vddd %dmV\n", + vdda, vddio, vddd); + + return -EINVAL; + } + + /* reset value */ + ana_pwr = snd_soc_component_read(component, SGTL5000_CHIP_ANA_POWER); + ana_pwr |= SGTL5000_DAC_STEREO | + SGTL5000_ADC_STEREO | + SGTL5000_REFTOP_POWERUP; + lreg_ctrl = snd_soc_component_read(component, SGTL5000_CHIP_LINREG_CTRL); + + if (vddio < 3100 && vdda < 3100) { + /* enable internal oscillator used for charge pump */ + snd_soc_component_update_bits(component, SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INT_OSC_EN, + SGTL5000_INT_OSC_EN); + /* Enable VDDC charge pump */ + ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP; + } else { + ana_pwr &= ~SGTL5000_VDDC_CHRGPMP_POWERUP; + /* + * if vddio == vdda the source of charge pump should be + * assigned manually to VDDIO + */ + if (regulator_is_equal(sgtl5000->supplies[VDDA].consumer, + sgtl5000->supplies[VDDIO].consumer)) { + lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD; + lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO << + SGTL5000_VDDC_MAN_ASSN_SHIFT; + } + } + + snd_soc_component_write(component, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl); + + snd_soc_component_write(component, SGTL5000_CHIP_ANA_POWER, ana_pwr); + + /* + * set ADC/DAC VAG to vdda / 2, + * should stay in range (0.8v, 1.575v) + */ + vag = vdda / 2; + if (vag <= SGTL5000_ANA_GND_BASE) + vag = 0; + else if (vag >= SGTL5000_ANA_GND_BASE + SGTL5000_ANA_GND_STP * + (SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT)) + vag = SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT; + else + vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP; + + snd_soc_component_update_bits(component, SGTL5000_CHIP_REF_CTRL, + SGTL5000_ANA_GND_MASK, vag << SGTL5000_ANA_GND_SHIFT); + + /* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */ + lo_vag = vddio / 2; + if (lo_vag <= SGTL5000_LINE_OUT_GND_BASE) + lo_vag = 0; + else if (lo_vag >= SGTL5000_LINE_OUT_GND_BASE + + SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX) + lo_vag = SGTL5000_LINE_OUT_GND_MAX; + else + lo_vag = (lo_vag - SGTL5000_LINE_OUT_GND_BASE) / + SGTL5000_LINE_OUT_GND_STP; + + snd_soc_component_update_bits(component, SGTL5000_CHIP_LINE_OUT_CTRL, + SGTL5000_LINE_OUT_CURRENT_MASK | + SGTL5000_LINE_OUT_GND_MASK, + lo_vag << SGTL5000_LINE_OUT_GND_SHIFT | + SGTL5000_LINE_OUT_CURRENT_360u << + SGTL5000_LINE_OUT_CURRENT_SHIFT); + + /* + * Set lineout output level in range (0..31) + * the same value is used for right and left channel + * + * Searching for a suitable index solving this formula: + * idx = 40 * log10(vag_val / lo_cagcntrl) + 15 + */ + vol_quot = lo_vag ? (vag * 100) / lo_vag : 0; + lo_vol = 0; + for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) { + if (vol_quot >= vol_quot_table[i]) + lo_vol = i; + else + break; + } + + snd_soc_component_update_bits(component, SGTL5000_CHIP_LINE_OUT_VOL, + SGTL5000_LINE_OUT_VOL_RIGHT_MASK | + SGTL5000_LINE_OUT_VOL_LEFT_MASK, + lo_vol << SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT | + lo_vol << SGTL5000_LINE_OUT_VOL_LEFT_SHIFT); + + return 0; +} + +static int sgtl5000_enable_regulators(struct i2c_client *client) +{ + int ret; + int i; + int external_vddd = 0; + struct regulator *vddd; + struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client); + + for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++) + sgtl5000->supplies[i].supply = supply_names[i]; + + vddd = regulator_get_optional(&client->dev, "VDDD"); + if (IS_ERR(vddd)) { + /* See if it's just not registered yet */ + if (PTR_ERR(vddd) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } else { + external_vddd = 1; + regulator_put(vddd); + } + + sgtl5000->num_supplies = ARRAY_SIZE(sgtl5000->supplies) + - 1 + external_vddd; + ret = regulator_bulk_get(&client->dev, sgtl5000->num_supplies, + sgtl5000->supplies); + if (ret) + return ret; + + ret = regulator_bulk_enable(sgtl5000->num_supplies, + sgtl5000->supplies); + if (!ret) + usleep_range(10, 20); + else + regulator_bulk_free(sgtl5000->num_supplies, + sgtl5000->supplies); + + return ret; +} + +static int sgtl5000_probe(struct snd_soc_component *component) +{ + int ret; + u16 reg; + struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component); + unsigned int zcd_mask = SGTL5000_HP_ZCD_EN | SGTL5000_ADC_ZCD_EN; + + /* power up sgtl5000 */ + ret = sgtl5000_set_power_regs(component); + if (ret) + goto err; + + /* enable small pop, introduce 400ms delay in turning off */ + snd_soc_component_update_bits(component, SGTL5000_CHIP_REF_CTRL, + SGTL5000_SMALL_POP, SGTL5000_SMALL_POP); + + /* disable short cut detector */ + snd_soc_component_write(component, SGTL5000_CHIP_SHORT_CTRL, 0); + + snd_soc_component_write(component, SGTL5000_CHIP_DIG_POWER, + SGTL5000_ADC_EN | SGTL5000_DAC_EN); + + /* enable dac volume ramp by default */ + snd_soc_component_write(component, SGTL5000_CHIP_ADCDAC_CTRL, + SGTL5000_DAC_VOL_RAMP_EN | + SGTL5000_DAC_MUTE_RIGHT | + SGTL5000_DAC_MUTE_LEFT); + + reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT | + (sgtl5000->sclk_strength) << SGTL5000_PAD_I2S_SCLK_SHIFT | + 0x1f); + snd_soc_component_write(component, SGTL5000_CHIP_PAD_STRENGTH, reg); + + snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL, + zcd_mask, zcd_mask); + + snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_MASK, + sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT); + + snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_VOLT_MASK, + sgtl5000->micbias_voltage << SGTL5000_BIAS_VOLT_SHIFT); + /* + * enable DAP Graphic EQ + * TODO: + * Add control for changing between PEQ/Tone Control/GEQ + */ + snd_soc_component_write(component, SGTL5000_DAP_AUDIO_EQ, SGTL5000_DAP_SEL_GEQ); + + /* Unmute DAC after start */ + snd_soc_component_update_bits(component, SGTL5000_CHIP_ADCDAC_CTRL, + SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT, 0); + + return 0; + +err: + return ret; +} + +static int sgtl5000_of_xlate_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + /* return dai id 0, whatever the endpoint index */ + return 0; +} + +static const struct snd_soc_component_driver sgtl5000_driver = { + .probe = sgtl5000_probe, + .set_bias_level = sgtl5000_set_bias_level, + .controls = sgtl5000_snd_controls, + .num_controls = ARRAY_SIZE(sgtl5000_snd_controls), + .dapm_widgets = sgtl5000_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets), + .dapm_routes = sgtl5000_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes), + .of_xlate_dai_id = sgtl5000_of_xlate_dai_id, + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config sgtl5000_regmap = { + .reg_bits = 16, + .val_bits = 16, + .reg_stride = 2, + + .max_register = SGTL5000_MAX_REG_OFFSET, + .volatile_reg = sgtl5000_volatile, + .readable_reg = sgtl5000_readable, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = sgtl5000_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(sgtl5000_reg_defaults), +}; + +/* + * Write all the default values from sgtl5000_reg_defaults[] array into the + * sgtl5000 registers, to make sure we always start with the sane registers + * values as stated in the datasheet. + * + * Since sgtl5000 does not have a reset line, nor a reset command in software, + * we follow this approach to guarantee we always start from the default values + * and avoid problems like, not being able to probe after an audio playback + * followed by a system reset or a 'reboot' command in Linux + */ +static void sgtl5000_fill_defaults(struct i2c_client *client) +{ + struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client); + int i, ret, val, index; + + for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) { + val = sgtl5000_reg_defaults[i].def; + index = sgtl5000_reg_defaults[i].reg; + ret = regmap_write(sgtl5000->regmap, index, val); + if (ret) + dev_err(&client->dev, + "%s: error %d setting reg 0x%02x to 0x%04x\n", + __func__, ret, index, val); + } +} + +static int sgtl5000_i2c_probe(struct i2c_client *client) +{ + struct sgtl5000_priv *sgtl5000; + int ret, reg, rev; + struct device_node *np = client->dev.of_node; + u32 value; + u16 ana_pwr; + + sgtl5000 = devm_kzalloc(&client->dev, sizeof(*sgtl5000), GFP_KERNEL); + if (!sgtl5000) + return -ENOMEM; + + i2c_set_clientdata(client, sgtl5000); + + ret = sgtl5000_enable_regulators(client); + if (ret) + return ret; + + sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap); + if (IS_ERR(sgtl5000->regmap)) { + ret = PTR_ERR(sgtl5000->regmap); + dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret); + goto disable_regs; + } + + sgtl5000->mclk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(sgtl5000->mclk)) { + ret = PTR_ERR(sgtl5000->mclk); + /* Defer the probe to see if the clk will be provided later */ + if (ret == -ENOENT) + ret = -EPROBE_DEFER; + + dev_err_probe(&client->dev, ret, "Failed to get mclock\n"); + + goto disable_regs; + } + + ret = clk_prepare_enable(sgtl5000->mclk); + if (ret) { + dev_err(&client->dev, "Error enabling clock %d\n", ret); + goto disable_regs; + } + + /* Need 8 clocks before I2C accesses */ + udelay(1); + + /* read chip information */ + ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); + if (ret) { + dev_err(&client->dev, "Error reading chip id %d\n", ret); + goto disable_clk; + } + + if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != + SGTL5000_PARTID_PART_ID) { + dev_err(&client->dev, + "Device with ID register %x is not a sgtl5000\n", reg); + ret = -ENODEV; + goto disable_clk; + } + + rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; + dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev); + sgtl5000->revision = rev; + + /* reconfigure the clocks in case we're using the PLL */ + ret = regmap_write(sgtl5000->regmap, + SGTL5000_CHIP_CLK_CTRL, + SGTL5000_CHIP_CLK_CTRL_DEFAULT); + if (ret) + dev_err(&client->dev, + "Error %d initializing CHIP_CLK_CTRL\n", ret); + + /* Mute everything to avoid pop from the following power-up */ + ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_CTRL, + SGTL5000_CHIP_ANA_CTRL_DEFAULT); + if (ret) { + dev_err(&client->dev, + "Error %d muting outputs via CHIP_ANA_CTRL\n", ret); + goto disable_clk; + } + + /* + * If VAG is powered-on (e.g. from previous boot), it would be disabled + * by the write to ANA_POWER in later steps of the probe code. This + * may create a loud pop even with all outputs muted. The proper way + * to circumvent this is disabling the bit first and waiting the proper + * cool-down time. + */ + ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, &value); + if (ret) { + dev_err(&client->dev, "Failed to read ANA_POWER: %d\n", ret); + goto disable_clk; + } + if (value & SGTL5000_VAG_POWERUP) { + ret = regmap_update_bits(sgtl5000->regmap, + SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, + 0); + if (ret) { + dev_err(&client->dev, "Error %d disabling VAG\n", ret); + goto disable_clk; + } + + msleep(SGTL5000_VAG_POWERDOWN_DELAY); + } + + /* Follow section 2.2.1.1 of AN3663 */ + ana_pwr = SGTL5000_ANA_POWER_DEFAULT; + if (sgtl5000->num_supplies <= VDDD) { + /* internal VDDD at 1.2V */ + ret = regmap_update_bits(sgtl5000->regmap, + SGTL5000_CHIP_LINREG_CTRL, + SGTL5000_LINREG_VDDD_MASK, + LINREG_VDDD); + if (ret) + dev_err(&client->dev, + "Error %d setting LINREG_VDDD\n", ret); + + ana_pwr |= SGTL5000_LINEREG_D_POWERUP; + dev_info(&client->dev, + "Using internal LDO instead of VDDD: check ER1 erratum\n"); + } else { + /* using external LDO for VDDD + * Clear startup powerup and simple powerup + * bits to save power + */ + ana_pwr &= ~(SGTL5000_STARTUP_POWERUP + | SGTL5000_LINREG_SIMPLE_POWERUP); + dev_dbg(&client->dev, "Using external VDDD\n"); + } + ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, ana_pwr); + if (ret) + dev_err(&client->dev, + "Error %d setting CHIP_ANA_POWER to %04x\n", + ret, ana_pwr); + + if (np) { + if (!of_property_read_u32(np, + "micbias-resistor-k-ohms", &value)) { + switch (value) { + case SGTL5000_MICBIAS_OFF: + sgtl5000->micbias_resistor = 0; + break; + case SGTL5000_MICBIAS_2K: + sgtl5000->micbias_resistor = 1; + break; + case SGTL5000_MICBIAS_4K: + sgtl5000->micbias_resistor = 2; + break; + case SGTL5000_MICBIAS_8K: + sgtl5000->micbias_resistor = 3; + break; + default: + sgtl5000->micbias_resistor = 2; + dev_err(&client->dev, + "Unsuitable MicBias resistor\n"); + } + } else { + /* default is 4Kohms */ + sgtl5000->micbias_resistor = 2; + } + if (!of_property_read_u32(np, + "micbias-voltage-m-volts", &value)) { + /* 1250mV => 0 */ + /* steps of 250mV */ + if ((value >= 1250) && (value <= 3000)) + sgtl5000->micbias_voltage = (value / 250) - 5; + else { + sgtl5000->micbias_voltage = 0; + dev_err(&client->dev, + "Unsuitable MicBias voltage\n"); + } + } else { + sgtl5000->micbias_voltage = 0; + } + } + + sgtl5000->lrclk_strength = I2S_LRCLK_STRENGTH_LOW; + if (!of_property_read_u32(np, "lrclk-strength", &value)) { + if (value > I2S_LRCLK_STRENGTH_HIGH) + value = I2S_LRCLK_STRENGTH_LOW; + sgtl5000->lrclk_strength = value; + } + + sgtl5000->sclk_strength = I2S_SCLK_STRENGTH_LOW; + if (!of_property_read_u32(np, "sclk-strength", &value)) { + if (value > I2S_SCLK_STRENGTH_HIGH) + value = I2S_SCLK_STRENGTH_LOW; + sgtl5000->sclk_strength = value; + } + + /* Ensure sgtl5000 will start with sane register values */ + sgtl5000_fill_defaults(client); + + ret = devm_snd_soc_register_component(&client->dev, + &sgtl5000_driver, &sgtl5000_dai, 1); + if (ret) + goto disable_clk; + + return 0; + +disable_clk: + clk_disable_unprepare(sgtl5000->mclk); + +disable_regs: + regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies); + regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies); + + return ret; +} + +static void sgtl5000_i2c_remove(struct i2c_client *client) +{ + struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client); + + regmap_write(sgtl5000->regmap, SGTL5000_CHIP_CLK_CTRL, SGTL5000_CHIP_CLK_CTRL_DEFAULT); + regmap_write(sgtl5000->regmap, SGTL5000_CHIP_DIG_POWER, SGTL5000_DIG_POWER_DEFAULT); + regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, SGTL5000_ANA_POWER_DEFAULT); + + clk_disable_unprepare(sgtl5000->mclk); + regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies); + regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies); +} + +static void sgtl5000_i2c_shutdown(struct i2c_client *client) +{ + sgtl5000_i2c_remove(client); +} + +static const struct i2c_device_id sgtl5000_id[] = { + {"sgtl5000", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, sgtl5000_id); + +static const struct of_device_id sgtl5000_dt_ids[] = { + { .compatible = "fsl,sgtl5000", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sgtl5000_dt_ids); + +static struct i2c_driver sgtl5000_i2c_driver = { + .driver = { + .name = "sgtl5000", + .of_match_table = sgtl5000_dt_ids, + }, + .probe = sgtl5000_i2c_probe, + .remove = sgtl5000_i2c_remove, + .shutdown = sgtl5000_i2c_shutdown, + .id_table = sgtl5000_id, +}; + +module_i2c_driver(sgtl5000_i2c_driver); + +MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver"); +MODULE_AUTHOR("Zeng Zhaoming "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h new file mode 100644 index 0000000000..3a808c7622 --- /dev/null +++ b/sound/soc/codecs/sgtl5000.h @@ -0,0 +1,408 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * sgtl5000.h - SGTL5000 audio codec interface + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + */ + +#ifndef _SGTL5000_H +#define _SGTL5000_H + +/* + * Registers addresses + */ +#define SGTL5000_CHIP_ID 0x0000 +#define SGTL5000_CHIP_DIG_POWER 0x0002 +#define SGTL5000_CHIP_CLK_CTRL 0x0004 +#define SGTL5000_CHIP_I2S_CTRL 0x0006 +#define SGTL5000_CHIP_SSS_CTRL 0x000a +#define SGTL5000_CHIP_ADCDAC_CTRL 0x000e +#define SGTL5000_CHIP_DAC_VOL 0x0010 +#define SGTL5000_CHIP_PAD_STRENGTH 0x0014 +#define SGTL5000_CHIP_ANA_ADC_CTRL 0x0020 +#define SGTL5000_CHIP_ANA_HP_CTRL 0x0022 +#define SGTL5000_CHIP_ANA_CTRL 0x0024 +#define SGTL5000_CHIP_LINREG_CTRL 0x0026 +#define SGTL5000_CHIP_REF_CTRL 0x0028 +#define SGTL5000_CHIP_MIC_CTRL 0x002a +#define SGTL5000_CHIP_LINE_OUT_CTRL 0x002c +#define SGTL5000_CHIP_LINE_OUT_VOL 0x002e +#define SGTL5000_CHIP_ANA_POWER 0x0030 +#define SGTL5000_CHIP_PLL_CTRL 0x0032 +#define SGTL5000_CHIP_CLK_TOP_CTRL 0x0034 +#define SGTL5000_CHIP_ANA_STATUS 0x0036 +#define SGTL5000_CHIP_SHORT_CTRL 0x003c +#define SGTL5000_CHIP_ANA_TEST2 0x003a +#define SGTL5000_DAP_CTRL 0x0100 +#define SGTL5000_DAP_PEQ 0x0102 +#define SGTL5000_DAP_BASS_ENHANCE 0x0104 +#define SGTL5000_DAP_BASS_ENHANCE_CTRL 0x0106 +#define SGTL5000_DAP_AUDIO_EQ 0x0108 +#define SGTL5000_DAP_SURROUND 0x010a +#define SGTL5000_DAP_FLT_COEF_ACCESS 0x010c +#define SGTL5000_DAP_COEF_WR_B0_MSB 0x010e +#define SGTL5000_DAP_COEF_WR_B0_LSB 0x0110 +#define SGTL5000_DAP_EQ_BASS_BAND0 0x0116 +#define SGTL5000_DAP_EQ_BASS_BAND1 0x0118 +#define SGTL5000_DAP_EQ_BASS_BAND2 0x011a +#define SGTL5000_DAP_EQ_BASS_BAND3 0x011c +#define SGTL5000_DAP_EQ_BASS_BAND4 0x011e +#define SGTL5000_DAP_MAIN_CHAN 0x0120 +#define SGTL5000_DAP_MIX_CHAN 0x0122 +#define SGTL5000_DAP_AVC_CTRL 0x0124 +#define SGTL5000_DAP_AVC_THRESHOLD 0x0126 +#define SGTL5000_DAP_AVC_ATTACK 0x0128 +#define SGTL5000_DAP_AVC_DECAY 0x012a +#define SGTL5000_DAP_COEF_WR_B1_MSB 0x012c +#define SGTL5000_DAP_COEF_WR_B1_LSB 0x012e +#define SGTL5000_DAP_COEF_WR_B2_MSB 0x0130 +#define SGTL5000_DAP_COEF_WR_B2_LSB 0x0132 +#define SGTL5000_DAP_COEF_WR_A1_MSB 0x0134 +#define SGTL5000_DAP_COEF_WR_A1_LSB 0x0136 +#define SGTL5000_DAP_COEF_WR_A2_MSB 0x0138 +#define SGTL5000_DAP_COEF_WR_A2_LSB 0x013a + +/* + * Field Definitions. + */ + +/* + * SGTL5000_CHIP_ID + */ +#define SGTL5000_PARTID_MASK 0xff00 +#define SGTL5000_PARTID_SHIFT 8 +#define SGTL5000_PARTID_WIDTH 8 +#define SGTL5000_PARTID_PART_ID 0xa0 +#define SGTL5000_REVID_MASK 0x00ff +#define SGTL5000_REVID_SHIFT 0 +#define SGTL5000_REVID_WIDTH 8 + +/* + * SGTL5000_CHIP_DIG_POWER + */ +#define SGTL5000_DIG_POWER_DEFAULT 0x0000 +#define SGTL5000_ADC_EN 0x0040 +#define SGTL5000_DAC_EN 0x0020 +#define SGTL5000_DAP_POWERUP 0x0010 +#define SGTL5000_I2S_OUT_POWERUP 0x0002 +#define SGTL5000_I2S_IN_POWERUP 0x0001 + +/* + * SGTL5000_CHIP_CLK_CTRL + */ +#define SGTL5000_CHIP_CLK_CTRL_DEFAULT 0x0008 +#define SGTL5000_RATE_MODE_MASK 0x0030 +#define SGTL5000_RATE_MODE_SHIFT 4 +#define SGTL5000_RATE_MODE_WIDTH 2 +#define SGTL5000_RATE_MODE_DIV_1 0 +#define SGTL5000_RATE_MODE_DIV_2 1 +#define SGTL5000_RATE_MODE_DIV_4 2 +#define SGTL5000_RATE_MODE_DIV_6 3 +#define SGTL5000_SYS_FS_MASK 0x000c +#define SGTL5000_SYS_FS_SHIFT 2 +#define SGTL5000_SYS_FS_WIDTH 2 +#define SGTL5000_SYS_FS_32k 0x0 +#define SGTL5000_SYS_FS_44_1k 0x1 +#define SGTL5000_SYS_FS_48k 0x2 +#define SGTL5000_SYS_FS_96k 0x3 +#define SGTL5000_MCLK_FREQ_MASK 0x0003 +#define SGTL5000_MCLK_FREQ_SHIFT 0 +#define SGTL5000_MCLK_FREQ_WIDTH 2 +#define SGTL5000_MCLK_FREQ_256FS 0x0 +#define SGTL5000_MCLK_FREQ_384FS 0x1 +#define SGTL5000_MCLK_FREQ_512FS 0x2 +#define SGTL5000_MCLK_FREQ_PLL 0x3 + +/* + * SGTL5000_CHIP_I2S_CTRL + */ +#define SGTL5000_I2S_SCLKFREQ_MASK 0x0100 +#define SGTL5000_I2S_SCLKFREQ_SHIFT 8 +#define SGTL5000_I2S_SCLKFREQ_WIDTH 1 +#define SGTL5000_I2S_SCLKFREQ_64FS 0x0 +#define SGTL5000_I2S_SCLKFREQ_32FS 0x1 /* Not for RJ mode */ +#define SGTL5000_I2S_MASTER 0x0080 +#define SGTL5000_I2S_SCLK_INV 0x0040 +#define SGTL5000_I2S_DLEN_MASK 0x0030 +#define SGTL5000_I2S_DLEN_SHIFT 4 +#define SGTL5000_I2S_DLEN_WIDTH 2 +#define SGTL5000_I2S_DLEN_32 0x0 +#define SGTL5000_I2S_DLEN_24 0x1 +#define SGTL5000_I2S_DLEN_20 0x2 +#define SGTL5000_I2S_DLEN_16 0x3 +#define SGTL5000_I2S_MODE_MASK 0x000c +#define SGTL5000_I2S_MODE_SHIFT 2 +#define SGTL5000_I2S_MODE_WIDTH 2 +#define SGTL5000_I2S_MODE_I2S_LJ 0x0 +#define SGTL5000_I2S_MODE_RJ 0x1 +#define SGTL5000_I2S_MODE_PCM 0x2 +#define SGTL5000_I2S_LRALIGN 0x0002 +#define SGTL5000_I2S_LRPOL 0x0001 /* set for which mode */ + +/* + * SGTL5000_CHIP_SSS_CTRL + */ +#define SGTL5000_DAP_MIX_LRSWAP 0x4000 +#define SGTL5000_DAP_LRSWAP 0x2000 +#define SGTL5000_DAC_LRSWAP 0x1000 +#define SGTL5000_I2S_OUT_LRSWAP 0x0400 +#define SGTL5000_DAP_MIX_SEL_MASK 0x0300 +#define SGTL5000_DAP_MIX_SEL_SHIFT 8 +#define SGTL5000_DAP_MIX_SEL_WIDTH 2 +#define SGTL5000_DAP_MIX_SEL_ADC 0x0 +#define SGTL5000_DAP_MIX_SEL_I2S_IN 0x1 +#define SGTL5000_DAP_SEL_MASK 0x00c0 +#define SGTL5000_DAP_SEL_SHIFT 6 +#define SGTL5000_DAP_SEL_WIDTH 2 +#define SGTL5000_DAP_SEL_ADC 0x0 +#define SGTL5000_DAP_SEL_I2S_IN 0x1 +#define SGTL5000_DAC_SEL_MASK 0x0030 +#define SGTL5000_DAC_SEL_SHIFT 4 +#define SGTL5000_DAC_SEL_WIDTH 2 +#define SGTL5000_DAC_SEL_ADC 0x0 +#define SGTL5000_DAC_SEL_I2S_IN 0x1 +#define SGTL5000_DAC_SEL_DAP 0x3 +#define SGTL5000_I2S_OUT_SEL_MASK 0x0003 +#define SGTL5000_I2S_OUT_SEL_SHIFT 0 +#define SGTL5000_I2S_OUT_SEL_WIDTH 2 +#define SGTL5000_I2S_OUT_SEL_ADC 0x0 +#define SGTL5000_I2S_OUT_SEL_I2S_IN 0x1 +#define SGTL5000_I2S_OUT_SEL_DAP 0x3 + +/* + * SGTL5000_CHIP_ADCDAC_CTRL + */ +#define SGTL5000_VOL_BUSY_DAC_RIGHT 0x2000 +#define SGTL5000_VOL_BUSY_DAC_LEFT 0x1000 +#define SGTL5000_DAC_VOL_RAMP_EN 0x0200 +#define SGTL5000_DAC_VOL_RAMP_EXPO 0x0100 +#define SGTL5000_DAC_MUTE_RIGHT 0x0008 +#define SGTL5000_DAC_MUTE_LEFT 0x0004 +#define SGTL5000_ADC_HPF_FREEZE 0x0002 +#define SGTL5000_ADC_HPF_BYPASS 0x0001 + +/* + * SGTL5000_CHIP_DAC_VOL + */ +#define SGTL5000_DAC_VOL_RIGHT_MASK 0xff00 +#define SGTL5000_DAC_VOL_RIGHT_SHIFT 8 +#define SGTL5000_DAC_VOL_RIGHT_WIDTH 8 +#define SGTL5000_DAC_VOL_LEFT_MASK 0x00ff +#define SGTL5000_DAC_VOL_LEFT_SHIFT 0 +#define SGTL5000_DAC_VOL_LEFT_WIDTH 8 + +/* + * SGTL5000_CHIP_PAD_STRENGTH + */ +#define SGTL5000_PAD_I2S_LRCLK_MASK 0x0300 +#define SGTL5000_PAD_I2S_LRCLK_SHIFT 8 +#define SGTL5000_PAD_I2S_LRCLK_WIDTH 2 +#define SGTL5000_PAD_I2S_SCLK_MASK 0x00c0 +#define SGTL5000_PAD_I2S_SCLK_SHIFT 6 +#define SGTL5000_PAD_I2S_SCLK_WIDTH 2 +#define SGTL5000_PAD_I2S_DOUT_MASK 0x0030 +#define SGTL5000_PAD_I2S_DOUT_SHIFT 4 +#define SGTL5000_PAD_I2S_DOUT_WIDTH 2 +#define SGTL5000_PAD_I2C_SDA_MASK 0x000c +#define SGTL5000_PAD_I2C_SDA_SHIFT 2 +#define SGTL5000_PAD_I2C_SDA_WIDTH 2 +#define SGTL5000_PAD_I2C_SCL_MASK 0x0003 +#define SGTL5000_PAD_I2C_SCL_SHIFT 0 +#define SGTL5000_PAD_I2C_SCL_WIDTH 2 + +/* + * SGTL5000_CHIP_ANA_ADC_CTRL + */ +#define SGTL5000_ADC_VOL_M6DB 0x0100 +#define SGTL5000_ADC_VOL_RIGHT_MASK 0x00f0 +#define SGTL5000_ADC_VOL_RIGHT_SHIFT 4 +#define SGTL5000_ADC_VOL_RIGHT_WIDTH 4 +#define SGTL5000_ADC_VOL_LEFT_MASK 0x000f +#define SGTL5000_ADC_VOL_LEFT_SHIFT 0 +#define SGTL5000_ADC_VOL_LEFT_WIDTH 4 + +/* + * SGTL5000_CHIP_ANA_HP_CTRL + */ +#define SGTL5000_HP_VOL_RIGHT_MASK 0x7f00 +#define SGTL5000_HP_VOL_RIGHT_SHIFT 8 +#define SGTL5000_HP_VOL_RIGHT_WIDTH 7 +#define SGTL5000_HP_VOL_LEFT_MASK 0x007f +#define SGTL5000_HP_VOL_LEFT_SHIFT 0 +#define SGTL5000_HP_VOL_LEFT_WIDTH 7 + +/* + * SGTL5000_CHIP_ANA_CTRL + */ +#define SGTL5000_CHIP_ANA_CTRL_DEFAULT 0x0133 +#define SGTL5000_LINE_OUT_MUTE 0x0100 +#define SGTL5000_HP_SEL_MASK 0x0040 +#define SGTL5000_HP_SEL_SHIFT 6 +#define SGTL5000_HP_SEL_WIDTH 1 +#define SGTL5000_HP_SEL_DAC 0x0 +#define SGTL5000_HP_SEL_LINE_IN 0x1 +#define SGTL5000_HP_ZCD_EN 0x0020 +#define SGTL5000_HP_MUTE 0x0010 +#define SGTL5000_ADC_SEL_MASK 0x0004 +#define SGTL5000_ADC_SEL_SHIFT 2 +#define SGTL5000_ADC_SEL_WIDTH 1 +#define SGTL5000_ADC_SEL_MIC 0x0 +#define SGTL5000_ADC_SEL_LINE_IN 0x1 +#define SGTL5000_ADC_ZCD_EN 0x0002 +#define SGTL5000_ADC_MUTE 0x0001 + +/* + * SGTL5000_CHIP_LINREG_CTRL + */ +#define SGTL5000_VDDC_MAN_ASSN_MASK 0x0040 +#define SGTL5000_VDDC_MAN_ASSN_SHIFT 6 +#define SGTL5000_VDDC_MAN_ASSN_WIDTH 1 +#define SGTL5000_VDDC_MAN_ASSN_VDDA 0x0 +#define SGTL5000_VDDC_MAN_ASSN_VDDIO 0x1 +#define SGTL5000_VDDC_ASSN_OVRD 0x0020 +#define SGTL5000_LINREG_VDDD_MASK 0x000f +#define SGTL5000_LINREG_VDDD_SHIFT 0 +#define SGTL5000_LINREG_VDDD_WIDTH 4 + +/* + * SGTL5000_CHIP_REF_CTRL + */ +#define SGTL5000_ANA_GND_MASK 0x01f0 +#define SGTL5000_ANA_GND_SHIFT 4 +#define SGTL5000_ANA_GND_WIDTH 5 +#define SGTL5000_ANA_GND_BASE 800 /* mv */ +#define SGTL5000_ANA_GND_STP 25 /*mv */ +#define SGTL5000_BIAS_CTRL_MASK 0x000e +#define SGTL5000_BIAS_CTRL_SHIFT 1 +#define SGTL5000_BIAS_CTRL_WIDTH 3 +#define SGTL5000_SMALL_POP 0x0001 + +/* + * SGTL5000_CHIP_MIC_CTRL + */ +#define SGTL5000_BIAS_R_MASK 0x0300 +#define SGTL5000_BIAS_R_SHIFT 8 +#define SGTL5000_BIAS_R_WIDTH 2 +#define SGTL5000_BIAS_R_off 0x0 +#define SGTL5000_BIAS_R_2K 0x1 +#define SGTL5000_BIAS_R_4k 0x2 +#define SGTL5000_BIAS_R_8k 0x3 +#define SGTL5000_BIAS_VOLT_MASK 0x0070 +#define SGTL5000_BIAS_VOLT_SHIFT 4 +#define SGTL5000_BIAS_VOLT_WIDTH 3 +#define SGTL5000_MIC_GAIN_MASK 0x0003 +#define SGTL5000_MIC_GAIN_SHIFT 0 +#define SGTL5000_MIC_GAIN_WIDTH 2 + +/* + * SGTL5000_CHIP_LINE_OUT_CTRL + */ +#define SGTL5000_LINE_OUT_CURRENT_MASK 0x0f00 +#define SGTL5000_LINE_OUT_CURRENT_SHIFT 8 +#define SGTL5000_LINE_OUT_CURRENT_WIDTH 4 +#define SGTL5000_LINE_OUT_CURRENT_180u 0x0 +#define SGTL5000_LINE_OUT_CURRENT_270u 0x1 +#define SGTL5000_LINE_OUT_CURRENT_360u 0x3 +#define SGTL5000_LINE_OUT_CURRENT_450u 0x7 +#define SGTL5000_LINE_OUT_CURRENT_540u 0xf +#define SGTL5000_LINE_OUT_GND_MASK 0x003f +#define SGTL5000_LINE_OUT_GND_SHIFT 0 +#define SGTL5000_LINE_OUT_GND_WIDTH 6 +#define SGTL5000_LINE_OUT_GND_BASE 800 /* mv */ +#define SGTL5000_LINE_OUT_GND_STP 25 +#define SGTL5000_LINE_OUT_GND_MAX 0x23 + +/* + * SGTL5000_CHIP_LINE_OUT_VOL + */ +#define SGTL5000_LINE_OUT_VOL_RIGHT_MASK 0x1f00 +#define SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT 8 +#define SGTL5000_LINE_OUT_VOL_RIGHT_WIDTH 5 +#define SGTL5000_LINE_OUT_VOL_LEFT_MASK 0x001f +#define SGTL5000_LINE_OUT_VOL_LEFT_SHIFT 0 +#define SGTL5000_LINE_OUT_VOL_LEFT_WIDTH 5 + +/* + * SGTL5000_CHIP_ANA_POWER + */ +#define SGTL5000_ANA_POWER_DEFAULT 0x7060 +#define SGTL5000_DAC_STEREO 0x4000 +#define SGTL5000_LINREG_SIMPLE_POWERUP 0x2000 +#define SGTL5000_STARTUP_POWERUP 0x1000 +#define SGTL5000_VDDC_CHRGPMP_POWERUP 0x0800 +#define SGTL5000_PLL_POWERUP 0x0400 +#define SGTL5000_LINEREG_D_POWERUP 0x0200 +#define SGTL5000_VCOAMP_POWERUP 0x0100 +#define SGTL5000_VAG_POWERUP 0x0080 +#define SGTL5000_ADC_STEREO 0x0040 +#define SGTL5000_REFTOP_POWERUP 0x0020 +#define SGTL5000_HP_POWERUP 0x0010 +#define SGTL5000_DAC_POWERUP 0x0008 +#define SGTL5000_CAPLESS_HP_POWERUP 0x0004 +#define SGTL5000_ADC_POWERUP 0x0002 +#define SGTL5000_LINE_OUT_POWERUP 0x0001 + +/* + * SGTL5000_CHIP_PLL_CTRL + */ +#define SGTL5000_PLL_INT_DIV_MASK 0xf800 +#define SGTL5000_PLL_INT_DIV_SHIFT 11 +#define SGTL5000_PLL_INT_DIV_WIDTH 5 +#define SGTL5000_PLL_FRAC_DIV_MASK 0x07ff +#define SGTL5000_PLL_FRAC_DIV_SHIFT 0 +#define SGTL5000_PLL_FRAC_DIV_WIDTH 11 + +/* + * SGTL5000_CHIP_CLK_TOP_CTRL + */ +#define SGTL5000_INT_OSC_EN 0x0800 +#define SGTL5000_INPUT_FREQ_DIV2 0x0008 + +/* + * SGTL5000_CHIP_ANA_STATUS + */ +#define SGTL5000_HP_LRSHORT 0x0200 +#define SGTL5000_CAPLESS_SHORT 0x0100 +#define SGTL5000_PLL_LOCKED 0x0010 + +/* + * SGTL5000_CHIP_SHORT_CTRL + */ +#define SGTL5000_LVLADJR_MASK 0x7000 +#define SGTL5000_LVLADJR_SHIFT 12 +#define SGTL5000_LVLADJR_WIDTH 3 +#define SGTL5000_LVLADJL_MASK 0x0700 +#define SGTL5000_LVLADJL_SHIFT 8 +#define SGTL5000_LVLADJL_WIDTH 3 +#define SGTL5000_LVLADJC_MASK 0x0070 +#define SGTL5000_LVLADJC_SHIFT 4 +#define SGTL5000_LVLADJC_WIDTH 3 +#define SGTL5000_LR_SHORT_MOD_MASK 0x000c +#define SGTL5000_LR_SHORT_MOD_SHIFT 2 +#define SGTL5000_LR_SHORT_MOD_WIDTH 2 +#define SGTL5000_CM_SHORT_MOD_MASK 0x0003 +#define SGTL5000_CM_SHORT_MOD_SHIFT 0 +#define SGTL5000_CM_SHORT_MOD_WIDTH 2 + +/* + *SGTL5000_CHIP_ANA_TEST2 + */ +#define SGTL5000_MONO_DAC 0x1000 + +/* + * SGTL5000_DAP_CTRL + */ +#define SGTL5000_DAP_MIX_EN 0x0010 +#define SGTL5000_DAP_EN 0x0001 + +#define SGTL5000_SYSCLK 0x00 +#define SGTL5000_LRCLK 0x01 + +/* + * SGTL5000_DAP_AUDIO_EQ + */ +#define SGTL5000_DAP_SEL_PEQ 1 +#define SGTL5000_DAP_SEL_TONE_CTRL 2 +#define SGTL5000_DAP_SEL_GEQ 3 + +#endif diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c new file mode 100644 index 0000000000..d87141ba84 --- /dev/null +++ b/sound/soc/codecs/si476x.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * sound/soc/codecs/si476x.c -- Codec driver for SI476X chips + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +enum si476x_audio_registers { + SI476X_DIGITAL_IO_OUTPUT_FORMAT = 0x0203, + SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE = 0x0202, +}; + +enum si476x_digital_io_output_format { + SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT = 11, + SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT = 8, +}; + +#define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0x7 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \ + (0x7 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT)) +#define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0x7e) + +enum si476x_daudio_formats { + SI476X_DAUDIO_MODE_I2S = (0x0 << 1), + SI476X_DAUDIO_MODE_DSP_A = (0x6 << 1), + SI476X_DAUDIO_MODE_DSP_B = (0x7 << 1), + SI476X_DAUDIO_MODE_LEFT_J = (0x8 << 1), + SI476X_DAUDIO_MODE_RIGHT_J = (0x9 << 1), + + SI476X_DAUDIO_MODE_IB = (1 << 5), + SI476X_DAUDIO_MODE_IF = (1 << 6), +}; + +enum si476x_pcm_format { + SI476X_PCM_FORMAT_S8 = 2, + SI476X_PCM_FORMAT_S16_LE = 4, + SI476X_PCM_FORMAT_S20_3LE = 5, + SI476X_PCM_FORMAT_S24_LE = 6, +}; + +static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("ROUT"), +}; + +static const struct snd_soc_dapm_route si476x_dapm_routes[] = { + { "Capture", NULL, "LOUT" }, + { "Capture", NULL, "ROUT" }, +}; + +static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct si476x_core *core = i2c_mfd_cell_to_core(codec_dai->dev); + int err; + u16 format = 0; + + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) + return -EINVAL; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + format |= SI476X_DAUDIO_MODE_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + format |= SI476X_DAUDIO_MODE_DSP_B; + break; + case SND_SOC_DAIFMT_I2S: + format |= SI476X_DAUDIO_MODE_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + format |= SI476X_DAUDIO_MODE_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + format |= SI476X_DAUDIO_MODE_LEFT_J; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + format |= SI476X_DAUDIO_MODE_IB; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + format |= SI476X_DAUDIO_MODE_IB | + SI476X_DAUDIO_MODE_IF; + break; + case SND_SOC_DAIFMT_IB_NF: + format |= SI476X_DAUDIO_MODE_IB; + break; + case SND_SOC_DAIFMT_NB_IF: + format |= SI476X_DAUDIO_MODE_IF; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + si476x_core_lock(core); + + err = snd_soc_component_update_bits(codec_dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT, + SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK, + format); + + si476x_core_unlock(core); + + if (err < 0) { + dev_err(codec_dai->component->dev, "Failed to set output format\n"); + return err; + } + + return 0; +} + +static int si476x_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct si476x_core *core = i2c_mfd_cell_to_core(dai->dev); + int rate, width, err; + + rate = params_rate(params); + if (rate < 32000 || rate > 48000) { + dev_err(dai->component->dev, "Rate: %d is not supported\n", rate); + return -EINVAL; + } + + switch (params_width(params)) { + case 8: + width = SI476X_PCM_FORMAT_S8; + break; + case 16: + width = SI476X_PCM_FORMAT_S16_LE; + break; + case 20: + width = SI476X_PCM_FORMAT_S20_3LE; + break; + case 24: + width = SI476X_PCM_FORMAT_S24_LE; + break; + default: + return -EINVAL; + } + + si476x_core_lock(core); + + err = snd_soc_component_write(dai->component, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE, + rate); + if (err < 0) { + dev_err(dai->component->dev, "Failed to set sample rate\n"); + goto out; + } + + err = snd_soc_component_update_bits(dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT, + SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK, + (width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | + (width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT)); + if (err < 0) { + dev_err(dai->component->dev, "Failed to set output width\n"); + goto out; + } + +out: + si476x_core_unlock(core); + + return err; +} + +static const struct snd_soc_dai_ops si476x_dai_ops = { + .hw_params = si476x_codec_hw_params, + .set_fmt = si476x_codec_set_dai_fmt, +}; + +static struct snd_soc_dai_driver si476x_dai = { + .name = "si476x-codec", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE + }, + .ops = &si476x_dai_ops, +}; + +static int si476x_probe(struct snd_soc_component *component) +{ + snd_soc_component_init_regmap(component, + dev_get_regmap(component->dev->parent, NULL)); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_si476x = { + .probe = si476x_probe, + .dapm_widgets = si476x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets), + .dapm_routes = si476x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int si476x_platform_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_si476x, + &si476x_dai, 1); +} + +MODULE_ALIAS("platform:si476x-codec"); + +static struct platform_driver si476x_platform_driver = { + .driver = { + .name = "si476x-codec", + }, + .probe = si476x_platform_probe, +}; +module_platform_driver(si476x_platform_driver); + +MODULE_AUTHOR("Andrey Smirnov "); +MODULE_DESCRIPTION("ASoC Si4761/64 codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c new file mode 100644 index 0000000000..cb4c491078 --- /dev/null +++ b/sound/soc/codecs/sigmadsp-i2c.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Load Analog Devices SigmaStudio firmware files + * + * Copyright 2009-2011 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include + +#include "sigmadsp.h" + +static int sigmadsp_write_i2c(void *control_data, + unsigned int addr, const uint8_t data[], size_t len) +{ + uint8_t *buf; + int ret; + + buf = kzalloc(2 + len, GFP_KERNEL | GFP_DMA); + if (!buf) + return -ENOMEM; + + put_unaligned_be16(addr, buf); + memcpy(buf + 2, data, len); + + ret = i2c_master_send(control_data, buf, len + 2); + + kfree(buf); + + if (ret < 0) + return ret; + + return 0; +} + +static int sigmadsp_read_i2c(void *control_data, + unsigned int addr, uint8_t data[], size_t len) +{ + struct i2c_client *client = control_data; + struct i2c_msg msgs[2]; + uint8_t buf[2]; + int ret; + + put_unaligned_be16(addr, buf); + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(buf); + msgs[0].buf = buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = len; + msgs[1].buf = data; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + else if (ret != ARRAY_SIZE(msgs)) + return -EIO; + return 0; +} + +/** + * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance + * @client: The parent I2C device + * @ops: The sigmadsp_ops to use for this instance + * @firmware_name: Name of the firmware file to load + * + * Allocates a SigmaDSP instance and loads the specified firmware file. + * + * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error. + */ +struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client, + const struct sigmadsp_ops *ops, const char *firmware_name) +{ + struct sigmadsp *sigmadsp; + + sigmadsp = devm_sigmadsp_init(&client->dev, ops, firmware_name); + if (IS_ERR(sigmadsp)) + return sigmadsp; + + sigmadsp->control_data = client; + sigmadsp->write = sigmadsp_write_i2c; + sigmadsp->read = sigmadsp_read_i2c; + + return sigmadsp; +} +EXPORT_SYMBOL_GPL(devm_sigmadsp_init_i2c); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("SigmaDSP I2C firmware loader"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sigmadsp-regmap.c b/sound/soc/codecs/sigmadsp-regmap.c new file mode 100644 index 0000000000..ba9a6795e4 --- /dev/null +++ b/sound/soc/codecs/sigmadsp-regmap.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Load Analog Devices SigmaStudio firmware files + * + * Copyright 2009-2011 Analog Devices Inc. + */ + +#include +#include +#include + +#include "sigmadsp.h" + +static int sigmadsp_write_regmap(void *control_data, + unsigned int addr, const uint8_t data[], size_t len) +{ + return regmap_raw_write(control_data, addr, + data, len); +} + +static int sigmadsp_read_regmap(void *control_data, + unsigned int addr, uint8_t data[], size_t len) +{ + return regmap_raw_read(control_data, addr, + data, len); +} + +/** + * devm_sigmadsp_init_regmap() - Initialize SigmaDSP instance + * @dev: The parent device + * @regmap: Regmap instance to use + * @ops: The sigmadsp_ops to use for this instance + * @firmware_name: Name of the firmware file to load + * + * Allocates a SigmaDSP instance and loads the specified firmware file. + * + * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error. + */ +struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev, + struct regmap *regmap, const struct sigmadsp_ops *ops, + const char *firmware_name) +{ + struct sigmadsp *sigmadsp; + + sigmadsp = devm_sigmadsp_init(dev, ops, firmware_name); + if (IS_ERR(sigmadsp)) + return sigmadsp; + + sigmadsp->control_data = regmap; + sigmadsp->write = sigmadsp_write_regmap; + sigmadsp->read = sigmadsp_read_regmap; + + return sigmadsp; +} +EXPORT_SYMBOL_GPL(devm_sigmadsp_init_regmap); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("SigmaDSP regmap firmware loader"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c new file mode 100644 index 0000000000..b93c078a80 --- /dev/null +++ b/sound/soc/codecs/sigmadsp.c @@ -0,0 +1,807 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Load Analog Devices SigmaStudio firmware files + * + * Copyright 2009-2014 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sigmadsp.h" + +#define SIGMA_MAGIC "ADISIGM" + +#define SIGMA_FW_CHUNK_TYPE_DATA 0 +#define SIGMA_FW_CHUNK_TYPE_CONTROL 1 +#define SIGMA_FW_CHUNK_TYPE_SAMPLERATES 2 + +#define READBACK_CTRL_NAME "ReadBack" + +struct sigmadsp_control { + struct list_head head; + uint32_t samplerates; + unsigned int addr; + unsigned int num_bytes; + const char *name; + struct snd_kcontrol *kcontrol; + bool is_readback; + bool cached; + uint8_t cache[]; +}; + +struct sigmadsp_data { + struct list_head head; + uint32_t samplerates; + unsigned int addr; + unsigned int length; + uint8_t data[]; +}; + +struct sigma_fw_chunk { + __le32 length; + __le32 tag; + __le32 samplerates; +} __packed; + +struct sigma_fw_chunk_data { + struct sigma_fw_chunk chunk; + __le16 addr; + uint8_t data[]; +} __packed; + +struct sigma_fw_chunk_control { + struct sigma_fw_chunk chunk; + __le16 type; + __le16 addr; + __le16 num_bytes; + const char name[]; +} __packed; + +struct sigma_fw_chunk_samplerate { + struct sigma_fw_chunk chunk; + __le32 samplerates[]; +} __packed; + +struct sigma_firmware_header { + unsigned char magic[7]; + u8 version; + __le32 crc; +} __packed; + +enum { + SIGMA_ACTION_WRITEXBYTES = 0, + SIGMA_ACTION_WRITESINGLE, + SIGMA_ACTION_WRITESAFELOAD, + SIGMA_ACTION_END, +}; + +struct sigma_action { + u8 instr; + u8 len_hi; + __le16 len; + __be16 addr; + unsigned char payload[]; +} __packed; + +static int sigmadsp_write(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t data[], size_t len) +{ + return sigmadsp->write(sigmadsp->control_data, addr, data, len); +} + +static int sigmadsp_read(struct sigmadsp *sigmadsp, unsigned int addr, + uint8_t data[], size_t len) +{ + return sigmadsp->read(sigmadsp->control_data, addr, data, len); +} + +static int sigmadsp_ctrl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + + info->type = SNDRV_CTL_ELEM_TYPE_BYTES; + info->count = ctrl->num_bytes; + + return 0; +} + +static int sigmadsp_ctrl_write(struct sigmadsp *sigmadsp, + struct sigmadsp_control *ctrl, void *data) +{ + /* safeload loads up to 20 bytes in a atomic operation */ + if (ctrl->num_bytes <= 20 && sigmadsp->ops && sigmadsp->ops->safeload) + return sigmadsp->ops->safeload(sigmadsp, ctrl->addr, data, + ctrl->num_bytes); + else + return sigmadsp_write(sigmadsp, ctrl->addr, data, + ctrl->num_bytes); +} + +static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); + uint8_t *data; + int ret = 0; + + mutex_lock(&sigmadsp->lock); + + data = ucontrol->value.bytes.data; + + if (!(kcontrol->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) + ret = sigmadsp_ctrl_write(sigmadsp, ctrl, data); + + if (ret == 0) { + memcpy(ctrl->cache, data, ctrl->num_bytes); + if (!ctrl->is_readback) + ctrl->cached = true; + } + + mutex_unlock(&sigmadsp->lock); + + return ret; +} + +static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol); + int ret = 0; + + mutex_lock(&sigmadsp->lock); + + if (!ctrl->cached) { + ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache, + ctrl->num_bytes); + } + + if (ret == 0) { + if (!ctrl->is_readback) + ctrl->cached = true; + memcpy(ucontrol->value.bytes.data, ctrl->cache, + ctrl->num_bytes); + } + + mutex_unlock(&sigmadsp->lock); + + return ret; +} + +static void sigmadsp_control_free(struct snd_kcontrol *kcontrol) +{ + struct sigmadsp_control *ctrl = (void *)kcontrol->private_value; + + ctrl->kcontrol = NULL; +} + +static bool sigma_fw_validate_control_name(const char *name, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) { + /* Normal ASCII characters are valid */ + if (name[i] < ' ' || name[i] > '~') + return false; + } + + return true; +} + +static int sigma_fw_load_control(struct sigmadsp *sigmadsp, + const struct sigma_fw_chunk *chunk, unsigned int length) +{ + const struct sigma_fw_chunk_control *ctrl_chunk; + struct sigmadsp_control *ctrl; + unsigned int num_bytes; + size_t name_len; + char *name; + int ret; + + if (length <= sizeof(*ctrl_chunk)) + return -EINVAL; + + ctrl_chunk = (const struct sigma_fw_chunk_control *)chunk; + + name_len = length - sizeof(*ctrl_chunk); + if (name_len >= SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + name_len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1; + + /* Make sure there are no non-displayable characaters in the string */ + if (!sigma_fw_validate_control_name(ctrl_chunk->name, name_len)) + return -EINVAL; + + num_bytes = le16_to_cpu(ctrl_chunk->num_bytes); + ctrl = kzalloc(sizeof(*ctrl) + num_bytes, GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + name = kmemdup_nul(ctrl_chunk->name, name_len, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto err_free_ctrl; + } + ctrl->name = name; + + /* + * Readbacks doesn't work with non-volatile controls, since the + * firmware updates the control value without driver interaction. Mark + * the readbacks to ensure that the values are not cached. + */ + if (ctrl->name && strncmp(ctrl->name, READBACK_CTRL_NAME, + (sizeof(READBACK_CTRL_NAME) - 1)) == 0) + ctrl->is_readback = true; + + ctrl->addr = le16_to_cpu(ctrl_chunk->addr); + ctrl->num_bytes = num_bytes; + ctrl->samplerates = le32_to_cpu(chunk->samplerates); + + list_add_tail(&ctrl->head, &sigmadsp->ctrl_list); + + return 0; + +err_free_ctrl: + kfree(ctrl); + + return ret; +} + +static int sigma_fw_load_data(struct sigmadsp *sigmadsp, + const struct sigma_fw_chunk *chunk, unsigned int length) +{ + const struct sigma_fw_chunk_data *data_chunk; + struct sigmadsp_data *data; + + if (length <= sizeof(*data_chunk)) + return -EINVAL; + + data_chunk = (struct sigma_fw_chunk_data *)chunk; + + length -= sizeof(*data_chunk); + + data = kzalloc(sizeof(*data) + length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = le16_to_cpu(data_chunk->addr); + data->length = length; + data->samplerates = le32_to_cpu(chunk->samplerates); + memcpy(data->data, data_chunk->data, length); + list_add_tail(&data->head, &sigmadsp->data_list); + + return 0; +} + +static int sigma_fw_load_samplerates(struct sigmadsp *sigmadsp, + const struct sigma_fw_chunk *chunk, unsigned int length) +{ + const struct sigma_fw_chunk_samplerate *rate_chunk; + unsigned int num_rates; + unsigned int *rates; + unsigned int i; + + rate_chunk = (const struct sigma_fw_chunk_samplerate *)chunk; + + num_rates = (length - sizeof(*rate_chunk)) / sizeof(__le32); + + if (num_rates > 32 || num_rates == 0) + return -EINVAL; + + /* We only allow one samplerates block per file */ + if (sigmadsp->rate_constraints.count) + return -EINVAL; + + rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL); + if (!rates) + return -ENOMEM; + + for (i = 0; i < num_rates; i++) + rates[i] = le32_to_cpu(rate_chunk->samplerates[i]); + + sigmadsp->rate_constraints.count = num_rates; + sigmadsp->rate_constraints.list = rates; + + return 0; +} + +static int sigmadsp_fw_load_v2(struct sigmadsp *sigmadsp, + const struct firmware *fw) +{ + struct sigma_fw_chunk *chunk; + unsigned int length, pos; + int ret; + + /* + * Make sure that there is at least one chunk to avoid integer + * underflows later on. Empty firmware is still valid though. + */ + if (fw->size < sizeof(*chunk) + sizeof(struct sigma_firmware_header)) + return 0; + + pos = sizeof(struct sigma_firmware_header); + + while (pos < fw->size - sizeof(*chunk)) { + chunk = (struct sigma_fw_chunk *)(fw->data + pos); + + length = le32_to_cpu(chunk->length); + + if (length > fw->size - pos || length < sizeof(*chunk)) + return -EINVAL; + + switch (le32_to_cpu(chunk->tag)) { + case SIGMA_FW_CHUNK_TYPE_DATA: + ret = sigma_fw_load_data(sigmadsp, chunk, length); + break; + case SIGMA_FW_CHUNK_TYPE_CONTROL: + ret = sigma_fw_load_control(sigmadsp, chunk, length); + break; + case SIGMA_FW_CHUNK_TYPE_SAMPLERATES: + ret = sigma_fw_load_samplerates(sigmadsp, chunk, length); + break; + default: + dev_warn(sigmadsp->dev, "Unknown chunk type: %d\n", + chunk->tag); + ret = 0; + break; + } + + if (ret) + return ret; + + /* + * This can not overflow since if length is larger than the + * maximum firmware size (0x4000000) we'll error out earilier. + */ + pos += ALIGN(length, sizeof(__le32)); + } + + return 0; +} + +static inline u32 sigma_action_len(struct sigma_action *sa) +{ + return (sa->len_hi << 16) | le16_to_cpu(sa->len); +} + +static size_t sigma_action_size(struct sigma_action *sa) +{ + size_t payload = 0; + + switch (sa->instr) { + case SIGMA_ACTION_WRITEXBYTES: + case SIGMA_ACTION_WRITESINGLE: + case SIGMA_ACTION_WRITESAFELOAD: + payload = sigma_action_len(sa); + break; + default: + break; + } + + payload = ALIGN(payload, 2); + + return payload + sizeof(struct sigma_action); +} + +/* + * Returns a negative error value in case of an error, 0 if processing of + * the firmware should be stopped after this action, 1 otherwise. + */ +static int process_sigma_action(struct sigmadsp *sigmadsp, + struct sigma_action *sa) +{ + size_t len = sigma_action_len(sa); + struct sigmadsp_data *data; + + pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__, + sa->instr, sa->addr, len); + + switch (sa->instr) { + case SIGMA_ACTION_WRITEXBYTES: + case SIGMA_ACTION_WRITESINGLE: + case SIGMA_ACTION_WRITESAFELOAD: + if (len < 3) + return -EINVAL; + + data = kzalloc(sizeof(*data) + len - 2, GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = be16_to_cpu(sa->addr); + data->length = len - 2; + memcpy(data->data, sa->payload, data->length); + list_add_tail(&data->head, &sigmadsp->data_list); + break; + case SIGMA_ACTION_END: + return 0; + default: + return -EINVAL; + } + + return 1; +} + +static int sigmadsp_fw_load_v1(struct sigmadsp *sigmadsp, + const struct firmware *fw) +{ + struct sigma_action *sa; + size_t size, pos; + int ret; + + pos = sizeof(struct sigma_firmware_header); + + while (pos + sizeof(*sa) <= fw->size) { + sa = (struct sigma_action *)(fw->data + pos); + + size = sigma_action_size(sa); + pos += size; + if (pos > fw->size || size == 0) + break; + + ret = process_sigma_action(sigmadsp, sa); + + pr_debug("%s: action returned %i\n", __func__, ret); + + if (ret <= 0) + return ret; + } + + if (pos != fw->size) + return -EINVAL; + + return 0; +} + +static void sigmadsp_firmware_release(struct sigmadsp *sigmadsp) +{ + struct sigmadsp_control *ctrl, *_ctrl; + struct sigmadsp_data *data, *_data; + + list_for_each_entry_safe(ctrl, _ctrl, &sigmadsp->ctrl_list, head) { + kfree(ctrl->name); + kfree(ctrl); + } + + list_for_each_entry_safe(data, _data, &sigmadsp->data_list, head) + kfree(data); + + INIT_LIST_HEAD(&sigmadsp->ctrl_list); + INIT_LIST_HEAD(&sigmadsp->data_list); +} + +static void devm_sigmadsp_release(struct device *dev, void *res) +{ + sigmadsp_firmware_release((struct sigmadsp *)res); +} + +static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name) +{ + const struct sigma_firmware_header *ssfw_head; + const struct firmware *fw; + int ret; + u32 crc; + + /* first load the blob */ + ret = request_firmware(&fw, name, sigmadsp->dev); + if (ret) { + pr_debug("%s: request_firmware() failed with %i\n", __func__, ret); + goto done; + } + + /* then verify the header */ + ret = -EINVAL; + + /* + * Reject too small or unreasonable large files. The upper limit has been + * chosen a bit arbitrarily, but it should be enough for all practical + * purposes and having the limit makes it easier to avoid integer + * overflows later in the loading process. + */ + if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) { + dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n"); + goto done; + } + + ssfw_head = (void *)fw->data; + if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) { + dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n"); + goto done; + } + + crc = crc32(0, fw->data + sizeof(*ssfw_head), + fw->size - sizeof(*ssfw_head)); + pr_debug("%s: crc=%x\n", __func__, crc); + if (crc != le32_to_cpu(ssfw_head->crc)) { + dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n", + le32_to_cpu(ssfw_head->crc), crc); + goto done; + } + + switch (ssfw_head->version) { + case 1: + ret = sigmadsp_fw_load_v1(sigmadsp, fw); + break; + case 2: + ret = sigmadsp_fw_load_v2(sigmadsp, fw); + break; + default: + dev_err(sigmadsp->dev, + "Failed to load firmware: Invalid version %d. Supported firmware versions: 1, 2\n", + ssfw_head->version); + ret = -EINVAL; + break; + } + + if (ret) + sigmadsp_firmware_release(sigmadsp); + +done: + release_firmware(fw); + + return ret; +} + +static int sigmadsp_init(struct sigmadsp *sigmadsp, struct device *dev, + const struct sigmadsp_ops *ops, const char *firmware_name) +{ + sigmadsp->ops = ops; + sigmadsp->dev = dev; + + INIT_LIST_HEAD(&sigmadsp->ctrl_list); + INIT_LIST_HEAD(&sigmadsp->data_list); + mutex_init(&sigmadsp->lock); + + return sigmadsp_firmware_load(sigmadsp, firmware_name); +} + +/** + * devm_sigmadsp_init() - Initialize SigmaDSP instance + * @dev: The parent device + * @ops: The sigmadsp_ops to use for this instance + * @firmware_name: Name of the firmware file to load + * + * Allocates a SigmaDSP instance and loads the specified firmware file. + * + * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error. + */ +struct sigmadsp *devm_sigmadsp_init(struct device *dev, + const struct sigmadsp_ops *ops, const char *firmware_name) +{ + struct sigmadsp *sigmadsp; + int ret; + + sigmadsp = devres_alloc(devm_sigmadsp_release, sizeof(*sigmadsp), + GFP_KERNEL); + if (!sigmadsp) + return ERR_PTR(-ENOMEM); + + ret = sigmadsp_init(sigmadsp, dev, ops, firmware_name); + if (ret) { + devres_free(sigmadsp); + return ERR_PTR(ret); + } + + devres_add(dev, sigmadsp); + + return sigmadsp; +} +EXPORT_SYMBOL_GPL(devm_sigmadsp_init); + +static int sigmadsp_rate_to_index(struct sigmadsp *sigmadsp, unsigned int rate) +{ + unsigned int i; + + for (i = 0; i < sigmadsp->rate_constraints.count; i++) { + if (sigmadsp->rate_constraints.list[i] == rate) + return i; + } + + return -EINVAL; +} + +static unsigned int sigmadsp_get_samplerate_mask(struct sigmadsp *sigmadsp, + unsigned int samplerate) +{ + int samplerate_index; + + if (samplerate == 0) + return 0; + + if (sigmadsp->rate_constraints.count) { + samplerate_index = sigmadsp_rate_to_index(sigmadsp, samplerate); + if (samplerate_index < 0) + return 0; + + return BIT(samplerate_index); + } else { + return ~0; + } +} + +static bool sigmadsp_samplerate_valid(unsigned int supported, + unsigned int requested) +{ + /* All samplerates are supported */ + if (!supported) + return true; + + return supported & requested; +} + +static int sigmadsp_alloc_control(struct sigmadsp *sigmadsp, + struct sigmadsp_control *ctrl, unsigned int samplerate_mask) +{ + struct snd_kcontrol_new template; + struct snd_kcontrol *kcontrol; + + memset(&template, 0, sizeof(template)); + template.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + template.name = ctrl->name; + template.info = sigmadsp_ctrl_info; + template.get = sigmadsp_ctrl_get; + template.put = sigmadsp_ctrl_put; + template.private_value = (unsigned long)ctrl; + template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + if (!sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask)) + template.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + + kcontrol = snd_ctl_new1(&template, sigmadsp); + if (!kcontrol) + return -ENOMEM; + + kcontrol->private_free = sigmadsp_control_free; + ctrl->kcontrol = kcontrol; + + return snd_ctl_add(sigmadsp->component->card->snd_card, kcontrol); +} + +static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp, + struct sigmadsp_control *ctrl, unsigned int samplerate_mask) +{ + struct snd_card *card = sigmadsp->component->card->snd_card; + bool active; + int changed; + + active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask); + if (!ctrl->kcontrol) + return; + changed = snd_ctl_activate_id(card, &ctrl->kcontrol->id, active); + if (active && changed > 0) { + mutex_lock(&sigmadsp->lock); + if (ctrl->cached) + sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache); + mutex_unlock(&sigmadsp->lock); + } +} + +/** + * sigmadsp_attach() - Attach a sigmadsp instance to a ASoC component + * @sigmadsp: The sigmadsp instance to attach + * @component: The component to attach to + * + * Typically called in the components probe callback. + * + * Note, once this function has been called the firmware must not be released + * until after the ALSA snd_card that the component belongs to has been + * disconnected, even if sigmadsp_attach() returns an error. + */ +int sigmadsp_attach(struct sigmadsp *sigmadsp, + struct snd_soc_component *component) +{ + struct sigmadsp_control *ctrl; + unsigned int samplerate_mask; + int ret; + + sigmadsp->component = component; + + samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, + sigmadsp->current_samplerate); + + list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) { + ret = sigmadsp_alloc_control(sigmadsp, ctrl, samplerate_mask); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(sigmadsp_attach); + +/** + * sigmadsp_setup() - Setup the DSP for the specified samplerate + * @sigmadsp: The sigmadsp instance to configure + * @samplerate: The samplerate the DSP should be configured for + * + * Loads the appropriate firmware program and parameter memory (if not already + * loaded) and enables the controls for the specified samplerate. Any control + * parameter changes that have been made previously will be restored. + * + * Returns 0 on success, a negative error code otherwise. + */ +int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate) +{ + struct sigmadsp_control *ctrl; + unsigned int samplerate_mask; + struct sigmadsp_data *data; + int ret; + + if (sigmadsp->current_samplerate == samplerate) + return 0; + + samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, samplerate); + if (samplerate_mask == 0) + return -EINVAL; + + list_for_each_entry(data, &sigmadsp->data_list, head) { + if (!sigmadsp_samplerate_valid(data->samplerates, + samplerate_mask)) + continue; + ret = sigmadsp_write(sigmadsp, data->addr, data->data, + data->length); + if (ret) + goto err; + } + + list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) + sigmadsp_activate_ctrl(sigmadsp, ctrl, samplerate_mask); + + sigmadsp->current_samplerate = samplerate; + + return 0; +err: + sigmadsp_reset(sigmadsp); + + return ret; +} +EXPORT_SYMBOL_GPL(sigmadsp_setup); + +/** + * sigmadsp_reset() - Notify the sigmadsp instance that the DSP has been reset + * @sigmadsp: The sigmadsp instance to reset + * + * Should be called whenever the DSP has been reset and parameter and program + * memory need to be re-loaded. + */ +void sigmadsp_reset(struct sigmadsp *sigmadsp) +{ + struct sigmadsp_control *ctrl; + + list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) + sigmadsp_activate_ctrl(sigmadsp, ctrl, false); + + sigmadsp->current_samplerate = 0; +} +EXPORT_SYMBOL_GPL(sigmadsp_reset); + +/** + * sigmadsp_restrict_params() - Applies DSP firmware specific constraints + * @sigmadsp: The sigmadsp instance + * @substream: The substream to restrict + * + * Applies samplerate constraints that may be required by the firmware Should + * typically be called from the CODEC/component drivers startup callback. + * + * Returns 0 on success, a negative error code otherwise. + */ +int sigmadsp_restrict_params(struct sigmadsp *sigmadsp, + struct snd_pcm_substream *substream) +{ + if (sigmadsp->rate_constraints.count == 0) + return 0; + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &sigmadsp->rate_constraints); +} +EXPORT_SYMBOL_GPL(sigmadsp_restrict_params); + +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h new file mode 100644 index 0000000000..2783eff633 --- /dev/null +++ b/sound/soc/codecs/sigmadsp.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Load firmware files from Analog Devices SigmaStudio + * + * Copyright 2009-2011 Analog Devices Inc. + */ + +#ifndef __SIGMA_FIRMWARE_H__ +#define __SIGMA_FIRMWARE_H__ + +#include +#include +#include + +#include + +struct sigmadsp; +struct snd_soc_component; +struct snd_pcm_substream; + +struct sigmadsp_ops { + int (*safeload)(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t *data, size_t len); +}; + +struct sigmadsp { + const struct sigmadsp_ops *ops; + + struct list_head ctrl_list; + struct list_head data_list; + + struct snd_pcm_hw_constraint_list rate_constraints; + + unsigned int current_samplerate; + struct snd_soc_component *component; + struct device *dev; + + struct mutex lock; + + void *control_data; + int (*write)(void *, unsigned int, const uint8_t *, size_t); + int (*read)(void *, unsigned int, uint8_t *, size_t); +}; + +struct sigmadsp *devm_sigmadsp_init(struct device *dev, + const struct sigmadsp_ops *ops, const char *firmware_name); + +int sigmadsp_restrict_params(struct sigmadsp *sigmadsp, + struct snd_pcm_substream *substream); + +struct i2c_client; + +struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev, + struct regmap *regmap, const struct sigmadsp_ops *ops, + const char *firmware_name); +struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client, + const struct sigmadsp_ops *ops, const char *firmware_name); + +int sigmadsp_attach(struct sigmadsp *sigmadsp, + struct snd_soc_component *component); +int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate); +void sigmadsp_reset(struct sigmadsp *sigmadsp); + +#endif diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c new file mode 100644 index 0000000000..d306c585b5 --- /dev/null +++ b/sound/soc/codecs/simple-amplifier.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017 BayLibre, SAS. + * Author: Jerome Brunet + */ + +#include +#include +#include +#include + +#define DRV_NAME "simple-amplifier" + +struct simple_amp { + struct gpio_desc *gpiod_enable; +}; + +static int drv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct simple_amp *priv = snd_soc_component_get_drvdata(c); + int val; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = 1; + break; + case SND_SOC_DAPM_PRE_PMD: + val = 0; + break; + default: + WARN(1, "Unexpected event"); + return -EINVAL; + } + + gpiod_set_value_cansleep(priv->gpiod_enable, val); + + return 0; +} + +static const struct snd_soc_dapm_widget simple_amp_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("INL"), + SND_SOC_DAPM_INPUT("INR"), + SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event, + (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)), + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_OUTPUT("OUTR"), + SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 20, 0), +}; + +static const struct snd_soc_dapm_route simple_amp_dapm_routes[] = { + { "DRV", NULL, "INL" }, + { "DRV", NULL, "INR" }, + { "OUTL", NULL, "VCC" }, + { "OUTR", NULL, "VCC" }, + { "OUTL", NULL, "DRV" }, + { "OUTR", NULL, "DRV" }, +}; + +static const struct snd_soc_component_driver simple_amp_component_driver = { + .dapm_widgets = simple_amp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(simple_amp_dapm_widgets), + .dapm_routes = simple_amp_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(simple_amp_dapm_routes), +}; + +static int simple_amp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct simple_amp *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(priv->gpiod_enable)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_enable), + "Failed to get 'enable' gpio"); + + return devm_snd_soc_register_component(dev, + &simple_amp_component_driver, + NULL, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id simple_amp_ids[] = { + { .compatible = "dioo,dio2125", }, + { .compatible = "simple-audio-amplifier", }, + { } +}; +MODULE_DEVICE_TABLE(of, simple_amp_ids); +#endif + +static struct platform_driver simple_amp_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(simple_amp_ids), + }, + .probe = simple_amp_probe, +}; + +module_platform_driver(simple_amp_driver); + +MODULE_DESCRIPTION("ASoC Simple Audio Amplifier driver"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c new file mode 100644 index 0000000000..bf67de12d2 --- /dev/null +++ b/sound/soc/codecs/simple-mux.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020 Bootlin SA + * Author: Alexandre Belloni + */ + +#include +#include +#include +#include + +struct simple_mux { + struct gpio_desc *gpiod_mux; + unsigned int mux; +}; + +static const char * const simple_mux_texts[] = { + "Input 1", "Input 2" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(simple_mux_enum, simple_mux_texts); + +static int simple_mux_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); + struct simple_mux *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.enumerated.item[0] = priv->mux; + + return 0; +} + +static int simple_mux_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); + struct simple_mux *priv = snd_soc_component_get_drvdata(c); + + if (ucontrol->value.enumerated.item[0] > e->items) + return -EINVAL; + + if (priv->mux == ucontrol->value.enumerated.item[0]) + return 0; + + priv->mux = ucontrol->value.enumerated.item[0]; + + gpiod_set_value_cansleep(priv->gpiod_mux, priv->mux); + + return snd_soc_dapm_mux_update_power(dapm, kcontrol, + ucontrol->value.enumerated.item[0], + e, NULL); +} + +static unsigned int simple_mux_read(struct snd_soc_component *component, + unsigned int reg) +{ + struct simple_mux *priv = snd_soc_component_get_drvdata(component); + + return priv->mux; +} + +static const struct snd_kcontrol_new simple_mux_mux = + SOC_DAPM_ENUM_EXT("Muxer", simple_mux_enum, simple_mux_control_get, simple_mux_control_put); + +static const struct snd_soc_dapm_widget simple_mux_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_MUX("MUX", SND_SOC_NOPM, 0, 0, &simple_mux_mux), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route simple_mux_dapm_routes[] = { + { "OUT", NULL, "MUX" }, + { "MUX", "Input 1", "IN1" }, + { "MUX", "Input 2", "IN2" }, +}; + +static const struct snd_soc_component_driver simple_mux_component_driver = { + .dapm_widgets = simple_mux_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(simple_mux_dapm_widgets), + .dapm_routes = simple_mux_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(simple_mux_dapm_routes), + .read = simple_mux_read, +}; + +static int simple_mux_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct simple_mux *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + + priv->gpiod_mux = devm_gpiod_get(dev, "mux", GPIOD_OUT_LOW); + if (IS_ERR(priv->gpiod_mux)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_mux), + "Failed to get 'mux' gpio"); + + return devm_snd_soc_register_component(dev, &simple_mux_component_driver, NULL, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id simple_mux_ids[] = { + { .compatible = "simple-audio-mux", }, + { } +}; +MODULE_DEVICE_TABLE(of, simple_mux_ids); +#endif + +static struct platform_driver simple_mux_driver = { + .driver = { + .name = "simple-mux", + .of_match_table = of_match_ptr(simple_mux_ids), + }, + .probe = simple_mux_probe, +}; + +module_platform_driver(simple_mux_driver); + +MODULE_DESCRIPTION("ASoC Simple Audio Mux driver"); +MODULE_AUTHOR("Alexandre Belloni "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c new file mode 100644 index 0000000000..7b9abbc1bd --- /dev/null +++ b/sound/soc/codecs/sma1303.c @@ -0,0 +1,1820 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// sma1303.c -- sma1303 ALSA SoC Audio driver +// +// Copyright 2023 Iron Device Corporation +// +// Auther: Gyuhwa Park +// Kiseok Jo + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sma1303.h" + +#define CHECK_PERIOD_TIME 1 /* sec per HZ */ +#define MAX_CONTROL_NAME 48 + +#define PLL_MATCH(_input_clk_name, _output_clk_name, _input_clk,\ + _post_n, _n, _vco, _p_cp)\ +{\ + .input_clk_name = _input_clk_name,\ + .output_clk_name = _output_clk_name,\ + .input_clk = _input_clk,\ + .post_n = _post_n,\ + .n = _n,\ + .vco = _vco,\ + .p_cp = _p_cp,\ +} + +enum sma1303_type { + SMA1303, +}; + +struct sma1303_pll_match { + char *input_clk_name; + char *output_clk_name; + unsigned int input_clk; + unsigned int post_n; + unsigned int n; + unsigned int vco; + unsigned int p_cp; +}; + +struct sma1303_priv { + enum sma1303_type devtype; + struct attribute_group *attr_grp; + struct delayed_work check_fault_work; + struct device *dev; + struct kobject *kobj; + struct regmap *regmap; + struct sma1303_pll_match *pll_matches; + bool amp_power_status; + bool force_mute_status; + int num_of_pll_matches; + int retry_cnt; + unsigned int amp_mode; + unsigned int cur_vol; + unsigned int format; + unsigned int frame_size; + unsigned int init_vol; + unsigned int last_bclk; + unsigned int last_ocp_val; + unsigned int last_over_temp; + unsigned int rev_num; + unsigned int sys_clk_id; + unsigned int tdm_slot_rx; + unsigned int tdm_slot_tx; + unsigned int tsdw_cnt; + long check_fault_period; + long check_fault_status; +}; + +static struct sma1303_pll_match sma1303_pll_matches[] = { +PLL_MATCH("1.411MHz", "24.595MHz", 1411200, 0x07, 0xF4, 0x8B, 0x03), +PLL_MATCH("1.536MHz", "24.576MHz", 1536000, 0x07, 0xE0, 0x8B, 0x03), +PLL_MATCH("3.072MHz", "24.576MHz", 3072000, 0x07, 0x70, 0x8B, 0x03), +PLL_MATCH("6.144MHz", "24.576MHz", 6144000, 0x07, 0x70, 0x8B, 0x07), +PLL_MATCH("12.288MHz", "24.576MHz", 12288000, 0x07, 0x70, 0x8B, 0x0B), +PLL_MATCH("19.2MHz", "24.343MHz", 19200000, 0x07, 0x47, 0x8B, 0x0A), +PLL_MATCH("24.576MHz", "24.576MHz", 24576000, 0x07, 0x70, 0x8B, 0x0F), +}; + +static int sma1303_startup(struct snd_soc_component *); +static int sma1303_shutdown(struct snd_soc_component *); + +static const struct reg_default sma1303_reg_def[] = { + { 0x00, 0x80 }, + { 0x01, 0x00 }, + { 0x02, 0x00 }, + { 0x03, 0x11 }, + { 0x04, 0x17 }, + { 0x09, 0x00 }, + { 0x0A, 0x31 }, + { 0x0B, 0x98 }, + { 0x0C, 0x84 }, + { 0x0D, 0x07 }, + { 0x0E, 0x3F }, + { 0x10, 0x00 }, + { 0x11, 0x00 }, + { 0x12, 0x00 }, + { 0x14, 0x5C }, + { 0x15, 0x01 }, + { 0x16, 0x0F }, + { 0x17, 0x0F }, + { 0x18, 0x0F }, + { 0x19, 0x00 }, + { 0x1A, 0x00 }, + { 0x1B, 0x00 }, + { 0x23, 0x19 }, + { 0x24, 0x00 }, + { 0x25, 0x00 }, + { 0x26, 0x04 }, + { 0x33, 0x00 }, + { 0x36, 0x92 }, + { 0x37, 0x27 }, + { 0x3B, 0x5A }, + { 0x3C, 0x20 }, + { 0x3D, 0x00 }, + { 0x3E, 0x03 }, + { 0x3F, 0x0C }, + { 0x8B, 0x07 }, + { 0x8C, 0x70 }, + { 0x8D, 0x8B }, + { 0x8E, 0x6F }, + { 0x8F, 0x03 }, + { 0x90, 0x26 }, + { 0x91, 0x42 }, + { 0x92, 0xE0 }, + { 0x94, 0x35 }, + { 0x95, 0x0C }, + { 0x96, 0x42 }, + { 0x97, 0x95 }, + { 0xA0, 0x00 }, + { 0xA1, 0x3B }, + { 0xA2, 0xC8 }, + { 0xA3, 0x28 }, + { 0xA4, 0x40 }, + { 0xA5, 0x01 }, + { 0xA6, 0x41 }, + { 0xA7, 0x00 }, +}; + +static bool sma1303_readable_register(struct device *dev, unsigned int reg) +{ + bool result; + + if (reg > SMA1303_FF_DEVICE_INDEX) + return false; + + switch (reg) { + case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4: + case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL: + case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3: + case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7: + case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4: + case SMA1303_33_SDM_CTRL ... SMA1303_34_OTP_DATA1: + case SMA1303_36_PROTECTION ... SMA1303_38_OTP_TRM0: + case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2: + case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL: + case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4: + case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON: + case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2: + result = true; + break; + case SMA1303_FF_DEVICE_INDEX: + result = true; + break; + default: + result = false; + break; + } + return result; +} + +static bool sma1303_writeable_register(struct device *dev, unsigned int reg) +{ + bool result; + + if (reg > SMA1303_FF_DEVICE_INDEX) + return false; + + switch (reg) { + case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4: + case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL: + case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3: + case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7: + case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4: + case SMA1303_33_SDM_CTRL: + case SMA1303_36_PROTECTION ... SMA1303_37_SLOPE_CTRL: + case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2: + case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL: + case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4: + case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON: + result = true; + break; + default: + result = false; + break; + } + return result; +} + +static bool sma1303_volatile_register(struct device *dev, unsigned int reg) +{ + bool result; + + switch (reg) { + case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2: + result = true; + break; + case SMA1303_FF_DEVICE_INDEX: + result = true; + break; + default: + result = false; + break; + } + return result; +} + +static const DECLARE_TLV_DB_SCALE(sma1303_spk_tlv, -6000, 50, 0); + +static int sma1303_regmap_write(struct sma1303_priv *sma1303, + unsigned int reg, unsigned int val) +{ + int ret = 0; + int cnt = sma1303->retry_cnt; + + while (cnt--) { + ret = regmap_write(sma1303->regmap, reg, val); + if (ret < 0) { + dev_err(sma1303->dev, + "Failed to write [0x%02X]\n", reg); + } else + break; + } + return ret; +} + +static int sma1303_regmap_update_bits(struct sma1303_priv *sma1303, + unsigned int reg, unsigned int mask, unsigned int val, bool *change) +{ + int ret = 0; + int cnt = sma1303->retry_cnt; + + while (cnt--) { + ret = regmap_update_bits_check(sma1303->regmap, reg, + mask, val, change); + if (ret < 0) { + dev_err(sma1303->dev, + "Failed to update [0x%02X]\n", reg); + } else + break; + } + return ret; +} + +static int sma1303_regmap_read(struct sma1303_priv *sma1303, + unsigned int reg, unsigned int *val) +{ + int ret = 0; + int cnt = sma1303->retry_cnt; + + while (cnt--) { + ret = regmap_read(sma1303->regmap, reg, val); + if (ret < 0) { + dev_err(sma1303->dev, + "Failed to read [0x%02X]\n", reg); + } else + break; + } + return ret; +} + +static const char * const sma1303_aif_in_source_text[] = { + "Mono", "Left", "Right"}; +static const char * const sma1303_aif_out_source_text[] = { + "Disable", "After_FmtC", "After_Mixer", "After_DSP", "After_Post", + "Clk_PLL", "Clk_OSC"}; +static const char * const sma1303_tdm_slot_text[] = { + "Slot0", "Slot1", "Slot2", "Slot3", + "Slot4", "Slot5", "Slot6", "Slot7"}; + +static const struct soc_enum sma1303_aif_in_source_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_in_source_text), + sma1303_aif_in_source_text); +static const struct soc_enum sma1303_aif_out_source_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_out_source_text), + sma1303_aif_out_source_text); +static const struct soc_enum sma1303_tdm_slot_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_tdm_slot_text), + sma1303_tdm_slot_text); + +static int sma1303_force_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = (int)sma1303->force_mute_status; + dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__, + sma1303->force_mute_status ? "ON" : "OFF"); + + return 0; +} + +static int sma1303_force_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + bool change = false, val = (bool)ucontrol->value.integer.value[0]; + + if (sma1303->force_mute_status == val) + change = false; + else { + change = true; + sma1303->force_mute_status = val; + } + dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__, + sma1303->force_mute_status ? "ON" : "OFF"); + + return change; +} + +static int sma1303_postscaler_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int val, ret; + + ret = sma1303_regmap_read(sma1303, SMA1303_90_POSTSCALER, &val); + if (ret < 0) + return -EINVAL; + + ucontrol->value.integer.value[0] = (val & 0x7E) >> 1; + + return 0; +} + +static int sma1303_postscaler_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret, val = (int)ucontrol->value.integer.value[0]; + bool change; + + ret = sma1303_regmap_update_bits(sma1303, + SMA1303_90_POSTSCALER, 0x7E, (val << 1), &change); + if (ret < 0) + return -EINVAL; + + return change; +} + +static int sma1303_tdm_slot_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int val, ret; + + ret = sma1303_regmap_read(sma1303, SMA1303_A5_TDM1, &val); + if (ret < 0) + return -EINVAL; + + ucontrol->value.integer.value[0] = (val & 0x38) >> 3; + sma1303->tdm_slot_rx = ucontrol->value.integer.value[0]; + + return 0; +} + +static int sma1303_tdm_slot_rx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret, val = (int)ucontrol->value.integer.value[0]; + bool change; + + ret = sma1303_regmap_update_bits(sma1303, + SMA1303_A5_TDM1, 0x38, (val << 3), &change); + if (ret < 0) + return -EINVAL; + + return change; +} + +static int sma1303_tdm_slot_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int val, ret; + + ret = sma1303_regmap_read(sma1303, SMA1303_A6_TDM2, &val); + if (ret < 0) + return -EINVAL; + + ucontrol->value.integer.value[0] = (val & 0x38) >> 3; + sma1303->tdm_slot_tx = ucontrol->value.integer.value[0]; + + return 0; +} + +static int sma1303_tdm_slot_tx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret, val = (int)ucontrol->value.integer.value[0]; + bool change; + + ret = sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, 0x38, (val << 3), &change); + if (ret < 0) + return -EINVAL; + + return change; +} + +static int sma1303_startup(struct snd_soc_component *component) +{ + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + bool change = false, temp = false; + + sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL, + SMA1303_PLL_PD2_MASK, SMA1303_PLL_OPERATION2, &temp); + if (temp == true) + change = true; + + sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL, + SMA1303_POWER_MASK, SMA1303_POWER_ON, &temp); + if (temp == true) + change = true; + + if (sma1303->amp_mode == SMA1303_MONO) { + sma1303_regmap_update_bits(sma1303, + SMA1303_10_SYSTEM_CTRL1, + SMA1303_SPK_MODE_MASK, + SMA1303_SPK_MONO, + &temp); + if (temp == true) + change = true; + + } else { + sma1303_regmap_update_bits(sma1303, + SMA1303_10_SYSTEM_CTRL1, + SMA1303_SPK_MODE_MASK, + SMA1303_SPK_STEREO, + &temp); + if (temp == true) + change = true; + } + + if (sma1303->check_fault_status) { + if (sma1303->check_fault_period > 0) + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + sma1303->check_fault_period * HZ); + else + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + CHECK_PERIOD_TIME * HZ); + } + + sma1303->amp_power_status = true; + + return change; +} + +static int sma1303_shutdown(struct snd_soc_component *component) +{ + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + bool change = false, temp = false; + + cancel_delayed_work_sync(&sma1303->check_fault_work); + + sma1303_regmap_update_bits(sma1303, SMA1303_10_SYSTEM_CTRL1, + SMA1303_SPK_MODE_MASK, SMA1303_SPK_OFF, &temp); + if (temp == true) + change = true; + + sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL, + SMA1303_POWER_MASK, SMA1303_POWER_OFF, &temp); + if (temp == true) + change = true; + sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL, + SMA1303_PLL_PD2_MASK, SMA1303_PLL_PD2, &temp); + if (temp == true) + change = true; + + sma1303->amp_power_status = false; + + return change; +} + +static int sma1303_aif_in_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + int ret = 0; + bool change = false, temp = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (mux) { + case 0: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_11_SYSTEM_CTRL2, + SMA1303_MONOMIX_MASK, + SMA1303_MONOMIX_ON, + &change); + sma1303->amp_mode = SMA1303_MONO; + break; + case 1: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_11_SYSTEM_CTRL2, + SMA1303_MONOMIX_MASK, + SMA1303_MONOMIX_OFF, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_11_SYSTEM_CTRL2, + SMA1303_LR_DATA_SW_MASK, + SMA1303_LR_DATA_SW_NORMAL, + &temp); + if (temp == true) + change = true; + sma1303->amp_mode = SMA1303_STEREO; + break; + case 2: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_11_SYSTEM_CTRL2, + SMA1303_MONOMIX_MASK, + SMA1303_MONOMIX_OFF, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_11_SYSTEM_CTRL2, + SMA1303_LR_DATA_SW_MASK, + SMA1303_LR_DATA_SW_SWAP, + &temp); + if (temp == true) + change = true; + sma1303->amp_mode = SMA1303_STEREO; + break; + default: + dev_err(sma1303->dev, "%s : Invalid value (%d)\n", + __func__, mux); + return -EINVAL; + } + + dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__, + sma1303_aif_in_source_text[mux]); + break; + } + if (ret < 0) + return -EINVAL; + return change; +} + +static int sma1303_aif_out_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + int ret = 0; + bool change = false, temp = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (mux) { + case 0: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_NORMAL_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_OUT_SEL_MASK, + SMA1303_OUT_SEL_DISABLE, + &temp); + if (temp == true) + change = true; + break; + case 1: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_NORMAL_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_OUT_SEL_MASK, + SMA1303_FORMAT_CONVERTER, + &temp); + if (temp == true) + change = true; + break; + case 2: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_NORMAL_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_OUT_SEL_MASK, + SMA1303_MIXER_OUTPUT, + &temp); + if (temp == true) + change = true; + break; + case 3: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_NORMAL_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_OUT_SEL_MASK, + SMA1303_SPEAKER_PATH, + &temp); + if (temp == true) + change = true; + break; + case 4: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_NORMAL_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_OUT_SEL_MASK, + SMA1303_POSTSCALER_OUTPUT, + &temp); + if (temp == true) + change = true; + break; + case 5: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_CLK_OUT_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_MON_OSC_PLL_MASK, + SMA1303_PLL_SDO, + &temp); + if (temp == true) + change = true; + break; + case 6: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_TEST_CLKO_EN_MASK, + SMA1303_CLK_OUT_SDO, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_MON_OSC_PLL_MASK, + SMA1303_OSC_SDO, + &temp); + if (temp == true) + change = true; + break; + default: + dev_err(sma1303->dev, "%s : Invalid value (%d)\n", + __func__, mux); + return -EINVAL; + } + + dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__, + sma1303_aif_out_source_text[mux]); + break; + } + if (ret < 0) + return -EINVAL; + return change; +} + +static int sma1303_sdo_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + bool change = false, temp = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_PRE_PMU\n", __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_CONFIG_MASK, + SMA1303_OUTPUT_PORT_ENABLE, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_SDO_OUTPUT_MASK, + SMA1303_NORMAL_OUT, + &temp); + if (temp == true) + change = true; + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_POST_PMD\n", __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_09_OUTPUT_CTRL, + SMA1303_PORT_CONFIG_MASK, + SMA1303_INPUT_PORT_ONLY, + &temp); + if (temp == true) + change = true; + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A3_TOP_MAN2, + SMA1303_SDO_OUTPUT_MASK, + SMA1303_HIGH_Z_OUT, + &temp); + if (temp == true) + change = true; + break; + } + if (ret < 0) + return -EINVAL; + return change; +} + +static int sma1303_post_scaler_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + bool change = false; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_PRE_PMU\n", __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_90_POSTSCALER, + SMA1303_BYP_POST_MASK, + SMA1303_EN_POST_SCALER, + &change); + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_POST_PMD\n", __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_90_POSTSCALER, + SMA1303_BYP_POST_MASK, + SMA1303_BYP_POST_SCALER, + &change); + break; + } + if (ret < 0) + return -EINVAL; + return change; +} + +static int sma1303_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_POST_PMU\n", __func__); + ret = sma1303_startup(component); + break; + case SND_SOC_DAPM_PRE_PMD: + dev_dbg(sma1303->dev, + "%s : SND_SOC_DAPM_PRE_PMD\n", __func__); + ret = sma1303_shutdown(component); + break; + } + return ret; +} + +static const struct snd_kcontrol_new sma1303_aif_in_source_control = + SOC_DAPM_ENUM("AIF IN Source", sma1303_aif_in_source_enum); +static const struct snd_kcontrol_new sma1303_aif_out_source_control = + SOC_DAPM_ENUM("AIF OUT Source", sma1303_aif_out_source_enum); +static const struct snd_kcontrol_new sma1303_sdo_control = + SOC_DAPM_SINGLE_VIRT("Switch", 1); +static const struct snd_kcontrol_new sma1303_post_scaler_control = + SOC_DAPM_SINGLE_VIRT("Switch", 1); +static const struct snd_kcontrol_new sma1303_enable_control = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new sma1303_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", SMA1303_0A_SPK_VOL, + 0, 167, 1, sma1303_spk_tlv), + SOC_SINGLE_BOOL_EXT("Force Mute Switch", 0, + sma1303_force_mute_get, sma1303_force_mute_put), + SOC_SINGLE_EXT("Postscaler Gain", SMA1303_90_POSTSCALER, 1, 0x30, 0, + sma1303_postscaler_get, sma1303_postscaler_put), + SOC_ENUM_EXT("TDM RX Slot Position", sma1303_tdm_slot_enum, + sma1303_tdm_slot_rx_get, sma1303_tdm_slot_rx_put), + SOC_ENUM_EXT("TDM TX Slot Position", sma1303_tdm_slot_enum, + sma1303_tdm_slot_tx_get, sma1303_tdm_slot_tx_put), +}; + +static const struct snd_soc_dapm_widget sma1303_dapm_widgets[] = { + /* platform domain */ + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("SDO"), + + /* path domain */ + SND_SOC_DAPM_MUX_E("AIF IN Source", SND_SOC_NOPM, 0, 0, + &sma1303_aif_in_source_control, + sma1303_aif_in_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MUX_E("AIF OUT Source", SND_SOC_NOPM, 0, 0, + &sma1303_aif_out_source_control, + sma1303_aif_out_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SWITCH_E("SDO Enable", SND_SOC_NOPM, 0, 0, + &sma1303_sdo_control, + sma1303_sdo_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("Entry", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SWITCH_E("Post Scaler", SND_SOC_NOPM, 0, 1, + &sma1303_post_scaler_control, + sma1303_post_scaler_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUT_DRV_E("AMP Power", SND_SOC_NOPM, 0, 0, NULL, 0, + sma1303_power_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1, + &sma1303_enable_control), + + /* stream domain */ + SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route sma1303_audio_map[] = { + /* Playback */ + {"AIF IN Source", "Mono", "AIF IN"}, + {"AIF IN Source", "Left", "AIF IN"}, + {"AIF IN Source", "Right", "AIF IN"}, + + {"SDO Enable", "Switch", "AIF IN"}, + {"AIF OUT Source", "Disable", "SDO Enable"}, + {"AIF OUT Source", "After_FmtC", "SDO Enable"}, + {"AIF OUT Source", "After_Mixer", "SDO Enable"}, + {"AIF OUT Source", "After_DSP", "SDO Enable"}, + {"AIF OUT Source", "After_Post", "SDO Enable"}, + {"AIF OUT Source", "Clk_PLL", "SDO Enable"}, + {"AIF OUT Source", "Clk_OSC", "SDO Enable"}, + + {"Entry", NULL, "AIF OUT Source"}, + {"Entry", NULL, "AIF IN Source"}, + + {"Post Scaler", "Switch", "Entry"}, + {"AMP Power", NULL, "Entry"}, + {"AMP Power", NULL, "Entry"}, + + {"AMP Enable", "Switch", "AMP Power"}, + {"SPK", NULL, "AMP Enable"}, + + /* Capture */ + {"AIF OUT", NULL, "AMP Enable"}, +}; + +static int sma1303_setup_pll(struct snd_soc_component *component, + unsigned int bclk) +{ + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + + int i = 0, ret = 0; + + dev_dbg(component->dev, "%s : BCLK = %dHz\n", + __func__, bclk); + + if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_MCLK) { + dev_dbg(component->dev, "%s : MCLK is not supported\n", + __func__); + } else if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) { + for (i = 0; i < sma1303->num_of_pll_matches; i++) { + if (sma1303->pll_matches[i].input_clk == bclk) + break; + } + if (i == sma1303->num_of_pll_matches) { + dev_dbg(component->dev, "%s : No matching value between pll table and SCK\n", + __func__); + return -EINVAL; + } + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A2_TOP_MAN1, + SMA1303_PLL_PD_MASK|SMA1303_PLL_REF_CLK_MASK, + SMA1303_PLL_OPERATION|SMA1303_PLL_SCK, + NULL); + } + + ret += sma1303_regmap_write(sma1303, + SMA1303_8B_PLL_POST_N, + sma1303->pll_matches[i].post_n); + + ret += sma1303_regmap_write(sma1303, + SMA1303_8C_PLL_N, + sma1303->pll_matches[i].n); + + ret += sma1303_regmap_write(sma1303, + SMA1303_8D_PLL_A_SETTING, + sma1303->pll_matches[i].vco); + + ret += sma1303_regmap_write(sma1303, + SMA1303_8F_PLL_P_CP, + sma1303->pll_matches[i].p_cp); + if (ret < 0) + return -EINVAL; + + return 0; +} + +static int sma1303_dai_hw_params_amp(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + unsigned int bclk = 0; + int ret = 0; + + if (sma1303->format == SND_SOC_DAIFMT_DSP_A) + bclk = params_rate(params) * sma1303->frame_size; + else + bclk = params_rate(params) * params_physical_width(params) + * params_channels(params); + + dev_dbg(component->dev, + "%s : rate = %d : bit size = %d : channel = %d\n", + __func__, params_rate(params), params_width(params), + params_channels(params)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) { + if (sma1303->last_bclk != bclk) { + sma1303_setup_pll(component, bclk); + sma1303->last_bclk = bclk; + } + } + + switch (params_rate(params)) { + case 8000: + case 12000: + case 16000: + case 24000: + case 32000: + case 44100: + case 48000: + case 96000: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A2_TOP_MAN1, + SMA1303_DAC_DN_CONV_MASK, + SMA1303_DAC_DN_CONV_DISABLE, + NULL); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_LEFTPOL_MASK, + SMA1303_LOW_FIRST_CH, + NULL); + break; + + case 192000: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A2_TOP_MAN1, + SMA1303_DAC_DN_CONV_MASK, + SMA1303_DAC_DN_CONV_ENABLE, + NULL); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_LEFTPOL_MASK, + SMA1303_HIGH_FIRST_CH, + NULL); + break; + + default: + dev_err(component->dev, "%s not support rate : %d\n", + __func__, params_rate(params)); + + return -EINVAL; + } + + } else { + + switch (params_format(params)) { + + case SNDRV_PCM_FORMAT_S16_LE: + dev_dbg(component->dev, + "%s set format SNDRV_PCM_FORMAT_S16_LE\n", + __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_SCK_RATE_MASK, + SMA1303_SCK_32FS, + NULL); + break; + + case SNDRV_PCM_FORMAT_S24_LE: + dev_dbg(component->dev, + "%s set format SNDRV_PCM_FORMAT_S24_LE\n", + __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_SCK_RATE_MASK, + SMA1303_SCK_64FS, + NULL); + break; + case SNDRV_PCM_FORMAT_S32_LE: + dev_dbg(component->dev, + "%s set format SNDRV_PCM_FORMAT_S32_LE\n", + __func__); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_SCK_RATE_MASK, + SMA1303_SCK_64FS, + NULL); + break; + default: + dev_err(component->dev, + "%s not support data bit : %d\n", __func__, + params_format(params)); + return -EINVAL; + } + } + + switch (sma1303->format) { + case SND_SOC_DAIFMT_I2S: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_I2S_MODE_MASK, + SMA1303_STANDARD_I2S, + NULL); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_O_FORMAT_MASK, + SMA1303_O_FMT_I2S, + NULL); + break; + case SND_SOC_DAIFMT_LEFT_J: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_I2S_MODE_MASK, + SMA1303_LJ, + NULL); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_O_FORMAT_MASK, + SMA1303_O_FMT_LJ, + NULL); + break; + case SND_SOC_DAIFMT_RIGHT_J: + switch (params_width(params)) { + case 16: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_I2S_MODE_MASK, + SMA1303_RJ_16BIT, + NULL); + break; + case 24: + case 32: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_I2S_MODE_MASK, + SMA1303_RJ_24BIT, + NULL); + break; + } + break; + case SND_SOC_DAIFMT_DSP_A: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_I2S_MODE_MASK, + SMA1303_STANDARD_I2S, + NULL); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_O_FORMAT_MASK, + SMA1303_O_FMT_TDM, + NULL); + break; + } + + switch (params_width(params)) { + case 16: + case 24: + case 32: + break; + default: + dev_err(component->dev, + "%s not support data bit : %d\n", __func__, + params_format(params)); + return -EINVAL; + } + if (ret < 0) + return -EINVAL; + + return 0; +} + +static int sma1303_dai_set_sysclk_amp(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case SMA1303_EXTERNAL_CLOCK_19_2: + break; + case SMA1303_EXTERNAL_CLOCK_24_576: + break; + case SMA1303_PLL_CLKIN_MCLK: + break; + case SMA1303_PLL_CLKIN_BCLK: + break; + default: + dev_err(component->dev, "Invalid clk id: %d\n", clk_id); + return -EINVAL; + } + sma1303->sys_clk_id = clk_id; + return 0; +} + +static int sma1303_dai_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + + if (stream == SNDRV_PCM_STREAM_CAPTURE) + return ret; + + if (mute) { + dev_dbg(component->dev, "%s : %s\n", __func__, "MUTE"); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_0E_MUTE_VOL_CTRL, + SMA1303_SPK_MUTE_MASK, + SMA1303_SPK_MUTE, + NULL); + + /* Need to wait time for mute slope */ + msleep(55); + } else { + if (!sma1303->force_mute_status) { + dev_dbg(component->dev, "%s : %s\n", + __func__, "UNMUTE"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_0E_MUTE_VOL_CTRL, + SMA1303_SPK_MUTE_MASK, + SMA1303_SPK_UNMUTE, + NULL); + } else { + dev_dbg(sma1303->dev, + "%s : FORCE MUTE!!!\n", __func__); + } + } + + if (ret < 0) + return -EINVAL; + return 0; +} + +static int sma1303_dai_set_fmt_amp(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + + case SND_SOC_DAIFMT_CBC_CFC: + dev_dbg(component->dev, + "%s : %s\n", __func__, "I2S/TDM Device mode"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_CONTROLLER_DEVICE_MASK, + SMA1303_DEVICE_MODE, + NULL); + break; + + case SND_SOC_DAIFMT_CBP_CFP: + dev_dbg(component->dev, + "%s : %s\n", __func__, "I2S/TDM Controller mode"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_CONTROLLER_DEVICE_MASK, + SMA1303_CONTROLLER_MODE, + NULL); + break; + + default: + dev_err(component->dev, + "Unsupported Controller/Device : 0x%x\n", fmt); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + sma1303->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + dev_err(component->dev, + "Unsupported Audio Interface Format : 0x%x\n", fmt); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + + case SND_SOC_DAIFMT_IB_NF: + dev_dbg(component->dev, "%s : %s\n", + __func__, "Invert BCLK + Normal Frame"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_SCK_RISING_MASK, + SMA1303_SCK_RISING_EDGE, + NULL); + break; + case SND_SOC_DAIFMT_IB_IF: + dev_dbg(component->dev, "%s : %s\n", + __func__, "Invert BCLK + Invert Frame"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_LEFTPOL_MASK|SMA1303_SCK_RISING_MASK, + SMA1303_HIGH_FIRST_CH|SMA1303_SCK_RISING_EDGE, + NULL); + break; + case SND_SOC_DAIFMT_NB_IF: + dev_dbg(component->dev, "%s : %s\n", + __func__, "Normal BCLK + Invert Frame"); + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_01_INPUT1_CTRL1, + SMA1303_LEFTPOL_MASK, + SMA1303_HIGH_FIRST_CH, + NULL); + break; + case SND_SOC_DAIFMT_NB_NF: + dev_dbg(component->dev, "%s : %s\n", + __func__, "Normal BCLK + Normal Frame"); + break; + default: + dev_err(component->dev, + "Unsupported Bit & Frameclock : 0x%x\n", fmt); + return -EINVAL; + } + + if (ret < 0) + return -EINVAL; + return 0; +} + +static int sma1303_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + int ret = 0; + + dev_dbg(component->dev, "%s : slots = %d, slot_width - %d\n", + __func__, slots, slot_width); + + sma1303->frame_size = slot_width * slots; + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A4_TOP_MAN3, + SMA1303_O_FORMAT_MASK, + SMA1303_O_FMT_TDM, + NULL); + + switch (slot_width) { + case 16: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, + SMA1303_TDM_DL_MASK, + SMA1303_TDM_DL_16, + NULL); + break; + case 32: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, + SMA1303_TDM_DL_MASK, + SMA1303_TDM_DL_32, + NULL); + break; + default: + dev_err(component->dev, "%s not support TDM %d slot_width\n", + __func__, slot_width); + break; + } + + switch (slots) { + case 4: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, + SMA1303_TDM_N_SLOT_MASK, + SMA1303_TDM_N_SLOT_4, + NULL); + break; + case 8: + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, + SMA1303_TDM_N_SLOT_MASK, + SMA1303_TDM_N_SLOT_8, + NULL); + break; + default: + dev_err(component->dev, "%s not support TDM %d slots\n", + __func__, slots); + break; + } + + if (sma1303->tdm_slot_rx < slots) + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A5_TDM1, + SMA1303_TDM_SLOT1_RX_POS_MASK, + (sma1303->tdm_slot_rx) << 3, + NULL); + else + dev_err(component->dev, "%s Incorrect tdm-slot-rx %d set\n", + __func__, sma1303->tdm_slot_rx); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A5_TDM1, + SMA1303_TDM_CLK_POL_MASK, + SMA1303_TDM_CLK_POL_RISE, + NULL); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A5_TDM1, + SMA1303_TDM_TX_MODE_MASK, + SMA1303_TDM_TX_MONO, + NULL); + + if (sma1303->tdm_slot_tx < slots) + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_A6_TDM2, + SMA1303_TDM_SLOT1_TX_POS_MASK, + (sma1303->tdm_slot_tx) << 3, + NULL); + else + dev_err(component->dev, "%s Incorrect tdm-slot-tx %d set\n", + __func__, sma1303->tdm_slot_tx); + + if (ret < 0) + return -EINVAL; + return 0; +} + +static const struct snd_soc_dai_ops sma1303_dai_ops_amp = { + .set_sysclk = sma1303_dai_set_sysclk_amp, + .set_fmt = sma1303_dai_set_fmt_amp, + .hw_params = sma1303_dai_hw_params_amp, + .mute_stream = sma1303_dai_mute, + .set_tdm_slot = sma1303_dai_set_tdm_slot, +}; + +#define SMA1303_RATES SNDRV_PCM_RATE_8000_192000 +#define SMA1303_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver sma1303_dai[] = { + { + .name = "sma1303-amplifier", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SMA1303_RATES, + .formats = SMA1303_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SMA1303_RATES, + .formats = SMA1303_FORMATS, + }, + .ops = &sma1303_dai_ops_amp, + }, +}; + +static void sma1303_check_fault_worker(struct work_struct *work) +{ + struct sma1303_priv *sma1303 = + container_of(work, struct sma1303_priv, check_fault_work.work); + int ret = 0; + unsigned int over_temp, ocp_val, uvlo_val; + + if (sma1303->tsdw_cnt) + ret = sma1303_regmap_read(sma1303, + SMA1303_0A_SPK_VOL, &sma1303->cur_vol); + else + ret = sma1303_regmap_read(sma1303, + SMA1303_0A_SPK_VOL, &sma1303->init_vol); + + if (ret != 0) { + dev_err(sma1303->dev, + "failed to read SMA1303_0A_SPK_VOL : %d\n", ret); + return; + } + + ret = sma1303_regmap_read(sma1303, SMA1303_FA_STATUS1, &over_temp); + if (ret != 0) { + dev_err(sma1303->dev, + "failed to read SMA1303_FA_STATUS1 : %d\n", ret); + return; + } + + ret = sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &ocp_val); + if (ret != 0) { + dev_err(sma1303->dev, + "failed to read SMA1303_FB_STATUS2 : %d\n", ret); + return; + } + + ret = sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &uvlo_val); + if (ret != 0) { + dev_err(sma1303->dev, + "failed to read SMA1303_FF_DEVICE_INDEX : %d\n", ret); + return; + } + + if (~over_temp & SMA1303_OT1_OK_STATUS) { + dev_crit(sma1303->dev, + "%s : OT1(Over Temperature Level 1)\n", __func__); + + if ((sma1303->cur_vol + 6) <= 0xFF) + sma1303_regmap_write(sma1303, + SMA1303_0A_SPK_VOL, sma1303->cur_vol + 6); + + sma1303->tsdw_cnt++; + } else if (sma1303->tsdw_cnt) { + sma1303_regmap_write(sma1303, + SMA1303_0A_SPK_VOL, sma1303->init_vol); + sma1303->tsdw_cnt = 0; + sma1303->cur_vol = sma1303->init_vol; + } + + if (~over_temp & SMA1303_OT2_OK_STATUS) { + dev_crit(sma1303->dev, + "%s : OT2(Over Temperature Level 2)\n", __func__); + } + if (ocp_val & SMA1303_OCP_SPK_STATUS) { + dev_crit(sma1303->dev, + "%s : OCP_SPK(Over Current Protect SPK)\n", __func__); + } + if (ocp_val & SMA1303_OCP_BST_STATUS) { + dev_crit(sma1303->dev, + "%s : OCP_BST(Over Current Protect Boost)\n", __func__); + } + if ((ocp_val & SMA1303_CLK_MON_STATUS) && (sma1303->amp_power_status)) { + dev_crit(sma1303->dev, + "%s : CLK_FAULT(No clock input)\n", __func__); + } + if (uvlo_val & SMA1303_UVLO_BST_STATUS) { + dev_crit(sma1303->dev, + "%s : UVLO(Under Voltage Lock Out)\n", __func__); + } + + if ((over_temp != sma1303->last_over_temp) || + (ocp_val != sma1303->last_ocp_val)) { + + dev_crit(sma1303->dev, "Please check AMP status"); + dev_dbg(sma1303->dev, "STATUS1=0x%02X : STATUS2=0x%02X\n", + over_temp, ocp_val); + sma1303->last_over_temp = over_temp; + sma1303->last_ocp_val = ocp_val; + } + + if (sma1303->check_fault_status) { + if (sma1303->check_fault_period > 0) + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + sma1303->check_fault_period * HZ); + else + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + CHECK_PERIOD_TIME * HZ); + } + + if (!(~over_temp & SMA1303_OT1_OK_STATUS) + && !(~over_temp & SMA1303_OT2_OK_STATUS) + && !(ocp_val & SMA1303_OCP_SPK_STATUS) + && !(ocp_val & SMA1303_OCP_BST_STATUS) + && !(ocp_val & SMA1303_CLK_MON_STATUS) + && !(uvlo_val & SMA1303_UVLO_BST_STATUS)) { + } +} + +static int sma1303_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static void sma1303_remove(struct snd_soc_component *component) +{ + struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&sma1303->check_fault_work); +} + +static const struct snd_soc_component_driver sma1303_component = { + .probe = sma1303_probe, + .remove = sma1303_remove, + .controls = sma1303_snd_controls, + .num_controls = ARRAY_SIZE(sma1303_snd_controls), + .dapm_widgets = sma1303_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sma1303_dapm_widgets), + .dapm_routes = sma1303_audio_map, + .num_dapm_routes = ARRAY_SIZE(sma1303_audio_map), +}; + +static const struct regmap_config sma_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = SMA1303_FF_DEVICE_INDEX, + .readable_reg = sma1303_readable_register, + .writeable_reg = sma1303_writeable_register, + .volatile_reg = sma1303_volatile_register, + + .cache_type = REGCACHE_NONE, + .reg_defaults = sma1303_reg_def, + .num_reg_defaults = ARRAY_SIZE(sma1303_reg_def), +}; + +static ssize_t check_fault_period_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sma1303_priv *sma1303 = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%ld\n", sma1303->check_fault_period); +} + +static ssize_t check_fault_period_store(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + struct sma1303_priv *sma1303 = dev_get_drvdata(dev); + int ret; + + ret = kstrtol(buf, 10, &sma1303->check_fault_period); + + if (ret) + return -EINVAL; + + return (ssize_t)count; +} + +static DEVICE_ATTR_RW(check_fault_period); + +static ssize_t check_fault_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sma1303_priv *sma1303 = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%ld\n", sma1303->check_fault_status); +} + +static ssize_t check_fault_status_store(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + struct sma1303_priv *sma1303 = dev_get_drvdata(dev); + int ret; + + ret = kstrtol(buf, 10, &sma1303->check_fault_status); + + if (ret) + return -EINVAL; + + if (sma1303->check_fault_status) { + if (sma1303->check_fault_period > 0) + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + sma1303->check_fault_period * HZ); + else + queue_delayed_work(system_freezable_wq, + &sma1303->check_fault_work, + CHECK_PERIOD_TIME * HZ); + } + + return (ssize_t)count; +} + +static DEVICE_ATTR_RW(check_fault_status); + +static struct attribute *sma1303_attr[] = { + &dev_attr_check_fault_period.attr, + &dev_attr_check_fault_status.attr, + NULL, +}; + +static struct attribute_group sma1303_attr_group = { + .attrs = sma1303_attr, +}; + +static int sma1303_i2c_probe(struct i2c_client *client) +{ + struct sma1303_priv *sma1303; + int ret, i = 0; + unsigned int device_info, status, otp_stat; + + sma1303 = devm_kzalloc(&client->dev, + sizeof(struct sma1303_priv), GFP_KERNEL); + if (!sma1303) + return -ENOMEM; + sma1303->dev = &client->dev; + + sma1303->regmap = devm_regmap_init_i2c(client, &sma_i2c_regmap); + if (IS_ERR(sma1303->regmap)) { + ret = PTR_ERR(sma1303->regmap); + dev_err(&client->dev, + "Failed to allocate register map: %d\n", ret); + + return ret; + } + + ret = sma1303_regmap_read(sma1303, + SMA1303_FF_DEVICE_INDEX, &device_info); + + if ((ret != 0) || ((device_info & 0xF8) != SMA1303_DEVICE_ID)) { + dev_err(&client->dev, "device initialization error (%d 0x%02X)", + ret, device_info); + } + dev_dbg(&client->dev, "chip version 0x%02X\n", device_info); + + ret += sma1303_regmap_update_bits(sma1303, + SMA1303_00_SYSTEM_CTRL, + SMA1303_RESETBYI2C_MASK, SMA1303_RESETBYI2C_RESET, + NULL); + + ret += sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &status); + sma1303->rev_num = status & SMA1303_REV_NUM_STATUS; + if (sma1303->rev_num == SMA1303_REV_NUM_TV0) + dev_dbg(&client->dev, "SMA1303 Trimming Version 0\n"); + else if (sma1303->rev_num == SMA1303_REV_NUM_TV1) + dev_dbg(&client->dev, "SMA1303 Trimming Version 1\n"); + + ret += sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &otp_stat); + if (ret < 0) + dev_err(&client->dev, + "failed to read, register: %02X, ret: %d\n", + SMA1303_FF_DEVICE_INDEX, ret); + + if (((sma1303->rev_num == SMA1303_REV_NUM_TV0) && + ((otp_stat & 0x0E) == SMA1303_OTP_STAT_OK_0)) || + ((sma1303->rev_num != SMA1303_REV_NUM_TV0) && + ((otp_stat & 0x0C) == SMA1303_OTP_STAT_OK_1))) + dev_dbg(&client->dev, "SMA1303 OTP Status Successful\n"); + else + dev_dbg(&client->dev, "SMA1303 OTP Status Fail\n"); + + for (i = 0; i < (unsigned int)ARRAY_SIZE(sma1303_reg_def); i++) + ret += sma1303_regmap_write(sma1303, + sma1303_reg_def[i].reg, + sma1303_reg_def[i].def); + + sma1303->amp_mode = SMA1303_MONO; + sma1303->amp_power_status = false; + sma1303->check_fault_period = CHECK_PERIOD_TIME; + sma1303->check_fault_status = true; + sma1303->force_mute_status = false; + sma1303->init_vol = 0x31; + sma1303->cur_vol = sma1303->init_vol; + sma1303->last_bclk = 0; + sma1303->last_ocp_val = 0x08; + sma1303->last_over_temp = 0xC0; + sma1303->tsdw_cnt = 0; + sma1303->retry_cnt = SMA1303_I2C_RETRY_COUNT; + sma1303->tdm_slot_rx = 0; + sma1303->tdm_slot_tx = 0; + sma1303->sys_clk_id = SMA1303_PLL_CLKIN_BCLK; + + sma1303->dev = &client->dev; + sma1303->kobj = &client->dev.kobj; + + INIT_DELAYED_WORK(&sma1303->check_fault_work, + sma1303_check_fault_worker); + + i2c_set_clientdata(client, sma1303); + + sma1303->pll_matches = sma1303_pll_matches; + sma1303->num_of_pll_matches = + ARRAY_SIZE(sma1303_pll_matches); + + ret = devm_snd_soc_register_component(&client->dev, + &sma1303_component, sma1303_dai, 1); + if (ret) { + dev_err(&client->dev, "Failed to register component"); + + return ret; + } + + sma1303->attr_grp = &sma1303_attr_group; + ret = sysfs_create_group(sma1303->kobj, sma1303->attr_grp); + if (ret) { + dev_err(&client->dev, + "failed to create attribute group [%d]\n", ret); + sma1303->attr_grp = NULL; + } + + return ret; +} + +static void sma1303_i2c_remove(struct i2c_client *client) +{ + struct sma1303_priv *sma1303 = + (struct sma1303_priv *) i2c_get_clientdata(client); + + cancel_delayed_work_sync(&sma1303->check_fault_work); +} + +static const struct i2c_device_id sma1303_i2c_id[] = { + {"sma1303", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sma1303_i2c_id); + +static const struct of_device_id sma1303_of_match[] = { + { .compatible = "irondevice,sma1303", }, + { } +}; +MODULE_DEVICE_TABLE(of, sma1303_of_match); + +static struct i2c_driver sma1303_i2c_driver = { + .driver = { + .name = "sma1303", + .of_match_table = sma1303_of_match, + }, + .probe = sma1303_i2c_probe, + .remove = sma1303_i2c_remove, + .id_table = sma1303_i2c_id, +}; + +module_i2c_driver(sma1303_i2c_driver); + +MODULE_DESCRIPTION("ALSA SoC SMA1303 driver"); +MODULE_AUTHOR("Gyuhwa Park, "); +MODULE_AUTHOR("Kiseok Jo, "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/sma1303.h b/sound/soc/codecs/sma1303.h new file mode 100644 index 0000000000..ae70f207ad --- /dev/null +++ b/sound/soc/codecs/sma1303.h @@ -0,0 +1,609 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * sma1303.h -- sma1303 ALSA SoC Audio driver + * + * Copyright 2023 Iron Device Corporation + * + * Author: Kiseok Jo + * + */ + +#ifndef _SMA1303_H +#define _SMA1303_H + +#define SMA1303_I2C_ADDR_00 0x1e +#define SMA1303_I2C_ADDR_01 0x3e +#define SMA1303_I2C_ADDR_10 0x5e +#define SMA1303_I2C_ADDR_11 0x7e + +#define SMA1303_EXTERNAL_CLOCK_19_2 0x00 +#define SMA1303_EXTERNAL_CLOCK_24_576 0x01 +#define SMA1303_PLL_CLKIN_MCLK 0x02 +#define SMA1303_PLL_CLKIN_BCLK 0x03 + +#define SMA1303_MONO 0x00 +#define SMA1303_STEREO 0x01 + +#define SMA1303_I2C_RETRY_COUNT 3 + +/* + * SMA1303 Register Definition + */ + +/* SMA1303 Register Addresses */ +#define SMA1303_00_SYSTEM_CTRL 0x00 +#define SMA1303_01_INPUT1_CTRL1 0x01 +#define SMA1303_02_INPUT1_CTRL2 0x02 +#define SMA1303_03_INPUT1_CTRL3 0x03 +#define SMA1303_04_INPUT1_CTRL4 0x04 +/* 0x05 ~ 0x08 : Reserved */ +#define SMA1303_09_OUTPUT_CTRL 0x09 +#define SMA1303_0A_SPK_VOL 0x0a +#define SMA1303_0B_BST_TEST 0x0b +#define SMA1303_0C_BST_TEST1 0x0c +#define SMA1303_0D_SPK_TEST 0x0d +#define SMA1303_0E_MUTE_VOL_CTRL 0x0e +/* 0x0F : Reserved */ +#define SMA1303_10_SYSTEM_CTRL1 0x10 +#define SMA1303_11_SYSTEM_CTRL2 0x11 +#define SMA1303_12_SYSTEM_CTRL3 0x12 +/* 0x13 : Reserved */ +#define SMA1303_14_MODULATOR 0x14 +#define SMA1303_15_BASS_SPK1 0x15 +#define SMA1303_16_BASS_SPK2 0x16 +#define SMA1303_17_BASS_SPK3 0x17 +#define SMA1303_18_BASS_SPK4 0x18 +#define SMA1303_19_BASS_SPK5 0x19 +#define SMA1303_1A_BASS_SPK6 0x1a +#define SMA1303_1B_BASS_SPK7 0x1b +/* 0x1C ~ 0x22 : Reserved */ +#define SMA1303_23_COMP_LIM1 0x23 +#define SMA1303_24_COMP_LIM2 0x24 +#define SMA1303_25_COMP_LIM3 0x25 +#define SMA1303_26_COMP_LIM4 0x26 +/* 0x27 ~ 0x32 : Reserved */ +#define SMA1303_33_SDM_CTRL 0x33 +#define SMA1303_34_OTP_DATA1 0x34 +/* 0x35 : Reserved */ +#define SMA1303_36_PROTECTION 0x36 +#define SMA1303_37_SLOPE_CTRL 0x37 +#define SMA1303_38_OTP_TRM0 0x38 +/* 0x39 ~ 0x3A : Reserved */ +#define SMA1303_3B_TEST1 0x3b +#define SMA1303_3C_TEST2 0x3c +#define SMA1303_3D_TEST3 0x3d +#define SMA1303_3E_ATEST1 0x3e +#define SMA1303_3F_ATEST2 0x3f +/* 0x40 ~ 0x8A : Reserved */ +#define SMA1303_8B_PLL_POST_N 0x8b +#define SMA1303_8C_PLL_N 0x8c +#define SMA1303_8D_PLL_A_SETTING 0x8d +#define SMA1303_8E_PLL_CTRL 0x8e +#define SMA1303_8F_PLL_P_CP 0x8f +#define SMA1303_90_POSTSCALER 0x90 +#define SMA1303_91_CLASS_G_CTRL 0x91 +#define SMA1303_92_FDPEC_CTRL 0x92 +/* 0x93 : Reserved */ +#define SMA1303_94_BOOST_CTRL1 0x94 +#define SMA1303_95_BOOST_CTRL2 0x95 +#define SMA1303_96_BOOST_CTRL3 0x96 +#define SMA1303_97_BOOST_CTRL4 0x97 +/* 0x98 ~ 0x9F : Reserved */ +#define SMA1303_A0_PAD_CTRL0 0xa0 +#define SMA1303_A1_PAD_CTRL1 0xa1 +#define SMA1303_A2_TOP_MAN1 0xa2 +#define SMA1303_A3_TOP_MAN2 0xa3 +#define SMA1303_A4_TOP_MAN3 0xa4 +#define SMA1303_A5_TDM1 0xa5 +#define SMA1303_A6_TDM2 0xa6 +#define SMA1303_A7_CLK_MON 0xa7 +/* 0xA8 ~ 0xF9 : Reserved */ +#define SMA1303_FA_STATUS1 0xfa +#define SMA1303_FB_STATUS2 0xfb +/* 0xFC ~ 0xFE : Reserved */ +#define SMA1303_FF_DEVICE_INDEX 0xff + +/* SMA1303 Registers Bit Fields */ + +/* SYSTEM_CTRL : 0x00 */ +#define SMA1303_RESETBYI2C_MASK (1<<1) +#define SMA1303_RESETBYI2C_NORMAL (0<<1) +#define SMA1303_RESETBYI2C_RESET (1<<1) + +#define SMA1303_POWER_MASK (1<<0) +#define SMA1303_POWER_OFF (0<<0) +#define SMA1303_POWER_ON (1<<0) + +/* INTPUT CTRL1 : 0x01 */ +#define SMA1303_CONTROLLER_DEVICE_MASK (1<<7) +#define SMA1303_DEVICE_MODE (0<<7) +#define SMA1303_CONTROLLER_MODE (1<<7) + +#define SMA1303_I2S_MODE_MASK (7<<4) +#define SMA1303_STANDARD_I2S (0<<4) +#define SMA1303_LJ (1<<4) +#define SMA1303_RJ_16BIT (4<<4) +#define SMA1303_RJ_18BIT (5<<4) +#define SMA1303_RJ_20BIT (6<<4) +#define SMA1303_RJ_24BIT (7<<4) + +#define SMA1303_LEFTPOL_MASK (1<<3) +#define SMA1303_LOW_FIRST_CH (0<<3) +#define SMA1303_HIGH_FIRST_CH (1<<3) + +#define SMA1303_SCK_RISING_MASK (1<<2) +#define SMA1303_SCK_FALLING_EDGE (0<<2) +#define SMA1303_SCK_RISING_EDGE (1<<2) + +/* INTPUT CTRL2 : 0x02 */ +#define SMA1303_IMODE_MASK (3<<6) +#define SMA1303_I2S (0<<6) +#define SMA1303_PCM_SHORT (1<<6) +#define SMA1303_PCM_LONG (2<<6) + +#define RSMA1303_IGHT_FIRST_MASK (1<<5) +#define SMA1303_LEFT_NORMAL (0<<5) +#define SMA1303_RIGHT_INVERTED (1<<5) + +#define SMA1303_PCM_ALAW_MASK (1<<4) +#define SMA1303_PCM_U_DECODING (0<<4) +#define SMA1303_PCM_A_DECODING (1<<4) + +#define SMA1303_PCM_COMP_MASK (1<<3) +#define SMA1303_PCM_LINEAR (0<<3) +#define SMA1303_PCM_COMPANDING (1<<3) + +#define SMA1303_INPUTSEL_MASK (1<<2) +#define SMA1303_PCM_8KHZ (0<<2) +#define SMA1303_PCM_16KHZ (1<<2) + +#define SMA1303_PCM_STEREO_MASK (1<<1) +#define SMA1303_PCM_MONO (0<<1) +#define SMA1303_PCM_STEREO (1<<1) + +#define SMA1303_PCM_DL_MASK (1<<0) +#define SMA1303_PCM_8BIT (0<<0) +#define SMA1303_PCM_16BIT (1<<0) + +/* INTPUT CTRL3 : 0x03 */ +#define SMA1303_PCM_N_SLOT_MASK (15<<0) +#define SMA1303_PCM_N_SLOT1 (0<<0) +#define SMA1303_PCM_N_SLOT2 (1<<0) +#define SMA1303_PCM_N_SLOT3 (2<<0) +#define SMA1303_PCM_N_SLOT4 (3<<0) +#define SMA1303_PCM_N_SLOT5 (4<<0) +#define SMA1303_PCM_N_SLOT6 (5<<0) +#define SMA1303_PCM_N_SLOT7 (6<<0) +#define SMA1303_PCM_N_SLOT8 (7<<0) +#define SMA1303_PCM_N_SLOT9 (8<<0) +#define SMA1303_PCM_N_SLOT10 (9<<0) +#define SMA1303_PCM_N_SLOT11 (10<<0) +#define SMA1303_PCM_N_SLOT12 (11<<0) +#define SMA1303_PCM_N_SLOT13 (12<<0) +#define SMA1303_PCM_N_SLOT14 (13<<0) +#define SMA1303_PCM_N_SLOT15 (14<<0) +#define SMA1303_PCM_N_SLOT16 (15<<0) + +/* INTPUT CTRL4 : 0x04 */ +#define SMA1303_PCM1_SLOT_MASK (15<<4) +#define SMA1303_PCM1_SLOT1 (0<<4) +#define SMA1303_PCM1_SLOT2 (1<<4) +#define SMA1303_PCM1_SLOT3 (2<<4) +#define SMA1303_PCM1_SLOT4 (3<<4) +#define SMA1303_PCM1_SLOT5 (4<<4) +#define SMA1303_PCM1_SLOT6 (5<<4) +#define SMA1303_PCM1_SLOT7 (6<<4) +#define SMA1303_PCM1_SLOT8 (7<<4) +#define SMA1303_PCM1_SLOT9 (8<<4) +#define SMA1303_PCM1_SLOT10 (9<<4) +#define SMA1303_PCM1_SLOT11 (10<<4) +#define SMA1303_PCM1_SLOT12 (11<<4) +#define SMA1303_PCM1_SLOT13 (12<<4) +#define SMA1303_PCM1_SLOT14 (13<<4) +#define SMA1303_PCM1_SLOT15 (14<<4) +#define SMA1303_PCM1_SLOT16 (15<<4) + +#define SMA1303_PCM2_SLOT_MASK (15<<0) +#define SMA1303_PCM2_SLOT1 (0<<0) +#define SMA1303_PCM2_SLOT2 (1<<0) +#define SMA1303_PCM2_SLOT3 (2<<0) +#define SMA1303_PCM2_SLOT4 (3<<0) +#define SMA1303_PCM2_SLOT5 (4<<0) +#define SMA1303_PCM2_SLOT6 (5<<0) +#define SMA1303_PCM2_SLOT7 (6<<0) +#define SMA1303_PCM2_SLOT8 (7<<0) +#define SMA1303_PCM2_SLOT9 (8<<0) +#define SMA1303_PCM2_SLOT10 (9<<0) +#define SMA1303_PCM2_SLOT11 (10<<0) +#define SMA1303_PCM2_SLOT12 (11<<0) +#define SMA1303_PCM2_SLOT13 (12<<0) +#define SMA1303_PCM2_SLOT14 (13<<0) +#define SMA1303_PCM2_SLOT15 (14<<0) +#define SMA1303_PCM2_SLOT16 (15<<0) + +/* OUTPUT CTRL : 0x09 */ +#define SMA1303_PORT_CONFIG_MASK (3<<5) +#define SMA1303_INPUT_PORT_ONLY (0<<5) +#define SMA1303_OUTPUT_PORT_ENABLE (2<<5) + +#define SMA1303_PORT_OUT_SEL_MASK (7<<0) +#define SMA1303_OUT_SEL_DISABLE (0<<0) +#define SMA1303_FORMAT_CONVERTER (1<<0) +#define SMA1303_MIXER_OUTPUT (2<<0) +#define SMA1303_SPEAKER_PATH (3<<0) +#define SMA1303_POSTSCALER_OUTPUT (4<<0) + +/* BST_TEST : 0x0B */ +#define SMA1303_BST_OFF_SLOPE_MASK (3<<6) +#define SMA1303_BST_OFF_SLOPE_6_7ns (0<<6) +#define SMA1303_BST_OFF_SLOPE_4_8ns (1<<6) +#define SMA1303_BST_OFF_SLOPE_2_6ns (2<<6) +#define SMA1303_BST_OFF_SLOPE_1_2ns (3<<6) + +#define SMA1303_OCP_TEST_MASK (1<<5) +#define SMA1303_OCP_NORMAL_MODE (0<<5) +#define SMA1303_OCP_TEST_MODE (1<<5) + +#define SMA1303_BST_FAST_LEBN_MASK (1<<4) +#define SMA1303_BST_SHORT_LEB (0<<4) +#define SMA1303_BST_LONG_LEB (1<<4) + +#define SMA1303_HIGH_PGAIN_MASK (1<<3) +#define SMA1303_NORMAL_P_GAIN (0<<3) +#define SMA1303_HIGH_P_GAIN (1<<3) + +#define SMA1303_VCOMP_MASK (1<<2) +#define SMA1303_VCOMP_NORMAL_MODE (0<<2) +#define SMA1303_VCOMP_V_MON_MODE (1<<2) + +#define SMA1303_PMOS_ON_MASK (1<<1) +#define SMA1303_PMOS_NORMAL_MODE (0<<1) +#define SMA1303_PMOS_TEST_MODE (1<<1) + +#define SMA1303_NMOS_ON_MASK (1<<0) +#define SMA1303_NMOS_NORMAL_MODE (0<<0) +#define SMA1303_NMOS_TEST_MODE (1<<0) + +/* BST_TEST1 : 0x0C */ +#define SMA1303_SET_OCP_H_MASK (3<<6) +#define SMA1303_HIGH_OCP_4_5_LVL (0<<6) +#define SMA1303_HIGH_OCP_3_2_LVL (1<<6) +#define SMA1303_HIGH_OCP_2_1_LVL (2<<6) +#define SMA1303_HIGH_OCP_0_9_LVL (3<<6) + +#define SMA1303_OCL_TEST_MASK (1<<5) +#define SMA1303_OCL_NORMAL_MODE (0<<5) +#define SMA1303_OCL_TEST_MODE (1<<5) + +#define SMA1303_LOOP_CHECK_MASK (1<<4) +#define SMA1303_BST_LOOP_NORMAL_MODE (0<<4) +#define SMA1303_BST_LOOP_CHECK_MODE (1<<4) + +#define SMA1303_EN_SH_PRT_MASK (1<<3) +#define SMA1303_EN_SH_PRT_DISABLE (0<<3) +#define SMA1303_EN_SH_PRT_ENABLE (1<<3) + +/* SPK_TEST : 0x0D */ +#define SMA1303_VREF_MON_MASK (1<<3) +#define SMA1303_VREF_NORMAL_MODE (0<<3) +#define SMA1303_VREF_V_MON_MODE (1<<3) + +#define SMA1303_SPK_OCP_DLYN_MASK (1<<2) +#define SMA1303_SPK_OCP_LONG_DELAY (0<<2) +#define SMA1303_SPK_OCP_NORMAL (1<<2) + +#define SMA1303_SPK_OFF_SLOPE_MASK (3<<0) +#define SMA1303_SPK_OFF_SLOPE_SLOW (0<<0) +#define SMA1303_SPK_OFF_SLOPE_FAST (3<<0) + +/* MUTE_VOL_CTRL : 0x0E */ +#define SMA1303_VOL_SLOPE_MASK (3<<6) +#define SMA1303_VOL_SLOPE_OFF (0<<6) +#define SMA1303_VOL_SLOPE_SLOW (1<<6) +#define SMA1303_VOL_SLOPE_MID (2<<6) +#define SMA1303_VOL_SLOPE_FAST (3<<6) + +#define SMA1303_MUTE_SLOPE_MASK (3<<4) +#define SMA1303_MUTE_SLOPE_OFF (0<<4) +#define SMA1303_MUTE_SLOPE_SLOW (1<<4) +#define SMA1303_MUTE_SLOPE_MID (2<<4) +#define SMA1303_MUTE_SLOPE_FAST (3<<4) + +#define SMA1303_SPK_MUTE_MASK (1<<0) +#define SMA1303_SPK_UNMUTE (0<<0) +#define SMA1303_SPK_MUTE (1<<0) + +/* SYSTEM_CTRL1 :0x10 */ +#define SMA1303_SPK_MODE_MASK (7<<2) +#define SMA1303_SPK_OFF (0<<2) +#define SMA1303_SPK_MONO (1<<2) +#define SMA1303_SPK_STEREO (4<<2) + +/* SYSTEM_CTRL2 : 0x11 */ +#define SMA1303_SPK_BS_MASK (1<<6) +#define SMA1303_SPK_BS_BYP (0<<6) +#define SMA1303_SPK_BS_EN (1<<6) +#define SMA1303_SPK_LIM_MASK (1<<5) +#define SMA1303_SPK_LIM_BYP (0<<5) +#define SMA1303_SPK_LIM_EN (1<<5) + +#define SMA1303_LR_DATA_SW_MASK (1<<4) +#define SMA1303_LR_DATA_SW_NORMAL (0<<4) +#define SMA1303_LR_DATA_SW_SWAP (1<<4) + +#define SMA1303_MONOMIX_MASK (1<<0) +#define SMA1303_MONOMIX_OFF (0<<0) +#define SMA1303_MONOMIX_ON (1<<0) + +/* SYSTEM_CTRL3 : 0x12 */ +#define SMA1303_INPUT_MASK (3<<6) +#define SMA1303_INPUT_0_DB (0<<6) +#define SMA1303_INPUT_M6_DB (1<<6) +#define SMA1303_INPUT_M12_DB (2<<6) +#define SMA1303_INPUT_INFI_DB (3<<6) +#define SMA1303_INPUT_R_MASK (3<<4) +#define SMA1303_INPUT_R_0_DB (0<<4) +#define SMA1303_INPUT_R_M6_DB (1<<4) +#define SMA1303_INPUT_R_M12_DB (2<<4) +#define SMA1303_INPUT_R_INFI_DB (3<<4) + +/* Modulator : 0x14 */ +#define SMA1303_SPK_HYSFB_MASK (3<<6) +#define SMA1303_HYSFB_625K (0<<6) +#define SMA1303_HYSFB_414K (1<<6) +#define SMA1303_HYSFB_297K (2<<6) +#define SMA1303_HYSFB_226K (3<<6) +#define SMA1303_SPK_BDELAY_MASK (63<<0) + +/* SDM CONTROL : 0x33 */ +#define SMA1303_SDM_Q_SEL_MASK (1<<2) +#define SMA1303_QUART_SEL_1_DIV_4 (0<<2) +#define SMA1303_QUART_SEL_1_DIV_8 (1<<2) + +/* OTP_DATA1 : 0x34 */ +#define SMA1303_OTP_LVL_MASK (1<<5) +#define SMA1303_OTP_LVL_NORMAL (0<<5) +#define SMA1303_OTP_LVL_LOW (1<<5) + +/* PROTECTION : 0x36 */ +#define SMA1303_EDGE_DIS_MASK (1<<7) +#define SMA1303_EDGE_DIS_ENABLE (0<<7) +#define SMA1303_EDGE_DIS_DISABLE (1<<7) + +#define SMA1303_SPK_OCP_DIS_MASK (1<<3) +#define SMA1303_SPK_OCP_ENABLE (0<<3) +#define SMA1303_SPK_OCP_DISABLE (1<<3) + +#define SMA1303_OCP_MODE_MASK (1<<2) +#define SMA1303_AUTO_RECOVER (0<<2) +#define SMA1303_SHUT_DOWN_PERMANENT (1<<2) + +#define SMA1303_OTP_MODE_MASK (3<<0) +#define SMA1303_OTP_MODE_DISABLE (0<<0) +#define SMA1303_IG_THR1_SHUT_THR2 (1<<0) +#define SMA1303_REC_THR1_SHUT_THR2 (2<<0) +#define SMA1303_SHUT_THR1_SHUT_THR2 (3<<0) + +/* TEST2 : 0x3C */ +#define SMA1303_SPK_HSDM_BP_MASK (1<<4) +#define SMA1303_SPK_HSDM_ENABLE (0<<4) +#define SMA1303_SPK_HSDM_BYPASS (1<<4) + +#define SMA1303_SDM_SYNC_DIS_MASK (1<<5) +#define SMA1303_SDM_SYNC_NORMAL (0<<5) +#define SMA1303_SDM_SYNC_DISABLE (1<<5) + +/* ATEST2 : 0x3F */ +#define SMA1303_SPK_OUT_FREQ_MASK (1<<2) +#define SMA1303_SPK_OUT_FREQ_360K (0<<2) +#define SMA1303_SPK_OUT_FREQ_410K (1<<2) + +#define SMA1303_LOW_POWER_MODE_MASK (1<<3) +#define SMA1303_LOW_POWER_MODE_DISABLE (0<<3) +#define SMA1303_LOW_POWER_MODE_ENABLE (1<<3) + +#define SMA1303_THERMAL_ADJUST_MASK (3<<5) +#define SMA1303_THERMAL_150_110 (0<<5) +#define SMA1303_THERMAL_160_120 (1<<5) +#define SMA1303_THERMAL_140_100 (2<<5) + +#define SMA1303_FAST_OFF_DRIVE_SPK_MASK (1<<0) +#define SMA1303_FAST_OFF_DRIVE_SPK_DISABLE (0<<0) +#define SMA1303_FAST_OFF_DRIVE_SPK_ENABLE (1<<0) + +/* PLL_CTRL : 0x8E */ +#define SMA1303_TRM_LVL_MASK (1<<4) +#define SMA1303_TRM_LVL_NORMAL (0<<4) +#define SMA1303_TRM_LVL_LOW (1<<4) + +#define SMA1303_LOW_OCL_MODE_MASK (1<<3) +#define SMA1303_LOW_OCL_MODE (0<<3) +#define SMA1303_NORMAL_OCL_MODE (1<<3) + +#define SMA1303_PLL_PD2_MASK (7<<0) +#define SMA1303_PLL_PD2 (7<<0) +#define SMA1303_PLL_OPERATION2 (0<<0) + +/* POSTSCALER : 0x90 */ +#define SMA1303_BYP_POST_MASK (1<<0) +#define SMA1303_EN_POST_SCALER (0<<0) +#define SMA1303_BYP_POST_SCALER (1<<0) + +/* FDPEC CONTROL : 0x92 */ +#define SMA1303_FLT_VDD_GAIN_MASK (15<<4) +#define SMA1303_FLT_VDD_GAIN_2P40 (0<<4) +#define SMA1303_FLT_VDD_GAIN_2P45 (1<<4) +#define SMA1303_FLT_VDD_GAIN_2P50 (2<<4) +#define SMA1303_FLT_VDD_GAIN_2P55 (3<<4) +#define SMA1303_FLT_VDD_GAIN_2P60 (4<<4) +#define SMA1303_FLT_VDD_GAIN_2P65 (5<<4) +#define SMA1303_FLT_VDD_GAIN_2P70 (6<<4) +#define SMA1303_FLT_VDD_GAIN_2P75 (7<<4) +#define SMA1303_FLT_VDD_GAIN_2P80 (8<<4) +#define SMA1303_FLT_VDD_GAIN_2P85 (9<<4) +#define SMA1303_FLT_VDD_GAIN_2P90 (10<<4) +#define SMA1303_FLT_VDD_GAIN_2P95 (11<<4) +#define SMA1303_FLT_VDD_GAIN_3P00 (12<<4) +#define SMA1303_FLT_VDD_GAIN_3P05 (13<<4) +#define SMA1303_FLT_VDD_GAIN_3P10 (14<<4) +#define SMA1303_FLT_VDD_GAIN_3P15 (15<<4) + +#define SMA1303_DIS_FCHG_MASK (1<<2) +#define SMA1303_EN_FAST_CHARGE (0<<2) +#define SMA1303_DIS_FAST_CHARGE (1<<2) + +/* BOOST_CONTROL4 : 0x97 */ +#define SMA1303_TRM_VBST_MASK (7<<2) +#define SMA1303_TRM_VBST_5P5 (0<<2) +#define SMA1303_TRM_VBST_5P6 (1<<2) +#define SMA1303_TRM_VBST_5P7 (2<<2) +#define SMA1303_TRM_VBST_5P8 (3<<2) +#define SMA1303_TRM_VBST_5P9 (4<<2) +#define SMA1303_TRM_VBST_6P0 (5<<2) +#define SMA1303_TRM_VBST_6P1 (6<<2) +#define SMA1303_TRM_VBST_6P2 (7<<2) + +/* TOP_MAN1 : 0xA2 */ +#define SMA1303_PLL_LOCK_SKIP_MASK (1<<7) +#define SMA1303_PLL_LOCK_ENABLE (0<<7) +#define SMA1303_PLL_LOCK_DISABLE (1<<7) + +#define SMA1303_PLL_PD_MASK (1<<6) +#define SMA1303_PLL_OPERATION (0<<6) +#define SMA1303_PLL_PD (1<<6) + +#define SMA1303_PLL_DIV_MASK (3<<4) +#define SMA1303_PLL_OUT (0<<4) +#define SMA1303_PLL_OUT_2 (1<<4) +#define SMA1303_PLL_OUT_4 (2<<4) +#define SMA1303_PLL_OUT_8 (3<<4) + +#define SMA1303_PLL_REF_CLK_MASK (1<<3) +#define SMA1303_PLL_REF_CLK1 (0<<3) +#define SMA1303_PLL_SCK (1<<3) + +#define SMA1303_DAC_DN_CONV_MASK (1<<2) +#define SMA1303_DAC_DN_CONV_DISABLE (0<<2) +#define SMA1303_DAC_DN_CONV_ENABLE (1<<2) + +#define SMA1303_SDO_IO_MASK (1<<1) +#define SMA1303_HIGH_Z_LRCK_H (0<<1) +#define SMA1303_HIGH_Z_LRCK_L (1<<1) + +#define SMA1303_SDO_OUTPUT2_MASK (1<<0) +#define SMA1303_SDO_NORMAL (0<<0) +#define SMA1303_SDO_OUTPUT_ONLY (1<<0) + +/* TOP_MAN2 : 0xA3 */ +#define SMA1303_MON_OSC_PLL_MASK (1<<7) +#define SMA1303_PLL_SDO (0<<7) +#define SMA1303_OSC_SDO (1<<7) + +#define SMA1303_TEST_CLKO_EN_MASK (1<<6) +#define SMA1303_NORMAL_SDO (0<<6) +#define SMA1303_CLK_OUT_SDO (1<<6) + +#define SMA1303_SDO_OUTPUT_MASK (1<<3) +#define SMA1303_NORMAL_OUT (0<<3) +#define SMA1303_HIGH_Z_OUT (1<<3) + +#define SMA1303_CLOCK_MON_MASK (1<<1) +#define SMA1303_CLOCK_MON (0<<1) +#define SMA1303_CLOCK_NOT_MON (1<<1) + +#define SMA1303_OSC_PD_MASK (1<<0) +#define SMA1303_NORMAL_OPERATION_OSC (0<<0) +#define SMA1303_POWER_DOWN_OSC (1<<0) + +/* TOP_MAN3 0xA4 */ +#define SMA1303_O_FORMAT_MASK (7<<5) +#define SMA1303_O_FMT_LJ (1<<5) +#define SMA1303_O_FMT_I2S (2<<5) +#define SMA1303_O_FMT_TDM (4<<5) + +#define SMA1303_SCK_RATE_MASK (1<<3) +#define SMA1303_SCK_64FS (0<<3) +#define SMA1303_SCK_32FS (2<<3) + +#define SMA1303_LRCK_POL_MASK (1<<0) +#define SMA1303_L_VALID (0<<0) +#define SMA1303_R_VALID (1<<0) + +/* TDM1 FORMAT : 0xA5 */ +#define SMA1303_TDM_CLK_POL_MASK (1<<7) +#define SMA1303_TDM_CLK_POL_RISE (0<<7) +#define SMA1303_TDM_CLK_POL_FALL (1<<7) + +#define SMA1303_TDM_TX_MODE_MASK (1<<6) +#define SMA1303_TDM_TX_MONO (0<<6) +#define SMA1303_TDM_TX_STEREO (1<<6) + +#define SMA1303_TDM_SLOT1_RX_POS_MASK (7<<3) +#define SMA1303_TDM_SLOT1_RX_POS_0 (0<<3) +#define SMA1303_TDM_SLOT1_RX_POS_1 (1<<3) +#define SMA1303_TDM_SLOT1_RX_POS_2 (2<<3) +#define SMA1303_TDM_SLOT1_RX_POS_3 (3<<3) +#define SMA1303_TDM_SLOT1_RX_POS_4 (4<<3) +#define SMA1303_TDM_SLOT1_RX_POS_5 (5<<3) +#define SMA1303_TDM_SLOT1_RX_POS_6 (6<<3) +#define SMA1303_TDM_SLOT1_RX_POS_7 (7<<3) + +#define SMA1303_TDM_SLOT2_RX_POS_MASK (7<<0) +#define SMA1303_TDM_SLOT2_RX_POS_0 (0<<0) +#define SMA1303_TDM_SLOT2_RX_POS_1 (1<<0) +#define SMA1303_TDM_SLOT2_RX_POS_2 (2<<0) +#define SMA1303_TDM_SLOT2_RX_POS_3 (3<<0) +#define SMA1303_TDM_SLOT2_RX_POS_4 (4<<0) +#define SMA1303_TDM_SLOT2_RX_POS_5 (5<<0) +#define SMA1303_TDM_SLOT2_RX_POS_6 (6<<0) +#define SMA1303_TDM_SLOT2_RX_POS_7 (7<<0) + +/* TDM2 FORMAT : 0xA6 */ +#define SMA1303_TDM_DL_MASK (1<<7) +#define SMA1303_TDM_DL_16 (0<<7) +#define SMA1303_TDM_DL_32 (1<<7) + +#define SMA1303_TDM_N_SLOT_MASK (1<<6) +#define SMA1303_TDM_N_SLOT_4 (0<<6) +#define SMA1303_TDM_N_SLOT_8 (1<<6) + +#define SMA1303_TDM_SLOT1_TX_POS_MASK (7<<3) +#define SMA1303_TDM_SLOT1_TX_POS_0 (0<<3) +#define SMA1303_TDM_SLOT1_TX_POS_1 (1<<3) +#define SMA1303_TDM_SLOT1_TX_POS_2 (2<<3) +#define SMA1303_TDM_SLOT1_TX_POS_3 (3<<3) +#define SMA1303_TDM_SLOT1_TX_POS_4 (4<<3) +#define SMA1303_TDM_SLOT1_TX_POS_5 (5<<3) +#define SMA1303_TDM_SLOT1_TX_POS_6 (6<<3) +#define SMA1303_TDM_SLOT1_TX_POS_7 (7<<3) + +#define SMA1303_TDM_SLOT2_TX_POS_MASK (7<<0) +#define SMA1303_TDM_SLOT2_TX_POS_0 (0<<0) +#define SMA1303_TDM_SLOT2_TX_POS_1 (1<<0) +#define SMA1303_TDM_SLOT2_TX_POS_2 (2<<0) +#define SMA1303_TDM_SLOT2_TX_POS_3 (3<<0) +#define SMA1303_TDM_SLOT2_TX_POS_4 (4<<0) +#define SMA1303_TDM_SLOT2_TX_POS_5 (5<<0) +#define SMA1303_TDM_SLOT2_TX_POS_6 (6<<0) +#define SMA1303_TDM_SLOT2_TX_POS_7 (7<<0) + +/* STATUS1 : 0xFA */ +#define SMA1303_OT1_OK_STATUS (1<<7) +#define SMA1303_OT2_OK_STATUS (1<<6) + +/* STATUS2 : 0xFB */ +#define SMA1303_OCP_SPK_STATUS (1<<5) +#define SMA1303_OCP_BST_STATUS (1<<4) +#define SMA1303_OTP_STAT_OK_0 (5<<1) +#define SMA1303_OTP_STAT_OK_1 (2<<2) + +#define SMA1303_CLK_MON_STATUS (1<<0) + +/* DEVICE_INFO : 0xFF */ +#define SMA1303_DEVICE_ID (2<<3) +#define SMA1303_UVLO_BST_STATUS (1<<2) +#define SMA1303_REV_NUM_STATUS (3<<0) +#define SMA1303_REV_NUM_TV0 (0<<0) +#define SMA1303_REV_NUM_TV1 (1<<0) + +#endif diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c new file mode 100644 index 0000000000..862e0b654a --- /dev/null +++ b/sound/soc/codecs/spdif_receiver.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC SPDIF DIR (Digital Interface Reciever) driver + * + * Based on ALSA SoC SPDIF DIT driver + * + * This driver is used by controllers which can operate in DIR (SPDI/F) where + * no codec is needed. This file provides stub codec that can be used + * in these configurations. SPEAr SPDIF IN Audio controller uses this driver. + * + * Author: Vipin Kumar, + * Copyright: (C) 2012 ST Microelectronics + */ + +#include +#include +#include +#include +#include +#include +#include + +static const struct snd_soc_dapm_widget dir_widgets[] = { + SND_SOC_DAPM_INPUT("spdif-in"), +}; + +static const struct snd_soc_dapm_route dir_routes[] = { + { "Capture", NULL, "spdif-in" }, +}; + +#define STUB_RATES SNDRV_PCM_RATE_8000_192000 +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static struct snd_soc_component_driver soc_codec_spdif_dir = { + .dapm_widgets = dir_widgets, + .num_dapm_widgets = ARRAY_SIZE(dir_widgets), + .dapm_routes = dir_routes, + .num_dapm_routes = ARRAY_SIZE(dir_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct snd_soc_dai_driver dir_stub_dai = { + .name = "dir-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, +}; + +static int spdif_dir_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_codec_spdif_dir, + &dir_stub_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id spdif_dir_dt_ids[] = { + { .compatible = "linux,spdif-dir", }, + { } +}; +MODULE_DEVICE_TABLE(of, spdif_dir_dt_ids); +#endif + +static struct platform_driver spdif_dir_driver = { + .probe = spdif_dir_probe, + .driver = { + .name = "spdif-dir", + .of_match_table = of_match_ptr(spdif_dir_dt_ids), + }, +}; + +module_platform_driver(spdif_dir_driver); + +MODULE_DESCRIPTION("ASoC SPDIF DIR driver"); +MODULE_AUTHOR("Vipin Kumar "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c new file mode 100644 index 0000000000..7365189215 --- /dev/null +++ b/sound/soc/codecs/spdif_transmitter.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC SPDIF DIT driver + * + * This driver is used by controllers which can operate in DIT (SPDI/F) where + * no codec is needed. This file provides stub codec that can be used + * in these configurations. TI DaVinci Audio controller uses this driver. + * + * Author: Steve Chen, + * Copyright: (C) 2009 MontaVista Software, Inc., + * Copyright: (C) 2009 Texas Instruments, India + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "spdif-dit" + +#define STUB_RATES SNDRV_PCM_RATE_8000_192000 +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dapm_widget dit_widgets[] = { + SND_SOC_DAPM_OUTPUT("spdif-out"), +}; + +static const struct snd_soc_dapm_route dit_routes[] = { + { "spdif-out", NULL, "Playback" }, +}; + +static struct snd_soc_component_driver soc_codec_spdif_dit = { + .dapm_widgets = dit_widgets, + .num_dapm_widgets = ARRAY_SIZE(dit_widgets), + .dapm_routes = dit_routes, + .num_dapm_routes = ARRAY_SIZE(dit_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct snd_soc_dai_driver dit_stub_dai = { + .name = "dit-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, +}; + +static int spdif_dit_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_codec_spdif_dit, + &dit_stub_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id spdif_dit_dt_ids[] = { + { .compatible = "linux,spdif-dit", }, + { } +}; +MODULE_DEVICE_TABLE(of, spdif_dit_dt_ids); +#endif + +static struct platform_driver spdif_dit_driver = { + .probe = spdif_dit_probe, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(spdif_dit_dt_ids), + }, +}; + +module_platform_driver(spdif_dit_driver); + +MODULE_AUTHOR("Steve Chen "); +MODULE_DESCRIPTION("SPDIF dummy codec driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c new file mode 100644 index 0000000000..93af8e209b --- /dev/null +++ b/sound/soc/codecs/src4xxx-i2c.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for SRC4XXX codecs +// +// Copyright 2021-2022 Deqx Pty Ltd +// Author: Matt Flax + +#include +#include +#include +#include + +#include "src4xxx.h" + +static int src4xxx_i2c_probe(struct i2c_client *i2c) +{ + return src4xxx_probe(&i2c->dev, + devm_regmap_init_i2c(i2c, &src4xxx_regmap_config), NULL); +} + +static const struct i2c_device_id src4xxx_i2c_ids[] = { + { "src4392", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, src4xxx_i2c_ids); + +static const struct of_device_id src4xxx_of_match[] __maybe_unused = { + { .compatible = "ti,src4392", }, + { } +}; +MODULE_DEVICE_TABLE(of, src4xxx_of_match); + + +static struct i2c_driver src4xxx_i2c_driver = { + .driver = { + .name = "src4xxx", + .of_match_table = of_match_ptr(src4xxx_of_match), + }, + .probe = src4xxx_i2c_probe, + .id_table = src4xxx_i2c_ids, +}; +module_i2c_driver(src4xxx_i2c_driver); + +MODULE_DESCRIPTION("ASoC SRC4392 CODEC I2C driver"); +MODULE_AUTHOR("Matt Flax "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/src4xxx.c b/sound/soc/codecs/src4xxx.c new file mode 100644 index 0000000000..db4e280dd0 --- /dev/null +++ b/sound/soc/codecs/src4xxx.c @@ -0,0 +1,518 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// TI SRC4xxx Audio Codec driver +// +// Copyright 2021-2022 Deqx Pty Ltd +// Author: Matt Flax + +#include + +#include +#include + +#include "src4xxx.h" + +struct src4xxx { + struct regmap *regmap; + bool master[2]; + int mclk_hz; + struct device *dev; +}; + +enum {SRC4XXX_PORTA, SRC4XXX_PORTB}; + +/* SRC attenuation */ +static const DECLARE_TLV_DB_SCALE(src_tlv, -12750, 50, 0); + +static const struct snd_kcontrol_new src4xxx_controls[] = { + SOC_DOUBLE_R_TLV("SRC Volume", + SRC4XXX_SCR_CTL_30, SRC4XXX_SCR_CTL_31, 0, 255, 1, src_tlv), +}; + +/* I2S port control */ +static const char * const port_out_src_text[] = { + "loopback", "other_port", "DIR", "SRC" +}; +static SOC_ENUM_SINGLE_DECL(porta_out_src_enum, SRC4XXX_PORTA_CTL_03, 4, + port_out_src_text); +static SOC_ENUM_SINGLE_DECL(portb_out_src_enum, SRC4XXX_PORTB_CTL_05, 4, + port_out_src_text); +static const struct snd_kcontrol_new porta_out_control = + SOC_DAPM_ENUM("Port A source select", porta_out_src_enum); +static const struct snd_kcontrol_new portb_out_control = + SOC_DAPM_ENUM("Port B source select", portb_out_src_enum); + +/* Digital audio transmitter control */ +static const char * const dit_mux_text[] = {"Port A", "Port B", "DIR", "SRC"}; +static SOC_ENUM_SINGLE_DECL(dit_mux_enum, SRC4XXX_TX_CTL_07, 3, dit_mux_text); +static const struct snd_kcontrol_new dit_mux_control = + SOC_DAPM_ENUM("DIT source", dit_mux_enum); + +/* SRC control */ +static const char * const src_in_text[] = {"Port A", "Port B", "DIR"}; +static SOC_ENUM_SINGLE_DECL(src_in_enum, SRC4XXX_SCR_CTL_2D, 0, src_in_text); +static const struct snd_kcontrol_new src_in_control = + SOC_DAPM_ENUM("SRC source select", src_in_enum); + +/* DIR control */ +static const char * const dir_in_text[] = {"Ch 1", "Ch 2", "Ch 3", "Ch 4"}; +static SOC_ENUM_SINGLE_DECL(dir_in_enum, SRC4XXX_RCV_CTL_0D, 0, dir_in_text); +static const struct snd_kcontrol_new dir_in_control = + SOC_DAPM_ENUM("Digital Input", dir_in_enum); + +static const struct snd_soc_dapm_widget src4xxx_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("loopback_A"), + SND_SOC_DAPM_INPUT("other_port_A"), + SND_SOC_DAPM_INPUT("DIR_A"), + SND_SOC_DAPM_INPUT("SRC_A"), + SND_SOC_DAPM_MUX("Port A source", + SND_SOC_NOPM, 0, 0, &porta_out_control), + + SND_SOC_DAPM_INPUT("loopback_B"), + SND_SOC_DAPM_INPUT("other_port_B"), + SND_SOC_DAPM_INPUT("DIR_B"), + SND_SOC_DAPM_INPUT("SRC_B"), + SND_SOC_DAPM_MUX("Port B source", + SND_SOC_NOPM, 0, 0, &portb_out_control), + + SND_SOC_DAPM_INPUT("Port_A"), + SND_SOC_DAPM_INPUT("Port_B"), + SND_SOC_DAPM_INPUT("DIR_"), + + /* Digital audio receivers and transmitters */ + SND_SOC_DAPM_OUTPUT("DIR_OUT"), + SND_SOC_DAPM_OUTPUT("SRC_OUT"), + SND_SOC_DAPM_MUX("DIT Out Src", SRC4XXX_PWR_RST_01, + SRC4XXX_ENABLE_DIT_SHIFT, 1, &dit_mux_control), + + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF_A_RX", "Playback A", 0, + SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_A_SHIFT, 1), + SND_SOC_DAPM_AIF_OUT("AIF_A_TX", "Capture A", 0, + SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_A_SHIFT, 1), + SND_SOC_DAPM_AIF_IN("AIF_B_RX", "Playback B", 0, + SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_B_SHIFT, 1), + SND_SOC_DAPM_AIF_OUT("AIF_B_TX", "Capture B", 0, + SRC4XXX_PWR_RST_01, SRC4XXX_ENABLE_PORT_B_SHIFT, 1), + + SND_SOC_DAPM_MUX("SRC source", SND_SOC_NOPM, 0, 0, &src_in_control), + + SND_SOC_DAPM_INPUT("MCLK"), + SND_SOC_DAPM_INPUT("RXMCLKI"), + SND_SOC_DAPM_INPUT("RXMCLKO"), + + SND_SOC_DAPM_INPUT("RX1"), + SND_SOC_DAPM_INPUT("RX2"), + SND_SOC_DAPM_INPUT("RX3"), + SND_SOC_DAPM_INPUT("RX4"), + SND_SOC_DAPM_MUX("Digital Input", SRC4XXX_PWR_RST_01, + SRC4XXX_ENABLE_DIR_SHIFT, 1, &dir_in_control), +}; + +static const struct snd_soc_dapm_route src4xxx_audio_routes[] = { + /* I2S Input to Output Routing */ + {"Port A source", "loopback", "loopback_A"}, + {"Port A source", "other_port", "other_port_A"}, + {"Port A source", "DIR", "DIR_A"}, + {"Port A source", "SRC", "SRC_A"}, + {"Port B source", "loopback", "loopback_B"}, + {"Port B source", "other_port", "other_port_B"}, + {"Port B source", "DIR", "DIR_B"}, + {"Port B source", "SRC", "SRC_B"}, + /* DIT muxing */ + {"DIT Out Src", "Port A", "Capture A"}, + {"DIT Out Src", "Port B", "Capture B"}, + {"DIT Out Src", "DIR", "DIR_OUT"}, + {"DIT Out Src", "SRC", "SRC_OUT"}, + + /* SRC input selection */ + {"SRC source", "Port A", "Port_A"}, + {"SRC source", "Port B", "Port_B"}, + {"SRC source", "DIR", "DIR_"}, + /* SRC mclk selection */ + {"SRC mclk source", "Master (MCLK)", "MCLK"}, + {"SRC mclk source", "Master (RXCLKI)", "RXMCLKI"}, + {"SRC mclk source", "Recovered receiver clk", "RXMCLKO"}, + /* DIR input selection */ + {"Digital Input", "Ch 1", "RX1"}, + {"Digital Input", "Ch 2", "RX2"}, + {"Digital Input", "Ch 3", "RX3"}, + {"Digital Input", "Ch 4", "RX4"}, +}; + + +static const struct snd_soc_component_driver src4xxx_driver = { + .controls = src4xxx_controls, + .num_controls = ARRAY_SIZE(src4xxx_controls), + + .dapm_widgets = src4xxx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(src4xxx_dapm_widgets), + .dapm_routes = src4xxx_audio_routes, + .num_dapm_routes = ARRAY_SIZE(src4xxx_audio_routes), +}; + +static int src4xxx_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct src4xxx *src4xxx = snd_soc_component_get_drvdata(component); + unsigned int ctrl; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl = SRC4XXX_BUS_MASTER; + src4xxx->master[dai->id] = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + ctrl = 0; + src4xxx->master[dai->id] = false; + break; + default: + return -EINVAL; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl |= SRC4XXX_BUS_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl |= SRC4XXX_BUS_LEFT_J; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl |= SRC4XXX_BUS_RIGHT_J_24; + break; + default: + return -EINVAL; + break; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + break; + } + + regmap_update_bits(src4xxx->regmap, SRC4XXX_BUS_FMT(dai->id), + SRC4XXX_BUS_FMT_MS_MASK, ctrl); + + return 0; +} + +static int src4xxx_set_mclk_hz(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct src4xxx *src4xxx = snd_soc_component_get_drvdata(component); + + dev_info(component->dev, "changing mclk rate from %d to %d Hz\n", + src4xxx->mclk_hz, freq); + src4xxx->mclk_hz = freq; + + return 0; +} + +static int src4xxx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct src4xxx *src4xxx = snd_soc_component_get_drvdata(component); + unsigned int mclk_div; + int val, pj, jd, d; + int reg; + int ret; + + switch (dai->id) { + case SRC4XXX_PORTB: + reg = SRC4XXX_PORTB_CTL_06; + break; + default: + reg = SRC4XXX_PORTA_CTL_04; + break; + } + + if (src4xxx->master[dai->id]) { + mclk_div = src4xxx->mclk_hz/params_rate(params); + if (src4xxx->mclk_hz != mclk_div*params_rate(params)) { + dev_err(component->dev, + "mclk %d / rate %d has a remainder.\n", + src4xxx->mclk_hz, params_rate(params)); + return -EINVAL; + } + + val = ((int)mclk_div - 128) / 128; + if ((val < 0) | (val > 3)) { + dev_err(component->dev, + "div register setting %d is out of range\n", + val); + dev_err(component->dev, + "unsupported sample rate %d Hz for the master clock of %d Hz\n", + params_rate(params), src4xxx->mclk_hz); + return -EINVAL; + } + + /* set the TX DIV */ + ret = regmap_update_bits(src4xxx->regmap, + SRC4XXX_TX_CTL_07, SRC4XXX_TX_MCLK_DIV_MASK, + val<dev, + "Couldn't set the TX's div register to %d << %d = 0x%x\n", + val, SRC4XXX_TX_MCLK_DIV_SHIFT, + val<mclk_hz) { + case 24576000: + pj = 0x22; + jd = 0x00; + d = 0x00; + break; + case 22579200: + pj = 0x22; + jd = 0x1b; + d = 0xa3; + break; + default: + /* don't error out here, + * other parts of the chip are still functional + * Dummy initialize variables to avoid + * -Wsometimes-uninitialized from clang. + */ + dev_info(component->dev, + "Couldn't set the RCV PLL as this master clock rate is unknown. Chosen regmap values may not match real world values.\n"); + pj = 0x0; + jd = 0xff; + d = 0xff; + break; + } + ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_0F, pj); + if (ret < 0) + dev_err(component->dev, + "Failed to update PLL register 0x%x\n", + SRC4XXX_RCV_PLL_0F); + ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_10, jd); + if (ret < 0) + dev_err(component->dev, + "Failed to update PLL register 0x%x\n", + SRC4XXX_RCV_PLL_10); + ret = regmap_write(src4xxx->regmap, SRC4XXX_RCV_PLL_11, d); + if (ret < 0) + dev_err(component->dev, + "Failed to update PLL register 0x%x\n", + SRC4XXX_RCV_PLL_11); + + ret = regmap_update_bits(src4xxx->regmap, + SRC4XXX_TX_CTL_07, SRC4XXX_TX_MCLK_DIV_MASK, + val<dev, + "Couldn't set the TX's div register to %d << %d = 0x%x\n", + val, SRC4XXX_TX_MCLK_DIV_SHIFT, + val<regmap, reg, + SRC4XXX_MCLK_DIV_MASK, val); + } else { + dev_info(dai->dev, "not setting up MCLK as not master\n"); + } + + return 0; +}; + +static const struct snd_soc_dai_ops src4xxx_dai_ops = { + .hw_params = src4xxx_hw_params, + .set_sysclk = src4xxx_set_mclk_hz, + .set_fmt = src4xxx_set_dai_fmt, +}; + +#define SRC4XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) +#define SRC4XXX_RATES (SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000|\ + SNDRV_PCM_RATE_88200|\ + SNDRV_PCM_RATE_96000|\ + SNDRV_PCM_RATE_176400|\ + SNDRV_PCM_RATE_192000) + +static struct snd_soc_dai_driver src4xxx_dai_driver[] = { + { + .id = SRC4XXX_PORTA, + .name = "src4xxx-portA", + .playback = { + .stream_name = "Playback A", + .channels_min = 2, + .channels_max = 2, + .rates = SRC4XXX_RATES, + .formats = SRC4XXX_FORMATS, + }, + .capture = { + .stream_name = "Capture A", + .channels_min = 2, + .channels_max = 2, + .rates = SRC4XXX_RATES, + .formats = SRC4XXX_FORMATS, + }, + .ops = &src4xxx_dai_ops, + }, + { + .id = SRC4XXX_PORTB, + .name = "src4xxx-portB", + .playback = { + .stream_name = "Playback B", + .channels_min = 2, + .channels_max = 2, + .rates = SRC4XXX_RATES, + .formats = SRC4XXX_FORMATS, + }, + .capture = { + .stream_name = "Capture B", + .channels_min = 2, + .channels_max = 2, + .rates = SRC4XXX_RATES, + .formats = SRC4XXX_FORMATS, + }, + .ops = &src4xxx_dai_ops, + }, +}; + +static const struct reg_default src4xxx_reg_defaults[] = { + { SRC4XXX_PWR_RST_01, 0x00 }, /* all powered down intially */ + { SRC4XXX_PORTA_CTL_03, 0x00 }, + { SRC4XXX_PORTA_CTL_04, 0x00 }, + { SRC4XXX_PORTB_CTL_05, 0x00 }, + { SRC4XXX_PORTB_CTL_06, 0x00 }, + { SRC4XXX_TX_CTL_07, 0x00 }, + { SRC4XXX_TX_CTL_08, 0x00 }, + { SRC4XXX_TX_CTL_09, 0x00 }, + { SRC4XXX_SRC_DIT_IRQ_MSK_0B, 0x00 }, + { SRC4XXX_SRC_DIT_IRQ_MODE_0C, 0x00 }, + { SRC4XXX_RCV_CTL_0D, 0x00 }, + { SRC4XXX_RCV_CTL_0E, 0x00 }, + { SRC4XXX_RCV_PLL_0F, 0x00 }, /* not spec. in the datasheet */ + { SRC4XXX_RCV_PLL_10, 0xff }, /* not spec. in the datasheet */ + { SRC4XXX_RCV_PLL_11, 0xff }, /* not spec. in the datasheet */ + { SRC4XXX_RVC_IRQ_MSK_16, 0x00 }, + { SRC4XXX_RVC_IRQ_MSK_17, 0x00 }, + { SRC4XXX_RVC_IRQ_MODE_18, 0x00 }, + { SRC4XXX_RVC_IRQ_MODE_19, 0x00 }, + { SRC4XXX_RVC_IRQ_MODE_1A, 0x00 }, + { SRC4XXX_GPIO_1_1B, 0x00 }, + { SRC4XXX_GPIO_2_1C, 0x00 }, + { SRC4XXX_GPIO_3_1D, 0x00 }, + { SRC4XXX_GPIO_4_1E, 0x00 }, + { SRC4XXX_SCR_CTL_2D, 0x00 }, + { SRC4XXX_SCR_CTL_2E, 0x00 }, + { SRC4XXX_SCR_CTL_2F, 0x00 }, + { SRC4XXX_SCR_CTL_30, 0x00 }, + { SRC4XXX_SCR_CTL_31, 0x00 }, +}; + +int src4xxx_probe(struct device *dev, struct regmap *regmap, + void (*switch_mode)(struct device *dev)) +{ + struct src4xxx *src4xxx; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + src4xxx = devm_kzalloc(dev, sizeof(*src4xxx), GFP_KERNEL); + if (!src4xxx) + return -ENOMEM; + + src4xxx->regmap = regmap; + src4xxx->dev = dev; + src4xxx->mclk_hz = 0; /* mclk has not been configured yet */ + dev_set_drvdata(dev, src4xxx); + + ret = regmap_write(regmap, SRC4XXX_PWR_RST_01, SRC4XXX_RESET); + if (ret < 0) + dev_err(dev, "Failed to issue reset: %d\n", ret); + usleep_range(1, 500); /* sleep for more then 500 ns */ + ret = regmap_write(regmap, SRC4XXX_PWR_RST_01, SRC4XXX_POWER_DOWN); + if (ret < 0) + dev_err(dev, "Failed to decommission reset: %d\n", ret); + usleep_range(500, 1000); /* sleep for 500 us or more */ + + ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_PWR_RST_01, + SRC4XXX_POWER_ENABLE, SRC4XXX_POWER_ENABLE); + if (ret < 0) + dev_err(dev, "Failed to port A and B : %d\n", ret); + + /* set receiver to use master clock (rcv mclk is most likely jittery) */ + ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_RCV_CTL_0D, + SRC4XXX_RXCLK_MCLK, SRC4XXX_RXCLK_MCLK); + if (ret < 0) + dev_err(dev, + "Failed to enable mclk as the PLL1 DIR reference : %d\n", ret); + + /* default to leaving the PLL2 running on loss of lock, divide by 8 */ + ret = regmap_update_bits(src4xxx->regmap, SRC4XXX_RCV_CTL_0E, + SRC4XXX_PLL2_DIV_8 | SRC4XXX_REC_MCLK_EN | SRC4XXX_PLL2_LOL, + SRC4XXX_PLL2_DIV_8 | SRC4XXX_REC_MCLK_EN | SRC4XXX_PLL2_LOL); + if (ret < 0) + dev_err(dev, "Failed to enable mclk rec and div : %d\n", ret); + + ret = devm_snd_soc_register_component(dev, &src4xxx_driver, + src4xxx_dai_driver, ARRAY_SIZE(src4xxx_dai_driver)); + if (ret == 0) + dev_info(dev, "src4392 probe ok %d\n", ret); + return ret; +} +EXPORT_SYMBOL_GPL(src4xxx_probe); + +static bool src4xxx_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SRC4XXX_RES_00: + case SRC4XXX_GLOBAL_ITR_STS_02: + case SRC4XXX_SRC_DIT_STS_0A: + case SRC4XXX_NON_AUDIO_D_12: + case SRC4XXX_RVC_STS_13: + case SRC4XXX_RVC_STS_14: + case SRC4XXX_RVC_STS_15: + case SRC4XXX_SUB_CODE_1F: + case SRC4XXX_SUB_CODE_20: + case SRC4XXX_SUB_CODE_21: + case SRC4XXX_SUB_CODE_22: + case SRC4XXX_SUB_CODE_23: + case SRC4XXX_SUB_CODE_24: + case SRC4XXX_SUB_CODE_25: + case SRC4XXX_SUB_CODE_26: + case SRC4XXX_SUB_CODE_27: + case SRC4XXX_SUB_CODE_28: + case SRC4XXX_PC_PREAMBLE_HI_29: + case SRC4XXX_PC_PREAMBLE_LO_2A: + case SRC4XXX_PD_PREAMBLE_HI_2B: + case SRC4XXX_PC_PREAMBLE_LO_2C: + case SRC4XXX_IO_RATIO_32: + case SRC4XXX_IO_RATIO_33: + return true; + } + + if (reg > SRC4XXX_IO_RATIO_33 && reg < SRC4XXX_PAGE_SEL_7F) + return true; + + return false; +} + +const struct regmap_config src4xxx_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + .max_register = SRC4XXX_IO_RATIO_33, + + .reg_defaults = src4xxx_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(src4xxx_reg_defaults), + .volatile_reg = src4xxx_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(src4xxx_regmap_config); + +MODULE_DESCRIPTION("ASoC SRC4XXX CODEC driver"); +MODULE_AUTHOR("Matt Flax "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/src4xxx.h b/sound/soc/codecs/src4xxx.h new file mode 100644 index 0000000000..5bf778fb99 --- /dev/null +++ b/sound/soc/codecs/src4xxx.h @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// src4xxx.h -- SRC4XXX ALSA SoC audio driver +// +// Copyright 2021-2022 Deqx Pty Ltd +// Author: Matt R Flax + +#ifndef __SRC4XXX_H__ +#define __SRC4XXX_H__ + +#define SRC4XXX_RES_00 0x00 +#define SRC4XXX_PWR_RST_01 0x01 +#define SRC4XXX_RESET 0x80 +#define SRC4XXX_POWER_DOWN 0x00 +#define SRC4XXX_POWER_ENABLE 0x20 +#define SRC4XXX_ENABLE_SRC 0x1 +#define SRC4XXX_ENABLE_SRC_SHIFT 0 +#define SRC4XXX_ENABLE_DIR 0x2 +#define SRC4XXX_ENABLE_DIR_SHIFT 1 +#define SRC4XXX_ENABLE_DIT 0x4 +#define SRC4XXX_ENABLE_DIT_SHIFT 2 +#define SRC4XXX_ENABLE_PORT_B 0x8 +#define SRC4XXX_ENABLE_PORT_B_SHIFT 3 +#define SRC4XXX_ENABLE_PORT_A 0x10 +#define SRC4XXX_ENABLE_PORT_A_SHIFT 4 + +#define SRC4XXX_PORTA_CTL_03 0x03 +#define SRC4XXX_BUS_MASTER 0x8 +#define SRC4XXX_BUS_LEFT_J 0x0 +#define SRC4XXX_BUS_I2S 0x1 +#define SRC4XXX_BUS_RIGHT_J_16 0x4 +#define SRC4XXX_BUS_RIGHT_J_18 0x5 +#define SRC4XXX_BUS_RIGHT_J_20 0x6 +#define SRC4XXX_BUS_RIGHT_J_24 0x7 +#define SRC4XXX_BUS_FMT_MS_MASK 0xf + +#define SRC4XXX_PORTA_CTL_04 0x04 +#define SRC4XXX_MCLK_DIV_MASK 0x3 + +#define SRC4XXX_BUS_FMT(id) (SRC4XXX_PORTA_CTL_03+2*id) +#define SRC4XXX_BUS_CLK(id) (SRC4XXX_PORTA_CTL_04+2*id) + +#define SRC4XXX_PORTB_CTL_05 0x05 +#define SRC4XXX_PORTB_CTL_06 0x06 + +#define SRC4XXX_TX_CTL_07 0x07 +#define SRC4XXX_TX_MCLK_DIV_MASK 0x60 +#define SRC4XXX_TX_MCLK_DIV_SHIFT 5 + +#define SRC4XXX_TX_CTL_08 0x08 +#define SRC4XXX_TX_CTL_09 0x09 +#define SRC4XXX_SRC_DIT_IRQ_MSK_0B 0x0B +#define SRC4XXX_SRC_BTI_EN 0x01 +#define SRC4XXX_SRC_TSLIP_EN 0x02 +#define SRC4XXX_SRC_DIT_IRQ_MODE_0C 0x0C +#define SRC4XXX_RCV_CTL_0D 0x0D +#define SRC4XXX_RXCLK_RXCKI 0x0 +#define SRC4XXX_RXCLK_MCLK 0x8 +#define SRC4XXX_RCV_CTL_0E 0x0E +#define SRC4XXX_REC_MCLK_EN 0x1 +#define SRC4XXX_PLL2_DIV_0 (0x0<<1) +#define SRC4XXX_PLL2_DIV_2 (0x1<<1) +#define SRC4XXX_PLL2_DIV_4 (0x2<<1) +#define SRC4XXX_PLL2_DIV_8 (0x3<<1) +#define SRC4XXX_PLL2_LOL 0x8 +#define SRC4XXX_RCV_PLL_0F 0x0F +#define SRC4XXX_RCV_PLL_10 0x10 +#define SRC4XXX_RCV_PLL_11 0x11 +#define SRC4XXX_RVC_IRQ_MSK_16 0x16 +#define SRC4XXX_RVC_IRQ_MSK_17 0x17 +#define SRC4XXX_RVC_IRQ_MODE_18 0x18 +#define SRC4XXX_RVC_IRQ_MODE_19 0x19 +#define SRC4XXX_RVC_IRQ_MODE_1A 0x1A +#define SRC4XXX_GPIO_1_1B 0x1B +#define SRC4XXX_GPIO_2_1C 0x1C +#define SRC4XXX_GPIO_3_1D 0x1D +#define SRC4XXX_GPIO_4_1E 0x1E +#define SRC4XXX_SCR_CTL_2D 0x2D +#define SRC4XXX_SCR_CTL_2E 0x2E +#define SRC4XXX_SCR_CTL_2F 0x2F +#define SRC4XXX_SCR_CTL_30 0x30 +#define SRC4XXX_SCR_CTL_31 0x31 +#define SRC4XXX_PAGE_SEL_7F 0x7F + +// read only registers +#define SRC4XXX_GLOBAL_ITR_STS_02 0x02 +#define SRC4XXX_SRC_DIT_STS_0A 0x0A +#define SRC4XXX_NON_AUDIO_D_12 0x12 +#define SRC4XXX_RVC_STS_13 0x13 +#define SRC4XXX_RVC_STS_14 0x14 +#define SRC4XXX_RVC_STS_15 0x15 +#define SRC4XXX_SUB_CODE_1F 0x1F +#define SRC4XXX_SUB_CODE_20 0x20 +#define SRC4XXX_SUB_CODE_21 0x21 +#define SRC4XXX_SUB_CODE_22 0x22 +#define SRC4XXX_SUB_CODE_23 0x23 +#define SRC4XXX_SUB_CODE_24 0x24 +#define SRC4XXX_SUB_CODE_25 0x25 +#define SRC4XXX_SUB_CODE_26 0x26 +#define SRC4XXX_SUB_CODE_27 0x27 +#define SRC4XXX_SUB_CODE_28 0x28 +#define SRC4XXX_PC_PREAMBLE_HI_29 0x29 +#define SRC4XXX_PC_PREAMBLE_LO_2A 0x2A +#define SRC4XXX_PD_PREAMBLE_HI_2B 0x2B +#define SRC4XXX_PC_PREAMBLE_LO_2C 0x2C +#define SRC4XXX_IO_RATIO_32 0x32 +#define SRC4XXX_IO_RATIO_33 0x33 + +int src4xxx_probe(struct device *dev, struct regmap *regmap, + void (*switch_mode)(struct device *dev)); +extern const struct regmap_config src4xxx_regmap_config; + +#endif /* __SRC4XXX_H__ */ diff --git a/sound/soc/codecs/ssm2305.c b/sound/soc/codecs/ssm2305.c new file mode 100644 index 0000000000..1d022643c3 --- /dev/null +++ b/sound/soc/codecs/ssm2305.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Analog Devices SSM2305 Amplifier Driver +// +// Copyright (C) 2018 Pengutronix, Marco Felsch +// + +#include +#include +#include + +#define DRV_NAME "ssm2305" + +struct ssm2305 { + /* shutdown gpio */ + struct gpio_desc *gpiod_shutdown; +}; + +static int ssm2305_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kctrl, int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct ssm2305 *data = snd_soc_component_get_drvdata(c); + + gpiod_set_value_cansleep(data->gpiod_shutdown, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget ssm2305_dapm_widgets[] = { + /* Stereo input/output */ + SND_SOC_DAPM_INPUT("L_IN"), + SND_SOC_DAPM_INPUT("R_IN"), + SND_SOC_DAPM_OUTPUT("L_OUT"), + SND_SOC_DAPM_OUTPUT("R_OUT"), + + SND_SOC_DAPM_SUPPLY("Power", SND_SOC_NOPM, 0, 0, ssm2305_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route ssm2305_dapm_routes[] = { + { "L_OUT", NULL, "L_IN" }, + { "R_OUT", NULL, "R_IN" }, + { "L_IN", NULL, "Power" }, + { "R_IN", NULL, "Power" }, +}; + +static const struct snd_soc_component_driver ssm2305_component_driver = { + .dapm_widgets = ssm2305_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm2305_dapm_widgets), + .dapm_routes = ssm2305_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ssm2305_dapm_routes), +}; + +static int ssm2305_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ssm2305 *priv; + + /* Allocate the private data */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + /* Get shutdown gpio */ + priv->gpiod_shutdown = devm_gpiod_get(dev, "shutdown", + GPIOD_OUT_LOW); + if (IS_ERR(priv->gpiod_shutdown)) + return dev_err_probe(dev, PTR_ERR(priv->gpiod_shutdown), + "Failed to get 'shutdown' gpio\n"); + + return devm_snd_soc_register_component(dev, &ssm2305_component_driver, + NULL, 0); +} + +#ifdef CONFIG_OF +static const struct of_device_id ssm2305_of_match[] = { + { .compatible = "adi,ssm2305", }, + { } +}; +MODULE_DEVICE_TABLE(of, ssm2305_of_match); +#endif + +static struct platform_driver ssm2305_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(ssm2305_of_match), + }, + .probe = ssm2305_probe, +}; + +module_platform_driver(ssm2305_driver); + +MODULE_DESCRIPTION("ASoC SSM2305 amplifier driver"); +MODULE_AUTHOR("Marco Felsch "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c new file mode 100644 index 0000000000..d20d897407 --- /dev/null +++ b/sound/soc/codecs/ssm2518.c @@ -0,0 +1,813 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SSM2518 amplifier audio driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ssm2518.h" + +#define SSM2518_REG_POWER1 0x00 +#define SSM2518_REG_CLOCK 0x01 +#define SSM2518_REG_SAI_CTRL1 0x02 +#define SSM2518_REG_SAI_CTRL2 0x03 +#define SSM2518_REG_CHAN_MAP 0x04 +#define SSM2518_REG_LEFT_VOL 0x05 +#define SSM2518_REG_RIGHT_VOL 0x06 +#define SSM2518_REG_MUTE_CTRL 0x07 +#define SSM2518_REG_FAULT_CTRL 0x08 +#define SSM2518_REG_POWER2 0x09 +#define SSM2518_REG_DRC_1 0x0a +#define SSM2518_REG_DRC_2 0x0b +#define SSM2518_REG_DRC_3 0x0c +#define SSM2518_REG_DRC_4 0x0d +#define SSM2518_REG_DRC_5 0x0e +#define SSM2518_REG_DRC_6 0x0f +#define SSM2518_REG_DRC_7 0x10 +#define SSM2518_REG_DRC_8 0x11 +#define SSM2518_REG_DRC_9 0x12 + +#define SSM2518_POWER1_RESET BIT(7) +#define SSM2518_POWER1_NO_BCLK BIT(5) +#define SSM2518_POWER1_MCS_MASK (0xf << 1) +#define SSM2518_POWER1_MCS_64FS (0x0 << 1) +#define SSM2518_POWER1_MCS_128FS (0x1 << 1) +#define SSM2518_POWER1_MCS_256FS (0x2 << 1) +#define SSM2518_POWER1_MCS_384FS (0x3 << 1) +#define SSM2518_POWER1_MCS_512FS (0x4 << 1) +#define SSM2518_POWER1_MCS_768FS (0x5 << 1) +#define SSM2518_POWER1_MCS_100FS (0x6 << 1) +#define SSM2518_POWER1_MCS_200FS (0x7 << 1) +#define SSM2518_POWER1_MCS_400FS (0x8 << 1) +#define SSM2518_POWER1_SPWDN BIT(0) + +#define SSM2518_CLOCK_ASR BIT(0) + +#define SSM2518_SAI_CTRL1_FMT_MASK (0x3 << 5) +#define SSM2518_SAI_CTRL1_FMT_I2S (0x0 << 5) +#define SSM2518_SAI_CTRL1_FMT_LJ (0x1 << 5) +#define SSM2518_SAI_CTRL1_FMT_RJ_24BIT (0x2 << 5) +#define SSM2518_SAI_CTRL1_FMT_RJ_16BIT (0x3 << 5) + +#define SSM2518_SAI_CTRL1_SAI_MASK (0x7 << 2) +#define SSM2518_SAI_CTRL1_SAI_I2S (0x0 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_2 (0x1 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_4 (0x2 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_8 (0x3 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_16 (0x4 << 2) +#define SSM2518_SAI_CTRL1_SAI_MONO (0x5 << 2) + +#define SSM2518_SAI_CTRL1_FS_MASK (0x3) +#define SSM2518_SAI_CTRL1_FS_8000_12000 (0x0) +#define SSM2518_SAI_CTRL1_FS_16000_24000 (0x1) +#define SSM2518_SAI_CTRL1_FS_32000_48000 (0x2) +#define SSM2518_SAI_CTRL1_FS_64000_96000 (0x3) + +#define SSM2518_SAI_CTRL2_BCLK_INTERAL BIT(7) +#define SSM2518_SAI_CTRL2_LRCLK_PULSE BIT(6) +#define SSM2518_SAI_CTRL2_LRCLK_INVERT BIT(5) +#define SSM2518_SAI_CTRL2_MSB BIT(4) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK (0x3 << 2) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_32 (0x0 << 2) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_24 (0x1 << 2) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_16 (0x2 << 2) +#define SSM2518_SAI_CTRL2_BCLK_INVERT BIT(1) + +#define SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET 4 +#define SSM2518_CHAN_MAP_RIGHT_SLOT_MASK 0xf0 +#define SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET 0 +#define SSM2518_CHAN_MAP_LEFT_SLOT_MASK 0x0f + +#define SSM2518_MUTE_CTRL_ANA_GAIN BIT(5) +#define SSM2518_MUTE_CTRL_MUTE_MASTER BIT(0) + +#define SSM2518_POWER2_APWDN BIT(0) + +#define SSM2518_DAC_MUTE BIT(6) +#define SSM2518_DAC_FS_MASK 0x07 +#define SSM2518_DAC_FS_8000 0x00 +#define SSM2518_DAC_FS_16000 0x01 +#define SSM2518_DAC_FS_32000 0x02 +#define SSM2518_DAC_FS_64000 0x03 +#define SSM2518_DAC_FS_128000 0x04 + +struct ssm2518 { + struct regmap *regmap; + bool right_j; + + unsigned int sysclk; + const struct snd_pcm_hw_constraint_list *constraints; + + struct gpio_desc *enable_gpio; +}; + +static const struct reg_default ssm2518_reg_defaults[] = { + { 0x00, 0x05 }, + { 0x01, 0x00 }, + { 0x02, 0x02 }, + { 0x03, 0x00 }, + { 0x04, 0x10 }, + { 0x05, 0x40 }, + { 0x06, 0x40 }, + { 0x07, 0x81 }, + { 0x08, 0x0c }, + { 0x09, 0x99 }, + { 0x0a, 0x7c }, + { 0x0b, 0x5b }, + { 0x0c, 0x57 }, + { 0x0d, 0x89 }, + { 0x0e, 0x8c }, + { 0x0f, 0x77 }, + { 0x10, 0x26 }, + { 0x11, 0x1c }, + { 0x12, 0x97 }, +}; + +static const DECLARE_TLV_DB_MINMAX_MUTE(ssm2518_vol_tlv, -7125, 2400); +static const DECLARE_TLV_DB_SCALE(ssm2518_compressor_tlv, -3400, 200, 0); +static const DECLARE_TLV_DB_SCALE(ssm2518_expander_tlv, -8100, 300, 0); +static const DECLARE_TLV_DB_SCALE(ssm2518_noise_gate_tlv, -9600, 300, 0); +static const DECLARE_TLV_DB_SCALE(ssm2518_post_drc_tlv, -2400, 300, 0); + +static const DECLARE_TLV_DB_RANGE(ssm2518_limiter_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-2200, 200, 0), + 7, 15, TLV_DB_SCALE_ITEM(-800, 100, 0), +); + +static const char * const ssm2518_drc_peak_detector_attack_time_text[] = { + "0 ms", "0.1 ms", "0.19 ms", "0.37 ms", "0.75 ms", "1.5 ms", "3 ms", + "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", "192 ms", "384 ms", + "768 ms", "1536 ms", +}; + +static const char * const ssm2518_drc_peak_detector_release_time_text[] = { + "0 ms", "1.5 ms", "3 ms", "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", + "192 ms", "384 ms", "768 ms", "1536 ms", "3072 ms", "6144 ms", + "12288 ms", "24576 ms" +}; + +static const char * const ssm2518_drc_hold_time_text[] = { + "0 ms", "0.67 ms", "1.33 ms", "2.67 ms", "5.33 ms", "10.66 ms", + "21.32 ms", "42.64 ms", "85.28 ms", "170.56 ms", "341.12 ms", + "682.24 ms", "1364 ms", +}; + +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum, + SSM2518_REG_DRC_2, 4, ssm2518_drc_peak_detector_attack_time_text); +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum, + SSM2518_REG_DRC_2, 0, ssm2518_drc_peak_detector_release_time_text); +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum, + SSM2518_REG_DRC_6, 4, ssm2518_drc_peak_detector_attack_time_text); +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum, + SSM2518_REG_DRC_6, 0, ssm2518_drc_peak_detector_release_time_text); +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum, + SSM2518_REG_DRC_7, 4, ssm2518_drc_hold_time_text); +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum, + SSM2518_REG_DRC_7, 0, ssm2518_drc_hold_time_text); +static SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum, + SSM2518_REG_DRC_9, 0, ssm2518_drc_peak_detector_release_time_text); + +static const struct snd_kcontrol_new ssm2518_snd_controls[] = { + SOC_SINGLE("Playback De-emphasis Switch", SSM2518_REG_MUTE_CTRL, + 4, 1, 0), + SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2518_REG_LEFT_VOL, + SSM2518_REG_RIGHT_VOL, 0, 0xff, 1, ssm2518_vol_tlv), + SOC_DOUBLE("Master Playback Switch", SSM2518_REG_MUTE_CTRL, 2, 1, 1, 1), + + SOC_SINGLE("Amp Low Power Mode Switch", SSM2518_REG_POWER2, 4, 1, 0), + SOC_SINGLE("DAC Low Power Mode Switch", SSM2518_REG_POWER2, 3, 1, 0), + + SOC_SINGLE("DRC Limiter Switch", SSM2518_REG_DRC_1, 5, 1, 0), + SOC_SINGLE("DRC Compressor Switch", SSM2518_REG_DRC_1, 4, 1, 0), + SOC_SINGLE("DRC Expander Switch", SSM2518_REG_DRC_1, 3, 1, 0), + SOC_SINGLE("DRC Noise Gate Switch", SSM2518_REG_DRC_1, 2, 1, 0), + SOC_DOUBLE("DRC Switch", SSM2518_REG_DRC_1, 0, 1, 1, 0), + + SOC_SINGLE_TLV("DRC Limiter Threshold Volume", + SSM2518_REG_DRC_3, 4, 15, 1, ssm2518_limiter_tlv), + SOC_SINGLE_TLV("DRC Compressor Lower Threshold Volume", + SSM2518_REG_DRC_3, 0, 15, 1, ssm2518_compressor_tlv), + SOC_SINGLE_TLV("DRC Expander Upper Threshold Volume", SSM2518_REG_DRC_4, + 4, 15, 1, ssm2518_expander_tlv), + SOC_SINGLE_TLV("DRC Noise Gate Threshold Volume", + SSM2518_REG_DRC_4, 0, 15, 1, ssm2518_noise_gate_tlv), + SOC_SINGLE_TLV("DRC Upper Output Threshold Volume", + SSM2518_REG_DRC_5, 4, 15, 1, ssm2518_limiter_tlv), + SOC_SINGLE_TLV("DRC Lower Output Threshold Volume", + SSM2518_REG_DRC_5, 0, 15, 1, ssm2518_noise_gate_tlv), + SOC_SINGLE_TLV("DRC Post Volume", SSM2518_REG_DRC_8, + 2, 15, 1, ssm2518_post_drc_tlv), + + SOC_ENUM("DRC Peak Detector Attack Time", + ssm2518_drc_peak_detector_attack_time_enum), + SOC_ENUM("DRC Peak Detector Release Time", + ssm2518_drc_peak_detector_release_time_enum), + SOC_ENUM("DRC Attack Time", ssm2518_drc_attack_time_enum), + SOC_ENUM("DRC Decay Time", ssm2518_drc_decay_time_enum), + SOC_ENUM("DRC Hold Time", ssm2518_drc_hold_time_enum), + SOC_ENUM("DRC Noise Gate Hold Time", + ssm2518_drc_noise_gate_hold_time_enum), + SOC_ENUM("DRC RMS Averaging Time", ssm2518_drc_rms_averaging_time_enum), +}; + +static const struct snd_soc_dapm_widget ssm2518_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DACL", "HiFi Playback", SSM2518_REG_POWER2, 1, 1), + SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SSM2518_REG_POWER2, 2, 1), + + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static const struct snd_soc_dapm_route ssm2518_routes[] = { + { "OUTL", NULL, "DACL" }, + { "OUTR", NULL, "DACR" }, +}; + +struct ssm2518_mcs_lut { + unsigned int rate; + const unsigned int *sysclks; +}; + +static const unsigned int ssm2518_sysclks_2048000[] = { + 2048000, 4096000, 8192000, 12288000, 16384000, 24576000, + 3200000, 6400000, 12800000, 0 +}; + +static const unsigned int ssm2518_sysclks_2822000[] = { + 2822000, 5644800, 11289600, 16934400, 22579200, 33868800, + 4410000, 8820000, 17640000, 0 +}; + +static const unsigned int ssm2518_sysclks_3072000[] = { + 3072000, 6144000, 12288000, 16384000, 24576000, 38864000, + 4800000, 9600000, 19200000, 0 +}; + +static const struct ssm2518_mcs_lut ssm2518_mcs_lut[] = { + { 8000, ssm2518_sysclks_2048000, }, + { 11025, ssm2518_sysclks_2822000, }, + { 12000, ssm2518_sysclks_3072000, }, + { 16000, ssm2518_sysclks_2048000, }, + { 24000, ssm2518_sysclks_3072000, }, + { 22050, ssm2518_sysclks_2822000, }, + { 32000, ssm2518_sysclks_2048000, }, + { 44100, ssm2518_sysclks_2822000, }, + { 48000, ssm2518_sysclks_3072000, }, + { 96000, ssm2518_sysclks_3072000, }, +}; + +static const unsigned int ssm2518_rates_2048000[] = { + 8000, 16000, 32000, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2048000 = { + .list = ssm2518_rates_2048000, + .count = ARRAY_SIZE(ssm2518_rates_2048000), +}; + +static const unsigned int ssm2518_rates_2822000[] = { + 11025, 22050, 44100, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2822000 = { + .list = ssm2518_rates_2822000, + .count = ARRAY_SIZE(ssm2518_rates_2822000), +}; + +static const unsigned int ssm2518_rates_3072000[] = { + 12000, 24000, 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_3072000 = { + .list = ssm2518_rates_3072000, + .count = ARRAY_SIZE(ssm2518_rates_3072000), +}; + +static const unsigned int ssm2518_rates_12288000[] = { + 8000, 12000, 16000, 24000, 32000, 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = { + .list = ssm2518_rates_12288000, + .count = ARRAY_SIZE(ssm2518_rates_12288000), +}; + +static int ssm2518_lookup_mcs(struct ssm2518 *ssm2518, + unsigned int rate) +{ + const unsigned int *sysclks = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(ssm2518_mcs_lut); i++) { + if (ssm2518_mcs_lut[i].rate == rate) { + sysclks = ssm2518_mcs_lut[i].sysclks; + break; + } + } + + if (!sysclks) + return -EINVAL; + + for (i = 0; sysclks[i]; i++) { + if (sysclks[i] == ssm2518->sysclk) + return i; + } + + return -EINVAL; +} + +static int ssm2518_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component); + unsigned int rate = params_rate(params); + unsigned int ctrl1, ctrl1_mask; + int mcs; + int ret; + + mcs = ssm2518_lookup_mcs(ssm2518, rate); + if (mcs < 0) + return mcs; + + ctrl1_mask = SSM2518_SAI_CTRL1_FS_MASK; + + if (rate >= 8000 && rate <= 12000) + ctrl1 = SSM2518_SAI_CTRL1_FS_8000_12000; + else if (rate >= 16000 && rate <= 24000) + ctrl1 = SSM2518_SAI_CTRL1_FS_16000_24000; + else if (rate >= 32000 && rate <= 48000) + ctrl1 = SSM2518_SAI_CTRL1_FS_32000_48000; + else if (rate >= 64000 && rate <= 96000) + ctrl1 = SSM2518_SAI_CTRL1_FS_64000_96000; + else + return -EINVAL; + + if (ssm2518->right_j) { + switch (params_width(params)) { + case 16: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_16BIT; + break; + case 24: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT; + break; + default: + return -EINVAL; + } + ctrl1_mask |= SSM2518_SAI_CTRL1_FMT_MASK; + } + + /* Disable auto samplerate detection */ + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_CLOCK, + SSM2518_CLOCK_ASR, SSM2518_CLOCK_ASR); + if (ret < 0) + return ret; + + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, + ctrl1_mask, ctrl1); + if (ret < 0) + return ret; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_MCS_MASK, mcs << 1); +} + +static int ssm2518_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + if (mute) + val = SSM2518_MUTE_CTRL_MUTE_MASTER; + else + val = 0; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_MUTE_CTRL, + SSM2518_MUTE_CTRL_MUTE_MASTER, val); +} + +static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component); + unsigned int ctrl1 = 0, ctrl2 = 0; + bool invert_fclk; + int ret; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_fclk = false; + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT; + invert_fclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_fclk = true; + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT; + invert_fclk = true; + break; + default: + return -EINVAL; + } + + ssm2518->right_j = false; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ; + invert_fclk = !invert_fclk; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT; + ssm2518->right_j = true; + invert_fclk = !invert_fclk; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE; + ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S; + invert_fclk = false; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE; + ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ; + invert_fclk = false; + break; + default: + return -EINVAL; + } + + if (invert_fclk) + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_INVERT; + + ret = regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, ctrl1); + if (ret) + return ret; + + return regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, ctrl2); +} + +static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable) +{ + int ret = 0; + + if (!enable) { + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_SPWDN, SSM2518_POWER1_SPWDN); + regcache_mark_dirty(ssm2518->regmap); + } + + if (ssm2518->enable_gpio) + gpiod_set_value_cansleep(ssm2518->enable_gpio, enable); + + regcache_cache_only(ssm2518->regmap, !enable); + + if (enable) { + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_SPWDN | SSM2518_POWER1_RESET, 0x00); + regcache_sync(ssm2518->regmap); + } + + return ret; +} + +static int ssm2518_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + ret = ssm2518_set_power(ssm2518, true); + break; + case SND_SOC_BIAS_OFF: + ret = ssm2518_set_power(ssm2518, false); + break; + } + + return ret; +} + +static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component); + unsigned int ctrl1, ctrl2; + int left_slot, right_slot; + int ret; + + if (slots == 0) + return regmap_update_bits(ssm2518->regmap, + SSM2518_REG_SAI_CTRL1, SSM2518_SAI_CTRL1_SAI_MASK, + SSM2518_SAI_CTRL1_SAI_I2S); + + if (tx_mask == 0 || rx_mask != 0) + return -EINVAL; + + if (slots == 1) { + if (tx_mask != 1) + return -EINVAL; + left_slot = 0; + right_slot = 0; + } else { + /* We assume the left channel < right channel */ + left_slot = __ffs(tx_mask); + tx_mask &= ~(1 << left_slot); + if (tx_mask == 0) { + right_slot = left_slot; + } else { + right_slot = __ffs(tx_mask); + tx_mask &= ~(1 << right_slot); + } + } + + if (tx_mask != 0 || left_slot >= slots || right_slot >= slots) + return -EINVAL; + + switch (width) { + case 16: + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_16; + break; + case 24: + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_24; + break; + case 32: + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_32; + break; + default: + return -EINVAL; + } + + switch (slots) { + case 1: + ctrl1 = SSM2518_SAI_CTRL1_SAI_MONO; + break; + case 2: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_2; + break; + case 4: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_4; + break; + case 8: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_8; + break; + case 16: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_16; + break; + default: + return -EINVAL; + } + + ret = regmap_write(ssm2518->regmap, SSM2518_REG_CHAN_MAP, + (left_slot << SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET) | + (right_slot << SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET)); + if (ret) + return ret; + + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, + SSM2518_SAI_CTRL1_SAI_MASK, ctrl1); + if (ret) + return ret; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, + SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK, ctrl2); +} + +static int ssm2518_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component); + + if (ssm2518->constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, ssm2518->constraints); + + return 0; +} + +#define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32) + +static const struct snd_soc_dai_ops ssm2518_dai_ops = { + .startup = ssm2518_startup, + .hw_params = ssm2518_hw_params, + .mute_stream = ssm2518_mute, + .set_fmt = ssm2518_set_dai_fmt, + .set_tdm_slot = ssm2518_set_tdm_slot, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver ssm2518_dai = { + .name = "ssm2518-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SSM2518_FORMATS, + }, + .ops = &ssm2518_dai_ops, +}; + +static int ssm2518_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (clk_id != SSM2518_SYSCLK) + return -EINVAL; + + switch (source) { + case SSM2518_SYSCLK_SRC_MCLK: + val = 0; + break; + case SSM2518_SYSCLK_SRC_BCLK: + /* In this case the bitclock is used as the system clock, and + * the bitclock signal needs to be connected to the MCLK pin and + * the BCLK pin is left unconnected */ + val = SSM2518_POWER1_NO_BCLK; + break; + default: + return -EINVAL; + } + + switch (freq) { + case 0: + ssm2518->constraints = NULL; + break; + case 2048000: + case 4096000: + case 8192000: + case 3200000: + case 6400000: + case 12800000: + ssm2518->constraints = &ssm2518_constraints_2048000; + break; + case 2822000: + case 5644800: + case 11289600: + case 16934400: + case 22579200: + case 33868800: + case 4410000: + case 8820000: + case 17640000: + ssm2518->constraints = &ssm2518_constraints_2822000; + break; + case 3072000: + case 6144000: + case 38864000: + case 4800000: + case 9600000: + case 19200000: + ssm2518->constraints = &ssm2518_constraints_3072000; + break; + case 12288000: + case 16384000: + case 24576000: + ssm2518->constraints = &ssm2518_constraints_12288000; + break; + default: + return -EINVAL; + } + + ssm2518->sysclk = freq; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_NO_BCLK, val); +} + +static const struct snd_soc_component_driver ssm2518_component_driver = { + .set_bias_level = ssm2518_set_bias_level, + .set_sysclk = ssm2518_set_sysclk, + .controls = ssm2518_snd_controls, + .num_controls = ARRAY_SIZE(ssm2518_snd_controls), + .dapm_widgets = ssm2518_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets), + .dapm_routes = ssm2518_routes, + .num_dapm_routes = ARRAY_SIZE(ssm2518_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config ssm2518_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + + .max_register = SSM2518_REG_DRC_9, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ssm2518_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults), +}; + +static int ssm2518_i2c_probe(struct i2c_client *i2c) +{ + struct ssm2518 *ssm2518; + int ret; + + ssm2518 = devm_kzalloc(&i2c->dev, sizeof(*ssm2518), GFP_KERNEL); + if (ssm2518 == NULL) + return -ENOMEM; + + /* Start with enabling the chip */ + ssm2518->enable_gpio = devm_gpiod_get_optional(&i2c->dev, NULL, + GPIOD_OUT_HIGH); + ret = PTR_ERR_OR_ZERO(ssm2518->enable_gpio); + if (ret) + return ret; + + gpiod_set_consumer_name(ssm2518->enable_gpio, "SSM2518 nSD"); + + i2c_set_clientdata(i2c, ssm2518); + + ssm2518->regmap = devm_regmap_init_i2c(i2c, &ssm2518_regmap_config); + if (IS_ERR(ssm2518->regmap)) + return PTR_ERR(ssm2518->regmap); + + /* + * The reset bit is obviously volatile, but we need to be able to cache + * the other bits in the register, so we can't just mark the whole + * register as volatile. Since this is the only place where we'll ever + * touch the reset bit just bypass the cache for this operation. + */ + regcache_cache_bypass(ssm2518->regmap, true); + ret = regmap_write(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_RESET); + regcache_cache_bypass(ssm2518->regmap, false); + if (ret) + return ret; + + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER2, + SSM2518_POWER2_APWDN, 0x00); + if (ret) + return ret; + + ret = ssm2518_set_power(ssm2518, false); + if (ret) + return ret; + + return devm_snd_soc_register_component(&i2c->dev, + &ssm2518_component_driver, + &ssm2518_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id ssm2518_dt_ids[] = { + { .compatible = "adi,ssm2518", }, + { } +}; +MODULE_DEVICE_TABLE(of, ssm2518_dt_ids); +#endif + +static const struct i2c_device_id ssm2518_i2c_ids[] = { + { "ssm2518", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids); + +static struct i2c_driver ssm2518_driver = { + .driver = { + .name = "ssm2518", + .of_match_table = of_match_ptr(ssm2518_dt_ids), + }, + .probe = ssm2518_i2c_probe, + .id_table = ssm2518_i2c_ids, +}; +module_i2c_driver(ssm2518_driver); + +MODULE_DESCRIPTION("ASoC SSM2518 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ssm2518.h b/sound/soc/codecs/ssm2518.h new file mode 100644 index 0000000000..273fd09774 --- /dev/null +++ b/sound/soc/codecs/ssm2518.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SSM2518 amplifier audio driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + */ + +#ifndef __SND_SOC_CODECS_SSM2518_H__ +#define __SND_SOC_CODECS_SSM2518_H__ + +#define SSM2518_SYSCLK 0 + +enum ssm2518_sysclk_src { + SSM2518_SYSCLK_SRC_MCLK = 0, + SSM2518_SYSCLK_SRC_BCLK = 1, +}; + +#endif diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c new file mode 100644 index 0000000000..596096466c --- /dev/null +++ b/sound/soc/codecs/ssm2602-i2c.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SSM2602/SSM2603/SSM2604 I2C audio driver + * + * Copyright 2014 Analog Devices Inc. + */ + +#include +#include +#include + +#include + +#include "ssm2602.h" + +static const struct i2c_device_id ssm2602_i2c_id[]; + +/* + * ssm2602 2 wire address is determined by GPIO5 + * state during powerup. + * low = 0x1a + * high = 0x1b + */ +static int ssm2602_i2c_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_match_id(ssm2602_i2c_id, client); + return ssm2602_probe(&client->dev, id->driver_data, + devm_regmap_init_i2c(client, &ssm2602_regmap_config)); +} + +static const struct i2c_device_id ssm2602_i2c_id[] = { + { "ssm2602", SSM2602 }, + { "ssm2603", SSM2602 }, + { "ssm2604", SSM2604 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); + +static const struct of_device_id ssm2602_of_match[] = { + { .compatible = "adi,ssm2602", }, + { .compatible = "adi,ssm2603", }, + { .compatible = "adi,ssm2604", }, + { } +}; +MODULE_DEVICE_TABLE(of, ssm2602_of_match); + +static struct i2c_driver ssm2602_i2c_driver = { + .driver = { + .name = "ssm2602", + .of_match_table = ssm2602_of_match, + }, + .probe = ssm2602_i2c_probe, + .id_table = ssm2602_i2c_id, +}; +module_i2c_driver(ssm2602_i2c_driver); + +MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 I2C driver"); +MODULE_AUTHOR("Cliff Cai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ssm2602-spi.c b/sound/soc/codecs/ssm2602-spi.c new file mode 100644 index 0000000000..bb49fb6b62 --- /dev/null +++ b/sound/soc/codecs/ssm2602-spi.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SSM2602 SPI audio driver + * + * Copyright 2014 Analog Devices Inc. + */ + +#include +#include +#include + +#include + +#include "ssm2602.h" + +static int ssm2602_spi_probe(struct spi_device *spi) +{ + return ssm2602_probe(&spi->dev, SSM2602, + devm_regmap_init_spi(spi, &ssm2602_regmap_config)); +} + +static const struct of_device_id ssm2602_of_match[] = { + { .compatible = "adi,ssm2602", }, + { } +}; +MODULE_DEVICE_TABLE(of, ssm2602_of_match); + +static struct spi_driver ssm2602_spi_driver = { + .driver = { + .name = "ssm2602", + .of_match_table = ssm2602_of_match, + }, + .probe = ssm2602_spi_probe, +}; +module_spi_driver(ssm2602_spi_driver); + +MODULE_DESCRIPTION("ASoC SSM2602 SPI driver"); +MODULE_AUTHOR("Cliff Cai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c new file mode 100644 index 0000000000..c29324403e --- /dev/null +++ b/sound/soc/codecs/ssm2602.c @@ -0,0 +1,694 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// File: sound/soc/codecs/ssm2602.c +// Author: Cliff Cai +// +// Created: Tue June 06 2008 +// Description: Driver for ssm2602 sound chip +// +// Modified: +// Copyright 2008 Analog Devices Inc. +// +// Bugs: Enter bugs at http://blackfin.uclinux.org/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ssm2602.h" + +/* codec private data */ +struct ssm2602_priv { + unsigned int sysclk; + const struct snd_pcm_hw_constraint_list *sysclk_constraints; + + struct regmap *regmap; + + enum ssm2602_type type; + unsigned int clk_out_pwr; +}; + +/* + * ssm2602 register cache + * We can't read the ssm2602 register space when we are + * using 2 wire for device control, so we cache them instead. + * There is no point in caching the reset register + */ +static const struct reg_default ssm2602_reg[SSM2602_CACHEREGNUM] = { + { .reg = 0x00, .def = 0x0097 }, + { .reg = 0x01, .def = 0x0097 }, + { .reg = 0x02, .def = 0x0079 }, + { .reg = 0x03, .def = 0x0079 }, + { .reg = 0x04, .def = 0x000a }, + { .reg = 0x05, .def = 0x0008 }, + { .reg = 0x06, .def = 0x009f }, + { .reg = 0x07, .def = 0x000a }, + { .reg = 0x08, .def = 0x0000 }, + { .reg = 0x09, .def = 0x0000 } +}; + +/* + * ssm2602 register patch + * Workaround for playback distortions after power up: activates digital + * core, and then powers on output, DAC, and whole chip at the same time + */ + +static const struct reg_sequence ssm2602_patch[] = { + { SSM2602_ACTIVE, 0x01 }, + { SSM2602_PWR, 0x07 }, + { SSM2602_RESET, 0x00 }, +}; + + +/*Appending several "None"s just for OSS mixer use*/ +static const char *ssm2602_input_select[] = { + "Line", "Mic", +}; + +static const char *ssm2602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; + +static const struct soc_enum ssm2602_enum[] = { + SOC_ENUM_SINGLE(SSM2602_APANA, 2, ARRAY_SIZE(ssm2602_input_select), + ssm2602_input_select), + SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, ARRAY_SIZE(ssm2602_deemph), + ssm2602_deemph), +}; + +static const DECLARE_TLV_DB_RANGE(ssm260x_outmix_tlv, + 0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0) +); + +static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0); + +static const struct snd_kcontrol_new ssm260x_snd_controls[] = { +SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0, + ssm260x_inpga_tlv), +SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), + +SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), +SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), + +SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), +}; + +static const struct snd_kcontrol_new ssm2602_snd_controls[] = { +SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, + 0, 127, 0, ssm260x_outmix_tlv), +SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V, + 7, 1, 0), +SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1, + ssm260x_sidetone_tlv), + +SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), +SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0), +}; + +/* Output Mixer */ +static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0), +SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0), +SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0), +}; + +static const struct snd_kcontrol_new mic_ctl = + SOC_DAPM_SINGLE("Switch", SSM2602_APANA, 1, 1, 1); + +/* Input mux */ +static const struct snd_kcontrol_new ssm2602_input_mux_controls = +SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]); + +static int ssm2602_mic_switch_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + /* + * According to the ssm2603 data sheet (control register sequencing), + * the digital core should be activated only after all necessary bits + * in the power register are enabled, and a delay determined by the + * decoupling capacitor on the VMID pin has passed. If the digital core + * is activated too early, or even before the ADC is powered up, audible + * artifacts appear at the beginning and end of the recorded signal. + * + * In practice, audible artifacts disappear well over 500 ms. + */ + msleep(500); + + return 0; +} + +static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), +SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), +SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), + +SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, NULL, 0), + +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("ROUT"), +SND_SOC_DAPM_INPUT("RLINEIN"), +SND_SOC_DAPM_INPUT("LLINEIN"), +}; + +static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1, + ssm260x_output_mixer_controls, + ARRAY_SIZE(ssm260x_output_mixer_controls)), + +SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls), +SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1), + +SND_SOC_DAPM_SWITCH_E("Mic Switch", SSM2602_APANA, 1, 1, &mic_ctl, + ssm2602_mic_switch_event, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_OUTPUT("LHPOUT"), +SND_SOC_DAPM_OUTPUT("RHPOUT"), +SND_SOC_DAPM_INPUT("MICIN"), +}; + +static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, + ssm260x_output_mixer_controls, + ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */ +}; + +static const struct snd_soc_dapm_route ssm260x_routes[] = { + {"DAC", NULL, "Digital Core Power"}, + {"ADC", NULL, "Digital Core Power"}, + + {"Output Mixer", "Line Bypass Switch", "Line Input"}, + {"Output Mixer", "HiFi Playback Switch", "DAC"}, + + {"ROUT", NULL, "Output Mixer"}, + {"LOUT", NULL, "Output Mixer"}, + + {"Line Input", NULL, "LLINEIN"}, + {"Line Input", NULL, "RLINEIN"}, +}; + +static const struct snd_soc_dapm_route ssm2602_routes[] = { + {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, + + {"RHPOUT", NULL, "Output Mixer"}, + {"LHPOUT", NULL, "Output Mixer"}, + + {"Input Mux", "Line", "Line Input"}, + {"Input Mux", "Mic", "Mic Switch"}, + {"ADC", NULL, "Input Mux"}, + + {"Mic Switch", NULL, "Mic Bias"}, + + {"Mic Bias", NULL, "MICIN"}, +}; + +static const struct snd_soc_dapm_route ssm2604_routes[] = { + {"ADC", NULL, "Line Input"}, +}; + +static const unsigned int ssm2602_rates_12288000[] = { + 8000, 16000, 32000, 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = { + .list = ssm2602_rates_12288000, + .count = ARRAY_SIZE(ssm2602_rates_12288000), +}; + +static const unsigned int ssm2602_rates_11289600[] = { + 8000, 11025, 22050, 44100, 88200, +}; + +static const struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = { + .list = ssm2602_rates_11289600, + .count = ARRAY_SIZE(ssm2602_rates_11289600), +}; + +struct ssm2602_coeff { + u32 mclk; + u32 rate; + u8 srate; +}; + +#define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb)) + +/* codec mclk clock coefficients */ +static const struct ssm2602_coeff ssm2602_coeff_table[] = { + /* 48k */ + {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)}, + {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)}, + {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)}, + + /* 32k */ + {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)}, + {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)}, + {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)}, + + /* 16k */ + {12288000, 16000, SSM2602_COEFF_SRATE(0x5, 0x0, 0x0)}, + {18432000, 16000, SSM2602_COEFF_SRATE(0x5, 0x1, 0x0)}, + {12000000, 16000, SSM2602_COEFF_SRATE(0xa, 0x0, 0x1)}, + + /* 8k */ + {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)}, + {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)}, + {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)}, + {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)}, + {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)}, + + /* 96k */ + {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)}, + {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)}, + {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)}, + + /* 11.025k */ + {11289600, 11025, SSM2602_COEFF_SRATE(0xc, 0x0, 0x0)}, + {16934400, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x0)}, + {12000000, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x1)}, + + /* 22.05k */ + {11289600, 22050, SSM2602_COEFF_SRATE(0xd, 0x0, 0x0)}, + {16934400, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x0)}, + {12000000, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x1)}, + + /* 44.1k */ + {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)}, + {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)}, + {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)}, + + /* 88.2k */ + {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)}, + {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)}, + {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)}, +}; + +static inline int ssm2602_get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) { + if (ssm2602_coeff_table[i].rate == rate) { + if (ssm2602_coeff_table[i].mclk == mclk) + return ssm2602_coeff_table[i].srate; + if (ssm2602_coeff_table[i].mclk == mclk / 2) + return ssm2602_coeff_table[i].srate | SRATE_CORECLK_DIV2; + } + } + return -EINVAL; +} + +static int ssm2602_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component); + int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params)); + unsigned int iface; + + if (srate < 0) + return srate; + + regmap_write(ssm2602->regmap, SSM2602_SRATE, srate); + + /* bit size */ + switch (params_width(params)) { + case 16: + iface = 0x0; + break; + case 20: + iface = 0x4; + break; + case 24: + iface = 0x8; + break; + case 32: + iface = 0xc; + break; + default: + return -EINVAL; + } + regmap_update_bits(ssm2602->regmap, SSM2602_IFACE, + IFACE_AUDIO_DATA_LEN, iface); + return 0; +} + +static int ssm2602_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component); + + if (ssm2602->sysclk_constraints) { + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + ssm2602->sysclk_constraints); + } + + return 0; +} + +static int ssm2602_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(dai->component); + + if (mute) + regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI, + APDIGI_ENABLE_DAC_MUTE, + APDIGI_ENABLE_DAC_MUTE); + else + regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI, + APDIGI_ENABLE_DAC_MUTE, 0); + return 0; +} + +static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component); + + if (dir == SND_SOC_CLOCK_IN) { + if (clk_id != SSM2602_SYSCLK) + return -EINVAL; + + switch (freq) { + case 12288000: + case 18432000: + case 24576000: + case 36864000: + ssm2602->sysclk_constraints = &ssm2602_constraints_12288000; + break; + case 11289600: + case 16934400: + case 22579200: + case 33868800: + ssm2602->sysclk_constraints = &ssm2602_constraints_11289600; + break; + case 12000000: + case 24000000: + ssm2602->sysclk_constraints = NULL; + break; + default: + return -EINVAL; + } + + ssm2602->sysclk = freq; + } else { + unsigned int mask; + + switch (clk_id) { + case SSM2602_CLK_CLKOUT: + mask = PWR_CLK_OUT_PDN; + break; + case SSM2602_CLK_XTO: + mask = PWR_OSC_PDN; + break; + default: + return -EINVAL; + } + + if (freq == 0) + ssm2602->clk_out_pwr |= mask; + else + ssm2602->clk_out_pwr &= ~mask; + + regmap_update_bits(ssm2602->regmap, SSM2602_PWR, + PWR_CLK_OUT_PDN | PWR_OSC_PDN, ssm2602->clk_out_pwr); + } + + return 0; +} + +static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(codec_dai->component); + unsigned int iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + iface |= 0x0040; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0013; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0003; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + /* set iface */ + regmap_write(ssm2602->regmap, SSM2602_IFACE, iface); + return 0; +} + +static int ssm2602_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + /* vref/mid on, osc and clkout on if enabled */ + regmap_update_bits(ssm2602->regmap, SSM2602_PWR, + PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN, + ssm2602->clk_out_pwr); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* everything off except vref/vmid, */ + regmap_update_bits(ssm2602->regmap, SSM2602_PWR, + PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN, + PWR_CLK_OUT_PDN | PWR_OSC_PDN); + break; + case SND_SOC_BIAS_OFF: + /* everything off */ + regmap_update_bits(ssm2602->regmap, SSM2602_PWR, + PWR_POWER_OFF, PWR_POWER_OFF); + break; + + } + return 0; +} + +#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000) + +#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops ssm2602_dai_ops = { + .startup = ssm2602_startup, + .hw_params = ssm2602_hw_params, + .mute_stream = ssm2602_mute, + .set_sysclk = ssm2602_set_dai_sysclk, + .set_fmt = ssm2602_set_dai_fmt, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver ssm2602_dai = { + .name = "ssm2602-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SSM2602_RATES, + .formats = SSM2602_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SSM2602_RATES, + .formats = SSM2602_FORMATS,}, + .ops = &ssm2602_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, +}; + +static int ssm2602_resume(struct snd_soc_component *component) +{ + struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component); + + regcache_sync(ssm2602->regmap); + + return 0; +} + +static int ssm2602_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component); + int ret; + + regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V, + LOUT1V_LRHP_BOTH, LOUT1V_LRHP_BOTH); + regmap_update_bits(ssm2602->regmap, SSM2602_ROUT1V, + ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH); + + ret = snd_soc_add_component_controls(component, ssm2602_snd_controls, + ARRAY_SIZE(ssm2602_snd_controls)); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets, + ARRAY_SIZE(ssm2602_dapm_widgets)); + if (ret) + return ret; + + return snd_soc_dapm_add_routes(dapm, ssm2602_routes, + ARRAY_SIZE(ssm2602_routes)); +} + +static int ssm2604_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets, + ARRAY_SIZE(ssm2604_dapm_widgets)); + if (ret) + return ret; + + return snd_soc_dapm_add_routes(dapm, ssm2604_routes, + ARRAY_SIZE(ssm2604_routes)); +} + +static int ssm260x_component_probe(struct snd_soc_component *component) +{ + struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component); + int ret; + + ret = regmap_write(ssm2602->regmap, SSM2602_RESET, 0); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + regmap_register_patch(ssm2602->regmap, ssm2602_patch, + ARRAY_SIZE(ssm2602_patch)); + + /* set the update bits */ + regmap_update_bits(ssm2602->regmap, SSM2602_LINVOL, + LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH); + regmap_update_bits(ssm2602->regmap, SSM2602_RINVOL, + RINVOL_RLIN_BOTH, RINVOL_RLIN_BOTH); + /*select Line in as default input*/ + regmap_write(ssm2602->regmap, SSM2602_APANA, APANA_SELECT_DAC | + APANA_ENABLE_MIC_BOOST); + + switch (ssm2602->type) { + case SSM2602: + ret = ssm2602_component_probe(component); + break; + case SSM2604: + ret = ssm2604_component_probe(component); + break; + } + + return ret; +} + +static const struct snd_soc_component_driver soc_component_dev_ssm2602 = { + .probe = ssm260x_component_probe, + .resume = ssm2602_resume, + .set_bias_level = ssm2602_set_bias_level, + .controls = ssm260x_snd_controls, + .num_controls = ARRAY_SIZE(ssm260x_snd_controls), + .dapm_widgets = ssm260x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets), + .dapm_routes = ssm260x_routes, + .num_dapm_routes = ARRAY_SIZE(ssm260x_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static bool ssm2602_register_volatile(struct device *dev, unsigned int reg) +{ + return reg == SSM2602_RESET; +} + +const struct regmap_config ssm2602_regmap_config = { + .val_bits = 9, + .reg_bits = 7, + + .max_register = SSM2602_RESET, + .volatile_reg = ssm2602_register_volatile, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ssm2602_reg, + .num_reg_defaults = ARRAY_SIZE(ssm2602_reg), +}; +EXPORT_SYMBOL_GPL(ssm2602_regmap_config); + +int ssm2602_probe(struct device *dev, enum ssm2602_type type, + struct regmap *regmap) +{ + struct ssm2602_priv *ssm2602; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ssm2602 = devm_kzalloc(dev, sizeof(*ssm2602), GFP_KERNEL); + if (ssm2602 == NULL) + return -ENOMEM; + + dev_set_drvdata(dev, ssm2602); + ssm2602->type = type; + ssm2602->regmap = regmap; + + return devm_snd_soc_register_component(dev, &soc_component_dev_ssm2602, + &ssm2602_dai, 1); +} +EXPORT_SYMBOL_GPL(ssm2602_probe); + +MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver"); +MODULE_AUTHOR("Cliff Cai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h new file mode 100644 index 0000000000..05073380a4 --- /dev/null +++ b/sound/soc/codecs/ssm2602.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * File: sound/soc/codecs/ssm2602.h + * Author: Cliff Cai + * + * Created: Tue June 06 2008 + * + * Modified: + * Copyright 2008 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + */ + +#ifndef _SSM2602_H +#define _SSM2602_H + +#include + +struct device; + +enum ssm2602_type { + SSM2602, + SSM2604, +}; + +extern const struct regmap_config ssm2602_regmap_config; + +int ssm2602_probe(struct device *dev, enum ssm2602_type type, + struct regmap *regmap); + +/* SSM2602 Codec Register definitions */ + +#define SSM2602_LINVOL 0x00 +#define SSM2602_RINVOL 0x01 +#define SSM2602_LOUT1V 0x02 +#define SSM2602_ROUT1V 0x03 +#define SSM2602_APANA 0x04 +#define SSM2602_APDIGI 0x05 +#define SSM2602_PWR 0x06 +#define SSM2602_IFACE 0x07 +#define SSM2602_SRATE 0x08 +#define SSM2602_ACTIVE 0x09 +#define SSM2602_RESET 0x0f + +/*SSM2602 Codec Register Field definitions + *(Mask value to extract the corresponding Register field) + */ + +/*Left ADC Volume Control (SSM2602_REG_LEFT_ADC_VOL)*/ +#define LINVOL_LIN_VOL 0x01F /* Left Channel PGA Volume control */ +#define LINVOL_LIN_ENABLE_MUTE 0x080 /* Left Channel Input Mute */ +#define LINVOL_LRIN_BOTH 0x100 /* Left Channel Line Input Volume update */ + +/*Right ADC Volume Control (SSM2602_REG_RIGHT_ADC_VOL)*/ +#define RINVOL_RIN_VOL 0x01F /* Right Channel PGA Volume control */ +#define RINVOL_RIN_ENABLE_MUTE 0x080 /* Right Channel Input Mute */ +#define RINVOL_RLIN_BOTH 0x100 /* Right Channel Line Input Volume update */ + +/*Left DAC Volume Control (SSM2602_REG_LEFT_DAC_VOL)*/ +#define LOUT1V_LHP_VOL 0x07F /* Left Channel Headphone volume control */ +#define LOUT1V_ENABLE_LZC 0x080 /* Left Channel Zero cross detect enable */ +#define LOUT1V_LRHP_BOTH 0x100 /* Left Channel Headphone volume update */ + +/*Right DAC Volume Control (SSM2602_REG_RIGHT_DAC_VOL)*/ +#define ROUT1V_RHP_VOL 0x07F /* Right Channel Headphone volume control */ +#define ROUT1V_ENABLE_RZC 0x080 /* Right Channel Zero cross detect enable */ +#define ROUT1V_RLHP_BOTH 0x100 /* Right Channel Headphone volume update */ + +/*Analogue Audio Path Control (SSM2602_REG_ANALOGUE_PATH)*/ +#define APANA_ENABLE_MIC_BOOST 0x001 /* Primary Microphone Amplifier gain booster control */ +#define APANA_ENABLE_MIC_MUTE 0x002 /* Microphone Mute Control */ +#define APANA_ADC_IN_SELECT 0x004 /* Microphone/Line IN select to ADC (1=MIC, 0=Line In) */ +#define APANA_ENABLE_BYPASS 0x008 /* Line input bypass to line output */ +#define APANA_SELECT_DAC 0x010 /* Select DAC (1=Select DAC, 0=Don't Select DAC) */ +#define APANA_ENABLE_SIDETONE 0x020 /* Enable/Disable Side Tone */ +#define APANA_SIDETONE_ATTN 0x0C0 /* Side Tone Attenuation */ +#define APANA_ENABLE_MIC_BOOST2 0x100 /* Secondary Microphone Amplifier gain booster control */ + +/*Digital Audio Path Control (SSM2602_REG_DIGITAL_PATH)*/ +#define APDIGI_ENABLE_ADC_HPF 0x001 /* Enable/Disable ADC Highpass Filter */ +#define APDIGI_DE_EMPHASIS 0x006 /* De-Emphasis Control */ +#define APDIGI_ENABLE_DAC_MUTE 0x008 /* DAC Mute Control */ +#define APDIGI_STORE_OFFSET 0x010 /* Store/Clear DC offset when HPF is disabled */ + +/*Power Down Control (SSM2602_REG_POWER) + *(1=Enable PowerDown, 0=Disable PowerDown) + */ +#define PWR_LINE_IN_PDN 0x001 /* Line Input Power Down */ +#define PWR_MIC_PDN 0x002 /* Microphone Input & Bias Power Down */ +#define PWR_ADC_PDN 0x004 /* ADC Power Down */ +#define PWR_DAC_PDN 0x008 /* DAC Power Down */ +#define PWR_OUT_PDN 0x010 /* Outputs Power Down */ +#define PWR_OSC_PDN 0x020 /* Oscillator Power Down */ +#define PWR_CLK_OUT_PDN 0x040 /* CLKOUT Power Down */ +#define PWR_POWER_OFF 0x080 /* POWEROFF Mode */ + +/*Digital Audio Interface Format (SSM2602_REG_DIGITAL_IFACE)*/ +#define IFACE_IFACE_FORMAT 0x003 /* Digital Audio input format control */ +#define IFACE_AUDIO_DATA_LEN 0x00C /* Audio Data word length control */ +#define IFACE_DAC_LR_POLARITY 0x010 /* Polarity Control for clocks in RJ,LJ and I2S modes */ +#define IFACE_DAC_LR_SWAP 0x020 /* Swap DAC data control */ +#define IFACE_ENABLE_MASTER 0x040 /* Enable/Disable Master Mode */ +#define IFACE_BCLK_INVERT 0x080 /* Bit Clock Inversion control */ + +/*Sampling Control (SSM2602_REG_SAMPLING_CTRL)*/ +#define SRATE_ENABLE_USB_MODE 0x001 /* Enable/Disable USB Mode */ +#define SRATE_BOS_RATE 0x002 /* Base Over-Sampling rate */ +#define SRATE_SAMPLE_RATE 0x03C /* Clock setting condition (Sampling rate control) */ +#define SRATE_CORECLK_DIV2 0x040 /* Core Clock divider select */ +#define SRATE_CLKOUT_DIV2 0x080 /* Clock Out divider select */ + +/*Active Control (SSM2602_REG_ACTIVE_CTRL)*/ +#define ACTIVE_ACTIVATE_CODEC 0x001 /* Activate Codec Digital Audio Interface */ + +/*********************************************************************/ + +#define SSM2602_CACHEREGNUM 10 + +enum ssm2602_clk { + SSM2602_SYSCLK, + SSM2602_CLK_CLKOUT, + SSM2602_CLK_XTO +}; + +#endif diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c new file mode 100644 index 0000000000..8c6665677a --- /dev/null +++ b/sound/soc/codecs/ssm3515.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +// +// Analog Devices' SSM3515 audio amp driver +// +// Copyright (C) The Asahi Linux Contributors + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#define SSM3515_PWR 0x00 +#define SSM3515_PWR_APWDN_EN BIT(7) +#define SSM3515_PWR_BSNS_PWDN BIT(6) +#define SSM3515_PWR_S_RST BIT(1) +#define SSM3515_PWR_SPWDN BIT(0) + +#define SSM3515_GEC 0x01 +#define SSM3515_GEC_EDGE BIT(4) +#define SSM3515_GEC_EDGE_SHIFT 4 +#define SSM3515_GEC_ANA_GAIN GENMASK(1, 0) + +#define SSM3515_DAC 0x02 +#define SSM3515_DAC_HV BIT(7) +#define SSM3515_DAC_MUTE BIT(6) +#define SSM3515_DAC_HPF BIT(5) +#define SSM3515_DAC_LPM BIT(4) +#define SSM3515_DAC_FS GENMASK(2, 0) + +#define SSM3515_DAC_VOL 0x03 + +#define SSM3515_SAI1 0x04 +#define SSM3515_SAI1_DAC_POL BIT(7) +#define SSM3515_SAI1_BCLK_POL BIT(6) +#define SSM3515_SAI1_TDM_BCLKS GENMASK(5, 3) +#define SSM3515_SAI1_FSYNC_MODE BIT(2) +#define SSM3515_SAI1_SDATA_FMT BIT(1) +#define SSM3515_SAI1_SAI_MODE BIT(0) + +#define SSM3515_SAI2 0x05 +#define SSM3515_SAI2_DATA_WIDTH BIT(7) +#define SSM3515_SAI2_AUTO_SLOT BIT(4) +#define SSM3515_SAI2_TDM_SLOT GENMASK(3, 0) + +#define SSM3515_VBAT_OUT 0x06 + +#define SSM3515_STATUS 0x0a +#define SSM3515_STATUS_UVLO_REG BIT(6) +#define SSM3515_STATUS_LIM_EG BIT(5) +#define SSM3515_STATUS_CLIP BIT(4) +#define SSM3515_STATUS_AMP_OC BIT(3) +#define SSM3515_STATUS_OTF BIT(2) +#define SSM3515_STATUS_OTW BIT(1) +#define SSM3515_STATUS_BAT_WARN BIT(0) + +static bool ssm3515_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SSM3515_STATUS: + case SSM3515_VBAT_OUT: + return true; + + default: + return false; + } +} + +static const struct reg_default ssm3515_reg_defaults[] = { + { SSM3515_PWR, 0x81 }, + { SSM3515_GEC, 0x01 }, + { SSM3515_DAC, 0x32 }, + { SSM3515_DAC_VOL, 0x40 }, + { SSM3515_SAI1, 0x11 }, + { SSM3515_SAI2, 0x00 }, +}; + +static const struct regmap_config ssm3515_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = ssm3515_volatile_reg, + .max_register = 0xb, + .reg_defaults = ssm3515_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ssm3515_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +struct ssm3515_data { + struct device *dev; + struct regmap *regmap; +}; + +// The specced range is -71.25...24.00 dB with step size of 0.375 dB, +// and a mute item below that. This is represented by -71.62...24.00 dB +// with the mute item mapped onto the low end. +static DECLARE_TLV_DB_MINMAX_MUTE(ssm3515_dac_volume, -7162, 2400); + +static const char * const ssm3515_ana_gain_text[] = { + "8.4 V Span", "12.6 V Span", "14 V Span", "15 V Span", +}; + +static SOC_ENUM_SINGLE_DECL(ssm3515_ana_gain_enum, SSM3515_GEC, + __bf_shf(SSM3515_GEC_ANA_GAIN), + ssm3515_ana_gain_text); + +static const struct snd_kcontrol_new ssm3515_snd_controls[] = { + SOC_SINGLE_TLV("DAC Playback Volume", SSM3515_DAC_VOL, + 0, 255, 1, ssm3515_dac_volume), + SOC_SINGLE("Low EMI Mode Switch", SSM3515_GEC, + __bf_shf(SSM3515_GEC_EDGE), 1, 0), + SOC_SINGLE("Soft Volume Ramping Switch", SSM3515_DAC, + __bf_shf(SSM3515_DAC_HV), 1, 1), + SOC_SINGLE("HPF Switch", SSM3515_DAC, + __bf_shf(SSM3515_DAC_HPF), 1, 0), + SOC_SINGLE("DAC Invert Switch", SSM3515_SAI1, + __bf_shf(SSM3515_SAI1_DAC_POL), 1, 0), + SOC_ENUM("DAC Analog Gain Select", ssm3515_ana_gain_enum), +}; + +static void ssm3515_read_faults(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_read(component, SSM3515_STATUS); + if (ret <= 0) { + /* + * If the read was erroneous, ASoC core has printed a message, + * and that's all that's appropriate in handling the error here. + */ + return; + } + + dev_err(component->dev, "device reports:%s%s%s%s%s%s%s\n", + FIELD_GET(SSM3515_STATUS_UVLO_REG, ret) ? " voltage regulator fault" : "", + FIELD_GET(SSM3515_STATUS_LIM_EG, ret) ? " limiter engaged" : "", + FIELD_GET(SSM3515_STATUS_CLIP, ret) ? " clipping detected" : "", + FIELD_GET(SSM3515_STATUS_AMP_OC, ret) ? " amp over-current fault" : "", + FIELD_GET(SSM3515_STATUS_OTF, ret) ? " overtemperature fault" : "", + FIELD_GET(SSM3515_STATUS_OTW, ret) ? " overtemperature warning" : "", + FIELD_GET(SSM3515_STATUS_BAT_WARN, ret) ? " bat voltage low warning" : ""); +} + +static int ssm3515_probe(struct snd_soc_component *component) +{ + int ret; + + /* Start out muted */ + ret = snd_soc_component_update_bits(component, SSM3515_DAC, + SSM3515_DAC_MUTE, SSM3515_DAC_MUTE); + if (ret < 0) + return ret; + + /* Disable the 'master power-down' */ + ret = snd_soc_component_update_bits(component, SSM3515_PWR, + SSM3515_PWR_SPWDN, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + int ret; + + ret = snd_soc_component_update_bits(dai->component, + SSM3515_DAC, + SSM3515_DAC_MUTE, + FIELD_PREP(SSM3515_DAC_MUTE, mute)); + if (ret < 0) + return ret; + return 0; +} + +static int ssm3515_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int ret, rateval; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + case SNDRV_PCM_FORMAT_S24: + ret = snd_soc_component_update_bits(component, + SSM3515_SAI2, SSM3515_SAI2_DATA_WIDTH, + FIELD_PREP(SSM3515_SAI2_DATA_WIDTH, + params_width(params) == 16)); + if (ret < 0) + return ret; + break; + + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 8000 ... 12000: + rateval = 0; + break; + case 16000 ... 24000: + rateval = 1; + break; + case 32000 ... 48000: + rateval = 2; + break; + case 64000 ... 96000: + rateval = 3; + break; + case 128000 ... 192000: + rateval = 4; + break; + case 48001 ... 63999: /* this is ...72000 but overlaps */ + rateval = 5; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, + SSM3515_DAC, SSM3515_DAC_FS, + FIELD_PREP(SSM3515_DAC_FS, rateval)); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + bool fpol_inv = false; /* non-inverted: frame starts with low-to-high FSYNC */ + int ret; + u8 sai1 = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: + sai1 |= SSM3515_SAI1_BCLK_POL; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + fpol_inv = 1; + sai1 &= ~SSM3515_SAI1_SDATA_FMT; /* 1 bit start delay */ + break; + case SND_SOC_DAIFMT_LEFT_J: + fpol_inv = 0; + sai1 |= SSM3515_SAI1_SDATA_FMT; /* no start delay */ + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + fpol_inv ^= 1; + break; + } + + /* Set the serial input to 'TDM mode' */ + sai1 |= SSM3515_SAI1_SAI_MODE; + + if (fpol_inv) { + /* + * We configure the codec in a 'TDM mode', in which the + * FSYNC_MODE bit of SAI1 is supposed to select between + * what the datasheet calls 'Pulsed FSYNC mode' and '50% + * FSYNC mode'. + * + * Experiments suggest that this bit in fact simply selects + * the FSYNC polarity, so go with that. + */ + sai1 |= SSM3515_SAI1_FSYNC_MODE; + } + + ret = snd_soc_component_update_bits(component, SSM3515_SAI1, + SSM3515_SAI1_BCLK_POL | SSM3515_SAI1_SDATA_FMT | + SSM3515_SAI1_SAI_MODE | SSM3515_SAI1_FSYNC_MODE, sai1); + + if (ret < 0) + return ret; + return 0; +} + +static int ssm3515_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + int slot, tdm_bclks_val, ret; + + if (tx_mask == 0 || rx_mask != 0) + return -EINVAL; + + slot = __ffs(tx_mask); + + if (tx_mask & ~BIT(slot)) + return -EINVAL; + + switch (slot_width) { + case 16: + tdm_bclks_val = 0; + break; + case 24: + tdm_bclks_val = 1; + break; + case 32: + tdm_bclks_val = 2; + break; + case 48: + tdm_bclks_val = 3; + break; + case 64: + tdm_bclks_val = 4; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, SSM3515_SAI1, + SSM3515_SAI1_TDM_BCLKS, + FIELD_PREP(SSM3515_SAI1_TDM_BCLKS, tdm_bclks_val)); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, SSM3515_SAI2, + SSM3515_SAI2_TDM_SLOT, + FIELD_PREP(SSM3515_SAI2_TDM_SLOT, slot)); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* + * We don't get live notification of faults, so at least at + * this time, when playback is over, check if we have tripped + * over anything and if so, log it. + */ + ssm3515_read_faults(dai->component); + return 0; +} + +static const struct snd_soc_dai_ops ssm3515_dai_ops = { + .mute_stream = ssm3515_mute, + .hw_params = ssm3515_hw_params, + .set_fmt = ssm3515_set_fmt, + .set_tdm_slot = ssm3515_set_tdm_slot, + .hw_free = ssm3515_hw_free, +}; + +static struct snd_soc_dai_driver ssm3515_dai_driver = { + .name = "SSM3515 SAI", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ssm3515_dai_ops, +}; + +static const struct snd_soc_dapm_widget ssm3515_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route ssm3515_dapm_routes[] = { + {"OUT", NULL, "DAC"}, + {"DAC", NULL, "Playback"}, +}; + +static const struct snd_soc_component_driver ssm3515_asoc_component = { + .probe = ssm3515_probe, + .controls = ssm3515_snd_controls, + .num_controls = ARRAY_SIZE(ssm3515_snd_controls), + .dapm_widgets = ssm3515_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm3515_dapm_widgets), + .dapm_routes = ssm3515_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ssm3515_dapm_routes), + .endianness = 1, +}; + +static int ssm3515_i2c_probe(struct i2c_client *client) +{ + struct ssm3515_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = &client->dev; + i2c_set_clientdata(client, data); + + data->regmap = devm_regmap_init_i2c(client, &ssm3515_i2c_regmap); + if (IS_ERR(data->regmap)) + return dev_err_probe(data->dev, PTR_ERR(data->regmap), + "initializing register map\n"); + + /* Perform a reset */ + ret = regmap_update_bits(data->regmap, SSM3515_PWR, + SSM3515_PWR_S_RST, SSM3515_PWR_S_RST); + if (ret < 0) + return dev_err_probe(data->dev, ret, + "performing software reset\n"); + regmap_reinit_cache(data->regmap, &ssm3515_i2c_regmap); + + return devm_snd_soc_register_component(data->dev, + &ssm3515_asoc_component, + &ssm3515_dai_driver, 1); +} + +static const struct of_device_id ssm3515_of_match[] = { + { .compatible = "adi,ssm3515" }, + {} +}; +MODULE_DEVICE_TABLE(of, ssm3515_of_match); + +static struct i2c_driver ssm3515_i2c_driver = { + .driver = { + .name = "ssm3515", + .of_match_table = ssm3515_of_match, + }, + .probe = ssm3515_i2c_probe, +}; +module_i2c_driver(ssm3515_i2c_driver); + +MODULE_AUTHOR("Martin Povišer "); +MODULE_DESCRIPTION("ASoC SSM3515 audio amp driver"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c new file mode 100644 index 0000000000..0a6f04d8f6 --- /dev/null +++ b/sound/soc/codecs/ssm4567.c @@ -0,0 +1,510 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SSM4567 amplifier audio driver + * + * Copyright 2014 Google Chromium project. + * Author: Anatol Pomozov + * + * Based on code copyright/by: + * Copyright 2013 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SSM4567_REG_POWER_CTRL 0x00 +#define SSM4567_REG_AMP_SNS_CTRL 0x01 +#define SSM4567_REG_DAC_CTRL 0x02 +#define SSM4567_REG_DAC_VOLUME 0x03 +#define SSM4567_REG_SAI_CTRL_1 0x04 +#define SSM4567_REG_SAI_CTRL_2 0x05 +#define SSM4567_REG_SAI_PLACEMENT_1 0x06 +#define SSM4567_REG_SAI_PLACEMENT_2 0x07 +#define SSM4567_REG_SAI_PLACEMENT_3 0x08 +#define SSM4567_REG_SAI_PLACEMENT_4 0x09 +#define SSM4567_REG_SAI_PLACEMENT_5 0x0a +#define SSM4567_REG_SAI_PLACEMENT_6 0x0b +#define SSM4567_REG_BATTERY_V_OUT 0x0c +#define SSM4567_REG_LIMITER_CTRL_1 0x0d +#define SSM4567_REG_LIMITER_CTRL_2 0x0e +#define SSM4567_REG_LIMITER_CTRL_3 0x0f +#define SSM4567_REG_STATUS_1 0x10 +#define SSM4567_REG_STATUS_2 0x11 +#define SSM4567_REG_FAULT_CTRL 0x12 +#define SSM4567_REG_PDM_CTRL 0x13 +#define SSM4567_REG_MCLK_RATIO 0x14 +#define SSM4567_REG_BOOST_CTRL_1 0x15 +#define SSM4567_REG_BOOST_CTRL_2 0x16 +#define SSM4567_REG_SOFT_RESET 0xff + +/* POWER_CTRL */ +#define SSM4567_POWER_APWDN_EN BIT(7) +#define SSM4567_POWER_BSNS_PWDN BIT(6) +#define SSM4567_POWER_VSNS_PWDN BIT(5) +#define SSM4567_POWER_ISNS_PWDN BIT(4) +#define SSM4567_POWER_BOOST_PWDN BIT(3) +#define SSM4567_POWER_AMP_PWDN BIT(2) +#define SSM4567_POWER_VBAT_ONLY BIT(1) +#define SSM4567_POWER_SPWDN BIT(0) + +/* DAC_CTRL */ +#define SSM4567_DAC_HV BIT(7) +#define SSM4567_DAC_MUTE BIT(6) +#define SSM4567_DAC_HPF BIT(5) +#define SSM4567_DAC_LPM BIT(4) +#define SSM4567_DAC_FS_MASK 0x7 +#define SSM4567_DAC_FS_8000_12000 0x0 +#define SSM4567_DAC_FS_16000_24000 0x1 +#define SSM4567_DAC_FS_32000_48000 0x2 +#define SSM4567_DAC_FS_64000_96000 0x3 +#define SSM4567_DAC_FS_128000_192000 0x4 + +/* SAI_CTRL_1 */ +#define SSM4567_SAI_CTRL_1_BCLK BIT(6) +#define SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK (0x3 << 4) +#define SSM4567_SAI_CTRL_1_TDM_BLCKS_32 (0x0 << 4) +#define SSM4567_SAI_CTRL_1_TDM_BLCKS_48 (0x1 << 4) +#define SSM4567_SAI_CTRL_1_TDM_BLCKS_64 (0x2 << 4) +#define SSM4567_SAI_CTRL_1_FSYNC BIT(3) +#define SSM4567_SAI_CTRL_1_LJ BIT(2) +#define SSM4567_SAI_CTRL_1_TDM BIT(1) +#define SSM4567_SAI_CTRL_1_PDM BIT(0) + +/* SAI_CTRL_2 */ +#define SSM4567_SAI_CTRL_2_AUTO_SLOT BIT(3) +#define SSM4567_SAI_CTRL_2_TDM_SLOT_MASK 0x7 +#define SSM4567_SAI_CTRL_2_TDM_SLOT(x) (x) + +struct ssm4567 { + struct regmap *regmap; +}; + +static const struct reg_default ssm4567_reg_defaults[] = { + { SSM4567_REG_POWER_CTRL, 0x81 }, + { SSM4567_REG_AMP_SNS_CTRL, 0x09 }, + { SSM4567_REG_DAC_CTRL, 0x32 }, + { SSM4567_REG_DAC_VOLUME, 0x40 }, + { SSM4567_REG_SAI_CTRL_1, 0x00 }, + { SSM4567_REG_SAI_CTRL_2, 0x08 }, + { SSM4567_REG_SAI_PLACEMENT_1, 0x01 }, + { SSM4567_REG_SAI_PLACEMENT_2, 0x20 }, + { SSM4567_REG_SAI_PLACEMENT_3, 0x32 }, + { SSM4567_REG_SAI_PLACEMENT_4, 0x07 }, + { SSM4567_REG_SAI_PLACEMENT_5, 0x07 }, + { SSM4567_REG_SAI_PLACEMENT_6, 0x07 }, + { SSM4567_REG_BATTERY_V_OUT, 0x00 }, + { SSM4567_REG_LIMITER_CTRL_1, 0xa4 }, + { SSM4567_REG_LIMITER_CTRL_2, 0x73 }, + { SSM4567_REG_LIMITER_CTRL_3, 0x00 }, + { SSM4567_REG_STATUS_1, 0x00 }, + { SSM4567_REG_STATUS_2, 0x00 }, + { SSM4567_REG_FAULT_CTRL, 0x30 }, + { SSM4567_REG_PDM_CTRL, 0x40 }, + { SSM4567_REG_MCLK_RATIO, 0x11 }, + { SSM4567_REG_BOOST_CTRL_1, 0x03 }, + { SSM4567_REG_BOOST_CTRL_2, 0x00 }, + { SSM4567_REG_SOFT_RESET, 0x00 }, +}; + + +static bool ssm4567_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SSM4567_REG_POWER_CTRL ... SSM4567_REG_BOOST_CTRL_2: + return true; + default: + return false; + } + +} + +static bool ssm4567_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SSM4567_REG_POWER_CTRL ... SSM4567_REG_SAI_PLACEMENT_6: + case SSM4567_REG_LIMITER_CTRL_1 ... SSM4567_REG_LIMITER_CTRL_3: + case SSM4567_REG_FAULT_CTRL ... SSM4567_REG_BOOST_CTRL_2: + /* The datasheet states that soft reset register is read-only, + * but logically it is write-only. */ + case SSM4567_REG_SOFT_RESET: + return true; + default: + return false; + } +} + +static bool ssm4567_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SSM4567_REG_BATTERY_V_OUT: + case SSM4567_REG_STATUS_1 ... SSM4567_REG_STATUS_2: + case SSM4567_REG_SOFT_RESET: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_MINMAX_MUTE(ssm4567_vol_tlv, -7125, 2400); + +static const struct snd_kcontrol_new ssm4567_snd_controls[] = { + SOC_SINGLE_TLV("Master Playback Volume", SSM4567_REG_DAC_VOLUME, 0, + 0xff, 1, ssm4567_vol_tlv), + SOC_SINGLE("DAC Low Power Mode Switch", SSM4567_REG_DAC_CTRL, 4, 1, 0), + SOC_SINGLE("DAC High Pass Filter Switch", SSM4567_REG_DAC_CTRL, + 5, 1, 0), +}; + +static const struct snd_kcontrol_new ssm4567_amplifier_boost_control = + SOC_DAPM_SINGLE("Switch", SSM4567_REG_POWER_CTRL, 1, 1, 1); + +static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM4567_REG_POWER_CTRL, 2, 1), + SND_SOC_DAPM_SWITCH("Amplifier Boost", SSM4567_REG_POWER_CTRL, 3, 1, + &ssm4567_amplifier_boost_control), + + SND_SOC_DAPM_SIGGEN("Sense"), + + SND_SOC_DAPM_PGA("Current Sense", SSM4567_REG_POWER_CTRL, 4, 1, NULL, 0), + SND_SOC_DAPM_PGA("Voltage Sense", SSM4567_REG_POWER_CTRL, 5, 1, NULL, 0), + SND_SOC_DAPM_PGA("VBAT Sense", SSM4567_REG_POWER_CTRL, 6, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route ssm4567_routes[] = { + { "OUT", NULL, "Amplifier Boost" }, + { "Amplifier Boost", "Switch", "DAC" }, + { "OUT", NULL, "DAC" }, + + { "Current Sense", NULL, "Sense" }, + { "Voltage Sense", NULL, "Sense" }, + { "VBAT Sense", NULL, "Sense" }, + { "Capture Sense", NULL, "Current Sense" }, + { "Capture Sense", NULL, "Voltage Sense" }, + { "Capture Sense", NULL, "VBAT Sense" }, +}; + +static int ssm4567_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct ssm4567 *ssm4567 = snd_soc_component_get_drvdata(component); + unsigned int rate = params_rate(params); + unsigned int dacfs; + + if (rate >= 8000 && rate <= 12000) + dacfs = SSM4567_DAC_FS_8000_12000; + else if (rate >= 16000 && rate <= 24000) + dacfs = SSM4567_DAC_FS_16000_24000; + else if (rate >= 32000 && rate <= 48000) + dacfs = SSM4567_DAC_FS_32000_48000; + else if (rate >= 64000 && rate <= 96000) + dacfs = SSM4567_DAC_FS_64000_96000; + else if (rate >= 128000 && rate <= 192000) + dacfs = SSM4567_DAC_FS_128000_192000; + else + return -EINVAL; + + return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL, + SSM4567_DAC_FS_MASK, dacfs); +} + +static int ssm4567_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct ssm4567 *ssm4567 = snd_soc_component_get_drvdata(dai->component); + unsigned int val; + + val = mute ? SSM4567_DAC_MUTE : 0; + return regmap_update_bits(ssm4567->regmap, SSM4567_REG_DAC_CTRL, + SSM4567_DAC_MUTE, val); +} + +static int ssm4567_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai); + unsigned int blcks; + int slot; + int ret; + + if (tx_mask == 0) + return -EINVAL; + + if (rx_mask && rx_mask != tx_mask) + return -EINVAL; + + slot = __ffs(tx_mask); + if (tx_mask != BIT(slot)) + return -EINVAL; + + switch (width) { + case 32: + blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_32; + break; + case 48: + blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_48; + break; + case 64: + blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_64; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_2, + SSM4567_SAI_CTRL_2_AUTO_SLOT | SSM4567_SAI_CTRL_2_TDM_SLOT_MASK, + SSM4567_SAI_CTRL_2_TDM_SLOT(slot)); + if (ret) + return ret; + + return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1, + SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK, blcks); +} + +static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai); + unsigned int ctrl1 = 0; + bool invert_fclk; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_fclk = false; + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1 |= SSM4567_SAI_CTRL_1_BCLK; + invert_fclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC; + invert_fclk = true; + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl1 |= SSM4567_SAI_CTRL_1_BCLK; + invert_fclk = true; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1 |= SSM4567_SAI_CTRL_1_LJ; + invert_fclk = !invert_fclk; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1 |= SSM4567_SAI_CTRL_1_TDM; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1 |= SSM4567_SAI_CTRL_1_TDM | SSM4567_SAI_CTRL_1_LJ; + break; + case SND_SOC_DAIFMT_PDM: + ctrl1 |= SSM4567_SAI_CTRL_1_PDM; + break; + default: + return -EINVAL; + } + + if (invert_fclk) + ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC; + + return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1, + SSM4567_SAI_CTRL_1_BCLK | + SSM4567_SAI_CTRL_1_FSYNC | + SSM4567_SAI_CTRL_1_LJ | + SSM4567_SAI_CTRL_1_TDM | + SSM4567_SAI_CTRL_1_PDM, + ctrl1); +} + +static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable) +{ + int ret = 0; + + if (!enable) { + ret = regmap_update_bits(ssm4567->regmap, + SSM4567_REG_POWER_CTRL, + SSM4567_POWER_SPWDN, SSM4567_POWER_SPWDN); + regcache_mark_dirty(ssm4567->regmap); + } + + regcache_cache_only(ssm4567->regmap, !enable); + + if (enable) { + ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET, + 0x00); + if (ret) + return ret; + + ret = regmap_update_bits(ssm4567->regmap, + SSM4567_REG_POWER_CTRL, + SSM4567_POWER_SPWDN, 0x00); + regcache_sync(ssm4567->regmap); + } + + return ret; +} + +static int ssm4567_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct ssm4567 *ssm4567 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + ret = ssm4567_set_power(ssm4567, true); + break; + case SND_SOC_BIAS_OFF: + ret = ssm4567_set_power(ssm4567, false); + break; + } + + return ret; +} + +static const struct snd_soc_dai_ops ssm4567_dai_ops = { + .hw_params = ssm4567_hw_params, + .mute_stream = ssm4567_mute, + .set_fmt = ssm4567_set_dai_fmt, + .set_tdm_slot = ssm4567_set_tdm_slot, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver ssm4567_dai = { + .name = "ssm4567-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32, + }, + .capture = { + .stream_name = "Capture Sense", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32, + }, + .ops = &ssm4567_dai_ops, +}; + +static const struct snd_soc_component_driver ssm4567_component_driver = { + .set_bias_level = ssm4567_set_bias_level, + .controls = ssm4567_snd_controls, + .num_controls = ARRAY_SIZE(ssm4567_snd_controls), + .dapm_widgets = ssm4567_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm4567_dapm_widgets), + .dapm_routes = ssm4567_routes, + .num_dapm_routes = ARRAY_SIZE(ssm4567_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config ssm4567_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + + .max_register = SSM4567_REG_SOFT_RESET, + .readable_reg = ssm4567_readable_reg, + .writeable_reg = ssm4567_writeable_reg, + .volatile_reg = ssm4567_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ssm4567_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults), +}; + +static int ssm4567_i2c_probe(struct i2c_client *i2c) +{ + struct ssm4567 *ssm4567; + int ret; + + ssm4567 = devm_kzalloc(&i2c->dev, sizeof(*ssm4567), GFP_KERNEL); + if (ssm4567 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, ssm4567); + + ssm4567->regmap = devm_regmap_init_i2c(i2c, &ssm4567_regmap_config); + if (IS_ERR(ssm4567->regmap)) + return PTR_ERR(ssm4567->regmap); + + ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET, 0x00); + if (ret) + return ret; + + ret = ssm4567_set_power(ssm4567, false); + if (ret) + return ret; + + return devm_snd_soc_register_component(&i2c->dev, &ssm4567_component_driver, + &ssm4567_dai, 1); +} + +static const struct i2c_device_id ssm4567_i2c_ids[] = { + { "ssm4567", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids); + +#ifdef CONFIG_OF +static const struct of_device_id ssm4567_of_match[] = { + { .compatible = "adi,ssm4567", }, + { } +}; +MODULE_DEVICE_TABLE(of, ssm4567_of_match); +#endif + +#ifdef CONFIG_ACPI + +static const struct acpi_device_id ssm4567_acpi_match[] = { + { "INT343B", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, ssm4567_acpi_match); + +#endif + +static struct i2c_driver ssm4567_driver = { + .driver = { + .name = "ssm4567", + .of_match_table = of_match_ptr(ssm4567_of_match), + .acpi_match_table = ACPI_PTR(ssm4567_acpi_match), + }, + .probe = ssm4567_i2c_probe, + .id_table = ssm4567_i2c_ids, +}; +module_i2c_driver(ssm4567_driver); + +MODULE_DESCRIPTION("ASoC SSM4567 driver"); +MODULE_AUTHOR("Anatol Pomozov "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c new file mode 100644 index 0000000000..34ffd32ab9 --- /dev/null +++ b/sound/soc/codecs/sta32x.c @@ -0,0 +1,1178 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system + * + * Copyright: 2011 Raumfeld GmbH + * Author: Johannes Stezenbach + * + * based on code from: + * Wolfson Microelectronics PLC. + * Mark Brown + * Freescale Semiconductor, Inc. + * Timur Tabi + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "sta32x.h" + +#define STA32X_RATES (SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) + +#define STA32X_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +/* Power-up register defaults */ +static const struct reg_default sta32x_regs[] = { + { 0x0, 0x63 }, + { 0x1, 0x80 }, + { 0x2, 0xc2 }, + { 0x3, 0x40 }, + { 0x4, 0xc2 }, + { 0x5, 0x5c }, + { 0x6, 0x10 }, + { 0x7, 0xff }, + { 0x8, 0x60 }, + { 0x9, 0x60 }, + { 0xa, 0x60 }, + { 0xb, 0x80 }, + { 0xc, 0x00 }, + { 0xd, 0x00 }, + { 0xe, 0x00 }, + { 0xf, 0x40 }, + { 0x10, 0x80 }, + { 0x11, 0x77 }, + { 0x12, 0x6a }, + { 0x13, 0x69 }, + { 0x14, 0x6a }, + { 0x15, 0x69 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, + { 0x18, 0x00 }, + { 0x19, 0x00 }, + { 0x1a, 0x00 }, + { 0x1b, 0x00 }, + { 0x1c, 0x00 }, + { 0x1d, 0x00 }, + { 0x1e, 0x00 }, + { 0x1f, 0x00 }, + { 0x20, 0x00 }, + { 0x21, 0x00 }, + { 0x22, 0x00 }, + { 0x23, 0x00 }, + { 0x24, 0x00 }, + { 0x25, 0x00 }, + { 0x26, 0x00 }, + { 0x27, 0x2d }, + { 0x28, 0xc0 }, + { 0x2b, 0x00 }, + { 0x2c, 0x0c }, +}; + +static const struct regmap_range sta32x_write_regs_range[] = { + regmap_reg_range(STA32X_CONFA, STA32X_FDRC2), +}; + +static const struct regmap_range sta32x_read_regs_range[] = { + regmap_reg_range(STA32X_CONFA, STA32X_FDRC2), +}; + +static const struct regmap_range sta32x_volatile_regs_range[] = { + regmap_reg_range(STA32X_CFADDR2, STA32X_CFUD), +}; + +static const struct regmap_access_table sta32x_write_regs = { + .yes_ranges = sta32x_write_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta32x_write_regs_range), +}; + +static const struct regmap_access_table sta32x_read_regs = { + .yes_ranges = sta32x_read_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta32x_read_regs_range), +}; + +static const struct regmap_access_table sta32x_volatile_regs = { + .yes_ranges = sta32x_volatile_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta32x_volatile_regs_range), +}; + +/* regulator power supply names */ +static const char *sta32x_supply_names[] = { + "Vdda", /* analog supply, 3.3VV */ + "Vdd3", /* digital supply, 3.3V */ + "Vcc" /* power amp spply, 10V - 36V */ +}; + +/* codec private data */ +struct sta32x_priv { + struct regmap *regmap; + struct clk *xti_clk; + struct regulator_bulk_data supplies[ARRAY_SIZE(sta32x_supply_names)]; + struct snd_soc_component *component; + struct sta32x_platform_data *pdata; + + unsigned int mclk; + unsigned int format; + + u32 coef_shadow[STA32X_COEF_COUNT]; + struct delayed_work watchdog_work; + int shutdown; + struct gpio_desc *gpiod_nreset; + struct mutex coeff_lock; +}; + +static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1); +static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1); +static const DECLARE_TLV_DB_SCALE(tone_tlv, -120, 200, 0); + +static const char *sta32x_drc_ac[] = { + "Anti-Clipping", "Dynamic Range Compression" }; +static const char *sta32x_auto_eq_mode[] = { + "User", "Preset", "Loudness" }; +static const char *sta32x_auto_gc_mode[] = { + "User", "AC no clipping", "AC limited clipping (10%)", + "DRC nighttime listening mode" }; +static const char *sta32x_auto_xo_mode[] = { + "User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz", "200Hz", + "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz", "340Hz", "360Hz" }; +static const char *sta32x_preset_eq_mode[] = { + "Flat", "Rock", "Soft Rock", "Jazz", "Classical", "Dance", "Pop", "Soft", + "Hard", "Party", "Vocal", "Hip-Hop", "Dialog", "Bass-boost #1", + "Bass-boost #2", "Bass-boost #3", "Loudness 1", "Loudness 2", + "Loudness 3", "Loudness 4", "Loudness 5", "Loudness 6", "Loudness 7", + "Loudness 8", "Loudness 9", "Loudness 10", "Loudness 11", "Loudness 12", + "Loudness 13", "Loudness 14", "Loudness 15", "Loudness 16" }; +static const char *sta32x_limiter_select[] = { + "Limiter Disabled", "Limiter #1", "Limiter #2" }; +static const char *sta32x_limiter_attack_rate[] = { + "3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024", + "0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752", + "0.0645", "0.0564", "0.0501", "0.0451" }; +static const char *sta32x_limiter_release_rate[] = { + "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299", + "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137", + "0.0134", "0.0117", "0.0110", "0.0104" }; +static DECLARE_TLV_DB_RANGE(sta32x_limiter_ac_attack_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 8, 16, TLV_DB_SCALE_ITEM(300, 100, 0), +); + +static DECLARE_TLV_DB_RANGE(sta32x_limiter_ac_release_tlv, + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0), + 3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0), + 8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0), +); + +static DECLARE_TLV_DB_RANGE(sta32x_limiter_drc_attack_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0), + 8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0), + 14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0), +); + +static DECLARE_TLV_DB_RANGE(sta32x_limiter_drc_release_tlv, + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0), + 3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0), + 5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0), + 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0), +); + +static SOC_ENUM_SINGLE_DECL(sta32x_drc_ac_enum, + STA32X_CONFD, STA32X_CONFD_DRC_SHIFT, + sta32x_drc_ac); +static SOC_ENUM_SINGLE_DECL(sta32x_auto_eq_enum, + STA32X_AUTO1, STA32X_AUTO1_AMEQ_SHIFT, + sta32x_auto_eq_mode); +static SOC_ENUM_SINGLE_DECL(sta32x_auto_gc_enum, + STA32X_AUTO1, STA32X_AUTO1_AMGC_SHIFT, + sta32x_auto_gc_mode); +static SOC_ENUM_SINGLE_DECL(sta32x_auto_xo_enum, + STA32X_AUTO2, STA32X_AUTO2_XO_SHIFT, + sta32x_auto_xo_mode); +static SOC_ENUM_SINGLE_DECL(sta32x_preset_eq_enum, + STA32X_AUTO3, STA32X_AUTO3_PEQ_SHIFT, + sta32x_preset_eq_mode); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch1_enum, + STA32X_C1CFG, STA32X_CxCFG_LS_SHIFT, + sta32x_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch2_enum, + STA32X_C2CFG, STA32X_CxCFG_LS_SHIFT, + sta32x_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter_ch3_enum, + STA32X_C3CFG, STA32X_CxCFG_LS_SHIFT, + sta32x_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter1_attack_rate_enum, + STA32X_L1AR, STA32X_LxA_SHIFT, + sta32x_limiter_attack_rate); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter2_attack_rate_enum, + STA32X_L2AR, STA32X_LxA_SHIFT, + sta32x_limiter_attack_rate); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter1_release_rate_enum, + STA32X_L1AR, STA32X_LxR_SHIFT, + sta32x_limiter_release_rate); +static SOC_ENUM_SINGLE_DECL(sta32x_limiter2_release_rate_enum, + STA32X_L2AR, STA32X_LxR_SHIFT, + sta32x_limiter_release_rate); + +/* byte array controls for setting biquad, mixer, scaling coefficients; + * for biquads all five coefficients need to be set in one go, + * mixer and pre/postscale coefs can be set individually; + * each coef is 24bit, the bytes are ordered in the same way + * as given in the STA32x data sheet (big endian; b1, b2, a1, a2, b0) + */ + +static int sta32x_coefficient_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int numcoef = kcontrol->private_value >> 16; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 3 * numcoef; + return 0; +} + +static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud, val; + int i, ret = 0; + + mutex_lock(&sta32x->coeff_lock); + + /* preserve reserved bits in STA32X_CFUD */ + regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); + cfud &= 0xf0; + /* + * chip documentation does not say if the bits are self clearing, + * so do it explicitly + */ + regmap_write(sta32x->regmap, STA32X_CFUD, cfud); + + regmap_write(sta32x->regmap, STA32X_CFADDR2, index); + if (numcoef == 1) { + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04); + } else if (numcoef == 5) { + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08); + } else { + ret = -EINVAL; + goto exit_unlock; + } + + for (i = 0; i < 3 * numcoef; i++) { + regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val); + ucontrol->value.bytes.data[i] = val; + } + +exit_unlock: + mutex_unlock(&sta32x->coeff_lock); + + return ret; +} + +static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud; + int i; + + /* preserve reserved bits in STA32X_CFUD */ + regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); + cfud &= 0xf0; + /* + * chip documentation does not say if the bits are self clearing, + * so do it explicitly + */ + regmap_write(sta32x->regmap, STA32X_CFUD, cfud); + + regmap_write(sta32x->regmap, STA32X_CFADDR2, index); + for (i = 0; i < numcoef && (index + i < STA32X_COEF_COUNT); i++) + sta32x->coef_shadow[index + i] = + (ucontrol->value.bytes.data[3 * i] << 16) + | (ucontrol->value.bytes.data[3 * i + 1] << 8) + | (ucontrol->value.bytes.data[3 * i + 2]); + for (i = 0; i < 3 * numcoef; i++) + regmap_write(sta32x->regmap, STA32X_B1CF1 + i, + ucontrol->value.bytes.data[i]); + if (numcoef == 1) + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01); + else if (numcoef == 5) + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x02); + else + return -EINVAL; + + return 0; +} + +static int sta32x_sync_coef_shadow(struct snd_soc_component *component) +{ + struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); + unsigned int cfud; + int i; + + /* preserve reserved bits in STA32X_CFUD */ + regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); + cfud &= 0xf0; + + for (i = 0; i < STA32X_COEF_COUNT; i++) { + regmap_write(sta32x->regmap, STA32X_CFADDR2, i); + regmap_write(sta32x->regmap, STA32X_B1CF1, + (sta32x->coef_shadow[i] >> 16) & 0xff); + regmap_write(sta32x->regmap, STA32X_B1CF2, + (sta32x->coef_shadow[i] >> 8) & 0xff); + regmap_write(sta32x->regmap, STA32X_B1CF3, + (sta32x->coef_shadow[i]) & 0xff); + /* + * chip documentation does not say if the bits are + * self-clearing, so do it explicitly + */ + regmap_write(sta32x->regmap, STA32X_CFUD, cfud); + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01); + } + return 0; +} + +static int sta32x_cache_sync(struct snd_soc_component *component) +{ + struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); + unsigned int mute; + int rc; + + /* mute during register sync */ + regmap_read(sta32x->regmap, STA32X_MMUTE, &mute); + regmap_write(sta32x->regmap, STA32X_MMUTE, mute | STA32X_MMUTE_MMUTE); + sta32x_sync_coef_shadow(component); + rc = regcache_sync(sta32x->regmap); + regmap_write(sta32x->regmap, STA32X_MMUTE, mute); + return rc; +} + +/* work around ESD issue where sta32x resets and loses all configuration */ +static void sta32x_watchdog(struct work_struct *work) +{ + struct sta32x_priv *sta32x = container_of(work, struct sta32x_priv, + watchdog_work.work); + struct snd_soc_component *component = sta32x->component; + unsigned int confa, confa_cached; + + /* check if sta32x has reset itself */ + confa_cached = snd_soc_component_read(component, STA32X_CONFA); + regcache_cache_bypass(sta32x->regmap, true); + confa = snd_soc_component_read(component, STA32X_CONFA); + regcache_cache_bypass(sta32x->regmap, false); + if (confa != confa_cached) { + regcache_mark_dirty(sta32x->regmap); + sta32x_cache_sync(component); + } + + if (!sta32x->shutdown) + queue_delayed_work(system_power_efficient_wq, + &sta32x->watchdog_work, + round_jiffies_relative(HZ)); +} + +static void sta32x_watchdog_start(struct sta32x_priv *sta32x) +{ + if (sta32x->pdata->needs_esd_watchdog) { + sta32x->shutdown = 0; + queue_delayed_work(system_power_efficient_wq, + &sta32x->watchdog_work, + round_jiffies_relative(HZ)); + } +} + +static void sta32x_watchdog_stop(struct sta32x_priv *sta32x) +{ + if (sta32x->pdata->needs_esd_watchdog) { + sta32x->shutdown = 1; + cancel_delayed_work_sync(&sta32x->watchdog_work); + } +} + +#define SINGLE_COEF(xname, index) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sta32x_coefficient_info, \ + .get = sta32x_coefficient_get,\ + .put = sta32x_coefficient_put, \ + .private_value = index | (1 << 16) } + +#define BIQUAD_COEFS(xname, index) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sta32x_coefficient_info, \ + .get = sta32x_coefficient_get,\ + .put = sta32x_coefficient_put, \ + .private_value = index | (5 << 16) } + +static const struct snd_kcontrol_new sta32x_snd_controls[] = { +SOC_SINGLE_TLV("Master Volume", STA32X_MVOL, 0, 0xff, 1, mvol_tlv), +SOC_SINGLE("Master Switch", STA32X_MMUTE, 0, 1, 1), +SOC_SINGLE("Ch1 Switch", STA32X_MMUTE, 1, 1, 1), +SOC_SINGLE("Ch2 Switch", STA32X_MMUTE, 2, 1, 1), +SOC_SINGLE("Ch3 Switch", STA32X_MMUTE, 3, 1, 1), +SOC_SINGLE_TLV("Ch1 Volume", STA32X_C1VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE_TLV("Ch2 Volume", STA32X_C2VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE_TLV("Ch3 Volume", STA32X_C3VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE("De-emphasis Filter Switch", STA32X_CONFD, STA32X_CONFD_DEMP_SHIFT, 1, 0), +SOC_ENUM("Compressor/Limiter Switch", sta32x_drc_ac_enum), +SOC_SINGLE("Miami Mode Switch", STA32X_CONFD, STA32X_CONFD_MME_SHIFT, 1, 0), +SOC_SINGLE("Zero Cross Switch", STA32X_CONFE, STA32X_CONFE_ZCE_SHIFT, 1, 0), +SOC_SINGLE("Soft Ramp Switch", STA32X_CONFE, STA32X_CONFE_SVE_SHIFT, 1, 0), +SOC_SINGLE("Auto-Mute Switch", STA32X_CONFF, STA32X_CONFF_IDE_SHIFT, 1, 0), +SOC_ENUM("Automode EQ", sta32x_auto_eq_enum), +SOC_ENUM("Automode GC", sta32x_auto_gc_enum), +SOC_ENUM("Automode XO", sta32x_auto_xo_enum), +SOC_ENUM("Preset EQ", sta32x_preset_eq_enum), +SOC_SINGLE("Ch1 Tone Control Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0), +SOC_SINGLE("Ch2 Tone Control Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_TCB_SHIFT, 1, 0), +SOC_SINGLE("Ch1 EQ Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0), +SOC_SINGLE("Ch2 EQ Bypass Switch", STA32X_C2CFG, STA32X_CxCFG_EQBP_SHIFT, 1, 0), +SOC_SINGLE("Ch1 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0), +SOC_SINGLE("Ch2 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0), +SOC_SINGLE("Ch3 Master Volume Bypass Switch", STA32X_C1CFG, STA32X_CxCFG_VBP_SHIFT, 1, 0), +SOC_ENUM("Ch1 Limiter Select", sta32x_limiter_ch1_enum), +SOC_ENUM("Ch2 Limiter Select", sta32x_limiter_ch2_enum), +SOC_ENUM("Ch3 Limiter Select", sta32x_limiter_ch3_enum), +SOC_SINGLE_TLV("Bass Tone Control", STA32X_TONE, STA32X_TONE_BTC_SHIFT, 15, 0, tone_tlv), +SOC_SINGLE_TLV("Treble Tone Control", STA32X_TONE, STA32X_TONE_TTC_SHIFT, 15, 0, tone_tlv), +SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta32x_limiter1_attack_rate_enum), +SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta32x_limiter2_attack_rate_enum), +SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta32x_limiter1_release_rate_enum), +SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta32x_limiter2_release_rate_enum), + +/* depending on mode, the attack/release thresholds have + * two different enum definitions; provide both + */ +SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT, + 16, 0, sta32x_limiter_ac_attack_tlv), +SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT, + 16, 0, sta32x_limiter_ac_attack_tlv), +SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT, + 16, 0, sta32x_limiter_ac_release_tlv), +SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT, + 16, 0, sta32x_limiter_ac_release_tlv), +SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxA_SHIFT, + 16, 0, sta32x_limiter_drc_attack_tlv), +SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxA_SHIFT, + 16, 0, sta32x_limiter_drc_attack_tlv), +SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)", STA32X_L1ATRT, STA32X_LxR_SHIFT, + 16, 0, sta32x_limiter_drc_release_tlv), +SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)", STA32X_L2ATRT, STA32X_LxR_SHIFT, + 16, 0, sta32x_limiter_drc_release_tlv), + +BIQUAD_COEFS("Ch1 - Biquad 1", 0), +BIQUAD_COEFS("Ch1 - Biquad 2", 5), +BIQUAD_COEFS("Ch1 - Biquad 3", 10), +BIQUAD_COEFS("Ch1 - Biquad 4", 15), +BIQUAD_COEFS("Ch2 - Biquad 1", 20), +BIQUAD_COEFS("Ch2 - Biquad 2", 25), +BIQUAD_COEFS("Ch2 - Biquad 3", 30), +BIQUAD_COEFS("Ch2 - Biquad 4", 35), +BIQUAD_COEFS("High-pass", 40), +BIQUAD_COEFS("Low-pass", 45), +SINGLE_COEF("Ch1 - Prescale", 50), +SINGLE_COEF("Ch2 - Prescale", 51), +SINGLE_COEF("Ch1 - Postscale", 52), +SINGLE_COEF("Ch2 - Postscale", 53), +SINGLE_COEF("Ch3 - Postscale", 54), +SINGLE_COEF("Thermal warning - Postscale", 55), +SINGLE_COEF("Ch1 - Mix 1", 56), +SINGLE_COEF("Ch1 - Mix 2", 57), +SINGLE_COEF("Ch2 - Mix 1", 58), +SINGLE_COEF("Ch2 - Mix 2", 59), +SINGLE_COEF("Ch3 - Mix 1", 60), +SINGLE_COEF("Ch3 - Mix 2", 61), +}; + +static const struct snd_soc_dapm_widget sta32x_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("LEFT"), +SND_SOC_DAPM_OUTPUT("RIGHT"), +SND_SOC_DAPM_OUTPUT("SUB"), +}; + +static const struct snd_soc_dapm_route sta32x_dapm_routes[] = { + { "LEFT", NULL, "DAC" }, + { "RIGHT", NULL, "DAC" }, + { "SUB", NULL, "DAC" }, +}; + +/* MCLK interpolation ratio per fs */ +static struct { + int fs; + int ir; +} interpolation_ratios[] = { + { 32000, 0 }, + { 44100, 0 }, + { 48000, 0 }, + { 88200, 1 }, + { 96000, 1 }, + { 176400, 2 }, + { 192000, 2 }, +}; + +/* MCLK to fs clock ratios */ +static int mcs_ratio_table[3][7] = { + { 768, 512, 384, 256, 128, 576, 0 }, + { 384, 256, 192, 128, 64, 0 }, + { 384, 256, 192, 128, 64, 0 }, +}; + +/** + * sta32x_set_dai_sysclk - configure MCLK + * @codec_dai: the codec DAI + * @clk_id: the clock ID (ignored) + * @freq: the MCLK input frequency + * @dir: the clock direction (ignored) + * + * The value of MCLK is used to determine which sample rates are supported + * by the STA32X, based on the mclk_ratios table. + * + * This function must be called by the machine driver's 'startup' function, + * otherwise the list of supported sample rates will not be available in + * time for ALSA. + * + * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause + * theoretically possible sample rates to be enabled. Call it again with a + * proper value set one the external clock is set (most probably you would do + * that from a machine's driver 'hw_param' hook. + */ +static int sta32x_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "mclk=%u\n", freq); + sta32x->mclk = freq; + + return 0; +} + +/** + * sta32x_set_dai_fmt - configure the codec for the selected audio format + * @codec_dai: the codec DAI + * @fmt: a SND_SOC_DAIFMT_x value indicating the data format + * + * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the + * codec accordingly. + */ +static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); + u8 confb = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + sta32x->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + confb |= STA32X_CONFB_C2IM; + break; + case SND_SOC_DAIFMT_NB_IF: + confb |= STA32X_CONFB_C1IM; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(sta32x->regmap, STA32X_CONFB, + STA32X_CONFB_C1IM | STA32X_CONFB_C2IM, confb); +} + +/** + * sta32x_hw_params - program the STA32X with the given hardware parameters. + * @substream: the audio stream + * @params: the hardware parameters to set + * @dai: the SOC DAI (ignored) + * + * This function programs the hardware with the values provided. + * Specifically, the sample rate and the data format. + */ +static int sta32x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); + int i, mcs = -EINVAL, ir = -EINVAL; + unsigned int confa, confb; + unsigned int rate, ratio; + int ret; + + if (!sta32x->mclk) { + dev_err(component->dev, + "sta32x->mclk is unset. Unable to determine ratio\n"); + return -EIO; + } + + rate = params_rate(params); + ratio = sta32x->mclk / rate; + dev_dbg(component->dev, "rate: %u, ratio: %u\n", rate, ratio); + + for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) { + if (interpolation_ratios[i].fs == rate) { + ir = interpolation_ratios[i].ir; + break; + } + } + + if (ir < 0) { + dev_err(component->dev, "Unsupported samplerate: %u\n", rate); + return -EINVAL; + } + + for (i = 0; i < 6; i++) { + if (mcs_ratio_table[ir][i] == ratio) { + mcs = i; + break; + } + } + + if (mcs < 0) { + dev_err(component->dev, "Unresolvable ratio: %u\n", ratio); + return -EINVAL; + } + + confa = (ir << STA32X_CONFA_IR_SHIFT) | + (mcs << STA32X_CONFA_MCS_SHIFT); + confb = 0; + + switch (params_width(params)) { + case 24: + dev_dbg(component->dev, "24bit\n"); + fallthrough; + case 32: + dev_dbg(component->dev, "24bit or 32bit\n"); + switch (sta32x->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x1; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0x2; + break; + } + + break; + case 20: + dev_dbg(component->dev, "20bit\n"); + switch (sta32x->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x4; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x5; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0x6; + break; + } + + break; + case 18: + dev_dbg(component->dev, "18bit\n"); + switch (sta32x->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x8; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x9; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0xa; + break; + } + + break; + case 16: + dev_dbg(component->dev, "16bit\n"); + switch (sta32x->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0xd; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0xe; + break; + } + + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(sta32x->regmap, STA32X_CONFA, + STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK, + confa); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sta32x->regmap, STA32X_CONFB, + STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB, + confb); + if (ret < 0) + return ret; + + return 0; +} + +static int sta32x_startup_sequence(struct sta32x_priv *sta32x) +{ + if (sta32x->gpiod_nreset) { + gpiod_set_value(sta32x->gpiod_nreset, 0); + mdelay(1); + gpiod_set_value(sta32x->gpiod_nreset, 1); + mdelay(1); + } + + return 0; +} + +/** + * sta32x_set_bias_level - DAPM callback + * @component: the component device + * @level: DAPM power level + * + * This is called by ALSA to put the component into low power mode + * or to wake it up. If the component is powered off completely + * all registers must be restored after power on. + */ +static int sta32x_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int ret; + struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "level = %d\n", level); + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* Full power on */ + regmap_update_bits(sta32x->regmap, STA32X_CONFF, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies), + sta32x->supplies); + if (ret != 0) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", ret); + return ret; + } + + sta32x_startup_sequence(sta32x); + sta32x_cache_sync(component); + sta32x_watchdog_start(sta32x); + } + + /* Power down */ + regmap_update_bits(sta32x->regmap, STA32X_CONFF, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, + 0); + + break; + + case SND_SOC_BIAS_OFF: + /* The chip runs through the power down sequence for us. */ + regmap_update_bits(sta32x->regmap, STA32X_CONFF, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, 0); + msleep(300); + sta32x_watchdog_stop(sta32x); + + gpiod_set_value(sta32x->gpiod_nreset, 0); + + regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), + sta32x->supplies); + break; + } + return 0; +} + +static const struct snd_soc_dai_ops sta32x_dai_ops = { + .hw_params = sta32x_hw_params, + .set_sysclk = sta32x_set_dai_sysclk, + .set_fmt = sta32x_set_dai_fmt, +}; + +static struct snd_soc_dai_driver sta32x_dai = { + .name = "sta32x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = STA32X_RATES, + .formats = STA32X_FORMATS, + }, + .ops = &sta32x_dai_ops, +}; + +static int sta32x_probe(struct snd_soc_component *component) +{ + struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); + struct sta32x_platform_data *pdata = sta32x->pdata; + int i, ret = 0, thermal = 0; + + sta32x->component = component; + + if (sta32x->xti_clk) { + ret = clk_prepare_enable(sta32x->xti_clk); + if (ret != 0) { + dev_err(component->dev, + "Failed to enable clock: %d\n", ret); + return ret; + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies), + sta32x->supplies); + if (ret != 0) { + dev_err(component->dev, "Failed to enable supplies: %d\n", ret); + goto err_clk_disable_unprepare; + } + + ret = sta32x_startup_sequence(sta32x); + if (ret < 0) { + dev_err(component->dev, "Failed to startup device\n"); + goto err_regulator_bulk_disable; + } + + /* CONFA */ + if (!pdata->thermal_warning_recovery) + thermal |= STA32X_CONFA_TWAB; + if (!pdata->thermal_warning_adjustment) + thermal |= STA32X_CONFA_TWRB; + if (!pdata->fault_detect_recovery) + thermal |= STA32X_CONFA_FDRB; + regmap_update_bits(sta32x->regmap, STA32X_CONFA, + STA32X_CONFA_TWAB | STA32X_CONFA_TWRB | + STA32X_CONFA_FDRB, + thermal); + + /* CONFC */ + regmap_update_bits(sta32x->regmap, STA32X_CONFC, + STA32X_CONFC_CSZ_MASK, + pdata->drop_compensation_ns + << STA32X_CONFC_CSZ_SHIFT); + + /* CONFE */ + regmap_update_bits(sta32x->regmap, STA32X_CONFE, + STA32X_CONFE_MPCV, + pdata->max_power_use_mpcc ? + STA32X_CONFE_MPCV : 0); + regmap_update_bits(sta32x->regmap, STA32X_CONFE, + STA32X_CONFE_MPC, + pdata->max_power_correction ? + STA32X_CONFE_MPC : 0); + regmap_update_bits(sta32x->regmap, STA32X_CONFE, + STA32X_CONFE_AME, + pdata->am_reduction_mode ? + STA32X_CONFE_AME : 0); + regmap_update_bits(sta32x->regmap, STA32X_CONFE, + STA32X_CONFE_PWMS, + pdata->odd_pwm_speed_mode ? + STA32X_CONFE_PWMS : 0); + + /* CONFF */ + regmap_update_bits(sta32x->regmap, STA32X_CONFF, + STA32X_CONFF_IDE, + pdata->invalid_input_detect_mute ? + STA32X_CONFF_IDE : 0); + + /* select output configuration */ + regmap_update_bits(sta32x->regmap, STA32X_CONFF, + STA32X_CONFF_OCFG_MASK, + pdata->output_conf + << STA32X_CONFF_OCFG_SHIFT); + + /* channel to output mapping */ + regmap_update_bits(sta32x->regmap, STA32X_C1CFG, + STA32X_CxCFG_OM_MASK, + pdata->ch1_output_mapping + << STA32X_CxCFG_OM_SHIFT); + regmap_update_bits(sta32x->regmap, STA32X_C2CFG, + STA32X_CxCFG_OM_MASK, + pdata->ch2_output_mapping + << STA32X_CxCFG_OM_SHIFT); + regmap_update_bits(sta32x->regmap, STA32X_C3CFG, + STA32X_CxCFG_OM_MASK, + pdata->ch3_output_mapping + << STA32X_CxCFG_OM_SHIFT); + + /* initialize coefficient shadow RAM with reset values */ + for (i = 4; i <= 49; i += 5) + sta32x->coef_shadow[i] = 0x400000; + for (i = 50; i <= 54; i++) + sta32x->coef_shadow[i] = 0x7fffff; + sta32x->coef_shadow[55] = 0x5a9df7; + sta32x->coef_shadow[56] = 0x7fffff; + sta32x->coef_shadow[59] = 0x7fffff; + sta32x->coef_shadow[60] = 0x400000; + sta32x->coef_shadow[61] = 0x400000; + + if (sta32x->pdata->needs_esd_watchdog) + INIT_DELAYED_WORK(&sta32x->watchdog_work, sta32x_watchdog); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + /* Bias level configuration will have done an extra enable */ + regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); + + return 0; + +err_regulator_bulk_disable: + regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); +err_clk_disable_unprepare: + if (sta32x->xti_clk) + clk_disable_unprepare(sta32x->xti_clk); + return ret; +} + +static void sta32x_remove(struct snd_soc_component *component) +{ + struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); + + sta32x_watchdog_stop(sta32x); + regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); + + if (sta32x->xti_clk) + clk_disable_unprepare(sta32x->xti_clk); +} + +static const struct snd_soc_component_driver sta32x_component = { + .probe = sta32x_probe, + .remove = sta32x_remove, + .set_bias_level = sta32x_set_bias_level, + .controls = sta32x_snd_controls, + .num_controls = ARRAY_SIZE(sta32x_snd_controls), + .dapm_widgets = sta32x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sta32x_dapm_widgets), + .dapm_routes = sta32x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sta32x_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config sta32x_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = STA32X_FDRC2, + .reg_defaults = sta32x_regs, + .num_reg_defaults = ARRAY_SIZE(sta32x_regs), + .cache_type = REGCACHE_MAPLE, + .wr_table = &sta32x_write_regs, + .rd_table = &sta32x_read_regs, + .volatile_table = &sta32x_volatile_regs, +}; + +#ifdef CONFIG_OF +static const struct of_device_id st32x_dt_ids[] = { + { .compatible = "st,sta32x", }, + { } +}; +MODULE_DEVICE_TABLE(of, st32x_dt_ids); + +static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x) +{ + struct device_node *np = dev->of_node; + struct sta32x_platform_data *pdata; + u16 tmp; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + of_property_read_u8(np, "st,output-conf", + &pdata->output_conf); + of_property_read_u8(np, "st,ch1-output-mapping", + &pdata->ch1_output_mapping); + of_property_read_u8(np, "st,ch2-output-mapping", + &pdata->ch2_output_mapping); + of_property_read_u8(np, "st,ch3-output-mapping", + &pdata->ch3_output_mapping); + + pdata->fault_detect_recovery = + of_property_read_bool(np, "st,fault-detect-recovery"); + pdata->thermal_warning_recovery = + of_property_read_bool(np, "st,thermal-warning-recovery"); + pdata->thermal_warning_adjustment = + of_property_read_bool(np, "st,thermal-warning-adjustment"); + pdata->needs_esd_watchdog = + of_property_read_bool(np, "st,needs_esd_watchdog"); + + tmp = 140; + of_property_read_u16(np, "st,drop-compensation-ns", &tmp); + pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20; + + /* CONFE */ + pdata->max_power_use_mpcc = + of_property_read_bool(np, "st,max-power-use-mpcc"); + pdata->max_power_correction = + of_property_read_bool(np, "st,max-power-correction"); + pdata->am_reduction_mode = + of_property_read_bool(np, "st,am-reduction-mode"); + pdata->odd_pwm_speed_mode = + of_property_read_bool(np, "st,odd-pwm-speed-mode"); + + /* CONFF */ + pdata->invalid_input_detect_mute = + of_property_read_bool(np, "st,invalid-input-detect-mute"); + + sta32x->pdata = pdata; + + return 0; +} +#endif + +static int sta32x_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct sta32x_priv *sta32x; + int ret, i; + + sta32x = devm_kzalloc(&i2c->dev, sizeof(struct sta32x_priv), + GFP_KERNEL); + if (!sta32x) + return -ENOMEM; + + mutex_init(&sta32x->coeff_lock); + sta32x->pdata = dev_get_platdata(dev); + +#ifdef CONFIG_OF + if (dev->of_node) { + ret = sta32x_probe_dt(dev, sta32x); + if (ret < 0) + return ret; + } +#endif + + /* Clock */ + sta32x->xti_clk = devm_clk_get(dev, "xti"); + if (IS_ERR(sta32x->xti_clk)) { + ret = PTR_ERR(sta32x->xti_clk); + + if (ret == -EPROBE_DEFER) + return ret; + + sta32x->xti_clk = NULL; + } + + /* GPIOs */ + sta32x->gpiod_nreset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(sta32x->gpiod_nreset)) + return PTR_ERR(sta32x->gpiod_nreset); + + /* regulators */ + for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++) + sta32x->supplies[i].supply = sta32x_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(sta32x->supplies), + sta32x->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + sta32x->regmap = devm_regmap_init_i2c(i2c, &sta32x_regmap); + if (IS_ERR(sta32x->regmap)) { + ret = PTR_ERR(sta32x->regmap); + dev_err(dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, sta32x); + + ret = devm_snd_soc_register_component(dev, &sta32x_component, + &sta32x_dai, 1); + if (ret < 0) + dev_err(dev, "Failed to register component (%d)\n", ret); + + return ret; +} + +static const struct i2c_device_id sta32x_i2c_id[] = { + { "sta326", 0 }, + { "sta328", 0 }, + { "sta329", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id); + +static struct i2c_driver sta32x_i2c_driver = { + .driver = { + .name = "sta32x", + .of_match_table = of_match_ptr(st32x_dt_ids), + }, + .probe = sta32x_i2c_probe, + .id_table = sta32x_i2c_id, +}; + +module_i2c_driver(sta32x_i2c_driver); + +MODULE_DESCRIPTION("ASoC STA32X driver"); +MODULE_AUTHOR("Johannes Stezenbach "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sta32x.h b/sound/soc/codecs/sta32x.h new file mode 100644 index 0000000000..1b90d7460b --- /dev/null +++ b/sound/soc/codecs/sta32x.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system + * + * Copyright: 2011 Raumfeld GmbH + * Author: Johannes Stezenbach + * + * based on code from: + * Wolfson Microelectronics PLC. + * Mark Brown + */ +#ifndef _ASOC_STA_32X_H +#define _ASOC_STA_32X_H + +/* STA326 register addresses */ + +#define STA32X_REGISTER_COUNT 0x2d +#define STA32X_COEF_COUNT 62 + +#define STA32X_CONFA 0x00 +#define STA32X_CONFB 0x01 +#define STA32X_CONFC 0x02 +#define STA32X_CONFD 0x03 +#define STA32X_CONFE 0x04 +#define STA32X_CONFF 0x05 +#define STA32X_MMUTE 0x06 +#define STA32X_MVOL 0x07 +#define STA32X_C1VOL 0x08 +#define STA32X_C2VOL 0x09 +#define STA32X_C3VOL 0x0a +#define STA32X_AUTO1 0x0b +#define STA32X_AUTO2 0x0c +#define STA32X_AUTO3 0x0d +#define STA32X_C1CFG 0x0e +#define STA32X_C2CFG 0x0f +#define STA32X_C3CFG 0x10 +#define STA32X_TONE 0x11 +#define STA32X_L1AR 0x12 +#define STA32X_L1ATRT 0x13 +#define STA32X_L2AR 0x14 +#define STA32X_L2ATRT 0x15 +#define STA32X_CFADDR2 0x16 +#define STA32X_B1CF1 0x17 +#define STA32X_B1CF2 0x18 +#define STA32X_B1CF3 0x19 +#define STA32X_B2CF1 0x1a +#define STA32X_B2CF2 0x1b +#define STA32X_B2CF3 0x1c +#define STA32X_A1CF1 0x1d +#define STA32X_A1CF2 0x1e +#define STA32X_A1CF3 0x1f +#define STA32X_A2CF1 0x20 +#define STA32X_A2CF2 0x21 +#define STA32X_A2CF3 0x22 +#define STA32X_B0CF1 0x23 +#define STA32X_B0CF2 0x24 +#define STA32X_B0CF3 0x25 +#define STA32X_CFUD 0x26 +#define STA32X_MPCC1 0x27 +#define STA32X_MPCC2 0x28 +/* Reserved 0x29 */ +/* Reserved 0x2a */ +#define STA32X_Reserved 0x2a +#define STA32X_FDRC1 0x2b +#define STA32X_FDRC2 0x2c +/* Reserved 0x2d */ + + +/* STA326 register field definitions */ + +/* 0x00 CONFA */ +#define STA32X_CONFA_MCS_MASK 0x03 +#define STA32X_CONFA_MCS_SHIFT 0 +#define STA32X_CONFA_IR_MASK 0x18 +#define STA32X_CONFA_IR_SHIFT 3 +#define STA32X_CONFA_TWRB 0x20 +#define STA32X_CONFA_TWAB 0x40 +#define STA32X_CONFA_FDRB 0x80 + +/* 0x01 CONFB */ +#define STA32X_CONFB_SAI_MASK 0x0f +#define STA32X_CONFB_SAI_SHIFT 0 +#define STA32X_CONFB_SAIFB 0x10 +#define STA32X_CONFB_DSCKE 0x20 +#define STA32X_CONFB_C1IM 0x40 +#define STA32X_CONFB_C2IM 0x80 + +/* 0x02 CONFC */ +#define STA32X_CONFC_OM_MASK 0x03 +#define STA32X_CONFC_OM_SHIFT 0 +#define STA32X_CONFC_CSZ_MASK 0x7c +#define STA32X_CONFC_CSZ_SHIFT 2 + +/* 0x03 CONFD */ +#define STA32X_CONFD_HPB 0x01 +#define STA32X_CONFD_HPB_SHIFT 0 +#define STA32X_CONFD_DEMP 0x02 +#define STA32X_CONFD_DEMP_SHIFT 1 +#define STA32X_CONFD_DSPB 0x04 +#define STA32X_CONFD_DSPB_SHIFT 2 +#define STA32X_CONFD_PSL 0x08 +#define STA32X_CONFD_PSL_SHIFT 3 +#define STA32X_CONFD_BQL 0x10 +#define STA32X_CONFD_BQL_SHIFT 4 +#define STA32X_CONFD_DRC 0x20 +#define STA32X_CONFD_DRC_SHIFT 5 +#define STA32X_CONFD_ZDE 0x40 +#define STA32X_CONFD_ZDE_SHIFT 6 +#define STA32X_CONFD_MME 0x80 +#define STA32X_CONFD_MME_SHIFT 7 + +/* 0x04 CONFE */ +#define STA32X_CONFE_MPCV 0x01 +#define STA32X_CONFE_MPCV_SHIFT 0 +#define STA32X_CONFE_MPC 0x02 +#define STA32X_CONFE_MPC_SHIFT 1 +#define STA32X_CONFE_AME 0x08 +#define STA32X_CONFE_AME_SHIFT 3 +#define STA32X_CONFE_PWMS 0x10 +#define STA32X_CONFE_PWMS_SHIFT 4 +#define STA32X_CONFE_ZCE 0x40 +#define STA32X_CONFE_ZCE_SHIFT 6 +#define STA32X_CONFE_SVE 0x80 +#define STA32X_CONFE_SVE_SHIFT 7 + +/* 0x05 CONFF */ +#define STA32X_CONFF_OCFG_MASK 0x03 +#define STA32X_CONFF_OCFG_SHIFT 0 +#define STA32X_CONFF_IDE 0x04 +#define STA32X_CONFF_IDE_SHIFT 2 +#define STA32X_CONFF_BCLE 0x08 +#define STA32X_CONFF_ECLE 0x20 +#define STA32X_CONFF_PWDN 0x40 +#define STA32X_CONFF_EAPD 0x80 + +/* 0x06 MMUTE */ +#define STA32X_MMUTE_MMUTE 0x01 + +/* 0x0b AUTO1 */ +#define STA32X_AUTO1_AMEQ_MASK 0x03 +#define STA32X_AUTO1_AMEQ_SHIFT 0 +#define STA32X_AUTO1_AMV_MASK 0xc0 +#define STA32X_AUTO1_AMV_SHIFT 2 +#define STA32X_AUTO1_AMGC_MASK 0x30 +#define STA32X_AUTO1_AMGC_SHIFT 4 +#define STA32X_AUTO1_AMPS 0x80 + +/* 0x0c AUTO2 */ +#define STA32X_AUTO2_AMAME 0x01 +#define STA32X_AUTO2_AMAM_MASK 0x0e +#define STA32X_AUTO2_AMAM_SHIFT 1 +#define STA32X_AUTO2_XO_MASK 0xf0 +#define STA32X_AUTO2_XO_SHIFT 4 + +/* 0x0d AUTO3 */ +#define STA32X_AUTO3_PEQ_MASK 0x1f +#define STA32X_AUTO3_PEQ_SHIFT 0 + +/* 0x0e 0x0f 0x10 CxCFG */ +#define STA32X_CxCFG_TCB 0x01 /* only C1 and C2 */ +#define STA32X_CxCFG_TCB_SHIFT 0 +#define STA32X_CxCFG_EQBP 0x02 /* only C1 and C2 */ +#define STA32X_CxCFG_EQBP_SHIFT 1 +#define STA32X_CxCFG_VBP 0x03 +#define STA32X_CxCFG_VBP_SHIFT 2 +#define STA32X_CxCFG_BO 0x04 +#define STA32X_CxCFG_LS_MASK 0x30 +#define STA32X_CxCFG_LS_SHIFT 4 +#define STA32X_CxCFG_OM_MASK 0xc0 +#define STA32X_CxCFG_OM_SHIFT 6 + +/* 0x11 TONE */ +#define STA32X_TONE_BTC_SHIFT 0 +#define STA32X_TONE_TTC_SHIFT 4 + +/* 0x12 0x13 0x14 0x15 limiter attack/release */ +#define STA32X_LxA_SHIFT 0 +#define STA32X_LxR_SHIFT 4 + +/* 0x26 CFUD */ +#define STA32X_CFUD_W1 0x01 +#define STA32X_CFUD_WA 0x02 +#define STA32X_CFUD_R1 0x04 +#define STA32X_CFUD_RA 0x08 + + +/* biquad filter coefficient table offsets */ +#define STA32X_C1_BQ_BASE 0 +#define STA32X_C2_BQ_BASE 20 +#define STA32X_CH_BQ_NUM 4 +#define STA32X_BQ_NUM_COEF 5 +#define STA32X_XO_HP_BQ_BASE 40 +#define STA32X_XO_LP_BQ_BASE 45 +#define STA32X_C1_PRESCALE 50 +#define STA32X_C2_PRESCALE 51 +#define STA32X_C1_POSTSCALE 52 +#define STA32X_C2_POSTSCALE 53 +#define STA32X_C3_POSTSCALE 54 +#define STA32X_TW_POSTSCALE 55 +#define STA32X_C1_MIX1 56 +#define STA32X_C1_MIX2 57 +#define STA32X_C2_MIX1 58 +#define STA32X_C2_MIX2 59 +#define STA32X_C3_MIX1 60 +#define STA32X_C3_MIX2 61 + +#endif /* _ASOC_STA_32X_H */ diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c new file mode 100644 index 0000000000..e4a9e9241c --- /dev/null +++ b/sound/soc/codecs/sta350.c @@ -0,0 +1,1261 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system + * + * Copyright: 2014 Raumfeld GmbH + * Author: Sven Brandau + * + * based on code from: + * Raumfeld GmbH + * Johannes Stezenbach + * Wolfson Microelectronics PLC. + * Mark Brown + * Freescale Semiconductor, Inc. + * Timur Tabi + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "sta350.h" + +#define STA350_RATES (SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) + +#define STA350_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +/* Power-up register defaults */ +static const struct reg_default sta350_regs[] = { + { 0x0, 0x63 }, + { 0x1, 0x80 }, + { 0x2, 0xdf }, + { 0x3, 0x40 }, + { 0x4, 0xc2 }, + { 0x5, 0x5c }, + { 0x6, 0x00 }, + { 0x7, 0xff }, + { 0x8, 0x60 }, + { 0x9, 0x60 }, + { 0xa, 0x60 }, + { 0xb, 0x00 }, + { 0xc, 0x00 }, + { 0xd, 0x00 }, + { 0xe, 0x00 }, + { 0xf, 0x40 }, + { 0x10, 0x80 }, + { 0x11, 0x77 }, + { 0x12, 0x6a }, + { 0x13, 0x69 }, + { 0x14, 0x6a }, + { 0x15, 0x69 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, + { 0x18, 0x00 }, + { 0x19, 0x00 }, + { 0x1a, 0x00 }, + { 0x1b, 0x00 }, + { 0x1c, 0x00 }, + { 0x1d, 0x00 }, + { 0x1e, 0x00 }, + { 0x1f, 0x00 }, + { 0x20, 0x00 }, + { 0x21, 0x00 }, + { 0x22, 0x00 }, + { 0x23, 0x00 }, + { 0x24, 0x00 }, + { 0x25, 0x00 }, + { 0x26, 0x00 }, + { 0x27, 0x2a }, + { 0x28, 0xc0 }, + { 0x29, 0xf3 }, + { 0x2a, 0x33 }, + { 0x2b, 0x00 }, + { 0x2c, 0x0c }, + { 0x31, 0x00 }, + { 0x36, 0x00 }, + { 0x37, 0x00 }, + { 0x38, 0x00 }, + { 0x39, 0x01 }, + { 0x3a, 0xee }, + { 0x3b, 0xff }, + { 0x3c, 0x7e }, + { 0x3d, 0xc0 }, + { 0x3e, 0x26 }, + { 0x3f, 0x00 }, + { 0x48, 0x00 }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x04 }, + { 0x4c, 0x00 }, +}; + +static const struct regmap_range sta350_write_regs_range[] = { + regmap_reg_range(STA350_CONFA, STA350_AUTO2), + regmap_reg_range(STA350_C1CFG, STA350_FDRC2), + regmap_reg_range(STA350_EQCFG, STA350_EVOLRES), + regmap_reg_range(STA350_NSHAPE, STA350_MISC2), +}; + +static const struct regmap_range sta350_read_regs_range[] = { + regmap_reg_range(STA350_CONFA, STA350_AUTO2), + regmap_reg_range(STA350_C1CFG, STA350_STATUS), + regmap_reg_range(STA350_EQCFG, STA350_EVOLRES), + regmap_reg_range(STA350_NSHAPE, STA350_MISC2), +}; + +static const struct regmap_range sta350_volatile_regs_range[] = { + regmap_reg_range(STA350_CFADDR2, STA350_CFUD), + regmap_reg_range(STA350_STATUS, STA350_STATUS), +}; + +static const struct regmap_access_table sta350_write_regs = { + .yes_ranges = sta350_write_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta350_write_regs_range), +}; + +static const struct regmap_access_table sta350_read_regs = { + .yes_ranges = sta350_read_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta350_read_regs_range), +}; + +static const struct regmap_access_table sta350_volatile_regs = { + .yes_ranges = sta350_volatile_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta350_volatile_regs_range), +}; + +/* regulator power supply names */ +static const char * const sta350_supply_names[] = { + "vdd-dig", /* digital supply, 3.3V */ + "vdd-pll", /* pll supply, 3.3V */ + "vcc" /* power amp supply, 5V - 26V */ +}; + +/* codec private data */ +struct sta350_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[ARRAY_SIZE(sta350_supply_names)]; + struct sta350_platform_data *pdata; + + unsigned int mclk; + unsigned int format; + + u32 coef_shadow[STA350_COEF_COUNT]; + int shutdown; + + struct gpio_desc *gpiod_nreset; + struct gpio_desc *gpiod_power_down; + + struct mutex coeff_lock; +}; + +static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(chvol_tlv, -7950, 50, 1); +static const DECLARE_TLV_DB_SCALE(tone_tlv, -1200, 200, 0); + +static const char * const sta350_drc_ac[] = { + "Anti-Clipping", "Dynamic Range Compression" +}; +static const char * const sta350_auto_gc_mode[] = { + "User", "AC no clipping", "AC limited clipping (10%)", + "DRC nighttime listening mode" +}; +static const char * const sta350_auto_xo_mode[] = { + "User", "80Hz", "100Hz", "120Hz", "140Hz", "160Hz", "180Hz", + "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz", "320Hz", + "340Hz", "360Hz" +}; +static const char * const sta350_binary_output[] = { + "FFX 3-state output - normal operation", "Binary output" +}; +static const char * const sta350_limiter_select[] = { + "Limiter Disabled", "Limiter #1", "Limiter #2" +}; +static const char * const sta350_limiter_attack_rate[] = { + "3.1584", "2.7072", "2.2560", "1.8048", "1.3536", "0.9024", + "0.4512", "0.2256", "0.1504", "0.1123", "0.0902", "0.0752", + "0.0645", "0.0564", "0.0501", "0.0451" +}; +static const char * const sta350_limiter_release_rate[] = { + "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299", + "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137", + "0.0134", "0.0117", "0.0110", "0.0104" +}; +static const char * const sta350_noise_shaper_type[] = { + "Third order", "Fourth order" +}; + +static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_attack_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 8, 16, TLV_DB_SCALE_ITEM(300, 100, 0), +); + +static DECLARE_TLV_DB_RANGE(sta350_limiter_ac_release_tlv, + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0), + 3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0), + 8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0), +); + +static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_attack_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0), + 8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0), + 14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0), +); + +static DECLARE_TLV_DB_RANGE(sta350_limiter_drc_release_tlv, + 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), + 1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0), + 3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0), + 5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0), + 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0), +); + +static SOC_ENUM_SINGLE_DECL(sta350_drc_ac_enum, + STA350_CONFD, STA350_CONFD_DRC_SHIFT, + sta350_drc_ac); +static SOC_ENUM_SINGLE_DECL(sta350_noise_shaper_enum, + STA350_CONFE, STA350_CONFE_NSBW_SHIFT, + sta350_noise_shaper_type); +static SOC_ENUM_SINGLE_DECL(sta350_auto_gc_enum, + STA350_AUTO1, STA350_AUTO1_AMGC_SHIFT, + sta350_auto_gc_mode); +static SOC_ENUM_SINGLE_DECL(sta350_auto_xo_enum, + STA350_AUTO2, STA350_AUTO2_XO_SHIFT, + sta350_auto_xo_mode); +static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch1_enum, + STA350_C1CFG, STA350_CxCFG_BO_SHIFT, + sta350_binary_output); +static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch2_enum, + STA350_C2CFG, STA350_CxCFG_BO_SHIFT, + sta350_binary_output); +static SOC_ENUM_SINGLE_DECL(sta350_binary_output_ch3_enum, + STA350_C3CFG, STA350_CxCFG_BO_SHIFT, + sta350_binary_output); +static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch1_enum, + STA350_C1CFG, STA350_CxCFG_LS_SHIFT, + sta350_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch2_enum, + STA350_C2CFG, STA350_CxCFG_LS_SHIFT, + sta350_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta350_limiter_ch3_enum, + STA350_C3CFG, STA350_CxCFG_LS_SHIFT, + sta350_limiter_select); +static SOC_ENUM_SINGLE_DECL(sta350_limiter1_attack_rate_enum, + STA350_L1AR, STA350_LxA_SHIFT, + sta350_limiter_attack_rate); +static SOC_ENUM_SINGLE_DECL(sta350_limiter2_attack_rate_enum, + STA350_L2AR, STA350_LxA_SHIFT, + sta350_limiter_attack_rate); +static SOC_ENUM_SINGLE_DECL(sta350_limiter1_release_rate_enum, + STA350_L1AR, STA350_LxR_SHIFT, + sta350_limiter_release_rate); +static SOC_ENUM_SINGLE_DECL(sta350_limiter2_release_rate_enum, + STA350_L2AR, STA350_LxR_SHIFT, + sta350_limiter_release_rate); + +/* + * byte array controls for setting biquad, mixer, scaling coefficients; + * for biquads all five coefficients need to be set in one go, + * mixer and pre/postscale coefs can be set individually; + * each coef is 24bit, the bytes are ordered in the same way + * as given in the STA350 data sheet (big endian; b1, b2, a1, a2, b0) + */ + +static int sta350_coefficient_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int numcoef = kcontrol->private_value >> 16; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 3 * numcoef; + return 0; +} + +static int sta350_coefficient_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud, val; + int i, ret = 0; + + mutex_lock(&sta350->coeff_lock); + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); + cfud &= 0xf0; + /* + * chip documentation does not say if the bits are self clearing, + * so do it explicitly + */ + regmap_write(sta350->regmap, STA350_CFUD, cfud); + + regmap_write(sta350->regmap, STA350_CFADDR2, index); + if (numcoef == 1) { + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x04); + } else if (numcoef == 5) { + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x08); + } else { + ret = -EINVAL; + goto exit_unlock; + } + + for (i = 0; i < 3 * numcoef; i++) { + regmap_read(sta350->regmap, STA350_B1CF1 + i, &val); + ucontrol->value.bytes.data[i] = val; + } + +exit_unlock: + mutex_unlock(&sta350->coeff_lock); + + return ret; +} + +static int sta350_coefficient_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + unsigned int cfud; + int i; + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); + cfud &= 0xf0; + /* + * chip documentation does not say if the bits are self clearing, + * so do it explicitly + */ + regmap_write(sta350->regmap, STA350_CFUD, cfud); + + regmap_write(sta350->regmap, STA350_CFADDR2, index); + for (i = 0; i < numcoef && (index + i < STA350_COEF_COUNT); i++) + sta350->coef_shadow[index + i] = + (ucontrol->value.bytes.data[3 * i] << 16) + | (ucontrol->value.bytes.data[3 * i + 1] << 8) + | (ucontrol->value.bytes.data[3 * i + 2]); + for (i = 0; i < 3 * numcoef; i++) + regmap_write(sta350->regmap, STA350_B1CF1 + i, + ucontrol->value.bytes.data[i]); + if (numcoef == 1) + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01); + else if (numcoef == 5) + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x02); + else + return -EINVAL; + + return 0; +} + +static int sta350_sync_coef_shadow(struct snd_soc_component *component) +{ + struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); + unsigned int cfud; + int i; + + /* preserve reserved bits in STA350_CFUD */ + regmap_read(sta350->regmap, STA350_CFUD, &cfud); + cfud &= 0xf0; + + for (i = 0; i < STA350_COEF_COUNT; i++) { + regmap_write(sta350->regmap, STA350_CFADDR2, i); + regmap_write(sta350->regmap, STA350_B1CF1, + (sta350->coef_shadow[i] >> 16) & 0xff); + regmap_write(sta350->regmap, STA350_B1CF2, + (sta350->coef_shadow[i] >> 8) & 0xff); + regmap_write(sta350->regmap, STA350_B1CF3, + (sta350->coef_shadow[i]) & 0xff); + /* + * chip documentation does not say if the bits are + * self-clearing, so do it explicitly + */ + regmap_write(sta350->regmap, STA350_CFUD, cfud); + regmap_write(sta350->regmap, STA350_CFUD, cfud | 0x01); + } + return 0; +} + +static int sta350_cache_sync(struct snd_soc_component *component) +{ + struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); + unsigned int mute; + int rc; + + /* mute during register sync */ + regmap_read(sta350->regmap, STA350_CFUD, &mute); + regmap_write(sta350->regmap, STA350_MMUTE, mute | STA350_MMUTE_MMUTE); + sta350_sync_coef_shadow(component); + rc = regcache_sync(sta350->regmap); + regmap_write(sta350->regmap, STA350_MMUTE, mute); + return rc; +} + +#define SINGLE_COEF(xname, index) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sta350_coefficient_info, \ + .get = sta350_coefficient_get,\ + .put = sta350_coefficient_put, \ + .private_value = index | (1 << 16) } + +#define BIQUAD_COEFS(xname, index) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sta350_coefficient_info, \ + .get = sta350_coefficient_get,\ + .put = sta350_coefficient_put, \ + .private_value = index | (5 << 16) } + +static const struct snd_kcontrol_new sta350_snd_controls[] = { +SOC_SINGLE_TLV("Master Volume", STA350_MVOL, 0, 0xff, 1, mvol_tlv), +/* VOL */ +SOC_SINGLE_TLV("Ch1 Volume", STA350_C1VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE_TLV("Ch2 Volume", STA350_C2VOL, 0, 0xff, 1, chvol_tlv), +SOC_SINGLE_TLV("Ch3 Volume", STA350_C3VOL, 0, 0xff, 1, chvol_tlv), +/* CONFD */ +SOC_SINGLE("High Pass Filter Bypass Switch", + STA350_CONFD, STA350_CONFD_HPB_SHIFT, 1, 1), +SOC_SINGLE("De-emphasis Filter Switch", + STA350_CONFD, STA350_CONFD_DEMP_SHIFT, 1, 0), +SOC_SINGLE("DSP Bypass Switch", + STA350_CONFD, STA350_CONFD_DSPB_SHIFT, 1, 0), +SOC_SINGLE("Post-scale Link Switch", + STA350_CONFD, STA350_CONFD_PSL_SHIFT, 1, 0), +SOC_SINGLE("Biquad Coefficient Link Switch", + STA350_CONFD, STA350_CONFD_BQL_SHIFT, 1, 0), +SOC_ENUM("Compressor/Limiter Switch", sta350_drc_ac_enum), +SOC_ENUM("Noise Shaper Bandwidth", sta350_noise_shaper_enum), +SOC_SINGLE("Zero-detect Mute Enable Switch", + STA350_CONFD, STA350_CONFD_ZDE_SHIFT, 1, 0), +SOC_SINGLE("Submix Mode Switch", + STA350_CONFD, STA350_CONFD_SME_SHIFT, 1, 0), +/* CONFE */ +SOC_SINGLE("Zero Cross Switch", STA350_CONFE, STA350_CONFE_ZCE_SHIFT, 1, 0), +SOC_SINGLE("Soft Ramp Switch", STA350_CONFE, STA350_CONFE_SVE_SHIFT, 1, 0), +/* MUTE */ +SOC_SINGLE("Master Switch", STA350_MMUTE, STA350_MMUTE_MMUTE_SHIFT, 1, 1), +SOC_SINGLE("Ch1 Switch", STA350_MMUTE, STA350_MMUTE_C1M_SHIFT, 1, 1), +SOC_SINGLE("Ch2 Switch", STA350_MMUTE, STA350_MMUTE_C2M_SHIFT, 1, 1), +SOC_SINGLE("Ch3 Switch", STA350_MMUTE, STA350_MMUTE_C3M_SHIFT, 1, 1), +/* AUTOx */ +SOC_ENUM("Automode GC", sta350_auto_gc_enum), +SOC_ENUM("Automode XO", sta350_auto_xo_enum), +/* CxCFG */ +SOC_SINGLE("Ch1 Tone Control Bypass Switch", + STA350_C1CFG, STA350_CxCFG_TCB_SHIFT, 1, 0), +SOC_SINGLE("Ch2 Tone Control Bypass Switch", + STA350_C2CFG, STA350_CxCFG_TCB_SHIFT, 1, 0), +SOC_SINGLE("Ch1 EQ Bypass Switch", + STA350_C1CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0), +SOC_SINGLE("Ch2 EQ Bypass Switch", + STA350_C2CFG, STA350_CxCFG_EQBP_SHIFT, 1, 0), +SOC_SINGLE("Ch1 Master Volume Bypass Switch", + STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), +SOC_SINGLE("Ch2 Master Volume Bypass Switch", + STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), +SOC_SINGLE("Ch3 Master Volume Bypass Switch", + STA350_C1CFG, STA350_CxCFG_VBP_SHIFT, 1, 0), +SOC_ENUM("Ch1 Binary Output Select", sta350_binary_output_ch1_enum), +SOC_ENUM("Ch2 Binary Output Select", sta350_binary_output_ch2_enum), +SOC_ENUM("Ch3 Binary Output Select", sta350_binary_output_ch3_enum), +SOC_ENUM("Ch1 Limiter Select", sta350_limiter_ch1_enum), +SOC_ENUM("Ch2 Limiter Select", sta350_limiter_ch2_enum), +SOC_ENUM("Ch3 Limiter Select", sta350_limiter_ch3_enum), +/* TONE */ +SOC_SINGLE_RANGE_TLV("Bass Tone Control Volume", + STA350_TONE, STA350_TONE_BTC_SHIFT, 1, 13, 0, tone_tlv), +SOC_SINGLE_RANGE_TLV("Treble Tone Control Volume", + STA350_TONE, STA350_TONE_TTC_SHIFT, 1, 13, 0, tone_tlv), +SOC_ENUM("Limiter1 Attack Rate (dB/ms)", sta350_limiter1_attack_rate_enum), +SOC_ENUM("Limiter2 Attack Rate (dB/ms)", sta350_limiter2_attack_rate_enum), +SOC_ENUM("Limiter1 Release Rate (dB/ms)", sta350_limiter1_release_rate_enum), +SOC_ENUM("Limiter2 Release Rate (dB/ms)", sta350_limiter2_release_rate_enum), + +/* + * depending on mode, the attack/release thresholds have + * two different enum definitions; provide both + */ +SOC_SINGLE_TLV("Limiter1 Attack Threshold (AC Mode)", + STA350_L1ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_ac_attack_tlv), +SOC_SINGLE_TLV("Limiter2 Attack Threshold (AC Mode)", + STA350_L2ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_ac_attack_tlv), +SOC_SINGLE_TLV("Limiter1 Release Threshold (AC Mode)", + STA350_L1ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_ac_release_tlv), +SOC_SINGLE_TLV("Limiter2 Release Threshold (AC Mode)", + STA350_L2ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_ac_release_tlv), +SOC_SINGLE_TLV("Limiter1 Attack Threshold (DRC Mode)", + STA350_L1ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_drc_attack_tlv), +SOC_SINGLE_TLV("Limiter2 Attack Threshold (DRC Mode)", + STA350_L2ATRT, STA350_LxA_SHIFT, + 16, 0, sta350_limiter_drc_attack_tlv), +SOC_SINGLE_TLV("Limiter1 Release Threshold (DRC Mode)", + STA350_L1ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_drc_release_tlv), +SOC_SINGLE_TLV("Limiter2 Release Threshold (DRC Mode)", + STA350_L2ATRT, STA350_LxR_SHIFT, + 16, 0, sta350_limiter_drc_release_tlv), + +BIQUAD_COEFS("Ch1 - Biquad 1", 0), +BIQUAD_COEFS("Ch1 - Biquad 2", 5), +BIQUAD_COEFS("Ch1 - Biquad 3", 10), +BIQUAD_COEFS("Ch1 - Biquad 4", 15), +BIQUAD_COEFS("Ch2 - Biquad 1", 20), +BIQUAD_COEFS("Ch2 - Biquad 2", 25), +BIQUAD_COEFS("Ch2 - Biquad 3", 30), +BIQUAD_COEFS("Ch2 - Biquad 4", 35), +BIQUAD_COEFS("High-pass", 40), +BIQUAD_COEFS("Low-pass", 45), +SINGLE_COEF("Ch1 - Prescale", 50), +SINGLE_COEF("Ch2 - Prescale", 51), +SINGLE_COEF("Ch1 - Postscale", 52), +SINGLE_COEF("Ch2 - Postscale", 53), +SINGLE_COEF("Ch3 - Postscale", 54), +SINGLE_COEF("Thermal warning - Postscale", 55), +SINGLE_COEF("Ch1 - Mix 1", 56), +SINGLE_COEF("Ch1 - Mix 2", 57), +SINGLE_COEF("Ch2 - Mix 1", 58), +SINGLE_COEF("Ch2 - Mix 2", 59), +SINGLE_COEF("Ch3 - Mix 1", 60), +SINGLE_COEF("Ch3 - Mix 2", 61), +}; + +static const struct snd_soc_dapm_widget sta350_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("LEFT"), +SND_SOC_DAPM_OUTPUT("RIGHT"), +SND_SOC_DAPM_OUTPUT("SUB"), +}; + +static const struct snd_soc_dapm_route sta350_dapm_routes[] = { + { "LEFT", NULL, "DAC" }, + { "RIGHT", NULL, "DAC" }, + { "SUB", NULL, "DAC" }, + { "DAC", NULL, "Playback" }, +}; + +/* MCLK interpolation ratio per fs */ +static struct { + int fs; + int ir; +} interpolation_ratios[] = { + { 32000, 0 }, + { 44100, 0 }, + { 48000, 0 }, + { 88200, 1 }, + { 96000, 1 }, + { 176400, 2 }, + { 192000, 2 }, +}; + +/* MCLK to fs clock ratios */ +static int mcs_ratio_table[3][6] = { + { 768, 512, 384, 256, 128, 576 }, + { 384, 256, 192, 128, 64, 0 }, + { 192, 128, 96, 64, 32, 0 }, +}; + +/** + * sta350_set_dai_sysclk - configure MCLK + * @codec_dai: the codec DAI + * @clk_id: the clock ID (ignored) + * @freq: the MCLK input frequency + * @dir: the clock direction (ignored) + * + * The value of MCLK is used to determine which sample rates are supported + * by the STA350, based on the mcs_ratio_table. + * + * This function must be called by the machine driver's 'startup' function, + * otherwise the list of supported sample rates will not be available in + * time for ALSA. + */ +static int sta350_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "mclk=%u\n", freq); + sta350->mclk = freq; + + return 0; +} + +/** + * sta350_set_dai_fmt - configure the codec for the selected audio format + * @codec_dai: the codec DAI + * @fmt: a SND_SOC_DAIFMT_x value indicating the data format + * + * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the + * codec accordingly. + */ +static int sta350_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); + unsigned int confb = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + sta350->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + confb |= STA350_CONFB_C2IM; + break; + case SND_SOC_DAIFMT_NB_IF: + confb |= STA350_CONFB_C1IM; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(sta350->regmap, STA350_CONFB, + STA350_CONFB_C1IM | STA350_CONFB_C2IM, confb); +} + +/** + * sta350_hw_params - program the STA350 with the given hardware parameters. + * @substream: the audio stream + * @params: the hardware parameters to set + * @dai: the SOC DAI (ignored) + * + * This function programs the hardware with the values provided. + * Specifically, the sample rate and the data format. + */ +static int sta350_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); + int i, mcs = -EINVAL, ir = -EINVAL; + unsigned int confa, confb; + unsigned int rate, ratio; + int ret; + + if (!sta350->mclk) { + dev_err(component->dev, + "sta350->mclk is unset. Unable to determine ratio\n"); + return -EIO; + } + + rate = params_rate(params); + ratio = sta350->mclk / rate; + dev_dbg(component->dev, "rate: %u, ratio: %u\n", rate, ratio); + + for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) { + if (interpolation_ratios[i].fs == rate) { + ir = interpolation_ratios[i].ir; + break; + } + } + + if (ir < 0) { + dev_err(component->dev, "Unsupported samplerate: %u\n", rate); + return -EINVAL; + } + + for (i = 0; i < 6; i++) { + if (mcs_ratio_table[ir][i] == ratio) { + mcs = i; + break; + } + } + + if (mcs < 0) { + dev_err(component->dev, "Unresolvable ratio: %u\n", ratio); + return -EINVAL; + } + + confa = (ir << STA350_CONFA_IR_SHIFT) | + (mcs << STA350_CONFA_MCS_SHIFT); + confb = 0; + + switch (params_width(params)) { + case 24: + dev_dbg(component->dev, "24bit\n"); + fallthrough; + case 32: + dev_dbg(component->dev, "24bit or 32bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x1; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0x2; + break; + } + + break; + case 20: + dev_dbg(component->dev, "20bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x4; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x5; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0x6; + break; + } + + break; + case 18: + dev_dbg(component->dev, "18bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x8; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0x9; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0xa; + break; + } + + break; + case 16: + dev_dbg(component->dev, "16bit\n"); + switch (sta350->format) { + case SND_SOC_DAIFMT_I2S: + confb |= 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + confb |= 0xd; + break; + case SND_SOC_DAIFMT_RIGHT_J: + confb |= 0xe; + break; + } + + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(sta350->regmap, STA350_CONFA, + STA350_CONFA_MCS_MASK | STA350_CONFA_IR_MASK, + confa); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sta350->regmap, STA350_CONFB, + STA350_CONFB_SAI_MASK | STA350_CONFB_SAIFB, + confb); + if (ret < 0) + return ret; + + return 0; +} + +static int sta350_startup_sequence(struct sta350_priv *sta350) +{ + if (sta350->gpiod_power_down) + gpiod_set_value(sta350->gpiod_power_down, 1); + + if (sta350->gpiod_nreset) { + gpiod_set_value(sta350->gpiod_nreset, 0); + mdelay(1); + gpiod_set_value(sta350->gpiod_nreset, 1); + mdelay(1); + } + + return 0; +} + +/** + * sta350_set_bias_level - DAPM callback + * @component: the component device + * @level: DAPM power level + * + * This is called by ALSA to put the component into low power mode + * or to wake it up. If the component is powered off completely + * all registers must be restored after power on. + */ +static int sta350_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); + int ret; + + dev_dbg(component->dev, "level = %d\n", level); + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* Full power on */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_PWDN | STA350_CONFF_EAPD, + STA350_CONFF_PWDN | STA350_CONFF_EAPD); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable( + ARRAY_SIZE(sta350->supplies), + sta350->supplies); + if (ret < 0) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + sta350_startup_sequence(sta350); + sta350_cache_sync(component); + } + + /* Power down */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_PWDN | STA350_CONFF_EAPD, + 0); + + break; + + case SND_SOC_BIAS_OFF: + /* The chip runs through the power down sequence for us */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_PWDN | STA350_CONFF_EAPD, 0); + + /* power down: low */ + if (sta350->gpiod_power_down) + gpiod_set_value(sta350->gpiod_power_down, 0); + + if (sta350->gpiod_nreset) + gpiod_set_value(sta350->gpiod_nreset, 0); + + regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), + sta350->supplies); + break; + } + return 0; +} + +static const struct snd_soc_dai_ops sta350_dai_ops = { + .hw_params = sta350_hw_params, + .set_sysclk = sta350_set_dai_sysclk, + .set_fmt = sta350_set_dai_fmt, +}; + +static struct snd_soc_dai_driver sta350_dai = { + .name = "sta350-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = STA350_RATES, + .formats = STA350_FORMATS, + }, + .ops = &sta350_dai_ops, +}; + +static int sta350_probe(struct snd_soc_component *component) +{ + struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); + struct sta350_platform_data *pdata = sta350->pdata; + int i, ret = 0, thermal = 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(sta350->supplies), + sta350->supplies); + if (ret < 0) { + dev_err(component->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = sta350_startup_sequence(sta350); + if (ret < 0) { + dev_err(component->dev, "Failed to startup device\n"); + return ret; + } + + /* CONFA */ + if (!pdata->thermal_warning_recovery) + thermal |= STA350_CONFA_TWAB; + if (!pdata->thermal_warning_adjustment) + thermal |= STA350_CONFA_TWRB; + if (!pdata->fault_detect_recovery) + thermal |= STA350_CONFA_FDRB; + regmap_update_bits(sta350->regmap, STA350_CONFA, + STA350_CONFA_TWAB | STA350_CONFA_TWRB | + STA350_CONFA_FDRB, + thermal); + + /* CONFC */ + regmap_update_bits(sta350->regmap, STA350_CONFC, + STA350_CONFC_OM_MASK, + pdata->ffx_power_output_mode + << STA350_CONFC_OM_SHIFT); + regmap_update_bits(sta350->regmap, STA350_CONFC, + STA350_CONFC_CSZ_MASK, + pdata->drop_compensation_ns + << STA350_CONFC_CSZ_SHIFT); + regmap_update_bits(sta350->regmap, + STA350_CONFC, + STA350_CONFC_OCRB, + pdata->oc_warning_adjustment ? + STA350_CONFC_OCRB : 0); + + /* CONFE */ + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_MPCV, + pdata->max_power_use_mpcc ? + STA350_CONFE_MPCV : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_MPC, + pdata->max_power_correction ? + STA350_CONFE_MPC : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_AME, + pdata->am_reduction_mode ? + STA350_CONFE_AME : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_PWMS, + pdata->odd_pwm_speed_mode ? + STA350_CONFE_PWMS : 0); + regmap_update_bits(sta350->regmap, STA350_CONFE, + STA350_CONFE_DCCV, + pdata->distortion_compensation ? + STA350_CONFE_DCCV : 0); + /* CONFF */ + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_IDE, + pdata->invalid_input_detect_mute ? + STA350_CONFF_IDE : 0); + regmap_update_bits(sta350->regmap, STA350_CONFF, + STA350_CONFF_OCFG_MASK, + pdata->output_conf + << STA350_CONFF_OCFG_SHIFT); + + /* channel to output mapping */ + regmap_update_bits(sta350->regmap, STA350_C1CFG, + STA350_CxCFG_OM_MASK, + pdata->ch1_output_mapping + << STA350_CxCFG_OM_SHIFT); + regmap_update_bits(sta350->regmap, STA350_C2CFG, + STA350_CxCFG_OM_MASK, + pdata->ch2_output_mapping + << STA350_CxCFG_OM_SHIFT); + regmap_update_bits(sta350->regmap, STA350_C3CFG, + STA350_CxCFG_OM_MASK, + pdata->ch3_output_mapping + << STA350_CxCFG_OM_SHIFT); + + /* miscellaneous registers */ + regmap_update_bits(sta350->regmap, STA350_MISC1, + STA350_MISC1_CPWMEN, + pdata->activate_mute_output ? + STA350_MISC1_CPWMEN : 0); + regmap_update_bits(sta350->regmap, STA350_MISC1, + STA350_MISC1_BRIDGOFF, + pdata->bridge_immediate_off ? + STA350_MISC1_BRIDGOFF : 0); + regmap_update_bits(sta350->regmap, STA350_MISC1, + STA350_MISC1_NSHHPEN, + pdata->noise_shape_dc_cut ? + STA350_MISC1_NSHHPEN : 0); + regmap_update_bits(sta350->regmap, STA350_MISC1, + STA350_MISC1_RPDNEN, + pdata->powerdown_master_vol ? + STA350_MISC1_RPDNEN: 0); + + regmap_update_bits(sta350->regmap, STA350_MISC2, + STA350_MISC2_PNDLSL_MASK, + pdata->powerdown_delay_divider + << STA350_MISC2_PNDLSL_SHIFT); + + /* initialize coefficient shadow RAM with reset values */ + for (i = 4; i <= 49; i += 5) + sta350->coef_shadow[i] = 0x400000; + for (i = 50; i <= 54; i++) + sta350->coef_shadow[i] = 0x7fffff; + sta350->coef_shadow[55] = 0x5a9df7; + sta350->coef_shadow[56] = 0x7fffff; + sta350->coef_shadow[59] = 0x7fffff; + sta350->coef_shadow[60] = 0x400000; + sta350->coef_shadow[61] = 0x400000; + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + /* Bias level configuration will have done an extra enable */ + regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); + + return 0; +} + +static void sta350_remove(struct snd_soc_component *component) +{ + struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component); + + regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies); +} + +static const struct snd_soc_component_driver sta350_component = { + .probe = sta350_probe, + .remove = sta350_remove, + .set_bias_level = sta350_set_bias_level, + .controls = sta350_snd_controls, + .num_controls = ARRAY_SIZE(sta350_snd_controls), + .dapm_widgets = sta350_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sta350_dapm_widgets), + .dapm_routes = sta350_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(sta350_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config sta350_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = STA350_MISC2, + .reg_defaults = sta350_regs, + .num_reg_defaults = ARRAY_SIZE(sta350_regs), + .cache_type = REGCACHE_MAPLE, + .wr_table = &sta350_write_regs, + .rd_table = &sta350_read_regs, + .volatile_table = &sta350_volatile_regs, +}; + +#ifdef CONFIG_OF +static const struct of_device_id st350_dt_ids[] = { + { .compatible = "st,sta350", }, + { } +}; +MODULE_DEVICE_TABLE(of, st350_dt_ids); + +static const char * const sta350_ffx_modes[] = { + [STA350_FFX_PM_DROP_COMP] = "drop-compensation", + [STA350_FFX_PM_TAPERED_COMP] = "tapered-compensation", + [STA350_FFX_PM_FULL_POWER] = "full-power-mode", + [STA350_FFX_PM_VARIABLE_DROP_COMP] = "variable-drop-compensation", +}; + +static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350) +{ + struct device_node *np = dev->of_node; + struct sta350_platform_data *pdata; + const char *ffx_power_mode; + u16 tmp; + u8 tmp8; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + of_property_read_u8(np, "st,output-conf", + &pdata->output_conf); + of_property_read_u8(np, "st,ch1-output-mapping", + &pdata->ch1_output_mapping); + of_property_read_u8(np, "st,ch2-output-mapping", + &pdata->ch2_output_mapping); + of_property_read_u8(np, "st,ch3-output-mapping", + &pdata->ch3_output_mapping); + + pdata->thermal_warning_recovery = + of_property_read_bool(np, "st,thermal-warning-recovery"); + pdata->thermal_warning_adjustment = + of_property_read_bool(np, "st,thermal-warning-adjustment"); + pdata->fault_detect_recovery = + of_property_read_bool(np, "st,fault-detect-recovery"); + + pdata->ffx_power_output_mode = STA350_FFX_PM_VARIABLE_DROP_COMP; + if (!of_property_read_string(np, "st,ffx-power-output-mode", + &ffx_power_mode)) { + int i, mode = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(sta350_ffx_modes); i++) + if (!strcasecmp(ffx_power_mode, sta350_ffx_modes[i])) + mode = i; + + if (mode < 0) + dev_warn(dev, "Unsupported ffx output mode: %s\n", + ffx_power_mode); + else + pdata->ffx_power_output_mode = mode; + } + + tmp = 140; + of_property_read_u16(np, "st,drop-compensation-ns", &tmp); + pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20; + + pdata->oc_warning_adjustment = + of_property_read_bool(np, "st,overcurrent-warning-adjustment"); + + /* CONFE */ + pdata->max_power_use_mpcc = + of_property_read_bool(np, "st,max-power-use-mpcc"); + pdata->max_power_correction = + of_property_read_bool(np, "st,max-power-correction"); + pdata->am_reduction_mode = + of_property_read_bool(np, "st,am-reduction-mode"); + pdata->odd_pwm_speed_mode = + of_property_read_bool(np, "st,odd-pwm-speed-mode"); + pdata->distortion_compensation = + of_property_read_bool(np, "st,distortion-compensation"); + + /* CONFF */ + pdata->invalid_input_detect_mute = + of_property_read_bool(np, "st,invalid-input-detect-mute"); + + /* MISC */ + pdata->activate_mute_output = + of_property_read_bool(np, "st,activate-mute-output"); + pdata->bridge_immediate_off = + of_property_read_bool(np, "st,bridge-immediate-off"); + pdata->noise_shape_dc_cut = + of_property_read_bool(np, "st,noise-shape-dc-cut"); + pdata->powerdown_master_vol = + of_property_read_bool(np, "st,powerdown-master-volume"); + + if (!of_property_read_u8(np, "st,powerdown-delay-divider", &tmp8)) { + if (is_power_of_2(tmp8) && tmp8 >= 1 && tmp8 <= 128) + pdata->powerdown_delay_divider = ilog2(tmp8); + else + dev_warn(dev, "Unsupported powerdown delay divider %d\n", + tmp8); + } + + sta350->pdata = pdata; + + return 0; +} +#endif + +static int sta350_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct sta350_priv *sta350; + int ret, i; + + sta350 = devm_kzalloc(dev, sizeof(struct sta350_priv), GFP_KERNEL); + if (!sta350) + return -ENOMEM; + + mutex_init(&sta350->coeff_lock); + sta350->pdata = dev_get_platdata(dev); + +#ifdef CONFIG_OF + if (dev->of_node) { + ret = sta350_probe_dt(dev, sta350); + if (ret < 0) + return ret; + } +#endif + + /* GPIOs */ + sta350->gpiod_nreset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(sta350->gpiod_nreset)) + return PTR_ERR(sta350->gpiod_nreset); + + sta350->gpiod_power_down = devm_gpiod_get_optional(dev, "power-down", + GPIOD_OUT_LOW); + if (IS_ERR(sta350->gpiod_power_down)) + return PTR_ERR(sta350->gpiod_power_down); + + /* regulators */ + for (i = 0; i < ARRAY_SIZE(sta350->supplies); i++) + sta350->supplies[i].supply = sta350_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sta350->supplies), + sta350->supplies); + if (ret < 0) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + sta350->regmap = devm_regmap_init_i2c(i2c, &sta350_regmap); + if (IS_ERR(sta350->regmap)) { + ret = PTR_ERR(sta350->regmap); + dev_err(dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, sta350); + + ret = devm_snd_soc_register_component(dev, &sta350_component, &sta350_dai, 1); + if (ret < 0) + dev_err(dev, "Failed to register component (%d)\n", ret); + + return ret; +} + +static void sta350_i2c_remove(struct i2c_client *client) +{} + +static const struct i2c_device_id sta350_i2c_id[] = { + { "sta350", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sta350_i2c_id); + +static struct i2c_driver sta350_i2c_driver = { + .driver = { + .name = "sta350", + .of_match_table = of_match_ptr(st350_dt_ids), + }, + .probe = sta350_i2c_probe, + .remove = sta350_i2c_remove, + .id_table = sta350_i2c_id, +}; + +module_i2c_driver(sta350_i2c_driver); + +MODULE_DESCRIPTION("ASoC STA350 driver"); +MODULE_AUTHOR("Sven Brandau "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h new file mode 100644 index 0000000000..80bf56093d --- /dev/null +++ b/sound/soc/codecs/sta350.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Codec driver for ST STA350 2.1-channel high-efficiency digital audio system + * + * Copyright: 2011 Raumfeld GmbH + * Author: Sven Brandau + * + * based on code from: + * Raumfeld GmbH + * Johannes Stezenbach + * Wolfson Microelectronics PLC. + * Mark Brown + */ +#ifndef _ASOC_STA_350_H +#define _ASOC_STA_350_H + +/* STA350 register addresses */ + +#define STA350_REGISTER_COUNT 0x4D +#define STA350_COEF_COUNT 62 + +#define STA350_CONFA 0x00 +#define STA350_CONFB 0x01 +#define STA350_CONFC 0x02 +#define STA350_CONFD 0x03 +#define STA350_CONFE 0x04 +#define STA350_CONFF 0x05 +#define STA350_MMUTE 0x06 +#define STA350_MVOL 0x07 +#define STA350_C1VOL 0x08 +#define STA350_C2VOL 0x09 +#define STA350_C3VOL 0x0a +#define STA350_AUTO1 0x0b +#define STA350_AUTO2 0x0c +#define STA350_AUTO3 0x0d +#define STA350_C1CFG 0x0e +#define STA350_C2CFG 0x0f +#define STA350_C3CFG 0x10 +#define STA350_TONE 0x11 +#define STA350_L1AR 0x12 +#define STA350_L1ATRT 0x13 +#define STA350_L2AR 0x14 +#define STA350_L2ATRT 0x15 +#define STA350_CFADDR2 0x16 +#define STA350_B1CF1 0x17 +#define STA350_B1CF2 0x18 +#define STA350_B1CF3 0x19 +#define STA350_B2CF1 0x1a +#define STA350_B2CF2 0x1b +#define STA350_B2CF3 0x1c +#define STA350_A1CF1 0x1d +#define STA350_A1CF2 0x1e +#define STA350_A1CF3 0x1f +#define STA350_A2CF1 0x20 +#define STA350_A2CF2 0x21 +#define STA350_A2CF3 0x22 +#define STA350_B0CF1 0x23 +#define STA350_B0CF2 0x24 +#define STA350_B0CF3 0x25 +#define STA350_CFUD 0x26 +#define STA350_MPCC1 0x27 +#define STA350_MPCC2 0x28 +#define STA350_DCC1 0x29 +#define STA350_DCC2 0x2a +#define STA350_FDRC1 0x2b +#define STA350_FDRC2 0x2c +#define STA350_STATUS 0x2d +/* reserved: 0x2d - 0x30 */ +#define STA350_EQCFG 0x31 +#define STA350_EATH1 0x32 +#define STA350_ERTH1 0x33 +#define STA350_EATH2 0x34 +#define STA350_ERTH2 0x35 +#define STA350_CONFX 0x36 +#define STA350_SVCA 0x37 +#define STA350_SVCB 0x38 +#define STA350_RMS0A 0x39 +#define STA350_RMS0B 0x3a +#define STA350_RMS0C 0x3b +#define STA350_RMS1A 0x3c +#define STA350_RMS1B 0x3d +#define STA350_RMS1C 0x3e +#define STA350_EVOLRES 0x3f +/* reserved: 0x40 - 0x47 */ +#define STA350_NSHAPE 0x48 +#define STA350_CTXB4B1 0x49 +#define STA350_CTXB7B5 0x4a +#define STA350_MISC1 0x4b +#define STA350_MISC2 0x4c + +/* 0x00 CONFA */ +#define STA350_CONFA_MCS_MASK 0x03 +#define STA350_CONFA_MCS_SHIFT 0 +#define STA350_CONFA_IR_MASK 0x18 +#define STA350_CONFA_IR_SHIFT 3 +#define STA350_CONFA_TWRB BIT(5) +#define STA350_CONFA_TWAB BIT(6) +#define STA350_CONFA_FDRB BIT(7) + +/* 0x01 CONFB */ +#define STA350_CONFB_SAI_MASK 0x0f +#define STA350_CONFB_SAI_SHIFT 0 +#define STA350_CONFB_SAIFB BIT(4) +#define STA350_CONFB_DSCKE BIT(5) +#define STA350_CONFB_C1IM BIT(6) +#define STA350_CONFB_C2IM BIT(7) + +/* 0x02 CONFC */ +#define STA350_CONFC_OM_MASK 0x03 +#define STA350_CONFC_OM_SHIFT 0 +#define STA350_CONFC_CSZ_MASK 0x3c +#define STA350_CONFC_CSZ_SHIFT 2 +#define STA350_CONFC_OCRB BIT(7) + +/* 0x03 CONFD */ +#define STA350_CONFD_HPB_SHIFT 0 +#define STA350_CONFD_DEMP_SHIFT 1 +#define STA350_CONFD_DSPB_SHIFT 2 +#define STA350_CONFD_PSL_SHIFT 3 +#define STA350_CONFD_BQL_SHIFT 4 +#define STA350_CONFD_DRC_SHIFT 5 +#define STA350_CONFD_ZDE_SHIFT 6 +#define STA350_CONFD_SME_SHIFT 7 + +/* 0x04 CONFE */ +#define STA350_CONFE_MPCV BIT(0) +#define STA350_CONFE_MPCV_SHIFT 0 +#define STA350_CONFE_MPC BIT(1) +#define STA350_CONFE_MPC_SHIFT 1 +#define STA350_CONFE_NSBW BIT(2) +#define STA350_CONFE_NSBW_SHIFT 2 +#define STA350_CONFE_AME BIT(3) +#define STA350_CONFE_AME_SHIFT 3 +#define STA350_CONFE_PWMS BIT(4) +#define STA350_CONFE_PWMS_SHIFT 4 +#define STA350_CONFE_DCCV BIT(5) +#define STA350_CONFE_DCCV_SHIFT 5 +#define STA350_CONFE_ZCE BIT(6) +#define STA350_CONFE_ZCE_SHIFT 6 +#define STA350_CONFE_SVE BIT(7) +#define STA350_CONFE_SVE_SHIFT 7 + +/* 0x05 CONFF */ +#define STA350_CONFF_OCFG_MASK 0x03 +#define STA350_CONFF_OCFG_SHIFT 0 +#define STA350_CONFF_IDE BIT(2) +#define STA350_CONFF_BCLE BIT(3) +#define STA350_CONFF_LDTE BIT(4) +#define STA350_CONFF_ECLE BIT(5) +#define STA350_CONFF_PWDN BIT(6) +#define STA350_CONFF_EAPD BIT(7) + +/* 0x06 MMUTE */ +#define STA350_MMUTE_MMUTE 0x01 +#define STA350_MMUTE_MMUTE_SHIFT 0 +#define STA350_MMUTE_C1M 0x02 +#define STA350_MMUTE_C1M_SHIFT 1 +#define STA350_MMUTE_C2M 0x04 +#define STA350_MMUTE_C2M_SHIFT 2 +#define STA350_MMUTE_C3M 0x08 +#define STA350_MMUTE_C3M_SHIFT 3 +#define STA350_MMUTE_LOC_MASK 0xC0 +#define STA350_MMUTE_LOC_SHIFT 6 + +/* 0x0b AUTO1 */ +#define STA350_AUTO1_AMGC_MASK 0x30 +#define STA350_AUTO1_AMGC_SHIFT 4 + +/* 0x0c AUTO2 */ +#define STA350_AUTO2_AMAME 0x01 +#define STA350_AUTO2_AMAM_MASK 0x0e +#define STA350_AUTO2_AMAM_SHIFT 1 +#define STA350_AUTO2_XO_MASK 0xf0 +#define STA350_AUTO2_XO_SHIFT 4 + +/* 0x0d AUTO3 */ +#define STA350_AUTO3_PEQ_MASK 0x1f +#define STA350_AUTO3_PEQ_SHIFT 0 + +/* 0x0e 0x0f 0x10 CxCFG */ +#define STA350_CxCFG_TCB_SHIFT 0 +#define STA350_CxCFG_EQBP_SHIFT 1 +#define STA350_CxCFG_VBP_SHIFT 2 +#define STA350_CxCFG_BO_SHIFT 3 +#define STA350_CxCFG_LS_SHIFT 4 +#define STA350_CxCFG_OM_MASK 0xc0 +#define STA350_CxCFG_OM_SHIFT 6 + +/* 0x11 TONE */ +#define STA350_TONE_BTC_SHIFT 0 +#define STA350_TONE_TTC_SHIFT 4 + +/* 0x12 0x13 0x14 0x15 limiter attack/release */ +#define STA350_LxA_SHIFT 0 +#define STA350_LxR_SHIFT 4 + +/* 0x26 CFUD */ +#define STA350_CFUD_W1 0x01 +#define STA350_CFUD_WA 0x02 +#define STA350_CFUD_R1 0x04 +#define STA350_CFUD_RA 0x08 + + +/* biquad filter coefficient table offsets */ +#define STA350_C1_BQ_BASE 0 +#define STA350_C2_BQ_BASE 20 +#define STA350_CH_BQ_NUM 4 +#define STA350_BQ_NUM_COEF 5 +#define STA350_XO_HP_BQ_BASE 40 +#define STA350_XO_LP_BQ_BASE 45 +#define STA350_C1_PRESCALE 50 +#define STA350_C2_PRESCALE 51 +#define STA350_C1_POSTSCALE 52 +#define STA350_C2_POSTSCALE 53 +#define STA350_C3_POSTSCALE 54 +#define STA350_TW_POSTSCALE 55 +#define STA350_C1_MIX1 56 +#define STA350_C1_MIX2 57 +#define STA350_C2_MIX1 58 +#define STA350_C2_MIX2 59 +#define STA350_C3_MIX1 60 +#define STA350_C3_MIX2 61 + +/* miscellaneous register 1 */ +#define STA350_MISC1_CPWMEN BIT(2) +#define STA350_MISC1_BRIDGOFF BIT(5) +#define STA350_MISC1_NSHHPEN BIT(6) +#define STA350_MISC1_RPDNEN BIT(7) + +/* miscellaneous register 2 */ +#define STA350_MISC2_PNDLSL_MASK 0x1c +#define STA350_MISC2_PNDLSL_SHIFT 2 + +#endif /* _ASOC_STA_350_H */ diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c new file mode 100644 index 0000000000..eedafef775 --- /dev/null +++ b/sound/soc/codecs/sta529.c @@ -0,0 +1,390 @@ +/* + * ASoC codec driver for spear platform + * + * sound/soc/codecs/sta529.c -- spear ALSA Soc codec driver + * + * Copyright (C) 2012 ST Microelectronics + * Rajeev Kumar + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* STA529 Register offsets */ +#define STA529_FFXCFG0 0x00 +#define STA529_FFXCFG1 0x01 +#define STA529_MVOL 0x02 +#define STA529_LVOL 0x03 +#define STA529_RVOL 0x04 +#define STA529_TTF0 0x05 +#define STA529_TTF1 0x06 +#define STA529_TTP0 0x07 +#define STA529_TTP1 0x08 +#define STA529_S2PCFG0 0x0A +#define STA529_S2PCFG1 0x0B +#define STA529_P2SCFG0 0x0C +#define STA529_P2SCFG1 0x0D +#define STA529_PLLCFG0 0x14 +#define STA529_PLLCFG1 0x15 +#define STA529_PLLCFG2 0x16 +#define STA529_PLLCFG3 0x17 +#define STA529_PLLPFE 0x18 +#define STA529_PLLST 0x19 +#define STA529_ADCCFG 0x1E /*mic_select*/ +#define STA529_CKOCFG 0x1F +#define STA529_MISC 0x20 +#define STA529_PADST0 0x21 +#define STA529_PADST1 0x22 +#define STA529_FFXST 0x23 +#define STA529_PWMIN1 0x2D +#define STA529_PWMIN2 0x2E +#define STA529_POWST 0x32 + +#define STA529_MAX_REGISTER 0x32 + +#define STA529_RATES (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +#define STA529_FORMAT (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define S2PC_VALUE 0x98 +#define CLOCK_OUT 0x60 +#define DATA_FORMAT_MSK 0x0E +#define LEFT_J_DATA_FORMAT 0x00 +#define I2S_DATA_FORMAT 0x02 +#define RIGHT_J_DATA_FORMAT 0x04 +#define CODEC_MUTE_VAL 0x80 + +#define POWER_CNTLMSAK 0x40 +#define POWER_STDBY 0x40 +#define FFX_MASK 0x80 +#define FFX_OFF 0x80 +#define POWER_UP 0x00 +#define FFX_CLK_ENB 0x01 +#define FFX_CLK_DIS 0x00 +#define FFX_CLK_MSK 0x01 +#define PLAY_FREQ_RANGE_MSK 0x70 +#define CAP_FREQ_RANGE_MSK 0x0C +#define PDATA_LEN_MSK 0xC0 +#define BCLK_TO_FS_MSK 0x30 +#define AUDIO_MUTE_MSK 0x80 + +static const struct reg_default sta529_reg_defaults[] = { + { 0, 0x35 }, /* R0 - FFX Configuration reg 0 */ + { 1, 0xc8 }, /* R1 - FFX Configuration reg 1 */ + { 2, 0x50 }, /* R2 - Master Volume */ + { 3, 0x00 }, /* R3 - Left Volume */ + { 4, 0x00 }, /* R4 - Right Volume */ + { 10, 0xb2 }, /* R10 - S2P Config Reg 0 */ + { 11, 0x41 }, /* R11 - S2P Config Reg 1 */ + { 12, 0x92 }, /* R12 - P2S Config Reg 0 */ + { 13, 0x41 }, /* R13 - P2S Config Reg 1 */ + { 30, 0xd2 }, /* R30 - ADC Config Reg */ + { 31, 0x40 }, /* R31 - clock Out Reg */ + { 32, 0x21 }, /* R32 - Misc Register */ +}; + +struct sta529 { + struct regmap *regmap; +}; + +static bool sta529_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + + case STA529_FFXCFG0: + case STA529_FFXCFG1: + case STA529_MVOL: + case STA529_LVOL: + case STA529_RVOL: + case STA529_S2PCFG0: + case STA529_S2PCFG1: + case STA529_P2SCFG0: + case STA529_P2SCFG1: + case STA529_ADCCFG: + case STA529_CKOCFG: + case STA529_MISC: + return true; + default: + return false; + } +} + + +static const char *pwm_mode_text[] = { "Binary", "Headphone", "Ternary", + "Phase-shift"}; + +static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -9150, 50, 0); +static const DECLARE_TLV_DB_SCALE(master_vol_tlv, -12750, 50, 0); +static SOC_ENUM_SINGLE_DECL(pwm_src, STA529_FFXCFG1, 4, pwm_mode_text); + +static const struct snd_kcontrol_new sta529_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Playback Volume", STA529_LVOL, STA529_RVOL, 0, + 127, 0, out_gain_tlv), + SOC_SINGLE_TLV("Master Playback Volume", STA529_MVOL, 0, 127, 1, + master_vol_tlv), + SOC_ENUM("PWM Select", pwm_src), +}; + +static int sta529_set_bias_level(struct snd_soc_component *component, enum + snd_soc_bias_level level) +{ + struct sta529 *sta529 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, STA529_FFXCFG0, POWER_CNTLMSAK, + POWER_UP); + snd_soc_component_update_bits(component, STA529_MISC, FFX_CLK_MSK, + FFX_CLK_ENB); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + regcache_sync(sta529->regmap); + snd_soc_component_update_bits(component, STA529_FFXCFG0, + POWER_CNTLMSAK, POWER_STDBY); + /* Making FFX output to zero */ + snd_soc_component_update_bits(component, STA529_FFXCFG0, FFX_MASK, + FFX_OFF); + snd_soc_component_update_bits(component, STA529_MISC, FFX_CLK_MSK, + FFX_CLK_DIS); + break; + case SND_SOC_BIAS_OFF: + break; + } + + return 0; + +} + +static int sta529_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int pdata, play_freq_val, record_freq_val; + int bclk_to_fs_ratio; + + switch (params_width(params)) { + case 16: + pdata = 1; + bclk_to_fs_ratio = 0; + break; + case 24: + pdata = 2; + bclk_to_fs_ratio = 1; + break; + case 32: + pdata = 3; + bclk_to_fs_ratio = 2; + break; + default: + dev_err(component->dev, "Unsupported format\n"); + return -EINVAL; + } + + switch (params_rate(params)) { + case 8000: + case 11025: + play_freq_val = 0; + record_freq_val = 2; + break; + case 16000: + case 22050: + play_freq_val = 1; + record_freq_val = 0; + break; + + case 32000: + case 44100: + case 48000: + play_freq_val = 2; + record_freq_val = 0; + break; + default: + dev_err(component->dev, "Unsupported rate\n"); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_component_update_bits(component, STA529_S2PCFG1, PDATA_LEN_MSK, + pdata << 6); + snd_soc_component_update_bits(component, STA529_S2PCFG1, BCLK_TO_FS_MSK, + bclk_to_fs_ratio << 4); + snd_soc_component_update_bits(component, STA529_MISC, PLAY_FREQ_RANGE_MSK, + play_freq_val << 4); + } else { + snd_soc_component_update_bits(component, STA529_P2SCFG1, PDATA_LEN_MSK, + pdata << 6); + snd_soc_component_update_bits(component, STA529_P2SCFG1, BCLK_TO_FS_MSK, + bclk_to_fs_ratio << 4); + snd_soc_component_update_bits(component, STA529_MISC, CAP_FREQ_RANGE_MSK, + record_freq_val << 2); + } + + return 0; +} + +static int sta529_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + u8 val = 0; + + if (mute) + val |= CODEC_MUTE_VAL; + + snd_soc_component_update_bits(dai->component, STA529_FFXCFG0, AUDIO_MUTE_MSK, val); + + return 0; +} + +static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u8 mode = 0; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + mode = LEFT_J_DATA_FORMAT; + break; + case SND_SOC_DAIFMT_I2S: + mode = I2S_DATA_FORMAT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + mode = RIGHT_J_DATA_FORMAT; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, STA529_S2PCFG0, DATA_FORMAT_MSK, mode); + + return 0; +} + +static const struct snd_soc_dai_ops sta529_dai_ops = { + .hw_params = sta529_hw_params, + .set_fmt = sta529_set_dai_fmt, + .mute_stream = sta529_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver sta529_dai = { + .name = "sta529-audio", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = STA529_RATES, + .formats = STA529_FORMAT, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = STA529_RATES, + .formats = STA529_FORMAT, + }, + .ops = &sta529_dai_ops, +}; + +static const struct snd_soc_component_driver sta529_component_driver = { + .set_bias_level = sta529_set_bias_level, + .controls = sta529_snd_controls, + .num_controls = ARRAY_SIZE(sta529_snd_controls), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config sta529_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = STA529_MAX_REGISTER, + .readable_reg = sta529_readable, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = sta529_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults), +}; + +static int sta529_i2c_probe(struct i2c_client *i2c) +{ + struct sta529 *sta529; + int ret; + + sta529 = devm_kzalloc(&i2c->dev, sizeof(struct sta529), GFP_KERNEL); + if (!sta529) + return -ENOMEM; + + sta529->regmap = devm_regmap_init_i2c(i2c, &sta529_regmap); + if (IS_ERR(sta529->regmap)) { + ret = PTR_ERR(sta529->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, sta529); + + ret = devm_snd_soc_register_component(&i2c->dev, + &sta529_component_driver, &sta529_dai, 1); + if (ret != 0) + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id sta529_i2c_id[] = { + { "sta529", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sta529_i2c_id); + +static const struct of_device_id sta529_of_match[] = { + { .compatible = "st,sta529", }, + { } +}; +MODULE_DEVICE_TABLE(of, sta529_of_match); + +static struct i2c_driver sta529_i2c_driver = { + .driver = { + .name = "sta529", + .of_match_table = sta529_of_match, + }, + .probe = sta529_i2c_probe, + .id_table = sta529_i2c_id, +}; + +module_i2c_driver(sta529_i2c_driver); + +MODULE_DESCRIPTION("ASoC STA529 codec driver"); +MODULE_AUTHOR("Rajeev Kumar "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c new file mode 100644 index 0000000000..2f9f10a4df --- /dev/null +++ b/sound/soc/codecs/stac9766.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * stac9766.c -- ALSA SoC STAC9766 codec support + * + * Copyright 2009 Jon Smirl, Digispeaker + * Author: Jon Smirl + * + * Features:- + * + * o Support for AC97 Codec, S/PDIF + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STAC9766_VENDOR_ID 0x83847666 +#define STAC9766_VENDOR_ID_MASK 0xffffffff + +#define AC97_STAC_DA_CONTROL 0x6A +#define AC97_STAC_ANALOG_SPECIAL 0x6E +#define AC97_STAC_STEREO_MIC 0x78 + +static const struct reg_default stac9766_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x0a, 0x0000 }, + { 0x0c, 0x8008 }, + { 0x0e, 0x8008 }, + { 0x10, 0x8808 }, + { 0x12, 0x8808 }, + { 0x14, 0x8808 }, + { 0x16, 0x8808 }, + { 0x18, 0x8808 }, + { 0x1a, 0x0000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x28, 0x0a05 }, + { 0x2c, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x3a, 0x2000 }, + { 0x3e, 0x0100 }, + { 0x4c, 0x0300 }, + { 0x4e, 0xffff }, + { 0x50, 0x0000 }, + { 0x52, 0x0000 }, + { 0x54, 0x0000 }, + { 0x6a, 0x0000 }, + { 0x6e, 0x1000 }, + { 0x72, 0x0000 }, + { 0x78, 0x0000 }, +}; + +static const struct regmap_config stac9766_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x78, + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = regmap_ac97_default_volatile, + + .reg_defaults = stac9766_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(stac9766_reg_defaults), +}; + +static const char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX", + "Line", "Stereo Mix", "Mono Mix", "Phone"}; +static const char *stac9766_mono_mux[] = {"Mix", "Mic"}; +static const char *stac9766_mic_mux[] = {"Mic1", "Mic2"}; +static const char *stac9766_SPDIF_mux[] = {"PCM", "ADC Record"}; +static const char *stac9766_popbypass_mux[] = {"Normal", "Bypass Mixer"}; +static const char *stac9766_record_all_mux[] = {"All analog", + "Analog plus DAC"}; +static const char *stac9766_boost1[] = {"0dB", "10dB"}; +static const char *stac9766_boost2[] = {"0dB", "20dB"}; +static const char *stac9766_stereo_mic[] = {"Off", "On"}; + +static SOC_ENUM_DOUBLE_DECL(stac9766_record_enum, + AC97_REC_SEL, 8, 0, stac9766_record_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_mono_enum, + AC97_GENERAL_PURPOSE, 9, stac9766_mono_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_mic_enum, + AC97_GENERAL_PURPOSE, 8, stac9766_mic_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_SPDIF_enum, + AC97_STAC_DA_CONTROL, 1, stac9766_SPDIF_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_popbypass_enum, + AC97_GENERAL_PURPOSE, 15, stac9766_popbypass_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_record_all_enum, + AC97_STAC_ANALOG_SPECIAL, 12, + stac9766_record_all_mux); +static SOC_ENUM_SINGLE_DECL(stac9766_boost1_enum, + AC97_MIC, 6, stac9766_boost1); /* 0/10dB */ +static SOC_ENUM_SINGLE_DECL(stac9766_boost2_enum, + AC97_STAC_ANALOG_SPECIAL, 2, stac9766_boost2); /* 0/20dB */ +static SOC_ENUM_SINGLE_DECL(stac9766_stereo_mic_enum, + AC97_STAC_STEREO_MIC, 2, stac9766_stereo_mic); + +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(master_tlv, -4650, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(record_tlv, 0, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(beep_tlv, -4500, 300, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(mix_tlv, -3450, 150, 0); + +static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = { + SOC_DOUBLE_TLV("Speaker Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv), + SOC_SINGLE("Speaker Switch", AC97_MASTER, 15, 1, 1), + SOC_DOUBLE_TLV("Headphone Volume", AC97_HEADPHONE, 8, 0, 31, 1, + master_tlv), + SOC_SINGLE("Headphone Switch", AC97_HEADPHONE, 15, 1, 1), + SOC_SINGLE_TLV("Mono Out Volume", AC97_MASTER_MONO, 0, 31, 1, + master_tlv), + SOC_SINGLE("Mono Out Switch", AC97_MASTER_MONO, 15, 1, 1), + + SOC_DOUBLE_TLV("Record Volume", AC97_REC_GAIN, 8, 0, 15, 0, record_tlv), + SOC_SINGLE("Record Switch", AC97_REC_GAIN, 15, 1, 1), + + + SOC_SINGLE_TLV("Beep Volume", AC97_PC_BEEP, 1, 15, 1, beep_tlv), + SOC_SINGLE("Beep Switch", AC97_PC_BEEP, 15, 1, 1), + SOC_SINGLE("Beep Frequency", AC97_PC_BEEP, 5, 127, 1), + SOC_SINGLE_TLV("Phone Volume", AC97_PHONE, 0, 31, 1, mix_tlv), + SOC_SINGLE("Phone Switch", AC97_PHONE, 15, 1, 1), + + SOC_ENUM("Mic Boost1", stac9766_boost1_enum), + SOC_ENUM("Mic Boost2", stac9766_boost2_enum), + SOC_SINGLE_TLV("Mic Volume", AC97_MIC, 0, 31, 1, mix_tlv), + SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1), + SOC_ENUM("Stereo Mic", stac9766_stereo_mic_enum), + + SOC_DOUBLE_TLV("Line Volume", AC97_LINE, 8, 0, 31, 1, mix_tlv), + SOC_SINGLE("Line Switch", AC97_LINE, 15, 1, 1), + SOC_DOUBLE_TLV("CD Volume", AC97_CD, 8, 0, 31, 1, mix_tlv), + SOC_SINGLE("CD Switch", AC97_CD, 15, 1, 1), + SOC_DOUBLE_TLV("AUX Volume", AC97_AUX, 8, 0, 31, 1, mix_tlv), + SOC_SINGLE("AUX Switch", AC97_AUX, 15, 1, 1), + SOC_DOUBLE_TLV("Video Volume", AC97_VIDEO, 8, 0, 31, 1, mix_tlv), + SOC_SINGLE("Video Switch", AC97_VIDEO, 15, 1, 1), + + SOC_DOUBLE_TLV("DAC Volume", AC97_PCM, 8, 0, 31, 1, mix_tlv), + SOC_SINGLE("DAC Switch", AC97_PCM, 15, 1, 1), + SOC_SINGLE("Loopback Test Switch", AC97_GENERAL_PURPOSE, 7, 1, 0), + SOC_SINGLE("3D Volume", AC97_3D_CONTROL, 3, 2, 1), + SOC_SINGLE("3D Switch", AC97_GENERAL_PURPOSE, 13, 1, 0), + + SOC_ENUM("SPDIF Mux", stac9766_SPDIF_enum), + SOC_ENUM("Mic1/2 Mux", stac9766_mic_enum), + SOC_ENUM("Record All Mux", stac9766_record_all_enum), + SOC_ENUM("Record Mux", stac9766_record_enum), + SOC_ENUM("Mono Mux", stac9766_mono_enum), + SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum), +}; + +static int ac97_analog_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned short reg; + + /* enable variable rate audio, disable SPDIF output */ + snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x5, 0x1); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = AC97_PCM_FRONT_DAC_RATE; + else + reg = AC97_PCM_LR_ADC_RATE; + + return snd_soc_component_write(component, reg, runtime->rate); +} + +static int ac97_digital_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned short reg; + + snd_soc_component_write(component, AC97_SPDIF, 0x2002); + + /* Enable VRA and SPDIF out */ + snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x5, 0x5); + + reg = AC97_PCM_FRONT_DAC_RATE; + + return snd_soc_component_write(component, reg, runtime->rate); +} + +static int stac9766_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: /* full On */ + case SND_SOC_BIAS_PREPARE: /* partial On */ + case SND_SOC_BIAS_STANDBY: /* Off, with power */ + snd_soc_component_write(component, AC97_POWERDOWN, 0x0000); + break; + case SND_SOC_BIAS_OFF: /* Off, without power */ + /* disable everything including AC link */ + snd_soc_component_write(component, AC97_POWERDOWN, 0xffff); + break; + } + return 0; +} + +static int stac9766_component_resume(struct snd_soc_component *component) +{ + struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component); + + return snd_ac97_reset(ac97, true, STAC9766_VENDOR_ID, + STAC9766_VENDOR_ID_MASK); +} + +static const struct snd_soc_dai_ops stac9766_dai_ops_analog = { + .prepare = ac97_analog_prepare, +}; + +static const struct snd_soc_dai_ops stac9766_dai_ops_digital = { + .prepare = ac97_digital_prepare, +}; + +static struct snd_soc_dai_driver stac9766_dai[] = { +{ + .name = "stac9766-hifi-analog", + + /* stream cababilities */ + .playback = { + .stream_name = "stac9766 analog", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SND_SOC_STD_AC97_FMTS, + }, + .capture = { + .stream_name = "stac9766 analog", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SND_SOC_STD_AC97_FMTS, + }, + /* alsa ops */ + .ops = &stac9766_dai_ops_analog, +}, +{ + .name = "stac9766-hifi-IEC958", + + /* stream cababilities */ + .playback = { + .stream_name = "stac9766 IEC958", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE, + }, + /* alsa ops */ + .ops = &stac9766_dai_ops_digital, +} +}; + +static int stac9766_component_probe(struct snd_soc_component *component) +{ + struct snd_ac97 *ac97; + struct regmap *regmap; + int ret; + + ac97 = snd_soc_new_ac97_component(component, STAC9766_VENDOR_ID, + STAC9766_VENDOR_ID_MASK); + if (IS_ERR(ac97)) + return PTR_ERR(ac97); + + regmap = regmap_init_ac97(ac97, &stac9766_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_free_ac97; + } + + snd_soc_component_init_regmap(component, regmap); + snd_soc_component_set_drvdata(component, ac97); + + return 0; +err_free_ac97: + snd_soc_free_ac97_component(ac97); + return ret; +} + +static void stac9766_component_remove(struct snd_soc_component *component) +{ + struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component); + + snd_soc_component_exit_regmap(component); + snd_soc_free_ac97_component(ac97); +} + +static const struct snd_soc_component_driver soc_component_dev_stac9766 = { + .controls = stac9766_snd_ac97_controls, + .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls), + .set_bias_level = stac9766_set_bias_level, + .probe = stac9766_component_probe, + .remove = stac9766_component_remove, + .resume = stac9766_component_resume, + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int stac9766_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_stac9766, stac9766_dai, ARRAY_SIZE(stac9766_dai)); +} + +static struct platform_driver stac9766_codec_driver = { + .driver = { + .name = "stac9766-codec", + }, + + .probe = stac9766_probe, +}; + +module_platform_driver(stac9766_codec_driver); + +MODULE_DESCRIPTION("ASoC stac9766 driver"); +MODULE_AUTHOR("Jon Smirl "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c new file mode 100644 index 0000000000..c421906a06 --- /dev/null +++ b/sound/soc/codecs/sti-sas.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) STMicroelectronics SA 2015 + * Authors: Arnaud Pouliquen + * for STMicroelectronics. + */ + +#include +#include +#include +#include +#include + +#include +#include + +/* DAC definitions */ + +/* stih407 DAC registers */ +/* sysconf 5041: Audio-Gue-Control */ +#define STIH407_AUDIO_GLUE_CTRL 0x000000A4 +/* sysconf 5042: Audio-DAC-Control */ +#define STIH407_AUDIO_DAC_CTRL 0x000000A8 + +/* DAC definitions */ +#define STIH407_DAC_SOFTMUTE 0x0 +#define STIH407_DAC_STANDBY_ANA 0x1 +#define STIH407_DAC_STANDBY 0x2 + +#define STIH407_DAC_SOFTMUTE_MASK BIT(STIH407_DAC_SOFTMUTE) +#define STIH407_DAC_STANDBY_ANA_MASK BIT(STIH407_DAC_STANDBY_ANA) +#define STIH407_DAC_STANDBY_MASK BIT(STIH407_DAC_STANDBY) + +/* SPDIF definitions */ +#define SPDIF_BIPHASE_ENABLE 0x6 +#define SPDIF_BIPHASE_IDLE 0x7 + +#define SPDIF_BIPHASE_ENABLE_MASK BIT(SPDIF_BIPHASE_ENABLE) +#define SPDIF_BIPHASE_IDLE_MASK BIT(SPDIF_BIPHASE_IDLE) + +enum { + STI_SAS_DAI_SPDIF_OUT, + STI_SAS_DAI_ANALOG_OUT, +}; + +static const struct reg_default stih407_sas_reg_defaults[] = { + { STIH407_AUDIO_DAC_CTRL, 0x000000000 }, + { STIH407_AUDIO_GLUE_CTRL, 0x00000040 }, +}; + +struct sti_dac_audio { + struct regmap *regmap; + struct regmap *virt_regmap; + int mclk; +}; + +struct sti_spdif_audio { + struct regmap *regmap; + int mclk; +}; + +/* device data structure */ +struct sti_sas_dev_data { + const struct regmap_config *regmap; + const struct snd_soc_dai_ops *dac_ops; /* DAC function callbacks */ + const struct snd_soc_dapm_widget *dapm_widgets; /* dapms declaration */ + const int num_dapm_widgets; /* dapms declaration */ + const struct snd_soc_dapm_route *dapm_routes; /* route declaration */ + const int num_dapm_routes; /* route declaration */ +}; + +/* driver data structure */ +struct sti_sas_data { + struct device *dev; + const struct sti_sas_dev_data *dev_data; + struct sti_dac_audio dac; + struct sti_spdif_audio spdif; +}; + +/* Read a register from the sysconf reg bank */ +static int sti_sas_read_reg(void *context, unsigned int reg, + unsigned int *value) +{ + struct sti_sas_data *drvdata = context; + int status; + u32 val; + + status = regmap_read(drvdata->dac.regmap, reg, &val); + *value = (unsigned int)val; + + return status; +} + +/* Read a register from the sysconf reg bank */ +static int sti_sas_write_reg(void *context, unsigned int reg, + unsigned int value) +{ + struct sti_sas_data *drvdata = context; + + return regmap_write(drvdata->dac.regmap, reg, value); +} + +static int sti_sas_init_sas_registers(struct snd_soc_component *component, + struct sti_sas_data *data) +{ + int ret; + /* + * DAC and SPDIF are activated by default + * put them in IDLE to save power + */ + + /* Initialise bi-phase formatter to disabled */ + ret = snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, + SPDIF_BIPHASE_ENABLE_MASK, 0); + + if (!ret) + /* Initialise bi-phase formatter idle value to 0 */ + ret = snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, + SPDIF_BIPHASE_IDLE_MASK, 0); + if (ret < 0) { + dev_err(component->dev, "Failed to update SPDIF registers\n"); + return ret; + } + + /* Init DAC configuration */ + /* init configuration */ + ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_STANDBY_MASK, + STIH407_DAC_STANDBY_MASK); + + if (!ret) + ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_STANDBY_ANA_MASK, + STIH407_DAC_STANDBY_ANA_MASK); + if (!ret) + ret = snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_SOFTMUTE_MASK, + STIH407_DAC_SOFTMUTE_MASK); + + if (ret < 0) { + dev_err(component->dev, "Failed to update DAC registers\n"); + return ret; + } + + return ret; +} + +/* + * DAC + */ +static int sti_sas_dac_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /* Sanity check only */ + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { + dev_err(dai->component->dev, + "%s: ERROR: Unsupported clocking 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dapm_widget stih407_sas_dapm_widgets[] = { + SND_SOC_DAPM_OUT_DRV("DAC standby ana", STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_STANDBY_ANA, 1, NULL, 0), + SND_SOC_DAPM_DAC("DAC standby", "dac_p", STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_STANDBY, 1), + SND_SOC_DAPM_OUTPUT("DAC Output"), +}; + +static const struct snd_soc_dapm_route stih407_sas_route[] = { + {"DAC Output", NULL, "DAC standby ana"}, + {"DAC standby ana", NULL, "DAC standby"}, +}; + + +static int stih407_sas_dac_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + + if (mute) { + return snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_SOFTMUTE_MASK, + STIH407_DAC_SOFTMUTE_MASK); + } else { + return snd_soc_component_update_bits(component, STIH407_AUDIO_DAC_CTRL, + STIH407_DAC_SOFTMUTE_MASK, + 0); + } +} + +/* + * SPDIF + */ +static int sti_sas_spdif_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { + dev_err(dai->component->dev, + "%s: ERROR: Unsupported clocking mask 0x%x\n", + __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); + return -EINVAL; + } + + return 0; +} + +/* + * sti_sas_spdif_trigger: + * Trigger function is used to ensure that BiPhase Formater is disabled + * before CPU dai is stopped. + * This is mandatory to avoid that BPF is stalled + */ +static int sti_sas_spdif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + return snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, + SPDIF_BIPHASE_ENABLE_MASK, + SPDIF_BIPHASE_ENABLE_MASK); + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + return snd_soc_component_update_bits(component, STIH407_AUDIO_GLUE_CTRL, + SPDIF_BIPHASE_ENABLE_MASK, + 0); + default: + return -EINVAL; + } +} + +static bool sti_sas_volatile_register(struct device *dev, unsigned int reg) +{ + if (reg == STIH407_AUDIO_GLUE_CTRL) + return true; + + return false; +} + +/* + * CODEC DAIS + */ + +/* + * sti_sas_set_sysclk: + * get MCLK input frequency to check that MCLK-FS ratio is coherent + */ +static int sti_sas_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); + + if (dir == SND_SOC_CLOCK_OUT) + return 0; + + if (clk_id != 0) + return -EINVAL; + + switch (dai->id) { + case STI_SAS_DAI_SPDIF_OUT: + drvdata->spdif.mclk = freq; + break; + + case STI_SAS_DAI_ANALOG_OUT: + drvdata->dac.mclk = freq; + break; + } + + return 0; +} + +static int sti_sas_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); + struct snd_pcm_runtime *runtime = substream->runtime; + + switch (dai->id) { + case STI_SAS_DAI_SPDIF_OUT: + if ((drvdata->spdif.mclk / runtime->rate) != 128) { + dev_err(component->dev, "unexpected mclk-fs ratio\n"); + return -EINVAL; + } + break; + case STI_SAS_DAI_ANALOG_OUT: + if ((drvdata->dac.mclk / runtime->rate) != 256) { + dev_err(component->dev, "unexpected mclk-fs ratio\n"); + return -EINVAL; + } + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops stih407_dac_ops = { + .set_fmt = sti_sas_dac_set_fmt, + .mute_stream = stih407_sas_dac_mute, + .prepare = sti_sas_prepare, + .set_sysclk = sti_sas_set_sysclk, +}; + +static const struct regmap_config stih407_sas_regmap = { + .reg_bits = 32, + .val_bits = 32, + .fast_io = true, + .max_register = STIH407_AUDIO_DAC_CTRL, + .reg_defaults = stih407_sas_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults), + .volatile_reg = sti_sas_volatile_register, + .cache_type = REGCACHE_MAPLE, + .reg_read = sti_sas_read_reg, + .reg_write = sti_sas_write_reg, +}; + +static const struct sti_sas_dev_data stih407_data = { + .regmap = &stih407_sas_regmap, + .dac_ops = &stih407_dac_ops, + .dapm_widgets = stih407_sas_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(stih407_sas_dapm_widgets), + .dapm_routes = stih407_sas_route, + .num_dapm_routes = ARRAY_SIZE(stih407_sas_route), +}; + +static struct snd_soc_dai_driver sti_sas_dai[] = { + { + .name = "sas-dai-spdif-out", + .id = STI_SAS_DAI_SPDIF_OUT, + .playback = { + .stream_name = "spdif_p", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = (struct snd_soc_dai_ops[]) { + { + .set_fmt = sti_sas_spdif_set_fmt, + .trigger = sti_sas_spdif_trigger, + .set_sysclk = sti_sas_set_sysclk, + .prepare = sti_sas_prepare, + } + }, + }, + { + .name = "sas-dai-dac", + .id = STI_SAS_DAI_ANALOG_OUT, + .playback = { + .stream_name = "dac_p", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + }, +}; + +#ifdef CONFIG_PM_SLEEP +static int sti_sas_resume(struct snd_soc_component *component) +{ + struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); + + return sti_sas_init_sas_registers(component, drvdata); +} +#else +#define sti_sas_resume NULL +#endif + +static int sti_sas_component_probe(struct snd_soc_component *component) +{ + struct sti_sas_data *drvdata = dev_get_drvdata(component->dev); + + return sti_sas_init_sas_registers(component, drvdata); +} + +static struct snd_soc_component_driver sti_sas_driver = { + .probe = sti_sas_component_probe, + .resume = sti_sas_resume, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id sti_sas_dev_match[] = { + { + .compatible = "st,stih407-sas-codec", + .data = &stih407_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sti_sas_dev_match); + +static int sti_sas_driver_probe(struct platform_device *pdev) +{ + struct device_node *pnode = pdev->dev.of_node; + struct sti_sas_data *drvdata; + const struct of_device_id *of_id; + + /* Allocate device structure */ + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct sti_sas_data), + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + /* Populate data structure depending on compatibility */ + of_id = of_match_node(sti_sas_dev_match, pnode); + if (!of_id->data) { + dev_err(&pdev->dev, "data associated to device is missing\n"); + return -EINVAL; + } + + drvdata->dev_data = (struct sti_sas_dev_data *)of_id->data; + + /* Initialise device structure */ + drvdata->dev = &pdev->dev; + + /* Request the DAC & SPDIF registers memory region */ + drvdata->dac.virt_regmap = devm_regmap_init(&pdev->dev, NULL, drvdata, + drvdata->dev_data->regmap); + if (IS_ERR(drvdata->dac.virt_regmap)) { + dev_err(&pdev->dev, "audio registers not enabled\n"); + return PTR_ERR(drvdata->dac.virt_regmap); + } + + /* Request the syscon region */ + drvdata->dac.regmap = + syscon_regmap_lookup_by_phandle(pnode, "st,syscfg"); + if (IS_ERR(drvdata->dac.regmap)) { + dev_err(&pdev->dev, "syscon registers not available\n"); + return PTR_ERR(drvdata->dac.regmap); + } + drvdata->spdif.regmap = drvdata->dac.regmap; + + sti_sas_dai[STI_SAS_DAI_ANALOG_OUT].ops = drvdata->dev_data->dac_ops; + + /* Set dapms*/ + sti_sas_driver.dapm_widgets = drvdata->dev_data->dapm_widgets; + sti_sas_driver.num_dapm_widgets = drvdata->dev_data->num_dapm_widgets; + + sti_sas_driver.dapm_routes = drvdata->dev_data->dapm_routes; + sti_sas_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes; + + /* Store context */ + dev_set_drvdata(&pdev->dev, drvdata); + + return devm_snd_soc_register_component(&pdev->dev, &sti_sas_driver, + sti_sas_dai, + ARRAY_SIZE(sti_sas_dai)); +} + +static struct platform_driver sti_sas_platform_driver = { + .driver = { + .name = "sti-sas-codec", + .of_match_table = sti_sas_dev_match, + }, + .probe = sti_sas_driver_probe, +}; + +module_platform_driver(sti_sas_platform_driver); + +MODULE_DESCRIPTION("audio codec for STMicroelectronics sti platforms"); +MODULE_AUTHOR("Arnaud.pouliquen@st.com"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c new file mode 100644 index 0000000000..8c9dc318b0 --- /dev/null +++ b/sound/soc/codecs/tas2552.c @@ -0,0 +1,773 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier + * + * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com + * + * Author: Dan Murphy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "tas2552.h" + +static const struct reg_default tas2552_reg_defs[] = { + {TAS2552_CFG_1, 0x22}, + {TAS2552_CFG_3, 0x80}, + {TAS2552_DOUT, 0x00}, + {TAS2552_OUTPUT_DATA, 0xc0}, + {TAS2552_PDM_CFG, 0x01}, + {TAS2552_PGA_GAIN, 0x00}, + {TAS2552_BOOST_APT_CTRL, 0x0f}, + {TAS2552_RESERVED_0D, 0xbe}, + {TAS2552_LIMIT_RATE_HYS, 0x08}, + {TAS2552_CFG_2, 0xef}, + {TAS2552_SER_CTRL_1, 0x00}, + {TAS2552_SER_CTRL_2, 0x00}, + {TAS2552_PLL_CTRL_1, 0x10}, + {TAS2552_PLL_CTRL_2, 0x00}, + {TAS2552_PLL_CTRL_3, 0x00}, + {TAS2552_BTIP, 0x8f}, + {TAS2552_BTS_CTRL, 0x80}, + {TAS2552_LIMIT_RELEASE, 0x04}, + {TAS2552_LIMIT_INT_COUNT, 0x00}, + {TAS2552_EDGE_RATE_CTRL, 0x40}, + {TAS2552_VBAT_DATA, 0x00}, +}; + +#define TAS2552_NUM_SUPPLIES 3 +static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = { + "vbat", /* vbat voltage */ + "iovdd", /* I/O Voltage */ + "avdd", /* Analog DAC Voltage */ +}; + +struct tas2552_data { + struct snd_soc_component *component; + struct regmap *regmap; + struct i2c_client *tas2552_client; + struct regulator_bulk_data supplies[TAS2552_NUM_SUPPLIES]; + struct gpio_desc *enable_gpio; + unsigned char regs[TAS2552_VBAT_DATA]; + unsigned int pll_clkin; + int pll_clk_id; + unsigned int pdm_clk; + int pdm_clk_id; + + unsigned int dai_fmt; + unsigned int tdm_delay; +}; + +static int tas2552_post_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(component, TAS2552_RESERVED_0D, 0xc0); + snd_soc_component_update_bits(component, TAS2552_LIMIT_RATE_HYS, (1 << 5), + (1 << 5)); + snd_soc_component_update_bits(component, TAS2552_CFG_2, 1, 0); + snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_SWS, 0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_SWS, + TAS2552_SWS); + snd_soc_component_update_bits(component, TAS2552_CFG_2, 1, 1); + snd_soc_component_update_bits(component, TAS2552_LIMIT_RATE_HYS, (1 << 5), 0); + snd_soc_component_write(component, TAS2552_RESERVED_0D, 0xbe); + break; + } + return 0; +} + +/* Input mux controls */ +static const char * const tas2552_input_texts[] = { + "Digital", "Analog" }; +static SOC_ENUM_SINGLE_DECL(tas2552_input_mux_enum, TAS2552_CFG_3, 7, + tas2552_input_texts); + +static const struct snd_kcontrol_new tas2552_input_mux_control = + SOC_DAPM_ENUM("Route", tas2552_input_mux_enum); + +static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] = +{ + SND_SOC_DAPM_INPUT("IN"), + + /* MUX Controls */ + SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0, + &tas2552_input_mux_control), + + SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUT_DRV("ClassD", TAS2552_CFG_2, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL", TAS2552_CFG_2, 3, 0, NULL, 0), + SND_SOC_DAPM_POST("Post Event", tas2552_post_event), + + SND_SOC_DAPM_OUTPUT("OUT") +}; + +static const struct snd_soc_dapm_route tas2552_audio_map[] = { + {"DAC", NULL, "DAC IN"}, + {"Input selection", "Digital", "DAC"}, + {"Input selection", "Analog", "IN"}, + {"ClassD", NULL, "Input selection"}, + {"OUT", NULL, "ClassD"}, + {"ClassD", NULL, "PLL"}, +}; + +#ifdef CONFIG_PM +static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown) +{ + u8 cfg1_reg = 0; + + if (!tas2552->component) + return; + + if (sw_shutdown) + cfg1_reg = TAS2552_SWS; + + snd_soc_component_update_bits(tas2552->component, TAS2552_CFG_1, TAS2552_SWS, + cfg1_reg); +} +#endif + +static int tas2552_setup_pll(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct tas2552_data *tas2552 = dev_get_drvdata(component->dev); + bool bypass_pll = false; + unsigned int pll_clk = params_rate(params) * 512; + unsigned int pll_clkin = tas2552->pll_clkin; + u8 pll_enable; + + if (!pll_clkin) { + if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK) + return -EINVAL; + + pll_clkin = snd_soc_params_to_bclk(params); + pll_clkin += tas2552->tdm_delay; + } + + pll_enable = snd_soc_component_read(component, TAS2552_CFG_2) & TAS2552_PLL_ENABLE; + snd_soc_component_update_bits(component, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0); + + if (pll_clkin == pll_clk) + bypass_pll = true; + + if (bypass_pll) { + /* By pass the PLL configuration */ + snd_soc_component_update_bits(component, TAS2552_PLL_CTRL_2, + TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS); + } else { + /* Fill in the PLL control registers for J & D + * pll_clk = (.5 * pll_clkin * J.D) / 2^p + * Need to fill in J and D here based on incoming freq + */ + unsigned int d, q, t; + u8 j; + u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK; + u8 p = snd_soc_component_read(component, TAS2552_PLL_CTRL_1); + + p = (p >> 7); + +recalc: + t = (pll_clk * 2) << p; + j = t / pll_clkin; + d = t % pll_clkin; + t = pll_clkin / 10000; + q = d / (t + 1); + d = q + ((9999 - pll_clkin % 10000) * (d / t - q)) / 10000; + + if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) { + if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) { + pll_clkin = 1800000; + pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) & + TAS2552_PLL_SRC_MASK; + } else { + pll_clkin = snd_soc_params_to_bclk(params); + pll_clkin += tas2552->tdm_delay; + pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) & + TAS2552_PLL_SRC_MASK; + } + goto recalc; + } + + snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK, + pll_sel); + + snd_soc_component_update_bits(component, TAS2552_PLL_CTRL_1, + TAS2552_PLL_J_MASK, j); + /* Will clear the PLL_BYPASS bit */ + snd_soc_component_write(component, TAS2552_PLL_CTRL_2, + TAS2552_PLL_D_UPPER(d)); + snd_soc_component_write(component, TAS2552_PLL_CTRL_3, + TAS2552_PLL_D_LOWER(d)); + } + + /* Restore PLL status */ + snd_soc_component_update_bits(component, TAS2552_CFG_2, TAS2552_PLL_ENABLE, + pll_enable); + + return 0; +} + +static int tas2552_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas2552_data *tas2552 = dev_get_drvdata(component->dev); + int cpf; + u8 ser_ctrl1_reg, wclk_rate; + + switch (params_width(params)) { + case 16: + ser_ctrl1_reg = TAS2552_WORDLENGTH_16BIT; + cpf = 32 + tas2552->tdm_delay; + break; + case 20: + ser_ctrl1_reg = TAS2552_WORDLENGTH_20BIT; + cpf = 64 + tas2552->tdm_delay; + break; + case 24: + ser_ctrl1_reg = TAS2552_WORDLENGTH_24BIT; + cpf = 64 + tas2552->tdm_delay; + break; + case 32: + ser_ctrl1_reg = TAS2552_WORDLENGTH_32BIT; + cpf = 64 + tas2552->tdm_delay; + break; + default: + dev_err(component->dev, "Not supported sample size: %d\n", + params_width(params)); + return -EINVAL; + } + + if (cpf <= 32) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_32; + else if (cpf <= 64) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_64; + else if (cpf <= 128) + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_128; + else + ser_ctrl1_reg |= TAS2552_CLKSPERFRAME_256; + + snd_soc_component_update_bits(component, TAS2552_SER_CTRL_1, + TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK, + ser_ctrl1_reg); + + switch (params_rate(params)) { + case 8000: + wclk_rate = TAS2552_WCLK_FREQ_8KHZ; + break; + case 11025: + case 12000: + wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ; + break; + case 16000: + wclk_rate = TAS2552_WCLK_FREQ_16KHZ; + break; + case 22050: + case 24000: + wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ; + break; + case 32000: + wclk_rate = TAS2552_WCLK_FREQ_32KHZ; + break; + case 44100: + case 48000: + wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ; + break; + case 88200: + case 96000: + wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ; + break; + case 176400: + case 192000: + wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ; + break; + default: + dev_err(component->dev, "Not supported sample rate: %d\n", + params_rate(params)); + return -EINVAL; + } + + snd_soc_component_update_bits(component, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK, + wclk_rate); + + return tas2552_setup_pll(component, params); +} + +#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ + TAS2552_WCLKDIR | \ + TAS2552_DATAFORMAT_MASK) +static int tas2552_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component); + int delay = 0; + + /* TDM slot selection only valid in DSP_A/_B mode */ + if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A) + delay += (tas2552->tdm_delay + 1); + else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B) + delay += tas2552->tdm_delay; + + /* Configure data delay */ + snd_soc_component_write(component, TAS2552_SER_CTRL_2, delay); + + return 0; +} + +static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tas2552_data *tas2552 = dev_get_drvdata(component->dev); + u8 serial_format; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + serial_format = 0x00; + break; + case SND_SOC_DAIFMT_CBC_CFP: + serial_format = TAS2552_WCLKDIR; + break; + case SND_SOC_DAIFMT_CBP_CFC: + serial_format = TAS2552_BCLKDIR; + break; + case SND_SOC_DAIFMT_CBP_CFP: + serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR); + break; + default: + dev_vdbg(component->dev, "DAI Format master is not found\n"); + return -EINVAL; + } + + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | + SND_SOC_DAIFMT_INV_MASK)) { + case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): + break; + case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF): + case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF): + serial_format |= TAS2552_DATAFORMAT_DSP; + break; + case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF): + serial_format |= TAS2552_DATAFORMAT_RIGHT_J; + break; + case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): + serial_format |= TAS2552_DATAFORMAT_LEFT_J; + break; + default: + dev_vdbg(component->dev, "DAI Format is not found\n"); + return -EINVAL; + } + tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + snd_soc_component_update_bits(component, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK, + serial_format); + return 0; +} + +static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct tas2552_data *tas2552 = dev_get_drvdata(component->dev); + u8 reg, mask, val; + + switch (clk_id) { + case TAS2552_PLL_CLKIN_MCLK: + case TAS2552_PLL_CLKIN_IVCLKIN: + if (freq < 512000 || freq > 24576000) { + /* out of range PLL_CLKIN, fall back to use BCLK */ + dev_warn(component->dev, "Out of range PLL_CLKIN: %u\n", + freq); + clk_id = TAS2552_PLL_CLKIN_BCLK; + freq = 0; + } + fallthrough; + case TAS2552_PLL_CLKIN_BCLK: + case TAS2552_PLL_CLKIN_1_8_FIXED: + mask = TAS2552_PLL_SRC_MASK; + val = (clk_id << 3) & mask; /* bit 4:5 in the register */ + reg = TAS2552_CFG_1; + tas2552->pll_clk_id = clk_id; + tas2552->pll_clkin = freq; + break; + case TAS2552_PDM_CLK_PLL: + case TAS2552_PDM_CLK_IVCLKIN: + case TAS2552_PDM_CLK_BCLK: + case TAS2552_PDM_CLK_MCLK: + mask = TAS2552_PDM_CLK_SEL_MASK; + val = (clk_id >> 1) & mask; /* bit 0:1 in the register */ + reg = TAS2552_PDM_CFG; + tas2552->pdm_clk_id = clk_id; + tas2552->pdm_clk = freq; + break; + default: + dev_err(component->dev, "Invalid clk id: %d\n", clk_id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, reg, mask, val); + + return 0; +} + +static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component); + unsigned int lsb; + + if (unlikely(!tx_mask)) { + dev_err(component->dev, "tx masks need to be non 0\n"); + return -EINVAL; + } + + /* TDM based on DSP mode requires slots to be adjacent */ + lsb = __ffs(tx_mask); + if ((lsb + 1) != __fls(tx_mask)) { + dev_err(component->dev, "Invalid mask, slots must be adjacent\n"); + return -EINVAL; + } + + tas2552->tdm_delay = lsb * slot_width; + + /* DOUT in high-impedance on inactive bit clocks */ + snd_soc_component_update_bits(component, TAS2552_DOUT, + TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE); + + return 0; +} + +static int tas2552_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + u8 cfg1_reg = 0; + struct snd_soc_component *component = dai->component; + + if (mute) + cfg1_reg |= TAS2552_MUTE; + + snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_MUTE, cfg1_reg); + + return 0; +} + +#ifdef CONFIG_PM +static int tas2552_runtime_suspend(struct device *dev) +{ + struct tas2552_data *tas2552 = dev_get_drvdata(dev); + + tas2552_sw_shutdown(tas2552, 1); + + regcache_cache_only(tas2552->regmap, true); + regcache_mark_dirty(tas2552->regmap); + + gpiod_set_value(tas2552->enable_gpio, 0); + + return 0; +} + +static int tas2552_runtime_resume(struct device *dev) +{ + struct tas2552_data *tas2552 = dev_get_drvdata(dev); + + gpiod_set_value(tas2552->enable_gpio, 1); + + tas2552_sw_shutdown(tas2552, 0); + + regcache_cache_only(tas2552->regmap, false); + regcache_sync(tas2552->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops tas2552_pm = { + SET_RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume, + NULL) +}; + +static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = { + .hw_params = tas2552_hw_params, + .prepare = tas2552_prepare, + .set_sysclk = tas2552_set_dai_sysclk, + .set_fmt = tas2552_set_dai_fmt, + .set_tdm_slot = tas2552_set_dai_tdm_slot, + .mute_stream = tas2552_mute, + .no_capture_mute = 1, +}; + +/* Formats supported by TAS2552 driver. */ +#define TAS2552_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +/* TAS2552 dai structure. */ +static struct snd_soc_dai_driver tas2552_dai[] = { + { + .name = "tas2552-amplifier", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2552_FORMATS, + }, + .ops = &tas2552_speaker_dai_ops, + }, +}; + +/* + * DAC digital volumes. From -7 to 24 dB in 1 dB steps + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, -700, 100, 0); + +static const char * const tas2552_din_source_select[] = { + "Muted", + "Left", + "Right", + "Left + Right average", +}; +static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum, + TAS2552_CFG_3, 3, + tas2552_din_source_select); + +static const struct snd_kcontrol_new tas2552_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Driver Playback Volume", + TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv), + SOC_ENUM("DIN source", tas2552_din_source_enum), +}; + +static int tas2552_component_probe(struct snd_soc_component *component) +{ + struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component); + int ret; + + tas2552->component = component; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies), + tas2552->supplies); + + if (ret != 0) { + dev_err(component->dev, "Failed to enable supplies: %d\n", + ret); + return ret; + } + + gpiod_set_value(tas2552->enable_gpio, 1); + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + dev_err(component->dev, "Enabling device failed: %d\n", + ret); + goto probe_fail; + } + + snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE); + snd_soc_component_write(component, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL | + TAS2552_DIN_SRC_SEL_AVG_L_R); + snd_soc_component_write(component, TAS2552_OUTPUT_DATA, + TAS2552_PDM_DATA_SEL_V_I | + TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA)); + snd_soc_component_write(component, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 | + TAS2552_APT_THRESH_20_17); + + snd_soc_component_write(component, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN | + TAS2552_LIM_EN); + + return 0; + +probe_fail: + pm_runtime_put_noidle(component->dev); + gpiod_set_value(tas2552->enable_gpio, 0); + + regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), + tas2552->supplies); + return ret; +} + +static void tas2552_component_remove(struct snd_soc_component *component) +{ + struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component); + + pm_runtime_put(component->dev); + + gpiod_set_value(tas2552->enable_gpio, 0); +}; + +#ifdef CONFIG_PM +static int tas2552_suspend(struct snd_soc_component *component) +{ + struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component); + int ret; + + ret = regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies), + tas2552->supplies); + + if (ret != 0) + dev_err(component->dev, "Failed to disable supplies: %d\n", + ret); + return ret; +} + +static int tas2552_resume(struct snd_soc_component *component) +{ + struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies), + tas2552->supplies); + + if (ret != 0) { + dev_err(component->dev, "Failed to enable supplies: %d\n", + ret); + } + + return ret; +} +#else +#define tas2552_suspend NULL +#define tas2552_resume NULL +#endif + +static const struct snd_soc_component_driver soc_component_dev_tas2552 = { + .probe = tas2552_component_probe, + .remove = tas2552_component_remove, + .suspend = tas2552_suspend, + .resume = tas2552_resume, + .controls = tas2552_snd_controls, + .num_controls = ARRAY_SIZE(tas2552_snd_controls), + .dapm_widgets = tas2552_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets), + .dapm_routes = tas2552_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map), + .idle_bias_on = 1, + .endianness = 1, +}; + +static const struct regmap_config tas2552_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = TAS2552_MAX_REG, + .reg_defaults = tas2552_reg_defs, + .num_reg_defaults = ARRAY_SIZE(tas2552_reg_defs), + .cache_type = REGCACHE_RBTREE, +}; + +static int tas2552_probe(struct i2c_client *client) +{ + struct device *dev; + struct tas2552_data *data; + int ret; + int i; + + dev = &client->dev; + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->enable_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(data->enable_gpio)) + return PTR_ERR(data->enable_gpio); + + data->tas2552_client = client; + data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(data->supplies); i++) + data->supplies[i].supply = tas2552_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), + data->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + pm_runtime_set_active(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_sync_autosuspend(&client->dev); + + dev_set_drvdata(&client->dev, data); + + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_tas2552, + tas2552_dai, ARRAY_SIZE(tas2552_dai)); + if (ret < 0) { + dev_err(&client->dev, "Failed to register component: %d\n", ret); + pm_runtime_get_noresume(&client->dev); + } + + return ret; +} + +static void tas2552_i2c_remove(struct i2c_client *client) +{ + pm_runtime_disable(&client->dev); +} + +static const struct i2c_device_id tas2552_id[] = { + { "tas2552", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2552_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tas2552_of_match[] = { + { .compatible = "ti,tas2552", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tas2552_of_match); +#endif + +static struct i2c_driver tas2552_i2c_driver = { + .driver = { + .name = "tas2552", + .of_match_table = of_match_ptr(tas2552_of_match), + .pm = &tas2552_pm, + }, + .probe = tas2552_probe, + .remove = tas2552_i2c_remove, + .id_table = tas2552_id, +}; + +module_i2c_driver(tas2552_i2c_driver); + +MODULE_AUTHOR("Dan Muprhy "); +MODULE_DESCRIPTION("TAS2552 Audio amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h new file mode 100644 index 0000000000..b9c2e70df5 --- /dev/null +++ b/sound/soc/codecs/tas2552.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tas2552.h - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier + * + * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com + * + * Author: Dan Murphy + */ + +#ifndef __TAS2552_H__ +#define __TAS2552_H__ + +/* Register Address Map */ +#define TAS2552_DEVICE_STATUS 0x00 +#define TAS2552_CFG_1 0x01 +#define TAS2552_CFG_2 0x02 +#define TAS2552_CFG_3 0x03 +#define TAS2552_DOUT 0x04 +#define TAS2552_SER_CTRL_1 0x05 +#define TAS2552_SER_CTRL_2 0x06 +#define TAS2552_OUTPUT_DATA 0x07 +#define TAS2552_PLL_CTRL_1 0x08 +#define TAS2552_PLL_CTRL_2 0x09 +#define TAS2552_PLL_CTRL_3 0x0a +#define TAS2552_BTIP 0x0b +#define TAS2552_BTS_CTRL 0x0c +#define TAS2552_RESERVED_0D 0x0d +#define TAS2552_LIMIT_RATE_HYS 0x0e +#define TAS2552_LIMIT_RELEASE 0x0f +#define TAS2552_LIMIT_INT_COUNT 0x10 +#define TAS2552_PDM_CFG 0x11 +#define TAS2552_PGA_GAIN 0x12 +#define TAS2552_EDGE_RATE_CTRL 0x13 +#define TAS2552_BOOST_APT_CTRL 0x14 +#define TAS2552_VER_NUM 0x16 +#define TAS2552_VBAT_DATA 0x19 +#define TAS2552_MAX_REG TAS2552_VBAT_DATA + +/* CFG1 Register Masks */ +#define TAS2552_DEV_RESET (1 << 0) +#define TAS2552_SWS (1 << 1) +#define TAS2552_MUTE (1 << 2) +#define TAS2552_PLL_SRC_MCLK (0x0 << 4) +#define TAS2552_PLL_SRC_BCLK (0x1 << 4) +#define TAS2552_PLL_SRC_IVCLKIN (0x2 << 4) +#define TAS2552_PLL_SRC_1_8_FIXED (0x3 << 4) +#define TAS2552_PLL_SRC_MASK TAS2552_PLL_SRC_1_8_FIXED + +/* CFG2 Register Masks */ +#define TAS2552_CLASSD_EN (1 << 7) +#define TAS2552_BOOST_EN (1 << 6) +#define TAS2552_APT_EN (1 << 5) +#define TAS2552_PLL_ENABLE (1 << 3) +#define TAS2552_LIM_EN (1 << 2) +#define TAS2552_IVSENSE_EN (1 << 1) + +/* CFG3 Register Masks */ +#define TAS2552_WCLK_FREQ_8KHZ (0x0 << 0) +#define TAS2552_WCLK_FREQ_11_12KHZ (0x1 << 0) +#define TAS2552_WCLK_FREQ_16KHZ (0x2 << 0) +#define TAS2552_WCLK_FREQ_22_24KHZ (0x3 << 0) +#define TAS2552_WCLK_FREQ_32KHZ (0x4 << 0) +#define TAS2552_WCLK_FREQ_44_48KHZ (0x5 << 0) +#define TAS2552_WCLK_FREQ_88_96KHZ (0x6 << 0) +#define TAS2552_WCLK_FREQ_176_192KHZ (0x7 << 0) +#define TAS2552_WCLK_FREQ_MASK TAS2552_WCLK_FREQ_176_192KHZ +#define TAS2552_DIN_SRC_SEL_MUTED (0x0 << 3) +#define TAS2552_DIN_SRC_SEL_LEFT (0x1 << 3) +#define TAS2552_DIN_SRC_SEL_RIGHT (0x2 << 3) +#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x3 << 3) +#define TAS2552_PDM_IN_SEL (1 << 5) +#define TAS2552_I2S_OUT_SEL (1 << 6) +#define TAS2552_ANALOG_IN_SEL (1 << 7) + +/* DOUT Register Masks */ +#define TAS2552_SDOUT_TRISTATE (1 << 2) + +/* Serial Interface Control Register Masks */ +#define TAS2552_WORDLENGTH_16BIT (0x0 << 0) +#define TAS2552_WORDLENGTH_20BIT (0x1 << 0) +#define TAS2552_WORDLENGTH_24BIT (0x2 << 0) +#define TAS2552_WORDLENGTH_32BIT (0x3 << 0) +#define TAS2552_WORDLENGTH_MASK TAS2552_WORDLENGTH_32BIT +#define TAS2552_DATAFORMAT_I2S (0x0 << 2) +#define TAS2552_DATAFORMAT_DSP (0x1 << 2) +#define TAS2552_DATAFORMAT_RIGHT_J (0x2 << 2) +#define TAS2552_DATAFORMAT_LEFT_J (0x3 << 2) +#define TAS2552_DATAFORMAT_MASK TAS2552_DATAFORMAT_LEFT_J +#define TAS2552_CLKSPERFRAME_32 (0x0 << 4) +#define TAS2552_CLKSPERFRAME_64 (0x1 << 4) +#define TAS2552_CLKSPERFRAME_128 (0x2 << 4) +#define TAS2552_CLKSPERFRAME_256 (0x3 << 4) +#define TAS2552_CLKSPERFRAME_MASK TAS2552_CLKSPERFRAME_256 +#define TAS2552_BCLKDIR (1 << 6) +#define TAS2552_WCLKDIR (1 << 7) + +/* OUTPUT_DATA register */ +#define TAS2552_DATA_OUT_I_DATA (0x0) +#define TAS2552_DATA_OUT_V_DATA (0x1) +#define TAS2552_DATA_OUT_VBAT_DATA (0x2) +#define TAS2552_DATA_OUT_VBOOST_DATA (0x3) +#define TAS2552_DATA_OUT_PGA_GAIN (0x4) +#define TAS2552_DATA_OUT_IV_DATA (0x5) +#define TAS2552_DATA_OUT_VBAT_VBOOST_GAIN (0x6) +#define TAS2552_DATA_OUT_DISABLED (0x7) +#define TAS2552_L_DATA_OUT(x) ((x) << 0) +#define TAS2552_R_DATA_OUT(x) ((x) << 3) +#define TAS2552_PDM_DATA_SEL_I (0x0 << 6) +#define TAS2552_PDM_DATA_SEL_V (0x1 << 6) +#define TAS2552_PDM_DATA_SEL_I_V (0x2 << 6) +#define TAS2552_PDM_DATA_SEL_V_I (0x3 << 6) +#define TAS2552_PDM_DATA_SEL_MASK TAS2552_PDM_DATA_SEL_V_I + +/* PDM CFG Register */ +#define TAS2552_PDM_CLK_SEL_PLL (0x0 << 0) +#define TAS2552_PDM_CLK_SEL_IVCLKIN (0x1 << 0) +#define TAS2552_PDM_CLK_SEL_BCLK (0x2 << 0) +#define TAS2552_PDM_CLK_SEL_MCLK (0x3 << 0) +#define TAS2552_PDM_CLK_SEL_MASK TAS2552_PDM_CLK_SEL_MCLK +#define TAS2552_PDM_DATA_ES (1 << 2) + +/* Boost Auto-pass through register */ +#define TAS2552_APT_DELAY_50 (0x0 << 0) +#define TAS2552_APT_DELAY_75 (0x1 << 0) +#define TAS2552_APT_DELAY_125 (0x2 << 0) +#define TAS2552_APT_DELAY_200 (0x3 << 0) +#define TAS2552_APT_THRESH_05_02 (0x0 << 2) +#define TAS2552_APT_THRESH_10_07 (0x1 << 2) +#define TAS2552_APT_THRESH_14_11 (0x2 << 2) +#define TAS2552_APT_THRESH_20_17 (0x3 << 2) + +/* PLL Control Register */ +#define TAS2552_PLL_J_MASK 0x7f +#define TAS2552_PLL_D_UPPER(x) (((x) >> 8) & 0x3f) +#define TAS2552_PLL_D_LOWER(x) ((x) & 0xff) +#define TAS2552_PLL_BYPASS (1 << 7) + +#endif diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c new file mode 100644 index 0000000000..962c2cdfa0 --- /dev/null +++ b/sound/soc/codecs/tas2562.c @@ -0,0 +1,794 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for the Texas Instruments TAS2562 CODEC +// Copyright (C) 2019 Texas Instruments Inc. + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tas2562.h" + +#define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FORMAT_S32_LE) + +/* DVC equation involves floating point math + * round(10^(volume in dB/20)*2^30) + * so create a lookup table for 2dB step + */ +static const unsigned int float_vol_db_lookup[] = { +0x00000d43, 0x000010b2, 0x00001505, 0x00001a67, 0x00002151, +0x000029f1, 0x000034cd, 0x00004279, 0x000053af, 0x0000695b, +0x0000695b, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a, +0x0001a36e, 0x00021008, 0x000298c0, 0x000344df, 0x00041d8f, +0x00052e5a, 0x000685c8, 0x00083621, 0x000a566d, 0x000d03a7, +0x0010624d, 0x0014a050, 0x0019f786, 0x0020b0bc, 0x0029279d, +0x0033cf8d, 0x004139d3, 0x00521d50, 0x00676044, 0x0082248a, +0x00a3d70a, 0x00ce4328, 0x0103ab3d, 0x0146e75d, 0x019b8c27, +0x02061b89, 0x028c423f, 0x03352529, 0x0409c2b0, 0x05156d68, +0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, 0x143d1362, +0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, 0x40000000 +}; + +struct tas2562_data { + struct snd_soc_component *component; + struct gpio_desc *sdz_gpio; + struct regmap *regmap; + struct device *dev; + struct i2c_client *client; + int v_sense_slot; + int i_sense_slot; + int volume_lvl; + int model_id; + bool dac_powered; + bool unmuted; +}; + +enum tas256x_model { + TAS2562, + TAS2563, + TAS2564, + TAS2110, +}; + +static int tas2562_set_samplerate(struct tas2562_data *tas2562, int samplerate) +{ + int samp_rate; + int ramp_rate; + + switch (samplerate) { + case 7350: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ; + break; + case 8000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ; + break; + case 14700: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ; + break; + case 16000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ; + break; + case 22050: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ; + break; + case 24000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ; + break; + case 29400: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ; + break; + case 32000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ; + break; + case 44100: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ; + break; + case 48000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ; + break; + case 88200: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ; + break; + case 96000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ; + break; + case 176400: + ramp_rate = TAS2562_TDM_CFG0_RAMPRATE_44_1; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ; + break; + case 192000: + ramp_rate = 0; + samp_rate = TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ; + break; + default: + dev_info(tas2562->dev, "%s, unsupported sample rate, %d\n", + __func__, samplerate); + return -EINVAL; + } + + snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG0, + TAS2562_TDM_CFG0_RAMPRATE_MASK, ramp_rate); + snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG0, + TAS2562_TDM_CFG0_SAMPRATE_MASK, samp_rate); + + return 0; +} + +static int tas2562_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + int left_slot, right_slot; + int slots_cfg; + int ret; + + if (!tx_mask) { + dev_err(component->dev, "tx masks must not be 0\n"); + return -EINVAL; + } + + if (slots == 1) { + if (tx_mask != 1) + return -EINVAL; + + left_slot = 0; + right_slot = 0; + } else { + left_slot = __ffs(tx_mask); + tx_mask &= ~(1 << left_slot); + if (tx_mask == 0) { + right_slot = left_slot; + } else { + right_slot = __ffs(tx_mask); + } + } + + slots_cfg = (right_slot << TAS2562_RIGHT_SLOT_SHIFT) | left_slot; + + ret = snd_soc_component_write(component, TAS2562_TDM_CFG3, slots_cfg); + if (ret < 0) + return ret; + + switch (slot_width) { + case 16: + ret = snd_soc_component_update_bits(component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXLEN_MASK, + TAS2562_TDM_CFG2_RXLEN_16B); + break; + case 24: + ret = snd_soc_component_update_bits(component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXLEN_MASK, + TAS2562_TDM_CFG2_RXLEN_24B); + break; + case 32: + ret = snd_soc_component_update_bits(component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXLEN_MASK, + TAS2562_TDM_CFG2_RXLEN_32B); + break; + + case 0: + /* Do not change slot width */ + break; + default: + dev_err(tas2562->dev, "slot width not supported"); + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG5, + TAS2562_TDM_CFG5_VSNS_SLOT_MASK, + tas2562->v_sense_slot); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG6, + TAS2562_TDM_CFG6_ISNS_SLOT_MASK, + tas2562->i_sense_slot); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2562_set_bitwidth(struct tas2562_data *tas2562, int bitwidth) +{ + int ret; + int val; + int sense_en; + + switch (bitwidth) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_component_update_bits(tas2562->component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXWLEN_MASK, + TAS2562_TDM_CFG2_RXWLEN_16B); + break; + case SNDRV_PCM_FORMAT_S24_LE: + snd_soc_component_update_bits(tas2562->component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXWLEN_MASK, + TAS2562_TDM_CFG2_RXWLEN_24B); + break; + case SNDRV_PCM_FORMAT_S32_LE: + snd_soc_component_update_bits(tas2562->component, + TAS2562_TDM_CFG2, + TAS2562_TDM_CFG2_RXWLEN_MASK, + TAS2562_TDM_CFG2_RXWLEN_32B); + break; + + default: + dev_info(tas2562->dev, "Unsupported bitwidth format\n"); + return -EINVAL; + } + + val = snd_soc_component_read(tas2562->component, TAS2562_PWR_CTRL); + if (val < 0) + return val; + + if (val & (1 << TAS2562_VSENSE_POWER_EN)) + sense_en = 0; + else + sense_en = TAS2562_TDM_CFG5_VSNS_EN; + + ret = snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG5, + TAS2562_TDM_CFG5_VSNS_EN, sense_en); + if (ret < 0) + return ret; + + if (val & (1 << TAS2562_ISENSE_POWER_EN)) + sense_en = 0; + else + sense_en = TAS2562_TDM_CFG6_ISNS_EN; + + ret = snd_soc_component_update_bits(tas2562->component, TAS2562_TDM_CFG6, + TAS2562_TDM_CFG6_ISNS_EN, sense_en); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2562_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + int ret; + + ret = tas2562_set_bitwidth(tas2562, params_format(params)); + if (ret) { + dev_err(tas2562->dev, "set bitwidth failed, %d\n", ret); + return ret; + } + + ret = tas2562_set_samplerate(tas2562, params_rate(params)); + if (ret) + dev_err(tas2562->dev, "set sample rate failed, %d\n", ret); + + return ret; +} + +static int tas2562_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + u8 asi_cfg_1 = 0; + u8 tdm_rx_start_slot = 0; + int ret; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + asi_cfg_1 = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + asi_cfg_1 |= TAS2562_TDM_CFG1_RX_FALLING; + break; + default: + dev_err(tas2562->dev, "ASI format Inverse is not found\n"); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1, + TAS2562_TDM_CFG1_RX_EDGE_MASK, + asi_cfg_1); + if (ret < 0) { + dev_err(tas2562->dev, "Failed to set RX edge\n"); + return ret; + } + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_DSP_B: + tdm_rx_start_slot = 0; + break; + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + tdm_rx_start_slot = 1; + break; + default: + dev_err(tas2562->dev, + "DAI Format is not found, fmt=0x%x\n", fmt); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG1, + TAS2562_RX_OFF_MASK, (tdm_rx_start_slot << 1)); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2562_update_pwr_ctrl(struct tas2562_data *tas2562) +{ + struct snd_soc_component *component = tas2562->component; + unsigned int val; + int ret; + + if (tas2562->dac_powered) + val = tas2562->unmuted ? + TAS2562_ACTIVE : TAS2562_MUTE; + else + val = TAS2562_SHUTDOWN; + + ret = snd_soc_component_update_bits(component, TAS2562_PWR_CTRL, + TAS2562_MODE_MASK, val); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2562_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(dai->component); + + tas2562->unmuted = !mute; + return tas2562_update_pwr_ctrl(tas2562); +} + +static int tas2562_codec_probe(struct snd_soc_component *component) +{ + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + + tas2562->component = component; + + if (tas2562->sdz_gpio) + gpiod_set_value_cansleep(tas2562->sdz_gpio, 1); + + return 0; +} + +#ifdef CONFIG_PM +static int tas2562_suspend(struct snd_soc_component *component) +{ + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + + regcache_cache_only(tas2562->regmap, true); + regcache_mark_dirty(tas2562->regmap); + + if (tas2562->sdz_gpio) + gpiod_set_value_cansleep(tas2562->sdz_gpio, 0); + + return 0; +} + +static int tas2562_resume(struct snd_soc_component *component) +{ + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + + if (tas2562->sdz_gpio) + gpiod_set_value_cansleep(tas2562->sdz_gpio, 1); + + regcache_cache_only(tas2562->regmap, false); + + return regcache_sync(tas2562->regmap); +} +#else +#define tas2562_suspend NULL +#define tas2562_resume NULL +#endif + +static const char * const tas2562_ASI1_src[] = { + "I2C offset", "Left", "Right", "LeftRightDiv2", +}; + +static SOC_ENUM_SINGLE_DECL(tas2562_ASI1_src_enum, TAS2562_TDM_CFG2, 4, + tas2562_ASI1_src); + +static const struct snd_kcontrol_new tas2562_asi1_mux = + SOC_DAPM_ENUM("ASI1 Source", tas2562_ASI1_src_enum); + +static int tas2562_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + tas2562->dac_powered = true; + ret = tas2562_update_pwr_ctrl(tas2562); + break; + case SND_SOC_DAPM_PRE_PMD: + tas2562->dac_powered = false; + ret = tas2562_update_pwr_ctrl(tas2562); + break; + default: + dev_err(tas2562->dev, "Not supported evevt\n"); + return -EINVAL; + } + + return ret; +} + +static int tas2562_volume_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = tas2562->volume_lvl; + return 0; +} + +static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); + int ret; + u32 reg_val; + + reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2]; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, + (reg_val & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, + ((reg_val >> 8) & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG2, + ((reg_val >> 16) & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, + ((reg_val >> 24) & 0xff)); + if (ret) + return ret; + + tas2562->volume_lvl = ucontrol->value.integer.value[0]; + + return 0; +} + +/* Digital Volume Control. From 0 dB to -110 dB in 1 dB steps */ +static const DECLARE_TLV_DB_SCALE(dvc_tlv, -11000, 100, 0); + +static DECLARE_TLV_DB_SCALE(tas2562_dac_tlv, 850, 50, 0); + +static const struct snd_kcontrol_new isense_switch = + SOC_DAPM_SINGLE("Switch", TAS2562_PWR_CTRL, TAS2562_ISENSE_POWER_EN, + 1, 1); + +static const struct snd_kcontrol_new vsense_switch = + SOC_DAPM_SINGLE("Switch", TAS2562_PWR_CTRL, TAS2562_VSENSE_POWER_EN, + 1, 1); + +static const struct snd_kcontrol_new tas2562_snd_controls[] = { + SOC_SINGLE_TLV("Amp Gain Volume", TAS2562_PB_CFG1, 1, 0x1c, 0, + tas2562_dac_tlv), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Volume Control", + .index = 0, + .tlv.p = dvc_tlv, + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_soc_info_volsw, + .get = tas2562_volume_control_get, + .put = tas2562_volume_control_put, + .private_value = SOC_SINGLE_VALUE(TAS2562_DVC_CFG1, 0, 110, 0, 0), + }, +}; + +static const struct snd_soc_dapm_widget tas2110_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route tas2110_audio_map[] = { + {"ASI1 Sel", "I2C offset", "ASI1"}, + {"ASI1 Sel", "Left", "ASI1"}, + {"ASI1 Sel", "Right", "ASI1"}, + {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, + { "DAC", NULL, "ASI1 Sel" }, + { "OUT", NULL, "DAC" }, +}; + +static const struct snd_soc_component_driver soc_component_dev_tas2110 = { + .probe = tas2562_codec_probe, + .suspend = tas2562_suspend, + .resume = tas2562_resume, + .controls = tas2562_snd_controls, + .num_controls = ARRAY_SIZE(tas2562_snd_controls), + .dapm_widgets = tas2110_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2110_dapm_widgets), + .dapm_routes = tas2110_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2110_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH("ISENSE", TAS2562_PWR_CTRL, 3, 1, &isense_switch), + SND_SOC_DAPM_SWITCH("VSENSE", TAS2562_PWR_CTRL, 2, 1, &vsense_switch), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON"), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route tas2562_audio_map[] = { + {"ASI1 Sel", "I2C offset", "ASI1"}, + {"ASI1 Sel", "Left", "ASI1"}, + {"ASI1 Sel", "Right", "ASI1"}, + {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, + { "DAC", NULL, "ASI1 Sel" }, + { "OUT", NULL, "DAC" }, + {"ISENSE", "Switch", "IMON"}, + {"VSENSE", "Switch", "VMON"}, +}; + +static const struct snd_soc_component_driver soc_component_dev_tas2562 = { + .probe = tas2562_codec_probe, + .suspend = tas2562_suspend, + .resume = tas2562_resume, + .controls = tas2562_snd_controls, + .num_controls = ARRAY_SIZE(tas2562_snd_controls), + .dapm_widgets = tas2562_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2562_dapm_widgets), + .dapm_routes = tas2562_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2562_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_dai_ops tas2562_speaker_dai_ops = { + .hw_params = tas2562_hw_params, + .set_fmt = tas2562_set_dai_fmt, + .set_tdm_slot = tas2562_set_dai_tdm_slot, + .mute_stream = tas2562_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver tas2562_dai[] = { + { + .name = "tas2562-amplifier", + .id = 0, + .playback = { + .stream_name = "ASI1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2562_FORMATS, + }, + .capture = { + .stream_name = "ASI1 Capture", + .channels_min = 0, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2562_FORMATS, + }, + .ops = &tas2562_speaker_dai_ops, + }, +}; + +static const struct regmap_range_cfg tas2562_ranges[] = { + { + .range_min = 0, + .range_max = 5 * 128, + .selector_reg = TAS2562_PAGE_CTRL, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct reg_default tas2562_reg_defaults[] = { + { TAS2562_PAGE_CTRL, 0x00 }, + { TAS2562_SW_RESET, 0x00 }, + { TAS2562_PWR_CTRL, 0x0e }, + { TAS2562_PB_CFG1, 0x20 }, + { TAS2562_TDM_CFG0, 0x09 }, + { TAS2562_TDM_CFG1, 0x02 }, + { TAS2562_DVC_CFG1, 0x40 }, + { TAS2562_DVC_CFG2, 0x40 }, + { TAS2562_DVC_CFG3, 0x00 }, + { TAS2562_DVC_CFG4, 0x00 }, +}; + +static const struct regmap_config tas2562_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 5 * 128, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = tas2562_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas2562_reg_defaults), + .ranges = tas2562_ranges, + .num_ranges = ARRAY_SIZE(tas2562_ranges), +}; + +static int tas2562_parse_dt(struct tas2562_data *tas2562) +{ + struct device *dev = tas2562->dev; + int ret = 0; + + tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); + if (IS_ERR(tas2562->sdz_gpio)) { + if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + tas2562->sdz_gpio = NULL; + } + + /* + * The shut-down property is deprecated but needs to be checked for + * backwards compatibility. + */ + if (tas2562->sdz_gpio == NULL) { + tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down", + GPIOD_OUT_HIGH); + if (IS_ERR(tas2562->sdz_gpio)) + if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + tas2562->sdz_gpio = NULL; + } + + if (tas2562->model_id == TAS2110) + return ret; + + ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", + &tas2562->i_sense_slot); + if (ret) { + dev_err(dev, "Property %s is missing setting default slot\n", + "ti,imon-slot-no"); + tas2562->i_sense_slot = 0; + } + + + ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", + &tas2562->v_sense_slot); + if (ret) { + dev_info(dev, "Property %s is missing setting default slot\n", + "ti,vmon-slot-no"); + tas2562->v_sense_slot = 2; + } + + if (tas2562->v_sense_slot < tas2562->i_sense_slot) { + dev_err(dev, "Vsense slot must be greater than Isense slot\n"); + return -EINVAL; + } + + return ret; +} + +static const struct i2c_device_id tas2562_id[] = { + { "tas2562", TAS2562 }, + { "tas2563", TAS2563 }, + { "tas2564", TAS2564 }, + { "tas2110", TAS2110 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2562_id); + +static int tas2562_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct tas2562_data *data; + int ret; + const struct i2c_device_id *id; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + id = i2c_match_id(tas2562_id, client); + data->client = client; + data->dev = &client->dev; + data->model_id = id->driver_data; + + tas2562_parse_dt(data); + + data->regmap = devm_regmap_init_i2c(client, &tas2562_regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + dev_err(dev, "failed to allocate register map: %d\n", ret); + return ret; + } + + dev_set_drvdata(&client->dev, data); + + if (data->model_id == TAS2110) + return devm_snd_soc_register_component(dev, + &soc_component_dev_tas2110, + tas2562_dai, + ARRAY_SIZE(tas2562_dai)); + + return devm_snd_soc_register_component(dev, &soc_component_dev_tas2562, + tas2562_dai, + ARRAY_SIZE(tas2562_dai)); + +} + +#ifdef CONFIG_OF +static const struct of_device_id tas2562_of_match[] = { + { .compatible = "ti,tas2562", }, + { .compatible = "ti,tas2563", }, + { .compatible = "ti,tas2564", }, + { .compatible = "ti,tas2110", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tas2562_of_match); +#endif + +static struct i2c_driver tas2562_i2c_driver = { + .driver = { + .name = "tas2562", + .of_match_table = of_match_ptr(tas2562_of_match), + }, + .probe = tas2562_probe, + .id_table = tas2562_id, +}; + +module_i2c_driver(tas2562_i2c_driver); + +MODULE_AUTHOR("Dan Murphy "); +MODULE_DESCRIPTION("TAS2562 Audio amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2562.h b/sound/soc/codecs/tas2562.h new file mode 100644 index 0000000000..55b2a1f52c --- /dev/null +++ b/sound/soc/codecs/tas2562.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tas2562.h - ALSA SoC Texas Instruments TAS2562 Mono Audio Amplifier + * + * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com + * + * Author: Dan Murphy + */ + +#ifndef __TAS2562_H__ +#define __TAS2562_H__ + +#define TAS2562_PAGE_CTRL 0x00 + +#define TAS2562_REG(page, reg) ((page * 128) + reg) + +#define TAS2562_SW_RESET TAS2562_REG(0, 0x01) +#define TAS2562_PWR_CTRL TAS2562_REG(0, 0x02) +#define TAS2562_PB_CFG1 TAS2562_REG(0, 0x03) +#define TAS2562_MISC_CFG1 TAS2562_REG(0, 0x04) +#define TAS2562_MISC_CFG2 TAS2562_REG(0, 0x05) + +#define TAS2562_TDM_CFG0 TAS2562_REG(0, 0x06) +#define TAS2562_TDM_CFG1 TAS2562_REG(0, 0x07) +#define TAS2562_TDM_CFG2 TAS2562_REG(0, 0x08) +#define TAS2562_TDM_CFG3 TAS2562_REG(0, 0x09) +#define TAS2562_TDM_CFG4 TAS2562_REG(0, 0x0a) +#define TAS2562_TDM_CFG5 TAS2562_REG(0, 0x0b) +#define TAS2562_TDM_CFG6 TAS2562_REG(0, 0x0c) +#define TAS2562_TDM_CFG7 TAS2562_REG(0, 0x0d) +#define TAS2562_TDM_CFG8 TAS2562_REG(0, 0x0e) +#define TAS2562_TDM_CFG9 TAS2562_REG(0, 0x0f) +#define TAS2562_TDM_CFG10 TAS2562_REG(0, 0x10) +#define TAS2562_TDM_DET TAS2562_REG(0, 0x11) +#define TAS2562_REV_ID TAS2562_REG(0, 0x7d) + +#define TAS2562_RX_OFF_MASK GENMASK(5, 1) +#define TAS2562_TX_OFF_MASK GENMASK(3, 1) +#define TAS2562_RIGHT_SLOT_SHIFT 4 + +/* Page 2 */ +#define TAS2562_DVC_CFG1 TAS2562_REG(2, 0x0c) +#define TAS2562_DVC_CFG2 TAS2562_REG(2, 0x0d) +#define TAS2562_DVC_CFG3 TAS2562_REG(2, 0x0e) +#define TAS2562_DVC_CFG4 TAS2562_REG(2, 0x0f) + +#define TAS2562_RESET BIT(0) + +#define TAS2562_MODE_MASK GENMASK(1,0) +#define TAS2562_ACTIVE 0x0 +#define TAS2562_MUTE 0x1 +#define TAS2562_SHUTDOWN 0x2 + +#define TAS2562_TDM_CFG1_RX_EDGE_MASK BIT(0) +#define TAS2562_TDM_CFG1_RX_FALLING 1 + +#define TAS2562_TDM_CFG0_RAMPRATE_MASK BIT(5) +#define TAS2562_TDM_CFG0_RAMPRATE_44_1 BIT(5) +#define TAS2562_TDM_CFG0_SAMPRATE_MASK GENMASK(3, 1) +#define TAS2562_TDM_CFG0_SAMPRATE_7305_8KHZ (0x0 << 1) +#define TAS2562_TDM_CFG0_SAMPRATE_14_7_16KHZ (0x1 << 1) +#define TAS2562_TDM_CFG0_SAMPRATE_22_05_24KHZ (0x2 << 1) +#define TAS2562_TDM_CFG0_SAMPRATE_29_4_32KHZ (0x3 << 1) +#define TAS2562_TDM_CFG0_SAMPRATE_44_1_48KHZ (0x4 << 1) +#define TAS2562_TDM_CFG0_SAMPRATE_88_2_96KHZ (0x5 << 1) +#define TAS2562_TDM_CFG0_SAMPRATE_176_4_192KHZ (0x6 << 1) + +#define TAS2562_TDM_CFG2_RIGHT_JUSTIFY BIT(6) + +#define TAS2562_TDM_CFG2_RXLEN_MASK GENMASK(1, 0) +#define TAS2562_TDM_CFG2_RXLEN_16B 0x0 +#define TAS2562_TDM_CFG2_RXLEN_24B BIT(0) +#define TAS2562_TDM_CFG2_RXLEN_32B BIT(1) + +#define TAS2562_TDM_CFG2_RXWLEN_MASK GENMASK(3, 2) +#define TAS2562_TDM_CFG2_RXWLEN_16B 0x0 +#define TAS2562_TDM_CFG2_RXWLEN_20B BIT(2) +#define TAS2562_TDM_CFG2_RXWLEN_24B BIT(3) +#define TAS2562_TDM_CFG2_RXWLEN_32B (BIT(2) | BIT(3)) + +#define TAS2562_VSENSE_POWER_EN 2 +#define TAS2562_ISENSE_POWER_EN 3 + +#define TAS2562_TDM_CFG5_VSNS_EN BIT(6) +#define TAS2562_TDM_CFG5_VSNS_SLOT_MASK GENMASK(5, 0) + +#define TAS2562_TDM_CFG6_ISNS_EN BIT(6) +#define TAS2562_TDM_CFG6_ISNS_SLOT_MASK GENMASK(5, 0) + +#endif /* __TAS2562_H__ */ diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c new file mode 100644 index 0000000000..a9838e0738 --- /dev/null +++ b/sound/soc/codecs/tas2764.c @@ -0,0 +1,766 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for the Texas Instruments TAS2764 CODEC +// Copyright (C) 2020 Texas Instruments Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2764.h" + +struct tas2764_priv { + struct snd_soc_component *component; + struct gpio_desc *reset_gpio; + struct gpio_desc *sdz_gpio; + struct regmap *regmap; + struct device *dev; + int irq; + + int v_sense_slot; + int i_sense_slot; + + bool dac_powered; + bool unmuted; +}; + +static const char *tas2764_int_ltch0_msgs[8] = { + "fault: over temperature", /* INT_LTCH0 & BIT(0) */ + "fault: over current", + "fault: bad TDM clock", + "limiter active", + "fault: PVDD below limiter inflection point", + "fault: limiter max attenuation", + "fault: BOP infinite hold", + "fault: BOP mute", /* INT_LTCH0 & BIT(7) */ +}; + +static const unsigned int tas2764_int_readout_regs[6] = { + TAS2764_INT_LTCH0, + TAS2764_INT_LTCH1, + TAS2764_INT_LTCH1_0, + TAS2764_INT_LTCH2, + TAS2764_INT_LTCH3, + TAS2764_INT_LTCH4, +}; + +static irqreturn_t tas2764_irq(int irq, void *data) +{ + struct tas2764_priv *tas2764 = data; + u8 latched[6] = {0, 0, 0, 0, 0, 0}; + int ret = IRQ_NONE; + int i; + + for (i = 0; i < ARRAY_SIZE(latched); i++) + latched[i] = snd_soc_component_read(tas2764->component, + tas2764_int_readout_regs[i]); + + for (i = 0; i < 8; i++) { + if (latched[0] & BIT(i)) { + dev_crit_ratelimited(tas2764->dev, "%s\n", + tas2764_int_ltch0_msgs[i]); + ret = IRQ_HANDLED; + } + } + + if (latched[0]) { + dev_err_ratelimited(tas2764->dev, "other context to the fault: %02x,%02x,%02x,%02x,%02x", + latched[1], latched[2], latched[3], latched[4], latched[5]); + snd_soc_component_update_bits(tas2764->component, + TAS2764_INT_CLK_CFG, + TAS2764_INT_CLK_CFG_IRQZ_CLR, + TAS2764_INT_CLK_CFG_IRQZ_CLR); + } + + return ret; +} + +static void tas2764_reset(struct tas2764_priv *tas2764) +{ + if (tas2764->reset_gpio) { + gpiod_set_value_cansleep(tas2764->reset_gpio, 0); + msleep(20); + gpiod_set_value_cansleep(tas2764->reset_gpio, 1); + usleep_range(1000, 2000); + } + + snd_soc_component_write(tas2764->component, TAS2764_SW_RST, + TAS2764_RST); + usleep_range(1000, 2000); +} + +static int tas2764_update_pwr_ctrl(struct tas2764_priv *tas2764) +{ + struct snd_soc_component *component = tas2764->component; + unsigned int val; + int ret; + + if (tas2764->dac_powered) + val = tas2764->unmuted ? + TAS2764_PWR_CTRL_ACTIVE : TAS2764_PWR_CTRL_MUTE; + else + val = TAS2764_PWR_CTRL_SHUTDOWN; + + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, val); + if (ret < 0) + return ret; + + return 0; +} + +#ifdef CONFIG_PM +static int tas2764_codec_suspend(struct snd_soc_component *component) +{ + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_SHUTDOWN); + + if (ret < 0) + return ret; + + if (tas2764->sdz_gpio) + gpiod_set_value_cansleep(tas2764->sdz_gpio, 0); + + regcache_cache_only(tas2764->regmap, true); + regcache_mark_dirty(tas2764->regmap); + + return 0; +} + +static int tas2764_codec_resume(struct snd_soc_component *component) +{ + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + if (tas2764->sdz_gpio) { + gpiod_set_value_cansleep(tas2764->sdz_gpio, 1); + usleep_range(1000, 2000); + } + + ret = tas2764_update_pwr_ctrl(tas2764); + + if (ret < 0) + return ret; + + regcache_cache_only(tas2764->regmap, false); + + return regcache_sync(tas2764->regmap); +} +#else +#define tas2764_codec_suspend NULL +#define tas2764_codec_resume NULL +#endif + +static const char * const tas2764_ASI1_src[] = { + "I2C offset", "Left", "Right", "LeftRightDiv2", +}; + +static SOC_ENUM_SINGLE_DECL( + tas2764_ASI1_src_enum, TAS2764_TDM_CFG2, TAS2764_TDM_CFG2_SCFG_SHIFT, + tas2764_ASI1_src); + +static const struct snd_kcontrol_new tas2764_asi1_mux = + SOC_DAPM_ENUM("ASI1 Source", tas2764_ASI1_src_enum); + +static int tas2764_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + tas2764->dac_powered = true; + ret = tas2764_update_pwr_ctrl(tas2764); + break; + case SND_SOC_DAPM_PRE_PMD: + tas2764->dac_powered = false; + ret = tas2764_update_pwr_ctrl(tas2764); + break; + default: + dev_err(tas2764->dev, "Unsupported event\n"); + return -EINVAL; + } + + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_kcontrol_new isense_switch = + SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, 1, 1); +static const struct snd_kcontrol_new vsense_switch = + SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, 1, 1); + +static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2764_asi1_mux), + SND_SOC_DAPM_SWITCH("ISENSE", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, + 1, &isense_switch), + SND_SOC_DAPM_SWITCH("VSENSE", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, + 1, &vsense_switch), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2764_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON") +}; + +static const struct snd_soc_dapm_route tas2764_audio_map[] = { + {"ASI1 Sel", "I2C offset", "ASI1"}, + {"ASI1 Sel", "Left", "ASI1"}, + {"ASI1 Sel", "Right", "ASI1"}, + {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, + {"DAC", NULL, "ASI1 Sel"}, + {"OUT", NULL, "DAC"}, + {"ISENSE", "Switch", "IMON"}, + {"VSENSE", "Switch", "VMON"}, +}; + +static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct tas2764_priv *tas2764 = + snd_soc_component_get_drvdata(dai->component); + + tas2764->unmuted = !mute; + return tas2764_update_pwr_ctrl(tas2764); +} + +static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth) +{ + struct snd_soc_component *component = tas2764->component; + int sense_en; + int val; + int ret; + + switch (bitwidth) { + case SNDRV_PCM_FORMAT_S16_LE: + ret = snd_soc_component_update_bits(component, + TAS2764_TDM_CFG2, + TAS2764_TDM_CFG2_RXW_MASK, + TAS2764_TDM_CFG2_RXW_16BITS); + break; + case SNDRV_PCM_FORMAT_S24_LE: + ret = snd_soc_component_update_bits(component, + TAS2764_TDM_CFG2, + TAS2764_TDM_CFG2_RXW_MASK, + TAS2764_TDM_CFG2_RXW_24BITS); + break; + case SNDRV_PCM_FORMAT_S32_LE: + ret = snd_soc_component_update_bits(component, + TAS2764_TDM_CFG2, + TAS2764_TDM_CFG2_RXW_MASK, + TAS2764_TDM_CFG2_RXW_32BITS); + break; + + default: + return -EINVAL; + } + + if (ret < 0) + return ret; + + val = snd_soc_component_read(tas2764->component, TAS2764_PWR_CTRL); + if (val < 0) + return val; + + if (val & (1 << TAS2764_VSENSE_POWER_EN)) + sense_en = 0; + else + sense_en = TAS2764_TDM_CFG5_VSNS_ENABLE; + + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5, + TAS2764_TDM_CFG5_VSNS_ENABLE, + sense_en); + if (ret < 0) + return ret; + + if (val & (1 << TAS2764_ISENSE_POWER_EN)) + sense_en = 0; + else + sense_en = TAS2764_TDM_CFG6_ISNS_ENABLE; + + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6, + TAS2764_TDM_CFG6_ISNS_ENABLE, + sense_en); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2764_set_samplerate(struct tas2764_priv *tas2764, int samplerate) +{ + struct snd_soc_component *component = tas2764->component; + int ramp_rate_val; + int ret; + + switch (samplerate) { + case 48000: + ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ | + TAS2764_TDM_CFG0_44_1_48KHZ; + break; + case 44100: + ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ | + TAS2764_TDM_CFG0_44_1_48KHZ; + break; + case 96000: + ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ | + TAS2764_TDM_CFG0_88_2_96KHZ; + break; + case 88200: + ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ | + TAS2764_TDM_CFG0_88_2_96KHZ; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0, + TAS2764_TDM_CFG0_SMP_MASK | + TAS2764_TDM_CFG0_MASK, + ramp_rate_val); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2764_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + ret = tas2764_set_bitwidth(tas2764, params_format(params)); + if (ret < 0) + return ret; + + return tas2764_set_samplerate(tas2764, params_rate(params)); +} + +static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + u8 tdm_rx_start_slot = 0, asi_cfg_0 = 0, asi_cfg_1 = 0; + int ret; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START; + fallthrough; + case SND_SOC_DAIFMT_NB_NF: + asi_cfg_1 = TAS2764_TDM_CFG1_RX_RISING; + break; + case SND_SOC_DAIFMT_IB_IF: + asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START; + fallthrough; + case SND_SOC_DAIFMT_IB_NF: + asi_cfg_1 = TAS2764_TDM_CFG1_RX_FALLING; + break; + } + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1, + TAS2764_TDM_CFG1_RX_MASK, + asi_cfg_1); + if (ret < 0) + return ret; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + tdm_rx_start_slot = 1; + break; + case SND_SOC_DAIFMT_DSP_B: + case SND_SOC_DAIFMT_LEFT_J: + tdm_rx_start_slot = 0; + break; + default: + dev_err(tas2764->dev, + "DAI Format is not found, fmt=0x%x\n", fmt); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0, + TAS2764_TDM_CFG0_FRAME_START, + asi_cfg_0); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1, + TAS2764_TDM_CFG1_MASK, + (tdm_rx_start_slot << TAS2764_TDM_CFG1_51_SHIFT)); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int left_slot, right_slot; + int slots_cfg; + int slot_size; + int ret; + + if (tx_mask == 0 || rx_mask != 0) + return -EINVAL; + + left_slot = __ffs(tx_mask); + tx_mask &= ~(1 << left_slot); + if (tx_mask == 0) { + right_slot = left_slot; + } else { + right_slot = __ffs(tx_mask); + tx_mask &= ~(1 << right_slot); + } + + if (tx_mask != 0 || left_slot >= slots || right_slot >= slots) + return -EINVAL; + + slots_cfg = (right_slot << TAS2764_TDM_CFG3_RXS_SHIFT) | left_slot; + + ret = snd_soc_component_write(component, TAS2764_TDM_CFG3, slots_cfg); + if (ret) + return ret; + + switch (slot_width) { + case 16: + slot_size = TAS2764_TDM_CFG2_RXS_16BITS; + break; + case 24: + slot_size = TAS2764_TDM_CFG2_RXS_24BITS; + break; + case 32: + slot_size = TAS2764_TDM_CFG2_RXS_32BITS; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2, + TAS2764_TDM_CFG2_RXS_MASK, + slot_size); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG5, + TAS2764_TDM_CFG5_50_MASK, + tas2764->v_sense_slot); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG6, + TAS2764_TDM_CFG6_50_MASK, + tas2764->i_sense_slot); + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_soc_dai_ops tas2764_dai_ops = { + .mute_stream = tas2764_mute, + .hw_params = tas2764_hw_params, + .set_fmt = tas2764_set_fmt, + .set_tdm_slot = tas2764_set_dai_tdm_slot, + .no_capture_mute = 1, +}; + +#define TAS2764_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define TAS2764_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200) + +static struct snd_soc_dai_driver tas2764_dai_driver[] = { + { + .name = "tas2764 ASI1", + .id = 0, + .playback = { + .stream_name = "ASI1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAS2764_RATES, + .formats = TAS2764_FORMATS, + }, + .capture = { + .stream_name = "ASI1 Capture", + .channels_min = 0, + .channels_max = 2, + .rates = TAS2764_RATES, + .formats = TAS2764_FORMATS, + }, + .ops = &tas2764_dai_ops, + .symmetric_rate = 1, + }, +}; + +static int tas2764_codec_probe(struct snd_soc_component *component) +{ + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + tas2764->component = component; + + if (tas2764->sdz_gpio) { + gpiod_set_value_cansleep(tas2764->sdz_gpio, 1); + usleep_range(1000, 2000); + } + + tas2764_reset(tas2764); + + if (tas2764->irq) { + ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0xff); + if (ret < 0) + return ret; + + ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK1, 0xff); + if (ret < 0) + return ret; + + ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK2, 0xff); + if (ret < 0) + return ret; + + ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK3, 0xff); + if (ret < 0) + return ret; + + ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK4, 0xff); + if (ret < 0) + return ret; + + ret = devm_request_threaded_irq(tas2764->dev, tas2764->irq, NULL, tas2764_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW, + "tas2764", tas2764); + if (ret) + dev_warn(tas2764->dev, "failed to request IRQ: %d\n", ret); + } + + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5, + TAS2764_TDM_CFG5_VSNS_ENABLE, 0); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6, + TAS2764_TDM_CFG6_ISNS_ENABLE, 0); + if (ret < 0) + return ret; + + return 0; +} + +static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0); +static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10050, 50, 1); + +static const char * const tas2764_hpf_texts[] = { + "Disabled", "2 Hz", "50 Hz", "100 Hz", "200 Hz", + "400 Hz", "800 Hz" +}; + +static SOC_ENUM_SINGLE_DECL( + tas2764_hpf_enum, TAS2764_DC_BLK0, + TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT, tas2764_hpf_texts); + +static const struct snd_kcontrol_new tas2764_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", TAS2764_DVC, 0, + TAS2764_DVC_MAX, 1, tas2764_playback_volume), + SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 1, 0x14, 0, + tas2764_digital_tlv), + SOC_ENUM("HPF Corner Frequency", tas2764_hpf_enum), +}; + +static const struct snd_soc_component_driver soc_component_driver_tas2764 = { + .probe = tas2764_codec_probe, + .suspend = tas2764_codec_suspend, + .resume = tas2764_codec_resume, + .controls = tas2764_snd_controls, + .num_controls = ARRAY_SIZE(tas2764_snd_controls), + .dapm_widgets = tas2764_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2764_dapm_widgets), + .dapm_routes = tas2764_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2764_audio_map), + .idle_bias_on = 1, + .endianness = 1, +}; + +static const struct reg_default tas2764_reg_defaults[] = { + { TAS2764_PAGE, 0x00 }, + { TAS2764_SW_RST, 0x00 }, + { TAS2764_PWR_CTRL, 0x1a }, + { TAS2764_DVC, 0x00 }, + { TAS2764_CHNL_0, 0x28 }, + { TAS2764_TDM_CFG0, 0x09 }, + { TAS2764_TDM_CFG1, 0x02 }, + { TAS2764_TDM_CFG2, 0x0a }, + { TAS2764_TDM_CFG3, 0x10 }, + { TAS2764_TDM_CFG5, 0x42 }, +}; + +static const struct regmap_range_cfg tas2764_regmap_ranges[] = { + { + .range_min = 0, + .range_max = 1 * 128, + .selector_reg = TAS2764_PAGE, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static bool tas2764_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4: + case TAS2764_INT_CLK_CFG: + return true; + default: + return false; + } +} + +static const struct regmap_config tas2764_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = tas2764_volatile_register, + .reg_defaults = tas2764_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas2764_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .ranges = tas2764_regmap_ranges, + .num_ranges = ARRAY_SIZE(tas2764_regmap_ranges), + .max_register = 1 * 128, +}; + +static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) +{ + int ret = 0; + + tas2764->reset_gpio = devm_gpiod_get_optional(tas2764->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(tas2764->reset_gpio)) { + if (PTR_ERR(tas2764->reset_gpio) == -EPROBE_DEFER) { + tas2764->reset_gpio = NULL; + return -EPROBE_DEFER; + } + } + + tas2764->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); + if (IS_ERR(tas2764->sdz_gpio)) { + if (PTR_ERR(tas2764->sdz_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + tas2764->sdz_gpio = NULL; + } + + ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", + &tas2764->i_sense_slot); + if (ret) + tas2764->i_sense_slot = 0; + + ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", + &tas2764->v_sense_slot); + if (ret) + tas2764->v_sense_slot = 2; + + return 0; +} + +static int tas2764_i2c_probe(struct i2c_client *client) +{ + struct tas2764_priv *tas2764; + int result; + + tas2764 = devm_kzalloc(&client->dev, sizeof(struct tas2764_priv), + GFP_KERNEL); + if (!tas2764) + return -ENOMEM; + + tas2764->dev = &client->dev; + tas2764->irq = client->irq; + i2c_set_clientdata(client, tas2764); + dev_set_drvdata(&client->dev, tas2764); + + tas2764->regmap = devm_regmap_init_i2c(client, &tas2764_i2c_regmap); + if (IS_ERR(tas2764->regmap)) { + result = PTR_ERR(tas2764->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + result); + return result; + } + + if (client->dev.of_node) { + result = tas2764_parse_dt(&client->dev, tas2764); + if (result) { + dev_err(tas2764->dev, "%s: Failed to parse devicetree\n", + __func__); + return result; + } + } + + return devm_snd_soc_register_component(tas2764->dev, + &soc_component_driver_tas2764, + tas2764_dai_driver, + ARRAY_SIZE(tas2764_dai_driver)); +} + +static const struct i2c_device_id tas2764_i2c_id[] = { + { "tas2764", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id tas2764_of_match[] = { + { .compatible = "ti,tas2764" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tas2764_of_match); +#endif + +static struct i2c_driver tas2764_i2c_driver = { + .driver = { + .name = "tas2764", + .of_match_table = of_match_ptr(tas2764_of_match), + }, + .probe = tas2764_i2c_probe, + .id_table = tas2764_i2c_id, +}; +module_i2c_driver(tas2764_i2c_driver); + +MODULE_AUTHOR("Dan Murphy "); +MODULE_DESCRIPTION("TAS2764 I2C Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h new file mode 100644 index 0000000000..168af772a8 --- /dev/null +++ b/sound/soc/codecs/tas2764.h @@ -0,0 +1,113 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tas2764.h - ALSA SoC Texas Instruments TAS2764 Mono Audio Amplifier + * + * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com + * + * Author: Dan Murphy + */ + +#ifndef __TAS2764__ +#define __TAS2764__ + +/* Book Control Register */ +#define TAS2764_BOOKCTL_PAGE 0 +#define TAS2764_BOOKCTL_REG 127 +#define TAS2764_REG(page, reg) ((page * 128) + reg) + +/* Page */ +#define TAS2764_PAGE TAS2764_REG(0X0, 0x00) +#define TAS2764_PAGE_PAGE_MASK 255 + +/* Software Reset */ +#define TAS2764_SW_RST TAS2764_REG(0X0, 0x01) +#define TAS2764_RST BIT(0) + +/* Power Control */ +#define TAS2764_PWR_CTRL TAS2764_REG(0X0, 0x02) +#define TAS2764_PWR_CTRL_MASK GENMASK(1, 0) +#define TAS2764_PWR_CTRL_ACTIVE 0x0 +#define TAS2764_PWR_CTRL_MUTE BIT(0) +#define TAS2764_PWR_CTRL_SHUTDOWN BIT(1) + +#define TAS2764_VSENSE_POWER_EN 3 +#define TAS2764_ISENSE_POWER_EN 4 + +/* DC Blocker Control */ +#define TAS2764_DC_BLK0 TAS2764_REG(0x0, 0x04) +#define TAS2764_DC_BLK0_HPF_FREQ_PB_SHIFT 0 + +/* Digital Volume Control */ +#define TAS2764_DVC TAS2764_REG(0X0, 0x1a) +#define TAS2764_DVC_MAX 0xc9 + +#define TAS2764_CHNL_0 TAS2764_REG(0X0, 0x03) + +/* TDM Configuration Reg0 */ +#define TAS2764_TDM_CFG0 TAS2764_REG(0X0, 0x08) +#define TAS2764_TDM_CFG0_SMP_MASK BIT(5) +#define TAS2764_TDM_CFG0_SMP_48KHZ 0x0 +#define TAS2764_TDM_CFG0_SMP_44_1KHZ BIT(5) +#define TAS2764_TDM_CFG0_MASK GENMASK(3, 1) +#define TAS2764_TDM_CFG0_44_1_48KHZ BIT(3) +#define TAS2764_TDM_CFG0_88_2_96KHZ (BIT(3) | BIT(1)) +#define TAS2764_TDM_CFG0_FRAME_START BIT(0) + +/* TDM Configuration Reg1 */ +#define TAS2764_TDM_CFG1 TAS2764_REG(0X0, 0x09) +#define TAS2764_TDM_CFG1_MASK GENMASK(5, 1) +#define TAS2764_TDM_CFG1_51_SHIFT 1 +#define TAS2764_TDM_CFG1_RX_MASK BIT(0) +#define TAS2764_TDM_CFG1_RX_RISING 0x0 +#define TAS2764_TDM_CFG1_RX_FALLING BIT(0) + +/* TDM Configuration Reg2 */ +#define TAS2764_TDM_CFG2 TAS2764_REG(0X0, 0x0a) +#define TAS2764_TDM_CFG2_RXW_MASK GENMASK(3, 2) +#define TAS2764_TDM_CFG2_RXW_16BITS 0x0 +#define TAS2764_TDM_CFG2_RXW_24BITS BIT(3) +#define TAS2764_TDM_CFG2_RXW_32BITS (BIT(3) | BIT(2)) +#define TAS2764_TDM_CFG2_RXS_MASK GENMASK(1, 0) +#define TAS2764_TDM_CFG2_RXS_16BITS 0x0 +#define TAS2764_TDM_CFG2_RXS_24BITS BIT(0) +#define TAS2764_TDM_CFG2_RXS_32BITS BIT(1) +#define TAS2764_TDM_CFG2_SCFG_SHIFT 4 + +/* TDM Configuration Reg3 */ +#define TAS2764_TDM_CFG3 TAS2764_REG(0X0, 0x0c) +#define TAS2764_TDM_CFG3_RXS_MASK GENMASK(7, 4) +#define TAS2764_TDM_CFG3_RXS_SHIFT 0x4 +#define TAS2764_TDM_CFG3_MASK GENMASK(3, 0) + +/* TDM Configuration Reg5 */ +#define TAS2764_TDM_CFG5 TAS2764_REG(0X0, 0x0e) +#define TAS2764_TDM_CFG5_VSNS_MASK BIT(6) +#define TAS2764_TDM_CFG5_VSNS_ENABLE BIT(6) +#define TAS2764_TDM_CFG5_50_MASK GENMASK(5, 0) + +/* TDM Configuration Reg6 */ +#define TAS2764_TDM_CFG6 TAS2764_REG(0X0, 0x0f) +#define TAS2764_TDM_CFG6_ISNS_MASK BIT(6) +#define TAS2764_TDM_CFG6_ISNS_ENABLE BIT(6) +#define TAS2764_TDM_CFG6_50_MASK GENMASK(5, 0) + +/* Interrupt Masks */ +#define TAS2764_INT_MASK0 TAS2764_REG(0x0, 0x3b) +#define TAS2764_INT_MASK1 TAS2764_REG(0x0, 0x3c) +#define TAS2764_INT_MASK2 TAS2764_REG(0x0, 0x40) +#define TAS2764_INT_MASK3 TAS2764_REG(0x0, 0x41) +#define TAS2764_INT_MASK4 TAS2764_REG(0x0, 0x3d) + +/* Latched Fault Registers */ +#define TAS2764_INT_LTCH0 TAS2764_REG(0x0, 0x49) +#define TAS2764_INT_LTCH1 TAS2764_REG(0x0, 0x4a) +#define TAS2764_INT_LTCH1_0 TAS2764_REG(0x0, 0x4b) +#define TAS2764_INT_LTCH2 TAS2764_REG(0x0, 0x4f) +#define TAS2764_INT_LTCH3 TAS2764_REG(0x0, 0x50) +#define TAS2764_INT_LTCH4 TAS2764_REG(0x0, 0x51) + +/* Clock/IRQ Settings */ +#define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c) +#define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2) + +#endif /* __TAS2764__ */ diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c new file mode 100644 index 0000000000..99bf402eb5 --- /dev/null +++ b/sound/soc/codecs/tas2770.c @@ -0,0 +1,730 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC Texas Instruments TAS2770 20-W Digital Input Mono Class-D +// Audio Amplifier with Speaker I/V Sense +// +// Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/ +// Author: Tracy Yi +// Frank Shi + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2770.h" + +#define TAS2770_MDELAY 0xFFFFFFFE + +static void tas2770_reset(struct tas2770_priv *tas2770) +{ + if (tas2770->reset_gpio) { + gpiod_set_value_cansleep(tas2770->reset_gpio, 0); + msleep(20); + gpiod_set_value_cansleep(tas2770->reset_gpio, 1); + usleep_range(1000, 2000); + } + + snd_soc_component_write(tas2770->component, TAS2770_SW_RST, + TAS2770_RST); + usleep_range(1000, 2000); +} + +static int tas2770_update_pwr_ctrl(struct tas2770_priv *tas2770) +{ + struct snd_soc_component *component = tas2770->component; + unsigned int val; + int ret; + + if (tas2770->dac_powered) + val = tas2770->unmuted ? + TAS2770_PWR_CTRL_ACTIVE : TAS2770_PWR_CTRL_MUTE; + else + val = TAS2770_PWR_CTRL_SHUTDOWN; + + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, val); + if (ret < 0) + return ret; + + return 0; +} + +#ifdef CONFIG_PM +static int tas2770_codec_suspend(struct snd_soc_component *component) +{ + struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + int ret = 0; + + regcache_cache_only(tas2770->regmap, true); + regcache_mark_dirty(tas2770->regmap); + + if (tas2770->sdz_gpio) { + gpiod_set_value_cansleep(tas2770->sdz_gpio, 0); + } else { + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); + if (ret < 0) { + regcache_cache_only(tas2770->regmap, false); + regcache_sync(tas2770->regmap); + return ret; + } + + ret = 0; + } + + return ret; +} + +static int tas2770_codec_resume(struct snd_soc_component *component) +{ + struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + int ret; + + if (tas2770->sdz_gpio) { + gpiod_set_value_cansleep(tas2770->sdz_gpio, 1); + usleep_range(1000, 2000); + } else { + ret = tas2770_update_pwr_ctrl(tas2770); + if (ret < 0) + return ret; + } + + regcache_cache_only(tas2770->regmap, false); + + return regcache_sync(tas2770->regmap); +} +#else +#define tas2770_codec_suspend NULL +#define tas2770_codec_resume NULL +#endif + +static const char * const tas2770_ASI1_src[] = { + "I2C offset", "Left", "Right", "LeftRightDiv2", +}; + +static SOC_ENUM_SINGLE_DECL( + tas2770_ASI1_src_enum, TAS2770_TDM_CFG_REG2, + 4, tas2770_ASI1_src); + +static const struct snd_kcontrol_new tas2770_asi1_mux = + SOC_DAPM_ENUM("ASI1 Source", tas2770_ASI1_src_enum); + +static int tas2770_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + tas2770->dac_powered = 1; + ret = tas2770_update_pwr_ctrl(tas2770); + break; + case SND_SOC_DAPM_PRE_PMD: + tas2770->dac_powered = 0; + ret = tas2770_update_pwr_ctrl(tas2770); + break; + default: + dev_err(tas2770->dev, "Not supported evevt\n"); + return -EINVAL; + } + + return ret; +} + +static const struct snd_kcontrol_new isense_switch = + SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 3, 1, 1); +static const struct snd_kcontrol_new vsense_switch = + SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 2, 1, 1); + +static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2770_asi1_mux), + SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch), + SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON") +}; + +static const struct snd_soc_dapm_route tas2770_audio_map[] = { + {"ASI1 Sel", "I2C offset", "ASI1"}, + {"ASI1 Sel", "Left", "ASI1"}, + {"ASI1 Sel", "Right", "ASI1"}, + {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, + {"DAC", NULL, "ASI1 Sel"}, + {"OUT", NULL, "DAC"}, + {"ISENSE", "Switch", "IMON"}, + {"VSENSE", "Switch", "VMON"}, +}; + +static int tas2770_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + + tas2770->unmuted = !mute; + return tas2770_update_pwr_ctrl(tas2770); +} + +static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) +{ + int ret; + struct snd_soc_component *component = tas2770->component; + + switch (bitwidth) { + case SNDRV_PCM_FORMAT_S16_LE: + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXW_MASK, + TAS2770_TDM_CFG_REG2_RXW_16BITS); + tas2770->v_sense_slot = tas2770->i_sense_slot + 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXW_MASK, + TAS2770_TDM_CFG_REG2_RXW_24BITS); + tas2770->v_sense_slot = tas2770->i_sense_slot + 4; + break; + case SNDRV_PCM_FORMAT_S32_LE: + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXW_MASK, + TAS2770_TDM_CFG_REG2_RXW_32BITS); + tas2770->v_sense_slot = tas2770->i_sense_slot + 4; + break; + + default: + return -EINVAL; + } + + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG5, + TAS2770_TDM_CFG_REG5_VSNS_MASK | + TAS2770_TDM_CFG_REG5_50_MASK, + TAS2770_TDM_CFG_REG5_VSNS_ENABLE | + tas2770->v_sense_slot); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG6, + TAS2770_TDM_CFG_REG6_ISNS_MASK | + TAS2770_TDM_CFG_REG6_50_MASK, + TAS2770_TDM_CFG_REG6_ISNS_ENABLE | + tas2770->i_sense_slot); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate) +{ + struct snd_soc_component *component = tas2770->component; + int ramp_rate_val; + int ret; + + switch (samplerate) { + case 48000: + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_48KHZ | + TAS2770_TDM_CFG_REG0_31_44_1_48KHZ; + break; + case 44100: + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_44_1KHZ | + TAS2770_TDM_CFG_REG0_31_44_1_48KHZ; + break; + case 96000: + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_48KHZ | + TAS2770_TDM_CFG_REG0_31_88_2_96KHZ; + break; + case 88200: + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_44_1KHZ | + TAS2770_TDM_CFG_REG0_31_88_2_96KHZ; + break; + case 192000: + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_48KHZ | + TAS2770_TDM_CFG_REG0_31_176_4_192KHZ; + break; + case 176400: + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_44_1KHZ | + TAS2770_TDM_CFG_REG0_31_176_4_192KHZ; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK | + TAS2770_TDM_CFG_REG0_31_MASK, + ramp_rate_val); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2770_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + int ret; + + ret = tas2770_set_bitwidth(tas2770, params_format(params)); + if (ret) + return ret; + + return tas2770_set_samplerate(tas2770, params_rate(params)); +} + +static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + u8 tdm_rx_start_slot = 0, invert_fpol = 0, fpol_preinv = 0, asi_cfg_1 = 0; + int ret; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + dev_err(tas2770->dev, "ASI invalid DAI clocking\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + invert_fpol = 1; + fallthrough; + case SND_SOC_DAIFMT_NB_NF: + asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_RSING; + break; + case SND_SOC_DAIFMT_IB_IF: + invert_fpol = 1; + fallthrough; + case SND_SOC_DAIFMT_IB_NF: + asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_FALING; + break; + default: + dev_err(tas2770->dev, "ASI format Inverse is not found\n"); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1, + TAS2770_TDM_CFG_REG1_RX_MASK, + asi_cfg_1); + if (ret < 0) + return ret; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + tdm_rx_start_slot = 1; + fpol_preinv = 0; + break; + case SND_SOC_DAIFMT_DSP_A: + tdm_rx_start_slot = 0; + fpol_preinv = 1; + break; + case SND_SOC_DAIFMT_DSP_B: + tdm_rx_start_slot = 1; + fpol_preinv = 1; + break; + case SND_SOC_DAIFMT_LEFT_J: + tdm_rx_start_slot = 0; + fpol_preinv = 1; + break; + default: + dev_err(tas2770->dev, + "DAI Format is not found, fmt=0x%x\n", fmt); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1, + TAS2770_TDM_CFG_REG1_MASK, + (tdm_rx_start_slot << TAS2770_TDM_CFG_REG1_51_SHIFT)); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_FPOL_MASK, + (fpol_preinv ^ invert_fpol) + ? TAS2770_TDM_CFG_REG0_FPOL_RSING + : TAS2770_TDM_CFG_REG0_FPOL_FALING); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + int left_slot, right_slot; + int ret; + + if (tx_mask == 0 || rx_mask != 0) + return -EINVAL; + + left_slot = __ffs(tx_mask); + tx_mask &= ~(1 << left_slot); + if (tx_mask == 0) { + right_slot = left_slot; + } else { + right_slot = __ffs(tx_mask); + tx_mask &= ~(1 << right_slot); + } + + if (tx_mask != 0 || left_slot >= slots || right_slot >= slots) + return -EINVAL; + + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3, + TAS2770_TDM_CFG_REG3_30_MASK, + (left_slot << TAS2770_TDM_CFG_REG3_30_SHIFT)); + if (ret < 0) + return ret; + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3, + TAS2770_TDM_CFG_REG3_RXS_MASK, + (right_slot << TAS2770_TDM_CFG_REG3_RXS_SHIFT)); + if (ret < 0) + return ret; + + switch (slot_width) { + case 16: + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXS_MASK, + TAS2770_TDM_CFG_REG2_RXS_16BITS); + break; + case 24: + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXS_MASK, + TAS2770_TDM_CFG_REG2_RXS_24BITS); + break; + case 32: + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXS_MASK, + TAS2770_TDM_CFG_REG2_RXS_32BITS); + break; + case 0: + /* Do not change slot width */ + ret = 0; + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_soc_dai_ops tas2770_dai_ops = { + .mute_stream = tas2770_mute, + .hw_params = tas2770_hw_params, + .set_fmt = tas2770_set_fmt, + .set_tdm_slot = tas2770_set_dai_tdm_slot, + .no_capture_mute = 1, +}; + +#define TAS2770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define TAS2770_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000\ + ) + +static struct snd_soc_dai_driver tas2770_dai_driver[] = { + { + .name = "tas2770 ASI1", + .id = 0, + .playback = { + .stream_name = "ASI1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAS2770_RATES, + .formats = TAS2770_FORMATS, + }, + .capture = { + .stream_name = "ASI1 Capture", + .channels_min = 0, + .channels_max = 2, + .rates = TAS2770_RATES, + .formats = TAS2770_FORMATS, + }, + .ops = &tas2770_dai_ops, + .symmetric_rate = 1, + }, +}; + +static const struct regmap_config tas2770_i2c_regmap; + +static int tas2770_codec_probe(struct snd_soc_component *component) +{ + struct tas2770_priv *tas2770 = + snd_soc_component_get_drvdata(component); + + tas2770->component = component; + + if (tas2770->sdz_gpio) { + gpiod_set_value_cansleep(tas2770->sdz_gpio, 1); + usleep_range(1000, 2000); + } + + tas2770_reset(tas2770); + regmap_reinit_cache(tas2770->regmap, &tas2770_i2c_regmap); + + return 0; +} + +static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0); +static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0); + +static const struct snd_kcontrol_new tas2770_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Playback Volume", TAS2770_PLAY_CFG_REG2, + 0, TAS2770_PLAY_CFG_REG2_VMAX, 1, tas2770_playback_volume), + SOC_SINGLE_TLV("Amp Gain Volume", TAS2770_PLAY_CFG_REG0, 0, 0x14, 0, + tas2770_digital_tlv), +}; + +static const struct snd_soc_component_driver soc_component_driver_tas2770 = { + .probe = tas2770_codec_probe, + .suspend = tas2770_codec_suspend, + .resume = tas2770_codec_resume, + .controls = tas2770_snd_controls, + .num_controls = ARRAY_SIZE(tas2770_snd_controls), + .dapm_widgets = tas2770_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2770_dapm_widgets), + .dapm_routes = tas2770_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2770_audio_map), + .idle_bias_on = 1, + .endianness = 1, +}; + +static int tas2770_register_codec(struct tas2770_priv *tas2770) +{ + return devm_snd_soc_register_component(tas2770->dev, + &soc_component_driver_tas2770, + tas2770_dai_driver, ARRAY_SIZE(tas2770_dai_driver)); +} + +static const struct reg_default tas2770_reg_defaults[] = { + { TAS2770_PAGE, 0x00 }, + { TAS2770_SW_RST, 0x00 }, + { TAS2770_PWR_CTRL, 0x0e }, + { TAS2770_PLAY_CFG_REG0, 0x10 }, + { TAS2770_PLAY_CFG_REG1, 0x01 }, + { TAS2770_PLAY_CFG_REG2, 0x00 }, + { TAS2770_MSC_CFG_REG0, 0x07 }, + { TAS2770_TDM_CFG_REG1, 0x02 }, + { TAS2770_TDM_CFG_REG2, 0x0a }, + { TAS2770_TDM_CFG_REG3, 0x10 }, + { TAS2770_INT_MASK_REG0, 0xfc }, + { TAS2770_INT_MASK_REG1, 0xb1 }, + { TAS2770_INT_CFG, 0x05 }, + { TAS2770_MISC_IRQ, 0x81 }, + { TAS2770_CLK_CGF, 0x0c }, + +}; + +static bool tas2770_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS2770_PAGE: /* regmap implementation requires this */ + case TAS2770_SW_RST: /* always clears after write */ + case TAS2770_BO_PRV_REG0:/* has a self clearing bit */ + case TAS2770_LVE_INT_REG0: + case TAS2770_LVE_INT_REG1: + case TAS2770_LAT_INT_REG0:/* Sticky interrupt flags */ + case TAS2770_LAT_INT_REG1:/* Sticky interrupt flags */ + case TAS2770_VBAT_MSB: + case TAS2770_VBAT_LSB: + case TAS2770_TEMP_MSB: + case TAS2770_TEMP_LSB: + return true; + } + + return false; +} + +static bool tas2770_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS2770_LVE_INT_REG0: + case TAS2770_LVE_INT_REG1: + case TAS2770_LAT_INT_REG0: + case TAS2770_LAT_INT_REG1: + case TAS2770_VBAT_MSB: + case TAS2770_VBAT_LSB: + case TAS2770_TEMP_MSB: + case TAS2770_TEMP_LSB: + case TAS2770_TDM_CLK_DETC: + case TAS2770_REV_AND_GPID: + return false; + } + + return true; +} + +static const struct regmap_range_cfg tas2770_regmap_ranges[] = { + { + .range_min = 0, + .range_max = 1 * 128, + .selector_reg = TAS2770_PAGE, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config tas2770_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = tas2770_writeable, + .volatile_reg = tas2770_volatile, + .reg_defaults = tas2770_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas2770_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .ranges = tas2770_regmap_ranges, + .num_ranges = ARRAY_SIZE(tas2770_regmap_ranges), + .max_register = 1 * 128, +}; + +static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) +{ + int rc = 0; + + rc = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", + &tas2770->i_sense_slot); + if (rc) { + dev_info(tas2770->dev, "Property %s is missing setting default slot\n", + "ti,imon-slot-no"); + + tas2770->i_sense_slot = 0; + } + + rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", + &tas2770->v_sense_slot); + if (rc) { + dev_info(tas2770->dev, "Property %s is missing setting default slot\n", + "ti,vmon-slot-no"); + + tas2770->v_sense_slot = 2; + } + + tas2770->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); + if (IS_ERR(tas2770->sdz_gpio)) { + if (PTR_ERR(tas2770->sdz_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + tas2770->sdz_gpio = NULL; + } + + return 0; +} + +static int tas2770_i2c_probe(struct i2c_client *client) +{ + struct tas2770_priv *tas2770; + int result; + + tas2770 = devm_kzalloc(&client->dev, sizeof(struct tas2770_priv), + GFP_KERNEL); + if (!tas2770) + return -ENOMEM; + + tas2770->dev = &client->dev; + i2c_set_clientdata(client, tas2770); + dev_set_drvdata(&client->dev, tas2770); + + tas2770->regmap = devm_regmap_init_i2c(client, &tas2770_i2c_regmap); + if (IS_ERR(tas2770->regmap)) { + result = PTR_ERR(tas2770->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + result); + return result; + } + + if (client->dev.of_node) { + result = tas2770_parse_dt(&client->dev, tas2770); + if (result) { + dev_err(tas2770->dev, "%s: Failed to parse devicetree\n", + __func__); + return result; + } + } + + tas2770->reset_gpio = devm_gpiod_get_optional(tas2770->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(tas2770->reset_gpio)) { + if (PTR_ERR(tas2770->reset_gpio) == -EPROBE_DEFER) { + tas2770->reset_gpio = NULL; + return -EPROBE_DEFER; + } + } + + result = tas2770_register_codec(tas2770); + if (result) + dev_err(tas2770->dev, "Register codec failed.\n"); + + return result; +} + +static const struct i2c_device_id tas2770_i2c_id[] = { + { "tas2770", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2770_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id tas2770_of_match[] = { + { .compatible = "ti,tas2770" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tas2770_of_match); +#endif + +static struct i2c_driver tas2770_i2c_driver = { + .driver = { + .name = "tas2770", + .of_match_table = of_match_ptr(tas2770_of_match), + }, + .probe = tas2770_i2c_probe, + .id_table = tas2770_i2c_id, +}; +module_i2c_driver(tas2770_i2c_driver); + +MODULE_AUTHOR("Shi Fu "); +MODULE_DESCRIPTION("TAS2770 I2C Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h new file mode 100644 index 0000000000..f75f40781a --- /dev/null +++ b/sound/soc/codecs/tas2770.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * ALSA SoC TAS2770 codec driver + * + * Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/ + */ +#ifndef __TAS2770__ +#define __TAS2770__ + +/* Book Control Register (available in page0 of each book) */ +#define TAS2770_BOOKCTL_PAGE 0 +#define TAS2770_BOOKCTL_REG 127 +#define TAS2770_REG(page, reg) ((page * 128) + reg) + /* Page */ +#define TAS2770_PAGE TAS2770_REG(0X0, 0x00) +#define TAS2770_PAGE_PAGE_MASK 255 + /* Software Reset */ +#define TAS2770_SW_RST TAS2770_REG(0X0, 0x01) +#define TAS2770_RST BIT(0) + /* Power Control */ +#define TAS2770_PWR_CTRL TAS2770_REG(0X0, 0x02) +#define TAS2770_PWR_CTRL_MASK GENMASK(1, 0) +#define TAS2770_PWR_CTRL_ACTIVE 0x0 +#define TAS2770_PWR_CTRL_MUTE BIT(0) +#define TAS2770_PWR_CTRL_SHUTDOWN 0x2 + /* Playback Configuration Reg0 */ +#define TAS2770_PLAY_CFG_REG0 TAS2770_REG(0X0, 0x03) + /* Playback Configuration Reg1 */ +#define TAS2770_PLAY_CFG_REG1 TAS2770_REG(0X0, 0x04) + /* Playback Configuration Reg2 */ +#define TAS2770_PLAY_CFG_REG2 TAS2770_REG(0X0, 0x05) +#define TAS2770_PLAY_CFG_REG2_VMAX 0xc9 + /* Misc Configuration Reg0 */ +#define TAS2770_MSC_CFG_REG0 TAS2770_REG(0X0, 0x07) + /* TDM Configuration Reg0 */ +#define TAS2770_TDM_CFG_REG0 TAS2770_REG(0X0, 0x0A) +#define TAS2770_TDM_CFG_REG0_SMP_MASK BIT(5) +#define TAS2770_TDM_CFG_REG0_SMP_48KHZ 0x0 +#define TAS2770_TDM_CFG_REG0_SMP_44_1KHZ BIT(5) +#define TAS2770_TDM_CFG_REG0_31_MASK GENMASK(3, 1) +#define TAS2770_TDM_CFG_REG0_31_44_1_48KHZ 0x6 +#define TAS2770_TDM_CFG_REG0_31_88_2_96KHZ 0x8 +#define TAS2770_TDM_CFG_REG0_31_176_4_192KHZ 0xa +#define TAS2770_TDM_CFG_REG0_FPOL_MASK BIT(0) +#define TAS2770_TDM_CFG_REG0_FPOL_RSING 0 +#define TAS2770_TDM_CFG_REG0_FPOL_FALING 1 + /* TDM Configuration Reg1 */ +#define TAS2770_TDM_CFG_REG1 TAS2770_REG(0X0, 0x0B) +#define TAS2770_TDM_CFG_REG1_MASK GENMASK(5, 1) +#define TAS2770_TDM_CFG_REG1_51_SHIFT 1 +#define TAS2770_TDM_CFG_REG1_RX_MASK BIT(0) +#define TAS2770_TDM_CFG_REG1_RX_RSING 0x0 +#define TAS2770_TDM_CFG_REG1_RX_FALING BIT(0) + /* TDM Configuration Reg2 */ +#define TAS2770_TDM_CFG_REG2 TAS2770_REG(0X0, 0x0C) +#define TAS2770_TDM_CFG_REG2_RXW_MASK GENMASK(3, 2) +#define TAS2770_TDM_CFG_REG2_RXW_16BITS 0x0 +#define TAS2770_TDM_CFG_REG2_RXW_24BITS 0x8 +#define TAS2770_TDM_CFG_REG2_RXW_32BITS 0xc +#define TAS2770_TDM_CFG_REG2_RXS_MASK GENMASK(1, 0) +#define TAS2770_TDM_CFG_REG2_RXS_16BITS 0x0 +#define TAS2770_TDM_CFG_REG2_RXS_24BITS BIT(0) +#define TAS2770_TDM_CFG_REG2_RXS_32BITS 0x2 + /* TDM Configuration Reg3 */ +#define TAS2770_TDM_CFG_REG3 TAS2770_REG(0X0, 0x0D) +#define TAS2770_TDM_CFG_REG3_RXS_MASK GENMASK(7, 4) +#define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4 +#define TAS2770_TDM_CFG_REG3_30_MASK GENMASK(3, 0) +#define TAS2770_TDM_CFG_REG3_30_SHIFT 0 + /* TDM Configuration Reg5 */ +#define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F) +#define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6) +#define TAS2770_TDM_CFG_REG5_VSNS_ENABLE BIT(6) +#define TAS2770_TDM_CFG_REG5_50_MASK GENMASK(5, 0) + /* TDM Configuration Reg6 */ +#define TAS2770_TDM_CFG_REG6 TAS2770_REG(0X0, 0x10) +#define TAS2770_TDM_CFG_REG6_ISNS_MASK BIT(6) +#define TAS2770_TDM_CFG_REG6_ISNS_ENABLE BIT(6) +#define TAS2770_TDM_CFG_REG6_50_MASK GENMASK(5, 0) + /* Brown Out Prevention Reg0 */ +#define TAS2770_BO_PRV_REG0 TAS2770_REG(0X0, 0x1B) + /* Interrupt MASK Reg0 */ +#define TAS2770_INT_MASK_REG0 TAS2770_REG(0X0, 0x20) +#define TAS2770_INT_REG0_DEFAULT 0xfc +#define TAS2770_INT_MASK_REG0_DISABLE 0xff + /* Interrupt MASK Reg1 */ +#define TAS2770_INT_MASK_REG1 TAS2770_REG(0X0, 0x21) +#define TAS2770_INT_REG1_DEFAULT 0xb1 +#define TAS2770_INT_MASK_REG1_DISABLE 0xff + /* Live-Interrupt Reg0 */ +#define TAS2770_LVE_INT_REG0 TAS2770_REG(0X0, 0x22) + /* Live-Interrupt Reg1 */ +#define TAS2770_LVE_INT_REG1 TAS2770_REG(0X0, 0x23) + /* Latched-Interrupt Reg0 */ +#define TAS2770_LAT_INT_REG0 TAS2770_REG(0X0, 0x24) +#define TAS2770_LAT_INT_REG0_OCE_FLG BIT(1) +#define TAS2770_LAT_INT_REG0_OTE_FLG BIT(0) + /* Latched-Interrupt Reg1 */ +#define TAS2770_LAT_INT_REG1 TAS2770_REG(0X0, 0x25) +#define TAS2770_LAT_INT_REG1_VBA_TOV BIT(3) +#define TAS2770_LAT_INT_REG1_VBA_TUV BIT(2) +#define TAS2770_LAT_INT_REG1_BOUT_FLG BIT(1) + /* VBAT MSB */ +#define TAS2770_VBAT_MSB TAS2770_REG(0X0, 0x27) + /* VBAT LSB */ +#define TAS2770_VBAT_LSB TAS2770_REG(0X0, 0x28) + /* TEMP MSB */ +#define TAS2770_TEMP_MSB TAS2770_REG(0X0, 0x29) + /* TEMP LSB */ +#define TAS2770_TEMP_LSB TAS2770_REG(0X0, 0x2A) + /* Interrupt Configuration */ +#define TAS2770_INT_CFG TAS2770_REG(0X0, 0x30) + /* Misc IRQ */ +#define TAS2770_MISC_IRQ TAS2770_REG(0X0, 0x32) + /* Clock Configuration */ +#define TAS2770_CLK_CGF TAS2770_REG(0X0, 0x3C) + /* TDM Clock detection monitor */ +#define TAS2770_TDM_CLK_DETC TAS2770_REG(0X0, 0x77) + /* Revision and PG ID */ +#define TAS2770_REV_AND_GPID TAS2770_REG(0X0, 0x7D) + +#define TAS2770_POWER_ACTIVE 0 +#define TAS2770_POWER_MUTE BIT(0) +#define TAS2770_POWER_SHUTDOWN BIT(1) + +#define ERROR_OVER_CURRENT BIT(0) +#define ERROR_DIE_OVERTEMP BIT(1) +#define ERROR_OVER_VOLTAGE BIT(2) +#define ERROR_UNDER_VOLTAGE BIT(3) +#define ERROR_BROWNOUT BIT(4) +#define ERROR_CLASSD_PWR BIT(5) + +struct tas2770_priv { + struct snd_soc_component *component; + struct gpio_desc *reset_gpio; + struct gpio_desc *sdz_gpio; + struct regmap *regmap; + struct device *dev; + int v_sense_slot; + int i_sense_slot; + bool dac_powered; + bool unmuted; +}; + +#endif /* __TAS2770__ */ diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c new file mode 100644 index 0000000000..41076be238 --- /dev/null +++ b/sound/soc/codecs/tas2780.c @@ -0,0 +1,655 @@ +// SPDX-License-Identifier: GPL-2.0 +// Driver for the Texas Instruments TAS2780 Mono +// Audio amplifier +// Copyright (C) 2022 Texas Instruments Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2780.h" + +struct tas2780_priv { + struct snd_soc_component *component; + struct gpio_desc *reset_gpio; + struct regmap *regmap; + struct device *dev; + int v_sense_slot; + int i_sense_slot; +}; + +static void tas2780_reset(struct tas2780_priv *tas2780) +{ + int ret = 0; + + if (tas2780->reset_gpio) { + gpiod_set_value_cansleep(tas2780->reset_gpio, 0); + usleep_range(2000, 2050); + gpiod_set_value_cansleep(tas2780->reset_gpio, 1); + usleep_range(2000, 2050); + } + + ret = snd_soc_component_write(tas2780->component, TAS2780_SW_RST, + TAS2780_RST); + if (ret) + dev_err(tas2780->dev, "%s:errCode:0x%x Reset error!\n", + __func__, ret); +} + +#ifdef CONFIG_PM +static int tas2780_codec_suspend(struct snd_soc_component *component) +{ + struct tas2780_priv *tas2780 = + snd_soc_component_get_drvdata(component); + int ret = 0; + + ret = snd_soc_component_update_bits(component, TAS2780_PWR_CTRL, + TAS2780_PWR_CTRL_MASK, TAS2780_PWR_CTRL_SHUTDOWN); + if (ret < 0) { + dev_err(tas2780->dev, "%s:errCode:0x%0x:power down error\n", + __func__, ret); + goto err; + } + ret = 0; + regcache_cache_only(tas2780->regmap, true); + regcache_mark_dirty(tas2780->regmap); +err: + return ret; +} + +static int tas2780_codec_resume(struct snd_soc_component *component) +{ + struct tas2780_priv *tas2780 = + snd_soc_component_get_drvdata(component); + int ret = 0; + + ret = snd_soc_component_update_bits(component, TAS2780_PWR_CTRL, + TAS2780_PWR_CTRL_MASK, TAS2780_PWR_CTRL_ACTIVE); + + if (ret < 0) { + dev_err(tas2780->dev, "%s:errCode:0x%0x:power down error\n", + __func__, ret); + goto err; + } + ret = 0; + regcache_cache_only(tas2780->regmap, false); + ret = regcache_sync(tas2780->regmap); +err: + return ret; +} +#endif + +static const char * const tas2780_ASI1_src[] = { + "I2C offset", "Left", "Right", "LeftRightDiv2", +}; + +static SOC_ENUM_SINGLE_DECL( + tas2780_ASI1_src_enum, TAS2780_TDM_CFG2, 4, tas2780_ASI1_src); + +static const struct snd_kcontrol_new tas2780_asi1_mux = + SOC_DAPM_ENUM("ASI1 Source", tas2780_ASI1_src_enum); + +static const struct snd_kcontrol_new isense_switch = + SOC_DAPM_SINGLE("Switch", TAS2780_PWR_CTRL, + TAS2780_ISENSE_POWER_EN, 1, 1); +static const struct snd_kcontrol_new vsense_switch = + SOC_DAPM_SINGLE("Switch", TAS2780_PWR_CTRL, + TAS2780_VSENSE_POWER_EN, 1, 1); + +static const struct snd_soc_dapm_widget tas2780_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2780_asi1_mux), + SND_SOC_DAPM_SWITCH("ISENSE", TAS2780_PWR_CTRL, + TAS2780_ISENSE_POWER_EN, 1, &isense_switch), + SND_SOC_DAPM_SWITCH("VSENSE", TAS2780_PWR_CTRL, + TAS2780_VSENSE_POWER_EN, 1, &vsense_switch), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON") +}; + +static const struct snd_soc_dapm_route tas2780_audio_map[] = { + {"ASI1 Sel", "I2C offset", "ASI1"}, + {"ASI1 Sel", "Left", "ASI1"}, + {"ASI1 Sel", "Right", "ASI1"}, + {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, + {"OUT", NULL, "ASI1 Sel"}, + {"ISENSE", "Switch", "IMON"}, + {"VSENSE", "Switch", "VMON"}, +}; + +static int tas2780_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct tas2780_priv *tas2780 = + snd_soc_component_get_drvdata(component); + int ret = 0; + + ret = snd_soc_component_update_bits(component, TAS2780_PWR_CTRL, + TAS2780_PWR_CTRL_MASK, + mute ? TAS2780_PWR_CTRL_MUTE : 0); + if (ret < 0) { + dev_err(tas2780->dev, "%s: Failed to set powercontrol\n", + __func__); + goto err; + } + ret = 0; +err: + return ret; +} + +static int tas2780_set_bitwidth(struct tas2780_priv *tas2780, int bitwidth) +{ + struct snd_soc_component *component = tas2780->component; + int sense_en; + int val; + int ret; + int slot_size; + + switch (bitwidth) { + case SNDRV_PCM_FORMAT_S16_LE: + ret = snd_soc_component_update_bits(component, + TAS2780_TDM_CFG2, + TAS2780_TDM_CFG2_RXW_MASK, + TAS2780_TDM_CFG2_RXW_16BITS); + slot_size = TAS2780_TDM_CFG2_RXS_16BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ret = snd_soc_component_update_bits(component, + TAS2780_TDM_CFG2, + TAS2780_TDM_CFG2_RXW_MASK, + TAS2780_TDM_CFG2_RXW_24BITS); + slot_size = TAS2780_TDM_CFG2_RXS_24BITS; + break; + case SNDRV_PCM_FORMAT_S32_LE: + ret = snd_soc_component_update_bits(component, + TAS2780_TDM_CFG2, + TAS2780_TDM_CFG2_RXW_MASK, + TAS2780_TDM_CFG2_RXW_32BITS); + slot_size = TAS2780_TDM_CFG2_RXS_32BITS; + break; + + default: + ret = -EINVAL; + } + + if (ret < 0) { + dev_err(tas2780->dev, "%s:errCode:0x%x set bitwidth error\n", + __func__, ret); + goto err; + } + + ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG2, + TAS2780_TDM_CFG2_RXS_MASK, slot_size); + if (ret < 0) { + dev_err(tas2780->dev, + "%s:errCode:0x%x set RX slot size error\n", + __func__, ret); + goto err; + } + + val = snd_soc_component_read(tas2780->component, TAS2780_PWR_CTRL); + if (val < 0) { + dev_err(tas2780->dev, "%s:errCode:0x%x read PWR_CTRL error\n", + __func__, val); + ret = val; + goto err; + } + + if (val & (1 << TAS2780_VSENSE_POWER_EN)) + sense_en = 0; + else + sense_en = TAS2780_TDM_CFG5_VSNS_ENABLE; + + ret = snd_soc_component_update_bits(tas2780->component, + TAS2780_TDM_CFG5, TAS2780_TDM_CFG5_VSNS_ENABLE, sense_en); + if (ret < 0) { + dev_err(tas2780->dev, "%s:errCode:0x%x enable vSNS error\n", + __func__, ret); + goto err; + } + + if (val & (1 << TAS2780_ISENSE_POWER_EN)) + sense_en = 0; + else + sense_en = TAS2780_TDM_CFG6_ISNS_ENABLE; + + ret = snd_soc_component_update_bits(tas2780->component, + TAS2780_TDM_CFG6, TAS2780_TDM_CFG6_ISNS_ENABLE, sense_en); + if (ret < 0) { + dev_err(tas2780->dev, "%s:errCode:0x%x enable iSNS error\n", + __func__, ret); + goto err; + } + ret = 0; +err: + return ret; +} + +static int tas2780_set_samplerate( + struct tas2780_priv *tas2780, int samplerate) +{ + struct snd_soc_component *component = tas2780->component; + int ramp_rate_val; + int ret; + + switch (samplerate) { + case 48000: + ramp_rate_val = TAS2780_TDM_CFG0_SMP_48KHZ | + TAS2780_TDM_CFG0_44_1_48KHZ; + break; + case 44100: + ramp_rate_val = TAS2780_TDM_CFG0_SMP_44_1KHZ | + TAS2780_TDM_CFG0_44_1_48KHZ; + break; + case 96000: + ramp_rate_val = TAS2780_TDM_CFG0_SMP_48KHZ | + TAS2780_TDM_CFG0_88_2_96KHZ; + break; + case 88200: + ramp_rate_val = TAS2780_TDM_CFG0_SMP_44_1KHZ | + TAS2780_TDM_CFG0_88_2_96KHZ; + break; + default: + return -EINVAL; + } + ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG0, + TAS2780_TDM_CFG0_SMP_MASK | TAS2780_TDM_CFG0_MASK, + ramp_rate_val); + if (ret < 0) { + dev_err(tas2780->dev, + "%s:errCode:0x%x Failed to set ramp_rate_val\n", + __func__, ret); + goto err; + } + ret = 0; +err: + return ret; +} + +static int tas2780_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas2780_priv *tas2780 = + snd_soc_component_get_drvdata(component); + int ret; + + ret = tas2780_set_bitwidth(tas2780, params_format(params)); + if (ret < 0) + return ret; + + return tas2780_set_samplerate(tas2780, params_rate(params)); +} + +static int tas2780_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tas2780_priv *tas2780 = + snd_soc_component_get_drvdata(component); + u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0; + int iface; + int ret = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + asi_cfg_1 = TAS2780_TDM_CFG1_RX_RISING; + break; + case SND_SOC_DAIFMT_IB_NF: + asi_cfg_1 = TAS2780_TDM_CFG1_RX_FALLING; + break; + default: + dev_err(tas2780->dev, "ASI format Inverse is not found\n"); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG1, + TAS2780_TDM_CFG1_RX_MASK, asi_cfg_1); + if (ret < 0) { + dev_err(tas2780->dev, + "%s:errCode:0x%x Failed to set asi_cfg_1\n", + __func__, ret); + goto err; + } + + if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) + || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) + == SND_SOC_DAIFMT_DSP_A)){ + iface = TAS2780_TDM_CFG2_SCFG_I2S; + tdm_rx_start_slot = 1; + } else { + if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) + == SND_SOC_DAIFMT_DSP_B) + || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) + == SND_SOC_DAIFMT_LEFT_J)) { + iface = TAS2780_TDM_CFG2_SCFG_LEFT_J; + tdm_rx_start_slot = 0; + } else { + dev_err(tas2780->dev, + "%s:DAI Format is not found, fmt=0x%x\n", + __func__, fmt); + ret = -EINVAL; + goto err; + } + } + ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG1, + TAS2780_TDM_CFG1_MASK, + (tdm_rx_start_slot << TAS2780_TDM_CFG1_51_SHIFT)); + if (ret < 0) { + dev_err(tas2780->dev, + "%s:errCode:0x%x Failed to set tdm_rx_start_slot\n", + __func__, ret); + goto err; + } + + ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG2, + TAS2780_TDM_CFG2_SCFG_MASK, iface); + if (ret < 0) { + dev_err(tas2780->dev, "%s:errCode:0x%x Failed to set iface\n", + __func__, ret); + goto err; + } + ret = 0; +err: + return ret; +} + +static int tas2780_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct tas2780_priv *tas2780 = + snd_soc_component_get_drvdata(component); + int left_slot, right_slot; + int slots_cfg; + int slot_size; + int ret = 0; + + if (tx_mask == 0 || rx_mask != 0) + return -EINVAL; + + left_slot = __ffs(tx_mask); + tx_mask &= ~(1 << left_slot); + if (tx_mask == 0) { + right_slot = left_slot; + } else { + right_slot = __ffs(tx_mask); + tx_mask &= ~(1 << right_slot); + } + + if (tx_mask != 0 || left_slot >= slots || right_slot >= slots) + return -EINVAL; + + slots_cfg = (right_slot << TAS2780_TDM_CFG3_RXS_SHIFT) | left_slot; + ret = snd_soc_component_write(component, TAS2780_TDM_CFG3, slots_cfg); + if (ret) { + dev_err(tas2780->dev, + "%s:errCode:0x%x Failed to set slots_cfg\n", + __func__, ret); + goto err; + } + + switch (slot_width) { + case 16: + slot_size = TAS2780_TDM_CFG2_RXS_16BITS; + break; + case 24: + slot_size = TAS2780_TDM_CFG2_RXS_24BITS; + break; + case 32: + slot_size = TAS2780_TDM_CFG2_RXS_32BITS; + break; + default: + ret = -EINVAL; + goto err; + } + + ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG2, + TAS2780_TDM_CFG2_RXS_MASK, slot_size); + if (ret < 0) { + dev_err(tas2780->dev, + "%s:errCode:0x%x Failed to set slot_size\n", + __func__, ret); + goto err; + } + + ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG5, + TAS2780_TDM_CFG5_50_MASK, tas2780->v_sense_slot); + if (ret < 0) { + dev_err(tas2780->dev, + "%s:errCode:0x%x Failed to set v_sense_slot\n", + __func__, ret); + goto err; + } + + ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG6, + TAS2780_TDM_CFG6_50_MASK, tas2780->i_sense_slot); + if (ret < 0) { + dev_err(tas2780->dev, + "%s:errCode:0x%x Failed to set i_sense_slot\n", + __func__, ret); + goto err; + } + ret = 0; +err: + return ret; +} + +static const struct snd_soc_dai_ops tas2780_dai_ops = { + .mute_stream = tas2780_mute, + .hw_params = tas2780_hw_params, + .set_fmt = tas2780_set_fmt, + .set_tdm_slot = tas2780_set_dai_tdm_slot, + .no_capture_mute = 1, +}; + +#define TAS2780_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define TAS2780_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200) + +static struct snd_soc_dai_driver tas2780_dai_driver[] = { + { + .name = "tas2780 ASI1", + .id = 0, + .playback = { + .stream_name = "ASI1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = TAS2780_RATES, + .formats = TAS2780_FORMATS, + }, + .capture = { + .stream_name = "ASI1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TAS2780_RATES, + .formats = TAS2780_FORMATS, + }, + .ops = &tas2780_dai_ops, + .symmetric_rate = 1, + }, +}; + +static int tas2780_codec_probe(struct snd_soc_component *component) +{ + struct tas2780_priv *tas2780 = + snd_soc_component_get_drvdata(component); + int ret = 0; + + tas2780->component = component; + + tas2780_reset(tas2780); + ret = snd_soc_component_update_bits(component, + TAS2780_IC_CFG, TAS2780_IC_CFG_MASK, + TAS2780_IC_CFG_ENABLE); + if (ret < 0) + dev_err(tas2780->dev, "%s:errCode:0x%0x\n", + __func__, ret); + + return ret; +} + +static DECLARE_TLV_DB_SCALE(tas2780_digital_tlv, 1100, 50, 0); +static DECLARE_TLV_DB_SCALE(tas2780_playback_volume, -10000, 50, 0); + +static const struct snd_kcontrol_new tas2780_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", TAS2780_DVC, 0, + TAS2780_DVC_MAX, 1, tas2780_playback_volume), + SOC_SINGLE_TLV("Amp Gain Volume", TAS2780_CHNL_0, 0, 0x14, 0, + tas2780_digital_tlv), +}; + +static const struct snd_soc_component_driver soc_component_driver_tas2780 = { + .probe = tas2780_codec_probe, +#ifdef CONFIG_PM + .suspend = tas2780_codec_suspend, + .resume = tas2780_codec_resume, +#endif + .controls = tas2780_snd_controls, + .num_controls = ARRAY_SIZE(tas2780_snd_controls), + .dapm_widgets = tas2780_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2780_dapm_widgets), + .dapm_routes = tas2780_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2780_audio_map), + .idle_bias_on = 1, + .endianness = 1, +}; + +static const struct reg_default tas2780_reg_defaults[] = { + { TAS2780_PAGE, 0x00 }, + { TAS2780_SW_RST, 0x00 }, + { TAS2780_PWR_CTRL, 0x1a }, + { TAS2780_DVC, 0x00 }, + { TAS2780_CHNL_0, 0x00 }, + { TAS2780_TDM_CFG0, 0x09 }, + { TAS2780_TDM_CFG1, 0x02 }, + { TAS2780_TDM_CFG2, 0x0a }, + { TAS2780_TDM_CFG3, 0x10 }, + { TAS2780_TDM_CFG5, 0x42 }, +}; + +static const struct regmap_range_cfg tas2780_regmap_ranges[] = { + { + .range_min = 0, + .range_max = 1 * 128, + .selector_reg = TAS2780_PAGE, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config tas2780_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .reg_defaults = tas2780_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas2780_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .ranges = tas2780_regmap_ranges, + .num_ranges = ARRAY_SIZE(tas2780_regmap_ranges), + .max_register = 1 * 128, +}; + +static int tas2780_parse_dt(struct device *dev, struct tas2780_priv *tas2780) +{ + int ret = 0; + + tas2780->reset_gpio = devm_gpiod_get_optional(tas2780->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(tas2780->reset_gpio)) { + if (PTR_ERR(tas2780->reset_gpio) == -EPROBE_DEFER) { + tas2780->reset_gpio = NULL; + return -EPROBE_DEFER; + } + } + + ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", + &tas2780->i_sense_slot); + if (ret) + tas2780->i_sense_slot = 0; + + ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", + &tas2780->v_sense_slot); + if (ret) + tas2780->v_sense_slot = 2; + + return 0; +} + +static int tas2780_i2c_probe(struct i2c_client *client) +{ + struct tas2780_priv *tas2780; + int result; + + tas2780 = devm_kzalloc(&client->dev, sizeof(struct tas2780_priv), + GFP_KERNEL); + if (!tas2780) + return -ENOMEM; + tas2780->dev = &client->dev; + i2c_set_clientdata(client, tas2780); + dev_set_drvdata(&client->dev, tas2780); + + tas2780->regmap = devm_regmap_init_i2c(client, &tas2780_i2c_regmap); + if (IS_ERR(tas2780->regmap)) { + result = PTR_ERR(tas2780->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + result); + return result; + } + + if (client->dev.of_node) { + result = tas2780_parse_dt(&client->dev, tas2780); + if (result) { + dev_err(tas2780->dev, + "%s: Failed to parse devicetree\n", __func__); + return result; + } + } + + return devm_snd_soc_register_component(tas2780->dev, + &soc_component_driver_tas2780, tas2780_dai_driver, + ARRAY_SIZE(tas2780_dai_driver)); +} + +static const struct i2c_device_id tas2780_i2c_id[] = { + { "tas2780", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2780_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id tas2780_of_match[] = { + { .compatible = "ti,tas2780" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tas2780_of_match); +#endif + +static struct i2c_driver tas2780_i2c_driver = { + .driver = { + .name = "tas2780", + .of_match_table = of_match_ptr(tas2780_of_match), + }, + .probe = tas2780_i2c_probe, + .id_table = tas2780_i2c_id, +}; +module_i2c_driver(tas2780_i2c_driver); + +MODULE_AUTHOR("Raphael Xu "); +MODULE_DESCRIPTION("TAS2780 I2C Smart Amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2780.h b/sound/soc/codecs/tas2780.h new file mode 100644 index 0000000000..661c25df4e --- /dev/null +++ b/sound/soc/codecs/tas2780.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * TAS2780.h - ALSA SoC Texas Instruments TAS2780 Mono Audio Amplifier + * + * Copyright (C) 2020-2022 Texas Instruments Incorporated - https://www.ti.com + * + * Author: Raphael Xu + */ + +#ifndef __TAS2780_H__ +#define __TAS2780_H__ + +/* Book Control Register */ +#define TAS2780_BOOKCTL_PAGE 0 +#define TAS2780_BOOKCTL_REG 127 +#define TAS2780_REG(page, reg) ((page * 128) + reg) + +/* Page */ +#define TAS2780_PAGE TAS2780_REG(0X0, 0x00) +#define TAS2780_PAGE_PAGE_MASK 255 + +/* Software Reset */ +#define TAS2780_SW_RST TAS2780_REG(0X0, 0x01) +#define TAS2780_RST BIT(0) + +/* Power Control */ +#define TAS2780_PWR_CTRL TAS2780_REG(0X0, 0x02) +#define TAS2780_PWR_CTRL_MASK GENMASK(1, 0) +#define TAS2780_PWR_CTRL_ACTIVE 0x0 +#define TAS2780_PWR_CTRL_MUTE BIT(0) +#define TAS2780_PWR_CTRL_SHUTDOWN BIT(1) + +#define TAS2780_VSENSE_POWER_EN 3 +#define TAS2780_ISENSE_POWER_EN 4 + +/* Digital Volume Control */ +#define TAS2780_DVC TAS2780_REG(0X0, 0x1a) +#define TAS2780_DVC_MAX 0xc9 + +#define TAS2780_CHNL_0 TAS2780_REG(0X0, 0x03) + +/* TDM Configuration Reg0 */ +#define TAS2780_TDM_CFG0 TAS2780_REG(0X0, 0x08) +#define TAS2780_TDM_CFG0_SMP_MASK BIT(5) +#define TAS2780_TDM_CFG0_SMP_48KHZ 0x0 +#define TAS2780_TDM_CFG0_SMP_44_1KHZ BIT(5) +#define TAS2780_TDM_CFG0_MASK GENMASK(3, 1) +#define TAS2780_TDM_CFG0_44_1_48KHZ BIT(3) +#define TAS2780_TDM_CFG0_88_2_96KHZ (BIT(3) | BIT(1)) + +/* TDM Configuration Reg1 */ +#define TAS2780_TDM_CFG1 TAS2780_REG(0X0, 0x09) +#define TAS2780_TDM_CFG1_MASK GENMASK(5, 1) +#define TAS2780_TDM_CFG1_51_SHIFT 1 +#define TAS2780_TDM_CFG1_RX_MASK BIT(0) +#define TAS2780_TDM_CFG1_RX_RISING 0x0 +#define TAS2780_TDM_CFG1_RX_FALLING BIT(0) + +/* TDM Configuration Reg2 */ +#define TAS2780_TDM_CFG2 TAS2780_REG(0X0, 0x0a) +#define TAS2780_TDM_CFG2_RXW_MASK GENMASK(3, 2) +#define TAS2780_TDM_CFG2_RXW_16BITS 0x0 +#define TAS2780_TDM_CFG2_RXW_24BITS BIT(3) +#define TAS2780_TDM_CFG2_RXW_32BITS (BIT(3) | BIT(2)) +#define TAS2780_TDM_CFG2_RXS_MASK GENMASK(1, 0) +#define TAS2780_TDM_CFG2_RXS_16BITS 0x0 +#define TAS2780_TDM_CFG2_RXS_24BITS BIT(0) +#define TAS2780_TDM_CFG2_RXS_32BITS BIT(1) +#define TAS2780_TDM_CFG2_SCFG_MASK GENMASK(5, 4) +#define TAS2780_TDM_CFG2_SCFG_I2S 0x0 +#define TAS2780_TDM_CFG2_SCFG_LEFT_J BIT(4) +#define TAS2780_TDM_CFG2_SCFG_RIGHT_J BIT(5) + +/* TDM Configuration Reg3 */ +#define TAS2780_TDM_CFG3 TAS2780_REG(0X0, 0x0c) +#define TAS2780_TDM_CFG3_RXS_MASK GENMASK(7, 4) +#define TAS2780_TDM_CFG3_RXS_SHIFT 0x4 +#define TAS2780_TDM_CFG3_MASK GENMASK(3, 0) + +/* TDM Configuration Reg4 */ +#define TAS2780_TDM_CFG4 TAS2780_REG(0X0, 0x0d) +#define TAS2780_TDM_CFG4_TX_OFFSET_MASK GENMASK(3, 1) + +/* TDM Configuration Reg5 */ +#define TAS2780_TDM_CFG5 TAS2780_REG(0X0, 0x0e) +#define TAS2780_TDM_CFG5_VSNS_MASK BIT(6) +#define TAS2780_TDM_CFG5_VSNS_ENABLE BIT(6) +#define TAS2780_TDM_CFG5_50_MASK GENMASK(5, 0) + +/* TDM Configuration Reg6 */ +#define TAS2780_TDM_CFG6 TAS2780_REG(0X0, 0x0f) +#define TAS2780_TDM_CFG6_ISNS_MASK BIT(6) +#define TAS2780_TDM_CFG6_ISNS_ENABLE BIT(6) +#define TAS2780_TDM_CFG6_50_MASK GENMASK(5, 0) + +/* IC CFG */ +#define TAS2780_IC_CFG TAS2780_REG(0X0, 0x5c) +#define TAS2780_IC_CFG_MASK GENMASK(7, 6) +#define TAS2780_IC_CFG_ENABLE (BIT(7) | BIT(6)) + +#endif /* __TAS2780_H__ */ diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c new file mode 100644 index 0000000000..00e35169ae --- /dev/null +++ b/sound/soc/codecs/tas2781-comlib.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers +// +// Copyright 2023 Texas Instruments, Inc. +// +// Author: Shenghao Ding + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TASDEVICE_CRC8_POLYNOMIAL 0x4d + +static const struct regmap_range_cfg tasdevice_ranges[] = { + { + .range_min = 0, + .range_max = 256 * 128, + .selector_reg = TASDEVICE_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config tasdevice_regmap = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .ranges = tasdevice_ranges, + .num_ranges = ARRAY_SIZE(tasdevice_ranges), + .max_register = 256 * 128, +}; + +static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv, + unsigned short chn, int book) +{ + struct i2c_client *client = (struct i2c_client *)tas_priv->client; + int ret = 0; + + if (chn < tas_priv->ndev) { + struct tasdevice *tasdev = &tas_priv->tasdevice[chn]; + struct regmap *map = tas_priv->regmap; + + if (client->addr != tasdev->dev_addr) { + client->addr = tasdev->dev_addr; + /* All tas2781s share the same regmap, clear the page + * inside regmap once switching to another tas2781. + * Register 0 at any pages and any books inside tas2781 + * is the same one for page-switching. + */ + ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + } + + if (tasdev->cur_book != book) { + ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + tasdev->cur_book = book; + } + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} + +int tasdevice_dev_read(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned int *val) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_read(map, TASDEVICE_PGRG(reg), val); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_read); + +int tasdevice_dev_write(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned int value) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_write(map, TASDEVICE_PGRG(reg), + value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_write); + +int tasdevice_dev_bulk_write( + struct tasdevice_priv *tas_priv, unsigned short chn, + unsigned int reg, unsigned char *data, + unsigned int len) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg), + data, len); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write); + +int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned char *data, + unsigned int len) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read); + +int tasdevice_dev_update_bits( + struct tasdevice_priv *tas_priv, unsigned short chn, + unsigned int reg, unsigned int mask, unsigned int value) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_update_bits(map, TASDEVICE_PGRG(reg), + mask, value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + ret = -EINVAL; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits); + +struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c) +{ + struct tasdevice_priv *tas_priv; + + tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL); + if (!tas_priv) + return NULL; + tas_priv->dev = &i2c->dev; + tas_priv->client = (void *)i2c; + + return tas_priv; +} +EXPORT_SYMBOL_GPL(tasdevice_kzalloc); + +void tas2781_reset(struct tasdevice_priv *tas_dev) +{ + int ret, i; + + if (tas_dev->reset) { + gpiod_set_value_cansleep(tas_dev->reset, 0); + usleep_range(500, 1000); + gpiod_set_value_cansleep(tas_dev->reset, 1); + } else { + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_write(tas_dev, i, + TAS2781_REG_SWRESET, + TAS2781_REG_SWRESET_RESET); + if (ret < 0) + dev_err(tas_dev->dev, + "dev %d swreset fail, %d\n", + i, ret); + } + } + usleep_range(1000, 1050); +} +EXPORT_SYMBOL_GPL(tas2781_reset); + +int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, + void (*cont)(const struct firmware *fw, void *context)) +{ + int ret = 0; + + /* Codec Lock Hold to ensure that codec_probe and firmware parsing and + * loading do not simultaneously execute. + */ + mutex_lock(&tas_priv->codec_lock); + + scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin", + tas_priv->dev_name, tas_priv->ndev); + crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); + tas_priv->codec = codec; + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv, + cont); + if (ret) + dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", + ret); + + /* Codec Lock Release*/ + mutex_unlock(&tas_priv->codec_lock); + return ret; +} +EXPORT_SYMBOL_GPL(tascodec_init); + +int tasdevice_init(struct tasdevice_priv *tas_priv) +{ + int ret = 0; + int i; + + tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client, + &tasdevice_regmap); + if (IS_ERR(tas_priv->regmap)) { + ret = PTR_ERR(tas_priv->regmap); + dev_err(tas_priv->dev, "Failed to allocate register map: %d\n", + ret); + goto out; + } + + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + + for (i = 0; i < tas_priv->ndev; i++) { + tas_priv->tasdevice[i].cur_book = -1; + tas_priv->tasdevice[i].cur_prog = -1; + tas_priv->tasdevice[i].cur_conf = -1; + } + + mutex_init(&tas_priv->codec_lock); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_init); + +static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (!prog) + return; + + tas_dt = &(prog->dev_data); + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &(tas_dt->dev_blks[i]); + kfree(blk->data); + } + kfree(tas_dt->dev_blks); +} + +static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_prog_blk_remove(&prog[i]); + kfree(prog); +} + +static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (cfg) { + tas_dt = &(cfg->dev_data); + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &(tas_dt->dev_blks[i]); + kfree(blk->data); + } + kfree(tas_dt->dev_blks); + } +} + +static void tasdev_dsp_cfg_remove(struct tasdevice_config *config, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_cfg_blk_remove(&config[i]); + kfree(config); +} + +void tasdevice_dsp_remove(void *context) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_dev->fmw; + + if (!tas_dev->fmw) + return; + + if (tas_fmw->programs) + tasdev_dsp_prog_remove(tas_fmw->programs, + tas_fmw->nr_programs); + if (tas_fmw->configs) + tasdev_dsp_cfg_remove(tas_fmw->configs, + tas_fmw->nr_configurations); + kfree(tas_fmw); + tas_dev->fmw = NULL; +} +EXPORT_SYMBOL_GPL(tasdevice_dsp_remove); + +void tasdevice_remove(struct tasdevice_priv *tas_priv) +{ + if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) + gpio_free(tas_priv->irq_info.irq_gpio); + kfree(tas_priv->acpi_subsystem_id); + mutex_destroy(&tas_priv->codec_lock); +} +EXPORT_SYMBOL_GPL(tasdevice_remove); + +static int tasdevice_clamp(int val, int max, unsigned int invert) +{ + if (val > max) + val = max; + if (invert) + val = max - val; + if (val < 0) + val = 0; + return val; +} + +int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask; + int max = mc->max; + int err_cnt = 0; + int val, i, ret; + + mask = (1 << fls(max)) - 1; + mask <<= mc->shift; + val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); + for (i = 0; i < tas_priv->ndev; i++) { + ret = tasdevice_dev_update_bits(tas_priv, i, + mc->reg, mask, (unsigned int)(val << mc->shift)); + if (!ret) + continue; + err_cnt++; + dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i); + } + + /* All the devices set error, return 0 */ + return (err_cnt == tas_priv->ndev) ? 0 : 1; +} +EXPORT_SYMBOL_GPL(tasdevice_amp_putvol); + +int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask = 0; + int max = mc->max; + int ret = 0; + int val; + + /* Read the primary device */ + ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__); + goto out; + } + + mask = (1 << fls(max)) - 1; + mask <<= mc->shift; + val = (val & mask) >> mc->shift; + val = tasdevice_clamp(val, max, invert); + ucontrol->value.integer.value[0] = val; + +out: + return ret; + +} +EXPORT_SYMBOL_GPL(tasdevice_amp_getvol); + +int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int err_cnt = 0; + int ret; + int val, i; + + val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); + + for (i = 0; i < tas_priv->ndev; i++) { + ret = tasdevice_dev_write(tas_priv, i, mc->reg, + (unsigned int)val); + if (!ret) + continue; + err_cnt++; + dev_err(tas_priv->dev, + "set digital vol err in dev %d\n", i); + } + + /* All the devices set error, return 0 */ + return (err_cnt == tas_priv->ndev) ? 0 : 1; + +} +EXPORT_SYMBOL_GPL(tasdevice_digital_putvol); + +int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int ret, val; + + /* Read the primary device as the whole */ + ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get digital vol error\n", + __func__); + goto out; + } + + val = tasdevice_clamp(val, max, invert); + ucontrol->value.integer.value[0] = val; + +out: + return ret; + +} +EXPORT_SYMBOL_GPL(tasdevice_digital_getvol); + +MODULE_DESCRIPTION("TAS2781 common library"); +MODULE_AUTHOR("Shenghao Ding, TI, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c new file mode 100644 index 0000000000..61b05629a9 --- /dev/null +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -0,0 +1,2431 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// tasdevice-fmw.c -- TASDEVICE firmware support +// +// Copyright 2023 Texas Instruments, Inc. +// +// Author: Shenghao Ding + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define ERROR_PRAM_CRCCHK 0x0000000 +#define ERROR_YRAM_CRCCHK 0x0000001 +#define PPC_DRIVER_CRCCHK 0x00000200 + +#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c) +#define TAS2781_YRAM_BOOK1 140 +#define TAS2781_YRAM1_PAGE 42 +#define TAS2781_YRAM1_START_REG 88 + +#define TAS2781_YRAM2_START_PAGE 43 +#define TAS2781_YRAM2_END_PAGE 49 +#define TAS2781_YRAM2_START_REG 8 +#define TAS2781_YRAM2_END_REG 127 + +#define TAS2781_YRAM3_PAGE 50 +#define TAS2781_YRAM3_START_REG 8 +#define TAS2781_YRAM3_END_REG 27 + +/*should not include B0_P53_R44-R47 */ +#define TAS2781_YRAM_BOOK2 0 +#define TAS2781_YRAM4_START_PAGE 50 +#define TAS2781_YRAM4_END_PAGE 60 + +#define TAS2781_YRAM5_PAGE 61 +#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG +#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG + +#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10 +#define MAIN_ALL_DEVICES_1X 0x01 +#define MAIN_DEVICE_A_1X 0x02 +#define MAIN_DEVICE_B_1X 0x03 +#define MAIN_DEVICE_C_1X 0x04 +#define MAIN_DEVICE_D_1X 0x05 +#define COEFF_DEVICE_A_1X 0x12 +#define COEFF_DEVICE_B_1X 0x13 +#define COEFF_DEVICE_C_1X 0x14 +#define COEFF_DEVICE_D_1X 0x15 +#define PRE_DEVICE_A_1X 0x22 +#define PRE_DEVICE_B_1X 0x23 +#define PRE_DEVICE_C_1X 0x24 +#define PRE_DEVICE_D_1X 0x25 +#define PRE_SOFTWARE_RESET_DEVICE_A 0x41 +#define PRE_SOFTWARE_RESET_DEVICE_B 0x42 +#define PRE_SOFTWARE_RESET_DEVICE_C 0x43 +#define PRE_SOFTWARE_RESET_DEVICE_D 0x44 +#define POST_SOFTWARE_RESET_DEVICE_A 0x45 +#define POST_SOFTWARE_RESET_DEVICE_B 0x46 +#define POST_SOFTWARE_RESET_DEVICE_C 0x47 +#define POST_SOFTWARE_RESET_DEVICE_D 0x48 + +struct tas_crc { + unsigned char offset; + unsigned char len; +}; + +static const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = { + 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4 +}; + +static struct tasdevice_config_info *tasdevice_add_config( + struct tasdevice_priv *tas_priv, unsigned char *config_data, + unsigned int config_size, int *status) +{ + struct tasdevice_config_info *cfg_info; + struct tasdev_blk_data **bk_da; + unsigned int config_offset = 0; + unsigned int i; + + /* In most projects are many audio cases, such as music, handfree, + * receiver, games, audio-to-haptics, PMIC record, bypass mode, + * portrait, landscape, etc. Even in multiple audios, one or + * two of the chips will work for the special case, such as + * ultrasonic application. In order to support these variable-numbers + * of audio cases, flexible configs have been introduced in the + * dsp firmware. + */ + cfg_info = kzalloc(sizeof(struct tasdevice_config_info), GFP_KERNEL); + if (!cfg_info) { + *status = -ENOMEM; + goto out; + } + + if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) { + if (config_offset + 64 > (int)config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add conf: Out of boundary\n"); + goto out; + } + config_offset += 64; + } + + if (config_offset + 4 > (int)config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add config: Out of boundary\n"); + goto out; + } + + /* convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + cfg_info->nblocks = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + /* Several kinds of dsp/algorithm firmwares can run on tas2781, + * the number and size of blk are not fixed and different among + * these firmwares. + */ + bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks, + sizeof(struct tasdev_blk_data *), GFP_KERNEL); + if (!bk_da) { + *status = -ENOMEM; + goto out; + } + cfg_info->real_nblocks = 0; + for (i = 0; i < cfg_info->nblocks; i++) { + if (config_offset + 12 > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d nblocks = %u!\n", + __func__, i, cfg_info->nblocks); + break; + } + bk_da[i] = kzalloc(sizeof(struct tasdev_blk_data), GFP_KERNEL); + if (!bk_da[i]) { + *status = -ENOMEM; + break; + } + + bk_da[i]->dev_idx = config_data[config_offset]; + config_offset++; + + bk_da[i]->block_type = config_data[config_offset]; + config_offset++; + + if (bk_da[i]->block_type == TASDEVICE_BIN_BLK_PRE_POWER_UP) { + if (bk_da[i]->dev_idx == 0) + cfg_info->active_dev = + (1 << tas_priv->ndev) - 1; + else + cfg_info->active_dev |= 1 << + (bk_da[i]->dev_idx - 1); + + } + bk_da[i]->yram_checksum = + be16_to_cpup((__be16 *)&config_data[config_offset]); + config_offset += 2; + bk_da[i]->block_size = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + bk_da[i]->n_subblks = + be32_to_cpup((__be32 *)&config_data[config_offset]); + + config_offset += 4; + + if (config_offset + bk_da[i]->block_size > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d blks = %u!\n", + __func__, i, cfg_info->nblocks); + break; + } + /* instead of kzalloc+memcpy */ + bk_da[i]->regdata = kmemdup(&config_data[config_offset], + bk_da[i]->block_size, GFP_KERNEL); + if (!bk_da[i]->regdata) { + *status = -ENOMEM; + goto out; + } + + config_offset += bk_da[i]->block_size; + cfg_info->real_nblocks += 1; + } + +out: + return cfg_info; +} + +int tasdevice_rca_parser(void *context, const struct firmware *fmw) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_config_info **cfg_info; + struct tasdevice_rca_hdr *fw_hdr; + struct tasdevice_rca *rca; + unsigned int total_config_sz = 0; + unsigned char *buf; + int offset = 0; + int ret = 0; + int i; + + rca = &(tas_priv->rcabin); + fw_hdr = &(rca->fw_hdr); + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "Failed to read %s\n", + tas_priv->rca_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + buf = (unsigned char *)fmw->data; + + fw_hdr->img_sz = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + if (fw_hdr->img_sz != fmw->size) { + dev_err(tas_priv->dev, + "File size not match, %d %u", (int)fmw->size, + fw_hdr->img_sz); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + + fw_hdr->checksum = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + fw_hdr->binary_version_num = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->binary_version_num < 0x103) { + dev_err(tas_priv->dev, "File version 0x%04x is too low", + fw_hdr->binary_version_num); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + offset += 4; + fw_hdr->drv_fw_version = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_hdr->plat_type = buf[offset]; + offset += 1; + fw_hdr->dev_family = buf[offset]; + offset += 1; + fw_hdr->reserve = buf[offset]; + offset += 1; + fw_hdr->ndev = buf[offset]; + offset += 1; + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "ndev(%u) in rcabin mismatch ndev(%u) in DTS\n", + fw_hdr->ndev, tas_priv->ndev); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) { + dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n"); + ret = -EINVAL; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++) + fw_hdr->devs[i] = buf[offset]; + + fw_hdr->nconfig = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) { + fw_hdr->config_size[i] = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + total_config_sz += fw_hdr->config_size[i]; + } + + if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) { + dev_err(tas_priv->dev, "Bin file error!\n"); + ret = -EINVAL; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL); + if (!cfg_info) { + ret = -ENOMEM; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + rca->cfg_info = cfg_info; + rca->ncfgs = 0; + for (i = 0; i < (int)fw_hdr->nconfig; i++) { + rca->ncfgs += 1; + cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset], + fw_hdr->config_size[i], &ret); + if (ret) { + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + offset += (int)fw_hdr->config_size[i]; + } +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_rca_parser, SND_SOC_TAS2781_FMWLIB); + +static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + + if (offset + 16 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + + /* convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + block->type = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + + block->blk_size = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + block->nr_subblocks = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + if (offset + block->blk_size > fmw->size) { + dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__); + offset = -EINVAL; + goto out; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL); + if (!block->data) { + offset = -ENOMEM; + goto out; + } + offset += block->blk_size; + +out: + return offset; +} + +static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = fmw->data; + struct tasdev_blk *blk; + unsigned int i; + + if (offset + 4 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + img_data->nr_blk = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < img_data->nr_blk; i++) { + blk = &(img_data->dev_blks[i]); + offset = fw_parse_block_data_kernel(tas_fmw, blk, fmw, offset); + if (offset < 0) { + offset = -EINVAL; + break; + } + } + +out: + return offset; +} + +static int fw_parse_program_data_kernel( + struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + struct tasdevice_prog *program; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &(tas_fmw->programs[i]); + if (offset + 72 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + /*skip 72 unused byts*/ + offset += 72; + + offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static int fw_parse_configuration_data_kernel( + struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + struct tasdevice_config *config; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &(tas_fmw->configs[i]); + if (offset + 80 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + memcpy(config->name, &data[offset], 64); + /*skip extra 16 bytes*/ + offset += 80; + + offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static int fw_parse_variable_header_kernel( + struct tasdevice_priv *tas_priv, const struct firmware *fmw, + int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + struct tasdevice_prog *program; + struct tasdevice_config *config; + const unsigned char *buf = fmw->data; + unsigned short max_confs; + unsigned int i; + + if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + fw_hdr->device_family = be16_to_cpup((__be16 *)&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s:not TAS device\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 2; + fw_hdr->device = be16_to_cpup((__be16 *)&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + offset = -EINVAL; + goto out; + } + offset += 2; + fw_hdr->ndev = deviceNumber[fw_hdr->device]; + + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n", + __func__, fw_hdr->ndev, tas_priv->ndev); + offset = -EINVAL; + goto out; + } + + tas_fmw->nr_programs = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + if (tas_fmw->nr_programs == 0 || tas_fmw->nr_programs > + TASDEVICE_MAXPROGRAM_NUM_KERNEL) { + dev_err(tas_priv->dev, "mnPrograms is invalid\n"); + offset = -EINVAL; + goto out; + } + + tas_fmw->programs = kcalloc(tas_fmw->nr_programs, + sizeof(struct tasdevice_prog), GFP_KERNEL); + if (!tas_fmw->programs) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &(tas_fmw->programs[i]); + program->prog_size = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + } + + /* Skip the unused prog_size */ + offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs); + + tas_fmw->nr_configurations = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + /* The max number of config in firmware greater than 4 pieces of + * tas2781s is different from the one lower than 4 pieces of + * tas2781s. + */ + max_confs = (fw_hdr->ndev >= 4) ? + TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS : + TASDEVICE_MAXCONFIG_NUM_KERNEL; + if (tas_fmw->nr_configurations == 0 || + tas_fmw->nr_configurations > max_confs) { + dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__); + offset = -EINVAL; + goto out; + } + + if (offset + 4 * max_confs > fmw->size) { + dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__); + offset = -EINVAL; + goto out; + } + + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(struct tasdevice_config), GFP_KERNEL); + if (!tas_fmw->configs) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + config = &(tas_fmw->configs[i]); + config->cfg_size = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + } + + /* Skip the unused configs */ + offset += 4 * (max_confs - tas_fmw->nr_programs); + +out: + return offset; +} + +static int tasdevice_process_block(void *context, unsigned char *data, + unsigned char dev_idx, int sublocksize) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + int subblk_offset, chn, chnend, rc; + unsigned char subblk_typ = data[1]; + int blktyp = dev_idx & 0xC0; + int idx = dev_idx & 0x3F; + bool is_err = false; + + if (idx) { + chn = idx - 1; + chnend = idx; + } else { + chn = 0; + chnend = tas_priv->ndev; + } + + for (; chn < chnend; chn++) { + if (tas_priv->tasdevice[chn].is_loading == false) + continue; + + is_err = false; + subblk_offset = 2; + switch (subblk_typ) { + case TASDEVICE_CMD_SING_W: { + int i; + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 * len > sublocksize) { + dev_err(tas_priv->dev, + "process_block: Out of boundary\n"); + is_err = true; + break; + } + + for (i = 0; i < len; i++) { + rc = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + data[subblk_offset + 3]); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "process_block: single write error\n"); + } + subblk_offset += 4; + } + } + break; + case TASDEVICE_CMD_BURST: { + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 + len > sublocksize) { + dev_err(tas_priv->dev, + "%s: BST Out of boundary\n", + __func__); + is_err = true; + break; + } + if (len % 4) { + dev_err(tas_priv->dev, + "%s:Bst-len(%u)not div by 4\n", + __func__, len); + break; + } + + rc = tasdevice_dev_bulk_write(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + &(data[subblk_offset + 4]), len); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "%s: bulk_write error = %d\n", + __func__, rc); + } + subblk_offset += (len + 4); + } + break; + case TASDEVICE_CMD_DELAY: { + unsigned int sleep_time = 0; + + if (subblk_offset + 2 > sublocksize) { + dev_err(tas_priv->dev, + "%s: delay Out of boundary\n", + __func__); + is_err = true; + break; + } + sleep_time = be16_to_cpup((__be16 *)&data[2]) * 1000; + usleep_range(sleep_time, sleep_time + 50); + subblk_offset += 2; + } + break; + case TASDEVICE_CMD_FIELD_W: + if (subblk_offset + 6 > sublocksize) { + dev_err(tas_priv->dev, + "%s: bit write Out of boundary\n", + __func__); + is_err = true; + break; + } + rc = tasdevice_dev_update_bits(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset + 2], + data[subblk_offset + 3], + data[subblk_offset + 4]), + data[subblk_offset + 1], + data[subblk_offset + 5]); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "%s: update_bits error = %d\n", + __func__, rc); + } + subblk_offset += 6; + break; + default: + break; + } + if (is_err == true && blktyp != 0) { + if (blktyp == 0x80) { + tas_priv->tasdevice[chn].cur_prog = -1; + tas_priv->tasdevice[chn].cur_conf = -1; + } else + tas_priv->tasdevice[chn].cur_conf = -1; + } + } + + return subblk_offset; +} + +void tasdevice_select_cfg_blk(void *pContext, int conf_no, + unsigned char block_type) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) pContext; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdev_blk_data **blk_data; + int j, k, chn, chnend; + + if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) { + dev_err(tas_priv->dev, "conf_no should be not more than %u\n", + rca->ncfgs); + return; + } + blk_data = cfg_info[conf_no]->blk_data; + + for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) { + unsigned int length = 0, rc = 0; + + if (block_type > 5 || block_type < 2) { + dev_err(tas_priv->dev, + "block_type should be in range from 2 to 5\n"); + break; + } + if (block_type != blk_data[j]->block_type) + continue; + + for (k = 0; k < (int)blk_data[j]->n_subblks; k++) { + if (blk_data[j]->dev_idx) { + chn = blk_data[j]->dev_idx - 1; + chnend = blk_data[j]->dev_idx; + } else { + chn = 0; + chnend = tas_priv->ndev; + } + for (; chn < chnend; chn++) + tas_priv->tasdevice[chn].is_loading = true; + + rc = tasdevice_process_block(tas_priv, + blk_data[j]->regdata + length, + blk_data[j]->dev_idx, + blk_data[j]->block_size - length); + length += rc; + if (blk_data[j]->block_size < length) { + dev_err(tas_priv->dev, + "%s: %u %u out of boundary\n", + __func__, length, + blk_data[j]->block_size); + break; + } + } + if (length != blk_data[j]->block_size) + dev_err(tas_priv->dev, "%s: %u %u size is not same\n", + __func__, length, blk_data[j]->block_size); + } +} +EXPORT_SYMBOL_NS_GPL(tasdevice_select_cfg_blk, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_load_block_kernel( + struct tasdevice_priv *tasdevice, struct tasdev_blk *block) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tasdevice->fmw->fw_hdr); + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr); + const unsigned int blk_size = block->blk_size; + unsigned int i, length; + unsigned char *data = block->data; + unsigned char dev_idx = 0; + + if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781) { + switch (block->type) { + case MAIN_ALL_DEVICES_1X: + dev_idx = 0x80; + break; + case MAIN_DEVICE_A_1X: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A_1X: + case PRE_DEVICE_A_1X: + case PRE_SOFTWARE_RESET_DEVICE_A: + case POST_SOFTWARE_RESET_DEVICE_A: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B_1X: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B_1X: + case PRE_DEVICE_B_1X: + case PRE_SOFTWARE_RESET_DEVICE_B: + case POST_SOFTWARE_RESET_DEVICE_B: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C_1X: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C_1X: + case PRE_DEVICE_C_1X: + case PRE_SOFTWARE_RESET_DEVICE_C: + case POST_SOFTWARE_RESET_DEVICE_C: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D_1X: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D_1X: + case PRE_DEVICE_D_1X: + case PRE_SOFTWARE_RESET_DEVICE_D: + case POST_SOFTWARE_RESET_DEVICE_D: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } else if (fw_fixed_hdr->ppcver >= + PPC3_VERSION) { + switch (block->type) { + case MAIN_ALL_DEVICES_1X: + dev_idx = 0x80; + break; + case MAIN_DEVICE_A_1X: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A_1X: + case PRE_DEVICE_A_1X: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B_1X: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B_1X: + case PRE_DEVICE_B_1X: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C_1X: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C_1X: + case PRE_DEVICE_C_1X: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D_1X: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D_1X: + case PRE_DEVICE_D_1X: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } else { + switch (block->type) { + case MAIN_ALL_DEVICES: + dev_idx = 0|0x80; + break; + case MAIN_DEVICE_A: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } + + for (i = 0, length = 0; i < block->nr_subblocks; i++) { + int rc = tasdevice_process_block(tasdevice, data + length, + dev_idx, blk_size - length); + if (rc < 0) { + dev_err(tasdevice->dev, + "%s: %u %u sublock write error\n", + __func__, length, blk_size); + break; + } + length += (unsigned int)rc; + if (blk_size < length) { + dev_err(tasdevice->dev, "%s: %u %u out of boundary\n", + __func__, length, blk_size); + break; + } + } + + return 0; +} + +static int fw_parse_variable_hdr(struct tasdevice_priv + *tas_priv, struct tasdevice_dspfw_hdr *fw_hdr, + const struct firmware *fmw, int offset) +{ + const unsigned char *buf = fmw->data; + int len = strlen((char *)&buf[offset]); + + len++; + + if (offset + len + 8 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + + offset += len; + + fw_hdr->device_family = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s: not TAS device\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + fw_hdr->device = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_hdr->ndev = deviceNumber[fw_hdr->device]; + +out: + return offset; +} + +static int fw_parse_variable_header_git(struct tasdevice_priv + *tas_priv, const struct firmware *fmw, int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + + offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset); + if (offset < 0) + goto out; + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n", + __func__, fw_hdr->ndev, tas_priv->ndev); + offset = -EINVAL; + } + +out: + return offset; +} + +static int fw_parse_block_data(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + int n; + + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Type error\n", __func__); + offset = -EINVAL; + goto out; + } + block->type = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) { + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "PChkSumPresent error\n"); + offset = -EINVAL; + goto out; + } + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + } else { + block->is_pchksum_present = 0; + block->is_ychksum_present = 0; + } + + block->nr_cmds = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + n = block->nr_cmds * 4; + if (offset + n > fmw->size) { + dev_err(tas_fmw->dev, + "%s: File Size(%lu) error offset = %d n = %d\n", + __func__, (unsigned long)fmw->size, offset, n); + offset = -EINVAL; + goto out; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], n, GFP_KERNEL); + if (!block->data) { + offset = -ENOMEM; + goto out; + } + offset += n; + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_data(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = (unsigned char *)fmw->data; + struct tasdev_blk *blk; + unsigned int i; + int n; + + if (offset + 64 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Name error\n", __func__); + offset = -EINVAL; + goto out; + } + memcpy(img_data->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n++; + if (offset + n + 2 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Description error\n", __func__); + offset = -EINVAL; + goto out; + } + offset += n; + img_data->nr_blk = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < img_data->nr_blk; i++) { + blk = &(img_data->dev_blks[i]); + offset = fw_parse_block_data(tas_fmw, blk, fmw, offset); + if (offset < 0) { + offset = -EINVAL; + goto out; + } + } + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_program_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + unsigned char *buf = (unsigned char *)fmw->data; + struct tasdevice_prog *program; + int i; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_programs = be16_to_cpup((__be16 *)&buf[offset]); + offset += 2; + + if (tas_fmw->nr_programs == 0) { + /*Not error in calibration Data file, return directly*/ + dev_info(tas_priv->dev, "%s: No Programs data, maybe calbin\n", + __func__); + goto out; + } + + tas_fmw->programs = + kcalloc(tas_fmw->nr_programs, sizeof(struct tasdevice_prog), + GFP_KERNEL); + if (!tas_fmw->programs) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_programs; i++) { + int n = 0; + + program = &(tas_fmw->programs[i]); + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 64; + + n = strlen((char *)&buf[offset]); + /* skip '\0' and 5 unused bytes */ + n += 6; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw, + offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_configuration_data( + struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + struct tasdevice_config *config; + unsigned int i; + int n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_configurations = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + if (tas_fmw->nr_configurations == 0) { + dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__); + /*Not error for calibration Data file, return directly*/ + goto out; + } + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(struct tasdevice_config), GFP_KERNEL); + if (!tas_fmw->configs) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &(tas_fmw->configs[i]); + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "File Size err\n"); + offset = -EINVAL; + goto out; + } + memcpy(config->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n += 15; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &(config->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static bool check_inpage_yram_rg(struct tas_crc *cd, + unsigned char reg, unsigned char len) +{ + bool in = false; + + + if (reg <= TAS2781_YRAM5_END_REG && + reg >= TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_END_REG) + cd->len = TAS2781_YRAM5_END_REG - reg + 1; + else + cd->len = len; + cd->offset = reg; + in = true; + } else if (reg < TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_START_REG) { + cd->offset = TAS2781_YRAM5_START_REG; + cd->len = len - TAS2781_YRAM5_START_REG + reg; + in = true; + } + } + + return in; +} + +static bool check_inpage_yram_bk1(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (page == TAS2781_YRAM1_PAGE) { + if (reg >= TAS2781_YRAM1_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg + len > TAS2781_YRAM1_START_REG) { + cd->offset = TAS2781_YRAM1_START_REG; + cd->len = len - TAS2781_YRAM1_START_REG + reg; + in = true; + } + } else if (page == TAS2781_YRAM3_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + + return in; +} + +/* Return Code: + * true -- the registers are in the inpage yram + * false -- the registers are NOT in the inpage yram + */ +static bool check_inpage_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1) { + in = check_inpage_yram_bk1(cd, page, reg, len); + goto end; + } + if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + +end: + return in; +} + +static bool check_inblock_yram_bk(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if ((page >= TAS2781_YRAM4_START_PAGE && + page <= TAS2781_YRAM4_END_PAGE) || + (page >= TAS2781_YRAM2_START_PAGE && + page <= TAS2781_YRAM2_END_PAGE)) { + if (reg <= TAS2781_YRAM2_END_REG && + reg >= TAS2781_YRAM2_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg < TAS2781_YRAM2_START_REG) { + if (reg + len - 1 >= TAS2781_YRAM2_START_REG) { + cd->offset = TAS2781_YRAM2_START_REG; + cd->len = reg + len - TAS2781_YRAM2_START_REG; + in = true; + } + } + } + + return in; +} + +/* Return Code: + * true -- the registers are in the inblock yram + * false -- the registers are NOT in the inblock yram + */ +static bool check_inblock_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2) + in = check_inblock_yram_bk(cd, page, reg, len); + + return in; +} + +static bool check_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in; + + in = check_inpage_yram(cd, book, page, reg, len); + if (in) + goto end; + in = check_inblock_yram(cd, book, page, reg, len); + +end: + return in; +} + +static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice, + unsigned short chn, unsigned char book, unsigned char page, + unsigned char reg, unsigned int len) +{ + struct tas_crc crc_data; + unsigned char crc_chksum = 0; + unsigned char nBuf1[128]; + int ret = 0; + int i; + bool in; + + if ((reg + len - 1) > 127) { + ret = -EINVAL; + dev_err(tasdevice->dev, "firmware error\n"); + goto end; + } + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (len == 4)) { + /*DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, len); + if (!in) + goto end; + + if (len == 1) { + dev_err(tasdevice->dev, "firmware error\n"); + ret = -EINVAL; + goto end; + } + + ret = tasdevice_dev_bulk_read(tasdevice, chn, + TASDEVICE_REG(book, page, crc_data.offset), + nBuf1, crc_data.len); + if (ret < 0) + goto end; + + for (i = 0; i < crc_data.len; i++) { + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID( + TAS2781_SA_COEFF_SWAP_REG)) + && ((i + crc_data.offset) + >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && ((i + crc_data.offset) + <= (TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG) + + 4))) + /*DSP swap command, bypass */ + continue; + else + crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i], + 1, 0); + } + + ret = crc_chksum; + +end: + return ret; +} + +static int do_singlereg_checksum(struct tasdevice_priv *tasdevice, + unsigned short chl, unsigned char book, unsigned char page, + unsigned char reg, unsigned char val) +{ + struct tas_crc crc_data; + unsigned int nData1; + int ret = 0; + bool in; + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (reg <= (TASDEVICE_PAGE_REG( + TAS2781_SA_COEFF_SWAP_REG) + 4))) { + /*DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, 1); + if (!in) + goto end; + ret = tasdevice_dev_read(tasdevice, chl, + TASDEVICE_REG(book, page, reg), &nData1); + if (ret < 0) + goto end; + + if (nData1 != val) { + dev_err(tasdevice->dev, + "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n", + book, page, reg, val, nData1); + tasdevice->tasdevice[chl].err_code |= ERROR_YRAM_CRCCHK; + ret = -EAGAIN; + goto end; + } + + ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0); + +end: + return ret; +} + +static void set_err_prg_cfg(unsigned int type, struct tasdevice *dev) +{ + if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A) + || (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C) + || (type == MAIN_DEVICE_D)) + dev->cur_prog = -1; + else + dev->cur_conf = -1; +} + +static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn, unsigned char book, + unsigned char page, unsigned char reg, unsigned int len, + unsigned char val, unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) + ret = tasdev_multibytes_chksum(tas_priv, chn, book, page, reg, + len); + else + ret = do_singlereg_checksum(tas_priv, chn, book, page, reg, + val); + + if (ret > 0) { + *crc_chksum += (unsigned char)ret; + goto end; + } + + if (ret != -EAGAIN) + goto end; + + block->nr_retry--; + if (block->nr_retry > 0) + goto end; + + set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]); + +end: + return ret; +} + +static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn, unsigned char book, + unsigned char page, unsigned char reg, unsigned char *data, + unsigned int len, unsigned int *nr_cmds, + unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) { + ret = tasdevice_dev_bulk_write(tas_priv, chn, + TASDEVICE_REG(book, page, reg), data + 3, len); + if (ret < 0) + goto end; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, chn, + book, page, reg, len, 0, crc_chksum); + } else { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(book, page, reg), data[3]); + if (ret < 0) + goto end; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, chn, book, + page, reg, 1, data[3], crc_chksum); + } + + if (!block->is_ychksum_present || ret >= 0) { + *nr_cmds += 1; + if (len >= 2) + *nr_cmds += ((len - 2) / 4) + 1; + } + +end: + return ret; +} + +static int tasdev_block_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn) +{ + unsigned int nr_value; + int ret; + + ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_I2CChecksum, + &nr_value); + if (ret < 0) { + dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn); + set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]); + goto end; + } + + if ((nr_value & 0xff) != block->pchksum) { + dev_err(tas_priv->dev, "%s: Blk PChkSum Chn %d ", __func__, + chn); + dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n", + block->pchksum, (nr_value & 0xff)); + tas_priv->tasdevice[chn].err_code |= ERROR_PRAM_CRCCHK; + ret = -EAGAIN; + block->nr_retry--; + + if (block->nr_retry <= 0) + set_err_prg_cfg(block->type, + &tas_priv->tasdevice[chn]); + } else + tas_priv->tasdevice[chn].err_code &= ~ERROR_PRAM_CRCCHK; + +end: + return ret; +} + +static int tasdev_load_blk(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn) +{ + unsigned int sleep_time; + unsigned int len; + unsigned int nr_cmds; + unsigned char *data = block->data; + unsigned char crc_chksum = 0; + unsigned char offset; + unsigned char book; + unsigned char page; + unsigned char val; + int ret = 0; + + while (block->nr_retry > 0) { + if (block->is_pchksum_present) { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_I2CChecksum, 0); + if (ret < 0) + break; + } + + if (block->is_ychksum_present) + crc_chksum = 0; + + nr_cmds = 0; + + while (nr_cmds < block->nr_cmds) { + data = block->data + nr_cmds * 4; + + book = data[0]; + page = data[1]; + offset = data[2]; + val = data[3]; + + nr_cmds++; + /*Single byte write*/ + if (offset <= 0x7F) { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(book, page, offset), + val); + if (ret < 0) + goto end; + if (block->is_ychksum_present) { + ret = tasdev_bytes_chksum(tas_priv, + block, chn, book, page, offset, + 1, val, &crc_chksum); + if (ret < 0) + break; + } + continue; + } + /*sleep command*/ + if (offset == 0x81) { + /*book -- data[0] page -- data[1]*/ + sleep_time = ((book << 8) + page)*1000; + usleep_range(sleep_time, sleep_time + 50); + continue; + } + /*Multiple bytes write*/ + if (offset == 0x85) { + data += 4; + len = (book << 8) + page; + book = data[0]; + page = data[1]; + offset = data[2]; + ret = tasdev_multibytes_wr(tas_priv, + block, chn, book, page, offset, data, + len, &nr_cmds, &crc_chksum); + if (ret < 0) + break; + } + } + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) /*err in current device, skip it*/ + break; + + if (block->is_pchksum_present) { + ret = tasdev_block_chksum(tas_priv, block, chn); + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) /*err in current device, skip it*/ + break; + } + + if (block->is_ychksum_present) { + /* TBD, open it when FW ready */ + dev_err(tas_priv->dev, + "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n", + block->ychksum, crc_chksum); + + tas_priv->tasdevice[chn].err_code &= + ~ERROR_YRAM_CRCCHK; + ret = 0; + } + /*skip current blk*/ + break; + } + +end: + return ret; +} + +static int tasdevice_load_block(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + int chnend = 0; + int ret = 0; + int chn = 0; + int rc = 0; + + switch (block->type) { + case MAIN_ALL_DEVICES: + chn = 0; + chnend = tas_priv->ndev; + break; + case MAIN_DEVICE_A: + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + chn = 0; + chnend = 1; + break; + case MAIN_DEVICE_B: + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + chn = 1; + chnend = 2; + break; + case MAIN_DEVICE_C: + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + chn = 2; + chnend = 3; + break; + case MAIN_DEVICE_D: + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + chn = 3; + chnend = 4; + break; + default: + dev_dbg(tas_priv->dev, "load blk: Other Type = 0x%02x\n", + block->type); + break; + } + + for (; chn < chnend; chn++) { + block->nr_retry = 6; + if (tas_priv->tasdevice[chn].is_loading == false) + continue; + ret = tasdev_load_blk(tas_priv, block, chn); + if (ret < 0) + dev_err(tas_priv->dev, "dev %d, Blk (%d) load error\n", + chn, block->type); + rc |= ret; + } + + return rc; +} + +static int dspfw_default_callback(struct tasdevice_priv *tas_priv, + unsigned int drv_ver, unsigned int ppcver) +{ + int rc = 0; + + if (drv_ver == 0x100) { + if (ppcver >= PPC3_VERSION) { + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + } else { + switch (ppcver) { + case 0x00: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + break; + default: + dev_err(tas_priv->dev, + "%s: PPCVer must be 0x0 or 0x%02x", + __func__, PPC3_VERSION); + dev_err(tas_priv->dev, " Current:0x%02x\n", + ppcver); + rc = -EINVAL; + break; + } + } + } else { + dev_err(tas_priv->dev, + "DrvVer must be 0x0, 0x230 or above 0x230 "); + dev_err(tas_priv->dev, "current is 0x%02x\n", drv_ver); + rc = -EINVAL; + } + + return rc; +} + +static int load_calib_data(struct tasdevice_priv *tas_priv, + struct tasdevice_data *dev_data) +{ + struct tasdev_blk *block; + unsigned int i; + int ret = 0; + + for (i = 0; i < dev_data->nr_blk; i++) { + block = &(dev_data->dev_blks[i]); + ret = tasdevice_load_block(tas_priv, block); + if (ret < 0) + break; + } + + return ret; +} + +static int fw_parse_header(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr); + const unsigned char magic_number[] = { 0x35, 0x35, 0x35, 0x32 }; + const unsigned char *buf = (unsigned char *)fmw->data; + + if (offset + 92 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + if (memcmp(&buf[offset], magic_number, 4)) { + dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + /* Convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + fw_fixed_hdr->fwsize = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + if (fw_fixed_hdr->fwsize != fmw->size) { + dev_err(tas_priv->dev, "File size not match, %lu %u", + (unsigned long)fmw->size, fw_fixed_hdr->fwsize); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_fixed_hdr->ppcver = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_fixed_hdr->drv_ver = be32_to_cpup((__be32 *)&buf[offset]); + offset += 72; + + out: + return offset; +} + +static int fw_parse_variable_hdr_cal(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + + offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset); + if (offset < 0) + goto out; + if (fw_hdr->ndev != 1) { + dev_err(tas_priv->dev, + "%s: calbin must be 1, but currently ndev(%u)\n", + __func__, fw_hdr->ndev); + offset = -EINVAL; + } + +out: + return offset; +} + +/* When calibrated data parsing error occurs, DSP can still work with default + * calibrated data, memory resource related to calibrated data will be + * released in the tasdevice_codec_remove. + */ +static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_calibration *calibration; + unsigned char *data = (unsigned char *)fmw->data; + unsigned int i, n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_calibrations = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + if (tas_fmw->nr_calibrations != 1) { + dev_err(tas_priv->dev, + "%s: only supports one calibration (%d)!\n", + __func__, tas_fmw->nr_calibrations); + goto out; + } + + tas_fmw->calibrations = kcalloc(tas_fmw->nr_calibrations, + sizeof(struct tasdevice_calibration), GFP_KERNEL); + if (!tas_fmw->calibrations) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "Calibrations error\n"); + offset = -EINVAL; + goto out; + } + calibration = &(tas_fmw->calibrations[i]); + offset += 64; + + n = strlen((char *)&data[offset]); + /* skip '\0' and 2 unused bytes */ + n += 3; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + offset += n; + + offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw, + offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +int tas2781_load_calibration(void *context, char *file_name, + unsigned short i) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); + const struct firmware *fw_entry; + struct tasdevice_fw *tas_fmw; + struct firmware fmw; + int offset = 0; + int ret; + + ret = request_firmware(&fw_entry, file_name, tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: Request firmware %s failed\n", + __func__, file_name); + goto out; + } + + if (!fw_entry->size) { + dev_err(tas_priv->dev, "%s: file read error: size = %lu\n", + __func__, (unsigned long)fw_entry->size); + ret = -EINVAL; + goto out; + } + fmw.size = fw_entry->size; + fmw.data = fw_entry->data; + + tas_fmw = tasdev->cali_data_fmw = kzalloc(sizeof(struct tasdevice_fw), + GFP_KERNEL); + if (!tasdev->cali_data_fmw) { + ret = -ENOMEM; + goto out; + } + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, + "%s: fw_parse_variable_header_cal EXIT!\n", __func__); + ret = offset; + goto out; + } + offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); + ret = offset; + goto out; + } + +out: + if (fw_entry) + release_firmware(fw_entry); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_dspfw_ready(const struct firmware *fmw, + void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr; + struct tasdevice_fw *tas_fmw; + int offset = 0; + int ret = 0; + + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n", + __func__, tas_priv->coef_binaryname); + ret = -EINVAL; + goto out; + } + + tas_priv->fmw = kzalloc(sizeof(struct tasdevice_fw), GFP_KERNEL); + if (!tas_priv->fmw) { + ret = -ENOMEM; + goto out; + } + tas_fmw = tas_priv->fmw; + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset); + + if (offset == -EINVAL) { + ret = -EINVAL; + goto out; + } + fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr); + /* Support different versions of firmware */ + switch (fw_fixed_hdr->drv_ver) { + case 0x301: + case 0x302: + case 0x502: + case 0x503: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + break; + case 0x202: + case 0x400: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + break; + default: + ret = dspfw_default_callback(tas_priv, + fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver); + if (ret) + goto out; + break; + } + + offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset); + if (offset < 0) { + ret = offset; + goto out; + } + offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw, + offset); + if (offset < 0) { + ret = offset; + goto out; + } + offset = tas_priv->fw_parse_configuration_data(tas_priv, + tas_fmw, fmw, offset); + if (offset < 0) + ret = offset; + +out: + return ret; +} + +int tasdevice_dsp_parser(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + const struct firmware *fw_entry; + int ret; + + ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, + tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: load %s error\n", __func__, + tas_priv->coef_binaryname); + goto out; + } + + ret = tasdevice_dspfw_ready(fw_entry, tas_priv); + release_firmware(fw_entry); + fw_entry = NULL; + +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, SND_SOC_TAS2781_FMWLIB); + +static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw) +{ + struct tasdevice_calibration *calibration; + struct tasdev_blk *block; + struct tasdevice_data *im; + unsigned int blks; + int i; + + if (!tas_fmw->calibrations) + goto out; + + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + calibration = &(tas_fmw->calibrations[i]); + if (!calibration) + continue; + + im = &(calibration->dev_data); + + if (!im->dev_blks) + continue; + + for (blks = 0; blks < im->nr_blk; blks++) { + block = &(im->dev_blks[blks]); + if (!block) + continue; + kfree(block->data); + } + kfree(im->dev_blks); + } + kfree(tas_fmw->calibrations); +out: + kfree(tas_fmw); +} + +void tasdevice_calbin_remove(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice *tasdev; + int i; + + if (!tas_priv) + return; + + for (i = 0; i < tas_priv->ndev; i++) { + tasdev = &(tas_priv->tasdevice[i]); + if (!tasdev->cali_data_fmw) + continue; + tas2781_clear_calfirmware(tasdev->cali_data_fmw); + tasdev->cali_data_fmw = NULL; + } +} +EXPORT_SYMBOL_NS_GPL(tasdevice_calbin_remove, SND_SOC_TAS2781_FMWLIB); + +void tasdevice_config_info_remove(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **ci = rca->cfg_info; + int i, j; + + if (!ci) + return; + for (i = 0; i < rca->ncfgs; i++) { + if (!ci[i]) + continue; + if (ci[i]->blk_data) { + for (j = 0; j < (int)ci[i]->real_nblocks; j++) { + if (!ci[i]->blk_data[j]) + continue; + kfree(ci[i]->blk_data[j]->regdata); + kfree(ci[i]->blk_data[j]); + } + kfree(ci[i]->blk_data); + } + kfree(ci[i]); + } + kfree(ci); +} +EXPORT_SYMBOL_NS_GPL(tasdevice_config_info_remove, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_load_data(struct tasdevice_priv *tas_priv, + struct tasdevice_data *dev_data) +{ + struct tasdev_blk *block; + unsigned int i; + int ret = 0; + + for (i = 0; i < dev_data->nr_blk; i++) { + block = &(dev_data->dev_blks[i]); + ret = tas_priv->tasdevice_load_block(tas_priv, block); + if (ret < 0) + break; + } + + return ret; +} + +int tasdevice_select_tuningprm_cfg(void *context, int prm_no, + int cfg_no, int rca_conf_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + struct tasdevice_config *conf; + int prog_status = 0; + int status, i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (cfg_no >= tas_fmw->nr_configurations) { + dev_err(tas_priv->dev, + "%s: cfg(%d) is not in range of conf %u\n", + __func__, cfg_no, tas_fmw->nr_configurations); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + if (rca_conf_no >= rca->ncfgs || rca_conf_no < 0 || + !cfg_info) { + dev_err(tas_priv->dev, + "conf_no:%d should be in range from 0 to %u\n", + rca_conf_no, rca->ncfgs-1); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (cfg_info[rca_conf_no]->active_dev & (1 << i)) { + if (prm_no >= 0 + && (tas_priv->tasdevice[i].cur_prog != prm_no + || tas_priv->force_fwload_status)) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + } else + tas_priv->tasdevice[i].is_loading = false; + tas_priv->tasdevice[i].is_loaderr = false; + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) { + struct tasdevice_fw *cal_fmw = + tas_priv->tasdevice[i].cali_data_fmw; + + if (cal_fmw) { + struct tasdevice_calibration + *cal = cal_fmw->calibrations; + + if (cal) + load_calib_data(tas_priv, + &(cal->dev_data)); + } + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + } + + for (i = 0, status = 0; i < tas_priv->ndev; i++) { + if (cfg_no >= 0 + && tas_priv->tasdevice[i].cur_conf != cfg_no + && (cfg_info[rca_conf_no]->active_dev & (1 << i)) + && (tas_priv->tasdevice[i].is_loaderr == false)) { + status++; + tas_priv->tasdevice[i].is_loading = true; + } else + tas_priv->tasdevice[i].is_loading = false; + } + + if (status) { + conf = &(tas_fmw->configs[cfg_no]); + status = 0; + tasdevice_load_data(tas_priv, &(conf->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) { + status |= 1 << (i + 4); + continue; + } else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) + tas_priv->tasdevice[i].cur_conf = cfg_no; + } + } else + dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n", + __func__, cfg_no); + + status |= cfg_info[rca_conf_no]->active_dev; + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_select_tuningprm_cfg, + SND_SOC_TAS2781_FMWLIB); + +int tasdevice_prmg_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + int prog_status = 0; + int i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (prm_no >= 0 && tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, SND_SOC_TAS2781_FMWLIB); + +int tasdevice_prmg_calibdata_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + int prog_status = 0; + int i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (prm_no >= 0 && tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + tas_priv->tasdevice[i].is_loaderr = false; + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) { + struct tasdevice_fw *cal_fmw = + tas_priv->tasdevice[i].cali_data_fmw; + + if (cal_fmw) { + struct tasdevice_calibration *cal = + cal_fmw->calibrations; + + if (cal) + load_calib_data(tas_priv, + &(cal->dev_data)); + } + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + } + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_calibdata_load, + SND_SOC_TAS2781_FMWLIB); + +void tasdevice_tuning_switch(void *context, int state) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + return; + } + + if (state == 0) { + if (tas_priv->cur_prog < tas_fmw->nr_programs) { + /*dsp mode or tuning mode*/ + profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + tasdevice_select_tuningprm_cfg(tas_priv, + tas_priv->cur_prog, tas_priv->cur_conf, + profile_cfg_id); + } + + tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_POWER_UP); + } else + tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_SHUTDOWN); +} +EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch, + SND_SOC_TAS2781_FMWLIB); + +MODULE_DESCRIPTION("Texas Firmware Support"); +MODULE_AUTHOR("Shenghao Ding, TI, "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c new file mode 100644 index 0000000000..917b1c15f7 --- /dev/null +++ b/sound/soc/codecs/tas2781-i2c.c @@ -0,0 +1,764 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier +// +// Copyright (C) 2022 - 2023 Texas Instruments Incorporated +// https://www.ti.com +// +// The TAS2781 driver implements a flexible and configurable +// algo coefficient setting for one, two, or even multiple +// TAS2781 chips. +// +// Author: Shenghao Ding +// Author: Kevin Lu +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct i2c_device_id tasdevice_id[] = { + { "tas2781", TAS2781 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tasdevice_id); + +#ifdef CONFIG_OF +static const struct of_device_id tasdevice_of_match[] = { + { .compatible = "ti,tas2781" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tasdevice_of_match); +#endif + +/** + * tas2781_digital_getvol - get the volum control + * @kcontrol: control pointer + * @ucontrol: User data + * Customer Kcontrol for tas2781 is primarily for regmap booking, paging + * depends on internal regmap mechanism. + * tas2781 contains book and page two-level register map, especially + * book switching will set the register BXXP00R7F, after switching to the + * correct book, then leverage the mechanism for paging to access the + * register. + */ +static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_digital_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_digital_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_amp_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_amp_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status; + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + tas_priv->force_fwload_status ? "ON" : "OFF"); + + return 0; +} + +static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(component); + bool change, val = (bool)ucontrol->value.integer.value[0]; + + if (tas_priv->force_fwload_status == val) + change = false; + else { + change = true; + tas_priv->force_fwload_status = val; + } + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + tas_priv->force_fwload_status ? "ON" : "OFF"); + + return change; +} + +static const struct snd_kcontrol_new tas2781_snd_controls[] = { + SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, + 1, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, amp_vol_tlv), + SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL, + 0, 0, 200, 1, tas2781_digital_getvol, + tas2781_digital_putvol, dvc_tlv), + SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, + tas2781_force_fwload_get, tas2781_force_fwload_put), +}; + +static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (tas_priv->rcabin.profile_cfg_id != + ucontrol->value.integer.value[0]) { + tas_priv->rcabin.profile_cfg_id = + ucontrol->value.integer.value[0]; + ret = 1; + } + + return ret; +} + +static int tasdevice_info_programs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)tas_fw->nr_programs; + + return 0; +} + +static int tasdevice_info_configurations( + struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)tas_fw->nr_configurations - 1; + + return 0; +} + +static int tasdevice_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1; + + return 0; +} + +static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id; + + return 0; +} + +static int tasdevice_create_control(struct tasdevice_priv *tas_priv) +{ + struct snd_kcontrol_new *prof_ctrls; + int nr_controls = 1; + int mix_index = 0; + int ret; + char *name; + + prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, + sizeof(prof_ctrls[0]), GFP_KERNEL); + if (!prof_ctrls) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the active profile */ + name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "Speaker Profile Id"); + prof_ctrls[mix_index].name = name; + prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + prof_ctrls[mix_index].info = tasdevice_info_profile; + prof_ctrls[mix_index].get = tasdevice_get_profile_id; + prof_ctrls[mix_index].put = tasdevice_set_profile_id; + mix_index++; + + ret = snd_soc_add_component_controls(tas_priv->codec, + prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); + +out: + return ret; +} + +static int tasdevice_program_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->cur_prog; + + return 0; +} + +static int tasdevice_program_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int nr_program = ucontrol->value.integer.value[0]; + int ret = 0; + + if (tas_priv->cur_prog != nr_program) { + tas_priv->cur_prog = nr_program; + ret = 1; + } + + return ret; +} + +static int tasdevice_configuration_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->cur_conf; + + return 0; +} + +static int tasdevice_configuration_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int nr_configuration = ucontrol->value.integer.value[0]; + int ret = 0; + + if (tas_priv->cur_conf != nr_configuration) { + tas_priv->cur_conf = nr_configuration; + ret = 1; + } + + return ret; +} + +static int tasdevice_dsp_create_ctrls( + struct tasdevice_priv *tas_priv) +{ + struct snd_kcontrol_new *dsp_ctrls; + char *prog_name, *conf_name; + int nr_controls = 2; + int mix_index = 0; + int ret; + + /* Alloc kcontrol via devm_kzalloc, which don't manually + * free the kcontrol + */ + dsp_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, + sizeof(dsp_ctrls[0]), GFP_KERNEL); + if (!dsp_ctrls) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the active profile */ + prog_name = devm_kzalloc(tas_priv->dev, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); + conf_name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + GFP_KERNEL); + if (!prog_name || !conf_name) { + ret = -ENOMEM; + goto out; + } + + scnprintf(prog_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "Speaker Program Id"); + dsp_ctrls[mix_index].name = prog_name; + dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + dsp_ctrls[mix_index].info = tasdevice_info_programs; + dsp_ctrls[mix_index].get = tasdevice_program_get; + dsp_ctrls[mix_index].put = tasdevice_program_put; + mix_index++; + + scnprintf(conf_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "Speaker Config Id"); + dsp_ctrls[mix_index].name = conf_name; + dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + dsp_ctrls[mix_index].info = tasdevice_info_configurations; + dsp_ctrls[mix_index].get = tasdevice_configuration_get; + dsp_ctrls[mix_index].put = tasdevice_configuration_put; + mix_index++; + + ret = snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls, + nr_controls < mix_index ? nr_controls : mix_index); + +out: + return ret; +} + +static void tasdevice_fw_ready(const struct firmware *fmw, + void *context) +{ + struct tasdevice_priv *tas_priv = context; + int ret = 0; + int i; + + mutex_lock(&tas_priv->codec_lock); + + ret = tasdevice_rca_parser(tas_priv, fmw); + if (ret) + goto out; + tasdevice_create_control(tas_priv); + + tasdevice_dsp_remove(tas_priv); + tasdevice_calbin_remove(tas_priv); + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; + scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", + tas_priv->dev_name); + ret = tasdevice_dsp_parser(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dspfw load %s error\n", + tas_priv->coef_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + tasdevice_dsp_create_ctrls(tas_priv); + + tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; + + /* If calibrated data occurs error, dsp will still works with default + * calibrated data inside algo. + */ + for (i = 0; i < tas_priv->ndev; i++) { + scnprintf(tas_priv->cal_binaryname[i], 64, "%s_cal_0x%02x.bin", + tas_priv->dev_name, tas_priv->tasdevice[i].dev_addr); + ret = tas2781_load_calibration(tas_priv, + tas_priv->cal_binaryname[i], i); + if (ret != 0) + dev_err(tas_priv->dev, + "%s: load %s error, default will effect\n", + __func__, tas_priv->cal_binaryname[i]); + } + + tasdevice_prmg_calibdata_load(tas_priv, 0); + tas_priv->cur_prog = 0; +out: + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + /*If DSP FW fail, kcontrol won't be created */ + tasdevice_config_info_remove(tas_priv); + tasdevice_dsp_remove(tas_priv); + } + mutex_unlock(&tas_priv->codec_lock); + if (fmw) + release_firmware(fmw); +} + +static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int state = 0; + + /* Codec Lock Hold */ + mutex_lock(&tas_priv->codec_lock); + if (event == SND_SOC_DAPM_PRE_PMD) + state = 1; + tasdevice_tuning_switch(tas_priv, state); + /* Codec Lock Release*/ + mutex_unlock(&tas_priv->codec_lock); + + return 0; +} + +static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, + 0, 0, tasdevice_dapm_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_INPUT("DMIC") +}; + +static const struct snd_soc_dapm_route tasdevice_audio_map[] = { + {"SPK", NULL, "ASI"}, + {"OUT", NULL, "SPK"}, + {"ASI OUT", NULL, "DMIC"} +}; + +static int tasdevice_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) { + dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + ret = -EINVAL; + } + + return ret; +} + +static int tasdevice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(dai); + unsigned int slot_width; + unsigned int fsrate; + int bclk_rate; + int rc = 0; + + fsrate = params_rate(params); + switch (fsrate) { + case 48000: + case 44100: + break; + default: + dev_err(tas_priv->dev, "%s: incorrect sample rate = %u\n", + __func__, fsrate); + rc = -EINVAL; + goto out; + } + + slot_width = params_width(params); + switch (slot_width) { + case 16: + case 20: + case 24: + case 32: + break; + default: + dev_err(tas_priv->dev, "%s: incorrect slot width = %u\n", + __func__, slot_width); + rc = -EINVAL; + goto out; + } + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) { + dev_err(tas_priv->dev, "%s: incorrect bclk rate = %d\n", + __func__, bclk_rate); + rc = bclk_rate; + goto out; + } + +out: + return rc; +} + +static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(codec_dai); + + tas_priv->sysclk = freq; + + return 0; +} + +static const struct snd_soc_dai_ops tasdevice_dai_ops = { + .startup = tasdevice_startup, + .hw_params = tasdevice_hw_params, + .set_sysclk = tasdevice_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver tasdevice_dai_driver[] = { + { + .name = "tas2781_codec", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 4, + .rates = TASDEVICE_RATES, + .formats = TASDEVICE_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TASDEVICE_RATES, + .formats = TASDEVICE_FORMATS, + }, + .ops = &tasdevice_dai_ops, + .symmetric_rate = 1, + }, +}; + +static int tasdevice_codec_probe(struct snd_soc_component *codec) +{ + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + return tascodec_init(tas_priv, codec, tasdevice_fw_ready); +} + +static void tasdevice_deinit(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + + tasdevice_config_info_remove(tas_priv); + tasdevice_dsp_remove(tas_priv); + tasdevice_calbin_remove(tas_priv); + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; +} + +static void tasdevice_codec_remove( + struct snd_soc_component *codec) +{ + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + tasdevice_deinit(tas_priv); +} + +static const struct snd_soc_component_driver + soc_codec_driver_tasdevice = { + .probe = tasdevice_codec_probe, + .remove = tasdevice_codec_remove, + .controls = tas2781_snd_controls, + .num_controls = ARRAY_SIZE(tas2781_snd_controls), + .dapm_widgets = tasdevice_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets), + .dapm_routes = tasdevice_audio_map, + .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map), + .idle_bias_on = 1, + .endianness = 1, +}; + +static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) +{ + struct i2c_client *client = (struct i2c_client *)tas_priv->client; + unsigned int dev_addrs[TASDEVICE_MAX_CHANNELS]; + int rc, i, ndev = 0; + + if (tas_priv->isacpi) { + ndev = device_property_read_u32_array(&client->dev, + "ti,audio-slots", NULL, 0); + if (ndev <= 0) { + ndev = 1; + dev_addrs[0] = client->addr; + } else { + ndev = (ndev < ARRAY_SIZE(dev_addrs)) + ? ndev : ARRAY_SIZE(dev_addrs); + ndev = device_property_read_u32_array(&client->dev, + "ti,audio-slots", dev_addrs, ndev); + } + + tas_priv->irq_info.irq_gpio = + acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0); + } else { + struct device_node *np = tas_priv->dev->of_node; +#ifdef CONFIG_OF + const __be32 *reg, *reg_end; + int len, sw, aw; + + aw = of_n_addr_cells(np); + sw = of_n_size_cells(np); + if (sw == 0) { + reg = (const __be32 *)of_get_property(np, + "reg", &len); + reg_end = reg + len/sizeof(*reg); + ndev = 0; + do { + dev_addrs[ndev] = of_read_number(reg, aw); + reg += aw; + ndev++; + } while (reg < reg_end); + } else { + ndev = 1; + dev_addrs[0] = client->addr; + } +#else + ndev = 1; + dev_addrs[0] = client->addr; +#endif + tas_priv->irq_info.irq_gpio = of_irq_get(np, 0); + } + tas_priv->ndev = ndev; + for (i = 0; i < ndev; i++) + tas_priv->tasdevice[i].dev_addr = dev_addrs[i]; + + tas_priv->reset = devm_gpiod_get_optional(&client->dev, + "reset-gpios", GPIOD_OUT_HIGH); + if (IS_ERR(tas_priv->reset)) + dev_err(tas_priv->dev, "%s Can't get reset GPIO\n", + __func__); + + strcpy(tas_priv->dev_name, tasdevice_id[tas_priv->chip_id].name); + + if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) { + rc = gpio_request(tas_priv->irq_info.irq_gpio, + "AUDEV-IRQ"); + if (!rc) { + gpio_direction_input( + tas_priv->irq_info.irq_gpio); + + tas_priv->irq_info.irq = + gpio_to_irq(tas_priv->irq_info.irq_gpio); + } else + dev_err(tas_priv->dev, "%s: GPIO %d request error\n", + __func__, tas_priv->irq_info.irq_gpio); + } else + dev_err(tas_priv->dev, + "Looking up irq-gpio property failed %d\n", + tas_priv->irq_info.irq_gpio); +} + +static int tasdevice_i2c_probe(struct i2c_client *i2c) +{ + const struct i2c_device_id *id = i2c_match_id(tasdevice_id, i2c); + const struct acpi_device_id *acpi_id; + struct tasdevice_priv *tas_priv; + int ret; + + tas_priv = tasdevice_kzalloc(i2c); + if (!tas_priv) + return -ENOMEM; + + dev_set_drvdata(&i2c->dev, tas_priv); + + if (ACPI_HANDLE(&i2c->dev)) { + acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table, + &i2c->dev); + if (!acpi_id) { + dev_err(&i2c->dev, "No driver data\n"); + ret = -EINVAL; + goto err; + } + tas_priv->chip_id = acpi_id->driver_data; + tas_priv->isacpi = true; + } else { + tas_priv->chip_id = id ? id->driver_data : 0; + tas_priv->isacpi = false; + } + + tasdevice_parse_dt(tas_priv); + + ret = tasdevice_init(tas_priv); + if (ret) + goto err; + + ret = devm_snd_soc_register_component(tas_priv->dev, + &soc_codec_driver_tasdevice, + tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver)); + if (ret) { + dev_err(tas_priv->dev, "%s: codec register error:0x%08x\n", + __func__, ret); + goto err; + } +err: + if (ret < 0) + tasdevice_remove(tas_priv); + return ret; +} + +static void tasdevice_i2c_remove(struct i2c_client *client) +{ + struct tasdevice_priv *tas_priv = i2c_get_clientdata(client); + + tasdevice_remove(tas_priv); +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id tasdevice_acpi_match[] = { + { "TAS2781", TAS2781 }, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match); +#endif + +static struct i2c_driver tasdevice_i2c_driver = { + .driver = { + .name = "tas2781-codec", + .of_match_table = of_match_ptr(tasdevice_of_match), +#ifdef CONFIG_ACPI + .acpi_match_table = ACPI_PTR(tasdevice_acpi_match), +#endif + }, + .probe = tasdevice_i2c_probe, + .remove = tasdevice_i2c_remove, + .id_table = tasdevice_id, +}; + +module_i2c_driver(tasdevice_i2c_driver); + +MODULE_AUTHOR("Shenghao Ding "); +MODULE_AUTHOR("Kevin Lu "); +MODULE_DESCRIPTION("ASoC TAS2781 Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB); diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c new file mode 100644 index 0000000000..60e59e993b --- /dev/null +++ b/sound/soc/codecs/tas5086.c @@ -0,0 +1,1001 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * TAS5086 ASoC codec driver + * + * Copyright (c) 2013 Daniel Mack + * + * TODO: + * - implement DAPM and input muxing + * - implement modulation limit + * - implement non-default PWM start + * + * Note that this chip has a very unusual register layout, specifically + * because the registers are of unequal size, and multi-byte registers + * require bulk writes to take effect. Regmap does not support that kind + * of devices. + * + * Currently, the driver does not touch any of the registers >= 0x20, so + * it doesn't matter because the entire map can be accessed as 8-bit + * array. In case more features will be added in the future + * that require access to higher registers, the entire regmap H/W I/O + * routines have to be open-coded. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAS5086_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define TAS5086_PCM_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) + +/* + * TAS5086 registers + */ +#define TAS5086_CLOCK_CONTROL 0x00 /* Clock control register */ +#define TAS5086_CLOCK_RATE(val) (val << 5) +#define TAS5086_CLOCK_RATE_MASK (0x7 << 5) +#define TAS5086_CLOCK_RATIO(val) (val << 2) +#define TAS5086_CLOCK_RATIO_MASK (0x7 << 2) +#define TAS5086_CLOCK_SCLK_RATIO_48 (1 << 1) +#define TAS5086_CLOCK_VALID (1 << 0) + +#define TAS5086_DEEMPH_MASK 0x03 +#define TAS5086_SOFT_MUTE_ALL 0x3f + +#define TAS5086_DEV_ID 0x01 /* Device ID register */ +#define TAS5086_ERROR_STATUS 0x02 /* Error status register */ +#define TAS5086_SYS_CONTROL_1 0x03 /* System control register 1 */ +#define TAS5086_SERIAL_DATA_IF 0x04 /* Serial data interface register */ +#define TAS5086_SYS_CONTROL_2 0x05 /* System control register 2 */ +#define TAS5086_SOFT_MUTE 0x06 /* Soft mute register */ +#define TAS5086_MASTER_VOL 0x07 /* Master volume */ +#define TAS5086_CHANNEL_VOL(X) (0x08 + (X)) /* Channel 1-6 volume */ +#define TAS5086_VOLUME_CONTROL 0x09 /* Volume control register */ +#define TAS5086_MOD_LIMIT 0x10 /* Modulation limit register */ +#define TAS5086_PWM_START 0x18 /* PWM start register */ +#define TAS5086_SURROUND 0x19 /* Surround register */ +#define TAS5086_SPLIT_CAP_CHARGE 0x1a /* Split cap charge period register */ +#define TAS5086_OSC_TRIM 0x1b /* Oscillator trim register */ +#define TAS5086_BKNDERR 0x1c +#define TAS5086_INPUT_MUX 0x20 +#define TAS5086_PWM_OUTPUT_MUX 0x25 + +#define TAS5086_MAX_REGISTER TAS5086_PWM_OUTPUT_MUX + +#define TAS5086_PWM_START_MIDZ_FOR_START_1 (1 << 7) +#define TAS5086_PWM_START_MIDZ_FOR_START_2 (1 << 6) +#define TAS5086_PWM_START_CHANNEL_MASK (0x3f) + +/* + * Default TAS5086 power-up configuration + */ +static const struct reg_default tas5086_reg_defaults[] = { + { 0x00, 0x6c }, + { 0x01, 0x03 }, + { 0x02, 0x00 }, + { 0x03, 0xa0 }, + { 0x04, 0x05 }, + { 0x05, 0x60 }, + { 0x06, 0x00 }, + { 0x07, 0xff }, + { 0x08, 0x30 }, + { 0x09, 0x30 }, + { 0x0a, 0x30 }, + { 0x0b, 0x30 }, + { 0x0c, 0x30 }, + { 0x0d, 0x30 }, + { 0x0e, 0xb1 }, + { 0x0f, 0x00 }, + { 0x10, 0x02 }, + { 0x11, 0x00 }, + { 0x12, 0x00 }, + { 0x13, 0x00 }, + { 0x14, 0x00 }, + { 0x15, 0x00 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, + { 0x18, 0x3f }, + { 0x19, 0x00 }, + { 0x1a, 0x18 }, + { 0x1b, 0x82 }, + { 0x1c, 0x05 }, +}; + +static int tas5086_register_size(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS5086_CLOCK_CONTROL ... TAS5086_BKNDERR: + return 1; + case TAS5086_INPUT_MUX: + case TAS5086_PWM_OUTPUT_MUX: + return 4; + } + + dev_err(dev, "Unsupported register address: %d\n", reg); + return 0; +} + +static bool tas5086_accessible_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x0f: + case 0x11 ... 0x17: + case 0x1d ... 0x1f: + return false; + default: + return true; + } +} + +static bool tas5086_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS5086_DEV_ID: + case TAS5086_ERROR_STATUS: + return true; + } + + return false; +} + +static bool tas5086_writeable_reg(struct device *dev, unsigned int reg) +{ + return tas5086_accessible_reg(dev, reg) && (reg != TAS5086_DEV_ID); +} + +static int tas5086_reg_write(void *context, unsigned int reg, + unsigned int value) +{ + struct i2c_client *client = context; + unsigned int i, size; + uint8_t buf[5]; + int ret; + + size = tas5086_register_size(&client->dev, reg); + if (size == 0) + return -EINVAL; + + buf[0] = reg; + + for (i = size; i >= 1; --i) { + buf[i] = value; + value >>= 8; + } + + ret = i2c_master_send(client, buf, size + 1); + if (ret == size + 1) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int tas5086_reg_read(void *context, unsigned int reg, + unsigned int *value) +{ + struct i2c_client *client = context; + uint8_t send_buf, recv_buf[4]; + struct i2c_msg msgs[2]; + unsigned int size; + unsigned int i; + int ret; + + size = tas5086_register_size(&client->dev, reg); + if (size == 0) + return -EINVAL; + + send_buf = reg; + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(send_buf); + msgs[0].buf = &send_buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = size; + msgs[1].buf = recv_buf; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + else if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *value = 0; + + for (i = 0; i < size; i++) { + *value <<= 8; + *value |= recv_buf[i]; + } + + return 0; +} + +static const char * const supply_names[] = { + "dvdd", "avdd" +}; + +struct tas5086_private { + struct regmap *regmap; + unsigned int mclk, sclk; + unsigned int format; + bool deemph; + unsigned int charge_period; + unsigned int pwm_start_mid_z; + /* Current sample rate for de-emphasis control */ + int rate; + /* GPIO driving Reset pin, if any */ + int gpio_nreset; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; +}; + +static int tas5086_deemph[] = { 0, 32000, 44100, 48000 }; + +static int tas5086_set_deemph(struct snd_soc_component *component) +{ + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + int i, val = 0; + + if (priv->deemph) { + for (i = 0; i < ARRAY_SIZE(tas5086_deemph); i++) { + if (tas5086_deemph[i] == priv->rate) { + val = i; + break; + } + } + } + + return regmap_update_bits(priv->regmap, TAS5086_SYS_CONTROL_1, + TAS5086_DEEMPH_MASK, val); +} + +static int tas5086_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = priv->deemph; + + return 0; +} + +static int tas5086_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + + priv->deemph = ucontrol->value.integer.value[0]; + + return tas5086_set_deemph(component); +} + + +static int tas5086_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case TAS5086_CLK_IDX_MCLK: + priv->mclk = freq; + break; + case TAS5086_CLK_IDX_SCLK: + priv->sclk = freq; + break; + } + + return 0; +} + +static int tas5086_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + + /* The TAS5086 can only be slave to all clocks */ + if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { + dev_err(component->dev, "Invalid clocking mode\n"); + return -EINVAL; + } + + /* we need to refer to the data format from hw_params() */ + priv->format = format; + + return 0; +} + +static const int tas5086_sample_rates[] = { + 32000, 38000, 44100, 48000, 88200, 96000, 176400, 192000 +}; + +static const int tas5086_ratios[] = { + 64, 128, 192, 256, 384, 512 +}; + +static int index_in_array(const int *array, int len, int needle) +{ + int i; + + for (i = 0; i < len; i++) + if (array[i] == needle) + return i; + + return -ENOENT; +} + +static int tas5086_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + int val; + int ret; + + priv->rate = params_rate(params); + + /* Look up the sample rate and refer to the offset in the list */ + val = index_in_array(tas5086_sample_rates, + ARRAY_SIZE(tas5086_sample_rates), priv->rate); + + if (val < 0) { + dev_err(component->dev, "Invalid sample rate\n"); + return -EINVAL; + } + + ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, + TAS5086_CLOCK_RATE_MASK, + TAS5086_CLOCK_RATE(val)); + if (ret < 0) + return ret; + + /* MCLK / Fs ratio */ + val = index_in_array(tas5086_ratios, ARRAY_SIZE(tas5086_ratios), + priv->mclk / priv->rate); + if (val < 0) { + dev_err(component->dev, "Invalid MCLK / Fs ratio\n"); + return -EINVAL; + } + + ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, + TAS5086_CLOCK_RATIO_MASK, + TAS5086_CLOCK_RATIO(val)); + if (ret < 0) + return ret; + + + ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, + TAS5086_CLOCK_SCLK_RATIO_48, + (priv->sclk == 48 * priv->rate) ? + TAS5086_CLOCK_SCLK_RATIO_48 : 0); + if (ret < 0) + return ret; + + /* + * The chip has a very unituitive register mapping and muxes information + * about data format and sample depth into the same register, but not on + * a logical bit-boundary. Hence, we have to refer to the format passed + * in the set_dai_fmt() callback and set up everything from here. + * + * First, determine the 'base' value, using the format ... + */ + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + val = 0x00; + break; + case SND_SOC_DAIFMT_I2S: + val = 0x03; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = 0x06; + break; + default: + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + /* ... then add the offset for the sample bit depth. */ + switch (params_width(params)) { + case 16: + val += 0; + break; + case 20: + val += 1; + break; + case 24: + val += 2; + break; + default: + dev_err(component->dev, "Invalid bit width\n"); + return -EINVAL; + } + + ret = regmap_write(priv->regmap, TAS5086_SERIAL_DATA_IF, val); + if (ret < 0) + return ret; + + /* clock is considered valid now */ + ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, + TAS5086_CLOCK_VALID, TAS5086_CLOCK_VALID); + if (ret < 0) + return ret; + + return tas5086_set_deemph(component); +} + +static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + unsigned int val = 0; + + if (mute) + val = TAS5086_SOFT_MUTE_ALL; + + return regmap_write(priv->regmap, TAS5086_SOFT_MUTE, val); +} + +static void tas5086_reset(struct tas5086_private *priv) +{ + if (gpio_is_valid(priv->gpio_nreset)) { + /* Reset codec - minimum assertion time is 400ns */ + gpio_direction_output(priv->gpio_nreset, 0); + udelay(1); + gpio_set_value(priv->gpio_nreset, 1); + + /* Codec needs ~15ms to wake up */ + msleep(15); + } +} + +/* charge period values in microseconds */ +static const int tas5086_charge_period[] = { + 13000, 16900, 23400, 31200, 41600, 54600, 72800, 96200, + 130000, 156000, 234000, 312000, 416000, 546000, 728000, 962000, + 1300000, 169000, 2340000, 3120000, 4160000, 5460000, 7280000, 9620000, +}; + +static int tas5086_init(struct device *dev, struct tas5086_private *priv) +{ + int ret, i; + + /* + * If any of the channels is configured to start in Mid-Z mode, + * configure 'part 1' of the PWM starts to use Mid-Z, and tell + * all configured mid-z channels to start under 'part 1'. + */ + if (priv->pwm_start_mid_z) + regmap_write(priv->regmap, TAS5086_PWM_START, + TAS5086_PWM_START_MIDZ_FOR_START_1 | + priv->pwm_start_mid_z); + + /* lookup and set split-capacitor charge period */ + if (priv->charge_period == 0) { + regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0); + } else { + i = index_in_array(tas5086_charge_period, + ARRAY_SIZE(tas5086_charge_period), + priv->charge_period); + if (i >= 0) + regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, + i + 0x08); + else + dev_warn(dev, + "Invalid split-cap charge period of %d ns.\n", + priv->charge_period); + } + + /* enable factory trim */ + ret = regmap_write(priv->regmap, TAS5086_OSC_TRIM, 0x00); + if (ret < 0) + return ret; + + /* start all channels */ + ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x20); + if (ret < 0) + return ret; + + /* mute all channels for now */ + ret = regmap_write(priv->regmap, TAS5086_SOFT_MUTE, + TAS5086_SOFT_MUTE_ALL); + if (ret < 0) + return ret; + + return 0; +} + +/* TAS5086 controls */ +static const DECLARE_TLV_DB_SCALE(tas5086_dac_tlv, -10350, 50, 1); + +static const struct snd_kcontrol_new tas5086_controls[] = { + SOC_SINGLE_TLV("Master Playback Volume", TAS5086_MASTER_VOL, + 0, 0xff, 1, tas5086_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume", + TAS5086_CHANNEL_VOL(0), TAS5086_CHANNEL_VOL(1), + 0, 0xff, 1, tas5086_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume", + TAS5086_CHANNEL_VOL(2), TAS5086_CHANNEL_VOL(3), + 0, 0xff, 1, tas5086_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume", + TAS5086_CHANNEL_VOL(4), TAS5086_CHANNEL_VOL(5), + 0, 0xff, 1, tas5086_dac_tlv), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + tas5086_get_deemph, tas5086_put_deemph), +}; + +/* Input mux controls */ +static const char *tas5086_dapm_sdin_texts[] = +{ + "SDIN1-L", "SDIN1-R", "SDIN2-L", "SDIN2-R", + "SDIN3-L", "SDIN3-R", "Ground (0)", "nc" +}; + +static const struct soc_enum tas5086_dapm_input_mux_enum[] = { + SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 20, 8, tas5086_dapm_sdin_texts), + SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 16, 8, tas5086_dapm_sdin_texts), + SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 12, 8, tas5086_dapm_sdin_texts), + SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 8, 8, tas5086_dapm_sdin_texts), + SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 4, 8, tas5086_dapm_sdin_texts), + SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 0, 8, tas5086_dapm_sdin_texts), +}; + +static const struct snd_kcontrol_new tas5086_dapm_input_mux_controls[] = { + SOC_DAPM_ENUM("Channel 1 input", tas5086_dapm_input_mux_enum[0]), + SOC_DAPM_ENUM("Channel 2 input", tas5086_dapm_input_mux_enum[1]), + SOC_DAPM_ENUM("Channel 3 input", tas5086_dapm_input_mux_enum[2]), + SOC_DAPM_ENUM("Channel 4 input", tas5086_dapm_input_mux_enum[3]), + SOC_DAPM_ENUM("Channel 5 input", tas5086_dapm_input_mux_enum[4]), + SOC_DAPM_ENUM("Channel 6 input", tas5086_dapm_input_mux_enum[5]), +}; + +/* Output mux controls */ +static const char *tas5086_dapm_channel_texts[] = + { "Channel 1 Mux", "Channel 2 Mux", "Channel 3 Mux", + "Channel 4 Mux", "Channel 5 Mux", "Channel 6 Mux" }; + +static const struct soc_enum tas5086_dapm_output_mux_enum[] = { + SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 20, 6, tas5086_dapm_channel_texts), + SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 16, 6, tas5086_dapm_channel_texts), + SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 12, 6, tas5086_dapm_channel_texts), + SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 8, 6, tas5086_dapm_channel_texts), + SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 4, 6, tas5086_dapm_channel_texts), + SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 0, 6, tas5086_dapm_channel_texts), +}; + +static const struct snd_kcontrol_new tas5086_dapm_output_mux_controls[] = { + SOC_DAPM_ENUM("PWM1 Output", tas5086_dapm_output_mux_enum[0]), + SOC_DAPM_ENUM("PWM2 Output", tas5086_dapm_output_mux_enum[1]), + SOC_DAPM_ENUM("PWM3 Output", tas5086_dapm_output_mux_enum[2]), + SOC_DAPM_ENUM("PWM4 Output", tas5086_dapm_output_mux_enum[3]), + SOC_DAPM_ENUM("PWM5 Output", tas5086_dapm_output_mux_enum[4]), + SOC_DAPM_ENUM("PWM6 Output", tas5086_dapm_output_mux_enum[5]), +}; + +static const struct snd_soc_dapm_widget tas5086_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("SDIN1-L"), + SND_SOC_DAPM_INPUT("SDIN1-R"), + SND_SOC_DAPM_INPUT("SDIN2-L"), + SND_SOC_DAPM_INPUT("SDIN2-R"), + SND_SOC_DAPM_INPUT("SDIN3-L"), + SND_SOC_DAPM_INPUT("SDIN3-R"), + SND_SOC_DAPM_INPUT("SDIN4-L"), + SND_SOC_DAPM_INPUT("SDIN4-R"), + + SND_SOC_DAPM_OUTPUT("PWM1"), + SND_SOC_DAPM_OUTPUT("PWM2"), + SND_SOC_DAPM_OUTPUT("PWM3"), + SND_SOC_DAPM_OUTPUT("PWM4"), + SND_SOC_DAPM_OUTPUT("PWM5"), + SND_SOC_DAPM_OUTPUT("PWM6"), + + SND_SOC_DAPM_MUX("Channel 1 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_input_mux_controls[0]), + SND_SOC_DAPM_MUX("Channel 2 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_input_mux_controls[1]), + SND_SOC_DAPM_MUX("Channel 3 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_input_mux_controls[2]), + SND_SOC_DAPM_MUX("Channel 4 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_input_mux_controls[3]), + SND_SOC_DAPM_MUX("Channel 5 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_input_mux_controls[4]), + SND_SOC_DAPM_MUX("Channel 6 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_input_mux_controls[5]), + + SND_SOC_DAPM_MUX("PWM1 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_output_mux_controls[0]), + SND_SOC_DAPM_MUX("PWM2 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_output_mux_controls[1]), + SND_SOC_DAPM_MUX("PWM3 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_output_mux_controls[2]), + SND_SOC_DAPM_MUX("PWM4 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_output_mux_controls[3]), + SND_SOC_DAPM_MUX("PWM5 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_output_mux_controls[4]), + SND_SOC_DAPM_MUX("PWM6 Mux", SND_SOC_NOPM, 0, 0, + &tas5086_dapm_output_mux_controls[5]), +}; + +static const struct snd_soc_dapm_route tas5086_dapm_routes[] = { + /* SDIN inputs -> channel muxes */ + { "Channel 1 Mux", "SDIN1-L", "SDIN1-L" }, + { "Channel 1 Mux", "SDIN1-R", "SDIN1-R" }, + { "Channel 1 Mux", "SDIN2-L", "SDIN2-L" }, + { "Channel 1 Mux", "SDIN2-R", "SDIN2-R" }, + { "Channel 1 Mux", "SDIN3-L", "SDIN3-L" }, + { "Channel 1 Mux", "SDIN3-R", "SDIN3-R" }, + + { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" }, + { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" }, + { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" }, + { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" }, + { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" }, + { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" }, + + { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" }, + { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" }, + { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" }, + { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" }, + { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" }, + { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" }, + + { "Channel 3 Mux", "SDIN1-L", "SDIN1-L" }, + { "Channel 3 Mux", "SDIN1-R", "SDIN1-R" }, + { "Channel 3 Mux", "SDIN2-L", "SDIN2-L" }, + { "Channel 3 Mux", "SDIN2-R", "SDIN2-R" }, + { "Channel 3 Mux", "SDIN3-L", "SDIN3-L" }, + { "Channel 3 Mux", "SDIN3-R", "SDIN3-R" }, + + { "Channel 4 Mux", "SDIN1-L", "SDIN1-L" }, + { "Channel 4 Mux", "SDIN1-R", "SDIN1-R" }, + { "Channel 4 Mux", "SDIN2-L", "SDIN2-L" }, + { "Channel 4 Mux", "SDIN2-R", "SDIN2-R" }, + { "Channel 4 Mux", "SDIN3-L", "SDIN3-L" }, + { "Channel 4 Mux", "SDIN3-R", "SDIN3-R" }, + + { "Channel 5 Mux", "SDIN1-L", "SDIN1-L" }, + { "Channel 5 Mux", "SDIN1-R", "SDIN1-R" }, + { "Channel 5 Mux", "SDIN2-L", "SDIN2-L" }, + { "Channel 5 Mux", "SDIN2-R", "SDIN2-R" }, + { "Channel 5 Mux", "SDIN3-L", "SDIN3-L" }, + { "Channel 5 Mux", "SDIN3-R", "SDIN3-R" }, + + { "Channel 6 Mux", "SDIN1-L", "SDIN1-L" }, + { "Channel 6 Mux", "SDIN1-R", "SDIN1-R" }, + { "Channel 6 Mux", "SDIN2-L", "SDIN2-L" }, + { "Channel 6 Mux", "SDIN2-R", "SDIN2-R" }, + { "Channel 6 Mux", "SDIN3-L", "SDIN3-L" }, + { "Channel 6 Mux", "SDIN3-R", "SDIN3-R" }, + + /* Channel muxes -> PWM muxes */ + { "PWM1 Mux", "Channel 1 Mux", "Channel 1 Mux" }, + { "PWM2 Mux", "Channel 1 Mux", "Channel 1 Mux" }, + { "PWM3 Mux", "Channel 1 Mux", "Channel 1 Mux" }, + { "PWM4 Mux", "Channel 1 Mux", "Channel 1 Mux" }, + { "PWM5 Mux", "Channel 1 Mux", "Channel 1 Mux" }, + { "PWM6 Mux", "Channel 1 Mux", "Channel 1 Mux" }, + + { "PWM1 Mux", "Channel 2 Mux", "Channel 2 Mux" }, + { "PWM2 Mux", "Channel 2 Mux", "Channel 2 Mux" }, + { "PWM3 Mux", "Channel 2 Mux", "Channel 2 Mux" }, + { "PWM4 Mux", "Channel 2 Mux", "Channel 2 Mux" }, + { "PWM5 Mux", "Channel 2 Mux", "Channel 2 Mux" }, + { "PWM6 Mux", "Channel 2 Mux", "Channel 2 Mux" }, + + { "PWM1 Mux", "Channel 3 Mux", "Channel 3 Mux" }, + { "PWM2 Mux", "Channel 3 Mux", "Channel 3 Mux" }, + { "PWM3 Mux", "Channel 3 Mux", "Channel 3 Mux" }, + { "PWM4 Mux", "Channel 3 Mux", "Channel 3 Mux" }, + { "PWM5 Mux", "Channel 3 Mux", "Channel 3 Mux" }, + { "PWM6 Mux", "Channel 3 Mux", "Channel 3 Mux" }, + + { "PWM1 Mux", "Channel 4 Mux", "Channel 4 Mux" }, + { "PWM2 Mux", "Channel 4 Mux", "Channel 4 Mux" }, + { "PWM3 Mux", "Channel 4 Mux", "Channel 4 Mux" }, + { "PWM4 Mux", "Channel 4 Mux", "Channel 4 Mux" }, + { "PWM5 Mux", "Channel 4 Mux", "Channel 4 Mux" }, + { "PWM6 Mux", "Channel 4 Mux", "Channel 4 Mux" }, + + { "PWM1 Mux", "Channel 5 Mux", "Channel 5 Mux" }, + { "PWM2 Mux", "Channel 5 Mux", "Channel 5 Mux" }, + { "PWM3 Mux", "Channel 5 Mux", "Channel 5 Mux" }, + { "PWM4 Mux", "Channel 5 Mux", "Channel 5 Mux" }, + { "PWM5 Mux", "Channel 5 Mux", "Channel 5 Mux" }, + { "PWM6 Mux", "Channel 5 Mux", "Channel 5 Mux" }, + + { "PWM1 Mux", "Channel 6 Mux", "Channel 6 Mux" }, + { "PWM2 Mux", "Channel 6 Mux", "Channel 6 Mux" }, + { "PWM3 Mux", "Channel 6 Mux", "Channel 6 Mux" }, + { "PWM4 Mux", "Channel 6 Mux", "Channel 6 Mux" }, + { "PWM5 Mux", "Channel 6 Mux", "Channel 6 Mux" }, + { "PWM6 Mux", "Channel 6 Mux", "Channel 6 Mux" }, + + /* The PWM muxes are directly connected to the PWM outputs */ + { "PWM1", NULL, "PWM1 Mux" }, + { "PWM2", NULL, "PWM2 Mux" }, + { "PWM3", NULL, "PWM3 Mux" }, + { "PWM4", NULL, "PWM4 Mux" }, + { "PWM5", NULL, "PWM5 Mux" }, + { "PWM6", NULL, "PWM6 Mux" }, + +}; + +static const struct snd_soc_dai_ops tas5086_dai_ops = { + .hw_params = tas5086_hw_params, + .set_sysclk = tas5086_set_dai_sysclk, + .set_fmt = tas5086_set_dai_fmt, + .mute_stream = tas5086_mute_stream, +}; + +static struct snd_soc_dai_driver tas5086_dai = { + .name = "tas5086-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 6, + .rates = TAS5086_PCM_RATES, + .formats = TAS5086_PCM_FORMATS, + }, + .ops = &tas5086_dai_ops, +}; + +#ifdef CONFIG_PM +static int tas5086_soc_suspend(struct snd_soc_component *component) +{ + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + int ret; + + /* Shut down all channels */ + ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x60); + if (ret < 0) + return ret; + + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); + + return 0; +} + +static int tas5086_soc_resume(struct snd_soc_component *component) +{ + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); + if (ret < 0) + return ret; + + tas5086_reset(priv); + regcache_mark_dirty(priv->regmap); + + ret = tas5086_init(component->dev, priv); + if (ret < 0) + return ret; + + ret = regcache_sync(priv->regmap); + if (ret < 0) + return ret; + + return 0; +} +#else +#define tas5086_soc_suspend NULL +#define tas5086_soc_resume NULL +#endif /* CONFIG_PM */ + +#ifdef CONFIG_OF +static const struct of_device_id tas5086_dt_ids[] = { + { .compatible = "ti,tas5086", }, + { } +}; +MODULE_DEVICE_TABLE(of, tas5086_dt_ids); +#endif + +static int tas5086_probe(struct snd_soc_component *component) +{ + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + int i, ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); + if (ret < 0) { + dev_err(component->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + priv->pwm_start_mid_z = 0; + priv->charge_period = 1300000; /* hardware default is 1300 ms */ + + if (of_match_device(of_match_ptr(tas5086_dt_ids), component->dev)) { + struct device_node *of_node = component->dev->of_node; + + of_property_read_u32(of_node, "ti,charge-period", + &priv->charge_period); + + for (i = 0; i < 6; i++) { + char name[25]; + + snprintf(name, sizeof(name), + "ti,mid-z-channel-%d", i + 1); + + if (of_property_read_bool(of_node, name)) + priv->pwm_start_mid_z |= 1 << i; + } + } + + tas5086_reset(priv); + ret = tas5086_init(component->dev, priv); + if (ret < 0) + goto exit_disable_regulators; + + /* set master volume to 0 dB */ + ret = regmap_write(priv->regmap, TAS5086_MASTER_VOL, 0x30); + if (ret < 0) + goto exit_disable_regulators; + + return 0; + +exit_disable_regulators: + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); + + return ret; +} + +static void tas5086_remove(struct snd_soc_component *component) +{ + struct tas5086_private *priv = snd_soc_component_get_drvdata(component); + + if (gpio_is_valid(priv->gpio_nreset)) + /* Set codec to the reset state */ + gpio_set_value(priv->gpio_nreset, 0); + + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); +}; + +static const struct snd_soc_component_driver soc_component_dev_tas5086 = { + .probe = tas5086_probe, + .remove = tas5086_remove, + .suspend = tas5086_soc_suspend, + .resume = tas5086_soc_resume, + .controls = tas5086_controls, + .num_controls = ARRAY_SIZE(tas5086_controls), + .dapm_widgets = tas5086_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets), + .dapm_routes = tas5086_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct i2c_device_id tas5086_i2c_id[] = { + { "tas5086", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id); + +static const struct regmap_config tas5086_regmap = { + .reg_bits = 8, + .val_bits = 32, + .max_register = TAS5086_MAX_REGISTER, + .reg_defaults = tas5086_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5086_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = tas5086_volatile_reg, + .writeable_reg = tas5086_writeable_reg, + .readable_reg = tas5086_accessible_reg, + .reg_read = tas5086_reg_read, + .reg_write = tas5086_reg_write, +}; + +static int tas5086_i2c_probe(struct i2c_client *i2c) +{ + struct tas5086_private *priv; + struct device *dev = &i2c->dev; + int gpio_nreset = -EINVAL; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + priv->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret < 0) { + dev_err(dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + priv->regmap = devm_regmap_init(dev, NULL, i2c, &tas5086_regmap); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, priv); + + if (of_match_device(of_match_ptr(tas5086_dt_ids), dev)) { + struct device_node *of_node = dev->of_node; + gpio_nreset = of_get_named_gpio(of_node, "reset-gpio", 0); + } + + if (gpio_is_valid(gpio_nreset)) + if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset")) + gpio_nreset = -EINVAL; + + priv->gpio_nreset = gpio_nreset; + + ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + tas5086_reset(priv); + + /* The TAS5086 always returns 0x03 in its TAS5086_DEV_ID register */ + ret = regmap_read(priv->regmap, TAS5086_DEV_ID, &i); + if (ret == 0 && i != 0x3) { + dev_err(dev, + "Failed to identify TAS5086 codec (got %02x)\n", i); + ret = -ENODEV; + } + + /* + * The chip has been identified, so we can turn off the power + * again until the dai link is set up. + */ + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); + + if (ret == 0) + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_tas5086, + &tas5086_dai, 1); + + return ret; +} + +static void tas5086_i2c_remove(struct i2c_client *i2c) +{} + +static struct i2c_driver tas5086_i2c_driver = { + .driver = { + .name = "tas5086", + .of_match_table = of_match_ptr(tas5086_dt_ids), + }, + .id_table = tas5086_i2c_id, + .probe = tas5086_i2c_probe, + .remove = tas5086_i2c_remove, +}; + +module_i2c_driver(tas5086_i2c_driver); + +MODULE_AUTHOR("Daniel Mack "); +MODULE_DESCRIPTION("Texas Instruments TAS5086 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c new file mode 100644 index 0000000000..1756edb359 --- /dev/null +++ b/sound/soc/codecs/tas571x.c @@ -0,0 +1,986 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * TAS571x amplifier audio driver + * + * Copyright (C) 2015 Google, Inc. + * Copyright (c) 2013 Daniel Mack + * + * TAS5721 support: + * Copyright (C) 2016 Petr Kulhavy, Barix AG + * + * TAS5707 support: + * Copyright (C) 2018 Jerome Brunet, Baylibre SAS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas571x.h" + +#define TAS571X_MAX_SUPPLIES 6 + +struct tas571x_chip { + const char *const *supply_names; + int num_supply_names; + const struct snd_kcontrol_new *controls; + int num_controls; + const struct regmap_config *regmap_config; + int vol_reg_size; +}; + +struct tas571x_private { + const struct tas571x_chip *chip; + struct regmap *regmap; + struct regulator_bulk_data supplies[TAS571X_MAX_SUPPLIES]; + struct clk *mclk; + unsigned int format; + struct gpio_desc *reset_gpio; + struct gpio_desc *pdn_gpio; + struct snd_soc_component_driver component_driver; +}; + +static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg) +{ + switch (reg) { + case TAS571X_MVOL_REG: + case TAS571X_CH1_VOL_REG: + case TAS571X_CH2_VOL_REG: + return priv->chip->vol_reg_size; + case TAS571X_INPUT_MUX_REG: + case TAS571X_CH4_SRC_SELECT_REG: + case TAS571X_PWM_MUX_REG: + case TAS5717_CH1_RIGHT_CH_MIX_REG: + case TAS5717_CH1_LEFT_CH_MIX_REG: + case TAS5717_CH2_LEFT_CH_MIX_REG: + case TAS5717_CH2_RIGHT_CH_MIX_REG: + return 4; + default: + return 1; + } +} + +static int tas571x_reg_write(void *context, unsigned int reg, + unsigned int value) +{ + struct i2c_client *client = context; + struct tas571x_private *priv = i2c_get_clientdata(client); + unsigned int i, size; + uint8_t buf[5]; + int ret; + + size = tas571x_register_size(priv, reg); + buf[0] = reg; + + for (i = size; i >= 1; --i) { + buf[i] = value; + value >>= 8; + } + + ret = i2c_master_send(client, buf, size + 1); + if (ret == size + 1) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int tas571x_reg_read(void *context, unsigned int reg, + unsigned int *value) +{ + struct i2c_client *client = context; + struct tas571x_private *priv = i2c_get_clientdata(client); + uint8_t send_buf, recv_buf[4]; + struct i2c_msg msgs[2]; + unsigned int size; + unsigned int i; + int ret; + + size = tas571x_register_size(priv, reg); + send_buf = reg; + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(send_buf); + msgs[0].buf = &send_buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = size; + msgs[1].buf = recv_buf; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + else if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *value = 0; + + for (i = 0; i < size; i++) { + *value <<= 8; + *value |= recv_buf[i]; + } + + return 0; +} + +/* + * register write for 8- and 20-byte registers + */ +static int tas571x_reg_write_multiword(struct i2c_client *client, + unsigned int reg, const long values[], size_t len) +{ + size_t i; + uint8_t *buf, *p; + int ret; + size_t send_size = 1 + len * sizeof(uint32_t); + + buf = kzalloc(send_size, GFP_KERNEL | GFP_DMA); + if (!buf) + return -ENOMEM; + buf[0] = reg; + + for (i = 0, p = buf + 1; i < len; i++, p += sizeof(uint32_t)) + put_unaligned_be32(values[i], p); + + ret = i2c_master_send(client, buf, send_size); + + kfree(buf); + + if (ret == send_size) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +/* + * register read for 8- and 20-byte registers + */ +static int tas571x_reg_read_multiword(struct i2c_client *client, + unsigned int reg, long values[], size_t len) +{ + unsigned int i; + uint8_t send_buf; + uint8_t *recv_buf, *p; + struct i2c_msg msgs[2]; + unsigned int recv_size = len * sizeof(uint32_t); + int ret; + + recv_buf = kzalloc(recv_size, GFP_KERNEL | GFP_DMA); + if (!recv_buf) + return -ENOMEM; + + send_buf = reg; + + msgs[0].addr = client->addr; + msgs[0].len = sizeof(send_buf); + msgs[0].buf = &send_buf; + msgs[0].flags = 0; + + msgs[1].addr = client->addr; + msgs[1].len = recv_size; + msgs[1].buf = recv_buf; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + goto err_ret; + else if (ret != ARRAY_SIZE(msgs)) { + ret = -EIO; + goto err_ret; + } + + for (i = 0, p = recv_buf; i < len; i++, p += sizeof(uint32_t)) + values[i] = get_unaligned_be32(p); + +err_ret: + kfree(recv_buf); + return ret; +} + +/* + * Integer array controls for setting biquad, mixer, DRC coefficients. + * According to the datasheet each coefficient is effectively 26bits, + * i.e. stored as 32bits, where bits [31:26] are ignored. + * TI's TAS57xx Graphical Development Environment tool however produces + * coefficients with more than 26 bits. For this reason we allow values + * in the full 32-bits reange. + * The coefficients are ordered as given in the TAS571x data sheet: + * b0, b1, b2, a1, a2 + */ + +static int tas571x_coefficient_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int numcoef = kcontrol->private_value >> 16; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = numcoef; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffffffff; + return 0; +} + +static int tas571x_coefficient_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct i2c_client *i2c = to_i2c_client(component->dev); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + + return tas571x_reg_read_multiword(i2c, index, + ucontrol->value.integer.value, numcoef); +} + +static int tas571x_coefficient_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct i2c_client *i2c = to_i2c_client(component->dev); + int numcoef = kcontrol->private_value >> 16; + int index = kcontrol->private_value & 0xffff; + + return tas571x_reg_write_multiword(i2c, index, + ucontrol->value.integer.value, numcoef); +} + +static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct tas571x_private *priv = snd_soc_component_get_drvdata(dai->component); + + priv->format = format; + + return 0; +} + +static int tas571x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tas571x_private *priv = snd_soc_component_get_drvdata(dai->component); + u32 val; + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + val = 0x00; + break; + case SND_SOC_DAIFMT_I2S: + val = 0x03; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = 0x06; + break; + default: + return -EINVAL; + } + + if (params_width(params) >= 24) + val += 2; + else if (params_width(params) >= 20) + val += 1; + + return regmap_update_bits(priv->regmap, TAS571X_SDI_REG, + TAS571X_SDI_FMT_MASK, val); +} + +static int tas571x_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u8 sysctl2; + int ret; + + sysctl2 = mute ? TAS571X_SYS_CTRL_2_SDN_MASK : 0; + + ret = snd_soc_component_update_bits(component, + TAS571X_SYS_CTRL_2_REG, + TAS571X_SYS_CTRL_2_SDN_MASK, + sysctl2); + usleep_range(1000, 2000); + + return ret; +} + +static int tas571x_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct tas571x_private *priv = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + if (!IS_ERR(priv->mclk)) { + ret = clk_prepare_enable(priv->mclk); + if (ret) { + dev_err(component->dev, + "Failed to enable master clock: %d\n", + ret); + return ret; + } + } + } + break; + case SND_SOC_BIAS_OFF: + if (!IS_ERR(priv->mclk)) + clk_disable_unprepare(priv->mclk); + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops tas571x_dai_ops = { + .set_fmt = tas571x_set_dai_fmt, + .hw_params = tas571x_hw_params, + .mute_stream = tas571x_mute, + .no_capture_mute = 1, +}; + + +#define BIQUAD_COEFS(xname, reg) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = tas571x_coefficient_info, \ + .get = tas571x_coefficient_get,\ + .put = tas571x_coefficient_put, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .private_value = reg | (5 << 16) } + +static const char *const tas5711_supply_names[] = { + "AVDD", + "DVDD", + "PVDD_A", + "PVDD_B", + "PVDD_C", + "PVDD_D", +}; + +static const DECLARE_TLV_DB_SCALE(tas5711_volume_tlv, -10350, 50, 1); + +static const struct snd_kcontrol_new tas5711_controls[] = { + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, + TAS571X_CH2_VOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), +}; + +static const struct regmap_range tas571x_readonly_regs_range[] = { + regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_DEV_ID_REG), +}; + +static const struct regmap_range tas571x_volatile_regs_range[] = { + regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG), + regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG), +}; + +static const struct regmap_access_table tas571x_write_regs = { + .no_ranges = tas571x_readonly_regs_range, + .n_no_ranges = ARRAY_SIZE(tas571x_readonly_regs_range), +}; + +static const struct regmap_access_table tas571x_volatile_regs = { + .yes_ranges = tas571x_volatile_regs_range, + .n_yes_ranges = ARRAY_SIZE(tas571x_volatile_regs_range), + +}; + +static const struct reg_default tas5711_reg_defaults[] = { + { 0x04, 0x05 }, + { 0x05, 0x40 }, + { 0x06, 0x00 }, + { 0x07, 0xff }, + { 0x08, 0x30 }, + { 0x09, 0x30 }, + { 0x1b, 0x82 }, +}; + +static const struct regmap_config tas5711_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5711_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5711_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas571x_volatile_regs, +}; + +static const struct tas571x_chip tas5711_chip = { + .supply_names = tas5711_supply_names, + .num_supply_names = ARRAY_SIZE(tas5711_supply_names), + .controls = tas5711_controls, + .num_controls = ARRAY_SIZE(tas5711_controls), + .regmap_config = &tas5711_regmap_config, + .vol_reg_size = 1, +}; + +static const struct regmap_range tas5707_volatile_regs_range[] = { + regmap_reg_range(TAS571X_CLK_CTRL_REG, TAS571X_ERR_STATUS_REG), + regmap_reg_range(TAS571X_OSC_TRIM_REG, TAS571X_OSC_TRIM_REG), + regmap_reg_range(TAS5707_CH1_BQ0_REG, TAS5707_CH2_BQ6_REG), +}; + +static const struct regmap_access_table tas5707_volatile_regs = { + .yes_ranges = tas5707_volatile_regs_range, + .n_yes_ranges = ARRAY_SIZE(tas5707_volatile_regs_range), + +}; + +static const DECLARE_TLV_DB_SCALE(tas5707_volume_tlv, -7900, 50, 1); + +static const char * const tas5707_volume_slew_step_txt[] = { + "256", "512", "1024", "2048", +}; + +static const unsigned int tas5707_volume_slew_step_values[] = { + 3, 0, 1, 2, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(tas5707_volume_slew_step_enum, + TAS571X_VOL_CFG_REG, 0, 0x3, + tas5707_volume_slew_step_txt, + tas5707_volume_slew_step_values); + +static const struct snd_kcontrol_new tas5707_controls[] = { + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, + 0, 0xff, 1, tas5707_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, + TAS571X_CH2_VOL_REG, + 0, 0xff, 1, tas5707_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), + + SOC_ENUM("Slew Rate Steps", tas5707_volume_slew_step_enum), + + BIQUAD_COEFS("CH1 - Biquad 0", TAS5707_CH1_BQ0_REG), + BIQUAD_COEFS("CH1 - Biquad 1", TAS5707_CH1_BQ1_REG), + BIQUAD_COEFS("CH1 - Biquad 2", TAS5707_CH1_BQ2_REG), + BIQUAD_COEFS("CH1 - Biquad 3", TAS5707_CH1_BQ3_REG), + BIQUAD_COEFS("CH1 - Biquad 4", TAS5707_CH1_BQ4_REG), + BIQUAD_COEFS("CH1 - Biquad 5", TAS5707_CH1_BQ5_REG), + BIQUAD_COEFS("CH1 - Biquad 6", TAS5707_CH1_BQ6_REG), + + BIQUAD_COEFS("CH2 - Biquad 0", TAS5707_CH2_BQ0_REG), + BIQUAD_COEFS("CH2 - Biquad 1", TAS5707_CH2_BQ1_REG), + BIQUAD_COEFS("CH2 - Biquad 2", TAS5707_CH2_BQ2_REG), + BIQUAD_COEFS("CH2 - Biquad 3", TAS5707_CH2_BQ3_REG), + BIQUAD_COEFS("CH2 - Biquad 4", TAS5707_CH2_BQ4_REG), + BIQUAD_COEFS("CH2 - Biquad 5", TAS5707_CH2_BQ5_REG), + BIQUAD_COEFS("CH2 - Biquad 6", TAS5707_CH2_BQ6_REG), +}; + +static const struct reg_default tas5707_reg_defaults[] = { + {TAS571X_CLK_CTRL_REG, 0x6c}, + {TAS571X_DEV_ID_REG, 0x70}, + {TAS571X_ERR_STATUS_REG, 0x00}, + {TAS571X_SYS_CTRL_1_REG, 0xa0}, + {TAS571X_SDI_REG, 0x05}, + {TAS571X_SYS_CTRL_2_REG, 0x40}, + {TAS571X_SOFT_MUTE_REG, 0x00}, + {TAS571X_MVOL_REG, 0xff}, + {TAS571X_CH1_VOL_REG, 0x30}, + {TAS571X_CH2_VOL_REG, 0x30}, + {TAS571X_VOL_CFG_REG, 0x91}, + {TAS571X_MODULATION_LIMIT_REG, 0x02}, + {TAS571X_IC_DELAY_CH1_REG, 0xac}, + {TAS571X_IC_DELAY_CH2_REG, 0x54}, + {TAS571X_IC_DELAY_CH3_REG, 0xac}, + {TAS571X_IC_DELAY_CH4_REG, 0x54}, + {TAS571X_START_STOP_PERIOD_REG, 0x0f}, + {TAS571X_OSC_TRIM_REG, 0x82}, + {TAS571X_BKND_ERR_REG, 0x02}, + {TAS571X_INPUT_MUX_REG, 0x17772}, + {TAS571X_PWM_MUX_REG, 0x1021345}, +}; + +static const struct regmap_config tas5707_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5707_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5707_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas5707_volatile_regs, +}; + +static const struct tas571x_chip tas5707_chip = { + .supply_names = tas5711_supply_names, + .num_supply_names = ARRAY_SIZE(tas5711_supply_names), + .controls = tas5707_controls, + .num_controls = ARRAY_SIZE(tas5707_controls), + .regmap_config = &tas5707_regmap_config, + .vol_reg_size = 1, +}; + +static const char *const tas5717_supply_names[] = { + "AVDD", + "DVDD", + "HPVDD", + "PVDD_AB", + "PVDD_CD", +}; + +static const DECLARE_TLV_DB_SCALE(tas5717_volume_tlv, -10375, 25, 0); + +static const struct snd_kcontrol_new tas5717_controls[] = { + /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */ + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, 1, 0x1ff, 1, + tas5717_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG, + 1, 0x1ff, 1, tas5717_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), + + SOC_DOUBLE_R_RANGE("CH1 Mixer Volume", + TAS5717_CH1_LEFT_CH_MIX_REG, + TAS5717_CH1_RIGHT_CH_MIX_REG, + 16, 0, 0x80, 0), + + SOC_DOUBLE_R_RANGE("CH2 Mixer Volume", + TAS5717_CH2_LEFT_CH_MIX_REG, + TAS5717_CH2_RIGHT_CH_MIX_REG, + 16, 0, 0x80, 0), + + /* + * The biquads are named according to the register names. + * Please note that TI's TAS57xx Graphical Development Environment + * tool names them different. + */ + BIQUAD_COEFS("CH1 - Biquad 0", TAS5717_CH1_BQ0_REG), + BIQUAD_COEFS("CH1 - Biquad 1", TAS5717_CH1_BQ1_REG), + BIQUAD_COEFS("CH1 - Biquad 2", TAS5717_CH1_BQ2_REG), + BIQUAD_COEFS("CH1 - Biquad 3", TAS5717_CH1_BQ3_REG), + BIQUAD_COEFS("CH1 - Biquad 4", TAS5717_CH1_BQ4_REG), + BIQUAD_COEFS("CH1 - Biquad 5", TAS5717_CH1_BQ5_REG), + BIQUAD_COEFS("CH1 - Biquad 6", TAS5717_CH1_BQ6_REG), + BIQUAD_COEFS("CH1 - Biquad 7", TAS5717_CH1_BQ7_REG), + BIQUAD_COEFS("CH1 - Biquad 8", TAS5717_CH1_BQ8_REG), + BIQUAD_COEFS("CH1 - Biquad 9", TAS5717_CH1_BQ9_REG), + BIQUAD_COEFS("CH1 - Biquad 10", TAS5717_CH1_BQ10_REG), + BIQUAD_COEFS("CH1 - Biquad 11", TAS5717_CH1_BQ11_REG), + + BIQUAD_COEFS("CH2 - Biquad 0", TAS5717_CH2_BQ0_REG), + BIQUAD_COEFS("CH2 - Biquad 1", TAS5717_CH2_BQ1_REG), + BIQUAD_COEFS("CH2 - Biquad 2", TAS5717_CH2_BQ2_REG), + BIQUAD_COEFS("CH2 - Biquad 3", TAS5717_CH2_BQ3_REG), + BIQUAD_COEFS("CH2 - Biquad 4", TAS5717_CH2_BQ4_REG), + BIQUAD_COEFS("CH2 - Biquad 5", TAS5717_CH2_BQ5_REG), + BIQUAD_COEFS("CH2 - Biquad 6", TAS5717_CH2_BQ6_REG), + BIQUAD_COEFS("CH2 - Biquad 7", TAS5717_CH2_BQ7_REG), + BIQUAD_COEFS("CH2 - Biquad 8", TAS5717_CH2_BQ8_REG), + BIQUAD_COEFS("CH2 - Biquad 9", TAS5717_CH2_BQ9_REG), + BIQUAD_COEFS("CH2 - Biquad 10", TAS5717_CH2_BQ10_REG), + BIQUAD_COEFS("CH2 - Biquad 11", TAS5717_CH2_BQ11_REG), + + BIQUAD_COEFS("CH3 - Biquad 0", TAS5717_CH3_BQ0_REG), + BIQUAD_COEFS("CH3 - Biquad 1", TAS5717_CH3_BQ1_REG), + + BIQUAD_COEFS("CH4 - Biquad 0", TAS5717_CH4_BQ0_REG), + BIQUAD_COEFS("CH4 - Biquad 1", TAS5717_CH4_BQ1_REG), +}; + +static const struct reg_default tas5717_reg_defaults[] = { + { 0x04, 0x05 }, + { 0x05, 0x40 }, + { 0x06, 0x00 }, + { 0x07, 0x03ff }, + { 0x08, 0x00c0 }, + { 0x09, 0x00c0 }, + { 0x1b, 0x82 }, + { TAS5717_CH1_RIGHT_CH_MIX_REG, 0x0 }, + { TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000}, + { TAS5717_CH2_LEFT_CH_MIX_REG, 0x0 }, + { TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000}, +}; + +static const struct regmap_config tas5717_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5717_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5717_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas571x_volatile_regs, +}; + +/* This entry is reused for tas5719 as the software interface is identical. */ +static const struct tas571x_chip tas5717_chip = { + .supply_names = tas5717_supply_names, + .num_supply_names = ARRAY_SIZE(tas5717_supply_names), + .controls = tas5717_controls, + .num_controls = ARRAY_SIZE(tas5717_controls), + .regmap_config = &tas5717_regmap_config, + .vol_reg_size = 2, +}; + +static const char *const tas5721_supply_names[] = { + "AVDD", + "DVDD", + "DRVDD", + "PVDD", +}; + +static const struct snd_kcontrol_new tas5721_controls[] = { + SOC_SINGLE_TLV("Master Volume", + TAS571X_MVOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE_R_TLV("Speaker Volume", + TAS571X_CH1_VOL_REG, + TAS571X_CH2_VOL_REG, + 0, 0xff, 1, tas5711_volume_tlv), + SOC_DOUBLE("Speaker Switch", + TAS571X_SOFT_MUTE_REG, + TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT, + 1, 1), +}; + +static const struct reg_default tas5721_reg_defaults[] = { + {TAS571X_CLK_CTRL_REG, 0x6c}, + {TAS571X_DEV_ID_REG, 0x00}, + {TAS571X_ERR_STATUS_REG, 0x00}, + {TAS571X_SYS_CTRL_1_REG, 0xa0}, + {TAS571X_SDI_REG, 0x05}, + {TAS571X_SYS_CTRL_2_REG, 0x40}, + {TAS571X_SOFT_MUTE_REG, 0x00}, + {TAS571X_MVOL_REG, 0xff}, + {TAS571X_CH1_VOL_REG, 0x30}, + {TAS571X_CH2_VOL_REG, 0x30}, + {TAS571X_CH3_VOL_REG, 0x30}, + {TAS571X_VOL_CFG_REG, 0x91}, + {TAS571X_MODULATION_LIMIT_REG, 0x02}, + {TAS571X_IC_DELAY_CH1_REG, 0xac}, + {TAS571X_IC_DELAY_CH2_REG, 0x54}, + {TAS571X_IC_DELAY_CH3_REG, 0xac}, + {TAS571X_IC_DELAY_CH4_REG, 0x54}, + {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30}, + {TAS571X_START_STOP_PERIOD_REG, 0x0f}, + {TAS571X_OSC_TRIM_REG, 0x82}, + {TAS571X_BKND_ERR_REG, 0x02}, + {TAS571X_INPUT_MUX_REG, 0x17772}, + {TAS571X_CH4_SRC_SELECT_REG, 0x4303}, + {TAS571X_PWM_MUX_REG, 0x1021345}, +}; + +static const struct regmap_config tas5721_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5721_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5721_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas571x_volatile_regs, +}; + +static const char *const tas5733_supply_names[] = { + "AVDD", + "DVDD", + "PVDD", +}; + +static const struct reg_default tas5733_reg_defaults[] = { + {TAS571X_CLK_CTRL_REG, 0x6c}, + {TAS571X_DEV_ID_REG, 0x00}, + {TAS571X_ERR_STATUS_REG, 0x00}, + {TAS571X_SYS_CTRL_1_REG, 0xa0}, + {TAS571X_SDI_REG, 0x05}, + {TAS571X_SYS_CTRL_2_REG, 0x40}, + {TAS571X_SOFT_MUTE_REG, 0x07}, + {TAS571X_MVOL_REG, 0x03ff}, + {TAS571X_CH1_VOL_REG, 0x00c0}, + {TAS571X_CH2_VOL_REG, 0x00c0}, + {TAS571X_CH3_VOL_REG, 0x00c0}, + {TAS571X_VOL_CFG_REG, 0xf0}, + {TAS571X_MODULATION_LIMIT_REG, 0x07}, + {TAS571X_IC_DELAY_CH1_REG, 0xb8}, + {TAS571X_IC_DELAY_CH2_REG, 0x60}, + {TAS571X_IC_DELAY_CH3_REG, 0xa0}, + {TAS571X_IC_DELAY_CH4_REG, 0x48}, + {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30}, + {TAS571X_START_STOP_PERIOD_REG, 0x68}, + {TAS571X_OSC_TRIM_REG, 0x82}, + {TAS571X_BKND_ERR_REG, 0x02}, + {TAS571X_INPUT_MUX_REG, 0x00897772}, + {TAS571X_PWM_MUX_REG, 0x01021345}, + {TAS5717_CH1_RIGHT_CH_MIX_REG, 0x00}, + {TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000}, + {TAS5717_CH2_LEFT_CH_MIX_REG, 0x00}, + {TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000}, +}; + +static const struct regmap_config tas5733_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .max_register = 0xff, + .reg_read = tas571x_reg_read, + .reg_write = tas571x_reg_write, + .reg_defaults = tas5733_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas5733_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .wr_table = &tas571x_write_regs, + .volatile_table = &tas571x_volatile_regs, +}; + +static const struct tas571x_chip tas5733_chip = { + .supply_names = tas5733_supply_names, + .num_supply_names = ARRAY_SIZE(tas5733_supply_names), + .controls = tas5717_controls, + .num_controls = ARRAY_SIZE(tas5717_controls), + .regmap_config = &tas5733_regmap_config, + .vol_reg_size = 2, +}; + +static const struct tas571x_chip tas5721_chip = { + .supply_names = tas5721_supply_names, + .num_supply_names = ARRAY_SIZE(tas5721_supply_names), + .controls = tas5721_controls, + .num_controls = ARRAY_SIZE(tas5721_controls), + .regmap_config = &tas5721_regmap_config, + .vol_reg_size = 1, +}; + +static const struct snd_soc_dapm_widget tas571x_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("OUT_A"), + SND_SOC_DAPM_OUTPUT("OUT_B"), + SND_SOC_DAPM_OUTPUT("OUT_C"), + SND_SOC_DAPM_OUTPUT("OUT_D"), +}; + +static const struct snd_soc_dapm_route tas571x_dapm_routes[] = { + { "DACL", NULL, "Playback" }, + { "DACR", NULL, "Playback" }, + + { "OUT_A", NULL, "DACL" }, + { "OUT_B", NULL, "DACL" }, + { "OUT_C", NULL, "DACR" }, + { "OUT_D", NULL, "DACR" }, +}; + +static const struct snd_soc_component_driver tas571x_component = { + .set_bias_level = tas571x_set_bias_level, + .dapm_widgets = tas571x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas571x_dapm_widgets), + .dapm_routes = tas571x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tas571x_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct snd_soc_dai_driver tas571x_dai = { + .name = "tas571x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tas571x_dai_ops, +}; + +static const struct of_device_id tas571x_of_match[] __maybe_unused; +static const struct i2c_device_id tas571x_i2c_id[]; + +static int tas571x_i2c_probe(struct i2c_client *client) +{ + struct tas571x_private *priv; + struct device *dev = &client->dev; + const struct of_device_id *of_id; + int i, ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + i2c_set_clientdata(client, priv); + + of_id = of_match_device(tas571x_of_match, dev); + if (of_id) + priv->chip = of_id->data; + else { + const struct i2c_device_id *id = + i2c_match_id(tas571x_i2c_id, client); + priv->chip = (void *) id->driver_data; + } + + priv->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(priv->mclk) && PTR_ERR(priv->mclk) != -ENOENT) { + dev_err(dev, "Failed to request mclk: %ld\n", + PTR_ERR(priv->mclk)); + return PTR_ERR(priv->mclk); + } + + if (WARN_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES)) + return -EINVAL; + for (i = 0; i < priv->chip->num_supply_names; i++) + priv->supplies[i].supply = priv->chip->supply_names[i]; + + ret = devm_regulator_bulk_get(dev, priv->chip->num_supply_names, + priv->supplies); + if (ret) { + dev_err(dev, "Failed to get supplies: %d\n", ret); + return ret; + } + ret = regulator_bulk_enable(priv->chip->num_supply_names, + priv->supplies); + if (ret) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + priv->regmap = devm_regmap_init(dev, NULL, client, + priv->chip->regmap_config); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + goto disable_regs; + } + + priv->pdn_gpio = devm_gpiod_get_optional(dev, "pdn", GPIOD_OUT_LOW); + if (IS_ERR(priv->pdn_gpio)) { + dev_err(dev, "error requesting pdn_gpio: %ld\n", + PTR_ERR(priv->pdn_gpio)); + ret = PTR_ERR(priv->pdn_gpio); + goto disable_regs; + } + + priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset_gpio)) { + dev_err(dev, "error requesting reset_gpio: %ld\n", + PTR_ERR(priv->reset_gpio)); + ret = PTR_ERR(priv->reset_gpio); + goto disable_regs; + } else if (priv->reset_gpio) { + /* pulse the active low reset line for ~100us */ + usleep_range(100, 200); + gpiod_set_value(priv->reset_gpio, 0); + usleep_range(13500, 20000); + } + + ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0); + if (ret) + goto disable_regs; + + usleep_range(50000, 60000); + + memcpy(&priv->component_driver, &tas571x_component, sizeof(priv->component_driver)); + priv->component_driver.controls = priv->chip->controls; + priv->component_driver.num_controls = priv->chip->num_controls; + + if (priv->chip->vol_reg_size == 2) { + /* + * The master volume defaults to 0x3ff (mute), but we ignore + * (zero) the LSB because the hardware step size is 0.125 dB + * and TLV_DB_SCALE_ITEM has a resolution of 0.01 dB. + */ + ret = regmap_update_bits(priv->regmap, TAS571X_MVOL_REG, 1, 0); + if (ret) + goto disable_regs; + } + + ret = devm_snd_soc_register_component(&client->dev, + &priv->component_driver, + &tas571x_dai, 1); + if (ret) + goto disable_regs; + + return ret; + +disable_regs: + regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); + return ret; +} + +static void tas571x_i2c_remove(struct i2c_client *client) +{ + struct tas571x_private *priv = i2c_get_clientdata(client); + + regulator_bulk_disable(priv->chip->num_supply_names, priv->supplies); +} + +static const struct of_device_id tas571x_of_match[] __maybe_unused = { + { .compatible = "ti,tas5707", .data = &tas5707_chip, }, + { .compatible = "ti,tas5711", .data = &tas5711_chip, }, + { .compatible = "ti,tas5717", .data = &tas5717_chip, }, + { .compatible = "ti,tas5719", .data = &tas5717_chip, }, + { .compatible = "ti,tas5721", .data = &tas5721_chip, }, + { .compatible = "ti,tas5733", .data = &tas5733_chip, }, + { } +}; +MODULE_DEVICE_TABLE(of, tas571x_of_match); + +static const struct i2c_device_id tas571x_i2c_id[] = { + { "tas5707", (kernel_ulong_t) &tas5707_chip }, + { "tas5711", (kernel_ulong_t) &tas5711_chip }, + { "tas5717", (kernel_ulong_t) &tas5717_chip }, + { "tas5719", (kernel_ulong_t) &tas5717_chip }, + { "tas5721", (kernel_ulong_t) &tas5721_chip }, + { "tas5733", (kernel_ulong_t) &tas5733_chip }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); + +static struct i2c_driver tas571x_i2c_driver = { + .driver = { + .name = "tas571x", + .of_match_table = of_match_ptr(tas571x_of_match), + }, + .probe = tas571x_i2c_probe, + .remove = tas571x_i2c_remove, + .id_table = tas571x_i2c_id, +}; +module_i2c_driver(tas571x_i2c_driver); + +MODULE_DESCRIPTION("ASoC TAS571x driver"); +MODULE_AUTHOR("Kevin Cernekee "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h new file mode 100644 index 0000000000..5340d3bec3 --- /dev/null +++ b/sound/soc/codecs/tas571x.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * TAS571x amplifier audio driver + * + * Copyright (C) 2015 Google, Inc. + */ + +#ifndef _TAS571X_H +#define _TAS571X_H + +/* device registers */ +#define TAS571X_CLK_CTRL_REG 0x00 +#define TAS571X_DEV_ID_REG 0x01 +#define TAS571X_ERR_STATUS_REG 0x02 +#define TAS571X_SYS_CTRL_1_REG 0x03 +#define TAS571X_SDI_REG 0x04 +#define TAS571X_SDI_FMT_MASK 0x0f + +#define TAS571X_SYS_CTRL_2_REG 0x05 +#define TAS571X_SYS_CTRL_2_SDN_MASK 0x40 + +#define TAS571X_SOFT_MUTE_REG 0x06 +#define TAS571X_SOFT_MUTE_CH1_SHIFT 0 +#define TAS571X_SOFT_MUTE_CH2_SHIFT 1 +#define TAS571X_SOFT_MUTE_CH3_SHIFT 2 + +#define TAS571X_MVOL_REG 0x07 +#define TAS571X_CH1_VOL_REG 0x08 +#define TAS571X_CH2_VOL_REG 0x09 +#define TAS571X_CH3_VOL_REG 0x0a +#define TAS571X_VOL_CFG_REG 0x0e +#define TAS571X_MODULATION_LIMIT_REG 0x10 +#define TAS571X_IC_DELAY_CH1_REG 0x11 +#define TAS571X_IC_DELAY_CH2_REG 0x12 +#define TAS571X_IC_DELAY_CH3_REG 0x13 +#define TAS571X_IC_DELAY_CH4_REG 0x14 + +#define TAS571X_PWM_CH_SDN_GROUP_REG 0x19 /* N/A on TAS5717, TAS5719 */ +#define TAS571X_PWM_CH1_SDN_MASK (1<<0) +#define TAS571X_PWM_CH2_SDN_SHIFT (1<<1) +#define TAS571X_PWM_CH3_SDN_SHIFT (1<<2) +#define TAS571X_PWM_CH4_SDN_SHIFT (1<<3) + +#define TAS571X_START_STOP_PERIOD_REG 0x1a +#define TAS571X_OSC_TRIM_REG 0x1b +#define TAS571X_BKND_ERR_REG 0x1c +#define TAS571X_INPUT_MUX_REG 0x20 +#define TAS571X_CH4_SRC_SELECT_REG 0x21 +#define TAS571X_PWM_MUX_REG 0x25 + +/* 20-byte biquad registers */ +#define TAS5707_CH1_BQ0_REG 0x29 +#define TAS5707_CH1_BQ1_REG 0x2a +#define TAS5707_CH1_BQ2_REG 0x2b +#define TAS5707_CH1_BQ3_REG 0x2c +#define TAS5707_CH1_BQ4_REG 0x2d +#define TAS5707_CH1_BQ5_REG 0x2e +#define TAS5707_CH1_BQ6_REG 0x2f + +#define TAS5707_CH2_BQ0_REG 0x30 +#define TAS5707_CH2_BQ1_REG 0x31 +#define TAS5707_CH2_BQ2_REG 0x32 +#define TAS5707_CH2_BQ3_REG 0x33 +#define TAS5707_CH2_BQ4_REG 0x34 +#define TAS5707_CH2_BQ5_REG 0x35 +#define TAS5707_CH2_BQ6_REG 0x36 + +#define TAS5717_CH1_BQ0_REG 0x26 +#define TAS5717_CH1_BQ1_REG 0x27 +#define TAS5717_CH1_BQ2_REG 0x28 +#define TAS5717_CH1_BQ3_REG 0x29 +#define TAS5717_CH1_BQ4_REG 0x2a +#define TAS5717_CH1_BQ5_REG 0x2b +#define TAS5717_CH1_BQ6_REG 0x2c +#define TAS5717_CH1_BQ7_REG 0x2d +#define TAS5717_CH1_BQ8_REG 0x2e +#define TAS5717_CH1_BQ9_REG 0x2f + +#define TAS5717_CH2_BQ0_REG 0x30 +#define TAS5717_CH2_BQ1_REG 0x31 +#define TAS5717_CH2_BQ2_REG 0x32 +#define TAS5717_CH2_BQ3_REG 0x33 +#define TAS5717_CH2_BQ4_REG 0x34 +#define TAS5717_CH2_BQ5_REG 0x35 +#define TAS5717_CH2_BQ6_REG 0x36 +#define TAS5717_CH2_BQ7_REG 0x37 +#define TAS5717_CH2_BQ8_REG 0x38 +#define TAS5717_CH2_BQ9_REG 0x39 + +#define TAS5717_CH1_BQ10_REG 0x58 +#define TAS5717_CH1_BQ11_REG 0x59 + +#define TAS5717_CH4_BQ0_REG 0x5a +#define TAS5717_CH4_BQ1_REG 0x5b + +#define TAS5717_CH2_BQ10_REG 0x5c +#define TAS5717_CH2_BQ11_REG 0x5d + +#define TAS5717_CH3_BQ0_REG 0x5e +#define TAS5717_CH3_BQ1_REG 0x5f + +#define TAS5717_CH1_RIGHT_CH_MIX_REG 0x72 +#define TAS5717_CH1_LEFT_CH_MIX_REG 0x73 +#define TAS5717_CH2_LEFT_CH_MIX_REG 0x76 +#define TAS5717_CH2_RIGHT_CH_MIX_REG 0x77 + +#endif /* _TAS571X_H */ diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c new file mode 100644 index 0000000000..6dd6c0896e --- /dev/null +++ b/sound/soc/codecs/tas5720.c @@ -0,0 +1,831 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * tas5720.c - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier + * + * Copyright (C)2015-2016 Texas Instruments Incorporated - https://www.ti.com + * + * Author: Andreas Dannenberg + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tas5720.h" + +/* Define how often to check (and clear) the fault status register (in ms) */ +#define TAS5720_FAULT_CHECK_INTERVAL 200 + +enum tas572x_type { + TAS5720, + TAS5720A_Q1, + TAS5722, +}; + +static const char * const tas5720_supply_names[] = { + "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ + "pvdd", /* Class-D amp and analog power supply (connected). */ +}; + +#define TAS5720_NUM_SUPPLIES ARRAY_SIZE(tas5720_supply_names) + +struct tas5720_data { + struct snd_soc_component *component; + struct regmap *regmap; + struct i2c_client *tas5720_client; + enum tas572x_type devtype; + struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES]; + struct delayed_work fault_check_work; + unsigned int last_fault; +}; + +static int tas5720_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned int rate = params_rate(params); + bool ssz_ds; + int ret; + + switch (rate) { + case 44100: + case 48000: + ssz_ds = false; + break; + case 88200: + case 96000: + ssz_ds = true; + break; + default: + dev_err(component->dev, "unsupported sample rate: %u\n", rate); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG, + TAS5720_SSZ_DS, ssz_ds); + if (ret < 0) { + dev_err(component->dev, "error setting sample rate: %d\n", ret); + return ret; + } + + return 0; +} + +static int tas5720_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u8 serial_format; + int ret; + + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) { + dev_vdbg(component->dev, "DAI clocking invalid\n"); + return -EINVAL; + } + + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | + SND_SOC_DAIFMT_INV_MASK)) { + case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): + /* 1st data bit occur one BCLK cycle after the frame sync */ + serial_format = TAS5720_SAIF_I2S; + break; + case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF): + /* + * Note that although the TAS5720 does not have a dedicated DSP + * mode it doesn't care about the LRCLK duty cycle during TDM + * operation. Therefore we can use the device's I2S mode with + * its delaying of the 1st data bit to receive DSP_A formatted + * data. See device datasheet for additional details. + */ + serial_format = TAS5720_SAIF_I2S; + break; + case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF): + /* + * Similar to DSP_A, we can use the fact that the TAS5720 does + * not care about the LRCLK duty cycle during TDM to receive + * DSP_B formatted data in LEFTJ mode (no delaying of the 1st + * data bit). + */ + serial_format = TAS5720_SAIF_LEFTJ; + break; + case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): + /* No delay after the frame sync */ + serial_format = TAS5720_SAIF_LEFTJ; + break; + default: + dev_vdbg(component->dev, "DAI Format is not found\n"); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG, + TAS5720_SAIF_FORMAT_MASK, + serial_format); + if (ret < 0) { + dev_err(component->dev, "error setting SAIF format: %d\n", ret); + return ret; + } + + return 0; +} + +static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); + unsigned int first_slot; + int ret; + + if (!tx_mask) { + dev_err(component->dev, "tx masks must not be 0\n"); + return -EINVAL; + } + + /* + * Determine the first slot that is being requested. We will only + * use the first slot that is found since the TAS5720 is a mono + * amplifier. + */ + first_slot = __ffs(tx_mask); + + if (first_slot > 7) { + dev_err(component->dev, "slot selection out of bounds (%u)\n", + first_slot); + return -EINVAL; + } + + /* + * Enable manual TDM slot selection (instead of I2C ID based). + * This is not applicable to TAS5720A-Q1. + */ + switch (tas5720->devtype) { + case TAS5720A_Q1: + break; + default: + ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG, + TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC); + if (ret < 0) + goto error_snd_soc_component_update_bits; + + /* Configure the TDM slot to process audio from */ + ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG, + TAS5720_TDM_SLOT_SEL_MASK, first_slot); + if (ret < 0) + goto error_snd_soc_component_update_bits; + break; + } + + /* Configure TDM slot width. This is only applicable to TAS5722. */ + switch (tas5720->devtype) { + case TAS5722: + ret = snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG, + TAS5722_TDM_SLOT_16B, + slot_width == 16 ? + TAS5722_TDM_SLOT_16B : 0); + if (ret < 0) + goto error_snd_soc_component_update_bits; + break; + default: + break; + } + + return 0; + +error_snd_soc_component_update_bits: + dev_err(component->dev, "error configuring TDM mode: %d\n", ret); + return ret; +} + +static int tas5720_mute_soc_component(struct snd_soc_component *component, int mute) +{ + struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); + unsigned int reg, mask; + int ret; + + switch (tas5720->devtype) { + case TAS5720A_Q1: + reg = TAS5720_Q1_VOLUME_CTRL_CFG_REG; + mask = TAS5720_Q1_MUTE; + break; + default: + reg = TAS5720_DIGITAL_CTRL2_REG; + mask = TAS5720_MUTE; + break; + } + + ret = snd_soc_component_update_bits(component, reg, mask, mute ? mask : 0); + if (ret < 0) { + dev_err(component->dev, "error (un-)muting device: %d\n", ret); + return ret; + } + + return 0; +} + +static int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + return tas5720_mute_soc_component(dai->component, mute); +} + +static void tas5720_fault_check_work(struct work_struct *work) +{ + struct tas5720_data *tas5720 = container_of(work, struct tas5720_data, + fault_check_work.work); + struct device *dev = tas5720->component->dev; + unsigned int curr_fault; + int ret; + + ret = regmap_read(tas5720->regmap, TAS5720_FAULT_REG, &curr_fault); + if (ret < 0) { + dev_err(dev, "failed to read FAULT register: %d\n", ret); + goto out; + } + + /* Check/handle all errors except SAIF clock errors */ + curr_fault &= TAS5720_OCE | TAS5720_DCE | TAS5720_OTE; + + /* + * Only flag errors once for a given occurrence. This is needed as + * the TAS5720 will take time clearing the fault condition internally + * during which we don't want to bombard the system with the same + * error message over and over. + */ + if ((curr_fault & TAS5720_OCE) && !(tas5720->last_fault & TAS5720_OCE)) + dev_crit(dev, "experienced an over current hardware fault\n"); + + if ((curr_fault & TAS5720_DCE) && !(tas5720->last_fault & TAS5720_DCE)) + dev_crit(dev, "experienced a DC detection fault\n"); + + if ((curr_fault & TAS5720_OTE) && !(tas5720->last_fault & TAS5720_OTE)) + dev_crit(dev, "experienced an over temperature fault\n"); + + /* Store current fault value so we can detect any changes next time */ + tas5720->last_fault = curr_fault; + + if (!curr_fault) + goto out; + + /* + * Periodically toggle SDZ (shutdown bit) H->L->H to clear any latching + * faults as long as a fault condition persists. Always going through + * the full sequence no matter the first return value to minimizes + * chances for the device to end up in shutdown mode. + */ + ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG, + TAS5720_SDZ, 0); + if (ret < 0) + dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret); + + ret = regmap_write_bits(tas5720->regmap, TAS5720_POWER_CTRL_REG, + TAS5720_SDZ, TAS5720_SDZ); + if (ret < 0) + dev_err(dev, "failed to write POWER_CTRL register: %d\n", ret); + +out: + /* Schedule the next fault check at the specified interval */ + schedule_delayed_work(&tas5720->fault_check_work, + msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL)); +} + +static int tas5720_codec_probe(struct snd_soc_component *component) +{ + struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); + unsigned int device_id, expected_device_id; + int ret; + + tas5720->component = component; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies), + tas5720->supplies); + if (ret != 0) { + dev_err(component->dev, "failed to enable supplies: %d\n", ret); + return ret; + } + + /* + * Take a liberal approach to checking the device ID to allow the + * driver to be used even if the device ID does not match, however + * issue a warning if there is a mismatch. + */ + ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id); + if (ret < 0) { + dev_err(component->dev, "failed to read device ID register: %d\n", + ret); + goto probe_fail; + } + + switch (tas5720->devtype) { + case TAS5720: + expected_device_id = TAS5720_DEVICE_ID; + break; + case TAS5720A_Q1: + expected_device_id = TAS5720A_Q1_DEVICE_ID; + break; + case TAS5722: + expected_device_id = TAS5722_DEVICE_ID; + break; + default: + dev_err(component->dev, "unexpected private driver data\n"); + ret = -EINVAL; + goto probe_fail; + } + + if (device_id != expected_device_id) + dev_warn(component->dev, "wrong device ID. expected: %u read: %u\n", + expected_device_id, device_id); + + /* Set device to mute */ + ret = tas5720_mute_soc_component(component, 1); + if (ret < 0) + goto error_snd_soc_component_update_bits; + + /* Set Bit 7 in TAS5720_ANALOG_CTRL_REG to 1 for TAS5720A_Q1 */ + switch (tas5720->devtype) { + case TAS5720A_Q1: + ret = snd_soc_component_update_bits(component, TAS5720_ANALOG_CTRL_REG, + TAS5720_Q1_RESERVED7_BIT, + TAS5720_Q1_RESERVED7_BIT); + break; + default: + break; + } + if (ret < 0) + goto error_snd_soc_component_update_bits; + + /* + * Enter shutdown mode - our default when not playing audio - to + * minimize current consumption. On the TAS5720 there is no real down + * side doing so as all device registers are preserved and the wakeup + * of the codec is rather quick which we do using a dapm widget. + */ + ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG, + TAS5720_SDZ, 0); + if (ret < 0) + goto error_snd_soc_component_update_bits; + + INIT_DELAYED_WORK(&tas5720->fault_check_work, tas5720_fault_check_work); + + return 0; + +error_snd_soc_component_update_bits: + dev_err(component->dev, "error configuring device registers: %d\n", ret); + +probe_fail: + regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), + tas5720->supplies); + return ret; +} + +static void tas5720_codec_remove(struct snd_soc_component *component) +{ + struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); + int ret; + + cancel_delayed_work_sync(&tas5720->fault_check_work); + + ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), + tas5720->supplies); + if (ret < 0) + dev_err(component->dev, "failed to disable supplies: %d\n", ret); +}; + +static int tas5720_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); + int ret; + + if (event & SND_SOC_DAPM_POST_PMU) { + /* Take TAS5720 out of shutdown mode */ + ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG, + TAS5720_SDZ, TAS5720_SDZ); + if (ret < 0) { + dev_err(component->dev, "error waking component: %d\n", ret); + return ret; + } + + /* + * Observe codec shutdown-to-active time. The datasheet only + * lists a nominal value however just use-it as-is without + * additional padding to minimize the delay introduced in + * starting to play audio (actually there is other setup done + * by the ASoC framework that will provide additional delays, + * so we should always be safe). + */ + msleep(25); + + /* Turn on TAS5720 periodic fault checking/handling */ + tas5720->last_fault = 0; + schedule_delayed_work(&tas5720->fault_check_work, + msecs_to_jiffies(TAS5720_FAULT_CHECK_INTERVAL)); + } else if (event & SND_SOC_DAPM_PRE_PMD) { + /* Disable TAS5720 periodic fault checking/handling */ + cancel_delayed_work_sync(&tas5720->fault_check_work); + + /* Place TAS5720 in shutdown mode to minimize current draw */ + ret = snd_soc_component_update_bits(component, TAS5720_POWER_CTRL_REG, + TAS5720_SDZ, 0); + if (ret < 0) { + dev_err(component->dev, "error shutting down component: %d\n", + ret); + return ret; + } + } + + return 0; +} + +#ifdef CONFIG_PM +static int tas5720_suspend(struct snd_soc_component *component) +{ + struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); + int ret; + + regcache_cache_only(tas5720->regmap, true); + regcache_mark_dirty(tas5720->regmap); + + ret = regulator_bulk_disable(ARRAY_SIZE(tas5720->supplies), + tas5720->supplies); + if (ret < 0) + dev_err(component->dev, "failed to disable supplies: %d\n", ret); + + return ret; +} + +static int tas5720_resume(struct snd_soc_component *component) +{ + struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas5720->supplies), + tas5720->supplies); + if (ret < 0) { + dev_err(component->dev, "failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_cache_only(tas5720->regmap, false); + + ret = regcache_sync(tas5720->regmap); + if (ret < 0) { + dev_err(component->dev, "failed to sync regcache: %d\n", ret); + return ret; + } + + return 0; +} +#else +#define tas5720_suspend NULL +#define tas5720_resume NULL +#endif + +static bool tas5720_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS5720_DEVICE_ID_REG: + case TAS5720_FAULT_REG: + return true; + default: + return false; + } +} + +static const struct regmap_config tas5720_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = TAS5720_MAX_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = tas5720_is_volatile_reg, +}; + +static const struct regmap_config tas5720a_q1_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = TAS5720_MAX_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = tas5720_is_volatile_reg, +}; + +static const struct regmap_config tas5722_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = TAS5722_MAX_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = tas5720_is_volatile_reg, +}; + +/* + * DAC analog gain. There are four discrete values to select from, ranging + * from 19.2 dB to 26.3dB. + */ +static const DECLARE_TLV_DB_RANGE(dac_analog_tlv, + 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0), + 0x1, 0x1, TLV_DB_SCALE_ITEM(2070, 0, 0), + 0x2, 0x2, TLV_DB_SCALE_ITEM(2350, 0, 0), + 0x3, 0x3, TLV_DB_SCALE_ITEM(2630, 0, 0), +); + +/* + * DAC analog gain for TAS5720A-Q1. There are three discrete values to select from, ranging + * from 19.2 dB to 25.0dB. + */ +static const DECLARE_TLV_DB_RANGE(dac_analog_tlv_a_q1, + 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0), + 0x1, 0x1, TLV_DB_SCALE_ITEM(2260, 0, 0), + 0x2, 0x2, TLV_DB_SCALE_ITEM(2500, 0, 0), +); + +/* + * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps + * depending on the device. Note that setting the gain below -100 dB + * (register value <0x7) is effectively a MUTE as per device datasheet. + * + * Note that for the TAS5722 the digital volume controls are actually split + * over two registers, so we need custom getters/setters for access. + */ +static DECLARE_TLV_DB_SCALE(tas5720_dac_tlv, -10350, 50, 0); +static DECLARE_TLV_DB_SCALE(tas5722_dac_tlv, -10350, 25, 0); + +static int tas5722_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int val; + + val = snd_soc_component_read(component, TAS5720_VOLUME_CTRL_REG); + ucontrol->value.integer.value[0] = val << 1; + + val = snd_soc_component_read(component, TAS5722_DIGITAL_CTRL2_REG); + ucontrol->value.integer.value[0] |= val & TAS5722_VOL_CONTROL_LSB; + + return 0; +} + +static int tas5722_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int sel = ucontrol->value.integer.value[0]; + + snd_soc_component_write(component, TAS5720_VOLUME_CTRL_REG, sel >> 1); + snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG, + TAS5722_VOL_CONTROL_LSB, sel); + + return 0; +} + +static const struct snd_kcontrol_new tas5720_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Driver Playback Volume", + TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, tas5720_dac_tlv), + SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, + TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), +}; + +static const struct snd_kcontrol_new tas5720a_q1_snd_controls[] = { + SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume", + TAS5720_Q1_VOLUME_CTRL_LEFT_REG, + TAS5720_Q1_VOLUME_CTRL_RIGHT_REG, + 0, 0xff, 0, tas5720_dac_tlv), + SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, + TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv_a_q1), +}; + +static const struct snd_kcontrol_new tas5722_snd_controls[] = { + SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume", + 0, 0, 511, 0, + tas5722_volume_get, tas5722_volume_set, + tas5722_dac_tlv), + SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, + TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), +}; + +static const struct snd_soc_dapm_widget tas5720_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas5720_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT") +}; + +static const struct snd_soc_dapm_route tas5720_audio_map[] = { + { "DAC", NULL, "DAC IN" }, + { "OUT", NULL, "DAC" }, +}; + +static const struct snd_soc_component_driver soc_component_dev_tas5720 = { + .probe = tas5720_codec_probe, + .remove = tas5720_codec_remove, + .suspend = tas5720_suspend, + .resume = tas5720_resume, + .controls = tas5720_snd_controls, + .num_controls = ARRAY_SIZE(tas5720_snd_controls), + .dapm_widgets = tas5720_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), + .dapm_routes = tas5720_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_component_driver soc_component_dev_tas5720_a_q1 = { + .probe = tas5720_codec_probe, + .remove = tas5720_codec_remove, + .suspend = tas5720_suspend, + .resume = tas5720_resume, + .controls = tas5720a_q1_snd_controls, + .num_controls = ARRAY_SIZE(tas5720a_q1_snd_controls), + .dapm_widgets = tas5720_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), + .dapm_routes = tas5720_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_component_driver soc_component_dev_tas5722 = { + .probe = tas5720_codec_probe, + .remove = tas5720_codec_remove, + .suspend = tas5720_suspend, + .resume = tas5720_resume, + .controls = tas5722_snd_controls, + .num_controls = ARRAY_SIZE(tas5722_snd_controls), + .dapm_widgets = tas5720_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), + .dapm_routes = tas5720_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +/* PCM rates supported by the TAS5720 driver */ +#define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +/* Formats supported by TAS5720 driver */ +#define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops tas5720_speaker_dai_ops = { + .hw_params = tas5720_hw_params, + .set_fmt = tas5720_set_dai_fmt, + .set_tdm_slot = tas5720_set_dai_tdm_slot, + .mute_stream = tas5720_mute, + .no_capture_mute = 1, +}; + +/* + * TAS5720 DAI structure + * + * Note that were are advertising .playback.channels_max = 2 despite this being + * a mono amplifier. The reason for that is that some serial ports such as TI's + * McASP module have a minimum number of channels (2) that they can output. + * Advertising more channels than we have will allow us to interface with such + * a serial port without really any negative side effects as the TAS5720 will + * simply ignore any extra channel(s) asides from the one channel that is + * configured to be played back. + */ +static struct snd_soc_dai_driver tas5720_dai[] = { + { + .name = "tas5720-amplifier", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAS5720_RATES, + .formats = TAS5720_FORMATS, + }, + .ops = &tas5720_speaker_dai_ops, + }, +}; + +static const struct i2c_device_id tas5720_id[] = { + { "tas5720", TAS5720 }, + { "tas5720a-q1", TAS5720A_Q1 }, + { "tas5722", TAS5722 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas5720_id); + +static int tas5720_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct tas5720_data *data; + const struct regmap_config *regmap_config; + const struct i2c_device_id *id; + int ret; + int i; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + id = i2c_match_id(tas5720_id, client); + data->tas5720_client = client; + data->devtype = id->driver_data; + + switch (id->driver_data) { + case TAS5720: + regmap_config = &tas5720_regmap_config; + break; + case TAS5720A_Q1: + regmap_config = &tas5720a_q1_regmap_config; + break; + case TAS5722: + regmap_config = &tas5722_regmap_config; + break; + default: + dev_err(dev, "unexpected private driver data\n"); + return -EINVAL; + } + data->regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + dev_err(dev, "failed to allocate register map: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(data->supplies); i++) + data->supplies[i].supply = tas5720_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies), + data->supplies); + if (ret != 0) { + dev_err(dev, "failed to request supplies: %d\n", ret); + return ret; + } + + dev_set_drvdata(dev, data); + + switch (id->driver_data) { + case TAS5720: + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_tas5720, + tas5720_dai, + ARRAY_SIZE(tas5720_dai)); + break; + case TAS5720A_Q1: + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_tas5720_a_q1, + tas5720_dai, + ARRAY_SIZE(tas5720_dai)); + break; + case TAS5722: + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_tas5722, + tas5720_dai, + ARRAY_SIZE(tas5720_dai)); + break; + default: + dev_err(dev, "unexpected private driver data\n"); + return -EINVAL; + } + if (ret < 0) { + dev_err(dev, "failed to register component: %d\n", ret); + return ret; + } + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tas5720_of_match[] = { + { .compatible = "ti,tas5720", }, + { .compatible = "ti,tas5720a-q1", }, + { .compatible = "ti,tas5722", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tas5720_of_match); +#endif + +static struct i2c_driver tas5720_i2c_driver = { + .driver = { + .name = "tas5720", + .of_match_table = of_match_ptr(tas5720_of_match), + }, + .probe = tas5720_probe, + .id_table = tas5720_id, +}; + +module_i2c_driver(tas5720_i2c_driver); + +MODULE_AUTHOR("Andreas Dannenberg "); +MODULE_DESCRIPTION("TAS5720 Audio amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas5720.h b/sound/soc/codecs/tas5720.h new file mode 100644 index 0000000000..54b59b05ef --- /dev/null +++ b/sound/soc/codecs/tas5720.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tas5720.h - ALSA SoC Texas Instruments TAS5720 Mono Audio Amplifier + * + * Copyright (C)2015-2016 Texas Instruments Incorporated - https://www.ti.com + * + * Author: Andreas Dannenberg + */ + +#ifndef __TAS5720_H__ +#define __TAS5720_H__ + +/* Register Address Map - first 3 regs are common for all variants */ +#define TAS5720_DEVICE_ID_REG 0x00 +#define TAS5720_POWER_CTRL_REG 0x01 +#define TAS5720_DIGITAL_CTRL1_REG 0x02 +#define TAS5720_DIGITAL_CTRL2_REG 0x03 +#define TAS5720_VOLUME_CTRL_REG 0x04 +#define TAS5720_ANALOG_CTRL_REG 0x06 +#define TAS5720_FAULT_REG 0x08 +#define TAS5720_DIGITAL_CLIP2_REG 0x10 +#define TAS5720_DIGITAL_CLIP1_REG 0x11 +#define TAS5720_MAX_REG TAS5720_DIGITAL_CLIP1_REG + +/* Additional TAS5722-specific Registers */ +#define TAS5722_DIGITAL_CTRL2_REG 0x13 +#define TAS5722_ANALOG_CTRL2_REG 0x14 +#define TAS5722_MAX_REG TAS5722_ANALOG_CTRL2_REG + +/* Register Address Map - volume controls for the TAS5720-Q1 variant */ +#define TAS5720_Q1_VOLUME_CTRL_CFG_REG 0x03 +#define TAS5720_Q1_VOLUME_CTRL_LEFT_REG 0x04 +#define TAS5720_Q1_VOLUME_CTRL_RIGHT_REG 0x05 + +/* TAS5720_DEVICE_ID_REG */ +#define TAS5720A_Q1_DEVICE_ID 0x00 +#define TAS5720_DEVICE_ID 0x01 +#define TAS5722_DEVICE_ID 0x12 + +/* TAS5720_POWER_CTRL_REG */ +#define TAS5720_DIG_CLIP_MASK GENMASK(7, 2) +#define TAS5720_SLEEP BIT(1) +#define TAS5720_SDZ BIT(0) + +/* TAS5720_DIGITAL_CTRL1_REG */ +#define TAS5720_HPF_BYPASS BIT(7) +#define TAS5720_TDM_CFG_SRC BIT(6) +#define TAS5720_SSZ_DS BIT(3) +#define TAS5720_SAIF_RIGHTJ_24BIT (0x0) +#define TAS5720_SAIF_RIGHTJ_20BIT (0x1) +#define TAS5720_SAIF_RIGHTJ_18BIT (0x2) +#define TAS5720_SAIF_RIGHTJ_16BIT (0x3) +#define TAS5720_SAIF_I2S (0x4) +#define TAS5720_SAIF_LEFTJ (0x5) +#define TAS5720_SAIF_FORMAT_MASK GENMASK(2, 0) + +/* TAS5720_DIGITAL_CTRL2_REG */ +#define TAS5722_VOL_RAMP_RATE BIT(6) +#define TAS5720_MUTE BIT(4) +#define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0) + +/* TAS5720_Q1_VOLUME_CTRL_CFG_REG */ +#define TAS5720_Q1_FADE BIT(7) +#define TAS5720_Q1_MUTE GENMASK(1, 0) + +/* TAS5720_ANALOG_CTRL_REG */ +#define TAS5720_PWM_RATE_6_3_FSYNC (0x0 << 4) +#define TAS5720_PWM_RATE_8_4_FSYNC (0x1 << 4) +#define TAS5720_PWM_RATE_10_5_FSYNC (0x2 << 4) +#define TAS5720_PWM_RATE_12_6_FSYNC (0x3 << 4) +#define TAS5720_PWM_RATE_14_7_FSYNC (0x4 << 4) +#define TAS5720_PWM_RATE_16_8_FSYNC (0x5 << 4) +#define TAS5720_PWM_RATE_20_10_FSYNC (0x6 << 4) +#define TAS5720_PWM_RATE_24_12_FSYNC (0x7 << 4) +#define TAS5720_PWM_RATE_MASK GENMASK(6, 4) +#define TAS5720_ANALOG_GAIN_19_2DBV (0x0 << 2) +#define TAS5720_ANALOG_GAIN_20_7DBV (0x1 << 2) +#define TAS5720_ANALOG_GAIN_23_5DBV (0x2 << 2) +#define TAS5720_ANALOG_GAIN_26_3DBV (0x3 << 2) +#define TAS5720_ANALOG_GAIN_MASK GENMASK(3, 2) +#define TAS5720_ANALOG_GAIN_SHIFT (0x2) + +/* TAS5720_Q1_ANALOG_CTRL_REG */ +#define TAS5720_Q1_RESERVED7_BIT BIT(7) +#define TAS5720_Q1_CHAN_SEL BIT(1) + +/* TAS5720_FAULT_REG */ +#define TAS5720_OC_THRESH_100PCT (0x0 << 4) +#define TAS5720_OC_THRESH_75PCT (0x1 << 4) +#define TAS5720_OC_THRESH_50PCT (0x2 << 4) +#define TAS5720_OC_THRESH_25PCT (0x3 << 4) +#define TAS5720_OC_THRESH_MASK GENMASK(5, 4) +#define TAS5720_CLKE BIT(3) +#define TAS5720_OCE BIT(2) +#define TAS5720_DCE BIT(1) +#define TAS5720_OTE BIT(0) +#define TAS5720_FAULT_MASK GENMASK(3, 0) + +/* TAS5720_DIGITAL_CLIP1_REG */ +#define TAS5720_CLIP1_MASK GENMASK(7, 2) +#define TAS5720_CLIP1_SHIFT (0x2) + +/* TAS5722_DIGITAL_CTRL2_REG */ +#define TAS5722_HPF_3_7HZ (0x0 << 5) +#define TAS5722_HPF_7_4HZ (0x1 << 5) +#define TAS5722_HPF_14_9HZ (0x2 << 5) +#define TAS5722_HPF_29_7HZ (0x3 << 5) +#define TAS5722_HPF_59_4HZ (0x4 << 5) +#define TAS5722_HPF_118_4HZ (0x5 << 5) +#define TAS5722_HPF_235_0HZ (0x6 << 5) +#define TAS5722_HPF_463_2HZ (0x7 << 5) +#define TAS5722_HPF_MASK GENMASK(7, 5) +#define TAS5722_AUTO_SLEEP_OFF (0x0 << 3) +#define TAS5722_AUTO_SLEEP_1024LR (0x1 << 3) +#define TAS5722_AUTO_SLEEP_65536LR (0x2 << 3) +#define TAS5722_AUTO_SLEEP_262144LR (0x3 << 3) +#define TAS5722_AUTO_SLEEP_MASK GENMASK(4, 3) +#define TAS5722_TDM_SLOT_16B BIT(2) +#define TAS5722_MCLK_PIN_CFG BIT(1) +#define TAS5722_VOL_CONTROL_LSB BIT(0) + +/* TAS5722_ANALOG_CTRL2_REG */ +#define TAS5722_FAULTZ_PU BIT(3) +#define TAS5722_VREG_LVL BIT(2) +#define TAS5722_PWR_TUNE BIT(0) + +#endif /* __TAS5720_H__ */ diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c new file mode 100644 index 0000000000..3b53eba38a --- /dev/null +++ b/sound/soc/codecs/tas5805m.c @@ -0,0 +1,613 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for the TAS5805M Audio Amplifier +// +// Author: Andy Liu +// Author: Daniel Beer +// +// This is based on a driver originally written by Andy Liu at TI and +// posted here: +// +// https://e2e.ti.com/support/audio-group/audio/f/audio-forum/722027/linux-tas5825m-linux-drivers +// +// It has been simplified a little and reworked for the 5.x ALSA SoC API. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Datasheet-defined registers on page 0, book 0 */ +#define REG_PAGE 0x00 +#define REG_DEVICE_CTRL_1 0x02 +#define REG_DEVICE_CTRL_2 0x03 +#define REG_SIG_CH_CTRL 0x28 +#define REG_SAP_CTRL_1 0x33 +#define REG_FS_MON 0x37 +#define REG_BCK_MON 0x38 +#define REG_CLKDET_STATUS 0x39 +#define REG_VOL_CTL 0x4c +#define REG_AGAIN 0x54 +#define REG_ADR_PIN_CTRL 0x60 +#define REG_ADR_PIN_CONFIG 0x61 +#define REG_CHAN_FAULT 0x70 +#define REG_GLOBAL_FAULT1 0x71 +#define REG_GLOBAL_FAULT2 0x72 +#define REG_FAULT 0x78 +#define REG_BOOK 0x7f + +/* DEVICE_CTRL_2 register values */ +#define DCTRL2_MODE_DEEP_SLEEP 0x00 +#define DCTRL2_MODE_SLEEP 0x01 +#define DCTRL2_MODE_HIZ 0x02 +#define DCTRL2_MODE_PLAY 0x03 + +#define DCTRL2_MUTE 0x08 +#define DCTRL2_DIS_DSP 0x10 + +/* This sequence of register writes must always be sent, prior to the + * 5ms delay while we wait for the DSP to boot. + */ +static const uint8_t dsp_cfg_preboot[] = { + 0x00, 0x00, 0x7f, 0x00, 0x03, 0x02, 0x01, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x00, 0x03, 0x02, +}; + +static const uint32_t tas5805m_volume[] = { + 0x0000001B, /* 0, -110dB */ 0x0000001E, /* 1, -109dB */ + 0x00000021, /* 2, -108dB */ 0x00000025, /* 3, -107dB */ + 0x0000002A, /* 4, -106dB */ 0x0000002F, /* 5, -105dB */ + 0x00000035, /* 6, -104dB */ 0x0000003B, /* 7, -103dB */ + 0x00000043, /* 8, -102dB */ 0x0000004B, /* 9, -101dB */ + 0x00000054, /* 10, -100dB */ 0x0000005E, /* 11, -99dB */ + 0x0000006A, /* 12, -98dB */ 0x00000076, /* 13, -97dB */ + 0x00000085, /* 14, -96dB */ 0x00000095, /* 15, -95dB */ + 0x000000A7, /* 16, -94dB */ 0x000000BC, /* 17, -93dB */ + 0x000000D3, /* 18, -92dB */ 0x000000EC, /* 19, -91dB */ + 0x00000109, /* 20, -90dB */ 0x0000012A, /* 21, -89dB */ + 0x0000014E, /* 22, -88dB */ 0x00000177, /* 23, -87dB */ + 0x000001A4, /* 24, -86dB */ 0x000001D8, /* 25, -85dB */ + 0x00000211, /* 26, -84dB */ 0x00000252, /* 27, -83dB */ + 0x0000029A, /* 28, -82dB */ 0x000002EC, /* 29, -81dB */ + 0x00000347, /* 30, -80dB */ 0x000003AD, /* 31, -79dB */ + 0x00000420, /* 32, -78dB */ 0x000004A1, /* 33, -77dB */ + 0x00000532, /* 34, -76dB */ 0x000005D4, /* 35, -75dB */ + 0x0000068A, /* 36, -74dB */ 0x00000756, /* 37, -73dB */ + 0x0000083B, /* 38, -72dB */ 0x0000093C, /* 39, -71dB */ + 0x00000A5D, /* 40, -70dB */ 0x00000BA0, /* 41, -69dB */ + 0x00000D0C, /* 42, -68dB */ 0x00000EA3, /* 43, -67dB */ + 0x0000106C, /* 44, -66dB */ 0x0000126D, /* 45, -65dB */ + 0x000014AD, /* 46, -64dB */ 0x00001733, /* 47, -63dB */ + 0x00001A07, /* 48, -62dB */ 0x00001D34, /* 49, -61dB */ + 0x000020C5, /* 50, -60dB */ 0x000024C4, /* 51, -59dB */ + 0x00002941, /* 52, -58dB */ 0x00002E49, /* 53, -57dB */ + 0x000033EF, /* 54, -56dB */ 0x00003A45, /* 55, -55dB */ + 0x00004161, /* 56, -54dB */ 0x0000495C, /* 57, -53dB */ + 0x0000524F, /* 58, -52dB */ 0x00005C5A, /* 59, -51dB */ + 0x0000679F, /* 60, -50dB */ 0x00007444, /* 61, -49dB */ + 0x00008274, /* 62, -48dB */ 0x0000925F, /* 63, -47dB */ + 0x0000A43B, /* 64, -46dB */ 0x0000B845, /* 65, -45dB */ + 0x0000CEC1, /* 66, -44dB */ 0x0000E7FB, /* 67, -43dB */ + 0x00010449, /* 68, -42dB */ 0x0001240C, /* 69, -41dB */ + 0x000147AE, /* 70, -40dB */ 0x00016FAA, /* 71, -39dB */ + 0x00019C86, /* 72, -38dB */ 0x0001CEDC, /* 73, -37dB */ + 0x00020756, /* 74, -36dB */ 0x000246B5, /* 75, -35dB */ + 0x00028DCF, /* 76, -34dB */ 0x0002DD96, /* 77, -33dB */ + 0x00033718, /* 78, -32dB */ 0x00039B87, /* 79, -31dB */ + 0x00040C37, /* 80, -30dB */ 0x00048AA7, /* 81, -29dB */ + 0x00051884, /* 82, -28dB */ 0x0005B7B1, /* 83, -27dB */ + 0x00066A4A, /* 84, -26dB */ 0x000732AE, /* 85, -25dB */ + 0x00081385, /* 86, -24dB */ 0x00090FCC, /* 87, -23dB */ + 0x000A2ADB, /* 88, -22dB */ 0x000B6873, /* 89, -21dB */ + 0x000CCCCD, /* 90, -20dB */ 0x000E5CA1, /* 91, -19dB */ + 0x00101D3F, /* 92, -18dB */ 0x0012149A, /* 93, -17dB */ + 0x00144961, /* 94, -16dB */ 0x0016C311, /* 95, -15dB */ + 0x00198A13, /* 96, -14dB */ 0x001CA7D7, /* 97, -13dB */ + 0x002026F3, /* 98, -12dB */ 0x00241347, /* 99, -11dB */ + 0x00287A27, /* 100, -10dB */ 0x002D6A86, /* 101, -9dB */ + 0x0032F52D, /* 102, -8dB */ 0x00392CEE, /* 103, -7dB */ + 0x004026E7, /* 104, -6dB */ 0x0047FACD, /* 105, -5dB */ + 0x0050C336, /* 106, -4dB */ 0x005A9DF8, /* 107, -3dB */ + 0x0065AC8C, /* 108, -2dB */ 0x00721483, /* 109, -1dB */ + 0x00800000, /* 110, 0dB */ 0x008F9E4D, /* 111, 1dB */ + 0x00A12478, /* 112, 2dB */ 0x00B4CE08, /* 113, 3dB */ + 0x00CADDC8, /* 114, 4dB */ 0x00E39EA9, /* 115, 5dB */ + 0x00FF64C1, /* 116, 6dB */ 0x011E8E6A, /* 117, 7dB */ + 0x0141857F, /* 118, 8dB */ 0x0168C0C6, /* 119, 9dB */ + 0x0194C584, /* 120, 10dB */ 0x01C62940, /* 121, 11dB */ + 0x01FD93C2, /* 122, 12dB */ 0x023BC148, /* 123, 13dB */ + 0x02818508, /* 124, 14dB */ 0x02CFCC01, /* 125, 15dB */ + 0x0327A01A, /* 126, 16dB */ 0x038A2BAD, /* 127, 17dB */ + 0x03F8BD7A, /* 128, 18dB */ 0x0474CD1B, /* 129, 19dB */ + 0x05000000, /* 130, 20dB */ 0x059C2F02, /* 131, 21dB */ + 0x064B6CAE, /* 132, 22dB */ 0x07100C4D, /* 133, 23dB */ + 0x07ECA9CD, /* 134, 24dB */ 0x08E43299, /* 135, 25dB */ + 0x09F9EF8E, /* 136, 26dB */ 0x0B319025, /* 137, 27dB */ + 0x0C8F36F2, /* 138, 28dB */ 0x0E1787B8, /* 139, 29dB */ + 0x0FCFB725, /* 140, 30dB */ 0x11BD9C84, /* 141, 31dB */ + 0x13E7C594, /* 142, 32dB */ 0x16558CCB, /* 143, 33dB */ + 0x190F3254, /* 144, 34dB */ 0x1C1DF80E, /* 145, 35dB */ + 0x1F8C4107, /* 146, 36dB */ 0x2365B4BF, /* 147, 37dB */ + 0x27B766C2, /* 148, 38dB */ 0x2C900313, /* 149, 39dB */ + 0x32000000, /* 150, 40dB */ 0x3819D612, /* 151, 41dB */ + 0x3EF23ECA, /* 152, 42dB */ 0x46A07B07, /* 153, 43dB */ + 0x4F3EA203, /* 154, 44dB */ 0x58E9F9F9, /* 155, 45dB */ + 0x63C35B8E, /* 156, 46dB */ 0x6FEFA16D, /* 157, 47dB */ + 0x7D982575, /* 158, 48dB */ +}; + +#define TAS5805M_VOLUME_MAX ((int)ARRAY_SIZE(tas5805m_volume) - 1) +#define TAS5805M_VOLUME_MIN 0 + +struct tas5805m_priv { + struct i2c_client *i2c; + struct regulator *pvdd; + struct gpio_desc *gpio_pdn_n; + + uint8_t *dsp_cfg_data; + int dsp_cfg_len; + + struct regmap *regmap; + + int vol[2]; + bool is_powered; + bool is_muted; + + struct work_struct work; + struct mutex lock; +}; + +static void set_dsp_scale(struct regmap *rm, int offset, int vol) +{ + uint8_t v[4]; + uint32_t x = tas5805m_volume[vol]; + int i; + + for (i = 0; i < 4; i++) { + v[3 - i] = x; + x >>= 8; + } + + regmap_bulk_write(rm, offset, v, ARRAY_SIZE(v)); +} + +static void tas5805m_refresh(struct tas5805m_priv *tas5805m) +{ + struct regmap *rm = tas5805m->regmap; + + dev_dbg(&tas5805m->i2c->dev, "refresh: is_muted=%d, vol=%d/%d\n", + tas5805m->is_muted, tas5805m->vol[0], tas5805m->vol[1]); + + regmap_write(rm, REG_PAGE, 0x00); + regmap_write(rm, REG_BOOK, 0x8c); + regmap_write(rm, REG_PAGE, 0x2a); + + /* Refresh volume. The actual volume control documented in the + * datasheet doesn't seem to work correctly. This is a pair of + * DSP registers which are *not* documented in the datasheet. + */ + set_dsp_scale(rm, 0x24, tas5805m->vol[0]); + set_dsp_scale(rm, 0x28, tas5805m->vol[1]); + + regmap_write(rm, REG_PAGE, 0x00); + regmap_write(rm, REG_BOOK, 0x00); + + /* Set/clear digital soft-mute */ + regmap_write(rm, REG_DEVICE_CTRL_2, + (tas5805m->is_muted ? DCTRL2_MUTE : 0) | + DCTRL2_MODE_PLAY); +} + +static int tas5805m_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + + uinfo->value.integer.min = TAS5805M_VOLUME_MIN; + uinfo->value.integer.max = TAS5805M_VOLUME_MAX; + return 0; +} + +static int tas5805m_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + + mutex_lock(&tas5805m->lock); + ucontrol->value.integer.value[0] = tas5805m->vol[0]; + ucontrol->value.integer.value[1] = tas5805m->vol[1]; + mutex_unlock(&tas5805m->lock); + + return 0; +} + +static inline int volume_is_valid(int v) +{ + return (v >= TAS5805M_VOLUME_MIN) && (v <= TAS5805M_VOLUME_MAX); +} + +static int tas5805m_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + int ret = 0; + + if (!(volume_is_valid(ucontrol->value.integer.value[0]) && + volume_is_valid(ucontrol->value.integer.value[1]))) + return -EINVAL; + + mutex_lock(&tas5805m->lock); + if (tas5805m->vol[0] != ucontrol->value.integer.value[0] || + tas5805m->vol[1] != ucontrol->value.integer.value[1]) { + tas5805m->vol[0] = ucontrol->value.integer.value[0]; + tas5805m->vol[1] = ucontrol->value.integer.value[1]; + dev_dbg(component->dev, "set vol=%d/%d (is_powered=%d)\n", + tas5805m->vol[0], tas5805m->vol[1], + tas5805m->is_powered); + if (tas5805m->is_powered) + tas5805m_refresh(tas5805m); + ret = 1; + } + mutex_unlock(&tas5805m->lock); + + return ret; +} + +static const struct snd_kcontrol_new tas5805m_snd_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = tas5805m_vol_info, + .get = tas5805m_vol_get, + .put = tas5805m_vol_put, + }, +}; + +static void send_cfg(struct regmap *rm, + const uint8_t *s, unsigned int len) +{ + unsigned int i; + + for (i = 0; i + 1 < len; i += 2) + regmap_write(rm, s[i], s[i + 1]); +} + +/* The TAS5805M DSP can't be configured until the I2S clock has been + * present and stable for 5ms, or else it won't boot and we get no + * sound. + */ +static int tas5805m_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dev_dbg(component->dev, "clock start\n"); + schedule_work(&tas5805m->work); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + + default: + return -EINVAL; + } + + return 0; +} + +static void do_work(struct work_struct *work) +{ + struct tas5805m_priv *tas5805m = + container_of(work, struct tas5805m_priv, work); + struct regmap *rm = tas5805m->regmap; + + dev_dbg(&tas5805m->i2c->dev, "DSP startup\n"); + + mutex_lock(&tas5805m->lock); + /* We mustn't issue any I2C transactions until the I2S + * clock is stable. Furthermore, we must allow a 5ms + * delay after the first set of register writes to + * allow the DSP to boot before configuring it. + */ + usleep_range(5000, 10000); + send_cfg(rm, dsp_cfg_preboot, ARRAY_SIZE(dsp_cfg_preboot)); + usleep_range(5000, 15000); + send_cfg(rm, tas5805m->dsp_cfg_data, tas5805m->dsp_cfg_len); + + tas5805m->is_powered = true; + tas5805m_refresh(tas5805m); + mutex_unlock(&tas5805m->lock); +} + +static int tas5805m_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + struct regmap *rm = tas5805m->regmap; + + if (event & SND_SOC_DAPM_PRE_PMD) { + unsigned int chan, global1, global2; + + dev_dbg(component->dev, "DSP shutdown\n"); + cancel_work_sync(&tas5805m->work); + + mutex_lock(&tas5805m->lock); + if (tas5805m->is_powered) { + tas5805m->is_powered = false; + + regmap_write(rm, REG_PAGE, 0x00); + regmap_write(rm, REG_BOOK, 0x00); + + regmap_read(rm, REG_CHAN_FAULT, &chan); + regmap_read(rm, REG_GLOBAL_FAULT1, &global1); + regmap_read(rm, REG_GLOBAL_FAULT2, &global2); + + dev_dbg(component->dev, "fault regs: CHAN=%02x, " + "GLOBAL1=%02x, GLOBAL2=%02x\n", + chan, global1, global2); + + regmap_write(rm, REG_DEVICE_CTRL_2, DCTRL2_MODE_HIZ); + } + mutex_unlock(&tas5805m->lock); + } + + return 0; +} + +static const struct snd_soc_dapm_route tas5805m_audio_map[] = { + { "DAC", NULL, "DAC IN" }, + { "OUT", NULL, "DAC" }, +}; + +static const struct snd_soc_dapm_widget tas5805m_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, + tas5805m_dac_event, SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT") +}; + +static const struct snd_soc_component_driver soc_codec_dev_tas5805m = { + .controls = tas5805m_snd_controls, + .num_controls = ARRAY_SIZE(tas5805m_snd_controls), + .dapm_widgets = tas5805m_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5805m_dapm_widgets), + .dapm_routes = tas5805m_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas5805m_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int tas5805m_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct tas5805m_priv *tas5805m = + snd_soc_component_get_drvdata(component); + + mutex_lock(&tas5805m->lock); + dev_dbg(component->dev, "set mute=%d (is_powered=%d)\n", + mute, tas5805m->is_powered); + + tas5805m->is_muted = mute; + if (tas5805m->is_powered) + tas5805m_refresh(tas5805m); + mutex_unlock(&tas5805m->lock); + + return 0; +} + +static const struct snd_soc_dai_ops tas5805m_dai_ops = { + .trigger = tas5805m_trigger, + .mute_stream = tas5805m_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver tas5805m_dai = { + .name = "tas5805m-amplifier", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tas5805m_dai_ops, +}; + +static const struct regmap_config tas5805m_regmap = { + .reg_bits = 8, + .val_bits = 8, + + /* We have quite a lot of multi-level bank switching and a + * relatively small number of register writes between bank + * switches. + */ + .cache_type = REGCACHE_NONE, +}; + +static int tas5805m_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct regmap *regmap; + struct tas5805m_priv *tas5805m; + char filename[128]; + const char *config_name; + const struct firmware *fw; + int ret; + + regmap = devm_regmap_init_i2c(i2c, &tas5805m_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "unable to allocate register map: %d\n", ret); + return ret; + } + + tas5805m = devm_kzalloc(dev, sizeof(struct tas5805m_priv), GFP_KERNEL); + if (!tas5805m) + return -ENOMEM; + + tas5805m->i2c = i2c; + tas5805m->pvdd = devm_regulator_get(dev, "pvdd"); + if (IS_ERR(tas5805m->pvdd)) { + dev_err(dev, "failed to get pvdd supply: %ld\n", + PTR_ERR(tas5805m->pvdd)); + return PTR_ERR(tas5805m->pvdd); + } + + dev_set_drvdata(dev, tas5805m); + tas5805m->regmap = regmap; + tas5805m->gpio_pdn_n = devm_gpiod_get(dev, "pdn", GPIOD_OUT_LOW); + if (IS_ERR(tas5805m->gpio_pdn_n)) { + dev_err(dev, "error requesting PDN gpio: %ld\n", + PTR_ERR(tas5805m->gpio_pdn_n)); + return PTR_ERR(tas5805m->gpio_pdn_n); + } + + /* This configuration must be generated by PPC3. The file loaded + * consists of a sequence of register writes, where bytes at + * even indices are register addresses and those at odd indices + * are register values. + * + * The fixed portion of PPC3's output prior to the 5ms delay + * should be omitted. + */ + if (device_property_read_string(dev, "ti,dsp-config-name", + &config_name)) + config_name = "default"; + + snprintf(filename, sizeof(filename), "tas5805m_dsp_%s.bin", + config_name); + ret = request_firmware(&fw, filename, dev); + if (ret) + return ret; + + if ((fw->size < 2) || (fw->size & 1)) { + dev_err(dev, "firmware is invalid\n"); + release_firmware(fw); + return -EINVAL; + } + + tas5805m->dsp_cfg_len = fw->size; + tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL); + if (!tas5805m->dsp_cfg_data) { + release_firmware(fw); + return -ENOMEM; + } + + release_firmware(fw); + + /* Do the first part of the power-on here, while we can expect + * the I2S interface to be quiet. We must raise PDN# and then + * wait 5ms before any I2S clock is sent, or else the internal + * regulator apparently won't come on. + * + * Also, we must keep the device in power down for 100ms or so + * after PVDD is applied, or else the ADR pin is sampled + * incorrectly and the device comes up with an unpredictable I2C + * address. + */ + tas5805m->vol[0] = TAS5805M_VOLUME_MIN; + tas5805m->vol[1] = TAS5805M_VOLUME_MIN; + + ret = regulator_enable(tas5805m->pvdd); + if (ret < 0) { + dev_err(dev, "failed to enable pvdd: %d\n", ret); + return ret; + } + + usleep_range(100000, 150000); + gpiod_set_value(tas5805m->gpio_pdn_n, 1); + usleep_range(10000, 15000); + + INIT_WORK(&tas5805m->work, do_work); + mutex_init(&tas5805m->lock); + + /* Don't register through devm. We need to be able to unregister + * the component prior to deasserting PDN# + */ + ret = snd_soc_register_component(dev, &soc_codec_dev_tas5805m, + &tas5805m_dai, 1); + if (ret < 0) { + dev_err(dev, "unable to register codec: %d\n", ret); + gpiod_set_value(tas5805m->gpio_pdn_n, 0); + regulator_disable(tas5805m->pvdd); + return ret; + } + + return 0; +} + +static void tas5805m_i2c_remove(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct tas5805m_priv *tas5805m = dev_get_drvdata(dev); + + cancel_work_sync(&tas5805m->work); + snd_soc_unregister_component(dev); + gpiod_set_value(tas5805m->gpio_pdn_n, 0); + usleep_range(10000, 15000); + regulator_disable(tas5805m->pvdd); +} + +static const struct i2c_device_id tas5805m_i2c_id[] = { + { "tas5805m", }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas5805m_i2c_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tas5805m_of_match[] = { + { .compatible = "ti,tas5805m", }, + { } +}; +MODULE_DEVICE_TABLE(of, tas5805m_of_match); +#endif + +static struct i2c_driver tas5805m_i2c_driver = { + .probe = tas5805m_i2c_probe, + .remove = tas5805m_i2c_remove, + .id_table = tas5805m_i2c_id, + .driver = { + .name = "tas5805m", + .of_match_table = of_match_ptr(tas5805m_of_match), + }, +}; + +module_i2c_driver(tas5805m_i2c_driver); + +MODULE_AUTHOR("Andy Liu "); +MODULE_AUTHOR("Daniel Beer "); +MODULE_DESCRIPTION("TAS5805M Audio Amplifier Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c new file mode 100644 index 0000000000..da89e8c681 --- /dev/null +++ b/sound/soc/codecs/tas6424.c @@ -0,0 +1,814 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier + * + * Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Andreas Dannenberg + * Andrew F. Davis + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tas6424.h" + +/* Define how often to check (and clear) the fault status register (in ms) */ +#define TAS6424_FAULT_CHECK_INTERVAL 200 + +static const char * const tas6424_supply_names[] = { + "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ + "vbat", /* Supply used for higher voltage analog circuits. */ + "pvdd", /* Class-D amp output FETs supply. */ +}; +#define TAS6424_NUM_SUPPLIES ARRAY_SIZE(tas6424_supply_names) + +struct tas6424_data { + struct device *dev; + struct regmap *regmap; + struct regulator_bulk_data supplies[TAS6424_NUM_SUPPLIES]; + struct delayed_work fault_check_work; + unsigned int last_cfault; + unsigned int last_fault1; + unsigned int last_fault2; + unsigned int last_warn; + struct gpio_desc *standby_gpio; + struct gpio_desc *mute_gpio; +}; + +/* + * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that + * setting the gain below -100 dB (register value <0x7) is effectively a MUTE + * as per device datasheet. + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0); + +static const struct snd_kcontrol_new tas6424_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Driver CH1 Playback Volume", + TAS6424_CH1_VOL_CTRL, 0, 0xff, 0, dac_tlv), + SOC_SINGLE_TLV("Speaker Driver CH2 Playback Volume", + TAS6424_CH2_VOL_CTRL, 0, 0xff, 0, dac_tlv), + SOC_SINGLE_TLV("Speaker Driver CH3 Playback Volume", + TAS6424_CH3_VOL_CTRL, 0, 0xff, 0, dac_tlv), + SOC_SINGLE_TLV("Speaker Driver CH4 Playback Volume", + TAS6424_CH4_VOL_CTRL, 0, 0xff, 0, dac_tlv), + SOC_SINGLE_STROBE("Auto Diagnostics Switch", TAS6424_DC_DIAG_CTRL1, + TAS6424_LDGBYPASS_SHIFT, 1), +}; + +static int tas6424_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s() event=0x%0x\n", __func__, event); + + if (event & SND_SOC_DAPM_POST_PMU) { + /* Observe codec shutdown-to-active time */ + msleep(12); + + /* Turn on TAS6424 periodic fault checking/handling */ + tas6424->last_fault1 = 0; + tas6424->last_fault2 = 0; + tas6424->last_warn = 0; + schedule_delayed_work(&tas6424->fault_check_work, + msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL)); + } else if (event & SND_SOC_DAPM_PRE_PMD) { + /* Disable TAS6424 periodic fault checking/handling */ + cancel_delayed_work_sync(&tas6424->fault_check_work); + } + + return 0; +} + +static const struct snd_soc_dapm_widget tas6424_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAC IN", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas6424_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT") +}; + +static const struct snd_soc_dapm_route tas6424_audio_map[] = { + { "DAC", NULL, "DAC IN" }, + { "OUT", NULL, "DAC" }, +}; + +static int tas6424_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned int rate = params_rate(params); + unsigned int width = params_width(params); + u8 sap_ctrl = 0; + + dev_dbg(component->dev, "%s() rate=%u width=%u\n", __func__, rate, width); + + switch (rate) { + case 44100: + sap_ctrl |= TAS6424_SAP_RATE_44100; + break; + case 48000: + sap_ctrl |= TAS6424_SAP_RATE_48000; + break; + case 96000: + sap_ctrl |= TAS6424_SAP_RATE_96000; + break; + default: + dev_err(component->dev, "unsupported sample rate: %u\n", rate); + return -EINVAL; + } + + switch (width) { + case 16: + sap_ctrl |= TAS6424_SAP_TDM_SLOT_SZ_16; + break; + case 24: + break; + default: + dev_err(component->dev, "unsupported sample width: %u\n", width); + return -EINVAL; + } + + snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, + TAS6424_SAP_RATE_MASK | + TAS6424_SAP_TDM_SLOT_SZ_16, + sap_ctrl); + + return 0; +} + +static int tas6424_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u8 serial_format = 0; + + dev_dbg(component->dev, "%s() fmt=0x%0x\n", __func__, fmt); + + /* clock masters */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + dev_err(component->dev, "Invalid DAI clocking\n"); + return -EINVAL; + } + + /* signal polarity */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_err(component->dev, "Invalid DAI clock signal polarity\n"); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + serial_format |= TAS6424_SAP_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + serial_format |= TAS6424_SAP_DSP; + break; + case SND_SOC_DAIFMT_DSP_B: + /* + * We can use the fact that the TAS6424 does not care about the + * LRCLK duty cycle during TDM to receive DSP_B formatted data + * in LEFTJ mode (no delaying of the 1st data bit). + */ + serial_format |= TAS6424_SAP_LEFTJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + serial_format |= TAS6424_SAP_LEFTJ; + break; + default: + dev_err(component->dev, "Invalid DAI interface format\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, + TAS6424_SAP_FMT_MASK, serial_format); + + return 0; +} + +static int tas6424_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int first_slot, last_slot; + bool sap_tdm_slot_last; + + dev_dbg(component->dev, "%s() tx_mask=%d rx_mask=%d\n", __func__, + tx_mask, rx_mask); + + if (!tx_mask || !rx_mask) + return 0; /* nothing needed to disable TDM mode */ + + /* + * Determine the first slot and last slot that is being requested so + * we'll be able to more easily enforce certain constraints as the + * TAS6424's TDM interface is not fully configurable. + */ + first_slot = __ffs(tx_mask); + last_slot = __fls(rx_mask); + + if (last_slot - first_slot != 4) { + dev_err(component->dev, "tdm mask must cover 4 contiguous slots\n"); + return -EINVAL; + } + + switch (first_slot) { + case 0: + sap_tdm_slot_last = false; + break; + case 4: + sap_tdm_slot_last = true; + break; + default: + dev_err(component->dev, "tdm mask must start at slot 0 or 4\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, TAS6424_SAP_CTRL, TAS6424_SAP_TDM_SLOT_LAST, + sap_tdm_slot_last ? TAS6424_SAP_TDM_SLOT_LAST : 0); + + return 0; +} + +static int tas6424_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); + unsigned int val; + + dev_dbg(component->dev, "%s() mute=%d\n", __func__, mute); + + if (tas6424->mute_gpio) { + gpiod_set_value_cansleep(tas6424->mute_gpio, mute); + return 0; + } + + if (mute) + val = TAS6424_ALL_STATE_MUTE; + else + val = TAS6424_ALL_STATE_PLAY; + + snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, val); + + return 0; +} + +static int tas6424_power_off(struct snd_soc_component *component) +{ + struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); + int ret; + + snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, TAS6424_ALL_STATE_HIZ); + + regcache_cache_only(tas6424->regmap, true); + regcache_mark_dirty(tas6424->regmap); + + ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), + tas6424->supplies); + if (ret < 0) { + dev_err(component->dev, "failed to disable supplies: %d\n", ret); + return ret; + } + + return 0; +} + +static int tas6424_power_on(struct snd_soc_component *component) +{ + struct tas6424_data *tas6424 = snd_soc_component_get_drvdata(component); + int ret; + u8 chan_states; + int no_auto_diags = 0; + unsigned int reg_val; + + if (!regmap_read(tas6424->regmap, TAS6424_DC_DIAG_CTRL1, ®_val)) + no_auto_diags = reg_val & TAS6424_LDGBYPASS_MASK; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies), + tas6424->supplies); + if (ret < 0) { + dev_err(component->dev, "failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_cache_only(tas6424->regmap, false); + + ret = regcache_sync(tas6424->regmap); + if (ret < 0) { + dev_err(component->dev, "failed to sync regcache: %d\n", ret); + return ret; + } + + if (tas6424->mute_gpio) { + gpiod_set_value_cansleep(tas6424->mute_gpio, 0); + /* + * channels are muted via the mute pin. Don't also mute + * them via the registers so that subsequent register + * access is not necessary to un-mute the channels + */ + chan_states = TAS6424_ALL_STATE_PLAY; + } else { + chan_states = TAS6424_ALL_STATE_MUTE; + } + snd_soc_component_write(component, TAS6424_CH_STATE_CTRL, chan_states); + + /* any time we come out of HIZ, the output channels automatically run DC + * load diagnostics if autodiagnotics are enabled. wait here until this + * completes. + */ + if (!no_auto_diags) + msleep(230); + + return 0; +} + +static int tas6424_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + dev_dbg(component->dev, "%s() level=%d\n", __func__, level); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + tas6424_power_on(component); + break; + case SND_SOC_BIAS_OFF: + tas6424_power_off(component); + break; + } + + return 0; +} + +static struct snd_soc_component_driver soc_codec_dev_tas6424 = { + .set_bias_level = tas6424_set_bias_level, + .controls = tas6424_snd_controls, + .num_controls = ARRAY_SIZE(tas6424_snd_controls), + .dapm_widgets = tas6424_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas6424_dapm_widgets), + .dapm_routes = tas6424_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas6424_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_dai_ops tas6424_speaker_dai_ops = { + .hw_params = tas6424_hw_params, + .set_fmt = tas6424_set_dai_fmt, + .set_tdm_slot = tas6424_set_dai_tdm_slot, + .mute_stream = tas6424_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver tas6424_dai[] = { + { + .name = "tas6424-amplifier", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 4, + .rates = TAS6424_RATES, + .formats = TAS6424_FORMATS, + }, + .ops = &tas6424_speaker_dai_ops, + }, +}; + +static void tas6424_fault_check_work(struct work_struct *work) +{ + struct tas6424_data *tas6424 = container_of(work, struct tas6424_data, + fault_check_work.work); + struct device *dev = tas6424->dev; + unsigned int reg; + int ret; + + ret = regmap_read(tas6424->regmap, TAS6424_CHANNEL_FAULT, ®); + if (ret < 0) { + dev_err(dev, "failed to read CHANNEL_FAULT register: %d\n", ret); + goto out; + } + + if (!reg) { + tas6424->last_cfault = reg; + goto check_global_fault1_reg; + } + + /* + * Only flag errors once for a given occurrence. This is needed as + * the TAS6424 will take time clearing the fault condition internally + * during which we don't want to bombard the system with the same + * error message over and over. + */ + if ((reg & TAS6424_FAULT_OC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH1)) + dev_crit(dev, "experienced a channel 1 overcurrent fault\n"); + + if ((reg & TAS6424_FAULT_OC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH2)) + dev_crit(dev, "experienced a channel 2 overcurrent fault\n"); + + if ((reg & TAS6424_FAULT_OC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH3)) + dev_crit(dev, "experienced a channel 3 overcurrent fault\n"); + + if ((reg & TAS6424_FAULT_OC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH4)) + dev_crit(dev, "experienced a channel 4 overcurrent fault\n"); + + if ((reg & TAS6424_FAULT_DC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH1)) + dev_crit(dev, "experienced a channel 1 DC fault\n"); + + if ((reg & TAS6424_FAULT_DC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH2)) + dev_crit(dev, "experienced a channel 2 DC fault\n"); + + if ((reg & TAS6424_FAULT_DC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH3)) + dev_crit(dev, "experienced a channel 3 DC fault\n"); + + if ((reg & TAS6424_FAULT_DC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH4)) + dev_crit(dev, "experienced a channel 4 DC fault\n"); + + /* Store current fault1 value so we can detect any changes next time */ + tas6424->last_cfault = reg; + +check_global_fault1_reg: + ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT1, ®); + if (ret < 0) { + dev_err(dev, "failed to read GLOB_FAULT1 register: %d\n", ret); + goto out; + } + + /* + * Ignore any clock faults as there is no clean way to check for them. + * We would need to start checking for those faults *after* the SAIF + * stream has been setup, and stop checking *before* the stream is + * stopped to avoid any false-positives. However there are no + * appropriate hooks to monitor these events. + */ + reg &= TAS6424_FAULT_PVDD_OV | + TAS6424_FAULT_VBAT_OV | + TAS6424_FAULT_PVDD_UV | + TAS6424_FAULT_VBAT_UV; + + if (!reg) { + tas6424->last_fault1 = reg; + goto check_global_fault2_reg; + } + + if ((reg & TAS6424_FAULT_PVDD_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_OV)) + dev_crit(dev, "experienced a PVDD overvoltage fault\n"); + + if ((reg & TAS6424_FAULT_VBAT_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_OV)) + dev_crit(dev, "experienced a VBAT overvoltage fault\n"); + + if ((reg & TAS6424_FAULT_PVDD_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_UV)) + dev_crit(dev, "experienced a PVDD undervoltage fault\n"); + + if ((reg & TAS6424_FAULT_VBAT_UV) && !(tas6424->last_fault1 & TAS6424_FAULT_VBAT_UV)) + dev_crit(dev, "experienced a VBAT undervoltage fault\n"); + + /* Store current fault1 value so we can detect any changes next time */ + tas6424->last_fault1 = reg; + +check_global_fault2_reg: + ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT2, ®); + if (ret < 0) { + dev_err(dev, "failed to read GLOB_FAULT2 register: %d\n", ret); + goto out; + } + + reg &= TAS6424_FAULT_OTSD | + TAS6424_FAULT_OTSD_CH1 | + TAS6424_FAULT_OTSD_CH2 | + TAS6424_FAULT_OTSD_CH3 | + TAS6424_FAULT_OTSD_CH4; + + if (!reg) { + tas6424->last_fault2 = reg; + goto check_warn_reg; + } + + if ((reg & TAS6424_FAULT_OTSD) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD)) + dev_crit(dev, "experienced a global overtemp shutdown\n"); + + if ((reg & TAS6424_FAULT_OTSD_CH1) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH1)) + dev_crit(dev, "experienced an overtemp shutdown on CH1\n"); + + if ((reg & TAS6424_FAULT_OTSD_CH2) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH2)) + dev_crit(dev, "experienced an overtemp shutdown on CH2\n"); + + if ((reg & TAS6424_FAULT_OTSD_CH3) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH3)) + dev_crit(dev, "experienced an overtemp shutdown on CH3\n"); + + if ((reg & TAS6424_FAULT_OTSD_CH4) && !(tas6424->last_fault2 & TAS6424_FAULT_OTSD_CH4)) + dev_crit(dev, "experienced an overtemp shutdown on CH4\n"); + + /* Store current fault2 value so we can detect any changes next time */ + tas6424->last_fault2 = reg; + +check_warn_reg: + ret = regmap_read(tas6424->regmap, TAS6424_WARN, ®); + if (ret < 0) { + dev_err(dev, "failed to read WARN register: %d\n", ret); + goto out; + } + + reg &= TAS6424_WARN_VDD_UV | + TAS6424_WARN_VDD_POR | + TAS6424_WARN_VDD_OTW | + TAS6424_WARN_VDD_OTW_CH1 | + TAS6424_WARN_VDD_OTW_CH2 | + TAS6424_WARN_VDD_OTW_CH3 | + TAS6424_WARN_VDD_OTW_CH4; + + if (!reg) { + tas6424->last_warn = reg; + goto out; + } + + if ((reg & TAS6424_WARN_VDD_UV) && !(tas6424->last_warn & TAS6424_WARN_VDD_UV)) + dev_warn(dev, "experienced a VDD under voltage condition\n"); + + if ((reg & TAS6424_WARN_VDD_POR) && !(tas6424->last_warn & TAS6424_WARN_VDD_POR)) + dev_warn(dev, "experienced a VDD POR condition\n"); + + if ((reg & TAS6424_WARN_VDD_OTW) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW)) + dev_warn(dev, "experienced a global overtemp warning\n"); + + if ((reg & TAS6424_WARN_VDD_OTW_CH1) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH1)) + dev_warn(dev, "experienced an overtemp warning on CH1\n"); + + if ((reg & TAS6424_WARN_VDD_OTW_CH2) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH2)) + dev_warn(dev, "experienced an overtemp warning on CH2\n"); + + if ((reg & TAS6424_WARN_VDD_OTW_CH3) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH3)) + dev_warn(dev, "experienced an overtemp warning on CH3\n"); + + if ((reg & TAS6424_WARN_VDD_OTW_CH4) && !(tas6424->last_warn & TAS6424_WARN_VDD_OTW_CH4)) + dev_warn(dev, "experienced an overtemp warning on CH4\n"); + + /* Store current warn value so we can detect any changes next time */ + tas6424->last_warn = reg; + + /* Clear any warnings by toggling the CLEAR_FAULT control bit */ + ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3, + TAS6424_CLEAR_FAULT, TAS6424_CLEAR_FAULT); + if (ret < 0) + dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret); + + ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3, + TAS6424_CLEAR_FAULT, 0); + if (ret < 0) + dev_err(dev, "failed to write MISC_CTRL3 register: %d\n", ret); + +out: + /* Schedule the next fault check at the specified interval */ + schedule_delayed_work(&tas6424->fault_check_work, + msecs_to_jiffies(TAS6424_FAULT_CHECK_INTERVAL)); +} + +static const struct reg_default tas6424_reg_defaults[] = { + { TAS6424_MODE_CTRL, 0x00 }, + { TAS6424_MISC_CTRL1, 0x32 }, + { TAS6424_MISC_CTRL2, 0x62 }, + { TAS6424_SAP_CTRL, 0x04 }, + { TAS6424_CH_STATE_CTRL, 0x55 }, + { TAS6424_CH1_VOL_CTRL, 0xcf }, + { TAS6424_CH2_VOL_CTRL, 0xcf }, + { TAS6424_CH3_VOL_CTRL, 0xcf }, + { TAS6424_CH4_VOL_CTRL, 0xcf }, + { TAS6424_DC_DIAG_CTRL1, 0x00 }, + { TAS6424_DC_DIAG_CTRL2, 0x11 }, + { TAS6424_DC_DIAG_CTRL3, 0x11 }, + { TAS6424_PIN_CTRL, 0xff }, + { TAS6424_AC_DIAG_CTRL1, 0x00 }, + { TAS6424_MISC_CTRL3, 0x00 }, + { TAS6424_CLIP_CTRL, 0x01 }, + { TAS6424_CLIP_WINDOW, 0x14 }, + { TAS6424_CLIP_WARN, 0x00 }, + { TAS6424_CBC_STAT, 0x00 }, + { TAS6424_MISC_CTRL4, 0x40 }, +}; + +static bool tas6424_is_writable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS6424_MODE_CTRL: + case TAS6424_MISC_CTRL1: + case TAS6424_MISC_CTRL2: + case TAS6424_SAP_CTRL: + case TAS6424_CH_STATE_CTRL: + case TAS6424_CH1_VOL_CTRL: + case TAS6424_CH2_VOL_CTRL: + case TAS6424_CH3_VOL_CTRL: + case TAS6424_CH4_VOL_CTRL: + case TAS6424_DC_DIAG_CTRL1: + case TAS6424_DC_DIAG_CTRL2: + case TAS6424_DC_DIAG_CTRL3: + case TAS6424_PIN_CTRL: + case TAS6424_AC_DIAG_CTRL1: + case TAS6424_MISC_CTRL3: + case TAS6424_CLIP_CTRL: + case TAS6424_CLIP_WINDOW: + case TAS6424_CLIP_WARN: + case TAS6424_CBC_STAT: + case TAS6424_MISC_CTRL4: + return true; + default: + return false; + } +} + +static bool tas6424_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS6424_DC_LOAD_DIAG_REP12: + case TAS6424_DC_LOAD_DIAG_REP34: + case TAS6424_DC_LOAD_DIAG_REPLO: + case TAS6424_CHANNEL_STATE: + case TAS6424_CHANNEL_FAULT: + case TAS6424_GLOB_FAULT1: + case TAS6424_GLOB_FAULT2: + case TAS6424_WARN: + case TAS6424_AC_LOAD_DIAG_REP1: + case TAS6424_AC_LOAD_DIAG_REP2: + case TAS6424_AC_LOAD_DIAG_REP3: + case TAS6424_AC_LOAD_DIAG_REP4: + return true; + default: + return false; + } +} + +static const struct regmap_config tas6424_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .writeable_reg = tas6424_is_writable_reg, + .volatile_reg = tas6424_is_volatile_reg, + + .max_register = TAS6424_MAX, + .reg_defaults = tas6424_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas6424_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tas6424_of_ids[] = { + { .compatible = "ti,tas6424", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tas6424_of_ids); +#endif + +static int tas6424_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct tas6424_data *tas6424; + int ret; + int i; + + tas6424 = devm_kzalloc(dev, sizeof(*tas6424), GFP_KERNEL); + if (!tas6424) + return -ENOMEM; + dev_set_drvdata(dev, tas6424); + + tas6424->dev = dev; + + tas6424->regmap = devm_regmap_init_i2c(client, &tas6424_regmap_config); + if (IS_ERR(tas6424->regmap)) { + ret = PTR_ERR(tas6424->regmap); + dev_err(dev, "unable to allocate register map: %d\n", ret); + return ret; + } + + /* + * Get control of the standby pin and set it LOW to take the codec + * out of the stand-by mode. + * Note: The actual pin polarity is taken care of in the GPIO lib + * according the polarity specified in the DTS. + */ + tas6424->standby_gpio = devm_gpiod_get_optional(dev, "standby", + GPIOD_OUT_LOW); + if (IS_ERR(tas6424->standby_gpio)) { + if (PTR_ERR(tas6424->standby_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "failed to get standby GPIO: %ld\n", + PTR_ERR(tas6424->standby_gpio)); + tas6424->standby_gpio = NULL; + } + + /* + * Get control of the mute pin and set it HIGH in order to start with + * all the output muted. + * Note: The actual pin polarity is taken care of in the GPIO lib + * according the polarity specified in the DTS. + */ + tas6424->mute_gpio = devm_gpiod_get_optional(dev, "mute", + GPIOD_OUT_HIGH); + if (IS_ERR(tas6424->mute_gpio)) { + if (PTR_ERR(tas6424->mute_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(dev, "failed to get nmute GPIO: %ld\n", + PTR_ERR(tas6424->mute_gpio)); + tas6424->mute_gpio = NULL; + } + + for (i = 0; i < ARRAY_SIZE(tas6424->supplies); i++) + tas6424->supplies[i].supply = tas6424_supply_names[i]; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(tas6424->supplies), + tas6424->supplies); + if (ret) { + dev_err(dev, "unable to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(tas6424->supplies), + tas6424->supplies); + if (ret) { + dev_err(dev, "unable to enable supplies: %d\n", ret); + return ret; + } + + /* Reset device to establish well-defined startup state */ + ret = regmap_update_bits(tas6424->regmap, TAS6424_MODE_CTRL, + TAS6424_RESET, TAS6424_RESET); + if (ret) { + dev_err(dev, "unable to reset device: %d\n", ret); + goto disable_regs; + } + + INIT_DELAYED_WORK(&tas6424->fault_check_work, tas6424_fault_check_work); + + ret = devm_snd_soc_register_component(dev, &soc_codec_dev_tas6424, + tas6424_dai, ARRAY_SIZE(tas6424_dai)); + if (ret < 0) { + dev_err(dev, "unable to register codec: %d\n", ret); + goto disable_regs; + } + + return 0; + +disable_regs: + regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), tas6424->supplies); + return ret; +} + +static void tas6424_i2c_remove(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct tas6424_data *tas6424 = dev_get_drvdata(dev); + int ret; + + cancel_delayed_work_sync(&tas6424->fault_check_work); + + /* put the codec in stand-by */ + if (tas6424->standby_gpio) + gpiod_set_value_cansleep(tas6424->standby_gpio, 1); + + ret = regulator_bulk_disable(ARRAY_SIZE(tas6424->supplies), + tas6424->supplies); + if (ret < 0) + dev_err(dev, "unable to disable supplies: %d\n", ret); +} + +static const struct i2c_device_id tas6424_i2c_ids[] = { + { "tas6424", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas6424_i2c_ids); + +static struct i2c_driver tas6424_i2c_driver = { + .driver = { + .name = "tas6424", + .of_match_table = of_match_ptr(tas6424_of_ids), + }, + .probe = tas6424_i2c_probe, + .remove = tas6424_i2c_remove, + .id_table = tas6424_i2c_ids, +}; +module_i2c_driver(tas6424_i2c_driver); + +MODULE_AUTHOR("Andreas Dannenberg "); +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TAS6424 Audio amplifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas6424.h b/sound/soc/codecs/tas6424.h new file mode 100644 index 0000000000..a6a0d00e51 --- /dev/null +++ b/sound/soc/codecs/tas6424.h @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC Texas Instruments TAS6424 Quad-Channel Audio Amplifier + * + * Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Andreas Dannenberg + * Andrew F. Davis + */ + +#ifndef __TAS6424_H__ +#define __TAS6424_H__ + +#define TAS6424_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000) + +#define TAS6424_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/* Register Address Map */ +#define TAS6424_MODE_CTRL 0x00 +#define TAS6424_MISC_CTRL1 0x01 +#define TAS6424_MISC_CTRL2 0x02 +#define TAS6424_SAP_CTRL 0x03 +#define TAS6424_CH_STATE_CTRL 0x04 +#define TAS6424_CH1_VOL_CTRL 0x05 +#define TAS6424_CH2_VOL_CTRL 0x06 +#define TAS6424_CH3_VOL_CTRL 0x07 +#define TAS6424_CH4_VOL_CTRL 0x08 +#define TAS6424_DC_DIAG_CTRL1 0x09 +#define TAS6424_DC_DIAG_CTRL2 0x0a +#define TAS6424_DC_DIAG_CTRL3 0x0b +#define TAS6424_DC_LOAD_DIAG_REP12 0x0c +#define TAS6424_DC_LOAD_DIAG_REP34 0x0d +#define TAS6424_DC_LOAD_DIAG_REPLO 0x0e +#define TAS6424_CHANNEL_STATE 0x0f +#define TAS6424_CHANNEL_FAULT 0x10 +#define TAS6424_GLOB_FAULT1 0x11 +#define TAS6424_GLOB_FAULT2 0x12 +#define TAS6424_WARN 0x13 +#define TAS6424_PIN_CTRL 0x14 +#define TAS6424_AC_DIAG_CTRL1 0x15 +#define TAS6424_AC_DIAG_CTRL2 0x16 +#define TAS6424_AC_LOAD_DIAG_REP1 0x17 +#define TAS6424_AC_LOAD_DIAG_REP2 0x18 +#define TAS6424_AC_LOAD_DIAG_REP3 0x19 +#define TAS6424_AC_LOAD_DIAG_REP4 0x1a +#define TAS6424_MISC_CTRL3 0x21 +#define TAS6424_CLIP_CTRL 0x22 +#define TAS6424_CLIP_WINDOW 0x23 +#define TAS6424_CLIP_WARN 0x24 +#define TAS6424_CBC_STAT 0x25 +#define TAS6424_MISC_CTRL4 0x26 +#define TAS6424_MAX TAS6424_MISC_CTRL4 + +/* TAS6424_MODE_CTRL_REG */ +#define TAS6424_RESET BIT(7) + +/* TAS6424_SAP_CTRL_REG */ +#define TAS6424_SAP_RATE_MASK GENMASK(7, 6) +#define TAS6424_SAP_RATE_44100 (0x00 << 6) +#define TAS6424_SAP_RATE_48000 (0x01 << 6) +#define TAS6424_SAP_RATE_96000 (0x02 << 6) +#define TAS6424_SAP_TDM_SLOT_LAST BIT(5) +#define TAS6424_SAP_TDM_SLOT_SZ_16 BIT(4) +#define TAS6424_SAP_TDM_SLOT_SWAP BIT(3) +#define TAS6424_SAP_FMT_MASK GENMASK(2, 0) +#define TAS6424_SAP_RIGHTJ_24 (0x00 << 0) +#define TAS6424_SAP_RIGHTJ_20 (0x01 << 0) +#define TAS6424_SAP_RIGHTJ_18 (0x02 << 0) +#define TAS6424_SAP_RIGHTJ_16 (0x03 << 0) +#define TAS6424_SAP_I2S (0x04 << 0) +#define TAS6424_SAP_LEFTJ (0x05 << 0) +#define TAS6424_SAP_DSP (0x06 << 0) + +/* TAS6424_CH_STATE_CTRL_REG */ +#define TAS6424_CH1_STATE_MASK GENMASK(7, 6) +#define TAS6424_CH1_STATE_PLAY (0x00 << 6) +#define TAS6424_CH1_STATE_HIZ (0x01 << 6) +#define TAS6424_CH1_STATE_MUTE (0x02 << 6) +#define TAS6424_CH1_STATE_DIAG (0x03 << 6) +#define TAS6424_CH2_STATE_MASK GENMASK(5, 4) +#define TAS6424_CH2_STATE_PLAY (0x00 << 4) +#define TAS6424_CH2_STATE_HIZ (0x01 << 4) +#define TAS6424_CH2_STATE_MUTE (0x02 << 4) +#define TAS6424_CH2_STATE_DIAG (0x03 << 4) +#define TAS6424_CH3_STATE_MASK GENMASK(3, 2) +#define TAS6424_CH3_STATE_PLAY (0x00 << 2) +#define TAS6424_CH3_STATE_HIZ (0x01 << 2) +#define TAS6424_CH3_STATE_MUTE (0x02 << 2) +#define TAS6424_CH3_STATE_DIAG (0x03 << 2) +#define TAS6424_CH4_STATE_MASK GENMASK(1, 0) +#define TAS6424_CH4_STATE_PLAY (0x00 << 0) +#define TAS6424_CH4_STATE_HIZ (0x01 << 0) +#define TAS6424_CH4_STATE_MUTE (0x02 << 0) +#define TAS6424_CH4_STATE_DIAG (0x03 << 0) +#define TAS6424_ALL_STATE_PLAY (TAS6424_CH1_STATE_PLAY | \ + TAS6424_CH2_STATE_PLAY | \ + TAS6424_CH3_STATE_PLAY | \ + TAS6424_CH4_STATE_PLAY) +#define TAS6424_ALL_STATE_HIZ (TAS6424_CH1_STATE_HIZ | \ + TAS6424_CH2_STATE_HIZ | \ + TAS6424_CH3_STATE_HIZ | \ + TAS6424_CH4_STATE_HIZ) +#define TAS6424_ALL_STATE_MUTE (TAS6424_CH1_STATE_MUTE | \ + TAS6424_CH2_STATE_MUTE | \ + TAS6424_CH3_STATE_MUTE | \ + TAS6424_CH4_STATE_MUTE) +#define TAS6424_ALL_STATE_DIAG (TAS6424_CH1_STATE_DIAG | \ + TAS6424_CH2_STATE_DIAG | \ + TAS6424_CH3_STATE_DIAG | \ + TAS6424_CH4_STATE_DIAG) + +/* TAS6424_DC_DIAG_CTRL1 */ +#define TAS6424_LDGBYPASS_SHIFT 0 +#define TAS6424_LDGBYPASS_MASK BIT(TAS6424_LDGBYPASS_SHIFT) + +/* TAS6424_GLOB_FAULT1_REG */ +#define TAS6424_FAULT_OC_CH1 BIT(7) +#define TAS6424_FAULT_OC_CH2 BIT(6) +#define TAS6424_FAULT_OC_CH3 BIT(5) +#define TAS6424_FAULT_OC_CH4 BIT(4) +#define TAS6424_FAULT_DC_CH1 BIT(3) +#define TAS6424_FAULT_DC_CH2 BIT(2) +#define TAS6424_FAULT_DC_CH3 BIT(1) +#define TAS6424_FAULT_DC_CH4 BIT(0) + +/* TAS6424_GLOB_FAULT1_REG */ +#define TAS6424_FAULT_CLOCK BIT(4) +#define TAS6424_FAULT_PVDD_OV BIT(3) +#define TAS6424_FAULT_VBAT_OV BIT(2) +#define TAS6424_FAULT_PVDD_UV BIT(1) +#define TAS6424_FAULT_VBAT_UV BIT(0) + +/* TAS6424_GLOB_FAULT2_REG */ +#define TAS6424_FAULT_OTSD BIT(4) +#define TAS6424_FAULT_OTSD_CH1 BIT(3) +#define TAS6424_FAULT_OTSD_CH2 BIT(2) +#define TAS6424_FAULT_OTSD_CH3 BIT(1) +#define TAS6424_FAULT_OTSD_CH4 BIT(0) + +/* TAS6424_WARN_REG */ +#define TAS6424_WARN_VDD_UV BIT(6) +#define TAS6424_WARN_VDD_POR BIT(5) +#define TAS6424_WARN_VDD_OTW BIT(4) +#define TAS6424_WARN_VDD_OTW_CH1 BIT(3) +#define TAS6424_WARN_VDD_OTW_CH2 BIT(2) +#define TAS6424_WARN_VDD_OTW_CH3 BIT(1) +#define TAS6424_WARN_VDD_OTW_CH4 BIT(0) + +/* TAS6424_MISC_CTRL3_REG */ +#define TAS6424_CLEAR_FAULT BIT(7) +#define TAS6424_PBTL_CH_SEL BIT(6) +#define TAS6424_MASK_CBC_WARN BIT(5) +#define TAS6424_MASK_VDD_UV BIT(4) +#define TAS6424_OTSD_AUTO_RECOVERY BIT(3) + +#endif /* __TAS6424_H__ */ diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c new file mode 100644 index 0000000000..e187d74a17 --- /dev/null +++ b/sound/soc/codecs/tda7419.c @@ -0,0 +1,640 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TDA7419 audio processor driver + * + * Copyright 2018 Konsulko Group + * + * Author: Matt Porter + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TDA7419_MAIN_SRC_REG 0x00 +#define TDA7419_LOUDNESS_REG 0x01 +#define TDA7419_MUTE_CLK_REG 0x02 +#define TDA7419_VOLUME_REG 0x03 +#define TDA7419_TREBLE_REG 0x04 +#define TDA7419_MIDDLE_REG 0x05 +#define TDA7419_BASS_REG 0x06 +#define TDA7419_SECOND_SRC_REG 0x07 +#define TDA7419_SUB_MID_BASS_REG 0x08 +#define TDA7419_MIXING_GAIN_REG 0x09 +#define TDA7419_ATTENUATOR_LF_REG 0x0a +#define TDA7419_ATTENUATOR_RF_REG 0x0b +#define TDA7419_ATTENUATOR_LR_REG 0x0c +#define TDA7419_ATTENUATOR_RR_REG 0x0d +#define TDA7419_MIXING_LEVEL_REG 0x0e +#define TDA7419_ATTENUATOR_SUB_REG 0x0f +#define TDA7419_SA_CLK_AC_REG 0x10 +#define TDA7419_TESTING_REG 0x11 + +#define TDA7419_MAIN_SRC_SEL 0 +#define TDA7419_MAIN_SRC_GAIN 3 +#define TDA7419_MAIN_SRC_AUTOZERO 7 + +#define TDA7419_LOUDNESS_ATTEN 0 +#define TDA7419_LOUDNESS_CENTER_FREQ 4 +#define TDA7419_LOUDNESS_BOOST 6 +#define TDA7419_LOUDNESS_SOFT_STEP 7 + +#define TDA7419_VOLUME_SOFT_STEP 7 + +#define TDA7419_SOFT_MUTE 0 +#define TDA7419_MUTE_INFLUENCE 1 +#define TDA7419_SOFT_MUTE_TIME 2 +#define TDA7419_SOFT_STEP_TIME 4 +#define TDA7419_CLK_FAST_MODE 7 + +#define TDA7419_TREBLE_CENTER_FREQ 5 +#define TDA7419_REF_OUT_SELECT 7 + +#define TDA7419_MIDDLE_Q_FACTOR 5 +#define TDA7419_MIDDLE_SOFT_STEP 7 + +#define TDA7419_BASS_Q_FACTOR 5 +#define TDA7419_BASS_SOFT_STEP 7 + +#define TDA7419_SECOND_SRC_SEL 0 +#define TDA7419_SECOND_SRC_GAIN 3 +#define TDA7419_REAR_SPKR_SRC 7 + +#define TDA7419_SUB_CUT_OFF_FREQ 0 +#define TDA7419_MIDDLE_CENTER_FREQ 2 +#define TDA7419_BASS_CENTER_FREQ 4 +#define TDA7419_BASS_DC_MODE 6 +#define TDA7419_SMOOTHING_FILTER 7 + +#define TDA7419_MIX_LF 0 +#define TDA7419_MIX_RF 1 +#define TDA7419_MIX_ENABLE 2 +#define TDA7419_SUB_ENABLE 3 +#define TDA7419_HPF_GAIN 4 + +#define TDA7419_SA_Q_FACTOR 0 +#define TDA7419_RESET_MODE 1 +#define TDA7419_SA_SOURCE 2 +#define TDA7419_SA_RUN 3 +#define TDA7419_RESET 4 +#define TDA7419_CLK_SOURCE 5 +#define TDA7419_COUPLING_MODE 6 + +struct tda7419_data { + struct regmap *regmap; +}; + +static bool tda7419_readable_reg(struct device *dev, unsigned int reg) +{ + return false; +} + +static const struct reg_default tda7419_regmap_defaults[] = { + { TDA7419_MAIN_SRC_REG, 0xfe }, + { TDA7419_LOUDNESS_REG, 0xfe }, + { TDA7419_MUTE_CLK_REG, 0xfe }, + { TDA7419_VOLUME_REG, 0xfe }, + { TDA7419_TREBLE_REG, 0xfe }, + { TDA7419_MIDDLE_REG, 0xfe }, + { TDA7419_BASS_REG, 0xfe }, + { TDA7419_SECOND_SRC_REG, 0xfe }, + { TDA7419_SUB_MID_BASS_REG, 0xfe }, + { TDA7419_MIXING_GAIN_REG, 0xfe }, + { TDA7419_ATTENUATOR_LF_REG, 0xfe }, + { TDA7419_ATTENUATOR_RF_REG, 0xfe }, + { TDA7419_ATTENUATOR_LR_REG, 0xfe }, + { TDA7419_ATTENUATOR_RR_REG, 0xfe }, + { TDA7419_MIXING_LEVEL_REG, 0xfe }, + { TDA7419_ATTENUATOR_SUB_REG, 0xfe }, + { TDA7419_SA_CLK_AC_REG, 0xfe }, + { TDA7419_TESTING_REG, 0xfe }, +}; + +static const struct regmap_config tda7419_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = TDA7419_TESTING_REG, + .cache_type = REGCACHE_RBTREE, + .readable_reg = tda7419_readable_reg, + .reg_defaults = tda7419_regmap_defaults, + .num_reg_defaults = ARRAY_SIZE(tda7419_regmap_defaults), +}; + +struct tda7419_vol_control { + int min, max; + unsigned int reg, rreg, mask, thresh; + unsigned int invert:1; +}; + +static inline bool tda7419_vol_is_stereo(struct tda7419_vol_control *tvc) +{ + if (tvc->reg == tvc->rreg) + return false; + + return true; +} + +static int tda7419_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct tda7419_vol_control *tvc = + (struct tda7419_vol_control *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = tda7419_vol_is_stereo(tvc) ? 2 : 1; + uinfo->value.integer.min = tvc->min; + uinfo->value.integer.max = tvc->max; + + return 0; +} + +static inline int tda7419_vol_get_value(int val, unsigned int mask, + int min, int thresh, + unsigned int invert) +{ + val &= mask; + if (val < thresh) { + if (invert) + val = 0 - val; + } else if (val > thresh) { + if (invert) + val = val - thresh; + else + val = thresh - val; + } + + if (val < min) + val = min; + + return val; +} + +static int tda7419_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct tda7419_vol_control *tvc = + (struct tda7419_vol_control *)kcontrol->private_value; + unsigned int reg = tvc->reg; + unsigned int rreg = tvc->rreg; + unsigned int mask = tvc->mask; + int min = tvc->min; + int thresh = tvc->thresh; + unsigned int invert = tvc->invert; + int val; + + val = snd_soc_component_read(component, reg); + ucontrol->value.integer.value[0] = + tda7419_vol_get_value(val, mask, min, thresh, invert); + + if (tda7419_vol_is_stereo(tvc)) { + val = snd_soc_component_read(component, rreg); + ucontrol->value.integer.value[1] = + tda7419_vol_get_value(val, mask, min, thresh, invert); + } + + return 0; +} + +static inline int tda7419_vol_put_value(int val, int thresh, + unsigned int invert) +{ + if (val < 0) { + if (invert) + val = abs(val); + else + val = thresh - val; + } else if ((val > 0) && invert) { + val += thresh; + } + + return val; +} + +static int tda7419_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_kcontrol_chip(kcontrol); + struct tda7419_vol_control *tvc = + (struct tda7419_vol_control *)kcontrol->private_value; + unsigned int reg = tvc->reg; + unsigned int rreg = tvc->rreg; + unsigned int mask = tvc->mask; + int thresh = tvc->thresh; + unsigned int invert = tvc->invert; + int val; + int ret; + + val = tda7419_vol_put_value(ucontrol->value.integer.value[0], + thresh, invert); + ret = snd_soc_component_update_bits(component, reg, + mask, val); + if (ret < 0) + return ret; + + if (tda7419_vol_is_stereo(tvc)) { + val = tda7419_vol_put_value(ucontrol->value.integer.value[1], + thresh, invert); + ret = snd_soc_component_update_bits(component, rreg, + mask, val); + } + + return ret; +} + +#define TDA7419_SINGLE_VALUE(xreg, xmask, xmin, xmax, xthresh, xinvert) \ + ((unsigned long)&(struct tda7419_vol_control) \ + {.reg = xreg, .rreg = xreg, .mask = xmask, .min = xmin, \ + .max = xmax, .thresh = xthresh, .invert = xinvert}) + +#define TDA7419_DOUBLE_R_VALUE(xregl, xregr, xmask, xmin, xmax, xthresh, \ + xinvert) \ + ((unsigned long)&(struct tda7419_vol_control) \ + {.reg = xregl, .rreg = xregr, .mask = xmask, .min = xmin, \ + .max = xmax, .thresh = xthresh, .invert = xinvert}) + +#define TDA7419_SINGLE_TLV(xname, xreg, xmask, xmin, xmax, xthresh, \ + xinvert, xtlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (xtlv_array), \ + .info = tda7419_vol_info, \ + .get = tda7419_vol_get, \ + .put = tda7419_vol_put, \ + .private_value = TDA7419_SINGLE_VALUE(xreg, xmask, xmin, \ + xmax, xthresh, xinvert), \ +} + +#define TDA7419_DOUBLE_R_TLV(xname, xregl, xregr, xmask, xmin, xmax, \ + xthresh, xinvert, xtlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (xtlv_array), \ + .info = tda7419_vol_info, \ + .get = tda7419_vol_get, \ + .put = tda7419_vol_put, \ + .private_value = TDA7419_DOUBLE_R_VALUE(xregl, xregr, xmask, \ + xmin, xmax, xthresh, \ + xinvert), \ +} + +static const char * const enum_src_sel[] = { + "QD", "SE1", "SE2", "SE3", "SE", "Mute", "Mute", "Mute"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_main_src_sel, + TDA7419_MAIN_SRC_REG, TDA7419_MAIN_SRC_SEL, enum_src_sel); +static const struct snd_kcontrol_new soc_mux_main_src_sel = + SOC_DAPM_ENUM("Main Source Select", soc_enum_main_src_sel); +static DECLARE_TLV_DB_SCALE(tlv_src_gain, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(tlv_loudness_atten, -1500, 100, 0); +static const char * const enum_loudness_center_freq[] = { + "Flat", "400 Hz", "800 Hz", "2400 Hz"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_loudness_center_freq, + TDA7419_LOUDNESS_REG, TDA7419_LOUDNESS_CENTER_FREQ, + enum_loudness_center_freq); +static const char * const enum_mute_influence[] = { + "Pin and IIC", "IIC"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_mute_influence, + TDA7419_MUTE_CLK_REG, TDA7419_MUTE_INFLUENCE, enum_mute_influence); +static const char * const enum_soft_mute_time[] = { + "0.48 ms", "0.96 ms", "123 ms", "123 ms"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_soft_mute_time, + TDA7419_MUTE_CLK_REG, TDA7419_SOFT_MUTE_TIME, enum_soft_mute_time); +static const char * const enum_soft_step_time[] = { + "0.160 ms", "0.321 ms", "0.642 ms", "1.28 ms", + "2.56 ms", "5.12 ms", "10.24 ms", "20.48 ms"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_soft_step_time, + TDA7419_MUTE_CLK_REG, TDA7419_SOFT_STEP_TIME, enum_soft_step_time); +static DECLARE_TLV_DB_SCALE(tlv_volume, -8000, 100, 1); +static const char * const enum_treble_center_freq[] = { + "10.0 kHz", "12.5 kHz", "15.0 kHz", "17.5 kHz"}; +static DECLARE_TLV_DB_SCALE(tlv_filter, -1500, 100, 0); +static SOC_ENUM_SINGLE_DECL(soc_enum_treble_center_freq, + TDA7419_TREBLE_REG, TDA7419_TREBLE_CENTER_FREQ, + enum_treble_center_freq); +static const char * const enum_ref_out_select[] = { + "External Vref (4 V)", "Internal Vref (3.3 V)"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_ref_out_select, + TDA7419_TREBLE_REG, TDA7419_REF_OUT_SELECT, enum_ref_out_select); +static const char * const enum_middle_q_factor[] = { + "0.5", "0.75", "1.0", "1.25"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_middle_q_factor, + TDA7419_MIDDLE_REG, TDA7419_MIDDLE_Q_FACTOR, enum_middle_q_factor); +static const char * const enum_bass_q_factor[] = { + "1.0", "1.25", "1.5", "2.0"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_bass_q_factor, + TDA7419_BASS_REG, TDA7419_BASS_Q_FACTOR, enum_bass_q_factor); +static SOC_ENUM_SINGLE_DECL(soc_enum_second_src_sel, + TDA7419_SECOND_SRC_REG, TDA7419_SECOND_SRC_SEL, enum_src_sel); +static const struct snd_kcontrol_new soc_mux_second_src_sel = + SOC_DAPM_ENUM("Second Source Select", soc_enum_second_src_sel); +static const char * const enum_rear_spkr_src[] = { + "Main", "Second"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_rear_spkr_src, + TDA7419_SECOND_SRC_REG, TDA7419_REAR_SPKR_SRC, enum_rear_spkr_src); +static const struct snd_kcontrol_new soc_mux_rear_spkr_src = + SOC_DAPM_ENUM("Rear Speaker Source", soc_enum_rear_spkr_src); +static const char * const enum_sub_cut_off_freq[] = { + "Flat", "80 Hz", "120 Hz", "160 Hz"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_sub_cut_off_freq, + TDA7419_SUB_MID_BASS_REG, TDA7419_SUB_CUT_OFF_FREQ, + enum_sub_cut_off_freq); +static const char * const enum_middle_center_freq[] = { + "500 Hz", "1000 Hz", "1500 Hz", "2500 Hz"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_middle_center_freq, + TDA7419_SUB_MID_BASS_REG, TDA7419_MIDDLE_CENTER_FREQ, + enum_middle_center_freq); +static const char * const enum_bass_center_freq[] = { + "60 Hz", "80 Hz", "100 Hz", "200 Hz"}; +static SOC_ENUM_SINGLE_DECL(soc_enum_bass_center_freq, + TDA7419_SUB_MID_BASS_REG, TDA7419_BASS_CENTER_FREQ, + enum_bass_center_freq); +static const char * const enum_sa_q_factor[] = { + "3.5", "1.75" }; +static SOC_ENUM_SINGLE_DECL(soc_enum_sa_q_factor, + TDA7419_SA_CLK_AC_REG, TDA7419_SA_Q_FACTOR, enum_sa_q_factor); +static const char * const enum_reset_mode[] = { + "IIC", "Auto" }; +static SOC_ENUM_SINGLE_DECL(soc_enum_reset_mode, + TDA7419_SA_CLK_AC_REG, TDA7419_RESET_MODE, enum_reset_mode); +static const char * const enum_sa_src[] = { + "Bass", "In Gain" }; +static SOC_ENUM_SINGLE_DECL(soc_enum_sa_src, + TDA7419_SA_CLK_AC_REG, TDA7419_SA_SOURCE, enum_sa_src); +static const char * const enum_clk_src[] = { + "Internal", "External" }; +static SOC_ENUM_SINGLE_DECL(soc_enum_clk_src, + TDA7419_SA_CLK_AC_REG, TDA7419_CLK_SOURCE, enum_clk_src); +static const char * const enum_coupling_mode[] = { + "DC Coupling (without HPF)", "AC Coupling after In Gain", + "DC Coupling (with HPF)", "AC Coupling after Bass" }; +static SOC_ENUM_SINGLE_DECL(soc_enum_coupling_mode, + TDA7419_SA_CLK_AC_REG, TDA7419_COUPLING_MODE, enum_coupling_mode); + +/* ASoC Controls */ +static struct snd_kcontrol_new tda7419_controls[] = { +SOC_SINGLE_TLV("Main Source Capture Volume", TDA7419_MAIN_SRC_REG, + TDA7419_MAIN_SRC_GAIN, 15, 0, tlv_src_gain), +SOC_SINGLE("Main Source AutoZero Switch", TDA7419_MAIN_SRC_REG, + TDA7419_MAIN_SRC_AUTOZERO, 1, 1), +SOC_SINGLE_TLV("Loudness Playback Volume", TDA7419_LOUDNESS_REG, + TDA7419_LOUDNESS_ATTEN, 15, 1, tlv_loudness_atten), +SOC_ENUM("Loudness Center Frequency", soc_enum_loudness_center_freq), +SOC_SINGLE("Loudness High Boost Switch", TDA7419_LOUDNESS_REG, + TDA7419_LOUDNESS_BOOST, 1, 1), +SOC_SINGLE("Loudness Soft Step Switch", TDA7419_LOUDNESS_REG, + TDA7419_LOUDNESS_SOFT_STEP, 1, 1), +SOC_SINGLE("Soft Mute Switch", TDA7419_MUTE_CLK_REG, TDA7419_SOFT_MUTE, 1, 1), +SOC_ENUM("Mute Influence", soc_enum_mute_influence), +SOC_ENUM("Soft Mute Time", soc_enum_soft_mute_time), +SOC_ENUM("Soft Step Time", soc_enum_soft_step_time), +SOC_SINGLE("Clock Fast Mode Switch", TDA7419_MUTE_CLK_REG, + TDA7419_CLK_FAST_MODE, 1, 1), +TDA7419_SINGLE_TLV("Master Playback Volume", TDA7419_VOLUME_REG, + 0x7f, -80, 15, 0x10, 0, tlv_volume), +SOC_SINGLE("Volume Soft Step Switch", TDA7419_VOLUME_REG, + TDA7419_VOLUME_SOFT_STEP, 1, 1), +TDA7419_SINGLE_TLV("Treble Playback Volume", TDA7419_TREBLE_REG, + 0x1f, -15, 15, 0x10, 1, tlv_filter), +SOC_ENUM("Treble Center Frequency", soc_enum_treble_center_freq), +SOC_ENUM("Reference Output Select", soc_enum_ref_out_select), +TDA7419_SINGLE_TLV("Middle Playback Volume", TDA7419_MIDDLE_REG, + 0x1f, -15, 15, 0x10, 1, tlv_filter), +SOC_ENUM("Middle Q Factor", soc_enum_middle_q_factor), +SOC_SINGLE("Middle Soft Step Switch", TDA7419_MIDDLE_REG, + TDA7419_MIDDLE_SOFT_STEP, 1, 1), +TDA7419_SINGLE_TLV("Bass Playback Volume", TDA7419_BASS_REG, + 0x1f, -15, 15, 0x10, 1, tlv_filter), +SOC_ENUM("Bass Q Factor", soc_enum_bass_q_factor), +SOC_SINGLE("Bass Soft Step Switch", TDA7419_BASS_REG, + TDA7419_BASS_SOFT_STEP, 1, 1), +SOC_SINGLE_TLV("Second Source Capture Volume", TDA7419_SECOND_SRC_REG, + TDA7419_SECOND_SRC_GAIN, 15, 0, tlv_src_gain), +SOC_ENUM("Subwoofer Cut-off Frequency", soc_enum_sub_cut_off_freq), +SOC_ENUM("Middle Center Frequency", soc_enum_middle_center_freq), +SOC_ENUM("Bass Center Frequency", soc_enum_bass_center_freq), +SOC_SINGLE("Bass DC Mode Switch", TDA7419_SUB_MID_BASS_REG, + TDA7419_BASS_DC_MODE, 1, 1), +SOC_SINGLE("Smoothing Filter Switch", TDA7419_SUB_MID_BASS_REG, + TDA7419_SMOOTHING_FILTER, 1, 1), +TDA7419_DOUBLE_R_TLV("Front Speaker Playback Volume", TDA7419_ATTENUATOR_LF_REG, + TDA7419_ATTENUATOR_RF_REG, 0x7f, -80, 15, 0x10, 0, + tlv_volume), +SOC_SINGLE("Left Front Soft Step Switch", TDA7419_ATTENUATOR_LF_REG, + TDA7419_VOLUME_SOFT_STEP, 1, 1), +SOC_SINGLE("Right Front Soft Step Switch", TDA7419_ATTENUATOR_RF_REG, + TDA7419_VOLUME_SOFT_STEP, 1, 1), +TDA7419_DOUBLE_R_TLV("Rear Speaker Playback Volume", TDA7419_ATTENUATOR_LR_REG, + TDA7419_ATTENUATOR_RR_REG, 0x7f, -80, 15, 0x10, 0, + tlv_volume), +SOC_SINGLE("Left Rear Soft Step Switch", TDA7419_ATTENUATOR_LR_REG, + TDA7419_VOLUME_SOFT_STEP, 1, 1), +SOC_SINGLE("Right Rear Soft Step Switch", TDA7419_ATTENUATOR_RR_REG, + TDA7419_VOLUME_SOFT_STEP, 1, 1), +TDA7419_SINGLE_TLV("Mixing Capture Volume", TDA7419_MIXING_LEVEL_REG, + 0x7f, -80, 15, 0x10, 0, tlv_volume), +SOC_SINGLE("Mixing Level Soft Step Switch", TDA7419_MIXING_LEVEL_REG, + TDA7419_VOLUME_SOFT_STEP, 1, 1), +TDA7419_SINGLE_TLV("Subwoofer Playback Volume", TDA7419_ATTENUATOR_SUB_REG, + 0x7f, -80, 15, 0x10, 0, tlv_volume), +SOC_SINGLE("Subwoofer Soft Step Switch", TDA7419_ATTENUATOR_SUB_REG, + TDA7419_VOLUME_SOFT_STEP, 1, 1), +SOC_ENUM("Spectrum Analyzer Q Factor", soc_enum_sa_q_factor), +SOC_ENUM("Spectrum Analyzer Reset Mode", soc_enum_reset_mode), +SOC_ENUM("Spectrum Analyzer Source", soc_enum_sa_src), +SOC_SINGLE("Spectrum Analyzer Run Switch", TDA7419_SA_CLK_AC_REG, + TDA7419_SA_RUN, 1, 1), +SOC_SINGLE("Spectrum Analyzer Reset Switch", TDA7419_SA_CLK_AC_REG, + TDA7419_RESET, 1, 1), +SOC_ENUM("Clock Source", soc_enum_clk_src), +SOC_ENUM("Coupling Mode", soc_enum_coupling_mode), +}; + +static const struct snd_kcontrol_new soc_mixer_lf_output_controls[] = { + SOC_DAPM_SINGLE("Mix to LF Speaker Switch", + TDA7419_MIXING_GAIN_REG, + TDA7419_MIX_LF, 1, 1), +}; + +static const struct snd_kcontrol_new soc_mixer_rf_output_controls[] = { + SOC_DAPM_SINGLE("Mix to RF Speaker Switch", + TDA7419_MIXING_GAIN_REG, + TDA7419_MIX_RF, 1, 1), +}; + +static const struct snd_kcontrol_new soc_mix_enable_switch_controls[] = { + SOC_DAPM_SINGLE("Switch", TDA7419_MIXING_GAIN_REG, + TDA7419_MIX_ENABLE, 1, 1), +}; + +static const struct snd_kcontrol_new soc_sub_enable_switch_controls[] = { + SOC_DAPM_SINGLE("Switch", TDA7419_MIXING_GAIN_REG, + TDA7419_MIX_ENABLE, 1, 1), +}; + +static const struct snd_soc_dapm_widget tda7419_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("SE3L"), + SND_SOC_DAPM_INPUT("SE3R"), + SND_SOC_DAPM_INPUT("SE2L"), + SND_SOC_DAPM_INPUT("SE2R"), + SND_SOC_DAPM_INPUT("SE1L"), + SND_SOC_DAPM_INPUT("SE1R"), + SND_SOC_DAPM_INPUT("DIFFL"), + SND_SOC_DAPM_INPUT("DIFFR"), + SND_SOC_DAPM_INPUT("MIX"), + + SND_SOC_DAPM_MUX("Main Source Select", SND_SOC_NOPM, + 0, 0, &soc_mux_main_src_sel), + SND_SOC_DAPM_MUX("Second Source Select", SND_SOC_NOPM, + 0, 0, &soc_mux_second_src_sel), + SND_SOC_DAPM_MUX("Rear Speaker Source", SND_SOC_NOPM, + 0, 0, &soc_mux_rear_spkr_src), + + SND_SOC_DAPM_SWITCH("Mix Enable", SND_SOC_NOPM, + 0, 0, &soc_mix_enable_switch_controls[0]), + SND_SOC_DAPM_MIXER_NAMED_CTL("LF Output Mixer", SND_SOC_NOPM, + 0, 0, &soc_mixer_lf_output_controls[0], + ARRAY_SIZE(soc_mixer_lf_output_controls)), + SND_SOC_DAPM_MIXER_NAMED_CTL("RF Output Mixer", SND_SOC_NOPM, + 0, 0, &soc_mixer_rf_output_controls[0], + ARRAY_SIZE(soc_mixer_rf_output_controls)), + + SND_SOC_DAPM_SWITCH("Subwoofer Enable", + SND_SOC_NOPM, 0, 0, + &soc_sub_enable_switch_controls[0]), + + SND_SOC_DAPM_OUTPUT("OUTLF"), + SND_SOC_DAPM_OUTPUT("OUTRF"), + SND_SOC_DAPM_OUTPUT("OUTLR"), + SND_SOC_DAPM_OUTPUT("OUTRR"), + SND_SOC_DAPM_OUTPUT("OUTSW"), +}; + +static const struct snd_soc_dapm_route tda7419_dapm_routes[] = { + {"Main Source Select", "SE3", "SE3L"}, + {"Main Source Select", "SE3", "SE3R"}, + {"Main Source Select", "SE2", "SE2L"}, + {"Main Source Select", "SE2", "SE2R"}, + {"Main Source Select", "SE1", "SE1L"}, + {"Main Source Select", "SE1", "SE1R"}, + {"Main Source Select", "SE", "DIFFL"}, + {"Main Source Select", "SE", "DIFFR"}, + {"Main Source Select", "QD", "DIFFL"}, + {"Main Source Select", "QD", "DIFFR"}, + + {"Second Source Select", "SE3", "SE3L"}, + {"Second Source Select", "SE3", "SE3R"}, + {"Second Source Select", "SE2", "SE2L"}, + {"Second Source Select", "SE2", "SE2R"}, + {"Second Source Select", "SE1", "SE1L"}, + {"Second Source Select", "SE1", "SE1R"}, + {"Second Source Select", "SE", "DIFFL"}, + {"Second Source Select", "SE", "DIFFR"}, + {"Second Source Select", "QD", "DIFFL"}, + {"Second Source Select", "QD", "DIFFR"}, + + {"Rear Speaker Source", "Main", "Main Source Select"}, + {"Rear Speaker Source", "Second", "Second Source Select"}, + + {"Subwoofer Enable", "Switch", "Main Source Select"}, + + {"Mix Enable", "Switch", "MIX"}, + + {"LF Output Mixer", NULL, "Main Source Select"}, + {"LF Output Mixer", "Mix to LF Speaker Switch", "Mix Enable"}, + {"RF Output Mixer", NULL, "Main Source Select"}, + {"RF Output Mixer", "Mix to RF Speaker Switch", "Mix Enable"}, + + {"OUTLF", NULL, "LF Output Mixer"}, + {"OUTRF", NULL, "RF Output Mixer"}, + {"OUTLR", NULL, "Rear Speaker Source"}, + {"OUTRR", NULL, "Rear Speaker Source"}, + {"OUTSW", NULL, "Subwoofer Enable"}, +}; + +static const struct snd_soc_component_driver tda7419_component_driver = { + .name = "tda7419", + .controls = tda7419_controls, + .num_controls = ARRAY_SIZE(tda7419_controls), + .dapm_widgets = tda7419_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tda7419_dapm_widgets), + .dapm_routes = tda7419_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tda7419_dapm_routes), +}; + +static int tda7419_probe(struct i2c_client *i2c) +{ + struct tda7419_data *tda7419; + int i, ret; + + tda7419 = devm_kzalloc(&i2c->dev, + sizeof(struct tda7419_data), + GFP_KERNEL); + if (tda7419 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, tda7419); + + tda7419->regmap = devm_regmap_init_i2c(i2c, &tda7419_regmap_config); + if (IS_ERR(tda7419->regmap)) { + ret = PTR_ERR(tda7419->regmap); + dev_err(&i2c->dev, "error initializing regmap: %d\n", + ret); + return ret; + } + + /* + * Reset registers to power-on defaults. The part does not provide a + * soft-reset function and the registers are not readable. This ensures + * that the cache matches register contents even if the registers have + * been previously initialized and not power cycled before probe. + */ + for (i = 0; i < ARRAY_SIZE(tda7419_regmap_defaults); i++) + regmap_write(tda7419->regmap, + tda7419_regmap_defaults[i].reg, + tda7419_regmap_defaults[i].def); + + ret = devm_snd_soc_register_component(&i2c->dev, + &tda7419_component_driver, NULL, 0); + if (ret < 0) { + dev_err(&i2c->dev, "error registering component: %d\n", + ret); + } + + return ret; +} + +static const struct i2c_device_id tda7419_i2c_id[] = { + { "tda7419", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tda7419_i2c_id); + +static const struct of_device_id tda7419_of_match[] = { + { .compatible = "st,tda7419" }, + { }, +}; + +static struct i2c_driver tda7419_driver = { + .driver = { + .name = "tda7419", + .of_match_table = tda7419_of_match, + }, + .probe = tda7419_probe, + .id_table = tda7419_i2c_id, +}; + +module_i2c_driver(tda7419_driver); + +MODULE_AUTHOR("Matt Porter "); +MODULE_DESCRIPTION("TDA7419 audio processor driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c new file mode 100644 index 0000000000..8cca2ceadd --- /dev/null +++ b/sound/soc/codecs/tfa9879.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// tfa9879.c -- driver for NXP Semiconductors TFA9879 +// +// Copyright (C) 2014 Axentia Technologies AB +// Author: Peter Rosin + +#include +#include +#include +#include +#include +#include +#include + +#include "tfa9879.h" + +struct tfa9879_priv { + struct regmap *regmap; + int lsb_justified; +}; + +static int tfa9879_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tfa9879_priv *tfa9879 = snd_soc_component_get_drvdata(component); + int fs; + int i2s_set = 0; + + switch (params_rate(params)) { + case 8000: + fs = TFA9879_I2S_FS_8000; + break; + case 11025: + fs = TFA9879_I2S_FS_11025; + break; + case 12000: + fs = TFA9879_I2S_FS_12000; + break; + case 16000: + fs = TFA9879_I2S_FS_16000; + break; + case 22050: + fs = TFA9879_I2S_FS_22050; + break; + case 24000: + fs = TFA9879_I2S_FS_24000; + break; + case 32000: + fs = TFA9879_I2S_FS_32000; + break; + case 44100: + fs = TFA9879_I2S_FS_44100; + break; + case 48000: + fs = TFA9879_I2S_FS_48000; + break; + case 64000: + fs = TFA9879_I2S_FS_64000; + break; + case 88200: + fs = TFA9879_I2S_FS_88200; + break; + case 96000: + fs = TFA9879_I2S_FS_96000; + break; + default: + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + i2s_set = TFA9879_I2S_SET_LSB_J_16; + break; + case 24: + i2s_set = TFA9879_I2S_SET_LSB_J_24; + break; + default: + return -EINVAL; + } + + if (tfa9879->lsb_justified) + snd_soc_component_update_bits(component, + TFA9879_SERIAL_INTERFACE_1, + TFA9879_I2S_SET_MASK, + i2s_set << TFA9879_I2S_SET_SHIFT); + + snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1, + TFA9879_I2S_FS_MASK, + fs << TFA9879_I2S_FS_SHIFT); + return 0; +} + +static int tfa9879_mute_stream(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + snd_soc_component_update_bits(component, TFA9879_MISC_CONTROL, + TFA9879_S_MUTE_MASK, + !!mute << TFA9879_S_MUTE_SHIFT); + + return 0; +} + +static int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tfa9879_priv *tfa9879 = snd_soc_component_get_drvdata(component); + int i2s_set; + int sck_pol; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + sck_pol = TFA9879_SCK_POL_NORMAL; + break; + case SND_SOC_DAIFMT_IB_NF: + sck_pol = TFA9879_SCK_POL_INVERSE; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + tfa9879->lsb_justified = 0; + i2s_set = TFA9879_I2S_SET_I2S_24; + break; + case SND_SOC_DAIFMT_LEFT_J: + tfa9879->lsb_justified = 0; + i2s_set = TFA9879_I2S_SET_MSB_J_24; + break; + case SND_SOC_DAIFMT_RIGHT_J: + tfa9879->lsb_justified = 1; + i2s_set = TFA9879_I2S_SET_LSB_J_24; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1, + TFA9879_SCK_POL_MASK, + sck_pol << TFA9879_SCK_POL_SHIFT); + snd_soc_component_update_bits(component, TFA9879_SERIAL_INTERFACE_1, + TFA9879_I2S_SET_MASK, + i2s_set << TFA9879_I2S_SET_SHIFT); + return 0; +} + +static const struct reg_default tfa9879_regs[] = { + { TFA9879_DEVICE_CONTROL, 0x0000 }, /* 0x00 */ + { TFA9879_SERIAL_INTERFACE_1, 0x0a18 }, /* 0x01 */ + { TFA9879_PCM_IOM2_FORMAT_1, 0x0007 }, /* 0x02 */ + { TFA9879_SERIAL_INTERFACE_2, 0x0a18 }, /* 0x03 */ + { TFA9879_PCM_IOM2_FORMAT_2, 0x0007 }, /* 0x04 */ + { TFA9879_EQUALIZER_A1, 0x59dd }, /* 0x05 */ + { TFA9879_EQUALIZER_A2, 0xc63e }, /* 0x06 */ + { TFA9879_EQUALIZER_B1, 0x651a }, /* 0x07 */ + { TFA9879_EQUALIZER_B2, 0xe53e }, /* 0x08 */ + { TFA9879_EQUALIZER_C1, 0x4616 }, /* 0x09 */ + { TFA9879_EQUALIZER_C2, 0xd33e }, /* 0x0a */ + { TFA9879_EQUALIZER_D1, 0x4df3 }, /* 0x0b */ + { TFA9879_EQUALIZER_D2, 0xea3e }, /* 0x0c */ + { TFA9879_EQUALIZER_E1, 0x5ee0 }, /* 0x0d */ + { TFA9879_EQUALIZER_E2, 0xf93e }, /* 0x0e */ + { TFA9879_BYPASS_CONTROL, 0x0093 }, /* 0x0f */ + { TFA9879_DYNAMIC_RANGE_COMPR, 0x92ba }, /* 0x10 */ + { TFA9879_BASS_TREBLE, 0x12a5 }, /* 0x11 */ + { TFA9879_HIGH_PASS_FILTER, 0x0004 }, /* 0x12 */ + { TFA9879_VOLUME_CONTROL, 0x10bd }, /* 0x13 */ + { TFA9879_MISC_CONTROL, 0x0000 }, /* 0x14 */ +}; + +static bool tfa9879_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == TFA9879_MISC_STATUS; +} + +static const DECLARE_TLV_DB_SCALE(volume_tlv, -7050, 50, 1); +static const DECLARE_TLV_DB_SCALE(tb_gain_tlv, -1800, 200, 0); +static const char * const tb_freq_text[] = { + "Low", "Mid", "High" +}; +static const struct soc_enum treble_freq_enum = + SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_TRBLE_SHIFT, + ARRAY_SIZE(tb_freq_text), tb_freq_text); +static const struct soc_enum bass_freq_enum = + SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_BASS_SHIFT, + ARRAY_SIZE(tb_freq_text), tb_freq_text); + +static const struct snd_kcontrol_new tfa9879_controls[] = { + SOC_SINGLE_TLV("PCM Playback Volume", TFA9879_VOLUME_CONTROL, + TFA9879_VOL_SHIFT, 0xbd, 1, volume_tlv), + SOC_SINGLE_TLV("Treble Volume", TFA9879_BASS_TREBLE, + TFA9879_G_TRBLE_SHIFT, 18, 0, tb_gain_tlv), + SOC_SINGLE_TLV("Bass Volume", TFA9879_BASS_TREBLE, + TFA9879_G_BASS_SHIFT, 18, 0, tb_gain_tlv), + SOC_ENUM("Treble Corner Freq", treble_freq_enum), + SOC_ENUM("Bass Corner Freq", bass_freq_enum), +}; + +static const struct snd_soc_dapm_widget tfa9879_dapm_widgets[] = { +SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_DAC("DAC", NULL, TFA9879_DEVICE_CONTROL, TFA9879_OPMODE_SHIFT, 0), +SND_SOC_DAPM_OUTPUT("LINEOUT"), +SND_SOC_DAPM_SUPPLY("POWER", TFA9879_DEVICE_CONTROL, TFA9879_POWERUP_SHIFT, 0, + NULL, 0), +}; + +static const struct snd_soc_dapm_route tfa9879_dapm_routes[] = { + { "DAC", NULL, "AIFINL" }, + { "DAC", NULL, "AIFINR" }, + + { "LINEOUT", NULL, "DAC" }, + + { "DAC", NULL, "POWER" }, +}; + +static const struct snd_soc_component_driver tfa9879_component = { + .controls = tfa9879_controls, + .num_controls = ARRAY_SIZE(tfa9879_controls), + .dapm_widgets = tfa9879_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tfa9879_dapm_widgets), + .dapm_routes = tfa9879_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tfa9879_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config tfa9879_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .volatile_reg = tfa9879_volatile_reg, + .max_register = TFA9879_MISC_STATUS, + .reg_defaults = tfa9879_regs, + .num_reg_defaults = ARRAY_SIZE(tfa9879_regs), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct snd_soc_dai_ops tfa9879_dai_ops = { + .hw_params = tfa9879_hw_params, + .mute_stream = tfa9879_mute_stream, + .set_fmt = tfa9879_set_fmt, + .no_capture_mute = 1, +}; + +#define TFA9879_RATES SNDRV_PCM_RATE_8000_96000 + +#define TFA9879_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_driver tfa9879_dai = { + .name = "tfa9879-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = TFA9879_RATES, + .formats = TFA9879_FORMATS, }, + .ops = &tfa9879_dai_ops, +}; + +static int tfa9879_i2c_probe(struct i2c_client *i2c) +{ + struct tfa9879_priv *tfa9879; + int i; + + tfa9879 = devm_kzalloc(&i2c->dev, sizeof(*tfa9879), GFP_KERNEL); + if (!tfa9879) + return -ENOMEM; + + i2c_set_clientdata(i2c, tfa9879); + + tfa9879->regmap = devm_regmap_init_i2c(i2c, &tfa9879_regmap); + if (IS_ERR(tfa9879->regmap)) + return PTR_ERR(tfa9879->regmap); + + /* Ensure the device is in reset state */ + for (i = 0; i < ARRAY_SIZE(tfa9879_regs); i++) + regmap_write(tfa9879->regmap, + tfa9879_regs[i].reg, tfa9879_regs[i].def); + + return devm_snd_soc_register_component(&i2c->dev, &tfa9879_component, + &tfa9879_dai, 1); +} + +static const struct i2c_device_id tfa9879_i2c_id[] = { + { "tfa9879", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id); + +static const struct of_device_id tfa9879_of_match[] = { + { .compatible = "nxp,tfa9879", }, + { } +}; +MODULE_DEVICE_TABLE(of, tfa9879_of_match); + +static struct i2c_driver tfa9879_i2c_driver = { + .driver = { + .name = "tfa9879", + .of_match_table = tfa9879_of_match, + }, + .probe = tfa9879_i2c_probe, + .id_table = tfa9879_i2c_id, +}; + +module_i2c_driver(tfa9879_i2c_driver); + +MODULE_DESCRIPTION("ASoC NXP Semiconductors TFA9879 driver"); +MODULE_AUTHOR("Peter Rosin "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tfa9879.h b/sound/soc/codecs/tfa9879.h new file mode 100644 index 0000000000..66c88d0396 --- /dev/null +++ b/sound/soc/codecs/tfa9879.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * tfa9879.h -- driver for NXP Semiconductors TFA9879 + * + * Copyright (C) 2014 Axentia Technologies AB + * Author: Peter Rosin + */ + +#ifndef _TFA9879_H +#define _TFA9879_H + +#define TFA9879_DEVICE_CONTROL 0x00 +#define TFA9879_SERIAL_INTERFACE_1 0x01 +#define TFA9879_PCM_IOM2_FORMAT_1 0x02 +#define TFA9879_SERIAL_INTERFACE_2 0x03 +#define TFA9879_PCM_IOM2_FORMAT_2 0x04 +#define TFA9879_EQUALIZER_A1 0x05 +#define TFA9879_EQUALIZER_A2 0x06 +#define TFA9879_EQUALIZER_B1 0x07 +#define TFA9879_EQUALIZER_B2 0x08 +#define TFA9879_EQUALIZER_C1 0x09 +#define TFA9879_EQUALIZER_C2 0x0a +#define TFA9879_EQUALIZER_D1 0x0b +#define TFA9879_EQUALIZER_D2 0x0c +#define TFA9879_EQUALIZER_E1 0x0d +#define TFA9879_EQUALIZER_E2 0x0e +#define TFA9879_BYPASS_CONTROL 0x0f +#define TFA9879_DYNAMIC_RANGE_COMPR 0x10 +#define TFA9879_BASS_TREBLE 0x11 +#define TFA9879_HIGH_PASS_FILTER 0x12 +#define TFA9879_VOLUME_CONTROL 0x13 +#define TFA9879_MISC_CONTROL 0x14 +#define TFA9879_MISC_STATUS 0x15 + +/* TFA9879_DEVICE_CONTROL */ +#define TFA9879_INPUT_SEL_MASK 0x0010 +#define TFA9879_INPUT_SEL_SHIFT 4 +#define TFA9879_OPMODE_MASK 0x0008 +#define TFA9879_OPMODE_SHIFT 3 +#define TFA9879_RESET_MASK 0x0002 +#define TFA9879_RESET_SHIFT 1 +#define TFA9879_POWERUP_MASK 0x0001 +#define TFA9879_POWERUP_SHIFT 0 + +/* TFA9879_SERIAL_INTERFACE */ +#define TFA9879_MONO_SEL_MASK 0x0c00 +#define TFA9879_MONO_SEL_SHIFT 10 +#define TFA9879_MONO_SEL_LEFT 0 +#define TFA9879_MONO_SEL_RIGHT 1 +#define TFA9879_MONO_SEL_BOTH 2 +#define TFA9879_I2S_FS_MASK 0x03c0 +#define TFA9879_I2S_FS_SHIFT 6 +#define TFA9879_I2S_FS_8000 0 +#define TFA9879_I2S_FS_11025 1 +#define TFA9879_I2S_FS_12000 2 +#define TFA9879_I2S_FS_16000 3 +#define TFA9879_I2S_FS_22050 4 +#define TFA9879_I2S_FS_24000 5 +#define TFA9879_I2S_FS_32000 6 +#define TFA9879_I2S_FS_44100 7 +#define TFA9879_I2S_FS_48000 8 +#define TFA9879_I2S_FS_64000 9 +#define TFA9879_I2S_FS_88200 10 +#define TFA9879_I2S_FS_96000 11 +#define TFA9879_I2S_SET_MASK 0x0038 +#define TFA9879_I2S_SET_SHIFT 3 +#define TFA9879_I2S_SET_MSB_J_24 2 +#define TFA9879_I2S_SET_I2S_24 3 +#define TFA9879_I2S_SET_LSB_J_16 4 +#define TFA9879_I2S_SET_LSB_J_18 5 +#define TFA9879_I2S_SET_LSB_J_20 6 +#define TFA9879_I2S_SET_LSB_J_24 7 +#define TFA9879_SCK_POL_MASK 0x0004 +#define TFA9879_SCK_POL_SHIFT 2 +#define TFA9879_SCK_POL_NORMAL 0 +#define TFA9879_SCK_POL_INVERSE 1 +#define TFA9879_I_MODE_MASK 0x0003 +#define TFA9879_I_MODE_SHIFT 0 +#define TFA9879_I_MODE_I2S 0 +#define TFA9879_I_MODE_PCM_IOM2_SHORT 1 +#define TFA9879_I_MODE_PCM_IOM2_LONG 2 + +/* TFA9879_PCM_IOM2_FORMAT */ +#define TFA9879_PCM_FS_MASK 0x0800 +#define TFA9879_PCM_FS_SHIFT 11 +#define TFA9879_A_LAW_MASK 0x0400 +#define TFA9879_A_LAW_SHIFT 10 +#define TFA9879_PCM_COMP_MASK 0x0200 +#define TFA9879_PCM_COMP_SHIFT 9 +#define TFA9879_PCM_DL_MASK 0x0100 +#define TFA9879_PCM_DL_SHIFT 8 +#define TFA9879_D1_SLOT_MASK 0x00f0 +#define TFA9879_D1_SLOT_SHIFT 4 +#define TFA9879_D2_SLOT_MASK 0x000f +#define TFA9879_D2_SLOT_SHIFT 0 + +/* TFA9879_EQUALIZER_X1 */ +#define TFA9879_T1_MASK 0x8000 +#define TFA9879_T1_SHIFT 15 +#define TFA9879_K1M_MASK 0x7ff0 +#define TFA9879_K1M_SHIFT 4 +#define TFA9879_K1E_MASK 0x000f +#define TFA9879_K1E_SHIFT 0 + +/* TFA9879_EQUALIZER_X2 */ +#define TFA9879_T2_MASK 0x8000 +#define TFA9879_T2_SHIFT 15 +#define TFA9879_K2M_MASK 0x7800 +#define TFA9879_K2M_SHIFT 11 +#define TFA9879_K2E_MASK 0x0700 +#define TFA9879_K2E_SHIFT 8 +#define TFA9879_K0_MASK 0x00fe +#define TFA9879_K0_SHIFT 1 +#define TFA9879_S_MASK 0x0001 +#define TFA9879_S_SHIFT 0 + +/* TFA9879_BYPASS_CONTROL */ +#define TFA9879_L_OCP_MASK 0x00c0 +#define TFA9879_L_OCP_SHIFT 6 +#define TFA9879_L_OTP_MASK 0x0030 +#define TFA9879_L_OTP_SHIFT 4 +#define TFA9879_CLIPCTRL_MASK 0x0008 +#define TFA9879_CLIPCTRL_SHIFT 3 +#define TFA9879_HPF_BP_MASK 0x0004 +#define TFA9879_HPF_BP_SHIFT 2 +#define TFA9879_DRC_BP_MASK 0x0002 +#define TFA9879_DRC_BP_SHIFT 1 +#define TFA9879_EQ_BP_MASK 0x0001 +#define TFA9879_EQ_BP_SHIFT 0 + +/* TFA9879_DYNAMIC_RANGE_COMPR */ +#define TFA9879_AT_LVL_MASK 0xf000 +#define TFA9879_AT_LVL_SHIFT 12 +#define TFA9879_AT_RATE_MASK 0x0f00 +#define TFA9879_AT_RATE_SHIFT 8 +#define TFA9879_RL_LVL_MASK 0x00f0 +#define TFA9879_RL_LVL_SHIFT 4 +#define TFA9879_RL_RATE_MASK 0x000f +#define TFA9879_RL_RATE_SHIFT 0 + +/* TFA9879_BASS_TREBLE */ +#define TFA9879_G_TRBLE_MASK 0x3e00 +#define TFA9879_G_TRBLE_SHIFT 9 +#define TFA9879_F_TRBLE_MASK 0x0180 +#define TFA9879_F_TRBLE_SHIFT 7 +#define TFA9879_G_BASS_MASK 0x007c +#define TFA9879_G_BASS_SHIFT 2 +#define TFA9879_F_BASS_MASK 0x0003 +#define TFA9879_F_BASS_SHIFT 0 + +/* TFA9879_HIGH_PASS_FILTER */ +#define TFA9879_HP_CTRL_MASK 0x00ff +#define TFA9879_HP_CTRL_SHIFT 0 + +/* TFA9879_VOLUME_CONTROL */ +#define TFA9879_ZR_CRSS_MASK 0x1000 +#define TFA9879_ZR_CRSS_SHIFT 12 +#define TFA9879_VOL_MASK 0x00ff +#define TFA9879_VOL_SHIFT 0 + +/* TFA9879_MISC_CONTROL */ +#define TFA9879_DE_PHAS_MASK 0x0c00 +#define TFA9879_DE_PHAS_SHIFT 10 +#define TFA9879_H_MUTE_MASK 0x0200 +#define TFA9879_H_MUTE_SHIFT 9 +#define TFA9879_S_MUTE_MASK 0x0100 +#define TFA9879_S_MUTE_SHIFT 8 +#define TFA9879_P_LIM_MASK 0x00ff +#define TFA9879_P_LIM_SHIFT 0 + +/* TFA9879_MISC_STATUS */ +#define TFA9879_PS_MASK 0x4000 +#define TFA9879_PS_SHIFT 14 +#define TFA9879_PORA_MASK 0x2000 +#define TFA9879_PORA_SHIFT 13 +#define TFA9879_AMP_MASK 0x0600 +#define TFA9879_AMP_SHIFT 9 +#define TFA9879_IBP_2_MASK 0x0100 +#define TFA9879_IBP_2_SHIFT 8 +#define TFA9879_OFP_2_MASK 0x0080 +#define TFA9879_OFP_2_SHIFT 7 +#define TFA9879_UFP_2_MASK 0x0040 +#define TFA9879_UFP_2_SHIFT 6 +#define TFA9879_IBP_1_MASK 0x0020 +#define TFA9879_IBP_1_SHIFT 5 +#define TFA9879_OFP_1_MASK 0x0010 +#define TFA9879_OFP_1_SHIFT 4 +#define TFA9879_UFP_1_MASK 0x0008 +#define TFA9879_UFP_1_SHIFT 3 +#define TFA9879_OCPOKA_MASK 0x0004 +#define TFA9879_OCPOKA_SHIFT 2 +#define TFA9879_OCPOKB_MASK 0x0002 +#define TFA9879_OCPOKB_SHIFT 1 +#define TFA9879_OTPOK_MASK 0x0001 +#define TFA9879_OTPOK_SHIFT 0 + +#endif diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c new file mode 100644 index 0000000000..79847a90ac --- /dev/null +++ b/sound/soc/codecs/tfa989x.c @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 Stephan Gerhold + * + * Register definitions/sequences taken from various tfa98xx kernel drivers: + * Copyright (C) 2014-2020 NXP Semiconductors, All Rights Reserved. + * Copyright (C) 2013 Sony Mobile Communications Inc. + */ + +#include +#include +#include +#include +#include +#include + +#define TFA989X_STATUSREG 0x00 +#define TFA989X_BATTERYVOLTAGE 0x01 +#define TFA989X_TEMPERATURE 0x02 +#define TFA989X_REVISIONNUMBER 0x03 +#define TFA989X_REVISIONNUMBER_REV_MSK GENMASK(7, 0) /* device revision */ +#define TFA989X_I2SREG 0x04 +#define TFA989X_I2SREG_RCV 2 /* receiver mode */ +#define TFA989X_I2SREG_CHSA 6 /* amplifier input select */ +#define TFA989X_I2SREG_CHSA_MSK GENMASK(7, 6) +#define TFA989X_I2SREG_I2SSR 12 /* sample rate */ +#define TFA989X_I2SREG_I2SSR_MSK GENMASK(15, 12) +#define TFA989X_BAT_PROT 0x05 +#define TFA989X_AUDIO_CTR 0x06 +#define TFA989X_DCDCBOOST 0x07 +#define TFA989X_SPKR_CALIBRATION 0x08 +#define TFA989X_SYS_CTRL 0x09 +#define TFA989X_SYS_CTRL_PWDN 0 /* power down */ +#define TFA989X_SYS_CTRL_I2CR 1 /* I2C reset */ +#define TFA989X_SYS_CTRL_CFE 2 /* enable CoolFlux DSP */ +#define TFA989X_SYS_CTRL_AMPE 3 /* enable amplifier */ +#define TFA989X_SYS_CTRL_DCA 4 /* enable boost */ +#define TFA989X_SYS_CTRL_SBSL 5 /* DSP configured */ +#define TFA989X_SYS_CTRL_AMPC 6 /* amplifier enabled by DSP */ +#define TFA989X_I2S_SEL_REG 0x0a +#define TFA989X_I2S_SEL_REG_SPKR_MSK GENMASK(10, 9) /* speaker impedance */ +#define TFA989X_I2S_SEL_REG_DCFG_MSK GENMASK(14, 11) /* DCDC compensation */ +#define TFA989X_HIDE_UNHIDE_KEY 0x40 +#define TFA989X_PWM_CONTROL 0x41 +#define TFA989X_CURRENTSENSE1 0x46 +#define TFA989X_CURRENTSENSE2 0x47 +#define TFA989X_CURRENTSENSE3 0x48 +#define TFA989X_CURRENTSENSE4 0x49 + +#define TFA9890_REVISION 0x80 +#define TFA9895_REVISION 0x12 +#define TFA9897_REVISION 0x97 + +struct tfa989x_rev { + unsigned int rev; + int (*init)(struct regmap *regmap); +}; + +struct tfa989x { + const struct tfa989x_rev *rev; + struct regulator *vddd_supply; + struct gpio_desc *rcv_gpiod; +}; + +static bool tfa989x_writeable_reg(struct device *dev, unsigned int reg) +{ + return reg > TFA989X_REVISIONNUMBER; +} + +static bool tfa989x_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg < TFA989X_REVISIONNUMBER; +} + +static const struct regmap_config tfa989x_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .writeable_reg = tfa989x_writeable_reg, + .volatile_reg = tfa989x_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static const char * const chsa_text[] = { "Left", "Right", /* "DSP" */ }; +static SOC_ENUM_SINGLE_DECL(chsa_enum, TFA989X_I2SREG, TFA989X_I2SREG_CHSA, chsa_text); +static const struct snd_kcontrol_new chsa_mux = SOC_DAPM_ENUM("Amp Input", chsa_enum); + +static const struct snd_soc_dapm_widget tfa989x_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_SUPPLY("POWER", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_PWDN, 1, NULL, 0), + SND_SOC_DAPM_OUT_DRV("AMPE", TFA989X_SYS_CTRL, TFA989X_SYS_CTRL_AMPE, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Amp Input", SND_SOC_NOPM, 0, 0, &chsa_mux), + SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route tfa989x_dapm_routes[] = { + {"OUT", NULL, "AMPE"}, + {"AMPE", NULL, "POWER"}, + {"AMPE", NULL, "Amp Input"}, + {"Amp Input", "Left", "AIFINL"}, + {"Amp Input", "Right", "AIFINR"}, +}; + +static int tfa989x_put_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component); + + gpiod_set_value_cansleep(tfa989x->rcv_gpiod, ucontrol->value.enumerated.item[0]); + + return snd_soc_put_enum_double(kcontrol, ucontrol); +} + +static const char * const mode_text[] = { "Speaker", "Receiver" }; +static SOC_ENUM_SINGLE_DECL(mode_enum, TFA989X_I2SREG, TFA989X_I2SREG_RCV, mode_text); +static const struct snd_kcontrol_new tfa989x_mode_controls[] = { + SOC_ENUM_EXT("Mode", mode_enum, snd_soc_get_enum_double, tfa989x_put_mode), +}; + +static int tfa989x_probe(struct snd_soc_component *component) +{ + struct tfa989x *tfa989x = snd_soc_component_get_drvdata(component); + + if (tfa989x->rev->rev == TFA9897_REVISION) + return snd_soc_add_component_controls(component, tfa989x_mode_controls, + ARRAY_SIZE(tfa989x_mode_controls)); + + return 0; +} + +static const struct snd_soc_component_driver tfa989x_component = { + .probe = tfa989x_probe, + .dapm_widgets = tfa989x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tfa989x_dapm_widgets), + .dapm_routes = tfa989x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tfa989x_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const unsigned int tfa989x_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 +}; + +static int tfa989x_find_sample_rate(unsigned int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tfa989x_rates); ++i) + if (tfa989x_rates[i] == rate) + return i; + + return -EINVAL; +} + +static int tfa989x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int sr; + + sr = tfa989x_find_sample_rate(params_rate(params)); + if (sr < 0) + return sr; + + return snd_soc_component_update_bits(component, TFA989X_I2SREG, + TFA989X_I2SREG_I2SSR_MSK, + sr << TFA989X_I2SREG_I2SSR); +} + +static const struct snd_soc_dai_ops tfa989x_dai_ops = { + .hw_params = tfa989x_hw_params, +}; + +static struct snd_soc_dai_driver tfa989x_dai = { + .name = "tfa989x-hifi", + .playback = { + .stream_name = "HiFi Playback", + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tfa989x_dai_ops, +}; + +static int tfa9890_init(struct regmap *regmap) +{ + int ret; + + /* temporarily allow access to hidden registers */ + ret = regmap_write(regmap, TFA989X_HIDE_UNHIDE_KEY, 0x5a6b); + if (ret) + return ret; + + /* update PLL registers */ + ret = regmap_set_bits(regmap, 0x59, 0x3); + if (ret) + return ret; + + /* hide registers again */ + ret = regmap_write(regmap, TFA989X_HIDE_UNHIDE_KEY, 0x0000); + if (ret) + return ret; + + return regmap_write(regmap, TFA989X_CURRENTSENSE2, 0x7BE1); +} + +static const struct tfa989x_rev tfa9890_rev = { + .rev = TFA9890_REVISION, + .init = tfa9890_init, +}; + +static const struct reg_sequence tfa9895_reg_init[] = { + /* some other registers must be set for optimal amplifier behaviour */ + { TFA989X_BAT_PROT, 0x13ab }, + { TFA989X_AUDIO_CTR, 0x001f }, + + /* peak voltage protection is always on, but may be written */ + { TFA989X_SPKR_CALIBRATION, 0x3c4e }, + + /* TFA989X_SYSCTRL_DCA = 0 */ + { TFA989X_SYS_CTRL, 0x024d }, + { TFA989X_PWM_CONTROL, 0x0308 }, + { TFA989X_CURRENTSENSE4, 0x0e82 }, +}; + +static int tfa9895_init(struct regmap *regmap) +{ + return regmap_multi_reg_write(regmap, tfa9895_reg_init, + ARRAY_SIZE(tfa9895_reg_init)); +} + +static const struct tfa989x_rev tfa9895_rev = { + .rev = TFA9895_REVISION, + .init = tfa9895_init, +}; + +static int tfa9897_init(struct regmap *regmap) +{ + int ret; + + /* Reduce slewrate by clearing iddqtestbst to avoid booster damage */ + ret = regmap_write(regmap, TFA989X_CURRENTSENSE3, 0x0300); + if (ret) + return ret; + + /* Enable clipping */ + ret = regmap_clear_bits(regmap, TFA989X_CURRENTSENSE4, 0x1); + if (ret) + return ret; + + /* Set required TDM configuration */ + return regmap_write(regmap, 0x14, 0x0); +} + +static const struct tfa989x_rev tfa9897_rev = { + .rev = TFA9897_REVISION, + .init = tfa9897_init, +}; + +/* + * Note: At the moment this driver bypasses the "CoolFlux DSP" built into the + * TFA989X amplifiers. Unfortunately, there seems to be absolutely + * no documentation for it - the public "short datasheets" do not provide + * any information about the DSP or available registers. + * + * Usually the TFA989X amplifiers are configured through proprietary userspace + * libraries. There are also some (rather complex) kernel drivers but even those + * rely on obscure firmware blobs for configuration (so-called "containers"). + * They seem to contain different "profiles" with tuned speaker settings, sample + * rates and volume steps (which would be better exposed as separate ALSA mixers). + * + * Bypassing the DSP disables volume control (and perhaps some speaker + * optimization?), but at least allows using the speaker without obscure + * kernel drivers and firmware. + * + * Ideally NXP (or now Goodix) should release proper documentation for these + * amplifiers so that support for the "CoolFlux DSP" can be implemented properly. + */ +static int tfa989x_dsp_bypass(struct regmap *regmap) +{ + int ret; + + /* Clear CHSA to bypass DSP and take input from I2S 1 left channel */ + ret = regmap_clear_bits(regmap, TFA989X_I2SREG, TFA989X_I2SREG_CHSA_MSK); + if (ret) + return ret; + + /* Set DCDC compensation to off and speaker impedance to 8 ohm */ + ret = regmap_update_bits(regmap, TFA989X_I2S_SEL_REG, + TFA989X_I2S_SEL_REG_DCFG_MSK | + TFA989X_I2S_SEL_REG_SPKR_MSK, + TFA989X_I2S_SEL_REG_SPKR_MSK); + if (ret) + return ret; + + /* Set DCDC to follower mode and disable CoolFlux DSP */ + return regmap_clear_bits(regmap, TFA989X_SYS_CTRL, + BIT(TFA989X_SYS_CTRL_DCA) | + BIT(TFA989X_SYS_CTRL_CFE) | + BIT(TFA989X_SYS_CTRL_AMPC)); +} + +static void tfa989x_regulator_disable(void *data) +{ + struct tfa989x *tfa989x = data; + + regulator_disable(tfa989x->vddd_supply); +} + +static int tfa989x_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + const struct tfa989x_rev *rev; + struct tfa989x *tfa989x; + struct regmap *regmap; + unsigned int val; + int ret; + + rev = device_get_match_data(dev); + if (!rev) { + dev_err(dev, "unknown device revision\n"); + return -ENODEV; + } + + tfa989x = devm_kzalloc(dev, sizeof(*tfa989x), GFP_KERNEL); + if (!tfa989x) + return -ENOMEM; + + tfa989x->rev = rev; + i2c_set_clientdata(i2c, tfa989x); + + tfa989x->vddd_supply = devm_regulator_get(dev, "vddd"); + if (IS_ERR(tfa989x->vddd_supply)) + return dev_err_probe(dev, PTR_ERR(tfa989x->vddd_supply), + "Failed to get vddd regulator\n"); + + if (tfa989x->rev->rev == TFA9897_REVISION) { + tfa989x->rcv_gpiod = devm_gpiod_get_optional(dev, "rcv", GPIOD_OUT_LOW); + if (IS_ERR(tfa989x->rcv_gpiod)) + return PTR_ERR(tfa989x->rcv_gpiod); + } + + regmap = devm_regmap_init_i2c(i2c, &tfa989x_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = regulator_enable(tfa989x->vddd_supply); + if (ret) { + dev_err(dev, "Failed to enable vddd regulator: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, tfa989x_regulator_disable, tfa989x); + if (ret) + return ret; + + /* Bypass regcache for reset and init sequence */ + regcache_cache_bypass(regmap, true); + + /* Dummy read to generate i2c clocks, required on some devices */ + regmap_read(regmap, TFA989X_REVISIONNUMBER, &val); + + ret = regmap_read(regmap, TFA989X_REVISIONNUMBER, &val); + if (ret) { + dev_err(dev, "failed to read revision number: %d\n", ret); + return ret; + } + + val &= TFA989X_REVISIONNUMBER_REV_MSK; + if (val != rev->rev) { + dev_err(dev, "invalid revision number, expected %#x, got %#x\n", + rev->rev, val); + return -ENODEV; + } + + ret = regmap_write(regmap, TFA989X_SYS_CTRL, BIT(TFA989X_SYS_CTRL_I2CR)); + if (ret) { + dev_err(dev, "failed to reset I2C registers: %d\n", ret); + return ret; + } + + ret = rev->init(regmap); + if (ret) { + dev_err(dev, "failed to initialize registers: %d\n", ret); + return ret; + } + + ret = tfa989x_dsp_bypass(regmap); + if (ret) { + dev_err(dev, "failed to enable DSP bypass: %d\n", ret); + return ret; + } + regcache_cache_bypass(regmap, false); + + return devm_snd_soc_register_component(dev, &tfa989x_component, + &tfa989x_dai, 1); +} + +static const struct of_device_id tfa989x_of_match[] = { + { .compatible = "nxp,tfa9890", .data = &tfa9890_rev }, + { .compatible = "nxp,tfa9895", .data = &tfa9895_rev }, + { .compatible = "nxp,tfa9897", .data = &tfa9897_rev }, + { } +}; +MODULE_DEVICE_TABLE(of, tfa989x_of_match); + +static struct i2c_driver tfa989x_i2c_driver = { + .driver = { + .name = "tfa989x", + .of_match_table = tfa989x_of_match, + }, + .probe = tfa989x_i2c_probe, +}; +module_i2c_driver(tfa989x_i2c_driver); + +MODULE_DESCRIPTION("ASoC NXP/Goodix TFA989X (TFA1) driver"); +MODULE_AUTHOR("Stephan Gerhold "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c new file mode 100644 index 0000000000..420bbf588e --- /dev/null +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -0,0 +1,1463 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Based on sound/soc/codecs/tlv320aic3x.c by Vladimir Barinov +// +// Copyright (C) 2010 Mistral Solutions Pvt Ltd. +// Author: Shahina Shaik +// +// Copyright (C) 2014-2018, Ambarella, Inc. +// Author: Dongge wu +// +// Copyright (C) 2021 Axis Communications AB +// Author: Ricard Wanderlof +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * General definitions defining exported functionality. + */ + +#define ADC3XXX_MICBIAS_PINS 2 + +/* Number of GPIO pins exposed via the gpiolib interface */ +#define ADC3XXX_GPIOS_MAX 2 + +#define ADC3XXX_RATES SNDRV_PCM_RATE_8000_96000 +#define ADC3XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* + * PLL modes, to be used for clk_id for set_sysclk callback. + * + * The default behavior (AUTO) is to take the first matching entry in the clock + * table, which is intended to be the PLL based one if there is more than one. + * + * Setting the clock source using simple-card (clocks or + * system-clock-frequency property) sets clk_id = 0 = ADC3XXX_PLL_AUTO. + */ +#define ADC3XXX_PLL_AUTO 0 /* Use first available mode */ +#define ADC3XXX_PLL_ENABLE 1 /* Use PLL for clock generation */ +#define ADC3XXX_PLL_BYPASS 2 /* Don't use PLL for clock generation */ + +/* Register definitions. */ + +#define ADC3XXX_PAGE_SIZE 128 +#define ADC3XXX_REG(page, reg) ((page * ADC3XXX_PAGE_SIZE) + reg) + +/* + * Page 0 registers. + */ + +#define ADC3XXX_PAGE_SELECT ADC3XXX_REG(0, 0) +#define ADC3XXX_RESET ADC3XXX_REG(0, 1) + +/* 2-3 Reserved */ + +#define ADC3XXX_CLKGEN_MUX ADC3XXX_REG(0, 4) +#define ADC3XXX_PLL_PROG_PR ADC3XXX_REG(0, 5) +#define ADC3XXX_PLL_PROG_J ADC3XXX_REG(0, 6) +#define ADC3XXX_PLL_PROG_D_MSB ADC3XXX_REG(0, 7) +#define ADC3XXX_PLL_PROG_D_LSB ADC3XXX_REG(0, 8) + +/* 9-17 Reserved */ + +#define ADC3XXX_ADC_NADC ADC3XXX_REG(0, 18) +#define ADC3XXX_ADC_MADC ADC3XXX_REG(0, 19) +#define ADC3XXX_ADC_AOSR ADC3XXX_REG(0, 20) +#define ADC3XXX_ADC_IADC ADC3XXX_REG(0, 21) + +/* 23-24 Reserved */ + +#define ADC3XXX_CLKOUT_MUX ADC3XXX_REG(0, 25) +#define ADC3XXX_CLKOUT_M_DIV ADC3XXX_REG(0, 26) +#define ADC3XXX_INTERFACE_CTRL_1 ADC3XXX_REG(0, 27) +#define ADC3XXX_CH_OFFSET_1 ADC3XXX_REG(0, 28) +#define ADC3XXX_INTERFACE_CTRL_2 ADC3XXX_REG(0, 29) +#define ADC3XXX_BCLK_N_DIV ADC3XXX_REG(0, 30) +#define ADC3XXX_INTERFACE_CTRL_3 ADC3XXX_REG(0, 31) +#define ADC3XXX_INTERFACE_CTRL_4 ADC3XXX_REG(0, 32) +#define ADC3XXX_INTERFACE_CTRL_5 ADC3XXX_REG(0, 33) +#define ADC3XXX_I2S_SYNC ADC3XXX_REG(0, 34) +/* 35 Reserved */ +#define ADC3XXX_ADC_FLAG ADC3XXX_REG(0, 36) +#define ADC3XXX_CH_OFFSET_2 ADC3XXX_REG(0, 37) +#define ADC3XXX_I2S_TDM_CTRL ADC3XXX_REG(0, 38) +/* 39-41 Reserved */ +#define ADC3XXX_INTR_FLAG_1 ADC3XXX_REG(0, 42) +#define ADC3XXX_INTR_FLAG_2 ADC3XXX_REG(0, 43) +/* 44 Reserved */ +#define ADC3XXX_INTR_FLAG_ADC1 ADC3XXX_REG(0, 45) +/* 46 Reserved */ +#define ADC3XXX_INTR_FLAG_ADC2 ADC3XXX_REG(0, 47) +#define ADC3XXX_INT1_CTRL ADC3XXX_REG(0, 48) +#define ADC3XXX_INT2_CTRL ADC3XXX_REG(0, 49) +/* 50 Reserved */ +#define ADC3XXX_GPIO2_CTRL ADC3XXX_REG(0, 51) +#define ADC3XXX_GPIO1_CTRL ADC3XXX_REG(0, 52) +#define ADC3XXX_DOUT_CTRL ADC3XXX_REG(0, 53) +/* 54-56 Reserved */ +#define ADC3XXX_SYNC_CTRL_1 ADC3XXX_REG(0, 57) +#define ADC3XXX_SYNC_CTRL_2 ADC3XXX_REG(0, 58) +#define ADC3XXX_CIC_GAIN_CTRL ADC3XXX_REG(0, 59) +/* 60 Reserved */ +#define ADC3XXX_PRB_SELECT ADC3XXX_REG(0, 61) +#define ADC3XXX_INST_MODE_CTRL ADC3XXX_REG(0, 62) +/* 63-79 Reserved */ +#define ADC3XXX_MIC_POLARITY_CTRL ADC3XXX_REG(0, 80) +#define ADC3XXX_ADC_DIGITAL ADC3XXX_REG(0, 81) +#define ADC3XXX_ADC_FGA ADC3XXX_REG(0, 82) +#define ADC3XXX_LADC_VOL ADC3XXX_REG(0, 83) +#define ADC3XXX_RADC_VOL ADC3XXX_REG(0, 84) +#define ADC3XXX_ADC_PHASE_COMP ADC3XXX_REG(0, 85) +#define ADC3XXX_LEFT_CHN_AGC_1 ADC3XXX_REG(0, 86) +#define ADC3XXX_LEFT_CHN_AGC_2 ADC3XXX_REG(0, 87) +#define ADC3XXX_LEFT_CHN_AGC_3 ADC3XXX_REG(0, 88) +#define ADC3XXX_LEFT_CHN_AGC_4 ADC3XXX_REG(0, 89) +#define ADC3XXX_LEFT_CHN_AGC_5 ADC3XXX_REG(0, 90) +#define ADC3XXX_LEFT_CHN_AGC_6 ADC3XXX_REG(0, 91) +#define ADC3XXX_LEFT_CHN_AGC_7 ADC3XXX_REG(0, 92) +#define ADC3XXX_LEFT_AGC_GAIN ADC3XXX_REG(0, 93) +#define ADC3XXX_RIGHT_CHN_AGC_1 ADC3XXX_REG(0, 94) +#define ADC3XXX_RIGHT_CHN_AGC_2 ADC3XXX_REG(0, 95) +#define ADC3XXX_RIGHT_CHN_AGC_3 ADC3XXX_REG(0, 96) +#define ADC3XXX_RIGHT_CHN_AGC_4 ADC3XXX_REG(0, 97) +#define ADC3XXX_RIGHT_CHN_AGC_5 ADC3XXX_REG(0, 98) +#define ADC3XXX_RIGHT_CHN_AGC_6 ADC3XXX_REG(0, 99) +#define ADC3XXX_RIGHT_CHN_AGC_7 ADC3XXX_REG(0, 100) +#define ADC3XXX_RIGHT_AGC_GAIN ADC3XXX_REG(0, 101) +/* 102-127 Reserved */ + +/* + * Page 1 registers. + */ + +/* 1-25 Reserved */ +#define ADC3XXX_DITHER_CTRL ADC3XXX_REG(1, 26) +/* 27-50 Reserved */ +#define ADC3XXX_MICBIAS_CTRL ADC3XXX_REG(1, 51) +#define ADC3XXX_LEFT_PGA_SEL_1 ADC3XXX_REG(1, 52) +/* 53 Reserved */ +#define ADC3XXX_LEFT_PGA_SEL_2 ADC3XXX_REG(1, 54) +#define ADC3XXX_RIGHT_PGA_SEL_1 ADC3XXX_REG(1, 55) +#define ADC3XXX_RIGHT_PGA_SEL_2 ADC3XXX_REG(1, 57) +#define ADC3XXX_LEFT_APGA_CTRL ADC3XXX_REG(1, 59) +#define ADC3XXX_RIGHT_APGA_CTRL ADC3XXX_REG(1, 60) +#define ADC3XXX_LOW_CURRENT_MODES ADC3XXX_REG(1, 61) +#define ADC3XXX_ANALOG_PGA_FLAGS ADC3XXX_REG(1, 62) +/* 63-127 Reserved */ + +/* + * Page 4 registers. First page of coefficient memory for the miniDSP. + */ +#define ADC3XXX_LEFT_ADC_IIR_COEFF_N0_MSB ADC3XXX_REG(4, 8) +#define ADC3XXX_LEFT_ADC_IIR_COEFF_N0_LSB ADC3XXX_REG(4, 9) +#define ADC3XXX_LEFT_ADC_IIR_COEFF_N1_MSB ADC3XXX_REG(4, 10) +#define ADC3XXX_LEFT_ADC_IIR_COEFF_N1_LSB ADC3XXX_REG(4, 11) +#define ADC3XXX_LEFT_ADC_IIR_COEFF_D1_MSB ADC3XXX_REG(4, 12) +#define ADC3XXX_LEFT_ADC_IIR_COEFF_D1_LSB ADC3XXX_REG(4, 13) + +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N0_MSB ADC3XXX_REG(4, 72) +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N0_LSB ADC3XXX_REG(4, 73) +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N1_MSB ADC3XXX_REG(4, 74) +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_N1_LSB ADC3XXX_REG(4, 75) +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_D1_MSB ADC3XXX_REG(4, 76) +#define ADC3XXX_RIGHT_ADC_IIR_COEFF_D1_LSB ADC3XXX_REG(4, 77) + +/* + * Register bits. + */ + +/* PLL Enable bits */ +#define ADC3XXX_ENABLE_PLL_SHIFT 7 +#define ADC3XXX_ENABLE_PLL (1 << ADC3XXX_ENABLE_PLL_SHIFT) +#define ADC3XXX_ENABLE_NADC_SHIFT 7 +#define ADC3XXX_ENABLE_NADC (1 << ADC3XXX_ENABLE_NADC_SHIFT) +#define ADC3XXX_ENABLE_MADC_SHIFT 7 +#define ADC3XXX_ENABLE_MADC (1 << ADC3XXX_ENABLE_MADC_SHIFT) +#define ADC3XXX_ENABLE_BCLK_SHIFT 7 +#define ADC3XXX_ENABLE_BCLK (1 << ADC3XXX_ENABLE_BCLK_SHIFT) + +/* Power bits */ +#define ADC3XXX_LADC_PWR_ON 0x80 +#define ADC3XXX_RADC_PWR_ON 0x40 + +#define ADC3XXX_SOFT_RESET 0x01 +#define ADC3XXX_BCLK_MASTER 0x08 +#define ADC3XXX_WCLK_MASTER 0x04 + +/* Interface register masks */ +#define ADC3XXX_FORMAT_MASK 0xc0 +#define ADC3XXX_FORMAT_SHIFT 6 +#define ADC3XXX_WLENGTH_MASK 0x30 +#define ADC3XXX_WLENGTH_SHIFT 4 +#define ADC3XXX_CLKDIR_MASK 0x0c +#define ADC3XXX_CLKDIR_SHIFT 2 + +/* Interface register bit patterns */ +#define ADC3XXX_FORMAT_I2S (0 << ADC3XXX_FORMAT_SHIFT) +#define ADC3XXX_FORMAT_DSP (1 << ADC3XXX_FORMAT_SHIFT) +#define ADC3XXX_FORMAT_RJF (2 << ADC3XXX_FORMAT_SHIFT) +#define ADC3XXX_FORMAT_LJF (3 << ADC3XXX_FORMAT_SHIFT) + +#define ADC3XXX_IFACE_16BITS (0 << ADC3XXX_WLENGTH_SHIFT) +#define ADC3XXX_IFACE_20BITS (1 << ADC3XXX_WLENGTH_SHIFT) +#define ADC3XXX_IFACE_24BITS (2 << ADC3XXX_WLENGTH_SHIFT) +#define ADC3XXX_IFACE_32BITS (3 << ADC3XXX_WLENGTH_SHIFT) + +/* PLL P/R bit offsets */ +#define ADC3XXX_PLLP_SHIFT 4 +#define ADC3XXX_PLLR_SHIFT 0 +#define ADC3XXX_PLL_PR_MASK 0x7f +#define ADC3XXX_PLLJ_MASK 0x3f +#define ADC3XXX_PLLD_MSB_MASK 0x3f +#define ADC3XXX_PLLD_LSB_MASK 0xff +#define ADC3XXX_NADC_MASK 0x7f +#define ADC3XXX_MADC_MASK 0x7f +#define ADC3XXX_AOSR_MASK 0xff +#define ADC3XXX_IADC_MASK 0xff +#define ADC3XXX_BDIV_MASK 0x7f + +/* PLL_CLKIN bits */ +#define ADC3XXX_PLL_CLKIN_SHIFT 2 +#define ADC3XXX_PLL_CLKIN_MCLK 0x0 +#define ADC3XXX_PLL_CLKIN_BCLK 0x1 +#define ADC3XXX_PLL_CLKIN_ZERO 0x3 + +/* CODEC_CLKIN bits */ +#define ADC3XXX_CODEC_CLKIN_SHIFT 0 +#define ADC3XXX_CODEC_CLKIN_MCLK 0x0 +#define ADC3XXX_CODEC_CLKIN_BCLK 0x1 +#define ADC3XXX_CODEC_CLKIN_PLL_CLK 0x3 + +#define ADC3XXX_USE_PLL ((ADC3XXX_PLL_CLKIN_MCLK << ADC3XXX_PLL_CLKIN_SHIFT) | \ + (ADC3XXX_CODEC_CLKIN_PLL_CLK << ADC3XXX_CODEC_CLKIN_SHIFT)) +#define ADC3XXX_NO_PLL ((ADC3XXX_PLL_CLKIN_ZERO << ADC3XXX_PLL_CLKIN_SHIFT) | \ + (ADC3XXX_CODEC_CLKIN_MCLK << ADC3XXX_CODEC_CLKIN_SHIFT)) + +/* Analog PGA control bits */ +#define ADC3XXX_LPGA_MUTE 0x80 +#define ADC3XXX_RPGA_MUTE 0x80 + +#define ADC3XXX_LPGA_GAIN_MASK 0x7f +#define ADC3XXX_RPGA_GAIN_MASK 0x7f + +/* ADC current modes */ +#define ADC3XXX_ADC_LOW_CURR_MODE 0x01 + +/* Left ADC Input selection bits */ +#define ADC3XXX_LCH_SEL1_SHIFT 0 +#define ADC3XXX_LCH_SEL2_SHIFT 2 +#define ADC3XXX_LCH_SEL3_SHIFT 4 +#define ADC3XXX_LCH_SEL4_SHIFT 6 + +#define ADC3XXX_LCH_SEL1X_SHIFT 0 +#define ADC3XXX_LCH_SEL2X_SHIFT 2 +#define ADC3XXX_LCH_SEL3X_SHIFT 4 +#define ADC3XXX_LCH_COMMON_MODE 0x40 +#define ADC3XXX_BYPASS_LPGA 0x80 + +/* Right ADC Input selection bits */ +#define ADC3XXX_RCH_SEL1_SHIFT 0 +#define ADC3XXX_RCH_SEL2_SHIFT 2 +#define ADC3XXX_RCH_SEL3_SHIFT 4 +#define ADC3XXX_RCH_SEL4_SHIFT 6 + +#define ADC3XXX_RCH_SEL1X_SHIFT 0 +#define ADC3XXX_RCH_SEL2X_SHIFT 2 +#define ADC3XXX_RCH_SEL3X_SHIFT 4 +#define ADC3XXX_RCH_COMMON_MODE 0x40 +#define ADC3XXX_BYPASS_RPGA 0x80 + +/* MICBIAS control bits */ +#define ADC3XXX_MICBIAS_MASK 0x3 +#define ADC3XXX_MICBIAS1_SHIFT 5 +#define ADC3XXX_MICBIAS2_SHIFT 3 + +#define ADC3XXX_ADC_MAX_VOLUME 64 +#define ADC3XXX_ADC_POS_VOL 24 + +/* GPIO control bits (GPIO1_CTRL and GPIO2_CTRL) */ +#define ADC3XXX_GPIO_CTRL_CFG_MASK 0x3c +#define ADC3XXX_GPIO_CTRL_CFG_SHIFT 2 +#define ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK 0x01 +#define ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_SHIFT 0 +#define ADC3XXX_GPIO_CTRL_INPUT_VALUE_MASK 0x02 +#define ADC3XXX_GPIO_CTRL_INPUT_VALUE_SHIFT 1 + +enum adc3xxx_type { + ADC3001 = 0, + ADC3101 +}; + +struct adc3xxx { + struct device *dev; + enum adc3xxx_type type; + struct clk *mclk; + struct regmap *regmap; + struct gpio_desc *rst_pin; + unsigned int pll_mode; + unsigned int sysclk; + unsigned int gpio_cfg[ADC3XXX_GPIOS_MAX]; /* value+1 (0 => not set) */ + unsigned int micbias_vg[ADC3XXX_MICBIAS_PINS]; + int master; + u8 page_no; + int use_pll; + struct gpio_chip gpio_chip; +}; + +static const unsigned int adc3xxx_gpio_ctrl_reg[ADC3XXX_GPIOS_MAX] = { + ADC3XXX_GPIO1_CTRL, + ADC3XXX_GPIO2_CTRL +}; + +static const unsigned int adc3xxx_micbias_shift[ADC3XXX_MICBIAS_PINS] = { + ADC3XXX_MICBIAS1_SHIFT, + ADC3XXX_MICBIAS2_SHIFT +}; + +static const struct reg_default adc3xxx_defaults[] = { + /* Page 0 */ + { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, { 3, 0x00 }, + { 4, 0x00 }, { 5, 0x11 }, { 6, 0x04 }, { 7, 0x00 }, + { 8, 0x00 }, { 9, 0x00 }, { 10, 0x00 }, { 11, 0x00 }, + { 12, 0x00 }, { 13, 0x00 }, { 14, 0x00 }, { 15, 0x00 }, + { 16, 0x00 }, { 17, 0x00 }, { 18, 0x01 }, { 19, 0x01 }, + { 20, 0x80 }, { 21, 0x80 }, { 22, 0x04 }, { 23, 0x00 }, + { 24, 0x00 }, { 25, 0x00 }, { 26, 0x01 }, { 27, 0x00 }, + { 28, 0x00 }, { 29, 0x02 }, { 30, 0x01 }, { 31, 0x00 }, + { 32, 0x00 }, { 33, 0x10 }, { 34, 0x00 }, { 35, 0x00 }, + { 36, 0x00 }, { 37, 0x00 }, { 38, 0x02 }, { 39, 0x00 }, + { 40, 0x00 }, { 41, 0x00 }, { 42, 0x00 }, { 43, 0x00 }, + { 44, 0x00 }, { 45, 0x00 }, { 46, 0x00 }, { 47, 0x00 }, + { 48, 0x00 }, { 49, 0x00 }, { 50, 0x00 }, { 51, 0x00 }, + { 52, 0x00 }, { 53, 0x12 }, { 54, 0x00 }, { 55, 0x00 }, + { 56, 0x00 }, { 57, 0x00 }, { 58, 0x00 }, { 59, 0x44 }, + { 60, 0x00 }, { 61, 0x01 }, { 62, 0x00 }, { 63, 0x00 }, + { 64, 0x00 }, { 65, 0x00 }, { 66, 0x00 }, { 67, 0x00 }, + { 68, 0x00 }, { 69, 0x00 }, { 70, 0x00 }, { 71, 0x00 }, + { 72, 0x00 }, { 73, 0x00 }, { 74, 0x00 }, { 75, 0x00 }, + { 76, 0x00 }, { 77, 0x00 }, { 78, 0x00 }, { 79, 0x00 }, + { 80, 0x00 }, { 81, 0x00 }, { 82, 0x88 }, { 83, 0x00 }, + { 84, 0x00 }, { 85, 0x00 }, { 86, 0x00 }, { 87, 0x00 }, + { 88, 0x7f }, { 89, 0x00 }, { 90, 0x00 }, { 91, 0x00 }, + { 92, 0x00 }, { 93, 0x00 }, { 94, 0x00 }, { 95, 0x00 }, + { 96, 0x7f }, { 97, 0x00 }, { 98, 0x00 }, { 99, 0x00 }, + { 100, 0x00 }, { 101, 0x00 }, { 102, 0x00 }, { 103, 0x00 }, + { 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 }, + { 108, 0x00 }, { 109, 0x00 }, { 110, 0x00 }, { 111, 0x00 }, + { 112, 0x00 }, { 113, 0x00 }, { 114, 0x00 }, { 115, 0x00 }, + { 116, 0x00 }, { 117, 0x00 }, { 118, 0x00 }, { 119, 0x00 }, + { 120, 0x00 }, { 121, 0x00 }, { 122, 0x00 }, { 123, 0x00 }, + { 124, 0x00 }, { 125, 0x00 }, { 126, 0x00 }, { 127, 0x00 }, + + /* Page 1 */ + { 128, 0x00 }, { 129, 0x00 }, { 130, 0x00 }, { 131, 0x00 }, + { 132, 0x00 }, { 133, 0x00 }, { 134, 0x00 }, { 135, 0x00 }, + { 136, 0x00 }, { 137, 0x00 }, { 138, 0x00 }, { 139, 0x00 }, + { 140, 0x00 }, { 141, 0x00 }, { 142, 0x00 }, { 143, 0x00 }, + { 144, 0x00 }, { 145, 0x00 }, { 146, 0x00 }, { 147, 0x00 }, + { 148, 0x00 }, { 149, 0x00 }, { 150, 0x00 }, { 151, 0x00 }, + { 152, 0x00 }, { 153, 0x00 }, { 154, 0x00 }, { 155, 0x00 }, + { 156, 0x00 }, { 157, 0x00 }, { 158, 0x00 }, { 159, 0x00 }, + { 160, 0x00 }, { 161, 0x00 }, { 162, 0x00 }, { 163, 0x00 }, + { 164, 0x00 }, { 165, 0x00 }, { 166, 0x00 }, { 167, 0x00 }, + { 168, 0x00 }, { 169, 0x00 }, { 170, 0x00 }, { 171, 0x00 }, + { 172, 0x00 }, { 173, 0x00 }, { 174, 0x00 }, { 175, 0x00 }, + { 176, 0x00 }, { 177, 0x00 }, { 178, 0x00 }, { 179, 0x00 }, + { 180, 0xff }, { 181, 0x00 }, { 182, 0x3f }, { 183, 0xff }, + { 184, 0x00 }, { 185, 0x3f }, { 186, 0x00 }, { 187, 0x80 }, + { 188, 0x80 }, { 189, 0x00 }, { 190, 0x00 }, { 191, 0x00 }, + + /* Page 4 */ + { 1024, 0x00 }, { 1026, 0x01 }, { 1027, 0x17 }, + { 1028, 0x01 }, { 1029, 0x17 }, { 1030, 0x7d }, { 1031, 0xd3 }, + { 1032, 0x7f }, { 1033, 0xff }, { 1034, 0x00 }, { 1035, 0x00 }, + { 1036, 0x00 }, { 1037, 0x00 }, { 1038, 0x7f }, { 1039, 0xff }, + { 1040, 0x00 }, { 1041, 0x00 }, { 1042, 0x00 }, { 1043, 0x00 }, + { 1044, 0x00 }, { 1045, 0x00 }, { 1046, 0x00 }, { 1047, 0x00 }, + { 1048, 0x7f }, { 1049, 0xff }, { 1050, 0x00 }, { 1051, 0x00 }, + { 1052, 0x00 }, { 1053, 0x00 }, { 1054, 0x00 }, { 1055, 0x00 }, + { 1056, 0x00 }, { 1057, 0x00 }, { 1058, 0x7f }, { 1059, 0xff }, + { 1060, 0x00 }, { 1061, 0x00 }, { 1062, 0x00 }, { 1063, 0x00 }, + { 1064, 0x00 }, { 1065, 0x00 }, { 1066, 0x00 }, { 1067, 0x00 }, + { 1068, 0x7f }, { 1069, 0xff }, { 1070, 0x00 }, { 1071, 0x00 }, + { 1072, 0x00 }, { 1073, 0x00 }, { 1074, 0x00 }, { 1075, 0x00 }, + { 1076, 0x00 }, { 1077, 0x00 }, { 1078, 0x7f }, { 1079, 0xff }, + { 1080, 0x00 }, { 1081, 0x00 }, { 1082, 0x00 }, { 1083, 0x00 }, + { 1084, 0x00 }, { 1085, 0x00 }, { 1086, 0x00 }, { 1087, 0x00 }, + { 1088, 0x00 }, { 1089, 0x00 }, { 1090, 0x00 }, { 1091, 0x00 }, + { 1092, 0x00 }, { 1093, 0x00 }, { 1094, 0x00 }, { 1095, 0x00 }, + { 1096, 0x00 }, { 1097, 0x00 }, { 1098, 0x00 }, { 1099, 0x00 }, + { 1100, 0x00 }, { 1101, 0x00 }, { 1102, 0x00 }, { 1103, 0x00 }, + { 1104, 0x00 }, { 1105, 0x00 }, { 1106, 0x00 }, { 1107, 0x00 }, + { 1108, 0x00 }, { 1109, 0x00 }, { 1110, 0x00 }, { 1111, 0x00 }, + { 1112, 0x00 }, { 1113, 0x00 }, { 1114, 0x00 }, { 1115, 0x00 }, + { 1116, 0x00 }, { 1117, 0x00 }, { 1118, 0x00 }, { 1119, 0x00 }, + { 1120, 0x00 }, { 1121, 0x00 }, { 1122, 0x00 }, { 1123, 0x00 }, + { 1124, 0x00 }, { 1125, 0x00 }, { 1126, 0x00 }, { 1127, 0x00 }, + { 1128, 0x00 }, { 1129, 0x00 }, { 1130, 0x00 }, { 1131, 0x00 }, + { 1132, 0x00 }, { 1133, 0x00 }, { 1134, 0x00 }, { 1135, 0x00 }, + { 1136, 0x00 }, { 1137, 0x00 }, { 1138, 0x00 }, { 1139, 0x00 }, + { 1140, 0x00 }, { 1141, 0x00 }, { 1142, 0x00 }, { 1143, 0x00 }, + { 1144, 0x00 }, { 1145, 0x00 }, { 1146, 0x00 }, { 1147, 0x00 }, + { 1148, 0x00 }, { 1149, 0x00 }, { 1150, 0x00 }, { 1151, 0x00 }, +}; + +static bool adc3xxx_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADC3XXX_RESET: + return true; + default: + return false; + } +} + +static const struct regmap_range_cfg adc3xxx_ranges[] = { + { + .range_min = 0, + .range_max = 5 * ADC3XXX_PAGE_SIZE, + .selector_reg = ADC3XXX_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = ADC3XXX_PAGE_SIZE, + } +}; + +static const struct regmap_config adc3xxx_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = adc3xxx_defaults, + .num_reg_defaults = ARRAY_SIZE(adc3xxx_defaults), + + .volatile_reg = adc3xxx_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + + .ranges = adc3xxx_ranges, + .num_ranges = ARRAY_SIZE(adc3xxx_ranges), + .max_register = 5 * ADC3XXX_PAGE_SIZE, +}; + +struct adc3xxx_rate_divs { + u32 mclk; + u32 rate; + u8 pll_p; + u8 pll_r; + u8 pll_j; + u16 pll_d; + u8 nadc; + u8 madc; + u8 aosr; +}; + +/* + * PLL and Clock settings. + * If p member is 0, PLL is not used. + * The order of the entries in this table have the PLL entries before + * the non-PLL entries, so that the PLL modes are preferred unless + * the PLL mode setting says otherwise. + */ +static const struct adc3xxx_rate_divs adc3xxx_divs[] = { + /* mclk, rate, p, r, j, d, nadc, madc, aosr */ + /* 8k rate */ + { 12000000, 8000, 1, 1, 7, 1680, 42, 2, 128 }, + { 12288000, 8000, 1, 1, 7, 0000, 42, 2, 128 }, + /* 11.025k rate */ + { 12000000, 11025, 1, 1, 6, 8208, 29, 2, 128 }, + /* 16k rate */ + { 12000000, 16000, 1, 1, 7, 1680, 21, 2, 128 }, + { 12288000, 16000, 1, 1, 7, 0000, 21, 2, 128 }, + /* 22.05k rate */ + { 12000000, 22050, 1, 1, 7, 560, 15, 2, 128 }, + /* 32k rate */ + { 12000000, 32000, 1, 1, 8, 1920, 12, 2, 128 }, + { 12288000, 32000, 1, 1, 8, 0000, 12, 2, 128 }, + /* 44.1k rate */ + { 12000000, 44100, 1, 1, 7, 5264, 8, 2, 128 }, + /* 48k rate */ + { 12000000, 48000, 1, 1, 7, 1680, 7, 2, 128 }, + { 12288000, 48000, 1, 1, 7, 0000, 7, 2, 128 }, + { 24576000, 48000, 1, 1, 3, 5000, 7, 2, 128 }, /* With PLL */ + { 24576000, 48000, 0, 0, 0, 0000, 2, 2, 128 }, /* Without PLL */ + /* 88.2k rate */ + { 12000000, 88200, 1, 1, 7, 5264, 4, 4, 64 }, + /* 96k rate */ + { 12000000, 96000, 1, 1, 8, 1920, 4, 4, 64 }, +}; + +static int adc3xxx_get_divs(struct device *dev, int mclk, int rate, int pll_mode) +{ + int i; + + dev_dbg(dev, "mclk = %d, rate = %d, clock mode %u\n", + mclk, rate, pll_mode); + for (i = 0; i < ARRAY_SIZE(adc3xxx_divs); i++) { + const struct adc3xxx_rate_divs *mode = &adc3xxx_divs[i]; + + /* Skip this entry if it doesn't fulfill the intended clock + * mode requirement. We consider anything besides the two + * modes below to be the same as ADC3XXX_PLL_AUTO. + */ + if ((pll_mode == ADC3XXX_PLL_BYPASS && mode->pll_p) || + (pll_mode == ADC3XXX_PLL_ENABLE && !mode->pll_p)) + continue; + + if (mode->rate == rate && mode->mclk == mclk) + return i; + } + + dev_info(dev, "Master clock rate %d and sample rate %d is not supported\n", + mclk, rate); + return -EINVAL; +} + +static int adc3xxx_pll_delay(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + /* 10msec delay needed after PLL power-up to allow + * PLL and dividers to stabilize (datasheet p13). + */ + usleep_range(10000, 20000); + + return 0; +} + +static int adc3xxx_coefficient_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int numcoeff = kcontrol->private_value >> 16; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = numcoeff; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; /* all coefficients are 16 bit */ + return 0; +} + +static int adc3xxx_coefficient_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int numcoeff = kcontrol->private_value >> 16; + int reg = kcontrol->private_value & 0xffff; + int index = 0; + + for (index = 0; index < numcoeff; index++) { + unsigned int value_msb, value_lsb, value; + + value_msb = snd_soc_component_read(component, reg++); + if ((int)value_msb < 0) + return (int)value_msb; + + value_lsb = snd_soc_component_read(component, reg++); + if ((int)value_lsb < 0) + return (int)value_lsb; + + value = (value_msb << 8) | value_lsb; + ucontrol->value.integer.value[index] = value; + } + + return 0; +} + +static int adc3xxx_coefficient_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int numcoeff = kcontrol->private_value >> 16; + int reg = kcontrol->private_value & 0xffff; + int index = 0; + int ret; + + for (index = 0; index < numcoeff; index++) { + unsigned int value = ucontrol->value.integer.value[index]; + unsigned int value_msb = (value >> 8) & 0xff; + unsigned int value_lsb = value & 0xff; + + ret = snd_soc_component_write(component, reg++, value_msb); + if (ret) + return ret; + + ret = snd_soc_component_write(component, reg++, value_lsb); + if (ret) + return ret; + } + + return 0; +} + +/* All on-chip filters have coefficients which are expressed in terms of + * 16 bit values, so represent them as strings of 16-bit integers. + */ +#define TI_COEFFICIENTS(xname, reg, numcoeffs) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = adc3xxx_coefficient_info, \ + .get = adc3xxx_coefficient_get,\ + .put = adc3xxx_coefficient_put, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .private_value = reg | (numcoeffs << 16) \ +} + +static const char * const adc_softstepping_text[] = { "1 step", "2 step", "off" }; +static SOC_ENUM_SINGLE_DECL(adc_softstepping_enum, ADC3XXX_ADC_DIGITAL, 0, + adc_softstepping_text); + +static const char * const multiplier_text[] = { "1", "2", "4", "8", "16", "32", "64", "128" }; +static SOC_ENUM_SINGLE_DECL(left_agc_attack_mult_enum, + ADC3XXX_LEFT_CHN_AGC_4, 0, multiplier_text); +static SOC_ENUM_SINGLE_DECL(right_agc_attack_mult_enum, + ADC3XXX_RIGHT_CHN_AGC_4, 0, multiplier_text); +static SOC_ENUM_SINGLE_DECL(left_agc_decay_mult_enum, + ADC3XXX_LEFT_CHN_AGC_5, 0, multiplier_text); +static SOC_ENUM_SINGLE_DECL(right_agc_decay_mult_enum, + ADC3XXX_RIGHT_CHN_AGC_5, 0, multiplier_text); + +static const char * const dither_dc_offset_text[] = { + "0mV", "15mV", "30mV", "45mV", "60mV", "75mV", "90mV", "105mV", + "-15mV", "-30mV", "-45mV", "-60mV", "-75mV", "-90mV", "-105mV" +}; +static const unsigned int dither_dc_offset_values[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15 +}; +static SOC_VALUE_ENUM_DOUBLE_DECL(dither_dc_offset_enum, + ADC3XXX_DITHER_CTRL, + 4, 0, 0xf, dither_dc_offset_text, + dither_dc_offset_values); + +static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_fine_tlv, -40, 10, 0); +/* AGC target: 8 values: -5.5, -8, -10, -12, -14, -17, -20, -24 dB */ +/* It would be nice to declare these in the order above, but empirically + * TLV_DB_SCALE_ITEM doesn't take lightly to the increment (second) parameter + * being negative, despite there being examples to the contrary in other + * drivers. So declare these in the order from lowest to highest, and + * set the invert flag in the SOC_DOUBLE_R_TLV declaration instead. + */ +static const DECLARE_TLV_DB_RANGE(agc_target_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-2400, 0, 0), + 1, 3, TLV_DB_SCALE_ITEM(-2000, 300, 0), + 4, 6, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 7, 7, TLV_DB_SCALE_ITEM(-550, 0, 0)); +/* Since the 'disabled' value (mute) is at the highest value in the dB + * range (i.e. just before -32 dB) rather than the lowest, we need to resort + * to using a TLV_DB_RANGE in order to get the mute value in the right place. + */ +static const DECLARE_TLV_DB_RANGE(agc_thresh_tlv, + 0, 30, TLV_DB_SCALE_ITEM(-9000, 200, 0), + 31, 31, TLV_DB_SCALE_ITEM(0, 0, 1)); /* disabled = mute */ +/* AGC hysteresis: 4 values: 1, 2, 4 dB, disabled (= mute) */ +static const DECLARE_TLV_DB_RANGE(agc_hysteresis_tlv, + 0, 1, TLV_DB_SCALE_ITEM(100, 100, 0), + 2, 2, TLV_DB_SCALE_ITEM(400, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(0, 0, 1)); /* disabled = mute */ +static const DECLARE_TLV_DB_SCALE(agc_max_tlv, 0, 50, 0); +/* Input attenuation: -6 dB or 0 dB */ +static const DECLARE_TLV_DB_SCALE(input_attenuation_tlv, -600, 600, 0); + +static const struct snd_kcontrol_new adc3xxx_snd_controls[] = { + SOC_DOUBLE_R_TLV("PGA Capture Volume", ADC3XXX_LEFT_APGA_CTRL, + ADC3XXX_RIGHT_APGA_CTRL, 0, 80, 0, pga_tlv), + SOC_DOUBLE("PGA Capture Switch", ADC3XXX_ADC_FGA, 7, 3, 1, 1), + SOC_DOUBLE_R("AGC Capture Switch", ADC3XXX_LEFT_CHN_AGC_1, + ADC3XXX_RIGHT_CHN_AGC_1, 7, 1, 0), + SOC_DOUBLE_R_TLV("AGC Target Level Capture Volume", ADC3XXX_LEFT_CHN_AGC_1, + ADC3XXX_RIGHT_CHN_AGC_2, 4, 0x07, 1, agc_target_tlv), + SOC_DOUBLE_R_TLV("AGC Noise Threshold Capture Volume", ADC3XXX_LEFT_CHN_AGC_2, + ADC3XXX_RIGHT_CHN_AGC_2, 1, 0x1f, 1, agc_thresh_tlv), + SOC_DOUBLE_R_TLV("AGC Hysteresis Capture Volume", ADC3XXX_LEFT_CHN_AGC_2, + ADC3XXX_RIGHT_CHN_AGC_2, 6, 3, 0, agc_hysteresis_tlv), + SOC_DOUBLE_R("AGC Clip Stepping Capture Switch", ADC3XXX_LEFT_CHN_AGC_2, + ADC3XXX_RIGHT_CHN_AGC_2, 0, 1, 0), + /* + * Oddly enough, the data sheet says the default value + * for the left/right AGC maximum gain register field + * (ADC3XXX_LEFT/RIGHT_CHN_AGC_3 bits 0..6) is 0x7f = 127 + * (verified empirically) even though this value (indeed, above + * 0x50) is specified as 'Reserved. Do not use.' in the accompanying + * table in the data sheet. + */ + SOC_DOUBLE_R_TLV("AGC Maximum Capture Volume", ADC3XXX_LEFT_CHN_AGC_3, + ADC3XXX_RIGHT_CHN_AGC_3, 0, 0x50, 0, agc_max_tlv), + SOC_DOUBLE_R("AGC Attack Time", ADC3XXX_LEFT_CHN_AGC_4, + ADC3XXX_RIGHT_CHN_AGC_4, 3, 0x1f, 0), + /* Would like to have the multipliers as LR pairs, but there is + * no SOC_ENUM_foo which accepts two values in separate registers. + */ + SOC_ENUM("AGC Left Attack Time Multiplier", left_agc_attack_mult_enum), + SOC_ENUM("AGC Right Attack Time Multiplier", right_agc_attack_mult_enum), + SOC_DOUBLE_R("AGC Decay Time", ADC3XXX_LEFT_CHN_AGC_5, + ADC3XXX_RIGHT_CHN_AGC_5, 3, 0x1f, 0), + SOC_ENUM("AGC Left Decay Time Multiplier", left_agc_decay_mult_enum), + SOC_ENUM("AGC Right Decay Time Multiplier", right_agc_decay_mult_enum), + SOC_DOUBLE_R("AGC Noise Debounce", ADC3XXX_LEFT_CHN_AGC_6, + ADC3XXX_RIGHT_CHN_AGC_6, 0, 0x1f, 0), + SOC_DOUBLE_R("AGC Signal Debounce", ADC3XXX_LEFT_CHN_AGC_7, + ADC3XXX_RIGHT_CHN_AGC_7, 0, 0x0f, 0), + /* Read only register */ + SOC_DOUBLE_R_S_TLV("AGC Applied Capture Volume", ADC3XXX_LEFT_AGC_GAIN, + ADC3XXX_RIGHT_AGC_GAIN, 0, -24, 40, 6, 0, adc_tlv), + /* ADC soft stepping */ + SOC_ENUM("ADC Soft Stepping", adc_softstepping_enum), + /* Left/Right Input attenuation */ + SOC_SINGLE_TLV("Left Input IN_1L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input IN_2L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 2, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input IN_3L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input IN_1R Capture Volume", + ADC3XXX_LEFT_PGA_SEL_2, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input DIF_2L_3L Capture Volume", + ADC3XXX_LEFT_PGA_SEL_1, 6, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input DIF_1L_1R Capture Volume", + ADC3XXX_LEFT_PGA_SEL_2, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Left Input DIF_2R_3R Capture Volume", + ADC3XXX_LEFT_PGA_SEL_2, 2, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_1R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_2R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 2, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_3R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input IN_1L Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_2, 0, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input DIF_2R_3R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_1, 6, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input DIF_1L_1R Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_2, 4, 1, 1, input_attenuation_tlv), + SOC_SINGLE_TLV("Right Input DIF_2L_3L Capture Volume", + ADC3XXX_RIGHT_PGA_SEL_2, 2, 1, 1, input_attenuation_tlv), + SOC_DOUBLE_R_S_TLV("ADC Volume Control Capture Volume", ADC3XXX_LADC_VOL, + ADC3XXX_RADC_VOL, 0, -24, 40, 6, 0, adc_tlv), + /* Empirically, the following doesn't work the way it's supposed + * to. Values 0, -0.1, -0.2 and -0.3 dB result in the same level, and + * -0.4 dB drops about 0.12 dB on a specific chip. + */ + SOC_DOUBLE_TLV("ADC Fine Volume Control Capture Volume", ADC3XXX_ADC_FGA, + 4, 0, 4, 1, adc_fine_tlv), + SOC_SINGLE("Left ADC Unselected CM Bias Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 6, 1, 0), + SOC_SINGLE("Right ADC Unselected CM Bias Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 6, 1, 0), + SOC_ENUM("Dither Control DC Offset", dither_dc_offset_enum), + + /* Coefficient memory for miniDSP. */ + /* For the default PRB_R1 processing block, the only available + * filter is the first order IIR. + */ + + TI_COEFFICIENTS("Left ADC IIR Coefficients N0 N1 D1", + ADC3XXX_LEFT_ADC_IIR_COEFF_N0_MSB, 3), + + TI_COEFFICIENTS("Right ADC IIR Coefficients N0 N1 D1", + ADC3XXX_RIGHT_ADC_IIR_COEFF_N0_MSB, 3), +}; + +/* Left input selection, Single Ended inputs and Differential inputs */ +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN_1L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 1, 0x1, 1), + SOC_DAPM_SINGLE("IN_2L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_3L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2L_3L Capture Switch", + ADC3XXX_LEFT_PGA_SEL_1, 7, 0x1, 1), + SOC_DAPM_SINGLE("DIF_1L_1R Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2R_3R Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_1R Capture Switch", + ADC3XXX_LEFT_PGA_SEL_2, 1, 0x1, 1), +}; + +/* Right input selection, Single Ended inputs and Differential inputs */ +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN_1R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 1, 0x1, 1), + SOC_DAPM_SINGLE("IN_2R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_3R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2R_3R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_1, 7, 0x1, 1), + SOC_DAPM_SINGLE("DIF_1L_1R Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 5, 0x1, 1), + SOC_DAPM_SINGLE("DIF_2L_3L Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 3, 0x1, 1), + SOC_DAPM_SINGLE("IN_1L Capture Switch", + ADC3XXX_RIGHT_PGA_SEL_2, 1, 0x1, 1), +}; + +/* Left Digital Mic input for left ADC */ +static const struct snd_kcontrol_new left_input_dmic_controls[] = { + SOC_DAPM_SINGLE("Left ADC Capture Switch", + ADC3XXX_ADC_DIGITAL, 3, 0x1, 0), +}; + +/* Right Digital Mic input for Right ADC */ +static const struct snd_kcontrol_new right_input_dmic_controls[] = { + SOC_DAPM_SINGLE("Right ADC Capture Switch", + ADC3XXX_ADC_DIGITAL, 2, 0x1, 0), +}; + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget adc3xxx_dapm_widgets[] = { + + /* Left Input Selection */ + SND_SOC_DAPM_MIXER("Left Input", SND_SOC_NOPM, 0, 0, + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), + /* Right Input Selection */ + SND_SOC_DAPM_MIXER("Right Input", SND_SOC_NOPM, 0, 0, + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), + /* PGA selection */ + SND_SOC_DAPM_PGA("Left PGA", ADC3XXX_LEFT_APGA_CTRL, 7, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right PGA", ADC3XXX_RIGHT_APGA_CTRL, 7, 1, NULL, 0), + + /* Digital Microphone Input Control for Left/Right ADC */ + SND_SOC_DAPM_MIXER("Left DMic Input", SND_SOC_NOPM, 0, 0, + &left_input_dmic_controls[0], + ARRAY_SIZE(left_input_dmic_controls)), + SND_SOC_DAPM_MIXER("Right DMic Input", SND_SOC_NOPM, 0, 0, + &right_input_dmic_controls[0], + ARRAY_SIZE(right_input_dmic_controls)), + + /* Left/Right ADC */ + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ADC3XXX_ADC_DIGITAL, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ADC3XXX_ADC_DIGITAL, 6, 0), + + /* Inputs */ + SND_SOC_DAPM_INPUT("IN_1L"), + SND_SOC_DAPM_INPUT("IN_1R"), + SND_SOC_DAPM_INPUT("IN_2L"), + SND_SOC_DAPM_INPUT("IN_2R"), + SND_SOC_DAPM_INPUT("IN_3L"), + SND_SOC_DAPM_INPUT("IN_3R"), + SND_SOC_DAPM_INPUT("DIFL_1L_1R"), + SND_SOC_DAPM_INPUT("DIFL_2L_3L"), + SND_SOC_DAPM_INPUT("DIFL_2R_3R"), + SND_SOC_DAPM_INPUT("DIFR_1L_1R"), + SND_SOC_DAPM_INPUT("DIFR_2L_3L"), + SND_SOC_DAPM_INPUT("DIFR_2R_3R"), + SND_SOC_DAPM_INPUT("DMic_L"), + SND_SOC_DAPM_INPUT("DMic_R"), + + /* Digital audio interface output */ + SND_SOC_DAPM_AIF_OUT("AIF_OUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + + /* Clocks */ + SND_SOC_DAPM_SUPPLY("PLL_CLK", ADC3XXX_PLL_PROG_PR, ADC3XXX_ENABLE_PLL_SHIFT, + 0, adc3xxx_pll_delay, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("ADC_CLK", ADC3XXX_ADC_NADC, ADC3XXX_ENABLE_NADC_SHIFT, + 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_MOD_CLK", ADC3XXX_ADC_MADC, ADC3XXX_ENABLE_MADC_SHIFT, + 0, NULL, 0), + + /* This refers to the generated BCLK in master mode. */ + SND_SOC_DAPM_SUPPLY("BCLK", ADC3XXX_BCLK_N_DIV, ADC3XXX_ENABLE_BCLK_SHIFT, + 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route adc3xxx_intercon[] = { + /* Left input selection from switches */ + { "Left Input", "IN_1L Capture Switch", "IN_1L" }, + { "Left Input", "IN_2L Capture Switch", "IN_2L" }, + { "Left Input", "IN_3L Capture Switch", "IN_3L" }, + { "Left Input", "DIF_2L_3L Capture Switch", "DIFL_2L_3L" }, + { "Left Input", "DIF_1L_1R Capture Switch", "DIFL_1L_1R" }, + { "Left Input", "DIF_2R_3R Capture Switch", "DIFL_2R_3R" }, + { "Left Input", "IN_1R Capture Switch", "IN_1R" }, + + /* Left input selection to left PGA */ + { "Left PGA", NULL, "Left Input" }, + + /* Left PGA to left ADC */ + { "Left ADC", NULL, "Left PGA" }, + + /* Right input selection from switches */ + { "Right Input", "IN_1R Capture Switch", "IN_1R" }, + { "Right Input", "IN_2R Capture Switch", "IN_2R" }, + { "Right Input", "IN_3R Capture Switch", "IN_3R" }, + { "Right Input", "DIF_2R_3R Capture Switch", "DIFR_2R_3R" }, + { "Right Input", "DIF_1L_1R Capture Switch", "DIFR_1L_1R" }, + { "Right Input", "DIF_2L_3L Capture Switch", "DIFR_2L_3L" }, + { "Right Input", "IN_1L Capture Switch", "IN_1L" }, + + /* Right input selection to right PGA */ + { "Right PGA", NULL, "Right Input" }, + + /* Right PGA to right ADC */ + { "Right ADC", NULL, "Right PGA" }, + + /* Left DMic Input selection from switch */ + { "Left DMic Input", "Left ADC Capture Switch", "DMic_L" }, + + /* Left DMic to left ADC */ + { "Left ADC", NULL, "Left DMic Input" }, + + /* Right DMic Input selection from switch */ + { "Right DMic Input", "Right ADC Capture Switch", "DMic_R" }, + + /* Right DMic to right ADC */ + { "Right ADC", NULL, "Right DMic Input" }, + + /* ADC to AIF output */ + { "AIF_OUT", NULL, "Left ADC" }, + { "AIF_OUT", NULL, "Right ADC" }, + + /* Clocking */ + { "ADC_MOD_CLK", NULL, "ADC_CLK" }, + { "Left ADC", NULL, "ADC_MOD_CLK" }, + { "Right ADC", NULL, "ADC_MOD_CLK" }, + + { "BCLK", NULL, "ADC_CLK" }, +}; + +static const struct snd_soc_dapm_route adc3xxx_pll_intercon[] = { + { "ADC_CLK", NULL, "PLL_CLK" }, +}; + +static const struct snd_soc_dapm_route adc3xxx_bclk_out_intercon[] = { + { "AIF_OUT", NULL, "BCLK" } +}; + +static int adc3xxx_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ + struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + + if (offset >= ADC3XXX_GPIOS_MAX) + return -EINVAL; + + /* GPIO1 is offset 0, GPIO2 is offset 1 */ + /* We check here that the GPIO pins are either not configured in the + * DT, or that they purposely are set as outputs. + * (Input mode not yet implemented). + */ + if (adc3xxx->gpio_cfg[offset] != 0 && + adc3xxx->gpio_cfg[offset] != ADC3XXX_GPIO_GPO + 1) + return -EINVAL; + + return 0; +} + +static int adc3xxx_gpio_direction_out(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + + /* Set GPIO output function. */ + return regmap_update_bits(adc3xxx->regmap, + adc3xxx_gpio_ctrl_reg[offset], + ADC3XXX_GPIO_CTRL_CFG_MASK | + ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK, + ADC3XXX_GPIO_GPO << ADC3XXX_GPIO_CTRL_CFG_SHIFT | + !!value << ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_SHIFT); +} + +/* With only GPIO outputs configured, we never get the .direction_out call, + * so we set the output mode and output value in the same call. Hence + * .set in practice does the same thing as .direction_out . + */ +static void adc3xxx_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + (void) adc3xxx_gpio_direction_out(chip, offset, value); +} + +/* Even though we only support GPIO output for now, some GPIO clients + * want to read the current pin state using the .get callback. + */ +static int adc3xxx_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct adc3xxx *adc3xxx = gpiochip_get_data(chip); + unsigned int regval; + int ret; + + /* We only allow output pins, so just read the value set in the output + * pin register field. + */ + ret = regmap_read(adc3xxx->regmap, adc3xxx_gpio_ctrl_reg[offset], ®val); + if (ret) + return ret; + return !!(regval & ADC3XXX_GPIO_CTRL_OUTPUT_CTRL_MASK); +} + +static const struct gpio_chip adc3xxx_gpio_chip = { + .label = "adc3xxx", + .owner = THIS_MODULE, + .request = adc3xxx_gpio_request, + .direction_output = adc3xxx_gpio_direction_out, + .set = adc3xxx_gpio_set, + .get = adc3xxx_gpio_get, + .can_sleep = 1, +}; + +static void adc3xxx_free_gpio(struct adc3xxx *adc3xxx) +{ +#ifdef CONFIG_GPIOLIB + gpiochip_remove(&adc3xxx->gpio_chip); +#endif +} + +static void adc3xxx_init_gpio(struct adc3xxx *adc3xxx) +{ + int gpio, micbias; + int ret; + + adc3xxx->gpio_chip = adc3xxx_gpio_chip; + adc3xxx->gpio_chip.ngpio = ADC3XXX_GPIOS_MAX; + adc3xxx->gpio_chip.parent = adc3xxx->dev; + adc3xxx->gpio_chip.base = -1; + + ret = gpiochip_add_data(&adc3xxx->gpio_chip, adc3xxx); + if (ret) + dev_err(adc3xxx->dev, "Failed to add gpios: %d\n", ret); + + /* Set up potential GPIO configuration from the devicetree. + * This allows us to set up things which are not software + * controllable GPIOs, such as PDM microphone I/O, + */ + for (gpio = 0; gpio < ADC3XXX_GPIOS_MAX; gpio++) { + unsigned int cfg = adc3xxx->gpio_cfg[gpio]; + + if (cfg) { + cfg--; /* actual value to use is stored +1 */ + regmap_update_bits(adc3xxx->regmap, + adc3xxx_gpio_ctrl_reg[gpio], + ADC3XXX_GPIO_CTRL_CFG_MASK, + cfg << ADC3XXX_GPIO_CTRL_CFG_SHIFT); + } + } + + /* Set up micbias voltage */ + for (micbias = 0; micbias < ADC3XXX_MICBIAS_PINS; micbias++) { + unsigned int vg = adc3xxx->micbias_vg[micbias]; + + regmap_update_bits(adc3xxx->regmap, + ADC3XXX_MICBIAS_CTRL, + ADC3XXX_MICBIAS_MASK << adc3xxx_micbias_shift[micbias], + vg << adc3xxx_micbias_shift[micbias]); + } +} + +static int adc3xxx_parse_dt_gpio(struct adc3xxx *adc3xxx, + const char *propname, unsigned int *cfg) +{ + struct device *dev = adc3xxx->dev; + struct device_node *np = dev->of_node; + unsigned int val; + + if (!of_property_read_u32(np, propname, &val)) { + if (val & ~15 || val == 7 || val >= 11) { + dev_err(dev, "Invalid property value for '%s'\n", propname); + return -EINVAL; + } + if (val == ADC3XXX_GPIO_GPI) + dev_warn(dev, "GPIO Input read not yet implemented\n"); + *cfg = val + 1; /* 0 => not set up, all others shifted +1 */ + } + return 0; +} + +static int adc3xxx_parse_dt_micbias(struct adc3xxx *adc3xxx, + const char *propname, unsigned int *vg) +{ + struct device *dev = adc3xxx->dev; + struct device_node *np = dev->of_node; + unsigned int val; + + if (!of_property_read_u32(np, propname, &val)) { + if (val > ADC3XXX_MICBIAS_AVDD) { + dev_err(dev, "Invalid property value for '%s'\n", propname); + return -EINVAL; + } + *vg = val; + } + return 0; +} + +static int adc3xxx_parse_pll_mode(uint32_t val, unsigned int *pll_mode) +{ + if (val != ADC3XXX_PLL_ENABLE && val != ADC3XXX_PLL_BYPASS && + val != ADC3XXX_PLL_AUTO) + return -EINVAL; + + *pll_mode = val; + + return 0; +} + +static void adc3xxx_setup_pll(struct snd_soc_component *component, + int div_entry) +{ + int i = div_entry; + + /* P & R values */ + snd_soc_component_write(component, ADC3XXX_PLL_PROG_PR, + (adc3xxx_divs[i].pll_p << ADC3XXX_PLLP_SHIFT) | + (adc3xxx_divs[i].pll_r << ADC3XXX_PLLR_SHIFT)); + /* J value */ + snd_soc_component_write(component, ADC3XXX_PLL_PROG_J, + adc3xxx_divs[i].pll_j & ADC3XXX_PLLJ_MASK); + /* D value */ + snd_soc_component_write(component, ADC3XXX_PLL_PROG_D_LSB, + adc3xxx_divs[i].pll_d & ADC3XXX_PLLD_LSB_MASK); + snd_soc_component_write(component, ADC3XXX_PLL_PROG_D_MSB, + (adc3xxx_divs[i].pll_d >> 8) & ADC3XXX_PLLD_MSB_MASK); +} + +static int adc3xxx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(dai->component); + struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component); + int i, width = 16; + u8 iface_len, bdiv; + + i = adc3xxx_get_divs(component->dev, adc3xxx->sysclk, + params_rate(params), adc3xxx->pll_mode); + + if (i < 0) + return i; + + /* select data word length */ + switch (params_width(params)) { + case 16: + iface_len = ADC3XXX_IFACE_16BITS; + width = 16; + break; + case 20: + iface_len = ADC3XXX_IFACE_20BITS; + width = 20; + break; + case 24: + iface_len = ADC3XXX_IFACE_24BITS; + width = 24; + break; + case 32: + iface_len = ADC3XXX_IFACE_32BITS; + width = 32; + break; + default: + dev_err(component->dev, "Unsupported serial data format\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, ADC3XXX_INTERFACE_CTRL_1, + ADC3XXX_WLENGTH_MASK, iface_len); + if (adc3xxx_divs[i].pll_p) { /* If PLL used for this mode */ + adc3xxx_setup_pll(component, i); + snd_soc_component_write(component, ADC3XXX_CLKGEN_MUX, ADC3XXX_USE_PLL); + if (!adc3xxx->use_pll) { + snd_soc_dapm_add_routes(dapm, adc3xxx_pll_intercon, + ARRAY_SIZE(adc3xxx_pll_intercon)); + adc3xxx->use_pll = 1; + } + } else { + snd_soc_component_write(component, ADC3XXX_CLKGEN_MUX, ADC3XXX_NO_PLL); + if (adc3xxx->use_pll) { + snd_soc_dapm_del_routes(dapm, adc3xxx_pll_intercon, + ARRAY_SIZE(adc3xxx_pll_intercon)); + adc3xxx->use_pll = 0; + } + } + + /* NADC */ + snd_soc_component_update_bits(component, ADC3XXX_ADC_NADC, + ADC3XXX_NADC_MASK, adc3xxx_divs[i].nadc); + /* MADC */ + snd_soc_component_update_bits(component, ADC3XXX_ADC_MADC, + ADC3XXX_MADC_MASK, adc3xxx_divs[i].madc); + /* AOSR */ + snd_soc_component_update_bits(component, ADC3XXX_ADC_AOSR, + ADC3XXX_AOSR_MASK, adc3xxx_divs[i].aosr); + /* BDIV N Value */ + /* BCLK is (by default) set up to be derived from ADC_CLK */ + bdiv = (adc3xxx_divs[i].aosr * adc3xxx_divs[i].madc) / (2 * width); + snd_soc_component_update_bits(component, ADC3XXX_BCLK_N_DIV, + ADC3XXX_BDIV_MASK, bdiv); + + return 0; +} + +static const char *adc3xxx_pll_mode_text(int pll_mode) +{ + switch (pll_mode) { + case ADC3XXX_PLL_AUTO: + return "PLL auto"; + case ADC3XXX_PLL_ENABLE: + return "PLL enable"; + case ADC3XXX_PLL_BYPASS: + return "PLL bypass"; + default: + break; + } + + return "PLL unknown"; +} + +static int adc3xxx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component); + int ret; + + ret = adc3xxx_parse_pll_mode(clk_id, &adc3xxx->pll_mode); + if (ret < 0) + return ret; + + adc3xxx->sysclk = freq; + dev_dbg(component->dev, "Set sysclk to %u Hz, %s\n", + freq, adc3xxx_pll_mode_text(adc3xxx->pll_mode)); + return 0; +} + +static int adc3xxx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct adc3xxx *adc3xxx = snd_soc_component_get_drvdata(component); + u8 clkdir = 0, format = 0; + int master = 0; + int ret; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + master = 1; + clkdir = ADC3XXX_BCLK_MASTER | ADC3XXX_WCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + master = 0; + break; + default: + dev_err(component->dev, "Invalid DAI clock setup\n"); + return -EINVAL; + } + + /* + * match both interface format and signal polarities since they + * are fixed + */ + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK)) { + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF: + format = ADC3XXX_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF: + format = ADC3XXX_FORMAT_DSP; + break; + case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF: + format = ADC3XXX_FORMAT_DSP; + break; + case SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF: + format = ADC3XXX_FORMAT_RJF; + break; + case SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF: + format = ADC3XXX_FORMAT_LJF; + break; + default: + dev_err(component->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + /* Add/del route enabling BCLK output as applicable */ + if (master && !adc3xxx->master) + snd_soc_dapm_add_routes(dapm, adc3xxx_bclk_out_intercon, + ARRAY_SIZE(adc3xxx_bclk_out_intercon)); + else if (!master && adc3xxx->master) + snd_soc_dapm_del_routes(dapm, adc3xxx_bclk_out_intercon, + ARRAY_SIZE(adc3xxx_bclk_out_intercon)); + adc3xxx->master = master; + + /* set clock direction and format */ + ret = snd_soc_component_update_bits(component, + ADC3XXX_INTERFACE_CTRL_1, + ADC3XXX_CLKDIR_MASK | ADC3XXX_FORMAT_MASK, + clkdir | format); + if (ret < 0) + return ret; + return 0; +} + +static const struct snd_soc_dai_ops adc3xxx_dai_ops = { + .hw_params = adc3xxx_hw_params, + .set_sysclk = adc3xxx_set_dai_sysclk, + .set_fmt = adc3xxx_set_dai_fmt, +}; + +static struct snd_soc_dai_driver adc3xxx_dai = { + .name = "tlv320adc3xxx-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = ADC3XXX_RATES, + .formats = ADC3XXX_FORMATS, + }, + .ops = &adc3xxx_dai_ops, +}; + +static const struct snd_soc_component_driver soc_component_dev_adc3xxx = { + .controls = adc3xxx_snd_controls, + .num_controls = ARRAY_SIZE(adc3xxx_snd_controls), + .dapm_widgets = adc3xxx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adc3xxx_dapm_widgets), + .dapm_routes = adc3xxx_intercon, + .num_dapm_routes = ARRAY_SIZE(adc3xxx_intercon), + .endianness = 1, +}; + +static const struct i2c_device_id adc3xxx_i2c_id[] = { + { "tlv320adc3001", ADC3001 }, + { "tlv320adc3101", ADC3101 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id); + +static int adc3xxx_i2c_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct adc3xxx *adc3xxx = NULL; + const struct i2c_device_id *id; + int ret; + + adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL); + if (!adc3xxx) + return -ENOMEM; + adc3xxx->dev = dev; + + adc3xxx->rst_pin = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(adc3xxx->rst_pin)) { + return dev_err_probe(dev, PTR_ERR(adc3xxx->rst_pin), + "Failed to request rst_pin\n"); + } + + adc3xxx->mclk = devm_clk_get(dev, NULL); + if (IS_ERR(adc3xxx->mclk)) { + /* + * The chip itself supports running off the BCLK either + * directly or via the PLL, but the driver does not (yet), so + * having a specified mclk is required. Otherwise, we could + * use the lack of a clocks property to indicate when BCLK is + * intended as the clock source. + */ + return dev_err_probe(dev, PTR_ERR(adc3xxx->mclk), + "Failed to acquire MCLK\n"); + } else if (adc3xxx->mclk) { + ret = clk_prepare_enable(adc3xxx->mclk); + if (ret < 0) + return ret; + dev_dbg(dev, "Enabled MCLK, freq %lu Hz\n", clk_get_rate(adc3xxx->mclk)); + } + + ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmdin-gpio1", &adc3xxx->gpio_cfg[0]); + if (ret < 0) + goto err_unprepare_mclk; + ret = adc3xxx_parse_dt_gpio(adc3xxx, "ti,dmclk-gpio2", &adc3xxx->gpio_cfg[1]); + if (ret < 0) + goto err_unprepare_mclk; + ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias1-vg", &adc3xxx->micbias_vg[0]); + if (ret < 0) + goto err_unprepare_mclk; + ret = adc3xxx_parse_dt_micbias(adc3xxx, "ti,micbias2-vg", &adc3xxx->micbias_vg[1]); + if (ret < 0) + goto err_unprepare_mclk; + + adc3xxx->regmap = devm_regmap_init_i2c(i2c, &adc3xxx_regmap); + if (IS_ERR(adc3xxx->regmap)) { + ret = PTR_ERR(adc3xxx->regmap); + goto err_unprepare_mclk; + } + + i2c_set_clientdata(i2c, adc3xxx); + + id = i2c_match_id(adc3xxx_i2c_id, i2c); + adc3xxx->type = id->driver_data; + + /* Reset codec chip */ + gpiod_set_value_cansleep(adc3xxx->rst_pin, 1); + usleep_range(2000, 100000); /* Requirement: > 10 ns (datasheet p13) */ + gpiod_set_value_cansleep(adc3xxx->rst_pin, 0); + + /* Potentially set up pins used as GPIOs */ + adc3xxx_init_gpio(adc3xxx); + + ret = snd_soc_register_component(dev, + &soc_component_dev_adc3xxx, &adc3xxx_dai, 1); + if (ret < 0) { + dev_err(dev, "Failed to register codec: %d\n", ret); + goto err_unprepare_mclk; + } + + return 0; + +err_unprepare_mclk: + clk_disable_unprepare(adc3xxx->mclk); + return ret; +} + +static void __exit adc3xxx_i2c_remove(struct i2c_client *client) +{ + struct adc3xxx *adc3xxx = i2c_get_clientdata(client); + + if (adc3xxx->mclk) + clk_disable_unprepare(adc3xxx->mclk); + adc3xxx_free_gpio(adc3xxx); + snd_soc_unregister_component(&client->dev); +} + +static const struct of_device_id tlv320adc3xxx_of_match[] = { + { .compatible = "ti,tlv320adc3001", }, + { .compatible = "ti,tlv320adc3101", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tlv320adc3xxx_of_match); + +static struct i2c_driver adc3xxx_i2c_driver = { + .driver = { + .name = "tlv320adc3xxx-codec", + .of_match_table = tlv320adc3xxx_of_match, + }, + .probe = adc3xxx_i2c_probe, + .remove = __exit_p(adc3xxx_i2c_remove), + .id_table = adc3xxx_i2c_id, +}; + +module_i2c_driver(adc3xxx_i2c_driver); + +MODULE_DESCRIPTION("ASoC TLV320ADC3xxx codec driver"); +MODULE_AUTHOR("shahina.s@mistralsolutions.com"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c new file mode 100644 index 0000000000..41342b3406 --- /dev/null +++ b/sound/soc/codecs/tlv320adcx140.c @@ -0,0 +1,1218 @@ +// SPDX-License-Identifier: GPL-2.0 +// TLV320ADCX140 Sound driver +// Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlv320adcx140.h" + +struct adcx140_priv { + struct snd_soc_component *component; + struct regulator *supply_areg; + struct gpio_desc *gpio_reset; + struct regmap *regmap; + struct device *dev; + + bool micbias_vg; + bool phase_calib_on; + + unsigned int dai_fmt; + unsigned int slot_width; +}; + +static const char * const gpo_config_names[] = { + "ti,gpo-config-1", + "ti,gpo-config-2", + "ti,gpo-config-3", + "ti,gpo-config-4", +}; + +static const struct reg_default adcx140_reg_defaults[] = { + { ADCX140_PAGE_SELECT, 0x00 }, + { ADCX140_SW_RESET, 0x00 }, + { ADCX140_SLEEP_CFG, 0x00 }, + { ADCX140_SHDN_CFG, 0x05 }, + { ADCX140_ASI_CFG0, 0x30 }, + { ADCX140_ASI_CFG1, 0x00 }, + { ADCX140_ASI_CFG2, 0x00 }, + { ADCX140_ASI_CH1, 0x00 }, + { ADCX140_ASI_CH2, 0x01 }, + { ADCX140_ASI_CH3, 0x02 }, + { ADCX140_ASI_CH4, 0x03 }, + { ADCX140_ASI_CH5, 0x04 }, + { ADCX140_ASI_CH6, 0x05 }, + { ADCX140_ASI_CH7, 0x06 }, + { ADCX140_ASI_CH8, 0x07 }, + { ADCX140_MST_CFG0, 0x02 }, + { ADCX140_MST_CFG1, 0x48 }, + { ADCX140_ASI_STS, 0xff }, + { ADCX140_CLK_SRC, 0x10 }, + { ADCX140_PDMCLK_CFG, 0x40 }, + { ADCX140_PDM_CFG, 0x00 }, + { ADCX140_GPIO_CFG0, 0x22 }, + { ADCX140_GPO_CFG0, 0x00 }, + { ADCX140_GPO_CFG1, 0x00 }, + { ADCX140_GPO_CFG2, 0x00 }, + { ADCX140_GPO_CFG3, 0x00 }, + { ADCX140_GPO_VAL, 0x00 }, + { ADCX140_GPIO_MON, 0x00 }, + { ADCX140_GPI_CFG0, 0x00 }, + { ADCX140_GPI_CFG1, 0x00 }, + { ADCX140_GPI_MON, 0x00 }, + { ADCX140_INT_CFG, 0x00 }, + { ADCX140_INT_MASK0, 0xff }, + { ADCX140_INT_LTCH0, 0x00 }, + { ADCX140_BIAS_CFG, 0x00 }, + { ADCX140_CH1_CFG0, 0x00 }, + { ADCX140_CH1_CFG1, 0x00 }, + { ADCX140_CH1_CFG2, 0xc9 }, + { ADCX140_CH1_CFG3, 0x80 }, + { ADCX140_CH1_CFG4, 0x00 }, + { ADCX140_CH2_CFG0, 0x00 }, + { ADCX140_CH2_CFG1, 0x00 }, + { ADCX140_CH2_CFG2, 0xc9 }, + { ADCX140_CH2_CFG3, 0x80 }, + { ADCX140_CH2_CFG4, 0x00 }, + { ADCX140_CH3_CFG0, 0x00 }, + { ADCX140_CH3_CFG1, 0x00 }, + { ADCX140_CH3_CFG2, 0xc9 }, + { ADCX140_CH3_CFG3, 0x80 }, + { ADCX140_CH3_CFG4, 0x00 }, + { ADCX140_CH4_CFG0, 0x00 }, + { ADCX140_CH4_CFG1, 0x00 }, + { ADCX140_CH4_CFG2, 0xc9 }, + { ADCX140_CH4_CFG3, 0x80 }, + { ADCX140_CH4_CFG4, 0x00 }, + { ADCX140_CH5_CFG2, 0xc9 }, + { ADCX140_CH5_CFG3, 0x80 }, + { ADCX140_CH5_CFG4, 0x00 }, + { ADCX140_CH6_CFG2, 0xc9 }, + { ADCX140_CH6_CFG3, 0x80 }, + { ADCX140_CH6_CFG4, 0x00 }, + { ADCX140_CH7_CFG2, 0xc9 }, + { ADCX140_CH7_CFG3, 0x80 }, + { ADCX140_CH7_CFG4, 0x00 }, + { ADCX140_CH8_CFG2, 0xc9 }, + { ADCX140_CH8_CFG3, 0x80 }, + { ADCX140_CH8_CFG4, 0x00 }, + { ADCX140_DSP_CFG0, 0x01 }, + { ADCX140_DSP_CFG1, 0x40 }, + { ADCX140_DRE_CFG0, 0x7b }, + { ADCX140_AGC_CFG0, 0xe7 }, + { ADCX140_IN_CH_EN, 0xf0 }, + { ADCX140_ASI_OUT_CH_EN, 0x00 }, + { ADCX140_PWR_CFG, 0x00 }, + { ADCX140_DEV_STS0, 0x00 }, + { ADCX140_DEV_STS1, 0x80 }, +}; + +static const struct regmap_range_cfg adcx140_ranges[] = { + { + .range_min = 0, + .range_max = 12 * 128, + .selector_reg = ADCX140_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static bool adcx140_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADCX140_SW_RESET: + case ADCX140_DEV_STS0: + case ADCX140_DEV_STS1: + case ADCX140_ASI_STS: + return true; + default: + return false; + } +} + +static const struct regmap_config adcx140_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .reg_defaults = adcx140_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(adcx140_reg_defaults), + .cache_type = REGCACHE_FLAT, + .ranges = adcx140_ranges, + .num_ranges = ARRAY_SIZE(adcx140_ranges), + .max_register = 12 * 128, + .volatile_reg = adcx140_volatile, +}; + +/* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */ +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10050, 50, 0); + +/* ADC gain. From 0 to 42 dB in 1 dB steps */ +static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); + +/* DRE Level. From -12 dB to -66 dB in 1 dB steps */ +static DECLARE_TLV_DB_SCALE(dre_thresh_tlv, -6600, 100, 0); +/* DRE Max Gain. From 2 dB to 26 dB in 2 dB steps */ +static DECLARE_TLV_DB_SCALE(dre_gain_tlv, 200, 200, 0); + +/* AGC Level. From -6 dB to -36 dB in 2 dB steps */ +static DECLARE_TLV_DB_SCALE(agc_thresh_tlv, -3600, 200, 0); +/* AGC Max Gain. From 3 dB to 42 dB in 3 dB steps */ +static DECLARE_TLV_DB_SCALE(agc_gain_tlv, 300, 300, 0); + +static const char * const decimation_filter_text[] = { + "Linear Phase", "Low Latency", "Ultra-low Latency" +}; + +static SOC_ENUM_SINGLE_DECL(decimation_filter_enum, ADCX140_DSP_CFG0, 4, + decimation_filter_text); + +static const struct snd_kcontrol_new decimation_filter_controls[] = { + SOC_DAPM_ENUM("Decimation Filter", decimation_filter_enum), +}; + +static const char * const pdmclk_text[] = { + "2.8224 MHz", "1.4112 MHz", "705.6 kHz", "5.6448 MHz" +}; + +static SOC_ENUM_SINGLE_DECL(pdmclk_select_enum, ADCX140_PDMCLK_CFG, 0, + pdmclk_text); + +static const struct snd_kcontrol_new pdmclk_div_controls[] = { + SOC_DAPM_ENUM("PDM Clk Divider Select", pdmclk_select_enum), +}; + +static const char * const resistor_text[] = { + "2.5 kOhm", "10 kOhm", "20 kOhm" +}; + +static SOC_ENUM_SINGLE_DECL(in1_resistor_enum, ADCX140_CH1_CFG0, 2, + resistor_text); +static SOC_ENUM_SINGLE_DECL(in2_resistor_enum, ADCX140_CH2_CFG0, 2, + resistor_text); +static SOC_ENUM_SINGLE_DECL(in3_resistor_enum, ADCX140_CH3_CFG0, 2, + resistor_text); +static SOC_ENUM_SINGLE_DECL(in4_resistor_enum, ADCX140_CH4_CFG0, 2, + resistor_text); + +static const struct snd_kcontrol_new in1_resistor_controls[] = { + SOC_DAPM_ENUM("CH1 Resistor Select", in1_resistor_enum), +}; +static const struct snd_kcontrol_new in2_resistor_controls[] = { + SOC_DAPM_ENUM("CH2 Resistor Select", in2_resistor_enum), +}; +static const struct snd_kcontrol_new in3_resistor_controls[] = { + SOC_DAPM_ENUM("CH3 Resistor Select", in3_resistor_enum), +}; +static const struct snd_kcontrol_new in4_resistor_controls[] = { + SOC_DAPM_ENUM("CH4 Resistor Select", in4_resistor_enum), +}; + +/* Analog/Digital Selection */ +static const char * const adcx140_mic_sel_text[] = {"Analog", "Line In", "Digital"}; +static const char * const adcx140_analog_sel_text[] = {"Analog", "Line In"}; + +static SOC_ENUM_SINGLE_DECL(adcx140_mic1p_enum, + ADCX140_CH1_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic1p_control = +SOC_DAPM_ENUM("MIC1P MUX", adcx140_mic1p_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic1_analog_enum, + ADCX140_CH1_CFG0, 7, + adcx140_analog_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic1_analog_control = +SOC_DAPM_ENUM("MIC1 Analog MUX", adcx140_mic1_analog_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic1m_enum, + ADCX140_CH1_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic1m_control = +SOC_DAPM_ENUM("MIC1M MUX", adcx140_mic1m_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic2p_enum, + ADCX140_CH2_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic2p_control = +SOC_DAPM_ENUM("MIC2P MUX", adcx140_mic2p_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic2_analog_enum, + ADCX140_CH2_CFG0, 7, + adcx140_analog_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic2_analog_control = +SOC_DAPM_ENUM("MIC2 Analog MUX", adcx140_mic2_analog_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic2m_enum, + ADCX140_CH2_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic2m_control = +SOC_DAPM_ENUM("MIC2M MUX", adcx140_mic2m_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic3p_enum, + ADCX140_CH3_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic3p_control = +SOC_DAPM_ENUM("MIC3P MUX", adcx140_mic3p_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic3_analog_enum, + ADCX140_CH3_CFG0, 7, + adcx140_analog_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic3_analog_control = +SOC_DAPM_ENUM("MIC3 Analog MUX", adcx140_mic3_analog_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic3m_enum, + ADCX140_CH3_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic3m_control = +SOC_DAPM_ENUM("MIC3M MUX", adcx140_mic3m_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic4p_enum, + ADCX140_CH4_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic4p_control = +SOC_DAPM_ENUM("MIC4P MUX", adcx140_mic4p_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic4_analog_enum, + ADCX140_CH4_CFG0, 7, + adcx140_analog_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic4_analog_control = +SOC_DAPM_ENUM("MIC4 Analog MUX", adcx140_mic4_analog_enum); + +static SOC_ENUM_SINGLE_DECL(adcx140_mic4m_enum, + ADCX140_CH4_CFG0, 5, + adcx140_mic_sel_text); + +static const struct snd_kcontrol_new adcx140_dapm_mic4m_control = +SOC_DAPM_ENUM("MIC4M MUX", adcx140_mic4m_enum); + +static const struct snd_kcontrol_new adcx140_dapm_ch1_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 7, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch2_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 6, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch3_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 5, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch4_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 4, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch5_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 3, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch6_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 2, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch7_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 1, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch8_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_ASI_OUT_CH_EN, 0, 1, 0); + +static const struct snd_kcontrol_new adcx140_dapm_ch1_dre_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_CH1_CFG0, 0, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch2_dre_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_CH2_CFG0, 0, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch3_dre_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_CH3_CFG0, 0, 1, 0); +static const struct snd_kcontrol_new adcx140_dapm_ch4_dre_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_CH4_CFG0, 0, 1, 0); + +static const struct snd_kcontrol_new adcx140_dapm_dre_en_switch = + SOC_DAPM_SINGLE("Switch", ADCX140_DSP_CFG1, 3, 1, 0); + +/* Output Mixer */ +static const struct snd_kcontrol_new adcx140_output_mixer_controls[] = { + SOC_DAPM_SINGLE("Digital CH1 Switch", 0, 0, 0, 0), + SOC_DAPM_SINGLE("Digital CH2 Switch", 0, 0, 0, 0), + SOC_DAPM_SINGLE("Digital CH3 Switch", 0, 0, 0, 0), + SOC_DAPM_SINGLE("Digital CH4 Switch", 0, 0, 0, 0), +}; + +static const struct snd_soc_dapm_widget adcx140_dapm_widgets[] = { + /* Analog Differential Inputs */ + SND_SOC_DAPM_INPUT("MIC1P"), + SND_SOC_DAPM_INPUT("MIC1M"), + SND_SOC_DAPM_INPUT("MIC2P"), + SND_SOC_DAPM_INPUT("MIC2M"), + SND_SOC_DAPM_INPUT("MIC3P"), + SND_SOC_DAPM_INPUT("MIC3M"), + SND_SOC_DAPM_INPUT("MIC4P"), + SND_SOC_DAPM_INPUT("MIC4M"), + + SND_SOC_DAPM_OUTPUT("CH1_OUT"), + SND_SOC_DAPM_OUTPUT("CH2_OUT"), + SND_SOC_DAPM_OUTPUT("CH3_OUT"), + SND_SOC_DAPM_OUTPUT("CH4_OUT"), + SND_SOC_DAPM_OUTPUT("CH5_OUT"), + SND_SOC_DAPM_OUTPUT("CH6_OUT"), + SND_SOC_DAPM_OUTPUT("CH7_OUT"), + SND_SOC_DAPM_OUTPUT("CH8_OUT"), + + SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, + &adcx140_output_mixer_controls[0], + ARRAY_SIZE(adcx140_output_mixer_controls)), + + /* Input Selection to MIC_PGA */ + SND_SOC_DAPM_MUX("MIC1P Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic1p_control), + SND_SOC_DAPM_MUX("MIC2P Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic2p_control), + SND_SOC_DAPM_MUX("MIC3P Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic3p_control), + SND_SOC_DAPM_MUX("MIC4P Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic4p_control), + + /* Input Selection to MIC_PGA */ + SND_SOC_DAPM_MUX("MIC1 Analog Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic1_analog_control), + SND_SOC_DAPM_MUX("MIC2 Analog Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic2_analog_control), + SND_SOC_DAPM_MUX("MIC3 Analog Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic3_analog_control), + SND_SOC_DAPM_MUX("MIC4 Analog Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic4_analog_control), + + SND_SOC_DAPM_MUX("MIC1M Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic1m_control), + SND_SOC_DAPM_MUX("MIC2M Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic2m_control), + SND_SOC_DAPM_MUX("MIC3M Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic3m_control), + SND_SOC_DAPM_MUX("MIC4M Input Mux", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_mic4m_control), + + SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC_GAIN_CTL_CH4", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_ADC("CH1_ADC", "CH1 Capture", ADCX140_IN_CH_EN, 7, 0), + SND_SOC_DAPM_ADC("CH2_ADC", "CH2 Capture", ADCX140_IN_CH_EN, 6, 0), + SND_SOC_DAPM_ADC("CH3_ADC", "CH3 Capture", ADCX140_IN_CH_EN, 5, 0), + SND_SOC_DAPM_ADC("CH4_ADC", "CH4 Capture", ADCX140_IN_CH_EN, 4, 0), + + SND_SOC_DAPM_ADC("CH1_DIG", "CH1 Capture", ADCX140_IN_CH_EN, 7, 0), + SND_SOC_DAPM_ADC("CH2_DIG", "CH2 Capture", ADCX140_IN_CH_EN, 6, 0), + SND_SOC_DAPM_ADC("CH3_DIG", "CH3 Capture", ADCX140_IN_CH_EN, 5, 0), + SND_SOC_DAPM_ADC("CH4_DIG", "CH4 Capture", ADCX140_IN_CH_EN, 4, 0), + SND_SOC_DAPM_ADC("CH5_DIG", "CH5 Capture", ADCX140_IN_CH_EN, 3, 0), + SND_SOC_DAPM_ADC("CH6_DIG", "CH6 Capture", ADCX140_IN_CH_EN, 2, 0), + SND_SOC_DAPM_ADC("CH7_DIG", "CH7 Capture", ADCX140_IN_CH_EN, 1, 0), + SND_SOC_DAPM_ADC("CH8_DIG", "CH8 Capture", ADCX140_IN_CH_EN, 0, 0), + + + SND_SOC_DAPM_SWITCH("CH1_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch1_en_switch), + SND_SOC_DAPM_SWITCH("CH2_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch2_en_switch), + SND_SOC_DAPM_SWITCH("CH3_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch3_en_switch), + SND_SOC_DAPM_SWITCH("CH4_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch4_en_switch), + + SND_SOC_DAPM_SWITCH("CH5_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch5_en_switch), + SND_SOC_DAPM_SWITCH("CH6_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch6_en_switch), + SND_SOC_DAPM_SWITCH("CH7_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch7_en_switch), + SND_SOC_DAPM_SWITCH("CH8_ASI_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch8_en_switch), + + SND_SOC_DAPM_SWITCH("DRE_ENABLE", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_dre_en_switch), + + SND_SOC_DAPM_SWITCH("CH1_DRE_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch1_dre_en_switch), + SND_SOC_DAPM_SWITCH("CH2_DRE_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch2_dre_en_switch), + SND_SOC_DAPM_SWITCH("CH3_DRE_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch3_dre_en_switch), + SND_SOC_DAPM_SWITCH("CH4_DRE_EN", SND_SOC_NOPM, 0, 0, + &adcx140_dapm_ch4_dre_en_switch), + + SND_SOC_DAPM_MUX("IN1 Analog Mic Resistor", SND_SOC_NOPM, 0, 0, + in1_resistor_controls), + SND_SOC_DAPM_MUX("IN2 Analog Mic Resistor", SND_SOC_NOPM, 0, 0, + in2_resistor_controls), + SND_SOC_DAPM_MUX("IN3 Analog Mic Resistor", SND_SOC_NOPM, 0, 0, + in3_resistor_controls), + SND_SOC_DAPM_MUX("IN4 Analog Mic Resistor", SND_SOC_NOPM, 0, 0, + in4_resistor_controls), + + SND_SOC_DAPM_MUX("PDM Clk Div Select", SND_SOC_NOPM, 0, 0, + pdmclk_div_controls), + + SND_SOC_DAPM_MUX("Decimation Filter", SND_SOC_NOPM, 0, 0, + decimation_filter_controls), +}; + +static const struct snd_soc_dapm_route adcx140_audio_map[] = { + /* Outputs */ + {"CH1_OUT", NULL, "Output Mixer"}, + {"CH2_OUT", NULL, "Output Mixer"}, + {"CH3_OUT", NULL, "Output Mixer"}, + {"CH4_OUT", NULL, "Output Mixer"}, + + {"CH1_ASI_EN", "Switch", "CH1_ADC"}, + {"CH2_ASI_EN", "Switch", "CH2_ADC"}, + {"CH3_ASI_EN", "Switch", "CH3_ADC"}, + {"CH4_ASI_EN", "Switch", "CH4_ADC"}, + + {"CH1_ASI_EN", "Switch", "CH1_DIG"}, + {"CH2_ASI_EN", "Switch", "CH2_DIG"}, + {"CH3_ASI_EN", "Switch", "CH3_DIG"}, + {"CH4_ASI_EN", "Switch", "CH4_DIG"}, + {"CH5_ASI_EN", "Switch", "CH5_DIG"}, + {"CH6_ASI_EN", "Switch", "CH6_DIG"}, + {"CH7_ASI_EN", "Switch", "CH7_DIG"}, + {"CH8_ASI_EN", "Switch", "CH8_DIG"}, + + {"CH5_ASI_EN", "Switch", "CH5_OUT"}, + {"CH6_ASI_EN", "Switch", "CH6_OUT"}, + {"CH7_ASI_EN", "Switch", "CH7_OUT"}, + {"CH8_ASI_EN", "Switch", "CH8_OUT"}, + + {"Decimation Filter", "Linear Phase", "DRE_ENABLE"}, + {"Decimation Filter", "Low Latency", "DRE_ENABLE"}, + {"Decimation Filter", "Ultra-low Latency", "DRE_ENABLE"}, + + {"DRE_ENABLE", "Switch", "CH1_DRE_EN"}, + {"DRE_ENABLE", "Switch", "CH2_DRE_EN"}, + {"DRE_ENABLE", "Switch", "CH3_DRE_EN"}, + {"DRE_ENABLE", "Switch", "CH4_DRE_EN"}, + + {"CH1_DRE_EN", "Switch", "CH1_ADC"}, + {"CH2_DRE_EN", "Switch", "CH2_ADC"}, + {"CH3_DRE_EN", "Switch", "CH3_ADC"}, + {"CH4_DRE_EN", "Switch", "CH4_ADC"}, + + /* Mic input */ + {"CH1_ADC", NULL, "MIC_GAIN_CTL_CH1"}, + {"CH2_ADC", NULL, "MIC_GAIN_CTL_CH2"}, + {"CH3_ADC", NULL, "MIC_GAIN_CTL_CH3"}, + {"CH4_ADC", NULL, "MIC_GAIN_CTL_CH4"}, + + {"MIC_GAIN_CTL_CH1", NULL, "IN1 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH1", NULL, "IN1 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH2", NULL, "IN2 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH2", NULL, "IN2 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH3", NULL, "IN3 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH3", NULL, "IN3 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH4", NULL, "IN4 Analog Mic Resistor"}, + {"MIC_GAIN_CTL_CH4", NULL, "IN4 Analog Mic Resistor"}, + + {"IN1 Analog Mic Resistor", "2.5 kOhm", "MIC1P Input Mux"}, + {"IN1 Analog Mic Resistor", "10 kOhm", "MIC1P Input Mux"}, + {"IN1 Analog Mic Resistor", "20 kOhm", "MIC1P Input Mux"}, + + {"IN1 Analog Mic Resistor", "2.5 kOhm", "MIC1M Input Mux"}, + {"IN1 Analog Mic Resistor", "10 kOhm", "MIC1M Input Mux"}, + {"IN1 Analog Mic Resistor", "20 kOhm", "MIC1M Input Mux"}, + + {"IN2 Analog Mic Resistor", "2.5 kOhm", "MIC2P Input Mux"}, + {"IN2 Analog Mic Resistor", "10 kOhm", "MIC2P Input Mux"}, + {"IN2 Analog Mic Resistor", "20 kOhm", "MIC2P Input Mux"}, + + {"IN2 Analog Mic Resistor", "2.5 kOhm", "MIC2M Input Mux"}, + {"IN2 Analog Mic Resistor", "10 kOhm", "MIC2M Input Mux"}, + {"IN2 Analog Mic Resistor", "20 kOhm", "MIC2M Input Mux"}, + + {"IN3 Analog Mic Resistor", "2.5 kOhm", "MIC3P Input Mux"}, + {"IN3 Analog Mic Resistor", "10 kOhm", "MIC3P Input Mux"}, + {"IN3 Analog Mic Resistor", "20 kOhm", "MIC3P Input Mux"}, + + {"IN3 Analog Mic Resistor", "2.5 kOhm", "MIC3M Input Mux"}, + {"IN3 Analog Mic Resistor", "10 kOhm", "MIC3M Input Mux"}, + {"IN3 Analog Mic Resistor", "20 kOhm", "MIC3M Input Mux"}, + + {"IN4 Analog Mic Resistor", "2.5 kOhm", "MIC4P Input Mux"}, + {"IN4 Analog Mic Resistor", "10 kOhm", "MIC4P Input Mux"}, + {"IN4 Analog Mic Resistor", "20 kOhm", "MIC4P Input Mux"}, + + {"IN4 Analog Mic Resistor", "2.5 kOhm", "MIC4M Input Mux"}, + {"IN4 Analog Mic Resistor", "10 kOhm", "MIC4M Input Mux"}, + {"IN4 Analog Mic Resistor", "20 kOhm", "MIC4M Input Mux"}, + + {"PDM Clk Div Select", "2.8224 MHz", "MIC1P Input Mux"}, + {"PDM Clk Div Select", "1.4112 MHz", "MIC1P Input Mux"}, + {"PDM Clk Div Select", "705.6 kHz", "MIC1P Input Mux"}, + {"PDM Clk Div Select", "5.6448 MHz", "MIC1P Input Mux"}, + + {"MIC1P Input Mux", NULL, "CH1_DIG"}, + {"MIC1M Input Mux", NULL, "CH2_DIG"}, + {"MIC2P Input Mux", NULL, "CH3_DIG"}, + {"MIC2M Input Mux", NULL, "CH4_DIG"}, + {"MIC3P Input Mux", NULL, "CH5_DIG"}, + {"MIC3M Input Mux", NULL, "CH6_DIG"}, + {"MIC4P Input Mux", NULL, "CH7_DIG"}, + {"MIC4M Input Mux", NULL, "CH8_DIG"}, + + {"MIC1 Analog Mux", "Line In", "MIC1P"}, + {"MIC2 Analog Mux", "Line In", "MIC2P"}, + {"MIC3 Analog Mux", "Line In", "MIC3P"}, + {"MIC4 Analog Mux", "Line In", "MIC4P"}, + + {"MIC1P Input Mux", "Analog", "MIC1P"}, + {"MIC1M Input Mux", "Analog", "MIC1M"}, + {"MIC2P Input Mux", "Analog", "MIC2P"}, + {"MIC2M Input Mux", "Analog", "MIC2M"}, + {"MIC3P Input Mux", "Analog", "MIC3P"}, + {"MIC3M Input Mux", "Analog", "MIC3M"}, + {"MIC4P Input Mux", "Analog", "MIC4P"}, + {"MIC4M Input Mux", "Analog", "MIC4M"}, + + {"MIC1P Input Mux", "Digital", "MIC1P"}, + {"MIC1M Input Mux", "Digital", "MIC1M"}, + {"MIC2P Input Mux", "Digital", "MIC2P"}, + {"MIC2M Input Mux", "Digital", "MIC2M"}, + {"MIC3P Input Mux", "Digital", "MIC3P"}, + {"MIC3M Input Mux", "Digital", "MIC3M"}, + {"MIC4P Input Mux", "Digital", "MIC4P"}, + {"MIC4M Input Mux", "Digital", "MIC4M"}, +}; + +#define ADCX140_PHASE_CALIB_SWITCH(xname) {\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .info = adcx140_phase_calib_info, \ + .get = adcx140_phase_calib_get, \ + .put = adcx140_phase_calib_put} + +static int adcx140_phase_calib_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int adcx140_phase_calib_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct snd_soc_component *codec = + snd_soc_kcontrol_component(kcontrol); + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(codec); + + value->value.integer.value[0] = adcx140->phase_calib_on ? 1 : 0; + + + return 0; +} + +static int adcx140_phase_calib_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(codec); + + bool v = value->value.integer.value[0] ? true : false; + + if (adcx140->phase_calib_on != v) { + adcx140->phase_calib_on = v; + return 1; + } + return 0; +} + +static const struct snd_kcontrol_new adcx140_snd_controls[] = { + SOC_SINGLE_TLV("Analog CH1 Mic Gain Volume", ADCX140_CH1_CFG1, 2, 42, 0, + adc_tlv), + SOC_SINGLE_TLV("Analog CH2 Mic Gain Volume", ADCX140_CH2_CFG1, 2, 42, 0, + adc_tlv), + SOC_SINGLE_TLV("Analog CH3 Mic Gain Volume", ADCX140_CH3_CFG1, 2, 42, 0, + adc_tlv), + SOC_SINGLE_TLV("Analog CH4 Mic Gain Volume", ADCX140_CH4_CFG1, 2, 42, 0, + adc_tlv), + + SOC_SINGLE_TLV("DRE Threshold", ADCX140_DRE_CFG0, 4, 9, 0, + dre_thresh_tlv), + SOC_SINGLE_TLV("DRE Max Gain", ADCX140_DRE_CFG0, 0, 12, 0, + dre_gain_tlv), + + SOC_SINGLE_TLV("AGC Threshold", ADCX140_AGC_CFG0, 4, 15, 0, + agc_thresh_tlv), + SOC_SINGLE_TLV("AGC Max Gain", ADCX140_AGC_CFG0, 0, 13, 0, + agc_gain_tlv), + + SOC_SINGLE_TLV("Digital CH1 Out Volume", ADCX140_CH1_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH2 Out Volume", ADCX140_CH2_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH3 Out Volume", ADCX140_CH3_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH4 Out Volume", ADCX140_CH4_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH5 Out Volume", ADCX140_CH5_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH6 Out Volume", ADCX140_CH6_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH7 Out Volume", ADCX140_CH7_CFG2, + 0, 0xff, 0, dig_vol_tlv), + SOC_SINGLE_TLV("Digital CH8 Out Volume", ADCX140_CH8_CFG2, + 0, 0xff, 0, dig_vol_tlv), + ADCX140_PHASE_CALIB_SWITCH("Phase Calibration Switch"), +}; + +static int adcx140_reset(struct adcx140_priv *adcx140) +{ + int ret = 0; + + if (adcx140->gpio_reset) { + gpiod_direction_output(adcx140->gpio_reset, 0); + /* 8.4.1: wait for hw shutdown (25ms) + >= 1ms */ + usleep_range(30000, 100000); + gpiod_direction_output(adcx140->gpio_reset, 1); + } else { + ret = regmap_write(adcx140->regmap, ADCX140_SW_RESET, + ADCX140_RESET); + } + + /* 8.4.2: wait >= 10 ms after entering sleep mode. */ + usleep_range(10000, 100000); + + return ret; +} + +static void adcx140_pwr_ctrl(struct adcx140_priv *adcx140, bool power_state) +{ + int pwr_ctrl = 0; + int ret = 0; + struct snd_soc_component *component = adcx140->component; + + if (power_state) + pwr_ctrl = ADCX140_PWR_CFG_ADC_PDZ | ADCX140_PWR_CFG_PLL_PDZ; + + if (adcx140->micbias_vg && power_state) + pwr_ctrl |= ADCX140_PWR_CFG_BIAS_PDZ; + + if (pwr_ctrl) { + ret = regmap_write(adcx140->regmap, ADCX140_PHASE_CALIB, + adcx140->phase_calib_on ? 0x00 : 0x40); + if (ret) + dev_err(component->dev, "%s: register write error %d\n", + __func__, ret); + } + + regmap_update_bits(adcx140->regmap, ADCX140_PWR_CFG, + ADCX140_PWR_CTRL_MSK, pwr_ctrl); +} + +static int adcx140_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + u8 data = 0; + + switch (params_width(params)) { + case 16: + data = ADCX140_16_BIT_WORD; + break; + case 20: + data = ADCX140_20_BIT_WORD; + break; + case 24: + data = ADCX140_24_BIT_WORD; + break; + case 32: + data = ADCX140_32_BIT_WORD; + break; + default: + dev_err(component->dev, "%s: Unsupported width %d\n", + __func__, params_width(params)); + return -EINVAL; + } + + adcx140_pwr_ctrl(adcx140, false); + + snd_soc_component_update_bits(component, ADCX140_ASI_CFG0, + ADCX140_WORD_LEN_MSK, data); + + adcx140_pwr_ctrl(adcx140, true); + + return 0; +} + +static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + u8 iface_reg1 = 0; + u8 iface_reg2 = 0; + int offset = 0; + bool inverted_bclk = false; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + iface_reg2 |= ADCX140_BCLK_FSYNC_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + dev_err(component->dev, "Invalid DAI clock provider\n"); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface_reg1 |= ADCX140_I2S_MODE_BIT; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg1 |= ADCX140_LEFT_JUST_BIT; + break; + case SND_SOC_DAIFMT_DSP_A: + offset = 1; + inverted_bclk = true; + break; + case SND_SOC_DAIFMT_DSP_B: + inverted_bclk = true; + break; + default: + dev_err(component->dev, "Invalid DAI interface format\n"); + return -EINVAL; + } + + /* signal polarity */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: + inverted_bclk = !inverted_bclk; + break; + case SND_SOC_DAIFMT_NB_IF: + iface_reg1 |= ADCX140_FSYNCINV_BIT; + break; + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_err(component->dev, "Invalid DAI clock signal polarity\n"); + return -EINVAL; + } + + if (inverted_bclk) + iface_reg1 |= ADCX140_BCLKINV_BIT; + + adcx140->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + adcx140_pwr_ctrl(adcx140, false); + + snd_soc_component_update_bits(component, ADCX140_ASI_CFG0, + ADCX140_FSYNCINV_BIT | + ADCX140_BCLKINV_BIT | + ADCX140_ASI_FORMAT_MSK, + iface_reg1); + snd_soc_component_update_bits(component, ADCX140_MST_CFG0, + ADCX140_BCLK_FSYNC_MASTER, iface_reg2); + + /* Configure data offset */ + snd_soc_component_update_bits(component, ADCX140_ASI_CFG1, + ADCX140_TX_OFFSET_MASK, offset); + + adcx140_pwr_ctrl(adcx140, true); + + return 0; +} + +static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = codec_dai->component; + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + + /* + * The chip itself supports arbitrary masks, but the driver currently + * only supports adjacent slots beginning at the first slot. + */ + if (tx_mask != GENMASK(__fls(tx_mask), 0)) { + dev_err(component->dev, "Only lower adjacent slots are supported\n"); + return -EINVAL; + } + + switch (slot_width) { + case 16: + case 20: + case 24: + case 32: + break; + default: + dev_err(component->dev, "Unsupported slot width %d\n", slot_width); + return -EINVAL; + } + + adcx140->slot_width = slot_width; + + return 0; +} + +static const struct snd_soc_dai_ops adcx140_dai_ops = { + .hw_params = adcx140_hw_params, + .set_fmt = adcx140_set_dai_fmt, + .set_tdm_slot = adcx140_set_dai_tdm_slot, +}; + +static int adcx140_configure_gpo(struct adcx140_priv *adcx140) +{ + u32 gpo_outputs[ADCX140_NUM_GPOS]; + u32 gpo_output_val = 0; + int ret; + int i; + + for (i = 0; i < ADCX140_NUM_GPOS; i++) { + ret = device_property_read_u32_array(adcx140->dev, + gpo_config_names[i], + gpo_outputs, + ADCX140_NUM_GPO_CFGS); + if (ret) + continue; + + if (gpo_outputs[0] > ADCX140_GPO_CFG_MAX) { + dev_err(adcx140->dev, "GPO%d config out of range\n", i + 1); + return -EINVAL; + } + + if (gpo_outputs[1] > ADCX140_GPO_DRV_MAX) { + dev_err(adcx140->dev, "GPO%d drive out of range\n", i + 1); + return -EINVAL; + } + + gpo_output_val = gpo_outputs[0] << ADCX140_GPO_SHIFT | + gpo_outputs[1]; + ret = regmap_write(adcx140->regmap, ADCX140_GPO_CFG0 + i, + gpo_output_val); + if (ret) + return ret; + } + + return 0; + +} + +static int adcx140_configure_gpio(struct adcx140_priv *adcx140) +{ + int gpio_count = 0; + u32 gpio_outputs[ADCX140_NUM_GPIO_CFGS]; + u32 gpio_output_val = 0; + int ret; + + gpio_count = device_property_count_u32(adcx140->dev, + "ti,gpio-config"); + if (gpio_count <= 0) + return 0; + + if (gpio_count != ADCX140_NUM_GPIO_CFGS) + return -EINVAL; + + ret = device_property_read_u32_array(adcx140->dev, "ti,gpio-config", + gpio_outputs, gpio_count); + if (ret) + return ret; + + if (gpio_outputs[0] > ADCX140_GPIO_CFG_MAX) { + dev_err(adcx140->dev, "GPIO config out of range\n"); + return -EINVAL; + } + + if (gpio_outputs[1] > ADCX140_GPIO_DRV_MAX) { + dev_err(adcx140->dev, "GPIO drive out of range\n"); + return -EINVAL; + } + + gpio_output_val = gpio_outputs[0] << ADCX140_GPIO_SHIFT + | gpio_outputs[1]; + + return regmap_write(adcx140->regmap, ADCX140_GPIO_CFG0, gpio_output_val); +} + +static int adcx140_codec_probe(struct snd_soc_component *component) +{ + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + int sleep_cfg_val = ADCX140_WAKE_DEV; + u32 bias_source; + u32 vref_source; + u8 bias_cfg; + int pdm_count; + u32 pdm_edges[ADCX140_NUM_PDM_EDGES]; + u32 pdm_edge_val = 0; + int gpi_count; + u32 gpi_inputs[ADCX140_NUM_GPI_PINS]; + u32 gpi_input_val = 0; + int i; + int ret; + bool tx_high_z; + + ret = device_property_read_u32(adcx140->dev, "ti,mic-bias-source", + &bias_source); + if (ret || bias_source > ADCX140_MIC_BIAS_VAL_AVDD) { + bias_source = ADCX140_MIC_BIAS_VAL_VREF; + adcx140->micbias_vg = false; + } else { + adcx140->micbias_vg = true; + } + + ret = device_property_read_u32(adcx140->dev, "ti,vref-source", + &vref_source); + if (ret) + vref_source = ADCX140_MIC_BIAS_VREF_275V; + + if (vref_source > ADCX140_MIC_BIAS_VREF_1375V) { + dev_err(adcx140->dev, "Mic Bias source value is invalid\n"); + return -EINVAL; + } + + bias_cfg = bias_source << ADCX140_MIC_BIAS_SHIFT | vref_source; + + ret = adcx140_reset(adcx140); + if (ret) + goto out; + + if (adcx140->supply_areg == NULL) + sleep_cfg_val |= ADCX140_AREG_INTERNAL; + + ret = regmap_write(adcx140->regmap, ADCX140_SLEEP_CFG, sleep_cfg_val); + if (ret) { + dev_err(adcx140->dev, "setting sleep config failed %d\n", ret); + goto out; + } + + /* 8.4.3: Wait >= 1ms after entering active mode. */ + usleep_range(1000, 100000); + + pdm_count = device_property_count_u32(adcx140->dev, + "ti,pdm-edge-select"); + if (pdm_count <= ADCX140_NUM_PDM_EDGES && pdm_count > 0) { + ret = device_property_read_u32_array(adcx140->dev, + "ti,pdm-edge-select", + pdm_edges, pdm_count); + if (ret) + return ret; + + for (i = 0; i < pdm_count; i++) + pdm_edge_val |= pdm_edges[i] << (ADCX140_PDM_EDGE_SHIFT - i); + + ret = regmap_write(adcx140->regmap, ADCX140_PDM_CFG, + pdm_edge_val); + if (ret) + return ret; + } + + gpi_count = device_property_count_u32(adcx140->dev, "ti,gpi-config"); + if (gpi_count <= ADCX140_NUM_GPI_PINS && gpi_count > 0) { + ret = device_property_read_u32_array(adcx140->dev, + "ti,gpi-config", + gpi_inputs, gpi_count); + if (ret) + return ret; + + gpi_input_val = gpi_inputs[ADCX140_GPI1_INDEX] << ADCX140_GPI_SHIFT | + gpi_inputs[ADCX140_GPI2_INDEX]; + + ret = regmap_write(adcx140->regmap, ADCX140_GPI_CFG0, + gpi_input_val); + if (ret) + return ret; + + gpi_input_val = gpi_inputs[ADCX140_GPI3_INDEX] << ADCX140_GPI_SHIFT | + gpi_inputs[ADCX140_GPI4_INDEX]; + + ret = regmap_write(adcx140->regmap, ADCX140_GPI_CFG1, + gpi_input_val); + if (ret) + return ret; + } + + ret = adcx140_configure_gpio(adcx140); + if (ret) + return ret; + + ret = adcx140_configure_gpo(adcx140); + if (ret) + goto out; + + ret = regmap_update_bits(adcx140->regmap, ADCX140_BIAS_CFG, + ADCX140_MIC_BIAS_VAL_MSK | + ADCX140_MIC_BIAS_VREF_MSK, bias_cfg); + if (ret) + dev_err(adcx140->dev, "setting MIC bias failed %d\n", ret); + + tx_high_z = device_property_read_bool(adcx140->dev, "ti,asi-tx-drive"); + if (tx_high_z) { + ret = regmap_update_bits(adcx140->regmap, ADCX140_ASI_CFG0, + ADCX140_TX_FILL, ADCX140_TX_FILL); + if (ret) { + dev_err(adcx140->dev, "Setting Tx drive failed %d\n", ret); + goto out; + } + } + + adcx140_pwr_ctrl(adcx140, true); +out: + return ret; +} + +static int adcx140_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + case SND_SOC_BIAS_STANDBY: + adcx140_pwr_ctrl(adcx140, true); + break; + case SND_SOC_BIAS_OFF: + adcx140_pwr_ctrl(adcx140, false); + break; + } + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_driver_adcx140 = { + .probe = adcx140_codec_probe, + .set_bias_level = adcx140_set_bias_level, + .controls = adcx140_snd_controls, + .num_controls = ARRAY_SIZE(adcx140_snd_controls), + .dapm_widgets = adcx140_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adcx140_dapm_widgets), + .dapm_routes = adcx140_audio_map, + .num_dapm_routes = ARRAY_SIZE(adcx140_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 0, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static struct snd_soc_dai_driver adcx140_dai_driver[] = { + { + .name = "tlv320adcx140-codec", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = ADCX140_MAX_CHANNELS, + .rates = ADCX140_RATES, + .formats = ADCX140_FORMATS, + }, + .ops = &adcx140_dai_ops, + .symmetric_rate = 1, + } +}; + +#ifdef CONFIG_OF +static const struct of_device_id tlv320adcx140_of_match[] = { + { .compatible = "ti,tlv320adc3140" }, + { .compatible = "ti,tlv320adc5140" }, + { .compatible = "ti,tlv320adc6140" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tlv320adcx140_of_match); +#endif + +static void adcx140_disable_regulator(void *arg) +{ + struct adcx140_priv *adcx140 = arg; + + regulator_disable(adcx140->supply_areg); +} + +static int adcx140_i2c_probe(struct i2c_client *i2c) +{ + struct adcx140_priv *adcx140; + int ret; + + adcx140 = devm_kzalloc(&i2c->dev, sizeof(*adcx140), GFP_KERNEL); + if (!adcx140) + return -ENOMEM; + + adcx140->phase_calib_on = false; + adcx140->dev = &i2c->dev; + + adcx140->gpio_reset = devm_gpiod_get_optional(adcx140->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(adcx140->gpio_reset)) + dev_info(&i2c->dev, "Reset GPIO not defined\n"); + + adcx140->supply_areg = devm_regulator_get_optional(adcx140->dev, + "areg"); + if (IS_ERR(adcx140->supply_areg)) { + if (PTR_ERR(adcx140->supply_areg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + adcx140->supply_areg = NULL; + } else { + ret = regulator_enable(adcx140->supply_areg); + if (ret) { + dev_err(adcx140->dev, "Failed to enable areg\n"); + return ret; + } + + ret = devm_add_action_or_reset(&i2c->dev, adcx140_disable_regulator, adcx140); + if (ret) + return ret; + } + + adcx140->regmap = devm_regmap_init_i2c(i2c, &adcx140_i2c_regmap); + if (IS_ERR(adcx140->regmap)) { + ret = PTR_ERR(adcx140->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + i2c_set_clientdata(i2c, adcx140); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_codec_driver_adcx140, + adcx140_dai_driver, 1); +} + +static const struct i2c_device_id adcx140_i2c_id[] = { + { "tlv320adc3140", 0 }, + { "tlv320adc5140", 1 }, + { "tlv320adc6140", 2 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adcx140_i2c_id); + +static struct i2c_driver adcx140_i2c_driver = { + .driver = { + .name = "tlv320adcx140-codec", + .of_match_table = of_match_ptr(tlv320adcx140_of_match), + }, + .probe = adcx140_i2c_probe, + .id_table = adcx140_i2c_id, +}; +module_i2c_driver(adcx140_i2c_driver); + +MODULE_AUTHOR("Dan Murphy "); +MODULE_DESCRIPTION("ASoC TLV320ADCX140 CODEC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tlv320adcx140.h b/sound/soc/codecs/tlv320adcx140.h new file mode 100644 index 0000000000..27a1f1012f --- /dev/null +++ b/sound/soc/codecs/tlv320adcx140.h @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +// TLV320ADCX140 Sound driver +// Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ + +#ifndef _TLV320ADCX140_H +#define _TLV320ADCX140_H + +#define ADCX140_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) + +#define ADCX140_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define ADCX140_PAGE_SELECT 0x00 +#define ADCX140_SW_RESET 0x01 +#define ADCX140_SLEEP_CFG 0x02 +#define ADCX140_SHDN_CFG 0x05 +#define ADCX140_ASI_CFG0 0x07 +#define ADCX140_ASI_CFG1 0x08 +#define ADCX140_ASI_CFG2 0x09 +#define ADCX140_ASI_CH1 0x0b +#define ADCX140_ASI_CH2 0x0c +#define ADCX140_ASI_CH3 0x0d +#define ADCX140_ASI_CH4 0x0e +#define ADCX140_ASI_CH5 0x0f +#define ADCX140_ASI_CH6 0x10 +#define ADCX140_ASI_CH7 0x11 +#define ADCX140_ASI_CH8 0x12 +#define ADCX140_MST_CFG0 0x13 +#define ADCX140_MST_CFG1 0x14 +#define ADCX140_ASI_STS 0x15 +#define ADCX140_CLK_SRC 0x16 +#define ADCX140_PDMCLK_CFG 0x1f +#define ADCX140_PDM_CFG 0x20 +#define ADCX140_GPIO_CFG0 0x21 +#define ADCX140_GPO_CFG0 0x22 +#define ADCX140_GPO_CFG1 0x23 +#define ADCX140_GPO_CFG2 0x24 +#define ADCX140_GPO_CFG3 0x25 +#define ADCX140_GPO_VAL 0x29 +#define ADCX140_GPIO_MON 0x2a +#define ADCX140_GPI_CFG0 0x2b +#define ADCX140_GPI_CFG1 0x2c +#define ADCX140_GPI_MON 0x2f +#define ADCX140_INT_CFG 0x32 +#define ADCX140_INT_MASK0 0x33 +#define ADCX140_INT_LTCH0 0x36 +#define ADCX140_BIAS_CFG 0x3b +#define ADCX140_CH1_CFG0 0x3c +#define ADCX140_CH1_CFG1 0x3d +#define ADCX140_CH1_CFG2 0x3e +#define ADCX140_CH1_CFG3 0x3f +#define ADCX140_CH1_CFG4 0x40 +#define ADCX140_CH2_CFG0 0x41 +#define ADCX140_CH2_CFG1 0x42 +#define ADCX140_CH2_CFG2 0x43 +#define ADCX140_CH2_CFG3 0x44 +#define ADCX140_CH2_CFG4 0x45 +#define ADCX140_CH3_CFG0 0x46 +#define ADCX140_CH3_CFG1 0x47 +#define ADCX140_CH3_CFG2 0x48 +#define ADCX140_CH3_CFG3 0x49 +#define ADCX140_CH3_CFG4 0x4a +#define ADCX140_CH4_CFG0 0x4b +#define ADCX140_CH4_CFG1 0x4c +#define ADCX140_CH4_CFG2 0x4d +#define ADCX140_CH4_CFG3 0x4e +#define ADCX140_CH4_CFG4 0x4f +#define ADCX140_CH5_CFG2 0x52 +#define ADCX140_CH5_CFG3 0x53 +#define ADCX140_CH5_CFG4 0x54 +#define ADCX140_CH6_CFG2 0x57 +#define ADCX140_CH6_CFG3 0x58 +#define ADCX140_CH6_CFG4 0x59 +#define ADCX140_CH7_CFG2 0x5c +#define ADCX140_CH7_CFG3 0x5d +#define ADCX140_CH7_CFG4 0x5e +#define ADCX140_CH8_CFG2 0x61 +#define ADCX140_CH8_CFG3 0x62 +#define ADCX140_CH8_CFG4 0x63 +#define ADCX140_DSP_CFG0 0x6b +#define ADCX140_DSP_CFG1 0x6c +#define ADCX140_DRE_CFG0 0x6d +#define ADCX140_AGC_CFG0 0x70 +#define ADCX140_IN_CH_EN 0x73 +#define ADCX140_ASI_OUT_CH_EN 0x74 +#define ADCX140_PWR_CFG 0x75 +#define ADCX140_DEV_STS0 0x76 +#define ADCX140_DEV_STS1 0x77 +#define ADCX140_PHASE_CALIB 0X7b + +#define ADCX140_RESET BIT(0) + +#define ADCX140_WAKE_DEV BIT(0) +#define ADCX140_AREG_INTERNAL BIT(7) + +#define ADCX140_BCLKINV_BIT BIT(2) +#define ADCX140_FSYNCINV_BIT BIT(3) +#define ADCX140_INV_MSK (ADCX140_BCLKINV_BIT | ADCX140_FSYNCINV_BIT) +#define ADCX140_BCLK_FSYNC_MASTER BIT(7) +#define ADCX140_I2S_MODE_BIT BIT(6) +#define ADCX140_LEFT_JUST_BIT BIT(7) +#define ADCX140_ASI_FORMAT_MSK (ADCX140_I2S_MODE_BIT | ADCX140_LEFT_JUST_BIT) + +#define ADCX140_16_BIT_WORD 0x0 +#define ADCX140_20_BIT_WORD BIT(4) +#define ADCX140_24_BIT_WORD BIT(5) +#define ADCX140_32_BIT_WORD (BIT(4) | BIT(5)) +#define ADCX140_WORD_LEN_MSK 0x30 + +#define ADCX140_MAX_CHANNELS 8 + +#define ADCX140_MIC_BIAS_VAL_VREF 0 +#define ADCX140_MIC_BIAS_VAL_VREF_1096 1 +#define ADCX140_MIC_BIAS_VAL_AVDD 6 +#define ADCX140_MIC_BIAS_VAL_MSK GENMASK(6, 4) +#define ADCX140_MIC_BIAS_SHIFT 4 + +#define ADCX140_MIC_BIAS_VREF_275V 0 +#define ADCX140_MIC_BIAS_VREF_25V 1 +#define ADCX140_MIC_BIAS_VREF_1375V 2 +#define ADCX140_MIC_BIAS_VREF_MSK GENMASK(1, 0) + +#define ADCX140_PWR_CTRL_MSK GENMASK(7, 5) +#define ADCX140_PWR_CFG_BIAS_PDZ BIT(7) +#define ADCX140_PWR_CFG_ADC_PDZ BIT(6) +#define ADCX140_PWR_CFG_PLL_PDZ BIT(5) + +#define ADCX140_TX_OFFSET_MASK GENMASK(4, 0) + +#define ADCX140_NUM_PDM_EDGES 4 +#define ADCX140_PDM_EDGE_SHIFT 7 + +#define ADCX140_NUM_GPI_PINS 4 +#define ADCX140_GPI_SHIFT 4 +#define ADCX140_GPI1_INDEX 0 +#define ADCX140_GPI2_INDEX 1 +#define ADCX140_GPI3_INDEX 2 +#define ADCX140_GPI4_INDEX 3 + +#define ADCX140_NUM_GPOS 4 +#define ADCX140_NUM_GPO_CFGS 2 +#define ADCX140_GPO_SHIFT 4 +#define ADCX140_GPO_CFG_MAX 4 +#define ADCX140_GPO_DRV_MAX 5 + +#define ADCX140_TX_FILL BIT(0) + +#define ADCX140_NUM_GPIO_CFGS 2 +#define ADCX140_GPIO_SHIFT 4 +#define ADCX140_GPIO_CFG_MAX 15 +#define ADCX140_GPIO_DRV_MAX 5 + +#endif /* _TLV320ADCX140_ */ diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c new file mode 100644 index 0000000000..9692ae007c --- /dev/null +++ b/sound/soc/codecs/tlv320aic23-i2c.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC TLV320AIC23 codec driver I2C interface + * + * Author: Arun KS, + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., + * + * Based on sound/soc/codecs/wm8731.c by Richard Purdie + */ + +#include +#include +#include +#include +#include + +#include "tlv320aic23.h" + +static int tlv320aic23_i2c_probe(struct i2c_client *i2c) +{ + struct regmap *regmap; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EINVAL; + + regmap = devm_regmap_init_i2c(i2c, &tlv320aic23_regmap); + return tlv320aic23_probe(&i2c->dev, regmap); +} + +static const struct i2c_device_id tlv320aic23_id[] = { + {"tlv320aic23", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tlv320aic23_id); + +#ifdef CONFIG_OF +static const struct of_device_id tlv320aic23_of_match[] = { + { .compatible = "ti,tlv320aic23", }, + { } +}; +MODULE_DEVICE_TABLE(of, tlv320aic23_of_match); +#endif + +static struct i2c_driver tlv320aic23_i2c_driver = { + .driver = { + .name = "tlv320aic23-codec", + .of_match_table = of_match_ptr(tlv320aic23_of_match), + }, + .probe = tlv320aic23_i2c_probe, + .id_table = tlv320aic23_id, +}; + +module_i2c_driver(tlv320aic23_i2c_driver); + +MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver I2C"); +MODULE_AUTHOR("Arun KS "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic23-spi.c b/sound/soc/codecs/tlv320aic23-spi.c new file mode 100644 index 0000000000..10765ae766 --- /dev/null +++ b/sound/soc/codecs/tlv320aic23-spi.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC TLV320AIC23 codec driver SPI interface + * + * Author: Arun KS, + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., + * + * Based on sound/soc/codecs/wm8731.c by Richard Purdie + */ + +#include +#include +#include +#include + +#include "tlv320aic23.h" + +static int aic23_spi_probe(struct spi_device *spi) +{ + int ret; + struct regmap *regmap; + + dev_dbg(&spi->dev, "probing tlv320aic23 spi device\n"); + + spi->mode = SPI_MODE_0; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + regmap = devm_regmap_init_spi(spi, &tlv320aic23_regmap); + return tlv320aic23_probe(&spi->dev, regmap); +} + +static struct spi_driver aic23_spi = { + .driver = { + .name = "tlv320aic23", + }, + .probe = aic23_spi_probe, +}; + +module_spi_driver(aic23_spi); + +MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver SPI"); +MODULE_AUTHOR("Arun KS "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c new file mode 100644 index 0000000000..c47aa4d416 --- /dev/null +++ b/sound/soc/codecs/tlv320aic23.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC TLV320AIC23 codec driver + * + * Author: Arun KS, + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., + * + * Based on sound/soc/codecs/wm8731.c by Richard Purdie + * + * Notes: + * The AIC23 is a driver for a low power stereo audio + * codec tlv320aic23 + * + * The machine layer should disable unsupported inputs/outputs by + * snd_soc_dapm_disable_pin(codec, "LHPOUT"), etc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlv320aic23.h" + +/* + * AIC23 register cache + */ +static const struct reg_default tlv320aic23_reg[] = { + { 0, 0x0097 }, + { 1, 0x0097 }, + { 2, 0x00F9 }, + { 3, 0x00F9 }, + { 4, 0x001A }, + { 5, 0x0004 }, + { 6, 0x0007 }, + { 7, 0x0001 }, + { 8, 0x0020 }, + { 9, 0x0000 }, +}; + +const struct regmap_config tlv320aic23_regmap = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = TLV320AIC23_RESET, + .reg_defaults = tlv320aic23_reg, + .num_reg_defaults = ARRAY_SIZE(tlv320aic23_reg), + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL(tlv320aic23_regmap); + +static const char *rec_src_text[] = { "Line", "Mic" }; +static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"}; + +static SOC_ENUM_SINGLE_DECL(rec_src_enum, + TLV320AIC23_ANLG, 2, rec_src_text); + +static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls = +SOC_DAPM_ENUM("Input Select", rec_src_enum); + +static SOC_ENUM_SINGLE_DECL(tlv320aic23_deemph, + TLV320AIC23_DIGT, 1, deemph_text); + +static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0); +static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0); + +static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + u16 val, reg; + + val = (ucontrol->value.integer.value[0] & 0x07); + + /* linear conversion to userspace + * 000 = -6db + * 001 = -9db + * 010 = -12db + * 011 = -18db (Min) + * 100 = 0db (Max) + */ + val = (val >= 4) ? 4 : (3 - val); + + reg = snd_soc_component_read(component, TLV320AIC23_ANLG) & (~0x1C0); + snd_soc_component_write(component, TLV320AIC23_ANLG, reg | (val << 6)); + + return 0; +} + +static int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + u16 val; + + val = snd_soc_component_read(component, TLV320AIC23_ANLG) & (0x1C0); + val = val >> 6; + val = (val >= 4) ? 4 : (3 - val); + ucontrol->value.integer.value[0] = val; + return 0; + +} + +static const struct snd_kcontrol_new tlv320aic23_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Playback Volume", TLV320AIC23_LCHNVOL, + TLV320AIC23_RCHNVOL, 0, 127, 0, out_gain_tlv), + SOC_SINGLE("Digital Playback Switch", TLV320AIC23_DIGT, 3, 1, 1), + SOC_DOUBLE_R("Line Input Switch", TLV320AIC23_LINVOL, + TLV320AIC23_RINVOL, 7, 1, 0), + SOC_DOUBLE_R_TLV("Line Input Volume", TLV320AIC23_LINVOL, + TLV320AIC23_RINVOL, 0, 31, 0, input_gain_tlv), + SOC_SINGLE("Mic Input Switch", TLV320AIC23_ANLG, 1, 1, 1), + SOC_SINGLE("Mic Booster Switch", TLV320AIC23_ANLG, 0, 1, 0), + SOC_SINGLE_EXT_TLV("Sidetone Volume", TLV320AIC23_ANLG, 6, 4, 0, + snd_soc_tlv320aic23_get_volsw, + snd_soc_tlv320aic23_put_volsw, sidetone_vol_tlv), + SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph), +}; + +/* PGA Mixer controls for Line and Mic switch */ +static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0), + SOC_DAPM_SINGLE("Mic Sidetone Switch", TLV320AIC23_ANLG, 5, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", TLV320AIC23_ANLG, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", TLV320AIC23_PWR, 3, 1), + SND_SOC_DAPM_ADC("ADC", "Capture", TLV320AIC23_PWR, 2, 1), + SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0, + &tlv320aic23_rec_src_mux_controls), + SND_SOC_DAPM_MIXER("Output Mixer", TLV320AIC23_PWR, 4, 1, + &tlv320aic23_output_mixer_controls[0], + ARRAY_SIZE(tlv320aic23_output_mixer_controls)), + SND_SOC_DAPM_PGA("Line Input", TLV320AIC23_PWR, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("Mic Input", TLV320AIC23_PWR, 1, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("LHPOUT"), + SND_SOC_DAPM_OUTPUT("RHPOUT"), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), + + SND_SOC_DAPM_INPUT("LLINEIN"), + SND_SOC_DAPM_INPUT("RLINEIN"), + + SND_SOC_DAPM_INPUT("MICIN"), +}; + +static const struct snd_soc_dapm_route tlv320aic23_intercon[] = { + /* Output Mixer */ + {"Output Mixer", "Line Bypass Switch", "Line Input"}, + {"Output Mixer", "Playback Switch", "DAC"}, + {"Output Mixer", "Mic Sidetone Switch", "Mic Input"}, + + /* Outputs */ + {"RHPOUT", NULL, "Output Mixer"}, + {"LHPOUT", NULL, "Output Mixer"}, + {"LOUT", NULL, "Output Mixer"}, + {"ROUT", NULL, "Output Mixer"}, + + /* Inputs */ + {"Line Input", NULL, "LLINEIN"}, + {"Line Input", NULL, "RLINEIN"}, + {"Mic Input", NULL, "MICIN"}, + + /* input mux */ + {"Capture Source", "Line", "Line Input"}, + {"Capture Source", "Mic", "Mic Input"}, + {"ADC", NULL, "Capture Source"}, + +}; + +/* AIC23 driver data */ +struct aic23 { + struct regmap *regmap; + int mclk; + int requested_adc; + int requested_dac; +}; + +/* + * Common Crystals used + * 11.2896 Mhz /128 = *88.2k /192 = 58.8k + * 12.0000 Mhz /125 = *96k /136 = 88.235K + * 12.2880 Mhz /128 = *96k /192 = 64k + * 16.9344 Mhz /128 = 132.3k /192 = *88.2k + * 18.4320 Mhz /128 = 144k /192 = *96k + */ + +/* + * Normal BOSR 0-256/2 = 128, 1-384/2 = 192 + * USB BOSR 0-250/2 = 125, 1-272/2 = 136 + */ +static const int bosr_usb_divisor_table[] = { + 128, 125, 192, 136 +}; +#define LOWER_GROUP ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<6) | (1<<7)) +#define UPPER_GROUP ((1<<8) | (1<<9) | (1<<10) | (1<<11) | (1<<15)) +static const unsigned short sr_valid_mask[] = { + LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 0*/ + LOWER_GROUP, /* Usb, bosr - 0*/ + LOWER_GROUP|UPPER_GROUP, /* Normal, bosr - 1*/ + UPPER_GROUP, /* Usb, bosr - 1*/ +}; +/* + * Every divisor is a factor of 11*12 + */ +#define SR_MULT (11*12) +#define A(x) (SR_MULT/x) +static const unsigned char sr_adc_mult_table[] = { + A(2), A(2), A(12), A(12), 0, 0, A(3), A(1), + A(2), A(2), A(11), A(11), 0, 0, 0, A(1) +}; +static const unsigned char sr_dac_mult_table[] = { + A(2), A(12), A(2), A(12), 0, 0, A(3), A(1), + A(2), A(11), A(2), A(11), 0, 0, 0, A(1) +}; + +static unsigned get_score(int adc, int adc_l, int adc_h, int need_adc, + int dac, int dac_l, int dac_h, int need_dac) +{ + if ((adc >= adc_l) && (adc <= adc_h) && + (dac >= dac_l) && (dac <= dac_h)) { + int diff_adc = need_adc - adc; + int diff_dac = need_dac - dac; + return abs(diff_adc) + abs(diff_dac); + } + return UINT_MAX; +} + +static int find_rate(int mclk, u32 need_adc, u32 need_dac) +{ + int i, j; + int best_i = -1; + int best_j = -1; + int best_div = 0; + unsigned best_score = UINT_MAX; + int adc_l, adc_h, dac_l, dac_h; + + need_adc *= SR_MULT; + need_dac *= SR_MULT; + /* + * rates given are +/- 1/32 + */ + adc_l = need_adc - (need_adc >> 5); + adc_h = need_adc + (need_adc >> 5); + dac_l = need_dac - (need_dac >> 5); + dac_h = need_dac + (need_dac >> 5); + for (i = 0; i < ARRAY_SIZE(bosr_usb_divisor_table); i++) { + int base = mclk / bosr_usb_divisor_table[i]; + int mask = sr_valid_mask[i]; + for (j = 0; j < ARRAY_SIZE(sr_adc_mult_table); + j++, mask >>= 1) { + int adc; + int dac; + int score; + if ((mask & 1) == 0) + continue; + adc = base * sr_adc_mult_table[j]; + dac = base * sr_dac_mult_table[j]; + score = get_score(adc, adc_l, adc_h, need_adc, + dac, dac_l, dac_h, need_dac); + if (best_score > score) { + best_score = score; + best_i = i; + best_j = j; + best_div = 0; + } + score = get_score((adc >> 1), adc_l, adc_h, need_adc, + (dac >> 1), dac_l, dac_h, need_dac); + /* prefer to have a /2 */ + if ((score != UINT_MAX) && (best_score >= score)) { + best_score = score; + best_i = i; + best_j = j; + best_div = 1; + } + } + } + return (best_j << 2) | best_i | (best_div << TLV320AIC23_CLKIN_SHIFT); +} + +#ifdef DEBUG +static void get_current_sample_rates(struct snd_soc_component *component, int mclk, + u32 *sample_rate_adc, u32 *sample_rate_dac) +{ + int src = snd_soc_component_read(component, TLV320AIC23_SRATE); + int sr = (src >> 2) & 0x0f; + int val = (mclk / bosr_usb_divisor_table[src & 3]); + int adc = (val * sr_adc_mult_table[sr]) / SR_MULT; + int dac = (val * sr_dac_mult_table[sr]) / SR_MULT; + if (src & TLV320AIC23_CLKIN_HALF) { + adc >>= 1; + dac >>= 1; + } + *sample_rate_adc = adc; + *sample_rate_dac = dac; +} +#endif + +static int set_sample_rate_control(struct snd_soc_component *component, int mclk, + u32 sample_rate_adc, u32 sample_rate_dac) +{ + /* Search for the right sample rate */ + int data = find_rate(mclk, sample_rate_adc, sample_rate_dac); + if (data < 0) { + printk(KERN_ERR "%s:Invalid rate %u,%u requested\n", + __func__, sample_rate_adc, sample_rate_dac); + return -EINVAL; + } + snd_soc_component_write(component, TLV320AIC23_SRATE, data); +#ifdef DEBUG + { + u32 adc, dac; + get_current_sample_rates(component, mclk, &adc, &dac); + printk(KERN_DEBUG "actual samplerate = %u,%u reg=%x\n", + adc, dac, data); + } +#endif + return 0; +} + +static int tlv320aic23_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 iface_reg; + int ret; + struct aic23 *aic23 = snd_soc_component_get_drvdata(component); + u32 sample_rate_adc = aic23->requested_adc; + u32 sample_rate_dac = aic23->requested_dac; + u32 sample_rate = params_rate(params); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aic23->requested_dac = sample_rate_dac = sample_rate; + if (!sample_rate_adc) + sample_rate_adc = sample_rate; + } else { + aic23->requested_adc = sample_rate_adc = sample_rate; + if (!sample_rate_dac) + sample_rate_dac = sample_rate; + } + ret = set_sample_rate_control(component, aic23->mclk, sample_rate_adc, + sample_rate_dac); + if (ret < 0) + return ret; + + iface_reg = snd_soc_component_read(component, TLV320AIC23_DIGT_FMT) & ~(0x03 << 2); + + switch (params_width(params)) { + case 16: + break; + case 20: + iface_reg |= (0x01 << 2); + break; + case 24: + iface_reg |= (0x02 << 2); + break; + case 32: + iface_reg |= (0x03 << 2); + break; + } + snd_soc_component_write(component, TLV320AIC23_DIGT_FMT, iface_reg); + + return 0; +} + +static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + /* set active */ + snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x0001); + + return 0; +} + +static void tlv320aic23_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aic23 *aic23 = snd_soc_component_get_drvdata(component); + + /* deactivate */ + if (!snd_soc_component_active(component)) { + udelay(50); + snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x0); + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + aic23->requested_dac = 0; + else + aic23->requested_adc = 0; +} + +static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 reg; + + reg = snd_soc_component_read(component, TLV320AIC23_DIGT); + if (mute) + reg |= TLV320AIC23_DACM_MUTE; + + else + reg &= ~TLV320AIC23_DACM_MUTE; + + snd_soc_component_write(component, TLV320AIC23_DIGT, reg); + + return 0; +} + +static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface_reg; + + iface_reg = snd_soc_component_read(component, TLV320AIC23_DIGT_FMT) & (~0x03); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + iface_reg |= TLV320AIC23_MS_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + iface_reg &= ~TLV320AIC23_MS_MASTER; + break; + default: + return -EINVAL; + + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface_reg |= TLV320AIC23_FOR_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg |= TLV320AIC23_LRP_ON; + fallthrough; + case SND_SOC_DAIFMT_DSP_B: + iface_reg |= TLV320AIC23_FOR_DSP; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg |= TLV320AIC23_FOR_LJUST; + break; + default: + return -EINVAL; + + } + + snd_soc_component_write(component, TLV320AIC23_DIGT_FMT, iface_reg); + + return 0; +} + +static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct aic23 *aic23 = snd_soc_dai_get_drvdata(codec_dai); + aic23->mclk = freq; + return 0; +} + +static int tlv320aic23_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + u16 reg = snd_soc_component_read(component, TLV320AIC23_PWR) & 0x17f; + + switch (level) { + case SND_SOC_BIAS_ON: + /* vref/mid, osc on, dac unmute */ + reg &= ~(TLV320AIC23_DEVICE_PWR_OFF | TLV320AIC23_OSC_OFF | \ + TLV320AIC23_DAC_OFF); + snd_soc_component_write(component, TLV320AIC23_PWR, reg); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* everything off except vref/vmid, */ + snd_soc_component_write(component, TLV320AIC23_PWR, + reg | TLV320AIC23_CLK_OFF); + break; + case SND_SOC_BIAS_OFF: + /* everything off, dac mute, inactive */ + snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x0); + snd_soc_component_write(component, TLV320AIC23_PWR, 0x1ff); + break; + } + return 0; +} + +#define AIC23_RATES SNDRV_PCM_RATE_8000_96000 +#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops tlv320aic23_dai_ops = { + .prepare = tlv320aic23_pcm_prepare, + .hw_params = tlv320aic23_hw_params, + .shutdown = tlv320aic23_shutdown, + .mute_stream = tlv320aic23_mute, + .set_fmt = tlv320aic23_set_dai_fmt, + .set_sysclk = tlv320aic23_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver tlv320aic23_dai = { + .name = "tlv320aic23-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AIC23_RATES, + .formats = AIC23_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AIC23_RATES, + .formats = AIC23_FORMATS,}, + .ops = &tlv320aic23_dai_ops, +}; + +static int tlv320aic23_resume(struct snd_soc_component *component) +{ + struct aic23 *aic23 = snd_soc_component_get_drvdata(component); + regcache_mark_dirty(aic23->regmap); + regcache_sync(aic23->regmap); + + return 0; +} + +static int tlv320aic23_component_probe(struct snd_soc_component *component) +{ + /* Reset codec */ + snd_soc_component_write(component, TLV320AIC23_RESET, 0); + + snd_soc_component_write(component, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K); + + /* Unmute input */ + snd_soc_component_update_bits(component, TLV320AIC23_LINVOL, + TLV320AIC23_LIM_MUTED, TLV320AIC23_LRS_ENABLED); + + snd_soc_component_update_bits(component, TLV320AIC23_RINVOL, + TLV320AIC23_LIM_MUTED, TLV320AIC23_LRS_ENABLED); + + snd_soc_component_update_bits(component, TLV320AIC23_ANLG, + TLV320AIC23_BYPASS_ON | TLV320AIC23_MICM_MUTED, + 0); + + /* Default output volume */ + snd_soc_component_write(component, TLV320AIC23_LCHNVOL, + TLV320AIC23_DEFAULT_OUT_VOL & TLV320AIC23_OUT_VOL_MASK); + snd_soc_component_write(component, TLV320AIC23_RCHNVOL, + TLV320AIC23_DEFAULT_OUT_VOL & TLV320AIC23_OUT_VOL_MASK); + + snd_soc_component_write(component, TLV320AIC23_ACTIVE, 0x1); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_tlv320aic23 = { + .probe = tlv320aic23_component_probe, + .resume = tlv320aic23_resume, + .set_bias_level = tlv320aic23_set_bias_level, + .controls = tlv320aic23_snd_controls, + .num_controls = ARRAY_SIZE(tlv320aic23_snd_controls), + .dapm_widgets = tlv320aic23_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), + .dapm_routes = tlv320aic23_intercon, + .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +int tlv320aic23_probe(struct device *dev, struct regmap *regmap) +{ + struct aic23 *aic23; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + aic23 = devm_kzalloc(dev, sizeof(struct aic23), GFP_KERNEL); + if (aic23 == NULL) + return -ENOMEM; + + aic23->regmap = regmap; + + dev_set_drvdata(dev, aic23); + + return devm_snd_soc_register_component(dev, + &soc_component_dev_tlv320aic23, + &tlv320aic23_dai, 1); +} +EXPORT_SYMBOL(tlv320aic23_probe); + +MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver"); +MODULE_AUTHOR("Arun KS "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic23.h b/sound/soc/codecs/tlv320aic23.h new file mode 100644 index 0000000000..0226be4011 --- /dev/null +++ b/sound/soc/codecs/tlv320aic23.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC TLV320AIC23 codec driver + * + * Author: Arun KS, + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd + */ + +#ifndef _TLV320AIC23_H +#define _TLV320AIC23_H + +struct device; +struct regmap_config; + +extern const struct regmap_config tlv320aic23_regmap; +int tlv320aic23_probe(struct device *dev, struct regmap *regmap); + +/* Codec TLV320AIC23 */ +#define TLV320AIC23_LINVOL 0x00 +#define TLV320AIC23_RINVOL 0x01 +#define TLV320AIC23_LCHNVOL 0x02 +#define TLV320AIC23_RCHNVOL 0x03 +#define TLV320AIC23_ANLG 0x04 +#define TLV320AIC23_DIGT 0x05 +#define TLV320AIC23_PWR 0x06 +#define TLV320AIC23_DIGT_FMT 0x07 +#define TLV320AIC23_SRATE 0x08 +#define TLV320AIC23_ACTIVE 0x09 +#define TLV320AIC23_RESET 0x0F + +/* Left (right) line input volume control register */ +#define TLV320AIC23_LRS_ENABLED 0x0100 +#define TLV320AIC23_LIM_MUTED 0x0080 +#define TLV320AIC23_LIV_DEFAULT 0x0017 +#define TLV320AIC23_LIV_MAX 0x001f +#define TLV320AIC23_LIV_MIN 0x0000 + +/* Left (right) channel headphone volume control register */ +#define TLV320AIC23_LZC_ON 0x0080 +#define TLV320AIC23_LHV_DEFAULT 0x0079 +#define TLV320AIC23_LHV_MAX 0x007f +#define TLV320AIC23_LHV_MIN 0x0000 + +/* Analog audio path control register */ +#define TLV320AIC23_STA_REG(x) ((x)<<6) +#define TLV320AIC23_STE_ENABLED 0x0020 +#define TLV320AIC23_DAC_SELECTED 0x0010 +#define TLV320AIC23_BYPASS_ON 0x0008 +#define TLV320AIC23_INSEL_MIC 0x0004 +#define TLV320AIC23_MICM_MUTED 0x0002 +#define TLV320AIC23_MICB_20DB 0x0001 + +/* Digital audio path control register */ +#define TLV320AIC23_DACM_MUTE 0x0008 +#define TLV320AIC23_DEEMP_32K 0x0002 +#define TLV320AIC23_DEEMP_44K 0x0004 +#define TLV320AIC23_DEEMP_48K 0x0006 +#define TLV320AIC23_ADCHP_ON 0x0001 + +/* Power control down register */ +#define TLV320AIC23_DEVICE_PWR_OFF 0x0080 +#define TLV320AIC23_CLK_OFF 0x0040 +#define TLV320AIC23_OSC_OFF 0x0020 +#define TLV320AIC23_OUT_OFF 0x0010 +#define TLV320AIC23_DAC_OFF 0x0008 +#define TLV320AIC23_ADC_OFF 0x0004 +#define TLV320AIC23_MIC_OFF 0x0002 +#define TLV320AIC23_LINE_OFF 0x0001 + +/* Digital audio interface register */ +#define TLV320AIC23_MS_MASTER 0x0040 +#define TLV320AIC23_LRSWAP_ON 0x0020 +#define TLV320AIC23_LRP_ON 0x0010 +#define TLV320AIC23_IWL_16 0x0000 +#define TLV320AIC23_IWL_20 0x0004 +#define TLV320AIC23_IWL_24 0x0008 +#define TLV320AIC23_IWL_32 0x000C +#define TLV320AIC23_FOR_I2S 0x0002 +#define TLV320AIC23_FOR_DSP 0x0003 +#define TLV320AIC23_FOR_LJUST 0x0001 + +/* Sample rate control register */ +#define TLV320AIC23_CLKOUT_HALF 0x0080 +#define TLV320AIC23_CLKIN_HALF 0x0040 +#define TLV320AIC23_BOSR_384fs 0x0002 /* BOSR_272fs in USB mode */ +#define TLV320AIC23_USB_CLK_ON 0x0001 +#define TLV320AIC23_SR_MASK 0xf +#define TLV320AIC23_CLKOUT_SHIFT 7 +#define TLV320AIC23_CLKIN_SHIFT 6 +#define TLV320AIC23_SR_SHIFT 2 +#define TLV320AIC23_BOSR_SHIFT 1 + +/* Digital interface register */ +#define TLV320AIC23_ACT_ON 0x0001 + +/* + * AUDIO related MACROS + */ + +#define TLV320AIC23_DEFAULT_OUT_VOL 0x70 +#define TLV320AIC23_DEFAULT_IN_VOLUME 0x10 + +#define TLV320AIC23_OUT_VOL_MIN TLV320AIC23_LHV_MIN +#define TLV320AIC23_OUT_VOL_MAX TLV320AIC23_LHV_MAX +#define TLV320AIC23_OUT_VO_RANGE (TLV320AIC23_OUT_VOL_MAX - \ + TLV320AIC23_OUT_VOL_MIN) +#define TLV320AIC23_OUT_VOL_MASK TLV320AIC23_OUT_VOL_MAX + +#define TLV320AIC23_IN_VOL_MIN TLV320AIC23_LIV_MIN +#define TLV320AIC23_IN_VOL_MAX TLV320AIC23_LIV_MAX +#define TLV320AIC23_IN_VOL_RANGE (TLV320AIC23_IN_VOL_MAX - \ + TLV320AIC23_IN_VOL_MIN) +#define TLV320AIC23_IN_VOL_MASK TLV320AIC23_IN_VOL_MAX + +#define TLV320AIC23_SIDETONE_MASK 0x1c0 +#define TLV320AIC23_SIDETONE_0 0x100 +#define TLV320AIC23_SIDETONE_6 0x000 +#define TLV320AIC23_SIDETONE_9 0x040 +#define TLV320AIC23_SIDETONE_12 0x080 +#define TLV320AIC23_SIDETONE_18 0x0c0 + +#endif /* _TLV320AIC23_H */ diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c new file mode 100644 index 0000000000..e5dfb3d752 --- /dev/null +++ b/sound/soc/codecs/tlv320aic26.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Texas Instruments TLV320AIC26 low power audio CODEC + * ALSA SoC CODEC driver + * + * Copyright (C) 2008 Secret Lab Technologies Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlv320aic26.h" + +MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver"); +MODULE_AUTHOR("Grant Likely "); +MODULE_LICENSE("GPL"); + +/* AIC26 driver private data */ +struct aic26 { + struct spi_device *spi; + struct regmap *regmap; + struct snd_soc_component *component; + int clock_provider; + int datfm; + int mclk; + + /* Keyclick parameters */ + int keyclick_amplitude; + int keyclick_freq; + int keyclick_len; +}; + +static const struct snd_soc_dapm_widget tlv320aic26_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("MICIN"), +SND_SOC_DAPM_INPUT("AUX"), + +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +}; + +static const struct snd_soc_dapm_route tlv320aic26_dapm_routes[] = { + { "Capture", NULL, "MICIN" }, + { "Capture", NULL, "AUX" }, + + { "HPL", NULL, "Playback" }, + { "HPR", NULL, "Playback" }, +}; + +/* --------------------------------------------------------------------- + * Digital Audio Interface Operations + */ +static int aic26_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aic26 *aic26 = snd_soc_component_get_drvdata(component); + int fsref, divisor, wlen, pval, jval, dval, qval; + u16 reg; + + dev_dbg(&aic26->spi->dev, "aic26_hw_params(substream=%p, params=%p)\n", + substream, params); + dev_dbg(&aic26->spi->dev, "rate=%i width=%d\n", params_rate(params), + params_width(params)); + + switch (params_rate(params)) { + case 8000: fsref = 48000; divisor = AIC26_DIV_6; break; + case 11025: fsref = 44100; divisor = AIC26_DIV_4; break; + case 12000: fsref = 48000; divisor = AIC26_DIV_4; break; + case 16000: fsref = 48000; divisor = AIC26_DIV_3; break; + case 22050: fsref = 44100; divisor = AIC26_DIV_2; break; + case 24000: fsref = 48000; divisor = AIC26_DIV_2; break; + case 32000: fsref = 48000; divisor = AIC26_DIV_1_5; break; + case 44100: fsref = 44100; divisor = AIC26_DIV_1; break; + case 48000: fsref = 48000; divisor = AIC26_DIV_1; break; + default: + dev_dbg(&aic26->spi->dev, "bad rate\n"); return -EINVAL; + } + + /* select data word length */ + switch (params_width(params)) { + case 8: wlen = AIC26_WLEN_16; break; + case 16: wlen = AIC26_WLEN_16; break; + case 24: wlen = AIC26_WLEN_24; break; + case 32: wlen = AIC26_WLEN_32; break; + default: + dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL; + } + + /** + * Configure PLL + * fsref = (mclk * PLLM) / 2048 + * where PLLM = J.DDDD (DDDD register ranges from 0 to 9999, decimal) + */ + pval = 1; + /* compute J portion of multiplier */ + jval = fsref / (aic26->mclk / 2048); + /* compute fractional DDDD component of multiplier */ + dval = fsref - (jval * (aic26->mclk / 2048)); + dval = (10000 * dval) / (aic26->mclk / 2048); + dev_dbg(&aic26->spi->dev, "Setting PLLM to %d.%04d\n", jval, dval); + qval = 0; + reg = 0x8000 | qval << 11 | pval << 8 | jval << 2; + snd_soc_component_write(component, AIC26_REG_PLL_PROG1, reg); + reg = dval << 2; + snd_soc_component_write(component, AIC26_REG_PLL_PROG2, reg); + + /* Audio Control 3 (clock provider mode, fsref rate) */ + if (aic26->clock_provider) + reg = 0x0800; + if (fsref == 48000) + reg = 0x2000; + snd_soc_component_update_bits(component, AIC26_REG_AUDIO_CTRL3, 0xf800, reg); + + /* Audio Control 1 (FSref divisor) */ + reg = wlen | aic26->datfm | (divisor << 3) | divisor; + snd_soc_component_update_bits(component, AIC26_REG_AUDIO_CTRL1, 0xfff, reg); + + return 0; +} + +/* + * aic26_mute - Mute control to reduce noise when changing audio format + */ +static int aic26_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct aic26 *aic26 = snd_soc_component_get_drvdata(component); + u16 reg; + + dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n", + dai, mute); + + if (mute) + reg = 0x8080; + else + reg = 0; + snd_soc_component_update_bits(component, AIC26_REG_DAC_GAIN, 0x8000, reg); + + return 0; +} + +static int aic26_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct aic26 *aic26 = snd_soc_component_get_drvdata(component); + + dev_dbg(&aic26->spi->dev, "aic26_set_sysclk(dai=%p, clk_id==%i," + " freq=%i, dir=%i)\n", + codec_dai, clk_id, freq, dir); + + /* MCLK needs to fall between 2MHz and 50 MHz */ + if ((freq < 2000000) || (freq > 50000000)) + return -EINVAL; + + aic26->mclk = freq; + return 0; +} + +static int aic26_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct aic26 *aic26 = snd_soc_component_get_drvdata(component); + + dev_dbg(&aic26->spi->dev, "aic26_set_fmt(dai=%p, fmt==%i)\n", + codec_dai, fmt); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: aic26->clock_provider = 1; break; + case SND_SOC_DAIFMT_CBC_CFC: aic26->clock_provider = 0; break; + default: + dev_dbg(&aic26->spi->dev, "bad master\n"); return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: aic26->datfm = AIC26_DATFM_I2S; break; + case SND_SOC_DAIFMT_DSP_A: aic26->datfm = AIC26_DATFM_DSP; break; + case SND_SOC_DAIFMT_RIGHT_J: aic26->datfm = AIC26_DATFM_RIGHTJ; break; + case SND_SOC_DAIFMT_LEFT_J: aic26->datfm = AIC26_DATFM_LEFTJ; break; + default: + dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL; + } + + return 0; +} + +/* --------------------------------------------------------------------- + * Digital Audio Interface Definition + */ +#define AIC26_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000) +#define AIC26_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE) + +static const struct snd_soc_dai_ops aic26_dai_ops = { + .hw_params = aic26_hw_params, + .mute_stream = aic26_mute, + .set_sysclk = aic26_set_sysclk, + .set_fmt = aic26_set_fmt, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver aic26_dai = { + .name = "tlv320aic26-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AIC26_RATES, + .formats = AIC26_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AIC26_RATES, + .formats = AIC26_FORMATS, + }, + .ops = &aic26_dai_ops, +}; + +/* --------------------------------------------------------------------- + * ALSA controls + */ +static const char *aic26_capture_src_text[] = {"Mic", "Aux"}; +static SOC_ENUM_SINGLE_DECL(aic26_capture_src_enum, + AIC26_REG_AUDIO_CTRL1, 12, + aic26_capture_src_text); + +static const struct snd_kcontrol_new aic26_snd_controls[] = { + /* Output */ + SOC_DOUBLE("PCM Playback Volume", AIC26_REG_DAC_GAIN, 8, 0, 0x7f, 1), + SOC_DOUBLE("PCM Playback Switch", AIC26_REG_DAC_GAIN, 15, 7, 1, 1), + SOC_SINGLE("PCM Capture Volume", AIC26_REG_ADC_GAIN, 8, 0x7f, 0), + SOC_SINGLE("PCM Capture Mute", AIC26_REG_ADC_GAIN, 15, 1, 1), + SOC_SINGLE("Keyclick activate", AIC26_REG_AUDIO_CTRL2, 15, 0x1, 0), + SOC_SINGLE("Keyclick amplitude", AIC26_REG_AUDIO_CTRL2, 12, 0x7, 0), + SOC_SINGLE("Keyclick frequency", AIC26_REG_AUDIO_CTRL2, 8, 0x7, 0), + SOC_SINGLE("Keyclick period", AIC26_REG_AUDIO_CTRL2, 4, 0xf, 0), + SOC_ENUM("Capture Source", aic26_capture_src_enum), +}; + +/* --------------------------------------------------------------------- + * SPI device portion of driver: sysfs files for debugging + */ + +static ssize_t keyclick_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aic26 *aic26 = dev_get_drvdata(dev); + int val, amp, freq, len; + + val = snd_soc_component_read(aic26->component, AIC26_REG_AUDIO_CTRL2); + amp = (val >> 12) & 0x7; + freq = (125 << ((val >> 8) & 0x7)) >> 1; + len = 2 * (1 + ((val >> 4) & 0xf)); + + return sysfs_emit(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len); +} + +/* Any write to the keyclick attribute will trigger the keyclick event */ +static ssize_t keyclick_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aic26 *aic26 = dev_get_drvdata(dev); + + snd_soc_component_update_bits(aic26->component, AIC26_REG_AUDIO_CTRL2, + 0x8000, 0x800); + + return count; +} + +static DEVICE_ATTR_RW(keyclick); + +/* --------------------------------------------------------------------- + * SoC CODEC portion of driver: probe and release routines + */ +static int aic26_probe(struct snd_soc_component *component) +{ + struct aic26 *aic26 = dev_get_drvdata(component->dev); + int ret, reg; + + aic26->component = component; + + /* Reset the codec to power on defaults */ + snd_soc_component_write(component, AIC26_REG_RESET, 0xBB00); + + /* Power up CODEC */ + snd_soc_component_write(component, AIC26_REG_POWER_CTRL, 0); + + /* Audio Control 3 (master mode, fsref rate) */ + reg = snd_soc_component_read(component, AIC26_REG_AUDIO_CTRL3); + reg &= ~0xf800; + reg |= 0x0800; /* set master mode */ + snd_soc_component_write(component, AIC26_REG_AUDIO_CTRL3, reg); + + /* Register the sysfs files for debugging */ + /* Create SysFS files */ + ret = device_create_file(component->dev, &dev_attr_keyclick); + if (ret) + dev_info(component->dev, "error creating sysfs files\n"); + + return 0; +} + +static const struct snd_soc_component_driver aic26_soc_component_dev = { + .probe = aic26_probe, + .controls = aic26_snd_controls, + .num_controls = ARRAY_SIZE(aic26_snd_controls), + .dapm_widgets = tlv320aic26_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets), + .dapm_routes = tlv320aic26_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config aic26_regmap = { + .reg_bits = 16, + .val_bits = 16, +}; + +/* --------------------------------------------------------------------- + * SPI device portion of driver: probe and release routines and SPI + * driver registration. + */ +static int aic26_spi_probe(struct spi_device *spi) +{ + struct aic26 *aic26; + int ret; + + dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n"); + + /* Allocate driver data */ + aic26 = devm_kzalloc(&spi->dev, sizeof *aic26, GFP_KERNEL); + if (!aic26) + return -ENOMEM; + + aic26->regmap = devm_regmap_init_spi(spi, &aic26_regmap); + if (IS_ERR(aic26->regmap)) + return PTR_ERR(aic26->regmap); + + /* Initialize the driver data */ + aic26->spi = spi; + dev_set_drvdata(&spi->dev, aic26); + aic26->clock_provider = 1; + + ret = devm_snd_soc_register_component(&spi->dev, + &aic26_soc_component_dev, &aic26_dai, 1); + return ret; +} + +static struct spi_driver aic26_spi = { + .driver = { + .name = "tlv320aic26-codec", + }, + .probe = aic26_spi_probe, +}; + +module_spi_driver(aic26_spi); diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h new file mode 100644 index 0000000000..c86569883e --- /dev/null +++ b/sound/soc/codecs/tlv320aic26.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Texas Instruments TLV320AIC26 low power audio CODEC + * register definitions + * + * Copyright (C) 2008 Secret Lab Technologies Ltd. + */ + +#ifndef _TLV320AIC26_H_ +#define _TLV320AIC26_H_ + +/* AIC26 Registers */ +#define AIC26_PAGE_ADDR(page, offset) ((page << 11) | offset << 5) + +/* Page 0: Auxiliary data registers */ +#define AIC26_REG_BAT1 AIC26_PAGE_ADDR(0, 0x05) +#define AIC26_REG_BAT2 AIC26_PAGE_ADDR(0, 0x06) +#define AIC26_REG_AUX AIC26_PAGE_ADDR(0, 0x07) +#define AIC26_REG_TEMP1 AIC26_PAGE_ADDR(0, 0x09) +#define AIC26_REG_TEMP2 AIC26_PAGE_ADDR(0, 0x0A) + +/* Page 1: Auxiliary control registers */ +#define AIC26_REG_AUX_ADC AIC26_PAGE_ADDR(1, 0x00) +#define AIC26_REG_STATUS AIC26_PAGE_ADDR(1, 0x01) +#define AIC26_REG_REFERENCE AIC26_PAGE_ADDR(1, 0x03) +#define AIC26_REG_RESET AIC26_PAGE_ADDR(1, 0x04) + +/* Page 2: Audio control registers */ +#define AIC26_REG_AUDIO_CTRL1 AIC26_PAGE_ADDR(2, 0x00) +#define AIC26_REG_ADC_GAIN AIC26_PAGE_ADDR(2, 0x01) +#define AIC26_REG_DAC_GAIN AIC26_PAGE_ADDR(2, 0x02) +#define AIC26_REG_SIDETONE AIC26_PAGE_ADDR(2, 0x03) +#define AIC26_REG_AUDIO_CTRL2 AIC26_PAGE_ADDR(2, 0x04) +#define AIC26_REG_POWER_CTRL AIC26_PAGE_ADDR(2, 0x05) +#define AIC26_REG_AUDIO_CTRL3 AIC26_PAGE_ADDR(2, 0x06) + +#define AIC26_REG_FILTER_COEFF_L_N0 AIC26_PAGE_ADDR(2, 0x07) +#define AIC26_REG_FILTER_COEFF_L_N1 AIC26_PAGE_ADDR(2, 0x08) +#define AIC26_REG_FILTER_COEFF_L_N2 AIC26_PAGE_ADDR(2, 0x09) +#define AIC26_REG_FILTER_COEFF_L_N3 AIC26_PAGE_ADDR(2, 0x0A) +#define AIC26_REG_FILTER_COEFF_L_N4 AIC26_PAGE_ADDR(2, 0x0B) +#define AIC26_REG_FILTER_COEFF_L_N5 AIC26_PAGE_ADDR(2, 0x0C) +#define AIC26_REG_FILTER_COEFF_L_D1 AIC26_PAGE_ADDR(2, 0x0D) +#define AIC26_REG_FILTER_COEFF_L_D2 AIC26_PAGE_ADDR(2, 0x0E) +#define AIC26_REG_FILTER_COEFF_L_D4 AIC26_PAGE_ADDR(2, 0x0F) +#define AIC26_REG_FILTER_COEFF_L_D5 AIC26_PAGE_ADDR(2, 0x10) +#define AIC26_REG_FILTER_COEFF_R_N0 AIC26_PAGE_ADDR(2, 0x11) +#define AIC26_REG_FILTER_COEFF_R_N1 AIC26_PAGE_ADDR(2, 0x12) +#define AIC26_REG_FILTER_COEFF_R_N2 AIC26_PAGE_ADDR(2, 0x13) +#define AIC26_REG_FILTER_COEFF_R_N3 AIC26_PAGE_ADDR(2, 0x14) +#define AIC26_REG_FILTER_COEFF_R_N4 AIC26_PAGE_ADDR(2, 0x15) +#define AIC26_REG_FILTER_COEFF_R_N5 AIC26_PAGE_ADDR(2, 0x16) +#define AIC26_REG_FILTER_COEFF_R_D1 AIC26_PAGE_ADDR(2, 0x17) +#define AIC26_REG_FILTER_COEFF_R_D2 AIC26_PAGE_ADDR(2, 0x18) +#define AIC26_REG_FILTER_COEFF_R_D4 AIC26_PAGE_ADDR(2, 0x19) +#define AIC26_REG_FILTER_COEFF_R_D5 AIC26_PAGE_ADDR(2, 0x1A) + +#define AIC26_REG_PLL_PROG1 AIC26_PAGE_ADDR(2, 0x1B) +#define AIC26_REG_PLL_PROG2 AIC26_PAGE_ADDR(2, 0x1C) +#define AIC26_REG_AUDIO_CTRL4 AIC26_PAGE_ADDR(2, 0x1D) +#define AIC26_REG_AUDIO_CTRL5 AIC26_PAGE_ADDR(2, 0x1E) + +/* fsref dividers; used in register 'Audio Control 1' */ +enum aic26_divisors { + AIC26_DIV_1 = 0, + AIC26_DIV_1_5 = 1, + AIC26_DIV_2 = 2, + AIC26_DIV_3 = 3, + AIC26_DIV_4 = 4, + AIC26_DIV_5 = 5, + AIC26_DIV_5_5 = 6, + AIC26_DIV_6 = 7, +}; + +/* Digital data format */ +enum aic26_datfm { + AIC26_DATFM_I2S = 0 << 8, + AIC26_DATFM_DSP = 1 << 8, + AIC26_DATFM_RIGHTJ = 2 << 8, /* right justified */ + AIC26_DATFM_LEFTJ = 3 << 8, /* left justified */ +}; + +/* Sample word length in bits; used in register 'Audio Control 1' */ +enum aic26_wlen { + AIC26_WLEN_16 = 0 << 10, + AIC26_WLEN_20 = 1 << 10, + AIC26_WLEN_24 = 2 << 10, + AIC26_WLEN_32 = 3 << 10, +}; + +#endif /* _TLV320AIC26_H_ */ diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c new file mode 100644 index 0000000000..9611aa8acb --- /dev/null +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -0,0 +1,1756 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC TLV320AIC31xx CODEC Driver + * + * Copyright (C) 2014-2017 Texas Instruments Incorporated - https://www.ti.com/ + * Jyri Sarha + * + * Based on ground work by: Ajit Kulkarni + * + * The TLV320AIC31xx series of audio codecs are low-power, highly integrated + * high performance codecs which provides a stereo DAC, a mono ADC, + * and mono/stereo Class-D speaker driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlv320aic31xx.h" + +static int aic31xx_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data); + +static const struct reg_default aic31xx_reg_defaults[] = { + { AIC31XX_CLKMUX, 0x00 }, + { AIC31XX_PLLPR, 0x11 }, + { AIC31XX_PLLJ, 0x04 }, + { AIC31XX_PLLDMSB, 0x00 }, + { AIC31XX_PLLDLSB, 0x00 }, + { AIC31XX_NDAC, 0x01 }, + { AIC31XX_MDAC, 0x01 }, + { AIC31XX_DOSRMSB, 0x00 }, + { AIC31XX_DOSRLSB, 0x80 }, + { AIC31XX_NADC, 0x01 }, + { AIC31XX_MADC, 0x01 }, + { AIC31XX_AOSR, 0x80 }, + { AIC31XX_IFACE1, 0x00 }, + { AIC31XX_DATA_OFFSET, 0x00 }, + { AIC31XX_IFACE2, 0x00 }, + { AIC31XX_BCLKN, 0x01 }, + { AIC31XX_DACSETUP, 0x14 }, + { AIC31XX_DACMUTE, 0x0c }, + { AIC31XX_LDACVOL, 0x00 }, + { AIC31XX_RDACVOL, 0x00 }, + { AIC31XX_ADCSETUP, 0x00 }, + { AIC31XX_ADCFGA, 0x80 }, + { AIC31XX_ADCVOL, 0x00 }, + { AIC31XX_HPDRIVER, 0x04 }, + { AIC31XX_SPKAMP, 0x06 }, + { AIC31XX_DACMIXERROUTE, 0x00 }, + { AIC31XX_LANALOGHPL, 0x7f }, + { AIC31XX_RANALOGHPR, 0x7f }, + { AIC31XX_LANALOGSPL, 0x7f }, + { AIC31XX_RANALOGSPR, 0x7f }, + { AIC31XX_HPLGAIN, 0x02 }, + { AIC31XX_HPRGAIN, 0x02 }, + { AIC31XX_SPLGAIN, 0x00 }, + { AIC31XX_SPRGAIN, 0x00 }, + { AIC31XX_MICBIAS, 0x00 }, + { AIC31XX_MICPGA, 0x80 }, + { AIC31XX_MICPGAPI, 0x00 }, + { AIC31XX_MICPGAMI, 0x00 }, +}; + +static bool aic31xx_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AIC31XX_PAGECTL: /* regmap implementation requires this */ + case AIC31XX_RESET: /* always clears after write */ + case AIC31XX_OT_FLAG: + case AIC31XX_ADCFLAG: + case AIC31XX_DACFLAG1: + case AIC31XX_DACFLAG2: + case AIC31XX_OFFLAG: /* Sticky interrupt flags */ + case AIC31XX_INTRDACFLAG: /* Sticky interrupt flags */ + case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */ + case AIC31XX_INTRDACFLAG2: + case AIC31XX_INTRADCFLAG2: + case AIC31XX_HSDETECT: + return true; + } + return false; +} + +static bool aic31xx_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AIC31XX_OT_FLAG: + case AIC31XX_ADCFLAG: + case AIC31XX_DACFLAG1: + case AIC31XX_DACFLAG2: + case AIC31XX_OFFLAG: /* Sticky interrupt flags */ + case AIC31XX_INTRDACFLAG: /* Sticky interrupt flags */ + case AIC31XX_INTRADCFLAG: /* Sticky interrupt flags */ + case AIC31XX_INTRDACFLAG2: + case AIC31XX_INTRADCFLAG2: + return false; + } + return true; +} + +static const struct regmap_range_cfg aic31xx_ranges[] = { + { + .range_min = 0, + .range_max = 12 * 128, + .selector_reg = AIC31XX_PAGECTL, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config aic31xx_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = aic31xx_writeable, + .volatile_reg = aic31xx_volatile, + .reg_defaults = aic31xx_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(aic31xx_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .ranges = aic31xx_ranges, + .num_ranges = ARRAY_SIZE(aic31xx_ranges), + .max_register = 12 * 128, +}; + +static const char * const aic31xx_supply_names[] = { + "HPVDD", + "SPRVDD", + "SPLVDD", + "AVDD", + "IOVDD", + "DVDD", +}; + +#define AIC31XX_NUM_SUPPLIES ARRAY_SIZE(aic31xx_supply_names) + +struct aic31xx_disable_nb { + struct notifier_block nb; + struct aic31xx_priv *aic31xx; +}; + +struct aic31xx_priv { + struct snd_soc_component *component; + u8 i2c_regs_status; + struct device *dev; + struct regmap *regmap; + enum aic31xx_type codec_type; + struct gpio_desc *gpio_reset; + int micbias_vg; + struct aic31xx_pdata pdata; + struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES]; + struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES]; + struct snd_soc_jack *jack; + u32 sysclk_id; + unsigned int sysclk; + u8 p_div; + int rate_div_line; + bool master_dapm_route_applied; + int irq; + u8 ocmv; /* output common-mode voltage */ +}; + +struct aic31xx_rate_divs { + u32 mclk_p; + u32 rate; + u8 pll_r; + u8 pll_j; + u16 pll_d; + u16 dosr; + u8 ndac; + u8 mdac; + u8 aosr; + u8 nadc; + u8 madc; +}; + +/* ADC dividers can be disabled by configuring them to 0 */ +static const struct aic31xx_rate_divs aic31xx_divs[] = { + /* mclk/p rate pll: r j d dosr ndac mdac aors nadc madc */ + /* 8k rate */ + { 512000, 8000, 4, 48, 0, 128, 48, 2, 128, 48, 2}, + {12000000, 8000, 1, 8, 1920, 128, 48, 2, 128, 48, 2}, + {12000000, 8000, 1, 8, 1920, 128, 32, 3, 128, 32, 3}, + {12500000, 8000, 1, 7, 8643, 128, 48, 2, 128, 48, 2}, + /* 11.025k rate */ + { 705600, 11025, 3, 48, 0, 128, 24, 3, 128, 24, 3}, + {12000000, 11025, 1, 7, 5264, 128, 32, 2, 128, 32, 2}, + {12000000, 11025, 1, 8, 4672, 128, 24, 3, 128, 24, 3}, + {12500000, 11025, 1, 7, 2253, 128, 32, 2, 128, 32, 2}, + /* 16k rate */ + { 512000, 16000, 4, 48, 0, 128, 16, 3, 128, 16, 3}, + { 1024000, 16000, 2, 48, 0, 128, 16, 3, 128, 16, 3}, + {12000000, 16000, 1, 8, 1920, 128, 24, 2, 128, 24, 2}, + {12000000, 16000, 1, 8, 1920, 128, 16, 3, 128, 16, 3}, + {12500000, 16000, 1, 7, 8643, 128, 24, 2, 128, 24, 2}, + /* 22.05k rate */ + { 705600, 22050, 4, 36, 0, 128, 12, 3, 128, 12, 3}, + { 1411200, 22050, 2, 36, 0, 128, 12, 3, 128, 12, 3}, + {12000000, 22050, 1, 7, 5264, 128, 16, 2, 128, 16, 2}, + {12000000, 22050, 1, 8, 4672, 128, 12, 3, 128, 12, 3}, + {12500000, 22050, 1, 7, 2253, 128, 16, 2, 128, 16, 2}, + /* 32k rate */ + { 1024000, 32000, 2, 48, 0, 128, 12, 2, 128, 12, 2}, + { 2048000, 32000, 1, 48, 0, 128, 12, 2, 128, 12, 2}, + {12000000, 32000, 1, 8, 1920, 128, 12, 2, 128, 12, 2}, + {12000000, 32000, 1, 8, 1920, 128, 8, 3, 128, 8, 3}, + {12500000, 32000, 1, 7, 8643, 128, 12, 2, 128, 12, 2}, + /* 44.1k rate */ + { 1411200, 44100, 2, 32, 0, 128, 8, 2, 128, 8, 2}, + { 2822400, 44100, 1, 32, 0, 128, 8, 2, 128, 8, 2}, + {12000000, 44100, 1, 7, 5264, 128, 8, 2, 128, 8, 2}, + {12000000, 44100, 1, 8, 4672, 128, 6, 3, 128, 6, 3}, + {12500000, 44100, 1, 7, 2253, 128, 8, 2, 128, 8, 2}, + /* 48k rate */ + { 1536000, 48000, 2, 32, 0, 128, 8, 2, 128, 8, 2}, + { 3072000, 48000, 1, 32, 0, 128, 8, 2, 128, 8, 2}, + {12000000, 48000, 1, 8, 1920, 128, 8, 2, 128, 8, 2}, + {12000000, 48000, 1, 7, 6800, 96, 5, 4, 96, 5, 4}, + {12500000, 48000, 1, 7, 8643, 128, 8, 2, 128, 8, 2}, + /* 88.2k rate */ + { 2822400, 88200, 2, 16, 0, 64, 8, 2, 64, 8, 2}, + { 5644800, 88200, 1, 16, 0, 64, 8, 2, 64, 8, 2}, + {12000000, 88200, 1, 7, 5264, 64, 8, 2, 64, 8, 2}, + {12000000, 88200, 1, 8, 4672, 64, 6, 3, 64, 6, 3}, + {12500000, 88200, 1, 7, 2253, 64, 8, 2, 64, 8, 2}, + /* 96k rate */ + { 3072000, 96000, 2, 16, 0, 64, 8, 2, 64, 8, 2}, + { 6144000, 96000, 1, 16, 0, 64, 8, 2, 64, 8, 2}, + {12000000, 96000, 1, 8, 1920, 64, 8, 2, 64, 8, 2}, + {12000000, 96000, 1, 7, 6800, 48, 5, 4, 48, 5, 4}, + {12500000, 96000, 1, 7, 8643, 64, 8, 2, 64, 8, 2}, + /* 176.4k rate */ + { 5644800, 176400, 2, 8, 0, 32, 8, 2, 32, 8, 2}, + {11289600, 176400, 1, 8, 0, 32, 8, 2, 32, 8, 2}, + {12000000, 176400, 1, 7, 5264, 32, 8, 2, 32, 8, 2}, + {12000000, 176400, 1, 8, 4672, 32, 6, 3, 32, 6, 3}, + {12500000, 176400, 1, 7, 2253, 32, 8, 2, 32, 8, 2}, + /* 192k rate */ + { 6144000, 192000, 2, 8, 0, 32, 8, 2, 32, 8, 2}, + {12288000, 192000, 1, 8, 0, 32, 8, 2, 32, 8, 2}, + {12000000, 192000, 1, 8, 1920, 32, 8, 2, 32, 8, 2}, + {12000000, 192000, 1, 7, 6800, 24, 5, 4, 24, 5, 4}, + {12500000, 192000, 1, 7, 8643, 32, 8, 2, 32, 8, 2}, +}; + +static const char * const ldac_in_text[] = { + "Off", "Left Data", "Right Data", "Mono" +}; + +static const char * const rdac_in_text[] = { + "Off", "Right Data", "Left Data", "Mono" +}; + +static SOC_ENUM_SINGLE_DECL(ldac_in_enum, AIC31XX_DACSETUP, 4, ldac_in_text); + +static SOC_ENUM_SINGLE_DECL(rdac_in_enum, AIC31XX_DACSETUP, 2, rdac_in_text); + +static const char * const mic_select_text[] = { + "Off", "FFR 10 Ohm", "FFR 20 Ohm", "FFR 40 Ohm" +}; + +static SOC_ENUM_SINGLE_DECL(mic1lp_p_enum, AIC31XX_MICPGAPI, 6, + mic_select_text); +static SOC_ENUM_SINGLE_DECL(mic1rp_p_enum, AIC31XX_MICPGAPI, 4, + mic_select_text); +static SOC_ENUM_SINGLE_DECL(mic1lm_p_enum, AIC31XX_MICPGAPI, 2, + mic_select_text); + +static SOC_ENUM_SINGLE_DECL(mic1lm_m_enum, AIC31XX_MICPGAMI, 4, + mic_select_text); + +static const char * const hp_poweron_time_text[] = { + "0us", "15.3us", "153us", "1.53ms", "15.3ms", "76.2ms", + "153ms", "304ms", "610ms", "1.22s", "3.04s", "6.1s" }; + +static SOC_ENUM_SINGLE_DECL(hp_poweron_time_enum, AIC31XX_HPPOP, 3, + hp_poweron_time_text); + +static const char * const hp_rampup_step_text[] = { + "0ms", "0.98ms", "1.95ms", "3.9ms" }; + +static SOC_ENUM_SINGLE_DECL(hp_rampup_step_enum, AIC31XX_HPPOP, 1, + hp_rampup_step_text); + +static const char * const vol_soft_step_mode_text[] = { + "fast", "slow", "disabled" }; + +static SOC_ENUM_SINGLE_DECL(vol_soft_step_mode_enum, AIC31XX_DACSETUP, 0, + vol_soft_step_mode_text); + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_fgain_tlv, 0, 10, 0); +static const DECLARE_TLV_DB_SCALE(adc_cgain_tlv, -2000, 50, 0); +static const DECLARE_TLV_DB_SCALE(mic_pga_tlv, 0, 50, 0); +static const DECLARE_TLV_DB_SCALE(hp_drv_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(class_D_drv_tlv, 600, 600, 0); +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -6350, 50, 0); +static const DECLARE_TLV_DB_SCALE(sp_vol_tlv, -6350, 50, 0); + +/* + * controls to be exported to the user space + */ +static const struct snd_kcontrol_new common31xx_snd_controls[] = { + SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL, + AIC31XX_RDACVOL, 0, -127, 48, 7, 0, dac_vol_tlv), + + SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN, + AIC31XX_HPRGAIN, 2, 1, 0), + SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN, + AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv), + + SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL, + AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv), + + /* HP de-pop control: apply power not immediately but via ramp + * function with these psarameters. Note that power up sequence + * has to wait for this to complete; this is implemented by + * polling HP driver status in aic31xx_dapm_power_event() + */ + SOC_ENUM("HP Output Driver Power-On time", hp_poweron_time_enum), + SOC_ENUM("HP Output Driver Ramp-up step", hp_rampup_step_enum), + + SOC_ENUM("Volume Soft Stepping", vol_soft_step_mode_enum), +}; + +static const struct snd_kcontrol_new aic31xx_snd_controls[] = { + SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1, + adc_fgain_tlv), + + SOC_SINGLE("ADC Capture Switch", AIC31XX_ADCFGA, 7, 1, 1), + SOC_DOUBLE_R_S_TLV("ADC Capture Volume", AIC31XX_ADCVOL, AIC31XX_ADCVOL, + 0, -24, 40, 6, 0, adc_cgain_tlv), + + SOC_SINGLE_TLV("Mic PGA Capture Volume", AIC31XX_MICPGA, 0, + 119, 0, mic_pga_tlv), +}; + +static const struct snd_kcontrol_new aic311x_snd_controls[] = { + SOC_DOUBLE_R("Speaker Driver Playback Switch", AIC31XX_SPLGAIN, + AIC31XX_SPRGAIN, 2, 1, 0), + SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume", AIC31XX_SPLGAIN, + AIC31XX_SPRGAIN, 3, 3, 0, class_D_drv_tlv), + + SOC_DOUBLE_R_TLV("Speaker Analog Playback Volume", AIC31XX_LANALOGSPL, + AIC31XX_RANALOGSPR, 0, 0x7F, 1, sp_vol_tlv), +}; + +static const struct snd_kcontrol_new aic310x_snd_controls[] = { + SOC_SINGLE("Speaker Driver Playback Switch", AIC31XX_SPLGAIN, + 2, 1, 0), + SOC_SINGLE_TLV("Speaker Driver Playback Volume", AIC31XX_SPLGAIN, + 3, 3, 0, class_D_drv_tlv), + + SOC_SINGLE_TLV("Speaker Analog Playback Volume", AIC31XX_LANALOGSPL, + 0, 0x7F, 1, sp_vol_tlv), +}; + +static const struct snd_kcontrol_new ldac_in_control = + SOC_DAPM_ENUM("DAC Left Input", ldac_in_enum); + +static const struct snd_kcontrol_new rdac_in_control = + SOC_DAPM_ENUM("DAC Right Input", rdac_in_enum); + +static int aic31xx_wait_bits(struct aic31xx_priv *aic31xx, unsigned int reg, + unsigned int mask, unsigned int wbits, int sleep, + int count) +{ + unsigned int bits; + int counter = count; + int ret = regmap_read(aic31xx->regmap, reg, &bits); + + while ((bits & mask) != wbits && counter && !ret) { + usleep_range(sleep, sleep * 2); + ret = regmap_read(aic31xx->regmap, reg, &bits); + counter--; + } + if ((bits & mask) != wbits) { + dev_err(aic31xx->dev, + "%s: Failed! 0x%x was 0x%x expected 0x%x (%d, 0x%x, %d us)\n", + __func__, reg, bits, wbits, ret, mask, + (count - counter) * sleep); + ret = -1; + } + return ret; +} + +#define WIDGET_BIT(reg, shift) (((shift) << 8) | (reg)) + +static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + unsigned int reg = AIC31XX_DACFLAG1; + unsigned int mask; + unsigned int timeout = 500 * USEC_PER_MSEC; + + switch (WIDGET_BIT(w->reg, w->shift)) { + case WIDGET_BIT(AIC31XX_DACSETUP, 7): + mask = AIC31XX_LDACPWRSTATUS_MASK; + break; + case WIDGET_BIT(AIC31XX_DACSETUP, 6): + mask = AIC31XX_RDACPWRSTATUS_MASK; + break; + case WIDGET_BIT(AIC31XX_HPDRIVER, 7): + mask = AIC31XX_HPLDRVPWRSTATUS_MASK; + if (event == SND_SOC_DAPM_POST_PMU) + timeout = 7 * USEC_PER_SEC; + break; + case WIDGET_BIT(AIC31XX_HPDRIVER, 6): + mask = AIC31XX_HPRDRVPWRSTATUS_MASK; + if (event == SND_SOC_DAPM_POST_PMU) + timeout = 7 * USEC_PER_SEC; + break; + case WIDGET_BIT(AIC31XX_SPKAMP, 7): + mask = AIC31XX_SPLDRVPWRSTATUS_MASK; + break; + case WIDGET_BIT(AIC31XX_SPKAMP, 6): + mask = AIC31XX_SPRDRVPWRSTATUS_MASK; + break; + case WIDGET_BIT(AIC31XX_ADCSETUP, 7): + mask = AIC31XX_ADCPWRSTATUS_MASK; + reg = AIC31XX_ADCFLAG; + break; + default: + dev_err(component->dev, "Unknown widget '%s' calling %s\n", + w->name, __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + return aic31xx_wait_bits(aic31xx, reg, mask, mask, + 5000, timeout / 5000); + case SND_SOC_DAPM_POST_PMD: + return aic31xx_wait_bits(aic31xx, reg, mask, 0, + 5000, timeout / 5000); + default: + dev_dbg(component->dev, + "Unhandled dapm widget event %d from %s\n", + event, w->name); + } + return 0; +} + +static const struct snd_kcontrol_new aic31xx_left_output_switches[] = { + SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0), + SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0), + SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0), +}; + +static const struct snd_kcontrol_new aic31xx_right_output_switches[] = { + SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0), + SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0), +}; + +static const struct snd_kcontrol_new dac31xx_left_output_switches[] = { + SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0), + SOC_DAPM_SINGLE("From AIN1", AIC31XX_DACMIXERROUTE, 5, 1, 0), + SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 4, 1, 0), +}; + +static const struct snd_kcontrol_new dac31xx_right_output_switches[] = { + SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0), + SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 1, 1, 0), +}; + +static const struct snd_kcontrol_new p_term_mic1lp = + SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum); + +static const struct snd_kcontrol_new p_term_mic1rp = + SOC_DAPM_ENUM("MIC1RP P-Terminal", mic1rp_p_enum); + +static const struct snd_kcontrol_new p_term_mic1lm = + SOC_DAPM_ENUM("MIC1LM P-Terminal", mic1lm_p_enum); + +static const struct snd_kcontrol_new m_term_mic1lm = + SOC_DAPM_ENUM("MIC1LM M-Terminal", mic1lm_m_enum); + +static const struct snd_kcontrol_new aic31xx_dapm_hpl_switch = + SOC_DAPM_SINGLE("Switch", AIC31XX_LANALOGHPL, 7, 1, 0); + +static const struct snd_kcontrol_new aic31xx_dapm_hpr_switch = + SOC_DAPM_SINGLE("Switch", AIC31XX_RANALOGHPR, 7, 1, 0); + +static const struct snd_kcontrol_new aic31xx_dapm_spl_switch = + SOC_DAPM_SINGLE("Switch", AIC31XX_LANALOGSPL, 7, 1, 0); + +static const struct snd_kcontrol_new aic31xx_dapm_spr_switch = + SOC_DAPM_SINGLE("Switch", AIC31XX_RANALOGSPR, 7, 1, 0); + +static int mic_bias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* change mic bias voltage to user defined */ + snd_soc_component_update_bits(component, AIC31XX_MICBIAS, + AIC31XX_MICBIAS_MASK, + aic31xx->micbias_vg << + AIC31XX_MICBIAS_SHIFT); + dev_dbg(component->dev, "%s: turned on\n", __func__); + break; + case SND_SOC_DAPM_PRE_PMD: + /* turn mic bias off */ + snd_soc_component_update_bits(component, AIC31XX_MICBIAS, + AIC31XX_MICBIAS_MASK, 0); + dev_dbg(component->dev, "%s: turned off\n", __func__); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("DAC Left Input", + SND_SOC_NOPM, 0, 0, &ldac_in_control), + SND_SOC_DAPM_MUX("DAC Right Input", + SND_SOC_NOPM, 0, 0, &rdac_in_control), + /* DACs */ + SND_SOC_DAPM_DAC_E("DAC Left", "Left Playback", + AIC31XX_DACSETUP, 7, 0, aic31xx_dapm_power_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("DAC Right", "Right Playback", + AIC31XX_DACSETUP, 6, 0, aic31xx_dapm_power_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* HP */ + SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0, + &aic31xx_dapm_hpl_switch), + SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0, + &aic31xx_dapm_hpr_switch), + + /* Output drivers */ + SND_SOC_DAPM_OUT_DRV_E("HPL Driver", AIC31XX_HPDRIVER, 7, 0, + NULL, 0, aic31xx_dapm_power_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUT_DRV_E("HPR Driver", AIC31XX_HPDRIVER, 6, 0, + NULL, 0, aic31xx_dapm_power_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + + /* Mic Bias */ + SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Keep BCLK/WCLK enabled even if DAC/ADC is powered down */ + SND_SOC_DAPM_SUPPLY("Activate I2S clocks", AIC31XX_IFACE2, 2, 0, + NULL, 0), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), +}; + +static const struct snd_soc_dapm_widget dac31xx_dapm_widgets[] = { + /* Inputs */ + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0, + dac31xx_left_output_switches, + ARRAY_SIZE(dac31xx_left_output_switches)), + SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0, + dac31xx_right_output_switches, + ARRAY_SIZE(dac31xx_right_output_switches)), +}; + +static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { + /* Inputs */ + SND_SOC_DAPM_INPUT("MIC1LP"), + SND_SOC_DAPM_INPUT("MIC1RP"), + SND_SOC_DAPM_INPUT("MIC1LM"), + + /* Input Selection to MIC_PGA */ + SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0, + &p_term_mic1lp), + SND_SOC_DAPM_MUX("MIC1RP P-Terminal", SND_SOC_NOPM, 0, 0, + &p_term_mic1rp), + SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0, + &p_term_mic1lm), + + /* ADC */ + SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0, + aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0, + &m_term_mic1lm), + + /* Enabling & Disabling MIC Gain Ctl */ + SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA, + 7, 1, NULL, 0), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0, + aic31xx_left_output_switches, + ARRAY_SIZE(aic31xx_left_output_switches)), + SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0, + aic31xx_right_output_switches, + ARRAY_SIZE(aic31xx_right_output_switches)), + + SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = { + /* AIC3111 and AIC3110 have stereo class-D amplifier */ + SND_SOC_DAPM_OUT_DRV_E("SPL ClassD", AIC31XX_SPKAMP, 7, 0, NULL, 0, + aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUT_DRV_E("SPR ClassD", AIC31XX_SPKAMP, 6, 0, NULL, 0, + aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SWITCH("Speaker Left", SND_SOC_NOPM, 0, 0, + &aic31xx_dapm_spl_switch), + SND_SOC_DAPM_SWITCH("Speaker Right", SND_SOC_NOPM, 0, 0, + &aic31xx_dapm_spr_switch), + SND_SOC_DAPM_OUTPUT("SPL"), + SND_SOC_DAPM_OUTPUT("SPR"), +}; + +/* AIC3100 and AIC3120 have only mono class-D amplifier */ +static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = { + SND_SOC_DAPM_OUT_DRV_E("SPK ClassD", AIC31XX_SPKAMP, 7, 0, NULL, 0, + aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SWITCH("Speaker", SND_SOC_NOPM, 0, 0, + &aic31xx_dapm_spl_switch), + SND_SOC_DAPM_OUTPUT("SPK"), +}; + +static const struct snd_soc_dapm_route +common31xx_audio_map[] = { + /* DAC Input Routing */ + {"DAC Left Input", "Left Data", "AIF IN"}, + {"DAC Left Input", "Right Data", "AIF IN"}, + {"DAC Left Input", "Mono", "AIF IN"}, + {"DAC Right Input", "Left Data", "AIF IN"}, + {"DAC Right Input", "Right Data", "AIF IN"}, + {"DAC Right Input", "Mono", "AIF IN"}, + {"DAC Left", NULL, "DAC Left Input"}, + {"DAC Right", NULL, "DAC Right Input"}, + + /* HPL path */ + {"HP Left", "Switch", "Output Left"}, + {"HPL Driver", NULL, "HP Left"}, + {"HPL", NULL, "HPL Driver"}, + + /* HPR path */ + {"HP Right", "Switch", "Output Right"}, + {"HPR Driver", NULL, "HP Right"}, + {"HPR", NULL, "HPR Driver"}, +}; + +static const struct snd_soc_dapm_route +dac31xx_audio_map[] = { + /* Left Output */ + {"Output Left", "From Left DAC", "DAC Left"}, + {"Output Left", "From AIN1", "AIN1"}, + {"Output Left", "From AIN2", "AIN2"}, + + /* Right Output */ + {"Output Right", "From Right DAC", "DAC Right"}, + {"Output Right", "From AIN2", "AIN2"}, +}; + +static const struct snd_soc_dapm_route +aic31xx_audio_map[] = { + /* Mic input */ + {"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"}, + {"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"}, + {"MIC1LP P-Terminal", "FFR 40 Ohm", "MIC1LP"}, + {"MIC1RP P-Terminal", "FFR 10 Ohm", "MIC1RP"}, + {"MIC1RP P-Terminal", "FFR 20 Ohm", "MIC1RP"}, + {"MIC1RP P-Terminal", "FFR 40 Ohm", "MIC1RP"}, + {"MIC1LM P-Terminal", "FFR 10 Ohm", "MIC1LM"}, + {"MIC1LM P-Terminal", "FFR 20 Ohm", "MIC1LM"}, + {"MIC1LM P-Terminal", "FFR 40 Ohm", "MIC1LM"}, + + {"MIC1LM M-Terminal", "FFR 10 Ohm", "MIC1LM"}, + {"MIC1LM M-Terminal", "FFR 20 Ohm", "MIC1LM"}, + {"MIC1LM M-Terminal", "FFR 40 Ohm", "MIC1LM"}, + + {"MIC_GAIN_CTL", NULL, "MIC1LP P-Terminal"}, + {"MIC_GAIN_CTL", NULL, "MIC1RP P-Terminal"}, + {"MIC_GAIN_CTL", NULL, "MIC1LM P-Terminal"}, + {"MIC_GAIN_CTL", NULL, "MIC1LM M-Terminal"}, + + {"ADC", NULL, "MIC_GAIN_CTL"}, + + {"AIF OUT", NULL, "ADC"}, + + /* Left Output */ + {"Output Left", "From Left DAC", "DAC Left"}, + {"Output Left", "From MIC1LP", "MIC1LP"}, + {"Output Left", "From MIC1RP", "MIC1RP"}, + + /* Right Output */ + {"Output Right", "From Right DAC", "DAC Right"}, + {"Output Right", "From MIC1RP", "MIC1RP"}, +}; + +static const struct snd_soc_dapm_route +aic311x_audio_map[] = { + /* SP L path */ + {"Speaker Left", "Switch", "Output Left"}, + {"SPL ClassD", NULL, "Speaker Left"}, + {"SPL", NULL, "SPL ClassD"}, + + /* SP R path */ + {"Speaker Right", "Switch", "Output Right"}, + {"SPR ClassD", NULL, "Speaker Right"}, + {"SPR", NULL, "SPR ClassD"}, +}; + +static const struct snd_soc_dapm_route +aic310x_audio_map[] = { + /* SP L path */ + {"Speaker", "Switch", "Output Left"}, + {"SPK ClassD", NULL, "Speaker"}, + {"SPK", NULL, "SPK ClassD"}, +}; + +/* + * Always connected DAPM routes for codec clock master modes. + * If the codec is the master on the I2S bus, we need to power up components + * to have valid DAC_CLK. + * + * In order to have the I2S clocks on the bus either the DACs/ADC need to be + * enabled, or the P0/R29/D2 (Keep bclk/wclk in power down) need to be set. + * + * Otherwise the codec will not generate clocks on the bus. + */ +static const struct snd_soc_dapm_route +common31xx_cm_audio_map[] = { + {"HPL", NULL, "AIF IN"}, + {"HPR", NULL, "AIF IN"}, + + {"AIF IN", NULL, "Activate I2S clocks"}, +}; + +static const struct snd_soc_dapm_route +aic31xx_cm_audio_map[] = { + {"AIF OUT", NULL, "MIC1LP"}, + {"AIF OUT", NULL, "MIC1RP"}, + {"AIF OUT", NULL, "MIC1LM"}, + + {"AIF OUT", NULL, "Activate I2S clocks"}, +}; + +static int aic31xx_add_controls(struct snd_soc_component *component) +{ + int ret = 0; + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + + if (!(aic31xx->codec_type & DAC31XX_BIT)) + ret = snd_soc_add_component_controls( + component, aic31xx_snd_controls, + ARRAY_SIZE(aic31xx_snd_controls)); + if (ret) + return ret; + + if (aic31xx->codec_type & AIC31XX_STEREO_CLASS_D_BIT) + ret = snd_soc_add_component_controls( + component, aic311x_snd_controls, + ARRAY_SIZE(aic311x_snd_controls)); + else + ret = snd_soc_add_component_controls( + component, aic310x_snd_controls, + ARRAY_SIZE(aic310x_snd_controls)); + + return ret; +} + +static int aic31xx_add_widgets(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + int ret = 0; + + if (aic31xx->codec_type & DAC31XX_BIT) { + ret = snd_soc_dapm_new_controls( + dapm, dac31xx_dapm_widgets, + ARRAY_SIZE(dac31xx_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, dac31xx_audio_map, + ARRAY_SIZE(dac31xx_audio_map)); + if (ret) + return ret; + } else { + ret = snd_soc_dapm_new_controls( + dapm, aic31xx_dapm_widgets, + ARRAY_SIZE(aic31xx_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, aic31xx_audio_map, + ARRAY_SIZE(aic31xx_audio_map)); + if (ret) + return ret; + } + + if (aic31xx->codec_type & AIC31XX_STEREO_CLASS_D_BIT) { + ret = snd_soc_dapm_new_controls( + dapm, aic311x_dapm_widgets, + ARRAY_SIZE(aic311x_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, aic311x_audio_map, + ARRAY_SIZE(aic311x_audio_map)); + if (ret) + return ret; + } else { + ret = snd_soc_dapm_new_controls( + dapm, aic310x_dapm_widgets, + ARRAY_SIZE(aic310x_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, aic310x_audio_map, + ARRAY_SIZE(aic310x_audio_map)); + if (ret) + return ret; + } + + return 0; +} + +static int aic31xx_setup_pll(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + int bclk_score = snd_soc_params_to_frame_size(params); + int mclk_p; + int bclk_n = 0; + int match = -1; + int i; + + if (!aic31xx->sysclk || !aic31xx->p_div) { + dev_err(component->dev, "Master clock not supplied\n"); + return -EINVAL; + } + mclk_p = aic31xx->sysclk / aic31xx->p_div; + + /* Use PLL as CODEC_CLKIN and DAC_CLK as BDIV_CLKIN */ + snd_soc_component_update_bits(component, AIC31XX_CLKMUX, + AIC31XX_CODEC_CLKIN_MASK, AIC31XX_CODEC_CLKIN_PLL); + snd_soc_component_update_bits(component, AIC31XX_IFACE2, + AIC31XX_BDIVCLK_MASK, AIC31XX_DAC2BCLK); + + for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++) { + if (aic31xx_divs[i].rate == params_rate(params) && + aic31xx_divs[i].mclk_p == mclk_p) { + int s = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) % + snd_soc_params_to_frame_size(params); + int bn = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) / + snd_soc_params_to_frame_size(params); + if (s < bclk_score && bn > 0) { + match = i; + bclk_n = bn; + bclk_score = s; + } + } + } + + if (match == -1) { + dev_err(component->dev, + "%s: Sample rate (%u) and format not supported\n", + __func__, params_rate(params)); + /* See bellow for details how fix this. */ + return -EINVAL; + } + if (bclk_score != 0) { + dev_warn(component->dev, "Can not produce exact bitclock"); + /* This is fine if using dsp format, but if using i2s + there may be trouble. To fix the issue edit the + aic31xx_divs table for your mclk and sample + rate. Details can be found from: + https://www.ti.com/lit/ds/symlink/tlv320aic3100.pdf + Section: 5.6 CLOCK Generation and PLL + */ + } + i = match; + + /* PLL configuration */ + snd_soc_component_update_bits(component, AIC31XX_PLLPR, AIC31XX_PLL_MASK, + (aic31xx->p_div << 4) | aic31xx_divs[i].pll_r); + snd_soc_component_write(component, AIC31XX_PLLJ, aic31xx_divs[i].pll_j); + + snd_soc_component_write(component, AIC31XX_PLLDMSB, + aic31xx_divs[i].pll_d >> 8); + snd_soc_component_write(component, AIC31XX_PLLDLSB, + aic31xx_divs[i].pll_d & 0xff); + + /* DAC dividers configuration */ + snd_soc_component_update_bits(component, AIC31XX_NDAC, AIC31XX_PLL_MASK, + aic31xx_divs[i].ndac); + snd_soc_component_update_bits(component, AIC31XX_MDAC, AIC31XX_PLL_MASK, + aic31xx_divs[i].mdac); + + snd_soc_component_write(component, AIC31XX_DOSRMSB, aic31xx_divs[i].dosr >> 8); + snd_soc_component_write(component, AIC31XX_DOSRLSB, aic31xx_divs[i].dosr & 0xff); + + /* ADC dividers configuration. Write reset value 1 if not used. */ + snd_soc_component_update_bits(component, AIC31XX_NADC, AIC31XX_PLL_MASK, + aic31xx_divs[i].nadc ? aic31xx_divs[i].nadc : 1); + snd_soc_component_update_bits(component, AIC31XX_MADC, AIC31XX_PLL_MASK, + aic31xx_divs[i].madc ? aic31xx_divs[i].madc : 1); + + snd_soc_component_write(component, AIC31XX_AOSR, aic31xx_divs[i].aosr); + + /* Bit clock divider configuration. */ + snd_soc_component_update_bits(component, AIC31XX_BCLKN, + AIC31XX_PLL_MASK, bclk_n); + + aic31xx->rate_div_line = i; + + dev_dbg(component->dev, + "pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n", + aic31xx_divs[i].pll_j, + aic31xx_divs[i].pll_d, + aic31xx->p_div, + aic31xx_divs[i].dosr, + aic31xx_divs[i].ndac, + aic31xx_divs[i].mdac, + aic31xx_divs[i].aosr, + aic31xx_divs[i].nadc, + aic31xx_divs[i].madc, + bclk_n + ); + + return 0; +} + +static int aic31xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + u8 data = 0; + + dev_dbg(component->dev, "## %s: width %d rate %d\n", + __func__, params_width(params), + params_rate(params)); + + switch (params_width(params)) { + case 16: + break; + case 20: + data = (AIC31XX_WORD_LEN_20BITS << + AIC31XX_IFACE1_DATALEN_SHIFT); + break; + case 24: + data = (AIC31XX_WORD_LEN_24BITS << + AIC31XX_IFACE1_DATALEN_SHIFT); + break; + case 32: + data = (AIC31XX_WORD_LEN_32BITS << + AIC31XX_IFACE1_DATALEN_SHIFT); + break; + default: + dev_err(component->dev, "%s: Unsupported width %d\n", + __func__, params_width(params)); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AIC31XX_IFACE1, + AIC31XX_IFACE1_DATALEN_MASK, + data); + + /* + * If BCLK is used as PLL input, the sysclk is determined by the hw + * params. So it must be updated here to match the input frequency. + */ + if (aic31xx->sysclk_id == AIC31XX_PLL_CLKIN_BCLK) { + aic31xx->sysclk = params_rate(params) * params_width(params) * + params_channels(params); + aic31xx->p_div = 1; + } + + return aic31xx_setup_pll(component, params); +} + +static int aic31xx_dac_mute(struct snd_soc_dai *codec_dai, int mute, + int direction) +{ + struct snd_soc_component *component = codec_dai->component; + + if (mute) { + snd_soc_component_update_bits(component, AIC31XX_DACMUTE, + AIC31XX_DACMUTE_MASK, + AIC31XX_DACMUTE_MASK); + } else { + snd_soc_component_update_bits(component, AIC31XX_DACMUTE, + AIC31XX_DACMUTE_MASK, 0x0); + } + + return 0; +} + +static int aic31xx_clock_master_routes(struct snd_soc_component *component, + unsigned int fmt) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + int ret; + + fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; + if (fmt == SND_SOC_DAIFMT_CBC_CFC && + aic31xx->master_dapm_route_applied) { + /* + * Remove the DAPM route(s) for codec clock master modes, + * if applied + */ + ret = snd_soc_dapm_del_routes(dapm, common31xx_cm_audio_map, + ARRAY_SIZE(common31xx_cm_audio_map)); + if (!ret && !(aic31xx->codec_type & DAC31XX_BIT)) + ret = snd_soc_dapm_del_routes(dapm, + aic31xx_cm_audio_map, + ARRAY_SIZE(aic31xx_cm_audio_map)); + + if (ret) + return ret; + + aic31xx->master_dapm_route_applied = false; + } else if (fmt != SND_SOC_DAIFMT_CBC_CFC && + !aic31xx->master_dapm_route_applied) { + /* + * Add the needed DAPM route(s) for codec clock master modes, + * if it is not done already + */ + ret = snd_soc_dapm_add_routes(dapm, common31xx_cm_audio_map, + ARRAY_SIZE(common31xx_cm_audio_map)); + if (!ret && !(aic31xx->codec_type & DAC31XX_BIT)) + ret = snd_soc_dapm_add_routes(dapm, + aic31xx_cm_audio_map, + ARRAY_SIZE(aic31xx_cm_audio_map)); + + if (ret) + return ret; + + aic31xx->master_dapm_route_applied = true; + } + + return 0; +} + +static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u8 iface_reg1 = 0; + u8 iface_reg2 = 0; + u8 dsp_a_val = 0; + + dev_dbg(component->dev, "## %s: fmt = 0x%x\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + iface_reg1 |= AIC31XX_BCLK_MASTER | AIC31XX_WCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFP: + iface_reg1 |= AIC31XX_WCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBP_CFC: + iface_reg1 |= AIC31XX_BCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + dev_err(component->dev, "Invalid DAI clock provider\n"); + return -EINVAL; + } + + /* signal polarity */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + iface_reg2 |= AIC31XX_BCLKINV_MASK; + break; + default: + dev_err(component->dev, "Invalid DAI clock signal polarity\n"); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + dsp_a_val = 0x1; + fallthrough; + case SND_SOC_DAIFMT_DSP_B: + /* + * NOTE: This CODEC samples on the falling edge of BCLK in + * DSP mode, this is inverted compared to what most DAIs + * expect, so we invert for this mode + */ + iface_reg2 ^= AIC31XX_BCLKINV_MASK; + iface_reg1 |= (AIC31XX_DSP_MODE << + AIC31XX_IFACE1_DATATYPE_SHIFT); + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_reg1 |= (AIC31XX_RIGHT_JUSTIFIED_MODE << + AIC31XX_IFACE1_DATATYPE_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg1 |= (AIC31XX_LEFT_JUSTIFIED_MODE << + AIC31XX_IFACE1_DATATYPE_SHIFT); + break; + default: + dev_err(component->dev, "Invalid DAI interface format\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AIC31XX_IFACE1, + AIC31XX_IFACE1_DATATYPE_MASK | + AIC31XX_IFACE1_MASTER_MASK, + iface_reg1); + snd_soc_component_update_bits(component, AIC31XX_DATA_OFFSET, + AIC31XX_DATA_OFFSET_MASK, + dsp_a_val); + snd_soc_component_update_bits(component, AIC31XX_IFACE2, + AIC31XX_BCLKINV_MASK, + iface_reg2); + + return aic31xx_clock_master_routes(component, fmt); +} + +static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + int i; + + dev_dbg(component->dev, "## %s: clk_id = %d, freq = %d, dir = %d\n", + __func__, clk_id, freq, dir); + + for (i = 1; i < 8; i++) + if (freq / i <= 20000000) + break; + if (freq/i > 20000000) { + dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n", + __func__, freq); + return -EINVAL; + } + aic31xx->p_div = i; + + for (i = 0; i < ARRAY_SIZE(aic31xx_divs); i++) + if (aic31xx_divs[i].mclk_p == freq / aic31xx->p_div) + break; + if (i == ARRAY_SIZE(aic31xx_divs)) { + dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n", + __func__, freq); + return -EINVAL; + } + + /* set clock on MCLK, BCLK, or GPIO1 as PLL input */ + snd_soc_component_update_bits(component, AIC31XX_CLKMUX, AIC31XX_PLL_CLKIN_MASK, + clk_id << AIC31XX_PLL_CLKIN_SHIFT); + + aic31xx->sysclk_id = clk_id; + aic31xx->sysclk = freq; + + return 0; +} + +static int aic31xx_regulator_event(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct aic31xx_disable_nb *disable_nb = + container_of(nb, struct aic31xx_disable_nb, nb); + struct aic31xx_priv *aic31xx = disable_nb->aic31xx; + + if (event & REGULATOR_EVENT_DISABLE) { + /* + * Put codec to reset and as at least one of the + * supplies was disabled. + */ + if (aic31xx->gpio_reset) + gpiod_set_value(aic31xx->gpio_reset, 1); + + regcache_mark_dirty(aic31xx->regmap); + dev_dbg(aic31xx->dev, "## %s: DISABLE received\n", __func__); + } + + return 0; +} + +static int aic31xx_reset(struct aic31xx_priv *aic31xx) +{ + int ret = 0; + + if (aic31xx->gpio_reset) { + gpiod_set_value(aic31xx->gpio_reset, 1); + ndelay(10); /* At least 10ns */ + gpiod_set_value(aic31xx->gpio_reset, 0); + } else { + ret = regmap_write(aic31xx->regmap, AIC31XX_RESET, 1); + } + mdelay(1); /* At least 1ms */ + + return ret; +} + +static void aic31xx_clk_on(struct snd_soc_component *component) +{ + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + u8 mask = AIC31XX_PM_MASK; + u8 on = AIC31XX_PM_MASK; + + dev_dbg(component->dev, "codec clock -> on (rate %d)\n", + aic31xx_divs[aic31xx->rate_div_line].rate); + snd_soc_component_update_bits(component, AIC31XX_PLLPR, mask, on); + mdelay(10); + snd_soc_component_update_bits(component, AIC31XX_NDAC, mask, on); + snd_soc_component_update_bits(component, AIC31XX_MDAC, mask, on); + if (aic31xx_divs[aic31xx->rate_div_line].nadc) + snd_soc_component_update_bits(component, AIC31XX_NADC, mask, on); + if (aic31xx_divs[aic31xx->rate_div_line].madc) + snd_soc_component_update_bits(component, AIC31XX_MADC, mask, on); + snd_soc_component_update_bits(component, AIC31XX_BCLKN, mask, on); +} + +static void aic31xx_clk_off(struct snd_soc_component *component) +{ + u8 mask = AIC31XX_PM_MASK; + u8 off = 0; + + dev_dbg(component->dev, "codec clock -> off\n"); + snd_soc_component_update_bits(component, AIC31XX_BCLKN, mask, off); + snd_soc_component_update_bits(component, AIC31XX_MADC, mask, off); + snd_soc_component_update_bits(component, AIC31XX_NADC, mask, off); + snd_soc_component_update_bits(component, AIC31XX_MDAC, mask, off); + snd_soc_component_update_bits(component, AIC31XX_NDAC, mask, off); + snd_soc_component_update_bits(component, AIC31XX_PLLPR, mask, off); +} + +static int aic31xx_power_on(struct snd_soc_component *component) +{ + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(aic31xx->supplies), + aic31xx->supplies); + if (ret) + return ret; + + regcache_cache_only(aic31xx->regmap, false); + + /* Reset device registers for a consistent power-on like state */ + ret = aic31xx_reset(aic31xx); + if (ret < 0) + dev_err(aic31xx->dev, "Could not reset device: %d\n", ret); + + ret = regcache_sync(aic31xx->regmap); + if (ret) { + dev_err(component->dev, + "Failed to restore cache: %d\n", ret); + regcache_cache_only(aic31xx->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies), + aic31xx->supplies); + return ret; + } + + /* + * The jack detection configuration is in the same register + * that is used to report jack detect status so is volatile + * and not covered by the cache sync, restore it separately. + */ + aic31xx_set_jack(component, aic31xx->jack, NULL); + + return 0; +} + +static void aic31xx_power_off(struct snd_soc_component *component) +{ + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + + regcache_cache_only(aic31xx->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(aic31xx->supplies), + aic31xx->supplies); +} + +static int aic31xx_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + dev_dbg(component->dev, "## %s: %d -> %d\n", __func__, + snd_soc_component_get_bias_level(component), level); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) + aic31xx_clk_on(component); + break; + case SND_SOC_BIAS_STANDBY: + switch (snd_soc_component_get_bias_level(component)) { + case SND_SOC_BIAS_OFF: + aic31xx_power_on(component); + break; + case SND_SOC_BIAS_PREPARE: + aic31xx_clk_off(component); + break; + default: + BUG(); + } + break; + case SND_SOC_BIAS_OFF: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) + aic31xx_power_off(component); + break; + } + + return 0; +} + +static int aic31xx_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + + aic31xx->jack = jack; + + /* Enable/Disable jack detection */ + regmap_write(aic31xx->regmap, AIC31XX_HSDETECT, + jack ? AIC31XX_HSD_ENABLE : 0); + + return 0; +} + +static int aic31xx_codec_probe(struct snd_soc_component *component) +{ + struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component); + int i, ret; + + dev_dbg(aic31xx->dev, "## %s\n", __func__); + + aic31xx->component = component; + + for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) { + aic31xx->disable_nb[i].nb.notifier_call = + aic31xx_regulator_event; + aic31xx->disable_nb[i].aic31xx = aic31xx; + ret = devm_regulator_register_notifier( + aic31xx->supplies[i].consumer, + &aic31xx->disable_nb[i].nb); + if (ret) { + dev_err(component->dev, + "Failed to request regulator notifier: %d\n", + ret); + return ret; + } + } + + regcache_cache_only(aic31xx->regmap, true); + regcache_mark_dirty(aic31xx->regmap); + + ret = aic31xx_add_controls(component); + if (ret) + return ret; + + ret = aic31xx_add_widgets(component); + if (ret) + return ret; + + /* set output common-mode voltage */ + snd_soc_component_update_bits(component, AIC31XX_HPDRIVER, + AIC31XX_HPD_OCMV_MASK, + aic31xx->ocmv << AIC31XX_HPD_OCMV_SHIFT); + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_driver_aic31xx = { + .probe = aic31xx_codec_probe, + .set_jack = aic31xx_set_jack, + .set_bias_level = aic31xx_set_bias_level, + .controls = common31xx_snd_controls, + .num_controls = ARRAY_SIZE(common31xx_snd_controls), + .dapm_widgets = common31xx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(common31xx_dapm_widgets), + .dapm_routes = common31xx_audio_map, + .num_dapm_routes = ARRAY_SIZE(common31xx_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_soc_dai_ops aic31xx_dai_ops = { + .hw_params = aic31xx_hw_params, + .set_sysclk = aic31xx_set_dai_sysclk, + .set_fmt = aic31xx_set_dai_fmt, + .mute_stream = aic31xx_dac_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver dac31xx_dai_driver[] = { + { + .name = "tlv320dac31xx-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AIC31XX_RATES, + .formats = AIC31XX_FORMATS, + }, + .ops = &aic31xx_dai_ops, + .symmetric_rate = 1, + } +}; + +static struct snd_soc_dai_driver aic31xx_dai_driver[] = { + { + .name = "tlv320aic31xx-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AIC31XX_RATES, + .formats = AIC31XX_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AIC31XX_RATES, + .formats = AIC31XX_FORMATS, + }, + .ops = &aic31xx_dai_ops, + .symmetric_rate = 1, + } +}; + +#if defined(CONFIG_OF) +static const struct of_device_id tlv320aic31xx_of_match[] = { + { .compatible = "ti,tlv320aic310x" }, + { .compatible = "ti,tlv320aic311x" }, + { .compatible = "ti,tlv320aic3100" }, + { .compatible = "ti,tlv320aic3110" }, + { .compatible = "ti,tlv320aic3120" }, + { .compatible = "ti,tlv320aic3111" }, + { .compatible = "ti,tlv320dac3100" }, + { .compatible = "ti,tlv320dac3101" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match); +#endif /* CONFIG_OF */ + +#ifdef CONFIG_ACPI +static const struct acpi_device_id aic31xx_acpi_match[] = { + { "10TI3100", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match); +#endif + +static irqreturn_t aic31xx_irq(int irq, void *data) +{ + struct aic31xx_priv *aic31xx = data; + struct device *dev = aic31xx->dev; + unsigned int value; + bool handled = false; + int ret; + + ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value); + if (ret) { + dev_err(dev, "Failed to read interrupt mask: %d\n", ret); + goto exit; + } + + if (value) + handled = true; + else + goto read_overflow; + + if (value & AIC31XX_HPLSCDETECT) + dev_err(dev, "Short circuit on Left output is detected\n"); + if (value & AIC31XX_HPRSCDETECT) + dev_err(dev, "Short circuit on Right output is detected\n"); + if (value & (AIC31XX_HSPLUG | AIC31XX_BUTTONPRESS)) { + unsigned int val; + int status = 0; + + ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG2, + &val); + if (ret) { + dev_err(dev, "Failed to read interrupt mask: %d\n", + ret); + goto exit; + } + + if (val & AIC31XX_BUTTONPRESS) + status |= SND_JACK_BTN_0; + + ret = regmap_read(aic31xx->regmap, AIC31XX_HSDETECT, &val); + if (ret) { + dev_err(dev, "Failed to read headset type: %d\n", ret); + goto exit; + } + + switch ((val & AIC31XX_HSD_TYPE_MASK) >> + AIC31XX_HSD_TYPE_SHIFT) { + case AIC31XX_HSD_HP: + status |= SND_JACK_HEADPHONE; + break; + case AIC31XX_HSD_HS: + status |= SND_JACK_HEADSET; + break; + default: + break; + } + + if (aic31xx->jack) + snd_soc_jack_report(aic31xx->jack, status, + AIC31XX_JACK_MASK); + } + if (value & ~(AIC31XX_HPLSCDETECT | + AIC31XX_HPRSCDETECT | + AIC31XX_HSPLUG | + AIC31XX_BUTTONPRESS)) + dev_err(dev, "Unknown DAC interrupt flags: 0x%08x\n", value); + +read_overflow: + ret = regmap_read(aic31xx->regmap, AIC31XX_OFFLAG, &value); + if (ret) { + dev_err(dev, "Failed to read overflow flag: %d\n", ret); + goto exit; + } + + if (value) + handled = true; + else + goto exit; + + if (value & AIC31XX_DAC_OF_LEFT) + dev_warn(dev, "Left-channel DAC overflow has occurred\n"); + if (value & AIC31XX_DAC_OF_RIGHT) + dev_warn(dev, "Right-channel DAC overflow has occurred\n"); + if (value & AIC31XX_DAC_OF_SHIFTER) + dev_warn(dev, "DAC barrel shifter overflow has occurred\n"); + if (value & AIC31XX_ADC_OF) + dev_warn(dev, "ADC overflow has occurred\n"); + if (value & AIC31XX_ADC_OF_SHIFTER) + dev_warn(dev, "ADC barrel shifter overflow has occurred\n"); + if (value & ~(AIC31XX_DAC_OF_LEFT | + AIC31XX_DAC_OF_RIGHT | + AIC31XX_DAC_OF_SHIFTER | + AIC31XX_ADC_OF | + AIC31XX_ADC_OF_SHIFTER)) + dev_warn(dev, "Unknown overflow interrupt flags: 0x%08x\n", value); + +exit: + if (handled) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static void aic31xx_configure_ocmv(struct aic31xx_priv *priv) +{ + struct device *dev = priv->dev; + int dvdd, avdd; + u32 value; + + if (dev->fwnode && + fwnode_property_read_u32(dev->fwnode, "ai31xx-ocmv", &value)) { + /* OCMV setting is forced by DT */ + if (value <= 3) { + priv->ocmv = value; + return; + } + } + + avdd = regulator_get_voltage(priv->supplies[3].consumer); + dvdd = regulator_get_voltage(priv->supplies[5].consumer); + + if (avdd > 3600000 || dvdd > 1950000) { + dev_warn(dev, + "Too high supply voltage(s) AVDD: %d, DVDD: %d\n", + avdd, dvdd); + } else if (avdd == 3600000 && dvdd == 1950000) { + priv->ocmv = AIC31XX_HPD_OCMV_1_8V; + } else if (avdd >= 3300000 && dvdd >= 1800000) { + priv->ocmv = AIC31XX_HPD_OCMV_1_65V; + } else if (avdd >= 3000000 && dvdd >= 1650000) { + priv->ocmv = AIC31XX_HPD_OCMV_1_5V; + } else if (avdd >= 2700000 && dvdd >= 1525000) { + priv->ocmv = AIC31XX_HPD_OCMV_1_35V; + } else { + dev_warn(dev, + "Invalid supply voltage(s) AVDD: %d, DVDD: %d\n", + avdd, dvdd); + } +} + +static const struct i2c_device_id aic31xx_i2c_id[] = { + { "tlv320aic310x", AIC3100 }, + { "tlv320aic311x", AIC3110 }, + { "tlv320aic3100", AIC3100 }, + { "tlv320aic3110", AIC3110 }, + { "tlv320aic3120", AIC3120 }, + { "tlv320aic3111", AIC3111 }, + { "tlv320dac3100", DAC3100 }, + { "tlv320dac3101", DAC3101 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id); + +static int aic31xx_i2c_probe(struct i2c_client *i2c) +{ + struct aic31xx_priv *aic31xx; + unsigned int micbias_value = MICBIAS_2_0V; + const struct i2c_device_id *id = i2c_match_id(aic31xx_i2c_id, i2c); + int i, ret; + + dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__, + id->name, (int)id->driver_data); + + aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL); + if (!aic31xx) + return -ENOMEM; + + aic31xx->regmap = devm_regmap_init_i2c(i2c, &aic31xx_i2c_regmap); + if (IS_ERR(aic31xx->regmap)) { + ret = PTR_ERR(aic31xx->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + regcache_cache_only(aic31xx->regmap, true); + + aic31xx->dev = &i2c->dev; + aic31xx->irq = i2c->irq; + + aic31xx->codec_type = id->driver_data; + + dev_set_drvdata(aic31xx->dev, aic31xx); + + fwnode_property_read_u32(aic31xx->dev->fwnode, "ai31xx-micbias-vg", + &micbias_value); + switch (micbias_value) { + case MICBIAS_2_0V: + case MICBIAS_2_5V: + case MICBIAS_AVDDV: + aic31xx->micbias_vg = micbias_value; + break; + default: + dev_err(aic31xx->dev, "Bad ai31xx-micbias-vg value %d\n", + micbias_value); + aic31xx->micbias_vg = MICBIAS_2_0V; + } + + if (dev_get_platdata(aic31xx->dev)) { + memcpy(&aic31xx->pdata, dev_get_platdata(aic31xx->dev), sizeof(aic31xx->pdata)); + aic31xx->codec_type = aic31xx->pdata.codec_type; + aic31xx->micbias_vg = aic31xx->pdata.micbias_vg; + } + + aic31xx->gpio_reset = devm_gpiod_get_optional(aic31xx->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(aic31xx->gpio_reset)) + return dev_err_probe(aic31xx->dev, PTR_ERR(aic31xx->gpio_reset), + "not able to acquire gpio\n"); + + for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) + aic31xx->supplies[i].supply = aic31xx_supply_names[i]; + + ret = devm_regulator_bulk_get(aic31xx->dev, + ARRAY_SIZE(aic31xx->supplies), + aic31xx->supplies); + if (ret) + return dev_err_probe(aic31xx->dev, ret, "Failed to request supplies\n"); + + aic31xx_configure_ocmv(aic31xx); + + if (aic31xx->irq > 0) { + regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1, + AIC31XX_GPIO1_FUNC_MASK, + AIC31XX_GPIO1_INT1 << + AIC31XX_GPIO1_FUNC_SHIFT); + + regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL, + AIC31XX_HSPLUGDET | + AIC31XX_BUTTONPRESSDET | + AIC31XX_SC | + AIC31XX_ENGINE); + + ret = devm_request_threaded_irq(aic31xx->dev, aic31xx->irq, + NULL, aic31xx_irq, + IRQF_ONESHOT, "aic31xx-irq", + aic31xx); + if (ret) { + dev_err(aic31xx->dev, "Unable to request IRQ\n"); + return ret; + } + } + + if (aic31xx->codec_type & DAC31XX_BIT) + return devm_snd_soc_register_component(&i2c->dev, + &soc_codec_driver_aic31xx, + dac31xx_dai_driver, + ARRAY_SIZE(dac31xx_dai_driver)); + else + return devm_snd_soc_register_component(&i2c->dev, + &soc_codec_driver_aic31xx, + aic31xx_dai_driver, + ARRAY_SIZE(aic31xx_dai_driver)); +} + +static struct i2c_driver aic31xx_i2c_driver = { + .driver = { + .name = "tlv320aic31xx-codec", + .of_match_table = of_match_ptr(tlv320aic31xx_of_match), + .acpi_match_table = ACPI_PTR(aic31xx_acpi_match), + }, + .probe = aic31xx_i2c_probe, + .id_table = aic31xx_i2c_id, +}; +module_i2c_driver(aic31xx_i2c_driver); + +MODULE_AUTHOR("Jyri Sarha "); +MODULE_DESCRIPTION("ASoC TLV320AIC31xx CODEC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h new file mode 100644 index 0000000000..80d062578f --- /dev/null +++ b/sound/soc/codecs/tlv320aic31xx.h @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC TLV320AIC31xx CODEC Driver Definitions + * + * Copyright (C) 2014-2017 Texas Instruments Incorporated - https://www.ti.com/ + */ + +#ifndef _TLV320AIC31XX_H +#define _TLV320AIC31XX_H + +#define AIC31XX_RATES SNDRV_PCM_RATE_8000_192000 + +#define AIC31XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AIC31XX_STEREO_CLASS_D_BIT BIT(1) +#define AIC31XX_MINIDSP_BIT BIT(2) +#define DAC31XX_BIT BIT(3) + +#define AIC31XX_JACK_MASK (SND_JACK_HEADPHONE | \ + SND_JACK_HEADSET | \ + SND_JACK_BTN_0) + +enum aic31xx_type { + AIC3100 = 0, + AIC3110 = AIC31XX_STEREO_CLASS_D_BIT, + AIC3120 = AIC31XX_MINIDSP_BIT, + AIC3111 = AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT, + DAC3100 = DAC31XX_BIT, + DAC3101 = DAC31XX_BIT | AIC31XX_STEREO_CLASS_D_BIT, +}; + +struct aic31xx_pdata { + enum aic31xx_type codec_type; + unsigned int gpio_reset; + int micbias_vg; +}; + +#define AIC31XX_REG(page, reg) ((page * 128) + reg) + +#define AIC31XX_PAGECTL AIC31XX_REG(0, 0) /* Page Control Register */ + +/* Page 0 Registers */ +#define AIC31XX_RESET AIC31XX_REG(0, 1) /* Software reset register */ +#define AIC31XX_OT_FLAG AIC31XX_REG(0, 3) /* OT FLAG register */ +#define AIC31XX_CLKMUX AIC31XX_REG(0, 4) /* Clock clock Gen muxing, Multiplexers*/ +#define AIC31XX_PLLPR AIC31XX_REG(0, 5) /* PLL P and R-VAL register */ +#define AIC31XX_PLLJ AIC31XX_REG(0, 6) /* PLL J-VAL register */ +#define AIC31XX_PLLDMSB AIC31XX_REG(0, 7) /* PLL D-VAL MSB register */ +#define AIC31XX_PLLDLSB AIC31XX_REG(0, 8) /* PLL D-VAL LSB register */ +#define AIC31XX_NDAC AIC31XX_REG(0, 11) /* DAC NDAC_VAL register*/ +#define AIC31XX_MDAC AIC31XX_REG(0, 12) /* DAC MDAC_VAL register */ +#define AIC31XX_DOSRMSB AIC31XX_REG(0, 13) /* DAC OSR setting register 1, MSB value */ +#define AIC31XX_DOSRLSB AIC31XX_REG(0, 14) /* DAC OSR setting register 2, LSB value */ +#define AIC31XX_MINI_DSP_INPOL AIC31XX_REG(0, 16) +#define AIC31XX_NADC AIC31XX_REG(0, 18) /* Clock setting register 8, PLL */ +#define AIC31XX_MADC AIC31XX_REG(0, 19) /* Clock setting register 9, PLL */ +#define AIC31XX_AOSR AIC31XX_REG(0, 20) /* ADC Oversampling (AOSR) Register */ +#define AIC31XX_CLKOUTMUX AIC31XX_REG(0, 25) /* Clock setting register 9, Multiplexers */ +#define AIC31XX_CLKOUTMVAL AIC31XX_REG(0, 26) /* Clock setting register 10, CLOCKOUT M divider value */ +#define AIC31XX_IFACE1 AIC31XX_REG(0, 27) /* Audio Interface Setting Register 1 */ +#define AIC31XX_DATA_OFFSET AIC31XX_REG(0, 28) /* Audio Data Slot Offset Programming */ +#define AIC31XX_IFACE2 AIC31XX_REG(0, 29) /* Audio Interface Setting Register 2 */ +#define AIC31XX_BCLKN AIC31XX_REG(0, 30) /* Clock setting register 11, BCLK N Divider */ +#define AIC31XX_IFACESEC1 AIC31XX_REG(0, 31) /* Audio Interface Setting Register 3, Secondary Audio Interface */ +#define AIC31XX_IFACESEC2 AIC31XX_REG(0, 32) /* Audio Interface Setting Register 4 */ +#define AIC31XX_IFACESEC3 AIC31XX_REG(0, 33) /* Audio Interface Setting Register 5 */ +#define AIC31XX_I2C AIC31XX_REG(0, 34) /* I2C Bus Condition */ +#define AIC31XX_ADCFLAG AIC31XX_REG(0, 36) /* ADC FLAG */ +#define AIC31XX_DACFLAG1 AIC31XX_REG(0, 37) /* DAC Flag Registers */ +#define AIC31XX_DACFLAG2 AIC31XX_REG(0, 38) +#define AIC31XX_OFFLAG AIC31XX_REG(0, 39) /* Sticky Interrupt flag (overflow) */ +#define AIC31XX_INTRDACFLAG AIC31XX_REG(0, 44) /* Sticy DAC Interrupt flags */ +#define AIC31XX_INTRADCFLAG AIC31XX_REG(0, 45) /* Sticy ADC Interrupt flags */ +#define AIC31XX_INTRDACFLAG2 AIC31XX_REG(0, 46) /* DAC Interrupt flags 2 */ +#define AIC31XX_INTRADCFLAG2 AIC31XX_REG(0, 47) /* ADC Interrupt flags 2 */ +#define AIC31XX_INT1CTRL AIC31XX_REG(0, 48) /* INT1 interrupt control */ +#define AIC31XX_INT2CTRL AIC31XX_REG(0, 49) /* INT2 interrupt control */ +#define AIC31XX_GPIO1 AIC31XX_REG(0, 51) /* GPIO1 control */ +#define AIC31XX_DACPRB AIC31XX_REG(0, 60) +#define AIC31XX_ADCPRB AIC31XX_REG(0, 61) /* ADC Instruction Set Register */ +#define AIC31XX_DACSETUP AIC31XX_REG(0, 63) /* DAC channel setup register */ +#define AIC31XX_DACMUTE AIC31XX_REG(0, 64) /* DAC Mute and volume control register */ +#define AIC31XX_LDACVOL AIC31XX_REG(0, 65) /* Left DAC channel digital volume control */ +#define AIC31XX_RDACVOL AIC31XX_REG(0, 66) /* Right DAC channel digital volume control */ +#define AIC31XX_HSDETECT AIC31XX_REG(0, 67) /* Headset detection */ +#define AIC31XX_ADCSETUP AIC31XX_REG(0, 81) /* ADC Digital Mic */ +#define AIC31XX_ADCFGA AIC31XX_REG(0, 82) /* ADC Digital Volume Control Fine Adjust */ +#define AIC31XX_ADCVOL AIC31XX_REG(0, 83) /* ADC Digital Volume Control Coarse Adjust */ + +/* Page 1 Registers */ +#define AIC31XX_HPDRIVER AIC31XX_REG(1, 31) /* Headphone drivers */ +#define AIC31XX_SPKAMP AIC31XX_REG(1, 32) /* Class-D Speakear Amplifier */ +#define AIC31XX_HPPOP AIC31XX_REG(1, 33) /* HP Output Drivers POP Removal Settings */ +#define AIC31XX_SPPGARAMP AIC31XX_REG(1, 34) /* Output Driver PGA Ramp-Down Period Control */ +#define AIC31XX_DACMIXERROUTE AIC31XX_REG(1, 35) /* DAC_L and DAC_R Output Mixer Routing */ +#define AIC31XX_LANALOGHPL AIC31XX_REG(1, 36) /* Left Analog Vol to HPL */ +#define AIC31XX_RANALOGHPR AIC31XX_REG(1, 37) /* Right Analog Vol to HPR */ +#define AIC31XX_LANALOGSPL AIC31XX_REG(1, 38) /* Left Analog Vol to SPL */ +#define AIC31XX_RANALOGSPR AIC31XX_REG(1, 39) /* Right Analog Vol to SPR */ +#define AIC31XX_HPLGAIN AIC31XX_REG(1, 40) /* HPL Driver */ +#define AIC31XX_HPRGAIN AIC31XX_REG(1, 41) /* HPR Driver */ +#define AIC31XX_SPLGAIN AIC31XX_REG(1, 42) /* SPL Driver */ +#define AIC31XX_SPRGAIN AIC31XX_REG(1, 43) /* SPR Driver */ +#define AIC31XX_HPCONTROL AIC31XX_REG(1, 44) /* HP Driver Control */ +#define AIC31XX_MICBIAS AIC31XX_REG(1, 46) /* MIC Bias Control */ +#define AIC31XX_MICPGA AIC31XX_REG(1, 47) /* MIC PGA*/ +#define AIC31XX_MICPGAPI AIC31XX_REG(1, 48) /* Delta-Sigma Mono ADC Channel Fine-Gain Input Selection for P-Terminal */ +#define AIC31XX_MICPGAMI AIC31XX_REG(1, 49) /* ADC Input Selection for M-Terminal */ +#define AIC31XX_MICPGACM AIC31XX_REG(1, 50) /* Input CM Settings */ + +/* Bits, masks, and shifts */ + +/* AIC31XX_CLKMUX */ +#define AIC31XX_PLL_CLKIN_MASK GENMASK(3, 2) +#define AIC31XX_PLL_CLKIN_SHIFT (2) +#define AIC31XX_PLL_CLKIN_MCLK 0x00 +#define AIC31XX_PLL_CLKIN_BCLK 0x01 +#define AIC31XX_PLL_CLKIN_GPIO1 0x02 +#define AIC31XX_PLL_CLKIN_DIN 0x03 +#define AIC31XX_CODEC_CLKIN_MASK GENMASK(1, 0) +#define AIC31XX_CODEC_CLKIN_SHIFT (0) +#define AIC31XX_CODEC_CLKIN_MCLK 0x00 +#define AIC31XX_CODEC_CLKIN_BCLK 0x01 +#define AIC31XX_CODEC_CLKIN_GPIO1 0x02 +#define AIC31XX_CODEC_CLKIN_PLL 0x03 + +/* AIC31XX_PLLPR */ +/* AIC31XX_NDAC */ +/* AIC31XX_MDAC */ +/* AIC31XX_NADC */ +/* AIC31XX_MADC */ +/* AIC31XX_BCLKN */ +#define AIC31XX_PLL_MASK GENMASK(6, 0) +#define AIC31XX_PM_MASK BIT(7) + +/* AIC31XX_IFACE1 */ +#define AIC31XX_IFACE1_DATATYPE_MASK GENMASK(7, 6) +#define AIC31XX_IFACE1_DATATYPE_SHIFT (6) +#define AIC31XX_I2S_MODE 0x00 +#define AIC31XX_DSP_MODE 0x01 +#define AIC31XX_RIGHT_JUSTIFIED_MODE 0x02 +#define AIC31XX_LEFT_JUSTIFIED_MODE 0x03 +#define AIC31XX_IFACE1_DATALEN_MASK GENMASK(5, 4) +#define AIC31XX_IFACE1_DATALEN_SHIFT (4) +#define AIC31XX_WORD_LEN_16BITS 0x00 +#define AIC31XX_WORD_LEN_20BITS 0x01 +#define AIC31XX_WORD_LEN_24BITS 0x02 +#define AIC31XX_WORD_LEN_32BITS 0x03 +#define AIC31XX_IFACE1_MASTER_MASK GENMASK(3, 2) +#define AIC31XX_BCLK_MASTER BIT(3) +#define AIC31XX_WCLK_MASTER BIT(2) + +/* AIC31XX_DATA_OFFSET */ +#define AIC31XX_DATA_OFFSET_MASK GENMASK(7, 0) + +/* AIC31XX_IFACE2 */ +#define AIC31XX_BCLKINV_MASK BIT(3) +#define AIC31XX_BDIVCLK_MASK GENMASK(1, 0) +#define AIC31XX_DAC2BCLK 0x00 +#define AIC31XX_DACMOD2BCLK 0x01 +#define AIC31XX_ADC2BCLK 0x02 +#define AIC31XX_ADCMOD2BCLK 0x03 +#define AIC31XX_KEEP_I2SCLK BIT(2) + +/* AIC31XX_ADCFLAG */ +#define AIC31XX_ADCPWRSTATUS_MASK BIT(6) + +/* AIC31XX_DACFLAG1 */ +#define AIC31XX_LDACPWRSTATUS_MASK BIT(7) +#define AIC31XX_HPLDRVPWRSTATUS_MASK BIT(5) +#define AIC31XX_SPLDRVPWRSTATUS_MASK BIT(4) +#define AIC31XX_RDACPWRSTATUS_MASK BIT(3) +#define AIC31XX_HPRDRVPWRSTATUS_MASK BIT(1) +#define AIC31XX_SPRDRVPWRSTATUS_MASK BIT(0) + +/* AIC31XX_OFFLAG */ +#define AIC31XX_DAC_OF_LEFT BIT(7) +#define AIC31XX_DAC_OF_RIGHT BIT(6) +#define AIC31XX_DAC_OF_SHIFTER BIT(5) +#define AIC31XX_ADC_OF BIT(3) +#define AIC31XX_ADC_OF_SHIFTER BIT(1) + +/* AIC31XX_INTRDACFLAG */ +#define AIC31XX_HPLSCDETECT BIT(7) +#define AIC31XX_HPRSCDETECT BIT(6) +#define AIC31XX_BUTTONPRESS BIT(5) +#define AIC31XX_HSPLUG BIT(4) +#define AIC31XX_LDRCTHRES BIT(3) +#define AIC31XX_RDRCTHRES BIT(2) +#define AIC31XX_DACSINT BIT(1) +#define AIC31XX_DACAINT BIT(0) + +/* AIC31XX_INT1CTRL */ +#define AIC31XX_HSPLUGDET BIT(7) +#define AIC31XX_BUTTONPRESSDET BIT(6) +#define AIC31XX_DRCTHRES BIT(5) +#define AIC31XX_AGCNOISE BIT(4) +#define AIC31XX_SC BIT(3) +#define AIC31XX_ENGINE BIT(2) + +/* AIC31XX_GPIO1 */ +#define AIC31XX_GPIO1_FUNC_MASK GENMASK(5, 2) +#define AIC31XX_GPIO1_FUNC_SHIFT 2 +#define AIC31XX_GPIO1_DISABLED 0x00 +#define AIC31XX_GPIO1_INPUT 0x01 +#define AIC31XX_GPIO1_GPI 0x02 +#define AIC31XX_GPIO1_GPO 0x03 +#define AIC31XX_GPIO1_CLKOUT 0x04 +#define AIC31XX_GPIO1_INT1 0x05 +#define AIC31XX_GPIO1_INT2 0x06 +#define AIC31XX_GPIO1_ADC_WCLK 0x07 +#define AIC31XX_GPIO1_SBCLK 0x08 +#define AIC31XX_GPIO1_SWCLK 0x09 +#define AIC31XX_GPIO1_ADC_MOD_CLK 0x10 +#define AIC31XX_GPIO1_SDOUT 0x11 + +/* AIC31XX_DACMUTE */ +#define AIC31XX_DACMUTE_MASK GENMASK(3, 2) + +/* AIC31XX_HSDETECT */ +#define AIC31XX_HSD_ENABLE BIT(7) +#define AIC31XX_HSD_TYPE_MASK GENMASK(6, 5) +#define AIC31XX_HSD_TYPE_SHIFT 5 +#define AIC31XX_HSD_NONE 0x00 +#define AIC31XX_HSD_HP 0x01 +#define AIC31XX_HSD_HS 0x03 + +/* AIC31XX_HPDRIVER */ +#define AIC31XX_HPD_OCMV_MASK GENMASK(4, 3) +#define AIC31XX_HPD_OCMV_SHIFT 3 +#define AIC31XX_HPD_OCMV_1_35V 0x0 +#define AIC31XX_HPD_OCMV_1_5V 0x1 +#define AIC31XX_HPD_OCMV_1_65V 0x2 +#define AIC31XX_HPD_OCMV_1_8V 0x3 + +/* AIC31XX_MICBIAS */ +#define AIC31XX_MICBIAS_MASK GENMASK(1, 0) +#define AIC31XX_MICBIAS_SHIFT 0 + +#endif /* _TLV320AIC31XX_H */ diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c new file mode 100644 index 0000000000..5c0a76a4a1 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -0,0 +1,499 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Clock Tree for the Texas Instruments TLV320AIC32x4 + * + * Copyright 2019 Annaliese McDermond + * + * Author: Annaliese McDermond + */ + +#include +#include +#include +#include + +#include "tlv320aic32x4.h" + +#define to_clk_aic32x4(_hw) container_of(_hw, struct clk_aic32x4, hw) +struct clk_aic32x4 { + struct clk_hw hw; + struct device *dev; + struct regmap *regmap; + unsigned int reg; +}; + +/* + * struct clk_aic32x4_pll_muldiv - Multiplier/divider settings + * @p: Divider + * @r: first multiplier + * @j: integer part of second multiplier + * @d: decimal part of second multiplier + */ +struct clk_aic32x4_pll_muldiv { + u8 p; + u16 r; + u8 j; + u16 d; +}; + +struct aic32x4_clkdesc { + const char *name; + const char * const *parent_names; + unsigned int num_parents; + const struct clk_ops *ops; + unsigned int reg; +}; + +static int clk_aic32x4_pll_prepare(struct clk_hw *hw) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + + return regmap_update_bits(pll->regmap, AIC32X4_PLLPR, + AIC32X4_PLLEN, AIC32X4_PLLEN); +} + +static void clk_aic32x4_pll_unprepare(struct clk_hw *hw) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + + regmap_update_bits(pll->regmap, AIC32X4_PLLPR, + AIC32X4_PLLEN, 0); +} + +static int clk_aic32x4_pll_is_prepared(struct clk_hw *hw) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + + unsigned int val; + int ret; + + ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val); + if (ret < 0) + return ret; + + return !!(val & AIC32X4_PLLEN); +} + +static int clk_aic32x4_pll_get_muldiv(struct clk_aic32x4 *pll, + struct clk_aic32x4_pll_muldiv *settings) +{ + /* Change to use regmap_bulk_read? */ + unsigned int val; + int ret; + + ret = regmap_read(pll->regmap, AIC32X4_PLLPR, &val); + if (ret < 0) + return ret; + settings->r = val & AIC32X4_PLL_R_MASK; + settings->p = (val & AIC32X4_PLL_P_MASK) >> AIC32X4_PLL_P_SHIFT; + + ret = regmap_read(pll->regmap, AIC32X4_PLLJ, &val); + if (ret < 0) + return ret; + settings->j = val; + + ret = regmap_read(pll->regmap, AIC32X4_PLLDMSB, &val); + if (ret < 0) + return ret; + settings->d = val << 8; + + ret = regmap_read(pll->regmap, AIC32X4_PLLDLSB, &val); + if (ret < 0) + return ret; + settings->d |= val; + + return 0; +} + +static int clk_aic32x4_pll_set_muldiv(struct clk_aic32x4 *pll, + struct clk_aic32x4_pll_muldiv *settings) +{ + int ret; + /* Change to use regmap_bulk_write for some if not all? */ + + ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, + AIC32X4_PLL_R_MASK, settings->r); + if (ret < 0) + return ret; + + ret = regmap_update_bits(pll->regmap, AIC32X4_PLLPR, + AIC32X4_PLL_P_MASK, + settings->p << AIC32X4_PLL_P_SHIFT); + if (ret < 0) + return ret; + + ret = regmap_write(pll->regmap, AIC32X4_PLLJ, settings->j); + if (ret < 0) + return ret; + + ret = regmap_write(pll->regmap, AIC32X4_PLLDMSB, (settings->d >> 8)); + if (ret < 0) + return ret; + ret = regmap_write(pll->regmap, AIC32X4_PLLDLSB, (settings->d & 0xff)); + if (ret < 0) + return ret; + + return 0; +} + +static unsigned long clk_aic32x4_pll_calc_rate( + struct clk_aic32x4_pll_muldiv *settings, + unsigned long parent_rate) +{ + u64 rate; + /* + * We scale j by 10000 to account for the decimal part of P and divide + * it back out later. + */ + rate = (u64) parent_rate * settings->r * + ((settings->j * 10000) + settings->d); + + return (unsigned long) DIV_ROUND_UP_ULL(rate, settings->p * 10000); +} + +static int clk_aic32x4_pll_calc_muldiv(struct clk_aic32x4_pll_muldiv *settings, + unsigned long rate, unsigned long parent_rate) +{ + u64 multiplier; + + settings->p = parent_rate / AIC32X4_MAX_PLL_CLKIN + 1; + if (settings->p > 8) + return -1; + + /* + * We scale this figure by 10000 so that we can get the decimal part + * of the multiplier. This is because we can't do floating point + * math in the kernel. + */ + multiplier = (u64) rate * settings->p * 10000; + do_div(multiplier, parent_rate); + + /* + * J can't be over 64, so R can scale this. + * R can't be greater than 4. + */ + settings->r = ((u32) multiplier / 640000) + 1; + if (settings->r > 4) + return -1; + do_div(multiplier, settings->r); + + /* + * J can't be < 1. + */ + if (multiplier < 10000) + return -1; + + /* Figure out the integer part, J, and the fractional part, D. */ + settings->j = (u32) multiplier / 10000; + settings->d = (u32) multiplier % 10000; + + return 0; +} + +static unsigned long clk_aic32x4_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + struct clk_aic32x4_pll_muldiv settings; + int ret; + + ret = clk_aic32x4_pll_get_muldiv(pll, &settings); + if (ret < 0) + return 0; + + return clk_aic32x4_pll_calc_rate(&settings, parent_rate); +} + +static int clk_aic32x4_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_aic32x4_pll_muldiv settings; + int ret; + + ret = clk_aic32x4_pll_calc_muldiv(&settings, req->rate, req->best_parent_rate); + if (ret < 0) + return -EINVAL; + + req->rate = clk_aic32x4_pll_calc_rate(&settings, req->best_parent_rate); + + return 0; +} + +static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + struct clk_aic32x4_pll_muldiv settings; + int ret; + + ret = clk_aic32x4_pll_calc_muldiv(&settings, rate, parent_rate); + if (ret < 0) + return -EINVAL; + + ret = clk_aic32x4_pll_set_muldiv(pll, &settings); + if (ret) + return ret; + + /* 10ms is the delay to wait before the clocks are stable */ + msleep(10); + + return 0; +} + +static int clk_aic32x4_pll_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + + return regmap_update_bits(pll->regmap, + AIC32X4_CLKMUX, + AIC32X4_PLL_CLKIN_MASK, + index << AIC32X4_PLL_CLKIN_SHIFT); +} + +static u8 clk_aic32x4_pll_get_parent(struct clk_hw *hw) +{ + struct clk_aic32x4 *pll = to_clk_aic32x4(hw); + unsigned int val; + + regmap_read(pll->regmap, AIC32X4_PLLPR, &val); + + return (val & AIC32X4_PLL_CLKIN_MASK) >> AIC32X4_PLL_CLKIN_SHIFT; +} + + +static const struct clk_ops aic32x4_pll_ops = { + .prepare = clk_aic32x4_pll_prepare, + .unprepare = clk_aic32x4_pll_unprepare, + .is_prepared = clk_aic32x4_pll_is_prepared, + .recalc_rate = clk_aic32x4_pll_recalc_rate, + .determine_rate = clk_aic32x4_pll_determine_rate, + .set_rate = clk_aic32x4_pll_set_rate, + .set_parent = clk_aic32x4_pll_set_parent, + .get_parent = clk_aic32x4_pll_get_parent, +}; + +static int clk_aic32x4_codec_clkin_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_aic32x4 *mux = to_clk_aic32x4(hw); + + return regmap_update_bits(mux->regmap, + AIC32X4_CLKMUX, + AIC32X4_CODEC_CLKIN_MASK, index << AIC32X4_CODEC_CLKIN_SHIFT); +} + +static u8 clk_aic32x4_codec_clkin_get_parent(struct clk_hw *hw) +{ + struct clk_aic32x4 *mux = to_clk_aic32x4(hw); + unsigned int val; + + regmap_read(mux->regmap, AIC32X4_CLKMUX, &val); + + return (val & AIC32X4_CODEC_CLKIN_MASK) >> AIC32X4_CODEC_CLKIN_SHIFT; +} + +static const struct clk_ops aic32x4_codec_clkin_ops = { + .determine_rate = clk_hw_determine_rate_no_reparent, + .set_parent = clk_aic32x4_codec_clkin_set_parent, + .get_parent = clk_aic32x4_codec_clkin_get_parent, +}; + +static int clk_aic32x4_div_prepare(struct clk_hw *hw) +{ + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + + return regmap_update_bits(div->regmap, div->reg, + AIC32X4_DIVEN, AIC32X4_DIVEN); +} + +static void clk_aic32x4_div_unprepare(struct clk_hw *hw) +{ + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + + regmap_update_bits(div->regmap, div->reg, + AIC32X4_DIVEN, 0); +} + +static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + u8 divisor; + + divisor = DIV_ROUND_UP(parent_rate, rate); + if (divisor > AIC32X4_DIV_MAX) + return -EINVAL; + + return regmap_update_bits(div->regmap, div->reg, + AIC32X4_DIV_MASK, divisor); +} + +static int clk_aic32x4_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long divisor; + + divisor = DIV_ROUND_UP(req->best_parent_rate, req->rate); + if (divisor > AIC32X4_DIV_MAX) + return -EINVAL; + + req->rate = DIV_ROUND_UP(req->best_parent_rate, divisor); + return 0; +} + +static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_aic32x4 *div = to_clk_aic32x4(hw); + unsigned int val; + int err; + + err = regmap_read(div->regmap, div->reg, &val); + if (err) + return 0; + + val &= AIC32X4_DIV_MASK; + if (!val) + val = AIC32X4_DIV_MAX; + + return DIV_ROUND_UP(parent_rate, val); +} + +static const struct clk_ops aic32x4_div_ops = { + .prepare = clk_aic32x4_div_prepare, + .unprepare = clk_aic32x4_div_unprepare, + .set_rate = clk_aic32x4_div_set_rate, + .determine_rate = clk_aic32x4_div_determine_rate, + .recalc_rate = clk_aic32x4_div_recalc_rate, +}; + +static int clk_aic32x4_bdiv_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_aic32x4 *mux = to_clk_aic32x4(hw); + + return regmap_update_bits(mux->regmap, AIC32X4_IFACE3, + AIC32X4_BDIVCLK_MASK, index); +} + +static u8 clk_aic32x4_bdiv_get_parent(struct clk_hw *hw) +{ + struct clk_aic32x4 *mux = to_clk_aic32x4(hw); + unsigned int val; + + regmap_read(mux->regmap, AIC32X4_IFACE3, &val); + + return val & AIC32X4_BDIVCLK_MASK; +} + +static const struct clk_ops aic32x4_bdiv_ops = { + .prepare = clk_aic32x4_div_prepare, + .unprepare = clk_aic32x4_div_unprepare, + .set_parent = clk_aic32x4_bdiv_set_parent, + .get_parent = clk_aic32x4_bdiv_get_parent, + .set_rate = clk_aic32x4_div_set_rate, + .determine_rate = clk_aic32x4_div_determine_rate, + .recalc_rate = clk_aic32x4_div_recalc_rate, +}; + +static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = { + { + .name = "pll", + .parent_names = + (const char* []) { "mclk", "bclk", "gpio", "din" }, + .num_parents = 4, + .ops = &aic32x4_pll_ops, + .reg = 0, + }, + { + .name = "codec_clkin", + .parent_names = + (const char *[]) { "mclk", "bclk", "gpio", "pll" }, + .num_parents = 4, + .ops = &aic32x4_codec_clkin_ops, + .reg = 0, + }, + { + .name = "ndac", + .parent_names = (const char * []) { "codec_clkin" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_NDAC, + }, + { + .name = "mdac", + .parent_names = (const char * []) { "ndac" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_MDAC, + }, + { + .name = "nadc", + .parent_names = (const char * []) { "codec_clkin" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_NADC, + }, + { + .name = "madc", + .parent_names = (const char * []) { "nadc" }, + .num_parents = 1, + .ops = &aic32x4_div_ops, + .reg = AIC32X4_MADC, + }, + { + .name = "bdiv", + .parent_names = + (const char *[]) { "ndac", "mdac", "nadc", "madc" }, + .num_parents = 4, + .ops = &aic32x4_bdiv_ops, + .reg = AIC32X4_BCLKN, + }, +}; + +static struct clk *aic32x4_register_clk(struct device *dev, + struct aic32x4_clkdesc *desc) +{ + struct clk_init_data init; + struct clk_aic32x4 *priv; + const char *devname = dev_name(dev); + + init.ops = desc->ops; + init.name = desc->name; + init.parent_names = desc->parent_names; + init.num_parents = desc->num_parents; + init.flags = 0; + + priv = devm_kzalloc(dev, sizeof(struct clk_aic32x4), GFP_KERNEL); + if (priv == NULL) + return (struct clk *) -ENOMEM; + + priv->dev = dev; + priv->hw.init = &init; + priv->regmap = dev_get_regmap(dev, NULL); + priv->reg = desc->reg; + + clk_hw_register_clkdev(&priv->hw, desc->name, devname); + return devm_clk_register(dev, &priv->hw); +} + +int aic32x4_register_clocks(struct device *dev, const char *mclk_name) +{ + int i; + + /* + * These lines are here to preserve the current functionality of + * the driver with regard to the DT. These should eventually be set + * by DT nodes so that the connections can be set up in configuration + * rather than code. + */ + aic32x4_clkdesc_array[0].parent_names = + (const char* []) { mclk_name, "bclk", "gpio", "din" }; + aic32x4_clkdesc_array[1].parent_names = + (const char *[]) { mclk_name, "bclk", "gpio", "pll" }; + + for (i = 0; i < ARRAY_SIZE(aic32x4_clkdesc_array); ++i) + aic32x4_register_clk(dev, &aic32x4_clkdesc_array[i]); + + return 0; +} +EXPORT_SYMBOL_GPL(aic32x4_register_clocks); diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c new file mode 100644 index 0000000000..49b33a256c --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2011-2019 NW Digital Radio + * + * Author: Annaliese McDermond + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. + * + */ + +#include +#include +#include +#include +#include + +#include "tlv320aic32x4.h" + +static const struct of_device_id aic32x4_of_id[]; +static const struct i2c_device_id aic32x4_i2c_id[]; + +static int aic32x4_i2c_probe(struct i2c_client *i2c) +{ + struct regmap *regmap; + struct regmap_config config; + + config = aic32x4_regmap_config; + config.reg_bits = 8; + config.val_bits = 8; + + regmap = devm_regmap_init_i2c(i2c, &config); + + if (i2c->dev.of_node) { + const struct of_device_id *oid; + + oid = of_match_node(aic32x4_of_id, i2c->dev.of_node); + dev_set_drvdata(&i2c->dev, (void *)oid->data); + } else { + const struct i2c_device_id *id; + + id = i2c_match_id(aic32x4_i2c_id, i2c); + dev_set_drvdata(&i2c->dev, (void *)id->driver_data); + } + + return aic32x4_probe(&i2c->dev, regmap); +} + +static void aic32x4_i2c_remove(struct i2c_client *i2c) +{ + aic32x4_remove(&i2c->dev); +} + +static const struct i2c_device_id aic32x4_i2c_id[] = { + { "tlv320aic32x4", (kernel_ulong_t)AIC32X4_TYPE_AIC32X4 }, + { "tlv320aic32x6", (kernel_ulong_t)AIC32X4_TYPE_AIC32X6 }, + { "tas2505", (kernel_ulong_t)AIC32X4_TYPE_TAS2505 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); + +static const struct of_device_id aic32x4_of_id[] = { + { .compatible = "ti,tlv320aic32x4", .data = (void *)AIC32X4_TYPE_AIC32X4 }, + { .compatible = "ti,tlv320aic32x6", .data = (void *)AIC32X4_TYPE_AIC32X6 }, + { .compatible = "ti,tas2505", .data = (void *)AIC32X4_TYPE_TAS2505 }, + { /* senitel */ } +}; +MODULE_DEVICE_TABLE(of, aic32x4_of_id); + +static struct i2c_driver aic32x4_i2c_driver = { + .driver = { + .name = "tlv320aic32x4", + .of_match_table = aic32x4_of_id, + }, + .probe = aic32x4_i2c_probe, + .remove = aic32x4_i2c_remove, + .id_table = aic32x4_i2c_id, +}; + +module_i2c_driver(aic32x4_i2c_driver); + +MODULE_DESCRIPTION("ASoC TLV320AIC32x4 codec driver I2C"); +MODULE_AUTHOR("Annaliese McDermond "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4-spi.c b/sound/soc/codecs/tlv320aic32x4-spi.c new file mode 100644 index 0000000000..03cce8d640 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4-spi.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright 2011-2019 NW Digital Radio + * + * Author: Annaliese McDermond + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. + * + */ + +#include +#include +#include +#include +#include + +#include "tlv320aic32x4.h" + +static const struct of_device_id aic32x4_of_id[]; + +static int aic32x4_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + struct regmap_config config; + + config = aic32x4_regmap_config; + config.reg_bits = 7; + config.pad_bits = 1; + config.val_bits = 8; + config.read_flag_mask = 0x01; + + regmap = devm_regmap_init_spi(spi, &config); + + if (spi->dev.of_node) { + const struct of_device_id *oid; + + oid = of_match_node(aic32x4_of_id, spi->dev.of_node); + dev_set_drvdata(&spi->dev, (void *)oid->data); + } else { + const struct spi_device_id *id_entry; + + id_entry = spi_get_device_id(spi); + dev_set_drvdata(&spi->dev, (void *)id_entry->driver_data); + } + + return aic32x4_probe(&spi->dev, regmap); +} + +static void aic32x4_spi_remove(struct spi_device *spi) +{ + aic32x4_remove(&spi->dev); +} + +static const struct spi_device_id aic32x4_spi_id[] = { + { "tlv320aic32x4", (kernel_ulong_t)AIC32X4_TYPE_AIC32X4 }, + { "tlv320aic32x6", (kernel_ulong_t)AIC32X4_TYPE_AIC32X6 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, aic32x4_spi_id); + +static const struct of_device_id aic32x4_of_id[] = { + { .compatible = "ti,tlv320aic32x4", .data = (void *)AIC32X4_TYPE_AIC32X4 }, + { .compatible = "ti,tlv320aic32x6", .data = (void *)AIC32X4_TYPE_AIC32X6 }, + { /* senitel */ } +}; +MODULE_DEVICE_TABLE(of, aic32x4_of_id); + +static struct spi_driver aic32x4_spi_driver = { + .driver = { + .name = "tlv320aic32x4", + .owner = THIS_MODULE, + .of_match_table = aic32x4_of_id, + }, + .probe = aic32x4_spi_probe, + .remove = aic32x4_spi_remove, + .id_table = aic32x4_spi_id, +}; + +module_spi_driver(aic32x4_spi_driver); + +MODULE_DESCRIPTION("ASoC TLV320AIC32x4 codec driver SPI"); +MODULE_AUTHOR("Annaliese McDermond "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c new file mode 100644 index 0000000000..6829834a41 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -0,0 +1,1437 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * linux/sound/soc/codecs/tlv320aic32x4.c + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlv320aic32x4.h" + +struct aic32x4_priv { + struct regmap *regmap; + u32 power_cfg; + u32 micpga_routing; + bool swapdacs; + int rstn_gpio; + const char *mclk_name; + + struct regulator *supply_ldo; + struct regulator *supply_iov; + struct regulator *supply_dv; + struct regulator *supply_av; + + struct aic32x4_setup_data *setup; + struct device *dev; + enum aic32x4_type type; + + unsigned int fmt; +}; + +static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u32 adc_reg; + + /* + * Workaround: the datasheet does not mention a required programming + * sequence but experiments show the ADC needs to be reset after each + * capture to avoid audible artifacts. + */ + switch (event) { + case SND_SOC_DAPM_POST_PMD: + adc_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP); + snd_soc_component_write(component, AIC32X4_ADCSETUP, adc_reg | + AIC32X4_LADC_EN | AIC32X4_RADC_EN); + snd_soc_component_write(component, AIC32X4_ADCSETUP, adc_reg); + break; + } + return 0; +}; + +static int mic_bias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Change Mic Bias Registor */ + snd_soc_component_update_bits(component, AIC32X4_MICBIAS, + AIC32x4_MICBIAS_MASK, + AIC32X4_MICBIAS_LDOIN | + AIC32X4_MICBIAS_2075V); + printk(KERN_DEBUG "%s: Mic Bias will be turned ON\n", __func__); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, AIC32X4_MICBIAS, + AIC32x4_MICBIAS_MASK, 0); + printk(KERN_DEBUG "%s: Mic Bias will be turned OFF\n", + __func__); + break; + } + + return 0; +} + + +static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; + + val = snd_soc_component_read(component, AIC32X4_DINCTL); + + ucontrol->value.integer.value[0] = (val & 0x01); + + return 0; +}; + +static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; + u8 gpio_check; + + val = snd_soc_component_read(component, AIC32X4_DOUTCTL); + gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); + if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { + printk(KERN_ERR "%s: MFP2 is not configure as a GPIO output\n", + __func__); + return -EINVAL; + } + + if (ucontrol->value.integer.value[0] == (val & AIC32X4_MFP2_GPIO_OUT_HIGH)) + return 0; + + if (ucontrol->value.integer.value[0]) + val |= ucontrol->value.integer.value[0]; + else + val &= ~AIC32X4_MFP2_GPIO_OUT_HIGH; + + snd_soc_component_write(component, AIC32X4_DOUTCTL, val); + + return 0; +}; + +static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; + + val = snd_soc_component_read(component, AIC32X4_SCLKCTL); + + ucontrol->value.integer.value[0] = (val & 0x01); + + return 0; +}; + +static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; + u8 gpio_check; + + val = snd_soc_component_read(component, AIC32X4_MISOCTL); + gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED); + if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) { + printk(KERN_ERR "%s: MFP4 is not configure as a GPIO output\n", + __func__); + return -EINVAL; + } + + if (ucontrol->value.integer.value[0] == (val & AIC32X4_MFP5_GPIO_OUT_HIGH)) + return 0; + + if (ucontrol->value.integer.value[0]) + val |= ucontrol->value.integer.value[0]; + else + val &= ~AIC32X4_MFP5_GPIO_OUT_HIGH; + + snd_soc_component_write(component, AIC32X4_MISOCTL, val); + + return 0; +}; + +static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; + + val = snd_soc_component_read(component, AIC32X4_GPIOCTL); + ucontrol->value.integer.value[0] = ((val & 0x2) >> 1); + + return 0; +}; + +static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + u8 val; + u8 gpio_check; + + val = snd_soc_component_read(component, AIC32X4_GPIOCTL); + gpio_check = (val & AIC32X4_MFP5_GPIO_OUTPUT); + if (gpio_check != AIC32X4_MFP5_GPIO_OUTPUT) { + printk(KERN_ERR "%s: MFP5 is not configure as a GPIO output\n", + __func__); + return -EINVAL; + } + + if (ucontrol->value.integer.value[0] == (val & 0x1)) + return 0; + + if (ucontrol->value.integer.value[0]) + val |= ucontrol->value.integer.value[0]; + else + val &= 0xfe; + + snd_soc_component_write(component, AIC32X4_GPIOCTL, val); + + return 0; +}; + +static const struct snd_kcontrol_new aic32x4_mfp1[] = { + SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL), +}; + +static const struct snd_kcontrol_new aic32x4_mfp2[] = { + SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio), +}; + +static const struct snd_kcontrol_new aic32x4_mfp3[] = { + SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL), +}; + +static const struct snd_kcontrol_new aic32x4_mfp4[] = { + SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio), +}; + +static const struct snd_kcontrol_new aic32x4_mfp5[] = { + SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio, + aic32x4_set_mfp5_gpio), +}; + +/* 0dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0); +/* -63.5dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_pcm, -6350, 50, 0); +/* -6dB min, 1dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_driver_gain, -600, 100, 0); +/* -12dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_adc_vol, -1200, 50, 0); +/* -6dB min, 1dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_tas_driver_gain, -5850, 50, 0); +static DECLARE_TLV_DB_SCALE(tlv_amp_vol, 0, 600, 1); + +static const char * const lo_cm_text[] = { + "Full Chip", "1.65V", +}; + +static SOC_ENUM_SINGLE_DECL(lo_cm_enum, AIC32X4_CMMODE, 3, lo_cm_text); + +static const char * const ptm_text[] = { + "P3", "P2", "P1", +}; + +static SOC_ENUM_SINGLE_DECL(l_ptm_enum, AIC32X4_LPLAYBACK, 2, ptm_text); +static SOC_ENUM_SINGLE_DECL(r_ptm_enum, AIC32X4_RPLAYBACK, 2, ptm_text); + +static const struct snd_kcontrol_new aic32x4_snd_controls[] = { + SOC_DOUBLE_R_S_TLV("PCM Playback Volume", AIC32X4_LDACVOL, + AIC32X4_RDACVOL, 0, -0x7f, 0x30, 7, 0, tlv_pcm), + SOC_ENUM("DAC Left Playback PowerTune Switch", l_ptm_enum), + SOC_ENUM("DAC Right Playback PowerTune Switch", r_ptm_enum), + SOC_DOUBLE_R_S_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 0, -0x6, 0x1d, 5, 0, + tlv_driver_gain), + SOC_DOUBLE_R_S_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 0, -0x6, 0x1d, 5, 0, + tlv_driver_gain), + SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 6, 0x01, 1), + SOC_ENUM("LO Playback Common Mode Switch", lo_cm_enum), + SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 7, 0x01, 1), + + SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), + SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), + + SOC_DOUBLE_R_S_TLV("ADC Level Volume", AIC32X4_LADCVOL, + AIC32X4_RADCVOL, 0, -0x18, 0x28, 6, 0, tlv_adc_vol), + SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), + + SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), + SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), + SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, + 4, 0x07, 0), + SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, + 0, 0x03, 0), + SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, + 6, 0x03, 0), + SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, + 1, 0x1F, 0), + SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, + 0, 0x7F, 0), + SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, + 0, 0x1F, 0), + SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, + 0, 0x0F, 0), +}; + +static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_HPRROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_R Switch", AIC32X4_HPRROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new lol_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_LOLROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new lor_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0), +}; + +static const char * const resistor_text[] = { + "Off", "10 kOhm", "20 kOhm", "40 kOhm", +}; + +/* Left mixer pins */ +static SOC_ENUM_SINGLE_DECL(in1l_lpga_p_enum, AIC32X4_LMICPGAPIN, 6, resistor_text); +static SOC_ENUM_SINGLE_DECL(in2l_lpga_p_enum, AIC32X4_LMICPGAPIN, 4, resistor_text); +static SOC_ENUM_SINGLE_DECL(in3l_lpga_p_enum, AIC32X4_LMICPGAPIN, 2, resistor_text); +static SOC_ENUM_SINGLE_DECL(in1r_lpga_p_enum, AIC32X4_LMICPGAPIN, 0, resistor_text); + +static SOC_ENUM_SINGLE_DECL(cml_lpga_n_enum, AIC32X4_LMICPGANIN, 6, resistor_text); +static SOC_ENUM_SINGLE_DECL(in2r_lpga_n_enum, AIC32X4_LMICPGANIN, 4, resistor_text); +static SOC_ENUM_SINGLE_DECL(in3r_lpga_n_enum, AIC32X4_LMICPGANIN, 2, resistor_text); + +static const struct snd_kcontrol_new in1l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN1_L L+ Switch", in1l_lpga_p_enum), +}; +static const struct snd_kcontrol_new in2l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN2_L L+ Switch", in2l_lpga_p_enum), +}; +static const struct snd_kcontrol_new in3l_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN3_L L+ Switch", in3l_lpga_p_enum), +}; +static const struct snd_kcontrol_new in1r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN1_R L+ Switch", in1r_lpga_p_enum), +}; +static const struct snd_kcontrol_new cml_to_lmixer_controls[] = { + SOC_DAPM_ENUM("CM_L L- Switch", cml_lpga_n_enum), +}; +static const struct snd_kcontrol_new in2r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN2_R L- Switch", in2r_lpga_n_enum), +}; +static const struct snd_kcontrol_new in3r_to_lmixer_controls[] = { + SOC_DAPM_ENUM("IN3_R L- Switch", in3r_lpga_n_enum), +}; + +/* Right mixer pins */ +static SOC_ENUM_SINGLE_DECL(in1r_rpga_p_enum, AIC32X4_RMICPGAPIN, 6, resistor_text); +static SOC_ENUM_SINGLE_DECL(in2r_rpga_p_enum, AIC32X4_RMICPGAPIN, 4, resistor_text); +static SOC_ENUM_SINGLE_DECL(in3r_rpga_p_enum, AIC32X4_RMICPGAPIN, 2, resistor_text); +static SOC_ENUM_SINGLE_DECL(in2l_rpga_p_enum, AIC32X4_RMICPGAPIN, 0, resistor_text); +static SOC_ENUM_SINGLE_DECL(cmr_rpga_n_enum, AIC32X4_RMICPGANIN, 6, resistor_text); +static SOC_ENUM_SINGLE_DECL(in1l_rpga_n_enum, AIC32X4_RMICPGANIN, 4, resistor_text); +static SOC_ENUM_SINGLE_DECL(in3l_rpga_n_enum, AIC32X4_RMICPGANIN, 2, resistor_text); + +static const struct snd_kcontrol_new in1r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN1_R R+ Switch", in1r_rpga_p_enum), +}; +static const struct snd_kcontrol_new in2r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN2_R R+ Switch", in2r_rpga_p_enum), +}; +static const struct snd_kcontrol_new in3r_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN3_R R+ Switch", in3r_rpga_p_enum), +}; +static const struct snd_kcontrol_new in2l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN2_L R+ Switch", in2l_rpga_p_enum), +}; +static const struct snd_kcontrol_new cmr_to_rmixer_controls[] = { + SOC_DAPM_ENUM("CM_R R- Switch", cmr_rpga_n_enum), +}; +static const struct snd_kcontrol_new in1l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN1_L R- Switch", in1l_rpga_n_enum), +}; +static const struct snd_kcontrol_new in3l_to_rmixer_controls[] = { + SOC_DAPM_ENUM("IN3_L R- Switch", in3l_rpga_n_enum), +}; + +static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", AIC32X4_DACSETUP, 7, 0), + SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, + &hpl_output_mixer_controls[0], + ARRAY_SIZE(hpl_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPL Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0, + &lol_output_mixer_controls[0], + ARRAY_SIZE(lol_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOL Power", AIC32X4_OUTPWRCTL, 3, 0, NULL, 0), + + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", AIC32X4_DACSETUP, 6, 0), + SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, + &hpr_output_mixer_controls[0], + ARRAY_SIZE(hpr_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPR Power", AIC32X4_OUTPWRCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0, + &lor_output_mixer_controls[0], + ARRAY_SIZE(lor_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0), + + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), + SND_SOC_DAPM_MUX("IN1_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in1r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN2_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in2r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN3_R to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in3r_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN2_L to Right Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in2l_to_rmixer_controls), + SND_SOC_DAPM_MUX("CM_R to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + cmr_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN1_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + in1l_to_rmixer_controls), + SND_SOC_DAPM_MUX("IN3_L to Right Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + in3l_to_rmixer_controls), + + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), + SND_SOC_DAPM_MUX("IN1_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in1l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN2_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in2l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN3_L to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in3l_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN1_R to Left Mixer Positive Resistor", SND_SOC_NOPM, 0, 0, + in1r_to_lmixer_controls), + SND_SOC_DAPM_MUX("CM_L to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + cml_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN2_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + in2r_to_lmixer_controls), + SND_SOC_DAPM_MUX("IN3_R to Left Mixer Negative Resistor", SND_SOC_NOPM, 0, 0, + in3r_to_lmixer_controls), + + SND_SOC_DAPM_SUPPLY("Mic Bias", AIC32X4_MICBIAS, 6, 0, mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_POST("ADC Reset", aic32x4_reset_adc), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LOL"), + SND_SOC_DAPM_OUTPUT("LOR"), + SND_SOC_DAPM_INPUT("IN1_L"), + SND_SOC_DAPM_INPUT("IN1_R"), + SND_SOC_DAPM_INPUT("IN2_L"), + SND_SOC_DAPM_INPUT("IN2_R"), + SND_SOC_DAPM_INPUT("IN3_L"), + SND_SOC_DAPM_INPUT("IN3_R"), + SND_SOC_DAPM_INPUT("CM_L"), + SND_SOC_DAPM_INPUT("CM_R"), +}; + +static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + /* Left Output */ + {"HPL Output Mixer", "L_DAC Switch", "Left DAC"}, + {"HPL Output Mixer", "IN1_L Switch", "IN1_L"}, + + {"HPL Power", NULL, "HPL Output Mixer"}, + {"HPL", NULL, "HPL Power"}, + + {"LOL Output Mixer", "L_DAC Switch", "Left DAC"}, + + {"LOL Power", NULL, "LOL Output Mixer"}, + {"LOL", NULL, "LOL Power"}, + + /* Right Output */ + {"HPR Output Mixer", "R_DAC Switch", "Right DAC"}, + {"HPR Output Mixer", "IN1_R Switch", "IN1_R"}, + + {"HPR Power", NULL, "HPR Output Mixer"}, + {"HPR", NULL, "HPR Power"}, + + {"LOR Output Mixer", "R_DAC Switch", "Right DAC"}, + + {"LOR Power", NULL, "LOR Output Mixer"}, + {"LOR", NULL, "LOR Power"}, + + /* Right Input */ + {"Right ADC", NULL, "IN1_R to Right Mixer Positive Resistor"}, + {"IN1_R to Right Mixer Positive Resistor", "10 kOhm", "IN1_R"}, + {"IN1_R to Right Mixer Positive Resistor", "20 kOhm", "IN1_R"}, + {"IN1_R to Right Mixer Positive Resistor", "40 kOhm", "IN1_R"}, + + {"Right ADC", NULL, "IN2_R to Right Mixer Positive Resistor"}, + {"IN2_R to Right Mixer Positive Resistor", "10 kOhm", "IN2_R"}, + {"IN2_R to Right Mixer Positive Resistor", "20 kOhm", "IN2_R"}, + {"IN2_R to Right Mixer Positive Resistor", "40 kOhm", "IN2_R"}, + + {"Right ADC", NULL, "IN3_R to Right Mixer Positive Resistor"}, + {"IN3_R to Right Mixer Positive Resistor", "10 kOhm", "IN3_R"}, + {"IN3_R to Right Mixer Positive Resistor", "20 kOhm", "IN3_R"}, + {"IN3_R to Right Mixer Positive Resistor", "40 kOhm", "IN3_R"}, + + {"Right ADC", NULL, "IN2_L to Right Mixer Positive Resistor"}, + {"IN2_L to Right Mixer Positive Resistor", "10 kOhm", "IN2_L"}, + {"IN2_L to Right Mixer Positive Resistor", "20 kOhm", "IN2_L"}, + {"IN2_L to Right Mixer Positive Resistor", "40 kOhm", "IN2_L"}, + + {"Right ADC", NULL, "CM_R to Right Mixer Negative Resistor"}, + {"CM_R to Right Mixer Negative Resistor", "10 kOhm", "CM_R"}, + {"CM_R to Right Mixer Negative Resistor", "20 kOhm", "CM_R"}, + {"CM_R to Right Mixer Negative Resistor", "40 kOhm", "CM_R"}, + + {"Right ADC", NULL, "IN1_L to Right Mixer Negative Resistor"}, + {"IN1_L to Right Mixer Negative Resistor", "10 kOhm", "IN1_L"}, + {"IN1_L to Right Mixer Negative Resistor", "20 kOhm", "IN1_L"}, + {"IN1_L to Right Mixer Negative Resistor", "40 kOhm", "IN1_L"}, + + {"Right ADC", NULL, "IN3_L to Right Mixer Negative Resistor"}, + {"IN3_L to Right Mixer Negative Resistor", "10 kOhm", "IN3_L"}, + {"IN3_L to Right Mixer Negative Resistor", "20 kOhm", "IN3_L"}, + {"IN3_L to Right Mixer Negative Resistor", "40 kOhm", "IN3_L"}, + + /* Left Input */ + {"Left ADC", NULL, "IN1_L to Left Mixer Positive Resistor"}, + {"IN1_L to Left Mixer Positive Resistor", "10 kOhm", "IN1_L"}, + {"IN1_L to Left Mixer Positive Resistor", "20 kOhm", "IN1_L"}, + {"IN1_L to Left Mixer Positive Resistor", "40 kOhm", "IN1_L"}, + + {"Left ADC", NULL, "IN2_L to Left Mixer Positive Resistor"}, + {"IN2_L to Left Mixer Positive Resistor", "10 kOhm", "IN2_L"}, + {"IN2_L to Left Mixer Positive Resistor", "20 kOhm", "IN2_L"}, + {"IN2_L to Left Mixer Positive Resistor", "40 kOhm", "IN2_L"}, + + {"Left ADC", NULL, "IN3_L to Left Mixer Positive Resistor"}, + {"IN3_L to Left Mixer Positive Resistor", "10 kOhm", "IN3_L"}, + {"IN3_L to Left Mixer Positive Resistor", "20 kOhm", "IN3_L"}, + {"IN3_L to Left Mixer Positive Resistor", "40 kOhm", "IN3_L"}, + + {"Left ADC", NULL, "IN1_R to Left Mixer Positive Resistor"}, + {"IN1_R to Left Mixer Positive Resistor", "10 kOhm", "IN1_R"}, + {"IN1_R to Left Mixer Positive Resistor", "20 kOhm", "IN1_R"}, + {"IN1_R to Left Mixer Positive Resistor", "40 kOhm", "IN1_R"}, + + {"Left ADC", NULL, "CM_L to Left Mixer Negative Resistor"}, + {"CM_L to Left Mixer Negative Resistor", "10 kOhm", "CM_L"}, + {"CM_L to Left Mixer Negative Resistor", "20 kOhm", "CM_L"}, + {"CM_L to Left Mixer Negative Resistor", "40 kOhm", "CM_L"}, + + {"Left ADC", NULL, "IN2_R to Left Mixer Negative Resistor"}, + {"IN2_R to Left Mixer Negative Resistor", "10 kOhm", "IN2_R"}, + {"IN2_R to Left Mixer Negative Resistor", "20 kOhm", "IN2_R"}, + {"IN2_R to Left Mixer Negative Resistor", "40 kOhm", "IN2_R"}, + + {"Left ADC", NULL, "IN3_R to Left Mixer Negative Resistor"}, + {"IN3_R to Left Mixer Negative Resistor", "10 kOhm", "IN3_R"}, + {"IN3_R to Left Mixer Negative Resistor", "20 kOhm", "IN3_R"}, + {"IN3_R to Left Mixer Negative Resistor", "40 kOhm", "IN3_R"}, +}; + +static const struct regmap_range_cfg aic32x4_regmap_pages[] = { + { + .selector_reg = 0, + .selector_mask = 0xff, + .window_start = 0, + .window_len = 128, + .range_min = 0, + .range_max = AIC32X4_REFPOWERUP, + }, +}; + +const struct regmap_config aic32x4_regmap_config = { + .max_register = AIC32X4_REFPOWERUP, + .ranges = aic32x4_regmap_pages, + .num_ranges = ARRAY_SIZE(aic32x4_regmap_pages), +}; +EXPORT_SYMBOL(aic32x4_regmap_config); + +static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct clk *mclk; + struct clk *pll; + + pll = devm_clk_get(component->dev, "pll"); + if (IS_ERR(pll)) + return PTR_ERR(pll); + + mclk = clk_get_parent(pll); + + return clk_set_rate(mclk, freq); +} + +static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + u8 iface_reg_1 = 0; + u8 iface_reg_2 = 0; + u8 iface_reg_3 = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + printk(KERN_ERR "aic32x4: invalid clock provider\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg_1 |= (AIC32X4_DSP_MODE << + AIC32X4_IFACE1_DATATYPE_SHIFT); + iface_reg_3 |= AIC32X4_BCLKINV_MASK; /* invert bit clock */ + iface_reg_2 = 0x01; /* add offset 1 */ + break; + case SND_SOC_DAIFMT_DSP_B: + iface_reg_1 |= (AIC32X4_DSP_MODE << + AIC32X4_IFACE1_DATATYPE_SHIFT); + iface_reg_3 |= AIC32X4_BCLKINV_MASK; /* invert bit clock */ + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_reg_1 |= (AIC32X4_RIGHT_JUSTIFIED_MODE << + AIC32X4_IFACE1_DATATYPE_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg_1 |= (AIC32X4_LEFT_JUSTIFIED_MODE << + AIC32X4_IFACE1_DATATYPE_SHIFT); + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); + return -EINVAL; + } + + aic32x4->fmt = fmt; + + snd_soc_component_update_bits(component, AIC32X4_IFACE1, + AIC32X4_IFACE1_DATATYPE_MASK | + AIC32X4_IFACE1_MASTER_MASK, iface_reg_1); + snd_soc_component_update_bits(component, AIC32X4_IFACE2, + AIC32X4_DATA_OFFSET_MASK, iface_reg_2); + snd_soc_component_update_bits(component, AIC32X4_IFACE3, + AIC32X4_BCLKINV_MASK, iface_reg_3); + + return 0; +} + +static int aic32x4_set_aosr(struct snd_soc_component *component, u8 aosr) +{ + return snd_soc_component_write(component, AIC32X4_AOSR, aosr); +} + +static int aic32x4_set_dosr(struct snd_soc_component *component, u16 dosr) +{ + snd_soc_component_write(component, AIC32X4_DOSRMSB, dosr >> 8); + snd_soc_component_write(component, AIC32X4_DOSRLSB, + (dosr & 0xff)); + + return 0; +} + +static int aic32x4_set_processing_blocks(struct snd_soc_component *component, + u8 r_block, u8 p_block) +{ + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + + if (aic32x4->type == AIC32X4_TYPE_TAS2505) { + if (r_block || p_block > 3) + return -EINVAL; + + snd_soc_component_write(component, AIC32X4_DACSPB, p_block); + } else { /* AIC32x4 */ + if (r_block > 18 || p_block > 25) + return -EINVAL; + + snd_soc_component_write(component, AIC32X4_ADCSPB, r_block); + snd_soc_component_write(component, AIC32X4_DACSPB, p_block); + } + + return 0; +} + +static int aic32x4_setup_clocks(struct snd_soc_component *component, + unsigned int sample_rate, unsigned int channels, + unsigned int bit_depth) +{ + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + u8 aosr; + u16 dosr; + u8 adc_resource_class, dac_resource_class; + u8 madc, nadc, mdac, ndac, max_nadc, min_mdac, max_ndac; + u8 dosr_increment; + u16 max_dosr, min_dosr; + unsigned long adc_clock_rate, dac_clock_rate; + int ret; + + static struct clk_bulk_data clocks[] = { + { .id = "pll" }, + { .id = "nadc" }, + { .id = "madc" }, + { .id = "ndac" }, + { .id = "mdac" }, + { .id = "bdiv" }, + }; + ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks); + if (ret) + return ret; + + if (sample_rate <= 48000) { + aosr = 128; + adc_resource_class = 6; + dac_resource_class = 8; + dosr_increment = 8; + if (aic32x4->type == AIC32X4_TYPE_TAS2505) + aic32x4_set_processing_blocks(component, 0, 1); + else + aic32x4_set_processing_blocks(component, 1, 1); + } else if (sample_rate <= 96000) { + aosr = 64; + adc_resource_class = 6; + dac_resource_class = 8; + dosr_increment = 4; + if (aic32x4->type == AIC32X4_TYPE_TAS2505) + aic32x4_set_processing_blocks(component, 0, 1); + else + aic32x4_set_processing_blocks(component, 1, 9); + } else if (sample_rate == 192000) { + aosr = 32; + adc_resource_class = 3; + dac_resource_class = 4; + dosr_increment = 2; + if (aic32x4->type == AIC32X4_TYPE_TAS2505) + aic32x4_set_processing_blocks(component, 0, 1); + else + aic32x4_set_processing_blocks(component, 13, 19); + } else { + dev_err(component->dev, "Sampling rate not supported\n"); + return -EINVAL; + } + + /* PCM over I2S is always 2-channel */ + if ((aic32x4->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) + channels = 2; + + madc = DIV_ROUND_UP((32 * adc_resource_class), aosr); + max_dosr = (AIC32X4_MAX_DOSR_FREQ / sample_rate / dosr_increment) * + dosr_increment; + min_dosr = (AIC32X4_MIN_DOSR_FREQ / sample_rate / dosr_increment) * + dosr_increment; + max_nadc = AIC32X4_MAX_CODEC_CLKIN_FREQ / (madc * aosr * sample_rate); + + for (nadc = max_nadc; nadc > 0; --nadc) { + adc_clock_rate = nadc * madc * aosr * sample_rate; + for (dosr = max_dosr; dosr >= min_dosr; + dosr -= dosr_increment) { + min_mdac = DIV_ROUND_UP((32 * dac_resource_class), dosr); + max_ndac = AIC32X4_MAX_CODEC_CLKIN_FREQ / + (min_mdac * dosr * sample_rate); + for (mdac = min_mdac; mdac <= 128; ++mdac) { + for (ndac = max_ndac; ndac > 0; --ndac) { + dac_clock_rate = ndac * mdac * dosr * + sample_rate; + if (dac_clock_rate == adc_clock_rate) { + if (clk_round_rate(clocks[0].clk, dac_clock_rate) == 0) + continue; + + clk_set_rate(clocks[0].clk, + dac_clock_rate); + + clk_set_rate(clocks[1].clk, + sample_rate * aosr * + madc); + clk_set_rate(clocks[2].clk, + sample_rate * aosr); + aic32x4_set_aosr(component, + aosr); + + clk_set_rate(clocks[3].clk, + sample_rate * dosr * + mdac); + clk_set_rate(clocks[4].clk, + sample_rate * dosr); + aic32x4_set_dosr(component, + dosr); + + clk_set_rate(clocks[5].clk, + sample_rate * channels * + bit_depth); + + return 0; + } + } + } + } + } + + dev_err(component->dev, + "Could not set clocks to support sample rate.\n"); + return -EINVAL; +} + +static int aic32x4_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + u8 iface1_reg = 0; + u8 dacsetup_reg = 0; + + aic32x4_setup_clocks(component, params_rate(params), + params_channels(params), + params_physical_width(params)); + + switch (params_physical_width(params)) { + case 16: + iface1_reg |= (AIC32X4_WORD_LEN_16BITS << + AIC32X4_IFACE1_DATALEN_SHIFT); + break; + case 20: + iface1_reg |= (AIC32X4_WORD_LEN_20BITS << + AIC32X4_IFACE1_DATALEN_SHIFT); + break; + case 24: + iface1_reg |= (AIC32X4_WORD_LEN_24BITS << + AIC32X4_IFACE1_DATALEN_SHIFT); + break; + case 32: + iface1_reg |= (AIC32X4_WORD_LEN_32BITS << + AIC32X4_IFACE1_DATALEN_SHIFT); + break; + } + snd_soc_component_update_bits(component, AIC32X4_IFACE1, + AIC32X4_IFACE1_DATALEN_MASK, iface1_reg); + + if (params_channels(params) == 1) { + dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2LCHN; + } else { + if (aic32x4->swapdacs) + dacsetup_reg = AIC32X4_RDAC2LCHN | AIC32X4_LDAC2RCHN; + else + dacsetup_reg = AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN; + } + snd_soc_component_update_bits(component, AIC32X4_DACSETUP, + AIC32X4_DAC_CHAN_MASK, dacsetup_reg); + + return 0; +} + +static int aic32x4_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + snd_soc_component_update_bits(component, AIC32X4_DACMUTE, + AIC32X4_MUTEON, mute ? AIC32X4_MUTEON : 0); + + return 0; +} + +static int aic32x4_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int ret; + + static struct clk_bulk_data clocks[] = { + { .id = "madc" }, + { .id = "mdac" }, + { .id = "bdiv" }, + }; + + ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks); + if (ret) + return ret; + + switch (level) { + case SND_SOC_BIAS_ON: + ret = clk_bulk_prepare_enable(ARRAY_SIZE(clocks), clocks); + if (ret) { + dev_err(component->dev, "Failed to enable clocks\n"); + return ret; + } + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* Initial cold start */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + break; + + clk_bulk_disable_unprepare(ARRAY_SIZE(clocks), clocks); + break; + case SND_SOC_BIAS_OFF: + break; + } + return 0; +} + +#define AIC32X4_RATES SNDRV_PCM_RATE_8000_192000 +#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops aic32x4_ops = { + .hw_params = aic32x4_hw_params, + .mute_stream = aic32x4_mute, + .set_fmt = aic32x4_set_dai_fmt, + .set_sysclk = aic32x4_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver aic32x4_dai = { + .name = "tlv320aic32x4-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 8, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .ops = &aic32x4_ops, + .symmetric_rate = 1, +}; + +static void aic32x4_setup_gpios(struct snd_soc_component *component) +{ + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + + /* setup GPIO functions */ + /* MFP1 */ + if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) { + snd_soc_component_write(component, AIC32X4_DINCTL, + aic32x4->setup->gpio_func[0]); + snd_soc_add_component_controls(component, aic32x4_mfp1, + ARRAY_SIZE(aic32x4_mfp1)); + } + + /* MFP2 */ + if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) { + snd_soc_component_write(component, AIC32X4_DOUTCTL, + aic32x4->setup->gpio_func[1]); + snd_soc_add_component_controls(component, aic32x4_mfp2, + ARRAY_SIZE(aic32x4_mfp2)); + } + + /* MFP3 */ + if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) { + snd_soc_component_write(component, AIC32X4_SCLKCTL, + aic32x4->setup->gpio_func[2]); + snd_soc_add_component_controls(component, aic32x4_mfp3, + ARRAY_SIZE(aic32x4_mfp3)); + } + + /* MFP4 */ + if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) { + snd_soc_component_write(component, AIC32X4_MISOCTL, + aic32x4->setup->gpio_func[3]); + snd_soc_add_component_controls(component, aic32x4_mfp4, + ARRAY_SIZE(aic32x4_mfp4)); + } + + /* MFP5 */ + if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) { + snd_soc_component_write(component, AIC32X4_GPIOCTL, + aic32x4->setup->gpio_func[4]); + snd_soc_add_component_controls(component, aic32x4_mfp5, + ARRAY_SIZE(aic32x4_mfp5)); + } +} + +static int aic32x4_component_probe(struct snd_soc_component *component) +{ + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + u32 tmp_reg; + int ret; + + static struct clk_bulk_data clocks[] = { + { .id = "codec_clkin" }, + { .id = "pll" }, + { .id = "bdiv" }, + { .id = "mdac" }, + }; + + ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks); + if (ret) + return ret; + + if (aic32x4->setup) + aic32x4_setup_gpios(component); + + clk_set_parent(clocks[0].clk, clocks[1].clk); + clk_set_parent(clocks[2].clk, clocks[3].clk); + + /* Power platform configuration */ + if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { + snd_soc_component_write(component, AIC32X4_MICBIAS, + AIC32X4_MICBIAS_LDOIN | AIC32X4_MICBIAS_2075V); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) + snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); + + tmp_reg = (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) ? + AIC32X4_LDOCTLEN : 0; + snd_soc_component_write(component, AIC32X4_LDOCTL, tmp_reg); + + tmp_reg = snd_soc_component_read(component, AIC32X4_CMMODE); + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) + tmp_reg |= AIC32X4_LDOIN_18_36; + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED) + tmp_reg |= AIC32X4_LDOIN2HP; + snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg); + + /* Mic PGA routing */ + if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) + snd_soc_component_write(component, AIC32X4_LMICPGANIN, + AIC32X4_LMICPGANIN_IN2R_10K); + else + snd_soc_component_write(component, AIC32X4_LMICPGANIN, + AIC32X4_LMICPGANIN_CM1L_10K); + if (aic32x4->micpga_routing & AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) + snd_soc_component_write(component, AIC32X4_RMICPGANIN, + AIC32X4_RMICPGANIN_IN1L_10K); + else + snd_soc_component_write(component, AIC32X4_RMICPGANIN, + AIC32X4_RMICPGANIN_CM1R_10K); + + /* + * Workaround: for an unknown reason, the ADC needs to be powered up + * and down for the first capture to work properly. It seems related to + * a HW BUG or some kind of behavior not documented in the datasheet. + */ + tmp_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP); + snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg | + AIC32X4_LADC_EN | AIC32X4_RADC_EN); + snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg); + + /* + * Enable the fast charging feature and ensure the needed 40ms ellapsed + * before using the analog circuits. + */ + snd_soc_component_write(component, AIC32X4_REFPOWERUP, + AIC32X4_REFPOWERUP_40MS); + msleep(40); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_aic32x4 = { + .probe = aic32x4_component_probe, + .set_bias_level = aic32x4_set_bias_level, + .controls = aic32x4_snd_controls, + .num_controls = ARRAY_SIZE(aic32x4_snd_controls), + .dapm_widgets = aic32x4_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), + .dapm_routes = aic32x4_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct snd_kcontrol_new aic32x4_tas2505_snd_controls[] = { + SOC_SINGLE_S8_TLV("PCM Playback Volume", + AIC32X4_LDACVOL, -0x7f, 0x30, tlv_pcm), + SOC_ENUM("DAC Playback PowerTune Switch", l_ptm_enum), + + SOC_SINGLE_TLV("HP Driver Gain Volume", + AIC32X4_HPLGAIN, 0, 0x74, 1, tlv_tas_driver_gain), + SOC_SINGLE("HP DAC Playback Switch", AIC32X4_HPLGAIN, 6, 1, 1), + + SOC_SINGLE_TLV("Speaker Driver Playback Volume", + TAS2505_SPKVOL1, 0, 0x74, 1, tlv_tas_driver_gain), + SOC_SINGLE_TLV("Speaker Amplifier Playback Volume", + TAS2505_SPKVOL2, 4, 5, 0, tlv_amp_vol), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), +}; + +static const struct snd_kcontrol_new hp_output_mixer_controls[] = { + SOC_DAPM_SINGLE("DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0), +}; + +static const struct snd_soc_dapm_widget aic32x4_tas2505_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", AIC32X4_DACSETUP, 7, 0), + SND_SOC_DAPM_MIXER("HP Output Mixer", SND_SOC_NOPM, 0, 0, + &hp_output_mixer_controls[0], + ARRAY_SIZE(hp_output_mixer_controls)), + SND_SOC_DAPM_PGA("HP Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Speaker Driver", TAS2505_SPK, 1, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("Speaker"), +}; + +static const struct snd_soc_dapm_route aic32x4_tas2505_dapm_routes[] = { + /* Left Output */ + {"HP Output Mixer", "DAC Switch", "DAC"}, + + {"HP Power", NULL, "HP Output Mixer"}, + {"HP", NULL, "HP Power"}, + + {"Speaker Driver", NULL, "DAC"}, + {"Speaker", NULL, "Speaker Driver"}, +}; + +static struct snd_soc_dai_driver aic32x4_tas2505_dai = { + .name = "tas2505-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = AIC32X4_FORMATS,}, + .ops = &aic32x4_ops, + .symmetric_rate = 1, +}; + +static int aic32x4_tas2505_component_probe(struct snd_soc_component *component) +{ + struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component); + u32 tmp_reg; + int ret; + + static struct clk_bulk_data clocks[] = { + { .id = "codec_clkin" }, + { .id = "pll" }, + { .id = "bdiv" }, + { .id = "mdac" }, + }; + + ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks); + if (ret) + return ret; + + if (aic32x4->setup) + aic32x4_setup_gpios(component); + + clk_set_parent(clocks[0].clk, clocks[1].clk); + clk_set_parent(clocks[2].clk, clocks[3].clk); + + /* Power platform configuration */ + if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) + snd_soc_component_write(component, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); + + tmp_reg = (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) ? + AIC32X4_LDOCTLEN : 0; + snd_soc_component_write(component, AIC32X4_LDOCTL, tmp_reg); + + tmp_reg = snd_soc_component_read(component, AIC32X4_CMMODE); + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) + tmp_reg |= AIC32X4_LDOIN_18_36; + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED) + tmp_reg |= AIC32X4_LDOIN2HP; + snd_soc_component_write(component, AIC32X4_CMMODE, tmp_reg); + + /* + * Enable the fast charging feature and ensure the needed 40ms ellapsed + * before using the analog circuits. + */ + snd_soc_component_write(component, TAS2505_REFPOWERUP, + AIC32X4_REFPOWERUP_40MS); + msleep(40); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_aic32x4_tas2505 = { + .probe = aic32x4_tas2505_component_probe, + .set_bias_level = aic32x4_set_bias_level, + .controls = aic32x4_tas2505_snd_controls, + .num_controls = ARRAY_SIZE(aic32x4_tas2505_snd_controls), + .dapm_widgets = aic32x4_tas2505_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic32x4_tas2505_dapm_widgets), + .dapm_routes = aic32x4_tas2505_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aic32x4_tas2505_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4, + struct device_node *np) +{ + struct aic32x4_setup_data *aic32x4_setup; + int ret; + + aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup), + GFP_KERNEL); + if (!aic32x4_setup) + return -ENOMEM; + + ret = of_property_match_string(np, "clock-names", "mclk"); + if (ret < 0) + return -EINVAL; + aic32x4->mclk_name = of_clk_get_parent_name(np, ret); + + aic32x4->swapdacs = false; + aic32x4->micpga_routing = 0; + aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0); + + if (of_property_read_u32_array(np, "aic32x4-gpio-func", + aic32x4_setup->gpio_func, 5) >= 0) + aic32x4->setup = aic32x4_setup; + return 0; +} + +static void aic32x4_disable_regulators(struct aic32x4_priv *aic32x4) +{ + regulator_disable(aic32x4->supply_iov); + + if (!IS_ERR(aic32x4->supply_ldo)) + regulator_disable(aic32x4->supply_ldo); + + if (!IS_ERR(aic32x4->supply_dv)) + regulator_disable(aic32x4->supply_dv); + + if (!IS_ERR(aic32x4->supply_av)) + regulator_disable(aic32x4->supply_av); +} + +static int aic32x4_setup_regulators(struct device *dev, + struct aic32x4_priv *aic32x4) +{ + int ret = 0; + + aic32x4->supply_ldo = devm_regulator_get_optional(dev, "ldoin"); + aic32x4->supply_iov = devm_regulator_get(dev, "iov"); + aic32x4->supply_dv = devm_regulator_get_optional(dev, "dv"); + aic32x4->supply_av = devm_regulator_get_optional(dev, "av"); + + /* Check if the regulator requirements are fulfilled */ + + if (IS_ERR(aic32x4->supply_iov)) { + dev_err(dev, "Missing supply 'iov'\n"); + return PTR_ERR(aic32x4->supply_iov); + } + + if (IS_ERR(aic32x4->supply_ldo)) { + if (PTR_ERR(aic32x4->supply_ldo) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + if (IS_ERR(aic32x4->supply_dv)) { + dev_err(dev, "Missing supply 'dv' or 'ldoin'\n"); + return PTR_ERR(aic32x4->supply_dv); + } + if (IS_ERR(aic32x4->supply_av)) { + dev_err(dev, "Missing supply 'av' or 'ldoin'\n"); + return PTR_ERR(aic32x4->supply_av); + } + } else { + if (PTR_ERR(aic32x4->supply_dv) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (PTR_ERR(aic32x4->supply_av) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } + + ret = regulator_enable(aic32x4->supply_iov); + if (ret) { + dev_err(dev, "Failed to enable regulator iov\n"); + return ret; + } + + if (!IS_ERR(aic32x4->supply_ldo)) { + ret = regulator_enable(aic32x4->supply_ldo); + if (ret) { + dev_err(dev, "Failed to enable regulator ldo\n"); + goto error_ldo; + } + } + + if (!IS_ERR(aic32x4->supply_dv)) { + ret = regulator_enable(aic32x4->supply_dv); + if (ret) { + dev_err(dev, "Failed to enable regulator dv\n"); + goto error_dv; + } + } + + if (!IS_ERR(aic32x4->supply_av)) { + ret = regulator_enable(aic32x4->supply_av); + if (ret) { + dev_err(dev, "Failed to enable regulator av\n"); + goto error_av; + } + } + + if (!IS_ERR(aic32x4->supply_ldo) && IS_ERR(aic32x4->supply_av)) + aic32x4->power_cfg |= AIC32X4_PWR_AIC32X4_LDO_ENABLE; + + return 0; + +error_av: + if (!IS_ERR(aic32x4->supply_dv)) + regulator_disable(aic32x4->supply_dv); + +error_dv: + if (!IS_ERR(aic32x4->supply_ldo)) + regulator_disable(aic32x4->supply_ldo); + +error_ldo: + regulator_disable(aic32x4->supply_iov); + return ret; +} + +int aic32x4_probe(struct device *dev, struct regmap *regmap) +{ + struct aic32x4_priv *aic32x4; + struct aic32x4_pdata *pdata = dev->platform_data; + struct device_node *np = dev->of_node; + int ret; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + aic32x4 = devm_kzalloc(dev, sizeof(struct aic32x4_priv), + GFP_KERNEL); + if (aic32x4 == NULL) + return -ENOMEM; + + aic32x4->dev = dev; + aic32x4->type = (uintptr_t)dev_get_drvdata(dev); + + dev_set_drvdata(dev, aic32x4); + + if (pdata) { + aic32x4->power_cfg = pdata->power_cfg; + aic32x4->swapdacs = pdata->swapdacs; + aic32x4->micpga_routing = pdata->micpga_routing; + aic32x4->rstn_gpio = pdata->rstn_gpio; + aic32x4->mclk_name = "mclk"; + } else if (np) { + ret = aic32x4_parse_dt(aic32x4, np); + if (ret) { + dev_err(dev, "Failed to parse DT node\n"); + return ret; + } + } else { + aic32x4->power_cfg = 0; + aic32x4->swapdacs = false; + aic32x4->micpga_routing = 0; + aic32x4->rstn_gpio = -1; + aic32x4->mclk_name = "mclk"; + } + + if (gpio_is_valid(aic32x4->rstn_gpio)) { + ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio, + GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn"); + if (ret != 0) + return ret; + } + + ret = aic32x4_setup_regulators(dev, aic32x4); + if (ret) { + dev_err(dev, "Failed to setup regulators\n"); + return ret; + } + + if (gpio_is_valid(aic32x4->rstn_gpio)) { + ndelay(10); + gpio_set_value_cansleep(aic32x4->rstn_gpio, 1); + mdelay(1); + } + + ret = regmap_write(regmap, AIC32X4_RESET, 0x01); + if (ret) + goto err_disable_regulators; + + ret = aic32x4_register_clocks(dev, aic32x4->mclk_name); + if (ret) + goto err_disable_regulators; + + switch (aic32x4->type) { + case AIC32X4_TYPE_TAS2505: + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_aic32x4_tas2505, &aic32x4_tas2505_dai, 1); + break; + default: + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_aic32x4, &aic32x4_dai, 1); + } + + if (ret) { + dev_err(dev, "Failed to register component\n"); + goto err_disable_regulators; + } + + return 0; + +err_disable_regulators: + aic32x4_disable_regulators(aic32x4); + + return ret; +} +EXPORT_SYMBOL(aic32x4_probe); + +void aic32x4_remove(struct device *dev) +{ + struct aic32x4_priv *aic32x4 = dev_get_drvdata(dev); + + aic32x4_disable_regulators(aic32x4); +} +EXPORT_SYMBOL(aic32x4_remove); + +MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver"); +MODULE_AUTHOR("Javier Martin "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h new file mode 100644 index 0000000000..d6101ce73f --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -0,0 +1,236 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tlv320aic32x4.h + */ + + +#ifndef _TLV320AIC32X4_H +#define _TLV320AIC32X4_H + +struct device; +struct regmap_config; + +enum aic32x4_type { + AIC32X4_TYPE_AIC32X4 = 0, + AIC32X4_TYPE_AIC32X6, + AIC32X4_TYPE_TAS2505, +}; + +extern const struct regmap_config aic32x4_regmap_config; +int aic32x4_probe(struct device *dev, struct regmap *regmap); +void aic32x4_remove(struct device *dev); +int aic32x4_register_clocks(struct device *dev, const char *mclk_name); + +/* tlv320aic32x4 register space (in decimal to match datasheet) */ + +#define AIC32X4_REG(page, reg) ((page * 128) + reg) + +#define AIC32X4_PSEL AIC32X4_REG(0, 0) + +#define AIC32X4_RESET AIC32X4_REG(0, 1) +#define AIC32X4_CLKMUX AIC32X4_REG(0, 4) +#define AIC32X4_PLLPR AIC32X4_REG(0, 5) +#define AIC32X4_PLLJ AIC32X4_REG(0, 6) +#define AIC32X4_PLLDMSB AIC32X4_REG(0, 7) +#define AIC32X4_PLLDLSB AIC32X4_REG(0, 8) +#define AIC32X4_NDAC AIC32X4_REG(0, 11) +#define AIC32X4_MDAC AIC32X4_REG(0, 12) +#define AIC32X4_DOSRMSB AIC32X4_REG(0, 13) +#define AIC32X4_DOSRLSB AIC32X4_REG(0, 14) +#define AIC32X4_NADC AIC32X4_REG(0, 18) +#define AIC32X4_MADC AIC32X4_REG(0, 19) +#define AIC32X4_AOSR AIC32X4_REG(0, 20) +#define AIC32X4_CLKMUX2 AIC32X4_REG(0, 25) +#define AIC32X4_CLKOUTM AIC32X4_REG(0, 26) +#define AIC32X4_IFACE1 AIC32X4_REG(0, 27) +#define AIC32X4_IFACE2 AIC32X4_REG(0, 28) +#define AIC32X4_IFACE3 AIC32X4_REG(0, 29) +#define AIC32X4_BCLKN AIC32X4_REG(0, 30) +#define AIC32X4_IFACE4 AIC32X4_REG(0, 31) +#define AIC32X4_IFACE5 AIC32X4_REG(0, 32) +#define AIC32X4_IFACE6 AIC32X4_REG(0, 33) +#define AIC32X4_GPIOCTL AIC32X4_REG(0, 52) +#define AIC32X4_DOUTCTL AIC32X4_REG(0, 53) +#define AIC32X4_DINCTL AIC32X4_REG(0, 54) +#define AIC32X4_MISOCTL AIC32X4_REG(0, 55) +#define AIC32X4_SCLKCTL AIC32X4_REG(0, 56) +#define AIC32X4_DACSPB AIC32X4_REG(0, 60) +#define AIC32X4_ADCSPB AIC32X4_REG(0, 61) +#define AIC32X4_DACSETUP AIC32X4_REG(0, 63) +#define AIC32X4_DACMUTE AIC32X4_REG(0, 64) +#define AIC32X4_LDACVOL AIC32X4_REG(0, 65) +#define AIC32X4_RDACVOL AIC32X4_REG(0, 66) +#define AIC32X4_ADCSETUP AIC32X4_REG(0, 81) +#define AIC32X4_ADCFGA AIC32X4_REG(0, 82) +#define AIC32X4_LADCVOL AIC32X4_REG(0, 83) +#define AIC32X4_RADCVOL AIC32X4_REG(0, 84) +#define AIC32X4_LAGC1 AIC32X4_REG(0, 86) +#define AIC32X4_LAGC2 AIC32X4_REG(0, 87) +#define AIC32X4_LAGC3 AIC32X4_REG(0, 88) +#define AIC32X4_LAGC4 AIC32X4_REG(0, 89) +#define AIC32X4_LAGC5 AIC32X4_REG(0, 90) +#define AIC32X4_LAGC6 AIC32X4_REG(0, 91) +#define AIC32X4_LAGC7 AIC32X4_REG(0, 92) +#define AIC32X4_RAGC1 AIC32X4_REG(0, 94) +#define AIC32X4_RAGC2 AIC32X4_REG(0, 95) +#define AIC32X4_RAGC3 AIC32X4_REG(0, 96) +#define AIC32X4_RAGC4 AIC32X4_REG(0, 97) +#define AIC32X4_RAGC5 AIC32X4_REG(0, 98) +#define AIC32X4_RAGC6 AIC32X4_REG(0, 99) +#define AIC32X4_RAGC7 AIC32X4_REG(0, 100) + +#define AIC32X4_PWRCFG AIC32X4_REG(1, 1) +#define AIC32X4_LDOCTL AIC32X4_REG(1, 2) +#define AIC32X4_LPLAYBACK AIC32X4_REG(1, 3) +#define AIC32X4_RPLAYBACK AIC32X4_REG(1, 4) +#define AIC32X4_OUTPWRCTL AIC32X4_REG(1, 9) +#define AIC32X4_CMMODE AIC32X4_REG(1, 10) +#define AIC32X4_HPLROUTE AIC32X4_REG(1, 12) +#define AIC32X4_HPRROUTE AIC32X4_REG(1, 13) +#define AIC32X4_LOLROUTE AIC32X4_REG(1, 14) +#define AIC32X4_LORROUTE AIC32X4_REG(1, 15) +#define AIC32X4_HPLGAIN AIC32X4_REG(1, 16) +#define AIC32X4_HPRGAIN AIC32X4_REG(1, 17) +#define AIC32X4_LOLGAIN AIC32X4_REG(1, 18) +#define AIC32X4_LORGAIN AIC32X4_REG(1, 19) +#define AIC32X4_HEADSTART AIC32X4_REG(1, 20) +#define TAS2505_SPK AIC32X4_REG(1, 45) +#define TAS2505_SPKVOL1 AIC32X4_REG(1, 46) +#define TAS2505_SPKVOL2 AIC32X4_REG(1, 48) +#define AIC32X4_MICBIAS AIC32X4_REG(1, 51) +#define AIC32X4_LMICPGAPIN AIC32X4_REG(1, 52) +#define AIC32X4_LMICPGANIN AIC32X4_REG(1, 54) +#define AIC32X4_RMICPGAPIN AIC32X4_REG(1, 55) +#define AIC32X4_RMICPGANIN AIC32X4_REG(1, 57) +#define AIC32X4_FLOATINGINPUT AIC32X4_REG(1, 58) +#define AIC32X4_LMICPGAVOL AIC32X4_REG(1, 59) +#define AIC32X4_RMICPGAVOL AIC32X4_REG(1, 60) +#define TAS2505_REFPOWERUP AIC32X4_REG(1, 122) +#define AIC32X4_REFPOWERUP AIC32X4_REG(1, 123) + +/* Bits, masks, and shifts */ + +/* AIC32X4_CLKMUX */ +#define AIC32X4_PLL_CLKIN_MASK GENMASK(3, 2) +#define AIC32X4_PLL_CLKIN_SHIFT (2) +#define AIC32X4_PLL_CLKIN_MCLK (0x00) +#define AIC32X4_PLL_CLKIN_BCKL (0x01) +#define AIC32X4_PLL_CLKIN_GPIO1 (0x02) +#define AIC32X4_PLL_CLKIN_DIN (0x03) +#define AIC32X4_CODEC_CLKIN_MASK GENMASK(1, 0) +#define AIC32X4_CODEC_CLKIN_SHIFT (0) +#define AIC32X4_CODEC_CLKIN_MCLK (0x00) +#define AIC32X4_CODEC_CLKIN_BCLK (0x01) +#define AIC32X4_CODEC_CLKIN_GPIO1 (0x02) +#define AIC32X4_CODEC_CLKIN_PLL (0x03) + +/* AIC32X4_PLLPR */ +#define AIC32X4_PLLEN BIT(7) +#define AIC32X4_PLL_P_MASK GENMASK(6, 4) +#define AIC32X4_PLL_P_SHIFT (4) +#define AIC32X4_PLL_R_MASK GENMASK(3, 0) + +/* AIC32X4_NDAC */ +#define AIC32X4_NDACEN BIT(7) +#define AIC32X4_NDAC_MASK GENMASK(6, 0) + +/* AIC32X4_MDAC */ +#define AIC32X4_MDACEN BIT(7) +#define AIC32X4_MDAC_MASK GENMASK(6, 0) + +/* AIC32X4_NADC */ +#define AIC32X4_NADCEN BIT(7) +#define AIC32X4_NADC_MASK GENMASK(6, 0) + +/* AIC32X4_MADC */ +#define AIC32X4_MADCEN BIT(7) +#define AIC32X4_MADC_MASK GENMASK(6, 0) + +/* AIC32X4_BCLKN */ +#define AIC32X4_BCLKEN BIT(7) +#define AIC32X4_BCLK_MASK GENMASK(6, 0) + +/* AIC32X4_IFACE1 */ +#define AIC32X4_IFACE1_DATATYPE_MASK GENMASK(7, 6) +#define AIC32X4_IFACE1_DATATYPE_SHIFT (6) +#define AIC32X4_I2S_MODE (0x00) +#define AIC32X4_DSP_MODE (0x01) +#define AIC32X4_RIGHT_JUSTIFIED_MODE (0x02) +#define AIC32X4_LEFT_JUSTIFIED_MODE (0x03) +#define AIC32X4_IFACE1_DATALEN_MASK GENMASK(5, 4) +#define AIC32X4_IFACE1_DATALEN_SHIFT (4) +#define AIC32X4_WORD_LEN_16BITS (0x00) +#define AIC32X4_WORD_LEN_20BITS (0x01) +#define AIC32X4_WORD_LEN_24BITS (0x02) +#define AIC32X4_WORD_LEN_32BITS (0x03) +#define AIC32X4_IFACE1_MASTER_MASK GENMASK(3, 2) +#define AIC32X4_BCLKMASTER BIT(2) +#define AIC32X4_WCLKMASTER BIT(3) + +/* AIC32X4_IFACE2 */ +#define AIC32X4_DATA_OFFSET_MASK GENMASK(7, 0) + +/* AIC32X4_IFACE3 */ +#define AIC32X4_BCLKINV_MASK BIT(3) +#define AIC32X4_BDIVCLK_MASK GENMASK(1, 0) +#define AIC32X4_BDIVCLK_SHIFT (0) +#define AIC32X4_DAC2BCLK (0x00) +#define AIC32X4_DACMOD2BCLK (0x01) +#define AIC32X4_ADC2BCLK (0x02) +#define AIC32X4_ADCMOD2BCLK (0x03) + +/* AIC32X4_DACSETUP */ +#define AIC32X4_DAC_CHAN_MASK GENMASK(5, 2) +#define AIC32X4_LDAC2RCHN BIT(5) +#define AIC32X4_LDAC2LCHN BIT(4) +#define AIC32X4_RDAC2LCHN BIT(3) +#define AIC32X4_RDAC2RCHN BIT(2) + +/* AIC32X4_DACMUTE */ +#define AIC32X4_MUTEON 0x0C + +/* AIC32X4_ADCSETUP */ +#define AIC32X4_LADC_EN BIT(7) +#define AIC32X4_RADC_EN BIT(6) + +/* AIC32X4_PWRCFG */ +#define AIC32X4_AVDDWEAKDISABLE BIT(3) + +/* AIC32X4_LDOCTL */ +#define AIC32X4_LDOCTLEN BIT(0) + +/* AIC32X4_CMMODE */ +#define AIC32X4_LDOIN_18_36 BIT(0) +#define AIC32X4_LDOIN2HP BIT(1) + +/* AIC32X4_MICBIAS */ +#define AIC32X4_MICBIAS_LDOIN BIT(3) +#define AIC32X4_MICBIAS_2075V 0x60 +#define AIC32x4_MICBIAS_MASK GENMASK(6, 3) + +/* AIC32X4_LMICPGANIN */ +#define AIC32X4_LMICPGANIN_IN2R_10K 0x10 +#define AIC32X4_LMICPGANIN_CM1L_10K 0x40 + +/* AIC32X4_RMICPGANIN */ +#define AIC32X4_RMICPGANIN_IN1L_10K 0x10 +#define AIC32X4_RMICPGANIN_CM1R_10K 0x40 + +/* AIC32X4_REFPOWERUP */ +#define AIC32X4_REFPOWERUP_SLOW 0x04 +#define AIC32X4_REFPOWERUP_40MS 0x05 +#define AIC32X4_REFPOWERUP_80MS 0x06 +#define AIC32X4_REFPOWERUP_120MS 0x07 + +/* Common mask and enable for all of the dividers */ +#define AIC32X4_DIVEN BIT(7) +#define AIC32X4_DIV_MASK GENMASK(6, 0) +#define AIC32X4_DIV_MAX 128 + +/* Clock Limits */ +#define AIC32X4_MAX_DOSR_FREQ 6200000 +#define AIC32X4_MIN_DOSR_FREQ 2800000 +#define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000 +#define AIC32X4_MAX_PLL_CLKIN 20000000 + +#endif /* _TLV320AIC32X4_H */ diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c new file mode 100644 index 0000000000..bb33fd3dfb --- /dev/null +++ b/sound/soc/codecs/tlv320aic3x-i2c.c @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * ALSA SoC TLV320AIC3x codec driver I2C interface + * + * Author: Arun KS, + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., + * + * Based on sound/soc/codecs/wm8731.c by Richard Purdie + * + */ + +#include +#include +#include +#include +#include + +#include "tlv320aic3x.h" + +static const struct i2c_device_id aic3x_i2c_id[] = { + { "tlv320aic3x", AIC3X_MODEL_3X }, + { "tlv320aic33", AIC3X_MODEL_33 }, + { "tlv320aic3007", AIC3X_MODEL_3007 }, + { "tlv320aic3104", AIC3X_MODEL_3104 }, + { "tlv320aic3106", AIC3X_MODEL_3106 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); + +static int aic3x_i2c_probe(struct i2c_client *i2c) +{ + struct regmap *regmap; + struct regmap_config config; + const struct i2c_device_id *id = i2c_match_id(aic3x_i2c_id, i2c); + + config = aic3x_regmap; + config.reg_bits = 8; + config.val_bits = 8; + + regmap = devm_regmap_init_i2c(i2c, &config); + return aic3x_probe(&i2c->dev, regmap, id->driver_data); +} + +static void aic3x_i2c_remove(struct i2c_client *i2c) +{ + aic3x_remove(&i2c->dev); +} + +static const struct of_device_id aic3x_of_id[] = { + { .compatible = "ti,tlv320aic3x", }, + { .compatible = "ti,tlv320aic33" }, + { .compatible = "ti,tlv320aic3007" }, + { .compatible = "ti,tlv320aic3104" }, + { .compatible = "ti,tlv320aic3106" }, + {}, +}; +MODULE_DEVICE_TABLE(of, aic3x_of_id); + +static struct i2c_driver aic3x_i2c_driver = { + .driver = { + .name = "tlv320aic3x", + .of_match_table = aic3x_of_id, + }, + .probe = aic3x_i2c_probe, + .remove = aic3x_i2c_remove, + .id_table = aic3x_i2c_id, +}; + +module_i2c_driver(aic3x_i2c_driver); + +MODULE_DESCRIPTION("ASoC TLV320AIC3x codec driver I2C"); +MODULE_AUTHOR("Arun KS "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic3x-spi.c b/sound/soc/codecs/tlv320aic3x-spi.c new file mode 100644 index 0000000000..deed6ec7e0 --- /dev/null +++ b/sound/soc/codecs/tlv320aic3x-spi.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * ALSA SoC TLV320AIC3x codec driver SPI interface + * + * Author: Arun KS, + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., + * + * Based on sound/soc/codecs/wm8731.c by Richard Purdie + * + */ + +#include +#include +#include +#include +#include + +#include "tlv320aic3x.h" + +static int aic3x_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + struct regmap_config config; + const struct spi_device_id *id = spi_get_device_id(spi); + + config = aic3x_regmap; + config.reg_bits = 7; + config.pad_bits = 1; + config.val_bits = 8; + config.read_flag_mask = 0x01; + + dev_dbg(&spi->dev, "probing tlv320aic3x spi device\n"); + + regmap = devm_regmap_init_spi(spi, &config); + return aic3x_probe(&spi->dev, regmap, id->driver_data); +} + +static void aic3x_spi_remove(struct spi_device *spi) +{ + aic3x_remove(&spi->dev); +} + +static const struct spi_device_id aic3x_spi_id[] = { + { "tlv320aic3x", AIC3X_MODEL_3X }, + { "tlv320aic33", AIC3X_MODEL_33 }, + { "tlv320aic3007", AIC3X_MODEL_3007 }, + { "tlv320aic3104", AIC3X_MODEL_3104 }, + { "tlv320aic3106", AIC3X_MODEL_3106 }, + { } +}; +MODULE_DEVICE_TABLE(spi, aic3x_spi_id); + +static const struct of_device_id aic3x_of_id[] = { + { .compatible = "ti,tlv320aic3x", }, + { .compatible = "ti,tlv320aic33" }, + { .compatible = "ti,tlv320aic3007" }, + { .compatible = "ti,tlv320aic3104" }, + { .compatible = "ti,tlv320aic3106" }, + {}, +}; +MODULE_DEVICE_TABLE(of, aic3x_of_id); + +static struct spi_driver aic3x_spi_driver = { + .driver = { + .name = "tlv320aic3x", + .owner = THIS_MODULE, + .of_match_table = aic3x_of_id, + }, + .probe = aic3x_spi_probe, + .remove = aic3x_spi_remove, + .id_table = aic3x_spi_id, +}; + +module_spi_driver(aic3x_spi_driver); + +MODULE_DESCRIPTION("ASoC TLV320AIC3x codec driver SPI"); +MODULE_AUTHOR("Arun KS "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c new file mode 100644 index 0000000000..56e795a00e --- /dev/null +++ b/sound/soc/codecs/tlv320aic3x.c @@ -0,0 +1,1855 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* ALSA SoC TLV320AIC3X codec driver + * + * Author: Vladimir Barinov, + * Copyright: (C) 2007 MontaVista Software, Inc., + * + * Based on sound/soc/codecs/wm8753.c by Liam Girdwood + * + * Notes: + * The AIC3X is a driver for a low power stereo audio + * codecs aic31, aic32, aic33, aic3007. + * + * It supports full aic33 codec functionality. + * The compatibility with aic32, aic31 and aic3007 is as follows: + * aic32/aic3007 | aic31 + * --------------------------------------- + * MONO_LOUT -> N/A | MONO_LOUT -> N/A + * | IN1L -> LINE1L + * | IN1R -> LINE1R + * | IN2L -> LINE2L + * | IN2R -> LINE2R + * | MIC3L/R -> N/A + * truncated internal functionality in + * accordance with documentation + * --------------------------------------- + * + * Hence the machine layer should disable unsupported inputs/outputs by + * snd_soc_dapm_disable_pin(codec, "MONO_LOUT"), etc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlv320aic3x.h" + +#define AIC3X_NUM_SUPPLIES 4 +static const char *aic3x_supply_names[AIC3X_NUM_SUPPLIES] = { + "IOVDD", /* I/O Voltage */ + "DVDD", /* Digital Core Voltage */ + "AVDD", /* Analog DAC Voltage */ + "DRVDD", /* ADC Analog and Output Driver Voltage */ +}; + +struct aic3x_priv; + +struct aic3x_disable_nb { + struct notifier_block nb; + struct aic3x_priv *aic3x; +}; + +struct aic3x_setup_data { + unsigned int gpio_func[2]; +}; + +/* codec private data */ +struct aic3x_priv { + struct snd_soc_component *component; + struct regmap *regmap; + struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES]; + struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES]; + struct aic3x_setup_data *setup; + unsigned int sysclk; + unsigned int dai_fmt; + unsigned int tdm_delay; + unsigned int slot_width; + int master; + struct gpio_desc *gpio_reset; + bool shared_reset; + int power; + u16 model; + + /* Selects the micbias voltage */ + enum aic3x_micbias_voltage micbias_vg; + /* Output Common-Mode Voltage */ + u8 ocmv; +}; + +static const struct reg_default aic3x_reg[] = { + { 0, 0x00 }, { 1, 0x00 }, { 2, 0x00 }, { 3, 0x10 }, + { 4, 0x04 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 }, + { 8, 0x00 }, { 9, 0x00 }, { 10, 0x00 }, { 11, 0x01 }, + { 12, 0x00 }, { 13, 0x00 }, { 14, 0x00 }, { 15, 0x80 }, + { 16, 0x80 }, { 17, 0xff }, { 18, 0xff }, { 19, 0x78 }, + { 20, 0x78 }, { 21, 0x78 }, { 22, 0x78 }, { 23, 0x78 }, + { 24, 0x78 }, { 25, 0x00 }, { 26, 0x00 }, { 27, 0xfe }, + { 28, 0x00 }, { 29, 0x00 }, { 30, 0xfe }, { 31, 0x00 }, + { 32, 0x18 }, { 33, 0x18 }, { 34, 0x00 }, { 35, 0x00 }, + { 36, 0x00 }, { 37, 0x00 }, { 38, 0x00 }, { 39, 0x00 }, + { 40, 0x00 }, { 41, 0x00 }, { 42, 0x00 }, { 43, 0x80 }, + { 44, 0x80 }, { 45, 0x00 }, { 46, 0x00 }, { 47, 0x00 }, + { 48, 0x00 }, { 49, 0x00 }, { 50, 0x00 }, { 51, 0x04 }, + { 52, 0x00 }, { 53, 0x00 }, { 54, 0x00 }, { 55, 0x00 }, + { 56, 0x00 }, { 57, 0x00 }, { 58, 0x04 }, { 59, 0x00 }, + { 60, 0x00 }, { 61, 0x00 }, { 62, 0x00 }, { 63, 0x00 }, + { 64, 0x00 }, { 65, 0x04 }, { 66, 0x00 }, { 67, 0x00 }, + { 68, 0x00 }, { 69, 0x00 }, { 70, 0x00 }, { 71, 0x00 }, + { 72, 0x04 }, { 73, 0x00 }, { 74, 0x00 }, { 75, 0x00 }, + { 76, 0x00 }, { 77, 0x00 }, { 78, 0x00 }, { 79, 0x00 }, + { 80, 0x00 }, { 81, 0x00 }, { 82, 0x00 }, { 83, 0x00 }, + { 84, 0x00 }, { 85, 0x00 }, { 86, 0x00 }, { 87, 0x00 }, + { 88, 0x00 }, { 89, 0x00 }, { 90, 0x00 }, { 91, 0x00 }, + { 92, 0x00 }, { 93, 0x00 }, { 94, 0x00 }, { 95, 0x00 }, + { 96, 0x00 }, { 97, 0x00 }, { 98, 0x00 }, { 99, 0x00 }, + { 100, 0x00 }, { 101, 0x00 }, { 102, 0x02 }, { 103, 0x00 }, + { 104, 0x00 }, { 105, 0x00 }, { 106, 0x00 }, { 107, 0x00 }, + { 108, 0x00 }, { 109, 0x00 }, +}; + +static bool aic3x_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AIC3X_RESET: + return true; + default: + return false; + } +} + +const struct regmap_config aic3x_regmap = { + .max_register = DAC_ICC_ADJ, + .reg_defaults = aic3x_reg, + .num_reg_defaults = ARRAY_SIZE(aic3x_reg), + + .volatile_reg = aic3x_volatile_reg, + + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(aic3x_regmap); + +#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ + SOC_SINGLE_EXT(xname, reg, shift, mask, invert, \ + snd_soc_dapm_get_volsw, snd_soc_dapm_put_volsw_aic3x) + +/* + * All input lines are connected when !0xf and disconnected with 0xf bit field, + * so we have to use specific dapm_put call for input mixer + */ +static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned short val; + struct snd_soc_dapm_update update = {}; + int connect, change; + + val = (ucontrol->value.integer.value[0] & mask); + + mask = 0xf; + if (val) + val = mask; + + connect = !!val; + + if (invert) + val = mask - val; + + mask <<= shift; + val <<= shift; + + change = snd_soc_component_test_bits(component, reg, mask, val); + if (change) { + update.kcontrol = kcontrol; + update.reg = reg; + update.mask = mask; + update.val = val; + + snd_soc_dapm_mixer_update_power(dapm, kcontrol, connect, + &update); + } + + return change; +} + +/* + * mic bias power on/off share the same register bits with + * output voltage of mic bias. when power on mic bias, we + * need reclaim it to voltage value. + * 0x0 = Powered off + * 0x1 = MICBIAS output is powered to 2.0V, + * 0x2 = MICBIAS output is powered to 2.5V + * 0x3 = MICBIAS output is connected to AVDD + */ +static int mic_bias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* change mic bias voltage to user defined */ + snd_soc_component_update_bits(component, MICBIAS_CTRL, + MICBIAS_LEVEL_MASK, + aic3x->micbias_vg << MICBIAS_LEVEL_SHIFT); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, MICBIAS_CTRL, + MICBIAS_LEVEL_MASK, 0); + break; + } + return 0; +} + +static const char * const aic3x_left_dac_mux[] = { + "DAC_L1", "DAC_L3", "DAC_L2" }; +static SOC_ENUM_SINGLE_DECL(aic3x_left_dac_enum, DAC_LINE_MUX, 6, + aic3x_left_dac_mux); + +static const char * const aic3x_right_dac_mux[] = { + "DAC_R1", "DAC_R3", "DAC_R2" }; +static SOC_ENUM_SINGLE_DECL(aic3x_right_dac_enum, DAC_LINE_MUX, 4, + aic3x_right_dac_mux); + +static const char * const aic3x_left_hpcom_mux[] = { + "differential of HPLOUT", "constant VCM", "single-ended" }; +static SOC_ENUM_SINGLE_DECL(aic3x_left_hpcom_enum, HPLCOM_CFG, 4, + aic3x_left_hpcom_mux); + +static const char * const aic3x_right_hpcom_mux[] = { + "differential of HPROUT", "constant VCM", "single-ended", + "differential of HPLCOM", "external feedback" }; +static SOC_ENUM_SINGLE_DECL(aic3x_right_hpcom_enum, HPRCOM_CFG, 3, + aic3x_right_hpcom_mux); + +static const char * const aic3x_linein_mode_mux[] = { + "single-ended", "differential" }; +static SOC_ENUM_SINGLE_DECL(aic3x_line1l_2_l_enum, LINE1L_2_LADC_CTRL, 7, + aic3x_linein_mode_mux); +static SOC_ENUM_SINGLE_DECL(aic3x_line1l_2_r_enum, LINE1L_2_RADC_CTRL, 7, + aic3x_linein_mode_mux); +static SOC_ENUM_SINGLE_DECL(aic3x_line1r_2_l_enum, LINE1R_2_LADC_CTRL, 7, + aic3x_linein_mode_mux); +static SOC_ENUM_SINGLE_DECL(aic3x_line1r_2_r_enum, LINE1R_2_RADC_CTRL, 7, + aic3x_linein_mode_mux); +static SOC_ENUM_SINGLE_DECL(aic3x_line2l_2_ldac_enum, LINE2L_2_LADC_CTRL, 7, + aic3x_linein_mode_mux); +static SOC_ENUM_SINGLE_DECL(aic3x_line2r_2_rdac_enum, LINE2R_2_RADC_CTRL, 7, + aic3x_linein_mode_mux); + +static const char * const aic3x_adc_hpf[] = { + "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" }; +static SOC_ENUM_DOUBLE_DECL(aic3x_adc_hpf_enum, AIC3X_CODEC_DFILT_CTRL, 6, 4, + aic3x_adc_hpf); + +static const char * const aic3x_agc_level[] = { + "-5.5dB", "-8dB", "-10dB", "-12dB", + "-14dB", "-17dB", "-20dB", "-24dB" }; +static SOC_ENUM_SINGLE_DECL(aic3x_lagc_level_enum, LAGC_CTRL_A, 4, + aic3x_agc_level); +static SOC_ENUM_SINGLE_DECL(aic3x_ragc_level_enum, RAGC_CTRL_A, 4, + aic3x_agc_level); + +static const char * const aic3x_agc_attack[] = { + "8ms", "11ms", "16ms", "20ms" }; +static SOC_ENUM_SINGLE_DECL(aic3x_lagc_attack_enum, LAGC_CTRL_A, 2, + aic3x_agc_attack); +static SOC_ENUM_SINGLE_DECL(aic3x_ragc_attack_enum, RAGC_CTRL_A, 2, + aic3x_agc_attack); + +static const char * const aic3x_agc_decay[] = { + "100ms", "200ms", "400ms", "500ms" }; +static SOC_ENUM_SINGLE_DECL(aic3x_lagc_decay_enum, LAGC_CTRL_A, 0, + aic3x_agc_decay); +static SOC_ENUM_SINGLE_DECL(aic3x_ragc_decay_enum, RAGC_CTRL_A, 0, + aic3x_agc_decay); + +static const char * const aic3x_poweron_time[] = { + "0us", "10us", "100us", "1ms", "10ms", "50ms", + "100ms", "200ms", "400ms", "800ms", "2s", "4s" }; +static SOC_ENUM_SINGLE_DECL(aic3x_poweron_time_enum, HPOUT_POP_REDUCTION, 4, + aic3x_poweron_time); + +static const char * const aic3x_rampup_step[] = { "0ms", "1ms", "2ms", "4ms" }; +static SOC_ENUM_SINGLE_DECL(aic3x_rampup_step_enum, HPOUT_POP_REDUCTION, 2, + aic3x_rampup_step); + +/* + * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, -6350, 50, 0); +/* ADC PGA gain volumes. From 0 to 59.5 dB in 0.5 dB steps */ +static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 50, 0); +/* + * Output stage volumes. From -78.3 to 0 dB. Muted below -78.3 dB. + * Step size is approximately 0.5 dB over most of the scale but increasing + * near the very low levels. + * Define dB scale so that it is mostly correct for range about -55 to 0 dB + * but having increasing dB difference below that (and where it doesn't count + * so much). This setting shows -50 dB (actual is -50.3 dB) for register + * value 100 and -58.5 dB (actual is -78.3 dB) for register value 117. + */ +static DECLARE_TLV_DB_SCALE(output_stage_tlv, -5900, 50, 1); + +/* Output volumes. From 0 to 9 dB in 1 dB steps */ +static const DECLARE_TLV_DB_SCALE(out_tlv, 0, 100, 0); + +static const struct snd_kcontrol_new aic3x_snd_controls[] = { + /* Output */ + SOC_DOUBLE_R_TLV("PCM Playback Volume", + LDAC_VOL, RDAC_VOL, 0, 0x7f, 1, dac_tlv), + + /* + * Output controls that map to output mixer switches. Note these are + * only for swapped L-to-R and R-to-L routes. See below stereo controls + * for direct L-to-L and R-to-R routes. + */ + SOC_SINGLE_TLV("Left Line Mixer PGAR Bypass Volume", + PGAR_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv), + SOC_SINGLE_TLV("Left Line Mixer DACR1 Playback Volume", + DACR1_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Right Line Mixer PGAL Bypass Volume", + PGAL_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv), + SOC_SINGLE_TLV("Right Line Mixer DACL1 Playback Volume", + DACL1_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Left HP Mixer PGAR Bypass Volume", + PGAR_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv), + SOC_SINGLE_TLV("Left HP Mixer DACR1 Playback Volume", + DACR1_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Right HP Mixer PGAL Bypass Volume", + PGAL_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv), + SOC_SINGLE_TLV("Right HP Mixer DACL1 Playback Volume", + DACL1_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Left HPCOM Mixer PGAR Bypass Volume", + PGAR_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv), + SOC_SINGLE_TLV("Left HPCOM Mixer DACR1 Playback Volume", + DACR1_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Right HPCOM Mixer PGAL Bypass Volume", + PGAL_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv), + SOC_SINGLE_TLV("Right HPCOM Mixer DACL1 Playback Volume", + DACL1_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv), + + /* Stereo output controls for direct L-to-L and R-to-R routes */ + SOC_DOUBLE_R_TLV("Line PGA Bypass Volume", + PGAL_2_LLOPM_VOL, PGAR_2_RLOPM_VOL, + 0, 118, 1, output_stage_tlv), + SOC_DOUBLE_R_TLV("Line DAC Playback Volume", + DACL1_2_LLOPM_VOL, DACR1_2_RLOPM_VOL, + 0, 118, 1, output_stage_tlv), + + SOC_DOUBLE_R_TLV("HP PGA Bypass Volume", + PGAL_2_HPLOUT_VOL, PGAR_2_HPROUT_VOL, + 0, 118, 1, output_stage_tlv), + SOC_DOUBLE_R_TLV("HP DAC Playback Volume", + DACL1_2_HPLOUT_VOL, DACR1_2_HPROUT_VOL, + 0, 118, 1, output_stage_tlv), + + SOC_DOUBLE_R_TLV("HPCOM PGA Bypass Volume", + PGAL_2_HPLCOM_VOL, PGAR_2_HPRCOM_VOL, + 0, 118, 1, output_stage_tlv), + SOC_DOUBLE_R_TLV("HPCOM DAC Playback Volume", + DACL1_2_HPLCOM_VOL, DACR1_2_HPRCOM_VOL, + 0, 118, 1, output_stage_tlv), + + /* Output pin controls */ + SOC_DOUBLE_R_TLV("Line Playback Volume", LLOPM_CTRL, RLOPM_CTRL, 4, + 9, 0, out_tlv), + SOC_DOUBLE_R("Line Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3, + 0x01, 0), + SOC_DOUBLE_R_TLV("HP Playback Volume", HPLOUT_CTRL, HPROUT_CTRL, 4, + 9, 0, out_tlv), + SOC_DOUBLE_R("HP Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3, + 0x01, 0), + SOC_DOUBLE_R_TLV("HPCOM Playback Volume", HPLCOM_CTRL, HPRCOM_CTRL, + 4, 9, 0, out_tlv), + SOC_DOUBLE_R("HPCOM Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3, + 0x01, 0), + + /* + * Note: enable Automatic input Gain Controller with care. It can + * adjust PGA to max value when ADC is on and will never go back. + */ + SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0), + SOC_ENUM("Left AGC Target level", aic3x_lagc_level_enum), + SOC_ENUM("Right AGC Target level", aic3x_ragc_level_enum), + SOC_ENUM("Left AGC Attack time", aic3x_lagc_attack_enum), + SOC_ENUM("Right AGC Attack time", aic3x_ragc_attack_enum), + SOC_ENUM("Left AGC Decay time", aic3x_lagc_decay_enum), + SOC_ENUM("Right AGC Decay time", aic3x_ragc_decay_enum), + + /* De-emphasis */ + SOC_DOUBLE("De-emphasis Switch", AIC3X_CODEC_DFILT_CTRL, 2, 0, 0x01, 0), + + /* Input */ + SOC_DOUBLE_R_TLV("PGA Capture Volume", LADC_VOL, RADC_VOL, + 0, 119, 0, adc_tlv), + SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1), + + SOC_ENUM("ADC HPF Cut-off", aic3x_adc_hpf_enum), + + /* Pop reduction */ + SOC_ENUM("Output Driver Power-On time", aic3x_poweron_time_enum), + SOC_ENUM("Output Driver Ramp-up step", aic3x_rampup_step_enum), +}; + +/* For other than tlv320aic3104 */ +static const struct snd_kcontrol_new aic3x_extra_snd_controls[] = { + /* + * Output controls that map to output mixer switches. Note these are + * only for swapped L-to-R and R-to-L routes. See below stereo controls + * for direct L-to-L and R-to-R routes. + */ + SOC_SINGLE_TLV("Left Line Mixer Line2R Bypass Volume", + LINE2R_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Right Line Mixer Line2L Bypass Volume", + LINE2L_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Left HP Mixer Line2R Bypass Volume", + LINE2R_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Right HP Mixer Line2L Bypass Volume", + LINE2L_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Left HPCOM Mixer Line2R Bypass Volume", + LINE2R_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Right HPCOM Mixer Line2L Bypass Volume", + LINE2L_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv), + + /* Stereo output controls for direct L-to-L and R-to-R routes */ + SOC_DOUBLE_R_TLV("Line Line2 Bypass Volume", + LINE2L_2_LLOPM_VOL, LINE2R_2_RLOPM_VOL, + 0, 118, 1, output_stage_tlv), + + SOC_DOUBLE_R_TLV("HP Line2 Bypass Volume", + LINE2L_2_HPLOUT_VOL, LINE2R_2_HPROUT_VOL, + 0, 118, 1, output_stage_tlv), + + SOC_DOUBLE_R_TLV("HPCOM Line2 Bypass Volume", + LINE2L_2_HPLCOM_VOL, LINE2R_2_HPRCOM_VOL, + 0, 118, 1, output_stage_tlv), +}; + +static const struct snd_kcontrol_new aic3x_mono_controls[] = { + SOC_DOUBLE_R_TLV("Mono Line2 Bypass Volume", + LINE2L_2_MONOLOPM_VOL, LINE2R_2_MONOLOPM_VOL, + 0, 118, 1, output_stage_tlv), + SOC_DOUBLE_R_TLV("Mono PGA Bypass Volume", + PGAL_2_MONOLOPM_VOL, PGAR_2_MONOLOPM_VOL, + 0, 118, 1, output_stage_tlv), + SOC_DOUBLE_R_TLV("Mono DAC Playback Volume", + DACL1_2_MONOLOPM_VOL, DACR1_2_MONOLOPM_VOL, + 0, 118, 1, output_stage_tlv), + + SOC_SINGLE("Mono Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0), + SOC_SINGLE_TLV("Mono Playback Volume", MONOLOPM_CTRL, 4, 9, 0, + out_tlv), + +}; + +/* + * Class-D amplifier gain. From 0 to 18 dB in 6 dB steps + */ +static DECLARE_TLV_DB_SCALE(classd_amp_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl = + SOC_DOUBLE_TLV("Class-D Playback Volume", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv); + +/* Left DAC Mux */ +static const struct snd_kcontrol_new aic3x_left_dac_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_left_dac_enum); + +/* Right DAC Mux */ +static const struct snd_kcontrol_new aic3x_right_dac_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_right_dac_enum); + +/* Left HPCOM Mux */ +static const struct snd_kcontrol_new aic3x_left_hpcom_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_left_hpcom_enum); + +/* Right HPCOM Mux */ +static const struct snd_kcontrol_new aic3x_right_hpcom_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_right_hpcom_enum); + +/* Left Line Mixer */ +static const struct snd_kcontrol_new aic3x_left_line_mixer_controls[] = { + SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_LLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_LLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_LLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_LLOPM_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_LLOPM_VOL, 7, 1, 0), +}; + +/* Right Line Mixer */ +static const struct snd_kcontrol_new aic3x_right_line_mixer_controls[] = { + SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_RLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_RLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_RLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_RLOPM_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_RLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new aic3x_mono_mixer_controls[] = { + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0), +}; + +/* Left HP Mixer */ +static const struct snd_kcontrol_new aic3x_left_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPLOUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPLOUT_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPLOUT_VOL, 7, 1, 0), +}; + +/* Right HP Mixer */ +static const struct snd_kcontrol_new aic3x_right_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPROUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPROUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPROUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPROUT_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPROUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0), +}; + +/* Left HPCOM Mixer */ +static const struct snd_kcontrol_new aic3x_left_hpcom_mixer_controls[] = { + SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPLCOM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPLCOM_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPLCOM_VOL, 7, 1, 0), +}; + +/* Right HPCOM Mixer */ +static const struct snd_kcontrol_new aic3x_right_hpcom_mixer_controls[] = { + SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPRCOM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPRCOM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPRCOM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0), +}; + +/* Left PGA Mixer */ +static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = { + SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_LADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_LADC_CTRL, 0, 1, 1), +}; + +/* Right PGA Mixer */ +static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = { + SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_RADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_RADC_CTRL, 4, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1), +}; + +/* Left PGA Mixer for tlv320aic3104 */ +static const struct snd_kcontrol_new aic3104_left_pga_mixer_controls[] = { + SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_LADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic2L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic2R Switch", MIC3LR_2_LADC_CTRL, 0, 1, 1), +}; + +/* Right PGA Mixer for tlv320aic3104 */ +static const struct snd_kcontrol_new aic3104_right_pga_mixer_controls[] = { + SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_RADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic2L Switch", MIC3LR_2_RADC_CTRL, 4, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic2R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1), +}; + +/* Left Line1 Mux */ +static const struct snd_kcontrol_new aic3x_left_line1l_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_line1l_2_l_enum); +static const struct snd_kcontrol_new aic3x_right_line1l_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_line1l_2_r_enum); + +/* Right Line1 Mux */ +static const struct snd_kcontrol_new aic3x_right_line1r_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_line1r_2_r_enum); +static const struct snd_kcontrol_new aic3x_left_line1r_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_line1r_2_l_enum); + +/* Left Line2 Mux */ +static const struct snd_kcontrol_new aic3x_left_line2_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_line2l_2_ldac_enum); + +/* Right Line2 Mux */ +static const struct snd_kcontrol_new aic3x_right_line2_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_line2r_2_rdac_enum); + +static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { + /* Left DAC to Left Outputs */ + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", DAC_PWR, 7, 0), + SND_SOC_DAPM_MUX("Left DAC Mux", SND_SOC_NOPM, 0, 0, + &aic3x_left_dac_mux_controls), + SND_SOC_DAPM_MUX("Left HPCOM Mux", SND_SOC_NOPM, 0, 0, + &aic3x_left_hpcom_mux_controls), + SND_SOC_DAPM_PGA("Left Line Out", LLOPM_CTRL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left HP Out", HPLOUT_CTRL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left HP Com", HPLCOM_CTRL, 0, 0, NULL, 0), + + /* Right DAC to Right Outputs */ + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", DAC_PWR, 6, 0), + SND_SOC_DAPM_MUX("Right DAC Mux", SND_SOC_NOPM, 0, 0, + &aic3x_right_dac_mux_controls), + SND_SOC_DAPM_MUX("Right HPCOM Mux", SND_SOC_NOPM, 0, 0, + &aic3x_right_hpcom_mux_controls), + SND_SOC_DAPM_PGA("Right Line Out", RLOPM_CTRL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right HP Out", HPROUT_CTRL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right HP Com", HPRCOM_CTRL, 0, 0, NULL, 0), + + /* Inputs to Left ADC */ + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0), + SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0, + &aic3x_left_line1l_mux_controls), + SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0, + &aic3x_left_line1r_mux_controls), + + /* Inputs to Right ADC */ + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", + LINE1R_2_RADC_CTRL, 2, 0), + SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0, + &aic3x_right_line1l_mux_controls), + SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0, + &aic3x_right_line1r_mux_controls), + + /* Mic Bias */ + SND_SOC_DAPM_SUPPLY("Mic Bias", MICBIAS_CTRL, 6, 0, + mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("LLOUT"), + SND_SOC_DAPM_OUTPUT("RLOUT"), + SND_SOC_DAPM_OUTPUT("HPLOUT"), + SND_SOC_DAPM_OUTPUT("HPROUT"), + SND_SOC_DAPM_OUTPUT("HPLCOM"), + SND_SOC_DAPM_OUTPUT("HPRCOM"), + + SND_SOC_DAPM_INPUT("LINE1L"), + SND_SOC_DAPM_INPUT("LINE1R"), + + /* + * Virtual output pin to detection block inside codec. This can be + * used to keep codec bias on if gpio or detection features are needed. + * Force pin on or construct a path with an input jack and mic bias + * widgets. + */ + SND_SOC_DAPM_OUTPUT("Detection"), +}; + +/* For other than tlv320aic3104 */ +static const struct snd_soc_dapm_widget aic3x_extra_dapm_widgets[] = { + /* Inputs to Left ADC */ + SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_pga_mixer_controls[0], + ARRAY_SIZE(aic3x_left_pga_mixer_controls)), + SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0, + &aic3x_left_line2_mux_controls), + + /* Inputs to Right ADC */ + SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_pga_mixer_controls[0], + ARRAY_SIZE(aic3x_right_pga_mixer_controls)), + SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0, + &aic3x_right_line2_mux_controls), + + /* + * Not a real mic bias widget but similar function. This is for dynamic + * control of GPIO1 digital mic modulator clock output function when + * using digital mic. + */ + SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "GPIO1 dmic modclk", + AIC3X_GPIO1_REG, 4, 0xf, + AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK, + AIC3X_GPIO1_FUNC_DISABLED), + + /* + * Also similar function like mic bias. Selects digital mic with + * configurable oversampling rate instead of ADC converter. + */ + SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 128", + AIC3X_ASD_INTF_CTRLA, 0, 3, 1, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 64", + AIC3X_ASD_INTF_CTRLA, 0, 3, 2, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 32", + AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0), + + /* Output mixers */ + SND_SOC_DAPM_MIXER("Left Line Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_line_mixer_controls[0], + ARRAY_SIZE(aic3x_left_line_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Line Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_line_mixer_controls[0], + ARRAY_SIZE(aic3x_right_line_mixer_controls)), + SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_hp_mixer_controls[0], + ARRAY_SIZE(aic3x_left_hp_mixer_controls)), + SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_hp_mixer_controls[0], + ARRAY_SIZE(aic3x_right_hp_mixer_controls)), + SND_SOC_DAPM_MIXER("Left HPCOM Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_hpcom_mixer_controls[0], + ARRAY_SIZE(aic3x_left_hpcom_mixer_controls)), + SND_SOC_DAPM_MIXER("Right HPCOM Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_hpcom_mixer_controls[0], + ARRAY_SIZE(aic3x_right_hpcom_mixer_controls)), + + SND_SOC_DAPM_INPUT("MIC3L"), + SND_SOC_DAPM_INPUT("MIC3R"), + SND_SOC_DAPM_INPUT("LINE2L"), + SND_SOC_DAPM_INPUT("LINE2R"), +}; + +/* For tlv320aic3104 */ +static const struct snd_soc_dapm_widget aic3104_extra_dapm_widgets[] = { + /* Inputs to Left ADC */ + SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0, + &aic3104_left_pga_mixer_controls[0], + ARRAY_SIZE(aic3104_left_pga_mixer_controls)), + + /* Inputs to Right ADC */ + SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0, + &aic3104_right_pga_mixer_controls[0], + ARRAY_SIZE(aic3104_right_pga_mixer_controls)), + + /* Output mixers */ + SND_SOC_DAPM_MIXER("Left Line Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_line_mixer_controls[0], + ARRAY_SIZE(aic3x_left_line_mixer_controls) - 2), + SND_SOC_DAPM_MIXER("Right Line Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_line_mixer_controls[0], + ARRAY_SIZE(aic3x_right_line_mixer_controls) - 2), + SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_hp_mixer_controls[0], + ARRAY_SIZE(aic3x_left_hp_mixer_controls) - 2), + SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_hp_mixer_controls[0], + ARRAY_SIZE(aic3x_right_hp_mixer_controls) - 2), + SND_SOC_DAPM_MIXER("Left HPCOM Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_hpcom_mixer_controls[0], + ARRAY_SIZE(aic3x_left_hpcom_mixer_controls) - 2), + SND_SOC_DAPM_MIXER("Right HPCOM Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_hpcom_mixer_controls[0], + ARRAY_SIZE(aic3x_right_hpcom_mixer_controls) - 2), + + SND_SOC_DAPM_INPUT("MIC2L"), + SND_SOC_DAPM_INPUT("MIC2R"), +}; + +static const struct snd_soc_dapm_widget aic3x_dapm_mono_widgets[] = { + /* Mono Output */ + SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_mono_mixer_controls[0], + ARRAY_SIZE(aic3x_mono_mixer_controls)), + + SND_SOC_DAPM_OUTPUT("MONO_LOUT"), +}; + +static const struct snd_soc_dapm_widget aic3007_dapm_widgets[] = { + /* Class-D outputs */ + SND_SOC_DAPM_PGA("Left Class-D Out", CLASSD_CTRL, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Class-D Out", CLASSD_CTRL, 2, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("SPOP"), + SND_SOC_DAPM_OUTPUT("SPOM"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* Left Input */ + {"Left Line1L Mux", "single-ended", "LINE1L"}, + {"Left Line1L Mux", "differential", "LINE1L"}, + {"Left Line1R Mux", "single-ended", "LINE1R"}, + {"Left Line1R Mux", "differential", "LINE1R"}, + + {"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"}, + {"Left PGA Mixer", "Line1R Switch", "Left Line1R Mux"}, + + {"Left ADC", NULL, "Left PGA Mixer"}, + + /* Right Input */ + {"Right Line1R Mux", "single-ended", "LINE1R"}, + {"Right Line1R Mux", "differential", "LINE1R"}, + {"Right Line1L Mux", "single-ended", "LINE1L"}, + {"Right Line1L Mux", "differential", "LINE1L"}, + + {"Right PGA Mixer", "Line1L Switch", "Right Line1L Mux"}, + {"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"}, + + {"Right ADC", NULL, "Right PGA Mixer"}, + + /* Left DAC Output */ + {"Left DAC Mux", "DAC_L1", "Left DAC"}, + {"Left DAC Mux", "DAC_L2", "Left DAC"}, + {"Left DAC Mux", "DAC_L3", "Left DAC"}, + + /* Right DAC Output */ + {"Right DAC Mux", "DAC_R1", "Right DAC"}, + {"Right DAC Mux", "DAC_R2", "Right DAC"}, + {"Right DAC Mux", "DAC_R3", "Right DAC"}, + + /* Left Line Output */ + {"Left Line Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, + {"Left Line Mixer", "DACL1 Switch", "Left DAC Mux"}, + {"Left Line Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, + {"Left Line Mixer", "DACR1 Switch", "Right DAC Mux"}, + + {"Left Line Out", NULL, "Left Line Mixer"}, + {"Left Line Out", NULL, "Left DAC Mux"}, + {"LLOUT", NULL, "Left Line Out"}, + + /* Right Line Output */ + {"Right Line Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, + {"Right Line Mixer", "DACL1 Switch", "Left DAC Mux"}, + {"Right Line Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, + {"Right Line Mixer", "DACR1 Switch", "Right DAC Mux"}, + + {"Right Line Out", NULL, "Right Line Mixer"}, + {"Right Line Out", NULL, "Right DAC Mux"}, + {"RLOUT", NULL, "Right Line Out"}, + + /* Left HP Output */ + {"Left HP Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, + {"Left HP Mixer", "DACL1 Switch", "Left DAC Mux"}, + {"Left HP Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, + {"Left HP Mixer", "DACR1 Switch", "Right DAC Mux"}, + + {"Left HP Out", NULL, "Left HP Mixer"}, + {"Left HP Out", NULL, "Left DAC Mux"}, + {"HPLOUT", NULL, "Left HP Out"}, + + /* Right HP Output */ + {"Right HP Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, + {"Right HP Mixer", "DACL1 Switch", "Left DAC Mux"}, + {"Right HP Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, + {"Right HP Mixer", "DACR1 Switch", "Right DAC Mux"}, + + {"Right HP Out", NULL, "Right HP Mixer"}, + {"Right HP Out", NULL, "Right DAC Mux"}, + {"HPROUT", NULL, "Right HP Out"}, + + /* Left HPCOM Output */ + {"Left HPCOM Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, + {"Left HPCOM Mixer", "DACL1 Switch", "Left DAC Mux"}, + {"Left HPCOM Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, + {"Left HPCOM Mixer", "DACR1 Switch", "Right DAC Mux"}, + + {"Left HPCOM Mux", "differential of HPLOUT", "Left HP Mixer"}, + {"Left HPCOM Mux", "constant VCM", "Left HPCOM Mixer"}, + {"Left HPCOM Mux", "single-ended", "Left HPCOM Mixer"}, + {"Left HP Com", NULL, "Left HPCOM Mux"}, + {"HPLCOM", NULL, "Left HP Com"}, + + /* Right HPCOM Output */ + {"Right HPCOM Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, + {"Right HPCOM Mixer", "DACL1 Switch", "Left DAC Mux"}, + {"Right HPCOM Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, + {"Right HPCOM Mixer", "DACR1 Switch", "Right DAC Mux"}, + + {"Right HPCOM Mux", "differential of HPROUT", "Right HP Mixer"}, + {"Right HPCOM Mux", "constant VCM", "Right HPCOM Mixer"}, + {"Right HPCOM Mux", "single-ended", "Right HPCOM Mixer"}, + {"Right HPCOM Mux", "differential of HPLCOM", "Left HPCOM Mixer"}, + {"Right HPCOM Mux", "external feedback", "Right HPCOM Mixer"}, + {"Right HP Com", NULL, "Right HPCOM Mux"}, + {"HPRCOM", NULL, "Right HP Com"}, +}; + +/* For other than tlv320aic3104 */ +static const struct snd_soc_dapm_route intercon_extra[] = { + /* Left Input */ + {"Left Line2L Mux", "single-ended", "LINE2L"}, + {"Left Line2L Mux", "differential", "LINE2L"}, + + {"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"}, + {"Left PGA Mixer", "Mic3L Switch", "MIC3L"}, + {"Left PGA Mixer", "Mic3R Switch", "MIC3R"}, + + {"Left ADC", NULL, "GPIO1 dmic modclk"}, + + /* Right Input */ + {"Right Line2R Mux", "single-ended", "LINE2R"}, + {"Right Line2R Mux", "differential", "LINE2R"}, + + {"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"}, + {"Right PGA Mixer", "Mic3L Switch", "MIC3L"}, + {"Right PGA Mixer", "Mic3R Switch", "MIC3R"}, + + {"Right ADC", NULL, "GPIO1 dmic modclk"}, + + /* + * Logical path between digital mic enable and GPIO1 modulator clock + * output function + */ + {"GPIO1 dmic modclk", NULL, "DMic Rate 128"}, + {"GPIO1 dmic modclk", NULL, "DMic Rate 64"}, + {"GPIO1 dmic modclk", NULL, "DMic Rate 32"}, + + /* Left Line Output */ + {"Left Line Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Left Line Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + + /* Right Line Output */ + {"Right Line Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Right Line Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + + /* Left HP Output */ + {"Left HP Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Left HP Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + + /* Right HP Output */ + {"Right HP Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Right HP Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + + /* Left HPCOM Output */ + {"Left HPCOM Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Left HPCOM Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + + /* Right HPCOM Output */ + {"Right HPCOM Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Right HPCOM Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, +}; + +/* For tlv320aic3104 */ +static const struct snd_soc_dapm_route intercon_extra_3104[] = { + /* Left Input */ + {"Left PGA Mixer", "Mic2L Switch", "MIC2L"}, + {"Left PGA Mixer", "Mic2R Switch", "MIC2R"}, + + /* Right Input */ + {"Right PGA Mixer", "Mic2L Switch", "MIC2L"}, + {"Right PGA Mixer", "Mic2R Switch", "MIC2R"}, +}; + +static const struct snd_soc_dapm_route intercon_mono[] = { + /* Mono Output */ + {"Mono Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Mono Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, + {"Mono Mixer", "DACL1 Switch", "Left DAC Mux"}, + {"Mono Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + {"Mono Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, + {"Mono Mixer", "DACR1 Switch", "Right DAC Mux"}, + {"Mono Out", NULL, "Mono Mixer"}, + {"MONO_LOUT", NULL, "Mono Out"}, +}; + +static const struct snd_soc_dapm_route intercon_3007[] = { + /* Class-D outputs */ + {"Left Class-D Out", NULL, "Left Line Out"}, + {"Right Class-D Out", NULL, "Left Line Out"}, + {"SPOP", NULL, "Left Class-D Out"}, + {"SPOM", NULL, "Right Class-D Out"}, +}; + +static int aic3x_add_widgets(struct snd_soc_component *component) +{ + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + switch (aic3x->model) { + case AIC3X_MODEL_3X: + case AIC3X_MODEL_33: + case AIC3X_MODEL_3106: + snd_soc_dapm_new_controls(dapm, aic3x_extra_dapm_widgets, + ARRAY_SIZE(aic3x_extra_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon_extra, + ARRAY_SIZE(intercon_extra)); + snd_soc_dapm_new_controls(dapm, aic3x_dapm_mono_widgets, + ARRAY_SIZE(aic3x_dapm_mono_widgets)); + snd_soc_dapm_add_routes(dapm, intercon_mono, + ARRAY_SIZE(intercon_mono)); + break; + case AIC3X_MODEL_3007: + snd_soc_dapm_new_controls(dapm, aic3x_extra_dapm_widgets, + ARRAY_SIZE(aic3x_extra_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon_extra, + ARRAY_SIZE(intercon_extra)); + snd_soc_dapm_new_controls(dapm, aic3007_dapm_widgets, + ARRAY_SIZE(aic3007_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon_3007, + ARRAY_SIZE(intercon_3007)); + break; + case AIC3X_MODEL_3104: + snd_soc_dapm_new_controls(dapm, aic3104_extra_dapm_widgets, + ARRAY_SIZE(aic3104_extra_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon_extra_3104, + ARRAY_SIZE(intercon_extra_3104)); + break; + } + + return 0; +} + +static int aic3x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0; + u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; + u16 d, pll_d = 1; + int clk; + int width = aic3x->slot_width; + + if (!width) + width = params_width(params); + + /* select data word length */ + data = snd_soc_component_read(component, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4)); + switch (width) { + case 16: + break; + case 20: + data |= (0x01 << 4); + break; + case 24: + data |= (0x02 << 4); + break; + case 32: + data |= (0x03 << 4); + break; + } + snd_soc_component_write(component, AIC3X_ASD_INTF_CTRLB, data); + + /* Fsref can be 44100 or 48000 */ + fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000; + + /* Try to find a value for Q which allows us to bypass the PLL and + * generate CODEC_CLK directly. */ + for (pll_q = 2; pll_q < 18; pll_q++) + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } + + if (bypass_pll) { + pll_q &= 0xf; + snd_soc_component_write(component, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT); + snd_soc_component_write(component, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV); + /* disable PLL if it is bypassed */ + snd_soc_component_update_bits(component, AIC3X_PLL_PROGA_REG, PLL_ENABLE, 0); + + } else { + snd_soc_component_write(component, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV); + /* enable PLL when it is used */ + snd_soc_component_update_bits(component, AIC3X_PLL_PROGA_REG, + PLL_ENABLE, PLL_ENABLE); + } + + /* Route Left DAC to left channel input and + * right DAC to right channel input */ + data = (LDAC2LCH | RDAC2RCH); + data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000; + if (params_rate(params) >= 64000) + data |= DUAL_RATE_MODE; + snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data); + + /* codec sample rate select */ + data = (fsref * 20) / params_rate(params); + if (params_rate(params) < 64000) + data /= 2; + data /= 5; + data -= 2; + data |= (data << 4); + snd_soc_component_write(component, AIC3X_SAMPLE_RATE_SEL_REG, data); + + if (bypass_pll) + return 0; + + /* Use PLL, compute appropriate setup for j, d, r and p, the closest + * one wins the game. Try with d==0 first, next with d!=0. + * Constraints for j are according to the datasheet. + * The sysclk is divided by 1000 to prevent integer overflows. + */ + + codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000); + + for (r = 1; r <= 16; r++) + for (p = 1; p <= 8; p++) { + for (j = 4; j <= 55; j++) { + /* This is actually 1000*((j+(d/10000))*r)/p + * The term had to be converted to get + * rid of the division by 10000; d = 0 here + */ + int tmp_clk = (1000 * j * r) / p; + + /* Check whether this values get closer than + * the best ones we had before + */ + if (abs(codec_clk - tmp_clk) < + abs(codec_clk - last_clk)) { + pll_j = j; pll_d = 0; + pll_r = r; pll_p = p; + last_clk = tmp_clk; + } + + /* Early exit for exact matches */ + if (tmp_clk == codec_clk) + goto found; + } + } + + /* try with d != 0 */ + for (p = 1; p <= 8; p++) { + j = codec_clk * p / 1000; + + if (j < 4 || j > 11) + continue; + + /* do not use codec_clk here since we'd loose precision */ + d = ((2048 * p * fsref) - j * aic3x->sysclk) + * 100 / (aic3x->sysclk/100); + + clk = (10000 * j + d) / (10 * p); + + /* check whether this values get closer than the best + * ones we had before */ + if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) { + pll_j = j; pll_d = d; pll_r = 1; pll_p = p; + last_clk = clk; + } + + /* Early exit for exact matches */ + if (clk == codec_clk) + goto found; + } + + if (last_clk == 0) { + printk(KERN_ERR "%s(): unable to setup PLL\n", __func__); + return -EINVAL; + } + +found: + snd_soc_component_update_bits(component, AIC3X_PLL_PROGA_REG, PLLP_MASK, pll_p); + snd_soc_component_write(component, AIC3X_OVRF_STATUS_AND_PLLR_REG, + pll_r << PLLR_SHIFT); + snd_soc_component_write(component, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT); + snd_soc_component_write(component, AIC3X_PLL_PROGC_REG, + (pll_d >> 6) << PLLD_MSB_SHIFT); + snd_soc_component_write(component, AIC3X_PLL_PROGD_REG, + (pll_d & 0x3F) << PLLD_LSB_SHIFT); + + return 0; +} + +static int aic3x_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + int delay = 0; + int width = aic3x->slot_width; + + if (!width) + width = substream->runtime->sample_bits; + + /* TDM slot selection only valid in DSP_A/_B mode */ + if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A) + delay += (aic3x->tdm_delay*width + 1); + else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B) + delay += aic3x->tdm_delay*width; + + /* Configure data delay */ + snd_soc_component_write(component, AIC3X_ASD_INTF_CTRLC, delay); + + return 0; +} + +static int aic3x_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u8 ldac_reg = snd_soc_component_read(component, LDAC_VOL) & ~MUTE_ON; + u8 rdac_reg = snd_soc_component_read(component, RDAC_VOL) & ~MUTE_ON; + + if (mute) { + snd_soc_component_write(component, LDAC_VOL, ldac_reg | MUTE_ON); + snd_soc_component_write(component, RDAC_VOL, rdac_reg | MUTE_ON); + } else { + snd_soc_component_write(component, LDAC_VOL, ldac_reg); + snd_soc_component_write(component, RDAC_VOL, rdac_reg); + } + + return 0; +} + +static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + + /* set clock on MCLK or GPIO2 or BCLK */ + snd_soc_component_update_bits(component, AIC3X_CLKGEN_CTRL_REG, PLLCLK_IN_MASK, + clk_id << PLLCLK_IN_SHIFT); + snd_soc_component_update_bits(component, AIC3X_CLKGEN_CTRL_REG, CLKDIV_IN_MASK, + clk_id << CLKDIV_IN_SHIFT); + + aic3x->sysclk = freq; + return 0; +} + +static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + u8 iface_areg, iface_breg; + + iface_areg = snd_soc_component_read(component, AIC3X_ASD_INTF_CTRLA) & 0x3f; + iface_breg = snd_soc_component_read(component, AIC3X_ASD_INTF_CTRLB) & 0x3f; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + aic3x->master = 1; + iface_areg |= BIT_CLK_MASTER | WORD_CLK_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFC: + aic3x->master = 0; + iface_areg &= ~(BIT_CLK_MASTER | WORD_CLK_MASTER); + break; + case SND_SOC_DAIFMT_CBP_CFC: + aic3x->master = 1; + iface_areg |= BIT_CLK_MASTER; + iface_areg &= ~WORD_CLK_MASTER; + break; + case SND_SOC_DAIFMT_CBC_CFP: + aic3x->master = 1; + iface_areg |= WORD_CLK_MASTER; + iface_areg &= ~BIT_CLK_MASTER; + break; + default: + return -EINVAL; + } + + /* + * match both interface format and signal polarities since they + * are fixed + */ + switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | + SND_SOC_DAIFMT_INV_MASK)) { + case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): + break; + case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF): + case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF): + iface_breg |= (0x01 << 6); + break; + case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF): + iface_breg |= (0x02 << 6); + break; + case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF): + iface_breg |= (0x03 << 6); + break; + default: + return -EINVAL; + } + + aic3x->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + /* set iface */ + snd_soc_component_write(component, AIC3X_ASD_INTF_CTRLA, iface_areg); + snd_soc_component_write(component, AIC3X_ASD_INTF_CTRLB, iface_breg); + + return 0; +} + +static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = codec_dai->component; + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + unsigned int lsb; + + if (tx_mask != rx_mask) { + dev_err(component->dev, "tx and rx masks must be symmetric\n"); + return -EINVAL; + } + + if (unlikely(!tx_mask)) { + dev_err(component->dev, "tx and rx masks need to be non 0\n"); + return -EINVAL; + } + + /* TDM based on DSP mode requires slots to be adjacent */ + lsb = __ffs(tx_mask); + if ((lsb + 1) != __fls(tx_mask)) { + dev_err(component->dev, "Invalid mask, slots must be adjacent\n"); + return -EINVAL; + } + + switch (slot_width) { + case 16: + case 20: + case 24: + case 32: + break; + default: + dev_err(component->dev, "Unsupported slot width %d\n", slot_width); + return -EINVAL; + } + + + aic3x->tdm_delay = lsb; + aic3x->slot_width = slot_width; + + /* DOUT in high-impedance on inactive bit clocks */ + snd_soc_component_update_bits(component, AIC3X_ASD_INTF_CTRLA, + DOUT_TRISTATE, DOUT_TRISTATE); + + return 0; +} + +static int aic3x_regulator_event(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct aic3x_disable_nb *disable_nb = + container_of(nb, struct aic3x_disable_nb, nb); + struct aic3x_priv *aic3x = disable_nb->aic3x; + + if (event & REGULATOR_EVENT_DISABLE) { + /* + * Put codec to reset and require cache sync as at least one + * of the supplies was disabled + */ + if (aic3x->gpio_reset) + gpiod_set_value(aic3x->gpio_reset, 1); + regcache_mark_dirty(aic3x->regmap); + } + + return 0; +} + +static int aic3x_set_power(struct snd_soc_component *component, int power) +{ + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + unsigned int pll_c, pll_d; + int ret; + + if (power) { + ret = regulator_bulk_enable(ARRAY_SIZE(aic3x->supplies), + aic3x->supplies); + if (ret) + goto out; + aic3x->power = 1; + + if (aic3x->gpio_reset) { + udelay(1); + gpiod_set_value(aic3x->gpio_reset, 0); + } + + /* Sync reg_cache with the hardware */ + regcache_cache_only(aic3x->regmap, false); + regcache_sync(aic3x->regmap); + + /* Rewrite paired PLL D registers in case cached sync skipped + * writing one of them and thus caused other one also not + * being written + */ + pll_c = snd_soc_component_read(component, AIC3X_PLL_PROGC_REG); + pll_d = snd_soc_component_read(component, AIC3X_PLL_PROGD_REG); + if (pll_c == aic3x_reg[AIC3X_PLL_PROGC_REG].def || + pll_d == aic3x_reg[AIC3X_PLL_PROGD_REG].def) { + snd_soc_component_write(component, AIC3X_PLL_PROGC_REG, pll_c); + snd_soc_component_write(component, AIC3X_PLL_PROGD_REG, pll_d); + } + + /* + * Delay is needed to reduce pop-noise after syncing back the + * registers + */ + mdelay(50); + } else { + /* + * Do soft reset to this codec instance in order to clear + * possible VDD leakage currents in case the supply regulators + * remain on + */ + snd_soc_component_write(component, AIC3X_RESET, SOFT_RESET); + regcache_mark_dirty(aic3x->regmap); + aic3x->power = 0; + /* HW writes are needless when bias is off */ + regcache_cache_only(aic3x->regmap, true); + ret = regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), + aic3x->supplies); + } +out: + return ret; +} + +static int aic3x_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY && + aic3x->master) { + /* enable pll */ + snd_soc_component_update_bits(component, AIC3X_PLL_PROGA_REG, + PLL_ENABLE, PLL_ENABLE); + } + break; + case SND_SOC_BIAS_STANDBY: + if (!aic3x->power) + aic3x_set_power(component, 1); + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE && + aic3x->master) { + /* disable pll */ + snd_soc_component_update_bits(component, AIC3X_PLL_PROGA_REG, + PLL_ENABLE, 0); + } + break; + case SND_SOC_BIAS_OFF: + if (aic3x->power) + aic3x_set_power(component, 0); + break; + } + + return 0; +} + +#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000 +#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops aic3x_dai_ops = { + .hw_params = aic3x_hw_params, + .prepare = aic3x_prepare, + .mute_stream = aic3x_mute, + .set_sysclk = aic3x_set_dai_sysclk, + .set_fmt = aic3x_set_dai_fmt, + .set_tdm_slot = aic3x_set_dai_tdm_slot, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver aic3x_dai = { + .name = "tlv320aic3x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AIC3X_RATES, + .formats = AIC3X_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AIC3X_RATES, + .formats = AIC3X_FORMATS,}, + .ops = &aic3x_dai_ops, + .symmetric_rate = 1, +}; + +static void aic3x_mono_init(struct snd_soc_component *component) +{ + /* DAC to Mono Line Out default volume and route to Output mixer */ + snd_soc_component_write(component, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); + snd_soc_component_write(component, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); + + /* unmute all outputs */ + snd_soc_component_update_bits(component, MONOLOPM_CTRL, UNMUTE, UNMUTE); + + /* PGA to Mono Line Out default volume, disconnect from Output Mixer */ + snd_soc_component_write(component, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL); + snd_soc_component_write(component, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL); + + /* Line2 to Mono Out default volume, disconnect from Output Mixer */ + snd_soc_component_write(component, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL); + snd_soc_component_write(component, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); +} + +/* + * initialise the AIC3X driver + * register the mixer and dsp interfaces with the kernel + */ +static int aic3x_init(struct snd_soc_component *component) +{ + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + + snd_soc_component_write(component, AIC3X_PAGE_SELECT, PAGE0_SELECT); + snd_soc_component_write(component, AIC3X_RESET, SOFT_RESET); + + /* DAC default volume and mute */ + snd_soc_component_write(component, LDAC_VOL, DEFAULT_VOL | MUTE_ON); + snd_soc_component_write(component, RDAC_VOL, DEFAULT_VOL | MUTE_ON); + + /* DAC to HP default volume and route to Output mixer */ + snd_soc_component_write(component, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON); + snd_soc_component_write(component, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON); + snd_soc_component_write(component, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON); + snd_soc_component_write(component, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON); + /* DAC to Line Out default volume and route to Output mixer */ + snd_soc_component_write(component, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON); + snd_soc_component_write(component, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON); + + /* unmute all outputs */ + snd_soc_component_update_bits(component, LLOPM_CTRL, UNMUTE, UNMUTE); + snd_soc_component_update_bits(component, RLOPM_CTRL, UNMUTE, UNMUTE); + snd_soc_component_update_bits(component, HPLOUT_CTRL, UNMUTE, UNMUTE); + snd_soc_component_update_bits(component, HPROUT_CTRL, UNMUTE, UNMUTE); + snd_soc_component_update_bits(component, HPLCOM_CTRL, UNMUTE, UNMUTE); + snd_soc_component_update_bits(component, HPRCOM_CTRL, UNMUTE, UNMUTE); + + /* ADC default volume and unmute */ + snd_soc_component_write(component, LADC_VOL, DEFAULT_GAIN); + snd_soc_component_write(component, RADC_VOL, DEFAULT_GAIN); + /* By default route Line1 to ADC PGA mixer */ + snd_soc_component_write(component, LINE1L_2_LADC_CTRL, 0x0); + snd_soc_component_write(component, LINE1R_2_RADC_CTRL, 0x0); + + /* PGA to HP Bypass default volume, disconnect from Output Mixer */ + snd_soc_component_write(component, PGAL_2_HPLOUT_VOL, DEFAULT_VOL); + snd_soc_component_write(component, PGAR_2_HPROUT_VOL, DEFAULT_VOL); + snd_soc_component_write(component, PGAL_2_HPLCOM_VOL, DEFAULT_VOL); + snd_soc_component_write(component, PGAR_2_HPRCOM_VOL, DEFAULT_VOL); + /* PGA to Line Out default volume, disconnect from Output Mixer */ + snd_soc_component_write(component, PGAL_2_LLOPM_VOL, DEFAULT_VOL); + snd_soc_component_write(component, PGAR_2_RLOPM_VOL, DEFAULT_VOL); + + /* On tlv320aic3104, these registers are reserved and must not be written */ + if (aic3x->model != AIC3X_MODEL_3104) { + /* Line2 to HP Bypass default volume, disconnect from Output Mixer */ + snd_soc_component_write(component, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL); + snd_soc_component_write(component, LINE2R_2_HPROUT_VOL, DEFAULT_VOL); + snd_soc_component_write(component, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL); + snd_soc_component_write(component, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL); + /* Line2 Line Out default volume, disconnect from Output Mixer */ + snd_soc_component_write(component, LINE2L_2_LLOPM_VOL, DEFAULT_VOL); + snd_soc_component_write(component, LINE2R_2_RLOPM_VOL, DEFAULT_VOL); + } + + switch (aic3x->model) { + case AIC3X_MODEL_3X: + case AIC3X_MODEL_33: + case AIC3X_MODEL_3106: + aic3x_mono_init(component); + break; + case AIC3X_MODEL_3007: + snd_soc_component_write(component, CLASSD_CTRL, 0); + break; + } + + /* Output common-mode voltage = 1.5 V */ + snd_soc_component_update_bits(component, HPOUT_SC, HPOUT_SC_OCMV_MASK, + aic3x->ocmv << HPOUT_SC_OCMV_SHIFT); + + return 0; +} + +static int aic3x_component_probe(struct snd_soc_component *component) +{ + struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); + int ret, i; + + aic3x->component = component; + + for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) { + aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event; + aic3x->disable_nb[i].aic3x = aic3x; + ret = devm_regulator_register_notifier( + aic3x->supplies[i].consumer, + &aic3x->disable_nb[i].nb); + if (ret) { + dev_err(component->dev, + "Failed to request regulator notifier: %d\n", + ret); + return ret; + } + } + + regcache_mark_dirty(aic3x->regmap); + aic3x_init(component); + + if (aic3x->setup) { + if (aic3x->model != AIC3X_MODEL_3104) { + /* setup GPIO functions */ + snd_soc_component_write(component, AIC3X_GPIO1_REG, + (aic3x->setup->gpio_func[0] & 0xf) << 4); + snd_soc_component_write(component, AIC3X_GPIO2_REG, + (aic3x->setup->gpio_func[1] & 0xf) << 4); + } else { + dev_warn(component->dev, "GPIO functionality is not supported on tlv320aic3104\n"); + } + } + + switch (aic3x->model) { + case AIC3X_MODEL_3X: + case AIC3X_MODEL_33: + case AIC3X_MODEL_3106: + snd_soc_add_component_controls(component, aic3x_extra_snd_controls, + ARRAY_SIZE(aic3x_extra_snd_controls)); + snd_soc_add_component_controls(component, aic3x_mono_controls, + ARRAY_SIZE(aic3x_mono_controls)); + break; + case AIC3X_MODEL_3007: + snd_soc_add_component_controls(component, aic3x_extra_snd_controls, + ARRAY_SIZE(aic3x_extra_snd_controls)); + snd_soc_add_component_controls(component, + &aic3x_classd_amp_gain_ctrl, 1); + break; + case AIC3X_MODEL_3104: + break; + } + + /* set mic bias voltage */ + switch (aic3x->micbias_vg) { + case AIC3X_MICBIAS_2_0V: + case AIC3X_MICBIAS_2_5V: + case AIC3X_MICBIAS_AVDDV: + snd_soc_component_update_bits(component, MICBIAS_CTRL, + MICBIAS_LEVEL_MASK, + (aic3x->micbias_vg) << MICBIAS_LEVEL_SHIFT); + break; + case AIC3X_MICBIAS_OFF: + /* + * noting to do. target won't enter here. This is just to avoid + * compile time warning "warning: enumeration value + * 'AIC3X_MICBIAS_OFF' not handled in switch" + */ + break; + } + + aic3x_add_widgets(component); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_aic3x = { + .set_bias_level = aic3x_set_bias_level, + .probe = aic3x_component_probe, + .controls = aic3x_snd_controls, + .num_controls = ARRAY_SIZE(aic3x_snd_controls), + .dapm_widgets = aic3x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static void aic3x_configure_ocmv(struct device *dev, struct aic3x_priv *aic3x) +{ + struct device_node *np = dev->of_node; + u32 value; + int dvdd, avdd; + + if (np && !of_property_read_u32(np, "ai3x-ocmv", &value)) { + /* OCMV setting is forced by DT */ + if (value <= 3) { + aic3x->ocmv = value; + return; + } + } + + dvdd = regulator_get_voltage(aic3x->supplies[1].consumer); + avdd = regulator_get_voltage(aic3x->supplies[2].consumer); + + if (avdd > 3600000 || dvdd > 1950000) { + dev_warn(dev, + "Too high supply voltage(s) AVDD: %d, DVDD: %d\n", + avdd, dvdd); + } else if (avdd == 3600000 && dvdd == 1950000) { + aic3x->ocmv = HPOUT_SC_OCMV_1_8V; + } else if (avdd > 3300000 && dvdd > 1800000) { + aic3x->ocmv = HPOUT_SC_OCMV_1_65V; + } else if (avdd > 3000000 && dvdd > 1650000) { + aic3x->ocmv = HPOUT_SC_OCMV_1_5V; + } else if (avdd >= 2700000 && dvdd >= 1525000) { + aic3x->ocmv = HPOUT_SC_OCMV_1_35V; + } else { + dev_warn(dev, + "Invalid supply voltage(s) AVDD: %d, DVDD: %d\n", + avdd, dvdd); + } +} + + +static const struct reg_sequence aic3007_class_d[] = { + /* Class-D speaker driver init; datasheet p. 46 */ + { AIC3X_PAGE_SELECT, 0x0D }, + { 0xD, 0x0D }, + { 0x8, 0x5C }, + { 0x8, 0x5D }, + { 0x8, 0x5C }, + { AIC3X_PAGE_SELECT, 0x00 }, +}; + +int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver_data) +{ + struct aic3x_priv *aic3x; + struct aic3x_setup_data *ai3x_setup; + struct device_node *np = dev->of_node; + int ret, i; + u32 value; + + aic3x = devm_kzalloc(dev, sizeof(struct aic3x_priv), GFP_KERNEL); + if (!aic3x) + return -ENOMEM; + + aic3x->regmap = regmap; + if (IS_ERR(aic3x->regmap)) { + ret = PTR_ERR(aic3x->regmap); + return ret; + } + + regcache_cache_only(aic3x->regmap, true); + + dev_set_drvdata(dev, aic3x); + if (np) { + ai3x_setup = devm_kzalloc(dev, sizeof(*ai3x_setup), GFP_KERNEL); + if (!ai3x_setup) + return -ENOMEM; + + if (of_property_read_u32_array(np, "ai3x-gpio-func", + ai3x_setup->gpio_func, 2) >= 0) { + aic3x->setup = ai3x_setup; + } + + if (!of_property_read_u32(np, "ai3x-micbias-vg", &value)) { + switch (value) { + case 1 : + aic3x->micbias_vg = AIC3X_MICBIAS_2_0V; + break; + case 2 : + aic3x->micbias_vg = AIC3X_MICBIAS_2_5V; + break; + case 3 : + aic3x->micbias_vg = AIC3X_MICBIAS_AVDDV; + break; + default : + aic3x->micbias_vg = AIC3X_MICBIAS_OFF; + dev_err(dev, "Unsuitable MicBias voltage " + "found in DT\n"); + } + } else { + aic3x->micbias_vg = AIC3X_MICBIAS_OFF; + } + } + + aic3x->model = driver_data; + + aic3x->gpio_reset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + ret = PTR_ERR_OR_ZERO(aic3x->gpio_reset); + if (ret) { + if (ret != -EBUSY) + return ret; + + /* + * Apparently there are setups where the codec is sharing + * its reset line. Try to get it non-exclusively, although + * the utility of this is unclear: how do we make sure that + * resetting one chip will not disturb the others that share + * the same line? + */ + aic3x->gpio_reset = devm_gpiod_get(dev, "reset", + GPIOD_ASIS | GPIOD_FLAGS_BIT_NONEXCLUSIVE); + ret = PTR_ERR_OR_ZERO(aic3x->gpio_reset); + if (ret) + return ret; + + aic3x->shared_reset = true; + } + + gpiod_set_consumer_name(aic3x->gpio_reset, "tlv320aic3x reset"); + + for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) + aic3x->supplies[i].supply = aic3x_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(aic3x->supplies), + aic3x->supplies); + if (ret) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + aic3x_configure_ocmv(dev, aic3x); + + if (aic3x->model == AIC3X_MODEL_3007) { + ret = regmap_register_patch(aic3x->regmap, aic3007_class_d, + ARRAY_SIZE(aic3007_class_d)); + if (ret != 0) + dev_err(dev, "Failed to init class D: %d\n", ret); + } + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_aic3x, &aic3x_dai, 1); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(aic3x_probe); + +void aic3x_remove(struct device *dev) +{ + struct aic3x_priv *aic3x = dev_get_drvdata(dev); + + /* Leave the codec in reset state */ + if (aic3x->gpio_reset && !aic3x->shared_reset) + gpiod_set_value(aic3x->gpio_reset, 1); +} +EXPORT_SYMBOL(aic3x_remove); + +MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h new file mode 100644 index 0000000000..066e5a6322 --- /dev/null +++ b/sound/soc/codecs/tlv320aic3x.h @@ -0,0 +1,344 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC TLV320AIC3X codec driver + * + * Author: Vladimir Barinov, + * Copyright: (C) 2007 MontaVista Software, Inc., + */ + +#ifndef _AIC3X_H +#define _AIC3X_H + +struct device; +struct regmap_config; + +extern const struct regmap_config aic3x_regmap; +int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver_data); +void aic3x_remove(struct device *dev); + +#define AIC3X_MODEL_3X 0 +#define AIC3X_MODEL_33 1 +#define AIC3X_MODEL_3007 2 +#define AIC3X_MODEL_3104 3 +#define AIC3X_MODEL_3106 4 + +/* AIC3X register space */ +#define AIC3X_CACHEREGNUM 110 + +/* Page select register */ +#define AIC3X_PAGE_SELECT 0 +/* Software reset register */ +#define AIC3X_RESET 1 +/* Codec Sample rate select register */ +#define AIC3X_SAMPLE_RATE_SEL_REG 2 +/* PLL progrramming register A */ +#define AIC3X_PLL_PROGA_REG 3 +/* PLL progrramming register B */ +#define AIC3X_PLL_PROGB_REG 4 +/* PLL progrramming register C */ +#define AIC3X_PLL_PROGC_REG 5 +/* PLL progrramming register D */ +#define AIC3X_PLL_PROGD_REG 6 +/* Codec datapath setup register */ +#define AIC3X_CODEC_DATAPATH_REG 7 +/* Audio serial data interface control register A */ +#define AIC3X_ASD_INTF_CTRLA 8 +/* Audio serial data interface control register B */ +#define AIC3X_ASD_INTF_CTRLB 9 +/* Audio serial data interface control register C */ +#define AIC3X_ASD_INTF_CTRLC 10 +/* Audio overflow status and PLL R value programming register */ +#define AIC3X_OVRF_STATUS_AND_PLLR_REG 11 +/* Audio codec digital filter control register */ +#define AIC3X_CODEC_DFILT_CTRL 12 +/* Headset/button press detection register */ +#define AIC3X_HEADSET_DETECT_CTRL_A 13 +#define AIC3X_HEADSET_DETECT_CTRL_B 14 +/* ADC PGA Gain control registers */ +#define LADC_VOL 15 +#define RADC_VOL 16 +/* MIC3 control registers */ +#define MIC3LR_2_LADC_CTRL 17 +#define MIC3LR_2_RADC_CTRL 18 +/* Line1 Input control registers */ +#define LINE1L_2_LADC_CTRL 19 +#define LINE1R_2_LADC_CTRL 21 +#define LINE1R_2_RADC_CTRL 22 +#define LINE1L_2_RADC_CTRL 24 +/* Line2 Input control registers */ +#define LINE2L_2_LADC_CTRL 20 +#define LINE2R_2_RADC_CTRL 23 +/* MICBIAS Control Register */ +#define MICBIAS_CTRL 25 + +/* AGC Control Registers A, B, C */ +#define LAGC_CTRL_A 26 +#define LAGC_CTRL_B 27 +#define LAGC_CTRL_C 28 +#define RAGC_CTRL_A 29 +#define RAGC_CTRL_B 30 +#define RAGC_CTRL_C 31 + +/* DAC Power and Left High Power Output control registers */ +#define DAC_PWR 37 +#define HPLCOM_CFG 37 +/* Right High Power Output control registers */ +#define HPRCOM_CFG 38 +/* High Power Output Stage Control Register */ +#define HPOUT_SC 40 +/* DAC Output Switching control registers */ +#define DAC_LINE_MUX 41 +/* High Power Output Driver Pop Reduction registers */ +#define HPOUT_POP_REDUCTION 42 +/* DAC Digital control registers */ +#define LDAC_VOL 43 +#define RDAC_VOL 44 +/* Left High Power Output control registers */ +#define LINE2L_2_HPLOUT_VOL 45 +#define PGAL_2_HPLOUT_VOL 46 +#define DACL1_2_HPLOUT_VOL 47 +#define LINE2R_2_HPLOUT_VOL 48 +#define PGAR_2_HPLOUT_VOL 49 +#define DACR1_2_HPLOUT_VOL 50 +#define HPLOUT_CTRL 51 +/* Left High Power COM control registers */ +#define LINE2L_2_HPLCOM_VOL 52 +#define PGAL_2_HPLCOM_VOL 53 +#define DACL1_2_HPLCOM_VOL 54 +#define LINE2R_2_HPLCOM_VOL 55 +#define PGAR_2_HPLCOM_VOL 56 +#define DACR1_2_HPLCOM_VOL 57 +#define HPLCOM_CTRL 58 +/* Right High Power Output control registers */ +#define LINE2L_2_HPROUT_VOL 59 +#define PGAL_2_HPROUT_VOL 60 +#define DACL1_2_HPROUT_VOL 61 +#define LINE2R_2_HPROUT_VOL 62 +#define PGAR_2_HPROUT_VOL 63 +#define DACR1_2_HPROUT_VOL 64 +#define HPROUT_CTRL 65 +/* Right High Power COM control registers */ +#define LINE2L_2_HPRCOM_VOL 66 +#define PGAL_2_HPRCOM_VOL 67 +#define DACL1_2_HPRCOM_VOL 68 +#define LINE2R_2_HPRCOM_VOL 69 +#define PGAR_2_HPRCOM_VOL 70 +#define DACR1_2_HPRCOM_VOL 71 +#define HPRCOM_CTRL 72 +/* Mono Line Output Plus/Minus control registers */ +#define LINE2L_2_MONOLOPM_VOL 73 +#define PGAL_2_MONOLOPM_VOL 74 +#define DACL1_2_MONOLOPM_VOL 75 +#define LINE2R_2_MONOLOPM_VOL 76 +#define PGAR_2_MONOLOPM_VOL 77 +#define DACR1_2_MONOLOPM_VOL 78 +#define MONOLOPM_CTRL 79 +/* Class-D speaker driver on tlv320aic3007 */ +#define CLASSD_CTRL 73 +/* Left Line Output Plus/Minus control registers */ +#define LINE2L_2_LLOPM_VOL 80 +#define PGAL_2_LLOPM_VOL 81 +#define DACL1_2_LLOPM_VOL 82 +#define LINE2R_2_LLOPM_VOL 83 +#define PGAR_2_LLOPM_VOL 84 +#define DACR1_2_LLOPM_VOL 85 +#define LLOPM_CTRL 86 +/* Right Line Output Plus/Minus control registers */ +#define LINE2L_2_RLOPM_VOL 87 +#define PGAL_2_RLOPM_VOL 88 +#define DACL1_2_RLOPM_VOL 89 +#define LINE2R_2_RLOPM_VOL 90 +#define PGAR_2_RLOPM_VOL 91 +#define DACR1_2_RLOPM_VOL 92 +#define RLOPM_CTRL 93 +/* GPIO/IRQ registers */ +#define AIC3X_STICKY_IRQ_FLAGS_REG 96 +#define AIC3X_RT_IRQ_FLAGS_REG 97 +#define AIC3X_GPIO1_REG 98 +#define AIC3X_GPIO2_REG 99 +#define AIC3X_GPIOA_REG 100 +#define AIC3X_GPIOB_REG 101 +/* Clock generation control register */ +#define AIC3X_CLKGEN_CTRL_REG 102 +/* New AGC registers */ +#define LAGCN_ATTACK 103 +#define LAGCN_DECAY 104 +#define RAGCN_ATTACK 105 +#define RAGCN_DECAY 106 +/* New Programmable ADC Digital Path and I2C Bus Condition Register */ +#define NEW_ADC_DIGITALPATH 107 +/* Passive Analog Signal Bypass Selection During Powerdown Register */ +#define PASSIVE_BYPASS 108 +/* DAC Quiescent Current Adjustment Register */ +#define DAC_ICC_ADJ 109 + +/* Page select register bits */ +#define PAGE0_SELECT 0 +#define PAGE1_SELECT 1 + +/* Audio serial data interface control register A bits */ +#define BIT_CLK_MASTER 0x80 +#define WORD_CLK_MASTER 0x40 +#define DOUT_TRISTATE 0x20 + +/* Codec Datapath setup register 7 */ +#define FSREF_44100 (1 << 7) +#define FSREF_48000 (0 << 7) +#define DUAL_RATE_MODE ((1 << 5) | (1 << 6)) +#define LDAC2LCH (0x1 << 3) +#define RDAC2RCH (0x1 << 1) +#define LDAC2RCH (0x2 << 3) +#define RDAC2LCH (0x2 << 1) +#define LDAC2MONOMIX (0x3 << 3) +#define RDAC2MONOMIX (0x3 << 1) + +/* PLL registers bitfields */ +#define PLLP_SHIFT 0 +#define PLLP_MASK 7 +#define PLLQ_SHIFT 3 +#define PLLR_SHIFT 0 +#define PLLJ_SHIFT 2 +#define PLLD_MSB_SHIFT 0 +#define PLLD_LSB_SHIFT 2 + +/* Clock generation register bits */ +#define CODEC_CLKIN_PLLDIV 0 +#define CODEC_CLKIN_CLKDIV 1 +#define PLL_CLKIN_SHIFT 4 +#define MCLK_SOURCE 0x0 +#define PLL_CLKDIV_SHIFT 0 +#define PLLCLK_IN_MASK 0x30 +#define PLLCLK_IN_SHIFT 4 +#define CLKDIV_IN_MASK 0xc0 +#define CLKDIV_IN_SHIFT 6 +/* clock in source */ +#define CLKIN_MCLK 0 +#define CLKIN_GPIO2 1 +#define CLKIN_BCLK 2 + +/* Software reset register bits */ +#define SOFT_RESET 0x80 + +/* PLL progrramming register A bits */ +#define PLL_ENABLE 0x80 + +/* Route bits */ +#define ROUTE_ON 0x80 + +/* Mute bits */ +#define UNMUTE 0x08 +#define MUTE_ON 0x80 + +/* Power bits */ +#define LADC_PWR_ON 0x04 +#define RADC_PWR_ON 0x04 +#define LDAC_PWR_ON 0x80 +#define RDAC_PWR_ON 0x40 +#define HPLOUT_PWR_ON 0x01 +#define HPROUT_PWR_ON 0x01 +#define HPLCOM_PWR_ON 0x01 +#define HPRCOM_PWR_ON 0x01 +#define MONOLOPM_PWR_ON 0x01 +#define LLOPM_PWR_ON 0x01 +#define RLOPM_PWR_ON 0x01 + +#define INVERT_VOL(val) (0x7f - val) + +/* Default output volume (inverted) */ +#define DEFAULT_VOL INVERT_VOL(0x50) +/* Default input volume */ +#define DEFAULT_GAIN 0x20 + +/* MICBIAS Control Register */ +#define MICBIAS_LEVEL_SHIFT (6) +#define MICBIAS_LEVEL_MASK (3 << 6) + +/* HPOUT_SC */ +#define HPOUT_SC_OCMV_MASK (3 << 6) +#define HPOUT_SC_OCMV_SHIFT (6) +#define HPOUT_SC_OCMV_1_35V 0 +#define HPOUT_SC_OCMV_1_5V 1 +#define HPOUT_SC_OCMV_1_65V 2 +#define HPOUT_SC_OCMV_1_8V 3 + +/* headset detection / button API */ + +/* The AIC3x supports detection of stereo headsets (GND + left + right signal) + * and cellular headsets (GND + speaker output + microphone input). + * It is recommended to enable MIC bias for this function to work properly. + * For more information, please refer to the datasheet. */ +enum { + AIC3X_HEADSET_DETECT_OFF = 0, + AIC3X_HEADSET_DETECT_STEREO = 1, + AIC3X_HEADSET_DETECT_CELLULAR = 2, + AIC3X_HEADSET_DETECT_BOTH = 3 +}; + +enum { + AIC3X_HEADSET_DEBOUNCE_16MS = 0, + AIC3X_HEADSET_DEBOUNCE_32MS = 1, + AIC3X_HEADSET_DEBOUNCE_64MS = 2, + AIC3X_HEADSET_DEBOUNCE_128MS = 3, + AIC3X_HEADSET_DEBOUNCE_256MS = 4, + AIC3X_HEADSET_DEBOUNCE_512MS = 5 +}; + +enum { + AIC3X_BUTTON_DEBOUNCE_0MS = 0, + AIC3X_BUTTON_DEBOUNCE_8MS = 1, + AIC3X_BUTTON_DEBOUNCE_16MS = 2, + AIC3X_BUTTON_DEBOUNCE_32MS = 3 +}; + +#define AIC3X_HEADSET_DETECT_ENABLED 0x80 +#define AIC3X_HEADSET_DETECT_SHIFT 5 +#define AIC3X_HEADSET_DETECT_MASK 3 +#define AIC3X_HEADSET_DEBOUNCE_SHIFT 2 +#define AIC3X_HEADSET_DEBOUNCE_MASK 7 +#define AIC3X_BUTTON_DEBOUNCE_SHIFT 0 +#define AIC3X_BUTTON_DEBOUNCE_MASK 3 + +/* GPIO API */ +enum { + AIC3X_GPIO1_FUNC_DISABLED = 0, + AIC3X_GPIO1_FUNC_AUDIO_WORDCLK_ADC = 1, + AIC3X_GPIO1_FUNC_CLOCK_MUX = 2, + AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV2 = 3, + AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV4 = 4, + AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV8 = 5, + AIC3X_GPIO1_FUNC_SHORT_CIRCUIT_IRQ = 6, + AIC3X_GPIO1_FUNC_AGC_NOISE_IRQ = 7, + AIC3X_GPIO1_FUNC_INPUT = 8, + AIC3X_GPIO1_FUNC_OUTPUT = 9, + AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK = 10, + AIC3X_GPIO1_FUNC_AUDIO_WORDCLK = 11, + AIC3X_GPIO1_FUNC_BUTTON_IRQ = 12, + AIC3X_GPIO1_FUNC_HEADSET_DETECT_IRQ = 13, + AIC3X_GPIO1_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 14, + AIC3X_GPIO1_FUNC_ALL_IRQ = 16 +}; + +enum { + AIC3X_GPIO2_FUNC_DISABLED = 0, + AIC3X_GPIO2_FUNC_HEADSET_DETECT_IRQ = 2, + AIC3X_GPIO2_FUNC_INPUT = 3, + AIC3X_GPIO2_FUNC_OUTPUT = 4, + AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT = 5, + AIC3X_GPIO2_FUNC_AUDIO_BITCLK = 8, + AIC3X_GPIO2_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 9, + AIC3X_GPIO2_FUNC_ALL_IRQ = 10, + AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_OR_AGC_IRQ = 11, + AIC3X_GPIO2_FUNC_HEADSET_OR_BUTTON_PRESS_OR_SHORT_CIRCUIT_IRQ = 12, + AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_IRQ = 13, + AIC3X_GPIO2_FUNC_AGC_NOISE_IRQ = 14, + AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ = 15 +}; + +enum aic3x_micbias_voltage { + AIC3X_MICBIAS_OFF = 0, + AIC3X_MICBIAS_2_0V = 1, + AIC3X_MICBIAS_2_5V = 2, + AIC3X_MICBIAS_AVDDV = 3, +}; + +#endif /* _AIC3X_H */ diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c new file mode 100644 index 0000000000..fa46f51d43 --- /dev/null +++ b/sound/soc/codecs/tlv320dac33.c @@ -0,0 +1,1572 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC Texas Instruments TLV320DAC33 codec driver + * + * Author: Peter Ujfalusi + * + * Copyright: (C) 2009 Nokia Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "tlv320dac33.h" + +/* + * The internal FIFO is 24576 bytes long + * It can be configured to hold 16bit or 24bit samples + * In 16bit configuration the FIFO can hold 6144 stereo samples + * In 24bit configuration the FIFO can hold 4096 stereo samples + */ +#define DAC33_FIFO_SIZE_16BIT 6144 +#define DAC33_FIFO_SIZE_24BIT 4096 +#define DAC33_MODE7_MARGIN 10 /* Safety margin for FIFO in Mode7 */ + +#define BURST_BASEFREQ_HZ 49152000 + +#define SAMPLES_TO_US(rate, samples) \ + (1000000000 / (((rate) * 1000) / (samples))) + +#define US_TO_SAMPLES(rate, us) \ + ((rate) / (1000000 / ((us) < 1000000 ? (us) : 1000000))) + +#define UTHR_FROM_PERIOD_SIZE(samples, playrate, burstrate) \ + (((samples)*5000) / (((burstrate)*5000) / ((burstrate) - (playrate)))) + +static void dac33_calculate_times(struct snd_pcm_substream *substream, + struct snd_soc_component *component); +static int dac33_prepare_chip(struct snd_pcm_substream *substream, + struct snd_soc_component *component); + +enum dac33_state { + DAC33_IDLE = 0, + DAC33_PREFILL, + DAC33_PLAYBACK, + DAC33_FLUSH, +}; + +enum dac33_fifo_modes { + DAC33_FIFO_BYPASS = 0, + DAC33_FIFO_MODE1, + DAC33_FIFO_MODE7, + DAC33_FIFO_LAST_MODE, +}; + +#define DAC33_NUM_SUPPLIES 3 +static const char *dac33_supply_names[DAC33_NUM_SUPPLIES] = { + "AVDD", + "DVDD", + "IOVDD", +}; + +struct tlv320dac33_priv { + struct mutex mutex; + struct work_struct work; + struct snd_soc_component *component; + struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES]; + struct snd_pcm_substream *substream; + int power_gpio; + int chip_power; + int irq; + unsigned int refclk; + + unsigned int alarm_threshold; /* set to be half of LATENCY_TIME_MS */ + enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */ + unsigned int fifo_size; /* Size of the FIFO in samples */ + unsigned int nsample; /* burst read amount from host */ + int mode1_latency; /* latency caused by the i2c writes in + * us */ + u8 burst_bclkdiv; /* BCLK divider value in burst mode */ + u8 *reg_cache; + unsigned int burst_rate; /* Interface speed in Burst modes */ + + int keep_bclk; /* Keep the BCLK continuously running + * in FIFO modes */ + spinlock_t lock; + unsigned long long t_stamp1; /* Time stamp for FIFO modes to */ + unsigned long long t_stamp2; /* calculate the FIFO caused delay */ + + unsigned int mode1_us_burst; /* Time to burst read n number of + * samples */ + unsigned int mode7_us_to_lthr; /* Time to reach lthr from uthr */ + + unsigned int uthr; + + enum dac33_state state; + struct i2c_client *i2c; +}; + +static const u8 dac33_reg[DAC33_CACHEREGNUM] = { +0x00, 0x00, 0x00, 0x00, /* 0x00 - 0x03 */ +0x00, 0x00, 0x00, 0x00, /* 0x04 - 0x07 */ +0x00, 0x00, 0x00, 0x00, /* 0x08 - 0x0b */ +0x00, 0x00, 0x00, 0x00, /* 0x0c - 0x0f */ +0x00, 0x00, 0x00, 0x00, /* 0x10 - 0x13 */ +0x00, 0x00, 0x00, 0x00, /* 0x14 - 0x17 */ +0x00, 0x00, 0x00, 0x00, /* 0x18 - 0x1b */ +0x00, 0x00, 0x00, 0x00, /* 0x1c - 0x1f */ +0x00, 0x00, 0x00, 0x00, /* 0x20 - 0x23 */ +0x00, 0x00, 0x00, 0x00, /* 0x24 - 0x27 */ +0x00, 0x00, 0x00, 0x00, /* 0x28 - 0x2b */ +0x00, 0x00, 0x00, 0x80, /* 0x2c - 0x2f */ +0x80, 0x00, 0x00, 0x00, /* 0x30 - 0x33 */ +0x00, 0x00, 0x00, 0x00, /* 0x34 - 0x37 */ +0x00, 0x00, /* 0x38 - 0x39 */ +/* Registers 0x3a - 0x3f are reserved */ + 0x00, 0x00, /* 0x3a - 0x3b */ +0x00, 0x00, 0x00, 0x00, /* 0x3c - 0x3f */ + +0x00, 0x00, 0x00, 0x00, /* 0x40 - 0x43 */ +0x00, 0x80, /* 0x44 - 0x45 */ +/* Registers 0x46 - 0x47 are reserved */ + 0x80, 0x80, /* 0x46 - 0x47 */ + +0x80, 0x00, 0x00, /* 0x48 - 0x4a */ +/* Registers 0x4b - 0x7c are reserved */ + 0x00, /* 0x4b */ +0x00, 0x00, 0x00, 0x00, /* 0x4c - 0x4f */ +0x00, 0x00, 0x00, 0x00, /* 0x50 - 0x53 */ +0x00, 0x00, 0x00, 0x00, /* 0x54 - 0x57 */ +0x00, 0x00, 0x00, 0x00, /* 0x58 - 0x5b */ +0x00, 0x00, 0x00, 0x00, /* 0x5c - 0x5f */ +0x00, 0x00, 0x00, 0x00, /* 0x60 - 0x63 */ +0x00, 0x00, 0x00, 0x00, /* 0x64 - 0x67 */ +0x00, 0x00, 0x00, 0x00, /* 0x68 - 0x6b */ +0x00, 0x00, 0x00, 0x00, /* 0x6c - 0x6f */ +0x00, 0x00, 0x00, 0x00, /* 0x70 - 0x73 */ +0x00, 0x00, 0x00, 0x00, /* 0x74 - 0x77 */ +0x00, 0x00, 0x00, 0x00, /* 0x78 - 0x7b */ +0x00, /* 0x7c */ + + 0xda, 0x33, 0x03, /* 0x7d - 0x7f */ +}; + +/* Register read and write */ +static inline unsigned int dac33_read_reg_cache(struct snd_soc_component *component, + unsigned reg) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + u8 *cache = dac33->reg_cache; + if (reg >= DAC33_CACHEREGNUM) + return 0; + + return cache[reg]; +} + +static inline void dac33_write_reg_cache(struct snd_soc_component *component, + u8 reg, u8 value) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + u8 *cache = dac33->reg_cache; + if (reg >= DAC33_CACHEREGNUM) + return; + + cache[reg] = value; +} + +static int dac33_read(struct snd_soc_component *component, unsigned int reg, + u8 *value) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + int val, ret = 0; + + *value = reg & 0xff; + + /* If powered off, return the cached value */ + if (dac33->chip_power) { + val = i2c_smbus_read_byte_data(dac33->i2c, value[0]); + if (val < 0) { + dev_err(component->dev, "Read failed (%d)\n", val); + value[0] = dac33_read_reg_cache(component, reg); + ret = val; + } else { + value[0] = val; + dac33_write_reg_cache(component, reg, val); + } + } else { + value[0] = dac33_read_reg_cache(component, reg); + } + + return ret; +} + +static int dac33_write(struct snd_soc_component *component, unsigned int reg, + unsigned int value) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + u8 data[2]; + int ret = 0; + + /* + * data is + * D15..D8 dac33 register offset + * D7...D0 register data + */ + data[0] = reg & 0xff; + data[1] = value & 0xff; + + dac33_write_reg_cache(component, data[0], data[1]); + if (dac33->chip_power) { + ret = i2c_master_send(dac33->i2c, data, 2); + if (ret != 2) + dev_err(component->dev, "Write failed (%d)\n", ret); + else + ret = 0; + } + + return ret; +} + +static int dac33_write_locked(struct snd_soc_component *component, unsigned int reg, + unsigned int value) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + int ret; + + mutex_lock(&dac33->mutex); + ret = dac33_write(component, reg, value); + mutex_unlock(&dac33->mutex); + + return ret; +} + +#define DAC33_I2C_ADDR_AUTOINC 0x80 +static int dac33_write16(struct snd_soc_component *component, unsigned int reg, + unsigned int value) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + u8 data[3]; + int ret = 0; + + /* + * data is + * D23..D16 dac33 register offset + * D15..D8 register data MSB + * D7...D0 register data LSB + */ + data[0] = reg & 0xff; + data[1] = (value >> 8) & 0xff; + data[2] = value & 0xff; + + dac33_write_reg_cache(component, data[0], data[1]); + dac33_write_reg_cache(component, data[0] + 1, data[2]); + + if (dac33->chip_power) { + /* We need to set autoincrement mode for 16 bit writes */ + data[0] |= DAC33_I2C_ADDR_AUTOINC; + ret = i2c_master_send(dac33->i2c, data, 3); + if (ret != 3) + dev_err(component->dev, "Write failed (%d)\n", ret); + else + ret = 0; + } + + return ret; +} + +static void dac33_init_chip(struct snd_soc_component *component) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + + if (unlikely(!dac33->chip_power)) + return; + + /* A : DAC sample rate Fsref/1.5 */ + dac33_write(component, DAC33_DAC_CTRL_A, DAC33_DACRATE(0)); + /* B : DAC src=normal, not muted */ + dac33_write(component, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT | + DAC33_DACSRCL_LEFT); + /* C : (defaults) */ + dac33_write(component, DAC33_DAC_CTRL_C, 0x00); + + /* 73 : volume soft stepping control, + clock source = internal osc (?) */ + dac33_write(component, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN); + + /* Restore only selected registers (gains mostly) */ + dac33_write(component, DAC33_LDAC_DIG_VOL_CTRL, + dac33_read_reg_cache(component, DAC33_LDAC_DIG_VOL_CTRL)); + dac33_write(component, DAC33_RDAC_DIG_VOL_CTRL, + dac33_read_reg_cache(component, DAC33_RDAC_DIG_VOL_CTRL)); + + dac33_write(component, DAC33_LINEL_TO_LLO_VOL, + dac33_read_reg_cache(component, DAC33_LINEL_TO_LLO_VOL)); + dac33_write(component, DAC33_LINER_TO_RLO_VOL, + dac33_read_reg_cache(component, DAC33_LINER_TO_RLO_VOL)); + + dac33_write(component, DAC33_OUT_AMP_CTRL, + dac33_read_reg_cache(component, DAC33_OUT_AMP_CTRL)); + + dac33_write(component, DAC33_LDAC_PWR_CTRL, + dac33_read_reg_cache(component, DAC33_LDAC_PWR_CTRL)); + dac33_write(component, DAC33_RDAC_PWR_CTRL, + dac33_read_reg_cache(component, DAC33_RDAC_PWR_CTRL)); +} + +static inline int dac33_read_id(struct snd_soc_component *component) +{ + int i, ret = 0; + u8 reg; + + for (i = 0; i < 3; i++) { + ret = dac33_read(component, DAC33_DEVICE_ID_MSB + i, ®); + if (ret < 0) + break; + } + + return ret; +} + +static inline void dac33_soft_power(struct snd_soc_component *component, int power) +{ + u8 reg; + + reg = dac33_read_reg_cache(component, DAC33_PWR_CTRL); + if (power) + reg |= DAC33_PDNALLB; + else + reg &= ~(DAC33_PDNALLB | DAC33_OSCPDNB | + DAC33_DACRPDNB | DAC33_DACLPDNB); + dac33_write(component, DAC33_PWR_CTRL, reg); +} + +static inline void dac33_disable_digital(struct snd_soc_component *component) +{ + u8 reg; + + /* Stop the DAI clock */ + reg = dac33_read_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_B); + reg &= ~DAC33_BCLKON; + dac33_write(component, DAC33_SER_AUDIOIF_CTRL_B, reg); + + /* Power down the Oscillator, and DACs */ + reg = dac33_read_reg_cache(component, DAC33_PWR_CTRL); + reg &= ~(DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB); + dac33_write(component, DAC33_PWR_CTRL, reg); +} + +static int dac33_hard_power(struct snd_soc_component *component, int power) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + int ret = 0; + + mutex_lock(&dac33->mutex); + + /* Safety check */ + if (unlikely(power == dac33->chip_power)) { + dev_dbg(component->dev, "Trying to set the same power state: %s\n", + power ? "ON" : "OFF"); + goto exit; + } + + if (power) { + ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies), + dac33->supplies); + if (ret != 0) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", ret); + goto exit; + } + + if (dac33->power_gpio >= 0) + gpio_set_value(dac33->power_gpio, 1); + + dac33->chip_power = 1; + } else { + dac33_soft_power(component, 0); + if (dac33->power_gpio >= 0) + gpio_set_value(dac33->power_gpio, 0); + + ret = regulator_bulk_disable(ARRAY_SIZE(dac33->supplies), + dac33->supplies); + if (ret != 0) { + dev_err(component->dev, + "Failed to disable supplies: %d\n", ret); + goto exit; + } + + dac33->chip_power = 0; + } + +exit: + mutex_unlock(&dac33->mutex); + return ret; +} + +static int dac33_playback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (likely(dac33->substream)) { + dac33_calculate_times(dac33->substream, component); + dac33_prepare_chip(dac33->substream, component); + } + break; + case SND_SOC_DAPM_POST_PMD: + dac33_disable_digital(component); + break; + } + return 0; +} + +static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = dac33->fifo_mode; + + return 0; +} + +static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + int ret = 0; + + if (dac33->fifo_mode == ucontrol->value.enumerated.item[0]) + return 0; + /* Do not allow changes while stream is running*/ + if (snd_soc_component_active(component)) + return -EPERM; + + if (ucontrol->value.enumerated.item[0] >= DAC33_FIFO_LAST_MODE) + ret = -EINVAL; + else + dac33->fifo_mode = ucontrol->value.enumerated.item[0]; + + return ret; +} + +/* Codec operation modes */ +static const char *dac33_fifo_mode_texts[] = { + "Bypass", "Mode 1", "Mode 7" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(dac33_fifo_mode_enum, dac33_fifo_mode_texts); + +/* L/R Line Output Gain */ +static const char *lr_lineout_gain_texts[] = { + "Line -12dB DAC 0dB", "Line -6dB DAC 6dB", + "Line 0dB DAC 12dB", "Line 6dB DAC 18dB", +}; + +static SOC_ENUM_SINGLE_DECL(l_lineout_gain_enum, + DAC33_LDAC_PWR_CTRL, 0, + lr_lineout_gain_texts); + +static SOC_ENUM_SINGLE_DECL(r_lineout_gain_enum, + DAC33_RDAC_PWR_CTRL, 0, + lr_lineout_gain_texts); + +/* + * DACL/R digital volume control: + * from 0 dB to -63.5 in 0.5 dB steps + * Need to be inverted later on: + * 0x00 == 0 dB + * 0x7f == -63.5 dB + */ +static DECLARE_TLV_DB_SCALE(dac_digivol_tlv, -6350, 50, 0); + +static const struct snd_kcontrol_new dac33_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC Digital Playback Volume", + DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL, + 0, 0x7f, 1, dac_digivol_tlv), + SOC_DOUBLE_R("DAC Digital Playback Switch", + DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL, 7, 1, 1), + SOC_DOUBLE_R("Line to Line Out Volume", + DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1), + SOC_ENUM("Left Line Output Gain", l_lineout_gain_enum), + SOC_ENUM("Right Line Output Gain", r_lineout_gain_enum), +}; + +static const struct snd_kcontrol_new dac33_mode_snd_controls[] = { + SOC_ENUM_EXT("FIFO Mode", dac33_fifo_mode_enum, + dac33_get_fifo_mode, dac33_set_fifo_mode), +}; + +/* Analog bypass */ +static const struct snd_kcontrol_new dac33_dapm_abypassl_control = + SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1); + +static const struct snd_kcontrol_new dac33_dapm_abypassr_control = + SOC_DAPM_SINGLE("Switch", DAC33_LINER_TO_RLO_VOL, 7, 1, 1); + +/* LOP L/R invert selection */ +static const char *dac33_lr_lom_texts[] = {"DAC", "LOP"}; + +static SOC_ENUM_SINGLE_DECL(dac33_left_lom_enum, + DAC33_OUT_AMP_CTRL, 3, + dac33_lr_lom_texts); + +static const struct snd_kcontrol_new dac33_dapm_left_lom_control = +SOC_DAPM_ENUM("Route", dac33_left_lom_enum); + +static SOC_ENUM_SINGLE_DECL(dac33_right_lom_enum, + DAC33_OUT_AMP_CTRL, 2, + dac33_lr_lom_texts); + +static const struct snd_kcontrol_new dac33_dapm_right_lom_control = +SOC_DAPM_ENUM("Route", dac33_right_lom_enum); + +static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("LEFT_LO"), + SND_SOC_DAPM_OUTPUT("RIGHT_LO"), + + SND_SOC_DAPM_INPUT("LINEL"), + SND_SOC_DAPM_INPUT("LINER"), + + SND_SOC_DAPM_DAC("DACL", "Left Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DACR", "Right Playback", SND_SOC_NOPM, 0, 0), + + /* Analog bypass */ + SND_SOC_DAPM_SWITCH("Analog Left Bypass", SND_SOC_NOPM, 0, 0, + &dac33_dapm_abypassl_control), + SND_SOC_DAPM_SWITCH("Analog Right Bypass", SND_SOC_NOPM, 0, 0, + &dac33_dapm_abypassr_control), + + SND_SOC_DAPM_MUX("Left LOM Inverted From", SND_SOC_NOPM, 0, 0, + &dac33_dapm_left_lom_control), + SND_SOC_DAPM_MUX("Right LOM Inverted From", SND_SOC_NOPM, 0, 0, + &dac33_dapm_right_lom_control), + /* + * For DAPM path, when only the anlog bypass path is enabled, and the + * LOP inverted from the corresponding DAC side. + * This is needed, so we can attach the DAC power supply in this case. + */ + SND_SOC_DAPM_PGA("Left Bypass PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Bypass PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Left Amplifier", + DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0), + SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amplifier", + DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0), + + SND_SOC_DAPM_SUPPLY("Left DAC Power", + DAC33_LDAC_PWR_CTRL, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Right DAC Power", + DAC33_RDAC_PWR_CTRL, 2, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Codec Power", + DAC33_PWR_CTRL, 4, 0, NULL, 0), + + SND_SOC_DAPM_PRE("Pre Playback", dac33_playback_event), + SND_SOC_DAPM_POST("Post Playback", dac33_playback_event), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Analog bypass */ + {"Analog Left Bypass", "Switch", "LINEL"}, + {"Analog Right Bypass", "Switch", "LINER"}, + + {"Output Left Amplifier", NULL, "DACL"}, + {"Output Right Amplifier", NULL, "DACR"}, + + {"Left Bypass PGA", NULL, "Analog Left Bypass"}, + {"Right Bypass PGA", NULL, "Analog Right Bypass"}, + + {"Left LOM Inverted From", "DAC", "Left Bypass PGA"}, + {"Right LOM Inverted From", "DAC", "Right Bypass PGA"}, + {"Left LOM Inverted From", "LOP", "Analog Left Bypass"}, + {"Right LOM Inverted From", "LOP", "Analog Right Bypass"}, + + {"Output Left Amplifier", NULL, "Left LOM Inverted From"}, + {"Output Right Amplifier", NULL, "Right LOM Inverted From"}, + + {"DACL", NULL, "Left DAC Power"}, + {"DACR", NULL, "Right DAC Power"}, + + {"Left Bypass PGA", NULL, "Left DAC Power"}, + {"Right Bypass PGA", NULL, "Right DAC Power"}, + + /* output */ + {"LEFT_LO", NULL, "Output Left Amplifier"}, + {"RIGHT_LO", NULL, "Output Right Amplifier"}, + + {"LEFT_LO", NULL, "Codec Power"}, + {"RIGHT_LO", NULL, "Codec Power"}, +}; + +static int dac33_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Coming from OFF, switch on the component */ + ret = dac33_hard_power(component, 1); + if (ret != 0) + return ret; + + dac33_init_chip(component); + } + break; + case SND_SOC_BIAS_OFF: + /* Do not power off, when the component is already off */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + return 0; + ret = dac33_hard_power(component, 0); + if (ret != 0) + return ret; + break; + } + + return 0; +} + +static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33) +{ + struct snd_soc_component *component = dac33->component; + unsigned int delay; + unsigned long flags; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: + dac33_write16(component, DAC33_NSAMPLE_MSB, + DAC33_THRREG(dac33->nsample)); + + /* Take the timestamps */ + spin_lock_irqsave(&dac33->lock, flags); + dac33->t_stamp2 = ktime_to_us(ktime_get()); + dac33->t_stamp1 = dac33->t_stamp2; + spin_unlock_irqrestore(&dac33->lock, flags); + + dac33_write16(component, DAC33_PREFILL_MSB, + DAC33_THRREG(dac33->alarm_threshold)); + /* Enable Alarm Threshold IRQ with a delay */ + delay = SAMPLES_TO_US(dac33->burst_rate, + dac33->alarm_threshold) + 1000; + usleep_range(delay, delay + 500); + dac33_write(component, DAC33_FIFO_IRQ_MASK, DAC33_MAT); + break; + case DAC33_FIFO_MODE7: + /* Take the timestamp */ + spin_lock_irqsave(&dac33->lock, flags); + dac33->t_stamp1 = ktime_to_us(ktime_get()); + /* Move back the timestamp with drain time */ + dac33->t_stamp1 -= dac33->mode7_us_to_lthr; + spin_unlock_irqrestore(&dac33->lock, flags); + + dac33_write16(component, DAC33_PREFILL_MSB, + DAC33_THRREG(DAC33_MODE7_MARGIN)); + + /* Enable Upper Threshold IRQ */ + dac33_write(component, DAC33_FIFO_IRQ_MASK, DAC33_MUT); + break; + default: + dev_warn(component->dev, "Unhandled FIFO mode: %d\n", + dac33->fifo_mode); + break; + } +} + +static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33) +{ + struct snd_soc_component *component = dac33->component; + unsigned long flags; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: + /* Take the timestamp */ + spin_lock_irqsave(&dac33->lock, flags); + dac33->t_stamp2 = ktime_to_us(ktime_get()); + spin_unlock_irqrestore(&dac33->lock, flags); + + dac33_write16(component, DAC33_NSAMPLE_MSB, + DAC33_THRREG(dac33->nsample)); + break; + case DAC33_FIFO_MODE7: + /* At the moment we are not using interrupts in mode7 */ + break; + default: + dev_warn(component->dev, "Unhandled FIFO mode: %d\n", + dac33->fifo_mode); + break; + } +} + +static void dac33_work(struct work_struct *work) +{ + struct snd_soc_component *component; + struct tlv320dac33_priv *dac33; + u8 reg; + + dac33 = container_of(work, struct tlv320dac33_priv, work); + component = dac33->component; + + mutex_lock(&dac33->mutex); + switch (dac33->state) { + case DAC33_PREFILL: + dac33->state = DAC33_PLAYBACK; + dac33_prefill_handler(dac33); + break; + case DAC33_PLAYBACK: + dac33_playback_handler(dac33); + break; + case DAC33_IDLE: + break; + case DAC33_FLUSH: + dac33->state = DAC33_IDLE; + /* Mask all interrupts from dac33 */ + dac33_write(component, DAC33_FIFO_IRQ_MASK, 0); + + /* flush fifo */ + reg = dac33_read_reg_cache(component, DAC33_FIFO_CTRL_A); + reg |= DAC33_FIFOFLUSH; + dac33_write(component, DAC33_FIFO_CTRL_A, reg); + break; + } + mutex_unlock(&dac33->mutex); +} + +static irqreturn_t dac33_interrupt_handler(int irq, void *dev) +{ + struct snd_soc_component *component = dev; + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + unsigned long flags; + + spin_lock_irqsave(&dac33->lock, flags); + dac33->t_stamp1 = ktime_to_us(ktime_get()); + spin_unlock_irqrestore(&dac33->lock, flags); + + /* Do not schedule the workqueue in Mode7 */ + if (dac33->fifo_mode != DAC33_FIFO_MODE7) + schedule_work(&dac33->work); + + return IRQ_HANDLED; +} + +static void dac33_oscwait(struct snd_soc_component *component) +{ + int timeout = 60; + u8 reg; + + do { + usleep_range(1000, 2000); + dac33_read(component, DAC33_INT_OSC_STATUS, ®); + } while (((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) && timeout--); + if ((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) + dev_err(component->dev, + "internal oscillator calibration failed\n"); +} + +static int dac33_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + + /* Stream started, save the substream pointer */ + dac33->substream = substream; + + return 0; +} + +static void dac33_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + + dac33->substream = NULL; +} + +#define CALC_BURST_RATE(bclkdiv, bclk_per_sample) \ + (BURST_BASEFREQ_HZ / bclkdiv / bclk_per_sample) +static int dac33_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + + /* Check parameters for validity */ + switch (params_rate(params)) { + case 44100: + case 48000: + break; + default: + dev_err(component->dev, "unsupported rate %d\n", + params_rate(params)); + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + dac33->fifo_size = DAC33_FIFO_SIZE_16BIT; + dac33->burst_rate = CALC_BURST_RATE(dac33->burst_bclkdiv, 32); + break; + case 32: + dac33->fifo_size = DAC33_FIFO_SIZE_24BIT; + dac33->burst_rate = CALC_BURST_RATE(dac33->burst_bclkdiv, 64); + break; + default: + dev_err(component->dev, "unsupported width %d\n", + params_width(params)); + return -EINVAL; + } + + return 0; +} + +#define CALC_OSCSET(rate, refclk) ( \ + ((((rate * 10000) / refclk) * 4096) + 7000) / 10000) +#define CALC_RATIOSET(rate, refclk) ( \ + ((((refclk * 100000) / rate) * 16384) + 50000) / 100000) + +/* + * tlv320dac33 is strict on the sequence of the register writes, if the register + * writes happens in different order, than dac33 might end up in unknown state. + * Use the known, working sequence of register writes to initialize the dac33. + */ +static int dac33_prepare_chip(struct snd_pcm_substream *substream, + struct snd_soc_component *component) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + unsigned int oscset, ratioset, pwr_ctrl, reg_tmp; + u8 aictrl_a, aictrl_b, fifoctrl_a; + + switch (substream->runtime->rate) { + case 44100: + case 48000: + oscset = CALC_OSCSET(substream->runtime->rate, dac33->refclk); + ratioset = CALC_RATIOSET(substream->runtime->rate, + dac33->refclk); + break; + default: + dev_err(component->dev, "unsupported rate %d\n", + substream->runtime->rate); + return -EINVAL; + } + + + aictrl_a = dac33_read_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_A); + aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK); + /* Read FIFO control A, and clear FIFO flush bit */ + fifoctrl_a = dac33_read_reg_cache(component, DAC33_FIFO_CTRL_A); + fifoctrl_a &= ~DAC33_FIFOFLUSH; + + fifoctrl_a &= ~DAC33_WIDTH; + switch (substream->runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + aictrl_a |= (DAC33_NCYCL_16 | DAC33_WLEN_16); + fifoctrl_a |= DAC33_WIDTH; + break; + case SNDRV_PCM_FORMAT_S32_LE: + aictrl_a |= (DAC33_NCYCL_32 | DAC33_WLEN_24); + break; + default: + dev_err(component->dev, "unsupported format %d\n", + substream->runtime->format); + return -EINVAL; + } + + mutex_lock(&dac33->mutex); + + if (!dac33->chip_power) { + /* + * Chip is not powered yet. + * Do the init in the dac33_set_bias_level later. + */ + mutex_unlock(&dac33->mutex); + return 0; + } + + dac33_soft_power(component, 0); + dac33_soft_power(component, 1); + + reg_tmp = dac33_read_reg_cache(component, DAC33_INT_OSC_CTRL); + dac33_write(component, DAC33_INT_OSC_CTRL, reg_tmp); + + /* Write registers 0x08 and 0x09 (MSB, LSB) */ + dac33_write16(component, DAC33_INT_OSC_FREQ_RAT_A, oscset); + + /* OSC calibration time */ + dac33_write(component, DAC33_CALIB_TIME, 96); + + /* adjustment treshold & step */ + dac33_write(component, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) | + DAC33_ADJSTEP(1)); + + /* div=4 / gain=1 / div */ + dac33_write(component, DAC33_INT_OSC_CTRL_C, DAC33_REFDIV(4)); + + pwr_ctrl = dac33_read_reg_cache(component, DAC33_PWR_CTRL); + pwr_ctrl |= DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB; + dac33_write(component, DAC33_PWR_CTRL, pwr_ctrl); + + dac33_oscwait(component); + + if (dac33->fifo_mode) { + /* Generic for all FIFO modes */ + /* 50-51 : ASRC Control registers */ + dac33_write(component, DAC33_ASRC_CTRL_A, DAC33_SRCLKDIV(1)); + dac33_write(component, DAC33_ASRC_CTRL_B, 1); /* ??? */ + + /* Write registers 0x34 and 0x35 (MSB, LSB) */ + dac33_write16(component, DAC33_SRC_REF_CLK_RATIO_A, ratioset); + + /* Set interrupts to high active */ + dac33_write(component, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH); + } else { + /* FIFO bypass mode */ + /* 50-51 : ASRC Control registers */ + dac33_write(component, DAC33_ASRC_CTRL_A, DAC33_SRCBYP); + dac33_write(component, DAC33_ASRC_CTRL_B, 0); /* ??? */ + } + + /* Interrupt behaviour configuration */ + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: + dac33_write(component, DAC33_FIFO_IRQ_MODE_B, + DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL)); + break; + case DAC33_FIFO_MODE7: + dac33_write(component, DAC33_FIFO_IRQ_MODE_A, + DAC33_UTM(DAC33_FIFO_IRQ_MODE_LEVEL)); + break; + default: + /* in FIFO bypass mode, the interrupts are not used */ + break; + } + + aictrl_b = dac33_read_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_B); + + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: + /* + * For mode1: + * Disable the FIFO bypass (Enable the use of FIFO) + * Select nSample mode + * BCLK is only running when data is needed by DAC33 + */ + fifoctrl_a &= ~DAC33_FBYPAS; + fifoctrl_a &= ~DAC33_FAUTO; + if (dac33->keep_bclk) + aictrl_b |= DAC33_BCLKON; + else + aictrl_b &= ~DAC33_BCLKON; + break; + case DAC33_FIFO_MODE7: + /* + * For mode1: + * Disable the FIFO bypass (Enable the use of FIFO) + * Select Threshold mode + * BCLK is only running when data is needed by DAC33 + */ + fifoctrl_a &= ~DAC33_FBYPAS; + fifoctrl_a |= DAC33_FAUTO; + if (dac33->keep_bclk) + aictrl_b |= DAC33_BCLKON; + else + aictrl_b &= ~DAC33_BCLKON; + break; + default: + /* + * For FIFO bypass mode: + * Enable the FIFO bypass (Disable the FIFO use) + * Set the BCLK as continuous + */ + fifoctrl_a |= DAC33_FBYPAS; + aictrl_b |= DAC33_BCLKON; + break; + } + + dac33_write(component, DAC33_FIFO_CTRL_A, fifoctrl_a); + dac33_write(component, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a); + dac33_write(component, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b); + + /* + * BCLK divide ratio + * 0: 1.5 + * 1: 1 + * 2: 2 + * ... + * 254: 254 + * 255: 255 + */ + if (dac33->fifo_mode) + dac33_write(component, DAC33_SER_AUDIOIF_CTRL_C, + dac33->burst_bclkdiv); + else + if (substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE) + dac33_write(component, DAC33_SER_AUDIOIF_CTRL_C, 32); + else + dac33_write(component, DAC33_SER_AUDIOIF_CTRL_C, 16); + + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: + dac33_write16(component, DAC33_ATHR_MSB, + DAC33_THRREG(dac33->alarm_threshold)); + break; + case DAC33_FIFO_MODE7: + /* + * Configure the threshold levels, and leave 10 sample space + * at the bottom, and also at the top of the FIFO + */ + dac33_write16(component, DAC33_UTHR_MSB, DAC33_THRREG(dac33->uthr)); + dac33_write16(component, DAC33_LTHR_MSB, + DAC33_THRREG(DAC33_MODE7_MARGIN)); + break; + default: + break; + } + + mutex_unlock(&dac33->mutex); + + return 0; +} + +static void dac33_calculate_times(struct snd_pcm_substream *substream, + struct snd_soc_component *component) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + unsigned int period_size = substream->runtime->period_size; + unsigned int rate = substream->runtime->rate; + unsigned int nsample_limit; + + /* In bypass mode we don't need to calculate */ + if (!dac33->fifo_mode) + return; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_MODE1: + /* Number of samples under i2c latency */ + dac33->alarm_threshold = US_TO_SAMPLES(rate, + dac33->mode1_latency); + nsample_limit = dac33->fifo_size - dac33->alarm_threshold; + + if (period_size <= dac33->alarm_threshold) + /* + * Configure nSamaple to number of periods, + * which covers the latency requironment. + */ + dac33->nsample = period_size * + ((dac33->alarm_threshold / period_size) + + ((dac33->alarm_threshold % period_size) ? + 1 : 0)); + else if (period_size > nsample_limit) + dac33->nsample = nsample_limit; + else + dac33->nsample = period_size; + + dac33->mode1_us_burst = SAMPLES_TO_US(dac33->burst_rate, + dac33->nsample); + dac33->t_stamp1 = 0; + dac33->t_stamp2 = 0; + break; + case DAC33_FIFO_MODE7: + dac33->uthr = UTHR_FROM_PERIOD_SIZE(period_size, rate, + dac33->burst_rate) + 9; + if (dac33->uthr > (dac33->fifo_size - DAC33_MODE7_MARGIN)) + dac33->uthr = dac33->fifo_size - DAC33_MODE7_MARGIN; + if (dac33->uthr < (DAC33_MODE7_MARGIN + 10)) + dac33->uthr = (DAC33_MODE7_MARGIN + 10); + + dac33->mode7_us_to_lthr = + SAMPLES_TO_US(substream->runtime->rate, + dac33->uthr - DAC33_MODE7_MARGIN + 1); + dac33->t_stamp1 = 0; + break; + default: + break; + } + +} + +static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (dac33->fifo_mode) { + dac33->state = DAC33_PREFILL; + schedule_work(&dac33->work); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (dac33->fifo_mode) { + dac33->state = DAC33_FLUSH; + schedule_work(&dac33->work); + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static snd_pcm_sframes_t dac33_dai_delay( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + unsigned long long t0, t1, t_now; + unsigned int time_delta, uthr; + int samples_out, samples_in, samples; + snd_pcm_sframes_t delay = 0; + unsigned long flags; + + switch (dac33->fifo_mode) { + case DAC33_FIFO_BYPASS: + break; + case DAC33_FIFO_MODE1: + spin_lock_irqsave(&dac33->lock, flags); + t0 = dac33->t_stamp1; + t1 = dac33->t_stamp2; + spin_unlock_irqrestore(&dac33->lock, flags); + t_now = ktime_to_us(ktime_get()); + + /* We have not started to fill the FIFO yet, delay is 0 */ + if (!t1) + goto out; + + if (t0 > t1) { + /* + * Phase 1: + * After Alarm threshold, and before nSample write + */ + time_delta = t_now - t0; + samples_out = time_delta ? US_TO_SAMPLES( + substream->runtime->rate, + time_delta) : 0; + + if (likely(dac33->alarm_threshold > samples_out)) + delay = dac33->alarm_threshold - samples_out; + else + delay = 0; + } else if ((t_now - t1) <= dac33->mode1_us_burst) { + /* + * Phase 2: + * After nSample write (during burst operation) + */ + time_delta = t_now - t0; + samples_out = time_delta ? US_TO_SAMPLES( + substream->runtime->rate, + time_delta) : 0; + + time_delta = t_now - t1; + samples_in = time_delta ? US_TO_SAMPLES( + dac33->burst_rate, + time_delta) : 0; + + samples = dac33->alarm_threshold; + samples += (samples_in - samples_out); + + if (likely(samples > 0)) + delay = samples; + else + delay = 0; + } else { + /* + * Phase 3: + * After burst operation, before next alarm threshold + */ + time_delta = t_now - t0; + samples_out = time_delta ? US_TO_SAMPLES( + substream->runtime->rate, + time_delta) : 0; + + samples_in = dac33->nsample; + samples = dac33->alarm_threshold; + samples += (samples_in - samples_out); + + if (likely(samples > 0)) + delay = samples > dac33->fifo_size ? + dac33->fifo_size : samples; + else + delay = 0; + } + break; + case DAC33_FIFO_MODE7: + spin_lock_irqsave(&dac33->lock, flags); + t0 = dac33->t_stamp1; + uthr = dac33->uthr; + spin_unlock_irqrestore(&dac33->lock, flags); + t_now = ktime_to_us(ktime_get()); + + /* We have not started to fill the FIFO yet, delay is 0 */ + if (!t0) + goto out; + + if (t_now <= t0) { + /* + * Either the timestamps are messed or equal. Report + * maximum delay + */ + delay = uthr; + goto out; + } + + time_delta = t_now - t0; + if (time_delta <= dac33->mode7_us_to_lthr) { + /* + * Phase 1: + * After burst (draining phase) + */ + samples_out = US_TO_SAMPLES( + substream->runtime->rate, + time_delta); + + if (likely(uthr > samples_out)) + delay = uthr - samples_out; + else + delay = 0; + } else { + /* + * Phase 2: + * During burst operation + */ + time_delta = time_delta - dac33->mode7_us_to_lthr; + + samples_out = US_TO_SAMPLES( + substream->runtime->rate, + time_delta); + samples_in = US_TO_SAMPLES( + dac33->burst_rate, + time_delta); + delay = DAC33_MODE7_MARGIN + samples_in - samples_out; + + if (unlikely(delay > uthr)) + delay = uthr; + } + break; + default: + dev_warn(component->dev, "Unhandled FIFO mode: %d\n", + dac33->fifo_mode); + break; + } +out: + return delay; +} + +static int dac33_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + u8 ioc_reg, asrcb_reg; + + ioc_reg = dac33_read_reg_cache(component, DAC33_INT_OSC_CTRL); + asrcb_reg = dac33_read_reg_cache(component, DAC33_ASRC_CTRL_B); + switch (clk_id) { + case TLV320DAC33_MCLK: + ioc_reg |= DAC33_REFSEL; + asrcb_reg |= DAC33_SRCREFSEL; + break; + case TLV320DAC33_SLEEPCLK: + ioc_reg &= ~DAC33_REFSEL; + asrcb_reg &= ~DAC33_SRCREFSEL; + break; + default: + dev_err(component->dev, "Invalid clock ID (%d)\n", clk_id); + break; + } + dac33->refclk = freq; + + dac33_write_reg_cache(component, DAC33_INT_OSC_CTRL, ioc_reg); + dac33_write_reg_cache(component, DAC33_ASRC_CTRL_B, asrcb_reg); + + return 0; +} + +static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + u8 aictrl_a, aictrl_b; + + aictrl_a = dac33_read_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_A); + aictrl_b = dac33_read_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_B); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + aictrl_a |= (DAC33_MSBCLK | DAC33_MSWCLK); + break; + case SND_SOC_DAIFMT_CBC_CFC: + if (dac33->fifo_mode) { + dev_err(component->dev, "FIFO mode requires provider mode\n"); + return -EINVAL; + } else + aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK); + break; + default: + return -EINVAL; + } + + aictrl_a &= ~DAC33_AFMT_MASK; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + aictrl_a |= DAC33_AFMT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + aictrl_a |= DAC33_AFMT_DSP; + aictrl_b &= ~DAC33_DATA_DELAY_MASK; + aictrl_b |= DAC33_DATA_DELAY(0); + break; + case SND_SOC_DAIFMT_RIGHT_J: + aictrl_a |= DAC33_AFMT_RIGHT_J; + break; + case SND_SOC_DAIFMT_LEFT_J: + aictrl_a |= DAC33_AFMT_LEFT_J; + break; + default: + dev_err(component->dev, "Unsupported format (%u)\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + dac33_write_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a); + dac33_write_reg_cache(component, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b); + + return 0; +} + +static int dac33_soc_probe(struct snd_soc_component *component) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + int ret = 0; + + dac33->component = component; + + /* Read the tlv320dac33 ID registers */ + ret = dac33_hard_power(component, 1); + if (ret != 0) { + dev_err(component->dev, "Failed to power up component: %d\n", ret); + goto err_power; + } + ret = dac33_read_id(component); + dac33_hard_power(component, 0); + + if (ret < 0) { + dev_err(component->dev, "Failed to read chip ID: %d\n", ret); + ret = -ENODEV; + goto err_power; + } + + /* Check if the IRQ number is valid and request it */ + if (dac33->irq >= 0) { + ret = request_irq(dac33->irq, dac33_interrupt_handler, + IRQF_TRIGGER_RISING, + component->name, component); + if (ret < 0) { + dev_err(component->dev, "Could not request IRQ%d (%d)\n", + dac33->irq, ret); + dac33->irq = -1; + } + if (dac33->irq != -1) { + INIT_WORK(&dac33->work, dac33_work); + } + } + + /* Only add the FIFO controls, if we have valid IRQ number */ + if (dac33->irq >= 0) + snd_soc_add_component_controls(component, dac33_mode_snd_controls, + ARRAY_SIZE(dac33_mode_snd_controls)); + +err_power: + return ret; +} + +static void dac33_soc_remove(struct snd_soc_component *component) +{ + struct tlv320dac33_priv *dac33 = snd_soc_component_get_drvdata(component); + + if (dac33->irq >= 0) { + free_irq(dac33->irq, dac33->component); + flush_work(&dac33->work); + } +} + +static const struct snd_soc_component_driver soc_component_dev_tlv320dac33 = { + .read = dac33_read_reg_cache, + .write = dac33_write_locked, + .set_bias_level = dac33_set_bias_level, + .probe = dac33_soc_probe, + .remove = dac33_soc_remove, + .controls = dac33_snd_controls, + .num_controls = ARRAY_SIZE(dac33_snd_controls), + .dapm_widgets = dac33_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dac33_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; + +#define DAC33_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) +#define DAC33_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops dac33_dai_ops = { + .startup = dac33_startup, + .shutdown = dac33_shutdown, + .hw_params = dac33_hw_params, + .trigger = dac33_pcm_trigger, + .delay = dac33_dai_delay, + .set_sysclk = dac33_set_dai_sysclk, + .set_fmt = dac33_set_dai_fmt, +}; + +static struct snd_soc_dai_driver dac33_dai = { + .name = "tlv320dac33-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = DAC33_RATES, + .formats = DAC33_FORMATS, + .sig_bits = 24, + }, + .ops = &dac33_dai_ops, +}; + +static int dac33_i2c_probe(struct i2c_client *client) +{ + struct tlv320dac33_platform_data *pdata; + struct tlv320dac33_priv *dac33; + int ret, i; + + if (client->dev.platform_data == NULL) { + dev_err(&client->dev, "Platform data not set\n"); + return -ENODEV; + } + pdata = client->dev.platform_data; + + dac33 = devm_kzalloc(&client->dev, sizeof(struct tlv320dac33_priv), + GFP_KERNEL); + if (dac33 == NULL) + return -ENOMEM; + + dac33->reg_cache = devm_kmemdup(&client->dev, + dac33_reg, + ARRAY_SIZE(dac33_reg) * sizeof(u8), + GFP_KERNEL); + if (!dac33->reg_cache) + return -ENOMEM; + + dac33->i2c = client; + mutex_init(&dac33->mutex); + spin_lock_init(&dac33->lock); + + i2c_set_clientdata(client, dac33); + + dac33->power_gpio = pdata->power_gpio; + dac33->burst_bclkdiv = pdata->burst_bclkdiv; + dac33->keep_bclk = pdata->keep_bclk; + dac33->mode1_latency = pdata->mode1_latency; + if (!dac33->mode1_latency) + dac33->mode1_latency = 10000; /* 10ms */ + dac33->irq = client->irq; + /* Disable FIFO use by default */ + dac33->fifo_mode = DAC33_FIFO_BYPASS; + + /* Check if the reset GPIO number is valid and request it */ + if (dac33->power_gpio >= 0) { + ret = gpio_request(dac33->power_gpio, "tlv320dac33 reset"); + if (ret < 0) { + dev_err(&client->dev, + "Failed to request reset GPIO (%d)\n", + dac33->power_gpio); + goto err_gpio; + } + gpio_direction_output(dac33->power_gpio, 0); + } + + for (i = 0; i < ARRAY_SIZE(dac33->supplies); i++) + dac33->supplies[i].supply = dac33_supply_names[i]; + + ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(dac33->supplies), + dac33->supplies); + + if (ret != 0) { + dev_err(&client->dev, "Failed to request supplies: %d\n", ret); + goto err_get; + } + + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_tlv320dac33, &dac33_dai, 1); + if (ret < 0) + goto err_get; + + return ret; +err_get: + if (dac33->power_gpio >= 0) + gpio_free(dac33->power_gpio); +err_gpio: + return ret; +} + +static void dac33_i2c_remove(struct i2c_client *client) +{ + struct tlv320dac33_priv *dac33 = i2c_get_clientdata(client); + + if (unlikely(dac33->chip_power)) + dac33_hard_power(dac33->component, 0); + + if (dac33->power_gpio >= 0) + gpio_free(dac33->power_gpio); +} + +static const struct i2c_device_id tlv320dac33_i2c_id[] = { + { + .name = "tlv320dac33", + .driver_data = 0, + }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id); + +static struct i2c_driver tlv320dac33_i2c_driver = { + .driver = { + .name = "tlv320dac33-codec", + }, + .probe = dac33_i2c_probe, + .remove = dac33_i2c_remove, + .id_table = tlv320dac33_i2c_id, +}; + +module_i2c_driver(tlv320dac33_i2c_driver); + +MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver"); +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h new file mode 100644 index 0000000000..ba2c2d89c7 --- /dev/null +++ b/sound/soc/codecs/tlv320dac33.h @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC Texas Instruments TLV320DAC33 codec driver + * + * Author: Peter Ujfalusi + * + * Copyright: (C) 2009 Nokia Corporation + */ + +#ifndef __TLV320DAC33_H +#define __TLV320DAC33_H + +#define DAC33_PAGE_SELECT 0x00 +#define DAC33_PWR_CTRL 0x01 +#define DAC33_PLL_CTRL_A 0x02 +#define DAC33_PLL_CTRL_B 0x03 +#define DAC33_PLL_CTRL_C 0x04 +#define DAC33_PLL_CTRL_D 0x05 +#define DAC33_PLL_CTRL_E 0x06 +#define DAC33_INT_OSC_CTRL 0x07 +#define DAC33_INT_OSC_FREQ_RAT_A 0x08 +#define DAC33_INT_OSC_FREQ_RAT_B 0x09 +#define DAC33_INT_OSC_DAC_RATIO_SET 0x0A +#define DAC33_CALIB_TIME 0x0B +#define DAC33_INT_OSC_CTRL_B 0x0C +#define DAC33_INT_OSC_CTRL_C 0x0D +#define DAC33_INT_OSC_STATUS 0x0E +#define DAC33_INT_OSC_DAC_RATIO_READ 0x0F +#define DAC33_INT_OSC_FREQ_RAT_READ_A 0x10 +#define DAC33_INT_OSC_FREQ_RAT_READ_B 0x11 +#define DAC33_SER_AUDIOIF_CTRL_A 0x12 +#define DAC33_SER_AUDIOIF_CTRL_B 0x13 +#define DAC33_SER_AUDIOIF_CTRL_C 0x14 +#define DAC33_FIFO_CTRL_A 0x15 +#define DAC33_UTHR_MSB 0x16 +#define DAC33_UTHR_LSB 0x17 +#define DAC33_ATHR_MSB 0x18 +#define DAC33_ATHR_LSB 0x19 +#define DAC33_LTHR_MSB 0x1A +#define DAC33_LTHR_LSB 0x1B +#define DAC33_PREFILL_MSB 0x1C +#define DAC33_PREFILL_LSB 0x1D +#define DAC33_NSAMPLE_MSB 0x1E +#define DAC33_NSAMPLE_LSB 0x1F +#define DAC33_FIFO_WPTR_MSB 0x20 +#define DAC33_FIFO_WPTR_LSB 0x21 +#define DAC33_FIFO_RPTR_MSB 0x22 +#define DAC33_FIFO_RPTR_LSB 0x23 +#define DAC33_FIFO_DEPTH_MSB 0x24 +#define DAC33_FIFO_DEPTH_LSB 0x25 +#define DAC33_SAMPLES_REMAINING_MSB 0x26 +#define DAC33_SAMPLES_REMAINING_LSB 0x27 +#define DAC33_FIFO_IRQ_FLAG 0x28 +#define DAC33_FIFO_IRQ_MASK 0x29 +#define DAC33_FIFO_IRQ_MODE_A 0x2A +#define DAC33_FIFO_IRQ_MODE_B 0x2B +#define DAC33_DAC_CTRL_A 0x2C +#define DAC33_DAC_CTRL_B 0x2D +#define DAC33_DAC_CTRL_C 0x2E +#define DAC33_LDAC_DIG_VOL_CTRL 0x2F +#define DAC33_RDAC_DIG_VOL_CTRL 0x30 +#define DAC33_DAC_STATUS_FLAGS 0x31 +#define DAC33_ASRC_CTRL_A 0x32 +#define DAC33_ASRC_CTRL_B 0x33 +#define DAC33_SRC_REF_CLK_RATIO_A 0x34 +#define DAC33_SRC_REF_CLK_RATIO_B 0x35 +#define DAC33_SRC_EST_REF_CLK_RATIO_A 0x36 +#define DAC33_SRC_EST_REF_CLK_RATIO_B 0x37 +#define DAC33_INTP_CTRL_A 0x38 +#define DAC33_INTP_CTRL_B 0x39 +/* Registers 0x3A - 0x3F Reserved */ +#define DAC33_LDAC_PWR_CTRL 0x40 +#define DAC33_RDAC_PWR_CTRL 0x41 +#define DAC33_OUT_AMP_CM_CTRL 0x42 +#define DAC33_OUT_AMP_PWR_CTRL 0x43 +#define DAC33_OUT_AMP_CTRL 0x44 +#define DAC33_LINEL_TO_LLO_VOL 0x45 +/* Registers 0x45 - 0x47 Reserved */ +#define DAC33_LINER_TO_RLO_VOL 0x48 +#define DAC33_ANA_VOL_SOFT_STEP_CTRL 0x49 +#define DAC33_OSC_TRIM 0x4A +/* Registers 0x4B - 0x7C Reserved */ +#define DAC33_DEVICE_ID_MSB 0x7D +#define DAC33_DEVICE_ID_LSB 0x7E +#define DAC33_DEVICE_REV_ID 0x7F + +#define DAC33_CACHEREGNUM 128 + +/* Bit definitions */ + +/* DAC33_PWR_CTRL (0x01) */ +#define DAC33_DACRPDNB (0x01 << 0) +#define DAC33_DACLPDNB (0x01 << 1) +#define DAC33_OSCPDNB (0x01 << 2) +#define DAC33_PLLPDNB (0x01 << 3) +#define DAC33_PDNALLB (0x01 << 4) +#define DAC33_SOFT_RESET (0x01 << 7) + +/* DAC33_INT_OSC_CTRL (0x07) */ +#define DAC33_REFSEL (0x01 << 1) + +/* DAC33_INT_OSC_CTRL_B (0x0C) */ +#define DAC33_ADJSTEP(x) (x << 0) +#define DAC33_ADJTHRSHLD(x) (x << 4) + +/* DAC33_INT_OSC_CTRL_C (0x0D) */ +#define DAC33_REFDIV(x) (x << 4) + +/* DAC33_INT_OSC_STATUS (0x0E) */ +#define DAC33_OSCSTATUS_IDLE_CALIB (0x00) +#define DAC33_OSCSTATUS_NORMAL (0x01) +#define DAC33_OSCSTATUS_ADJUSTMENT (0x03) +#define DAC33_OSCSTATUS_NOT_USED (0x02) + +/* DAC33_SER_AUDIOIF_CTRL_A (0x12) */ +#define DAC33_MSWCLK (0x01 << 0) +#define DAC33_MSBCLK (0x01 << 1) +#define DAC33_AFMT_MASK (0x03 << 2) +#define DAC33_AFMT_I2S (0x00 << 2) +#define DAC33_AFMT_DSP (0x01 << 2) +#define DAC33_AFMT_RIGHT_J (0x02 << 2) +#define DAC33_AFMT_LEFT_J (0x03 << 2) +#define DAC33_WLEN_MASK (0x03 << 4) +#define DAC33_WLEN_16 (0x00 << 4) +#define DAC33_WLEN_20 (0x01 << 4) +#define DAC33_WLEN_24 (0x02 << 4) +#define DAC33_WLEN_32 (0x03 << 4) +#define DAC33_NCYCL_MASK (0x03 << 6) +#define DAC33_NCYCL_16 (0x00 << 6) +#define DAC33_NCYCL_20 (0x01 << 6) +#define DAC33_NCYCL_24 (0x02 << 6) +#define DAC33_NCYCL_32 (0x03 << 6) + +/* DAC33_SER_AUDIOIF_CTRL_B (0x13) */ +#define DAC33_DATA_DELAY_MASK (0x03 << 2) +#define DAC33_DATA_DELAY(x) (x << 2) +#define DAC33_BCLKON (0x01 << 5) + +/* DAC33_FIFO_CTRL_A (0x15) */ +#define DAC33_WIDTH (0x01 << 0) +#define DAC33_FBYPAS (0x01 << 1) +#define DAC33_FAUTO (0x01 << 2) +#define DAC33_FIFOFLUSH (0x01 << 3) + +/* + * UTHR, ATHR, LTHR, PREFILL, NSAMPLE (0x16 - 0x1F) + * 13-bit values +*/ +#define DAC33_THRREG(x) (((x) & 0x1FFF) << 3) + +/* DAC33_FIFO_IRQ_MASK (0x29) */ +#define DAC33_MNS (0x01 << 0) +#define DAC33_MPS (0x01 << 1) +#define DAC33_MAT (0x01 << 2) +#define DAC33_MLT (0x01 << 3) +#define DAC33_MUT (0x01 << 4) +#define DAC33_MUF (0x01 << 5) +#define DAC33_MOF (0x01 << 6) + +#define DAC33_FIFO_IRQ_MODE_MASK (0x03) +#define DAC33_FIFO_IRQ_MODE_RISING (0x00) +#define DAC33_FIFO_IRQ_MODE_FALLING (0x01) +#define DAC33_FIFO_IRQ_MODE_LEVEL (0x02) +#define DAC33_FIFO_IRQ_MODE_EDGE (0x03) + +/* DAC33_FIFO_IRQ_MODE_A (0x2A) */ +#define DAC33_UTM(x) (x << 0) +#define DAC33_UFM(x) (x << 2) +#define DAC33_OFM(x) (x << 4) + +/* DAC33_FIFO_IRQ_MODE_B (0x2B) */ +#define DAC33_NSM(x) (x << 0) +#define DAC33_PSM(x) (x << 2) +#define DAC33_ATM(x) (x << 4) +#define DAC33_LTM(x) (x << 6) + +/* DAC33_DAC_CTRL_A (0x2C) */ +#define DAC33_DACRATE(x) (x << 0) +#define DAC33_DACDUAL (0x01 << 4) +#define DAC33_DACLKSEL_MASK (0x03 << 5) +#define DAC33_DACLKSEL_INTSOC (0x00 << 5) +#define DAC33_DACLKSEL_PLL (0x01 << 5) +#define DAC33_DACLKSEL_MCLK (0x02 << 5) +#define DAC33_DACLKSEL_BCLK (0x03 << 5) + +/* DAC33_DAC_CTRL_B (0x2D) */ +#define DAC33_DACSRCR_MASK (0x03 << 0) +#define DAC33_DACSRCR_MUTE (0x00 << 0) +#define DAC33_DACSRCR_RIGHT (0x01 << 0) +#define DAC33_DACSRCR_LEFT (0x02 << 0) +#define DAC33_DACSRCR_MONOMIX (0x03 << 0) +#define DAC33_DACSRCL_MASK (0x03 << 2) +#define DAC33_DACSRCL_MUTE (0x00 << 2) +#define DAC33_DACSRCL_LEFT (0x01 << 2) +#define DAC33_DACSRCL_RIGHT (0x02 << 2) +#define DAC33_DACSRCL_MONOMIX (0x03 << 2) +#define DAC33_DVOLSTEP_MASK (0x03 << 4) +#define DAC33_DVOLSTEP_SS_PERFS (0x00 << 4) +#define DAC33_DVOLSTEP_SS_PER2FS (0x01 << 4) +#define DAC33_DVOLSTEP_SS_DISABLED (0x02 << 4) +#define DAC33_DVOLCTRL_MASK (0x03 << 6) +#define DAC33_DVOLCTRL_LR_INDEPENDENT1 (0x00 << 6) +#define DAC33_DVOLCTRL_LR_RIGHT_CONTROL (0x01 << 6) +#define DAC33_DVOLCTRL_LR_LEFT_CONTROL (0x02 << 6) +#define DAC33_DVOLCTRL_LR_INDEPENDENT2 (0x03 << 6) + +/* DAC33_DAC_CTRL_C (0x2E) */ +#define DAC33_DEEMENR (0x01 << 0) +#define DAC33_EFFENR (0x01 << 1) +#define DAC33_DEEMENL (0x01 << 2) +#define DAC33_EFFENL (0x01 << 3) +#define DAC33_EN3D (0x01 << 4) +#define DAC33_RESYNMUTE (0x01 << 5) +#define DAC33_RESYNEN (0x01 << 6) + +/* DAC33_ASRC_CTRL_A (0x32) */ +#define DAC33_SRCBYP (0x01 << 0) +#define DAC33_SRCLKSEL_MASK (0x03 << 1) +#define DAC33_SRCLKSEL_INTSOC (0x00 << 1) +#define DAC33_SRCLKSEL_PLL (0x01 << 1) +#define DAC33_SRCLKSEL_MCLK (0x02 << 1) +#define DAC33_SRCLKSEL_BCLK (0x03 << 1) +#define DAC33_SRCLKDIV(x) (x << 3) + +/* DAC33_ASRC_CTRL_B (0x33) */ +#define DAC33_SRCSETUP(x) (x << 0) +#define DAC33_SRCREFSEL (0x01 << 4) +#define DAC33_SRCREFDIV(x) (x << 5) + +/* DAC33_INTP_CTRL_A (0x38) */ +#define DAC33_INTPSEL (0x01 << 0) +#define DAC33_INTPM_MASK (0x03 << 1) +#define DAC33_INTPM_ALOW_OPENDRAIN (0x00 << 1) +#define DAC33_INTPM_ALOW (0x01 << 1) +#define DAC33_INTPM_AHIGH (0x02 << 1) + +/* DAC33_LDAC_PWR_CTRL (0x40) */ +/* DAC33_RDAC_PWR_CTRL (0x41) */ +#define DAC33_DACLRNUM (0x01 << 2) +#define DAC33_LROUT_GAIN(x) (x << 0) + +/* DAC33_ANA_VOL_SOFT_STEP_CTRL (0x49) */ +#define DAC33_VOLCLKSEL (0x01 << 0) +#define DAC33_VOLCLKEN (0x01 << 1) +#define DAC33_VOLBYPASS (0x01 << 2) + +#define TLV320DAC33_MCLK 0 +#define TLV320DAC33_SLEEPCLK 1 + +#endif /* __TLV320DAC33_H */ diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c new file mode 100644 index 0000000000..5bc486283f --- /dev/null +++ b/sound/soc/codecs/tpa6130a2.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver + * + * Copyright (C) Nokia Corporation + * + * Author: Peter Ujfalusi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpa6130a2.h" + +enum tpa_model { + TPA6130A2, + TPA6140A2, +}; + +/* This struct is used to save the context */ +struct tpa6130a2_data { + struct device *dev; + struct regmap *regmap; + struct regulator *supply; + int power_gpio; + enum tpa_model id; +}; + +static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable) +{ + int ret = 0, ret2; + + if (enable) { + ret = regulator_enable(data->supply); + if (ret != 0) { + dev_err(data->dev, + "Failed to enable supply: %d\n", ret); + return ret; + } + /* Power on */ + if (data->power_gpio >= 0) + gpio_set_value(data->power_gpio, 1); + + /* Sync registers */ + regcache_cache_only(data->regmap, false); + ret = regcache_sync(data->regmap); + if (ret != 0) { + dev_err(data->dev, + "Failed to sync registers: %d\n", ret); + regcache_cache_only(data->regmap, true); + if (data->power_gpio >= 0) + gpio_set_value(data->power_gpio, 0); + ret2 = regulator_disable(data->supply); + if (ret2 != 0) + dev_err(data->dev, + "Failed to disable supply: %d\n", ret2); + return ret; + } + } else { + /* Powered off device does not retain registers. While device + * is off, any register updates (i.e. volume changes) should + * happen in cache only. + */ + regcache_mark_dirty(data->regmap); + regcache_cache_only(data->regmap, true); + + /* Power off */ + if (data->power_gpio >= 0) + gpio_set_value(data->power_gpio, 0); + + ret = regulator_disable(data->supply); + if (ret != 0) { + dev_err(data->dev, + "Failed to disable supply: %d\n", ret); + return ret; + } + } + + return ret; +} + +static int tpa6130a2_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kctrl, int event) +{ + struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); + struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Before widget power up: turn chip on, sync registers */ + return tpa6130a2_power(data, true); + } else { + /* After widget power down: turn chip off */ + return tpa6130a2_power(data, false); + } +} + +/* + * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going + * down in gain. + */ +static const DECLARE_TLV_DB_RANGE(tpa6130_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0), + 2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0), + 4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0), + 6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0), + 8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0), + 10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0), + 12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0), + 14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0), + 21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0), + 38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0) +); + +static const struct snd_kcontrol_new tpa6130a2_controls[] = { + SOC_SINGLE_TLV("Headphone Playback Volume", + TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0, + tpa6130_tlv), +}; + +static const DECLARE_TLV_DB_RANGE(tpa6140_tlv, + 0, 8, TLV_DB_SCALE_ITEM(-5900, 400, 0), + 9, 16, TLV_DB_SCALE_ITEM(-2500, 200, 0), + 17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0) +); + +static const struct snd_kcontrol_new tpa6140a2_controls[] = { + SOC_SINGLE_TLV("Headphone Playback Volume", + TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0, + tpa6140_tlv), +}; + +static int tpa6130a2_component_probe(struct snd_soc_component *component) +{ + struct tpa6130a2_data *data = snd_soc_component_get_drvdata(component); + + if (data->id == TPA6140A2) + return snd_soc_add_component_controls(component, + tpa6140a2_controls, ARRAY_SIZE(tpa6140a2_controls)); + else + return snd_soc_add_component_controls(component, + tpa6130a2_controls, ARRAY_SIZE(tpa6130a2_controls)); +} + +static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("LEFTIN"), + SND_SOC_DAPM_INPUT("RIGHTIN"), + SND_SOC_DAPM_OUTPUT("HPLEFT"), + SND_SOC_DAPM_OUTPUT("HPRIGHT"), + + SND_SOC_DAPM_PGA("Left Mute", TPA6130A2_REG_VOL_MUTE, + TPA6130A2_HP_EN_L_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right Mute", TPA6130A2_REG_VOL_MUTE, + TPA6130A2_HP_EN_R_SHIFT, 1, NULL, 0), + SND_SOC_DAPM_PGA("Left PGA", TPA6130A2_REG_CONTROL, + TPA6130A2_HP_EN_L_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right PGA", TPA6130A2_REG_CONTROL, + TPA6130A2_HP_EN_R_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Power", TPA6130A2_REG_CONTROL, + TPA6130A2_SWS_SHIFT, 1, tpa6130a2_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route tpa6130a2_dapm_routes[] = { + { "Left PGA", NULL, "LEFTIN" }, + { "Right PGA", NULL, "RIGHTIN" }, + + { "Left Mute", NULL, "Left PGA" }, + { "Right Mute", NULL, "Right PGA" }, + + { "HPLEFT", NULL, "Left Mute" }, + { "HPRIGHT", NULL, "Right Mute" }, + + { "Left PGA", NULL, "Power" }, + { "Right PGA", NULL, "Power" }, +}; + +static const struct snd_soc_component_driver tpa6130a2_component_driver = { + .name = "tpa6130a2", + .probe = tpa6130a2_component_probe, + .dapm_widgets = tpa6130a2_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tpa6130a2_dapm_widgets), + .dapm_routes = tpa6130a2_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tpa6130a2_dapm_routes), +}; + +static const struct reg_default tpa6130a2_reg_defaults[] = { + { TPA6130A2_REG_CONTROL, TPA6130A2_SWS }, + { TPA6130A2_REG_VOL_MUTE, TPA6130A2_MUTE_R | TPA6130A2_MUTE_L }, +}; + +static const struct regmap_config tpa6130a2_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = TPA6130A2_REG_VERSION, + .reg_defaults = tpa6130a2_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tpa6130a2_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static const struct i2c_device_id tpa6130a2_id[] = { + { "tpa6130a2", TPA6130A2 }, + { "tpa6140a2", TPA6140A2 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tpa6130a2_id); + +static int tpa6130a2_probe(struct i2c_client *client) +{ + struct device *dev; + struct tpa6130a2_data *data; + struct tpa6130a2_platform_data *pdata = client->dev.platform_data; + struct device_node *np = client->dev.of_node; + const struct i2c_device_id *id; + const char *regulator; + unsigned int version; + int ret; + + dev = &client->dev; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + + data->regmap = devm_regmap_init_i2c(client, &tpa6130a2_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + if (pdata) { + data->power_gpio = pdata->power_gpio; + } else if (np) { + data->power_gpio = of_get_named_gpio(np, "power-gpio", 0); + } else { + dev_err(dev, "Platform data not set\n"); + dump_stack(); + return -ENODEV; + } + + i2c_set_clientdata(client, data); + + id = i2c_match_id(tpa6130a2_id, client); + data->id = id->driver_data; + + if (data->power_gpio >= 0) { + ret = devm_gpio_request(dev, data->power_gpio, + "tpa6130a2 enable"); + if (ret < 0) { + dev_err(dev, "Failed to request power GPIO (%d)\n", + data->power_gpio); + return ret; + } + gpio_direction_output(data->power_gpio, 0); + } + + switch (data->id) { + default: + dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n", + data->id); + fallthrough; + case TPA6130A2: + regulator = "Vdd"; + break; + case TPA6140A2: + regulator = "AVdd"; + break; + } + + data->supply = devm_regulator_get(dev, regulator); + if (IS_ERR(data->supply)) { + ret = PTR_ERR(data->supply); + dev_err(dev, "Failed to request supply: %d\n", ret); + return ret; + } + + ret = tpa6130a2_power(data, true); + if (ret != 0) + return ret; + + + /* Read version */ + regmap_read(data->regmap, TPA6130A2_REG_VERSION, &version); + version &= TPA6130A2_VERSION_MASK; + if ((version != 1) && (version != 2)) + dev_warn(dev, "UNTESTED version detected (%d)\n", version); + + /* Disable the chip */ + ret = tpa6130a2_power(data, false); + if (ret != 0) + return ret; + + return devm_snd_soc_register_component(&client->dev, + &tpa6130a2_component_driver, NULL, 0); +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tpa6130a2_of_match[] = { + { .compatible = "ti,tpa6130a2", }, + { .compatible = "ti,tpa6140a2" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tpa6130a2_of_match); +#endif + +static struct i2c_driver tpa6130a2_i2c_driver = { + .driver = { + .name = "tpa6130a2", + .of_match_table = of_match_ptr(tpa6130a2_of_match), + }, + .probe = tpa6130a2_probe, + .id_table = tpa6130a2_id, +}; + +module_i2c_driver(tpa6130a2_i2c_driver); + +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h new file mode 100644 index 0000000000..bfa1fc11a1 --- /dev/null +++ b/sound/soc/codecs/tpa6130a2.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC TPA6130A2 amplifier driver + * + * Copyright (C) Nokia Corporation + * + * Author: Peter Ujfalusi + */ + +#ifndef __TPA6130A2_H__ +#define __TPA6130A2_H__ + +/* Register addresses */ +#define TPA6130A2_REG_CONTROL 0x01 +#define TPA6130A2_REG_VOL_MUTE 0x02 +#define TPA6130A2_REG_OUT_IMPEDANCE 0x03 +#define TPA6130A2_REG_VERSION 0x04 + +/* Register bits */ +/* TPA6130A2_REG_CONTROL (0x01) */ +#define TPA6130A2_SWS_SHIFT 0 +#define TPA6130A2_SWS (0x01 << TPA6130A2_SWS_SHIFT) +#define TPA6130A2_TERMAL (0x01 << 1) +#define TPA6130A2_MODE(x) (x << 4) +#define TPA6130A2_MODE_STEREO (0x00) +#define TPA6130A2_MODE_DUAL_MONO (0x01) +#define TPA6130A2_MODE_BRIDGE (0x02) +#define TPA6130A2_MODE_MASK (0x03) +#define TPA6130A2_HP_EN_R_SHIFT 6 +#define TPA6130A2_HP_EN_R (0x01 << TPA6130A2_HP_EN_R_SHIFT) +#define TPA6130A2_HP_EN_L_SHIFT 7 +#define TPA6130A2_HP_EN_L (0x01 << TPA6130A2_HP_EN_L_SHIFT) + +/* TPA6130A2_REG_VOL_MUTE (0x02) */ +#define TPA6130A2_VOLUME(x) ((x & 0x3f) << 0) +#define TPA6130A2_MUTE_R (0x01 << 6) +#define TPA6130A2_MUTE_L (0x01 << 7) + +/* TPA6130A2_REG_OUT_IMPEDANCE (0x03) */ +#define TPA6130A2_HIZ_R (0x01 << 0) +#define TPA6130A2_HIZ_L (0x01 << 1) + +/* TPA6130A2_REG_VERSION (0x04) */ +#define TPA6130A2_VERSION_MASK (0x0f) + +#endif /* __TPA6130A2_H__ */ diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c new file mode 100644 index 0000000000..6d9166d911 --- /dev/null +++ b/sound/soc/codecs/ts3a227e.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TS3A227E Autonomous Audio Accessory Detection and Configuration Switch + * + * Copyright (C) 2014 Google, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ts3a227e.h" + +struct ts3a227e { + struct device *dev; + struct regmap *regmap; + struct snd_soc_jack *jack; + bool plugged; + bool mic_present; + unsigned int buttons_held; + int irq; +}; + +/* Button values to be reported on the jack */ +static const int ts3a227e_buttons[] = { + SND_JACK_BTN_0, + SND_JACK_BTN_1, + SND_JACK_BTN_2, + SND_JACK_BTN_3, +}; + +#define TS3A227E_NUM_BUTTONS 4 +#define TS3A227E_JACK_MASK (SND_JACK_HEADPHONE | \ + SND_JACK_MICROPHONE | \ + SND_JACK_BTN_0 | \ + SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | \ + SND_JACK_BTN_3) + +/* TS3A227E registers */ +#define TS3A227E_REG_DEVICE_ID 0x00 +#define TS3A227E_REG_INTERRUPT 0x01 +#define TS3A227E_REG_KP_INTERRUPT 0x02 +#define TS3A227E_REG_INTERRUPT_DISABLE 0x03 +#define TS3A227E_REG_SETTING_1 0x04 +#define TS3A227E_REG_SETTING_2 0x05 +#define TS3A227E_REG_SETTING_3 0x06 +#define TS3A227E_REG_SWITCH_CONTROL_1 0x07 +#define TS3A227E_REG_SWITCH_CONTROL_2 0x08 +#define TS3A227E_REG_SWITCH_STATUS_1 0x09 +#define TS3A227E_REG_SWITCH_STATUS_2 0x0a +#define TS3A227E_REG_ACCESSORY_STATUS 0x0b +#define TS3A227E_REG_ADC_OUTPUT 0x0c +#define TS3A227E_REG_KP_THRESHOLD_1 0x0d +#define TS3A227E_REG_KP_THRESHOLD_2 0x0e +#define TS3A227E_REG_KP_THRESHOLD_3 0x0f + +/* TS3A227E_REG_INTERRUPT 0x01 */ +#define INS_REM_EVENT 0x01 +#define DETECTION_COMPLETE_EVENT 0x02 + +/* TS3A227E_REG_KP_INTERRUPT 0x02 */ +#define PRESS_MASK(idx) (0x01 << (2 * (idx))) +#define RELEASE_MASK(idx) (0x02 << (2 * (idx))) + +/* TS3A227E_REG_INTERRUPT_DISABLE 0x03 */ +#define INS_REM_INT_DISABLE 0x01 +#define DETECTION_COMPLETE_INT_DISABLE 0x02 +#define ADC_COMPLETE_INT_DISABLE 0x04 +#define INTB_DISABLE 0x08 + +/* TS3A227E_REG_SETTING_1 0x4 */ +#define DEBOUNCE_INSERTION_SETTING_SFT (0) +#define DEBOUNCE_INSERTION_SETTING_MASK (0x7 << DEBOUNCE_PRESS_SETTING_SFT) + +/* TS3A227E_REG_SETTING_2 0x05 */ +#define KP_ENABLE 0x04 + +/* TS3A227E_REG_SETTING_3 0x06 */ +#define MICBIAS_SETTING_SFT 3 +#define MICBIAS_SETTING_MASK (0x7 << MICBIAS_SETTING_SFT) +#define DEBOUNCE_RELEASE_SETTING_SFT 2 +#define DEBOUNCE_RELEASE_SETTING_MASK (0x1 << DEBOUNCE_RELEASE_SETTING_SFT) +#define DEBOUNCE_PRESS_SETTING_SFT 0 +#define DEBOUNCE_PRESS_SETTING_MASK (0x3 << DEBOUNCE_PRESS_SETTING_SFT) + +/* TS3A227E_REG_ACCESSORY_STATUS 0x0b */ +#define TYPE_3_POLE 0x01 +#define TYPE_4_POLE_OMTP 0x02 +#define TYPE_4_POLE_STANDARD 0x04 +#define JACK_INSERTED 0x08 +#define EITHER_MIC_MASK (TYPE_4_POLE_OMTP | TYPE_4_POLE_STANDARD) + +static const struct reg_default ts3a227e_reg_defaults[] = { + { TS3A227E_REG_DEVICE_ID, 0x10 }, + { TS3A227E_REG_INTERRUPT, 0x00 }, + { TS3A227E_REG_KP_INTERRUPT, 0x00 }, + { TS3A227E_REG_INTERRUPT_DISABLE, 0x08 }, + { TS3A227E_REG_SETTING_1, 0x23 }, + { TS3A227E_REG_SETTING_2, 0x00 }, + { TS3A227E_REG_SETTING_3, 0x0e }, + { TS3A227E_REG_SWITCH_CONTROL_1, 0x00 }, + { TS3A227E_REG_SWITCH_CONTROL_2, 0x00 }, + { TS3A227E_REG_SWITCH_STATUS_1, 0x0c }, + { TS3A227E_REG_SWITCH_STATUS_2, 0x00 }, + { TS3A227E_REG_ACCESSORY_STATUS, 0x00 }, + { TS3A227E_REG_ADC_OUTPUT, 0x00 }, + { TS3A227E_REG_KP_THRESHOLD_1, 0x20 }, + { TS3A227E_REG_KP_THRESHOLD_2, 0x40 }, + { TS3A227E_REG_KP_THRESHOLD_3, 0x68 }, +}; + +static bool ts3a227e_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TS3A227E_REG_DEVICE_ID ... TS3A227E_REG_KP_THRESHOLD_3: + return true; + default: + return false; + } +} + +static bool ts3a227e_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TS3A227E_REG_INTERRUPT_DISABLE ... TS3A227E_REG_SWITCH_CONTROL_2: + case TS3A227E_REG_KP_THRESHOLD_1 ... TS3A227E_REG_KP_THRESHOLD_3: + return true; + default: + return false; + } +} + +static bool ts3a227e_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TS3A227E_REG_INTERRUPT ... TS3A227E_REG_INTERRUPT_DISABLE: + case TS3A227E_REG_SETTING_1 ... TS3A227E_REG_SETTING_2: + case TS3A227E_REG_SWITCH_STATUS_1 ... TS3A227E_REG_ADC_OUTPUT: + return true; + default: + return false; + } +} + +static void ts3a227e_jack_report(struct ts3a227e *ts3a227e) +{ + unsigned int i; + int report = 0; + + if (!ts3a227e->jack) + return; + + if (ts3a227e->plugged) + report = SND_JACK_HEADPHONE; + if (ts3a227e->mic_present) + report |= SND_JACK_MICROPHONE; + for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) { + if (ts3a227e->buttons_held & (1 << i)) + report |= ts3a227e_buttons[i]; + } + snd_soc_jack_report(ts3a227e->jack, report, TS3A227E_JACK_MASK); +} + +static void ts3a227e_new_jack_state(struct ts3a227e *ts3a227e, unsigned acc_reg) +{ + bool plugged, mic_present; + + plugged = !!(acc_reg & JACK_INSERTED); + mic_present = plugged && !!(acc_reg & EITHER_MIC_MASK); + + ts3a227e->plugged = plugged; + + if (mic_present != ts3a227e->mic_present) { + ts3a227e->mic_present = mic_present; + ts3a227e->buttons_held = 0; + if (mic_present) { + /* Enable key press detection. */ + regmap_update_bits(ts3a227e->regmap, + TS3A227E_REG_SETTING_2, + KP_ENABLE, KP_ENABLE); + } + } +} + +static irqreturn_t ts3a227e_interrupt(int irq, void *data) +{ + struct ts3a227e *ts3a227e = (struct ts3a227e *)data; + struct regmap *regmap = ts3a227e->regmap; + unsigned int int_reg, kp_int_reg, acc_reg, i; + struct device *dev = ts3a227e->dev; + int ret; + + /* Check for plug/unplug. */ + ret = regmap_read(regmap, TS3A227E_REG_INTERRUPT, &int_reg); + if (ret) { + dev_err(dev, "failed to clear interrupt ret=%d\n", ret); + return IRQ_NONE; + } + + if (int_reg & (DETECTION_COMPLETE_EVENT | INS_REM_EVENT)) { + regmap_read(regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg); + ts3a227e_new_jack_state(ts3a227e, acc_reg); + } + + /* Report any key events. */ + ret = regmap_read(regmap, TS3A227E_REG_KP_INTERRUPT, &kp_int_reg); + if (ret) { + dev_err(dev, "failed to clear key interrupt ret=%d\n", ret); + return IRQ_NONE; + } + + for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) { + if (kp_int_reg & PRESS_MASK(i)) + ts3a227e->buttons_held |= (1 << i); + if (kp_int_reg & RELEASE_MASK(i)) + ts3a227e->buttons_held &= ~(1 << i); + } + + ts3a227e_jack_report(ts3a227e); + + return IRQ_HANDLED; +} + +/** + * ts3a227e_enable_jack_detect - Specify a jack for event reporting + * + * @component: component to register the jack with + * @jack: jack to use to report headset and button events on + * + * After this function has been called the headset insert/remove and button + * events 0-3 will be routed to the given jack. Jack can be null to stop + * reporting. + */ +int ts3a227e_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack) +{ + struct ts3a227e *ts3a227e = snd_soc_component_get_drvdata(component); + + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + + ts3a227e->jack = jack; + ts3a227e_jack_report(ts3a227e); + + return 0; +} +EXPORT_SYMBOL_GPL(ts3a227e_enable_jack_detect); + +static int ts3a227e_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) +{ + if (jack == NULL) + return -EINVAL; + + return ts3a227e_enable_jack_detect(component, jack); +} + +static int ts3a227e_get_jack_type(struct snd_soc_component *component) +{ + return SND_JACK_HEADSET; +} + +static const struct snd_soc_component_driver ts3a227e_soc_driver = { + .name = "ti,ts3a227e", + .set_jack = ts3a227e_set_jack, + .get_jack_type = ts3a227e_get_jack_type, +}; + +static const struct regmap_config ts3a227e_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + + .max_register = TS3A227E_REG_KP_THRESHOLD_3, + .readable_reg = ts3a227e_readable_reg, + .writeable_reg = ts3a227e_writeable_reg, + .volatile_reg = ts3a227e_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ts3a227e_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults), +}; + +static int ts3a227e_parse_device_property(struct ts3a227e *ts3a227e, + struct device *dev) +{ + u32 value; + u32 value_ms; + u32 setting3_value = 0; + u32 setting3_mask = 0; + int err; + + err = device_property_read_u32(dev, "ti,micbias", &value); + if (!err) { + setting3_mask = MICBIAS_SETTING_MASK; + setting3_value = (value << MICBIAS_SETTING_SFT) & + MICBIAS_SETTING_MASK; + } + + err = device_property_read_u32(dev, "ti,debounce-release-ms", + &value_ms); + if (!err) { + value = (value_ms > 10); + setting3_mask |= DEBOUNCE_RELEASE_SETTING_MASK; + setting3_value |= (value << DEBOUNCE_RELEASE_SETTING_SFT) & + DEBOUNCE_RELEASE_SETTING_MASK; + } + + err = device_property_read_u32(dev, "ti,debounce-press-ms", &value_ms); + if (!err) { + value = (value_ms + 20) / 40; + if (value > 3) + value = 3; + setting3_mask |= DEBOUNCE_PRESS_SETTING_MASK; + setting3_value |= (value << DEBOUNCE_PRESS_SETTING_SFT) & + DEBOUNCE_PRESS_SETTING_MASK; + } + + if (setting3_mask) + regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_3, + setting3_mask, setting3_value); + + err = device_property_read_u32(dev, "ti,debounce-insertion-ms", + &value_ms); + if (!err) { + if (value_ms < 165) + value = (value_ms + 15) / 30; + else if (value_ms < 1500) + value = 6; + else + value = 7; + regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_1, + DEBOUNCE_INSERTION_SETTING_MASK, + (value << DEBOUNCE_INSERTION_SETTING_SFT) & + DEBOUNCE_INSERTION_SETTING_MASK); + } + + return 0; +} + +static int ts3a227e_i2c_probe(struct i2c_client *i2c) +{ + struct ts3a227e *ts3a227e; + struct device *dev = &i2c->dev; + int ret; + unsigned int acc_reg; + + ts3a227e = devm_kzalloc(&i2c->dev, sizeof(*ts3a227e), GFP_KERNEL); + if (ts3a227e == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, ts3a227e); + ts3a227e->dev = dev; + ts3a227e->irq = i2c->irq; + + ts3a227e->regmap = devm_regmap_init_i2c(i2c, &ts3a227e_regmap_config); + if (IS_ERR(ts3a227e->regmap)) + return PTR_ERR(ts3a227e->regmap); + + ret = ts3a227e_parse_device_property(ts3a227e, dev); + if (ret) { + dev_err(dev, "Failed to parse device property: %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "TS3A227E", ts3a227e); + if (ret) { + dev_err(dev, "Cannot request irq %d (%d)\n", i2c->irq, ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, &ts3a227e_soc_driver, + NULL, 0); + if (ret) + return ret; + + /* Enable interrupts except for ADC complete. */ + regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_INTERRUPT_DISABLE, + INTB_DISABLE | ADC_COMPLETE_INT_DISABLE, + ADC_COMPLETE_INT_DISABLE); + + /* Read jack status because chip might not trigger interrupt at boot. */ + regmap_read(ts3a227e->regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg); + ts3a227e_new_jack_state(ts3a227e, acc_reg); + ts3a227e_jack_report(ts3a227e); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ts3a227e_suspend(struct device *dev) +{ + struct ts3a227e *ts3a227e = dev_get_drvdata(dev); + + dev_dbg(ts3a227e->dev, "suspend disable irq\n"); + disable_irq(ts3a227e->irq); + + return 0; +} + +static int ts3a227e_resume(struct device *dev) +{ + struct ts3a227e *ts3a227e = dev_get_drvdata(dev); + + dev_dbg(ts3a227e->dev, "resume enable irq\n"); + enable_irq(ts3a227e->irq); + + return 0; +} +#endif + +static const struct dev_pm_ops ts3a227e_pm = { + SET_SYSTEM_SLEEP_PM_OPS(ts3a227e_suspend, ts3a227e_resume) +}; + +static const struct i2c_device_id ts3a227e_i2c_ids[] = { + { "ts3a227e", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ts3a227e_i2c_ids); + +#ifdef CONFIG_OF +static const struct of_device_id ts3a227e_of_match[] = { + { .compatible = "ti,ts3a227e", }, + { } +}; +MODULE_DEVICE_TABLE(of, ts3a227e_of_match); +#endif + +#ifdef CONFIG_ACPI +static struct acpi_device_id ts3a227e_acpi_match[] = { + { "104C227E", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, ts3a227e_acpi_match); +#endif + +static struct i2c_driver ts3a227e_driver = { + .driver = { + .name = "ts3a227e", + .pm = &ts3a227e_pm, + .of_match_table = of_match_ptr(ts3a227e_of_match), + .acpi_match_table = ACPI_PTR(ts3a227e_acpi_match), + }, + .probe = ts3a227e_i2c_probe, + .id_table = ts3a227e_i2c_ids, +}; +module_i2c_driver(ts3a227e_driver); + +MODULE_DESCRIPTION("ASoC ts3a227e driver"); +MODULE_AUTHOR("Dylan Reid "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/ts3a227e.h b/sound/soc/codecs/ts3a227e.h new file mode 100644 index 0000000000..3565e5931c --- /dev/null +++ b/sound/soc/codecs/ts3a227e.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * TS3A227E Autonous Audio Accessory Detection and Configureation Switch + * + * Copyright (C) 2014 Google, Inc. + */ + +#ifndef _TS3A227E_H +#define _TS3A227E_H + +int ts3a227e_enable_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack); + +#endif diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c new file mode 100644 index 0000000000..1eefc1fe6e --- /dev/null +++ b/sound/soc/codecs/tscs42xx.c @@ -0,0 +1,1514 @@ +// SPDX-License-Identifier: GPL-2.0 +// tscs42xx.c -- TSCS42xx ALSA SoC Audio driver +// Copyright 2017 Tempo Semiconductor, Inc. +// Author: Steven Eckhoff + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tscs42xx.h" + +#define COEFF_SIZE 3 +#define BIQUAD_COEFF_COUNT 5 +#define BIQUAD_SIZE (COEFF_SIZE * BIQUAD_COEFF_COUNT) + +#define COEFF_RAM_MAX_ADDR 0xcd +#define COEFF_RAM_COEFF_COUNT (COEFF_RAM_MAX_ADDR + 1) +#define COEFF_RAM_SIZE (COEFF_SIZE * COEFF_RAM_COEFF_COUNT) + +struct tscs42xx { + + int bclk_ratio; + int samplerate; + struct mutex audio_params_lock; + + u8 coeff_ram[COEFF_RAM_SIZE]; + bool coeff_ram_synced; + struct mutex coeff_ram_lock; + + struct mutex pll_lock; + + struct regmap *regmap; + + struct clk *sysclk; + int sysclk_src_id; +}; + +struct coeff_ram_ctl { + unsigned int addr; + struct soc_bytes_ext bytes_ext; +}; + +static bool tscs42xx_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_DACCRWRL: + case R_DACCRWRM: + case R_DACCRWRH: + case R_DACCRRDL: + case R_DACCRRDM: + case R_DACCRRDH: + case R_DACCRSTAT: + case R_DACCRADDR: + case R_PLLCTL0: + return true; + default: + return false; + } +} + +static bool tscs42xx_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_DACCRWRL: + case R_DACCRWRM: + case R_DACCRWRH: + case R_DACCRRDL: + case R_DACCRRDM: + case R_DACCRRDH: + return true; + default: + return false; + } +} + +static const struct regmap_config tscs42xx_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .volatile_reg = tscs42xx_volatile, + .precious_reg = tscs42xx_precious, + .max_register = R_DACMBCREL3H, + + .cache_type = REGCACHE_RBTREE, + .can_multi_write = true, +}; + +#define MAX_PLL_LOCK_20MS_WAITS 1 +static bool plls_locked(struct snd_soc_component *component) +{ + int ret; + int count = MAX_PLL_LOCK_20MS_WAITS; + + do { + ret = snd_soc_component_read(component, R_PLLCTL0); + if (ret < 0) { + dev_err(component->dev, + "Failed to read PLL lock status (%d)\n", ret); + return false; + } else if (ret > 0) { + return true; + } + msleep(20); + } while (count--); + + return false; +} + +static int sample_rate_to_pll_freq_out(int sample_rate) +{ + switch (sample_rate) { + case 11025: + case 22050: + case 44100: + case 88200: + return 112896000; + case 8000: + case 16000: + case 32000: + case 48000: + case 96000: + return 122880000; + default: + return -EINVAL; + } +} + +#define DACCRSTAT_MAX_TRYS 10 +static int write_coeff_ram(struct snd_soc_component *component, u8 *coeff_ram, + unsigned int addr, unsigned int coeff_cnt) +{ + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + int cnt; + int trys; + int ret; + + for (cnt = 0; cnt < coeff_cnt; cnt++, addr++) { + + for (trys = 0; trys < DACCRSTAT_MAX_TRYS; trys++) { + ret = snd_soc_component_read(component, R_DACCRSTAT); + if (ret < 0) { + dev_err(component->dev, + "Failed to read stat (%d)\n", ret); + return ret; + } + if (!ret) + break; + } + + if (trys == DACCRSTAT_MAX_TRYS) { + ret = -EIO; + dev_err(component->dev, + "dac coefficient write error (%d)\n", ret); + return ret; + } + + ret = regmap_write(tscs42xx->regmap, R_DACCRADDR, addr); + if (ret < 0) { + dev_err(component->dev, + "Failed to write dac ram address (%d)\n", ret); + return ret; + } + + ret = regmap_bulk_write(tscs42xx->regmap, R_DACCRWRL, + &coeff_ram[addr * COEFF_SIZE], + COEFF_SIZE); + if (ret < 0) { + dev_err(component->dev, + "Failed to write dac ram (%d)\n", ret); + return ret; + } + } + + return 0; +} + +static int power_up_audio_plls(struct snd_soc_component *component) +{ + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + int freq_out; + int ret; + unsigned int mask; + unsigned int val; + + freq_out = sample_rate_to_pll_freq_out(tscs42xx->samplerate); + switch (freq_out) { + case 122880000: /* 48k */ + mask = RM_PLLCTL1C_PDB_PLL1; + val = RV_PLLCTL1C_PDB_PLL1_ENABLE; + break; + case 112896000: /* 44.1k */ + mask = RM_PLLCTL1C_PDB_PLL2; + val = RV_PLLCTL1C_PDB_PLL2_ENABLE; + break; + default: + ret = -EINVAL; + dev_err(component->dev, + "Unrecognized PLL output freq (%d)\n", ret); + return ret; + } + + mutex_lock(&tscs42xx->pll_lock); + + ret = snd_soc_component_update_bits(component, R_PLLCTL1C, mask, val); + if (ret < 0) { + dev_err(component->dev, "Failed to turn PLL on (%d)\n", ret); + goto exit; + } + + if (!plls_locked(component)) { + dev_err(component->dev, "Failed to lock plls\n"); + ret = -ENOMSG; + goto exit; + } + + ret = 0; +exit: + mutex_unlock(&tscs42xx->pll_lock); + + return ret; +} + +static int power_down_audio_plls(struct snd_soc_component *component) +{ + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + int ret; + + mutex_lock(&tscs42xx->pll_lock); + + ret = snd_soc_component_update_bits(component, R_PLLCTL1C, + RM_PLLCTL1C_PDB_PLL1, + RV_PLLCTL1C_PDB_PLL1_DISABLE); + if (ret < 0) { + dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); + goto exit; + } + ret = snd_soc_component_update_bits(component, R_PLLCTL1C, + RM_PLLCTL1C_PDB_PLL2, + RV_PLLCTL1C_PDB_PLL2_DISABLE); + if (ret < 0) { + dev_err(component->dev, "Failed to turn PLL off (%d)\n", ret); + goto exit; + } + + ret = 0; +exit: + mutex_unlock(&tscs42xx->pll_lock); + + return ret; +} + +static int coeff_ram_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + mutex_lock(&tscs42xx->coeff_ram_lock); + + memcpy(ucontrol->value.bytes.data, + &tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], params->max); + + mutex_unlock(&tscs42xx->coeff_ram_lock); + + return 0; +} + +static int coeff_ram_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + unsigned int coeff_cnt = params->max / COEFF_SIZE; + int ret; + + mutex_lock(&tscs42xx->coeff_ram_lock); + + tscs42xx->coeff_ram_synced = false; + + memcpy(&tscs42xx->coeff_ram[ctl->addr * COEFF_SIZE], + ucontrol->value.bytes.data, params->max); + + mutex_lock(&tscs42xx->pll_lock); + + if (plls_locked(component)) { + ret = write_coeff_ram(component, tscs42xx->coeff_ram, + ctl->addr, coeff_cnt); + if (ret < 0) { + dev_err(component->dev, + "Failed to flush coeff ram cache (%d)\n", ret); + goto exit; + } + tscs42xx->coeff_ram_synced = true; + } + + ret = 0; +exit: + mutex_unlock(&tscs42xx->pll_lock); + + mutex_unlock(&tscs42xx->coeff_ram_lock); + + return ret; +} + +/* Input L Capture Route */ +static char const * const input_select_text[] = { + "Line 1", "Line 2", "Line 3", "D2S" +}; + +static const struct soc_enum left_input_select_enum = +SOC_ENUM_SINGLE(R_INSELL, FB_INSELL, ARRAY_SIZE(input_select_text), + input_select_text); + +static const struct snd_kcontrol_new left_input_select = +SOC_DAPM_ENUM("LEFT_INPUT_SELECT_ENUM", left_input_select_enum); + +/* Input R Capture Route */ +static const struct soc_enum right_input_select_enum = +SOC_ENUM_SINGLE(R_INSELR, FB_INSELR, ARRAY_SIZE(input_select_text), + input_select_text); + +static const struct snd_kcontrol_new right_input_select = +SOC_DAPM_ENUM("RIGHT_INPUT_SELECT_ENUM", right_input_select_enum); + +/* Input Channel Mapping */ +static char const * const ch_map_select_text[] = { + "Normal", "Left to Right", "Right to Left", "Swap" +}; + +static const struct soc_enum ch_map_select_enum = +SOC_ENUM_SINGLE(R_AIC2, FB_AIC2_ADCDSEL, ARRAY_SIZE(ch_map_select_text), + ch_map_select_text); + +static int dapm_vref_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + msleep(20); + return 0; +} + +static int dapm_micb_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + msleep(20); + return 0; +} + +static int pll_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + int ret; + + if (SND_SOC_DAPM_EVENT_ON(event)) + ret = power_up_audio_plls(component); + else + ret = power_down_audio_plls(component); + + return ret; +} + +static int dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + int ret; + + mutex_lock(&tscs42xx->coeff_ram_lock); + + if (!tscs42xx->coeff_ram_synced) { + ret = write_coeff_ram(component, tscs42xx->coeff_ram, 0x00, + COEFF_RAM_COEFF_COUNT); + if (ret < 0) + goto exit; + tscs42xx->coeff_ram_synced = true; + } + + ret = 0; +exit: + mutex_unlock(&tscs42xx->coeff_ram_lock); + + return ret; +} + +static const struct snd_soc_dapm_widget tscs42xx_dapm_widgets[] = { + /* Vref */ + SND_SOC_DAPM_SUPPLY_S("Vref", 1, R_PWRM2, FB_PWRM2_VREF, 0, + dapm_vref_event, SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + + /* PLL */ + SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, pll_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Headphone */ + SND_SOC_DAPM_DAC_E("DAC L", "HiFi Playback", R_PWRM2, FB_PWRM2_HPL, 0, + dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_DAC_E("DAC R", "HiFi Playback", R_PWRM2, FB_PWRM2_HPR, 0, + dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("Headphone L"), + SND_SOC_DAPM_OUTPUT("Headphone R"), + + /* Speaker */ + SND_SOC_DAPM_DAC_E("ClassD L", "HiFi Playback", + R_PWRM2, FB_PWRM2_SPKL, 0, + dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_DAC_E("ClassD R", "HiFi Playback", + R_PWRM2, FB_PWRM2_SPKR, 0, + dac_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_OUTPUT("Speaker L"), + SND_SOC_DAPM_OUTPUT("Speaker R"), + + /* Capture */ + SND_SOC_DAPM_PGA("Analog In PGA L", R_PWRM1, FB_PWRM1_PGAL, 0, NULL, 0), + SND_SOC_DAPM_PGA("Analog In PGA R", R_PWRM1, FB_PWRM1_PGAR, 0, NULL, 0), + SND_SOC_DAPM_PGA("Analog Boost L", R_PWRM1, FB_PWRM1_BSTL, 0, NULL, 0), + SND_SOC_DAPM_PGA("Analog Boost R", R_PWRM1, FB_PWRM1_BSTR, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC Mute", R_CNVRTR0, FB_CNVRTR0_HPOR, true, NULL, 0), + SND_SOC_DAPM_ADC("ADC L", "HiFi Capture", R_PWRM1, FB_PWRM1_ADCL, 0), + SND_SOC_DAPM_ADC("ADC R", "HiFi Capture", R_PWRM1, FB_PWRM1_ADCR, 0), + + /* Capture Input */ + SND_SOC_DAPM_MUX("Input L Capture Route", R_PWRM2, + FB_PWRM2_INSELL, 0, &left_input_select), + SND_SOC_DAPM_MUX("Input R Capture Route", R_PWRM2, + FB_PWRM2_INSELR, 0, &right_input_select), + + /* Digital Mic */ + SND_SOC_DAPM_SUPPLY_S("Digital Mic Enable", 2, R_DMICCTL, + FB_DMICCTL_DMICEN, 0, NULL, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + + /* Analog Mic */ + SND_SOC_DAPM_SUPPLY_S("Mic Bias", 2, R_PWRM1, FB_PWRM1_MICB, + 0, dapm_micb_event, SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + + /* Line In */ + SND_SOC_DAPM_INPUT("Line In 1 L"), + SND_SOC_DAPM_INPUT("Line In 1 R"), + SND_SOC_DAPM_INPUT("Line In 2 L"), + SND_SOC_DAPM_INPUT("Line In 2 R"), + SND_SOC_DAPM_INPUT("Line In 3 L"), + SND_SOC_DAPM_INPUT("Line In 3 R"), +}; + +static const struct snd_soc_dapm_route tscs42xx_intercon[] = { + {"DAC L", NULL, "PLL"}, + {"DAC R", NULL, "PLL"}, + {"DAC L", NULL, "Vref"}, + {"DAC R", NULL, "Vref"}, + {"Headphone L", NULL, "DAC L"}, + {"Headphone R", NULL, "DAC R"}, + + {"ClassD L", NULL, "PLL"}, + {"ClassD R", NULL, "PLL"}, + {"ClassD L", NULL, "Vref"}, + {"ClassD R", NULL, "Vref"}, + {"Speaker L", NULL, "ClassD L"}, + {"Speaker R", NULL, "ClassD R"}, + + {"Input L Capture Route", NULL, "Vref"}, + {"Input R Capture Route", NULL, "Vref"}, + + {"Mic Bias", NULL, "Vref"}, + + {"Input L Capture Route", "Line 1", "Line In 1 L"}, + {"Input R Capture Route", "Line 1", "Line In 1 R"}, + {"Input L Capture Route", "Line 2", "Line In 2 L"}, + {"Input R Capture Route", "Line 2", "Line In 2 R"}, + {"Input L Capture Route", "Line 3", "Line In 3 L"}, + {"Input R Capture Route", "Line 3", "Line In 3 R"}, + + {"Analog In PGA L", NULL, "Input L Capture Route"}, + {"Analog In PGA R", NULL, "Input R Capture Route"}, + {"Analog Boost L", NULL, "Analog In PGA L"}, + {"Analog Boost R", NULL, "Analog In PGA R"}, + {"ADC Mute", NULL, "Analog Boost L"}, + {"ADC Mute", NULL, "Analog Boost R"}, + {"ADC L", NULL, "PLL"}, + {"ADC R", NULL, "PLL"}, + {"ADC L", NULL, "ADC Mute"}, + {"ADC R", NULL, "ADC Mute"}, +}; + +/************ + * CONTROLS * + ************/ + +static char const * const eq_band_enable_text[] = { + "Prescale only", + "Band1", + "Band1:2", + "Band1:3", + "Band1:4", + "Band1:5", + "Band1:6", +}; + +static char const * const level_detection_text[] = { + "Average", + "Peak", +}; + +static char const * const level_detection_window_text[] = { + "512 Samples", + "64 Samples", +}; + +static char const * const compressor_ratio_text[] = { + "Reserved", "1.5:1", "2:1", "3:1", "4:1", "5:1", "6:1", + "7:1", "8:1", "9:1", "10:1", "11:1", "12:1", "13:1", "14:1", + "15:1", "16:1", "17:1", "18:1", "19:1", "20:1", +}; + +static DECLARE_TLV_DB_SCALE(hpvol_scale, -8850, 75, 0); +static DECLARE_TLV_DB_SCALE(spkvol_scale, -7725, 75, 0); +static DECLARE_TLV_DB_SCALE(dacvol_scale, -9563, 38, 0); +static DECLARE_TLV_DB_SCALE(adcvol_scale, -7125, 38, 0); +static DECLARE_TLV_DB_SCALE(invol_scale, -1725, 75, 0); +static DECLARE_TLV_DB_SCALE(mic_boost_scale, 0, 1000, 0); +static DECLARE_TLV_DB_MINMAX(mugain_scale, 0, 4650); +static DECLARE_TLV_DB_MINMAX(compth_scale, -9562, 0); + +static const struct soc_enum eq1_band_enable_enum = + SOC_ENUM_SINGLE(R_CONFIG1, FB_CONFIG1_EQ1_BE, + ARRAY_SIZE(eq_band_enable_text), eq_band_enable_text); + +static const struct soc_enum eq2_band_enable_enum = + SOC_ENUM_SINGLE(R_CONFIG1, FB_CONFIG1_EQ2_BE, + ARRAY_SIZE(eq_band_enable_text), eq_band_enable_text); + +static const struct soc_enum cle_level_detection_enum = + SOC_ENUM_SINGLE(R_CLECTL, FB_CLECTL_LVL_MODE, + ARRAY_SIZE(level_detection_text), + level_detection_text); + +static const struct soc_enum cle_level_detection_window_enum = + SOC_ENUM_SINGLE(R_CLECTL, FB_CLECTL_WINDOWSEL, + ARRAY_SIZE(level_detection_window_text), + level_detection_window_text); + +static const struct soc_enum mbc_level_detection_enums[] = { + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE1, + ARRAY_SIZE(level_detection_text), + level_detection_text), + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE2, + ARRAY_SIZE(level_detection_text), + level_detection_text), + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE3, + ARRAY_SIZE(level_detection_text), + level_detection_text), +}; + +static const struct soc_enum mbc_level_detection_window_enums[] = { + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL1, + ARRAY_SIZE(level_detection_window_text), + level_detection_window_text), + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL2, + ARRAY_SIZE(level_detection_window_text), + level_detection_window_text), + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL3, + ARRAY_SIZE(level_detection_window_text), + level_detection_window_text), +}; + +static const struct soc_enum compressor_ratio_enum = + SOC_ENUM_SINGLE(R_CMPRAT, FB_CMPRAT, + ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text); + +static const struct soc_enum dac_mbc1_compressor_ratio_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT1, FB_DACMBCRAT1_RATIO, + ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text); + +static const struct soc_enum dac_mbc2_compressor_ratio_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT2, FB_DACMBCRAT2_RATIO, + ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text); + +static const struct soc_enum dac_mbc3_compressor_ratio_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT3, FB_DACMBCRAT3_RATIO, + ARRAY_SIZE(compressor_ratio_text), compressor_ratio_text); + +static int bytes_info_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} + +#define COEFF_RAM_CTL(xname, xcount, xaddr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = bytes_info_ext, \ + .get = coeff_ram_get, .put = coeff_ram_put, \ + .private_value = (unsigned long)&(struct coeff_ram_ctl) { \ + .addr = xaddr, \ + .bytes_ext = {.max = xcount, }, \ + } \ +} + +static const struct snd_kcontrol_new tscs42xx_snd_controls[] = { + /* Volumes */ + SOC_DOUBLE_R_TLV("Headphone Volume", R_HPVOLL, R_HPVOLR, + FB_HPVOLL, 0x7F, 0, hpvol_scale), + SOC_DOUBLE_R_TLV("Speaker Volume", R_SPKVOLL, R_SPKVOLR, + FB_SPKVOLL, 0x7F, 0, spkvol_scale), + SOC_DOUBLE_R_TLV("Master Volume", R_DACVOLL, R_DACVOLR, + FB_DACVOLL, 0xFF, 0, dacvol_scale), + SOC_DOUBLE_R_TLV("PCM Volume", R_ADCVOLL, R_ADCVOLR, + FB_ADCVOLL, 0xFF, 0, adcvol_scale), + SOC_DOUBLE_R_TLV("Input Volume", R_INVOLL, R_INVOLR, + FB_INVOLL, 0x3F, 0, invol_scale), + + /* INSEL */ + SOC_DOUBLE_R_TLV("Mic Boost Volume", R_INSELL, R_INSELR, + FB_INSELL_MICBSTL, FV_INSELL_MICBSTL_30DB, + 0, mic_boost_scale), + + /* Input Channel Map */ + SOC_ENUM("Input Channel Map", ch_map_select_enum), + + /* Mic Bias */ + SOC_SINGLE("Mic Bias Boost Switch", 0x71, 0x07, 1, 0), + + /* Headphone Auto Switching */ + SOC_SINGLE("Headphone Auto Switching Switch", + R_CTL, FB_CTL_HPSWEN, 1, 0), + SOC_SINGLE("Headphone Detect Polarity Toggle Switch", + R_CTL, FB_CTL_HPSWPOL, 1, 0), + + /* Coefficient Ram */ + COEFF_RAM_CTL("Cascade1L BiQuad1", BIQUAD_SIZE, 0x00), + COEFF_RAM_CTL("Cascade1L BiQuad2", BIQUAD_SIZE, 0x05), + COEFF_RAM_CTL("Cascade1L BiQuad3", BIQUAD_SIZE, 0x0a), + COEFF_RAM_CTL("Cascade1L BiQuad4", BIQUAD_SIZE, 0x0f), + COEFF_RAM_CTL("Cascade1L BiQuad5", BIQUAD_SIZE, 0x14), + COEFF_RAM_CTL("Cascade1L BiQuad6", BIQUAD_SIZE, 0x19), + + COEFF_RAM_CTL("Cascade1R BiQuad1", BIQUAD_SIZE, 0x20), + COEFF_RAM_CTL("Cascade1R BiQuad2", BIQUAD_SIZE, 0x25), + COEFF_RAM_CTL("Cascade1R BiQuad3", BIQUAD_SIZE, 0x2a), + COEFF_RAM_CTL("Cascade1R BiQuad4", BIQUAD_SIZE, 0x2f), + COEFF_RAM_CTL("Cascade1R BiQuad5", BIQUAD_SIZE, 0x34), + COEFF_RAM_CTL("Cascade1R BiQuad6", BIQUAD_SIZE, 0x39), + + COEFF_RAM_CTL("Cascade1L Prescale", COEFF_SIZE, 0x1f), + COEFF_RAM_CTL("Cascade1R Prescale", COEFF_SIZE, 0x3f), + + COEFF_RAM_CTL("Cascade2L BiQuad1", BIQUAD_SIZE, 0x40), + COEFF_RAM_CTL("Cascade2L BiQuad2", BIQUAD_SIZE, 0x45), + COEFF_RAM_CTL("Cascade2L BiQuad3", BIQUAD_SIZE, 0x4a), + COEFF_RAM_CTL("Cascade2L BiQuad4", BIQUAD_SIZE, 0x4f), + COEFF_RAM_CTL("Cascade2L BiQuad5", BIQUAD_SIZE, 0x54), + COEFF_RAM_CTL("Cascade2L BiQuad6", BIQUAD_SIZE, 0x59), + + COEFF_RAM_CTL("Cascade2R BiQuad1", BIQUAD_SIZE, 0x60), + COEFF_RAM_CTL("Cascade2R BiQuad2", BIQUAD_SIZE, 0x65), + COEFF_RAM_CTL("Cascade2R BiQuad3", BIQUAD_SIZE, 0x6a), + COEFF_RAM_CTL("Cascade2R BiQuad4", BIQUAD_SIZE, 0x6f), + COEFF_RAM_CTL("Cascade2R BiQuad5", BIQUAD_SIZE, 0x74), + COEFF_RAM_CTL("Cascade2R BiQuad6", BIQUAD_SIZE, 0x79), + + COEFF_RAM_CTL("Cascade2L Prescale", COEFF_SIZE, 0x5f), + COEFF_RAM_CTL("Cascade2R Prescale", COEFF_SIZE, 0x7f), + + COEFF_RAM_CTL("Bass Extraction BiQuad1", BIQUAD_SIZE, 0x80), + COEFF_RAM_CTL("Bass Extraction BiQuad2", BIQUAD_SIZE, 0x85), + + COEFF_RAM_CTL("Bass Non Linear Function 1", COEFF_SIZE, 0x8a), + COEFF_RAM_CTL("Bass Non Linear Function 2", COEFF_SIZE, 0x8b), + + COEFF_RAM_CTL("Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c), + + COEFF_RAM_CTL("Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91), + + COEFF_RAM_CTL("Bass Mix", COEFF_SIZE, 0x96), + + COEFF_RAM_CTL("Treb Extraction BiQuad1", BIQUAD_SIZE, 0x97), + COEFF_RAM_CTL("Treb Extraction BiQuad2", BIQUAD_SIZE, 0x9c), + + COEFF_RAM_CTL("Treb Non Linear Function 1", COEFF_SIZE, 0xa1), + COEFF_RAM_CTL("Treb Non Linear Function 2", COEFF_SIZE, 0xa2), + + COEFF_RAM_CTL("Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3), + + COEFF_RAM_CTL("Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8), + + COEFF_RAM_CTL("Treb Mix", COEFF_SIZE, 0xad), + + COEFF_RAM_CTL("3D", COEFF_SIZE, 0xae), + + COEFF_RAM_CTL("3D Mix", COEFF_SIZE, 0xaf), + + COEFF_RAM_CTL("MBC1 BiQuad1", BIQUAD_SIZE, 0xb0), + COEFF_RAM_CTL("MBC1 BiQuad2", BIQUAD_SIZE, 0xb5), + + COEFF_RAM_CTL("MBC2 BiQuad1", BIQUAD_SIZE, 0xba), + COEFF_RAM_CTL("MBC2 BiQuad2", BIQUAD_SIZE, 0xbf), + + COEFF_RAM_CTL("MBC3 BiQuad1", BIQUAD_SIZE, 0xc4), + COEFF_RAM_CTL("MBC3 BiQuad2", BIQUAD_SIZE, 0xc9), + + /* EQ */ + SOC_SINGLE("EQ1 Switch", R_CONFIG1, FB_CONFIG1_EQ1_EN, 1, 0), + SOC_SINGLE("EQ2 Switch", R_CONFIG1, FB_CONFIG1_EQ2_EN, 1, 0), + SOC_ENUM("EQ1 Band Enable", eq1_band_enable_enum), + SOC_ENUM("EQ2 Band Enable", eq2_band_enable_enum), + + /* CLE */ + SOC_ENUM("CLE Level Detect", + cle_level_detection_enum), + SOC_ENUM("CLE Level Detect Win", + cle_level_detection_window_enum), + SOC_SINGLE("Expander Switch", + R_CLECTL, FB_CLECTL_EXP_EN, 1, 0), + SOC_SINGLE("Limiter Switch", + R_CLECTL, FB_CLECTL_LIMIT_EN, 1, 0), + SOC_SINGLE("Comp Switch", + R_CLECTL, FB_CLECTL_COMP_EN, 1, 0), + SOC_SINGLE_TLV("CLE Make-Up Gain Volume", + R_MUGAIN, FB_MUGAIN_CLEMUG, 0x1f, 0, mugain_scale), + SOC_SINGLE_TLV("Comp Thresh Volume", + R_COMPTH, FB_COMPTH, 0xff, 0, compth_scale), + SOC_ENUM("Comp Ratio", compressor_ratio_enum), + SND_SOC_BYTES("Comp Atk Time", R_CATKTCL, 2), + + /* Effects */ + SOC_SINGLE("3D Switch", R_FXCTL, FB_FXCTL_3DEN, 1, 0), + SOC_SINGLE("Treble Switch", R_FXCTL, FB_FXCTL_TEEN, 1, 0), + SOC_SINGLE("Treble Bypass Switch", R_FXCTL, FB_FXCTL_TNLFBYPASS, 1, 0), + SOC_SINGLE("Bass Switch", R_FXCTL, FB_FXCTL_BEEN, 1, 0), + SOC_SINGLE("Bass Bypass Switch", R_FXCTL, FB_FXCTL_BNLFBYPASS, 1, 0), + + /* MBC */ + SOC_SINGLE("MBC Band1 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN1, 1, 0), + SOC_SINGLE("MBC Band2 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN2, 1, 0), + SOC_SINGLE("MBC Band3 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN3, 1, 0), + SOC_ENUM("MBC Band1 Level Detect", + mbc_level_detection_enums[0]), + SOC_ENUM("MBC Band2 Level Detect", + mbc_level_detection_enums[1]), + SOC_ENUM("MBC Band3 Level Detect", + mbc_level_detection_enums[2]), + SOC_ENUM("MBC Band1 Level Detect Win", + mbc_level_detection_window_enums[0]), + SOC_ENUM("MBC Band2 Level Detect Win", + mbc_level_detection_window_enums[1]), + SOC_ENUM("MBC Band3 Level Detect Win", + mbc_level_detection_window_enums[2]), + + SOC_SINGLE("MBC1 Phase Invert Switch", + R_DACMBCMUG1, FB_DACMBCMUG1_PHASE, 1, 0), + SOC_SINGLE_TLV("DAC MBC1 Make-Up Gain Volume", + R_DACMBCMUG1, FB_DACMBCMUG1_MUGAIN, 0x1f, 0, mugain_scale), + SOC_SINGLE_TLV("DAC MBC1 Comp Thresh Volume", + R_DACMBCTHR1, FB_DACMBCTHR1_THRESH, 0xff, 0, compth_scale), + SOC_ENUM("DAC MBC1 Comp Ratio", + dac_mbc1_compressor_ratio_enum), + SND_SOC_BYTES("DAC MBC1 Comp Atk Time", R_DACMBCATK1L, 2), + SND_SOC_BYTES("DAC MBC1 Comp Rel Time Const", + R_DACMBCREL1L, 2), + + SOC_SINGLE("MBC2 Phase Invert Switch", + R_DACMBCMUG2, FB_DACMBCMUG2_PHASE, 1, 0), + SOC_SINGLE_TLV("DAC MBC2 Make-Up Gain Volume", + R_DACMBCMUG2, FB_DACMBCMUG2_MUGAIN, 0x1f, 0, mugain_scale), + SOC_SINGLE_TLV("DAC MBC2 Comp Thresh Volume", + R_DACMBCTHR2, FB_DACMBCTHR2_THRESH, 0xff, 0, compth_scale), + SOC_ENUM("DAC MBC2 Comp Ratio", + dac_mbc2_compressor_ratio_enum), + SND_SOC_BYTES("DAC MBC2 Comp Atk Time", R_DACMBCATK2L, 2), + SND_SOC_BYTES("DAC MBC2 Comp Rel Time Const", + R_DACMBCREL2L, 2), + + SOC_SINGLE("MBC3 Phase Invert Switch", + R_DACMBCMUG3, FB_DACMBCMUG3_PHASE, 1, 0), + SOC_SINGLE_TLV("DAC MBC3 Make-Up Gain Volume", + R_DACMBCMUG3, FB_DACMBCMUG3_MUGAIN, 0x1f, 0, mugain_scale), + SOC_SINGLE_TLV("DAC MBC3 Comp Thresh Volume", + R_DACMBCTHR3, FB_DACMBCTHR3_THRESH, 0xff, 0, compth_scale), + SOC_ENUM("DAC MBC3 Comp Ratio", + dac_mbc3_compressor_ratio_enum), + SND_SOC_BYTES("DAC MBC3 Comp Atk Time", R_DACMBCATK3L, 2), + SND_SOC_BYTES("DAC MBC3 Comp Rel Time Const", + R_DACMBCREL3L, 2), +}; + +static int setup_sample_format(struct snd_soc_component *component, + snd_pcm_format_t format) +{ + unsigned int width; + int ret; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + width = RV_AIC1_WL_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + width = RV_AIC1_WL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + width = RV_AIC1_WL_24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + width = RV_AIC1_WL_32; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unsupported format width (%d)\n", ret); + return ret; + } + ret = snd_soc_component_update_bits(component, + R_AIC1, RM_AIC1_WL, width); + if (ret < 0) { + dev_err(component->dev, + "Failed to set sample width (%d)\n", ret); + return ret; + } + + return 0; +} + +static int setup_sample_rate(struct snd_soc_component *component, + unsigned int rate) +{ + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + unsigned int br, bm; + int ret; + + switch (rate) { + case 8000: + br = RV_DACSR_DBR_32; + bm = RV_DACSR_DBM_PT25; + break; + case 16000: + br = RV_DACSR_DBR_32; + bm = RV_DACSR_DBM_PT5; + break; + case 24000: + br = RV_DACSR_DBR_48; + bm = RV_DACSR_DBM_PT5; + break; + case 32000: + br = RV_DACSR_DBR_32; + bm = RV_DACSR_DBM_1; + break; + case 48000: + br = RV_DACSR_DBR_48; + bm = RV_DACSR_DBM_1; + break; + case 96000: + br = RV_DACSR_DBR_48; + bm = RV_DACSR_DBM_2; + break; + case 11025: + br = RV_DACSR_DBR_44_1; + bm = RV_DACSR_DBM_PT25; + break; + case 22050: + br = RV_DACSR_DBR_44_1; + bm = RV_DACSR_DBM_PT5; + break; + case 44100: + br = RV_DACSR_DBR_44_1; + bm = RV_DACSR_DBM_1; + break; + case 88200: + br = RV_DACSR_DBR_44_1; + bm = RV_DACSR_DBM_2; + break; + default: + dev_err(component->dev, "Unsupported sample rate %d\n", rate); + return -EINVAL; + } + + /* DAC and ADC share bit and frame clock */ + ret = snd_soc_component_update_bits(component, + R_DACSR, RM_DACSR_DBR, br); + if (ret < 0) { + dev_err(component->dev, + "Failed to update register (%d)\n", ret); + return ret; + } + ret = snd_soc_component_update_bits(component, + R_DACSR, RM_DACSR_DBM, bm); + if (ret < 0) { + dev_err(component->dev, + "Failed to update register (%d)\n", ret); + return ret; + } + ret = snd_soc_component_update_bits(component, + R_ADCSR, RM_DACSR_DBR, br); + if (ret < 0) { + dev_err(component->dev, + "Failed to update register (%d)\n", ret); + return ret; + } + ret = snd_soc_component_update_bits(component, + R_ADCSR, RM_DACSR_DBM, bm); + if (ret < 0) { + dev_err(component->dev, + "Failed to update register (%d)\n", ret); + return ret; + } + + mutex_lock(&tscs42xx->audio_params_lock); + + tscs42xx->samplerate = rate; + + mutex_unlock(&tscs42xx->audio_params_lock); + + return 0; +} + +struct reg_setting { + unsigned int addr; + unsigned int val; + unsigned int mask; +}; + +#define PLL_REG_SETTINGS_COUNT 13 +struct pll_ctl { + int input_freq; + struct reg_setting settings[PLL_REG_SETTINGS_COUNT]; +}; + +#define PLL_CTL(f, rt, rd, r1b_l, r9, ra, rb, \ + rc, r12, r1b_h, re, rf, r10, r11) \ + { \ + .input_freq = f, \ + .settings = { \ + {R_TIMEBASE, rt, 0xFF}, \ + {R_PLLCTLD, rd, 0xFF}, \ + {R_PLLCTL1B, r1b_l, 0x0F}, \ + {R_PLLCTL9, r9, 0xFF}, \ + {R_PLLCTLA, ra, 0xFF}, \ + {R_PLLCTLB, rb, 0xFF}, \ + {R_PLLCTLC, rc, 0xFF}, \ + {R_PLLCTL12, r12, 0xFF}, \ + {R_PLLCTL1B, r1b_h, 0xF0}, \ + {R_PLLCTLE, re, 0xFF}, \ + {R_PLLCTLF, rf, 0xFF}, \ + {R_PLLCTL10, r10, 0xFF}, \ + {R_PLLCTL11, r11, 0xFF}, \ + }, \ + } + +static const struct pll_ctl pll_ctls[] = { + PLL_CTL(1411200, 0x05, + 0x39, 0x04, 0x07, 0x02, 0xC3, 0x04, + 0x1B, 0x10, 0x03, 0x03, 0xD0, 0x02), + PLL_CTL(1536000, 0x05, + 0x1A, 0x04, 0x02, 0x03, 0xE0, 0x01, + 0x1A, 0x10, 0x02, 0x03, 0xB9, 0x01), + PLL_CTL(2822400, 0x0A, + 0x23, 0x04, 0x07, 0x04, 0xC3, 0x04, + 0x22, 0x10, 0x05, 0x03, 0x58, 0x02), + PLL_CTL(3072000, 0x0B, + 0x22, 0x04, 0x07, 0x03, 0x48, 0x03, + 0x1A, 0x10, 0x04, 0x03, 0xB9, 0x01), + PLL_CTL(5644800, 0x15, + 0x23, 0x04, 0x0E, 0x04, 0xC3, 0x04, + 0x1A, 0x10, 0x08, 0x03, 0xE0, 0x01), + PLL_CTL(6144000, 0x17, + 0x1A, 0x04, 0x08, 0x03, 0xE0, 0x01, + 0x1A, 0x10, 0x08, 0x03, 0xB9, 0x01), + PLL_CTL(12000000, 0x2E, + 0x1B, 0x04, 0x19, 0x03, 0x00, 0x03, + 0x2A, 0x10, 0x19, 0x05, 0x98, 0x04), + PLL_CTL(19200000, 0x4A, + 0x13, 0x04, 0x14, 0x03, 0x80, 0x01, + 0x1A, 0x10, 0x19, 0x03, 0xB9, 0x01), + PLL_CTL(22000000, 0x55, + 0x2A, 0x04, 0x37, 0x05, 0x00, 0x06, + 0x22, 0x10, 0x26, 0x03, 0x49, 0x02), + PLL_CTL(22579200, 0x57, + 0x22, 0x04, 0x31, 0x03, 0x20, 0x03, + 0x1A, 0x10, 0x1D, 0x03, 0xB3, 0x01), + PLL_CTL(24000000, 0x5D, + 0x13, 0x04, 0x19, 0x03, 0x80, 0x01, + 0x1B, 0x10, 0x19, 0x05, 0x4C, 0x02), + PLL_CTL(24576000, 0x5F, + 0x13, 0x04, 0x1D, 0x03, 0xB3, 0x01, + 0x22, 0x10, 0x40, 0x03, 0x72, 0x03), + PLL_CTL(27000000, 0x68, + 0x22, 0x04, 0x4B, 0x03, 0x00, 0x04, + 0x2A, 0x10, 0x7D, 0x03, 0x20, 0x06), + PLL_CTL(36000000, 0x8C, + 0x1B, 0x04, 0x4B, 0x03, 0x00, 0x03, + 0x2A, 0x10, 0x7D, 0x03, 0x98, 0x04), + PLL_CTL(25000000, 0x61, + 0x1B, 0x04, 0x37, 0x03, 0x2B, 0x03, + 0x1A, 0x10, 0x2A, 0x03, 0x39, 0x02), + PLL_CTL(26000000, 0x65, + 0x23, 0x04, 0x41, 0x05, 0x00, 0x06, + 0x1A, 0x10, 0x26, 0x03, 0xEF, 0x01), + PLL_CTL(12288000, 0x2F, + 0x1A, 0x04, 0x12, 0x03, 0x1C, 0x02, + 0x22, 0x10, 0x20, 0x03, 0x72, 0x03), + PLL_CTL(40000000, 0x9B, + 0x22, 0x08, 0x7D, 0x03, 0x80, 0x04, + 0x23, 0x10, 0x7D, 0x05, 0xE4, 0x06), + PLL_CTL(512000, 0x01, + 0x22, 0x04, 0x01, 0x03, 0xD0, 0x02, + 0x1B, 0x10, 0x01, 0x04, 0x72, 0x03), + PLL_CTL(705600, 0x02, + 0x22, 0x04, 0x02, 0x03, 0x15, 0x04, + 0x22, 0x10, 0x01, 0x04, 0x80, 0x02), + PLL_CTL(1024000, 0x03, + 0x22, 0x04, 0x02, 0x03, 0xD0, 0x02, + 0x1B, 0x10, 0x02, 0x04, 0x72, 0x03), + PLL_CTL(2048000, 0x07, + 0x22, 0x04, 0x04, 0x03, 0xD0, 0x02, + 0x1B, 0x10, 0x04, 0x04, 0x72, 0x03), + PLL_CTL(2400000, 0x08, + 0x22, 0x04, 0x05, 0x03, 0x00, 0x03, + 0x23, 0x10, 0x05, 0x05, 0x98, 0x04), +}; + +static const struct pll_ctl *get_pll_ctl(int input_freq) +{ + int i; + const struct pll_ctl *pll_ctl = NULL; + + for (i = 0; i < ARRAY_SIZE(pll_ctls); ++i) + if (input_freq == pll_ctls[i].input_freq) { + pll_ctl = &pll_ctls[i]; + break; + } + + return pll_ctl; +} + +static int set_pll_ctl_from_input_freq(struct snd_soc_component *component, + const int input_freq) +{ + int ret; + int i; + const struct pll_ctl *pll_ctl; + + pll_ctl = get_pll_ctl(input_freq); + if (!pll_ctl) { + ret = -EINVAL; + dev_err(component->dev, "No PLL input entry for %d (%d)\n", + input_freq, ret); + return ret; + } + + for (i = 0; i < PLL_REG_SETTINGS_COUNT; ++i) { + ret = snd_soc_component_update_bits(component, + pll_ctl->settings[i].addr, + pll_ctl->settings[i].mask, + pll_ctl->settings[i].val); + if (ret < 0) { + dev_err(component->dev, "Failed to set pll ctl (%d)\n", + ret); + return ret; + } + } + + return 0; +} + +static int tscs42xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_component *component = codec_dai->component; + int ret; + + ret = setup_sample_format(component, params_format(params)); + if (ret < 0) { + dev_err(component->dev, "Failed to setup sample format (%d)\n", + ret); + return ret; + } + + ret = setup_sample_rate(component, params_rate(params)); + if (ret < 0) { + dev_err(component->dev, + "Failed to setup sample rate (%d)\n", ret); + return ret; + } + + return 0; +} + +static inline int dac_mute(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_update_bits(component, + R_CNVRTR1, RM_CNVRTR1_DACMU, + RV_CNVRTR1_DACMU_ENABLE); + if (ret < 0) { + dev_err(component->dev, "Failed to mute DAC (%d)\n", + ret); + return ret; + } + + return 0; +} + +static inline int dac_unmute(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_update_bits(component, + R_CNVRTR1, RM_CNVRTR1_DACMU, + RV_CNVRTR1_DACMU_DISABLE); + if (ret < 0) { + dev_err(component->dev, "Failed to unmute DAC (%d)\n", + ret); + return ret; + } + + return 0; +} + +static inline int adc_mute(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_update_bits(component, + R_CNVRTR0, RM_CNVRTR0_ADCMU, RV_CNVRTR0_ADCMU_ENABLE); + if (ret < 0) { + dev_err(component->dev, "Failed to mute ADC (%d)\n", + ret); + return ret; + } + + return 0; +} + +static inline int adc_unmute(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_update_bits(component, + R_CNVRTR0, RM_CNVRTR0_ADCMU, RV_CNVRTR0_ADCMU_DISABLE); + if (ret < 0) { + dev_err(component->dev, "Failed to unmute ADC (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int tscs42xx_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + int ret; + + if (mute) + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = dac_mute(component); + else + ret = adc_mute(component); + else + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = dac_unmute(component); + else + ret = adc_unmute(component); + + return ret; +} + +static int tscs42xx_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + int ret; + + /* Consumer mode not supported since it needs always-on frame clock */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + ret = snd_soc_component_update_bits(component, + R_AIC1, RM_AIC1_MS, RV_AIC1_MS_MASTER); + if (ret < 0) { + dev_err(component->dev, + "Failed to set codec DAI master (%d)\n", ret); + return ret; + } + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unsupported format (%d)\n", ret); + return ret; + } + + return 0; +} + +static int tscs42xx_set_dai_bclk_ratio(struct snd_soc_dai *codec_dai, + unsigned int ratio) +{ + struct snd_soc_component *component = codec_dai->component; + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + unsigned int value; + int ret = 0; + + switch (ratio) { + case 32: + value = RV_DACSR_DBCM_32; + break; + case 40: + value = RV_DACSR_DBCM_40; + break; + case 64: + value = RV_DACSR_DBCM_64; + break; + default: + dev_err(component->dev, "Unsupported bclk ratio (%d)\n", ret); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, + R_DACSR, RM_DACSR_DBCM, value); + if (ret < 0) { + dev_err(component->dev, + "Failed to set DAC BCLK ratio (%d)\n", ret); + return ret; + } + ret = snd_soc_component_update_bits(component, + R_ADCSR, RM_ADCSR_ABCM, value); + if (ret < 0) { + dev_err(component->dev, + "Failed to set ADC BCLK ratio (%d)\n", ret); + return ret; + } + + mutex_lock(&tscs42xx->audio_params_lock); + + tscs42xx->bclk_ratio = ratio; + + mutex_unlock(&tscs42xx->audio_params_lock); + + return 0; +} + +static const struct snd_soc_dai_ops tscs42xx_dai_ops = { + .hw_params = tscs42xx_hw_params, + .mute_stream = tscs42xx_mute_stream, + .set_fmt = tscs42xx_set_dai_fmt, + .set_bclk_ratio = tscs42xx_set_dai_bclk_ratio, +}; + +static int part_is_valid(struct tscs42xx *tscs42xx) +{ + int val; + int ret; + unsigned int reg; + + ret = regmap_read(tscs42xx->regmap, R_DEVIDH, ®); + if (ret < 0) + return ret; + + val = reg << 8; + ret = regmap_read(tscs42xx->regmap, R_DEVIDL, ®); + if (ret < 0) + return ret; + + val |= reg; + + switch (val) { + case 0x4A74: + case 0x4A73: + return true; + default: + return false; + } +} + +static int set_sysclk(struct snd_soc_component *component) +{ + struct tscs42xx *tscs42xx = snd_soc_component_get_drvdata(component); + unsigned long freq; + int ret; + + switch (tscs42xx->sysclk_src_id) { + case TSCS42XX_PLL_SRC_XTAL: + case TSCS42XX_PLL_SRC_MCLK1: + ret = snd_soc_component_write(component, R_PLLREFSEL, + RV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1 | + RV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1); + if (ret < 0) { + dev_err(component->dev, + "Failed to set pll reference input (%d)\n", + ret); + return ret; + } + break; + case TSCS42XX_PLL_SRC_MCLK2: + ret = snd_soc_component_write(component, R_PLLREFSEL, + RV_PLLREFSEL_PLL1_REF_SEL_MCLK2 | + RV_PLLREFSEL_PLL2_REF_SEL_MCLK2); + if (ret < 0) { + dev_err(component->dev, + "Failed to set PLL reference (%d)\n", ret); + return ret; + } + break; + default: + dev_err(component->dev, "pll src is unsupported\n"); + return -EINVAL; + } + + freq = clk_get_rate(tscs42xx->sysclk); + ret = set_pll_ctl_from_input_freq(component, freq); + if (ret < 0) { + dev_err(component->dev, + "Failed to setup PLL input freq (%d)\n", ret); + return ret; + } + + return 0; +} + +static int tscs42xx_probe(struct snd_soc_component *component) +{ + return set_sysclk(component); +} + +static const struct snd_soc_component_driver soc_codec_dev_tscs42xx = { + .probe = tscs42xx_probe, + .dapm_widgets = tscs42xx_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tscs42xx_dapm_widgets), + .dapm_routes = tscs42xx_intercon, + .num_dapm_routes = ARRAY_SIZE(tscs42xx_intercon), + .controls = tscs42xx_snd_controls, + .num_controls = ARRAY_SIZE(tscs42xx_snd_controls), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static inline void init_coeff_ram_cache(struct tscs42xx *tscs42xx) +{ + static const u8 norm_addrs[] = { + 0x00, 0x05, 0x0a, 0x0f, 0x14, 0x19, 0x1f, 0x20, 0x25, 0x2a, + 0x2f, 0x34, 0x39, 0x3f, 0x40, 0x45, 0x4a, 0x4f, 0x54, 0x59, + 0x5f, 0x60, 0x65, 0x6a, 0x6f, 0x74, 0x79, 0x7f, 0x80, 0x85, + 0x8c, 0x91, 0x96, 0x97, 0x9c, 0xa3, 0xa8, 0xad, 0xaf, 0xb0, + 0xb5, 0xba, 0xbf, 0xc4, 0xc9, + }; + u8 *coeff_ram = tscs42xx->coeff_ram; + int i; + + for (i = 0; i < ARRAY_SIZE(norm_addrs); i++) + coeff_ram[((norm_addrs[i] + 1) * COEFF_SIZE) - 1] = 0x40; +} + +#define TSCS42XX_RATES SNDRV_PCM_RATE_8000_96000 + +#define TSCS42XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver tscs42xx_dai = { + .name = "tscs42xx-HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = TSCS42XX_RATES, + .formats = TSCS42XX_FORMATS,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 2, + .channels_max = 2, + .rates = TSCS42XX_RATES, + .formats = TSCS42XX_FORMATS,}, + .ops = &tscs42xx_dai_ops, + .symmetric_rate = 1, + .symmetric_channels = 1, + .symmetric_sample_bits = 1, +}; + +static const struct reg_sequence tscs42xx_patch[] = { + { R_AIC2, RV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED }, +}; + +static char const * const src_names[TSCS42XX_PLL_SRC_CNT] = { + "xtal", "mclk1", "mclk2"}; + +static int tscs42xx_i2c_probe(struct i2c_client *i2c) +{ + struct tscs42xx *tscs42xx; + int src; + int ret; + + tscs42xx = devm_kzalloc(&i2c->dev, sizeof(*tscs42xx), GFP_KERNEL); + if (!tscs42xx) { + ret = -ENOMEM; + dev_err(&i2c->dev, + "Failed to allocate memory for data (%d)\n", ret); + return ret; + } + i2c_set_clientdata(i2c, tscs42xx); + + for (src = TSCS42XX_PLL_SRC_XTAL; src < TSCS42XX_PLL_SRC_CNT; src++) { + tscs42xx->sysclk = devm_clk_get(&i2c->dev, src_names[src]); + if (!IS_ERR(tscs42xx->sysclk)) { + break; + } else if (PTR_ERR(tscs42xx->sysclk) != -ENOENT) { + ret = PTR_ERR(tscs42xx->sysclk); + dev_err(&i2c->dev, "Failed to get sysclk (%d)\n", ret); + return ret; + } + } + if (src == TSCS42XX_PLL_SRC_CNT) { + ret = -EINVAL; + dev_err(&i2c->dev, "Failed to get a valid clock name (%d)\n", + ret); + return ret; + } + tscs42xx->sysclk_src_id = src; + + tscs42xx->regmap = devm_regmap_init_i2c(i2c, &tscs42xx_regmap); + if (IS_ERR(tscs42xx->regmap)) { + ret = PTR_ERR(tscs42xx->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap (%d)\n", ret); + return ret; + } + + init_coeff_ram_cache(tscs42xx); + + ret = part_is_valid(tscs42xx); + if (ret <= 0) { + dev_err(&i2c->dev, "No valid part (%d)\n", ret); + ret = -ENODEV; + return ret; + } + + ret = regmap_write(tscs42xx->regmap, R_RESET, RV_RESET_ENABLE); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to reset device (%d)\n", ret); + return ret; + } + + ret = regmap_register_patch(tscs42xx->regmap, tscs42xx_patch, + ARRAY_SIZE(tscs42xx_patch)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to apply patch (%d)\n", ret); + return ret; + } + + mutex_init(&tscs42xx->audio_params_lock); + mutex_init(&tscs42xx->coeff_ram_lock); + mutex_init(&tscs42xx->pll_lock); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_tscs42xx, &tscs42xx_dai, 1); + if (ret) { + dev_err(&i2c->dev, "Failed to register codec (%d)\n", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id tscs42xx_i2c_id[] = { + { "tscs42A1", 0 }, + { "tscs42A2", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tscs42xx_i2c_id); + +static const struct of_device_id tscs42xx_of_match[] = { + { .compatible = "tempo,tscs42A1", }, + { .compatible = "tempo,tscs42A2", }, + { } +}; +MODULE_DEVICE_TABLE(of, tscs42xx_of_match); + +static struct i2c_driver tscs42xx_i2c_driver = { + .driver = { + .name = "tscs42xx", + .of_match_table = tscs42xx_of_match, + }, + .probe = tscs42xx_i2c_probe, + .id_table = tscs42xx_i2c_id, +}; + +module_i2c_driver(tscs42xx_i2c_driver); + +MODULE_AUTHOR("Tempo Semiconductor + +#ifndef __WOOKIE_H__ +#define __WOOKIE_H__ + +enum { + TSCS42XX_PLL_SRC_XTAL, + TSCS42XX_PLL_SRC_MCLK1, + TSCS42XX_PLL_SRC_MCLK2, + TSCS42XX_PLL_SRC_CNT, +}; + +#define R_HPVOLL 0x0 +#define R_HPVOLR 0x1 +#define R_SPKVOLL 0x2 +#define R_SPKVOLR 0x3 +#define R_DACVOLL 0x4 +#define R_DACVOLR 0x5 +#define R_ADCVOLL 0x6 +#define R_ADCVOLR 0x7 +#define R_INVOLL 0x8 +#define R_INVOLR 0x9 +#define R_INMODE 0x0B +#define R_INSELL 0x0C +#define R_INSELR 0x0D +#define R_AIC1 0x13 +#define R_AIC2 0x14 +#define R_CNVRTR0 0x16 +#define R_ADCSR 0x17 +#define R_CNVRTR1 0x18 +#define R_DACSR 0x19 +#define R_PWRM1 0x1A +#define R_PWRM2 0x1B +#define R_CTL 0x1C +#define R_CONFIG0 0x1F +#define R_CONFIG1 0x20 +#define R_DMICCTL 0x24 +#define R_CLECTL 0x25 +#define R_MUGAIN 0x26 +#define R_COMPTH 0x27 +#define R_CMPRAT 0x28 +#define R_CATKTCL 0x29 +#define R_CATKTCH 0x2A +#define R_CRELTCL 0x2B +#define R_CRELTCH 0x2C +#define R_LIMTH 0x2D +#define R_LIMTGT 0x2E +#define R_LATKTCL 0x2F +#define R_LATKTCH 0x30 +#define R_LRELTCL 0x31 +#define R_LRELTCH 0x32 +#define R_EXPTH 0x33 +#define R_EXPRAT 0x34 +#define R_XATKTCL 0x35 +#define R_XATKTCH 0x36 +#define R_XRELTCL 0x37 +#define R_XRELTCH 0x38 +#define R_FXCTL 0x39 +#define R_DACCRWRL 0x3A +#define R_DACCRWRM 0x3B +#define R_DACCRWRH 0x3C +#define R_DACCRRDL 0x3D +#define R_DACCRRDM 0x3E +#define R_DACCRRDH 0x3F +#define R_DACCRADDR 0x40 +#define R_DCOFSEL 0x41 +#define R_PLLCTL9 0x4E +#define R_PLLCTLA 0x4F +#define R_PLLCTLB 0x50 +#define R_PLLCTLC 0x51 +#define R_PLLCTLD 0x52 +#define R_PLLCTLE 0x53 +#define R_PLLCTLF 0x54 +#define R_PLLCTL10 0x55 +#define R_PLLCTL11 0x56 +#define R_PLLCTL12 0x57 +#define R_PLLCTL1B 0x60 +#define R_PLLCTL1C 0x61 +#define R_TIMEBASE 0x77 +#define R_DEVIDL 0x7D +#define R_DEVIDH 0x7E +#define R_RESET 0x80 +#define R_DACCRSTAT 0x8A +#define R_PLLCTL0 0x8E +#define R_PLLREFSEL 0x8F +#define R_DACMBCEN 0xC7 +#define R_DACMBCCTL 0xC8 +#define R_DACMBCMUG1 0xC9 +#define R_DACMBCTHR1 0xCA +#define R_DACMBCRAT1 0xCB +#define R_DACMBCATK1L 0xCC +#define R_DACMBCATK1H 0xCD +#define R_DACMBCREL1L 0xCE +#define R_DACMBCREL1H 0xCF +#define R_DACMBCMUG2 0xD0 +#define R_DACMBCTHR2 0xD1 +#define R_DACMBCRAT2 0xD2 +#define R_DACMBCATK2L 0xD3 +#define R_DACMBCATK2H 0xD4 +#define R_DACMBCREL2L 0xD5 +#define R_DACMBCREL2H 0xD6 +#define R_DACMBCMUG3 0xD7 +#define R_DACMBCTHR3 0xD8 +#define R_DACMBCRAT3 0xD9 +#define R_DACMBCATK3L 0xDA +#define R_DACMBCATK3H 0xDB +#define R_DACMBCREL3L 0xDC +#define R_DACMBCREL3H 0xDD + +/* Helpers */ +#define RM(m, b) ((m)<<(b)) +#define RV(v, b) ((v)<<(b)) + +/**************************** + * R_HPVOLL (0x0) * + ****************************/ + +/* Field Offsets */ +#define FB_HPVOLL 0 + +/* Field Masks */ +#define FM_HPVOLL 0X7F + +/* Field Values */ +#define FV_HPVOLL_P6DB 0x7F +#define FV_HPVOLL_N88PT5DB 0x1 +#define FV_HPVOLL_MUTE 0x0 + +/* Register Masks */ +#define RM_HPVOLL RM(FM_HPVOLL, FB_HPVOLL) + +/* Register Values */ +#define RV_HPVOLL_P6DB RV(FV_HPVOLL_P6DB, FB_HPVOLL) +#define RV_HPVOLL_N88PT5DB RV(FV_HPVOLL_N88PT5DB, FB_HPVOLL) +#define RV_HPVOLL_MUTE RV(FV_HPVOLL_MUTE, FB_HPVOLL) + +/**************************** + * R_HPVOLR (0x1) * + ****************************/ + +/* Field Offsets */ +#define FB_HPVOLR 0 + +/* Field Masks */ +#define FM_HPVOLR 0X7F + +/* Field Values */ +#define FV_HPVOLR_P6DB 0x7F +#define FV_HPVOLR_N88PT5DB 0x1 +#define FV_HPVOLR_MUTE 0x0 + +/* Register Masks */ +#define RM_HPVOLR RM(FM_HPVOLR, FB_HPVOLR) + +/* Register Values */ +#define RV_HPVOLR_P6DB RV(FV_HPVOLR_P6DB, FB_HPVOLR) +#define RV_HPVOLR_N88PT5DB RV(FV_HPVOLR_N88PT5DB, FB_HPVOLR) +#define RV_HPVOLR_MUTE RV(FV_HPVOLR_MUTE, FB_HPVOLR) + +/***************************** + * R_SPKVOLL (0x2) * + *****************************/ + +/* Field Offsets */ +#define FB_SPKVOLL 0 + +/* Field Masks */ +#define FM_SPKVOLL 0X7F + +/* Field Values */ +#define FV_SPKVOLL_P12DB 0x7F +#define FV_SPKVOLL_N77PT25DB 0x8 +#define FV_SPKVOLL_MUTE 0x0 + +/* Register Masks */ +#define RM_SPKVOLL RM(FM_SPKVOLL, FB_SPKVOLL) + +/* Register Values */ +#define RV_SPKVOLL_P12DB RV(FV_SPKVOLL_P12DB, FB_SPKVOLL) +#define RV_SPKVOLL_N77PT25DB \ + RV(FV_SPKVOLL_N77PT25DB, FB_SPKVOLL) + +#define RV_SPKVOLL_MUTE RV(FV_SPKVOLL_MUTE, FB_SPKVOLL) + +/***************************** + * R_SPKVOLR (0x3) * + *****************************/ + +/* Field Offsets */ +#define FB_SPKVOLR 0 + +/* Field Masks */ +#define FM_SPKVOLR 0X7F + +/* Field Values */ +#define FV_SPKVOLR_P12DB 0x7F +#define FV_SPKVOLR_N77PT25DB 0x8 +#define FV_SPKVOLR_MUTE 0x0 + +/* Register Masks */ +#define RM_SPKVOLR RM(FM_SPKVOLR, FB_SPKVOLR) + +/* Register Values */ +#define RV_SPKVOLR_P12DB RV(FV_SPKVOLR_P12DB, FB_SPKVOLR) +#define RV_SPKVOLR_N77PT25DB \ + RV(FV_SPKVOLR_N77PT25DB, FB_SPKVOLR) + +#define RV_SPKVOLR_MUTE RV(FV_SPKVOLR_MUTE, FB_SPKVOLR) + +/***************************** + * R_DACVOLL (0x4) * + *****************************/ + +/* Field Offsets */ +#define FB_DACVOLL 0 + +/* Field Masks */ +#define FM_DACVOLL 0XFF + +/* Field Values */ +#define FV_DACVOLL_0DB 0xFF +#define FV_DACVOLL_N95PT625DB 0x1 +#define FV_DACVOLL_MUTE 0x0 + +/* Register Masks */ +#define RM_DACVOLL RM(FM_DACVOLL, FB_DACVOLL) + +/* Register Values */ +#define RV_DACVOLL_0DB RV(FV_DACVOLL_0DB, FB_DACVOLL) +#define RV_DACVOLL_N95PT625DB \ + RV(FV_DACVOLL_N95PT625DB, FB_DACVOLL) + +#define RV_DACVOLL_MUTE RV(FV_DACVOLL_MUTE, FB_DACVOLL) + +/***************************** + * R_DACVOLR (0x5) * + *****************************/ + +/* Field Offsets */ +#define FB_DACVOLR 0 + +/* Field Masks */ +#define FM_DACVOLR 0XFF + +/* Field Values */ +#define FV_DACVOLR_0DB 0xFF +#define FV_DACVOLR_N95PT625DB 0x1 +#define FV_DACVOLR_MUTE 0x0 + +/* Register Masks */ +#define RM_DACVOLR RM(FM_DACVOLR, FB_DACVOLR) + +/* Register Values */ +#define RV_DACVOLR_0DB RV(FV_DACVOLR_0DB, FB_DACVOLR) +#define RV_DACVOLR_N95PT625DB \ + RV(FV_DACVOLR_N95PT625DB, FB_DACVOLR) + +#define RV_DACVOLR_MUTE RV(FV_DACVOLR_MUTE, FB_DACVOLR) + +/***************************** + * R_ADCVOLL (0x6) * + *****************************/ + +/* Field Offsets */ +#define FB_ADCVOLL 0 + +/* Field Masks */ +#define FM_ADCVOLL 0XFF + +/* Field Values */ +#define FV_ADCVOLL_P24DB 0xFF +#define FV_ADCVOLL_N71PT25DB 0x1 +#define FV_ADCVOLL_MUTE 0x0 + +/* Register Masks */ +#define RM_ADCVOLL RM(FM_ADCVOLL, FB_ADCVOLL) + +/* Register Values */ +#define RV_ADCVOLL_P24DB RV(FV_ADCVOLL_P24DB, FB_ADCVOLL) +#define RV_ADCVOLL_N71PT25DB \ + RV(FV_ADCVOLL_N71PT25DB, FB_ADCVOLL) + +#define RV_ADCVOLL_MUTE RV(FV_ADCVOLL_MUTE, FB_ADCVOLL) + +/***************************** + * R_ADCVOLR (0x7) * + *****************************/ + +/* Field Offsets */ +#define FB_ADCVOLR 0 + +/* Field Masks */ +#define FM_ADCVOLR 0XFF + +/* Field Values */ +#define FV_ADCVOLR_P24DB 0xFF +#define FV_ADCVOLR_N71PT25DB 0x1 +#define FV_ADCVOLR_MUTE 0x0 + +/* Register Masks */ +#define RM_ADCVOLR RM(FM_ADCVOLR, FB_ADCVOLR) + +/* Register Values */ +#define RV_ADCVOLR_P24DB RV(FV_ADCVOLR_P24DB, FB_ADCVOLR) +#define RV_ADCVOLR_N71PT25DB \ + RV(FV_ADCVOLR_N71PT25DB, FB_ADCVOLR) + +#define RV_ADCVOLR_MUTE RV(FV_ADCVOLR_MUTE, FB_ADCVOLR) + +/**************************** + * R_INVOLL (0x8) * + ****************************/ + +/* Field Offsets */ +#define FB_INVOLL_INMUTEL 7 +#define FB_INVOLL_IZCL 6 +#define FB_INVOLL 0 + +/* Field Masks */ +#define FM_INVOLL_INMUTEL 0X1 +#define FM_INVOLL_IZCL 0X1 +#define FM_INVOLL 0X3F + +/* Field Values */ +#define FV_INVOLL_INMUTEL_ENABLE 0x1 +#define FV_INVOLL_INMUTEL_DISABLE 0x0 +#define FV_INVOLL_IZCL_ENABLE 0x1 +#define FV_INVOLL_IZCL_DISABLE 0x0 +#define FV_INVOLL_P30DB 0x3F +#define FV_INVOLL_N17PT25DB 0x0 + +/* Register Masks */ +#define RM_INVOLL_INMUTEL \ + RM(FM_INVOLL_INMUTEL, FB_INVOLL_INMUTEL) + +#define RM_INVOLL_IZCL RM(FM_INVOLL_IZCL, FB_INVOLL_IZCL) +#define RM_INVOLL RM(FM_INVOLL, FB_INVOLL) + +/* Register Values */ +#define RV_INVOLL_INMUTEL_ENABLE \ + RV(FV_INVOLL_INMUTEL_ENABLE, FB_INVOLL_INMUTEL) + +#define RV_INVOLL_INMUTEL_DISABLE \ + RV(FV_INVOLL_INMUTEL_DISABLE, FB_INVOLL_INMUTEL) + +#define RV_INVOLL_IZCL_ENABLE \ + RV(FV_INVOLL_IZCL_ENABLE, FB_INVOLL_IZCL) + +#define RV_INVOLL_IZCL_DISABLE \ + RV(FV_INVOLL_IZCL_DISABLE, FB_INVOLL_IZCL) + +#define RV_INVOLL_P30DB RV(FV_INVOLL_P30DB, FB_INVOLL) +#define RV_INVOLL_N17PT25DB RV(FV_INVOLL_N17PT25DB, FB_INVOLL) + +/**************************** + * R_INVOLR (0x9) * + ****************************/ + +/* Field Offsets */ +#define FB_INVOLR_INMUTER 7 +#define FB_INVOLR_IZCR 6 +#define FB_INVOLR 0 + +/* Field Masks */ +#define FM_INVOLR_INMUTER 0X1 +#define FM_INVOLR_IZCR 0X1 +#define FM_INVOLR 0X3F + +/* Field Values */ +#define FV_INVOLR_INMUTER_ENABLE 0x1 +#define FV_INVOLR_INMUTER_DISABLE 0x0 +#define FV_INVOLR_IZCR_ENABLE 0x1 +#define FV_INVOLR_IZCR_DISABLE 0x0 +#define FV_INVOLR_P30DB 0x3F +#define FV_INVOLR_N17PT25DB 0x0 + +/* Register Masks */ +#define RM_INVOLR_INMUTER \ + RM(FM_INVOLR_INMUTER, FB_INVOLR_INMUTER) + +#define RM_INVOLR_IZCR RM(FM_INVOLR_IZCR, FB_INVOLR_IZCR) +#define RM_INVOLR RM(FM_INVOLR, FB_INVOLR) + +/* Register Values */ +#define RV_INVOLR_INMUTER_ENABLE \ + RV(FV_INVOLR_INMUTER_ENABLE, FB_INVOLR_INMUTER) + +#define RV_INVOLR_INMUTER_DISABLE \ + RV(FV_INVOLR_INMUTER_DISABLE, FB_INVOLR_INMUTER) + +#define RV_INVOLR_IZCR_ENABLE \ + RV(FV_INVOLR_IZCR_ENABLE, FB_INVOLR_IZCR) + +#define RV_INVOLR_IZCR_DISABLE \ + RV(FV_INVOLR_IZCR_DISABLE, FB_INVOLR_IZCR) + +#define RV_INVOLR_P30DB RV(FV_INVOLR_P30DB, FB_INVOLR) +#define RV_INVOLR_N17PT25DB RV(FV_INVOLR_N17PT25DB, FB_INVOLR) + +/***************************** + * R_INMODE (0x0B) * + *****************************/ + +/* Field Offsets */ +#define FB_INMODE_DS 0 + +/* Field Masks */ +#define FM_INMODE_DS 0X1 + +/* Field Values */ +#define FV_INMODE_DS_LRIN1 0x0 +#define FV_INMODE_DS_LRIN2 0x1 + +/* Register Masks */ +#define RM_INMODE_DS RM(FM_INMODE_DS, FB_INMODE_DS) + +/* Register Values */ +#define RV_INMODE_DS_LRIN1 \ + RV(FV_INMODE_DS_LRIN1, FB_INMODE_DS) + +#define RV_INMODE_DS_LRIN2 \ + RV(FV_INMODE_DS_LRIN2, FB_INMODE_DS) + + +/***************************** + * R_INSELL (0x0C) * + *****************************/ + +/* Field Offsets */ +#define FB_INSELL 6 +#define FB_INSELL_MICBSTL 4 + +/* Field Masks */ +#define FM_INSELL 0X3 +#define FM_INSELL_MICBSTL 0X3 + +/* Field Values */ +#define FV_INSELL_IN1 0x0 +#define FV_INSELL_IN2 0x1 +#define FV_INSELL_IN3 0x2 +#define FV_INSELL_D2S 0x3 +#define FV_INSELL_MICBSTL_OFF 0x0 +#define FV_INSELL_MICBSTL_10DB 0x1 +#define FV_INSELL_MICBSTL_20DB 0x2 +#define FV_INSELL_MICBSTL_30DB 0x3 + +/* Register Masks */ +#define RM_INSELL RM(FM_INSELL, FB_INSELL) +#define RM_INSELL_MICBSTL \ + RM(FM_INSELL_MICBSTL, FB_INSELL_MICBSTL) + + +/* Register Values */ +#define RV_INSELL_IN1 RV(FV_INSELL_IN1, FB_INSELL) +#define RV_INSELL_IN2 RV(FV_INSELL_IN2, FB_INSELL) +#define RV_INSELL_IN3 RV(FV_INSELL_IN3, FB_INSELL) +#define RV_INSELL_D2S RV(FV_INSELL_D2S, FB_INSELL) +#define RV_INSELL_MICBSTL_OFF \ + RV(FV_INSELL_MICBSTL_OFF, FB_INSELL_MICBSTL) + +#define RV_INSELL_MICBSTL_10DB \ + RV(FV_INSELL_MICBSTL_10DB, FB_INSELL_MICBSTL) + +#define RV_INSELL_MICBSTL_20DB \ + RV(FV_INSELL_MICBSTL_20DB, FB_INSELL_MICBSTL) + +#define RV_INSELL_MICBSTL_30DB \ + RV(FV_INSELL_MICBSTL_30DB, FB_INSELL_MICBSTL) + + +/***************************** + * R_INSELR (0x0D) * + *****************************/ + +/* Field Offsets */ +#define FB_INSELR 6 +#define FB_INSELR_MICBSTR 4 + +/* Field Masks */ +#define FM_INSELR 0X3 +#define FM_INSELR_MICBSTR 0X3 + +/* Field Values */ +#define FV_INSELR_IN1 0x0 +#define FV_INSELR_IN2 0x1 +#define FV_INSELR_IN3 0x2 +#define FV_INSELR_D2S 0x3 +#define FV_INSELR_MICBSTR_OFF 0x0 +#define FV_INSELR_MICBSTR_10DB 0x1 +#define FV_INSELR_MICBSTR_20DB 0x2 +#define FV_INSELR_MICBSTR_30DB 0x3 + +/* Register Masks */ +#define RM_INSELR RM(FM_INSELR, FB_INSELR) +#define RM_INSELR_MICBSTR \ + RM(FM_INSELR_MICBSTR, FB_INSELR_MICBSTR) + + +/* Register Values */ +#define RV_INSELR_IN1 RV(FV_INSELR_IN1, FB_INSELR) +#define RV_INSELR_IN2 RV(FV_INSELR_IN2, FB_INSELR) +#define RV_INSELR_IN3 RV(FV_INSELR_IN3, FB_INSELR) +#define RV_INSELR_D2S RV(FV_INSELR_D2S, FB_INSELR) +#define RV_INSELR_MICBSTR_OFF \ + RV(FV_INSELR_MICBSTR_OFF, FB_INSELR_MICBSTR) + +#define RV_INSELR_MICBSTR_10DB \ + RV(FV_INSELR_MICBSTR_10DB, FB_INSELR_MICBSTR) + +#define RV_INSELR_MICBSTR_20DB \ + RV(FV_INSELR_MICBSTR_20DB, FB_INSELR_MICBSTR) + +#define RV_INSELR_MICBSTR_30DB \ + RV(FV_INSELR_MICBSTR_30DB, FB_INSELR_MICBSTR) + + +/*************************** + * R_AIC1 (0x13) * + ***************************/ + +/* Field Offsets */ +#define FB_AIC1_BCLKINV 6 +#define FB_AIC1_MS 5 +#define FB_AIC1_LRP 4 +#define FB_AIC1_WL 2 +#define FB_AIC1_FORMAT 0 + +/* Field Masks */ +#define FM_AIC1_BCLKINV 0X1 +#define FM_AIC1_MS 0X1 +#define FM_AIC1_LRP 0X1 +#define FM_AIC1_WL 0X3 +#define FM_AIC1_FORMAT 0X3 + +/* Field Values */ +#define FV_AIC1_BCLKINV_ENABLE 0x1 +#define FV_AIC1_BCLKINV_DISABLE 0x0 +#define FV_AIC1_MS_MASTER 0x1 +#define FV_AIC1_MS_SLAVE 0x0 +#define FV_AIC1_LRP_INVERT 0x1 +#define FV_AIC1_LRP_NORMAL 0x0 +#define FV_AIC1_WL_16 0x0 +#define FV_AIC1_WL_20 0x1 +#define FV_AIC1_WL_24 0x2 +#define FV_AIC1_WL_32 0x3 +#define FV_AIC1_FORMAT_RIGHT 0x0 +#define FV_AIC1_FORMAT_LEFT 0x1 +#define FV_AIC1_FORMAT_I2S 0x2 + +/* Register Masks */ +#define RM_AIC1_BCLKINV \ + RM(FM_AIC1_BCLKINV, FB_AIC1_BCLKINV) + +#define RM_AIC1_MS RM(FM_AIC1_MS, FB_AIC1_MS) +#define RM_AIC1_LRP RM(FM_AIC1_LRP, FB_AIC1_LRP) +#define RM_AIC1_WL RM(FM_AIC1_WL, FB_AIC1_WL) +#define RM_AIC1_FORMAT RM(FM_AIC1_FORMAT, FB_AIC1_FORMAT) + +/* Register Values */ +#define RV_AIC1_BCLKINV_ENABLE \ + RV(FV_AIC1_BCLKINV_ENABLE, FB_AIC1_BCLKINV) + +#define RV_AIC1_BCLKINV_DISABLE \ + RV(FV_AIC1_BCLKINV_DISABLE, FB_AIC1_BCLKINV) + +#define RV_AIC1_MS_MASTER RV(FV_AIC1_MS_MASTER, FB_AIC1_MS) +#define RV_AIC1_MS_SLAVE RV(FV_AIC1_MS_SLAVE, FB_AIC1_MS) +#define RV_AIC1_LRP_INVERT \ + RV(FV_AIC1_LRP_INVERT, FB_AIC1_LRP) + +#define RV_AIC1_LRP_NORMAL \ + RV(FV_AIC1_LRP_NORMAL, FB_AIC1_LRP) + +#define RV_AIC1_WL_16 RV(FV_AIC1_WL_16, FB_AIC1_WL) +#define RV_AIC1_WL_20 RV(FV_AIC1_WL_20, FB_AIC1_WL) +#define RV_AIC1_WL_24 RV(FV_AIC1_WL_24, FB_AIC1_WL) +#define RV_AIC1_WL_32 RV(FV_AIC1_WL_32, FB_AIC1_WL) +#define RV_AIC1_FORMAT_RIGHT \ + RV(FV_AIC1_FORMAT_RIGHT, FB_AIC1_FORMAT) + +#define RV_AIC1_FORMAT_LEFT \ + RV(FV_AIC1_FORMAT_LEFT, FB_AIC1_FORMAT) + +#define RV_AIC1_FORMAT_I2S \ + RV(FV_AIC1_FORMAT_I2S, FB_AIC1_FORMAT) + + +/*************************** + * R_AIC2 (0x14) * + ***************************/ + +/* Field Offsets */ +#define FB_AIC2_DACDSEL 6 +#define FB_AIC2_ADCDSEL 4 +#define FB_AIC2_TRI 3 +#define FB_AIC2_BLRCM 0 + +/* Field Masks */ +#define FM_AIC2_DACDSEL 0X3 +#define FM_AIC2_ADCDSEL 0X3 +#define FM_AIC2_TRI 0X1 +#define FM_AIC2_BLRCM 0X7 + +/* Field Values */ +#define FV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED 0x3 + +/* Register Masks */ +#define RM_AIC2_DACDSEL \ + RM(FM_AIC2_DACDSEL, FB_AIC2_DACDSEL) + +#define RM_AIC2_ADCDSEL \ + RM(FM_AIC2_ADCDSEL, FB_AIC2_ADCDSEL) + +#define RM_AIC2_TRI RM(FM_AIC2_TRI, FB_AIC2_TRI) +#define RM_AIC2_BLRCM RM(FM_AIC2_BLRCM, FB_AIC2_BLRCM) + +/* Register Values */ +#define RV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED \ + RV(FV_AIC2_BLRCM_DAC_BCLK_LRCLK_SHARED, FB_AIC2_BLRCM) + + +/****************************** + * R_CNVRTR0 (0x16) * + ******************************/ + +/* Field Offsets */ +#define FB_CNVRTR0_ADCPOLR 7 +#define FB_CNVRTR0_ADCPOLL 6 +#define FB_CNVRTR0_AMONOMIX 4 +#define FB_CNVRTR0_ADCMU 3 +#define FB_CNVRTR0_HPOR 2 +#define FB_CNVRTR0_ADCHPDR 1 +#define FB_CNVRTR0_ADCHPDL 0 + +/* Field Masks */ +#define FM_CNVRTR0_ADCPOLR 0X1 +#define FM_CNVRTR0_ADCPOLL 0X1 +#define FM_CNVRTR0_AMONOMIX 0X3 +#define FM_CNVRTR0_ADCMU 0X1 +#define FM_CNVRTR0_HPOR 0X1 +#define FM_CNVRTR0_ADCHPDR 0X1 +#define FM_CNVRTR0_ADCHPDL 0X1 + +/* Field Values */ +#define FV_CNVRTR0_ADCPOLR_INVERT 0x1 +#define FV_CNVRTR0_ADCPOLR_NORMAL 0x0 +#define FV_CNVRTR0_ADCPOLL_INVERT 0x1 +#define FV_CNVRTR0_ADCPOLL_NORMAL 0x0 +#define FV_CNVRTR0_ADCMU_ENABLE 0x1 +#define FV_CNVRTR0_ADCMU_DISABLE 0x0 +#define FV_CNVRTR0_ADCHPDR_ENABLE 0x1 +#define FV_CNVRTR0_ADCHPDR_DISABLE 0x0 +#define FV_CNVRTR0_ADCHPDL_ENABLE 0x1 +#define FV_CNVRTR0_ADCHPDL_DISABLE 0x0 + +/* Register Masks */ +#define RM_CNVRTR0_ADCPOLR \ + RM(FM_CNVRTR0_ADCPOLR, FB_CNVRTR0_ADCPOLR) + +#define RM_CNVRTR0_ADCPOLL \ + RM(FM_CNVRTR0_ADCPOLL, FB_CNVRTR0_ADCPOLL) + +#define RM_CNVRTR0_AMONOMIX \ + RM(FM_CNVRTR0_AMONOMIX, FB_CNVRTR0_AMONOMIX) + +#define RM_CNVRTR0_ADCMU \ + RM(FM_CNVRTR0_ADCMU, FB_CNVRTR0_ADCMU) + +#define RM_CNVRTR0_HPOR \ + RM(FM_CNVRTR0_HPOR, FB_CNVRTR0_HPOR) + +#define RM_CNVRTR0_ADCHPDR \ + RM(FM_CNVRTR0_ADCHPDR, FB_CNVRTR0_ADCHPDR) + +#define RM_CNVRTR0_ADCHPDL \ + RM(FM_CNVRTR0_ADCHPDL, FB_CNVRTR0_ADCHPDL) + + +/* Register Values */ +#define RV_CNVRTR0_ADCPOLR_INVERT \ + RV(FV_CNVRTR0_ADCPOLR_INVERT, FB_CNVRTR0_ADCPOLR) + +#define RV_CNVRTR0_ADCPOLR_NORMAL \ + RV(FV_CNVRTR0_ADCPOLR_NORMAL, FB_CNVRTR0_ADCPOLR) + +#define RV_CNVRTR0_ADCPOLL_INVERT \ + RV(FV_CNVRTR0_ADCPOLL_INVERT, FB_CNVRTR0_ADCPOLL) + +#define RV_CNVRTR0_ADCPOLL_NORMAL \ + RV(FV_CNVRTR0_ADCPOLL_NORMAL, FB_CNVRTR0_ADCPOLL) + +#define RV_CNVRTR0_ADCMU_ENABLE \ + RV(FV_CNVRTR0_ADCMU_ENABLE, FB_CNVRTR0_ADCMU) + +#define RV_CNVRTR0_ADCMU_DISABLE \ + RV(FV_CNVRTR0_ADCMU_DISABLE, FB_CNVRTR0_ADCMU) + +#define RV_CNVRTR0_ADCHPDR_ENABLE \ + RV(FV_CNVRTR0_ADCHPDR_ENABLE, FB_CNVRTR0_ADCHPDR) + +#define RV_CNVRTR0_ADCHPDR_DISABLE \ + RV(FV_CNVRTR0_ADCHPDR_DISABLE, FB_CNVRTR0_ADCHPDR) + +#define RV_CNVRTR0_ADCHPDL_ENABLE \ + RV(FV_CNVRTR0_ADCHPDL_ENABLE, FB_CNVRTR0_ADCHPDL) + +#define RV_CNVRTR0_ADCHPDL_DISABLE \ + RV(FV_CNVRTR0_ADCHPDL_DISABLE, FB_CNVRTR0_ADCHPDL) + + +/**************************** + * R_ADCSR (0x17) * + ****************************/ + +/* Field Offsets */ +#define FB_ADCSR_ABCM 6 +#define FB_ADCSR_ABR 3 +#define FB_ADCSR_ABM 0 + +/* Field Masks */ +#define FM_ADCSR_ABCM 0X3 +#define FM_ADCSR_ABR 0X3 +#define FM_ADCSR_ABM 0X7 + +/* Field Values */ +#define FV_ADCSR_ABCM_AUTO 0x0 +#define FV_ADCSR_ABCM_32 0x1 +#define FV_ADCSR_ABCM_40 0x2 +#define FV_ADCSR_ABCM_64 0x3 +#define FV_ADCSR_ABR_32 0x0 +#define FV_ADCSR_ABR_44_1 0x1 +#define FV_ADCSR_ABR_48 0x2 +#define FV_ADCSR_ABM_PT25 0x0 +#define FV_ADCSR_ABM_PT5 0x1 +#define FV_ADCSR_ABM_1 0x2 +#define FV_ADCSR_ABM_2 0x3 + +/* Register Masks */ +#define RM_ADCSR_ABCM RM(FM_ADCSR_ABCM, FB_ADCSR_ABCM) +#define RM_ADCSR_ABR RM(FM_ADCSR_ABR, FB_ADCSR_ABR) +#define RM_ADCSR_ABM RM(FM_ADCSR_ABM, FB_ADCSR_ABM) + +/* Register Values */ +#define RV_ADCSR_ABCM_AUTO \ + RV(FV_ADCSR_ABCM_AUTO, FB_ADCSR_ABCM) + +#define RV_ADCSR_ABCM_32 \ + RV(FV_ADCSR_ABCM_32, FB_ADCSR_ABCM) + +#define RV_ADCSR_ABCM_40 \ + RV(FV_ADCSR_ABCM_40, FB_ADCSR_ABCM) + +#define RV_ADCSR_ABCM_64 \ + RV(FV_ADCSR_ABCM_64, FB_ADCSR_ABCM) + +#define RV_ADCSR_ABR_32 RV(FV_ADCSR_ABR_32, FB_ADCSR_ABR) +#define RV_ADCSR_ABR_44_1 \ + RV(FV_ADCSR_ABR_44_1, FB_ADCSR_ABR) + +#define RV_ADCSR_ABR_48 RV(FV_ADCSR_ABR_48, FB_ADCSR_ABR) +#define RV_ADCSR_ABR_ RV(FV_ADCSR_ABR_, FB_ADCSR_ABR) +#define RV_ADCSR_ABM_PT25 \ + RV(FV_ADCSR_ABM_PT25, FB_ADCSR_ABM) + +#define RV_ADCSR_ABM_PT5 RV(FV_ADCSR_ABM_PT5, FB_ADCSR_ABM) +#define RV_ADCSR_ABM_1 RV(FV_ADCSR_ABM_1, FB_ADCSR_ABM) +#define RV_ADCSR_ABM_2 RV(FV_ADCSR_ABM_2, FB_ADCSR_ABM) + +/****************************** + * R_CNVRTR1 (0x18) * + ******************************/ + +/* Field Offsets */ +#define FB_CNVRTR1_DACPOLR 7 +#define FB_CNVRTR1_DACPOLL 6 +#define FB_CNVRTR1_DMONOMIX 4 +#define FB_CNVRTR1_DACMU 3 +#define FB_CNVRTR1_DEEMPH 2 +#define FB_CNVRTR1_DACDITH 0 + +/* Field Masks */ +#define FM_CNVRTR1_DACPOLR 0X1 +#define FM_CNVRTR1_DACPOLL 0X1 +#define FM_CNVRTR1_DMONOMIX 0X3 +#define FM_CNVRTR1_DACMU 0X1 +#define FM_CNVRTR1_DEEMPH 0X1 +#define FM_CNVRTR1_DACDITH 0X3 + +/* Field Values */ +#define FV_CNVRTR1_DACPOLR_INVERT 0x1 +#define FV_CNVRTR1_DACPOLR_NORMAL 0x0 +#define FV_CNVRTR1_DACPOLL_INVERT 0x1 +#define FV_CNVRTR1_DACPOLL_NORMAL 0x0 +#define FV_CNVRTR1_DMONOMIX_ENABLE 0x1 +#define FV_CNVRTR1_DMONOMIX_DISABLE 0x0 +#define FV_CNVRTR1_DACMU_ENABLE 0x1 +#define FV_CNVRTR1_DACMU_DISABLE 0x0 + +/* Register Masks */ +#define RM_CNVRTR1_DACPOLR \ + RM(FM_CNVRTR1_DACPOLR, FB_CNVRTR1_DACPOLR) + +#define RM_CNVRTR1_DACPOLL \ + RM(FM_CNVRTR1_DACPOLL, FB_CNVRTR1_DACPOLL) + +#define RM_CNVRTR1_DMONOMIX \ + RM(FM_CNVRTR1_DMONOMIX, FB_CNVRTR1_DMONOMIX) + +#define RM_CNVRTR1_DACMU \ + RM(FM_CNVRTR1_DACMU, FB_CNVRTR1_DACMU) + +#define RM_CNVRTR1_DEEMPH \ + RM(FM_CNVRTR1_DEEMPH, FB_CNVRTR1_DEEMPH) + +#define RM_CNVRTR1_DACDITH \ + RM(FM_CNVRTR1_DACDITH, FB_CNVRTR1_DACDITH) + + +/* Register Values */ +#define RV_CNVRTR1_DACPOLR_INVERT \ + RV(FV_CNVRTR1_DACPOLR_INVERT, FB_CNVRTR1_DACPOLR) + +#define RV_CNVRTR1_DACPOLR_NORMAL \ + RV(FV_CNVRTR1_DACPOLR_NORMAL, FB_CNVRTR1_DACPOLR) + +#define RV_CNVRTR1_DACPOLL_INVERT \ + RV(FV_CNVRTR1_DACPOLL_INVERT, FB_CNVRTR1_DACPOLL) + +#define RV_CNVRTR1_DACPOLL_NORMAL \ + RV(FV_CNVRTR1_DACPOLL_NORMAL, FB_CNVRTR1_DACPOLL) + +#define RV_CNVRTR1_DMONOMIX_ENABLE \ + RV(FV_CNVRTR1_DMONOMIX_ENABLE, FB_CNVRTR1_DMONOMIX) + +#define RV_CNVRTR1_DMONOMIX_DISABLE \ + RV(FV_CNVRTR1_DMONOMIX_DISABLE, FB_CNVRTR1_DMONOMIX) + +#define RV_CNVRTR1_DACMU_ENABLE \ + RV(FV_CNVRTR1_DACMU_ENABLE, FB_CNVRTR1_DACMU) + +#define RV_CNVRTR1_DACMU_DISABLE \ + RV(FV_CNVRTR1_DACMU_DISABLE, FB_CNVRTR1_DACMU) + + +/**************************** + * R_DACSR (0x19) * + ****************************/ + +/* Field Offsets */ +#define FB_DACSR_DBCM 6 +#define FB_DACSR_DBR 3 +#define FB_DACSR_DBM 0 + +/* Field Masks */ +#define FM_DACSR_DBCM 0X3 +#define FM_DACSR_DBR 0X3 +#define FM_DACSR_DBM 0X7 + +/* Field Values */ +#define FV_DACSR_DBCM_AUTO 0x0 +#define FV_DACSR_DBCM_32 0x1 +#define FV_DACSR_DBCM_40 0x2 +#define FV_DACSR_DBCM_64 0x3 +#define FV_DACSR_DBR_32 0x0 +#define FV_DACSR_DBR_44_1 0x1 +#define FV_DACSR_DBR_48 0x2 +#define FV_DACSR_DBM_PT25 0x0 +#define FV_DACSR_DBM_PT5 0x1 +#define FV_DACSR_DBM_1 0x2 +#define FV_DACSR_DBM_2 0x3 + +/* Register Masks */ +#define RM_DACSR_DBCM RM(FM_DACSR_DBCM, FB_DACSR_DBCM) +#define RM_DACSR_DBR RM(FM_DACSR_DBR, FB_DACSR_DBR) +#define RM_DACSR_DBM RM(FM_DACSR_DBM, FB_DACSR_DBM) + +/* Register Values */ +#define RV_DACSR_DBCM_AUTO \ + RV(FV_DACSR_DBCM_AUTO, FB_DACSR_DBCM) + +#define RV_DACSR_DBCM_32 \ + RV(FV_DACSR_DBCM_32, FB_DACSR_DBCM) + +#define RV_DACSR_DBCM_40 \ + RV(FV_DACSR_DBCM_40, FB_DACSR_DBCM) + +#define RV_DACSR_DBCM_64 \ + RV(FV_DACSR_DBCM_64, FB_DACSR_DBCM) + +#define RV_DACSR_DBR_32 RV(FV_DACSR_DBR_32, FB_DACSR_DBR) +#define RV_DACSR_DBR_44_1 \ + RV(FV_DACSR_DBR_44_1, FB_DACSR_DBR) + +#define RV_DACSR_DBR_48 RV(FV_DACSR_DBR_48, FB_DACSR_DBR) +#define RV_DACSR_DBM_PT25 \ + RV(FV_DACSR_DBM_PT25, FB_DACSR_DBM) + +#define RV_DACSR_DBM_PT5 RV(FV_DACSR_DBM_PT5, FB_DACSR_DBM) +#define RV_DACSR_DBM_1 RV(FV_DACSR_DBM_1, FB_DACSR_DBM) +#define RV_DACSR_DBM_2 RV(FV_DACSR_DBM_2, FB_DACSR_DBM) + +/**************************** + * R_PWRM1 (0x1A) * + ****************************/ + +/* Field Offsets */ +#define FB_PWRM1_BSTL 7 +#define FB_PWRM1_BSTR 6 +#define FB_PWRM1_PGAL 5 +#define FB_PWRM1_PGAR 4 +#define FB_PWRM1_ADCL 3 +#define FB_PWRM1_ADCR 2 +#define FB_PWRM1_MICB 1 +#define FB_PWRM1_DIGENB 0 + +/* Field Masks */ +#define FM_PWRM1_BSTL 0X1 +#define FM_PWRM1_BSTR 0X1 +#define FM_PWRM1_PGAL 0X1 +#define FM_PWRM1_PGAR 0X1 +#define FM_PWRM1_ADCL 0X1 +#define FM_PWRM1_ADCR 0X1 +#define FM_PWRM1_MICB 0X1 +#define FM_PWRM1_DIGENB 0X1 + +/* Field Values */ +#define FV_PWRM1_BSTL_ENABLE 0x1 +#define FV_PWRM1_BSTL_DISABLE 0x0 +#define FV_PWRM1_BSTR_ENABLE 0x1 +#define FV_PWRM1_BSTR_DISABLE 0x0 +#define FV_PWRM1_PGAL_ENABLE 0x1 +#define FV_PWRM1_PGAL_DISABLE 0x0 +#define FV_PWRM1_PGAR_ENABLE 0x1 +#define FV_PWRM1_PGAR_DISABLE 0x0 +#define FV_PWRM1_ADCL_ENABLE 0x1 +#define FV_PWRM1_ADCL_DISABLE 0x0 +#define FV_PWRM1_ADCR_ENABLE 0x1 +#define FV_PWRM1_ADCR_DISABLE 0x0 +#define FV_PWRM1_MICB_ENABLE 0x1 +#define FV_PWRM1_MICB_DISABLE 0x0 +#define FV_PWRM1_DIGENB_DISABLE 0x1 +#define FV_PWRM1_DIGENB_ENABLE 0x0 + +/* Register Masks */ +#define RM_PWRM1_BSTL RM(FM_PWRM1_BSTL, FB_PWRM1_BSTL) +#define RM_PWRM1_BSTR RM(FM_PWRM1_BSTR, FB_PWRM1_BSTR) +#define RM_PWRM1_PGAL RM(FM_PWRM1_PGAL, FB_PWRM1_PGAL) +#define RM_PWRM1_PGAR RM(FM_PWRM1_PGAR, FB_PWRM1_PGAR) +#define RM_PWRM1_ADCL RM(FM_PWRM1_ADCL, FB_PWRM1_ADCL) +#define RM_PWRM1_ADCR RM(FM_PWRM1_ADCR, FB_PWRM1_ADCR) +#define RM_PWRM1_MICB RM(FM_PWRM1_MICB, FB_PWRM1_MICB) +#define RM_PWRM1_DIGENB \ + RM(FM_PWRM1_DIGENB, FB_PWRM1_DIGENB) + + +/* Register Values */ +#define RV_PWRM1_BSTL_ENABLE \ + RV(FV_PWRM1_BSTL_ENABLE, FB_PWRM1_BSTL) + +#define RV_PWRM1_BSTL_DISABLE \ + RV(FV_PWRM1_BSTL_DISABLE, FB_PWRM1_BSTL) + +#define RV_PWRM1_BSTR_ENABLE \ + RV(FV_PWRM1_BSTR_ENABLE, FB_PWRM1_BSTR) + +#define RV_PWRM1_BSTR_DISABLE \ + RV(FV_PWRM1_BSTR_DISABLE, FB_PWRM1_BSTR) + +#define RV_PWRM1_PGAL_ENABLE \ + RV(FV_PWRM1_PGAL_ENABLE, FB_PWRM1_PGAL) + +#define RV_PWRM1_PGAL_DISABLE \ + RV(FV_PWRM1_PGAL_DISABLE, FB_PWRM1_PGAL) + +#define RV_PWRM1_PGAR_ENABLE \ + RV(FV_PWRM1_PGAR_ENABLE, FB_PWRM1_PGAR) + +#define RV_PWRM1_PGAR_DISABLE \ + RV(FV_PWRM1_PGAR_DISABLE, FB_PWRM1_PGAR) + +#define RV_PWRM1_ADCL_ENABLE \ + RV(FV_PWRM1_ADCL_ENABLE, FB_PWRM1_ADCL) + +#define RV_PWRM1_ADCL_DISABLE \ + RV(FV_PWRM1_ADCL_DISABLE, FB_PWRM1_ADCL) + +#define RV_PWRM1_ADCR_ENABLE \ + RV(FV_PWRM1_ADCR_ENABLE, FB_PWRM1_ADCR) + +#define RV_PWRM1_ADCR_DISABLE \ + RV(FV_PWRM1_ADCR_DISABLE, FB_PWRM1_ADCR) + +#define RV_PWRM1_MICB_ENABLE \ + RV(FV_PWRM1_MICB_ENABLE, FB_PWRM1_MICB) + +#define RV_PWRM1_MICB_DISABLE \ + RV(FV_PWRM1_MICB_DISABLE, FB_PWRM1_MICB) + +#define RV_PWRM1_DIGENB_DISABLE \ + RV(FV_PWRM1_DIGENB_DISABLE, FB_PWRM1_DIGENB) + +#define RV_PWRM1_DIGENB_ENABLE \ + RV(FV_PWRM1_DIGENB_ENABLE, FB_PWRM1_DIGENB) + + +/**************************** + * R_PWRM2 (0x1B) * + ****************************/ + +/* Field Offsets */ +#define FB_PWRM2_D2S 7 +#define FB_PWRM2_HPL 6 +#define FB_PWRM2_HPR 5 +#define FB_PWRM2_SPKL 4 +#define FB_PWRM2_SPKR 3 +#define FB_PWRM2_INSELL 2 +#define FB_PWRM2_INSELR 1 +#define FB_PWRM2_VREF 0 + +/* Field Masks */ +#define FM_PWRM2_D2S 0X1 +#define FM_PWRM2_HPL 0X1 +#define FM_PWRM2_HPR 0X1 +#define FM_PWRM2_SPKL 0X1 +#define FM_PWRM2_SPKR 0X1 +#define FM_PWRM2_INSELL 0X1 +#define FM_PWRM2_INSELR 0X1 +#define FM_PWRM2_VREF 0X1 + +/* Field Values */ +#define FV_PWRM2_D2S_ENABLE 0x1 +#define FV_PWRM2_D2S_DISABLE 0x0 +#define FV_PWRM2_HPL_ENABLE 0x1 +#define FV_PWRM2_HPL_DISABLE 0x0 +#define FV_PWRM2_HPR_ENABLE 0x1 +#define FV_PWRM2_HPR_DISABLE 0x0 +#define FV_PWRM2_SPKL_ENABLE 0x1 +#define FV_PWRM2_SPKL_DISABLE 0x0 +#define FV_PWRM2_SPKR_ENABLE 0x1 +#define FV_PWRM2_SPKR_DISABLE 0x0 +#define FV_PWRM2_INSELL_ENABLE 0x1 +#define FV_PWRM2_INSELL_DISABLE 0x0 +#define FV_PWRM2_INSELR_ENABLE 0x1 +#define FV_PWRM2_INSELR_DISABLE 0x0 +#define FV_PWRM2_VREF_ENABLE 0x1 +#define FV_PWRM2_VREF_DISABLE 0x0 + +/* Register Masks */ +#define RM_PWRM2_D2S RM(FM_PWRM2_D2S, FB_PWRM2_D2S) +#define RM_PWRM2_HPL RM(FM_PWRM2_HPL, FB_PWRM2_HPL) +#define RM_PWRM2_HPR RM(FM_PWRM2_HPR, FB_PWRM2_HPR) +#define RM_PWRM2_SPKL RM(FM_PWRM2_SPKL, FB_PWRM2_SPKL) +#define RM_PWRM2_SPKR RM(FM_PWRM2_SPKR, FB_PWRM2_SPKR) +#define RM_PWRM2_INSELL \ + RM(FM_PWRM2_INSELL, FB_PWRM2_INSELL) + +#define RM_PWRM2_INSELR \ + RM(FM_PWRM2_INSELR, FB_PWRM2_INSELR) + +#define RM_PWRM2_VREF RM(FM_PWRM2_VREF, FB_PWRM2_VREF) + +/* Register Values */ +#define RV_PWRM2_D2S_ENABLE \ + RV(FV_PWRM2_D2S_ENABLE, FB_PWRM2_D2S) + +#define RV_PWRM2_D2S_DISABLE \ + RV(FV_PWRM2_D2S_DISABLE, FB_PWRM2_D2S) + +#define RV_PWRM2_HPL_ENABLE \ + RV(FV_PWRM2_HPL_ENABLE, FB_PWRM2_HPL) + +#define RV_PWRM2_HPL_DISABLE \ + RV(FV_PWRM2_HPL_DISABLE, FB_PWRM2_HPL) + +#define RV_PWRM2_HPR_ENABLE \ + RV(FV_PWRM2_HPR_ENABLE, FB_PWRM2_HPR) + +#define RV_PWRM2_HPR_DISABLE \ + RV(FV_PWRM2_HPR_DISABLE, FB_PWRM2_HPR) + +#define RV_PWRM2_SPKL_ENABLE \ + RV(FV_PWRM2_SPKL_ENABLE, FB_PWRM2_SPKL) + +#define RV_PWRM2_SPKL_DISABLE \ + RV(FV_PWRM2_SPKL_DISABLE, FB_PWRM2_SPKL) + +#define RV_PWRM2_SPKR_ENABLE \ + RV(FV_PWRM2_SPKR_ENABLE, FB_PWRM2_SPKR) + +#define RV_PWRM2_SPKR_DISABLE \ + RV(FV_PWRM2_SPKR_DISABLE, FB_PWRM2_SPKR) + +#define RV_PWRM2_INSELL_ENABLE \ + RV(FV_PWRM2_INSELL_ENABLE, FB_PWRM2_INSELL) + +#define RV_PWRM2_INSELL_DISABLE \ + RV(FV_PWRM2_INSELL_DISABLE, FB_PWRM2_INSELL) + +#define RV_PWRM2_INSELR_ENABLE \ + RV(FV_PWRM2_INSELR_ENABLE, FB_PWRM2_INSELR) + +#define RV_PWRM2_INSELR_DISABLE \ + RV(FV_PWRM2_INSELR_DISABLE, FB_PWRM2_INSELR) + +#define RV_PWRM2_VREF_ENABLE \ + RV(FV_PWRM2_VREF_ENABLE, FB_PWRM2_VREF) + +#define RV_PWRM2_VREF_DISABLE \ + RV(FV_PWRM2_VREF_DISABLE, FB_PWRM2_VREF) + +/****************************** + * R_CTL (0x1C) * + ******************************/ + +/* Fiel Offsets */ +#define FB_CTL_HPSWEN 7 +#define FB_CTL_HPSWPOL 6 + +/****************************** + * R_CONFIG0 (0x1F) * + ******************************/ + +/* Field Offsets */ +#define FB_CONFIG0_ASDM 6 +#define FB_CONFIG0_DSDM 4 +#define FB_CONFIG0_DC_BYPASS 1 +#define FB_CONFIG0_SD_FORCE_ON 0 + +/* Field Masks */ +#define FM_CONFIG0_ASDM 0X3 +#define FM_CONFIG0_DSDM 0X3 +#define FM_CONFIG0_DC_BYPASS 0X1 +#define FM_CONFIG0_SD_FORCE_ON 0X1 + +/* Field Values */ +#define FV_CONFIG0_ASDM_HALF 0x1 +#define FV_CONFIG0_ASDM_FULL 0x2 +#define FV_CONFIG0_ASDM_AUTO 0x3 +#define FV_CONFIG0_DSDM_HALF 0x1 +#define FV_CONFIG0_DSDM_FULL 0x2 +#define FV_CONFIG0_DSDM_AUTO 0x3 +#define FV_CONFIG0_DC_BYPASS_ENABLE 0x1 +#define FV_CONFIG0_DC_BYPASS_DISABLE 0x0 +#define FV_CONFIG0_SD_FORCE_ON_ENABLE 0x1 +#define FV_CONFIG0_SD_FORCE_ON_DISABLE 0x0 + +/* Register Masks */ +#define RM_CONFIG0_ASDM \ + RM(FM_CONFIG0_ASDM, FB_CONFIG0_ASDM) + +#define RM_CONFIG0_DSDM \ + RM(FM_CONFIG0_DSDM, FB_CONFIG0_DSDM) + +#define RM_CONFIG0_DC_BYPASS \ + RM(FM_CONFIG0_DC_BYPASS, FB_CONFIG0_DC_BYPASS) + +#define RM_CONFIG0_SD_FORCE_ON \ + RM(FM_CONFIG0_SD_FORCE_ON, FB_CONFIG0_SD_FORCE_ON) + + +/* Register Values */ +#define RV_CONFIG0_ASDM_HALF \ + RV(FV_CONFIG0_ASDM_HALF, FB_CONFIG0_ASDM) + +#define RV_CONFIG0_ASDM_FULL \ + RV(FV_CONFIG0_ASDM_FULL, FB_CONFIG0_ASDM) + +#define RV_CONFIG0_ASDM_AUTO \ + RV(FV_CONFIG0_ASDM_AUTO, FB_CONFIG0_ASDM) + +#define RV_CONFIG0_DSDM_HALF \ + RV(FV_CONFIG0_DSDM_HALF, FB_CONFIG0_DSDM) + +#define RV_CONFIG0_DSDM_FULL \ + RV(FV_CONFIG0_DSDM_FULL, FB_CONFIG0_DSDM) + +#define RV_CONFIG0_DSDM_AUTO \ + RV(FV_CONFIG0_DSDM_AUTO, FB_CONFIG0_DSDM) + +#define RV_CONFIG0_DC_BYPASS_ENABLE \ + RV(FV_CONFIG0_DC_BYPASS_ENABLE, FB_CONFIG0_DC_BYPASS) + +#define RV_CONFIG0_DC_BYPASS_DISABLE \ + RV(FV_CONFIG0_DC_BYPASS_DISABLE, FB_CONFIG0_DC_BYPASS) + +#define RV_CONFIG0_SD_FORCE_ON_ENABLE \ + RV(FV_CONFIG0_SD_FORCE_ON_ENABLE, FB_CONFIG0_SD_FORCE_ON) + +#define RV_CONFIG0_SD_FORCE_ON_DISABLE \ + RV(FV_CONFIG0_SD_FORCE_ON_DISABLE, FB_CONFIG0_SD_FORCE_ON) + + +/****************************** + * R_CONFIG1 (0x20) * + ******************************/ + +/* Field Offsets */ +#define FB_CONFIG1_EQ2_EN 7 +#define FB_CONFIG1_EQ2_BE 4 +#define FB_CONFIG1_EQ1_EN 3 +#define FB_CONFIG1_EQ1_BE 0 + +/* Field Masks */ +#define FM_CONFIG1_EQ2_EN 0X1 +#define FM_CONFIG1_EQ2_BE 0X7 +#define FM_CONFIG1_EQ1_EN 0X1 +#define FM_CONFIG1_EQ1_BE 0X7 + +/* Field Values */ +#define FV_CONFIG1_EQ2_EN_ENABLE 0x1 +#define FV_CONFIG1_EQ2_EN_DISABLE 0x0 +#define FV_CONFIG1_EQ2_BE_PRE 0x0 +#define FV_CONFIG1_EQ2_BE_PRE_EQ_0 0x1 +#define FV_CONFIG1_EQ2_BE_PRE_EQ0_1 0x2 +#define FV_CONFIG1_EQ2_BE_PRE_EQ0_2 0x3 +#define FV_CONFIG1_EQ2_BE_PRE_EQ0_3 0x4 +#define FV_CONFIG1_EQ2_BE_PRE_EQ0_4 0x5 +#define FV_CONFIG1_EQ2_BE_PRE_EQ0_5 0x6 +#define FV_CONFIG1_EQ1_EN_ENABLE 0x1 +#define FV_CONFIG1_EQ1_EN_DISABLE 0x0 +#define FV_CONFIG1_EQ1_BE_PRE 0x0 +#define FV_CONFIG1_EQ1_BE_PRE_EQ_0 0x1 +#define FV_CONFIG1_EQ1_BE_PRE_EQ0_1 0x2 +#define FV_CONFIG1_EQ1_BE_PRE_EQ0_2 0x3 +#define FV_CONFIG1_EQ1_BE_PRE_EQ0_3 0x4 +#define FV_CONFIG1_EQ1_BE_PRE_EQ0_4 0x5 +#define FV_CONFIG1_EQ1_BE_PRE_EQ0_5 0x6 + +/* Register Masks */ +#define RM_CONFIG1_EQ2_EN \ + RM(FM_CONFIG1_EQ2_EN, FB_CONFIG1_EQ2_EN) + +#define RM_CONFIG1_EQ2_BE \ + RM(FM_CONFIG1_EQ2_BE, FB_CONFIG1_EQ2_BE) + +#define RM_CONFIG1_EQ1_EN \ + RM(FM_CONFIG1_EQ1_EN, FB_CONFIG1_EQ1_EN) + +#define RM_CONFIG1_EQ1_BE \ + RM(FM_CONFIG1_EQ1_BE, FB_CONFIG1_EQ1_BE) + + +/* Register Values */ +#define RV_CONFIG1_EQ2_EN_ENABLE \ + RV(FV_CONFIG1_EQ2_EN_ENABLE, FB_CONFIG1_EQ2_EN) + +#define RV_CONFIG1_EQ2_EN_DISABLE \ + RV(FV_CONFIG1_EQ2_EN_DISABLE, FB_CONFIG1_EQ2_EN) + +#define RV_CONFIG1_EQ2_BE_PRE \ + RV(FV_CONFIG1_EQ2_BE_PRE, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ_0 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ_0, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ0_1 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_1, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ0_2 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_2, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ0_3 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_3, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ0_4 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_4, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ2_BE_PRE_EQ0_5 \ + RV(FV_CONFIG1_EQ2_BE_PRE_EQ0_5, FB_CONFIG1_EQ2_BE) + +#define RV_CONFIG1_EQ1_EN_ENABLE \ + RV(FV_CONFIG1_EQ1_EN_ENABLE, FB_CONFIG1_EQ1_EN) + +#define RV_CONFIG1_EQ1_EN_DISABLE \ + RV(FV_CONFIG1_EQ1_EN_DISABLE, FB_CONFIG1_EQ1_EN) + +#define RV_CONFIG1_EQ1_BE_PRE \ + RV(FV_CONFIG1_EQ1_BE_PRE, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ_0 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ_0, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ0_1 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_1, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ0_2 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_2, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ0_3 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_3, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ0_4 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_4, FB_CONFIG1_EQ1_BE) + +#define RV_CONFIG1_EQ1_BE_PRE_EQ0_5 \ + RV(FV_CONFIG1_EQ1_BE_PRE_EQ0_5, FB_CONFIG1_EQ1_BE) + + +/****************************** + * R_DMICCTL (0x24) * + ******************************/ + +/* Field Offsets */ +#define FB_DMICCTL_DMICEN 7 +#define FB_DMICCTL_DMONO 4 +#define FB_DMICCTL_DMPHADJ 2 +#define FB_DMICCTL_DMRATE 0 + +/* Field Masks */ +#define FM_DMICCTL_DMICEN 0X1 +#define FM_DMICCTL_DMONO 0X1 +#define FM_DMICCTL_DMPHADJ 0X3 +#define FM_DMICCTL_DMRATE 0X3 + +/* Field Values */ +#define FV_DMICCTL_DMICEN_ENABLE 0x1 +#define FV_DMICCTL_DMICEN_DISABLE 0x0 +#define FV_DMICCTL_DMONO_STEREO 0x0 +#define FV_DMICCTL_DMONO_MONO 0x1 + +/* Register Masks */ +#define RM_DMICCTL_DMICEN \ + RM(FM_DMICCTL_DMICEN, FB_DMICCTL_DMICEN) + +#define RM_DMICCTL_DMONO \ + RM(FM_DMICCTL_DMONO, FB_DMICCTL_DMONO) + +#define RM_DMICCTL_DMPHADJ \ + RM(FM_DMICCTL_DMPHADJ, FB_DMICCTL_DMPHADJ) + +#define RM_DMICCTL_DMRATE \ + RM(FM_DMICCTL_DMRATE, FB_DMICCTL_DMRATE) + + +/* Register Values */ +#define RV_DMICCTL_DMICEN_ENABLE \ + RV(FV_DMICCTL_DMICEN_ENABLE, FB_DMICCTL_DMICEN) + +#define RV_DMICCTL_DMICEN_DISABLE \ + RV(FV_DMICCTL_DMICEN_DISABLE, FB_DMICCTL_DMICEN) + +#define RV_DMICCTL_DMONO_STEREO \ + RV(FV_DMICCTL_DMONO_STEREO, FB_DMICCTL_DMONO) + +#define RV_DMICCTL_DMONO_MONO \ + RV(FV_DMICCTL_DMONO_MONO, FB_DMICCTL_DMONO) + + +/***************************** + * R_CLECTL (0x25) * + *****************************/ + +/* Field Offsets */ +#define FB_CLECTL_LVL_MODE 4 +#define FB_CLECTL_WINDOWSEL 3 +#define FB_CLECTL_EXP_EN 2 +#define FB_CLECTL_LIMIT_EN 1 +#define FB_CLECTL_COMP_EN 0 + +/* Field Masks */ +#define FM_CLECTL_LVL_MODE 0X1 +#define FM_CLECTL_WINDOWSEL 0X1 +#define FM_CLECTL_EXP_EN 0X1 +#define FM_CLECTL_LIMIT_EN 0X1 +#define FM_CLECTL_COMP_EN 0X1 + +/* Field Values */ +#define FV_CLECTL_LVL_MODE_AVG 0x0 +#define FV_CLECTL_LVL_MODE_PEAK 0x1 +#define FV_CLECTL_WINDOWSEL_512 0x0 +#define FV_CLECTL_WINDOWSEL_64 0x1 +#define FV_CLECTL_EXP_EN_ENABLE 0x1 +#define FV_CLECTL_EXP_EN_DISABLE 0x0 +#define FV_CLECTL_LIMIT_EN_ENABLE 0x1 +#define FV_CLECTL_LIMIT_EN_DISABLE 0x0 +#define FV_CLECTL_COMP_EN_ENABLE 0x1 +#define FV_CLECTL_COMP_EN_DISABLE 0x0 + +/* Register Masks */ +#define RM_CLECTL_LVL_MODE \ + RM(FM_CLECTL_LVL_MODE, FB_CLECTL_LVL_MODE) + +#define RM_CLECTL_WINDOWSEL \ + RM(FM_CLECTL_WINDOWSEL, FB_CLECTL_WINDOWSEL) + +#define RM_CLECTL_EXP_EN \ + RM(FM_CLECTL_EXP_EN, FB_CLECTL_EXP_EN) + +#define RM_CLECTL_LIMIT_EN \ + RM(FM_CLECTL_LIMIT_EN, FB_CLECTL_LIMIT_EN) + +#define RM_CLECTL_COMP_EN \ + RM(FM_CLECTL_COMP_EN, FB_CLECTL_COMP_EN) + + +/* Register Values */ +#define RV_CLECTL_LVL_MODE_AVG \ + RV(FV_CLECTL_LVL_MODE_AVG, FB_CLECTL_LVL_MODE) + +#define RV_CLECTL_LVL_MODE_PEAK \ + RV(FV_CLECTL_LVL_MODE_PEAK, FB_CLECTL_LVL_MODE) + +#define RV_CLECTL_WINDOWSEL_512 \ + RV(FV_CLECTL_WINDOWSEL_512, FB_CLECTL_WINDOWSEL) + +#define RV_CLECTL_WINDOWSEL_64 \ + RV(FV_CLECTL_WINDOWSEL_64, FB_CLECTL_WINDOWSEL) + +#define RV_CLECTL_EXP_EN_ENABLE \ + RV(FV_CLECTL_EXP_EN_ENABLE, FB_CLECTL_EXP_EN) + +#define RV_CLECTL_EXP_EN_DISABLE \ + RV(FV_CLECTL_EXP_EN_DISABLE, FB_CLECTL_EXP_EN) + +#define RV_CLECTL_LIMIT_EN_ENABLE \ + RV(FV_CLECTL_LIMIT_EN_ENABLE, FB_CLECTL_LIMIT_EN) + +#define RV_CLECTL_LIMIT_EN_DISABLE \ + RV(FV_CLECTL_LIMIT_EN_DISABLE, FB_CLECTL_LIMIT_EN) + +#define RV_CLECTL_COMP_EN_ENABLE \ + RV(FV_CLECTL_COMP_EN_ENABLE, FB_CLECTL_COMP_EN) + +#define RV_CLECTL_COMP_EN_DISABLE \ + RV(FV_CLECTL_COMP_EN_DISABLE, FB_CLECTL_COMP_EN) + + +/***************************** + * R_MUGAIN (0x26) * + *****************************/ + +/* Field Offsets */ +#define FB_MUGAIN_CLEMUG 0 + +/* Field Masks */ +#define FM_MUGAIN_CLEMUG 0X1F + +/* Field Values */ +#define FV_MUGAIN_CLEMUG_46PT5DB 0x1F +#define FV_MUGAIN_CLEMUG_0DB 0x0 + +/* Register Masks */ +#define RM_MUGAIN_CLEMUG \ + RM(FM_MUGAIN_CLEMUG, FB_MUGAIN_CLEMUG) + + +/* Register Values */ +#define RV_MUGAIN_CLEMUG_46PT5DB \ + RV(FV_MUGAIN_CLEMUG_46PT5DB, FB_MUGAIN_CLEMUG) + +#define RV_MUGAIN_CLEMUG_0DB \ + RV(FV_MUGAIN_CLEMUG_0DB, FB_MUGAIN_CLEMUG) + + +/***************************** + * R_COMPTH (0x27) * + *****************************/ + +/* Field Offsets */ +#define FB_COMPTH 0 + +/* Field Masks */ +#define FM_COMPTH 0XFF + +/* Field Values */ +#define FV_COMPTH_0DB 0xFF +#define FV_COMPTH_N95PT625DB 0x0 + +/* Register Masks */ +#define RM_COMPTH RM(FM_COMPTH, FB_COMPTH) + +/* Register Values */ +#define RV_COMPTH_0DB RV(FV_COMPTH_0DB, FB_COMPTH) +#define RV_COMPTH_N95PT625DB \ + RV(FV_COMPTH_N95PT625DB, FB_COMPTH) + + +/***************************** + * R_CMPRAT (0x28) * + *****************************/ + +/* Field Offsets */ +#define FB_CMPRAT 0 + +/* Field Masks */ +#define FM_CMPRAT 0X1F + +/* Register Masks */ +#define RM_CMPRAT RM(FM_CMPRAT, FB_CMPRAT) + +/****************************** + * R_CATKTCL (0x29) * + ******************************/ + +/* Field Offsets */ +#define FB_CATKTCL 0 + +/* Field Masks */ +#define FM_CATKTCL 0XFF + +/* Register Masks */ +#define RM_CATKTCL RM(FM_CATKTCL, FB_CATKTCL) + +/****************************** + * R_CATKTCH (0x2A) * + ******************************/ + +/* Field Offsets */ +#define FB_CATKTCH 0 + +/* Field Masks */ +#define FM_CATKTCH 0XFF + +/* Register Masks */ +#define RM_CATKTCH RM(FM_CATKTCH, FB_CATKTCH) + +/****************************** + * R_CRELTCL (0x2B) * + ******************************/ + +/* Field Offsets */ +#define FB_CRELTCL 0 + +/* Field Masks */ +#define FM_CRELTCL 0XFF + +/* Register Masks */ +#define RM_CRELTCL RM(FM_CRELTCL, FB_CRELTCL) + +/****************************** + * R_CRELTCH (0x2C) * + ******************************/ + +/* Field Offsets */ +#define FB_CRELTCH 0 + +/* Field Masks */ +#define FM_CRELTCH 0XFF + +/* Register Masks */ +#define RM_CRELTCH RM(FM_CRELTCH, FB_CRELTCH) + +/**************************** + * R_LIMTH (0x2D) * + ****************************/ + +/* Field Offsets */ +#define FB_LIMTH 0 + +/* Field Masks */ +#define FM_LIMTH 0XFF + +/* Field Values */ +#define FV_LIMTH_0DB 0xFF +#define FV_LIMTH_N95PT625DB 0x0 + +/* Register Masks */ +#define RM_LIMTH RM(FM_LIMTH, FB_LIMTH) + +/* Register Values */ +#define RV_LIMTH_0DB RV(FV_LIMTH_0DB, FB_LIMTH) +#define RV_LIMTH_N95PT625DB RV(FV_LIMTH_N95PT625DB, FB_LIMTH) + +/***************************** + * R_LIMTGT (0x2E) * + *****************************/ + +/* Field Offsets */ +#define FB_LIMTGT 0 + +/* Field Masks */ +#define FM_LIMTGT 0XFF + +/* Field Values */ +#define FV_LIMTGT_0DB 0xFF +#define FV_LIMTGT_N95PT625DB 0x0 + +/* Register Masks */ +#define RM_LIMTGT RM(FM_LIMTGT, FB_LIMTGT) + +/* Register Values */ +#define RV_LIMTGT_0DB RV(FV_LIMTGT_0DB, FB_LIMTGT) +#define RV_LIMTGT_N95PT625DB \ + RV(FV_LIMTGT_N95PT625DB, FB_LIMTGT) + + +/****************************** + * R_LATKTCL (0x2F) * + ******************************/ + +/* Field Offsets */ +#define FB_LATKTCL 0 + +/* Field Masks */ +#define FM_LATKTCL 0XFF + +/* Register Masks */ +#define RM_LATKTCL RM(FM_LATKTCL, FB_LATKTCL) + +/****************************** + * R_LATKTCH (0x30) * + ******************************/ + +/* Field Offsets */ +#define FB_LATKTCH 0 + +/* Field Masks */ +#define FM_LATKTCH 0XFF + +/* Register Masks */ +#define RM_LATKTCH RM(FM_LATKTCH, FB_LATKTCH) + +/****************************** + * R_LRELTCL (0x31) * + ******************************/ + +/* Field Offsets */ +#define FB_LRELTCL 0 + +/* Field Masks */ +#define FM_LRELTCL 0XFF + +/* Register Masks */ +#define RM_LRELTCL RM(FM_LRELTCL, FB_LRELTCL) + +/****************************** + * R_LRELTCH (0x32) * + ******************************/ + +/* Field Offsets */ +#define FB_LRELTCH 0 + +/* Field Masks */ +#define FM_LRELTCH 0XFF + +/* Register Masks */ +#define RM_LRELTCH RM(FM_LRELTCH, FB_LRELTCH) + +/**************************** + * R_EXPTH (0x33) * + ****************************/ + +/* Field Offsets */ +#define FB_EXPTH 0 + +/* Field Masks */ +#define FM_EXPTH 0XFF + +/* Field Values */ +#define FV_EXPTH_0DB 0xFF +#define FV_EXPTH_N95PT625DB 0x0 + +/* Register Masks */ +#define RM_EXPTH RM(FM_EXPTH, FB_EXPTH) + +/* Register Values */ +#define RV_EXPTH_0DB RV(FV_EXPTH_0DB, FB_EXPTH) +#define RV_EXPTH_N95PT625DB RV(FV_EXPTH_N95PT625DB, FB_EXPTH) + +/***************************** + * R_EXPRAT (0x34) * + *****************************/ + +/* Field Offsets */ +#define FB_EXPRAT 0 + +/* Field Masks */ +#define FM_EXPRAT 0X7 + +/* Register Masks */ +#define RM_EXPRAT RM(FM_EXPRAT, FB_EXPRAT) + +/****************************** + * R_XATKTCL (0x35) * + ******************************/ + +/* Field Offsets */ +#define FB_XATKTCL 0 + +/* Field Masks */ +#define FM_XATKTCL 0XFF + +/* Register Masks */ +#define RM_XATKTCL RM(FM_XATKTCL, FB_XATKTCL) + +/****************************** + * R_XATKTCH (0x36) * + ******************************/ + +/* Field Offsets */ +#define FB_XATKTCH 0 + +/* Field Masks */ +#define FM_XATKTCH 0XFF + +/* Register Masks */ +#define RM_XATKTCH RM(FM_XATKTCH, FB_XATKTCH) + +/****************************** + * R_XRELTCL (0x37) * + ******************************/ + +/* Field Offsets */ +#define FB_XRELTCL 0 + +/* Field Masks */ +#define FM_XRELTCL 0XFF + +/* Register Masks */ +#define RM_XRELTCL RM(FM_XRELTCL, FB_XRELTCL) + +/****************************** + * R_XRELTCH (0x38) * + ******************************/ + +/* Field Offsets */ +#define FB_XRELTCH 0 + +/* Field Masks */ +#define FM_XRELTCH 0XFF + +/* Register Masks */ +#define RM_XRELTCH RM(FM_XRELTCH, FB_XRELTCH) + +/**************************** + * R_FXCTL (0x39) * + ****************************/ + +/* Field Offsets */ +#define FB_FXCTL_3DEN 4 +#define FB_FXCTL_TEEN 3 +#define FB_FXCTL_TNLFBYPASS 2 +#define FB_FXCTL_BEEN 1 +#define FB_FXCTL_BNLFBYPASS 0 + +/* Field Masks */ +#define FM_FXCTL_3DEN 0X1 +#define FM_FXCTL_TEEN 0X1 +#define FM_FXCTL_TNLFBYPASS 0X1 +#define FM_FXCTL_BEEN 0X1 +#define FM_FXCTL_BNLFBYPASS 0X1 + +/* Field Values */ +#define FV_FXCTL_3DEN_ENABLE 0x1 +#define FV_FXCTL_3DEN_DISABLE 0x0 +#define FV_FXCTL_TEEN_ENABLE 0x1 +#define FV_FXCTL_TEEN_DISABLE 0x0 +#define FV_FXCTL_TNLFBYPASS_ENABLE 0x1 +#define FV_FXCTL_TNLFBYPASS_DISABLE 0x0 +#define FV_FXCTL_BEEN_ENABLE 0x1 +#define FV_FXCTL_BEEN_DISABLE 0x0 +#define FV_FXCTL_BNLFBYPASS_ENABLE 0x1 +#define FV_FXCTL_BNLFBYPASS_DISABLE 0x0 + +/* Register Masks */ +#define RM_FXCTL_3DEN RM(FM_FXCTL_3DEN, FB_FXCTL_3DEN) +#define RM_FXCTL_TEEN RM(FM_FXCTL_TEEN, FB_FXCTL_TEEN) +#define RM_FXCTL_TNLFBYPASS \ + RM(FM_FXCTL_TNLFBYPASS, FB_FXCTL_TNLFBYPASS) + +#define RM_FXCTL_BEEN RM(FM_FXCTL_BEEN, FB_FXCTL_BEEN) +#define RM_FXCTL_BNLFBYPASS \ + RM(FM_FXCTL_BNLFBYPASS, FB_FXCTL_BNLFBYPASS) + + +/* Register Values */ +#define RV_FXCTL_3DEN_ENABLE \ + RV(FV_FXCTL_3DEN_ENABLE, FB_FXCTL_3DEN) + +#define RV_FXCTL_3DEN_DISABLE \ + RV(FV_FXCTL_3DEN_DISABLE, FB_FXCTL_3DEN) + +#define RV_FXCTL_TEEN_ENABLE \ + RV(FV_FXCTL_TEEN_ENABLE, FB_FXCTL_TEEN) + +#define RV_FXCTL_TEEN_DISABLE \ + RV(FV_FXCTL_TEEN_DISABLE, FB_FXCTL_TEEN) + +#define RV_FXCTL_TNLFBYPASS_ENABLE \ + RV(FV_FXCTL_TNLFBYPASS_ENABLE, FB_FXCTL_TNLFBYPASS) + +#define RV_FXCTL_TNLFBYPASS_DISABLE \ + RV(FV_FXCTL_TNLFBYPASS_DISABLE, FB_FXCTL_TNLFBYPASS) + +#define RV_FXCTL_BEEN_ENABLE \ + RV(FV_FXCTL_BEEN_ENABLE, FB_FXCTL_BEEN) + +#define RV_FXCTL_BEEN_DISABLE \ + RV(FV_FXCTL_BEEN_DISABLE, FB_FXCTL_BEEN) + +#define RV_FXCTL_BNLFBYPASS_ENABLE \ + RV(FV_FXCTL_BNLFBYPASS_ENABLE, FB_FXCTL_BNLFBYPASS) + +#define RV_FXCTL_BNLFBYPASS_DISABLE \ + RV(FV_FXCTL_BNLFBYPASS_DISABLE, FB_FXCTL_BNLFBYPASS) + + +/******************************* + * R_DACCRWRL (0x3A) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRWRL_DACCRWDL 0 + +/* Field Masks */ +#define FM_DACCRWRL_DACCRWDL 0XFF + +/* Register Masks */ +#define RM_DACCRWRL_DACCRWDL \ + RM(FM_DACCRWRL_DACCRWDL, FB_DACCRWRL_DACCRWDL) + + +/******************************* + * R_DACCRWRM (0x3B) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRWRM_DACCRWDM 0 + +/* Field Masks */ +#define FM_DACCRWRM_DACCRWDM 0XFF + +/* Register Masks */ +#define RM_DACCRWRM_DACCRWDM \ + RM(FM_DACCRWRM_DACCRWDM, FB_DACCRWRM_DACCRWDM) + + +/******************************* + * R_DACCRWRH (0x3C) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRWRH_DACCRWDH 0 + +/* Field Masks */ +#define FM_DACCRWRH_DACCRWDH 0XFF + +/* Register Masks */ +#define RM_DACCRWRH_DACCRWDH \ + RM(FM_DACCRWRH_DACCRWDH, FB_DACCRWRH_DACCRWDH) + + +/******************************* + * R_DACCRRDL (0x3D) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRRDL 0 + +/* Field Masks */ +#define FM_DACCRRDL 0XFF + +/* Register Masks */ +#define RM_DACCRRDL RM(FM_DACCRRDL, FB_DACCRRDL) + +/******************************* + * R_DACCRRDM (0x3E) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRRDM 0 + +/* Field Masks */ +#define FM_DACCRRDM 0XFF + +/* Register Masks */ +#define RM_DACCRRDM RM(FM_DACCRRDM, FB_DACCRRDM) + +/******************************* + * R_DACCRRDH (0x3F) * + *******************************/ + +/* Field Offsets */ +#define FB_DACCRRDH 0 + +/* Field Masks */ +#define FM_DACCRRDH 0XFF + +/* Register Masks */ +#define RM_DACCRRDH RM(FM_DACCRRDH, FB_DACCRRDH) + +/******************************** + * R_DACCRADDR (0x40) * + ********************************/ + +/* Field Offsets */ +#define FB_DACCRADDR_DACCRADD 0 + +/* Field Masks */ +#define FM_DACCRADDR_DACCRADD 0XFF + +/* Register Masks */ +#define RM_DACCRADDR_DACCRADD \ + RM(FM_DACCRADDR_DACCRADD, FB_DACCRADDR_DACCRADD) + + +/****************************** + * R_DCOFSEL (0x41) * + ******************************/ + +/* Field Offsets */ +#define FB_DCOFSEL_DC_COEF_SEL 0 + +/* Field Masks */ +#define FM_DCOFSEL_DC_COEF_SEL 0X7 + +/* Field Values */ +#define FV_DCOFSEL_DC_COEF_SEL_2_N8 0x0 +#define FV_DCOFSEL_DC_COEF_SEL_2_N9 0x1 +#define FV_DCOFSEL_DC_COEF_SEL_2_N10 0x2 +#define FV_DCOFSEL_DC_COEF_SEL_2_N11 0x3 +#define FV_DCOFSEL_DC_COEF_SEL_2_N12 0x4 +#define FV_DCOFSEL_DC_COEF_SEL_2_N13 0x5 +#define FV_DCOFSEL_DC_COEF_SEL_2_N14 0x6 +#define FV_DCOFSEL_DC_COEF_SEL_2_N15 0x7 + +/* Register Masks */ +#define RM_DCOFSEL_DC_COEF_SEL \ + RM(FM_DCOFSEL_DC_COEF_SEL, FB_DCOFSEL_DC_COEF_SEL) + + +/* Register Values */ +#define RV_DCOFSEL_DC_COEF_SEL_2_N8 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N8, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N9 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N9, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N10 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N10, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N11 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N11, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N12 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N12, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N13 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N13, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N14 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N14, FB_DCOFSEL_DC_COEF_SEL) + +#define RV_DCOFSEL_DC_COEF_SEL_2_N15 \ + RV(FV_DCOFSEL_DC_COEF_SEL_2_N15, FB_DCOFSEL_DC_COEF_SEL) + + +/****************************** + * R_PLLCTL9 (0x4E) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTL9_REFDIV_PLL1 0 + +/* Field Masks */ +#define FM_PLLCTL9_REFDIV_PLL1 0XFF + +/* Register Masks */ +#define RM_PLLCTL9_REFDIV_PLL1 \ + RM(FM_PLLCTL9_REFDIV_PLL1, FB_PLLCTL9_REFDIV_PLL1) + + +/****************************** + * R_PLLCTLA (0x4F) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLA_OUTDIV_PLL1 0 + +/* Field Masks */ +#define FM_PLLCTLA_OUTDIV_PLL1 0XFF + +/* Register Masks */ +#define RM_PLLCTLA_OUTDIV_PLL1 \ + RM(FM_PLLCTLA_OUTDIV_PLL1, FB_PLLCTLA_OUTDIV_PLL1) + + +/****************************** + * R_PLLCTLB (0x50) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLB_FBDIV_PLL1L 0 + +/* Field Masks */ +#define FM_PLLCTLB_FBDIV_PLL1L 0XFF + +/* Register Masks */ +#define RM_PLLCTLB_FBDIV_PLL1L \ + RM(FM_PLLCTLB_FBDIV_PLL1L, FB_PLLCTLB_FBDIV_PLL1L) + + +/****************************** + * R_PLLCTLC (0x51) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLC_FBDIV_PLL1H 0 + +/* Field Masks */ +#define FM_PLLCTLC_FBDIV_PLL1H 0X7 + +/* Register Masks */ +#define RM_PLLCTLC_FBDIV_PLL1H \ + RM(FM_PLLCTLC_FBDIV_PLL1H, FB_PLLCTLC_FBDIV_PLL1H) + + +/****************************** + * R_PLLCTLD (0x52) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLD_RZ_PLL1 3 +#define FB_PLLCTLD_CP_PLL1 0 + +/* Field Masks */ +#define FM_PLLCTLD_RZ_PLL1 0X7 +#define FM_PLLCTLD_CP_PLL1 0X7 + +/* Register Masks */ +#define RM_PLLCTLD_RZ_PLL1 \ + RM(FM_PLLCTLD_RZ_PLL1, FB_PLLCTLD_RZ_PLL1) + +#define RM_PLLCTLD_CP_PLL1 \ + RM(FM_PLLCTLD_CP_PLL1, FB_PLLCTLD_CP_PLL1) + + +/****************************** + * R_PLLCTLE (0x53) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLE_REFDIV_PLL2 0 + +/* Field Masks */ +#define FM_PLLCTLE_REFDIV_PLL2 0XFF + +/* Register Masks */ +#define RM_PLLCTLE_REFDIV_PLL2 \ + RM(FM_PLLCTLE_REFDIV_PLL2, FB_PLLCTLE_REFDIV_PLL2) + + +/****************************** + * R_PLLCTLF (0x54) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTLF_OUTDIV_PLL2 0 + +/* Field Masks */ +#define FM_PLLCTLF_OUTDIV_PLL2 0XFF + +/* Register Masks */ +#define RM_PLLCTLF_OUTDIV_PLL2 \ + RM(FM_PLLCTLF_OUTDIV_PLL2, FB_PLLCTLF_OUTDIV_PLL2) + + +/******************************* + * R_PLLCTL10 (0x55) * + *******************************/ + +/* Field Offsets */ +#define FB_PLLCTL10_FBDIV_PLL2L 0 + +/* Field Masks */ +#define FM_PLLCTL10_FBDIV_PLL2L 0XFF + +/* Register Masks */ +#define RM_PLLCTL10_FBDIV_PLL2L \ + RM(FM_PLLCTL10_FBDIV_PLL2L, FB_PLLCTL10_FBDIV_PLL2L) + + +/******************************* + * R_PLLCTL11 (0x56) * + *******************************/ + +/* Field Offsets */ +#define FB_PLLCTL11_FBDIV_PLL2H 0 + +/* Field Masks */ +#define FM_PLLCTL11_FBDIV_PLL2H 0X7 + +/* Register Masks */ +#define RM_PLLCTL11_FBDIV_PLL2H \ + RM(FM_PLLCTL11_FBDIV_PLL2H, FB_PLLCTL11_FBDIV_PLL2H) + + +/******************************* + * R_PLLCTL12 (0x57) * + *******************************/ + +/* Field Offsets */ +#define FB_PLLCTL12_RZ_PLL2 3 +#define FB_PLLCTL12_CP_PLL2 0 + +/* Field Masks */ +#define FM_PLLCTL12_RZ_PLL2 0X7 +#define FM_PLLCTL12_CP_PLL2 0X7 + +/* Register Masks */ +#define RM_PLLCTL12_RZ_PLL2 \ + RM(FM_PLLCTL12_RZ_PLL2, FB_PLLCTL12_RZ_PLL2) + +#define RM_PLLCTL12_CP_PLL2 \ + RM(FM_PLLCTL12_CP_PLL2, FB_PLLCTL12_CP_PLL2) + + +/******************************* + * R_PLLCTL1B (0x60) * + *******************************/ + +/* Field Offsets */ +#define FB_PLLCTL1B_VCOI_PLL2 4 +#define FB_PLLCTL1B_VCOI_PLL1 2 + +/* Field Masks */ +#define FM_PLLCTL1B_VCOI_PLL2 0X3 +#define FM_PLLCTL1B_VCOI_PLL1 0X3 + +/* Register Masks */ +#define RM_PLLCTL1B_VCOI_PLL2 \ + RM(FM_PLLCTL1B_VCOI_PLL2, FB_PLLCTL1B_VCOI_PLL2) + +#define RM_PLLCTL1B_VCOI_PLL1 \ + RM(FM_PLLCTL1B_VCOI_PLL1, FB_PLLCTL1B_VCOI_PLL1) + + +/******************************* + * R_PLLCTL1C (0x61) * + *******************************/ + +/* Field Offsets */ +#define FB_PLLCTL1C_PDB_PLL2 2 +#define FB_PLLCTL1C_PDB_PLL1 1 + +/* Field Masks */ +#define FM_PLLCTL1C_PDB_PLL2 0X1 +#define FM_PLLCTL1C_PDB_PLL1 0X1 + +/* Field Values */ +#define FV_PLLCTL1C_PDB_PLL2_ENABLE 0x1 +#define FV_PLLCTL1C_PDB_PLL2_DISABLE 0x0 +#define FV_PLLCTL1C_PDB_PLL1_ENABLE 0x1 +#define FV_PLLCTL1C_PDB_PLL1_DISABLE 0x0 + +/* Register Masks */ +#define RM_PLLCTL1C_PDB_PLL2 \ + RM(FM_PLLCTL1C_PDB_PLL2, FB_PLLCTL1C_PDB_PLL2) + +#define RM_PLLCTL1C_PDB_PLL1 \ + RM(FM_PLLCTL1C_PDB_PLL1, FB_PLLCTL1C_PDB_PLL1) + + +/* Register Values */ +#define RV_PLLCTL1C_PDB_PLL2_ENABLE \ + RV(FV_PLLCTL1C_PDB_PLL2_ENABLE, FB_PLLCTL1C_PDB_PLL2) + +#define RV_PLLCTL1C_PDB_PLL2_DISABLE \ + RV(FV_PLLCTL1C_PDB_PLL2_DISABLE, FB_PLLCTL1C_PDB_PLL2) + +#define RV_PLLCTL1C_PDB_PLL1_ENABLE \ + RV(FV_PLLCTL1C_PDB_PLL1_ENABLE, FB_PLLCTL1C_PDB_PLL1) + +#define RV_PLLCTL1C_PDB_PLL1_DISABLE \ + RV(FV_PLLCTL1C_PDB_PLL1_DISABLE, FB_PLLCTL1C_PDB_PLL1) + + +/******************************* + * R_TIMEBASE (0x77) * + *******************************/ + +/* Field Offsets */ +#define FB_TIMEBASE_DIVIDER 0 + +/* Field Masks */ +#define FM_TIMEBASE_DIVIDER 0XFF + +/* Register Masks */ +#define RM_TIMEBASE_DIVIDER \ + RM(FM_TIMEBASE_DIVIDER, FB_TIMEBASE_DIVIDER) + + +/***************************** + * R_DEVIDL (0x7D) * + *****************************/ + +/* Field Offsets */ +#define FB_DEVIDL_DIDL 0 + +/* Field Masks */ +#define FM_DEVIDL_DIDL 0XFF + +/* Register Masks */ +#define RM_DEVIDL_DIDL RM(FM_DEVIDL_DIDL, FB_DEVIDL_DIDL) + +/***************************** + * R_DEVIDH (0x7E) * + *****************************/ + +/* Field Offsets */ +#define FB_DEVIDH_DIDH 0 + +/* Field Masks */ +#define FM_DEVIDH_DIDH 0XFF + +/* Register Masks */ +#define RM_DEVIDH_DIDH RM(FM_DEVIDH_DIDH, FB_DEVIDH_DIDH) + +/**************************** + * R_RESET (0x80) * + ****************************/ + +/* Field Offsets */ +#define FB_RESET 0 + +/* Field Masks */ +#define FM_RESET 0XFF + +/* Field Values */ +#define FV_RESET_ENABLE 0x85 + +/* Register Masks */ +#define RM_RESET RM(FM_RESET, FB_RESET) + +/* Register Values */ +#define RV_RESET_ENABLE RV(FV_RESET_ENABLE, FB_RESET) + +/******************************** + * R_DACCRSTAT (0x8A) * + ********************************/ + +/* Field Offsets */ +#define FB_DACCRSTAT_DACCR_BUSY 7 + +/* Field Masks */ +#define FM_DACCRSTAT_DACCR_BUSY 0X1 + +/* Register Masks */ +#define RM_DACCRSTAT_DACCR_BUSY \ + RM(FM_DACCRSTAT_DACCR_BUSY, FB_DACCRSTAT_DACCR_BUSY) + + +/****************************** + * R_PLLCTL0 (0x8E) * + ******************************/ + +/* Field Offsets */ +#define FB_PLLCTL0_PLL2_LOCK 1 +#define FB_PLLCTL0_PLL1_LOCK 0 + +/* Field Masks */ +#define FM_PLLCTL0_PLL2_LOCK 0X1 +#define FM_PLLCTL0_PLL1_LOCK 0X1 + +/* Register Masks */ +#define RM_PLLCTL0_PLL2_LOCK \ + RM(FM_PLLCTL0_PLL2_LOCK, FB_PLLCTL0_PLL2_LOCK) + +#define RM_PLLCTL0_PLL1_LOCK \ + RM(FM_PLLCTL0_PLL1_LOCK, FB_PLLCTL0_PLL1_LOCK) + + +/******************************** + * R_PLLREFSEL (0x8F) * + ********************************/ + +/* Field Offsets */ +#define FB_PLLREFSEL_PLL2_REF_SEL 4 +#define FB_PLLREFSEL_PLL1_REF_SEL 0 + +/* Field Masks */ +#define FM_PLLREFSEL_PLL2_REF_SEL 0X7 +#define FM_PLLREFSEL_PLL1_REF_SEL 0X7 + +/* Field Values */ +#define FV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1 0x0 +#define FV_PLLREFSEL_PLL2_REF_SEL_MCLK2 0x1 +#define FV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1 0x0 +#define FV_PLLREFSEL_PLL1_REF_SEL_MCLK2 0x1 + +/* Register Masks */ +#define RM_PLLREFSEL_PLL2_REF_SEL \ + RM(FM_PLLREFSEL_PLL2_REF_SEL, FB_PLLREFSEL_PLL2_REF_SEL) + +#define RM_PLLREFSEL_PLL1_REF_SEL \ + RM(FM_PLLREFSEL_PLL1_REF_SEL, FB_PLLREFSEL_PLL1_REF_SEL) + + +/* Register Values */ +#define RV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1 \ + RV(FV_PLLREFSEL_PLL2_REF_SEL_XTAL_MCLK1, FB_PLLREFSEL_PLL2_REF_SEL) + +#define RV_PLLREFSEL_PLL2_REF_SEL_MCLK2 \ + RV(FV_PLLREFSEL_PLL2_REF_SEL_MCLK2, FB_PLLREFSEL_PLL2_REF_SEL) + +#define RV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1 \ + RV(FV_PLLREFSEL_PLL1_REF_SEL_XTAL_MCLK1, FB_PLLREFSEL_PLL1_REF_SEL) + +#define RV_PLLREFSEL_PLL1_REF_SEL_MCLK2 \ + RV(FV_PLLREFSEL_PLL1_REF_SEL_MCLK2, FB_PLLREFSEL_PLL1_REF_SEL) + + +/******************************* + * R_DACMBCEN (0xC7) * + *******************************/ + +/* Field Offsets */ +#define FB_DACMBCEN_MBCEN3 2 +#define FB_DACMBCEN_MBCEN2 1 +#define FB_DACMBCEN_MBCEN1 0 + +/* Field Masks */ +#define FM_DACMBCEN_MBCEN3 0X1 +#define FM_DACMBCEN_MBCEN2 0X1 +#define FM_DACMBCEN_MBCEN1 0X1 + +/* Register Masks */ +#define RM_DACMBCEN_MBCEN3 \ + RM(FM_DACMBCEN_MBCEN3, FB_DACMBCEN_MBCEN3) + +#define RM_DACMBCEN_MBCEN2 \ + RM(FM_DACMBCEN_MBCEN2, FB_DACMBCEN_MBCEN2) + +#define RM_DACMBCEN_MBCEN1 \ + RM(FM_DACMBCEN_MBCEN1, FB_DACMBCEN_MBCEN1) + + +/******************************** + * R_DACMBCCTL (0xC8) * + ********************************/ + +/* Field Offsets */ +#define FB_DACMBCCTL_LVLMODE3 5 +#define FB_DACMBCCTL_WINSEL3 4 +#define FB_DACMBCCTL_LVLMODE2 3 +#define FB_DACMBCCTL_WINSEL2 2 +#define FB_DACMBCCTL_LVLMODE1 1 +#define FB_DACMBCCTL_WINSEL1 0 + +/* Field Masks */ +#define FM_DACMBCCTL_LVLMODE3 0X1 +#define FM_DACMBCCTL_WINSEL3 0X1 +#define FM_DACMBCCTL_LVLMODE2 0X1 +#define FM_DACMBCCTL_WINSEL2 0X1 +#define FM_DACMBCCTL_LVLMODE1 0X1 +#define FM_DACMBCCTL_WINSEL1 0X1 + +/* Register Masks */ +#define RM_DACMBCCTL_LVLMODE3 \ + RM(FM_DACMBCCTL_LVLMODE3, FB_DACMBCCTL_LVLMODE3) + +#define RM_DACMBCCTL_WINSEL3 \ + RM(FM_DACMBCCTL_WINSEL3, FB_DACMBCCTL_WINSEL3) + +#define RM_DACMBCCTL_LVLMODE2 \ + RM(FM_DACMBCCTL_LVLMODE2, FB_DACMBCCTL_LVLMODE2) + +#define RM_DACMBCCTL_WINSEL2 \ + RM(FM_DACMBCCTL_WINSEL2, FB_DACMBCCTL_WINSEL2) + +#define RM_DACMBCCTL_LVLMODE1 \ + RM(FM_DACMBCCTL_LVLMODE1, FB_DACMBCCTL_LVLMODE1) + +#define RM_DACMBCCTL_WINSEL1 \ + RM(FM_DACMBCCTL_WINSEL1, FB_DACMBCCTL_WINSEL1) + + +/********************************* + * R_DACMBCMUG1 (0xC9) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCMUG1_PHASE 5 +#define FB_DACMBCMUG1_MUGAIN 0 + +/* Field Masks */ +#define FM_DACMBCMUG1_PHASE 0X1 +#define FM_DACMBCMUG1_MUGAIN 0X1F + +/* Register Masks */ +#define RM_DACMBCMUG1_PHASE \ + RM(FM_DACMBCMUG1_PHASE, FB_DACMBCMUG1_PHASE) + +#define RM_DACMBCMUG1_MUGAIN \ + RM(FM_DACMBCMUG1_MUGAIN, FB_DACMBCMUG1_MUGAIN) + + +/********************************* + * R_DACMBCTHR1 (0xCA) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCTHR1_THRESH 0 + +/* Field Masks */ +#define FM_DACMBCTHR1_THRESH 0XFF + +/* Register Masks */ +#define RM_DACMBCTHR1_THRESH \ + RM(FM_DACMBCTHR1_THRESH, FB_DACMBCTHR1_THRESH) + + +/********************************* + * R_DACMBCRAT1 (0xCB) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCRAT1_RATIO 0 + +/* Field Masks */ +#define FM_DACMBCRAT1_RATIO 0X1F + +/* Register Masks */ +#define RM_DACMBCRAT1_RATIO \ + RM(FM_DACMBCRAT1_RATIO, FB_DACMBCRAT1_RATIO) + + +/********************************** + * R_DACMBCATK1L (0xCC) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK1L_TCATKL 0 + +/* Field Masks */ +#define FM_DACMBCATK1L_TCATKL 0XFF + +/* Register Masks */ +#define RM_DACMBCATK1L_TCATKL \ + RM(FM_DACMBCATK1L_TCATKL, FB_DACMBCATK1L_TCATKL) + + +/********************************** + * R_DACMBCATK1H (0xCD) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK1H_TCATKH 0 + +/* Field Masks */ +#define FM_DACMBCATK1H_TCATKH 0XFF + +/* Register Masks */ +#define RM_DACMBCATK1H_TCATKH \ + RM(FM_DACMBCATK1H_TCATKH, FB_DACMBCATK1H_TCATKH) + + +/********************************** + * R_DACMBCREL1L (0xCE) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL1L_TCRELL 0 + +/* Field Masks */ +#define FM_DACMBCREL1L_TCRELL 0XFF + +/* Register Masks */ +#define RM_DACMBCREL1L_TCRELL \ + RM(FM_DACMBCREL1L_TCRELL, FB_DACMBCREL1L_TCRELL) + + +/********************************** + * R_DACMBCREL1H (0xCF) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL1H_TCRELH 0 + +/* Field Masks */ +#define FM_DACMBCREL1H_TCRELH 0XFF + +/* Register Masks */ +#define RM_DACMBCREL1H_TCRELH \ + RM(FM_DACMBCREL1H_TCRELH, FB_DACMBCREL1H_TCRELH) + + +/********************************* + * R_DACMBCMUG2 (0xD0) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCMUG2_PHASE 5 +#define FB_DACMBCMUG2_MUGAIN 0 + +/* Field Masks */ +#define FM_DACMBCMUG2_PHASE 0X1 +#define FM_DACMBCMUG2_MUGAIN 0X1F + +/* Register Masks */ +#define RM_DACMBCMUG2_PHASE \ + RM(FM_DACMBCMUG2_PHASE, FB_DACMBCMUG2_PHASE) + +#define RM_DACMBCMUG2_MUGAIN \ + RM(FM_DACMBCMUG2_MUGAIN, FB_DACMBCMUG2_MUGAIN) + + +/********************************* + * R_DACMBCTHR2 (0xD1) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCTHR2_THRESH 0 + +/* Field Masks */ +#define FM_DACMBCTHR2_THRESH 0XFF + +/* Register Masks */ +#define RM_DACMBCTHR2_THRESH \ + RM(FM_DACMBCTHR2_THRESH, FB_DACMBCTHR2_THRESH) + + +/********************************* + * R_DACMBCRAT2 (0xD2) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCRAT2_RATIO 0 + +/* Field Masks */ +#define FM_DACMBCRAT2_RATIO 0X1F + +/* Register Masks */ +#define RM_DACMBCRAT2_RATIO \ + RM(FM_DACMBCRAT2_RATIO, FB_DACMBCRAT2_RATIO) + + +/********************************** + * R_DACMBCATK2L (0xD3) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK2L_TCATKL 0 + +/* Field Masks */ +#define FM_DACMBCATK2L_TCATKL 0XFF + +/* Register Masks */ +#define RM_DACMBCATK2L_TCATKL \ + RM(FM_DACMBCATK2L_TCATKL, FB_DACMBCATK2L_TCATKL) + + +/********************************** + * R_DACMBCATK2H (0xD4) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK2H_TCATKH 0 + +/* Field Masks */ +#define FM_DACMBCATK2H_TCATKH 0XFF + +/* Register Masks */ +#define RM_DACMBCATK2H_TCATKH \ + RM(FM_DACMBCATK2H_TCATKH, FB_DACMBCATK2H_TCATKH) + + +/********************************** + * R_DACMBCREL2L (0xD5) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL2L_TCRELL 0 + +/* Field Masks */ +#define FM_DACMBCREL2L_TCRELL 0XFF + +/* Register Masks */ +#define RM_DACMBCREL2L_TCRELL \ + RM(FM_DACMBCREL2L_TCRELL, FB_DACMBCREL2L_TCRELL) + + +/********************************** + * R_DACMBCREL2H (0xD6) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL2H_TCRELH 0 + +/* Field Masks */ +#define FM_DACMBCREL2H_TCRELH 0XFF + +/* Register Masks */ +#define RM_DACMBCREL2H_TCRELH \ + RM(FM_DACMBCREL2H_TCRELH, FB_DACMBCREL2H_TCRELH) + + +/********************************* + * R_DACMBCMUG3 (0xD7) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCMUG3_PHASE 5 +#define FB_DACMBCMUG3_MUGAIN 0 + +/* Field Masks */ +#define FM_DACMBCMUG3_PHASE 0X1 +#define FM_DACMBCMUG3_MUGAIN 0X1F + +/* Register Masks */ +#define RM_DACMBCMUG3_PHASE \ + RM(FM_DACMBCMUG3_PHASE, FB_DACMBCMUG3_PHASE) + +#define RM_DACMBCMUG3_MUGAIN \ + RM(FM_DACMBCMUG3_MUGAIN, FB_DACMBCMUG3_MUGAIN) + + +/********************************* + * R_DACMBCTHR3 (0xD8) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCTHR3_THRESH 0 + +/* Field Masks */ +#define FM_DACMBCTHR3_THRESH 0XFF + +/* Register Masks */ +#define RM_DACMBCTHR3_THRESH \ + RM(FM_DACMBCTHR3_THRESH, FB_DACMBCTHR3_THRESH) + + +/********************************* + * R_DACMBCRAT3 (0xD9) * + *********************************/ + +/* Field Offsets */ +#define FB_DACMBCRAT3_RATIO 0 + +/* Field Masks */ +#define FM_DACMBCRAT3_RATIO 0X1F + +/* Register Masks */ +#define RM_DACMBCRAT3_RATIO \ + RM(FM_DACMBCRAT3_RATIO, FB_DACMBCRAT3_RATIO) + + +/********************************** + * R_DACMBCATK3L (0xDA) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK3L_TCATKL 0 + +/* Field Masks */ +#define FM_DACMBCATK3L_TCATKL 0XFF + +/* Register Masks */ +#define RM_DACMBCATK3L_TCATKL \ + RM(FM_DACMBCATK3L_TCATKL, FB_DACMBCATK3L_TCATKL) + + +/********************************** + * R_DACMBCATK3H (0xDB) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCATK3H_TCATKH 0 + +/* Field Masks */ +#define FM_DACMBCATK3H_TCATKH 0XFF + +/* Register Masks */ +#define RM_DACMBCATK3H_TCATKH \ + RM(FM_DACMBCATK3H_TCATKH, FB_DACMBCATK3H_TCATKH) + + +/********************************** + * R_DACMBCREL3L (0xDC) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL3L_TCRELL 0 + +/* Field Masks */ +#define FM_DACMBCREL3L_TCRELL 0XFF + +/* Register Masks */ +#define RM_DACMBCREL3L_TCRELL \ + RM(FM_DACMBCREL3L_TCRELL, FB_DACMBCREL3L_TCRELL) + + +/********************************** + * R_DACMBCREL3H (0xDD) * + **********************************/ + +/* Field Offsets */ +#define FB_DACMBCREL3H_TCRELH 0 + +/* Field Masks */ +#define FM_DACMBCREL3H_TCRELH 0XFF + +/* Register Masks */ +#define RM_DACMBCREL3H_TCRELH \ + RM(FM_DACMBCREL3H_TCRELH, FB_DACMBCREL3H_TCRELH) + + +#endif /* __WOOKIE_H__ */ diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c new file mode 100644 index 0000000000..744aef32a2 --- /dev/null +++ b/sound/soc/codecs/tscs454.c @@ -0,0 +1,3484 @@ +// SPDX-License-Identifier: GPL-2.0 +// tscs454.c -- TSCS454 ALSA SoC Audio driver +// Copyright 2018 Tempo Semiconductor, Inc. +// Author: Steven Eckhoff + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tscs454.h" + +static const unsigned int PLL_44_1K_RATE = (44100 * 256); + +#define COEFF_SIZE 3 +#define BIQUAD_COEFF_COUNT 5 +#define BIQUAD_SIZE (COEFF_SIZE * BIQUAD_COEFF_COUNT) + +#define COEFF_RAM_MAX_ADDR 0xcd +#define COEFF_RAM_COEFF_COUNT (COEFF_RAM_MAX_ADDR + 1) +#define COEFF_RAM_SIZE (COEFF_SIZE * COEFF_RAM_COEFF_COUNT) + +enum { + TSCS454_DAI1_ID, + TSCS454_DAI2_ID, + TSCS454_DAI3_ID, + TSCS454_DAI_COUNT, +}; + +struct pll { + int id; + unsigned int users; + struct mutex lock; +}; + +static inline void pll_init(struct pll *pll, int id) +{ + pll->id = id; + mutex_init(&pll->lock); +} + +struct internal_rate { + struct pll *pll; +}; + +struct aif { + unsigned int id; + bool provider; + struct pll *pll; +}; + +static inline void aif_init(struct aif *aif, unsigned int id) +{ + aif->id = id; +} + +struct coeff_ram { + u8 cache[COEFF_RAM_SIZE]; + bool synced; + struct mutex lock; +}; + +static inline void init_coeff_ram_cache(u8 *cache) +{ + static const u8 norm_addrs[] = { 0x00, 0x05, 0x0a, 0x0f, 0x14, 0x19, + 0x1f, 0x20, 0x25, 0x2a, 0x2f, 0x34, 0x39, 0x3f, 0x40, 0x45, + 0x4a, 0x4f, 0x54, 0x59, 0x5f, 0x60, 0x65, 0x6a, 0x6f, 0x74, + 0x79, 0x7f, 0x80, 0x85, 0x8c, 0x91, 0x96, 0x97, 0x9c, 0xa3, + 0xa8, 0xad, 0xaf, 0xb0, 0xb5, 0xba, 0xbf, 0xc4, 0xc9}; + int i; + + for (i = 0; i < ARRAY_SIZE(norm_addrs); i++) + cache[((norm_addrs[i] + 1) * COEFF_SIZE) - 1] = 0x40; +} + +static inline void coeff_ram_init(struct coeff_ram *ram) +{ + init_coeff_ram_cache(ram->cache); + mutex_init(&ram->lock); +} + +struct aifs_status { + u8 streams; +}; + +static inline void set_aif_status_active(struct aifs_status *status, + int aif_id, bool playback) +{ + u8 mask = 0x01 << (aif_id * 2 + !playback); + + status->streams |= mask; +} + +static inline void set_aif_status_inactive(struct aifs_status *status, + int aif_id, bool playback) +{ + u8 mask = ~(0x01 << (aif_id * 2 + !playback)); + + status->streams &= mask; +} + +static bool aifs_active(struct aifs_status *status) +{ + return status->streams; +} + +static bool aif_active(struct aifs_status *status, int aif_id) +{ + return (0x03 << aif_id * 2) & status->streams; +} + +struct tscs454 { + struct regmap *regmap; + struct aif aifs[TSCS454_DAI_COUNT]; + + struct aifs_status aifs_status; + struct mutex aifs_status_lock; + + struct pll pll1; + struct pll pll2; + struct internal_rate internal_rate; + + struct coeff_ram dac_ram; + struct coeff_ram spk_ram; + struct coeff_ram sub_ram; + + struct clk *sysclk; + int sysclk_src_id; + unsigned int bclk_freq; +}; + +struct coeff_ram_ctl { + unsigned int addr; + struct soc_bytes_ext bytes_ext; +}; + +static const struct reg_sequence tscs454_patch[] = { + /* Assign ASRC out of the box so DAI 1 just works */ + { R_AUDIOMUX1, FV_ASRCIMUX_I2S1 | FV_I2S2MUX_I2S2 }, + { R_AUDIOMUX2, FV_ASRCOMUX_I2S1 | FV_DACMUX_I2S1 | FV_I2S3MUX_I2S3 }, + { R_AUDIOMUX3, FV_CLSSDMUX_I2S1 | FV_SUBMUX_I2S1_LR }, + { R_TDMCTL0, FV_TDMMD_256 }, + { VIRT_ADDR(0x0A, 0x13), 1 << 3 }, +}; + +static bool tscs454_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_PLLSTAT: + + case R_SPKCRRDL: + case R_SPKCRRDM: + case R_SPKCRRDH: + case R_SPKCRS: + + case R_DACCRRDL: + case R_DACCRRDM: + case R_DACCRRDH: + case R_DACCRS: + + case R_SUBCRRDL: + case R_SUBCRRDM: + case R_SUBCRRDH: + case R_SUBCRS: + return true; + default: + return false; + } +} + +static bool tscs454_writable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_SPKCRRDL: + case R_SPKCRRDM: + case R_SPKCRRDH: + + case R_DACCRRDL: + case R_DACCRRDM: + case R_DACCRRDH: + + case R_SUBCRRDL: + case R_SUBCRRDM: + case R_SUBCRRDH: + return false; + default: + return true; + } +} + +static bool tscs454_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_SPKCRWDL: + case R_SPKCRWDM: + case R_SPKCRWDH: + + case R_DACCRWDL: + case R_DACCRWDM: + case R_DACCRWDH: + + case R_SUBCRWDL: + case R_SUBCRWDM: + case R_SUBCRWDH: + return false; + default: + return true; + } +} + +static bool tscs454_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case R_SPKCRWDL: + case R_SPKCRWDM: + case R_SPKCRWDH: + case R_SPKCRRDL: + case R_SPKCRRDM: + case R_SPKCRRDH: + + case R_DACCRWDL: + case R_DACCRWDM: + case R_DACCRWDH: + case R_DACCRRDL: + case R_DACCRRDM: + case R_DACCRRDH: + + case R_SUBCRWDL: + case R_SUBCRWDM: + case R_SUBCRWDH: + case R_SUBCRRDL: + case R_SUBCRRDM: + case R_SUBCRRDH: + return true; + default: + return false; + } +} + +static const struct regmap_range_cfg tscs454_regmap_range_cfg = { + .name = "Pages", + .range_min = VIRT_BASE, + .range_max = VIRT_ADDR(0xFE, 0x02), + .selector_reg = R_PAGESEL, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, +}; + +static struct regmap_config const tscs454_regmap_cfg = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = tscs454_writable, + .readable_reg = tscs454_readable, + .volatile_reg = tscs454_volatile, + .precious_reg = tscs454_precious, + .ranges = &tscs454_regmap_range_cfg, + .num_ranges = 1, + .max_register = VIRT_ADDR(0xFE, 0x02), + .cache_type = REGCACHE_RBTREE, +}; + +static inline int tscs454_data_init(struct tscs454 *tscs454, + struct i2c_client *i2c) +{ + int i; + int ret; + + tscs454->regmap = devm_regmap_init_i2c(i2c, &tscs454_regmap_cfg); + if (IS_ERR(tscs454->regmap)) { + ret = PTR_ERR(tscs454->regmap); + return ret; + } + + for (i = 0; i < TSCS454_DAI_COUNT; i++) + aif_init(&tscs454->aifs[i], i); + + mutex_init(&tscs454->aifs_status_lock); + pll_init(&tscs454->pll1, 1); + pll_init(&tscs454->pll2, 2); + + coeff_ram_init(&tscs454->dac_ram); + coeff_ram_init(&tscs454->spk_ram); + coeff_ram_init(&tscs454->sub_ram); + + return 0; +} + +struct reg_setting { + unsigned int addr; + unsigned int val; +}; + +static int coeff_ram_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + u8 *coeff_ram; + struct mutex *coeff_ram_lock; + + if (strstr(kcontrol->id.name, "DAC")) { + coeff_ram = tscs454->dac_ram.cache; + coeff_ram_lock = &tscs454->dac_ram.lock; + } else if (strstr(kcontrol->id.name, "Speaker")) { + coeff_ram = tscs454->spk_ram.cache; + coeff_ram_lock = &tscs454->spk_ram.lock; + } else if (strstr(kcontrol->id.name, "Sub")) { + coeff_ram = tscs454->sub_ram.cache; + coeff_ram_lock = &tscs454->sub_ram.lock; + } else { + return -EINVAL; + } + + mutex_lock(coeff_ram_lock); + + memcpy(ucontrol->value.bytes.data, + &coeff_ram[ctl->addr * COEFF_SIZE], params->max); + + mutex_unlock(coeff_ram_lock); + + return 0; +} + +#define DACCRSTAT_MAX_TRYS 10 +static int write_coeff_ram(struct snd_soc_component *component, u8 *coeff_ram, + unsigned int r_stat, unsigned int r_addr, unsigned int r_wr, + unsigned int coeff_addr, unsigned int coeff_cnt) +{ + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + unsigned int val; + int cnt; + int trys; + int ret; + + for (cnt = 0; cnt < coeff_cnt; cnt++, coeff_addr++) { + + for (trys = 0; trys < DACCRSTAT_MAX_TRYS; trys++) { + val = snd_soc_component_read(component, r_stat); + if (!val) + break; + } + + if (trys == DACCRSTAT_MAX_TRYS) { + ret = -EIO; + dev_err(component->dev, + "Coefficient write error (%d)\n", ret); + return ret; + } + + ret = regmap_write(tscs454->regmap, r_addr, coeff_addr); + if (ret < 0) { + dev_err(component->dev, + "Failed to write dac ram address (%d)\n", ret); + return ret; + } + + ret = regmap_bulk_write(tscs454->regmap, r_wr, + &coeff_ram[coeff_addr * COEFF_SIZE], + COEFF_SIZE); + if (ret < 0) { + dev_err(component->dev, + "Failed to write dac ram (%d)\n", ret); + return ret; + } + } + + return 0; +} + +static int coeff_ram_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + unsigned int coeff_cnt = params->max / COEFF_SIZE; + u8 *coeff_ram; + struct mutex *coeff_ram_lock; + bool *coeff_ram_synced; + unsigned int r_stat; + unsigned int r_addr; + unsigned int r_wr; + unsigned int val; + int ret; + + if (strstr(kcontrol->id.name, "DAC")) { + coeff_ram = tscs454->dac_ram.cache; + coeff_ram_lock = &tscs454->dac_ram.lock; + coeff_ram_synced = &tscs454->dac_ram.synced; + r_stat = R_DACCRS; + r_addr = R_DACCRADD; + r_wr = R_DACCRWDL; + } else if (strstr(kcontrol->id.name, "Speaker")) { + coeff_ram = tscs454->spk_ram.cache; + coeff_ram_lock = &tscs454->spk_ram.lock; + coeff_ram_synced = &tscs454->spk_ram.synced; + r_stat = R_SPKCRS; + r_addr = R_SPKCRADD; + r_wr = R_SPKCRWDL; + } else if (strstr(kcontrol->id.name, "Sub")) { + coeff_ram = tscs454->sub_ram.cache; + coeff_ram_lock = &tscs454->sub_ram.lock; + coeff_ram_synced = &tscs454->sub_ram.synced; + r_stat = R_SUBCRS; + r_addr = R_SUBCRADD; + r_wr = R_SUBCRWDL; + } else { + return -EINVAL; + } + + mutex_lock(coeff_ram_lock); + + *coeff_ram_synced = false; + + memcpy(&coeff_ram[ctl->addr * COEFF_SIZE], + ucontrol->value.bytes.data, params->max); + + mutex_lock(&tscs454->pll1.lock); + mutex_lock(&tscs454->pll2.lock); + + val = snd_soc_component_read(component, R_PLLSTAT); + if (val) { /* PLLs locked */ + ret = write_coeff_ram(component, coeff_ram, + r_stat, r_addr, r_wr, + ctl->addr, coeff_cnt); + if (ret < 0) { + dev_err(component->dev, + "Failed to flush coeff ram cache (%d)\n", ret); + goto exit; + } + *coeff_ram_synced = true; + } + + ret = 0; +exit: + mutex_unlock(&tscs454->pll2.lock); + mutex_unlock(&tscs454->pll1.lock); + mutex_unlock(coeff_ram_lock); + + return ret; +} + +static inline int coeff_ram_sync(struct snd_soc_component *component, + struct tscs454 *tscs454) +{ + int ret; + + mutex_lock(&tscs454->dac_ram.lock); + if (!tscs454->dac_ram.synced) { + ret = write_coeff_ram(component, tscs454->dac_ram.cache, + R_DACCRS, R_DACCRADD, R_DACCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) { + mutex_unlock(&tscs454->dac_ram.lock); + return ret; + } + } + mutex_unlock(&tscs454->dac_ram.lock); + + mutex_lock(&tscs454->spk_ram.lock); + if (!tscs454->spk_ram.synced) { + ret = write_coeff_ram(component, tscs454->spk_ram.cache, + R_SPKCRS, R_SPKCRADD, R_SPKCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) { + mutex_unlock(&tscs454->spk_ram.lock); + return ret; + } + } + mutex_unlock(&tscs454->spk_ram.lock); + + mutex_lock(&tscs454->sub_ram.lock); + if (!tscs454->sub_ram.synced) { + ret = write_coeff_ram(component, tscs454->sub_ram.cache, + R_SUBCRS, R_SUBCRADD, R_SUBCRWDL, + 0x00, COEFF_RAM_COEFF_COUNT); + if (ret < 0) { + mutex_unlock(&tscs454->sub_ram.lock); + return ret; + } + } + mutex_unlock(&tscs454->sub_ram.lock); + + return 0; +} + +#define PLL_REG_SETTINGS_COUNT 11 +struct pll_ctl { + int freq_in; + struct reg_setting settings[PLL_REG_SETTINGS_COUNT]; +}; + +#define PLL_CTL(f, t, c1, r1, o1, f1l, f1h, c2, r2, o2, f2l, f2h) \ + { \ + .freq_in = f, \ + .settings = { \ + {R_PLL1CTL, c1}, \ + {R_PLL1RDIV, r1}, \ + {R_PLL1ODIV, o1}, \ + {R_PLL1FDIVL, f1l}, \ + {R_PLL1FDIVH, f1h}, \ + {R_PLL2CTL, c2}, \ + {R_PLL2RDIV, r2}, \ + {R_PLL2ODIV, o2}, \ + {R_PLL2FDIVL, f2l}, \ + {R_PLL2FDIVH, f2h}, \ + {R_TIMEBASE, t}, \ + }, \ + } + +static const struct pll_ctl pll_ctls[] = { + PLL_CTL(1411200, 0x05, + 0xB9, 0x07, 0x02, 0xC3, 0x04, + 0x5A, 0x02, 0x03, 0xE0, 0x01), + PLL_CTL(1536000, 0x05, + 0x5A, 0x02, 0x03, 0xE0, 0x01, + 0x5A, 0x02, 0x03, 0xB9, 0x01), + PLL_CTL(2822400, 0x0A, + 0x63, 0x07, 0x04, 0xC3, 0x04, + 0x62, 0x07, 0x03, 0x48, 0x03), + PLL_CTL(3072000, 0x0B, + 0x62, 0x07, 0x03, 0x48, 0x03, + 0x5A, 0x04, 0x03, 0xB9, 0x01), + PLL_CTL(5644800, 0x15, + 0x63, 0x0E, 0x04, 0xC3, 0x04, + 0x5A, 0x08, 0x03, 0xE0, 0x01), + PLL_CTL(6144000, 0x17, + 0x5A, 0x08, 0x03, 0xE0, 0x01, + 0x5A, 0x08, 0x03, 0xB9, 0x01), + PLL_CTL(12000000, 0x2E, + 0x5B, 0x19, 0x03, 0x00, 0x03, + 0x6A, 0x19, 0x05, 0x98, 0x04), + PLL_CTL(19200000, 0x4A, + 0x53, 0x14, 0x03, 0x80, 0x01, + 0x5A, 0x19, 0x03, 0xB9, 0x01), + PLL_CTL(22000000, 0x55, + 0x6A, 0x37, 0x05, 0x00, 0x06, + 0x62, 0x26, 0x03, 0x49, 0x02), + PLL_CTL(22579200, 0x57, + 0x62, 0x31, 0x03, 0x20, 0x03, + 0x53, 0x1D, 0x03, 0xB3, 0x01), + PLL_CTL(24000000, 0x5D, + 0x53, 0x19, 0x03, 0x80, 0x01, + 0x5B, 0x19, 0x05, 0x4C, 0x02), + PLL_CTL(24576000, 0x5F, + 0x53, 0x1D, 0x03, 0xB3, 0x01, + 0x62, 0x40, 0x03, 0x72, 0x03), + PLL_CTL(27000000, 0x68, + 0x62, 0x4B, 0x03, 0x00, 0x04, + 0x6A, 0x7D, 0x03, 0x20, 0x06), + PLL_CTL(36000000, 0x8C, + 0x5B, 0x4B, 0x03, 0x00, 0x03, + 0x6A, 0x7D, 0x03, 0x98, 0x04), + PLL_CTL(11289600, 0x2B, + 0x6A, 0x31, 0x03, 0x40, 0x06, + 0x5A, 0x12, 0x03, 0x1C, 0x02), + PLL_CTL(26000000, 0x65, + 0x63, 0x41, 0x05, 0x00, 0x06, + 0x5A, 0x26, 0x03, 0xEF, 0x01), + PLL_CTL(12288000, 0x2F, + 0x5A, 0x12, 0x03, 0x1C, 0x02, + 0x62, 0x20, 0x03, 0x72, 0x03), + PLL_CTL(40000000, 0x9B, + 0xA2, 0x7D, 0x03, 0x80, 0x04, + 0x63, 0x7D, 0x05, 0xE4, 0x06), + PLL_CTL(512000, 0x01, + 0x62, 0x01, 0x03, 0xD0, 0x02, + 0x5B, 0x01, 0x04, 0x72, 0x03), + PLL_CTL(705600, 0x02, + 0x62, 0x02, 0x03, 0x15, 0x04, + 0x62, 0x01, 0x04, 0x80, 0x02), + PLL_CTL(1024000, 0x03, + 0x62, 0x02, 0x03, 0xD0, 0x02, + 0x5B, 0x02, 0x04, 0x72, 0x03), + PLL_CTL(2048000, 0x07, + 0x62, 0x04, 0x03, 0xD0, 0x02, + 0x5B, 0x04, 0x04, 0x72, 0x03), + PLL_CTL(2400000, 0x08, + 0x62, 0x05, 0x03, 0x00, 0x03, + 0x63, 0x05, 0x05, 0x98, 0x04), +}; + +static inline const struct pll_ctl *get_pll_ctl(unsigned long freq_in) +{ + int i; + struct pll_ctl const *pll_ctl = NULL; + + for (i = 0; i < ARRAY_SIZE(pll_ctls); ++i) + if (pll_ctls[i].freq_in == freq_in) { + pll_ctl = &pll_ctls[i]; + break; + } + + return pll_ctl; +} + +enum { + PLL_INPUT_XTAL = 0, + PLL_INPUT_MCLK1, + PLL_INPUT_MCLK2, + PLL_INPUT_BCLK, +}; + +static int set_sysclk(struct snd_soc_component *component) +{ + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct pll_ctl const *pll_ctl; + unsigned long freq; + int i; + int ret; + + if (tscs454->sysclk_src_id < PLL_INPUT_BCLK) + freq = clk_get_rate(tscs454->sysclk); + else + freq = tscs454->bclk_freq; + pll_ctl = get_pll_ctl(freq); + if (!pll_ctl) { + ret = -EINVAL; + dev_err(component->dev, + "Invalid PLL input %lu (%d)\n", freq, ret); + return ret; + } + + for (i = 0; i < PLL_REG_SETTINGS_COUNT; ++i) { + ret = snd_soc_component_write(component, + pll_ctl->settings[i].addr, + pll_ctl->settings[i].val); + if (ret < 0) { + dev_err(component->dev, + "Failed to set pll setting (%d)\n", + ret); + return ret; + } + } + + return 0; +} + +static inline void reserve_pll(struct pll *pll) +{ + mutex_lock(&pll->lock); + pll->users++; + mutex_unlock(&pll->lock); +} + +static inline void free_pll(struct pll *pll) +{ + mutex_lock(&pll->lock); + pll->users--; + mutex_unlock(&pll->lock); +} + +static int pll_connected(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + int users; + + if (strstr(source->name, "PLL 1")) { + mutex_lock(&tscs454->pll1.lock); + users = tscs454->pll1.users; + mutex_unlock(&tscs454->pll1.lock); + dev_dbg(component->dev, "%s(): PLL 1 users = %d\n", __func__, + users); + } else { + mutex_lock(&tscs454->pll2.lock); + users = tscs454->pll2.users; + mutex_unlock(&tscs454->pll2.lock); + dev_dbg(component->dev, "%s(): PLL 2 users = %d\n", __func__, + users); + } + + return users; +} + +/* + * PLL must be enabled after power up and must be disabled before power down + * for proper clock switching. + */ +static int pll_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + bool enable; + bool pll1; + unsigned int msk; + unsigned int val; + int ret; + + if (strstr(w->name, "PLL 1")) + pll1 = true; + else + pll1 = false; + + msk = pll1 ? FM_PLLCTL_PLL1CLKEN : FM_PLLCTL_PLL2CLKEN; + + if (event == SND_SOC_DAPM_POST_PMU) + enable = true; + else + enable = false; + + if (enable) + val = pll1 ? FV_PLL1CLKEN_ENABLE : FV_PLL2CLKEN_ENABLE; + else + /* + * FV_PLL1CLKEN_DISABLE and FV_PLL2CLKEN_DISABLE are + * identical zero vzalues, there is no need to test + * the PLL index + */ + val = FV_PLL1CLKEN_DISABLE; + + ret = snd_soc_component_update_bits(component, R_PLLCTL, msk, val); + if (ret < 0) { + dev_err(component->dev, "Failed to %s PLL %d (%d)\n", + enable ? "enable" : "disable", + pll1 ? 1 : 2, + ret); + return ret; + } + + if (enable) { + msleep(20); // Wait for lock + ret = coeff_ram_sync(component, tscs454); + if (ret < 0) { + dev_err(component->dev, + "Failed to sync coeff ram (%d)\n", ret); + return ret; + } + } + + return 0; +} + +static inline int aif_set_provider(struct snd_soc_component *component, + unsigned int aif_id, bool provider) +{ + unsigned int reg; + unsigned int mask; + unsigned int val; + int ret; + + switch (aif_id) { + case TSCS454_DAI1_ID: + reg = R_I2SP1CTL; + break; + case TSCS454_DAI2_ID: + reg = R_I2SP2CTL; + break; + case TSCS454_DAI3_ID: + reg = R_I2SP3CTL; + break; + default: + ret = -ENODEV; + dev_err(component->dev, "Unknown DAI %d (%d)\n", aif_id, ret); + return ret; + } + mask = FM_I2SPCTL_PORTMS; + val = provider ? FV_PORTMS_MASTER : FV_PORTMS_SLAVE; + + ret = snd_soc_component_update_bits(component, reg, mask, val); + if (ret < 0) { + dev_err(component->dev, "Failed to set DAI %d to %s (%d)\n", + aif_id, provider ? "provider" : "consumer", ret); + return ret; + } + + return 0; +} + +static inline +int aif_prepare(struct snd_soc_component *component, struct aif *aif) +{ + int ret; + + ret = aif_set_provider(component, aif->id, aif->provider); + if (ret < 0) + return ret; + + return 0; +} + +static inline int aif_free(struct snd_soc_component *component, + struct aif *aif, bool playback) +{ + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + + mutex_lock(&tscs454->aifs_status_lock); + + dev_dbg(component->dev, "%s(): aif %d\n", __func__, aif->id); + + set_aif_status_inactive(&tscs454->aifs_status, aif->id, playback); + + dev_dbg(component->dev, "Set aif %d inactive. Streams status is 0x%x\n", + aif->id, tscs454->aifs_status.streams); + + if (!aif_active(&tscs454->aifs_status, aif->id)) { + /* Do config in slave mode */ + aif_set_provider(component, aif->id, false); + dev_dbg(component->dev, "Freeing pll %d from aif %d\n", + aif->pll->id, aif->id); + free_pll(aif->pll); + } + + if (!aifs_active(&tscs454->aifs_status)) { + dev_dbg(component->dev, "Freeing pll %d from ir\n", + tscs454->internal_rate.pll->id); + free_pll(tscs454->internal_rate.pll); + } + + mutex_unlock(&tscs454->aifs_status_lock); + + return 0; +} + +/* R_PLLCTL PG 0 ADDR 0x15 */ +static char const * const bclk_sel_txt[] = { + "BCLK 1", "BCLK 2", "BCLK 3"}; + +static struct soc_enum const bclk_sel_enum = + SOC_ENUM_SINGLE(R_PLLCTL, FB_PLLCTL_BCLKSEL, + ARRAY_SIZE(bclk_sel_txt), bclk_sel_txt); + +/* R_ISRC PG 0 ADDR 0x16 */ +static char const * const isrc_br_txt[] = { + "44.1kHz", "48kHz"}; + +static struct soc_enum const isrc_br_enum = + SOC_ENUM_SINGLE(R_ISRC, FB_ISRC_IBR, + ARRAY_SIZE(isrc_br_txt), isrc_br_txt); + +static char const * const isrc_bm_txt[] = { + "0.25x", "0.5x", "1.0x", "2.0x"}; + +static struct soc_enum const isrc_bm_enum = + SOC_ENUM_SINGLE(R_ISRC, FB_ISRC_IBM, + ARRAY_SIZE(isrc_bm_txt), isrc_bm_txt); + +/* R_SCLKCTL PG 0 ADDR 0x18 */ +static char const * const modular_rate_txt[] = { + "Reserved", "Half", "Full", "Auto",}; + +static struct soc_enum const adc_modular_rate_enum = + SOC_ENUM_SINGLE(R_SCLKCTL, FB_SCLKCTL_ASDM, + ARRAY_SIZE(modular_rate_txt), modular_rate_txt); + +static struct soc_enum const dac_modular_rate_enum = + SOC_ENUM_SINGLE(R_SCLKCTL, FB_SCLKCTL_DSDM, + ARRAY_SIZE(modular_rate_txt), modular_rate_txt); + +/* R_I2SIDCTL PG 0 ADDR 0x38 */ +static char const * const data_ctrl_txt[] = { + "L/R", "L/L", "R/R", "R/L"}; + +static struct soc_enum const data_in_ctrl_enums[] = { + SOC_ENUM_SINGLE(R_I2SIDCTL, FB_I2SIDCTL_I2SI1DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), + SOC_ENUM_SINGLE(R_I2SIDCTL, FB_I2SIDCTL_I2SI2DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), + SOC_ENUM_SINGLE(R_I2SIDCTL, FB_I2SIDCTL_I2SI3DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), +}; + +/* R_I2SODCTL PG 0 ADDR 0x39 */ +static struct soc_enum const data_out_ctrl_enums[] = { + SOC_ENUM_SINGLE(R_I2SODCTL, FB_I2SODCTL_I2SO1DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), + SOC_ENUM_SINGLE(R_I2SODCTL, FB_I2SODCTL_I2SO2DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), + SOC_ENUM_SINGLE(R_I2SODCTL, FB_I2SODCTL_I2SO3DCTL, + ARRAY_SIZE(data_ctrl_txt), data_ctrl_txt), +}; + +/* R_AUDIOMUX1 PG 0 ADDR 0x3A */ +static char const * const asrc_mux_txt[] = { + "None", "DAI 1", "DAI 2", "DAI 3"}; + +static struct soc_enum const asrc_in_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX1, FB_AUDIOMUX1_ASRCIMUX, + ARRAY_SIZE(asrc_mux_txt), asrc_mux_txt); + +static char const * const dai_mux_txt[] = { + "CH 0_1", "CH 2_3", "CH 4_5", "ADC/DMic 1", + "DMic 2", "ClassD", "DAC", "Sub"}; + +static struct soc_enum const dai2_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX1, FB_AUDIOMUX1_I2S2MUX, + ARRAY_SIZE(dai_mux_txt), dai_mux_txt); + +static struct snd_kcontrol_new const dai2_mux_dapm_enum = + SOC_DAPM_ENUM("DAI 2 Mux", dai2_mux_enum); + +static struct soc_enum const dai1_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX1, FB_AUDIOMUX1_I2S1MUX, + ARRAY_SIZE(dai_mux_txt), dai_mux_txt); + +static struct snd_kcontrol_new const dai1_mux_dapm_enum = + SOC_DAPM_ENUM("DAI 1 Mux", dai1_mux_enum); + +/* R_AUDIOMUX2 PG 0 ADDR 0x3B */ +static struct soc_enum const asrc_out_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX2, FB_AUDIOMUX2_ASRCOMUX, + ARRAY_SIZE(asrc_mux_txt), asrc_mux_txt); + +static struct soc_enum const dac_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX2, FB_AUDIOMUX2_DACMUX, + ARRAY_SIZE(dai_mux_txt), dai_mux_txt); + +static struct snd_kcontrol_new const dac_mux_dapm_enum = + SOC_DAPM_ENUM("DAC Mux", dac_mux_enum); + +static struct soc_enum const dai3_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX2, FB_AUDIOMUX2_I2S3MUX, + ARRAY_SIZE(dai_mux_txt), dai_mux_txt); + +static struct snd_kcontrol_new const dai3_mux_dapm_enum = + SOC_DAPM_ENUM("DAI 3 Mux", dai3_mux_enum); + +/* R_AUDIOMUX3 PG 0 ADDR 0x3C */ +static char const * const sub_mux_txt[] = { + "CH 0", "CH 1", "CH 0 + 1", + "CH 2", "CH 3", "CH 2 + 3", + "CH 4", "CH 5", "CH 4 + 5", + "ADC/DMic 1 Left", "ADC/DMic 1 Right", + "ADC/DMic 1 Left Plus Right", + "DMic 2 Left", "DMic 2 Right", "DMic 2 Left Plus Right", + "ClassD Left", "ClassD Right", "ClassD Left Plus Right"}; + +static struct soc_enum const sub_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX3, FB_AUDIOMUX3_SUBMUX, + ARRAY_SIZE(sub_mux_txt), sub_mux_txt); + +static struct snd_kcontrol_new const sub_mux_dapm_enum = + SOC_DAPM_ENUM("Sub Mux", sub_mux_enum); + +static struct soc_enum const classd_mux_enum = + SOC_ENUM_SINGLE(R_AUDIOMUX3, FB_AUDIOMUX3_CLSSDMUX, + ARRAY_SIZE(dai_mux_txt), dai_mux_txt); + +static struct snd_kcontrol_new const classd_mux_dapm_enum = + SOC_DAPM_ENUM("ClassD Mux", classd_mux_enum); + +/* R_HSDCTL1 PG 1 ADDR 0x01 */ +static char const * const jack_type_txt[] = { + "3 Terminal", "4 Terminal"}; + +static struct soc_enum const hp_jack_type_enum = + SOC_ENUM_SINGLE(R_HSDCTL1, FB_HSDCTL1_HPJKTYPE, + ARRAY_SIZE(jack_type_txt), jack_type_txt); + +static char const * const hs_det_pol_txt[] = { + "Rising", "Falling"}; + +static struct soc_enum const hs_det_pol_enum = + SOC_ENUM_SINGLE(R_HSDCTL1, FB_HSDCTL1_HSDETPOL, + ARRAY_SIZE(hs_det_pol_txt), hs_det_pol_txt); + +/* R_HSDCTL1 PG 1 ADDR 0x02 */ +static char const * const hs_mic_bias_force_txt[] = { + "Off", "Ring", "Sleeve"}; + +static struct soc_enum const hs_mic_bias_force_enum = + SOC_ENUM_SINGLE(R_HSDCTL2, FB_HSDCTL2_FMICBIAS1, + ARRAY_SIZE(hs_mic_bias_force_txt), + hs_mic_bias_force_txt); + +static char const * const plug_type_txt[] = { + "OMTP", "CTIA", "Reserved", "Headphone"}; + +static struct soc_enum const plug_type_force_enum = + SOC_ENUM_SINGLE(R_HSDCTL2, FB_HSDCTL2_FPLUGTYPE, + ARRAY_SIZE(plug_type_txt), plug_type_txt); + + +/* R_CH0AIC PG 1 ADDR 0x06 */ +static char const * const in_bst_mux_txt[] = { + "Input 1", "Input 2", "Input 3", "D2S"}; + +static struct soc_enum const in_bst_mux_ch0_enum = + SOC_ENUM_SINGLE(R_CH0AIC, FB_CH0AIC_INSELL, + ARRAY_SIZE(in_bst_mux_txt), + in_bst_mux_txt); +static struct snd_kcontrol_new const in_bst_mux_ch0_dapm_enum = + SOC_DAPM_ENUM("Input Boost Channel 0 Enum", + in_bst_mux_ch0_enum); + +static DECLARE_TLV_DB_SCALE(in_bst_vol_tlv_arr, 0, 1000, 0); + +static char const * const adc_mux_txt[] = { + "Input 1 Boost Bypass", "Input 2 Boost Bypass", + "Input 3 Boost Bypass", "Input Boost"}; + +static struct soc_enum const adc_mux_ch0_enum = + SOC_ENUM_SINGLE(R_CH0AIC, FB_CH0AIC_LADCIN, + ARRAY_SIZE(adc_mux_txt), adc_mux_txt); +static struct snd_kcontrol_new const adc_mux_ch0_dapm_enum = + SOC_DAPM_ENUM("ADC Channel 0 Enum", adc_mux_ch0_enum); + +static char const * const in_proc_mux_txt[] = { + "ADC", "DMic"}; + +static struct soc_enum const in_proc_ch0_enum = + SOC_ENUM_SINGLE(R_CH0AIC, FB_CH0AIC_IPCH0S, + ARRAY_SIZE(in_proc_mux_txt), in_proc_mux_txt); +static struct snd_kcontrol_new const in_proc_mux_ch0_dapm_enum = + SOC_DAPM_ENUM("Input Processor Channel 0 Enum", + in_proc_ch0_enum); + +/* R_CH1AIC PG 1 ADDR 0x07 */ +static struct soc_enum const in_bst_mux_ch1_enum = + SOC_ENUM_SINGLE(R_CH1AIC, FB_CH1AIC_INSELR, + ARRAY_SIZE(in_bst_mux_txt), + in_bst_mux_txt); +static struct snd_kcontrol_new const in_bst_mux_ch1_dapm_enum = + SOC_DAPM_ENUM("Input Boost Channel 1 Enum", + in_bst_mux_ch1_enum); + +static struct soc_enum const adc_mux_ch1_enum = + SOC_ENUM_SINGLE(R_CH1AIC, FB_CH1AIC_RADCIN, + ARRAY_SIZE(adc_mux_txt), adc_mux_txt); +static struct snd_kcontrol_new const adc_mux_ch1_dapm_enum = + SOC_DAPM_ENUM("ADC Channel 1 Enum", adc_mux_ch1_enum); + +static struct soc_enum const in_proc_ch1_enum = + SOC_ENUM_SINGLE(R_CH1AIC, FB_CH1AIC_IPCH1S, + ARRAY_SIZE(in_proc_mux_txt), in_proc_mux_txt); +static struct snd_kcontrol_new const in_proc_mux_ch1_dapm_enum = + SOC_DAPM_ENUM("Input Processor Channel 1 Enum", + in_proc_ch1_enum); + +/* R_ICTL0 PG 1 ADDR 0x0A */ +static char const * const pol_txt[] = { + "Normal", "Invert"}; + +static struct soc_enum const in_pol_ch1_enum = + SOC_ENUM_SINGLE(R_ICTL0, FB_ICTL0_IN0POL, + ARRAY_SIZE(pol_txt), pol_txt); + +static struct soc_enum const in_pol_ch0_enum = + SOC_ENUM_SINGLE(R_ICTL0, FB_ICTL0_IN1POL, + ARRAY_SIZE(pol_txt), pol_txt); + +static char const * const in_proc_ch_sel_txt[] = { + "Normal", "Mono Mix to Channel 0", + "Mono Mix to Channel 1", "Add"}; + +static struct soc_enum const in_proc_ch01_sel_enum = + SOC_ENUM_SINGLE(R_ICTL0, FB_ICTL0_INPCH10SEL, + ARRAY_SIZE(in_proc_ch_sel_txt), + in_proc_ch_sel_txt); + +/* R_ICTL1 PG 1 ADDR 0x0B */ +static struct soc_enum const in_pol_ch3_enum = + SOC_ENUM_SINGLE(R_ICTL1, FB_ICTL1_IN2POL, + ARRAY_SIZE(pol_txt), pol_txt); + +static struct soc_enum const in_pol_ch2_enum = + SOC_ENUM_SINGLE(R_ICTL1, FB_ICTL1_IN3POL, + ARRAY_SIZE(pol_txt), pol_txt); + +static struct soc_enum const in_proc_ch23_sel_enum = + SOC_ENUM_SINGLE(R_ICTL1, FB_ICTL1_INPCH32SEL, + ARRAY_SIZE(in_proc_ch_sel_txt), + in_proc_ch_sel_txt); + +/* R_MICBIAS PG 1 ADDR 0x0C */ +static char const * const mic_bias_txt[] = { + "2.5V", "2.1V", "1.8V", "Vdd"}; + +static struct soc_enum const mic_bias_2_enum = + SOC_ENUM_SINGLE(R_MICBIAS, FB_MICBIAS_MICBOV2, + ARRAY_SIZE(mic_bias_txt), mic_bias_txt); + +static struct soc_enum const mic_bias_1_enum = + SOC_ENUM_SINGLE(R_MICBIAS, FB_MICBIAS_MICBOV1, + ARRAY_SIZE(mic_bias_txt), mic_bias_txt); + +/* R_PGACTL0 PG 1 ADDR 0x0D */ +/* R_PGACTL1 PG 1 ADDR 0x0E */ +/* R_PGACTL2 PG 1 ADDR 0x0F */ +/* R_PGACTL3 PG 1 ADDR 0x10 */ +static DECLARE_TLV_DB_SCALE(in_pga_vol_tlv_arr, -1725, 75, 0); + +/* R_ICH0VOL PG1 ADDR 0x12 */ +/* R_ICH1VOL PG1 ADDR 0x13 */ +/* R_ICH2VOL PG1 ADDR 0x14 */ +/* R_ICH3VOL PG1 ADDR 0x15 */ +static DECLARE_TLV_DB_MINMAX(in_vol_tlv_arr, -7125, 2400); + +/* R_ASRCILVOL PG1 ADDR 0x16 */ +/* R_ASRCIRVOL PG1 ADDR 0x17 */ +/* R_ASRCOLVOL PG1 ADDR 0x18 */ +/* R_ASRCORVOL PG1 ADDR 0x19 */ +static DECLARE_TLV_DB_MINMAX(asrc_vol_tlv_arr, -9562, 600); + +/* R_ALCCTL0 PG1 ADDR 0x1D */ +static char const * const alc_mode_txt[] = { + "ALC", "Limiter"}; + +static struct soc_enum const alc_mode_enum = + SOC_ENUM_SINGLE(R_ALCCTL0, FB_ALCCTL0_ALCMODE, + ARRAY_SIZE(alc_mode_txt), alc_mode_txt); + +static char const * const alc_ref_text[] = { + "Channel 0", "Channel 1", "Channel 2", "Channel 3", "Peak"}; + +static struct soc_enum const alc_ref_enum = + SOC_ENUM_SINGLE(R_ALCCTL0, FB_ALCCTL0_ALCREF, + ARRAY_SIZE(alc_ref_text), alc_ref_text); + +/* R_ALCCTL1 PG 1 ADDR 0x1E */ +static DECLARE_TLV_DB_SCALE(alc_max_gain_tlv_arr, -1200, 600, 0); +static DECLARE_TLV_DB_SCALE(alc_target_tlv_arr, -2850, 150, 0); + +/* R_ALCCTL2 PG 1 ADDR 0x1F */ +static DECLARE_TLV_DB_SCALE(alc_min_gain_tlv_arr, -1725, 600, 0); + +/* R_NGATE PG 1 ADDR 0x21 */ +static DECLARE_TLV_DB_SCALE(ngth_tlv_arr, -7650, 150, 0); + +static char const * const ngate_type_txt[] = { + "PGA Constant", "ADC Mute"}; + +static struct soc_enum const ngate_type_enum = + SOC_ENUM_SINGLE(R_NGATE, FB_NGATE_NGG, + ARRAY_SIZE(ngate_type_txt), ngate_type_txt); + +/* R_DMICCTL PG 1 ADDR 0x22 */ +static char const * const dmic_mono_sel_txt[] = { + "Stereo", "Mono"}; + +static struct soc_enum const dmic_mono_sel_enum = + SOC_ENUM_SINGLE(R_DMICCTL, FB_DMICCTL_DMONO, + ARRAY_SIZE(dmic_mono_sel_txt), dmic_mono_sel_txt); + +/* R_DACCTL PG 2 ADDR 0x01 */ +static struct soc_enum const dac_pol_r_enum = + SOC_ENUM_SINGLE(R_DACCTL, FB_DACCTL_DACPOLR, + ARRAY_SIZE(pol_txt), pol_txt); + +static struct soc_enum const dac_pol_l_enum = + SOC_ENUM_SINGLE(R_DACCTL, FB_DACCTL_DACPOLL, + ARRAY_SIZE(pol_txt), pol_txt); + +static char const * const dac_dith_txt[] = { + "Half", "Full", "Disabled", "Static"}; + +static struct soc_enum const dac_dith_enum = + SOC_ENUM_SINGLE(R_DACCTL, FB_DACCTL_DACDITH, + ARRAY_SIZE(dac_dith_txt), dac_dith_txt); + +/* R_SPKCTL PG 2 ADDR 0x02 */ +static struct soc_enum const spk_pol_r_enum = + SOC_ENUM_SINGLE(R_SPKCTL, FB_SPKCTL_SPKPOLR, + ARRAY_SIZE(pol_txt), pol_txt); + +static struct soc_enum const spk_pol_l_enum = + SOC_ENUM_SINGLE(R_SPKCTL, FB_SPKCTL_SPKPOLL, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SUBCTL PG 2 ADDR 0x03 */ +static struct soc_enum const sub_pol_enum = + SOC_ENUM_SINGLE(R_SUBCTL, FB_SUBCTL_SUBPOL, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_MVOLL PG 2 ADDR 0x08 */ +/* R_MVOLR PG 2 ADDR 0x09 */ +static DECLARE_TLV_DB_MINMAX(mvol_tlv_arr, -9562, 0); + +/* R_HPVOLL PG 2 ADDR 0x0A */ +/* R_HPVOLR PG 2 ADDR 0x0B */ +static DECLARE_TLV_DB_SCALE(hp_vol_tlv_arr, -8850, 75, 0); + +/* R_SPKVOLL PG 2 ADDR 0x0C */ +/* R_SPKVOLR PG 2 ADDR 0x0D */ +static DECLARE_TLV_DB_SCALE(spk_vol_tlv_arr, -7725, 75, 0); + +/* R_SPKEQFILT PG 3 ADDR 0x01 */ +static char const * const eq_txt[] = { + "Pre Scale", + "Pre Scale + EQ Band 0", + "Pre Scale + EQ Band 0 - 1", + "Pre Scale + EQ Band 0 - 2", + "Pre Scale + EQ Band 0 - 3", + "Pre Scale + EQ Band 0 - 4", + "Pre Scale + EQ Band 0 - 5", +}; + +static struct soc_enum const spk_eq_enums[] = { + SOC_ENUM_SINGLE(R_SPKEQFILT, FB_SPKEQFILT_EQ2BE, + ARRAY_SIZE(eq_txt), eq_txt), + SOC_ENUM_SINGLE(R_SPKEQFILT, FB_SPKEQFILT_EQ1BE, + ARRAY_SIZE(eq_txt), eq_txt), +}; + +/* R_SPKMBCCTL PG 3 ADDR 0x0B */ +static char const * const lvl_mode_txt[] = { + "Average", "Peak"}; + +static struct soc_enum const spk_mbc3_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_LVLMODE3, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static char const * const win_sel_txt[] = { + "512", "64"}; + +static struct soc_enum const spk_mbc3_win_sel_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_WINSEL3, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const spk_mbc2_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_LVLMODE2, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const spk_mbc2_win_sel_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_WINSEL2, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const spk_mbc1_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_LVLMODE1, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const spk_mbc1_win_sel_enum = + SOC_ENUM_SINGLE(R_SPKMBCCTL, FB_SPKMBCCTL_WINSEL1, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_SPKMBCMUG1 PG 3 ADDR 0x0C */ +static struct soc_enum const spk_mbc1_phase_pol_enum = + SOC_ENUM_SINGLE(R_SPKMBCMUG1, FB_SPKMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +static DECLARE_TLV_DB_MINMAX(mbc_mug_tlv_arr, -4650, 0); + +/* R_SPKMBCTHR1 PG 3 ADDR 0x0D */ +static DECLARE_TLV_DB_MINMAX(thr_tlv_arr, -9562, 0); + +/* R_SPKMBCRAT1 PG 3 ADDR 0x0E */ +static char const * const comp_rat_txt[] = { + "Reserved", "1.5:1", "2:1", "3:1", "4:1", "5:1", "6:1", + "7:1", "8:1", "9:1", "10:1", "11:1", "12:1", "13:1", "14:1", + "15:1", "16:1", "17:1", "18:1", "19:1", "20:1"}; + +static struct soc_enum const spk_mbc1_comp_rat_enum = + SOC_ENUM_SINGLE(R_SPKMBCRAT1, FB_SPKMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SPKMBCMUG2 PG 3 ADDR 0x13 */ +static struct soc_enum const spk_mbc2_phase_pol_enum = + SOC_ENUM_SINGLE(R_SPKMBCMUG2, FB_SPKMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SPKMBCRAT2 PG 3 ADDR 0x15 */ +static struct soc_enum const spk_mbc2_comp_rat_enum = + SOC_ENUM_SINGLE(R_SPKMBCRAT2, FB_SPKMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SPKMBCMUG3 PG 3 ADDR 0x1A */ +static struct soc_enum const spk_mbc3_phase_pol_enum = + SOC_ENUM_SINGLE(R_SPKMBCMUG3, FB_SPKMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SPKMBCRAT3 PG 3 ADDR 0x1C */ +static struct soc_enum const spk_mbc3_comp_rat_enum = + SOC_ENUM_SINGLE(R_SPKMBCRAT3, FB_SPKMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SPKCLECTL PG 3 ADDR 0x21 */ +static struct soc_enum const spk_cle_lvl_mode_enum = + SOC_ENUM_SINGLE(R_SPKCLECTL, FB_SPKCLECTL_LVLMODE, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const spk_cle_win_sel_enum = + SOC_ENUM_SINGLE(R_SPKCLECTL, FB_SPKCLECTL_WINSEL, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_SPKCLEMUG PG 3 ADDR 0x22 */ +static DECLARE_TLV_DB_MINMAX(cle_mug_tlv_arr, 0, 4650); + +/* R_SPKCOMPRAT PG 3 ADDR 0x24 */ +static struct soc_enum const spk_comp_rat_enum = + SOC_ENUM_SINGLE(R_SPKCOMPRAT, FB_SPKCOMPRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SPKEXPTHR PG 3 ADDR 0x2F */ +static char const * const exp_rat_txt[] = { + "Reserved", "Reserved", "1:2", "1:3", + "1:4", "1:5", "1:6", "1:7"}; + +static struct soc_enum const spk_exp_rat_enum = + SOC_ENUM_SINGLE(R_SPKEXPRAT, FB_SPKEXPRAT_RATIO, + ARRAY_SIZE(exp_rat_txt), exp_rat_txt); + +/* R_DACEQFILT PG 4 ADDR 0x01 */ +static struct soc_enum const dac_eq_enums[] = { + SOC_ENUM_SINGLE(R_DACEQFILT, FB_DACEQFILT_EQ2BE, + ARRAY_SIZE(eq_txt), eq_txt), + SOC_ENUM_SINGLE(R_DACEQFILT, FB_DACEQFILT_EQ1BE, + ARRAY_SIZE(eq_txt), eq_txt), +}; + +/* R_DACMBCCTL PG 4 ADDR 0x0B */ +static struct soc_enum const dac_mbc3_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE3, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const dac_mbc3_win_sel_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL3, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const dac_mbc2_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE2, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const dac_mbc2_win_sel_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL2, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const dac_mbc1_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_LVLMODE1, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const dac_mbc1_win_sel_enum = + SOC_ENUM_SINGLE(R_DACMBCCTL, FB_DACMBCCTL_WINSEL1, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_DACMBCMUG1 PG 4 ADDR 0x0C */ +static struct soc_enum const dac_mbc1_phase_pol_enum = + SOC_ENUM_SINGLE(R_DACMBCMUG1, FB_DACMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_DACMBCRAT1 PG 4 ADDR 0x0E */ +static struct soc_enum const dac_mbc1_comp_rat_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT1, FB_DACMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_DACMBCMUG2 PG 4 ADDR 0x13 */ +static struct soc_enum const dac_mbc2_phase_pol_enum = + SOC_ENUM_SINGLE(R_DACMBCMUG2, FB_DACMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_DACMBCRAT2 PG 4 ADDR 0x15 */ +static struct soc_enum const dac_mbc2_comp_rat_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT2, FB_DACMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_DACMBCMUG3 PG 4 ADDR 0x1A */ +static struct soc_enum const dac_mbc3_phase_pol_enum = + SOC_ENUM_SINGLE(R_DACMBCMUG3, FB_DACMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_DACMBCRAT3 PG 4 ADDR 0x1C */ +static struct soc_enum const dac_mbc3_comp_rat_enum = + SOC_ENUM_SINGLE(R_DACMBCRAT3, FB_DACMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_DACCLECTL PG 4 ADDR 0x21 */ +static struct soc_enum const dac_cle_lvl_mode_enum = + SOC_ENUM_SINGLE(R_DACCLECTL, FB_DACCLECTL_LVLMODE, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const dac_cle_win_sel_enum = + SOC_ENUM_SINGLE(R_DACCLECTL, FB_DACCLECTL_WINSEL, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_DACCOMPRAT PG 4 ADDR 0x24 */ +static struct soc_enum const dac_comp_rat_enum = + SOC_ENUM_SINGLE(R_DACCOMPRAT, FB_DACCOMPRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_DACEXPRAT PG 4 ADDR 0x30 */ +static struct soc_enum const dac_exp_rat_enum = + SOC_ENUM_SINGLE(R_DACEXPRAT, FB_DACEXPRAT_RATIO, + ARRAY_SIZE(exp_rat_txt), exp_rat_txt); + +/* R_SUBEQFILT PG 5 ADDR 0x01 */ +static struct soc_enum const sub_eq_enums[] = { + SOC_ENUM_SINGLE(R_SUBEQFILT, FB_SUBEQFILT_EQ2BE, + ARRAY_SIZE(eq_txt), eq_txt), + SOC_ENUM_SINGLE(R_SUBEQFILT, FB_SUBEQFILT_EQ1BE, + ARRAY_SIZE(eq_txt), eq_txt), +}; + +/* R_SUBMBCCTL PG 5 ADDR 0x0B */ +static struct soc_enum const sub_mbc3_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_LVLMODE3, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const sub_mbc3_win_sel_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_WINSEL3, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const sub_mbc2_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_LVLMODE2, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const sub_mbc2_win_sel_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_WINSEL2, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +static struct soc_enum const sub_mbc1_lvl_det_mode_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_LVLMODE1, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); + +static struct soc_enum const sub_mbc1_win_sel_enum = + SOC_ENUM_SINGLE(R_SUBMBCCTL, FB_SUBMBCCTL_WINSEL1, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_SUBMBCMUG1 PG 5 ADDR 0x0C */ +static struct soc_enum const sub_mbc1_phase_pol_enum = + SOC_ENUM_SINGLE(R_SUBMBCMUG1, FB_SUBMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SUBMBCRAT1 PG 5 ADDR 0x0E */ +static struct soc_enum const sub_mbc1_comp_rat_enum = + SOC_ENUM_SINGLE(R_SUBMBCRAT1, FB_SUBMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SUBMBCMUG2 PG 5 ADDR 0x13 */ +static struct soc_enum const sub_mbc2_phase_pol_enum = + SOC_ENUM_SINGLE(R_SUBMBCMUG2, FB_SUBMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SUBMBCRAT2 PG 5 ADDR 0x15 */ +static struct soc_enum const sub_mbc2_comp_rat_enum = + SOC_ENUM_SINGLE(R_SUBMBCRAT2, FB_SUBMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SUBMBCMUG3 PG 5 ADDR 0x1A */ +static struct soc_enum const sub_mbc3_phase_pol_enum = + SOC_ENUM_SINGLE(R_SUBMBCMUG3, FB_SUBMBCMUG_PHASE, + ARRAY_SIZE(pol_txt), pol_txt); + +/* R_SUBMBCRAT3 PG 5 ADDR 0x1C */ +static struct soc_enum const sub_mbc3_comp_rat_enum = + SOC_ENUM_SINGLE(R_SUBMBCRAT3, FB_SUBMBCRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SUBCLECTL PG 5 ADDR 0x21 */ +static struct soc_enum const sub_cle_lvl_mode_enum = + SOC_ENUM_SINGLE(R_SUBCLECTL, FB_SUBCLECTL_LVLMODE, + ARRAY_SIZE(lvl_mode_txt), lvl_mode_txt); +static struct soc_enum const sub_cle_win_sel_enum = + SOC_ENUM_SINGLE(R_SUBCLECTL, FB_SUBCLECTL_WINSEL, + ARRAY_SIZE(win_sel_txt), win_sel_txt); + +/* R_SUBCOMPRAT PG 5 ADDR 0x24 */ +static struct soc_enum const sub_comp_rat_enum = + SOC_ENUM_SINGLE(R_SUBCOMPRAT, FB_SUBCOMPRAT_RATIO, + ARRAY_SIZE(comp_rat_txt), comp_rat_txt); + +/* R_SUBEXPRAT PG 5 ADDR 0x30 */ +static struct soc_enum const sub_exp_rat_enum = + SOC_ENUM_SINGLE(R_SUBEXPRAT, FB_SUBEXPRAT_RATIO, + ARRAY_SIZE(exp_rat_txt), exp_rat_txt); + +static int bytes_info_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct coeff_ram_ctl *ctl = + (struct coeff_ram_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} + +/* CH 0_1 Input Mux */ +static char const * const ch_0_1_mux_txt[] = {"DAI 1", "TDM 0_1"}; + +static struct soc_enum const ch_0_1_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(ch_0_1_mux_txt), ch_0_1_mux_txt); + +static struct snd_kcontrol_new const ch_0_1_mux_dapm_enum = + SOC_DAPM_ENUM("CH 0_1 Input Mux", ch_0_1_mux_enum); + +/* CH 2_3 Input Mux */ +static char const * const ch_2_3_mux_txt[] = {"DAI 2", "TDM 2_3"}; + +static struct soc_enum const ch_2_3_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(ch_2_3_mux_txt), ch_2_3_mux_txt); + +static struct snd_kcontrol_new const ch_2_3_mux_dapm_enum = + SOC_DAPM_ENUM("CH 2_3 Input Mux", ch_2_3_mux_enum); + +/* CH 4_5 Input Mux */ +static char const * const ch_4_5_mux_txt[] = {"DAI 3", "TDM 4_5"}; + +static struct soc_enum const ch_4_5_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(ch_4_5_mux_txt), ch_4_5_mux_txt); + +static struct snd_kcontrol_new const ch_4_5_mux_dapm_enum = + SOC_DAPM_ENUM("CH 4_5 Input Mux", ch_4_5_mux_enum); + +#define COEFF_RAM_CTL(xname, xcount, xaddr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = bytes_info_ext, \ + .get = coeff_ram_get, .put = coeff_ram_put, \ + .private_value = (unsigned long)&(struct coeff_ram_ctl) { \ + .addr = xaddr, \ + .bytes_ext = {.max = xcount, }, \ + } \ +} + +static struct snd_kcontrol_new const tscs454_snd_controls[] = { + /* R_PLLCTL PG 0 ADDR 0x15 */ + SOC_ENUM("PLL BCLK Input", bclk_sel_enum), + /* R_ISRC PG 0 ADDR 0x16 */ + SOC_ENUM("Internal Rate", isrc_br_enum), + SOC_ENUM("Internal Rate Multiple", isrc_bm_enum), + /* R_SCLKCTL PG 0 ADDR 0x18 */ + SOC_ENUM("ADC Modular Rate", adc_modular_rate_enum), + SOC_ENUM("DAC Modular Rate", dac_modular_rate_enum), + /* R_ASRC PG 0 ADDR 0x28 */ + SOC_SINGLE("ASRC Out High Bandwidth Switch", + R_ASRC, FB_ASRC_ASRCOBW, 1, 0), + SOC_SINGLE("ASRC In High Bandwidth Switch", + R_ASRC, FB_ASRC_ASRCIBW, 1, 0), + /* R_I2SIDCTL PG 0 ADDR 0x38 */ + SOC_ENUM("I2S 1 Data In Control", data_in_ctrl_enums[0]), + SOC_ENUM("I2S 2 Data In Control", data_in_ctrl_enums[1]), + SOC_ENUM("I2S 3 Data In Control", data_in_ctrl_enums[2]), + /* R_I2SODCTL PG 0 ADDR 0x39 */ + SOC_ENUM("I2S 1 Data Out Control", data_out_ctrl_enums[0]), + SOC_ENUM("I2S 2 Data Out Control", data_out_ctrl_enums[1]), + SOC_ENUM("I2S 3 Data Out Control", data_out_ctrl_enums[2]), + /* R_AUDIOMUX1 PG 0 ADDR 0x3A */ + SOC_ENUM("ASRC In", asrc_in_mux_enum), + /* R_AUDIOMUX2 PG 0 ADDR 0x3B */ + SOC_ENUM("ASRC Out", asrc_out_mux_enum), + /* R_HSDCTL1 PG 1 ADDR 0x01 */ + SOC_ENUM("Headphone Jack Type", hp_jack_type_enum), + SOC_ENUM("Headset Detection Polarity", hs_det_pol_enum), + SOC_SINGLE("Headphone Detection Switch", + R_HSDCTL1, FB_HSDCTL1_HPID_EN, 1, 0), + SOC_SINGLE("Headset OMTP/CTIA Switch", + R_HSDCTL1, FB_HSDCTL1_GBLHS_EN, 1, 0), + /* R_HSDCTL1 PG 1 ADDR 0x02 */ + SOC_ENUM("Headset Mic Bias Force", hs_mic_bias_force_enum), + SOC_SINGLE("Manual Mic Bias Switch", + R_HSDCTL2, FB_HSDCTL2_MB1MODE, 1, 0), + SOC_SINGLE("Ring/Sleeve Auto Switch", + R_HSDCTL2, FB_HSDCTL2_SWMODE, 1, 0), + SOC_ENUM("Manual Mode Plug Type", plug_type_force_enum), + /* R_CH0AIC PG 1 ADDR 0x06 */ + SOC_SINGLE_TLV("Input Boost Channel 0 Volume", R_CH0AIC, + FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr), + /* R_CH1AIC PG 1 ADDR 0x07 */ + SOC_SINGLE_TLV("Input Boost Channel 1 Volume", R_CH1AIC, + FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr), + /* R_CH2AIC PG 1 ADDR 0x08 */ + SOC_SINGLE_TLV("Input Boost Channel 2 Volume", R_CH2AIC, + FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr), + /* R_CH3AIC PG 1 ADDR 0x09 */ + SOC_SINGLE_TLV("Input Boost Channel 3 Volume", R_CH3AIC, + FB_CHAIC_MICBST, 0x3, 0, in_bst_vol_tlv_arr), + /* R_ICTL0 PG 1 ADDR 0x0A */ + SOC_ENUM("Input Channel 1 Polarity", in_pol_ch1_enum), + SOC_ENUM("Input Channel 0 Polarity", in_pol_ch0_enum), + SOC_ENUM("Input Processor Channel 0/1 Operation", + in_proc_ch01_sel_enum), + SOC_SINGLE("Input Channel 1 Mute Switch", + R_ICTL0, FB_ICTL0_IN1MUTE, 1, 0), + SOC_SINGLE("Input Channel 0 Mute Switch", + R_ICTL0, FB_ICTL0_IN0MUTE, 1, 0), + SOC_SINGLE("Input Channel 1 HPF Disable Switch", + R_ICTL0, FB_ICTL0_IN1HP, 1, 0), + SOC_SINGLE("Input Channel 0 HPF Disable Switch", + R_ICTL0, FB_ICTL0_IN0HP, 1, 0), + /* R_ICTL1 PG 1 ADDR 0x0B */ + SOC_ENUM("Input Channel 3 Polarity", in_pol_ch3_enum), + SOC_ENUM("Input Channel 2 Polarity", in_pol_ch2_enum), + SOC_ENUM("Input Processor Channel 2/3 Operation", + in_proc_ch23_sel_enum), + SOC_SINGLE("Input Channel 3 Mute Switch", + R_ICTL1, FB_ICTL1_IN3MUTE, 1, 0), + SOC_SINGLE("Input Channel 2 Mute Switch", + R_ICTL1, FB_ICTL1_IN2MUTE, 1, 0), + SOC_SINGLE("Input Channel 3 HPF Disable Switch", + R_ICTL1, FB_ICTL1_IN3HP, 1, 0), + SOC_SINGLE("Input Channel 2 HPF Disable Switch", + R_ICTL1, FB_ICTL1_IN2HP, 1, 0), + /* R_MICBIAS PG 1 ADDR 0x0C */ + SOC_ENUM("Mic Bias 2 Voltage", mic_bias_2_enum), + SOC_ENUM("Mic Bias 1 Voltage", mic_bias_1_enum), + /* R_PGACTL0 PG 1 ADDR 0x0D */ + SOC_SINGLE("Input Channel 0 PGA Mute Switch", + R_PGACTL0, FB_PGACTL_PGAMUTE, 1, 0), + SOC_SINGLE_TLV("Input Channel 0 PGA Volume", R_PGACTL0, + FB_PGACTL_PGAVOL, + FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr), + /* R_PGACTL1 PG 1 ADDR 0x0E */ + SOC_SINGLE("Input Channel 1 PGA Mute Switch", + R_PGACTL1, FB_PGACTL_PGAMUTE, 1, 0), + SOC_SINGLE_TLV("Input Channel 1 PGA Volume", R_PGACTL1, + FB_PGACTL_PGAVOL, + FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr), + /* R_PGACTL2 PG 1 ADDR 0x0F */ + SOC_SINGLE("Input Channel 2 PGA Mute Switch", + R_PGACTL2, FB_PGACTL_PGAMUTE, 1, 0), + SOC_SINGLE_TLV("Input Channel 2 PGA Volume", R_PGACTL2, + FB_PGACTL_PGAVOL, + FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr), + /* R_PGACTL3 PG 1 ADDR 0x10 */ + SOC_SINGLE("Input Channel 3 PGA Mute Switch", + R_PGACTL3, FB_PGACTL_PGAMUTE, 1, 0), + SOC_SINGLE_TLV("Input Channel 3 PGA Volume", R_PGACTL3, + FB_PGACTL_PGAVOL, + FM_PGACTL_PGAVOL, 0, in_pga_vol_tlv_arr), + /* R_ICH0VOL PG 1 ADDR 0x12 */ + SOC_SINGLE_TLV("Input Channel 0 Volume", R_ICH0VOL, + FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr), + /* R_ICH1VOL PG 1 ADDR 0x13 */ + SOC_SINGLE_TLV("Input Channel 1 Volume", R_ICH1VOL, + FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr), + /* R_ICH2VOL PG 1 ADDR 0x14 */ + SOC_SINGLE_TLV("Input Channel 2 Volume", R_ICH2VOL, + FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr), + /* R_ICH3VOL PG 1 ADDR 0x15 */ + SOC_SINGLE_TLV("Input Channel 3 Volume", R_ICH3VOL, + FB_ICHVOL_ICHVOL, FM_ICHVOL_ICHVOL, 0, in_vol_tlv_arr), + /* R_ASRCILVOL PG 1 ADDR 0x16 */ + SOC_SINGLE_TLV("ASRC Input Left Volume", R_ASRCILVOL, + FB_ASRCILVOL_ASRCILVOL, FM_ASRCILVOL_ASRCILVOL, + 0, asrc_vol_tlv_arr), + /* R_ASRCIRVOL PG 1 ADDR 0x17 */ + SOC_SINGLE_TLV("ASRC Input Right Volume", R_ASRCIRVOL, + FB_ASRCIRVOL_ASRCIRVOL, FM_ASRCIRVOL_ASRCIRVOL, + 0, asrc_vol_tlv_arr), + /* R_ASRCOLVOL PG 1 ADDR 0x18 */ + SOC_SINGLE_TLV("ASRC Output Left Volume", R_ASRCOLVOL, + FB_ASRCOLVOL_ASRCOLVOL, FM_ASRCOLVOL_ASRCOLVOL, + 0, asrc_vol_tlv_arr), + /* R_ASRCORVOL PG 1 ADDR 0x19 */ + SOC_SINGLE_TLV("ASRC Output Right Volume", R_ASRCORVOL, + FB_ASRCORVOL_ASRCOLVOL, FM_ASRCORVOL_ASRCOLVOL, + 0, asrc_vol_tlv_arr), + /* R_IVOLCTLU PG 1 ADDR 0x1C */ + /* R_ALCCTL0 PG 1 ADDR 0x1D */ + SOC_ENUM("ALC Mode", alc_mode_enum), + SOC_ENUM("ALC Reference", alc_ref_enum), + SOC_SINGLE("Input Channel 3 ALC Switch", + R_ALCCTL0, FB_ALCCTL0_ALCEN3, 1, 0), + SOC_SINGLE("Input Channel 2 ALC Switch", + R_ALCCTL0, FB_ALCCTL0_ALCEN2, 1, 0), + SOC_SINGLE("Input Channel 1 ALC Switch", + R_ALCCTL0, FB_ALCCTL0_ALCEN1, 1, 0), + SOC_SINGLE("Input Channel 0 ALC Switch", + R_ALCCTL0, FB_ALCCTL0_ALCEN0, 1, 0), + /* R_ALCCTL1 PG 1 ADDR 0x1E */ + SOC_SINGLE_TLV("ALC Max Gain Volume", R_ALCCTL1, + FB_ALCCTL1_MAXGAIN, FM_ALCCTL1_MAXGAIN, + 0, alc_max_gain_tlv_arr), + SOC_SINGLE_TLV("ALC Target Volume", R_ALCCTL1, + FB_ALCCTL1_ALCL, FM_ALCCTL1_ALCL, + 0, alc_target_tlv_arr), + /* R_ALCCTL2 PG 1 ADDR 0x1F */ + SOC_SINGLE("ALC Zero Cross Switch", + R_ALCCTL2, FB_ALCCTL2_ALCZC, 1, 0), + SOC_SINGLE_TLV("ALC Min Gain Volume", R_ALCCTL2, + FB_ALCCTL2_MINGAIN, FM_ALCCTL2_MINGAIN, + 0, alc_min_gain_tlv_arr), + SOC_SINGLE_RANGE("ALC Hold", R_ALCCTL2, + FB_ALCCTL2_HLD, 0, FM_ALCCTL2_HLD, 0), + /* R_ALCCTL3 PG 1 ADDR 0x20 */ + SOC_SINGLE_RANGE("ALC Decay", R_ALCCTL3, + FB_ALCCTL3_DCY, 0, FM_ALCCTL3_DCY, 0), + SOC_SINGLE_RANGE("ALC Attack", R_ALCCTL3, + FB_ALCCTL3_ATK, 0, FM_ALCCTL3_ATK, 0), + /* R_NGATE PG 1 ADDR 0x21 */ + SOC_SINGLE_TLV("Noise Gate Threshold Volume", R_NGATE, + FB_NGATE_NGTH, FM_NGATE_NGTH, 0, ngth_tlv_arr), + SOC_ENUM("Noise Gate Type", ngate_type_enum), + SOC_SINGLE("Noise Gate Switch", R_NGATE, FB_NGATE_NGAT, 1, 0), + /* R_DMICCTL PG 1 ADDR 0x22 */ + SOC_SINGLE("Digital Mic 2 Switch", R_DMICCTL, FB_DMICCTL_DMIC2EN, 1, 0), + SOC_SINGLE("Digital Mic 1 Switch", R_DMICCTL, FB_DMICCTL_DMIC1EN, 1, 0), + SOC_ENUM("Digital Mic Mono Select", dmic_mono_sel_enum), + /* R_DACCTL PG 2 ADDR 0x01 */ + SOC_ENUM("DAC Polarity Left", dac_pol_r_enum), + SOC_ENUM("DAC Polarity Right", dac_pol_l_enum), + SOC_ENUM("DAC Dither", dac_dith_enum), + SOC_SINGLE("DAC Mute Switch", R_DACCTL, FB_DACCTL_DACMUTE, 1, 0), + SOC_SINGLE("DAC De-Emphasis Switch", R_DACCTL, FB_DACCTL_DACDEM, 1, 0), + /* R_SPKCTL PG 2 ADDR 0x02 */ + SOC_ENUM("Speaker Polarity Right", spk_pol_r_enum), + SOC_ENUM("Speaker Polarity Left", spk_pol_l_enum), + SOC_SINGLE("Speaker Mute Switch", R_SPKCTL, FB_SPKCTL_SPKMUTE, 1, 0), + SOC_SINGLE("Speaker De-Emphasis Switch", + R_SPKCTL, FB_SPKCTL_SPKDEM, 1, 0), + /* R_SUBCTL PG 2 ADDR 0x03 */ + SOC_ENUM("Sub Polarity", sub_pol_enum), + SOC_SINGLE("SUB Mute Switch", R_SUBCTL, FB_SUBCTL_SUBMUTE, 1, 0), + SOC_SINGLE("Sub De-Emphasis Switch", R_SUBCTL, FB_SUBCTL_SUBDEM, 1, 0), + /* R_DCCTL PG 2 ADDR 0x04 */ + SOC_SINGLE("Sub DC Removal Switch", R_DCCTL, FB_DCCTL_SUBDCBYP, 1, 1), + SOC_SINGLE("DAC DC Removal Switch", R_DCCTL, FB_DCCTL_DACDCBYP, 1, 1), + SOC_SINGLE("Speaker DC Removal Switch", + R_DCCTL, FB_DCCTL_SPKDCBYP, 1, 1), + SOC_SINGLE("DC Removal Coefficient Switch", R_DCCTL, FB_DCCTL_DCCOEFSEL, + FM_DCCTL_DCCOEFSEL, 0), + /* R_OVOLCTLU PG 2 ADDR 0x06 */ + SOC_SINGLE("Output Fade Switch", R_OVOLCTLU, FB_OVOLCTLU_OFADE, 1, 0), + /* R_MVOLL PG 2 ADDR 0x08 */ + /* R_MVOLR PG 2 ADDR 0x09 */ + SOC_DOUBLE_R_TLV("Master Volume", R_MVOLL, R_MVOLR, + FB_MVOLL_MVOL_L, FM_MVOLL_MVOL_L, 0, mvol_tlv_arr), + /* R_HPVOLL PG 2 ADDR 0x0A */ + /* R_HPVOLR PG 2 ADDR 0x0B */ + SOC_DOUBLE_R_TLV("Headphone Volume", R_HPVOLL, R_HPVOLR, + FB_HPVOLL_HPVOL_L, FM_HPVOLL_HPVOL_L, 0, + hp_vol_tlv_arr), + /* R_SPKVOLL PG 2 ADDR 0x0C */ + /* R_SPKVOLR PG 2 ADDR 0x0D */ + SOC_DOUBLE_R_TLV("Speaker Volume", R_SPKVOLL, R_SPKVOLR, + FB_SPKVOLL_SPKVOL_L, FM_SPKVOLL_SPKVOL_L, 0, + spk_vol_tlv_arr), + /* R_SUBVOL PG 2 ADDR 0x10 */ + SOC_SINGLE_TLV("Sub Volume", R_SUBVOL, + FB_SUBVOL_SUBVOL, FM_SUBVOL_SUBVOL, 0, spk_vol_tlv_arr), + /* R_SPKEQFILT PG 3 ADDR 0x01 */ + SOC_SINGLE("Speaker EQ 2 Switch", + R_SPKEQFILT, FB_SPKEQFILT_EQ2EN, 1, 0), + SOC_ENUM("Speaker EQ 2 Band", spk_eq_enums[0]), + SOC_SINGLE("Speaker EQ 1 Switch", + R_SPKEQFILT, FB_SPKEQFILT_EQ1EN, 1, 0), + SOC_ENUM("Speaker EQ 1 Band", spk_eq_enums[1]), + /* R_SPKMBCEN PG 3 ADDR 0x0A */ + SOC_SINGLE("Speaker MBC 3 Switch", + R_SPKMBCEN, FB_SPKMBCEN_MBCEN3, 1, 0), + SOC_SINGLE("Speaker MBC 2 Switch", + R_SPKMBCEN, FB_SPKMBCEN_MBCEN2, 1, 0), + SOC_SINGLE("Speaker MBC 1 Switch", + R_SPKMBCEN, FB_SPKMBCEN_MBCEN1, 1, 0), + /* R_SPKMBCCTL PG 3 ADDR 0x0B */ + SOC_ENUM("Speaker MBC 3 Mode", spk_mbc3_lvl_det_mode_enum), + SOC_ENUM("Speaker MBC 3 Window", spk_mbc3_win_sel_enum), + SOC_ENUM("Speaker MBC 2 Mode", spk_mbc2_lvl_det_mode_enum), + SOC_ENUM("Speaker MBC 2 Window", spk_mbc2_win_sel_enum), + SOC_ENUM("Speaker MBC 1 Mode", spk_mbc1_lvl_det_mode_enum), + SOC_ENUM("Speaker MBC 1 Window", spk_mbc1_win_sel_enum), + /* R_SPKMBCMUG1 PG 3 ADDR 0x0C */ + SOC_ENUM("Speaker MBC 1 Phase Polarity", spk_mbc1_phase_pol_enum), + SOC_SINGLE_TLV("Speaker MBC1 Make-Up Gain Volume", R_SPKMBCMUG1, + FB_SPKMBCMUG_MUGAIN, FM_SPKMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SPKMBCTHR1 PG 3 ADDR 0x0D */ + SOC_SINGLE_TLV("Speaker MBC 1 Compressor Threshold Volume", + R_SPKMBCTHR1, FB_SPKMBCTHR_THRESH, FM_SPKMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKMBCRAT1 PG 3 ADDR 0x0E */ + SOC_ENUM("Speaker MBC 1 Compressor Ratio", spk_mbc1_comp_rat_enum), + /* R_SPKMBCATK1L PG 3 ADDR 0x0F */ + /* R_SPKMBCATK1H PG 3 ADDR 0x10 */ + SND_SOC_BYTES("Speaker MBC 1 Attack", R_SPKMBCATK1L, 2), + /* R_SPKMBCREL1L PG 3 ADDR 0x11 */ + /* R_SPKMBCREL1H PG 3 ADDR 0x12 */ + SND_SOC_BYTES("Speaker MBC 1 Release", R_SPKMBCREL1L, 2), + /* R_SPKMBCMUG2 PG 3 ADDR 0x13 */ + SOC_ENUM("Speaker MBC 2 Phase Polarity", spk_mbc2_phase_pol_enum), + SOC_SINGLE_TLV("Speaker MBC2 Make-Up Gain Volume", R_SPKMBCMUG2, + FB_SPKMBCMUG_MUGAIN, FM_SPKMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SPKMBCTHR2 PG 3 ADDR 0x14 */ + SOC_SINGLE_TLV("Speaker MBC 2 Compressor Threshold Volume", + R_SPKMBCTHR2, FB_SPKMBCTHR_THRESH, FM_SPKMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKMBCRAT2 PG 3 ADDR 0x15 */ + SOC_ENUM("Speaker MBC 2 Compressor Ratio", spk_mbc2_comp_rat_enum), + /* R_SPKMBCATK2L PG 3 ADDR 0x16 */ + /* R_SPKMBCATK2H PG 3 ADDR 0x17 */ + SND_SOC_BYTES("Speaker MBC 2 Attack", R_SPKMBCATK2L, 2), + /* R_SPKMBCREL2L PG 3 ADDR 0x18 */ + /* R_SPKMBCREL2H PG 3 ADDR 0x19 */ + SND_SOC_BYTES("Speaker MBC 2 Release", R_SPKMBCREL2L, 2), + /* R_SPKMBCMUG3 PG 3 ADDR 0x1A */ + SOC_ENUM("Speaker MBC 3 Phase Polarity", spk_mbc3_phase_pol_enum), + SOC_SINGLE_TLV("Speaker MBC 3 Make-Up Gain Volume", R_SPKMBCMUG3, + FB_SPKMBCMUG_MUGAIN, FM_SPKMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SPKMBCTHR3 PG 3 ADDR 0x1B */ + SOC_SINGLE_TLV("Speaker MBC 3 Threshold Volume", R_SPKMBCTHR3, + FB_SPKMBCTHR_THRESH, FM_SPKMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKMBCRAT3 PG 3 ADDR 0x1C */ + SOC_ENUM("Speaker MBC 3 Compressor Ratio", spk_mbc3_comp_rat_enum), + /* R_SPKMBCATK3L PG 3 ADDR 0x1D */ + /* R_SPKMBCATK3H PG 3 ADDR 0x1E */ + SND_SOC_BYTES("Speaker MBC 3 Attack", R_SPKMBCATK3L, 3), + /* R_SPKMBCREL3L PG 3 ADDR 0x1F */ + /* R_SPKMBCREL3H PG 3 ADDR 0x20 */ + SND_SOC_BYTES("Speaker MBC 3 Release", R_SPKMBCREL3L, 3), + /* R_SPKCLECTL PG 3 ADDR 0x21 */ + SOC_ENUM("Speaker CLE Level Mode", spk_cle_lvl_mode_enum), + SOC_ENUM("Speaker CLE Window", spk_cle_win_sel_enum), + SOC_SINGLE("Speaker CLE Expander Switch", + R_SPKCLECTL, FB_SPKCLECTL_EXPEN, 1, 0), + SOC_SINGLE("Speaker CLE Limiter Switch", + R_SPKCLECTL, FB_SPKCLECTL_LIMEN, 1, 0), + SOC_SINGLE("Speaker CLE Compressor Switch", + R_SPKCLECTL, FB_SPKCLECTL_COMPEN, 1, 0), + /* R_SPKCLEMUG PG 3 ADDR 0x22 */ + SOC_SINGLE_TLV("Speaker CLE Make-Up Gain Volume", R_SPKCLEMUG, + FB_SPKCLEMUG_MUGAIN, FM_SPKCLEMUG_MUGAIN, + 0, cle_mug_tlv_arr), + /* R_SPKCOMPTHR PG 3 ADDR 0x23 */ + SOC_SINGLE_TLV("Speaker Compressor Threshold Volume", R_SPKCOMPTHR, + FB_SPKCOMPTHR_THRESH, FM_SPKCOMPTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKCOMPRAT PG 3 ADDR 0x24 */ + SOC_ENUM("Speaker Compressor Ratio", spk_comp_rat_enum), + /* R_SPKCOMPATKL PG 3 ADDR 0x25 */ + /* R_SPKCOMPATKH PG 3 ADDR 0x26 */ + SND_SOC_BYTES("Speaker Compressor Attack", R_SPKCOMPATKL, 2), + /* R_SPKCOMPRELL PG 3 ADDR 0x27 */ + /* R_SPKCOMPRELH PG 3 ADDR 0x28 */ + SND_SOC_BYTES("Speaker Compressor Release", R_SPKCOMPRELL, 2), + /* R_SPKLIMTHR PG 3 ADDR 0x29 */ + SOC_SINGLE_TLV("Speaker Limiter Threshold Volume", R_SPKLIMTHR, + FB_SPKLIMTHR_THRESH, FM_SPKLIMTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKLIMTGT PG 3 ADDR 0x2A */ + SOC_SINGLE_TLV("Speaker Limiter Target Volume", R_SPKLIMTGT, + FB_SPKLIMTGT_TARGET, FM_SPKLIMTGT_TARGET, + 0, thr_tlv_arr), + /* R_SPKLIMATKL PG 3 ADDR 0x2B */ + /* R_SPKLIMATKH PG 3 ADDR 0x2C */ + SND_SOC_BYTES("Speaker Limiter Attack", R_SPKLIMATKL, 2), + /* R_SPKLIMRELL PG 3 ADDR 0x2D */ + /* R_SPKLIMRELR PG 3 ADDR 0x2E */ + SND_SOC_BYTES("Speaker Limiter Release", R_SPKLIMRELL, 2), + /* R_SPKEXPTHR PG 3 ADDR 0x2F */ + SOC_SINGLE_TLV("Speaker Expander Threshold Volume", R_SPKEXPTHR, + FB_SPKEXPTHR_THRESH, FM_SPKEXPTHR_THRESH, + 0, thr_tlv_arr), + /* R_SPKEXPRAT PG 3 ADDR 0x30 */ + SOC_ENUM("Speaker Expander Ratio", spk_exp_rat_enum), + /* R_SPKEXPATKL PG 3 ADDR 0x31 */ + /* R_SPKEXPATKR PG 3 ADDR 0x32 */ + SND_SOC_BYTES("Speaker Expander Attack", R_SPKEXPATKL, 2), + /* R_SPKEXPRELL PG 3 ADDR 0x33 */ + /* R_SPKEXPRELR PG 3 ADDR 0x34 */ + SND_SOC_BYTES("Speaker Expander Release", R_SPKEXPRELL, 2), + /* R_SPKFXCTL PG 3 ADDR 0x35 */ + SOC_SINGLE("Speaker 3D Switch", R_SPKFXCTL, FB_SPKFXCTL_3DEN, 1, 0), + SOC_SINGLE("Speaker Treble Enhancement Switch", + R_SPKFXCTL, FB_SPKFXCTL_TEEN, 1, 0), + SOC_SINGLE("Speaker Treble NLF Switch", + R_SPKFXCTL, FB_SPKFXCTL_TNLFBYP, 1, 1), + SOC_SINGLE("Speaker Bass Enhancement Switch", + R_SPKFXCTL, FB_SPKFXCTL_BEEN, 1, 0), + SOC_SINGLE("Speaker Bass NLF Switch", + R_SPKFXCTL, FB_SPKFXCTL_BNLFBYP, 1, 1), + /* R_DACEQFILT PG 4 ADDR 0x01 */ + SOC_SINGLE("DAC EQ 2 Switch", + R_DACEQFILT, FB_DACEQFILT_EQ2EN, 1, 0), + SOC_ENUM("DAC EQ 2 Band", dac_eq_enums[0]), + SOC_SINGLE("DAC EQ 1 Switch", R_DACEQFILT, FB_DACEQFILT_EQ1EN, 1, 0), + SOC_ENUM("DAC EQ 1 Band", dac_eq_enums[1]), + /* R_DACMBCEN PG 4 ADDR 0x0A */ + SOC_SINGLE("DAC MBC 3 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN3, 1, 0), + SOC_SINGLE("DAC MBC 2 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN2, 1, 0), + SOC_SINGLE("DAC MBC 1 Switch", R_DACMBCEN, FB_DACMBCEN_MBCEN1, 1, 0), + /* R_DACMBCCTL PG 4 ADDR 0x0B */ + SOC_ENUM("DAC MBC 3 Mode", dac_mbc3_lvl_det_mode_enum), + SOC_ENUM("DAC MBC 3 Window", dac_mbc3_win_sel_enum), + SOC_ENUM("DAC MBC 2 Mode", dac_mbc2_lvl_det_mode_enum), + SOC_ENUM("DAC MBC 2 Window", dac_mbc2_win_sel_enum), + SOC_ENUM("DAC MBC 1 Mode", dac_mbc1_lvl_det_mode_enum), + SOC_ENUM("DAC MBC 1 Window", dac_mbc1_win_sel_enum), + /* R_DACMBCMUG1 PG 4 ADDR 0x0C */ + SOC_ENUM("DAC MBC 1 Phase Polarity", dac_mbc1_phase_pol_enum), + SOC_SINGLE_TLV("DAC MBC 1 Make-Up Gain Volume", R_DACMBCMUG1, + FB_DACMBCMUG_MUGAIN, FM_DACMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_DACMBCTHR1 PG 4 ADDR 0x0D */ + SOC_SINGLE_TLV("DAC MBC 1 Compressor Threshold Volume", R_DACMBCTHR1, + FB_DACMBCTHR_THRESH, FM_DACMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACMBCRAT1 PG 4 ADDR 0x0E */ + SOC_ENUM("DAC MBC 1 Compressor Ratio", dac_mbc1_comp_rat_enum), + /* R_DACMBCATK1L PG 4 ADDR 0x0F */ + /* R_DACMBCATK1H PG 4 ADDR 0x10 */ + SND_SOC_BYTES("DAC MBC 1 Attack", R_DACMBCATK1L, 2), + /* R_DACMBCREL1L PG 4 ADDR 0x11 */ + /* R_DACMBCREL1H PG 4 ADDR 0x12 */ + SND_SOC_BYTES("DAC MBC 1 Release", R_DACMBCREL1L, 2), + /* R_DACMBCMUG2 PG 4 ADDR 0x13 */ + SOC_ENUM("DAC MBC 2 Phase Polarity", dac_mbc2_phase_pol_enum), + SOC_SINGLE_TLV("DAC MBC 2 Make-Up Gain Volume", R_DACMBCMUG2, + FB_DACMBCMUG_MUGAIN, FM_DACMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_DACMBCTHR2 PG 4 ADDR 0x14 */ + SOC_SINGLE_TLV("DAC MBC 2 Compressor Threshold Volume", R_DACMBCTHR2, + FB_DACMBCTHR_THRESH, FM_DACMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACMBCRAT2 PG 4 ADDR 0x15 */ + SOC_ENUM("DAC MBC 2 Compressor Ratio", dac_mbc2_comp_rat_enum), + /* R_DACMBCATK2L PG 4 ADDR 0x16 */ + /* R_DACMBCATK2H PG 4 ADDR 0x17 */ + SND_SOC_BYTES("DAC MBC 2 Attack", R_DACMBCATK2L, 2), + /* R_DACMBCREL2L PG 4 ADDR 0x18 */ + /* R_DACMBCREL2H PG 4 ADDR 0x19 */ + SND_SOC_BYTES("DAC MBC 2 Release", R_DACMBCREL2L, 2), + /* R_DACMBCMUG3 PG 4 ADDR 0x1A */ + SOC_ENUM("DAC MBC 3 Phase Polarity", dac_mbc3_phase_pol_enum), + SOC_SINGLE_TLV("DAC MBC 3 Make-Up Gain Volume", R_DACMBCMUG3, + FB_DACMBCMUG_MUGAIN, FM_DACMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_DACMBCTHR3 PG 4 ADDR 0x1B */ + SOC_SINGLE_TLV("DAC MBC 3 Threshold Volume", R_DACMBCTHR3, + FB_DACMBCTHR_THRESH, FM_DACMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACMBCRAT3 PG 4 ADDR 0x1C */ + SOC_ENUM("DAC MBC 3 Compressor Ratio", dac_mbc3_comp_rat_enum), + /* R_DACMBCATK3L PG 4 ADDR 0x1D */ + /* R_DACMBCATK3H PG 4 ADDR 0x1E */ + SND_SOC_BYTES("DAC MBC 3 Attack", R_DACMBCATK3L, 3), + /* R_DACMBCREL3L PG 4 ADDR 0x1F */ + /* R_DACMBCREL3H PG 4 ADDR 0x20 */ + SND_SOC_BYTES("DAC MBC 3 Release", R_DACMBCREL3L, 3), + /* R_DACCLECTL PG 4 ADDR 0x21 */ + SOC_ENUM("DAC CLE Level Mode", dac_cle_lvl_mode_enum), + SOC_ENUM("DAC CLE Window", dac_cle_win_sel_enum), + SOC_SINGLE("DAC CLE Expander Switch", + R_DACCLECTL, FB_DACCLECTL_EXPEN, 1, 0), + SOC_SINGLE("DAC CLE Limiter Switch", + R_DACCLECTL, FB_DACCLECTL_LIMEN, 1, 0), + SOC_SINGLE("DAC CLE Compressor Switch", + R_DACCLECTL, FB_DACCLECTL_COMPEN, 1, 0), + /* R_DACCLEMUG PG 4 ADDR 0x22 */ + SOC_SINGLE_TLV("DAC CLE Make-Up Gain Volume", R_DACCLEMUG, + FB_DACCLEMUG_MUGAIN, FM_DACCLEMUG_MUGAIN, + 0, cle_mug_tlv_arr), + /* R_DACCOMPTHR PG 4 ADDR 0x23 */ + SOC_SINGLE_TLV("DAC Compressor Threshold Volume", R_DACCOMPTHR, + FB_DACCOMPTHR_THRESH, FM_DACCOMPTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACCOMPRAT PG 4 ADDR 0x24 */ + SOC_ENUM("DAC Compressor Ratio", dac_comp_rat_enum), + /* R_DACCOMPATKL PG 4 ADDR 0x25 */ + /* R_DACCOMPATKH PG 4 ADDR 0x26 */ + SND_SOC_BYTES("DAC Compressor Attack", R_DACCOMPATKL, 2), + /* R_DACCOMPRELL PG 4 ADDR 0x27 */ + /* R_DACCOMPRELH PG 4 ADDR 0x28 */ + SND_SOC_BYTES("DAC Compressor Release", R_DACCOMPRELL, 2), + /* R_DACLIMTHR PG 4 ADDR 0x29 */ + SOC_SINGLE_TLV("DAC Limiter Threshold Volume", R_DACLIMTHR, + FB_DACLIMTHR_THRESH, FM_DACLIMTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACLIMTGT PG 4 ADDR 0x2A */ + SOC_SINGLE_TLV("DAC Limiter Target Volume", R_DACLIMTGT, + FB_DACLIMTGT_TARGET, FM_DACLIMTGT_TARGET, + 0, thr_tlv_arr), + /* R_DACLIMATKL PG 4 ADDR 0x2B */ + /* R_DACLIMATKH PG 4 ADDR 0x2C */ + SND_SOC_BYTES("DAC Limiter Attack", R_DACLIMATKL, 2), + /* R_DACLIMRELL PG 4 ADDR 0x2D */ + /* R_DACLIMRELR PG 4 ADDR 0x2E */ + SND_SOC_BYTES("DAC Limiter Release", R_DACLIMRELL, 2), + /* R_DACEXPTHR PG 4 ADDR 0x2F */ + SOC_SINGLE_TLV("DAC Expander Threshold Volume", R_DACEXPTHR, + FB_DACEXPTHR_THRESH, FM_DACEXPTHR_THRESH, + 0, thr_tlv_arr), + /* R_DACEXPRAT PG 4 ADDR 0x30 */ + SOC_ENUM("DAC Expander Ratio", dac_exp_rat_enum), + /* R_DACEXPATKL PG 4 ADDR 0x31 */ + /* R_DACEXPATKR PG 4 ADDR 0x32 */ + SND_SOC_BYTES("DAC Expander Attack", R_DACEXPATKL, 2), + /* R_DACEXPRELL PG 4 ADDR 0x33 */ + /* R_DACEXPRELR PG 4 ADDR 0x34 */ + SND_SOC_BYTES("DAC Expander Release", R_DACEXPRELL, 2), + /* R_DACFXCTL PG 4 ADDR 0x35 */ + SOC_SINGLE("DAC 3D Switch", R_DACFXCTL, FB_DACFXCTL_3DEN, 1, 0), + SOC_SINGLE("DAC Treble Enhancement Switch", + R_DACFXCTL, FB_DACFXCTL_TEEN, 1, 0), + SOC_SINGLE("DAC Treble NLF Switch", + R_DACFXCTL, FB_DACFXCTL_TNLFBYP, 1, 1), + SOC_SINGLE("DAC Bass Enhancement Switch", + R_DACFXCTL, FB_DACFXCTL_BEEN, 1, 0), + SOC_SINGLE("DAC Bass NLF Switch", + R_DACFXCTL, FB_DACFXCTL_BNLFBYP, 1, 1), + /* R_SUBEQFILT PG 5 ADDR 0x01 */ + SOC_SINGLE("Sub EQ 2 Switch", + R_SUBEQFILT, FB_SUBEQFILT_EQ2EN, 1, 0), + SOC_ENUM("Sub EQ 2 Band", sub_eq_enums[0]), + SOC_SINGLE("Sub EQ 1 Switch", R_SUBEQFILT, FB_SUBEQFILT_EQ1EN, 1, 0), + SOC_ENUM("Sub EQ 1 Band", sub_eq_enums[1]), + /* R_SUBMBCEN PG 5 ADDR 0x0A */ + SOC_SINGLE("Sub MBC 3 Switch", R_SUBMBCEN, FB_SUBMBCEN_MBCEN3, 1, 0), + SOC_SINGLE("Sub MBC 2 Switch", R_SUBMBCEN, FB_SUBMBCEN_MBCEN2, 1, 0), + SOC_SINGLE("Sub MBC 1 Switch", R_SUBMBCEN, FB_SUBMBCEN_MBCEN1, 1, 0), + /* R_SUBMBCCTL PG 5 ADDR 0x0B */ + SOC_ENUM("Sub MBC 3 Mode", sub_mbc3_lvl_det_mode_enum), + SOC_ENUM("Sub MBC 3 Window", sub_mbc3_win_sel_enum), + SOC_ENUM("Sub MBC 2 Mode", sub_mbc2_lvl_det_mode_enum), + SOC_ENUM("Sub MBC 2 Window", sub_mbc2_win_sel_enum), + SOC_ENUM("Sub MBC 1 Mode", sub_mbc1_lvl_det_mode_enum), + SOC_ENUM("Sub MBC 1 Window", sub_mbc1_win_sel_enum), + /* R_SUBMBCMUG1 PG 5 ADDR 0x0C */ + SOC_ENUM("Sub MBC 1 Phase Polarity", sub_mbc1_phase_pol_enum), + SOC_SINGLE_TLV("Sub MBC 1 Make-Up Gain Volume", R_SUBMBCMUG1, + FB_SUBMBCMUG_MUGAIN, FM_SUBMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SUBMBCTHR1 PG 5 ADDR 0x0D */ + SOC_SINGLE_TLV("Sub MBC 1 Compressor Threshold Volume", R_SUBMBCTHR1, + FB_SUBMBCTHR_THRESH, FM_SUBMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBMBCRAT1 PG 5 ADDR 0x0E */ + SOC_ENUM("Sub MBC 1 Compressor Ratio", sub_mbc1_comp_rat_enum), + /* R_SUBMBCATK1L PG 5 ADDR 0x0F */ + /* R_SUBMBCATK1H PG 5 ADDR 0x10 */ + SND_SOC_BYTES("Sub MBC 1 Attack", R_SUBMBCATK1L, 2), + /* R_SUBMBCREL1L PG 5 ADDR 0x11 */ + /* R_SUBMBCREL1H PG 5 ADDR 0x12 */ + SND_SOC_BYTES("Sub MBC 1 Release", R_SUBMBCREL1L, 2), + /* R_SUBMBCMUG2 PG 5 ADDR 0x13 */ + SOC_ENUM("Sub MBC 2 Phase Polarity", sub_mbc2_phase_pol_enum), + SOC_SINGLE_TLV("Sub MBC 2 Make-Up Gain Volume", R_SUBMBCMUG2, + FB_SUBMBCMUG_MUGAIN, FM_SUBMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SUBMBCTHR2 PG 5 ADDR 0x14 */ + SOC_SINGLE_TLV("Sub MBC 2 Compressor Threshold Volume", R_SUBMBCTHR2, + FB_SUBMBCTHR_THRESH, FM_SUBMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBMBCRAT2 PG 5 ADDR 0x15 */ + SOC_ENUM("Sub MBC 2 Compressor Ratio", sub_mbc2_comp_rat_enum), + /* R_SUBMBCATK2L PG 5 ADDR 0x16 */ + /* R_SUBMBCATK2H PG 5 ADDR 0x17 */ + SND_SOC_BYTES("Sub MBC 2 Attack", R_SUBMBCATK2L, 2), + /* R_SUBMBCREL2L PG 5 ADDR 0x18 */ + /* R_SUBMBCREL2H PG 5 ADDR 0x19 */ + SND_SOC_BYTES("Sub MBC 2 Release", R_SUBMBCREL2L, 2), + /* R_SUBMBCMUG3 PG 5 ADDR 0x1A */ + SOC_ENUM("Sub MBC 3 Phase Polarity", sub_mbc3_phase_pol_enum), + SOC_SINGLE_TLV("Sub MBC 3 Make-Up Gain Volume", R_SUBMBCMUG3, + FB_SUBMBCMUG_MUGAIN, FM_SUBMBCMUG_MUGAIN, + 0, mbc_mug_tlv_arr), + /* R_SUBMBCTHR3 PG 5 ADDR 0x1B */ + SOC_SINGLE_TLV("Sub MBC 3 Threshold Volume", R_SUBMBCTHR3, + FB_SUBMBCTHR_THRESH, FM_SUBMBCTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBMBCRAT3 PG 5 ADDR 0x1C */ + SOC_ENUM("Sub MBC 3 Compressor Ratio", sub_mbc3_comp_rat_enum), + /* R_SUBMBCATK3L PG 5 ADDR 0x1D */ + /* R_SUBMBCATK3H PG 5 ADDR 0x1E */ + SND_SOC_BYTES("Sub MBC 3 Attack", R_SUBMBCATK3L, 3), + /* R_SUBMBCREL3L PG 5 ADDR 0x1F */ + /* R_SUBMBCREL3H PG 5 ADDR 0x20 */ + SND_SOC_BYTES("Sub MBC 3 Release", R_SUBMBCREL3L, 3), + /* R_SUBCLECTL PG 5 ADDR 0x21 */ + SOC_ENUM("Sub CLE Level Mode", sub_cle_lvl_mode_enum), + SOC_ENUM("Sub CLE Window", sub_cle_win_sel_enum), + SOC_SINGLE("Sub CLE Expander Switch", + R_SUBCLECTL, FB_SUBCLECTL_EXPEN, 1, 0), + SOC_SINGLE("Sub CLE Limiter Switch", + R_SUBCLECTL, FB_SUBCLECTL_LIMEN, 1, 0), + SOC_SINGLE("Sub CLE Compressor Switch", + R_SUBCLECTL, FB_SUBCLECTL_COMPEN, 1, 0), + /* R_SUBCLEMUG PG 5 ADDR 0x22 */ + SOC_SINGLE_TLV("Sub CLE Make-Up Gain Volume", R_SUBCLEMUG, + FB_SUBCLEMUG_MUGAIN, FM_SUBCLEMUG_MUGAIN, + 0, cle_mug_tlv_arr), + /* R_SUBCOMPTHR PG 5 ADDR 0x23 */ + SOC_SINGLE_TLV("Sub Compressor Threshold Volume", R_SUBCOMPTHR, + FB_SUBCOMPTHR_THRESH, FM_SUBCOMPTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBCOMPRAT PG 5 ADDR 0x24 */ + SOC_ENUM("Sub Compressor Ratio", sub_comp_rat_enum), + /* R_SUBCOMPATKL PG 5 ADDR 0x25 */ + /* R_SUBCOMPATKH PG 5 ADDR 0x26 */ + SND_SOC_BYTES("Sub Compressor Attack", R_SUBCOMPATKL, 2), + /* R_SUBCOMPRELL PG 5 ADDR 0x27 */ + /* R_SUBCOMPRELH PG 5 ADDR 0x28 */ + SND_SOC_BYTES("Sub Compressor Release", R_SUBCOMPRELL, 2), + /* R_SUBLIMTHR PG 5 ADDR 0x29 */ + SOC_SINGLE_TLV("Sub Limiter Threshold Volume", R_SUBLIMTHR, + FB_SUBLIMTHR_THRESH, FM_SUBLIMTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBLIMTGT PG 5 ADDR 0x2A */ + SOC_SINGLE_TLV("Sub Limiter Target Volume", R_SUBLIMTGT, + FB_SUBLIMTGT_TARGET, FM_SUBLIMTGT_TARGET, + 0, thr_tlv_arr), + /* R_SUBLIMATKL PG 5 ADDR 0x2B */ + /* R_SUBLIMATKH PG 5 ADDR 0x2C */ + SND_SOC_BYTES("Sub Limiter Attack", R_SUBLIMATKL, 2), + /* R_SUBLIMRELL PG 5 ADDR 0x2D */ + /* R_SUBLIMRELR PG 5 ADDR 0x2E */ + SND_SOC_BYTES("Sub Limiter Release", R_SUBLIMRELL, 2), + /* R_SUBEXPTHR PG 5 ADDR 0x2F */ + SOC_SINGLE_TLV("Sub Expander Threshold Volume", R_SUBEXPTHR, + FB_SUBEXPTHR_THRESH, FM_SUBEXPTHR_THRESH, + 0, thr_tlv_arr), + /* R_SUBEXPRAT PG 5 ADDR 0x30 */ + SOC_ENUM("Sub Expander Ratio", sub_exp_rat_enum), + /* R_SUBEXPATKL PG 5 ADDR 0x31 */ + /* R_SUBEXPATKR PG 5 ADDR 0x32 */ + SND_SOC_BYTES("Sub Expander Attack", R_SUBEXPATKL, 2), + /* R_SUBEXPRELL PG 5 ADDR 0x33 */ + /* R_SUBEXPRELR PG 5 ADDR 0x34 */ + SND_SOC_BYTES("Sub Expander Release", R_SUBEXPRELL, 2), + /* R_SUBFXCTL PG 5 ADDR 0x35 */ + SOC_SINGLE("Sub Treble Enhancement Switch", + R_SUBFXCTL, FB_SUBFXCTL_TEEN, 1, 0), + SOC_SINGLE("Sub Treble NLF Switch", + R_SUBFXCTL, FB_SUBFXCTL_TNLFBYP, 1, 1), + SOC_SINGLE("Sub Bass Enhancement Switch", + R_SUBFXCTL, FB_SUBFXCTL_BEEN, 1, 0), + SOC_SINGLE("Sub Bass NLF Switch", + R_SUBFXCTL, FB_SUBFXCTL_BNLFBYP, 1, 1), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 1", BIQUAD_SIZE, 0x00), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 2", BIQUAD_SIZE, 0x05), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 3", BIQUAD_SIZE, 0x0a), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 4", BIQUAD_SIZE, 0x0f), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 5", BIQUAD_SIZE, 0x14), + COEFF_RAM_CTL("DAC Cascade 1 Left BiQuad 6", BIQUAD_SIZE, 0x19), + + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 1", BIQUAD_SIZE, 0x20), + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 2", BIQUAD_SIZE, 0x25), + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 3", BIQUAD_SIZE, 0x2a), + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 4", BIQUAD_SIZE, 0x2f), + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 5", BIQUAD_SIZE, 0x34), + COEFF_RAM_CTL("DAC Cascade 1 Right BiQuad 6", BIQUAD_SIZE, 0x39), + + COEFF_RAM_CTL("DAC Cascade 1 Left Prescale", COEFF_SIZE, 0x1f), + COEFF_RAM_CTL("DAC Cascade 1 Right Prescale", COEFF_SIZE, 0x3f), + + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 1", BIQUAD_SIZE, 0x40), + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 2", BIQUAD_SIZE, 0x45), + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 3", BIQUAD_SIZE, 0x4a), + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 4", BIQUAD_SIZE, 0x4f), + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 5", BIQUAD_SIZE, 0x54), + COEFF_RAM_CTL("DAC Cascade 2 Left BiQuad 6", BIQUAD_SIZE, 0x59), + + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 1", BIQUAD_SIZE, 0x60), + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 2", BIQUAD_SIZE, 0x65), + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 3", BIQUAD_SIZE, 0x6a), + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 4", BIQUAD_SIZE, 0x6f), + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 5", BIQUAD_SIZE, 0x74), + COEFF_RAM_CTL("DAC Cascade 2 Right BiQuad 6", BIQUAD_SIZE, 0x79), + + COEFF_RAM_CTL("DAC Cascade 2 Left Prescale", COEFF_SIZE, 0x5f), + COEFF_RAM_CTL("DAC Cascade 2 Right Prescale", COEFF_SIZE, 0x7f), + + COEFF_RAM_CTL("DAC Bass Extraction BiQuad 1", BIQUAD_SIZE, 0x80), + COEFF_RAM_CTL("DAC Bass Extraction BiQuad 2", BIQUAD_SIZE, 0x85), + + COEFF_RAM_CTL("DAC Bass Non Linear Function 1", COEFF_SIZE, 0x8a), + COEFF_RAM_CTL("DAC Bass Non Linear Function 2", COEFF_SIZE, 0x8b), + + COEFF_RAM_CTL("DAC Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c), + + COEFF_RAM_CTL("DAC Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91), + + COEFF_RAM_CTL("DAC Bass Mix", COEFF_SIZE, 0x96), + + COEFF_RAM_CTL("DAC Treb Extraction BiQuad 1", BIQUAD_SIZE, 0x97), + COEFF_RAM_CTL("DAC Treb Extraction BiQuad 2", BIQUAD_SIZE, 0x9c), + + COEFF_RAM_CTL("DAC Treb Non Linear Function 1", COEFF_SIZE, 0xa1), + COEFF_RAM_CTL("DAC Treb Non Linear Function 2", COEFF_SIZE, 0xa2), + + COEFF_RAM_CTL("DAC Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3), + + COEFF_RAM_CTL("DAC Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8), + + COEFF_RAM_CTL("DAC Treb Mix", COEFF_SIZE, 0xad), + + COEFF_RAM_CTL("DAC 3D", COEFF_SIZE, 0xae), + + COEFF_RAM_CTL("DAC 3D Mix", COEFF_SIZE, 0xaf), + + COEFF_RAM_CTL("DAC MBC 1 BiQuad 1", BIQUAD_SIZE, 0xb0), + COEFF_RAM_CTL("DAC MBC 1 BiQuad 2", BIQUAD_SIZE, 0xb5), + + COEFF_RAM_CTL("DAC MBC 2 BiQuad 1", BIQUAD_SIZE, 0xba), + COEFF_RAM_CTL("DAC MBC 2 BiQuad 2", BIQUAD_SIZE, 0xbf), + + COEFF_RAM_CTL("DAC MBC 3 BiQuad 1", BIQUAD_SIZE, 0xc4), + COEFF_RAM_CTL("DAC MBC 3 BiQuad 2", BIQUAD_SIZE, 0xc9), + + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 1", BIQUAD_SIZE, 0x00), + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 2", BIQUAD_SIZE, 0x05), + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 3", BIQUAD_SIZE, 0x0a), + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 4", BIQUAD_SIZE, 0x0f), + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 5", BIQUAD_SIZE, 0x14), + COEFF_RAM_CTL("Speaker Cascade 1 Left BiQuad 6", BIQUAD_SIZE, 0x19), + + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 1", BIQUAD_SIZE, 0x20), + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 2", BIQUAD_SIZE, 0x25), + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 3", BIQUAD_SIZE, 0x2a), + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 4", BIQUAD_SIZE, 0x2f), + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 5", BIQUAD_SIZE, 0x34), + COEFF_RAM_CTL("Speaker Cascade 1 Right BiQuad 6", BIQUAD_SIZE, 0x39), + + COEFF_RAM_CTL("Speaker Cascade 1 Left Prescale", COEFF_SIZE, 0x1f), + COEFF_RAM_CTL("Speaker Cascade 1 Right Prescale", COEFF_SIZE, 0x3f), + + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 1", BIQUAD_SIZE, 0x40), + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 2", BIQUAD_SIZE, 0x45), + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 3", BIQUAD_SIZE, 0x4a), + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 4", BIQUAD_SIZE, 0x4f), + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 5", BIQUAD_SIZE, 0x54), + COEFF_RAM_CTL("Speaker Cascade 2 Left BiQuad 6", BIQUAD_SIZE, 0x59), + + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 1", BIQUAD_SIZE, 0x60), + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 2", BIQUAD_SIZE, 0x65), + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 3", BIQUAD_SIZE, 0x6a), + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 4", BIQUAD_SIZE, 0x6f), + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 5", BIQUAD_SIZE, 0x74), + COEFF_RAM_CTL("Speaker Cascade 2 Right BiQuad 6", BIQUAD_SIZE, 0x79), + + COEFF_RAM_CTL("Speaker Cascade 2 Left Prescale", COEFF_SIZE, 0x5f), + COEFF_RAM_CTL("Speaker Cascade 2 Right Prescale", COEFF_SIZE, 0x7f), + + COEFF_RAM_CTL("Speaker Bass Extraction BiQuad 1", BIQUAD_SIZE, 0x80), + COEFF_RAM_CTL("Speaker Bass Extraction BiQuad 2", BIQUAD_SIZE, 0x85), + + COEFF_RAM_CTL("Speaker Bass Non Linear Function 1", COEFF_SIZE, 0x8a), + COEFF_RAM_CTL("Speaker Bass Non Linear Function 2", COEFF_SIZE, 0x8b), + + COEFF_RAM_CTL("Speaker Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c), + + COEFF_RAM_CTL("Speaker Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91), + + COEFF_RAM_CTL("Speaker Bass Mix", COEFF_SIZE, 0x96), + + COEFF_RAM_CTL("Speaker Treb Extraction BiQuad 1", BIQUAD_SIZE, 0x97), + COEFF_RAM_CTL("Speaker Treb Extraction BiQuad 2", BIQUAD_SIZE, 0x9c), + + COEFF_RAM_CTL("Speaker Treb Non Linear Function 1", COEFF_SIZE, 0xa1), + COEFF_RAM_CTL("Speaker Treb Non Linear Function 2", COEFF_SIZE, 0xa2), + + COEFF_RAM_CTL("Speaker Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3), + + COEFF_RAM_CTL("Speaker Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8), + + COEFF_RAM_CTL("Speaker Treb Mix", COEFF_SIZE, 0xad), + + COEFF_RAM_CTL("Speaker 3D", COEFF_SIZE, 0xae), + + COEFF_RAM_CTL("Speaker 3D Mix", COEFF_SIZE, 0xaf), + + COEFF_RAM_CTL("Speaker MBC 1 BiQuad 1", BIQUAD_SIZE, 0xb0), + COEFF_RAM_CTL("Speaker MBC 1 BiQuad 2", BIQUAD_SIZE, 0xb5), + + COEFF_RAM_CTL("Speaker MBC 2 BiQuad 1", BIQUAD_SIZE, 0xba), + COEFF_RAM_CTL("Speaker MBC 2 BiQuad 2", BIQUAD_SIZE, 0xbf), + + COEFF_RAM_CTL("Speaker MBC 3 BiQuad 1", BIQUAD_SIZE, 0xc4), + COEFF_RAM_CTL("Speaker MBC 3 BiQuad 2", BIQUAD_SIZE, 0xc9), + + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 1", BIQUAD_SIZE, 0x00), + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 2", BIQUAD_SIZE, 0x05), + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 3", BIQUAD_SIZE, 0x0a), + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 4", BIQUAD_SIZE, 0x0f), + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 5", BIQUAD_SIZE, 0x14), + COEFF_RAM_CTL("Sub Cascade 1 Left BiQuad 6", BIQUAD_SIZE, 0x19), + + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 1", BIQUAD_SIZE, 0x20), + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 2", BIQUAD_SIZE, 0x25), + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 3", BIQUAD_SIZE, 0x2a), + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 4", BIQUAD_SIZE, 0x2f), + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 5", BIQUAD_SIZE, 0x34), + COEFF_RAM_CTL("Sub Cascade 1 Right BiQuad 6", BIQUAD_SIZE, 0x39), + + COEFF_RAM_CTL("Sub Cascade 1 Left Prescale", COEFF_SIZE, 0x1f), + COEFF_RAM_CTL("Sub Cascade 1 Right Prescale", COEFF_SIZE, 0x3f), + + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 1", BIQUAD_SIZE, 0x40), + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 2", BIQUAD_SIZE, 0x45), + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 3", BIQUAD_SIZE, 0x4a), + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 4", BIQUAD_SIZE, 0x4f), + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 5", BIQUAD_SIZE, 0x54), + COEFF_RAM_CTL("Sub Cascade 2 Left BiQuad 6", BIQUAD_SIZE, 0x59), + + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 1", BIQUAD_SIZE, 0x60), + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 2", BIQUAD_SIZE, 0x65), + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 3", BIQUAD_SIZE, 0x6a), + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 4", BIQUAD_SIZE, 0x6f), + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 5", BIQUAD_SIZE, 0x74), + COEFF_RAM_CTL("Sub Cascade 2 Right BiQuad 6", BIQUAD_SIZE, 0x79), + + COEFF_RAM_CTL("Sub Cascade 2 Left Prescale", COEFF_SIZE, 0x5f), + COEFF_RAM_CTL("Sub Cascade 2 Right Prescale", COEFF_SIZE, 0x7f), + + COEFF_RAM_CTL("Sub Bass Extraction BiQuad 1", BIQUAD_SIZE, 0x80), + COEFF_RAM_CTL("Sub Bass Extraction BiQuad 2", BIQUAD_SIZE, 0x85), + + COEFF_RAM_CTL("Sub Bass Non Linear Function 1", COEFF_SIZE, 0x8a), + COEFF_RAM_CTL("Sub Bass Non Linear Function 2", COEFF_SIZE, 0x8b), + + COEFF_RAM_CTL("Sub Bass Limiter BiQuad", BIQUAD_SIZE, 0x8c), + + COEFF_RAM_CTL("Sub Bass Cut Off BiQuad", BIQUAD_SIZE, 0x91), + + COEFF_RAM_CTL("Sub Bass Mix", COEFF_SIZE, 0x96), + + COEFF_RAM_CTL("Sub Treb Extraction BiQuad 1", BIQUAD_SIZE, 0x97), + COEFF_RAM_CTL("Sub Treb Extraction BiQuad 2", BIQUAD_SIZE, 0x9c), + + COEFF_RAM_CTL("Sub Treb Non Linear Function 1", COEFF_SIZE, 0xa1), + COEFF_RAM_CTL("Sub Treb Non Linear Function 2", COEFF_SIZE, 0xa2), + + COEFF_RAM_CTL("Sub Treb Limiter BiQuad", BIQUAD_SIZE, 0xa3), + + COEFF_RAM_CTL("Sub Treb Cut Off BiQuad", BIQUAD_SIZE, 0xa8), + + COEFF_RAM_CTL("Sub Treb Mix", COEFF_SIZE, 0xad), + + COEFF_RAM_CTL("Sub 3D", COEFF_SIZE, 0xae), + + COEFF_RAM_CTL("Sub 3D Mix", COEFF_SIZE, 0xaf), + + COEFF_RAM_CTL("Sub MBC 1 BiQuad 1", BIQUAD_SIZE, 0xb0), + COEFF_RAM_CTL("Sub MBC 1 BiQuad 2", BIQUAD_SIZE, 0xb5), + + COEFF_RAM_CTL("Sub MBC 2 BiQuad 1", BIQUAD_SIZE, 0xba), + COEFF_RAM_CTL("Sub MBC 2 BiQuad 2", BIQUAD_SIZE, 0xbf), + + COEFF_RAM_CTL("Sub MBC 3 BiQuad 1", BIQUAD_SIZE, 0xc4), + COEFF_RAM_CTL("Sub MBC 3 BiQuad 2", BIQUAD_SIZE, 0xc9), +}; + +static struct snd_soc_dapm_widget const tscs454_dapm_widgets[] = { + /* R_PLLCTL PG 0 ADDR 0x15 */ + SND_SOC_DAPM_SUPPLY("PLL 1 Power", R_PLLCTL, FB_PLLCTL_PU_PLL1, 0, + pll_power_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PLL 2 Power", R_PLLCTL, FB_PLLCTL_PU_PLL2, 0, + pll_power_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD), + /* R_I2SPINC0 PG 0 ADDR 0x22 */ + SND_SOC_DAPM_AIF_OUT("DAI 3 Out", "DAI 3 Capture", 0, + R_I2SPINC0, FB_I2SPINC0_SDO3TRI, 1), + SND_SOC_DAPM_AIF_OUT("DAI 2 Out", "DAI 2 Capture", 0, + R_I2SPINC0, FB_I2SPINC0_SDO2TRI, 1), + SND_SOC_DAPM_AIF_OUT("DAI 1 Out", "DAI 1 Capture", 0, + R_I2SPINC0, FB_I2SPINC0_SDO1TRI, 1), + /* R_PWRM0 PG 0 ADDR 0x33 */ + SND_SOC_DAPM_ADC("Input Processor Channel 3", NULL, + R_PWRM0, FB_PWRM0_INPROC3PU, 0), + SND_SOC_DAPM_ADC("Input Processor Channel 2", NULL, + R_PWRM0, FB_PWRM0_INPROC2PU, 0), + SND_SOC_DAPM_ADC("Input Processor Channel 1", NULL, + R_PWRM0, FB_PWRM0_INPROC1PU, 0), + SND_SOC_DAPM_ADC("Input Processor Channel 0", NULL, + R_PWRM0, FB_PWRM0_INPROC0PU, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias 2", + R_PWRM0, FB_PWRM0_MICB2PU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias 1", R_PWRM0, + FB_PWRM0_MICB1PU, 0, NULL, 0), + /* R_PWRM1 PG 0 ADDR 0x34 */ + SND_SOC_DAPM_SUPPLY("Sub Power", R_PWRM1, FB_PWRM1_SUBPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Left Power", + R_PWRM1, FB_PWRM1_HPLPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Right Power", + R_PWRM1, FB_PWRM1_HPRPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Speaker Left Power", + R_PWRM1, FB_PWRM1_SPKLPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Speaker Right Power", + R_PWRM1, FB_PWRM1_SPKRPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Differential Input 2 Power", + R_PWRM1, FB_PWRM1_D2S2PU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Differential Input 1 Power", + R_PWRM1, FB_PWRM1_D2S1PU, 0, NULL, 0), + /* R_PWRM2 PG 0 ADDR 0x35 */ + SND_SOC_DAPM_SUPPLY("DAI 3 Out Power", + R_PWRM2, FB_PWRM2_I2S3OPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAI 2 Out Power", + R_PWRM2, FB_PWRM2_I2S2OPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAI 1 Out Power", + R_PWRM2, FB_PWRM2_I2S1OPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAI 3 In Power", + R_PWRM2, FB_PWRM2_I2S3IPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAI 2 In Power", + R_PWRM2, FB_PWRM2_I2S2IPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAI 1 In Power", + R_PWRM2, FB_PWRM2_I2S1IPU, 0, NULL, 0), + /* R_PWRM3 PG 0 ADDR 0x36 */ + SND_SOC_DAPM_SUPPLY("Line Out Left Power", + R_PWRM3, FB_PWRM3_LLINEPU, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Line Out Right Power", + R_PWRM3, FB_PWRM3_RLINEPU, 0, NULL, 0), + /* R_PWRM4 PG 0 ADDR 0x37 */ + SND_SOC_DAPM_DAC("Sub", NULL, R_PWRM4, FB_PWRM4_OPSUBPU, 0), + SND_SOC_DAPM_DAC("DAC Left", NULL, R_PWRM4, FB_PWRM4_OPDACLPU, 0), + SND_SOC_DAPM_DAC("DAC Right", NULL, R_PWRM4, FB_PWRM4_OPDACRPU, 0), + SND_SOC_DAPM_DAC("ClassD Left", NULL, R_PWRM4, FB_PWRM4_OPSPKLPU, 0), + SND_SOC_DAPM_DAC("ClassD Right", NULL, R_PWRM4, FB_PWRM4_OPSPKRPU, 0), + /* R_AUDIOMUX1 PG 0 ADDR 0x3A */ + SND_SOC_DAPM_MUX("DAI 2 Out Mux", SND_SOC_NOPM, 0, 0, + &dai2_mux_dapm_enum), + SND_SOC_DAPM_MUX("DAI 1 Out Mux", SND_SOC_NOPM, 0, 0, + &dai1_mux_dapm_enum), + /* R_AUDIOMUX2 PG 0 ADDR 0x3B */ + SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, + &dac_mux_dapm_enum), + SND_SOC_DAPM_MUX("DAI 3 Out Mux", SND_SOC_NOPM, 0, 0, + &dai3_mux_dapm_enum), + /* R_AUDIOMUX3 PG 0 ADDR 0x3C */ + SND_SOC_DAPM_MUX("Sub Mux", SND_SOC_NOPM, 0, 0, + &sub_mux_dapm_enum), + SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0, + &classd_mux_dapm_enum), + /* R_HSDCTL1 PG 1 ADDR 0x01 */ + SND_SOC_DAPM_SUPPLY("GHS Detect Power", R_HSDCTL1, + FB_HSDCTL1_CON_DET_PWD, 1, NULL, 0), + /* R_CH0AIC PG 1 ADDR 0x06 */ + SND_SOC_DAPM_MUX("Input Boost Channel 0 Mux", SND_SOC_NOPM, 0, 0, + &in_bst_mux_ch0_dapm_enum), + SND_SOC_DAPM_MUX("ADC Channel 0 Mux", SND_SOC_NOPM, 0, 0, + &adc_mux_ch0_dapm_enum), + SND_SOC_DAPM_MUX("Input Processor Channel 0 Mux", SND_SOC_NOPM, 0, 0, + &in_proc_mux_ch0_dapm_enum), + /* R_CH1AIC PG 1 ADDR 0x07 */ + SND_SOC_DAPM_MUX("Input Boost Channel 1 Mux", SND_SOC_NOPM, 0, 0, + &in_bst_mux_ch1_dapm_enum), + SND_SOC_DAPM_MUX("ADC Channel 1 Mux", SND_SOC_NOPM, 0, 0, + &adc_mux_ch1_dapm_enum), + SND_SOC_DAPM_MUX("Input Processor Channel 1 Mux", SND_SOC_NOPM, 0, 0, + &in_proc_mux_ch1_dapm_enum), + /* Virtual */ + SND_SOC_DAPM_AIF_IN("DAI 3 In", "DAI 3 Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DAI 2 In", "DAI 2 Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DAI 1 In", "DAI 1 Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("PLLs", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("Sub Out"), + SND_SOC_DAPM_OUTPUT("Headphone Left"), + SND_SOC_DAPM_OUTPUT("Headphone Right"), + SND_SOC_DAPM_OUTPUT("Speaker Left"), + SND_SOC_DAPM_OUTPUT("Speaker Right"), + SND_SOC_DAPM_OUTPUT("Line Out Left"), + SND_SOC_DAPM_OUTPUT("Line Out Right"), + SND_SOC_DAPM_INPUT("D2S 2"), + SND_SOC_DAPM_INPUT("D2S 1"), + SND_SOC_DAPM_INPUT("Line In 1 Left"), + SND_SOC_DAPM_INPUT("Line In 1 Right"), + SND_SOC_DAPM_INPUT("Line In 2 Left"), + SND_SOC_DAPM_INPUT("Line In 2 Right"), + SND_SOC_DAPM_INPUT("Line In 3 Left"), + SND_SOC_DAPM_INPUT("Line In 3 Right"), + SND_SOC_DAPM_INPUT("DMic 1"), + SND_SOC_DAPM_INPUT("DMic 2"), + + SND_SOC_DAPM_MUX("CH 0_1 Mux", SND_SOC_NOPM, 0, 0, + &ch_0_1_mux_dapm_enum), + SND_SOC_DAPM_MUX("CH 2_3 Mux", SND_SOC_NOPM, 0, 0, + &ch_2_3_mux_dapm_enum), + SND_SOC_DAPM_MUX("CH 4_5 Mux", SND_SOC_NOPM, 0, 0, + &ch_4_5_mux_dapm_enum), +}; + +static struct snd_soc_dapm_route const tscs454_intercon[] = { + /* PLLs */ + {"PLLs", NULL, "PLL 1 Power", pll_connected}, + {"PLLs", NULL, "PLL 2 Power", pll_connected}, + /* Inputs */ + {"DAI 3 In", NULL, "DAI 3 In Power"}, + {"DAI 2 In", NULL, "DAI 2 In Power"}, + {"DAI 1 In", NULL, "DAI 1 In Power"}, + /* Outputs */ + {"DAI 3 Out", NULL, "DAI 3 Out Power"}, + {"DAI 2 Out", NULL, "DAI 2 Out Power"}, + {"DAI 1 Out", NULL, "DAI 1 Out Power"}, + /* Ch Muxing */ + {"CH 0_1 Mux", "DAI 1", "DAI 1 In"}, + {"CH 0_1 Mux", "TDM 0_1", "DAI 1 In"}, + {"CH 2_3 Mux", "DAI 2", "DAI 2 In"}, + {"CH 2_3 Mux", "TDM 2_3", "DAI 1 In"}, + {"CH 4_5 Mux", "DAI 3", "DAI 2 In"}, + {"CH 4_5 Mux", "TDM 4_5", "DAI 1 In"}, + /* In/Out Muxing */ + {"DAI 1 Out Mux", "CH 0_1", "CH 0_1 Mux"}, + {"DAI 1 Out Mux", "CH 2_3", "CH 2_3 Mux"}, + {"DAI 1 Out Mux", "CH 4_5", "CH 4_5 Mux"}, + {"DAI 2 Out Mux", "CH 0_1", "CH 0_1 Mux"}, + {"DAI 2 Out Mux", "CH 2_3", "CH 2_3 Mux"}, + {"DAI 2 Out Mux", "CH 4_5", "CH 4_5 Mux"}, + {"DAI 3 Out Mux", "CH 0_1", "CH 0_1 Mux"}, + {"DAI 3 Out Mux", "CH 2_3", "CH 2_3 Mux"}, + {"DAI 3 Out Mux", "CH 4_5", "CH 4_5 Mux"}, + /****************** + * Playback Paths * + ******************/ + /* DAC Path */ + {"DAC Mux", "CH 4_5", "CH 4_5 Mux"}, + {"DAC Mux", "CH 2_3", "CH 2_3 Mux"}, + {"DAC Mux", "CH 0_1", "CH 0_1 Mux"}, + {"DAC Left", NULL, "DAC Mux"}, + {"DAC Right", NULL, "DAC Mux"}, + {"DAC Left", NULL, "PLLs"}, + {"DAC Right", NULL, "PLLs"}, + {"Headphone Left", NULL, "Headphone Left Power"}, + {"Headphone Right", NULL, "Headphone Right Power"}, + {"Headphone Left", NULL, "DAC Left"}, + {"Headphone Right", NULL, "DAC Right"}, + /* Line Out */ + {"Line Out Left", NULL, "Line Out Left Power"}, + {"Line Out Right", NULL, "Line Out Right Power"}, + {"Line Out Left", NULL, "DAC Left"}, + {"Line Out Right", NULL, "DAC Right"}, + /* ClassD Path */ + {"Speaker Mux", "CH 4_5", "CH 4_5 Mux"}, + {"Speaker Mux", "CH 2_3", "CH 2_3 Mux"}, + {"Speaker Mux", "CH 0_1", "CH 0_1 Mux"}, + {"ClassD Left", NULL, "Speaker Mux"}, + {"ClassD Right", NULL, "Speaker Mux"}, + {"ClassD Left", NULL, "PLLs"}, + {"ClassD Right", NULL, "PLLs"}, + {"Speaker Left", NULL, "Speaker Left Power"}, + {"Speaker Right", NULL, "Speaker Right Power"}, + {"Speaker Left", NULL, "ClassD Left"}, + {"Speaker Right", NULL, "ClassD Right"}, + /* Sub Path */ + {"Sub Mux", "CH 4", "CH 4_5 Mux"}, + {"Sub Mux", "CH 5", "CH 4_5 Mux"}, + {"Sub Mux", "CH 4 + 5", "CH 4_5 Mux"}, + {"Sub Mux", "CH 2", "CH 2_3 Mux"}, + {"Sub Mux", "CH 3", "CH 2_3 Mux"}, + {"Sub Mux", "CH 2 + 3", "CH 2_3 Mux"}, + {"Sub Mux", "CH 0", "CH 0_1 Mux"}, + {"Sub Mux", "CH 1", "CH 0_1 Mux"}, + {"Sub Mux", "CH 0 + 1", "CH 0_1 Mux"}, + {"Sub Mux", "ADC/DMic 1 Left", "Input Processor Channel 0"}, + {"Sub Mux", "ADC/DMic 1 Right", "Input Processor Channel 1"}, + {"Sub Mux", "ADC/DMic 1 Left Plus Right", "Input Processor Channel 0"}, + {"Sub Mux", "ADC/DMic 1 Left Plus Right", "Input Processor Channel 1"}, + {"Sub Mux", "DMic 2 Left", "DMic 2"}, + {"Sub Mux", "DMic 2 Right", "DMic 2"}, + {"Sub Mux", "DMic 2 Left Plus Right", "DMic 2"}, + {"Sub Mux", "ClassD Left", "ClassD Left"}, + {"Sub Mux", "ClassD Right", "ClassD Right"}, + {"Sub Mux", "ClassD Left Plus Right", "ClassD Left"}, + {"Sub Mux", "ClassD Left Plus Right", "ClassD Right"}, + {"Sub", NULL, "Sub Mux"}, + {"Sub", NULL, "PLLs"}, + {"Sub Out", NULL, "Sub Power"}, + {"Sub Out", NULL, "Sub"}, + /***************** + * Capture Paths * + *****************/ + {"Input Boost Channel 0 Mux", "Input 3", "Line In 3 Left"}, + {"Input Boost Channel 0 Mux", "Input 2", "Line In 2 Left"}, + {"Input Boost Channel 0 Mux", "Input 1", "Line In 1 Left"}, + {"Input Boost Channel 0 Mux", "D2S", "D2S 1"}, + + {"Input Boost Channel 1 Mux", "Input 3", "Line In 3 Right"}, + {"Input Boost Channel 1 Mux", "Input 2", "Line In 2 Right"}, + {"Input Boost Channel 1 Mux", "Input 1", "Line In 1 Right"}, + {"Input Boost Channel 1 Mux", "D2S", "D2S 2"}, + + {"ADC Channel 0 Mux", "Input 3 Boost Bypass", "Line In 3 Left"}, + {"ADC Channel 0 Mux", "Input 2 Boost Bypass", "Line In 2 Left"}, + {"ADC Channel 0 Mux", "Input 1 Boost Bypass", "Line In 1 Left"}, + {"ADC Channel 0 Mux", "Input Boost", "Input Boost Channel 0 Mux"}, + + {"ADC Channel 1 Mux", "Input 3 Boost Bypass", "Line In 3 Right"}, + {"ADC Channel 1 Mux", "Input 2 Boost Bypass", "Line In 2 Right"}, + {"ADC Channel 1 Mux", "Input 1 Boost Bypass", "Line In 1 Right"}, + {"ADC Channel 1 Mux", "Input Boost", "Input Boost Channel 1 Mux"}, + + {"Input Processor Channel 0 Mux", "ADC", "ADC Channel 0 Mux"}, + {"Input Processor Channel 0 Mux", "DMic", "DMic 1"}, + + {"Input Processor Channel 0", NULL, "PLLs"}, + {"Input Processor Channel 0", NULL, "Input Processor Channel 0 Mux"}, + + {"Input Processor Channel 1 Mux", "ADC", "ADC Channel 1 Mux"}, + {"Input Processor Channel 1 Mux", "DMic", "DMic 1"}, + + {"Input Processor Channel 1", NULL, "PLLs"}, + {"Input Processor Channel 1", NULL, "Input Processor Channel 1 Mux"}, + + {"Input Processor Channel 2", NULL, "PLLs"}, + {"Input Processor Channel 2", NULL, "DMic 2"}, + + {"Input Processor Channel 3", NULL, "PLLs"}, + {"Input Processor Channel 3", NULL, "DMic 2"}, + + {"DAI 1 Out Mux", "ADC/DMic 1", "Input Processor Channel 0"}, + {"DAI 1 Out Mux", "ADC/DMic 1", "Input Processor Channel 1"}, + {"DAI 1 Out Mux", "DMic 2", "Input Processor Channel 2"}, + {"DAI 1 Out Mux", "DMic 2", "Input Processor Channel 3"}, + + {"DAI 2 Out Mux", "ADC/DMic 1", "Input Processor Channel 0"}, + {"DAI 2 Out Mux", "ADC/DMic 1", "Input Processor Channel 1"}, + {"DAI 2 Out Mux", "DMic 2", "Input Processor Channel 2"}, + {"DAI 2 Out Mux", "DMic 2", "Input Processor Channel 3"}, + + {"DAI 3 Out Mux", "ADC/DMic 1", "Input Processor Channel 0"}, + {"DAI 3 Out Mux", "ADC/DMic 1", "Input Processor Channel 1"}, + {"DAI 3 Out Mux", "DMic 2", "Input Processor Channel 2"}, + {"DAI 3 Out Mux", "DMic 2", "Input Processor Channel 3"}, + + {"DAI 1 Out", NULL, "DAI 1 Out Mux"}, + {"DAI 2 Out", NULL, "DAI 2 Out Mux"}, + {"DAI 3 Out", NULL, "DAI 3 Out Mux"}, +}; + +/* This is used when BCLK is sourcing the PLLs */ +static int tscs454_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + unsigned int val; + int bclk_dai; + + dev_dbg(component->dev, "%s(): freq = %u\n", __func__, freq); + + val = snd_soc_component_read(component, R_PLLCTL); + + bclk_dai = (val & FM_PLLCTL_BCLKSEL) >> FB_PLLCTL_BCLKSEL; + if (bclk_dai != dai->id) + return 0; + + tscs454->bclk_freq = freq; + return set_sysclk(component); +} + +static int tscs454_set_bclk_ratio(struct snd_soc_dai *dai, + unsigned int ratio) +{ + unsigned int mask; + int ret; + struct snd_soc_component *component = dai->component; + unsigned int val; + int shift; + + dev_dbg(component->dev, "set_bclk_ratio() id = %d ratio = %u\n", + dai->id, ratio); + + switch (dai->id) { + case TSCS454_DAI1_ID: + mask = FM_I2SCMC_BCMP1; + shift = FB_I2SCMC_BCMP1; + break; + case TSCS454_DAI2_ID: + mask = FM_I2SCMC_BCMP2; + shift = FB_I2SCMC_BCMP2; + break; + case TSCS454_DAI3_ID: + mask = FM_I2SCMC_BCMP3; + shift = FB_I2SCMC_BCMP3; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unknown audio interface (%d)\n", ret); + return ret; + } + + switch (ratio) { + case 32: + val = I2SCMC_BCMP_32X; + break; + case 40: + val = I2SCMC_BCMP_40X; + break; + case 64: + val = I2SCMC_BCMP_64X; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unsupported bclk ratio (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, + R_I2SCMC, mask, val << shift); + if (ret < 0) { + dev_err(component->dev, + "Failed to set DAI BCLK ratio (%d)\n", ret); + return ret; + } + + return 0; +} + +static inline int set_aif_provider_from_fmt(struct snd_soc_component *component, + struct aif *aif, unsigned int fmt) +{ + int ret; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + aif->provider = true; + break; + case SND_SOC_DAIFMT_CBC_CFC: + aif->provider = false; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unsupported format (%d)\n", ret); + return ret; + } + + return 0; +} + +static inline int set_aif_tdm_delay(struct snd_soc_component *component, + unsigned int dai_id, bool delay) +{ + unsigned int reg; + int ret; + + switch (dai_id) { + case TSCS454_DAI1_ID: + reg = R_TDMCTL0; + break; + case TSCS454_DAI2_ID: + reg = R_PCMP2CTL0; + break; + case TSCS454_DAI3_ID: + reg = R_PCMP3CTL0; + break; + default: + ret = -EINVAL; + dev_err(component->dev, + "DAI %d unknown (%d)\n", dai_id + 1, ret); + return ret; + } + ret = snd_soc_component_update_bits(component, + reg, FM_TDMCTL0_BDELAY, delay); + if (ret < 0) { + dev_err(component->dev, "Failed to setup tdm format (%d)\n", + ret); + return ret; + } + + return 0; +} + +static inline int set_aif_format_from_fmt(struct snd_soc_component *component, + unsigned int dai_id, unsigned int fmt) +{ + unsigned int reg; + unsigned int val; + int ret; + + switch (dai_id) { + case TSCS454_DAI1_ID: + reg = R_I2SP1CTL; + break; + case TSCS454_DAI2_ID: + reg = R_I2SP2CTL; + break; + case TSCS454_DAI3_ID: + reg = R_I2SP3CTL; + break; + default: + ret = -EINVAL; + dev_err(component->dev, + "DAI %d unknown (%d)\n", dai_id + 1, ret); + return ret; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + val = FV_FORMAT_RIGHT; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = FV_FORMAT_LEFT; + break; + case SND_SOC_DAIFMT_I2S: + val = FV_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + ret = set_aif_tdm_delay(component, dai_id, true); + if (ret < 0) + return ret; + val = FV_FORMAT_TDM; + break; + case SND_SOC_DAIFMT_DSP_B: + ret = set_aif_tdm_delay(component, dai_id, false); + if (ret < 0) + return ret; + val = FV_FORMAT_TDM; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Format unsupported (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, + reg, FM_I2SPCTL_FORMAT, val); + if (ret < 0) { + dev_err(component->dev, "Failed to set DAI %d format (%d)\n", + dai_id + 1, ret); + return ret; + } + + return 0; +} + +static inline int +set_aif_clock_format_from_fmt(struct snd_soc_component *component, + unsigned int dai_id, unsigned int fmt) +{ + unsigned int reg; + unsigned int val; + int ret; + + switch (dai_id) { + case TSCS454_DAI1_ID: + reg = R_I2SP1CTL; + break; + case TSCS454_DAI2_ID: + reg = R_I2SP2CTL; + break; + case TSCS454_DAI3_ID: + reg = R_I2SP3CTL; + break; + default: + ret = -EINVAL; + dev_err(component->dev, + "DAI %d unknown (%d)\n", dai_id + 1, ret); + return ret; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val = FV_BCLKP_NOT_INVERTED | FV_LRCLKP_NOT_INVERTED; + break; + case SND_SOC_DAIFMT_NB_IF: + val = FV_BCLKP_NOT_INVERTED | FV_LRCLKP_INVERTED; + break; + case SND_SOC_DAIFMT_IB_NF: + val = FV_BCLKP_INVERTED | FV_LRCLKP_NOT_INVERTED; + break; + case SND_SOC_DAIFMT_IB_IF: + val = FV_BCLKP_INVERTED | FV_LRCLKP_INVERTED; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Format unknown (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, reg, + FM_I2SPCTL_BCLKP | FM_I2SPCTL_LRCLKP, val); + if (ret < 0) { + dev_err(component->dev, + "Failed to set clock polarity for DAI%d (%d)\n", + dai_id + 1, ret); + return ret; + } + + return 0; +} + +static int tscs454_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct aif *aif = &tscs454->aifs[dai->id]; + int ret; + + ret = set_aif_provider_from_fmt(component, aif, fmt); + if (ret < 0) + return ret; + + ret = set_aif_format_from_fmt(component, dai->id, fmt); + if (ret < 0) + return ret; + + ret = set_aif_clock_format_from_fmt(component, dai->id, fmt); + if (ret < 0) + return ret; + + return 0; +} + +static int tscs454_dai1_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, + int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int val; + int ret; + + if (!slots) + return 0; + + if (tx_mask >= (1 << slots) || rx_mask >= (1 << slots)) { + ret = -EINVAL; + dev_err(component->dev, "Invalid TDM slot mask (%d)\n", ret); + return ret; + } + + switch (slots) { + case 2: + val = FV_TDMSO_2 | FV_TDMSI_2; + break; + case 4: + val = FV_TDMSO_4 | FV_TDMSI_4; + break; + case 6: + val = FV_TDMSO_6 | FV_TDMSI_6; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Invalid number of slots (%d)\n", ret); + return ret; + } + + switch (slot_width) { + case 16: + val = val | FV_TDMDSS_16; + break; + case 24: + val = val | FV_TDMDSS_24; + break; + case 32: + val = val | FV_TDMDSS_32; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Invalid TDM slot width (%d)\n", ret); + return ret; + } + ret = snd_soc_component_write(component, R_TDMCTL1, val); + if (ret < 0) { + dev_err(component->dev, "Failed to set slots (%d)\n", ret); + return ret; + } + + return 0; +} + +static int tscs454_dai23_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, + int slot_width) +{ + struct snd_soc_component *component = dai->component; + unsigned int reg; + unsigned int val; + int ret; + + if (!slots) + return 0; + + if (tx_mask >= (1 << slots) || rx_mask >= (1 << slots)) { + ret = -EINVAL; + dev_err(component->dev, "Invalid TDM slot mask (%d)\n", ret); + return ret; + } + + switch (dai->id) { + case TSCS454_DAI2_ID: + reg = R_PCMP2CTL1; + break; + case TSCS454_DAI3_ID: + reg = R_PCMP3CTL1; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unrecognized interface %d (%d)\n", + dai->id, ret); + return ret; + } + + switch (slots) { + case 1: + val = FV_PCMSOP_1 | FV_PCMSIP_1; + break; + case 2: + val = FV_PCMSOP_2 | FV_PCMSIP_2; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Invalid number of slots (%d)\n", ret); + return ret; + } + + switch (slot_width) { + case 16: + val = val | FV_PCMDSSP_16; + break; + case 24: + val = val | FV_PCMDSSP_24; + break; + case 32: + val = val | FV_PCMDSSP_32; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Invalid TDM slot width (%d)\n", ret); + return ret; + } + ret = snd_soc_component_write(component, reg, val); + if (ret < 0) { + dev_err(component->dev, "Failed to set slots (%d)\n", ret); + return ret; + } + + return 0; +} + +static int set_aif_fs(struct snd_soc_component *component, + unsigned int id, + unsigned int rate) +{ + unsigned int reg; + unsigned int br; + unsigned int bm; + int ret; + + switch (rate) { + case 8000: + br = FV_I2SMBR_32; + bm = FV_I2SMBM_0PT25; + break; + case 16000: + br = FV_I2SMBR_32; + bm = FV_I2SMBM_0PT5; + break; + case 24000: + br = FV_I2SMBR_48; + bm = FV_I2SMBM_0PT5; + break; + case 32000: + br = FV_I2SMBR_32; + bm = FV_I2SMBM_1; + break; + case 48000: + br = FV_I2SMBR_48; + bm = FV_I2SMBM_1; + break; + case 96000: + br = FV_I2SMBR_48; + bm = FV_I2SMBM_2; + break; + case 11025: + br = FV_I2SMBR_44PT1; + bm = FV_I2SMBM_0PT25; + break; + case 22050: + br = FV_I2SMBR_44PT1; + bm = FV_I2SMBM_0PT5; + break; + case 44100: + br = FV_I2SMBR_44PT1; + bm = FV_I2SMBM_1; + break; + case 88200: + br = FV_I2SMBR_44PT1; + bm = FV_I2SMBM_2; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unsupported sample rate (%d)\n", ret); + return ret; + } + + switch (id) { + case TSCS454_DAI1_ID: + reg = R_I2S1MRATE; + break; + case TSCS454_DAI2_ID: + reg = R_I2S2MRATE; + break; + case TSCS454_DAI3_ID: + reg = R_I2S3MRATE; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "DAI ID not recognized (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, reg, + FM_I2SMRATE_I2SMBR | FM_I2SMRATE_I2SMBM, br|bm); + if (ret < 0) { + dev_err(component->dev, + "Failed to update register (%d)\n", ret); + return ret; + } + + return 0; +} + +static int set_aif_sample_format(struct snd_soc_component *component, + snd_pcm_format_t format, + int aif_id) +{ + unsigned int reg; + unsigned int width; + int ret; + + switch (snd_pcm_format_width(format)) { + case 16: + width = FV_WL_16; + break; + case 20: + width = FV_WL_20; + break; + case 24: + width = FV_WL_24; + break; + case 32: + width = FV_WL_32; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Unsupported format width (%d)\n", ret); + return ret; + } + + switch (aif_id) { + case TSCS454_DAI1_ID: + reg = R_I2SP1CTL; + break; + case TSCS454_DAI2_ID: + reg = R_I2SP2CTL; + break; + case TSCS454_DAI3_ID: + reg = R_I2SP3CTL; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "AIF ID not recognized (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, + reg, FM_I2SPCTL_WL, width); + if (ret < 0) { + dev_err(component->dev, + "Failed to set sample width (%d)\n", ret); + return ret; + } + + return 0; +} + +static int tscs454_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + unsigned int fs = params_rate(params); + struct aif *aif = &tscs454->aifs[dai->id]; + unsigned int val; + int ret; + + mutex_lock(&tscs454->aifs_status_lock); + + dev_dbg(component->dev, "%s(): aif %d fs = %u\n", __func__, + aif->id, fs); + + if (!aif_active(&tscs454->aifs_status, aif->id)) { + if (PLL_44_1K_RATE % fs) + aif->pll = &tscs454->pll1; + else + aif->pll = &tscs454->pll2; + + dev_dbg(component->dev, "Reserving pll %d for aif %d\n", + aif->pll->id, aif->id); + + reserve_pll(aif->pll); + } + + if (!aifs_active(&tscs454->aifs_status)) { /* First active aif */ + val = snd_soc_component_read(component, R_ISRC); + if ((val & FM_ISRC_IBR) == FV_IBR_48) + tscs454->internal_rate.pll = &tscs454->pll1; + else + tscs454->internal_rate.pll = &tscs454->pll2; + + dev_dbg(component->dev, "Reserving pll %d for ir\n", + tscs454->internal_rate.pll->id); + + reserve_pll(tscs454->internal_rate.pll); + } + + ret = set_aif_fs(component, aif->id, fs); + if (ret < 0) { + dev_err(component->dev, "Failed to set aif fs (%d)\n", ret); + goto exit; + } + + ret = set_aif_sample_format(component, params_format(params), aif->id); + if (ret < 0) { + dev_err(component->dev, + "Failed to set aif sample format (%d)\n", ret); + goto exit; + } + + set_aif_status_active(&tscs454->aifs_status, aif->id, + substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + + dev_dbg(component->dev, "Set aif %d active. Streams status is 0x%x\n", + aif->id, tscs454->aifs_status.streams); + + ret = 0; +exit: + mutex_unlock(&tscs454->aifs_status_lock); + + return ret; +} + +static int tscs454_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct aif *aif = &tscs454->aifs[dai->id]; + + return aif_free(component, aif, + substream->stream == SNDRV_PCM_STREAM_PLAYBACK); +} + +static int tscs454_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + struct snd_soc_component *component = dai->component; + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + struct aif *aif = &tscs454->aifs[dai->id]; + + ret = aif_prepare(component, aif); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_dai_ops const tscs454_dai1_ops = { + .set_sysclk = tscs454_set_sysclk, + .set_bclk_ratio = tscs454_set_bclk_ratio, + .set_fmt = tscs454_set_dai_fmt, + .set_tdm_slot = tscs454_dai1_set_tdm_slot, + .hw_params = tscs454_hw_params, + .hw_free = tscs454_hw_free, + .prepare = tscs454_prepare, +}; + +static struct snd_soc_dai_ops const tscs454_dai23_ops = { + .set_sysclk = tscs454_set_sysclk, + .set_bclk_ratio = tscs454_set_bclk_ratio, + .set_fmt = tscs454_set_dai_fmt, + .set_tdm_slot = tscs454_dai23_set_tdm_slot, + .hw_params = tscs454_hw_params, + .hw_free = tscs454_hw_free, + .prepare = tscs454_prepare, +}; + +static int tscs454_probe(struct snd_soc_component *component) +{ + struct tscs454 *tscs454 = snd_soc_component_get_drvdata(component); + unsigned int val; + int ret = 0; + + switch (tscs454->sysclk_src_id) { + case PLL_INPUT_XTAL: + val = FV_PLLISEL_XTAL; + break; + case PLL_INPUT_MCLK1: + val = FV_PLLISEL_MCLK1; + break; + case PLL_INPUT_MCLK2: + val = FV_PLLISEL_MCLK2; + break; + case PLL_INPUT_BCLK: + val = FV_PLLISEL_BCLK; + break; + default: + ret = -EINVAL; + dev_err(component->dev, "Invalid sysclk src id (%d)\n", ret); + return ret; + } + + ret = snd_soc_component_update_bits(component, R_PLLCTL, + FM_PLLCTL_PLLISEL, val); + if (ret < 0) { + dev_err(component->dev, "Failed to set PLL input (%d)\n", ret); + return ret; + } + + if (tscs454->sysclk_src_id < PLL_INPUT_BCLK) + ret = set_sysclk(component); + + return ret; +} + +static const struct snd_soc_component_driver soc_component_dev_tscs454 = { + .probe = tscs454_probe, + .dapm_widgets = tscs454_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tscs454_dapm_widgets), + .dapm_routes = tscs454_intercon, + .num_dapm_routes = ARRAY_SIZE(tscs454_intercon), + .controls = tscs454_snd_controls, + .num_controls = ARRAY_SIZE(tscs454_snd_controls), + .endianness = 1, +}; + +#define TSCS454_RATES SNDRV_PCM_RATE_8000_96000 + +#define TSCS454_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver tscs454_dais[] = { + { + .name = "tscs454-dai1", + .id = TSCS454_DAI1_ID, + .playback = { + .stream_name = "DAI 1 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .capture = { + .stream_name = "DAI 1 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .ops = &tscs454_dai1_ops, + .symmetric_rate = 1, + .symmetric_channels = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "tscs454-dai2", + .id = TSCS454_DAI2_ID, + .playback = { + .stream_name = "DAI 2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .capture = { + .stream_name = "DAI 2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .ops = &tscs454_dai23_ops, + .symmetric_rate = 1, + .symmetric_channels = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "tscs454-dai3", + .id = TSCS454_DAI3_ID, + .playback = { + .stream_name = "DAI 3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .capture = { + .stream_name = "DAI 3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TSCS454_RATES, + .formats = TSCS454_FORMATS,}, + .ops = &tscs454_dai23_ops, + .symmetric_rate = 1, + .symmetric_channels = 1, + .symmetric_sample_bits = 1, + }, +}; + +static char const * const src_names[] = { + "xtal", "mclk1", "mclk2", "bclk"}; + +static int tscs454_i2c_probe(struct i2c_client *i2c) +{ + struct tscs454 *tscs454; + int src; + int ret; + + tscs454 = devm_kzalloc(&i2c->dev, sizeof(*tscs454), GFP_KERNEL); + if (!tscs454) + return -ENOMEM; + + ret = tscs454_data_init(tscs454, i2c); + if (ret < 0) + return ret; + + i2c_set_clientdata(i2c, tscs454); + + for (src = PLL_INPUT_XTAL; src < PLL_INPUT_BCLK; src++) { + tscs454->sysclk = devm_clk_get(&i2c->dev, src_names[src]); + if (!IS_ERR(tscs454->sysclk)) { + break; + } else if (PTR_ERR(tscs454->sysclk) != -ENOENT) { + ret = PTR_ERR(tscs454->sysclk); + dev_err(&i2c->dev, "Failed to get sysclk (%d)\n", ret); + return ret; + } + } + dev_dbg(&i2c->dev, "PLL input is %s\n", src_names[src]); + tscs454->sysclk_src_id = src; + + ret = regmap_write(tscs454->regmap, + R_RESET, FV_RESET_PWR_ON_DEFAULTS); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to reset the component (%d)\n", ret); + return ret; + } + regcache_mark_dirty(tscs454->regmap); + + ret = regmap_register_patch(tscs454->regmap, tscs454_patch, + ARRAY_SIZE(tscs454_patch)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to apply patch (%d)\n", ret); + return ret; + } + /* Sync pg sel reg with cache */ + regmap_write(tscs454->regmap, R_PAGESEL, 0x00); + + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_tscs454, + tscs454_dais, ARRAY_SIZE(tscs454_dais)); + if (ret) { + dev_err(&i2c->dev, "Failed to register component (%d)\n", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id tscs454_i2c_id[] = { + { "tscs454", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tscs454_i2c_id); + +static const struct of_device_id tscs454_of_match[] = { + { .compatible = "tempo,tscs454", }, + { } +}; +MODULE_DEVICE_TABLE(of, tscs454_of_match); + +static struct i2c_driver tscs454_i2c_driver = { + .driver = { + .name = "tscs454", + .of_match_table = tscs454_of_match, + }, + .probe = tscs454_i2c_probe, + .id_table = tscs454_i2c_id, +}; + +module_i2c_driver(tscs454_i2c_driver); + +MODULE_AUTHOR("Tempo Semiconductor + +#ifndef __REDWOODPUBLIC_H__ +#define __REDWOODPUBLIC_H__ + +#define VIRT_BASE 0x00 +#define PAGE_LEN 0x100 +#define VIRT_PAGE_BASE(page) (VIRT_BASE + (PAGE_LEN * page)) +#define VIRT_ADDR(page, address) (VIRT_PAGE_BASE(page) + address) +#define ADDR(page, virt_address) (virt_address - VIRT_PAGE_BASE(page)) + +#define R_PAGESEL 0x0 +#define R_RESET VIRT_ADDR(0x0, 0x1) +#define R_IRQEN VIRT_ADDR(0x0, 0x2) +#define R_IRQMASK VIRT_ADDR(0x0, 0x3) +#define R_IRQSTAT VIRT_ADDR(0x0, 0x4) +#define R_DEVADD0 VIRT_ADDR(0x0, 0x6) +#define R_DEVID VIRT_ADDR(0x0, 0x8) +#define R_DEVREV VIRT_ADDR(0x0, 0x9) +#define R_PLLSTAT VIRT_ADDR(0x0, 0x0A) +#define R_PLL1CTL VIRT_ADDR(0x0, 0x0B) +#define R_PLL1RDIV VIRT_ADDR(0x0, 0x0C) +#define R_PLL1ODIV VIRT_ADDR(0x0, 0x0D) +#define R_PLL1FDIVL VIRT_ADDR(0x0, 0x0E) +#define R_PLL1FDIVH VIRT_ADDR(0x0, 0x0F) +#define R_PLL2CTL VIRT_ADDR(0x0, 0x10) +#define R_PLL2RDIV VIRT_ADDR(0x0, 0x11) +#define R_PLL2ODIV VIRT_ADDR(0x0, 0x12) +#define R_PLL2FDIVL VIRT_ADDR(0x0, 0x13) +#define R_PLL2FDIVH VIRT_ADDR(0x0, 0x14) +#define R_PLLCTL VIRT_ADDR(0x0, 0x15) +#define R_ISRC VIRT_ADDR(0x0, 0x16) +#define R_SCLKCTL VIRT_ADDR(0x0, 0x18) +#define R_TIMEBASE VIRT_ADDR(0x0, 0x19) +#define R_I2SP1CTL VIRT_ADDR(0x0, 0x1A) +#define R_I2SP2CTL VIRT_ADDR(0x0, 0x1B) +#define R_I2SP3CTL VIRT_ADDR(0x0, 0x1C) +#define R_I2S1MRATE VIRT_ADDR(0x0, 0x1D) +#define R_I2S2MRATE VIRT_ADDR(0x0, 0x1E) +#define R_I2S3MRATE VIRT_ADDR(0x0, 0x1F) +#define R_I2SCMC VIRT_ADDR(0x0, 0x20) +#define R_MCLK2PINC VIRT_ADDR(0x0, 0x21) +#define R_I2SPINC0 VIRT_ADDR(0x0, 0x22) +#define R_I2SPINC1 VIRT_ADDR(0x0, 0x23) +#define R_I2SPINC2 VIRT_ADDR(0x0, 0x24) +#define R_GPIOCTL0 VIRT_ADDR(0x0, 0x25) +#define R_GPIOCTL1 VIRT_ADDR(0x0, 0x26) +#define R_ASRC VIRT_ADDR(0x0, 0x28) +#define R_TDMCTL0 VIRT_ADDR(0x0, 0x2D) +#define R_TDMCTL1 VIRT_ADDR(0x0, 0x2E) +#define R_PCMP2CTL0 VIRT_ADDR(0x0, 0x2F) +#define R_PCMP2CTL1 VIRT_ADDR(0x0, 0x30) +#define R_PCMP3CTL0 VIRT_ADDR(0x0, 0x31) +#define R_PCMP3CTL1 VIRT_ADDR(0x0, 0x32) +#define R_PWRM0 VIRT_ADDR(0x0, 0x33) +#define R_PWRM1 VIRT_ADDR(0x0, 0x34) +#define R_PWRM2 VIRT_ADDR(0x0, 0x35) +#define R_PWRM3 VIRT_ADDR(0x0, 0x36) +#define R_PWRM4 VIRT_ADDR(0x0, 0x37) +#define R_I2SIDCTL VIRT_ADDR(0x0, 0x38) +#define R_I2SODCTL VIRT_ADDR(0x0, 0x39) +#define R_AUDIOMUX1 VIRT_ADDR(0x0, 0x3A) +#define R_AUDIOMUX2 VIRT_ADDR(0x0, 0x3B) +#define R_AUDIOMUX3 VIRT_ADDR(0x0, 0x3C) +#define R_HSDCTL1 VIRT_ADDR(0x1, 0x1) +#define R_HSDCTL2 VIRT_ADDR(0x1, 0x2) +#define R_HSDSTAT VIRT_ADDR(0x1, 0x3) +#define R_HSDDELAY VIRT_ADDR(0x1, 0x4) +#define R_BUTCTL VIRT_ADDR(0x1, 0x5) +#define R_CH0AIC VIRT_ADDR(0x1, 0x6) +#define R_CH1AIC VIRT_ADDR(0x1, 0x7) +#define R_CH2AIC VIRT_ADDR(0x1, 0x8) +#define R_CH3AIC VIRT_ADDR(0x1, 0x9) +#define R_ICTL0 VIRT_ADDR(0x1, 0x0A) +#define R_ICTL1 VIRT_ADDR(0x1, 0x0B) +#define R_MICBIAS VIRT_ADDR(0x1, 0x0C) +#define R_PGACTL0 VIRT_ADDR(0x1, 0x0D) +#define R_PGACTL1 VIRT_ADDR(0x1, 0x0E) +#define R_PGACTL2 VIRT_ADDR(0x1, 0x0F) +#define R_PGACTL3 VIRT_ADDR(0x1, 0x10) +#define R_PGAZ VIRT_ADDR(0x1, 0x11) +#define R_ICH0VOL VIRT_ADDR(0x1, 0x12) +#define R_ICH1VOL VIRT_ADDR(0x1, 0x13) +#define R_ICH2VOL VIRT_ADDR(0x1, 0x14) +#define R_ICH3VOL VIRT_ADDR(0x1, 0x15) +#define R_ASRCILVOL VIRT_ADDR(0x1, 0x16) +#define R_ASRCIRVOL VIRT_ADDR(0x1, 0x17) +#define R_ASRCOLVOL VIRT_ADDR(0x1, 0x18) +#define R_ASRCORVOL VIRT_ADDR(0x1, 0x19) +#define R_IVOLCTLU VIRT_ADDR(0x1, 0x1C) +#define R_ALCCTL0 VIRT_ADDR(0x1, 0x1D) +#define R_ALCCTL1 VIRT_ADDR(0x1, 0x1E) +#define R_ALCCTL2 VIRT_ADDR(0x1, 0x1F) +#define R_ALCCTL3 VIRT_ADDR(0x1, 0x20) +#define R_NGATE VIRT_ADDR(0x1, 0x21) +#define R_DMICCTL VIRT_ADDR(0x1, 0x22) +#define R_DACCTL VIRT_ADDR(0x2, 0x1) +#define R_SPKCTL VIRT_ADDR(0x2, 0x2) +#define R_SUBCTL VIRT_ADDR(0x2, 0x3) +#define R_DCCTL VIRT_ADDR(0x2, 0x4) +#define R_OVOLCTLU VIRT_ADDR(0x2, 0x6) +#define R_MUTEC VIRT_ADDR(0x2, 0x7) +#define R_MVOLL VIRT_ADDR(0x2, 0x8) +#define R_MVOLR VIRT_ADDR(0x2, 0x9) +#define R_HPVOLL VIRT_ADDR(0x2, 0x0A) +#define R_HPVOLR VIRT_ADDR(0x2, 0x0B) +#define R_SPKVOLL VIRT_ADDR(0x2, 0x0C) +#define R_SPKVOLR VIRT_ADDR(0x2, 0x0D) +#define R_SUBVOL VIRT_ADDR(0x2, 0x10) +#define R_COP0 VIRT_ADDR(0x2, 0x11) +#define R_COP1 VIRT_ADDR(0x2, 0x12) +#define R_COPSTAT VIRT_ADDR(0x2, 0x13) +#define R_PWM0 VIRT_ADDR(0x2, 0x14) +#define R_PWM1 VIRT_ADDR(0x2, 0x15) +#define R_PWM2 VIRT_ADDR(0x2, 0x16) +#define R_PWM3 VIRT_ADDR(0x2, 0x17) +#define R_HPSW VIRT_ADDR(0x2, 0x18) +#define R_THERMTS VIRT_ADDR(0x2, 0x19) +#define R_THERMSPK1 VIRT_ADDR(0x2, 0x1A) +#define R_THERMSTAT VIRT_ADDR(0x2, 0x1B) +#define R_SCSTAT VIRT_ADDR(0x2, 0x1C) +#define R_SDMON VIRT_ADDR(0x2, 0x1D) +#define R_SPKEQFILT VIRT_ADDR(0x3, 0x1) +#define R_SPKCRWDL VIRT_ADDR(0x3, 0x2) +#define R_SPKCRWDM VIRT_ADDR(0x3, 0x3) +#define R_SPKCRWDH VIRT_ADDR(0x3, 0x4) +#define R_SPKCRRDL VIRT_ADDR(0x3, 0x5) +#define R_SPKCRRDM VIRT_ADDR(0x3, 0x6) +#define R_SPKCRRDH VIRT_ADDR(0x3, 0x7) +#define R_SPKCRADD VIRT_ADDR(0x3, 0x8) +#define R_SPKCRS VIRT_ADDR(0x3, 0x9) +#define R_SPKMBCEN VIRT_ADDR(0x3, 0x0A) +#define R_SPKMBCCTL VIRT_ADDR(0x3, 0x0B) +#define R_SPKMBCMUG1 VIRT_ADDR(0x3, 0x0C) +#define R_SPKMBCTHR1 VIRT_ADDR(0x3, 0x0D) +#define R_SPKMBCRAT1 VIRT_ADDR(0x3, 0x0E) +#define R_SPKMBCATK1L VIRT_ADDR(0x3, 0x0F) +#define R_SPKMBCATK1H VIRT_ADDR(0x3, 0x10) +#define R_SPKMBCREL1L VIRT_ADDR(0x3, 0x11) +#define R_SPKMBCREL1H VIRT_ADDR(0x3, 0x12) +#define R_SPKMBCMUG2 VIRT_ADDR(0x3, 0x13) +#define R_SPKMBCTHR2 VIRT_ADDR(0x3, 0x14) +#define R_SPKMBCRAT2 VIRT_ADDR(0x3, 0x15) +#define R_SPKMBCATK2L VIRT_ADDR(0x3, 0x16) +#define R_SPKMBCATK2H VIRT_ADDR(0x3, 0x17) +#define R_SPKMBCREL2L VIRT_ADDR(0x3, 0x18) +#define R_SPKMBCREL2H VIRT_ADDR(0x3, 0x19) +#define R_SPKMBCMUG3 VIRT_ADDR(0x3, 0x1A) +#define R_SPKMBCTHR3 VIRT_ADDR(0x3, 0x1B) +#define R_SPKMBCRAT3 VIRT_ADDR(0x3, 0x1C) +#define R_SPKMBCATK3L VIRT_ADDR(0x3, 0x1D) +#define R_SPKMBCATK3H VIRT_ADDR(0x3, 0x1E) +#define R_SPKMBCREL3L VIRT_ADDR(0x3, 0x1F) +#define R_SPKMBCREL3H VIRT_ADDR(0x3, 0x20) +#define R_SPKCLECTL VIRT_ADDR(0x3, 0x21) +#define R_SPKCLEMUG VIRT_ADDR(0x3, 0x22) +#define R_SPKCOMPTHR VIRT_ADDR(0x3, 0x23) +#define R_SPKCOMPRAT VIRT_ADDR(0x3, 0x24) +#define R_SPKCOMPATKL VIRT_ADDR(0x3, 0x25) +#define R_SPKCOMPATKH VIRT_ADDR(0x3, 0x26) +#define R_SPKCOMPRELL VIRT_ADDR(0x3, 0x27) +#define R_SPKCOMPRELH VIRT_ADDR(0x3, 0x28) +#define R_SPKLIMTHR VIRT_ADDR(0x3, 0x29) +#define R_SPKLIMTGT VIRT_ADDR(0x3, 0x2A) +#define R_SPKLIMATKL VIRT_ADDR(0x3, 0x2B) +#define R_SPKLIMATKH VIRT_ADDR(0x3, 0x2C) +#define R_SPKLIMRELL VIRT_ADDR(0x3, 0x2D) +#define R_SPKLIMRELH VIRT_ADDR(0x3, 0x2E) +#define R_SPKEXPTHR VIRT_ADDR(0x3, 0x2F) +#define R_SPKEXPRAT VIRT_ADDR(0x3, 0x30) +#define R_SPKEXPATKL VIRT_ADDR(0x3, 0x31) +#define R_SPKEXPATKH VIRT_ADDR(0x3, 0x32) +#define R_SPKEXPRELL VIRT_ADDR(0x3, 0x33) +#define R_SPKEXPRELH VIRT_ADDR(0x3, 0x34) +#define R_SPKFXCTL VIRT_ADDR(0x3, 0x35) +#define R_DACEQFILT VIRT_ADDR(0x4, 0x1) +#define R_DACCRWDL VIRT_ADDR(0x4, 0x2) +#define R_DACCRWDM VIRT_ADDR(0x4, 0x3) +#define R_DACCRWDH VIRT_ADDR(0x4, 0x4) +#define R_DACCRRDL VIRT_ADDR(0x4, 0x5) +#define R_DACCRRDM VIRT_ADDR(0x4, 0x6) +#define R_DACCRRDH VIRT_ADDR(0x4, 0x7) +#define R_DACCRADD VIRT_ADDR(0x4, 0x8) +#define R_DACCRS VIRT_ADDR(0x4, 0x9) +#define R_DACMBCEN VIRT_ADDR(0x4, 0x0A) +#define R_DACMBCCTL VIRT_ADDR(0x4, 0x0B) +#define R_DACMBCMUG1 VIRT_ADDR(0x4, 0x0C) +#define R_DACMBCTHR1 VIRT_ADDR(0x4, 0x0D) +#define R_DACMBCRAT1 VIRT_ADDR(0x4, 0x0E) +#define R_DACMBCATK1L VIRT_ADDR(0x4, 0x0F) +#define R_DACMBCATK1H VIRT_ADDR(0x4, 0x10) +#define R_DACMBCREL1L VIRT_ADDR(0x4, 0x11) +#define R_DACMBCREL1H VIRT_ADDR(0x4, 0x12) +#define R_DACMBCMUG2 VIRT_ADDR(0x4, 0x13) +#define R_DACMBCTHR2 VIRT_ADDR(0x4, 0x14) +#define R_DACMBCRAT2 VIRT_ADDR(0x4, 0x15) +#define R_DACMBCATK2L VIRT_ADDR(0x4, 0x16) +#define R_DACMBCATK2H VIRT_ADDR(0x4, 0x17) +#define R_DACMBCREL2L VIRT_ADDR(0x4, 0x18) +#define R_DACMBCREL2H VIRT_ADDR(0x4, 0x19) +#define R_DACMBCMUG3 VIRT_ADDR(0x4, 0x1A) +#define R_DACMBCTHR3 VIRT_ADDR(0x4, 0x1B) +#define R_DACMBCRAT3 VIRT_ADDR(0x4, 0x1C) +#define R_DACMBCATK3L VIRT_ADDR(0x4, 0x1D) +#define R_DACMBCATK3H VIRT_ADDR(0x4, 0x1E) +#define R_DACMBCREL3L VIRT_ADDR(0x4, 0x1F) +#define R_DACMBCREL3H VIRT_ADDR(0x4, 0x20) +#define R_DACCLECTL VIRT_ADDR(0x4, 0x21) +#define R_DACCLEMUG VIRT_ADDR(0x4, 0x22) +#define R_DACCOMPTHR VIRT_ADDR(0x4, 0x23) +#define R_DACCOMPRAT VIRT_ADDR(0x4, 0x24) +#define R_DACCOMPATKL VIRT_ADDR(0x4, 0x25) +#define R_DACCOMPATKH VIRT_ADDR(0x4, 0x26) +#define R_DACCOMPRELL VIRT_ADDR(0x4, 0x27) +#define R_DACCOMPRELH VIRT_ADDR(0x4, 0x28) +#define R_DACLIMTHR VIRT_ADDR(0x4, 0x29) +#define R_DACLIMTGT VIRT_ADDR(0x4, 0x2A) +#define R_DACLIMATKL VIRT_ADDR(0x4, 0x2B) +#define R_DACLIMATKH VIRT_ADDR(0x4, 0x2C) +#define R_DACLIMRELL VIRT_ADDR(0x4, 0x2D) +#define R_DACLIMRELH VIRT_ADDR(0x4, 0x2E) +#define R_DACEXPTHR VIRT_ADDR(0x4, 0x2F) +#define R_DACEXPRAT VIRT_ADDR(0x4, 0x30) +#define R_DACEXPATKL VIRT_ADDR(0x4, 0x31) +#define R_DACEXPATKH VIRT_ADDR(0x4, 0x32) +#define R_DACEXPRELL VIRT_ADDR(0x4, 0x33) +#define R_DACEXPRELH VIRT_ADDR(0x4, 0x34) +#define R_DACFXCTL VIRT_ADDR(0x4, 0x35) +#define R_SUBEQFILT VIRT_ADDR(0x5, 0x1) +#define R_SUBCRWDL VIRT_ADDR(0x5, 0x2) +#define R_SUBCRWDM VIRT_ADDR(0x5, 0x3) +#define R_SUBCRWDH VIRT_ADDR(0x5, 0x4) +#define R_SUBCRRDL VIRT_ADDR(0x5, 0x5) +#define R_SUBCRRDM VIRT_ADDR(0x5, 0x6) +#define R_SUBCRRDH VIRT_ADDR(0x5, 0x7) +#define R_SUBCRADD VIRT_ADDR(0x5, 0x8) +#define R_SUBCRS VIRT_ADDR(0x5, 0x9) +#define R_SUBMBCEN VIRT_ADDR(0x5, 0x0A) +#define R_SUBMBCCTL VIRT_ADDR(0x5, 0x0B) +#define R_SUBMBCMUG1 VIRT_ADDR(0x5, 0x0C) +#define R_SUBMBCTHR1 VIRT_ADDR(0x5, 0x0D) +#define R_SUBMBCRAT1 VIRT_ADDR(0x5, 0x0E) +#define R_SUBMBCATK1L VIRT_ADDR(0x5, 0x0F) +#define R_SUBMBCATK1H VIRT_ADDR(0x5, 0x10) +#define R_SUBMBCREL1L VIRT_ADDR(0x5, 0x11) +#define R_SUBMBCREL1H VIRT_ADDR(0x5, 0x12) +#define R_SUBMBCMUG2 VIRT_ADDR(0x5, 0x13) +#define R_SUBMBCTHR2 VIRT_ADDR(0x5, 0x14) +#define R_SUBMBCRAT2 VIRT_ADDR(0x5, 0x15) +#define R_SUBMBCATK2L VIRT_ADDR(0x5, 0x16) +#define R_SUBMBCATK2H VIRT_ADDR(0x5, 0x17) +#define R_SUBMBCREL2L VIRT_ADDR(0x5, 0x18) +#define R_SUBMBCREL2H VIRT_ADDR(0x5, 0x19) +#define R_SUBMBCMUG3 VIRT_ADDR(0x5, 0x1A) +#define R_SUBMBCTHR3 VIRT_ADDR(0x5, 0x1B) +#define R_SUBMBCRAT3 VIRT_ADDR(0x5, 0x1C) +#define R_SUBMBCATK3L VIRT_ADDR(0x5, 0x1D) +#define R_SUBMBCATK3H VIRT_ADDR(0x5, 0x1E) +#define R_SUBMBCREL3L VIRT_ADDR(0x5, 0x1F) +#define R_SUBMBCREL3H VIRT_ADDR(0x5, 0x20) +#define R_SUBCLECTL VIRT_ADDR(0x5, 0x21) +#define R_SUBCLEMUG VIRT_ADDR(0x5, 0x22) +#define R_SUBCOMPTHR VIRT_ADDR(0x5, 0x23) +#define R_SUBCOMPRAT VIRT_ADDR(0x5, 0x24) +#define R_SUBCOMPATKL VIRT_ADDR(0x5, 0x25) +#define R_SUBCOMPATKH VIRT_ADDR(0x5, 0x26) +#define R_SUBCOMPRELL VIRT_ADDR(0x5, 0x27) +#define R_SUBCOMPRELH VIRT_ADDR(0x5, 0x28) +#define R_SUBLIMTHR VIRT_ADDR(0x5, 0x29) +#define R_SUBLIMTGT VIRT_ADDR(0x5, 0x2A) +#define R_SUBLIMATKL VIRT_ADDR(0x5, 0x2B) +#define R_SUBLIMATKH VIRT_ADDR(0x5, 0x2C) +#define R_SUBLIMRELL VIRT_ADDR(0x5, 0x2D) +#define R_SUBLIMRELH VIRT_ADDR(0x5, 0x2E) +#define R_SUBEXPTHR VIRT_ADDR(0x5, 0x2F) +#define R_SUBEXPRAT VIRT_ADDR(0x5, 0x30) +#define R_SUBEXPATKL VIRT_ADDR(0x5, 0x31) +#define R_SUBEXPATKH VIRT_ADDR(0x5, 0x32) +#define R_SUBEXPRELL VIRT_ADDR(0x5, 0x33) +#define R_SUBEXPRELH VIRT_ADDR(0x5, 0x34) +#define R_SUBFXCTL VIRT_ADDR(0x5, 0x35) + +// *** PLLCTL *** +#define FB_PLLCTL_VCCI_PLL 6 +#define FM_PLLCTL_VCCI_PLL 0xC0 + +#define FB_PLLCTL_RZ_PLL 3 +#define FM_PLLCTL_RZ_PLL 0x38 + +#define FB_PLLCTL_CP_PLL 0 +#define FM_PLLCTL_CP_PLL 0x7 + +// *** PLLRDIV *** +#define FB_PLLRDIV_REFDIV_PLL 0 +#define FM_PLLRDIV_REFDIV_PLL 0xFF + +// *** PLLODIV *** +#define FB_PLLODIV_OUTDIV_PLL 0 +#define FM_PLLODIV_OUTDIV_PLL 0xFF + +// *** PLLFDIVL *** +#define FB_PLLFDIVL_FBDIVL_PLL 0 +#define FM_PLLFDIVL_FBDIVL_PLL 0xFF + +// *** PLLFDIVH *** +#define FB_PLLFDIVH_FBDIVH_PLL 0 +#define FM_PLLFDIVH_FBDIVH_PLL 0xF + +// *** I2SPCTL *** +#define FB_I2SPCTL_BCLKSTAT 7 +#define FM_I2SPCTL_BCLKSTAT 0x80 +#define FV_BCLKSTAT_LOST 0x80 +#define FV_BCLKSTAT_NOT_LOST 0x0 + +#define FB_I2SPCTL_BCLKP 6 +#define FM_I2SPCTL_BCLKP 0x40 +#define FV_BCLKP_NOT_INVERTED 0x0 +#define FV_BCLKP_INVERTED 0x40 + +#define FB_I2SPCTL_PORTMS 5 +#define FM_I2SPCTL_PORTMS 0x20 +#define FV_PORTMS_SLAVE 0x0 +#define FV_PORTMS_MASTER 0x20 + +#define FB_I2SPCTL_LRCLKP 4 +#define FM_I2SPCTL_LRCLKP 0x10 +#define FV_LRCLKP_NOT_INVERTED 0x0 +#define FV_LRCLKP_INVERTED 0x10 + +#define FB_I2SPCTL_WL 2 +#define FM_I2SPCTL_WL 0xC +#define FV_WL_16 0x0 +#define FV_WL_20 0x4 +#define FV_WL_24 0x8 +#define FV_WL_32 0xC + +#define FB_I2SPCTL_FORMAT 0 +#define FM_I2SPCTL_FORMAT 0x3 +#define FV_FORMAT_RIGHT 0x0 +#define FV_FORMAT_LEFT 0x1 +#define FV_FORMAT_I2S 0x2 +#define FV_FORMAT_TDM 0x3 + +// *** I2SMRATE *** +#define FB_I2SMRATE_I2SMCLKHALF 7 +#define FM_I2SMRATE_I2SMCLKHALF 0x80 +#define FV_I2SMCLKHALF_I2S1MCLKDIV_DIV_2 0x0 +#define FV_I2SMCLKHALF_I2S1MCLKDIV_ONLY 0x80 + +#define FB_I2SMRATE_I2SMCLKDIV 5 +#define FM_I2SMRATE_I2SMCLKDIV 0x60 +#define FV_I2SMCLKDIV_125 0x0 +#define FV_I2SMCLKDIV_128 0x20 +#define FV_I2SMCLKDIV_136 0x40 +#define FV_I2SMCLKDIV_192 0x60 + +#define FB_I2SMRATE_I2SMBR 3 +#define FM_I2SMRATE_I2SMBR 0x18 +#define FV_I2SMBR_32 0x0 +#define FV_I2SMBR_44PT1 0x8 +#define FV_I2SMBR_48 0x10 +#define FV_I2SMBR_MCLK_MODE 0x18 + +#define FB_I2SMRATE_I2SMBM 0 +#define FM_I2SMRATE_I2SMBM 0x3 +#define FV_I2SMBM_0PT25 0x0 +#define FV_I2SMBM_0PT5 0x1 +#define FV_I2SMBM_1 0x2 +#define FV_I2SMBM_2 0x3 + +// *** PCMPCTL0 *** +#define FB_PCMPCTL0_PCMFLENP 2 +#define FM_PCMPCTL0_PCMFLENP 0x4 +#define FV_PCMFLENP_128 0x0 +#define FV_PCMFLENP_256 0x4 + +#define FB_PCMPCTL0_SLSYNCP 1 +#define FM_PCMPCTL0_SLSYNCP 0x2 +#define FV_SLSYNCP_SHORT 0x0 +#define FV_SLSYNCP_LONG 0x2 + +#define FB_PCMPCTL0_BDELAYP 0 +#define FM_PCMPCTL0_BDELAYP 0x1 +#define FV_BDELAYP_NO_DELAY 0x0 +#define FV_BDELAYP_1BCLK_DELAY 0x1 + +// *** PCMPCTL1 *** +#define FB_PCMPCTL1_PCMMOMP 6 +#define FM_PCMPCTL1_PCMMOMP 0x40 + +#define FB_PCMPCTL1_PCMSOP 5 +#define FM_PCMPCTL1_PCMSOP 0x20 +#define FV_PCMSOP_1 0x0 +#define FV_PCMSOP_2 0x20 + +#define FB_PCMPCTL1_PCMDSSP 3 +#define FM_PCMPCTL1_PCMDSSP 0x18 +#define FV_PCMDSSP_16 0x0 +#define FV_PCMDSSP_24 0x8 +#define FV_PCMDSSP_32 0x10 + +#define FB_PCMPCTL1_PCMMIMP 1 +#define FM_PCMPCTL1_PCMMIMP 0x2 + +#define FB_PCMPCTL1_PCMSIP 0 +#define FM_PCMPCTL1_PCMSIP 0x1 +#define FV_PCMSIP_1 0x0 +#define FV_PCMSIP_2 0x1 + +// *** CHAIC *** +#define FB_CHAIC_MICBST 4 +#define FM_CHAIC_MICBST 0x30 + +// *** PGACTL *** +#define FB_PGACTL_PGAMUTE 7 +#define FM_PGACTL_PGAMUTE 0x80 + +#define FB_PGACTL_PGAVOL 0 +#define FM_PGACTL_PGAVOL 0x3F + +// *** ICHVOL *** +#define FB_ICHVOL_ICHVOL 0 +#define FM_ICHVOL_ICHVOL 0xFF + +// *** SPKMBCMUG *** +#define FB_SPKMBCMUG_PHASE 5 +#define FM_SPKMBCMUG_PHASE 0x20 + +#define FB_SPKMBCMUG_MUGAIN 0 +#define FM_SPKMBCMUG_MUGAIN 0x1F + +// *** SPKMBCTHR *** +#define FB_SPKMBCTHR_THRESH 0 +#define FM_SPKMBCTHR_THRESH 0xFF + +// *** SPKMBCRAT *** +#define FB_SPKMBCRAT_RATIO 0 +#define FM_SPKMBCRAT_RATIO 0x1F + +// *** SPKMBCATKL *** +#define FB_SPKMBCATKL_TCATKL 0 +#define FM_SPKMBCATKL_TCATKL 0xFF + +// *** SPKMBCATKH *** +#define FB_SPKMBCATKH_TCATKH 0 +#define FM_SPKMBCATKH_TCATKH 0xFF + +// *** SPKMBCRELL *** +#define FB_SPKMBCRELL_TCRELL 0 +#define FM_SPKMBCRELL_TCRELL 0xFF + +// *** SPKMBCRELH *** +#define FB_SPKMBCRELH_TCRELH 0 +#define FM_SPKMBCRELH_TCRELH 0xFF + +// *** DACMBCMUG *** +#define FB_DACMBCMUG_PHASE 5 +#define FM_DACMBCMUG_PHASE 0x20 + +#define FB_DACMBCMUG_MUGAIN 0 +#define FM_DACMBCMUG_MUGAIN 0x1F + +// *** DACMBCTHR *** +#define FB_DACMBCTHR_THRESH 0 +#define FM_DACMBCTHR_THRESH 0xFF + +// *** DACMBCRAT *** +#define FB_DACMBCRAT_RATIO 0 +#define FM_DACMBCRAT_RATIO 0x1F + +// *** DACMBCATKL *** +#define FB_DACMBCATKL_TCATKL 0 +#define FM_DACMBCATKL_TCATKL 0xFF + +// *** DACMBCATKH *** +#define FB_DACMBCATKH_TCATKH 0 +#define FM_DACMBCATKH_TCATKH 0xFF + +// *** DACMBCRELL *** +#define FB_DACMBCRELL_TCRELL 0 +#define FM_DACMBCRELL_TCRELL 0xFF + +// *** DACMBCRELH *** +#define FB_DACMBCRELH_TCRELH 0 +#define FM_DACMBCRELH_TCRELH 0xFF + +// *** SUBMBCMUG *** +#define FB_SUBMBCMUG_PHASE 5 +#define FM_SUBMBCMUG_PHASE 0x20 + +#define FB_SUBMBCMUG_MUGAIN 0 +#define FM_SUBMBCMUG_MUGAIN 0x1F + +// *** SUBMBCTHR *** +#define FB_SUBMBCTHR_THRESH 0 +#define FM_SUBMBCTHR_THRESH 0xFF + +// *** SUBMBCRAT *** +#define FB_SUBMBCRAT_RATIO 0 +#define FM_SUBMBCRAT_RATIO 0x1F + +// *** SUBMBCATKL *** +#define FB_SUBMBCATKL_TCATKL 0 +#define FM_SUBMBCATKL_TCATKL 0xFF + +// *** SUBMBCATKH *** +#define FB_SUBMBCATKH_TCATKH 0 +#define FM_SUBMBCATKH_TCATKH 0xFF + +// *** SUBMBCRELL *** +#define FB_SUBMBCRELL_TCRELL 0 +#define FM_SUBMBCRELL_TCRELL 0xFF + +// *** SUBMBCRELH *** +#define FB_SUBMBCRELH_TCRELH 0 +#define FM_SUBMBCRELH_TCRELH 0xFF + +// *** PAGESEL *** +#define FB_PAGESEL_PAGESEL 0 +#define FM_PAGESEL_PAGESEL 0xFF + +// *** RESET *** +#define FB_RESET_RESET 0 +#define FM_RESET_RESET 0xFF +#define FV_RESET_PWR_ON_DEFAULTS 0x85 + +// *** IRQEN *** +#define FB_IRQEN_THRMINTEN 6 +#define FM_IRQEN_THRMINTEN 0x40 +#define FV_THRMINTEN_ENABLED 0x40 +#define FV_THRMINTEN_DISABLED 0x0 + +#define FB_IRQEN_HBPINTEN 5 +#define FM_IRQEN_HBPINTEN 0x20 +#define FV_HBPINTEN_ENABLED 0x20 +#define FV_HBPINTEN_DISABLED 0x0 + +#define FB_IRQEN_HSDINTEN 4 +#define FM_IRQEN_HSDINTEN 0x10 +#define FV_HSDINTEN_ENABLED 0x10 +#define FV_HSDINTEN_DISABLED 0x0 + +#define FB_IRQEN_HPDINTEN 3 +#define FM_IRQEN_HPDINTEN 0x8 +#define FV_HPDINTEN_ENABLED 0x8 +#define FV_HPDINTEN_DISABLED 0x0 + +#define FB_IRQEN_GPIO3INTEN 1 +#define FM_IRQEN_GPIO3INTEN 0x2 +#define FV_GPIO3INTEN_ENABLED 0x2 +#define FV_GPIO3INTEN_DISABLED 0x0 + +#define FB_IRQEN_GPIO2INTEN 0 +#define FM_IRQEN_GPIO2INTEN 0x1 +#define FV_GPIO2INTEN_ENABLED 0x1 +#define FV_GPIO2INTEN_DISABLED 0x0 + +#define IRQEN_GPIOINTEN_ENABLED 0x1 +#define IRQEN_GPIOINTEN_DISABLED 0x0 + +// *** IRQMASK *** +#define FB_IRQMASK_THRMIM 6 +#define FM_IRQMASK_THRMIM 0x40 +#define FV_THRMIM_MASKED 0x0 +#define FV_THRMIM_NOT_MASKED 0x40 + +#define FB_IRQMASK_HBPIM 5 +#define FM_IRQMASK_HBPIM 0x20 +#define FV_HBPIM_MASKED 0x0 +#define FV_HBPIM_NOT_MASKED 0x20 + +#define FB_IRQMASK_HSDIM 4 +#define FM_IRQMASK_HSDIM 0x10 +#define FV_HSDIM_MASKED 0x0 +#define FV_HSDIM_NOT_MASKED 0x10 + +#define FB_IRQMASK_HPDIM 3 +#define FM_IRQMASK_HPDIM 0x8 +#define FV_HPDIM_MASKED 0x0 +#define FV_HPDIM_NOT_MASKED 0x8 + +#define FB_IRQMASK_GPIO3M 1 +#define FM_IRQMASK_GPIO3M 0x2 +#define FV_GPIO3M_MASKED 0x0 +#define FV_GPIO3M_NOT_MASKED 0x2 + +#define FB_IRQMASK_GPIO2M 0 +#define FM_IRQMASK_GPIO2M 0x1 +#define FV_GPIO2M_MASKED 0x0 +#define FV_GPIO2M_NOT_MASKED 0x1 + +#define IRQMASK_GPIOM_MASKED 0x0 +#define IRQMASK_GPIOM_NOT_MASKED 0x1 + +// *** IRQSTAT *** +#define FB_IRQSTAT_THRMINT 6 +#define FM_IRQSTAT_THRMINT 0x40 +#define FV_THRMINT_INTERRUPTED 0x40 +#define FV_THRMINT_NOT_INTERRUPTED 0x0 + +#define FB_IRQSTAT_HBPINT 5 +#define FM_IRQSTAT_HBPINT 0x20 +#define FV_HBPINT_INTERRUPTED 0x20 +#define FV_HBPINT_NOT_INTERRUPTED 0x0 + +#define FB_IRQSTAT_HSDINT 4 +#define FM_IRQSTAT_HSDINT 0x10 +#define FV_HSDINT_INTERRUPTED 0x10 +#define FV_HSDINT_NOT_INTERRUPTED 0x0 + +#define FB_IRQSTAT_HPDINT 3 +#define FM_IRQSTAT_HPDINT 0x8 +#define FV_HPDINT_INTERRUPTED 0x8 +#define FV_HPDINT_NOT_INTERRUPTED 0x0 + +#define FB_IRQSTAT_GPIO3INT 1 +#define FM_IRQSTAT_GPIO3INT 0x2 +#define FV_GPIO3INT_INTERRUPTED 0x2 +#define FV_GPIO3INT_NOT_INTERRUPTED 0x0 + +#define FB_IRQSTAT_GPIO2INT 0 +#define FM_IRQSTAT_GPIO2INT 0x1 +#define FV_GPIO2INT_INTERRUPTED 0x1 +#define FV_GPIO2INT_NOT_INTERRUPTED 0x0 + +#define IRQSTAT_GPIOINT_INTERRUPTED 0x1 +#define IRQSTAT_GPIOINT_NOT_INTERRUPTED 0x0 + +// *** DEVADD0 *** +#define FB_DEVADD0_DEVADD0 1 +#define FM_DEVADD0_DEVADD0 0xFE + +#define FB_DEVADD0_I2C_ADDRLK 0 +#define FM_DEVADD0_I2C_ADDRLK 0x1 +#define FV_I2C_ADDRLK_LOCK 0x1 + +// *** DEVID *** +#define FB_DEVID_DEV_ID 0 +#define FM_DEVID_DEV_ID 0xFF + +// *** DEVREV *** +#define FB_DEVREV_MAJ_REV 4 +#define FM_DEVREV_MAJ_REV 0xF0 + +#define FB_DEVREV_MIN_REV 0 +#define FM_DEVREV_MIN_REV 0xF + +// *** PLLSTAT *** +#define FB_PLLSTAT_PLL2LK 1 +#define FM_PLLSTAT_PLL2LK 0x2 +#define FV_PLL2LK_LOCKED 0x2 +#define FV_PLL2LK_UNLOCKED 0x0 + +#define FB_PLLSTAT_PLL1LK 0 +#define FM_PLLSTAT_PLL1LK 0x1 +#define FV_PLL1LK_LOCKED 0x1 +#define FV_PLL1LK_UNLOCKED 0x0 + +#define PLLSTAT_PLLLK_LOCKED 0x1 +#define PLLSTAT_PLLLK_UNLOCKED 0x0 + +// *** PLLCTL *** +#define FB_PLLCTL_PU_PLL2 7 +#define FM_PLLCTL_PU_PLL2 0x80 +#define FV_PU_PLL2_PWR_UP 0x80 +#define FV_PU_PLL2_PWR_DWN 0x0 + +#define FB_PLLCTL_PU_PLL1 6 +#define FM_PLLCTL_PU_PLL1 0x40 +#define FV_PU_PLL1_PWR_UP 0x40 +#define FV_PU_PLL1_PWR_DWN 0x0 + +#define FB_PLLCTL_PLL2CLKEN 5 +#define FM_PLLCTL_PLL2CLKEN 0x20 +#define FV_PLL2CLKEN_ENABLE 0x20 +#define FV_PLL2CLKEN_DISABLE 0x0 + +#define FB_PLLCTL_PLL1CLKEN 4 +#define FM_PLLCTL_PLL1CLKEN 0x10 +#define FV_PLL1CLKEN_ENABLE 0x10 +#define FV_PLL1CLKEN_DISABLE 0x0 + +#define FB_PLLCTL_BCLKSEL 2 +#define FM_PLLCTL_BCLKSEL 0xC +#define FV_BCLKSEL_BCLK1 0x0 +#define FV_BCLKSEL_BCLK2 0x4 +#define FV_BCLKSEL_BCLK3 0x8 + +#define FB_PLLCTL_PLLISEL 0 +#define FM_PLLCTL_PLLISEL 0x3 +#define FV_PLLISEL_XTAL 0x0 +#define FV_PLLISEL_MCLK1 0x1 +#define FV_PLLISEL_MCLK2 0x2 +#define FV_PLLISEL_BCLK 0x3 + +#define PLLCTL_PU_PLL_PWR_UP 0x1 +#define PLLCTL_PU_PLL_PWR_DWN 0x0 +#define PLLCTL_PLLCLKEN_ENABLE 0x1 +#define PLLCTL_PLLCLKEN_DISABLE 0x0 + +// *** ISRC *** +#define FB_ISRC_IBR 2 +#define FM_ISRC_IBR 0x4 +#define FV_IBR_44PT1 0x0 +#define FV_IBR_48 0x4 + +#define FB_ISRC_IBM 0 +#define FM_ISRC_IBM 0x3 +#define FV_IBM_0PT25 0x0 +#define FV_IBM_0PT5 0x1 +#define FV_IBM_1 0x2 +#define FV_IBM_2 0x3 + +// *** SCLKCTL *** +#define FB_SCLKCTL_ASDM 6 +#define FM_SCLKCTL_ASDM 0xC0 +#define FV_ASDM_HALF 0x40 +#define FV_ASDM_FULL 0x80 +#define FV_ASDM_AUTO 0xC0 + +#define FB_SCLKCTL_DSDM 4 +#define FM_SCLKCTL_DSDM 0x30 +#define FV_DSDM_HALF 0x10 +#define FV_DSDM_FULL 0x20 +#define FV_DSDM_AUTO 0x30 + +// *** TIMEBASE *** +#define FB_TIMEBASE_TIMEBASE 0 +#define FM_TIMEBASE_TIMEBASE 0xFF + +// *** I2SCMC *** +#define FB_I2SCMC_BCMP3 4 +#define FM_I2SCMC_BCMP3 0x30 +#define FV_BCMP3_AUTO 0x0 +#define FV_BCMP3_32X 0x10 +#define FV_BCMP3_40X 0x20 +#define FV_BCMP3_64X 0x30 + +#define FB_I2SCMC_BCMP2 2 +#define FM_I2SCMC_BCMP2 0xC +#define FV_BCMP2_AUTO 0x0 +#define FV_BCMP2_32X 0x4 +#define FV_BCMP2_40X 0x8 +#define FV_BCMP2_64X 0xC + +#define FB_I2SCMC_BCMP1 0 +#define FM_I2SCMC_BCMP1 0x3 +#define FV_BCMP1_AUTO 0x0 +#define FV_BCMP1_32X 0x1 +#define FV_BCMP1_40X 0x2 +#define FV_BCMP1_64X 0x3 + +#define I2SCMC_BCMP_AUTO 0x0 +#define I2SCMC_BCMP_32X 0x1 +#define I2SCMC_BCMP_40X 0x2 +#define I2SCMC_BCMP_64X 0x3 + +// *** MCLK2PINC *** +#define FB_MCLK2PINC_SLEWOUT 4 +#define FM_MCLK2PINC_SLEWOUT 0xF0 + +#define FB_MCLK2PINC_MCLK2IO 2 +#define FM_MCLK2PINC_MCLK2IO 0x4 +#define FV_MCLK2IO_INPUT 0x0 +#define FV_MCLK2IO_OUTPUT 0x4 + +#define FB_MCLK2PINC_MCLK2OS 0 +#define FM_MCLK2PINC_MCLK2OS 0x3 +#define FV_MCLK2OS_24PT576 0x0 +#define FV_MCLK2OS_22PT5792 0x1 +#define FV_MCLK2OS_PLL2 0x2 + +// *** I2SPINC0 *** +#define FB_I2SPINC0_SDO3TRI 7 +#define FM_I2SPINC0_SDO3TRI 0x80 + +#define FB_I2SPINC0_SDO2TRI 6 +#define FM_I2SPINC0_SDO2TRI 0x40 + +#define FB_I2SPINC0_SDO1TRI 5 +#define FM_I2SPINC0_SDO1TRI 0x20 + +#define FB_I2SPINC0_PCM3TRI 2 +#define FM_I2SPINC0_PCM3TRI 0x4 + +#define FB_I2SPINC0_PCM2TRI 1 +#define FM_I2SPINC0_PCM2TRI 0x2 + +#define FB_I2SPINC0_PCM1TRI 0 +#define FM_I2SPINC0_PCM1TRI 0x1 + +// *** I2SPINC1 *** +#define FB_I2SPINC1_SDO3PDD 2 +#define FM_I2SPINC1_SDO3PDD 0x4 + +#define FB_I2SPINC1_SDO2PDD 1 +#define FM_I2SPINC1_SDO2PDD 0x2 + +#define FB_I2SPINC1_SDO1PDD 0 +#define FM_I2SPINC1_SDO1PDD 0x1 + +// *** I2SPINC2 *** +#define FB_I2SPINC2_LR3PDD 5 +#define FM_I2SPINC2_LR3PDD 0x20 + +#define FB_I2SPINC2_BC3PDD 4 +#define FM_I2SPINC2_BC3PDD 0x10 + +#define FB_I2SPINC2_LR2PDD 3 +#define FM_I2SPINC2_LR2PDD 0x8 + +#define FB_I2SPINC2_BC2PDD 2 +#define FM_I2SPINC2_BC2PDD 0x4 + +#define FB_I2SPINC2_LR1PDD 1 +#define FM_I2SPINC2_LR1PDD 0x2 + +#define FB_I2SPINC2_BC1PDD 0 +#define FM_I2SPINC2_BC1PDD 0x1 + +// *** GPIOCTL0 *** +#define FB_GPIOCTL0_GPIO3INTP 7 +#define FM_GPIOCTL0_GPIO3INTP 0x80 + +#define FB_GPIOCTL0_GPIO2INTP 6 +#define FM_GPIOCTL0_GPIO2INTP 0x40 + +#define FB_GPIOCTL0_GPIO3CFG 5 +#define FM_GPIOCTL0_GPIO3CFG 0x20 + +#define FB_GPIOCTL0_GPIO2CFG 4 +#define FM_GPIOCTL0_GPIO2CFG 0x10 + +#define FB_GPIOCTL0_GPIO3IO 3 +#define FM_GPIOCTL0_GPIO3IO 0x8 + +#define FB_GPIOCTL0_GPIO2IO 2 +#define FM_GPIOCTL0_GPIO2IO 0x4 + +#define FB_GPIOCTL0_GPIO1IO 1 +#define FM_GPIOCTL0_GPIO1IO 0x2 + +#define FB_GPIOCTL0_GPIO0IO 0 +#define FM_GPIOCTL0_GPIO0IO 0x1 + +// *** GPIOCTL1 *** +#define FB_GPIOCTL1_GPIO3 7 +#define FM_GPIOCTL1_GPIO3 0x80 + +#define FB_GPIOCTL1_GPIO2 6 +#define FM_GPIOCTL1_GPIO2 0x40 + +#define FB_GPIOCTL1_GPIO1 5 +#define FM_GPIOCTL1_GPIO1 0x20 + +#define FB_GPIOCTL1_GPIO0 4 +#define FM_GPIOCTL1_GPIO0 0x10 + +#define FB_GPIOCTL1_GPIO3RD 3 +#define FM_GPIOCTL1_GPIO3RD 0x8 + +#define FB_GPIOCTL1_GPIO2RD 2 +#define FM_GPIOCTL1_GPIO2RD 0x4 + +#define FB_GPIOCTL1_GPIO1RD 1 +#define FM_GPIOCTL1_GPIO1RD 0x2 + +#define FB_GPIOCTL1_GPIO0RD 0 +#define FM_GPIOCTL1_GPIO0RD 0x1 + +// *** ASRC *** +#define FB_ASRC_ASRCOBW 7 +#define FM_ASRC_ASRCOBW 0x80 + +#define FB_ASRC_ASRCIBW 6 +#define FM_ASRC_ASRCIBW 0x40 + +#define FB_ASRC_ASRCOB 5 +#define FM_ASRC_ASRCOB 0x20 +#define FV_ASRCOB_ACTIVE 0x0 +#define FV_ASRCOB_BYPASSED 0x20 + +#define FB_ASRC_ASRCIB 4 +#define FM_ASRC_ASRCIB 0x10 +#define FV_ASRCIB_ACTIVE 0x0 +#define FV_ASRCIB_BYPASSED 0x10 + +#define FB_ASRC_ASRCOL 3 +#define FM_ASRC_ASRCOL 0x8 + +#define FB_ASRC_ASRCIL 2 +#define FM_ASRC_ASRCIL 0x4 + +// *** TDMCTL0 *** +#define FB_TDMCTL0_TDMMD 2 +#define FM_TDMCTL0_TDMMD 0x4 +#define FV_TDMMD_200 0x0 +#define FV_TDMMD_256 0x4 + +#define FB_TDMCTL0_SLSYNC 1 +#define FM_TDMCTL0_SLSYNC 0x2 +#define FV_SLSYNC_SHORT 0x0 +#define FV_SLSYNC_LONG 0x2 + +#define FB_TDMCTL0_BDELAY 0 +#define FM_TDMCTL0_BDELAY 0x1 +#define FV_BDELAY_NO_DELAY 0x0 +#define FV_BDELAY_1BCLK_DELAY 0x1 + +// *** TDMCTL1 *** +#define FB_TDMCTL1_TDMSO 5 +#define FM_TDMCTL1_TDMSO 0x60 +#define FV_TDMSO_2 0x0 +#define FV_TDMSO_4 0x20 +#define FV_TDMSO_6 0x40 + +#define FB_TDMCTL1_TDMDSS 3 +#define FM_TDMCTL1_TDMDSS 0x18 +#define FV_TDMDSS_16 0x0 +#define FV_TDMDSS_24 0x10 +#define FV_TDMDSS_32 0x18 + +#define FB_TDMCTL1_TDMSI 0 +#define FM_TDMCTL1_TDMSI 0x3 +#define FV_TDMSI_2 0x0 +#define FV_TDMSI_4 0x1 +#define FV_TDMSI_6 0x2 + +// *** PWRM0 *** +#define FB_PWRM0_INPROC3PU 6 +#define FM_PWRM0_INPROC3PU 0x40 + +#define FB_PWRM0_INPROC2PU 5 +#define FM_PWRM0_INPROC2PU 0x20 + +#define FB_PWRM0_INPROC1PU 4 +#define FM_PWRM0_INPROC1PU 0x10 + +#define FB_PWRM0_INPROC0PU 3 +#define FM_PWRM0_INPROC0PU 0x8 + +#define FB_PWRM0_MICB2PU 2 +#define FM_PWRM0_MICB2PU 0x4 + +#define FB_PWRM0_MICB1PU 1 +#define FM_PWRM0_MICB1PU 0x2 + +#define FB_PWRM0_MCLKPEN 0 +#define FM_PWRM0_MCLKPEN 0x1 + +// *** PWRM1 *** +#define FB_PWRM1_SUBPU 7 +#define FM_PWRM1_SUBPU 0x80 + +#define FB_PWRM1_HPLPU 6 +#define FM_PWRM1_HPLPU 0x40 + +#define FB_PWRM1_HPRPU 5 +#define FM_PWRM1_HPRPU 0x20 + +#define FB_PWRM1_SPKLPU 4 +#define FM_PWRM1_SPKLPU 0x10 + +#define FB_PWRM1_SPKRPU 3 +#define FM_PWRM1_SPKRPU 0x8 + +#define FB_PWRM1_D2S2PU 2 +#define FM_PWRM1_D2S2PU 0x4 + +#define FB_PWRM1_D2S1PU 1 +#define FM_PWRM1_D2S1PU 0x2 + +#define FB_PWRM1_VREFPU 0 +#define FM_PWRM1_VREFPU 0x1 + +// *** PWRM2 *** +#define FB_PWRM2_I2S3OPU 5 +#define FM_PWRM2_I2S3OPU 0x20 +#define FV_I2S3OPU_PWR_DOWN 0x0 +#define FV_I2S3OPU_PWR_UP 0x20 + +#define FB_PWRM2_I2S2OPU 4 +#define FM_PWRM2_I2S2OPU 0x10 +#define FV_I2S2OPU_PWR_DOWN 0x0 +#define FV_I2S2OPU_PWR_UP 0x10 + +#define FB_PWRM2_I2S1OPU 3 +#define FM_PWRM2_I2S1OPU 0x8 +#define FV_I2S1OPU_PWR_DOWN 0x0 +#define FV_I2S1OPU_PWR_UP 0x8 + +#define FB_PWRM2_I2S3IPU 2 +#define FM_PWRM2_I2S3IPU 0x4 +#define FV_I2S3IPU_PWR_DOWN 0x0 +#define FV_I2S3IPU_PWR_UP 0x4 + +#define FB_PWRM2_I2S2IPU 1 +#define FM_PWRM2_I2S2IPU 0x2 +#define FV_I2S2IPU_PWR_DOWN 0x0 +#define FV_I2S2IPU_PWR_UP 0x2 + +#define FB_PWRM2_I2S1IPU 0 +#define FM_PWRM2_I2S1IPU 0x1 +#define FV_I2S1IPU_PWR_DOWN 0x0 +#define FV_I2S1IPU_PWR_UP 0x1 + +#define PWRM2_I2SOPU_PWR_DOWN 0x0 +#define PWRM2_I2SOPU_PWR_UP 0x1 +#define PWRM2_I2SIPU_PWR_DOWN 0x0 +#define PWRM2_I2SIPU_PWR_UP 0x1 + +// *** PWRM3 *** +#define FB_PWRM3_BGSBUP 6 +#define FM_PWRM3_BGSBUP 0x40 +#define FV_BGSBUP_ON 0x0 +#define FV_BGSBUP_OFF 0x40 + +#define FB_PWRM3_VGBAPU 5 +#define FM_PWRM3_VGBAPU 0x20 +#define FV_VGBAPU_ON 0x0 +#define FV_VGBAPU_OFF 0x20 + +#define FB_PWRM3_LLINEPU 4 +#define FM_PWRM3_LLINEPU 0x10 + +#define FB_PWRM3_RLINEPU 3 +#define FM_PWRM3_RLINEPU 0x8 + +// *** PWRM4 *** +#define FB_PWRM4_OPSUBPU 4 +#define FM_PWRM4_OPSUBPU 0x10 + +#define FB_PWRM4_OPDACLPU 3 +#define FM_PWRM4_OPDACLPU 0x8 + +#define FB_PWRM4_OPDACRPU 2 +#define FM_PWRM4_OPDACRPU 0x4 + +#define FB_PWRM4_OPSPKLPU 1 +#define FM_PWRM4_OPSPKLPU 0x2 + +#define FB_PWRM4_OPSPKRPU 0 +#define FM_PWRM4_OPSPKRPU 0x1 + +// *** I2SIDCTL *** +#define FB_I2SIDCTL_I2SI3DCTL 4 +#define FM_I2SIDCTL_I2SI3DCTL 0x30 + +#define FB_I2SIDCTL_I2SI2DCTL 2 +#define FM_I2SIDCTL_I2SI2DCTL 0xC + +#define FB_I2SIDCTL_I2SI1DCTL 0 +#define FM_I2SIDCTL_I2SI1DCTL 0x3 + +// *** I2SODCTL *** +#define FB_I2SODCTL_I2SO3DCTL 4 +#define FM_I2SODCTL_I2SO3DCTL 0x30 + +#define FB_I2SODCTL_I2SO2DCTL 2 +#define FM_I2SODCTL_I2SO2DCTL 0xC + +#define FB_I2SODCTL_I2SO1DCTL 0 +#define FM_I2SODCTL_I2SO1DCTL 0x3 + +// *** AUDIOMUX1 *** +#define FB_AUDIOMUX1_ASRCIMUX 6 +#define FM_AUDIOMUX1_ASRCIMUX 0xC0 +#define FV_ASRCIMUX_NONE 0x0 +#define FV_ASRCIMUX_I2S1 0x40 +#define FV_ASRCIMUX_I2S2 0x80 +#define FV_ASRCIMUX_I2S3 0xC0 + +#define FB_AUDIOMUX1_I2S2MUX 3 +#define FM_AUDIOMUX1_I2S2MUX 0x38 +#define FV_I2S2MUX_I2S1 0x0 +#define FV_I2S2MUX_I2S2 0x8 +#define FV_I2S2MUX_I2S3 0x10 +#define FV_I2S2MUX_ADC_DMIC 0x18 +#define FV_I2S2MUX_DMIC2 0x20 +#define FV_I2S2MUX_CLASSD_DSP 0x28 +#define FV_I2S2MUX_DAC_DSP 0x30 +#define FV_I2S2MUX_SUB_DSP 0x38 + +#define FB_AUDIOMUX1_I2S1MUX 0 +#define FM_AUDIOMUX1_I2S1MUX 0x7 +#define FV_I2S1MUX_I2S1 0x0 +#define FV_I2S1MUX_I2S2 0x1 +#define FV_I2S1MUX_I2S3 0x2 +#define FV_I2S1MUX_ADC_DMIC 0x3 +#define FV_I2S1MUX_DMIC2 0x4 +#define FV_I2S1MUX_CLASSD_DSP 0x5 +#define FV_I2S1MUX_DAC_DSP 0x6 +#define FV_I2S1MUX_SUB_DSP 0x7 + +#define AUDIOMUX1_I2SMUX_I2S1 0x0 +#define AUDIOMUX1_I2SMUX_I2S2 0x1 +#define AUDIOMUX1_I2SMUX_I2S3 0x2 +#define AUDIOMUX1_I2SMUX_ADC_DMIC 0x3 +#define AUDIOMUX1_I2SMUX_DMIC2 0x4 +#define AUDIOMUX1_I2SMUX_CLASSD_DSP 0x5 +#define AUDIOMUX1_I2SMUX_DAC_DSP 0x6 +#define AUDIOMUX1_I2SMUX_SUB_DSP 0x7 + +// *** AUDIOMUX2 *** +#define FB_AUDIOMUX2_ASRCOMUX 6 +#define FM_AUDIOMUX2_ASRCOMUX 0xC0 +#define FV_ASRCOMUX_NONE 0x0 +#define FV_ASRCOMUX_I2S1 0x40 +#define FV_ASRCOMUX_I2S2 0x80 +#define FV_ASRCOMUX_I2S3 0xC0 + +#define FB_AUDIOMUX2_DACMUX 3 +#define FM_AUDIOMUX2_DACMUX 0x38 +#define FV_DACMUX_I2S1 0x0 +#define FV_DACMUX_I2S2 0x8 +#define FV_DACMUX_I2S3 0x10 +#define FV_DACMUX_ADC_DMIC 0x18 +#define FV_DACMUX_DMIC2 0x20 +#define FV_DACMUX_CLASSD_DSP 0x28 +#define FV_DACMUX_DAC_DSP 0x30 +#define FV_DACMUX_SUB_DSP 0x38 + +#define FB_AUDIOMUX2_I2S3MUX 0 +#define FM_AUDIOMUX2_I2S3MUX 0x7 +#define FV_I2S3MUX_I2S1 0x0 +#define FV_I2S3MUX_I2S2 0x1 +#define FV_I2S3MUX_I2S3 0x2 +#define FV_I2S3MUX_ADC_DMIC 0x3 +#define FV_I2S3MUX_DMIC2 0x4 +#define FV_I2S3MUX_CLASSD_DSP 0x5 +#define FV_I2S3MUX_DAC_DSP 0x6 +#define FV_I2S3MUX_SUB_DSP 0x7 + +// *** AUDIOMUX3 *** +#define FB_AUDIOMUX3_SUBMUX 3 +#define FM_AUDIOMUX3_SUBMUX 0xF8 +#define FV_SUBMUX_I2S1_L 0x0 +#define FV_SUBMUX_I2S1_R 0x8 +#define FV_SUBMUX_I2S1_LR 0x10 +#define FV_SUBMUX_I2S2_L 0x18 +#define FV_SUBMUX_I2S2_R 0x20 +#define FV_SUBMUX_I2S2_LR 0x28 +#define FV_SUBMUX_I2S3_L 0x30 +#define FV_SUBMUX_I2S3_R 0x38 +#define FV_SUBMUX_I2S3_LR 0x40 +#define FV_SUBMUX_ADC_DMIC_L 0x48 +#define FV_SUBMUX_ADC_DMIC_R 0x50 +#define FV_SUBMUX_ADC_DMIC_LR 0x58 +#define FV_SUBMUX_DMIC_L 0x60 +#define FV_SUBMUX_DMIC_R 0x68 +#define FV_SUBMUX_DMIC_LR 0x70 +#define FV_SUBMUX_CLASSD_DSP_L 0x78 +#define FV_SUBMUX_CLASSD_DSP_R 0x80 +#define FV_SUBMUX_CLASSD_DSP_LR 0x88 + +#define FB_AUDIOMUX3_CLSSDMUX 0 +#define FM_AUDIOMUX3_CLSSDMUX 0x7 +#define FV_CLSSDMUX_I2S1 0x0 +#define FV_CLSSDMUX_I2S2 0x1 +#define FV_CLSSDMUX_I2S3 0x2 +#define FV_CLSSDMUX_ADC_DMIC 0x3 +#define FV_CLSSDMUX_DMIC2 0x4 +#define FV_CLSSDMUX_CLASSD_DSP 0x5 +#define FV_CLSSDMUX_DAC_DSP 0x6 +#define FV_CLSSDMUX_SUB_DSP 0x7 + +// *** HSDCTL1 *** +#define FB_HSDCTL1_HPJKTYPE 7 +#define FM_HSDCTL1_HPJKTYPE 0x80 + +#define FB_HSDCTL1_CON_DET_PWD 6 +#define FM_HSDCTL1_CON_DET_PWD 0x40 + +#define FB_HSDCTL1_DETCYC 4 +#define FM_HSDCTL1_DETCYC 0x30 + +#define FB_HSDCTL1_HPDLYBYP 3 +#define FM_HSDCTL1_HPDLYBYP 0x8 + +#define FB_HSDCTL1_HSDETPOL 2 +#define FM_HSDCTL1_HSDETPOL 0x4 + +#define FB_HSDCTL1_HPID_EN 1 +#define FM_HSDCTL1_HPID_EN 0x2 + +#define FB_HSDCTL1_GBLHS_EN 0 +#define FM_HSDCTL1_GBLHS_EN 0x1 + +// *** HSDCTL2 *** +#define FB_HSDCTL2_FMICBIAS1 6 +#define FM_HSDCTL2_FMICBIAS1 0xC0 + +#define FB_HSDCTL2_MB1MODE 5 +#define FM_HSDCTL2_MB1MODE 0x20 +#define FV_MB1MODE_AUTO 0x0 +#define FV_MB1MODE_MANUAL 0x20 + +#define FB_HSDCTL2_FORCETRG 4 +#define FM_HSDCTL2_FORCETRG 0x10 + +#define FB_HSDCTL2_SWMODE 3 +#define FM_HSDCTL2_SWMODE 0x8 + +#define FB_HSDCTL2_GHSHIZ 2 +#define FM_HSDCTL2_GHSHIZ 0x4 + +#define FB_HSDCTL2_FPLUGTYPE 0 +#define FM_HSDCTL2_FPLUGTYPE 0x3 + +// *** HSDSTAT *** +#define FB_HSDSTAT_MBIAS1DRV 5 +#define FM_HSDSTAT_MBIAS1DRV 0x60 + +#define FB_HSDSTAT_HSDETSTAT 3 +#define FM_HSDSTAT_HSDETSTAT 0x8 + +#define FB_HSDSTAT_PLUGTYPE 1 +#define FM_HSDSTAT_PLUGTYPE 0x6 + +#define FB_HSDSTAT_HSDETDONE 0 +#define FM_HSDSTAT_HSDETDONE 0x1 + +// *** HSDDELAY *** +#define FB_HSDDELAY_T_STABLE 0 +#define FM_HSDDELAY_T_STABLE 0x7 + +// *** BUTCTL *** +#define FB_BUTCTL_BPUSHSTAT 7 +#define FM_BUTCTL_BPUSHSTAT 0x80 + +#define FB_BUTCTL_BPUSHDET 6 +#define FM_BUTCTL_BPUSHDET 0x40 + +#define FB_BUTCTL_BPUSHEN 5 +#define FM_BUTCTL_BPUSHEN 0x20 + +#define FB_BUTCTL_BSTABLE_L 3 +#define FM_BUTCTL_BSTABLE_L 0x18 + +#define FB_BUTCTL_BSTABLE_S 0 +#define FM_BUTCTL_BSTABLE_S 0x7 + +// *** CH0AIC *** +#define FB_CH0AIC_INSELL 6 +#define FM_CH0AIC_INSELL 0xC0 + +#define FB_CH0AIC_MICBST0 4 +#define FM_CH0AIC_MICBST0 0x30 + +#define FB_CH0AIC_LADCIN 2 +#define FM_CH0AIC_LADCIN 0xC + +#define FB_CH0AIC_IN_BYPS_L_SEL 1 +#define FM_CH0AIC_IN_BYPS_L_SEL 0x2 + +#define FB_CH0AIC_IPCH0S 0 +#define FM_CH0AIC_IPCH0S 0x1 + +// *** CH1AIC *** +#define FB_CH1AIC_INSELR 6 +#define FM_CH1AIC_INSELR 0xC0 + +#define FB_CH1AIC_MICBST1 4 +#define FM_CH1AIC_MICBST1 0x30 + +#define FB_CH1AIC_RADCIN 2 +#define FM_CH1AIC_RADCIN 0xC + +#define FB_CH1AIC_IN_BYPS_R_SEL 1 +#define FM_CH1AIC_IN_BYPS_R_SEL 0x2 + +#define FB_CH1AIC_IPCH1S 0 +#define FM_CH1AIC_IPCH1S 0x1 + +// *** ICTL0 *** +#define FB_ICTL0_IN1POL 7 +#define FM_ICTL0_IN1POL 0x80 + +#define FB_ICTL0_IN0POL 6 +#define FM_ICTL0_IN0POL 0x40 + +#define FB_ICTL0_INPCH10SEL 4 +#define FM_ICTL0_INPCH10SEL 0x30 + +#define FB_ICTL0_IN1MUTE 3 +#define FM_ICTL0_IN1MUTE 0x8 + +#define FB_ICTL0_IN0MUTE 2 +#define FM_ICTL0_IN0MUTE 0x4 + +#define FB_ICTL0_IN1HP 1 +#define FM_ICTL0_IN1HP 0x2 + +#define FB_ICTL0_IN0HP 0 +#define FM_ICTL0_IN0HP 0x1 + +// *** ICTL1 *** +#define FB_ICTL1_IN3POL 7 +#define FM_ICTL1_IN3POL 0x80 + +#define FB_ICTL1_IN2POL 6 +#define FM_ICTL1_IN2POL 0x40 + +#define FB_ICTL1_INPCH32SEL 4 +#define FM_ICTL1_INPCH32SEL 0x30 + +#define FB_ICTL1_IN3MUTE 3 +#define FM_ICTL1_IN3MUTE 0x8 + +#define FB_ICTL1_IN2MUTE 2 +#define FM_ICTL1_IN2MUTE 0x4 + +#define FB_ICTL1_IN3HP 1 +#define FM_ICTL1_IN3HP 0x2 + +#define FB_ICTL1_IN2HP 0 +#define FM_ICTL1_IN2HP 0x1 + +// *** MICBIAS *** +#define FB_MICBIAS_MICBOV2 4 +#define FM_MICBIAS_MICBOV2 0x30 + +#define FB_MICBIAS_MICBOV1 6 +#define FM_MICBIAS_MICBOV1 0xC0 + +#define FB_MICBIAS_SPARE1 2 +#define FM_MICBIAS_SPARE1 0xC + +#define FB_MICBIAS_SPARE2 0 +#define FM_MICBIAS_SPARE2 0x3 + +// *** PGAZ *** +#define FB_PGAZ_INHPOR 1 +#define FM_PGAZ_INHPOR 0x2 + +#define FB_PGAZ_TOEN 0 +#define FM_PGAZ_TOEN 0x1 + +// *** ASRCILVOL *** +#define FB_ASRCILVOL_ASRCILVOL 0 +#define FM_ASRCILVOL_ASRCILVOL 0xFF + +// *** ASRCIRVOL *** +#define FB_ASRCIRVOL_ASRCIRVOL 0 +#define FM_ASRCIRVOL_ASRCIRVOL 0xFF + +// *** ASRCOLVOL *** +#define FB_ASRCOLVOL_ASRCOLVOL 0 +#define FM_ASRCOLVOL_ASRCOLVOL 0xFF + +// *** ASRCORVOL *** +#define FB_ASRCORVOL_ASRCOLVOL 0 +#define FM_ASRCORVOL_ASRCOLVOL 0xFF + +// *** IVOLCTLU *** +#define FB_IVOLCTLU_IFADE 3 +#define FM_IVOLCTLU_IFADE 0x8 + +#define FB_IVOLCTLU_INPVOLU 2 +#define FM_IVOLCTLU_INPVOLU 0x4 + +#define FB_IVOLCTLU_PGAVOLU 1 +#define FM_IVOLCTLU_PGAVOLU 0x2 + +#define FB_IVOLCTLU_ASRCVOLU 0 +#define FM_IVOLCTLU_ASRCVOLU 0x1 + +// *** ALCCTL0 *** +#define FB_ALCCTL0_ALCMODE 7 +#define FM_ALCCTL0_ALCMODE 0x80 + +#define FB_ALCCTL0_ALCREF 4 +#define FM_ALCCTL0_ALCREF 0x70 + +#define FB_ALCCTL0_ALCEN3 3 +#define FM_ALCCTL0_ALCEN3 0x8 + +#define FB_ALCCTL0_ALCEN2 2 +#define FM_ALCCTL0_ALCEN2 0x4 + +#define FB_ALCCTL0_ALCEN1 1 +#define FM_ALCCTL0_ALCEN1 0x2 + +#define FB_ALCCTL0_ALCEN0 0 +#define FM_ALCCTL0_ALCEN0 0x1 + +// *** ALCCTL1 *** +#define FB_ALCCTL1_MAXGAIN 4 +#define FM_ALCCTL1_MAXGAIN 0x70 + +#define FB_ALCCTL1_ALCL 0 +#define FM_ALCCTL1_ALCL 0xF + +// *** ALCCTL2 *** +#define FB_ALCCTL2_ALCZC 7 +#define FM_ALCCTL2_ALCZC 0x80 + +#define FB_ALCCTL2_MINGAIN 4 +#define FM_ALCCTL2_MINGAIN 0x70 + +#define FB_ALCCTL2_HLD 0 +#define FM_ALCCTL2_HLD 0xF + +// *** ALCCTL3 *** +#define FB_ALCCTL3_DCY 4 +#define FM_ALCCTL3_DCY 0xF0 + +#define FB_ALCCTL3_ATK 0 +#define FM_ALCCTL3_ATK 0xF + +// *** NGATE *** +#define FB_NGATE_NGTH 3 +#define FM_NGATE_NGTH 0xF8 + +#define FB_NGATE_NGG 1 +#define FM_NGATE_NGG 0x6 + +#define FB_NGATE_NGAT 0 +#define FM_NGATE_NGAT 0x1 + +// *** DMICCTL *** +#define FB_DMICCTL_DMIC2EN 7 +#define FM_DMICCTL_DMIC2EN 0x80 + +#define FB_DMICCTL_DMIC1EN 6 +#define FM_DMICCTL_DMIC1EN 0x40 + +#define FB_DMICCTL_DMONO 4 +#define FM_DMICCTL_DMONO 0x10 + +#define FB_DMICCTL_DMDCLK 2 +#define FM_DMICCTL_DMDCLK 0xC + +#define FB_DMICCTL_DMRATE 0 +#define FM_DMICCTL_DMRATE 0x3 + +// *** DACCTL *** +#define FB_DACCTL_DACPOLR 7 +#define FM_DACCTL_DACPOLR 0x80 +#define FV_DACPOLR_NORMAL 0x0 +#define FV_DACPOLR_INVERTED 0x80 + +#define FB_DACCTL_DACPOLL 6 +#define FM_DACCTL_DACPOLL 0x40 +#define FV_DACPOLL_NORMAL 0x0 +#define FV_DACPOLL_INVERTED 0x40 + +#define FB_DACCTL_DACDITH 4 +#define FM_DACCTL_DACDITH 0x30 +#define FV_DACDITH_DYNAMIC_HALF 0x0 +#define FV_DACDITH_DYNAMIC_FULL 0x10 +#define FV_DACDITH_DISABLED 0x20 +#define FV_DACDITH_STATIC 0x30 + +#define FB_DACCTL_DACMUTE 3 +#define FM_DACCTL_DACMUTE 0x8 +#define FV_DACMUTE_ENABLE 0x8 +#define FV_DACMUTE_DISABLE 0x0 + +#define FB_DACCTL_DACDEM 2 +#define FM_DACCTL_DACDEM 0x4 +#define FV_DACDEM_ENABLE 0x4 +#define FV_DACDEM_DISABLE 0x0 + +#define FB_DACCTL_ABYPASS 0 +#define FM_DACCTL_ABYPASS 0x1 + +// *** SPKCTL *** +#define FB_SPKCTL_SPKPOLR 7 +#define FM_SPKCTL_SPKPOLR 0x80 +#define FV_SPKPOLR_NORMAL 0x0 +#define FV_SPKPOLR_INVERTED 0x80 + +#define FB_SPKCTL_SPKPOLL 6 +#define FM_SPKCTL_SPKPOLL 0x40 +#define FV_SPKPOLL_NORMAL 0x0 +#define FV_SPKPOLL_INVERTED 0x40 + +#define FB_SPKCTL_SPKMUTE 3 +#define FM_SPKCTL_SPKMUTE 0x8 +#define FV_SPKMUTE_ENABLE 0x8 +#define FV_SPKMUTE_DISABLE 0x0 + +#define FB_SPKCTL_SPKDEM 2 +#define FM_SPKCTL_SPKDEM 0x4 +#define FV_SPKDEM_ENABLE 0x4 +#define FV_SPKDEM_DISABLE 0x0 + +// *** SUBCTL *** +#define FB_SUBCTL_SUBPOL 7 +#define FM_SUBCTL_SUBPOL 0x80 + +#define FB_SUBCTL_SUBMUTE 3 +#define FM_SUBCTL_SUBMUTE 0x8 + +#define FB_SUBCTL_SUBDEM 2 +#define FM_SUBCTL_SUBDEM 0x4 + +#define FB_SUBCTL_SUBMUX 1 +#define FM_SUBCTL_SUBMUX 0x2 + +#define FB_SUBCTL_SUBILMDIS 0 +#define FM_SUBCTL_SUBILMDIS 0x1 + +// *** DCCTL *** +#define FB_DCCTL_SUBDCBYP 7 +#define FM_DCCTL_SUBDCBYP 0x80 + +#define FB_DCCTL_DACDCBYP 6 +#define FM_DCCTL_DACDCBYP 0x40 + +#define FB_DCCTL_SPKDCBYP 5 +#define FM_DCCTL_SPKDCBYP 0x20 + +#define FB_DCCTL_DCCOEFSEL 0 +#define FM_DCCTL_DCCOEFSEL 0x7 + +// *** OVOLCTLU *** +#define FB_OVOLCTLU_OFADE 4 +#define FM_OVOLCTLU_OFADE 0x10 + +#define FB_OVOLCTLU_SUBVOLU 3 +#define FM_OVOLCTLU_SUBVOLU 0x8 + +#define FB_OVOLCTLU_MVOLU 2 +#define FM_OVOLCTLU_MVOLU 0x4 + +#define FB_OVOLCTLU_SPKVOLU 1 +#define FM_OVOLCTLU_SPKVOLU 0x2 + +#define FB_OVOLCTLU_HPVOLU 0 +#define FM_OVOLCTLU_HPVOLU 0x1 + +// *** MUTEC *** +#define FB_MUTEC_ZDSTAT 7 +#define FM_MUTEC_ZDSTAT 0x80 + +#define FB_MUTEC_ZDLEN 4 +#define FM_MUTEC_ZDLEN 0x30 + +#define FB_MUTEC_APWD 3 +#define FM_MUTEC_APWD 0x8 + +#define FB_MUTEC_AMUTE 2 +#define FM_MUTEC_AMUTE 0x4 + +// *** MVOLL *** +#define FB_MVOLL_MVOL_L 0 +#define FM_MVOLL_MVOL_L 0xFF + +// *** MVOLR *** +#define FB_MVOLR_MVOL_R 0 +#define FM_MVOLR_MVOL_R 0xFF + +// *** HPVOLL *** +#define FB_HPVOLL_HPVOL_L 0 +#define FM_HPVOLL_HPVOL_L 0x7F + +// *** HPVOLR *** +#define FB_HPVOLR_HPVOL_R 0 +#define FM_HPVOLR_HPVOL_R 0x7F + +// *** SPKVOLL *** +#define FB_SPKVOLL_SPKVOL_L 0 +#define FM_SPKVOLL_SPKVOL_L 0x7F + +// *** SPKVOLR *** +#define FB_SPKVOLR_SPKVOL_R 0 +#define FM_SPKVOLR_SPKVOL_R 0x7F + +// *** SUBVOL *** +#define FB_SUBVOL_SUBVOL 0 +#define FM_SUBVOL_SUBVOL 0x7F + +// *** COP0 *** +#define FB_COP0_COPATTEN 7 +#define FM_COP0_COPATTEN 0x80 + +#define FB_COP0_COPGAIN 6 +#define FM_COP0_COPGAIN 0x40 + +#define FB_COP0_HDELTAEN 5 +#define FM_COP0_HDELTAEN 0x20 + +#define FB_COP0_COPTARGET 0 +#define FM_COP0_COPTARGET 0x1F + +// *** COP1 *** +#define FB_COP1_HDCOMPMODE 6 +#define FM_COP1_HDCOMPMODE 0x40 + +#define FB_COP1_AVGLENGTH 2 +#define FM_COP1_AVGLENGTH 0x3C + +#define FB_COP1_MONRATE 0 +#define FM_COP1_MONRATE 0x3 + +// *** COPSTAT *** +#define FB_COPSTAT_HDELTADET 7 +#define FM_COPSTAT_HDELTADET 0x80 + +#define FB_COPSTAT_UV 6 +#define FM_COPSTAT_UV 0x40 + +#define FB_COPSTAT_COPADJ 0 +#define FM_COPSTAT_COPADJ 0x3F + +// *** PWM0 *** +#define FB_PWM0_SCTO 6 +#define FM_PWM0_SCTO 0xC0 + +#define FB_PWM0_UVLO 5 +#define FM_PWM0_UVLO 0x20 + +#define FB_PWM0_BFDIS 3 +#define FM_PWM0_BFDIS 0x8 + +#define FB_PWM0_PWMMODE 2 +#define FM_PWM0_PWMMODE 0x4 + +#define FB_PWM0_NOOFFSET 0 +#define FM_PWM0_NOOFFSET 0x1 + +// *** PWM1 *** +#define FB_PWM1_DITHPOS 4 +#define FM_PWM1_DITHPOS 0x70 + +#define FB_PWM1_DYNDITH 1 +#define FM_PWM1_DYNDITH 0x2 + +#define FB_PWM1_DITHDIS 0 +#define FM_PWM1_DITHDIS 0x1 + +// *** PWM2 *** +// *** PWM3 *** +#define FB_PWM3_PWMMUX 6 +#define FM_PWM3_PWMMUX 0xC0 + +#define FB_PWM3_CVALUE 0 +#define FM_PWM3_CVALUE 0x7 + +// *** HPSW *** +#define FB_HPSW_HPDETSTATE 4 +#define FM_HPSW_HPDETSTATE 0x10 + +#define FB_HPSW_HPSWEN 2 +#define FM_HPSW_HPSWEN 0xC + +#define FB_HPSW_HPSWPOL 1 +#define FM_HPSW_HPSWPOL 0x2 + +#define FB_HPSW_TSDEN 0 +#define FM_HPSW_TSDEN 0x1 + +// *** THERMTS *** +#define FB_THERMTS_TRIPHS 7 +#define FM_THERMTS_TRIPHS 0x80 + +#define FB_THERMTS_TRIPLS 6 +#define FM_THERMTS_TRIPLS 0x40 + +#define FB_THERMTS_TRIPSPLIT 4 +#define FM_THERMTS_TRIPSPLIT 0x30 + +#define FB_THERMTS_TRIPSHIFT 2 +#define FM_THERMTS_TRIPSHIFT 0xC + +#define FB_THERMTS_TSPOLL 0 +#define FM_THERMTS_TSPOLL 0x3 + +// *** THERMSPK1 *** +#define FB_THERMSPK1_FORCEPWD 7 +#define FM_THERMSPK1_FORCEPWD 0x80 + +#define FB_THERMSPK1_INSTCUTMODE 6 +#define FM_THERMSPK1_INSTCUTMODE 0x40 + +#define FB_THERMSPK1_INCRATIO 4 +#define FM_THERMSPK1_INCRATIO 0x30 + +#define FB_THERMSPK1_INCSTEP 2 +#define FM_THERMSPK1_INCSTEP 0xC + +#define FB_THERMSPK1_DECSTEP 0 +#define FM_THERMSPK1_DECSTEP 0x3 + +// *** THERMSTAT *** +#define FB_THERMSTAT_FPWDS 7 +#define FM_THERMSTAT_FPWDS 0x80 + +#define FB_THERMSTAT_VOLSTAT 0 +#define FM_THERMSTAT_VOLSTAT 0x7F + +// *** SCSTAT *** +#define FB_SCSTAT_ESDF 3 +#define FM_SCSTAT_ESDF 0x18 + +#define FB_SCSTAT_CPF 2 +#define FM_SCSTAT_CPF 0x4 + +#define FB_SCSTAT_CLSDF 0 +#define FM_SCSTAT_CLSDF 0x3 + +// *** SDMON *** +#define FB_SDMON_SDFORCE 7 +#define FM_SDMON_SDFORCE 0x80 + +#define FB_SDMON_SDVALUE 0 +#define FM_SDMON_SDVALUE 0x1F + +// *** SPKEQFILT *** +#define FB_SPKEQFILT_EQ2EN 7 +#define FM_SPKEQFILT_EQ2EN 0x80 +#define FV_EQ2EN_ENABLE 0x80 +#define FV_EQ2EN_DISABLE 0x0 + +#define FB_SPKEQFILT_EQ2BE 4 +#define FM_SPKEQFILT_EQ2BE 0x70 + +#define FB_SPKEQFILT_EQ1EN 3 +#define FM_SPKEQFILT_EQ1EN 0x8 +#define FV_EQ1EN_ENABLE 0x8 +#define FV_EQ1EN_DISABLE 0x0 + +#define FB_SPKEQFILT_EQ1BE 0 +#define FM_SPKEQFILT_EQ1BE 0x7 + +#define SPKEQFILT_EQEN_ENABLE 0x1 +#define SPKEQFILT_EQEN_DISABLE 0x0 + +// *** SPKCRWDL *** +#define FB_SPKCRWDL_WDATA_L 0 +#define FM_SPKCRWDL_WDATA_L 0xFF + +// *** SPKCRWDM *** +#define FB_SPKCRWDM_WDATA_M 0 +#define FM_SPKCRWDM_WDATA_M 0xFF + +// *** SPKCRWDH *** +#define FB_SPKCRWDH_WDATA_H 0 +#define FM_SPKCRWDH_WDATA_H 0xFF + +// *** SPKCRRDL *** +#define FB_SPKCRRDL_RDATA_L 0 +#define FM_SPKCRRDL_RDATA_L 0xFF + +// *** SPKCRRDM *** +#define FB_SPKCRRDM_RDATA_M 0 +#define FM_SPKCRRDM_RDATA_M 0xFF + +// *** SPKCRRDH *** +#define FB_SPKCRRDH_RDATA_H 0 +#define FM_SPKCRRDH_RDATA_H 0xFF + +// *** SPKCRADD *** +#define FB_SPKCRADD_ADDRESS 0 +#define FM_SPKCRADD_ADDRESS 0xFF + +// *** SPKCRS *** +#define FB_SPKCRS_ACCSTAT 7 +#define FM_SPKCRS_ACCSTAT 0x80 + +// *** SPKMBCEN *** +#define FB_SPKMBCEN_MBCEN3 2 +#define FM_SPKMBCEN_MBCEN3 0x4 +#define FV_MBCEN3_ENABLE 0x4 +#define FV_MBCEN3_DISABLE 0x0 + +#define FB_SPKMBCEN_MBCEN2 1 +#define FM_SPKMBCEN_MBCEN2 0x2 +#define FV_MBCEN2_ENABLE 0x2 +#define FV_MBCEN2_DISABLE 0x0 + +#define FB_SPKMBCEN_MBCEN1 0 +#define FM_SPKMBCEN_MBCEN1 0x1 +#define FV_MBCEN1_ENABLE 0x1 +#define FV_MBCEN1_DISABLE 0x0 + +#define SPKMBCEN_MBCEN_ENABLE 0x1 +#define SPKMBCEN_MBCEN_DISABLE 0x0 + +// *** SPKMBCCTL *** +#define FB_SPKMBCCTL_LVLMODE3 5 +#define FM_SPKMBCCTL_LVLMODE3 0x20 + +#define FB_SPKMBCCTL_WINSEL3 4 +#define FM_SPKMBCCTL_WINSEL3 0x10 + +#define FB_SPKMBCCTL_LVLMODE2 3 +#define FM_SPKMBCCTL_LVLMODE2 0x8 + +#define FB_SPKMBCCTL_WINSEL2 2 +#define FM_SPKMBCCTL_WINSEL2 0x4 + +#define FB_SPKMBCCTL_LVLMODE1 1 +#define FM_SPKMBCCTL_LVLMODE1 0x2 + +#define FB_SPKMBCCTL_WINSEL1 0 +#define FM_SPKMBCCTL_WINSEL1 0x1 + +// *** SPKCLECTL *** +#define FB_SPKCLECTL_LVLMODE 4 +#define FM_SPKCLECTL_LVLMODE 0x10 + +#define FB_SPKCLECTL_WINSEL 3 +#define FM_SPKCLECTL_WINSEL 0x8 + +#define FB_SPKCLECTL_EXPEN 2 +#define FM_SPKCLECTL_EXPEN 0x4 +#define FV_EXPEN_ENABLE 0x4 +#define FV_EXPEN_DISABLE 0x0 + +#define FB_SPKCLECTL_LIMEN 1 +#define FM_SPKCLECTL_LIMEN 0x2 +#define FV_LIMEN_ENABLE 0x2 +#define FV_LIMEN_DISABLE 0x0 + +#define FB_SPKCLECTL_COMPEN 0 +#define FM_SPKCLECTL_COMPEN 0x1 +#define FV_COMPEN_ENABLE 0x1 +#define FV_COMPEN_DISABLE 0x0 + +// *** SPKCLEMUG *** +#define FB_SPKCLEMUG_MUGAIN 0 +#define FM_SPKCLEMUG_MUGAIN 0x1F + +// *** SPKCOMPTHR *** +#define FB_SPKCOMPTHR_THRESH 0 +#define FM_SPKCOMPTHR_THRESH 0xFF + +// *** SPKCOMPRAT *** +#define FB_SPKCOMPRAT_RATIO 0 +#define FM_SPKCOMPRAT_RATIO 0x1F + +// *** SPKCOMPATKL *** +#define FB_SPKCOMPATKL_TCATKL 0 +#define FM_SPKCOMPATKL_TCATKL 0xFF + +// *** SPKCOMPATKH *** +#define FB_SPKCOMPATKH_TCATKH 0 +#define FM_SPKCOMPATKH_TCATKH 0xFF + +// *** SPKCOMPRELL *** +#define FB_SPKCOMPRELL_TCRELL 0 +#define FM_SPKCOMPRELL_TCRELL 0xFF + +// *** SPKCOMPRELH *** +#define FB_SPKCOMPRELH_TCRELH 0 +#define FM_SPKCOMPRELH_TCRELH 0xFF + +// *** SPKLIMTHR *** +#define FB_SPKLIMTHR_THRESH 0 +#define FM_SPKLIMTHR_THRESH 0xFF + +// *** SPKLIMTGT *** +#define FB_SPKLIMTGT_TARGET 0 +#define FM_SPKLIMTGT_TARGET 0xFF + +// *** SPKLIMATKL *** +#define FB_SPKLIMATKL_TCATKL 0 +#define FM_SPKLIMATKL_TCATKL 0xFF + +// *** SPKLIMATKH *** +#define FB_SPKLIMATKH_TCATKH 0 +#define FM_SPKLIMATKH_TCATKH 0xFF + +// *** SPKLIMRELL *** +#define FB_SPKLIMRELL_TCRELL 0 +#define FM_SPKLIMRELL_TCRELL 0xFF + +// *** SPKLIMRELH *** +#define FB_SPKLIMRELH_TCRELH 0 +#define FM_SPKLIMRELH_TCRELH 0xFF + +// *** SPKEXPTHR *** +#define FB_SPKEXPTHR_THRESH 0 +#define FM_SPKEXPTHR_THRESH 0xFF + +// *** SPKEXPRAT *** +#define FB_SPKEXPRAT_RATIO 0 +#define FM_SPKEXPRAT_RATIO 0x7 + +// *** SPKEXPATKL *** +#define FB_SPKEXPATKL_TCATKL 0 +#define FM_SPKEXPATKL_TCATKL 0xFF + +// *** SPKEXPATKH *** +#define FB_SPKEXPATKH_TCATKH 0 +#define FM_SPKEXPATKH_TCATKH 0xFF + +// *** SPKEXPRELL *** +#define FB_SPKEXPRELL_TCRELL 0 +#define FM_SPKEXPRELL_TCRELL 0xFF + +// *** SPKEXPRELH *** +#define FB_SPKEXPRELH_TCRELH 0 +#define FM_SPKEXPRELH_TCRELH 0xFF + +// *** SPKFXCTL *** +#define FB_SPKFXCTL_3DEN 4 +#define FM_SPKFXCTL_3DEN 0x10 + +#define FB_SPKFXCTL_TEEN 3 +#define FM_SPKFXCTL_TEEN 0x8 + +#define FB_SPKFXCTL_TNLFBYP 2 +#define FM_SPKFXCTL_TNLFBYP 0x4 + +#define FB_SPKFXCTL_BEEN 1 +#define FM_SPKFXCTL_BEEN 0x2 + +#define FB_SPKFXCTL_BNLFBYP 0 +#define FM_SPKFXCTL_BNLFBYP 0x1 + +// *** DACEQFILT *** +#define FB_DACEQFILT_EQ2EN 7 +#define FM_DACEQFILT_EQ2EN 0x80 +#define FV_EQ2EN_ENABLE 0x80 +#define FV_EQ2EN_DISABLE 0x0 + +#define FB_DACEQFILT_EQ2BE 4 +#define FM_DACEQFILT_EQ2BE 0x70 + +#define FB_DACEQFILT_EQ1EN 3 +#define FM_DACEQFILT_EQ1EN 0x8 +#define FV_EQ1EN_ENABLE 0x8 +#define FV_EQ1EN_DISABLE 0x0 + +#define FB_DACEQFILT_EQ1BE 0 +#define FM_DACEQFILT_EQ1BE 0x7 + +#define DACEQFILT_EQEN_ENABLE 0x1 +#define DACEQFILT_EQEN_DISABLE 0x0 + +// *** DACCRWDL *** +#define FB_DACCRWDL_WDATA_L 0 +#define FM_DACCRWDL_WDATA_L 0xFF + +// *** DACCRWDM *** +#define FB_DACCRWDM_WDATA_M 0 +#define FM_DACCRWDM_WDATA_M 0xFF + +// *** DACCRWDH *** +#define FB_DACCRWDH_WDATA_H 0 +#define FM_DACCRWDH_WDATA_H 0xFF + +// *** DACCRRDL *** +#define FB_DACCRRDL_RDATA_L 0 +#define FM_DACCRRDL_RDATA_L 0xFF + +// *** DACCRRDM *** +#define FB_DACCRRDM_RDATA_M 0 +#define FM_DACCRRDM_RDATA_M 0xFF + +// *** DACCRRDH *** +#define FB_DACCRRDH_RDATA_H 0 +#define FM_DACCRRDH_RDATA_H 0xFF + +// *** DACCRADD *** +#define FB_DACCRADD_ADDRESS 0 +#define FM_DACCRADD_ADDRESS 0xFF + +// *** DACCRS *** +#define FB_DACCRS_ACCSTAT 7 +#define FM_DACCRS_ACCSTAT 0x80 + +// *** DACMBCEN *** +#define FB_DACMBCEN_MBCEN3 2 +#define FM_DACMBCEN_MBCEN3 0x4 +#define FV_MBCEN3_ENABLE 0x4 +#define FV_MBCEN3_DISABLE 0x0 + +#define FB_DACMBCEN_MBCEN2 1 +#define FM_DACMBCEN_MBCEN2 0x2 +#define FV_MBCEN2_ENABLE 0x2 +#define FV_MBCEN2_DISABLE 0x0 + +#define FB_DACMBCEN_MBCEN1 0 +#define FM_DACMBCEN_MBCEN1 0x1 +#define FV_MBCEN1_ENABLE 0x1 +#define FV_MBCEN1_DISABLE 0x0 + +#define DACMBCEN_MBCEN_ENABLE 0x1 +#define DACMBCEN_MBCEN_DISABLE 0x0 + +// *** DACMBCCTL *** +#define FB_DACMBCCTL_LVLMODE3 5 +#define FM_DACMBCCTL_LVLMODE3 0x20 + +#define FB_DACMBCCTL_WINSEL3 4 +#define FM_DACMBCCTL_WINSEL3 0x10 + +#define FB_DACMBCCTL_LVLMODE2 3 +#define FM_DACMBCCTL_LVLMODE2 0x8 + +#define FB_DACMBCCTL_WINSEL2 2 +#define FM_DACMBCCTL_WINSEL2 0x4 + +#define FB_DACMBCCTL_LVLMODE1 1 +#define FM_DACMBCCTL_LVLMODE1 0x2 + +#define FB_DACMBCCTL_WINSEL1 0 +#define FM_DACMBCCTL_WINSEL1 0x1 + +// *** DACCLECTL *** +#define FB_DACCLECTL_LVLMODE 4 +#define FM_DACCLECTL_LVLMODE 0x10 + +#define FB_DACCLECTL_WINSEL 3 +#define FM_DACCLECTL_WINSEL 0x8 + +#define FB_DACCLECTL_EXPEN 2 +#define FM_DACCLECTL_EXPEN 0x4 +#define FV_EXPEN_ENABLE 0x4 +#define FV_EXPEN_DISABLE 0x0 + +#define FB_DACCLECTL_LIMEN 1 +#define FM_DACCLECTL_LIMEN 0x2 +#define FV_LIMEN_ENABLE 0x2 +#define FV_LIMEN_DISABLE 0x0 + +#define FB_DACCLECTL_COMPEN 0 +#define FM_DACCLECTL_COMPEN 0x1 +#define FV_COMPEN_ENABLE 0x1 +#define FV_COMPEN_DISABLE 0x0 + +// *** DACCLEMUG *** +#define FB_DACCLEMUG_MUGAIN 0 +#define FM_DACCLEMUG_MUGAIN 0x1F + +// *** DACCOMPTHR *** +#define FB_DACCOMPTHR_THRESH 0 +#define FM_DACCOMPTHR_THRESH 0xFF + +// *** DACCOMPRAT *** +#define FB_DACCOMPRAT_RATIO 0 +#define FM_DACCOMPRAT_RATIO 0x1F + +// *** DACCOMPATKL *** +#define FB_DACCOMPATKL_TCATKL 0 +#define FM_DACCOMPATKL_TCATKL 0xFF + +// *** DACCOMPATKH *** +#define FB_DACCOMPATKH_TCATKH 0 +#define FM_DACCOMPATKH_TCATKH 0xFF + +// *** DACCOMPRELL *** +#define FB_DACCOMPRELL_TCRELL 0 +#define FM_DACCOMPRELL_TCRELL 0xFF + +// *** DACCOMPRELH *** +#define FB_DACCOMPRELH_TCRELH 0 +#define FM_DACCOMPRELH_TCRELH 0xFF + +// *** DACLIMTHR *** +#define FB_DACLIMTHR_THRESH 0 +#define FM_DACLIMTHR_THRESH 0xFF + +// *** DACLIMTGT *** +#define FB_DACLIMTGT_TARGET 0 +#define FM_DACLIMTGT_TARGET 0xFF + +// *** DACLIMATKL *** +#define FB_DACLIMATKL_TCATKL 0 +#define FM_DACLIMATKL_TCATKL 0xFF + +// *** DACLIMATKH *** +#define FB_DACLIMATKH_TCATKH 0 +#define FM_DACLIMATKH_TCATKH 0xFF + +// *** DACLIMRELL *** +#define FB_DACLIMRELL_TCRELL 0 +#define FM_DACLIMRELL_TCRELL 0xFF + +// *** DACLIMRELH *** +#define FB_DACLIMRELH_TCRELH 0 +#define FM_DACLIMRELH_TCRELH 0xFF + +// *** DACEXPTHR *** +#define FB_DACEXPTHR_THRESH 0 +#define FM_DACEXPTHR_THRESH 0xFF + +// *** DACEXPRAT *** +#define FB_DACEXPRAT_RATIO 0 +#define FM_DACEXPRAT_RATIO 0x7 + +// *** DACEXPATKL *** +#define FB_DACEXPATKL_TCATKL 0 +#define FM_DACEXPATKL_TCATKL 0xFF + +// *** DACEXPATKH *** +#define FB_DACEXPATKH_TCATKH 0 +#define FM_DACEXPATKH_TCATKH 0xFF + +// *** DACEXPRELL *** +#define FB_DACEXPRELL_TCRELL 0 +#define FM_DACEXPRELL_TCRELL 0xFF + +// *** DACEXPRELH *** +#define FB_DACEXPRELH_TCRELH 0 +#define FM_DACEXPRELH_TCRELH 0xFF + +// *** DACFXCTL *** +#define FB_DACFXCTL_3DEN 4 +#define FM_DACFXCTL_3DEN 0x10 + +#define FB_DACFXCTL_TEEN 3 +#define FM_DACFXCTL_TEEN 0x8 + +#define FB_DACFXCTL_TNLFBYP 2 +#define FM_DACFXCTL_TNLFBYP 0x4 + +#define FB_DACFXCTL_BEEN 1 +#define FM_DACFXCTL_BEEN 0x2 + +#define FB_DACFXCTL_BNLFBYP 0 +#define FM_DACFXCTL_BNLFBYP 0x1 + +// *** SUBEQFILT *** +#define FB_SUBEQFILT_EQ2EN 7 +#define FM_SUBEQFILT_EQ2EN 0x80 +#define FV_EQ2EN_ENABLE 0x80 +#define FV_EQ2EN_DISABLE 0x0 + +#define FB_SUBEQFILT_EQ2BE 4 +#define FM_SUBEQFILT_EQ2BE 0x70 + +#define FB_SUBEQFILT_EQ1EN 3 +#define FM_SUBEQFILT_EQ1EN 0x8 +#define FV_EQ1EN_ENABLE 0x8 +#define FV_EQ1EN_DISABLE 0x0 + +#define FB_SUBEQFILT_EQ1BE 0 +#define FM_SUBEQFILT_EQ1BE 0x7 + +#define SUBEQFILT_EQEN_ENABLE 0x1 +#define SUBEQFILT_EQEN_DISABLE 0x0 + +// *** SUBCRWDL *** +#define FB_SUBCRWDL_WDATA_L 0 +#define FM_SUBCRWDL_WDATA_L 0xFF + +// *** SUBCRWDM *** +#define FB_SUBCRWDM_WDATA_M 0 +#define FM_SUBCRWDM_WDATA_M 0xFF + +// *** SUBCRWDH *** +#define FB_SUBCRWDH_WDATA_H 0 +#define FM_SUBCRWDH_WDATA_H 0xFF + +// *** SUBCRRDL *** +#define FB_SUBCRRDL_RDATA_L 0 +#define FM_SUBCRRDL_RDATA_L 0xFF + +// *** SUBCRRDM *** +#define FB_SUBCRRDM_RDATA_M 0 +#define FM_SUBCRRDM_RDATA_M 0xFF + +// *** SUBCRRDH *** +#define FB_SUBCRRDH_RDATA_H 0 +#define FM_SUBCRRDH_RDATA_H 0xFF + +// *** SUBCRADD *** +#define FB_SUBCRADD_ADDRESS 0 +#define FM_SUBCRADD_ADDRESS 0xFF + +// *** SUBCRS *** +#define FB_SUBCRS_ACCSTAT 7 +#define FM_SUBCRS_ACCSTAT 0x80 + +// *** SUBMBCEN *** +#define FB_SUBMBCEN_MBCEN3 2 +#define FM_SUBMBCEN_MBCEN3 0x4 +#define FV_MBCEN3_ENABLE 0x4 +#define FV_MBCEN3_DISABLE 0x0 + +#define FB_SUBMBCEN_MBCEN2 1 +#define FM_SUBMBCEN_MBCEN2 0x2 +#define FV_MBCEN2_ENABLE 0x2 +#define FV_MBCEN2_DISABLE 0x0 + +#define FB_SUBMBCEN_MBCEN1 0 +#define FM_SUBMBCEN_MBCEN1 0x1 +#define FV_MBCEN1_ENABLE 0x1 +#define FV_MBCEN1_DISABLE 0x0 + +#define SUBMBCEN_MBCEN_ENABLE 0x1 +#define SUBMBCEN_MBCEN_DISABLE 0x0 + +// *** SUBMBCCTL *** +#define FB_SUBMBCCTL_LVLMODE3 5 +#define FM_SUBMBCCTL_LVLMODE3 0x20 + +#define FB_SUBMBCCTL_WINSEL3 4 +#define FM_SUBMBCCTL_WINSEL3 0x10 + +#define FB_SUBMBCCTL_LVLMODE2 3 +#define FM_SUBMBCCTL_LVLMODE2 0x8 + +#define FB_SUBMBCCTL_WINSEL2 2 +#define FM_SUBMBCCTL_WINSEL2 0x4 + +#define FB_SUBMBCCTL_LVLMODE1 1 +#define FM_SUBMBCCTL_LVLMODE1 0x2 + +#define FB_SUBMBCCTL_WINSEL1 0 +#define FM_SUBMBCCTL_WINSEL1 0x1 + +// *** SUBCLECTL *** +#define FB_SUBCLECTL_LVLMODE 4 +#define FM_SUBCLECTL_LVLMODE 0x10 + +#define FB_SUBCLECTL_WINSEL 3 +#define FM_SUBCLECTL_WINSEL 0x8 + +#define FB_SUBCLECTL_EXPEN 2 +#define FM_SUBCLECTL_EXPEN 0x4 +#define FV_EXPEN_ENABLE 0x4 +#define FV_EXPEN_DISABLE 0x0 + +#define FB_SUBCLECTL_LIMEN 1 +#define FM_SUBCLECTL_LIMEN 0x2 +#define FV_LIMEN_ENABLE 0x2 +#define FV_LIMEN_DISABLE 0x0 + +#define FB_SUBCLECTL_COMPEN 0 +#define FM_SUBCLECTL_COMPEN 0x1 +#define FV_COMPEN_ENABLE 0x1 +#define FV_COMPEN_DISABLE 0x0 + +// *** SUBCLEMUG *** +#define FB_SUBCLEMUG_MUGAIN 0 +#define FM_SUBCLEMUG_MUGAIN 0x1F + +// *** SUBCOMPTHR *** +#define FB_SUBCOMPTHR_THRESH 0 +#define FM_SUBCOMPTHR_THRESH 0xFF + +// *** SUBCOMPRAT *** +#define FB_SUBCOMPRAT_RATIO 0 +#define FM_SUBCOMPRAT_RATIO 0x1F + +// *** SUBCOMPATKL *** +#define FB_SUBCOMPATKL_TCATKL 0 +#define FM_SUBCOMPATKL_TCATKL 0xFF + +// *** SUBCOMPATKH *** +#define FB_SUBCOMPATKH_TCATKH 0 +#define FM_SUBCOMPATKH_TCATKH 0xFF + +// *** SUBCOMPRELL *** +#define FB_SUBCOMPRELL_TCRELL 0 +#define FM_SUBCOMPRELL_TCRELL 0xFF + +// *** SUBCOMPRELH *** +#define FB_SUBCOMPRELH_TCRELH 0 +#define FM_SUBCOMPRELH_TCRELH 0xFF + +// *** SUBLIMTHR *** +#define FB_SUBLIMTHR_THRESH 0 +#define FM_SUBLIMTHR_THRESH 0xFF + +// *** SUBLIMTGT *** +#define FB_SUBLIMTGT_TARGET 0 +#define FM_SUBLIMTGT_TARGET 0xFF + +// *** SUBLIMATKL *** +#define FB_SUBLIMATKL_TCATKL 0 +#define FM_SUBLIMATKL_TCATKL 0xFF + +// *** SUBLIMATKH *** +#define FB_SUBLIMATKH_TCATKH 0 +#define FM_SUBLIMATKH_TCATKH 0xFF + +// *** SUBLIMRELL *** +#define FB_SUBLIMRELL_TCRELL 0 +#define FM_SUBLIMRELL_TCRELL 0xFF + +// *** SUBLIMRELH *** +#define FB_SUBLIMRELH_TCRELH 0 +#define FM_SUBLIMRELH_TCRELH 0xFF + +// *** SUBEXPTHR *** +#define FB_SUBEXPTHR_THRESH 0 +#define FM_SUBEXPTHR_THRESH 0xFF + +// *** SUBEXPRAT *** +#define FB_SUBEXPRAT_RATIO 0 +#define FM_SUBEXPRAT_RATIO 0x7 + +// *** SUBEXPATKL *** +#define FB_SUBEXPATKL_TCATKL 0 +#define FM_SUBEXPATKL_TCATKL 0xFF + +// *** SUBEXPATKH *** +#define FB_SUBEXPATKH_TCATKH 0 +#define FM_SUBEXPATKH_TCATKH 0xFF + +// *** SUBEXPRELL *** +#define FB_SUBEXPRELL_TCRELL 0 +#define FM_SUBEXPRELL_TCRELL 0xFF + +// *** SUBEXPRELH *** +#define FB_SUBEXPRELH_TCRELH 0 +#define FM_SUBEXPRELH_TCRELH 0xFF + +// *** SUBFXCTL *** +#define FB_SUBFXCTL_TEEN 3 +#define FM_SUBFXCTL_TEEN 0x8 + +#define FB_SUBFXCTL_TNLFBYP 2 +#define FM_SUBFXCTL_TNLFBYP 0x4 + +#define FB_SUBFXCTL_BEEN 1 +#define FM_SUBFXCTL_BEEN 0x2 + +#define FB_SUBFXCTL_BNLFBYP 0 +#define FM_SUBFXCTL_BNLFBYP 0x1 + +#endif /* __REDWOODPUBLIC_H__ */ diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c new file mode 100644 index 0000000000..9c50ac356c --- /dev/null +++ b/sound/soc/codecs/twl4030.c @@ -0,0 +1,2222 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC TWL4030 codec driver + * + * Author: Steve Sakoman, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register descriptions are here */ +#include + +/* TWL4030 PMBR1 Register */ +#define TWL4030_PMBR1_REG 0x0D +/* TWL4030 PMBR1 Register GPIO6 mux bits */ +#define TWL4030_GPIO6_PWM0_MUTE(value) ((value & 0x03) << 2) + +#define TWL4030_CACHEREGNUM (TWL4030_REG_MISC_SET_2 + 1) + +struct twl4030_board_params { + unsigned int digimic_delay; /* in ms */ + unsigned int ramp_delay_value; + unsigned int offset_cncl_path; + unsigned int hs_extmute:1; + int hs_extmute_gpio; +}; + +/* codec private data */ +struct twl4030_priv { + unsigned int codec_powered; + + /* reference counts of AIF/APLL users */ + unsigned int apll_enabled; + + struct snd_pcm_substream *master_substream; + struct snd_pcm_substream *slave_substream; + + unsigned int configured; + unsigned int rate; + unsigned int sample_bits; + unsigned int channels; + + unsigned int sysclk; + + /* Output (with associated amp) states */ + u8 hsl_enabled, hsr_enabled; + u8 earpiece_enabled; + u8 predrivel_enabled, predriver_enabled; + u8 carkitl_enabled, carkitr_enabled; + u8 ctl_cache[TWL4030_REG_PRECKR_CTL - TWL4030_REG_EAR_CTL + 1]; + + struct twl4030_board_params *board_params; +}; + +static void tw4030_init_ctl_cache(struct twl4030_priv *twl4030) +{ + int i; + u8 byte; + + for (i = TWL4030_REG_EAR_CTL; i <= TWL4030_REG_PRECKR_CTL; i++) { + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, i); + twl4030->ctl_cache[i - TWL4030_REG_EAR_CTL] = byte; + } +} + +static unsigned int twl4030_read(struct snd_soc_component *component, unsigned int reg) +{ + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + u8 value = 0; + + if (reg >= TWL4030_CACHEREGNUM) + return -EIO; + + switch (reg) { + case TWL4030_REG_EAR_CTL: + case TWL4030_REG_PREDL_CTL: + case TWL4030_REG_PREDR_CTL: + case TWL4030_REG_PRECKL_CTL: + case TWL4030_REG_PRECKR_CTL: + case TWL4030_REG_HS_GAIN_SET: + value = twl4030->ctl_cache[reg - TWL4030_REG_EAR_CTL]; + break; + default: + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg); + break; + } + + return value; +} + +static bool twl4030_can_write_to_chip(struct twl4030_priv *twl4030, + unsigned int reg) +{ + bool write_to_reg = false; + + /* Decide if the given register can be written */ + switch (reg) { + case TWL4030_REG_EAR_CTL: + if (twl4030->earpiece_enabled) + write_to_reg = true; + break; + case TWL4030_REG_PREDL_CTL: + if (twl4030->predrivel_enabled) + write_to_reg = true; + break; + case TWL4030_REG_PREDR_CTL: + if (twl4030->predriver_enabled) + write_to_reg = true; + break; + case TWL4030_REG_PRECKL_CTL: + if (twl4030->carkitl_enabled) + write_to_reg = true; + break; + case TWL4030_REG_PRECKR_CTL: + if (twl4030->carkitr_enabled) + write_to_reg = true; + break; + case TWL4030_REG_HS_GAIN_SET: + if (twl4030->hsl_enabled || twl4030->hsr_enabled) + write_to_reg = true; + break; + default: + /* All other register can be written */ + write_to_reg = true; + break; + } + + return write_to_reg; +} + +static int twl4030_write(struct snd_soc_component *component, unsigned int reg, + unsigned int value) +{ + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + + /* Update the ctl cache */ + switch (reg) { + case TWL4030_REG_EAR_CTL: + case TWL4030_REG_PREDL_CTL: + case TWL4030_REG_PREDR_CTL: + case TWL4030_REG_PRECKL_CTL: + case TWL4030_REG_PRECKR_CTL: + case TWL4030_REG_HS_GAIN_SET: + twl4030->ctl_cache[reg - TWL4030_REG_EAR_CTL] = value; + break; + default: + break; + } + + if (twl4030_can_write_to_chip(twl4030, reg)) + return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg); + + return 0; +} + +static inline void twl4030_wait_ms(int time) +{ + if (time < 60) { + time *= 1000; + usleep_range(time, time + 500); + } else { + msleep(time); + } +} + +static void twl4030_codec_enable(struct snd_soc_component *component, int enable) +{ + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + int mode; + + if (enable == twl4030->codec_powered) + return; + + if (enable) + mode = twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); + else + mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); + + if (mode >= 0) + twl4030->codec_powered = enable; + + /* REVISIT: this delay is present in TI sample drivers */ + /* but there seems to be no TRM requirement for it */ + udelay(10); +} + +static void +twl4030_get_board_param_values(struct twl4030_board_params *board_params, + struct device_node *node) +{ + int value; + + of_property_read_u32(node, "ti,digimic_delay", &board_params->digimic_delay); + of_property_read_u32(node, "ti,ramp_delay_value", &board_params->ramp_delay_value); + of_property_read_u32(node, "ti,offset_cncl_path", &board_params->offset_cncl_path); + if (!of_property_read_u32(node, "ti,hs_extmute", &value)) + board_params->hs_extmute = value; + + board_params->hs_extmute_gpio = of_get_named_gpio(node, "ti,hs_extmute_gpio", 0); + if (gpio_is_valid(board_params->hs_extmute_gpio)) + board_params->hs_extmute = 1; +} + +static struct twl4030_board_params* +twl4030_get_board_params(struct snd_soc_component *component) +{ + struct twl4030_board_params *board_params = NULL; + struct device_node *twl4030_codec_node = NULL; + + twl4030_codec_node = of_get_child_by_name(component->dev->parent->of_node, + "codec"); + + if (twl4030_codec_node) { + board_params = devm_kzalloc(component->dev, + sizeof(struct twl4030_board_params), + GFP_KERNEL); + if (!board_params) { + of_node_put(twl4030_codec_node); + return NULL; + } + twl4030_get_board_param_values(board_params, twl4030_codec_node); + of_node_put(twl4030_codec_node); + } + + return board_params; +} + +static void twl4030_init_chip(struct snd_soc_component *component) +{ + struct twl4030_board_params *board_params; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + u8 reg, byte; + int i = 0; + + board_params = twl4030_get_board_params(component); + + if (board_params && board_params->hs_extmute) { + if (gpio_is_valid(board_params->hs_extmute_gpio)) { + int ret; + + if (!board_params->hs_extmute_gpio) + dev_warn(component->dev, + "Extmute GPIO is 0 is this correct?\n"); + + ret = gpio_request_one(board_params->hs_extmute_gpio, + GPIOF_OUT_INIT_LOW, + "hs_extmute"); + if (ret) { + dev_err(component->dev, + "Failed to get hs_extmute GPIO\n"); + board_params->hs_extmute_gpio = -1; + } + } else { + u8 pin_mux; + + /* Set TWL4030 GPIO6 as EXTMUTE signal */ + twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux, + TWL4030_PMBR1_REG); + pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03); + pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02); + twl_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux, + TWL4030_PMBR1_REG); + } + } + + /* Initialize the local ctl register cache */ + tw4030_init_ctl_cache(twl4030); + + /* anti-pop when changing analog gain */ + reg = twl4030_read(component, TWL4030_REG_MISC_SET_1); + twl4030_write(component, TWL4030_REG_MISC_SET_1, + reg | TWL4030_SMOOTH_ANAVOL_EN); + + twl4030_write(component, TWL4030_REG_OPTION, + TWL4030_ATXL1_EN | TWL4030_ATXR1_EN | + TWL4030_ARXL2_EN | TWL4030_ARXR2_EN); + + /* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */ + twl4030_write(component, TWL4030_REG_ARXR2_APGA_CTL, 0x32); + + /* Machine dependent setup */ + if (!board_params) + return; + + twl4030->board_params = board_params; + + reg = twl4030_read(component, TWL4030_REG_HS_POPN_SET); + reg &= ~TWL4030_RAMP_DELAY; + reg |= (board_params->ramp_delay_value << 2); + twl4030_write(component, TWL4030_REG_HS_POPN_SET, reg); + + /* initiate offset cancellation */ + twl4030_codec_enable(component, 1); + + reg = twl4030_read(component, TWL4030_REG_ANAMICL); + reg &= ~TWL4030_OFFSET_CNCL_SEL; + reg |= board_params->offset_cncl_path; + twl4030_write(component, TWL4030_REG_ANAMICL, + reg | TWL4030_CNCL_OFFSET_START); + + /* + * Wait for offset cancellation to complete. + * Since this takes a while, do not slam the i2c. + * Start polling the status after ~20ms. + */ + msleep(20); + do { + usleep_range(1000, 2000); + twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, true); + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, + TWL4030_REG_ANAMICL); + twl_set_regcache_bypass(TWL4030_MODULE_AUDIO_VOICE, false); + } while ((i++ < 100) && + ((byte & TWL4030_CNCL_OFFSET_START) == + TWL4030_CNCL_OFFSET_START)); + + twl4030_codec_enable(component, 0); +} + +static void twl4030_apll_enable(struct snd_soc_component *component, int enable) +{ + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + + if (enable) { + twl4030->apll_enabled++; + if (twl4030->apll_enabled == 1) + twl4030_audio_enable_resource( + TWL4030_AUDIO_RES_APLL); + } else { + twl4030->apll_enabled--; + if (!twl4030->apll_enabled) + twl4030_audio_disable_resource( + TWL4030_AUDIO_RES_APLL); + } +} + +/* Earpiece */ +static const struct snd_kcontrol_new twl4030_dapm_earpiece_controls[] = { + SOC_DAPM_SINGLE("Voice", TWL4030_REG_EAR_CTL, 0, 1, 0), + SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_EAR_CTL, 1, 1, 0), + SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_EAR_CTL, 2, 1, 0), + SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_EAR_CTL, 3, 1, 0), +}; + +/* PreDrive Left */ +static const struct snd_kcontrol_new twl4030_dapm_predrivel_controls[] = { + SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDL_CTL, 0, 1, 0), + SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PREDL_CTL, 1, 1, 0), + SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDL_CTL, 2, 1, 0), + SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDL_CTL, 3, 1, 0), +}; + +/* PreDrive Right */ +static const struct snd_kcontrol_new twl4030_dapm_predriver_controls[] = { + SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDR_CTL, 0, 1, 0), + SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PREDR_CTL, 1, 1, 0), + SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDR_CTL, 2, 1, 0), + SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDR_CTL, 3, 1, 0), +}; + +/* Headset Left */ +static const struct snd_kcontrol_new twl4030_dapm_hsol_controls[] = { + SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 0, 1, 0), + SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_HS_SEL, 1, 1, 0), + SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_HS_SEL, 2, 1, 0), +}; + +/* Headset Right */ +static const struct snd_kcontrol_new twl4030_dapm_hsor_controls[] = { + SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 3, 1, 0), + SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_HS_SEL, 4, 1, 0), + SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_HS_SEL, 5, 1, 0), +}; + +/* Carkit Left */ +static const struct snd_kcontrol_new twl4030_dapm_carkitl_controls[] = { + SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKL_CTL, 0, 1, 0), + SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PRECKL_CTL, 1, 1, 0), + SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PRECKL_CTL, 2, 1, 0), +}; + +/* Carkit Right */ +static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls[] = { + SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKR_CTL, 0, 1, 0), + SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PRECKR_CTL, 1, 1, 0), + SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PRECKR_CTL, 2, 1, 0), +}; + +/* Handsfree Left */ +static const char *twl4030_handsfreel_texts[] = + {"Voice", "AudioL1", "AudioL2", "AudioR2"}; + +static SOC_ENUM_SINGLE_DECL(twl4030_handsfreel_enum, + TWL4030_REG_HFL_CTL, 0, + twl4030_handsfreel_texts); + +static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control = +SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum); + +/* Handsfree Left virtual mute */ +static const struct snd_kcontrol_new twl4030_dapm_handsfreelmute_control = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +/* Handsfree Right */ +static const char *twl4030_handsfreer_texts[] = + {"Voice", "AudioR1", "AudioR2", "AudioL2"}; + +static SOC_ENUM_SINGLE_DECL(twl4030_handsfreer_enum, + TWL4030_REG_HFR_CTL, 0, + twl4030_handsfreer_texts); + +static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control = +SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum); + +/* Handsfree Right virtual mute */ +static const struct snd_kcontrol_new twl4030_dapm_handsfreermute_control = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +/* Vibra */ +/* Vibra audio path selection */ +static const char *twl4030_vibra_texts[] = + {"AudioL1", "AudioR1", "AudioL2", "AudioR2"}; + +static SOC_ENUM_SINGLE_DECL(twl4030_vibra_enum, + TWL4030_REG_VIBRA_CTL, 2, + twl4030_vibra_texts); + +static const struct snd_kcontrol_new twl4030_dapm_vibra_control = +SOC_DAPM_ENUM("Route", twl4030_vibra_enum); + +/* Vibra path selection: local vibrator (PWM) or audio driven */ +static const char *twl4030_vibrapath_texts[] = + {"Local vibrator", "Audio"}; + +static SOC_ENUM_SINGLE_DECL(twl4030_vibrapath_enum, + TWL4030_REG_VIBRA_CTL, 4, + twl4030_vibrapath_texts); + +static const struct snd_kcontrol_new twl4030_dapm_vibrapath_control = +SOC_DAPM_ENUM("Route", twl4030_vibrapath_enum); + +/* Left analog microphone selection */ +static const struct snd_kcontrol_new twl4030_dapm_analoglmic_controls[] = { + SOC_DAPM_SINGLE("Main Mic Capture Switch", + TWL4030_REG_ANAMICL, 0, 1, 0), + SOC_DAPM_SINGLE("Headset Mic Capture Switch", + TWL4030_REG_ANAMICL, 1, 1, 0), + SOC_DAPM_SINGLE("AUXL Capture Switch", + TWL4030_REG_ANAMICL, 2, 1, 0), + SOC_DAPM_SINGLE("Carkit Mic Capture Switch", + TWL4030_REG_ANAMICL, 3, 1, 0), +}; + +/* Right analog microphone selection */ +static const struct snd_kcontrol_new twl4030_dapm_analogrmic_controls[] = { + SOC_DAPM_SINGLE("Sub Mic Capture Switch", TWL4030_REG_ANAMICR, 0, 1, 0), + SOC_DAPM_SINGLE("AUXR Capture Switch", TWL4030_REG_ANAMICR, 2, 1, 0), +}; + +/* TX1 L/R Analog/Digital microphone selection */ +static const char *twl4030_micpathtx1_texts[] = + {"Analog", "Digimic0"}; + +static SOC_ENUM_SINGLE_DECL(twl4030_micpathtx1_enum, + TWL4030_REG_ADCMICSEL, 0, + twl4030_micpathtx1_texts); + +static const struct snd_kcontrol_new twl4030_dapm_micpathtx1_control = +SOC_DAPM_ENUM("Route", twl4030_micpathtx1_enum); + +/* TX2 L/R Analog/Digital microphone selection */ +static const char *twl4030_micpathtx2_texts[] = + {"Analog", "Digimic1"}; + +static SOC_ENUM_SINGLE_DECL(twl4030_micpathtx2_enum, + TWL4030_REG_ADCMICSEL, 2, + twl4030_micpathtx2_texts); + +static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control = +SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum); + +/* Analog bypass for AudioR1 */ +static const struct snd_kcontrol_new twl4030_dapm_abypassr1_control = + SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR1_APGA_CTL, 2, 1, 0); + +/* Analog bypass for AudioL1 */ +static const struct snd_kcontrol_new twl4030_dapm_abypassl1_control = + SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL1_APGA_CTL, 2, 1, 0); + +/* Analog bypass for AudioR2 */ +static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control = + SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR2_APGA_CTL, 2, 1, 0); + +/* Analog bypass for AudioL2 */ +static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control = + SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0); + +/* Analog bypass for Voice */ +static const struct snd_kcontrol_new twl4030_dapm_abypassv_control = + SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0); + +/* Digital bypass gain, mute instead of -30dB */ +static const DECLARE_TLV_DB_RANGE(twl4030_dapm_dbypass_tlv, + 0, 1, TLV_DB_SCALE_ITEM(-3000, 600, 1), + 2, 3, TLV_DB_SCALE_ITEM(-2400, 0, 0), + 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0) +); + +/* Digital bypass left (TX1L -> RX2L) */ +static const struct snd_kcontrol_new twl4030_dapm_dbypassl_control = + SOC_DAPM_SINGLE_TLV("Volume", + TWL4030_REG_ATX2ARXPGA, 3, 7, 0, + twl4030_dapm_dbypass_tlv); + +/* Digital bypass right (TX1R -> RX2R) */ +static const struct snd_kcontrol_new twl4030_dapm_dbypassr_control = + SOC_DAPM_SINGLE_TLV("Volume", + TWL4030_REG_ATX2ARXPGA, 0, 7, 0, + twl4030_dapm_dbypass_tlv); + +/* + * Voice Sidetone GAIN volume control: + * from -51 to -10 dB in 1 dB steps (mute instead of -51 dB) + */ +static DECLARE_TLV_DB_SCALE(twl4030_dapm_dbypassv_tlv, -5100, 100, 1); + +/* Digital bypass voice: sidetone (VUL -> VDL)*/ +static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control = + SOC_DAPM_SINGLE_TLV("Volume", + TWL4030_REG_VSTPGA, 0, 0x29, 0, + twl4030_dapm_dbypassv_tlv); + +/* + * Output PGA builder: + * Handle the muting and unmuting of the given output (turning off the + * amplifier associated with the output pin) + * On mute bypass the reg_cache and write 0 to the register + * On unmute: restore the register content from the reg_cache + * Outputs handled in this way: Earpiece, PreDrivL/R, CarkitL/R + */ +#define TWL4030_OUTPUT_PGA(pin_name, reg, mask) \ +static int pin_name##pga_event(struct snd_soc_dapm_widget *w, \ + struct snd_kcontrol *kcontrol, int event) \ +{ \ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); \ + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); \ + \ + switch (event) { \ + case SND_SOC_DAPM_POST_PMU: \ + twl4030->pin_name##_enabled = 1; \ + twl4030_write(component, reg, twl4030_read(component, reg)); \ + break; \ + case SND_SOC_DAPM_POST_PMD: \ + twl4030->pin_name##_enabled = 0; \ + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 0, reg); \ + break; \ + } \ + return 0; \ +} + +TWL4030_OUTPUT_PGA(earpiece, TWL4030_REG_EAR_CTL, TWL4030_EAR_GAIN); +TWL4030_OUTPUT_PGA(predrivel, TWL4030_REG_PREDL_CTL, TWL4030_PREDL_GAIN); +TWL4030_OUTPUT_PGA(predriver, TWL4030_REG_PREDR_CTL, TWL4030_PREDR_GAIN); +TWL4030_OUTPUT_PGA(carkitl, TWL4030_REG_PRECKL_CTL, TWL4030_PRECKL_GAIN); +TWL4030_OUTPUT_PGA(carkitr, TWL4030_REG_PRECKR_CTL, TWL4030_PRECKR_GAIN); + +static void handsfree_ramp(struct snd_soc_component *component, int reg, int ramp) +{ + unsigned char hs_ctl; + + hs_ctl = twl4030_read(component, reg); + + if (ramp) { + /* HF ramp-up */ + hs_ctl |= TWL4030_HF_CTL_REF_EN; + twl4030_write(component, reg, hs_ctl); + udelay(10); + hs_ctl |= TWL4030_HF_CTL_RAMP_EN; + twl4030_write(component, reg, hs_ctl); + udelay(40); + hs_ctl |= TWL4030_HF_CTL_LOOP_EN; + hs_ctl |= TWL4030_HF_CTL_HB_EN; + twl4030_write(component, reg, hs_ctl); + } else { + /* HF ramp-down */ + hs_ctl &= ~TWL4030_HF_CTL_LOOP_EN; + hs_ctl &= ~TWL4030_HF_CTL_HB_EN; + twl4030_write(component, reg, hs_ctl); + hs_ctl &= ~TWL4030_HF_CTL_RAMP_EN; + twl4030_write(component, reg, hs_ctl); + udelay(40); + hs_ctl &= ~TWL4030_HF_CTL_REF_EN; + twl4030_write(component, reg, hs_ctl); + } +} + +static int handsfreelpga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + handsfree_ramp(component, TWL4030_REG_HFL_CTL, 1); + break; + case SND_SOC_DAPM_POST_PMD: + handsfree_ramp(component, TWL4030_REG_HFL_CTL, 0); + break; + } + return 0; +} + +static int handsfreerpga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + handsfree_ramp(component, TWL4030_REG_HFR_CTL, 1); + break; + case SND_SOC_DAPM_POST_PMD: + handsfree_ramp(component, TWL4030_REG_HFR_CTL, 0); + break; + } + return 0; +} + +static int vibramux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + twl4030_write(component, TWL4030_REG_VIBRA_SET, 0xff); + return 0; +} + +static int apll_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + twl4030_apll_enable(component, 1); + break; + case SND_SOC_DAPM_POST_PMD: + twl4030_apll_enable(component, 0); + break; + } + return 0; +} + +static int aif_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u8 audio_if; + + audio_if = twl4030_read(component, TWL4030_REG_AUDIO_IF); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable AIF */ + /* enable the PLL before we use it to clock the DAI */ + twl4030_apll_enable(component, 1); + + twl4030_write(component, TWL4030_REG_AUDIO_IF, + audio_if | TWL4030_AIF_EN); + break; + case SND_SOC_DAPM_POST_PMD: + /* disable the DAI before we stop it's source PLL */ + twl4030_write(component, TWL4030_REG_AUDIO_IF, + audio_if & ~TWL4030_AIF_EN); + twl4030_apll_enable(component, 0); + break; + } + return 0; +} + +static void headset_ramp(struct snd_soc_component *component, int ramp) +{ + unsigned char hs_gain, hs_pop; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + struct twl4030_board_params *board_params = twl4030->board_params; + /* Base values for ramp delay calculation: 2^19 - 2^26 */ + static const unsigned int ramp_base[] = { + 524288, 1048576, 2097152, 4194304, + 8388608, 16777216, 33554432, 67108864 + }; + unsigned int delay; + + hs_gain = twl4030_read(component, TWL4030_REG_HS_GAIN_SET); + hs_pop = twl4030_read(component, TWL4030_REG_HS_POPN_SET); + delay = (ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] / + twl4030->sysclk) + 1; + + /* Enable external mute control, this dramatically reduces + * the pop-noise */ + if (board_params && board_params->hs_extmute) { + if (gpio_is_valid(board_params->hs_extmute_gpio)) { + gpio_set_value(board_params->hs_extmute_gpio, 1); + } else { + hs_pop |= TWL4030_EXTMUTE; + twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop); + } + } + + if (ramp) { + /* Headset ramp-up according to the TRM */ + hs_pop |= TWL4030_VMID_EN; + twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop); + /* Actually write to the register */ + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain, + TWL4030_REG_HS_GAIN_SET); + hs_pop |= TWL4030_RAMP_EN; + twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop); + /* Wait ramp delay time + 1, so the VMID can settle */ + twl4030_wait_ms(delay); + } else { + /* Headset ramp-down _not_ according to + * the TRM, but in a way that it is working */ + hs_pop &= ~TWL4030_RAMP_EN; + twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop); + /* Wait ramp delay time + 1, so the VMID can settle */ + twl4030_wait_ms(delay); + /* Bypass the reg_cache to mute the headset */ + twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, hs_gain & (~0x0f), + TWL4030_REG_HS_GAIN_SET); + + hs_pop &= ~TWL4030_VMID_EN; + twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop); + } + + /* Disable external mute */ + if (board_params && board_params->hs_extmute) { + if (gpio_is_valid(board_params->hs_extmute_gpio)) { + gpio_set_value(board_params->hs_extmute_gpio, 0); + } else { + hs_pop &= ~TWL4030_EXTMUTE; + twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop); + } + } +} + +static int headsetlpga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Do the ramp-up only once */ + if (!twl4030->hsr_enabled) + headset_ramp(component, 1); + + twl4030->hsl_enabled = 1; + break; + case SND_SOC_DAPM_POST_PMD: + /* Do the ramp-down only if both headsetL/R is disabled */ + if (!twl4030->hsr_enabled) + headset_ramp(component, 0); + + twl4030->hsl_enabled = 0; + break; + } + return 0; +} + +static int headsetrpga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Do the ramp-up only once */ + if (!twl4030->hsl_enabled) + headset_ramp(component, 1); + + twl4030->hsr_enabled = 1; + break; + case SND_SOC_DAPM_POST_PMD: + /* Do the ramp-down only if both headsetL/R is disabled */ + if (!twl4030->hsl_enabled) + headset_ramp(component, 0); + + twl4030->hsr_enabled = 0; + break; + } + return 0; +} + +static int digimic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + struct twl4030_board_params *board_params = twl4030->board_params; + + if (board_params && board_params->digimic_delay) + twl4030_wait_ms(board_params->digimic_delay); + return 0; +} + +/* + * Some of the gain controls in TWL (mostly those which are associated with + * the outputs) are implemented in an interesting way: + * 0x0 : Power down (mute) + * 0x1 : 6dB + * 0x2 : 0 dB + * 0x3 : -6 dB + * Inverting not going to help with these. + * Custom volsw and volsw_2r get/put functions to handle these gain bits. + */ +static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int mask = (1 << fls(max)) - 1; + + ucontrol->value.integer.value[0] = + (twl4030_read(component, reg) >> shift) & mask; + if (ucontrol->value.integer.value[0]) + ucontrol->value.integer.value[0] = + max + 1 - ucontrol->value.integer.value[0]; + + if (shift != rshift) { + ucontrol->value.integer.value[1] = + (twl4030_read(component, reg) >> rshift) & mask; + if (ucontrol->value.integer.value[1]) + ucontrol->value.integer.value[1] = + max + 1 - ucontrol->value.integer.value[1]; + } + + return 0; +} + +static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg = mc->reg; + unsigned int shift = mc->shift; + unsigned int rshift = mc->rshift; + int max = mc->max; + int mask = (1 << fls(max)) - 1; + unsigned short val, val2, val_mask; + + val = (ucontrol->value.integer.value[0] & mask); + + val_mask = mask << shift; + if (val) + val = max + 1 - val; + val = val << shift; + if (shift != rshift) { + val2 = (ucontrol->value.integer.value[1] & mask); + val_mask |= mask << rshift; + if (val2) + val2 = max + 1 - val2; + val |= val2 << rshift; + } + return snd_soc_component_update_bits(component, reg, val_mask, val); +} + +static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + int max = mc->max; + int mask = (1<value.integer.value[0] = + (twl4030_read(component, reg) >> shift) & mask; + ucontrol->value.integer.value[1] = + (twl4030_read(component, reg2) >> shift) & mask; + + if (ucontrol->value.integer.value[0]) + ucontrol->value.integer.value[0] = + max + 1 - ucontrol->value.integer.value[0]; + if (ucontrol->value.integer.value[1]) + ucontrol->value.integer.value[1] = + max + 1 - ucontrol->value.integer.value[1]; + + return 0; +} + +static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + unsigned int shift = mc->shift; + int max = mc->max; + int mask = (1 << fls(max)) - 1; + int err; + unsigned short val, val2, val_mask; + + val_mask = mask << shift; + val = (ucontrol->value.integer.value[0] & mask); + val2 = (ucontrol->value.integer.value[1] & mask); + + if (val) + val = max + 1 - val; + if (val2) + val2 = max + 1 - val2; + + val = val << shift; + val2 = val2 << shift; + + err = snd_soc_component_update_bits(component, reg, val_mask, val); + if (err < 0) + return err; + + err = snd_soc_component_update_bits(component, reg2, val_mask, val2); + return err; +} + +/* Codec operation modes */ +static const char *twl4030_op_modes_texts[] = { + "Option 2 (voice/audio)", "Option 1 (audio)" +}; + +static SOC_ENUM_SINGLE_DECL(twl4030_op_modes_enum, + TWL4030_REG_CODEC_MODE, 0, + twl4030_op_modes_texts); + +static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + + if (twl4030->configured) { + dev_err(component->dev, + "operation mode cannot be changed on-the-fly\n"); + return -EBUSY; + } + + return snd_soc_put_enum_double(kcontrol, ucontrol); +} + +/* + * FGAIN volume control: + * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB) + */ +static DECLARE_TLV_DB_SCALE(digital_fine_tlv, -6300, 100, 1); + +/* + * CGAIN volume control: + * 0 dB to 12 dB in 6 dB steps + * value 2 and 3 means 12 dB + */ +static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0); + +/* + * Voice Downlink GAIN volume control: + * from -37 to 12 dB in 1 dB steps (mute instead of -37 dB) + */ +static DECLARE_TLV_DB_SCALE(digital_voice_downlink_tlv, -3700, 100, 1); + +/* + * Analog playback gain + * -24 dB to 12 dB in 2 dB steps + */ +static DECLARE_TLV_DB_SCALE(analog_tlv, -2400, 200, 0); + +/* + * Gain controls tied to outputs + * -6 dB to 6 dB in 6 dB steps (mute instead of -12) + */ +static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1); + +/* + * Gain control for earpiece amplifier + * 0 dB to 12 dB in 6 dB steps (mute instead of -6) + */ +static DECLARE_TLV_DB_SCALE(output_ear_tvl, -600, 600, 1); + +/* + * Capture gain after the ADCs + * from 0 dB to 31 dB in 1 dB steps + */ +static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0); + +/* + * Gain control for input amplifiers + * 0 dB to 30 dB in 6 dB steps + */ +static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0); + +/* AVADC clock priority */ +static const char *twl4030_avadc_clk_priority_texts[] = { + "Voice high priority", "HiFi high priority" +}; + +static SOC_ENUM_SINGLE_DECL(twl4030_avadc_clk_priority_enum, + TWL4030_REG_AVADC_CTL, 2, + twl4030_avadc_clk_priority_texts); + +static const char *twl4030_rampdelay_texts[] = { + "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms", + "437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms", + "3495/2581/1748 ms" +}; + +static SOC_ENUM_SINGLE_DECL(twl4030_rampdelay_enum, + TWL4030_REG_HS_POPN_SET, 2, + twl4030_rampdelay_texts); + +/* Vibra H-bridge direction mode */ +static const char *twl4030_vibradirmode_texts[] = { + "Vibra H-bridge direction", "Audio data MSB", +}; + +static SOC_ENUM_SINGLE_DECL(twl4030_vibradirmode_enum, + TWL4030_REG_VIBRA_CTL, 5, + twl4030_vibradirmode_texts); + +/* Vibra H-bridge direction */ +static const char *twl4030_vibradir_texts[] = { + "Positive polarity", "Negative polarity", +}; + +static SOC_ENUM_SINGLE_DECL(twl4030_vibradir_enum, + TWL4030_REG_VIBRA_CTL, 1, + twl4030_vibradir_texts); + +/* Digimic Left and right swapping */ +static const char *twl4030_digimicswap_texts[] = { + "Not swapped", "Swapped", +}; + +static SOC_ENUM_SINGLE_DECL(twl4030_digimicswap_enum, + TWL4030_REG_MISC_SET_1, 0, + twl4030_digimicswap_texts); + +static const struct snd_kcontrol_new twl4030_snd_controls[] = { + /* Codec operation mode control */ + SOC_ENUM_EXT("Codec Operation Mode", twl4030_op_modes_enum, + snd_soc_get_enum_double, + snd_soc_put_twl4030_opmode_enum_double), + + /* Common playback gain controls */ + SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", + TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA, + 0, 0x3f, 0, digital_fine_tlv), + SOC_DOUBLE_R_TLV("DAC2 Digital Fine Playback Volume", + TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA, + 0, 0x3f, 0, digital_fine_tlv), + + SOC_DOUBLE_R_TLV("DAC1 Digital Coarse Playback Volume", + TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA, + 6, 0x2, 0, digital_coarse_tlv), + SOC_DOUBLE_R_TLV("DAC2 Digital Coarse Playback Volume", + TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA, + 6, 0x2, 0, digital_coarse_tlv), + + SOC_DOUBLE_R_TLV("DAC1 Analog Playback Volume", + TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL, + 3, 0x12, 1, analog_tlv), + SOC_DOUBLE_R_TLV("DAC2 Analog Playback Volume", + TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL, + 3, 0x12, 1, analog_tlv), + SOC_DOUBLE_R("DAC1 Analog Playback Switch", + TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL, + 1, 1, 0), + SOC_DOUBLE_R("DAC2 Analog Playback Switch", + TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL, + 1, 1, 0), + + /* Common voice downlink gain controls */ + SOC_SINGLE_TLV("DAC Voice Digital Downlink Volume", + TWL4030_REG_VRXPGA, 0, 0x31, 0, digital_voice_downlink_tlv), + + SOC_SINGLE_TLV("DAC Voice Analog Downlink Volume", + TWL4030_REG_VDL_APGA_CTL, 3, 0x12, 1, analog_tlv), + + SOC_SINGLE("DAC Voice Analog Downlink Switch", + TWL4030_REG_VDL_APGA_CTL, 1, 1, 0), + + /* Separate output gain controls */ + SOC_DOUBLE_R_EXT_TLV("PreDriv Playback Volume", + TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL, + 4, 3, 0, snd_soc_get_volsw_r2_twl4030, + snd_soc_put_volsw_r2_twl4030, output_tvl), + + SOC_DOUBLE_EXT_TLV("Headset Playback Volume", + TWL4030_REG_HS_GAIN_SET, 0, 2, 3, 0, snd_soc_get_volsw_twl4030, + snd_soc_put_volsw_twl4030, output_tvl), + + SOC_DOUBLE_R_EXT_TLV("Carkit Playback Volume", + TWL4030_REG_PRECKL_CTL, TWL4030_REG_PRECKR_CTL, + 4, 3, 0, snd_soc_get_volsw_r2_twl4030, + snd_soc_put_volsw_r2_twl4030, output_tvl), + + SOC_SINGLE_EXT_TLV("Earpiece Playback Volume", + TWL4030_REG_EAR_CTL, 4, 3, 0, snd_soc_get_volsw_twl4030, + snd_soc_put_volsw_twl4030, output_ear_tvl), + + /* Common capture gain controls */ + SOC_DOUBLE_R_TLV("TX1 Digital Capture Volume", + TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA, + 0, 0x1f, 0, digital_capture_tlv), + SOC_DOUBLE_R_TLV("TX2 Digital Capture Volume", + TWL4030_REG_AVTXL2PGA, TWL4030_REG_AVTXR2PGA, + 0, 0x1f, 0, digital_capture_tlv), + + SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN, + 0, 3, 5, 0, input_gain_tlv), + + SOC_ENUM("AVADC Clock Priority", twl4030_avadc_clk_priority_enum), + + SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum), + + SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum), + SOC_ENUM("Vibra H-bridge direction", twl4030_vibradir_enum), + + SOC_ENUM("Digimic LR Swap", twl4030_digimicswap_enum), +}; + +static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { + /* Left channel inputs */ + SND_SOC_DAPM_INPUT("MAINMIC"), + SND_SOC_DAPM_INPUT("HSMIC"), + SND_SOC_DAPM_INPUT("AUXL"), + SND_SOC_DAPM_INPUT("CARKITMIC"), + /* Right channel inputs */ + SND_SOC_DAPM_INPUT("SUBMIC"), + SND_SOC_DAPM_INPUT("AUXR"), + /* Digital microphones (Stereo) */ + SND_SOC_DAPM_INPUT("DIGIMIC0"), + SND_SOC_DAPM_INPUT("DIGIMIC1"), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("EARPIECE"), + SND_SOC_DAPM_OUTPUT("PREDRIVEL"), + SND_SOC_DAPM_OUTPUT("PREDRIVER"), + SND_SOC_DAPM_OUTPUT("HSOL"), + SND_SOC_DAPM_OUTPUT("HSOR"), + SND_SOC_DAPM_OUTPUT("CARKITL"), + SND_SOC_DAPM_OUTPUT("CARKITR"), + SND_SOC_DAPM_OUTPUT("HFL"), + SND_SOC_DAPM_OUTPUT("HFR"), + SND_SOC_DAPM_OUTPUT("VIBRA"), + + /* AIF and APLL clocks for running DAIs (including loopback) */ + SND_SOC_DAPM_OUTPUT("Virtual HiFi OUT"), + SND_SOC_DAPM_INPUT("Virtual HiFi IN"), + SND_SOC_DAPM_OUTPUT("Virtual Voice OUT"), + + /* DACs */ + SND_SOC_DAPM_DAC("DAC Right1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC Left1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC Right2", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC Left2", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DAC Voice", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("VAIFIN", "Voice Playback", 0, + TWL4030_REG_VOICE_IF, 6, 0), + + /* Analog bypasses */ + SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassr1_control), + SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassl1_control), + SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassr2_control), + SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassl2_control), + SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassv_control), + + /* Master analog loopback switch */ + SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0, + NULL, 0), + + /* Digital bypasses */ + SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassl_control), + SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassr_control), + SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassv_control), + + /* Digital mixers, power control for the physical DACs */ + SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer", + TWL4030_REG_AVDAC_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Digital L1 Playback Mixer", + TWL4030_REG_AVDAC_CTL, 1, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Digital R2 Playback Mixer", + TWL4030_REG_AVDAC_CTL, 2, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Digital L2 Playback Mixer", + TWL4030_REG_AVDAC_CTL, 3, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Digital Voice Playback Mixer", + TWL4030_REG_AVDAC_CTL, 4, 0, NULL, 0), + + /* Analog mixers, power control for the physical PGAs */ + SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", + TWL4030_REG_ARXR1_APGA_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", + TWL4030_REG_ARXL1_APGA_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", + TWL4030_REG_ARXR2_APGA_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", + TWL4030_REG_ARXL2_APGA_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer", + TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event, + SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("AIF Enable", SND_SOC_NOPM, 0, 0, aif_event, + SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD), + + /* Output MIXER controls */ + /* Earpiece */ + SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_earpiece_controls[0], + ARRAY_SIZE(twl4030_dapm_earpiece_controls)), + SND_SOC_DAPM_PGA_E("Earpiece PGA", SND_SOC_NOPM, + 0, 0, NULL, 0, earpiecepga_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + /* PreDrivL/R */ + SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_predrivel_controls[0], + ARRAY_SIZE(twl4030_dapm_predrivel_controls)), + SND_SOC_DAPM_PGA_E("PredriveL PGA", SND_SOC_NOPM, + 0, 0, NULL, 0, predrivelpga_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_predriver_controls[0], + ARRAY_SIZE(twl4030_dapm_predriver_controls)), + SND_SOC_DAPM_PGA_E("PredriveR PGA", SND_SOC_NOPM, + 0, 0, NULL, 0, predriverpga_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + /* HeadsetL/R */ + SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_hsol_controls[0], + ARRAY_SIZE(twl4030_dapm_hsol_controls)), + SND_SOC_DAPM_PGA_E("HeadsetL PGA", SND_SOC_NOPM, + 0, 0, NULL, 0, headsetlpga_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("HeadsetR Mixer", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_hsor_controls[0], + ARRAY_SIZE(twl4030_dapm_hsor_controls)), + SND_SOC_DAPM_PGA_E("HeadsetR PGA", SND_SOC_NOPM, + 0, 0, NULL, 0, headsetrpga_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + /* CarkitL/R */ + SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_carkitl_controls[0], + ARRAY_SIZE(twl4030_dapm_carkitl_controls)), + SND_SOC_DAPM_PGA_E("CarkitL PGA", SND_SOC_NOPM, + 0, 0, NULL, 0, carkitlpga_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_carkitr_controls[0], + ARRAY_SIZE(twl4030_dapm_carkitr_controls)), + SND_SOC_DAPM_PGA_E("CarkitR PGA", SND_SOC_NOPM, + 0, 0, NULL, 0, carkitrpga_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + + /* Output MUX controls */ + /* HandsfreeL/R */ + SND_SOC_DAPM_MUX("HandsfreeL Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_handsfreel_control), + SND_SOC_DAPM_SWITCH("HandsfreeL", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_handsfreelmute_control), + SND_SOC_DAPM_PGA_E("HandsfreeL PGA", SND_SOC_NOPM, + 0, 0, NULL, 0, handsfreelpga_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("HandsfreeR Mux", SND_SOC_NOPM, 5, 0, + &twl4030_dapm_handsfreer_control), + SND_SOC_DAPM_SWITCH("HandsfreeR", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_handsfreermute_control), + SND_SOC_DAPM_PGA_E("HandsfreeR PGA", SND_SOC_NOPM, + 0, 0, NULL, 0, handsfreerpga_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + /* Vibra */ + SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0, + &twl4030_dapm_vibra_control, vibramux_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_vibrapath_control), + + /* Introducing four virtual ADC, since TWL4030 have four channel for + capture */ + SND_SOC_DAPM_ADC("ADC Virtual Left1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC Virtual Right1", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC Virtual Left2", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC Virtual Right2", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("VAIFOUT", "Voice Capture", 0, + TWL4030_REG_VOICE_IF, 5, 0), + + /* Analog/Digital mic path selection. + TX1 Left/Right: either analog Left/Right or Digimic0 + TX2 Left/Right: either analog Left/Right or Digimic1 */ + SND_SOC_DAPM_MUX("TX1 Capture Route", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_micpathtx1_control), + SND_SOC_DAPM_MUX("TX2 Capture Route", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_micpathtx2_control), + + /* Analog input mixers for the capture amplifiers */ + SND_SOC_DAPM_MIXER("Analog Left", + TWL4030_REG_ANAMICL, 4, 0, + &twl4030_dapm_analoglmic_controls[0], + ARRAY_SIZE(twl4030_dapm_analoglmic_controls)), + SND_SOC_DAPM_MIXER("Analog Right", + TWL4030_REG_ANAMICR, 4, 0, + &twl4030_dapm_analogrmic_controls[0], + ARRAY_SIZE(twl4030_dapm_analogrmic_controls)), + + SND_SOC_DAPM_PGA("ADC Physical Left", + TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC Physical Right", + TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0), + + SND_SOC_DAPM_PGA_E("Digimic0 Enable", + TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0, + digimic_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("Digimic1 Enable", + TWL4030_REG_ADCMICSEL, 3, 0, NULL, 0, + digimic_event, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("micbias1 select", TWL4030_REG_MICBIAS_CTL, 5, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0, + NULL, 0), + + /* Microphone bias */ + SND_SOC_DAPM_SUPPLY("Mic Bias 1", + TWL4030_REG_MICBIAS_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Mic Bias 2", + TWL4030_REG_MICBIAS_CTL, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headset Mic Bias", + TWL4030_REG_MICBIAS_CTL, 2, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("VIF Enable", TWL4030_REG_VOICE_IF, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* Stream -> DAC mapping */ + {"DAC Right1", NULL, "HiFi Playback"}, + {"DAC Left1", NULL, "HiFi Playback"}, + {"DAC Right2", NULL, "HiFi Playback"}, + {"DAC Left2", NULL, "HiFi Playback"}, + {"DAC Voice", NULL, "VAIFIN"}, + + /* ADC -> Stream mapping */ + {"HiFi Capture", NULL, "ADC Virtual Left1"}, + {"HiFi Capture", NULL, "ADC Virtual Right1"}, + {"HiFi Capture", NULL, "ADC Virtual Left2"}, + {"HiFi Capture", NULL, "ADC Virtual Right2"}, + {"VAIFOUT", NULL, "ADC Virtual Left2"}, + {"VAIFOUT", NULL, "ADC Virtual Right2"}, + {"VAIFOUT", NULL, "VIF Enable"}, + + {"Digital L1 Playback Mixer", NULL, "DAC Left1"}, + {"Digital R1 Playback Mixer", NULL, "DAC Right1"}, + {"Digital L2 Playback Mixer", NULL, "DAC Left2"}, + {"Digital R2 Playback Mixer", NULL, "DAC Right2"}, + {"Digital Voice Playback Mixer", NULL, "DAC Voice"}, + + /* Supply for the digital part (APLL) */ + {"Digital Voice Playback Mixer", NULL, "APLL Enable"}, + + {"DAC Left1", NULL, "AIF Enable"}, + {"DAC Right1", NULL, "AIF Enable"}, + {"DAC Left2", NULL, "AIF Enable"}, + {"DAC Right1", NULL, "AIF Enable"}, + {"DAC Voice", NULL, "VIF Enable"}, + + {"Digital R2 Playback Mixer", NULL, "AIF Enable"}, + {"Digital L2 Playback Mixer", NULL, "AIF Enable"}, + + {"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"}, + {"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"}, + {"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"}, + {"Analog R2 Playback Mixer", NULL, "Digital R2 Playback Mixer"}, + {"Analog Voice Playback Mixer", NULL, "Digital Voice Playback Mixer"}, + + /* Internal playback routings */ + /* Earpiece */ + {"Earpiece Mixer", "Voice", "Analog Voice Playback Mixer"}, + {"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"}, + {"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"}, + {"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"}, + {"Earpiece PGA", NULL, "Earpiece Mixer"}, + /* PreDrivL */ + {"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"}, + {"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, + {"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, + {"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"}, + {"PredriveL PGA", NULL, "PredriveL Mixer"}, + /* PreDrivR */ + {"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"}, + {"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, + {"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, + {"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"}, + {"PredriveR PGA", NULL, "PredriveR Mixer"}, + /* HeadsetL */ + {"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"}, + {"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, + {"HeadsetL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, + {"HeadsetL PGA", NULL, "HeadsetL Mixer"}, + /* HeadsetR */ + {"HeadsetR Mixer", "Voice", "Analog Voice Playback Mixer"}, + {"HeadsetR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, + {"HeadsetR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, + {"HeadsetR PGA", NULL, "HeadsetR Mixer"}, + /* CarkitL */ + {"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"}, + {"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"}, + {"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"}, + {"CarkitL PGA", NULL, "CarkitL Mixer"}, + /* CarkitR */ + {"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"}, + {"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"}, + {"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"}, + {"CarkitR PGA", NULL, "CarkitR Mixer"}, + /* HandsfreeL */ + {"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"}, + {"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"}, + {"HandsfreeL Mux", "AudioL2", "Analog L2 Playback Mixer"}, + {"HandsfreeL Mux", "AudioR2", "Analog R2 Playback Mixer"}, + {"HandsfreeL", "Switch", "HandsfreeL Mux"}, + {"HandsfreeL PGA", NULL, "HandsfreeL"}, + /* HandsfreeR */ + {"HandsfreeR Mux", "Voice", "Analog Voice Playback Mixer"}, + {"HandsfreeR Mux", "AudioR1", "Analog R1 Playback Mixer"}, + {"HandsfreeR Mux", "AudioR2", "Analog R2 Playback Mixer"}, + {"HandsfreeR Mux", "AudioL2", "Analog L2 Playback Mixer"}, + {"HandsfreeR", "Switch", "HandsfreeR Mux"}, + {"HandsfreeR PGA", NULL, "HandsfreeR"}, + /* Vibra */ + {"Vibra Mux", "AudioL1", "DAC Left1"}, + {"Vibra Mux", "AudioR1", "DAC Right1"}, + {"Vibra Mux", "AudioL2", "DAC Left2"}, + {"Vibra Mux", "AudioR2", "DAC Right2"}, + + /* outputs */ + /* Must be always connected (for AIF and APLL) */ + {"Virtual HiFi OUT", NULL, "DAC Left1"}, + {"Virtual HiFi OUT", NULL, "DAC Right1"}, + {"Virtual HiFi OUT", NULL, "DAC Left2"}, + {"Virtual HiFi OUT", NULL, "DAC Right2"}, + /* Must be always connected (for APLL) */ + {"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"}, + /* Physical outputs */ + {"EARPIECE", NULL, "Earpiece PGA"}, + {"PREDRIVEL", NULL, "PredriveL PGA"}, + {"PREDRIVER", NULL, "PredriveR PGA"}, + {"HSOL", NULL, "HeadsetL PGA"}, + {"HSOR", NULL, "HeadsetR PGA"}, + {"CARKITL", NULL, "CarkitL PGA"}, + {"CARKITR", NULL, "CarkitR PGA"}, + {"HFL", NULL, "HandsfreeL PGA"}, + {"HFR", NULL, "HandsfreeR PGA"}, + {"Vibra Route", "Audio", "Vibra Mux"}, + {"VIBRA", NULL, "Vibra Route"}, + + /* Capture path */ + /* Must be always connected (for AIF and APLL) */ + {"ADC Virtual Left1", NULL, "Virtual HiFi IN"}, + {"ADC Virtual Right1", NULL, "Virtual HiFi IN"}, + {"ADC Virtual Left2", NULL, "Virtual HiFi IN"}, + {"ADC Virtual Right2", NULL, "Virtual HiFi IN"}, + /* Physical inputs */ + {"Analog Left", "Main Mic Capture Switch", "MAINMIC"}, + {"Analog Left", "Headset Mic Capture Switch", "HSMIC"}, + {"Analog Left", "AUXL Capture Switch", "AUXL"}, + {"Analog Left", "Carkit Mic Capture Switch", "CARKITMIC"}, + + {"Analog Right", "Sub Mic Capture Switch", "SUBMIC"}, + {"Analog Right", "AUXR Capture Switch", "AUXR"}, + + {"ADC Physical Left", NULL, "Analog Left"}, + {"ADC Physical Right", NULL, "Analog Right"}, + + {"Digimic0 Enable", NULL, "DIGIMIC0"}, + {"Digimic1 Enable", NULL, "DIGIMIC1"}, + + {"DIGIMIC0", NULL, "micbias1 select"}, + {"DIGIMIC1", NULL, "micbias2 select"}, + + /* TX1 Left capture path */ + {"TX1 Capture Route", "Analog", "ADC Physical Left"}, + {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, + /* TX1 Right capture path */ + {"TX1 Capture Route", "Analog", "ADC Physical Right"}, + {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, + /* TX2 Left capture path */ + {"TX2 Capture Route", "Analog", "ADC Physical Left"}, + {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"}, + /* TX2 Right capture path */ + {"TX2 Capture Route", "Analog", "ADC Physical Right"}, + {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"}, + + {"ADC Virtual Left1", NULL, "TX1 Capture Route"}, + {"ADC Virtual Right1", NULL, "TX1 Capture Route"}, + {"ADC Virtual Left2", NULL, "TX2 Capture Route"}, + {"ADC Virtual Right2", NULL, "TX2 Capture Route"}, + + {"ADC Virtual Left1", NULL, "AIF Enable"}, + {"ADC Virtual Right1", NULL, "AIF Enable"}, + {"ADC Virtual Left2", NULL, "AIF Enable"}, + {"ADC Virtual Right2", NULL, "AIF Enable"}, + + /* Analog bypass routes */ + {"Right1 Analog Loopback", "Switch", "Analog Right"}, + {"Left1 Analog Loopback", "Switch", "Analog Left"}, + {"Right2 Analog Loopback", "Switch", "Analog Right"}, + {"Left2 Analog Loopback", "Switch", "Analog Left"}, + {"Voice Analog Loopback", "Switch", "Analog Left"}, + + /* Supply for the Analog loopbacks */ + {"Right1 Analog Loopback", NULL, "FM Loop Enable"}, + {"Left1 Analog Loopback", NULL, "FM Loop Enable"}, + {"Right2 Analog Loopback", NULL, "FM Loop Enable"}, + {"Left2 Analog Loopback", NULL, "FM Loop Enable"}, + {"Voice Analog Loopback", NULL, "FM Loop Enable"}, + + {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"}, + {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"}, + {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, + {"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"}, + {"Analog Voice Playback Mixer", NULL, "Voice Analog Loopback"}, + + /* Digital bypass routes */ + {"Right Digital Loopback", "Volume", "TX1 Capture Route"}, + {"Left Digital Loopback", "Volume", "TX1 Capture Route"}, + {"Voice Digital Loopback", "Volume", "TX2 Capture Route"}, + + {"Digital R2 Playback Mixer", NULL, "Right Digital Loopback"}, + {"Digital L2 Playback Mixer", NULL, "Left Digital Loopback"}, + {"Digital Voice Playback Mixer", NULL, "Voice Digital Loopback"}, + +}; + +static int twl4030_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + twl4030_codec_enable(component, 1); + break; + case SND_SOC_BIAS_OFF: + twl4030_codec_enable(component, 0); + break; + } + + return 0; +} + +static void twl4030_constraints(struct twl4030_priv *twl4030, + struct snd_pcm_substream *mst_substream) +{ + struct snd_pcm_substream *slv_substream; + + /* Pick the stream, which need to be constrained */ + if (mst_substream == twl4030->master_substream) + slv_substream = twl4030->slave_substream; + else if (mst_substream == twl4030->slave_substream) + slv_substream = twl4030->master_substream; + else /* This should not happen.. */ + return; + + /* Set the constraints according to the already configured stream */ + snd_pcm_hw_constraint_single(slv_substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + twl4030->rate); + + snd_pcm_hw_constraint_single(slv_substream->runtime, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + twl4030->sample_bits); + + snd_pcm_hw_constraint_single(slv_substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + twl4030->channels); +} + +/* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for + * capture has to be enabled/disabled. */ +static void twl4030_tdm_enable(struct snd_soc_component *component, int direction, + int enable) +{ + u8 reg, mask; + + reg = twl4030_read(component, TWL4030_REG_OPTION); + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN; + else + mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN; + + if (enable) + reg |= mask; + else + reg &= ~mask; + + twl4030_write(component, TWL4030_REG_OPTION, reg); +} + +static int twl4030_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + + if (twl4030->master_substream) { + twl4030->slave_substream = substream; + /* The DAI has one configuration for playback and capture, so + * if the DAI has been already configured then constrain this + * substream to match it. */ + if (twl4030->configured) + twl4030_constraints(twl4030, twl4030->master_substream); + } else { + if (!(twl4030_read(component, TWL4030_REG_CODEC_MODE) & + TWL4030_OPTION_1)) { + /* In option2 4 channel is not supported, set the + * constraint for the first stream for channels, the + * second stream will 'inherit' this cosntraint */ + snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 2); + } + twl4030->master_substream = substream; + } + + return 0; +} + +static void twl4030_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + + if (twl4030->master_substream == substream) + twl4030->master_substream = twl4030->slave_substream; + + twl4030->slave_substream = NULL; + + /* If all streams are closed, or the remaining stream has not yet + * been configured than set the DAI as not configured. */ + if (!twl4030->master_substream) + twl4030->configured = 0; + else if (!twl4030->master_substream->runtime->channels) + twl4030->configured = 0; + + /* If the closing substream had 4 channel, do the necessary cleanup */ + if (substream->runtime->channels == 4) + twl4030_tdm_enable(component, substream->stream, 0); +} + +static int twl4030_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + u8 mode, old_mode, format, old_format; + + /* If the substream has 4 channel, do the necessary setup */ + if (params_channels(params) == 4) { + format = twl4030_read(component, TWL4030_REG_AUDIO_IF); + mode = twl4030_read(component, TWL4030_REG_CODEC_MODE); + + /* Safety check: are we in the correct operating mode and + * the interface is in TDM mode? */ + if ((mode & TWL4030_OPTION_1) && + ((format & TWL4030_AIF_FORMAT) == TWL4030_AIF_FORMAT_TDM)) + twl4030_tdm_enable(component, substream->stream, 1); + else + return -EINVAL; + } + + if (twl4030->configured) + /* Ignoring hw_params for already configured DAI */ + return 0; + + /* bit rate */ + old_mode = twl4030_read(component, + TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ; + mode = old_mode & ~TWL4030_APLL_RATE; + + switch (params_rate(params)) { + case 8000: + mode |= TWL4030_APLL_RATE_8000; + break; + case 11025: + mode |= TWL4030_APLL_RATE_11025; + break; + case 12000: + mode |= TWL4030_APLL_RATE_12000; + break; + case 16000: + mode |= TWL4030_APLL_RATE_16000; + break; + case 22050: + mode |= TWL4030_APLL_RATE_22050; + break; + case 24000: + mode |= TWL4030_APLL_RATE_24000; + break; + case 32000: + mode |= TWL4030_APLL_RATE_32000; + break; + case 44100: + mode |= TWL4030_APLL_RATE_44100; + break; + case 48000: + mode |= TWL4030_APLL_RATE_48000; + break; + case 96000: + mode |= TWL4030_APLL_RATE_96000; + break; + default: + dev_err(component->dev, "%s: unknown rate %d\n", __func__, + params_rate(params)); + return -EINVAL; + } + + /* sample size */ + old_format = twl4030_read(component, TWL4030_REG_AUDIO_IF); + format = old_format; + format &= ~TWL4030_DATA_WIDTH; + switch (params_width(params)) { + case 16: + format |= TWL4030_DATA_WIDTH_16S_16W; + break; + case 32: + format |= TWL4030_DATA_WIDTH_32S_24W; + break; + default: + dev_err(component->dev, "%s: unsupported bits/sample %d\n", + __func__, params_width(params)); + return -EINVAL; + } + + if (format != old_format || mode != old_mode) { + if (twl4030->codec_powered) { + /* + * If the codec is powered, than we need to toggle the + * codec power. + */ + twl4030_codec_enable(component, 0); + twl4030_write(component, TWL4030_REG_CODEC_MODE, mode); + twl4030_write(component, TWL4030_REG_AUDIO_IF, format); + twl4030_codec_enable(component, 1); + } else { + twl4030_write(component, TWL4030_REG_CODEC_MODE, mode); + twl4030_write(component, TWL4030_REG_AUDIO_IF, format); + } + } + + /* Store the important parameters for the DAI configuration and set + * the DAI as configured */ + twl4030->configured = 1; + twl4030->rate = params_rate(params); + twl4030->sample_bits = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; + twl4030->channels = params_channels(params); + + /* If both playback and capture streams are open, and one of them + * is setting the hw parameters right now (since we are here), set + * constraints to the other stream to match the current one. */ + if (twl4030->slave_substream) + twl4030_constraints(twl4030, substream); + + return 0; +} + +static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case 19200000: + case 26000000: + case 38400000: + break; + default: + dev_err(component->dev, "Unsupported HFCLKIN: %u\n", freq); + return -EINVAL; + } + + if ((freq / 1000) != twl4030->sysclk) { + dev_err(component->dev, + "Mismatch in HFCLKIN: %u (configured: %u)\n", + freq, twl4030->sysclk * 1000); + return -EINVAL; + } + + return 0; +} + +static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + u8 old_format, format; + + /* get format */ + old_format = twl4030_read(component, TWL4030_REG_AUDIO_IF); + format = old_format; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + format &= ~(TWL4030_AIF_SLAVE_EN); + format &= ~(TWL4030_CLK256FS_EN); + break; + case SND_SOC_DAIFMT_CBC_CFC: + format |= TWL4030_AIF_SLAVE_EN; + format |= TWL4030_CLK256FS_EN; + break; + default: + return -EINVAL; + } + + /* interface format */ + format &= ~TWL4030_AIF_FORMAT; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format |= TWL4030_AIF_FORMAT_CODEC; + break; + case SND_SOC_DAIFMT_DSP_A: + format |= TWL4030_AIF_FORMAT_TDM; + break; + default: + return -EINVAL; + } + + if (format != old_format) { + if (twl4030->codec_powered) { + /* + * If the codec is powered, than we need to toggle the + * codec power. + */ + twl4030_codec_enable(component, 0); + twl4030_write(component, TWL4030_REG_AUDIO_IF, format); + twl4030_codec_enable(component, 1); + } else { + twl4030_write(component, TWL4030_REG_AUDIO_IF, format); + } + } + + return 0; +} + +static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_component *component = dai->component; + u8 reg = twl4030_read(component, TWL4030_REG_AUDIO_IF); + + if (tristate) + reg |= TWL4030_AIF_TRI_EN; + else + reg &= ~TWL4030_AIF_TRI_EN; + + return twl4030_write(component, TWL4030_REG_AUDIO_IF, reg); +} + +/* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R + * (VTXL, VTXR) for uplink has to be enabled/disabled. */ +static void twl4030_voice_enable(struct snd_soc_component *component, int direction, + int enable) +{ + u8 reg, mask; + + reg = twl4030_read(component, TWL4030_REG_OPTION); + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + mask = TWL4030_ARXL1_VRX_EN; + else + mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN; + + if (enable) + reg |= mask; + else + reg &= ~mask; + + twl4030_write(component, TWL4030_REG_OPTION, reg); +} + +static int twl4030_voice_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + u8 mode; + + /* If the system master clock is not 26MHz, the voice PCM interface is + * not available. + */ + if (twl4030->sysclk != 26000) { + dev_err(component->dev, + "%s: HFCLKIN is %u KHz, voice interface needs 26MHz\n", + __func__, twl4030->sysclk); + return -EINVAL; + } + + /* If the codec mode is not option2, the voice PCM interface is not + * available. + */ + mode = twl4030_read(component, TWL4030_REG_CODEC_MODE) + & TWL4030_OPT_MODE; + + if (mode != TWL4030_OPTION_2) { + dev_err(component->dev, "%s: the codec mode is not option2\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static void twl4030_voice_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + /* Enable voice digital filters */ + twl4030_voice_enable(component, substream->stream, 0); +} + +static int twl4030_voice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + u8 old_mode, mode; + + /* Enable voice digital filters */ + twl4030_voice_enable(component, substream->stream, 1); + + /* bit rate */ + old_mode = twl4030_read(component, + TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ; + mode = old_mode; + + switch (params_rate(params)) { + case 8000: + mode &= ~(TWL4030_SEL_16K); + break; + case 16000: + mode |= TWL4030_SEL_16K; + break; + default: + dev_err(component->dev, "%s: unknown rate %d\n", __func__, + params_rate(params)); + return -EINVAL; + } + + if (mode != old_mode) { + if (twl4030->codec_powered) { + /* + * If the codec is powered, than we need to toggle the + * codec power. + */ + twl4030_codec_enable(component, 0); + twl4030_write(component, TWL4030_REG_CODEC_MODE, mode); + twl4030_codec_enable(component, 1); + } else { + twl4030_write(component, TWL4030_REG_CODEC_MODE, mode); + } + } + + return 0; +} + +static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + + if (freq != 26000000) { + dev_err(component->dev, + "%s: HFCLKIN is %u KHz, voice interface needs 26MHz\n", + __func__, freq / 1000); + return -EINVAL; + } + if ((freq / 1000) != twl4030->sysclk) { + dev_err(component->dev, + "Mismatch in HFCLKIN: %u (configured: %u)\n", + freq, twl4030->sysclk * 1000); + return -EINVAL; + } + return 0; +} + +static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + u8 old_format, format; + + /* get format */ + old_format = twl4030_read(component, TWL4030_REG_VOICE_IF); + format = old_format; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + format &= ~(TWL4030_VIF_SLAVE_EN); + break; + case SND_SOC_DAIFMT_CBS_CFS: + format |= TWL4030_VIF_SLAVE_EN; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + format &= ~(TWL4030_VIF_FORMAT); + break; + case SND_SOC_DAIFMT_NB_IF: + format |= TWL4030_VIF_FORMAT; + break; + default: + return -EINVAL; + } + + if (format != old_format) { + if (twl4030->codec_powered) { + /* + * If the codec is powered, than we need to toggle the + * codec power. + */ + twl4030_codec_enable(component, 0); + twl4030_write(component, TWL4030_REG_VOICE_IF, format); + twl4030_codec_enable(component, 1); + } else { + twl4030_write(component, TWL4030_REG_VOICE_IF, format); + } + } + + return 0; +} + +static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_component *component = dai->component; + u8 reg = twl4030_read(component, TWL4030_REG_VOICE_IF); + + if (tristate) + reg |= TWL4030_VIF_TRI_EN; + else + reg &= ~TWL4030_VIF_TRI_EN; + + return twl4030_write(component, TWL4030_REG_VOICE_IF, reg); +} + +#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) +#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops twl4030_dai_hifi_ops = { + .startup = twl4030_startup, + .shutdown = twl4030_shutdown, + .hw_params = twl4030_hw_params, + .set_sysclk = twl4030_set_dai_sysclk, + .set_fmt = twl4030_set_dai_fmt, + .set_tristate = twl4030_set_tristate, +}; + +static const struct snd_soc_dai_ops twl4030_dai_voice_ops = { + .startup = twl4030_voice_startup, + .shutdown = twl4030_voice_shutdown, + .hw_params = twl4030_voice_hw_params, + .set_sysclk = twl4030_voice_set_dai_sysclk, + .set_fmt = twl4030_voice_set_dai_fmt, + .set_tristate = twl4030_voice_set_tristate, +}; + +static struct snd_soc_dai_driver twl4030_dai[] = { +{ + .name = "twl4030-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 4, + .rates = TWL4030_RATES | SNDRV_PCM_RATE_96000, + .formats = TWL4030_FORMATS, + .sig_bits = 24,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 2, + .channels_max = 4, + .rates = TWL4030_RATES, + .formats = TWL4030_FORMATS, + .sig_bits = 24,}, + .ops = &twl4030_dai_hifi_ops, +}, +{ + .name = "twl4030-voice", + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .stream_name = "Voice Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = &twl4030_dai_voice_ops, +}, +}; + +static int twl4030_soc_probe(struct snd_soc_component *component) +{ + struct twl4030_priv *twl4030; + + twl4030 = devm_kzalloc(component->dev, sizeof(struct twl4030_priv), + GFP_KERNEL); + if (!twl4030) + return -ENOMEM; + snd_soc_component_set_drvdata(component, twl4030); + /* Set the defaults, and power up the codec */ + twl4030->sysclk = twl4030_audio_get_mclk() / 1000; + + twl4030_init_chip(component); + + return 0; +} + +static void twl4030_soc_remove(struct snd_soc_component *component) +{ + struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component); + struct twl4030_board_params *board_params = twl4030->board_params; + + if (board_params && board_params->hs_extmute && + gpio_is_valid(board_params->hs_extmute_gpio)) + gpio_free(board_params->hs_extmute_gpio); +} + +static const struct snd_soc_component_driver soc_component_dev_twl4030 = { + .probe = twl4030_soc_probe, + .remove = twl4030_soc_remove, + .read = twl4030_read, + .write = twl4030_write, + .set_bias_level = twl4030_set_bias_level, + .controls = twl4030_snd_controls, + .num_controls = ARRAY_SIZE(twl4030_snd_controls), + .dapm_widgets = twl4030_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(twl4030_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int twl4030_codec_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_twl4030, + twl4030_dai, ARRAY_SIZE(twl4030_dai)); +} + +MODULE_ALIAS("platform:twl4030-codec"); + +static struct platform_driver twl4030_codec_driver = { + .probe = twl4030_codec_probe, + .driver = { + .name = "twl4030-codec", + }, +}; + +module_platform_driver(twl4030_codec_driver); + +MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); +MODULE_AUTHOR("Steve Sakoman"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c new file mode 100644 index 0000000000..dd5ee5dc0c --- /dev/null +++ b/sound/soc/codecs/twl6040.c @@ -0,0 +1,1176 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC TWL6040 codec driver + * + * Author: Misael Lopez Cruz + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "twl6040.h" + +enum twl6040_dai_id { + TWL6040_DAI_LEGACY = 0, + TWL6040_DAI_UL, + TWL6040_DAI_DL1, + TWL6040_DAI_DL2, + TWL6040_DAI_VIB, +}; + +#define TWL6040_RATES SNDRV_PCM_RATE_8000_96000 +#define TWL6040_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) + +#define TWL6040_OUTHS_0dB 0x00 +#define TWL6040_OUTHS_M30dB 0x0F +#define TWL6040_OUTHF_0dB 0x03 +#define TWL6040_OUTHF_M52dB 0x1D + +#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1) + +struct twl6040_jack_data { + struct snd_soc_jack *jack; + struct delayed_work work; + int report; +}; + +/* codec private data */ +struct twl6040_data { + int plug_irq; + int codec_powered; + int pll; + int pll_power_mode; + int hs_power_mode; + int hs_power_mode_locked; + bool dl1_unmuted; + bool dl2_unmuted; + u8 dl12_cache[TWL6040_REG_HFRCTL - TWL6040_REG_HSLCTL + 1]; + unsigned int clk_in; + unsigned int sysclk; + struct twl6040_jack_data hs_jack; + struct snd_soc_component *component; + struct mutex mutex; +}; + +/* set of rates for each pll: low-power and high-performance */ +static const unsigned int lp_rates[] = { + 8000, + 11250, + 16000, + 22500, + 32000, + 44100, + 48000, + 88200, + 96000, +}; + +static const unsigned int hp_rates[] = { + 8000, + 16000, + 32000, + 48000, + 96000, +}; + +static const struct snd_pcm_hw_constraint_list sysclk_constraints[] = { + { .count = ARRAY_SIZE(lp_rates), .list = lp_rates, }, + { .count = ARRAY_SIZE(hp_rates), .list = hp_rates, }, +}; + +#define to_twl6040(component) dev_get_drvdata((component)->dev->parent) + +static unsigned int twl6040_read(struct snd_soc_component *component, unsigned int reg) +{ + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + struct twl6040 *twl6040 = to_twl6040(component); + u8 value; + + if (reg >= TWL6040_CACHEREGNUM) + return -EIO; + + switch (reg) { + case TWL6040_REG_HSLCTL: + case TWL6040_REG_HSRCTL: + case TWL6040_REG_EARCTL: + case TWL6040_REG_HFLCTL: + case TWL6040_REG_HFRCTL: + value = priv->dl12_cache[reg - TWL6040_REG_HSLCTL]; + break; + default: + value = twl6040_reg_read(twl6040, reg); + break; + } + + return value; +} + +static bool twl6040_can_write_to_chip(struct snd_soc_component *component, + unsigned int reg) +{ + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + + switch (reg) { + case TWL6040_REG_HSLCTL: + case TWL6040_REG_HSRCTL: + case TWL6040_REG_EARCTL: + /* DL1 path */ + return priv->dl1_unmuted; + case TWL6040_REG_HFLCTL: + case TWL6040_REG_HFRCTL: + return priv->dl2_unmuted; + default: + return true; + } +} + +static inline void twl6040_update_dl12_cache(struct snd_soc_component *component, + u8 reg, u8 value) +{ + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + + switch (reg) { + case TWL6040_REG_HSLCTL: + case TWL6040_REG_HSRCTL: + case TWL6040_REG_EARCTL: + case TWL6040_REG_HFLCTL: + case TWL6040_REG_HFRCTL: + priv->dl12_cache[reg - TWL6040_REG_HSLCTL] = value; + break; + default: + break; + } +} + +static int twl6040_write(struct snd_soc_component *component, + unsigned int reg, unsigned int value) +{ + struct twl6040 *twl6040 = to_twl6040(component); + + if (reg >= TWL6040_CACHEREGNUM) + return -EIO; + + twl6040_update_dl12_cache(component, reg, value); + if (twl6040_can_write_to_chip(component, reg)) + return twl6040_reg_write(twl6040, reg, value); + else + return 0; +} + +static void twl6040_init_chip(struct snd_soc_component *component) +{ + twl6040_read(component, TWL6040_REG_TRIM1); + twl6040_read(component, TWL6040_REG_TRIM2); + twl6040_read(component, TWL6040_REG_TRIM3); + twl6040_read(component, TWL6040_REG_HSOTRIM); + twl6040_read(component, TWL6040_REG_HFOTRIM); + + /* Change chip defaults */ + /* No imput selected for microphone amplifiers */ + twl6040_write(component, TWL6040_REG_MICLCTL, 0x18); + twl6040_write(component, TWL6040_REG_MICRCTL, 0x18); + + /* + * We need to lower the default gain values, so the ramp code + * can work correctly for the first playback. + * This reduces the pop noise heard at the first playback. + */ + twl6040_write(component, TWL6040_REG_HSGAIN, 0xff); + twl6040_write(component, TWL6040_REG_EARCTL, 0x1e); + twl6040_write(component, TWL6040_REG_HFLGAIN, 0x1d); + twl6040_write(component, TWL6040_REG_HFRGAIN, 0x1d); + twl6040_write(component, TWL6040_REG_LINEGAIN, 0); +} + +/* set headset dac and driver power mode */ +static int headset_power_mode(struct snd_soc_component *component, int high_perf) +{ + int hslctl, hsrctl; + int mask = TWL6040_HSDRVMODE | TWL6040_HSDACMODE; + + hslctl = twl6040_read(component, TWL6040_REG_HSLCTL); + hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL); + + if (high_perf) { + hslctl &= ~mask; + hsrctl &= ~mask; + } else { + hslctl |= mask; + hsrctl |= mask; + } + + twl6040_write(component, TWL6040_REG_HSLCTL, hslctl); + twl6040_write(component, TWL6040_REG_HSRCTL, hsrctl); + + return 0; +} + +static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u8 hslctl, hsrctl; + + /* + * Workaround for Headset DC offset caused pop noise: + * Both HS DAC need to be turned on (before the HS driver) and off at + * the same time. + */ + hslctl = twl6040_read(component, TWL6040_REG_HSLCTL); + hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL); + if (SND_SOC_DAPM_EVENT_ON(event)) { + hslctl |= TWL6040_HSDACENA; + hsrctl |= TWL6040_HSDACENA; + } else { + hslctl &= ~TWL6040_HSDACENA; + hsrctl &= ~TWL6040_HSDACENA; + } + twl6040_write(component, TWL6040_REG_HSLCTL, hslctl); + twl6040_write(component, TWL6040_REG_HSRCTL, hsrctl); + + msleep(1); + return 0; +} + +static int twl6040_ep_drv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + int ret = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Earphone doesn't support low power mode */ + priv->hs_power_mode_locked = 1; + ret = headset_power_mode(component, 1); + } else { + priv->hs_power_mode_locked = 0; + ret = headset_power_mode(component, priv->hs_power_mode); + } + + msleep(1); + + return ret; +} + +static void twl6040_hs_jack_report(struct snd_soc_component *component, + struct snd_soc_jack *jack, int report) +{ + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + int status; + + mutex_lock(&priv->mutex); + + /* Sync status */ + status = twl6040_read(component, TWL6040_REG_STATUS); + if (status & TWL6040_PLUGCOMP) + snd_soc_jack_report(jack, report, report); + else + snd_soc_jack_report(jack, 0, report); + + mutex_unlock(&priv->mutex); +} + +void twl6040_hs_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, int report) +{ + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + struct twl6040_jack_data *hs_jack = &priv->hs_jack; + + hs_jack->jack = jack; + hs_jack->report = report; + + twl6040_hs_jack_report(component, hs_jack->jack, hs_jack->report); +} +EXPORT_SYMBOL_GPL(twl6040_hs_jack_detect); + +static void twl6040_accessory_work(struct work_struct *work) +{ + struct twl6040_data *priv = container_of(work, + struct twl6040_data, hs_jack.work.work); + struct snd_soc_component *component = priv->component; + struct twl6040_jack_data *hs_jack = &priv->hs_jack; + + twl6040_hs_jack_report(component, hs_jack->jack, hs_jack->report); +} + +/* audio interrupt handler */ +static irqreturn_t twl6040_audio_handler(int irq, void *data) +{ + struct snd_soc_component *component = data; + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + + queue_delayed_work(system_power_efficient_wq, + &priv->hs_jack.work, msecs_to_jiffies(200)); + + return IRQ_HANDLED; +} + +static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + + /* Do not allow changes while Input/FF efect is running */ + val = twl6040_read(component, e->reg); + if (val & TWL6040_VIBENA && !(val & TWL6040_VIBSEL)) + return -EBUSY; + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +/* + * MICATT volume control: + * from -6 to 0 dB in 6 dB steps + */ +static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0); + +/* + * MICGAIN volume control: + * from 6 to 30 dB in 6 dB steps + */ +static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0); + +/* + * AFMGAIN volume control: + * from -18 to 24 dB in 6 dB steps + */ +static DECLARE_TLV_DB_SCALE(afm_amp_tlv, -1800, 600, 0); + +/* + * HSGAIN volume control: + * from -30 to 0 dB in 2 dB steps + */ +static DECLARE_TLV_DB_SCALE(hs_tlv, -3000, 200, 0); + +/* + * HFGAIN volume control: + * from -52 to 6 dB in 2 dB steps + */ +static DECLARE_TLV_DB_SCALE(hf_tlv, -5200, 200, 0); + +/* + * EPGAIN volume control: + * from -24 to 6 dB in 2 dB steps + */ +static DECLARE_TLV_DB_SCALE(ep_tlv, -2400, 200, 0); + +/* Left analog microphone selection */ +static const char *twl6040_amicl_texts[] = + {"Headset Mic", "Main Mic", "Aux/FM Left", "Off"}; + +/* Right analog microphone selection */ +static const char *twl6040_amicr_texts[] = + {"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"}; + +static const struct soc_enum twl6040_enum[] = { + SOC_ENUM_SINGLE(TWL6040_REG_MICLCTL, 3, + ARRAY_SIZE(twl6040_amicl_texts), twl6040_amicl_texts), + SOC_ENUM_SINGLE(TWL6040_REG_MICRCTL, 3, + ARRAY_SIZE(twl6040_amicr_texts), twl6040_amicr_texts), +}; + +static const char *twl6040_hs_texts[] = { + "Off", "HS DAC", "Line-In amp" +}; + +static const struct soc_enum twl6040_hs_enum[] = { + SOC_ENUM_SINGLE(TWL6040_REG_HSLCTL, 5, ARRAY_SIZE(twl6040_hs_texts), + twl6040_hs_texts), + SOC_ENUM_SINGLE(TWL6040_REG_HSRCTL, 5, ARRAY_SIZE(twl6040_hs_texts), + twl6040_hs_texts), +}; + +static const char *twl6040_hf_texts[] = { + "Off", "HF DAC", "Line-In amp" +}; + +static const struct soc_enum twl6040_hf_enum[] = { + SOC_ENUM_SINGLE(TWL6040_REG_HFLCTL, 2, ARRAY_SIZE(twl6040_hf_texts), + twl6040_hf_texts), + SOC_ENUM_SINGLE(TWL6040_REG_HFRCTL, 2, ARRAY_SIZE(twl6040_hf_texts), + twl6040_hf_texts), +}; + +static const char *twl6040_vibrapath_texts[] = { + "Input FF", "Audio PDM" +}; + +static const struct soc_enum twl6040_vibra_enum[] = { + SOC_ENUM_SINGLE(TWL6040_REG_VIBCTLL, 1, + ARRAY_SIZE(twl6040_vibrapath_texts), + twl6040_vibrapath_texts), + SOC_ENUM_SINGLE(TWL6040_REG_VIBCTLR, 1, + ARRAY_SIZE(twl6040_vibrapath_texts), + twl6040_vibrapath_texts), +}; + +static const struct snd_kcontrol_new amicl_control = + SOC_DAPM_ENUM("Route", twl6040_enum[0]); + +static const struct snd_kcontrol_new amicr_control = + SOC_DAPM_ENUM("Route", twl6040_enum[1]); + +/* Headset DAC playback switches */ +static const struct snd_kcontrol_new hsl_mux_controls = + SOC_DAPM_ENUM("Route", twl6040_hs_enum[0]); + +static const struct snd_kcontrol_new hsr_mux_controls = + SOC_DAPM_ENUM("Route", twl6040_hs_enum[1]); + +/* Handsfree DAC playback switches */ +static const struct snd_kcontrol_new hfl_mux_controls = + SOC_DAPM_ENUM("Route", twl6040_hf_enum[0]); + +static const struct snd_kcontrol_new hfr_mux_controls = + SOC_DAPM_ENUM("Route", twl6040_hf_enum[1]); + +static const struct snd_kcontrol_new ep_path_enable_control = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const struct snd_kcontrol_new auxl_switch_control = + SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 6, 1, 0); + +static const struct snd_kcontrol_new auxr_switch_control = + SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 6, 1, 0); + +/* Vibra playback switches */ +static const struct snd_kcontrol_new vibral_mux_controls = + SOC_DAPM_ENUM_EXT("Route", twl6040_vibra_enum[0], + snd_soc_dapm_get_enum_double, + twl6040_soc_dapm_put_vibra_enum); + +static const struct snd_kcontrol_new vibrar_mux_controls = + SOC_DAPM_ENUM_EXT("Route", twl6040_vibra_enum[1], + snd_soc_dapm_get_enum_double, + twl6040_soc_dapm_put_vibra_enum); + +/* Headset power mode */ +static const char *twl6040_power_mode_texts[] = { + "Low-Power", "High-Performance", +}; + +static SOC_ENUM_SINGLE_EXT_DECL(twl6040_power_mode_enum, + twl6040_power_mode_texts); + +static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = priv->hs_power_mode; + + return 0; +} + +static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + int high_perf = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (!priv->hs_power_mode_locked) + ret = headset_power_mode(component, high_perf); + + if (!ret) + priv->hs_power_mode = high_perf; + + return ret; +} + +static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = priv->pll_power_mode; + + return 0; +} + +static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + + priv->pll_power_mode = ucontrol->value.enumerated.item[0]; + + return 0; +} + +int twl6040_get_dl1_gain(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + if (snd_soc_dapm_get_pin_status(dapm, "EP")) + return -1; /* -1dB */ + + if (snd_soc_dapm_get_pin_status(dapm, "HSOR") || + snd_soc_dapm_get_pin_status(dapm, "HSOL")) { + + u8 val = twl6040_read(component, TWL6040_REG_HSLCTL); + if (val & TWL6040_HSDACMODE) + /* HSDACL in LP mode */ + return -8; /* -8dB */ + else + /* HSDACL in HP mode */ + return -1; /* -1dB */ + } + return 0; /* 0dB */ +} +EXPORT_SYMBOL_GPL(twl6040_get_dl1_gain); + +int twl6040_get_clk_id(struct snd_soc_component *component) +{ + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + + return priv->pll_power_mode; +} +EXPORT_SYMBOL_GPL(twl6040_get_clk_id); + +int twl6040_get_trim_value(struct snd_soc_component *component, enum twl6040_trim trim) +{ + if (unlikely(trim >= TWL6040_TRIM_INVAL)) + return -EINVAL; + + return twl6040_read(component, TWL6040_REG_TRIM1 + trim); +} +EXPORT_SYMBOL_GPL(twl6040_get_trim_value); + +int twl6040_get_hs_step_size(struct snd_soc_component *component) +{ + struct twl6040 *twl6040 = to_twl6040(component); + + if (twl6040_get_revid(twl6040) < TWL6040_REV_ES1_3) + /* For ES under ES_1.3 HS step is 2 mV */ + return 2; + else + /* For ES_1.3 HS step is 1 mV */ + return 1; +} +EXPORT_SYMBOL_GPL(twl6040_get_hs_step_size); + +static const struct snd_kcontrol_new twl6040_snd_controls[] = { + /* Capture gains */ + SOC_DOUBLE_TLV("Capture Preamplifier Volume", + TWL6040_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv), + SOC_DOUBLE_TLV("Capture Volume", + TWL6040_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv), + + /* AFM gains */ + SOC_DOUBLE_TLV("Aux FM Volume", + TWL6040_REG_LINEGAIN, 0, 3, 7, 0, afm_amp_tlv), + + /* Playback gains */ + SOC_DOUBLE_TLV("Headset Playback Volume", + TWL6040_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv), + SOC_DOUBLE_R_TLV("Handsfree Playback Volume", + TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), + SOC_SINGLE_TLV("Earphone Playback Volume", + TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), + + SOC_ENUM_EXT("Headset Power Mode", twl6040_power_mode_enum, + twl6040_headset_power_get_enum, + twl6040_headset_power_put_enum), + + /* Left HS PDM data routed to Right HSDAC */ + SOC_SINGLE("Headset Mono to Stereo Playback Switch", + TWL6040_REG_HSRCTL, 7, 1, 0), + + /* Left HF PDM data routed to Right HFDAC */ + SOC_SINGLE("Handsfree Mono to Stereo Playback Switch", + TWL6040_REG_HFRCTL, 5, 1, 0), + + SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum, + twl6040_pll_get_enum, twl6040_pll_put_enum), +}; + +static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { + /* Inputs */ + SND_SOC_DAPM_INPUT("MAINMIC"), + SND_SOC_DAPM_INPUT("HSMIC"), + SND_SOC_DAPM_INPUT("SUBMIC"), + SND_SOC_DAPM_INPUT("AFML"), + SND_SOC_DAPM_INPUT("AFMR"), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("HSOL"), + SND_SOC_DAPM_OUTPUT("HSOR"), + SND_SOC_DAPM_OUTPUT("HFL"), + SND_SOC_DAPM_OUTPUT("HFR"), + SND_SOC_DAPM_OUTPUT("EP"), + SND_SOC_DAPM_OUTPUT("AUXL"), + SND_SOC_DAPM_OUTPUT("AUXR"), + SND_SOC_DAPM_OUTPUT("VIBRAL"), + SND_SOC_DAPM_OUTPUT("VIBRAR"), + + /* Analog input muxes for the capture amplifiers */ + SND_SOC_DAPM_MUX("Analog Left Capture Route", + SND_SOC_NOPM, 0, 0, &amicl_control), + SND_SOC_DAPM_MUX("Analog Right Capture Route", + SND_SOC_NOPM, 0, 0, &amicr_control), + + /* Analog capture PGAs */ + SND_SOC_DAPM_PGA("MicAmpL", + TWL6040_REG_MICLCTL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("MicAmpR", + TWL6040_REG_MICRCTL, 0, 0, NULL, 0), + + /* Auxiliary FM PGAs */ + SND_SOC_DAPM_PGA("AFMAmpL", + TWL6040_REG_MICLCTL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("AFMAmpR", + TWL6040_REG_MICRCTL, 1, 0, NULL, 0), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC Left", NULL, TWL6040_REG_MICLCTL, 2, 0), + SND_SOC_DAPM_ADC("ADC Right", NULL, TWL6040_REG_MICRCTL, 2, 0), + + /* Microphone bias */ + SND_SOC_DAPM_SUPPLY("Headset Mic Bias", + TWL6040_REG_AMICBCTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Main Mic Bias", + TWL6040_REG_AMICBCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Digital Mic1 Bias", + TWL6040_REG_DMICBCTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Digital Mic2 Bias", + TWL6040_REG_DMICBCTL, 4, 0, NULL, 0), + + /* DACs */ + SND_SOC_DAPM_DAC("HSDAC Left", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("HSDAC Right", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("HFDAC Left", NULL, TWL6040_REG_HFLCTL, 0, 0), + SND_SOC_DAPM_DAC("HFDAC Right", NULL, TWL6040_REG_HFRCTL, 0, 0), + /* Virtual DAC for vibra path (DL4 channel) */ + SND_SOC_DAPM_DAC("VIBRA DAC", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("Handsfree Left Playback", + SND_SOC_NOPM, 0, 0, &hfl_mux_controls), + SND_SOC_DAPM_MUX("Handsfree Right Playback", + SND_SOC_NOPM, 0, 0, &hfr_mux_controls), + /* Analog playback Muxes */ + SND_SOC_DAPM_MUX("Headset Left Playback", + SND_SOC_NOPM, 0, 0, &hsl_mux_controls), + SND_SOC_DAPM_MUX("Headset Right Playback", + SND_SOC_NOPM, 0, 0, &hsr_mux_controls), + + SND_SOC_DAPM_MUX("Vibra Left Playback", SND_SOC_NOPM, 0, 0, + &vibral_mux_controls), + SND_SOC_DAPM_MUX("Vibra Right Playback", SND_SOC_NOPM, 0, 0, + &vibrar_mux_controls), + + SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0, + &ep_path_enable_control), + SND_SOC_DAPM_SWITCH("AUXL Playback", SND_SOC_NOPM, 0, 0, + &auxl_switch_control), + SND_SOC_DAPM_SWITCH("AUXR Playback", SND_SOC_NOPM, 0, 0, + &auxr_switch_control), + + /* Analog playback drivers */ + SND_SOC_DAPM_OUT_DRV("HF Left Driver", + TWL6040_REG_HFLCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HF Right Driver", + TWL6040_REG_HFRCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HS Left Driver", + TWL6040_REG_HSLCTL, 2, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("HS Right Driver", + TWL6040_REG_HSRCTL, 2, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV_E("Earphone Driver", + TWL6040_REG_EARCTL, 0, 0, NULL, 0, + twl6040_ep_drv_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUT_DRV("Vibra Left Driver", + TWL6040_REG_VIBCTLL, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Vibra Right Driver", + TWL6040_REG_VIBCTLR, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Vibra Left Control", TWL6040_REG_VIBCTLL, 2, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("Vibra Right Control", TWL6040_REG_VIBCTLR, 2, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("HSDAC Power", 1, SND_SOC_NOPM, 0, 0, + twl6040_hs_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Analog playback PGAs */ + SND_SOC_DAPM_PGA("HF Left PGA", + TWL6040_REG_HFLCTL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("HF Right PGA", + TWL6040_REG_HFRCTL, 1, 0, NULL, 0), + +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* Stream -> DAC mapping */ + {"HSDAC Left", NULL, "Legacy Playback"}, + {"HSDAC Left", NULL, "Headset Playback"}, + {"HSDAC Right", NULL, "Legacy Playback"}, + {"HSDAC Right", NULL, "Headset Playback"}, + + {"HFDAC Left", NULL, "Legacy Playback"}, + {"HFDAC Left", NULL, "Handsfree Playback"}, + {"HFDAC Right", NULL, "Legacy Playback"}, + {"HFDAC Right", NULL, "Handsfree Playback"}, + + {"VIBRA DAC", NULL, "Legacy Playback"}, + {"VIBRA DAC", NULL, "Vibra Playback"}, + + /* ADC -> Stream mapping */ + {"Legacy Capture" , NULL, "ADC Left"}, + {"Capture", NULL, "ADC Left"}, + {"Legacy Capture", NULL, "ADC Right"}, + {"Capture" , NULL, "ADC Right"}, + + /* Capture path */ + {"Analog Left Capture Route", "Headset Mic", "HSMIC"}, + {"Analog Left Capture Route", "Main Mic", "MAINMIC"}, + {"Analog Left Capture Route", "Aux/FM Left", "AFML"}, + + {"Analog Right Capture Route", "Headset Mic", "HSMIC"}, + {"Analog Right Capture Route", "Sub Mic", "SUBMIC"}, + {"Analog Right Capture Route", "Aux/FM Right", "AFMR"}, + + {"MicAmpL", NULL, "Analog Left Capture Route"}, + {"MicAmpR", NULL, "Analog Right Capture Route"}, + + {"ADC Left", NULL, "MicAmpL"}, + {"ADC Right", NULL, "MicAmpR"}, + + /* AFM path */ + {"AFMAmpL", NULL, "AFML"}, + {"AFMAmpR", NULL, "AFMR"}, + + {"HSDAC Left", NULL, "HSDAC Power"}, + {"HSDAC Right", NULL, "HSDAC Power"}, + + {"Headset Left Playback", "HS DAC", "HSDAC Left"}, + {"Headset Left Playback", "Line-In amp", "AFMAmpL"}, + + {"Headset Right Playback", "HS DAC", "HSDAC Right"}, + {"Headset Right Playback", "Line-In amp", "AFMAmpR"}, + + {"HS Left Driver", NULL, "Headset Left Playback"}, + {"HS Right Driver", NULL, "Headset Right Playback"}, + + {"HSOL", NULL, "HS Left Driver"}, + {"HSOR", NULL, "HS Right Driver"}, + + /* Earphone playback path */ + {"Earphone Playback", "Switch", "HSDAC Left"}, + {"Earphone Driver", NULL, "Earphone Playback"}, + {"EP", NULL, "Earphone Driver"}, + + {"Handsfree Left Playback", "HF DAC", "HFDAC Left"}, + {"Handsfree Left Playback", "Line-In amp", "AFMAmpL"}, + + {"Handsfree Right Playback", "HF DAC", "HFDAC Right"}, + {"Handsfree Right Playback", "Line-In amp", "AFMAmpR"}, + + {"HF Left PGA", NULL, "Handsfree Left Playback"}, + {"HF Right PGA", NULL, "Handsfree Right Playback"}, + + {"HF Left Driver", NULL, "HF Left PGA"}, + {"HF Right Driver", NULL, "HF Right PGA"}, + + {"HFL", NULL, "HF Left Driver"}, + {"HFR", NULL, "HF Right Driver"}, + + {"AUXL Playback", "Switch", "HF Left PGA"}, + {"AUXR Playback", "Switch", "HF Right PGA"}, + + {"AUXL", NULL, "AUXL Playback"}, + {"AUXR", NULL, "AUXR Playback"}, + + /* Vibrator paths */ + {"Vibra Left Playback", "Audio PDM", "VIBRA DAC"}, + {"Vibra Right Playback", "Audio PDM", "VIBRA DAC"}, + + {"Vibra Left Driver", NULL, "Vibra Left Playback"}, + {"Vibra Right Driver", NULL, "Vibra Right Playback"}, + {"Vibra Left Driver", NULL, "Vibra Left Control"}, + {"Vibra Right Driver", NULL, "Vibra Right Control"}, + + {"VIBRAL", NULL, "Vibra Left Driver"}, + {"VIBRAR", NULL, "Vibra Right Driver"}, +}; + +static int twl6040_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct twl6040 *twl6040 = to_twl6040(component); + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (priv->codec_powered) { + /* Select low power PLL in standby */ + ret = twl6040_set_pll(twl6040, TWL6040_SYSCLK_SEL_LPPLL, + 32768, 19200000); + break; + } + + ret = twl6040_power(twl6040, 1); + if (ret) + break; + + priv->codec_powered = 1; + + /* Set external boost GPO */ + twl6040_write(component, TWL6040_REG_GPOCTL, 0x02); + break; + case SND_SOC_BIAS_OFF: + if (!priv->codec_powered) + break; + + twl6040_power(twl6040, 0); + priv->codec_powered = 0; + break; + } + + return ret; +} + +static int twl6040_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &sysclk_constraints[priv->pll_power_mode]); + + return 0; +} + +static int twl6040_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + int rate; + + rate = params_rate(params); + switch (rate) { + case 11250: + case 22500: + case 44100: + case 88200: + /* These rates are not supported when HPPLL is in use */ + if (unlikely(priv->pll == TWL6040_SYSCLK_SEL_HPPLL)) { + dev_err(component->dev, "HPPLL does not support rate %d\n", + rate); + return -EINVAL; + } + priv->sysclk = 17640000; + break; + case 8000: + case 16000: + case 32000: + case 48000: + case 96000: + priv->sysclk = 19200000; + break; + default: + dev_err(component->dev, "unsupported rate %d\n", rate); + return -EINVAL; + } + + return 0; +} + +static int twl6040_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct twl6040 *twl6040 = to_twl6040(component); + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + int ret; + + if (!priv->sysclk) { + dev_err(component->dev, + "no mclk configured, call set_sysclk() on init\n"); + return -EINVAL; + } + + ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk); + if (ret) { + dev_err(component->dev, "Can not set PLL (%d)\n", ret); + return -EPERM; + } + + return 0; +} + +static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case TWL6040_SYSCLK_SEL_LPPLL: + case TWL6040_SYSCLK_SEL_HPPLL: + priv->pll = clk_id; + priv->clk_in = freq; + break; + default: + dev_err(component->dev, "unknown clk_id %d\n", clk_id); + return -EINVAL; + } + + return 0; +} + +static void twl6040_mute_path(struct snd_soc_component *component, enum twl6040_dai_id id, + int mute) +{ + struct twl6040 *twl6040 = to_twl6040(component); + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + int hslctl, hsrctl, earctl; + int hflctl, hfrctl; + + switch (id) { + case TWL6040_DAI_DL1: + hslctl = twl6040_read(component, TWL6040_REG_HSLCTL); + hsrctl = twl6040_read(component, TWL6040_REG_HSRCTL); + earctl = twl6040_read(component, TWL6040_REG_EARCTL); + + if (mute) { + /* Power down drivers and DACs */ + earctl &= ~0x01; + hslctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA); + hsrctl &= ~(TWL6040_HSDRVENA | TWL6040_HSDACENA); + + } + + twl6040_reg_write(twl6040, TWL6040_REG_EARCTL, earctl); + twl6040_reg_write(twl6040, TWL6040_REG_HSLCTL, hslctl); + twl6040_reg_write(twl6040, TWL6040_REG_HSRCTL, hsrctl); + priv->dl1_unmuted = !mute; + break; + case TWL6040_DAI_DL2: + hflctl = twl6040_read(component, TWL6040_REG_HFLCTL); + hfrctl = twl6040_read(component, TWL6040_REG_HFRCTL); + + if (mute) { + /* Power down drivers and DACs */ + hflctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA | + TWL6040_HFDRVENA | TWL6040_HFSWENA); + hfrctl &= ~(TWL6040_HFDACENA | TWL6040_HFPGAENA | + TWL6040_HFDRVENA | TWL6040_HFSWENA); + } + + twl6040_reg_write(twl6040, TWL6040_REG_HFLCTL, hflctl); + twl6040_reg_write(twl6040, TWL6040_REG_HFRCTL, hfrctl); + priv->dl2_unmuted = !mute; + break; + default: + break; + } +} + +static int twl6040_mute_stream(struct snd_soc_dai *dai, int mute, int direction) +{ + switch (dai->id) { + case TWL6040_DAI_LEGACY: + twl6040_mute_path(dai->component, TWL6040_DAI_DL1, mute); + twl6040_mute_path(dai->component, TWL6040_DAI_DL2, mute); + break; + case TWL6040_DAI_DL1: + case TWL6040_DAI_DL2: + twl6040_mute_path(dai->component, dai->id, mute); + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops twl6040_dai_ops = { + .startup = twl6040_startup, + .hw_params = twl6040_hw_params, + .prepare = twl6040_prepare, + .set_sysclk = twl6040_set_dai_sysclk, + .mute_stream = twl6040_mute_stream, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver twl6040_dai[] = { +{ + .name = "twl6040-legacy", + .id = TWL6040_DAI_LEGACY, + .playback = { + .stream_name = "Legacy Playback", + .channels_min = 1, + .channels_max = 5, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .capture = { + .stream_name = "Legacy Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-ul", + .id = TWL6040_DAI_UL, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-dl1", + .id = TWL6040_DAI_DL1, + .playback = { + .stream_name = "Headset Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-dl2", + .id = TWL6040_DAI_DL2, + .playback = { + .stream_name = "Handsfree Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TWL6040_RATES, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +{ + .name = "twl6040-vib", + .id = TWL6040_DAI_VIB, + .playback = { + .stream_name = "Vibra Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = TWL6040_FORMATS, + }, + .ops = &twl6040_dai_ops, +}, +}; + +static int twl6040_probe(struct snd_soc_component *component) +{ + struct twl6040_data *priv; + struct platform_device *pdev = to_platform_device(component->dev); + int ret = 0; + + priv = devm_kzalloc(component->dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + snd_soc_component_set_drvdata(component, priv); + + priv->component = component; + + priv->plug_irq = platform_get_irq(pdev, 0); + if (priv->plug_irq < 0) + return priv->plug_irq; + + INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work); + + mutex_init(&priv->mutex); + + ret = request_threaded_irq(priv->plug_irq, NULL, + twl6040_audio_handler, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + "twl6040_irq_plug", component); + if (ret) { + dev_err(component->dev, "PLUG IRQ request failed: %d\n", ret); + return ret; + } + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + twl6040_init_chip(component); + + return 0; +} + +static void twl6040_remove(struct snd_soc_component *component) +{ + struct twl6040_data *priv = snd_soc_component_get_drvdata(component); + + free_irq(priv->plug_irq, component); +} + +static const struct snd_soc_component_driver soc_component_dev_twl6040 = { + .probe = twl6040_probe, + .remove = twl6040_remove, + .read = twl6040_read, + .write = twl6040_write, + .set_bias_level = twl6040_set_bias_level, + .controls = twl6040_snd_controls, + .num_controls = ARRAY_SIZE(twl6040_snd_controls), + .dapm_widgets = twl6040_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .endianness = 1, +}; + +static int twl6040_codec_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_twl6040, + twl6040_dai, ARRAY_SIZE(twl6040_dai)); +} + +static struct platform_driver twl6040_codec_driver = { + .driver = { + .name = "twl6040-codec", + }, + .probe = twl6040_codec_probe, +}; + +module_platform_driver(twl6040_codec_driver); + +MODULE_DESCRIPTION("ASoC TWL6040 codec driver"); +MODULE_AUTHOR("Misael Lopez Cruz"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h new file mode 100644 index 0000000000..f4f4b14cc0 --- /dev/null +++ b/sound/soc/codecs/twl6040.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC TWL6040 codec driver + * + * Author: Misael Lopez Cruz + */ + +#ifndef __TWL6040_H__ +#define __TWL6040_H__ + +enum twl6040_trim { + TWL6040_TRIM_TRIM1 = 0, + TWL6040_TRIM_TRIM2, + TWL6040_TRIM_TRIM3, + TWL6040_TRIM_HSOTRIM, + TWL6040_TRIM_HFOTRIM, + TWL6040_TRIM_INVAL, +}; + +#define TWL6040_HSF_TRIM_LEFT(x) (x & 0x0f) +#define TWL6040_HSF_TRIM_RIGHT(x) ((x >> 4) & 0x0f) + +int twl6040_get_dl1_gain(struct snd_soc_component *component); +void twl6040_hs_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, int report); +int twl6040_get_clk_id(struct snd_soc_component *component); +int twl6040_get_trim_value(struct snd_soc_component *component, enum twl6040_trim trim); +int twl6040_get_hs_step_size(struct snd_soc_component *component); + +#endif /* End of __TWL6040_H__ */ diff --git a/sound/soc/codecs/uda1334.c b/sound/soc/codecs/uda1334.c new file mode 100644 index 0000000000..eace965336 --- /dev/null +++ b/sound/soc/codecs/uda1334.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// uda1334.c -- UDA1334 ALSA SoC Audio driver +// +// Based on WM8523 ALSA SoC Audio driver written by Mark Brown + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UDA1334_NUM_RATES 6 + +/* codec private data */ +struct uda1334_priv { + struct gpio_desc *mute; + struct gpio_desc *deemph; + unsigned int sysclk; + unsigned int rate_constraint_list[UDA1334_NUM_RATES]; + struct snd_pcm_hw_constraint_list rate_constraint; +}; + +static const struct snd_soc_dapm_widget uda1334_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("LINEVOUTL"), +SND_SOC_DAPM_OUTPUT("LINEVOUTR"), +}; + +static const struct snd_soc_dapm_route uda1334_dapm_routes[] = { + { "LINEVOUTL", NULL, "DAC" }, + { "LINEVOUTR", NULL, "DAC" }, +}; + +static int uda1334_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component); + int deemph = ucontrol->value.integer.value[0]; + + if (deemph > 1) + return -EINVAL; + + gpiod_set_value_cansleep(uda1334->deemph, deemph); + + return 0; +}; + +static int uda1334_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component); + int ret; + + ret = gpiod_get_value_cansleep(uda1334->deemph); + if (ret < 0) + return -EINVAL; + + ucontrol->value.integer.value[0] = ret; + + return 0; +}; + +static const struct snd_kcontrol_new uda1334_snd_controls[] = { + SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, + uda1334_get_deemph, uda1334_put_deemph), +}; + +static const struct { + int value; + int ratio; +} lrclk_ratios[UDA1334_NUM_RATES] = { + { 1, 128 }, + { 2, 192 }, + { 3, 256 }, + { 4, 384 }, + { 5, 512 }, + { 6, 768 }, +}; + +static int uda1334_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component); + + /* + * The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + if (!uda1334->sysclk) { + dev_err(component->dev, + "No MCLK configured, call set_sysclk() on init\n"); + return -EINVAL; + } + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &uda1334->rate_constraint); + + gpiod_set_value_cansleep(uda1334->mute, 1); + + return 0; +} + +static void uda1334_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component); + + gpiod_set_value_cansleep(uda1334->mute, 0); +} + +static int uda1334_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component); + unsigned int val; + int i, j = 0; + + uda1334->sysclk = freq; + + uda1334->rate_constraint.count = 0; + for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { + val = freq / lrclk_ratios[i].ratio; + /* + * Check that it's a standard rate since core can't + * cope with others and having the odd rates confuses + * constraint matching. + */ + + switch (val) { + case 8000: + case 32000: + case 44100: + case 48000: + case 64000: + case 88200: + case 96000: + dev_dbg(component->dev, "Supported sample rate: %dHz\n", + val); + uda1334->rate_constraint_list[j++] = val; + uda1334->rate_constraint.count++; + break; + default: + dev_dbg(component->dev, "Skipping sample rate: %dHz\n", + val); + } + } + + /* Need at least one supported rate... */ + if (uda1334->rate_constraint.count == 0) + return -EINVAL; + + return 0; +} + +static int uda1334_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK | + SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK); + + if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBC_CFC)) { + dev_err(codec_dai->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + return 0; +} + +static int uda1334_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(dai->component); + + if (uda1334->mute) + gpiod_set_value_cansleep(uda1334->mute, mute); + + return 0; +} + +#define UDA1334_RATES SNDRV_PCM_RATE_8000_96000 + +#define UDA1334_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops uda1334_dai_ops = { + .startup = uda1334_startup, + .shutdown = uda1334_shutdown, + .set_sysclk = uda1334_set_dai_sysclk, + .set_fmt = uda1334_set_fmt, + .mute_stream = uda1334_mute_stream, +}; + +static struct snd_soc_dai_driver uda1334_dai = { + .name = "uda1334-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = UDA1334_RATES, + .formats = UDA1334_FORMATS, + }, + .ops = &uda1334_dai_ops, +}; + +static int uda1334_probe(struct snd_soc_component *component) +{ + struct uda1334_priv *uda1334 = snd_soc_component_get_drvdata(component); + + uda1334->rate_constraint.list = &uda1334->rate_constraint_list[0]; + uda1334->rate_constraint.count = + ARRAY_SIZE(uda1334->rate_constraint_list); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_uda1334 = { + .probe = uda1334_probe, + .controls = uda1334_snd_controls, + .num_controls = ARRAY_SIZE(uda1334_snd_controls), + .dapm_widgets = uda1334_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(uda1334_dapm_widgets), + .dapm_routes = uda1334_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(uda1334_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id uda1334_of_match[] = { + { .compatible = "nxp,uda1334" }, + { /* sentinel*/ } +}; +MODULE_DEVICE_TABLE(of, uda1334_of_match); + +static int uda1334_codec_probe(struct platform_device *pdev) +{ + struct uda1334_priv *uda1334; + int ret; + + uda1334 = devm_kzalloc(&pdev->dev, sizeof(struct uda1334_priv), + GFP_KERNEL); + if (!uda1334) + return -ENOMEM; + + platform_set_drvdata(pdev, uda1334); + + uda1334->mute = devm_gpiod_get(&pdev->dev, "nxp,mute", GPIOD_OUT_LOW); + if (IS_ERR(uda1334->mute)) { + ret = PTR_ERR(uda1334->mute); + dev_err(&pdev->dev, "Failed to get mute line: %d\n", ret); + return ret; + } + + uda1334->deemph = devm_gpiod_get(&pdev->dev, "nxp,deemph", GPIOD_OUT_LOW); + if (IS_ERR(uda1334->deemph)) { + ret = PTR_ERR(uda1334->deemph); + dev_err(&pdev->dev, "Failed to get deemph line: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_uda1334, + &uda1334_dai, 1); + if (ret < 0) + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + + return ret; +} + +static struct platform_driver uda1334_codec_driver = { + .probe = uda1334_codec_probe, + .driver = { + .name = "uda1334-codec", + .of_match_table = uda1334_of_match, + }, +}; +module_platform_driver(uda1334_codec_driver); + +MODULE_DESCRIPTION("ASoC UDA1334 driver"); +MODULE_AUTHOR("Andra Danciu "); +MODULE_ALIAS("platform:uda1334-codec"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c new file mode 100644 index 0000000000..5c5751dc14 --- /dev/null +++ b/sound/soc/codecs/uda1380.c @@ -0,0 +1,809 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * uda1380.c - Philips UDA1380 ALSA SoC audio driver + * + * Copyright (c) 2007-2009 Philipp Zabel + * + * Modified by Richard Purdie to fit into SoC + * codec model. + * + * Copyright (c) 2005 Giorgio Padrin + * Copyright 2005 Openedhand Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uda1380.h" + +/* codec private data */ +struct uda1380_priv { + struct snd_soc_component *component; + unsigned int dac_clk; + struct work_struct work; + struct i2c_client *i2c; + u16 *reg_cache; +}; + +/* + * uda1380 register cache + */ +static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = { + 0x0502, 0x0000, 0x0000, 0x3f3f, + 0x0202, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0xff00, 0x0000, 0x4800, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x8000, 0x0002, 0x0000, +}; + +static unsigned long uda1380_cache_dirty; + +/* + * read uda1380 register cache + */ +static inline unsigned int uda1380_read_reg_cache(struct snd_soc_component *component, + unsigned int reg) +{ + struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); + u16 *cache = uda1380->reg_cache; + + if (reg == UDA1380_RESET) + return 0; + if (reg >= UDA1380_CACHEREGNUM) + return -1; + return cache[reg]; +} + +/* + * write uda1380 register cache + */ +static inline void uda1380_write_reg_cache(struct snd_soc_component *component, + u16 reg, unsigned int value) +{ + struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); + u16 *cache = uda1380->reg_cache; + + if (reg >= UDA1380_CACHEREGNUM) + return; + if ((reg >= 0x10) && (cache[reg] != value)) + set_bit(reg - 0x10, &uda1380_cache_dirty); + cache[reg] = value; +} + +/* + * write to the UDA1380 register space + */ +static int uda1380_write(struct snd_soc_component *component, unsigned int reg, + unsigned int value) +{ + struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); + u8 data[3]; + + /* data is + * data[0] is register offset + * data[1] is MS byte + * data[2] is LS byte + */ + data[0] = reg; + data[1] = (value & 0xff00) >> 8; + data[2] = value & 0x00ff; + + uda1380_write_reg_cache(component, reg, value); + + /* the interpolator & decimator regs must only be written when the + * codec DAI is active. + */ + if (!snd_soc_component_active(component) && (reg >= UDA1380_MVOL)) + return 0; + pr_debug("uda1380: hw write %x val %x\n", reg, value); + if (i2c_master_send(uda1380->i2c, data, 3) == 3) { + unsigned int val; + i2c_master_send(uda1380->i2c, data, 1); + i2c_master_recv(uda1380->i2c, data, 2); + val = (data[0]<<8) | data[1]; + if (val != value) { + pr_debug("uda1380: READ BACK VAL %x\n", + (data[0]<<8) | data[1]); + return -EIO; + } + if (reg >= 0x10) + clear_bit(reg - 0x10, &uda1380_cache_dirty); + return 0; + } else + return -EIO; +} + +static void uda1380_sync_cache(struct snd_soc_component *component) +{ + struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); + int reg; + u8 data[3]; + u16 *cache = uda1380->reg_cache; + + /* Sync reg_cache with the hardware */ + for (reg = 0; reg < UDA1380_MVOL; reg++) { + data[0] = reg; + data[1] = (cache[reg] & 0xff00) >> 8; + data[2] = cache[reg] & 0x00ff; + if (i2c_master_send(uda1380->i2c, data, 3) != 3) + dev_err(component->dev, "%s: write to reg 0x%x failed\n", + __func__, reg); + } +} + +static int uda1380_reset(struct snd_soc_component *component) +{ + struct uda1380_platform_data *pdata = component->dev->platform_data; + struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); + + if (gpio_is_valid(pdata->gpio_reset)) { + gpio_set_value(pdata->gpio_reset, 1); + mdelay(1); + gpio_set_value(pdata->gpio_reset, 0); + } else { + u8 data[3]; + + data[0] = UDA1380_RESET; + data[1] = 0; + data[2] = 0; + + if (i2c_master_send(uda1380->i2c, data, 3) != 3) { + dev_err(component->dev, "%s: failed\n", __func__); + return -EIO; + } + } + + return 0; +} + +static void uda1380_flush_work(struct work_struct *work) +{ + struct uda1380_priv *uda1380 = container_of(work, struct uda1380_priv, work); + struct snd_soc_component *uda1380_component = uda1380->component; + int bit, reg; + + for_each_set_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) { + reg = 0x10 + bit; + pr_debug("uda1380: flush reg %x val %x:\n", reg, + uda1380_read_reg_cache(uda1380_component, reg)); + uda1380_write(uda1380_component, reg, + uda1380_read_reg_cache(uda1380_component, reg)); + clear_bit(bit, &uda1380_cache_dirty); + } + +} + +/* declarations of ALSA reg_elem_REAL controls */ +static const char *uda1380_deemp[] = { + "None", + "32kHz", + "44.1kHz", + "48kHz", + "96kHz", +}; +static const char *uda1380_input_sel[] = { + "Line", + "Mic + Line R", + "Line L", + "Mic", +}; +static const char *uda1380_output_sel[] = { + "DAC", + "Analog Mixer", +}; +static const char *uda1380_spf_mode[] = { + "Flat", + "Minimum1", + "Minimum2", + "Maximum" +}; +static const char *uda1380_capture_sel[] = { + "ADC", + "Digital Mixer" +}; +static const char *uda1380_sel_ns[] = { + "3rd-order", + "5th-order" +}; +static const char *uda1380_mix_control[] = { + "off", + "PCM only", + "before sound processing", + "after sound processing" +}; +static const char *uda1380_sdet_setting[] = { + "3200", + "4800", + "9600", + "19200" +}; +static const char *uda1380_os_setting[] = { + "single-speed", + "double-speed (no mixing)", + "quad-speed (no mixing)" +}; + +static const struct soc_enum uda1380_deemp_enum[] = { + SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, ARRAY_SIZE(uda1380_deemp), + uda1380_deemp), + SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, ARRAY_SIZE(uda1380_deemp), + uda1380_deemp), +}; +static SOC_ENUM_SINGLE_DECL(uda1380_input_sel_enum, + UDA1380_ADC, 2, uda1380_input_sel); /* SEL_MIC, SEL_LNA */ +static SOC_ENUM_SINGLE_DECL(uda1380_output_sel_enum, + UDA1380_PM, 7, uda1380_output_sel); /* R02_EN_AVC */ +static SOC_ENUM_SINGLE_DECL(uda1380_spf_enum, + UDA1380_MODE, 14, uda1380_spf_mode); /* M */ +static SOC_ENUM_SINGLE_DECL(uda1380_capture_sel_enum, + UDA1380_IFACE, 6, uda1380_capture_sel); /* SEL_SOURCE */ +static SOC_ENUM_SINGLE_DECL(uda1380_sel_ns_enum, + UDA1380_MIXER, 14, uda1380_sel_ns); /* SEL_NS */ +static SOC_ENUM_SINGLE_DECL(uda1380_mix_enum, + UDA1380_MIXER, 12, uda1380_mix_control); /* MIX, MIX_POS */ +static SOC_ENUM_SINGLE_DECL(uda1380_sdet_enum, + UDA1380_MIXER, 4, uda1380_sdet_setting); /* SD_VALUE */ +static SOC_ENUM_SINGLE_DECL(uda1380_os_enum, + UDA1380_MIXER, 0, uda1380_os_setting); /* OS */ + +/* + * from -48 dB in 1.5 dB steps (mute instead of -49.5 dB) + */ +static DECLARE_TLV_DB_SCALE(amix_tlv, -4950, 150, 1); + +/* + * from -78 dB in 1 dB steps (3 dB steps, really. LSB are ignored), + * from -66 dB in 0.5 dB steps (2 dB steps, really) and + * from -52 dB in 0.25 dB steps + */ +static const DECLARE_TLV_DB_RANGE(mvol_tlv, + 0, 15, TLV_DB_SCALE_ITEM(-8200, 100, 1), + 16, 43, TLV_DB_SCALE_ITEM(-6600, 50, 0), + 44, 252, TLV_DB_SCALE_ITEM(-5200, 25, 0) +); + +/* + * from -72 dB in 1.5 dB steps (6 dB steps really), + * from -66 dB in 0.75 dB steps (3 dB steps really), + * from -60 dB in 0.5 dB steps (2 dB steps really) and + * from -46 dB in 0.25 dB steps + */ +static const DECLARE_TLV_DB_RANGE(vc_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-7800, 150, 1), + 8, 15, TLV_DB_SCALE_ITEM(-6600, 75, 0), + 16, 43, TLV_DB_SCALE_ITEM(-6000, 50, 0), + 44, 228, TLV_DB_SCALE_ITEM(-4600, 25, 0) +); + +/* from 0 to 6 dB in 2 dB steps if SPF mode != flat */ +static DECLARE_TLV_DB_SCALE(tr_tlv, 0, 200, 0); + +/* from 0 to 24 dB in 2 dB steps, if SPF mode == maximum, otherwise cuts + * off at 18 dB max) */ +static DECLARE_TLV_DB_SCALE(bb_tlv, 0, 200, 0); + +/* from -63 to 24 dB in 0.5 dB steps (-128...48) */ +static DECLARE_TLV_DB_SCALE(dec_tlv, -6400, 50, 1); + +/* from 0 to 24 dB in 3 dB steps */ +static DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0); + +/* from 0 to 30 dB in 2 dB steps */ +static DECLARE_TLV_DB_SCALE(vga_tlv, 0, 200, 0); + +static const struct snd_kcontrol_new uda1380_snd_controls[] = { + SOC_DOUBLE_TLV("Analog Mixer Volume", UDA1380_AMIX, 0, 8, 44, 1, amix_tlv), /* AVCR, AVCL */ + SOC_DOUBLE_TLV("Master Playback Volume", UDA1380_MVOL, 0, 8, 252, 1, mvol_tlv), /* MVCL, MVCR */ + SOC_SINGLE_TLV("ADC Playback Volume", UDA1380_MIXVOL, 8, 228, 1, vc_tlv), /* VC2 */ + SOC_SINGLE_TLV("PCM Playback Volume", UDA1380_MIXVOL, 0, 228, 1, vc_tlv), /* VC1 */ + SOC_ENUM("Sound Processing Filter", uda1380_spf_enum), /* M */ + SOC_DOUBLE_TLV("Tone Control - Treble", UDA1380_MODE, 4, 12, 3, 0, tr_tlv), /* TRL, TRR */ + SOC_DOUBLE_TLV("Tone Control - Bass", UDA1380_MODE, 0, 8, 15, 0, bb_tlv), /* BBL, BBR */ +/**/ SOC_SINGLE("Master Playback Switch", UDA1380_DEEMP, 14, 1, 1), /* MTM */ + SOC_SINGLE("ADC Playback Switch", UDA1380_DEEMP, 11, 1, 1), /* MT2 from decimation filter */ + SOC_ENUM("ADC Playback De-emphasis", uda1380_deemp_enum[0]), /* DE2 */ + SOC_SINGLE("PCM Playback Switch", UDA1380_DEEMP, 3, 1, 1), /* MT1, from digital data input */ + SOC_ENUM("PCM Playback De-emphasis", uda1380_deemp_enum[1]), /* DE1 */ + SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0), /* DA_POL_INV */ + SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum), /* SEL_NS */ + SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum), /* MIX_POS, MIX */ + SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0), /* SDET_ON */ + SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum), /* SD_VALUE */ + SOC_ENUM("Oversampling Input", uda1380_os_enum), /* OS */ + SOC_DOUBLE_S8_TLV("ADC Capture Volume", UDA1380_DEC, -128, 48, dec_tlv), /* ML_DEC, MR_DEC */ +/**/ SOC_SINGLE("ADC Capture Switch", UDA1380_PGA, 15, 1, 1), /* MT_ADC */ + SOC_DOUBLE_TLV("Line Capture Volume", UDA1380_PGA, 0, 8, 8, 0, pga_tlv), /* PGA_GAINCTRLL, PGA_GAINCTRLR */ + SOC_SINGLE("ADC Polarity inverting Switch", UDA1380_ADC, 12, 1, 0), /* ADCPOL_INV */ + SOC_SINGLE_TLV("Mic Capture Volume", UDA1380_ADC, 8, 15, 0, vga_tlv), /* VGA_CTRL */ + SOC_SINGLE("DC Filter Bypass Switch", UDA1380_ADC, 1, 1, 0), /* SKIP_DCFIL (before decimator) */ + SOC_SINGLE("DC Filter Enable Switch", UDA1380_ADC, 0, 1, 0), /* EN_DCFIL (at output of decimator) */ + SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0), /* TODO: enum, see table 62 */ + SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1), /* AGC_LEVEL */ + /* -5.5, -8, -11.5, -14 dBFS */ + SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0), +}; + +/* Input mux */ +static const struct snd_kcontrol_new uda1380_input_mux_control = + SOC_DAPM_ENUM("Route", uda1380_input_sel_enum); + +/* Output mux */ +static const struct snd_kcontrol_new uda1380_output_mux_control = + SOC_DAPM_ENUM("Route", uda1380_output_sel_enum); + +/* Capture mux */ +static const struct snd_kcontrol_new uda1380_capture_mux_control = + SOC_DAPM_ENUM("Route", uda1380_capture_sel_enum); + + +static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = { + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, + &uda1380_input_mux_control), + SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0, + &uda1380_output_mux_control), + SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, + &uda1380_capture_mux_control), + SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", UDA1380_PM, 2, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", UDA1380_PM, 0, 0), + SND_SOC_DAPM_INPUT("VINM"), + SND_SOC_DAPM_INPUT("VINL"), + SND_SOC_DAPM_INPUT("VINR"), + SND_SOC_DAPM_MIXER("Analog Mixer", UDA1380_PM, 6, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("VOUTLHP"), + SND_SOC_DAPM_OUTPUT("VOUTRHP"), + SND_SOC_DAPM_OUTPUT("VOUTL"), + SND_SOC_DAPM_OUTPUT("VOUTR"), + SND_SOC_DAPM_DAC("DAC", "Playback", UDA1380_PM, 10, 0), + SND_SOC_DAPM_PGA("HeadPhone Driver", UDA1380_PM, 13, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route uda1380_dapm_routes[] = { + + /* output mux */ + {"HeadPhone Driver", NULL, "Output Mux"}, + {"VOUTR", NULL, "Output Mux"}, + {"VOUTL", NULL, "Output Mux"}, + + {"Analog Mixer", NULL, "VINR"}, + {"Analog Mixer", NULL, "VINL"}, + {"Analog Mixer", NULL, "DAC"}, + + {"Output Mux", "DAC", "DAC"}, + {"Output Mux", "Analog Mixer", "Analog Mixer"}, + + /* {"DAC", "Digital Mixer", "I2S" } */ + + /* headphone driver */ + {"VOUTLHP", NULL, "HeadPhone Driver"}, + {"VOUTRHP", NULL, "HeadPhone Driver"}, + + /* input mux */ + {"Left ADC", NULL, "Input Mux"}, + {"Input Mux", "Mic", "Mic LNA"}, + {"Input Mux", "Mic + Line R", "Mic LNA"}, + {"Input Mux", "Line L", "Left PGA"}, + {"Input Mux", "Line", "Left PGA"}, + + /* right input */ + {"Right ADC", "Mic + Line R", "Right PGA"}, + {"Right ADC", "Line", "Right PGA"}, + + /* inputs */ + {"Mic LNA", NULL, "VINM"}, + {"Left PGA", NULL, "VINL"}, + {"Right PGA", NULL, "VINR"}, +}; + +static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + int iface; + + /* set up DAI based upon fmt */ + iface = uda1380_read_reg_cache(component, UDA1380_IFACE); + iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= R01_SFORI_I2S | R01_SFORO_I2S; + break; + case SND_SOC_DAIFMT_LSB: + iface |= R01_SFORI_LSB16 | R01_SFORO_LSB16; + break; + case SND_SOC_DAIFMT_MSB: + iface |= R01_SFORI_MSB | R01_SFORO_MSB; + } + + /* DATAI is consumer only */ + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) + return -EINVAL; + + uda1380_write_reg_cache(component, UDA1380_IFACE, iface); + + return 0; +} + +static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + int iface; + + /* set up DAI based upon fmt */ + iface = uda1380_read_reg_cache(component, UDA1380_IFACE); + iface &= ~R01_SFORI_MASK; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= R01_SFORI_I2S; + break; + case SND_SOC_DAIFMT_LSB: + iface |= R01_SFORI_LSB16; + break; + case SND_SOC_DAIFMT_MSB: + iface |= R01_SFORI_MSB; + } + + /* DATAI is consumer only */ + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) + return -EINVAL; + + uda1380_write(component, UDA1380_IFACE, iface); + + return 0; +} + +static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + int iface; + + /* set up DAI based upon fmt */ + iface = uda1380_read_reg_cache(component, UDA1380_IFACE); + iface &= ~(R01_SIM | R01_SFORO_MASK); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= R01_SFORO_I2S; + break; + case SND_SOC_DAIFMT_LSB: + iface |= R01_SFORO_LSB16; + break; + case SND_SOC_DAIFMT_MSB: + iface |= R01_SFORO_MSB; + } + + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == SND_SOC_DAIFMT_CBP_CFP) + iface |= R01_SIM; + + uda1380_write(component, UDA1380_IFACE, iface); + + return 0; +} + +static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); + int mixer = uda1380_read_reg_cache(component, UDA1380_MIXER); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + uda1380_write_reg_cache(component, UDA1380_MIXER, + mixer & ~R14_SILENCE); + schedule_work(&uda1380->work); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + uda1380_write_reg_cache(component, UDA1380_MIXER, + mixer | R14_SILENCE); + schedule_work(&uda1380->work); + break; + } + return 0; +} + +static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 clk = uda1380_read_reg_cache(component, UDA1380_CLK); + + /* set WSPLL power and divider if running from this clock */ + if (clk & R00_DAC_CLK) { + int rate = params_rate(params); + u16 pm = uda1380_read_reg_cache(component, UDA1380_PM); + clk &= ~0x3; /* clear SEL_LOOP_DIV */ + switch (rate) { + case 6250 ... 12500: + clk |= 0x0; + break; + case 12501 ... 25000: + clk |= 0x1; + break; + case 25001 ... 50000: + clk |= 0x2; + break; + case 50001 ... 100000: + clk |= 0x3; + break; + } + uda1380_write(component, UDA1380_PM, R02_PON_PLL | pm); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + clk |= R00_EN_DAC | R00_EN_INT; + else + clk |= R00_EN_ADC | R00_EN_DEC; + + uda1380_write(component, UDA1380_CLK, clk); + return 0; +} + +static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 clk = uda1380_read_reg_cache(component, UDA1380_CLK); + + /* shut down WSPLL power if running from this clock */ + if (clk & R00_DAC_CLK) { + u16 pm = uda1380_read_reg_cache(component, UDA1380_PM); + uda1380_write(component, UDA1380_PM, ~R02_PON_PLL & pm); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + clk &= ~(R00_EN_DAC | R00_EN_INT); + else + clk &= ~(R00_EN_ADC | R00_EN_DEC); + + uda1380_write(component, UDA1380_CLK, clk); +} + +static int uda1380_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int pm = uda1380_read_reg_cache(component, UDA1380_PM); + int reg; + struct uda1380_platform_data *pdata = component->dev->platform_data; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + /* ADC, DAC on */ + uda1380_write(component, UDA1380_PM, R02_PON_BIAS | pm); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + if (gpio_is_valid(pdata->gpio_power)) { + gpio_set_value(pdata->gpio_power, 1); + mdelay(1); + uda1380_reset(component); + } + + uda1380_sync_cache(component); + } + uda1380_write(component, UDA1380_PM, 0x0); + break; + case SND_SOC_BIAS_OFF: + if (!gpio_is_valid(pdata->gpio_power)) + break; + + gpio_set_value(pdata->gpio_power, 0); + + /* Mark mixer regs cache dirty to sync them with + * codec regs on power on. + */ + for (reg = UDA1380_MVOL; reg < UDA1380_CACHEREGNUM; reg++) + set_bit(reg - 0x10, &uda1380_cache_dirty); + } + return 0; +} + +#define UDA1380_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +static const struct snd_soc_dai_ops uda1380_dai_ops = { + .hw_params = uda1380_pcm_hw_params, + .shutdown = uda1380_pcm_shutdown, + .trigger = uda1380_trigger, + .set_fmt = uda1380_set_dai_fmt_both, +}; + +static const struct snd_soc_dai_ops uda1380_dai_ops_playback = { + .hw_params = uda1380_pcm_hw_params, + .shutdown = uda1380_pcm_shutdown, + .trigger = uda1380_trigger, + .set_fmt = uda1380_set_dai_fmt_playback, +}; + +static const struct snd_soc_dai_ops uda1380_dai_ops_capture = { + .hw_params = uda1380_pcm_hw_params, + .shutdown = uda1380_pcm_shutdown, + .trigger = uda1380_trigger, + .set_fmt = uda1380_set_dai_fmt_capture, +}; + +static struct snd_soc_dai_driver uda1380_dai[] = { +{ + .name = "uda1380-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = UDA1380_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = UDA1380_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = &uda1380_dai_ops, +}, +{ /* playback only - dual interface */ + .name = "uda1380-hifi-playback", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = UDA1380_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &uda1380_dai_ops_playback, +}, +{ /* capture only - dual interface*/ + .name = "uda1380-hifi-capture", + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = UDA1380_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &uda1380_dai_ops_capture, +}, +}; + +static int uda1380_probe(struct snd_soc_component *component) +{ + struct uda1380_platform_data *pdata =component->dev->platform_data; + struct uda1380_priv *uda1380 = snd_soc_component_get_drvdata(component); + int ret; + + uda1380->component = component; + + if (!gpio_is_valid(pdata->gpio_power)) { + ret = uda1380_reset(component); + if (ret) + return ret; + } + + INIT_WORK(&uda1380->work, uda1380_flush_work); + + /* set clock input */ + switch (pdata->dac_clk) { + case UDA1380_DAC_CLK_SYSCLK: + uda1380_write_reg_cache(component, UDA1380_CLK, 0); + break; + case UDA1380_DAC_CLK_WSPLL: + uda1380_write_reg_cache(component, UDA1380_CLK, + R00_DAC_CLK); + break; + } + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_uda1380 = { + .probe = uda1380_probe, + .read = uda1380_read_reg_cache, + .write = uda1380_write, + .set_bias_level = uda1380_set_bias_level, + .controls = uda1380_snd_controls, + .num_controls = ARRAY_SIZE(uda1380_snd_controls), + .dapm_widgets = uda1380_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets), + .dapm_routes = uda1380_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int uda1380_i2c_probe(struct i2c_client *i2c) +{ + struct uda1380_platform_data *pdata = i2c->dev.platform_data; + struct uda1380_priv *uda1380; + int ret; + + if (!pdata) + return -EINVAL; + + uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv), + GFP_KERNEL); + if (uda1380 == NULL) + return -ENOMEM; + + if (gpio_is_valid(pdata->gpio_reset)) { + ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_reset, + GPIOF_OUT_INIT_LOW, "uda1380 reset"); + if (ret) + return ret; + } + + if (gpio_is_valid(pdata->gpio_power)) { + ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_power, + GPIOF_OUT_INIT_LOW, "uda1380 power"); + if (ret) + return ret; + } + + uda1380->reg_cache = devm_kmemdup(&i2c->dev, + uda1380_reg, + ARRAY_SIZE(uda1380_reg) * sizeof(u16), + GFP_KERNEL); + if (!uda1380->reg_cache) + return -ENOMEM; + + i2c_set_clientdata(i2c, uda1380); + uda1380->i2c = i2c; + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_uda1380, uda1380_dai, ARRAY_SIZE(uda1380_dai)); + return ret; +} + +static const struct i2c_device_id uda1380_i2c_id[] = { + { "uda1380", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id); + +static const struct of_device_id uda1380_of_match[] = { + { .compatible = "nxp,uda1380", }, + { } +}; +MODULE_DEVICE_TABLE(of, uda1380_of_match); + +static struct i2c_driver uda1380_i2c_driver = { + .driver = { + .name = "uda1380-codec", + .of_match_table = uda1380_of_match, + }, + .probe = uda1380_i2c_probe, + .id_table = uda1380_i2c_id, +}; + +module_i2c_driver(uda1380_i2c_driver); + +MODULE_AUTHOR("Giorgio Padrin"); +MODULE_DESCRIPTION("Audio support for codec Philips UDA1380"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h new file mode 100644 index 0000000000..0222f2ab81 --- /dev/null +++ b/sound/soc/codecs/uda1380.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Audio support for Philips UDA1380 + * + * Copyright (c) 2005 Giorgio Padrin + */ + +#ifndef _UDA1380_H +#define _UDA1380_H + +#define UDA1380_CLK 0x00 +#define UDA1380_IFACE 0x01 +#define UDA1380_PM 0x02 +#define UDA1380_AMIX 0x03 +#define UDA1380_HP 0x04 +#define UDA1380_MVOL 0x10 +#define UDA1380_MIXVOL 0x11 +#define UDA1380_MODE 0x12 +#define UDA1380_DEEMP 0x13 +#define UDA1380_MIXER 0x14 +#define UDA1380_INTSTAT 0x18 +#define UDA1380_DEC 0x20 +#define UDA1380_PGA 0x21 +#define UDA1380_ADC 0x22 +#define UDA1380_AGC 0x23 +#define UDA1380_DECSTAT 0x28 +#define UDA1380_RESET 0x7f + +#define UDA1380_CACHEREGNUM 0x24 + +/* Register flags */ +#define R00_EN_ADC 0x0800 +#define R00_EN_DEC 0x0400 +#define R00_EN_DAC 0x0200 +#define R00_EN_INT 0x0100 +#define R00_DAC_CLK 0x0010 +#define R01_SFORI_I2S 0x0000 +#define R01_SFORI_LSB16 0x0100 +#define R01_SFORI_LSB18 0x0200 +#define R01_SFORI_LSB20 0x0300 +#define R01_SFORI_MSB 0x0500 +#define R01_SFORI_MASK 0x0700 +#define R01_SFORO_I2S 0x0000 +#define R01_SFORO_LSB16 0x0001 +#define R01_SFORO_LSB18 0x0002 +#define R01_SFORO_LSB20 0x0003 +#define R01_SFORO_LSB24 0x0004 +#define R01_SFORO_MSB 0x0005 +#define R01_SFORO_MASK 0x0007 +#define R01_SEL_SOURCE 0x0040 +#define R01_SIM 0x0010 +#define R02_PON_PLL 0x8000 +#define R02_PON_HP 0x2000 +#define R02_PON_DAC 0x0400 +#define R02_PON_BIAS 0x0100 +#define R02_EN_AVC 0x0080 +#define R02_PON_AVC 0x0040 +#define R02_PON_LNA 0x0010 +#define R02_PON_PGAL 0x0008 +#define R02_PON_ADCL 0x0004 +#define R02_PON_PGAR 0x0002 +#define R02_PON_ADCR 0x0001 +#define R13_MTM 0x4000 +#define R14_SILENCE 0x0080 +#define R14_SDET_ON 0x0040 +#define R21_MT_ADC 0x8000 +#define R22_SEL_LNA 0x0008 +#define R22_SEL_MIC 0x0004 +#define R22_SKIP_DCFIL 0x0002 +#define R23_AGC_EN 0x0001 + +#endif /* _UDA1380_H */ diff --git a/sound/soc/codecs/wcd-clsh-v2.c b/sound/soc/codecs/wcd-clsh-v2.c new file mode 100644 index 0000000000..d96e23ec43 --- /dev/null +++ b/sound/soc/codecs/wcd-clsh-v2.c @@ -0,0 +1,905 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +// Copyright (c) 2017-2018, Linaro Limited + +#include +#include +#include +#include +#include "wcd9335.h" +#include "wcd-clsh-v2.h" + +struct wcd_clsh_ctrl { + int state; + int mode; + int flyback_users; + int buck_users; + int clsh_users; + int codec_version; + struct snd_soc_component *comp; +}; + +/* Class-H registers for codecs from and above WCD9335 */ +#define WCD9XXX_A_CDC_RX0_RX_PATH_CFG0 WCD9335_REG(0xB, 0x42) +#define WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK BIT(6) +#define WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE BIT(6) +#define WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE 0 +#define WCD9XXX_A_CDC_RX1_RX_PATH_CFG0 WCD9335_REG(0xB, 0x56) +#define WCD9XXX_A_CDC_RX2_RX_PATH_CFG0 WCD9335_REG(0xB, 0x6A) +#define WCD9XXX_A_CDC_CLSH_K1_MSB WCD9335_REG(0xC, 0x08) +#define WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK GENMASK(3, 0) +#define WCD9XXX_A_CDC_CLSH_K1_LSB WCD9335_REG(0xC, 0x09) +#define WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK GENMASK(7, 0) +#define WCD9XXX_A_ANA_RX_SUPPLIES WCD9335_REG(0x6, 0x08) +#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK BIT(1) +#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H 0 +#define WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB BIT(1) +#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK BIT(2) +#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA BIT(2) +#define WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT 0 +#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK BIT(3) +#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA BIT(3) +#define WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT 0 +#define WCD9XXX_A_ANA_RX_VNEG_EN_MASK BIT(6) +#define WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT 6 +#define WCD9XXX_A_ANA_RX_VNEG_ENABLE BIT(6) +#define WCD9XXX_A_ANA_RX_VNEG_DISABLE 0 +#define WCD9XXX_A_ANA_RX_VPOS_EN_MASK BIT(7) +#define WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT 7 +#define WCD9XXX_A_ANA_RX_VPOS_ENABLE BIT(7) +#define WCD9XXX_A_ANA_RX_VPOS_DISABLE 0 +#define WCD9XXX_A_ANA_HPH WCD9335_REG(0x6, 0x09) +#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK GENMASK(3, 2) +#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA 0x08 +#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP 0x04 +#define WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL 0x0 +#define WCD9XXX_A_CDC_CLSH_CRC WCD9335_REG(0xC, 0x01) +#define WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK BIT(0) +#define WCD9XXX_A_CDC_CLSH_CRC_CLK_ENABLE BIT(0) +#define WCD9XXX_A_CDC_CLSH_CRC_CLK_DISABLE 0 +#define WCD9XXX_FLYBACK_EN WCD9335_REG(0x6, 0xA4) +#define WCD9XXX_FLYBACK_EN_DELAY_SEL_MASK GENMASK(6, 5) +#define WCD9XXX_FLYBACK_EN_DELAY_26P25_US 0x40 +#define WCD9XXX_FLYBACK_EN_RESET_BY_EXT_MASK BIT(4) +#define WCD9XXX_FLYBACK_EN_PWDN_WITHOUT_DELAY BIT(4) +#define WCD9XXX_FLYBACK_EN_PWDN_WITH_DELAY 0 +#define WCD9XXX_RX_BIAS_FLYB_BUFF WCD9335_REG(0x6, 0xC7) +#define WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK GENMASK(7, 4) +#define WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK GENMASK(3, 0) +#define WCD9XXX_HPH_L_EN WCD9335_REG(0x6, 0xD3) +#define WCD9XXX_HPH_CONST_SEL_L_MASK GENMASK(7, 3) +#define WCD9XXX_HPH_CONST_SEL_BYPASS 0 +#define WCD9XXX_HPH_CONST_SEL_LP_PATH 0x40 +#define WCD9XXX_HPH_CONST_SEL_HQ_PATH 0x80 +#define WCD9XXX_HPH_R_EN WCD9335_REG(0x6, 0xD6) +#define WCD9XXX_HPH_REFBUFF_UHQA_CTL WCD9335_REG(0x6, 0xDD) +#define WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK GENMASK(2, 0) +#define WCD9XXX_CLASSH_CTRL_VCL_2 WCD9335_REG(0x6, 0x9B) +#define WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK GENMASK(5, 4) +#define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM 0x20 +#define WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM 0x0 +#define WCD9XXX_CDC_RX1_RX_PATH_CTL WCD9335_REG(0xB, 0x55) +#define WCD9XXX_CDC_RX2_RX_PATH_CTL WCD9335_REG(0xB, 0x69) +#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL WCD9335_REG(0xD, 0x41) +#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_EN_MASK BIT(0) +#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_11P3_EN_MASK BIT(1) +#define WCD9XXX_CLASSH_CTRL_CCL_1 WCD9335_REG(0x6, 0x9C) +#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK GENMASK(7, 4) +#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA 0x50 +#define WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA 0x30 + +#define WCD9XXX_BASE_ADDRESS 0x3000 +#define WCD9XXX_ANA_RX_SUPPLIES (WCD9XXX_BASE_ADDRESS+0x008) +#define WCD9XXX_ANA_HPH (WCD9XXX_BASE_ADDRESS+0x009) +#define WCD9XXX_CLASSH_MODE_2 (WCD9XXX_BASE_ADDRESS+0x098) +#define WCD9XXX_CLASSH_MODE_3 (WCD9XXX_BASE_ADDRESS+0x099) +#define WCD9XXX_FLYBACK_VNEG_CTRL_1 (WCD9XXX_BASE_ADDRESS+0x0A5) +#define WCD9XXX_FLYBACK_VNEG_CTRL_4 (WCD9XXX_BASE_ADDRESS+0x0A8) +#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2 (WCD9XXX_BASE_ADDRESS+0x0AF) +#define WCD9XXX_RX_BIAS_HPH_LOWPOWER (WCD9XXX_BASE_ADDRESS+0x0BF) +#define WCD9XXX_V3_RX_BIAS_FLYB_BUFF (WCD9XXX_BASE_ADDRESS+0x0C7) +#define WCD9XXX_HPH_PA_CTL1 (WCD9XXX_BASE_ADDRESS+0x0D1) +#define WCD9XXX_HPH_NEW_INT_PA_MISC2 (WCD9XXX_BASE_ADDRESS+0x138) + +#define CLSH_REQ_ENABLE true +#define CLSH_REQ_DISABLE false +#define WCD_USLEEP_RANGE 50 + +enum { + DAC_GAIN_0DB = 0, + DAC_GAIN_0P2DB, + DAC_GAIN_0P4DB, + DAC_GAIN_0P6DB, + DAC_GAIN_0P8DB, + DAC_GAIN_M0P2DB, + DAC_GAIN_M0P4DB, + DAC_GAIN_M0P6DB, +}; + +static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl, + bool enable) +{ + struct snd_soc_component *comp = ctrl->comp; + + if ((enable && ++ctrl->clsh_users == 1) || + (!enable && --ctrl->clsh_users == 0)) + snd_soc_component_update_bits(comp, WCD9XXX_A_CDC_CLSH_CRC, + WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK, + enable); + if (ctrl->clsh_users < 0) + ctrl->clsh_users = 0; +} + +static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp, + int mode) +{ + /* set to HIFI */ + if (mode == CLS_H_HIFI) + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK, + WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_UHQA); + else + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_MASK, + WCD9XXX_A_ANA_RX_VPOS_PWR_LVL_DEFAULT); +} + +static void wcd_clsh_v3_set_buck_mode(struct snd_soc_component *component, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB_LOHIFI) + snd_soc_component_update_bits(component, + WCD9XXX_ANA_RX_SUPPLIES, + 0x08, 0x08); /* set to HIFI */ + else + snd_soc_component_update_bits(component, + WCD9XXX_ANA_RX_SUPPLIES, + 0x08, 0x00); /* set to default */ +} + +static inline void wcd_clsh_set_flyback_mode(struct snd_soc_component *comp, + int mode) +{ + /* set to HIFI */ + if (mode == CLS_H_HIFI) + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK, + WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_UHQA); + else + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_MASK, + WCD9XXX_A_ANA_RX_VNEG_PWR_LVL_DEFAULT); +} + +static void wcd_clsh_buck_ctrl(struct wcd_clsh_ctrl *ctrl, + int mode, + bool enable) +{ + struct snd_soc_component *comp = ctrl->comp; + + /* enable/disable buck */ + if ((enable && (++ctrl->buck_users == 1)) || + (!enable && (--ctrl->buck_users == 0))) + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VPOS_EN_MASK, + enable << WCD9XXX_A_ANA_RX_VPOS_EN_SHIFT); + /* + * 500us sleep is required after buck enable/disable + * as per HW requirement + */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); +} + +static void wcd_clsh_v3_buck_ctrl(struct snd_soc_component *component, + struct wcd_clsh_ctrl *ctrl, + int mode, + bool enable) +{ + /* enable/disable buck */ + if ((enable && (++ctrl->buck_users == 1)) || + (!enable && (--ctrl->buck_users == 0))) { + snd_soc_component_update_bits(component, + WCD9XXX_ANA_RX_SUPPLIES, + (1 << 7), (enable << 7)); + /* + * 500us sleep is required after buck enable/disable + * as per HW requirement + */ + usleep_range(500, 510); + if (mode == CLS_H_LOHIFI || mode == CLS_H_ULP || + mode == CLS_H_HIFI || mode == CLS_H_LP) + snd_soc_component_update_bits(component, + WCD9XXX_CLASSH_MODE_3, + 0x02, 0x00); + + snd_soc_component_update_bits(component, + WCD9XXX_CLASSH_MODE_2, + 0xFF, 0x3A); + /* 500usec delay is needed as per HW requirement */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); + } +} + +static void wcd_clsh_flyback_ctrl(struct wcd_clsh_ctrl *ctrl, + int mode, + bool enable) +{ + struct snd_soc_component *comp = ctrl->comp; + + /* enable/disable flyback */ + if ((enable && (++ctrl->flyback_users == 1)) || + (!enable && (--ctrl->flyback_users == 0))) { + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_VNEG_EN_MASK, + enable << WCD9XXX_A_ANA_RX_VNEG_EN_SHIFT); + /* 100usec delay is needed as per HW requirement */ + usleep_range(100, 110); + } + /* + * 500us sleep is required after flyback enable/disable + * as per HW requirement + */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); +} + +static void wcd_clsh_set_gain_path(struct wcd_clsh_ctrl *ctrl, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + int val = 0; + + switch (mode) { + case CLS_H_NORMAL: + case CLS_AB: + val = WCD9XXX_HPH_CONST_SEL_BYPASS; + break; + case CLS_H_HIFI: + val = WCD9XXX_HPH_CONST_SEL_HQ_PATH; + break; + case CLS_H_LP: + val = WCD9XXX_HPH_CONST_SEL_LP_PATH; + break; + } + + snd_soc_component_update_bits(comp, WCD9XXX_HPH_L_EN, + WCD9XXX_HPH_CONST_SEL_L_MASK, + val); + + snd_soc_component_update_bits(comp, WCD9XXX_HPH_R_EN, + WCD9XXX_HPH_CONST_SEL_L_MASK, + val); +} + +static void wcd_clsh_v2_set_hph_mode(struct snd_soc_component *comp, int mode) +{ + int val = 0, gain = 0, res_val; + int ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; + + res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_0KOHM; + switch (mode) { + case CLS_H_NORMAL: + res_val = WCD9XXX_CLASSH_CTRL_VCL_VREF_FILT_R_50KOHM; + val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL; + gain = DAC_GAIN_0DB; + ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; + break; + case CLS_AB: + val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_NORMAL; + gain = DAC_GAIN_0DB; + ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; + break; + case CLS_H_HIFI: + val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_UHQA; + gain = DAC_GAIN_M0P2DB; + ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_50MA; + break; + case CLS_H_LP: + val = WCD9XXX_A_ANA_HPH_PWR_LEVEL_LP; + ipeak = WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_30MA; + break; + } + + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_HPH, + WCD9XXX_A_ANA_HPH_PWR_LEVEL_MASK, val); + snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_VCL_2, + WCD9XXX_CLASSH_CTRL_VCL_2_VREF_FILT_1_MASK, + res_val); + if (mode != CLS_H_LP) + snd_soc_component_update_bits(comp, + WCD9XXX_HPH_REFBUFF_UHQA_CTL, + WCD9XXX_HPH_REFBUFF_UHQA_GAIN_MASK, + gain); + snd_soc_component_update_bits(comp, WCD9XXX_CLASSH_CTRL_CCL_1, + WCD9XXX_CLASSH_CTRL_CCL_1_DELTA_IPEAK_MASK, + ipeak); +} + +static void wcd_clsh_v3_set_hph_mode(struct snd_soc_component *component, + int mode) +{ + u8 val; + + switch (mode) { + case CLS_H_NORMAL: + val = 0x00; + break; + case CLS_AB: + case CLS_H_ULP: + val = 0x0C; + break; + case CLS_AB_HIFI: + case CLS_H_HIFI: + val = 0x08; + break; + case CLS_H_LP: + case CLS_H_LOHIFI: + case CLS_AB_LP: + case CLS_AB_LOHIFI: + val = 0x04; + break; + default: + dev_err(component->dev, "%s:Invalid mode %d\n", __func__, mode); + return; + } + + snd_soc_component_update_bits(component, WCD9XXX_ANA_HPH, 0x0C, val); +} + +void wcd_clsh_set_hph_mode(struct wcd_clsh_ctrl *ctrl, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (ctrl->codec_version >= WCD937X) + wcd_clsh_v3_set_hph_mode(comp, mode); + else + wcd_clsh_v2_set_hph_mode(comp, mode); + +} +EXPORT_SYMBOL_GPL(wcd_clsh_set_hph_mode); + +static void wcd_clsh_set_flyback_current(struct snd_soc_component *comp, + int mode) +{ + + snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF, + WCD9XXX_RX_BIAS_FLYB_VPOS_5_UA_MASK, 0x0A); + snd_soc_component_update_bits(comp, WCD9XXX_RX_BIAS_FLYB_BUFF, + WCD9XXX_RX_BIAS_FLYB_VNEG_5_UA_MASK, 0x0A); + /* Sleep needed to avoid click and pop as per HW requirement */ + usleep_range(100, 110); +} + +static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_component *comp, + int mode) +{ + if (mode == CLS_AB) + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK, + WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_AB); + else + snd_soc_component_update_bits(comp, WCD9XXX_A_ANA_RX_SUPPLIES, + WCD9XXX_A_ANA_RX_REGULATOR_MODE_MASK, + WCD9XXX_A_ANA_RX_REGULATOR_MODE_CLS_H); +} + +static void wcd_clsh_v3_set_buck_regulator_mode(struct snd_soc_component *component, + int mode) +{ + snd_soc_component_update_bits(component, WCD9XXX_ANA_RX_SUPPLIES, + 0x02, 0x00); +} + +static void wcd_clsh_v3_set_flyback_mode(struct snd_soc_component *component, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB_LOHIFI) { + snd_soc_component_update_bits(component, + WCD9XXX_ANA_RX_SUPPLIES, + 0x04, 0x04); + snd_soc_component_update_bits(component, + WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x80); + } else { + snd_soc_component_update_bits(component, + WCD9XXX_ANA_RX_SUPPLIES, + 0x04, 0x00); /* set to Default */ + snd_soc_component_update_bits(component, + WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x70); + } +} + +static void wcd_clsh_v3_force_iq_ctl(struct snd_soc_component *component, + int mode, bool enable) +{ + if (enable) { + snd_soc_component_update_bits(component, + WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, 0xA0); + /* 100usec delay is needed as per HW requirement */ + usleep_range(100, 110); + snd_soc_component_update_bits(component, + WCD9XXX_CLASSH_MODE_3, + 0x02, 0x02); + snd_soc_component_update_bits(component, + WCD9XXX_CLASSH_MODE_2, + 0xFF, 0x1C); + if (mode == CLS_H_LOHIFI || mode == CLS_AB_LOHIFI) { + snd_soc_component_update_bits(component, + WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x20); + snd_soc_component_update_bits(component, + WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0xC0); + snd_soc_component_update_bits(component, + WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x02); + } + } else { + snd_soc_component_update_bits(component, + WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x00); + snd_soc_component_update_bits(component, + WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0x80); + snd_soc_component_update_bits(component, + WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x06); + } +} + +static void wcd_clsh_v3_flyback_ctrl(struct snd_soc_component *component, + struct wcd_clsh_ctrl *ctrl, + int mode, + bool enable) +{ + /* enable/disable flyback */ + if ((enable && (++ctrl->flyback_users == 1)) || + (!enable && (--ctrl->flyback_users == 0))) { + snd_soc_component_update_bits(component, + WCD9XXX_FLYBACK_VNEG_CTRL_1, + 0xE0, 0xE0); + snd_soc_component_update_bits(component, + WCD9XXX_ANA_RX_SUPPLIES, + (1 << 6), (enable << 6)); + /* + * 100us sleep is required after flyback enable/disable + * as per HW requirement + */ + usleep_range(100, 110); + snd_soc_component_update_bits(component, + WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, 0xE0); + /* 500usec delay is needed as per HW requirement */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); + } +} + +static void wcd_clsh_v3_set_flyback_current(struct snd_soc_component *component, + int mode) +{ + snd_soc_component_update_bits(component, WCD9XXX_V3_RX_BIAS_FLYB_BUFF, + 0x0F, 0x0A); + snd_soc_component_update_bits(component, WCD9XXX_V3_RX_BIAS_FLYB_BUFF, + 0xF0, 0xA0); + /* Sleep needed to avoid click and pop as per HW requirement */ + usleep_range(100, 110); +} + +static void wcd_clsh_v3_state_aux(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *component = ctrl->comp; + + if (is_enable) { + wcd_clsh_v3_set_buck_mode(component, mode); + wcd_clsh_v3_set_flyback_mode(component, mode); + wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true); + wcd_clsh_v3_set_flyback_current(component, mode); + wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true); + } else { + wcd_clsh_v3_buck_ctrl(component, ctrl, mode, false); + wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, false); + wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL); + wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_lo(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (mode != CLS_AB) { + dev_err(comp->dev, "%s: LO cannot be in this mode: %d\n", + __func__, mode); + return; + } + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(comp, mode); + wcd_clsh_set_buck_mode(comp, mode); + wcd_clsh_set_flyback_mode(comp, mode); + wcd_clsh_flyback_ctrl(ctrl, mode, true); + wcd_clsh_set_flyback_current(comp, mode); + wcd_clsh_buck_ctrl(ctrl, mode, true); + } else { + wcd_clsh_buck_ctrl(ctrl, mode, false); + wcd_clsh_flyback_ctrl(ctrl, mode, false); + wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); + } +} + +static void wcd_clsh_v3_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *component = ctrl->comp; + + if (mode == CLS_H_NORMAL) { + dev_dbg(component->dev, "%s: Normal mode not applicable for hph_r\n", + __func__); + return; + } + + if (is_enable) { + wcd_clsh_v3_set_buck_regulator_mode(component, mode); + wcd_clsh_v3_set_flyback_mode(component, mode); + wcd_clsh_v3_force_iq_ctl(component, mode, true); + wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true); + wcd_clsh_v3_set_flyback_current(component, mode); + wcd_clsh_v3_set_buck_mode(component, mode); + wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true); + wcd_clsh_v3_set_hph_mode(component, mode); + } else { + wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL); + + /* buck and flyback set to default mode and disable */ + wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false); + wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false); + wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false); + wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL); + wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_r(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (mode == CLS_H_NORMAL) { + dev_err(comp->dev, "%s: Normal mode not applicable for hph_r\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB) { + wcd_enable_clsh_block(ctrl, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_CLSH_K1_MSB, + WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK, + 0x00); + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_CLSH_K1_LSB, + WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK, + 0xC0); + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); + } + wcd_clsh_set_buck_regulator_mode(comp, mode); + wcd_clsh_set_flyback_mode(comp, mode); + wcd_clsh_flyback_ctrl(ctrl, mode, true); + wcd_clsh_set_flyback_current(comp, mode); + wcd_clsh_set_buck_mode(comp, mode); + wcd_clsh_buck_ctrl(ctrl, mode, true); + wcd_clsh_v2_set_hph_mode(comp, mode); + wcd_clsh_set_gain_path(ctrl, mode); + } else { + wcd_clsh_v2_set_hph_mode(comp, CLS_H_NORMAL); + + if (mode != CLS_AB) { + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); + wcd_enable_clsh_block(ctrl, false); + } + /* buck and flyback set to default mode and disable */ + wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false); + wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false); + wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); + } +} + +static void wcd_clsh_v3_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *component = ctrl->comp; + + if (mode == CLS_H_NORMAL) { + dev_dbg(component->dev, "%s: Normal mode not applicable for hph_l\n", + __func__); + return; + } + + if (is_enable) { + wcd_clsh_v3_set_buck_regulator_mode(component, mode); + wcd_clsh_v3_set_flyback_mode(component, mode); + wcd_clsh_v3_force_iq_ctl(component, mode, true); + wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true); + wcd_clsh_v3_set_flyback_current(component, mode); + wcd_clsh_v3_set_buck_mode(component, mode); + wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true); + wcd_clsh_v3_set_hph_mode(component, mode); + } else { + wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL); + + /* set buck and flyback to Default Mode */ + wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false); + wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false); + wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false); + wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL); + wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_l(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (mode == CLS_H_NORMAL) { + dev_err(comp->dev, "%s: Normal mode not applicable for hph_l\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB) { + wcd_enable_clsh_block(ctrl, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_CLSH_K1_MSB, + WCD9XXX_A_CDC_CLSH_K1_MSB_COEF_MASK, + 0x00); + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_CLSH_K1_LSB, + WCD9XXX_A_CDC_CLSH_K1_LSB_COEF_MASK, + 0xC0); + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); + } + wcd_clsh_set_buck_regulator_mode(comp, mode); + wcd_clsh_set_flyback_mode(comp, mode); + wcd_clsh_flyback_ctrl(ctrl, mode, true); + wcd_clsh_set_flyback_current(comp, mode); + wcd_clsh_set_buck_mode(comp, mode); + wcd_clsh_buck_ctrl(ctrl, mode, true); + wcd_clsh_v2_set_hph_mode(comp, mode); + wcd_clsh_set_gain_path(ctrl, mode); + } else { + wcd_clsh_v2_set_hph_mode(comp, CLS_H_NORMAL); + + if (mode != CLS_AB) { + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); + wcd_enable_clsh_block(ctrl, false); + } + /* set buck and flyback to Default Mode */ + wcd_clsh_buck_ctrl(ctrl, CLS_H_NORMAL, false); + wcd_clsh_flyback_ctrl(ctrl, CLS_H_NORMAL, false); + wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(comp, CLS_H_NORMAL); + } +} + +static void wcd_clsh_v3_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *component = ctrl->comp; + + if (is_enable) { + wcd_clsh_v3_set_buck_regulator_mode(component, mode); + wcd_clsh_v3_set_flyback_mode(component, mode); + wcd_clsh_v3_force_iq_ctl(component, mode, true); + wcd_clsh_v3_flyback_ctrl(component, ctrl, mode, true); + wcd_clsh_v3_set_flyback_current(component, mode); + wcd_clsh_v3_set_buck_mode(component, mode); + wcd_clsh_v3_buck_ctrl(component, ctrl, mode, true); + wcd_clsh_v3_set_hph_mode(component, mode); + } else { + wcd_clsh_v3_set_hph_mode(component, CLS_H_NORMAL); + + /* set buck and flyback to Default Mode */ + wcd_clsh_v3_flyback_ctrl(component, ctrl, CLS_H_NORMAL, false); + wcd_clsh_v3_buck_ctrl(component, ctrl, CLS_H_NORMAL, false); + wcd_clsh_v3_force_iq_ctl(component, CLS_H_NORMAL, false); + wcd_clsh_v3_set_flyback_mode(component, CLS_H_NORMAL); + wcd_clsh_v3_set_buck_mode(component, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_ear(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (mode != CLS_H_NORMAL) { + dev_err(comp->dev, "%s: mode: %d cannot be used for EAR\n", + __func__, mode); + return; + } + + if (is_enable) { + wcd_enable_clsh_block(ctrl, true); + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_ENABLE); + wcd_clsh_set_buck_mode(comp, mode); + wcd_clsh_set_flyback_mode(comp, mode); + wcd_clsh_flyback_ctrl(ctrl, mode, true); + wcd_clsh_set_flyback_current(comp, mode); + wcd_clsh_buck_ctrl(ctrl, mode, true); + } else { + snd_soc_component_update_bits(comp, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + WCD9XXX_A_CDC_RX_PATH_CLSH_EN_MASK, + WCD9XXX_A_CDC_RX_PATH_CLSH_DISABLE); + wcd_enable_clsh_block(ctrl, false); + wcd_clsh_buck_ctrl(ctrl, mode, false); + wcd_clsh_flyback_ctrl(ctrl, mode, false); + wcd_clsh_set_flyback_mode(comp, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(comp, CLS_H_NORMAL); + } +} + +static int _wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, int req_state, + bool is_enable, int mode) +{ + switch (req_state) { + case WCD_CLSH_STATE_EAR: + if (ctrl->codec_version >= WCD937X) + wcd_clsh_v3_state_ear(ctrl, req_state, is_enable, mode); + else + wcd_clsh_state_ear(ctrl, req_state, is_enable, mode); + break; + case WCD_CLSH_STATE_HPHL: + if (ctrl->codec_version >= WCD937X) + wcd_clsh_v3_state_hph_l(ctrl, req_state, is_enable, mode); + else + wcd_clsh_state_hph_l(ctrl, req_state, is_enable, mode); + break; + case WCD_CLSH_STATE_HPHR: + if (ctrl->codec_version >= WCD937X) + wcd_clsh_v3_state_hph_r(ctrl, req_state, is_enable, mode); + else + wcd_clsh_state_hph_r(ctrl, req_state, is_enable, mode); + break; + case WCD_CLSH_STATE_LO: + if (ctrl->codec_version < WCD937X) + wcd_clsh_state_lo(ctrl, req_state, is_enable, mode); + break; + case WCD_CLSH_STATE_AUX: + if (ctrl->codec_version >= WCD937X) + wcd_clsh_v3_state_aux(ctrl, req_state, is_enable, mode); + break; + default: + break; + } + + return 0; +} + +/* + * Function: wcd_clsh_is_state_valid + * Params: state + * Description: + * Provides information on valid states of Class H configuration + */ +static bool wcd_clsh_is_state_valid(int state) +{ + switch (state) { + case WCD_CLSH_STATE_IDLE: + case WCD_CLSH_STATE_EAR: + case WCD_CLSH_STATE_HPHL: + case WCD_CLSH_STATE_HPHR: + case WCD_CLSH_STATE_LO: + case WCD_CLSH_STATE_AUX: + return true; + default: + return false; + }; +} + +/* + * Function: wcd_clsh_fsm + * Params: ctrl, req_state, req_type, clsh_event + * Description: + * This function handles PRE DAC and POST DAC conditions of different devices + * and updates class H configuration of different combination of devices + * based on validity of their states. ctrl will contain current + * class h state information + */ +int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, + enum wcd_clsh_event clsh_event, + int nstate, + enum wcd_clsh_mode mode) +{ + struct snd_soc_component *comp = ctrl->comp; + + if (nstate == ctrl->state) + return 0; + + if (!wcd_clsh_is_state_valid(nstate)) { + dev_err(comp->dev, "Class-H not a valid new state:\n"); + return -EINVAL; + } + + switch (clsh_event) { + case WCD_CLSH_EVENT_PRE_DAC: + _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_ENABLE, mode); + break; + case WCD_CLSH_EVENT_POST_PA: + _wcd_clsh_ctrl_set_state(ctrl, nstate, CLSH_REQ_DISABLE, mode); + break; + } + + ctrl->state = nstate; + ctrl->mode = mode; + + return 0; +} +EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_set_state); + +int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl) +{ + return ctrl->state; +} +EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_get_state); + +struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp, + int version) +{ + struct wcd_clsh_ctrl *ctrl; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return ERR_PTR(-ENOMEM); + + ctrl->state = WCD_CLSH_STATE_IDLE; + ctrl->comp = comp; + ctrl->codec_version = version; + + return ctrl; +} +EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_alloc); + +void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl) +{ + kfree(ctrl); +} +EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_free); + +MODULE_DESCRIPTION("WCD93XX Class-H driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd-clsh-v2.h b/sound/soc/codecs/wcd-clsh-v2.h new file mode 100644 index 0000000000..4e36530582 --- /dev/null +++ b/sound/soc/codecs/wcd-clsh-v2.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _WCD_CLSH_V2_H_ +#define _WCD_CLSH_V2_H_ +#include + +enum wcd_clsh_event { + WCD_CLSH_EVENT_PRE_DAC = 1, + WCD_CLSH_EVENT_POST_PA, +}; + +/* + * Basic states for Class H state machine. + * represented as a bit mask within a u8 data type + * bit 0: EAR mode + * bit 1: HPH Left mode + * bit 2: HPH Right mode + * bit 3: Lineout mode + */ +#define WCD_CLSH_STATE_IDLE 0 +#define WCD_CLSH_STATE_EAR BIT(0) +#define WCD_CLSH_STATE_HPHL BIT(1) +#define WCD_CLSH_STATE_HPHR BIT(2) +#define WCD_CLSH_STATE_LO BIT(3) +#define WCD_CLSH_STATE_AUX BIT(4) +#define WCD_CLSH_STATE_MAX 4 +#define WCD_CLSH_V3_STATE_MAX 5 +#define NUM_CLSH_STATES_V2 BIT(WCD_CLSH_STATE_MAX) +#define NUM_CLSH_STATES_V3 BIT(WCD_CLSH_V3_STATE_MAX) + +enum wcd_clsh_mode { + CLS_H_NORMAL = 0, /* Class-H Default */ + CLS_H_HIFI, /* Class-H HiFi */ + CLS_H_LP, /* Class-H Low Power */ + CLS_AB, /* Class-AB */ + CLS_H_LOHIFI, /* LoHIFI */ + CLS_H_ULP, /* Ultra Low power */ + CLS_AB_HIFI, /* Class-AB */ + CLS_AB_LP, /* Class-AB Low Power */ + CLS_AB_LOHIFI, /* Class-AB Low HIFI */ + CLS_NONE, /* None of the above modes */ +}; + +enum wcd_codec_version { + WCD9335 = 0, + WCD934X = 1, + /* New CLSH after this */ + WCD937X = 2, + WCD938X = 3, +}; +struct wcd_clsh_ctrl; + +extern struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc( + struct snd_soc_component *comp, + int version); +extern void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl); +extern int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl); +extern int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, + enum wcd_clsh_event clsh_event, + int nstate, + enum wcd_clsh_mode mode); +extern void wcd_clsh_set_hph_mode(struct wcd_clsh_ctrl *ctrl, + int mode); + +#endif /* _WCD_CLSH_V2_H_ */ diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c new file mode 100644 index 0000000000..5da1934527 --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -0,0 +1,1584 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd-mbhc-v2.h" + +#define HS_DETECT_PLUG_TIME_MS (3 * 1000) +#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 +#define GND_MIC_SWAP_THRESHOLD 4 +#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 +#define HPHL_CROSS_CONN_THRESHOLD 100 +#define HS_VREF_MIN_VAL 1400 +#define FAKE_REM_RETRY_ATTEMPTS 3 +#define WCD_MBHC_ADC_HS_THRESHOLD_MV 1700 +#define WCD_MBHC_ADC_HPH_THRESHOLD_MV 75 +#define WCD_MBHC_ADC_MICBIAS_MV 1800 +#define WCD_MBHC_FAKE_INS_RETRY 4 + +#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | \ + SND_JACK_MECHANICAL) + +#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3 | \ + SND_JACK_BTN_4 | SND_JACK_BTN_5) + +enum wcd_mbhc_adc_mux_ctl { + MUX_CTL_AUTO = 0, + MUX_CTL_IN2P, + MUX_CTL_IN3P, + MUX_CTL_IN4P, + MUX_CTL_HPH_L, + MUX_CTL_HPH_R, + MUX_CTL_NONE, +}; + +struct wcd_mbhc { + struct device *dev; + struct snd_soc_component *component; + struct snd_soc_jack *jack; + struct wcd_mbhc_config *cfg; + const struct wcd_mbhc_cb *mbhc_cb; + const struct wcd_mbhc_intr *intr_ids; + struct wcd_mbhc_field *fields; + /* Delayed work to report long button press */ + struct delayed_work mbhc_btn_dwork; + /* Work to correct accessory type */ + struct work_struct correct_plug_swch; + struct mutex lock; + int buttons_pressed; + u32 hph_status; /* track headhpone status */ + u8 current_plug; + bool is_btn_press; + bool in_swch_irq_handler; + bool hs_detect_work_stop; + bool is_hs_recording; + bool extn_cable_hph_rem; + bool force_linein; + bool impedance_detect; + unsigned long event_state; + unsigned long jiffies_atreport; + /* impedance of hphl and hphr */ + uint32_t zl, zr; + /* Holds type of Headset - Mono/Stereo */ + enum wcd_mbhc_hph_type hph_type; + /* Holds mbhc detection method - ADC/Legacy */ + int mbhc_detection_logic; +}; + +static inline int wcd_mbhc_write_field(const struct wcd_mbhc *mbhc, + int field, int val) +{ + if (!mbhc->fields[field].reg) + return 0; + + return snd_soc_component_write_field(mbhc->component, + mbhc->fields[field].reg, + mbhc->fields[field].mask, val); +} + +static inline int wcd_mbhc_read_field(const struct wcd_mbhc *mbhc, int field) +{ + if (!mbhc->fields[field].reg) + return 0; + + return snd_soc_component_read_field(mbhc->component, + mbhc->fields[field].reg, + mbhc->fields[field].mask); +} + +static void wcd_program_hs_vref(struct wcd_mbhc *mbhc) +{ + u32 reg_val = ((mbhc->cfg->v_hs_max - HS_VREF_MIN_VAL) / 100); + + wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_VREF, reg_val); +} + +static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias) +{ + struct snd_soc_component *component = mbhc->component; + + mbhc->mbhc_cb->set_btn_thr(component, mbhc->cfg->btn_low, + mbhc->cfg->btn_high, + mbhc->cfg->num_btn, micbias); +} + +static void wcd_mbhc_curr_micbias_control(const struct wcd_mbhc *mbhc, + const enum wcd_mbhc_cs_mb_en_flag cs_mb_en) +{ + + /* + * Some codecs handle micbias/pullup enablement in codec + * drivers itself and micbias is not needed for regular + * plug type detection. So if micbias_control callback function + * is defined, just return. + */ + if (mbhc->mbhc_cb->mbhc_micbias_control) + return; + + switch (cs_mb_en) { + case WCD_MBHC_EN_CS: + wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3); + /* Program Button threshold registers as per CS */ + wcd_program_btn_threshold(mbhc, false); + break; + case WCD_MBHC_EN_MB: + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1); + /* Disable PULL_UP_EN & enable MICBIAS */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 2); + /* Program Button threshold registers as per MICBIAS */ + wcd_program_btn_threshold(mbhc, true); + break; + case WCD_MBHC_EN_PULLUP: + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3); + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1); + wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 1); + /* Program Button threshold registers as per MICBIAS */ + wcd_program_btn_threshold(mbhc, true); + break; + case WCD_MBHC_EN_NONE: + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1); + wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0); + break; + default: + dev_err(mbhc->dev, "%s: Invalid parameter", __func__); + break; + } +} + +int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event) +{ + + struct snd_soc_component *component; + bool micbias2 = false; + + if (!mbhc) + return 0; + + component = mbhc->component; + + if (mbhc->mbhc_cb->micbias_enable_status) + micbias2 = mbhc->mbhc_cb->micbias_enable_status(component, MIC_BIAS_2); + + switch (event) { + /* MICBIAS usage change */ + case WCD_EVENT_POST_DAPM_MICBIAS_2_ON: + mbhc->is_hs_recording = true; + break; + case WCD_EVENT_POST_MICBIAS_2_ON: + /* Disable current source if micbias2 enabled */ + if (mbhc->mbhc_cb->mbhc_micbias_control) { + if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN)) + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0); + } else { + mbhc->is_hs_recording = true; + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB); + } + break; + case WCD_EVENT_PRE_MICBIAS_2_OFF: + /* + * Before MICBIAS_2 is turned off, if FSM is enabled, + * make sure current source is enabled so as to detect + * button press/release events + */ + if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/) { + if (wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN)) + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3); + } + break; + /* MICBIAS usage change */ + case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF: + mbhc->is_hs_recording = false; + break; + case WCD_EVENT_POST_MICBIAS_2_OFF: + if (!mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->is_hs_recording = false; + + /* Enable PULL UP if PA's are enabled */ + if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) || + (test_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state))) + /* enable pullup and cs, disable mb */ + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP); + else + /* enable current source and disable mb, pullup*/ + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS); + + break; + case WCD_EVENT_POST_HPHL_PA_OFF: + clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state); + + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, pullup & enable cs */ + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS); + break; + case WCD_EVENT_POST_HPHR_PA_OFF: + clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, pullup & enable cs */ + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_CS); + break; + case WCD_EVENT_PRE_HPHL_PA_ON: + set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, enable pullup & cs */ + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP); + break; + case WCD_EVENT_PRE_HPHR_PA_ON: + set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, enable pullup & cs */ + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_PULLUP); + break; + default: + break; + } + return 0; +} +EXPORT_SYMBOL_GPL(wcd_mbhc_event_notify); + +static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc) +{ + return cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork); +} + +static void wcd_micbias_disable(struct wcd_mbhc *mbhc) +{ + struct snd_soc_component *component = mbhc->component; + + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE); + + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(component, MIC_BIAS_2, false); + + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value(component); + wcd_mbhc_write_field(mbhc, WCD_MBHC_MICB_CTRL, 0); + } +} + +static void wcd_mbhc_report_plug_removal(struct wcd_mbhc *mbhc, + enum snd_jack_types jack_type) +{ + mbhc->hph_status &= ~jack_type; + /* + * cancel possibly scheduled btn work and + * report release if we reported button press + */ + if (!wcd_cancel_btn_work(mbhc) && mbhc->buttons_pressed) { + snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed); + mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK; + } + + wcd_micbias_disable(mbhc); + mbhc->hph_type = WCD_MBHC_HPH_NONE; + mbhc->zl = mbhc->zr = 0; + snd_soc_jack_report(mbhc->jack, mbhc->hph_status, WCD_MBHC_JACK_MASK); + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + mbhc->force_linein = false; +} + +static void wcd_mbhc_compute_impedance(struct wcd_mbhc *mbhc) +{ + + if (!mbhc->impedance_detect) + return; + + if (mbhc->cfg->linein_th != 0) { + u8 fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN); + /* Set MUX_CTL to AUTO for Z-det */ + + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO); + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1); + mbhc->mbhc_cb->compute_impedance(mbhc->component, &mbhc->zl, &mbhc->zr); + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en); + } +} + +static void wcd_mbhc_report_plug_insertion(struct wcd_mbhc *mbhc, + enum snd_jack_types jack_type) +{ + bool is_pa_on; + /* + * Report removal of current jack type. + * Headphone to headset shouldn't report headphone + * removal. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET && + jack_type == SND_JACK_HEADPHONE) + mbhc->hph_status &= ~SND_JACK_HEADSET; + + /* Report insertion */ + switch (jack_type) { + case SND_JACK_HEADPHONE: + mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE; + break; + case SND_JACK_HEADSET: + mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET; + mbhc->jiffies_atreport = jiffies; + break; + case SND_JACK_LINEOUT: + mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + break; + default: + break; + } + + + is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN); + + if (!is_pa_on) { + wcd_mbhc_compute_impedance(mbhc); + if ((mbhc->zl > mbhc->cfg->linein_th) && + (mbhc->zr > mbhc->cfg->linein_th) && + (jack_type == SND_JACK_HEADPHONE)) { + jack_type = SND_JACK_LINEOUT; + mbhc->force_linein = true; + mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + if (mbhc->hph_status) { + mbhc->hph_status &= ~(SND_JACK_HEADSET | + SND_JACK_LINEOUT); + snd_soc_jack_report(mbhc->jack, mbhc->hph_status, + WCD_MBHC_JACK_MASK); + } + } + } + + /* Do not calculate impedance again for lineout + * as during playback pa is on and impedance values + * will not be correct resulting in lineout detected + * as headphone. + */ + if (is_pa_on && mbhc->force_linein) { + jack_type = SND_JACK_LINEOUT; + mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + if (mbhc->hph_status) { + mbhc->hph_status &= ~(SND_JACK_HEADSET | + SND_JACK_LINEOUT); + snd_soc_jack_report(mbhc->jack, mbhc->hph_status, + WCD_MBHC_JACK_MASK); + } + } + + mbhc->hph_status |= jack_type; + + if (jack_type == SND_JACK_HEADPHONE && mbhc->mbhc_cb->mbhc_micb_ramp_control) + mbhc->mbhc_cb->mbhc_micb_ramp_control(mbhc->component, false); + + snd_soc_jack_report(mbhc->jack, (mbhc->hph_status | SND_JACK_MECHANICAL), + WCD_MBHC_JACK_MASK); +} + +static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, + enum snd_jack_types jack_type) +{ + + WARN_ON(!mutex_is_locked(&mbhc->lock)); + + if (!insertion) /* Report removal */ + wcd_mbhc_report_plug_removal(mbhc, jack_type); + else + wcd_mbhc_report_plug_insertion(mbhc, jack_type); + +} + +static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + mbhc->hs_detect_work_stop = true; + mutex_unlock(&mbhc->lock); + cancel_work_sync(work); + mutex_lock(&mbhc->lock); +} + +static void wcd_mbhc_cancel_pending_work(struct wcd_mbhc *mbhc) +{ + /* cancel pending button press */ + wcd_cancel_btn_work(mbhc); + /* cancel correct work function */ + wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); +} + +static void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc) +{ + wcd_mbhc_cancel_pending_work(mbhc); + /* Report extension cable */ + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + /* + * Disable HPHL trigger and MIC Schmitt triggers. + * Setup for insertion detection. + */ + disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr); + wcd_mbhc_curr_micbias_control(mbhc, WCD_MBHC_EN_NONE); + /* Disable HW FSM */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 3); + + /* Set the detection type appropriately */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1); + enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr); +} + +static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + if (mbhc->current_plug == plug_type) + return; + + mutex_lock(&mbhc->lock); + + switch (plug_type) { + case MBHC_PLUG_TYPE_HEADPHONE: + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE); + break; + case MBHC_PLUG_TYPE_HEADSET: + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADSET); + break; + case MBHC_PLUG_TYPE_HIGH_HPH: + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + break; + case MBHC_PLUG_TYPE_GND_MIC_SWAP: + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE); + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET); + break; + default: + WARN(1, "Unexpected current plug_type %d, plug_type %d\n", + mbhc->current_plug, plug_type); + break; + } + mutex_unlock(&mbhc->lock); +} + +static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + WARN_ON(!mutex_is_locked(&mbhc->lock)); + mbhc->hs_detect_work_stop = false; + schedule_work(work); +} + +static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc) +{ + struct snd_soc_component *component = mbhc->component; + + WARN_ON(!mutex_is_locked(&mbhc->lock)); + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(component, false); + + wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0); + + if (mbhc->mbhc_cb->mbhc_micbias_control) { + mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, + MICB_ENABLE); + wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + } +} + +static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) +{ + struct snd_soc_component *component; + enum snd_jack_types jack_type; + struct wcd_mbhc *mbhc = data; + bool detection_type; + + component = mbhc->component; + mutex_lock(&mbhc->lock); + + mbhc->in_swch_irq_handler = true; + + wcd_mbhc_cancel_pending_work(mbhc); + + detection_type = wcd_mbhc_read_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE); + + /* Set the detection type appropriately */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_MECH_DETECTION_TYPE, !detection_type); + + /* Enable micbias ramp */ + if (mbhc->mbhc_cb->mbhc_micb_ramp_control) + mbhc->mbhc_cb->mbhc_micb_ramp_control(component, true); + + if (detection_type) { + if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE) + goto exit; + /* Make sure MASTER_BIAS_CTL is enabled */ + mbhc->mbhc_cb->mbhc_bias(component, true); + mbhc->is_btn_press = false; + wcd_mbhc_adc_detect_plug_type(mbhc); + } else { + /* Disable HW FSM */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0); + mbhc->extn_cable_hph_rem = false; + + if (mbhc->current_plug == MBHC_PLUG_TYPE_NONE) + goto exit; + + mbhc->is_btn_press = false; + switch (mbhc->current_plug) { + case MBHC_PLUG_TYPE_HEADPHONE: + jack_type = SND_JACK_HEADPHONE; + break; + case MBHC_PLUG_TYPE_HEADSET: + jack_type = SND_JACK_HEADSET; + break; + case MBHC_PLUG_TYPE_HIGH_HPH: + if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC) + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 0); + jack_type = SND_JACK_LINEOUT; + break; + case MBHC_PLUG_TYPE_GND_MIC_SWAP: + dev_err(mbhc->dev, "Ground and Mic Swapped on plug\n"); + goto exit; + default: + dev_err(mbhc->dev, "Invalid current plug: %d\n", + mbhc->current_plug); + goto exit; + } + disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr); + disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr); + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_DETECTION_TYPE, 1); + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0); + wcd_mbhc_report_plug(mbhc, 0, jack_type); + } + +exit: + mbhc->in_swch_irq_handler = false; + mutex_unlock(&mbhc->lock); + return IRQ_HANDLED; +} + +static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc) +{ + int mask = 0; + int btn; + + btn = wcd_mbhc_read_field(mbhc, WCD_MBHC_BTN_RESULT); + + switch (btn) { + case 0: + mask = SND_JACK_BTN_0; + break; + case 1: + mask = SND_JACK_BTN_1; + break; + case 2: + mask = SND_JACK_BTN_2; + break; + case 3: + mask = SND_JACK_BTN_3; + break; + case 4: + mask = SND_JACK_BTN_4; + break; + case 5: + mask = SND_JACK_BTN_5; + break; + default: + break; + } + + return mask; +} + +static void wcd_btn_long_press_fn(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct wcd_mbhc *mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork); + + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) + snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed, + mbhc->buttons_pressed); +} + +static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int mask; + unsigned long msec_val; + + mutex_lock(&mbhc->lock); + wcd_cancel_btn_work(mbhc); + mbhc->is_btn_press = true; + msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport); + + /* Too short, ignore button press */ + if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN) + goto done; + + /* If switch interrupt already kicked in, ignore button press */ + if (mbhc->in_swch_irq_handler) + goto done; + + /* Plug isn't headset, ignore button press */ + if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) + goto done; + + mask = wcd_mbhc_get_button_mask(mbhc); + mbhc->buttons_pressed |= mask; + if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, msecs_to_jiffies(400)) == 0) + WARN(1, "Button pressed twice without release event\n"); +done: + mutex_unlock(&mbhc->lock); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_btn_release_handler(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int ret; + + mutex_lock(&mbhc->lock); + if (mbhc->is_btn_press) + mbhc->is_btn_press = false; + else /* fake btn press */ + goto exit; + + if (!(mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK)) + goto exit; + + ret = wcd_cancel_btn_work(mbhc); + if (ret == 0) { /* Reporting long button release event */ + snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed); + } else { + if (!mbhc->in_swch_irq_handler) { + /* Reporting btn press n Release */ + snd_soc_jack_report(mbhc->jack, mbhc->buttons_pressed, + mbhc->buttons_pressed); + snd_soc_jack_report(mbhc->jack, 0, mbhc->buttons_pressed); + } + } + mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK; +exit: + mutex_unlock(&mbhc->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hph_ocp_irq(struct wcd_mbhc *mbhc, bool hphr) +{ + + /* TODO Find a better way to report this to Userspace */ + dev_err(mbhc->dev, "MBHC Over Current on %s detected\n", + hphr ? "HPHR" : "HPHL"); + + wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_OCP_FSM_EN, 1); + + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data) +{ + return wcd_mbhc_hph_ocp_irq(data, false); +} + +static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data) +{ + return wcd_mbhc_hph_ocp_irq(data, true); +} + +static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) +{ + struct snd_soc_component *component = mbhc->component; + int ret; + + ret = pm_runtime_get_sync(component->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(component->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(component->dev); + return ret; + } + + mutex_lock(&mbhc->lock); + + /* enable HS detection */ + if (mbhc->mbhc_cb->hph_pull_up_control_v2) + mbhc->mbhc_cb->hph_pull_up_control_v2(component, + HS_PULLUP_I_DEFAULT); + else if (mbhc->mbhc_cb->hph_pull_up_control) + mbhc->mbhc_cb->hph_pull_up_control(component, I_DEFAULT); + else + wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3); + + wcd_mbhc_write_field(mbhc, WCD_MBHC_HPHL_PLUG_TYPE, mbhc->cfg->hphl_swh); + wcd_mbhc_write_field(mbhc, WCD_MBHC_GND_PLUG_TYPE, mbhc->cfg->gnd_swh); + wcd_mbhc_write_field(mbhc, WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1); + if (mbhc->cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl) + mbhc->mbhc_cb->mbhc_gnd_det_ctrl(component, true); + wcd_mbhc_write_field(mbhc, WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1); + + wcd_mbhc_write_field(mbhc, WCD_MBHC_L_DET_EN, 1); + + /* Insertion debounce set to 96ms */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_INSREM_DBNC, 6); + + /* Button Debounce set to 16ms */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_DBNC, 2); + + /* enable bias */ + mbhc->mbhc_cb->mbhc_bias(component, true); + /* enable MBHC clock */ + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(component, true); + + /* program HS_VREF value */ + wcd_program_hs_vref(mbhc); + + wcd_program_btn_threshold(mbhc, false); + + mutex_unlock(&mbhc->lock); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc) +{ + int micbias = 0; + + if (mbhc->mbhc_cb->get_micbias_val) { + mbhc->mbhc_cb->get_micbias_val(mbhc->component, &micbias); + } else { + u8 vout_ctl = 0; + /* Read MBHC Micbias (Mic Bias2) voltage */ + vout_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_MICB2_VOUT); + /* Formula for getting micbias from vout + * micbias = 1.0V + VOUT_CTL * 50mV + */ + micbias = 1000 + (vout_ctl * 50); + } + return micbias; +} + +static int wcd_get_voltage_from_adc(u8 val, int micbias) +{ + /* Formula for calculating voltage from ADC + * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8 + */ + return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10)); +} + +static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc) +{ + u8 adc_result; + int output_mv; + int retry = 3; + u8 adc_en; + + /* Pre-requisites for ADC continuous measurement */ + /* Read legacy electircal detection and disable */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0x00); + /* Set ADC to continuous measurement */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 1); + /* Read ADC Enable bit to restore after adc measurement */ + adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN); + /* Disable ADC_ENABLE bit */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0); + /* Disable MBHC FSM */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0); + /* Set the MUX selection to IN2P */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_IN2P); + /* Enable MBHC FSM */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1); + /* Enable ADC_ENABLE bit */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1); + + while (retry--) { + /* wait for 3 msec before reading ADC result */ + usleep_range(3000, 3100); + adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT); + } + + /* Restore ADC Enable */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en); + /* Get voltage from ADC result */ + output_mv = wcd_get_voltage_from_adc(adc_result, wcd_mbhc_get_micbias(mbhc)); + + return output_mv; +} + +static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl) +{ + struct device *dev = mbhc->dev; + u8 adc_timeout = 0; + u8 adc_complete = 0; + u8 adc_result; + int retry = 6; + int ret; + int output_mv = 0; + u8 adc_en; + + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0); + /* Read ADC Enable bit to restore after adc measurement */ + adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN); + /* Trigger ADC one time measurement */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0); + /* Set the appropriate MUX selection */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, mux_ctl); + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1); + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 1); + + while (retry--) { + /* wait for 600usec to get adc results */ + usleep_range(600, 610); + + /* check for ADC Timeout */ + adc_timeout = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_TIMEOUT); + if (adc_timeout) + continue; + + /* Read ADC complete bit */ + adc_complete = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_COMPLETE); + if (!adc_complete) + continue; + + /* Read ADC result */ + adc_result = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_RESULT); + + /* Get voltage from ADC result */ + output_mv = wcd_get_voltage_from_adc(adc_result, + wcd_mbhc_get_micbias(mbhc)); + break; + } + + /* Restore ADC Enable */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en); + + if (retry <= 0) { + dev_err(dev, "%s: adc complete: %d, adc timeout: %d\n", + __func__, adc_complete, adc_timeout); + ret = -EINVAL; + } else { + ret = output_mv; + } + + return ret; +} + +/* To determine if cross connection occurred */ +static int wcd_check_cross_conn(struct wcd_mbhc *mbhc) +{ + u8 adc_mode, elect_ctl, adc_en, fsm_en; + int hphl_adc_res, hphr_adc_res; + bool is_cross_conn = false; + + /* If PA is enabled, dont check for cross-connection */ + if (wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN)) + return -EINVAL; + + /* Read legacy electircal detection and disable */ + elect_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC); + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0); + + /* Read and set ADC to single measurement */ + adc_mode = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_MODE); + /* Read ADC Enable bit to restore after adc measurement */ + adc_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_ADC_EN); + /* Read FSM status */ + fsm_en = wcd_mbhc_read_field(mbhc, WCD_MBHC_FSM_EN); + + /* Get adc result for HPH L */ + hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L); + if (hphl_adc_res < 0) + return hphl_adc_res; + + /* Get adc result for HPH R in mV */ + hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R); + if (hphr_adc_res < 0) + return hphr_adc_res; + + if (hphl_adc_res > HPHL_CROSS_CONN_THRESHOLD || + hphr_adc_res > HPHL_CROSS_CONN_THRESHOLD) + is_cross_conn = true; + + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 0); + /* Set the MUX selection to Auto */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_MUX_CTL, MUX_CTL_AUTO); + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, 1); + /* Restore ADC Enable */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, adc_en); + /* Restore ADC mode */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, adc_mode); + /* Restore FSM state */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_FSM_EN, fsm_en); + /* Restore electrical detection */ + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl); + + return is_cross_conn; +} + +static int wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc *mbhc) +{ + int hs_threshold, micbias_mv; + + micbias_mv = wcd_mbhc_get_micbias(mbhc); + if (mbhc->cfg->hs_thr) { + if (mbhc->cfg->micb_mv == micbias_mv) + hs_threshold = mbhc->cfg->hs_thr; + else + hs_threshold = (mbhc->cfg->hs_thr * + micbias_mv) / mbhc->cfg->micb_mv; + } else { + hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV); + } + return hs_threshold; +} + +static int wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc *mbhc) +{ + int hph_threshold, micbias_mv; + + micbias_mv = wcd_mbhc_get_micbias(mbhc); + if (mbhc->cfg->hph_thr) { + if (mbhc->cfg->micb_mv == micbias_mv) + hph_threshold = mbhc->cfg->hph_thr; + else + hph_threshold = (mbhc->cfg->hph_thr * + micbias_mv) / mbhc->cfg->micb_mv; + } else { + hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV * + micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV); + } + return hph_threshold; +} + +static void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + bool micbias2 = false; + + switch (plug_type) { + case MBHC_PLUG_TYPE_HEADPHONE: + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3); + break; + case MBHC_PLUG_TYPE_HEADSET: + if (mbhc->mbhc_cb->micbias_enable_status) + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc->component, + MIC_BIAS_2); + + if (!mbhc->is_hs_recording && !micbias2) + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 3); + break; + default: + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0); + break; + + } +} + +static void wcd_mbhc_bcs_enable(struct wcd_mbhc *mbhc, int plug_type, bool enable) +{ + switch (plug_type) { + case MBHC_PLUG_TYPE_HEADSET: + case MBHC_PLUG_TYPE_HEADPHONE: + if (mbhc->mbhc_cb->bcs_enable) + mbhc->mbhc_cb->bcs_enable(mbhc->component, enable); + break; + default: + break; + } +} + +static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result) + +{ + enum wcd_mbhc_plug_type plug_type; + u32 hph_thr, hs_thr; + + hs_thr = wcd_mbhc_adc_get_hs_thres(mbhc); + hph_thr = wcd_mbhc_adc_get_hph_thres(mbhc); + + if (adc_result < hph_thr) + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + else if (adc_result > hs_thr) + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + else + plug_type = MBHC_PLUG_TYPE_HEADSET; + + return plug_type; +} + +static int wcd_mbhc_get_spl_hs_thres(struct wcd_mbhc *mbhc) +{ + int hs_threshold, micbias_mv; + + micbias_mv = wcd_mbhc_get_micbias(mbhc); + if (mbhc->cfg->hs_thr && mbhc->cfg->micb_mv != WCD_MBHC_ADC_MICBIAS_MV) { + if (mbhc->cfg->micb_mv == micbias_mv) + hs_threshold = mbhc->cfg->hs_thr; + else + hs_threshold = (mbhc->cfg->hs_thr * micbias_mv) / mbhc->cfg->micb_mv; + } else { + hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * micbias_mv) / + WCD_MBHC_ADC_MICBIAS_MV); + } + return hs_threshold; +} + +static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc) +{ + bool is_spl_hs = false; + int output_mv, hs_threshold, hph_threshold; + + if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + return false; + + /* Bump up MIC_BIAS2 to 2.7V */ + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, true); + usleep_range(10000, 10100); + + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + hs_threshold = wcd_mbhc_get_spl_hs_thres(mbhc); + hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc); + + if (!(output_mv > hs_threshold || output_mv < hph_threshold)) + is_spl_hs = true; + + /* Back MIC_BIAS2 to 1.8v if the type is not special headset */ + if (!is_spl_hs) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->component, MIC_BIAS_2, false); + /* Add 10ms delay for micbias to settle */ + usleep_range(10000, 10100); + } + + return is_spl_hs; +} + +static void wcd_correct_swch_plug(struct work_struct *work) +{ + struct wcd_mbhc *mbhc; + struct snd_soc_component *component; + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; + unsigned long timeout; + int pt_gnd_mic_swap_cnt = 0; + int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv; + bool is_spl_hs = false; + bool is_pa_on; + int ret; + + mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); + component = mbhc->component; + + ret = pm_runtime_get_sync(component->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(component->dev, + "pm_runtime_get_sync failed in %s, ret %d\n", + __func__, ret); + pm_runtime_put_noidle(component->dev); + return; + } + micbias_mv = wcd_mbhc_get_micbias(mbhc); + hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); + + /* Mask ADC COMPLETE interrupt */ + disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr); + + /* Check for cross connection */ + do { + cross_conn = wcd_check_cross_conn(mbhc); + try++; + } while (try < GND_MIC_SWAP_THRESHOLD); + + if (cross_conn > 0) { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + dev_err(mbhc->dev, "cross connection found, Plug type %d\n", + plug_type); + goto correct_plug_type; + } + + /* Find plug type */ + output_mv = wcd_measure_adc_continuous(mbhc); + plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); + + /* + * Report plug type if it is either headset or headphone + * else start the 3 sec loop + */ + switch (plug_type) { + case MBHC_PLUG_TYPE_HEADPHONE: + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + break; + case MBHC_PLUG_TYPE_HEADSET: + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1); + break; + default: + break; + } + +correct_plug_type: + + /* Disable BCS slow insertion detection */ + wcd_mbhc_bcs_enable(mbhc, plug_type, false); + + timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS); + + while (!time_after(jiffies, timeout)) { + if (mbhc->hs_detect_work_stop) { + wcd_micbias_disable(mbhc); + goto exit; + } + + msleep(180); + /* + * Use ADC single mode to minimize the chance of missing out + * btn press/release for HEADSET type during correct work. + */ + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); + is_pa_on = wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN); + + if (output_mv > hs_threshold && !is_spl_hs) { + is_spl_hs = wcd_mbhc_check_for_spl_headset(mbhc); + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + + if (is_spl_hs) { + hs_threshold *= wcd_mbhc_get_micbias(mbhc); + hs_threshold /= micbias_mv; + } + } + + if ((output_mv <= hs_threshold) && !is_pa_on) { + /* Check for cross connection*/ + cross_conn = wcd_check_cross_conn(mbhc); + if (cross_conn > 0) { /* cross-connection */ + pt_gnd_mic_swap_cnt++; + if (pt_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD) + continue; + else + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + } else if (!cross_conn) { /* no cross connection */ + pt_gnd_mic_swap_cnt = 0; + plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); + continue; + } else /* Error if (cross_conn < 0) */ + continue; + + if (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) { + /* US_EU gpio present, flip switch */ + if (mbhc->cfg->swap_gnd_mic) { + if (mbhc->cfg->swap_gnd_mic(component, true)) + continue; + } + } + } + + /* cable is extension cable */ + if (output_mv > hs_threshold || mbhc->force_linein) + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + } + + wcd_mbhc_bcs_enable(mbhc, plug_type, true); + + if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) { + if (is_spl_hs) + plug_type = MBHC_PLUG_TYPE_HEADSET; + else + wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_ISRC_EN, 1); + } + + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + + /* + * Set DETECTION_DONE bit for HEADSET + * so that btn press/release interrupt can be generated. + * For other plug type, clear the bit. + */ + if (plug_type == MBHC_PLUG_TYPE_HEADSET) + wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1); + else + wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0); + + if (mbhc->mbhc_cb->mbhc_micbias_control) + wcd_mbhc_adc_update_fsm_source(mbhc, plug_type); + +exit: + if (mbhc->mbhc_cb->mbhc_micbias_control/* && !mbhc->micbias_enable*/) + mbhc->mbhc_cb->mbhc_micbias_control(component, MIC_BIAS_2, MICB_DISABLE); + + /* + * If plug type is corrected from special headset to headphone, + * clear the micbias enable flag, set micbias back to 1.8V and + * disable micbias. + */ + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { + wcd_micbias_disable(mbhc); + /* + * Enable ADC COMPLETE interrupt for HEADPHONE. + * Btn release may happen after the correct work, ADC COMPLETE + * interrupt needs to be captured to correct plug type. + */ + enable_irq(mbhc->intr_ids->mbhc_hs_ins_intr); + } + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(component, true); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); +} + +static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + unsigned long timeout; + int adc_threshold, output_mv, retry = 0; + + mutex_lock(&mbhc->lock); + timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); + adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); + + do { + retry++; + /* + * read output_mv every 10ms to look for + * any change in IN2_P + */ + usleep_range(10000, 10100); + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + + /* Check for fake removal */ + if ((output_mv <= adc_threshold) && retry > FAKE_REM_RETRY_ATTEMPTS) + goto exit; + } while (!time_after(jiffies, timeout)); + + /* + * ADC COMPLETE and ELEC_REM interrupts are both enabled for + * HEADPHONE, need to reject the ADC COMPLETE interrupt which + * follows ELEC_REM one when HEADPHONE is removed. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) + mbhc->extn_cable_hph_rem = true; + + wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 0); + wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_EN, 0); + wcd_mbhc_elec_hs_report_unplug(mbhc); + wcd_mbhc_write_field(mbhc, WCD_MBHC_BTN_ISRC_CTL, 0); + +exit: + mutex_unlock(&mbhc->lock); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + u8 clamp_state; + u8 clamp_retry = WCD_MBHC_FAKE_INS_RETRY; + + /* + * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE, + * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one + * when HEADPHONE is removed. + */ + if (mbhc->extn_cable_hph_rem == true) { + mbhc->extn_cable_hph_rem = false; + return IRQ_HANDLED; + } + + do { + clamp_state = wcd_mbhc_read_field(mbhc, WCD_MBHC_IN2P_CLAMP_STATE); + if (clamp_state) + return IRQ_HANDLED; + /* + * check clamp for 120ms but at 30ms chunks to leave + * room for other interrupts to be processed + */ + usleep_range(30000, 30100); + } while (--clamp_retry); + + /* + * If current plug is headphone then there is no chance to + * get ADC complete interrupt, so connected cable should be + * headset not headphone. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { + disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr); + wcd_mbhc_write_field(mbhc, WCD_MBHC_DETECTION_DONE, 1); + wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); + return IRQ_HANDLED; + } + + return IRQ_HANDLED; +} + +int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, uint32_t *zr) +{ + *zl = mbhc->zl; + *zr = mbhc->zr; + + if (*zl && *zr) + return 0; + else + return -EINVAL; +} +EXPORT_SYMBOL(wcd_mbhc_get_impedance); + +void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type) +{ + mbhc->hph_type = hph_type; +} +EXPORT_SYMBOL(wcd_mbhc_set_hph_type); + +int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc) +{ + return mbhc->hph_type; +} +EXPORT_SYMBOL(wcd_mbhc_get_hph_type); + +int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *cfg, + struct snd_soc_jack *jack) +{ + if (!mbhc || !cfg || !jack) + return -EINVAL; + + mbhc->cfg = cfg; + mbhc->jack = jack; + + return wcd_mbhc_initialise(mbhc); +} +EXPORT_SYMBOL(wcd_mbhc_start); + +void wcd_mbhc_stop(struct wcd_mbhc *mbhc) +{ + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + mbhc->hph_status = 0; + disable_irq_nosync(mbhc->intr_ids->hph_left_ocp); + disable_irq_nosync(mbhc->intr_ids->hph_right_ocp); +} +EXPORT_SYMBOL(wcd_mbhc_stop); + +int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg) +{ + struct device_node *np = dev->of_node; + int ret, i, microvolt; + + if (of_property_read_bool(np, "qcom,hphl-jack-type-normally-closed")) + cfg->hphl_swh = false; + else + cfg->hphl_swh = true; + + if (of_property_read_bool(np, "qcom,ground-jack-type-normally-closed")) + cfg->gnd_swh = false; + else + cfg->gnd_swh = true; + + ret = of_property_read_u32(np, "qcom,mbhc-headset-vthreshold-microvolt", + µvolt); + if (ret) + dev_dbg(dev, "missing qcom,mbhc-hs-mic-max-vthreshold--microvolt in dt node\n"); + else + cfg->hs_thr = microvolt/1000; + + ret = of_property_read_u32(np, "qcom,mbhc-headphone-vthreshold-microvolt", + µvolt); + if (ret) + dev_dbg(dev, "missing qcom,mbhc-hs-mic-min-vthreshold-microvolt entry\n"); + else + cfg->hph_thr = microvolt/1000; + + ret = of_property_read_u32_array(np, + "qcom,mbhc-buttons-vthreshold-microvolt", + &cfg->btn_high[0], + WCD_MBHC_DEF_BUTTONS); + if (ret) + dev_err(dev, "missing qcom,mbhc-buttons-vthreshold-microvolt entry\n"); + + for (i = 0; i < WCD_MBHC_DEF_BUTTONS; i++) { + if (ret) /* default voltage */ + cfg->btn_high[i] = 500000; + else + /* Micro to Milli Volts */ + cfg->btn_high[i] = cfg->btn_high[i]/1000; + } + + return 0; +} +EXPORT_SYMBOL(wcd_dt_parse_mbhc_data); + +struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *intr_ids, + struct wcd_mbhc_field *fields, + bool impedance_det_en) +{ + struct device *dev = component->dev; + struct wcd_mbhc *mbhc; + int ret; + + if (!intr_ids || !fields || !mbhc_cb || !mbhc_cb->mbhc_bias || !mbhc_cb->set_btn_thr) { + dev_err(dev, "%s: Insufficient mbhc configuration\n", __func__); + return ERR_PTR(-EINVAL); + } + + mbhc = kzalloc(sizeof(*mbhc), GFP_KERNEL); + if (!mbhc) + return ERR_PTR(-ENOMEM); + + mbhc->component = component; + mbhc->dev = dev; + mbhc->intr_ids = intr_ids; + mbhc->mbhc_cb = mbhc_cb; + mbhc->fields = fields; + mbhc->mbhc_detection_logic = WCD_DETECTION_ADC; + + if (mbhc_cb->compute_impedance) + mbhc->impedance_detect = impedance_det_en; + + INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_long_press_fn); + + mutex_init(&mbhc->lock); + + INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); + + ret = request_threaded_irq(mbhc->intr_ids->mbhc_sw_intr, NULL, + wcd_mbhc_mech_plug_detect_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "mbhc sw intr", mbhc); + if (ret) + goto err_free_mbhc; + + ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_press_intr, NULL, + wcd_mbhc_btn_press_handler, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "Button Press detect", mbhc); + if (ret) + goto err_free_sw_intr; + + ret = request_threaded_irq(mbhc->intr_ids->mbhc_btn_release_intr, NULL, + wcd_mbhc_btn_release_handler, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "Button Release detect", mbhc); + if (ret) + goto err_free_btn_press_intr; + + ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_ins_intr, NULL, + wcd_mbhc_adc_hs_ins_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "Elect Insert", mbhc); + if (ret) + goto err_free_btn_release_intr; + + disable_irq_nosync(mbhc->intr_ids->mbhc_hs_ins_intr); + + ret = request_threaded_irq(mbhc->intr_ids->mbhc_hs_rem_intr, NULL, + wcd_mbhc_adc_hs_rem_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "Elect Remove", mbhc); + if (ret) + goto err_free_hs_ins_intr; + + disable_irq_nosync(mbhc->intr_ids->mbhc_hs_rem_intr); + + ret = request_threaded_irq(mbhc->intr_ids->hph_left_ocp, NULL, + wcd_mbhc_hphl_ocp_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPH_L OCP detect", mbhc); + if (ret) + goto err_free_hs_rem_intr; + + ret = request_threaded_irq(mbhc->intr_ids->hph_right_ocp, NULL, + wcd_mbhc_hphr_ocp_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPH_R OCP detect", mbhc); + if (ret) + goto err_free_hph_left_ocp; + + return mbhc; + +err_free_hph_left_ocp: + free_irq(mbhc->intr_ids->hph_left_ocp, mbhc); +err_free_hs_rem_intr: + free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc); +err_free_hs_ins_intr: + free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc); +err_free_btn_release_intr: + free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc); +err_free_btn_press_intr: + free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc); +err_free_sw_intr: + free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc); +err_free_mbhc: + kfree(mbhc); + + dev_err(dev, "Failed to request mbhc interrupts %d\n", ret); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL(wcd_mbhc_init); + +void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) +{ + free_irq(mbhc->intr_ids->hph_right_ocp, mbhc); + free_irq(mbhc->intr_ids->hph_left_ocp, mbhc); + free_irq(mbhc->intr_ids->mbhc_hs_rem_intr, mbhc); + free_irq(mbhc->intr_ids->mbhc_hs_ins_intr, mbhc); + free_irq(mbhc->intr_ids->mbhc_btn_release_intr, mbhc); + free_irq(mbhc->intr_ids->mbhc_btn_press_intr, mbhc); + free_irq(mbhc->intr_ids->mbhc_sw_intr, mbhc); + + mutex_lock(&mbhc->lock); + wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + mutex_unlock(&mbhc->lock); + + kfree(mbhc); +} +EXPORT_SYMBOL(wcd_mbhc_deinit); + +static int __init mbhc_init(void) +{ + return 0; +} + +static void __exit mbhc_exit(void) +{ +} + +module_init(mbhc_init); +module_exit(mbhc_exit); + +MODULE_DESCRIPTION("wcd MBHC v2 module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h new file mode 100644 index 0000000000..006118f3e8 --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -0,0 +1,340 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __WCD_MBHC_V2_H__ +#define __WCD_MBHC_V2_H__ + +#include + +#define WCD_MBHC_FIELD(id, rreg, rmask) \ + [id] = { .reg = rreg, .mask = rmask } + +enum wcd_mbhc_field_function { + WCD_MBHC_L_DET_EN, + WCD_MBHC_GND_DET_EN, + WCD_MBHC_MECH_DETECTION_TYPE, + WCD_MBHC_MIC_CLAMP_CTL, + WCD_MBHC_ELECT_DETECTION_TYPE, + WCD_MBHC_HS_L_DET_PULL_UP_CTRL, + WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, + WCD_MBHC_HPHL_PLUG_TYPE, + WCD_MBHC_GND_PLUG_TYPE, + WCD_MBHC_SW_HPH_LP_100K_TO_GND, + WCD_MBHC_ELECT_SCHMT_ISRC, + WCD_MBHC_FSM_EN, + WCD_MBHC_INSREM_DBNC, + WCD_MBHC_BTN_DBNC, + WCD_MBHC_HS_VREF, + WCD_MBHC_HS_COMP_RESULT, + WCD_MBHC_IN2P_CLAMP_STATE, + WCD_MBHC_MIC_SCHMT_RESULT, + WCD_MBHC_HPHL_SCHMT_RESULT, + WCD_MBHC_HPHR_SCHMT_RESULT, + WCD_MBHC_OCP_FSM_EN, + WCD_MBHC_BTN_RESULT, + WCD_MBHC_BTN_ISRC_CTL, + WCD_MBHC_ELECT_RESULT, + WCD_MBHC_MICB_CTRL, /* Pull-up and micb control */ + WCD_MBHC_HPH_CNP_WG_TIME, + WCD_MBHC_HPHR_PA_EN, + WCD_MBHC_HPHL_PA_EN, + WCD_MBHC_HPH_PA_EN, + WCD_MBHC_SWCH_LEVEL_REMOVE, + WCD_MBHC_PULLDOWN_CTRL, + WCD_MBHC_ANC_DET_EN, + WCD_MBHC_FSM_STATUS, + WCD_MBHC_MUX_CTL, + WCD_MBHC_MOISTURE_STATUS, + WCD_MBHC_HPHR_GND, + WCD_MBHC_HPHL_GND, + WCD_MBHC_HPHL_OCP_DET_EN, + WCD_MBHC_HPHR_OCP_DET_EN, + WCD_MBHC_HPHL_OCP_STATUS, + WCD_MBHC_HPHR_OCP_STATUS, + WCD_MBHC_ADC_EN, + WCD_MBHC_ADC_COMPLETE, + WCD_MBHC_ADC_TIMEOUT, + WCD_MBHC_ADC_RESULT, + WCD_MBHC_MICB2_VOUT, + WCD_MBHC_ADC_MODE, + WCD_MBHC_DETECTION_DONE, + WCD_MBHC_ELECT_ISRC_EN, + WCD_MBHC_REG_FUNC_MAX, +}; + +#define WCD_MBHC_DEF_BUTTONS 8 +#define WCD_MBHC_KEYCODE_NUM 8 +#define WCD_MBHC_USLEEP_RANGE_MARGIN_US 100 +#define WCD_MBHC_THR_HS_MICB_MV 2700 +#define WCD_MONO_HS_MIN_THR 2 + +enum wcd_mbhc_detect_logic { + WCD_DETECTION_LEGACY, + WCD_DETECTION_ADC, +}; + +enum wcd_mbhc_cs_mb_en_flag { + WCD_MBHC_EN_CS = 0, + WCD_MBHC_EN_MB, + WCD_MBHC_EN_PULLUP, + WCD_MBHC_EN_NONE, +}; + +enum { + WCD_MBHC_ELEC_HS_INS, + WCD_MBHC_ELEC_HS_REM, +}; + +enum wcd_mbhc_plug_type { + MBHC_PLUG_TYPE_INVALID = -1, + MBHC_PLUG_TYPE_NONE, + MBHC_PLUG_TYPE_HEADSET, + MBHC_PLUG_TYPE_HEADPHONE, + MBHC_PLUG_TYPE_HIGH_HPH, + MBHC_PLUG_TYPE_GND_MIC_SWAP, +}; + +enum pa_dac_ack_flags { + WCD_MBHC_HPHL_PA_OFF_ACK = 0, + WCD_MBHC_HPHR_PA_OFF_ACK, +}; + +enum wcd_mbhc_btn_det_mem { + WCD_MBHC_BTN_DET_V_BTN_LOW, + WCD_MBHC_BTN_DET_V_BTN_HIGH +}; + +enum { + MIC_BIAS_1 = 1, + MIC_BIAS_2, + MIC_BIAS_3, + MIC_BIAS_4 +}; + +enum { + MICB_PULLUP_ENABLE, + MICB_PULLUP_DISABLE, + MICB_ENABLE, + MICB_DISABLE, +}; + +enum wcd_notify_event { + WCD_EVENT_INVALID, + /* events for micbias ON and OFF */ + WCD_EVENT_PRE_MICBIAS_2_OFF, + WCD_EVENT_POST_MICBIAS_2_OFF, + WCD_EVENT_PRE_MICBIAS_2_ON, + WCD_EVENT_POST_MICBIAS_2_ON, + WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF, + WCD_EVENT_PRE_DAPM_MICBIAS_2_ON, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON, + /* events for PA ON and OFF */ + WCD_EVENT_PRE_HPHL_PA_ON, + WCD_EVENT_POST_HPHL_PA_OFF, + WCD_EVENT_PRE_HPHR_PA_ON, + WCD_EVENT_POST_HPHR_PA_OFF, + WCD_EVENT_PRE_HPHL_PA_OFF, + WCD_EVENT_PRE_HPHR_PA_OFF, + WCD_EVENT_OCP_OFF, + WCD_EVENT_OCP_ON, + WCD_EVENT_LAST, +}; + +enum wcd_mbhc_event_state { + WCD_MBHC_EVENT_PA_HPHL, + WCD_MBHC_EVENT_PA_HPHR, +}; + +enum wcd_mbhc_hph_type { + WCD_MBHC_HPH_NONE = 0, + WCD_MBHC_HPH_MONO, + WCD_MBHC_HPH_STEREO, +}; + +/* + * These enum definitions are directly mapped to the register + * definitions + */ + +enum mbhc_hs_pullup_iref { + I_DEFAULT = -1, + I_OFF = 0, + I_1P0_UA, + I_2P0_UA, + I_3P0_UA, +}; + +enum mbhc_hs_pullup_iref_v2 { + HS_PULLUP_I_DEFAULT = -1, + HS_PULLUP_I_3P0_UA = 0, + HS_PULLUP_I_2P25_UA, + HS_PULLUP_I_1P5_UA, + HS_PULLUP_I_0P75_UA, + HS_PULLUP_I_1P125_UA = 0x05, + HS_PULLUP_I_0P375_UA = 0x07, + HS_PULLUP_I_2P0_UA, + HS_PULLUP_I_1P0_UA = 0x0A, + HS_PULLUP_I_0P5_UA, + HS_PULLUP_I_0P25_UA = 0x0F, + HS_PULLUP_I_0P125_UA = 0x17, + HS_PULLUP_I_OFF, +}; + +enum mbhc_moisture_rref { + R_OFF, + R_24_KOHM, + R_84_KOHM, + R_184_KOHM, +}; + +struct wcd_mbhc_config { + int btn_high[WCD_MBHC_DEF_BUTTONS]; + int btn_low[WCD_MBHC_DEF_BUTTONS]; + int v_hs_max; + int num_btn; + bool mono_stero_detection; + bool (*swap_gnd_mic)(struct snd_soc_component *component, bool active); + bool hs_ext_micbias; + bool gnd_det_en; + uint32_t linein_th; + bool moisture_en; + int mbhc_micbias; + int anc_micbias; + bool moisture_duty_cycle_en; + bool hphl_swh; /*track HPHL switch NC / NO */ + bool gnd_swh; /*track GND switch NC / NO */ + u32 hs_thr; + u32 hph_thr; + u32 micb_mv; + u32 moist_vref; + u32 moist_iref; + u32 moist_rref; +}; + +struct wcd_mbhc_intr { + int mbhc_sw_intr; + int mbhc_btn_press_intr; + int mbhc_btn_release_intr; + int mbhc_hs_ins_intr; + int mbhc_hs_rem_intr; + int hph_left_ocp; + int hph_right_ocp; +}; + +struct wcd_mbhc_field { + u16 reg; + u8 mask; +}; + +struct wcd_mbhc; + +struct wcd_mbhc_cb { + void (*update_cross_conn_thr)(struct snd_soc_component *component); + void (*get_micbias_val)(struct snd_soc_component *component, int *mb); + void (*bcs_enable)(struct snd_soc_component *component, bool bcs_enable); + void (*compute_impedance)(struct snd_soc_component *component, + uint32_t *zl, uint32_t *zr); + void (*set_micbias_value)(struct snd_soc_component *component); + void (*set_auto_zeroing)(struct snd_soc_component *component, + bool enable); + void (*clk_setup)(struct snd_soc_component *component, bool enable); + bool (*micbias_enable_status)(struct snd_soc_component *component, int micb_num); + void (*mbhc_bias)(struct snd_soc_component *component, bool enable); + void (*set_btn_thr)(struct snd_soc_component *component, + int *btn_low, int *btn_high, + int num_btn, bool is_micbias); + void (*hph_pull_up_control)(struct snd_soc_component *component, + enum mbhc_hs_pullup_iref); + int (*mbhc_micbias_control)(struct snd_soc_component *component, + int micb_num, int req); + void (*mbhc_micb_ramp_control)(struct snd_soc_component *component, + bool enable); + bool (*extn_use_mb)(struct snd_soc_component *component); + int (*mbhc_micb_ctrl_thr_mic)(struct snd_soc_component *component, + int micb_num, bool req_en); + void (*mbhc_gnd_det_ctrl)(struct snd_soc_component *component, + bool enable); + void (*hph_pull_down_ctrl)(struct snd_soc_component *component, + bool enable); + void (*mbhc_moisture_config)(struct snd_soc_component *component); + void (*update_anc_state)(struct snd_soc_component *component, + bool enable, int anc_num); + void (*hph_pull_up_control_v2)(struct snd_soc_component *component, + int pull_up_cur); + bool (*mbhc_get_moisture_status)(struct snd_soc_component *component); + void (*mbhc_moisture_polling_ctrl)(struct snd_soc_component *component, bool enable); + void (*mbhc_moisture_detect_en)(struct snd_soc_component *component, bool enable); +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD_MBHC) +int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg); +int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg, + struct snd_soc_jack *jack); +void wcd_mbhc_stop(struct wcd_mbhc *mbhc); +void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type); +int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc); +struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_field *fields, + bool impedance_det_en); +int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr); +void wcd_mbhc_deinit(struct wcd_mbhc *mbhc); +int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event); + +#else +static inline int wcd_dt_parse_mbhc_data(struct device *dev, + struct wcd_mbhc_config *cfg) +{ + return -ENOTSUPP; +} + +static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc) +{ +} + +static inline struct wcd_mbhc *wcd_mbhc_init(struct snd_soc_component *component, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_field *fields, + bool impedance_det_en) +{ + return ERR_PTR(-ENOTSUPP); +} + +static inline void wcd_mbhc_set_hph_type(struct wcd_mbhc *mbhc, int hph_type) +{ +} + +static inline int wcd_mbhc_get_hph_type(struct wcd_mbhc *mbhc) +{ + return -ENOTSUPP; +} + +static inline int wcd_mbhc_event_notify(struct wcd_mbhc *mbhc, unsigned long event) +{ + return -ENOTSUPP; +} + +static inline int wcd_mbhc_start(struct wcd_mbhc *mbhc, + struct wcd_mbhc_config *mbhc_cfg, + struct snd_soc_jack *jack) +{ + return 0; +} + +static inline int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, + uint32_t *zl, + uint32_t *zr) +{ + *zl = 0; + *zr = 0; + return -EINVAL; +} +static inline void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) +{ +} +#endif + +#endif /* __WCD_MBHC_V2_H__ */ diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c new file mode 100644 index 0000000000..a05b553e64 --- /dev/null +++ b/sound/soc/codecs/wcd9335.c @@ -0,0 +1,5250 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +// Copyright (c) 2017-2018, Linaro Limited + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd9335.h" +#include "wcd-clsh-v2.h" + +#include + +#define WCD9335_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +/* Fractional Rates */ +#define WCD9335_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100) +#define WCD9335_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/* slave port water mark level + * (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes) + */ +#define SLAVE_PORT_WATER_MARK_6BYTES 0 +#define SLAVE_PORT_WATER_MARK_9BYTES 1 +#define SLAVE_PORT_WATER_MARK_12BYTES 2 +#define SLAVE_PORT_WATER_MARK_15BYTES 3 +#define SLAVE_PORT_WATER_MARK_SHIFT 1 +#define SLAVE_PORT_ENABLE 1 +#define SLAVE_PORT_DISABLE 0 +#define WCD9335_SLIM_WATER_MARK_VAL \ + ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \ + (SLAVE_PORT_ENABLE)) + +#define WCD9335_SLIM_NUM_PORT_REG 3 +#define WCD9335_SLIM_PGD_PORT_INT_TX_EN0 (WCD9335_SLIM_PGD_PORT_INT_EN0 + 2) + +#define WCD9335_MCLK_CLK_12P288MHZ 12288000 +#define WCD9335_MCLK_CLK_9P6MHZ 9600000 + +#define WCD9335_SLIM_CLOSE_TIMEOUT 1000 +#define WCD9335_SLIM_IRQ_OVERFLOW (1 << 0) +#define WCD9335_SLIM_IRQ_UNDERFLOW (1 << 1) +#define WCD9335_SLIM_IRQ_PORT_CLOSED (1 << 2) + +#define WCD9335_NUM_INTERPOLATORS 9 +#define WCD9335_RX_START 16 +#define WCD9335_SLIM_CH_START 128 +#define WCD9335_MAX_MICBIAS 4 +#define WCD9335_MAX_VALID_ADC_MUX 13 +#define WCD9335_INVALID_ADC_MUX 9 + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 +#define WCD9335_DMIC_CLK_DIV_2 0x0 +#define WCD9335_DMIC_CLK_DIV_3 0x1 +#define WCD9335_DMIC_CLK_DIV_4 0x2 +#define WCD9335_DMIC_CLK_DIV_6 0x3 +#define WCD9335_DMIC_CLK_DIV_8 0x4 +#define WCD9335_DMIC_CLK_DIV_16 0x5 +#define WCD9335_DMIC_CLK_DRIVE_DEFAULT 0x02 +#define WCD9335_AMIC_PWR_LEVEL_LP 0 +#define WCD9335_AMIC_PWR_LEVEL_DEFAULT 1 +#define WCD9335_AMIC_PWR_LEVEL_HP 2 +#define WCD9335_AMIC_PWR_LVL_MASK 0x60 +#define WCD9335_AMIC_PWR_LVL_SHIFT 0x5 + +#define WCD9335_DEC_PWR_LVL_MASK 0x06 +#define WCD9335_DEC_PWR_LVL_LP 0x02 +#define WCD9335_DEC_PWR_LVL_HP 0x04 +#define WCD9335_DEC_PWR_LVL_DF 0x00 + +#define WCD9335_SLIM_RX_CH(p) \ + {.port = p + WCD9335_RX_START, .shift = p,} + +#define WCD9335_SLIM_TX_CH(p) \ + {.port = p, .shift = p,} + +/* vout step value */ +#define WCD9335_CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25) + +#define WCD9335_INTERPOLATOR_PATH(id) \ + {"RX INT" #id "_1 MIX1 INP0", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_2 MUX", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_2 MUX", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_2 MUX", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_2 MUX", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_2 MUX", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_2 MUX", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_2 MUX", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_2 MUX", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP0"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP1"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP2"}, \ + {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_2 MUX"}, \ + {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_1 MIX1"}, \ + {"RX INT" #id " MIX2", NULL, "RX INT" #id " SEC MIX"}, \ + {"RX INT" #id " INTERP", NULL, "RX INT" #id " MIX2"} + +#define WCD9335_ADC_MUX_PATH(id) \ + {"AIF1_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id " MUX"}, \ + {"AIF2_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id " MUX"}, \ + {"AIF3_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id " MUX"}, \ + {"SLIM TX" #id " MUX", "DEC" #id, "ADC MUX" #id}, \ + {"ADC MUX" #id, "DMIC", "DMIC MUX" #id}, \ + {"ADC MUX" #id, "AMIC", "AMIC MUX" #id}, \ + {"DMIC MUX" #id, "DMIC0", "DMIC0"}, \ + {"DMIC MUX" #id, "DMIC1", "DMIC1"}, \ + {"DMIC MUX" #id, "DMIC2", "DMIC2"}, \ + {"DMIC MUX" #id, "DMIC3", "DMIC3"}, \ + {"DMIC MUX" #id, "DMIC4", "DMIC4"}, \ + {"DMIC MUX" #id, "DMIC5", "DMIC5"}, \ + {"AMIC MUX" #id, "ADC1", "ADC1"}, \ + {"AMIC MUX" #id, "ADC2", "ADC2"}, \ + {"AMIC MUX" #id, "ADC3", "ADC3"}, \ + {"AMIC MUX" #id, "ADC4", "ADC4"}, \ + {"AMIC MUX" #id, "ADC5", "ADC5"}, \ + {"AMIC MUX" #id, "ADC6", "ADC6"} + +enum { + WCD9335_RX0 = 0, + WCD9335_RX1, + WCD9335_RX2, + WCD9335_RX3, + WCD9335_RX4, + WCD9335_RX5, + WCD9335_RX6, + WCD9335_RX7, + WCD9335_RX8, + WCD9335_RX9, + WCD9335_RX10, + WCD9335_RX11, + WCD9335_RX12, + WCD9335_RX_MAX, +}; + +enum { + WCD9335_TX0 = 0, + WCD9335_TX1, + WCD9335_TX2, + WCD9335_TX3, + WCD9335_TX4, + WCD9335_TX5, + WCD9335_TX6, + WCD9335_TX7, + WCD9335_TX8, + WCD9335_TX9, + WCD9335_TX10, + WCD9335_TX11, + WCD9335_TX12, + WCD9335_TX13, + WCD9335_TX14, + WCD9335_TX15, + WCD9335_TX_MAX, +}; + +enum { + SIDO_SOURCE_INTERNAL = 0, + SIDO_SOURCE_RCO_BG, +}; + +enum wcd9335_sido_voltage { + SIDO_VOLTAGE_SVS_MV = 950, + SIDO_VOLTAGE_NOMINAL_MV = 1100, +}; + +enum { + COMPANDER_1, /* HPH_L */ + COMPANDER_2, /* HPH_R */ + COMPANDER_3, /* LO1_DIFF */ + COMPANDER_4, /* LO2_DIFF */ + COMPANDER_5, /* LO3_SE */ + COMPANDER_6, /* LO4_SE */ + COMPANDER_7, /* SWR SPK CH1 */ + COMPANDER_8, /* SWR SPK CH2 */ + COMPANDER_MAX, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, + INTn_2_INP_SEL_RX6, + INTn_2_INP_SEL_RX7, + INTn_2_INP_SEL_PROXIMITY, +}; + +enum { + INTn_1_MIX_INP_SEL_ZERO = 0, + INTn_1_MIX_INP_SEL_DEC0, + INTn_1_MIX_INP_SEL_DEC1, + INTn_1_MIX_INP_SEL_IIR0, + INTn_1_MIX_INP_SEL_IIR1, + INTn_1_MIX_INP_SEL_RX0, + INTn_1_MIX_INP_SEL_RX1, + INTn_1_MIX_INP_SEL_RX2, + INTn_1_MIX_INP_SEL_RX3, + INTn_1_MIX_INP_SEL_RX4, + INTn_1_MIX_INP_SEL_RX5, + INTn_1_MIX_INP_SEL_RX6, + INTn_1_MIX_INP_SEL_RX7, + +}; + +enum { + INTERP_EAR = 0, + INTERP_HPHL, + INTERP_HPHR, + INTERP_LO1, + INTERP_LO2, + INTERP_LO3, + INTERP_LO4, + INTERP_SPKR1, + INTERP_SPKR2, +}; + +enum wcd_clock_type { + WCD_CLK_OFF, + WCD_CLK_RCO, + WCD_CLK_MCLK, +}; + +enum { + MIC_BIAS_1 = 1, + MIC_BIAS_2, + MIC_BIAS_3, + MIC_BIAS_4 +}; + +enum { + MICB_PULLUP_ENABLE, + MICB_PULLUP_DISABLE, + MICB_ENABLE, + MICB_DISABLE, +}; + +struct wcd9335_slim_ch { + u32 ch_num; + u16 port; + u16 shift; + struct list_head list; +}; + +struct wcd_slim_codec_dai_data { + struct list_head slim_ch_list; + struct slim_stream_config sconfig; + struct slim_stream_runtime *sruntime; +}; + +struct wcd9335_codec { + struct device *dev; + struct clk *mclk; + struct clk *native_clk; + u32 mclk_rate; + u8 version; + + struct slim_device *slim; + struct slim_device *slim_ifc_dev; + struct regmap *regmap; + struct regmap *if_regmap; + struct regmap_irq_chip_data *irq_data; + + struct wcd9335_slim_ch rx_chs[WCD9335_RX_MAX]; + struct wcd9335_slim_ch tx_chs[WCD9335_TX_MAX]; + u32 num_rx_port; + u32 num_tx_port; + + int sido_input_src; + enum wcd9335_sido_voltage sido_voltage; + + struct wcd_slim_codec_dai_data dai[NUM_CODEC_DAIS]; + struct snd_soc_component *component; + + int master_bias_users; + int clk_mclk_users; + int clk_rco_users; + int sido_ccl_cnt; + enum wcd_clock_type clk_type; + + struct wcd_clsh_ctrl *clsh_ctrl; + u32 hph_mode; + int prim_int_users[WCD9335_NUM_INTERPOLATORS]; + + int comp_enabled[COMPANDER_MAX]; + + int intr1; + int reset_gpio; + struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY]; + + unsigned int rx_port_value[WCD9335_RX_MAX]; + unsigned int tx_port_value[WCD9335_TX_MAX]; + int hph_l_gain; + int hph_r_gain; + u32 rx_bias_count; + + /*TX*/ + int micb_ref[WCD9335_MAX_MICBIAS]; + int pullup_ref[WCD9335_MAX_MICBIAS]; + + int dmic_0_1_clk_cnt; + int dmic_2_3_clk_cnt; + int dmic_4_5_clk_cnt; + int dmic_sample_rate; + int mad_dmic_sample_rate; + + int native_clk_users; +}; + +struct wcd9335_irq { + int irq; + irqreturn_t (*handler)(int irq, void *data); + char *name; +}; + +static const struct wcd9335_slim_ch wcd9335_tx_chs[WCD9335_TX_MAX] = { + WCD9335_SLIM_TX_CH(0), + WCD9335_SLIM_TX_CH(1), + WCD9335_SLIM_TX_CH(2), + WCD9335_SLIM_TX_CH(3), + WCD9335_SLIM_TX_CH(4), + WCD9335_SLIM_TX_CH(5), + WCD9335_SLIM_TX_CH(6), + WCD9335_SLIM_TX_CH(7), + WCD9335_SLIM_TX_CH(8), + WCD9335_SLIM_TX_CH(9), + WCD9335_SLIM_TX_CH(10), + WCD9335_SLIM_TX_CH(11), + WCD9335_SLIM_TX_CH(12), + WCD9335_SLIM_TX_CH(13), + WCD9335_SLIM_TX_CH(14), + WCD9335_SLIM_TX_CH(15), +}; + +static const struct wcd9335_slim_ch wcd9335_rx_chs[WCD9335_RX_MAX] = { + WCD9335_SLIM_RX_CH(0), /* 16 */ + WCD9335_SLIM_RX_CH(1), /* 17 */ + WCD9335_SLIM_RX_CH(2), + WCD9335_SLIM_RX_CH(3), + WCD9335_SLIM_RX_CH(4), + WCD9335_SLIM_RX_CH(5), + WCD9335_SLIM_RX_CH(6), + WCD9335_SLIM_RX_CH(7), + WCD9335_SLIM_RX_CH(8), + WCD9335_SLIM_RX_CH(9), + WCD9335_SLIM_RX_CH(10), + WCD9335_SLIM_RX_CH(11), + WCD9335_SLIM_RX_CH(12), +}; + +struct interp_sample_rate { + int rate; + int rate_val; +}; + +static struct interp_sample_rate int_mix_rate_val[] = { + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ +}; + +static struct interp_sample_rate int_prim_rate_val[] = { + {8000, 0x0}, /* 8K */ + {16000, 0x1}, /* 16K */ + {24000, -EINVAL},/* 24K */ + {32000, 0x3}, /* 32K */ + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ + {384000, 0x7}, /* 384K */ + {44100, 0x8}, /* 44.1K */ +}; + +struct wcd9335_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +static const struct wcd9335_reg_mask_val wcd9335_codec_reg_init[] = { + /* Rbuckfly/R_EAR(32) */ + {WCD9335_CDC_CLSH_K2_MSB, 0x0F, 0x00}, + {WCD9335_CDC_CLSH_K2_LSB, 0xFF, 0x60}, + {WCD9335_CPE_SS_DMIC_CFG, 0x80, 0x00}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x70, 0x50}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x70, 0x50}, + {WCD9335_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, + {WCD9335_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, + {WCD9335_ANA_LO_1_2, 0x3C, 0X3C}, + {WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x70, 0x00}, + {WCD9335_DIFF_LO_COM_PA_FREQ, 0x70, 0x40}, + {WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03, 0x03}, + {WCD9335_CDC_TOP_TOP_CFG1, 0x02, 0x02}, + {WCD9335_CDC_TOP_TOP_CFG1, 0x01, 0x01}, + {WCD9335_EAR_CMBUFF, 0x08, 0x00}, + {WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD9335_CDC_RX0_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX1_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX2_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX4_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX5_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX6_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX7_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX8_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_VBADC_IBIAS_FE, 0x0C, 0x08}, + {WCD9335_RCO_CTRL_2, 0x0F, 0x08}, + {WCD9335_RX_BIAS_FLYB_MID_RST, 0xF0, 0x10}, + {WCD9335_FLYBACK_CTRL_1, 0x20, 0x20}, + {WCD9335_HPH_OCP_CTL, 0xFF, 0x5A}, + {WCD9335_HPH_L_TEST, 0x01, 0x01}, + {WCD9335_HPH_R_TEST, 0x01, 0x01}, + {WCD9335_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {WCD9335_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x1E, 0x18}, + {WCD9335_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {WCD9335_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x1E, 0x18}, + {WCD9335_CDC_TX0_TX_PATH_SEC7, 0xFF, 0x45}, + {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_HPH_REFBUFF_LP_CTL, 0x08, 0x08}, + {WCD9335_HPH_REFBUFF_LP_CTL, 0x06, 0x02}, +}; + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +static const char * const rx_int0_7_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7", "PROXIMITY" +}; + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_interp_mux_text[] = { + "ZERO", "RX INT0 MIX2", +}; + +static const char * const rx_int1_interp_mux_text[] = { + "ZERO", "RX INT1 MIX2", +}; + +static const char * const rx_int2_interp_mux_text[] = { + "ZERO", "RX INT2 MIX2", +}; + +static const char * const rx_int3_interp_mux_text[] = { + "ZERO", "RX INT3 MIX2", +}; + +static const char * const rx_int4_interp_mux_text[] = { + "ZERO", "RX INT4 MIX2", +}; + +static const char * const rx_int5_interp_mux_text[] = { + "ZERO", "RX INT5 MIX2", +}; + +static const char * const rx_int6_interp_mux_text[] = { + "ZERO", "RX INT6 MIX2", +}; + +static const char * const rx_int7_interp_mux_text[] = { + "ZERO", "RX INT7 MIX2", +}; + +static const char * const rx_int8_interp_mux_text[] = { + "ZERO", "RX INT8 SEC MIX" +}; + +static const char * const rx_hph_mode_mux_text[] = { + "Class H Invalid", "Class-H Hi-Fi", "Class-H Low Power", "Class-AB", + "Class-H Hi-Fi Low Power" +}; + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB", +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" +}; + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", + "SMIC0", "SMIC1", "SMIC2", "SMIC3" +}; + +static const char * const dmic_mux_alt_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", +}; + +static const char * const amic_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6" +}; + +static const char * const sb_tx0_mux_text[] = { + "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192" +}; + +static const char * const sb_tx1_mux_text[] = { + "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192" +}; + +static const char * const sb_tx2_mux_text[] = { + "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192" +}; + +static const char * const sb_tx3_mux_text[] = { + "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192" +}; + +static const char * const sb_tx4_mux_text[] = { + "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192" +}; + +static const char * const sb_tx5_mux_text[] = { + "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192" +}; + +static const char * const sb_tx6_mux_text[] = { + "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192" +}; + +static const char * const sb_tx7_mux_text[] = { + "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192" +}; + +static const char * const sb_tx8_mux_text[] = { + "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192" +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); +static const DECLARE_TLV_DB_SCALE(ear_pa_gain, 0, 150, 0); + +static const struct soc_enum cf_dec0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_int0_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int1_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int2_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int3_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int4_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int5_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int5_2_enum, WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int6_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int6_2_enum, WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int7_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int8_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const struct soc_enum slim_rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text); + +static const struct soc_enum rx_int0_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int1_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int2_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int3_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int4_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int5_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int6_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int7_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int8_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int0_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int1_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int2_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int0_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CTL, 5, 2, + rx_int0_interp_mux_text); + +static const struct soc_enum rx_int1_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CTL, 5, 2, + rx_int1_interp_mux_text); + +static const struct soc_enum rx_int2_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CTL, 5, 2, + rx_int2_interp_mux_text); + +static const struct soc_enum rx_int3_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CTL, 5, 2, + rx_int3_interp_mux_text); + +static const struct soc_enum rx_int4_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CTL, 5, 2, + rx_int4_interp_mux_text); + +static const struct soc_enum rx_int5_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CTL, 5, 2, + rx_int5_interp_mux_text); + +static const struct soc_enum rx_int6_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CTL, 5, 2, + rx_int6_interp_mux_text); + +static const struct soc_enum rx_int7_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CTL, 5, 2, + rx_int7_interp_mux_text); + +static const struct soc_enum rx_int8_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CTL, 5, 2, + rx_int8_interp_mux_text); + +static const struct soc_enum tx_adc_mux0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux3_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux4_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux5_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux6_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux7_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux8_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_dmic_mux0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_amic_mux0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum sb_tx0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 0, 4, + sb_tx0_mux_text); + +static const struct soc_enum sb_tx1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 2, 4, + sb_tx1_mux_text); + +static const struct soc_enum sb_tx2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 4, 4, + sb_tx2_mux_text); + +static const struct soc_enum sb_tx3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 6, 4, + sb_tx3_mux_text); + +static const struct soc_enum sb_tx4_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 0, 4, + sb_tx4_mux_text); + +static const struct soc_enum sb_tx5_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 2, 4, + sb_tx5_mux_text); + +static const struct soc_enum sb_tx6_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 4, 4, + sb_tx6_mux_text); + +static const struct soc_enum sb_tx7_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 6, 4, + sb_tx7_mux_text); + +static const struct soc_enum sb_tx8_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 0, 4, + sb_tx8_mux_text); + +static const struct snd_kcontrol_new rx_int0_2_mux = + SOC_DAPM_ENUM("RX INT0_2 MUX Mux", rx_int0_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int1_2_mux = + SOC_DAPM_ENUM("RX INT1_2 MUX Mux", rx_int1_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int2_2_mux = + SOC_DAPM_ENUM("RX INT2_2 MUX Mux", rx_int2_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int3_2_mux = + SOC_DAPM_ENUM("RX INT3_2 MUX Mux", rx_int3_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int4_2_mux = + SOC_DAPM_ENUM("RX INT4_2 MUX Mux", rx_int4_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int5_2_mux = + SOC_DAPM_ENUM("RX INT5_2 MUX Mux", rx_int5_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int6_2_mux = + SOC_DAPM_ENUM("RX INT6_2 MUX Mux", rx_int6_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int7_2_mux = + SOC_DAPM_ENUM("RX INT7_2 MUX Mux", rx_int7_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int8_2_mux = + SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP1 Mux", rx_int0_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP2 Mux", rx_int0_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP0 Mux", rx_int1_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP1 Mux", rx_int1_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP2 Mux", rx_int1_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP0 Mux", rx_int2_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP1 Mux", rx_int2_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP2 Mux", rx_int2_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP0 Mux", rx_int3_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP1 Mux", rx_int3_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP2 Mux", rx_int3_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP0 Mux", rx_int4_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP1 Mux", rx_int4_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP2 Mux", rx_int4_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP0 Mux", rx_int5_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP1 Mux", rx_int5_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP2 Mux", rx_int5_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP0 Mux", rx_int6_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP1 Mux", rx_int6_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP2 Mux", rx_int6_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP0 Mux", rx_int7_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP1 Mux", rx_int7_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP2 Mux", rx_int7_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP0 Mux", rx_int8_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP1 Mux", rx_int8_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP2 Mux", rx_int8_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int0_interp_mux = + SOC_DAPM_ENUM("RX INT0 INTERP Mux", rx_int0_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_interp_mux = + SOC_DAPM_ENUM("RX INT1 INTERP Mux", rx_int1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_interp_mux = + SOC_DAPM_ENUM("RX INT2 INTERP Mux", rx_int2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_interp_mux = + SOC_DAPM_ENUM("RX INT3 INTERP Mux", rx_int3_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_interp_mux = + SOC_DAPM_ENUM("RX INT4 INTERP Mux", rx_int4_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int5_interp_mux = + SOC_DAPM_ENUM("RX INT5 INTERP Mux", rx_int5_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int6_interp_mux = + SOC_DAPM_ENUM("RX INT6 INTERP Mux", rx_int6_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_interp_mux = + SOC_DAPM_ENUM("RX INT7 INTERP Mux", rx_int7_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int8_interp_mux = + SOC_DAPM_ENUM("RX INT8 INTERP Mux", rx_int8_interp_mux_enum); + +static const struct snd_kcontrol_new tx_dmic_mux0 = + SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum); + +static const struct snd_kcontrol_new tx_dmic_mux1 = + SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum); + +static const struct snd_kcontrol_new tx_dmic_mux2 = + SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum); + +static const struct snd_kcontrol_new tx_dmic_mux3 = + SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum); + +static const struct snd_kcontrol_new tx_dmic_mux4 = + SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum); + +static const struct snd_kcontrol_new tx_dmic_mux5 = + SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum); + +static const struct snd_kcontrol_new tx_dmic_mux6 = + SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum); + +static const struct snd_kcontrol_new tx_dmic_mux7 = + SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum); + +static const struct snd_kcontrol_new tx_dmic_mux8 = + SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum); + +static const struct snd_kcontrol_new tx_amic_mux0 = + SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum); + +static const struct snd_kcontrol_new tx_amic_mux1 = + SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum); + +static const struct snd_kcontrol_new tx_amic_mux2 = + SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum); + +static const struct snd_kcontrol_new tx_amic_mux3 = + SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum); + +static const struct snd_kcontrol_new tx_amic_mux4 = + SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum); + +static const struct snd_kcontrol_new tx_amic_mux5 = + SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum); + +static const struct snd_kcontrol_new tx_amic_mux6 = + SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum); + +static const struct snd_kcontrol_new tx_amic_mux7 = + SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum); + +static const struct snd_kcontrol_new tx_amic_mux8 = + SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum); + +static const struct snd_kcontrol_new sb_tx0_mux = + SOC_DAPM_ENUM("SLIM TX0 MUX Mux", sb_tx0_mux_enum); + +static const struct snd_kcontrol_new sb_tx1_mux = + SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum); + +static const struct snd_kcontrol_new sb_tx2_mux = + SOC_DAPM_ENUM("SLIM TX2 MUX Mux", sb_tx2_mux_enum); + +static const struct snd_kcontrol_new sb_tx3_mux = + SOC_DAPM_ENUM("SLIM TX3 MUX Mux", sb_tx3_mux_enum); + +static const struct snd_kcontrol_new sb_tx4_mux = + SOC_DAPM_ENUM("SLIM TX4 MUX Mux", sb_tx4_mux_enum); + +static const struct snd_kcontrol_new sb_tx5_mux = + SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum); + +static const struct snd_kcontrol_new sb_tx6_mux = + SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum); + +static const struct snd_kcontrol_new sb_tx7_mux = + SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum); + +static const struct snd_kcontrol_new sb_tx8_mux = + SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum); + +static int slim_rx_mux_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(w->dapm->dev); + u32 port_id = w->shift; + + ucontrol->value.enumerated.item[0] = wcd->rx_port_value[port_id]; + + return 0; +} + +static int slim_rx_mux_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(w->dapm->dev); + struct soc_enum *e = (struct soc_enum *)kc->private_value; + struct snd_soc_dapm_update *update = NULL; + u32 port_id = w->shift; + + if (wcd->rx_port_value[port_id] == ucontrol->value.enumerated.item[0]) + return 0; + + wcd->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + + /* Remove channel from any list it's in before adding it to a new one */ + list_del_init(&wcd->rx_chs[port_id].list); + + switch (wcd->rx_port_value[port_id]) { + case 0: + /* Channel already removed from lists. Nothing to do here */ + break; + case 1: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF1_PB].slim_ch_list); + break; + case 2: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF2_PB].slim_ch_list); + break; + case 3: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF3_PB].slim_ch_list); + break; + case 4: + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[AIF4_PB].slim_ch_list); + break; + default: + dev_err(wcd->dev, "Unknown AIF %d\n", wcd->rx_port_value[port_id]); + goto err; + } + + snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id], + e, update); + + return 0; +err: + return -EINVAL; +} + +static int slim_tx_mixer_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(dapm->dev); + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kc); + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kc->private_value; + int dai_id = widget->shift; + int port_id = mixer->shift; + + ucontrol->value.integer.value[0] = wcd->tx_port_value[port_id] == dai_id; + + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(widget->dapm->dev); + struct snd_soc_dapm_update *update = NULL; + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kc->private_value; + int enable = ucontrol->value.integer.value[0]; + int dai_id = widget->shift; + int port_id = mixer->shift; + + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && wcd->tx_port_value[port_id] != dai_id) { + wcd->tx_port_value[port_id] = dai_id; + list_add_tail(&wcd->tx_chs[port_id].list, + &wcd->dai[dai_id].slim_ch_list); + } else if (!enable && wcd->tx_port_value[port_id] == dai_id) { + wcd->tx_port_value[port_id] = -1; + list_del_init(&wcd->tx_chs[port_id].list); + } + break; + default: + dev_err(wcd->dev, "Unknown AIF %d\n", dai_id); + return -EINVAL; + } + + snd_soc_dapm_mixer_update_power(widget->dapm, kc, enable, update); + + return 0; +} + +static const struct snd_kcontrol_new slim_rx_mux[WCD9335_RX_MAX] = { + SOC_DAPM_ENUM_EXT("SLIM RX0 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), +}; + +static const struct snd_kcontrol_new aif1_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD9335_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9335_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9335_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9335_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9335_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9335_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9335_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9335_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9335_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD9335_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD9335_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD9335_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD9335_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD9335_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9335_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9335_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9335_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9335_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9335_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9335_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9335_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9335_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD9335_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD9335_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD9335_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD9335_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD9335_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD9335_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD9335_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD9335_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD9335_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD9335_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD9335_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD9335_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD9335_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static int wcd9335_put_dec_enum(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + struct soc_enum *e = (struct soc_enum *)kc->private_value; + unsigned int val, reg, sel; + + val = ucontrol->value.enumerated.item[0]; + + switch (e->reg) { + case WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + reg = WCD9335_CDC_TX0_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + reg = WCD9335_CDC_TX1_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + reg = WCD9335_CDC_TX2_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + reg = WCD9335_CDC_TX3_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0: + reg = WCD9335_CDC_TX4_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0: + reg = WCD9335_CDC_TX5_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0: + reg = WCD9335_CDC_TX6_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0: + reg = WCD9335_CDC_TX7_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0: + reg = WCD9335_CDC_TX8_TX_PATH_CFG0; + break; + default: + return -EINVAL; + } + + /* AMIC: 0, DMIC: 1 */ + sel = val ? WCD9335_CDC_TX_ADC_AMIC_SEL : WCD9335_CDC_TX_ADC_DMIC_SEL; + snd_soc_component_update_bits(component, reg, + WCD9335_CDC_TX_ADC_AMIC_DMIC_SEL_MASK, + sel); + + return snd_soc_dapm_put_enum_double(kc, ucontrol); +} + +static int wcd9335_int_dem_inp_mux_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kc->private_value; + struct snd_soc_component *component; + int reg, val; + + component = snd_soc_dapm_kcontrol_component(kc); + val = ucontrol->value.enumerated.item[0]; + + if (e->reg == WCD9335_CDC_RX0_RX_PATH_SEC0) + reg = WCD9335_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD9335_CDC_RX1_RX_PATH_SEC0) + reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD9335_CDC_RX2_RX_PATH_SEC0) + reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + else + return -EINVAL; + + /* Set Look Ahead Delay */ + snd_soc_component_update_bits(component, reg, + WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN_MASK, + val ? WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN : 0); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kc, ucontrol); +} + +static const struct snd_kcontrol_new rx_int0_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT0 DEM MUX Mux", rx_int0_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd9335_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int1_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT1 DEM MUX Mux", rx_int1_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd9335_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int2_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT2 DEM MUX Mux", rx_int2_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd9335_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new tx_adc_mux0 = + SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_chain_enum, + snd_soc_dapm_get_enum_double, + wcd9335_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux1 = + SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_chain_enum, + snd_soc_dapm_get_enum_double, + wcd9335_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux2 = + SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_chain_enum, + snd_soc_dapm_get_enum_double, + wcd9335_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux3 = + SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_chain_enum, + snd_soc_dapm_get_enum_double, + wcd9335_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux4 = + SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_chain_enum, + snd_soc_dapm_get_enum_double, + wcd9335_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux5 = + SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_chain_enum, + snd_soc_dapm_get_enum_double, + wcd9335_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux6 = + SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_chain_enum, + snd_soc_dapm_get_enum_double, + wcd9335_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux7 = + SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_chain_enum, + snd_soc_dapm_get_enum_double, + wcd9335_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux8 = + SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_chain_enum, + snd_soc_dapm_get_enum_double, + wcd9335_put_dec_enum); + +static int wcd9335_set_mix_interpolator_rate(struct snd_soc_dai *dai, + int rate_val, + u32 rate) +{ + struct snd_soc_component *component = dai->component; + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + struct wcd9335_slim_ch *ch; + int val, j; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + for (j = 0; j < WCD9335_NUM_INTERPOLATORS; j++) { + val = snd_soc_component_read(component, + WCD9335_CDC_RX_INP_MUX_RX_INT_CFG1(j)) & + WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + + if (val == (ch->shift + INTn_2_INP_SEL_RX0)) + snd_soc_component_update_bits(component, + WCD9335_CDC_RX_PATH_MIX_CTL(j), + WCD9335_CDC_MIX_PCM_RATE_MASK, + rate_val); + } + } + + return 0; +} + +static int wcd9335_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_val, + u32 rate) +{ + struct snd_soc_component *comp = dai->component; + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + struct wcd9335_slim_ch *ch; + u8 cfg0, cfg1, inp0_sel, inp1_sel, inp2_sel; + int inp, j; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + inp = ch->shift + INTn_1_MIX_INP_SEL_RX0; + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < WCD9335_NUM_INTERPOLATORS; j++) { + cfg0 = snd_soc_component_read(comp, + WCD9335_CDC_RX_INP_MUX_RX_INT_CFG0(j)); + cfg1 = snd_soc_component_read(comp, + WCD9335_CDC_RX_INP_MUX_RX_INT_CFG1(j)); + + inp0_sel = cfg0 & + WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + inp1_sel = (cfg0 >> 4) & + WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + inp2_sel = (cfg1 >> 4) & + WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + + if ((inp0_sel == inp) || (inp1_sel == inp) || + (inp2_sel == inp)) { + /* rate is in Hz */ + if ((j == 0) && (rate == 44100)) + dev_info(wcd->dev, + "Cannot set 44.1KHz on INT0\n"); + else + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX_PATH_CTL(j), + WCD9335_CDC_MIX_PCM_RATE_MASK, + rate_val); + } + } + } + + return 0; +} + +static int wcd9335_set_interpolator_rate(struct snd_soc_dai *dai, u32 rate) +{ + int i; + + /* set mixing path rate */ + for (i = 0; i < ARRAY_SIZE(int_mix_rate_val); i++) { + if (rate == int_mix_rate_val[i].rate) { + wcd9335_set_mix_interpolator_rate(dai, + int_mix_rate_val[i].rate_val, rate); + break; + } + } + + /* set primary path sample rate */ + for (i = 0; i < ARRAY_SIZE(int_prim_rate_val); i++) { + if (rate == int_prim_rate_val[i].rate) { + wcd9335_set_prim_interpolator_rate(dai, + int_prim_rate_val[i].rate_val, rate); + break; + } + } + + return 0; +} + +static int wcd9335_slim_set_hw_params(struct wcd9335_codec *wcd, + struct wcd_slim_codec_dai_data *dai_data, + int direction) +{ + struct list_head *slim_ch_list = &dai_data->slim_ch_list; + struct slim_stream_config *cfg = &dai_data->sconfig; + struct wcd9335_slim_ch *ch; + u16 payload = 0; + int ret, i; + + cfg->ch_count = 0; + cfg->direction = direction; + cfg->port_mask = 0; + + /* Configure slave interface device */ + list_for_each_entry(ch, slim_ch_list, list) { + cfg->ch_count++; + payload |= 1 << ch->shift; + cfg->port_mask |= BIT(ch->port); + } + + cfg->chs = kcalloc(cfg->ch_count, sizeof(unsigned int), GFP_KERNEL); + if (!cfg->chs) + return -ENOMEM; + + i = 0; + list_for_each_entry(ch, slim_ch_list, list) { + cfg->chs[i++] = ch->ch_num; + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + /* write to interface device */ + ret = regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_RX_PORT_MULTI_CHNL_0(ch->port), + payload); + + if (ret < 0) + goto err; + + /* configure the slave port for water mark and enable*/ + ret = regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_RX_PORT_CFG(ch->port), + WCD9335_SLIM_WATER_MARK_VAL); + if (ret < 0) + goto err; + } else { + ret = regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_0(ch->port), + payload & 0x00FF); + if (ret < 0) + goto err; + + /* ports 8,9 */ + ret = regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_1(ch->port), + (payload & 0xFF00)>>8); + if (ret < 0) + goto err; + + /* configure the slave port for water mark and enable*/ + ret = regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_TX_PORT_CFG(ch->port), + WCD9335_SLIM_WATER_MARK_VAL); + + if (ret < 0) + goto err; + } + } + + dai_data->sruntime = slim_stream_allocate(wcd->slim, "WCD9335-SLIM"); + + return 0; + +err: + dev_err(wcd->dev, "Error Setting slim hw params\n"); + kfree(cfg->chs); + cfg->chs = NULL; + + return ret; +} + +static int wcd9335_set_decimator_rate(struct snd_soc_dai *dai, + u8 rate_val, u32 rate) +{ + struct snd_soc_component *comp = dai->component; + struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(comp); + u8 shift = 0, shift_val = 0, tx_mux_sel; + struct wcd9335_slim_ch *ch; + int tx_port, tx_port_reg; + int decimator = -1; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + tx_port = ch->port; + if ((tx_port == 12) || (tx_port >= 14)) { + dev_err(wcd->dev, "Invalid SLIM TX%u port DAI ID:%d\n", + tx_port, dai->id); + return -EINVAL; + } + /* Find the SB TX MUX input - which decimator is connected */ + if (tx_port < 4) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + } else if (tx_port < 8) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + } else if (tx_port < 11) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + } else if (tx_port == 11) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + } else /* (tx_port == 13) */ { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + } + + tx_mux_sel = snd_soc_component_read(comp, tx_port_reg) & + (shift_val << shift); + + tx_mux_sel = tx_mux_sel >> shift; + if (tx_port <= 8) { + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + } else if (tx_port <= 10) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + } else if (tx_port == 11) { + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + } else if (tx_port == 13) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + } + + if (decimator >= 0) { + snd_soc_component_update_bits(comp, + WCD9335_CDC_TX_PATH_CTL(decimator), + WCD9335_CDC_TX_PATH_CTL_PCM_RATE_MASK, + rate_val); + } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) { + /* Check if the TX Mux input is RX MIX TXn */ + dev_err(wcd->dev, "RX_MIX_TX%u going to SLIM TX%u\n", + tx_port, tx_port); + } else { + dev_err(wcd->dev, "ERROR: Invalid decimator: %d\n", + decimator); + return -EINVAL; + } + } + + return 0; +} + +static int wcd9335_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd9335_codec *wcd; + int ret, tx_fs_rate = 0; + + wcd = snd_soc_component_get_drvdata(dai->component); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = wcd9335_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(wcd->dev, "cannot set sample rate: %u\n", + params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16 ... 24: + wcd->dai[dai->id].sconfig.bps = params_width(params); + break; + default: + dev_err(wcd->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + } + break; + + case SNDRV_PCM_STREAM_CAPTURE: + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(wcd->dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + + } + + ret = wcd9335_set_decimator_rate(dai, tx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(wcd->dev, "Cannot set TX Decimator rate\n"); + return ret; + } + switch (params_width(params)) { + case 16 ... 32: + wcd->dai[dai->id].sconfig.bps = params_width(params); + break; + default: + dev_err(wcd->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + } + break; + default: + dev_err(wcd->dev, "Invalid stream type %d\n", + substream->stream); + return -EINVAL; + } + + wcd->dai[dai->id].sconfig.rate = params_rate(params); + wcd9335_slim_set_hw_params(wcd, &wcd->dai[dai->id], substream->stream); + + return 0; +} + +static int wcd9335_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct wcd_slim_codec_dai_data *dai_data; + struct wcd9335_codec *wcd; + struct slim_stream_config *cfg; + + wcd = snd_soc_component_get_drvdata(dai->component); + + dai_data = &wcd->dai[dai->id]; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cfg = &dai_data->sconfig; + slim_stream_prepare(dai_data->sruntime, cfg); + slim_stream_enable(dai_data->sruntime); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + slim_stream_disable(dai_data->sruntime); + slim_stream_unprepare(dai_data->sruntime); + break; + default: + break; + } + + return 0; +} + +static int wcd9335_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct wcd9335_codec *wcd; + int i; + + wcd = snd_soc_component_get_drvdata(dai->component); + + if (!tx_slot || !rx_slot) { + dev_err(wcd->dev, "Invalid tx_slot=%p, rx_slot=%p\n", + tx_slot, rx_slot); + return -EINVAL; + } + + wcd->num_rx_port = rx_num; + for (i = 0; i < rx_num; i++) { + wcd->rx_chs[i].ch_num = rx_slot[i]; + INIT_LIST_HEAD(&wcd->rx_chs[i].list); + } + + wcd->num_tx_port = tx_num; + for (i = 0; i < tx_num; i++) { + wcd->tx_chs[i].ch_num = tx_slot[i]; + INIT_LIST_HEAD(&wcd->tx_chs[i].list); + } + + return 0; +} + +static int wcd9335_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct wcd9335_slim_ch *ch; + struct wcd9335_codec *wcd; + int i = 0; + + wcd = snd_soc_component_get_drvdata(dai->component); + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + if (!rx_slot || !rx_num) { + dev_err(wcd->dev, "Invalid rx_slot %p or rx_num %p\n", + rx_slot, rx_num); + return -EINVAL; + } + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) + rx_slot[i++] = ch->ch_num; + + *rx_num = i; + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + if (!tx_slot || !tx_num) { + dev_err(wcd->dev, "Invalid tx_slot %p or tx_num %p\n", + tx_slot, tx_num); + return -EINVAL; + } + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) + tx_slot[i++] = ch->ch_num; + + *tx_num = i; + break; + default: + dev_err(wcd->dev, "Invalid DAI ID %x\n", dai->id); + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops wcd9335_dai_ops = { + .hw_params = wcd9335_hw_params, + .trigger = wcd9335_trigger, + .set_channel_map = wcd9335_set_channel_map, + .get_channel_map = wcd9335_get_channel_map, +}; + +static struct snd_soc_dai_driver wcd9335_slim_dais[] = { + [0] = { + .name = "wcd9335_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK | + SNDRV_PCM_RATE_384000, + .formats = WCD9335_FORMATS_S16_S24_LE, + .rate_max = 384000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd9335_dai_ops, + }, + [1] = { + .name = "wcd9335_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9335_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd9335_dai_ops, + }, + [2] = { + .name = "wcd9335_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK | + SNDRV_PCM_RATE_384000, + .formats = WCD9335_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd9335_dai_ops, + }, + [3] = { + .name = "wcd9335_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9335_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd9335_dai_ops, + }, + [4] = { + .name = "wcd9335_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK | + SNDRV_PCM_RATE_384000, + .formats = WCD9335_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd9335_dai_ops, + }, + [5] = { + .name = "wcd9335_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD9335_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd9335_dai_ops, + }, + [6] = { + .name = "wcd9335_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK | + SNDRV_PCM_RATE_384000, + .formats = WCD9335_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd9335_dai_ops, + }, +}; + +static int wcd9335_get_compander(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + int comp = ((struct soc_mixer_control *)kc->private_value)->shift; + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + + ucontrol->value.integer.value[0] = wcd->comp_enabled[comp]; + return 0; +} + +static int wcd9335_set_compander(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int comp = ((struct soc_mixer_control *) kc->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + int sel; + + wcd->comp_enabled[comp] = value; + sel = value ? WCD9335_HPH_GAIN_SRC_SEL_COMPANDER : + WCD9335_HPH_GAIN_SRC_SEL_REGISTER; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_component_update_bits(component, WCD9335_HPH_L_EN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, sel); + break; + case COMPANDER_2: + snd_soc_component_update_bits(component, WCD9335_HPH_R_EN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, sel); + break; + case COMPANDER_5: + snd_soc_component_update_bits(component, WCD9335_SE_LO_LO3_GAIN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, sel); + break; + case COMPANDER_6: + snd_soc_component_update_bits(component, WCD9335_SE_LO_LO4_GAIN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, sel); + break; + default: + break; + } + + return 0; +} + +static int wcd9335_rx_hph_mode_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + + ucontrol->value.enumerated.item[0] = wcd->hph_mode; + + return 0; +} + +static int wcd9335_rx_hph_mode_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + if (mode_val == 0) { + dev_err(wcd->dev, "Invalid HPH Mode, default to ClSH HiFi\n"); + mode_val = CLS_H_HIFI; + } + wcd->hph_mode = mode_val; + + return 0; +} + +static const struct snd_kcontrol_new wcd9335_snd_controls[] = { + /* -84dB min - 40dB max */ + SOC_SINGLE_S8_TLV("RX0 Digital Volume", WCD9335_CDC_RX0_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX1 Digital Volume", WCD9335_CDC_RX1_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX2 Digital Volume", WCD9335_CDC_RX2_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX3 Digital Volume", WCD9335_CDC_RX3_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX4 Digital Volume", WCD9335_CDC_RX4_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX5 Digital Volume", WCD9335_CDC_RX5_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX6 Digital Volume", WCD9335_CDC_RX6_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX7 Digital Volume", WCD9335_CDC_RX7_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX8 Digital Volume", WCD9335_CDC_RX8_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX0 Mix Digital Volume", WCD9335_CDC_RX0_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX1 Mix Digital Volume", WCD9335_CDC_RX1_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX2 Mix Digital Volume", WCD9335_CDC_RX2_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX3 Mix Digital Volume", WCD9335_CDC_RX3_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX4 Mix Digital Volume", WCD9335_CDC_RX4_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX5 Mix Digital Volume", WCD9335_CDC_RX5_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX6 Mix Digital Volume", WCD9335_CDC_RX6_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX7 Mix Digital Volume", WCD9335_CDC_RX7_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX8 Mix Digital Volume", WCD9335_CDC_RX8_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum), + SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum), + SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum), + SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum), + SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum), + SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum), + SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum), + SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum), + SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum), + SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum), + SOC_ENUM("RX INT5_1 HPF cut off", cf_int5_1_enum), + SOC_ENUM("RX INT5_2 HPF cut off", cf_int5_2_enum), + SOC_ENUM("RX INT6_1 HPF cut off", cf_int6_1_enum), + SOC_ENUM("RX INT6_2 HPF cut off", cf_int6_2_enum), + SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum), + SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum), + SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum), + SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum), + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP5 Switch", SND_SOC_NOPM, COMPANDER_5, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP6 Switch", SND_SOC_NOPM, COMPANDER_6, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + wcd9335_get_compander, wcd9335_set_compander), + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd9335_rx_hph_mode_get, wcd9335_rx_hph_mode_put), + + /* Gain Controls */ + SOC_SINGLE_TLV("EAR PA Volume", WCD9335_ANA_EAR, 4, 4, 1, + ear_pa_gain), + SOC_SINGLE_TLV("HPHL Volume", WCD9335_HPH_L_EN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD9335_HPH_R_EN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT1 Volume", WCD9335_DIFF_LO_LO1_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT2 Volume", WCD9335_DIFF_LO_LO2_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT3 Volume", WCD9335_SE_LO_LO3_GAIN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT4 Volume", WCD9335_SE_LO_LO4_GAIN, 0, 20, 1, + line_gain), + + SOC_SINGLE_TLV("ADC1 Volume", WCD9335_ANA_AMIC1, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD9335_ANA_AMIC2, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD9335_ANA_AMIC3, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD9335_ANA_AMIC4, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC5 Volume", WCD9335_ANA_AMIC5, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC6 Volume", WCD9335_ANA_AMIC6, 0, 20, 0, + analog_gain), + + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_dec5_enum), + SOC_ENUM("TX6 HPF cut off", cf_dec6_enum), + SOC_ENUM("TX7 HPF cut off", cf_dec7_enum), + SOC_ENUM("TX8 HPF cut off", cf_dec8_enum), +}; + +static const struct snd_soc_dapm_route wcd9335_audio_map[] = { + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"}, + + {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"}, + + {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"}, + + {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"}, + + {"SLIM RX0", NULL, "SLIM RX0 MUX"}, + {"SLIM RX1", NULL, "SLIM RX1 MUX"}, + {"SLIM RX2", NULL, "SLIM RX2 MUX"}, + {"SLIM RX3", NULL, "SLIM RX3 MUX"}, + {"SLIM RX4", NULL, "SLIM RX4 MUX"}, + {"SLIM RX5", NULL, "SLIM RX5 MUX"}, + {"SLIM RX6", NULL, "SLIM RX6 MUX"}, + {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + + WCD9335_INTERPOLATOR_PATH(0), + WCD9335_INTERPOLATOR_PATH(1), + WCD9335_INTERPOLATOR_PATH(2), + WCD9335_INTERPOLATOR_PATH(3), + WCD9335_INTERPOLATOR_PATH(4), + WCD9335_INTERPOLATOR_PATH(5), + WCD9335_INTERPOLATOR_PATH(6), + WCD9335_INTERPOLATOR_PATH(7), + WCD9335_INTERPOLATOR_PATH(8), + + /* EAR PA */ + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 INTERP"}, + {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, + {"RX INT0 DAC", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "RX INT0 DAC"}, + {"EAR", NULL, "EAR PA"}, + + /* HPHL */ + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 INTERP"}, + {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"}, + {"RX INT1 DAC", NULL, "RX_BIAS"}, + {"HPHL PA", NULL, "RX INT1 DAC"}, + {"HPHL", NULL, "HPHL PA"}, + + /* HPHR */ + {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 INTERP"}, + {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"}, + {"RX INT2 DAC", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "RX INT2 DAC"}, + {"HPHR", NULL, "HPHR PA"}, + + /* LINEOUT1 */ + {"RX INT3 DAC", NULL, "RX INT3 INTERP"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + /* LINEOUT2 */ + {"RX INT4 DAC", NULL, "RX INT4 INTERP"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + /* LINEOUT3 */ + {"RX INT5 DAC", NULL, "RX INT5 INTERP"}, + {"RX INT5 DAC", NULL, "RX_BIAS"}, + {"LINEOUT3 PA", NULL, "RX INT5 DAC"}, + {"LINEOUT3", NULL, "LINEOUT3 PA"}, + + /* LINEOUT4 */ + {"RX INT6 DAC", NULL, "RX INT6 INTERP"}, + {"RX INT6 DAC", NULL, "RX_BIAS"}, + {"LINEOUT4 PA", NULL, "RX INT6 DAC"}, + {"LINEOUT4", NULL, "LINEOUT4 PA"}, + + /* SLIMBUS Connections */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + /* ADC Mux */ + WCD9335_ADC_MUX_PATH(0), + WCD9335_ADC_MUX_PATH(1), + WCD9335_ADC_MUX_PATH(2), + WCD9335_ADC_MUX_PATH(3), + WCD9335_ADC_MUX_PATH(4), + WCD9335_ADC_MUX_PATH(5), + WCD9335_ADC_MUX_PATH(6), + WCD9335_ADC_MUX_PATH(7), + WCD9335_ADC_MUX_PATH(8), + + /* ADC Connections */ + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4"}, + {"ADC5", NULL, "AMIC5"}, + {"ADC6", NULL, "AMIC6"}, +}; + +static int wcd9335_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm) +{ + struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(component); + int micb_index = micb_num - 1; + u16 micb_reg; + + if ((micb_index < 0) || (micb_index > WCD9335_MAX_MICBIAS - 1)) { + dev_err(wcd->dev, "Invalid micbias index, micb_ind:%d\n", + micb_index); + return -EINVAL; + } + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD9335_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD9335_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD9335_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD9335_ANA_MICB4; + break; + default: + dev_err(component->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + + switch (req) { + case MICB_PULLUP_ENABLE: + wcd->pullup_ref[micb_index]++; + if ((wcd->pullup_ref[micb_index] == 1) && + (wcd->micb_ref[micb_index] == 0)) + snd_soc_component_update_bits(component, micb_reg, + 0xC0, 0x80); + break; + case MICB_PULLUP_DISABLE: + wcd->pullup_ref[micb_index]--; + if ((wcd->pullup_ref[micb_index] == 0) && + (wcd->micb_ref[micb_index] == 0)) + snd_soc_component_update_bits(component, micb_reg, + 0xC0, 0x00); + break; + case MICB_ENABLE: + wcd->micb_ref[micb_index]++; + if (wcd->micb_ref[micb_index] == 1) + snd_soc_component_update_bits(component, micb_reg, + 0xC0, 0x40); + break; + case MICB_DISABLE: + wcd->micb_ref[micb_index]--; + if ((wcd->micb_ref[micb_index] == 0) && + (wcd->pullup_ref[micb_index] > 0)) + snd_soc_component_update_bits(component, micb_reg, + 0xC0, 0x80); + else if ((wcd->micb_ref[micb_index] == 0) && + (wcd->pullup_ref[micb_index] == 0)) { + snd_soc_component_update_bits(component, micb_reg, + 0xC0, 0x00); + } + break; + } + + return 0; +} + +static int __wcd9335_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int micb_num; + + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) + micb_num = MIC_BIAS_4; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * MIC BIAS can also be requested by MBHC, + * so use ref count to handle micbias pullup + * and enable requests + */ + wcd9335_micbias_control(comp, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* wait for cnp time */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd9335_micbias_control(comp, micb_num, MICB_DISABLE, true); + break; + } + + return 0; +} + +static int wcd9335_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + return __wcd9335_codec_enable_micbias(w, event); +} + +static void wcd9335_codec_set_tx_hold(struct snd_soc_component *comp, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == WCD9335_ANA_AMIC1 || amic_reg == WCD9335_ANA_AMIC3 || + amic_reg == WCD9335_ANA_AMIC5) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case WCD9335_ANA_AMIC1: + case WCD9335_ANA_AMIC2: + snd_soc_component_update_bits(comp, WCD9335_ANA_AMIC2, mask, + val); + break; + case WCD9335_ANA_AMIC3: + case WCD9335_ANA_AMIC4: + snd_soc_component_update_bits(comp, WCD9335_ANA_AMIC4, mask, + val); + break; + case WCD9335_ANA_AMIC5: + case WCD9335_ANA_AMIC6: + snd_soc_component_update_bits(comp, WCD9335_ANA_AMIC6, mask, + val); + break; + default: + dev_err(comp->dev, "%s: invalid amic: %d\n", + __func__, amic_reg); + break; + } +} + +static int wcd9335_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd9335_codec_set_tx_hold(comp, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int wcd9335_codec_find_amic_input(struct snd_soc_component *comp, + int adc_mux_n) +{ + int mux_sel, reg, mreg; + + if (adc_mux_n < 0 || adc_mux_n > WCD9335_MAX_VALID_ADC_MUX || + adc_mux_n == WCD9335_INVALID_ADC_MUX) + return 0; + + /* Check whether adc mux input is AMIC or DMIC */ + if (adc_mux_n < 4) { + reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + 2 * adc_mux_n; + mreg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + 2 * adc_mux_n; + mux_sel = snd_soc_component_read(comp, reg) & 0x3; + } else { + reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + adc_mux_n - 4; + mreg = reg; + mux_sel = snd_soc_component_read(comp, reg) >> 6; + } + + if (mux_sel != WCD9335_CDC_TX_INP_MUX_SEL_AMIC) + return 0; + + return snd_soc_component_read(comp, mreg) & 0x07; +} + +static u16 wcd9335_codec_get_amic_pwlvl_reg(struct snd_soc_component *comp, + int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = WCD9335_ANA_AMIC1; + break; + + case 3: + case 4: + pwr_level_reg = WCD9335_ANA_AMIC3; + break; + + case 5: + case 6: + pwr_level_reg = WCD9335_ANA_AMIC5; + break; + default: + dev_err(comp->dev, "invalid amic: %d\n", amic); + break; + } + + return pwr_level_reg; +} + +static int wcd9335_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + unsigned int decimator; + char *dec_adc_mux_name = NULL; + char *widget_name = NULL; + char *wname; + int ret = 0, amic_n; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + char *dec; + u8 hpf_coff_freq; + + widget_name = kmemdup_nul(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + wname = widget_name; + dec_adc_mux_name = strsep(&widget_name, " "); + if (!dec_adc_mux_name) { + dev_err(comp->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + dec_adc_mux_name = widget_name; + + dec = strpbrk(dec_adc_mux_name, "012345678"); + if (!dec) { + dev_err(comp->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(comp->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD9335_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + amic_n = wcd9335_codec_find_amic_input(comp, decimator); + if (amic_n) + pwr_level_reg = wcd9335_codec_get_amic_pwlvl_reg(comp, + amic_n); + + if (pwr_level_reg) { + switch ((snd_soc_component_read(comp, pwr_level_reg) & + WCD9335_AMIC_PWR_LVL_MASK) >> + WCD9335_AMIC_PWR_LVL_SHIFT) { + case WCD9335_AMIC_PWR_LEVEL_LP: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_LP); + break; + + case WCD9335_AMIC_PWR_LEVEL_HP: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_HP); + break; + case WCD9335_AMIC_PWR_LEVEL_DEFAULT: + default: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_DF); + break; + } + } + hpf_coff_freq = (snd_soc_component_read(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + if (hpf_coff_freq != CF_MIN_3DB_150HZ) + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + /* Enable TX PGA Mute */ + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, + 0x10, 0x10); + /* Enable APC */ + snd_soc_component_update_bits(comp, dec_cfg_reg, 0x08, 0x08); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(comp, hpf_gate_reg, 0x01, 0x00); + + if (decimator == 0) { + snd_soc_component_write(comp, + WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); + snd_soc_component_write(comp, + WCD9335_MBHC_ZDET_RAMP_CTL, 0xA3); + snd_soc_component_write(comp, + WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); + snd_soc_component_write(comp, + WCD9335_MBHC_ZDET_RAMP_CTL, 0x03); + } + + snd_soc_component_update_bits(comp, hpf_gate_reg, + 0x01, 0x01); + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, + 0x10, 0x00); + snd_soc_component_write(comp, tx_gain_ctl_reg, + snd_soc_component_read(comp, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_coff_freq = (snd_soc_component_read(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x10); + snd_soc_component_update_bits(comp, dec_cfg_reg, 0x08, 0x00); + if (hpf_coff_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_coff_freq << 5); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, 0x10, 0x00); + break; + } +out: + kfree(wname); + return ret; +} + +static u8 wcd9335_get_dmic_clk_val(struct snd_soc_component *component, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + dev_err(component->dev, + "%s: mclk_rate = %d, dmic_sample_rate = %d\n", + __func__, mclk_rate, dmic_clk_rate); + + /* Default value to return in case of error */ + if (mclk_rate == WCD9335_MCLK_CLK_9P6MHZ) + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; + else + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; + + if (dmic_clk_rate == 0) { + dev_err(component->dev, + "%s: dmic_sample_rate cannot be 0\n", + __func__); + goto done; + } + + div_factor = mclk_rate / dmic_clk_rate; + switch (div_factor) { + case 2: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_6; + break; + case 8: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_8; + break; + case 16: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_16; + break; + default: + dev_err(component->dev, + "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", + __func__, div_factor, mclk_rate, dmic_clk_rate); + break; + } + +done: + return dmic_ctl_val; +} + +static int wcd9335_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(comp); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift = 1; + unsigned int dmic; + int ret; + char *wname; + + wname = strpbrk(w->name, "012345"); + if (!wname) { + dev_err(comp->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(comp->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(wcd->dmic_0_1_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(wcd->dmic_2_3_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(wcd->dmic_4_5_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(comp->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_rate_val = + wcd9335_get_dmic_clk_val(comp, + wcd->mclk_rate, + wcd->dmic_sample_rate); + + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_component_update_bits(comp, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + snd_soc_component_update_bits(comp, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + dmic_rate_val = + wcd9335_get_dmic_clk_val(comp, + wcd->mclk_rate, + wcd->mad_dmic_sample_rate); + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_component_update_bits(comp, dmic_clk_reg, + dmic_clk_en, 0); + snd_soc_component_update_bits(comp, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + } + break; + } + + return 0; +} + +static void wcd9335_codec_enable_int_port(struct wcd_slim_codec_dai_data *dai, + struct snd_soc_component *component) +{ + int port_num = 0; + unsigned short reg = 0; + unsigned int val = 0; + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + struct wcd9335_slim_ch *ch; + + list_for_each_entry(ch, &dai->slim_ch_list, list) { + if (ch->port >= WCD9335_RX_START) { + port_num = ch->port - WCD9335_RX_START; + reg = WCD9335_SLIM_PGD_PORT_INT_EN0 + (port_num / 8); + } else { + port_num = ch->port; + reg = WCD9335_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + } + + regmap_read(wcd->if_regmap, reg, &val); + if (!(val & BIT(port_num % 8))) + regmap_write(wcd->if_regmap, reg, + val | BIT(port_num % 8)); + } +} + +static int wcd9335_codec_enable_slim(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = snd_soc_component_get_drvdata(comp); + struct wcd_slim_codec_dai_data *dai = &wcd->dai[w->shift]; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wcd9335_codec_enable_int_port(dai, comp); + break; + case SND_SOC_DAPM_POST_PMD: + kfree(dai->sconfig.chs); + + break; + } + + return 0; +} + +static int wcd9335_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + int offset_val = 0; + int val = 0; + + switch (w->reg) { + case WCD9335_CDC_RX0_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX0_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX1_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX1_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX2_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX2_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX3_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX3_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX4_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX4_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX5_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX5_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX6_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX6_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX7_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX7_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX8_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX8_RX_VOL_MIX_CTL; + break; + default: + dev_err(comp->dev, "%s: No gain register avail for %s\n", + __func__, w->name); + return 0; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read(comp, gain_reg); + val += offset_val; + snd_soc_component_write(comp, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + break; + } + + return 0; +} + +static u16 wcd9335_interp_get_primary_reg(u16 reg, u16 *ind) +{ + u16 prim_int_reg = WCD9335_CDC_RX0_RX_PATH_CTL; + + switch (reg) { + case WCD9335_CDC_RX0_RX_PATH_CTL: + case WCD9335_CDC_RX0_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX0_RX_PATH_CTL; + *ind = 0; + break; + case WCD9335_CDC_RX1_RX_PATH_CTL: + case WCD9335_CDC_RX1_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX1_RX_PATH_CTL; + *ind = 1; + break; + case WCD9335_CDC_RX2_RX_PATH_CTL: + case WCD9335_CDC_RX2_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX2_RX_PATH_CTL; + *ind = 2; + break; + case WCD9335_CDC_RX3_RX_PATH_CTL: + case WCD9335_CDC_RX3_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + *ind = 3; + break; + case WCD9335_CDC_RX4_RX_PATH_CTL: + case WCD9335_CDC_RX4_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + *ind = 4; + break; + case WCD9335_CDC_RX5_RX_PATH_CTL: + case WCD9335_CDC_RX5_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + *ind = 5; + break; + case WCD9335_CDC_RX6_RX_PATH_CTL: + case WCD9335_CDC_RX6_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + *ind = 6; + break; + case WCD9335_CDC_RX7_RX_PATH_CTL: + case WCD9335_CDC_RX7_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX7_RX_PATH_CTL; + *ind = 7; + break; + case WCD9335_CDC_RX8_RX_PATH_CTL: + case WCD9335_CDC_RX8_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX8_RX_PATH_CTL; + *ind = 8; + break; + } + + return prim_int_reg; +} + +static void wcd9335_codec_hd2_control(struct snd_soc_component *component, + u16 prim_int_reg, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + if (prim_int_reg == WCD9335_CDC_RX1_RX_PATH_CTL) { + hd2_scale_reg = WCD9335_CDC_RX1_RX_PATH_SEC3; + hd2_enable_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + } + if (prim_int_reg == WCD9335_CDC_RX2_RX_PATH_CTL) { + hd2_scale_reg = WCD9335_CDC_RX2_RX_PATH_SEC3; + hd2_enable_reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_MASK, + WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P2500); + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_MASK, + WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_2); + snd_soc_component_update_bits(component, hd2_enable_reg, + WCD9335_CDC_RX_PATH_CFG_HD2_EN_MASK, + WCD9335_CDC_RX_PATH_CFG_HD2_ENABLE); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, hd2_enable_reg, + WCD9335_CDC_RX_PATH_CFG_HD2_EN_MASK, + WCD9335_CDC_RX_PATH_CFG_HD2_DISABLE); + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_MASK, + WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_1); + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_MASK, + WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000); + } +} + +static int wcd9335_codec_enable_prim_interpolator( + struct snd_soc_component *comp, + u16 reg, int event) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + u16 ind = 0; + int prim_int_reg = wcd9335_interp_get_primary_reg(reg, &ind); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd->prim_int_users[ind]++; + if (wcd->prim_int_users[ind] == 1) { + snd_soc_component_update_bits(comp, prim_int_reg, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_ENABLE); + wcd9335_codec_hd2_control(comp, prim_int_reg, event); + snd_soc_component_update_bits(comp, prim_int_reg, + WCD9335_CDC_RX_CLK_EN_MASK, + WCD9335_CDC_RX_CLK_ENABLE); + } + + if ((reg != prim_int_reg) && + ((snd_soc_component_read(comp, prim_int_reg)) & + WCD9335_CDC_RX_PGA_MUTE_EN_MASK)) + snd_soc_component_update_bits(comp, reg, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + wcd->prim_int_users[ind]--; + if (wcd->prim_int_users[ind] == 0) { + snd_soc_component_update_bits(comp, prim_int_reg, + WCD9335_CDC_RX_CLK_EN_MASK, + WCD9335_CDC_RX_CLK_DISABLE); + snd_soc_component_update_bits(comp, prim_int_reg, + WCD9335_CDC_RX_RESET_MASK, + WCD9335_CDC_RX_RESET_ENABLE); + snd_soc_component_update_bits(comp, prim_int_reg, + WCD9335_CDC_RX_RESET_MASK, + WCD9335_CDC_RX_RESET_DISABLE); + wcd9335_codec_hd2_control(comp, prim_int_reg, event); + } + break; + } + + return 0; +} + +static int wcd9335_config_compander(struct snd_soc_component *component, + int interp_n, int event) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + comp = interp_n - 1; + if (!wcd->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = WCD9335_CDC_COMPANDER1_CTL(comp); + rx_path_cfg0_reg = WCD9335_CDC_RX1_RX_PATH_CFG(comp); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_CLK_EN_MASK, + WCD9335_CDC_COMPANDER_CLK_ENABLE); + /* Reset comander */ + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_SOFT_RST_MASK, + WCD9335_CDC_COMPANDER_SOFT_RST_ENABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_SOFT_RST_MASK, + WCD9335_CDC_COMPANDER_SOFT_RST_DISABLE); + /* Enables DRE in this path */ + snd_soc_component_update_bits(component, rx_path_cfg0_reg, + WCD9335_CDC_RX_PATH_CFG_CMP_EN_MASK, + WCD9335_CDC_RX_PATH_CFG_CMP_ENABLE); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_HALT_MASK, + WCD9335_CDC_COMPANDER_HALT); + snd_soc_component_update_bits(component, rx_path_cfg0_reg, + WCD9335_CDC_RX_PATH_CFG_CMP_EN_MASK, + WCD9335_CDC_RX_PATH_CFG_CMP_DISABLE); + + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_SOFT_RST_MASK, + WCD9335_CDC_COMPANDER_SOFT_RST_ENABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_SOFT_RST_MASK, + WCD9335_CDC_COMPANDER_SOFT_RST_DISABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_CLK_EN_MASK, + WCD9335_CDC_COMPANDER_CLK_DISABLE); + snd_soc_component_update_bits(component, comp_ctl0_reg, + WCD9335_CDC_COMPANDER_HALT_MASK, + WCD9335_CDC_COMPANDER_NOHALT); + } + + return 0; +} + +static int wcd9335_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + if (!(strcmp(w->name, "RX INT0 INTERP"))) { + reg = WCD9335_CDC_RX0_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX0_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT1 INTERP"))) { + reg = WCD9335_CDC_RX1_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX1_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT2 INTERP"))) { + reg = WCD9335_CDC_RX2_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX2_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT3 INTERP"))) { + reg = WCD9335_CDC_RX3_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX3_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT4 INTERP"))) { + reg = WCD9335_CDC_RX4_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX4_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT5 INTERP"))) { + reg = WCD9335_CDC_RX5_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX5_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT6 INTERP"))) { + reg = WCD9335_CDC_RX6_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX6_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT7 INTERP"))) { + reg = WCD9335_CDC_RX7_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX7_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT8 INTERP"))) { + reg = WCD9335_CDC_RX8_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX8_RX_VOL_CTL; + } else { + dev_err(comp->dev, "%s: Interpolator reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Reset if needed */ + wcd9335_codec_enable_prim_interpolator(comp, reg, event); + break; + case SND_SOC_DAPM_POST_PMU: + wcd9335_config_compander(comp, w->shift, event); + val = snd_soc_component_read(comp, gain_reg); + val += offset_val; + snd_soc_component_write(comp, gain_reg, val); + break; + case SND_SOC_DAPM_POST_PMD: + wcd9335_config_compander(comp, w->shift, event); + wcd9335_codec_enable_prim_interpolator(comp, reg, event); + break; + } + + return 0; +} + +static void wcd9335_codec_hph_mode_gain_opt(struct snd_soc_component *component, + u8 gain) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + u8 hph_l_en, hph_r_en; + u8 l_val, r_val; + u8 hph_pa_status; + bool is_hphl_pa, is_hphr_pa; + + hph_pa_status = snd_soc_component_read(component, WCD9335_ANA_HPH); + is_hphl_pa = hph_pa_status >> 7; + is_hphr_pa = (hph_pa_status & 0x40) >> 6; + + hph_l_en = snd_soc_component_read(component, WCD9335_HPH_L_EN); + hph_r_en = snd_soc_component_read(component, WCD9335_HPH_R_EN); + + l_val = (hph_l_en & 0xC0) | 0x20 | gain; + r_val = (hph_r_en & 0xC0) | 0x20 | gain; + + /* + * Set HPH_L & HPH_R gain source selection to REGISTER + * for better click and pop only if corresponding PAs are + * not enabled. Also cache the values of the HPHL/R + * PA gains to be applied after PAs are enabled + */ + if ((l_val != hph_l_en) && !is_hphl_pa) { + snd_soc_component_write(component, WCD9335_HPH_L_EN, l_val); + wcd->hph_l_gain = hph_l_en & 0x1F; + } + + if ((r_val != hph_r_en) && !is_hphr_pa) { + snd_soc_component_write(component, WCD9335_HPH_R_EN, r_val); + wcd->hph_r_gain = hph_r_en & 0x1F; + } +} + +static void wcd9335_codec_hph_lohifi_config(struct snd_soc_component *comp, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(comp, WCD9335_RX_BIAS_HPH_PA, + WCD9335_RX_BIAS_HPH_PA_AMP_5_UA_MASK, + 0x06); + snd_soc_component_update_bits(comp, + WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, + 0xF0, 0x40); + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL1, + WCD9335_HPH_PA_GM3_IB_SCALE_MASK, + 0x0C); + wcd9335_codec_hph_mode_gain_opt(comp, 0x11); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500); + snd_soc_component_write(comp, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, + 0x8A); + snd_soc_component_update_bits(comp, WCD9335_RX_BIAS_HPH_PA, + WCD9335_RX_BIAS_HPH_PA_AMP_5_UA_MASK, + 0x0A); + } +} + +static void wcd9335_codec_hph_lp_config(struct snd_soc_component *comp, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL1, + WCD9335_HPH_PA_GM3_IB_SCALE_MASK, + 0x0C); + wcd9335_codec_hph_mode_gain_opt(comp, 0x10); + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_PSRREH_MASK, + WCD9335_HPH_PA_CTL2_FORCE_PSRREH_ENABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_HPH_PSRR_ENH_MASK, + WCD9335_HPH_PA_CTL2_HPH_PSRR_ENABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_RDAC_LDO_CTL, + WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_MASK, + WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_V_N1P60); + snd_soc_component_update_bits(comp, WCD9335_HPH_RDAC_LDO_CTL, + WCD9335_HPH_RDAC_1P65_LD_OUTCTL_MASK, + WCD9335_HPH_RDAC_1P65_LD_OUTCTL_V_N1P60); + snd_soc_component_update_bits(comp, + WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x0F, 0x01); + snd_soc_component_update_bits(comp, + WCD9335_RX_BIAS_HPH_RDAC_LDO, 0xF0, 0x10); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_write(comp, WCD9335_RX_BIAS_HPH_RDAC_LDO, + 0x88); + snd_soc_component_write(comp, WCD9335_HPH_RDAC_LDO_CTL, + 0x33); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_HPH_PSRR_ENH_MASK, + WCD9335_HPH_PA_CTL2_HPH_PSRR_DISABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_PSRREH_MASK, + WCD9335_HPH_PA_CTL2_FORCE_PSRREH_DISABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500); + snd_soc_component_update_bits(comp, WCD9335_HPH_R_EN, + WCD9335_HPH_CONST_SEL_L_MASK, + WCD9335_HPH_CONST_SEL_L_HQ_PATH); + snd_soc_component_update_bits(comp, WCD9335_HPH_L_EN, + WCD9335_HPH_CONST_SEL_L_MASK, + WCD9335_HPH_CONST_SEL_L_HQ_PATH); + } +} + +static void wcd9335_codec_hph_hifi_config(struct snd_soc_component *comp, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL1, + WCD9335_HPH_PA_GM3_IB_SCALE_MASK, + 0x0C); + wcd9335_codec_hph_mode_gain_opt(comp, 0x11); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(comp, WCD9335_HPH_PA_CTL2, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK, + WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE); + snd_soc_component_update_bits(comp, WCD9335_HPH_CNP_WG_CTL, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK, + WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500); + } +} + +static void wcd9335_codec_hph_mode_config(struct snd_soc_component *component, + int event, int mode) +{ + switch (mode) { + case CLS_H_LP: + wcd9335_codec_hph_lp_config(component, event); + break; + case CLS_H_LOHIFI: + wcd9335_codec_hph_lohifi_config(component, event); + break; + case CLS_H_HIFI: + wcd9335_codec_hph_hifi_config(component, event); + break; + } +} + +static int wcd9335_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + u8 dem_inp; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Read DEM INP Select */ + dem_inp = snd_soc_component_read(comp, + WCD9335_CDC_RX1_RX_PATH_SEC0) & 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(comp->dev, "Incorrect DEM Input\n"); + return -EINVAL; + } + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + wcd9335_codec_hph_mode_config(comp, event, hph_mode); + + break; + case SND_SOC_DAPM_POST_PMU: + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_PRE_PMD: + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + if (!(wcd_clsh_ctrl_get_state(wcd->clsh_ctrl) & + WCD_CLSH_STATE_HPHR)) + wcd9335_codec_hph_mode_config(comp, event, hph_mode); + + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + break; + } + + return 0; +} + +static int wcd9335_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, CLS_AB); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, CLS_AB); + break; + } + + return 0; +} + +static int wcd9335_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + break; + } + + return 0; +} + +static void wcd9335_codec_hph_post_pa_config(struct wcd9335_codec *wcd, + int mode, int event) +{ + u8 scale_val = 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + switch (mode) { + case CLS_H_HIFI: + scale_val = 0x3; + break; + case CLS_H_LOHIFI: + scale_val = 0x1; + break; + } + break; + case SND_SOC_DAPM_PRE_PMD: + scale_val = 0x6; + break; + } + + if (scale_val) + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_PA_CTL1, + WCD9335_HPH_PA_GM3_IB_SCALE_MASK, + scale_val << 1); + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (wcd->comp_enabled[COMPANDER_1] || + wcd->comp_enabled[COMPANDER_2]) { + /* GAIN Source Selection */ + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_L_EN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, + WCD9335_HPH_GAIN_SRC_SEL_COMPANDER); + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_R_EN, + WCD9335_HPH_GAIN_SRC_SEL_MASK, + WCD9335_HPH_GAIN_SRC_SEL_COMPANDER); + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_AUTO_CHOP, + WCD9335_HPH_AUTO_CHOP_MASK, + WCD9335_HPH_AUTO_CHOP_FORCE_ENABLE); + } + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_L_EN, + WCD9335_HPH_PA_GAIN_MASK, + wcd->hph_l_gain); + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_R_EN, + WCD9335_HPH_PA_GAIN_MASK, + wcd->hph_r_gain); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) + snd_soc_component_update_bits(wcd->component, + WCD9335_HPH_AUTO_CHOP, + WCD9335_HPH_AUTO_CHOP_MASK, + WCD9335_HPH_AUTO_CHOP_ENABLE_BY_CMPDR_GAIN); +} + +static int wcd9335_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + u8 dem_inp; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + + /* Read DEM INP Select */ + dem_inp = snd_soc_component_read(comp, + WCD9335_CDC_RX2_RX_PATH_SEC0) & + WCD9335_CDC_RX_PATH_DEM_INP_SEL_MASK; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(comp->dev, "DEM Input not set correctly, hph_mode: %d\n", + hph_mode); + return -EINVAL; + } + + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + wcd9335_codec_hph_mode_config(comp, event, hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + if (!(wcd_clsh_ctrl_get_state(wcd->clsh_ctrl) & + WCD_CLSH_STATE_HPHL)) + wcd9335_codec_hph_mode_config(comp, event, hph_mode); + + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + break; + } + + return 0; +} + +static int wcd9335_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(7000, 7100); + + wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event); + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX1_RX_PATH_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read(comp, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) & + WCD9335_CDC_RX_PGA_MUTE_EN_MASK) + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + + break; + case SND_SOC_DAPM_PRE_PMD: + wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + break; + } + + return 0; +} + +static int wcd9335_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int vol_reg = 0, mix_vol_reg = 0; + + if (w->reg == WCD9335_ANA_LO_1_2) { + if (w->shift == 7) { + vol_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + mix_vol_reg = WCD9335_CDC_RX3_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + vol_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + mix_vol_reg = WCD9335_CDC_RX4_RX_PATH_MIX_CTL; + } + } else if (w->reg == WCD9335_ANA_LO_3_4) { + if (w->shift == 7) { + vol_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + mix_vol_reg = WCD9335_CDC_RX5_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + vol_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + mix_vol_reg = WCD9335_CDC_RX6_RX_PATH_MIX_CTL; + } + } else { + dev_err(comp->dev, "Error enabling lineout PA\n"); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_component_update_bits(comp, vol_reg, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read(comp, mix_vol_reg)) & + WCD9335_CDC_RX_PGA_MUTE_EN_MASK) + snd_soc_component_update_bits(comp, mix_vol_reg, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + break; + } + + return 0; +} + +static void wcd9335_codec_init_flyback(struct snd_soc_component *component) +{ + snd_soc_component_update_bits(component, WCD9335_HPH_L_EN, + WCD9335_HPH_CONST_SEL_L_MASK, + WCD9335_HPH_CONST_SEL_L_BYPASS); + snd_soc_component_update_bits(component, WCD9335_HPH_R_EN, + WCD9335_HPH_CONST_SEL_L_MASK, + WCD9335_HPH_CONST_SEL_L_BYPASS); + snd_soc_component_update_bits(component, WCD9335_RX_BIAS_FLYB_BUFF, + WCD9335_RX_BIAS_FLYB_VPOS_5_UA_MASK, + WCD9335_RX_BIAS_FLYB_I_0P0_UA); + snd_soc_component_update_bits(component, WCD9335_RX_BIAS_FLYB_BUFF, + WCD9335_RX_BIAS_FLYB_VNEG_5_UA_MASK, + WCD9335_RX_BIAS_FLYB_I_0P0_UA); +} + +static int wcd9335_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd->rx_bias_count++; + if (wcd->rx_bias_count == 1) { + wcd9335_codec_init_flyback(comp); + snd_soc_component_update_bits(comp, + WCD9335_ANA_RX_SUPPLIES, + WCD9335_ANA_RX_BIAS_ENABLE_MASK, + WCD9335_ANA_RX_BIAS_ENABLE); + } + break; + case SND_SOC_DAPM_POST_PMD: + wcd->rx_bias_count--; + if (!wcd->rx_bias_count) + snd_soc_component_update_bits(comp, + WCD9335_ANA_RX_SUPPLIES, + WCD9335_ANA_RX_BIAS_ENABLE_MASK, + WCD9335_ANA_RX_BIAS_DISABLE); + break; + } + + return 0; +} + +static int wcd9335_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(7000, 7100); + wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event); + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX2_RX_PATH_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read(comp, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) & + WCD9335_CDC_RX_PGA_MUTE_EN_MASK) + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + + break; + + case SND_SOC_DAPM_PRE_PMD: + wcd9335_codec_hph_post_pa_config(wcd, hph_mode, event); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + break; + } + + return 0; +} + +static int wcd9335_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX0_RX_PATH_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read(comp, + WCD9335_CDC_RX0_RX_PATH_MIX_CTL)) & + WCD9335_CDC_RX_PGA_MUTE_EN_MASK) + snd_soc_component_update_bits(comp, + WCD9335_CDC_RX0_RX_PATH_MIX_CTL, + WCD9335_CDC_RX_PGA_MUTE_EN_MASK, + WCD9335_CDC_RX_PGA_MUTE_DISABLE); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + + break; + } + + return 0; +} + +static irqreturn_t wcd9335_slimbus_irq(int irq, void *data) +{ + struct wcd9335_codec *wcd = data; + unsigned long status = 0; + int i, j, port_id; + unsigned int val, int_val = 0; + irqreturn_t ret = IRQ_NONE; + bool tx; + unsigned short reg = 0; + + for (i = WCD9335_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= WCD9335_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + regmap_read(wcd->if_regmap, i, &val); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = (j >= 16); + port_id = (tx ? j - 16 : j); + regmap_read(wcd->if_regmap, + WCD9335_SLIM_PGD_PORT_INT_RX_SOURCE0 + j, &val); + if (val) { + if (!tx) + reg = WCD9335_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = WCD9335_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + regmap_read( + wcd->if_regmap, reg, &int_val); + /* + * Ignore interrupts for ports for which the + * interrupts are not specifically enabled. + */ + if (!(int_val & (1 << (port_id % 8)))) + continue; + } + + if (val & WCD9335_SLIM_IRQ_OVERFLOW) + dev_err_ratelimited(wcd->dev, + "%s: overflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + + if (val & WCD9335_SLIM_IRQ_UNDERFLOW) + dev_err_ratelimited(wcd->dev, + "%s: underflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + + if ((val & WCD9335_SLIM_IRQ_OVERFLOW) || + (val & WCD9335_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = WCD9335_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = WCD9335_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + regmap_read( + wcd->if_regmap, reg, &int_val); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + regmap_write(wcd->if_regmap, + reg, int_val); + } + } + + regmap_write(wcd->if_regmap, + WCD9335_SLIM_PGD_PORT_INT_CLR_RX_0 + (j / 8), + BIT(j % 8)); + ret = IRQ_HANDLED; + } + + return ret; +} + +static struct wcd9335_irq wcd9335_irqs[] = { + { + .irq = WCD9335_IRQ_SLIMBUS, + .handler = wcd9335_slimbus_irq, + .name = "SLIM Slave", + }, +}; + +static int wcd9335_setup_irqs(struct wcd9335_codec *wcd) +{ + int irq, ret, i; + + for (i = 0; i < ARRAY_SIZE(wcd9335_irqs); i++) { + irq = regmap_irq_get_virq(wcd->irq_data, wcd9335_irqs[i].irq); + if (irq < 0) { + dev_err(wcd->dev, "Failed to get %s\n", + wcd9335_irqs[i].name); + return irq; + } + + ret = devm_request_threaded_irq(wcd->dev, irq, NULL, + wcd9335_irqs[i].handler, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, + wcd9335_irqs[i].name, wcd); + if (ret) { + dev_err(wcd->dev, "Failed to request %s\n", + wcd9335_irqs[i].name); + return ret; + } + } + + /* enable interrupts on all slave ports */ + for (i = 0; i < WCD9335_SLIM_NUM_PORT_REG; i++) + regmap_write(wcd->if_regmap, WCD9335_SLIM_PGD_PORT_INT_EN0 + i, + 0xFF); + + return ret; +} + +static void wcd9335_teardown_irqs(struct wcd9335_codec *wcd) +{ + int i; + + /* disable interrupts on all slave ports */ + for (i = 0; i < WCD9335_SLIM_NUM_PORT_REG; i++) + regmap_write(wcd->if_regmap, WCD9335_SLIM_PGD_PORT_INT_EN0 + i, + 0x00); +} + +static void wcd9335_cdc_sido_ccl_enable(struct wcd9335_codec *wcd, + bool ccl_flag) +{ + struct snd_soc_component *comp = wcd->component; + + if (ccl_flag) { + if (++wcd->sido_ccl_cnt == 1) + snd_soc_component_write(comp, WCD9335_SIDO_SIDO_CCL_10, + WCD9335_SIDO_SIDO_CCL_DEF_VALUE); + } else { + if (wcd->sido_ccl_cnt == 0) { + dev_err(wcd->dev, "sido_ccl already disabled\n"); + return; + } + if (--wcd->sido_ccl_cnt == 0) + snd_soc_component_write(comp, WCD9335_SIDO_SIDO_CCL_10, + WCD9335_SIDO_SIDO_CCL_10_ICHARG_PWR_SEL_C320FF); + } +} + +static int wcd9335_enable_master_bias(struct wcd9335_codec *wcd) +{ + wcd->master_bias_users++; + if (wcd->master_bias_users == 1) { + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_EN_MASK, + WCD9335_ANA_BIAS_ENABLE); + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_PRECHRG_EN_MASK, + WCD9335_ANA_BIAS_PRECHRG_ENABLE); + /* + * 1ms delay is required after pre-charge is enabled + * as per HW requirement + */ + usleep_range(1000, 1100); + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_PRECHRG_EN_MASK, + WCD9335_ANA_BIAS_PRECHRG_DISABLE); + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_PRECHRG_CTL_MODE, + WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_MANUAL); + } + + return 0; +} + +static int wcd9335_enable_mclk(struct wcd9335_codec *wcd) +{ + /* Enable mclk requires master bias to be enabled first */ + if (wcd->master_bias_users <= 0) + return -EINVAL; + + if (((wcd->clk_mclk_users == 0) && (wcd->clk_type == WCD_CLK_MCLK)) || + ((wcd->clk_mclk_users > 0) && (wcd->clk_type != WCD_CLK_MCLK))) { + dev_err(wcd->dev, "Error enabling MCLK, clk_type: %d\n", + wcd->clk_type); + return -EINVAL; + } + + if (++wcd->clk_mclk_users == 1) { + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_EXT_CLKBUF_EN_MASK, + WCD9335_ANA_CLK_EXT_CLKBUF_ENABLE); + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_MCLK_SRC_MASK, + WCD9335_ANA_CLK_MCLK_SRC_EXTERNAL); + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_MCLK_EN_MASK, + WCD9335_ANA_CLK_MCLK_ENABLE); + regmap_update_bits(wcd->regmap, + WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + WCD9335_CDC_CLK_RST_CTRL_FS_CNT_EN_MASK, + WCD9335_CDC_CLK_RST_CTRL_FS_CNT_ENABLE); + regmap_update_bits(wcd->regmap, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + WCD9335_CDC_CLK_RST_CTRL_MCLK_EN_MASK, + WCD9335_CDC_CLK_RST_CTRL_MCLK_ENABLE); + /* + * 10us sleep is required after clock is enabled + * as per HW requirement + */ + usleep_range(10, 15); + } + + wcd->clk_type = WCD_CLK_MCLK; + + return 0; +} + +static int wcd9335_disable_mclk(struct wcd9335_codec *wcd) +{ + if (wcd->clk_mclk_users <= 0) + return -EINVAL; + + if (--wcd->clk_mclk_users == 0) { + if (wcd->clk_rco_users > 0) { + /* MCLK to RCO switch */ + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_MCLK_SRC_MASK, + WCD9335_ANA_CLK_MCLK_SRC_RCO); + wcd->clk_type = WCD_CLK_RCO; + } else { + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_MCLK_EN_MASK, + WCD9335_ANA_CLK_MCLK_DISABLE); + wcd->clk_type = WCD_CLK_OFF; + } + + regmap_update_bits(wcd->regmap, WCD9335_ANA_CLK_TOP, + WCD9335_ANA_CLK_EXT_CLKBUF_EN_MASK, + WCD9335_ANA_CLK_EXT_CLKBUF_DISABLE); + } + + return 0; +} + +static int wcd9335_disable_master_bias(struct wcd9335_codec *wcd) +{ + if (wcd->master_bias_users <= 0) + return -EINVAL; + + wcd->master_bias_users--; + if (wcd->master_bias_users == 0) { + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_EN_MASK, + WCD9335_ANA_BIAS_DISABLE); + regmap_update_bits(wcd->regmap, WCD9335_ANA_BIAS, + WCD9335_ANA_BIAS_PRECHRG_CTL_MODE, + WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_MANUAL); + } + return 0; +} + +static int wcd9335_cdc_req_mclk_enable(struct wcd9335_codec *wcd, + bool enable) +{ + int ret = 0; + + if (enable) { + wcd9335_cdc_sido_ccl_enable(wcd, true); + ret = clk_prepare_enable(wcd->mclk); + if (ret) { + dev_err(wcd->dev, "%s: ext clk enable failed\n", + __func__); + goto err; + } + /* get BG */ + wcd9335_enable_master_bias(wcd); + /* get MCLK */ + wcd9335_enable_mclk(wcd); + + } else { + /* put MCLK */ + wcd9335_disable_mclk(wcd); + /* put BG */ + wcd9335_disable_master_bias(wcd); + clk_disable_unprepare(wcd->mclk); + wcd9335_cdc_sido_ccl_enable(wcd, false); + } +err: + return ret; +} + +static void wcd9335_codec_apply_sido_voltage(struct wcd9335_codec *wcd, + enum wcd9335_sido_voltage req_mv) +{ + struct snd_soc_component *comp = wcd->component; + int vout_d_val; + + if (req_mv == wcd->sido_voltage) + return; + + /* compute the vout_d step value */ + vout_d_val = WCD9335_CALCULATE_VOUT_D(req_mv) & + WCD9335_ANA_BUCK_VOUT_MASK; + snd_soc_component_write(comp, WCD9335_ANA_BUCK_VOUT_D, vout_d_val); + snd_soc_component_update_bits(comp, WCD9335_ANA_BUCK_CTL, + WCD9335_ANA_BUCK_CTL_RAMP_START_MASK, + WCD9335_ANA_BUCK_CTL_RAMP_START_ENABLE); + + /* 1 msec sleep required after SIDO Vout_D voltage change */ + usleep_range(1000, 1100); + wcd->sido_voltage = req_mv; + snd_soc_component_update_bits(comp, WCD9335_ANA_BUCK_CTL, + WCD9335_ANA_BUCK_CTL_RAMP_START_MASK, + WCD9335_ANA_BUCK_CTL_RAMP_START_DISABLE); +} + +static int wcd9335_codec_update_sido_voltage(struct wcd9335_codec *wcd, + enum wcd9335_sido_voltage req_mv) +{ + int ret = 0; + + /* enable mclk before setting SIDO voltage */ + ret = wcd9335_cdc_req_mclk_enable(wcd, true); + if (ret) { + dev_err(wcd->dev, "Ext clk enable failed\n"); + goto err; + } + + wcd9335_codec_apply_sido_voltage(wcd, req_mv); + wcd9335_cdc_req_mclk_enable(wcd, false); + +err: + return ret; +} + +static int _wcd9335_codec_enable_mclk(struct snd_soc_component *component, + int enable) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int ret; + + if (enable) { + ret = wcd9335_cdc_req_mclk_enable(wcd, true); + if (ret) + return ret; + + wcd9335_codec_apply_sido_voltage(wcd, + SIDO_VOLTAGE_NOMINAL_MV); + } else { + wcd9335_codec_update_sido_voltage(wcd, + wcd->sido_voltage); + wcd9335_cdc_req_mclk_enable(wcd, false); + } + + return 0; +} + +static int wcd9335_codec_enable_mclk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return _wcd9335_codec_enable_mclk(comp, true); + case SND_SOC_DAPM_POST_PMD: + return _wcd9335_codec_enable_mclk(comp, false); + } + + return 0; +} + +static const struct snd_soc_dapm_widget wcd9335_dapm_widgets[] = { + /* TODO SPK1 & SPK2 OUT*/ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_OUTPUT("LINEOUT3"), + SND_SOC_DAPM_OUTPUT("LINEOUT4"), + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, WCD9335_RX0, 0, + &slim_rx_mux[WCD9335_RX0]), + SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, WCD9335_RX1, 0, + &slim_rx_mux[WCD9335_RX1]), + SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, WCD9335_RX2, 0, + &slim_rx_mux[WCD9335_RX2]), + SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, WCD9335_RX3, 0, + &slim_rx_mux[WCD9335_RX3]), + SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, WCD9335_RX4, 0, + &slim_rx_mux[WCD9335_RX4]), + SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, WCD9335_RX5, 0, + &slim_rx_mux[WCD9335_RX5]), + SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, WCD9335_RX6, 0, + &slim_rx_mux[WCD9335_RX6]), + SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, WCD9335_RX7, 0, + &slim_rx_mux[WCD9335_RX7]), + SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", WCD9335_CDC_RX0_RX_PATH_MIX_CTL, + 5, 0, &rx_int0_2_mux, wcd9335_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 5, 0, &rx_int1_2_mux, wcd9335_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 5, 0, &rx_int2_2_mux, wcd9335_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", WCD9335_CDC_RX3_RX_PATH_MIX_CTL, + 5, 0, &rx_int3_2_mux, wcd9335_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", WCD9335_CDC_RX4_RX_PATH_MIX_CTL, + 5, 0, &rx_int4_2_mux, wcd9335_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT5_2 MUX", WCD9335_CDC_RX5_RX_PATH_MIX_CTL, + 5, 0, &rx_int5_2_mux, wcd9335_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT6_2 MUX", WCD9335_CDC_RX6_RX_PATH_MIX_CTL, + 5, 0, &rx_int6_2_mux, wcd9335_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", WCD9335_CDC_RX7_RX_PATH_MIX_CTL, + 5, 0, &rx_int7_2_mux, wcd9335_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", WCD9335_CDC_RX8_RX_PATH_MIX_CTL, + 5, 0, &rx_int8_2_mux, wcd9335_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux), + + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT6_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT6 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT6 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int0_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int1_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int2_dem_inp_mux), + + SND_SOC_DAPM_MUX_E("RX INT0 INTERP", SND_SOC_NOPM, + INTERP_EAR, 0, &rx_int0_interp_mux, + wcd9335_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 INTERP", SND_SOC_NOPM, + INTERP_HPHL, 0, &rx_int1_interp_mux, + wcd9335_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 INTERP", SND_SOC_NOPM, + INTERP_HPHR, 0, &rx_int2_interp_mux, + wcd9335_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3 INTERP", SND_SOC_NOPM, + INTERP_LO1, 0, &rx_int3_interp_mux, + wcd9335_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4 INTERP", SND_SOC_NOPM, + INTERP_LO2, 0, &rx_int4_interp_mux, + wcd9335_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT5 INTERP", SND_SOC_NOPM, + INTERP_LO3, 0, &rx_int5_interp_mux, + wcd9335_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT6 INTERP", SND_SOC_NOPM, + INTERP_LO4, 0, &rx_int6_interp_mux, + wcd9335_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 INTERP", SND_SOC_NOPM, + INTERP_SPKR1, 0, &rx_int7_interp_mux, + wcd9335_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8 INTERP", SND_SOC_NOPM, + INTERP_SPKR2, 0, &rx_int8_interp_mux, + wcd9335_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd9335_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, WCD9335_ANA_HPH, + 5, 0, wcd9335_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, WCD9335_ANA_HPH, + 4, 0, wcd9335_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd9335_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd9335_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT5 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd9335_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT6 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd9335_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PA", WCD9335_ANA_HPH, 7, 0, NULL, 0, + wcd9335_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", WCD9335_ANA_HPH, 6, 0, NULL, 0, + wcd9335_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0, + wcd9335_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD9335_ANA_LO_1_2, 7, 0, NULL, 0, + wcd9335_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD9335_ANA_LO_1_2, 6, 0, NULL, 0, + wcd9335_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT3 PA", WCD9335_ANA_LO_3_4, 7, 0, NULL, 0, + wcd9335_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT4 PA", WCD9335_ANA_LO_3_4, 6, 0, NULL, 0, + wcd9335_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_mclk, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + /* TX */ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + SND_SOC_DAPM_INPUT("AMIC6"), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, + AIF2_CAP, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, + AIF3_CAP, 0, wcd9335_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD9335_ANA_AMIC1, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD9335_ANA_AMIC2, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD9335_ANA_AMIC3, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD9335_ANA_AMIC4, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC5", NULL, WCD9335_ANA_AMIC5, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC6", NULL, WCD9335_ANA_AMIC6, 7, 0, + wcd9335_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0, + wcd9335_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux0), + SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux1), + SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux2), + SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux3), + SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux4), + SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux5), + SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux6), + SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux7), + SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux8), + + SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0, + &tx_amic_mux0), + SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0, + &tx_amic_mux1), + SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0, + &tx_amic_mux2), + SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0, + &tx_amic_mux3), + SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0, + &tx_amic_mux4), + SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0, + &tx_amic_mux5), + SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0, + &tx_amic_mux6), + SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0, + &tx_amic_mux7), + SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0, + &tx_amic_mux8), + + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), + + SND_SOC_DAPM_MUX("SLIM TX0 MUX", SND_SOC_NOPM, WCD9335_TX0, 0, + &sb_tx0_mux), + SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, WCD9335_TX1, 0, + &sb_tx1_mux), + SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, WCD9335_TX2, 0, + &sb_tx2_mux), + SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, WCD9335_TX3, 0, + &sb_tx3_mux), + SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, WCD9335_TX4, 0, + &sb_tx4_mux), + SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, WCD9335_TX5, 0, + &sb_tx5_mux), + SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, WCD9335_TX6, 0, + &sb_tx6_mux), + SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, WCD9335_TX7, 0, + &sb_tx7_mux), + SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, WCD9335_TX8, 0, + &sb_tx8_mux), + + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD9335_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0, wcd9335_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX1", WCD9335_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc_mux1, wcd9335_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX2", WCD9335_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc_mux2, wcd9335_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX3", WCD9335_CDC_TX3_TX_PATH_CTL, 5, 0, + &tx_adc_mux3, wcd9335_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX4", WCD9335_CDC_TX4_TX_PATH_CTL, 5, 0, + &tx_adc_mux4, wcd9335_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX5", WCD9335_CDC_TX5_TX_PATH_CTL, 5, 0, + &tx_adc_mux5, wcd9335_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX6", WCD9335_CDC_TX6_TX_PATH_CTL, 5, 0, + &tx_adc_mux6, wcd9335_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX7", WCD9335_CDC_TX7_TX_PATH_CTL, 5, 0, + &tx_adc_mux7, wcd9335_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX8", WCD9335_CDC_TX8_TX_PATH_CTL, 5, 0, + &tx_adc_mux8, wcd9335_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +}; + +static void wcd9335_enable_sido_buck(struct snd_soc_component *component) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + + snd_soc_component_update_bits(component, WCD9335_ANA_RCO, + WCD9335_ANA_RCO_BG_EN_MASK, + WCD9335_ANA_RCO_BG_ENABLE); + snd_soc_component_update_bits(component, WCD9335_ANA_BUCK_CTL, + WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_MASK, + WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_EXT); + /* 100us sleep needed after IREF settings */ + usleep_range(100, 110); + snd_soc_component_update_bits(component, WCD9335_ANA_BUCK_CTL, + WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_MASK, + WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_EXT); + /* 100us sleep needed after VREF settings */ + usleep_range(100, 110); + wcd->sido_input_src = SIDO_SOURCE_RCO_BG; +} + +static int wcd9335_enable_efuse_sensing(struct snd_soc_component *comp) +{ + _wcd9335_codec_enable_mclk(comp, true); + snd_soc_component_update_bits(comp, + WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, + WCD9335_CHIP_TIER_CTRL_EFUSE_EN_MASK, + WCD9335_CHIP_TIER_CTRL_EFUSE_ENABLE); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + + if (!(snd_soc_component_read(comp, + WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS) & + WCD9335_CHIP_TIER_CTRL_EFUSE_EN_MASK)) + WARN(1, "%s: Efuse sense is not complete\n", __func__); + + wcd9335_enable_sido_buck(comp); + _wcd9335_codec_enable_mclk(comp, false); + + return 0; +} + +static void wcd9335_codec_init(struct snd_soc_component *component) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int i; + + /* ungate MCLK and set clk rate */ + regmap_update_bits(wcd->regmap, WCD9335_CODEC_RPM_CLK_GATE, + WCD9335_CODEC_RPM_CLK_GATE_MCLK_GATE_MASK, 0); + + regmap_update_bits(wcd->regmap, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ); + + for (i = 0; i < ARRAY_SIZE(wcd9335_codec_reg_init); i++) + snd_soc_component_update_bits(component, + wcd9335_codec_reg_init[i].reg, + wcd9335_codec_reg_init[i].mask, + wcd9335_codec_reg_init[i].val); + + wcd9335_enable_efuse_sensing(component); +} + +static int wcd9335_codec_probe(struct snd_soc_component *component) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(component->dev); + int ret; + int i; + + snd_soc_component_init_regmap(component, wcd->regmap); + /* Class-H Init*/ + wcd->clsh_ctrl = wcd_clsh_ctrl_alloc(component, WCD9335); + if (IS_ERR(wcd->clsh_ctrl)) + return PTR_ERR(wcd->clsh_ctrl); + + /* Default HPH Mode to Class-H HiFi */ + wcd->hph_mode = CLS_H_HIFI; + wcd->component = component; + + wcd9335_codec_init(component); + + for (i = 0; i < NUM_CODEC_DAIS; i++) + INIT_LIST_HEAD(&wcd->dai[i].slim_ch_list); + + ret = wcd9335_setup_irqs(wcd); + if (ret) + goto free_clsh_ctrl; + + return 0; + +free_clsh_ctrl: + wcd_clsh_ctrl_free(wcd->clsh_ctrl); + return ret; +} + +static void wcd9335_codec_remove(struct snd_soc_component *comp) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + + wcd_clsh_ctrl_free(wcd->clsh_ctrl); + wcd9335_teardown_irqs(wcd); +} + +static int wcd9335_codec_set_sysclk(struct snd_soc_component *comp, + int clk_id, int source, + unsigned int freq, int dir) +{ + struct wcd9335_codec *wcd = dev_get_drvdata(comp->dev); + + wcd->mclk_rate = freq; + + if (wcd->mclk_rate == WCD9335_MCLK_CLK_12P288MHZ) + snd_soc_component_update_bits(comp, + WCD9335_CODEC_RPM_CLK_MCLK_CFG, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ); + else if (wcd->mclk_rate == WCD9335_MCLK_CLK_9P6MHZ) + snd_soc_component_update_bits(comp, + WCD9335_CODEC_RPM_CLK_MCLK_CFG, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + WCD9335_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ); + + return clk_set_rate(wcd->mclk, freq); +} + +static const struct snd_soc_component_driver wcd9335_component_drv = { + .probe = wcd9335_codec_probe, + .remove = wcd9335_codec_remove, + .set_sysclk = wcd9335_codec_set_sysclk, + .controls = wcd9335_snd_controls, + .num_controls = ARRAY_SIZE(wcd9335_snd_controls), + .dapm_widgets = wcd9335_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd9335_dapm_widgets), + .dapm_routes = wcd9335_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd9335_audio_map), + .endianness = 1, +}; + +static int wcd9335_probe(struct wcd9335_codec *wcd) +{ + struct device *dev = wcd->dev; + + memcpy(wcd->rx_chs, wcd9335_rx_chs, sizeof(wcd9335_rx_chs)); + memcpy(wcd->tx_chs, wcd9335_tx_chs, sizeof(wcd9335_tx_chs)); + + wcd->sido_input_src = SIDO_SOURCE_INTERNAL; + wcd->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV; + + return devm_snd_soc_register_component(dev, &wcd9335_component_drv, + wcd9335_slim_dais, + ARRAY_SIZE(wcd9335_slim_dais)); +} + +static const struct regmap_range_cfg wcd9335_ranges[] = { + { + .name = "WCD9335", + .range_min = 0x0, + .range_max = WCD9335_MAX_REGISTER, + .selector_reg = WCD9335_SEL_REGISTER, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0x800, + .window_len = 0x100, + }, +}; + +static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD9335_INTR_PIN1_STATUS0...WCD9335_INTR_PIN2_CLEAR3: + case WCD9335_ANA_MBHC_RESULT_3: + case WCD9335_ANA_MBHC_RESULT_2: + case WCD9335_ANA_MBHC_RESULT_1: + case WCD9335_ANA_MBHC_MECH: + case WCD9335_ANA_MBHC_ELECT: + case WCD9335_ANA_MBHC_ZDET: + case WCD9335_ANA_MICB2: + case WCD9335_ANA_RCO: + case WCD9335_ANA_BIAS: + return true; + default: + return false; + } +} + +static struct regmap_config wcd9335_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .max_register = WCD9335_MAX_REGISTER, + .can_multi_write = true, + .ranges = wcd9335_ranges, + .num_ranges = ARRAY_SIZE(wcd9335_ranges), + .volatile_reg = wcd9335_is_volatile_register, +}; + +static const struct regmap_range_cfg wcd9335_ifc_ranges[] = { + { + .name = "WCD9335-IFC-DEV", + .range_min = 0x0, + .range_max = WCD9335_MAX_REGISTER, + .selector_reg = WCD9335_SEL_REGISTER, + .selector_mask = 0xfff, + .selector_shift = 0, + .window_start = 0x800, + .window_len = 0x400, + }, +}; + +static struct regmap_config wcd9335_ifc_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .can_multi_write = true, + .max_register = WCD9335_MAX_REGISTER, + .ranges = wcd9335_ifc_ranges, + .num_ranges = ARRAY_SIZE(wcd9335_ifc_ranges), +}; + +static const struct regmap_irq wcd9335_codec_irqs[] = { + /* INTR_REG 0 */ + [WCD9335_IRQ_SLIMBUS] = { + .reg_offset = 0, + .mask = BIT(0), + .type = { + .type_reg_offset = 0, + .types_supported = IRQ_TYPE_EDGE_BOTH, + .type_reg_mask = BIT(0), + }, + }, +}; + +static const unsigned int wcd9335_config_regs[] = { + WCD9335_INTR_LEVEL0, +}; + +static const struct regmap_irq_chip wcd9335_regmap_irq1_chip = { + .name = "wcd9335_pin1_irq", + .status_base = WCD9335_INTR_PIN1_STATUS0, + .mask_base = WCD9335_INTR_PIN1_MASK0, + .ack_base = WCD9335_INTR_PIN1_CLEAR0, + .num_regs = 4, + .irqs = wcd9335_codec_irqs, + .num_irqs = ARRAY_SIZE(wcd9335_codec_irqs), + .config_base = wcd9335_config_regs, + .num_config_bases = ARRAY_SIZE(wcd9335_config_regs), + .num_config_regs = 4, + .set_type_config = regmap_irq_set_type_config_simple, +}; + +static int wcd9335_parse_dt(struct wcd9335_codec *wcd) +{ + struct device *dev = wcd->dev; + struct device_node *np = dev->of_node; + int ret; + + wcd->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); + if (wcd->reset_gpio < 0) { + dev_err(dev, "Reset GPIO missing from DT\n"); + return wcd->reset_gpio; + } + + wcd->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(wcd->mclk)) { + dev_err(dev, "mclk not found\n"); + return PTR_ERR(wcd->mclk); + } + + wcd->native_clk = devm_clk_get(dev, "slimbus"); + if (IS_ERR(wcd->native_clk)) { + dev_err(dev, "slimbus clock not found\n"); + return PTR_ERR(wcd->native_clk); + } + + wcd->supplies[0].supply = "vdd-buck"; + wcd->supplies[1].supply = "vdd-buck-sido"; + wcd->supplies[2].supply = "vdd-tx"; + wcd->supplies[3].supply = "vdd-rx"; + wcd->supplies[4].supply = "vdd-io"; + + ret = regulator_bulk_get(dev, WCD9335_MAX_SUPPLY, wcd->supplies); + if (ret) { + dev_err(dev, "Failed to get supplies: err = %d\n", ret); + return ret; + } + + return 0; +} + +static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) +{ + struct device *dev = wcd->dev; + int ret; + + ret = regulator_bulk_enable(WCD9335_MAX_SUPPLY, wcd->supplies); + if (ret) { + dev_err(dev, "Failed to get supplies: err = %d\n", ret); + return ret; + } + + /* + * For WCD9335, it takes about 600us for the Vout_A and + * Vout_D to be ready after BUCK_SIDO is powered up. + * SYS_RST_N shouldn't be pulled high during this time + * Toggle the reset line to make sure the reset pulse is + * correctly applied + */ + usleep_range(600, 650); + + gpio_direction_output(wcd->reset_gpio, 0); + msleep(20); + gpio_set_value(wcd->reset_gpio, 1); + msleep(20); + + return 0; +} + +static int wcd9335_bring_up(struct wcd9335_codec *wcd) +{ + struct regmap *rm = wcd->regmap; + int val, byte0; + + regmap_read(rm, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val); + regmap_read(rm, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, &byte0); + + if ((val < 0) || (byte0 < 0)) { + dev_err(wcd->dev, "WCD9335 CODEC version detection fail!\n"); + return -EINVAL; + } + + if (byte0 == 0x1) { + dev_info(wcd->dev, "WCD9335 CODEC version is v2.0\n"); + wcd->version = WCD9335_VERSION_2_0; + regmap_write(rm, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(rm, WCD9335_SIDO_SIDO_TEST_2, 0x00); + regmap_write(rm, WCD9335_SIDO_SIDO_CCL_8, 0x6F); + regmap_write(rm, WCD9335_BIAS_VBG_FINE_ADJ, 0x65); + regmap_write(rm, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + regmap_write(rm, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + regmap_write(rm, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + regmap_write(rm, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else { + dev_err(wcd->dev, "WCD9335 CODEC version not supported\n"); + return -EINVAL; + } + + return 0; +} + +static int wcd9335_irq_init(struct wcd9335_codec *wcd) +{ + int ret; + + /* + * INTR1 consists of all possible interrupt sources Ear OCP, + * HPH OCP, MBHC, MAD, VBAT, and SVA + * INTR2 is a subset of first interrupt sources MAD, VBAT, and SVA + */ + wcd->intr1 = of_irq_get_byname(wcd->dev->of_node, "intr1"); + if (wcd->intr1 < 0) + return dev_err_probe(wcd->dev, wcd->intr1, + "Unable to configure IRQ\n"); + + ret = devm_regmap_add_irq_chip(wcd->dev, wcd->regmap, wcd->intr1, + IRQF_TRIGGER_HIGH, 0, + &wcd9335_regmap_irq1_chip, &wcd->irq_data); + if (ret) + return dev_err_probe(wcd->dev, ret, "Failed to register IRQ chip\n"); + + return 0; +} + +static int wcd9335_slim_probe(struct slim_device *slim) +{ + struct device *dev = &slim->dev; + struct wcd9335_codec *wcd; + int ret; + + wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + wcd->dev = dev; + ret = wcd9335_parse_dt(wcd); + if (ret) { + dev_err(dev, "Error parsing DT: %d\n", ret); + return ret; + } + + ret = wcd9335_power_on_reset(wcd); + if (ret) + return ret; + + dev_set_drvdata(dev, wcd); + + return 0; +} + +static int wcd9335_slim_status(struct slim_device *sdev, + enum slim_device_status status) +{ + struct device *dev = &sdev->dev; + struct device_node *ifc_dev_np; + struct wcd9335_codec *wcd; + int ret; + + wcd = dev_get_drvdata(dev); + + ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0); + if (!ifc_dev_np) { + dev_err(dev, "No Interface device found\n"); + return -EINVAL; + } + + wcd->slim = sdev; + wcd->slim_ifc_dev = of_slim_get_device(sdev->ctrl, ifc_dev_np); + of_node_put(ifc_dev_np); + if (!wcd->slim_ifc_dev) { + dev_err(dev, "Unable to get SLIM Interface device\n"); + return -EINVAL; + } + + slim_get_logical_addr(wcd->slim_ifc_dev); + + wcd->regmap = regmap_init_slimbus(sdev, &wcd9335_regmap_config); + if (IS_ERR(wcd->regmap)) + return dev_err_probe(dev, PTR_ERR(wcd->regmap), + "Failed to allocate slim register map\n"); + + wcd->if_regmap = regmap_init_slimbus(wcd->slim_ifc_dev, + &wcd9335_ifc_regmap_config); + if (IS_ERR(wcd->if_regmap)) + return dev_err_probe(dev, PTR_ERR(wcd->if_regmap), + "Failed to allocate ifc register map\n"); + + ret = wcd9335_bring_up(wcd); + if (ret) { + dev_err(dev, "Failed to bringup WCD9335\n"); + return ret; + } + + ret = wcd9335_irq_init(wcd); + if (ret) + return ret; + + wcd9335_probe(wcd); + + return 0; +} + +static const struct slim_device_id wcd9335_slim_id[] = { + {SLIM_MANF_ID_QCOM, SLIM_PROD_CODE_WCD9335, 0x1, 0x0}, + {} +}; +MODULE_DEVICE_TABLE(slim, wcd9335_slim_id); + +static struct slim_driver wcd9335_slim_driver = { + .driver = { + .name = "wcd9335-slim", + }, + .probe = wcd9335_slim_probe, + .device_status = wcd9335_slim_status, + .id_table = wcd9335_slim_id, +}; + +module_slim_driver(wcd9335_slim_driver); +MODULE_DESCRIPTION("WCD9335 slim driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("slim:217:1a0:*"); diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h new file mode 100644 index 0000000000..490fc44144 --- /dev/null +++ b/sound/soc/codecs/wcd9335.h @@ -0,0 +1,641 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __WCD9335_H__ +#define __WCD9335_H__ + +/* + * WCD9335 register base can change according to the mode it works in. + * In slimbus mode the reg base starts from 0x800. + * In i2s/i2c mode the reg base is 0x0. + */ +#define WCD9335_REG(pg, r) ((pg << 8) | (r)) +#define WCD9335_REG_OFFSET(r) (r & 0xFF) +#define WCD9335_PAGE_OFFSET(r) ((r >> 8) & 0xFF) + +/* Page-0 Registers */ +#define WCD9335_PAGE0_PAGE_REGISTER WCD9335_REG(0x00, 0x000) +#define WCD9335_CODEC_RPM_CLK_GATE WCD9335_REG(0x00, 0x002) +#define WCD9335_CODEC_RPM_CLK_GATE_MCLK_GATE_MASK GENMASK(1, 0) +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG WCD9335_REG(0x00, 0x003) +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ BIT(0) +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ BIT(0) +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK GENMASK(1, 0) +#define WCD9335_CODEC_RPM_RST_CTL WCD9335_REG(0x00, 0x009) +#define WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL WCD9335_REG(0x00, 0x011) +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0 WCD9335_REG(0x00, 0x021) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_CTL WCD9335_REG(0x00, 0x025) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_SSTATE_MASK GENMASK(4, 1) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_EN_MASK BIT(0) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_ENABLE BIT(0) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0 WCD9335_REG(0x00, 0x029) +#define WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS WCD9335_REG(0x00, 0x039) +#define WCD9335_INTR_CFG WCD9335_REG(0x00, 0x081) +#define WCD9335_INTR_CLR_COMMIT WCD9335_REG(0x00, 0x082) +#define WCD9335_INTR_PIN1_MASK0 WCD9335_REG(0x00, 0x089) +#define WCD9335_INTR_PIN1_MASK1 WCD9335_REG(0x00, 0x08a) +#define WCD9335_INTR_PIN1_MASK2 WCD9335_REG(0x00, 0x08b) +#define WCD9335_INTR_PIN1_MASK3 WCD9335_REG(0x00, 0x08c) +#define WCD9335_INTR_PIN1_STATUS0 WCD9335_REG(0x00, 0x091) +#define WCD9335_INTR_PIN1_STATUS1 WCD9335_REG(0x00, 0x092) +#define WCD9335_INTR_PIN1_STATUS2 WCD9335_REG(0x00, 0x093) +#define WCD9335_INTR_PIN1_STATUS3 WCD9335_REG(0x00, 0x094) +#define WCD9335_INTR_PIN1_CLEAR0 WCD9335_REG(0x00, 0x099) +#define WCD9335_INTR_PIN1_CLEAR1 WCD9335_REG(0x00, 0x09a) +#define WCD9335_INTR_PIN1_CLEAR2 WCD9335_REG(0x00, 0x09b) +#define WCD9335_INTR_PIN1_CLEAR3 WCD9335_REG(0x00, 0x09c) +#define WCD9335_INTR_PIN2_MASK0 WCD9335_REG(0x00, 0x0a1) +#define WCD9335_INTR_PIN2_MASK1 WCD9335_REG(0x00, 0x0a2) +#define WCD9335_INTR_PIN2_MASK2 WCD9335_REG(0x00, 0x0a3) +#define WCD9335_INTR_PIN2_MASK3 WCD9335_REG(0x00, 0x0a4) +#define WCD9335_INTR_PIN2_STATUS0 WCD9335_REG(0x00, 0x0a9) +#define WCD9335_INTR_PIN2_STATUS1 WCD9335_REG(0x00, 0x0aa) +#define WCD9335_INTR_PIN2_STATUS2 WCD9335_REG(0x00, 0x0ab) +#define WCD9335_INTR_PIN2_STATUS3 WCD9335_REG(0x00, 0x0ac) +#define WCD9335_INTR_PIN2_CLEAR0 WCD9335_REG(0x00, 0x0b1) +#define WCD9335_INTR_PIN2_CLEAR1 WCD9335_REG(0x00, 0x0b2) +#define WCD9335_INTR_PIN2_CLEAR2 WCD9335_REG(0x00, 0x0b3) +#define WCD9335_INTR_PIN2_CLEAR3 WCD9335_REG(0x00, 0x0b4) +#define WCD9335_INTR_LEVEL0 WCD9335_REG(0x00, 0x0e1) +#define WCD9335_INTR_LEVEL1 WCD9335_REG(0x00, 0x0e2) +#define WCD9335_INTR_LEVEL2 WCD9335_REG(0x00, 0x0e3) +#define WCD9335_INTR_LEVEL3 WCD9335_REG(0x00, 0x0e4) + +/* Page-1 Registers */ +#define WCD9335_CPE_FLL_USER_CTL_0 WCD9335_REG(0x01, 0x001) +#define WCD9335_CPE_FLL_USER_CTL_1 WCD9335_REG(0x01, 0x002) +#define WCD9335_CPE_FLL_USER_CTL_2 WCD9335_REG(0x01, 0x003) +#define WCD9335_CPE_FLL_USER_CTL_3 WCD9335_REG(0x01, 0x004) +#define WCD9335_CPE_FLL_USER_CTL_4 WCD9335_REG(0x01, 0x005) +#define WCD9335_CPE_FLL_USER_CTL_5 WCD9335_REG(0x01, 0x006) +#define WCD9335_CPE_FLL_USER_CTL_6 WCD9335_REG(0x01, 0x007) +#define WCD9335_CPE_FLL_USER_CTL_7 WCD9335_REG(0x01, 0x008) +#define WCD9335_CPE_FLL_USER_CTL_8 WCD9335_REG(0x01, 0x009) +#define WCD9335_CPE_FLL_USER_CTL_9 WCD9335_REG(0x01, 0x00a) +#define WCD9335_CPE_FLL_L_VAL_CTL_0 WCD9335_REG(0x01, 0x00b) +#define WCD9335_CPE_FLL_L_VAL_CTL_1 WCD9335_REG(0x01, 0x00c) +#define WCD9335_CPE_FLL_DSM_FRAC_CTL_0 WCD9335_REG(0x01, 0x00d) +#define WCD9335_CPE_FLL_DSM_FRAC_CTL_1 WCD9335_REG(0x01, 0x00e) +#define WCD9335_CPE_FLL_CONFIG_CTL_0 WCD9335_REG(0x01, 0x00f) +#define WCD9335_CPE_FLL_CONFIG_CTL_1 WCD9335_REG(0x01, 0x010) +#define WCD9335_CPE_FLL_CONFIG_CTL_2 WCD9335_REG(0x01, 0x011) +#define WCD9335_CPE_FLL_CONFIG_CTL_3 WCD9335_REG(0x01, 0x012) +#define WCD9335_CPE_FLL_CONFIG_CTL_4 WCD9335_REG(0x01, 0x013) +#define WCD9335_CPE_FLL_TEST_CTL_0 WCD9335_REG(0x01, 0x014) +#define WCD9335_CPE_FLL_TEST_CTL_1 WCD9335_REG(0x01, 0x015) +#define WCD9335_CPE_FLL_TEST_CTL_2 WCD9335_REG(0x01, 0x016) +#define WCD9335_CPE_FLL_TEST_CTL_3 WCD9335_REG(0x01, 0x017) +#define WCD9335_CPE_FLL_TEST_CTL_4 WCD9335_REG(0x01, 0x018) +#define WCD9335_CPE_FLL_TEST_CTL_5 WCD9335_REG(0x01, 0x019) +#define WCD9335_CPE_FLL_TEST_CTL_6 WCD9335_REG(0x01, 0x01a) +#define WCD9335_CPE_FLL_TEST_CTL_7 WCD9335_REG(0x01, 0x01b) +#define WCD9335_CPE_FLL_FREQ_CTL_0 WCD9335_REG(0x01, 0x01c) +#define WCD9335_CPE_FLL_FREQ_CTL_1 WCD9335_REG(0x01, 0x01d) +#define WCD9335_CPE_FLL_FREQ_CTL_2 WCD9335_REG(0x01, 0x01e) +#define WCD9335_CPE_FLL_FREQ_CTL_3 WCD9335_REG(0x01, 0x01f) +#define WCD9335_CPE_FLL_SSC_CTL_0 WCD9335_REG(0x01, 0x020) +#define WCD9335_CPE_FLL_SSC_CTL_1 WCD9335_REG(0x01, 0x021) +#define WCD9335_CPE_FLL_SSC_CTL_2 WCD9335_REG(0x01, 0x022) +#define WCD9335_CPE_FLL_SSC_CTL_3 WCD9335_REG(0x01, 0x023) +#define WCD9335_CPE_FLL_FLL_MODE WCD9335_REG(0x01, 0x024) +#define WCD9335_CPE_FLL_STATUS_0 WCD9335_REG(0x01, 0x025) +#define WCD9335_CPE_FLL_STATUS_1 WCD9335_REG(0x01, 0x026) +#define WCD9335_CPE_FLL_STATUS_2 WCD9335_REG(0x01, 0x027) +#define WCD9335_CPE_FLL_STATUS_3 WCD9335_REG(0x01, 0x028) +#define WCD9335_I2S_FLL_USER_CTL_0 WCD9335_REG(0x01, 0x041) +#define WCD9335_I2S_FLL_USER_CTL_1 WCD9335_REG(0x01, 0x042) +#define WCD9335_I2S_FLL_USER_CTL_2 WCD9335_REG(0x01, 0x043) +#define WCD9335_I2S_FLL_USER_CTL_3 WCD9335_REG(0x01, 0x044) +#define WCD9335_I2S_FLL_USER_CTL_4 WCD9335_REG(0x01, 0x045) +#define WCD9335_I2S_FLL_USER_CTL_5 WCD9335_REG(0x01, 0x046) +#define WCD9335_I2S_FLL_USER_CTL_6 WCD9335_REG(0x01, 0x047) +#define WCD9335_I2S_FLL_USER_CTL_7 WCD9335_REG(0x01, 0x048) +#define WCD9335_I2S_FLL_USER_CTL_8 WCD9335_REG(0x01, 0x049) +#define WCD9335_I2S_FLL_USER_CTL_9 WCD9335_REG(0x01, 0x04a) +#define WCD9335_I2S_FLL_L_VAL_CTL_0 WCD9335_REG(0x01, 0x04b) +#define WCD9335_I2S_FLL_L_VAL_CTL_1 WCD9335_REG(0x01, 0x04c) +#define WCD9335_I2S_FLL_DSM_FRAC_CTL_0 WCD9335_REG(0x01, 0x04d) +#define WCD9335_I2S_FLL_DSM_FRAC_CTL_1 WCD9335_REG(0x01, 0x04e) +#define WCD9335_I2S_FLL_CONFIG_CTL_0 WCD9335_REG(0x01, 0x04f) +#define WCD9335_I2S_FLL_CONFIG_CTL_1 WCD9335_REG(0x01, 0x050) +#define WCD9335_I2S_FLL_CONFIG_CTL_2 WCD9335_REG(0x01, 0x051) +#define WCD9335_I2S_FLL_CONFIG_CTL_3 WCD9335_REG(0x01, 0x052) +#define WCD9335_I2S_FLL_CONFIG_CTL_4 WCD9335_REG(0x01, 0x053) +#define WCD9335_I2S_FLL_TEST_CTL_0 WCD9335_REG(0x01, 0x054) +#define WCD9335_I2S_FLL_TEST_CTL_1 WCD9335_REG(0x01, 0x055) +#define WCD9335_I2S_FLL_TEST_CTL_2 WCD9335_REG(0x01, 0x056) +#define WCD9335_I2S_FLL_TEST_CTL_3 WCD9335_REG(0x01, 0x057) +#define WCD9335_I2S_FLL_TEST_CTL_4 WCD9335_REG(0x01, 0x058) +#define WCD9335_I2S_FLL_TEST_CTL_5 WCD9335_REG(0x01, 0x059) +#define WCD9335_I2S_FLL_TEST_CTL_6 WCD9335_REG(0x01, 0x05a) +#define WCD9335_I2S_FLL_TEST_CTL_7 WCD9335_REG(0x01, 0x05b) +#define WCD9335_I2S_FLL_FREQ_CTL_0 WCD9335_REG(0x01, 0x05c) +#define WCD9335_I2S_FLL_FREQ_CTL_1 WCD9335_REG(0x01, 0x05d) +#define WCD9335_I2S_FLL_FREQ_CTL_2 WCD9335_REG(0x01, 0x05e) +#define WCD9335_I2S_FLL_FREQ_CTL_3 WCD9335_REG(0x01, 0x05f) +#define WCD9335_I2S_FLL_SSC_CTL_0 WCD9335_REG(0x01, 0x060) +#define WCD9335_I2S_FLL_SSC_CTL_1 WCD9335_REG(0x01, 0x061) +#define WCD9335_I2S_FLL_SSC_CTL_2 WCD9335_REG(0x01, 0x062) +#define WCD9335_I2S_FLL_SSC_CTL_3 WCD9335_REG(0x01, 0x063) +#define WCD9335_I2S_FLL_FLL_MODE WCD9335_REG(0x01, 0x064) +#define WCD9335_I2S_FLL_STATUS_0 WCD9335_REG(0x01, 0x065) +#define WCD9335_I2S_FLL_STATUS_1 WCD9335_REG(0x01, 0x066) +#define WCD9335_I2S_FLL_STATUS_2 WCD9335_REG(0x01, 0x067) +#define WCD9335_I2S_FLL_STATUS_3 WCD9335_REG(0x01, 0x068) +#define WCD9335_SB_FLL_USER_CTL_0 WCD9335_REG(0x01, 0x081) +#define WCD9335_SB_FLL_USER_CTL_1 WCD9335_REG(0x01, 0x082) +#define WCD9335_SB_FLL_USER_CTL_2 WCD9335_REG(0x01, 0x083) +#define WCD9335_SB_FLL_USER_CTL_3 WCD9335_REG(0x01, 0x084) +#define WCD9335_SB_FLL_USER_CTL_4 WCD9335_REG(0x01, 0x085) +#define WCD9335_SB_FLL_USER_CTL_5 WCD9335_REG(0x01, 0x086) +#define WCD9335_SB_FLL_USER_CTL_6 WCD9335_REG(0x01, 0x087) +#define WCD9335_SB_FLL_USER_CTL_7 WCD9335_REG(0x01, 0x088) +#define WCD9335_SB_FLL_USER_CTL_8 WCD9335_REG(0x01, 0x089) +#define WCD9335_SB_FLL_USER_CTL_9 WCD9335_REG(0x01, 0x08a) +#define WCD9335_SB_FLL_L_VAL_CTL_0 WCD9335_REG(0x01, 0x08b) +#define WCD9335_SB_FLL_L_VAL_CTL_1 WCD9335_REG(0x01, 0x08c) +#define WCD9335_SB_FLL_DSM_FRAC_CTL_0 WCD9335_REG(0x01, 0x08d) +#define WCD9335_SB_FLL_DSM_FRAC_CTL_1 WCD9335_REG(0x01, 0x08e) +#define WCD9335_SB_FLL_CONFIG_CTL_0 WCD9335_REG(0x01, 0x08f) +#define WCD9335_SB_FLL_CONFIG_CTL_1 WCD9335_REG(0x01, 0x090) +#define WCD9335_SB_FLL_CONFIG_CTL_2 WCD9335_REG(0x01, 0x091) +#define WCD9335_SB_FLL_CONFIG_CTL_3 WCD9335_REG(0x01, 0x092) +#define WCD9335_SB_FLL_CONFIG_CTL_4 WCD9335_REG(0x01, 0x093) +#define WCD9335_SB_FLL_TEST_CTL_0 WCD9335_REG(0x01, 0x094) +#define WCD9335_SB_FLL_TEST_CTL_1 WCD9335_REG(0x01, 0x095) +#define WCD9335_SB_FLL_TEST_CTL_2 WCD9335_REG(0x01, 0x096) +#define WCD9335_SB_FLL_TEST_CTL_3 WCD9335_REG(0x01, 0x097) +#define WCD9335_SB_FLL_TEST_CTL_4 WCD9335_REG(0x01, 0x098) +#define WCD9335_SB_FLL_TEST_CTL_5 WCD9335_REG(0x01, 0x099) +#define WCD9335_SB_FLL_TEST_CTL_6 WCD9335_REG(0x01, 0x09a) +#define WCD9335_SB_FLL_TEST_CTL_7 WCD9335_REG(0x01, 0x09b) +#define WCD9335_SB_FLL_FREQ_CTL_0 WCD9335_REG(0x01, 0x09c) +#define WCD9335_SB_FLL_FREQ_CTL_1 WCD9335_REG(0x01, 0x09d) +#define WCD9335_SB_FLL_FREQ_CTL_2 WCD9335_REG(0x01, 0x09e) +#define WCD9335_SB_FLL_FREQ_CTL_3 WCD9335_REG(0x01, 0x09f) +#define WCD9335_SB_FLL_SSC_CTL_0 WCD9335_REG(0x01, 0x0a0) +#define WCD9335_SB_FLL_SSC_CTL_1 WCD9335_REG(0x01, 0x0a1) +#define WCD9335_SB_FLL_SSC_CTL_2 WCD9335_REG(0x01, 0x0a2) +#define WCD9335_SB_FLL_SSC_CTL_3 WCD9335_REG(0x01, 0x0a3) +#define WCD9335_SB_FLL_FLL_MODE WCD9335_REG(0x01, 0x0a4) +#define WCD9335_SB_FLL_STATUS_0 WCD9335_REG(0x01, 0x0a5) +#define WCD9335_SB_FLL_STATUS_1 WCD9335_REG(0x01, 0x0a6) +#define WCD9335_SB_FLL_STATUS_2 WCD9335_REG(0x01, 0x0a7) +#define WCD9335_SB_FLL_STATUS_3 WCD9335_REG(0x01, 0x0a8) + +/* Page-2 Registers */ +#define WCD9335_PAGE2_PAGE_REGISTER WCD9335_REG(0x02, 0x000) +#define WCD9335_CPE_SS_DMIC0_CTL WCD9335_REG(0x02, 0x063) +#define WCD9335_CPE_SS_DMIC1_CTL WCD9335_REG(0x02, 0x064) +#define WCD9335_CPE_SS_DMIC2_CTL WCD9335_REG(0x02, 0x065) +#define WCD9335_CPE_SS_DMIC_CFG WCD9335_REG(0x02, 0x066) +#define WCD9335_SOC_MAD_AUDIO_CTL_2 WCD9335_REG(0x02, 0x084) + +/* Page-6 Registers */ +#define WCD9335_PAGE6_PAGE_REGISTER WCD9335_REG(0x06, 0x000) +#define WCD9335_ANA_BIAS WCD9335_REG(0x06, 0x001) +#define WCD9335_ANA_BIAS_EN_MASK BIT(7) +#define WCD9335_ANA_BIAS_ENABLE BIT(7) +#define WCD9335_ANA_BIAS_DISABLE 0 +#define WCD9335_ANA_BIAS_PRECHRG_EN_MASK BIT(6) +#define WCD9335_ANA_BIAS_PRECHRG_ENABLE BIT(6) +#define WCD9335_ANA_BIAS_PRECHRG_DISABLE 0 +#define WCD9335_ANA_BIAS_PRECHRG_CTL_MODE BIT(5) +#define WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_AUTO BIT(5) +#define WCD9335_ANA_BIAS_PRECHRG_CTL_MODE_MANUAL 0 +#define WCD9335_ANA_CLK_TOP WCD9335_REG(0x06, 0x002) +#define WCD9335_ANA_CLK_MCLK_EN_MASK BIT(2) +#define WCD9335_ANA_CLK_MCLK_ENABLE BIT(2) +#define WCD9335_ANA_CLK_MCLK_DISABLE 0 +#define WCD9335_ANA_CLK_MCLK_SRC_MASK BIT(3) +#define WCD9335_ANA_CLK_MCLK_SRC_RCO BIT(3) +#define WCD9335_ANA_CLK_MCLK_SRC_EXTERNAL 0 +#define WCD9335_ANA_CLK_EXT_CLKBUF_EN_MASK BIT(7) +#define WCD9335_ANA_CLK_EXT_CLKBUF_ENABLE BIT(7) +#define WCD9335_ANA_CLK_EXT_CLKBUF_DISABLE 0 +#define WCD9335_ANA_RCO WCD9335_REG(0x06, 0x003) +#define WCD9335_ANA_RCO_BG_EN_MASK BIT(7) +#define WCD9335_ANA_RCO_BG_ENABLE BIT(7) +#define WCD9335_ANA_BUCK_VOUT_D WCD9335_REG(0x06, 0x005) +#define WCD9335_ANA_BUCK_VOUT_MASK GENMASK(7, 0) +#define WCD9335_ANA_BUCK_CTL WCD9335_REG(0x06, 0x006) +#define WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_MASK BIT(1) +#define WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_EXT BIT(1) +#define WCD9335_ANA_BUCK_CTL_VOUT_D_IREF_INT 0 +#define WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_MASK BIT(2) +#define WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_EXT BIT(2) +#define WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_INT 0 +#define WCD9335_ANA_BUCK_CTL_RAMP_START_MASK BIT(7) +#define WCD9335_ANA_BUCK_CTL_RAMP_START_ENABLE BIT(7) +#define WCD9335_ANA_BUCK_CTL_RAMP_START_DISABLE 0 +#define WCD9335_ANA_RX_SUPPLIES WCD9335_REG(0x06, 0x008) +#define WCD9335_ANA_RX_BIAS_ENABLE_MASK BIT(0) +#define WCD9335_ANA_RX_BIAS_ENABLE BIT(0) +#define WCD9335_ANA_RX_BIAS_DISABLE 0 +#define WCD9335_ANA_HPH WCD9335_REG(0x06, 0x009) +#define WCD9335_ANA_EAR WCD9335_REG(0x06, 0x00a) +#define WCD9335_ANA_LO_1_2 WCD9335_REG(0x06, 0x00b) +#define WCD9335_ANA_LO_3_4 WCD9335_REG(0x06, 0x00c) +#define WCD9335_ANA_AMIC1 WCD9335_REG(0x06, 0x00e) +#define WCD9335_ANA_AMIC2 WCD9335_REG(0x06, 0x00f) +#define WCD9335_ANA_AMIC3 WCD9335_REG(0x06, 0x010) +#define WCD9335_ANA_AMIC4 WCD9335_REG(0x06, 0x011) +#define WCD9335_ANA_AMIC5 WCD9335_REG(0x06, 0x012) +#define WCD9335_ANA_AMIC6 WCD9335_REG(0x06, 0x013) +#define WCD9335_ANA_MBHC_MECH WCD9335_REG(0x06, 0x014) +#define WCD9335_MBHC_L_DET_EN_MASK BIT(7) +#define WCD9335_MBHC_L_DET_EN BIT(7) +#define WCD9335_MBHC_GND_DET_EN_MASK BIT(6) +#define WCD9335_MBHC_MECH_DETECT_TYPE_MASK BIT(5) +#define WCD9335_MBHC_MECH_DETECT_TYPE_SHIFT 5 +#define WCD9335_MBHC_HPHL_PLUG_TYPE_MASK BIT(4) +#define WCD9335_MBHC_HPHL_PLUG_TYPE_NO BIT(4) +#define WCD9335_MBHC_GND_PLUG_TYPE_MASK BIT(3) +#define WCD9335_MBHC_GND_PLUG_TYPE_NO BIT(3) +#define WCD9335_MBHC_HSL_PULLUP_COMP_EN BIT(2) +#define WCD9335_MBHC_HPHL_100K_TO_GND_EN BIT(0) + +#define WCD9335_ANA_MBHC_ELECT WCD9335_REG(0x06, 0x015) +#define WCD9335_ANA_MBHC_BD_ISRC_CTL_MASK GENMASK(6, 4) +#define WCD9335_ANA_MBHC_BD_ISRC_100UA GENMASK(5, 4) +#define WCD9335_ANA_MBHC_BD_ISRC_OFF 0 +#define WCD9335_ANA_MBHC_BIAS_EN_MASK BIT(0) +#define WCD9335_ANA_MBHC_BIAS_EN BIT(0) +#define WCD9335_ANA_MBHC_ZDET WCD9335_REG(0x06, 0x016) +#define WCD9335_ANA_MBHC_RESULT_1 WCD9335_REG(0x06, 0x017) +#define WCD9335_ANA_MBHC_RESULT_2 WCD9335_REG(0x06, 0x018) +#define WCD9335_ANA_MBHC_RESULT_3 WCD9335_REG(0x06, 0x019) +#define WCD9335_MBHC_BTN_RESULT_MASK GENMASK(2, 0) +#define WCD9335_ANA_MBHC_BTN0 WCD9335_REG(0x06, 0x01a) +#define WCD9335_ANA_MBHC_BTN1 WCD9335_REG(0x06, 0x01b) +#define WCD9335_ANA_MBHC_BTN2 WCD9335_REG(0x06, 0x01c) +#define WCD9335_ANA_MBHC_BTN3 WCD9335_REG(0x06, 0x01d) +#define WCD9335_ANA_MBHC_BTN4 WCD9335_REG(0x06, 0x01e) +#define WCD9335_ANA_MBHC_BTN5 WCD9335_REG(0x06, 0x01f) +#define WCD9335_ANA_MBHC_BTN6 WCD9335_REG(0x06, 0x020) +#define WCD9335_ANA_MBHC_BTN7 WCD9335_REG(0x06, 0x021) +#define WCD9335_ANA_MICB1 WCD9335_REG(0x06, 0x022) +#define WCD9335_ANA_MICB2 WCD9335_REG(0x06, 0x023) +#define WCD9335_ANA_MICB2_ENABLE BIT(6) +#define WCD9335_ANA_MICB2_RAMP WCD9335_REG(0x06, 0x024) +#define WCD9335_ANA_MICB3 WCD9335_REG(0x06, 0x025) +#define WCD9335_ANA_MICB4 WCD9335_REG(0x06, 0x026) +#define WCD9335_ANA_VBADC WCD9335_REG(0x06, 0x027) +#define WCD9335_BIAS_VBG_FINE_ADJ WCD9335_REG(0x06, 0x029) +#define WCD9335_RCO_CTRL_2 WCD9335_REG(0x06, 0x02f) +#define WCD9335_SIDO_SIDO_CCL_2 WCD9335_REG(0x06, 0x042) +#define WCD9335_SIDO_SIDO_CCL_4 WCD9335_REG(0x06, 0x044) +#define WCD9335_SIDO_SIDO_CCL_8 WCD9335_REG(0x06, 0x048) +#define WCD9335_SIDO_SIDO_CCL_10 WCD9335_REG(0x06, 0x04a) +#define WCD9335_SIDO_SIDO_CCL_10_ICHARG_PWR_SEL_C320FF 0x2 +/* Comparator 1 and 2 Bias current at 1P0UA with start pulse width of C320FF */ +#define WCD9335_SIDO_SIDO_CCL_DEF_VALUE 0x6e +#define WCD9335_SIDO_SIDO_TEST_2 WCD9335_REG(0x06, 0x055) +#define WCD9335_MBHC_CTL_1 WCD9335_REG(0x06, 0x056) +#define WCD9335_MBHC_BTN_DBNC_MASK GENMASK(1, 0) +#define WCD9335_MBHC_BTN_DBNC_T_16_MS 0x2 +#define WCD9335_MBHC_CTL_RCO_EN_MASK BIT(7) +#define WCD9335_MBHC_CTL_RCO_EN BIT(7) + +#define WCD9335_MBHC_CTL_2 WCD9335_REG(0x06, 0x057) +#define WCD9335_MBHC_HS_VREF_CTL_MASK GENMASK(1, 0) +#define WCD9335_MBHC_HS_VREF_1P5_V 0x1 +#define WCD9335_MBHC_PLUG_DETECT_CTL WCD9335_REG(0x06, 0x058) +#define WCD9335_MBHC_HSDET_PULLUP_CTL_MASK GENMASK(7, 6) +#define WCD9335_MBHC_HSDET_PULLUP_CTL_SHIFT 6 +#define WCD9335_MBHC_HSDET_PULLUP_CTL_1_2P0_UA 0x80 +#define WCD9335_MBHC_DBNC_TIMER_INSREM_DBNC_T_96_MS 0x6 + +#define WCD9335_MBHC_ZDET_RAMP_CTL WCD9335_REG(0x06, 0x05a) +#define WCD9335_VBADC_IBIAS_FE WCD9335_REG(0x06, 0x05e) +#define WCD9335_FLYBACK_CTRL_1 WCD9335_REG(0x06, 0x0b1) +#define WCD9335_RX_BIAS_HPH_PA WCD9335_REG(0x06, 0x0bb) +#define WCD9335_RX_BIAS_HPH_PA_AMP_5_UA_MASK GENMASK(3, 0) +#define WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2 WCD9335_REG(0x06, 0x0bc) +#define WCD9335_RX_BIAS_HPH_RDAC_LDO WCD9335_REG(0x06, 0x0bd) +#define WCD9335_RX_BIAS_FLYB_BUFF WCD9335_REG(0x06, 0x0c7) +#define WCD9335_RX_BIAS_FLYB_VPOS_5_UA_MASK GENMASK(3, 0) +#define WCD9335_RX_BIAS_FLYB_I_0P0_UA 0 +#define WCD9335_RX_BIAS_FLYB_VNEG_5_UA_MASK GENMASK(7, 4) +#define WCD9335_RX_BIAS_FLYB_MID_RST WCD9335_REG(0x06, 0x0c8) +#define WCD9335_HPH_CNP_WG_CTL WCD9335_REG(0x06, 0x0cc) +#define WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_MASK GENMASK(2, 0) +#define WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_500 0x2 +#define WCD9335_HPH_CNP_WG_CTL_CURR_LDIV_RATIO_1000 0x3 +#define WCD9335_HPH_OCP_CTL WCD9335_REG(0x06, 0x0ce) +#define WCD9335_HPH_AUTO_CHOP WCD9335_REG(0x06, 0x0cf) +#define WCD9335_HPH_AUTO_CHOP_MASK BIT(5) +#define WCD9335_HPH_AUTO_CHOP_FORCE_ENABLE BIT(5) +#define WCD9335_HPH_AUTO_CHOP_ENABLE_BY_CMPDR_GAIN 0 +#define WCD9335_HPH_PA_CTL1 WCD9335_REG(0x06, 0x0d1) +#define WCD9335_HPH_PA_GM3_IB_SCALE_MASK GENMASK(3, 1) +#define WCD9335_HPH_PA_CTL2 WCD9335_REG(0x06, 0x0d2) +#define WCD9335_HPH_PA_CTL2_FORCE_PSRREH_MASK BIT(2) +#define WCD9335_HPH_PA_CTL2_FORCE_PSRREH_ENABLE BIT(2) +#define WCD9335_HPH_PA_CTL2_FORCE_PSRREH_DISABLE 0 +#define WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_MASK BIT(3) +#define WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_ENABLE BIT(3) +#define WCD9335_HPH_PA_CTL2_FORCE_IQCTRL_DISABLE 0 +#define WCD9335_HPH_PA_CTL2_HPH_PSRR_ENH_MASK BIT(5) +#define WCD9335_HPH_PA_CTL2_HPH_PSRR_ENABLE BIT(5) +#define WCD9335_HPH_PA_CTL2_HPH_PSRR_DISABLE 0 +#define WCD9335_HPH_L_EN WCD9335_REG(0x06, 0x0d3) +#define WCD9335_HPH_CONST_SEL_L_MASK GENMASK(7, 6) +#define WCD9335_HPH_CONST_SEL_L_BYPASS 0 +#define WCD9335_HPH_CONST_SEL_L_LP_PATH 0x40 +#define WCD9335_HPH_CONST_SEL_L_HQ_PATH 0x80 +#define WCD9335_HPH_PA_GAIN_MASK GENMASK(4, 0) +#define WCD9335_HPH_GAIN_SRC_SEL_MASK BIT(5) +#define WCD9335_HPH_GAIN_SRC_SEL_COMPANDER 0 +#define WCD9335_HPH_GAIN_SRC_SEL_REGISTER BIT(5) +#define WCD9335_HPH_L_TEST WCD9335_REG(0x06, 0x0d4) +#define WCD9335_HPH_R_EN WCD9335_REG(0x06, 0x0d6) +#define WCD9335_HPH_R_TEST WCD9335_REG(0x06, 0x0d7) +#define WCD9335_HPH_R_ATEST WCD9335_REG(0x06, 0x0d8) +#define WCD9335_HPH_RDAC_LDO_CTL WCD9335_REG(0x06, 0x0db) +#define WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_MASK GENMASK(2, 0) +#define WCD9335_HPH_RDAC_N1P65_LD_OUTCTL_V_N1P60 0x1 +#define WCD9335_HPH_RDAC_1P65_LD_OUTCTL_MASK GENMASK(6, 4) +#define WCD9335_HPH_RDAC_1P65_LD_OUTCTL_V_N1P60 0x10 +#define WCD9335_HPH_REFBUFF_LP_CTL WCD9335_REG(0x06, 0x0de) +#define WCD9335_HPH_L_DAC_CTL WCD9335_REG(0x06, 0x0df) +#define WCD9335_HPH_DAC_LDO_POWERMODE_MASK BIT(0) +#define WCD9335_HPH_DAC_LDO_POWERMODE_LOWPOWER 0 +#define WCD9335_HPH_DAC_LDO_POWERMODE_UHQA BIT(0) +#define WCD9335_HPH_DAC_LDO_UHQA_OV_MASK BIT(1) +#define WCD9335_HPH_DAC_LDO_UHQA_OV_ENABLE BIT(1) +#define WCD9335_HPH_DAC_LDO_UHQA_OV_DISABLE 0 + +#define WCD9335_EAR_CMBUFF WCD9335_REG(0x06, 0x0e2) +#define WCD9335_DIFF_LO_LO2_COMPANDER WCD9335_REG(0x06, 0x0ea) +#define WCD9335_DIFF_LO_LO1_COMPANDER WCD9335_REG(0x06, 0x0eb) +#define WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ WCD9335_REG(0x06, 0x0f1) +#define WCD9335_DIFF_LO_COM_PA_FREQ WCD9335_REG(0x06, 0x0f2) +#define WCD9335_SE_LO_LO3_GAIN WCD9335_REG(0x06, 0x0f8) +#define WCD9335_SE_LO_LO3_CTRL WCD9335_REG(0x06, 0x0f9) +#define WCD9335_SE_LO_LO4_GAIN WCD9335_REG(0x06, 0x0fa) + +/* Page-10 Registers */ +#define WCD9335_CDC_TX0_TX_PATH_CTL WCD9335_REG(0x0a, 0x031) +#define WCD9335_CDC_TX_PATH_CTL_PCM_RATE_MASK GENMASK(3, 0) +#define WCD9335_CDC_TX_PATH_CTL(dec) WCD9335_REG(0xa, (0x31 + dec * 0x10)) +#define WCD9335_CDC_TX0_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x032) +#define WCD9335_CDC_TX_ADC_AMIC_DMIC_SEL_MASK BIT(7) +#define WCD9335_CDC_TX_ADC_DMIC_SEL BIT(7) +#define WCD9335_CDC_TX_ADC_AMIC_SEL 0 +#define WCD9335_CDC_TX0_TX_VOL_CTL WCD9335_REG(0x0a, 0x034) +#define WCD9335_CDC_TX0_TX_PATH_SEC2 WCD9335_REG(0x0a, 0x039) +#define WCD9335_CDC_TX0_TX_PATH_SEC7 WCD9335_REG(0x0a, 0x03e) +#define WCD9335_CDC_TX1_TX_PATH_CTL WCD9335_REG(0x0a, 0x041) +#define WCD9335_CDC_TX1_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x042) +#define WCD9335_CDC_TX2_TX_PATH_CTL WCD9335_REG(0x0a, 0x051) +#define WCD9335_CDC_TX2_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x052) +#define WCD9335_CDC_TX2_TX_VOL_CTL WCD9335_REG(0x0a, 0x054) +#define WCD9335_CDC_TX3_TX_PATH_CTL WCD9335_REG(0x0a, 0x061) +#define WCD9335_CDC_TX3_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x062) +#define WCD9335_CDC_TX3_TX_VOL_CTL WCD9335_REG(0x0a, 0x064) +#define WCD9335_CDC_TX4_TX_PATH_CTL WCD9335_REG(0x0a, 0x071) +#define WCD9335_CDC_TX4_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x072) +#define WCD9335_CDC_TX4_TX_VOL_CTL WCD9335_REG(0x0a, 0x074) +#define WCD9335_CDC_TX5_TX_PATH_CTL WCD9335_REG(0x0a, 0x081) +#define WCD9335_CDC_TX5_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x082) +#define WCD9335_CDC_TX5_TX_VOL_CTL WCD9335_REG(0x0a, 0x084) +#define WCD9335_CDC_TX6_TX_PATH_CTL WCD9335_REG(0x0a, 0x091) +#define WCD9335_CDC_TX6_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x092) +#define WCD9335_CDC_TX6_TX_VOL_CTL WCD9335_REG(0x0a, 0x094) +#define WCD9335_CDC_TX7_TX_PATH_CTL WCD9335_REG(0x0a, 0x0a1) +#define WCD9335_CDC_TX7_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x0a2) +#define WCD9335_CDC_TX7_TX_VOL_CTL WCD9335_REG(0x0a, 0x0a4) +#define WCD9335_CDC_TX8_TX_PATH_CTL WCD9335_REG(0x0a, 0x0b1) +#define WCD9335_CDC_TX8_TX_PATH_CFG0 WCD9335_REG(0x0a, 0x0b2) +#define WCD9335_CDC_TX8_TX_VOL_CTL WCD9335_REG(0x0a, 0x0b4) +#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0c3) +#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0c7) +#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0cb) +#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0 WCD9335_REG(0x0a, 0x0cf) + +/* Page-11 Registers */ +#define WCD9335_PAGE11_PAGE_REGISTER WCD9335_REG(0x0b, 0x000) +#define WCD9335_CDC_COMPANDER1_CTL0 WCD9335_REG(0x0b, 0x001) +#define WCD9335_CDC_COMPANDER1_CTL(c) WCD9335_REG(0x0b, (0x001 + c * 0x8)) +#define WCD9335_CDC_COMPANDER_CLK_EN_MASK BIT(0) +#define WCD9335_CDC_COMPANDER_CLK_ENABLE BIT(0) +#define WCD9335_CDC_COMPANDER_CLK_DISABLE 0 +#define WCD9335_CDC_COMPANDER_SOFT_RST_MASK BIT(1) +#define WCD9335_CDC_COMPANDER_SOFT_RST_ENABLE BIT(1) +#define WCD9335_CDC_COMPANDER_SOFT_RST_DISABLE 0 +#define WCD9335_CDC_COMPANDER_HALT_MASK BIT(2) +#define WCD9335_CDC_COMPANDER_HALT BIT(2) +#define WCD9335_CDC_COMPANDER_NOHALT 0 +#define WCD9335_CDC_COMPANDER7_CTL3 WCD9335_REG(0x0b, 0x034) +#define WCD9335_CDC_COMPANDER7_CTL7 WCD9335_REG(0x0b, 0x038) +#define WCD9335_CDC_COMPANDER8_CTL3 WCD9335_REG(0x0b, 0x03c) +#define WCD9335_CDC_COMPANDER8_CTL7 WCD9335_REG(0x0b, 0x040) +#define WCD9335_CDC_RX0_RX_PATH_CTL WCD9335_REG(0x0b, 0x041) +#define WCD9335_CDC_RX_PGA_MUTE_EN_MASK BIT(4) +#define WCD9335_CDC_RX_PGA_MUTE_ENABLE BIT(4) +#define WCD9335_CDC_RX_PGA_MUTE_DISABLE 0 +#define WCD9335_CDC_RX_CLK_EN_MASK BIT(5) +#define WCD9335_CDC_RX_CLK_ENABLE BIT(5) +#define WCD9335_CDC_RX_CLK_DISABLE 0 +#define WCD9335_CDC_RX_RESET_MASK BIT(6) +#define WCD9335_CDC_RX_RESET_ENABLE BIT(6) +#define WCD9335_CDC_RX_RESET_DISABLE 0 +#define WCD9335_CDC_RX_PATH_CTL(rx) WCD9335_REG(0x0b, (0x041 + rx * 0x14)) +#define WCD9335_CDC_RX0_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x042) +#define WCD9335_CDC_RX0_RX_PATH_CFG1 WCD9335_REG(0x0b, 0x043) +#define WCD9335_CDC_RX0_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x044) +#define WCD9335_CDC_RX0_RX_VOL_CTL WCD9335_REG(0x0b, 0x045) +#define WCD9335_CDC_RX0_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x046) +#define WCD9335_CDC_MIX_PCM_RATE_MASK GENMASK(3, 0) +#define WCD9335_CDC_RX_PATH_MIX_CTL(rx) WCD9335_REG(0x0b, (0x46 + rx * 0x14)) +#define WCD9335_CDC_RX0_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x047) +#define WCD9335_CDC_RX0_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x048) +#define WCD9335_CDC_RX0_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x049) +#define WCD9335_CDC_RX0_RX_PATH_SEC7 WCD9335_REG(0x0b, 0x050) +#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC0 WCD9335_REG(0x0b, 0x051) +#define WCD9335_CDC_RX1_RX_PATH_CTL WCD9335_REG(0x0b, 0x055) +#define WCD9335_CDC_RX1_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x056) +#define WCD9335_CDC_RX1_RX_PATH_CFG(c) WCD9335_REG(0x0b, (0x056 + c * 0x14)) +#define WCD9335_CDC_RX_PATH_CFG_CMP_EN_MASK BIT(1) +#define WCD9335_CDC_RX_PATH_CFG_CMP_ENABLE BIT(1) +#define WCD9335_CDC_RX_PATH_CFG_CMP_DISABLE 0 +#define WCD9335_CDC_RX_PATH_CFG_HD2_EN_MASK BIT(2) +#define WCD9335_CDC_RX_PATH_CFG_HD2_ENABLE BIT(2) +#define WCD9335_CDC_RX_PATH_CFG_HD2_DISABLE 0 +#define WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN_MASK BIT(3) +#define WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_EN BIT(3) +#define WCD9335_CDC_RX_PATH_CFG0_DLY_ZN_DISABLE 0 +#define WCD9335_CDC_RX1_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x058) +#define WCD9335_CDC_RX1_RX_VOL_CTL WCD9335_REG(0x0b, 0x059) +#define WCD9335_CDC_RX1_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x05a) +#define WCD9335_CDC_RX1_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x05b) +#define WCD9335_CDC_RX1_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x05c) +#define WCD9335_CDC_RX1_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x05d) +#define WCD9335_CDC_RX1_RX_PATH_SEC3 WCD9335_REG(0x0b, 0x060) +#define WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_MASK GENMASK(1, 0) +#define WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_2 0x1 +#define WCD9335_CDC_RX_PATH_SEC_HD2_SCALE_1 0 +#define WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_MASK GENMASK(5, 2) +#define WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P2500 0x10 +#define WCD9335_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000 0 +#define WCD9335_CDC_RX2_RX_PATH_CTL WCD9335_REG(0x0b, 0x069) +#define WCD9335_CDC_RX2_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x06a) +#define WCD9335_CDC_RX2_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x06c) +#define WCD9335_CDC_RX2_RX_VOL_CTL WCD9335_REG(0x0b, 0x06d) +#define WCD9335_CDC_RX2_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x06e) +#define WCD9335_CDC_RX2_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x06f) +#define WCD9335_CDC_RX2_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x070) +#define WCD9335_CDC_RX2_RX_PATH_SEC0 WCD9335_REG(0x0b, 0x071) +#define WCD9335_CDC_RX_PATH_DEM_INP_SEL_MASK GENMASK(1, 0) +#define WCD9335_CDC_RX2_RX_PATH_SEC3 WCD9335_REG(0x0b, 0x074) +#define WCD9335_CDC_RX3_RX_PATH_CTL WCD9335_REG(0x0b, 0x07d) +#define WCD9335_CDC_RX3_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x07e) +#define WCD9335_CDC_RX3_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x080) +#define WCD9335_CDC_RX3_RX_VOL_CTL WCD9335_REG(0x0b, 0x081) +#define WCD9335_CDC_RX3_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x082) +#define WCD9335_CDC_RX3_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x083) +#define WCD9335_CDC_RX3_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x084) +#define WCD9335_CDC_RX4_RX_PATH_CTL WCD9335_REG(0x0b, 0x091) +#define WCD9335_CDC_RX4_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x092) +#define WCD9335_CDC_RX4_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x094) +#define WCD9335_CDC_RX4_RX_VOL_CTL WCD9335_REG(0x0b, 0x095) +#define WCD9335_CDC_RX4_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x096) +#define WCD9335_CDC_RX4_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x097) +#define WCD9335_CDC_RX4_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x098) +#define WCD9335_CDC_RX5_RX_PATH_CTL WCD9335_REG(0x0b, 0x0a5) +#define WCD9335_CDC_RX5_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0a6) +#define WCD9335_CDC_RX5_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0a8) +#define WCD9335_CDC_RX5_RX_VOL_CTL WCD9335_REG(0x0b, 0x0a9) +#define WCD9335_CDC_RX5_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0aa) +#define WCD9335_CDC_RX5_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0ab) +#define WCD9335_CDC_RX5_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0ac) +#define WCD9335_CDC_RX6_RX_PATH_CTL WCD9335_REG(0x0b, 0x0b9) +#define WCD9335_CDC_RX6_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0ba) +#define WCD9335_CDC_RX6_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0bc) +#define WCD9335_CDC_RX6_RX_VOL_CTL WCD9335_REG(0x0b, 0x0bd) +#define WCD9335_CDC_RX6_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0be) +#define WCD9335_CDC_RX6_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0bf) +#define WCD9335_CDC_RX6_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0c0) +#define WCD9335_CDC_RX7_RX_PATH_CTL WCD9335_REG(0x0b, 0x0cd) +#define WCD9335_CDC_RX7_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0ce) +#define WCD9335_CDC_RX7_RX_PATH_CFG1 WCD9335_REG(0x0b, 0x0cf) +#define WCD9335_CDC_RX7_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0d0) +#define WCD9335_CDC_RX7_RX_VOL_CTL WCD9335_REG(0x0b, 0x0d1) +#define WCD9335_CDC_RX7_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0d2) +#define WCD9335_CDC_RX7_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0d3) +#define WCD9335_CDC_RX7_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0d4) +#define WCD9335_CDC_RX8_RX_PATH_CTL WCD9335_REG(0x0b, 0x0e1) +#define WCD9335_CDC_RX8_RX_PATH_CFG0 WCD9335_REG(0x0b, 0x0e2) +#define WCD9335_CDC_RX8_RX_PATH_CFG1 WCD9335_REG(0x0b, 0x0e3) +#define WCD9335_CDC_RX8_RX_PATH_CFG2 WCD9335_REG(0x0b, 0x0e4) +#define WCD9335_CDC_RX8_RX_VOL_CTL WCD9335_REG(0x0b, 0x0e5) +#define WCD9335_CDC_RX8_RX_PATH_MIX_CTL WCD9335_REG(0x0b, 0x0e6) +#define WCD9335_CDC_RX8_RX_PATH_MIX_CFG WCD9335_REG(0x0b, 0x0e7) +#define WCD9335_CDC_RX8_RX_VOL_MIX_CTL WCD9335_REG(0x0b, 0x0e8) + +/* Page-12 Registers */ +#define WCD9335_PAGE12_PAGE_REGISTER WCD9335_REG(0x0c, 0x000) +#define WCD9335_CDC_CLSH_K2_MSB WCD9335_REG(0x0c, 0x00a) +#define WCD9335_CDC_CLSH_K2_LSB WCD9335_REG(0x0c, 0x00b) +#define WCD9335_CDC_BOOST0_BOOST_CTL WCD9335_REG(0x0c, 0x01a) +#define WCD9335_CDC_BOOST0_BOOST_CFG1 WCD9335_REG(0x0c, 0x01b) +#define WCD9335_CDC_BOOST0_BOOST_CFG2 WCD9335_REG(0x0c, 0x01c) +#define WCD9335_CDC_BOOST1_BOOST_CTL WCD9335_REG(0x0c, 0x022) +#define WCD9335_CDC_BOOST1_BOOST_CFG1 WCD9335_REG(0x0c, 0x023) +#define WCD9335_CDC_BOOST1_BOOST_CFG2 WCD9335_REG(0x0c, 0x024) + +/* Page-13 Registers */ +#define WCD9335_PAGE13_PAGE_REGISTER WCD9335_REG(0x0d, 0x000) +#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0 WCD9335_REG(0x0d, 0x001) +#define WCD9335_CDC_RX_INP_MUX_RX_INT_CFG0(i) WCD9335_REG(0xd, (0x1 + i * 0x2)) +#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1 WCD9335_REG(0xd, 0x002) +#define WCD9335_CDC_RX_INP_MUX_RX_INT_SEL_MASK GENMASK(3, 0) +#define WCD9335_CDC_RX_INP_MUX_RX_INT_CFG1(i) WCD9335_REG(0xd, (0x2 + i * 0x2)) + +#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 WCD9335_REG(0x0d, 0x003) +#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1 WCD9335_REG(0x0d, 0x004) +#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0 WCD9335_REG(0x0d, 0x005) +#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1 WCD9335_REG(0x0d, 0x006) +#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0 WCD9335_REG(0x0d, 0x007) +#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1 WCD9335_REG(0x0d, 0x008) +#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0 WCD9335_REG(0x0d, 0x009) +#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1 WCD9335_REG(0x0d, 0x00a) +#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0 WCD9335_REG(0x0d, 0x00b) +#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1 WCD9335_REG(0x0d, 0x00c) +#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0 WCD9335_REG(0x0d, 0x00d) +#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1 WCD9335_REG(0x0d, 0x00e) +#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0 WCD9335_REG(0x0d, 0x00f) +#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1 WCD9335_REG(0x0d, 0x010) +#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0 WCD9335_REG(0x0d, 0x011) +#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1 WCD9335_REG(0x0d, 0x012) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 WCD9335_REG(0x0d, 0x01d) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 WCD9335_REG(0x0d, 0x01e) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0 WCD9335_REG(0x0d, 0x01f) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1 WCD9335_REG(0x0d, 0x020) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0 WCD9335_REG(0x0d, 0x021) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1 WCD9335_REG(0x0d, 0x022) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0 WCD9335_REG(0x0d, 0x023) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1 WCD9335_REG(0x0d, 0x024) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 WCD9335_REG(0x0d, 0x025) +#define WCD9335_CDC_TX_INP_MUX_SEL_AMIC 0x1 +#define WCD9335_CDC_TX_INP_MUX_SEL_DMIC 0 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0 WCD9335_REG(0x0d, 0x026) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0 WCD9335_REG(0x0d, 0x027) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0 WCD9335_REG(0x0d, 0x028) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0 WCD9335_REG(0x0d, 0x029) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0 WCD9335_REG(0x0d, 0x02b) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0 WCD9335_REG(0x0d, 0x02c) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0 WCD9335_REG(0x0d, 0x02d) +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0 WCD9335_REG(0x0d, 0x02e) +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0 WCD9335_REG(0x0d, 0x03a) +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1 WCD9335_REG(0x0d, 0x03b) +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2 WCD9335_REG(0x0d, 0x03c) +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3 WCD9335_REG(0x0d, 0x03d) +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL WCD9335_REG(0x0d, 0x041) +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_EN_MASK BIT(0) +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_ENABLE BIT(0) +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_DISABLE 0 +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL WCD9335_REG(0x0d, 0x042) +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_EN_MASK BIT(0) +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_ENABLE BIT(0) +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_DISABLE 0 +#define WCD9335_CDC_TOP_TOP_CFG1 WCD9335_REG(0x0d, 0x082) +#define WCD9335_MAX_REGISTER 0xffff +#define WCD9335_SEL_REGISTER 0x800 + +/* SLIMBUS Slave Registers */ +#define WCD9335_SLIM_PGD_PORT_INT_EN0 WCD9335_REG(0, 0x30) +#define WCD9335_SLIM_PGD_PORT_INT_STATUS_RX_0 WCD9335_REG(0, 0x34) +#define WCD9335_SLIM_PGD_PORT_INT_STATUS_RX_1 WCD9335_REG(0, 0x35) +#define WCD9335_SLIM_PGD_PORT_INT_STATUS_TX_0 WCD9335_REG(0, 0x36) +#define WCD9335_SLIM_PGD_PORT_INT_STATUS_TX_1 WCD9335_REG(0, 0x37) +#define WCD9335_SLIM_PGD_PORT_INT_CLR_RX_0 WCD9335_REG(0, 0x38) +#define WCD9335_SLIM_PGD_PORT_INT_CLR_RX_1 WCD9335_REG(0, 0x39) +#define WCD9335_SLIM_PGD_PORT_INT_CLR_TX_0 WCD9335_REG(0, 0x3A) +#define WCD9335_SLIM_PGD_PORT_INT_CLR_TX_1 WCD9335_REG(0, 0x3B) +#define WCD9335_SLIM_PGD_PORT_INT_RX_SOURCE0 WCD9335_REG(0, 0x60) +#define WCD9335_SLIM_PGD_PORT_INT_TX_SOURCE0 WCD9335_REG(0, 0x70) +#define WCD9335_SLIM_PGD_RX_PORT_CFG(p) WCD9335_REG(0, (0x30 + p)) +#define WCD9335_SLIM_PGD_PORT_CFG(p) WCD9335_REG(0, (0x40 + p)) +#define WCD9335_SLIM_PGD_TX_PORT_CFG(p) WCD9335_REG(0, (0x50 + p)) +#define WCD9335_SLIM_PGD_PORT_INT_SRC(p) WCD9335_REG(0, (0x60 + p)) +#define WCD9335_SLIM_PGD_PORT_INT_STATUS(p) WCD9335_REG(0, (0x80 + p)) +#define WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_0(p) WCD9335_REG(0, (0x100 + 4 * p)) +/* ports range from 10-16 */ +#define WCD9335_SLIM_PGD_TX_PORT_MULTI_CHNL_1(p) WCD9335_REG(0, (0x101 + 4 * p)) +#define WCD9335_SLIM_PGD_RX_PORT_MULTI_CHNL_0(p) WCD9335_REG(0, (0x140 + 4 * p)) + +#define WCD9335_IRQ_SLIMBUS 0 +#define WCD9335_IRQ_MBHC_SW_DET 8 +#define WCD9335_IRQ_MBHC_ELECT_INS_REM_DET 9 +#define WCD9335_IRQ_MBHC_BUTTON_PRESS_DET 10 +#define WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET 11 +#define WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET 12 + +#define SLIM_MANF_ID_QCOM 0x217 +#define SLIM_PROD_CODE_WCD9335 0x1a0 + +#define WCD9335_VERSION_2_0 2 +#define WCD9335_MAX_SUPPLY 5 + +#endif /* __WCD9335_H__ */ diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c new file mode 100644 index 0000000000..1b6e376f38 --- /dev/null +++ b/sound/soc/codecs/wcd934x.c @@ -0,0 +1,5974 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019, Linaro Limited + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd-clsh-v2.h" +#include "wcd-mbhc-v2.h" + +#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +/* Fractional Rates */ +#define WCD934X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400) +#define WCD934X_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +/* slave port water mark level + * (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes) + */ +#define SLAVE_PORT_WATER_MARK_6BYTES 0 +#define SLAVE_PORT_WATER_MARK_9BYTES 1 +#define SLAVE_PORT_WATER_MARK_12BYTES 2 +#define SLAVE_PORT_WATER_MARK_15BYTES 3 +#define SLAVE_PORT_WATER_MARK_SHIFT 1 +#define SLAVE_PORT_ENABLE 1 +#define SLAVE_PORT_DISABLE 0 +#define WCD934X_SLIM_WATER_MARK_VAL \ + ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \ + (SLAVE_PORT_ENABLE)) + +#define WCD934X_SLIM_NUM_PORT_REG 3 +#define WCD934X_SLIM_PGD_PORT_INT_TX_EN0 (WCD934X_SLIM_PGD_PORT_INT_EN0 + 2) +#define WCD934X_SLIM_IRQ_OVERFLOW BIT(0) +#define WCD934X_SLIM_IRQ_UNDERFLOW BIT(1) +#define WCD934X_SLIM_IRQ_PORT_CLOSED BIT(2) + +#define WCD934X_MCLK_CLK_12P288MHZ 12288000 +#define WCD934X_MCLK_CLK_9P6MHZ 9600000 + +/* Only valid for 9.6 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ 2400000 +#define WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ 4800000 + +/* Only valid for 12.288 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ 4096000 + +#define WCD934X_DMIC_CLK_DIV_2 0x0 +#define WCD934X_DMIC_CLK_DIV_3 0x1 +#define WCD934X_DMIC_CLK_DIV_4 0x2 +#define WCD934X_DMIC_CLK_DIV_6 0x3 +#define WCD934X_DMIC_CLK_DIV_8 0x4 +#define WCD934X_DMIC_CLK_DIV_16 0x5 +#define WCD934X_DMIC_CLK_DRIVE_DEFAULT 0x02 + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define WCD934X_RX_START 16 +#define WCD934X_NUM_INTERPOLATORS 9 +#define WCD934X_RX_PATH_CTL_OFFSET 20 +#define WCD934X_MAX_VALID_ADC_MUX 13 +#define WCD934X_INVALID_ADC_MUX 9 + +#define WCD934X_SLIM_RX_CH(p) \ + {.port = p + WCD934X_RX_START, .shift = p,} + +#define WCD934X_SLIM_TX_CH(p) \ + {.port = p, .shift = p,} + +/* Feature masks to distinguish codec version */ +#define DSD_DISABLED_MASK 0 +#define SLNQ_DISABLED_MASK 1 + +#define DSD_DISABLED BIT(DSD_DISABLED_MASK) +#define SLNQ_DISABLED BIT(SLNQ_DISABLED_MASK) + +/* As fine version info cannot be retrieved before wcd probe. + * Define three coarse versions for possible future use before wcd probe. + */ +#define WCD_VERSION_WCD9340_1_0 0x400 +#define WCD_VERSION_WCD9341_1_0 0x410 +#define WCD_VERSION_WCD9340_1_1 0x401 +#define WCD_VERSION_WCD9341_1_1 0x411 +#define WCD934X_AMIC_PWR_LEVEL_LP 0 +#define WCD934X_AMIC_PWR_LEVEL_DEFAULT 1 +#define WCD934X_AMIC_PWR_LEVEL_HP 2 +#define WCD934X_AMIC_PWR_LEVEL_HYBRID 3 +#define WCD934X_AMIC_PWR_LVL_MASK 0x60 +#define WCD934X_AMIC_PWR_LVL_SHIFT 0x5 + +#define WCD934X_DEC_PWR_LVL_MASK 0x06 +#define WCD934X_DEC_PWR_LVL_LP 0x02 +#define WCD934X_DEC_PWR_LVL_HP 0x04 +#define WCD934X_DEC_PWR_LVL_DF 0x00 +#define WCD934X_DEC_PWR_LVL_HYBRID WCD934X_DEC_PWR_LVL_DF + +#define WCD934X_DEF_MICBIAS_MV 1800 +#define WCD934X_MAX_MICBIAS_MV 2850 + +#define WCD_IIR_FILTER_SIZE (sizeof(u32) * BAND_MAX) + +#define WCD_IIR_FILTER_CTL(xname, iidx, bidx) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = wcd934x_iir_filter_info, \ + .get = wcd934x_get_iir_band_audio_mixer, \ + .put = wcd934x_put_iir_band_audio_mixer, \ + .private_value = (unsigned long)&(struct wcd_iir_filter_ctl) { \ + .iir_idx = iidx, \ + .band_idx = bidx, \ + .bytes_ext = {.max = WCD_IIR_FILTER_SIZE, }, \ + } \ +} + +/* Z value defined in milliohm */ +#define WCD934X_ZDET_VAL_32 32000 +#define WCD934X_ZDET_VAL_400 400000 +#define WCD934X_ZDET_VAL_1200 1200000 +#define WCD934X_ZDET_VAL_100K 100000000 +/* Z floating defined in ohms */ +#define WCD934X_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE + +#define WCD934X_ZDET_NUM_MEASUREMENTS 900 +#define WCD934X_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define WCD934X_MBHC_GET_X1(x) (x & 0x3FFF) +/* Z value compared in milliOhm */ +#define WCD934X_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define WCD934X_MBHC_ZDET_CONST (86 * 16384) +#define WCD934X_MBHC_MOISTURE_RREF R_24_KOHM +#define WCD934X_MBHC_MAX_BUTTONS (8) +#define WCD_MBHC_HS_V_MAX 1600 + +#define WCD934X_INTERPOLATOR_PATH(id) \ + {"RX INT" #id "_1 MIX1 INP0", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP0", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP0", "IIR0", "IIR0"}, \ + {"RX INT" #id "_1 MIX1 INP0", "IIR1", "IIR1"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP1", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP1", "IIR0", "IIR0"}, \ + {"RX INT" #id "_1 MIX1 INP1", "IIR1", "IIR1"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_1 MIX1 INP2", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_1 MIX1 INP2", "IIR0", "IIR0"}, \ + {"RX INT" #id "_1 MIX1 INP2", "IIR1", "IIR1"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP0"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP1"}, \ + {"RX INT" #id "_1 MIX1", NULL, "RX INT" #id "_1 MIX1 INP2"}, \ + {"RX INT" #id "_2 MUX", "RX0", "SLIM RX0"}, \ + {"RX INT" #id "_2 MUX", "RX1", "SLIM RX1"}, \ + {"RX INT" #id "_2 MUX", "RX2", "SLIM RX2"}, \ + {"RX INT" #id "_2 MUX", "RX3", "SLIM RX3"}, \ + {"RX INT" #id "_2 MUX", "RX4", "SLIM RX4"}, \ + {"RX INT" #id "_2 MUX", "RX5", "SLIM RX5"}, \ + {"RX INT" #id "_2 MUX", "RX6", "SLIM RX6"}, \ + {"RX INT" #id "_2 MUX", "RX7", "SLIM RX7"}, \ + {"RX INT" #id "_2 MUX", NULL, "INT" #id "_CLK"}, \ + {"RX INT" #id "_2 MUX", NULL, "DSMDEM" #id "_CLK"}, \ + {"RX INT" #id "_2 INTERP", NULL, "RX INT" #id "_2 MUX"}, \ + {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_2 INTERP"}, \ + {"RX INT" #id "_1 INTERP", NULL, "RX INT" #id "_1 MIX1"}, \ + {"RX INT" #id "_1 INTERP", NULL, "INT" #id "_CLK"}, \ + {"RX INT" #id "_1 INTERP", NULL, "DSMDEM" #id "_CLK"}, \ + {"RX INT" #id " SEC MIX", NULL, "RX INT" #id "_1 INTERP"} + +#define WCD934X_INTERPOLATOR_MIX2(id) \ + {"RX INT" #id " MIX2", NULL, "RX INT" #id " SEC MIX"}, \ + {"RX INT" #id " MIX2", NULL, "RX INT" #id " MIX2 INP"} + +#define WCD934X_SLIM_RX_AIF_PATH(id) \ + {"SLIM RX"#id" MUX", "AIF1_PB", "AIF1 PB"}, \ + {"SLIM RX"#id" MUX", "AIF2_PB", "AIF2 PB"}, \ + {"SLIM RX"#id" MUX", "AIF3_PB", "AIF3 PB"}, \ + {"SLIM RX"#id" MUX", "AIF4_PB", "AIF4 PB"}, \ + {"SLIM RX"#id, NULL, "SLIM RX"#id" MUX"} + +#define WCD934X_ADC_MUX(id) \ + {"ADC MUX" #id, "DMIC", "DMIC MUX" #id }, \ + {"ADC MUX" #id, "AMIC", "AMIC MUX" #id }, \ + {"DMIC MUX" #id, "DMIC0", "DMIC0"}, \ + {"DMIC MUX" #id, "DMIC1", "DMIC1"}, \ + {"DMIC MUX" #id, "DMIC2", "DMIC2"}, \ + {"DMIC MUX" #id, "DMIC3", "DMIC3"}, \ + {"DMIC MUX" #id, "DMIC4", "DMIC4"}, \ + {"DMIC MUX" #id, "DMIC5", "DMIC5"}, \ + {"AMIC MUX" #id, "ADC1", "ADC1"}, \ + {"AMIC MUX" #id, "ADC2", "ADC2"}, \ + {"AMIC MUX" #id, "ADC3", "ADC3"}, \ + {"AMIC MUX" #id, "ADC4", "ADC4"} + +#define WCD934X_IIR_INP_MUX(id) \ + {"IIR" #id, NULL, "IIR" #id " INP0 MUX"}, \ + {"IIR" #id " INP0 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP0 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP0 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP0 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP0 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP0 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP0 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP0 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP0 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP0 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP0 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP0 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP0 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP0 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP0 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP0 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP0 MUX", "RX7", "SLIM RX7"}, \ + {"IIR" #id, NULL, "IIR" #id " INP1 MUX"}, \ + {"IIR" #id " INP1 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP1 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP1 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP1 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP1 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP1 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP1 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP1 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP1 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP1 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP1 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP1 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP1 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP1 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP1 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP1 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP1 MUX", "RX7", "SLIM RX7"}, \ + {"IIR" #id, NULL, "IIR" #id " INP2 MUX"}, \ + {"IIR" #id " INP2 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP2 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP2 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP2 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP2 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP2 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP2 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP2 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP2 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP2 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP2 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP2 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP2 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP2 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP2 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP2 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP2 MUX", "RX7", "SLIM RX7"}, \ + {"IIR" #id, NULL, "IIR" #id " INP3 MUX"}, \ + {"IIR" #id " INP3 MUX", "DEC0", "ADC MUX0"}, \ + {"IIR" #id " INP3 MUX", "DEC1", "ADC MUX1"}, \ + {"IIR" #id " INP3 MUX", "DEC2", "ADC MUX2"}, \ + {"IIR" #id " INP3 MUX", "DEC3", "ADC MUX3"}, \ + {"IIR" #id " INP3 MUX", "DEC4", "ADC MUX4"}, \ + {"IIR" #id " INP3 MUX", "DEC5", "ADC MUX5"}, \ + {"IIR" #id " INP3 MUX", "DEC6", "ADC MUX6"}, \ + {"IIR" #id " INP3 MUX", "DEC7", "ADC MUX7"}, \ + {"IIR" #id " INP3 MUX", "DEC8", "ADC MUX8"}, \ + {"IIR" #id " INP3 MUX", "RX0", "SLIM RX0"}, \ + {"IIR" #id " INP3 MUX", "RX1", "SLIM RX1"}, \ + {"IIR" #id " INP3 MUX", "RX2", "SLIM RX2"}, \ + {"IIR" #id " INP3 MUX", "RX3", "SLIM RX3"}, \ + {"IIR" #id " INP3 MUX", "RX4", "SLIM RX4"}, \ + {"IIR" #id " INP3 MUX", "RX5", "SLIM RX5"}, \ + {"IIR" #id " INP3 MUX", "RX6", "SLIM RX6"}, \ + {"IIR" #id " INP3 MUX", "RX7", "SLIM RX7"} + +#define WCD934X_SLIM_TX_AIF_PATH(id) \ + {"AIF1_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \ + {"AIF2_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \ + {"AIF3_CAP Mixer", "SLIM TX" #id, "SLIM TX" #id }, \ + {"SLIM TX" #id, NULL, "CDC_IF TX" #id " MUX"} + +#define WCD934X_MAX_MICBIAS MIC_BIAS_4 + +enum { + SIDO_SOURCE_INTERNAL, + SIDO_SOURCE_RCO_BG, +}; + +enum { + INTERP_EAR = 0, + INTERP_HPHL, + INTERP_HPHR, + INTERP_LO1, + INTERP_LO2, + INTERP_LO3_NA, /* LO3 not avalible in Tavil */ + INTERP_LO4_NA, + INTERP_SPKR1, /*INT7 WSA Speakers via soundwire */ + INTERP_SPKR2, /*INT8 WSA Speakers via soundwire */ + INTERP_MAX, +}; + +enum { + WCD934X_RX0 = 0, + WCD934X_RX1, + WCD934X_RX2, + WCD934X_RX3, + WCD934X_RX4, + WCD934X_RX5, + WCD934X_RX6, + WCD934X_RX7, + WCD934X_RX8, + WCD934X_RX9, + WCD934X_RX10, + WCD934X_RX11, + WCD934X_RX12, + WCD934X_RX_MAX, +}; + +enum { + WCD934X_TX0 = 0, + WCD934X_TX1, + WCD934X_TX2, + WCD934X_TX3, + WCD934X_TX4, + WCD934X_TX5, + WCD934X_TX6, + WCD934X_TX7, + WCD934X_TX8, + WCD934X_TX9, + WCD934X_TX10, + WCD934X_TX11, + WCD934X_TX12, + WCD934X_TX13, + WCD934X_TX14, + WCD934X_TX15, + WCD934X_TX_MAX, +}; + +struct wcd934x_slim_ch { + u32 ch_num; + u16 port; + u16 shift; + struct list_head list; +}; + +static const struct wcd934x_slim_ch wcd934x_tx_chs[WCD934X_TX_MAX] = { + WCD934X_SLIM_TX_CH(0), + WCD934X_SLIM_TX_CH(1), + WCD934X_SLIM_TX_CH(2), + WCD934X_SLIM_TX_CH(3), + WCD934X_SLIM_TX_CH(4), + WCD934X_SLIM_TX_CH(5), + WCD934X_SLIM_TX_CH(6), + WCD934X_SLIM_TX_CH(7), + WCD934X_SLIM_TX_CH(8), + WCD934X_SLIM_TX_CH(9), + WCD934X_SLIM_TX_CH(10), + WCD934X_SLIM_TX_CH(11), + WCD934X_SLIM_TX_CH(12), + WCD934X_SLIM_TX_CH(13), + WCD934X_SLIM_TX_CH(14), + WCD934X_SLIM_TX_CH(15), +}; + +static const struct wcd934x_slim_ch wcd934x_rx_chs[WCD934X_RX_MAX] = { + WCD934X_SLIM_RX_CH(0), /* 16 */ + WCD934X_SLIM_RX_CH(1), /* 17 */ + WCD934X_SLIM_RX_CH(2), + WCD934X_SLIM_RX_CH(3), + WCD934X_SLIM_RX_CH(4), + WCD934X_SLIM_RX_CH(5), + WCD934X_SLIM_RX_CH(6), + WCD934X_SLIM_RX_CH(7), + WCD934X_SLIM_RX_CH(8), + WCD934X_SLIM_RX_CH(9), + WCD934X_SLIM_RX_CH(10), + WCD934X_SLIM_RX_CH(11), + WCD934X_SLIM_RX_CH(12), +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +enum { + COMPANDER_1, /* HPH_L */ + COMPANDER_2, /* HPH_R */ + COMPANDER_3, /* LO1_DIFF */ + COMPANDER_4, /* LO2_DIFF */ + COMPANDER_5, /* LO3_SE - not used in Tavil */ + COMPANDER_6, /* LO4_SE - not used in Tavil */ + COMPANDER_7, /* SWR SPK CH1 */ + COMPANDER_8, /* SWR SPK CH2 */ + COMPANDER_MAX, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_PB, + AIF4_VIFEED, + AIF4_MAD_TX, + NUM_CODEC_DAIS, +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, + INTn_1_INP_SEL_IIR0, + INTn_1_INP_SEL_IIR1, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_RX4, + INTn_1_INP_SEL_RX5, + INTn_1_INP_SEL_RX6, + INTn_1_INP_SEL_RX7, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, + INTn_2_INP_SEL_RX6, + INTn_2_INP_SEL_RX7, + INTn_2_INP_SEL_PROXIMITY, +}; + +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate sr_val_tbl[] = { + {8000, 0x0}, + {16000, 0x1}, + {32000, 0x3}, + {48000, 0x4}, + {96000, 0x5}, + {192000, 0x6}, + {384000, 0x7}, + {44100, 0x9}, + {88200, 0xA}, + {176400, 0xB}, + {352800, 0xC}, +}; + +struct wcd934x_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +struct wcd_slim_codec_dai_data { + struct list_head slim_ch_list; + struct slim_stream_config sconfig; + struct slim_stream_runtime *sruntime; +}; + +static const struct regmap_range_cfg wcd934x_ifc_ranges[] = { + { + .name = "WCD9335-IFC-DEV", + .range_min = 0x0, + .range_max = 0xffff, + .selector_reg = 0x800, + .selector_mask = 0xfff, + .selector_shift = 0, + .window_start = 0x800, + .window_len = 0x400, + }, +}; + +static struct regmap_config wcd934x_ifc_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xffff, + .ranges = wcd934x_ifc_ranges, + .num_ranges = ARRAY_SIZE(wcd934x_ifc_ranges), +}; + +struct wcd934x_codec { + struct device *dev; + struct clk_hw hw; + struct clk *extclk; + struct regmap *regmap; + struct regmap *if_regmap; + struct slim_device *sdev; + struct slim_device *sidev; + struct wcd_clsh_ctrl *clsh_ctrl; + struct snd_soc_component *component; + struct wcd934x_slim_ch rx_chs[WCD934X_RX_MAX]; + struct wcd934x_slim_ch tx_chs[WCD934X_TX_MAX]; + struct wcd_slim_codec_dai_data dai[NUM_CODEC_DAIS]; + int rate; + u32 version; + u32 hph_mode; + int num_rx_port; + int num_tx_port; + u32 tx_port_value[WCD934X_TX_MAX]; + u32 rx_port_value[WCD934X_RX_MAX]; + int sido_input_src; + int dmic_0_1_clk_cnt; + int dmic_2_3_clk_cnt; + int dmic_4_5_clk_cnt; + int dmic_sample_rate; + int comp_enabled[COMPANDER_MAX]; + int sysclk_users; + struct mutex sysclk_mutex; + /* mbhc module */ + struct wcd_mbhc *mbhc; + struct wcd_mbhc_config mbhc_cfg; + struct wcd_mbhc_intr intr_ids; + bool mbhc_started; + struct mutex micb_lock; + u32 micb_ref[WCD934X_MAX_MICBIAS]; + u32 pullup_ref[WCD934X_MAX_MICBIAS]; + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + u32 micb4_mv; +}; + +#define to_wcd934x_codec(_hw) container_of(_hw, struct wcd934x_codec, hw) + +struct wcd_iir_filter_ctl { + unsigned int iir_idx; + unsigned int band_idx; + struct soc_bytes_ext bytes_ext; +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); +static const DECLARE_TLV_DB_SCALE(ear_pa_gain, 0, 150, 0); + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +static const char * const rx_hph_mode_mux_text[] = { + "Class H Invalid", "Class-H Hi-Fi", "Class-H Low Power", "Class-AB", + "Class-H Hi-Fi Low Power" +}; + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB", +}; + +static const char * const rx_int0_7_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7", "PROXIMITY" +}; + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0", "SRC1", "SRC_SUM" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", + "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_1_interp_mux_text[] = { + "ZERO", "RX INT0_1 MIX1", +}; + +static const char * const rx_int1_1_interp_mux_text[] = { + "ZERO", "RX INT1_1 MIX1", +}; + +static const char * const rx_int2_1_interp_mux_text[] = { + "ZERO", "RX INT2_1 MIX1", +}; + +static const char * const rx_int3_1_interp_mux_text[] = { + "ZERO", "RX INT3_1 MIX1", +}; + +static const char * const rx_int4_1_interp_mux_text[] = { + "ZERO", "RX INT4_1 MIX1", +}; + +static const char * const rx_int7_1_interp_mux_text[] = { + "ZERO", "RX INT7_1 MIX1", +}; + +static const char * const rx_int8_1_interp_mux_text[] = { + "ZERO", "RX INT8_1 MIX1", +}; + +static const char * const rx_int0_2_interp_mux_text[] = { + "ZERO", "RX INT0_2 MUX", +}; + +static const char * const rx_int1_2_interp_mux_text[] = { + "ZERO", "RX INT1_2 MUX", +}; + +static const char * const rx_int2_2_interp_mux_text[] = { + "ZERO", "RX INT2_2 MUX", +}; + +static const char * const rx_int3_2_interp_mux_text[] = { + "ZERO", "RX INT3_2 MUX", +}; + +static const char * const rx_int4_2_interp_mux_text[] = { + "ZERO", "RX INT4_2 MUX", +}; + +static const char * const rx_int7_2_interp_mux_text[] = { + "ZERO", "RX INT7_2 MUX", +}; + +static const char * const rx_int8_2_interp_mux_text[] = { + "ZERO", "RX INT8_2 MUX", +}; + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5" +}; + +static const char * const amic_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4" +}; + +static const char * const amic4_5_sel_text[] = { + "AMIC4", "AMIC5" +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" +}; + +static const char * const cdc_if_tx0_mux_text[] = { + "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192" +}; + +static const char * const cdc_if_tx1_mux_text[] = { + "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192" +}; + +static const char * const cdc_if_tx2_mux_text[] = { + "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192" +}; + +static const char * const cdc_if_tx3_mux_text[] = { + "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192" +}; + +static const char * const cdc_if_tx4_mux_text[] = { + "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192" +}; + +static const char * const cdc_if_tx5_mux_text[] = { + "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192" +}; + +static const char * const cdc_if_tx6_mux_text[] = { + "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192" +}; + +static const char * const cdc_if_tx7_mux_text[] = { + "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192" +}; + +static const char * const cdc_if_tx8_mux_text[] = { + "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192" +}; + +static const char * const cdc_if_tx9_mux_text[] = { + "ZERO", "DEC7", "DEC7_192" +}; + +static const char * const cdc_if_tx10_mux_text[] = { + "ZERO", "DEC6", "DEC6_192" +}; + +static const char * const cdc_if_tx11_mux_text[] = { + "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST" +}; + +static const char * const cdc_if_tx11_inp1_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", + "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12" +}; + +static const char * const cdc_if_tx13_mux_text[] = { + "CDC_DEC_5", "MAD_BRDCST" +}; + +static const char * const cdc_if_tx13_inp1_mux_text[] = { + "ZERO", "DEC5", "DEC5_192" +}; + +static const struct soc_enum cf_dec0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_int0_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int1_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int2_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int3_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int4_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int7_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int8_1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const struct soc_enum slim_rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text); + +static const struct soc_enum rx_int0_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int1_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int2_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int3_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int4_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int7_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int8_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int0_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int1_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int2_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int3_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int4_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int7_mix2_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum iir0_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir0_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir0_inp2_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir0_inp3_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp2_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum iir1_inp3_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, + 0, 18, iir_inp_mux_text); + +static const struct soc_enum rx_int0_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX0_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int1_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX1_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int2_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_RX2_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum tx_adc_mux0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2, + ARRAY_SIZE(adc_mux_text), adc_mux_text); +static const struct soc_enum tx_adc_mux8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 4, + ARRAY_SIZE(adc_mux_text), adc_mux_text); + +static const struct soc_enum rx_int0_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, + rx_int0_1_interp_mux_text); + +static const struct soc_enum rx_int1_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, + rx_int1_1_interp_mux_text); + +static const struct soc_enum rx_int2_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, + rx_int2_1_interp_mux_text); + +static const struct soc_enum rx_int3_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int3_1_interp_mux_text); + +static const struct soc_enum rx_int4_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int4_1_interp_mux_text); + +static const struct soc_enum rx_int7_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int7_1_interp_mux_text); + +static const struct soc_enum rx_int8_1_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int8_1_interp_mux_text); + +static const struct soc_enum rx_int0_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int0_2_interp_mux_text); + +static const struct soc_enum rx_int1_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int1_2_interp_mux_text); + +static const struct soc_enum rx_int2_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int2_2_interp_mux_text); + +static const struct soc_enum rx_int3_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int3_2_interp_mux_text); + +static const struct soc_enum rx_int4_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int4_2_interp_mux_text); + +static const struct soc_enum rx_int7_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int7_2_interp_mux_text); + +static const struct soc_enum rx_int8_2_interp_mux_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, 2, rx_int8_2_interp_mux_text); + +static const struct soc_enum tx_dmic_mux0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7, + dmic_mux_text); + +static const struct soc_enum tx_amic_mux0_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux1_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux2_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux3_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux4_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux5_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux6_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux7_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 5, + amic_mux_text); +static const struct soc_enum tx_amic_mux8_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 5, + amic_mux_text); + +static const struct soc_enum tx_amic4_5_enum = + SOC_ENUM_SINGLE(WCD934X_TX_NEW_AMIC_4_5_SEL, 7, 2, amic4_5_sel_text); + +static const struct soc_enum cdc_if_tx0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0, + ARRAY_SIZE(cdc_if_tx0_mux_text), cdc_if_tx0_mux_text); +static const struct soc_enum cdc_if_tx1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 2, + ARRAY_SIZE(cdc_if_tx1_mux_text), cdc_if_tx1_mux_text); +static const struct soc_enum cdc_if_tx2_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 4, + ARRAY_SIZE(cdc_if_tx2_mux_text), cdc_if_tx2_mux_text); +static const struct soc_enum cdc_if_tx3_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 6, + ARRAY_SIZE(cdc_if_tx3_mux_text), cdc_if_tx3_mux_text); +static const struct soc_enum cdc_if_tx4_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0, + ARRAY_SIZE(cdc_if_tx4_mux_text), cdc_if_tx4_mux_text); +static const struct soc_enum cdc_if_tx5_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 2, + ARRAY_SIZE(cdc_if_tx5_mux_text), cdc_if_tx5_mux_text); +static const struct soc_enum cdc_if_tx6_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 4, + ARRAY_SIZE(cdc_if_tx6_mux_text), cdc_if_tx6_mux_text); +static const struct soc_enum cdc_if_tx7_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 6, + ARRAY_SIZE(cdc_if_tx7_mux_text), cdc_if_tx7_mux_text); +static const struct soc_enum cdc_if_tx8_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0, + ARRAY_SIZE(cdc_if_tx8_mux_text), cdc_if_tx8_mux_text); +static const struct soc_enum cdc_if_tx9_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 2, + ARRAY_SIZE(cdc_if_tx9_mux_text), cdc_if_tx9_mux_text); +static const struct soc_enum cdc_if_tx10_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 4, + ARRAY_SIZE(cdc_if_tx10_mux_text), cdc_if_tx10_mux_text); +static const struct soc_enum cdc_if_tx11_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0, + ARRAY_SIZE(cdc_if_tx11_inp1_mux_text), + cdc_if_tx11_inp1_mux_text); +static const struct soc_enum cdc_if_tx11_mux_enum = + SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0, + ARRAY_SIZE(cdc_if_tx11_mux_text), cdc_if_tx11_mux_text); +static const struct soc_enum cdc_if_tx13_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 4, + ARRAY_SIZE(cdc_if_tx13_inp1_mux_text), + cdc_if_tx13_inp1_mux_text); +static const struct soc_enum cdc_if_tx13_mux_enum = + SOC_ENUM_SINGLE(WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0, + ARRAY_SIZE(cdc_if_tx13_mux_text), cdc_if_tx13_mux_text); + +static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD934X_ANA_MBHC_MECH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD934X_ANA_MBHC_MECH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD934X_ANA_MBHC_MECH, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x30), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD934X_ANA_MBHC_ELECT, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD934X_ANA_MBHC_MECH, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD934X_ANA_MBHC_MECH, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD934X_ANA_MBHC_MECH, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD934X_ANA_MBHC_MECH, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD934X_ANA_MBHC_ELECT, 0x06), + WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD934X_ANA_MBHC_ELECT, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F), + WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD934X_MBHC_NEW_CTL_1, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD934X_MBHC_NEW_CTL_2, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD934X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD934X_HPH_OCP_CTL, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0x07), + WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD934X_ANA_MBHC_ELECT, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD934X_ANA_MBHC_RESULT_3, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD934X_ANA_MICB2, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD934X_HPH_CNP_WG_TIME, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD934X_ANA_HPH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD934X_ANA_HPH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD934X_ANA_HPH, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD934X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD934X_MBHC_CTL_BCS, 0x02), + WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD934X_MBHC_STATUS_SPARE_1, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD934X_MBHC_NEW_CTL_2, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD934X_MBHC_NEW_FSM_STATUS, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD934X_HPH_PA_CTL2, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD934X_HPH_PA_CTL2, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD934X_HPH_L_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD934X_HPH_R_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD934X_INTR_PIN1_STATUS0, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD934X_INTR_PIN1_STATUS0, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD934X_MBHC_NEW_CTL_1, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD934X_MBHC_NEW_FSM_STATUS, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD934X_MBHC_NEW_FSM_STATUS, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD934X_MBHC_NEW_ADC_RESULT, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD934X_ANA_MICB2, 0x3F), + WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD934X_MBHC_NEW_CTL_1, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD934X_MBHC_NEW_CTL_1, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD934X_ANA_MBHC_ZDET, 0x02), +}; + +static int wcd934x_set_sido_input_src(struct wcd934x_codec *wcd, int sido_src) +{ + if (sido_src == wcd->sido_input_src) + return 0; + + if (sido_src == SIDO_SOURCE_RCO_BG) { + regmap_update_bits(wcd->regmap, WCD934X_ANA_RCO, + WCD934X_ANA_RCO_BG_EN_MASK, + WCD934X_ANA_RCO_BG_ENABLE); + usleep_range(100, 110); + } + wcd->sido_input_src = sido_src; + + return 0; +} + +static int wcd934x_enable_ana_bias_and_sysclk(struct wcd934x_codec *wcd) +{ + mutex_lock(&wcd->sysclk_mutex); + + if (++wcd->sysclk_users != 1) { + mutex_unlock(&wcd->sysclk_mutex); + return 0; + } + mutex_unlock(&wcd->sysclk_mutex); + + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_BIAS_EN_MASK, + WCD934X_ANA_BIAS_EN); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_EN_MASK, + WCD934X_ANA_PRECHRG_EN); + /* + * 1ms delay is required after pre-charge is enabled + * as per HW requirement + */ + usleep_range(1000, 1100); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_EN_MASK, 0); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_MODE_MASK, 0); + + /* + * In data clock contrl register is changed + * to CLK_SYS_MCLK_PRG + */ + + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_BUF_EN_MASK, + WCD934X_EXT_CLK_BUF_EN); + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_DIV_RATIO_MASK, + WCD934X_EXT_CLK_DIV_BY_2); + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_MCLK_SRC_MASK, + WCD934X_MCLK_SRC_EXT_CLK); + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_MCLK_EN_MASK, WCD934X_MCLK_EN); + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + WCD934X_CDC_FS_MCLK_CNT_EN_MASK, + WCD934X_CDC_FS_MCLK_CNT_ENABLE); + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + WCD934X_MCLK_EN_MASK, + WCD934X_MCLK_EN); + regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_GATE, + WCD934X_CODEC_RPM_CLK_GATE_MASK, 0x0); + /* + * 10us sleep is required after clock is enabled + * as per HW requirement + */ + usleep_range(10, 15); + + wcd934x_set_sido_input_src(wcd, SIDO_SOURCE_RCO_BG); + + return 0; +} + +static int wcd934x_disable_ana_bias_and_syclk(struct wcd934x_codec *wcd) +{ + mutex_lock(&wcd->sysclk_mutex); + if (--wcd->sysclk_users != 0) { + mutex_unlock(&wcd->sysclk_mutex); + return 0; + } + mutex_unlock(&wcd->sysclk_mutex); + + regmap_update_bits(wcd->regmap, WCD934X_CLK_SYS_MCLK_PRG, + WCD934X_EXT_CLK_BUF_EN_MASK | + WCD934X_MCLK_EN_MASK, 0x0); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_BIAS_EN_MASK, 0); + regmap_update_bits(wcd->regmap, WCD934X_ANA_BIAS, + WCD934X_ANA_PRECHRG_EN_MASK, 0); + + return 0; +} + +static int __wcd934x_cdc_mclk_enable(struct wcd934x_codec *wcd, bool enable) +{ + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(wcd->extclk); + + if (ret) { + dev_err(wcd->dev, "%s: ext clk enable failed\n", + __func__); + return ret; + } + ret = wcd934x_enable_ana_bias_and_sysclk(wcd); + } else { + int val; + + regmap_read(wcd->regmap, WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + &val); + + /* Don't disable clock if soundwire using it.*/ + if (val & WCD934X_CDC_SWR_CLK_EN_MASK) + return 0; + + wcd934x_disable_ana_bias_and_syclk(wcd); + clk_disable_unprepare(wcd->extclk); + } + + return ret; +} + +static int wcd934x_codec_enable_mclk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return __wcd934x_cdc_mclk_enable(wcd, true); + case SND_SOC_DAPM_POST_PMD: + return __wcd934x_cdc_mclk_enable(wcd, false); + } + + return 0; +} + +static int wcd934x_get_version(struct wcd934x_codec *wcd) +{ + int val1, val2, ver, ret; + struct regmap *regmap; + u16 id_minor; + u32 version_mask = 0; + + regmap = wcd->regmap; + ver = 0; + + ret = regmap_bulk_read(regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + + if (ret) + return ret; + + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1); + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2); + + version_mask |= (!!((u8)val1 & 0x80)) << DSD_DISABLED_MASK; + version_mask |= (!!((u8)val2 & 0x01)) << SLNQ_DISABLED_MASK; + + switch (version_mask) { + case DSD_DISABLED | SLNQ_DISABLED: + if (id_minor == 0) + ver = WCD_VERSION_WCD9340_1_0; + else if (id_minor == 0x01) + ver = WCD_VERSION_WCD9340_1_1; + break; + case SLNQ_DISABLED: + if (id_minor == 0) + ver = WCD_VERSION_WCD9341_1_0; + else if (id_minor == 0x01) + ver = WCD_VERSION_WCD9341_1_1; + break; + } + + wcd->version = ver; + dev_info(wcd->dev, "WCD934X Minor:0x%x Version:0x%x\n", id_minor, ver); + + return 0; +} + +static void wcd934x_enable_efuse_sensing(struct wcd934x_codec *wcd) +{ + int rc, val; + + __wcd934x_cdc_mclk_enable(wcd, true); + + regmap_update_bits(wcd->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, + WCD934X_EFUSE_SENSE_STATE_MASK, + WCD934X_EFUSE_SENSE_STATE_DEF); + regmap_update_bits(wcd->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, + WCD934X_EFUSE_SENSE_EN_MASK, + WCD934X_EFUSE_SENSE_ENABLE); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + wcd934x_set_sido_input_src(wcd, SIDO_SOURCE_RCO_BG); + + rc = regmap_read(wcd->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, &val); + if (rc || (!(val & 0x01))) + WARN(1, "%s: Efuse sense is not complete val=%x, ret=%d\n", + __func__, val, rc); + + __wcd934x_cdc_mclk_enable(wcd, false); +} + +static int wcd934x_swrm_clock(struct wcd934x_codec *wcd, bool enable) +{ + if (enable) { + __wcd934x_cdc_mclk_enable(wcd, true); + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + WCD934X_CDC_SWR_CLK_EN_MASK, + WCD934X_CDC_SWR_CLK_ENABLE); + } else { + regmap_update_bits(wcd->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + WCD934X_CDC_SWR_CLK_EN_MASK, 0); + __wcd934x_cdc_mclk_enable(wcd, false); + } + + return 0; +} + +static int wcd934x_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_val, u32 rate) +{ + struct snd_soc_component *comp = dai->component; + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + struct wcd934x_slim_ch *ch; + u8 cfg0, cfg1, inp0_sel, inp1_sel, inp2_sel; + int inp, j; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + inp = ch->shift + INTn_1_INP_SEL_RX0; + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) { + /* Interpolators 5 and 6 are not aviliable in Tavil */ + if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) + continue; + + cfg0 = snd_soc_component_read(comp, + WCD934X_CDC_RX_INP_MUX_RX_INT_CFG0(j)); + cfg1 = snd_soc_component_read(comp, + WCD934X_CDC_RX_INP_MUX_RX_INT_CFG1(j)); + + inp0_sel = cfg0 & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + inp1_sel = (cfg0 >> 4) & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + inp2_sel = (cfg1 >> 4) & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + + if ((inp0_sel == inp) || (inp1_sel == inp) || + (inp2_sel == inp)) { + /* rate is in Hz */ + /* + * Ear and speaker primary path does not support + * native sample rates + */ + if ((j == INTERP_EAR || j == INTERP_SPKR1 || + j == INTERP_SPKR2) && rate == 44100) + dev_err(wcd->dev, + "Cannot set 44.1KHz on INT%d\n", + j); + else + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX_PATH_CTL(j), + WCD934X_CDC_MIX_PCM_RATE_MASK, + rate_val); + } + } + } + + return 0; +} + +static int wcd934x_set_mix_interpolator_rate(struct snd_soc_dai *dai, + int rate_val, u32 rate) +{ + struct snd_soc_component *component = dai->component; + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + struct wcd934x_slim_ch *ch; + int val, j; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) { + /* Interpolators 5 and 6 are not aviliable in Tavil */ + if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) + continue; + val = snd_soc_component_read(component, + WCD934X_CDC_RX_INP_MUX_RX_INT_CFG1(j)) & + WCD934X_CDC_RX_INP_MUX_RX_INT_SEL_MASK; + + if (val == (ch->shift + INTn_2_INP_SEL_RX0)) { + /* + * Ear mix path supports only 48, 96, 192, + * 384KHz only + */ + if ((j == INTERP_EAR) && + (rate_val < 0x4 || + rate_val > 0x7)) { + dev_err(component->dev, + "Invalid rate for AIF_PB DAI(%d)\n", + dai->id); + return -EINVAL; + } + + snd_soc_component_update_bits(component, + WCD934X_CDC_RX_PATH_MIX_CTL(j), + WCD934X_CDC_MIX_PCM_RATE_MASK, + rate_val); + } + } + } + + return 0; +} + +static int wcd934x_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) { + if (sample_rate == sr_val_tbl[i].sample_rate) { + rate_val = sr_val_tbl[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) { + dev_err(dai->dev, "Unsupported sample rate: %d\n", sample_rate); + return -EINVAL; + } + + ret = wcd934x_set_prim_interpolator_rate(dai, (u8)rate_val, + sample_rate); + if (ret) + return ret; + ret = wcd934x_set_mix_interpolator_rate(dai, (u8)rate_val, + sample_rate); + + return ret; +} + +static int wcd934x_set_decimator_rate(struct snd_soc_dai *dai, + u8 rate_val, u32 rate) +{ + struct snd_soc_component *comp = dai->component; + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp); + u8 shift = 0, shift_val = 0, tx_mux_sel; + struct wcd934x_slim_ch *ch; + int tx_port, tx_port_reg; + int decimator = -1; + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) { + tx_port = ch->port; + /* Find the SB TX MUX input - which decimator is connected */ + switch (tx_port) { + case 0 ... 3: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + break; + case 4 ... 7: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + break; + case 8 ... 10: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + break; + case 11: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + break; + case 13: + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + break; + default: + dev_err(wcd->dev, "Invalid SLIM TX%u port DAI ID:%d\n", + tx_port, dai->id); + return -EINVAL; + } + + tx_mux_sel = snd_soc_component_read(comp, tx_port_reg) & + (shift_val << shift); + + tx_mux_sel = tx_mux_sel >> shift; + switch (tx_port) { + case 0 ... 8: + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + break; + case 9 ... 10: + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + break; + case 11: + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + break; + case 13: + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + break; + default: + dev_err(wcd->dev, "ERROR: Invalid tx_port: %d\n", + tx_port); + return -EINVAL; + } + + snd_soc_component_update_bits(comp, + WCD934X_CDC_TX_PATH_CTL(decimator), + WCD934X_CDC_TX_PATH_CTL_PCM_RATE_MASK, + rate_val); + } + + return 0; +} + +static int wcd934x_slim_set_hw_params(struct wcd934x_codec *wcd, + struct wcd_slim_codec_dai_data *dai_data, + int direction) +{ + struct list_head *slim_ch_list = &dai_data->slim_ch_list; + struct slim_stream_config *cfg = &dai_data->sconfig; + struct wcd934x_slim_ch *ch; + u16 payload = 0; + int ret, i; + + cfg->ch_count = 0; + cfg->direction = direction; + cfg->port_mask = 0; + + /* Configure slave interface device */ + list_for_each_entry(ch, slim_ch_list, list) { + cfg->ch_count++; + payload |= 1 << ch->shift; + cfg->port_mask |= BIT(ch->port); + } + + cfg->chs = kcalloc(cfg->ch_count, sizeof(unsigned int), GFP_KERNEL); + if (!cfg->chs) + return -ENOMEM; + + i = 0; + list_for_each_entry(ch, slim_ch_list, list) { + cfg->chs[i++] = ch->ch_num; + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + /* write to interface device */ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_RX_PORT_MULTI_CHNL_0(ch->port), + payload); + + if (ret < 0) + goto err; + + /* configure the slave port for water mark and enable*/ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_RX_PORT_CFG(ch->port), + WCD934X_SLIM_WATER_MARK_VAL); + if (ret < 0) + goto err; + } else { + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_TX_PORT_MULTI_CHNL_0(ch->port), + payload & 0x00FF); + if (ret < 0) + goto err; + + /* ports 8,9 */ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_TX_PORT_MULTI_CHNL_1(ch->port), + (payload & 0xFF00) >> 8); + if (ret < 0) + goto err; + + /* configure the slave port for water mark and enable*/ + ret = regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_TX_PORT_CFG(ch->port), + WCD934X_SLIM_WATER_MARK_VAL); + + if (ret < 0) + goto err; + } + } + + dai_data->sruntime = slim_stream_allocate(wcd->sdev, "WCD934x-SLIM"); + + return 0; + +err: + dev_err(wcd->dev, "Error Setting slim hw params\n"); + kfree(cfg->chs); + cfg->chs = NULL; + + return ret; +} + +static int wcd934x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd934x_codec *wcd; + int ret, tx_fs_rate = 0; + + wcd = snd_soc_component_get_drvdata(dai->component); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = wcd934x_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(wcd->dev, "cannot set sample rate: %u\n", + params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16 ... 24: + wcd->dai[dai->id].sconfig.bps = params_width(params); + break; + default: + dev_err(wcd->dev, "Invalid format 0x%x\n", + params_width(params)); + return -EINVAL; + } + break; + + case SNDRV_PCM_STREAM_CAPTURE: + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(wcd->dev, "Invalid TX sample rate: %d\n", + params_rate(params)); + return -EINVAL; + + } + + ret = wcd934x_set_decimator_rate(dai, tx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(wcd->dev, "Cannot set TX Decimator rate\n"); + return ret; + } + switch (params_width(params)) { + case 16 ... 32: + wcd->dai[dai->id].sconfig.bps = params_width(params); + break; + default: + dev_err(wcd->dev, "Invalid format 0x%x\n", + params_width(params)); + return -EINVAL; + } + break; + default: + dev_err(wcd->dev, "Invalid stream type %d\n", + substream->stream); + return -EINVAL; + } + + wcd->dai[dai->id].sconfig.rate = params_rate(params); + + return wcd934x_slim_set_hw_params(wcd, &wcd->dai[dai->id], substream->stream); +} + +static int wcd934x_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wcd_slim_codec_dai_data *dai_data; + struct wcd934x_codec *wcd; + + wcd = snd_soc_component_get_drvdata(dai->component); + + dai_data = &wcd->dai[dai->id]; + + kfree(dai_data->sconfig.chs); + + return 0; +} + +static int wcd934x_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct wcd_slim_codec_dai_data *dai_data; + struct wcd934x_codec *wcd; + struct slim_stream_config *cfg; + + wcd = snd_soc_component_get_drvdata(dai->component); + + dai_data = &wcd->dai[dai->id]; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + cfg = &dai_data->sconfig; + slim_stream_prepare(dai_data->sruntime, cfg); + slim_stream_enable(dai_data->sruntime); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + slim_stream_disable(dai_data->sruntime); + slim_stream_unprepare(dai_data->sruntime); + break; + default: + break; + } + + return 0; +} + +static int wcd934x_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct wcd934x_codec *wcd; + int i; + + wcd = snd_soc_component_get_drvdata(dai->component); + + if (tx_num > WCD934X_TX_MAX || rx_num > WCD934X_RX_MAX) { + dev_err(wcd->dev, "Invalid tx %d or rx %d channel count\n", + tx_num, rx_num); + return -EINVAL; + } + + if (!tx_slot || !rx_slot) { + dev_err(wcd->dev, "Invalid tx_slot=%p, rx_slot=%p\n", + tx_slot, rx_slot); + return -EINVAL; + } + + wcd->num_rx_port = rx_num; + for (i = 0; i < rx_num; i++) { + wcd->rx_chs[i].ch_num = rx_slot[i]; + INIT_LIST_HEAD(&wcd->rx_chs[i].list); + } + + wcd->num_tx_port = tx_num; + for (i = 0; i < tx_num; i++) { + wcd->tx_chs[i].ch_num = tx_slot[i]; + INIT_LIST_HEAD(&wcd->tx_chs[i].list); + } + + return 0; +} + +static int wcd934x_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct wcd934x_slim_ch *ch; + struct wcd934x_codec *wcd; + int i = 0; + + wcd = snd_soc_component_get_drvdata(dai->component); + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + if (!rx_slot || !rx_num) { + dev_err(wcd->dev, "Invalid rx_slot %p or rx_num %p\n", + rx_slot, rx_num); + return -EINVAL; + } + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) + rx_slot[i++] = ch->ch_num; + + *rx_num = i; + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + if (!tx_slot || !tx_num) { + dev_err(wcd->dev, "Invalid tx_slot %p or tx_num %p\n", + tx_slot, tx_num); + return -EINVAL; + } + + list_for_each_entry(ch, &wcd->dai[dai->id].slim_ch_list, list) + tx_slot[i++] = ch->ch_num; + + *tx_num = i; + break; + default: + dev_err(wcd->dev, "Invalid DAI ID %x\n", dai->id); + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops wcd934x_dai_ops = { + .hw_params = wcd934x_hw_params, + .hw_free = wcd934x_hw_free, + .trigger = wcd934x_trigger, + .set_channel_map = wcd934x_set_channel_map, + .get_channel_map = wcd934x_get_channel_map, +}; + +static struct snd_soc_dai_driver wcd934x_slim_dais[] = { + [0] = { + .name = "wcd934x_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, + [1] = { + .name = "wcd934x_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD934X_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd934x_dai_ops, + }, + [2] = { + .name = "wcd934x_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, + [3] = { + .name = "wcd934x_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD934X_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd934x_dai_ops, + }, + [4] = { + .name = "wcd934x_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, + [5] = { + .name = "wcd934x_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD934X_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd934x_dai_ops, + }, + [6] = { + .name = "wcd934x_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd934x_dai_ops, + }, +}; + +static int swclk_gate_enable(struct clk_hw *hw) +{ + return wcd934x_swrm_clock(to_wcd934x_codec(hw), true); +} + +static void swclk_gate_disable(struct clk_hw *hw) +{ + wcd934x_swrm_clock(to_wcd934x_codec(hw), false); +} + +static int swclk_gate_is_enabled(struct clk_hw *hw) +{ + struct wcd934x_codec *wcd = to_wcd934x_codec(hw); + int ret, val; + + regmap_read(wcd->regmap, WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, &val); + ret = val & WCD934X_CDC_SWR_CLK_EN_MASK; + + return ret; +} + +static unsigned long swclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate / 2; +} + +static const struct clk_ops swclk_gate_ops = { + .prepare = swclk_gate_enable, + .unprepare = swclk_gate_disable, + .is_enabled = swclk_gate_is_enabled, + .recalc_rate = swclk_recalc_rate, + +}; + +static struct clk *wcd934x_register_mclk_output(struct wcd934x_codec *wcd) +{ + struct clk *parent = wcd->extclk; + struct device *dev = wcd->dev; + struct device_node *np = dev->parent->of_node; + const char *parent_clk_name = NULL; + const char *clk_name = "mclk"; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (of_property_read_u32(np, "clock-frequency", &wcd->rate)) + return NULL; + + parent_clk_name = __clk_get_name(parent); + + of_property_read_string(np, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = &swclk_gate_ops; + init.flags = 0; + init.parent_names = &parent_clk_name; + init.num_parents = 1; + wcd->hw.init = &init; + + hw = &wcd->hw; + ret = devm_clk_hw_register(wcd->dev->parent, hw); + if (ret) + return ERR_PTR(ret); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); + if (ret) + return ERR_PTR(ret); + + return NULL; +} + +static int wcd934x_get_micbias_val(struct device *dev, const char *micbias, + u32 *micb_mv) +{ + int mv; + + if (of_property_read_u32(dev->parent->of_node, micbias, &mv)) { + dev_err(dev, "%s value not found, using default\n", micbias); + mv = WCD934X_DEF_MICBIAS_MV; + } else { + /* convert it to milli volts */ + mv = mv/1000; + } + + if (mv < 1000 || mv > 2850) { + dev_err(dev, "%s value not in valid range, using default\n", + micbias); + mv = WCD934X_DEF_MICBIAS_MV; + } + + *micb_mv = mv; + + return (mv - 1000) / 50; +} + +static int wcd934x_init_dmic(struct snd_soc_component *comp) +{ + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + u32 def_dmic_rate, dmic_clk_drv; + + vout_ctl_1 = wcd934x_get_micbias_val(comp->dev, + "qcom,micbias1-microvolt", + &wcd->micb1_mv); + vout_ctl_2 = wcd934x_get_micbias_val(comp->dev, + "qcom,micbias2-microvolt", + &wcd->micb2_mv); + vout_ctl_3 = wcd934x_get_micbias_val(comp->dev, + "qcom,micbias3-microvolt", + &wcd->micb3_mv); + vout_ctl_4 = wcd934x_get_micbias_val(comp->dev, + "qcom,micbias4-microvolt", + &wcd->micb4_mv); + + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB1, + WCD934X_MICB_VAL_MASK, vout_ctl_1); + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB2, + WCD934X_MICB_VAL_MASK, vout_ctl_2); + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB3, + WCD934X_MICB_VAL_MASK, vout_ctl_3); + snd_soc_component_update_bits(comp, WCD934X_ANA_MICB4, + WCD934X_MICB_VAL_MASK, vout_ctl_4); + + if (wcd->rate == WCD934X_MCLK_CLK_9P6MHZ) + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + else + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + + wcd->dmic_sample_rate = def_dmic_rate; + + dmic_clk_drv = 0; + snd_soc_component_update_bits(comp, WCD934X_TEST_DEBUG_PAD_DRVCTL_0, + 0x0C, dmic_clk_drv << 2); + + return 0; +} + +static void wcd934x_hw_init(struct wcd934x_codec *wcd) +{ + struct regmap *rm = wcd->regmap; + + /* set SPKR rate to FS_2P4_3P072 */ + regmap_update_bits(rm, WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08); + regmap_update_bits(rm, WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08); + + /* Take DMICs out of reset */ + regmap_update_bits(rm, WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00); +} + +static int wcd934x_comp_init(struct snd_soc_component *component) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + + wcd934x_hw_init(wcd); + wcd934x_enable_efuse_sensing(wcd); + wcd934x_get_version(wcd); + + return 0; +} + +static irqreturn_t wcd934x_slim_irq_handler(int irq, void *data) +{ + struct wcd934x_codec *wcd = data; + unsigned long status = 0; + int i, j, port_id; + unsigned int val, int_val = 0; + irqreturn_t ret = IRQ_NONE; + bool tx; + unsigned short reg = 0; + + for (i = WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + regmap_read(wcd->if_regmap, i, &val); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = false; + port_id = j; + + if (j >= 16) { + tx = true; + port_id = j - 16; + } + + regmap_read(wcd->if_regmap, + WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 + j, &val); + if (val) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + regmap_read(wcd->if_regmap, reg, &int_val); + } + + if (val & WCD934X_SLIM_IRQ_OVERFLOW) + dev_err_ratelimited(wcd->dev, + "overflow error on %s port %d, value %x\n", + (tx ? "TX" : "RX"), port_id, val); + + if (val & WCD934X_SLIM_IRQ_UNDERFLOW) + dev_err_ratelimited(wcd->dev, + "underflow error on %s port %d, value %x\n", + (tx ? "TX" : "RX"), port_id, val); + + if ((val & WCD934X_SLIM_IRQ_OVERFLOW) || + (val & WCD934X_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + regmap_read( + wcd->if_regmap, reg, &int_val); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + regmap_write(wcd->if_regmap, + reg, int_val); + } + } + + if (val & WCD934X_SLIM_IRQ_PORT_CLOSED) + dev_err_ratelimited(wcd->dev, + "Port Closed %s port %d, value %x\n", + (tx ? "TX" : "RX"), port_id, val); + + regmap_write(wcd->if_regmap, + WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 + (j / 8), + BIT(j % 8)); + ret = IRQ_HANDLED; + } + + return ret; +} + +static void wcd934x_mbhc_clk_setup(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD934X_MBHC_NEW_CTL_1, + WCD934X_MBHC_CTL_RCO_EN_MASK, enable); +} + +static void wcd934x_mbhc_mbhc_bias_control(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD934X_ANA_MBHC_ELECT, + WCD934X_ANA_MBHC_BIAS_EN, enable); +} + +static void wcd934x_mbhc_program_btn_thr(struct snd_soc_component *component, + int *btn_low, int *btn_high, + int num_btn, bool is_micbias) +{ + int i, vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(component->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_component_write_field(component, WCD934X_ANA_MBHC_BTN0 + i, + WCD934X_MBHC_BTN_VTH_MASK, vth); + } +} + +static bool wcd934x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = snd_soc_component_read_field(component, WCD934X_ANA_MICB2, + WCD934X_ANA_MICB2_ENABLE_MASK); + if (val == WCD934X_MICB_ENABLE) + return true; + } + return false; +} + +static void wcd934x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component, + enum mbhc_hs_pullup_iref pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA || + pull_up_cur == I_DEFAULT) + pull_up_cur = I_2P0_UA; + + + snd_soc_component_write_field(component, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, + WCD934X_HSDET_PULLUP_C_MASK, pull_up_cur); +} + +static int wcd934x_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm) +{ + struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component); + int micb_index = micb_num - 1; + u16 micb_reg; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD934X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD934X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD934X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD934X_ANA_MICB4; + break; + default: + dev_err(component->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + mutex_lock(&wcd934x->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: + wcd934x->pullup_ref[micb_index]++; + if ((wcd934x->pullup_ref[micb_index] == 1) && + (wcd934x->micb_ref[micb_index] == 0)) + snd_soc_component_write_field(component, micb_reg, + WCD934X_ANA_MICB_EN_MASK, + WCD934X_MICB_PULL_UP); + break; + case MICB_PULLUP_DISABLE: + if (wcd934x->pullup_ref[micb_index] > 0) + wcd934x->pullup_ref[micb_index]--; + + if ((wcd934x->pullup_ref[micb_index] == 0) && + (wcd934x->micb_ref[micb_index] == 0)) + snd_soc_component_write_field(component, micb_reg, + WCD934X_ANA_MICB_EN_MASK, 0); + break; + case MICB_ENABLE: + wcd934x->micb_ref[micb_index]++; + if (wcd934x->micb_ref[micb_index] == 1) { + snd_soc_component_write_field(component, micb_reg, + WCD934X_ANA_MICB_EN_MASK, + WCD934X_MICB_ENABLE); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd934x->mbhc, + WCD_EVENT_POST_MICBIAS_2_ON); + } + + if (micb_num == MIC_BIAS_2 && is_dapm) + wcd_mbhc_event_notify(wcd934x->mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + break; + case MICB_DISABLE: + if (wcd934x->micb_ref[micb_index] > 0) + wcd934x->micb_ref[micb_index]--; + + if ((wcd934x->micb_ref[micb_index] == 0) && + (wcd934x->pullup_ref[micb_index] > 0)) + snd_soc_component_write_field(component, micb_reg, + WCD934X_ANA_MICB_EN_MASK, + WCD934X_MICB_PULL_UP); + else if ((wcd934x->micb_ref[micb_index] == 0) && + (wcd934x->pullup_ref[micb_index] == 0)) { + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd934x->mbhc, + WCD_EVENT_PRE_MICBIAS_2_OFF); + + snd_soc_component_write_field(component, micb_reg, + WCD934X_ANA_MICB_EN_MASK, 0); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd934x->mbhc, + WCD_EVENT_POST_MICBIAS_2_OFF); + } + if (is_dapm && micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd934x->mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); + break; + } + + mutex_unlock(&wcd934x->micb_lock); + + return 0; +} + +static int wcd934x_mbhc_request_micbias(struct snd_soc_component *component, + int micb_num, int req) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + int ret; + + if (req == MICB_ENABLE) + __wcd934x_cdc_mclk_enable(wcd, true); + + ret = wcd934x_micbias_control(component, micb_num, req, false); + + if (req == MICB_DISABLE) + __wcd934x_cdc_mclk_enable(wcd, false); + + return ret; +} + +static void wcd934x_mbhc_micb_ramp_control(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP, + WCD934X_RAMP_SHIFT_CTRL_MASK, 0x3); + snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP, + WCD934X_RAMP_EN_MASK, 1); + } else { + snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP, + WCD934X_RAMP_EN_MASK, 0); + snd_soc_component_write_field(component, WCD934X_ANA_MICB2_RAMP, + WCD934X_RAMP_SHIFT_CTRL_MASK, 0); + } +} + +static int wcd934x_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) + return -EINVAL; + + return (micb_mv - 1000) / 50; +} + +static int wcd934x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) +{ + struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component); + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD934X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD934X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD934X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD934X_ANA_MICB4; + break; + default: + return -EINVAL; + } + mutex_lock(&wcd934x->micb_lock); + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_en = snd_soc_component_read_field(component, micb_reg, + WCD934X_ANA_MICB_EN_MASK); + cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, + WCD934X_MICB_VAL_MASK); + + req_vout_ctl = wcd934x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = -EINVAL; + goto exit; + } + + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + if (micb_en == WCD934X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, + WCD934X_ANA_MICB_EN_MASK, + WCD934X_MICB_PULL_UP); + + snd_soc_component_write_field(component, micb_reg, + WCD934X_MICB_VAL_MASK, + req_vout_ctl); + + if (micb_en == WCD934X_MICB_ENABLE) { + snd_soc_component_write_field(component, micb_reg, + WCD934X_ANA_MICB_EN_MASK, + WCD934X_MICB_ENABLE); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&wcd934x->micb_lock); + return ret; +} + +static int wcd934x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, + int micb_num, bool req_en) +{ + struct wcd934x_codec *wcd934x = snd_soc_component_get_drvdata(component); + int rc, micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (wcd934x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd934x->micb2_mv; + + rc = wcd934x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); + + return rc; +} + +static void wcd934x_mbhc_get_result_params(struct wcd934x_codec *wcd934x, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < WCD934X_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = WCD934X_MBHC_GET_X1(val); + c1 = WCD934X_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_err(wcd934x->dev, "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (WCD934X_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = WCD934X_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(wcd934x->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%di (milliohm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + + while (x1) { + regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_1, &val); + regmap_read(wcd934x->regmap, WCD934X_ANA_MBHC_RESULT_2, &val1); + val = val << 0x08; + val |= val1; + x1 = WCD934X_MBHC_GET_X1(val); + i++; + if (i == WCD934X_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void wcd934x_mbhc_zdet_ramp(struct snd_soc_component *component, + struct wcd934x_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct wcd934x_codec *wcd934x = dev_get_drvdata(component->dev); + int32_t zdet = 0; + + snd_soc_component_write_field(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL, + WCD934X_ZDET_MAXV_CTL_MASK, zdet_param->ldo_ctl); + snd_soc_component_update_bits(component, WCD934X_ANA_MBHC_BTN5, + WCD934X_VTH_MASK, zdet_param->btn5); + snd_soc_component_update_bits(component, WCD934X_ANA_MBHC_BTN6, + WCD934X_VTH_MASK, zdet_param->btn6); + snd_soc_component_update_bits(component, WCD934X_ANA_MBHC_BTN7, + WCD934X_VTH_MASK, zdet_param->btn7); + snd_soc_component_write_field(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL, + WCD934X_ZDET_RANGE_CTL_MASK, zdet_param->noff); + snd_soc_component_update_bits(component, WCD934X_MBHC_NEW_ZDET_RAMP_CTL, + 0x0F, zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x80, 0x80); + wcd934x_mbhc_get_result_params(wcd934x, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x40, 0x40); + wcd934x_mbhc_get_result_params(wcd934x, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static void wcd934x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (WCD934X_ZDET_VAL_400/1000)) + q1 = snd_soc_component_read(component, + WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r)); + else + q1 = snd_soc_component_read(component, + WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void wcd934x_wcd_mbhc_calc_impedance(struct snd_soc_component *component, + uint32_t *zl, uint32_t *zr) +{ + struct wcd934x_codec *wcd934x = dev_get_drvdata(component->dev); + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct wcd934x_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct wcd934x_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + reg0 = snd_soc_component_read(component, WCD934X_ANA_MBHC_BTN5); + reg1 = snd_soc_component_read(component, WCD934X_ANA_MBHC_BTN6); + reg2 = snd_soc_component_read(component, WCD934X_ANA_MBHC_BTN7); + reg3 = snd_soc_component_read(component, WCD934X_MBHC_CTL_CLK); + reg4 = snd_soc_component_read(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_component_read(component, WCD934X_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (wcd934x->mbhc_cfg.hphl_swh) + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x01, 0x00); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); + + if (!WCD934X_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1L < WCD934X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > WCD934X_ZDET_VAL_400) && + (z1L <= WCD934X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > WCD934X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == WCD934X_ZDET_FLOATING_IMPEDANCE) || + (z1L > WCD934X_ZDET_VAL_100K)) { + *zl = WCD934X_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + wcd934x_wcd_mbhc_qfuse_cal(component, zl, 0); + } + dev_info(component->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1); + if (WCD934X_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > WCD934X_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != WCD934X_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1R < WCD934X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > WCD934X_ZDET_VAL_400) && + (z1R <= WCD934X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > WCD934X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd934x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == WCD934X_ZDET_FLOATING_IMPEDANCE) || + (z1R > WCD934X_ZDET_VAL_100K)) { + *zr = WCD934X_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + wcd934x_wcd_mbhc_qfuse_cal(component, zr, 1); + } + dev_err(component->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if ((*zl == WCD934X_ZDET_FLOATING_IMPEDANCE) && + (*zr == WCD934X_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(component->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == WCD934X_ZDET_FLOATING_IMPEDANCE) || + (*zr == WCD934X_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(component->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + wcd_mbhc_set_hph_type(wcd934x->mbhc, WCD_MBHC_HPH_MONO); + goto zdet_complete; + } + snd_soc_component_write_field(component, WCD934X_HPH_R_ATEST, + WCD934X_HPHPA_GND_OVR_MASK, 1); + snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2, + WCD934X_HPHPA_GND_R_MASK, 1); + if (*zl < (WCD934X_ZDET_VAL_32/1000)) + wcd934x_mbhc_zdet_ramp(component, &zdet_param[0], &z1Ls, NULL, d1); + else + wcd934x_mbhc_zdet_ramp(component, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2, + WCD934X_HPHPA_GND_R_MASK, 0); + snd_soc_component_write_field(component, WCD934X_HPH_R_ATEST, + WCD934X_HPHPA_GND_OVR_MASK, 0); + z1Ls /= 1000; + wcd934x_wcd_mbhc_qfuse_cal(component, &z1Ls, 0); + /* Parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_err(component->dev, "%s: stereo plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd934x->mbhc, WCD_MBHC_HPH_STEREO); + } else { + dev_err(component->dev, "%s: MONO plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd934x->mbhc, WCD_MBHC_HPH_MONO); + } + +zdet_complete: + snd_soc_component_write(component, WCD934X_ANA_MBHC_BTN5, reg0); + snd_soc_component_write(component, WCD934X_ANA_MBHC_BTN6, reg1); + snd_soc_component_write(component, WCD934X_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (wcd934x->mbhc_cfg.hphl_swh) + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_component_write(component, WCD934X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_component_write(component, WCD934X_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd934x->regmap, WCD934X_ANA_MBHC_ELECT, 0x80, 0x80); +} + +static void wcd934x_mbhc_gnd_det_ctrl(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH, + WCD934X_MBHC_HSG_PULLUP_COMP_EN, 1); + snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH, + WCD934X_MBHC_GND_DET_EN_MASK, 1); + } else { + snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH, + WCD934X_MBHC_GND_DET_EN_MASK, 0); + snd_soc_component_write_field(component, WCD934X_ANA_MBHC_MECH, + WCD934X_MBHC_HSG_PULLUP_COMP_EN, 0); + } +} + +static void wcd934x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2, + WCD934X_HPHPA_GND_R_MASK, enable); + snd_soc_component_write_field(component, WCD934X_HPH_PA_CTL2, + WCD934X_HPHPA_GND_L_MASK, enable); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .clk_setup = wcd934x_mbhc_clk_setup, + .mbhc_bias = wcd934x_mbhc_mbhc_bias_control, + .set_btn_thr = wcd934x_mbhc_program_btn_thr, + .micbias_enable_status = wcd934x_mbhc_micb_en_status, + .hph_pull_up_control = wcd934x_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = wcd934x_mbhc_request_micbias, + .mbhc_micb_ramp_control = wcd934x_mbhc_micb_ramp_control, + .mbhc_micb_ctrl_thr_mic = wcd934x_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = wcd934x_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = wcd934x_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = wcd934x_mbhc_hph_pull_down_ctrl, +}; + +static int wcd934x_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd->mbhc); + + return 0; +} + +static int wcd934x_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_mixer_control *mc; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component); + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(wcd->mbhc, &zl, &zr); + dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0, + wcd934x_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0, + wcd934x_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0, + wcd934x_hph_impedance_get, NULL), +}; + +static int wcd934x_mbhc_init(struct snd_soc_component *component) +{ + struct wcd934x_ddata *data = dev_get_drvdata(component->dev->parent); + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component); + struct wcd_mbhc_intr *intr_ids = &wcd->intr_ids; + + intr_ids->mbhc_sw_intr = regmap_irq_get_virq(data->irq_data, + WCD934X_IRQ_MBHC_SW_DET); + intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(data->irq_data, + WCD934X_IRQ_MBHC_BUTTON_PRESS_DET); + intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(data->irq_data, + WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET); + intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(data->irq_data, + WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET); + intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(data->irq_data, + WCD934X_IRQ_MBHC_ELECT_INS_REM_DET); + intr_ids->hph_left_ocp = regmap_irq_get_virq(data->irq_data, + WCD934X_IRQ_HPH_PA_OCPL_FAULT); + intr_ids->hph_right_ocp = regmap_irq_get_virq(data->irq_data, + WCD934X_IRQ_HPH_PA_OCPR_FAULT); + + wcd->mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true); + if (IS_ERR(wcd->mbhc)) { + wcd->mbhc = NULL; + return -EINVAL; + } + + snd_soc_add_component_controls(component, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_component_controls(component, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + return 0; +} + +static void wcd934x_mbhc_deinit(struct snd_soc_component *component) +{ + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(component); + + if (!wcd->mbhc) + return; + + wcd_mbhc_deinit(wcd->mbhc); +} + +static int wcd934x_comp_probe(struct snd_soc_component *component) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + int i; + + snd_soc_component_init_regmap(component, wcd->regmap); + wcd->component = component; + + /* Class-H Init*/ + wcd->clsh_ctrl = wcd_clsh_ctrl_alloc(component, wcd->version); + if (IS_ERR(wcd->clsh_ctrl)) + return PTR_ERR(wcd->clsh_ctrl); + + /* Default HPH Mode to Class-H Low HiFi */ + wcd->hph_mode = CLS_H_LOHIFI; + + wcd934x_comp_init(component); + + for (i = 0; i < NUM_CODEC_DAIS; i++) + INIT_LIST_HEAD(&wcd->dai[i].slim_ch_list); + + wcd934x_init_dmic(component); + + if (wcd934x_mbhc_init(component)) + dev_err(component->dev, "Failed to Initialize MBHC\n"); + + return 0; +} + +static void wcd934x_comp_remove(struct snd_soc_component *comp) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + wcd934x_mbhc_deinit(comp); + wcd_clsh_ctrl_free(wcd->clsh_ctrl); +} + +static int wcd934x_comp_set_sysclk(struct snd_soc_component *comp, + int clk_id, int source, + unsigned int freq, int dir) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int val = WCD934X_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ; + + wcd->rate = freq; + + if (wcd->rate == WCD934X_MCLK_CLK_12P288MHZ) + val = WCD934X_CODEC_RPM_CLK_MCLK_CFG_12P288MHZ; + + snd_soc_component_update_bits(comp, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + WCD934X_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + val); + + return clk_set_rate(wcd->extclk, freq); +} + +static uint32_t get_iir_band_coeff(struct snd_soc_component *component, + int iir_idx, int band_idx, int coeff_idx) +{ + u32 value = 0; + int reg, b2_reg; + + /* Address does not automatically update if reading */ + reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx; + b2_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx; + + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) * + sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_component_read(component, b2_reg); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_component_read(component, b2_reg) << 8); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_component_read(component, b2_reg) << 16); + snd_soc_component_write(component, reg, + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= (snd_soc_component_read(component, b2_reg) << 24); + return value; +} + +static void set_iir_band_coeff(struct snd_soc_component *component, + int iir_idx, int band_idx, uint32_t value) +{ + int reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx; + + snd_soc_component_write(component, reg, (value & 0xFF)); + snd_soc_component_write(component, reg, (value >> 8) & 0xFF); + snd_soc_component_write(component, reg, (value >> 16) & 0xFF); + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_component_write(component, reg, (value >> 24) & 0x3F); +} + +static int wcd934x_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + int iir_idx = ctl->iir_idx; + int band_idx = ctl->band_idx; + u32 coeff[BAND_MAX]; + int reg = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx; + + memcpy(&coeff[0], ucontrol->value.bytes.data, params->max); + + /* Mask top bit it is reserved */ + /* Updates addr automatically for each B2 write */ + snd_soc_component_write(component, reg, (band_idx * BAND_MAX * + sizeof(uint32_t)) & 0x7F); + + set_iir_band_coeff(component, iir_idx, band_idx, coeff[0]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[1]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[2]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[3]); + set_iir_band_coeff(component, iir_idx, band_idx, coeff[4]); + + return 0; +} + +static int wcd934x_get_iir_band_audio_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + int iir_idx = ctl->iir_idx; + int band_idx = ctl->band_idx; + u32 coeff[BAND_MAX]; + + coeff[0] = get_iir_band_coeff(component, iir_idx, band_idx, 0); + coeff[1] = get_iir_band_coeff(component, iir_idx, band_idx, 1); + coeff[2] = get_iir_band_coeff(component, iir_idx, band_idx, 2); + coeff[3] = get_iir_band_coeff(component, iir_idx, band_idx, 3); + coeff[4] = get_iir_band_coeff(component, iir_idx, band_idx, 4); + + memcpy(ucontrol->value.bytes.data, &coeff[0], params->max); + + return 0; +} + +static int wcd934x_iir_filter_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct wcd_iir_filter_ctl *ctl = + (struct wcd_iir_filter_ctl *)kcontrol->private_value; + struct soc_bytes_ext *params = &ctl->bytes_ext; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} + +static int wcd934x_compander_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + int comp = ((struct soc_mixer_control *)kc->private_value)->shift; + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + + ucontrol->value.integer.value[0] = wcd->comp_enabled[comp]; + + return 0; +} + +static int wcd934x_compander_set(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + int comp = ((struct soc_mixer_control *)kc->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + int sel; + + if (wcd->comp_enabled[comp] == value) + return 0; + + wcd->comp_enabled[comp] = value; + sel = value ? WCD934X_HPH_GAIN_SRC_SEL_COMPANDER : + WCD934X_HPH_GAIN_SRC_SEL_REGISTER; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_component_update_bits(component, WCD934X_HPH_L_EN, + WCD934X_HPH_GAIN_SRC_SEL_MASK, + sel); + break; + case COMPANDER_2: + snd_soc_component_update_bits(component, WCD934X_HPH_R_EN, + WCD934X_HPH_GAIN_SRC_SEL_MASK, + sel); + break; + case COMPANDER_3: + case COMPANDER_4: + case COMPANDER_7: + case COMPANDER_8: + break; + default: + return 0; + } + + return 1; +} + +static int wcd934x_rx_hph_mode_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + + ucontrol->value.enumerated.item[0] = wcd->hph_mode; + + return 0; +} + +static int wcd934x_rx_hph_mode_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + if (mode_val == wcd->hph_mode) + return 0; + + if (mode_val == 0) { + dev_err(wcd->dev, "Invalid HPH Mode, default to ClSH HiFi\n"); + mode_val = CLS_H_LOHIFI; + } + wcd->hph_mode = mode_val; + + return 1; +} + +static int slim_rx_mux_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(dapm->dev); + + ucontrol->value.enumerated.item[0] = wcd->rx_port_value[w->shift]; + + return 0; +} + +static int slim_rx_mux_to_dai_id(int mux) +{ + int aif_id; + + switch (mux) { + case 1: + aif_id = AIF1_PB; + break; + case 2: + aif_id = AIF2_PB; + break; + case 3: + aif_id = AIF3_PB; + break; + case 4: + aif_id = AIF4_PB; + break; + default: + aif_id = -1; + break; + } + + return aif_id; +} + +static int slim_rx_mux_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(w->dapm->dev); + struct soc_enum *e = (struct soc_enum *)kc->private_value; + struct snd_soc_dapm_update *update = NULL; + struct wcd934x_slim_ch *ch, *c; + u32 port_id = w->shift; + bool found = false; + int mux_idx; + int prev_mux_idx = wcd->rx_port_value[port_id]; + int aif_id; + + mux_idx = ucontrol->value.enumerated.item[0]; + + if (mux_idx == prev_mux_idx) + return 0; + + switch(mux_idx) { + case 0: + aif_id = slim_rx_mux_to_dai_id(prev_mux_idx); + if (aif_id < 0) + return 0; + + list_for_each_entry_safe(ch, c, &wcd->dai[aif_id].slim_ch_list, list) { + if (ch->port == port_id + WCD934X_RX_START) { + found = true; + list_del_init(&ch->list); + break; + } + } + if (!found) + return 0; + + break; + case 1 ... 4: + aif_id = slim_rx_mux_to_dai_id(mux_idx); + if (aif_id < 0) + return 0; + + if (list_empty(&wcd->rx_chs[port_id].list)) { + list_add_tail(&wcd->rx_chs[port_id].list, + &wcd->dai[aif_id].slim_ch_list); + } else { + dev_err(wcd->dev ,"SLIM_RX%d PORT is busy\n", port_id); + return 0; + } + break; + + default: + dev_err(wcd->dev, "Unknown AIF %d\n", mux_idx); + goto err; + } + + wcd->rx_port_value[port_id] = mux_idx; + snd_soc_dapm_mux_update_power(w->dapm, kc, wcd->rx_port_value[port_id], + e, update); + + return 1; +err: + return -EINVAL; +} + +static int wcd934x_int_dem_inp_mux_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kc->private_value; + struct snd_soc_component *component; + int reg, val; + + component = snd_soc_dapm_kcontrol_component(kc); + val = ucontrol->value.enumerated.item[0]; + if (e->reg == WCD934X_CDC_RX0_RX_PATH_SEC0) + reg = WCD934X_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX1_RX_PATH_SEC0) + reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX2_RX_PATH_SEC0) + reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + else + return -EINVAL; + + /* Set Look Ahead Delay */ + if (val) + snd_soc_component_update_bits(component, reg, + WCD934X_RX_DLY_ZN_EN_MASK, + WCD934X_RX_DLY_ZN_ENABLE); + else + snd_soc_component_update_bits(component, reg, + WCD934X_RX_DLY_ZN_EN_MASK, + WCD934X_RX_DLY_ZN_DISABLE); + + return snd_soc_dapm_put_enum_double(kc, ucontrol); +} + +static int wcd934x_dec_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg = 0; + u8 mic_sel; + + comp = snd_soc_dapm_kcontrol_component(kcontrol); + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + switch (e->reg) { + case WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX0_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX4_TX_PATH_CFG0; + else if (e->shift_l == 4) + mic_sel_reg = WCD934X_CDC_TX8_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX1_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX5_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX2_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX6_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX3_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX7_TX_PATH_CFG0; + break; + default: + dev_err(comp->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + /* ADC: 0, DMIC: 1 */ + mic_sel = val ? 0x0 : 0x1; + if (mic_sel_reg) + snd_soc_component_update_bits(comp, mic_sel_reg, BIT(7), + mic_sel << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static const struct snd_kcontrol_new rx_int0_2_mux = + SOC_DAPM_ENUM("RX INT0_2 MUX Mux", rx_int0_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int1_2_mux = + SOC_DAPM_ENUM("RX INT1_2 MUX Mux", rx_int1_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int2_2_mux = + SOC_DAPM_ENUM("RX INT2_2 MUX Mux", rx_int2_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int3_2_mux = + SOC_DAPM_ENUM("RX INT3_2 MUX Mux", rx_int3_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int4_2_mux = + SOC_DAPM_ENUM("RX INT4_2 MUX Mux", rx_int4_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int7_2_mux = + SOC_DAPM_ENUM("RX INT7_2 MUX Mux", rx_int7_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int8_2_mux = + SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP1 Mux", rx_int0_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP2 Mux", rx_int0_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP0 Mux", rx_int1_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP1 Mux", rx_int1_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP2 Mux", rx_int1_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP0 Mux", rx_int2_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP1 Mux", rx_int2_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP2 Mux", rx_int2_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP0 Mux", rx_int3_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP1 Mux", rx_int3_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP2 Mux", rx_int3_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP0 Mux", rx_int4_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP1 Mux", rx_int4_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP2 Mux", rx_int4_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP0 Mux", rx_int7_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP1 Mux", rx_int7_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP2 Mux", rx_int7_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP0 Mux", rx_int8_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP1 Mux", rx_int8_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP2 Mux", rx_int8_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int0_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT0 MIX2 INP Mux", rx_int0_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT1 MIX2 INP Mux", rx_int1_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT2 MIX2 INP Mux", rx_int2_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT3 MIX2 INP Mux", rx_int3_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT4 MIX2 INP Mux", rx_int4_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT7 MIX2 INP Mux", rx_int7_mix2_inp_mux_enum); + +static const struct snd_kcontrol_new iir0_inp0_mux = + SOC_DAPM_ENUM("IIR0 INP0 Mux", iir0_inp0_mux_enum); +static const struct snd_kcontrol_new iir0_inp1_mux = + SOC_DAPM_ENUM("IIR0 INP1 Mux", iir0_inp1_mux_enum); +static const struct snd_kcontrol_new iir0_inp2_mux = + SOC_DAPM_ENUM("IIR0 INP2 Mux", iir0_inp2_mux_enum); +static const struct snd_kcontrol_new iir0_inp3_mux = + SOC_DAPM_ENUM("IIR0 INP3 Mux", iir0_inp3_mux_enum); + +static const struct snd_kcontrol_new iir1_inp0_mux = + SOC_DAPM_ENUM("IIR1 INP0 Mux", iir1_inp0_mux_enum); +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); +static const struct snd_kcontrol_new iir1_inp2_mux = + SOC_DAPM_ENUM("IIR1 INP2 Mux", iir1_inp2_mux_enum); +static const struct snd_kcontrol_new iir1_inp3_mux = + SOC_DAPM_ENUM("IIR1 INP3 Mux", iir1_inp3_mux_enum); + +static const struct snd_kcontrol_new slim_rx_mux[WCD934X_RX_MAX] = { + SOC_DAPM_ENUM_EXT("SLIM RX0 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), +}; + +static const struct snd_kcontrol_new rx_int1_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int2_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int3_asrc_switch[] = { + SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int4_asrc_switch[] = { + SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int0_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT0 DEM MUX Mux", rx_int0_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd934x_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int1_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT1 DEM MUX Mux", rx_int1_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd934x_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int2_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT2 DEM MUX Mux", rx_int2_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + wcd934x_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int0_1_interp_mux = + SOC_DAPM_ENUM("RX INT0_1 INTERP Mux", rx_int0_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_1_interp_mux = + SOC_DAPM_ENUM("RX INT1_1 INTERP Mux", rx_int1_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_1_interp_mux = + SOC_DAPM_ENUM("RX INT2_1 INTERP Mux", rx_int2_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_1_interp_mux = + SOC_DAPM_ENUM("RX INT3_1 INTERP Mux", rx_int3_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_1_interp_mux = + SOC_DAPM_ENUM("RX INT4_1 INTERP Mux", rx_int4_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_1_interp_mux = + SOC_DAPM_ENUM("RX INT7_1 INTERP Mux", rx_int7_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int8_1_interp_mux = + SOC_DAPM_ENUM("RX INT8_1 INTERP Mux", rx_int8_1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int0_2_interp_mux = + SOC_DAPM_ENUM("RX INT0_2 INTERP Mux", rx_int0_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_2_interp_mux = + SOC_DAPM_ENUM("RX INT1_2 INTERP Mux", rx_int1_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_2_interp_mux = + SOC_DAPM_ENUM("RX INT2_2 INTERP Mux", rx_int2_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_2_interp_mux = + SOC_DAPM_ENUM("RX INT3_2 INTERP Mux", rx_int3_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_2_interp_mux = + SOC_DAPM_ENUM("RX INT4_2 INTERP Mux", rx_int4_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_2_interp_mux = + SOC_DAPM_ENUM("RX INT7_2 INTERP Mux", rx_int7_2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int8_2_interp_mux = + SOC_DAPM_ENUM("RX INT8_2 INTERP Mux", rx_int8_2_interp_mux_enum); + +static const struct snd_kcontrol_new tx_dmic_mux0 = + SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum); + +static const struct snd_kcontrol_new tx_dmic_mux1 = + SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum); + +static const struct snd_kcontrol_new tx_dmic_mux2 = + SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum); + +static const struct snd_kcontrol_new tx_dmic_mux3 = + SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum); + +static const struct snd_kcontrol_new tx_dmic_mux4 = + SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum); + +static const struct snd_kcontrol_new tx_dmic_mux5 = + SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum); + +static const struct snd_kcontrol_new tx_dmic_mux6 = + SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum); + +static const struct snd_kcontrol_new tx_dmic_mux7 = + SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum); + +static const struct snd_kcontrol_new tx_dmic_mux8 = + SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum); + +static const struct snd_kcontrol_new tx_amic_mux0 = + SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum); + +static const struct snd_kcontrol_new tx_amic_mux1 = + SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum); + +static const struct snd_kcontrol_new tx_amic_mux2 = + SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum); + +static const struct snd_kcontrol_new tx_amic_mux3 = + SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum); + +static const struct snd_kcontrol_new tx_amic_mux4 = + SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum); + +static const struct snd_kcontrol_new tx_amic_mux5 = + SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum); + +static const struct snd_kcontrol_new tx_amic_mux6 = + SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum); + +static const struct snd_kcontrol_new tx_amic_mux7 = + SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum); + +static const struct snd_kcontrol_new tx_amic_mux8 = + SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum); + +static const struct snd_kcontrol_new tx_amic4_5 = + SOC_DAPM_ENUM("AMIC4_5 SEL Mux", tx_amic4_5_enum); + +static const struct snd_kcontrol_new tx_adc_mux0_mux = + SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux1_mux = + SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux2_mux = + SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux3_mux = + SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux4_mux = + SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux5_mux = + SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux6_mux = + SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux7_mux = + SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); +static const struct snd_kcontrol_new tx_adc_mux8_mux = + SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_enum, + snd_soc_dapm_get_enum_double, wcd934x_dec_enum_put); + +static const struct snd_kcontrol_new cdc_if_tx0_mux = + SOC_DAPM_ENUM("CDC_IF TX0 MUX Mux", cdc_if_tx0_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx1_mux = + SOC_DAPM_ENUM("CDC_IF TX1 MUX Mux", cdc_if_tx1_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx2_mux = + SOC_DAPM_ENUM("CDC_IF TX2 MUX Mux", cdc_if_tx2_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx3_mux = + SOC_DAPM_ENUM("CDC_IF TX3 MUX Mux", cdc_if_tx3_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx4_mux = + SOC_DAPM_ENUM("CDC_IF TX4 MUX Mux", cdc_if_tx4_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx5_mux = + SOC_DAPM_ENUM("CDC_IF TX5 MUX Mux", cdc_if_tx5_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx6_mux = + SOC_DAPM_ENUM("CDC_IF TX6 MUX Mux", cdc_if_tx6_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx7_mux = + SOC_DAPM_ENUM("CDC_IF TX7 MUX Mux", cdc_if_tx7_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx8_mux = + SOC_DAPM_ENUM("CDC_IF TX8 MUX Mux", cdc_if_tx8_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx9_mux = + SOC_DAPM_ENUM("CDC_IF TX9 MUX Mux", cdc_if_tx9_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx10_mux = + SOC_DAPM_ENUM("CDC_IF TX10 MUX Mux", cdc_if_tx10_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx11_mux = + SOC_DAPM_ENUM("CDC_IF TX11 MUX Mux", cdc_if_tx11_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx11_inp1_mux = + SOC_DAPM_ENUM("CDC_IF TX11 INP1 MUX Mux", cdc_if_tx11_inp1_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx13_mux = + SOC_DAPM_ENUM("CDC_IF TX13 MUX Mux", cdc_if_tx13_mux_enum); +static const struct snd_kcontrol_new cdc_if_tx13_inp1_mux = + SOC_DAPM_ENUM("CDC_IF TX13 INP1 MUX Mux", cdc_if_tx13_inp1_mux_enum); + +static int slim_tx_mixer_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(dapm->dev); + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kc->private_value; + int port_id = mixer->shift; + + ucontrol->value.integer.value[0] = wcd->tx_port_value[port_id]; + + return 0; +} + +static int slim_tx_mixer_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kc); + struct wcd934x_codec *wcd = dev_get_drvdata(widget->dapm->dev); + struct snd_soc_dapm_update *update = NULL; + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kc->private_value; + int enable = ucontrol->value.integer.value[0]; + struct wcd934x_slim_ch *ch, *c; + int dai_id = widget->shift; + int port_id = mixer->shift; + + /* only add to the list if value not set */ + if (enable == wcd->tx_port_value[port_id]) + return 0; + + if (enable) { + if (list_empty(&wcd->tx_chs[port_id].list)) { + list_add_tail(&wcd->tx_chs[port_id].list, + &wcd->dai[dai_id].slim_ch_list); + } else { + dev_err(wcd->dev ,"SLIM_TX%d PORT is busy\n", port_id); + return 0; + } + } else { + bool found = false; + + list_for_each_entry_safe(ch, c, &wcd->dai[dai_id].slim_ch_list, list) { + if (ch->port == port_id) { + found = true; + list_del_init(&wcd->tx_chs[port_id].list); + break; + } + } + if (!found) + return 0; + } + + wcd->tx_port_value[port_id] = enable; + snd_soc_dapm_mixer_update_power(widget->dapm, kc, enable, update); + + return 1; +} + +static const struct snd_kcontrol_new aif1_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_slim_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new wcd934x_snd_controls[] = { + /* Gain Controls */ + SOC_SINGLE_TLV("EAR PA Volume", WCD934X_ANA_EAR, 4, 4, 1, ear_pa_gain), + SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 24, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 24, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT2 Volume", WCD934X_DIFF_LO_LO2_COMPANDER, + 3, 16, 1, line_gain), + + SOC_SINGLE_TLV("ADC1 Volume", WCD934X_ANA_AMIC1, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD934X_ANA_AMIC2, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD934X_ANA_AMIC3, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD934X_ANA_AMIC4, 0, 20, 0, analog_gain), + + SOC_SINGLE_S8_TLV("RX0 Digital Volume", WCD934X_CDC_RX0_RX_VOL_CTL, + -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_S8_TLV("RX1 Digital Volume", WCD934X_CDC_RX1_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX2 Digital Volume", WCD934X_CDC_RX2_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX3 Digital Volume", WCD934X_CDC_RX3_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX4 Digital Volume", WCD934X_CDC_RX4_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX7 Digital Volume", WCD934X_CDC_RX7_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX8 Digital Volume", WCD934X_CDC_RX8_RX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX0 Mix Digital Volume", + WCD934X_CDC_RX0_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX1 Mix Digital Volume", + WCD934X_CDC_RX1_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX2 Mix Digital Volume", + WCD934X_CDC_RX2_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX3 Mix Digital Volume", + WCD934X_CDC_RX3_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX4 Mix Digital Volume", + WCD934X_CDC_RX4_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX7 Mix Digital Volume", + WCD934X_CDC_RX7_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("RX8 Mix Digital Volume", + WCD934X_CDC_RX8_RX_VOL_MIX_CTL, + -84, 40, digital_gain), + + SOC_SINGLE_S8_TLV("DEC0 Volume", WCD934X_CDC_TX0_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC1 Volume", WCD934X_CDC_TX1_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC2 Volume", WCD934X_CDC_TX2_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC3 Volume", WCD934X_CDC_TX3_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC4 Volume", WCD934X_CDC_TX4_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC5 Volume", WCD934X_CDC_TX5_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC6 Volume", WCD934X_CDC_TX6_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC7 Volume", WCD934X_CDC_TX7_TX_VOL_CTL, + -84, 40, digital_gain), + SOC_SINGLE_S8_TLV("DEC8 Volume", WCD934X_CDC_TX8_TX_VOL_CTL, + -84, 40, digital_gain), + + SOC_SINGLE_S8_TLV("IIR0 INP0 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR0 INP1 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR0 INP2 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR0 INP3 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP0 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP1 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP2 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, -84, 40, + digital_gain), + SOC_SINGLE_S8_TLV("IIR1 INP3 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, -84, 40, + digital_gain), + + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_dec5_enum), + SOC_ENUM("TX6 HPF cut off", cf_dec6_enum), + SOC_ENUM("TX7 HPF cut off", cf_dec7_enum), + SOC_ENUM("TX8 HPF cut off", cf_dec8_enum), + + SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum), + SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum), + SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum), + SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum), + SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum), + SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum), + SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum), + SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum), + SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum), + SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum), + SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum), + SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum), + SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum), + SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd934x_rx_hph_mode_get, wcd934x_rx_hph_mode_put), + + SOC_SINGLE("IIR1 Band1 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 0, 1, 0), + SOC_SINGLE("IIR1 Band2 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 1, 1, 0), + SOC_SINGLE("IIR1 Band3 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 2, 1, 0), + SOC_SINGLE("IIR1 Band4 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 3, 1, 0), + SOC_SINGLE("IIR1 Band5 Switch", WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, + 4, 1, 0), + SOC_SINGLE("IIR2 Band1 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 0, 1, 0), + SOC_SINGLE("IIR2 Band2 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 1, 1, 0), + SOC_SINGLE("IIR2 Band3 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 2, 1, 0), + SOC_SINGLE("IIR2 Band4 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 3, 1, 0), + SOC_SINGLE("IIR2 Band5 Switch", WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, + 4, 1, 0), + WCD_IIR_FILTER_CTL("IIR0 Band1", IIR0, BAND1), + WCD_IIR_FILTER_CTL("IIR0 Band2", IIR0, BAND2), + WCD_IIR_FILTER_CTL("IIR0 Band3", IIR0, BAND3), + WCD_IIR_FILTER_CTL("IIR0 Band4", IIR0, BAND4), + WCD_IIR_FILTER_CTL("IIR0 Band5", IIR0, BAND5), + + WCD_IIR_FILTER_CTL("IIR1 Band1", IIR1, BAND1), + WCD_IIR_FILTER_CTL("IIR1 Band2", IIR1, BAND2), + WCD_IIR_FILTER_CTL("IIR1 Band3", IIR1, BAND3), + WCD_IIR_FILTER_CTL("IIR1 Band4", IIR1, BAND4), + WCD_IIR_FILTER_CTL("IIR1 Band5", IIR1, BAND5), + + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + wcd934x_compander_get, wcd934x_compander_set), +}; + +static void wcd934x_codec_enable_int_port(struct wcd_slim_codec_dai_data *dai, + struct snd_soc_component *component) +{ + int port_num = 0; + unsigned short reg = 0; + unsigned int val = 0; + struct wcd934x_codec *wcd = dev_get_drvdata(component->dev); + struct wcd934x_slim_ch *ch; + + list_for_each_entry(ch, &dai->slim_ch_list, list) { + if (ch->port >= WCD934X_RX_START) { + port_num = ch->port - WCD934X_RX_START; + reg = WCD934X_SLIM_PGD_PORT_INT_EN0 + (port_num / 8); + } else { + port_num = ch->port; + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + } + + regmap_read(wcd->if_regmap, reg, &val); + if (!(val & BIT(port_num % 8))) + regmap_write(wcd->if_regmap, reg, + val | BIT(port_num % 8)); + } +} + +static int wcd934x_codec_enable_slim(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp); + struct wcd_slim_codec_dai_data *dai = &wcd->dai[w->shift]; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wcd934x_codec_enable_int_port(dai, comp); + break; + } + + return 0; +} + +static void wcd934x_codec_hd2_control(struct snd_soc_component *component, + u16 interp_idx, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hd2_scale_reg = WCD934X_CDC_RX1_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + break; + case INTERP_HPHR: + hd2_scale_reg = WCD934X_CDC_RX2_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + break; + default: + return; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_MASK, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_0P3125); + snd_soc_component_update_bits(component, hd2_enable_reg, + WCD934X_CDC_RX_PATH_CFG_HD2_EN_MASK, + WCD934X_CDC_RX_PATH_CFG_HD2_ENABLE); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(component, hd2_enable_reg, + WCD934X_CDC_RX_PATH_CFG_HD2_EN_MASK, + WCD934X_CDC_RX_PATH_CFG_HD2_DISABLE); + snd_soc_component_update_bits(component, hd2_scale_reg, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_MASK, + WCD934X_CDC_RX_PATH_SEC_HD2_ALPHA_0P0000); + } +} + +static void wcd934x_codec_hphdelay_lutbypass(struct snd_soc_component *comp, + u16 interp_idx, int event) +{ + u8 hph_dly_mask; + u16 hph_lut_bypass_reg = 0; + + switch (interp_idx) { + case INTERP_HPHL: + hph_dly_mask = 1; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT; + break; + case INTERP_HPHR: + hph_dly_mask = 2; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT; + break; + default: + return; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(comp, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, 0x0); + snd_soc_component_update_bits(comp, hph_lut_bypass_reg, + WCD934X_HPH_LUT_BYPASS_MASK, + WCD934X_HPH_LUT_BYPASS_ENABLE); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_component_update_bits(comp, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, hph_dly_mask); + snd_soc_component_update_bits(comp, hph_lut_bypass_reg, + WCD934X_HPH_LUT_BYPASS_MASK, + WCD934X_HPH_LUT_BYPASS_DISABLE); + } +} + +static int wcd934x_config_compander(struct snd_soc_component *comp, + int interp_n, int event) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int compander; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + compander = interp_n - 1; + if (!wcd->comp_enabled[compander]) + return 0; + + comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (compander * 8); + rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (compander * 20); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable Compander Clock */ + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_CLK_EN_MASK, + WCD934X_COMP_CLK_ENABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_ENABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_DISABLE); + snd_soc_component_update_bits(comp, rx_path_cfg0_reg, + WCD934X_HPH_CMP_EN_MASK, + WCD934X_HPH_CMP_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(comp, rx_path_cfg0_reg, + WCD934X_HPH_CMP_EN_MASK, + WCD934X_HPH_CMP_DISABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_HALT_MASK, + WCD934X_COMP_HALT); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_ENABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, + WCD934X_COMP_SOFT_RST_DISABLE); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_CLK_EN_MASK, 0x0); + snd_soc_component_update_bits(comp, comp_ctl0_reg, + WCD934X_COMP_SOFT_RST_MASK, 0x0); + break; + } + + return 0; +} + +static int wcd934x_codec_enable_interp_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int interp_idx = w->shift; + u16 main_reg = WCD934X_CDC_RX0_RX_PATH_CTL + (interp_idx * 20); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Clk enable */ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_CLK_EN_MASK, + WCD934X_RX_CLK_ENABLE); + wcd934x_codec_hd2_control(comp, interp_idx, event); + wcd934x_codec_hphdelay_lutbypass(comp, interp_idx, event); + wcd934x_config_compander(comp, interp_idx, event); + break; + case SND_SOC_DAPM_POST_PMD: + wcd934x_config_compander(comp, interp_idx, event); + wcd934x_codec_hphdelay_lutbypass(comp, interp_idx, event); + wcd934x_codec_hd2_control(comp, interp_idx, event); + /* Clk Disable */ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_CLK_EN_MASK, 0); + /* Reset enable and disable */ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_RESET_MASK, + WCD934X_RX_RESET_ENABLE); + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_RESET_MASK, + WCD934X_RX_RESET_DISABLE); + /* Reset rate to 48K*/ + snd_soc_component_update_bits(comp, main_reg, + WCD934X_RX_PCM_RATE_MASK, + WCD934X_RX_PCM_RATE_F_48K); + break; + } + + return 0; +} + +static int wcd934x_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int offset_val = 0; + u16 gain_reg, mix_reg; + int val = 0; + + gain_reg = WCD934X_CDC_RX0_RX_VOL_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + mix_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Clk enable */ + snd_soc_component_update_bits(comp, mix_reg, + WCD934X_CDC_RX_MIX_CLK_EN_MASK, + WCD934X_CDC_RX_MIX_CLK_ENABLE); + break; + + case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read(comp, gain_reg); + val += offset_val; + snd_soc_component_write(comp, gain_reg, val); + break; + } + + return 0; +} + +static int wcd934x_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + int reg = w->reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* B1 GAIN */ + snd_soc_component_write(comp, reg, + snd_soc_component_read(comp, reg)); + /* B2 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read(comp, reg)); + /* B3 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read(comp, reg)); + /* B4 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read(comp, reg)); + /* B5 GAIN */ + reg++; + snd_soc_component_write(comp, reg, + snd_soc_component_read(comp, reg)); + break; + default: + break; + } + return 0; +} + +static int wcd934x_codec_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + u16 gain_reg; + + gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write(comp, gain_reg, + snd_soc_component_read(comp, gain_reg)); + break; + } + + return 0; +} + +static int wcd934x_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disable AutoChop timer during power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, CLS_H_NORMAL); + break; + } + + return 0; +} + +static int wcd934x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + u8 dem_inp; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Read DEM INP Select */ + dem_inp = snd_soc_component_read(comp, + WCD934X_CDC_RX1_RX_PATH_SEC0) & 0x03; + + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + return -EINVAL; + } + if (hph_mode != CLS_H_LP) + /* Ripple freq control enable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, + WCD934X_SIDO_RIPPLE_FREQ_ENABLE); + /* Disable AutoChop timer during power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, hph_mode); + + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, hph_mode); + if (hph_mode != CLS_H_LP) + /* Ripple freq control disable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, 0x0); + + break; + default: + break; + } + + return 0; +} + +static int wcd934x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int hph_mode = wcd->hph_mode; + u8 dem_inp; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dem_inp = snd_soc_component_read(comp, + WCD934X_CDC_RX2_RX_PATH_SEC0) & 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + return -EINVAL; + } + if (hph_mode != CLS_H_LP) + /* Ripple freq control enable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, + WCD934X_SIDO_RIPPLE_FREQ_ENABLE); + /* Disable AutoChop timer during power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, 0x0); + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + hph_mode); + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, hph_mode); + if (hph_mode != CLS_H_LP) + /* Ripple freq control disable */ + snd_soc_component_update_bits(comp, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + WCD934X_SIDO_RIPPLE_FREQ_EN_MASK, 0x0); + break; + default: + break; + } + + return 0; +} + +static int wcd934x_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kc, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, CLS_AB); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_ctrl_set_state(wcd->clsh_ctrl, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, CLS_AB); + break; + } + + return 0; +} + +static int wcd934x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + usleep_range(20000, 20100); + + snd_soc_component_update_bits(comp, WCD934X_HPH_L_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_ENABLE); + /* Remove Mute on primary path */ + snd_soc_component_update_bits(comp, WCD934X_CDC_RX1_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + 0); + /* Enable GM3 boost */ + snd_soc_component_update_bits(comp, WCD934X_HPH_CNP_WG_CTL, + WCD934X_HPH_GM3_BOOST_EN_MASK, + WCD934X_HPH_GM3_BOOST_ENABLE); + /* Enable AutoChop timer at the end of power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, + WCD934X_HPH_AUTOCHOP_TIMER_ENABLE); + /* Remove mix path mute */ + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + WCD934X_CDC_RX_PGA_MUTE_EN_MASK, 0x00); + break; + case SND_SOC_DAPM_PRE_PMD: + wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_POST_HPHL_PA_OFF); + /* Enable DSD Mute before PA disable */ + snd_soc_component_update_bits(comp, WCD934X_HPH_L_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_DISABLE); + snd_soc_component_update_bits(comp, WCD934X_CDC_RX1_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + WCD934X_RX_PATH_PGA_MUTE_ENABLE); + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + WCD934X_RX_PATH_PGA_MUTE_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + usleep_range(20000, 20100); + wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_POST_HPHL_PA_OFF); + break; + } + + return 0; +} + +static int wcd934x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = snd_soc_component_get_drvdata(comp); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + usleep_range(20000, 20100); + snd_soc_component_update_bits(comp, WCD934X_HPH_R_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_ENABLE); + /* Remove mute */ + snd_soc_component_update_bits(comp, WCD934X_CDC_RX2_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + 0); + /* Enable GM3 boost */ + snd_soc_component_update_bits(comp, WCD934X_HPH_CNP_WG_CTL, + WCD934X_HPH_GM3_BOOST_EN_MASK, + WCD934X_HPH_GM3_BOOST_ENABLE); + /* Enable AutoChop timer at the end of power up */ + snd_soc_component_update_bits(comp, + WCD934X_HPH_NEW_INT_HPH_TIMER1, + WCD934X_HPH_AUTOCHOP_TIMER_EN_MASK, + WCD934X_HPH_AUTOCHOP_TIMER_ENABLE); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_component_read(comp, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) & 0x10) + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + WCD934X_CDC_RX_PGA_MUTE_EN_MASK, + WCD934X_CDC_RX_PGA_MUTE_DISABLE); + break; + case SND_SOC_DAPM_PRE_PMD: + wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_PRE_HPHR_PA_OFF); + snd_soc_component_update_bits(comp, WCD934X_HPH_R_TEST, + WCD934X_HPH_OCP_DET_MASK, + WCD934X_HPH_OCP_DET_DISABLE); + snd_soc_component_update_bits(comp, WCD934X_CDC_RX2_RX_PATH_CTL, + WCD934X_RX_PATH_PGA_MUTE_EN_MASK, + WCD934X_RX_PATH_PGA_MUTE_ENABLE); + snd_soc_component_update_bits(comp, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + WCD934X_CDC_RX_PGA_MUTE_EN_MASK, + WCD934X_CDC_RX_PGA_MUTE_ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + usleep_range(20000, 20100); + wcd_mbhc_event_notify(wcd->mbhc, WCD_EVENT_POST_HPHR_PA_OFF); + break; + } + + return 0; +} + +static u32 wcd934x_get_dmic_sample_rate(struct snd_soc_component *comp, + unsigned int dmic, + struct wcd934x_codec *wcd) +{ + u8 tx_stream_fs; + u8 adc_mux_index = 0, adc_mux_sel = 0; + bool dec_found = false; + u16 adc_mux_ctl_reg, tx_fs_reg; + u32 dmic_fs; + + while (!dec_found && adc_mux_index < WCD934X_MAX_VALID_ADC_MUX) { + if (adc_mux_index < 4) { + adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + (adc_mux_index * 2); + } else if (adc_mux_index < WCD934X_INVALID_ADC_MUX) { + adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_index - 4; + } else if (adc_mux_index == WCD934X_INVALID_ADC_MUX) { + ++adc_mux_index; + continue; + } + adc_mux_sel = ((snd_soc_component_read(comp, adc_mux_ctl_reg) + & 0xF8) >> 3) - 1; + + if (adc_mux_sel == dmic) { + dec_found = true; + break; + } + + ++adc_mux_index; + } + + if (dec_found && adc_mux_index <= 8) { + tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index); + tx_stream_fs = snd_soc_component_read(comp, tx_fs_reg) & 0x0F; + if (tx_stream_fs <= 4) + dmic_fs = min(wcd->dmic_sample_rate, WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ); + else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + } else { + dmic_fs = wcd->dmic_sample_rate; + } + + return dmic_fs; +} + +static u8 wcd934x_get_dmic_clk_val(struct snd_soc_component *comp, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + /* Default value to return in case of error */ + if (mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2; + else + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3; + + if (dmic_clk_rate == 0) { + dev_err(comp->dev, + "%s: dmic_sample_rate cannot be 0\n", + __func__); + goto done; + } + + div_factor = mclk_rate / dmic_clk_rate; + switch (div_factor) { + case 2: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_6; + break; + case 8: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_8; + break; + case 16: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_16; + break; + default: + dev_err(comp->dev, + "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", + __func__, div_factor, mclk_rate, dmic_clk_rate); + break; + } + +done: + return dmic_ctl_val; +} + +static int wcd934x_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift = 1; + unsigned int dmic; + u32 dmic_sample_rate; + int ret; + char *wname; + + wname = strpbrk(w->name, "012345"); + if (!wname) { + dev_err(comp->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(comp->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &wcd->dmic_0_1_clk_cnt; + dmic_clk_reg = WCD934X_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &wcd->dmic_2_3_clk_cnt; + dmic_clk_reg = WCD934X_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &wcd->dmic_4_5_clk_cnt; + dmic_clk_reg = WCD934X_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(comp->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_sample_rate = wcd934x_get_dmic_sample_rate(comp, dmic, + wcd); + dmic_rate_val = wcd934x_get_dmic_clk_val(comp, wcd->rate, + dmic_sample_rate); + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + dmic_rate_val = dmic_rate_val << dmic_rate_shift; + snd_soc_component_update_bits(comp, dmic_clk_reg, + WCD934X_DMIC_RATE_MASK, + dmic_rate_val); + snd_soc_component_update_bits(comp, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) + snd_soc_component_update_bits(comp, dmic_clk_reg, + dmic_clk_en, 0); + break; + } + + return 0; +} + +static int wcd934x_codec_find_amic_input(struct snd_soc_component *comp, + int adc_mux_n) +{ + u16 mask, shift, adc_mux_in_reg; + u16 amic_mux_sel_reg; + bool is_amic; + + if (adc_mux_n < 0 || adc_mux_n > WCD934X_MAX_VALID_ADC_MUX || + adc_mux_n == WCD934X_INVALID_ADC_MUX) + return 0; + + if (adc_mux_n < 3) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + adc_mux_n; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 4) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 7) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + (adc_mux_n - 4); + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 8) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 12) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + ((adc_mux_n == 8) ? (adc_mux_n - 8) : + (adc_mux_n - 9)); + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 13) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1; + mask = 0xC0; + shift = 6; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } + + is_amic = (((snd_soc_component_read(comp, adc_mux_in_reg) + & mask) >> shift) == 1); + if (!is_amic) + return 0; + + return snd_soc_component_read(comp, amic_mux_sel_reg) & 0x07; +} + +static u16 wcd934x_codec_get_amic_pwlvl_reg(struct snd_soc_component *comp, + int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = WCD934X_ANA_AMIC1; + break; + + case 3: + case 4: + pwr_level_reg = WCD934X_ANA_AMIC3; + break; + default: + break; + } + + return pwr_level_reg; +} + +static int wcd934x_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + unsigned int decimator; + char *dec_adc_mux_name = NULL; + char *widget_name = NULL; + char *wname; + int ret = 0, amic_n; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + char *dec; + u8 hpf_coff_freq; + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + wname = widget_name; + dec_adc_mux_name = strsep(&widget_name, " "); + if (!dec_adc_mux_name) { + dev_err(comp->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + dec_adc_mux_name = widget_name; + + dec = strpbrk(dec_adc_mux_name, "012345678"); + if (!dec) { + dev_err(comp->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(comp->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD934X_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + amic_n = wcd934x_codec_find_amic_input(comp, decimator); + if (amic_n) + pwr_level_reg = wcd934x_codec_get_amic_pwlvl_reg(comp, + amic_n); + + if (!pwr_level_reg) + break; + + switch ((snd_soc_component_read(comp, pwr_level_reg) & + WCD934X_AMIC_PWR_LVL_MASK) >> + WCD934X_AMIC_PWR_LVL_SHIFT) { + case WCD934X_AMIC_PWR_LEVEL_LP: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_LP); + break; + case WCD934X_AMIC_PWR_LEVEL_HP: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_HP); + break; + case WCD934X_AMIC_PWR_LEVEL_DEFAULT: + case WCD934X_AMIC_PWR_LEVEL_HYBRID: + default: + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + } + break; + case SND_SOC_DAPM_POST_PMU: + hpf_coff_freq = (snd_soc_component_read(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + if (hpf_coff_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + snd_soc_component_update_bits(comp, hpf_gate_reg, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_component_update_bits(comp, hpf_gate_reg, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + 0); + } + /* apply gain after decimator is enabled */ + snd_soc_component_write(comp, tx_gain_ctl_reg, + snd_soc_component_read(comp, + tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_coff_freq = (snd_soc_component_read(comp, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + if (hpf_coff_freq != CF_MIN_3DB_150HZ) { + snd_soc_component_update_bits(comp, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_coff_freq << 5); + snd_soc_component_update_bits(comp, hpf_gate_reg, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_component_update_bits(comp, hpf_gate_reg, + WCD934X_HPH_CUTOFF_FREQ_CHANGE_REQ_MASK, + 0); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(comp, tx_vol_ctl_reg, + 0x10, 0x00); + snd_soc_component_update_bits(comp, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + } +out: + kfree(wname); + return ret; +} + +static void wcd934x_codec_set_tx_hold(struct snd_soc_component *comp, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == WCD934X_ANA_AMIC1 || + amic_reg == WCD934X_ANA_AMIC3) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case WCD934X_ANA_AMIC1: + case WCD934X_ANA_AMIC2: + snd_soc_component_update_bits(comp, WCD934X_ANA_AMIC2, + mask, val); + break; + case WCD934X_ANA_AMIC3: + case WCD934X_ANA_AMIC4: + snd_soc_component_update_bits(comp, WCD934X_ANA_AMIC4, + mask, val); + break; + default: + break; + } +} + +static int wcd934x_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd934x_codec_set_tx_hold(comp, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int wcd934x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd934x_micbias_control(component, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd934x_micbias_control(component, micb_num, MICB_DISABLE, true); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget wcd934x_dapm_widgets[] = { + /* Analog Outputs */ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + SND_SOC_DAPM_OUTPUT("ANC HPHL"), + SND_SOC_DAPM_OUTPUT("ANC HPHR"), + SND_SOC_DAPM_OUTPUT("WDMA3_OUT"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"), + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, WCD934X_RX0, 0, + &slim_rx_mux[WCD934X_RX0]), + SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, WCD934X_RX1, 0, + &slim_rx_mux[WCD934X_RX1]), + SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, WCD934X_RX2, 0, + &slim_rx_mux[WCD934X_RX2]), + SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, WCD934X_RX3, 0, + &slim_rx_mux[WCD934X_RX3]), + SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, WCD934X_RX4, 0, + &slim_rx_mux[WCD934X_RX4]), + SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, WCD934X_RX5, 0, + &slim_rx_mux[WCD934X_RX5]), + SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, WCD934X_RX6, 0, + &slim_rx_mux[WCD934X_RX6]), + SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, WCD934X_RX7, 0, + &slim_rx_mux[WCD934X_RX7]), + + SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_2_mux, wcd934x_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux), + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int1_asrc_switch, + ARRAY_SIZE(rx_int1_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int2_asrc_switch, + ARRAY_SIZE(rx_int2_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int3_asrc_switch, + ARRAY_SIZE(rx_int3_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int4_asrc_switch, + ARRAY_SIZE(rx_int4_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, NULL, 0), + SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", WCD934X_CDC_RX0_RX_PATH_CFG0, 4, + 0, &rx_int0_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", WCD934X_CDC_RX1_RX_PATH_CFG0, 4, + 0, &rx_int1_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", WCD934X_CDC_RX2_RX_PATH_CFG0, 4, + 0, &rx_int2_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3 MIX2 INP", WCD934X_CDC_RX3_RX_PATH_CFG0, 4, + 0, &rx_int3_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4 MIX2 INP", WCD934X_CDC_RX4_RX_PATH_CFG0, 4, + 0, &rx_int4_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 MIX2 INP", WCD934X_CDC_RX7_RX_PATH_CFG0, 4, + 0, &rx_int7_mix2_inp_mux, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux), + SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux), + SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux), + SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux), + SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux), + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux), + SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux), + + SND_SOC_DAPM_PGA_E("IIR0", WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + 0, 0, NULL, 0, wcd934x_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_E("IIR1", WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + 1, 0, NULL, 0, wcd934x_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MIXER("SRC0", WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int0_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int1_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int2_dem_inp_mux), + + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_1 INTERP", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_1 INTERP", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_1_interp_mux, + wcd934x_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int0_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT1_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int1_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT2_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int2_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT3_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int3_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT4_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int4_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT7_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int7_2_interp_mux), + SND_SOC_DAPM_MUX("RX INT8_2 INTERP", SND_SOC_NOPM, 0, 0, + &rx_int8_2_interp_mux), + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd934x_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, WCD934X_ANA_HPH, + 5, 0, wcd934x_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, WCD934X_ANA_HPH, + 4, 0, wcd934x_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd934x_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM, + 0, 0, wcd934x_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("HPHL PA", WCD934X_ANA_HPH, 7, 0, NULL, 0, + wcd934x_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", WCD934X_ANA_HPH, 6, 0, NULL, 0, + wcd934x_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD934X_ANA_LO_1_2, 7, 0, NULL, 0, + NULL, 0), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("RX_BIAS", WCD934X_ANA_RX_SUPPLIES, 0, 0, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SBOOST0", WCD934X_CDC_RX7_RX_PATH_CFG1, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SBOOST0_CLK", WCD934X_CDC_BOOST0_BOOST_PATH_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SBOOST1", WCD934X_CDC_RX8_RX_PATH_CFG1, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SBOOST1_CLK", WCD934X_CDC_BOOST1_BOOST_PATH_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("INT0_CLK", SND_SOC_NOPM, INTERP_EAR, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT1_CLK", SND_SOC_NOPM, INTERP_HPHL, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT2_CLK", SND_SOC_NOPM, INTERP_HPHR, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT3_CLK", SND_SOC_NOPM, INTERP_LO1, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT4_CLK", SND_SOC_NOPM, INTERP_LO2, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT7_CLK", SND_SOC_NOPM, INTERP_SPKR1, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT8_CLK", SND_SOC_NOPM, INTERP_SPKR2, 0, + wcd934x_codec_enable_interp_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DSMDEM0_CLK", WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM1_CLK", WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM2_CLK", WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM3_CLK", WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM4_CLK", WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM7_CLK", WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSMDEM8_CLK", WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_mclk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* TX */ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + SND_SOC_DAPM_INPUT("DMIC0 Pin"), + SND_SOC_DAPM_INPUT("DMIC1 Pin"), + SND_SOC_DAPM_INPUT("DMIC2 Pin"), + SND_SOC_DAPM_INPUT("DMIC3 Pin"), + SND_SOC_DAPM_INPUT("DMIC4 Pin"), + SND_SOC_DAPM_INPUT("DMIC5 Pin"), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, + AIF2_CAP, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, + AIF3_CAP, 0, wcd934x_codec_enable_slim, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX8", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX9", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX10", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX11", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX13", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0, + wcd934x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_dmic_mux0), + SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_dmic_mux1), + SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_dmic_mux2), + SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_dmic_mux3), + SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_dmic_mux4), + SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_dmic_mux5), + SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_dmic_mux6), + SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_dmic_mux7), + SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0, &tx_dmic_mux8), + SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_amic_mux0), + SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_amic_mux1), + SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_amic_mux2), + SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_amic_mux3), + SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_amic_mux4), + SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_amic_mux5), + SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_amic_mux6), + SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_amic_mux7), + SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0, &tx_amic_mux8), + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD934X_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX1", WCD934X_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc_mux1_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX2", WCD934X_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc_mux2_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX3", WCD934X_CDC_TX3_TX_PATH_CTL, 5, 0, + &tx_adc_mux3_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX4", WCD934X_CDC_TX4_TX_PATH_CTL, 5, 0, + &tx_adc_mux4_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX5", WCD934X_CDC_TX5_TX_PATH_CTL, 5, 0, + &tx_adc_mux5_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX6", WCD934X_CDC_TX6_TX_PATH_CTL, 5, 0, + &tx_adc_mux6_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX7", WCD934X_CDC_TX7_TX_PATH_CTL, 5, 0, + &tx_adc_mux7_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ADC MUX8", WCD934X_CDC_TX8_TX_PATH_CTL, 5, 0, + &tx_adc_mux8_mux, wcd934x_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD934X_ANA_AMIC1, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD934X_ANA_AMIC2, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD934X_ANA_AMIC3, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD934X_ANA_AMIC4, 7, 0, + wcd934x_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, + wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, + wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0, + wcd934x_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("AMIC4_5 SEL", SND_SOC_NOPM, 0, 0, &tx_amic4_5), + SND_SOC_DAPM_MUX("CDC_IF TX0 MUX", SND_SOC_NOPM, WCD934X_TX0, 0, + &cdc_if_tx0_mux), + SND_SOC_DAPM_MUX("CDC_IF TX1 MUX", SND_SOC_NOPM, WCD934X_TX1, 0, + &cdc_if_tx1_mux), + SND_SOC_DAPM_MUX("CDC_IF TX2 MUX", SND_SOC_NOPM, WCD934X_TX2, 0, + &cdc_if_tx2_mux), + SND_SOC_DAPM_MUX("CDC_IF TX3 MUX", SND_SOC_NOPM, WCD934X_TX3, 0, + &cdc_if_tx3_mux), + SND_SOC_DAPM_MUX("CDC_IF TX4 MUX", SND_SOC_NOPM, WCD934X_TX4, 0, + &cdc_if_tx4_mux), + SND_SOC_DAPM_MUX("CDC_IF TX5 MUX", SND_SOC_NOPM, WCD934X_TX5, 0, + &cdc_if_tx5_mux), + SND_SOC_DAPM_MUX("CDC_IF TX6 MUX", SND_SOC_NOPM, WCD934X_TX6, 0, + &cdc_if_tx6_mux), + SND_SOC_DAPM_MUX("CDC_IF TX7 MUX", SND_SOC_NOPM, WCD934X_TX7, 0, + &cdc_if_tx7_mux), + SND_SOC_DAPM_MUX("CDC_IF TX8 MUX", SND_SOC_NOPM, WCD934X_TX8, 0, + &cdc_if_tx8_mux), + SND_SOC_DAPM_MUX("CDC_IF TX9 MUX", SND_SOC_NOPM, WCD934X_TX9, 0, + &cdc_if_tx9_mux), + SND_SOC_DAPM_MUX("CDC_IF TX10 MUX", SND_SOC_NOPM, WCD934X_TX10, 0, + &cdc_if_tx10_mux), + SND_SOC_DAPM_MUX("CDC_IF TX11 MUX", SND_SOC_NOPM, WCD934X_TX11, 0, + &cdc_if_tx11_mux), + SND_SOC_DAPM_MUX("CDC_IF TX11 INP1 MUX", SND_SOC_NOPM, WCD934X_TX11, 0, + &cdc_if_tx11_inp1_mux), + SND_SOC_DAPM_MUX("CDC_IF TX13 MUX", SND_SOC_NOPM, WCD934X_TX13, 0, + &cdc_if_tx13_mux), + SND_SOC_DAPM_MUX("CDC_IF TX13 INP1 MUX", SND_SOC_NOPM, WCD934X_TX13, 0, + &cdc_if_tx13_inp1_mux), + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_slim_cap_mixer, + ARRAY_SIZE(aif1_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_slim_cap_mixer, + ARRAY_SIZE(aif2_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_slim_cap_mixer, + ARRAY_SIZE(aif3_slim_cap_mixer)), +}; + +static const struct snd_soc_dapm_route wcd934x_audio_map[] = { + /* RX0-RX7 */ + WCD934X_SLIM_RX_AIF_PATH(0), + WCD934X_SLIM_RX_AIF_PATH(1), + WCD934X_SLIM_RX_AIF_PATH(2), + WCD934X_SLIM_RX_AIF_PATH(3), + WCD934X_SLIM_RX_AIF_PATH(4), + WCD934X_SLIM_RX_AIF_PATH(5), + WCD934X_SLIM_RX_AIF_PATH(6), + WCD934X_SLIM_RX_AIF_PATH(7), + + /* RX0 Ear out */ + WCD934X_INTERPOLATOR_PATH(0), + WCD934X_INTERPOLATOR_MIX2(0), + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"}, + {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, + {"RX INT0 DAC", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "RX INT0 DAC"}, + {"EAR", NULL, "EAR PA"}, + + /* RX1 Headphone left */ + WCD934X_INTERPOLATOR_PATH(1), + WCD934X_INTERPOLATOR_MIX2(1), + {"RX INT1 MIX3", NULL, "RX INT1 MIX2"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX3"}, + {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"}, + {"RX INT1 DAC", NULL, "RX_BIAS"}, + {"HPHL PA", NULL, "RX INT1 DAC"}, + {"HPHL", NULL, "HPHL PA"}, + + /* RX2 Headphone right */ + WCD934X_INTERPOLATOR_PATH(2), + WCD934X_INTERPOLATOR_MIX2(2), + {"RX INT2 MIX3", NULL, "RX INT2 MIX2"}, + {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX3"}, + {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"}, + {"RX INT2 DAC", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "RX INT2 DAC"}, + {"HPHR", NULL, "HPHR PA"}, + + /* RX3 HIFi LineOut1 */ + WCD934X_INTERPOLATOR_PATH(3), + WCD934X_INTERPOLATOR_MIX2(3), + {"RX INT3 MIX3", NULL, "RX INT3 MIX2"}, + {"RX INT3 DAC", NULL, "RX INT3 MIX3"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + /* RX4 HIFi LineOut2 */ + WCD934X_INTERPOLATOR_PATH(4), + WCD934X_INTERPOLATOR_MIX2(4), + {"RX INT4 MIX3", NULL, "RX INT4 MIX2"}, + {"RX INT4 DAC", NULL, "RX INT4 MIX3"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + /* RX7 Speaker Left Out PA */ + WCD934X_INTERPOLATOR_PATH(7), + WCD934X_INTERPOLATOR_MIX2(7), + {"RX INT7 CHAIN", NULL, "RX INT7 MIX2"}, + {"RX INT7 CHAIN", NULL, "RX_BIAS"}, + {"RX INT7 CHAIN", NULL, "SBOOST0"}, + {"RX INT7 CHAIN", NULL, "SBOOST0_CLK"}, + {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + + /* RX8 Speaker Right Out PA */ + WCD934X_INTERPOLATOR_PATH(8), + {"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"}, + {"RX INT8 CHAIN", NULL, "RX_BIAS"}, + {"RX INT8 CHAIN", NULL, "SBOOST1"}, + {"RX INT8 CHAIN", NULL, "SBOOST1_CLK"}, + {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + + /* Tx */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + WCD934X_SLIM_TX_AIF_PATH(0), + WCD934X_SLIM_TX_AIF_PATH(1), + WCD934X_SLIM_TX_AIF_PATH(2), + WCD934X_SLIM_TX_AIF_PATH(3), + WCD934X_SLIM_TX_AIF_PATH(4), + WCD934X_SLIM_TX_AIF_PATH(5), + WCD934X_SLIM_TX_AIF_PATH(6), + WCD934X_SLIM_TX_AIF_PATH(7), + WCD934X_SLIM_TX_AIF_PATH(8), + + WCD934X_ADC_MUX(0), + WCD934X_ADC_MUX(1), + WCD934X_ADC_MUX(2), + WCD934X_ADC_MUX(3), + WCD934X_ADC_MUX(4), + WCD934X_ADC_MUX(5), + WCD934X_ADC_MUX(6), + WCD934X_ADC_MUX(7), + WCD934X_ADC_MUX(8), + + {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX2 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX3 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX4 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX5 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX6 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX7 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX8 MUX", "DEC8", "ADC MUX8"}, + + {"AMIC4_5 SEL", "AMIC4", "AMIC4"}, + {"AMIC4_5 SEL", "AMIC5", "AMIC5"}, + + { "DMIC0", NULL, "DMIC0 Pin" }, + { "DMIC1", NULL, "DMIC1 Pin" }, + { "DMIC2", NULL, "DMIC2 Pin" }, + { "DMIC3", NULL, "DMIC3 Pin" }, + { "DMIC4", NULL, "DMIC4 Pin" }, + { "DMIC5", NULL, "DMIC5 Pin" }, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4_5 SEL"}, + + WCD934X_IIR_INP_MUX(0), + WCD934X_IIR_INP_MUX(1), + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, +}; + +static int wcd934x_codec_set_jack(struct snd_soc_component *comp, + struct snd_soc_jack *jack, void *data) +{ + struct wcd934x_codec *wcd = dev_get_drvdata(comp->dev); + int ret = 0; + + if (!wcd->mbhc) + return -ENOTSUPP; + + if (jack && !wcd->mbhc_started) { + ret = wcd_mbhc_start(wcd->mbhc, &wcd->mbhc_cfg, jack); + wcd->mbhc_started = true; + } else if (wcd->mbhc_started) { + wcd_mbhc_stop(wcd->mbhc); + wcd->mbhc_started = false; + } + + return ret; +} + +static const struct snd_soc_component_driver wcd934x_component_drv = { + .probe = wcd934x_comp_probe, + .remove = wcd934x_comp_remove, + .set_sysclk = wcd934x_comp_set_sysclk, + .controls = wcd934x_snd_controls, + .num_controls = ARRAY_SIZE(wcd934x_snd_controls), + .dapm_widgets = wcd934x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd934x_dapm_widgets), + .dapm_routes = wcd934x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd934x_audio_map), + .set_jack = wcd934x_codec_set_jack, + .endianness = 1, +}; + +static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd) +{ + struct device *dev = &wcd->sdev->dev; + struct wcd_mbhc_config *cfg = &wcd->mbhc_cfg; + struct device_node *ifc_dev_np; + + ifc_dev_np = of_parse_phandle(dev->of_node, "slim-ifc-dev", 0); + if (!ifc_dev_np) { + dev_err(dev, "No Interface device found\n"); + return -EINVAL; + } + + wcd->sidev = of_slim_get_device(wcd->sdev->ctrl, ifc_dev_np); + of_node_put(ifc_dev_np); + if (!wcd->sidev) { + dev_err(dev, "Unable to get SLIM Interface device\n"); + return -EINVAL; + } + + slim_get_logical_addr(wcd->sidev); + wcd->if_regmap = regmap_init_slimbus(wcd->sidev, + &wcd934x_ifc_regmap_config); + if (IS_ERR(wcd->if_regmap)) + return dev_err_probe(dev, PTR_ERR(wcd->if_regmap), + "Failed to allocate ifc register map\n"); + + of_property_read_u32(dev->parent->of_node, "qcom,dmic-sample-rate", + &wcd->dmic_sample_rate); + + cfg->mbhc_micbias = MIC_BIAS_2; + cfg->anc_micbias = MIC_BIAS_2; + cfg->v_hs_max = WCD_MBHC_HS_V_MAX; + cfg->num_btn = WCD934X_MBHC_MAX_BUTTONS; + cfg->micb_mv = wcd->micb2_mv; + cfg->linein_th = 5000; + cfg->hs_thr = 1700; + cfg->hph_thr = 50; + + wcd_dt_parse_mbhc_data(dev, cfg); + + + return 0; +} + +static int wcd934x_codec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd934x_ddata *data = dev_get_drvdata(dev->parent); + struct wcd934x_codec *wcd; + int ret, irq; + + wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + wcd->dev = dev; + wcd->regmap = data->regmap; + wcd->extclk = data->extclk; + wcd->sdev = to_slim_device(data->dev); + mutex_init(&wcd->sysclk_mutex); + mutex_init(&wcd->micb_lock); + + ret = wcd934x_codec_parse_data(wcd); + if (ret) { + dev_err(wcd->dev, "Failed to get SLIM IRQ\n"); + return ret; + } + + /* set default rate 9P6MHz */ + regmap_update_bits(wcd->regmap, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + WCD934X_CODEC_RPM_CLK_MCLK_CFG_MCLK_MASK, + WCD934X_CODEC_RPM_CLK_MCLK_CFG_9P6MHZ); + memcpy(wcd->rx_chs, wcd934x_rx_chs, sizeof(wcd934x_rx_chs)); + memcpy(wcd->tx_chs, wcd934x_tx_chs, sizeof(wcd934x_tx_chs)); + + irq = regmap_irq_get_virq(data->irq_data, WCD934X_IRQ_SLIMBUS); + if (irq < 0) + return dev_err_probe(wcd->dev, irq, "Failed to get SLIM IRQ\n"); + + ret = devm_request_threaded_irq(dev, irq, NULL, + wcd934x_slim_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "slim", wcd); + if (ret) + return dev_err_probe(dev, ret, "Failed to request slimbus irq\n"); + + wcd934x_register_mclk_output(wcd); + platform_set_drvdata(pdev, wcd); + + return devm_snd_soc_register_component(dev, &wcd934x_component_drv, + wcd934x_slim_dais, + ARRAY_SIZE(wcd934x_slim_dais)); +} + +static const struct platform_device_id wcd934x_driver_id[] = { + { + .name = "wcd934x-codec", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, wcd934x_driver_id); + +static struct platform_driver wcd934x_codec_driver = { + .probe = &wcd934x_codec_probe, + .id_table = wcd934x_driver_id, + .driver = { + .name = "wcd934x-codec", + } +}; + +MODULE_ALIAS("platform:wcd934x-codec"); +module_platform_driver(wcd934x_codec_driver); +MODULE_DESCRIPTION("WCD934x codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c new file mode 100644 index 0000000000..a1f04010da --- /dev/null +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -0,0 +1,1358 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2021, Linaro Limited + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd938x.h" + +#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) + +static struct wcd938x_sdw_ch_info wcd938x_sdw_rx_ch_info[] = { + WCD_SDW_CH(WCD938X_HPH_L, WCD938X_HPH_PORT, BIT(0)), + WCD_SDW_CH(WCD938X_HPH_R, WCD938X_HPH_PORT, BIT(1)), + WCD_SDW_CH(WCD938X_CLSH, WCD938X_CLSH_PORT, BIT(0)), + WCD_SDW_CH(WCD938X_COMP_L, WCD938X_COMP_PORT, BIT(0)), + WCD_SDW_CH(WCD938X_COMP_R, WCD938X_COMP_PORT, BIT(1)), + WCD_SDW_CH(WCD938X_LO, WCD938X_LO_PORT, BIT(0)), + WCD_SDW_CH(WCD938X_DSD_L, WCD938X_DSD_PORT, BIT(0)), + WCD_SDW_CH(WCD938X_DSD_R, WCD938X_DSD_PORT, BIT(1)), +}; + +static struct wcd938x_sdw_ch_info wcd938x_sdw_tx_ch_info[] = { + WCD_SDW_CH(WCD938X_ADC1, WCD938X_ADC_1_2_PORT, BIT(0)), + WCD_SDW_CH(WCD938X_ADC2, WCD938X_ADC_1_2_PORT, BIT(1)), + WCD_SDW_CH(WCD938X_ADC3, WCD938X_ADC_3_4_PORT, BIT(0)), + WCD_SDW_CH(WCD938X_ADC4, WCD938X_ADC_3_4_PORT, BIT(1)), + WCD_SDW_CH(WCD938X_DMIC0, WCD938X_DMIC_0_3_MBHC_PORT, BIT(0)), + WCD_SDW_CH(WCD938X_DMIC1, WCD938X_DMIC_0_3_MBHC_PORT, BIT(1)), + WCD_SDW_CH(WCD938X_MBHC, WCD938X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD938X_DMIC2, WCD938X_DMIC_0_3_MBHC_PORT, BIT(2)), + WCD_SDW_CH(WCD938X_DMIC3, WCD938X_DMIC_0_3_MBHC_PORT, BIT(3)), + WCD_SDW_CH(WCD938X_DMIC4, WCD938X_DMIC_4_7_PORT, BIT(0)), + WCD_SDW_CH(WCD938X_DMIC5, WCD938X_DMIC_4_7_PORT, BIT(1)), + WCD_SDW_CH(WCD938X_DMIC6, WCD938X_DMIC_4_7_PORT, BIT(2)), + WCD_SDW_CH(WCD938X_DMIC7, WCD938X_DMIC_4_7_PORT, BIT(3)), +}; + +static struct sdw_dpn_prop wcd938x_dpn_prop[WCD938X_MAX_SWR_PORTS] = { + { + .num = 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 8, + .simple_ch_prep_sm = true, + }, { + .num = 2, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, { + .num = 3, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, { + .num = 4, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + }, { + .num = 5, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 4, + .simple_ch_prep_sm = true, + } +}; + +struct device *wcd938x_sdw_device_get(struct device_node *np) +{ + return bus_find_device_by_of_node(&sdw_bus_type, np); + +} +EXPORT_SYMBOL_GPL(wcd938x_sdw_device_get); + +int wcd938x_swr_get_current_bank(struct sdw_slave *sdev) +{ + int bank; + + bank = sdw_read(sdev, SDW_SCP_CTRL); + + return ((bank & 0x40) ? 1 : 0); +} +EXPORT_SYMBOL_GPL(wcd938x_swr_get_current_bank); + +int wcd938x_sdw_hw_params(struct wcd938x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sdw_port_config port_config[WCD938X_MAX_SWR_PORTS]; + unsigned long ch_mask; + int i, j; + + wcd->sconfig.ch_count = 1; + wcd->active_ports = 0; + for (i = 0; i < WCD938X_MAX_SWR_PORTS; i++) { + ch_mask = wcd->port_config[i].ch_mask; + + if (!ch_mask) + continue; + + for_each_set_bit(j, &ch_mask, 4) + wcd->sconfig.ch_count++; + + port_config[wcd->active_ports] = wcd->port_config[i]; + wcd->active_ports++; + } + + wcd->sconfig.bps = 1; + wcd->sconfig.frame_rate = params_rate(params); + if (wcd->is_tx) + wcd->sconfig.direction = SDW_DATA_DIR_TX; + else + wcd->sconfig.direction = SDW_DATA_DIR_RX; + + wcd->sconfig.type = SDW_STREAM_PCM; + + return sdw_stream_add_slave(wcd->sdev, &wcd->sconfig, + &port_config[0], wcd->active_ports, + wcd->sruntime); +} +EXPORT_SYMBOL_GPL(wcd938x_sdw_hw_params); + +int wcd938x_sdw_free(struct wcd938x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + sdw_stream_remove_slave(wcd->sdev, wcd->sruntime); + + return 0; +} +EXPORT_SYMBOL_GPL(wcd938x_sdw_free); + +int wcd938x_sdw_set_sdw_stream(struct wcd938x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction) +{ + wcd->sruntime = stream; + + return 0; +} +EXPORT_SYMBOL_GPL(wcd938x_sdw_set_sdw_stream); + +static int wcd9380_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wcd938x_sdw_priv *wcd = dev_get_drvdata(&slave->dev); + + if (wcd->regmap && (status == SDW_SLAVE_ATTACHED)) { + /* Write out any cached changes that happened between probe and attach */ + regcache_cache_only(wcd->regmap, false); + return regcache_sync(wcd->regmap); + } + + return 0; +} + +static int wcd9380_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), 0x01); + + return 0; +} + +static int wcd9380_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct wcd938x_sdw_priv *wcd = dev_get_drvdata(&slave->dev); + struct irq_domain *slave_irq = wcd->slave_irq; + u32 sts1, sts2, sts3; + + do { + handle_nested_irq(irq_find_mapping(slave_irq, 0)); + regmap_read(wcd->regmap, WCD938X_DIGITAL_INTR_STATUS_0, &sts1); + regmap_read(wcd->regmap, WCD938X_DIGITAL_INTR_STATUS_1, &sts2); + regmap_read(wcd->regmap, WCD938X_DIGITAL_INTR_STATUS_2, &sts3); + + } while (sts1 || sts2 || sts3); + + return IRQ_HANDLED; +} + +static const struct reg_default wcd938x_defaults[] = { + {WCD938X_ANA_PAGE_REGISTER, 0x00}, + {WCD938X_ANA_BIAS, 0x00}, + {WCD938X_ANA_RX_SUPPLIES, 0x00}, + {WCD938X_ANA_HPH, 0x0C}, + {WCD938X_ANA_EAR, 0x00}, + {WCD938X_ANA_EAR_COMPANDER_CTL, 0x02}, + {WCD938X_ANA_TX_CH1, 0x20}, + {WCD938X_ANA_TX_CH2, 0x00}, + {WCD938X_ANA_TX_CH3, 0x20}, + {WCD938X_ANA_TX_CH4, 0x00}, + {WCD938X_ANA_MICB1_MICB2_DSP_EN_LOGIC, 0x00}, + {WCD938X_ANA_MICB3_DSP_EN_LOGIC, 0x00}, + {WCD938X_ANA_MBHC_MECH, 0x39}, + {WCD938X_ANA_MBHC_ELECT, 0x08}, + {WCD938X_ANA_MBHC_ZDET, 0x00}, + {WCD938X_ANA_MBHC_RESULT_1, 0x00}, + {WCD938X_ANA_MBHC_RESULT_2, 0x00}, + {WCD938X_ANA_MBHC_RESULT_3, 0x00}, + {WCD938X_ANA_MBHC_BTN0, 0x00}, + {WCD938X_ANA_MBHC_BTN1, 0x10}, + {WCD938X_ANA_MBHC_BTN2, 0x20}, + {WCD938X_ANA_MBHC_BTN3, 0x30}, + {WCD938X_ANA_MBHC_BTN4, 0x40}, + {WCD938X_ANA_MBHC_BTN5, 0x50}, + {WCD938X_ANA_MBHC_BTN6, 0x60}, + {WCD938X_ANA_MBHC_BTN7, 0x70}, + {WCD938X_ANA_MICB1, 0x10}, + {WCD938X_ANA_MICB2, 0x10}, + {WCD938X_ANA_MICB2_RAMP, 0x00}, + {WCD938X_ANA_MICB3, 0x10}, + {WCD938X_ANA_MICB4, 0x10}, + {WCD938X_BIAS_CTL, 0x2A}, + {WCD938X_BIAS_VBG_FINE_ADJ, 0x55}, + {WCD938X_LDOL_VDDCX_ADJUST, 0x01}, + {WCD938X_LDOL_DISABLE_LDOL, 0x00}, + {WCD938X_MBHC_CTL_CLK, 0x00}, + {WCD938X_MBHC_CTL_ANA, 0x00}, + {WCD938X_MBHC_CTL_SPARE_1, 0x00}, + {WCD938X_MBHC_CTL_SPARE_2, 0x00}, + {WCD938X_MBHC_CTL_BCS, 0x00}, + {WCD938X_MBHC_MOISTURE_DET_FSM_STATUS, 0x00}, + {WCD938X_MBHC_TEST_CTL, 0x00}, + {WCD938X_LDOH_MODE, 0x2B}, + {WCD938X_LDOH_BIAS, 0x68}, + {WCD938X_LDOH_STB_LOADS, 0x00}, + {WCD938X_LDOH_SLOWRAMP, 0x50}, + {WCD938X_MICB1_TEST_CTL_1, 0x1A}, + {WCD938X_MICB1_TEST_CTL_2, 0x00}, + {WCD938X_MICB1_TEST_CTL_3, 0xA4}, + {WCD938X_MICB2_TEST_CTL_1, 0x1A}, + {WCD938X_MICB2_TEST_CTL_2, 0x00}, + {WCD938X_MICB2_TEST_CTL_3, 0x24}, + {WCD938X_MICB3_TEST_CTL_1, 0x1A}, + {WCD938X_MICB3_TEST_CTL_2, 0x00}, + {WCD938X_MICB3_TEST_CTL_3, 0xA4}, + {WCD938X_MICB4_TEST_CTL_1, 0x1A}, + {WCD938X_MICB4_TEST_CTL_2, 0x00}, + {WCD938X_MICB4_TEST_CTL_3, 0xA4}, + {WCD938X_TX_COM_ADC_VCM, 0x39}, + {WCD938X_TX_COM_BIAS_ATEST, 0xE0}, + {WCD938X_TX_COM_SPARE1, 0x00}, + {WCD938X_TX_COM_SPARE2, 0x00}, + {WCD938X_TX_COM_TXFE_DIV_CTL, 0x22}, + {WCD938X_TX_COM_TXFE_DIV_START, 0x00}, + {WCD938X_TX_COM_SPARE3, 0x00}, + {WCD938X_TX_COM_SPARE4, 0x00}, + {WCD938X_TX_1_2_TEST_EN, 0xCC}, + {WCD938X_TX_1_2_ADC_IB, 0xE9}, + {WCD938X_TX_1_2_ATEST_REFCTL, 0x0A}, + {WCD938X_TX_1_2_TEST_CTL, 0x38}, + {WCD938X_TX_1_2_TEST_BLK_EN1, 0xFF}, + {WCD938X_TX_1_2_TXFE1_CLKDIV, 0x00}, + {WCD938X_TX_1_2_SAR2_ERR, 0x00}, + {WCD938X_TX_1_2_SAR1_ERR, 0x00}, + {WCD938X_TX_3_4_TEST_EN, 0xCC}, + {WCD938X_TX_3_4_ADC_IB, 0xE9}, + {WCD938X_TX_3_4_ATEST_REFCTL, 0x0A}, + {WCD938X_TX_3_4_TEST_CTL, 0x38}, + {WCD938X_TX_3_4_TEST_BLK_EN3, 0xFF}, + {WCD938X_TX_3_4_TXFE3_CLKDIV, 0x00}, + {WCD938X_TX_3_4_SAR4_ERR, 0x00}, + {WCD938X_TX_3_4_SAR3_ERR, 0x00}, + {WCD938X_TX_3_4_TEST_BLK_EN2, 0xFB}, + {WCD938X_TX_3_4_TXFE2_CLKDIV, 0x00}, + {WCD938X_TX_3_4_SPARE1, 0x00}, + {WCD938X_TX_3_4_TEST_BLK_EN4, 0xFB}, + {WCD938X_TX_3_4_TXFE4_CLKDIV, 0x00}, + {WCD938X_TX_3_4_SPARE2, 0x00}, + {WCD938X_CLASSH_MODE_1, 0x40}, + {WCD938X_CLASSH_MODE_2, 0x3A}, + {WCD938X_CLASSH_MODE_3, 0x00}, + {WCD938X_CLASSH_CTRL_VCL_1, 0x70}, + {WCD938X_CLASSH_CTRL_VCL_2, 0x82}, + {WCD938X_CLASSH_CTRL_CCL_1, 0x31}, + {WCD938X_CLASSH_CTRL_CCL_2, 0x80}, + {WCD938X_CLASSH_CTRL_CCL_3, 0x80}, + {WCD938X_CLASSH_CTRL_CCL_4, 0x51}, + {WCD938X_CLASSH_CTRL_CCL_5, 0x00}, + {WCD938X_CLASSH_BUCK_TMUX_A_D, 0x00}, + {WCD938X_CLASSH_BUCK_SW_DRV_CNTL, 0x77}, + {WCD938X_CLASSH_SPARE, 0x00}, + {WCD938X_FLYBACK_EN, 0x4E}, + {WCD938X_FLYBACK_VNEG_CTRL_1, 0x0B}, + {WCD938X_FLYBACK_VNEG_CTRL_2, 0x45}, + {WCD938X_FLYBACK_VNEG_CTRL_3, 0x74}, + {WCD938X_FLYBACK_VNEG_CTRL_4, 0x7F}, + {WCD938X_FLYBACK_VNEG_CTRL_5, 0x83}, + {WCD938X_FLYBACK_VNEG_CTRL_6, 0x98}, + {WCD938X_FLYBACK_VNEG_CTRL_7, 0xA9}, + {WCD938X_FLYBACK_VNEG_CTRL_8, 0x68}, + {WCD938X_FLYBACK_VNEG_CTRL_9, 0x64}, + {WCD938X_FLYBACK_VNEGDAC_CTRL_1, 0xED}, + {WCD938X_FLYBACK_VNEGDAC_CTRL_2, 0xF0}, + {WCD938X_FLYBACK_VNEGDAC_CTRL_3, 0xA6}, + {WCD938X_FLYBACK_CTRL_1, 0x65}, + {WCD938X_FLYBACK_TEST_CTL, 0x00}, + {WCD938X_RX_AUX_SW_CTL, 0x00}, + {WCD938X_RX_PA_AUX_IN_CONN, 0x01}, + {WCD938X_RX_TIMER_DIV, 0x32}, + {WCD938X_RX_OCP_CTL, 0x1F}, + {WCD938X_RX_OCP_COUNT, 0x77}, + {WCD938X_RX_BIAS_EAR_DAC, 0xA0}, + {WCD938X_RX_BIAS_EAR_AMP, 0xAA}, + {WCD938X_RX_BIAS_HPH_LDO, 0xA9}, + {WCD938X_RX_BIAS_HPH_PA, 0xAA}, + {WCD938X_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8A}, + {WCD938X_RX_BIAS_HPH_RDAC_LDO, 0x88}, + {WCD938X_RX_BIAS_HPH_CNP1, 0x82}, + {WCD938X_RX_BIAS_HPH_LOWPOWER, 0x82}, + {WCD938X_RX_BIAS_AUX_DAC, 0xA0}, + {WCD938X_RX_BIAS_AUX_AMP, 0xAA}, + {WCD938X_RX_BIAS_VNEGDAC_BLEEDER, 0x50}, + {WCD938X_RX_BIAS_MISC, 0x00}, + {WCD938X_RX_BIAS_BUCK_RST, 0x08}, + {WCD938X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44}, + {WCD938X_RX_BIAS_FLYB_ERRAMP, 0x40}, + {WCD938X_RX_BIAS_FLYB_BUFF, 0xAA}, + {WCD938X_RX_BIAS_FLYB_MID_RST, 0x14}, + {WCD938X_HPH_L_STATUS, 0x04}, + {WCD938X_HPH_R_STATUS, 0x04}, + {WCD938X_HPH_CNP_EN, 0x80}, + {WCD938X_HPH_CNP_WG_CTL, 0x9A}, + {WCD938X_HPH_CNP_WG_TIME, 0x14}, + {WCD938X_HPH_OCP_CTL, 0x28}, + {WCD938X_HPH_AUTO_CHOP, 0x16}, + {WCD938X_HPH_CHOP_CTL, 0x83}, + {WCD938X_HPH_PA_CTL1, 0x46}, + {WCD938X_HPH_PA_CTL2, 0x50}, + {WCD938X_HPH_L_EN, 0x80}, + {WCD938X_HPH_L_TEST, 0xE0}, + {WCD938X_HPH_L_ATEST, 0x50}, + {WCD938X_HPH_R_EN, 0x80}, + {WCD938X_HPH_R_TEST, 0xE0}, + {WCD938X_HPH_R_ATEST, 0x54}, + {WCD938X_HPH_RDAC_CLK_CTL1, 0x99}, + {WCD938X_HPH_RDAC_CLK_CTL2, 0x9B}, + {WCD938X_HPH_RDAC_LDO_CTL, 0x33}, + {WCD938X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00}, + {WCD938X_HPH_REFBUFF_UHQA_CTL, 0x68}, + {WCD938X_HPH_REFBUFF_LP_CTL, 0x0E}, + {WCD938X_HPH_L_DAC_CTL, 0x20}, + {WCD938X_HPH_R_DAC_CTL, 0x20}, + {WCD938X_HPH_SURGE_HPHLR_SURGE_COMP_SEL, 0x55}, + {WCD938X_HPH_SURGE_HPHLR_SURGE_EN, 0x19}, + {WCD938X_HPH_SURGE_HPHLR_SURGE_MISC1, 0xA0}, + {WCD938X_HPH_SURGE_HPHLR_SURGE_STATUS, 0x00}, + {WCD938X_EAR_EAR_EN_REG, 0x22}, + {WCD938X_EAR_EAR_PA_CON, 0x44}, + {WCD938X_EAR_EAR_SP_CON, 0xDB}, + {WCD938X_EAR_EAR_DAC_CON, 0x80}, + {WCD938X_EAR_EAR_CNP_FSM_CON, 0xB2}, + {WCD938X_EAR_TEST_CTL, 0x00}, + {WCD938X_EAR_STATUS_REG_1, 0x00}, + {WCD938X_EAR_STATUS_REG_2, 0x08}, + {WCD938X_ANA_NEW_PAGE_REGISTER, 0x00}, + {WCD938X_HPH_NEW_ANA_HPH2, 0x00}, + {WCD938X_HPH_NEW_ANA_HPH3, 0x00}, + {WCD938X_SLEEP_CTL, 0x16}, + {WCD938X_SLEEP_WATCHDOG_CTL, 0x00}, + {WCD938X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00}, + {WCD938X_MBHC_NEW_CTL_1, 0x02}, + {WCD938X_MBHC_NEW_CTL_2, 0x05}, + {WCD938X_MBHC_NEW_PLUG_DETECT_CTL, 0xE9}, + {WCD938X_MBHC_NEW_ZDET_ANA_CTL, 0x0F}, + {WCD938X_MBHC_NEW_ZDET_RAMP_CTL, 0x00}, + {WCD938X_MBHC_NEW_FSM_STATUS, 0x00}, + {WCD938X_MBHC_NEW_ADC_RESULT, 0x00}, + {WCD938X_TX_NEW_AMIC_MUX_CFG, 0x00}, + {WCD938X_AUX_AUXPA, 0x00}, + {WCD938X_LDORXTX_MODE, 0x0C}, + {WCD938X_LDORXTX_CONFIG, 0x10}, + {WCD938X_DIE_CRACK_DIE_CRK_DET_EN, 0x00}, + {WCD938X_DIE_CRACK_DIE_CRK_DET_OUT, 0x00}, + {WCD938X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40}, + {WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81}, + {WCD938X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10}, + {WCD938X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00}, + {WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81}, + {WCD938X_HPH_NEW_INT_PA_MISC1, 0x22}, + {WCD938X_HPH_NEW_INT_PA_MISC2, 0x00}, + {WCD938X_HPH_NEW_INT_PA_RDAC_MISC, 0x00}, + {WCD938X_HPH_NEW_INT_HPH_TIMER1, 0xFE}, + {WCD938X_HPH_NEW_INT_HPH_TIMER2, 0x02}, + {WCD938X_HPH_NEW_INT_HPH_TIMER3, 0x4E}, + {WCD938X_HPH_NEW_INT_HPH_TIMER4, 0x54}, + {WCD938X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00}, + {WCD938X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00}, + {WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L_NEW, 0x90}, + {WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R_NEW, 0x90}, + {WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x62}, + {WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01}, + {WCD938X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11}, + {WCD938X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL, 0x57}, + {WCD938X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, 0x01}, + {WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x00}, + {WCD938X_MBHC_NEW_INT_SPARE_2, 0x00}, + {WCD938X_EAR_INT_NEW_EAR_CHOPPER_CON, 0xA8}, + {WCD938X_EAR_INT_NEW_CNP_VCM_CON1, 0x42}, + {WCD938X_EAR_INT_NEW_CNP_VCM_CON2, 0x22}, + {WCD938X_EAR_INT_NEW_EAR_DYNAMIC_BIAS, 0x00}, + {WCD938X_AUX_INT_EN_REG, 0x00}, + {WCD938X_AUX_INT_PA_CTRL, 0x06}, + {WCD938X_AUX_INT_SP_CTRL, 0xD2}, + {WCD938X_AUX_INT_DAC_CTRL, 0x80}, + {WCD938X_AUX_INT_CLK_CTRL, 0x50}, + {WCD938X_AUX_INT_TEST_CTRL, 0x00}, + {WCD938X_AUX_INT_STATUS_REG, 0x00}, + {WCD938X_AUX_INT_MISC, 0x00}, + {WCD938X_LDORXTX_INT_BIAS, 0x6E}, + {WCD938X_LDORXTX_INT_STB_LOADS_DTEST, 0x50}, + {WCD938X_LDORXTX_INT_TEST0, 0x1C}, + {WCD938X_LDORXTX_INT_STARTUP_TIMER, 0xFF}, + {WCD938X_LDORXTX_INT_TEST1, 0x1F}, + {WCD938X_LDORXTX_INT_STATUS, 0x00}, + {WCD938X_SLEEP_INT_WATCHDOG_CTL_1, 0x0A}, + {WCD938X_SLEEP_INT_WATCHDOG_CTL_2, 0x0A}, + {WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT1, 0x02}, + {WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT2, 0x60}, + {WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L2, 0xFF}, + {WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L1, 0x7F}, + {WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L0, 0x3F}, + {WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP1P2M, 0x1F}, + {WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP0P6M, 0x0F}, + {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L2L1, 0xD7}, + {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L0, 0xC8}, + {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_ULP, 0xC6}, + {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L2L1, 0xD5}, + {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L0, 0xCA}, + {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_ULP, 0x05}, + {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_L2L1L0, 0xA5}, + {WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_ULP, 0x13}, + {WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L2L1, 0x88}, + {WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L0ULP, 0x42}, + {WCD938X_TX_COM_NEW_INT_TXADC_INT_L2, 0xFF}, + {WCD938X_TX_COM_NEW_INT_TXADC_INT_L1, 0x64}, + {WCD938X_TX_COM_NEW_INT_TXADC_INT_L0, 0x64}, + {WCD938X_TX_COM_NEW_INT_TXADC_INT_ULP, 0x77}, + {WCD938X_DIGITAL_PAGE_REGISTER, 0x00}, + {WCD938X_DIGITAL_CHIP_ID0, 0x00}, + {WCD938X_DIGITAL_CHIP_ID1, 0x00}, + {WCD938X_DIGITAL_CHIP_ID2, 0x0D}, + {WCD938X_DIGITAL_CHIP_ID3, 0x01}, + {WCD938X_DIGITAL_SWR_TX_CLK_RATE, 0x00}, + {WCD938X_DIGITAL_CDC_RST_CTL, 0x03}, + {WCD938X_DIGITAL_TOP_CLK_CFG, 0x00}, + {WCD938X_DIGITAL_CDC_ANA_CLK_CTL, 0x00}, + {WCD938X_DIGITAL_CDC_DIG_CLK_CTL, 0xF0}, + {WCD938X_DIGITAL_SWR_RST_EN, 0x00}, + {WCD938X_DIGITAL_CDC_PATH_MODE, 0x55}, + {WCD938X_DIGITAL_CDC_RX_RST, 0x00}, + {WCD938X_DIGITAL_CDC_RX0_CTL, 0xFC}, + {WCD938X_DIGITAL_CDC_RX1_CTL, 0xFC}, + {WCD938X_DIGITAL_CDC_RX2_CTL, 0xFC}, + {WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1, 0x00}, + {WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3, 0x00}, + {WCD938X_DIGITAL_CDC_COMP_CTL_0, 0x00}, + {WCD938X_DIGITAL_CDC_ANA_TX_CLK_CTL, 0x1E}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A1_0, 0x00}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A1_1, 0x01}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A2_0, 0x63}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A2_1, 0x04}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A3_0, 0xAC}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A3_1, 0x04}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A4_0, 0x1A}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A4_1, 0x03}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A5_0, 0xBC}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A5_1, 0x02}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A6_0, 0xC7}, + {WCD938X_DIGITAL_CDC_HPH_DSM_A7_0, 0xF8}, + {WCD938X_DIGITAL_CDC_HPH_DSM_C_0, 0x47}, + {WCD938X_DIGITAL_CDC_HPH_DSM_C_1, 0x43}, + {WCD938X_DIGITAL_CDC_HPH_DSM_C_2, 0xB1}, + {WCD938X_DIGITAL_CDC_HPH_DSM_C_3, 0x17}, + {WCD938X_DIGITAL_CDC_HPH_DSM_R1, 0x4D}, + {WCD938X_DIGITAL_CDC_HPH_DSM_R2, 0x29}, + {WCD938X_DIGITAL_CDC_HPH_DSM_R3, 0x34}, + {WCD938X_DIGITAL_CDC_HPH_DSM_R4, 0x59}, + {WCD938X_DIGITAL_CDC_HPH_DSM_R5, 0x66}, + {WCD938X_DIGITAL_CDC_HPH_DSM_R6, 0x87}, + {WCD938X_DIGITAL_CDC_HPH_DSM_R7, 0x64}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A1_0, 0x00}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A1_1, 0x01}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A2_0, 0x96}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A2_1, 0x09}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A3_0, 0xAB}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A3_1, 0x05}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A4_0, 0x1C}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A4_1, 0x02}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A5_0, 0x17}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A5_1, 0x02}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A6_0, 0xAA}, + {WCD938X_DIGITAL_CDC_AUX_DSM_A7_0, 0xE3}, + {WCD938X_DIGITAL_CDC_AUX_DSM_C_0, 0x69}, + {WCD938X_DIGITAL_CDC_AUX_DSM_C_1, 0x54}, + {WCD938X_DIGITAL_CDC_AUX_DSM_C_2, 0x02}, + {WCD938X_DIGITAL_CDC_AUX_DSM_C_3, 0x15}, + {WCD938X_DIGITAL_CDC_AUX_DSM_R1, 0xA4}, + {WCD938X_DIGITAL_CDC_AUX_DSM_R2, 0xB5}, + {WCD938X_DIGITAL_CDC_AUX_DSM_R3, 0x86}, + {WCD938X_DIGITAL_CDC_AUX_DSM_R4, 0x85}, + {WCD938X_DIGITAL_CDC_AUX_DSM_R5, 0xAA}, + {WCD938X_DIGITAL_CDC_AUX_DSM_R6, 0xE2}, + {WCD938X_DIGITAL_CDC_AUX_DSM_R7, 0x62}, + {WCD938X_DIGITAL_CDC_HPH_GAIN_RX_0, 0x55}, + {WCD938X_DIGITAL_CDC_HPH_GAIN_RX_1, 0xA9}, + {WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_0, 0x3D}, + {WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_1, 0x2E}, + {WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_2, 0x01}, + {WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_0, 0x00}, + {WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_1, 0xFC}, + {WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_2, 0x01}, + {WCD938X_DIGITAL_CDC_HPH_GAIN_CTL, 0x00}, + {WCD938X_DIGITAL_CDC_AUX_GAIN_CTL, 0x00}, + {WCD938X_DIGITAL_CDC_EAR_PATH_CTL, 0x00}, + {WCD938X_DIGITAL_CDC_SWR_CLH, 0x00}, + {WCD938X_DIGITAL_SWR_CLH_BYP, 0x00}, + {WCD938X_DIGITAL_CDC_TX0_CTL, 0x68}, + {WCD938X_DIGITAL_CDC_TX1_CTL, 0x68}, + {WCD938X_DIGITAL_CDC_TX2_CTL, 0x68}, + {WCD938X_DIGITAL_CDC_TX_RST, 0x00}, + {WCD938X_DIGITAL_CDC_REQ_CTL, 0x01}, + {WCD938X_DIGITAL_CDC_RST, 0x00}, + {WCD938X_DIGITAL_CDC_AMIC_CTL, 0x0F}, + {WCD938X_DIGITAL_CDC_DMIC_CTL, 0x04}, + {WCD938X_DIGITAL_CDC_DMIC1_CTL, 0x01}, + {WCD938X_DIGITAL_CDC_DMIC2_CTL, 0x01}, + {WCD938X_DIGITAL_CDC_DMIC3_CTL, 0x01}, + {WCD938X_DIGITAL_CDC_DMIC4_CTL, 0x01}, + {WCD938X_DIGITAL_EFUSE_PRG_CTL, 0x00}, + {WCD938X_DIGITAL_EFUSE_CTL, 0x2B}, + {WCD938X_DIGITAL_CDC_DMIC_RATE_1_2, 0x11}, + {WCD938X_DIGITAL_CDC_DMIC_RATE_3_4, 0x11}, + {WCD938X_DIGITAL_PDM_WD_CTL0, 0x00}, + {WCD938X_DIGITAL_PDM_WD_CTL1, 0x00}, + {WCD938X_DIGITAL_PDM_WD_CTL2, 0x00}, + {WCD938X_DIGITAL_INTR_MODE, 0x00}, + {WCD938X_DIGITAL_INTR_MASK_0, 0xFF}, + {WCD938X_DIGITAL_INTR_MASK_1, 0xFF}, + {WCD938X_DIGITAL_INTR_MASK_2, 0x3F}, + {WCD938X_DIGITAL_INTR_STATUS_0, 0x00}, + {WCD938X_DIGITAL_INTR_STATUS_1, 0x00}, + {WCD938X_DIGITAL_INTR_STATUS_2, 0x00}, + {WCD938X_DIGITAL_INTR_CLEAR_0, 0x00}, + {WCD938X_DIGITAL_INTR_CLEAR_1, 0x00}, + {WCD938X_DIGITAL_INTR_CLEAR_2, 0x00}, + {WCD938X_DIGITAL_INTR_LEVEL_0, 0x00}, + {WCD938X_DIGITAL_INTR_LEVEL_1, 0x00}, + {WCD938X_DIGITAL_INTR_LEVEL_2, 0x00}, + {WCD938X_DIGITAL_INTR_SET_0, 0x00}, + {WCD938X_DIGITAL_INTR_SET_1, 0x00}, + {WCD938X_DIGITAL_INTR_SET_2, 0x00}, + {WCD938X_DIGITAL_INTR_TEST_0, 0x00}, + {WCD938X_DIGITAL_INTR_TEST_1, 0x00}, + {WCD938X_DIGITAL_INTR_TEST_2, 0x00}, + {WCD938X_DIGITAL_TX_MODE_DBG_EN, 0x00}, + {WCD938X_DIGITAL_TX_MODE_DBG_0_1, 0x00}, + {WCD938X_DIGITAL_TX_MODE_DBG_2_3, 0x00}, + {WCD938X_DIGITAL_LB_IN_SEL_CTL, 0x00}, + {WCD938X_DIGITAL_LOOP_BACK_MODE, 0x00}, + {WCD938X_DIGITAL_SWR_DAC_TEST, 0x00}, + {WCD938X_DIGITAL_SWR_HM_TEST_RX_0, 0x40}, + {WCD938X_DIGITAL_SWR_HM_TEST_TX_0, 0x40}, + {WCD938X_DIGITAL_SWR_HM_TEST_RX_1, 0x00}, + {WCD938X_DIGITAL_SWR_HM_TEST_TX_1, 0x00}, + {WCD938X_DIGITAL_SWR_HM_TEST_TX_2, 0x00}, + {WCD938X_DIGITAL_SWR_HM_TEST_0, 0x00}, + {WCD938X_DIGITAL_SWR_HM_TEST_1, 0x00}, + {WCD938X_DIGITAL_PAD_CTL_SWR_0, 0x8F}, + {WCD938X_DIGITAL_PAD_CTL_SWR_1, 0x06}, + {WCD938X_DIGITAL_I2C_CTL, 0x00}, + {WCD938X_DIGITAL_CDC_TX_TANGGU_SW_MODE, 0x00}, + {WCD938X_DIGITAL_EFUSE_TEST_CTL_0, 0x00}, + {WCD938X_DIGITAL_EFUSE_TEST_CTL_1, 0x00}, + {WCD938X_DIGITAL_EFUSE_T_DATA_0, 0x00}, + {WCD938X_DIGITAL_EFUSE_T_DATA_1, 0x00}, + {WCD938X_DIGITAL_PAD_CTL_PDM_RX0, 0xF1}, + {WCD938X_DIGITAL_PAD_CTL_PDM_RX1, 0xF1}, + {WCD938X_DIGITAL_PAD_CTL_PDM_TX0, 0xF1}, + {WCD938X_DIGITAL_PAD_CTL_PDM_TX1, 0xF1}, + {WCD938X_DIGITAL_PAD_CTL_PDM_TX2, 0xF1}, + {WCD938X_DIGITAL_PAD_INP_DIS_0, 0x00}, + {WCD938X_DIGITAL_PAD_INP_DIS_1, 0x00}, + {WCD938X_DIGITAL_DRIVE_STRENGTH_0, 0x00}, + {WCD938X_DIGITAL_DRIVE_STRENGTH_1, 0x00}, + {WCD938X_DIGITAL_DRIVE_STRENGTH_2, 0x00}, + {WCD938X_DIGITAL_RX_DATA_EDGE_CTL, 0x1F}, + {WCD938X_DIGITAL_TX_DATA_EDGE_CTL, 0x80}, + {WCD938X_DIGITAL_GPIO_MODE, 0x00}, + {WCD938X_DIGITAL_PIN_CTL_OE, 0x00}, + {WCD938X_DIGITAL_PIN_CTL_DATA_0, 0x00}, + {WCD938X_DIGITAL_PIN_CTL_DATA_1, 0x00}, + {WCD938X_DIGITAL_PIN_STATUS_0, 0x00}, + {WCD938X_DIGITAL_PIN_STATUS_1, 0x00}, + {WCD938X_DIGITAL_DIG_DEBUG_CTL, 0x00}, + {WCD938X_DIGITAL_DIG_DEBUG_EN, 0x00}, + {WCD938X_DIGITAL_ANA_CSR_DBG_ADD, 0x00}, + {WCD938X_DIGITAL_ANA_CSR_DBG_CTL, 0x48}, + {WCD938X_DIGITAL_SSP_DBG, 0x00}, + {WCD938X_DIGITAL_MODE_STATUS_0, 0x00}, + {WCD938X_DIGITAL_MODE_STATUS_1, 0x00}, + {WCD938X_DIGITAL_SPARE_0, 0x00}, + {WCD938X_DIGITAL_SPARE_1, 0x00}, + {WCD938X_DIGITAL_SPARE_2, 0x00}, + {WCD938X_DIGITAL_EFUSE_REG_0, 0x00}, + {WCD938X_DIGITAL_EFUSE_REG_1, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_2, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_3, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_4, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_5, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_6, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_7, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_8, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_9, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_10, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_11, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_12, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_13, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_14, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_15, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_16, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_17, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_18, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_19, 0xFF}, + {WCD938X_DIGITAL_EFUSE_REG_20, 0x0E}, + {WCD938X_DIGITAL_EFUSE_REG_21, 0x00}, + {WCD938X_DIGITAL_EFUSE_REG_22, 0x00}, + {WCD938X_DIGITAL_EFUSE_REG_23, 0xF8}, + {WCD938X_DIGITAL_EFUSE_REG_24, 0x16}, + {WCD938X_DIGITAL_EFUSE_REG_25, 0x00}, + {WCD938X_DIGITAL_EFUSE_REG_26, 0x00}, + {WCD938X_DIGITAL_EFUSE_REG_27, 0x00}, + {WCD938X_DIGITAL_EFUSE_REG_28, 0x00}, + {WCD938X_DIGITAL_EFUSE_REG_29, 0x00}, + {WCD938X_DIGITAL_EFUSE_REG_30, 0x00}, + {WCD938X_DIGITAL_EFUSE_REG_31, 0x00}, + {WCD938X_DIGITAL_TX_REQ_FB_CTL_0, 0x88}, + {WCD938X_DIGITAL_TX_REQ_FB_CTL_1, 0x88}, + {WCD938X_DIGITAL_TX_REQ_FB_CTL_2, 0x88}, + {WCD938X_DIGITAL_TX_REQ_FB_CTL_3, 0x88}, + {WCD938X_DIGITAL_TX_REQ_FB_CTL_4, 0x88}, + {WCD938X_DIGITAL_DEM_BYPASS_DATA0, 0x55}, + {WCD938X_DIGITAL_DEM_BYPASS_DATA1, 0x55}, + {WCD938X_DIGITAL_DEM_BYPASS_DATA2, 0x55}, + {WCD938X_DIGITAL_DEM_BYPASS_DATA3, 0x01}, +}; + +static bool wcd938x_rdwr_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD938X_ANA_PAGE_REGISTER: + case WCD938X_ANA_BIAS: + case WCD938X_ANA_RX_SUPPLIES: + case WCD938X_ANA_HPH: + case WCD938X_ANA_EAR: + case WCD938X_ANA_EAR_COMPANDER_CTL: + case WCD938X_ANA_TX_CH1: + case WCD938X_ANA_TX_CH2: + case WCD938X_ANA_TX_CH3: + case WCD938X_ANA_TX_CH4: + case WCD938X_ANA_MICB1_MICB2_DSP_EN_LOGIC: + case WCD938X_ANA_MICB3_DSP_EN_LOGIC: + case WCD938X_ANA_MBHC_MECH: + case WCD938X_ANA_MBHC_ELECT: + case WCD938X_ANA_MBHC_ZDET: + case WCD938X_ANA_MBHC_BTN0: + case WCD938X_ANA_MBHC_BTN1: + case WCD938X_ANA_MBHC_BTN2: + case WCD938X_ANA_MBHC_BTN3: + case WCD938X_ANA_MBHC_BTN4: + case WCD938X_ANA_MBHC_BTN5: + case WCD938X_ANA_MBHC_BTN6: + case WCD938X_ANA_MBHC_BTN7: + case WCD938X_ANA_MICB1: + case WCD938X_ANA_MICB2: + case WCD938X_ANA_MICB2_RAMP: + case WCD938X_ANA_MICB3: + case WCD938X_ANA_MICB4: + case WCD938X_BIAS_CTL: + case WCD938X_BIAS_VBG_FINE_ADJ: + case WCD938X_LDOL_VDDCX_ADJUST: + case WCD938X_LDOL_DISABLE_LDOL: + case WCD938X_MBHC_CTL_CLK: + case WCD938X_MBHC_CTL_ANA: + case WCD938X_MBHC_CTL_SPARE_1: + case WCD938X_MBHC_CTL_SPARE_2: + case WCD938X_MBHC_CTL_BCS: + case WCD938X_MBHC_TEST_CTL: + case WCD938X_LDOH_MODE: + case WCD938X_LDOH_BIAS: + case WCD938X_LDOH_STB_LOADS: + case WCD938X_LDOH_SLOWRAMP: + case WCD938X_MICB1_TEST_CTL_1: + case WCD938X_MICB1_TEST_CTL_2: + case WCD938X_MICB1_TEST_CTL_3: + case WCD938X_MICB2_TEST_CTL_1: + case WCD938X_MICB2_TEST_CTL_2: + case WCD938X_MICB2_TEST_CTL_3: + case WCD938X_MICB3_TEST_CTL_1: + case WCD938X_MICB3_TEST_CTL_2: + case WCD938X_MICB3_TEST_CTL_3: + case WCD938X_MICB4_TEST_CTL_1: + case WCD938X_MICB4_TEST_CTL_2: + case WCD938X_MICB4_TEST_CTL_3: + case WCD938X_TX_COM_ADC_VCM: + case WCD938X_TX_COM_BIAS_ATEST: + case WCD938X_TX_COM_SPARE1: + case WCD938X_TX_COM_SPARE2: + case WCD938X_TX_COM_TXFE_DIV_CTL: + case WCD938X_TX_COM_TXFE_DIV_START: + case WCD938X_TX_COM_SPARE3: + case WCD938X_TX_COM_SPARE4: + case WCD938X_TX_1_2_TEST_EN: + case WCD938X_TX_1_2_ADC_IB: + case WCD938X_TX_1_2_ATEST_REFCTL: + case WCD938X_TX_1_2_TEST_CTL: + case WCD938X_TX_1_2_TEST_BLK_EN1: + case WCD938X_TX_1_2_TXFE1_CLKDIV: + case WCD938X_TX_3_4_TEST_EN: + case WCD938X_TX_3_4_ADC_IB: + case WCD938X_TX_3_4_ATEST_REFCTL: + case WCD938X_TX_3_4_TEST_CTL: + case WCD938X_TX_3_4_TEST_BLK_EN3: + case WCD938X_TX_3_4_TXFE3_CLKDIV: + case WCD938X_TX_3_4_TEST_BLK_EN2: + case WCD938X_TX_3_4_TXFE2_CLKDIV: + case WCD938X_TX_3_4_SPARE1: + case WCD938X_TX_3_4_TEST_BLK_EN4: + case WCD938X_TX_3_4_TXFE4_CLKDIV: + case WCD938X_TX_3_4_SPARE2: + case WCD938X_CLASSH_MODE_1: + case WCD938X_CLASSH_MODE_2: + case WCD938X_CLASSH_MODE_3: + case WCD938X_CLASSH_CTRL_VCL_1: + case WCD938X_CLASSH_CTRL_VCL_2: + case WCD938X_CLASSH_CTRL_CCL_1: + case WCD938X_CLASSH_CTRL_CCL_2: + case WCD938X_CLASSH_CTRL_CCL_3: + case WCD938X_CLASSH_CTRL_CCL_4: + case WCD938X_CLASSH_CTRL_CCL_5: + case WCD938X_CLASSH_BUCK_TMUX_A_D: + case WCD938X_CLASSH_BUCK_SW_DRV_CNTL: + case WCD938X_CLASSH_SPARE: + case WCD938X_FLYBACK_EN: + case WCD938X_FLYBACK_VNEG_CTRL_1: + case WCD938X_FLYBACK_VNEG_CTRL_2: + case WCD938X_FLYBACK_VNEG_CTRL_3: + case WCD938X_FLYBACK_VNEG_CTRL_4: + case WCD938X_FLYBACK_VNEG_CTRL_5: + case WCD938X_FLYBACK_VNEG_CTRL_6: + case WCD938X_FLYBACK_VNEG_CTRL_7: + case WCD938X_FLYBACK_VNEG_CTRL_8: + case WCD938X_FLYBACK_VNEG_CTRL_9: + case WCD938X_FLYBACK_VNEGDAC_CTRL_1: + case WCD938X_FLYBACK_VNEGDAC_CTRL_2: + case WCD938X_FLYBACK_VNEGDAC_CTRL_3: + case WCD938X_FLYBACK_CTRL_1: + case WCD938X_FLYBACK_TEST_CTL: + case WCD938X_RX_AUX_SW_CTL: + case WCD938X_RX_PA_AUX_IN_CONN: + case WCD938X_RX_TIMER_DIV: + case WCD938X_RX_OCP_CTL: + case WCD938X_RX_OCP_COUNT: + case WCD938X_RX_BIAS_EAR_DAC: + case WCD938X_RX_BIAS_EAR_AMP: + case WCD938X_RX_BIAS_HPH_LDO: + case WCD938X_RX_BIAS_HPH_PA: + case WCD938X_RX_BIAS_HPH_RDACBUFF_CNP2: + case WCD938X_RX_BIAS_HPH_RDAC_LDO: + case WCD938X_RX_BIAS_HPH_CNP1: + case WCD938X_RX_BIAS_HPH_LOWPOWER: + case WCD938X_RX_BIAS_AUX_DAC: + case WCD938X_RX_BIAS_AUX_AMP: + case WCD938X_RX_BIAS_VNEGDAC_BLEEDER: + case WCD938X_RX_BIAS_MISC: + case WCD938X_RX_BIAS_BUCK_RST: + case WCD938X_RX_BIAS_BUCK_VREF_ERRAMP: + case WCD938X_RX_BIAS_FLYB_ERRAMP: + case WCD938X_RX_BIAS_FLYB_BUFF: + case WCD938X_RX_BIAS_FLYB_MID_RST: + case WCD938X_HPH_CNP_EN: + case WCD938X_HPH_CNP_WG_CTL: + case WCD938X_HPH_CNP_WG_TIME: + case WCD938X_HPH_OCP_CTL: + case WCD938X_HPH_AUTO_CHOP: + case WCD938X_HPH_CHOP_CTL: + case WCD938X_HPH_PA_CTL1: + case WCD938X_HPH_PA_CTL2: + case WCD938X_HPH_L_EN: + case WCD938X_HPH_L_TEST: + case WCD938X_HPH_L_ATEST: + case WCD938X_HPH_R_EN: + case WCD938X_HPH_R_TEST: + case WCD938X_HPH_R_ATEST: + case WCD938X_HPH_RDAC_CLK_CTL1: + case WCD938X_HPH_RDAC_CLK_CTL2: + case WCD938X_HPH_RDAC_LDO_CTL: + case WCD938X_HPH_RDAC_CHOP_CLK_LP_CTL: + case WCD938X_HPH_REFBUFF_UHQA_CTL: + case WCD938X_HPH_REFBUFF_LP_CTL: + case WCD938X_HPH_L_DAC_CTL: + case WCD938X_HPH_R_DAC_CTL: + case WCD938X_HPH_SURGE_HPHLR_SURGE_COMP_SEL: + case WCD938X_HPH_SURGE_HPHLR_SURGE_EN: + case WCD938X_HPH_SURGE_HPHLR_SURGE_MISC1: + case WCD938X_EAR_EAR_EN_REG: + case WCD938X_EAR_EAR_PA_CON: + case WCD938X_EAR_EAR_SP_CON: + case WCD938X_EAR_EAR_DAC_CON: + case WCD938X_EAR_EAR_CNP_FSM_CON: + case WCD938X_EAR_TEST_CTL: + case WCD938X_ANA_NEW_PAGE_REGISTER: + case WCD938X_HPH_NEW_ANA_HPH2: + case WCD938X_HPH_NEW_ANA_HPH3: + case WCD938X_SLEEP_CTL: + case WCD938X_SLEEP_WATCHDOG_CTL: + case WCD938X_MBHC_NEW_ELECT_REM_CLAMP_CTL: + case WCD938X_MBHC_NEW_CTL_1: + case WCD938X_MBHC_NEW_CTL_2: + case WCD938X_MBHC_NEW_PLUG_DETECT_CTL: + case WCD938X_MBHC_NEW_ZDET_ANA_CTL: + case WCD938X_MBHC_NEW_ZDET_RAMP_CTL: + case WCD938X_TX_NEW_AMIC_MUX_CFG: + case WCD938X_AUX_AUXPA: + case WCD938X_LDORXTX_MODE: + case WCD938X_LDORXTX_CONFIG: + case WCD938X_DIE_CRACK_DIE_CRK_DET_EN: + case WCD938X_HPH_NEW_INT_RDAC_GAIN_CTL: + case WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L: + case WCD938X_HPH_NEW_INT_RDAC_VREF_CTL: + case WCD938X_HPH_NEW_INT_RDAC_OVERRIDE_CTL: + case WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R: + case WCD938X_HPH_NEW_INT_PA_MISC1: + case WCD938X_HPH_NEW_INT_PA_MISC2: + case WCD938X_HPH_NEW_INT_PA_RDAC_MISC: + case WCD938X_HPH_NEW_INT_HPH_TIMER1: + case WCD938X_HPH_NEW_INT_HPH_TIMER2: + case WCD938X_HPH_NEW_INT_HPH_TIMER3: + case WCD938X_HPH_NEW_INT_HPH_TIMER4: + case WCD938X_HPH_NEW_INT_PA_RDAC_MISC2: + case WCD938X_HPH_NEW_INT_PA_RDAC_MISC3: + case WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L_NEW: + case WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R_NEW: + case WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI: + case WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_ULP: + case WCD938X_RX_NEW_INT_HPH_RDAC_LDO_LP: + case WCD938X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL: + case WCD938X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL: + case WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT: + case WCD938X_MBHC_NEW_INT_SPARE_2: + case WCD938X_EAR_INT_NEW_EAR_CHOPPER_CON: + case WCD938X_EAR_INT_NEW_CNP_VCM_CON1: + case WCD938X_EAR_INT_NEW_CNP_VCM_CON2: + case WCD938X_EAR_INT_NEW_EAR_DYNAMIC_BIAS: + case WCD938X_AUX_INT_EN_REG: + case WCD938X_AUX_INT_PA_CTRL: + case WCD938X_AUX_INT_SP_CTRL: + case WCD938X_AUX_INT_DAC_CTRL: + case WCD938X_AUX_INT_CLK_CTRL: + case WCD938X_AUX_INT_TEST_CTRL: + case WCD938X_AUX_INT_MISC: + case WCD938X_LDORXTX_INT_BIAS: + case WCD938X_LDORXTX_INT_STB_LOADS_DTEST: + case WCD938X_LDORXTX_INT_TEST0: + case WCD938X_LDORXTX_INT_STARTUP_TIMER: + case WCD938X_LDORXTX_INT_TEST1: + case WCD938X_SLEEP_INT_WATCHDOG_CTL_1: + case WCD938X_SLEEP_INT_WATCHDOG_CTL_2: + case WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT1: + case WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT2: + case WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L2: + case WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L1: + case WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L0: + case WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP1P2M: + case WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP0P6M: + case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L2L1: + case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L0: + case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_ULP: + case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L2L1: + case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L0: + case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_ULP: + case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_L2L1L0: + case WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_ULP: + case WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L2L1: + case WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L0ULP: + case WCD938X_TX_COM_NEW_INT_TXADC_INT_L2: + case WCD938X_TX_COM_NEW_INT_TXADC_INT_L1: + case WCD938X_TX_COM_NEW_INT_TXADC_INT_L0: + case WCD938X_TX_COM_NEW_INT_TXADC_INT_ULP: + case WCD938X_DIGITAL_PAGE_REGISTER: + case WCD938X_DIGITAL_SWR_TX_CLK_RATE: + case WCD938X_DIGITAL_CDC_RST_CTL: + case WCD938X_DIGITAL_TOP_CLK_CFG: + case WCD938X_DIGITAL_CDC_ANA_CLK_CTL: + case WCD938X_DIGITAL_CDC_DIG_CLK_CTL: + case WCD938X_DIGITAL_SWR_RST_EN: + case WCD938X_DIGITAL_CDC_PATH_MODE: + case WCD938X_DIGITAL_CDC_RX_RST: + case WCD938X_DIGITAL_CDC_RX0_CTL: + case WCD938X_DIGITAL_CDC_RX1_CTL: + case WCD938X_DIGITAL_CDC_RX2_CTL: + case WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1: + case WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3: + case WCD938X_DIGITAL_CDC_COMP_CTL_0: + case WCD938X_DIGITAL_CDC_ANA_TX_CLK_CTL: + case WCD938X_DIGITAL_CDC_HPH_DSM_A1_0: + case WCD938X_DIGITAL_CDC_HPH_DSM_A1_1: + case WCD938X_DIGITAL_CDC_HPH_DSM_A2_0: + case WCD938X_DIGITAL_CDC_HPH_DSM_A2_1: + case WCD938X_DIGITAL_CDC_HPH_DSM_A3_0: + case WCD938X_DIGITAL_CDC_HPH_DSM_A3_1: + case WCD938X_DIGITAL_CDC_HPH_DSM_A4_0: + case WCD938X_DIGITAL_CDC_HPH_DSM_A4_1: + case WCD938X_DIGITAL_CDC_HPH_DSM_A5_0: + case WCD938X_DIGITAL_CDC_HPH_DSM_A5_1: + case WCD938X_DIGITAL_CDC_HPH_DSM_A6_0: + case WCD938X_DIGITAL_CDC_HPH_DSM_A7_0: + case WCD938X_DIGITAL_CDC_HPH_DSM_C_0: + case WCD938X_DIGITAL_CDC_HPH_DSM_C_1: + case WCD938X_DIGITAL_CDC_HPH_DSM_C_2: + case WCD938X_DIGITAL_CDC_HPH_DSM_C_3: + case WCD938X_DIGITAL_CDC_HPH_DSM_R1: + case WCD938X_DIGITAL_CDC_HPH_DSM_R2: + case WCD938X_DIGITAL_CDC_HPH_DSM_R3: + case WCD938X_DIGITAL_CDC_HPH_DSM_R4: + case WCD938X_DIGITAL_CDC_HPH_DSM_R5: + case WCD938X_DIGITAL_CDC_HPH_DSM_R6: + case WCD938X_DIGITAL_CDC_HPH_DSM_R7: + case WCD938X_DIGITAL_CDC_AUX_DSM_A1_0: + case WCD938X_DIGITAL_CDC_AUX_DSM_A1_1: + case WCD938X_DIGITAL_CDC_AUX_DSM_A2_0: + case WCD938X_DIGITAL_CDC_AUX_DSM_A2_1: + case WCD938X_DIGITAL_CDC_AUX_DSM_A3_0: + case WCD938X_DIGITAL_CDC_AUX_DSM_A3_1: + case WCD938X_DIGITAL_CDC_AUX_DSM_A4_0: + case WCD938X_DIGITAL_CDC_AUX_DSM_A4_1: + case WCD938X_DIGITAL_CDC_AUX_DSM_A5_0: + case WCD938X_DIGITAL_CDC_AUX_DSM_A5_1: + case WCD938X_DIGITAL_CDC_AUX_DSM_A6_0: + case WCD938X_DIGITAL_CDC_AUX_DSM_A7_0: + case WCD938X_DIGITAL_CDC_AUX_DSM_C_0: + case WCD938X_DIGITAL_CDC_AUX_DSM_C_1: + case WCD938X_DIGITAL_CDC_AUX_DSM_C_2: + case WCD938X_DIGITAL_CDC_AUX_DSM_C_3: + case WCD938X_DIGITAL_CDC_AUX_DSM_R1: + case WCD938X_DIGITAL_CDC_AUX_DSM_R2: + case WCD938X_DIGITAL_CDC_AUX_DSM_R3: + case WCD938X_DIGITAL_CDC_AUX_DSM_R4: + case WCD938X_DIGITAL_CDC_AUX_DSM_R5: + case WCD938X_DIGITAL_CDC_AUX_DSM_R6: + case WCD938X_DIGITAL_CDC_AUX_DSM_R7: + case WCD938X_DIGITAL_CDC_HPH_GAIN_RX_0: + case WCD938X_DIGITAL_CDC_HPH_GAIN_RX_1: + case WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_0: + case WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_1: + case WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_2: + case WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_0: + case WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_1: + case WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_2: + case WCD938X_DIGITAL_CDC_HPH_GAIN_CTL: + case WCD938X_DIGITAL_CDC_AUX_GAIN_CTL: + case WCD938X_DIGITAL_CDC_EAR_PATH_CTL: + case WCD938X_DIGITAL_CDC_SWR_CLH: + case WCD938X_DIGITAL_SWR_CLH_BYP: + case WCD938X_DIGITAL_CDC_TX0_CTL: + case WCD938X_DIGITAL_CDC_TX1_CTL: + case WCD938X_DIGITAL_CDC_TX2_CTL: + case WCD938X_DIGITAL_CDC_TX_RST: + case WCD938X_DIGITAL_CDC_REQ_CTL: + case WCD938X_DIGITAL_CDC_RST: + case WCD938X_DIGITAL_CDC_AMIC_CTL: + case WCD938X_DIGITAL_CDC_DMIC_CTL: + case WCD938X_DIGITAL_CDC_DMIC1_CTL: + case WCD938X_DIGITAL_CDC_DMIC2_CTL: + case WCD938X_DIGITAL_CDC_DMIC3_CTL: + case WCD938X_DIGITAL_CDC_DMIC4_CTL: + case WCD938X_DIGITAL_EFUSE_PRG_CTL: + case WCD938X_DIGITAL_EFUSE_CTL: + case WCD938X_DIGITAL_CDC_DMIC_RATE_1_2: + case WCD938X_DIGITAL_CDC_DMIC_RATE_3_4: + case WCD938X_DIGITAL_PDM_WD_CTL0: + case WCD938X_DIGITAL_PDM_WD_CTL1: + case WCD938X_DIGITAL_PDM_WD_CTL2: + case WCD938X_DIGITAL_INTR_MODE: + case WCD938X_DIGITAL_INTR_MASK_0: + case WCD938X_DIGITAL_INTR_MASK_1: + case WCD938X_DIGITAL_INTR_MASK_2: + case WCD938X_DIGITAL_INTR_CLEAR_0: + case WCD938X_DIGITAL_INTR_CLEAR_1: + case WCD938X_DIGITAL_INTR_CLEAR_2: + case WCD938X_DIGITAL_INTR_LEVEL_0: + case WCD938X_DIGITAL_INTR_LEVEL_1: + case WCD938X_DIGITAL_INTR_LEVEL_2: + case WCD938X_DIGITAL_INTR_SET_0: + case WCD938X_DIGITAL_INTR_SET_1: + case WCD938X_DIGITAL_INTR_SET_2: + case WCD938X_DIGITAL_INTR_TEST_0: + case WCD938X_DIGITAL_INTR_TEST_1: + case WCD938X_DIGITAL_INTR_TEST_2: + case WCD938X_DIGITAL_TX_MODE_DBG_EN: + case WCD938X_DIGITAL_TX_MODE_DBG_0_1: + case WCD938X_DIGITAL_TX_MODE_DBG_2_3: + case WCD938X_DIGITAL_LB_IN_SEL_CTL: + case WCD938X_DIGITAL_LOOP_BACK_MODE: + case WCD938X_DIGITAL_SWR_DAC_TEST: + case WCD938X_DIGITAL_SWR_HM_TEST_RX_0: + case WCD938X_DIGITAL_SWR_HM_TEST_TX_0: + case WCD938X_DIGITAL_SWR_HM_TEST_RX_1: + case WCD938X_DIGITAL_SWR_HM_TEST_TX_1: + case WCD938X_DIGITAL_SWR_HM_TEST_TX_2: + case WCD938X_DIGITAL_PAD_CTL_SWR_0: + case WCD938X_DIGITAL_PAD_CTL_SWR_1: + case WCD938X_DIGITAL_I2C_CTL: + case WCD938X_DIGITAL_CDC_TX_TANGGU_SW_MODE: + case WCD938X_DIGITAL_EFUSE_TEST_CTL_0: + case WCD938X_DIGITAL_EFUSE_TEST_CTL_1: + case WCD938X_DIGITAL_PAD_CTL_PDM_RX0: + case WCD938X_DIGITAL_PAD_CTL_PDM_RX1: + case WCD938X_DIGITAL_PAD_CTL_PDM_TX0: + case WCD938X_DIGITAL_PAD_CTL_PDM_TX1: + case WCD938X_DIGITAL_PAD_CTL_PDM_TX2: + case WCD938X_DIGITAL_PAD_INP_DIS_0: + case WCD938X_DIGITAL_PAD_INP_DIS_1: + case WCD938X_DIGITAL_DRIVE_STRENGTH_0: + case WCD938X_DIGITAL_DRIVE_STRENGTH_1: + case WCD938X_DIGITAL_DRIVE_STRENGTH_2: + case WCD938X_DIGITAL_RX_DATA_EDGE_CTL: + case WCD938X_DIGITAL_TX_DATA_EDGE_CTL: + case WCD938X_DIGITAL_GPIO_MODE: + case WCD938X_DIGITAL_PIN_CTL_OE: + case WCD938X_DIGITAL_PIN_CTL_DATA_0: + case WCD938X_DIGITAL_PIN_CTL_DATA_1: + case WCD938X_DIGITAL_DIG_DEBUG_CTL: + case WCD938X_DIGITAL_DIG_DEBUG_EN: + case WCD938X_DIGITAL_ANA_CSR_DBG_ADD: + case WCD938X_DIGITAL_ANA_CSR_DBG_CTL: + case WCD938X_DIGITAL_SSP_DBG: + case WCD938X_DIGITAL_SPARE_0: + case WCD938X_DIGITAL_SPARE_1: + case WCD938X_DIGITAL_SPARE_2: + case WCD938X_DIGITAL_TX_REQ_FB_CTL_0: + case WCD938X_DIGITAL_TX_REQ_FB_CTL_1: + case WCD938X_DIGITAL_TX_REQ_FB_CTL_2: + case WCD938X_DIGITAL_TX_REQ_FB_CTL_3: + case WCD938X_DIGITAL_TX_REQ_FB_CTL_4: + case WCD938X_DIGITAL_DEM_BYPASS_DATA0: + case WCD938X_DIGITAL_DEM_BYPASS_DATA1: + case WCD938X_DIGITAL_DEM_BYPASS_DATA2: + case WCD938X_DIGITAL_DEM_BYPASS_DATA3: + return true; + } + + return false; +} + +static bool wcd938x_readonly_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WCD938X_ANA_MBHC_RESULT_1: + case WCD938X_ANA_MBHC_RESULT_2: + case WCD938X_ANA_MBHC_RESULT_3: + case WCD938X_MBHC_MOISTURE_DET_FSM_STATUS: + case WCD938X_TX_1_2_SAR2_ERR: + case WCD938X_TX_1_2_SAR1_ERR: + case WCD938X_TX_3_4_SAR4_ERR: + case WCD938X_TX_3_4_SAR3_ERR: + case WCD938X_HPH_L_STATUS: + case WCD938X_HPH_R_STATUS: + case WCD938X_HPH_SURGE_HPHLR_SURGE_STATUS: + case WCD938X_EAR_STATUS_REG_1: + case WCD938X_EAR_STATUS_REG_2: + case WCD938X_MBHC_NEW_FSM_STATUS: + case WCD938X_MBHC_NEW_ADC_RESULT: + case WCD938X_DIE_CRACK_DIE_CRK_DET_OUT: + case WCD938X_AUX_INT_STATUS_REG: + case WCD938X_LDORXTX_INT_STATUS: + case WCD938X_DIGITAL_CHIP_ID0: + case WCD938X_DIGITAL_CHIP_ID1: + case WCD938X_DIGITAL_CHIP_ID2: + case WCD938X_DIGITAL_CHIP_ID3: + case WCD938X_DIGITAL_INTR_STATUS_0: + case WCD938X_DIGITAL_INTR_STATUS_1: + case WCD938X_DIGITAL_INTR_STATUS_2: + case WCD938X_DIGITAL_INTR_CLEAR_0: + case WCD938X_DIGITAL_INTR_CLEAR_1: + case WCD938X_DIGITAL_INTR_CLEAR_2: + case WCD938X_DIGITAL_SWR_HM_TEST_0: + case WCD938X_DIGITAL_SWR_HM_TEST_1: + case WCD938X_DIGITAL_EFUSE_T_DATA_0: + case WCD938X_DIGITAL_EFUSE_T_DATA_1: + case WCD938X_DIGITAL_PIN_STATUS_0: + case WCD938X_DIGITAL_PIN_STATUS_1: + case WCD938X_DIGITAL_MODE_STATUS_0: + case WCD938X_DIGITAL_MODE_STATUS_1: + case WCD938X_DIGITAL_EFUSE_REG_0: + case WCD938X_DIGITAL_EFUSE_REG_1: + case WCD938X_DIGITAL_EFUSE_REG_2: + case WCD938X_DIGITAL_EFUSE_REG_3: + case WCD938X_DIGITAL_EFUSE_REG_4: + case WCD938X_DIGITAL_EFUSE_REG_5: + case WCD938X_DIGITAL_EFUSE_REG_6: + case WCD938X_DIGITAL_EFUSE_REG_7: + case WCD938X_DIGITAL_EFUSE_REG_8: + case WCD938X_DIGITAL_EFUSE_REG_9: + case WCD938X_DIGITAL_EFUSE_REG_10: + case WCD938X_DIGITAL_EFUSE_REG_11: + case WCD938X_DIGITAL_EFUSE_REG_12: + case WCD938X_DIGITAL_EFUSE_REG_13: + case WCD938X_DIGITAL_EFUSE_REG_14: + case WCD938X_DIGITAL_EFUSE_REG_15: + case WCD938X_DIGITAL_EFUSE_REG_16: + case WCD938X_DIGITAL_EFUSE_REG_17: + case WCD938X_DIGITAL_EFUSE_REG_18: + case WCD938X_DIGITAL_EFUSE_REG_19: + case WCD938X_DIGITAL_EFUSE_REG_20: + case WCD938X_DIGITAL_EFUSE_REG_21: + case WCD938X_DIGITAL_EFUSE_REG_22: + case WCD938X_DIGITAL_EFUSE_REG_23: + case WCD938X_DIGITAL_EFUSE_REG_24: + case WCD938X_DIGITAL_EFUSE_REG_25: + case WCD938X_DIGITAL_EFUSE_REG_26: + case WCD938X_DIGITAL_EFUSE_REG_27: + case WCD938X_DIGITAL_EFUSE_REG_28: + case WCD938X_DIGITAL_EFUSE_REG_29: + case WCD938X_DIGITAL_EFUSE_REG_30: + case WCD938X_DIGITAL_EFUSE_REG_31: + return true; + } + return false; +} + +static bool wcd938x_readable_register(struct device *dev, unsigned int reg) +{ + bool ret; + + ret = wcd938x_readonly_register(dev, reg); + if (!ret) + return wcd938x_rdwr_register(dev, reg); + + return ret; +} + +static bool wcd938x_writeable_register(struct device *dev, unsigned int reg) +{ + return wcd938x_rdwr_register(dev, reg); +} + +static bool wcd938x_volatile_register(struct device *dev, unsigned int reg) +{ + if (reg <= WCD938X_BASE_ADDRESS) + return false; + + if (reg == WCD938X_DIGITAL_SWR_TX_CLK_RATE) + return true; + + if (wcd938x_readonly_register(dev, reg)) + return true; + + return false; +} + +static const struct regmap_config wcd938x_regmap_config = { + .name = "wcd938x_csr", + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wcd938x_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd938x_defaults), + .max_register = WCD938X_MAX_REGISTER, + .readable_reg = wcd938x_readable_register, + .writeable_reg = wcd938x_writeable_register, + .volatile_reg = wcd938x_volatile_register, +}; + +static const struct sdw_slave_ops wcd9380_slave_ops = { + .update_status = wcd9380_update_status, + .interrupt_callback = wcd9380_interrupt_callback, + .bus_config = wcd9380_bus_config, +}; + +static int wcd938x_sdw_component_bind(struct device *dev, + struct device *master, void *data) +{ + return 0; +} + +static void wcd938x_sdw_component_unbind(struct device *dev, + struct device *master, void *data) +{ +} + +static const struct component_ops wcd938x_sdw_component_ops = { + .bind = wcd938x_sdw_component_bind, + .unbind = wcd938x_sdw_component_unbind, +}; + +static int wcd9380_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct device *dev = &pdev->dev; + struct wcd938x_sdw_priv *wcd; + int ret; + + wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL); + if (!wcd) + return -ENOMEM; + + /** + * Port map index starts with 0, however the data port for this codec + * are from index 1 + */ + if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) { + wcd->is_tx = true; + ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping", + &pdev->m_port_map[1], + WCD938X_MAX_TX_SWR_PORTS); + } else { + ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping", + &pdev->m_port_map[1], + WCD938X_MAX_SWR_PORTS); + } + + if (ret < 0) + dev_info(dev, "Static Port mapping not specified\n"); + + wcd->sdev = pdev; + dev_set_drvdata(dev, wcd); + + pdev->prop.scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | + SDW_SCP_INT1_BUS_CLASH | + SDW_SCP_INT1_PARITY; + pdev->prop.lane_control_support = true; + pdev->prop.simple_clk_stop_capable = true; + if (wcd->is_tx) { + pdev->prop.source_ports = GENMASK(WCD938X_MAX_SWR_PORTS, 0); + pdev->prop.src_dpn_prop = wcd938x_dpn_prop; + wcd->ch_info = &wcd938x_sdw_tx_ch_info[0]; + pdev->prop.wake_capable = true; + } else { + pdev->prop.sink_ports = GENMASK(WCD938X_MAX_SWR_PORTS, 0); + pdev->prop.sink_dpn_prop = wcd938x_dpn_prop; + wcd->ch_info = &wcd938x_sdw_rx_ch_info[0]; + } + + if (wcd->is_tx) { + wcd->regmap = devm_regmap_init_sdw(pdev, &wcd938x_regmap_config); + if (IS_ERR(wcd->regmap)) + return dev_err_probe(dev, PTR_ERR(wcd->regmap), + "Regmap init failed\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(wcd->regmap, true); + } + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = component_add(dev, &wcd938x_sdw_component_ops); + if (ret) + goto err_disable_rpm; + + return 0; + +err_disable_rpm: + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); + + return ret; +} + +static int wcd9380_remove(struct sdw_slave *pdev) +{ + struct device *dev = &pdev->dev; + + component_del(dev, &wcd938x_sdw_component_ops); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); + + return 0; +} + +static const struct sdw_device_id wcd9380_slave_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x10d, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, wcd9380_slave_id); + +static int __maybe_unused wcd938x_sdw_runtime_suspend(struct device *dev) +{ + struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev); + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, true); + regcache_mark_dirty(wcd->regmap); + } + + return 0; +} + +static int __maybe_unused wcd938x_sdw_runtime_resume(struct device *dev) +{ + struct wcd938x_sdw_priv *wcd = dev_get_drvdata(dev); + + if (wcd->regmap) { + regcache_cache_only(wcd->regmap, false); + regcache_sync(wcd->regmap); + } + + pm_runtime_mark_last_busy(dev); + + return 0; +} + +static const struct dev_pm_ops wcd938x_sdw_pm_ops = { + SET_RUNTIME_PM_OPS(wcd938x_sdw_runtime_suspend, wcd938x_sdw_runtime_resume, NULL) +}; + + +static struct sdw_driver wcd9380_codec_driver = { + .probe = wcd9380_probe, + .remove = wcd9380_remove, + .ops = &wcd9380_slave_ops, + .id_table = wcd9380_slave_id, + .driver = { + .name = "wcd9380-codec", + .pm = &wcd938x_sdw_pm_ops, + } +}; +module_sdw_driver(wcd9380_codec_driver); + +MODULE_DESCRIPTION("WCD938X SDW codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c new file mode 100644 index 0000000000..d27b919c63 --- /dev/null +++ b/sound/soc/codecs/wcd938x.c @@ -0,0 +1,3657 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wcd-clsh-v2.h" +#include "wcd-mbhc-v2.h" +#include "wcd938x.h" + +#define WCD938X_MAX_MICBIAS (4) +#define WCD938X_MAX_SUPPLY (4) +#define WCD938X_MBHC_MAX_BUTTONS (8) +#define TX_ADC_MAX (4) +#define WCD938X_TX_MAX_SWR_PORTS (5) + +#define WCD938X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +/* Fractional Rates */ +#define WCD938X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400) +#define WCD938X_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) +#define SWR_CLK_RATE_0P6MHZ (600000) +#define SWR_CLK_RATE_1P2MHZ (1200000) +#define SWR_CLK_RATE_2P4MHZ (2400000) +#define SWR_CLK_RATE_4P8MHZ (4800000) +#define SWR_CLK_RATE_9P6MHZ (9600000) +#define SWR_CLK_RATE_11P2896MHZ (1128960) + +#define WCD938X_DRV_NAME "wcd938x_codec" +#define WCD938X_VERSION_1_0 (1) +#define EAR_RX_PATH_AUX (1) + +#define ADC_MODE_VAL_HIFI 0x01 +#define ADC_MODE_VAL_LO_HIF 0x02 +#define ADC_MODE_VAL_NORMAL 0x03 +#define ADC_MODE_VAL_LP 0x05 +#define ADC_MODE_VAL_ULP1 0x09 +#define ADC_MODE_VAL_ULP2 0x0B + +/* Z value defined in milliohm */ +#define WCD938X_ZDET_VAL_32 (32000) +#define WCD938X_ZDET_VAL_400 (400000) +#define WCD938X_ZDET_VAL_1200 (1200000) +#define WCD938X_ZDET_VAL_100K (100000000) +/* Z floating defined in ohms */ +#define WCD938X_ZDET_FLOATING_IMPEDANCE (0x0FFFFFFE) +#define WCD938X_ZDET_NUM_MEASUREMENTS (900) +#define WCD938X_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define WCD938X_MBHC_GET_X1(x) (x & 0x3FFF) +/* Z value compared in milliOhm */ +#define WCD938X_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define WCD938X_MBHC_ZDET_CONST (86 * 16384) +#define WCD938X_MBHC_MOISTURE_RREF R_24_KOHM +#define WCD_MBHC_HS_V_MAX 1600 + +#define WCD938X_EAR_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = wcd938x_ear_pa_put_gain, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } + +enum { + WCD9380 = 0, + WCD9385 = 5, +}; + +enum { + TX_HDR12 = 0, + TX_HDR34, + TX_HDR_MAX, +}; + +enum { + WCD_RX1, + WCD_RX2, + WCD_RX3 +}; + +enum { + /* INTR_CTRL_INT_MASK_0 */ + WCD938X_IRQ_MBHC_BUTTON_PRESS_DET = 0, + WCD938X_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD938X_IRQ_MBHC_ELECT_INS_REM_DET, + WCD938X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD938X_IRQ_MBHC_SW_DET, + WCD938X_IRQ_HPHR_OCP_INT, + WCD938X_IRQ_HPHR_CNP_INT, + WCD938X_IRQ_HPHL_OCP_INT, + + /* INTR_CTRL_INT_MASK_1 */ + WCD938X_IRQ_HPHL_CNP_INT, + WCD938X_IRQ_EAR_CNP_INT, + WCD938X_IRQ_EAR_SCD_INT, + WCD938X_IRQ_AUX_CNP_INT, + WCD938X_IRQ_AUX_SCD_INT, + WCD938X_IRQ_HPHL_PDM_WD_INT, + WCD938X_IRQ_HPHR_PDM_WD_INT, + WCD938X_IRQ_AUX_PDM_WD_INT, + + /* INTR_CTRL_INT_MASK_2 */ + WCD938X_IRQ_LDORT_SCD_INT, + WCD938X_IRQ_MBHC_MOISTURE_INT, + WCD938X_IRQ_HPHL_SURGE_DET_INT, + WCD938X_IRQ_HPHR_SURGE_DET_INT, + WCD938X_NUM_IRQS, +}; + +enum { + WCD_ADC1 = 0, + WCD_ADC2, + WCD_ADC3, + WCD_ADC4, + ALLOW_BUCK_DISABLE, + HPH_COMP_DELAY, + HPH_PA_DELAY, + AMIC2_BCS_ENABLE, + WCD_SUPPLIES_LPM_MODE, +}; + +enum { + ADC_MODE_INVALID = 0, + ADC_MODE_HIFI, + ADC_MODE_LO_HIF, + ADC_MODE_NORMAL, + ADC_MODE_LP, + ADC_MODE_ULP1, + ADC_MODE_ULP2, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + NUM_CODEC_DAIS, +}; + +static u8 tx_mode_bit[] = { + [ADC_MODE_INVALID] = 0x00, + [ADC_MODE_HIFI] = 0x01, + [ADC_MODE_LO_HIF] = 0x02, + [ADC_MODE_NORMAL] = 0x04, + [ADC_MODE_LP] = 0x08, + [ADC_MODE_ULP1] = 0x10, + [ADC_MODE_ULP2] = 0x20, +}; + +struct wcd938x_priv { + struct sdw_slave *tx_sdw_dev; + struct wcd938x_sdw_priv *sdw_priv[NUM_CODEC_DAIS]; + struct device *txdev; + struct device *rxdev; + struct device_node *rxnode, *txnode; + struct regmap *regmap; + struct mutex micb_lock; + /* mbhc module */ + struct wcd_mbhc *wcd_mbhc; + struct wcd_mbhc_config mbhc_cfg; + struct wcd_mbhc_intr intr_ids; + struct wcd_clsh_ctrl *clsh_info; + struct irq_domain *virq; + struct regmap_irq_chip *wcd_regmap_irq_chip; + struct regmap_irq_chip_data *irq_chip; + struct regulator_bulk_data supplies[WCD938X_MAX_SUPPLY]; + struct snd_soc_jack *jack; + unsigned long status_mask; + s32 micb_ref[WCD938X_MAX_MICBIAS]; + s32 pullup_ref[WCD938X_MAX_MICBIAS]; + u32 hph_mode; + u32 tx_mode[TX_ADC_MAX]; + int flyback_cur_det_disable; + int ear_rx_path; + int variant; + int reset_gpio; + struct gpio_desc *us_euro_gpio; + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + u32 micb4_mv; + int hphr_pdm_wd_int; + int hphl_pdm_wd_int; + int aux_pdm_wd_int; + bool comp1_enable; + bool comp2_enable; + bool ldoh; + bool bcs_dis; +}; + +static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800); +static const DECLARE_TLV_DB_SCALE(line_gain, -3000, 150, -3000); +static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(analog_gain, 0, 3000); + +struct wcd938x_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static struct wcd_mbhc_field wcd_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, WCD938X_ANA_MBHC_MECH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, WCD938X_ANA_MBHC_MECH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, WCD938X_ANA_MBHC_MECH, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, WCD938X_MBHC_NEW_PLUG_DETECT_CTL, 0x30), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, WCD938X_ANA_MBHC_ELECT, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT, 0x1F), + WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, WCD938X_ANA_MBHC_MECH, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, WCD938X_ANA_MBHC_MECH, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, WCD938X_ANA_MBHC_MECH, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, WCD938X_ANA_MBHC_MECH, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, WCD938X_ANA_MBHC_ELECT, 0x06), + WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, WCD938X_ANA_MBHC_ELECT, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, WCD938X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F), + WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, WCD938X_MBHC_NEW_CTL_1, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, WCD938X_MBHC_NEW_CTL_2, 0x03), + WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, WCD938X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_OCP_FSM_EN, WCD938X_HPH_OCP_CTL, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0x07), + WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, WCD938X_ANA_MBHC_ELECT, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, WCD938X_ANA_MBHC_RESULT_3, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, WCD938X_ANA_MICB2, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_HPH_CNP_WG_TIME, WCD938X_HPH_CNP_WG_TIME, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, WCD938X_ANA_HPH, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, WCD938X_ANA_HPH, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, WCD938X_ANA_HPH, 0xC0), + WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, WCD938X_ANA_MBHC_RESULT_3, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_ANC_DET_EN, WCD938X_MBHC_CTL_BCS, 0x02), + WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, WCD938X_MBHC_NEW_FSM_STATUS, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, WCD938X_MBHC_NEW_CTL_2, 0x70), + WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, WCD938X_MBHC_NEW_FSM_STATUS, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_GND, WCD938X_HPH_PA_CTL2, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_GND, WCD938X_HPH_PA_CTL2, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, WCD938X_HPH_L_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, WCD938X_HPH_R_TEST, 0x01), + WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, WCD938X_DIGITAL_INTR_STATUS_0, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, WCD938X_DIGITAL_INTR_STATUS_0, 0x20), + WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, WCD938X_MBHC_NEW_CTL_1, 0x08), + WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, WCD938X_MBHC_NEW_FSM_STATUS, 0x40), + WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, WCD938X_MBHC_NEW_FSM_STATUS, 0x80), + WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, WCD938X_MBHC_NEW_ADC_RESULT, 0xFF), + WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, WCD938X_ANA_MICB2, 0x3F), + WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, WCD938X_MBHC_NEW_CTL_1, 0x10), + WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, WCD938X_MBHC_NEW_CTL_1, 0x04), + WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, WCD938X_ANA_MBHC_ZDET, 0x02), +}; + +static const struct regmap_irq wcd938x_irqs[WCD938X_NUM_IRQS] = { + REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01), + REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02), + REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04), + REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08), + REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_SW_DET, 0, 0x10), + REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_OCP_INT, 0, 0x20), + REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_CNP_INT, 0, 0x40), + REGMAP_IRQ_REG(WCD938X_IRQ_HPHL_OCP_INT, 0, 0x80), + REGMAP_IRQ_REG(WCD938X_IRQ_HPHL_CNP_INT, 1, 0x01), + REGMAP_IRQ_REG(WCD938X_IRQ_EAR_CNP_INT, 1, 0x02), + REGMAP_IRQ_REG(WCD938X_IRQ_EAR_SCD_INT, 1, 0x04), + REGMAP_IRQ_REG(WCD938X_IRQ_AUX_CNP_INT, 1, 0x08), + REGMAP_IRQ_REG(WCD938X_IRQ_AUX_SCD_INT, 1, 0x10), + REGMAP_IRQ_REG(WCD938X_IRQ_HPHL_PDM_WD_INT, 1, 0x20), + REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_PDM_WD_INT, 1, 0x40), + REGMAP_IRQ_REG(WCD938X_IRQ_AUX_PDM_WD_INT, 1, 0x80), + REGMAP_IRQ_REG(WCD938X_IRQ_LDORT_SCD_INT, 2, 0x01), + REGMAP_IRQ_REG(WCD938X_IRQ_MBHC_MOISTURE_INT, 2, 0x02), + REGMAP_IRQ_REG(WCD938X_IRQ_HPHL_SURGE_DET_INT, 2, 0x04), + REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08), +}; + +static struct regmap_irq_chip wcd938x_regmap_irq_chip = { + .name = "wcd938x", + .irqs = wcd938x_irqs, + .num_irqs = ARRAY_SIZE(wcd938x_irqs), + .num_regs = 3, + .status_base = WCD938X_DIGITAL_INTR_STATUS_0, + .mask_base = WCD938X_DIGITAL_INTR_MASK_0, + .ack_base = WCD938X_DIGITAL_INTR_CLEAR_0, + .use_ack = 1, + .runtime_pm = true, + .irq_drv_data = NULL, +}; + +static int wcd938x_get_clk_rate(int mode) +{ + int rate; + + switch (mode) { + case ADC_MODE_ULP2: + rate = SWR_CLK_RATE_0P6MHZ; + break; + case ADC_MODE_ULP1: + rate = SWR_CLK_RATE_1P2MHZ; + break; + case ADC_MODE_LP: + rate = SWR_CLK_RATE_4P8MHZ; + break; + case ADC_MODE_NORMAL: + case ADC_MODE_LO_HIF: + case ADC_MODE_HIFI: + case ADC_MODE_INVALID: + default: + rate = SWR_CLK_RATE_9P6MHZ; + break; + } + + return rate; +} + +static int wcd938x_set_swr_clk_rate(struct snd_soc_component *component, int rate, int bank) +{ + u8 mask = (bank ? 0xF0 : 0x0F); + u8 val = 0; + + switch (rate) { + case SWR_CLK_RATE_0P6MHZ: + val = (bank ? 0x60 : 0x06); + break; + case SWR_CLK_RATE_1P2MHZ: + val = (bank ? 0x50 : 0x05); + break; + case SWR_CLK_RATE_2P4MHZ: + val = (bank ? 0x30 : 0x03); + break; + case SWR_CLK_RATE_4P8MHZ: + val = (bank ? 0x10 : 0x01); + break; + case SWR_CLK_RATE_9P6MHZ: + default: + val = 0x00; + break; + } + snd_soc_component_update_bits(component, WCD938X_DIGITAL_SWR_TX_CLK_RATE, + mask, val); + + return 0; +} + +static int wcd938x_io_init(struct wcd938x_priv *wcd938x) +{ + struct regmap *rm = wcd938x->regmap; + + regmap_update_bits(rm, WCD938X_SLEEP_CTL, 0x0E, 0x0E); + regmap_update_bits(rm, WCD938X_SLEEP_CTL, 0x80, 0x80); + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1010); + regmap_update_bits(rm, WCD938X_SLEEP_CTL, 0x40, 0x40); + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1010); + regmap_update_bits(rm, WCD938X_LDORXTX_CONFIG, 0x10, 0x00); + regmap_update_bits(rm, WCD938X_BIAS_VBG_FINE_ADJ, + 0xF0, 0x80); + regmap_update_bits(rm, WCD938X_ANA_BIAS, 0x80, 0x80); + regmap_update_bits(rm, WCD938X_ANA_BIAS, 0x40, 0x40); + /* 10 msec delay as per HW requirement */ + usleep_range(10000, 10010); + + regmap_update_bits(rm, WCD938X_ANA_BIAS, 0x40, 0x00); + regmap_update_bits(rm, WCD938X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x00); + regmap_update_bits(rm, WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L_NEW, + 0x1F, 0x15); + regmap_update_bits(rm, WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R_NEW, + 0x1F, 0x15); + regmap_update_bits(rm, WCD938X_HPH_REFBUFF_UHQA_CTL, + 0xC0, 0x80); + regmap_update_bits(rm, WCD938X_DIGITAL_CDC_DMIC_CTL, + 0x02, 0x02); + + regmap_update_bits(rm, WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_ULP, + 0xFF, 0x14); + regmap_update_bits(rm, WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_ULP, + 0x1F, 0x08); + + regmap_update_bits(rm, WCD938X_DIGITAL_TX_REQ_FB_CTL_0, 0xFF, 0x55); + regmap_update_bits(rm, WCD938X_DIGITAL_TX_REQ_FB_CTL_1, 0xFF, 0x44); + regmap_update_bits(rm, WCD938X_DIGITAL_TX_REQ_FB_CTL_2, 0xFF, 0x11); + regmap_update_bits(rm, WCD938X_DIGITAL_TX_REQ_FB_CTL_3, 0xFF, 0x00); + regmap_update_bits(rm, WCD938X_DIGITAL_TX_REQ_FB_CTL_4, 0xFF, 0x00); + + /* Set Noise Filter Resistor value */ + regmap_update_bits(rm, WCD938X_MICB1_TEST_CTL_1, 0xE0, 0xE0); + regmap_update_bits(rm, WCD938X_MICB2_TEST_CTL_1, 0xE0, 0xE0); + regmap_update_bits(rm, WCD938X_MICB3_TEST_CTL_1, 0xE0, 0xE0); + regmap_update_bits(rm, WCD938X_MICB4_TEST_CTL_1, 0xE0, 0xE0); + + regmap_update_bits(rm, WCD938X_TX_3_4_TEST_BLK_EN2, 0x01, 0x00); + regmap_update_bits(rm, WCD938X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0xC0); + + return 0; + +} + +static int wcd938x_sdw_connect_port(struct wcd938x_sdw_ch_info *ch_info, + struct sdw_port_config *port_config, + u8 enable) +{ + u8 ch_mask, port_num; + + port_num = ch_info->port_num; + ch_mask = ch_info->ch_mask; + + port_config->num = port_num; + + if (enable) + port_config->ch_mask |= ch_mask; + else + port_config->ch_mask &= ~ch_mask; + + return 0; +} + +static int wcd938x_connect_port(struct wcd938x_sdw_priv *wcd, u8 port_num, u8 ch_id, u8 enable) +{ + return wcd938x_sdw_connect_port(&wcd->ch_info[ch_id], + &wcd->port_config[port_num - 1], + enable); +} + +static int wcd938x_codec_enable_rxclk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_RX_CLK_EN_MASK, 1); + snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES, + WCD938X_RX_BIAS_EN_MASK, 1); + snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_RX0_CTL, + WCD938X_DEM_DITHER_ENABLE_MASK, 0); + snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_RX1_CTL, + WCD938X_DEM_DITHER_ENABLE_MASK, 0); + snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_RX2_CTL, + WCD938X_DEM_DITHER_ENABLE_MASK, 0); + snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_RX_DIV2_CLK_EN_MASK, 1); + snd_soc_component_write_field(component, WCD938X_AUX_AUXPA, + WCD938X_AUXPA_CLK_EN_MASK, 1); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES, + WCD938X_VNEG_EN_MASK, 0); + snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES, + WCD938X_VPOS_EN_MASK, 0); + snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES, + WCD938X_RX_BIAS_EN_MASK, 0); + snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_RX_DIV2_CLK_EN_MASK, 0); + snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_RX_CLK_EN_MASK, 0); + break; + } + return 0; +} + +static int wcd938x_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_RXD0_CLK_EN_MASK, 0x01); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD938X_HPHL_RX_EN_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_HPH_RDAC_CLK_CTL1, + WCD938X_CHOP_CLK_EN_MASK, 0); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write_field(component, + WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L, + WCD938X_HPH_RES_DIV_MASK, 0x02); + if (wcd938x->comp1_enable) { + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_COMP_CTL_0, + WCD938X_HPHL_COMP_EN_MASK, 1); + /* 5msec compander delay as per HW requirement */ + if (!wcd938x->comp2_enable || (snd_soc_component_read(component, + WCD938X_DIGITAL_CDC_COMP_CTL_0) & 0x01)) + usleep_range(5000, 5010); + snd_soc_component_write_field(component, WCD938X_HPH_NEW_INT_HPH_TIMER1, + WCD938X_AUTOCHOP_TIMER_EN, 0); + } else { + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_COMP_CTL_0, + WCD938X_HPHL_COMP_EN_MASK, 0); + snd_soc_component_write_field(component, + WCD938X_HPH_L_EN, + WCD938X_GAIN_SRC_SEL_MASK, + WCD938X_GAIN_SRC_SEL_REGISTER); + + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, + WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD938X_HPH_RES_DIV_MASK, 0x1); + break; + } + + return 0; +} + +static int wcd938x_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_RXD1_CLK_EN_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD938X_HPHR_RX_EN_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_HPH_RDAC_CLK_CTL1, + WCD938X_CHOP_CLK_EN_MASK, 0); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_write_field(component, + WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD938X_HPH_RES_DIV_MASK, 0x02); + if (wcd938x->comp2_enable) { + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_COMP_CTL_0, + WCD938X_HPHR_COMP_EN_MASK, 1); + /* 5msec compander delay as per HW requirement */ + if (!wcd938x->comp1_enable || + (snd_soc_component_read(component, + WCD938X_DIGITAL_CDC_COMP_CTL_0) & 0x02)) + usleep_range(5000, 5010); + snd_soc_component_write_field(component, WCD938X_HPH_NEW_INT_HPH_TIMER1, + WCD938X_AUTOCHOP_TIMER_EN, 0); + } else { + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_COMP_CTL_0, + WCD938X_HPHR_COMP_EN_MASK, 0); + snd_soc_component_write_field(component, + WCD938X_HPH_R_EN, + WCD938X_GAIN_SRC_SEL_MASK, + WCD938X_GAIN_SRC_SEL_REGISTER); + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, + WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R, + WCD938X_HPH_RES_DIV_MASK, 0x01); + break; + } + + return 0; +} + +static int wcd938x_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd938x->ear_rx_path = + snd_soc_component_read( + component, WCD938X_DIGITAL_CDC_EAR_PATH_CTL); + if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX) { + snd_soc_component_write_field(component, + WCD938X_EAR_EAR_DAC_CON, + WCD938X_DAC_SAMPLE_EDGE_SEL_MASK, 0); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_AUX_GAIN_CTL, + WCD938X_AUX_EN_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_RXD2_CLK_EN_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_ANA_EAR_COMPANDER_CTL, + WCD938X_GAIN_OVRD_REG_MASK, 1); + } else { + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD938X_HPHL_RX_EN_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_RXD0_CLK_EN_MASK, 1); + if (wcd938x->comp1_enable) + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_COMP_CTL_0, + WCD938X_HPHL_COMP_EN_MASK, 1); + } + /* 5 msec delay as per HW requirement */ + usleep_range(5000, 5010); + if (wcd938x->flyback_cur_det_disable == 0) + snd_soc_component_write_field(component, WCD938X_FLYBACK_EN, + WCD938X_EN_CUR_DET_MASK, 0); + wcd938x->flyback_cur_det_disable++; + wcd_clsh_ctrl_set_state(wcd938x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + wcd938x->hph_mode); + break; + case SND_SOC_DAPM_POST_PMD: + if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX) { + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_AUX_GAIN_CTL, + WCD938X_AUX_EN_MASK, 0); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_RXD2_CLK_EN_MASK, 0); + } else { + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_HPH_GAIN_CTL, + WCD938X_HPHL_RX_EN_MASK, 0); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_RXD0_CLK_EN_MASK, 0); + if (wcd938x->comp1_enable) + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_COMP_CTL_0, + WCD938X_HPHL_COMP_EN_MASK, 0); + } + snd_soc_component_write_field(component, WCD938X_ANA_EAR_COMPANDER_CTL, + WCD938X_GAIN_OVRD_REG_MASK, 0); + snd_soc_component_write_field(component, + WCD938X_EAR_EAR_DAC_CON, + WCD938X_DAC_SAMPLE_EDGE_SEL_MASK, 1); + break; + } + return 0; + +} + +static int wcd938x_codec_aux_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_RX_DIV4_CLK_EN_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_RXD2_CLK_EN_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_AUX_GAIN_CTL, + WCD938X_AUX_EN_MASK, 1); + if (wcd938x->flyback_cur_det_disable == 0) + snd_soc_component_write_field(component, WCD938X_FLYBACK_EN, + WCD938X_EN_CUR_DET_MASK, 0); + wcd938x->flyback_cur_det_disable++; + wcd_clsh_ctrl_set_state(wcd938x->clsh_info, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_AUX, + wcd938x->hph_mode); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_RX_DIV4_CLK_EN_MASK, 0); + break; + } + return 0; + +} + +static int wcd938x_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd938x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wcd938x->ldoh) + snd_soc_component_write_field(component, WCD938X_LDOH_MODE, + WCD938X_LDOH_EN_MASK, 1); + wcd_clsh_ctrl_set_state(wcd938x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, hph_mode); + wcd_clsh_set_hph_mode(wcd938x->clsh_info, CLS_H_HIFI); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_ULP) { + snd_soc_component_write_field(component, + WCD938X_HPH_REFBUFF_LP_CTL, + WCD938X_PREREF_FLIT_BYPASS_MASK, 1); + } + snd_soc_component_write_field(component, WCD938X_ANA_HPH, + WCD938X_HPHR_REF_EN_MASK, 1); + wcd_clsh_set_hph_mode(wcd938x->clsh_info, hph_mode); + /* 100 usec delay as per HW requirement */ + usleep_range(100, 110); + set_bit(HPH_PA_DELAY, &wcd938x->status_mask); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_PDM_WD_CTL1, + WCD938X_PDM_WD_EN_MASK, 0x3); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd938x->status_mask)) { + if (!wcd938x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD938X_HPH_REFBUFF_LP_CTL, + WCD938X_PREREF_FLIT_BYPASS_MASK, 0); + clear_bit(HPH_PA_DELAY, &wcd938x->status_mask); + } + snd_soc_component_write_field(component, WCD938X_HPH_NEW_INT_HPH_TIMER1, + WCD938X_AUTOCHOP_TIMER_EN, 1); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI || + hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI) + snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES, + WCD938X_REGULATOR_MODE_MASK, + WCD938X_REGULATOR_MODE_CLASS_AB); + enable_irq(wcd938x->hphr_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd938x->hphr_pdm_wd_int); + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (!wcd938x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + snd_soc_component_write_field(component, WCD938X_ANA_HPH, + WCD938X_HPHR_EN_MASK, 0); + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_PRE_HPHR_PA_OFF); + set_bit(HPH_PA_DELAY, &wcd938x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd938x->status_mask)) { + if (!wcd938x->comp2_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd938x->status_mask); + } + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_HPHR_PA_OFF); + snd_soc_component_write_field(component, WCD938X_ANA_HPH, + WCD938X_HPHR_REF_EN_MASK, 0); + snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL1, + WCD938X_PDM_WD_EN_MASK, 0); + wcd_clsh_ctrl_set_state(wcd938x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, hph_mode); + if (wcd938x->ldoh) + snd_soc_component_write_field(component, WCD938X_LDOH_MODE, + WCD938X_LDOH_EN_MASK, 0); + break; + } + + return 0; +} + +static int wcd938x_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd938x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wcd938x->ldoh) + snd_soc_component_write_field(component, WCD938X_LDOH_MODE, + WCD938X_LDOH_EN_MASK, 1); + wcd_clsh_ctrl_set_state(wcd938x->clsh_info, WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, hph_mode); + wcd_clsh_set_hph_mode(wcd938x->clsh_info, CLS_H_HIFI); + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_ULP) { + snd_soc_component_write_field(component, + WCD938X_HPH_REFBUFF_LP_CTL, + WCD938X_PREREF_FLIT_BYPASS_MASK, 1); + } + snd_soc_component_write_field(component, WCD938X_ANA_HPH, + WCD938X_HPHL_REF_EN_MASK, 1); + wcd_clsh_set_hph_mode(wcd938x->clsh_info, hph_mode); + /* 100 usec delay as per HW requirement */ + usleep_range(100, 110); + set_bit(HPH_PA_DELAY, &wcd938x->status_mask); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_PDM_WD_CTL0, + WCD938X_PDM_WD_EN_MASK, 0x3); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd938x->status_mask)) { + if (!wcd938x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + if (hph_mode == CLS_H_LP || hph_mode == CLS_H_LOHIFI || + hph_mode == CLS_H_ULP) + snd_soc_component_write_field(component, + WCD938X_HPH_REFBUFF_LP_CTL, + WCD938X_PREREF_FLIT_BYPASS_MASK, 0); + clear_bit(HPH_PA_DELAY, &wcd938x->status_mask); + } + + snd_soc_component_write_field(component, WCD938X_HPH_NEW_INT_HPH_TIMER1, + WCD938X_AUTOCHOP_TIMER_EN, 1); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI || + hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI) + snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES, + WCD938X_REGULATOR_MODE_MASK, + WCD938X_REGULATOR_MODE_CLASS_AB); + enable_irq(wcd938x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd938x->hphl_pdm_wd_int); + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (!wcd938x->comp1_enable) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + snd_soc_component_write_field(component, WCD938X_ANA_HPH, + WCD938X_HPHL_EN_MASK, 0); + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, WCD_EVENT_PRE_HPHL_PA_OFF); + set_bit(HPH_PA_DELAY, &wcd938x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 7ms sleep is required if compander is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is required. + */ + if (test_bit(HPH_PA_DELAY, &wcd938x->status_mask)) { + if (!wcd938x->comp1_enable) + usleep_range(21000, 21100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &wcd938x->status_mask); + } + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_HPHL_PA_OFF); + snd_soc_component_write_field(component, WCD938X_ANA_HPH, + WCD938X_HPHL_REF_EN_MASK, 0); + snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL0, + WCD938X_PDM_WD_EN_MASK, 0); + wcd_clsh_ctrl_set_state(wcd938x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, hph_mode); + if (wcd938x->ldoh) + snd_soc_component_write_field(component, WCD938X_LDOH_MODE, + WCD938X_LDOH_EN_MASK, 0); + break; + } + + return 0; +} + +static int wcd938x_codec_enable_aux_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd938x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL2, + WCD938X_AUX_PDM_WD_EN_MASK, 1); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1010); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI || + hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI) + snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES, + WCD938X_REGULATOR_MODE_MASK, + WCD938X_REGULATOR_MODE_CLASS_AB); + enable_irq(wcd938x->aux_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + disable_irq_nosync(wcd938x->aux_pdm_wd_int); + break; + case SND_SOC_DAPM_POST_PMD: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1010); + snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL2, + WCD938X_AUX_PDM_WD_EN_MASK, 0); + wcd_clsh_ctrl_set_state(wcd938x->clsh_info, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_AUX, + hph_mode); + + wcd938x->flyback_cur_det_disable--; + if (wcd938x->flyback_cur_det_disable == 0) + snd_soc_component_write_field(component, WCD938X_FLYBACK_EN, + WCD938X_EN_CUR_DET_MASK, 1); + break; + } + return 0; +} + +static int wcd938x_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int hph_mode = wcd938x->hph_mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * Enable watchdog interrupt for HPHL or AUX + * depending on mux value + */ + wcd938x->ear_rx_path = snd_soc_component_read(component, + WCD938X_DIGITAL_CDC_EAR_PATH_CTL); + if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX) + snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL2, + WCD938X_AUX_PDM_WD_EN_MASK, 1); + else + snd_soc_component_write_field(component, + WCD938X_DIGITAL_PDM_WD_CTL0, + WCD938X_PDM_WD_EN_MASK, 0x3); + if (!wcd938x->comp1_enable) + snd_soc_component_write_field(component, + WCD938X_ANA_EAR_COMPANDER_CTL, + WCD938X_GAIN_OVRD_REG_MASK, 1); + + break; + case SND_SOC_DAPM_POST_PMU: + /* 6 msec delay as per HW requirement */ + usleep_range(6000, 6010); + if (hph_mode == CLS_AB || hph_mode == CLS_AB_HIFI || + hph_mode == CLS_AB_LP || hph_mode == CLS_AB_LOHIFI) + snd_soc_component_write_field(component, WCD938X_ANA_RX_SUPPLIES, + WCD938X_REGULATOR_MODE_MASK, + WCD938X_REGULATOR_MODE_CLASS_AB); + if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX) + enable_irq(wcd938x->aux_pdm_wd_int); + else + enable_irq(wcd938x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_PRE_PMD: + if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX) + disable_irq_nosync(wcd938x->aux_pdm_wd_int); + else + disable_irq_nosync(wcd938x->hphl_pdm_wd_int); + break; + case SND_SOC_DAPM_POST_PMD: + if (!wcd938x->comp1_enable) + snd_soc_component_write_field(component, WCD938X_ANA_EAR_COMPANDER_CTL, + WCD938X_GAIN_OVRD_REG_MASK, 0); + /* 7 msec delay as per HW requirement */ + usleep_range(7000, 7010); + if (wcd938x->ear_rx_path & EAR_RX_PATH_AUX) + snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL2, + WCD938X_AUX_PDM_WD_EN_MASK, 0); + else + snd_soc_component_write_field(component, WCD938X_DIGITAL_PDM_WD_CTL0, + WCD938X_PDM_WD_EN_MASK, 0); + + wcd_clsh_ctrl_set_state(wcd938x->clsh_info, WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, hph_mode); + + wcd938x->flyback_cur_det_disable--; + if (wcd938x->flyback_cur_det_disable == 0) + snd_soc_component_write_field(component, WCD938X_FLYBACK_EN, + WCD938X_EN_CUR_DET_MASK, 1); + break; + } + + return 0; +} + +static int wcd938x_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 dmic_clk_reg, dmic_clk_en_reg; + u8 dmic_sel_mask, dmic_clk_mask; + + switch (w->shift) { + case 0: + case 1: + dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC_RATE_1_2; + dmic_clk_en_reg = WCD938X_DIGITAL_CDC_DMIC1_CTL; + dmic_clk_mask = WCD938X_DMIC1_RATE_MASK; + dmic_sel_mask = WCD938X_AMIC1_IN_SEL_MASK; + break; + case 2: + case 3: + dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC_RATE_1_2; + dmic_clk_en_reg = WCD938X_DIGITAL_CDC_DMIC2_CTL; + dmic_clk_mask = WCD938X_DMIC2_RATE_MASK; + dmic_sel_mask = WCD938X_AMIC3_IN_SEL_MASK; + break; + case 4: + case 5: + dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC_RATE_3_4; + dmic_clk_en_reg = WCD938X_DIGITAL_CDC_DMIC3_CTL; + dmic_clk_mask = WCD938X_DMIC3_RATE_MASK; + dmic_sel_mask = WCD938X_AMIC4_IN_SEL_MASK; + break; + case 6: + case 7: + dmic_clk_reg = WCD938X_DIGITAL_CDC_DMIC_RATE_3_4; + dmic_clk_en_reg = WCD938X_DIGITAL_CDC_DMIC4_CTL; + dmic_clk_mask = WCD938X_DMIC4_RATE_MASK; + dmic_sel_mask = WCD938X_AMIC5_IN_SEL_MASK; + break; + default: + dev_err(component->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_AMIC_CTL, + dmic_sel_mask, + WCD938X_AMIC1_IN_SEL_DMIC); + /* 250us sleep as per HW requirement */ + usleep_range(250, 260); + /* Setting DMIC clock rate to 2.4MHz */ + snd_soc_component_write_field(component, dmic_clk_reg, + dmic_clk_mask, + WCD938X_DMIC4_RATE_2P4MHZ); + snd_soc_component_write_field(component, dmic_clk_en_reg, + WCD938X_DMIC_CLK_EN_MASK, 1); + /* enable clock scaling */ + snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_DMIC_CTL, + WCD938X_DMIC_CLK_SCALING_EN_MASK, 0x3); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_AMIC_CTL, + dmic_sel_mask, WCD938X_AMIC1_IN_SEL_AMIC); + snd_soc_component_write_field(component, dmic_clk_en_reg, + WCD938X_DMIC_CLK_EN_MASK, 0); + break; + } + return 0; +} + +static int wcd938x_tx_swr_ctrl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int bank; + int rate; + + bank = (wcd938x_swr_get_current_bank(wcd938x->sdw_priv[AIF1_CAP]->sdev)) ? 0 : 1; + bank = bank ? 0 : 1; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strnstr(w->name, "ADC", sizeof("ADC"))) { + int i = 0, mode = 0; + + if (test_bit(WCD_ADC1, &wcd938x->status_mask)) + mode |= tx_mode_bit[wcd938x->tx_mode[WCD_ADC1]]; + if (test_bit(WCD_ADC2, &wcd938x->status_mask)) + mode |= tx_mode_bit[wcd938x->tx_mode[WCD_ADC2]]; + if (test_bit(WCD_ADC3, &wcd938x->status_mask)) + mode |= tx_mode_bit[wcd938x->tx_mode[WCD_ADC3]]; + if (test_bit(WCD_ADC4, &wcd938x->status_mask)) + mode |= tx_mode_bit[wcd938x->tx_mode[WCD_ADC4]]; + + if (mode != 0) { + for (i = 0; i < ADC_MODE_ULP2; i++) { + if (mode & (1 << i)) { + i++; + break; + } + } + } + rate = wcd938x_get_clk_rate(i); + wcd938x_set_swr_clk_rate(component, rate, bank); + /* Copy clk settings to active bank */ + wcd938x_set_swr_clk_rate(component, rate, !bank); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (strnstr(w->name, "ADC", sizeof("ADC"))) { + rate = wcd938x_get_clk_rate(ADC_MODE_INVALID); + wcd938x_set_swr_clk_rate(component, rate, !bank); + wcd938x_set_swr_clk_rate(component, rate, bank); + } + break; + } + + return 0; +} + +static int wcd938x_get_adc_mode(int val) +{ + int ret = 0; + + switch (val) { + case ADC_MODE_INVALID: + ret = ADC_MODE_VAL_NORMAL; + break; + case ADC_MODE_HIFI: + ret = ADC_MODE_VAL_HIFI; + break; + case ADC_MODE_LO_HIF: + ret = ADC_MODE_VAL_LO_HIF; + break; + case ADC_MODE_NORMAL: + ret = ADC_MODE_VAL_NORMAL; + break; + case ADC_MODE_LP: + ret = ADC_MODE_VAL_LP; + break; + case ADC_MODE_ULP1: + ret = ADC_MODE_VAL_ULP1; + break; + case ADC_MODE_ULP2: + ret = ADC_MODE_VAL_ULP2; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int wcd938x_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_TX_CLK_EN_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_TX_DIV2_CLK_EN_MASK, 1); + set_bit(w->shift, &wcd938x->status_mask); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write_field(component, WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_TX_CLK_EN_MASK, 0); + clear_bit(w->shift, &wcd938x->status_mask); + break; + } + + return 0; +} + +static void wcd938x_tx_channel_config(struct snd_soc_component *component, + int channel, int mode) +{ + int reg, mask; + + switch (channel) { + case 0: + reg = WCD938X_ANA_TX_CH2; + mask = WCD938X_HPF1_INIT_MASK; + break; + case 1: + reg = WCD938X_ANA_TX_CH2; + mask = WCD938X_HPF2_INIT_MASK; + break; + case 2: + reg = WCD938X_ANA_TX_CH4; + mask = WCD938X_HPF3_INIT_MASK; + break; + case 3: + reg = WCD938X_ANA_TX_CH4; + mask = WCD938X_HPF4_INIT_MASK; + break; + default: + return; + } + + snd_soc_component_write_field(component, reg, mask, mode); +} + +static int wcd938x_adc_enable_req(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int mode; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_REQ_CTL, + WCD938X_FS_RATE_4P8_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_REQ_CTL, + WCD938X_NO_NOTCH_MASK, 0); + wcd938x_tx_channel_config(component, w->shift, 1); + mode = wcd938x_get_adc_mode(wcd938x->tx_mode[w->shift]); + if (mode < 0) { + dev_info(component->dev, "Invalid ADC mode\n"); + return -EINVAL; + } + switch (w->shift) { + case 0: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD938X_TXD0_MODE_MASK, mode); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_TXD0_CLK_EN_MASK, 1); + break; + case 1: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD938X_TXD1_MODE_MASK, mode); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_TXD1_CLK_EN_MASK, 1); + break; + case 2: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD938X_TXD2_MODE_MASK, mode); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_TXD2_CLK_EN_MASK, 1); + break; + case 3: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD938X_TXD3_MODE_MASK, mode); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_TXD3_CLK_EN_MASK, 1); + break; + default: + break; + } + + wcd938x_tx_channel_config(component, w->shift, 0); + break; + case SND_SOC_DAPM_POST_PMD: + switch (w->shift) { + case 0: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD938X_TXD0_MODE_MASK, 0); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_TXD0_CLK_EN_MASK, 0); + break; + case 1: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1, + WCD938X_TXD1_MODE_MASK, 0); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_TXD1_CLK_EN_MASK, 0); + break; + case 2: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD938X_TXD2_MODE_MASK, 0); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_TXD2_CLK_EN_MASK, 0); + break; + case 3: + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3, + WCD938X_TXD3_MODE_MASK, 0); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_TXD3_CLK_EN_MASK, 0); + break; + default: + break; + } + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_TX_DIV2_CLK_EN_MASK, 0); + break; + } + + return 0; +} + +static int wcd938x_micbias_control(struct snd_soc_component *component, + int micb_num, int req, bool is_dapm) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int micb_index = micb_num - 1; + u16 micb_reg; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD938X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD938X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD938X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD938X_ANA_MICB4; + break; + default: + dev_err(component->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + + switch (req) { + case MICB_PULLUP_ENABLE: + wcd938x->pullup_ref[micb_index]++; + if ((wcd938x->pullup_ref[micb_index] == 1) && + (wcd938x->micb_ref[micb_index] == 0)) + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_EN_MASK, + WCD938X_MICB_PULL_UP); + break; + case MICB_PULLUP_DISABLE: + if (wcd938x->pullup_ref[micb_index] > 0) + wcd938x->pullup_ref[micb_index]--; + + if ((wcd938x->pullup_ref[micb_index] == 0) && + (wcd938x->micb_ref[micb_index] == 0)) + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_EN_MASK, 0); + break; + case MICB_ENABLE: + wcd938x->micb_ref[micb_index]++; + if (wcd938x->micb_ref[micb_index] == 1) { + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_DIG_CLK_CTL, + WCD938X_TX_CLK_EN_MASK, 0xF); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_ANA_CLK_CTL, + WCD938X_ANA_TX_DIV2_CLK_EN_MASK, 1); + snd_soc_component_write_field(component, + WCD938X_DIGITAL_CDC_ANA_TX_CLK_CTL, + WCD938X_TX_SC_CLK_EN_MASK, 1); + + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_EN_MASK, + WCD938X_MICB_ENABLE); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_ON); + } + if (micb_num == MIC_BIAS_2 && is_dapm) + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + + + break; + case MICB_DISABLE: + if (wcd938x->micb_ref[micb_index] > 0) + wcd938x->micb_ref[micb_index]--; + + if ((wcd938x->micb_ref[micb_index] == 0) && + (wcd938x->pullup_ref[micb_index] > 0)) + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_EN_MASK, + WCD938X_MICB_PULL_UP); + else if ((wcd938x->micb_ref[micb_index] == 0) && + (wcd938x->pullup_ref[micb_index] == 0)) { + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_PRE_MICBIAS_2_OFF); + + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_EN_MASK, 0); + if (micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_MICBIAS_2_OFF); + } + if (is_dapm && micb_num == MIC_BIAS_2) + wcd_mbhc_event_notify(wcd938x->wcd_mbhc, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); + break; + } + + return 0; +} + +static int wcd938x_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd938x_micbias_control(component, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd938x_micbias_control(component, micb_num, MICB_DISABLE, true); + break; + } + + return 0; +} + +static int wcd938x_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int micb_num = w->shift; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd938x_micbias_control(component, micb_num, + MICB_PULLUP_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* 1 msec delay as per HW requirement */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + wcd938x_micbias_control(component, micb_num, + MICB_PULLUP_DISABLE, true); + break; + } + + return 0; +} + +static int wcd938x_tx_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + ucontrol->value.enumerated.item[0] = wcd938x->tx_mode[path]; + + return 0; +} + +static int wcd938x_tx_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + int path = e->shift_l; + + if (wcd938x->tx_mode[path] == ucontrol->value.enumerated.item[0]) + return 0; + + wcd938x->tx_mode[path] = ucontrol->value.enumerated.item[0]; + + return 1; +} + +static int wcd938x_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wcd938x->hph_mode; + + return 0; +} + +static int wcd938x_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + if (wcd938x->hph_mode == ucontrol->value.enumerated.item[0]) + return 0; + + wcd938x->hph_mode = ucontrol->value.enumerated.item[0]; + + return 1; +} + +static int wcd938x_ear_pa_put_gain(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + if (wcd938x->comp1_enable) { + dev_err(component->dev, "Can not set EAR PA Gain, compander1 is enabled\n"); + return -EINVAL; + } + + snd_soc_component_write_field(component, WCD938X_ANA_EAR_COMPANDER_CTL, + WCD938X_EAR_GAIN_MASK, + ucontrol->value.integer.value[0]); + + return 1; +} + +static int wcd938x_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc; + bool hphr; + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + + if (hphr) + ucontrol->value.integer.value[0] = wcd938x->comp2_enable; + else + ucontrol->value.integer.value[0] = wcd938x->comp1_enable; + + return 0; +} + +static int wcd938x_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + struct wcd938x_sdw_priv *wcd; + int value = ucontrol->value.integer.value[0]; + int portidx; + struct soc_mixer_control *mc; + bool hphr; + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + + wcd = wcd938x->sdw_priv[AIF1_PB]; + + if (hphr) + wcd938x->comp2_enable = value; + else + wcd938x->comp1_enable = value; + + portidx = wcd->ch_info[mc->reg].port_num; + + if (value) + wcd938x_connect_port(wcd, portidx, mc->reg, true); + else + wcd938x_connect_port(wcd, portidx, mc->reg, false); + + return 1; +} + +static int wcd938x_ldoh_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd938x->ldoh; + + return 0; +} + +static int wcd938x_ldoh_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + if (wcd938x->ldoh == ucontrol->value.integer.value[0]) + return 0; + + wcd938x->ldoh = ucontrol->value.integer.value[0]; + + return 1; +} + +static int wcd938x_bcs_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd938x->bcs_dis; + + return 0; +} + +static int wcd938x_bcs_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + if (wcd938x->bcs_dis == ucontrol->value.integer.value[0]) + return 0; + + wcd938x->bcs_dis = ucontrol->value.integer.value[0]; + + return 1; +} + +static const char * const tx_mode_mux_text_wcd9380[] = { + "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", +}; + +static const char * const tx_mode_mux_text[] = { + "ADC_INVALID", "ADC_HIFI", "ADC_LO_HIF", "ADC_NORMAL", "ADC_LP", + "ADC_ULP1", "ADC_ULP2", +}; + +static const char * const rx_hph_mode_mux_text_wcd9380[] = { + "CLS_H_INVALID", "CLS_H_INVALID_1", "CLS_H_LP", "CLS_AB", + "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_H_INVALID_2", "CLS_AB_LP", + "CLS_AB_LOHIFI", +}; + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", + "CLS_H_ULP", "CLS_AB_HIFI", "CLS_AB_LP", "CLS_AB_LOHIFI", +}; + +static const char * const adc2_mux_text[] = { + "INP2", "INP3" +}; + +static const char * const adc3_mux_text[] = { + "INP4", "INP6" +}; + +static const char * const adc4_mux_text[] = { + "INP5", "INP7" +}; + +static const char * const rdac3_mux_text[] = { + "RX1", "RX3" +}; + +static const char * const hdr12_mux_text[] = { + "NO_HDR12", "HDR12" +}; + +static const char * const hdr34_mux_text[] = { + "NO_HDR34", "HDR34" +}; + +static const struct soc_enum tx0_mode_enum_wcd9380 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text_wcd9380), + tx_mode_mux_text_wcd9380); + +static const struct soc_enum tx1_mode_enum_wcd9380 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text_wcd9380), + tx_mode_mux_text_wcd9380); + +static const struct soc_enum tx2_mode_enum_wcd9380 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text_wcd9380), + tx_mode_mux_text_wcd9380); + +static const struct soc_enum tx3_mode_enum_wcd9380 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text_wcd9380), + tx_mode_mux_text_wcd9380); + +static const struct soc_enum tx0_mode_enum_wcd9385 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx1_mode_enum_wcd9385 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 1, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx2_mode_enum_wcd9385 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 2, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum tx3_mode_enum_wcd9385 = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 3, ARRAY_SIZE(tx_mode_mux_text), + tx_mode_mux_text); + +static const struct soc_enum rx_hph_mode_mux_enum_wcd9380 = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text_wcd9380), + rx_hph_mode_mux_text_wcd9380); + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(WCD938X_TX_NEW_AMIC_MUX_CFG, 7, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +static const struct soc_enum adc3_enum = + SOC_ENUM_SINGLE(WCD938X_TX_NEW_AMIC_MUX_CFG, 6, + ARRAY_SIZE(adc3_mux_text), adc3_mux_text); + +static const struct soc_enum adc4_enum = + SOC_ENUM_SINGLE(WCD938X_TX_NEW_AMIC_MUX_CFG, 5, + ARRAY_SIZE(adc4_mux_text), adc4_mux_text); + +static const struct soc_enum hdr12_enum = + SOC_ENUM_SINGLE(WCD938X_TX_NEW_AMIC_MUX_CFG, 4, + ARRAY_SIZE(hdr12_mux_text), hdr12_mux_text); + +static const struct soc_enum hdr34_enum = + SOC_ENUM_SINGLE(WCD938X_TX_NEW_AMIC_MUX_CFG, 3, + ARRAY_SIZE(hdr34_mux_text), hdr34_mux_text); + +static const struct soc_enum rdac3_enum = + SOC_ENUM_SINGLE(WCD938X_DIGITAL_CDC_EAR_PATH_CTL, 0, + ARRAY_SIZE(rdac3_mux_text), rdac3_mux_text); + +static const struct snd_kcontrol_new adc1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new adc4_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic1_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic2_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic3_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic4_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic5_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic6_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic7_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new dmic8_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new ear_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new aux_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphl_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new hphr_rdac_switch[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new tx_adc2_mux = + SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + +static const struct snd_kcontrol_new tx_adc3_mux = + SOC_DAPM_ENUM("ADC3 MUX Mux", adc3_enum); + +static const struct snd_kcontrol_new tx_adc4_mux = + SOC_DAPM_ENUM("ADC4 MUX Mux", adc4_enum); + +static const struct snd_kcontrol_new tx_hdr12_mux = + SOC_DAPM_ENUM("HDR12 MUX Mux", hdr12_enum); + +static const struct snd_kcontrol_new tx_hdr34_mux = + SOC_DAPM_ENUM("HDR34 MUX Mux", hdr34_enum); + +static const struct snd_kcontrol_new rx_rdac3_mux = + SOC_DAPM_ENUM("RDAC3_MUX Mux", rdac3_enum); + +static const struct snd_kcontrol_new wcd9380_snd_controls[] = { + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum_wcd9380, + wcd938x_rx_hph_mode_get, wcd938x_rx_hph_mode_put), + SOC_ENUM_EXT("TX0 MODE", tx0_mode_enum_wcd9380, + wcd938x_tx_mode_get, wcd938x_tx_mode_put), + SOC_ENUM_EXT("TX1 MODE", tx1_mode_enum_wcd9380, + wcd938x_tx_mode_get, wcd938x_tx_mode_put), + SOC_ENUM_EXT("TX2 MODE", tx2_mode_enum_wcd9380, + wcd938x_tx_mode_get, wcd938x_tx_mode_put), + SOC_ENUM_EXT("TX3 MODE", tx3_mode_enum_wcd9380, + wcd938x_tx_mode_get, wcd938x_tx_mode_put), +}; + +static const struct snd_kcontrol_new wcd9385_snd_controls[] = { + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + wcd938x_rx_hph_mode_get, wcd938x_rx_hph_mode_put), + SOC_ENUM_EXT("TX0 MODE", tx0_mode_enum_wcd9385, + wcd938x_tx_mode_get, wcd938x_tx_mode_put), + SOC_ENUM_EXT("TX1 MODE", tx1_mode_enum_wcd9385, + wcd938x_tx_mode_get, wcd938x_tx_mode_put), + SOC_ENUM_EXT("TX2 MODE", tx2_mode_enum_wcd9385, + wcd938x_tx_mode_get, wcd938x_tx_mode_put), + SOC_ENUM_EXT("TX3 MODE", tx3_mode_enum_wcd9385, + wcd938x_tx_mode_get, wcd938x_tx_mode_put), +}; + +static int wcd938x_get_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(comp); + struct wcd938x_sdw_priv *wcd; + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int dai_id = mixer->shift; + int portidx, ch_idx = mixer->reg; + + + wcd = wcd938x->sdw_priv[dai_id]; + portidx = wcd->ch_info[ch_idx].port_num; + + ucontrol->value.integer.value[0] = wcd->port_enable[portidx]; + + return 0; +} + +static int wcd938x_set_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(comp); + struct wcd938x_sdw_priv *wcd; + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kcontrol->private_value; + int ch_idx = mixer->reg; + int portidx; + int dai_id = mixer->shift; + bool enable; + + wcd = wcd938x->sdw_priv[dai_id]; + + portidx = wcd->ch_info[ch_idx].port_num; + if (ucontrol->value.integer.value[0]) + enable = true; + else + enable = false; + + wcd->port_enable[portidx] = enable; + + wcd938x_connect_port(wcd, portidx, ch_idx, enable); + + return 1; + +} + +/* MBHC related */ +static void wcd938x_mbhc_clk_setup(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_1, + WCD938X_MBHC_CTL_RCO_EN_MASK, enable); +} + +static void wcd938x_mbhc_mbhc_bias_control(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_ELECT, + WCD938X_ANA_MBHC_BIAS_EN, enable); +} + +static void wcd938x_mbhc_program_btn_thr(struct snd_soc_component *component, + int *btn_low, int *btn_high, + int num_btn, bool is_micbias) +{ + int i, vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(component->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_BTN0 + i, + WCD938X_MBHC_BTN_VTH_MASK, vth); + dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool wcd938x_mbhc_micb_en_status(struct snd_soc_component *component, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = snd_soc_component_read_field(component, + WCD938X_ANA_MICB2, + WCD938X_ANA_MICB2_ENABLE_MASK); + if (val == WCD938X_MICB_ENABLE) + return true; + } + return false; +} + +static void wcd938x_mbhc_hph_l_pull_up_control(struct snd_soc_component *component, + int pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur > HS_PULLUP_I_OFF || pull_up_cur < HS_PULLUP_I_3P0_UA) + pull_up_cur = HS_PULLUP_I_2P0_UA; + + snd_soc_component_write_field(component, + WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT, + WCD938X_HSDET_PULLUP_C_MASK, pull_up_cur); +} + +static int wcd938x_mbhc_request_micbias(struct snd_soc_component *component, + int micb_num, int req) +{ + return wcd938x_micbias_control(component, micb_num, req, false); +} + +static void wcd938x_mbhc_micb_ramp_control(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP, + WCD938X_RAMP_SHIFT_CTRL_MASK, 0x0C); + snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP, + WCD938X_RAMP_EN_MASK, 1); + } else { + snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP, + WCD938X_RAMP_EN_MASK, 0); + snd_soc_component_write_field(component, WCD938X_ANA_MICB2_RAMP, + WCD938X_RAMP_SHIFT_CTRL_MASK, 0); + } +} + +static int wcd938x_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) + return -EINVAL; + + return (micb_mv - 1000) / 50; +} + +static int wcd938x_mbhc_micb_adjust_voltage(struct snd_soc_component *component, + int req_volt, int micb_num) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int cur_vout_ctl, req_vout_ctl, micb_reg, micb_en, ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD938X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD938X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD938X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD938X_ANA_MICB4; + break; + default: + return -EINVAL; + } + mutex_lock(&wcd938x->micb_lock); + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_en = snd_soc_component_read_field(component, micb_reg, + WCD938X_MICB_EN_MASK); + cur_vout_ctl = snd_soc_component_read_field(component, micb_reg, + WCD938X_MICB_VOUT_MASK); + + req_vout_ctl = wcd938x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = -EINVAL; + goto exit; + } + + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + if (micb_en == WCD938X_MICB_ENABLE) + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_EN_MASK, + WCD938X_MICB_PULL_UP); + + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_VOUT_MASK, + req_vout_ctl); + + if (micb_en == WCD938X_MICB_ENABLE) { + snd_soc_component_write_field(component, micb_reg, + WCD938X_MICB_EN_MASK, + WCD938X_MICB_ENABLE); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&wcd938x->micb_lock); + return ret; +} + +static int wcd938x_mbhc_micb_ctrl_threshold_mic(struct snd_soc_component *component, + int micb_num, bool req_en) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (wcd938x->micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : wcd938x->micb2_mv; + + return wcd938x_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2); +} + +static void wcd938x_mbhc_get_result_params(struct snd_soc_component *component, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + static const int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < WCD938X_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd938x->regmap, WCD938X_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd938x->regmap, WCD938X_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = WCD938X_MBHC_GET_X1(val); + c1 = WCD938X_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_err(component->dev, "Impedance detect ramp error, c1=%d, x1=0x%x\n", + c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (WCD938X_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = WCD938X_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(component->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d (milliohm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_read(wcd938x->regmap, + WCD938X_ANA_MBHC_RESULT_1, &val); + regmap_read(wcd938x->regmap, + WCD938X_ANA_MBHC_RESULT_2, &val1); + val = val << 0x08; + val |= val1; + x1 = WCD938X_MBHC_GET_X1(val); + i++; + if (i == WCD938X_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void wcd938x_mbhc_zdet_ramp(struct snd_soc_component *component, + struct wcd938x_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + int32_t zdet = 0; + + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL, + WCD938X_ZDET_MAXV_CTL_MASK, zdet_param->ldo_ctl); + snd_soc_component_update_bits(component, WCD938X_ANA_MBHC_BTN5, + WCD938X_VTH_MASK, zdet_param->btn5); + snd_soc_component_update_bits(component, WCD938X_ANA_MBHC_BTN6, + WCD938X_VTH_MASK, zdet_param->btn6); + snd_soc_component_update_bits(component, WCD938X_ANA_MBHC_BTN7, + WCD938X_VTH_MASK, zdet_param->btn7); + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL, + WCD938X_ZDET_RANGE_CTL_MASK, zdet_param->noff); + snd_soc_component_update_bits(component, WCD938X_MBHC_NEW_ZDET_RAMP_CTL, + 0x0F, zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(component->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + wcd938x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(component->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + wcd938x_mbhc_get_result_params(component, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static void wcd938x_wcd_mbhc_qfuse_cal(struct snd_soc_component *component, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (WCD938X_ZDET_VAL_400/1000)) + q1 = snd_soc_component_read(component, + WCD938X_DIGITAL_EFUSE_REG_23 + (2 * flag_l_r)); + else + q1 = snd_soc_component_read(component, + WCD938X_DIGITAL_EFUSE_REG_24 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void wcd938x_wcd_mbhc_calc_impedance(struct snd_soc_component *component, + uint32_t *zl, uint32_t *zr) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct wcd938x_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct wcd938x_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + reg0 = snd_soc_component_read(component, WCD938X_ANA_MBHC_BTN5); + reg1 = snd_soc_component_read(component, WCD938X_ANA_MBHC_BTN6); + reg2 = snd_soc_component_read(component, WCD938X_ANA_MBHC_BTN7); + reg3 = snd_soc_component_read(component, WCD938X_MBHC_CTL_CLK); + reg4 = snd_soc_component_read(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_component_read(component, WCD938X_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (wcd938x->mbhc_cfg.hphl_swh) + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_MECH, 0x01, 0x00); + + /* Disable surge protection before impedance detection. + * This is done to give correct value for high impedance. + */ + regmap_update_bits(wcd938x->regmap, + WCD938X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0x00); + /* 1ms delay needed after disable surge protection */ + usleep_range(1000, 1010); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); + + if (!WCD938X_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1L < WCD938X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > WCD938X_ZDET_VAL_400) && + (z1L <= WCD938X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > WCD938X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == WCD938X_ZDET_FLOATING_IMPEDANCE) || + (z1L > WCD938X_ZDET_VAL_100K)) { + *zl = WCD938X_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + wcd938x_wcd_mbhc_qfuse_cal(component, zl, 0); + } + dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1); + if (WCD938X_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > WCD938X_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != WCD938X_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1R < WCD938X_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > WCD938X_ZDET_VAL_400) && + (z1R <= WCD938X_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > WCD938X_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + wcd938x_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == WCD938X_ZDET_FLOATING_IMPEDANCE) || + (z1R > WCD938X_ZDET_VAL_100K)) { + *zr = WCD938X_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + wcd938x_wcd_mbhc_qfuse_cal(component, zr, 1); + } + dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if ((*zl == WCD938X_ZDET_FLOATING_IMPEDANCE) && + (*zr == WCD938X_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(component->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == WCD938X_ZDET_FLOATING_IMPEDANCE) || + (*zr == WCD938X_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(component->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + wcd_mbhc_set_hph_type(wcd938x->wcd_mbhc, WCD_MBHC_HPH_MONO); + goto zdet_complete; + } + snd_soc_component_write_field(component, WCD938X_HPH_R_ATEST, + WCD938X_HPHPA_GND_OVR_MASK, 1); + snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2, + WCD938X_HPHPA_GND_R_MASK, 1); + if (*zl < (WCD938X_ZDET_VAL_32/1000)) + wcd938x_mbhc_zdet_ramp(component, &zdet_param[0], &z1Ls, NULL, d1); + else + wcd938x_mbhc_zdet_ramp(component, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2, + WCD938X_HPHPA_GND_R_MASK, 0); + snd_soc_component_write_field(component, WCD938X_HPH_R_ATEST, + WCD938X_HPHPA_GND_OVR_MASK, 0); + z1Ls /= 1000; + wcd938x_wcd_mbhc_qfuse_cal(component, &z1Ls, 0); + /* Parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_dbg(component->dev, "%s: stereo plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd938x->wcd_mbhc, WCD_MBHC_HPH_STEREO); + } else { + dev_dbg(component->dev, "%s: MONO plug type detected\n", + __func__); + wcd_mbhc_set_hph_type(wcd938x->wcd_mbhc, WCD_MBHC_HPH_MONO); + } + + /* Enable surge protection again after impedance detection */ + regmap_update_bits(wcd938x->regmap, + WCD938X_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0xC0); +zdet_complete: + snd_soc_component_write(component, WCD938X_ANA_MBHC_BTN5, reg0); + snd_soc_component_write(component, WCD938X_ANA_MBHC_BTN6, reg1); + snd_soc_component_write(component, WCD938X_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (wcd938x->mbhc_cfg.hphl_swh) + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_component_write(component, WCD938X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_component_write(component, WCD938X_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd938x->regmap, + WCD938X_ANA_MBHC_ELECT, 0x80, 0x80); +} + +static void wcd938x_mbhc_gnd_det_ctrl(struct snd_soc_component *component, + bool enable) +{ + if (enable) { + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH, + WCD938X_MBHC_HSG_PULLUP_COMP_EN, 1); + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH, + WCD938X_MBHC_GND_DET_EN_MASK, 1); + } else { + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH, + WCD938X_MBHC_GND_DET_EN_MASK, 0); + snd_soc_component_write_field(component, WCD938X_ANA_MBHC_MECH, + WCD938X_MBHC_HSG_PULLUP_COMP_EN, 0); + } +} + +static void wcd938x_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2, + WCD938X_HPHPA_GND_R_MASK, enable); + snd_soc_component_write_field(component, WCD938X_HPH_PA_CTL2, + WCD938X_HPHPA_GND_L_MASK, enable); +} + +static void wcd938x_mbhc_moisture_config(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + if (wcd938x->mbhc_cfg.moist_rref == R_OFF) { + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, R_OFF); + return; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd938x->mbhc_cfg.hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, R_OFF); + return; + } + + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, wcd938x->mbhc_cfg.moist_rref); +} + +static void wcd938x_mbhc_moisture_detect_en(struct snd_soc_component *component, bool enable) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + if (enable) + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, wcd938x->mbhc_cfg.moist_rref); + else + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, R_OFF); +} + +static bool wcd938x_mbhc_get_moisture_status(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + bool ret = false; + + if (wcd938x->mbhc_cfg.moist_rref == R_OFF) { + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, R_OFF); + goto done; + } + + /* Do not enable moisture detection if jack type is NC */ + if (!wcd938x->mbhc_cfg.hphl_swh) { + dev_dbg(component->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_component_write_field(component, WCD938X_MBHC_NEW_CTL_2, + WCD938X_M_RTH_CTL_MASK, R_OFF); + goto done; + } + + /* + * If moisture_en is already enabled, then skip to plug type + * detection. + */ + if (snd_soc_component_read_field(component, WCD938X_MBHC_NEW_CTL_2, WCD938X_M_RTH_CTL_MASK)) + goto done; + + wcd938x_mbhc_moisture_detect_en(component, true); + /* Read moisture comparator status */ + ret = ((snd_soc_component_read(component, WCD938X_MBHC_NEW_FSM_STATUS) + & 0x20) ? 0 : 1); + +done: + return ret; + +} + +static void wcd938x_mbhc_moisture_polling_ctrl(struct snd_soc_component *component, + bool enable) +{ + snd_soc_component_write_field(component, + WCD938X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL, + WCD938X_MOISTURE_EN_POLLING_MASK, enable); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .clk_setup = wcd938x_mbhc_clk_setup, + .mbhc_bias = wcd938x_mbhc_mbhc_bias_control, + .set_btn_thr = wcd938x_mbhc_program_btn_thr, + .micbias_enable_status = wcd938x_mbhc_micb_en_status, + .hph_pull_up_control_v2 = wcd938x_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = wcd938x_mbhc_request_micbias, + .mbhc_micb_ramp_control = wcd938x_mbhc_micb_ramp_control, + .mbhc_micb_ctrl_thr_mic = wcd938x_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = wcd938x_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = wcd938x_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = wcd938x_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = wcd938x_mbhc_moisture_config, + .mbhc_get_moisture_status = wcd938x_mbhc_get_moisture_status, + .mbhc_moisture_polling_ctrl = wcd938x_mbhc_moisture_polling_ctrl, + .mbhc_moisture_detect_en = wcd938x_mbhc_moisture_detect_en, +}; + +static int wcd938x_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wcd_mbhc_get_hph_type(wcd938x->wcd_mbhc); + + return 0; +} + +static int wcd938x_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_mixer_control *mc; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + mc = (struct soc_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(wcd938x->wcd_mbhc, &zl, &zr); + dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, WCD_MBHC_HPH_STEREO, 0, + wcd938x_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, INT_MAX, 0, + wcd938x_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, INT_MAX, 0, + wcd938x_hph_impedance_get, NULL), +}; + +static int wcd938x_mbhc_init(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + struct wcd_mbhc_intr *intr_ids = &wcd938x->intr_ids; + + intr_ids->mbhc_sw_intr = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_MBHC_SW_DET); + intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_MBHC_BUTTON_PRESS_DET); + intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_MBHC_BUTTON_RELEASE_DET); + intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_MBHC_ELECT_INS_REM_LEG_DET); + intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_MBHC_ELECT_INS_REM_DET); + intr_ids->hph_left_ocp = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_HPHL_OCP_INT); + intr_ids->hph_right_ocp = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_HPHR_OCP_INT); + + wcd938x->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, wcd_mbhc_fields, true); + if (IS_ERR(wcd938x->wcd_mbhc)) + return PTR_ERR(wcd938x->wcd_mbhc); + + snd_soc_add_component_controls(component, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_component_controls(component, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + return 0; +} + +static void wcd938x_mbhc_deinit(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + wcd_mbhc_deinit(wcd938x->wcd_mbhc); +} + +/* END MBHC */ + +static const struct snd_kcontrol_new wcd938x_snd_controls[] = { + SOC_SINGLE_EXT("HPHL_COMP Switch", WCD938X_COMP_L, 0, 1, 0, + wcd938x_get_compander, wcd938x_set_compander), + SOC_SINGLE_EXT("HPHR_COMP Switch", WCD938X_COMP_R, 1, 1, 0, + wcd938x_get_compander, wcd938x_set_compander), + SOC_SINGLE_EXT("HPHL Switch", WCD938X_HPH_L, 0, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("HPHR Switch", WCD938X_HPH_R, 0, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("CLSH Switch", WCD938X_CLSH, 0, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("LO Switch", WCD938X_LO, 0, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("DSD_L Switch", WCD938X_DSD_L, 0, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("DSD_R Switch", WCD938X_DSD_R, 0, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_TLV("HPHL Volume", WCD938X_HPH_L_EN, 0, 0x18, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD938X_HPH_R_EN, 0, 0x18, 1, line_gain), + WCD938X_EAR_PA_GAIN_TLV("EAR_PA Volume", WCD938X_ANA_EAR_COMPANDER_CTL, + 2, 0x10, 0, ear_pa_gain), + SOC_SINGLE_EXT("ADC1 Switch", WCD938X_ADC1, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("ADC2 Switch", WCD938X_ADC2, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("ADC3 Switch", WCD938X_ADC3, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("ADC4 Switch", WCD938X_ADC4, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("DMIC0 Switch", WCD938X_DMIC0, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("DMIC1 Switch", WCD938X_DMIC1, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("MBHC Switch", WCD938X_MBHC, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("DMIC2 Switch", WCD938X_DMIC2, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("DMIC3 Switch", WCD938X_DMIC3, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("DMIC4 Switch", WCD938X_DMIC4, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("DMIC5 Switch", WCD938X_DMIC5, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("DMIC6 Switch", WCD938X_DMIC6, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("DMIC7 Switch", WCD938X_DMIC7, 1, 1, 0, + wcd938x_get_swr_port, wcd938x_set_swr_port), + SOC_SINGLE_EXT("LDOH Enable Switch", SND_SOC_NOPM, 0, 1, 0, + wcd938x_ldoh_get, wcd938x_ldoh_put), + SOC_SINGLE_EXT("ADC2_BCS Disable Switch", SND_SOC_NOPM, 0, 1, 0, + wcd938x_bcs_get, wcd938x_bcs_put), + + SOC_SINGLE_TLV("ADC1 Volume", WCD938X_ANA_TX_CH1, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD938X_ANA_TX_CH2, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD938X_ANA_TX_CH3, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD938X_ANA_TX_CH4, 0, 20, 0, analog_gain), +}; + +static const struct snd_soc_dapm_widget wcd938x_dapm_widgets[] = { + + /*input widgets*/ + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + SND_SOC_DAPM_INPUT("AMIC6"), + SND_SOC_DAPM_INPUT("AMIC7"), + SND_SOC_DAPM_MIC("Analog Mic1", NULL), + SND_SOC_DAPM_MIC("Analog Mic2", NULL), + SND_SOC_DAPM_MIC("Analog Mic3", NULL), + SND_SOC_DAPM_MIC("Analog Mic4", NULL), + SND_SOC_DAPM_MIC("Analog Mic5", NULL), + + /*tx widgets*/ + SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, + wcd938x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0, + wcd938x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC3", NULL, SND_SOC_NOPM, 2, 0, + wcd938x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC4", NULL, SND_SOC_NOPM, 3, 0, + wcd938x_codec_enable_adc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + wcd938x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0, + wcd938x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 2, 0, + wcd938x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 3, 0, + wcd938x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 4, 0, + wcd938x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 5, 0, + wcd938x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC7", NULL, SND_SOC_NOPM, 6, 0, + wcd938x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC8", NULL, SND_SOC_NOPM, 7, 0, + wcd938x_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("ADC1 REQ", SND_SOC_NOPM, 0, 0, + NULL, 0, wcd938x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2 REQ", SND_SOC_NOPM, 1, 0, + NULL, 0, wcd938x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3 REQ", SND_SOC_NOPM, 2, 0, + NULL, 0, wcd938x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC4 REQ", SND_SOC_NOPM, 3, 0, NULL, 0, + wcd938x_adc_enable_req, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + SND_SOC_DAPM_MUX("ADC3 MUX", SND_SOC_NOPM, 0, 0, &tx_adc3_mux), + SND_SOC_DAPM_MUX("ADC4 MUX", SND_SOC_NOPM, 0, 0, &tx_adc4_mux), + SND_SOC_DAPM_MUX("HDR12 MUX", SND_SOC_NOPM, 0, 0, &tx_hdr12_mux), + SND_SOC_DAPM_MUX("HDR34 MUX", SND_SOC_NOPM, 0, 0, &tx_hdr34_mux), + + /*tx mixers*/ + SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0, adc1_switch, + ARRAY_SIZE(adc1_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 0, 0, adc2_switch, + ARRAY_SIZE(adc2_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC3_MIXER", SND_SOC_NOPM, 0, 0, adc3_switch, + ARRAY_SIZE(adc3_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("ADC4_MIXER", SND_SOC_NOPM, 0, 0, adc4_switch, + ARRAY_SIZE(adc4_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0, 0, dmic1_switch, + ARRAY_SIZE(dmic1_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 0, 0, dmic2_switch, + ARRAY_SIZE(dmic2_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC3_MIXER", SND_SOC_NOPM, 0, 0, dmic3_switch, + ARRAY_SIZE(dmic3_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC4_MIXER", SND_SOC_NOPM, 0, 0, dmic4_switch, + ARRAY_SIZE(dmic4_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC5_MIXER", SND_SOC_NOPM, 0, 0, dmic5_switch, + ARRAY_SIZE(dmic5_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC6_MIXER", SND_SOC_NOPM, 0, 0, dmic6_switch, + ARRAY_SIZE(dmic6_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC7_MIXER", SND_SOC_NOPM, 0, 0, dmic7_switch, + ARRAY_SIZE(dmic7_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("DMIC8_MIXER", SND_SOC_NOPM, 0, 0, dmic8_switch, + ARRAY_SIZE(dmic8_switch), wcd938x_tx_swr_ctrl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /* micbias widgets*/ + SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd938x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, + wcd938x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, + wcd938x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0, + wcd938x_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* micbias pull up widgets*/ + SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, + wcd938x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS2", SND_SOC_NOPM, MIC_BIAS_2, 0, + wcd938x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS3", SND_SOC_NOPM, MIC_BIAS_3, 0, + wcd938x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VA MIC BIAS4", SND_SOC_NOPM, MIC_BIAS_4, 0, + wcd938x_codec_enable_micbias_pullup, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /*output widgets tx*/ + SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("ADC4_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC3_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC4_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC5_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC6_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC7_OUTPUT"), + SND_SOC_DAPM_OUTPUT("DMIC8_OUTPUT"), + + SND_SOC_DAPM_INPUT("IN1_HPHL"), + SND_SOC_DAPM_INPUT("IN2_HPHR"), + SND_SOC_DAPM_INPUT("IN3_AUX"), + + /*rx widgets*/ + SND_SOC_DAPM_PGA_E("EAR PGA", WCD938X_ANA_EAR, 7, 0, NULL, 0, + wcd938x_codec_enable_ear_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("AUX PGA", WCD938X_AUX_AUXPA, 7, 0, NULL, 0, + wcd938x_codec_enable_aux_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PGA", WCD938X_ANA_HPH, 7, 0, NULL, 0, + wcd938x_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PGA", WCD938X_ANA_HPH, 6, 0, NULL, 0, + wcd938x_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, + wcd938x_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0, + wcd938x_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0, + wcd938x_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RDAC4", NULL, SND_SOC_NOPM, 0, 0, + wcd938x_codec_aux_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RDAC3_MUX", SND_SOC_NOPM, 0, 0, &rx_rdac3_mux), + + SND_SOC_DAPM_SUPPLY("VDD_BUCK", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0, + wcd938x_codec_enable_rxclk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("CLS_H_PORT", 1, SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX3", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0), + + /* rx mixer widgets*/ + SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0, + ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)), + SND_SOC_DAPM_MIXER("AUX_RDAC", SND_SOC_NOPM, 0, 0, + aux_rdac_switch, ARRAY_SIZE(aux_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0, + hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)), + SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0, + hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)), + + /*output widgets rx*/ + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("AUX"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + +}; + +static const struct snd_soc_dapm_route wcd938x_audio_map[] = { + {"ADC1_OUTPUT", NULL, "ADC1_MIXER"}, + {"ADC1_MIXER", "Switch", "ADC1 REQ"}, + {"ADC1 REQ", NULL, "ADC1"}, + {"ADC1", NULL, "AMIC1"}, + + {"ADC2_OUTPUT", NULL, "ADC2_MIXER"}, + {"ADC2_MIXER", "Switch", "ADC2 REQ"}, + {"ADC2 REQ", NULL, "ADC2"}, + {"ADC2", NULL, "HDR12 MUX"}, + {"HDR12 MUX", "NO_HDR12", "ADC2 MUX"}, + {"HDR12 MUX", "HDR12", "AMIC1"}, + {"ADC2 MUX", "INP3", "AMIC3"}, + {"ADC2 MUX", "INP2", "AMIC2"}, + + {"ADC3_OUTPUT", NULL, "ADC3_MIXER"}, + {"ADC3_MIXER", "Switch", "ADC3 REQ"}, + {"ADC3 REQ", NULL, "ADC3"}, + {"ADC3", NULL, "HDR34 MUX"}, + {"HDR34 MUX", "NO_HDR34", "ADC3 MUX"}, + {"HDR34 MUX", "HDR34", "AMIC5"}, + {"ADC3 MUX", "INP4", "AMIC4"}, + {"ADC3 MUX", "INP6", "AMIC6"}, + + {"ADC4_OUTPUT", NULL, "ADC4_MIXER"}, + {"ADC4_MIXER", "Switch", "ADC4 REQ"}, + {"ADC4 REQ", NULL, "ADC4"}, + {"ADC4", NULL, "ADC4 MUX"}, + {"ADC4 MUX", "INP5", "AMIC5"}, + {"ADC4 MUX", "INP7", "AMIC7"}, + + {"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"}, + {"DMIC1_MIXER", "Switch", "DMIC1"}, + + {"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"}, + {"DMIC2_MIXER", "Switch", "DMIC2"}, + + {"DMIC3_OUTPUT", NULL, "DMIC3_MIXER"}, + {"DMIC3_MIXER", "Switch", "DMIC3"}, + + {"DMIC4_OUTPUT", NULL, "DMIC4_MIXER"}, + {"DMIC4_MIXER", "Switch", "DMIC4"}, + + {"DMIC5_OUTPUT", NULL, "DMIC5_MIXER"}, + {"DMIC5_MIXER", "Switch", "DMIC5"}, + + {"DMIC6_OUTPUT", NULL, "DMIC6_MIXER"}, + {"DMIC6_MIXER", "Switch", "DMIC6"}, + + {"DMIC7_OUTPUT", NULL, "DMIC7_MIXER"}, + {"DMIC7_MIXER", "Switch", "DMIC7"}, + + {"DMIC8_OUTPUT", NULL, "DMIC8_MIXER"}, + {"DMIC8_MIXER", "Switch", "DMIC8"}, + + {"IN1_HPHL", NULL, "VDD_BUCK"}, + {"IN1_HPHL", NULL, "CLS_H_PORT"}, + + {"RX1", NULL, "IN1_HPHL"}, + {"RX1", NULL, "RXCLK"}, + {"RDAC1", NULL, "RX1"}, + {"HPHL_RDAC", "Switch", "RDAC1"}, + {"HPHL PGA", NULL, "HPHL_RDAC"}, + {"HPHL", NULL, "HPHL PGA"}, + + {"IN2_HPHR", NULL, "VDD_BUCK"}, + {"IN2_HPHR", NULL, "CLS_H_PORT"}, + {"RX2", NULL, "IN2_HPHR"}, + {"RDAC2", NULL, "RX2"}, + {"RX2", NULL, "RXCLK"}, + {"HPHR_RDAC", "Switch", "RDAC2"}, + {"HPHR PGA", NULL, "HPHR_RDAC"}, + {"HPHR", NULL, "HPHR PGA"}, + + {"IN3_AUX", NULL, "VDD_BUCK"}, + {"IN3_AUX", NULL, "CLS_H_PORT"}, + {"RX3", NULL, "IN3_AUX"}, + {"RDAC4", NULL, "RX3"}, + {"RX3", NULL, "RXCLK"}, + {"AUX_RDAC", "Switch", "RDAC4"}, + {"AUX PGA", NULL, "AUX_RDAC"}, + {"AUX", NULL, "AUX PGA"}, + + {"RDAC3_MUX", "RX3", "RX3"}, + {"RDAC3_MUX", "RX1", "RX1"}, + {"RDAC3", NULL, "RDAC3_MUX"}, + {"EAR_RDAC", "Switch", "RDAC3"}, + {"EAR PGA", NULL, "EAR_RDAC"}, + {"EAR", NULL, "EAR PGA"}, +}; + +static int wcd938x_set_micbias_data(struct wcd938x_priv *wcd938x) +{ + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + + /* set micbias voltage */ + vout_ctl_1 = wcd938x_get_micb_vout_ctl_val(wcd938x->micb1_mv); + vout_ctl_2 = wcd938x_get_micb_vout_ctl_val(wcd938x->micb2_mv); + vout_ctl_3 = wcd938x_get_micb_vout_ctl_val(wcd938x->micb3_mv); + vout_ctl_4 = wcd938x_get_micb_vout_ctl_val(wcd938x->micb4_mv); + if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || vout_ctl_3 < 0 || vout_ctl_4 < 0) + return -EINVAL; + + regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB1, + WCD938X_MICB_VOUT_MASK, vout_ctl_1); + regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB2, + WCD938X_MICB_VOUT_MASK, vout_ctl_2); + regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB3, + WCD938X_MICB_VOUT_MASK, vout_ctl_3); + regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB4, + WCD938X_MICB_VOUT_MASK, vout_ctl_4); + + return 0; +} + +static irqreturn_t wcd938x_wd_handle_irq(int irq, void *data) +{ + return IRQ_HANDLED; +} + +static struct irq_chip wcd_irq_chip = { + .name = "WCD938x", +}; + +static int wcd_irq_chip_map(struct irq_domain *irqd, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &wcd_irq_chip, handle_simple_irq); + irq_set_nested_thread(virq, 1); + irq_set_noprobe(virq); + + return 0; +} + +static const struct irq_domain_ops wcd_domain_ops = { + .map = wcd_irq_chip_map, +}; + +static int wcd938x_irq_init(struct wcd938x_priv *wcd, struct device *dev) +{ + + wcd->virq = irq_domain_add_linear(NULL, 1, &wcd_domain_ops, NULL); + if (!(wcd->virq)) { + dev_err(dev, "%s: Failed to add IRQ domain\n", __func__); + return -EINVAL; + } + + return devm_regmap_add_irq_chip(dev, wcd->regmap, + irq_create_mapping(wcd->virq, 0), + IRQF_ONESHOT, 0, &wcd938x_regmap_irq_chip, + &wcd->irq_chip); +} + +static int wcd938x_soc_codec_probe(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + struct sdw_slave *tx_sdw_dev = wcd938x->tx_sdw_dev; + struct device *dev = component->dev; + unsigned long time_left; + int ret, i; + + time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, + msecs_to_jiffies(2000)); + if (!time_left) { + dev_err(dev, "soundwire device init timeout\n"); + return -ETIMEDOUT; + } + + snd_soc_component_init_regmap(component, wcd938x->regmap); + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + wcd938x->variant = snd_soc_component_read_field(component, + WCD938X_DIGITAL_EFUSE_REG_0, + WCD938X_ID_MASK); + + wcd938x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD938X); + if (IS_ERR(wcd938x->clsh_info)) { + pm_runtime_put(dev); + return PTR_ERR(wcd938x->clsh_info); + } + + wcd938x_io_init(wcd938x); + /* Set all interrupts as edge triggered */ + for (i = 0; i < wcd938x_regmap_irq_chip.num_regs; i++) { + regmap_write(wcd938x->regmap, + (WCD938X_DIGITAL_INTR_LEVEL_0 + i), 0); + } + + pm_runtime_put(dev); + + wcd938x->hphr_pdm_wd_int = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_HPHR_PDM_WD_INT); + wcd938x->hphl_pdm_wd_int = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_HPHL_PDM_WD_INT); + wcd938x->aux_pdm_wd_int = regmap_irq_get_virq(wcd938x->irq_chip, + WCD938X_IRQ_AUX_PDM_WD_INT); + + /* Request for watchdog interrupt */ + ret = request_threaded_irq(wcd938x->hphr_pdm_wd_int, NULL, wcd938x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHR PDM WD INT", wcd938x); + if (ret) { + dev_err(dev, "Failed to request HPHR WD interrupt (%d)\n", ret); + goto err_free_clsh_ctrl; + } + + ret = request_threaded_irq(wcd938x->hphl_pdm_wd_int, NULL, wcd938x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "HPHL PDM WD INT", wcd938x); + if (ret) { + dev_err(dev, "Failed to request HPHL WD interrupt (%d)\n", ret); + goto err_free_hphr_pdm_wd_int; + } + + ret = request_threaded_irq(wcd938x->aux_pdm_wd_int, NULL, wcd938x_wd_handle_irq, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "AUX PDM WD INT", wcd938x); + if (ret) { + dev_err(dev, "Failed to request Aux WD interrupt (%d)\n", ret); + goto err_free_hphl_pdm_wd_int; + } + + /* Disable watchdog interrupt for HPH and AUX */ + disable_irq_nosync(wcd938x->hphr_pdm_wd_int); + disable_irq_nosync(wcd938x->hphl_pdm_wd_int); + disable_irq_nosync(wcd938x->aux_pdm_wd_int); + + switch (wcd938x->variant) { + case WCD9380: + ret = snd_soc_add_component_controls(component, wcd9380_snd_controls, + ARRAY_SIZE(wcd9380_snd_controls)); + if (ret < 0) { + dev_err(component->dev, + "%s: Failed to add snd ctrls for variant: %d\n", + __func__, wcd938x->variant); + goto err_free_aux_pdm_wd_int; + } + break; + case WCD9385: + ret = snd_soc_add_component_controls(component, wcd9385_snd_controls, + ARRAY_SIZE(wcd9385_snd_controls)); + if (ret < 0) { + dev_err(component->dev, + "%s: Failed to add snd ctrls for variant: %d\n", + __func__, wcd938x->variant); + goto err_free_aux_pdm_wd_int; + } + break; + default: + break; + } + + ret = wcd938x_mbhc_init(component); + if (ret) { + dev_err(component->dev, "mbhc initialization failed\n"); + goto err_free_aux_pdm_wd_int; + } + + return 0; + +err_free_aux_pdm_wd_int: + free_irq(wcd938x->aux_pdm_wd_int, wcd938x); +err_free_hphl_pdm_wd_int: + free_irq(wcd938x->hphl_pdm_wd_int, wcd938x); +err_free_hphr_pdm_wd_int: + free_irq(wcd938x->hphr_pdm_wd_int, wcd938x); +err_free_clsh_ctrl: + wcd_clsh_ctrl_free(wcd938x->clsh_info); + + return ret; +} + +static void wcd938x_soc_codec_remove(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + wcd938x_mbhc_deinit(component); + + free_irq(wcd938x->aux_pdm_wd_int, wcd938x); + free_irq(wcd938x->hphl_pdm_wd_int, wcd938x); + free_irq(wcd938x->hphr_pdm_wd_int, wcd938x); + + wcd_clsh_ctrl_free(wcd938x->clsh_info); +} + +static int wcd938x_codec_set_jack(struct snd_soc_component *comp, + struct snd_soc_jack *jack, void *data) +{ + struct wcd938x_priv *wcd = dev_get_drvdata(comp->dev); + + if (jack) + return wcd_mbhc_start(wcd->wcd_mbhc, &wcd->mbhc_cfg, jack); + else + wcd_mbhc_stop(wcd->wcd_mbhc); + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_wcd938x = { + .name = "wcd938x_codec", + .probe = wcd938x_soc_codec_probe, + .remove = wcd938x_soc_codec_remove, + .controls = wcd938x_snd_controls, + .num_controls = ARRAY_SIZE(wcd938x_snd_controls), + .dapm_widgets = wcd938x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wcd938x_dapm_widgets), + .dapm_routes = wcd938x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wcd938x_audio_map), + .set_jack = wcd938x_codec_set_jack, + .endianness = 1, +}; + +static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_priv *wcd) +{ + struct device_node *np = dev->of_node; + u32 prop_val = 0; + int rc = 0; + + rc = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val); + if (!rc) + wcd->micb1_mv = prop_val/1000; + else + dev_info(dev, "%s: Micbias1 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val); + if (!rc) + wcd->micb2_mv = prop_val/1000; + else + dev_info(dev, "%s: Micbias2 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val); + if (!rc) + wcd->micb3_mv = prop_val/1000; + else + dev_info(dev, "%s: Micbias3 DT property not found\n", __func__); + + rc = of_property_read_u32(np, "qcom,micbias4-microvolt", &prop_val); + if (!rc) + wcd->micb4_mv = prop_val/1000; + else + dev_info(dev, "%s: Micbias4 DT property not found\n", __func__); +} + +static bool wcd938x_swap_gnd_mic(struct snd_soc_component *component, bool active) +{ + int value; + + struct wcd938x_priv *wcd938x; + + wcd938x = snd_soc_component_get_drvdata(component); + + value = gpiod_get_value(wcd938x->us_euro_gpio); + + gpiod_set_value(wcd938x->us_euro_gpio, !value); + + return true; +} + + +static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device *dev) +{ + struct wcd_mbhc_config *cfg = &wcd938x->mbhc_cfg; + int ret; + + wcd938x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0); + if (wcd938x->reset_gpio < 0) + return dev_err_probe(dev, wcd938x->reset_gpio, + "Failed to get reset gpio\n"); + + wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro", + GPIOD_OUT_LOW); + if (IS_ERR(wcd938x->us_euro_gpio)) + return dev_err_probe(dev, PTR_ERR(wcd938x->us_euro_gpio), + "us-euro swap Control GPIO not found\n"); + + cfg->swap_gnd_mic = wcd938x_swap_gnd_mic; + + wcd938x->supplies[0].supply = "vdd-rxtx"; + wcd938x->supplies[1].supply = "vdd-io"; + wcd938x->supplies[2].supply = "vdd-buck"; + wcd938x->supplies[3].supply = "vdd-mic-bias"; + + ret = regulator_bulk_get(dev, WCD938X_MAX_SUPPLY, wcd938x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get supplies\n"); + + ret = regulator_bulk_enable(WCD938X_MAX_SUPPLY, wcd938x->supplies); + if (ret) { + regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies); + return dev_err_probe(dev, ret, "Failed to enable supplies\n"); + } + + wcd938x_dt_parse_micbias_info(dev, wcd938x); + + cfg->mbhc_micbias = MIC_BIAS_2; + cfg->anc_micbias = MIC_BIAS_2; + cfg->v_hs_max = WCD_MBHC_HS_V_MAX; + cfg->num_btn = WCD938X_MBHC_MAX_BUTTONS; + cfg->micb_mv = wcd938x->micb2_mv; + cfg->linein_th = 5000; + cfg->hs_thr = 1700; + cfg->hph_thr = 50; + + wcd_dt_parse_mbhc_data(dev, cfg); + + return 0; +} + +static int wcd938x_reset(struct wcd938x_priv *wcd938x) +{ + gpio_direction_output(wcd938x->reset_gpio, 0); + /* 20us sleep required after pulling the reset gpio to LOW */ + usleep_range(20, 30); + gpio_set_value(wcd938x->reset_gpio, 1); + /* 20us sleep required after pulling the reset gpio to HIGH */ + usleep_range(20, 30); + + return 0; +} + +static int wcd938x_codec_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wcd938x_priv *wcd938x = dev_get_drvdata(dai->dev); + struct wcd938x_sdw_priv *wcd = wcd938x->sdw_priv[dai->id]; + + return wcd938x_sdw_hw_params(wcd, substream, params, dai); +} + +static int wcd938x_codec_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wcd938x_priv *wcd938x = dev_get_drvdata(dai->dev); + struct wcd938x_sdw_priv *wcd = wcd938x->sdw_priv[dai->id]; + + return wcd938x_sdw_free(wcd, substream, dai); +} + +static int wcd938x_codec_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wcd938x_priv *wcd938x = dev_get_drvdata(dai->dev); + struct wcd938x_sdw_priv *wcd = wcd938x->sdw_priv[dai->id]; + + return wcd938x_sdw_set_sdw_stream(wcd, dai, stream, direction); + +} + +static const struct snd_soc_dai_ops wcd938x_sdw_dai_ops = { + .hw_params = wcd938x_codec_hw_params, + .hw_free = wcd938x_codec_free, + .set_stream = wcd938x_codec_set_sdw_stream, +}; + +static struct snd_soc_dai_driver wcd938x_dais[] = { + [0] = { + .name = "wcd938x-sdw-rx", + .playback = { + .stream_name = "WCD AIF1 Playback", + .rates = WCD938X_RATES_MASK | WCD938X_FRAC_RATES_MASK, + .formats = WCD938X_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &wcd938x_sdw_dai_ops, + }, + [1] = { + .name = "wcd938x-sdw-tx", + .capture = { + .stream_name = "WCD AIF1 Capture", + .rates = WCD938X_RATES_MASK, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &wcd938x_sdw_dai_ops, + }, +}; + +static int wcd938x_bind(struct device *dev) +{ + struct wcd938x_priv *wcd938x = dev_get_drvdata(dev); + int ret; + + ret = component_bind_all(dev, wcd938x); + if (ret) { + dev_err(dev, "%s: Slave bind failed, ret = %d\n", + __func__, ret); + return ret; + } + + wcd938x->rxdev = wcd938x_sdw_device_get(wcd938x->rxnode); + if (!wcd938x->rxdev) { + dev_err(dev, "could not find slave with matching of node\n"); + ret = -EINVAL; + goto err_unbind; + } + wcd938x->sdw_priv[AIF1_PB] = dev_get_drvdata(wcd938x->rxdev); + wcd938x->sdw_priv[AIF1_PB]->wcd938x = wcd938x; + + wcd938x->txdev = wcd938x_sdw_device_get(wcd938x->txnode); + if (!wcd938x->txdev) { + dev_err(dev, "could not find txslave with matching of node\n"); + ret = -EINVAL; + goto err_put_rxdev; + } + wcd938x->sdw_priv[AIF1_CAP] = dev_get_drvdata(wcd938x->txdev); + wcd938x->sdw_priv[AIF1_CAP]->wcd938x = wcd938x; + wcd938x->tx_sdw_dev = dev_to_sdw_dev(wcd938x->txdev); + + /* As TX is main CSR reg interface, which should not be suspended first. + * expicilty add the dependency link */ + if (!device_link_add(wcd938x->rxdev, wcd938x->txdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink tx and rx\n"); + ret = -EINVAL; + goto err_put_txdev; + } + + if (!device_link_add(dev, wcd938x->txdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink wcd and tx\n"); + ret = -EINVAL; + goto err_remove_rxtx_link; + } + + if (!device_link_add(dev, wcd938x->rxdev, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME)) { + dev_err(dev, "could not devlink wcd and rx\n"); + ret = -EINVAL; + goto err_remove_tx_link; + } + + wcd938x->regmap = dev_get_regmap(&wcd938x->tx_sdw_dev->dev, NULL); + if (!wcd938x->regmap) { + dev_err(dev, "could not get TX device regmap\n"); + ret = -EINVAL; + goto err_remove_rx_link; + } + + ret = wcd938x_irq_init(wcd938x, dev); + if (ret) { + dev_err(dev, "%s: IRQ init failed: %d\n", __func__, ret); + goto err_remove_rx_link; + } + + wcd938x->sdw_priv[AIF1_PB]->slave_irq = wcd938x->virq; + wcd938x->sdw_priv[AIF1_CAP]->slave_irq = wcd938x->virq; + + ret = wcd938x_set_micbias_data(wcd938x); + if (ret < 0) { + dev_err(dev, "%s: bad micbias pdata\n", __func__); + goto err_remove_rx_link; + } + + ret = snd_soc_register_component(dev, &soc_codec_dev_wcd938x, + wcd938x_dais, ARRAY_SIZE(wcd938x_dais)); + if (ret) { + dev_err(dev, "%s: Codec registration failed\n", + __func__); + goto err_remove_rx_link; + } + + return 0; + +err_remove_rx_link: + device_link_remove(dev, wcd938x->rxdev); +err_remove_tx_link: + device_link_remove(dev, wcd938x->txdev); +err_remove_rxtx_link: + device_link_remove(wcd938x->rxdev, wcd938x->txdev); +err_put_txdev: + put_device(wcd938x->txdev); +err_put_rxdev: + put_device(wcd938x->rxdev); +err_unbind: + component_unbind_all(dev, wcd938x); + + return ret; +} + +static void wcd938x_unbind(struct device *dev) +{ + struct wcd938x_priv *wcd938x = dev_get_drvdata(dev); + + snd_soc_unregister_component(dev); + device_link_remove(dev, wcd938x->txdev); + device_link_remove(dev, wcd938x->rxdev); + device_link_remove(wcd938x->rxdev, wcd938x->txdev); + put_device(wcd938x->txdev); + put_device(wcd938x->rxdev); + component_unbind_all(dev, wcd938x); +} + +static const struct component_master_ops wcd938x_comp_ops = { + .bind = wcd938x_bind, + .unbind = wcd938x_unbind, +}; + +static int wcd938x_add_slave_components(struct wcd938x_priv *wcd938x, + struct device *dev, + struct component_match **matchptr) +{ + struct device_node *np; + + np = dev->of_node; + + wcd938x->rxnode = of_parse_phandle(np, "qcom,rx-device", 0); + if (!wcd938x->rxnode) { + dev_err(dev, "%s: Rx-device node not defined\n", __func__); + return -ENODEV; + } + + of_node_get(wcd938x->rxnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd938x->rxnode); + + wcd938x->txnode = of_parse_phandle(np, "qcom,tx-device", 0); + if (!wcd938x->txnode) { + dev_err(dev, "%s: Tx-device node not defined\n", __func__); + return -ENODEV; + } + of_node_get(wcd938x->txnode); + component_match_add_release(dev, matchptr, component_release_of, + component_compare_of, wcd938x->txnode); + return 0; +} + +static int wcd938x_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + struct wcd938x_priv *wcd938x = NULL; + struct device *dev = &pdev->dev; + int ret; + + wcd938x = devm_kzalloc(dev, sizeof(struct wcd938x_priv), + GFP_KERNEL); + if (!wcd938x) + return -ENOMEM; + + dev_set_drvdata(dev, wcd938x); + mutex_init(&wcd938x->micb_lock); + + ret = wcd938x_populate_dt_data(wcd938x, dev); + if (ret) { + dev_err(dev, "%s: Fail to obtain platform data\n", __func__); + return -EINVAL; + } + + ret = wcd938x_add_slave_components(wcd938x, dev, &match); + if (ret) + goto err_disable_regulators; + + wcd938x_reset(wcd938x); + + ret = component_master_add_with_match(dev, &wcd938x_comp_ops, match); + if (ret) + goto err_disable_regulators; + + pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_disable_regulators: + regulator_bulk_disable(WCD938X_MAX_SUPPLY, wcd938x->supplies); + regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies); + + return ret; +} + +static void wcd938x_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wcd938x_priv *wcd938x = dev_get_drvdata(dev); + + component_master_del(dev, &wcd938x_comp_ops); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_dont_use_autosuspend(dev); + + regulator_bulk_disable(WCD938X_MAX_SUPPLY, wcd938x->supplies); + regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies); +} + +#if defined(CONFIG_OF) +static const struct of_device_id wcd938x_dt_match[] = { + { .compatible = "qcom,wcd9380-codec" }, + { .compatible = "qcom,wcd9385-codec" }, + {} +}; +MODULE_DEVICE_TABLE(of, wcd938x_dt_match); +#endif + +static struct platform_driver wcd938x_codec_driver = { + .probe = wcd938x_probe, + .remove_new = wcd938x_remove, + .driver = { + .name = "wcd938x_codec", + .of_match_table = of_match_ptr(wcd938x_dt_match), + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(wcd938x_codec_driver); +MODULE_DESCRIPTION("WCD938X Codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wcd938x.h b/sound/soc/codecs/wcd938x.h new file mode 100644 index 0000000000..74b1498fec --- /dev/null +++ b/sound/soc/codecs/wcd938x.h @@ -0,0 +1,718 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __WCD938X_H__ +#define __WCD938X_H__ +#include +#include + +#define WCD938X_BASE_ADDRESS (0x3000) +#define WCD938X_ANA_PAGE_REGISTER (0x3000) +#define WCD938X_ANA_BIAS (0x3001) +#define WCD938X_ANA_RX_SUPPLIES (0x3008) +#define WCD938X_RX_BIAS_EN_MASK BIT(0) +#define WCD938X_REGULATOR_MODE_MASK BIT(1) +#define WCD938X_REGULATOR_MODE_CLASS_AB 1 +#define WCD938X_VNEG_EN_MASK BIT(6) +#define WCD938X_VPOS_EN_MASK BIT(7) +#define WCD938X_ANA_HPH (0x3009) +#define WCD938X_HPHR_REF_EN_MASK BIT(4) +#define WCD938X_HPHL_REF_EN_MASK BIT(5) +#define WCD938X_HPHR_EN_MASK BIT(6) +#define WCD938X_HPHL_EN_MASK BIT(7) +#define WCD938X_ANA_EAR (0x300A) +#define WCD938X_ANA_EAR_COMPANDER_CTL (0x300B) +#define WCD938X_GAIN_OVRD_REG_MASK BIT(7) +#define WCD938X_EAR_GAIN_MASK GENMASK(6, 2) +#define WCD938X_ANA_TX_CH1 (0x300E) +#define WCD938X_ANA_TX_CH2 (0x300F) +#define WCD938X_HPF1_INIT_MASK BIT(6) +#define WCD938X_HPF2_INIT_MASK BIT(5) +#define WCD938X_ANA_TX_CH3 (0x3010) +#define WCD938X_ANA_TX_CH4 (0x3011) +#define WCD938X_HPF3_INIT_MASK BIT(6) +#define WCD938X_HPF4_INIT_MASK BIT(5) +#define WCD938X_ANA_MICB1_MICB2_DSP_EN_LOGIC (0x3012) +#define WCD938X_ANA_MICB3_DSP_EN_LOGIC (0x3013) +#define WCD938X_ANA_MBHC_MECH (0x3014) +#define WCD938X_MBHC_L_DET_EN_MASK BIT(7) +#define WCD938X_MBHC_L_DET_EN BIT(7) +#define WCD938X_MBHC_GND_DET_EN_MASK BIT(6) +#define WCD938X_MBHC_MECH_DETECT_TYPE_MASK BIT(5) +#define WCD938X_MBHC_MECH_DETECT_TYPE_INS 1 +#define WCD938X_MBHC_HPHL_PLUG_TYPE_MASK BIT(4) +#define WCD938X_MBHC_HPHL_PLUG_TYPE_NO 1 +#define WCD938X_MBHC_GND_PLUG_TYPE_MASK BIT(3) +#define WCD938X_MBHC_GND_PLUG_TYPE_NO 1 +#define WCD938X_MBHC_HSL_PULLUP_COMP_EN BIT(2) +#define WCD938X_MBHC_HSG_PULLUP_COMP_EN BIT(1) +#define WCD938X_MBHC_HPHL_100K_TO_GND_EN BIT(0) + +#define WCD938X_ANA_MBHC_ELECT (0x3015) +#define WCD938X_ANA_MBHC_BD_ISRC_CTL_MASK GENMASK(6, 4) +#define WCD938X_ANA_MBHC_BD_ISRC_100UA GENMASK(5, 4) +#define WCD938X_ANA_MBHC_BD_ISRC_OFF 0 +#define WCD938X_ANA_MBHC_BIAS_EN_MASK BIT(0) +#define WCD938X_ANA_MBHC_BIAS_EN BIT(0) +#define WCD938X_ANA_MBHC_ZDET (0x3016) +#define WCD938X_ANA_MBHC_RESULT_1 (0x3017) +#define WCD938X_ANA_MBHC_RESULT_2 (0x3018) +#define WCD938X_ANA_MBHC_RESULT_3 (0x3019) +#define WCD938X_MBHC_BTN_RESULT_MASK GENMASK(2, 0) +#define WCD938X_ANA_MBHC_BTN0 (0x301A) +#define WCD938X_MBHC_BTN_VTH_MASK GENMASK(7, 2) +#define WCD938X_ANA_MBHC_BTN1 (0x301B) +#define WCD938X_ANA_MBHC_BTN2 (0x301C) +#define WCD938X_ANA_MBHC_BTN3 (0x301D) +#define WCD938X_ANA_MBHC_BTN4 (0x301E) +#define WCD938X_ANA_MBHC_BTN5 (0x301F) +#define WCD938X_VTH_MASK GENMASK(7, 2) +#define WCD938X_ANA_MBHC_BTN6 (0x3020) +#define WCD938X_ANA_MBHC_BTN7 (0x3021) +#define WCD938X_ANA_MICB1 (0x3022) +#define WCD938X_MICB_VOUT_MASK GENMASK(5, 0) +#define WCD938X_MICB_EN_MASK GENMASK(7, 6) +#define WCD938X_MICB_DISABLE 0 +#define WCD938X_MICB_ENABLE 1 +#define WCD938X_MICB_PULL_UP 2 +#define WCD938X_MICB_PULL_DOWN 3 +#define WCD938X_ANA_MICB2 (0x3023) +#define WCD938X_ANA_MICB2_ENABLE BIT(6) +#define WCD938X_ANA_MICB2_ENABLE_MASK GENMASK(7, 6) +#define WCD938X_ANA_MICB2_VOUT_MASK GENMASK(5, 0) +#define WCD938X_ANA_MICB2_RAMP (0x3024) +#define WCD938X_RAMP_EN_MASK BIT(7) +#define WCD938X_RAMP_SHIFT_CTRL_MASK GENMASK(4, 2) +#define WCD938X_ANA_MICB3 (0x3025) +#define WCD938X_ANA_MICB4 (0x3026) +#define WCD938X_BIAS_CTL (0x3028) +#define WCD938X_BIAS_VBG_FINE_ADJ (0x3029) +#define WCD938X_LDOL_VDDCX_ADJUST (0x3040) +#define WCD938X_LDOL_DISABLE_LDOL (0x3041) +#define WCD938X_MBHC_CTL_CLK (0x3056) +#define WCD938X_MBHC_CTL_ANA (0x3057) +#define WCD938X_MBHC_CTL_SPARE_1 (0x3058) +#define WCD938X_MBHC_CTL_SPARE_2 (0x3059) +#define WCD938X_MBHC_CTL_BCS (0x305A) +#define WCD938X_MBHC_MOISTURE_DET_FSM_STATUS (0x305B) +#define WCD938X_MBHC_TEST_CTL (0x305C) +#define WCD938X_LDOH_MODE (0x3067) +#define WCD938X_LDOH_EN_MASK BIT(7) +#define WCD938X_LDOH_BIAS (0x3068) +#define WCD938X_LDOH_STB_LOADS (0x3069) +#define WCD938X_LDOH_SLOWRAMP (0x306A) +#define WCD938X_MICB1_TEST_CTL_1 (0x306B) +#define WCD938X_MICB1_TEST_CTL_2 (0x306C) +#define WCD938X_MICB1_TEST_CTL_3 (0x306D) +#define WCD938X_MICB2_TEST_CTL_1 (0x306E) +#define WCD938X_MICB2_TEST_CTL_2 (0x306F) +#define WCD938X_MICB2_TEST_CTL_3 (0x3070) +#define WCD938X_MICB3_TEST_CTL_1 (0x3071) +#define WCD938X_MICB3_TEST_CTL_2 (0x3072) +#define WCD938X_MICB3_TEST_CTL_3 (0x3073) +#define WCD938X_MICB4_TEST_CTL_1 (0x3074) +#define WCD938X_MICB4_TEST_CTL_2 (0x3075) +#define WCD938X_MICB4_TEST_CTL_3 (0x3076) +#define WCD938X_TX_COM_ADC_VCM (0x3077) +#define WCD938X_TX_COM_BIAS_ATEST (0x3078) +#define WCD938X_TX_COM_SPARE1 (0x3079) +#define WCD938X_TX_COM_SPARE2 (0x307A) +#define WCD938X_TX_COM_TXFE_DIV_CTL (0x307B) +#define WCD938X_TX_COM_TXFE_DIV_START (0x307C) +#define WCD938X_TX_COM_SPARE3 (0x307D) +#define WCD938X_TX_COM_SPARE4 (0x307E) +#define WCD938X_TX_1_2_TEST_EN (0x307F) +#define WCD938X_TX_1_2_ADC_IB (0x3080) +#define WCD938X_TX_1_2_ATEST_REFCTL (0x3081) +#define WCD938X_TX_1_2_TEST_CTL (0x3082) +#define WCD938X_TX_1_2_TEST_BLK_EN1 (0x3083) +#define WCD938X_TX_1_2_TXFE1_CLKDIV (0x3084) +#define WCD938X_TX_1_2_SAR2_ERR (0x3085) +#define WCD938X_TX_1_2_SAR1_ERR (0x3086) +#define WCD938X_TX_3_4_TEST_EN (0x3087) +#define WCD938X_TX_3_4_ADC_IB (0x3088) +#define WCD938X_TX_3_4_ATEST_REFCTL (0x3089) +#define WCD938X_TX_3_4_TEST_CTL (0x308A) +#define WCD938X_TX_3_4_TEST_BLK_EN3 (0x308B) +#define WCD938X_TX_3_4_TXFE3_CLKDIV (0x308C) +#define WCD938X_TX_3_4_SAR4_ERR (0x308D) +#define WCD938X_TX_3_4_SAR3_ERR (0x308E) +#define WCD938X_TX_3_4_TEST_BLK_EN2 (0x308F) +#define WCD938X_TX_3_4_TXFE2_CLKDIV (0x3090) +#define WCD938X_TX_3_4_SPARE1 (0x3091) +#define WCD938X_TX_3_4_TEST_BLK_EN4 (0x3092) +#define WCD938X_TX_3_4_TXFE4_CLKDIV (0x3093) +#define WCD938X_TX_3_4_SPARE2 (0x3094) +#define WCD938X_CLASSH_MODE_1 (0x3097) +#define WCD938X_CLASSH_MODE_2 (0x3098) +#define WCD938X_CLASSH_MODE_3 (0x3099) +#define WCD938X_CLASSH_CTRL_VCL_1 (0x309A) +#define WCD938X_CLASSH_CTRL_VCL_2 (0x309B) +#define WCD938X_CLASSH_CTRL_CCL_1 (0x309C) +#define WCD938X_CLASSH_CTRL_CCL_2 (0x309D) +#define WCD938X_CLASSH_CTRL_CCL_3 (0x309E) +#define WCD938X_CLASSH_CTRL_CCL_4 (0x309F) +#define WCD938X_CLASSH_CTRL_CCL_5 (0x30A0) +#define WCD938X_CLASSH_BUCK_TMUX_A_D (0x30A1) +#define WCD938X_CLASSH_BUCK_SW_DRV_CNTL (0x30A2) +#define WCD938X_CLASSH_SPARE (0x30A3) +#define WCD938X_FLYBACK_EN (0x30A4) +#define WCD938X_EN_CUR_DET_MASK BIT(2) +#define WCD938X_FLYBACK_VNEG_CTRL_1 (0x30A5) +#define WCD938X_FLYBACK_VNEG_CTRL_2 (0x30A6) +#define WCD938X_FLYBACK_VNEG_CTRL_3 (0x30A7) +#define WCD938X_FLYBACK_VNEG_CTRL_4 (0x30A8) +#define WCD938X_FLYBACK_VNEG_CTRL_5 (0x30A9) +#define WCD938X_FLYBACK_VNEG_CTRL_6 (0x30AA) +#define WCD938X_FLYBACK_VNEG_CTRL_7 (0x30AB) +#define WCD938X_FLYBACK_VNEG_CTRL_8 (0x30AC) +#define WCD938X_FLYBACK_VNEG_CTRL_9 (0x30AD) +#define WCD938X_FLYBACK_VNEGDAC_CTRL_1 (0x30AE) +#define WCD938X_FLYBACK_VNEGDAC_CTRL_2 (0x30AF) +#define WCD938X_FLYBACK_VNEGDAC_CTRL_3 (0x30B0) +#define WCD938X_FLYBACK_CTRL_1 (0x30B1) +#define WCD938X_FLYBACK_TEST_CTL (0x30B2) +#define WCD938X_RX_AUX_SW_CTL (0x30B3) +#define WCD938X_RX_PA_AUX_IN_CONN (0x30B4) +#define WCD938X_RX_TIMER_DIV (0x30B5) +#define WCD938X_RX_OCP_CTL (0x30B6) +#define WCD938X_RX_OCP_COUNT (0x30B7) +#define WCD938X_RX_BIAS_EAR_DAC (0x30B8) +#define WCD938X_RX_BIAS_EAR_AMP (0x30B9) +#define WCD938X_RX_BIAS_HPH_LDO (0x30BA) +#define WCD938X_RX_BIAS_HPH_PA (0x30BB) +#define WCD938X_RX_BIAS_HPH_RDACBUFF_CNP2 (0x30BC) +#define WCD938X_RX_BIAS_HPH_RDAC_LDO (0x30BD) +#define WCD938X_RX_BIAS_HPH_CNP1 (0x30BE) +#define WCD938X_RX_BIAS_HPH_LOWPOWER (0x30BF) +#define WCD938X_RX_BIAS_AUX_DAC (0x30C0) +#define WCD938X_RX_BIAS_AUX_AMP (0x30C1) +#define WCD938X_RX_BIAS_VNEGDAC_BLEEDER (0x30C2) +#define WCD938X_RX_BIAS_MISC (0x30C3) +#define WCD938X_RX_BIAS_BUCK_RST (0x30C4) +#define WCD938X_RX_BIAS_BUCK_VREF_ERRAMP (0x30C5) +#define WCD938X_RX_BIAS_FLYB_ERRAMP (0x30C6) +#define WCD938X_RX_BIAS_FLYB_BUFF (0x30C7) +#define WCD938X_RX_BIAS_FLYB_MID_RST (0x30C8) +#define WCD938X_HPH_L_STATUS (0x30C9) +#define WCD938X_HPH_R_STATUS (0x30CA) +#define WCD938X_HPH_CNP_EN (0x30CB) +#define WCD938X_HPH_CNP_WG_CTL (0x30CC) +#define WCD938X_HPH_CNP_WG_TIME (0x30CD) +#define WCD938X_HPH_OCP_CTL (0x30CE) +#define WCD938X_HPH_AUTO_CHOP (0x30CF) +#define WCD938X_HPH_CHOP_CTL (0x30D0) +#define WCD938X_HPH_PA_CTL1 (0x30D1) +#define WCD938X_HPH_PA_CTL2 (0x30D2) +#define WCD938X_HPHPA_GND_R_MASK BIT(6) +#define WCD938X_HPHPA_GND_L_MASK BIT(4) +#define WCD938X_HPH_L_EN (0x30D3) +#define WCD938X_HPH_L_TEST (0x30D4) +#define WCD938X_HPH_L_ATEST (0x30D5) +#define WCD938X_HPH_R_EN (0x30D6) +#define WCD938X_GAIN_SRC_SEL_MASK BIT(5) +#define WCD938X_GAIN_SRC_SEL_REGISTER 1 +#define WCD938X_HPH_R_TEST (0x30D7) +#define WCD938X_HPH_R_ATEST (0x30D8) +#define WCD938X_HPHPA_GND_OVR_MASK BIT(1) +#define WCD938X_HPH_RDAC_CLK_CTL1 (0x30D9) +#define WCD938X_CHOP_CLK_EN_MASK BIT(7) +#define WCD938X_HPH_RDAC_CLK_CTL2 (0x30DA) +#define WCD938X_HPH_RDAC_LDO_CTL (0x30DB) +#define WCD938X_HPH_RDAC_CHOP_CLK_LP_CTL (0x30DC) +#define WCD938X_HPH_REFBUFF_UHQA_CTL (0x30DD) +#define WCD938X_HPH_REFBUFF_LP_CTL (0x30DE) +#define WCD938X_PREREF_FLIT_BYPASS_MASK BIT(0) +#define WCD938X_HPH_L_DAC_CTL (0x30DF) +#define WCD938X_HPH_R_DAC_CTL (0x30E0) +#define WCD938X_HPH_SURGE_HPHLR_SURGE_COMP_SEL (0x30E1) +#define WCD938X_HPH_SURGE_HPHLR_SURGE_EN (0x30E2) +#define WCD938X_HPH_SURGE_HPHLR_SURGE_MISC1 (0x30E3) +#define WCD938X_HPH_SURGE_HPHLR_SURGE_STATUS (0x30E4) +#define WCD938X_EAR_EAR_EN_REG (0x30E9) +#define WCD938X_EAR_EAR_PA_CON (0x30EA) +#define WCD938X_EAR_EAR_SP_CON (0x30EB) +#define WCD938X_EAR_EAR_DAC_CON (0x30EC) +#define WCD938X_DAC_SAMPLE_EDGE_SEL_MASK BIT(7) +#define WCD938X_EAR_EAR_CNP_FSM_CON (0x30ED) +#define WCD938X_EAR_TEST_CTL (0x30EE) +#define WCD938X_EAR_STATUS_REG_1 (0x30EF) +#define WCD938X_EAR_STATUS_REG_2 (0x30F0) +#define WCD938X_ANA_NEW_PAGE_REGISTER (0x3100) +#define WCD938X_HPH_NEW_ANA_HPH2 (0x3101) +#define WCD938X_HPH_NEW_ANA_HPH3 (0x3102) +#define WCD938X_SLEEP_CTL (0x3103) +#define WCD938X_SLEEP_WATCHDOG_CTL (0x3104) +#define WCD938X_MBHC_NEW_ELECT_REM_CLAMP_CTL (0x311F) +#define WCD938X_MBHC_NEW_CTL_1 (0x3120) +#define WCD938X_MBHC_CTL_RCO_EN_MASK BIT(7) +#define WCD938X_MBHC_CTL_RCO_EN BIT(7) +#define WCD938X_MBHC_BTN_DBNC_MASK GENMASK(1, 0) +#define WCD938X_MBHC_BTN_DBNC_T_16_MS 0x2 +#define WCD938X_MBHC_NEW_CTL_2 (0x3121) +#define WCD938X_M_RTH_CTL_MASK GENMASK(3, 2) +#define WCD938X_MBHC_HS_VREF_CTL_MASK GENMASK(1, 0) +#define WCD938X_MBHC_HS_VREF_1P5_V 0x1 +#define WCD938X_MBHC_NEW_PLUG_DETECT_CTL (0x3122) +#define WCD938X_MBHC_DBNC_TIMER_INSREM_DBNC_T_96_MS 0x6 + +#define WCD938X_MBHC_NEW_ZDET_ANA_CTL (0x3123) +#define WCD938X_ZDET_RANGE_CTL_MASK GENMASK(3, 0) +#define WCD938X_ZDET_MAXV_CTL_MASK GENMASK(6, 4) +#define WCD938X_MBHC_NEW_ZDET_RAMP_CTL (0x3124) +#define WCD938X_MBHC_NEW_FSM_STATUS (0x3125) +#define WCD938X_MBHC_NEW_ADC_RESULT (0x3126) +#define WCD938X_TX_NEW_AMIC_MUX_CFG (0x3127) +#define WCD938X_AUX_AUXPA (0x3128) +#define WCD938X_AUXPA_CLK_EN_MASK BIT(4) +#define WCD938X_LDORXTX_MODE (0x3129) +#define WCD938X_LDORXTX_CONFIG (0x312A) +#define WCD938X_DIE_CRACK_DIE_CRK_DET_EN (0x312C) +#define WCD938X_DIE_CRACK_DIE_CRK_DET_OUT (0x312D) +#define WCD938X_HPH_NEW_INT_RDAC_GAIN_CTL (0x3132) +#define WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L (0x3133) +#define WCD938X_HPH_NEW_INT_RDAC_VREF_CTL (0x3134) +#define WCD938X_HPH_NEW_INT_RDAC_OVERRIDE_CTL (0x3135) +#define WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R (0x3136) +#define WCD938X_HPH_RES_DIV_MASK GENMASK(4, 0) +#define WCD938X_HPH_NEW_INT_PA_MISC1 (0x3137) +#define WCD938X_HPH_NEW_INT_PA_MISC2 (0x3138) +#define WCD938X_HPH_NEW_INT_PA_RDAC_MISC (0x3139) +#define WCD938X_HPH_NEW_INT_HPH_TIMER1 (0x313A) +#define WCD938X_AUTOCHOP_TIMER_EN BIT(1) +#define WCD938X_HPH_NEW_INT_HPH_TIMER2 (0x313B) +#define WCD938X_HPH_NEW_INT_HPH_TIMER3 (0x313C) +#define WCD938X_HPH_NEW_INT_HPH_TIMER4 (0x313D) +#define WCD938X_HPH_NEW_INT_PA_RDAC_MISC2 (0x313E) +#define WCD938X_HPH_NEW_INT_PA_RDAC_MISC3 (0x313F) +#define WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_L_NEW (0x3140) +#define WCD938X_HPH_NEW_INT_RDAC_HD2_CTL_R_NEW (0x3141) +#define WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI (0x3145) +#define WCD938X_RX_NEW_INT_HPH_RDAC_BIAS_ULP (0x3146) +#define WCD938X_RX_NEW_INT_HPH_RDAC_LDO_LP (0x3147) +#define WCD938X_MBHC_NEW_INT_MOISTURE_DET_DC_CTRL (0x31AF) +#define WCD938X_MBHC_NEW_INT_MOISTURE_DET_POLLING_CTRL (0x31B0) +#define WCD938X_MOISTURE_EN_POLLING_MASK BIT(2) +#define WCD938X_MBHC_NEW_INT_MECH_DET_CURRENT (0x31B1) +#define WCD938X_HSDET_PULLUP_C_MASK GENMASK(4, 0) +#define WCD938X_MBHC_NEW_INT_SPARE_2 (0x31B2) +#define WCD938X_EAR_INT_NEW_EAR_CHOPPER_CON (0x31B7) +#define WCD938X_EAR_INT_NEW_CNP_VCM_CON1 (0x31B8) +#define WCD938X_EAR_INT_NEW_CNP_VCM_CON2 (0x31B9) +#define WCD938X_EAR_INT_NEW_EAR_DYNAMIC_BIAS (0x31BA) +#define WCD938X_AUX_INT_EN_REG (0x31BD) +#define WCD938X_AUX_INT_PA_CTRL (0x31BE) +#define WCD938X_AUX_INT_SP_CTRL (0x31BF) +#define WCD938X_AUX_INT_DAC_CTRL (0x31C0) +#define WCD938X_AUX_INT_CLK_CTRL (0x31C1) +#define WCD938X_AUX_INT_TEST_CTRL (0x31C2) +#define WCD938X_AUX_INT_STATUS_REG (0x31C3) +#define WCD938X_AUX_INT_MISC (0x31C4) +#define WCD938X_LDORXTX_INT_BIAS (0x31C5) +#define WCD938X_LDORXTX_INT_STB_LOADS_DTEST (0x31C6) +#define WCD938X_LDORXTX_INT_TEST0 (0x31C7) +#define WCD938X_LDORXTX_INT_STARTUP_TIMER (0x31C8) +#define WCD938X_LDORXTX_INT_TEST1 (0x31C9) +#define WCD938X_LDORXTX_INT_STATUS (0x31CA) +#define WCD938X_SLEEP_INT_WATCHDOG_CTL_1 (0x31D0) +#define WCD938X_SLEEP_INT_WATCHDOG_CTL_2 (0x31D1) +#define WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT1 (0x31D3) +#define WCD938X_DIE_CRACK_INT_DIE_CRK_DET_INT2 (0x31D4) +#define WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L2 (0x31D5) +#define WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L1 (0x31D6) +#define WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_L0 (0x31D7) +#define WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP1P2M (0x31D8) +#define WCD938X_TX_COM_NEW_INT_TXFE_DIVSTOP_ULP0P6M (0x31D9) +#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L2L1 (0x31DA) +#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_L0 (0x31DB) +#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG1_ULP (0x31DC) +#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L2L1 (0x31DD) +#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_L0 (0x31DE) +#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2MAIN_ULP (0x31DF) +#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_L2L1L0 (0x31E0) +#define WCD938X_TX_COM_NEW_INT_TXFE_ICTRL_STG2CASC_ULP (0x31E1) +#define WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L2L1 (0x31E2) +#define WCD938X_TX_COM_NEW_INT_TXADC_SCBIAS_L0ULP (0x31E3) +#define WCD938X_TX_COM_NEW_INT_TXADC_INT_L2 (0x31E4) +#define WCD938X_TX_COM_NEW_INT_TXADC_INT_L1 (0x31E5) +#define WCD938X_TX_COM_NEW_INT_TXADC_INT_L0 (0x31E6) +#define WCD938X_TX_COM_NEW_INT_TXADC_INT_ULP (0x31E7) +#define WCD938X_DIGITAL_PAGE_REGISTER (0x3400) +#define WCD938X_DIGITAL_CHIP_ID0 (0x3401) +#define WCD938X_DIGITAL_CHIP_ID1 (0x3402) +#define WCD938X_DIGITAL_CHIP_ID2 (0x3403) +#define WCD938X_DIGITAL_CHIP_ID3 (0x3404) +#define WCD938X_DIGITAL_SWR_TX_CLK_RATE (0x3405) +#define WCD938X_DIGITAL_CDC_RST_CTL (0x3406) +#define WCD938X_DIGITAL_TOP_CLK_CFG (0x3407) +#define WCD938X_DIGITAL_CDC_ANA_CLK_CTL (0x3408) +#define WCD938X_ANA_RX_CLK_EN_MASK BIT(0) +#define WCD938X_ANA_RX_DIV2_CLK_EN_MASK BIT(1) +#define WCD938X_ANA_RX_DIV4_CLK_EN_MASK BIT(2) +#define WCD938X_ANA_TX_CLK_EN_MASK BIT(3) +#define WCD938X_ANA_TX_DIV2_CLK_EN_MASK BIT(4) +#define WCD938X_ANA_TX_DIV4_CLK_EN_MASK BIT(5) +#define WCD938X_DIGITAL_CDC_DIG_CLK_CTL (0x3409) +#define WCD938X_TXD3_CLK_EN_MASK BIT(7) +#define WCD938X_TXD2_CLK_EN_MASK BIT(6) +#define WCD938X_TXD1_CLK_EN_MASK BIT(5) +#define WCD938X_TXD0_CLK_EN_MASK BIT(4) +#define WCD938X_TX_CLK_EN_MASK GENMASK(7, 4) +#define WCD938X_RXD2_CLK_EN_MASK BIT(2) +#define WCD938X_RXD1_CLK_EN_MASK BIT(1) +#define WCD938X_RXD0_CLK_EN_MASK BIT(0) +#define WCD938X_DIGITAL_SWR_RST_EN (0x340A) +#define WCD938X_DIGITAL_CDC_PATH_MODE (0x340B) +#define WCD938X_DIGITAL_CDC_RX_RST (0x340C) +#define WCD938X_DIGITAL_CDC_RX0_CTL (0x340D) +#define WCD938X_DEM_DITHER_ENABLE_MASK BIT(6) +#define WCD938X_DIGITAL_CDC_RX1_CTL (0x340E) +#define WCD938X_DIGITAL_CDC_RX2_CTL (0x340F) +#define WCD938X_DIGITAL_CDC_TX_ANA_MODE_0_1 (0x3410) +#define WCD938X_TXD0_MODE_MASK GENMASK(3, 0) +#define WCD938X_TXD1_MODE_MASK GENMASK(7, 4) +#define WCD938X_DIGITAL_CDC_TX_ANA_MODE_2_3 (0x3411) +#define WCD938X_TXD2_MODE_MASK GENMASK(3, 0) +#define WCD938X_TXD3_MODE_MASK GENMASK(7, 4) +#define WCD938X_DIGITAL_CDC_COMP_CTL_0 (0x3414) +#define WCD938X_HPHR_COMP_EN_MASK BIT(0) +#define WCD938X_HPHL_COMP_EN_MASK BIT(1) +#define WCD938X_DIGITAL_CDC_ANA_TX_CLK_CTL (0x3417) +#define WCD938X_TX_SC_CLK_EN_MASK BIT(0) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A1_0 (0x3418) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A1_1 (0x3419) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A2_0 (0x341A) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A2_1 (0x341B) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A3_0 (0x341C) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A3_1 (0x341D) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A4_0 (0x341E) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A4_1 (0x341F) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A5_0 (0x3420) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A5_1 (0x3421) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A6_0 (0x3422) +#define WCD938X_DIGITAL_CDC_HPH_DSM_A7_0 (0x3423) +#define WCD938X_DIGITAL_CDC_HPH_DSM_C_0 (0x3424) +#define WCD938X_DIGITAL_CDC_HPH_DSM_C_1 (0x3425) +#define WCD938X_DIGITAL_CDC_HPH_DSM_C_2 (0x3426) +#define WCD938X_DIGITAL_CDC_HPH_DSM_C_3 (0x3427) +#define WCD938X_DIGITAL_CDC_HPH_DSM_R1 (0x3428) +#define WCD938X_DIGITAL_CDC_HPH_DSM_R2 (0x3429) +#define WCD938X_DIGITAL_CDC_HPH_DSM_R3 (0x342A) +#define WCD938X_DIGITAL_CDC_HPH_DSM_R4 (0x342B) +#define WCD938X_DIGITAL_CDC_HPH_DSM_R5 (0x342C) +#define WCD938X_DIGITAL_CDC_HPH_DSM_R6 (0x342D) +#define WCD938X_DIGITAL_CDC_HPH_DSM_R7 (0x342E) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A1_0 (0x342F) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A1_1 (0x3430) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A2_0 (0x3431) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A2_1 (0x3432) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A3_0 (0x3433) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A3_1 (0x3434) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A4_0 (0x3435) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A4_1 (0x3436) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A5_0 (0x3437) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A5_1 (0x3438) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A6_0 (0x3439) +#define WCD938X_DIGITAL_CDC_AUX_DSM_A7_0 (0x343A) +#define WCD938X_DIGITAL_CDC_AUX_DSM_C_0 (0x343B) +#define WCD938X_DIGITAL_CDC_AUX_DSM_C_1 (0x343C) +#define WCD938X_DIGITAL_CDC_AUX_DSM_C_2 (0x343D) +#define WCD938X_DIGITAL_CDC_AUX_DSM_C_3 (0x343E) +#define WCD938X_DIGITAL_CDC_AUX_DSM_R1 (0x343F) +#define WCD938X_DIGITAL_CDC_AUX_DSM_R2 (0x3440) +#define WCD938X_DIGITAL_CDC_AUX_DSM_R3 (0x3441) +#define WCD938X_DIGITAL_CDC_AUX_DSM_R4 (0x3442) +#define WCD938X_DIGITAL_CDC_AUX_DSM_R5 (0x3443) +#define WCD938X_DIGITAL_CDC_AUX_DSM_R6 (0x3444) +#define WCD938X_DIGITAL_CDC_AUX_DSM_R7 (0x3445) +#define WCD938X_DIGITAL_CDC_HPH_GAIN_RX_0 (0x3446) +#define WCD938X_DIGITAL_CDC_HPH_GAIN_RX_1 (0x3447) +#define WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_0 (0x3448) +#define WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_1 (0x3449) +#define WCD938X_DIGITAL_CDC_HPH_GAIN_DSD_2 (0x344A) +#define WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_0 (0x344B) +#define WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_1 (0x344C) +#define WCD938X_DIGITAL_CDC_AUX_GAIN_DSD_2 (0x344D) +#define WCD938X_DIGITAL_CDC_HPH_GAIN_CTL (0x344E) +#define WCD938X_HPHL_RX_EN_MASK BIT(2) +#define WCD938X_HPHR_RX_EN_MASK BIT(3) +#define WCD938X_DIGITAL_CDC_AUX_GAIN_CTL (0x344F) +#define WCD938X_AUX_EN_MASK BIT(0) +#define WCD938X_DIGITAL_CDC_EAR_PATH_CTL (0x3450) +#define WCD938X_DIGITAL_CDC_SWR_CLH (0x3451) +#define WCD938X_DIGITAL_SWR_CLH_BYP (0x3452) +#define WCD938X_DIGITAL_CDC_TX0_CTL (0x3453) +#define WCD938X_DIGITAL_CDC_TX1_CTL (0x3454) +#define WCD938X_DIGITAL_CDC_TX2_CTL (0x3455) +#define WCD938X_DIGITAL_CDC_TX_RST (0x3456) +#define WCD938X_DIGITAL_CDC_REQ_CTL (0x3457) +#define WCD938X_FS_RATE_4P8_MASK BIT(1) +#define WCD938X_NO_NOTCH_MASK BIT(0) +#define WCD938X_DIGITAL_CDC_RST (0x3458) +#define WCD938X_DIGITAL_CDC_AMIC_CTL (0x345A) +#define WCD938X_AMIC1_IN_SEL_DMIC 0 +#define WCD938X_AMIC1_IN_SEL_AMIC 0 +#define WCD938X_AMIC1_IN_SEL_MASK BIT(0) +#define WCD938X_AMIC3_IN_SEL_MASK BIT(1) +#define WCD938X_AMIC4_IN_SEL_MASK BIT(2) +#define WCD938X_AMIC5_IN_SEL_MASK BIT(3) +#define WCD938X_DIGITAL_CDC_DMIC_CTL (0x345B) +#define WCD938X_DMIC_CLK_SCALING_EN_MASK GENMASK(2, 1) +#define WCD938X_DIGITAL_CDC_DMIC1_CTL (0x345C) +#define WCD938X_DMIC_CLK_EN_MASK BIT(3) +#define WCD938X_DIGITAL_CDC_DMIC2_CTL (0x345D) +#define WCD938X_DIGITAL_CDC_DMIC3_CTL (0x345E) +#define WCD938X_DIGITAL_CDC_DMIC4_CTL (0x345F) +#define WCD938X_DIGITAL_EFUSE_PRG_CTL (0x3460) +#define WCD938X_DIGITAL_EFUSE_CTL (0x3461) +#define WCD938X_DIGITAL_CDC_DMIC_RATE_1_2 (0x3462) +#define WCD938X_DIGITAL_CDC_DMIC_RATE_3_4 (0x3463) +#define WCD938X_DMIC1_RATE_MASK GENMASK(3, 0) +#define WCD938X_DMIC2_RATE_MASK GENMASK(7, 4) +#define WCD938X_DMIC3_RATE_MASK GENMASK(3, 0) +#define WCD938X_DMIC4_RATE_MASK GENMASK(7, 4) +#define WCD938X_DMIC4_RATE_2P4MHZ 3 + +#define WCD938X_DIGITAL_PDM_WD_CTL0 (0x3465) +#define WCD938X_PDM_WD_EN_MASK GENMASK(2, 0) +#define WCD938X_DIGITAL_PDM_WD_CTL1 (0x3466) +#define WCD938X_DIGITAL_PDM_WD_CTL2 (0x3467) +#define WCD938X_AUX_PDM_WD_EN_MASK GENMASK(2, 0) +#define WCD938X_DIGITAL_INTR_MODE (0x346A) +#define WCD938X_DIGITAL_INTR_MASK_0 (0x346B) +#define WCD938X_DIGITAL_INTR_MASK_1 (0x346C) +#define WCD938X_DIGITAL_INTR_MASK_2 (0x346D) +#define WCD938X_DIGITAL_INTR_STATUS_0 (0x346E) +#define WCD938X_DIGITAL_INTR_STATUS_1 (0x346F) +#define WCD938X_DIGITAL_INTR_STATUS_2 (0x3470) +#define WCD938X_DIGITAL_INTR_CLEAR_0 (0x3471) +#define WCD938X_DIGITAL_INTR_CLEAR_1 (0x3472) +#define WCD938X_DIGITAL_INTR_CLEAR_2 (0x3473) +#define WCD938X_DIGITAL_INTR_LEVEL_0 (0x3474) +#define WCD938X_DIGITAL_INTR_LEVEL_1 (0x3475) +#define WCD938X_DIGITAL_INTR_LEVEL_2 (0x3476) +#define WCD938X_DIGITAL_INTR_SET_0 (0x3477) +#define WCD938X_DIGITAL_INTR_SET_1 (0x3478) +#define WCD938X_DIGITAL_INTR_SET_2 (0x3479) +#define WCD938X_DIGITAL_INTR_TEST_0 (0x347A) +#define WCD938X_DIGITAL_INTR_TEST_1 (0x347B) +#define WCD938X_DIGITAL_INTR_TEST_2 (0x347C) +#define WCD938X_DIGITAL_TX_MODE_DBG_EN (0x347F) +#define WCD938X_DIGITAL_TX_MODE_DBG_0_1 (0x3480) +#define WCD938X_DIGITAL_TX_MODE_DBG_2_3 (0x3481) +#define WCD938X_DIGITAL_LB_IN_SEL_CTL (0x3482) +#define WCD938X_DIGITAL_LOOP_BACK_MODE (0x3483) +#define WCD938X_DIGITAL_SWR_DAC_TEST (0x3484) +#define WCD938X_DIGITAL_SWR_HM_TEST_RX_0 (0x3485) +#define WCD938X_DIGITAL_SWR_HM_TEST_TX_0 (0x3486) +#define WCD938X_DIGITAL_SWR_HM_TEST_RX_1 (0x3487) +#define WCD938X_DIGITAL_SWR_HM_TEST_TX_1 (0x3488) +#define WCD938X_DIGITAL_SWR_HM_TEST_TX_2 (0x3489) +#define WCD938X_DIGITAL_SWR_HM_TEST_0 (0x348A) +#define WCD938X_DIGITAL_SWR_HM_TEST_1 (0x348B) +#define WCD938X_DIGITAL_PAD_CTL_SWR_0 (0x348C) +#define WCD938X_DIGITAL_PAD_CTL_SWR_1 (0x348D) +#define WCD938X_DIGITAL_I2C_CTL (0x348E) +#define WCD938X_DIGITAL_CDC_TX_TANGGU_SW_MODE (0x348F) +#define WCD938X_DIGITAL_EFUSE_TEST_CTL_0 (0x3490) +#define WCD938X_DIGITAL_EFUSE_TEST_CTL_1 (0x3491) +#define WCD938X_DIGITAL_EFUSE_T_DATA_0 (0x3492) +#define WCD938X_DIGITAL_EFUSE_T_DATA_1 (0x3493) +#define WCD938X_DIGITAL_PAD_CTL_PDM_RX0 (0x3494) +#define WCD938X_DIGITAL_PAD_CTL_PDM_RX1 (0x3495) +#define WCD938X_DIGITAL_PAD_CTL_PDM_TX0 (0x3496) +#define WCD938X_DIGITAL_PAD_CTL_PDM_TX1 (0x3497) +#define WCD938X_DIGITAL_PAD_CTL_PDM_TX2 (0x3498) +#define WCD938X_DIGITAL_PAD_INP_DIS_0 (0x3499) +#define WCD938X_DIGITAL_PAD_INP_DIS_1 (0x349A) +#define WCD938X_DIGITAL_DRIVE_STRENGTH_0 (0x349B) +#define WCD938X_DIGITAL_DRIVE_STRENGTH_1 (0x349C) +#define WCD938X_DIGITAL_DRIVE_STRENGTH_2 (0x349D) +#define WCD938X_DIGITAL_RX_DATA_EDGE_CTL (0x349E) +#define WCD938X_DIGITAL_TX_DATA_EDGE_CTL (0x349F) +#define WCD938X_DIGITAL_GPIO_MODE (0x34A0) +#define WCD938X_DIGITAL_PIN_CTL_OE (0x34A1) +#define WCD938X_DIGITAL_PIN_CTL_DATA_0 (0x34A2) +#define WCD938X_DIGITAL_PIN_CTL_DATA_1 (0x34A3) +#define WCD938X_DIGITAL_PIN_STATUS_0 (0x34A4) +#define WCD938X_DIGITAL_PIN_STATUS_1 (0x34A5) +#define WCD938X_DIGITAL_DIG_DEBUG_CTL (0x34A6) +#define WCD938X_DIGITAL_DIG_DEBUG_EN (0x34A7) +#define WCD938X_DIGITAL_ANA_CSR_DBG_ADD (0x34A8) +#define WCD938X_DIGITAL_ANA_CSR_DBG_CTL (0x34A9) +#define WCD938X_DIGITAL_SSP_DBG (0x34AA) +#define WCD938X_DIGITAL_MODE_STATUS_0 (0x34AB) +#define WCD938X_DIGITAL_MODE_STATUS_1 (0x34AC) +#define WCD938X_DIGITAL_SPARE_0 (0x34AD) +#define WCD938X_DIGITAL_SPARE_1 (0x34AE) +#define WCD938X_DIGITAL_SPARE_2 (0x34AF) +#define WCD938X_DIGITAL_EFUSE_REG_0 (0x34B0) +#define WCD938X_ID_MASK GENMASK(4, 1) +#define WCD938X_DIGITAL_EFUSE_REG_1 (0x34B1) +#define WCD938X_DIGITAL_EFUSE_REG_2 (0x34B2) +#define WCD938X_DIGITAL_EFUSE_REG_3 (0x34B3) +#define WCD938X_DIGITAL_EFUSE_REG_4 (0x34B4) +#define WCD938X_DIGITAL_EFUSE_REG_5 (0x34B5) +#define WCD938X_DIGITAL_EFUSE_REG_6 (0x34B6) +#define WCD938X_DIGITAL_EFUSE_REG_7 (0x34B7) +#define WCD938X_DIGITAL_EFUSE_REG_8 (0x34B8) +#define WCD938X_DIGITAL_EFUSE_REG_9 (0x34B9) +#define WCD938X_DIGITAL_EFUSE_REG_10 (0x34BA) +#define WCD938X_DIGITAL_EFUSE_REG_11 (0x34BB) +#define WCD938X_DIGITAL_EFUSE_REG_12 (0x34BC) +#define WCD938X_DIGITAL_EFUSE_REG_13 (0x34BD) +#define WCD938X_DIGITAL_EFUSE_REG_14 (0x34BE) +#define WCD938X_DIGITAL_EFUSE_REG_15 (0x34BF) +#define WCD938X_DIGITAL_EFUSE_REG_16 (0x34C0) +#define WCD938X_DIGITAL_EFUSE_REG_17 (0x34C1) +#define WCD938X_DIGITAL_EFUSE_REG_18 (0x34C2) +#define WCD938X_DIGITAL_EFUSE_REG_19 (0x34C3) +#define WCD938X_DIGITAL_EFUSE_REG_20 (0x34C4) +#define WCD938X_DIGITAL_EFUSE_REG_21 (0x34C5) +#define WCD938X_DIGITAL_EFUSE_REG_22 (0x34C6) +#define WCD938X_DIGITAL_EFUSE_REG_23 (0x34C7) +#define WCD938X_DIGITAL_EFUSE_REG_24 (0x34C8) +#define WCD938X_DIGITAL_EFUSE_REG_25 (0x34C9) +#define WCD938X_DIGITAL_EFUSE_REG_26 (0x34CA) +#define WCD938X_DIGITAL_EFUSE_REG_27 (0x34CB) +#define WCD938X_DIGITAL_EFUSE_REG_28 (0x34CC) +#define WCD938X_DIGITAL_EFUSE_REG_29 (0x34CD) +#define WCD938X_DIGITAL_EFUSE_REG_30 (0x34CE) +#define WCD938X_DIGITAL_EFUSE_REG_31 (0x34CF) +#define WCD938X_DIGITAL_TX_REQ_FB_CTL_0 (0x34D0) +#define WCD938X_DIGITAL_TX_REQ_FB_CTL_1 (0x34D1) +#define WCD938X_DIGITAL_TX_REQ_FB_CTL_2 (0x34D2) +#define WCD938X_DIGITAL_TX_REQ_FB_CTL_3 (0x34D3) +#define WCD938X_DIGITAL_TX_REQ_FB_CTL_4 (0x34D4) +#define WCD938X_DIGITAL_DEM_BYPASS_DATA0 (0x34D5) +#define WCD938X_DIGITAL_DEM_BYPASS_DATA1 (0x34D6) +#define WCD938X_DIGITAL_DEM_BYPASS_DATA2 (0x34D7) +#define WCD938X_DIGITAL_DEM_BYPASS_DATA3 (0x34D8) +#define WCD938X_MAX_REGISTER (WCD938X_DIGITAL_DEM_BYPASS_DATA3) + +#define WCD938X_MAX_SWR_PORTS 5 +#define WCD938X_MAX_TX_SWR_PORTS 4 +#define WCD938X_MAX_SWR_CH_IDS 15 + +struct wcd938x_sdw_ch_info { + int port_num; + unsigned int ch_mask; +}; + +#define WCD_SDW_CH(id, pn, cmask) \ + [id] = { \ + .port_num = pn, \ + .ch_mask = cmask, \ + } + +enum wcd938x_tx_sdw_ports { + WCD938X_ADC_1_2_PORT = 1, + WCD938X_ADC_3_4_PORT, + /* DMIC0_0, DMIC0_1, DMIC1_0, DMIC1_1 */ + WCD938X_DMIC_0_3_MBHC_PORT, + WCD938X_DMIC_4_7_PORT, +}; + +enum wcd938x_tx_sdw_channels { + WCD938X_ADC1, + WCD938X_ADC2, + WCD938X_ADC3, + WCD938X_ADC4, + WCD938X_DMIC0, + WCD938X_DMIC1, + WCD938X_MBHC, + WCD938X_DMIC2, + WCD938X_DMIC3, + WCD938X_DMIC4, + WCD938X_DMIC5, + WCD938X_DMIC6, + WCD938X_DMIC7, +}; + +enum wcd938x_rx_sdw_ports { + WCD938X_HPH_PORT = 1, + WCD938X_CLSH_PORT, + WCD938X_COMP_PORT, + WCD938X_LO_PORT, + WCD938X_DSD_PORT, +}; + +enum wcd938x_rx_sdw_channels { + WCD938X_HPH_L, + WCD938X_HPH_R, + WCD938X_CLSH, + WCD938X_COMP_L, + WCD938X_COMP_R, + WCD938X_LO, + WCD938X_DSD_R, + WCD938X_DSD_L, +}; +enum { + WCD938X_SDW_DIR_RX, + WCD938X_SDW_DIR_TX, +}; + +struct wcd938x_priv; +struct wcd938x_sdw_priv { + struct sdw_slave *sdev; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WCD938X_MAX_SWR_PORTS]; + struct wcd938x_sdw_ch_info *ch_info; + bool port_enable[WCD938X_MAX_SWR_CH_IDS]; + int active_ports; + int num_ports; + bool is_tx; + struct wcd938x_priv *wcd938x; + struct irq_domain *slave_irq; + struct regmap *regmap; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD938X_SDW) +int wcd938x_sdw_free(struct wcd938x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +int wcd938x_sdw_set_sdw_stream(struct wcd938x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction); +int wcd938x_sdw_hw_params(struct wcd938x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai); + +struct device *wcd938x_sdw_device_get(struct device_node *np); +int wcd938x_swr_get_current_bank(struct sdw_slave *sdev); + +#else + +static inline int wcd938x_sdw_free(struct wcd938x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} + +static inline int wcd938x_sdw_set_sdw_stream(struct wcd938x_sdw_priv *wcd, + struct snd_soc_dai *dai, + void *stream, int direction) +{ + return -EOPNOTSUPP; +} + +static inline int wcd938x_sdw_hw_params(struct wcd938x_sdw_priv *wcd, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return -EOPNOTSUPP; +} + +static inline struct device *wcd938x_sdw_device_get(struct device_node *np) +{ + return NULL; +} + +static inline int wcd938x_swr_get_current_bank(struct sdw_slave *sdev) +{ + return 0; +} +#endif /* CONFIG_SND_SOC_WCD938X_SDW */ +#endif /* __WCD938X_H__ */ diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c new file mode 100644 index 0000000000..737ca82cf9 --- /dev/null +++ b/sound/soc/codecs/wl1273.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC WL1273 codec driver + * + * Author: Matti Aaltonen, + * + * Copyright: (C) 2010, 2011 Nokia Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "wl1273.h" + +enum wl1273_mode { WL1273_MODE_BT, WL1273_MODE_FM_RX, WL1273_MODE_FM_TX }; + +/* codec private data */ +struct wl1273_priv { + enum wl1273_mode mode; + struct wl1273_core *core; + unsigned int channels; +}; + +static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core, + int rate, int width) +{ + struct device *dev = &core->client->dev; + int r = 0; + u16 mode; + + dev_dbg(dev, "rate: %d\n", rate); + dev_dbg(dev, "width: %d\n", width); + + mutex_lock(&core->lock); + + mode = core->i2s_mode & ~WL1273_IS2_WIDTH & ~WL1273_IS2_RATE; + + switch (rate) { + case 48000: + mode |= WL1273_IS2_RATE_48K; + break; + case 44100: + mode |= WL1273_IS2_RATE_44_1K; + break; + case 32000: + mode |= WL1273_IS2_RATE_32K; + break; + case 22050: + mode |= WL1273_IS2_RATE_22_05K; + break; + case 16000: + mode |= WL1273_IS2_RATE_16K; + break; + case 12000: + mode |= WL1273_IS2_RATE_12K; + break; + case 11025: + mode |= WL1273_IS2_RATE_11_025; + break; + case 8000: + mode |= WL1273_IS2_RATE_8K; + break; + default: + dev_err(dev, "Sampling rate: %d not supported\n", rate); + r = -EINVAL; + goto out; + } + + switch (width) { + case 16: + mode |= WL1273_IS2_WIDTH_32; + break; + case 20: + mode |= WL1273_IS2_WIDTH_40; + break; + case 24: + mode |= WL1273_IS2_WIDTH_48; + break; + case 25: + mode |= WL1273_IS2_WIDTH_50; + break; + case 30: + mode |= WL1273_IS2_WIDTH_60; + break; + case 32: + mode |= WL1273_IS2_WIDTH_64; + break; + case 40: + mode |= WL1273_IS2_WIDTH_80; + break; + case 48: + mode |= WL1273_IS2_WIDTH_96; + break; + case 64: + mode |= WL1273_IS2_WIDTH_128; + break; + default: + dev_err(dev, "Data width: %d not supported\n", width); + r = -EINVAL; + goto out; + } + + dev_dbg(dev, "WL1273_I2S_DEF_MODE: 0x%04x\n", WL1273_I2S_DEF_MODE); + dev_dbg(dev, "core->i2s_mode: 0x%04x\n", core->i2s_mode); + dev_dbg(dev, "mode: 0x%04x\n", mode); + + if (core->i2s_mode != mode) { + r = core->write(core, WL1273_I2S_MODE_CONFIG_SET, mode); + if (r) + goto out; + + core->i2s_mode = mode; + r = core->write(core, WL1273_AUDIO_ENABLE, + WL1273_AUDIO_ENABLE_I2S); + if (r) + goto out; + } +out: + mutex_unlock(&core->lock); + + return r; +} + +static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core, + int channel_number) +{ + struct device *dev = &core->client->dev; + int r = 0; + + dev_dbg(dev, "%s\n", __func__); + + mutex_lock(&core->lock); + + if (core->channel_number == channel_number) + goto out; + + if (channel_number == 1 && core->mode == WL1273_MODE_RX) + r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO); + else if (channel_number == 1 && core->mode == WL1273_MODE_TX) + r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO); + else if (channel_number == 2 && core->mode == WL1273_MODE_RX) + r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO); + else if (channel_number == 2 && core->mode == WL1273_MODE_TX) + r = core->write(core, WL1273_MONO_SET, WL1273_TX_STEREO); + else + r = -EINVAL; +out: + mutex_unlock(&core->lock); + + return r; +} + +static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wl1273->mode; + + return 0; +} + +/* + * TODO: Implement the audio routing in the driver. Now this control + * only indicates the setting that has been done elsewhere (in the user + * space). + */ +static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; + +static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); + + if (wl1273->mode == ucontrol->value.enumerated.item[0]) + return 0; + + /* Do not allow changes while stream is running */ + if (snd_soc_component_active(component)) + return -EPERM; + + if (ucontrol->value.enumerated.item[0] >= ARRAY_SIZE(wl1273_audio_route)) + return -EINVAL; + + wl1273->mode = ucontrol->value.enumerated.item[0]; + + return 1; +} + +static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route); + +static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s: enter.\n", __func__); + + ucontrol->value.enumerated.item[0] = wl1273->core->audio_mode; + + return 0; +} + +static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); + int val, r = 0; + + dev_dbg(component->dev, "%s: enter.\n", __func__); + + val = ucontrol->value.enumerated.item[0]; + if (wl1273->core->audio_mode == val) + return 0; + + r = wl1273->core->set_audio(wl1273->core, val); + if (r < 0) + return r; + + return 1; +} + +static const char * const wl1273_audio_strings[] = { "Digital", "Analog" }; + +static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings); + +static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s: enter.\n", __func__); + + ucontrol->value.integer.value[0] = wl1273->core->volume; + + return 0; +} + +static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); + int r; + + dev_dbg(component->dev, "%s: enter.\n", __func__); + + r = wl1273->core->set_volume(wl1273->core, + ucontrol->value.integer.value[0]); + if (r) + return r; + + return 1; +} + +static const struct snd_kcontrol_new wl1273_controls[] = { + SOC_ENUM_EXT("Codec Mode", wl1273_enum, + snd_wl1273_get_audio_route, snd_wl1273_set_audio_route), + SOC_ENUM_EXT("Audio Switch", wl1273_audio_enum, + snd_wl1273_fm_audio_get, snd_wl1273_fm_audio_put), + SOC_SINGLE_EXT("Volume", 0, 0, WL1273_MAX_VOLUME, 0, + snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put), +}; + +static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route wl1273_dapm_routes[] = { + { "Capture", NULL, "RX" }, + + { "TX", NULL, "Playback" }, +}; + +static int wl1273_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); + + switch (wl1273->mode) { + case WL1273_MODE_BT: + snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, 8000); + snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, 1); + break; + case WL1273_MODE_FM_RX: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_err("Cannot play in RX mode.\n"); + return -EINVAL; + } + break; + case WL1273_MODE_FM_TX: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + pr_err("Cannot capture in TX mode.\n"); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wl1273_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(dai->component); + struct wl1273_core *core = wl1273->core; + unsigned int rate, width, r; + + if (params_width(params) != 16) { + dev_err(dai->dev, "%d bits/sample not supported\n", + params_width(params)); + return -EINVAL; + } + + rate = params_rate(params); + width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; + + if (wl1273->mode == WL1273_MODE_BT) { + if (rate != 8000) { + pr_err("Rate %d not supported.\n", params_rate(params)); + return -EINVAL; + } + + if (params_channels(params) != 1) { + pr_err("Only mono supported.\n"); + return -EINVAL; + } + + return 0; + } + + if (wl1273->mode == WL1273_MODE_FM_TX && + substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + pr_err("Only playback supported with TX.\n"); + return -EINVAL; + } + + if (wl1273->mode == WL1273_MODE_FM_RX && + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_err("Only capture supported with RX.\n"); + return -EINVAL; + } + + if (wl1273->mode != WL1273_MODE_FM_RX && + wl1273->mode != WL1273_MODE_FM_TX) { + pr_err("Unexpected mode: %d.\n", wl1273->mode); + return -EINVAL; + } + + r = snd_wl1273_fm_set_i2s_mode(core, rate, width); + if (r) + return r; + + wl1273->channels = params_channels(params); + r = snd_wl1273_fm_set_channel_number(core, wl1273->channels); + if (r) + return r; + + return 0; +} + +static const struct snd_soc_dai_ops wl1273_dai_ops = { + .startup = wl1273_startup, + .hw_params = wl1273_hw_params, +}; + +static struct snd_soc_dai_driver wl1273_dai = { + .name = "wl1273-fm", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE}, + .ops = &wl1273_dai_ops, +}; + +/* Audio interface format for the soc_card driver */ +int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt) +{ + struct wl1273_priv *wl1273; + + if (component == NULL || fmt == NULL) + return -EINVAL; + + wl1273 = snd_soc_component_get_drvdata(component); + + switch (wl1273->mode) { + case WL1273_MODE_FM_RX: + case WL1273_MODE_FM_TX: + *fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBP_CFP; + + break; + case WL1273_MODE_BT: + *fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBP_CFP; + + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wl1273_get_format); + +static int wl1273_probe(struct snd_soc_component *component) +{ + struct wl1273_core **core = component->dev->platform_data; + struct wl1273_priv *wl1273; + + dev_dbg(component->dev, "%s.\n", __func__); + + if (!core) { + dev_err(component->dev, "Platform data is missing.\n"); + return -EINVAL; + } + + wl1273 = kzalloc(sizeof(struct wl1273_priv), GFP_KERNEL); + if (!wl1273) + return -ENOMEM; + + wl1273->mode = WL1273_MODE_BT; + wl1273->core = *core; + + snd_soc_component_set_drvdata(component, wl1273); + + return 0; +} + +static void wl1273_remove(struct snd_soc_component *component) +{ + struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "%s\n", __func__); + kfree(wl1273); +} + +static const struct snd_soc_component_driver soc_component_dev_wl1273 = { + .probe = wl1273_probe, + .remove = wl1273_remove, + .controls = wl1273_controls, + .num_controls = ARRAY_SIZE(wl1273_controls), + .dapm_widgets = wl1273_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets), + .dapm_routes = wl1273_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wl1273_platform_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wl1273, + &wl1273_dai, 1); +} + +MODULE_ALIAS("platform:wl1273-codec"); + +static struct platform_driver wl1273_platform_driver = { + .driver = { + .name = "wl1273-codec", + }, + .probe = wl1273_platform_probe, +}; + +module_platform_driver(wl1273_platform_driver); + +MODULE_AUTHOR("Matti Aaltonen "); +MODULE_DESCRIPTION("ASoC WL1273 codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wl1273.h b/sound/soc/codecs/wl1273.h new file mode 100644 index 0000000000..66c312fa7e --- /dev/null +++ b/sound/soc/codecs/wl1273.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * sound/soc/codec/wl1273.h + * + * ALSA SoC WL1273 codec driver + * + * Copyright (C) Nokia Corporation + * Author: Matti Aaltonen + */ + +#ifndef __WL1273_CODEC_H__ +#define __WL1273_CODEC_H__ + +int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt); + +#endif /* End of __WL1273_CODEC_H__ */ diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c new file mode 100644 index 0000000000..1d4259433f --- /dev/null +++ b/sound/soc/codecs/wm0010.c @@ -0,0 +1,999 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm0010.c -- WM0010 DSP Driver + * + * Copyright 2012 Wolfson Microelectronics PLC. + * + * Authors: Mark Brown + * Dimitris Papastamos + * Scott Ling + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEVICE_ID_WM0010 10 + +/* We only support v1 of the .dfw INFO record */ +#define INFO_VERSION 1 + +enum dfw_cmd { + DFW_CMD_FUSE = 0x01, + DFW_CMD_CODE_HDR, + DFW_CMD_CODE_DATA, + DFW_CMD_PLL, + DFW_CMD_INFO = 0xff +}; + +struct dfw_binrec { + u8 command; + u32 length:24; + u32 address; + uint8_t data[]; +} __packed; + +struct dfw_inforec { + u8 info_version; + u8 tool_major_version; + u8 tool_minor_version; + u8 dsp_target; +}; + +struct dfw_pllrec { + u8 command; + u32 length:24; + u32 address; + u32 clkctrl1; + u32 clkctrl2; + u32 clkctrl3; + u32 ldetctrl; + u32 uart_div; + u32 spi_div; +} __packed; + +static struct pll_clock_map { + int max_sysclk; + int max_pll_spi_speed; + u32 pll_clkctrl1; +} pll_clock_map[] = { /* Dividers */ + { 22000000, 26000000, 0x00201f11 }, /* 2,32,2 */ + { 18000000, 26000000, 0x00203f21 }, /* 2,64,4 */ + { 14000000, 26000000, 0x00202620 }, /* 1,39,4 */ + { 10000000, 22000000, 0x00203120 }, /* 1,50,4 */ + { 6500000, 22000000, 0x00204520 }, /* 1,70,4 */ + { 5500000, 22000000, 0x00103f10 }, /* 1,64,2 */ +}; + +enum wm0010_state { + WM0010_POWER_OFF, + WM0010_OUT_OF_RESET, + WM0010_BOOTROM, + WM0010_STAGE2, + WM0010_FIRMWARE, +}; + +struct wm0010_priv { + struct snd_soc_component *component; + + struct mutex lock; + struct device *dev; + + struct wm0010_pdata pdata; + + int gpio_reset; + int gpio_reset_value; + + struct regulator_bulk_data core_supplies[2]; + struct regulator *dbvdd; + + int sysclk; + + enum wm0010_state state; + bool boot_failed; + bool ready; + bool pll_running; + int max_spi_freq; + int board_max_spi_speed; + u32 pll_clkctrl1; + + spinlock_t irq_lock; + int irq; + + struct completion boot_completion; +}; + +struct wm0010_spi_msg { + struct spi_message m; + struct spi_transfer t; + u8 *tx_buf; + u8 *rx_buf; + size_t len; +}; + +static const struct snd_soc_dapm_widget wm0010_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("CLKIN", SND_SOC_NOPM, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route wm0010_dapm_routes[] = { + { "SDI2 Capture", NULL, "SDI1 Playback" }, + { "SDI1 Capture", NULL, "SDI2 Playback" }, + + { "SDI1 Capture", NULL, "CLKIN" }, + { "SDI2 Capture", NULL, "CLKIN" }, + { "SDI1 Playback", NULL, "CLKIN" }, + { "SDI2 Playback", NULL, "CLKIN" }, +}; + +static const char *wm0010_state_to_str(enum wm0010_state state) +{ + static const char * const state_to_str[] = { + "Power off", + "Out of reset", + "Boot ROM", + "Stage2", + "Firmware" + }; + + if (state < 0 || state >= ARRAY_SIZE(state_to_str)) + return "null"; + return state_to_str[state]; +} + +/* Called with wm0010->lock held */ +static void wm0010_halt(struct snd_soc_component *component) +{ + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); + unsigned long flags; + enum wm0010_state state; + + /* Fetch the wm0010 state */ + spin_lock_irqsave(&wm0010->irq_lock, flags); + state = wm0010->state; + spin_unlock_irqrestore(&wm0010->irq_lock, flags); + + switch (state) { + case WM0010_POWER_OFF: + /* If there's nothing to do, bail out */ + return; + case WM0010_OUT_OF_RESET: + case WM0010_BOOTROM: + case WM0010_STAGE2: + case WM0010_FIRMWARE: + /* Remember to put chip back into reset */ + gpio_set_value_cansleep(wm0010->gpio_reset, + wm0010->gpio_reset_value); + /* Disable the regulators */ + regulator_disable(wm0010->dbvdd); + regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies), + wm0010->core_supplies); + break; + } + + spin_lock_irqsave(&wm0010->irq_lock, flags); + wm0010->state = WM0010_POWER_OFF; + spin_unlock_irqrestore(&wm0010->irq_lock, flags); +} + +struct wm0010_boot_xfer { + struct list_head list; + struct snd_soc_component *component; + struct completion *done; + struct spi_message m; + struct spi_transfer t; +}; + +/* Called with wm0010->lock held */ +static void wm0010_mark_boot_failure(struct wm0010_priv *wm0010) +{ + enum wm0010_state state; + unsigned long flags; + + spin_lock_irqsave(&wm0010->irq_lock, flags); + state = wm0010->state; + spin_unlock_irqrestore(&wm0010->irq_lock, flags); + + dev_err(wm0010->dev, "Failed to transition from `%s' state to `%s' state\n", + wm0010_state_to_str(state), wm0010_state_to_str(state + 1)); + + wm0010->boot_failed = true; +} + +static void wm0010_boot_xfer_complete(void *data) +{ + struct wm0010_boot_xfer *xfer = data; + struct snd_soc_component *component = xfer->component; + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); + u32 *out32 = xfer->t.rx_buf; + int i; + + if (xfer->m.status != 0) { + dev_err(component->dev, "SPI transfer failed: %d\n", + xfer->m.status); + wm0010_mark_boot_failure(wm0010); + if (xfer->done) + complete(xfer->done); + return; + } + + for (i = 0; i < xfer->t.len / 4; i++) { + dev_dbg(component->dev, "%d: %04x\n", i, out32[i]); + + switch (be32_to_cpu(out32[i])) { + case 0xe0e0e0e0: + dev_err(component->dev, + "%d: ROM error reported in stage 2\n", i); + wm0010_mark_boot_failure(wm0010); + break; + + case 0x55555555: + if (wm0010->state < WM0010_STAGE2) + break; + dev_err(component->dev, + "%d: ROM bootloader running in stage 2\n", i); + wm0010_mark_boot_failure(wm0010); + break; + + case 0x0fed0000: + dev_dbg(component->dev, "Stage2 loader running\n"); + break; + + case 0x0fed0007: + dev_dbg(component->dev, "CODE_HDR packet received\n"); + break; + + case 0x0fed0008: + dev_dbg(component->dev, "CODE_DATA packet received\n"); + break; + + case 0x0fed0009: + dev_dbg(component->dev, "Download complete\n"); + break; + + case 0x0fed000c: + dev_dbg(component->dev, "Application start\n"); + break; + + case 0x0fed000e: + dev_dbg(component->dev, "PLL packet received\n"); + wm0010->pll_running = true; + break; + + case 0x0fed0025: + dev_err(component->dev, "Device reports image too long\n"); + wm0010_mark_boot_failure(wm0010); + break; + + case 0x0fed002c: + dev_err(component->dev, "Device reports bad SPI packet\n"); + wm0010_mark_boot_failure(wm0010); + break; + + case 0x0fed0031: + dev_err(component->dev, "Device reports SPI read overflow\n"); + wm0010_mark_boot_failure(wm0010); + break; + + case 0x0fed0032: + dev_err(component->dev, "Device reports SPI underclock\n"); + wm0010_mark_boot_failure(wm0010); + break; + + case 0x0fed0033: + dev_err(component->dev, "Device reports bad header packet\n"); + wm0010_mark_boot_failure(wm0010); + break; + + case 0x0fed0034: + dev_err(component->dev, "Device reports invalid packet type\n"); + wm0010_mark_boot_failure(wm0010); + break; + + case 0x0fed0035: + dev_err(component->dev, "Device reports data before header error\n"); + wm0010_mark_boot_failure(wm0010); + break; + + case 0x0fed0038: + dev_err(component->dev, "Device reports invalid PLL packet\n"); + break; + + case 0x0fed003a: + dev_err(component->dev, "Device reports packet alignment error\n"); + wm0010_mark_boot_failure(wm0010); + break; + + default: + dev_err(component->dev, "Unrecognised return 0x%x\n", + be32_to_cpu(out32[i])); + wm0010_mark_boot_failure(wm0010); + break; + } + + if (wm0010->boot_failed) + break; + } + + if (xfer->done) + complete(xfer->done); +} + +static void byte_swap_64(u64 *data_in, u64 *data_out, u32 len) +{ + int i; + + for (i = 0; i < len / 8; i++) + data_out[i] = cpu_to_be64(le64_to_cpu(data_in[i])); +} + +static int wm0010_firmware_load(const char *name, struct snd_soc_component *component) +{ + struct spi_device *spi = to_spi_device(component->dev); + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); + struct list_head xfer_list; + struct wm0010_boot_xfer *xfer; + int ret; + DECLARE_COMPLETION_ONSTACK(done); + const struct firmware *fw; + const struct dfw_binrec *rec; + const struct dfw_inforec *inforec; + u64 *img; + u8 *out, dsp; + u32 len, offset; + + INIT_LIST_HEAD(&xfer_list); + + ret = request_firmware(&fw, name, component->dev); + if (ret != 0) { + dev_err(component->dev, "Failed to request application(%s): %d\n", + name, ret); + return ret; + } + + rec = (const struct dfw_binrec *)fw->data; + inforec = (const struct dfw_inforec *)rec->data; + offset = 0; + dsp = inforec->dsp_target; + wm0010->boot_failed = false; + if (WARN_ON(!list_empty(&xfer_list))) + return -EINVAL; + + /* First record should be INFO */ + if (rec->command != DFW_CMD_INFO) { + dev_err(component->dev, "First record not INFO\r\n"); + ret = -EINVAL; + goto abort; + } + + if (inforec->info_version != INFO_VERSION) { + dev_err(component->dev, + "Unsupported version (%02d) of INFO record\r\n", + inforec->info_version); + ret = -EINVAL; + goto abort; + } + + dev_dbg(component->dev, "Version v%02d INFO record found\r\n", + inforec->info_version); + + /* Check it's a DSP file */ + if (dsp != DEVICE_ID_WM0010) { + dev_err(component->dev, "Not a WM0010 firmware file.\r\n"); + ret = -EINVAL; + goto abort; + } + + /* Skip the info record as we don't need to send it */ + offset += ((rec->length) + 8); + rec = (void *)&rec->data[rec->length]; + + while (offset < fw->size) { + dev_dbg(component->dev, + "Packet: command %d, data length = 0x%x\r\n", + rec->command, rec->length); + len = rec->length + 8; + + xfer = kzalloc(sizeof(*xfer), GFP_KERNEL); + if (!xfer) { + ret = -ENOMEM; + goto abort; + } + + xfer->component = component; + list_add_tail(&xfer->list, &xfer_list); + + out = kzalloc(len, GFP_KERNEL | GFP_DMA); + if (!out) { + ret = -ENOMEM; + goto abort1; + } + xfer->t.rx_buf = out; + + img = kzalloc(len, GFP_KERNEL | GFP_DMA); + if (!img) { + ret = -ENOMEM; + goto abort1; + } + xfer->t.tx_buf = img; + + byte_swap_64((u64 *)&rec->command, img, len); + + spi_message_init(&xfer->m); + xfer->m.complete = wm0010_boot_xfer_complete; + xfer->m.context = xfer; + xfer->t.len = len; + xfer->t.bits_per_word = 8; + + if (!wm0010->pll_running) { + xfer->t.speed_hz = wm0010->sysclk / 6; + } else { + xfer->t.speed_hz = wm0010->max_spi_freq; + + if (wm0010->board_max_spi_speed && + (wm0010->board_max_spi_speed < wm0010->max_spi_freq)) + xfer->t.speed_hz = wm0010->board_max_spi_speed; + } + + /* Store max usable spi frequency for later use */ + wm0010->max_spi_freq = xfer->t.speed_hz; + + spi_message_add_tail(&xfer->t, &xfer->m); + + offset += ((rec->length) + 8); + rec = (void *)&rec->data[rec->length]; + + if (offset >= fw->size) { + dev_dbg(component->dev, "All transfers scheduled\n"); + xfer->done = &done; + } + + ret = spi_async(spi, &xfer->m); + if (ret != 0) { + dev_err(component->dev, "Write failed: %d\n", ret); + goto abort1; + } + + if (wm0010->boot_failed) { + dev_dbg(component->dev, "Boot fail!\n"); + ret = -EINVAL; + goto abort1; + } + } + + wait_for_completion(&done); + + ret = 0; + +abort1: + while (!list_empty(&xfer_list)) { + xfer = list_first_entry(&xfer_list, struct wm0010_boot_xfer, + list); + kfree(xfer->t.rx_buf); + kfree(xfer->t.tx_buf); + list_del(&xfer->list); + kfree(xfer); + } + +abort: + release_firmware(fw); + return ret; +} + +static int wm0010_stage2_load(struct snd_soc_component *component) +{ + struct spi_device *spi = to_spi_device(component->dev); + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); + const struct firmware *fw; + struct spi_message m; + struct spi_transfer t; + u32 *img; + u8 *out; + int i; + int ret = 0; + + ret = request_firmware(&fw, "wm0010_stage2.bin", component->dev); + if (ret != 0) { + dev_err(component->dev, "Failed to request stage2 loader: %d\n", + ret); + return ret; + } + + dev_dbg(component->dev, "Downloading %zu byte stage 2 loader\n", fw->size); + + /* Copy to local buffer first as vmalloc causes problems for dma */ + img = kmemdup(&fw->data[0], fw->size, GFP_KERNEL | GFP_DMA); + if (!img) { + ret = -ENOMEM; + goto abort2; + } + + out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); + if (!out) { + ret = -ENOMEM; + goto abort1; + } + + spi_message_init(&m); + memset(&t, 0, sizeof(t)); + t.rx_buf = out; + t.tx_buf = img; + t.len = fw->size; + t.bits_per_word = 8; + t.speed_hz = wm0010->sysclk / 10; + spi_message_add_tail(&t, &m); + + dev_dbg(component->dev, "Starting initial download at %dHz\n", + t.speed_hz); + + ret = spi_sync(spi, &m); + if (ret != 0) { + dev_err(component->dev, "Initial download failed: %d\n", ret); + goto abort; + } + + /* Look for errors from the boot ROM */ + for (i = 0; i < fw->size; i++) { + if (out[i] != 0x55) { + dev_err(component->dev, "Boot ROM error: %x in %d\n", + out[i], i); + wm0010_mark_boot_failure(wm0010); + ret = -EBUSY; + goto abort; + } + } +abort: + kfree(out); +abort1: + kfree(img); +abort2: + release_firmware(fw); + + return ret; +} + +static int wm0010_boot(struct snd_soc_component *component) +{ + struct spi_device *spi = to_spi_device(component->dev); + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); + unsigned long flags; + int ret; + struct spi_message m; + struct spi_transfer t; + struct dfw_pllrec pll_rec; + u32 *p, len; + u64 *img_swap; + u8 *out; + int i; + + spin_lock_irqsave(&wm0010->irq_lock, flags); + if (wm0010->state != WM0010_POWER_OFF) + dev_warn(wm0010->dev, "DSP already powered up!\n"); + spin_unlock_irqrestore(&wm0010->irq_lock, flags); + + if (wm0010->sysclk > 26000000) { + dev_err(component->dev, "Max DSP clock frequency is 26MHz\n"); + ret = -ECANCELED; + goto err; + } + + mutex_lock(&wm0010->lock); + wm0010->pll_running = false; + + dev_dbg(component->dev, "max_spi_freq: %d\n", wm0010->max_spi_freq); + + ret = regulator_bulk_enable(ARRAY_SIZE(wm0010->core_supplies), + wm0010->core_supplies); + if (ret != 0) { + dev_err(&spi->dev, "Failed to enable core supplies: %d\n", + ret); + mutex_unlock(&wm0010->lock); + goto err; + } + + ret = regulator_enable(wm0010->dbvdd); + if (ret != 0) { + dev_err(&spi->dev, "Failed to enable DBVDD: %d\n", ret); + goto err_core; + } + + /* Release reset */ + gpio_set_value_cansleep(wm0010->gpio_reset, !wm0010->gpio_reset_value); + spin_lock_irqsave(&wm0010->irq_lock, flags); + wm0010->state = WM0010_OUT_OF_RESET; + spin_unlock_irqrestore(&wm0010->irq_lock, flags); + + if (!wait_for_completion_timeout(&wm0010->boot_completion, + msecs_to_jiffies(20))) + dev_err(component->dev, "Failed to get interrupt from DSP\n"); + + spin_lock_irqsave(&wm0010->irq_lock, flags); + wm0010->state = WM0010_BOOTROM; + spin_unlock_irqrestore(&wm0010->irq_lock, flags); + + ret = wm0010_stage2_load(component); + if (ret) + goto abort; + + if (!wait_for_completion_timeout(&wm0010->boot_completion, + msecs_to_jiffies(20))) + dev_err(component->dev, "Failed to get interrupt from DSP loader.\n"); + + spin_lock_irqsave(&wm0010->irq_lock, flags); + wm0010->state = WM0010_STAGE2; + spin_unlock_irqrestore(&wm0010->irq_lock, flags); + + /* Only initialise PLL if max_spi_freq initialised */ + if (wm0010->max_spi_freq) { + + /* Initialise a PLL record */ + memset(&pll_rec, 0, sizeof(pll_rec)); + pll_rec.command = DFW_CMD_PLL; + pll_rec.length = (sizeof(pll_rec) - 8); + + /* On wm0010 only the CLKCTRL1 value is used */ + pll_rec.clkctrl1 = wm0010->pll_clkctrl1; + + ret = -ENOMEM; + len = pll_rec.length + 8; + out = kzalloc(len, GFP_KERNEL | GFP_DMA); + if (!out) + goto abort; + + img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA); + if (!img_swap) + goto abort_out; + + /* We need to re-order for 0010 */ + byte_swap_64((u64 *)&pll_rec, img_swap, len); + + spi_message_init(&m); + memset(&t, 0, sizeof(t)); + t.rx_buf = out; + t.tx_buf = img_swap; + t.len = len; + t.bits_per_word = 8; + t.speed_hz = wm0010->sysclk / 6; + spi_message_add_tail(&t, &m); + + ret = spi_sync(spi, &m); + if (ret) { + dev_err(component->dev, "First PLL write failed: %d\n", ret); + goto abort_swap; + } + + /* Use a second send of the message to get the return status */ + ret = spi_sync(spi, &m); + if (ret) { + dev_err(component->dev, "Second PLL write failed: %d\n", ret); + goto abort_swap; + } + + p = (u32 *)out; + + /* Look for PLL active code from the DSP */ + for (i = 0; i < len / 4; i++) { + if (*p == 0x0e00ed0f) { + dev_dbg(component->dev, "PLL packet received\n"); + wm0010->pll_running = true; + break; + } + p++; + } + + kfree(img_swap); + kfree(out); + } else + dev_dbg(component->dev, "Not enabling DSP PLL."); + + ret = wm0010_firmware_load("wm0010.dfw", component); + + if (ret != 0) + goto abort; + + spin_lock_irqsave(&wm0010->irq_lock, flags); + wm0010->state = WM0010_FIRMWARE; + spin_unlock_irqrestore(&wm0010->irq_lock, flags); + + mutex_unlock(&wm0010->lock); + + return 0; + +abort_swap: + kfree(img_swap); +abort_out: + kfree(out); +abort: + /* Put the chip back into reset */ + wm0010_halt(component); + mutex_unlock(&wm0010->lock); + return ret; + +err_core: + mutex_unlock(&wm0010->lock); + regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies), + wm0010->core_supplies); +err: + return ret; +} + +static int wm0010_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) + wm0010_boot(component); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) { + mutex_lock(&wm0010->lock); + wm0010_halt(component); + mutex_unlock(&wm0010->lock); + } + break; + case SND_SOC_BIAS_OFF: + break; + } + + return 0; +} + +static int wm0010_set_sysclk(struct snd_soc_component *component, int source, + int clk_id, unsigned int freq, int dir) +{ + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); + unsigned int i; + + wm0010->sysclk = freq; + + if (freq < pll_clock_map[ARRAY_SIZE(pll_clock_map)-1].max_sysclk) { + wm0010->max_spi_freq = 0; + } else { + for (i = 0; i < ARRAY_SIZE(pll_clock_map); i++) + if (freq >= pll_clock_map[i].max_sysclk) { + wm0010->max_spi_freq = pll_clock_map[i].max_pll_spi_speed; + wm0010->pll_clkctrl1 = pll_clock_map[i].pll_clkctrl1; + break; + } + } + + return 0; +} + +static int wm0010_probe(struct snd_soc_component *component); + +static const struct snd_soc_component_driver soc_component_dev_wm0010 = { + .probe = wm0010_probe, + .set_bias_level = wm0010_set_bias_level, + .set_sysclk = wm0010_set_sysclk, + .dapm_widgets = wm0010_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm0010_dapm_widgets), + .dapm_routes = wm0010_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm0010_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +#define WM0010_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) +#define WM0010_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm0010_dai[] = { + { + .name = "wm0010-sdi1", + .playback = { + .stream_name = "SDI1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM0010_RATES, + .formats = WM0010_FORMATS, + }, + .capture = { + .stream_name = "SDI1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM0010_RATES, + .formats = WM0010_FORMATS, + }, + }, + { + .name = "wm0010-sdi2", + .playback = { + .stream_name = "SDI2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM0010_RATES, + .formats = WM0010_FORMATS, + }, + .capture = { + .stream_name = "SDI2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM0010_RATES, + .formats = WM0010_FORMATS, + }, + }, +}; + +static irqreturn_t wm0010_irq(int irq, void *data) +{ + struct wm0010_priv *wm0010 = data; + + switch (wm0010->state) { + case WM0010_OUT_OF_RESET: + case WM0010_BOOTROM: + case WM0010_STAGE2: + spin_lock(&wm0010->irq_lock); + complete(&wm0010->boot_completion); + spin_unlock(&wm0010->irq_lock); + return IRQ_HANDLED; + default: + return IRQ_NONE; + } + + return IRQ_NONE; +} + +static int wm0010_probe(struct snd_soc_component *component) +{ + struct wm0010_priv *wm0010 = snd_soc_component_get_drvdata(component); + + wm0010->component = component; + + return 0; +} + +static int wm0010_spi_probe(struct spi_device *spi) +{ + unsigned long gpio_flags; + int ret; + int trigger; + int irq; + struct wm0010_priv *wm0010; + + wm0010 = devm_kzalloc(&spi->dev, sizeof(*wm0010), + GFP_KERNEL); + if (!wm0010) + return -ENOMEM; + + mutex_init(&wm0010->lock); + spin_lock_init(&wm0010->irq_lock); + + spi_set_drvdata(spi, wm0010); + wm0010->dev = &spi->dev; + + if (dev_get_platdata(&spi->dev)) + memcpy(&wm0010->pdata, dev_get_platdata(&spi->dev), + sizeof(wm0010->pdata)); + + init_completion(&wm0010->boot_completion); + + wm0010->core_supplies[0].supply = "AVDD"; + wm0010->core_supplies[1].supply = "DCVDD"; + ret = devm_regulator_bulk_get(wm0010->dev, ARRAY_SIZE(wm0010->core_supplies), + wm0010->core_supplies); + if (ret != 0) { + dev_err(wm0010->dev, "Failed to obtain core supplies: %d\n", + ret); + return ret; + } + + wm0010->dbvdd = devm_regulator_get(wm0010->dev, "DBVDD"); + if (IS_ERR(wm0010->dbvdd)) { + ret = PTR_ERR(wm0010->dbvdd); + dev_err(wm0010->dev, "Failed to obtain DBVDD: %d\n", ret); + return ret; + } + + if (wm0010->pdata.gpio_reset) { + wm0010->gpio_reset = wm0010->pdata.gpio_reset; + + if (wm0010->pdata.reset_active_high) + wm0010->gpio_reset_value = 1; + else + wm0010->gpio_reset_value = 0; + + if (wm0010->gpio_reset_value) + gpio_flags = GPIOF_OUT_INIT_HIGH; + else + gpio_flags = GPIOF_OUT_INIT_LOW; + + ret = devm_gpio_request_one(wm0010->dev, wm0010->gpio_reset, + gpio_flags, "wm0010 reset"); + if (ret < 0) { + dev_err(wm0010->dev, + "Failed to request GPIO for DSP reset: %d\n", + ret); + return ret; + } + } else { + dev_err(wm0010->dev, "No reset GPIO configured\n"); + return -EINVAL; + } + + wm0010->state = WM0010_POWER_OFF; + + irq = spi->irq; + if (wm0010->pdata.irq_flags) + trigger = wm0010->pdata.irq_flags; + else + trigger = IRQF_TRIGGER_FALLING; + trigger |= IRQF_ONESHOT; + + ret = request_threaded_irq(irq, NULL, wm0010_irq, trigger, + "wm0010", wm0010); + if (ret) { + dev_err(wm0010->dev, "Failed to request IRQ %d: %d\n", + irq, ret); + return ret; + } + wm0010->irq = irq; + + ret = irq_set_irq_wake(irq, 1); + if (ret) { + dev_err(wm0010->dev, "Failed to set IRQ %d as wake source: %d\n", + irq, ret); + return ret; + } + + if (spi->max_speed_hz) + wm0010->board_max_spi_speed = spi->max_speed_hz; + else + wm0010->board_max_spi_speed = 0; + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm0010, wm0010_dai, + ARRAY_SIZE(wm0010_dai)); + if (ret < 0) + return ret; + + return 0; +} + +static void wm0010_spi_remove(struct spi_device *spi) +{ + struct wm0010_priv *wm0010 = spi_get_drvdata(spi); + + gpio_set_value_cansleep(wm0010->gpio_reset, + wm0010->gpio_reset_value); + + irq_set_irq_wake(wm0010->irq, 0); + + if (wm0010->irq) + free_irq(wm0010->irq, wm0010); +} + +static struct spi_driver wm0010_spi_driver = { + .driver = { + .name = "wm0010", + }, + .probe = wm0010_spi_probe, + .remove = wm0010_spi_remove, +}; + +module_spi_driver(wm0010_spi_driver); + +MODULE_DESCRIPTION("ASoC WM0010 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); + +MODULE_FIRMWARE("wm0010.dfw"); +MODULE_FIRMWARE("wm0010_stage2.bin"); diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c new file mode 100644 index 0000000000..d7eeb41ba6 --- /dev/null +++ b/sound/soc/codecs/wm1250-ev1.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for the 1250-EV1 audio I/O module + * + * Copyright 2011 Wolfson Microelectronics plc + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +static const char *wm1250_gpio_names[WM1250_EV1_NUM_GPIOS] = { + "WM1250 CLK_ENA", + "WM1250 CLK_SEL0", + "WM1250 CLK_SEL1", + "WM1250 OSR", + "WM1250 MASTER", +}; + +struct wm1250_priv { + struct gpio gpios[WM1250_EV1_NUM_GPIOS]; +}; + +static int wm1250_ev1_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm1250_priv *wm1250 = dev_get_drvdata(component->dev); + int ena; + + if (wm1250) + ena = wm1250->gpios[WM1250_EV1_GPIO_CLK_ENA].gpio; + else + ena = -1; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (ena >= 0) + gpio_set_value_cansleep(ena, 1); + break; + + case SND_SOC_BIAS_OFF: + if (ena >= 0) + gpio_set_value_cansleep(ena, 0); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget wm1250_ev1_dapm_widgets[] = { +SND_SOC_DAPM_ADC("ADC", "wm1250-ev1 Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_DAC("DAC", "wm1250-ev1 Playback", SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_INPUT("WM1250 Input"), +SND_SOC_DAPM_OUTPUT("WM1250 Output"), +}; + +static const struct snd_soc_dapm_route wm1250_ev1_dapm_routes[] = { + { "ADC", NULL, "WM1250 Input" }, + { "WM1250 Output", NULL, "DAC" }, +}; + +static int wm1250_ev1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wm1250_priv *wm1250 = snd_soc_component_get_drvdata(dai->component); + + switch (params_rate(params)) { + case 8000: + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, + 1); + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, + 1); + break; + case 16000: + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, + 0); + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, + 1); + break; + case 32000: + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, + 1); + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, + 0); + break; + case 64000: + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].gpio, + 0); + gpio_set_value(wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].gpio, + 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops wm1250_ev1_ops = { + .hw_params = wm1250_ev1_hw_params, +}; + +#define WM1250_EV1_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000) + +static struct snd_soc_dai_driver wm1250_ev1_dai = { + .name = "wm1250-ev1", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM1250_EV1_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM1250_EV1_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &wm1250_ev1_ops, +}; + +static const struct snd_soc_component_driver soc_component_dev_wm1250_ev1 = { + .dapm_widgets = wm1250_ev1_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm1250_ev1_dapm_widgets), + .dapm_routes = wm1250_ev1_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm1250_ev1_dapm_routes), + .set_bias_level = wm1250_ev1_set_bias_level, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm1250_ev1_pdata(struct i2c_client *i2c) +{ + struct wm1250_ev1_pdata *pdata = dev_get_platdata(&i2c->dev); + struct wm1250_priv *wm1250; + int i, ret; + + if (!pdata) + return 0; + + wm1250 = devm_kzalloc(&i2c->dev, sizeof(*wm1250), GFP_KERNEL); + if (!wm1250) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < ARRAY_SIZE(wm1250->gpios); i++) { + wm1250->gpios[i].gpio = pdata->gpios[i]; + wm1250->gpios[i].label = wm1250_gpio_names[i]; + wm1250->gpios[i].flags = GPIOF_OUT_INIT_LOW; + } + wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL0].flags = GPIOF_OUT_INIT_HIGH; + wm1250->gpios[WM1250_EV1_GPIO_CLK_SEL1].flags = GPIOF_OUT_INIT_HIGH; + + ret = gpio_request_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios)); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to get GPIOs: %d\n", ret); + goto err; + } + + dev_set_drvdata(&i2c->dev, wm1250); + + return ret; + +err: + return ret; +} + +static void wm1250_ev1_free(struct i2c_client *i2c) +{ + struct wm1250_priv *wm1250 = dev_get_drvdata(&i2c->dev); + + if (wm1250) + gpio_free_array(wm1250->gpios, ARRAY_SIZE(wm1250->gpios)); +} + +static int wm1250_ev1_probe(struct i2c_client *i2c) +{ + int id, board, rev, ret; + + dev_set_drvdata(&i2c->dev, NULL); + + board = i2c_smbus_read_byte_data(i2c, 0); + if (board < 0) { + dev_err(&i2c->dev, "Failed to read ID: %d\n", board); + return board; + } + + id = (board & 0xfe) >> 2; + rev = board & 0x3; + + if (id != 1) { + dev_err(&i2c->dev, "Unknown board ID %d\n", id); + return -ENODEV; + } + + dev_info(&i2c->dev, "revision %d\n", rev + 1); + + ret = wm1250_ev1_pdata(i2c); + if (ret != 0) + return ret; + + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_wm1250_ev1, + &wm1250_ev1_dai, 1); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + wm1250_ev1_free(i2c); + return ret; + } + + return 0; +} + +static void wm1250_ev1_remove(struct i2c_client *i2c) +{ + wm1250_ev1_free(i2c); +} + +static const struct i2c_device_id wm1250_ev1_i2c_id[] = { + { "wm1250-ev1", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id); + +static struct i2c_driver wm1250_ev1_i2c_driver = { + .driver = { + .name = "wm1250-ev1", + }, + .probe = wm1250_ev1_probe, + .remove = wm1250_ev1_remove, + .id_table = wm1250_ev1_i2c_id, +}; + +module_i2c_driver(wm1250_ev1_i2c_driver); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("WM1250-EV1 audio I/O module driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c new file mode 100644 index 0000000000..9571ea53cb --- /dev/null +++ b/sound/soc/codecs/wm2000.c @@ -0,0 +1,949 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm2000.c -- WM2000 ALSA Soc Audio driver + * + * Copyright 2008-2011 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * The download image for the WM2000 will be requested as + * 'wm2000_anc.bin' by default (overridable via platform data) at + * runtime and is expected to be in flat binary format. This is + * generated by Wolfson configuration tools and includes + * system-specific calibration information. If supplied as a + * sequence of ASCII-encoded hexidecimal bytes this can be converted + * into a flat binary with a command such as this on the command line: + * + * perl -e 'while (<>) { s/[\r\n]+// ; printf("%c", hex($_)); }' + * < file > wm2000_anc.bin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "wm2000.h" + +#define WM2000_NUM_SUPPLIES 3 + +static const char *wm2000_supplies[WM2000_NUM_SUPPLIES] = { + "SPKVDD", + "DBVDD", + "DCVDD", +}; + +enum wm2000_anc_mode { + ANC_ACTIVE = 0, + ANC_BYPASS = 1, + ANC_STANDBY = 2, + ANC_OFF = 3, +}; + +struct wm2000_priv { + struct i2c_client *i2c; + struct regmap *regmap; + struct clk *mclk; + + struct regulator_bulk_data supplies[WM2000_NUM_SUPPLIES]; + + enum wm2000_anc_mode anc_mode; + + unsigned int anc_active:1; + unsigned int anc_eng_ena:1; + unsigned int spk_ena:1; + + unsigned int speech_clarity:1; + + int anc_download_size; + char *anc_download; + + struct mutex lock; +}; + +static int wm2000_write(struct i2c_client *i2c, unsigned int reg, + unsigned int value) +{ + struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c); + return regmap_write(wm2000->regmap, reg, value); +} + +static void wm2000_reset(struct wm2000_priv *wm2000) +{ + struct i2c_client *i2c = wm2000->i2c; + + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR); + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR); + wm2000_write(i2c, WM2000_REG_ID1, 0); + + wm2000->anc_mode = ANC_OFF; +} + +static int wm2000_poll_bit(struct i2c_client *i2c, + unsigned int reg, u8 mask) +{ + struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c); + int timeout = 4000; + unsigned int val; + + regmap_read(wm2000->regmap, reg, &val); + + while (!(val & mask) && --timeout) { + msleep(1); + regmap_read(wm2000->regmap, reg, &val); + } + + if (timeout == 0) + return 0; + else + return 1; +} + +static int wm2000_power_up(struct i2c_client *i2c, int analogue) +{ + struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); + unsigned long rate; + unsigned int val; + int ret; + + if (WARN_ON(wm2000->anc_mode != ANC_OFF)) + return -EINVAL; + + dev_dbg(&i2c->dev, "Beginning power up\n"); + + ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + rate = clk_get_rate(wm2000->mclk); + if (rate <= 13500000) { + dev_dbg(&i2c->dev, "Disabling MCLK divider\n"); + wm2000_write(i2c, WM2000_REG_SYS_CTL2, + WM2000_MCLK_DIV2_ENA_CLR); + } else { + dev_dbg(&i2c->dev, "Enabling MCLK divider\n"); + wm2000_write(i2c, WM2000_REG_SYS_CTL2, + WM2000_MCLK_DIV2_ENA_SET); + } + + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR); + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_SET); + + /* Wait for ANC engine to become ready */ + if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, + WM2000_ANC_ENG_IDLE)) { + dev_err(&i2c->dev, "ANC engine failed to reset\n"); + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); + return -ETIMEDOUT; + } + + if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, + WM2000_STATUS_BOOT_COMPLETE)) { + dev_err(&i2c->dev, "ANC engine failed to initialise\n"); + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); + return -ETIMEDOUT; + } + + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET); + + /* Open code download of the data since it is the only bulk + * write we do. */ + dev_dbg(&i2c->dev, "Downloading %d bytes\n", + wm2000->anc_download_size - 2); + + ret = i2c_master_send(i2c, wm2000->anc_download, + wm2000->anc_download_size); + if (ret < 0) { + dev_err(&i2c->dev, "i2c_transfer() failed: %d\n", ret); + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); + return ret; + } + if (ret != wm2000->anc_download_size) { + dev_err(&i2c->dev, "i2c_transfer() failed, %d != %d\n", + ret, wm2000->anc_download_size); + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); + return -EIO; + } + + dev_dbg(&i2c->dev, "Download complete\n"); + + if (analogue) { + wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, 248 / 4); + + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_ANA_SEQ_INCLUDE | + WM2000_MODE_MOUSE_ENABLE | + WM2000_MODE_THERMAL_ENABLE); + } else { + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_MOUSE_ENABLE | + WM2000_MODE_THERMAL_ENABLE); + } + + ret = regmap_read(wm2000->regmap, WM2000_REG_SPEECH_CLARITY, &val); + if (ret != 0) { + dev_err(&i2c->dev, "Unable to read Speech Clarity: %d\n", ret); + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); + return ret; + } + if (wm2000->speech_clarity) + val |= WM2000_SPEECH_CLARITY; + else + val &= ~WM2000_SPEECH_CLARITY; + wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, val); + + wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33); + wm2000_write(i2c, WM2000_REG_SYS_START1, 0x02); + + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR); + + if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, + WM2000_STATUS_MOUSE_ACTIVE)) { + dev_err(&i2c->dev, "Timed out waiting for device\n"); + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); + return -ETIMEDOUT; + } + + dev_dbg(&i2c->dev, "ANC active\n"); + if (analogue) + dev_dbg(&i2c->dev, "Analogue active\n"); + wm2000->anc_mode = ANC_ACTIVE; + + return 0; +} + +static int wm2000_power_down(struct i2c_client *i2c, int analogue) +{ + struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); + + if (analogue) { + wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, 248 / 4); + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_ANA_SEQ_INCLUDE | + WM2000_MODE_POWER_DOWN); + } else { + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_POWER_DOWN); + } + + if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, + WM2000_STATUS_POWER_DOWN_COMPLETE)) { + dev_err(&i2c->dev, "Timeout waiting for ANC power down\n"); + return -ETIMEDOUT; + } + + if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, + WM2000_ANC_ENG_IDLE)) { + dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n"); + return -ETIMEDOUT; + } + + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); + + dev_dbg(&i2c->dev, "powered off\n"); + wm2000->anc_mode = ANC_OFF; + + return 0; +} + +static int wm2000_enter_bypass(struct i2c_client *i2c, int analogue) +{ + struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); + + if (WARN_ON(wm2000->anc_mode != ANC_ACTIVE)) + return -EINVAL; + + if (analogue) { + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_ANA_SEQ_INCLUDE | + WM2000_MODE_THERMAL_ENABLE | + WM2000_MODE_BYPASS_ENTRY); + } else { + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_THERMAL_ENABLE | + WM2000_MODE_BYPASS_ENTRY); + } + + if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, + WM2000_STATUS_ANC_DISABLED)) { + dev_err(&i2c->dev, "Timeout waiting for ANC disable\n"); + return -ETIMEDOUT; + } + + if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, + WM2000_ANC_ENG_IDLE)) { + dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n"); + return -ETIMEDOUT; + } + + wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY); + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR); + + wm2000->anc_mode = ANC_BYPASS; + dev_dbg(&i2c->dev, "bypass enabled\n"); + + return 0; +} + +static int wm2000_exit_bypass(struct i2c_client *i2c, int analogue) +{ + struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); + + if (WARN_ON(wm2000->anc_mode != ANC_BYPASS)) + return -EINVAL; + + wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0); + + if (analogue) { + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_ANA_SEQ_INCLUDE | + WM2000_MODE_MOUSE_ENABLE | + WM2000_MODE_THERMAL_ENABLE); + } else { + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_MOUSE_ENABLE | + WM2000_MODE_THERMAL_ENABLE); + } + + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET); + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR); + + if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, + WM2000_STATUS_MOUSE_ACTIVE)) { + dev_err(&i2c->dev, "Timed out waiting for MOUSE\n"); + return -ETIMEDOUT; + } + + wm2000->anc_mode = ANC_ACTIVE; + dev_dbg(&i2c->dev, "MOUSE active\n"); + + return 0; +} + +static int wm2000_enter_standby(struct i2c_client *i2c, int analogue) +{ + struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); + + if (WARN_ON(wm2000->anc_mode != ANC_ACTIVE)) + return -EINVAL; + + if (analogue) { + wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, 248 / 4); + + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_ANA_SEQ_INCLUDE | + WM2000_MODE_THERMAL_ENABLE | + WM2000_MODE_STANDBY_ENTRY); + } else { + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_THERMAL_ENABLE | + WM2000_MODE_STANDBY_ENTRY); + } + + if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, + WM2000_STATUS_ANC_DISABLED)) { + dev_err(&i2c->dev, + "Timed out waiting for ANC disable after 1ms\n"); + return -ETIMEDOUT; + } + + if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, WM2000_ANC_ENG_IDLE)) { + dev_err(&i2c->dev, + "Timed out waiting for standby\n"); + return -ETIMEDOUT; + } + + wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY); + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR); + + wm2000->anc_mode = ANC_STANDBY; + dev_dbg(&i2c->dev, "standby\n"); + if (analogue) + dev_dbg(&i2c->dev, "Analogue disabled\n"); + + return 0; +} + +static int wm2000_exit_standby(struct i2c_client *i2c, int analogue) +{ + struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); + + if (WARN_ON(wm2000->anc_mode != ANC_STANDBY)) + return -EINVAL; + + wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0); + + if (analogue) { + wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, 248 / 4); + + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_ANA_SEQ_INCLUDE | + WM2000_MODE_THERMAL_ENABLE | + WM2000_MODE_MOUSE_ENABLE); + } else { + wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL, + WM2000_MODE_THERMAL_ENABLE | + WM2000_MODE_MOUSE_ENABLE); + } + + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET); + wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR); + + if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS, + WM2000_STATUS_MOUSE_ACTIVE)) { + dev_err(&i2c->dev, "Timed out waiting for MOUSE\n"); + return -ETIMEDOUT; + } + + wm2000->anc_mode = ANC_ACTIVE; + dev_dbg(&i2c->dev, "MOUSE active\n"); + if (analogue) + dev_dbg(&i2c->dev, "Analogue enabled\n"); + + return 0; +} + +typedef int (*wm2000_mode_fn)(struct i2c_client *i2c, int analogue); + +static struct { + enum wm2000_anc_mode source; + enum wm2000_anc_mode dest; + int analogue; + wm2000_mode_fn step[2]; +} anc_transitions[] = { + { + .source = ANC_OFF, + .dest = ANC_ACTIVE, + .analogue = 1, + .step = { + wm2000_power_up, + }, + }, + { + .source = ANC_OFF, + .dest = ANC_STANDBY, + .step = { + wm2000_power_up, + wm2000_enter_standby, + }, + }, + { + .source = ANC_OFF, + .dest = ANC_BYPASS, + .analogue = 1, + .step = { + wm2000_power_up, + wm2000_enter_bypass, + }, + }, + { + .source = ANC_ACTIVE, + .dest = ANC_BYPASS, + .analogue = 1, + .step = { + wm2000_enter_bypass, + }, + }, + { + .source = ANC_ACTIVE, + .dest = ANC_STANDBY, + .analogue = 1, + .step = { + wm2000_enter_standby, + }, + }, + { + .source = ANC_ACTIVE, + .dest = ANC_OFF, + .analogue = 1, + .step = { + wm2000_power_down, + }, + }, + { + .source = ANC_BYPASS, + .dest = ANC_ACTIVE, + .analogue = 1, + .step = { + wm2000_exit_bypass, + }, + }, + { + .source = ANC_BYPASS, + .dest = ANC_STANDBY, + .analogue = 1, + .step = { + wm2000_exit_bypass, + wm2000_enter_standby, + }, + }, + { + .source = ANC_BYPASS, + .dest = ANC_OFF, + .step = { + wm2000_exit_bypass, + wm2000_power_down, + }, + }, + { + .source = ANC_STANDBY, + .dest = ANC_ACTIVE, + .analogue = 1, + .step = { + wm2000_exit_standby, + }, + }, + { + .source = ANC_STANDBY, + .dest = ANC_BYPASS, + .analogue = 1, + .step = { + wm2000_exit_standby, + wm2000_enter_bypass, + }, + }, + { + .source = ANC_STANDBY, + .dest = ANC_OFF, + .step = { + wm2000_exit_standby, + wm2000_power_down, + }, + }, +}; + +static int wm2000_anc_transition(struct wm2000_priv *wm2000, + enum wm2000_anc_mode mode) +{ + struct i2c_client *i2c = wm2000->i2c; + int i, j; + int ret = 0; + + if (wm2000->anc_mode == mode) + return 0; + + for (i = 0; i < ARRAY_SIZE(anc_transitions); i++) + if (anc_transitions[i].source == wm2000->anc_mode && + anc_transitions[i].dest == mode) + break; + if (i == ARRAY_SIZE(anc_transitions)) { + dev_err(&i2c->dev, "No transition for %d->%d\n", + wm2000->anc_mode, mode); + return -EINVAL; + } + + /* Maintain clock while active */ + if (anc_transitions[i].source == ANC_OFF) { + ret = clk_prepare_enable(wm2000->mclk); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable MCLK: %d\n", ret); + return ret; + } + } + + for (j = 0; j < ARRAY_SIZE(anc_transitions[j].step); j++) { + if (!anc_transitions[i].step[j]) + break; + ret = anc_transitions[i].step[j](i2c, + anc_transitions[i].analogue); + if (ret != 0) + break; + } + + if (anc_transitions[i].dest == ANC_OFF) + clk_disable_unprepare(wm2000->mclk); + + return ret; +} + +static int wm2000_anc_set_mode(struct wm2000_priv *wm2000) +{ + struct i2c_client *i2c = wm2000->i2c; + enum wm2000_anc_mode mode; + + if (wm2000->anc_eng_ena && wm2000->spk_ena) + if (wm2000->anc_active) + mode = ANC_ACTIVE; + else + mode = ANC_BYPASS; + else + mode = ANC_STANDBY; + + dev_dbg(&i2c->dev, "Set mode %d (enabled %d, mute %d, active %d)\n", + mode, wm2000->anc_eng_ena, !wm2000->spk_ena, + wm2000->anc_active); + + return wm2000_anc_transition(wm2000, mode); +} + +static int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + + ucontrol->value.integer.value[0] = wm2000->anc_active; + + return 0; +} + +static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + unsigned int anc_active = ucontrol->value.integer.value[0]; + int ret; + + if (anc_active > 1) + return -EINVAL; + + mutex_lock(&wm2000->lock); + + wm2000->anc_active = anc_active; + + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; +} + +static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + + ucontrol->value.integer.value[0] = wm2000->spk_ena; + + return 0; +} + +static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + unsigned int val = ucontrol->value.integer.value[0]; + int ret; + + if (val > 1) + return -EINVAL; + + mutex_lock(&wm2000->lock); + + wm2000->spk_ena = val; + + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; +} + +static const struct snd_kcontrol_new wm2000_controls[] = { + SOC_SINGLE("ANC Volume", WM2000_REG_ANC_GAIN_CTRL, 0, 255, 0), + SOC_SINGLE_BOOL_EXT("WM2000 ANC Switch", 0, + wm2000_anc_mode_get, + wm2000_anc_mode_put), + SOC_SINGLE_BOOL_EXT("WM2000 Switch", 0, + wm2000_speaker_get, + wm2000_speaker_put), +}; + +static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + int ret; + + mutex_lock(&wm2000->lock); + + if (SND_SOC_DAPM_EVENT_ON(event)) + wm2000->anc_eng_ena = 1; + + if (SND_SOC_DAPM_EVENT_OFF(event)) + wm2000->anc_eng_ena = 0; + + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; +} + +static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { +/* Externally visible pins */ +SND_SOC_DAPM_OUTPUT("SPKN"), +SND_SOC_DAPM_OUTPUT("SPKP"), + +SND_SOC_DAPM_INPUT("LINN"), +SND_SOC_DAPM_INPUT("LINP"), + +SND_SOC_DAPM_PGA_E("ANC Engine", SND_SOC_NOPM, 0, 0, NULL, 0, + wm2000_anc_power_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +}; + +/* Target, Path, Source */ +static const struct snd_soc_dapm_route wm2000_audio_map[] = { + { "SPKN", NULL, "ANC Engine" }, + { "SPKP", NULL, "ANC Engine" }, + { "ANC Engine", NULL, "LINN" }, + { "ANC Engine", NULL, "LINP" }, +}; + +#ifdef CONFIG_PM +static int wm2000_suspend(struct snd_soc_component *component) +{ + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + + return wm2000_anc_transition(wm2000, ANC_OFF); +} + +static int wm2000_resume(struct snd_soc_component *component) +{ + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + + return wm2000_anc_set_mode(wm2000); +} +#else +#define wm2000_suspend NULL +#define wm2000_resume NULL +#endif + +static bool wm2000_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM2000_REG_SYS_START: + case WM2000_REG_ANC_GAIN_CTRL: + case WM2000_REG_MSE_TH1: + case WM2000_REG_MSE_TH2: + case WM2000_REG_SPEECH_CLARITY: + case WM2000_REG_SYS_WATCHDOG: + case WM2000_REG_ANA_VMID_PD_TIME: + case WM2000_REG_ANA_VMID_PU_TIME: + case WM2000_REG_CAT_FLTR_INDX: + case WM2000_REG_CAT_GAIN_0: + case WM2000_REG_SYS_STATUS: + case WM2000_REG_SYS_MODE_CNTRL: + case WM2000_REG_SYS_START0: + case WM2000_REG_SYS_START1: + case WM2000_REG_ID1: + case WM2000_REG_ID2: + case WM2000_REG_REVISON: + case WM2000_REG_SYS_CTL1: + case WM2000_REG_SYS_CTL2: + case WM2000_REG_ANC_STAT: + case WM2000_REG_IF_CTL: + case WM2000_REG_ANA_MIC_CTL: + case WM2000_REG_SPK_CTL: + return true; + default: + return false; + } +} + +static const struct regmap_config wm2000_regmap = { + .reg_bits = 16, + .val_bits = 8, + + .max_register = WM2000_REG_SPK_CTL, + .readable_reg = wm2000_readable_reg, +}; + +static int wm2000_probe(struct snd_soc_component *component) +{ + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + + /* This will trigger a transition to standby mode by default */ + wm2000_anc_set_mode(wm2000); + + return 0; +} + +static void wm2000_remove(struct snd_soc_component *component) +{ + struct wm2000_priv *wm2000 = dev_get_drvdata(component->dev); + + wm2000_anc_transition(wm2000, ANC_OFF); +} + +static const struct snd_soc_component_driver soc_component_dev_wm2000 = { + .probe = wm2000_probe, + .remove = wm2000_remove, + .suspend = wm2000_suspend, + .resume = wm2000_resume, + .controls = wm2000_controls, + .num_controls = ARRAY_SIZE(wm2000_controls), + .dapm_widgets = wm2000_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm2000_dapm_widgets), + .dapm_routes = wm2000_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm2000_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, +}; + +static int wm2000_i2c_probe(struct i2c_client *i2c) +{ + struct wm2000_priv *wm2000; + struct wm2000_platform_data *pdata; + const char *filename; + const struct firmware *fw = NULL; + int ret, i; + unsigned int reg; + u16 id; + + wm2000 = devm_kzalloc(&i2c->dev, sizeof(*wm2000), GFP_KERNEL); + if (!wm2000) + return -ENOMEM; + + mutex_init(&wm2000->lock); + + dev_set_drvdata(&i2c->dev, wm2000); + + wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap); + if (IS_ERR(wm2000->regmap)) { + ret = PTR_ERR(wm2000->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + goto out; + } + + for (i = 0; i < WM2000_NUM_SUPPLIES; i++) + wm2000->supplies[i].supply = wm2000_supplies[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, WM2000_NUM_SUPPLIES, + wm2000->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to get supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + /* Verify that this is a WM2000 */ + ret = regmap_read(wm2000->regmap, WM2000_REG_ID1, ®); + if (ret != 0) { + dev_err(&i2c->dev, "Unable to read ID1: %d\n", ret); + return ret; + } + id = reg << 8; + ret = regmap_read(wm2000->regmap, WM2000_REG_ID2, ®); + if (ret != 0) { + dev_err(&i2c->dev, "Unable to read ID2: %d\n", ret); + return ret; + } + id |= reg & 0xff; + + if (id != 0x2000) { + dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id); + ret = -ENODEV; + goto err_supplies; + } + + ret = regmap_read(wm2000->regmap, WM2000_REG_REVISON, ®); + if (ret != 0) { + dev_err(&i2c->dev, "Unable to read Revision: %d\n", ret); + return ret; + } + dev_info(&i2c->dev, "revision %c\n", reg + 'A'); + + wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK"); + if (IS_ERR(wm2000->mclk)) { + ret = PTR_ERR(wm2000->mclk); + dev_err(&i2c->dev, "Failed to get MCLK: %d\n", ret); + goto err_supplies; + } + + filename = "wm2000_anc.bin"; + pdata = dev_get_platdata(&i2c->dev); + if (pdata) { + wm2000->speech_clarity = !pdata->speech_enh_disable; + + if (pdata->download_file) + filename = pdata->download_file; + } + + ret = request_firmware(&fw, filename, &i2c->dev); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret); + goto err_supplies; + } + + /* Pre-cook the concatenation of the register address onto the image */ + wm2000->anc_download_size = fw->size + 2; + wm2000->anc_download = devm_kzalloc(&i2c->dev, + wm2000->anc_download_size, + GFP_KERNEL); + if (wm2000->anc_download == NULL) { + ret = -ENOMEM; + goto err_supplies; + } + + wm2000->anc_download[0] = 0x80; + wm2000->anc_download[1] = 0x00; + memcpy(wm2000->anc_download + 2, fw->data, fw->size); + + wm2000->anc_eng_ena = 1; + wm2000->anc_active = 1; + wm2000->spk_ena = 1; + wm2000->i2c = i2c; + + wm2000_reset(wm2000); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm2000, NULL, 0); + +err_supplies: + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); + +out: + release_firmware(fw); + return ret; +} + +static const struct i2c_device_id wm2000_i2c_id[] = { + { "wm2000", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm2000_i2c_id); + +static struct i2c_driver wm2000_i2c_driver = { + .driver = { + .name = "wm2000", + }, + .probe = wm2000_i2c_probe, + .id_table = wm2000_i2c_id, +}; + +module_i2c_driver(wm2000_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM2000 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm2000.h b/sound/soc/codecs/wm2000.h new file mode 100644 index 0000000000..6d3241dea0 --- /dev/null +++ b/sound/soc/codecs/wm2000.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm2000.h -- WM2000 Soc Audio driver + */ + +#ifndef _WM2000_H +#define _WM2000_H + +#define WM2000_REG_SYS_START 0x8000 +#define WM2000_REG_ANC_GAIN_CTRL 0x8fa2 +#define WM2000_REG_MSE_TH2 0x8fdf +#define WM2000_REG_MSE_TH1 0x8fe0 +#define WM2000_REG_SPEECH_CLARITY 0x8fef +#define WM2000_REG_SYS_WATCHDOG 0x8ff6 +#define WM2000_REG_ANA_VMID_PD_TIME 0x8ff7 +#define WM2000_REG_ANA_VMID_PU_TIME 0x8ff8 +#define WM2000_REG_CAT_FLTR_INDX 0x8ff9 +#define WM2000_REG_CAT_GAIN_0 0x8ffa +#define WM2000_REG_SYS_STATUS 0x8ffc +#define WM2000_REG_SYS_MODE_CNTRL 0x8ffd +#define WM2000_REG_SYS_START0 0x8ffe +#define WM2000_REG_SYS_START1 0x8fff +#define WM2000_REG_ID1 0xf000 +#define WM2000_REG_ID2 0xf001 +#define WM2000_REG_REVISON 0xf002 +#define WM2000_REG_SYS_CTL1 0xf003 +#define WM2000_REG_SYS_CTL2 0xf004 +#define WM2000_REG_ANC_STAT 0xf005 +#define WM2000_REG_IF_CTL 0xf006 +#define WM2000_REG_ANA_MIC_CTL 0xf028 +#define WM2000_REG_SPK_CTL 0xf034 + +/* SPEECH_CLARITY */ +#define WM2000_SPEECH_CLARITY 0x01 + +/* SYS_STATUS */ +#define WM2000_STATUS_MOUSE_ACTIVE 0x40 +#define WM2000_STATUS_CAT_FREQ_COMPLETE 0x20 +#define WM2000_STATUS_CAT_GAIN_COMPLETE 0x10 +#define WM2000_STATUS_THERMAL_SHUTDOWN_COMPLETE 0x08 +#define WM2000_STATUS_ANC_DISABLED 0x04 +#define WM2000_STATUS_POWER_DOWN_COMPLETE 0x02 +#define WM2000_STATUS_BOOT_COMPLETE 0x01 + +/* SYS_MODE_CNTRL */ +#define WM2000_MODE_ANA_SEQ_INCLUDE 0x80 +#define WM2000_MODE_MOUSE_ENABLE 0x40 +#define WM2000_MODE_CAT_FREQ_ENABLE 0x20 +#define WM2000_MODE_CAT_GAIN_ENABLE 0x10 +#define WM2000_MODE_BYPASS_ENTRY 0x08 +#define WM2000_MODE_STANDBY_ENTRY 0x04 +#define WM2000_MODE_THERMAL_ENABLE 0x02 +#define WM2000_MODE_POWER_DOWN 0x01 + +/* SYS_CTL1 */ +#define WM2000_SYS_STBY 0x01 + +/* SYS_CTL2 */ +#define WM2000_MCLK_DIV2_ENA_CLR 0x80 +#define WM2000_MCLK_DIV2_ENA_SET 0x40 +#define WM2000_ANC_ENG_CLR 0x20 +#define WM2000_ANC_ENG_SET 0x10 +#define WM2000_ANC_INT_N_CLR 0x08 +#define WM2000_ANC_INT_N_SET 0x04 +#define WM2000_RAM_CLR 0x02 +#define WM2000_RAM_SET 0x01 + +/* ANC_STAT */ +#define WM2000_ANC_ENG_IDLE 0x01 + +#endif diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c new file mode 100644 index 0000000000..9679906c6b --- /dev/null +++ b/sound/soc/codecs/wm2200.c @@ -0,0 +1,2497 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm2200.c -- WM2200 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm2200.h" +#include "wm_adsp.h" + +#define WM2200_DSP_CONTROL_1 0x00 +#define WM2200_DSP_CONTROL_2 0x02 +#define WM2200_DSP_CONTROL_3 0x03 +#define WM2200_DSP_CONTROL_4 0x04 +#define WM2200_DSP_CONTROL_5 0x06 +#define WM2200_DSP_CONTROL_6 0x07 +#define WM2200_DSP_CONTROL_7 0x08 +#define WM2200_DSP_CONTROL_8 0x09 +#define WM2200_DSP_CONTROL_9 0x0A +#define WM2200_DSP_CONTROL_10 0x0B +#define WM2200_DSP_CONTROL_11 0x0C +#define WM2200_DSP_CONTROL_12 0x0D +#define WM2200_DSP_CONTROL_13 0x0F +#define WM2200_DSP_CONTROL_14 0x10 +#define WM2200_DSP_CONTROL_15 0x11 +#define WM2200_DSP_CONTROL_16 0x12 +#define WM2200_DSP_CONTROL_17 0x13 +#define WM2200_DSP_CONTROL_18 0x14 +#define WM2200_DSP_CONTROL_19 0x16 +#define WM2200_DSP_CONTROL_20 0x17 +#define WM2200_DSP_CONTROL_21 0x18 +#define WM2200_DSP_CONTROL_22 0x1A +#define WM2200_DSP_CONTROL_23 0x1B +#define WM2200_DSP_CONTROL_24 0x1C +#define WM2200_DSP_CONTROL_25 0x1E +#define WM2200_DSP_CONTROL_26 0x20 +#define WM2200_DSP_CONTROL_27 0x21 +#define WM2200_DSP_CONTROL_28 0x22 +#define WM2200_DSP_CONTROL_29 0x23 +#define WM2200_DSP_CONTROL_30 0x24 +#define WM2200_DSP_CONTROL_31 0x26 + +/* The code assumes DCVDD is generated internally */ +#define WM2200_NUM_CORE_SUPPLIES 2 +static const char *wm2200_core_supply_names[WM2200_NUM_CORE_SUPPLIES] = { + "DBVDD", + "LDOVDD", +}; + +/* codec private data */ +struct wm2200_priv { + struct wm_adsp dsp[2]; + struct regmap *regmap; + struct device *dev; + struct snd_soc_component *component; + struct wm2200_pdata pdata; + struct regulator_bulk_data core_supplies[WM2200_NUM_CORE_SUPPLIES]; + + struct completion fll_lock; + int fll_fout; + int fll_fref; + int fll_src; + + int rev; + int sysclk; + + unsigned int symmetric_rates:1; +}; + +#define WM2200_DSP_RANGE_BASE (WM2200_MAX_REGISTER + 1) +#define WM2200_DSP_SPACING 12288 + +#define WM2200_DSP1_DM_BASE (WM2200_DSP_RANGE_BASE + (0 * WM2200_DSP_SPACING)) +#define WM2200_DSP1_PM_BASE (WM2200_DSP_RANGE_BASE + (1 * WM2200_DSP_SPACING)) +#define WM2200_DSP1_ZM_BASE (WM2200_DSP_RANGE_BASE + (2 * WM2200_DSP_SPACING)) +#define WM2200_DSP2_DM_BASE (WM2200_DSP_RANGE_BASE + (3 * WM2200_DSP_SPACING)) +#define WM2200_DSP2_PM_BASE (WM2200_DSP_RANGE_BASE + (4 * WM2200_DSP_SPACING)) +#define WM2200_DSP2_ZM_BASE (WM2200_DSP_RANGE_BASE + (5 * WM2200_DSP_SPACING)) + +static const struct regmap_range_cfg wm2200_ranges[] = { + { .name = "DSP1DM", .range_min = WM2200_DSP1_DM_BASE, + .range_max = WM2200_DSP1_DM_BASE + 12287, + .selector_reg = WM2200_DSP1_CONTROL_3, + .selector_mask = WM2200_DSP1_PAGE_BASE_DM_0_MASK, + .selector_shift = WM2200_DSP1_PAGE_BASE_DM_0_SHIFT, + .window_start = WM2200_DSP1_DM_0, .window_len = 2048, }, + + { .name = "DSP1PM", .range_min = WM2200_DSP1_PM_BASE, + .range_max = WM2200_DSP1_PM_BASE + 12287, + .selector_reg = WM2200_DSP1_CONTROL_2, + .selector_mask = WM2200_DSP1_PAGE_BASE_PM_0_MASK, + .selector_shift = WM2200_DSP1_PAGE_BASE_PM_0_SHIFT, + .window_start = WM2200_DSP1_PM_0, .window_len = 768, }, + + { .name = "DSP1ZM", .range_min = WM2200_DSP1_ZM_BASE, + .range_max = WM2200_DSP1_ZM_BASE + 2047, + .selector_reg = WM2200_DSP1_CONTROL_4, + .selector_mask = WM2200_DSP1_PAGE_BASE_ZM_0_MASK, + .selector_shift = WM2200_DSP1_PAGE_BASE_ZM_0_SHIFT, + .window_start = WM2200_DSP1_ZM_0, .window_len = 1024, }, + + { .name = "DSP2DM", .range_min = WM2200_DSP2_DM_BASE, + .range_max = WM2200_DSP2_DM_BASE + 4095, + .selector_reg = WM2200_DSP2_CONTROL_3, + .selector_mask = WM2200_DSP2_PAGE_BASE_DM_0_MASK, + .selector_shift = WM2200_DSP2_PAGE_BASE_DM_0_SHIFT, + .window_start = WM2200_DSP2_DM_0, .window_len = 2048, }, + + { .name = "DSP2PM", .range_min = WM2200_DSP2_PM_BASE, + .range_max = WM2200_DSP2_PM_BASE + 11287, + .selector_reg = WM2200_DSP2_CONTROL_2, + .selector_mask = WM2200_DSP2_PAGE_BASE_PM_0_MASK, + .selector_shift = WM2200_DSP2_PAGE_BASE_PM_0_SHIFT, + .window_start = WM2200_DSP2_PM_0, .window_len = 768, }, + + { .name = "DSP2ZM", .range_min = WM2200_DSP2_ZM_BASE, + .range_max = WM2200_DSP2_ZM_BASE + 2047, + .selector_reg = WM2200_DSP2_CONTROL_4, + .selector_mask = WM2200_DSP2_PAGE_BASE_ZM_0_MASK, + .selector_shift = WM2200_DSP2_PAGE_BASE_ZM_0_SHIFT, + .window_start = WM2200_DSP2_ZM_0, .window_len = 1024, }, +}; + +static const struct cs_dsp_region wm2200_dsp1_regions[] = { + { .type = WMFW_ADSP1_PM, .base = WM2200_DSP1_PM_BASE }, + { .type = WMFW_ADSP1_DM, .base = WM2200_DSP1_DM_BASE }, + { .type = WMFW_ADSP1_ZM, .base = WM2200_DSP1_ZM_BASE }, +}; + +static const struct cs_dsp_region wm2200_dsp2_regions[] = { + { .type = WMFW_ADSP1_PM, .base = WM2200_DSP2_PM_BASE }, + { .type = WMFW_ADSP1_DM, .base = WM2200_DSP2_DM_BASE }, + { .type = WMFW_ADSP1_ZM, .base = WM2200_DSP2_ZM_BASE }, +}; + +static const struct reg_default wm2200_reg_defaults[] = { + { 0x000B, 0x0000 }, /* R11 - Tone Generator 1 */ + { 0x0102, 0x0000 }, /* R258 - Clocking 3 */ + { 0x0103, 0x0011 }, /* R259 - Clocking 4 */ + { 0x0111, 0x0000 }, /* R273 - FLL Control 1 */ + { 0x0112, 0x0000 }, /* R274 - FLL Control 2 */ + { 0x0113, 0x0000 }, /* R275 - FLL Control 3 */ + { 0x0114, 0x0000 }, /* R276 - FLL Control 4 */ + { 0x0116, 0x0177 }, /* R278 - FLL Control 6 */ + { 0x0117, 0x0004 }, /* R279 - FLL Control 7 */ + { 0x0119, 0x0000 }, /* R281 - FLL EFS 1 */ + { 0x011A, 0x0002 }, /* R282 - FLL EFS 2 */ + { 0x0200, 0x0000 }, /* R512 - Mic Charge Pump 1 */ + { 0x0201, 0x03FF }, /* R513 - Mic Charge Pump 2 */ + { 0x0202, 0x9BDE }, /* R514 - DM Charge Pump 1 */ + { 0x020C, 0x0000 }, /* R524 - Mic Bias Ctrl 1 */ + { 0x020D, 0x0000 }, /* R525 - Mic Bias Ctrl 2 */ + { 0x020F, 0x0000 }, /* R527 - Ear Piece Ctrl 1 */ + { 0x0210, 0x0000 }, /* R528 - Ear Piece Ctrl 2 */ + { 0x0301, 0x0000 }, /* R769 - Input Enables */ + { 0x0302, 0x2240 }, /* R770 - IN1L Control */ + { 0x0303, 0x0040 }, /* R771 - IN1R Control */ + { 0x0304, 0x2240 }, /* R772 - IN2L Control */ + { 0x0305, 0x0040 }, /* R773 - IN2R Control */ + { 0x0306, 0x2240 }, /* R774 - IN3L Control */ + { 0x0307, 0x0040 }, /* R775 - IN3R Control */ + { 0x030A, 0x0000 }, /* R778 - RXANC_SRC */ + { 0x030B, 0x0022 }, /* R779 - Input Volume Ramp */ + { 0x030C, 0x0180 }, /* R780 - ADC Digital Volume 1L */ + { 0x030D, 0x0180 }, /* R781 - ADC Digital Volume 1R */ + { 0x030E, 0x0180 }, /* R782 - ADC Digital Volume 2L */ + { 0x030F, 0x0180 }, /* R783 - ADC Digital Volume 2R */ + { 0x0310, 0x0180 }, /* R784 - ADC Digital Volume 3L */ + { 0x0311, 0x0180 }, /* R785 - ADC Digital Volume 3R */ + { 0x0400, 0x0000 }, /* R1024 - Output Enables */ + { 0x0401, 0x0000 }, /* R1025 - DAC Volume Limit 1L */ + { 0x0402, 0x0000 }, /* R1026 - DAC Volume Limit 1R */ + { 0x0403, 0x0000 }, /* R1027 - DAC Volume Limit 2L */ + { 0x0404, 0x0000 }, /* R1028 - DAC Volume Limit 2R */ + { 0x0409, 0x0000 }, /* R1033 - DAC AEC Control 1 */ + { 0x040A, 0x0022 }, /* R1034 - Output Volume Ramp */ + { 0x040B, 0x0180 }, /* R1035 - DAC Digital Volume 1L */ + { 0x040C, 0x0180 }, /* R1036 - DAC Digital Volume 1R */ + { 0x040D, 0x0180 }, /* R1037 - DAC Digital Volume 2L */ + { 0x040E, 0x0180 }, /* R1038 - DAC Digital Volume 2R */ + { 0x0417, 0x0069 }, /* R1047 - PDM 1 */ + { 0x0418, 0x0000 }, /* R1048 - PDM 2 */ + { 0x0500, 0x0000 }, /* R1280 - Audio IF 1_1 */ + { 0x0501, 0x0008 }, /* R1281 - Audio IF 1_2 */ + { 0x0502, 0x0000 }, /* R1282 - Audio IF 1_3 */ + { 0x0503, 0x0000 }, /* R1283 - Audio IF 1_4 */ + { 0x0504, 0x0000 }, /* R1284 - Audio IF 1_5 */ + { 0x0505, 0x0001 }, /* R1285 - Audio IF 1_6 */ + { 0x0506, 0x0001 }, /* R1286 - Audio IF 1_7 */ + { 0x0507, 0x0000 }, /* R1287 - Audio IF 1_8 */ + { 0x0508, 0x0000 }, /* R1288 - Audio IF 1_9 */ + { 0x0509, 0x0000 }, /* R1289 - Audio IF 1_10 */ + { 0x050A, 0x0000 }, /* R1290 - Audio IF 1_11 */ + { 0x050B, 0x0000 }, /* R1291 - Audio IF 1_12 */ + { 0x050C, 0x0000 }, /* R1292 - Audio IF 1_13 */ + { 0x050D, 0x0000 }, /* R1293 - Audio IF 1_14 */ + { 0x050E, 0x0000 }, /* R1294 - Audio IF 1_15 */ + { 0x050F, 0x0000 }, /* R1295 - Audio IF 1_16 */ + { 0x0510, 0x0000 }, /* R1296 - Audio IF 1_17 */ + { 0x0511, 0x0000 }, /* R1297 - Audio IF 1_18 */ + { 0x0512, 0x0000 }, /* R1298 - Audio IF 1_19 */ + { 0x0513, 0x0000 }, /* R1299 - Audio IF 1_20 */ + { 0x0514, 0x0000 }, /* R1300 - Audio IF 1_21 */ + { 0x0515, 0x0001 }, /* R1301 - Audio IF 1_22 */ + { 0x0600, 0x0000 }, /* R1536 - OUT1LMIX Input 1 Source */ + { 0x0601, 0x0080 }, /* R1537 - OUT1LMIX Input 1 Volume */ + { 0x0602, 0x0000 }, /* R1538 - OUT1LMIX Input 2 Source */ + { 0x0603, 0x0080 }, /* R1539 - OUT1LMIX Input 2 Volume */ + { 0x0604, 0x0000 }, /* R1540 - OUT1LMIX Input 3 Source */ + { 0x0605, 0x0080 }, /* R1541 - OUT1LMIX Input 3 Volume */ + { 0x0606, 0x0000 }, /* R1542 - OUT1LMIX Input 4 Source */ + { 0x0607, 0x0080 }, /* R1543 - OUT1LMIX Input 4 Volume */ + { 0x0608, 0x0000 }, /* R1544 - OUT1RMIX Input 1 Source */ + { 0x0609, 0x0080 }, /* R1545 - OUT1RMIX Input 1 Volume */ + { 0x060A, 0x0000 }, /* R1546 - OUT1RMIX Input 2 Source */ + { 0x060B, 0x0080 }, /* R1547 - OUT1RMIX Input 2 Volume */ + { 0x060C, 0x0000 }, /* R1548 - OUT1RMIX Input 3 Source */ + { 0x060D, 0x0080 }, /* R1549 - OUT1RMIX Input 3 Volume */ + { 0x060E, 0x0000 }, /* R1550 - OUT1RMIX Input 4 Source */ + { 0x060F, 0x0080 }, /* R1551 - OUT1RMIX Input 4 Volume */ + { 0x0610, 0x0000 }, /* R1552 - OUT2LMIX Input 1 Source */ + { 0x0611, 0x0080 }, /* R1553 - OUT2LMIX Input 1 Volume */ + { 0x0612, 0x0000 }, /* R1554 - OUT2LMIX Input 2 Source */ + { 0x0613, 0x0080 }, /* R1555 - OUT2LMIX Input 2 Volume */ + { 0x0614, 0x0000 }, /* R1556 - OUT2LMIX Input 3 Source */ + { 0x0615, 0x0080 }, /* R1557 - OUT2LMIX Input 3 Volume */ + { 0x0616, 0x0000 }, /* R1558 - OUT2LMIX Input 4 Source */ + { 0x0617, 0x0080 }, /* R1559 - OUT2LMIX Input 4 Volume */ + { 0x0618, 0x0000 }, /* R1560 - OUT2RMIX Input 1 Source */ + { 0x0619, 0x0080 }, /* R1561 - OUT2RMIX Input 1 Volume */ + { 0x061A, 0x0000 }, /* R1562 - OUT2RMIX Input 2 Source */ + { 0x061B, 0x0080 }, /* R1563 - OUT2RMIX Input 2 Volume */ + { 0x061C, 0x0000 }, /* R1564 - OUT2RMIX Input 3 Source */ + { 0x061D, 0x0080 }, /* R1565 - OUT2RMIX Input 3 Volume */ + { 0x061E, 0x0000 }, /* R1566 - OUT2RMIX Input 4 Source */ + { 0x061F, 0x0080 }, /* R1567 - OUT2RMIX Input 4 Volume */ + { 0x0620, 0x0000 }, /* R1568 - AIF1TX1MIX Input 1 Source */ + { 0x0621, 0x0080 }, /* R1569 - AIF1TX1MIX Input 1 Volume */ + { 0x0622, 0x0000 }, /* R1570 - AIF1TX1MIX Input 2 Source */ + { 0x0623, 0x0080 }, /* R1571 - AIF1TX1MIX Input 2 Volume */ + { 0x0624, 0x0000 }, /* R1572 - AIF1TX1MIX Input 3 Source */ + { 0x0625, 0x0080 }, /* R1573 - AIF1TX1MIX Input 3 Volume */ + { 0x0626, 0x0000 }, /* R1574 - AIF1TX1MIX Input 4 Source */ + { 0x0627, 0x0080 }, /* R1575 - AIF1TX1MIX Input 4 Volume */ + { 0x0628, 0x0000 }, /* R1576 - AIF1TX2MIX Input 1 Source */ + { 0x0629, 0x0080 }, /* R1577 - AIF1TX2MIX Input 1 Volume */ + { 0x062A, 0x0000 }, /* R1578 - AIF1TX2MIX Input 2 Source */ + { 0x062B, 0x0080 }, /* R1579 - AIF1TX2MIX Input 2 Volume */ + { 0x062C, 0x0000 }, /* R1580 - AIF1TX2MIX Input 3 Source */ + { 0x062D, 0x0080 }, /* R1581 - AIF1TX2MIX Input 3 Volume */ + { 0x062E, 0x0000 }, /* R1582 - AIF1TX2MIX Input 4 Source */ + { 0x062F, 0x0080 }, /* R1583 - AIF1TX2MIX Input 4 Volume */ + { 0x0630, 0x0000 }, /* R1584 - AIF1TX3MIX Input 1 Source */ + { 0x0631, 0x0080 }, /* R1585 - AIF1TX3MIX Input 1 Volume */ + { 0x0632, 0x0000 }, /* R1586 - AIF1TX3MIX Input 2 Source */ + { 0x0633, 0x0080 }, /* R1587 - AIF1TX3MIX Input 2 Volume */ + { 0x0634, 0x0000 }, /* R1588 - AIF1TX3MIX Input 3 Source */ + { 0x0635, 0x0080 }, /* R1589 - AIF1TX3MIX Input 3 Volume */ + { 0x0636, 0x0000 }, /* R1590 - AIF1TX3MIX Input 4 Source */ + { 0x0637, 0x0080 }, /* R1591 - AIF1TX3MIX Input 4 Volume */ + { 0x0638, 0x0000 }, /* R1592 - AIF1TX4MIX Input 1 Source */ + { 0x0639, 0x0080 }, /* R1593 - AIF1TX4MIX Input 1 Volume */ + { 0x063A, 0x0000 }, /* R1594 - AIF1TX4MIX Input 2 Source */ + { 0x063B, 0x0080 }, /* R1595 - AIF1TX4MIX Input 2 Volume */ + { 0x063C, 0x0000 }, /* R1596 - AIF1TX4MIX Input 3 Source */ + { 0x063D, 0x0080 }, /* R1597 - AIF1TX4MIX Input 3 Volume */ + { 0x063E, 0x0000 }, /* R1598 - AIF1TX4MIX Input 4 Source */ + { 0x063F, 0x0080 }, /* R1599 - AIF1TX4MIX Input 4 Volume */ + { 0x0640, 0x0000 }, /* R1600 - AIF1TX5MIX Input 1 Source */ + { 0x0641, 0x0080 }, /* R1601 - AIF1TX5MIX Input 1 Volume */ + { 0x0642, 0x0000 }, /* R1602 - AIF1TX5MIX Input 2 Source */ + { 0x0643, 0x0080 }, /* R1603 - AIF1TX5MIX Input 2 Volume */ + { 0x0644, 0x0000 }, /* R1604 - AIF1TX5MIX Input 3 Source */ + { 0x0645, 0x0080 }, /* R1605 - AIF1TX5MIX Input 3 Volume */ + { 0x0646, 0x0000 }, /* R1606 - AIF1TX5MIX Input 4 Source */ + { 0x0647, 0x0080 }, /* R1607 - AIF1TX5MIX Input 4 Volume */ + { 0x0648, 0x0000 }, /* R1608 - AIF1TX6MIX Input 1 Source */ + { 0x0649, 0x0080 }, /* R1609 - AIF1TX6MIX Input 1 Volume */ + { 0x064A, 0x0000 }, /* R1610 - AIF1TX6MIX Input 2 Source */ + { 0x064B, 0x0080 }, /* R1611 - AIF1TX6MIX Input 2 Volume */ + { 0x064C, 0x0000 }, /* R1612 - AIF1TX6MIX Input 3 Source */ + { 0x064D, 0x0080 }, /* R1613 - AIF1TX6MIX Input 3 Volume */ + { 0x064E, 0x0000 }, /* R1614 - AIF1TX6MIX Input 4 Source */ + { 0x064F, 0x0080 }, /* R1615 - AIF1TX6MIX Input 4 Volume */ + { 0x0650, 0x0000 }, /* R1616 - EQLMIX Input 1 Source */ + { 0x0651, 0x0080 }, /* R1617 - EQLMIX Input 1 Volume */ + { 0x0652, 0x0000 }, /* R1618 - EQLMIX Input 2 Source */ + { 0x0653, 0x0080 }, /* R1619 - EQLMIX Input 2 Volume */ + { 0x0654, 0x0000 }, /* R1620 - EQLMIX Input 3 Source */ + { 0x0655, 0x0080 }, /* R1621 - EQLMIX Input 3 Volume */ + { 0x0656, 0x0000 }, /* R1622 - EQLMIX Input 4 Source */ + { 0x0657, 0x0080 }, /* R1623 - EQLMIX Input 4 Volume */ + { 0x0658, 0x0000 }, /* R1624 - EQRMIX Input 1 Source */ + { 0x0659, 0x0080 }, /* R1625 - EQRMIX Input 1 Volume */ + { 0x065A, 0x0000 }, /* R1626 - EQRMIX Input 2 Source */ + { 0x065B, 0x0080 }, /* R1627 - EQRMIX Input 2 Volume */ + { 0x065C, 0x0000 }, /* R1628 - EQRMIX Input 3 Source */ + { 0x065D, 0x0080 }, /* R1629 - EQRMIX Input 3 Volume */ + { 0x065E, 0x0000 }, /* R1630 - EQRMIX Input 4 Source */ + { 0x065F, 0x0080 }, /* R1631 - EQRMIX Input 4 Volume */ + { 0x0660, 0x0000 }, /* R1632 - LHPF1MIX Input 1 Source */ + { 0x0661, 0x0080 }, /* R1633 - LHPF1MIX Input 1 Volume */ + { 0x0662, 0x0000 }, /* R1634 - LHPF1MIX Input 2 Source */ + { 0x0663, 0x0080 }, /* R1635 - LHPF1MIX Input 2 Volume */ + { 0x0664, 0x0000 }, /* R1636 - LHPF1MIX Input 3 Source */ + { 0x0665, 0x0080 }, /* R1637 - LHPF1MIX Input 3 Volume */ + { 0x0666, 0x0000 }, /* R1638 - LHPF1MIX Input 4 Source */ + { 0x0667, 0x0080 }, /* R1639 - LHPF1MIX Input 4 Volume */ + { 0x0668, 0x0000 }, /* R1640 - LHPF2MIX Input 1 Source */ + { 0x0669, 0x0080 }, /* R1641 - LHPF2MIX Input 1 Volume */ + { 0x066A, 0x0000 }, /* R1642 - LHPF2MIX Input 2 Source */ + { 0x066B, 0x0080 }, /* R1643 - LHPF2MIX Input 2 Volume */ + { 0x066C, 0x0000 }, /* R1644 - LHPF2MIX Input 3 Source */ + { 0x066D, 0x0080 }, /* R1645 - LHPF2MIX Input 3 Volume */ + { 0x066E, 0x0000 }, /* R1646 - LHPF2MIX Input 4 Source */ + { 0x066F, 0x0080 }, /* R1647 - LHPF2MIX Input 4 Volume */ + { 0x0670, 0x0000 }, /* R1648 - DSP1LMIX Input 1 Source */ + { 0x0671, 0x0080 }, /* R1649 - DSP1LMIX Input 1 Volume */ + { 0x0672, 0x0000 }, /* R1650 - DSP1LMIX Input 2 Source */ + { 0x0673, 0x0080 }, /* R1651 - DSP1LMIX Input 2 Volume */ + { 0x0674, 0x0000 }, /* R1652 - DSP1LMIX Input 3 Source */ + { 0x0675, 0x0080 }, /* R1653 - DSP1LMIX Input 3 Volume */ + { 0x0676, 0x0000 }, /* R1654 - DSP1LMIX Input 4 Source */ + { 0x0677, 0x0080 }, /* R1655 - DSP1LMIX Input 4 Volume */ + { 0x0678, 0x0000 }, /* R1656 - DSP1RMIX Input 1 Source */ + { 0x0679, 0x0080 }, /* R1657 - DSP1RMIX Input 1 Volume */ + { 0x067A, 0x0000 }, /* R1658 - DSP1RMIX Input 2 Source */ + { 0x067B, 0x0080 }, /* R1659 - DSP1RMIX Input 2 Volume */ + { 0x067C, 0x0000 }, /* R1660 - DSP1RMIX Input 3 Source */ + { 0x067D, 0x0080 }, /* R1661 - DSP1RMIX Input 3 Volume */ + { 0x067E, 0x0000 }, /* R1662 - DSP1RMIX Input 4 Source */ + { 0x067F, 0x0080 }, /* R1663 - DSP1RMIX Input 4 Volume */ + { 0x0680, 0x0000 }, /* R1664 - DSP1AUX1MIX Input 1 Source */ + { 0x0681, 0x0000 }, /* R1665 - DSP1AUX2MIX Input 1 Source */ + { 0x0682, 0x0000 }, /* R1666 - DSP1AUX3MIX Input 1 Source */ + { 0x0683, 0x0000 }, /* R1667 - DSP1AUX4MIX Input 1 Source */ + { 0x0684, 0x0000 }, /* R1668 - DSP1AUX5MIX Input 1 Source */ + { 0x0685, 0x0000 }, /* R1669 - DSP1AUX6MIX Input 1 Source */ + { 0x0686, 0x0000 }, /* R1670 - DSP2LMIX Input 1 Source */ + { 0x0687, 0x0080 }, /* R1671 - DSP2LMIX Input 1 Volume */ + { 0x0688, 0x0000 }, /* R1672 - DSP2LMIX Input 2 Source */ + { 0x0689, 0x0080 }, /* R1673 - DSP2LMIX Input 2 Volume */ + { 0x068A, 0x0000 }, /* R1674 - DSP2LMIX Input 3 Source */ + { 0x068B, 0x0080 }, /* R1675 - DSP2LMIX Input 3 Volume */ + { 0x068C, 0x0000 }, /* R1676 - DSP2LMIX Input 4 Source */ + { 0x068D, 0x0080 }, /* R1677 - DSP2LMIX Input 4 Volume */ + { 0x068E, 0x0000 }, /* R1678 - DSP2RMIX Input 1 Source */ + { 0x068F, 0x0080 }, /* R1679 - DSP2RMIX Input 1 Volume */ + { 0x0690, 0x0000 }, /* R1680 - DSP2RMIX Input 2 Source */ + { 0x0691, 0x0080 }, /* R1681 - DSP2RMIX Input 2 Volume */ + { 0x0692, 0x0000 }, /* R1682 - DSP2RMIX Input 3 Source */ + { 0x0693, 0x0080 }, /* R1683 - DSP2RMIX Input 3 Volume */ + { 0x0694, 0x0000 }, /* R1684 - DSP2RMIX Input 4 Source */ + { 0x0695, 0x0080 }, /* R1685 - DSP2RMIX Input 4 Volume */ + { 0x0696, 0x0000 }, /* R1686 - DSP2AUX1MIX Input 1 Source */ + { 0x0697, 0x0000 }, /* R1687 - DSP2AUX2MIX Input 1 Source */ + { 0x0698, 0x0000 }, /* R1688 - DSP2AUX3MIX Input 1 Source */ + { 0x0699, 0x0000 }, /* R1689 - DSP2AUX4MIX Input 1 Source */ + { 0x069A, 0x0000 }, /* R1690 - DSP2AUX5MIX Input 1 Source */ + { 0x069B, 0x0000 }, /* R1691 - DSP2AUX6MIX Input 1 Source */ + { 0x0700, 0xA101 }, /* R1792 - GPIO CTRL 1 */ + { 0x0701, 0xA101 }, /* R1793 - GPIO CTRL 2 */ + { 0x0702, 0xA101 }, /* R1794 - GPIO CTRL 3 */ + { 0x0703, 0xA101 }, /* R1795 - GPIO CTRL 4 */ + { 0x0709, 0x0000 }, /* R1801 - Misc Pad Ctrl 1 */ + { 0x0801, 0x00FF }, /* R2049 - Interrupt Status 1 Mask */ + { 0x0804, 0xFFFF }, /* R2052 - Interrupt Status 2 Mask */ + { 0x0808, 0x0000 }, /* R2056 - Interrupt Control */ + { 0x0900, 0x0000 }, /* R2304 - EQL_1 */ + { 0x0901, 0x0000 }, /* R2305 - EQL_2 */ + { 0x0902, 0x0000 }, /* R2306 - EQL_3 */ + { 0x0903, 0x0000 }, /* R2307 - EQL_4 */ + { 0x0904, 0x0000 }, /* R2308 - EQL_5 */ + { 0x0905, 0x0000 }, /* R2309 - EQL_6 */ + { 0x0906, 0x0000 }, /* R2310 - EQL_7 */ + { 0x0907, 0x0000 }, /* R2311 - EQL_8 */ + { 0x0908, 0x0000 }, /* R2312 - EQL_9 */ + { 0x0909, 0x0000 }, /* R2313 - EQL_10 */ + { 0x090A, 0x0000 }, /* R2314 - EQL_11 */ + { 0x090B, 0x0000 }, /* R2315 - EQL_12 */ + { 0x090C, 0x0000 }, /* R2316 - EQL_13 */ + { 0x090D, 0x0000 }, /* R2317 - EQL_14 */ + { 0x090E, 0x0000 }, /* R2318 - EQL_15 */ + { 0x090F, 0x0000 }, /* R2319 - EQL_16 */ + { 0x0910, 0x0000 }, /* R2320 - EQL_17 */ + { 0x0911, 0x0000 }, /* R2321 - EQL_18 */ + { 0x0912, 0x0000 }, /* R2322 - EQL_19 */ + { 0x0913, 0x0000 }, /* R2323 - EQL_20 */ + { 0x0916, 0x0000 }, /* R2326 - EQR_1 */ + { 0x0917, 0x0000 }, /* R2327 - EQR_2 */ + { 0x0918, 0x0000 }, /* R2328 - EQR_3 */ + { 0x0919, 0x0000 }, /* R2329 - EQR_4 */ + { 0x091A, 0x0000 }, /* R2330 - EQR_5 */ + { 0x091B, 0x0000 }, /* R2331 - EQR_6 */ + { 0x091C, 0x0000 }, /* R2332 - EQR_7 */ + { 0x091D, 0x0000 }, /* R2333 - EQR_8 */ + { 0x091E, 0x0000 }, /* R2334 - EQR_9 */ + { 0x091F, 0x0000 }, /* R2335 - EQR_10 */ + { 0x0920, 0x0000 }, /* R2336 - EQR_11 */ + { 0x0921, 0x0000 }, /* R2337 - EQR_12 */ + { 0x0922, 0x0000 }, /* R2338 - EQR_13 */ + { 0x0923, 0x0000 }, /* R2339 - EQR_14 */ + { 0x0924, 0x0000 }, /* R2340 - EQR_15 */ + { 0x0925, 0x0000 }, /* R2341 - EQR_16 */ + { 0x0926, 0x0000 }, /* R2342 - EQR_17 */ + { 0x0927, 0x0000 }, /* R2343 - EQR_18 */ + { 0x0928, 0x0000 }, /* R2344 - EQR_19 */ + { 0x0929, 0x0000 }, /* R2345 - EQR_20 */ + { 0x093E, 0x0000 }, /* R2366 - HPLPF1_1 */ + { 0x093F, 0x0000 }, /* R2367 - HPLPF1_2 */ + { 0x0942, 0x0000 }, /* R2370 - HPLPF2_1 */ + { 0x0943, 0x0000 }, /* R2371 - HPLPF2_2 */ + { 0x0A00, 0x0000 }, /* R2560 - DSP1 Control 1 */ + { 0x0A02, 0x0000 }, /* R2562 - DSP1 Control 2 */ + { 0x0A03, 0x0000 }, /* R2563 - DSP1 Control 3 */ + { 0x0A04, 0x0000 }, /* R2564 - DSP1 Control 4 */ + { 0x0A06, 0x0000 }, /* R2566 - DSP1 Control 5 */ + { 0x0A07, 0x0000 }, /* R2567 - DSP1 Control 6 */ + { 0x0A08, 0x0000 }, /* R2568 - DSP1 Control 7 */ + { 0x0A09, 0x0000 }, /* R2569 - DSP1 Control 8 */ + { 0x0A0A, 0x0000 }, /* R2570 - DSP1 Control 9 */ + { 0x0A0B, 0x0000 }, /* R2571 - DSP1 Control 10 */ + { 0x0A0C, 0x0000 }, /* R2572 - DSP1 Control 11 */ + { 0x0A0D, 0x0000 }, /* R2573 - DSP1 Control 12 */ + { 0x0A0F, 0x0000 }, /* R2575 - DSP1 Control 13 */ + { 0x0A10, 0x0000 }, /* R2576 - DSP1 Control 14 */ + { 0x0A11, 0x0000 }, /* R2577 - DSP1 Control 15 */ + { 0x0A12, 0x0000 }, /* R2578 - DSP1 Control 16 */ + { 0x0A13, 0x0000 }, /* R2579 - DSP1 Control 17 */ + { 0x0A14, 0x0000 }, /* R2580 - DSP1 Control 18 */ + { 0x0A16, 0x0000 }, /* R2582 - DSP1 Control 19 */ + { 0x0A17, 0x0000 }, /* R2583 - DSP1 Control 20 */ + { 0x0A18, 0x0000 }, /* R2584 - DSP1 Control 21 */ + { 0x0A1A, 0x1800 }, /* R2586 - DSP1 Control 22 */ + { 0x0A1B, 0x1000 }, /* R2587 - DSP1 Control 23 */ + { 0x0A1C, 0x0400 }, /* R2588 - DSP1 Control 24 */ + { 0x0A1E, 0x0000 }, /* R2590 - DSP1 Control 25 */ + { 0x0A20, 0x0000 }, /* R2592 - DSP1 Control 26 */ + { 0x0A21, 0x0000 }, /* R2593 - DSP1 Control 27 */ + { 0x0A22, 0x0000 }, /* R2594 - DSP1 Control 28 */ + { 0x0A23, 0x0000 }, /* R2595 - DSP1 Control 29 */ + { 0x0A24, 0x0000 }, /* R2596 - DSP1 Control 30 */ + { 0x0A26, 0x0000 }, /* R2598 - DSP1 Control 31 */ + { 0x0B00, 0x0000 }, /* R2816 - DSP2 Control 1 */ + { 0x0B02, 0x0000 }, /* R2818 - DSP2 Control 2 */ + { 0x0B03, 0x0000 }, /* R2819 - DSP2 Control 3 */ + { 0x0B04, 0x0000 }, /* R2820 - DSP2 Control 4 */ + { 0x0B06, 0x0000 }, /* R2822 - DSP2 Control 5 */ + { 0x0B07, 0x0000 }, /* R2823 - DSP2 Control 6 */ + { 0x0B08, 0x0000 }, /* R2824 - DSP2 Control 7 */ + { 0x0B09, 0x0000 }, /* R2825 - DSP2 Control 8 */ + { 0x0B0A, 0x0000 }, /* R2826 - DSP2 Control 9 */ + { 0x0B0B, 0x0000 }, /* R2827 - DSP2 Control 10 */ + { 0x0B0C, 0x0000 }, /* R2828 - DSP2 Control 11 */ + { 0x0B0D, 0x0000 }, /* R2829 - DSP2 Control 12 */ + { 0x0B0F, 0x0000 }, /* R2831 - DSP2 Control 13 */ + { 0x0B10, 0x0000 }, /* R2832 - DSP2 Control 14 */ + { 0x0B11, 0x0000 }, /* R2833 - DSP2 Control 15 */ + { 0x0B12, 0x0000 }, /* R2834 - DSP2 Control 16 */ + { 0x0B13, 0x0000 }, /* R2835 - DSP2 Control 17 */ + { 0x0B14, 0x0000 }, /* R2836 - DSP2 Control 18 */ + { 0x0B16, 0x0000 }, /* R2838 - DSP2 Control 19 */ + { 0x0B17, 0x0000 }, /* R2839 - DSP2 Control 20 */ + { 0x0B18, 0x0000 }, /* R2840 - DSP2 Control 21 */ + { 0x0B1A, 0x0800 }, /* R2842 - DSP2 Control 22 */ + { 0x0B1B, 0x1000 }, /* R2843 - DSP2 Control 23 */ + { 0x0B1C, 0x0400 }, /* R2844 - DSP2 Control 24 */ + { 0x0B1E, 0x0000 }, /* R2846 - DSP2 Control 25 */ + { 0x0B20, 0x0000 }, /* R2848 - DSP2 Control 26 */ + { 0x0B21, 0x0000 }, /* R2849 - DSP2 Control 27 */ + { 0x0B22, 0x0000 }, /* R2850 - DSP2 Control 28 */ + { 0x0B23, 0x0000 }, /* R2851 - DSP2 Control 29 */ + { 0x0B24, 0x0000 }, /* R2852 - DSP2 Control 30 */ + { 0x0B26, 0x0000 }, /* R2854 - DSP2 Control 31 */ +}; + +static bool wm2200_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wm2200_ranges); i++) + if ((reg >= wm2200_ranges[i].window_start && + reg <= wm2200_ranges[i].window_start + + wm2200_ranges[i].window_len) || + (reg >= wm2200_ranges[i].range_min && + reg <= wm2200_ranges[i].range_max)) + return true; + + switch (reg) { + case WM2200_SOFTWARE_RESET: + case WM2200_DEVICE_REVISION: + case WM2200_ADPS1_IRQ0: + case WM2200_ADPS1_IRQ1: + case WM2200_INTERRUPT_STATUS_1: + case WM2200_INTERRUPT_STATUS_2: + case WM2200_INTERRUPT_RAW_STATUS_2: + return true; + default: + return false; + } +} + +static bool wm2200_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wm2200_ranges); i++) + if ((reg >= wm2200_ranges[i].window_start && + reg <= wm2200_ranges[i].window_start + + wm2200_ranges[i].window_len) || + (reg >= wm2200_ranges[i].range_min && + reg <= wm2200_ranges[i].range_max)) + return true; + + switch (reg) { + case WM2200_SOFTWARE_RESET: + case WM2200_DEVICE_REVISION: + case WM2200_TONE_GENERATOR_1: + case WM2200_CLOCKING_3: + case WM2200_CLOCKING_4: + case WM2200_FLL_CONTROL_1: + case WM2200_FLL_CONTROL_2: + case WM2200_FLL_CONTROL_3: + case WM2200_FLL_CONTROL_4: + case WM2200_FLL_CONTROL_6: + case WM2200_FLL_CONTROL_7: + case WM2200_FLL_EFS_1: + case WM2200_FLL_EFS_2: + case WM2200_MIC_CHARGE_PUMP_1: + case WM2200_MIC_CHARGE_PUMP_2: + case WM2200_DM_CHARGE_PUMP_1: + case WM2200_MIC_BIAS_CTRL_1: + case WM2200_MIC_BIAS_CTRL_2: + case WM2200_EAR_PIECE_CTRL_1: + case WM2200_EAR_PIECE_CTRL_2: + case WM2200_INPUT_ENABLES: + case WM2200_IN1L_CONTROL: + case WM2200_IN1R_CONTROL: + case WM2200_IN2L_CONTROL: + case WM2200_IN2R_CONTROL: + case WM2200_IN3L_CONTROL: + case WM2200_IN3R_CONTROL: + case WM2200_RXANC_SRC: + case WM2200_INPUT_VOLUME_RAMP: + case WM2200_ADC_DIGITAL_VOLUME_1L: + case WM2200_ADC_DIGITAL_VOLUME_1R: + case WM2200_ADC_DIGITAL_VOLUME_2L: + case WM2200_ADC_DIGITAL_VOLUME_2R: + case WM2200_ADC_DIGITAL_VOLUME_3L: + case WM2200_ADC_DIGITAL_VOLUME_3R: + case WM2200_OUTPUT_ENABLES: + case WM2200_DAC_VOLUME_LIMIT_1L: + case WM2200_DAC_VOLUME_LIMIT_1R: + case WM2200_DAC_VOLUME_LIMIT_2L: + case WM2200_DAC_VOLUME_LIMIT_2R: + case WM2200_DAC_AEC_CONTROL_1: + case WM2200_OUTPUT_VOLUME_RAMP: + case WM2200_DAC_DIGITAL_VOLUME_1L: + case WM2200_DAC_DIGITAL_VOLUME_1R: + case WM2200_DAC_DIGITAL_VOLUME_2L: + case WM2200_DAC_DIGITAL_VOLUME_2R: + case WM2200_PDM_1: + case WM2200_PDM_2: + case WM2200_AUDIO_IF_1_1: + case WM2200_AUDIO_IF_1_2: + case WM2200_AUDIO_IF_1_3: + case WM2200_AUDIO_IF_1_4: + case WM2200_AUDIO_IF_1_5: + case WM2200_AUDIO_IF_1_6: + case WM2200_AUDIO_IF_1_7: + case WM2200_AUDIO_IF_1_8: + case WM2200_AUDIO_IF_1_9: + case WM2200_AUDIO_IF_1_10: + case WM2200_AUDIO_IF_1_11: + case WM2200_AUDIO_IF_1_12: + case WM2200_AUDIO_IF_1_13: + case WM2200_AUDIO_IF_1_14: + case WM2200_AUDIO_IF_1_15: + case WM2200_AUDIO_IF_1_16: + case WM2200_AUDIO_IF_1_17: + case WM2200_AUDIO_IF_1_18: + case WM2200_AUDIO_IF_1_19: + case WM2200_AUDIO_IF_1_20: + case WM2200_AUDIO_IF_1_21: + case WM2200_AUDIO_IF_1_22: + case WM2200_OUT1LMIX_INPUT_1_SOURCE: + case WM2200_OUT1LMIX_INPUT_1_VOLUME: + case WM2200_OUT1LMIX_INPUT_2_SOURCE: + case WM2200_OUT1LMIX_INPUT_2_VOLUME: + case WM2200_OUT1LMIX_INPUT_3_SOURCE: + case WM2200_OUT1LMIX_INPUT_3_VOLUME: + case WM2200_OUT1LMIX_INPUT_4_SOURCE: + case WM2200_OUT1LMIX_INPUT_4_VOLUME: + case WM2200_OUT1RMIX_INPUT_1_SOURCE: + case WM2200_OUT1RMIX_INPUT_1_VOLUME: + case WM2200_OUT1RMIX_INPUT_2_SOURCE: + case WM2200_OUT1RMIX_INPUT_2_VOLUME: + case WM2200_OUT1RMIX_INPUT_3_SOURCE: + case WM2200_OUT1RMIX_INPUT_3_VOLUME: + case WM2200_OUT1RMIX_INPUT_4_SOURCE: + case WM2200_OUT1RMIX_INPUT_4_VOLUME: + case WM2200_OUT2LMIX_INPUT_1_SOURCE: + case WM2200_OUT2LMIX_INPUT_1_VOLUME: + case WM2200_OUT2LMIX_INPUT_2_SOURCE: + case WM2200_OUT2LMIX_INPUT_2_VOLUME: + case WM2200_OUT2LMIX_INPUT_3_SOURCE: + case WM2200_OUT2LMIX_INPUT_3_VOLUME: + case WM2200_OUT2LMIX_INPUT_4_SOURCE: + case WM2200_OUT2LMIX_INPUT_4_VOLUME: + case WM2200_OUT2RMIX_INPUT_1_SOURCE: + case WM2200_OUT2RMIX_INPUT_1_VOLUME: + case WM2200_OUT2RMIX_INPUT_2_SOURCE: + case WM2200_OUT2RMIX_INPUT_2_VOLUME: + case WM2200_OUT2RMIX_INPUT_3_SOURCE: + case WM2200_OUT2RMIX_INPUT_3_VOLUME: + case WM2200_OUT2RMIX_INPUT_4_SOURCE: + case WM2200_OUT2RMIX_INPUT_4_VOLUME: + case WM2200_AIF1TX1MIX_INPUT_1_SOURCE: + case WM2200_AIF1TX1MIX_INPUT_1_VOLUME: + case WM2200_AIF1TX1MIX_INPUT_2_SOURCE: + case WM2200_AIF1TX1MIX_INPUT_2_VOLUME: + case WM2200_AIF1TX1MIX_INPUT_3_SOURCE: + case WM2200_AIF1TX1MIX_INPUT_3_VOLUME: + case WM2200_AIF1TX1MIX_INPUT_4_SOURCE: + case WM2200_AIF1TX1MIX_INPUT_4_VOLUME: + case WM2200_AIF1TX2MIX_INPUT_1_SOURCE: + case WM2200_AIF1TX2MIX_INPUT_1_VOLUME: + case WM2200_AIF1TX2MIX_INPUT_2_SOURCE: + case WM2200_AIF1TX2MIX_INPUT_2_VOLUME: + case WM2200_AIF1TX2MIX_INPUT_3_SOURCE: + case WM2200_AIF1TX2MIX_INPUT_3_VOLUME: + case WM2200_AIF1TX2MIX_INPUT_4_SOURCE: + case WM2200_AIF1TX2MIX_INPUT_4_VOLUME: + case WM2200_AIF1TX3MIX_INPUT_1_SOURCE: + case WM2200_AIF1TX3MIX_INPUT_1_VOLUME: + case WM2200_AIF1TX3MIX_INPUT_2_SOURCE: + case WM2200_AIF1TX3MIX_INPUT_2_VOLUME: + case WM2200_AIF1TX3MIX_INPUT_3_SOURCE: + case WM2200_AIF1TX3MIX_INPUT_3_VOLUME: + case WM2200_AIF1TX3MIX_INPUT_4_SOURCE: + case WM2200_AIF1TX3MIX_INPUT_4_VOLUME: + case WM2200_AIF1TX4MIX_INPUT_1_SOURCE: + case WM2200_AIF1TX4MIX_INPUT_1_VOLUME: + case WM2200_AIF1TX4MIX_INPUT_2_SOURCE: + case WM2200_AIF1TX4MIX_INPUT_2_VOLUME: + case WM2200_AIF1TX4MIX_INPUT_3_SOURCE: + case WM2200_AIF1TX4MIX_INPUT_3_VOLUME: + case WM2200_AIF1TX4MIX_INPUT_4_SOURCE: + case WM2200_AIF1TX4MIX_INPUT_4_VOLUME: + case WM2200_AIF1TX5MIX_INPUT_1_SOURCE: + case WM2200_AIF1TX5MIX_INPUT_1_VOLUME: + case WM2200_AIF1TX5MIX_INPUT_2_SOURCE: + case WM2200_AIF1TX5MIX_INPUT_2_VOLUME: + case WM2200_AIF1TX5MIX_INPUT_3_SOURCE: + case WM2200_AIF1TX5MIX_INPUT_3_VOLUME: + case WM2200_AIF1TX5MIX_INPUT_4_SOURCE: + case WM2200_AIF1TX5MIX_INPUT_4_VOLUME: + case WM2200_AIF1TX6MIX_INPUT_1_SOURCE: + case WM2200_AIF1TX6MIX_INPUT_1_VOLUME: + case WM2200_AIF1TX6MIX_INPUT_2_SOURCE: + case WM2200_AIF1TX6MIX_INPUT_2_VOLUME: + case WM2200_AIF1TX6MIX_INPUT_3_SOURCE: + case WM2200_AIF1TX6MIX_INPUT_3_VOLUME: + case WM2200_AIF1TX6MIX_INPUT_4_SOURCE: + case WM2200_AIF1TX6MIX_INPUT_4_VOLUME: + case WM2200_EQLMIX_INPUT_1_SOURCE: + case WM2200_EQLMIX_INPUT_1_VOLUME: + case WM2200_EQLMIX_INPUT_2_SOURCE: + case WM2200_EQLMIX_INPUT_2_VOLUME: + case WM2200_EQLMIX_INPUT_3_SOURCE: + case WM2200_EQLMIX_INPUT_3_VOLUME: + case WM2200_EQLMIX_INPUT_4_SOURCE: + case WM2200_EQLMIX_INPUT_4_VOLUME: + case WM2200_EQRMIX_INPUT_1_SOURCE: + case WM2200_EQRMIX_INPUT_1_VOLUME: + case WM2200_EQRMIX_INPUT_2_SOURCE: + case WM2200_EQRMIX_INPUT_2_VOLUME: + case WM2200_EQRMIX_INPUT_3_SOURCE: + case WM2200_EQRMIX_INPUT_3_VOLUME: + case WM2200_EQRMIX_INPUT_4_SOURCE: + case WM2200_EQRMIX_INPUT_4_VOLUME: + case WM2200_LHPF1MIX_INPUT_1_SOURCE: + case WM2200_LHPF1MIX_INPUT_1_VOLUME: + case WM2200_LHPF1MIX_INPUT_2_SOURCE: + case WM2200_LHPF1MIX_INPUT_2_VOLUME: + case WM2200_LHPF1MIX_INPUT_3_SOURCE: + case WM2200_LHPF1MIX_INPUT_3_VOLUME: + case WM2200_LHPF1MIX_INPUT_4_SOURCE: + case WM2200_LHPF1MIX_INPUT_4_VOLUME: + case WM2200_LHPF2MIX_INPUT_1_SOURCE: + case WM2200_LHPF2MIX_INPUT_1_VOLUME: + case WM2200_LHPF2MIX_INPUT_2_SOURCE: + case WM2200_LHPF2MIX_INPUT_2_VOLUME: + case WM2200_LHPF2MIX_INPUT_3_SOURCE: + case WM2200_LHPF2MIX_INPUT_3_VOLUME: + case WM2200_LHPF2MIX_INPUT_4_SOURCE: + case WM2200_LHPF2MIX_INPUT_4_VOLUME: + case WM2200_DSP1LMIX_INPUT_1_SOURCE: + case WM2200_DSP1LMIX_INPUT_1_VOLUME: + case WM2200_DSP1LMIX_INPUT_2_SOURCE: + case WM2200_DSP1LMIX_INPUT_2_VOLUME: + case WM2200_DSP1LMIX_INPUT_3_SOURCE: + case WM2200_DSP1LMIX_INPUT_3_VOLUME: + case WM2200_DSP1LMIX_INPUT_4_SOURCE: + case WM2200_DSP1LMIX_INPUT_4_VOLUME: + case WM2200_DSP1RMIX_INPUT_1_SOURCE: + case WM2200_DSP1RMIX_INPUT_1_VOLUME: + case WM2200_DSP1RMIX_INPUT_2_SOURCE: + case WM2200_DSP1RMIX_INPUT_2_VOLUME: + case WM2200_DSP1RMIX_INPUT_3_SOURCE: + case WM2200_DSP1RMIX_INPUT_3_VOLUME: + case WM2200_DSP1RMIX_INPUT_4_SOURCE: + case WM2200_DSP1RMIX_INPUT_4_VOLUME: + case WM2200_DSP1AUX1MIX_INPUT_1_SOURCE: + case WM2200_DSP1AUX2MIX_INPUT_1_SOURCE: + case WM2200_DSP1AUX3MIX_INPUT_1_SOURCE: + case WM2200_DSP1AUX4MIX_INPUT_1_SOURCE: + case WM2200_DSP1AUX5MIX_INPUT_1_SOURCE: + case WM2200_DSP1AUX6MIX_INPUT_1_SOURCE: + case WM2200_DSP2LMIX_INPUT_1_SOURCE: + case WM2200_DSP2LMIX_INPUT_1_VOLUME: + case WM2200_DSP2LMIX_INPUT_2_SOURCE: + case WM2200_DSP2LMIX_INPUT_2_VOLUME: + case WM2200_DSP2LMIX_INPUT_3_SOURCE: + case WM2200_DSP2LMIX_INPUT_3_VOLUME: + case WM2200_DSP2LMIX_INPUT_4_SOURCE: + case WM2200_DSP2LMIX_INPUT_4_VOLUME: + case WM2200_DSP2RMIX_INPUT_1_SOURCE: + case WM2200_DSP2RMIX_INPUT_1_VOLUME: + case WM2200_DSP2RMIX_INPUT_2_SOURCE: + case WM2200_DSP2RMIX_INPUT_2_VOLUME: + case WM2200_DSP2RMIX_INPUT_3_SOURCE: + case WM2200_DSP2RMIX_INPUT_3_VOLUME: + case WM2200_DSP2RMIX_INPUT_4_SOURCE: + case WM2200_DSP2RMIX_INPUT_4_VOLUME: + case WM2200_DSP2AUX1MIX_INPUT_1_SOURCE: + case WM2200_DSP2AUX2MIX_INPUT_1_SOURCE: + case WM2200_DSP2AUX3MIX_INPUT_1_SOURCE: + case WM2200_DSP2AUX4MIX_INPUT_1_SOURCE: + case WM2200_DSP2AUX5MIX_INPUT_1_SOURCE: + case WM2200_DSP2AUX6MIX_INPUT_1_SOURCE: + case WM2200_GPIO_CTRL_1: + case WM2200_GPIO_CTRL_2: + case WM2200_GPIO_CTRL_3: + case WM2200_GPIO_CTRL_4: + case WM2200_ADPS1_IRQ0: + case WM2200_ADPS1_IRQ1: + case WM2200_MISC_PAD_CTRL_1: + case WM2200_INTERRUPT_STATUS_1: + case WM2200_INTERRUPT_STATUS_1_MASK: + case WM2200_INTERRUPT_STATUS_2: + case WM2200_INTERRUPT_RAW_STATUS_2: + case WM2200_INTERRUPT_STATUS_2_MASK: + case WM2200_INTERRUPT_CONTROL: + case WM2200_EQL_1: + case WM2200_EQL_2: + case WM2200_EQL_3: + case WM2200_EQL_4: + case WM2200_EQL_5: + case WM2200_EQL_6: + case WM2200_EQL_7: + case WM2200_EQL_8: + case WM2200_EQL_9: + case WM2200_EQL_10: + case WM2200_EQL_11: + case WM2200_EQL_12: + case WM2200_EQL_13: + case WM2200_EQL_14: + case WM2200_EQL_15: + case WM2200_EQL_16: + case WM2200_EQL_17: + case WM2200_EQL_18: + case WM2200_EQL_19: + case WM2200_EQL_20: + case WM2200_EQR_1: + case WM2200_EQR_2: + case WM2200_EQR_3: + case WM2200_EQR_4: + case WM2200_EQR_5: + case WM2200_EQR_6: + case WM2200_EQR_7: + case WM2200_EQR_8: + case WM2200_EQR_9: + case WM2200_EQR_10: + case WM2200_EQR_11: + case WM2200_EQR_12: + case WM2200_EQR_13: + case WM2200_EQR_14: + case WM2200_EQR_15: + case WM2200_EQR_16: + case WM2200_EQR_17: + case WM2200_EQR_18: + case WM2200_EQR_19: + case WM2200_EQR_20: + case WM2200_HPLPF1_1: + case WM2200_HPLPF1_2: + case WM2200_HPLPF2_1: + case WM2200_HPLPF2_2: + case WM2200_DSP1_CONTROL_1: + case WM2200_DSP1_CONTROL_2: + case WM2200_DSP1_CONTROL_3: + case WM2200_DSP1_CONTROL_4: + case WM2200_DSP1_CONTROL_5: + case WM2200_DSP1_CONTROL_6: + case WM2200_DSP1_CONTROL_7: + case WM2200_DSP1_CONTROL_8: + case WM2200_DSP1_CONTROL_9: + case WM2200_DSP1_CONTROL_10: + case WM2200_DSP1_CONTROL_11: + case WM2200_DSP1_CONTROL_12: + case WM2200_DSP1_CONTROL_13: + case WM2200_DSP1_CONTROL_14: + case WM2200_DSP1_CONTROL_15: + case WM2200_DSP1_CONTROL_16: + case WM2200_DSP1_CONTROL_17: + case WM2200_DSP1_CONTROL_18: + case WM2200_DSP1_CONTROL_19: + case WM2200_DSP1_CONTROL_20: + case WM2200_DSP1_CONTROL_21: + case WM2200_DSP1_CONTROL_22: + case WM2200_DSP1_CONTROL_23: + case WM2200_DSP1_CONTROL_24: + case WM2200_DSP1_CONTROL_25: + case WM2200_DSP1_CONTROL_26: + case WM2200_DSP1_CONTROL_27: + case WM2200_DSP1_CONTROL_28: + case WM2200_DSP1_CONTROL_29: + case WM2200_DSP1_CONTROL_30: + case WM2200_DSP1_CONTROL_31: + case WM2200_DSP2_CONTROL_1: + case WM2200_DSP2_CONTROL_2: + case WM2200_DSP2_CONTROL_3: + case WM2200_DSP2_CONTROL_4: + case WM2200_DSP2_CONTROL_5: + case WM2200_DSP2_CONTROL_6: + case WM2200_DSP2_CONTROL_7: + case WM2200_DSP2_CONTROL_8: + case WM2200_DSP2_CONTROL_9: + case WM2200_DSP2_CONTROL_10: + case WM2200_DSP2_CONTROL_11: + case WM2200_DSP2_CONTROL_12: + case WM2200_DSP2_CONTROL_13: + case WM2200_DSP2_CONTROL_14: + case WM2200_DSP2_CONTROL_15: + case WM2200_DSP2_CONTROL_16: + case WM2200_DSP2_CONTROL_17: + case WM2200_DSP2_CONTROL_18: + case WM2200_DSP2_CONTROL_19: + case WM2200_DSP2_CONTROL_20: + case WM2200_DSP2_CONTROL_21: + case WM2200_DSP2_CONTROL_22: + case WM2200_DSP2_CONTROL_23: + case WM2200_DSP2_CONTROL_24: + case WM2200_DSP2_CONTROL_25: + case WM2200_DSP2_CONTROL_26: + case WM2200_DSP2_CONTROL_27: + case WM2200_DSP2_CONTROL_28: + case WM2200_DSP2_CONTROL_29: + case WM2200_DSP2_CONTROL_30: + case WM2200_DSP2_CONTROL_31: + return true; + default: + return false; + } +} + +static const struct reg_sequence wm2200_reva_patch[] = { + { 0x07, 0x0003 }, + { 0x102, 0x0200 }, + { 0x203, 0x0084 }, + { 0x201, 0x83FF }, + { 0x20C, 0x0062 }, + { 0x20D, 0x0062 }, + { 0x207, 0x2002 }, + { 0x208, 0x20C0 }, + { 0x21D, 0x01C0 }, + { 0x50A, 0x0001 }, + { 0x50B, 0x0002 }, + { 0x50C, 0x0003 }, + { 0x50D, 0x0004 }, + { 0x50E, 0x0005 }, + { 0x510, 0x0001 }, + { 0x511, 0x0002 }, + { 0x512, 0x0003 }, + { 0x513, 0x0004 }, + { 0x514, 0x0005 }, + { 0x515, 0x0000 }, + { 0x201, 0x8084 }, + { 0x202, 0xBBDE }, + { 0x203, 0x00EC }, + { 0x500, 0x8000 }, + { 0x507, 0x1820 }, + { 0x508, 0x1820 }, + { 0x505, 0x0300 }, + { 0x506, 0x0300 }, + { 0x302, 0x2280 }, + { 0x303, 0x0080 }, + { 0x304, 0x2280 }, + { 0x305, 0x0080 }, + { 0x306, 0x2280 }, + { 0x307, 0x0080 }, + { 0x401, 0x0080 }, + { 0x402, 0x0080 }, + { 0x417, 0x3069 }, + { 0x900, 0x6318 }, + { 0x901, 0x6300 }, + { 0x902, 0x0FC8 }, + { 0x903, 0x03FE }, + { 0x904, 0x00E0 }, + { 0x905, 0x1EC4 }, + { 0x906, 0xF136 }, + { 0x907, 0x0409 }, + { 0x908, 0x04CC }, + { 0x909, 0x1C9B }, + { 0x90A, 0xF337 }, + { 0x90B, 0x040B }, + { 0x90C, 0x0CBB }, + { 0x90D, 0x16F8 }, + { 0x90E, 0xF7D9 }, + { 0x90F, 0x040A }, + { 0x910, 0x1F14 }, + { 0x911, 0x058C }, + { 0x912, 0x0563 }, + { 0x913, 0x4000 }, + { 0x916, 0x6318 }, + { 0x917, 0x6300 }, + { 0x918, 0x0FC8 }, + { 0x919, 0x03FE }, + { 0x91A, 0x00E0 }, + { 0x91B, 0x1EC4 }, + { 0x91C, 0xF136 }, + { 0x91D, 0x0409 }, + { 0x91E, 0x04CC }, + { 0x91F, 0x1C9B }, + { 0x920, 0xF337 }, + { 0x921, 0x040B }, + { 0x922, 0x0CBB }, + { 0x923, 0x16F8 }, + { 0x924, 0xF7D9 }, + { 0x925, 0x040A }, + { 0x926, 0x1F14 }, + { 0x927, 0x058C }, + { 0x928, 0x0563 }, + { 0x929, 0x4000 }, + { 0x709, 0x2000 }, + { 0x207, 0x200E }, + { 0x208, 0x20D4 }, + { 0x20A, 0x0080 }, + { 0x07, 0x0000 }, +}; + +static int wm2200_reset(struct wm2200_priv *wm2200) +{ + if (wm2200->pdata.reset) { + gpio_set_value_cansleep(wm2200->pdata.reset, 0); + gpio_set_value_cansleep(wm2200->pdata.reset, 1); + + return 0; + } else { + return regmap_write(wm2200->regmap, WM2200_SOFTWARE_RESET, + 0x2200); + } +} + +static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0); + +static const char * const wm2200_mixer_texts[] = { + "None", + "Tone Generator", + "AEC Loopback", + "IN1L", + "IN1R", + "IN2L", + "IN2R", + "IN3L", + "IN3R", + "AIF1RX1", + "AIF1RX2", + "AIF1RX3", + "AIF1RX4", + "AIF1RX5", + "AIF1RX6", + "EQL", + "EQR", + "LHPF1", + "LHPF2", + "DSP1.1", + "DSP1.2", + "DSP1.3", + "DSP1.4", + "DSP1.5", + "DSP1.6", + "DSP2.1", + "DSP2.2", + "DSP2.3", + "DSP2.4", + "DSP2.5", + "DSP2.6", +}; + +static unsigned int wm2200_mixer_values[] = { + 0x00, + 0x04, /* Tone */ + 0x08, /* AEC */ + 0x10, /* Input */ + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x20, /* AIF */ + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, + 0x50, /* EQ */ + 0x51, + 0x60, /* LHPF1 */ + 0x61, /* LHPF2 */ + 0x68, /* DSP1 */ + 0x69, + 0x6a, + 0x6b, + 0x6c, + 0x6d, + 0x70, /* DSP2 */ + 0x71, + 0x72, + 0x73, + 0x74, + 0x75, +}; + +#define WM2200_MIXER_CONTROLS(name, base) \ + SOC_SINGLE_TLV(name " Input 1 Volume", base + 1 , \ + WM2200_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \ + SOC_SINGLE_TLV(name " Input 2 Volume", base + 3 , \ + WM2200_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \ + SOC_SINGLE_TLV(name " Input 3 Volume", base + 5 , \ + WM2200_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \ + SOC_SINGLE_TLV(name " Input 4 Volume", base + 7 , \ + WM2200_MIXER_VOL_SHIFT, 80, 0, mixer_tlv) + +#define WM2200_MUX_ENUM_DECL(name, reg) \ + SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff, \ + wm2200_mixer_texts, wm2200_mixer_values) + +#define WM2200_MUX_CTL_DECL(name) \ + const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM("Route", name##_enum) + +#define WM2200_MIXER_ENUMS(name, base_reg) \ + static WM2200_MUX_ENUM_DECL(name##_in1_enum, base_reg); \ + static WM2200_MUX_ENUM_DECL(name##_in2_enum, base_reg + 2); \ + static WM2200_MUX_ENUM_DECL(name##_in3_enum, base_reg + 4); \ + static WM2200_MUX_ENUM_DECL(name##_in4_enum, base_reg + 6); \ + static WM2200_MUX_CTL_DECL(name##_in1); \ + static WM2200_MUX_CTL_DECL(name##_in2); \ + static WM2200_MUX_CTL_DECL(name##_in3); \ + static WM2200_MUX_CTL_DECL(name##_in4) + +#define WM2200_DSP_ENUMS(name, base_reg) \ + static WM2200_MUX_ENUM_DECL(name##_aux1_enum, base_reg); \ + static WM2200_MUX_ENUM_DECL(name##_aux2_enum, base_reg + 1); \ + static WM2200_MUX_ENUM_DECL(name##_aux3_enum, base_reg + 2); \ + static WM2200_MUX_ENUM_DECL(name##_aux4_enum, base_reg + 3); \ + static WM2200_MUX_ENUM_DECL(name##_aux5_enum, base_reg + 4); \ + static WM2200_MUX_ENUM_DECL(name##_aux6_enum, base_reg + 5); \ + static WM2200_MUX_CTL_DECL(name##_aux1); \ + static WM2200_MUX_CTL_DECL(name##_aux2); \ + static WM2200_MUX_CTL_DECL(name##_aux3); \ + static WM2200_MUX_CTL_DECL(name##_aux4); \ + static WM2200_MUX_CTL_DECL(name##_aux5); \ + static WM2200_MUX_CTL_DECL(name##_aux6); + +static const char *wm2200_rxanc_input_sel_texts[] = { + "None", "IN1", "IN2", "IN3", +}; + +static SOC_ENUM_SINGLE_DECL(wm2200_rxanc_input_sel, + WM2200_RXANC_SRC, + WM2200_IN_RXANC_SEL_SHIFT, + wm2200_rxanc_input_sel_texts); + +static const struct snd_kcontrol_new wm2200_snd_controls[] = { +SOC_SINGLE("IN1 High Performance Switch", WM2200_IN1L_CONTROL, + WM2200_IN1_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN2 High Performance Switch", WM2200_IN2L_CONTROL, + WM2200_IN2_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN3 High Performance Switch", WM2200_IN3L_CONTROL, + WM2200_IN3_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R_TLV("IN1 Volume", WM2200_IN1L_CONTROL, WM2200_IN1R_CONTROL, + WM2200_IN1L_PGA_VOL_SHIFT, 0x5f, 0, in_tlv), +SOC_DOUBLE_R_TLV("IN2 Volume", WM2200_IN2L_CONTROL, WM2200_IN2R_CONTROL, + WM2200_IN2L_PGA_VOL_SHIFT, 0x5f, 0, in_tlv), +SOC_DOUBLE_R_TLV("IN3 Volume", WM2200_IN3L_CONTROL, WM2200_IN3R_CONTROL, + WM2200_IN3L_PGA_VOL_SHIFT, 0x5f, 0, in_tlv), + +SOC_DOUBLE_R("IN1 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, + WM2200_ADC_DIGITAL_VOLUME_1R, WM2200_IN1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_2L, + WM2200_ADC_DIGITAL_VOLUME_2R, WM2200_IN2L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_3L, + WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("IN1 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_1L, + WM2200_ADC_DIGITAL_VOLUME_1R, WM2200_IN1L_DIG_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("IN2 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_2L, + WM2200_ADC_DIGITAL_VOLUME_2R, WM2200_IN2L_DIG_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("IN3 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_3L, + WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_DIG_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SND_SOC_BYTES_MASK("EQL Coefficients", WM2200_EQL_1, 20, WM2200_EQL_ENA), +SND_SOC_BYTES_MASK("EQR Coefficients", WM2200_EQR_1, 20, WM2200_EQR_ENA), + +SND_SOC_BYTES("LHPF1 Coefficients", WM2200_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficients", WM2200_HPLPF2_2, 1), + +SOC_SINGLE("OUT1 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_1L, + WM2200_OUT1_OSR_SHIFT, 1, 0), +SOC_SINGLE("OUT2 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_2L, + WM2200_OUT2_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("OUT1 Digital Switch", WM2200_DAC_DIGITAL_VOLUME_1L, + WM2200_DAC_DIGITAL_VOLUME_1R, WM2200_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R_TLV("OUT1 Digital Volume", WM2200_DAC_DIGITAL_VOLUME_1L, + WM2200_DAC_DIGITAL_VOLUME_1R, WM2200_OUT1L_VOL_SHIFT, 0x9f, 0, + digital_tlv), +SOC_DOUBLE_R_TLV("OUT1 Volume", WM2200_DAC_VOLUME_LIMIT_1L, + WM2200_DAC_VOLUME_LIMIT_1R, WM2200_OUT1L_PGA_VOL_SHIFT, + 0x46, 0, out_tlv), + +SOC_DOUBLE_R("OUT2 Digital Switch", WM2200_DAC_DIGITAL_VOLUME_2L, + WM2200_DAC_DIGITAL_VOLUME_2R, WM2200_OUT2L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R_TLV("OUT2 Digital Volume", WM2200_DAC_DIGITAL_VOLUME_2L, + WM2200_DAC_DIGITAL_VOLUME_2R, WM2200_OUT2L_VOL_SHIFT, 0x9f, 0, + digital_tlv), +SOC_DOUBLE("OUT2 Switch", WM2200_PDM_1, WM2200_SPK1L_MUTE_SHIFT, + WM2200_SPK1R_MUTE_SHIFT, 1, 1), +SOC_ENUM("RxANC Src", wm2200_rxanc_input_sel), + +WM_ADSP_FW_CONTROL("DSP1", 0), +WM_ADSP_FW_CONTROL("DSP2", 1), +}; + +WM2200_MIXER_ENUMS(OUT1L, WM2200_OUT1LMIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(OUT1R, WM2200_OUT1RMIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(OUT2L, WM2200_OUT2LMIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(OUT2R, WM2200_OUT2RMIX_INPUT_1_SOURCE); + +WM2200_MIXER_ENUMS(AIF1TX1, WM2200_AIF1TX1MIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(AIF1TX2, WM2200_AIF1TX2MIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(AIF1TX3, WM2200_AIF1TX3MIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(AIF1TX4, WM2200_AIF1TX4MIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(AIF1TX5, WM2200_AIF1TX5MIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(AIF1TX6, WM2200_AIF1TX6MIX_INPUT_1_SOURCE); + +WM2200_MIXER_ENUMS(EQL, WM2200_EQLMIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(EQR, WM2200_EQRMIX_INPUT_1_SOURCE); + +WM2200_MIXER_ENUMS(DSP1L, WM2200_DSP1LMIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(DSP1R, WM2200_DSP1RMIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(DSP2L, WM2200_DSP2LMIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(DSP2R, WM2200_DSP2RMIX_INPUT_1_SOURCE); + +WM2200_DSP_ENUMS(DSP1, WM2200_DSP1AUX1MIX_INPUT_1_SOURCE); +WM2200_DSP_ENUMS(DSP2, WM2200_DSP2AUX1MIX_INPUT_1_SOURCE); + +WM2200_MIXER_ENUMS(LHPF1, WM2200_LHPF1MIX_INPUT_1_SOURCE); +WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE); + +#define WM2200_MUX(name, ctrl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl) + +#define WM2200_MIXER_WIDGETS(name, name_str) \ + WM2200_MUX(name_str " Input 1", &name##_in1_mux), \ + WM2200_MUX(name_str " Input 2", &name##_in2_mux), \ + WM2200_MUX(name_str " Input 3", &name##_in3_mux), \ + WM2200_MUX(name_str " Input 4", &name##_in4_mux), \ + SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0) + +#define WM2200_DSP_WIDGETS(name, name_str) \ + WM2200_MIXER_WIDGETS(name##L, name_str "L"), \ + WM2200_MIXER_WIDGETS(name##R, name_str "R"), \ + WM2200_MUX(name_str " Aux 1", &name##_aux1_mux), \ + WM2200_MUX(name_str " Aux 2", &name##_aux2_mux), \ + WM2200_MUX(name_str " Aux 3", &name##_aux3_mux), \ + WM2200_MUX(name_str " Aux 4", &name##_aux4_mux), \ + WM2200_MUX(name_str " Aux 5", &name##_aux5_mux), \ + WM2200_MUX(name_str " Aux 6", &name##_aux6_mux) + +#define WM2200_MIXER_INPUT_ROUTES(name) \ + { name, "Tone Generator", "Tone Generator" }, \ + { name, "AEC Loopback", "AEC Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "IN3L", "IN3L PGA" }, \ + { name, "IN3R", "IN3R PGA" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" }, \ + { name, "DSP2.1", "DSP2" }, \ + { name, "DSP2.2", "DSP2" }, \ + { name, "DSP2.3", "DSP2" }, \ + { name, "DSP2.4", "DSP2" }, \ + { name, "DSP2.5", "DSP2" }, \ + { name, "DSP2.6", "DSP2" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "EQL", "EQL" }, \ + { name, "EQR", "EQR" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" } + +#define WM2200_MIXER_ROUTES(widget, name) \ + { widget, NULL, name " Mixer" }, \ + { name " Mixer", NULL, name " Input 1" }, \ + { name " Mixer", NULL, name " Input 2" }, \ + { name " Mixer", NULL, name " Input 3" }, \ + { name " Mixer", NULL, name " Input 4" }, \ + WM2200_MIXER_INPUT_ROUTES(name " Input 1"), \ + WM2200_MIXER_INPUT_ROUTES(name " Input 2"), \ + WM2200_MIXER_INPUT_ROUTES(name " Input 3"), \ + WM2200_MIXER_INPUT_ROUTES(name " Input 4") + +#define WM2200_DSP_AUX_ROUTES(name) \ + { name, NULL, name " Aux 1" }, \ + { name, NULL, name " Aux 2" }, \ + { name, NULL, name " Aux 3" }, \ + { name, NULL, name " Aux 4" }, \ + { name, NULL, name " Aux 5" }, \ + { name, NULL, name " Aux 6" }, \ + WM2200_MIXER_INPUT_ROUTES(name " Aux 1"), \ + WM2200_MIXER_INPUT_ROUTES(name " Aux 2"), \ + WM2200_MIXER_INPUT_ROUTES(name " Aux 3"), \ + WM2200_MIXER_INPUT_ROUTES(name " Aux 4"), \ + WM2200_MIXER_INPUT_ROUTES(name " Aux 5"), \ + WM2200_MIXER_INPUT_ROUTES(name " Aux 6") + +static const char *wm2200_aec_loopback_texts[] = { + "OUT1L", "OUT1R", "OUT2L", "OUT2R", +}; + +static SOC_ENUM_SINGLE_DECL(wm2200_aec_loopback, + WM2200_DAC_AEC_CONTROL_1, + WM2200_AEC_LOOPBACK_SRC_SHIFT, + wm2200_aec_loopback_texts); + +static const struct snd_kcontrol_new wm2200_aec_loopback_mux = + SOC_DAPM_ENUM("AEC Loopback", wm2200_aec_loopback); + +static const struct snd_soc_dapm_widget wm2200_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", WM2200_CLOCKING_3, WM2200_SYSCLK_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_SUPPLY("CP1", WM2200_DM_CHARGE_PUMP_1, WM2200_CPDM_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_SUPPLY("CP2", WM2200_MIC_CHARGE_PUMP_1, WM2200_CPMIC_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS1", WM2200_MIC_BIAS_CTRL_1, WM2200_MICB1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", WM2200_MIC_BIAS_CTRL_2, WM2200_MICB2_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("AVDD", 20, 0), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), +SND_SOC_DAPM_INPUT("IN3L"), +SND_SOC_DAPM_INPUT("IN3R"), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_PGA("Tone Generator", WM2200_TONE_GENERATOR_1, + WM2200_TONE_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("IN1L PGA", WM2200_INPUT_ENABLES, WM2200_IN1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("IN1R PGA", WM2200_INPUT_ENABLES, WM2200_IN1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("IN2L PGA", WM2200_INPUT_ENABLES, WM2200_IN2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("IN2R PGA", WM2200_INPUT_ENABLES, WM2200_IN2R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("IN3L PGA", WM2200_INPUT_ENABLES, WM2200_IN3L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("IN3R PGA", WM2200_INPUT_ENABLES, WM2200_IN3R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", "Playback", 0, + WM2200_AUDIO_IF_1_22, WM2200_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", "Playback", 1, + WM2200_AUDIO_IF_1_22, WM2200_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", "Playback", 2, + WM2200_AUDIO_IF_1_22, WM2200_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", "Playback", 3, + WM2200_AUDIO_IF_1_22, WM2200_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", "Playback", 4, + WM2200_AUDIO_IF_1_22, WM2200_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", "Playback", 5, + WM2200_AUDIO_IF_1_22, WM2200_AIF1RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA("EQL", WM2200_EQL_1, WM2200_EQL_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQR", WM2200_EQR_1, WM2200_EQR_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", WM2200_HPLPF1_1, WM2200_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", WM2200_HPLPF2_1, WM2200_LHPF2_ENA_SHIFT, 0, + NULL, 0), + +WM_ADSP1("DSP1", 0), +WM_ADSP1("DSP2", 1), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0, + WM2200_AUDIO_IF_1_22, WM2200_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", "Capture", 1, + WM2200_AUDIO_IF_1_22, WM2200_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", "Capture", 2, + WM2200_AUDIO_IF_1_22, WM2200_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", "Capture", 3, + WM2200_AUDIO_IF_1_22, WM2200_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 4, + WM2200_AUDIO_IF_1_22, WM2200_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 5, + WM2200_AUDIO_IF_1_22, WM2200_AIF1TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_MUX("AEC Loopback", WM2200_DAC_AEC_CONTROL_1, + WM2200_AEC_LOOPBACK_ENA_SHIFT, 0, &wm2200_aec_loopback_mux), + +SND_SOC_DAPM_PGA_S("OUT1L", 0, WM2200_OUTPUT_ENABLES, + WM2200_OUT1L_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("OUT1R", 0, WM2200_OUTPUT_ENABLES, + WM2200_OUT1R_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("EPD_LP", 1, WM2200_EAR_PIECE_CTRL_1, + WM2200_EPD_LP_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("EPD_OUTP_LP", 1, WM2200_EAR_PIECE_CTRL_1, + WM2200_EPD_OUTP_LP_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("EPD_RMV_SHRT_LP", 1, WM2200_EAR_PIECE_CTRL_1, + WM2200_EPD_RMV_SHRT_LP_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("EPD_LN", 1, WM2200_EAR_PIECE_CTRL_1, + WM2200_EPD_LN_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("EPD_OUTP_LN", 1, WM2200_EAR_PIECE_CTRL_1, + WM2200_EPD_OUTP_LN_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("EPD_RMV_SHRT_LN", 1, WM2200_EAR_PIECE_CTRL_1, + WM2200_EPD_RMV_SHRT_LN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("EPD_RP", 1, WM2200_EAR_PIECE_CTRL_2, + WM2200_EPD_RP_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("EPD_OUTP_RP", 1, WM2200_EAR_PIECE_CTRL_2, + WM2200_EPD_OUTP_RP_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("EPD_RMV_SHRT_RP", 1, WM2200_EAR_PIECE_CTRL_2, + WM2200_EPD_RMV_SHRT_RP_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("EPD_RN", 1, WM2200_EAR_PIECE_CTRL_2, + WM2200_EPD_RN_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("EPD_OUTP_RN", 1, WM2200_EAR_PIECE_CTRL_2, + WM2200_EPD_OUTP_RN_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("EPD_RMV_SHRT_RN", 1, WM2200_EAR_PIECE_CTRL_2, + WM2200_EPD_RMV_SHRT_RN_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("OUT2L", WM2200_OUTPUT_ENABLES, WM2200_OUT2L_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("OUT2R", WM2200_OUTPUT_ENABLES, WM2200_OUT2R_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_OUTPUT("EPOUTLN"), +SND_SOC_DAPM_OUTPUT("EPOUTLP"), +SND_SOC_DAPM_OUTPUT("EPOUTRN"), +SND_SOC_DAPM_OUTPUT("EPOUTRP"), +SND_SOC_DAPM_OUTPUT("SPK"), + +WM2200_MIXER_WIDGETS(EQL, "EQL"), +WM2200_MIXER_WIDGETS(EQR, "EQR"), + +WM2200_MIXER_WIDGETS(LHPF1, "LHPF1"), +WM2200_MIXER_WIDGETS(LHPF2, "LHPF2"), + +WM2200_DSP_WIDGETS(DSP1, "DSP1"), +WM2200_DSP_WIDGETS(DSP2, "DSP2"), + +WM2200_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +WM2200_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +WM2200_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +WM2200_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +WM2200_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +WM2200_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), + +WM2200_MIXER_WIDGETS(OUT1L, "OUT1L"), +WM2200_MIXER_WIDGETS(OUT1R, "OUT1R"), +WM2200_MIXER_WIDGETS(OUT2L, "OUT2L"), +WM2200_MIXER_WIDGETS(OUT2R, "OUT2R"), +}; + +static const struct snd_soc_dapm_route wm2200_dapm_routes[] = { + /* Everything needs SYSCLK but only hook up things on the edge + * of the chip */ + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + { "IN3L", NULL, "SYSCLK" }, + { "IN3R", NULL, "SYSCLK" }, + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "AIF1RX1", NULL, "SYSCLK" }, + { "AIF1RX2", NULL, "SYSCLK" }, + { "AIF1RX3", NULL, "SYSCLK" }, + { "AIF1RX4", NULL, "SYSCLK" }, + { "AIF1RX5", NULL, "SYSCLK" }, + { "AIF1RX6", NULL, "SYSCLK" }, + { "AIF1TX1", NULL, "SYSCLK" }, + { "AIF1TX2", NULL, "SYSCLK" }, + { "AIF1TX3", NULL, "SYSCLK" }, + { "AIF1TX4", NULL, "SYSCLK" }, + { "AIF1TX5", NULL, "SYSCLK" }, + { "AIF1TX6", NULL, "SYSCLK" }, + + { "IN1L", NULL, "AVDD" }, + { "IN1R", NULL, "AVDD" }, + { "IN2L", NULL, "AVDD" }, + { "IN2R", NULL, "AVDD" }, + { "IN3L", NULL, "AVDD" }, + { "IN3R", NULL, "AVDD" }, + { "OUT1L", NULL, "AVDD" }, + { "OUT1R", NULL, "AVDD" }, + + { "IN1L PGA", NULL, "IN1L" }, + { "IN1R PGA", NULL, "IN1R" }, + { "IN2L PGA", NULL, "IN2L" }, + { "IN2R PGA", NULL, "IN2R" }, + { "IN3L PGA", NULL, "IN3L" }, + { "IN3R PGA", NULL, "IN3R" }, + + { "Tone Generator", NULL, "TONE" }, + + { "CP2", NULL, "CPVDD" }, + { "MICBIAS1", NULL, "CP2" }, + { "MICBIAS2", NULL, "CP2" }, + + { "CP1", NULL, "CPVDD" }, + { "EPD_LN", NULL, "CP1" }, + { "EPD_LP", NULL, "CP1" }, + { "EPD_RN", NULL, "CP1" }, + { "EPD_RP", NULL, "CP1" }, + + { "EPD_LP", NULL, "OUT1L" }, + { "EPD_OUTP_LP", NULL, "EPD_LP" }, + { "EPD_RMV_SHRT_LP", NULL, "EPD_OUTP_LP" }, + { "EPOUTLP", NULL, "EPD_RMV_SHRT_LP" }, + + { "EPD_LN", NULL, "OUT1L" }, + { "EPD_OUTP_LN", NULL, "EPD_LN" }, + { "EPD_RMV_SHRT_LN", NULL, "EPD_OUTP_LN" }, + { "EPOUTLN", NULL, "EPD_RMV_SHRT_LN" }, + + { "EPD_RP", NULL, "OUT1R" }, + { "EPD_OUTP_RP", NULL, "EPD_RP" }, + { "EPD_RMV_SHRT_RP", NULL, "EPD_OUTP_RP" }, + { "EPOUTRP", NULL, "EPD_RMV_SHRT_RP" }, + + { "EPD_RN", NULL, "OUT1R" }, + { "EPD_OUTP_RN", NULL, "EPD_RN" }, + { "EPD_RMV_SHRT_RN", NULL, "EPD_OUTP_RN" }, + { "EPOUTRN", NULL, "EPD_RMV_SHRT_RN" }, + + { "SPK", NULL, "OUT2L" }, + { "SPK", NULL, "OUT2R" }, + + { "AEC Loopback", "OUT1L", "OUT1L" }, + { "AEC Loopback", "OUT1R", "OUT1R" }, + { "AEC Loopback", "OUT2L", "OUT2L" }, + { "AEC Loopback", "OUT2R", "OUT2R" }, + + WM2200_MIXER_ROUTES("DSP1", "DSP1L"), + WM2200_MIXER_ROUTES("DSP1", "DSP1R"), + WM2200_MIXER_ROUTES("DSP2", "DSP2L"), + WM2200_MIXER_ROUTES("DSP2", "DSP2R"), + + WM2200_DSP_AUX_ROUTES("DSP1"), + WM2200_DSP_AUX_ROUTES("DSP2"), + + WM2200_MIXER_ROUTES("OUT1L", "OUT1L"), + WM2200_MIXER_ROUTES("OUT1R", "OUT1R"), + WM2200_MIXER_ROUTES("OUT2L", "OUT2L"), + WM2200_MIXER_ROUTES("OUT2R", "OUT2R"), + + WM2200_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + WM2200_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + WM2200_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + WM2200_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + WM2200_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + WM2200_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + + WM2200_MIXER_ROUTES("EQL", "EQL"), + WM2200_MIXER_ROUTES("EQR", "EQR"), + + WM2200_MIXER_ROUTES("LHPF1", "LHPF1"), + WM2200_MIXER_ROUTES("LHPF2", "LHPF2"), +}; + +static int wm2200_probe(struct snd_soc_component *component) +{ + struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component); + + wm2200->component = component; + + return 0; +} + +static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + int lrclk, bclk, fmt_val; + + lrclk = 0; + bclk = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + fmt_val = 0; + break; + case SND_SOC_DAIFMT_I2S: + fmt_val = 2; + break; + default: + dev_err(component->dev, "Unsupported DAI format %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBS_CFM: + lrclk |= WM2200_AIF1TX_LRCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + bclk |= WM2200_AIF1_BCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + lrclk |= WM2200_AIF1TX_LRCLK_MSTR; + bclk |= WM2200_AIF1_BCLK_MSTR; + break; + default: + dev_err(component->dev, "Unsupported master mode %d\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + bclk |= WM2200_AIF1_BCLK_INV; + lrclk |= WM2200_AIF1TX_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + bclk |= WM2200_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + lrclk |= WM2200_AIF1TX_LRCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_1, WM2200_AIF1_BCLK_MSTR | + WM2200_AIF1_BCLK_INV, bclk); + snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_2, + WM2200_AIF1TX_LRCLK_MSTR | WM2200_AIF1TX_LRCLK_INV, + lrclk); + snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_3, + WM2200_AIF1TX_LRCLK_MSTR | WM2200_AIF1TX_LRCLK_INV, + lrclk); + snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_5, + WM2200_AIF1_FMT_MASK, fmt_val); + + return 0; +} + +static int wm2200_sr_code[] = { + 0, + 12000, + 24000, + 48000, + 96000, + 192000, + 384000, + 768000, + 0, + 11025, + 22050, + 44100, + 88200, + 176400, + 352800, + 705600, + 4000, + 8000, + 16000, + 32000, + 64000, + 128000, + 256000, + 512000, +}; + +#define WM2200_NUM_BCLK_RATES 12 + +static int wm2200_bclk_rates_dat[WM2200_NUM_BCLK_RATES] = { + 6144000, + 3072000, + 2048000, + 1536000, + 768000, + 512000, + 384000, + 256000, + 192000, + 128000, + 96000, + 64000, +}; + +static int wm2200_bclk_rates_cd[WM2200_NUM_BCLK_RATES] = { + 5644800, + 3763200, + 2882400, + 1881600, + 1411200, + 705600, + 470400, + 352800, + 176400, + 117600, + 88200, + 58800, +}; + +static int wm2200_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component); + int i, bclk, lrclk, wl, fl, sr_code; + int *bclk_rates; + + /* Data sizes if not using TDM */ + wl = params_width(params); + if (wl < 0) + return wl; + fl = snd_soc_params_to_frame_size(params); + if (fl < 0) + return fl; + + dev_dbg(component->dev, "Word length %d bits, frame length %d bits\n", + wl, fl); + + /* Target BCLK rate */ + bclk = snd_soc_params_to_bclk(params); + if (bclk < 0) + return bclk; + + if (!wm2200->sysclk) { + dev_err(component->dev, "SYSCLK has no rate set\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(wm2200_sr_code); i++) + if (wm2200_sr_code[i] == params_rate(params)) + break; + if (i == ARRAY_SIZE(wm2200_sr_code)) { + dev_err(component->dev, "Unsupported sample rate: %dHz\n", + params_rate(params)); + return -EINVAL; + } + sr_code = i; + + dev_dbg(component->dev, "Target BCLK is %dHz, using %dHz SYSCLK\n", + bclk, wm2200->sysclk); + + if (wm2200->sysclk % 4000) + bclk_rates = wm2200_bclk_rates_cd; + else + bclk_rates = wm2200_bclk_rates_dat; + + for (i = 0; i < WM2200_NUM_BCLK_RATES; i++) + if (bclk_rates[i] >= bclk && (bclk_rates[i] % bclk == 0)) + break; + if (i == WM2200_NUM_BCLK_RATES) { + dev_err(component->dev, + "No valid BCLK for %dHz found from %dHz SYSCLK\n", + bclk, wm2200->sysclk); + return -EINVAL; + } + + bclk = i; + dev_dbg(component->dev, "Setting %dHz BCLK\n", bclk_rates[bclk]); + snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_1, + WM2200_AIF1_BCLK_DIV_MASK, bclk); + + lrclk = bclk_rates[bclk] / params_rate(params); + dev_dbg(component->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || + wm2200->symmetric_rates) + snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_7, + WM2200_AIF1RX_BCPF_MASK, lrclk); + else + snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_6, + WM2200_AIF1TX_BCPF_MASK, lrclk); + + i = (wl << WM2200_AIF1TX_WL_SHIFT) | wl; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_9, + WM2200_AIF1RX_WL_MASK | + WM2200_AIF1RX_SLOT_LEN_MASK, i); + else + snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_8, + WM2200_AIF1TX_WL_MASK | + WM2200_AIF1TX_SLOT_LEN_MASK, i); + + snd_soc_component_update_bits(component, WM2200_CLOCKING_4, + WM2200_SAMPLE_RATE_1_MASK, sr_code); + + return 0; +} + +static int wm2200_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component); + int fval; + + switch (clk_id) { + case WM2200_CLK_SYSCLK: + break; + + default: + dev_err(component->dev, "Unknown clock %d\n", clk_id); + return -EINVAL; + } + + switch (source) { + case WM2200_CLKSRC_MCLK1: + case WM2200_CLKSRC_MCLK2: + case WM2200_CLKSRC_FLL: + case WM2200_CLKSRC_BCLK1: + break; + default: + dev_err(component->dev, "Invalid source %d\n", source); + return -EINVAL; + } + + switch (freq) { + case 22579200: + case 24576000: + fval = 2; + break; + default: + dev_err(component->dev, "Invalid clock rate: %d\n", freq); + return -EINVAL; + } + + /* TODO: Check if MCLKs are in use and enable/disable pulls to + * match. + */ + + snd_soc_component_update_bits(component, WM2200_CLOCKING_3, WM2200_SYSCLK_FREQ_MASK | + WM2200_SYSCLK_SRC_MASK, + fval << WM2200_SYSCLK_FREQ_SHIFT | source); + + wm2200->sysclk = freq; + + return 0; +} + +struct _fll_div { + u16 fll_fratio; + u16 fll_outdiv; + u16 fll_refclk_div; + u16 n; + u16 theta; + u16 lambda; +}; + +static struct { + unsigned int min; + unsigned int max; + u16 fll_fratio; + int ratio; +} fll_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, + unsigned int Fout) +{ + unsigned int target; + unsigned int div; + unsigned int fratio, gcd_fll; + int i; + + /* Fref must be <=13.5MHz */ + div = 1; + fll_div->fll_refclk_div = 0; + while ((Fref / div) > 13500000) { + div *= 2; + fll_div->fll_refclk_div++; + + if (div > 8) { + pr_err("Can't scale %dMHz input down to <=13.5MHz\n", + Fref); + return -EINVAL; + } + } + + pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout); + + /* Apply the division for our remaining calculations */ + Fref /= div; + + /* Fvco should be 90-100MHz; don't check the upper bound */ + div = 2; + while (Fout * div < 90000000) { + div++; + if (div > 64) { + pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + } + target = Fout * div; + fll_div->fll_outdiv = div - 1; + + pr_debug("FLL Fvco=%dHz\n", target); + + /* Find an appropraite FLL_FRATIO and factor it out of the target */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + fll_div->fll_fratio = fll_fratios[i].fll_fratio; + fratio = fll_fratios[i].ratio; + break; + } + } + if (i == ARRAY_SIZE(fll_fratios)) { + pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref); + return -EINVAL; + } + + fll_div->n = target / (fratio * Fref); + + if (target % Fref == 0) { + fll_div->theta = 0; + fll_div->lambda = 0; + } else { + gcd_fll = gcd(target, fratio * Fref); + + fll_div->theta = (target - (fll_div->n * fratio * Fref)) + / gcd_fll; + fll_div->lambda = (fratio * Fref) / gcd_fll; + } + + pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n", + fll_div->n, fll_div->theta, fll_div->lambda); + pr_debug("FLL_FRATIO=%x(%d) FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n", + fll_div->fll_fratio, fratio, fll_div->fll_outdiv, + fll_div->fll_refclk_div); + + return 0; +} + +static int wm2200_set_fll(struct snd_soc_component *component, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct i2c_client *i2c = to_i2c_client(component->dev); + struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component); + struct _fll_div factors; + int ret, i, timeout; + unsigned long time_left; + + if (!Fout) { + dev_dbg(component->dev, "FLL disabled"); + + if (wm2200->fll_fout) + pm_runtime_put(component->dev); + + wm2200->fll_fout = 0; + snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_1, + WM2200_FLL_ENA, 0); + return 0; + } + + switch (source) { + case WM2200_FLL_SRC_MCLK1: + case WM2200_FLL_SRC_MCLK2: + case WM2200_FLL_SRC_BCLK: + break; + default: + dev_err(component->dev, "Invalid FLL source %d\n", source); + return -EINVAL; + } + + ret = fll_factors(&factors, Fref, Fout); + if (ret < 0) + return ret; + + /* Disable the FLL while we reconfigure */ + snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_1, WM2200_FLL_ENA, 0); + + snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_2, + WM2200_FLL_OUTDIV_MASK | WM2200_FLL_FRATIO_MASK, + (factors.fll_outdiv << WM2200_FLL_OUTDIV_SHIFT) | + factors.fll_fratio); + if (factors.theta) { + snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_3, + WM2200_FLL_FRACN_ENA, + WM2200_FLL_FRACN_ENA); + snd_soc_component_update_bits(component, WM2200_FLL_EFS_2, + WM2200_FLL_EFS_ENA, + WM2200_FLL_EFS_ENA); + } else { + snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_3, + WM2200_FLL_FRACN_ENA, 0); + snd_soc_component_update_bits(component, WM2200_FLL_EFS_2, + WM2200_FLL_EFS_ENA, 0); + } + + snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_4, WM2200_FLL_THETA_MASK, + factors.theta); + snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_6, WM2200_FLL_N_MASK, + factors.n); + snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_7, + WM2200_FLL_CLK_REF_DIV_MASK | + WM2200_FLL_CLK_REF_SRC_MASK, + (factors.fll_refclk_div + << WM2200_FLL_CLK_REF_DIV_SHIFT) | source); + snd_soc_component_update_bits(component, WM2200_FLL_EFS_1, + WM2200_FLL_LAMBDA_MASK, factors.lambda); + + /* Clear any pending completions */ + try_wait_for_completion(&wm2200->fll_lock); + + pm_runtime_get_sync(component->dev); + + snd_soc_component_update_bits(component, WM2200_FLL_CONTROL_1, + WM2200_FLL_ENA, WM2200_FLL_ENA); + + if (i2c->irq) + timeout = 2; + else + timeout = 50; + + snd_soc_component_update_bits(component, WM2200_CLOCKING_3, WM2200_SYSCLK_ENA, + WM2200_SYSCLK_ENA); + + /* Poll for the lock; will use the interrupt to exit quickly */ + for (i = 0; i < timeout; i++) { + if (i2c->irq) { + time_left = wait_for_completion_timeout( + &wm2200->fll_lock, + msecs_to_jiffies(25)); + if (time_left > 0) + break; + } else { + msleep(1); + } + + ret = snd_soc_component_read(component, + WM2200_INTERRUPT_RAW_STATUS_2); + if (ret < 0) { + dev_err(component->dev, + "Failed to read FLL status: %d\n", + ret); + continue; + } + if (ret & WM2200_FLL_LOCK_STS) + break; + } + if (i == timeout) { + dev_err(component->dev, "FLL lock timed out\n"); + pm_runtime_put(component->dev); + return -ETIMEDOUT; + } + + wm2200->fll_src = source; + wm2200->fll_fref = Fref; + wm2200->fll_fout = Fout; + + dev_dbg(component->dev, "FLL running %dHz->%dHz\n", Fref, Fout); + + return 0; +} + +static int wm2200_dai_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm2200_priv *wm2200 = snd_soc_component_get_drvdata(component); + unsigned int val = 0; + int ret; + + ret = snd_soc_component_read(component, WM2200_GPIO_CTRL_1); + if (ret >= 0) { + if ((ret & WM2200_GP1_FN_MASK) != 0) { + wm2200->symmetric_rates = true; + val = WM2200_AIF1TX_LRCLK_SRC; + } + } else { + dev_err(component->dev, "Failed to read GPIO 1 config: %d\n", ret); + } + + snd_soc_component_update_bits(component, WM2200_AUDIO_IF_1_2, + WM2200_AIF1TX_LRCLK_SRC, val); + + return 0; +} + +static const struct snd_soc_dai_ops wm2200_dai_ops = { + .probe = wm2200_dai_probe, + .set_fmt = wm2200_set_fmt, + .hw_params = wm2200_hw_params, +}; + +#define WM2200_RATES SNDRV_PCM_RATE_8000_48000 + +#define WM2200_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm2200_dai = { + .name = "wm2200", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM2200_RATES, + .formats = WM2200_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = WM2200_RATES, + .formats = WM2200_FORMATS, + }, + .ops = &wm2200_dai_ops, +}; + +static const struct snd_soc_component_driver soc_component_wm2200 = { + .probe = wm2200_probe, + .set_sysclk = wm2200_set_sysclk, + .set_pll = wm2200_set_fll, + .controls = wm2200_snd_controls, + .num_controls = ARRAY_SIZE(wm2200_snd_controls), + .dapm_widgets = wm2200_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm2200_dapm_widgets), + .dapm_routes = wm2200_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm2200_dapm_routes), + .endianness = 1, +}; + +static irqreturn_t wm2200_irq(int irq, void *data) +{ + struct wm2200_priv *wm2200 = data; + unsigned int val, mask; + int ret; + + ret = regmap_read(wm2200->regmap, WM2200_INTERRUPT_STATUS_2, &val); + if (ret != 0) { + dev_err(wm2200->dev, "Failed to read IRQ status: %d\n", ret); + return IRQ_NONE; + } + + ret = regmap_read(wm2200->regmap, WM2200_INTERRUPT_STATUS_2_MASK, + &mask); + if (ret != 0) { + dev_warn(wm2200->dev, "Failed to read IRQ mask: %d\n", ret); + mask = 0; + } + + val &= ~mask; + + if (val & WM2200_FLL_LOCK_EINT) { + dev_dbg(wm2200->dev, "FLL locked\n"); + complete(&wm2200->fll_lock); + } + + if (val) { + regmap_write(wm2200->regmap, WM2200_INTERRUPT_STATUS_2, val); + + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +static const struct regmap_config wm2200_regmap = { + .reg_bits = 16, + .val_bits = 16, + + .max_register = WM2200_MAX_REGISTER + (ARRAY_SIZE(wm2200_ranges) * + WM2200_DSP_SPACING), + .reg_defaults = wm2200_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm2200_reg_defaults), + .volatile_reg = wm2200_volatile_register, + .readable_reg = wm2200_readable_register, + .cache_type = REGCACHE_MAPLE, + .ranges = wm2200_ranges, + .num_ranges = ARRAY_SIZE(wm2200_ranges), +}; + +static const unsigned int wm2200_dig_vu[] = { + WM2200_DAC_DIGITAL_VOLUME_1L, + WM2200_DAC_DIGITAL_VOLUME_1R, + WM2200_DAC_DIGITAL_VOLUME_2L, + WM2200_DAC_DIGITAL_VOLUME_2R, + WM2200_ADC_DIGITAL_VOLUME_1L, + WM2200_ADC_DIGITAL_VOLUME_1R, + WM2200_ADC_DIGITAL_VOLUME_2L, + WM2200_ADC_DIGITAL_VOLUME_2R, + WM2200_ADC_DIGITAL_VOLUME_3L, + WM2200_ADC_DIGITAL_VOLUME_3R, +}; + +static const unsigned int wm2200_mic_ctrl_reg[] = { + WM2200_IN1L_CONTROL, + WM2200_IN2L_CONTROL, + WM2200_IN3L_CONTROL, +}; + +static int wm2200_i2c_probe(struct i2c_client *i2c) +{ + struct wm2200_pdata *pdata = dev_get_platdata(&i2c->dev); + struct wm2200_priv *wm2200; + unsigned int reg; + int ret, i; + int val; + + wm2200 = devm_kzalloc(&i2c->dev, sizeof(struct wm2200_priv), + GFP_KERNEL); + if (wm2200 == NULL) + return -ENOMEM; + + wm2200->dev = &i2c->dev; + init_completion(&wm2200->fll_lock); + + wm2200->regmap = devm_regmap_init_i2c(i2c, &wm2200_regmap); + if (IS_ERR(wm2200->regmap)) { + ret = PTR_ERR(wm2200->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + for (i = 0; i < 2; i++) { + wm2200->dsp[i].cs_dsp.type = WMFW_ADSP1; + wm2200->dsp[i].part = "wm2200"; + wm2200->dsp[i].cs_dsp.num = i + 1; + wm2200->dsp[i].cs_dsp.dev = &i2c->dev; + wm2200->dsp[i].cs_dsp.regmap = wm2200->regmap; + wm2200->dsp[i].cs_dsp.sysclk_reg = WM2200_CLOCKING_3; + wm2200->dsp[i].cs_dsp.sysclk_mask = WM2200_SYSCLK_FREQ_MASK; + wm2200->dsp[i].cs_dsp.sysclk_shift = WM2200_SYSCLK_FREQ_SHIFT; + } + + wm2200->dsp[0].cs_dsp.base = WM2200_DSP1_CONTROL_1; + wm2200->dsp[0].cs_dsp.mem = wm2200_dsp1_regions; + wm2200->dsp[0].cs_dsp.num_mems = ARRAY_SIZE(wm2200_dsp1_regions); + + wm2200->dsp[1].cs_dsp.base = WM2200_DSP2_CONTROL_1; + wm2200->dsp[1].cs_dsp.mem = wm2200_dsp2_regions; + wm2200->dsp[1].cs_dsp.num_mems = ARRAY_SIZE(wm2200_dsp2_regions); + + for (i = 0; i < ARRAY_SIZE(wm2200->dsp); i++) + wm_adsp1_init(&wm2200->dsp[i]); + + if (pdata) + wm2200->pdata = *pdata; + + i2c_set_clientdata(i2c, wm2200); + + for (i = 0; i < ARRAY_SIZE(wm2200->core_supplies); i++) + wm2200->core_supplies[i].supply = wm2200_core_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, + ARRAY_SIZE(wm2200->core_supplies), + wm2200->core_supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request core supplies: %d\n", + ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm2200->core_supplies), + wm2200->core_supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable core supplies: %d\n", + ret); + return ret; + } + + if (wm2200->pdata.ldo_ena) { + ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.ldo_ena, + GPIOF_OUT_INIT_HIGH, + "WM2200 LDOENA"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n", + wm2200->pdata.ldo_ena, ret); + goto err_enable; + } + msleep(2); + } + + if (wm2200->pdata.reset) { + ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.reset, + GPIOF_OUT_INIT_HIGH, + "WM2200 /RESET"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n", + wm2200->pdata.reset, ret); + goto err_ldo; + } + } + + ret = regmap_read(wm2200->regmap, WM2200_SOFTWARE_RESET, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret); + goto err_reset; + } + switch (reg) { + case 0x2200: + break; + + default: + dev_err(&i2c->dev, "Device is not a WM2200, ID is %x\n", reg); + ret = -EINVAL; + goto err_reset; + } + + ret = regmap_read(wm2200->regmap, WM2200_DEVICE_REVISION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read revision register\n"); + goto err_reset; + } + + wm2200->rev = reg & WM2200_DEVICE_REVISION_MASK; + + dev_info(&i2c->dev, "revision %c\n", wm2200->rev + 'A'); + + switch (wm2200->rev) { + case 0: + case 1: + ret = regmap_register_patch(wm2200->regmap, wm2200_reva_patch, + ARRAY_SIZE(wm2200_reva_patch)); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register patch: %d\n", + ret); + } + break; + default: + break; + } + + ret = wm2200_reset(wm2200); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to issue reset\n"); + goto err_reset; + } + + for (i = 0; i < ARRAY_SIZE(wm2200->pdata.gpio_defaults); i++) { + if (!wm2200->pdata.gpio_defaults[i]) + continue; + + regmap_write(wm2200->regmap, WM2200_GPIO_CTRL_1 + i, + wm2200->pdata.gpio_defaults[i]); + } + + for (i = 0; i < ARRAY_SIZE(wm2200_dig_vu); i++) + regmap_update_bits(wm2200->regmap, wm2200_dig_vu[i], + WM2200_OUT_VU, WM2200_OUT_VU); + + /* Assign slots 1-6 to channels 1-6 for both TX and RX */ + for (i = 0; i < 6; i++) { + regmap_write(wm2200->regmap, WM2200_AUDIO_IF_1_10 + i, i); + regmap_write(wm2200->regmap, WM2200_AUDIO_IF_1_16 + i, i); + } + + for (i = 0; i < WM2200_MAX_MICBIAS; i++) { + if (!wm2200->pdata.micbias[i].mb_lvl && + !wm2200->pdata.micbias[i].bypass) + continue; + + /* Apply default for bypass mode */ + if (!wm2200->pdata.micbias[i].mb_lvl) + wm2200->pdata.micbias[i].mb_lvl + = WM2200_MBIAS_LVL_1V5; + + val = (wm2200->pdata.micbias[i].mb_lvl -1) + << WM2200_MICB1_LVL_SHIFT; + + if (wm2200->pdata.micbias[i].discharge) + val |= WM2200_MICB1_DISCH; + + if (wm2200->pdata.micbias[i].fast_start) + val |= WM2200_MICB1_RATE; + + if (wm2200->pdata.micbias[i].bypass) + val |= WM2200_MICB1_MODE; + + regmap_update_bits(wm2200->regmap, + WM2200_MIC_BIAS_CTRL_1 + i, + WM2200_MICB1_LVL_MASK | + WM2200_MICB1_DISCH | + WM2200_MICB1_MODE | + WM2200_MICB1_RATE, val); + } + + for (i = 0; i < ARRAY_SIZE(wm2200->pdata.in_mode); i++) { + regmap_update_bits(wm2200->regmap, wm2200_mic_ctrl_reg[i], + WM2200_IN1_MODE_MASK | + WM2200_IN1_DMIC_SUP_MASK, + (wm2200->pdata.in_mode[i] << + WM2200_IN1_MODE_SHIFT) | + (wm2200->pdata.dmic_sup[i] << + WM2200_IN1_DMIC_SUP_SHIFT)); + } + + if (i2c->irq) { + ret = request_threaded_irq(i2c->irq, NULL, wm2200_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "wm2200", wm2200); + if (ret == 0) + regmap_update_bits(wm2200->regmap, + WM2200_INTERRUPT_STATUS_2_MASK, + WM2200_FLL_LOCK_EINT, 0); + else + dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", + i2c->irq, ret); + } + + pm_runtime_set_active(&i2c->dev); + pm_runtime_enable(&i2c->dev); + pm_request_idle(&i2c->dev); + + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_wm2200, + &wm2200_dai, 1); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + goto err_pm_runtime; + } + + return 0; + +err_pm_runtime: + pm_runtime_disable(&i2c->dev); + if (i2c->irq) + free_irq(i2c->irq, wm2200); +err_reset: + if (wm2200->pdata.reset) + gpio_set_value_cansleep(wm2200->pdata.reset, 0); +err_ldo: + if (wm2200->pdata.ldo_ena) + gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0); +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), + wm2200->core_supplies); + return ret; +} + +static void wm2200_i2c_remove(struct i2c_client *i2c) +{ + struct wm2200_priv *wm2200 = i2c_get_clientdata(i2c); + + pm_runtime_disable(&i2c->dev); + if (i2c->irq) + free_irq(i2c->irq, wm2200); + if (wm2200->pdata.reset) + gpio_set_value_cansleep(wm2200->pdata.reset, 0); + if (wm2200->pdata.ldo_ena) + gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0); + regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), + wm2200->core_supplies); +} + +#ifdef CONFIG_PM +static int wm2200_runtime_suspend(struct device *dev) +{ + struct wm2200_priv *wm2200 = dev_get_drvdata(dev); + + regcache_cache_only(wm2200->regmap, true); + regcache_mark_dirty(wm2200->regmap); + if (wm2200->pdata.ldo_ena) + gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0); + regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies), + wm2200->core_supplies); + + return 0; +} + +static int wm2200_runtime_resume(struct device *dev) +{ + struct wm2200_priv *wm2200 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(wm2200->core_supplies), + wm2200->core_supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", + ret); + return ret; + } + + if (wm2200->pdata.ldo_ena) { + gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 1); + msleep(2); + } + + regcache_cache_only(wm2200->regmap, false); + regcache_sync(wm2200->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops wm2200_pm = { + SET_RUNTIME_PM_OPS(wm2200_runtime_suspend, wm2200_runtime_resume, + NULL) +}; + +static const struct i2c_device_id wm2200_i2c_id[] = { + { "wm2200", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm2200_i2c_id); + +static struct i2c_driver wm2200_i2c_driver = { + .driver = { + .name = "wm2200", + .pm = &wm2200_pm, + }, + .probe = wm2200_i2c_probe, + .remove = wm2200_i2c_remove, + .id_table = wm2200_i2c_id, +}; + +module_i2c_driver(wm2200_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM2200 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm2200.h b/sound/soc/codecs/wm2200.h new file mode 100644 index 0000000000..906117bd36 --- /dev/null +++ b/sound/soc/codecs/wm2200.h @@ -0,0 +1,3670 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * wm2200.h - WM2200 audio codec interface + * + * Copyright 2012 Wolfson Microelectronics PLC. + * Author: Mark Brown + */ + +#ifndef _WM2200_H +#define _WM2200_H + +#define WM2200_CLK_SYSCLK 1 + +#define WM2200_CLKSRC_MCLK1 0 +#define WM2200_CLKSRC_MCLK2 1 +#define WM2200_CLKSRC_FLL 4 +#define WM2200_CLKSRC_BCLK1 8 + +#define WM2200_FLL_SRC_MCLK1 0 +#define WM2200_FLL_SRC_MCLK2 1 +#define WM2200_FLL_SRC_BCLK 2 + +/* + * Register values. + */ +#define WM2200_SOFTWARE_RESET 0x00 +#define WM2200_DEVICE_REVISION 0x01 +#define WM2200_TONE_GENERATOR_1 0x0B +#define WM2200_CLOCKING_3 0x102 +#define WM2200_CLOCKING_4 0x103 +#define WM2200_FLL_CONTROL_1 0x111 +#define WM2200_FLL_CONTROL_2 0x112 +#define WM2200_FLL_CONTROL_3 0x113 +#define WM2200_FLL_CONTROL_4 0x114 +#define WM2200_FLL_CONTROL_6 0x116 +#define WM2200_FLL_CONTROL_7 0x117 +#define WM2200_FLL_EFS_1 0x119 +#define WM2200_FLL_EFS_2 0x11A +#define WM2200_MIC_CHARGE_PUMP_1 0x200 +#define WM2200_MIC_CHARGE_PUMP_2 0x201 +#define WM2200_DM_CHARGE_PUMP_1 0x202 +#define WM2200_MIC_BIAS_CTRL_1 0x20C +#define WM2200_MIC_BIAS_CTRL_2 0x20D +#define WM2200_EAR_PIECE_CTRL_1 0x20F +#define WM2200_EAR_PIECE_CTRL_2 0x210 +#define WM2200_INPUT_ENABLES 0x301 +#define WM2200_IN1L_CONTROL 0x302 +#define WM2200_IN1R_CONTROL 0x303 +#define WM2200_IN2L_CONTROL 0x304 +#define WM2200_IN2R_CONTROL 0x305 +#define WM2200_IN3L_CONTROL 0x306 +#define WM2200_IN3R_CONTROL 0x307 +#define WM2200_RXANC_SRC 0x30A +#define WM2200_INPUT_VOLUME_RAMP 0x30B +#define WM2200_ADC_DIGITAL_VOLUME_1L 0x30C +#define WM2200_ADC_DIGITAL_VOLUME_1R 0x30D +#define WM2200_ADC_DIGITAL_VOLUME_2L 0x30E +#define WM2200_ADC_DIGITAL_VOLUME_2R 0x30F +#define WM2200_ADC_DIGITAL_VOLUME_3L 0x310 +#define WM2200_ADC_DIGITAL_VOLUME_3R 0x311 +#define WM2200_OUTPUT_ENABLES 0x400 +#define WM2200_DAC_VOLUME_LIMIT_1L 0x401 +#define WM2200_DAC_VOLUME_LIMIT_1R 0x402 +#define WM2200_DAC_VOLUME_LIMIT_2L 0x403 +#define WM2200_DAC_VOLUME_LIMIT_2R 0x404 +#define WM2200_DAC_AEC_CONTROL_1 0x409 +#define WM2200_OUTPUT_VOLUME_RAMP 0x40A +#define WM2200_DAC_DIGITAL_VOLUME_1L 0x40B +#define WM2200_DAC_DIGITAL_VOLUME_1R 0x40C +#define WM2200_DAC_DIGITAL_VOLUME_2L 0x40D +#define WM2200_DAC_DIGITAL_VOLUME_2R 0x40E +#define WM2200_PDM_1 0x417 +#define WM2200_PDM_2 0x418 +#define WM2200_AUDIO_IF_1_1 0x500 +#define WM2200_AUDIO_IF_1_2 0x501 +#define WM2200_AUDIO_IF_1_3 0x502 +#define WM2200_AUDIO_IF_1_4 0x503 +#define WM2200_AUDIO_IF_1_5 0x504 +#define WM2200_AUDIO_IF_1_6 0x505 +#define WM2200_AUDIO_IF_1_7 0x506 +#define WM2200_AUDIO_IF_1_8 0x507 +#define WM2200_AUDIO_IF_1_9 0x508 +#define WM2200_AUDIO_IF_1_10 0x509 +#define WM2200_AUDIO_IF_1_11 0x50A +#define WM2200_AUDIO_IF_1_12 0x50B +#define WM2200_AUDIO_IF_1_13 0x50C +#define WM2200_AUDIO_IF_1_14 0x50D +#define WM2200_AUDIO_IF_1_15 0x50E +#define WM2200_AUDIO_IF_1_16 0x50F +#define WM2200_AUDIO_IF_1_17 0x510 +#define WM2200_AUDIO_IF_1_18 0x511 +#define WM2200_AUDIO_IF_1_19 0x512 +#define WM2200_AUDIO_IF_1_20 0x513 +#define WM2200_AUDIO_IF_1_21 0x514 +#define WM2200_AUDIO_IF_1_22 0x515 +#define WM2200_OUT1LMIX_INPUT_1_SOURCE 0x600 +#define WM2200_OUT1LMIX_INPUT_1_VOLUME 0x601 +#define WM2200_OUT1LMIX_INPUT_2_SOURCE 0x602 +#define WM2200_OUT1LMIX_INPUT_2_VOLUME 0x603 +#define WM2200_OUT1LMIX_INPUT_3_SOURCE 0x604 +#define WM2200_OUT1LMIX_INPUT_3_VOLUME 0x605 +#define WM2200_OUT1LMIX_INPUT_4_SOURCE 0x606 +#define WM2200_OUT1LMIX_INPUT_4_VOLUME 0x607 +#define WM2200_OUT1RMIX_INPUT_1_SOURCE 0x608 +#define WM2200_OUT1RMIX_INPUT_1_VOLUME 0x609 +#define WM2200_OUT1RMIX_INPUT_2_SOURCE 0x60A +#define WM2200_OUT1RMIX_INPUT_2_VOLUME 0x60B +#define WM2200_OUT1RMIX_INPUT_3_SOURCE 0x60C +#define WM2200_OUT1RMIX_INPUT_3_VOLUME 0x60D +#define WM2200_OUT1RMIX_INPUT_4_SOURCE 0x60E +#define WM2200_OUT1RMIX_INPUT_4_VOLUME 0x60F +#define WM2200_OUT2LMIX_INPUT_1_SOURCE 0x610 +#define WM2200_OUT2LMIX_INPUT_1_VOLUME 0x611 +#define WM2200_OUT2LMIX_INPUT_2_SOURCE 0x612 +#define WM2200_OUT2LMIX_INPUT_2_VOLUME 0x613 +#define WM2200_OUT2LMIX_INPUT_3_SOURCE 0x614 +#define WM2200_OUT2LMIX_INPUT_3_VOLUME 0x615 +#define WM2200_OUT2LMIX_INPUT_4_SOURCE 0x616 +#define WM2200_OUT2LMIX_INPUT_4_VOLUME 0x617 +#define WM2200_OUT2RMIX_INPUT_1_SOURCE 0x618 +#define WM2200_OUT2RMIX_INPUT_1_VOLUME 0x619 +#define WM2200_OUT2RMIX_INPUT_2_SOURCE 0x61A +#define WM2200_OUT2RMIX_INPUT_2_VOLUME 0x61B +#define WM2200_OUT2RMIX_INPUT_3_SOURCE 0x61C +#define WM2200_OUT2RMIX_INPUT_3_VOLUME 0x61D +#define WM2200_OUT2RMIX_INPUT_4_SOURCE 0x61E +#define WM2200_OUT2RMIX_INPUT_4_VOLUME 0x61F +#define WM2200_AIF1TX1MIX_INPUT_1_SOURCE 0x620 +#define WM2200_AIF1TX1MIX_INPUT_1_VOLUME 0x621 +#define WM2200_AIF1TX1MIX_INPUT_2_SOURCE 0x622 +#define WM2200_AIF1TX1MIX_INPUT_2_VOLUME 0x623 +#define WM2200_AIF1TX1MIX_INPUT_3_SOURCE 0x624 +#define WM2200_AIF1TX1MIX_INPUT_3_VOLUME 0x625 +#define WM2200_AIF1TX1MIX_INPUT_4_SOURCE 0x626 +#define WM2200_AIF1TX1MIX_INPUT_4_VOLUME 0x627 +#define WM2200_AIF1TX2MIX_INPUT_1_SOURCE 0x628 +#define WM2200_AIF1TX2MIX_INPUT_1_VOLUME 0x629 +#define WM2200_AIF1TX2MIX_INPUT_2_SOURCE 0x62A +#define WM2200_AIF1TX2MIX_INPUT_2_VOLUME 0x62B +#define WM2200_AIF1TX2MIX_INPUT_3_SOURCE 0x62C +#define WM2200_AIF1TX2MIX_INPUT_3_VOLUME 0x62D +#define WM2200_AIF1TX2MIX_INPUT_4_SOURCE 0x62E +#define WM2200_AIF1TX2MIX_INPUT_4_VOLUME 0x62F +#define WM2200_AIF1TX3MIX_INPUT_1_SOURCE 0x630 +#define WM2200_AIF1TX3MIX_INPUT_1_VOLUME 0x631 +#define WM2200_AIF1TX3MIX_INPUT_2_SOURCE 0x632 +#define WM2200_AIF1TX3MIX_INPUT_2_VOLUME 0x633 +#define WM2200_AIF1TX3MIX_INPUT_3_SOURCE 0x634 +#define WM2200_AIF1TX3MIX_INPUT_3_VOLUME 0x635 +#define WM2200_AIF1TX3MIX_INPUT_4_SOURCE 0x636 +#define WM2200_AIF1TX3MIX_INPUT_4_VOLUME 0x637 +#define WM2200_AIF1TX4MIX_INPUT_1_SOURCE 0x638 +#define WM2200_AIF1TX4MIX_INPUT_1_VOLUME 0x639 +#define WM2200_AIF1TX4MIX_INPUT_2_SOURCE 0x63A +#define WM2200_AIF1TX4MIX_INPUT_2_VOLUME 0x63B +#define WM2200_AIF1TX4MIX_INPUT_3_SOURCE 0x63C +#define WM2200_AIF1TX4MIX_INPUT_3_VOLUME 0x63D +#define WM2200_AIF1TX4MIX_INPUT_4_SOURCE 0x63E +#define WM2200_AIF1TX4MIX_INPUT_4_VOLUME 0x63F +#define WM2200_AIF1TX5MIX_INPUT_1_SOURCE 0x640 +#define WM2200_AIF1TX5MIX_INPUT_1_VOLUME 0x641 +#define WM2200_AIF1TX5MIX_INPUT_2_SOURCE 0x642 +#define WM2200_AIF1TX5MIX_INPUT_2_VOLUME 0x643 +#define WM2200_AIF1TX5MIX_INPUT_3_SOURCE 0x644 +#define WM2200_AIF1TX5MIX_INPUT_3_VOLUME 0x645 +#define WM2200_AIF1TX5MIX_INPUT_4_SOURCE 0x646 +#define WM2200_AIF1TX5MIX_INPUT_4_VOLUME 0x647 +#define WM2200_AIF1TX6MIX_INPUT_1_SOURCE 0x648 +#define WM2200_AIF1TX6MIX_INPUT_1_VOLUME 0x649 +#define WM2200_AIF1TX6MIX_INPUT_2_SOURCE 0x64A +#define WM2200_AIF1TX6MIX_INPUT_2_VOLUME 0x64B +#define WM2200_AIF1TX6MIX_INPUT_3_SOURCE 0x64C +#define WM2200_AIF1TX6MIX_INPUT_3_VOLUME 0x64D +#define WM2200_AIF1TX6MIX_INPUT_4_SOURCE 0x64E +#define WM2200_AIF1TX6MIX_INPUT_4_VOLUME 0x64F +#define WM2200_EQLMIX_INPUT_1_SOURCE 0x650 +#define WM2200_EQLMIX_INPUT_1_VOLUME 0x651 +#define WM2200_EQLMIX_INPUT_2_SOURCE 0x652 +#define WM2200_EQLMIX_INPUT_2_VOLUME 0x653 +#define WM2200_EQLMIX_INPUT_3_SOURCE 0x654 +#define WM2200_EQLMIX_INPUT_3_VOLUME 0x655 +#define WM2200_EQLMIX_INPUT_4_SOURCE 0x656 +#define WM2200_EQLMIX_INPUT_4_VOLUME 0x657 +#define WM2200_EQRMIX_INPUT_1_SOURCE 0x658 +#define WM2200_EQRMIX_INPUT_1_VOLUME 0x659 +#define WM2200_EQRMIX_INPUT_2_SOURCE 0x65A +#define WM2200_EQRMIX_INPUT_2_VOLUME 0x65B +#define WM2200_EQRMIX_INPUT_3_SOURCE 0x65C +#define WM2200_EQRMIX_INPUT_3_VOLUME 0x65D +#define WM2200_EQRMIX_INPUT_4_SOURCE 0x65E +#define WM2200_EQRMIX_INPUT_4_VOLUME 0x65F +#define WM2200_LHPF1MIX_INPUT_1_SOURCE 0x660 +#define WM2200_LHPF1MIX_INPUT_1_VOLUME 0x661 +#define WM2200_LHPF1MIX_INPUT_2_SOURCE 0x662 +#define WM2200_LHPF1MIX_INPUT_2_VOLUME 0x663 +#define WM2200_LHPF1MIX_INPUT_3_SOURCE 0x664 +#define WM2200_LHPF1MIX_INPUT_3_VOLUME 0x665 +#define WM2200_LHPF1MIX_INPUT_4_SOURCE 0x666 +#define WM2200_LHPF1MIX_INPUT_4_VOLUME 0x667 +#define WM2200_LHPF2MIX_INPUT_1_SOURCE 0x668 +#define WM2200_LHPF2MIX_INPUT_1_VOLUME 0x669 +#define WM2200_LHPF2MIX_INPUT_2_SOURCE 0x66A +#define WM2200_LHPF2MIX_INPUT_2_VOLUME 0x66B +#define WM2200_LHPF2MIX_INPUT_3_SOURCE 0x66C +#define WM2200_LHPF2MIX_INPUT_3_VOLUME 0x66D +#define WM2200_LHPF2MIX_INPUT_4_SOURCE 0x66E +#define WM2200_LHPF2MIX_INPUT_4_VOLUME 0x66F +#define WM2200_DSP1LMIX_INPUT_1_SOURCE 0x670 +#define WM2200_DSP1LMIX_INPUT_1_VOLUME 0x671 +#define WM2200_DSP1LMIX_INPUT_2_SOURCE 0x672 +#define WM2200_DSP1LMIX_INPUT_2_VOLUME 0x673 +#define WM2200_DSP1LMIX_INPUT_3_SOURCE 0x674 +#define WM2200_DSP1LMIX_INPUT_3_VOLUME 0x675 +#define WM2200_DSP1LMIX_INPUT_4_SOURCE 0x676 +#define WM2200_DSP1LMIX_INPUT_4_VOLUME 0x677 +#define WM2200_DSP1RMIX_INPUT_1_SOURCE 0x678 +#define WM2200_DSP1RMIX_INPUT_1_VOLUME 0x679 +#define WM2200_DSP1RMIX_INPUT_2_SOURCE 0x67A +#define WM2200_DSP1RMIX_INPUT_2_VOLUME 0x67B +#define WM2200_DSP1RMIX_INPUT_3_SOURCE 0x67C +#define WM2200_DSP1RMIX_INPUT_3_VOLUME 0x67D +#define WM2200_DSP1RMIX_INPUT_4_SOURCE 0x67E +#define WM2200_DSP1RMIX_INPUT_4_VOLUME 0x67F +#define WM2200_DSP1AUX1MIX_INPUT_1_SOURCE 0x680 +#define WM2200_DSP1AUX2MIX_INPUT_1_SOURCE 0x681 +#define WM2200_DSP1AUX3MIX_INPUT_1_SOURCE 0x682 +#define WM2200_DSP1AUX4MIX_INPUT_1_SOURCE 0x683 +#define WM2200_DSP1AUX5MIX_INPUT_1_SOURCE 0x684 +#define WM2200_DSP1AUX6MIX_INPUT_1_SOURCE 0x685 +#define WM2200_DSP2LMIX_INPUT_1_SOURCE 0x686 +#define WM2200_DSP2LMIX_INPUT_1_VOLUME 0x687 +#define WM2200_DSP2LMIX_INPUT_2_SOURCE 0x688 +#define WM2200_DSP2LMIX_INPUT_2_VOLUME 0x689 +#define WM2200_DSP2LMIX_INPUT_3_SOURCE 0x68A +#define WM2200_DSP2LMIX_INPUT_3_VOLUME 0x68B +#define WM2200_DSP2LMIX_INPUT_4_SOURCE 0x68C +#define WM2200_DSP2LMIX_INPUT_4_VOLUME 0x68D +#define WM2200_DSP2RMIX_INPUT_1_SOURCE 0x68E +#define WM2200_DSP2RMIX_INPUT_1_VOLUME 0x68F +#define WM2200_DSP2RMIX_INPUT_2_SOURCE 0x690 +#define WM2200_DSP2RMIX_INPUT_2_VOLUME 0x691 +#define WM2200_DSP2RMIX_INPUT_3_SOURCE 0x692 +#define WM2200_DSP2RMIX_INPUT_3_VOLUME 0x693 +#define WM2200_DSP2RMIX_INPUT_4_SOURCE 0x694 +#define WM2200_DSP2RMIX_INPUT_4_VOLUME 0x695 +#define WM2200_DSP2AUX1MIX_INPUT_1_SOURCE 0x696 +#define WM2200_DSP2AUX2MIX_INPUT_1_SOURCE 0x697 +#define WM2200_DSP2AUX3MIX_INPUT_1_SOURCE 0x698 +#define WM2200_DSP2AUX4MIX_INPUT_1_SOURCE 0x699 +#define WM2200_DSP2AUX5MIX_INPUT_1_SOURCE 0x69A +#define WM2200_DSP2AUX6MIX_INPUT_1_SOURCE 0x69B +#define WM2200_GPIO_CTRL_1 0x700 +#define WM2200_GPIO_CTRL_2 0x701 +#define WM2200_GPIO_CTRL_3 0x702 +#define WM2200_GPIO_CTRL_4 0x703 +#define WM2200_ADPS1_IRQ0 0x707 +#define WM2200_ADPS1_IRQ1 0x708 +#define WM2200_MISC_PAD_CTRL_1 0x709 +#define WM2200_INTERRUPT_STATUS_1 0x800 +#define WM2200_INTERRUPT_STATUS_1_MASK 0x801 +#define WM2200_INTERRUPT_STATUS_2 0x802 +#define WM2200_INTERRUPT_RAW_STATUS_2 0x803 +#define WM2200_INTERRUPT_STATUS_2_MASK 0x804 +#define WM2200_INTERRUPT_CONTROL 0x808 +#define WM2200_EQL_1 0x900 +#define WM2200_EQL_2 0x901 +#define WM2200_EQL_3 0x902 +#define WM2200_EQL_4 0x903 +#define WM2200_EQL_5 0x904 +#define WM2200_EQL_6 0x905 +#define WM2200_EQL_7 0x906 +#define WM2200_EQL_8 0x907 +#define WM2200_EQL_9 0x908 +#define WM2200_EQL_10 0x909 +#define WM2200_EQL_11 0x90A +#define WM2200_EQL_12 0x90B +#define WM2200_EQL_13 0x90C +#define WM2200_EQL_14 0x90D +#define WM2200_EQL_15 0x90E +#define WM2200_EQL_16 0x90F +#define WM2200_EQL_17 0x910 +#define WM2200_EQL_18 0x911 +#define WM2200_EQL_19 0x912 +#define WM2200_EQL_20 0x913 +#define WM2200_EQR_1 0x916 +#define WM2200_EQR_2 0x917 +#define WM2200_EQR_3 0x918 +#define WM2200_EQR_4 0x919 +#define WM2200_EQR_5 0x91A +#define WM2200_EQR_6 0x91B +#define WM2200_EQR_7 0x91C +#define WM2200_EQR_8 0x91D +#define WM2200_EQR_9 0x91E +#define WM2200_EQR_10 0x91F +#define WM2200_EQR_11 0x920 +#define WM2200_EQR_12 0x921 +#define WM2200_EQR_13 0x922 +#define WM2200_EQR_14 0x923 +#define WM2200_EQR_15 0x924 +#define WM2200_EQR_16 0x925 +#define WM2200_EQR_17 0x926 +#define WM2200_EQR_18 0x927 +#define WM2200_EQR_19 0x928 +#define WM2200_EQR_20 0x929 +#define WM2200_HPLPF1_1 0x93E +#define WM2200_HPLPF1_2 0x93F +#define WM2200_HPLPF2_1 0x942 +#define WM2200_HPLPF2_2 0x943 +#define WM2200_DSP1_CONTROL_1 0xA00 +#define WM2200_DSP1_CONTROL_2 0xA02 +#define WM2200_DSP1_CONTROL_3 0xA03 +#define WM2200_DSP1_CONTROL_4 0xA04 +#define WM2200_DSP1_CONTROL_5 0xA06 +#define WM2200_DSP1_CONTROL_6 0xA07 +#define WM2200_DSP1_CONTROL_7 0xA08 +#define WM2200_DSP1_CONTROL_8 0xA09 +#define WM2200_DSP1_CONTROL_9 0xA0A +#define WM2200_DSP1_CONTROL_10 0xA0B +#define WM2200_DSP1_CONTROL_11 0xA0C +#define WM2200_DSP1_CONTROL_12 0xA0D +#define WM2200_DSP1_CONTROL_13 0xA0F +#define WM2200_DSP1_CONTROL_14 0xA10 +#define WM2200_DSP1_CONTROL_15 0xA11 +#define WM2200_DSP1_CONTROL_16 0xA12 +#define WM2200_DSP1_CONTROL_17 0xA13 +#define WM2200_DSP1_CONTROL_18 0xA14 +#define WM2200_DSP1_CONTROL_19 0xA16 +#define WM2200_DSP1_CONTROL_20 0xA17 +#define WM2200_DSP1_CONTROL_21 0xA18 +#define WM2200_DSP1_CONTROL_22 0xA1A +#define WM2200_DSP1_CONTROL_23 0xA1B +#define WM2200_DSP1_CONTROL_24 0xA1C +#define WM2200_DSP1_CONTROL_25 0xA1E +#define WM2200_DSP1_CONTROL_26 0xA20 +#define WM2200_DSP1_CONTROL_27 0xA21 +#define WM2200_DSP1_CONTROL_28 0xA22 +#define WM2200_DSP1_CONTROL_29 0xA23 +#define WM2200_DSP1_CONTROL_30 0xA24 +#define WM2200_DSP1_CONTROL_31 0xA26 +#define WM2200_DSP2_CONTROL_1 0xB00 +#define WM2200_DSP2_CONTROL_2 0xB02 +#define WM2200_DSP2_CONTROL_3 0xB03 +#define WM2200_DSP2_CONTROL_4 0xB04 +#define WM2200_DSP2_CONTROL_5 0xB06 +#define WM2200_DSP2_CONTROL_6 0xB07 +#define WM2200_DSP2_CONTROL_7 0xB08 +#define WM2200_DSP2_CONTROL_8 0xB09 +#define WM2200_DSP2_CONTROL_9 0xB0A +#define WM2200_DSP2_CONTROL_10 0xB0B +#define WM2200_DSP2_CONTROL_11 0xB0C +#define WM2200_DSP2_CONTROL_12 0xB0D +#define WM2200_DSP2_CONTROL_13 0xB0F +#define WM2200_DSP2_CONTROL_14 0xB10 +#define WM2200_DSP2_CONTROL_15 0xB11 +#define WM2200_DSP2_CONTROL_16 0xB12 +#define WM2200_DSP2_CONTROL_17 0xB13 +#define WM2200_DSP2_CONTROL_18 0xB14 +#define WM2200_DSP2_CONTROL_19 0xB16 +#define WM2200_DSP2_CONTROL_20 0xB17 +#define WM2200_DSP2_CONTROL_21 0xB18 +#define WM2200_DSP2_CONTROL_22 0xB1A +#define WM2200_DSP2_CONTROL_23 0xB1B +#define WM2200_DSP2_CONTROL_24 0xB1C +#define WM2200_DSP2_CONTROL_25 0xB1E +#define WM2200_DSP2_CONTROL_26 0xB20 +#define WM2200_DSP2_CONTROL_27 0xB21 +#define WM2200_DSP2_CONTROL_28 0xB22 +#define WM2200_DSP2_CONTROL_29 0xB23 +#define WM2200_DSP2_CONTROL_30 0xB24 +#define WM2200_DSP2_CONTROL_31 0xB26 +#define WM2200_ANC_CTRL1 0xD00 +#define WM2200_ANC_CTRL2 0xD01 +#define WM2200_ANC_CTRL3 0xD02 +#define WM2200_ANC_CTRL7 0xD08 +#define WM2200_ANC_CTRL8 0xD09 +#define WM2200_ANC_CTRL9 0xD0A +#define WM2200_ANC_CTRL10 0xD0B +#define WM2200_ANC_CTRL11 0xD0C +#define WM2200_ANC_CTRL12 0xD0D +#define WM2200_ANC_CTRL13 0xD0E +#define WM2200_ANC_CTRL14 0xD0F +#define WM2200_ANC_CTRL15 0xD10 +#define WM2200_ANC_CTRL16 0xD11 +#define WM2200_ANC_CTRL17 0xD12 +#define WM2200_ANC_CTRL18 0xD15 +#define WM2200_ANC_CTRL19 0xD16 +#define WM2200_ANC_CTRL20 0xD17 +#define WM2200_ANC_CTRL21 0xD18 +#define WM2200_ANC_CTRL22 0xD19 +#define WM2200_ANC_CTRL23 0xD1A +#define WM2200_ANC_CTRL24 0xD1B +#define WM2200_ANC_CTRL25 0xD1C +#define WM2200_ANC_CTRL26 0xD1D +#define WM2200_ANC_CTRL27 0xD1E +#define WM2200_ANC_CTRL28 0xD1F +#define WM2200_ANC_CTRL29 0xD20 +#define WM2200_ANC_CTRL30 0xD21 +#define WM2200_ANC_CTRL31 0xD23 +#define WM2200_ANC_CTRL32 0xD24 +#define WM2200_ANC_CTRL33 0xD25 +#define WM2200_ANC_CTRL34 0xD27 +#define WM2200_ANC_CTRL35 0xD28 +#define WM2200_ANC_CTRL36 0xD29 +#define WM2200_ANC_CTRL37 0xD2A +#define WM2200_ANC_CTRL38 0xD2B +#define WM2200_ANC_CTRL39 0xD2C +#define WM2200_ANC_CTRL40 0xD2D +#define WM2200_ANC_CTRL41 0xD2E +#define WM2200_ANC_CTRL42 0xD2F +#define WM2200_ANC_CTRL43 0xD30 +#define WM2200_ANC_CTRL44 0xD31 +#define WM2200_ANC_CTRL45 0xD32 +#define WM2200_ANC_CTRL46 0xD33 +#define WM2200_ANC_CTRL47 0xD34 +#define WM2200_ANC_CTRL48 0xD35 +#define WM2200_ANC_CTRL49 0xD36 +#define WM2200_ANC_CTRL50 0xD37 +#define WM2200_ANC_CTRL51 0xD38 +#define WM2200_ANC_CTRL52 0xD39 +#define WM2200_ANC_CTRL53 0xD3A +#define WM2200_ANC_CTRL54 0xD3B +#define WM2200_ANC_CTRL55 0xD3C +#define WM2200_ANC_CTRL56 0xD3D +#define WM2200_ANC_CTRL57 0xD3E +#define WM2200_ANC_CTRL58 0xD3F +#define WM2200_ANC_CTRL59 0xD40 +#define WM2200_ANC_CTRL60 0xD41 +#define WM2200_ANC_CTRL61 0xD42 +#define WM2200_ANC_CTRL62 0xD43 +#define WM2200_ANC_CTRL63 0xD44 +#define WM2200_ANC_CTRL64 0xD45 +#define WM2200_ANC_CTRL65 0xD46 +#define WM2200_ANC_CTRL66 0xD47 +#define WM2200_ANC_CTRL67 0xD48 +#define WM2200_ANC_CTRL68 0xD49 +#define WM2200_ANC_CTRL69 0xD4A +#define WM2200_ANC_CTRL70 0xD4B +#define WM2200_ANC_CTRL71 0xD4C +#define WM2200_ANC_CTRL72 0xD4D +#define WM2200_ANC_CTRL73 0xD4E +#define WM2200_ANC_CTRL74 0xD4F +#define WM2200_ANC_CTRL75 0xD50 +#define WM2200_ANC_CTRL76 0xD51 +#define WM2200_ANC_CTRL77 0xD52 +#define WM2200_ANC_CTRL78 0xD53 +#define WM2200_ANC_CTRL79 0xD54 +#define WM2200_ANC_CTRL80 0xD55 +#define WM2200_ANC_CTRL81 0xD56 +#define WM2200_ANC_CTRL82 0xD57 +#define WM2200_ANC_CTRL83 0xD58 +#define WM2200_ANC_CTRL84 0xD5B +#define WM2200_ANC_CTRL85 0xD5C +#define WM2200_ANC_CTRL86 0xD5F +#define WM2200_ANC_CTRL87 0xD60 +#define WM2200_ANC_CTRL88 0xD61 +#define WM2200_ANC_CTRL89 0xD62 +#define WM2200_ANC_CTRL90 0xD63 +#define WM2200_ANC_CTRL91 0xD64 +#define WM2200_ANC_CTRL92 0xD65 +#define WM2200_ANC_CTRL93 0xD66 +#define WM2200_ANC_CTRL94 0xD67 +#define WM2200_ANC_CTRL95 0xD68 +#define WM2200_ANC_CTRL96 0xD69 +#define WM2200_DSP1_DM_0 0x3000 +#define WM2200_DSP1_DM_1 0x3001 +#define WM2200_DSP1_DM_2 0x3002 +#define WM2200_DSP1_DM_3 0x3003 +#define WM2200_DSP1_DM_2044 0x37FC +#define WM2200_DSP1_DM_2045 0x37FD +#define WM2200_DSP1_DM_2046 0x37FE +#define WM2200_DSP1_DM_2047 0x37FF +#define WM2200_DSP1_PM_0 0x3800 +#define WM2200_DSP1_PM_1 0x3801 +#define WM2200_DSP1_PM_2 0x3802 +#define WM2200_DSP1_PM_3 0x3803 +#define WM2200_DSP1_PM_4 0x3804 +#define WM2200_DSP1_PM_5 0x3805 +#define WM2200_DSP1_PM_762 0x3AFA +#define WM2200_DSP1_PM_763 0x3AFB +#define WM2200_DSP1_PM_764 0x3AFC +#define WM2200_DSP1_PM_765 0x3AFD +#define WM2200_DSP1_PM_766 0x3AFE +#define WM2200_DSP1_PM_767 0x3AFF +#define WM2200_DSP1_ZM_0 0x3C00 +#define WM2200_DSP1_ZM_1 0x3C01 +#define WM2200_DSP1_ZM_2 0x3C02 +#define WM2200_DSP1_ZM_3 0x3C03 +#define WM2200_DSP1_ZM_1020 0x3FFC +#define WM2200_DSP1_ZM_1021 0x3FFD +#define WM2200_DSP1_ZM_1022 0x3FFE +#define WM2200_DSP1_ZM_1023 0x3FFF +#define WM2200_DSP2_DM_0 0x4000 +#define WM2200_DSP2_DM_1 0x4001 +#define WM2200_DSP2_DM_2 0x4002 +#define WM2200_DSP2_DM_3 0x4003 +#define WM2200_DSP2_DM_2044 0x47FC +#define WM2200_DSP2_DM_2045 0x47FD +#define WM2200_DSP2_DM_2046 0x47FE +#define WM2200_DSP2_DM_2047 0x47FF +#define WM2200_DSP2_PM_0 0x4800 +#define WM2200_DSP2_PM_1 0x4801 +#define WM2200_DSP2_PM_2 0x4802 +#define WM2200_DSP2_PM_3 0x4803 +#define WM2200_DSP2_PM_4 0x4804 +#define WM2200_DSP2_PM_5 0x4805 +#define WM2200_DSP2_PM_762 0x4AFA +#define WM2200_DSP2_PM_763 0x4AFB +#define WM2200_DSP2_PM_764 0x4AFC +#define WM2200_DSP2_PM_765 0x4AFD +#define WM2200_DSP2_PM_766 0x4AFE +#define WM2200_DSP2_PM_767 0x4AFF +#define WM2200_DSP2_ZM_0 0x4C00 +#define WM2200_DSP2_ZM_1 0x4C01 +#define WM2200_DSP2_ZM_2 0x4C02 +#define WM2200_DSP2_ZM_3 0x4C03 +#define WM2200_DSP2_ZM_1020 0x4FFC +#define WM2200_DSP2_ZM_1021 0x4FFD +#define WM2200_DSP2_ZM_1022 0x4FFE +#define WM2200_DSP2_ZM_1023 0x4FFF + +#define WM2200_REGISTER_COUNT 494 +#define WM2200_MAX_REGISTER 0x4FFF + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - software reset + */ +#define WM2200_SW_RESET_CHIP_ID1_MASK 0xFFFF /* SW_RESET_CHIP_ID1 - [15:0] */ +#define WM2200_SW_RESET_CHIP_ID1_SHIFT 0 /* SW_RESET_CHIP_ID1 - [15:0] */ +#define WM2200_SW_RESET_CHIP_ID1_WIDTH 16 /* SW_RESET_CHIP_ID1 - [15:0] */ + +/* + * R1 (0x01) - Device Revision + */ +#define WM2200_DEVICE_REVISION_MASK 0x000F /* DEVICE_REVISION - [3:0] */ +#define WM2200_DEVICE_REVISION_SHIFT 0 /* DEVICE_REVISION - [3:0] */ +#define WM2200_DEVICE_REVISION_WIDTH 4 /* DEVICE_REVISION - [3:0] */ + +/* + * R11 (0x0B) - Tone Generator 1 + */ +#define WM2200_TONE_ENA 0x0001 /* TONE_ENA */ +#define WM2200_TONE_ENA_MASK 0x0001 /* TONE_ENA */ +#define WM2200_TONE_ENA_SHIFT 0 /* TONE_ENA */ +#define WM2200_TONE_ENA_WIDTH 1 /* TONE_ENA */ + +/* + * R258 (0x102) - Clocking 3 + */ +#define WM2200_SYSCLK_FREQ_MASK 0x0700 /* SYSCLK_FREQ - [10:8] */ +#define WM2200_SYSCLK_FREQ_SHIFT 8 /* SYSCLK_FREQ - [10:8] */ +#define WM2200_SYSCLK_FREQ_WIDTH 3 /* SYSCLK_FREQ - [10:8] */ +#define WM2200_SYSCLK_ENA 0x0040 /* SYSCLK_ENA */ +#define WM2200_SYSCLK_ENA_MASK 0x0040 /* SYSCLK_ENA */ +#define WM2200_SYSCLK_ENA_SHIFT 6 /* SYSCLK_ENA */ +#define WM2200_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */ +#define WM2200_SYSCLK_SRC_MASK 0x000F /* SYSCLK_SRC - [3:0] */ +#define WM2200_SYSCLK_SRC_SHIFT 0 /* SYSCLK_SRC - [3:0] */ +#define WM2200_SYSCLK_SRC_WIDTH 4 /* SYSCLK_SRC - [3:0] */ + +/* + * R259 (0x103) - Clocking 4 + */ +#define WM2200_SAMPLE_RATE_1_MASK 0x001F /* SAMPLE_RATE_1 - [4:0] */ +#define WM2200_SAMPLE_RATE_1_SHIFT 0 /* SAMPLE_RATE_1 - [4:0] */ +#define WM2200_SAMPLE_RATE_1_WIDTH 5 /* SAMPLE_RATE_1 - [4:0] */ + +/* + * R273 (0x111) - FLL Control 1 + */ +#define WM2200_FLL_ENA 0x0001 /* FLL_ENA */ +#define WM2200_FLL_ENA_MASK 0x0001 /* FLL_ENA */ +#define WM2200_FLL_ENA_SHIFT 0 /* FLL_ENA */ +#define WM2200_FLL_ENA_WIDTH 1 /* FLL_ENA */ + +/* + * R274 (0x112) - FLL Control 2 + */ +#define WM2200_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */ +#define WM2200_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */ +#define WM2200_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */ +#define WM2200_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */ +#define WM2200_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */ +#define WM2200_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */ + +/* + * R275 (0x113) - FLL Control 3 + */ +#define WM2200_FLL_FRACN_ENA 0x0001 /* FLL_FRACN_ENA */ +#define WM2200_FLL_FRACN_ENA_MASK 0x0001 /* FLL_FRACN_ENA */ +#define WM2200_FLL_FRACN_ENA_SHIFT 0 /* FLL_FRACN_ENA */ +#define WM2200_FLL_FRACN_ENA_WIDTH 1 /* FLL_FRACN_ENA */ + +/* + * R276 (0x114) - FLL Control 4 + */ +#define WM2200_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */ +#define WM2200_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */ +#define WM2200_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */ + +/* + * R278 (0x116) - FLL Control 6 + */ +#define WM2200_FLL_N_MASK 0x03FF /* FLL_N - [9:0] */ +#define WM2200_FLL_N_SHIFT 0 /* FLL_N - [9:0] */ +#define WM2200_FLL_N_WIDTH 10 /* FLL_N - [9:0] */ + +/* + * R279 (0x117) - FLL Control 7 + */ +#define WM2200_FLL_CLK_REF_DIV_MASK 0x0030 /* FLL_CLK_REF_DIV - [5:4] */ +#define WM2200_FLL_CLK_REF_DIV_SHIFT 4 /* FLL_CLK_REF_DIV - [5:4] */ +#define WM2200_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [5:4] */ +#define WM2200_FLL_CLK_REF_SRC_MASK 0x0003 /* FLL_CLK_REF_SRC - [1:0] */ +#define WM2200_FLL_CLK_REF_SRC_SHIFT 0 /* FLL_CLK_REF_SRC - [1:0] */ +#define WM2200_FLL_CLK_REF_SRC_WIDTH 2 /* FLL_CLK_REF_SRC - [1:0] */ + +/* + * R281 (0x119) - FLL EFS 1 + */ +#define WM2200_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */ +#define WM2200_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */ +#define WM2200_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */ + +/* + * R282 (0x11A) - FLL EFS 2 + */ +#define WM2200_FLL_EFS_ENA 0x0001 /* FLL_EFS_ENA */ +#define WM2200_FLL_EFS_ENA_MASK 0x0001 /* FLL_EFS_ENA */ +#define WM2200_FLL_EFS_ENA_SHIFT 0 /* FLL_EFS_ENA */ +#define WM2200_FLL_EFS_ENA_WIDTH 1 /* FLL_EFS_ENA */ + +/* + * R512 (0x200) - Mic Charge Pump 1 + */ +#define WM2200_CPMIC_BYPASS_MODE 0x0020 /* CPMIC_BYPASS_MODE */ +#define WM2200_CPMIC_BYPASS_MODE_MASK 0x0020 /* CPMIC_BYPASS_MODE */ +#define WM2200_CPMIC_BYPASS_MODE_SHIFT 5 /* CPMIC_BYPASS_MODE */ +#define WM2200_CPMIC_BYPASS_MODE_WIDTH 1 /* CPMIC_BYPASS_MODE */ +#define WM2200_CPMIC_ENA 0x0001 /* CPMIC_ENA */ +#define WM2200_CPMIC_ENA_MASK 0x0001 /* CPMIC_ENA */ +#define WM2200_CPMIC_ENA_SHIFT 0 /* CPMIC_ENA */ +#define WM2200_CPMIC_ENA_WIDTH 1 /* CPMIC_ENA */ + +/* + * R513 (0x201) - Mic Charge Pump 2 + */ +#define WM2200_CPMIC_LDO_VSEL_OVERRIDE_MASK 0xF800 /* CPMIC_LDO_VSEL_OVERRIDE - [15:11] */ +#define WM2200_CPMIC_LDO_VSEL_OVERRIDE_SHIFT 11 /* CPMIC_LDO_VSEL_OVERRIDE - [15:11] */ +#define WM2200_CPMIC_LDO_VSEL_OVERRIDE_WIDTH 5 /* CPMIC_LDO_VSEL_OVERRIDE - [15:11] */ + +/* + * R514 (0x202) - DM Charge Pump 1 + */ +#define WM2200_CPDM_ENA 0x0001 /* CPDM_ENA */ +#define WM2200_CPDM_ENA_MASK 0x0001 /* CPDM_ENA */ +#define WM2200_CPDM_ENA_SHIFT 0 /* CPDM_ENA */ +#define WM2200_CPDM_ENA_WIDTH 1 /* CPDM_ENA */ + +/* + * R524 (0x20C) - Mic Bias Ctrl 1 + */ +#define WM2200_MICB1_DISCH 0x0040 /* MICB1_DISCH */ +#define WM2200_MICB1_DISCH_MASK 0x0040 /* MICB1_DISCH */ +#define WM2200_MICB1_DISCH_SHIFT 6 /* MICB1_DISCH */ +#define WM2200_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */ +#define WM2200_MICB1_RATE 0x0020 /* MICB1_RATE */ +#define WM2200_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */ +#define WM2200_MICB1_RATE_SHIFT 5 /* MICB1_RATE */ +#define WM2200_MICB1_RATE_WIDTH 1 /* MICB1_RATE */ +#define WM2200_MICB1_LVL_MASK 0x001C /* MICB1_LVL - [4:2] */ +#define WM2200_MICB1_LVL_SHIFT 2 /* MICB1_LVL - [4:2] */ +#define WM2200_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [4:2] */ +#define WM2200_MICB1_MODE 0x0002 /* MICB1_MODE */ +#define WM2200_MICB1_MODE_MASK 0x0002 /* MICB1_MODE */ +#define WM2200_MICB1_MODE_SHIFT 1 /* MICB1_MODE */ +#define WM2200_MICB1_MODE_WIDTH 1 /* MICB1_MODE */ +#define WM2200_MICB1_ENA 0x0001 /* MICB1_ENA */ +#define WM2200_MICB1_ENA_MASK 0x0001 /* MICB1_ENA */ +#define WM2200_MICB1_ENA_SHIFT 0 /* MICB1_ENA */ +#define WM2200_MICB1_ENA_WIDTH 1 /* MICB1_ENA */ + +/* + * R525 (0x20D) - Mic Bias Ctrl 2 + */ +#define WM2200_MICB2_DISCH 0x0040 /* MICB2_DISCH */ +#define WM2200_MICB2_DISCH_MASK 0x0040 /* MICB2_DISCH */ +#define WM2200_MICB2_DISCH_SHIFT 6 /* MICB2_DISCH */ +#define WM2200_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */ +#define WM2200_MICB2_RATE 0x0020 /* MICB2_RATE */ +#define WM2200_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */ +#define WM2200_MICB2_RATE_SHIFT 5 /* MICB2_RATE */ +#define WM2200_MICB2_RATE_WIDTH 1 /* MICB2_RATE */ +#define WM2200_MICB2_LVL_MASK 0x001C /* MICB2_LVL - [4:2] */ +#define WM2200_MICB2_LVL_SHIFT 2 /* MICB2_LVL - [4:2] */ +#define WM2200_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [4:2] */ +#define WM2200_MICB2_MODE 0x0002 /* MICB2_MODE */ +#define WM2200_MICB2_MODE_MASK 0x0002 /* MICB2_MODE */ +#define WM2200_MICB2_MODE_SHIFT 1 /* MICB2_MODE */ +#define WM2200_MICB2_MODE_WIDTH 1 /* MICB2_MODE */ +#define WM2200_MICB2_ENA 0x0001 /* MICB2_ENA */ +#define WM2200_MICB2_ENA_MASK 0x0001 /* MICB2_ENA */ +#define WM2200_MICB2_ENA_SHIFT 0 /* MICB2_ENA */ +#define WM2200_MICB2_ENA_WIDTH 1 /* MICB2_ENA */ + +/* + * R527 (0x20F) - Ear Piece Ctrl 1 + */ +#define WM2200_EPD_LP_ENA 0x4000 /* EPD_LP_ENA */ +#define WM2200_EPD_LP_ENA_MASK 0x4000 /* EPD_LP_ENA */ +#define WM2200_EPD_LP_ENA_SHIFT 14 /* EPD_LP_ENA */ +#define WM2200_EPD_LP_ENA_WIDTH 1 /* EPD_LP_ENA */ +#define WM2200_EPD_OUTP_LP_ENA 0x2000 /* EPD_OUTP_LP_ENA */ +#define WM2200_EPD_OUTP_LP_ENA_MASK 0x2000 /* EPD_OUTP_LP_ENA */ +#define WM2200_EPD_OUTP_LP_ENA_SHIFT 13 /* EPD_OUTP_LP_ENA */ +#define WM2200_EPD_OUTP_LP_ENA_WIDTH 1 /* EPD_OUTP_LP_ENA */ +#define WM2200_EPD_RMV_SHRT_LP 0x1000 /* EPD_RMV_SHRT_LP */ +#define WM2200_EPD_RMV_SHRT_LP_MASK 0x1000 /* EPD_RMV_SHRT_LP */ +#define WM2200_EPD_RMV_SHRT_LP_SHIFT 12 /* EPD_RMV_SHRT_LP */ +#define WM2200_EPD_RMV_SHRT_LP_WIDTH 1 /* EPD_RMV_SHRT_LP */ +#define WM2200_EPD_LN_ENA 0x0800 /* EPD_LN_ENA */ +#define WM2200_EPD_LN_ENA_MASK 0x0800 /* EPD_LN_ENA */ +#define WM2200_EPD_LN_ENA_SHIFT 11 /* EPD_LN_ENA */ +#define WM2200_EPD_LN_ENA_WIDTH 1 /* EPD_LN_ENA */ +#define WM2200_EPD_OUTP_LN_ENA 0x0400 /* EPD_OUTP_LN_ENA */ +#define WM2200_EPD_OUTP_LN_ENA_MASK 0x0400 /* EPD_OUTP_LN_ENA */ +#define WM2200_EPD_OUTP_LN_ENA_SHIFT 10 /* EPD_OUTP_LN_ENA */ +#define WM2200_EPD_OUTP_LN_ENA_WIDTH 1 /* EPD_OUTP_LN_ENA */ +#define WM2200_EPD_RMV_SHRT_LN 0x0200 /* EPD_RMV_SHRT_LN */ +#define WM2200_EPD_RMV_SHRT_LN_MASK 0x0200 /* EPD_RMV_SHRT_LN */ +#define WM2200_EPD_RMV_SHRT_LN_SHIFT 9 /* EPD_RMV_SHRT_LN */ +#define WM2200_EPD_RMV_SHRT_LN_WIDTH 1 /* EPD_RMV_SHRT_LN */ + +/* + * R528 (0x210) - Ear Piece Ctrl 2 + */ +#define WM2200_EPD_RP_ENA 0x4000 /* EPD_RP_ENA */ +#define WM2200_EPD_RP_ENA_MASK 0x4000 /* EPD_RP_ENA */ +#define WM2200_EPD_RP_ENA_SHIFT 14 /* EPD_RP_ENA */ +#define WM2200_EPD_RP_ENA_WIDTH 1 /* EPD_RP_ENA */ +#define WM2200_EPD_OUTP_RP_ENA 0x2000 /* EPD_OUTP_RP_ENA */ +#define WM2200_EPD_OUTP_RP_ENA_MASK 0x2000 /* EPD_OUTP_RP_ENA */ +#define WM2200_EPD_OUTP_RP_ENA_SHIFT 13 /* EPD_OUTP_RP_ENA */ +#define WM2200_EPD_OUTP_RP_ENA_WIDTH 1 /* EPD_OUTP_RP_ENA */ +#define WM2200_EPD_RMV_SHRT_RP 0x1000 /* EPD_RMV_SHRT_RP */ +#define WM2200_EPD_RMV_SHRT_RP_MASK 0x1000 /* EPD_RMV_SHRT_RP */ +#define WM2200_EPD_RMV_SHRT_RP_SHIFT 12 /* EPD_RMV_SHRT_RP */ +#define WM2200_EPD_RMV_SHRT_RP_WIDTH 1 /* EPD_RMV_SHRT_RP */ +#define WM2200_EPD_RN_ENA 0x0800 /* EPD_RN_ENA */ +#define WM2200_EPD_RN_ENA_MASK 0x0800 /* EPD_RN_ENA */ +#define WM2200_EPD_RN_ENA_SHIFT 11 /* EPD_RN_ENA */ +#define WM2200_EPD_RN_ENA_WIDTH 1 /* EPD_RN_ENA */ +#define WM2200_EPD_OUTP_RN_ENA 0x0400 /* EPD_OUTP_RN_ENA */ +#define WM2200_EPD_OUTP_RN_ENA_MASK 0x0400 /* EPD_OUTP_RN_ENA */ +#define WM2200_EPD_OUTP_RN_ENA_SHIFT 10 /* EPD_OUTP_RN_ENA */ +#define WM2200_EPD_OUTP_RN_ENA_WIDTH 1 /* EPD_OUTP_RN_ENA */ +#define WM2200_EPD_RMV_SHRT_RN 0x0200 /* EPD_RMV_SHRT_RN */ +#define WM2200_EPD_RMV_SHRT_RN_MASK 0x0200 /* EPD_RMV_SHRT_RN */ +#define WM2200_EPD_RMV_SHRT_RN_SHIFT 9 /* EPD_RMV_SHRT_RN */ +#define WM2200_EPD_RMV_SHRT_RN_WIDTH 1 /* EPD_RMV_SHRT_RN */ + +/* + * R769 (0x301) - Input Enables + */ +#define WM2200_IN3L_ENA 0x0020 /* IN3L_ENA */ +#define WM2200_IN3L_ENA_MASK 0x0020 /* IN3L_ENA */ +#define WM2200_IN3L_ENA_SHIFT 5 /* IN3L_ENA */ +#define WM2200_IN3L_ENA_WIDTH 1 /* IN3L_ENA */ +#define WM2200_IN3R_ENA 0x0010 /* IN3R_ENA */ +#define WM2200_IN3R_ENA_MASK 0x0010 /* IN3R_ENA */ +#define WM2200_IN3R_ENA_SHIFT 4 /* IN3R_ENA */ +#define WM2200_IN3R_ENA_WIDTH 1 /* IN3R_ENA */ +#define WM2200_IN2L_ENA 0x0008 /* IN2L_ENA */ +#define WM2200_IN2L_ENA_MASK 0x0008 /* IN2L_ENA */ +#define WM2200_IN2L_ENA_SHIFT 3 /* IN2L_ENA */ +#define WM2200_IN2L_ENA_WIDTH 1 /* IN2L_ENA */ +#define WM2200_IN2R_ENA 0x0004 /* IN2R_ENA */ +#define WM2200_IN2R_ENA_MASK 0x0004 /* IN2R_ENA */ +#define WM2200_IN2R_ENA_SHIFT 2 /* IN2R_ENA */ +#define WM2200_IN2R_ENA_WIDTH 1 /* IN2R_ENA */ +#define WM2200_IN1L_ENA 0x0002 /* IN1L_ENA */ +#define WM2200_IN1L_ENA_MASK 0x0002 /* IN1L_ENA */ +#define WM2200_IN1L_ENA_SHIFT 1 /* IN1L_ENA */ +#define WM2200_IN1L_ENA_WIDTH 1 /* IN1L_ENA */ +#define WM2200_IN1R_ENA 0x0001 /* IN1R_ENA */ +#define WM2200_IN1R_ENA_MASK 0x0001 /* IN1R_ENA */ +#define WM2200_IN1R_ENA_SHIFT 0 /* IN1R_ENA */ +#define WM2200_IN1R_ENA_WIDTH 1 /* IN1R_ENA */ + +/* + * R770 (0x302) - IN1L Control + */ +#define WM2200_IN1_OSR 0x2000 /* IN1_OSR */ +#define WM2200_IN1_OSR_MASK 0x2000 /* IN1_OSR */ +#define WM2200_IN1_OSR_SHIFT 13 /* IN1_OSR */ +#define WM2200_IN1_OSR_WIDTH 1 /* IN1_OSR */ +#define WM2200_IN1_DMIC_SUP_MASK 0x1800 /* IN1_DMIC_SUP - [12:11] */ +#define WM2200_IN1_DMIC_SUP_SHIFT 11 /* IN1_DMIC_SUP - [12:11] */ +#define WM2200_IN1_DMIC_SUP_WIDTH 2 /* IN1_DMIC_SUP - [12:11] */ +#define WM2200_IN1_MODE_MASK 0x0600 /* IN1_MODE - [10:9] */ +#define WM2200_IN1_MODE_SHIFT 9 /* IN1_MODE - [10:9] */ +#define WM2200_IN1_MODE_WIDTH 2 /* IN1_MODE - [10:9] */ +#define WM2200_IN1L_PGA_VOL_MASK 0x00FE /* IN1L_PGA_VOL - [7:1] */ +#define WM2200_IN1L_PGA_VOL_SHIFT 1 /* IN1L_PGA_VOL - [7:1] */ +#define WM2200_IN1L_PGA_VOL_WIDTH 7 /* IN1L_PGA_VOL - [7:1] */ + +/* + * R771 (0x303) - IN1R Control + */ +#define WM2200_IN1R_PGA_VOL_MASK 0x00FE /* IN1R_PGA_VOL - [7:1] */ +#define WM2200_IN1R_PGA_VOL_SHIFT 1 /* IN1R_PGA_VOL - [7:1] */ +#define WM2200_IN1R_PGA_VOL_WIDTH 7 /* IN1R_PGA_VOL - [7:1] */ + +/* + * R772 (0x304) - IN2L Control + */ +#define WM2200_IN2_OSR 0x2000 /* IN2_OSR */ +#define WM2200_IN2_OSR_MASK 0x2000 /* IN2_OSR */ +#define WM2200_IN2_OSR_SHIFT 13 /* IN2_OSR */ +#define WM2200_IN2_OSR_WIDTH 1 /* IN2_OSR */ +#define WM2200_IN2_DMIC_SUP_MASK 0x1800 /* IN2_DMIC_SUP - [12:11] */ +#define WM2200_IN2_DMIC_SUP_SHIFT 11 /* IN2_DMIC_SUP - [12:11] */ +#define WM2200_IN2_DMIC_SUP_WIDTH 2 /* IN2_DMIC_SUP - [12:11] */ +#define WM2200_IN2_MODE_MASK 0x0600 /* IN2_MODE - [10:9] */ +#define WM2200_IN2_MODE_SHIFT 9 /* IN2_MODE - [10:9] */ +#define WM2200_IN2_MODE_WIDTH 2 /* IN2_MODE - [10:9] */ +#define WM2200_IN2L_PGA_VOL_MASK 0x00FE /* IN2L_PGA_VOL - [7:1] */ +#define WM2200_IN2L_PGA_VOL_SHIFT 1 /* IN2L_PGA_VOL - [7:1] */ +#define WM2200_IN2L_PGA_VOL_WIDTH 7 /* IN2L_PGA_VOL - [7:1] */ + +/* + * R773 (0x305) - IN2R Control + */ +#define WM2200_IN2R_PGA_VOL_MASK 0x00FE /* IN2R_PGA_VOL - [7:1] */ +#define WM2200_IN2R_PGA_VOL_SHIFT 1 /* IN2R_PGA_VOL - [7:1] */ +#define WM2200_IN2R_PGA_VOL_WIDTH 7 /* IN2R_PGA_VOL - [7:1] */ + +/* + * R774 (0x306) - IN3L Control + */ +#define WM2200_IN3_OSR 0x2000 /* IN3_OSR */ +#define WM2200_IN3_OSR_MASK 0x2000 /* IN3_OSR */ +#define WM2200_IN3_OSR_SHIFT 13 /* IN3_OSR */ +#define WM2200_IN3_OSR_WIDTH 1 /* IN3_OSR */ +#define WM2200_IN3_DMIC_SUP_MASK 0x1800 /* IN3_DMIC_SUP - [12:11] */ +#define WM2200_IN3_DMIC_SUP_SHIFT 11 /* IN3_DMIC_SUP - [12:11] */ +#define WM2200_IN3_DMIC_SUP_WIDTH 2 /* IN3_DMIC_SUP - [12:11] */ +#define WM2200_IN3_MODE_MASK 0x0600 /* IN3_MODE - [10:9] */ +#define WM2200_IN3_MODE_SHIFT 9 /* IN3_MODE - [10:9] */ +#define WM2200_IN3_MODE_WIDTH 2 /* IN3_MODE - [10:9] */ +#define WM2200_IN3L_PGA_VOL_MASK 0x00FE /* IN3L_PGA_VOL - [7:1] */ +#define WM2200_IN3L_PGA_VOL_SHIFT 1 /* IN3L_PGA_VOL - [7:1] */ +#define WM2200_IN3L_PGA_VOL_WIDTH 7 /* IN3L_PGA_VOL - [7:1] */ + +/* + * R775 (0x307) - IN3R Control + */ +#define WM2200_IN3R_PGA_VOL_MASK 0x00FE /* IN3R_PGA_VOL - [7:1] */ +#define WM2200_IN3R_PGA_VOL_SHIFT 1 /* IN3R_PGA_VOL - [7:1] */ +#define WM2200_IN3R_PGA_VOL_WIDTH 7 /* IN3R_PGA_VOL - [7:1] */ + +/* + * R778 (0x30A) - RXANC_SRC + */ +#define WM2200_IN_RXANC_SEL_MASK 0x0007 /* IN_RXANC_SEL - [2:0] */ +#define WM2200_IN_RXANC_SEL_SHIFT 0 /* IN_RXANC_SEL - [2:0] */ +#define WM2200_IN_RXANC_SEL_WIDTH 3 /* IN_RXANC_SEL - [2:0] */ + +/* + * R779 (0x30B) - Input Volume Ramp + */ +#define WM2200_IN_VD_RAMP_MASK 0x0070 /* IN_VD_RAMP - [6:4] */ +#define WM2200_IN_VD_RAMP_SHIFT 4 /* IN_VD_RAMP - [6:4] */ +#define WM2200_IN_VD_RAMP_WIDTH 3 /* IN_VD_RAMP - [6:4] */ +#define WM2200_IN_VI_RAMP_MASK 0x0007 /* IN_VI_RAMP - [2:0] */ +#define WM2200_IN_VI_RAMP_SHIFT 0 /* IN_VI_RAMP - [2:0] */ +#define WM2200_IN_VI_RAMP_WIDTH 3 /* IN_VI_RAMP - [2:0] */ + +/* + * R780 (0x30C) - ADC Digital Volume 1L + */ +#define WM2200_IN_VU 0x0200 /* IN_VU */ +#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM2200_IN_VU_SHIFT 9 /* IN_VU */ +#define WM2200_IN_VU_WIDTH 1 /* IN_VU */ +#define WM2200_IN1L_MUTE 0x0100 /* IN1L_MUTE */ +#define WM2200_IN1L_MUTE_MASK 0x0100 /* IN1L_MUTE */ +#define WM2200_IN1L_MUTE_SHIFT 8 /* IN1L_MUTE */ +#define WM2200_IN1L_MUTE_WIDTH 1 /* IN1L_MUTE */ +#define WM2200_IN1L_DIG_VOL_MASK 0x00FF /* IN1L_DIG_VOL - [7:0] */ +#define WM2200_IN1L_DIG_VOL_SHIFT 0 /* IN1L_DIG_VOL - [7:0] */ +#define WM2200_IN1L_DIG_VOL_WIDTH 8 /* IN1L_DIG_VOL - [7:0] */ + +/* + * R781 (0x30D) - ADC Digital Volume 1R + */ +#define WM2200_IN_VU 0x0200 /* IN_VU */ +#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM2200_IN_VU_SHIFT 9 /* IN_VU */ +#define WM2200_IN_VU_WIDTH 1 /* IN_VU */ +#define WM2200_IN1R_MUTE 0x0100 /* IN1R_MUTE */ +#define WM2200_IN1R_MUTE_MASK 0x0100 /* IN1R_MUTE */ +#define WM2200_IN1R_MUTE_SHIFT 8 /* IN1R_MUTE */ +#define WM2200_IN1R_MUTE_WIDTH 1 /* IN1R_MUTE */ +#define WM2200_IN1R_DIG_VOL_MASK 0x00FF /* IN1R_DIG_VOL - [7:0] */ +#define WM2200_IN1R_DIG_VOL_SHIFT 0 /* IN1R_DIG_VOL - [7:0] */ +#define WM2200_IN1R_DIG_VOL_WIDTH 8 /* IN1R_DIG_VOL - [7:0] */ + +/* + * R782 (0x30E) - ADC Digital Volume 2L + */ +#define WM2200_IN_VU 0x0200 /* IN_VU */ +#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM2200_IN_VU_SHIFT 9 /* IN_VU */ +#define WM2200_IN_VU_WIDTH 1 /* IN_VU */ +#define WM2200_IN2L_MUTE 0x0100 /* IN2L_MUTE */ +#define WM2200_IN2L_MUTE_MASK 0x0100 /* IN2L_MUTE */ +#define WM2200_IN2L_MUTE_SHIFT 8 /* IN2L_MUTE */ +#define WM2200_IN2L_MUTE_WIDTH 1 /* IN2L_MUTE */ +#define WM2200_IN2L_DIG_VOL_MASK 0x00FF /* IN2L_DIG_VOL - [7:0] */ +#define WM2200_IN2L_DIG_VOL_SHIFT 0 /* IN2L_DIG_VOL - [7:0] */ +#define WM2200_IN2L_DIG_VOL_WIDTH 8 /* IN2L_DIG_VOL - [7:0] */ + +/* + * R783 (0x30F) - ADC Digital Volume 2R + */ +#define WM2200_IN_VU 0x0200 /* IN_VU */ +#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM2200_IN_VU_SHIFT 9 /* IN_VU */ +#define WM2200_IN_VU_WIDTH 1 /* IN_VU */ +#define WM2200_IN2R_MUTE 0x0100 /* IN2R_MUTE */ +#define WM2200_IN2R_MUTE_MASK 0x0100 /* IN2R_MUTE */ +#define WM2200_IN2R_MUTE_SHIFT 8 /* IN2R_MUTE */ +#define WM2200_IN2R_MUTE_WIDTH 1 /* IN2R_MUTE */ +#define WM2200_IN2R_DIG_VOL_MASK 0x00FF /* IN2R_DIG_VOL - [7:0] */ +#define WM2200_IN2R_DIG_VOL_SHIFT 0 /* IN2R_DIG_VOL - [7:0] */ +#define WM2200_IN2R_DIG_VOL_WIDTH 8 /* IN2R_DIG_VOL - [7:0] */ + +/* + * R784 (0x310) - ADC Digital Volume 3L + */ +#define WM2200_IN_VU 0x0200 /* IN_VU */ +#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM2200_IN_VU_SHIFT 9 /* IN_VU */ +#define WM2200_IN_VU_WIDTH 1 /* IN_VU */ +#define WM2200_IN3L_MUTE 0x0100 /* IN3L_MUTE */ +#define WM2200_IN3L_MUTE_MASK 0x0100 /* IN3L_MUTE */ +#define WM2200_IN3L_MUTE_SHIFT 8 /* IN3L_MUTE */ +#define WM2200_IN3L_MUTE_WIDTH 1 /* IN3L_MUTE */ +#define WM2200_IN3L_DIG_VOL_MASK 0x00FF /* IN3L_DIG_VOL - [7:0] */ +#define WM2200_IN3L_DIG_VOL_SHIFT 0 /* IN3L_DIG_VOL - [7:0] */ +#define WM2200_IN3L_DIG_VOL_WIDTH 8 /* IN3L_DIG_VOL - [7:0] */ + +/* + * R785 (0x311) - ADC Digital Volume 3R + */ +#define WM2200_IN_VU 0x0200 /* IN_VU */ +#define WM2200_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM2200_IN_VU_SHIFT 9 /* IN_VU */ +#define WM2200_IN_VU_WIDTH 1 /* IN_VU */ +#define WM2200_IN3R_MUTE 0x0100 /* IN3R_MUTE */ +#define WM2200_IN3R_MUTE_MASK 0x0100 /* IN3R_MUTE */ +#define WM2200_IN3R_MUTE_SHIFT 8 /* IN3R_MUTE */ +#define WM2200_IN3R_MUTE_WIDTH 1 /* IN3R_MUTE */ +#define WM2200_IN3R_DIG_VOL_MASK 0x00FF /* IN3R_DIG_VOL - [7:0] */ +#define WM2200_IN3R_DIG_VOL_SHIFT 0 /* IN3R_DIG_VOL - [7:0] */ +#define WM2200_IN3R_DIG_VOL_WIDTH 8 /* IN3R_DIG_VOL - [7:0] */ + +/* + * R1024 (0x400) - Output Enables + */ +#define WM2200_OUT2L_ENA 0x0008 /* OUT2L_ENA */ +#define WM2200_OUT2L_ENA_MASK 0x0008 /* OUT2L_ENA */ +#define WM2200_OUT2L_ENA_SHIFT 3 /* OUT2L_ENA */ +#define WM2200_OUT2L_ENA_WIDTH 1 /* OUT2L_ENA */ +#define WM2200_OUT2R_ENA 0x0004 /* OUT2R_ENA */ +#define WM2200_OUT2R_ENA_MASK 0x0004 /* OUT2R_ENA */ +#define WM2200_OUT2R_ENA_SHIFT 2 /* OUT2R_ENA */ +#define WM2200_OUT2R_ENA_WIDTH 1 /* OUT2R_ENA */ +#define WM2200_OUT1L_ENA 0x0002 /* OUT1L_ENA */ +#define WM2200_OUT1L_ENA_MASK 0x0002 /* OUT1L_ENA */ +#define WM2200_OUT1L_ENA_SHIFT 1 /* OUT1L_ENA */ +#define WM2200_OUT1L_ENA_WIDTH 1 /* OUT1L_ENA */ +#define WM2200_OUT1R_ENA 0x0001 /* OUT1R_ENA */ +#define WM2200_OUT1R_ENA_MASK 0x0001 /* OUT1R_ENA */ +#define WM2200_OUT1R_ENA_SHIFT 0 /* OUT1R_ENA */ +#define WM2200_OUT1R_ENA_WIDTH 1 /* OUT1R_ENA */ + +/* + * R1025 (0x401) - DAC Volume Limit 1L + */ +#define WM2200_OUT1_OSR 0x2000 /* OUT1_OSR */ +#define WM2200_OUT1_OSR_MASK 0x2000 /* OUT1_OSR */ +#define WM2200_OUT1_OSR_SHIFT 13 /* OUT1_OSR */ +#define WM2200_OUT1_OSR_WIDTH 1 /* OUT1_OSR */ +#define WM2200_OUT1L_ANC_SRC 0x0800 /* OUT1L_ANC_SRC */ +#define WM2200_OUT1L_ANC_SRC_MASK 0x0800 /* OUT1L_ANC_SRC */ +#define WM2200_OUT1L_ANC_SRC_SHIFT 11 /* OUT1L_ANC_SRC */ +#define WM2200_OUT1L_ANC_SRC_WIDTH 1 /* OUT1L_ANC_SRC */ +#define WM2200_OUT1L_PGA_VOL_MASK 0x00FE /* OUT1L_PGA_VOL - [7:1] */ +#define WM2200_OUT1L_PGA_VOL_SHIFT 1 /* OUT1L_PGA_VOL - [7:1] */ +#define WM2200_OUT1L_PGA_VOL_WIDTH 7 /* OUT1L_PGA_VOL - [7:1] */ + +/* + * R1026 (0x402) - DAC Volume Limit 1R + */ +#define WM2200_OUT1R_ANC_SRC 0x0800 /* OUT1R_ANC_SRC */ +#define WM2200_OUT1R_ANC_SRC_MASK 0x0800 /* OUT1R_ANC_SRC */ +#define WM2200_OUT1R_ANC_SRC_SHIFT 11 /* OUT1R_ANC_SRC */ +#define WM2200_OUT1R_ANC_SRC_WIDTH 1 /* OUT1R_ANC_SRC */ +#define WM2200_OUT1R_PGA_VOL_MASK 0x00FE /* OUT1R_PGA_VOL - [7:1] */ +#define WM2200_OUT1R_PGA_VOL_SHIFT 1 /* OUT1R_PGA_VOL - [7:1] */ +#define WM2200_OUT1R_PGA_VOL_WIDTH 7 /* OUT1R_PGA_VOL - [7:1] */ + +/* + * R1027 (0x403) - DAC Volume Limit 2L + */ +#define WM2200_OUT2_OSR 0x2000 /* OUT2_OSR */ +#define WM2200_OUT2_OSR_MASK 0x2000 /* OUT2_OSR */ +#define WM2200_OUT2_OSR_SHIFT 13 /* OUT2_OSR */ +#define WM2200_OUT2_OSR_WIDTH 1 /* OUT2_OSR */ +#define WM2200_OUT2L_ANC_SRC 0x0800 /* OUT2L_ANC_SRC */ +#define WM2200_OUT2L_ANC_SRC_MASK 0x0800 /* OUT2L_ANC_SRC */ +#define WM2200_OUT2L_ANC_SRC_SHIFT 11 /* OUT2L_ANC_SRC */ +#define WM2200_OUT2L_ANC_SRC_WIDTH 1 /* OUT2L_ANC_SRC */ + +/* + * R1028 (0x404) - DAC Volume Limit 2R + */ +#define WM2200_OUT2R_ANC_SRC 0x0800 /* OUT2R_ANC_SRC */ +#define WM2200_OUT2R_ANC_SRC_MASK 0x0800 /* OUT2R_ANC_SRC */ +#define WM2200_OUT2R_ANC_SRC_SHIFT 11 /* OUT2R_ANC_SRC */ +#define WM2200_OUT2R_ANC_SRC_WIDTH 1 /* OUT2R_ANC_SRC */ + +/* + * R1033 (0x409) - DAC AEC Control 1 + */ +#define WM2200_AEC_LOOPBACK_ENA 0x0004 /* AEC_LOOPBACK_ENA */ +#define WM2200_AEC_LOOPBACK_ENA_MASK 0x0004 /* AEC_LOOPBACK_ENA */ +#define WM2200_AEC_LOOPBACK_ENA_SHIFT 2 /* AEC_LOOPBACK_ENA */ +#define WM2200_AEC_LOOPBACK_ENA_WIDTH 1 /* AEC_LOOPBACK_ENA */ +#define WM2200_AEC_LOOPBACK_SRC_MASK 0x0003 /* AEC_LOOPBACK_SRC - [1:0] */ +#define WM2200_AEC_LOOPBACK_SRC_SHIFT 0 /* AEC_LOOPBACK_SRC - [1:0] */ +#define WM2200_AEC_LOOPBACK_SRC_WIDTH 2 /* AEC_LOOPBACK_SRC - [1:0] */ + +/* + * R1034 (0x40A) - Output Volume Ramp + */ +#define WM2200_OUT_VD_RAMP_MASK 0x0070 /* OUT_VD_RAMP - [6:4] */ +#define WM2200_OUT_VD_RAMP_SHIFT 4 /* OUT_VD_RAMP - [6:4] */ +#define WM2200_OUT_VD_RAMP_WIDTH 3 /* OUT_VD_RAMP - [6:4] */ +#define WM2200_OUT_VI_RAMP_MASK 0x0007 /* OUT_VI_RAMP - [2:0] */ +#define WM2200_OUT_VI_RAMP_SHIFT 0 /* OUT_VI_RAMP - [2:0] */ +#define WM2200_OUT_VI_RAMP_WIDTH 3 /* OUT_VI_RAMP - [2:0] */ + +/* + * R1035 (0x40B) - DAC Digital Volume 1L + */ +#define WM2200_OUT_VU 0x0200 /* OUT_VU */ +#define WM2200_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM2200_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM2200_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM2200_OUT1L_MUTE 0x0100 /* OUT1L_MUTE */ +#define WM2200_OUT1L_MUTE_MASK 0x0100 /* OUT1L_MUTE */ +#define WM2200_OUT1L_MUTE_SHIFT 8 /* OUT1L_MUTE */ +#define WM2200_OUT1L_MUTE_WIDTH 1 /* OUT1L_MUTE */ +#define WM2200_OUT1L_VOL_MASK 0x00FF /* OUT1L_VOL - [7:0] */ +#define WM2200_OUT1L_VOL_SHIFT 0 /* OUT1L_VOL - [7:0] */ +#define WM2200_OUT1L_VOL_WIDTH 8 /* OUT1L_VOL - [7:0] */ + +/* + * R1036 (0x40C) - DAC Digital Volume 1R + */ +#define WM2200_OUT_VU 0x0200 /* OUT_VU */ +#define WM2200_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM2200_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM2200_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM2200_OUT1R_MUTE 0x0100 /* OUT1R_MUTE */ +#define WM2200_OUT1R_MUTE_MASK 0x0100 /* OUT1R_MUTE */ +#define WM2200_OUT1R_MUTE_SHIFT 8 /* OUT1R_MUTE */ +#define WM2200_OUT1R_MUTE_WIDTH 1 /* OUT1R_MUTE */ +#define WM2200_OUT1R_VOL_MASK 0x00FF /* OUT1R_VOL - [7:0] */ +#define WM2200_OUT1R_VOL_SHIFT 0 /* OUT1R_VOL - [7:0] */ +#define WM2200_OUT1R_VOL_WIDTH 8 /* OUT1R_VOL - [7:0] */ + +/* + * R1037 (0x40D) - DAC Digital Volume 2L + */ +#define WM2200_OUT_VU 0x0200 /* OUT_VU */ +#define WM2200_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM2200_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM2200_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM2200_OUT2L_MUTE 0x0100 /* OUT2L_MUTE */ +#define WM2200_OUT2L_MUTE_MASK 0x0100 /* OUT2L_MUTE */ +#define WM2200_OUT2L_MUTE_SHIFT 8 /* OUT2L_MUTE */ +#define WM2200_OUT2L_MUTE_WIDTH 1 /* OUT2L_MUTE */ +#define WM2200_OUT2L_VOL_MASK 0x00FF /* OUT2L_VOL - [7:0] */ +#define WM2200_OUT2L_VOL_SHIFT 0 /* OUT2L_VOL - [7:0] */ +#define WM2200_OUT2L_VOL_WIDTH 8 /* OUT2L_VOL - [7:0] */ + +/* + * R1038 (0x40E) - DAC Digital Volume 2R + */ +#define WM2200_OUT_VU 0x0200 /* OUT_VU */ +#define WM2200_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM2200_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM2200_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM2200_OUT2R_MUTE 0x0100 /* OUT2R_MUTE */ +#define WM2200_OUT2R_MUTE_MASK 0x0100 /* OUT2R_MUTE */ +#define WM2200_OUT2R_MUTE_SHIFT 8 /* OUT2R_MUTE */ +#define WM2200_OUT2R_MUTE_WIDTH 1 /* OUT2R_MUTE */ +#define WM2200_OUT2R_VOL_MASK 0x00FF /* OUT2R_VOL - [7:0] */ +#define WM2200_OUT2R_VOL_SHIFT 0 /* OUT2R_VOL - [7:0] */ +#define WM2200_OUT2R_VOL_WIDTH 8 /* OUT2R_VOL - [7:0] */ + +/* + * R1047 (0x417) - PDM 1 + */ +#define WM2200_SPK1R_MUTE 0x2000 /* SPK1R_MUTE */ +#define WM2200_SPK1R_MUTE_MASK 0x2000 /* SPK1R_MUTE */ +#define WM2200_SPK1R_MUTE_SHIFT 13 /* SPK1R_MUTE */ +#define WM2200_SPK1R_MUTE_WIDTH 1 /* SPK1R_MUTE */ +#define WM2200_SPK1L_MUTE 0x1000 /* SPK1L_MUTE */ +#define WM2200_SPK1L_MUTE_MASK 0x1000 /* SPK1L_MUTE */ +#define WM2200_SPK1L_MUTE_SHIFT 12 /* SPK1L_MUTE */ +#define WM2200_SPK1L_MUTE_WIDTH 1 /* SPK1L_MUTE */ +#define WM2200_SPK1_MUTE_ENDIAN 0x0100 /* SPK1_MUTE_ENDIAN */ +#define WM2200_SPK1_MUTE_ENDIAN_MASK 0x0100 /* SPK1_MUTE_ENDIAN */ +#define WM2200_SPK1_MUTE_ENDIAN_SHIFT 8 /* SPK1_MUTE_ENDIAN */ +#define WM2200_SPK1_MUTE_ENDIAN_WIDTH 1 /* SPK1_MUTE_ENDIAN */ +#define WM2200_SPK1_MUTE_SEQL_MASK 0x00FF /* SPK1_MUTE_SEQL - [7:0] */ +#define WM2200_SPK1_MUTE_SEQL_SHIFT 0 /* SPK1_MUTE_SEQL - [7:0] */ +#define WM2200_SPK1_MUTE_SEQL_WIDTH 8 /* SPK1_MUTE_SEQL - [7:0] */ + +/* + * R1048 (0x418) - PDM 2 + */ +#define WM2200_SPK1_FMT 0x0001 /* SPK1_FMT */ +#define WM2200_SPK1_FMT_MASK 0x0001 /* SPK1_FMT */ +#define WM2200_SPK1_FMT_SHIFT 0 /* SPK1_FMT */ +#define WM2200_SPK1_FMT_WIDTH 1 /* SPK1_FMT */ + +/* + * R1280 (0x500) - Audio IF 1_1 + */ +#define WM2200_AIF1_BCLK_INV 0x0040 /* AIF1_BCLK_INV */ +#define WM2200_AIF1_BCLK_INV_MASK 0x0040 /* AIF1_BCLK_INV */ +#define WM2200_AIF1_BCLK_INV_SHIFT 6 /* AIF1_BCLK_INV */ +#define WM2200_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */ +#define WM2200_AIF1_BCLK_FRC 0x0020 /* AIF1_BCLK_FRC */ +#define WM2200_AIF1_BCLK_FRC_MASK 0x0020 /* AIF1_BCLK_FRC */ +#define WM2200_AIF1_BCLK_FRC_SHIFT 5 /* AIF1_BCLK_FRC */ +#define WM2200_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */ +#define WM2200_AIF1_BCLK_MSTR 0x0010 /* AIF1_BCLK_MSTR */ +#define WM2200_AIF1_BCLK_MSTR_MASK 0x0010 /* AIF1_BCLK_MSTR */ +#define WM2200_AIF1_BCLK_MSTR_SHIFT 4 /* AIF1_BCLK_MSTR */ +#define WM2200_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */ +#define WM2200_AIF1_BCLK_DIV_MASK 0x000F /* AIF1_BCLK_DIV - [3:0] */ +#define WM2200_AIF1_BCLK_DIV_SHIFT 0 /* AIF1_BCLK_DIV - [3:0] */ +#define WM2200_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [3:0] */ + +/* + * R1281 (0x501) - Audio IF 1_2 + */ +#define WM2200_AIF1TX_DAT_TRI 0x0020 /* AIF1TX_DAT_TRI */ +#define WM2200_AIF1TX_DAT_TRI_MASK 0x0020 /* AIF1TX_DAT_TRI */ +#define WM2200_AIF1TX_DAT_TRI_SHIFT 5 /* AIF1TX_DAT_TRI */ +#define WM2200_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */ +#define WM2200_AIF1TX_LRCLK_SRC 0x0008 /* AIF1TX_LRCLK_SRC */ +#define WM2200_AIF1TX_LRCLK_SRC_MASK 0x0008 /* AIF1TX_LRCLK_SRC */ +#define WM2200_AIF1TX_LRCLK_SRC_SHIFT 3 /* AIF1TX_LRCLK_SRC */ +#define WM2200_AIF1TX_LRCLK_SRC_WIDTH 1 /* AIF1TX_LRCLK_SRC */ +#define WM2200_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */ +#define WM2200_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */ +#define WM2200_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */ +#define WM2200_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */ +#define WM2200_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */ +#define WM2200_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */ +#define WM2200_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */ +#define WM2200_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */ +#define WM2200_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */ +#define WM2200_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */ +#define WM2200_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */ +#define WM2200_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */ + +/* + * R1282 (0x502) - Audio IF 1_3 + */ +#define WM2200_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */ +#define WM2200_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */ +#define WM2200_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */ +#define WM2200_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */ +#define WM2200_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */ +#define WM2200_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */ +#define WM2200_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */ +#define WM2200_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */ +#define WM2200_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */ +#define WM2200_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */ +#define WM2200_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */ +#define WM2200_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */ + +/* + * R1283 (0x503) - Audio IF 1_4 + */ +#define WM2200_AIF1_TRI 0x0040 /* AIF1_TRI */ +#define WM2200_AIF1_TRI_MASK 0x0040 /* AIF1_TRI */ +#define WM2200_AIF1_TRI_SHIFT 6 /* AIF1_TRI */ +#define WM2200_AIF1_TRI_WIDTH 1 /* AIF1_TRI */ + +/* + * R1284 (0x504) - Audio IF 1_5 + */ +#define WM2200_AIF1_FMT_MASK 0x0007 /* AIF1_FMT - [2:0] */ +#define WM2200_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [2:0] */ +#define WM2200_AIF1_FMT_WIDTH 3 /* AIF1_FMT - [2:0] */ + +/* + * R1285 (0x505) - Audio IF 1_6 + */ +#define WM2200_AIF1TX_BCPF_MASK 0x07FF /* AIF1TX_BCPF - [10:0] */ +#define WM2200_AIF1TX_BCPF_SHIFT 0 /* AIF1TX_BCPF - [10:0] */ +#define WM2200_AIF1TX_BCPF_WIDTH 11 /* AIF1TX_BCPF - [10:0] */ + +/* + * R1286 (0x506) - Audio IF 1_7 + */ +#define WM2200_AIF1RX_BCPF_MASK 0x07FF /* AIF1RX_BCPF - [10:0] */ +#define WM2200_AIF1RX_BCPF_SHIFT 0 /* AIF1RX_BCPF - [10:0] */ +#define WM2200_AIF1RX_BCPF_WIDTH 11 /* AIF1RX_BCPF - [10:0] */ + +/* + * R1287 (0x507) - Audio IF 1_8 + */ +#define WM2200_AIF1TX_WL_MASK 0x3F00 /* AIF1TX_WL - [13:8] */ +#define WM2200_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [13:8] */ +#define WM2200_AIF1TX_WL_WIDTH 6 /* AIF1TX_WL - [13:8] */ +#define WM2200_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */ +#define WM2200_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */ +#define WM2200_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */ + +/* + * R1288 (0x508) - Audio IF 1_9 + */ +#define WM2200_AIF1RX_WL_MASK 0x3F00 /* AIF1RX_WL - [13:8] */ +#define WM2200_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [13:8] */ +#define WM2200_AIF1RX_WL_WIDTH 6 /* AIF1RX_WL - [13:8] */ +#define WM2200_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */ +#define WM2200_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */ +#define WM2200_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */ + +/* + * R1289 (0x509) - Audio IF 1_10 + */ +#define WM2200_AIF1TX1_SLOT_MASK 0x003F /* AIF1TX1_SLOT - [5:0] */ +#define WM2200_AIF1TX1_SLOT_SHIFT 0 /* AIF1TX1_SLOT - [5:0] */ +#define WM2200_AIF1TX1_SLOT_WIDTH 6 /* AIF1TX1_SLOT - [5:0] */ + +/* + * R1290 (0x50A) - Audio IF 1_11 + */ +#define WM2200_AIF1TX2_SLOT_MASK 0x003F /* AIF1TX2_SLOT - [5:0] */ +#define WM2200_AIF1TX2_SLOT_SHIFT 0 /* AIF1TX2_SLOT - [5:0] */ +#define WM2200_AIF1TX2_SLOT_WIDTH 6 /* AIF1TX2_SLOT - [5:0] */ + +/* + * R1291 (0x50B) - Audio IF 1_12 + */ +#define WM2200_AIF1TX3_SLOT_MASK 0x003F /* AIF1TX3_SLOT - [5:0] */ +#define WM2200_AIF1TX3_SLOT_SHIFT 0 /* AIF1TX3_SLOT - [5:0] */ +#define WM2200_AIF1TX3_SLOT_WIDTH 6 /* AIF1TX3_SLOT - [5:0] */ + +/* + * R1292 (0x50C) - Audio IF 1_13 + */ +#define WM2200_AIF1TX4_SLOT_MASK 0x003F /* AIF1TX4_SLOT - [5:0] */ +#define WM2200_AIF1TX4_SLOT_SHIFT 0 /* AIF1TX4_SLOT - [5:0] */ +#define WM2200_AIF1TX4_SLOT_WIDTH 6 /* AIF1TX4_SLOT - [5:0] */ + +/* + * R1293 (0x50D) - Audio IF 1_14 + */ +#define WM2200_AIF1TX5_SLOT_MASK 0x003F /* AIF1TX5_SLOT - [5:0] */ +#define WM2200_AIF1TX5_SLOT_SHIFT 0 /* AIF1TX5_SLOT - [5:0] */ +#define WM2200_AIF1TX5_SLOT_WIDTH 6 /* AIF1TX5_SLOT - [5:0] */ + +/* + * R1294 (0x50E) - Audio IF 1_15 + */ +#define WM2200_AIF1TX6_SLOT_MASK 0x003F /* AIF1TX6_SLOT - [5:0] */ +#define WM2200_AIF1TX6_SLOT_SHIFT 0 /* AIF1TX6_SLOT - [5:0] */ +#define WM2200_AIF1TX6_SLOT_WIDTH 6 /* AIF1TX6_SLOT - [5:0] */ + +/* + * R1295 (0x50F) - Audio IF 1_16 + */ +#define WM2200_AIF1RX1_SLOT_MASK 0x003F /* AIF1RX1_SLOT - [5:0] */ +#define WM2200_AIF1RX1_SLOT_SHIFT 0 /* AIF1RX1_SLOT - [5:0] */ +#define WM2200_AIF1RX1_SLOT_WIDTH 6 /* AIF1RX1_SLOT - [5:0] */ + +/* + * R1296 (0x510) - Audio IF 1_17 + */ +#define WM2200_AIF1RX2_SLOT_MASK 0x003F /* AIF1RX2_SLOT - [5:0] */ +#define WM2200_AIF1RX2_SLOT_SHIFT 0 /* AIF1RX2_SLOT - [5:0] */ +#define WM2200_AIF1RX2_SLOT_WIDTH 6 /* AIF1RX2_SLOT - [5:0] */ + +/* + * R1297 (0x511) - Audio IF 1_18 + */ +#define WM2200_AIF1RX3_SLOT_MASK 0x003F /* AIF1RX3_SLOT - [5:0] */ +#define WM2200_AIF1RX3_SLOT_SHIFT 0 /* AIF1RX3_SLOT - [5:0] */ +#define WM2200_AIF1RX3_SLOT_WIDTH 6 /* AIF1RX3_SLOT - [5:0] */ + +/* + * R1298 (0x512) - Audio IF 1_19 + */ +#define WM2200_AIF1RX4_SLOT_MASK 0x003F /* AIF1RX4_SLOT - [5:0] */ +#define WM2200_AIF1RX4_SLOT_SHIFT 0 /* AIF1RX4_SLOT - [5:0] */ +#define WM2200_AIF1RX4_SLOT_WIDTH 6 /* AIF1RX4_SLOT - [5:0] */ + +/* + * R1299 (0x513) - Audio IF 1_20 + */ +#define WM2200_AIF1RX5_SLOT_MASK 0x003F /* AIF1RX5_SLOT - [5:0] */ +#define WM2200_AIF1RX5_SLOT_SHIFT 0 /* AIF1RX5_SLOT - [5:0] */ +#define WM2200_AIF1RX5_SLOT_WIDTH 6 /* AIF1RX5_SLOT - [5:0] */ + +/* + * R1300 (0x514) - Audio IF 1_21 + */ +#define WM2200_AIF1RX6_SLOT_MASK 0x003F /* AIF1RX6_SLOT - [5:0] */ +#define WM2200_AIF1RX6_SLOT_SHIFT 0 /* AIF1RX6_SLOT - [5:0] */ +#define WM2200_AIF1RX6_SLOT_WIDTH 6 /* AIF1RX6_SLOT - [5:0] */ + +/* + * R1301 (0x515) - Audio IF 1_22 + */ +#define WM2200_AIF1RX6_ENA 0x0800 /* AIF1RX6_ENA */ +#define WM2200_AIF1RX6_ENA_MASK 0x0800 /* AIF1RX6_ENA */ +#define WM2200_AIF1RX6_ENA_SHIFT 11 /* AIF1RX6_ENA */ +#define WM2200_AIF1RX6_ENA_WIDTH 1 /* AIF1RX6_ENA */ +#define WM2200_AIF1RX5_ENA 0x0400 /* AIF1RX5_ENA */ +#define WM2200_AIF1RX5_ENA_MASK 0x0400 /* AIF1RX5_ENA */ +#define WM2200_AIF1RX5_ENA_SHIFT 10 /* AIF1RX5_ENA */ +#define WM2200_AIF1RX5_ENA_WIDTH 1 /* AIF1RX5_ENA */ +#define WM2200_AIF1RX4_ENA 0x0200 /* AIF1RX4_ENA */ +#define WM2200_AIF1RX4_ENA_MASK 0x0200 /* AIF1RX4_ENA */ +#define WM2200_AIF1RX4_ENA_SHIFT 9 /* AIF1RX4_ENA */ +#define WM2200_AIF1RX4_ENA_WIDTH 1 /* AIF1RX4_ENA */ +#define WM2200_AIF1RX3_ENA 0x0100 /* AIF1RX3_ENA */ +#define WM2200_AIF1RX3_ENA_MASK 0x0100 /* AIF1RX3_ENA */ +#define WM2200_AIF1RX3_ENA_SHIFT 8 /* AIF1RX3_ENA */ +#define WM2200_AIF1RX3_ENA_WIDTH 1 /* AIF1RX3_ENA */ +#define WM2200_AIF1RX2_ENA 0x0080 /* AIF1RX2_ENA */ +#define WM2200_AIF1RX2_ENA_MASK 0x0080 /* AIF1RX2_ENA */ +#define WM2200_AIF1RX2_ENA_SHIFT 7 /* AIF1RX2_ENA */ +#define WM2200_AIF1RX2_ENA_WIDTH 1 /* AIF1RX2_ENA */ +#define WM2200_AIF1RX1_ENA 0x0040 /* AIF1RX1_ENA */ +#define WM2200_AIF1RX1_ENA_MASK 0x0040 /* AIF1RX1_ENA */ +#define WM2200_AIF1RX1_ENA_SHIFT 6 /* AIF1RX1_ENA */ +#define WM2200_AIF1RX1_ENA_WIDTH 1 /* AIF1RX1_ENA */ +#define WM2200_AIF1TX6_ENA 0x0020 /* AIF1TX6_ENA */ +#define WM2200_AIF1TX6_ENA_MASK 0x0020 /* AIF1TX6_ENA */ +#define WM2200_AIF1TX6_ENA_SHIFT 5 /* AIF1TX6_ENA */ +#define WM2200_AIF1TX6_ENA_WIDTH 1 /* AIF1TX6_ENA */ +#define WM2200_AIF1TX5_ENA 0x0010 /* AIF1TX5_ENA */ +#define WM2200_AIF1TX5_ENA_MASK 0x0010 /* AIF1TX5_ENA */ +#define WM2200_AIF1TX5_ENA_SHIFT 4 /* AIF1TX5_ENA */ +#define WM2200_AIF1TX5_ENA_WIDTH 1 /* AIF1TX5_ENA */ +#define WM2200_AIF1TX4_ENA 0x0008 /* AIF1TX4_ENA */ +#define WM2200_AIF1TX4_ENA_MASK 0x0008 /* AIF1TX4_ENA */ +#define WM2200_AIF1TX4_ENA_SHIFT 3 /* AIF1TX4_ENA */ +#define WM2200_AIF1TX4_ENA_WIDTH 1 /* AIF1TX4_ENA */ +#define WM2200_AIF1TX3_ENA 0x0004 /* AIF1TX3_ENA */ +#define WM2200_AIF1TX3_ENA_MASK 0x0004 /* AIF1TX3_ENA */ +#define WM2200_AIF1TX3_ENA_SHIFT 2 /* AIF1TX3_ENA */ +#define WM2200_AIF1TX3_ENA_WIDTH 1 /* AIF1TX3_ENA */ +#define WM2200_AIF1TX2_ENA 0x0002 /* AIF1TX2_ENA */ +#define WM2200_AIF1TX2_ENA_MASK 0x0002 /* AIF1TX2_ENA */ +#define WM2200_AIF1TX2_ENA_SHIFT 1 /* AIF1TX2_ENA */ +#define WM2200_AIF1TX2_ENA_WIDTH 1 /* AIF1TX2_ENA */ +#define WM2200_AIF1TX1_ENA 0x0001 /* AIF1TX1_ENA */ +#define WM2200_AIF1TX1_ENA_MASK 0x0001 /* AIF1TX1_ENA */ +#define WM2200_AIF1TX1_ENA_SHIFT 0 /* AIF1TX1_ENA */ +#define WM2200_AIF1TX1_ENA_WIDTH 1 /* AIF1TX1_ENA */ + +/* + * R1536 (0x600) - OUT1LMIX Input 1 Source + */ +#define WM2200_OUT1LMIX_SRC1_MASK 0x007F /* OUT1LMIX_SRC1 - [6:0] */ +#define WM2200_OUT1LMIX_SRC1_SHIFT 0 /* OUT1LMIX_SRC1 - [6:0] */ +#define WM2200_OUT1LMIX_SRC1_WIDTH 7 /* OUT1LMIX_SRC1 - [6:0] */ + +/* + * R1537 (0x601) - OUT1LMIX Input 1 Volume + */ +#define WM2200_OUT1LMIX_VOL1_MASK 0x00FE /* OUT1LMIX_VOL1 - [7:1] */ +#define WM2200_OUT1LMIX_VOL1_SHIFT 1 /* OUT1LMIX_VOL1 - [7:1] */ +#define WM2200_OUT1LMIX_VOL1_WIDTH 7 /* OUT1LMIX_VOL1 - [7:1] */ + +/* + * R1538 (0x602) - OUT1LMIX Input 2 Source + */ +#define WM2200_OUT1LMIX_SRC2_MASK 0x007F /* OUT1LMIX_SRC2 - [6:0] */ +#define WM2200_OUT1LMIX_SRC2_SHIFT 0 /* OUT1LMIX_SRC2 - [6:0] */ +#define WM2200_OUT1LMIX_SRC2_WIDTH 7 /* OUT1LMIX_SRC2 - [6:0] */ + +/* + * R1539 (0x603) - OUT1LMIX Input 2 Volume + */ +#define WM2200_OUT1LMIX_VOL2_MASK 0x00FE /* OUT1LMIX_VOL2 - [7:1] */ +#define WM2200_OUT1LMIX_VOL2_SHIFT 1 /* OUT1LMIX_VOL2 - [7:1] */ +#define WM2200_OUT1LMIX_VOL2_WIDTH 7 /* OUT1LMIX_VOL2 - [7:1] */ + +/* + * R1540 (0x604) - OUT1LMIX Input 3 Source + */ +#define WM2200_OUT1LMIX_SRC3_MASK 0x007F /* OUT1LMIX_SRC3 - [6:0] */ +#define WM2200_OUT1LMIX_SRC3_SHIFT 0 /* OUT1LMIX_SRC3 - [6:0] */ +#define WM2200_OUT1LMIX_SRC3_WIDTH 7 /* OUT1LMIX_SRC3 - [6:0] */ + +/* + * R1541 (0x605) - OUT1LMIX Input 3 Volume + */ +#define WM2200_OUT1LMIX_VOL3_MASK 0x00FE /* OUT1LMIX_VOL3 - [7:1] */ +#define WM2200_OUT1LMIX_VOL3_SHIFT 1 /* OUT1LMIX_VOL3 - [7:1] */ +#define WM2200_OUT1LMIX_VOL3_WIDTH 7 /* OUT1LMIX_VOL3 - [7:1] */ + +/* + * R1542 (0x606) - OUT1LMIX Input 4 Source + */ +#define WM2200_OUT1LMIX_SRC4_MASK 0x007F /* OUT1LMIX_SRC4 - [6:0] */ +#define WM2200_OUT1LMIX_SRC4_SHIFT 0 /* OUT1LMIX_SRC4 - [6:0] */ +#define WM2200_OUT1LMIX_SRC4_WIDTH 7 /* OUT1LMIX_SRC4 - [6:0] */ + +/* + * R1543 (0x607) - OUT1LMIX Input 4 Volume + */ +#define WM2200_OUT1LMIX_VOL4_MASK 0x00FE /* OUT1LMIX_VOL4 - [7:1] */ +#define WM2200_OUT1LMIX_VOL4_SHIFT 1 /* OUT1LMIX_VOL4 - [7:1] */ +#define WM2200_OUT1LMIX_VOL4_WIDTH 7 /* OUT1LMIX_VOL4 - [7:1] */ + +/* + * R1544 (0x608) - OUT1RMIX Input 1 Source + */ +#define WM2200_OUT1RMIX_SRC1_MASK 0x007F /* OUT1RMIX_SRC1 - [6:0] */ +#define WM2200_OUT1RMIX_SRC1_SHIFT 0 /* OUT1RMIX_SRC1 - [6:0] */ +#define WM2200_OUT1RMIX_SRC1_WIDTH 7 /* OUT1RMIX_SRC1 - [6:0] */ + +/* + * R1545 (0x609) - OUT1RMIX Input 1 Volume + */ +#define WM2200_OUT1RMIX_VOL1_MASK 0x00FE /* OUT1RMIX_VOL1 - [7:1] */ +#define WM2200_OUT1RMIX_VOL1_SHIFT 1 /* OUT1RMIX_VOL1 - [7:1] */ +#define WM2200_OUT1RMIX_VOL1_WIDTH 7 /* OUT1RMIX_VOL1 - [7:1] */ + +/* + * R1546 (0x60A) - OUT1RMIX Input 2 Source + */ +#define WM2200_OUT1RMIX_SRC2_MASK 0x007F /* OUT1RMIX_SRC2 - [6:0] */ +#define WM2200_OUT1RMIX_SRC2_SHIFT 0 /* OUT1RMIX_SRC2 - [6:0] */ +#define WM2200_OUT1RMIX_SRC2_WIDTH 7 /* OUT1RMIX_SRC2 - [6:0] */ + +/* + * R1547 (0x60B) - OUT1RMIX Input 2 Volume + */ +#define WM2200_OUT1RMIX_VOL2_MASK 0x00FE /* OUT1RMIX_VOL2 - [7:1] */ +#define WM2200_OUT1RMIX_VOL2_SHIFT 1 /* OUT1RMIX_VOL2 - [7:1] */ +#define WM2200_OUT1RMIX_VOL2_WIDTH 7 /* OUT1RMIX_VOL2 - [7:1] */ + +/* + * R1548 (0x60C) - OUT1RMIX Input 3 Source + */ +#define WM2200_OUT1RMIX_SRC3_MASK 0x007F /* OUT1RMIX_SRC3 - [6:0] */ +#define WM2200_OUT1RMIX_SRC3_SHIFT 0 /* OUT1RMIX_SRC3 - [6:0] */ +#define WM2200_OUT1RMIX_SRC3_WIDTH 7 /* OUT1RMIX_SRC3 - [6:0] */ + +/* + * R1549 (0x60D) - OUT1RMIX Input 3 Volume + */ +#define WM2200_OUT1RMIX_VOL3_MASK 0x00FE /* OUT1RMIX_VOL3 - [7:1] */ +#define WM2200_OUT1RMIX_VOL3_SHIFT 1 /* OUT1RMIX_VOL3 - [7:1] */ +#define WM2200_OUT1RMIX_VOL3_WIDTH 7 /* OUT1RMIX_VOL3 - [7:1] */ + +/* + * R1550 (0x60E) - OUT1RMIX Input 4 Source + */ +#define WM2200_OUT1RMIX_SRC4_MASK 0x007F /* OUT1RMIX_SRC4 - [6:0] */ +#define WM2200_OUT1RMIX_SRC4_SHIFT 0 /* OUT1RMIX_SRC4 - [6:0] */ +#define WM2200_OUT1RMIX_SRC4_WIDTH 7 /* OUT1RMIX_SRC4 - [6:0] */ + +/* + * R1551 (0x60F) - OUT1RMIX Input 4 Volume + */ +#define WM2200_OUT1RMIX_VOL4_MASK 0x00FE /* OUT1RMIX_VOL4 - [7:1] */ +#define WM2200_OUT1RMIX_VOL4_SHIFT 1 /* OUT1RMIX_VOL4 - [7:1] */ +#define WM2200_OUT1RMIX_VOL4_WIDTH 7 /* OUT1RMIX_VOL4 - [7:1] */ + +/* + * R1552 (0x610) - OUT2LMIX Input 1 Source + */ +#define WM2200_OUT2LMIX_SRC1_MASK 0x007F /* OUT2LMIX_SRC1 - [6:0] */ +#define WM2200_OUT2LMIX_SRC1_SHIFT 0 /* OUT2LMIX_SRC1 - [6:0] */ +#define WM2200_OUT2LMIX_SRC1_WIDTH 7 /* OUT2LMIX_SRC1 - [6:0] */ + +/* + * R1553 (0x611) - OUT2LMIX Input 1 Volume + */ +#define WM2200_OUT2LMIX_VOL1_MASK 0x00FE /* OUT2LMIX_VOL1 - [7:1] */ +#define WM2200_OUT2LMIX_VOL1_SHIFT 1 /* OUT2LMIX_VOL1 - [7:1] */ +#define WM2200_OUT2LMIX_VOL1_WIDTH 7 /* OUT2LMIX_VOL1 - [7:1] */ + +/* + * R1554 (0x612) - OUT2LMIX Input 2 Source + */ +#define WM2200_OUT2LMIX_SRC2_MASK 0x007F /* OUT2LMIX_SRC2 - [6:0] */ +#define WM2200_OUT2LMIX_SRC2_SHIFT 0 /* OUT2LMIX_SRC2 - [6:0] */ +#define WM2200_OUT2LMIX_SRC2_WIDTH 7 /* OUT2LMIX_SRC2 - [6:0] */ + +/* + * R1555 (0x613) - OUT2LMIX Input 2 Volume + */ +#define WM2200_OUT2LMIX_VOL2_MASK 0x00FE /* OUT2LMIX_VOL2 - [7:1] */ +#define WM2200_OUT2LMIX_VOL2_SHIFT 1 /* OUT2LMIX_VOL2 - [7:1] */ +#define WM2200_OUT2LMIX_VOL2_WIDTH 7 /* OUT2LMIX_VOL2 - [7:1] */ + +/* + * R1556 (0x614) - OUT2LMIX Input 3 Source + */ +#define WM2200_OUT2LMIX_SRC3_MASK 0x007F /* OUT2LMIX_SRC3 - [6:0] */ +#define WM2200_OUT2LMIX_SRC3_SHIFT 0 /* OUT2LMIX_SRC3 - [6:0] */ +#define WM2200_OUT2LMIX_SRC3_WIDTH 7 /* OUT2LMIX_SRC3 - [6:0] */ + +/* + * R1557 (0x615) - OUT2LMIX Input 3 Volume + */ +#define WM2200_OUT2LMIX_VOL3_MASK 0x00FE /* OUT2LMIX_VOL3 - [7:1] */ +#define WM2200_OUT2LMIX_VOL3_SHIFT 1 /* OUT2LMIX_VOL3 - [7:1] */ +#define WM2200_OUT2LMIX_VOL3_WIDTH 7 /* OUT2LMIX_VOL3 - [7:1] */ + +/* + * R1558 (0x616) - OUT2LMIX Input 4 Source + */ +#define WM2200_OUT2LMIX_SRC4_MASK 0x007F /* OUT2LMIX_SRC4 - [6:0] */ +#define WM2200_OUT2LMIX_SRC4_SHIFT 0 /* OUT2LMIX_SRC4 - [6:0] */ +#define WM2200_OUT2LMIX_SRC4_WIDTH 7 /* OUT2LMIX_SRC4 - [6:0] */ + +/* + * R1559 (0x617) - OUT2LMIX Input 4 Volume + */ +#define WM2200_OUT2LMIX_VOL4_MASK 0x00FE /* OUT2LMIX_VOL4 - [7:1] */ +#define WM2200_OUT2LMIX_VOL4_SHIFT 1 /* OUT2LMIX_VOL4 - [7:1] */ +#define WM2200_OUT2LMIX_VOL4_WIDTH 7 /* OUT2LMIX_VOL4 - [7:1] */ + +/* + * R1560 (0x618) - OUT2RMIX Input 1 Source + */ +#define WM2200_OUT2RMIX_SRC1_MASK 0x007F /* OUT2RMIX_SRC1 - [6:0] */ +#define WM2200_OUT2RMIX_SRC1_SHIFT 0 /* OUT2RMIX_SRC1 - [6:0] */ +#define WM2200_OUT2RMIX_SRC1_WIDTH 7 /* OUT2RMIX_SRC1 - [6:0] */ + +/* + * R1561 (0x619) - OUT2RMIX Input 1 Volume + */ +#define WM2200_OUT2RMIX_VOL1_MASK 0x00FE /* OUT2RMIX_VOL1 - [7:1] */ +#define WM2200_OUT2RMIX_VOL1_SHIFT 1 /* OUT2RMIX_VOL1 - [7:1] */ +#define WM2200_OUT2RMIX_VOL1_WIDTH 7 /* OUT2RMIX_VOL1 - [7:1] */ + +/* + * R1562 (0x61A) - OUT2RMIX Input 2 Source + */ +#define WM2200_OUT2RMIX_SRC2_MASK 0x007F /* OUT2RMIX_SRC2 - [6:0] */ +#define WM2200_OUT2RMIX_SRC2_SHIFT 0 /* OUT2RMIX_SRC2 - [6:0] */ +#define WM2200_OUT2RMIX_SRC2_WIDTH 7 /* OUT2RMIX_SRC2 - [6:0] */ + +/* + * R1563 (0x61B) - OUT2RMIX Input 2 Volume + */ +#define WM2200_OUT2RMIX_VOL2_MASK 0x00FE /* OUT2RMIX_VOL2 - [7:1] */ +#define WM2200_OUT2RMIX_VOL2_SHIFT 1 /* OUT2RMIX_VOL2 - [7:1] */ +#define WM2200_OUT2RMIX_VOL2_WIDTH 7 /* OUT2RMIX_VOL2 - [7:1] */ + +/* + * R1564 (0x61C) - OUT2RMIX Input 3 Source + */ +#define WM2200_OUT2RMIX_SRC3_MASK 0x007F /* OUT2RMIX_SRC3 - [6:0] */ +#define WM2200_OUT2RMIX_SRC3_SHIFT 0 /* OUT2RMIX_SRC3 - [6:0] */ +#define WM2200_OUT2RMIX_SRC3_WIDTH 7 /* OUT2RMIX_SRC3 - [6:0] */ + +/* + * R1565 (0x61D) - OUT2RMIX Input 3 Volume + */ +#define WM2200_OUT2RMIX_VOL3_MASK 0x00FE /* OUT2RMIX_VOL3 - [7:1] */ +#define WM2200_OUT2RMIX_VOL3_SHIFT 1 /* OUT2RMIX_VOL3 - [7:1] */ +#define WM2200_OUT2RMIX_VOL3_WIDTH 7 /* OUT2RMIX_VOL3 - [7:1] */ + +/* + * R1566 (0x61E) - OUT2RMIX Input 4 Source + */ +#define WM2200_OUT2RMIX_SRC4_MASK 0x007F /* OUT2RMIX_SRC4 - [6:0] */ +#define WM2200_OUT2RMIX_SRC4_SHIFT 0 /* OUT2RMIX_SRC4 - [6:0] */ +#define WM2200_OUT2RMIX_SRC4_WIDTH 7 /* OUT2RMIX_SRC4 - [6:0] */ + +/* + * R1567 (0x61F) - OUT2RMIX Input 4 Volume + */ +#define WM2200_OUT2RMIX_VOL4_MASK 0x00FE /* OUT2RMIX_VOL4 - [7:1] */ +#define WM2200_OUT2RMIX_VOL4_SHIFT 1 /* OUT2RMIX_VOL4 - [7:1] */ +#define WM2200_OUT2RMIX_VOL4_WIDTH 7 /* OUT2RMIX_VOL4 - [7:1] */ + +/* + * R1568 (0x620) - AIF1TX1MIX Input 1 Source + */ +#define WM2200_AIF1TX1MIX_SRC1_MASK 0x007F /* AIF1TX1MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX1MIX_SRC1_SHIFT 0 /* AIF1TX1MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX1MIX_SRC1_WIDTH 7 /* AIF1TX1MIX_SRC1 - [6:0] */ + +/* + * R1569 (0x621) - AIF1TX1MIX Input 1 Volume + */ +#define WM2200_AIF1TX1MIX_VOL1_MASK 0x00FE /* AIF1TX1MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX1MIX_VOL1_SHIFT 1 /* AIF1TX1MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX1MIX_VOL1_WIDTH 7 /* AIF1TX1MIX_VOL1 - [7:1] */ + +/* + * R1570 (0x622) - AIF1TX1MIX Input 2 Source + */ +#define WM2200_AIF1TX1MIX_SRC2_MASK 0x007F /* AIF1TX1MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX1MIX_SRC2_SHIFT 0 /* AIF1TX1MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX1MIX_SRC2_WIDTH 7 /* AIF1TX1MIX_SRC2 - [6:0] */ + +/* + * R1571 (0x623) - AIF1TX1MIX Input 2 Volume + */ +#define WM2200_AIF1TX1MIX_VOL2_MASK 0x00FE /* AIF1TX1MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX1MIX_VOL2_SHIFT 1 /* AIF1TX1MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX1MIX_VOL2_WIDTH 7 /* AIF1TX1MIX_VOL2 - [7:1] */ + +/* + * R1572 (0x624) - AIF1TX1MIX Input 3 Source + */ +#define WM2200_AIF1TX1MIX_SRC3_MASK 0x007F /* AIF1TX1MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX1MIX_SRC3_SHIFT 0 /* AIF1TX1MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX1MIX_SRC3_WIDTH 7 /* AIF1TX1MIX_SRC3 - [6:0] */ + +/* + * R1573 (0x625) - AIF1TX1MIX Input 3 Volume + */ +#define WM2200_AIF1TX1MIX_VOL3_MASK 0x00FE /* AIF1TX1MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX1MIX_VOL3_SHIFT 1 /* AIF1TX1MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX1MIX_VOL3_WIDTH 7 /* AIF1TX1MIX_VOL3 - [7:1] */ + +/* + * R1574 (0x626) - AIF1TX1MIX Input 4 Source + */ +#define WM2200_AIF1TX1MIX_SRC4_MASK 0x007F /* AIF1TX1MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX1MIX_SRC4_SHIFT 0 /* AIF1TX1MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX1MIX_SRC4_WIDTH 7 /* AIF1TX1MIX_SRC4 - [6:0] */ + +/* + * R1575 (0x627) - AIF1TX1MIX Input 4 Volume + */ +#define WM2200_AIF1TX1MIX_VOL4_MASK 0x00FE /* AIF1TX1MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX1MIX_VOL4_SHIFT 1 /* AIF1TX1MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX1MIX_VOL4_WIDTH 7 /* AIF1TX1MIX_VOL4 - [7:1] */ + +/* + * R1576 (0x628) - AIF1TX2MIX Input 1 Source + */ +#define WM2200_AIF1TX2MIX_SRC1_MASK 0x007F /* AIF1TX2MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX2MIX_SRC1_SHIFT 0 /* AIF1TX2MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX2MIX_SRC1_WIDTH 7 /* AIF1TX2MIX_SRC1 - [6:0] */ + +/* + * R1577 (0x629) - AIF1TX2MIX Input 1 Volume + */ +#define WM2200_AIF1TX2MIX_VOL1_MASK 0x00FE /* AIF1TX2MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX2MIX_VOL1_SHIFT 1 /* AIF1TX2MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX2MIX_VOL1_WIDTH 7 /* AIF1TX2MIX_VOL1 - [7:1] */ + +/* + * R1578 (0x62A) - AIF1TX2MIX Input 2 Source + */ +#define WM2200_AIF1TX2MIX_SRC2_MASK 0x007F /* AIF1TX2MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX2MIX_SRC2_SHIFT 0 /* AIF1TX2MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX2MIX_SRC2_WIDTH 7 /* AIF1TX2MIX_SRC2 - [6:0] */ + +/* + * R1579 (0x62B) - AIF1TX2MIX Input 2 Volume + */ +#define WM2200_AIF1TX2MIX_VOL2_MASK 0x00FE /* AIF1TX2MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX2MIX_VOL2_SHIFT 1 /* AIF1TX2MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX2MIX_VOL2_WIDTH 7 /* AIF1TX2MIX_VOL2 - [7:1] */ + +/* + * R1580 (0x62C) - AIF1TX2MIX Input 3 Source + */ +#define WM2200_AIF1TX2MIX_SRC3_MASK 0x007F /* AIF1TX2MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX2MIX_SRC3_SHIFT 0 /* AIF1TX2MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX2MIX_SRC3_WIDTH 7 /* AIF1TX2MIX_SRC3 - [6:0] */ + +/* + * R1581 (0x62D) - AIF1TX2MIX Input 3 Volume + */ +#define WM2200_AIF1TX2MIX_VOL3_MASK 0x00FE /* AIF1TX2MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX2MIX_VOL3_SHIFT 1 /* AIF1TX2MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX2MIX_VOL3_WIDTH 7 /* AIF1TX2MIX_VOL3 - [7:1] */ + +/* + * R1582 (0x62E) - AIF1TX2MIX Input 4 Source + */ +#define WM2200_AIF1TX2MIX_SRC4_MASK 0x007F /* AIF1TX2MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX2MIX_SRC4_SHIFT 0 /* AIF1TX2MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX2MIX_SRC4_WIDTH 7 /* AIF1TX2MIX_SRC4 - [6:0] */ + +/* + * R1583 (0x62F) - AIF1TX2MIX Input 4 Volume + */ +#define WM2200_AIF1TX2MIX_VOL4_MASK 0x00FE /* AIF1TX2MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX2MIX_VOL4_SHIFT 1 /* AIF1TX2MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX2MIX_VOL4_WIDTH 7 /* AIF1TX2MIX_VOL4 - [7:1] */ + +/* + * R1584 (0x630) - AIF1TX3MIX Input 1 Source + */ +#define WM2200_AIF1TX3MIX_SRC1_MASK 0x007F /* AIF1TX3MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX3MIX_SRC1_SHIFT 0 /* AIF1TX3MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX3MIX_SRC1_WIDTH 7 /* AIF1TX3MIX_SRC1 - [6:0] */ + +/* + * R1585 (0x631) - AIF1TX3MIX Input 1 Volume + */ +#define WM2200_AIF1TX3MIX_VOL1_MASK 0x00FE /* AIF1TX3MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX3MIX_VOL1_SHIFT 1 /* AIF1TX3MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX3MIX_VOL1_WIDTH 7 /* AIF1TX3MIX_VOL1 - [7:1] */ + +/* + * R1586 (0x632) - AIF1TX3MIX Input 2 Source + */ +#define WM2200_AIF1TX3MIX_SRC2_MASK 0x007F /* AIF1TX3MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX3MIX_SRC2_SHIFT 0 /* AIF1TX3MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX3MIX_SRC2_WIDTH 7 /* AIF1TX3MIX_SRC2 - [6:0] */ + +/* + * R1587 (0x633) - AIF1TX3MIX Input 2 Volume + */ +#define WM2200_AIF1TX3MIX_VOL2_MASK 0x00FE /* AIF1TX3MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX3MIX_VOL2_SHIFT 1 /* AIF1TX3MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX3MIX_VOL2_WIDTH 7 /* AIF1TX3MIX_VOL2 - [7:1] */ + +/* + * R1588 (0x634) - AIF1TX3MIX Input 3 Source + */ +#define WM2200_AIF1TX3MIX_SRC3_MASK 0x007F /* AIF1TX3MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX3MIX_SRC3_SHIFT 0 /* AIF1TX3MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX3MIX_SRC3_WIDTH 7 /* AIF1TX3MIX_SRC3 - [6:0] */ + +/* + * R1589 (0x635) - AIF1TX3MIX Input 3 Volume + */ +#define WM2200_AIF1TX3MIX_VOL3_MASK 0x00FE /* AIF1TX3MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX3MIX_VOL3_SHIFT 1 /* AIF1TX3MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX3MIX_VOL3_WIDTH 7 /* AIF1TX3MIX_VOL3 - [7:1] */ + +/* + * R1590 (0x636) - AIF1TX3MIX Input 4 Source + */ +#define WM2200_AIF1TX3MIX_SRC4_MASK 0x007F /* AIF1TX3MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX3MIX_SRC4_SHIFT 0 /* AIF1TX3MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX3MIX_SRC4_WIDTH 7 /* AIF1TX3MIX_SRC4 - [6:0] */ + +/* + * R1591 (0x637) - AIF1TX3MIX Input 4 Volume + */ +#define WM2200_AIF1TX3MIX_VOL4_MASK 0x00FE /* AIF1TX3MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX3MIX_VOL4_SHIFT 1 /* AIF1TX3MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX3MIX_VOL4_WIDTH 7 /* AIF1TX3MIX_VOL4 - [7:1] */ + +/* + * R1592 (0x638) - AIF1TX4MIX Input 1 Source + */ +#define WM2200_AIF1TX4MIX_SRC1_MASK 0x007F /* AIF1TX4MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX4MIX_SRC1_SHIFT 0 /* AIF1TX4MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX4MIX_SRC1_WIDTH 7 /* AIF1TX4MIX_SRC1 - [6:0] */ + +/* + * R1593 (0x639) - AIF1TX4MIX Input 1 Volume + */ +#define WM2200_AIF1TX4MIX_VOL1_MASK 0x00FE /* AIF1TX4MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX4MIX_VOL1_SHIFT 1 /* AIF1TX4MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX4MIX_VOL1_WIDTH 7 /* AIF1TX4MIX_VOL1 - [7:1] */ + +/* + * R1594 (0x63A) - AIF1TX4MIX Input 2 Source + */ +#define WM2200_AIF1TX4MIX_SRC2_MASK 0x007F /* AIF1TX4MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX4MIX_SRC2_SHIFT 0 /* AIF1TX4MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX4MIX_SRC2_WIDTH 7 /* AIF1TX4MIX_SRC2 - [6:0] */ + +/* + * R1595 (0x63B) - AIF1TX4MIX Input 2 Volume + */ +#define WM2200_AIF1TX4MIX_VOL2_MASK 0x00FE /* AIF1TX4MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX4MIX_VOL2_SHIFT 1 /* AIF1TX4MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX4MIX_VOL2_WIDTH 7 /* AIF1TX4MIX_VOL2 - [7:1] */ + +/* + * R1596 (0x63C) - AIF1TX4MIX Input 3 Source + */ +#define WM2200_AIF1TX4MIX_SRC3_MASK 0x007F /* AIF1TX4MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX4MIX_SRC3_SHIFT 0 /* AIF1TX4MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX4MIX_SRC3_WIDTH 7 /* AIF1TX4MIX_SRC3 - [6:0] */ + +/* + * R1597 (0x63D) - AIF1TX4MIX Input 3 Volume + */ +#define WM2200_AIF1TX4MIX_VOL3_MASK 0x00FE /* AIF1TX4MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX4MIX_VOL3_SHIFT 1 /* AIF1TX4MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX4MIX_VOL3_WIDTH 7 /* AIF1TX4MIX_VOL3 - [7:1] */ + +/* + * R1598 (0x63E) - AIF1TX4MIX Input 4 Source + */ +#define WM2200_AIF1TX4MIX_SRC4_MASK 0x007F /* AIF1TX4MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX4MIX_SRC4_SHIFT 0 /* AIF1TX4MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX4MIX_SRC4_WIDTH 7 /* AIF1TX4MIX_SRC4 - [6:0] */ + +/* + * R1599 (0x63F) - AIF1TX4MIX Input 4 Volume + */ +#define WM2200_AIF1TX4MIX_VOL4_MASK 0x00FE /* AIF1TX4MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX4MIX_VOL4_SHIFT 1 /* AIF1TX4MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX4MIX_VOL4_WIDTH 7 /* AIF1TX4MIX_VOL4 - [7:1] */ + +/* + * R1600 (0x640) - AIF1TX5MIX Input 1 Source + */ +#define WM2200_AIF1TX5MIX_SRC1_MASK 0x007F /* AIF1TX5MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX5MIX_SRC1_SHIFT 0 /* AIF1TX5MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX5MIX_SRC1_WIDTH 7 /* AIF1TX5MIX_SRC1 - [6:0] */ + +/* + * R1601 (0x641) - AIF1TX5MIX Input 1 Volume + */ +#define WM2200_AIF1TX5MIX_VOL1_MASK 0x00FE /* AIF1TX5MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX5MIX_VOL1_SHIFT 1 /* AIF1TX5MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX5MIX_VOL1_WIDTH 7 /* AIF1TX5MIX_VOL1 - [7:1] */ + +/* + * R1602 (0x642) - AIF1TX5MIX Input 2 Source + */ +#define WM2200_AIF1TX5MIX_SRC2_MASK 0x007F /* AIF1TX5MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX5MIX_SRC2_SHIFT 0 /* AIF1TX5MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX5MIX_SRC2_WIDTH 7 /* AIF1TX5MIX_SRC2 - [6:0] */ + +/* + * R1603 (0x643) - AIF1TX5MIX Input 2 Volume + */ +#define WM2200_AIF1TX5MIX_VOL2_MASK 0x00FE /* AIF1TX5MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX5MIX_VOL2_SHIFT 1 /* AIF1TX5MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX5MIX_VOL2_WIDTH 7 /* AIF1TX5MIX_VOL2 - [7:1] */ + +/* + * R1604 (0x644) - AIF1TX5MIX Input 3 Source + */ +#define WM2200_AIF1TX5MIX_SRC3_MASK 0x007F /* AIF1TX5MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX5MIX_SRC3_SHIFT 0 /* AIF1TX5MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX5MIX_SRC3_WIDTH 7 /* AIF1TX5MIX_SRC3 - [6:0] */ + +/* + * R1605 (0x645) - AIF1TX5MIX Input 3 Volume + */ +#define WM2200_AIF1TX5MIX_VOL3_MASK 0x00FE /* AIF1TX5MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX5MIX_VOL3_SHIFT 1 /* AIF1TX5MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX5MIX_VOL3_WIDTH 7 /* AIF1TX5MIX_VOL3 - [7:1] */ + +/* + * R1606 (0x646) - AIF1TX5MIX Input 4 Source + */ +#define WM2200_AIF1TX5MIX_SRC4_MASK 0x007F /* AIF1TX5MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX5MIX_SRC4_SHIFT 0 /* AIF1TX5MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX5MIX_SRC4_WIDTH 7 /* AIF1TX5MIX_SRC4 - [6:0] */ + +/* + * R1607 (0x647) - AIF1TX5MIX Input 4 Volume + */ +#define WM2200_AIF1TX5MIX_VOL4_MASK 0x00FE /* AIF1TX5MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX5MIX_VOL4_SHIFT 1 /* AIF1TX5MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX5MIX_VOL4_WIDTH 7 /* AIF1TX5MIX_VOL4 - [7:1] */ + +/* + * R1608 (0x648) - AIF1TX6MIX Input 1 Source + */ +#define WM2200_AIF1TX6MIX_SRC1_MASK 0x007F /* AIF1TX6MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX6MIX_SRC1_SHIFT 0 /* AIF1TX6MIX_SRC1 - [6:0] */ +#define WM2200_AIF1TX6MIX_SRC1_WIDTH 7 /* AIF1TX6MIX_SRC1 - [6:0] */ + +/* + * R1609 (0x649) - AIF1TX6MIX Input 1 Volume + */ +#define WM2200_AIF1TX6MIX_VOL1_MASK 0x00FE /* AIF1TX6MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX6MIX_VOL1_SHIFT 1 /* AIF1TX6MIX_VOL1 - [7:1] */ +#define WM2200_AIF1TX6MIX_VOL1_WIDTH 7 /* AIF1TX6MIX_VOL1 - [7:1] */ + +/* + * R1610 (0x64A) - AIF1TX6MIX Input 2 Source + */ +#define WM2200_AIF1TX6MIX_SRC2_MASK 0x007F /* AIF1TX6MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX6MIX_SRC2_SHIFT 0 /* AIF1TX6MIX_SRC2 - [6:0] */ +#define WM2200_AIF1TX6MIX_SRC2_WIDTH 7 /* AIF1TX6MIX_SRC2 - [6:0] */ + +/* + * R1611 (0x64B) - AIF1TX6MIX Input 2 Volume + */ +#define WM2200_AIF1TX6MIX_VOL2_MASK 0x00FE /* AIF1TX6MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX6MIX_VOL2_SHIFT 1 /* AIF1TX6MIX_VOL2 - [7:1] */ +#define WM2200_AIF1TX6MIX_VOL2_WIDTH 7 /* AIF1TX6MIX_VOL2 - [7:1] */ + +/* + * R1612 (0x64C) - AIF1TX6MIX Input 3 Source + */ +#define WM2200_AIF1TX6MIX_SRC3_MASK 0x007F /* AIF1TX6MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX6MIX_SRC3_SHIFT 0 /* AIF1TX6MIX_SRC3 - [6:0] */ +#define WM2200_AIF1TX6MIX_SRC3_WIDTH 7 /* AIF1TX6MIX_SRC3 - [6:0] */ + +/* + * R1613 (0x64D) - AIF1TX6MIX Input 3 Volume + */ +#define WM2200_AIF1TX6MIX_VOL3_MASK 0x00FE /* AIF1TX6MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX6MIX_VOL3_SHIFT 1 /* AIF1TX6MIX_VOL3 - [7:1] */ +#define WM2200_AIF1TX6MIX_VOL3_WIDTH 7 /* AIF1TX6MIX_VOL3 - [7:1] */ + +/* + * R1614 (0x64E) - AIF1TX6MIX Input 4 Source + */ +#define WM2200_AIF1TX6MIX_SRC4_MASK 0x007F /* AIF1TX6MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX6MIX_SRC4_SHIFT 0 /* AIF1TX6MIX_SRC4 - [6:0] */ +#define WM2200_AIF1TX6MIX_SRC4_WIDTH 7 /* AIF1TX6MIX_SRC4 - [6:0] */ + +/* + * R1615 (0x64F) - AIF1TX6MIX Input 4 Volume + */ +#define WM2200_AIF1TX6MIX_VOL4_MASK 0x00FE /* AIF1TX6MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX6MIX_VOL4_SHIFT 1 /* AIF1TX6MIX_VOL4 - [7:1] */ +#define WM2200_AIF1TX6MIX_VOL4_WIDTH 7 /* AIF1TX6MIX_VOL4 - [7:1] */ + +/* + * R1616 (0x650) - EQLMIX Input 1 Source + */ +#define WM2200_EQLMIX_SRC1_MASK 0x007F /* EQLMIX_SRC1 - [6:0] */ +#define WM2200_EQLMIX_SRC1_SHIFT 0 /* EQLMIX_SRC1 - [6:0] */ +#define WM2200_EQLMIX_SRC1_WIDTH 7 /* EQLMIX_SRC1 - [6:0] */ + +/* + * R1617 (0x651) - EQLMIX Input 1 Volume + */ +#define WM2200_EQLMIX_VOL1_MASK 0x00FE /* EQLMIX_VOL1 - [7:1] */ +#define WM2200_EQLMIX_VOL1_SHIFT 1 /* EQLMIX_VOL1 - [7:1] */ +#define WM2200_EQLMIX_VOL1_WIDTH 7 /* EQLMIX_VOL1 - [7:1] */ + +/* + * R1618 (0x652) - EQLMIX Input 2 Source + */ +#define WM2200_EQLMIX_SRC2_MASK 0x007F /* EQLMIX_SRC2 - [6:0] */ +#define WM2200_EQLMIX_SRC2_SHIFT 0 /* EQLMIX_SRC2 - [6:0] */ +#define WM2200_EQLMIX_SRC2_WIDTH 7 /* EQLMIX_SRC2 - [6:0] */ + +/* + * R1619 (0x653) - EQLMIX Input 2 Volume + */ +#define WM2200_EQLMIX_VOL2_MASK 0x00FE /* EQLMIX_VOL2 - [7:1] */ +#define WM2200_EQLMIX_VOL2_SHIFT 1 /* EQLMIX_VOL2 - [7:1] */ +#define WM2200_EQLMIX_VOL2_WIDTH 7 /* EQLMIX_VOL2 - [7:1] */ + +/* + * R1620 (0x654) - EQLMIX Input 3 Source + */ +#define WM2200_EQLMIX_SRC3_MASK 0x007F /* EQLMIX_SRC3 - [6:0] */ +#define WM2200_EQLMIX_SRC3_SHIFT 0 /* EQLMIX_SRC3 - [6:0] */ +#define WM2200_EQLMIX_SRC3_WIDTH 7 /* EQLMIX_SRC3 - [6:0] */ + +/* + * R1621 (0x655) - EQLMIX Input 3 Volume + */ +#define WM2200_EQLMIX_VOL3_MASK 0x00FE /* EQLMIX_VOL3 - [7:1] */ +#define WM2200_EQLMIX_VOL3_SHIFT 1 /* EQLMIX_VOL3 - [7:1] */ +#define WM2200_EQLMIX_VOL3_WIDTH 7 /* EQLMIX_VOL3 - [7:1] */ + +/* + * R1622 (0x656) - EQLMIX Input 4 Source + */ +#define WM2200_EQLMIX_SRC4_MASK 0x007F /* EQLMIX_SRC4 - [6:0] */ +#define WM2200_EQLMIX_SRC4_SHIFT 0 /* EQLMIX_SRC4 - [6:0] */ +#define WM2200_EQLMIX_SRC4_WIDTH 7 /* EQLMIX_SRC4 - [6:0] */ + +/* + * R1623 (0x657) - EQLMIX Input 4 Volume + */ +#define WM2200_EQLMIX_VOL4_MASK 0x00FE /* EQLMIX_VOL4 - [7:1] */ +#define WM2200_EQLMIX_VOL4_SHIFT 1 /* EQLMIX_VOL4 - [7:1] */ +#define WM2200_EQLMIX_VOL4_WIDTH 7 /* EQLMIX_VOL4 - [7:1] */ + +/* + * R1624 (0x658) - EQRMIX Input 1 Source + */ +#define WM2200_EQRMIX_SRC1_MASK 0x007F /* EQRMIX_SRC1 - [6:0] */ +#define WM2200_EQRMIX_SRC1_SHIFT 0 /* EQRMIX_SRC1 - [6:0] */ +#define WM2200_EQRMIX_SRC1_WIDTH 7 /* EQRMIX_SRC1 - [6:0] */ + +/* + * R1625 (0x659) - EQRMIX Input 1 Volume + */ +#define WM2200_EQRMIX_VOL1_MASK 0x00FE /* EQRMIX_VOL1 - [7:1] */ +#define WM2200_EQRMIX_VOL1_SHIFT 1 /* EQRMIX_VOL1 - [7:1] */ +#define WM2200_EQRMIX_VOL1_WIDTH 7 /* EQRMIX_VOL1 - [7:1] */ + +/* + * R1626 (0x65A) - EQRMIX Input 2 Source + */ +#define WM2200_EQRMIX_SRC2_MASK 0x007F /* EQRMIX_SRC2 - [6:0] */ +#define WM2200_EQRMIX_SRC2_SHIFT 0 /* EQRMIX_SRC2 - [6:0] */ +#define WM2200_EQRMIX_SRC2_WIDTH 7 /* EQRMIX_SRC2 - [6:0] */ + +/* + * R1627 (0x65B) - EQRMIX Input 2 Volume + */ +#define WM2200_EQRMIX_VOL2_MASK 0x00FE /* EQRMIX_VOL2 - [7:1] */ +#define WM2200_EQRMIX_VOL2_SHIFT 1 /* EQRMIX_VOL2 - [7:1] */ +#define WM2200_EQRMIX_VOL2_WIDTH 7 /* EQRMIX_VOL2 - [7:1] */ + +/* + * R1628 (0x65C) - EQRMIX Input 3 Source + */ +#define WM2200_EQRMIX_SRC3_MASK 0x007F /* EQRMIX_SRC3 - [6:0] */ +#define WM2200_EQRMIX_SRC3_SHIFT 0 /* EQRMIX_SRC3 - [6:0] */ +#define WM2200_EQRMIX_SRC3_WIDTH 7 /* EQRMIX_SRC3 - [6:0] */ + +/* + * R1629 (0x65D) - EQRMIX Input 3 Volume + */ +#define WM2200_EQRMIX_VOL3_MASK 0x00FE /* EQRMIX_VOL3 - [7:1] */ +#define WM2200_EQRMIX_VOL3_SHIFT 1 /* EQRMIX_VOL3 - [7:1] */ +#define WM2200_EQRMIX_VOL3_WIDTH 7 /* EQRMIX_VOL3 - [7:1] */ + +/* + * R1630 (0x65E) - EQRMIX Input 4 Source + */ +#define WM2200_EQRMIX_SRC4_MASK 0x007F /* EQRMIX_SRC4 - [6:0] */ +#define WM2200_EQRMIX_SRC4_SHIFT 0 /* EQRMIX_SRC4 - [6:0] */ +#define WM2200_EQRMIX_SRC4_WIDTH 7 /* EQRMIX_SRC4 - [6:0] */ + +/* + * R1631 (0x65F) - EQRMIX Input 4 Volume + */ +#define WM2200_EQRMIX_VOL4_MASK 0x00FE /* EQRMIX_VOL4 - [7:1] */ +#define WM2200_EQRMIX_VOL4_SHIFT 1 /* EQRMIX_VOL4 - [7:1] */ +#define WM2200_EQRMIX_VOL4_WIDTH 7 /* EQRMIX_VOL4 - [7:1] */ + +/* + * R1632 (0x660) - LHPF1MIX Input 1 Source + */ +#define WM2200_LHPF1MIX_SRC1_MASK 0x007F /* LHPF1MIX_SRC1 - [6:0] */ +#define WM2200_LHPF1MIX_SRC1_SHIFT 0 /* LHPF1MIX_SRC1 - [6:0] */ +#define WM2200_LHPF1MIX_SRC1_WIDTH 7 /* LHPF1MIX_SRC1 - [6:0] */ + +/* + * R1633 (0x661) - LHPF1MIX Input 1 Volume + */ +#define WM2200_LHPF1MIX_VOL1_MASK 0x00FE /* LHPF1MIX_VOL1 - [7:1] */ +#define WM2200_LHPF1MIX_VOL1_SHIFT 1 /* LHPF1MIX_VOL1 - [7:1] */ +#define WM2200_LHPF1MIX_VOL1_WIDTH 7 /* LHPF1MIX_VOL1 - [7:1] */ + +/* + * R1634 (0x662) - LHPF1MIX Input 2 Source + */ +#define WM2200_LHPF1MIX_SRC2_MASK 0x007F /* LHPF1MIX_SRC2 - [6:0] */ +#define WM2200_LHPF1MIX_SRC2_SHIFT 0 /* LHPF1MIX_SRC2 - [6:0] */ +#define WM2200_LHPF1MIX_SRC2_WIDTH 7 /* LHPF1MIX_SRC2 - [6:0] */ + +/* + * R1635 (0x663) - LHPF1MIX Input 2 Volume + */ +#define WM2200_LHPF1MIX_VOL2_MASK 0x00FE /* LHPF1MIX_VOL2 - [7:1] */ +#define WM2200_LHPF1MIX_VOL2_SHIFT 1 /* LHPF1MIX_VOL2 - [7:1] */ +#define WM2200_LHPF1MIX_VOL2_WIDTH 7 /* LHPF1MIX_VOL2 - [7:1] */ + +/* + * R1636 (0x664) - LHPF1MIX Input 3 Source + */ +#define WM2200_LHPF1MIX_SRC3_MASK 0x007F /* LHPF1MIX_SRC3 - [6:0] */ +#define WM2200_LHPF1MIX_SRC3_SHIFT 0 /* LHPF1MIX_SRC3 - [6:0] */ +#define WM2200_LHPF1MIX_SRC3_WIDTH 7 /* LHPF1MIX_SRC3 - [6:0] */ + +/* + * R1637 (0x665) - LHPF1MIX Input 3 Volume + */ +#define WM2200_LHPF1MIX_VOL3_MASK 0x00FE /* LHPF1MIX_VOL3 - [7:1] */ +#define WM2200_LHPF1MIX_VOL3_SHIFT 1 /* LHPF1MIX_VOL3 - [7:1] */ +#define WM2200_LHPF1MIX_VOL3_WIDTH 7 /* LHPF1MIX_VOL3 - [7:1] */ + +/* + * R1638 (0x666) - LHPF1MIX Input 4 Source + */ +#define WM2200_LHPF1MIX_SRC4_MASK 0x007F /* LHPF1MIX_SRC4 - [6:0] */ +#define WM2200_LHPF1MIX_SRC4_SHIFT 0 /* LHPF1MIX_SRC4 - [6:0] */ +#define WM2200_LHPF1MIX_SRC4_WIDTH 7 /* LHPF1MIX_SRC4 - [6:0] */ + +/* + * R1639 (0x667) - LHPF1MIX Input 4 Volume + */ +#define WM2200_LHPF1MIX_VOL4_MASK 0x00FE /* LHPF1MIX_VOL4 - [7:1] */ +#define WM2200_LHPF1MIX_VOL4_SHIFT 1 /* LHPF1MIX_VOL4 - [7:1] */ +#define WM2200_LHPF1MIX_VOL4_WIDTH 7 /* LHPF1MIX_VOL4 - [7:1] */ + +/* + * R1640 (0x668) - LHPF2MIX Input 1 Source + */ +#define WM2200_LHPF2MIX_SRC1_MASK 0x007F /* LHPF2MIX_SRC1 - [6:0] */ +#define WM2200_LHPF2MIX_SRC1_SHIFT 0 /* LHPF2MIX_SRC1 - [6:0] */ +#define WM2200_LHPF2MIX_SRC1_WIDTH 7 /* LHPF2MIX_SRC1 - [6:0] */ + +/* + * R1641 (0x669) - LHPF2MIX Input 1 Volume + */ +#define WM2200_LHPF2MIX_VOL1_MASK 0x00FE /* LHPF2MIX_VOL1 - [7:1] */ +#define WM2200_LHPF2MIX_VOL1_SHIFT 1 /* LHPF2MIX_VOL1 - [7:1] */ +#define WM2200_LHPF2MIX_VOL1_WIDTH 7 /* LHPF2MIX_VOL1 - [7:1] */ + +/* + * R1642 (0x66A) - LHPF2MIX Input 2 Source + */ +#define WM2200_LHPF2MIX_SRC2_MASK 0x007F /* LHPF2MIX_SRC2 - [6:0] */ +#define WM2200_LHPF2MIX_SRC2_SHIFT 0 /* LHPF2MIX_SRC2 - [6:0] */ +#define WM2200_LHPF2MIX_SRC2_WIDTH 7 /* LHPF2MIX_SRC2 - [6:0] */ + +/* + * R1643 (0x66B) - LHPF2MIX Input 2 Volume + */ +#define WM2200_LHPF2MIX_VOL2_MASK 0x00FE /* LHPF2MIX_VOL2 - [7:1] */ +#define WM2200_LHPF2MIX_VOL2_SHIFT 1 /* LHPF2MIX_VOL2 - [7:1] */ +#define WM2200_LHPF2MIX_VOL2_WIDTH 7 /* LHPF2MIX_VOL2 - [7:1] */ + +/* + * R1644 (0x66C) - LHPF2MIX Input 3 Source + */ +#define WM2200_LHPF2MIX_SRC3_MASK 0x007F /* LHPF2MIX_SRC3 - [6:0] */ +#define WM2200_LHPF2MIX_SRC3_SHIFT 0 /* LHPF2MIX_SRC3 - [6:0] */ +#define WM2200_LHPF2MIX_SRC3_WIDTH 7 /* LHPF2MIX_SRC3 - [6:0] */ + +/* + * R1645 (0x66D) - LHPF2MIX Input 3 Volume + */ +#define WM2200_LHPF2MIX_VOL3_MASK 0x00FE /* LHPF2MIX_VOL3 - [7:1] */ +#define WM2200_LHPF2MIX_VOL3_SHIFT 1 /* LHPF2MIX_VOL3 - [7:1] */ +#define WM2200_LHPF2MIX_VOL3_WIDTH 7 /* LHPF2MIX_VOL3 - [7:1] */ + +/* + * R1646 (0x66E) - LHPF2MIX Input 4 Source + */ +#define WM2200_LHPF2MIX_SRC4_MASK 0x007F /* LHPF2MIX_SRC4 - [6:0] */ +#define WM2200_LHPF2MIX_SRC4_SHIFT 0 /* LHPF2MIX_SRC4 - [6:0] */ +#define WM2200_LHPF2MIX_SRC4_WIDTH 7 /* LHPF2MIX_SRC4 - [6:0] */ + +/* + * R1647 (0x66F) - LHPF2MIX Input 4 Volume + */ +#define WM2200_LHPF2MIX_VOL4_MASK 0x00FE /* LHPF2MIX_VOL4 - [7:1] */ +#define WM2200_LHPF2MIX_VOL4_SHIFT 1 /* LHPF2MIX_VOL4 - [7:1] */ +#define WM2200_LHPF2MIX_VOL4_WIDTH 7 /* LHPF2MIX_VOL4 - [7:1] */ + +/* + * R1648 (0x670) - DSP1LMIX Input 1 Source + */ +#define WM2200_DSP1LMIX_SRC1_MASK 0x007F /* DSP1LMIX_SRC1 - [6:0] */ +#define WM2200_DSP1LMIX_SRC1_SHIFT 0 /* DSP1LMIX_SRC1 - [6:0] */ +#define WM2200_DSP1LMIX_SRC1_WIDTH 7 /* DSP1LMIX_SRC1 - [6:0] */ + +/* + * R1649 (0x671) - DSP1LMIX Input 1 Volume + */ +#define WM2200_DSP1LMIX_VOL1_MASK 0x00FE /* DSP1LMIX_VOL1 - [7:1] */ +#define WM2200_DSP1LMIX_VOL1_SHIFT 1 /* DSP1LMIX_VOL1 - [7:1] */ +#define WM2200_DSP1LMIX_VOL1_WIDTH 7 /* DSP1LMIX_VOL1 - [7:1] */ + +/* + * R1650 (0x672) - DSP1LMIX Input 2 Source + */ +#define WM2200_DSP1LMIX_SRC2_MASK 0x007F /* DSP1LMIX_SRC2 - [6:0] */ +#define WM2200_DSP1LMIX_SRC2_SHIFT 0 /* DSP1LMIX_SRC2 - [6:0] */ +#define WM2200_DSP1LMIX_SRC2_WIDTH 7 /* DSP1LMIX_SRC2 - [6:0] */ + +/* + * R1651 (0x673) - DSP1LMIX Input 2 Volume + */ +#define WM2200_DSP1LMIX_VOL2_MASK 0x00FE /* DSP1LMIX_VOL2 - [7:1] */ +#define WM2200_DSP1LMIX_VOL2_SHIFT 1 /* DSP1LMIX_VOL2 - [7:1] */ +#define WM2200_DSP1LMIX_VOL2_WIDTH 7 /* DSP1LMIX_VOL2 - [7:1] */ + +/* + * R1652 (0x674) - DSP1LMIX Input 3 Source + */ +#define WM2200_DSP1LMIX_SRC3_MASK 0x007F /* DSP1LMIX_SRC3 - [6:0] */ +#define WM2200_DSP1LMIX_SRC3_SHIFT 0 /* DSP1LMIX_SRC3 - [6:0] */ +#define WM2200_DSP1LMIX_SRC3_WIDTH 7 /* DSP1LMIX_SRC3 - [6:0] */ + +/* + * R1653 (0x675) - DSP1LMIX Input 3 Volume + */ +#define WM2200_DSP1LMIX_VOL3_MASK 0x00FE /* DSP1LMIX_VOL3 - [7:1] */ +#define WM2200_DSP1LMIX_VOL3_SHIFT 1 /* DSP1LMIX_VOL3 - [7:1] */ +#define WM2200_DSP1LMIX_VOL3_WIDTH 7 /* DSP1LMIX_VOL3 - [7:1] */ + +/* + * R1654 (0x676) - DSP1LMIX Input 4 Source + */ +#define WM2200_DSP1LMIX_SRC4_MASK 0x007F /* DSP1LMIX_SRC4 - [6:0] */ +#define WM2200_DSP1LMIX_SRC4_SHIFT 0 /* DSP1LMIX_SRC4 - [6:0] */ +#define WM2200_DSP1LMIX_SRC4_WIDTH 7 /* DSP1LMIX_SRC4 - [6:0] */ + +/* + * R1655 (0x677) - DSP1LMIX Input 4 Volume + */ +#define WM2200_DSP1LMIX_VOL4_MASK 0x00FE /* DSP1LMIX_VOL4 - [7:1] */ +#define WM2200_DSP1LMIX_VOL4_SHIFT 1 /* DSP1LMIX_VOL4 - [7:1] */ +#define WM2200_DSP1LMIX_VOL4_WIDTH 7 /* DSP1LMIX_VOL4 - [7:1] */ + +/* + * R1656 (0x678) - DSP1RMIX Input 1 Source + */ +#define WM2200_DSP1RMIX_SRC1_MASK 0x007F /* DSP1RMIX_SRC1 - [6:0] */ +#define WM2200_DSP1RMIX_SRC1_SHIFT 0 /* DSP1RMIX_SRC1 - [6:0] */ +#define WM2200_DSP1RMIX_SRC1_WIDTH 7 /* DSP1RMIX_SRC1 - [6:0] */ + +/* + * R1657 (0x679) - DSP1RMIX Input 1 Volume + */ +#define WM2200_DSP1RMIX_VOL1_MASK 0x00FE /* DSP1RMIX_VOL1 - [7:1] */ +#define WM2200_DSP1RMIX_VOL1_SHIFT 1 /* DSP1RMIX_VOL1 - [7:1] */ +#define WM2200_DSP1RMIX_VOL1_WIDTH 7 /* DSP1RMIX_VOL1 - [7:1] */ + +/* + * R1658 (0x67A) - DSP1RMIX Input 2 Source + */ +#define WM2200_DSP1RMIX_SRC2_MASK 0x007F /* DSP1RMIX_SRC2 - [6:0] */ +#define WM2200_DSP1RMIX_SRC2_SHIFT 0 /* DSP1RMIX_SRC2 - [6:0] */ +#define WM2200_DSP1RMIX_SRC2_WIDTH 7 /* DSP1RMIX_SRC2 - [6:0] */ + +/* + * R1659 (0x67B) - DSP1RMIX Input 2 Volume + */ +#define WM2200_DSP1RMIX_VOL2_MASK 0x00FE /* DSP1RMIX_VOL2 - [7:1] */ +#define WM2200_DSP1RMIX_VOL2_SHIFT 1 /* DSP1RMIX_VOL2 - [7:1] */ +#define WM2200_DSP1RMIX_VOL2_WIDTH 7 /* DSP1RMIX_VOL2 - [7:1] */ + +/* + * R1660 (0x67C) - DSP1RMIX Input 3 Source + */ +#define WM2200_DSP1RMIX_SRC3_MASK 0x007F /* DSP1RMIX_SRC3 - [6:0] */ +#define WM2200_DSP1RMIX_SRC3_SHIFT 0 /* DSP1RMIX_SRC3 - [6:0] */ +#define WM2200_DSP1RMIX_SRC3_WIDTH 7 /* DSP1RMIX_SRC3 - [6:0] */ + +/* + * R1661 (0x67D) - DSP1RMIX Input 3 Volume + */ +#define WM2200_DSP1RMIX_VOL3_MASK 0x00FE /* DSP1RMIX_VOL3 - [7:1] */ +#define WM2200_DSP1RMIX_VOL3_SHIFT 1 /* DSP1RMIX_VOL3 - [7:1] */ +#define WM2200_DSP1RMIX_VOL3_WIDTH 7 /* DSP1RMIX_VOL3 - [7:1] */ + +/* + * R1662 (0x67E) - DSP1RMIX Input 4 Source + */ +#define WM2200_DSP1RMIX_SRC4_MASK 0x007F /* DSP1RMIX_SRC4 - [6:0] */ +#define WM2200_DSP1RMIX_SRC4_SHIFT 0 /* DSP1RMIX_SRC4 - [6:0] */ +#define WM2200_DSP1RMIX_SRC4_WIDTH 7 /* DSP1RMIX_SRC4 - [6:0] */ + +/* + * R1663 (0x67F) - DSP1RMIX Input 4 Volume + */ +#define WM2200_DSP1RMIX_VOL4_MASK 0x00FE /* DSP1RMIX_VOL4 - [7:1] */ +#define WM2200_DSP1RMIX_VOL4_SHIFT 1 /* DSP1RMIX_VOL4 - [7:1] */ +#define WM2200_DSP1RMIX_VOL4_WIDTH 7 /* DSP1RMIX_VOL4 - [7:1] */ + +/* + * R1664 (0x680) - DSP1AUX1MIX Input 1 Source + */ +#define WM2200_DSP1AUX1MIX_SRC1_MASK 0x007F /* DSP1AUX1MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX1MIX_SRC1_SHIFT 0 /* DSP1AUX1MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX1MIX_SRC1_WIDTH 7 /* DSP1AUX1MIX_SRC1 - [6:0] */ + +/* + * R1665 (0x681) - DSP1AUX2MIX Input 1 Source + */ +#define WM2200_DSP1AUX2MIX_SRC1_MASK 0x007F /* DSP1AUX2MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX2MIX_SRC1_SHIFT 0 /* DSP1AUX2MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX2MIX_SRC1_WIDTH 7 /* DSP1AUX2MIX_SRC1 - [6:0] */ + +/* + * R1666 (0x682) - DSP1AUX3MIX Input 1 Source + */ +#define WM2200_DSP1AUX3MIX_SRC1_MASK 0x007F /* DSP1AUX3MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX3MIX_SRC1_SHIFT 0 /* DSP1AUX3MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX3MIX_SRC1_WIDTH 7 /* DSP1AUX3MIX_SRC1 - [6:0] */ + +/* + * R1667 (0x683) - DSP1AUX4MIX Input 1 Source + */ +#define WM2200_DSP1AUX4MIX_SRC1_MASK 0x007F /* DSP1AUX4MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX4MIX_SRC1_SHIFT 0 /* DSP1AUX4MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX4MIX_SRC1_WIDTH 7 /* DSP1AUX4MIX_SRC1 - [6:0] */ + +/* + * R1668 (0x684) - DSP1AUX5MIX Input 1 Source + */ +#define WM2200_DSP1AUX5MIX_SRC1_MASK 0x007F /* DSP1AUX5MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX5MIX_SRC1_SHIFT 0 /* DSP1AUX5MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX5MIX_SRC1_WIDTH 7 /* DSP1AUX5MIX_SRC1 - [6:0] */ + +/* + * R1669 (0x685) - DSP1AUX6MIX Input 1 Source + */ +#define WM2200_DSP1AUX6MIX_SRC1_MASK 0x007F /* DSP1AUX6MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX6MIX_SRC1_SHIFT 0 /* DSP1AUX6MIX_SRC1 - [6:0] */ +#define WM2200_DSP1AUX6MIX_SRC1_WIDTH 7 /* DSP1AUX6MIX_SRC1 - [6:0] */ + +/* + * R1670 (0x686) - DSP2LMIX Input 1 Source + */ +#define WM2200_DSP2LMIX_SRC1_MASK 0x007F /* DSP2LMIX_SRC1 - [6:0] */ +#define WM2200_DSP2LMIX_SRC1_SHIFT 0 /* DSP2LMIX_SRC1 - [6:0] */ +#define WM2200_DSP2LMIX_SRC1_WIDTH 7 /* DSP2LMIX_SRC1 - [6:0] */ + +/* + * R1671 (0x687) - DSP2LMIX Input 1 Volume + */ +#define WM2200_DSP2LMIX_VOL1_MASK 0x00FE /* DSP2LMIX_VOL1 - [7:1] */ +#define WM2200_DSP2LMIX_VOL1_SHIFT 1 /* DSP2LMIX_VOL1 - [7:1] */ +#define WM2200_DSP2LMIX_VOL1_WIDTH 7 /* DSP2LMIX_VOL1 - [7:1] */ + +/* + * R1672 (0x688) - DSP2LMIX Input 2 Source + */ +#define WM2200_DSP2LMIX_SRC2_MASK 0x007F /* DSP2LMIX_SRC2 - [6:0] */ +#define WM2200_DSP2LMIX_SRC2_SHIFT 0 /* DSP2LMIX_SRC2 - [6:0] */ +#define WM2200_DSP2LMIX_SRC2_WIDTH 7 /* DSP2LMIX_SRC2 - [6:0] */ + +/* + * R1673 (0x689) - DSP2LMIX Input 2 Volume + */ +#define WM2200_DSP2LMIX_VOL2_MASK 0x00FE /* DSP2LMIX_VOL2 - [7:1] */ +#define WM2200_DSP2LMIX_VOL2_SHIFT 1 /* DSP2LMIX_VOL2 - [7:1] */ +#define WM2200_DSP2LMIX_VOL2_WIDTH 7 /* DSP2LMIX_VOL2 - [7:1] */ + +/* + * R1674 (0x68A) - DSP2LMIX Input 3 Source + */ +#define WM2200_DSP2LMIX_SRC3_MASK 0x007F /* DSP2LMIX_SRC3 - [6:0] */ +#define WM2200_DSP2LMIX_SRC3_SHIFT 0 /* DSP2LMIX_SRC3 - [6:0] */ +#define WM2200_DSP2LMIX_SRC3_WIDTH 7 /* DSP2LMIX_SRC3 - [6:0] */ + +/* + * R1675 (0x68B) - DSP2LMIX Input 3 Volume + */ +#define WM2200_DSP2LMIX_VOL3_MASK 0x00FE /* DSP2LMIX_VOL3 - [7:1] */ +#define WM2200_DSP2LMIX_VOL3_SHIFT 1 /* DSP2LMIX_VOL3 - [7:1] */ +#define WM2200_DSP2LMIX_VOL3_WIDTH 7 /* DSP2LMIX_VOL3 - [7:1] */ + +/* + * R1676 (0x68C) - DSP2LMIX Input 4 Source + */ +#define WM2200_DSP2LMIX_SRC4_MASK 0x007F /* DSP2LMIX_SRC4 - [6:0] */ +#define WM2200_DSP2LMIX_SRC4_SHIFT 0 /* DSP2LMIX_SRC4 - [6:0] */ +#define WM2200_DSP2LMIX_SRC4_WIDTH 7 /* DSP2LMIX_SRC4 - [6:0] */ + +/* + * R1677 (0x68D) - DSP2LMIX Input 4 Volume + */ +#define WM2200_DSP2LMIX_VOL4_MASK 0x00FE /* DSP2LMIX_VOL4 - [7:1] */ +#define WM2200_DSP2LMIX_VOL4_SHIFT 1 /* DSP2LMIX_VOL4 - [7:1] */ +#define WM2200_DSP2LMIX_VOL4_WIDTH 7 /* DSP2LMIX_VOL4 - [7:1] */ + +/* + * R1678 (0x68E) - DSP2RMIX Input 1 Source + */ +#define WM2200_DSP2RMIX_SRC1_MASK 0x007F /* DSP2RMIX_SRC1 - [6:0] */ +#define WM2200_DSP2RMIX_SRC1_SHIFT 0 /* DSP2RMIX_SRC1 - [6:0] */ +#define WM2200_DSP2RMIX_SRC1_WIDTH 7 /* DSP2RMIX_SRC1 - [6:0] */ + +/* + * R1679 (0x68F) - DSP2RMIX Input 1 Volume + */ +#define WM2200_DSP2RMIX_VOL1_MASK 0x00FE /* DSP2RMIX_VOL1 - [7:1] */ +#define WM2200_DSP2RMIX_VOL1_SHIFT 1 /* DSP2RMIX_VOL1 - [7:1] */ +#define WM2200_DSP2RMIX_VOL1_WIDTH 7 /* DSP2RMIX_VOL1 - [7:1] */ + +/* + * R1680 (0x690) - DSP2RMIX Input 2 Source + */ +#define WM2200_DSP2RMIX_SRC2_MASK 0x007F /* DSP2RMIX_SRC2 - [6:0] */ +#define WM2200_DSP2RMIX_SRC2_SHIFT 0 /* DSP2RMIX_SRC2 - [6:0] */ +#define WM2200_DSP2RMIX_SRC2_WIDTH 7 /* DSP2RMIX_SRC2 - [6:0] */ + +/* + * R1681 (0x691) - DSP2RMIX Input 2 Volume + */ +#define WM2200_DSP2RMIX_VOL2_MASK 0x00FE /* DSP2RMIX_VOL2 - [7:1] */ +#define WM2200_DSP2RMIX_VOL2_SHIFT 1 /* DSP2RMIX_VOL2 - [7:1] */ +#define WM2200_DSP2RMIX_VOL2_WIDTH 7 /* DSP2RMIX_VOL2 - [7:1] */ + +/* + * R1682 (0x692) - DSP2RMIX Input 3 Source + */ +#define WM2200_DSP2RMIX_SRC3_MASK 0x007F /* DSP2RMIX_SRC3 - [6:0] */ +#define WM2200_DSP2RMIX_SRC3_SHIFT 0 /* DSP2RMIX_SRC3 - [6:0] */ +#define WM2200_DSP2RMIX_SRC3_WIDTH 7 /* DSP2RMIX_SRC3 - [6:0] */ + +/* + * R1683 (0x693) - DSP2RMIX Input 3 Volume + */ +#define WM2200_DSP2RMIX_VOL3_MASK 0x00FE /* DSP2RMIX_VOL3 - [7:1] */ +#define WM2200_DSP2RMIX_VOL3_SHIFT 1 /* DSP2RMIX_VOL3 - [7:1] */ +#define WM2200_DSP2RMIX_VOL3_WIDTH 7 /* DSP2RMIX_VOL3 - [7:1] */ + +/* + * R1684 (0x694) - DSP2RMIX Input 4 Source + */ +#define WM2200_DSP2RMIX_SRC4_MASK 0x007F /* DSP2RMIX_SRC4 - [6:0] */ +#define WM2200_DSP2RMIX_SRC4_SHIFT 0 /* DSP2RMIX_SRC4 - [6:0] */ +#define WM2200_DSP2RMIX_SRC4_WIDTH 7 /* DSP2RMIX_SRC4 - [6:0] */ + +/* + * R1685 (0x695) - DSP2RMIX Input 4 Volume + */ +#define WM2200_DSP2RMIX_VOL4_MASK 0x00FE /* DSP2RMIX_VOL4 - [7:1] */ +#define WM2200_DSP2RMIX_VOL4_SHIFT 1 /* DSP2RMIX_VOL4 - [7:1] */ +#define WM2200_DSP2RMIX_VOL4_WIDTH 7 /* DSP2RMIX_VOL4 - [7:1] */ + +/* + * R1686 (0x696) - DSP2AUX1MIX Input 1 Source + */ +#define WM2200_DSP2AUX1MIX_SRC1_MASK 0x007F /* DSP2AUX1MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX1MIX_SRC1_SHIFT 0 /* DSP2AUX1MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX1MIX_SRC1_WIDTH 7 /* DSP2AUX1MIX_SRC1 - [6:0] */ + +/* + * R1687 (0x697) - DSP2AUX2MIX Input 1 Source + */ +#define WM2200_DSP2AUX2MIX_SRC1_MASK 0x007F /* DSP2AUX2MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX2MIX_SRC1_SHIFT 0 /* DSP2AUX2MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX2MIX_SRC1_WIDTH 7 /* DSP2AUX2MIX_SRC1 - [6:0] */ + +/* + * R1688 (0x698) - DSP2AUX3MIX Input 1 Source + */ +#define WM2200_DSP2AUX3MIX_SRC1_MASK 0x007F /* DSP2AUX3MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX3MIX_SRC1_SHIFT 0 /* DSP2AUX3MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX3MIX_SRC1_WIDTH 7 /* DSP2AUX3MIX_SRC1 - [6:0] */ + +/* + * R1689 (0x699) - DSP2AUX4MIX Input 1 Source + */ +#define WM2200_DSP2AUX4MIX_SRC1_MASK 0x007F /* DSP2AUX4MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX4MIX_SRC1_SHIFT 0 /* DSP2AUX4MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX4MIX_SRC1_WIDTH 7 /* DSP2AUX4MIX_SRC1 - [6:0] */ + +/* + * R1690 (0x69A) - DSP2AUX5MIX Input 1 Source + */ +#define WM2200_DSP2AUX5MIX_SRC1_MASK 0x007F /* DSP2AUX5MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX5MIX_SRC1_SHIFT 0 /* DSP2AUX5MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX5MIX_SRC1_WIDTH 7 /* DSP2AUX5MIX_SRC1 - [6:0] */ + +/* + * R1691 (0x69B) - DSP2AUX6MIX Input 1 Source + */ +#define WM2200_DSP2AUX6MIX_SRC1_MASK 0x007F /* DSP2AUX6MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX6MIX_SRC1_SHIFT 0 /* DSP2AUX6MIX_SRC1 - [6:0] */ +#define WM2200_DSP2AUX6MIX_SRC1_WIDTH 7 /* DSP2AUX6MIX_SRC1 - [6:0] */ + +/* + * R1792 (0x700) - GPIO CTRL 1 + */ +#define WM2200_GP1_DIR 0x8000 /* GP1_DIR */ +#define WM2200_GP1_DIR_MASK 0x8000 /* GP1_DIR */ +#define WM2200_GP1_DIR_SHIFT 15 /* GP1_DIR */ +#define WM2200_GP1_DIR_WIDTH 1 /* GP1_DIR */ +#define WM2200_GP1_PU 0x4000 /* GP1_PU */ +#define WM2200_GP1_PU_MASK 0x4000 /* GP1_PU */ +#define WM2200_GP1_PU_SHIFT 14 /* GP1_PU */ +#define WM2200_GP1_PU_WIDTH 1 /* GP1_PU */ +#define WM2200_GP1_PD 0x2000 /* GP1_PD */ +#define WM2200_GP1_PD_MASK 0x2000 /* GP1_PD */ +#define WM2200_GP1_PD_SHIFT 13 /* GP1_PD */ +#define WM2200_GP1_PD_WIDTH 1 /* GP1_PD */ +#define WM2200_GP1_POL 0x0400 /* GP1_POL */ +#define WM2200_GP1_POL_MASK 0x0400 /* GP1_POL */ +#define WM2200_GP1_POL_SHIFT 10 /* GP1_POL */ +#define WM2200_GP1_POL_WIDTH 1 /* GP1_POL */ +#define WM2200_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */ +#define WM2200_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */ +#define WM2200_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */ +#define WM2200_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */ +#define WM2200_GP1_DB 0x0100 /* GP1_DB */ +#define WM2200_GP1_DB_MASK 0x0100 /* GP1_DB */ +#define WM2200_GP1_DB_SHIFT 8 /* GP1_DB */ +#define WM2200_GP1_DB_WIDTH 1 /* GP1_DB */ +#define WM2200_GP1_LVL 0x0040 /* GP1_LVL */ +#define WM2200_GP1_LVL_MASK 0x0040 /* GP1_LVL */ +#define WM2200_GP1_LVL_SHIFT 6 /* GP1_LVL */ +#define WM2200_GP1_LVL_WIDTH 1 /* GP1_LVL */ +#define WM2200_GP1_FN_MASK 0x003F /* GP1_FN - [5:0] */ +#define WM2200_GP1_FN_SHIFT 0 /* GP1_FN - [5:0] */ +#define WM2200_GP1_FN_WIDTH 6 /* GP1_FN - [5:0] */ + +/* + * R1793 (0x701) - GPIO CTRL 2 + */ +#define WM2200_GP2_DIR 0x8000 /* GP2_DIR */ +#define WM2200_GP2_DIR_MASK 0x8000 /* GP2_DIR */ +#define WM2200_GP2_DIR_SHIFT 15 /* GP2_DIR */ +#define WM2200_GP2_DIR_WIDTH 1 /* GP2_DIR */ +#define WM2200_GP2_PU 0x4000 /* GP2_PU */ +#define WM2200_GP2_PU_MASK 0x4000 /* GP2_PU */ +#define WM2200_GP2_PU_SHIFT 14 /* GP2_PU */ +#define WM2200_GP2_PU_WIDTH 1 /* GP2_PU */ +#define WM2200_GP2_PD 0x2000 /* GP2_PD */ +#define WM2200_GP2_PD_MASK 0x2000 /* GP2_PD */ +#define WM2200_GP2_PD_SHIFT 13 /* GP2_PD */ +#define WM2200_GP2_PD_WIDTH 1 /* GP2_PD */ +#define WM2200_GP2_POL 0x0400 /* GP2_POL */ +#define WM2200_GP2_POL_MASK 0x0400 /* GP2_POL */ +#define WM2200_GP2_POL_SHIFT 10 /* GP2_POL */ +#define WM2200_GP2_POL_WIDTH 1 /* GP2_POL */ +#define WM2200_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */ +#define WM2200_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */ +#define WM2200_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */ +#define WM2200_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */ +#define WM2200_GP2_DB 0x0100 /* GP2_DB */ +#define WM2200_GP2_DB_MASK 0x0100 /* GP2_DB */ +#define WM2200_GP2_DB_SHIFT 8 /* GP2_DB */ +#define WM2200_GP2_DB_WIDTH 1 /* GP2_DB */ +#define WM2200_GP2_LVL 0x0040 /* GP2_LVL */ +#define WM2200_GP2_LVL_MASK 0x0040 /* GP2_LVL */ +#define WM2200_GP2_LVL_SHIFT 6 /* GP2_LVL */ +#define WM2200_GP2_LVL_WIDTH 1 /* GP2_LVL */ +#define WM2200_GP2_FN_MASK 0x003F /* GP2_FN - [5:0] */ +#define WM2200_GP2_FN_SHIFT 0 /* GP2_FN - [5:0] */ +#define WM2200_GP2_FN_WIDTH 6 /* GP2_FN - [5:0] */ + +/* + * R1794 (0x702) - GPIO CTRL 3 + */ +#define WM2200_GP3_DIR 0x8000 /* GP3_DIR */ +#define WM2200_GP3_DIR_MASK 0x8000 /* GP3_DIR */ +#define WM2200_GP3_DIR_SHIFT 15 /* GP3_DIR */ +#define WM2200_GP3_DIR_WIDTH 1 /* GP3_DIR */ +#define WM2200_GP3_PU 0x4000 /* GP3_PU */ +#define WM2200_GP3_PU_MASK 0x4000 /* GP3_PU */ +#define WM2200_GP3_PU_SHIFT 14 /* GP3_PU */ +#define WM2200_GP3_PU_WIDTH 1 /* GP3_PU */ +#define WM2200_GP3_PD 0x2000 /* GP3_PD */ +#define WM2200_GP3_PD_MASK 0x2000 /* GP3_PD */ +#define WM2200_GP3_PD_SHIFT 13 /* GP3_PD */ +#define WM2200_GP3_PD_WIDTH 1 /* GP3_PD */ +#define WM2200_GP3_POL 0x0400 /* GP3_POL */ +#define WM2200_GP3_POL_MASK 0x0400 /* GP3_POL */ +#define WM2200_GP3_POL_SHIFT 10 /* GP3_POL */ +#define WM2200_GP3_POL_WIDTH 1 /* GP3_POL */ +#define WM2200_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */ +#define WM2200_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */ +#define WM2200_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */ +#define WM2200_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */ +#define WM2200_GP3_DB 0x0100 /* GP3_DB */ +#define WM2200_GP3_DB_MASK 0x0100 /* GP3_DB */ +#define WM2200_GP3_DB_SHIFT 8 /* GP3_DB */ +#define WM2200_GP3_DB_WIDTH 1 /* GP3_DB */ +#define WM2200_GP3_LVL 0x0040 /* GP3_LVL */ +#define WM2200_GP3_LVL_MASK 0x0040 /* GP3_LVL */ +#define WM2200_GP3_LVL_SHIFT 6 /* GP3_LVL */ +#define WM2200_GP3_LVL_WIDTH 1 /* GP3_LVL */ +#define WM2200_GP3_FN_MASK 0x003F /* GP3_FN - [5:0] */ +#define WM2200_GP3_FN_SHIFT 0 /* GP3_FN - [5:0] */ +#define WM2200_GP3_FN_WIDTH 6 /* GP3_FN - [5:0] */ + +/* + * R1795 (0x703) - GPIO CTRL 4 + */ +#define WM2200_GP4_DIR 0x8000 /* GP4_DIR */ +#define WM2200_GP4_DIR_MASK 0x8000 /* GP4_DIR */ +#define WM2200_GP4_DIR_SHIFT 15 /* GP4_DIR */ +#define WM2200_GP4_DIR_WIDTH 1 /* GP4_DIR */ +#define WM2200_GP4_PU 0x4000 /* GP4_PU */ +#define WM2200_GP4_PU_MASK 0x4000 /* GP4_PU */ +#define WM2200_GP4_PU_SHIFT 14 /* GP4_PU */ +#define WM2200_GP4_PU_WIDTH 1 /* GP4_PU */ +#define WM2200_GP4_PD 0x2000 /* GP4_PD */ +#define WM2200_GP4_PD_MASK 0x2000 /* GP4_PD */ +#define WM2200_GP4_PD_SHIFT 13 /* GP4_PD */ +#define WM2200_GP4_PD_WIDTH 1 /* GP4_PD */ +#define WM2200_GP4_POL 0x0400 /* GP4_POL */ +#define WM2200_GP4_POL_MASK 0x0400 /* GP4_POL */ +#define WM2200_GP4_POL_SHIFT 10 /* GP4_POL */ +#define WM2200_GP4_POL_WIDTH 1 /* GP4_POL */ +#define WM2200_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */ +#define WM2200_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */ +#define WM2200_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */ +#define WM2200_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */ +#define WM2200_GP4_DB 0x0100 /* GP4_DB */ +#define WM2200_GP4_DB_MASK 0x0100 /* GP4_DB */ +#define WM2200_GP4_DB_SHIFT 8 /* GP4_DB */ +#define WM2200_GP4_DB_WIDTH 1 /* GP4_DB */ +#define WM2200_GP4_LVL 0x0040 /* GP4_LVL */ +#define WM2200_GP4_LVL_MASK 0x0040 /* GP4_LVL */ +#define WM2200_GP4_LVL_SHIFT 6 /* GP4_LVL */ +#define WM2200_GP4_LVL_WIDTH 1 /* GP4_LVL */ +#define WM2200_GP4_FN_MASK 0x003F /* GP4_FN - [5:0] */ +#define WM2200_GP4_FN_SHIFT 0 /* GP4_FN - [5:0] */ +#define WM2200_GP4_FN_WIDTH 6 /* GP4_FN - [5:0] */ + +/* + * R1799 (0x707) - ADPS1 IRQ0 + */ +#define WM2200_DSP_IRQ1 0x0002 /* DSP_IRQ1 */ +#define WM2200_DSP_IRQ1_MASK 0x0002 /* DSP_IRQ1 */ +#define WM2200_DSP_IRQ1_SHIFT 1 /* DSP_IRQ1 */ +#define WM2200_DSP_IRQ1_WIDTH 1 /* DSP_IRQ1 */ +#define WM2200_DSP_IRQ0 0x0001 /* DSP_IRQ0 */ +#define WM2200_DSP_IRQ0_MASK 0x0001 /* DSP_IRQ0 */ +#define WM2200_DSP_IRQ0_SHIFT 0 /* DSP_IRQ0 */ +#define WM2200_DSP_IRQ0_WIDTH 1 /* DSP_IRQ0 */ + +/* + * R1800 (0x708) - ADPS1 IRQ1 + */ +#define WM2200_DSP_IRQ3 0x0002 /* DSP_IRQ3 */ +#define WM2200_DSP_IRQ3_MASK 0x0002 /* DSP_IRQ3 */ +#define WM2200_DSP_IRQ3_SHIFT 1 /* DSP_IRQ3 */ +#define WM2200_DSP_IRQ3_WIDTH 1 /* DSP_IRQ3 */ +#define WM2200_DSP_IRQ2 0x0001 /* DSP_IRQ2 */ +#define WM2200_DSP_IRQ2_MASK 0x0001 /* DSP_IRQ2 */ +#define WM2200_DSP_IRQ2_SHIFT 0 /* DSP_IRQ2 */ +#define WM2200_DSP_IRQ2_WIDTH 1 /* DSP_IRQ2 */ + +/* + * R1801 (0x709) - Misc Pad Ctrl 1 + */ +#define WM2200_LDO1ENA_PD 0x8000 /* LDO1ENA_PD */ +#define WM2200_LDO1ENA_PD_MASK 0x8000 /* LDO1ENA_PD */ +#define WM2200_LDO1ENA_PD_SHIFT 15 /* LDO1ENA_PD */ +#define WM2200_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */ +#define WM2200_MCLK2_PD 0x2000 /* MCLK2_PD */ +#define WM2200_MCLK2_PD_MASK 0x2000 /* MCLK2_PD */ +#define WM2200_MCLK2_PD_SHIFT 13 /* MCLK2_PD */ +#define WM2200_MCLK2_PD_WIDTH 1 /* MCLK2_PD */ +#define WM2200_MCLK1_PD 0x1000 /* MCLK1_PD */ +#define WM2200_MCLK1_PD_MASK 0x1000 /* MCLK1_PD */ +#define WM2200_MCLK1_PD_SHIFT 12 /* MCLK1_PD */ +#define WM2200_MCLK1_PD_WIDTH 1 /* MCLK1_PD */ +#define WM2200_DACLRCLK1_PU 0x0400 /* DACLRCLK1_PU */ +#define WM2200_DACLRCLK1_PU_MASK 0x0400 /* DACLRCLK1_PU */ +#define WM2200_DACLRCLK1_PU_SHIFT 10 /* DACLRCLK1_PU */ +#define WM2200_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */ +#define WM2200_DACLRCLK1_PD 0x0200 /* DACLRCLK1_PD */ +#define WM2200_DACLRCLK1_PD_MASK 0x0200 /* DACLRCLK1_PD */ +#define WM2200_DACLRCLK1_PD_SHIFT 9 /* DACLRCLK1_PD */ +#define WM2200_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */ +#define WM2200_BCLK1_PU 0x0100 /* BCLK1_PU */ +#define WM2200_BCLK1_PU_MASK 0x0100 /* BCLK1_PU */ +#define WM2200_BCLK1_PU_SHIFT 8 /* BCLK1_PU */ +#define WM2200_BCLK1_PU_WIDTH 1 /* BCLK1_PU */ +#define WM2200_BCLK1_PD 0x0080 /* BCLK1_PD */ +#define WM2200_BCLK1_PD_MASK 0x0080 /* BCLK1_PD */ +#define WM2200_BCLK1_PD_SHIFT 7 /* BCLK1_PD */ +#define WM2200_BCLK1_PD_WIDTH 1 /* BCLK1_PD */ +#define WM2200_DACDAT1_PU 0x0040 /* DACDAT1_PU */ +#define WM2200_DACDAT1_PU_MASK 0x0040 /* DACDAT1_PU */ +#define WM2200_DACDAT1_PU_SHIFT 6 /* DACDAT1_PU */ +#define WM2200_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */ +#define WM2200_DACDAT1_PD 0x0020 /* DACDAT1_PD */ +#define WM2200_DACDAT1_PD_MASK 0x0020 /* DACDAT1_PD */ +#define WM2200_DACDAT1_PD_SHIFT 5 /* DACDAT1_PD */ +#define WM2200_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */ +#define WM2200_DMICDAT3_PD 0x0010 /* DMICDAT3_PD */ +#define WM2200_DMICDAT3_PD_MASK 0x0010 /* DMICDAT3_PD */ +#define WM2200_DMICDAT3_PD_SHIFT 4 /* DMICDAT3_PD */ +#define WM2200_DMICDAT3_PD_WIDTH 1 /* DMICDAT3_PD */ +#define WM2200_DMICDAT2_PD 0x0008 /* DMICDAT2_PD */ +#define WM2200_DMICDAT2_PD_MASK 0x0008 /* DMICDAT2_PD */ +#define WM2200_DMICDAT2_PD_SHIFT 3 /* DMICDAT2_PD */ +#define WM2200_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */ +#define WM2200_DMICDAT1_PD 0x0004 /* DMICDAT1_PD */ +#define WM2200_DMICDAT1_PD_MASK 0x0004 /* DMICDAT1_PD */ +#define WM2200_DMICDAT1_PD_SHIFT 2 /* DMICDAT1_PD */ +#define WM2200_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */ +#define WM2200_RSTB_PU 0x0002 /* RSTB_PU */ +#define WM2200_RSTB_PU_MASK 0x0002 /* RSTB_PU */ +#define WM2200_RSTB_PU_SHIFT 1 /* RSTB_PU */ +#define WM2200_RSTB_PU_WIDTH 1 /* RSTB_PU */ +#define WM2200_ADDR_PD 0x0001 /* ADDR_PD */ +#define WM2200_ADDR_PD_MASK 0x0001 /* ADDR_PD */ +#define WM2200_ADDR_PD_SHIFT 0 /* ADDR_PD */ +#define WM2200_ADDR_PD_WIDTH 1 /* ADDR_PD */ + +/* + * R2048 (0x800) - Interrupt Status 1 + */ +#define WM2200_DSP_IRQ0_EINT 0x0080 /* DSP_IRQ0_EINT */ +#define WM2200_DSP_IRQ0_EINT_MASK 0x0080 /* DSP_IRQ0_EINT */ +#define WM2200_DSP_IRQ0_EINT_SHIFT 7 /* DSP_IRQ0_EINT */ +#define WM2200_DSP_IRQ0_EINT_WIDTH 1 /* DSP_IRQ0_EINT */ +#define WM2200_DSP_IRQ1_EINT 0x0040 /* DSP_IRQ1_EINT */ +#define WM2200_DSP_IRQ1_EINT_MASK 0x0040 /* DSP_IRQ1_EINT */ +#define WM2200_DSP_IRQ1_EINT_SHIFT 6 /* DSP_IRQ1_EINT */ +#define WM2200_DSP_IRQ1_EINT_WIDTH 1 /* DSP_IRQ1_EINT */ +#define WM2200_DSP_IRQ2_EINT 0x0020 /* DSP_IRQ2_EINT */ +#define WM2200_DSP_IRQ2_EINT_MASK 0x0020 /* DSP_IRQ2_EINT */ +#define WM2200_DSP_IRQ2_EINT_SHIFT 5 /* DSP_IRQ2_EINT */ +#define WM2200_DSP_IRQ2_EINT_WIDTH 1 /* DSP_IRQ2_EINT */ +#define WM2200_DSP_IRQ3_EINT 0x0010 /* DSP_IRQ3_EINT */ +#define WM2200_DSP_IRQ3_EINT_MASK 0x0010 /* DSP_IRQ3_EINT */ +#define WM2200_DSP_IRQ3_EINT_SHIFT 4 /* DSP_IRQ3_EINT */ +#define WM2200_DSP_IRQ3_EINT_WIDTH 1 /* DSP_IRQ3_EINT */ +#define WM2200_GP4_EINT 0x0008 /* GP4_EINT */ +#define WM2200_GP4_EINT_MASK 0x0008 /* GP4_EINT */ +#define WM2200_GP4_EINT_SHIFT 3 /* GP4_EINT */ +#define WM2200_GP4_EINT_WIDTH 1 /* GP4_EINT */ +#define WM2200_GP3_EINT 0x0004 /* GP3_EINT */ +#define WM2200_GP3_EINT_MASK 0x0004 /* GP3_EINT */ +#define WM2200_GP3_EINT_SHIFT 2 /* GP3_EINT */ +#define WM2200_GP3_EINT_WIDTH 1 /* GP3_EINT */ +#define WM2200_GP2_EINT 0x0002 /* GP2_EINT */ +#define WM2200_GP2_EINT_MASK 0x0002 /* GP2_EINT */ +#define WM2200_GP2_EINT_SHIFT 1 /* GP2_EINT */ +#define WM2200_GP2_EINT_WIDTH 1 /* GP2_EINT */ +#define WM2200_GP1_EINT 0x0001 /* GP1_EINT */ +#define WM2200_GP1_EINT_MASK 0x0001 /* GP1_EINT */ +#define WM2200_GP1_EINT_SHIFT 0 /* GP1_EINT */ +#define WM2200_GP1_EINT_WIDTH 1 /* GP1_EINT */ + +/* + * R2049 (0x801) - Interrupt Status 1 Mask + */ +#define WM2200_IM_DSP_IRQ0_EINT 0x0080 /* IM_DSP_IRQ0_EINT */ +#define WM2200_IM_DSP_IRQ0_EINT_MASK 0x0080 /* IM_DSP_IRQ0_EINT */ +#define WM2200_IM_DSP_IRQ0_EINT_SHIFT 7 /* IM_DSP_IRQ0_EINT */ +#define WM2200_IM_DSP_IRQ0_EINT_WIDTH 1 /* IM_DSP_IRQ0_EINT */ +#define WM2200_IM_DSP_IRQ1_EINT 0x0040 /* IM_DSP_IRQ1_EINT */ +#define WM2200_IM_DSP_IRQ1_EINT_MASK 0x0040 /* IM_DSP_IRQ1_EINT */ +#define WM2200_IM_DSP_IRQ1_EINT_SHIFT 6 /* IM_DSP_IRQ1_EINT */ +#define WM2200_IM_DSP_IRQ1_EINT_WIDTH 1 /* IM_DSP_IRQ1_EINT */ +#define WM2200_IM_DSP_IRQ2_EINT 0x0020 /* IM_DSP_IRQ2_EINT */ +#define WM2200_IM_DSP_IRQ2_EINT_MASK 0x0020 /* IM_DSP_IRQ2_EINT */ +#define WM2200_IM_DSP_IRQ2_EINT_SHIFT 5 /* IM_DSP_IRQ2_EINT */ +#define WM2200_IM_DSP_IRQ2_EINT_WIDTH 1 /* IM_DSP_IRQ2_EINT */ +#define WM2200_IM_DSP_IRQ3_EINT 0x0010 /* IM_DSP_IRQ3_EINT */ +#define WM2200_IM_DSP_IRQ3_EINT_MASK 0x0010 /* IM_DSP_IRQ3_EINT */ +#define WM2200_IM_DSP_IRQ3_EINT_SHIFT 4 /* IM_DSP_IRQ3_EINT */ +#define WM2200_IM_DSP_IRQ3_EINT_WIDTH 1 /* IM_DSP_IRQ3_EINT */ +#define WM2200_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */ +#define WM2200_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */ +#define WM2200_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */ +#define WM2200_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */ +#define WM2200_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */ +#define WM2200_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */ +#define WM2200_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */ +#define WM2200_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */ +#define WM2200_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */ +#define WM2200_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */ +#define WM2200_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */ +#define WM2200_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */ +#define WM2200_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */ +#define WM2200_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */ +#define WM2200_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */ +#define WM2200_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */ + +/* + * R2050 (0x802) - Interrupt Status 2 + */ +#define WM2200_WSEQ_BUSY_EINT 0x0100 /* WSEQ_BUSY_EINT */ +#define WM2200_WSEQ_BUSY_EINT_MASK 0x0100 /* WSEQ_BUSY_EINT */ +#define WM2200_WSEQ_BUSY_EINT_SHIFT 8 /* WSEQ_BUSY_EINT */ +#define WM2200_WSEQ_BUSY_EINT_WIDTH 1 /* WSEQ_BUSY_EINT */ +#define WM2200_FLL_LOCK_EINT 0x0002 /* FLL_LOCK_EINT */ +#define WM2200_FLL_LOCK_EINT_MASK 0x0002 /* FLL_LOCK_EINT */ +#define WM2200_FLL_LOCK_EINT_SHIFT 1 /* FLL_LOCK_EINT */ +#define WM2200_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */ +#define WM2200_CLKGEN_EINT 0x0001 /* CLKGEN_EINT */ +#define WM2200_CLKGEN_EINT_MASK 0x0001 /* CLKGEN_EINT */ +#define WM2200_CLKGEN_EINT_SHIFT 0 /* CLKGEN_EINT */ +#define WM2200_CLKGEN_EINT_WIDTH 1 /* CLKGEN_EINT */ + +/* + * R2051 (0x803) - Interrupt Raw Status 2 + */ +#define WM2200_WSEQ_BUSY_STS 0x0100 /* WSEQ_BUSY_STS */ +#define WM2200_WSEQ_BUSY_STS_MASK 0x0100 /* WSEQ_BUSY_STS */ +#define WM2200_WSEQ_BUSY_STS_SHIFT 8 /* WSEQ_BUSY_STS */ +#define WM2200_WSEQ_BUSY_STS_WIDTH 1 /* WSEQ_BUSY_STS */ +#define WM2200_FLL_LOCK_STS 0x0002 /* FLL_LOCK_STS */ +#define WM2200_FLL_LOCK_STS_MASK 0x0002 /* FLL_LOCK_STS */ +#define WM2200_FLL_LOCK_STS_SHIFT 1 /* FLL_LOCK_STS */ +#define WM2200_FLL_LOCK_STS_WIDTH 1 /* FLL_LOCK_STS */ +#define WM2200_CLKGEN_STS 0x0001 /* CLKGEN_STS */ +#define WM2200_CLKGEN_STS_MASK 0x0001 /* CLKGEN_STS */ +#define WM2200_CLKGEN_STS_SHIFT 0 /* CLKGEN_STS */ +#define WM2200_CLKGEN_STS_WIDTH 1 /* CLKGEN_STS */ + +/* + * R2052 (0x804) - Interrupt Status 2 Mask + */ +#define WM2200_IM_WSEQ_BUSY_EINT 0x0100 /* IM_WSEQ_BUSY_EINT */ +#define WM2200_IM_WSEQ_BUSY_EINT_MASK 0x0100 /* IM_WSEQ_BUSY_EINT */ +#define WM2200_IM_WSEQ_BUSY_EINT_SHIFT 8 /* IM_WSEQ_BUSY_EINT */ +#define WM2200_IM_WSEQ_BUSY_EINT_WIDTH 1 /* IM_WSEQ_BUSY_EINT */ +#define WM2200_IM_FLL_LOCK_EINT 0x0002 /* IM_FLL_LOCK_EINT */ +#define WM2200_IM_FLL_LOCK_EINT_MASK 0x0002 /* IM_FLL_LOCK_EINT */ +#define WM2200_IM_FLL_LOCK_EINT_SHIFT 1 /* IM_FLL_LOCK_EINT */ +#define WM2200_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */ +#define WM2200_IM_CLKGEN_EINT 0x0001 /* IM_CLKGEN_EINT */ +#define WM2200_IM_CLKGEN_EINT_MASK 0x0001 /* IM_CLKGEN_EINT */ +#define WM2200_IM_CLKGEN_EINT_SHIFT 0 /* IM_CLKGEN_EINT */ +#define WM2200_IM_CLKGEN_EINT_WIDTH 1 /* IM_CLKGEN_EINT */ + +/* + * R2056 (0x808) - Interrupt Control + */ +#define WM2200_IM_IRQ 0x0001 /* IM_IRQ */ +#define WM2200_IM_IRQ_MASK 0x0001 /* IM_IRQ */ +#define WM2200_IM_IRQ_SHIFT 0 /* IM_IRQ */ +#define WM2200_IM_IRQ_WIDTH 1 /* IM_IRQ */ + +/* + * R2304 (0x900) - EQL_1 + */ +#define WM2200_EQL_B1_GAIN_MASK 0xF800 /* EQL_B1_GAIN - [15:11] */ +#define WM2200_EQL_B1_GAIN_SHIFT 11 /* EQL_B1_GAIN - [15:11] */ +#define WM2200_EQL_B1_GAIN_WIDTH 5 /* EQL_B1_GAIN - [15:11] */ +#define WM2200_EQL_B2_GAIN_MASK 0x07C0 /* EQL_B2_GAIN - [10:6] */ +#define WM2200_EQL_B2_GAIN_SHIFT 6 /* EQL_B2_GAIN - [10:6] */ +#define WM2200_EQL_B2_GAIN_WIDTH 5 /* EQL_B2_GAIN - [10:6] */ +#define WM2200_EQL_B3_GAIN_MASK 0x003E /* EQL_B3_GAIN - [5:1] */ +#define WM2200_EQL_B3_GAIN_SHIFT 1 /* EQL_B3_GAIN - [5:1] */ +#define WM2200_EQL_B3_GAIN_WIDTH 5 /* EQL_B3_GAIN - [5:1] */ +#define WM2200_EQL_ENA 0x0001 /* EQL_ENA */ +#define WM2200_EQL_ENA_MASK 0x0001 /* EQL_ENA */ +#define WM2200_EQL_ENA_SHIFT 0 /* EQL_ENA */ +#define WM2200_EQL_ENA_WIDTH 1 /* EQL_ENA */ + +/* + * R2305 (0x901) - EQL_2 + */ +#define WM2200_EQL_B4_GAIN_MASK 0xF800 /* EQL_B4_GAIN - [15:11] */ +#define WM2200_EQL_B4_GAIN_SHIFT 11 /* EQL_B4_GAIN - [15:11] */ +#define WM2200_EQL_B4_GAIN_WIDTH 5 /* EQL_B4_GAIN - [15:11] */ +#define WM2200_EQL_B5_GAIN_MASK 0x07C0 /* EQL_B5_GAIN - [10:6] */ +#define WM2200_EQL_B5_GAIN_SHIFT 6 /* EQL_B5_GAIN - [10:6] */ +#define WM2200_EQL_B5_GAIN_WIDTH 5 /* EQL_B5_GAIN - [10:6] */ + +/* + * R2306 (0x902) - EQL_3 + */ +#define WM2200_EQL_B1_A_MASK 0xFFFF /* EQL_B1_A - [15:0] */ +#define WM2200_EQL_B1_A_SHIFT 0 /* EQL_B1_A - [15:0] */ +#define WM2200_EQL_B1_A_WIDTH 16 /* EQL_B1_A - [15:0] */ + +/* + * R2307 (0x903) - EQL_4 + */ +#define WM2200_EQL_B1_B_MASK 0xFFFF /* EQL_B1_B - [15:0] */ +#define WM2200_EQL_B1_B_SHIFT 0 /* EQL_B1_B - [15:0] */ +#define WM2200_EQL_B1_B_WIDTH 16 /* EQL_B1_B - [15:0] */ + +/* + * R2308 (0x904) - EQL_5 + */ +#define WM2200_EQL_B1_PG_MASK 0xFFFF /* EQL_B1_PG - [15:0] */ +#define WM2200_EQL_B1_PG_SHIFT 0 /* EQL_B1_PG - [15:0] */ +#define WM2200_EQL_B1_PG_WIDTH 16 /* EQL_B1_PG - [15:0] */ + +/* + * R2309 (0x905) - EQL_6 + */ +#define WM2200_EQL_B2_A_MASK 0xFFFF /* EQL_B2_A - [15:0] */ +#define WM2200_EQL_B2_A_SHIFT 0 /* EQL_B2_A - [15:0] */ +#define WM2200_EQL_B2_A_WIDTH 16 /* EQL_B2_A - [15:0] */ + +/* + * R2310 (0x906) - EQL_7 + */ +#define WM2200_EQL_B2_B_MASK 0xFFFF /* EQL_B2_B - [15:0] */ +#define WM2200_EQL_B2_B_SHIFT 0 /* EQL_B2_B - [15:0] */ +#define WM2200_EQL_B2_B_WIDTH 16 /* EQL_B2_B - [15:0] */ + +/* + * R2311 (0x907) - EQL_8 + */ +#define WM2200_EQL_B2_C_MASK 0xFFFF /* EQL_B2_C - [15:0] */ +#define WM2200_EQL_B2_C_SHIFT 0 /* EQL_B2_C - [15:0] */ +#define WM2200_EQL_B2_C_WIDTH 16 /* EQL_B2_C - [15:0] */ + +/* + * R2312 (0x908) - EQL_9 + */ +#define WM2200_EQL_B2_PG_MASK 0xFFFF /* EQL_B2_PG - [15:0] */ +#define WM2200_EQL_B2_PG_SHIFT 0 /* EQL_B2_PG - [15:0] */ +#define WM2200_EQL_B2_PG_WIDTH 16 /* EQL_B2_PG - [15:0] */ + +/* + * R2313 (0x909) - EQL_10 + */ +#define WM2200_EQL_B3_A_MASK 0xFFFF /* EQL_B3_A - [15:0] */ +#define WM2200_EQL_B3_A_SHIFT 0 /* EQL_B3_A - [15:0] */ +#define WM2200_EQL_B3_A_WIDTH 16 /* EQL_B3_A - [15:0] */ + +/* + * R2314 (0x90A) - EQL_11 + */ +#define WM2200_EQL_B3_B_MASK 0xFFFF /* EQL_B3_B - [15:0] */ +#define WM2200_EQL_B3_B_SHIFT 0 /* EQL_B3_B - [15:0] */ +#define WM2200_EQL_B3_B_WIDTH 16 /* EQL_B3_B - [15:0] */ + +/* + * R2315 (0x90B) - EQL_12 + */ +#define WM2200_EQL_B3_C_MASK 0xFFFF /* EQL_B3_C - [15:0] */ +#define WM2200_EQL_B3_C_SHIFT 0 /* EQL_B3_C - [15:0] */ +#define WM2200_EQL_B3_C_WIDTH 16 /* EQL_B3_C - [15:0] */ + +/* + * R2316 (0x90C) - EQL_13 + */ +#define WM2200_EQL_B3_PG_MASK 0xFFFF /* EQL_B3_PG - [15:0] */ +#define WM2200_EQL_B3_PG_SHIFT 0 /* EQL_B3_PG - [15:0] */ +#define WM2200_EQL_B3_PG_WIDTH 16 /* EQL_B3_PG - [15:0] */ + +/* + * R2317 (0x90D) - EQL_14 + */ +#define WM2200_EQL_B4_A_MASK 0xFFFF /* EQL_B4_A - [15:0] */ +#define WM2200_EQL_B4_A_SHIFT 0 /* EQL_B4_A - [15:0] */ +#define WM2200_EQL_B4_A_WIDTH 16 /* EQL_B4_A - [15:0] */ + +/* + * R2318 (0x90E) - EQL_15 + */ +#define WM2200_EQL_B4_B_MASK 0xFFFF /* EQL_B4_B - [15:0] */ +#define WM2200_EQL_B4_B_SHIFT 0 /* EQL_B4_B - [15:0] */ +#define WM2200_EQL_B4_B_WIDTH 16 /* EQL_B4_B - [15:0] */ + +/* + * R2319 (0x90F) - EQL_16 + */ +#define WM2200_EQL_B4_C_MASK 0xFFFF /* EQL_B4_C - [15:0] */ +#define WM2200_EQL_B4_C_SHIFT 0 /* EQL_B4_C - [15:0] */ +#define WM2200_EQL_B4_C_WIDTH 16 /* EQL_B4_C - [15:0] */ + +/* + * R2320 (0x910) - EQL_17 + */ +#define WM2200_EQL_B4_PG_MASK 0xFFFF /* EQL_B4_PG - [15:0] */ +#define WM2200_EQL_B4_PG_SHIFT 0 /* EQL_B4_PG - [15:0] */ +#define WM2200_EQL_B4_PG_WIDTH 16 /* EQL_B4_PG - [15:0] */ + +/* + * R2321 (0x911) - EQL_18 + */ +#define WM2200_EQL_B5_A_MASK 0xFFFF /* EQL_B5_A - [15:0] */ +#define WM2200_EQL_B5_A_SHIFT 0 /* EQL_B5_A - [15:0] */ +#define WM2200_EQL_B5_A_WIDTH 16 /* EQL_B5_A - [15:0] */ + +/* + * R2322 (0x912) - EQL_19 + */ +#define WM2200_EQL_B5_B_MASK 0xFFFF /* EQL_B5_B - [15:0] */ +#define WM2200_EQL_B5_B_SHIFT 0 /* EQL_B5_B - [15:0] */ +#define WM2200_EQL_B5_B_WIDTH 16 /* EQL_B5_B - [15:0] */ + +/* + * R2323 (0x913) - EQL_20 + */ +#define WM2200_EQL_B5_PG_MASK 0xFFFF /* EQL_B5_PG - [15:0] */ +#define WM2200_EQL_B5_PG_SHIFT 0 /* EQL_B5_PG - [15:0] */ +#define WM2200_EQL_B5_PG_WIDTH 16 /* EQL_B5_PG - [15:0] */ + +/* + * R2326 (0x916) - EQR_1 + */ +#define WM2200_EQR_B1_GAIN_MASK 0xF800 /* EQR_B1_GAIN - [15:11] */ +#define WM2200_EQR_B1_GAIN_SHIFT 11 /* EQR_B1_GAIN - [15:11] */ +#define WM2200_EQR_B1_GAIN_WIDTH 5 /* EQR_B1_GAIN - [15:11] */ +#define WM2200_EQR_B2_GAIN_MASK 0x07C0 /* EQR_B2_GAIN - [10:6] */ +#define WM2200_EQR_B2_GAIN_SHIFT 6 /* EQR_B2_GAIN - [10:6] */ +#define WM2200_EQR_B2_GAIN_WIDTH 5 /* EQR_B2_GAIN - [10:6] */ +#define WM2200_EQR_B3_GAIN_MASK 0x003E /* EQR_B3_GAIN - [5:1] */ +#define WM2200_EQR_B3_GAIN_SHIFT 1 /* EQR_B3_GAIN - [5:1] */ +#define WM2200_EQR_B3_GAIN_WIDTH 5 /* EQR_B3_GAIN - [5:1] */ +#define WM2200_EQR_ENA 0x0001 /* EQR_ENA */ +#define WM2200_EQR_ENA_MASK 0x0001 /* EQR_ENA */ +#define WM2200_EQR_ENA_SHIFT 0 /* EQR_ENA */ +#define WM2200_EQR_ENA_WIDTH 1 /* EQR_ENA */ + +/* + * R2327 (0x917) - EQR_2 + */ +#define WM2200_EQR_B4_GAIN_MASK 0xF800 /* EQR_B4_GAIN - [15:11] */ +#define WM2200_EQR_B4_GAIN_SHIFT 11 /* EQR_B4_GAIN - [15:11] */ +#define WM2200_EQR_B4_GAIN_WIDTH 5 /* EQR_B4_GAIN - [15:11] */ +#define WM2200_EQR_B5_GAIN_MASK 0x07C0 /* EQR_B5_GAIN - [10:6] */ +#define WM2200_EQR_B5_GAIN_SHIFT 6 /* EQR_B5_GAIN - [10:6] */ +#define WM2200_EQR_B5_GAIN_WIDTH 5 /* EQR_B5_GAIN - [10:6] */ + +/* + * R2328 (0x918) - EQR_3 + */ +#define WM2200_EQR_B1_A_MASK 0xFFFF /* EQR_B1_A - [15:0] */ +#define WM2200_EQR_B1_A_SHIFT 0 /* EQR_B1_A - [15:0] */ +#define WM2200_EQR_B1_A_WIDTH 16 /* EQR_B1_A - [15:0] */ + +/* + * R2329 (0x919) - EQR_4 + */ +#define WM2200_EQR_B1_B_MASK 0xFFFF /* EQR_B1_B - [15:0] */ +#define WM2200_EQR_B1_B_SHIFT 0 /* EQR_B1_B - [15:0] */ +#define WM2200_EQR_B1_B_WIDTH 16 /* EQR_B1_B - [15:0] */ + +/* + * R2330 (0x91A) - EQR_5 + */ +#define WM2200_EQR_B1_PG_MASK 0xFFFF /* EQR_B1_PG - [15:0] */ +#define WM2200_EQR_B1_PG_SHIFT 0 /* EQR_B1_PG - [15:0] */ +#define WM2200_EQR_B1_PG_WIDTH 16 /* EQR_B1_PG - [15:0] */ + +/* + * R2331 (0x91B) - EQR_6 + */ +#define WM2200_EQR_B2_A_MASK 0xFFFF /* EQR_B2_A - [15:0] */ +#define WM2200_EQR_B2_A_SHIFT 0 /* EQR_B2_A - [15:0] */ +#define WM2200_EQR_B2_A_WIDTH 16 /* EQR_B2_A - [15:0] */ + +/* + * R2332 (0x91C) - EQR_7 + */ +#define WM2200_EQR_B2_B_MASK 0xFFFF /* EQR_B2_B - [15:0] */ +#define WM2200_EQR_B2_B_SHIFT 0 /* EQR_B2_B - [15:0] */ +#define WM2200_EQR_B2_B_WIDTH 16 /* EQR_B2_B - [15:0] */ + +/* + * R2333 (0x91D) - EQR_8 + */ +#define WM2200_EQR_B2_C_MASK 0xFFFF /* EQR_B2_C - [15:0] */ +#define WM2200_EQR_B2_C_SHIFT 0 /* EQR_B2_C - [15:0] */ +#define WM2200_EQR_B2_C_WIDTH 16 /* EQR_B2_C - [15:0] */ + +/* + * R2334 (0x91E) - EQR_9 + */ +#define WM2200_EQR_B2_PG_MASK 0xFFFF /* EQR_B2_PG - [15:0] */ +#define WM2200_EQR_B2_PG_SHIFT 0 /* EQR_B2_PG - [15:0] */ +#define WM2200_EQR_B2_PG_WIDTH 16 /* EQR_B2_PG - [15:0] */ + +/* + * R2335 (0x91F) - EQR_10 + */ +#define WM2200_EQR_B3_A_MASK 0xFFFF /* EQR_B3_A - [15:0] */ +#define WM2200_EQR_B3_A_SHIFT 0 /* EQR_B3_A - [15:0] */ +#define WM2200_EQR_B3_A_WIDTH 16 /* EQR_B3_A - [15:0] */ + +/* + * R2336 (0x920) - EQR_11 + */ +#define WM2200_EQR_B3_B_MASK 0xFFFF /* EQR_B3_B - [15:0] */ +#define WM2200_EQR_B3_B_SHIFT 0 /* EQR_B3_B - [15:0] */ +#define WM2200_EQR_B3_B_WIDTH 16 /* EQR_B3_B - [15:0] */ + +/* + * R2337 (0x921) - EQR_12 + */ +#define WM2200_EQR_B3_C_MASK 0xFFFF /* EQR_B3_C - [15:0] */ +#define WM2200_EQR_B3_C_SHIFT 0 /* EQR_B3_C - [15:0] */ +#define WM2200_EQR_B3_C_WIDTH 16 /* EQR_B3_C - [15:0] */ + +/* + * R2338 (0x922) - EQR_13 + */ +#define WM2200_EQR_B3_PG_MASK 0xFFFF /* EQR_B3_PG - [15:0] */ +#define WM2200_EQR_B3_PG_SHIFT 0 /* EQR_B3_PG - [15:0] */ +#define WM2200_EQR_B3_PG_WIDTH 16 /* EQR_B3_PG - [15:0] */ + +/* + * R2339 (0x923) - EQR_14 + */ +#define WM2200_EQR_B4_A_MASK 0xFFFF /* EQR_B4_A - [15:0] */ +#define WM2200_EQR_B4_A_SHIFT 0 /* EQR_B4_A - [15:0] */ +#define WM2200_EQR_B4_A_WIDTH 16 /* EQR_B4_A - [15:0] */ + +/* + * R2340 (0x924) - EQR_15 + */ +#define WM2200_EQR_B4_B_MASK 0xFFFF /* EQR_B4_B - [15:0] */ +#define WM2200_EQR_B4_B_SHIFT 0 /* EQR_B4_B - [15:0] */ +#define WM2200_EQR_B4_B_WIDTH 16 /* EQR_B4_B - [15:0] */ + +/* + * R2341 (0x925) - EQR_16 + */ +#define WM2200_EQR_B4_C_MASK 0xFFFF /* EQR_B4_C - [15:0] */ +#define WM2200_EQR_B4_C_SHIFT 0 /* EQR_B4_C - [15:0] */ +#define WM2200_EQR_B4_C_WIDTH 16 /* EQR_B4_C - [15:0] */ + +/* + * R2342 (0x926) - EQR_17 + */ +#define WM2200_EQR_B4_PG_MASK 0xFFFF /* EQR_B4_PG - [15:0] */ +#define WM2200_EQR_B4_PG_SHIFT 0 /* EQR_B4_PG - [15:0] */ +#define WM2200_EQR_B4_PG_WIDTH 16 /* EQR_B4_PG - [15:0] */ + +/* + * R2343 (0x927) - EQR_18 + */ +#define WM2200_EQR_B5_A_MASK 0xFFFF /* EQR_B5_A - [15:0] */ +#define WM2200_EQR_B5_A_SHIFT 0 /* EQR_B5_A - [15:0] */ +#define WM2200_EQR_B5_A_WIDTH 16 /* EQR_B5_A - [15:0] */ + +/* + * R2344 (0x928) - EQR_19 + */ +#define WM2200_EQR_B5_B_MASK 0xFFFF /* EQR_B5_B - [15:0] */ +#define WM2200_EQR_B5_B_SHIFT 0 /* EQR_B5_B - [15:0] */ +#define WM2200_EQR_B5_B_WIDTH 16 /* EQR_B5_B - [15:0] */ + +/* + * R2345 (0x929) - EQR_20 + */ +#define WM2200_EQR_B5_PG_MASK 0xFFFF /* EQR_B5_PG - [15:0] */ +#define WM2200_EQR_B5_PG_SHIFT 0 /* EQR_B5_PG - [15:0] */ +#define WM2200_EQR_B5_PG_WIDTH 16 /* EQR_B5_PG - [15:0] */ + +/* + * R2366 (0x93E) - HPLPF1_1 + */ +#define WM2200_LHPF1_MODE 0x0002 /* LHPF1_MODE */ +#define WM2200_LHPF1_MODE_MASK 0x0002 /* LHPF1_MODE */ +#define WM2200_LHPF1_MODE_SHIFT 1 /* LHPF1_MODE */ +#define WM2200_LHPF1_MODE_WIDTH 1 /* LHPF1_MODE */ +#define WM2200_LHPF1_ENA 0x0001 /* LHPF1_ENA */ +#define WM2200_LHPF1_ENA_MASK 0x0001 /* LHPF1_ENA */ +#define WM2200_LHPF1_ENA_SHIFT 0 /* LHPF1_ENA */ +#define WM2200_LHPF1_ENA_WIDTH 1 /* LHPF1_ENA */ + +/* + * R2367 (0x93F) - HPLPF1_2 + */ +#define WM2200_LHPF1_COEFF_MASK 0xFFFF /* LHPF1_COEFF - [15:0] */ +#define WM2200_LHPF1_COEFF_SHIFT 0 /* LHPF1_COEFF - [15:0] */ +#define WM2200_LHPF1_COEFF_WIDTH 16 /* LHPF1_COEFF - [15:0] */ + +/* + * R2370 (0x942) - HPLPF2_1 + */ +#define WM2200_LHPF2_MODE 0x0002 /* LHPF2_MODE */ +#define WM2200_LHPF2_MODE_MASK 0x0002 /* LHPF2_MODE */ +#define WM2200_LHPF2_MODE_SHIFT 1 /* LHPF2_MODE */ +#define WM2200_LHPF2_MODE_WIDTH 1 /* LHPF2_MODE */ +#define WM2200_LHPF2_ENA 0x0001 /* LHPF2_ENA */ +#define WM2200_LHPF2_ENA_MASK 0x0001 /* LHPF2_ENA */ +#define WM2200_LHPF2_ENA_SHIFT 0 /* LHPF2_ENA */ +#define WM2200_LHPF2_ENA_WIDTH 1 /* LHPF2_ENA */ + +/* + * R2371 (0x943) - HPLPF2_2 + */ +#define WM2200_LHPF2_COEFF_MASK 0xFFFF /* LHPF2_COEFF - [15:0] */ +#define WM2200_LHPF2_COEFF_SHIFT 0 /* LHPF2_COEFF - [15:0] */ +#define WM2200_LHPF2_COEFF_WIDTH 16 /* LHPF2_COEFF - [15:0] */ + +/* + * R2560 (0xA00) - DSP1 Control 1 + */ +#define WM2200_DSP1_RW_SEQUENCE_ENA 0x0001 /* DSP1_RW_SEQUENCE_ENA */ +#define WM2200_DSP1_RW_SEQUENCE_ENA_MASK 0x0001 /* DSP1_RW_SEQUENCE_ENA */ +#define WM2200_DSP1_RW_SEQUENCE_ENA_SHIFT 0 /* DSP1_RW_SEQUENCE_ENA */ +#define WM2200_DSP1_RW_SEQUENCE_ENA_WIDTH 1 /* DSP1_RW_SEQUENCE_ENA */ + +/* + * R2562 (0xA02) - DSP1 Control 2 + */ +#define WM2200_DSP1_PAGE_BASE_PM_0_MASK 0xFF00 /* DSP1_PAGE_BASE_PM - [15:8] */ +#define WM2200_DSP1_PAGE_BASE_PM_0_SHIFT 8 /* DSP1_PAGE_BASE_PM - [15:8] */ +#define WM2200_DSP1_PAGE_BASE_PM_0_WIDTH 8 /* DSP1_PAGE_BASE_PM - [15:8] */ + +/* + * R2563 (0xA03) - DSP1 Control 3 + */ +#define WM2200_DSP1_PAGE_BASE_DM_0_MASK 0xFF00 /* DSP1_PAGE_BASE_DM - [15:8] */ +#define WM2200_DSP1_PAGE_BASE_DM_0_SHIFT 8 /* DSP1_PAGE_BASE_DM - [15:8] */ +#define WM2200_DSP1_PAGE_BASE_DM_0_WIDTH 8 /* DSP1_PAGE_BASE_DM - [15:8] */ + +/* + * R2564 (0xA04) - DSP1 Control 4 + */ +#define WM2200_DSP1_PAGE_BASE_ZM_0_MASK 0xFF00 /* DSP1_PAGE_BASE_ZM - [15:8] */ +#define WM2200_DSP1_PAGE_BASE_ZM_0_SHIFT 8 /* DSP1_PAGE_BASE_ZM - [15:8] */ +#define WM2200_DSP1_PAGE_BASE_ZM_0_WIDTH 8 /* DSP1_PAGE_BASE_ZM - [15:8] */ + +/* + * R2566 (0xA06) - DSP1 Control 5 + */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_0_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_0_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_0_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */ + +/* + * R2567 (0xA07) - DSP1 Control 6 + */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_1_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_1_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_1_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */ + +/* + * R2568 (0xA08) - DSP1 Control 7 + */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_2_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_2_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_2_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */ + +/* + * R2569 (0xA09) - DSP1 Control 8 + */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_3_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_3_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_3_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */ + +/* + * R2570 (0xA0A) - DSP1 Control 9 + */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_4_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_4_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_4_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */ + +/* + * R2571 (0xA0B) - DSP1 Control 10 + */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_5_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_5_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_5_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */ + +/* + * R2572 (0xA0C) - DSP1 Control 11 + */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_6_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_6_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_6_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */ + +/* + * R2573 (0xA0D) - DSP1 Control 12 + */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_7_MASK 0x3FFF /* DSP1_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_7_SHIFT 0 /* DSP1_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_WDMA_BUFFER_7_WIDTH 14 /* DSP1_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */ + +/* + * R2575 (0xA0F) - DSP1 Control 13 + */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_0_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_0_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_0_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */ + +/* + * R2576 (0xA10) - DSP1 Control 14 + */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_1_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_1_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_1_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */ + +/* + * R2577 (0xA11) - DSP1 Control 15 + */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_2_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_2_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_2_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */ + +/* + * R2578 (0xA12) - DSP1 Control 16 + */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_3_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_3_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_3_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */ + +/* + * R2579 (0xA13) - DSP1 Control 17 + */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_4_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_4_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_4_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */ + +/* + * R2580 (0xA14) - DSP1 Control 18 + */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_5_MASK 0x3FFF /* DSP1_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_5_SHIFT 0 /* DSP1_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */ +#define WM2200_DSP1_START_ADDRESS_RDMA_BUFFER_5_WIDTH 14 /* DSP1_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */ + +/* + * R2582 (0xA16) - DSP1 Control 19 + */ +#define WM2200_DSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ +#define WM2200_DSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ +#define WM2200_DSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ + +/* + * R2583 (0xA17) - DSP1 Control 20 + */ +#define WM2200_DSP1_WDMA_CHANNEL_ENABLE_MASK 0x00FF /* DSP1_WDMA_CHANNEL_ENABLE - [7:0] */ +#define WM2200_DSP1_WDMA_CHANNEL_ENABLE_SHIFT 0 /* DSP1_WDMA_CHANNEL_ENABLE - [7:0] */ +#define WM2200_DSP1_WDMA_CHANNEL_ENABLE_WIDTH 8 /* DSP1_WDMA_CHANNEL_ENABLE - [7:0] */ + +/* + * R2584 (0xA18) - DSP1 Control 21 + */ +#define WM2200_DSP1_RDMA_CHANNEL_ENABLE_MASK 0x003F /* DSP1_RDMA_CHANNEL_ENABLE - [5:0] */ +#define WM2200_DSP1_RDMA_CHANNEL_ENABLE_SHIFT 0 /* DSP1_RDMA_CHANNEL_ENABLE - [5:0] */ +#define WM2200_DSP1_RDMA_CHANNEL_ENABLE_WIDTH 6 /* DSP1_RDMA_CHANNEL_ENABLE - [5:0] */ + +/* + * R2586 (0xA1A) - DSP1 Control 22 + */ +#define WM2200_DSP1_DM_SIZE_MASK 0xFFFF /* DSP1_DM_SIZE - [15:0] */ +#define WM2200_DSP1_DM_SIZE_SHIFT 0 /* DSP1_DM_SIZE - [15:0] */ +#define WM2200_DSP1_DM_SIZE_WIDTH 16 /* DSP1_DM_SIZE - [15:0] */ + +/* + * R2587 (0xA1B) - DSP1 Control 23 + */ +#define WM2200_DSP1_PM_SIZE_MASK 0xFFFF /* DSP1_PM_SIZE - [15:0] */ +#define WM2200_DSP1_PM_SIZE_SHIFT 0 /* DSP1_PM_SIZE - [15:0] */ +#define WM2200_DSP1_PM_SIZE_WIDTH 16 /* DSP1_PM_SIZE - [15:0] */ + +/* + * R2588 (0xA1C) - DSP1 Control 24 + */ +#define WM2200_DSP1_ZM_SIZE_MASK 0xFFFF /* DSP1_ZM_SIZE - [15:0] */ +#define WM2200_DSP1_ZM_SIZE_SHIFT 0 /* DSP1_ZM_SIZE - [15:0] */ +#define WM2200_DSP1_ZM_SIZE_WIDTH 16 /* DSP1_ZM_SIZE - [15:0] */ + +/* + * R2590 (0xA1E) - DSP1 Control 25 + */ +#define WM2200_DSP1_PING_FULL 0x8000 /* DSP1_PING_FULL */ +#define WM2200_DSP1_PING_FULL_MASK 0x8000 /* DSP1_PING_FULL */ +#define WM2200_DSP1_PING_FULL_SHIFT 15 /* DSP1_PING_FULL */ +#define WM2200_DSP1_PING_FULL_WIDTH 1 /* DSP1_PING_FULL */ +#define WM2200_DSP1_PONG_FULL 0x4000 /* DSP1_PONG_FULL */ +#define WM2200_DSP1_PONG_FULL_MASK 0x4000 /* DSP1_PONG_FULL */ +#define WM2200_DSP1_PONG_FULL_SHIFT 14 /* DSP1_PONG_FULL */ +#define WM2200_DSP1_PONG_FULL_WIDTH 1 /* DSP1_PONG_FULL */ +#define WM2200_DSP1_WDMA_ACTIVE_CHANNELS_MASK 0x00FF /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */ +#define WM2200_DSP1_WDMA_ACTIVE_CHANNELS_SHIFT 0 /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */ +#define WM2200_DSP1_WDMA_ACTIVE_CHANNELS_WIDTH 8 /* DSP1_WDMA_ACTIVE_CHANNELS - [7:0] */ + +/* + * R2592 (0xA20) - DSP1 Control 26 + */ +#define WM2200_DSP1_SCRATCH_0_MASK 0xFFFF /* DSP1_SCRATCH_0 - [15:0] */ +#define WM2200_DSP1_SCRATCH_0_SHIFT 0 /* DSP1_SCRATCH_0 - [15:0] */ +#define WM2200_DSP1_SCRATCH_0_WIDTH 16 /* DSP1_SCRATCH_0 - [15:0] */ + +/* + * R2593 (0xA21) - DSP1 Control 27 + */ +#define WM2200_DSP1_SCRATCH_1_MASK 0xFFFF /* DSP1_SCRATCH_1 - [15:0] */ +#define WM2200_DSP1_SCRATCH_1_SHIFT 0 /* DSP1_SCRATCH_1 - [15:0] */ +#define WM2200_DSP1_SCRATCH_1_WIDTH 16 /* DSP1_SCRATCH_1 - [15:0] */ + +/* + * R2594 (0xA22) - DSP1 Control 28 + */ +#define WM2200_DSP1_SCRATCH_2_MASK 0xFFFF /* DSP1_SCRATCH_2 - [15:0] */ +#define WM2200_DSP1_SCRATCH_2_SHIFT 0 /* DSP1_SCRATCH_2 - [15:0] */ +#define WM2200_DSP1_SCRATCH_2_WIDTH 16 /* DSP1_SCRATCH_2 - [15:0] */ + +/* + * R2595 (0xA23) - DSP1 Control 29 + */ +#define WM2200_DSP1_SCRATCH_3_MASK 0xFFFF /* DSP1_SCRATCH_3 - [15:0] */ +#define WM2200_DSP1_SCRATCH_3_SHIFT 0 /* DSP1_SCRATCH_3 - [15:0] */ +#define WM2200_DSP1_SCRATCH_3_WIDTH 16 /* DSP1_SCRATCH_3 - [15:0] */ + +/* + * R2596 (0xA24) - DSP1 Control 30 + */ +#define WM2200_DSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ +#define WM2200_DSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ +#define WM2200_DSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ +#define WM2200_DSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ +#define WM2200_DSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ +#define WM2200_DSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ +#define WM2200_DSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ +#define WM2200_DSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ +#define WM2200_DSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ +#define WM2200_DSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ +#define WM2200_DSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ +#define WM2200_DSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ +#define WM2200_DSP1_START 0x0001 /* DSP1_START */ +#define WM2200_DSP1_START_MASK 0x0001 /* DSP1_START */ +#define WM2200_DSP1_START_SHIFT 0 /* DSP1_START */ +#define WM2200_DSP1_START_WIDTH 1 /* DSP1_START */ + +/* + * R2598 (0xA26) - DSP1 Control 31 + */ +#define WM2200_DSP1_CLK_RATE_MASK 0x0018 /* DSP1_CLK_RATE - [4:3] */ +#define WM2200_DSP1_CLK_RATE_SHIFT 3 /* DSP1_CLK_RATE - [4:3] */ +#define WM2200_DSP1_CLK_RATE_WIDTH 2 /* DSP1_CLK_RATE - [4:3] */ +#define WM2200_DSP1_CLK_AVAIL 0x0004 /* DSP1_CLK_AVAIL */ +#define WM2200_DSP1_CLK_AVAIL_MASK 0x0004 /* DSP1_CLK_AVAIL */ +#define WM2200_DSP1_CLK_AVAIL_SHIFT 2 /* DSP1_CLK_AVAIL */ +#define WM2200_DSP1_CLK_AVAIL_WIDTH 1 /* DSP1_CLK_AVAIL */ +#define WM2200_DSP1_CLK_REQ_MASK 0x0003 /* DSP1_CLK_REQ - [1:0] */ +#define WM2200_DSP1_CLK_REQ_SHIFT 0 /* DSP1_CLK_REQ - [1:0] */ +#define WM2200_DSP1_CLK_REQ_WIDTH 2 /* DSP1_CLK_REQ - [1:0] */ + +/* + * R2816 (0xB00) - DSP2 Control 1 + */ +#define WM2200_DSP2_RW_SEQUENCE_ENA 0x0001 /* DSP2_RW_SEQUENCE_ENA */ +#define WM2200_DSP2_RW_SEQUENCE_ENA_MASK 0x0001 /* DSP2_RW_SEQUENCE_ENA */ +#define WM2200_DSP2_RW_SEQUENCE_ENA_SHIFT 0 /* DSP2_RW_SEQUENCE_ENA */ +#define WM2200_DSP2_RW_SEQUENCE_ENA_WIDTH 1 /* DSP2_RW_SEQUENCE_ENA */ + +/* + * R2818 (0xB02) - DSP2 Control 2 + */ +#define WM2200_DSP2_PAGE_BASE_PM_0_MASK 0xFF00 /* DSP2_PAGE_BASE_PM - [15:8] */ +#define WM2200_DSP2_PAGE_BASE_PM_0_SHIFT 8 /* DSP2_PAGE_BASE_PM - [15:8] */ +#define WM2200_DSP2_PAGE_BASE_PM_0_WIDTH 8 /* DSP2_PAGE_BASE_PM - [15:8] */ + +/* + * R2819 (0xB03) - DSP2 Control 3 + */ +#define WM2200_DSP2_PAGE_BASE_DM_0_MASK 0xFF00 /* DSP2_PAGE_BASE_DM - [15:8] */ +#define WM2200_DSP2_PAGE_BASE_DM_0_SHIFT 8 /* DSP2_PAGE_BASE_DM - [15:8] */ +#define WM2200_DSP2_PAGE_BASE_DM_0_WIDTH 8 /* DSP2_PAGE_BASE_DM - [15:8] */ + +/* + * R2820 (0xB04) - DSP2 Control 4 + */ +#define WM2200_DSP2_PAGE_BASE_ZM_0_MASK 0xFF00 /* DSP2_PAGE_BASE_ZM - [15:8] */ +#define WM2200_DSP2_PAGE_BASE_ZM_0_SHIFT 8 /* DSP2_PAGE_BASE_ZM - [15:8] */ +#define WM2200_DSP2_PAGE_BASE_ZM_0_WIDTH 8 /* DSP2_PAGE_BASE_ZM - [15:8] */ + +/* + * R2822 (0xB06) - DSP2 Control 5 + */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_0_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_0_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_0_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_0 - [13:0] */ + +/* + * R2823 (0xB07) - DSP2 Control 6 + */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_1_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_1_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_1_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_1 - [13:0] */ + +/* + * R2824 (0xB08) - DSP2 Control 7 + */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_2_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_2_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_2_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_2 - [13:0] */ + +/* + * R2825 (0xB09) - DSP2 Control 8 + */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_3_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_3_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_3_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_3 - [13:0] */ + +/* + * R2826 (0xB0A) - DSP2 Control 9 + */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_4_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_4_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_4_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_4 - [13:0] */ + +/* + * R2827 (0xB0B) - DSP2 Control 10 + */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_5_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_5_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_5_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_5 - [13:0] */ + +/* + * R2828 (0xB0C) - DSP2 Control 11 + */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_6_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_6_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_6_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_6 - [13:0] */ + +/* + * R2829 (0xB0D) - DSP2 Control 12 + */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_7_MASK 0x3FFF /* DSP2_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_7_SHIFT 0 /* DSP2_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_WDMA_BUFFER_7_WIDTH 14 /* DSP2_START_ADDRESS_WDMA_BUFFER_7 - [13:0] */ + +/* + * R2831 (0xB0F) - DSP2 Control 13 + */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_0_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_0_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_0_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_0 - [13:0] */ + +/* + * R2832 (0xB10) - DSP2 Control 14 + */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_1_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_1_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_1_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_1 - [13:0] */ + +/* + * R2833 (0xB11) - DSP2 Control 15 + */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_2_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_2_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_2_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_2 - [13:0] */ + +/* + * R2834 (0xB12) - DSP2 Control 16 + */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_3_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_3_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_3_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_3 - [13:0] */ + +/* + * R2835 (0xB13) - DSP2 Control 17 + */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_4_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_4_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_4_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_4 - [13:0] */ + +/* + * R2836 (0xB14) - DSP2 Control 18 + */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_5_MASK 0x3FFF /* DSP2_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_5_SHIFT 0 /* DSP2_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */ +#define WM2200_DSP2_START_ADDRESS_RDMA_BUFFER_5_WIDTH 14 /* DSP2_START_ADDRESS_RDMA_BUFFER_5 - [13:0] */ + +/* + * R2838 (0xB16) - DSP2 Control 19 + */ +#define WM2200_DSP2_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP2_WDMA_BUFFER_LENGTH - [7:0] */ +#define WM2200_DSP2_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP2_WDMA_BUFFER_LENGTH - [7:0] */ +#define WM2200_DSP2_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP2_WDMA_BUFFER_LENGTH - [7:0] */ + +/* + * R2839 (0xB17) - DSP2 Control 20 + */ +#define WM2200_DSP2_WDMA_CHANNEL_ENABLE_MASK 0x00FF /* DSP2_WDMA_CHANNEL_ENABLE - [7:0] */ +#define WM2200_DSP2_WDMA_CHANNEL_ENABLE_SHIFT 0 /* DSP2_WDMA_CHANNEL_ENABLE - [7:0] */ +#define WM2200_DSP2_WDMA_CHANNEL_ENABLE_WIDTH 8 /* DSP2_WDMA_CHANNEL_ENABLE - [7:0] */ + +/* + * R2840 (0xB18) - DSP2 Control 21 + */ +#define WM2200_DSP2_RDMA_CHANNEL_ENABLE_MASK 0x003F /* DSP2_RDMA_CHANNEL_ENABLE - [5:0] */ +#define WM2200_DSP2_RDMA_CHANNEL_ENABLE_SHIFT 0 /* DSP2_RDMA_CHANNEL_ENABLE - [5:0] */ +#define WM2200_DSP2_RDMA_CHANNEL_ENABLE_WIDTH 6 /* DSP2_RDMA_CHANNEL_ENABLE - [5:0] */ + +/* + * R2842 (0xB1A) - DSP2 Control 22 + */ +#define WM2200_DSP2_DM_SIZE_MASK 0xFFFF /* DSP2_DM_SIZE - [15:0] */ +#define WM2200_DSP2_DM_SIZE_SHIFT 0 /* DSP2_DM_SIZE - [15:0] */ +#define WM2200_DSP2_DM_SIZE_WIDTH 16 /* DSP2_DM_SIZE - [15:0] */ + +/* + * R2843 (0xB1B) - DSP2 Control 23 + */ +#define WM2200_DSP2_PM_SIZE_MASK 0xFFFF /* DSP2_PM_SIZE - [15:0] */ +#define WM2200_DSP2_PM_SIZE_SHIFT 0 /* DSP2_PM_SIZE - [15:0] */ +#define WM2200_DSP2_PM_SIZE_WIDTH 16 /* DSP2_PM_SIZE - [15:0] */ + +/* + * R2844 (0xB1C) - DSP2 Control 24 + */ +#define WM2200_DSP2_ZM_SIZE_MASK 0xFFFF /* DSP2_ZM_SIZE - [15:0] */ +#define WM2200_DSP2_ZM_SIZE_SHIFT 0 /* DSP2_ZM_SIZE - [15:0] */ +#define WM2200_DSP2_ZM_SIZE_WIDTH 16 /* DSP2_ZM_SIZE - [15:0] */ + +/* + * R2846 (0xB1E) - DSP2 Control 25 + */ +#define WM2200_DSP2_PING_FULL 0x8000 /* DSP2_PING_FULL */ +#define WM2200_DSP2_PING_FULL_MASK 0x8000 /* DSP2_PING_FULL */ +#define WM2200_DSP2_PING_FULL_SHIFT 15 /* DSP2_PING_FULL */ +#define WM2200_DSP2_PING_FULL_WIDTH 1 /* DSP2_PING_FULL */ +#define WM2200_DSP2_PONG_FULL 0x4000 /* DSP2_PONG_FULL */ +#define WM2200_DSP2_PONG_FULL_MASK 0x4000 /* DSP2_PONG_FULL */ +#define WM2200_DSP2_PONG_FULL_SHIFT 14 /* DSP2_PONG_FULL */ +#define WM2200_DSP2_PONG_FULL_WIDTH 1 /* DSP2_PONG_FULL */ +#define WM2200_DSP2_WDMA_ACTIVE_CHANNELS_MASK 0x00FF /* DSP2_WDMA_ACTIVE_CHANNELS - [7:0] */ +#define WM2200_DSP2_WDMA_ACTIVE_CHANNELS_SHIFT 0 /* DSP2_WDMA_ACTIVE_CHANNELS - [7:0] */ +#define WM2200_DSP2_WDMA_ACTIVE_CHANNELS_WIDTH 8 /* DSP2_WDMA_ACTIVE_CHANNELS - [7:0] */ + +/* + * R2848 (0xB20) - DSP2 Control 26 + */ +#define WM2200_DSP2_SCRATCH_0_MASK 0xFFFF /* DSP2_SCRATCH_0 - [15:0] */ +#define WM2200_DSP2_SCRATCH_0_SHIFT 0 /* DSP2_SCRATCH_0 - [15:0] */ +#define WM2200_DSP2_SCRATCH_0_WIDTH 16 /* DSP2_SCRATCH_0 - [15:0] */ + +/* + * R2849 (0xB21) - DSP2 Control 27 + */ +#define WM2200_DSP2_SCRATCH_1_MASK 0xFFFF /* DSP2_SCRATCH_1 - [15:0] */ +#define WM2200_DSP2_SCRATCH_1_SHIFT 0 /* DSP2_SCRATCH_1 - [15:0] */ +#define WM2200_DSP2_SCRATCH_1_WIDTH 16 /* DSP2_SCRATCH_1 - [15:0] */ + +/* + * R2850 (0xB22) - DSP2 Control 28 + */ +#define WM2200_DSP2_SCRATCH_2_MASK 0xFFFF /* DSP2_SCRATCH_2 - [15:0] */ +#define WM2200_DSP2_SCRATCH_2_SHIFT 0 /* DSP2_SCRATCH_2 - [15:0] */ +#define WM2200_DSP2_SCRATCH_2_WIDTH 16 /* DSP2_SCRATCH_2 - [15:0] */ + +/* + * R2851 (0xB23) - DSP2 Control 29 + */ +#define WM2200_DSP2_SCRATCH_3_MASK 0xFFFF /* DSP2_SCRATCH_3 - [15:0] */ +#define WM2200_DSP2_SCRATCH_3_SHIFT 0 /* DSP2_SCRATCH_3 - [15:0] */ +#define WM2200_DSP2_SCRATCH_3_WIDTH 16 /* DSP2_SCRATCH_3 - [15:0] */ + +/* + * R2852 (0xB24) - DSP2 Control 30 + */ +#define WM2200_DSP2_DBG_CLK_ENA 0x0008 /* DSP2_DBG_CLK_ENA */ +#define WM2200_DSP2_DBG_CLK_ENA_MASK 0x0008 /* DSP2_DBG_CLK_ENA */ +#define WM2200_DSP2_DBG_CLK_ENA_SHIFT 3 /* DSP2_DBG_CLK_ENA */ +#define WM2200_DSP2_DBG_CLK_ENA_WIDTH 1 /* DSP2_DBG_CLK_ENA */ +#define WM2200_DSP2_SYS_ENA 0x0004 /* DSP2_SYS_ENA */ +#define WM2200_DSP2_SYS_ENA_MASK 0x0004 /* DSP2_SYS_ENA */ +#define WM2200_DSP2_SYS_ENA_SHIFT 2 /* DSP2_SYS_ENA */ +#define WM2200_DSP2_SYS_ENA_WIDTH 1 /* DSP2_SYS_ENA */ +#define WM2200_DSP2_CORE_ENA 0x0002 /* DSP2_CORE_ENA */ +#define WM2200_DSP2_CORE_ENA_MASK 0x0002 /* DSP2_CORE_ENA */ +#define WM2200_DSP2_CORE_ENA_SHIFT 1 /* DSP2_CORE_ENA */ +#define WM2200_DSP2_CORE_ENA_WIDTH 1 /* DSP2_CORE_ENA */ +#define WM2200_DSP2_START 0x0001 /* DSP2_START */ +#define WM2200_DSP2_START_MASK 0x0001 /* DSP2_START */ +#define WM2200_DSP2_START_SHIFT 0 /* DSP2_START */ +#define WM2200_DSP2_START_WIDTH 1 /* DSP2_START */ + +/* + * R2854 (0xB26) - DSP2 Control 31 + */ +#define WM2200_DSP2_CLK_RATE_MASK 0x0018 /* DSP2_CLK_RATE - [4:3] */ +#define WM2200_DSP2_CLK_RATE_SHIFT 3 /* DSP2_CLK_RATE - [4:3] */ +#define WM2200_DSP2_CLK_RATE_WIDTH 2 /* DSP2_CLK_RATE - [4:3] */ +#define WM2200_DSP2_CLK_AVAIL 0x0004 /* DSP2_CLK_AVAIL */ +#define WM2200_DSP2_CLK_AVAIL_MASK 0x0004 /* DSP2_CLK_AVAIL */ +#define WM2200_DSP2_CLK_AVAIL_SHIFT 2 /* DSP2_CLK_AVAIL */ +#define WM2200_DSP2_CLK_AVAIL_WIDTH 1 /* DSP2_CLK_AVAIL */ +#define WM2200_DSP2_CLK_REQ_MASK 0x0003 /* DSP2_CLK_REQ - [1:0] */ +#define WM2200_DSP2_CLK_REQ_SHIFT 0 /* DSP2_CLK_REQ - [1:0] */ +#define WM2200_DSP2_CLK_REQ_WIDTH 2 /* DSP2_CLK_REQ - [1:0] */ + +#endif diff --git a/sound/soc/codecs/wm5100-tables.c b/sound/soc/codecs/wm5100-tables.c new file mode 100644 index 0000000000..9a6ce8f2c9 --- /dev/null +++ b/sound/soc/codecs/wm5100-tables.c @@ -0,0 +1,1481 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm5100-tables.c -- WM5100 ALSA SoC Audio driver data + * + * Copyright 2011-2 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include "wm5100.h" + +bool wm5100_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM5100_SOFTWARE_RESET: + case WM5100_DEVICE_REVISION: + case WM5100_FX_CTRL: + case WM5100_INTERRUPT_STATUS_1: + case WM5100_INTERRUPT_STATUS_2: + case WM5100_INTERRUPT_STATUS_3: + case WM5100_INTERRUPT_STATUS_4: + case WM5100_INTERRUPT_RAW_STATUS_2: + case WM5100_INTERRUPT_RAW_STATUS_3: + case WM5100_INTERRUPT_RAW_STATUS_4: + case WM5100_OUTPUT_STATUS_1: + case WM5100_OUTPUT_STATUS_2: + case WM5100_INPUT_ENABLES_STATUS: + case WM5100_MIC_DETECT_3: + return true; + default: + if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) || + (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) || + (reg >= WM5100_DSP1_DM_0 && reg <= WM5100_DSP1_DM_511) || + (reg >= WM5100_DSP2_PM_0 && reg <= WM5100_DSP2_PM_1535) || + (reg >= WM5100_DSP2_ZM_0 && reg <= WM5100_DSP2_ZM_2047) || + (reg >= WM5100_DSP2_DM_0 && reg <= WM5100_DSP2_DM_511) || + (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) || + (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) || + (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511)) + return true; + else + return false; + } +} + +bool wm5100_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM5100_SOFTWARE_RESET: + case WM5100_DEVICE_REVISION: + case WM5100_CTRL_IF_1: + case WM5100_TONE_GENERATOR_1: + case WM5100_PWM_DRIVE_1: + case WM5100_PWM_DRIVE_2: + case WM5100_PWM_DRIVE_3: + case WM5100_CLOCKING_1: + case WM5100_CLOCKING_3: + case WM5100_CLOCKING_4: + case WM5100_CLOCKING_5: + case WM5100_CLOCKING_6: + case WM5100_CLOCKING_7: + case WM5100_CLOCKING_8: + case WM5100_ASRC_ENABLE: + case WM5100_ASRC_STATUS: + case WM5100_ASRC_RATE1: + case WM5100_ISRC_1_CTRL_1: + case WM5100_ISRC_1_CTRL_2: + case WM5100_ISRC_2_CTRL1: + case WM5100_ISRC_2_CTRL_2: + case WM5100_FLL1_CONTROL_1: + case WM5100_FLL1_CONTROL_2: + case WM5100_FLL1_CONTROL_3: + case WM5100_FLL1_CONTROL_5: + case WM5100_FLL1_CONTROL_6: + case WM5100_FLL1_EFS_1: + case WM5100_FLL2_CONTROL_1: + case WM5100_FLL2_CONTROL_2: + case WM5100_FLL2_CONTROL_3: + case WM5100_FLL2_CONTROL_5: + case WM5100_FLL2_CONTROL_6: + case WM5100_FLL2_EFS_1: + case WM5100_MIC_CHARGE_PUMP_1: + case WM5100_MIC_CHARGE_PUMP_2: + case WM5100_HP_CHARGE_PUMP_1: + case WM5100_LDO1_CONTROL: + case WM5100_MIC_BIAS_CTRL_1: + case WM5100_MIC_BIAS_CTRL_2: + case WM5100_MIC_BIAS_CTRL_3: + case WM5100_ACCESSORY_DETECT_MODE_1: + case WM5100_HEADPHONE_DETECT_1: + case WM5100_HEADPHONE_DETECT_2: + case WM5100_MIC_DETECT_1: + case WM5100_MIC_DETECT_2: + case WM5100_MIC_DETECT_3: + case WM5100_MISC_CONTROL: + case WM5100_INPUT_ENABLES: + case WM5100_INPUT_ENABLES_STATUS: + case WM5100_IN1L_CONTROL: + case WM5100_IN1R_CONTROL: + case WM5100_IN2L_CONTROL: + case WM5100_IN2R_CONTROL: + case WM5100_IN3L_CONTROL: + case WM5100_IN3R_CONTROL: + case WM5100_IN4L_CONTROL: + case WM5100_IN4R_CONTROL: + case WM5100_RXANC_SRC: + case WM5100_INPUT_VOLUME_RAMP: + case WM5100_ADC_DIGITAL_VOLUME_1L: + case WM5100_ADC_DIGITAL_VOLUME_1R: + case WM5100_ADC_DIGITAL_VOLUME_2L: + case WM5100_ADC_DIGITAL_VOLUME_2R: + case WM5100_ADC_DIGITAL_VOLUME_3L: + case WM5100_ADC_DIGITAL_VOLUME_3R: + case WM5100_ADC_DIGITAL_VOLUME_4L: + case WM5100_ADC_DIGITAL_VOLUME_4R: + case WM5100_OUTPUT_ENABLES_2: + case WM5100_OUTPUT_STATUS_1: + case WM5100_OUTPUT_STATUS_2: + case WM5100_CHANNEL_ENABLES_1: + case WM5100_OUT_VOLUME_1L: + case WM5100_OUT_VOLUME_1R: + case WM5100_DAC_VOLUME_LIMIT_1L: + case WM5100_DAC_VOLUME_LIMIT_1R: + case WM5100_OUT_VOLUME_2L: + case WM5100_OUT_VOLUME_2R: + case WM5100_DAC_VOLUME_LIMIT_2L: + case WM5100_DAC_VOLUME_LIMIT_2R: + case WM5100_OUT_VOLUME_3L: + case WM5100_OUT_VOLUME_3R: + case WM5100_DAC_VOLUME_LIMIT_3L: + case WM5100_DAC_VOLUME_LIMIT_3R: + case WM5100_OUT_VOLUME_4L: + case WM5100_OUT_VOLUME_4R: + case WM5100_DAC_VOLUME_LIMIT_5L: + case WM5100_DAC_VOLUME_LIMIT_5R: + case WM5100_DAC_VOLUME_LIMIT_6L: + case WM5100_DAC_VOLUME_LIMIT_6R: + case WM5100_DAC_AEC_CONTROL_1: + case WM5100_OUTPUT_VOLUME_RAMP: + case WM5100_DAC_DIGITAL_VOLUME_1L: + case WM5100_DAC_DIGITAL_VOLUME_1R: + case WM5100_DAC_DIGITAL_VOLUME_2L: + case WM5100_DAC_DIGITAL_VOLUME_2R: + case WM5100_DAC_DIGITAL_VOLUME_3L: + case WM5100_DAC_DIGITAL_VOLUME_3R: + case WM5100_DAC_DIGITAL_VOLUME_4L: + case WM5100_DAC_DIGITAL_VOLUME_4R: + case WM5100_DAC_DIGITAL_VOLUME_5L: + case WM5100_DAC_DIGITAL_VOLUME_5R: + case WM5100_DAC_DIGITAL_VOLUME_6L: + case WM5100_DAC_DIGITAL_VOLUME_6R: + case WM5100_PDM_SPK1_CTRL_1: + case WM5100_PDM_SPK1_CTRL_2: + case WM5100_PDM_SPK2_CTRL_1: + case WM5100_PDM_SPK2_CTRL_2: + case WM5100_AUDIO_IF_1_1: + case WM5100_AUDIO_IF_1_2: + case WM5100_AUDIO_IF_1_3: + case WM5100_AUDIO_IF_1_4: + case WM5100_AUDIO_IF_1_5: + case WM5100_AUDIO_IF_1_6: + case WM5100_AUDIO_IF_1_7: + case WM5100_AUDIO_IF_1_8: + case WM5100_AUDIO_IF_1_9: + case WM5100_AUDIO_IF_1_10: + case WM5100_AUDIO_IF_1_11: + case WM5100_AUDIO_IF_1_12: + case WM5100_AUDIO_IF_1_13: + case WM5100_AUDIO_IF_1_14: + case WM5100_AUDIO_IF_1_15: + case WM5100_AUDIO_IF_1_16: + case WM5100_AUDIO_IF_1_17: + case WM5100_AUDIO_IF_1_18: + case WM5100_AUDIO_IF_1_19: + case WM5100_AUDIO_IF_1_20: + case WM5100_AUDIO_IF_1_21: + case WM5100_AUDIO_IF_1_22: + case WM5100_AUDIO_IF_1_23: + case WM5100_AUDIO_IF_1_24: + case WM5100_AUDIO_IF_1_25: + case WM5100_AUDIO_IF_1_26: + case WM5100_AUDIO_IF_1_27: + case WM5100_AUDIO_IF_2_1: + case WM5100_AUDIO_IF_2_2: + case WM5100_AUDIO_IF_2_3: + case WM5100_AUDIO_IF_2_4: + case WM5100_AUDIO_IF_2_5: + case WM5100_AUDIO_IF_2_6: + case WM5100_AUDIO_IF_2_7: + case WM5100_AUDIO_IF_2_8: + case WM5100_AUDIO_IF_2_9: + case WM5100_AUDIO_IF_2_10: + case WM5100_AUDIO_IF_2_11: + case WM5100_AUDIO_IF_2_18: + case WM5100_AUDIO_IF_2_19: + case WM5100_AUDIO_IF_2_26: + case WM5100_AUDIO_IF_2_27: + case WM5100_AUDIO_IF_3_1: + case WM5100_AUDIO_IF_3_2: + case WM5100_AUDIO_IF_3_3: + case WM5100_AUDIO_IF_3_4: + case WM5100_AUDIO_IF_3_5: + case WM5100_AUDIO_IF_3_6: + case WM5100_AUDIO_IF_3_7: + case WM5100_AUDIO_IF_3_8: + case WM5100_AUDIO_IF_3_9: + case WM5100_AUDIO_IF_3_10: + case WM5100_AUDIO_IF_3_11: + case WM5100_AUDIO_IF_3_18: + case WM5100_AUDIO_IF_3_19: + case WM5100_AUDIO_IF_3_26: + case WM5100_AUDIO_IF_3_27: + case WM5100_PWM1MIX_INPUT_1_SOURCE: + case WM5100_PWM1MIX_INPUT_1_VOLUME: + case WM5100_PWM1MIX_INPUT_2_SOURCE: + case WM5100_PWM1MIX_INPUT_2_VOLUME: + case WM5100_PWM1MIX_INPUT_3_SOURCE: + case WM5100_PWM1MIX_INPUT_3_VOLUME: + case WM5100_PWM1MIX_INPUT_4_SOURCE: + case WM5100_PWM1MIX_INPUT_4_VOLUME: + case WM5100_PWM2MIX_INPUT_1_SOURCE: + case WM5100_PWM2MIX_INPUT_1_VOLUME: + case WM5100_PWM2MIX_INPUT_2_SOURCE: + case WM5100_PWM2MIX_INPUT_2_VOLUME: + case WM5100_PWM2MIX_INPUT_3_SOURCE: + case WM5100_PWM2MIX_INPUT_3_VOLUME: + case WM5100_PWM2MIX_INPUT_4_SOURCE: + case WM5100_PWM2MIX_INPUT_4_VOLUME: + case WM5100_OUT1LMIX_INPUT_1_SOURCE: + case WM5100_OUT1LMIX_INPUT_1_VOLUME: + case WM5100_OUT1LMIX_INPUT_2_SOURCE: + case WM5100_OUT1LMIX_INPUT_2_VOLUME: + case WM5100_OUT1LMIX_INPUT_3_SOURCE: + case WM5100_OUT1LMIX_INPUT_3_VOLUME: + case WM5100_OUT1LMIX_INPUT_4_SOURCE: + case WM5100_OUT1LMIX_INPUT_4_VOLUME: + case WM5100_OUT1RMIX_INPUT_1_SOURCE: + case WM5100_OUT1RMIX_INPUT_1_VOLUME: + case WM5100_OUT1RMIX_INPUT_2_SOURCE: + case WM5100_OUT1RMIX_INPUT_2_VOLUME: + case WM5100_OUT1RMIX_INPUT_3_SOURCE: + case WM5100_OUT1RMIX_INPUT_3_VOLUME: + case WM5100_OUT1RMIX_INPUT_4_SOURCE: + case WM5100_OUT1RMIX_INPUT_4_VOLUME: + case WM5100_OUT2LMIX_INPUT_1_SOURCE: + case WM5100_OUT2LMIX_INPUT_1_VOLUME: + case WM5100_OUT2LMIX_INPUT_2_SOURCE: + case WM5100_OUT2LMIX_INPUT_2_VOLUME: + case WM5100_OUT2LMIX_INPUT_3_SOURCE: + case WM5100_OUT2LMIX_INPUT_3_VOLUME: + case WM5100_OUT2LMIX_INPUT_4_SOURCE: + case WM5100_OUT2LMIX_INPUT_4_VOLUME: + case WM5100_OUT2RMIX_INPUT_1_SOURCE: + case WM5100_OUT2RMIX_INPUT_1_VOLUME: + case WM5100_OUT2RMIX_INPUT_2_SOURCE: + case WM5100_OUT2RMIX_INPUT_2_VOLUME: + case WM5100_OUT2RMIX_INPUT_3_SOURCE: + case WM5100_OUT2RMIX_INPUT_3_VOLUME: + case WM5100_OUT2RMIX_INPUT_4_SOURCE: + case WM5100_OUT2RMIX_INPUT_4_VOLUME: + case WM5100_OUT3LMIX_INPUT_1_SOURCE: + case WM5100_OUT3LMIX_INPUT_1_VOLUME: + case WM5100_OUT3LMIX_INPUT_2_SOURCE: + case WM5100_OUT3LMIX_INPUT_2_VOLUME: + case WM5100_OUT3LMIX_INPUT_3_SOURCE: + case WM5100_OUT3LMIX_INPUT_3_VOLUME: + case WM5100_OUT3LMIX_INPUT_4_SOURCE: + case WM5100_OUT3LMIX_INPUT_4_VOLUME: + case WM5100_OUT3RMIX_INPUT_1_SOURCE: + case WM5100_OUT3RMIX_INPUT_1_VOLUME: + case WM5100_OUT3RMIX_INPUT_2_SOURCE: + case WM5100_OUT3RMIX_INPUT_2_VOLUME: + case WM5100_OUT3RMIX_INPUT_3_SOURCE: + case WM5100_OUT3RMIX_INPUT_3_VOLUME: + case WM5100_OUT3RMIX_INPUT_4_SOURCE: + case WM5100_OUT3RMIX_INPUT_4_VOLUME: + case WM5100_OUT4LMIX_INPUT_1_SOURCE: + case WM5100_OUT4LMIX_INPUT_1_VOLUME: + case WM5100_OUT4LMIX_INPUT_2_SOURCE: + case WM5100_OUT4LMIX_INPUT_2_VOLUME: + case WM5100_OUT4LMIX_INPUT_3_SOURCE: + case WM5100_OUT4LMIX_INPUT_3_VOLUME: + case WM5100_OUT4LMIX_INPUT_4_SOURCE: + case WM5100_OUT4LMIX_INPUT_4_VOLUME: + case WM5100_OUT4RMIX_INPUT_1_SOURCE: + case WM5100_OUT4RMIX_INPUT_1_VOLUME: + case WM5100_OUT4RMIX_INPUT_2_SOURCE: + case WM5100_OUT4RMIX_INPUT_2_VOLUME: + case WM5100_OUT4RMIX_INPUT_3_SOURCE: + case WM5100_OUT4RMIX_INPUT_3_VOLUME: + case WM5100_OUT4RMIX_INPUT_4_SOURCE: + case WM5100_OUT4RMIX_INPUT_4_VOLUME: + case WM5100_OUT5LMIX_INPUT_1_SOURCE: + case WM5100_OUT5LMIX_INPUT_1_VOLUME: + case WM5100_OUT5LMIX_INPUT_2_SOURCE: + case WM5100_OUT5LMIX_INPUT_2_VOLUME: + case WM5100_OUT5LMIX_INPUT_3_SOURCE: + case WM5100_OUT5LMIX_INPUT_3_VOLUME: + case WM5100_OUT5LMIX_INPUT_4_SOURCE: + case WM5100_OUT5LMIX_INPUT_4_VOLUME: + case WM5100_OUT5RMIX_INPUT_1_SOURCE: + case WM5100_OUT5RMIX_INPUT_1_VOLUME: + case WM5100_OUT5RMIX_INPUT_2_SOURCE: + case WM5100_OUT5RMIX_INPUT_2_VOLUME: + case WM5100_OUT5RMIX_INPUT_3_SOURCE: + case WM5100_OUT5RMIX_INPUT_3_VOLUME: + case WM5100_OUT5RMIX_INPUT_4_SOURCE: + case WM5100_OUT5RMIX_INPUT_4_VOLUME: + case WM5100_OUT6LMIX_INPUT_1_SOURCE: + case WM5100_OUT6LMIX_INPUT_1_VOLUME: + case WM5100_OUT6LMIX_INPUT_2_SOURCE: + case WM5100_OUT6LMIX_INPUT_2_VOLUME: + case WM5100_OUT6LMIX_INPUT_3_SOURCE: + case WM5100_OUT6LMIX_INPUT_3_VOLUME: + case WM5100_OUT6LMIX_INPUT_4_SOURCE: + case WM5100_OUT6LMIX_INPUT_4_VOLUME: + case WM5100_OUT6RMIX_INPUT_1_SOURCE: + case WM5100_OUT6RMIX_INPUT_1_VOLUME: + case WM5100_OUT6RMIX_INPUT_2_SOURCE: + case WM5100_OUT6RMIX_INPUT_2_VOLUME: + case WM5100_OUT6RMIX_INPUT_3_SOURCE: + case WM5100_OUT6RMIX_INPUT_3_VOLUME: + case WM5100_OUT6RMIX_INPUT_4_SOURCE: + case WM5100_OUT6RMIX_INPUT_4_VOLUME: + case WM5100_AIF1TX1MIX_INPUT_1_SOURCE: + case WM5100_AIF1TX1MIX_INPUT_1_VOLUME: + case WM5100_AIF1TX1MIX_INPUT_2_SOURCE: + case WM5100_AIF1TX1MIX_INPUT_2_VOLUME: + case WM5100_AIF1TX1MIX_INPUT_3_SOURCE: + case WM5100_AIF1TX1MIX_INPUT_3_VOLUME: + case WM5100_AIF1TX1MIX_INPUT_4_SOURCE: + case WM5100_AIF1TX1MIX_INPUT_4_VOLUME: + case WM5100_AIF1TX2MIX_INPUT_1_SOURCE: + case WM5100_AIF1TX2MIX_INPUT_1_VOLUME: + case WM5100_AIF1TX2MIX_INPUT_2_SOURCE: + case WM5100_AIF1TX2MIX_INPUT_2_VOLUME: + case WM5100_AIF1TX2MIX_INPUT_3_SOURCE: + case WM5100_AIF1TX2MIX_INPUT_3_VOLUME: + case WM5100_AIF1TX2MIX_INPUT_4_SOURCE: + case WM5100_AIF1TX2MIX_INPUT_4_VOLUME: + case WM5100_AIF1TX3MIX_INPUT_1_SOURCE: + case WM5100_AIF1TX3MIX_INPUT_1_VOLUME: + case WM5100_AIF1TX3MIX_INPUT_2_SOURCE: + case WM5100_AIF1TX3MIX_INPUT_2_VOLUME: + case WM5100_AIF1TX3MIX_INPUT_3_SOURCE: + case WM5100_AIF1TX3MIX_INPUT_3_VOLUME: + case WM5100_AIF1TX3MIX_INPUT_4_SOURCE: + case WM5100_AIF1TX3MIX_INPUT_4_VOLUME: + case WM5100_AIF1TX4MIX_INPUT_1_SOURCE: + case WM5100_AIF1TX4MIX_INPUT_1_VOLUME: + case WM5100_AIF1TX4MIX_INPUT_2_SOURCE: + case WM5100_AIF1TX4MIX_INPUT_2_VOLUME: + case WM5100_AIF1TX4MIX_INPUT_3_SOURCE: + case WM5100_AIF1TX4MIX_INPUT_3_VOLUME: + case WM5100_AIF1TX4MIX_INPUT_4_SOURCE: + case WM5100_AIF1TX4MIX_INPUT_4_VOLUME: + case WM5100_AIF1TX5MIX_INPUT_1_SOURCE: + case WM5100_AIF1TX5MIX_INPUT_1_VOLUME: + case WM5100_AIF1TX5MIX_INPUT_2_SOURCE: + case WM5100_AIF1TX5MIX_INPUT_2_VOLUME: + case WM5100_AIF1TX5MIX_INPUT_3_SOURCE: + case WM5100_AIF1TX5MIX_INPUT_3_VOLUME: + case WM5100_AIF1TX5MIX_INPUT_4_SOURCE: + case WM5100_AIF1TX5MIX_INPUT_4_VOLUME: + case WM5100_AIF1TX6MIX_INPUT_1_SOURCE: + case WM5100_AIF1TX6MIX_INPUT_1_VOLUME: + case WM5100_AIF1TX6MIX_INPUT_2_SOURCE: + case WM5100_AIF1TX6MIX_INPUT_2_VOLUME: + case WM5100_AIF1TX6MIX_INPUT_3_SOURCE: + case WM5100_AIF1TX6MIX_INPUT_3_VOLUME: + case WM5100_AIF1TX6MIX_INPUT_4_SOURCE: + case WM5100_AIF1TX6MIX_INPUT_4_VOLUME: + case WM5100_AIF1TX7MIX_INPUT_1_SOURCE: + case WM5100_AIF1TX7MIX_INPUT_1_VOLUME: + case WM5100_AIF1TX7MIX_INPUT_2_SOURCE: + case WM5100_AIF1TX7MIX_INPUT_2_VOLUME: + case WM5100_AIF1TX7MIX_INPUT_3_SOURCE: + case WM5100_AIF1TX7MIX_INPUT_3_VOLUME: + case WM5100_AIF1TX7MIX_INPUT_4_SOURCE: + case WM5100_AIF1TX7MIX_INPUT_4_VOLUME: + case WM5100_AIF1TX8MIX_INPUT_1_SOURCE: + case WM5100_AIF1TX8MIX_INPUT_1_VOLUME: + case WM5100_AIF1TX8MIX_INPUT_2_SOURCE: + case WM5100_AIF1TX8MIX_INPUT_2_VOLUME: + case WM5100_AIF1TX8MIX_INPUT_3_SOURCE: + case WM5100_AIF1TX8MIX_INPUT_3_VOLUME: + case WM5100_AIF1TX8MIX_INPUT_4_SOURCE: + case WM5100_AIF1TX8MIX_INPUT_4_VOLUME: + case WM5100_AIF2TX1MIX_INPUT_1_SOURCE: + case WM5100_AIF2TX1MIX_INPUT_1_VOLUME: + case WM5100_AIF2TX1MIX_INPUT_2_SOURCE: + case WM5100_AIF2TX1MIX_INPUT_2_VOLUME: + case WM5100_AIF2TX1MIX_INPUT_3_SOURCE: + case WM5100_AIF2TX1MIX_INPUT_3_VOLUME: + case WM5100_AIF2TX1MIX_INPUT_4_SOURCE: + case WM5100_AIF2TX1MIX_INPUT_4_VOLUME: + case WM5100_AIF2TX2MIX_INPUT_1_SOURCE: + case WM5100_AIF2TX2MIX_INPUT_1_VOLUME: + case WM5100_AIF2TX2MIX_INPUT_2_SOURCE: + case WM5100_AIF2TX2MIX_INPUT_2_VOLUME: + case WM5100_AIF2TX2MIX_INPUT_3_SOURCE: + case WM5100_AIF2TX2MIX_INPUT_3_VOLUME: + case WM5100_AIF2TX2MIX_INPUT_4_SOURCE: + case WM5100_AIF2TX2MIX_INPUT_4_VOLUME: + case WM5100_AIF3TX1MIX_INPUT_1_SOURCE: + case WM5100_AIF3TX1MIX_INPUT_1_VOLUME: + case WM5100_AIF3TX1MIX_INPUT_2_SOURCE: + case WM5100_AIF3TX1MIX_INPUT_2_VOLUME: + case WM5100_AIF3TX1MIX_INPUT_3_SOURCE: + case WM5100_AIF3TX1MIX_INPUT_3_VOLUME: + case WM5100_AIF3TX1MIX_INPUT_4_SOURCE: + case WM5100_AIF3TX1MIX_INPUT_4_VOLUME: + case WM5100_AIF3TX2MIX_INPUT_1_SOURCE: + case WM5100_AIF3TX2MIX_INPUT_1_VOLUME: + case WM5100_AIF3TX2MIX_INPUT_2_SOURCE: + case WM5100_AIF3TX2MIX_INPUT_2_VOLUME: + case WM5100_AIF3TX2MIX_INPUT_3_SOURCE: + case WM5100_AIF3TX2MIX_INPUT_3_VOLUME: + case WM5100_AIF3TX2MIX_INPUT_4_SOURCE: + case WM5100_AIF3TX2MIX_INPUT_4_VOLUME: + case WM5100_EQ1MIX_INPUT_1_SOURCE: + case WM5100_EQ1MIX_INPUT_1_VOLUME: + case WM5100_EQ1MIX_INPUT_2_SOURCE: + case WM5100_EQ1MIX_INPUT_2_VOLUME: + case WM5100_EQ1MIX_INPUT_3_SOURCE: + case WM5100_EQ1MIX_INPUT_3_VOLUME: + case WM5100_EQ1MIX_INPUT_4_SOURCE: + case WM5100_EQ1MIX_INPUT_4_VOLUME: + case WM5100_EQ2MIX_INPUT_1_SOURCE: + case WM5100_EQ2MIX_INPUT_1_VOLUME: + case WM5100_EQ2MIX_INPUT_2_SOURCE: + case WM5100_EQ2MIX_INPUT_2_VOLUME: + case WM5100_EQ2MIX_INPUT_3_SOURCE: + case WM5100_EQ2MIX_INPUT_3_VOLUME: + case WM5100_EQ2MIX_INPUT_4_SOURCE: + case WM5100_EQ2MIX_INPUT_4_VOLUME: + case WM5100_EQ3MIX_INPUT_1_SOURCE: + case WM5100_EQ3MIX_INPUT_1_VOLUME: + case WM5100_EQ3MIX_INPUT_2_SOURCE: + case WM5100_EQ3MIX_INPUT_2_VOLUME: + case WM5100_EQ3MIX_INPUT_3_SOURCE: + case WM5100_EQ3MIX_INPUT_3_VOLUME: + case WM5100_EQ3MIX_INPUT_4_SOURCE: + case WM5100_EQ3MIX_INPUT_4_VOLUME: + case WM5100_EQ4MIX_INPUT_1_SOURCE: + case WM5100_EQ4MIX_INPUT_1_VOLUME: + case WM5100_EQ4MIX_INPUT_2_SOURCE: + case WM5100_EQ4MIX_INPUT_2_VOLUME: + case WM5100_EQ4MIX_INPUT_3_SOURCE: + case WM5100_EQ4MIX_INPUT_3_VOLUME: + case WM5100_EQ4MIX_INPUT_4_SOURCE: + case WM5100_EQ4MIX_INPUT_4_VOLUME: + case WM5100_DRC1LMIX_INPUT_1_SOURCE: + case WM5100_DRC1LMIX_INPUT_1_VOLUME: + case WM5100_DRC1LMIX_INPUT_2_SOURCE: + case WM5100_DRC1LMIX_INPUT_2_VOLUME: + case WM5100_DRC1LMIX_INPUT_3_SOURCE: + case WM5100_DRC1LMIX_INPUT_3_VOLUME: + case WM5100_DRC1LMIX_INPUT_4_SOURCE: + case WM5100_DRC1LMIX_INPUT_4_VOLUME: + case WM5100_DRC1RMIX_INPUT_1_SOURCE: + case WM5100_DRC1RMIX_INPUT_1_VOLUME: + case WM5100_DRC1RMIX_INPUT_2_SOURCE: + case WM5100_DRC1RMIX_INPUT_2_VOLUME: + case WM5100_DRC1RMIX_INPUT_3_SOURCE: + case WM5100_DRC1RMIX_INPUT_3_VOLUME: + case WM5100_DRC1RMIX_INPUT_4_SOURCE: + case WM5100_DRC1RMIX_INPUT_4_VOLUME: + case WM5100_HPLP1MIX_INPUT_1_SOURCE: + case WM5100_HPLP1MIX_INPUT_1_VOLUME: + case WM5100_HPLP1MIX_INPUT_2_SOURCE: + case WM5100_HPLP1MIX_INPUT_2_VOLUME: + case WM5100_HPLP1MIX_INPUT_3_SOURCE: + case WM5100_HPLP1MIX_INPUT_3_VOLUME: + case WM5100_HPLP1MIX_INPUT_4_SOURCE: + case WM5100_HPLP1MIX_INPUT_4_VOLUME: + case WM5100_HPLP2MIX_INPUT_1_SOURCE: + case WM5100_HPLP2MIX_INPUT_1_VOLUME: + case WM5100_HPLP2MIX_INPUT_2_SOURCE: + case WM5100_HPLP2MIX_INPUT_2_VOLUME: + case WM5100_HPLP2MIX_INPUT_3_SOURCE: + case WM5100_HPLP2MIX_INPUT_3_VOLUME: + case WM5100_HPLP2MIX_INPUT_4_SOURCE: + case WM5100_HPLP2MIX_INPUT_4_VOLUME: + case WM5100_HPLP3MIX_INPUT_1_SOURCE: + case WM5100_HPLP3MIX_INPUT_1_VOLUME: + case WM5100_HPLP3MIX_INPUT_2_SOURCE: + case WM5100_HPLP3MIX_INPUT_2_VOLUME: + case WM5100_HPLP3MIX_INPUT_3_SOURCE: + case WM5100_HPLP3MIX_INPUT_3_VOLUME: + case WM5100_HPLP3MIX_INPUT_4_SOURCE: + case WM5100_HPLP3MIX_INPUT_4_VOLUME: + case WM5100_HPLP4MIX_INPUT_1_SOURCE: + case WM5100_HPLP4MIX_INPUT_1_VOLUME: + case WM5100_HPLP4MIX_INPUT_2_SOURCE: + case WM5100_HPLP4MIX_INPUT_2_VOLUME: + case WM5100_HPLP4MIX_INPUT_3_SOURCE: + case WM5100_HPLP4MIX_INPUT_3_VOLUME: + case WM5100_HPLP4MIX_INPUT_4_SOURCE: + case WM5100_HPLP4MIX_INPUT_4_VOLUME: + case WM5100_DSP1LMIX_INPUT_1_SOURCE: + case WM5100_DSP1LMIX_INPUT_1_VOLUME: + case WM5100_DSP1LMIX_INPUT_2_SOURCE: + case WM5100_DSP1LMIX_INPUT_2_VOLUME: + case WM5100_DSP1LMIX_INPUT_3_SOURCE: + case WM5100_DSP1LMIX_INPUT_3_VOLUME: + case WM5100_DSP1LMIX_INPUT_4_SOURCE: + case WM5100_DSP1LMIX_INPUT_4_VOLUME: + case WM5100_DSP1RMIX_INPUT_1_SOURCE: + case WM5100_DSP1RMIX_INPUT_1_VOLUME: + case WM5100_DSP1RMIX_INPUT_2_SOURCE: + case WM5100_DSP1RMIX_INPUT_2_VOLUME: + case WM5100_DSP1RMIX_INPUT_3_SOURCE: + case WM5100_DSP1RMIX_INPUT_3_VOLUME: + case WM5100_DSP1RMIX_INPUT_4_SOURCE: + case WM5100_DSP1RMIX_INPUT_4_VOLUME: + case WM5100_DSP1AUX1MIX_INPUT_1_SOURCE: + case WM5100_DSP1AUX2MIX_INPUT_1_SOURCE: + case WM5100_DSP1AUX3MIX_INPUT_1_SOURCE: + case WM5100_DSP1AUX4MIX_INPUT_1_SOURCE: + case WM5100_DSP1AUX5MIX_INPUT_1_SOURCE: + case WM5100_DSP1AUX6MIX_INPUT_1_SOURCE: + case WM5100_DSP2LMIX_INPUT_1_SOURCE: + case WM5100_DSP2LMIX_INPUT_1_VOLUME: + case WM5100_DSP2LMIX_INPUT_2_SOURCE: + case WM5100_DSP2LMIX_INPUT_2_VOLUME: + case WM5100_DSP2LMIX_INPUT_3_SOURCE: + case WM5100_DSP2LMIX_INPUT_3_VOLUME: + case WM5100_DSP2LMIX_INPUT_4_SOURCE: + case WM5100_DSP2LMIX_INPUT_4_VOLUME: + case WM5100_DSP2RMIX_INPUT_1_SOURCE: + case WM5100_DSP2RMIX_INPUT_1_VOLUME: + case WM5100_DSP2RMIX_INPUT_2_SOURCE: + case WM5100_DSP2RMIX_INPUT_2_VOLUME: + case WM5100_DSP2RMIX_INPUT_3_SOURCE: + case WM5100_DSP2RMIX_INPUT_3_VOLUME: + case WM5100_DSP2RMIX_INPUT_4_SOURCE: + case WM5100_DSP2RMIX_INPUT_4_VOLUME: + case WM5100_DSP2AUX1MIX_INPUT_1_SOURCE: + case WM5100_DSP2AUX2MIX_INPUT_1_SOURCE: + case WM5100_DSP2AUX3MIX_INPUT_1_SOURCE: + case WM5100_DSP2AUX4MIX_INPUT_1_SOURCE: + case WM5100_DSP2AUX5MIX_INPUT_1_SOURCE: + case WM5100_DSP2AUX6MIX_INPUT_1_SOURCE: + case WM5100_DSP3LMIX_INPUT_1_SOURCE: + case WM5100_DSP3LMIX_INPUT_1_VOLUME: + case WM5100_DSP3LMIX_INPUT_2_SOURCE: + case WM5100_DSP3LMIX_INPUT_2_VOLUME: + case WM5100_DSP3LMIX_INPUT_3_SOURCE: + case WM5100_DSP3LMIX_INPUT_3_VOLUME: + case WM5100_DSP3LMIX_INPUT_4_SOURCE: + case WM5100_DSP3LMIX_INPUT_4_VOLUME: + case WM5100_DSP3RMIX_INPUT_1_SOURCE: + case WM5100_DSP3RMIX_INPUT_1_VOLUME: + case WM5100_DSP3RMIX_INPUT_2_SOURCE: + case WM5100_DSP3RMIX_INPUT_2_VOLUME: + case WM5100_DSP3RMIX_INPUT_3_SOURCE: + case WM5100_DSP3RMIX_INPUT_3_VOLUME: + case WM5100_DSP3RMIX_INPUT_4_SOURCE: + case WM5100_DSP3RMIX_INPUT_4_VOLUME: + case WM5100_DSP3AUX1MIX_INPUT_1_SOURCE: + case WM5100_DSP3AUX2MIX_INPUT_1_SOURCE: + case WM5100_DSP3AUX3MIX_INPUT_1_SOURCE: + case WM5100_DSP3AUX4MIX_INPUT_1_SOURCE: + case WM5100_DSP3AUX5MIX_INPUT_1_SOURCE: + case WM5100_DSP3AUX6MIX_INPUT_1_SOURCE: + case WM5100_ASRC1LMIX_INPUT_1_SOURCE: + case WM5100_ASRC1RMIX_INPUT_1_SOURCE: + case WM5100_ASRC2LMIX_INPUT_1_SOURCE: + case WM5100_ASRC2RMIX_INPUT_1_SOURCE: + case WM5100_ISRC1DEC1MIX_INPUT_1_SOURCE: + case WM5100_ISRC1DEC2MIX_INPUT_1_SOURCE: + case WM5100_ISRC1DEC3MIX_INPUT_1_SOURCE: + case WM5100_ISRC1DEC4MIX_INPUT_1_SOURCE: + case WM5100_ISRC1INT1MIX_INPUT_1_SOURCE: + case WM5100_ISRC1INT2MIX_INPUT_1_SOURCE: + case WM5100_ISRC1INT3MIX_INPUT_1_SOURCE: + case WM5100_ISRC1INT4MIX_INPUT_1_SOURCE: + case WM5100_ISRC2DEC1MIX_INPUT_1_SOURCE: + case WM5100_ISRC2DEC2MIX_INPUT_1_SOURCE: + case WM5100_ISRC2DEC3MIX_INPUT_1_SOURCE: + case WM5100_ISRC2DEC4MIX_INPUT_1_SOURCE: + case WM5100_ISRC2INT1MIX_INPUT_1_SOURCE: + case WM5100_ISRC2INT2MIX_INPUT_1_SOURCE: + case WM5100_ISRC2INT3MIX_INPUT_1_SOURCE: + case WM5100_ISRC2INT4MIX_INPUT_1_SOURCE: + case WM5100_GPIO_CTRL_1: + case WM5100_GPIO_CTRL_2: + case WM5100_GPIO_CTRL_3: + case WM5100_GPIO_CTRL_4: + case WM5100_GPIO_CTRL_5: + case WM5100_GPIO_CTRL_6: + case WM5100_MISC_PAD_CTRL_1: + case WM5100_MISC_PAD_CTRL_2: + case WM5100_MISC_PAD_CTRL_3: + case WM5100_MISC_PAD_CTRL_4: + case WM5100_MISC_PAD_CTRL_5: + case WM5100_MISC_GPIO_1: + case WM5100_INTERRUPT_STATUS_1: + case WM5100_INTERRUPT_STATUS_2: + case WM5100_INTERRUPT_STATUS_3: + case WM5100_INTERRUPT_STATUS_4: + case WM5100_INTERRUPT_RAW_STATUS_2: + case WM5100_INTERRUPT_RAW_STATUS_3: + case WM5100_INTERRUPT_RAW_STATUS_4: + case WM5100_INTERRUPT_STATUS_1_MASK: + case WM5100_INTERRUPT_STATUS_2_MASK: + case WM5100_INTERRUPT_STATUS_3_MASK: + case WM5100_INTERRUPT_STATUS_4_MASK: + case WM5100_INTERRUPT_CONTROL: + case WM5100_IRQ_DEBOUNCE_1: + case WM5100_IRQ_DEBOUNCE_2: + case WM5100_FX_CTRL: + case WM5100_EQ1_1: + case WM5100_EQ1_2: + case WM5100_EQ1_3: + case WM5100_EQ1_4: + case WM5100_EQ1_5: + case WM5100_EQ1_6: + case WM5100_EQ1_7: + case WM5100_EQ1_8: + case WM5100_EQ1_9: + case WM5100_EQ1_10: + case WM5100_EQ1_11: + case WM5100_EQ1_12: + case WM5100_EQ1_13: + case WM5100_EQ1_14: + case WM5100_EQ1_15: + case WM5100_EQ1_16: + case WM5100_EQ1_17: + case WM5100_EQ1_18: + case WM5100_EQ1_19: + case WM5100_EQ1_20: + case WM5100_EQ2_1: + case WM5100_EQ2_2: + case WM5100_EQ2_3: + case WM5100_EQ2_4: + case WM5100_EQ2_5: + case WM5100_EQ2_6: + case WM5100_EQ2_7: + case WM5100_EQ2_8: + case WM5100_EQ2_9: + case WM5100_EQ2_10: + case WM5100_EQ2_11: + case WM5100_EQ2_12: + case WM5100_EQ2_13: + case WM5100_EQ2_14: + case WM5100_EQ2_15: + case WM5100_EQ2_16: + case WM5100_EQ2_17: + case WM5100_EQ2_18: + case WM5100_EQ2_19: + case WM5100_EQ2_20: + case WM5100_EQ3_1: + case WM5100_EQ3_2: + case WM5100_EQ3_3: + case WM5100_EQ3_4: + case WM5100_EQ3_5: + case WM5100_EQ3_6: + case WM5100_EQ3_7: + case WM5100_EQ3_8: + case WM5100_EQ3_9: + case WM5100_EQ3_10: + case WM5100_EQ3_11: + case WM5100_EQ3_12: + case WM5100_EQ3_13: + case WM5100_EQ3_14: + case WM5100_EQ3_15: + case WM5100_EQ3_16: + case WM5100_EQ3_17: + case WM5100_EQ3_18: + case WM5100_EQ3_19: + case WM5100_EQ3_20: + case WM5100_EQ4_1: + case WM5100_EQ4_2: + case WM5100_EQ4_3: + case WM5100_EQ4_4: + case WM5100_EQ4_5: + case WM5100_EQ4_6: + case WM5100_EQ4_7: + case WM5100_EQ4_8: + case WM5100_EQ4_9: + case WM5100_EQ4_10: + case WM5100_EQ4_11: + case WM5100_EQ4_12: + case WM5100_EQ4_13: + case WM5100_EQ4_14: + case WM5100_EQ4_15: + case WM5100_EQ4_16: + case WM5100_EQ4_17: + case WM5100_EQ4_18: + case WM5100_EQ4_19: + case WM5100_EQ4_20: + case WM5100_DRC1_CTRL1: + case WM5100_DRC1_CTRL2: + case WM5100_DRC1_CTRL3: + case WM5100_DRC1_CTRL4: + case WM5100_DRC1_CTRL5: + case WM5100_HPLPF1_1: + case WM5100_HPLPF1_2: + case WM5100_HPLPF2_1: + case WM5100_HPLPF2_2: + case WM5100_HPLPF3_1: + case WM5100_HPLPF3_2: + case WM5100_HPLPF4_1: + case WM5100_HPLPF4_2: + case WM5100_DSP1_CONTROL_1: + case WM5100_DSP1_CONTROL_2: + case WM5100_DSP1_CONTROL_3: + case WM5100_DSP1_CONTROL_4: + case WM5100_DSP1_CONTROL_5: + case WM5100_DSP1_CONTROL_6: + case WM5100_DSP1_CONTROL_7: + case WM5100_DSP1_CONTROL_8: + case WM5100_DSP1_CONTROL_9: + case WM5100_DSP1_CONTROL_10: + case WM5100_DSP1_CONTROL_11: + case WM5100_DSP1_CONTROL_12: + case WM5100_DSP1_CONTROL_13: + case WM5100_DSP1_CONTROL_14: + case WM5100_DSP1_CONTROL_15: + case WM5100_DSP1_CONTROL_16: + case WM5100_DSP1_CONTROL_17: + case WM5100_DSP1_CONTROL_18: + case WM5100_DSP1_CONTROL_19: + case WM5100_DSP1_CONTROL_20: + case WM5100_DSP1_CONTROL_21: + case WM5100_DSP1_CONTROL_22: + case WM5100_DSP1_CONTROL_23: + case WM5100_DSP1_CONTROL_24: + case WM5100_DSP1_CONTROL_25: + case WM5100_DSP1_CONTROL_26: + case WM5100_DSP1_CONTROL_27: + case WM5100_DSP1_CONTROL_28: + case WM5100_DSP1_CONTROL_29: + case WM5100_DSP1_CONTROL_30: + case WM5100_DSP2_CONTROL_1: + case WM5100_DSP2_CONTROL_2: + case WM5100_DSP2_CONTROL_3: + case WM5100_DSP2_CONTROL_4: + case WM5100_DSP2_CONTROL_5: + case WM5100_DSP2_CONTROL_6: + case WM5100_DSP2_CONTROL_7: + case WM5100_DSP2_CONTROL_8: + case WM5100_DSP2_CONTROL_9: + case WM5100_DSP2_CONTROL_10: + case WM5100_DSP2_CONTROL_11: + case WM5100_DSP2_CONTROL_12: + case WM5100_DSP2_CONTROL_13: + case WM5100_DSP2_CONTROL_14: + case WM5100_DSP2_CONTROL_15: + case WM5100_DSP2_CONTROL_16: + case WM5100_DSP2_CONTROL_17: + case WM5100_DSP2_CONTROL_18: + case WM5100_DSP2_CONTROL_19: + case WM5100_DSP2_CONTROL_20: + case WM5100_DSP2_CONTROL_21: + case WM5100_DSP2_CONTROL_22: + case WM5100_DSP2_CONTROL_23: + case WM5100_DSP2_CONTROL_24: + case WM5100_DSP2_CONTROL_25: + case WM5100_DSP2_CONTROL_26: + case WM5100_DSP2_CONTROL_27: + case WM5100_DSP2_CONTROL_28: + case WM5100_DSP2_CONTROL_29: + case WM5100_DSP2_CONTROL_30: + case WM5100_DSP3_CONTROL_1: + case WM5100_DSP3_CONTROL_2: + case WM5100_DSP3_CONTROL_3: + case WM5100_DSP3_CONTROL_4: + case WM5100_DSP3_CONTROL_5: + case WM5100_DSP3_CONTROL_6: + case WM5100_DSP3_CONTROL_7: + case WM5100_DSP3_CONTROL_8: + case WM5100_DSP3_CONTROL_9: + case WM5100_DSP3_CONTROL_10: + case WM5100_DSP3_CONTROL_11: + case WM5100_DSP3_CONTROL_12: + case WM5100_DSP3_CONTROL_13: + case WM5100_DSP3_CONTROL_14: + case WM5100_DSP3_CONTROL_15: + case WM5100_DSP3_CONTROL_16: + case WM5100_DSP3_CONTROL_17: + case WM5100_DSP3_CONTROL_18: + case WM5100_DSP3_CONTROL_19: + case WM5100_DSP3_CONTROL_20: + case WM5100_DSP3_CONTROL_21: + case WM5100_DSP3_CONTROL_22: + case WM5100_DSP3_CONTROL_23: + case WM5100_DSP3_CONTROL_24: + case WM5100_DSP3_CONTROL_25: + case WM5100_DSP3_CONTROL_26: + case WM5100_DSP3_CONTROL_27: + case WM5100_DSP3_CONTROL_28: + case WM5100_DSP3_CONTROL_29: + case WM5100_DSP3_CONTROL_30: + return true; + default: + if ((reg >= WM5100_DSP1_PM_0 && reg <= WM5100_DSP1_PM_1535) || + (reg >= WM5100_DSP1_ZM_0 && reg <= WM5100_DSP1_ZM_2047) || + (reg >= WM5100_DSP1_DM_0 && reg <= WM5100_DSP1_DM_511) || + (reg >= WM5100_DSP2_PM_0 && reg <= WM5100_DSP2_PM_1535) || + (reg >= WM5100_DSP2_ZM_0 && reg <= WM5100_DSP2_ZM_2047) || + (reg >= WM5100_DSP2_DM_0 && reg <= WM5100_DSP2_DM_511) || + (reg >= WM5100_DSP3_PM_0 && reg <= WM5100_DSP3_PM_1535) || + (reg >= WM5100_DSP3_ZM_0 && reg <= WM5100_DSP3_ZM_2047) || + (reg >= WM5100_DSP3_DM_0 && reg <= WM5100_DSP3_DM_511)) + return true; + else + return false; + } +} + +struct reg_default wm5100_reg_defaults[WM5100_REGISTER_COUNT] = { + { 0x0000, 0x0000 }, /* R0 - software reset */ + { 0x0001, 0x0000 }, /* R1 - Device Revision */ + { 0x0010, 0x0801 }, /* R16 - Ctrl IF 1 */ + { 0x0020, 0x0000 }, /* R32 - Tone Generator 1 */ + { 0x0030, 0x0000 }, /* R48 - PWM Drive 1 */ + { 0x0031, 0x0100 }, /* R49 - PWM Drive 2 */ + { 0x0032, 0x0100 }, /* R50 - PWM Drive 3 */ + { 0x0100, 0x0002 }, /* R256 - Clocking 1 */ + { 0x0101, 0x0000 }, /* R257 - Clocking 3 */ + { 0x0102, 0x0011 }, /* R258 - Clocking 4 */ + { 0x0103, 0x0011 }, /* R259 - Clocking 5 */ + { 0x0104, 0x0011 }, /* R260 - Clocking 6 */ + { 0x0107, 0x0000 }, /* R263 - Clocking 7 */ + { 0x0108, 0x0000 }, /* R264 - Clocking 8 */ + { 0x0120, 0x0000 }, /* R288 - ASRC_ENABLE */ + { 0x0121, 0x0000 }, /* R289 - ASRC_STATUS */ + { 0x0122, 0x0000 }, /* R290 - ASRC_RATE1 */ + { 0x0141, 0x8000 }, /* R321 - ISRC 1 CTRL 1 */ + { 0x0142, 0x0000 }, /* R322 - ISRC 1 CTRL 2 */ + { 0x0143, 0x8000 }, /* R323 - ISRC 2 CTRL1 */ + { 0x0144, 0x0000 }, /* R324 - ISRC 2 CTRL 2 */ + { 0x0182, 0x0000 }, /* R386 - FLL1 Control 1 */ + { 0x0183, 0x0000 }, /* R387 - FLL1 Control 2 */ + { 0x0184, 0x0000 }, /* R388 - FLL1 Control 3 */ + { 0x0186, 0x0177 }, /* R390 - FLL1 Control 5 */ + { 0x0187, 0x0001 }, /* R391 - FLL1 Control 6 */ + { 0x0188, 0x0000 }, /* R392 - FLL1 EFS 1 */ + { 0x01A2, 0x0000 }, /* R418 - FLL2 Control 1 */ + { 0x01A3, 0x0000 }, /* R419 - FLL2 Control 2 */ + { 0x01A4, 0x0000 }, /* R420 - FLL2 Control 3 */ + { 0x01A6, 0x0177 }, /* R422 - FLL2 Control 5 */ + { 0x01A7, 0x0001 }, /* R423 - FLL2 Control 6 */ + { 0x01A8, 0x0000 }, /* R424 - FLL2 EFS 1 */ + { 0x0200, 0x0020 }, /* R512 - Mic Charge Pump 1 */ + { 0x0201, 0xB084 }, /* R513 - Mic Charge Pump 2 */ + { 0x0202, 0xBBDE }, /* R514 - HP Charge Pump 1 */ + { 0x0211, 0x20D4 }, /* R529 - LDO1 Control */ + { 0x0215, 0x0062 }, /* R533 - Mic Bias Ctrl 1 */ + { 0x0216, 0x0062 }, /* R534 - Mic Bias Ctrl 2 */ + { 0x0217, 0x0062 }, /* R535 - Mic Bias Ctrl 3 */ + { 0x0280, 0x0004 }, /* R640 - Accessory Detect Mode 1 */ + { 0x0288, 0x0020 }, /* R648 - Headphone Detect 1 */ + { 0x0289, 0x0000 }, /* R649 - Headphone Detect 2 */ + { 0x0290, 0x1100 }, /* R656 - Mic Detect 1 */ + { 0x0291, 0x009F }, /* R657 - Mic Detect 2 */ + { 0x0292, 0x0000 }, /* R658 - Mic Detect 3 */ + { 0x0301, 0x0000 }, /* R769 - Input Enables */ + { 0x0302, 0x0000 }, /* R770 - Input Enables Status */ + { 0x0310, 0x2280 }, /* R784 - Status */ + { 0x0311, 0x0080 }, /* R785 - IN1R Control */ + { 0x0312, 0x2280 }, /* R786 - IN2L Control */ + { 0x0313, 0x0080 }, /* R787 - IN2R Control */ + { 0x0314, 0x2280 }, /* R788 - IN3L Control */ + { 0x0315, 0x0080 }, /* R789 - IN3R Control */ + { 0x0316, 0x2280 }, /* R790 - IN4L Control */ + { 0x0317, 0x0080 }, /* R791 - IN4R Control */ + { 0x0318, 0x0000 }, /* R792 - RXANC_SRC */ + { 0x0319, 0x0022 }, /* R793 - Input Volume Ramp */ + { 0x0320, 0x0180 }, /* R800 - ADC Digital Volume 1L */ + { 0x0321, 0x0180 }, /* R801 - ADC Digital Volume 1R */ + { 0x0322, 0x0180 }, /* R802 - ADC Digital Volume 2L */ + { 0x0323, 0x0180 }, /* R803 - ADC Digital Volume 2R */ + { 0x0324, 0x0180 }, /* R804 - ADC Digital Volume 3L */ + { 0x0325, 0x0180 }, /* R805 - ADC Digital Volume 3R */ + { 0x0326, 0x0180 }, /* R806 - ADC Digital Volume 4L */ + { 0x0327, 0x0180 }, /* R807 - ADC Digital Volume 4R */ + { 0x0401, 0x0000 }, /* R1025 - Output Enables 2 */ + { 0x0402, 0x0000 }, /* R1026 - Output Status 1 */ + { 0x0403, 0x0000 }, /* R1027 - Output Status 2 */ + { 0x0408, 0x0000 }, /* R1032 - Channel Enables 1 */ + { 0x0410, 0x0080 }, /* R1040 - Out Volume 1L */ + { 0x0411, 0x0080 }, /* R1041 - Out Volume 1R */ + { 0x0412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */ + { 0x0413, 0x0080 }, /* R1043 - DAC Volume Limit 1R */ + { 0x0414, 0x0080 }, /* R1044 - Out Volume 2L */ + { 0x0415, 0x0080 }, /* R1045 - Out Volume 2R */ + { 0x0416, 0x0080 }, /* R1046 - DAC Volume Limit 2L */ + { 0x0417, 0x0080 }, /* R1047 - DAC Volume Limit 2R */ + { 0x0418, 0x0080 }, /* R1048 - Out Volume 3L */ + { 0x0419, 0x0080 }, /* R1049 - Out Volume 3R */ + { 0x041A, 0x0080 }, /* R1050 - DAC Volume Limit 3L */ + { 0x041B, 0x0080 }, /* R1051 - DAC Volume Limit 3R */ + { 0x041C, 0x0080 }, /* R1052 - Out Volume 4L */ + { 0x041D, 0x0080 }, /* R1053 - Out Volume 4R */ + { 0x041E, 0x0080 }, /* R1054 - DAC Volume Limit 5L */ + { 0x041F, 0x0080 }, /* R1055 - DAC Volume Limit 5R */ + { 0x0420, 0x0080 }, /* R1056 - DAC Volume Limit 6L */ + { 0x0421, 0x0080 }, /* R1057 - DAC Volume Limit 6R */ + { 0x0440, 0x0000 }, /* R1088 - DAC AEC Control 1 */ + { 0x0441, 0x0022 }, /* R1089 - Output Volume Ramp */ + { 0x0480, 0x0180 }, /* R1152 - DAC Digital Volume 1L */ + { 0x0481, 0x0180 }, /* R1153 - DAC Digital Volume 1R */ + { 0x0482, 0x0180 }, /* R1154 - DAC Digital Volume 2L */ + { 0x0483, 0x0180 }, /* R1155 - DAC Digital Volume 2R */ + { 0x0484, 0x0180 }, /* R1156 - DAC Digital Volume 3L */ + { 0x0485, 0x0180 }, /* R1157 - DAC Digital Volume 3R */ + { 0x0486, 0x0180 }, /* R1158 - DAC Digital Volume 4L */ + { 0x0487, 0x0180 }, /* R1159 - DAC Digital Volume 4R */ + { 0x0488, 0x0180 }, /* R1160 - DAC Digital Volume 5L */ + { 0x0489, 0x0180 }, /* R1161 - DAC Digital Volume 5R */ + { 0x048A, 0x0180 }, /* R1162 - DAC Digital Volume 6L */ + { 0x048B, 0x0180 }, /* R1163 - DAC Digital Volume 6R */ + { 0x04C0, 0x0069 }, /* R1216 - PDM SPK1 CTRL 1 */ + { 0x04C1, 0x0000 }, /* R1217 - PDM SPK1 CTRL 2 */ + { 0x04C2, 0x0069 }, /* R1218 - PDM SPK2 CTRL 1 */ + { 0x04C3, 0x0000 }, /* R1219 - PDM SPK2 CTRL 2 */ + { 0x0500, 0x000C }, /* R1280 - Audio IF 1_1 */ + { 0x0501, 0x0008 }, /* R1281 - Audio IF 1_2 */ + { 0x0502, 0x0000 }, /* R1282 - Audio IF 1_3 */ + { 0x0503, 0x0000 }, /* R1283 - Audio IF 1_4 */ + { 0x0504, 0x0000 }, /* R1284 - Audio IF 1_5 */ + { 0x0505, 0x0300 }, /* R1285 - Audio IF 1_6 */ + { 0x0506, 0x0300 }, /* R1286 - Audio IF 1_7 */ + { 0x0507, 0x1820 }, /* R1287 - Audio IF 1_8 */ + { 0x0508, 0x1820 }, /* R1288 - Audio IF 1_9 */ + { 0x0509, 0x0000 }, /* R1289 - Audio IF 1_10 */ + { 0x050A, 0x0001 }, /* R1290 - Audio IF 1_11 */ + { 0x050B, 0x0002 }, /* R1291 - Audio IF 1_12 */ + { 0x050C, 0x0003 }, /* R1292 - Audio IF 1_13 */ + { 0x050D, 0x0004 }, /* R1293 - Audio IF 1_14 */ + { 0x050E, 0x0005 }, /* R1294 - Audio IF 1_15 */ + { 0x050F, 0x0006 }, /* R1295 - Audio IF 1_16 */ + { 0x0510, 0x0007 }, /* R1296 - Audio IF 1_17 */ + { 0x0511, 0x0000 }, /* R1297 - Audio IF 1_18 */ + { 0x0512, 0x0001 }, /* R1298 - Audio IF 1_19 */ + { 0x0513, 0x0002 }, /* R1299 - Audio IF 1_20 */ + { 0x0514, 0x0003 }, /* R1300 - Audio IF 1_21 */ + { 0x0515, 0x0004 }, /* R1301 - Audio IF 1_22 */ + { 0x0516, 0x0005 }, /* R1302 - Audio IF 1_23 */ + { 0x0517, 0x0006 }, /* R1303 - Audio IF 1_24 */ + { 0x0518, 0x0007 }, /* R1304 - Audio IF 1_25 */ + { 0x0519, 0x0000 }, /* R1305 - Audio IF 1_26 */ + { 0x051A, 0x0000 }, /* R1306 - Audio IF 1_27 */ + { 0x0540, 0x000C }, /* R1344 - Audio IF 2_1 */ + { 0x0541, 0x0008 }, /* R1345 - Audio IF 2_2 */ + { 0x0542, 0x0000 }, /* R1346 - Audio IF 2_3 */ + { 0x0543, 0x0000 }, /* R1347 - Audio IF 2_4 */ + { 0x0544, 0x0000 }, /* R1348 - Audio IF 2_5 */ + { 0x0545, 0x0300 }, /* R1349 - Audio IF 2_6 */ + { 0x0546, 0x0300 }, /* R1350 - Audio IF 2_7 */ + { 0x0547, 0x1820 }, /* R1351 - Audio IF 2_8 */ + { 0x0548, 0x1820 }, /* R1352 - Audio IF 2_9 */ + { 0x0549, 0x0000 }, /* R1353 - Audio IF 2_10 */ + { 0x054A, 0x0001 }, /* R1354 - Audio IF 2_11 */ + { 0x0551, 0x0000 }, /* R1361 - Audio IF 2_18 */ + { 0x0552, 0x0001 }, /* R1362 - Audio IF 2_19 */ + { 0x0559, 0x0000 }, /* R1369 - Audio IF 2_26 */ + { 0x055A, 0x0000 }, /* R1370 - Audio IF 2_27 */ + { 0x0580, 0x000C }, /* R1408 - Audio IF 3_1 */ + { 0x0581, 0x0008 }, /* R1409 - Audio IF 3_2 */ + { 0x0582, 0x0000 }, /* R1410 - Audio IF 3_3 */ + { 0x0583, 0x0000 }, /* R1411 - Audio IF 3_4 */ + { 0x0584, 0x0000 }, /* R1412 - Audio IF 3_5 */ + { 0x0585, 0x0300 }, /* R1413 - Audio IF 3_6 */ + { 0x0586, 0x0300 }, /* R1414 - Audio IF 3_7 */ + { 0x0587, 0x1820 }, /* R1415 - Audio IF 3_8 */ + { 0x0588, 0x1820 }, /* R1416 - Audio IF 3_9 */ + { 0x0589, 0x0000 }, /* R1417 - Audio IF 3_10 */ + { 0x058A, 0x0001 }, /* R1418 - Audio IF 3_11 */ + { 0x0591, 0x0000 }, /* R1425 - Audio IF 3_18 */ + { 0x0592, 0x0001 }, /* R1426 - Audio IF 3_19 */ + { 0x0599, 0x0000 }, /* R1433 - Audio IF 3_26 */ + { 0x059A, 0x0000 }, /* R1434 - Audio IF 3_27 */ + { 0x0640, 0x0000 }, /* R1600 - PWM1MIX Input 1 Source */ + { 0x0641, 0x0080 }, /* R1601 - PWM1MIX Input 1 Volume */ + { 0x0642, 0x0000 }, /* R1602 - PWM1MIX Input 2 Source */ + { 0x0643, 0x0080 }, /* R1603 - PWM1MIX Input 2 Volume */ + { 0x0644, 0x0000 }, /* R1604 - PWM1MIX Input 3 Source */ + { 0x0645, 0x0080 }, /* R1605 - PWM1MIX Input 3 Volume */ + { 0x0646, 0x0000 }, /* R1606 - PWM1MIX Input 4 Source */ + { 0x0647, 0x0080 }, /* R1607 - PWM1MIX Input 4 Volume */ + { 0x0648, 0x0000 }, /* R1608 - PWM2MIX Input 1 Source */ + { 0x0649, 0x0080 }, /* R1609 - PWM2MIX Input 1 Volume */ + { 0x064A, 0x0000 }, /* R1610 - PWM2MIX Input 2 Source */ + { 0x064B, 0x0080 }, /* R1611 - PWM2MIX Input 2 Volume */ + { 0x064C, 0x0000 }, /* R1612 - PWM2MIX Input 3 Source */ + { 0x064D, 0x0080 }, /* R1613 - PWM2MIX Input 3 Volume */ + { 0x064E, 0x0000 }, /* R1614 - PWM2MIX Input 4 Source */ + { 0x064F, 0x0080 }, /* R1615 - PWM2MIX Input 4 Volume */ + { 0x0680, 0x0000 }, /* R1664 - OUT1LMIX Input 1 Source */ + { 0x0681, 0x0080 }, /* R1665 - OUT1LMIX Input 1 Volume */ + { 0x0682, 0x0000 }, /* R1666 - OUT1LMIX Input 2 Source */ + { 0x0683, 0x0080 }, /* R1667 - OUT1LMIX Input 2 Volume */ + { 0x0684, 0x0000 }, /* R1668 - OUT1LMIX Input 3 Source */ + { 0x0685, 0x0080 }, /* R1669 - OUT1LMIX Input 3 Volume */ + { 0x0686, 0x0000 }, /* R1670 - OUT1LMIX Input 4 Source */ + { 0x0687, 0x0080 }, /* R1671 - OUT1LMIX Input 4 Volume */ + { 0x0688, 0x0000 }, /* R1672 - OUT1RMIX Input 1 Source */ + { 0x0689, 0x0080 }, /* R1673 - OUT1RMIX Input 1 Volume */ + { 0x068A, 0x0000 }, /* R1674 - OUT1RMIX Input 2 Source */ + { 0x068B, 0x0080 }, /* R1675 - OUT1RMIX Input 2 Volume */ + { 0x068C, 0x0000 }, /* R1676 - OUT1RMIX Input 3 Source */ + { 0x068D, 0x0080 }, /* R1677 - OUT1RMIX Input 3 Volume */ + { 0x068E, 0x0000 }, /* R1678 - OUT1RMIX Input 4 Source */ + { 0x068F, 0x0080 }, /* R1679 - OUT1RMIX Input 4 Volume */ + { 0x0690, 0x0000 }, /* R1680 - OUT2LMIX Input 1 Source */ + { 0x0691, 0x0080 }, /* R1681 - OUT2LMIX Input 1 Volume */ + { 0x0692, 0x0000 }, /* R1682 - OUT2LMIX Input 2 Source */ + { 0x0693, 0x0080 }, /* R1683 - OUT2LMIX Input 2 Volume */ + { 0x0694, 0x0000 }, /* R1684 - OUT2LMIX Input 3 Source */ + { 0x0695, 0x0080 }, /* R1685 - OUT2LMIX Input 3 Volume */ + { 0x0696, 0x0000 }, /* R1686 - OUT2LMIX Input 4 Source */ + { 0x0697, 0x0080 }, /* R1687 - OUT2LMIX Input 4 Volume */ + { 0x0698, 0x0000 }, /* R1688 - OUT2RMIX Input 1 Source */ + { 0x0699, 0x0080 }, /* R1689 - OUT2RMIX Input 1 Volume */ + { 0x069A, 0x0000 }, /* R1690 - OUT2RMIX Input 2 Source */ + { 0x069B, 0x0080 }, /* R1691 - OUT2RMIX Input 2 Volume */ + { 0x069C, 0x0000 }, /* R1692 - OUT2RMIX Input 3 Source */ + { 0x069D, 0x0080 }, /* R1693 - OUT2RMIX Input 3 Volume */ + { 0x069E, 0x0000 }, /* R1694 - OUT2RMIX Input 4 Source */ + { 0x069F, 0x0080 }, /* R1695 - OUT2RMIX Input 4 Volume */ + { 0x06A0, 0x0000 }, /* R1696 - OUT3LMIX Input 1 Source */ + { 0x06A1, 0x0080 }, /* R1697 - OUT3LMIX Input 1 Volume */ + { 0x06A2, 0x0000 }, /* R1698 - OUT3LMIX Input 2 Source */ + { 0x06A3, 0x0080 }, /* R1699 - OUT3LMIX Input 2 Volume */ + { 0x06A4, 0x0000 }, /* R1700 - OUT3LMIX Input 3 Source */ + { 0x06A5, 0x0080 }, /* R1701 - OUT3LMIX Input 3 Volume */ + { 0x06A6, 0x0000 }, /* R1702 - OUT3LMIX Input 4 Source */ + { 0x06A7, 0x0080 }, /* R1703 - OUT3LMIX Input 4 Volume */ + { 0x06A8, 0x0000 }, /* R1704 - OUT3RMIX Input 1 Source */ + { 0x06A9, 0x0080 }, /* R1705 - OUT3RMIX Input 1 Volume */ + { 0x06AA, 0x0000 }, /* R1706 - OUT3RMIX Input 2 Source */ + { 0x06AB, 0x0080 }, /* R1707 - OUT3RMIX Input 2 Volume */ + { 0x06AC, 0x0000 }, /* R1708 - OUT3RMIX Input 3 Source */ + { 0x06AD, 0x0080 }, /* R1709 - OUT3RMIX Input 3 Volume */ + { 0x06AE, 0x0000 }, /* R1710 - OUT3RMIX Input 4 Source */ + { 0x06AF, 0x0080 }, /* R1711 - OUT3RMIX Input 4 Volume */ + { 0x06B0, 0x0000 }, /* R1712 - OUT4LMIX Input 1 Source */ + { 0x06B1, 0x0080 }, /* R1713 - OUT4LMIX Input 1 Volume */ + { 0x06B2, 0x0000 }, /* R1714 - OUT4LMIX Input 2 Source */ + { 0x06B3, 0x0080 }, /* R1715 - OUT4LMIX Input 2 Volume */ + { 0x06B4, 0x0000 }, /* R1716 - OUT4LMIX Input 3 Source */ + { 0x06B5, 0x0080 }, /* R1717 - OUT4LMIX Input 3 Volume */ + { 0x06B6, 0x0000 }, /* R1718 - OUT4LMIX Input 4 Source */ + { 0x06B7, 0x0080 }, /* R1719 - OUT4LMIX Input 4 Volume */ + { 0x06B8, 0x0000 }, /* R1720 - OUT4RMIX Input 1 Source */ + { 0x06B9, 0x0080 }, /* R1721 - OUT4RMIX Input 1 Volume */ + { 0x06BA, 0x0000 }, /* R1722 - OUT4RMIX Input 2 Source */ + { 0x06BB, 0x0080 }, /* R1723 - OUT4RMIX Input 2 Volume */ + { 0x06BC, 0x0000 }, /* R1724 - OUT4RMIX Input 3 Source */ + { 0x06BD, 0x0080 }, /* R1725 - OUT4RMIX Input 3 Volume */ + { 0x06BE, 0x0000 }, /* R1726 - OUT4RMIX Input 4 Source */ + { 0x06BF, 0x0080 }, /* R1727 - OUT4RMIX Input 4 Volume */ + { 0x06C0, 0x0000 }, /* R1728 - OUT5LMIX Input 1 Source */ + { 0x06C1, 0x0080 }, /* R1729 - OUT5LMIX Input 1 Volume */ + { 0x06C2, 0x0000 }, /* R1730 - OUT5LMIX Input 2 Source */ + { 0x06C3, 0x0080 }, /* R1731 - OUT5LMIX Input 2 Volume */ + { 0x06C4, 0x0000 }, /* R1732 - OUT5LMIX Input 3 Source */ + { 0x06C5, 0x0080 }, /* R1733 - OUT5LMIX Input 3 Volume */ + { 0x06C6, 0x0000 }, /* R1734 - OUT5LMIX Input 4 Source */ + { 0x06C7, 0x0080 }, /* R1735 - OUT5LMIX Input 4 Volume */ + { 0x06C8, 0x0000 }, /* R1736 - OUT5RMIX Input 1 Source */ + { 0x06C9, 0x0080 }, /* R1737 - OUT5RMIX Input 1 Volume */ + { 0x06CA, 0x0000 }, /* R1738 - OUT5RMIX Input 2 Source */ + { 0x06CB, 0x0080 }, /* R1739 - OUT5RMIX Input 2 Volume */ + { 0x06CC, 0x0000 }, /* R1740 - OUT5RMIX Input 3 Source */ + { 0x06CD, 0x0080 }, /* R1741 - OUT5RMIX Input 3 Volume */ + { 0x06CE, 0x0000 }, /* R1742 - OUT5RMIX Input 4 Source */ + { 0x06CF, 0x0080 }, /* R1743 - OUT5RMIX Input 4 Volume */ + { 0x06D0, 0x0000 }, /* R1744 - OUT6LMIX Input 1 Source */ + { 0x06D1, 0x0080 }, /* R1745 - OUT6LMIX Input 1 Volume */ + { 0x06D2, 0x0000 }, /* R1746 - OUT6LMIX Input 2 Source */ + { 0x06D3, 0x0080 }, /* R1747 - OUT6LMIX Input 2 Volume */ + { 0x06D4, 0x0000 }, /* R1748 - OUT6LMIX Input 3 Source */ + { 0x06D5, 0x0080 }, /* R1749 - OUT6LMIX Input 3 Volume */ + { 0x06D6, 0x0000 }, /* R1750 - OUT6LMIX Input 4 Source */ + { 0x06D7, 0x0080 }, /* R1751 - OUT6LMIX Input 4 Volume */ + { 0x06D8, 0x0000 }, /* R1752 - OUT6RMIX Input 1 Source */ + { 0x06D9, 0x0080 }, /* R1753 - OUT6RMIX Input 1 Volume */ + { 0x06DA, 0x0000 }, /* R1754 - OUT6RMIX Input 2 Source */ + { 0x06DB, 0x0080 }, /* R1755 - OUT6RMIX Input 2 Volume */ + { 0x06DC, 0x0000 }, /* R1756 - OUT6RMIX Input 3 Source */ + { 0x06DD, 0x0080 }, /* R1757 - OUT6RMIX Input 3 Volume */ + { 0x06DE, 0x0000 }, /* R1758 - OUT6RMIX Input 4 Source */ + { 0x06DF, 0x0080 }, /* R1759 - OUT6RMIX Input 4 Volume */ + { 0x0700, 0x0000 }, /* R1792 - AIF1TX1MIX Input 1 Source */ + { 0x0701, 0x0080 }, /* R1793 - AIF1TX1MIX Input 1 Volume */ + { 0x0702, 0x0000 }, /* R1794 - AIF1TX1MIX Input 2 Source */ + { 0x0703, 0x0080 }, /* R1795 - AIF1TX1MIX Input 2 Volume */ + { 0x0704, 0x0000 }, /* R1796 - AIF1TX1MIX Input 3 Source */ + { 0x0705, 0x0080 }, /* R1797 - AIF1TX1MIX Input 3 Volume */ + { 0x0706, 0x0000 }, /* R1798 - AIF1TX1MIX Input 4 Source */ + { 0x0707, 0x0080 }, /* R1799 - AIF1TX1MIX Input 4 Volume */ + { 0x0708, 0x0000 }, /* R1800 - AIF1TX2MIX Input 1 Source */ + { 0x0709, 0x0080 }, /* R1801 - AIF1TX2MIX Input 1 Volume */ + { 0x070A, 0x0000 }, /* R1802 - AIF1TX2MIX Input 2 Source */ + { 0x070B, 0x0080 }, /* R1803 - AIF1TX2MIX Input 2 Volume */ + { 0x070C, 0x0000 }, /* R1804 - AIF1TX2MIX Input 3 Source */ + { 0x070D, 0x0080 }, /* R1805 - AIF1TX2MIX Input 3 Volume */ + { 0x070E, 0x0000 }, /* R1806 - AIF1TX2MIX Input 4 Source */ + { 0x070F, 0x0080 }, /* R1807 - AIF1TX2MIX Input 4 Volume */ + { 0x0710, 0x0000 }, /* R1808 - AIF1TX3MIX Input 1 Source */ + { 0x0711, 0x0080 }, /* R1809 - AIF1TX3MIX Input 1 Volume */ + { 0x0712, 0x0000 }, /* R1810 - AIF1TX3MIX Input 2 Source */ + { 0x0713, 0x0080 }, /* R1811 - AIF1TX3MIX Input 2 Volume */ + { 0x0714, 0x0000 }, /* R1812 - AIF1TX3MIX Input 3 Source */ + { 0x0715, 0x0080 }, /* R1813 - AIF1TX3MIX Input 3 Volume */ + { 0x0716, 0x0000 }, /* R1814 - AIF1TX3MIX Input 4 Source */ + { 0x0717, 0x0080 }, /* R1815 - AIF1TX3MIX Input 4 Volume */ + { 0x0718, 0x0000 }, /* R1816 - AIF1TX4MIX Input 1 Source */ + { 0x0719, 0x0080 }, /* R1817 - AIF1TX4MIX Input 1 Volume */ + { 0x071A, 0x0000 }, /* R1818 - AIF1TX4MIX Input 2 Source */ + { 0x071B, 0x0080 }, /* R1819 - AIF1TX4MIX Input 2 Volume */ + { 0x071C, 0x0000 }, /* R1820 - AIF1TX4MIX Input 3 Source */ + { 0x071D, 0x0080 }, /* R1821 - AIF1TX4MIX Input 3 Volume */ + { 0x071E, 0x0000 }, /* R1822 - AIF1TX4MIX Input 4 Source */ + { 0x071F, 0x0080 }, /* R1823 - AIF1TX4MIX Input 4 Volume */ + { 0x0720, 0x0000 }, /* R1824 - AIF1TX5MIX Input 1 Source */ + { 0x0721, 0x0080 }, /* R1825 - AIF1TX5MIX Input 1 Volume */ + { 0x0722, 0x0000 }, /* R1826 - AIF1TX5MIX Input 2 Source */ + { 0x0723, 0x0080 }, /* R1827 - AIF1TX5MIX Input 2 Volume */ + { 0x0724, 0x0000 }, /* R1828 - AIF1TX5MIX Input 3 Source */ + { 0x0725, 0x0080 }, /* R1829 - AIF1TX5MIX Input 3 Volume */ + { 0x0726, 0x0000 }, /* R1830 - AIF1TX5MIX Input 4 Source */ + { 0x0727, 0x0080 }, /* R1831 - AIF1TX5MIX Input 4 Volume */ + { 0x0728, 0x0000 }, /* R1832 - AIF1TX6MIX Input 1 Source */ + { 0x0729, 0x0080 }, /* R1833 - AIF1TX6MIX Input 1 Volume */ + { 0x072A, 0x0000 }, /* R1834 - AIF1TX6MIX Input 2 Source */ + { 0x072B, 0x0080 }, /* R1835 - AIF1TX6MIX Input 2 Volume */ + { 0x072C, 0x0000 }, /* R1836 - AIF1TX6MIX Input 3 Source */ + { 0x072D, 0x0080 }, /* R1837 - AIF1TX6MIX Input 3 Volume */ + { 0x072E, 0x0000 }, /* R1838 - AIF1TX6MIX Input 4 Source */ + { 0x072F, 0x0080 }, /* R1839 - AIF1TX6MIX Input 4 Volume */ + { 0x0730, 0x0000 }, /* R1840 - AIF1TX7MIX Input 1 Source */ + { 0x0731, 0x0080 }, /* R1841 - AIF1TX7MIX Input 1 Volume */ + { 0x0732, 0x0000 }, /* R1842 - AIF1TX7MIX Input 2 Source */ + { 0x0733, 0x0080 }, /* R1843 - AIF1TX7MIX Input 2 Volume */ + { 0x0734, 0x0000 }, /* R1844 - AIF1TX7MIX Input 3 Source */ + { 0x0735, 0x0080 }, /* R1845 - AIF1TX7MIX Input 3 Volume */ + { 0x0736, 0x0000 }, /* R1846 - AIF1TX7MIX Input 4 Source */ + { 0x0737, 0x0080 }, /* R1847 - AIF1TX7MIX Input 4 Volume */ + { 0x0738, 0x0000 }, /* R1848 - AIF1TX8MIX Input 1 Source */ + { 0x0739, 0x0080 }, /* R1849 - AIF1TX8MIX Input 1 Volume */ + { 0x073A, 0x0000 }, /* R1850 - AIF1TX8MIX Input 2 Source */ + { 0x073B, 0x0080 }, /* R1851 - AIF1TX8MIX Input 2 Volume */ + { 0x073C, 0x0000 }, /* R1852 - AIF1TX8MIX Input 3 Source */ + { 0x073D, 0x0080 }, /* R1853 - AIF1TX8MIX Input 3 Volume */ + { 0x073E, 0x0000 }, /* R1854 - AIF1TX8MIX Input 4 Source */ + { 0x073F, 0x0080 }, /* R1855 - AIF1TX8MIX Input 4 Volume */ + { 0x0740, 0x0000 }, /* R1856 - AIF2TX1MIX Input 1 Source */ + { 0x0741, 0x0080 }, /* R1857 - AIF2TX1MIX Input 1 Volume */ + { 0x0742, 0x0000 }, /* R1858 - AIF2TX1MIX Input 2 Source */ + { 0x0743, 0x0080 }, /* R1859 - AIF2TX1MIX Input 2 Volume */ + { 0x0744, 0x0000 }, /* R1860 - AIF2TX1MIX Input 3 Source */ + { 0x0745, 0x0080 }, /* R1861 - AIF2TX1MIX Input 3 Volume */ + { 0x0746, 0x0000 }, /* R1862 - AIF2TX1MIX Input 4 Source */ + { 0x0747, 0x0080 }, /* R1863 - AIF2TX1MIX Input 4 Volume */ + { 0x0748, 0x0000 }, /* R1864 - AIF2TX2MIX Input 1 Source */ + { 0x0749, 0x0080 }, /* R1865 - AIF2TX2MIX Input 1 Volume */ + { 0x074A, 0x0000 }, /* R1866 - AIF2TX2MIX Input 2 Source */ + { 0x074B, 0x0080 }, /* R1867 - AIF2TX2MIX Input 2 Volume */ + { 0x074C, 0x0000 }, /* R1868 - AIF2TX2MIX Input 3 Source */ + { 0x074D, 0x0080 }, /* R1869 - AIF2TX2MIX Input 3 Volume */ + { 0x074E, 0x0000 }, /* R1870 - AIF2TX2MIX Input 4 Source */ + { 0x074F, 0x0080 }, /* R1871 - AIF2TX2MIX Input 4 Volume */ + { 0x0780, 0x0000 }, /* R1920 - AIF3TX1MIX Input 1 Source */ + { 0x0781, 0x0080 }, /* R1921 - AIF3TX1MIX Input 1 Volume */ + { 0x0782, 0x0000 }, /* R1922 - AIF3TX1MIX Input 2 Source */ + { 0x0783, 0x0080 }, /* R1923 - AIF3TX1MIX Input 2 Volume */ + { 0x0784, 0x0000 }, /* R1924 - AIF3TX1MIX Input 3 Source */ + { 0x0785, 0x0080 }, /* R1925 - AIF3TX1MIX Input 3 Volume */ + { 0x0786, 0x0000 }, /* R1926 - AIF3TX1MIX Input 4 Source */ + { 0x0787, 0x0080 }, /* R1927 - AIF3TX1MIX Input 4 Volume */ + { 0x0788, 0x0000 }, /* R1928 - AIF3TX2MIX Input 1 Source */ + { 0x0789, 0x0080 }, /* R1929 - AIF3TX2MIX Input 1 Volume */ + { 0x078A, 0x0000 }, /* R1930 - AIF3TX2MIX Input 2 Source */ + { 0x078B, 0x0080 }, /* R1931 - AIF3TX2MIX Input 2 Volume */ + { 0x078C, 0x0000 }, /* R1932 - AIF3TX2MIX Input 3 Source */ + { 0x078D, 0x0080 }, /* R1933 - AIF3TX2MIX Input 3 Volume */ + { 0x078E, 0x0000 }, /* R1934 - AIF3TX2MIX Input 4 Source */ + { 0x078F, 0x0080 }, /* R1935 - AIF3TX2MIX Input 4 Volume */ + { 0x0880, 0x0000 }, /* R2176 - EQ1MIX Input 1 Source */ + { 0x0881, 0x0080 }, /* R2177 - EQ1MIX Input 1 Volume */ + { 0x0882, 0x0000 }, /* R2178 - EQ1MIX Input 2 Source */ + { 0x0883, 0x0080 }, /* R2179 - EQ1MIX Input 2 Volume */ + { 0x0884, 0x0000 }, /* R2180 - EQ1MIX Input 3 Source */ + { 0x0885, 0x0080 }, /* R2181 - EQ1MIX Input 3 Volume */ + { 0x0886, 0x0000 }, /* R2182 - EQ1MIX Input 4 Source */ + { 0x0887, 0x0080 }, /* R2183 - EQ1MIX Input 4 Volume */ + { 0x0888, 0x0000 }, /* R2184 - EQ2MIX Input 1 Source */ + { 0x0889, 0x0080 }, /* R2185 - EQ2MIX Input 1 Volume */ + { 0x088A, 0x0000 }, /* R2186 - EQ2MIX Input 2 Source */ + { 0x088B, 0x0080 }, /* R2187 - EQ2MIX Input 2 Volume */ + { 0x088C, 0x0000 }, /* R2188 - EQ2MIX Input 3 Source */ + { 0x088D, 0x0080 }, /* R2189 - EQ2MIX Input 3 Volume */ + { 0x088E, 0x0000 }, /* R2190 - EQ2MIX Input 4 Source */ + { 0x088F, 0x0080 }, /* R2191 - EQ2MIX Input 4 Volume */ + { 0x0890, 0x0000 }, /* R2192 - EQ3MIX Input 1 Source */ + { 0x0891, 0x0080 }, /* R2193 - EQ3MIX Input 1 Volume */ + { 0x0892, 0x0000 }, /* R2194 - EQ3MIX Input 2 Source */ + { 0x0893, 0x0080 }, /* R2195 - EQ3MIX Input 2 Volume */ + { 0x0894, 0x0000 }, /* R2196 - EQ3MIX Input 3 Source */ + { 0x0895, 0x0080 }, /* R2197 - EQ3MIX Input 3 Volume */ + { 0x0896, 0x0000 }, /* R2198 - EQ3MIX Input 4 Source */ + { 0x0897, 0x0080 }, /* R2199 - EQ3MIX Input 4 Volume */ + { 0x0898, 0x0000 }, /* R2200 - EQ4MIX Input 1 Source */ + { 0x0899, 0x0080 }, /* R2201 - EQ4MIX Input 1 Volume */ + { 0x089A, 0x0000 }, /* R2202 - EQ4MIX Input 2 Source */ + { 0x089B, 0x0080 }, /* R2203 - EQ4MIX Input 2 Volume */ + { 0x089C, 0x0000 }, /* R2204 - EQ4MIX Input 3 Source */ + { 0x089D, 0x0080 }, /* R2205 - EQ4MIX Input 3 Volume */ + { 0x089E, 0x0000 }, /* R2206 - EQ4MIX Input 4 Source */ + { 0x089F, 0x0080 }, /* R2207 - EQ4MIX Input 4 Volume */ + { 0x08C0, 0x0000 }, /* R2240 - DRC1LMIX Input 1 Source */ + { 0x08C1, 0x0080 }, /* R2241 - DRC1LMIX Input 1 Volume */ + { 0x08C2, 0x0000 }, /* R2242 - DRC1LMIX Input 2 Source */ + { 0x08C3, 0x0080 }, /* R2243 - DRC1LMIX Input 2 Volume */ + { 0x08C4, 0x0000 }, /* R2244 - DRC1LMIX Input 3 Source */ + { 0x08C5, 0x0080 }, /* R2245 - DRC1LMIX Input 3 Volume */ + { 0x08C6, 0x0000 }, /* R2246 - DRC1LMIX Input 4 Source */ + { 0x08C7, 0x0080 }, /* R2247 - DRC1LMIX Input 4 Volume */ + { 0x08C8, 0x0000 }, /* R2248 - DRC1RMIX Input 1 Source */ + { 0x08C9, 0x0080 }, /* R2249 - DRC1RMIX Input 1 Volume */ + { 0x08CA, 0x0000 }, /* R2250 - DRC1RMIX Input 2 Source */ + { 0x08CB, 0x0080 }, /* R2251 - DRC1RMIX Input 2 Volume */ + { 0x08CC, 0x0000 }, /* R2252 - DRC1RMIX Input 3 Source */ + { 0x08CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */ + { 0x08CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */ + { 0x08CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */ + { 0x0900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */ + { 0x0901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */ + { 0x0902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */ + { 0x0903, 0x0080 }, /* R2307 - HPLP1MIX Input 2 Volume */ + { 0x0904, 0x0000 }, /* R2308 - HPLP1MIX Input 3 Source */ + { 0x0905, 0x0080 }, /* R2309 - HPLP1MIX Input 3 Volume */ + { 0x0906, 0x0000 }, /* R2310 - HPLP1MIX Input 4 Source */ + { 0x0907, 0x0080 }, /* R2311 - HPLP1MIX Input 4 Volume */ + { 0x0908, 0x0000 }, /* R2312 - HPLP2MIX Input 1 Source */ + { 0x0909, 0x0080 }, /* R2313 - HPLP2MIX Input 1 Volume */ + { 0x090A, 0x0000 }, /* R2314 - HPLP2MIX Input 2 Source */ + { 0x090B, 0x0080 }, /* R2315 - HPLP2MIX Input 2 Volume */ + { 0x090C, 0x0000 }, /* R2316 - HPLP2MIX Input 3 Source */ + { 0x090D, 0x0080 }, /* R2317 - HPLP2MIX Input 3 Volume */ + { 0x090E, 0x0000 }, /* R2318 - HPLP2MIX Input 4 Source */ + { 0x090F, 0x0080 }, /* R2319 - HPLP2MIX Input 4 Volume */ + { 0x0910, 0x0000 }, /* R2320 - HPLP3MIX Input 1 Source */ + { 0x0911, 0x0080 }, /* R2321 - HPLP3MIX Input 1 Volume */ + { 0x0912, 0x0000 }, /* R2322 - HPLP3MIX Input 2 Source */ + { 0x0913, 0x0080 }, /* R2323 - HPLP3MIX Input 2 Volume */ + { 0x0914, 0x0000 }, /* R2324 - HPLP3MIX Input 3 Source */ + { 0x0915, 0x0080 }, /* R2325 - HPLP3MIX Input 3 Volume */ + { 0x0916, 0x0000 }, /* R2326 - HPLP3MIX Input 4 Source */ + { 0x0917, 0x0080 }, /* R2327 - HPLP3MIX Input 4 Volume */ + { 0x0918, 0x0000 }, /* R2328 - HPLP4MIX Input 1 Source */ + { 0x0919, 0x0080 }, /* R2329 - HPLP4MIX Input 1 Volume */ + { 0x091A, 0x0000 }, /* R2330 - HPLP4MIX Input 2 Source */ + { 0x091B, 0x0080 }, /* R2331 - HPLP4MIX Input 2 Volume */ + { 0x091C, 0x0000 }, /* R2332 - HPLP4MIX Input 3 Source */ + { 0x091D, 0x0080 }, /* R2333 - HPLP4MIX Input 3 Volume */ + { 0x091E, 0x0000 }, /* R2334 - HPLP4MIX Input 4 Source */ + { 0x091F, 0x0080 }, /* R2335 - HPLP4MIX Input 4 Volume */ + { 0x0940, 0x0000 }, /* R2368 - DSP1LMIX Input 1 Source */ + { 0x0941, 0x0080 }, /* R2369 - DSP1LMIX Input 1 Volume */ + { 0x0942, 0x0000 }, /* R2370 - DSP1LMIX Input 2 Source */ + { 0x0943, 0x0080 }, /* R2371 - DSP1LMIX Input 2 Volume */ + { 0x0944, 0x0000 }, /* R2372 - DSP1LMIX Input 3 Source */ + { 0x0945, 0x0080 }, /* R2373 - DSP1LMIX Input 3 Volume */ + { 0x0946, 0x0000 }, /* R2374 - DSP1LMIX Input 4 Source */ + { 0x0947, 0x0080 }, /* R2375 - DSP1LMIX Input 4 Volume */ + { 0x0948, 0x0000 }, /* R2376 - DSP1RMIX Input 1 Source */ + { 0x0949, 0x0080 }, /* R2377 - DSP1RMIX Input 1 Volume */ + { 0x094A, 0x0000 }, /* R2378 - DSP1RMIX Input 2 Source */ + { 0x094B, 0x0080 }, /* R2379 - DSP1RMIX Input 2 Volume */ + { 0x094C, 0x0000 }, /* R2380 - DSP1RMIX Input 3 Source */ + { 0x094D, 0x0080 }, /* R2381 - DSP1RMIX Input 3 Volume */ + { 0x094E, 0x0000 }, /* R2382 - DSP1RMIX Input 4 Source */ + { 0x094F, 0x0080 }, /* R2383 - DSP1RMIX Input 4 Volume */ + { 0x0950, 0x0000 }, /* R2384 - DSP1AUX1MIX Input 1 Source */ + { 0x0958, 0x0000 }, /* R2392 - DSP1AUX2MIX Input 1 Source */ + { 0x0960, 0x0000 }, /* R2400 - DSP1AUX3MIX Input 1 Source */ + { 0x0968, 0x0000 }, /* R2408 - DSP1AUX4MIX Input 1 Source */ + { 0x0970, 0x0000 }, /* R2416 - DSP1AUX5MIX Input 1 Source */ + { 0x0978, 0x0000 }, /* R2424 - DSP1AUX6MIX Input 1 Source */ + { 0x0980, 0x0000 }, /* R2432 - DSP2LMIX Input 1 Source */ + { 0x0981, 0x0080 }, /* R2433 - DSP2LMIX Input 1 Volume */ + { 0x0982, 0x0000 }, /* R2434 - DSP2LMIX Input 2 Source */ + { 0x0983, 0x0080 }, /* R2435 - DSP2LMIX Input 2 Volume */ + { 0x0984, 0x0000 }, /* R2436 - DSP2LMIX Input 3 Source */ + { 0x0985, 0x0080 }, /* R2437 - DSP2LMIX Input 3 Volume */ + { 0x0986, 0x0000 }, /* R2438 - DSP2LMIX Input 4 Source */ + { 0x0987, 0x0080 }, /* R2439 - DSP2LMIX Input 4 Volume */ + { 0x0988, 0x0000 }, /* R2440 - DSP2RMIX Input 1 Source */ + { 0x0989, 0x0080 }, /* R2441 - DSP2RMIX Input 1 Volume */ + { 0x098A, 0x0000 }, /* R2442 - DSP2RMIX Input 2 Source */ + { 0x098B, 0x0080 }, /* R2443 - DSP2RMIX Input 2 Volume */ + { 0x098C, 0x0000 }, /* R2444 - DSP2RMIX Input 3 Source */ + { 0x098D, 0x0080 }, /* R2445 - DSP2RMIX Input 3 Volume */ + { 0x098E, 0x0000 }, /* R2446 - DSP2RMIX Input 4 Source */ + { 0x098F, 0x0080 }, /* R2447 - DSP2RMIX Input 4 Volume */ + { 0x0990, 0x0000 }, /* R2448 - DSP2AUX1MIX Input 1 Source */ + { 0x0998, 0x0000 }, /* R2456 - DSP2AUX2MIX Input 1 Source */ + { 0x09A0, 0x0000 }, /* R2464 - DSP2AUX3MIX Input 1 Source */ + { 0x09A8, 0x0000 }, /* R2472 - DSP2AUX4MIX Input 1 Source */ + { 0x09B0, 0x0000 }, /* R2480 - DSP2AUX5MIX Input 1 Source */ + { 0x09B8, 0x0000 }, /* R2488 - DSP2AUX6MIX Input 1 Source */ + { 0x09C0, 0x0000 }, /* R2496 - DSP3LMIX Input 1 Source */ + { 0x09C1, 0x0080 }, /* R2497 - DSP3LMIX Input 1 Volume */ + { 0x09C2, 0x0000 }, /* R2498 - DSP3LMIX Input 2 Source */ + { 0x09C3, 0x0080 }, /* R2499 - DSP3LMIX Input 2 Volume */ + { 0x09C4, 0x0000 }, /* R2500 - DSP3LMIX Input 3 Source */ + { 0x09C5, 0x0080 }, /* R2501 - DSP3LMIX Input 3 Volume */ + { 0x09C6, 0x0000 }, /* R2502 - DSP3LMIX Input 4 Source */ + { 0x09C7, 0x0080 }, /* R2503 - DSP3LMIX Input 4 Volume */ + { 0x09C8, 0x0000 }, /* R2504 - DSP3RMIX Input 1 Source */ + { 0x09C9, 0x0080 }, /* R2505 - DSP3RMIX Input 1 Volume */ + { 0x09CA, 0x0000 }, /* R2506 - DSP3RMIX Input 2 Source */ + { 0x09CB, 0x0080 }, /* R2507 - DSP3RMIX Input 2 Volume */ + { 0x09CC, 0x0000 }, /* R2508 - DSP3RMIX Input 3 Source */ + { 0x09CD, 0x0080 }, /* R2509 - DSP3RMIX Input 3 Volume */ + { 0x09CE, 0x0000 }, /* R2510 - DSP3RMIX Input 4 Source */ + { 0x09CF, 0x0080 }, /* R2511 - DSP3RMIX Input 4 Volume */ + { 0x09D0, 0x0000 }, /* R2512 - DSP3AUX1MIX Input 1 Source */ + { 0x09D8, 0x0000 }, /* R2520 - DSP3AUX2MIX Input 1 Source */ + { 0x09E0, 0x0000 }, /* R2528 - DSP3AUX3MIX Input 1 Source */ + { 0x09E8, 0x0000 }, /* R2536 - DSP3AUX4MIX Input 1 Source */ + { 0x09F0, 0x0000 }, /* R2544 - DSP3AUX5MIX Input 1 Source */ + { 0x09F8, 0x0000 }, /* R2552 - DSP3AUX6MIX Input 1 Source */ + { 0x0A80, 0x0000 }, /* R2688 - ASRC1LMIX Input 1 Source */ + { 0x0A88, 0x0000 }, /* R2696 - ASRC1RMIX Input 1 Source */ + { 0x0A90, 0x0000 }, /* R2704 - ASRC2LMIX Input 1 Source */ + { 0x0A98, 0x0000 }, /* R2712 - ASRC2RMIX Input 1 Source */ + { 0x0B00, 0x0000 }, /* R2816 - ISRC1DEC1MIX Input 1 Source */ + { 0x0B08, 0x0000 }, /* R2824 - ISRC1DEC2MIX Input 1 Source */ + { 0x0B10, 0x0000 }, /* R2832 - ISRC1DEC3MIX Input 1 Source */ + { 0x0B18, 0x0000 }, /* R2840 - ISRC1DEC4MIX Input 1 Source */ + { 0x0B20, 0x0000 }, /* R2848 - ISRC1INT1MIX Input 1 Source */ + { 0x0B28, 0x0000 }, /* R2856 - ISRC1INT2MIX Input 1 Source */ + { 0x0B30, 0x0000 }, /* R2864 - ISRC1INT3MIX Input 1 Source */ + { 0x0B38, 0x0000 }, /* R2872 - ISRC1INT4MIX Input 1 Source */ + { 0x0B40, 0x0000 }, /* R2880 - ISRC2DEC1MIX Input 1 Source */ + { 0x0B48, 0x0000 }, /* R2888 - ISRC2DEC2MIX Input 1 Source */ + { 0x0B50, 0x0000 }, /* R2896 - ISRC2DEC3MIX Input 1 Source */ + { 0x0B58, 0x0000 }, /* R2904 - ISRC2DEC4MIX Input 1 Source */ + { 0x0B60, 0x0000 }, /* R2912 - ISRC2INT1MIX Input 1 Source */ + { 0x0B68, 0x0000 }, /* R2920 - ISRC2INT2MIX Input 1 Source */ + { 0x0B70, 0x0000 }, /* R2928 - ISRC2INT3MIX Input 1 Source */ + { 0x0B78, 0x0000 }, /* R2936 - ISRC2INT4MIX Input 1 Source */ + { 0x0C00, 0xA001 }, /* R3072 - GPIO CTRL 1 */ + { 0x0C01, 0xA001 }, /* R3073 - GPIO CTRL 2 */ + { 0x0C02, 0xA001 }, /* R3074 - GPIO CTRL 3 */ + { 0x0C03, 0xA001 }, /* R3075 - GPIO CTRL 4 */ + { 0x0C04, 0xA001 }, /* R3076 - GPIO CTRL 5 */ + { 0x0C05, 0xA001 }, /* R3077 - GPIO CTRL 6 */ + { 0x0C23, 0x4003 }, /* R3107 - Misc Pad Ctrl 1 */ + { 0x0C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 2 */ + { 0x0C25, 0x0000 }, /* R3109 - Misc Pad Ctrl 3 */ + { 0x0C26, 0x0000 }, /* R3110 - Misc Pad Ctrl 4 */ + { 0x0C27, 0x0000 }, /* R3111 - Misc Pad Ctrl 5 */ + { 0x0C28, 0x0000 }, /* R3112 - Misc GPIO 1 */ + { 0x0D00, 0x0000 }, /* R3328 - Interrupt Status 1 */ + { 0x0D01, 0x0000 }, /* R3329 - Interrupt Status 2 */ + { 0x0D02, 0x0000 }, /* R3330 - Interrupt Status 3 */ + { 0x0D03, 0x0000 }, /* R3331 - Interrupt Status 4 */ + { 0x0D04, 0x0000 }, /* R3332 - Interrupt Raw Status 2 */ + { 0x0D05, 0x0000 }, /* R3333 - Interrupt Raw Status 3 */ + { 0x0D06, 0x0000 }, /* R3334 - Interrupt Raw Status 4 */ + { 0x0D07, 0xFFFF }, /* R3335 - Interrupt Status 1 Mask */ + { 0x0D08, 0xFFFF }, /* R3336 - Interrupt Status 2 Mask */ + { 0x0D09, 0xFFFF }, /* R3337 - Interrupt Status 3 Mask */ + { 0x0D0A, 0xFFFF }, /* R3338 - Interrupt Status 4 Mask */ + { 0x0D1F, 0x0000 }, /* R3359 - Interrupt Control */ + { 0x0D20, 0xFFFF }, /* R3360 - IRQ Debounce 1 */ + { 0x0D21, 0xFFFF }, /* R3361 - IRQ Debounce 2 */ + { 0x0E00, 0x0000 }, /* R3584 - FX_Ctrl */ + { 0x0E10, 0x6318 }, /* R3600 - EQ1_1 */ + { 0x0E11, 0x6300 }, /* R3601 - EQ1_2 */ + { 0x0E12, 0x0FC8 }, /* R3602 - EQ1_3 */ + { 0x0E13, 0x03FE }, /* R3603 - EQ1_4 */ + { 0x0E14, 0x00E0 }, /* R3604 - EQ1_5 */ + { 0x0E15, 0x1EC4 }, /* R3605 - EQ1_6 */ + { 0x0E16, 0xF136 }, /* R3606 - EQ1_7 */ + { 0x0E17, 0x0409 }, /* R3607 - EQ1_8 */ + { 0x0E18, 0x04CC }, /* R3608 - EQ1_9 */ + { 0x0E19, 0x1C9B }, /* R3609 - EQ1_10 */ + { 0x0E1A, 0xF337 }, /* R3610 - EQ1_11 */ + { 0x0E1B, 0x040B }, /* R3611 - EQ1_12 */ + { 0x0E1C, 0x0CBB }, /* R3612 - EQ1_13 */ + { 0x0E1D, 0x16F8 }, /* R3613 - EQ1_14 */ + { 0x0E1E, 0xF7D9 }, /* R3614 - EQ1_15 */ + { 0x0E1F, 0x040A }, /* R3615 - EQ1_16 */ + { 0x0E20, 0x1F14 }, /* R3616 - EQ1_17 */ + { 0x0E21, 0x058C }, /* R3617 - EQ1_18 */ + { 0x0E22, 0x0563 }, /* R3618 - EQ1_19 */ + { 0x0E23, 0x4000 }, /* R3619 - EQ1_20 */ + { 0x0E26, 0x6318 }, /* R3622 - EQ2_1 */ + { 0x0E27, 0x6300 }, /* R3623 - EQ2_2 */ + { 0x0E28, 0x0FC8 }, /* R3624 - EQ2_3 */ + { 0x0E29, 0x03FE }, /* R3625 - EQ2_4 */ + { 0x0E2A, 0x00E0 }, /* R3626 - EQ2_5 */ + { 0x0E2B, 0x1EC4 }, /* R3627 - EQ2_6 */ + { 0x0E2C, 0xF136 }, /* R3628 - EQ2_7 */ + { 0x0E2D, 0x0409 }, /* R3629 - EQ2_8 */ + { 0x0E2E, 0x04CC }, /* R3630 - EQ2_9 */ + { 0x0E2F, 0x1C9B }, /* R3631 - EQ2_10 */ + { 0x0E30, 0xF337 }, /* R3632 - EQ2_11 */ + { 0x0E31, 0x040B }, /* R3633 - EQ2_12 */ + { 0x0E32, 0x0CBB }, /* R3634 - EQ2_13 */ + { 0x0E33, 0x16F8 }, /* R3635 - EQ2_14 */ + { 0x0E34, 0xF7D9 }, /* R3636 - EQ2_15 */ + { 0x0E35, 0x040A }, /* R3637 - EQ2_16 */ + { 0x0E36, 0x1F14 }, /* R3638 - EQ2_17 */ + { 0x0E37, 0x058C }, /* R3639 - EQ2_18 */ + { 0x0E38, 0x0563 }, /* R3640 - EQ2_19 */ + { 0x0E39, 0x4000 }, /* R3641 - EQ2_20 */ + { 0x0E3C, 0x6318 }, /* R3644 - EQ3_1 */ + { 0x0E3D, 0x6300 }, /* R3645 - EQ3_2 */ + { 0x0E3E, 0x0FC8 }, /* R3646 - EQ3_3 */ + { 0x0E3F, 0x03FE }, /* R3647 - EQ3_4 */ + { 0x0E40, 0x00E0 }, /* R3648 - EQ3_5 */ + { 0x0E41, 0x1EC4 }, /* R3649 - EQ3_6 */ + { 0x0E42, 0xF136 }, /* R3650 - EQ3_7 */ + { 0x0E43, 0x0409 }, /* R3651 - EQ3_8 */ + { 0x0E44, 0x04CC }, /* R3652 - EQ3_9 */ + { 0x0E45, 0x1C9B }, /* R3653 - EQ3_10 */ + { 0x0E46, 0xF337 }, /* R3654 - EQ3_11 */ + { 0x0E47, 0x040B }, /* R3655 - EQ3_12 */ + { 0x0E48, 0x0CBB }, /* R3656 - EQ3_13 */ + { 0x0E49, 0x16F8 }, /* R3657 - EQ3_14 */ + { 0x0E4A, 0xF7D9 }, /* R3658 - EQ3_15 */ + { 0x0E4B, 0x040A }, /* R3659 - EQ3_16 */ + { 0x0E4C, 0x1F14 }, /* R3660 - EQ3_17 */ + { 0x0E4D, 0x058C }, /* R3661 - EQ3_18 */ + { 0x0E4E, 0x0563 }, /* R3662 - EQ3_19 */ + { 0x0E4F, 0x4000 }, /* R3663 - EQ3_20 */ + { 0x0E52, 0x6318 }, /* R3666 - EQ4_1 */ + { 0x0E53, 0x6300 }, /* R3667 - EQ4_2 */ + { 0x0E54, 0x0FC8 }, /* R3668 - EQ4_3 */ + { 0x0E55, 0x03FE }, /* R3669 - EQ4_4 */ + { 0x0E56, 0x00E0 }, /* R3670 - EQ4_5 */ + { 0x0E57, 0x1EC4 }, /* R3671 - EQ4_6 */ + { 0x0E58, 0xF136 }, /* R3672 - EQ4_7 */ + { 0x0E59, 0x0409 }, /* R3673 - EQ4_8 */ + { 0x0E5A, 0x04CC }, /* R3674 - EQ4_9 */ + { 0x0E5B, 0x1C9B }, /* R3675 - EQ4_10 */ + { 0x0E5C, 0xF337 }, /* R3676 - EQ4_11 */ + { 0x0E5D, 0x040B }, /* R3677 - EQ4_12 */ + { 0x0E5E, 0x0CBB }, /* R3678 - EQ4_13 */ + { 0x0E5F, 0x16F8 }, /* R3679 - EQ4_14 */ + { 0x0E60, 0xF7D9 }, /* R3680 - EQ4_15 */ + { 0x0E61, 0x040A }, /* R3681 - EQ4_16 */ + { 0x0E62, 0x1F14 }, /* R3682 - EQ4_17 */ + { 0x0E63, 0x058C }, /* R3683 - EQ4_18 */ + { 0x0E64, 0x0563 }, /* R3684 - EQ4_19 */ + { 0x0E65, 0x4000 }, /* R3685 - EQ4_20 */ + { 0x0E80, 0x0018 }, /* R3712 - DRC1 ctrl1 */ + { 0x0E81, 0x0933 }, /* R3713 - DRC1 ctrl2 */ + { 0x0E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */ + { 0x0E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */ + { 0x0E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */ + { 0x0EC0, 0x0000 }, /* R3776 - HPLPF1_1 */ + { 0x0EC1, 0x0000 }, /* R3777 - HPLPF1_2 */ + { 0x0EC4, 0x0000 }, /* R3780 - HPLPF2_1 */ + { 0x0EC5, 0x0000 }, /* R3781 - HPLPF2_2 */ + { 0x0EC8, 0x0000 }, /* R3784 - HPLPF3_1 */ + { 0x0EC9, 0x0000 }, /* R3785 - HPLPF3_2 */ + { 0x0ECC, 0x0000 }, /* R3788 - HPLPF4_1 */ + { 0x0ECD, 0x0000 }, /* R3789 - HPLPF4_2 */ + { 0x0F02, 0x0000 }, /* R3842 - DSP1 Control 2 */ + { 0x0F03, 0x0000 }, /* R3843 - DSP1 Control 3 */ + { 0x0F04, 0x0000 }, /* R3844 - DSP1 Control 4 */ + { 0x1002, 0x0000 }, /* R4098 - DSP2 Control 2 */ + { 0x1003, 0x0000 }, /* R4099 - DSP2 Control 3 */ + { 0x1004, 0x0000 }, /* R4100 - DSP2 Control 4 */ + { 0x1102, 0x0000 }, /* R4354 - DSP3 Control 2 */ + { 0x1103, 0x0000 }, /* R4355 - DSP3 Control 3 */ + { 0x1104, 0x0000 }, /* R4356 - DSP3 Control 4 */ +}; diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c new file mode 100644 index 0000000000..ff63723928 --- /dev/null +++ b/sound/soc/codecs/wm5100.c @@ -0,0 +1,2721 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm5100.c -- WM5100 ALSA SoC Audio driver + * + * Copyright 2011-2 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm5100.h" + +#define WM5100_NUM_CORE_SUPPLIES 2 +static const char *wm5100_core_supply_names[WM5100_NUM_CORE_SUPPLIES] = { + "DBVDD1", + "LDOVDD", /* If DCVDD is supplied externally specify as LDOVDD */ +}; + +#define WM5100_AIFS 3 +#define WM5100_SYNC_SRS 3 + +struct wm5100_fll { + int fref; + int fout; + int src; + struct completion lock; +}; + +/* codec private data */ +struct wm5100_priv { + struct device *dev; + struct regmap *regmap; + struct snd_soc_component *component; + + struct regulator_bulk_data core_supplies[WM5100_NUM_CORE_SUPPLIES]; + + int rev; + + int sysclk; + int asyncclk; + + bool aif_async[WM5100_AIFS]; + bool aif_symmetric[WM5100_AIFS]; + int sr_ref[WM5100_SYNC_SRS]; + + bool out_ena[2]; + + struct snd_soc_jack *jack; + bool jack_detecting; + bool jack_mic; + int jack_mode; + int jack_flips; + + struct wm5100_fll fll[2]; + + struct wm5100_pdata pdata; + +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif +}; + +static int wm5100_sr_code[] = { + 0, + 12000, + 24000, + 48000, + 96000, + 192000, + 384000, + 768000, + 0, + 11025, + 22050, + 44100, + 88200, + 176400, + 352800, + 705600, + 4000, + 8000, + 16000, + 32000, + 64000, + 128000, + 256000, + 512000, +}; + +static int wm5100_sr_regs[WM5100_SYNC_SRS] = { + WM5100_CLOCKING_4, + WM5100_CLOCKING_5, + WM5100_CLOCKING_6, +}; + +static int wm5100_alloc_sr(struct snd_soc_component *component, int rate) +{ + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + int sr_code, sr_free, i; + + for (i = 0; i < ARRAY_SIZE(wm5100_sr_code); i++) + if (wm5100_sr_code[i] == rate) + break; + if (i == ARRAY_SIZE(wm5100_sr_code)) { + dev_err(component->dev, "Unsupported sample rate: %dHz\n", rate); + return -EINVAL; + } + sr_code = i; + + if ((wm5100->sysclk % rate) == 0) { + /* Is this rate already in use? */ + sr_free = -1; + for (i = 0; i < ARRAY_SIZE(wm5100_sr_regs); i++) { + if (!wm5100->sr_ref[i] && sr_free == -1) { + sr_free = i; + continue; + } + if ((snd_soc_component_read(component, wm5100_sr_regs[i]) & + WM5100_SAMPLE_RATE_1_MASK) == sr_code) + break; + } + + if (i < ARRAY_SIZE(wm5100_sr_regs)) { + wm5100->sr_ref[i]++; + dev_dbg(component->dev, "SR %dHz, slot %d, ref %d\n", + rate, i, wm5100->sr_ref[i]); + return i; + } + + if (sr_free == -1) { + dev_err(component->dev, "All SR slots already in use\n"); + return -EBUSY; + } + + dev_dbg(component->dev, "Allocating SR slot %d for %dHz\n", + sr_free, rate); + wm5100->sr_ref[sr_free]++; + snd_soc_component_update_bits(component, wm5100_sr_regs[sr_free], + WM5100_SAMPLE_RATE_1_MASK, + sr_code); + + return sr_free; + + } else { + dev_err(component->dev, + "SR %dHz incompatible with %dHz SYSCLK and %dHz ASYNCCLK\n", + rate, wm5100->sysclk, wm5100->asyncclk); + return -EINVAL; + } +} + +static void wm5100_free_sr(struct snd_soc_component *component, int rate) +{ + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + int i, sr_code; + + for (i = 0; i < ARRAY_SIZE(wm5100_sr_code); i++) + if (wm5100_sr_code[i] == rate) + break; + if (i == ARRAY_SIZE(wm5100_sr_code)) { + dev_err(component->dev, "Unsupported sample rate: %dHz\n", rate); + return; + } + sr_code = wm5100_sr_code[i]; + + for (i = 0; i < ARRAY_SIZE(wm5100_sr_regs); i++) { + if (!wm5100->sr_ref[i]) + continue; + + if ((snd_soc_component_read(component, wm5100_sr_regs[i]) & + WM5100_SAMPLE_RATE_1_MASK) == sr_code) + break; + } + if (i < ARRAY_SIZE(wm5100_sr_regs)) { + wm5100->sr_ref[i]--; + dev_dbg(component->dev, "Dereference SR %dHz, count now %d\n", + rate, wm5100->sr_ref[i]); + } else { + dev_warn(component->dev, "Freeing unreferenced sample rate %dHz\n", + rate); + } +} + +static int wm5100_reset(struct wm5100_priv *wm5100) +{ + if (wm5100->pdata.reset) { + gpio_set_value_cansleep(wm5100->pdata.reset, 0); + gpio_set_value_cansleep(wm5100->pdata.reset, 1); + + return 0; + } else { + return regmap_write(wm5100->regmap, WM5100_SOFTWARE_RESET, 0); + } +} + +static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(mixer_tlv, -3200, 100, 0); +static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); + +static const char *wm5100_mixer_texts[] = { + "None", + "Tone Generator 1", + "Tone Generator 2", + "AEC loopback", + "IN1L", + "IN1R", + "IN2L", + "IN2R", + "IN3L", + "IN3R", + "IN4L", + "IN4R", + "AIF1RX1", + "AIF1RX2", + "AIF1RX3", + "AIF1RX4", + "AIF1RX5", + "AIF1RX6", + "AIF1RX7", + "AIF1RX8", + "AIF2RX1", + "AIF2RX2", + "AIF3RX1", + "AIF3RX2", + "EQ1", + "EQ2", + "EQ3", + "EQ4", + "DRC1L", + "DRC1R", + "LHPF1", + "LHPF2", + "LHPF3", + "LHPF4", + "DSP1.1", + "DSP1.2", + "DSP1.3", + "DSP1.4", + "DSP1.5", + "DSP1.6", + "DSP2.1", + "DSP2.2", + "DSP2.3", + "DSP2.4", + "DSP2.5", + "DSP2.6", + "DSP3.1", + "DSP3.2", + "DSP3.3", + "DSP3.4", + "DSP3.5", + "DSP3.6", + "ASRC1L", + "ASRC1R", + "ASRC2L", + "ASRC2R", + "ISRC1INT1", + "ISRC1INT2", + "ISRC1INT3", + "ISRC1INT4", + "ISRC2INT1", + "ISRC2INT2", + "ISRC2INT3", + "ISRC2INT4", + "ISRC1DEC1", + "ISRC1DEC2", + "ISRC1DEC3", + "ISRC1DEC4", + "ISRC2DEC1", + "ISRC2DEC2", + "ISRC2DEC3", + "ISRC2DEC4", +}; + +static int wm5100_mixer_values[] = { + 0x00, + 0x04, /* Tone */ + 0x05, + 0x08, /* AEC */ + 0x10, /* Input */ + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x20, /* AIF */ + 0x21, + 0x22, + 0x23, + 0x24, + 0x25, + 0x26, + 0x27, + 0x28, + 0x29, + 0x30, /* AIF3 - check */ + 0x31, + 0x50, /* EQ */ + 0x51, + 0x52, + 0x53, + 0x54, + 0x58, /* DRC */ + 0x59, + 0x60, /* LHPF1 */ + 0x61, /* LHPF2 */ + 0x62, /* LHPF3 */ + 0x63, /* LHPF4 */ + 0x68, /* DSP1 */ + 0x69, + 0x6a, + 0x6b, + 0x6c, + 0x6d, + 0x70, /* DSP2 */ + 0x71, + 0x72, + 0x73, + 0x74, + 0x75, + 0x78, /* DSP3 */ + 0x79, + 0x7a, + 0x7b, + 0x7c, + 0x7d, + 0x90, /* ASRC1 */ + 0x91, + 0x92, /* ASRC2 */ + 0x93, + 0xa0, /* ISRC1DEC1 */ + 0xa1, + 0xa2, + 0xa3, + 0xa4, /* ISRC1INT1 */ + 0xa5, + 0xa6, + 0xa7, + 0xa8, /* ISRC2DEC1 */ + 0xa9, + 0xaa, + 0xab, + 0xac, /* ISRC2INT1 */ + 0xad, + 0xae, + 0xaf, +}; + +#define WM5100_MIXER_CONTROLS(name, base) \ + SOC_SINGLE_TLV(name " Input 1 Volume", base + 1 , \ + WM5100_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \ + SOC_SINGLE_TLV(name " Input 2 Volume", base + 3 , \ + WM5100_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \ + SOC_SINGLE_TLV(name " Input 3 Volume", base + 5 , \ + WM5100_MIXER_VOL_SHIFT, 80, 0, mixer_tlv), \ + SOC_SINGLE_TLV(name " Input 4 Volume", base + 7 , \ + WM5100_MIXER_VOL_SHIFT, 80, 0, mixer_tlv) + +#define WM5100_MUX_ENUM_DECL(name, reg) \ + SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff, \ + wm5100_mixer_texts, wm5100_mixer_values) + +#define WM5100_MUX_CTL_DECL(name) \ + const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM("Route", name##_enum) + +#define WM5100_MIXER_ENUMS(name, base_reg) \ + static WM5100_MUX_ENUM_DECL(name##_in1_enum, base_reg); \ + static WM5100_MUX_ENUM_DECL(name##_in2_enum, base_reg + 2); \ + static WM5100_MUX_ENUM_DECL(name##_in3_enum, base_reg + 4); \ + static WM5100_MUX_ENUM_DECL(name##_in4_enum, base_reg + 6); \ + static WM5100_MUX_CTL_DECL(name##_in1); \ + static WM5100_MUX_CTL_DECL(name##_in2); \ + static WM5100_MUX_CTL_DECL(name##_in3); \ + static WM5100_MUX_CTL_DECL(name##_in4) + +WM5100_MIXER_ENUMS(HPOUT1L, WM5100_OUT1LMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(HPOUT1R, WM5100_OUT1RMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(HPOUT2L, WM5100_OUT2LMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(HPOUT2R, WM5100_OUT2RMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(HPOUT3L, WM5100_OUT3LMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(HPOUT3R, WM5100_OUT3RMIX_INPUT_1_SOURCE); + +WM5100_MIXER_ENUMS(SPKOUTL, WM5100_OUT4LMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(SPKOUTR, WM5100_OUT4RMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(SPKDAT1L, WM5100_OUT5LMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(SPKDAT1R, WM5100_OUT5RMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(SPKDAT2L, WM5100_OUT6LMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(SPKDAT2R, WM5100_OUT6RMIX_INPUT_1_SOURCE); + +WM5100_MIXER_ENUMS(PWM1, WM5100_PWM1MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(PWM2, WM5100_PWM1MIX_INPUT_1_SOURCE); + +WM5100_MIXER_ENUMS(AIF1TX1, WM5100_AIF1TX1MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(AIF1TX2, WM5100_AIF1TX2MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(AIF1TX3, WM5100_AIF1TX3MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(AIF1TX4, WM5100_AIF1TX4MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(AIF1TX5, WM5100_AIF1TX5MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(AIF1TX6, WM5100_AIF1TX6MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(AIF1TX7, WM5100_AIF1TX7MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(AIF1TX8, WM5100_AIF1TX8MIX_INPUT_1_SOURCE); + +WM5100_MIXER_ENUMS(AIF2TX1, WM5100_AIF2TX1MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(AIF2TX2, WM5100_AIF2TX2MIX_INPUT_1_SOURCE); + +WM5100_MIXER_ENUMS(AIF3TX1, WM5100_AIF1TX1MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(AIF3TX2, WM5100_AIF1TX2MIX_INPUT_1_SOURCE); + +WM5100_MIXER_ENUMS(EQ1, WM5100_EQ1MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(EQ2, WM5100_EQ2MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(EQ3, WM5100_EQ3MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(EQ4, WM5100_EQ4MIX_INPUT_1_SOURCE); + +WM5100_MIXER_ENUMS(DRC1L, WM5100_DRC1LMIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(DRC1R, WM5100_DRC1RMIX_INPUT_1_SOURCE); + +WM5100_MIXER_ENUMS(LHPF1, WM5100_HPLP1MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(LHPF2, WM5100_HPLP2MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(LHPF3, WM5100_HPLP3MIX_INPUT_1_SOURCE); +WM5100_MIXER_ENUMS(LHPF4, WM5100_HPLP4MIX_INPUT_1_SOURCE); + +#define WM5100_MUX(name, ctrl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl) + +#define WM5100_MIXER_WIDGETS(name, name_str) \ + WM5100_MUX(name_str " Input 1", &name##_in1_mux), \ + WM5100_MUX(name_str " Input 2", &name##_in2_mux), \ + WM5100_MUX(name_str " Input 3", &name##_in3_mux), \ + WM5100_MUX(name_str " Input 4", &name##_in4_mux), \ + SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0) + +#define WM5100_MIXER_INPUT_ROUTES(name) \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "IN3L", "IN3L PGA" }, \ + { name, "IN3R", "IN3R PGA" }, \ + { name, "IN4L", "IN4L PGA" }, \ + { name, "IN4R", "IN4R PGA" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" } + +#define WM5100_MIXER_ROUTES(widget, name) \ + { widget, NULL, name " Mixer" }, \ + { name " Mixer", NULL, name " Input 1" }, \ + { name " Mixer", NULL, name " Input 2" }, \ + { name " Mixer", NULL, name " Input 3" }, \ + { name " Mixer", NULL, name " Input 4" }, \ + WM5100_MIXER_INPUT_ROUTES(name " Input 1"), \ + WM5100_MIXER_INPUT_ROUTES(name " Input 2"), \ + WM5100_MIXER_INPUT_ROUTES(name " Input 3"), \ + WM5100_MIXER_INPUT_ROUTES(name " Input 4") + +static const char *wm5100_lhpf_mode_text[] = { + "Low-pass", "High-pass" +}; + +static SOC_ENUM_SINGLE_DECL(wm5100_lhpf1_mode, + WM5100_HPLPF1_1, WM5100_LHPF1_MODE_SHIFT, + wm5100_lhpf_mode_text); + +static SOC_ENUM_SINGLE_DECL(wm5100_lhpf2_mode, + WM5100_HPLPF2_1, WM5100_LHPF2_MODE_SHIFT, + wm5100_lhpf_mode_text); + +static SOC_ENUM_SINGLE_DECL(wm5100_lhpf3_mode, + WM5100_HPLPF3_1, WM5100_LHPF3_MODE_SHIFT, + wm5100_lhpf_mode_text); + +static SOC_ENUM_SINGLE_DECL(wm5100_lhpf4_mode, + WM5100_HPLPF4_1, WM5100_LHPF4_MODE_SHIFT, + wm5100_lhpf_mode_text); + +static const struct snd_kcontrol_new wm5100_snd_controls[] = { +SOC_SINGLE("IN1 High Performance Switch", WM5100_IN1L_CONTROL, + WM5100_IN1_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN2 High Performance Switch", WM5100_IN2L_CONTROL, + WM5100_IN2_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN3 High Performance Switch", WM5100_IN3L_CONTROL, + WM5100_IN3_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN4 High Performance Switch", WM5100_IN4L_CONTROL, + WM5100_IN4_OSR_SHIFT, 1, 0), + +/* Only applicable for analogue inputs */ +SOC_DOUBLE_R_TLV("IN1 Volume", WM5100_IN1L_CONTROL, WM5100_IN1R_CONTROL, + WM5100_IN1L_PGA_VOL_SHIFT, 94, 0, in_tlv), +SOC_DOUBLE_R_TLV("IN2 Volume", WM5100_IN2L_CONTROL, WM5100_IN2R_CONTROL, + WM5100_IN2L_PGA_VOL_SHIFT, 94, 0, in_tlv), +SOC_DOUBLE_R_TLV("IN3 Volume", WM5100_IN3L_CONTROL, WM5100_IN3R_CONTROL, + WM5100_IN3L_PGA_VOL_SHIFT, 94, 0, in_tlv), +SOC_DOUBLE_R_TLV("IN4 Volume", WM5100_IN4L_CONTROL, WM5100_IN4R_CONTROL, + WM5100_IN4L_PGA_VOL_SHIFT, 94, 0, in_tlv), + +SOC_DOUBLE_R_TLV("IN1 Digital Volume", WM5100_ADC_DIGITAL_VOLUME_1L, + WM5100_ADC_DIGITAL_VOLUME_1R, WM5100_IN1L_VOL_SHIFT, 191, + 0, digital_tlv), +SOC_DOUBLE_R_TLV("IN2 Digital Volume", WM5100_ADC_DIGITAL_VOLUME_2L, + WM5100_ADC_DIGITAL_VOLUME_2R, WM5100_IN2L_VOL_SHIFT, 191, + 0, digital_tlv), +SOC_DOUBLE_R_TLV("IN3 Digital Volume", WM5100_ADC_DIGITAL_VOLUME_3L, + WM5100_ADC_DIGITAL_VOLUME_3R, WM5100_IN3L_VOL_SHIFT, 191, + 0, digital_tlv), +SOC_DOUBLE_R_TLV("IN4 Digital Volume", WM5100_ADC_DIGITAL_VOLUME_4L, + WM5100_ADC_DIGITAL_VOLUME_4R, WM5100_IN4L_VOL_SHIFT, 191, + 0, digital_tlv), + +SOC_DOUBLE_R("IN1 Switch", WM5100_ADC_DIGITAL_VOLUME_1L, + WM5100_ADC_DIGITAL_VOLUME_1R, WM5100_IN1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("IN2 Switch", WM5100_ADC_DIGITAL_VOLUME_2L, + WM5100_ADC_DIGITAL_VOLUME_2R, WM5100_IN2L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("IN3 Switch", WM5100_ADC_DIGITAL_VOLUME_3L, + WM5100_ADC_DIGITAL_VOLUME_3R, WM5100_IN3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("IN4 Switch", WM5100_ADC_DIGITAL_VOLUME_4L, + WM5100_ADC_DIGITAL_VOLUME_4R, WM5100_IN4L_MUTE_SHIFT, 1, 1), + +SND_SOC_BYTES_MASK("EQ1 Coefficients", WM5100_EQ1_1, 20, WM5100_EQ1_ENA), +SND_SOC_BYTES_MASK("EQ2 Coefficients", WM5100_EQ2_1, 20, WM5100_EQ2_ENA), +SND_SOC_BYTES_MASK("EQ3 Coefficients", WM5100_EQ3_1, 20, WM5100_EQ3_ENA), +SND_SOC_BYTES_MASK("EQ4 Coefficients", WM5100_EQ4_1, 20, WM5100_EQ4_ENA), + +SND_SOC_BYTES_MASK("DRC Coefficients", WM5100_DRC1_CTRL1, 5, + WM5100_DRCL_ENA | WM5100_DRCR_ENA), + +SND_SOC_BYTES("LHPF1 Coefficients", WM5100_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficients", WM5100_HPLPF2_2, 1), +SND_SOC_BYTES("LHPF3 Coefficients", WM5100_HPLPF3_2, 1), +SND_SOC_BYTES("LHPF4 Coefficients", WM5100_HPLPF4_2, 1), + +SOC_SINGLE("HPOUT1 High Performance Switch", WM5100_OUT_VOLUME_1L, + WM5100_OUT1_OSR_SHIFT, 1, 0), +SOC_SINGLE("HPOUT2 High Performance Switch", WM5100_OUT_VOLUME_2L, + WM5100_OUT2_OSR_SHIFT, 1, 0), +SOC_SINGLE("HPOUT3 High Performance Switch", WM5100_OUT_VOLUME_3L, + WM5100_OUT3_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKOUT High Performance Switch", WM5100_OUT_VOLUME_4L, + WM5100_OUT4_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT1 High Performance Switch", WM5100_DAC_VOLUME_LIMIT_5L, + WM5100_OUT5_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT2 High Performance Switch", WM5100_DAC_VOLUME_LIMIT_6L, + WM5100_OUT6_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", WM5100_DAC_DIGITAL_VOLUME_1L, + WM5100_DAC_DIGITAL_VOLUME_1R, WM5100_OUT1L_VOL_SHIFT, 159, 0, + digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", WM5100_DAC_DIGITAL_VOLUME_2L, + WM5100_DAC_DIGITAL_VOLUME_2R, WM5100_OUT2L_VOL_SHIFT, 159, 0, + digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", WM5100_DAC_DIGITAL_VOLUME_3L, + WM5100_DAC_DIGITAL_VOLUME_3R, WM5100_OUT3L_VOL_SHIFT, 159, 0, + digital_tlv), +SOC_DOUBLE_R_TLV("SPKOUT Digital Volume", WM5100_DAC_DIGITAL_VOLUME_4L, + WM5100_DAC_DIGITAL_VOLUME_4R, WM5100_OUT4L_VOL_SHIFT, 159, 0, + digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", WM5100_DAC_DIGITAL_VOLUME_5L, + WM5100_DAC_DIGITAL_VOLUME_5R, WM5100_OUT5L_VOL_SHIFT, 159, 0, + digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT2 Digital Volume", WM5100_DAC_DIGITAL_VOLUME_6L, + WM5100_DAC_DIGITAL_VOLUME_6R, WM5100_OUT6L_VOL_SHIFT, 159, 0, + digital_tlv), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", WM5100_DAC_DIGITAL_VOLUME_1L, + WM5100_DAC_DIGITAL_VOLUME_1R, WM5100_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT2 Digital Switch", WM5100_DAC_DIGITAL_VOLUME_2L, + WM5100_DAC_DIGITAL_VOLUME_2R, WM5100_OUT2L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT3 Digital Switch", WM5100_DAC_DIGITAL_VOLUME_3L, + WM5100_DAC_DIGITAL_VOLUME_3R, WM5100_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKOUT Digital Switch", WM5100_DAC_DIGITAL_VOLUME_4L, + WM5100_DAC_DIGITAL_VOLUME_4R, WM5100_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", WM5100_DAC_DIGITAL_VOLUME_5L, + WM5100_DAC_DIGITAL_VOLUME_5R, WM5100_OUT5L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT2 Digital Switch", WM5100_DAC_DIGITAL_VOLUME_6L, + WM5100_DAC_DIGITAL_VOLUME_6R, WM5100_OUT6L_MUTE_SHIFT, 1, 1), + +/* FIXME: Only valid from -12dB to 0dB (52-64) */ +SOC_DOUBLE_R_TLV("HPOUT1 Volume", WM5100_OUT_VOLUME_1L, WM5100_OUT_VOLUME_1R, + WM5100_OUT1L_PGA_VOL_SHIFT, 64, 0, out_tlv), +SOC_DOUBLE_R_TLV("HPOUT2 Volume", WM5100_OUT_VOLUME_2L, WM5100_OUT_VOLUME_2R, + WM5100_OUT2L_PGA_VOL_SHIFT, 64, 0, out_tlv), +SOC_DOUBLE_R_TLV("HPOUT3 Volume", WM5100_OUT_VOLUME_3L, WM5100_OUT_VOLUME_3R, + WM5100_OUT2L_PGA_VOL_SHIFT, 64, 0, out_tlv), + +SOC_DOUBLE("SPKDAT1 Switch", WM5100_PDM_SPK1_CTRL_1, WM5100_SPK1L_MUTE_SHIFT, + WM5100_SPK1R_MUTE_SHIFT, 1, 1), +SOC_DOUBLE("SPKDAT2 Switch", WM5100_PDM_SPK2_CTRL_1, WM5100_SPK2L_MUTE_SHIFT, + WM5100_SPK2R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE_TLV("EQ1 Band 1 Volume", WM5100_EQ1_1, WM5100_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 Band 2 Volume", WM5100_EQ1_1, WM5100_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 Band 3 Volume", WM5100_EQ1_1, WM5100_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 Band 4 Volume", WM5100_EQ1_2, WM5100_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 Band 5 Volume", WM5100_EQ1_2, WM5100_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ2 Band 1 Volume", WM5100_EQ2_1, WM5100_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 Band 2 Volume", WM5100_EQ2_1, WM5100_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 Band 3 Volume", WM5100_EQ2_1, WM5100_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 Band 4 Volume", WM5100_EQ2_2, WM5100_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 Band 5 Volume", WM5100_EQ2_2, WM5100_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ3 Band 1 Volume", WM5100_EQ1_1, WM5100_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 Band 2 Volume", WM5100_EQ3_1, WM5100_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 Band 3 Volume", WM5100_EQ3_1, WM5100_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 Band 4 Volume", WM5100_EQ3_2, WM5100_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 Band 5 Volume", WM5100_EQ3_2, WM5100_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ4 Band 1 Volume", WM5100_EQ4_1, WM5100_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 Band 2 Volume", WM5100_EQ4_1, WM5100_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 Band 3 Volume", WM5100_EQ4_1, WM5100_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 Band 4 Volume", WM5100_EQ4_2, WM5100_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 Band 5 Volume", WM5100_EQ4_2, WM5100_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_ENUM("LHPF1 Mode", wm5100_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", wm5100_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", wm5100_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", wm5100_lhpf4_mode), + +WM5100_MIXER_CONTROLS("HPOUT1L", WM5100_OUT1LMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("HPOUT1R", WM5100_OUT1RMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("HPOUT2L", WM5100_OUT2LMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("HPOUT2R", WM5100_OUT2RMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("HPOUT3L", WM5100_OUT3LMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("HPOUT3R", WM5100_OUT3RMIX_INPUT_1_SOURCE), + +WM5100_MIXER_CONTROLS("SPKOUTL", WM5100_OUT4LMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("SPKOUTR", WM5100_OUT4RMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("SPKDAT1L", WM5100_OUT5LMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("SPKDAT1R", WM5100_OUT5RMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("SPKDAT2L", WM5100_OUT6LMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("SPKDAT2R", WM5100_OUT6RMIX_INPUT_1_SOURCE), + +WM5100_MIXER_CONTROLS("PWM1", WM5100_PWM1MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("PWM2", WM5100_PWM2MIX_INPUT_1_SOURCE), + +WM5100_MIXER_CONTROLS("AIF1TX1", WM5100_AIF1TX1MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("AIF1TX2", WM5100_AIF1TX2MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("AIF1TX3", WM5100_AIF1TX3MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("AIF1TX4", WM5100_AIF1TX4MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("AIF1TX5", WM5100_AIF1TX5MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("AIF1TX6", WM5100_AIF1TX6MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("AIF1TX7", WM5100_AIF1TX7MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("AIF1TX8", WM5100_AIF1TX8MIX_INPUT_1_SOURCE), + +WM5100_MIXER_CONTROLS("AIF2TX1", WM5100_AIF2TX1MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("AIF2TX2", WM5100_AIF2TX2MIX_INPUT_1_SOURCE), + +WM5100_MIXER_CONTROLS("AIF3TX1", WM5100_AIF3TX1MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("AIF3TX2", WM5100_AIF3TX2MIX_INPUT_1_SOURCE), + +WM5100_MIXER_CONTROLS("EQ1", WM5100_EQ1MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("EQ2", WM5100_EQ2MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("EQ3", WM5100_EQ3MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("EQ4", WM5100_EQ4MIX_INPUT_1_SOURCE), + +WM5100_MIXER_CONTROLS("DRC1L", WM5100_DRC1LMIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("DRC1R", WM5100_DRC1RMIX_INPUT_1_SOURCE), +SND_SOC_BYTES_MASK("DRC", WM5100_DRC1_CTRL1, 5, + WM5100_DRCL_ENA | WM5100_DRCR_ENA), + +WM5100_MIXER_CONTROLS("LHPF1", WM5100_HPLP1MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("LHPF2", WM5100_HPLP2MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("LHPF3", WM5100_HPLP3MIX_INPUT_1_SOURCE), +WM5100_MIXER_CONTROLS("LHPF4", WM5100_HPLP4MIX_INPUT_1_SOURCE), +}; + +static void wm5100_seq_notifier(struct snd_soc_component *component, + enum snd_soc_dapm_type event, int subseq) +{ + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + u16 val, expect, i; + + /* Wait for the outputs to flag themselves as enabled */ + if (wm5100->out_ena[0]) { + expect = snd_soc_component_read(component, WM5100_CHANNEL_ENABLES_1); + for (i = 0; i < 200; i++) { + val = snd_soc_component_read(component, WM5100_OUTPUT_STATUS_1); + if (val == expect) { + wm5100->out_ena[0] = false; + break; + } + } + if (i == 200) { + dev_err(component->dev, "Timeout waiting for OUTPUT1 %x\n", + expect); + } + } + + if (wm5100->out_ena[1]) { + expect = snd_soc_component_read(component, WM5100_OUTPUT_ENABLES_2); + for (i = 0; i < 200; i++) { + val = snd_soc_component_read(component, WM5100_OUTPUT_STATUS_2); + if (val == expect) { + wm5100->out_ena[1] = false; + break; + } + } + if (i == 200) { + dev_err(component->dev, "Timeout waiting for OUTPUT2 %x\n", + expect); + } + } +} + +static int wm5100_out_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + + switch (w->reg) { + case WM5100_CHANNEL_ENABLES_1: + wm5100->out_ena[0] = true; + break; + case WM5100_OUTPUT_ENABLES_2: + wm5100->out_ena[0] = true; + break; + default: + break; + } + + return 0; +} + +static void wm5100_log_status3(struct wm5100_priv *wm5100, int val) +{ + if (val & WM5100_SPK_SHUTDOWN_WARN_EINT) + dev_crit(wm5100->dev, "Speaker shutdown warning\n"); + if (val & WM5100_SPK_SHUTDOWN_EINT) + dev_crit(wm5100->dev, "Speaker shutdown\n"); + if (val & WM5100_CLKGEN_ERR_EINT) + dev_crit(wm5100->dev, "SYSCLK underclocked\n"); + if (val & WM5100_CLKGEN_ERR_ASYNC_EINT) + dev_crit(wm5100->dev, "ASYNCCLK underclocked\n"); +} + +static void wm5100_log_status4(struct wm5100_priv *wm5100, int val) +{ + if (val & WM5100_AIF3_ERR_EINT) + dev_err(wm5100->dev, "AIF3 configuration error\n"); + if (val & WM5100_AIF2_ERR_EINT) + dev_err(wm5100->dev, "AIF2 configuration error\n"); + if (val & WM5100_AIF1_ERR_EINT) + dev_err(wm5100->dev, "AIF1 configuration error\n"); + if (val & WM5100_CTRLIF_ERR_EINT) + dev_err(wm5100->dev, "Control interface error\n"); + if (val & WM5100_ISRC2_UNDERCLOCKED_EINT) + dev_err(wm5100->dev, "ISRC2 underclocked\n"); + if (val & WM5100_ISRC1_UNDERCLOCKED_EINT) + dev_err(wm5100->dev, "ISRC1 underclocked\n"); + if (val & WM5100_FX_UNDERCLOCKED_EINT) + dev_err(wm5100->dev, "FX underclocked\n"); + if (val & WM5100_AIF3_UNDERCLOCKED_EINT) + dev_err(wm5100->dev, "AIF3 underclocked\n"); + if (val & WM5100_AIF2_UNDERCLOCKED_EINT) + dev_err(wm5100->dev, "AIF2 underclocked\n"); + if (val & WM5100_AIF1_UNDERCLOCKED_EINT) + dev_err(wm5100->dev, "AIF1 underclocked\n"); + if (val & WM5100_ASRC_UNDERCLOCKED_EINT) + dev_err(wm5100->dev, "ASRC underclocked\n"); + if (val & WM5100_DAC_UNDERCLOCKED_EINT) + dev_err(wm5100->dev, "DAC underclocked\n"); + if (val & WM5100_ADC_UNDERCLOCKED_EINT) + dev_err(wm5100->dev, "ADC underclocked\n"); + if (val & WM5100_MIXER_UNDERCLOCKED_EINT) + dev_err(wm5100->dev, "Mixer underclocked\n"); +} + +static int wm5100_post_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_component_read(component, WM5100_INTERRUPT_RAW_STATUS_3); + ret &= WM5100_SPK_SHUTDOWN_WARN_STS | + WM5100_SPK_SHUTDOWN_STS | WM5100_CLKGEN_ERR_STS | + WM5100_CLKGEN_ERR_ASYNC_STS; + wm5100_log_status3(wm5100, ret); + + ret = snd_soc_component_read(component, WM5100_INTERRUPT_RAW_STATUS_4); + wm5100_log_status4(wm5100, ret); + + return 0; +} + +static const struct snd_soc_dapm_widget wm5100_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", WM5100_CLOCKING_3, WM5100_SYSCLK_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", WM5100_CLOCKING_6, WM5100_ASYNC_CLK_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), + +SND_SOC_DAPM_SUPPLY("CP1", WM5100_HP_CHARGE_PUMP_1, WM5100_CP1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_SUPPLY("CP2", WM5100_MIC_CHARGE_PUMP_1, WM5100_CP2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_SUPPLY("CP2 Active", WM5100_MIC_CHARGE_PUMP_1, + WM5100_CP2_BYPASS_SHIFT, 1, NULL, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", WM5100_MIC_BIAS_CTRL_1, WM5100_MICB1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", WM5100_MIC_BIAS_CTRL_2, WM5100_MICB2_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", WM5100_MIC_BIAS_CTRL_3, WM5100_MICB3_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), +SND_SOC_DAPM_INPUT("IN3L"), +SND_SOC_DAPM_INPUT("IN3R"), +SND_SOC_DAPM_INPUT("IN4L"), +SND_SOC_DAPM_INPUT("IN4R"), +SND_SOC_DAPM_SIGGEN("TONE"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", WM5100_INPUT_ENABLES, WM5100_IN1L_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", WM5100_INPUT_ENABLES, WM5100_IN1R_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", WM5100_INPUT_ENABLES, WM5100_IN2L_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", WM5100_INPUT_ENABLES, WM5100_IN2R_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3L PGA", WM5100_INPUT_ENABLES, WM5100_IN3L_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3R PGA", WM5100_INPUT_ENABLES, WM5100_IN3R_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4L PGA", WM5100_INPUT_ENABLES, WM5100_IN4L_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4R PGA", WM5100_INPUT_ENABLES, WM5100_IN4R_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("Tone Generator 1", WM5100_TONE_GENERATOR_1, + WM5100_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", WM5100_TONE_GENERATOR_1, + WM5100_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", "AIF1 Playback", 0, + WM5100_AUDIO_IF_1_27, WM5100_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", "AIF1 Playback", 1, + WM5100_AUDIO_IF_1_27, WM5100_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", "AIF1 Playback", 2, + WM5100_AUDIO_IF_1_27, WM5100_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", "AIF1 Playback", 3, + WM5100_AUDIO_IF_1_27, WM5100_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 4, + WM5100_AUDIO_IF_1_27, WM5100_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", "AIF1 Playback", 5, + WM5100_AUDIO_IF_1_27, WM5100_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", "AIF1 Playback", 6, + WM5100_AUDIO_IF_1_27, WM5100_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", "AIF1 Playback", 7, + WM5100_AUDIO_IF_1_27, WM5100_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 0, + WM5100_AUDIO_IF_2_27, WM5100_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", "AIF2 Playback", 1, + WM5100_AUDIO_IF_2_27, WM5100_AIF2RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", "AIF3 Playback", 0, + WM5100_AUDIO_IF_3_27, WM5100_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", "AIF3 Playback", 1, + WM5100_AUDIO_IF_3_27, WM5100_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", "AIF1 Capture", 0, + WM5100_AUDIO_IF_1_26, WM5100_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", "AIF1 Capture", 1, + WM5100_AUDIO_IF_1_26, WM5100_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", "AIF1 Capture", 2, + WM5100_AUDIO_IF_1_26, WM5100_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", "AIF1 Capture", 3, + WM5100_AUDIO_IF_1_26, WM5100_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", "AIF1 Capture", 4, + WM5100_AUDIO_IF_1_26, WM5100_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", "AIF1 Capture", 5, + WM5100_AUDIO_IF_1_26, WM5100_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", "AIF1 Capture", 6, + WM5100_AUDIO_IF_1_26, WM5100_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", "AIF1 Capture", 7, + WM5100_AUDIO_IF_1_26, WM5100_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", "AIF2 Capture", 0, + WM5100_AUDIO_IF_2_26, WM5100_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", "AIF2 Capture", 1, + WM5100_AUDIO_IF_2_26, WM5100_AIF2TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", "AIF3 Capture", 0, + WM5100_AUDIO_IF_3_26, WM5100_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", "AIF3 Capture", 1, + WM5100_AUDIO_IF_3_26, WM5100_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT6L", WM5100_OUTPUT_ENABLES_2, WM5100_OUT6L_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT6R", WM5100_OUTPUT_ENABLES_2, WM5100_OUT6R_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", WM5100_OUTPUT_ENABLES_2, WM5100_OUT5L_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", WM5100_OUTPUT_ENABLES_2, WM5100_OUT5R_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT4L", WM5100_OUTPUT_ENABLES_2, WM5100_OUT4L_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT4R", WM5100_OUTPUT_ENABLES_2, WM5100_OUT4R_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", WM5100_CHANNEL_ENABLES_1, WM5100_HP3L_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3R", WM5100_CHANNEL_ENABLES_1, WM5100_HP3R_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", WM5100_CHANNEL_ENABLES_1, WM5100_HP2L_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", WM5100_CHANNEL_ENABLES_1, WM5100_HP2R_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1L", WM5100_CHANNEL_ENABLES_1, WM5100_HP1L_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", WM5100_CHANNEL_ENABLES_1, WM5100_HP1R_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("PWM1 Driver", WM5100_PWM_DRIVE_1, WM5100_PWM1_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("PWM2 Driver", WM5100_PWM_DRIVE_1, WM5100_PWM2_ENA_SHIFT, 0, + NULL, 0, wm5100_out_ev, SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("EQ1", WM5100_EQ1_1, WM5100_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", WM5100_EQ2_1, WM5100_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", WM5100_EQ3_1, WM5100_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", WM5100_EQ4_1, WM5100_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", WM5100_DRC1_CTRL1, WM5100_DRCL_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", WM5100_DRC1_CTRL1, WM5100_DRCR_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", WM5100_HPLPF1_1, WM5100_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", WM5100_HPLPF2_1, WM5100_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", WM5100_HPLPF3_1, WM5100_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", WM5100_HPLPF4_1, WM5100_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +WM5100_MIXER_WIDGETS(EQ1, "EQ1"), +WM5100_MIXER_WIDGETS(EQ2, "EQ2"), +WM5100_MIXER_WIDGETS(EQ3, "EQ3"), +WM5100_MIXER_WIDGETS(EQ4, "EQ4"), + +WM5100_MIXER_WIDGETS(DRC1L, "DRC1L"), +WM5100_MIXER_WIDGETS(DRC1R, "DRC1R"), + +WM5100_MIXER_WIDGETS(LHPF1, "LHPF1"), +WM5100_MIXER_WIDGETS(LHPF2, "LHPF2"), +WM5100_MIXER_WIDGETS(LHPF3, "LHPF3"), +WM5100_MIXER_WIDGETS(LHPF4, "LHPF4"), + +WM5100_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +WM5100_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +WM5100_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +WM5100_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +WM5100_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +WM5100_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +WM5100_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +WM5100_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +WM5100_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +WM5100_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), + +WM5100_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +WM5100_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +WM5100_MIXER_WIDGETS(HPOUT1L, "HPOUT1L"), +WM5100_MIXER_WIDGETS(HPOUT1R, "HPOUT1R"), +WM5100_MIXER_WIDGETS(HPOUT2L, "HPOUT2L"), +WM5100_MIXER_WIDGETS(HPOUT2R, "HPOUT2R"), +WM5100_MIXER_WIDGETS(HPOUT3L, "HPOUT3L"), +WM5100_MIXER_WIDGETS(HPOUT3R, "HPOUT3R"), + +WM5100_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +WM5100_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"), +WM5100_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +WM5100_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), +WM5100_MIXER_WIDGETS(SPKDAT2L, "SPKDAT2L"), +WM5100_MIXER_WIDGETS(SPKDAT2R, "SPKDAT2R"), + +WM5100_MIXER_WIDGETS(PWM1, "PWM1"), +WM5100_MIXER_WIDGETS(PWM2, "PWM2"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2L"), +SND_SOC_DAPM_OUTPUT("HPOUT2R"), +SND_SOC_DAPM_OUTPUT("HPOUT3L"), +SND_SOC_DAPM_OUTPUT("HPOUT3R"), +SND_SOC_DAPM_OUTPUT("SPKOUTL"), +SND_SOC_DAPM_OUTPUT("SPKOUTR"), +SND_SOC_DAPM_OUTPUT("SPKDAT1"), +SND_SOC_DAPM_OUTPUT("SPKDAT2"), +SND_SOC_DAPM_OUTPUT("PWM1"), +SND_SOC_DAPM_OUTPUT("PWM2"), +}; + +/* We register a _POST event if we don't have IRQ support so we can + * look at the error status from the CODEC - if we've got the IRQ + * hooked up then we will get prompted to look by an interrupt. + */ +static const struct snd_soc_dapm_widget wm5100_dapm_widgets_noirq[] = { +SND_SOC_DAPM_POST("Post", wm5100_post_ev), +}; + +static const struct snd_soc_dapm_route wm5100_dapm_routes[] = { + { "CP1", NULL, "CPVDD" }, + { "CP2 Active", NULL, "CPVDD" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + { "IN3L", NULL, "SYSCLK" }, + { "IN3R", NULL, "SYSCLK" }, + { "IN4L", NULL, "SYSCLK" }, + { "IN4R", NULL, "SYSCLK" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT3R", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT4R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + { "OUT6L", NULL, "SYSCLK" }, + { "OUT6R", NULL, "SYSCLK" }, + + { "AIF1RX1", NULL, "SYSCLK" }, + { "AIF1RX2", NULL, "SYSCLK" }, + { "AIF1RX3", NULL, "SYSCLK" }, + { "AIF1RX4", NULL, "SYSCLK" }, + { "AIF1RX5", NULL, "SYSCLK" }, + { "AIF1RX6", NULL, "SYSCLK" }, + { "AIF1RX7", NULL, "SYSCLK" }, + { "AIF1RX8", NULL, "SYSCLK" }, + + { "AIF2RX1", NULL, "SYSCLK" }, + { "AIF2RX1", NULL, "DBVDD2" }, + { "AIF2RX2", NULL, "SYSCLK" }, + { "AIF2RX2", NULL, "DBVDD2" }, + + { "AIF3RX1", NULL, "SYSCLK" }, + { "AIF3RX1", NULL, "DBVDD3" }, + { "AIF3RX2", NULL, "SYSCLK" }, + { "AIF3RX2", NULL, "DBVDD3" }, + + { "AIF1TX1", NULL, "SYSCLK" }, + { "AIF1TX2", NULL, "SYSCLK" }, + { "AIF1TX3", NULL, "SYSCLK" }, + { "AIF1TX4", NULL, "SYSCLK" }, + { "AIF1TX5", NULL, "SYSCLK" }, + { "AIF1TX6", NULL, "SYSCLK" }, + { "AIF1TX7", NULL, "SYSCLK" }, + { "AIF1TX8", NULL, "SYSCLK" }, + + { "AIF2TX1", NULL, "SYSCLK" }, + { "AIF2TX1", NULL, "DBVDD2" }, + { "AIF2TX2", NULL, "SYSCLK" }, + { "AIF2TX2", NULL, "DBVDD2" }, + + { "AIF3TX1", NULL, "SYSCLK" }, + { "AIF3TX1", NULL, "DBVDD3" }, + { "AIF3TX2", NULL, "SYSCLK" }, + { "AIF3TX2", NULL, "DBVDD3" }, + + { "MICBIAS1", NULL, "CP2" }, + { "MICBIAS2", NULL, "CP2" }, + { "MICBIAS3", NULL, "CP2" }, + + { "IN1L PGA", NULL, "CP2" }, + { "IN1R PGA", NULL, "CP2" }, + { "IN2L PGA", NULL, "CP2" }, + { "IN2R PGA", NULL, "CP2" }, + { "IN3L PGA", NULL, "CP2" }, + { "IN3R PGA", NULL, "CP2" }, + { "IN4L PGA", NULL, "CP2" }, + { "IN4R PGA", NULL, "CP2" }, + + { "IN1L PGA", NULL, "CP2 Active" }, + { "IN1R PGA", NULL, "CP2 Active" }, + { "IN2L PGA", NULL, "CP2 Active" }, + { "IN2R PGA", NULL, "CP2 Active" }, + { "IN3L PGA", NULL, "CP2 Active" }, + { "IN3R PGA", NULL, "CP2 Active" }, + { "IN4L PGA", NULL, "CP2 Active" }, + { "IN4R PGA", NULL, "CP2 Active" }, + + { "OUT1L", NULL, "CP1" }, + { "OUT1R", NULL, "CP1" }, + { "OUT2L", NULL, "CP1" }, + { "OUT2R", NULL, "CP1" }, + { "OUT3L", NULL, "CP1" }, + { "OUT3R", NULL, "CP1" }, + + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "IN1L PGA", NULL, "IN1L" }, + { "IN1R PGA", NULL, "IN1R" }, + { "IN2L PGA", NULL, "IN2L" }, + { "IN2R PGA", NULL, "IN2R" }, + { "IN3L PGA", NULL, "IN3L" }, + { "IN3R PGA", NULL, "IN3R" }, + { "IN4L PGA", NULL, "IN4L" }, + { "IN4R PGA", NULL, "IN4R" }, + + WM5100_MIXER_ROUTES("OUT1L", "HPOUT1L"), + WM5100_MIXER_ROUTES("OUT1R", "HPOUT1R"), + WM5100_MIXER_ROUTES("OUT2L", "HPOUT2L"), + WM5100_MIXER_ROUTES("OUT2R", "HPOUT2R"), + WM5100_MIXER_ROUTES("OUT3L", "HPOUT3L"), + WM5100_MIXER_ROUTES("OUT3R", "HPOUT3R"), + + WM5100_MIXER_ROUTES("OUT4L", "SPKOUTL"), + WM5100_MIXER_ROUTES("OUT4R", "SPKOUTR"), + WM5100_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + WM5100_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + WM5100_MIXER_ROUTES("OUT6L", "SPKDAT2L"), + WM5100_MIXER_ROUTES("OUT6R", "SPKDAT2R"), + + WM5100_MIXER_ROUTES("PWM1 Driver", "PWM1"), + WM5100_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + WM5100_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + WM5100_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + WM5100_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + WM5100_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + WM5100_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + WM5100_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + WM5100_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + WM5100_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + WM5100_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + WM5100_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + + WM5100_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + WM5100_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + WM5100_MIXER_ROUTES("EQ1", "EQ1"), + WM5100_MIXER_ROUTES("EQ2", "EQ2"), + WM5100_MIXER_ROUTES("EQ3", "EQ3"), + WM5100_MIXER_ROUTES("EQ4", "EQ4"), + + WM5100_MIXER_ROUTES("DRC1L", "DRC1L"), + WM5100_MIXER_ROUTES("DRC1R", "DRC1R"), + + WM5100_MIXER_ROUTES("LHPF1", "LHPF1"), + WM5100_MIXER_ROUTES("LHPF2", "LHPF2"), + WM5100_MIXER_ROUTES("LHPF3", "LHPF3"), + WM5100_MIXER_ROUTES("LHPF4", "LHPF4"), + + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + { "HPOUT2L", NULL, "OUT2L" }, + { "HPOUT2R", NULL, "OUT2R" }, + { "HPOUT3L", NULL, "OUT3L" }, + { "HPOUT3R", NULL, "OUT3R" }, + { "SPKOUTL", NULL, "OUT4L" }, + { "SPKOUTR", NULL, "OUT4R" }, + { "SPKDAT1", NULL, "OUT5L" }, + { "SPKDAT1", NULL, "OUT5R" }, + { "SPKDAT2", NULL, "OUT6L" }, + { "SPKDAT2", NULL, "OUT6R" }, + { "PWM1", NULL, "PWM1 Driver" }, + { "PWM2", NULL, "PWM2 Driver" }, +}; + +static const struct reg_sequence wm5100_reva_patches[] = { + { WM5100_AUDIO_IF_1_10, 0 }, + { WM5100_AUDIO_IF_1_11, 1 }, + { WM5100_AUDIO_IF_1_12, 2 }, + { WM5100_AUDIO_IF_1_13, 3 }, + { WM5100_AUDIO_IF_1_14, 4 }, + { WM5100_AUDIO_IF_1_15, 5 }, + { WM5100_AUDIO_IF_1_16, 6 }, + { WM5100_AUDIO_IF_1_17, 7 }, + + { WM5100_AUDIO_IF_1_18, 0 }, + { WM5100_AUDIO_IF_1_19, 1 }, + { WM5100_AUDIO_IF_1_20, 2 }, + { WM5100_AUDIO_IF_1_21, 3 }, + { WM5100_AUDIO_IF_1_22, 4 }, + { WM5100_AUDIO_IF_1_23, 5 }, + { WM5100_AUDIO_IF_1_24, 6 }, + { WM5100_AUDIO_IF_1_25, 7 }, + + { WM5100_AUDIO_IF_2_10, 0 }, + { WM5100_AUDIO_IF_2_11, 1 }, + + { WM5100_AUDIO_IF_2_18, 0 }, + { WM5100_AUDIO_IF_2_19, 1 }, + + { WM5100_AUDIO_IF_3_10, 0 }, + { WM5100_AUDIO_IF_3_11, 1 }, + + { WM5100_AUDIO_IF_3_18, 0 }, + { WM5100_AUDIO_IF_3_19, 1 }, +}; + +static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + int lrclk, bclk, mask, base; + + base = dai->driver->base; + + lrclk = 0; + bclk = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + mask = 0; + break; + case SND_SOC_DAIFMT_I2S: + mask = 2; + break; + default: + dev_err(component->dev, "Unsupported DAI format %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBS_CFM: + lrclk |= WM5100_AIF1TX_LRCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + bclk |= WM5100_AIF1_BCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + lrclk |= WM5100_AIF1TX_LRCLK_MSTR; + bclk |= WM5100_AIF1_BCLK_MSTR; + break; + default: + dev_err(component->dev, "Unsupported master mode %d\n", + fmt & SND_SOC_DAIFMT_MASTER_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + bclk |= WM5100_AIF1_BCLK_INV; + lrclk |= WM5100_AIF1TX_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + bclk |= WM5100_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + lrclk |= WM5100_AIF1TX_LRCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, base + 1, WM5100_AIF1_BCLK_MSTR | + WM5100_AIF1_BCLK_INV, bclk); + snd_soc_component_update_bits(component, base + 2, WM5100_AIF1TX_LRCLK_MSTR | + WM5100_AIF1TX_LRCLK_INV, lrclk); + snd_soc_component_update_bits(component, base + 3, WM5100_AIF1TX_LRCLK_MSTR | + WM5100_AIF1TX_LRCLK_INV, lrclk); + snd_soc_component_update_bits(component, base + 5, WM5100_AIF1_FMT_MASK, mask); + + return 0; +} + +#define WM5100_NUM_BCLK_RATES 19 + +static int wm5100_bclk_rates_dat[WM5100_NUM_BCLK_RATES] = { + 32000, + 48000, + 64000, + 96000, + 128000, + 192000, + 256000, + 384000, + 512000, + 768000, + 1024000, + 1536000, + 2048000, + 3072000, + 4096000, + 6144000, + 8192000, + 12288000, + 24576000, +}; + +static int wm5100_bclk_rates_cd[WM5100_NUM_BCLK_RATES] = { + 29400, + 44100, + 58800, + 88200, + 117600, + 176400, + 235200, + 352800, + 470400, + 705600, + 940800, + 1411200, + 1881600, + 2882400, + 3763200, + 5644800, + 7526400, + 11289600, + 22579600, +}; + +static int wm5100_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + bool async = wm5100->aif_async[dai->id]; + int i, base, bclk, aif_rate, lrclk, wl, fl, sr; + int *bclk_rates; + + base = dai->driver->base; + + /* Data sizes if not using TDM */ + wl = params_width(params); + if (wl < 0) + return wl; + fl = snd_soc_params_to_frame_size(params); + if (fl < 0) + return fl; + + dev_dbg(component->dev, "Word length %d bits, frame length %d bits\n", + wl, fl); + + /* Target BCLK rate */ + bclk = snd_soc_params_to_bclk(params); + if (bclk < 0) + return bclk; + + /* Root for BCLK depends on SYS/ASYNCCLK */ + if (!async) { + aif_rate = wm5100->sysclk; + sr = wm5100_alloc_sr(component, params_rate(params)); + if (sr < 0) + return sr; + } else { + /* If we're in ASYNCCLK set the ASYNC sample rate */ + aif_rate = wm5100->asyncclk; + sr = 3; + + for (i = 0; i < ARRAY_SIZE(wm5100_sr_code); i++) + if (params_rate(params) == wm5100_sr_code[i]) + break; + if (i == ARRAY_SIZE(wm5100_sr_code)) { + dev_err(component->dev, "Invalid rate %dHzn", + params_rate(params)); + return -EINVAL; + } + + /* TODO: We should really check for symmetry */ + snd_soc_component_update_bits(component, WM5100_CLOCKING_8, + WM5100_ASYNC_SAMPLE_RATE_MASK, i); + } + + if (!aif_rate) { + dev_err(component->dev, "%s has no rate set\n", + async ? "ASYNCCLK" : "SYSCLK"); + return -EINVAL; + } + + dev_dbg(component->dev, "Target BCLK is %dHz, using %dHz %s\n", + bclk, aif_rate, async ? "ASYNCCLK" : "SYSCLK"); + + if (aif_rate % 4000) + bclk_rates = wm5100_bclk_rates_cd; + else + bclk_rates = wm5100_bclk_rates_dat; + + for (i = 0; i < WM5100_NUM_BCLK_RATES; i++) + if (bclk_rates[i] >= bclk && (bclk_rates[i] % bclk == 0)) + break; + if (i == WM5100_NUM_BCLK_RATES) { + dev_err(component->dev, + "No valid BCLK for %dHz found from %dHz %s\n", + bclk, aif_rate, async ? "ASYNCCLK" : "SYSCLK"); + return -EINVAL; + } + + bclk = i; + dev_dbg(component->dev, "Setting %dHz BCLK\n", bclk_rates[bclk]); + snd_soc_component_update_bits(component, base + 1, WM5100_AIF1_BCLK_FREQ_MASK, bclk); + + lrclk = bclk_rates[bclk] / params_rate(params); + dev_dbg(component->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || + wm5100->aif_symmetric[dai->id]) + snd_soc_component_update_bits(component, base + 7, + WM5100_AIF1RX_BCPF_MASK, lrclk); + else + snd_soc_component_update_bits(component, base + 6, + WM5100_AIF1TX_BCPF_MASK, lrclk); + + i = (wl << WM5100_AIF1TX_WL_SHIFT) | fl; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_component_update_bits(component, base + 9, + WM5100_AIF1RX_WL_MASK | + WM5100_AIF1RX_SLOT_LEN_MASK, i); + else + snd_soc_component_update_bits(component, base + 8, + WM5100_AIF1TX_WL_MASK | + WM5100_AIF1TX_SLOT_LEN_MASK, i); + + snd_soc_component_update_bits(component, base + 4, WM5100_AIF1_RATE_MASK, sr); + + return 0; +} + +static const struct snd_soc_dai_ops wm5100_dai_ops = { + .set_fmt = wm5100_set_fmt, + .hw_params = wm5100_hw_params, +}; + +static int wm5100_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + int *rate_store; + int fval, audio_rate, ret, reg; + + switch (clk_id) { + case WM5100_CLK_SYSCLK: + reg = WM5100_CLOCKING_3; + rate_store = &wm5100->sysclk; + break; + case WM5100_CLK_ASYNCCLK: + reg = WM5100_CLOCKING_7; + rate_store = &wm5100->asyncclk; + break; + case WM5100_CLK_32KHZ: + /* The 32kHz clock is slightly different to the others */ + switch (source) { + case WM5100_CLKSRC_MCLK1: + case WM5100_CLKSRC_MCLK2: + case WM5100_CLKSRC_SYSCLK: + snd_soc_component_update_bits(component, WM5100_CLOCKING_1, + WM5100_CLK_32K_SRC_MASK, + source); + break; + default: + return -EINVAL; + } + return 0; + + case WM5100_CLK_AIF1: + case WM5100_CLK_AIF2: + case WM5100_CLK_AIF3: + /* Not real clocks, record which clock domain they're in */ + switch (source) { + case WM5100_CLKSRC_SYSCLK: + wm5100->aif_async[clk_id - 1] = false; + break; + case WM5100_CLKSRC_ASYNCCLK: + wm5100->aif_async[clk_id - 1] = true; + break; + default: + dev_err(component->dev, "Invalid source %d\n", source); + return -EINVAL; + } + return 0; + + case WM5100_CLK_OPCLK: + switch (freq) { + case 5644800: + case 6144000: + snd_soc_component_update_bits(component, WM5100_MISC_GPIO_1, + WM5100_OPCLK_SEL_MASK, 0); + break; + case 11289600: + case 12288000: + snd_soc_component_update_bits(component, WM5100_MISC_GPIO_1, + WM5100_OPCLK_SEL_MASK, 0); + break; + case 22579200: + case 24576000: + snd_soc_component_update_bits(component, WM5100_MISC_GPIO_1, + WM5100_OPCLK_SEL_MASK, 0); + break; + default: + dev_err(component->dev, "Unsupported OPCLK %dHz\n", + freq); + return -EINVAL; + } + return 0; + + default: + dev_err(component->dev, "Unknown clock %d\n", clk_id); + return -EINVAL; + } + + switch (source) { + case WM5100_CLKSRC_SYSCLK: + case WM5100_CLKSRC_ASYNCCLK: + dev_err(component->dev, "Invalid source %d\n", source); + return -EINVAL; + } + + switch (freq) { + case 5644800: + case 6144000: + fval = 0; + break; + case 11289600: + case 12288000: + fval = 1; + break; + case 22579200: + case 24576000: + fval = 2; + break; + default: + dev_err(component->dev, "Invalid clock rate: %d\n", freq); + return -EINVAL; + } + + switch (freq) { + case 5644800: + case 11289600: + case 22579200: + audio_rate = 44100; + break; + + case 6144000: + case 12288000: + case 24576000: + audio_rate = 48000; + break; + + default: + BUG(); + audio_rate = 0; + break; + } + + /* TODO: Check if MCLKs are in use and enable/disable pulls to + * match. + */ + + snd_soc_component_update_bits(component, reg, WM5100_SYSCLK_FREQ_MASK | + WM5100_SYSCLK_SRC_MASK, + fval << WM5100_SYSCLK_FREQ_SHIFT | source); + + /* If this is SYSCLK then configure the clock rate for the + * internal audio functions to the natural sample rate for + * this clock rate. + */ + if (clk_id == WM5100_CLK_SYSCLK) { + dev_dbg(component->dev, "Setting primary audio rate to %dHz", + audio_rate); + if (0 && *rate_store) + wm5100_free_sr(component, audio_rate); + ret = wm5100_alloc_sr(component, audio_rate); + if (ret != 0) + dev_warn(component->dev, "Primary audio slot is %d\n", + ret); + } + + *rate_store = freq; + + return 0; +} + +struct _fll_div { + u16 fll_fratio; + u16 fll_outdiv; + u16 fll_refclk_div; + u16 n; + u16 theta; + u16 lambda; +}; + +static struct { + unsigned int min; + unsigned int max; + u16 fll_fratio; + int ratio; +} fll_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, + unsigned int Fout) +{ + unsigned int target; + unsigned int div; + unsigned int fratio, gcd_fll; + int i; + + /* Fref must be <=13.5MHz */ + div = 1; + fll_div->fll_refclk_div = 0; + while ((Fref / div) > 13500000) { + div *= 2; + fll_div->fll_refclk_div++; + + if (div > 8) { + pr_err("Can't scale %dMHz input down to <=13.5MHz\n", + Fref); + return -EINVAL; + } + } + + pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout); + + /* Apply the division for our remaining calculations */ + Fref /= div; + + /* Fvco should be 90-100MHz; don't check the upper bound */ + div = 2; + while (Fout * div < 90000000) { + div++; + if (div > 64) { + pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + } + target = Fout * div; + fll_div->fll_outdiv = div - 1; + + pr_debug("FLL Fvco=%dHz\n", target); + + /* Find an appropraite FLL_FRATIO and factor it out of the target */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + fll_div->fll_fratio = fll_fratios[i].fll_fratio; + fratio = fll_fratios[i].ratio; + break; + } + } + if (i == ARRAY_SIZE(fll_fratios)) { + pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref); + return -EINVAL; + } + + fll_div->n = target / (fratio * Fref); + + if (target % Fref == 0) { + fll_div->theta = 0; + fll_div->lambda = 0; + } else { + gcd_fll = gcd(target, fratio * Fref); + + fll_div->theta = (target - (fll_div->n * fratio * Fref)) + / gcd_fll; + fll_div->lambda = (fratio * Fref) / gcd_fll; + } + + pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n", + fll_div->n, fll_div->theta, fll_div->lambda); + pr_debug("FLL_FRATIO=%x(%d) FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n", + fll_div->fll_fratio, fratio, fll_div->fll_outdiv, + fll_div->fll_refclk_div); + + return 0; +} + +static int wm5100_set_fll(struct snd_soc_component *component, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct i2c_client *i2c = to_i2c_client(component->dev); + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + struct _fll_div factors; + struct wm5100_fll *fll; + int ret, base, lock, i, timeout; + unsigned long time_left; + + switch (fll_id) { + case WM5100_FLL1: + fll = &wm5100->fll[0]; + base = WM5100_FLL1_CONTROL_1 - 1; + lock = WM5100_FLL1_LOCK_STS; + break; + case WM5100_FLL2: + fll = &wm5100->fll[1]; + base = WM5100_FLL2_CONTROL_2 - 1; + lock = WM5100_FLL2_LOCK_STS; + break; + default: + dev_err(component->dev, "Unknown FLL %d\n",fll_id); + return -EINVAL; + } + + if (!Fout) { + dev_dbg(component->dev, "FLL%d disabled", fll_id); + if (fll->fout) + pm_runtime_put(component->dev); + fll->fout = 0; + snd_soc_component_update_bits(component, base + 1, WM5100_FLL1_ENA, 0); + return 0; + } + + switch (source) { + case WM5100_FLL_SRC_MCLK1: + case WM5100_FLL_SRC_MCLK2: + case WM5100_FLL_SRC_FLL1: + case WM5100_FLL_SRC_FLL2: + case WM5100_FLL_SRC_AIF1BCLK: + case WM5100_FLL_SRC_AIF2BCLK: + case WM5100_FLL_SRC_AIF3BCLK: + break; + default: + dev_err(component->dev, "Invalid FLL source %d\n", source); + return -EINVAL; + } + + ret = fll_factors(&factors, Fref, Fout); + if (ret < 0) + return ret; + + /* Disable the FLL while we reconfigure */ + snd_soc_component_update_bits(component, base + 1, WM5100_FLL1_ENA, 0); + + snd_soc_component_update_bits(component, base + 2, + WM5100_FLL1_OUTDIV_MASK | WM5100_FLL1_FRATIO_MASK, + (factors.fll_outdiv << WM5100_FLL1_OUTDIV_SHIFT) | + factors.fll_fratio); + snd_soc_component_update_bits(component, base + 3, WM5100_FLL1_THETA_MASK, + factors.theta); + snd_soc_component_update_bits(component, base + 5, WM5100_FLL1_N_MASK, factors.n); + snd_soc_component_update_bits(component, base + 6, + WM5100_FLL1_REFCLK_DIV_MASK | + WM5100_FLL1_REFCLK_SRC_MASK, + (factors.fll_refclk_div + << WM5100_FLL1_REFCLK_DIV_SHIFT) | source); + snd_soc_component_update_bits(component, base + 7, WM5100_FLL1_LAMBDA_MASK, + factors.lambda); + + /* Clear any pending completions */ + try_wait_for_completion(&fll->lock); + + pm_runtime_get_sync(component->dev); + + snd_soc_component_update_bits(component, base + 1, WM5100_FLL1_ENA, WM5100_FLL1_ENA); + + if (i2c->irq) + timeout = 2; + else + timeout = 50; + + snd_soc_component_update_bits(component, WM5100_CLOCKING_3, WM5100_SYSCLK_ENA, + WM5100_SYSCLK_ENA); + + /* Poll for the lock; will use interrupt when we can test */ + for (i = 0; i < timeout; i++) { + if (i2c->irq) { + time_left = wait_for_completion_timeout(&fll->lock, + msecs_to_jiffies(25)); + if (time_left > 0) + break; + } else { + msleep(1); + } + + ret = snd_soc_component_read(component, + WM5100_INTERRUPT_RAW_STATUS_3); + if (ret < 0) { + dev_err(component->dev, + "Failed to read FLL status: %d\n", + ret); + continue; + } + if (ret & lock) + break; + } + if (i == timeout) { + dev_err(component->dev, "FLL%d lock timed out\n", fll_id); + pm_runtime_put(component->dev); + return -ETIMEDOUT; + } + + fll->src = source; + fll->fref = Fref; + fll->fout = Fout; + + dev_dbg(component->dev, "FLL%d running %dHz->%dHz\n", fll_id, + Fref, Fout); + + return 0; +} + +/* Actually go much higher */ +#define WM5100_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM5100_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm5100_dai[] = { + { + .name = "wm5100-aif1", + .base = WM5100_AUDIO_IF_1_1 - 1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM5100_RATES, + .formats = WM5100_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = WM5100_RATES, + .formats = WM5100_FORMATS, + }, + .ops = &wm5100_dai_ops, + }, + { + .name = "wm5100-aif2", + .id = 1, + .base = WM5100_AUDIO_IF_2_1 - 1, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM5100_RATES, + .formats = WM5100_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = WM5100_RATES, + .formats = WM5100_FORMATS, + }, + .ops = &wm5100_dai_ops, + }, + { + .name = "wm5100-aif3", + .id = 2, + .base = WM5100_AUDIO_IF_3_1 - 1, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM5100_RATES, + .formats = WM5100_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = WM5100_RATES, + .formats = WM5100_FORMATS, + }, + .ops = &wm5100_dai_ops, + }, +}; + +static int wm5100_dig_vu[] = { + WM5100_ADC_DIGITAL_VOLUME_1L, + WM5100_ADC_DIGITAL_VOLUME_1R, + WM5100_ADC_DIGITAL_VOLUME_2L, + WM5100_ADC_DIGITAL_VOLUME_2R, + WM5100_ADC_DIGITAL_VOLUME_3L, + WM5100_ADC_DIGITAL_VOLUME_3R, + WM5100_ADC_DIGITAL_VOLUME_4L, + WM5100_ADC_DIGITAL_VOLUME_4R, + + WM5100_DAC_DIGITAL_VOLUME_1L, + WM5100_DAC_DIGITAL_VOLUME_1R, + WM5100_DAC_DIGITAL_VOLUME_2L, + WM5100_DAC_DIGITAL_VOLUME_2R, + WM5100_DAC_DIGITAL_VOLUME_3L, + WM5100_DAC_DIGITAL_VOLUME_3R, + WM5100_DAC_DIGITAL_VOLUME_4L, + WM5100_DAC_DIGITAL_VOLUME_4R, + WM5100_DAC_DIGITAL_VOLUME_5L, + WM5100_DAC_DIGITAL_VOLUME_5R, + WM5100_DAC_DIGITAL_VOLUME_6L, + WM5100_DAC_DIGITAL_VOLUME_6R, +}; + +static void wm5100_set_detect_mode(struct wm5100_priv *wm5100, int the_mode) +{ + struct wm5100_jack_mode *mode = &wm5100->pdata.jack_modes[the_mode]; + + if (WARN_ON(the_mode >= ARRAY_SIZE(wm5100->pdata.jack_modes))) + return; + + gpio_set_value_cansleep(wm5100->pdata.hp_pol, mode->hp_pol); + regmap_update_bits(wm5100->regmap, WM5100_ACCESSORY_DETECT_MODE_1, + WM5100_ACCDET_BIAS_SRC_MASK | + WM5100_ACCDET_SRC, + (mode->bias << WM5100_ACCDET_BIAS_SRC_SHIFT) | + mode->micd_src << WM5100_ACCDET_SRC_SHIFT); + regmap_update_bits(wm5100->regmap, WM5100_MISC_CONTROL, + WM5100_HPCOM_SRC, + mode->micd_src << WM5100_HPCOM_SRC_SHIFT); + + wm5100->jack_mode = the_mode; + + dev_dbg(wm5100->dev, "Set microphone polarity to %d\n", + wm5100->jack_mode); +} + +static void wm5100_report_headphone(struct wm5100_priv *wm5100) +{ + dev_dbg(wm5100->dev, "Headphone detected\n"); + wm5100->jack_detecting = false; + snd_soc_jack_report(wm5100->jack, SND_JACK_HEADPHONE, + SND_JACK_HEADPHONE); + + /* Increase the detection rate a bit for responsiveness. */ + regmap_update_bits(wm5100->regmap, WM5100_MIC_DETECT_1, + WM5100_ACCDET_RATE_MASK, + 7 << WM5100_ACCDET_RATE_SHIFT); +} + +static void wm5100_micd_irq(struct wm5100_priv *wm5100) +{ + unsigned int val; + int ret; + + ret = regmap_read(wm5100->regmap, WM5100_MIC_DETECT_3, &val); + if (ret != 0) { + dev_err(wm5100->dev, "Failed to read microphone status: %d\n", + ret); + return; + } + + dev_dbg(wm5100->dev, "Microphone event: %x\n", val); + + if (!(val & WM5100_ACCDET_VALID)) { + dev_warn(wm5100->dev, "Microphone detection state invalid\n"); + return; + } + + /* No accessory, reset everything and report removal */ + if (!(val & WM5100_ACCDET_STS)) { + dev_dbg(wm5100->dev, "Jack removal detected\n"); + wm5100->jack_mic = false; + wm5100->jack_detecting = true; + wm5100->jack_flips = 0; + snd_soc_jack_report(wm5100->jack, 0, + SND_JACK_LINEOUT | SND_JACK_HEADSET | + SND_JACK_BTN_0); + + regmap_update_bits(wm5100->regmap, WM5100_MIC_DETECT_1, + WM5100_ACCDET_RATE_MASK, + WM5100_ACCDET_RATE_MASK); + return; + } + + /* If the measurement is very high we've got a microphone, + * either we just detected one or if we already reported then + * we've got a button release event. + */ + if (val & 0x400) { + if (wm5100->jack_detecting) { + dev_dbg(wm5100->dev, "Microphone detected\n"); + wm5100->jack_mic = true; + wm5100->jack_detecting = false; + snd_soc_jack_report(wm5100->jack, + SND_JACK_HEADSET, + SND_JACK_HEADSET | SND_JACK_BTN_0); + + /* Increase poll rate to give better responsiveness + * for buttons */ + regmap_update_bits(wm5100->regmap, WM5100_MIC_DETECT_1, + WM5100_ACCDET_RATE_MASK, + 5 << WM5100_ACCDET_RATE_SHIFT); + } else { + dev_dbg(wm5100->dev, "Mic button up\n"); + snd_soc_jack_report(wm5100->jack, 0, SND_JACK_BTN_0); + } + + return; + } + + /* If we detected a lower impedence during initial startup + * then we probably have the wrong polarity, flip it. Don't + * do this for the lowest impedences to speed up detection of + * plain headphones and give up if neither polarity looks + * sensible. + */ + if (wm5100->jack_detecting && (val & 0x3f8)) { + wm5100->jack_flips++; + + if (wm5100->jack_flips > 1) + wm5100_report_headphone(wm5100); + else + wm5100_set_detect_mode(wm5100, !wm5100->jack_mode); + + return; + } + + /* Don't distinguish between buttons, just report any low + * impedence as BTN_0. + */ + if (val & 0x3fc) { + if (wm5100->jack_mic) { + dev_dbg(wm5100->dev, "Mic button detected\n"); + snd_soc_jack_report(wm5100->jack, SND_JACK_BTN_0, + SND_JACK_BTN_0); + } else if (wm5100->jack_detecting) { + wm5100_report_headphone(wm5100); + } + } +} + +int wm5100_detect(struct snd_soc_component *component, struct snd_soc_jack *jack) +{ + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + if (jack) { + wm5100->jack = jack; + wm5100->jack_detecting = true; + wm5100->jack_flips = 0; + + wm5100_set_detect_mode(wm5100, 0); + + /* Slowest detection rate, gives debounce for initial + * detection */ + snd_soc_component_update_bits(component, WM5100_MIC_DETECT_1, + WM5100_ACCDET_BIAS_STARTTIME_MASK | + WM5100_ACCDET_RATE_MASK, + (7 << WM5100_ACCDET_BIAS_STARTTIME_SHIFT) | + WM5100_ACCDET_RATE_MASK); + + /* We need the charge pump to power MICBIAS */ + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_force_enable_pin_unlocked(dapm, "CP2"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK"); + + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); + + /* We start off just enabling microphone detection - even a + * plain headphone will trigger detection. + */ + snd_soc_component_update_bits(component, WM5100_MIC_DETECT_1, + WM5100_ACCDET_ENA, WM5100_ACCDET_ENA); + + snd_soc_component_update_bits(component, WM5100_INTERRUPT_STATUS_3_MASK, + WM5100_IM_ACCDET_EINT, 0); + } else { + snd_soc_component_update_bits(component, WM5100_INTERRUPT_STATUS_3_MASK, + WM5100_IM_HPDET_EINT | + WM5100_IM_ACCDET_EINT, + WM5100_IM_HPDET_EINT | + WM5100_IM_ACCDET_EINT); + snd_soc_component_update_bits(component, WM5100_MIC_DETECT_1, + WM5100_ACCDET_ENA, 0); + wm5100->jack = NULL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm5100_detect); + +static irqreturn_t wm5100_irq(int irq, void *data) +{ + struct wm5100_priv *wm5100 = data; + irqreturn_t status = IRQ_NONE; + unsigned int irq_val, mask_val; + int ret; + + ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_3, &irq_val); + if (ret < 0) { + dev_err(wm5100->dev, "Failed to read IRQ status 3: %d\n", + ret); + irq_val = 0; + } + + ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_3_MASK, + &mask_val); + if (ret < 0) { + dev_err(wm5100->dev, "Failed to read IRQ mask 3: %d\n", + ret); + mask_val = 0xffff; + } + + irq_val &= ~mask_val; + + regmap_write(wm5100->regmap, WM5100_INTERRUPT_STATUS_3, irq_val); + + if (irq_val) + status = IRQ_HANDLED; + + wm5100_log_status3(wm5100, irq_val); + + if (irq_val & WM5100_FLL1_LOCK_EINT) { + dev_dbg(wm5100->dev, "FLL1 locked\n"); + complete(&wm5100->fll[0].lock); + } + if (irq_val & WM5100_FLL2_LOCK_EINT) { + dev_dbg(wm5100->dev, "FLL2 locked\n"); + complete(&wm5100->fll[1].lock); + } + + if (irq_val & WM5100_ACCDET_EINT) + wm5100_micd_irq(wm5100); + + ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_4, &irq_val); + if (ret < 0) { + dev_err(wm5100->dev, "Failed to read IRQ status 4: %d\n", + ret); + irq_val = 0; + } + + ret = regmap_read(wm5100->regmap, WM5100_INTERRUPT_STATUS_4_MASK, + &mask_val); + if (ret < 0) { + dev_err(wm5100->dev, "Failed to read IRQ mask 4: %d\n", + ret); + mask_val = 0xffff; + } + + irq_val &= ~mask_val; + + if (irq_val) + status = IRQ_HANDLED; + + regmap_write(wm5100->regmap, WM5100_INTERRUPT_STATUS_4, irq_val); + + wm5100_log_status4(wm5100, irq_val); + + return status; +} + +static irqreturn_t wm5100_edge_irq(int irq, void *data) +{ + irqreturn_t ret = IRQ_NONE; + irqreturn_t val; + + do { + val = wm5100_irq(irq, data); + if (val != IRQ_NONE) + ret = val; + } while (val != IRQ_NONE); + + return ret; +} + +#ifdef CONFIG_GPIOLIB +static void wm5100_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm5100_priv *wm5100 = gpiochip_get_data(chip); + + regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset, + WM5100_GP1_LVL, !!value << WM5100_GP1_LVL_SHIFT); +} + +static int wm5100_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm5100_priv *wm5100 = gpiochip_get_data(chip); + int val, ret; + + val = (1 << WM5100_GP1_FN_SHIFT) | (!!value << WM5100_GP1_LVL_SHIFT); + + ret = regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset, + WM5100_GP1_FN_MASK | WM5100_GP1_DIR | + WM5100_GP1_LVL, val); + if (ret < 0) + return ret; + else + return 0; +} + +static int wm5100_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm5100_priv *wm5100 = gpiochip_get_data(chip); + unsigned int reg; + int ret; + + ret = regmap_read(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset, ®); + if (ret < 0) + return ret; + + return (reg & WM5100_GP1_LVL) != 0; +} + +static int wm5100_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm5100_priv *wm5100 = gpiochip_get_data(chip); + + return regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset, + WM5100_GP1_FN_MASK | WM5100_GP1_DIR, + (1 << WM5100_GP1_FN_SHIFT) | + (1 << WM5100_GP1_DIR_SHIFT)); +} + +static const struct gpio_chip wm5100_template_chip = { + .label = "wm5100", + .owner = THIS_MODULE, + .direction_output = wm5100_gpio_direction_out, + .set = wm5100_gpio_set, + .direction_input = wm5100_gpio_direction_in, + .get = wm5100_gpio_get, + .can_sleep = 1, +}; + +static void wm5100_init_gpio(struct i2c_client *i2c) +{ + struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c); + int ret; + + wm5100->gpio_chip = wm5100_template_chip; + wm5100->gpio_chip.ngpio = 6; + wm5100->gpio_chip.parent = &i2c->dev; + + if (wm5100->pdata.gpio_base) + wm5100->gpio_chip.base = wm5100->pdata.gpio_base; + else + wm5100->gpio_chip.base = -1; + + ret = gpiochip_add_data(&wm5100->gpio_chip, wm5100); + if (ret != 0) + dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void wm5100_free_gpio(struct i2c_client *i2c) +{ + struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c); + + gpiochip_remove(&wm5100->gpio_chip); +} +#else +static void wm5100_init_gpio(struct i2c_client *i2c) +{ +} + +static void wm5100_free_gpio(struct i2c_client *i2c) +{ +} +#endif + +static int wm5100_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct i2c_client *i2c = to_i2c_client(component->dev); + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + int ret, i; + + wm5100->component = component; + + for (i = 0; i < ARRAY_SIZE(wm5100_dig_vu); i++) + snd_soc_component_update_bits(component, wm5100_dig_vu[i], WM5100_OUT_VU, + WM5100_OUT_VU); + + /* Don't debounce interrupts to support use of SYSCLK only */ + snd_soc_component_write(component, WM5100_IRQ_DEBOUNCE_1, 0); + snd_soc_component_write(component, WM5100_IRQ_DEBOUNCE_2, 0); + + /* TODO: check if we're symmetric */ + + if (i2c->irq) + snd_soc_dapm_new_controls(dapm, wm5100_dapm_widgets_noirq, + ARRAY_SIZE(wm5100_dapm_widgets_noirq)); + + if (wm5100->pdata.hp_pol) { + ret = gpio_request_one(wm5100->pdata.hp_pol, + GPIOF_OUT_INIT_HIGH, "WM5100 HP_POL"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request HP_POL %d: %d\n", + wm5100->pdata.hp_pol, ret); + goto err_gpio; + } + } + + return 0; + +err_gpio: + + return ret; +} + +static void wm5100_remove(struct snd_soc_component *component) +{ + struct wm5100_priv *wm5100 = snd_soc_component_get_drvdata(component); + + if (wm5100->pdata.hp_pol) { + gpio_free(wm5100->pdata.hp_pol); + } +} + +static const struct snd_soc_component_driver soc_component_dev_wm5100 = { + .probe = wm5100_probe, + .remove = wm5100_remove, + .set_sysclk = wm5100_set_sysclk, + .set_pll = wm5100_set_fll, + .seq_notifier = wm5100_seq_notifier, + .controls = wm5100_snd_controls, + .num_controls = ARRAY_SIZE(wm5100_snd_controls), + .dapm_widgets = wm5100_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm5100_dapm_widgets), + .dapm_routes = wm5100_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm5100_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm5100_regmap = { + .reg_bits = 16, + .val_bits = 16, + + .max_register = WM5100_MAX_REGISTER, + .reg_defaults = wm5100_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm5100_reg_defaults), + .volatile_reg = wm5100_volatile_register, + .readable_reg = wm5100_readable_register, + .cache_type = REGCACHE_MAPLE, +}; + +static const unsigned int wm5100_mic_ctrl_reg[] = { + WM5100_IN1L_CONTROL, + WM5100_IN2L_CONTROL, + WM5100_IN3L_CONTROL, + WM5100_IN4L_CONTROL, +}; + +static int wm5100_i2c_probe(struct i2c_client *i2c) +{ + struct wm5100_pdata *pdata = dev_get_platdata(&i2c->dev); + struct wm5100_priv *wm5100; + unsigned int reg; + int ret, i, irq_flags; + + wm5100 = devm_kzalloc(&i2c->dev, sizeof(struct wm5100_priv), + GFP_KERNEL); + if (wm5100 == NULL) + return -ENOMEM; + + wm5100->dev = &i2c->dev; + + wm5100->regmap = devm_regmap_init_i2c(i2c, &wm5100_regmap); + if (IS_ERR(wm5100->regmap)) { + ret = PTR_ERR(wm5100->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + goto err; + } + + for (i = 0; i < ARRAY_SIZE(wm5100->fll); i++) + init_completion(&wm5100->fll[i].lock); + + if (pdata) + wm5100->pdata = *pdata; + + i2c_set_clientdata(i2c, wm5100); + + for (i = 0; i < ARRAY_SIZE(wm5100->core_supplies); i++) + wm5100->core_supplies[i].supply = wm5100_core_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, + ARRAY_SIZE(wm5100->core_supplies), + wm5100->core_supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request core supplies: %d\n", + ret); + goto err; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies), + wm5100->core_supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable core supplies: %d\n", + ret); + goto err; + } + + if (wm5100->pdata.ldo_ena) { + ret = gpio_request_one(wm5100->pdata.ldo_ena, + GPIOF_OUT_INIT_HIGH, "WM5100 LDOENA"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n", + wm5100->pdata.ldo_ena, ret); + goto err_enable; + } + msleep(2); + } + + if (wm5100->pdata.reset) { + ret = gpio_request_one(wm5100->pdata.reset, + GPIOF_OUT_INIT_HIGH, "WM5100 /RESET"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n", + wm5100->pdata.reset, ret); + goto err_ldo; + } + } + + ret = regmap_read(wm5100->regmap, WM5100_SOFTWARE_RESET, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret); + goto err_reset; + } + switch (reg) { + case 0x8997: + case 0x5100: + break; + + default: + dev_err(&i2c->dev, "Device is not a WM5100, ID is %x\n", reg); + ret = -EINVAL; + goto err_reset; + } + + ret = regmap_read(wm5100->regmap, WM5100_DEVICE_REVISION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read revision register\n"); + goto err_reset; + } + wm5100->rev = reg & WM5100_DEVICE_REVISION_MASK; + + dev_info(&i2c->dev, "revision %c\n", wm5100->rev + 'A'); + + ret = wm5100_reset(wm5100); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to issue reset\n"); + goto err_reset; + } + + switch (wm5100->rev) { + case 0: + ret = regmap_register_patch(wm5100->regmap, + wm5100_reva_patches, + ARRAY_SIZE(wm5100_reva_patches)); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register patches: %d\n", + ret); + goto err_reset; + } + break; + default: + break; + } + + + wm5100_init_gpio(i2c); + + for (i = 0; i < ARRAY_SIZE(wm5100->pdata.gpio_defaults); i++) { + if (!wm5100->pdata.gpio_defaults[i]) + continue; + + regmap_write(wm5100->regmap, WM5100_GPIO_CTRL_1 + i, + wm5100->pdata.gpio_defaults[i]); + } + + for (i = 0; i < ARRAY_SIZE(wm5100->pdata.in_mode); i++) { + regmap_update_bits(wm5100->regmap, wm5100_mic_ctrl_reg[i], + WM5100_IN1_MODE_MASK | + WM5100_IN1_DMIC_SUP_MASK, + (wm5100->pdata.in_mode[i] << + WM5100_IN1_MODE_SHIFT) | + (wm5100->pdata.dmic_sup[i] << + WM5100_IN1_DMIC_SUP_SHIFT)); + } + + if (i2c->irq) { + if (wm5100->pdata.irq_flags) + irq_flags = wm5100->pdata.irq_flags; + else + irq_flags = IRQF_TRIGGER_LOW; + + irq_flags |= IRQF_ONESHOT; + + if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) + ret = request_threaded_irq(i2c->irq, NULL, + wm5100_edge_irq, irq_flags, + "wm5100", wm5100); + else + ret = request_threaded_irq(i2c->irq, NULL, wm5100_irq, + irq_flags, "wm5100", + wm5100); + + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", + i2c->irq, ret); + } else { + /* Enable default interrupts */ + regmap_update_bits(wm5100->regmap, + WM5100_INTERRUPT_STATUS_3_MASK, + WM5100_IM_SPK_SHUTDOWN_WARN_EINT | + WM5100_IM_SPK_SHUTDOWN_EINT | + WM5100_IM_ASRC2_LOCK_EINT | + WM5100_IM_ASRC1_LOCK_EINT | + WM5100_IM_FLL2_LOCK_EINT | + WM5100_IM_FLL1_LOCK_EINT | + WM5100_CLKGEN_ERR_EINT | + WM5100_CLKGEN_ERR_ASYNC_EINT, 0); + + regmap_update_bits(wm5100->regmap, + WM5100_INTERRUPT_STATUS_4_MASK, + WM5100_AIF3_ERR_EINT | + WM5100_AIF2_ERR_EINT | + WM5100_AIF1_ERR_EINT | + WM5100_CTRLIF_ERR_EINT | + WM5100_ISRC2_UNDERCLOCKED_EINT | + WM5100_ISRC1_UNDERCLOCKED_EINT | + WM5100_FX_UNDERCLOCKED_EINT | + WM5100_AIF3_UNDERCLOCKED_EINT | + WM5100_AIF2_UNDERCLOCKED_EINT | + WM5100_AIF1_UNDERCLOCKED_EINT | + WM5100_ASRC_UNDERCLOCKED_EINT | + WM5100_DAC_UNDERCLOCKED_EINT | + WM5100_ADC_UNDERCLOCKED_EINT | + WM5100_MIXER_UNDERCLOCKED_EINT, 0); + } + } + + pm_runtime_set_active(&i2c->dev); + pm_runtime_enable(&i2c->dev); + pm_request_idle(&i2c->dev); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm5100, wm5100_dai, + ARRAY_SIZE(wm5100_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register WM5100: %d\n", ret); + goto err_reset; + } + + return ret; + +err_reset: + pm_runtime_disable(&i2c->dev); + if (i2c->irq) + free_irq(i2c->irq, wm5100); + wm5100_free_gpio(i2c); + if (wm5100->pdata.reset) { + gpio_set_value_cansleep(wm5100->pdata.reset, 0); + gpio_free(wm5100->pdata.reset); + } +err_ldo: + if (wm5100->pdata.ldo_ena) { + gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); + gpio_free(wm5100->pdata.ldo_ena); + } +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), + wm5100->core_supplies); +err: + return ret; +} + +static void wm5100_i2c_remove(struct i2c_client *i2c) +{ + struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c); + + pm_runtime_disable(&i2c->dev); + if (i2c->irq) + free_irq(i2c->irq, wm5100); + wm5100_free_gpio(i2c); + if (wm5100->pdata.reset) { + gpio_set_value_cansleep(wm5100->pdata.reset, 0); + gpio_free(wm5100->pdata.reset); + } + if (wm5100->pdata.ldo_ena) { + gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); + gpio_free(wm5100->pdata.ldo_ena); + } +} + +#ifdef CONFIG_PM +static int wm5100_runtime_suspend(struct device *dev) +{ + struct wm5100_priv *wm5100 = dev_get_drvdata(dev); + + regcache_cache_only(wm5100->regmap, true); + regcache_mark_dirty(wm5100->regmap); + if (wm5100->pdata.ldo_ena) + gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 0); + regulator_bulk_disable(ARRAY_SIZE(wm5100->core_supplies), + wm5100->core_supplies); + + return 0; +} + +static int wm5100_runtime_resume(struct device *dev) +{ + struct wm5100_priv *wm5100 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(wm5100->core_supplies), + wm5100->core_supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", + ret); + return ret; + } + + if (wm5100->pdata.ldo_ena) { + gpio_set_value_cansleep(wm5100->pdata.ldo_ena, 1); + msleep(2); + } + + regcache_cache_only(wm5100->regmap, false); + regcache_sync(wm5100->regmap); + + return 0; +} +#endif + +static const struct dev_pm_ops wm5100_pm = { + SET_RUNTIME_PM_OPS(wm5100_runtime_suspend, wm5100_runtime_resume, + NULL) +}; + +static const struct i2c_device_id wm5100_i2c_id[] = { + { "wm5100", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm5100_i2c_id); + +static struct i2c_driver wm5100_i2c_driver = { + .driver = { + .name = "wm5100", + .pm = &wm5100_pm, + }, + .probe = wm5100_i2c_probe, + .remove = wm5100_i2c_remove, + .id_table = wm5100_i2c_id, +}; + +module_i2c_driver(wm5100_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM5100 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm5100.h b/sound/soc/codecs/wm5100.h new file mode 100644 index 0000000000..602ee96323 --- /dev/null +++ b/sound/soc/codecs/wm5100.h @@ -0,0 +1,5311 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm5100.h -- WM5100 ALSA SoC Audio driver + * + * Copyright 2011 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#ifndef WM5100_ASOC_H +#define WM5100_ASOC_H + +#include +#include + +int wm5100_detect(struct snd_soc_component *component, struct snd_soc_jack *jack); + +#define WM5100_CLK_AIF1 1 +#define WM5100_CLK_AIF2 2 +#define WM5100_CLK_AIF3 3 +#define WM5100_CLK_SYSCLK 4 +#define WM5100_CLK_ASYNCCLK 5 +#define WM5100_CLK_32KHZ 6 +#define WM5100_CLK_OPCLK 7 + +#define WM5100_CLKSRC_MCLK1 0 +#define WM5100_CLKSRC_MCLK2 1 +#define WM5100_CLKSRC_SYSCLK 2 +#define WM5100_CLKSRC_FLL1 4 +#define WM5100_CLKSRC_FLL2 5 +#define WM5100_CLKSRC_AIF1BCLK 8 +#define WM5100_CLKSRC_AIF2BCLK 9 +#define WM5100_CLKSRC_AIF3BCLK 10 +#define WM5100_CLKSRC_ASYNCCLK 0x100 + +#define WM5100_FLL1 1 +#define WM5100_FLL2 2 + +#define WM5100_FLL_SRC_MCLK1 0x0 +#define WM5100_FLL_SRC_MCLK2 0x1 +#define WM5100_FLL_SRC_FLL1 0x4 +#define WM5100_FLL_SRC_FLL2 0x5 +#define WM5100_FLL_SRC_AIF1BCLK 0x8 +#define WM5100_FLL_SRC_AIF2BCLK 0x9 +#define WM5100_FLL_SRC_AIF3BCLK 0xa + +/* + * Register values. + */ +#define WM5100_SOFTWARE_RESET 0x00 +#define WM5100_DEVICE_REVISION 0x01 +#define WM5100_CTRL_IF_1 0x10 +#define WM5100_TONE_GENERATOR_1 0x20 +#define WM5100_PWM_DRIVE_1 0x30 +#define WM5100_PWM_DRIVE_2 0x31 +#define WM5100_PWM_DRIVE_3 0x32 +#define WM5100_CLOCKING_1 0x100 +#define WM5100_CLOCKING_3 0x101 +#define WM5100_CLOCKING_4 0x102 +#define WM5100_CLOCKING_5 0x103 +#define WM5100_CLOCKING_6 0x104 +#define WM5100_CLOCKING_7 0x107 +#define WM5100_CLOCKING_8 0x108 +#define WM5100_ASRC_ENABLE 0x120 +#define WM5100_ASRC_STATUS 0x121 +#define WM5100_ASRC_RATE1 0x122 +#define WM5100_ISRC_1_CTRL_1 0x141 +#define WM5100_ISRC_1_CTRL_2 0x142 +#define WM5100_ISRC_2_CTRL1 0x143 +#define WM5100_ISRC_2_CTRL_2 0x144 +#define WM5100_FLL1_CONTROL_1 0x182 +#define WM5100_FLL1_CONTROL_2 0x183 +#define WM5100_FLL1_CONTROL_3 0x184 +#define WM5100_FLL1_CONTROL_5 0x186 +#define WM5100_FLL1_CONTROL_6 0x187 +#define WM5100_FLL1_EFS_1 0x188 +#define WM5100_FLL2_CONTROL_1 0x1A2 +#define WM5100_FLL2_CONTROL_2 0x1A3 +#define WM5100_FLL2_CONTROL_3 0x1A4 +#define WM5100_FLL2_CONTROL_5 0x1A6 +#define WM5100_FLL2_CONTROL_6 0x1A7 +#define WM5100_FLL2_EFS_1 0x1A8 +#define WM5100_MIC_CHARGE_PUMP_1 0x200 +#define WM5100_MIC_CHARGE_PUMP_2 0x201 +#define WM5100_HP_CHARGE_PUMP_1 0x202 +#define WM5100_LDO1_CONTROL 0x211 +#define WM5100_MIC_BIAS_CTRL_1 0x215 +#define WM5100_MIC_BIAS_CTRL_2 0x216 +#define WM5100_MIC_BIAS_CTRL_3 0x217 +#define WM5100_ACCESSORY_DETECT_MODE_1 0x280 +#define WM5100_HEADPHONE_DETECT_1 0x288 +#define WM5100_HEADPHONE_DETECT_2 0x289 +#define WM5100_MIC_DETECT_1 0x290 +#define WM5100_MIC_DETECT_2 0x291 +#define WM5100_MIC_DETECT_3 0x292 +#define WM5100_MISC_CONTROL 0x2BB +#define WM5100_INPUT_ENABLES 0x301 +#define WM5100_INPUT_ENABLES_STATUS 0x302 +#define WM5100_IN1L_CONTROL 0x310 +#define WM5100_IN1R_CONTROL 0x311 +#define WM5100_IN2L_CONTROL 0x312 +#define WM5100_IN2R_CONTROL 0x313 +#define WM5100_IN3L_CONTROL 0x314 +#define WM5100_IN3R_CONTROL 0x315 +#define WM5100_IN4L_CONTROL 0x316 +#define WM5100_IN4R_CONTROL 0x317 +#define WM5100_RXANC_SRC 0x318 +#define WM5100_INPUT_VOLUME_RAMP 0x319 +#define WM5100_ADC_DIGITAL_VOLUME_1L 0x320 +#define WM5100_ADC_DIGITAL_VOLUME_1R 0x321 +#define WM5100_ADC_DIGITAL_VOLUME_2L 0x322 +#define WM5100_ADC_DIGITAL_VOLUME_2R 0x323 +#define WM5100_ADC_DIGITAL_VOLUME_3L 0x324 +#define WM5100_ADC_DIGITAL_VOLUME_3R 0x325 +#define WM5100_ADC_DIGITAL_VOLUME_4L 0x326 +#define WM5100_ADC_DIGITAL_VOLUME_4R 0x327 +#define WM5100_OUTPUT_ENABLES_2 0x401 +#define WM5100_OUTPUT_STATUS_1 0x402 +#define WM5100_OUTPUT_STATUS_2 0x403 +#define WM5100_CHANNEL_ENABLES_1 0x408 +#define WM5100_OUT_VOLUME_1L 0x410 +#define WM5100_OUT_VOLUME_1R 0x411 +#define WM5100_DAC_VOLUME_LIMIT_1L 0x412 +#define WM5100_DAC_VOLUME_LIMIT_1R 0x413 +#define WM5100_OUT_VOLUME_2L 0x414 +#define WM5100_OUT_VOLUME_2R 0x415 +#define WM5100_DAC_VOLUME_LIMIT_2L 0x416 +#define WM5100_DAC_VOLUME_LIMIT_2R 0x417 +#define WM5100_OUT_VOLUME_3L 0x418 +#define WM5100_OUT_VOLUME_3R 0x419 +#define WM5100_DAC_VOLUME_LIMIT_3L 0x41A +#define WM5100_DAC_VOLUME_LIMIT_3R 0x41B +#define WM5100_OUT_VOLUME_4L 0x41C +#define WM5100_OUT_VOLUME_4R 0x41D +#define WM5100_DAC_VOLUME_LIMIT_5L 0x41E +#define WM5100_DAC_VOLUME_LIMIT_5R 0x41F +#define WM5100_DAC_VOLUME_LIMIT_6L 0x420 +#define WM5100_DAC_VOLUME_LIMIT_6R 0x421 +#define WM5100_DAC_AEC_CONTROL_1 0x440 +#define WM5100_OUTPUT_VOLUME_RAMP 0x441 +#define WM5100_DAC_DIGITAL_VOLUME_1L 0x480 +#define WM5100_DAC_DIGITAL_VOLUME_1R 0x481 +#define WM5100_DAC_DIGITAL_VOLUME_2L 0x482 +#define WM5100_DAC_DIGITAL_VOLUME_2R 0x483 +#define WM5100_DAC_DIGITAL_VOLUME_3L 0x484 +#define WM5100_DAC_DIGITAL_VOLUME_3R 0x485 +#define WM5100_DAC_DIGITAL_VOLUME_4L 0x486 +#define WM5100_DAC_DIGITAL_VOLUME_4R 0x487 +#define WM5100_DAC_DIGITAL_VOLUME_5L 0x488 +#define WM5100_DAC_DIGITAL_VOLUME_5R 0x489 +#define WM5100_DAC_DIGITAL_VOLUME_6L 0x48A +#define WM5100_DAC_DIGITAL_VOLUME_6R 0x48B +#define WM5100_PDM_SPK1_CTRL_1 0x4C0 +#define WM5100_PDM_SPK1_CTRL_2 0x4C1 +#define WM5100_PDM_SPK2_CTRL_1 0x4C2 +#define WM5100_PDM_SPK2_CTRL_2 0x4C3 +#define WM5100_AUDIO_IF_1_1 0x500 +#define WM5100_AUDIO_IF_1_2 0x501 +#define WM5100_AUDIO_IF_1_3 0x502 +#define WM5100_AUDIO_IF_1_4 0x503 +#define WM5100_AUDIO_IF_1_5 0x504 +#define WM5100_AUDIO_IF_1_6 0x505 +#define WM5100_AUDIO_IF_1_7 0x506 +#define WM5100_AUDIO_IF_1_8 0x507 +#define WM5100_AUDIO_IF_1_9 0x508 +#define WM5100_AUDIO_IF_1_10 0x509 +#define WM5100_AUDIO_IF_1_11 0x50A +#define WM5100_AUDIO_IF_1_12 0x50B +#define WM5100_AUDIO_IF_1_13 0x50C +#define WM5100_AUDIO_IF_1_14 0x50D +#define WM5100_AUDIO_IF_1_15 0x50E +#define WM5100_AUDIO_IF_1_16 0x50F +#define WM5100_AUDIO_IF_1_17 0x510 +#define WM5100_AUDIO_IF_1_18 0x511 +#define WM5100_AUDIO_IF_1_19 0x512 +#define WM5100_AUDIO_IF_1_20 0x513 +#define WM5100_AUDIO_IF_1_21 0x514 +#define WM5100_AUDIO_IF_1_22 0x515 +#define WM5100_AUDIO_IF_1_23 0x516 +#define WM5100_AUDIO_IF_1_24 0x517 +#define WM5100_AUDIO_IF_1_25 0x518 +#define WM5100_AUDIO_IF_1_26 0x519 +#define WM5100_AUDIO_IF_1_27 0x51A +#define WM5100_AUDIO_IF_2_1 0x540 +#define WM5100_AUDIO_IF_2_2 0x541 +#define WM5100_AUDIO_IF_2_3 0x542 +#define WM5100_AUDIO_IF_2_4 0x543 +#define WM5100_AUDIO_IF_2_5 0x544 +#define WM5100_AUDIO_IF_2_6 0x545 +#define WM5100_AUDIO_IF_2_7 0x546 +#define WM5100_AUDIO_IF_2_8 0x547 +#define WM5100_AUDIO_IF_2_9 0x548 +#define WM5100_AUDIO_IF_2_10 0x549 +#define WM5100_AUDIO_IF_2_11 0x54A +#define WM5100_AUDIO_IF_2_18 0x551 +#define WM5100_AUDIO_IF_2_19 0x552 +#define WM5100_AUDIO_IF_2_26 0x559 +#define WM5100_AUDIO_IF_2_27 0x55A +#define WM5100_AUDIO_IF_3_1 0x580 +#define WM5100_AUDIO_IF_3_2 0x581 +#define WM5100_AUDIO_IF_3_3 0x582 +#define WM5100_AUDIO_IF_3_4 0x583 +#define WM5100_AUDIO_IF_3_5 0x584 +#define WM5100_AUDIO_IF_3_6 0x585 +#define WM5100_AUDIO_IF_3_7 0x586 +#define WM5100_AUDIO_IF_3_8 0x587 +#define WM5100_AUDIO_IF_3_9 0x588 +#define WM5100_AUDIO_IF_3_10 0x589 +#define WM5100_AUDIO_IF_3_11 0x58A +#define WM5100_AUDIO_IF_3_18 0x591 +#define WM5100_AUDIO_IF_3_19 0x592 +#define WM5100_AUDIO_IF_3_26 0x599 +#define WM5100_AUDIO_IF_3_27 0x59A +#define WM5100_PWM1MIX_INPUT_1_SOURCE 0x640 +#define WM5100_PWM1MIX_INPUT_1_VOLUME 0x641 +#define WM5100_PWM1MIX_INPUT_2_SOURCE 0x642 +#define WM5100_PWM1MIX_INPUT_2_VOLUME 0x643 +#define WM5100_PWM1MIX_INPUT_3_SOURCE 0x644 +#define WM5100_PWM1MIX_INPUT_3_VOLUME 0x645 +#define WM5100_PWM1MIX_INPUT_4_SOURCE 0x646 +#define WM5100_PWM1MIX_INPUT_4_VOLUME 0x647 +#define WM5100_PWM2MIX_INPUT_1_SOURCE 0x648 +#define WM5100_PWM2MIX_INPUT_1_VOLUME 0x649 +#define WM5100_PWM2MIX_INPUT_2_SOURCE 0x64A +#define WM5100_PWM2MIX_INPUT_2_VOLUME 0x64B +#define WM5100_PWM2MIX_INPUT_3_SOURCE 0x64C +#define WM5100_PWM2MIX_INPUT_3_VOLUME 0x64D +#define WM5100_PWM2MIX_INPUT_4_SOURCE 0x64E +#define WM5100_PWM2MIX_INPUT_4_VOLUME 0x64F +#define WM5100_OUT1LMIX_INPUT_1_SOURCE 0x680 +#define WM5100_OUT1LMIX_INPUT_1_VOLUME 0x681 +#define WM5100_OUT1LMIX_INPUT_2_SOURCE 0x682 +#define WM5100_OUT1LMIX_INPUT_2_VOLUME 0x683 +#define WM5100_OUT1LMIX_INPUT_3_SOURCE 0x684 +#define WM5100_OUT1LMIX_INPUT_3_VOLUME 0x685 +#define WM5100_OUT1LMIX_INPUT_4_SOURCE 0x686 +#define WM5100_OUT1LMIX_INPUT_4_VOLUME 0x687 +#define WM5100_OUT1RMIX_INPUT_1_SOURCE 0x688 +#define WM5100_OUT1RMIX_INPUT_1_VOLUME 0x689 +#define WM5100_OUT1RMIX_INPUT_2_SOURCE 0x68A +#define WM5100_OUT1RMIX_INPUT_2_VOLUME 0x68B +#define WM5100_OUT1RMIX_INPUT_3_SOURCE 0x68C +#define WM5100_OUT1RMIX_INPUT_3_VOLUME 0x68D +#define WM5100_OUT1RMIX_INPUT_4_SOURCE 0x68E +#define WM5100_OUT1RMIX_INPUT_4_VOLUME 0x68F +#define WM5100_OUT2LMIX_INPUT_1_SOURCE 0x690 +#define WM5100_OUT2LMIX_INPUT_1_VOLUME 0x691 +#define WM5100_OUT2LMIX_INPUT_2_SOURCE 0x692 +#define WM5100_OUT2LMIX_INPUT_2_VOLUME 0x693 +#define WM5100_OUT2LMIX_INPUT_3_SOURCE 0x694 +#define WM5100_OUT2LMIX_INPUT_3_VOLUME 0x695 +#define WM5100_OUT2LMIX_INPUT_4_SOURCE 0x696 +#define WM5100_OUT2LMIX_INPUT_4_VOLUME 0x697 +#define WM5100_OUT2RMIX_INPUT_1_SOURCE 0x698 +#define WM5100_OUT2RMIX_INPUT_1_VOLUME 0x699 +#define WM5100_OUT2RMIX_INPUT_2_SOURCE 0x69A +#define WM5100_OUT2RMIX_INPUT_2_VOLUME 0x69B +#define WM5100_OUT2RMIX_INPUT_3_SOURCE 0x69C +#define WM5100_OUT2RMIX_INPUT_3_VOLUME 0x69D +#define WM5100_OUT2RMIX_INPUT_4_SOURCE 0x69E +#define WM5100_OUT2RMIX_INPUT_4_VOLUME 0x69F +#define WM5100_OUT3LMIX_INPUT_1_SOURCE 0x6A0 +#define WM5100_OUT3LMIX_INPUT_1_VOLUME 0x6A1 +#define WM5100_OUT3LMIX_INPUT_2_SOURCE 0x6A2 +#define WM5100_OUT3LMIX_INPUT_2_VOLUME 0x6A3 +#define WM5100_OUT3LMIX_INPUT_3_SOURCE 0x6A4 +#define WM5100_OUT3LMIX_INPUT_3_VOLUME 0x6A5 +#define WM5100_OUT3LMIX_INPUT_4_SOURCE 0x6A6 +#define WM5100_OUT3LMIX_INPUT_4_VOLUME 0x6A7 +#define WM5100_OUT3RMIX_INPUT_1_SOURCE 0x6A8 +#define WM5100_OUT3RMIX_INPUT_1_VOLUME 0x6A9 +#define WM5100_OUT3RMIX_INPUT_2_SOURCE 0x6AA +#define WM5100_OUT3RMIX_INPUT_2_VOLUME 0x6AB +#define WM5100_OUT3RMIX_INPUT_3_SOURCE 0x6AC +#define WM5100_OUT3RMIX_INPUT_3_VOLUME 0x6AD +#define WM5100_OUT3RMIX_INPUT_4_SOURCE 0x6AE +#define WM5100_OUT3RMIX_INPUT_4_VOLUME 0x6AF +#define WM5100_OUT4LMIX_INPUT_1_SOURCE 0x6B0 +#define WM5100_OUT4LMIX_INPUT_1_VOLUME 0x6B1 +#define WM5100_OUT4LMIX_INPUT_2_SOURCE 0x6B2 +#define WM5100_OUT4LMIX_INPUT_2_VOLUME 0x6B3 +#define WM5100_OUT4LMIX_INPUT_3_SOURCE 0x6B4 +#define WM5100_OUT4LMIX_INPUT_3_VOLUME 0x6B5 +#define WM5100_OUT4LMIX_INPUT_4_SOURCE 0x6B6 +#define WM5100_OUT4LMIX_INPUT_4_VOLUME 0x6B7 +#define WM5100_OUT4RMIX_INPUT_1_SOURCE 0x6B8 +#define WM5100_OUT4RMIX_INPUT_1_VOLUME 0x6B9 +#define WM5100_OUT4RMIX_INPUT_2_SOURCE 0x6BA +#define WM5100_OUT4RMIX_INPUT_2_VOLUME 0x6BB +#define WM5100_OUT4RMIX_INPUT_3_SOURCE 0x6BC +#define WM5100_OUT4RMIX_INPUT_3_VOLUME 0x6BD +#define WM5100_OUT4RMIX_INPUT_4_SOURCE 0x6BE +#define WM5100_OUT4RMIX_INPUT_4_VOLUME 0x6BF +#define WM5100_OUT5LMIX_INPUT_1_SOURCE 0x6C0 +#define WM5100_OUT5LMIX_INPUT_1_VOLUME 0x6C1 +#define WM5100_OUT5LMIX_INPUT_2_SOURCE 0x6C2 +#define WM5100_OUT5LMIX_INPUT_2_VOLUME 0x6C3 +#define WM5100_OUT5LMIX_INPUT_3_SOURCE 0x6C4 +#define WM5100_OUT5LMIX_INPUT_3_VOLUME 0x6C5 +#define WM5100_OUT5LMIX_INPUT_4_SOURCE 0x6C6 +#define WM5100_OUT5LMIX_INPUT_4_VOLUME 0x6C7 +#define WM5100_OUT5RMIX_INPUT_1_SOURCE 0x6C8 +#define WM5100_OUT5RMIX_INPUT_1_VOLUME 0x6C9 +#define WM5100_OUT5RMIX_INPUT_2_SOURCE 0x6CA +#define WM5100_OUT5RMIX_INPUT_2_VOLUME 0x6CB +#define WM5100_OUT5RMIX_INPUT_3_SOURCE 0x6CC +#define WM5100_OUT5RMIX_INPUT_3_VOLUME 0x6CD +#define WM5100_OUT5RMIX_INPUT_4_SOURCE 0x6CE +#define WM5100_OUT5RMIX_INPUT_4_VOLUME 0x6CF +#define WM5100_OUT6LMIX_INPUT_1_SOURCE 0x6D0 +#define WM5100_OUT6LMIX_INPUT_1_VOLUME 0x6D1 +#define WM5100_OUT6LMIX_INPUT_2_SOURCE 0x6D2 +#define WM5100_OUT6LMIX_INPUT_2_VOLUME 0x6D3 +#define WM5100_OUT6LMIX_INPUT_3_SOURCE 0x6D4 +#define WM5100_OUT6LMIX_INPUT_3_VOLUME 0x6D5 +#define WM5100_OUT6LMIX_INPUT_4_SOURCE 0x6D6 +#define WM5100_OUT6LMIX_INPUT_4_VOLUME 0x6D7 +#define WM5100_OUT6RMIX_INPUT_1_SOURCE 0x6D8 +#define WM5100_OUT6RMIX_INPUT_1_VOLUME 0x6D9 +#define WM5100_OUT6RMIX_INPUT_2_SOURCE 0x6DA +#define WM5100_OUT6RMIX_INPUT_2_VOLUME 0x6DB +#define WM5100_OUT6RMIX_INPUT_3_SOURCE 0x6DC +#define WM5100_OUT6RMIX_INPUT_3_VOLUME 0x6DD +#define WM5100_OUT6RMIX_INPUT_4_SOURCE 0x6DE +#define WM5100_OUT6RMIX_INPUT_4_VOLUME 0x6DF +#define WM5100_AIF1TX1MIX_INPUT_1_SOURCE 0x700 +#define WM5100_AIF1TX1MIX_INPUT_1_VOLUME 0x701 +#define WM5100_AIF1TX1MIX_INPUT_2_SOURCE 0x702 +#define WM5100_AIF1TX1MIX_INPUT_2_VOLUME 0x703 +#define WM5100_AIF1TX1MIX_INPUT_3_SOURCE 0x704 +#define WM5100_AIF1TX1MIX_INPUT_3_VOLUME 0x705 +#define WM5100_AIF1TX1MIX_INPUT_4_SOURCE 0x706 +#define WM5100_AIF1TX1MIX_INPUT_4_VOLUME 0x707 +#define WM5100_AIF1TX2MIX_INPUT_1_SOURCE 0x708 +#define WM5100_AIF1TX2MIX_INPUT_1_VOLUME 0x709 +#define WM5100_AIF1TX2MIX_INPUT_2_SOURCE 0x70A +#define WM5100_AIF1TX2MIX_INPUT_2_VOLUME 0x70B +#define WM5100_AIF1TX2MIX_INPUT_3_SOURCE 0x70C +#define WM5100_AIF1TX2MIX_INPUT_3_VOLUME 0x70D +#define WM5100_AIF1TX2MIX_INPUT_4_SOURCE 0x70E +#define WM5100_AIF1TX2MIX_INPUT_4_VOLUME 0x70F +#define WM5100_AIF1TX3MIX_INPUT_1_SOURCE 0x710 +#define WM5100_AIF1TX3MIX_INPUT_1_VOLUME 0x711 +#define WM5100_AIF1TX3MIX_INPUT_2_SOURCE 0x712 +#define WM5100_AIF1TX3MIX_INPUT_2_VOLUME 0x713 +#define WM5100_AIF1TX3MIX_INPUT_3_SOURCE 0x714 +#define WM5100_AIF1TX3MIX_INPUT_3_VOLUME 0x715 +#define WM5100_AIF1TX3MIX_INPUT_4_SOURCE 0x716 +#define WM5100_AIF1TX3MIX_INPUT_4_VOLUME 0x717 +#define WM5100_AIF1TX4MIX_INPUT_1_SOURCE 0x718 +#define WM5100_AIF1TX4MIX_INPUT_1_VOLUME 0x719 +#define WM5100_AIF1TX4MIX_INPUT_2_SOURCE 0x71A +#define WM5100_AIF1TX4MIX_INPUT_2_VOLUME 0x71B +#define WM5100_AIF1TX4MIX_INPUT_3_SOURCE 0x71C +#define WM5100_AIF1TX4MIX_INPUT_3_VOLUME 0x71D +#define WM5100_AIF1TX4MIX_INPUT_4_SOURCE 0x71E +#define WM5100_AIF1TX4MIX_INPUT_4_VOLUME 0x71F +#define WM5100_AIF1TX5MIX_INPUT_1_SOURCE 0x720 +#define WM5100_AIF1TX5MIX_INPUT_1_VOLUME 0x721 +#define WM5100_AIF1TX5MIX_INPUT_2_SOURCE 0x722 +#define WM5100_AIF1TX5MIX_INPUT_2_VOLUME 0x723 +#define WM5100_AIF1TX5MIX_INPUT_3_SOURCE 0x724 +#define WM5100_AIF1TX5MIX_INPUT_3_VOLUME 0x725 +#define WM5100_AIF1TX5MIX_INPUT_4_SOURCE 0x726 +#define WM5100_AIF1TX5MIX_INPUT_4_VOLUME 0x727 +#define WM5100_AIF1TX6MIX_INPUT_1_SOURCE 0x728 +#define WM5100_AIF1TX6MIX_INPUT_1_VOLUME 0x729 +#define WM5100_AIF1TX6MIX_INPUT_2_SOURCE 0x72A +#define WM5100_AIF1TX6MIX_INPUT_2_VOLUME 0x72B +#define WM5100_AIF1TX6MIX_INPUT_3_SOURCE 0x72C +#define WM5100_AIF1TX6MIX_INPUT_3_VOLUME 0x72D +#define WM5100_AIF1TX6MIX_INPUT_4_SOURCE 0x72E +#define WM5100_AIF1TX6MIX_INPUT_4_VOLUME 0x72F +#define WM5100_AIF1TX7MIX_INPUT_1_SOURCE 0x730 +#define WM5100_AIF1TX7MIX_INPUT_1_VOLUME 0x731 +#define WM5100_AIF1TX7MIX_INPUT_2_SOURCE 0x732 +#define WM5100_AIF1TX7MIX_INPUT_2_VOLUME 0x733 +#define WM5100_AIF1TX7MIX_INPUT_3_SOURCE 0x734 +#define WM5100_AIF1TX7MIX_INPUT_3_VOLUME 0x735 +#define WM5100_AIF1TX7MIX_INPUT_4_SOURCE 0x736 +#define WM5100_AIF1TX7MIX_INPUT_4_VOLUME 0x737 +#define WM5100_AIF1TX8MIX_INPUT_1_SOURCE 0x738 +#define WM5100_AIF1TX8MIX_INPUT_1_VOLUME 0x739 +#define WM5100_AIF1TX8MIX_INPUT_2_SOURCE 0x73A +#define WM5100_AIF1TX8MIX_INPUT_2_VOLUME 0x73B +#define WM5100_AIF1TX8MIX_INPUT_3_SOURCE 0x73C +#define WM5100_AIF1TX8MIX_INPUT_3_VOLUME 0x73D +#define WM5100_AIF1TX8MIX_INPUT_4_SOURCE 0x73E +#define WM5100_AIF1TX8MIX_INPUT_4_VOLUME 0x73F +#define WM5100_AIF2TX1MIX_INPUT_1_SOURCE 0x740 +#define WM5100_AIF2TX1MIX_INPUT_1_VOLUME 0x741 +#define WM5100_AIF2TX1MIX_INPUT_2_SOURCE 0x742 +#define WM5100_AIF2TX1MIX_INPUT_2_VOLUME 0x743 +#define WM5100_AIF2TX1MIX_INPUT_3_SOURCE 0x744 +#define WM5100_AIF2TX1MIX_INPUT_3_VOLUME 0x745 +#define WM5100_AIF2TX1MIX_INPUT_4_SOURCE 0x746 +#define WM5100_AIF2TX1MIX_INPUT_4_VOLUME 0x747 +#define WM5100_AIF2TX2MIX_INPUT_1_SOURCE 0x748 +#define WM5100_AIF2TX2MIX_INPUT_1_VOLUME 0x749 +#define WM5100_AIF2TX2MIX_INPUT_2_SOURCE 0x74A +#define WM5100_AIF2TX2MIX_INPUT_2_VOLUME 0x74B +#define WM5100_AIF2TX2MIX_INPUT_3_SOURCE 0x74C +#define WM5100_AIF2TX2MIX_INPUT_3_VOLUME 0x74D +#define WM5100_AIF2TX2MIX_INPUT_4_SOURCE 0x74E +#define WM5100_AIF2TX2MIX_INPUT_4_VOLUME 0x74F +#define WM5100_AIF3TX1MIX_INPUT_1_SOURCE 0x780 +#define WM5100_AIF3TX1MIX_INPUT_1_VOLUME 0x781 +#define WM5100_AIF3TX1MIX_INPUT_2_SOURCE 0x782 +#define WM5100_AIF3TX1MIX_INPUT_2_VOLUME 0x783 +#define WM5100_AIF3TX1MIX_INPUT_3_SOURCE 0x784 +#define WM5100_AIF3TX1MIX_INPUT_3_VOLUME 0x785 +#define WM5100_AIF3TX1MIX_INPUT_4_SOURCE 0x786 +#define WM5100_AIF3TX1MIX_INPUT_4_VOLUME 0x787 +#define WM5100_AIF3TX2MIX_INPUT_1_SOURCE 0x788 +#define WM5100_AIF3TX2MIX_INPUT_1_VOLUME 0x789 +#define WM5100_AIF3TX2MIX_INPUT_2_SOURCE 0x78A +#define WM5100_AIF3TX2MIX_INPUT_2_VOLUME 0x78B +#define WM5100_AIF3TX2MIX_INPUT_3_SOURCE 0x78C +#define WM5100_AIF3TX2MIX_INPUT_3_VOLUME 0x78D +#define WM5100_AIF3TX2MIX_INPUT_4_SOURCE 0x78E +#define WM5100_AIF3TX2MIX_INPUT_4_VOLUME 0x78F +#define WM5100_EQ1MIX_INPUT_1_SOURCE 0x880 +#define WM5100_EQ1MIX_INPUT_1_VOLUME 0x881 +#define WM5100_EQ1MIX_INPUT_2_SOURCE 0x882 +#define WM5100_EQ1MIX_INPUT_2_VOLUME 0x883 +#define WM5100_EQ1MIX_INPUT_3_SOURCE 0x884 +#define WM5100_EQ1MIX_INPUT_3_VOLUME 0x885 +#define WM5100_EQ1MIX_INPUT_4_SOURCE 0x886 +#define WM5100_EQ1MIX_INPUT_4_VOLUME 0x887 +#define WM5100_EQ2MIX_INPUT_1_SOURCE 0x888 +#define WM5100_EQ2MIX_INPUT_1_VOLUME 0x889 +#define WM5100_EQ2MIX_INPUT_2_SOURCE 0x88A +#define WM5100_EQ2MIX_INPUT_2_VOLUME 0x88B +#define WM5100_EQ2MIX_INPUT_3_SOURCE 0x88C +#define WM5100_EQ2MIX_INPUT_3_VOLUME 0x88D +#define WM5100_EQ2MIX_INPUT_4_SOURCE 0x88E +#define WM5100_EQ2MIX_INPUT_4_VOLUME 0x88F +#define WM5100_EQ3MIX_INPUT_1_SOURCE 0x890 +#define WM5100_EQ3MIX_INPUT_1_VOLUME 0x891 +#define WM5100_EQ3MIX_INPUT_2_SOURCE 0x892 +#define WM5100_EQ3MIX_INPUT_2_VOLUME 0x893 +#define WM5100_EQ3MIX_INPUT_3_SOURCE 0x894 +#define WM5100_EQ3MIX_INPUT_3_VOLUME 0x895 +#define WM5100_EQ3MIX_INPUT_4_SOURCE 0x896 +#define WM5100_EQ3MIX_INPUT_4_VOLUME 0x897 +#define WM5100_EQ4MIX_INPUT_1_SOURCE 0x898 +#define WM5100_EQ4MIX_INPUT_1_VOLUME 0x899 +#define WM5100_EQ4MIX_INPUT_2_SOURCE 0x89A +#define WM5100_EQ4MIX_INPUT_2_VOLUME 0x89B +#define WM5100_EQ4MIX_INPUT_3_SOURCE 0x89C +#define WM5100_EQ4MIX_INPUT_3_VOLUME 0x89D +#define WM5100_EQ4MIX_INPUT_4_SOURCE 0x89E +#define WM5100_EQ4MIX_INPUT_4_VOLUME 0x89F +#define WM5100_DRC1LMIX_INPUT_1_SOURCE 0x8C0 +#define WM5100_DRC1LMIX_INPUT_1_VOLUME 0x8C1 +#define WM5100_DRC1LMIX_INPUT_2_SOURCE 0x8C2 +#define WM5100_DRC1LMIX_INPUT_2_VOLUME 0x8C3 +#define WM5100_DRC1LMIX_INPUT_3_SOURCE 0x8C4 +#define WM5100_DRC1LMIX_INPUT_3_VOLUME 0x8C5 +#define WM5100_DRC1LMIX_INPUT_4_SOURCE 0x8C6 +#define WM5100_DRC1LMIX_INPUT_4_VOLUME 0x8C7 +#define WM5100_DRC1RMIX_INPUT_1_SOURCE 0x8C8 +#define WM5100_DRC1RMIX_INPUT_1_VOLUME 0x8C9 +#define WM5100_DRC1RMIX_INPUT_2_SOURCE 0x8CA +#define WM5100_DRC1RMIX_INPUT_2_VOLUME 0x8CB +#define WM5100_DRC1RMIX_INPUT_3_SOURCE 0x8CC +#define WM5100_DRC1RMIX_INPUT_3_VOLUME 0x8CD +#define WM5100_DRC1RMIX_INPUT_4_SOURCE 0x8CE +#define WM5100_DRC1RMIX_INPUT_4_VOLUME 0x8CF +#define WM5100_HPLP1MIX_INPUT_1_SOURCE 0x900 +#define WM5100_HPLP1MIX_INPUT_1_VOLUME 0x901 +#define WM5100_HPLP1MIX_INPUT_2_SOURCE 0x902 +#define WM5100_HPLP1MIX_INPUT_2_VOLUME 0x903 +#define WM5100_HPLP1MIX_INPUT_3_SOURCE 0x904 +#define WM5100_HPLP1MIX_INPUT_3_VOLUME 0x905 +#define WM5100_HPLP1MIX_INPUT_4_SOURCE 0x906 +#define WM5100_HPLP1MIX_INPUT_4_VOLUME 0x907 +#define WM5100_HPLP2MIX_INPUT_1_SOURCE 0x908 +#define WM5100_HPLP2MIX_INPUT_1_VOLUME 0x909 +#define WM5100_HPLP2MIX_INPUT_2_SOURCE 0x90A +#define WM5100_HPLP2MIX_INPUT_2_VOLUME 0x90B +#define WM5100_HPLP2MIX_INPUT_3_SOURCE 0x90C +#define WM5100_HPLP2MIX_INPUT_3_VOLUME 0x90D +#define WM5100_HPLP2MIX_INPUT_4_SOURCE 0x90E +#define WM5100_HPLP2MIX_INPUT_4_VOLUME 0x90F +#define WM5100_HPLP3MIX_INPUT_1_SOURCE 0x910 +#define WM5100_HPLP3MIX_INPUT_1_VOLUME 0x911 +#define WM5100_HPLP3MIX_INPUT_2_SOURCE 0x912 +#define WM5100_HPLP3MIX_INPUT_2_VOLUME 0x913 +#define WM5100_HPLP3MIX_INPUT_3_SOURCE 0x914 +#define WM5100_HPLP3MIX_INPUT_3_VOLUME 0x915 +#define WM5100_HPLP3MIX_INPUT_4_SOURCE 0x916 +#define WM5100_HPLP3MIX_INPUT_4_VOLUME 0x917 +#define WM5100_HPLP4MIX_INPUT_1_SOURCE 0x918 +#define WM5100_HPLP4MIX_INPUT_1_VOLUME 0x919 +#define WM5100_HPLP4MIX_INPUT_2_SOURCE 0x91A +#define WM5100_HPLP4MIX_INPUT_2_VOLUME 0x91B +#define WM5100_HPLP4MIX_INPUT_3_SOURCE 0x91C +#define WM5100_HPLP4MIX_INPUT_3_VOLUME 0x91D +#define WM5100_HPLP4MIX_INPUT_4_SOURCE 0x91E +#define WM5100_HPLP4MIX_INPUT_4_VOLUME 0x91F +#define WM5100_DSP1LMIX_INPUT_1_SOURCE 0x940 +#define WM5100_DSP1LMIX_INPUT_1_VOLUME 0x941 +#define WM5100_DSP1LMIX_INPUT_2_SOURCE 0x942 +#define WM5100_DSP1LMIX_INPUT_2_VOLUME 0x943 +#define WM5100_DSP1LMIX_INPUT_3_SOURCE 0x944 +#define WM5100_DSP1LMIX_INPUT_3_VOLUME 0x945 +#define WM5100_DSP1LMIX_INPUT_4_SOURCE 0x946 +#define WM5100_DSP1LMIX_INPUT_4_VOLUME 0x947 +#define WM5100_DSP1RMIX_INPUT_1_SOURCE 0x948 +#define WM5100_DSP1RMIX_INPUT_1_VOLUME 0x949 +#define WM5100_DSP1RMIX_INPUT_2_SOURCE 0x94A +#define WM5100_DSP1RMIX_INPUT_2_VOLUME 0x94B +#define WM5100_DSP1RMIX_INPUT_3_SOURCE 0x94C +#define WM5100_DSP1RMIX_INPUT_3_VOLUME 0x94D +#define WM5100_DSP1RMIX_INPUT_4_SOURCE 0x94E +#define WM5100_DSP1RMIX_INPUT_4_VOLUME 0x94F +#define WM5100_DSP1AUX1MIX_INPUT_1_SOURCE 0x950 +#define WM5100_DSP1AUX2MIX_INPUT_1_SOURCE 0x958 +#define WM5100_DSP1AUX3MIX_INPUT_1_SOURCE 0x960 +#define WM5100_DSP1AUX4MIX_INPUT_1_SOURCE 0x968 +#define WM5100_DSP1AUX5MIX_INPUT_1_SOURCE 0x970 +#define WM5100_DSP1AUX6MIX_INPUT_1_SOURCE 0x978 +#define WM5100_DSP2LMIX_INPUT_1_SOURCE 0x980 +#define WM5100_DSP2LMIX_INPUT_1_VOLUME 0x981 +#define WM5100_DSP2LMIX_INPUT_2_SOURCE 0x982 +#define WM5100_DSP2LMIX_INPUT_2_VOLUME 0x983 +#define WM5100_DSP2LMIX_INPUT_3_SOURCE 0x984 +#define WM5100_DSP2LMIX_INPUT_3_VOLUME 0x985 +#define WM5100_DSP2LMIX_INPUT_4_SOURCE 0x986 +#define WM5100_DSP2LMIX_INPUT_4_VOLUME 0x987 +#define WM5100_DSP2RMIX_INPUT_1_SOURCE 0x988 +#define WM5100_DSP2RMIX_INPUT_1_VOLUME 0x989 +#define WM5100_DSP2RMIX_INPUT_2_SOURCE 0x98A +#define WM5100_DSP2RMIX_INPUT_2_VOLUME 0x98B +#define WM5100_DSP2RMIX_INPUT_3_SOURCE 0x98C +#define WM5100_DSP2RMIX_INPUT_3_VOLUME 0x98D +#define WM5100_DSP2RMIX_INPUT_4_SOURCE 0x98E +#define WM5100_DSP2RMIX_INPUT_4_VOLUME 0x98F +#define WM5100_DSP2AUX1MIX_INPUT_1_SOURCE 0x990 +#define WM5100_DSP2AUX2MIX_INPUT_1_SOURCE 0x998 +#define WM5100_DSP2AUX3MIX_INPUT_1_SOURCE 0x9A0 +#define WM5100_DSP2AUX4MIX_INPUT_1_SOURCE 0x9A8 +#define WM5100_DSP2AUX5MIX_INPUT_1_SOURCE 0x9B0 +#define WM5100_DSP2AUX6MIX_INPUT_1_SOURCE 0x9B8 +#define WM5100_DSP3LMIX_INPUT_1_SOURCE 0x9C0 +#define WM5100_DSP3LMIX_INPUT_1_VOLUME 0x9C1 +#define WM5100_DSP3LMIX_INPUT_2_SOURCE 0x9C2 +#define WM5100_DSP3LMIX_INPUT_2_VOLUME 0x9C3 +#define WM5100_DSP3LMIX_INPUT_3_SOURCE 0x9C4 +#define WM5100_DSP3LMIX_INPUT_3_VOLUME 0x9C5 +#define WM5100_DSP3LMIX_INPUT_4_SOURCE 0x9C6 +#define WM5100_DSP3LMIX_INPUT_4_VOLUME 0x9C7 +#define WM5100_DSP3RMIX_INPUT_1_SOURCE 0x9C8 +#define WM5100_DSP3RMIX_INPUT_1_VOLUME 0x9C9 +#define WM5100_DSP3RMIX_INPUT_2_SOURCE 0x9CA +#define WM5100_DSP3RMIX_INPUT_2_VOLUME 0x9CB +#define WM5100_DSP3RMIX_INPUT_3_SOURCE 0x9CC +#define WM5100_DSP3RMIX_INPUT_3_VOLUME 0x9CD +#define WM5100_DSP3RMIX_INPUT_4_SOURCE 0x9CE +#define WM5100_DSP3RMIX_INPUT_4_VOLUME 0x9CF +#define WM5100_DSP3AUX1MIX_INPUT_1_SOURCE 0x9D0 +#define WM5100_DSP3AUX2MIX_INPUT_1_SOURCE 0x9D8 +#define WM5100_DSP3AUX3MIX_INPUT_1_SOURCE 0x9E0 +#define WM5100_DSP3AUX4MIX_INPUT_1_SOURCE 0x9E8 +#define WM5100_DSP3AUX5MIX_INPUT_1_SOURCE 0x9F0 +#define WM5100_DSP3AUX6MIX_INPUT_1_SOURCE 0x9F8 +#define WM5100_ASRC1LMIX_INPUT_1_SOURCE 0xA80 +#define WM5100_ASRC1RMIX_INPUT_1_SOURCE 0xA88 +#define WM5100_ASRC2LMIX_INPUT_1_SOURCE 0xA90 +#define WM5100_ASRC2RMIX_INPUT_1_SOURCE 0xA98 +#define WM5100_ISRC1DEC1MIX_INPUT_1_SOURCE 0xB00 +#define WM5100_ISRC1DEC2MIX_INPUT_1_SOURCE 0xB08 +#define WM5100_ISRC1DEC3MIX_INPUT_1_SOURCE 0xB10 +#define WM5100_ISRC1DEC4MIX_INPUT_1_SOURCE 0xB18 +#define WM5100_ISRC1INT1MIX_INPUT_1_SOURCE 0xB20 +#define WM5100_ISRC1INT2MIX_INPUT_1_SOURCE 0xB28 +#define WM5100_ISRC1INT3MIX_INPUT_1_SOURCE 0xB30 +#define WM5100_ISRC1INT4MIX_INPUT_1_SOURCE 0xB38 +#define WM5100_ISRC2DEC1MIX_INPUT_1_SOURCE 0xB40 +#define WM5100_ISRC2DEC2MIX_INPUT_1_SOURCE 0xB48 +#define WM5100_ISRC2DEC3MIX_INPUT_1_SOURCE 0xB50 +#define WM5100_ISRC2DEC4MIX_INPUT_1_SOURCE 0xB58 +#define WM5100_ISRC2INT1MIX_INPUT_1_SOURCE 0xB60 +#define WM5100_ISRC2INT2MIX_INPUT_1_SOURCE 0xB68 +#define WM5100_ISRC2INT3MIX_INPUT_1_SOURCE 0xB70 +#define WM5100_ISRC2INT4MIX_INPUT_1_SOURCE 0xB78 +#define WM5100_GPIO_CTRL_1 0xC00 +#define WM5100_GPIO_CTRL_2 0xC01 +#define WM5100_GPIO_CTRL_3 0xC02 +#define WM5100_GPIO_CTRL_4 0xC03 +#define WM5100_GPIO_CTRL_5 0xC04 +#define WM5100_GPIO_CTRL_6 0xC05 +#define WM5100_MISC_PAD_CTRL_1 0xC23 +#define WM5100_MISC_PAD_CTRL_2 0xC24 +#define WM5100_MISC_PAD_CTRL_3 0xC25 +#define WM5100_MISC_PAD_CTRL_4 0xC26 +#define WM5100_MISC_PAD_CTRL_5 0xC27 +#define WM5100_MISC_GPIO_1 0xC28 +#define WM5100_INTERRUPT_STATUS_1 0xD00 +#define WM5100_INTERRUPT_STATUS_2 0xD01 +#define WM5100_INTERRUPT_STATUS_3 0xD02 +#define WM5100_INTERRUPT_STATUS_4 0xD03 +#define WM5100_INTERRUPT_RAW_STATUS_2 0xD04 +#define WM5100_INTERRUPT_RAW_STATUS_3 0xD05 +#define WM5100_INTERRUPT_RAW_STATUS_4 0xD06 +#define WM5100_INTERRUPT_STATUS_1_MASK 0xD07 +#define WM5100_INTERRUPT_STATUS_2_MASK 0xD08 +#define WM5100_INTERRUPT_STATUS_3_MASK 0xD09 +#define WM5100_INTERRUPT_STATUS_4_MASK 0xD0A +#define WM5100_INTERRUPT_CONTROL 0xD1F +#define WM5100_IRQ_DEBOUNCE_1 0xD20 +#define WM5100_IRQ_DEBOUNCE_2 0xD21 +#define WM5100_FX_CTRL 0xE00 +#define WM5100_EQ1_1 0xE10 +#define WM5100_EQ1_2 0xE11 +#define WM5100_EQ1_3 0xE12 +#define WM5100_EQ1_4 0xE13 +#define WM5100_EQ1_5 0xE14 +#define WM5100_EQ1_6 0xE15 +#define WM5100_EQ1_7 0xE16 +#define WM5100_EQ1_8 0xE17 +#define WM5100_EQ1_9 0xE18 +#define WM5100_EQ1_10 0xE19 +#define WM5100_EQ1_11 0xE1A +#define WM5100_EQ1_12 0xE1B +#define WM5100_EQ1_13 0xE1C +#define WM5100_EQ1_14 0xE1D +#define WM5100_EQ1_15 0xE1E +#define WM5100_EQ1_16 0xE1F +#define WM5100_EQ1_17 0xE20 +#define WM5100_EQ1_18 0xE21 +#define WM5100_EQ1_19 0xE22 +#define WM5100_EQ1_20 0xE23 +#define WM5100_EQ2_1 0xE26 +#define WM5100_EQ2_2 0xE27 +#define WM5100_EQ2_3 0xE28 +#define WM5100_EQ2_4 0xE29 +#define WM5100_EQ2_5 0xE2A +#define WM5100_EQ2_6 0xE2B +#define WM5100_EQ2_7 0xE2C +#define WM5100_EQ2_8 0xE2D +#define WM5100_EQ2_9 0xE2E +#define WM5100_EQ2_10 0xE2F +#define WM5100_EQ2_11 0xE30 +#define WM5100_EQ2_12 0xE31 +#define WM5100_EQ2_13 0xE32 +#define WM5100_EQ2_14 0xE33 +#define WM5100_EQ2_15 0xE34 +#define WM5100_EQ2_16 0xE35 +#define WM5100_EQ2_17 0xE36 +#define WM5100_EQ2_18 0xE37 +#define WM5100_EQ2_19 0xE38 +#define WM5100_EQ2_20 0xE39 +#define WM5100_EQ3_1 0xE3C +#define WM5100_EQ3_2 0xE3D +#define WM5100_EQ3_3 0xE3E +#define WM5100_EQ3_4 0xE3F +#define WM5100_EQ3_5 0xE40 +#define WM5100_EQ3_6 0xE41 +#define WM5100_EQ3_7 0xE42 +#define WM5100_EQ3_8 0xE43 +#define WM5100_EQ3_9 0xE44 +#define WM5100_EQ3_10 0xE45 +#define WM5100_EQ3_11 0xE46 +#define WM5100_EQ3_12 0xE47 +#define WM5100_EQ3_13 0xE48 +#define WM5100_EQ3_14 0xE49 +#define WM5100_EQ3_15 0xE4A +#define WM5100_EQ3_16 0xE4B +#define WM5100_EQ3_17 0xE4C +#define WM5100_EQ3_18 0xE4D +#define WM5100_EQ3_19 0xE4E +#define WM5100_EQ3_20 0xE4F +#define WM5100_EQ4_1 0xE52 +#define WM5100_EQ4_2 0xE53 +#define WM5100_EQ4_3 0xE54 +#define WM5100_EQ4_4 0xE55 +#define WM5100_EQ4_5 0xE56 +#define WM5100_EQ4_6 0xE57 +#define WM5100_EQ4_7 0xE58 +#define WM5100_EQ4_8 0xE59 +#define WM5100_EQ4_9 0xE5A +#define WM5100_EQ4_10 0xE5B +#define WM5100_EQ4_11 0xE5C +#define WM5100_EQ4_12 0xE5D +#define WM5100_EQ4_13 0xE5E +#define WM5100_EQ4_14 0xE5F +#define WM5100_EQ4_15 0xE60 +#define WM5100_EQ4_16 0xE61 +#define WM5100_EQ4_17 0xE62 +#define WM5100_EQ4_18 0xE63 +#define WM5100_EQ4_19 0xE64 +#define WM5100_EQ4_20 0xE65 +#define WM5100_DRC1_CTRL1 0xE80 +#define WM5100_DRC1_CTRL2 0xE81 +#define WM5100_DRC1_CTRL3 0xE82 +#define WM5100_DRC1_CTRL4 0xE83 +#define WM5100_DRC1_CTRL5 0xE84 +#define WM5100_HPLPF1_1 0xEC0 +#define WM5100_HPLPF1_2 0xEC1 +#define WM5100_HPLPF2_1 0xEC4 +#define WM5100_HPLPF2_2 0xEC5 +#define WM5100_HPLPF3_1 0xEC8 +#define WM5100_HPLPF3_2 0xEC9 +#define WM5100_HPLPF4_1 0xECC +#define WM5100_HPLPF4_2 0xECD +#define WM5100_DSP1_CONTROL_1 0xF00 +#define WM5100_DSP1_CONTROL_2 0xF02 +#define WM5100_DSP1_CONTROL_3 0xF03 +#define WM5100_DSP1_CONTROL_4 0xF04 +#define WM5100_DSP1_CONTROL_5 0xF06 +#define WM5100_DSP1_CONTROL_6 0xF07 +#define WM5100_DSP1_CONTROL_7 0xF08 +#define WM5100_DSP1_CONTROL_8 0xF09 +#define WM5100_DSP1_CONTROL_9 0xF0A +#define WM5100_DSP1_CONTROL_10 0xF0B +#define WM5100_DSP1_CONTROL_11 0xF0C +#define WM5100_DSP1_CONTROL_12 0xF0D +#define WM5100_DSP1_CONTROL_13 0xF0F +#define WM5100_DSP1_CONTROL_14 0xF10 +#define WM5100_DSP1_CONTROL_15 0xF11 +#define WM5100_DSP1_CONTROL_16 0xF12 +#define WM5100_DSP1_CONTROL_17 0xF13 +#define WM5100_DSP1_CONTROL_18 0xF14 +#define WM5100_DSP1_CONTROL_19 0xF16 +#define WM5100_DSP1_CONTROL_20 0xF17 +#define WM5100_DSP1_CONTROL_21 0xF18 +#define WM5100_DSP1_CONTROL_22 0xF1A +#define WM5100_DSP1_CONTROL_23 0xF1B +#define WM5100_DSP1_CONTROL_24 0xF1C +#define WM5100_DSP1_CONTROL_25 0xF1E +#define WM5100_DSP1_CONTROL_26 0xF20 +#define WM5100_DSP1_CONTROL_27 0xF21 +#define WM5100_DSP1_CONTROL_28 0xF22 +#define WM5100_DSP1_CONTROL_29 0xF23 +#define WM5100_DSP1_CONTROL_30 0xF24 +#define WM5100_DSP2_CONTROL_1 0x1000 +#define WM5100_DSP2_CONTROL_2 0x1002 +#define WM5100_DSP2_CONTROL_3 0x1003 +#define WM5100_DSP2_CONTROL_4 0x1004 +#define WM5100_DSP2_CONTROL_5 0x1006 +#define WM5100_DSP2_CONTROL_6 0x1007 +#define WM5100_DSP2_CONTROL_7 0x1008 +#define WM5100_DSP2_CONTROL_8 0x1009 +#define WM5100_DSP2_CONTROL_9 0x100A +#define WM5100_DSP2_CONTROL_10 0x100B +#define WM5100_DSP2_CONTROL_11 0x100C +#define WM5100_DSP2_CONTROL_12 0x100D +#define WM5100_DSP2_CONTROL_13 0x100F +#define WM5100_DSP2_CONTROL_14 0x1010 +#define WM5100_DSP2_CONTROL_15 0x1011 +#define WM5100_DSP2_CONTROL_16 0x1012 +#define WM5100_DSP2_CONTROL_17 0x1013 +#define WM5100_DSP2_CONTROL_18 0x1014 +#define WM5100_DSP2_CONTROL_19 0x1016 +#define WM5100_DSP2_CONTROL_20 0x1017 +#define WM5100_DSP2_CONTROL_21 0x1018 +#define WM5100_DSP2_CONTROL_22 0x101A +#define WM5100_DSP2_CONTROL_23 0x101B +#define WM5100_DSP2_CONTROL_24 0x101C +#define WM5100_DSP2_CONTROL_25 0x101E +#define WM5100_DSP2_CONTROL_26 0x1020 +#define WM5100_DSP2_CONTROL_27 0x1021 +#define WM5100_DSP2_CONTROL_28 0x1022 +#define WM5100_DSP2_CONTROL_29 0x1023 +#define WM5100_DSP2_CONTROL_30 0x1024 +#define WM5100_DSP3_CONTROL_1 0x1100 +#define WM5100_DSP3_CONTROL_2 0x1102 +#define WM5100_DSP3_CONTROL_3 0x1103 +#define WM5100_DSP3_CONTROL_4 0x1104 +#define WM5100_DSP3_CONTROL_5 0x1106 +#define WM5100_DSP3_CONTROL_6 0x1107 +#define WM5100_DSP3_CONTROL_7 0x1108 +#define WM5100_DSP3_CONTROL_8 0x1109 +#define WM5100_DSP3_CONTROL_9 0x110A +#define WM5100_DSP3_CONTROL_10 0x110B +#define WM5100_DSP3_CONTROL_11 0x110C +#define WM5100_DSP3_CONTROL_12 0x110D +#define WM5100_DSP3_CONTROL_13 0x110F +#define WM5100_DSP3_CONTROL_14 0x1110 +#define WM5100_DSP3_CONTROL_15 0x1111 +#define WM5100_DSP3_CONTROL_16 0x1112 +#define WM5100_DSP3_CONTROL_17 0x1113 +#define WM5100_DSP3_CONTROL_18 0x1114 +#define WM5100_DSP3_CONTROL_19 0x1116 +#define WM5100_DSP3_CONTROL_20 0x1117 +#define WM5100_DSP3_CONTROL_21 0x1118 +#define WM5100_DSP3_CONTROL_22 0x111A +#define WM5100_DSP3_CONTROL_23 0x111B +#define WM5100_DSP3_CONTROL_24 0x111C +#define WM5100_DSP3_CONTROL_25 0x111E +#define WM5100_DSP3_CONTROL_26 0x1120 +#define WM5100_DSP3_CONTROL_27 0x1121 +#define WM5100_DSP3_CONTROL_28 0x1122 +#define WM5100_DSP3_CONTROL_29 0x1123 +#define WM5100_DSP3_CONTROL_30 0x1124 +#define WM5100_DSP1_DM_0 0x4000 +#define WM5100_DSP1_DM_1 0x4001 +#define WM5100_DSP1_DM_2 0x4002 +#define WM5100_DSP1_DM_3 0x4003 +#define WM5100_DSP1_DM_508 0x41FC +#define WM5100_DSP1_DM_509 0x41FD +#define WM5100_DSP1_DM_510 0x41FE +#define WM5100_DSP1_DM_511 0x41FF +#define WM5100_DSP1_PM_0 0x4800 +#define WM5100_DSP1_PM_1 0x4801 +#define WM5100_DSP1_PM_2 0x4802 +#define WM5100_DSP1_PM_3 0x4803 +#define WM5100_DSP1_PM_4 0x4804 +#define WM5100_DSP1_PM_5 0x4805 +#define WM5100_DSP1_PM_1530 0x4DFA +#define WM5100_DSP1_PM_1531 0x4DFB +#define WM5100_DSP1_PM_1532 0x4DFC +#define WM5100_DSP1_PM_1533 0x4DFD +#define WM5100_DSP1_PM_1534 0x4DFE +#define WM5100_DSP1_PM_1535 0x4DFF +#define WM5100_DSP1_ZM_0 0x5000 +#define WM5100_DSP1_ZM_1 0x5001 +#define WM5100_DSP1_ZM_2 0x5002 +#define WM5100_DSP1_ZM_3 0x5003 +#define WM5100_DSP1_ZM_2044 0x57FC +#define WM5100_DSP1_ZM_2045 0x57FD +#define WM5100_DSP1_ZM_2046 0x57FE +#define WM5100_DSP1_ZM_2047 0x57FF +#define WM5100_DSP2_DM_0 0x6000 +#define WM5100_DSP2_DM_1 0x6001 +#define WM5100_DSP2_DM_2 0x6002 +#define WM5100_DSP2_DM_3 0x6003 +#define WM5100_DSP2_DM_508 0x61FC +#define WM5100_DSP2_DM_509 0x61FD +#define WM5100_DSP2_DM_510 0x61FE +#define WM5100_DSP2_DM_511 0x61FF +#define WM5100_DSP2_PM_0 0x6800 +#define WM5100_DSP2_PM_1 0x6801 +#define WM5100_DSP2_PM_2 0x6802 +#define WM5100_DSP2_PM_3 0x6803 +#define WM5100_DSP2_PM_4 0x6804 +#define WM5100_DSP2_PM_5 0x6805 +#define WM5100_DSP2_PM_1530 0x6DFA +#define WM5100_DSP2_PM_1531 0x6DFB +#define WM5100_DSP2_PM_1532 0x6DFC +#define WM5100_DSP2_PM_1533 0x6DFD +#define WM5100_DSP2_PM_1534 0x6DFE +#define WM5100_DSP2_PM_1535 0x6DFF +#define WM5100_DSP2_ZM_0 0x7000 +#define WM5100_DSP2_ZM_1 0x7001 +#define WM5100_DSP2_ZM_2 0x7002 +#define WM5100_DSP2_ZM_3 0x7003 +#define WM5100_DSP2_ZM_2044 0x77FC +#define WM5100_DSP2_ZM_2045 0x77FD +#define WM5100_DSP2_ZM_2046 0x77FE +#define WM5100_DSP2_ZM_2047 0x77FF +#define WM5100_DSP3_DM_0 0x8000 +#define WM5100_DSP3_DM_1 0x8001 +#define WM5100_DSP3_DM_2 0x8002 +#define WM5100_DSP3_DM_3 0x8003 +#define WM5100_DSP3_DM_508 0x81FC +#define WM5100_DSP3_DM_509 0x81FD +#define WM5100_DSP3_DM_510 0x81FE +#define WM5100_DSP3_DM_511 0x81FF +#define WM5100_DSP3_PM_0 0x8800 +#define WM5100_DSP3_PM_1 0x8801 +#define WM5100_DSP3_PM_2 0x8802 +#define WM5100_DSP3_PM_3 0x8803 +#define WM5100_DSP3_PM_4 0x8804 +#define WM5100_DSP3_PM_5 0x8805 +#define WM5100_DSP3_PM_1530 0x8DFA +#define WM5100_DSP3_PM_1531 0x8DFB +#define WM5100_DSP3_PM_1532 0x8DFC +#define WM5100_DSP3_PM_1533 0x8DFD +#define WM5100_DSP3_PM_1534 0x8DFE +#define WM5100_DSP3_PM_1535 0x8DFF +#define WM5100_DSP3_ZM_0 0x9000 +#define WM5100_DSP3_ZM_1 0x9001 +#define WM5100_DSP3_ZM_2 0x9002 +#define WM5100_DSP3_ZM_3 0x9003 +#define WM5100_DSP3_ZM_2044 0x97FC +#define WM5100_DSP3_ZM_2045 0x97FD +#define WM5100_DSP3_ZM_2046 0x97FE +#define WM5100_DSP3_ZM_2047 0x97FF + +#define WM5100_REGISTER_COUNT 1435 +#define WM5100_MAX_REGISTER 0x97FF + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - software reset + */ +#define WM5100_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */ +#define WM5100_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */ +#define WM5100_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */ + +/* + * R1 (0x01) - Device Revision + */ +#define WM5100_DEVICE_REVISION_MASK 0x000F /* DEVICE_REVISION - [3:0] */ +#define WM5100_DEVICE_REVISION_SHIFT 0 /* DEVICE_REVISION - [3:0] */ +#define WM5100_DEVICE_REVISION_WIDTH 4 /* DEVICE_REVISION - [3:0] */ + +/* + * R16 (0x10) - Ctrl IF 1 + */ +#define WM5100_AUTO_INC 0x0001 /* AUTO_INC */ +#define WM5100_AUTO_INC_MASK 0x0001 /* AUTO_INC */ +#define WM5100_AUTO_INC_SHIFT 0 /* AUTO_INC */ +#define WM5100_AUTO_INC_WIDTH 1 /* AUTO_INC */ + +/* + * R32 (0x20) - Tone Generator 1 + */ +#define WM5100_TONE_RATE_MASK 0x3000 /* TONE_RATE - [13:12] */ +#define WM5100_TONE_RATE_SHIFT 12 /* TONE_RATE - [13:12] */ +#define WM5100_TONE_RATE_WIDTH 2 /* TONE_RATE - [13:12] */ +#define WM5100_TONE_OFFSET_MASK 0x0300 /* TONE_OFFSET - [9:8] */ +#define WM5100_TONE_OFFSET_SHIFT 8 /* TONE_OFFSET - [9:8] */ +#define WM5100_TONE_OFFSET_WIDTH 2 /* TONE_OFFSET - [9:8] */ +#define WM5100_TONE2_ENA 0x0002 /* TONE2_ENA */ +#define WM5100_TONE2_ENA_MASK 0x0002 /* TONE2_ENA */ +#define WM5100_TONE2_ENA_SHIFT 1 /* TONE2_ENA */ +#define WM5100_TONE2_ENA_WIDTH 1 /* TONE2_ENA */ +#define WM5100_TONE1_ENA 0x0001 /* TONE1_ENA */ +#define WM5100_TONE1_ENA_MASK 0x0001 /* TONE1_ENA */ +#define WM5100_TONE1_ENA_SHIFT 0 /* TONE1_ENA */ +#define WM5100_TONE1_ENA_WIDTH 1 /* TONE1_ENA */ + +/* + * R48 (0x30) - PWM Drive 1 + */ +#define WM5100_PWM_RATE_MASK 0x3000 /* PWM_RATE - [13:12] */ +#define WM5100_PWM_RATE_SHIFT 12 /* PWM_RATE - [13:12] */ +#define WM5100_PWM_RATE_WIDTH 2 /* PWM_RATE - [13:12] */ +#define WM5100_PWM_CLK_SEL_MASK 0x0300 /* PWM_CLK_SEL - [9:8] */ +#define WM5100_PWM_CLK_SEL_SHIFT 8 /* PWM_CLK_SEL - [9:8] */ +#define WM5100_PWM_CLK_SEL_WIDTH 2 /* PWM_CLK_SEL - [9:8] */ +#define WM5100_PWM2_OVD 0x0020 /* PWM2_OVD */ +#define WM5100_PWM2_OVD_MASK 0x0020 /* PWM2_OVD */ +#define WM5100_PWM2_OVD_SHIFT 5 /* PWM2_OVD */ +#define WM5100_PWM2_OVD_WIDTH 1 /* PWM2_OVD */ +#define WM5100_PWM1_OVD 0x0010 /* PWM1_OVD */ +#define WM5100_PWM1_OVD_MASK 0x0010 /* PWM1_OVD */ +#define WM5100_PWM1_OVD_SHIFT 4 /* PWM1_OVD */ +#define WM5100_PWM1_OVD_WIDTH 1 /* PWM1_OVD */ +#define WM5100_PWM2_ENA 0x0002 /* PWM2_ENA */ +#define WM5100_PWM2_ENA_MASK 0x0002 /* PWM2_ENA */ +#define WM5100_PWM2_ENA_SHIFT 1 /* PWM2_ENA */ +#define WM5100_PWM2_ENA_WIDTH 1 /* PWM2_ENA */ +#define WM5100_PWM1_ENA 0x0001 /* PWM1_ENA */ +#define WM5100_PWM1_ENA_MASK 0x0001 /* PWM1_ENA */ +#define WM5100_PWM1_ENA_SHIFT 0 /* PWM1_ENA */ +#define WM5100_PWM1_ENA_WIDTH 1 /* PWM1_ENA */ + +/* + * R49 (0x31) - PWM Drive 2 + */ +#define WM5100_PWM1_LVL_MASK 0x03FF /* PWM1_LVL - [9:0] */ +#define WM5100_PWM1_LVL_SHIFT 0 /* PWM1_LVL - [9:0] */ +#define WM5100_PWM1_LVL_WIDTH 10 /* PWM1_LVL - [9:0] */ + +/* + * R50 (0x32) - PWM Drive 3 + */ +#define WM5100_PWM2_LVL_MASK 0x03FF /* PWM2_LVL - [9:0] */ +#define WM5100_PWM2_LVL_SHIFT 0 /* PWM2_LVL - [9:0] */ +#define WM5100_PWM2_LVL_WIDTH 10 /* PWM2_LVL - [9:0] */ + +/* + * R256 (0x100) - Clocking 1 + */ +#define WM5100_CLK_32K_SRC_MASK 0x000F /* CLK_32K_SRC - [3:0] */ +#define WM5100_CLK_32K_SRC_SHIFT 0 /* CLK_32K_SRC - [3:0] */ +#define WM5100_CLK_32K_SRC_WIDTH 4 /* CLK_32K_SRC - [3:0] */ + +/* + * R257 (0x101) - Clocking 3 + */ +#define WM5100_SYSCLK_FREQ_MASK 0x0700 /* SYSCLK_FREQ - [10:8] */ +#define WM5100_SYSCLK_FREQ_SHIFT 8 /* SYSCLK_FREQ - [10:8] */ +#define WM5100_SYSCLK_FREQ_WIDTH 3 /* SYSCLK_FREQ - [10:8] */ +#define WM5100_SYSCLK_ENA 0x0040 /* SYSCLK_ENA */ +#define WM5100_SYSCLK_ENA_MASK 0x0040 /* SYSCLK_ENA */ +#define WM5100_SYSCLK_ENA_SHIFT 6 /* SYSCLK_ENA */ +#define WM5100_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */ +#define WM5100_SYSCLK_SRC_MASK 0x000F /* SYSCLK_SRC - [3:0] */ +#define WM5100_SYSCLK_SRC_SHIFT 0 /* SYSCLK_SRC - [3:0] */ +#define WM5100_SYSCLK_SRC_WIDTH 4 /* SYSCLK_SRC - [3:0] */ + +/* + * R258 (0x102) - Clocking 4 + */ +#define WM5100_SAMPLE_RATE_1_MASK 0x001F /* SAMPLE_RATE_1 - [4:0] */ +#define WM5100_SAMPLE_RATE_1_SHIFT 0 /* SAMPLE_RATE_1 - [4:0] */ +#define WM5100_SAMPLE_RATE_1_WIDTH 5 /* SAMPLE_RATE_1 - [4:0] */ + +/* + * R259 (0x103) - Clocking 5 + */ +#define WM5100_SAMPLE_RATE_2_MASK 0x001F /* SAMPLE_RATE_2 - [4:0] */ +#define WM5100_SAMPLE_RATE_2_SHIFT 0 /* SAMPLE_RATE_2 - [4:0] */ +#define WM5100_SAMPLE_RATE_2_WIDTH 5 /* SAMPLE_RATE_2 - [4:0] */ + +/* + * R260 (0x104) - Clocking 6 + */ +#define WM5100_SAMPLE_RATE_3_MASK 0x001F /* SAMPLE_RATE_3 - [4:0] */ +#define WM5100_SAMPLE_RATE_3_SHIFT 0 /* SAMPLE_RATE_3 - [4:0] */ +#define WM5100_SAMPLE_RATE_3_WIDTH 5 /* SAMPLE_RATE_3 - [4:0] */ + +/* + * R263 (0x107) - Clocking 7 + */ +#define WM5100_ASYNC_CLK_FREQ_MASK 0x0700 /* ASYNC_CLK_FREQ - [10:8] */ +#define WM5100_ASYNC_CLK_FREQ_SHIFT 8 /* ASYNC_CLK_FREQ - [10:8] */ +#define WM5100_ASYNC_CLK_FREQ_WIDTH 3 /* ASYNC_CLK_FREQ - [10:8] */ +#define WM5100_ASYNC_CLK_ENA 0x0040 /* ASYNC_CLK_ENA */ +#define WM5100_ASYNC_CLK_ENA_MASK 0x0040 /* ASYNC_CLK_ENA */ +#define WM5100_ASYNC_CLK_ENA_SHIFT 6 /* ASYNC_CLK_ENA */ +#define WM5100_ASYNC_CLK_ENA_WIDTH 1 /* ASYNC_CLK_ENA */ +#define WM5100_ASYNC_CLK_SRC_MASK 0x000F /* ASYNC_CLK_SRC - [3:0] */ +#define WM5100_ASYNC_CLK_SRC_SHIFT 0 /* ASYNC_CLK_SRC - [3:0] */ +#define WM5100_ASYNC_CLK_SRC_WIDTH 4 /* ASYNC_CLK_SRC - [3:0] */ + +/* + * R264 (0x108) - Clocking 8 + */ +#define WM5100_ASYNC_SAMPLE_RATE_MASK 0x001F /* ASYNC_SAMPLE_RATE - [4:0] */ +#define WM5100_ASYNC_SAMPLE_RATE_SHIFT 0 /* ASYNC_SAMPLE_RATE - [4:0] */ +#define WM5100_ASYNC_SAMPLE_RATE_WIDTH 5 /* ASYNC_SAMPLE_RATE - [4:0] */ + +/* + * R288 (0x120) - ASRC_ENABLE + */ +#define WM5100_ASRC2L_ENA 0x0008 /* ASRC2L_ENA */ +#define WM5100_ASRC2L_ENA_MASK 0x0008 /* ASRC2L_ENA */ +#define WM5100_ASRC2L_ENA_SHIFT 3 /* ASRC2L_ENA */ +#define WM5100_ASRC2L_ENA_WIDTH 1 /* ASRC2L_ENA */ +#define WM5100_ASRC2R_ENA 0x0004 /* ASRC2R_ENA */ +#define WM5100_ASRC2R_ENA_MASK 0x0004 /* ASRC2R_ENA */ +#define WM5100_ASRC2R_ENA_SHIFT 2 /* ASRC2R_ENA */ +#define WM5100_ASRC2R_ENA_WIDTH 1 /* ASRC2R_ENA */ +#define WM5100_ASRC1L_ENA 0x0002 /* ASRC1L_ENA */ +#define WM5100_ASRC1L_ENA_MASK 0x0002 /* ASRC1L_ENA */ +#define WM5100_ASRC1L_ENA_SHIFT 1 /* ASRC1L_ENA */ +#define WM5100_ASRC1L_ENA_WIDTH 1 /* ASRC1L_ENA */ +#define WM5100_ASRC1R_ENA 0x0001 /* ASRC1R_ENA */ +#define WM5100_ASRC1R_ENA_MASK 0x0001 /* ASRC1R_ENA */ +#define WM5100_ASRC1R_ENA_SHIFT 0 /* ASRC1R_ENA */ +#define WM5100_ASRC1R_ENA_WIDTH 1 /* ASRC1R_ENA */ + +/* + * R289 (0x121) - ASRC_STATUS + */ +#define WM5100_ASRC2L_ENA_STS 0x0008 /* ASRC2L_ENA_STS */ +#define WM5100_ASRC2L_ENA_STS_MASK 0x0008 /* ASRC2L_ENA_STS */ +#define WM5100_ASRC2L_ENA_STS_SHIFT 3 /* ASRC2L_ENA_STS */ +#define WM5100_ASRC2L_ENA_STS_WIDTH 1 /* ASRC2L_ENA_STS */ +#define WM5100_ASRC2R_ENA_STS 0x0004 /* ASRC2R_ENA_STS */ +#define WM5100_ASRC2R_ENA_STS_MASK 0x0004 /* ASRC2R_ENA_STS */ +#define WM5100_ASRC2R_ENA_STS_SHIFT 2 /* ASRC2R_ENA_STS */ +#define WM5100_ASRC2R_ENA_STS_WIDTH 1 /* ASRC2R_ENA_STS */ +#define WM5100_ASRC1L_ENA_STS 0x0002 /* ASRC1L_ENA_STS */ +#define WM5100_ASRC1L_ENA_STS_MASK 0x0002 /* ASRC1L_ENA_STS */ +#define WM5100_ASRC1L_ENA_STS_SHIFT 1 /* ASRC1L_ENA_STS */ +#define WM5100_ASRC1L_ENA_STS_WIDTH 1 /* ASRC1L_ENA_STS */ +#define WM5100_ASRC1R_ENA_STS 0x0001 /* ASRC1R_ENA_STS */ +#define WM5100_ASRC1R_ENA_STS_MASK 0x0001 /* ASRC1R_ENA_STS */ +#define WM5100_ASRC1R_ENA_STS_SHIFT 0 /* ASRC1R_ENA_STS */ +#define WM5100_ASRC1R_ENA_STS_WIDTH 1 /* ASRC1R_ENA_STS */ + +/* + * R290 (0x122) - ASRC_RATE1 + */ +#define WM5100_ASRC_RATE1_MASK 0x0006 /* ASRC_RATE1 - [2:1] */ +#define WM5100_ASRC_RATE1_SHIFT 1 /* ASRC_RATE1 - [2:1] */ +#define WM5100_ASRC_RATE1_WIDTH 2 /* ASRC_RATE1 - [2:1] */ + +/* + * R321 (0x141) - ISRC 1 CTRL 1 + */ +#define WM5100_ISRC1_DFS_ENA 0x2000 /* ISRC1_DFS_ENA */ +#define WM5100_ISRC1_DFS_ENA_MASK 0x2000 /* ISRC1_DFS_ENA */ +#define WM5100_ISRC1_DFS_ENA_SHIFT 13 /* ISRC1_DFS_ENA */ +#define WM5100_ISRC1_DFS_ENA_WIDTH 1 /* ISRC1_DFS_ENA */ +#define WM5100_ISRC1_CLK_SEL_MASK 0x0300 /* ISRC1_CLK_SEL - [9:8] */ +#define WM5100_ISRC1_CLK_SEL_SHIFT 8 /* ISRC1_CLK_SEL - [9:8] */ +#define WM5100_ISRC1_CLK_SEL_WIDTH 2 /* ISRC1_CLK_SEL - [9:8] */ +#define WM5100_ISRC1_FSH_MASK 0x000C /* ISRC1_FSH - [3:2] */ +#define WM5100_ISRC1_FSH_SHIFT 2 /* ISRC1_FSH - [3:2] */ +#define WM5100_ISRC1_FSH_WIDTH 2 /* ISRC1_FSH - [3:2] */ +#define WM5100_ISRC1_FSL_MASK 0x0003 /* ISRC1_FSL - [1:0] */ +#define WM5100_ISRC1_FSL_SHIFT 0 /* ISRC1_FSL - [1:0] */ +#define WM5100_ISRC1_FSL_WIDTH 2 /* ISRC1_FSL - [1:0] */ + +/* + * R322 (0x142) - ISRC 1 CTRL 2 + */ +#define WM5100_ISRC1_INT1_ENA 0x8000 /* ISRC1_INT1_ENA */ +#define WM5100_ISRC1_INT1_ENA_MASK 0x8000 /* ISRC1_INT1_ENA */ +#define WM5100_ISRC1_INT1_ENA_SHIFT 15 /* ISRC1_INT1_ENA */ +#define WM5100_ISRC1_INT1_ENA_WIDTH 1 /* ISRC1_INT1_ENA */ +#define WM5100_ISRC1_INT2_ENA 0x4000 /* ISRC1_INT2_ENA */ +#define WM5100_ISRC1_INT2_ENA_MASK 0x4000 /* ISRC1_INT2_ENA */ +#define WM5100_ISRC1_INT2_ENA_SHIFT 14 /* ISRC1_INT2_ENA */ +#define WM5100_ISRC1_INT2_ENA_WIDTH 1 /* ISRC1_INT2_ENA */ +#define WM5100_ISRC1_INT3_ENA 0x2000 /* ISRC1_INT3_ENA */ +#define WM5100_ISRC1_INT3_ENA_MASK 0x2000 /* ISRC1_INT3_ENA */ +#define WM5100_ISRC1_INT3_ENA_SHIFT 13 /* ISRC1_INT3_ENA */ +#define WM5100_ISRC1_INT3_ENA_WIDTH 1 /* ISRC1_INT3_ENA */ +#define WM5100_ISRC1_INT4_ENA 0x1000 /* ISRC1_INT4_ENA */ +#define WM5100_ISRC1_INT4_ENA_MASK 0x1000 /* ISRC1_INT4_ENA */ +#define WM5100_ISRC1_INT4_ENA_SHIFT 12 /* ISRC1_INT4_ENA */ +#define WM5100_ISRC1_INT4_ENA_WIDTH 1 /* ISRC1_INT4_ENA */ +#define WM5100_ISRC1_DEC1_ENA 0x0200 /* ISRC1_DEC1_ENA */ +#define WM5100_ISRC1_DEC1_ENA_MASK 0x0200 /* ISRC1_DEC1_ENA */ +#define WM5100_ISRC1_DEC1_ENA_SHIFT 9 /* ISRC1_DEC1_ENA */ +#define WM5100_ISRC1_DEC1_ENA_WIDTH 1 /* ISRC1_DEC1_ENA */ +#define WM5100_ISRC1_DEC2_ENA 0x0100 /* ISRC1_DEC2_ENA */ +#define WM5100_ISRC1_DEC2_ENA_MASK 0x0100 /* ISRC1_DEC2_ENA */ +#define WM5100_ISRC1_DEC2_ENA_SHIFT 8 /* ISRC1_DEC2_ENA */ +#define WM5100_ISRC1_DEC2_ENA_WIDTH 1 /* ISRC1_DEC2_ENA */ +#define WM5100_ISRC1_DEC3_ENA 0x0080 /* ISRC1_DEC3_ENA */ +#define WM5100_ISRC1_DEC3_ENA_MASK 0x0080 /* ISRC1_DEC3_ENA */ +#define WM5100_ISRC1_DEC3_ENA_SHIFT 7 /* ISRC1_DEC3_ENA */ +#define WM5100_ISRC1_DEC3_ENA_WIDTH 1 /* ISRC1_DEC3_ENA */ +#define WM5100_ISRC1_DEC4_ENA 0x0040 /* ISRC1_DEC4_ENA */ +#define WM5100_ISRC1_DEC4_ENA_MASK 0x0040 /* ISRC1_DEC4_ENA */ +#define WM5100_ISRC1_DEC4_ENA_SHIFT 6 /* ISRC1_DEC4_ENA */ +#define WM5100_ISRC1_DEC4_ENA_WIDTH 1 /* ISRC1_DEC4_ENA */ +#define WM5100_ISRC1_NOTCH_ENA 0x0001 /* ISRC1_NOTCH_ENA */ +#define WM5100_ISRC1_NOTCH_ENA_MASK 0x0001 /* ISRC1_NOTCH_ENA */ +#define WM5100_ISRC1_NOTCH_ENA_SHIFT 0 /* ISRC1_NOTCH_ENA */ +#define WM5100_ISRC1_NOTCH_ENA_WIDTH 1 /* ISRC1_NOTCH_ENA */ + +/* + * R323 (0x143) - ISRC 2 CTRL1 + */ +#define WM5100_ISRC2_DFS_ENA 0x2000 /* ISRC2_DFS_ENA */ +#define WM5100_ISRC2_DFS_ENA_MASK 0x2000 /* ISRC2_DFS_ENA */ +#define WM5100_ISRC2_DFS_ENA_SHIFT 13 /* ISRC2_DFS_ENA */ +#define WM5100_ISRC2_DFS_ENA_WIDTH 1 /* ISRC2_DFS_ENA */ +#define WM5100_ISRC2_CLK_SEL_MASK 0x0300 /* ISRC2_CLK_SEL - [9:8] */ +#define WM5100_ISRC2_CLK_SEL_SHIFT 8 /* ISRC2_CLK_SEL - [9:8] */ +#define WM5100_ISRC2_CLK_SEL_WIDTH 2 /* ISRC2_CLK_SEL - [9:8] */ +#define WM5100_ISRC2_FSH_MASK 0x000C /* ISRC2_FSH - [3:2] */ +#define WM5100_ISRC2_FSH_SHIFT 2 /* ISRC2_FSH - [3:2] */ +#define WM5100_ISRC2_FSH_WIDTH 2 /* ISRC2_FSH - [3:2] */ +#define WM5100_ISRC2_FSL_MASK 0x0003 /* ISRC2_FSL - [1:0] */ +#define WM5100_ISRC2_FSL_SHIFT 0 /* ISRC2_FSL - [1:0] */ +#define WM5100_ISRC2_FSL_WIDTH 2 /* ISRC2_FSL - [1:0] */ + +/* + * R324 (0x144) - ISRC 2 CTRL 2 + */ +#define WM5100_ISRC2_INT1_ENA 0x8000 /* ISRC2_INT1_ENA */ +#define WM5100_ISRC2_INT1_ENA_MASK 0x8000 /* ISRC2_INT1_ENA */ +#define WM5100_ISRC2_INT1_ENA_SHIFT 15 /* ISRC2_INT1_ENA */ +#define WM5100_ISRC2_INT1_ENA_WIDTH 1 /* ISRC2_INT1_ENA */ +#define WM5100_ISRC2_INT2_ENA 0x4000 /* ISRC2_INT2_ENA */ +#define WM5100_ISRC2_INT2_ENA_MASK 0x4000 /* ISRC2_INT2_ENA */ +#define WM5100_ISRC2_INT2_ENA_SHIFT 14 /* ISRC2_INT2_ENA */ +#define WM5100_ISRC2_INT2_ENA_WIDTH 1 /* ISRC2_INT2_ENA */ +#define WM5100_ISRC2_INT3_ENA 0x2000 /* ISRC2_INT3_ENA */ +#define WM5100_ISRC2_INT3_ENA_MASK 0x2000 /* ISRC2_INT3_ENA */ +#define WM5100_ISRC2_INT3_ENA_SHIFT 13 /* ISRC2_INT3_ENA */ +#define WM5100_ISRC2_INT3_ENA_WIDTH 1 /* ISRC2_INT3_ENA */ +#define WM5100_ISRC2_INT4_ENA 0x1000 /* ISRC2_INT4_ENA */ +#define WM5100_ISRC2_INT4_ENA_MASK 0x1000 /* ISRC2_INT4_ENA */ +#define WM5100_ISRC2_INT4_ENA_SHIFT 12 /* ISRC2_INT4_ENA */ +#define WM5100_ISRC2_INT4_ENA_WIDTH 1 /* ISRC2_INT4_ENA */ +#define WM5100_ISRC2_DEC1_ENA 0x0200 /* ISRC2_DEC1_ENA */ +#define WM5100_ISRC2_DEC1_ENA_MASK 0x0200 /* ISRC2_DEC1_ENA */ +#define WM5100_ISRC2_DEC1_ENA_SHIFT 9 /* ISRC2_DEC1_ENA */ +#define WM5100_ISRC2_DEC1_ENA_WIDTH 1 /* ISRC2_DEC1_ENA */ +#define WM5100_ISRC2_DEC2_ENA 0x0100 /* ISRC2_DEC2_ENA */ +#define WM5100_ISRC2_DEC2_ENA_MASK 0x0100 /* ISRC2_DEC2_ENA */ +#define WM5100_ISRC2_DEC2_ENA_SHIFT 8 /* ISRC2_DEC2_ENA */ +#define WM5100_ISRC2_DEC2_ENA_WIDTH 1 /* ISRC2_DEC2_ENA */ +#define WM5100_ISRC2_DEC3_ENA 0x0080 /* ISRC2_DEC3_ENA */ +#define WM5100_ISRC2_DEC3_ENA_MASK 0x0080 /* ISRC2_DEC3_ENA */ +#define WM5100_ISRC2_DEC3_ENA_SHIFT 7 /* ISRC2_DEC3_ENA */ +#define WM5100_ISRC2_DEC3_ENA_WIDTH 1 /* ISRC2_DEC3_ENA */ +#define WM5100_ISRC2_DEC4_ENA 0x0040 /* ISRC2_DEC4_ENA */ +#define WM5100_ISRC2_DEC4_ENA_MASK 0x0040 /* ISRC2_DEC4_ENA */ +#define WM5100_ISRC2_DEC4_ENA_SHIFT 6 /* ISRC2_DEC4_ENA */ +#define WM5100_ISRC2_DEC4_ENA_WIDTH 1 /* ISRC2_DEC4_ENA */ +#define WM5100_ISRC2_NOTCH_ENA 0x0001 /* ISRC2_NOTCH_ENA */ +#define WM5100_ISRC2_NOTCH_ENA_MASK 0x0001 /* ISRC2_NOTCH_ENA */ +#define WM5100_ISRC2_NOTCH_ENA_SHIFT 0 /* ISRC2_NOTCH_ENA */ +#define WM5100_ISRC2_NOTCH_ENA_WIDTH 1 /* ISRC2_NOTCH_ENA */ + +/* + * R386 (0x182) - FLL1 Control 1 + */ +#define WM5100_FLL1_ENA 0x0001 /* FLL1_ENA */ +#define WM5100_FLL1_ENA_MASK 0x0001 /* FLL1_ENA */ +#define WM5100_FLL1_ENA_SHIFT 0 /* FLL1_ENA */ +#define WM5100_FLL1_ENA_WIDTH 1 /* FLL1_ENA */ + +/* + * R387 (0x183) - FLL1 Control 2 + */ +#define WM5100_FLL1_OUTDIV_MASK 0x3F00 /* FLL1_OUTDIV - [13:8] */ +#define WM5100_FLL1_OUTDIV_SHIFT 8 /* FLL1_OUTDIV - [13:8] */ +#define WM5100_FLL1_OUTDIV_WIDTH 6 /* FLL1_OUTDIV - [13:8] */ +#define WM5100_FLL1_FRATIO_MASK 0x0007 /* FLL1_FRATIO - [2:0] */ +#define WM5100_FLL1_FRATIO_SHIFT 0 /* FLL1_FRATIO - [2:0] */ +#define WM5100_FLL1_FRATIO_WIDTH 3 /* FLL1_FRATIO - [2:0] */ + +/* + * R388 (0x184) - FLL1 Control 3 + */ +#define WM5100_FLL1_THETA_MASK 0xFFFF /* FLL1_THETA - [15:0] */ +#define WM5100_FLL1_THETA_SHIFT 0 /* FLL1_THETA - [15:0] */ +#define WM5100_FLL1_THETA_WIDTH 16 /* FLL1_THETA - [15:0] */ + +/* + * R390 (0x186) - FLL1 Control 5 + */ +#define WM5100_FLL1_N_MASK 0x03FF /* FLL1_N - [9:0] */ +#define WM5100_FLL1_N_SHIFT 0 /* FLL1_N - [9:0] */ +#define WM5100_FLL1_N_WIDTH 10 /* FLL1_N - [9:0] */ + +/* + * R391 (0x187) - FLL1 Control 6 + */ +#define WM5100_FLL1_REFCLK_DIV_MASK 0x00C0 /* FLL1_REFCLK_DIV - [7:6] */ +#define WM5100_FLL1_REFCLK_DIV_SHIFT 6 /* FLL1_REFCLK_DIV - [7:6] */ +#define WM5100_FLL1_REFCLK_DIV_WIDTH 2 /* FLL1_REFCLK_DIV - [7:6] */ +#define WM5100_FLL1_REFCLK_SRC_MASK 0x000F /* FLL1_REFCLK_SRC - [3:0] */ +#define WM5100_FLL1_REFCLK_SRC_SHIFT 0 /* FLL1_REFCLK_SRC - [3:0] */ +#define WM5100_FLL1_REFCLK_SRC_WIDTH 4 /* FLL1_REFCLK_SRC - [3:0] */ + +/* + * R392 (0x188) - FLL1 EFS 1 + */ +#define WM5100_FLL1_LAMBDA_MASK 0xFFFF /* FLL1_LAMBDA - [15:0] */ +#define WM5100_FLL1_LAMBDA_SHIFT 0 /* FLL1_LAMBDA - [15:0] */ +#define WM5100_FLL1_LAMBDA_WIDTH 16 /* FLL1_LAMBDA - [15:0] */ + +/* + * R418 (0x1A2) - FLL2 Control 1 + */ +#define WM5100_FLL2_ENA 0x0001 /* FLL2_ENA */ +#define WM5100_FLL2_ENA_MASK 0x0001 /* FLL2_ENA */ +#define WM5100_FLL2_ENA_SHIFT 0 /* FLL2_ENA */ +#define WM5100_FLL2_ENA_WIDTH 1 /* FLL2_ENA */ + +/* + * R419 (0x1A3) - FLL2 Control 2 + */ +#define WM5100_FLL2_OUTDIV_MASK 0x3F00 /* FLL2_OUTDIV - [13:8] */ +#define WM5100_FLL2_OUTDIV_SHIFT 8 /* FLL2_OUTDIV - [13:8] */ +#define WM5100_FLL2_OUTDIV_WIDTH 6 /* FLL2_OUTDIV - [13:8] */ +#define WM5100_FLL2_FRATIO_MASK 0x0007 /* FLL2_FRATIO - [2:0] */ +#define WM5100_FLL2_FRATIO_SHIFT 0 /* FLL2_FRATIO - [2:0] */ +#define WM5100_FLL2_FRATIO_WIDTH 3 /* FLL2_FRATIO - [2:0] */ + +/* + * R420 (0x1A4) - FLL2 Control 3 + */ +#define WM5100_FLL2_THETA_MASK 0xFFFF /* FLL2_THETA - [15:0] */ +#define WM5100_FLL2_THETA_SHIFT 0 /* FLL2_THETA - [15:0] */ +#define WM5100_FLL2_THETA_WIDTH 16 /* FLL2_THETA - [15:0] */ + +/* + * R422 (0x1A6) - FLL2 Control 5 + */ +#define WM5100_FLL2_N_MASK 0x03FF /* FLL2_N - [9:0] */ +#define WM5100_FLL2_N_SHIFT 0 /* FLL2_N - [9:0] */ +#define WM5100_FLL2_N_WIDTH 10 /* FLL2_N - [9:0] */ + +/* + * R423 (0x1A7) - FLL2 Control 6 + */ +#define WM5100_FLL2_REFCLK_DIV_MASK 0x00C0 /* FLL2_REFCLK_DIV - [7:6] */ +#define WM5100_FLL2_REFCLK_DIV_SHIFT 6 /* FLL2_REFCLK_DIV - [7:6] */ +#define WM5100_FLL2_REFCLK_DIV_WIDTH 2 /* FLL2_REFCLK_DIV - [7:6] */ +#define WM5100_FLL2_REFCLK_SRC_MASK 0x000F /* FLL2_REFCLK_SRC - [3:0] */ +#define WM5100_FLL2_REFCLK_SRC_SHIFT 0 /* FLL2_REFCLK_SRC - [3:0] */ +#define WM5100_FLL2_REFCLK_SRC_WIDTH 4 /* FLL2_REFCLK_SRC - [3:0] */ + +/* + * R424 (0x1A8) - FLL2 EFS 1 + */ +#define WM5100_FLL2_LAMBDA_MASK 0xFFFF /* FLL2_LAMBDA - [15:0] */ +#define WM5100_FLL2_LAMBDA_SHIFT 0 /* FLL2_LAMBDA - [15:0] */ +#define WM5100_FLL2_LAMBDA_WIDTH 16 /* FLL2_LAMBDA - [15:0] */ + +/* + * R512 (0x200) - Mic Charge Pump 1 + */ +#define WM5100_CP2_BYPASS 0x0020 /* CP2_BYPASS */ +#define WM5100_CP2_BYPASS_MASK 0x0020 /* CP2_BYPASS */ +#define WM5100_CP2_BYPASS_SHIFT 5 /* CP2_BYPASS */ +#define WM5100_CP2_BYPASS_WIDTH 1 /* CP2_BYPASS */ +#define WM5100_CP2_ENA 0x0001 /* CP2_ENA */ +#define WM5100_CP2_ENA_MASK 0x0001 /* CP2_ENA */ +#define WM5100_CP2_ENA_SHIFT 0 /* CP2_ENA */ +#define WM5100_CP2_ENA_WIDTH 1 /* CP2_ENA */ + +/* + * R513 (0x201) - Mic Charge Pump 2 + */ +#define WM5100_LDO2_VSEL_MASK 0xF800 /* LDO2_VSEL - [15:11] */ +#define WM5100_LDO2_VSEL_SHIFT 11 /* LDO2_VSEL - [15:11] */ +#define WM5100_LDO2_VSEL_WIDTH 5 /* LDO2_VSEL - [15:11] */ + +/* + * R514 (0x202) - HP Charge Pump 1 + */ +#define WM5100_CP1_ENA 0x0001 /* CP1_ENA */ +#define WM5100_CP1_ENA_MASK 0x0001 /* CP1_ENA */ +#define WM5100_CP1_ENA_SHIFT 0 /* CP1_ENA */ +#define WM5100_CP1_ENA_WIDTH 1 /* CP1_ENA */ + +/* + * R529 (0x211) - LDO1 Control + */ +#define WM5100_LDO1_BYPASS 0x0002 /* LDO1_BYPASS */ +#define WM5100_LDO1_BYPASS_MASK 0x0002 /* LDO1_BYPASS */ +#define WM5100_LDO1_BYPASS_SHIFT 1 /* LDO1_BYPASS */ +#define WM5100_LDO1_BYPASS_WIDTH 1 /* LDO1_BYPASS */ + +/* + * R533 (0x215) - Mic Bias Ctrl 1 + */ +#define WM5100_MICB1_DISCH 0x0040 /* MICB1_DISCH */ +#define WM5100_MICB1_DISCH_MASK 0x0040 /* MICB1_DISCH */ +#define WM5100_MICB1_DISCH_SHIFT 6 /* MICB1_DISCH */ +#define WM5100_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */ +#define WM5100_MICB1_RATE 0x0020 /* MICB1_RATE */ +#define WM5100_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */ +#define WM5100_MICB1_RATE_SHIFT 5 /* MICB1_RATE */ +#define WM5100_MICB1_RATE_WIDTH 1 /* MICB1_RATE */ +#define WM5100_MICB1_LVL_MASK 0x001C /* MICB1_LVL - [4:2] */ +#define WM5100_MICB1_LVL_SHIFT 2 /* MICB1_LVL - [4:2] */ +#define WM5100_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [4:2] */ +#define WM5100_MICB1_BYPASS 0x0002 /* MICB1_BYPASS */ +#define WM5100_MICB1_BYPASS_MASK 0x0002 /* MICB1_BYPASS */ +#define WM5100_MICB1_BYPASS_SHIFT 1 /* MICB1_BYPASS */ +#define WM5100_MICB1_BYPASS_WIDTH 1 /* MICB1_BYPASS */ +#define WM5100_MICB1_ENA 0x0001 /* MICB1_ENA */ +#define WM5100_MICB1_ENA_MASK 0x0001 /* MICB1_ENA */ +#define WM5100_MICB1_ENA_SHIFT 0 /* MICB1_ENA */ +#define WM5100_MICB1_ENA_WIDTH 1 /* MICB1_ENA */ + +/* + * R534 (0x216) - Mic Bias Ctrl 2 + */ +#define WM5100_MICB2_DISCH 0x0040 /* MICB2_DISCH */ +#define WM5100_MICB2_DISCH_MASK 0x0040 /* MICB2_DISCH */ +#define WM5100_MICB2_DISCH_SHIFT 6 /* MICB2_DISCH */ +#define WM5100_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */ +#define WM5100_MICB2_RATE 0x0020 /* MICB2_RATE */ +#define WM5100_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */ +#define WM5100_MICB2_RATE_SHIFT 5 /* MICB2_RATE */ +#define WM5100_MICB2_RATE_WIDTH 1 /* MICB2_RATE */ +#define WM5100_MICB2_LVL_MASK 0x001C /* MICB2_LVL - [4:2] */ +#define WM5100_MICB2_LVL_SHIFT 2 /* MICB2_LVL - [4:2] */ +#define WM5100_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [4:2] */ +#define WM5100_MICB2_BYPASS 0x0002 /* MICB2_BYPASS */ +#define WM5100_MICB2_BYPASS_MASK 0x0002 /* MICB2_BYPASS */ +#define WM5100_MICB2_BYPASS_SHIFT 1 /* MICB2_BYPASS */ +#define WM5100_MICB2_BYPASS_WIDTH 1 /* MICB2_BYPASS */ +#define WM5100_MICB2_ENA 0x0001 /* MICB2_ENA */ +#define WM5100_MICB2_ENA_MASK 0x0001 /* MICB2_ENA */ +#define WM5100_MICB2_ENA_SHIFT 0 /* MICB2_ENA */ +#define WM5100_MICB2_ENA_WIDTH 1 /* MICB2_ENA */ + +/* + * R535 (0x217) - Mic Bias Ctrl 3 + */ +#define WM5100_MICB3_DISCH 0x0040 /* MICB3_DISCH */ +#define WM5100_MICB3_DISCH_MASK 0x0040 /* MICB3_DISCH */ +#define WM5100_MICB3_DISCH_SHIFT 6 /* MICB3_DISCH */ +#define WM5100_MICB3_DISCH_WIDTH 1 /* MICB3_DISCH */ +#define WM5100_MICB3_RATE 0x0020 /* MICB3_RATE */ +#define WM5100_MICB3_RATE_MASK 0x0020 /* MICB3_RATE */ +#define WM5100_MICB3_RATE_SHIFT 5 /* MICB3_RATE */ +#define WM5100_MICB3_RATE_WIDTH 1 /* MICB3_RATE */ +#define WM5100_MICB3_LVL_MASK 0x001C /* MICB3_LVL - [4:2] */ +#define WM5100_MICB3_LVL_SHIFT 2 /* MICB3_LVL - [4:2] */ +#define WM5100_MICB3_LVL_WIDTH 3 /* MICB3_LVL - [4:2] */ +#define WM5100_MICB3_BYPASS 0x0002 /* MICB3_BYPASS */ +#define WM5100_MICB3_BYPASS_MASK 0x0002 /* MICB3_BYPASS */ +#define WM5100_MICB3_BYPASS_SHIFT 1 /* MICB3_BYPASS */ +#define WM5100_MICB3_BYPASS_WIDTH 1 /* MICB3_BYPASS */ +#define WM5100_MICB3_ENA 0x0001 /* MICB3_ENA */ +#define WM5100_MICB3_ENA_MASK 0x0001 /* MICB3_ENA */ +#define WM5100_MICB3_ENA_SHIFT 0 /* MICB3_ENA */ +#define WM5100_MICB3_ENA_WIDTH 1 /* MICB3_ENA */ + +/* + * R640 (0x280) - Accessory Detect Mode 1 + */ +#define WM5100_ACCDET_BIAS_SRC_MASK 0xC000 /* ACCDET_BIAS_SRC - [15:14] */ +#define WM5100_ACCDET_BIAS_SRC_SHIFT 14 /* ACCDET_BIAS_SRC - [15:14] */ +#define WM5100_ACCDET_BIAS_SRC_WIDTH 2 /* ACCDET_BIAS_SRC - [15:14] */ +#define WM5100_ACCDET_SRC 0x2000 /* ACCDET_SRC */ +#define WM5100_ACCDET_SRC_MASK 0x2000 /* ACCDET_SRC */ +#define WM5100_ACCDET_SRC_SHIFT 13 /* ACCDET_SRC */ +#define WM5100_ACCDET_SRC_WIDTH 1 /* ACCDET_SRC */ +#define WM5100_ACCDET_MODE_MASK 0x0003 /* ACCDET_MODE - [1:0] */ +#define WM5100_ACCDET_MODE_SHIFT 0 /* ACCDET_MODE - [1:0] */ +#define WM5100_ACCDET_MODE_WIDTH 2 /* ACCDET_MODE - [1:0] */ + +/* + * R648 (0x288) - Headphone Detect 1 + */ +#define WM5100_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */ +#define WM5100_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */ +#define WM5100_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */ +#define WM5100_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */ +#define WM5100_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */ +#define WM5100_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */ +#define WM5100_HP_STEP_SIZE 0x0002 /* HP_STEP_SIZE */ +#define WM5100_HP_STEP_SIZE_MASK 0x0002 /* HP_STEP_SIZE */ +#define WM5100_HP_STEP_SIZE_SHIFT 1 /* HP_STEP_SIZE */ +#define WM5100_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */ +#define WM5100_HP_POLL 0x0001 /* HP_POLL */ +#define WM5100_HP_POLL_MASK 0x0001 /* HP_POLL */ +#define WM5100_HP_POLL_SHIFT 0 /* HP_POLL */ +#define WM5100_HP_POLL_WIDTH 1 /* HP_POLL */ + +/* + * R649 (0x289) - Headphone Detect 2 + */ +#define WM5100_HP_DONE 0x0080 /* HP_DONE */ +#define WM5100_HP_DONE_MASK 0x0080 /* HP_DONE */ +#define WM5100_HP_DONE_SHIFT 7 /* HP_DONE */ +#define WM5100_HP_DONE_WIDTH 1 /* HP_DONE */ +#define WM5100_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */ +#define WM5100_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */ +#define WM5100_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */ + +/* + * R656 (0x290) - Mic Detect 1 + */ +#define WM5100_ACCDET_BIAS_STARTTIME_MASK 0xF000 /* ACCDET_BIAS_STARTTIME - [15:12] */ +#define WM5100_ACCDET_BIAS_STARTTIME_SHIFT 12 /* ACCDET_BIAS_STARTTIME - [15:12] */ +#define WM5100_ACCDET_BIAS_STARTTIME_WIDTH 4 /* ACCDET_BIAS_STARTTIME - [15:12] */ +#define WM5100_ACCDET_RATE_MASK 0x0F00 /* ACCDET_RATE - [11:8] */ +#define WM5100_ACCDET_RATE_SHIFT 8 /* ACCDET_RATE - [11:8] */ +#define WM5100_ACCDET_RATE_WIDTH 4 /* ACCDET_RATE - [11:8] */ +#define WM5100_ACCDET_DBTIME 0x0002 /* ACCDET_DBTIME */ +#define WM5100_ACCDET_DBTIME_MASK 0x0002 /* ACCDET_DBTIME */ +#define WM5100_ACCDET_DBTIME_SHIFT 1 /* ACCDET_DBTIME */ +#define WM5100_ACCDET_DBTIME_WIDTH 1 /* ACCDET_DBTIME */ +#define WM5100_ACCDET_ENA 0x0001 /* ACCDET_ENA */ +#define WM5100_ACCDET_ENA_MASK 0x0001 /* ACCDET_ENA */ +#define WM5100_ACCDET_ENA_SHIFT 0 /* ACCDET_ENA */ +#define WM5100_ACCDET_ENA_WIDTH 1 /* ACCDET_ENA */ + +/* + * R657 (0x291) - Mic Detect 2 + */ +#define WM5100_ACCDET_LVL_SEL_MASK 0x00FF /* ACCDET_LVL_SEL - [7:0] */ +#define WM5100_ACCDET_LVL_SEL_SHIFT 0 /* ACCDET_LVL_SEL - [7:0] */ +#define WM5100_ACCDET_LVL_SEL_WIDTH 8 /* ACCDET_LVL_SEL - [7:0] */ + +/* + * R658 (0x292) - Mic Detect 3 + */ +#define WM5100_ACCDET_LVL_MASK 0x07FC /* ACCDET_LVL - [10:2] */ +#define WM5100_ACCDET_LVL_SHIFT 2 /* ACCDET_LVL - [10:2] */ +#define WM5100_ACCDET_LVL_WIDTH 9 /* ACCDET_LVL - [10:2] */ +#define WM5100_ACCDET_VALID 0x0002 /* ACCDET_VALID */ +#define WM5100_ACCDET_VALID_MASK 0x0002 /* ACCDET_VALID */ +#define WM5100_ACCDET_VALID_SHIFT 1 /* ACCDET_VALID */ +#define WM5100_ACCDET_VALID_WIDTH 1 /* ACCDET_VALID */ +#define WM5100_ACCDET_STS 0x0001 /* ACCDET_STS */ +#define WM5100_ACCDET_STS_MASK 0x0001 /* ACCDET_STS */ +#define WM5100_ACCDET_STS_SHIFT 0 /* ACCDET_STS */ +#define WM5100_ACCDET_STS_WIDTH 1 /* ACCDET_STS */ + +/* + * R699 (0x2BB) - Misc Control + */ +#define WM5100_HPCOM_SRC 0x200 /* HPCOM_SRC */ +#define WM5100_HPCOM_SRC_SHIFT 9 /* HPCOM_SRC */ + +/* + * R769 (0x301) - Input Enables + */ +#define WM5100_IN4L_ENA 0x0080 /* IN4L_ENA */ +#define WM5100_IN4L_ENA_MASK 0x0080 /* IN4L_ENA */ +#define WM5100_IN4L_ENA_SHIFT 7 /* IN4L_ENA */ +#define WM5100_IN4L_ENA_WIDTH 1 /* IN4L_ENA */ +#define WM5100_IN4R_ENA 0x0040 /* IN4R_ENA */ +#define WM5100_IN4R_ENA_MASK 0x0040 /* IN4R_ENA */ +#define WM5100_IN4R_ENA_SHIFT 6 /* IN4R_ENA */ +#define WM5100_IN4R_ENA_WIDTH 1 /* IN4R_ENA */ +#define WM5100_IN3L_ENA 0x0020 /* IN3L_ENA */ +#define WM5100_IN3L_ENA_MASK 0x0020 /* IN3L_ENA */ +#define WM5100_IN3L_ENA_SHIFT 5 /* IN3L_ENA */ +#define WM5100_IN3L_ENA_WIDTH 1 /* IN3L_ENA */ +#define WM5100_IN3R_ENA 0x0010 /* IN3R_ENA */ +#define WM5100_IN3R_ENA_MASK 0x0010 /* IN3R_ENA */ +#define WM5100_IN3R_ENA_SHIFT 4 /* IN3R_ENA */ +#define WM5100_IN3R_ENA_WIDTH 1 /* IN3R_ENA */ +#define WM5100_IN2L_ENA 0x0008 /* IN2L_ENA */ +#define WM5100_IN2L_ENA_MASK 0x0008 /* IN2L_ENA */ +#define WM5100_IN2L_ENA_SHIFT 3 /* IN2L_ENA */ +#define WM5100_IN2L_ENA_WIDTH 1 /* IN2L_ENA */ +#define WM5100_IN2R_ENA 0x0004 /* IN2R_ENA */ +#define WM5100_IN2R_ENA_MASK 0x0004 /* IN2R_ENA */ +#define WM5100_IN2R_ENA_SHIFT 2 /* IN2R_ENA */ +#define WM5100_IN2R_ENA_WIDTH 1 /* IN2R_ENA */ +#define WM5100_IN1L_ENA 0x0002 /* IN1L_ENA */ +#define WM5100_IN1L_ENA_MASK 0x0002 /* IN1L_ENA */ +#define WM5100_IN1L_ENA_SHIFT 1 /* IN1L_ENA */ +#define WM5100_IN1L_ENA_WIDTH 1 /* IN1L_ENA */ +#define WM5100_IN1R_ENA 0x0001 /* IN1R_ENA */ +#define WM5100_IN1R_ENA_MASK 0x0001 /* IN1R_ENA */ +#define WM5100_IN1R_ENA_SHIFT 0 /* IN1R_ENA */ +#define WM5100_IN1R_ENA_WIDTH 1 /* IN1R_ENA */ + +/* + * R770 (0x302) - Input Enables Status + */ +#define WM5100_IN4L_ENA_STS 0x0080 /* IN4L_ENA_STS */ +#define WM5100_IN4L_ENA_STS_MASK 0x0080 /* IN4L_ENA_STS */ +#define WM5100_IN4L_ENA_STS_SHIFT 7 /* IN4L_ENA_STS */ +#define WM5100_IN4L_ENA_STS_WIDTH 1 /* IN4L_ENA_STS */ +#define WM5100_IN4R_ENA_STS 0x0040 /* IN4R_ENA_STS */ +#define WM5100_IN4R_ENA_STS_MASK 0x0040 /* IN4R_ENA_STS */ +#define WM5100_IN4R_ENA_STS_SHIFT 6 /* IN4R_ENA_STS */ +#define WM5100_IN4R_ENA_STS_WIDTH 1 /* IN4R_ENA_STS */ +#define WM5100_IN3L_ENA_STS 0x0020 /* IN3L_ENA_STS */ +#define WM5100_IN3L_ENA_STS_MASK 0x0020 /* IN3L_ENA_STS */ +#define WM5100_IN3L_ENA_STS_SHIFT 5 /* IN3L_ENA_STS */ +#define WM5100_IN3L_ENA_STS_WIDTH 1 /* IN3L_ENA_STS */ +#define WM5100_IN3R_ENA_STS 0x0010 /* IN3R_ENA_STS */ +#define WM5100_IN3R_ENA_STS_MASK 0x0010 /* IN3R_ENA_STS */ +#define WM5100_IN3R_ENA_STS_SHIFT 4 /* IN3R_ENA_STS */ +#define WM5100_IN3R_ENA_STS_WIDTH 1 /* IN3R_ENA_STS */ +#define WM5100_IN2L_ENA_STS 0x0008 /* IN2L_ENA_STS */ +#define WM5100_IN2L_ENA_STS_MASK 0x0008 /* IN2L_ENA_STS */ +#define WM5100_IN2L_ENA_STS_SHIFT 3 /* IN2L_ENA_STS */ +#define WM5100_IN2L_ENA_STS_WIDTH 1 /* IN2L_ENA_STS */ +#define WM5100_IN2R_ENA_STS 0x0004 /* IN2R_ENA_STS */ +#define WM5100_IN2R_ENA_STS_MASK 0x0004 /* IN2R_ENA_STS */ +#define WM5100_IN2R_ENA_STS_SHIFT 2 /* IN2R_ENA_STS */ +#define WM5100_IN2R_ENA_STS_WIDTH 1 /* IN2R_ENA_STS */ +#define WM5100_IN1L_ENA_STS 0x0002 /* IN1L_ENA_STS */ +#define WM5100_IN1L_ENA_STS_MASK 0x0002 /* IN1L_ENA_STS */ +#define WM5100_IN1L_ENA_STS_SHIFT 1 /* IN1L_ENA_STS */ +#define WM5100_IN1L_ENA_STS_WIDTH 1 /* IN1L_ENA_STS */ +#define WM5100_IN1R_ENA_STS 0x0001 /* IN1R_ENA_STS */ +#define WM5100_IN1R_ENA_STS_MASK 0x0001 /* IN1R_ENA_STS */ +#define WM5100_IN1R_ENA_STS_SHIFT 0 /* IN1R_ENA_STS */ +#define WM5100_IN1R_ENA_STS_WIDTH 1 /* IN1R_ENA_STS */ + +/* + * R784 (0x310) - IN1L Control + */ +#define WM5100_IN_RATE_MASK 0xC000 /* IN_RATE - [15:14] */ +#define WM5100_IN_RATE_SHIFT 14 /* IN_RATE - [15:14] */ +#define WM5100_IN_RATE_WIDTH 2 /* IN_RATE - [15:14] */ +#define WM5100_IN1_OSR 0x2000 /* IN1_OSR */ +#define WM5100_IN1_OSR_MASK 0x2000 /* IN1_OSR */ +#define WM5100_IN1_OSR_SHIFT 13 /* IN1_OSR */ +#define WM5100_IN1_OSR_WIDTH 1 /* IN1_OSR */ +#define WM5100_IN1_DMIC_SUP_MASK 0x1800 /* IN1_DMIC_SUP - [12:11] */ +#define WM5100_IN1_DMIC_SUP_SHIFT 11 /* IN1_DMIC_SUP - [12:11] */ +#define WM5100_IN1_DMIC_SUP_WIDTH 2 /* IN1_DMIC_SUP - [12:11] */ +#define WM5100_IN1_MODE_MASK 0x0600 /* IN1_MODE - [10:9] */ +#define WM5100_IN1_MODE_SHIFT 9 /* IN1_MODE - [10:9] */ +#define WM5100_IN1_MODE_WIDTH 2 /* IN1_MODE - [10:9] */ +#define WM5100_IN1L_PGA_VOL_MASK 0x00FE /* IN1L_PGA_VOL - [7:1] */ +#define WM5100_IN1L_PGA_VOL_SHIFT 1 /* IN1L_PGA_VOL - [7:1] */ +#define WM5100_IN1L_PGA_VOL_WIDTH 7 /* IN1L_PGA_VOL - [7:1] */ + +/* + * R785 (0x311) - IN1R Control + */ +#define WM5100_IN1R_PGA_VOL_MASK 0x00FE /* IN1R_PGA_VOL - [7:1] */ +#define WM5100_IN1R_PGA_VOL_SHIFT 1 /* IN1R_PGA_VOL - [7:1] */ +#define WM5100_IN1R_PGA_VOL_WIDTH 7 /* IN1R_PGA_VOL - [7:1] */ + +/* + * R786 (0x312) - IN2L Control + */ +#define WM5100_IN2_OSR 0x2000 /* IN2_OSR */ +#define WM5100_IN2_OSR_MASK 0x2000 /* IN2_OSR */ +#define WM5100_IN2_OSR_SHIFT 13 /* IN2_OSR */ +#define WM5100_IN2_OSR_WIDTH 1 /* IN2_OSR */ +#define WM5100_IN2_DMIC_SUP_MASK 0x1800 /* IN2_DMIC_SUP - [12:11] */ +#define WM5100_IN2_DMIC_SUP_SHIFT 11 /* IN2_DMIC_SUP - [12:11] */ +#define WM5100_IN2_DMIC_SUP_WIDTH 2 /* IN2_DMIC_SUP - [12:11] */ +#define WM5100_IN2_MODE_MASK 0x0600 /* IN2_MODE - [10:9] */ +#define WM5100_IN2_MODE_SHIFT 9 /* IN2_MODE - [10:9] */ +#define WM5100_IN2_MODE_WIDTH 2 /* IN2_MODE - [10:9] */ +#define WM5100_IN2L_PGA_VOL_MASK 0x00FE /* IN2L_PGA_VOL - [7:1] */ +#define WM5100_IN2L_PGA_VOL_SHIFT 1 /* IN2L_PGA_VOL - [7:1] */ +#define WM5100_IN2L_PGA_VOL_WIDTH 7 /* IN2L_PGA_VOL - [7:1] */ + +/* + * R787 (0x313) - IN2R Control + */ +#define WM5100_IN2R_PGA_VOL_MASK 0x00FE /* IN2R_PGA_VOL - [7:1] */ +#define WM5100_IN2R_PGA_VOL_SHIFT 1 /* IN2R_PGA_VOL - [7:1] */ +#define WM5100_IN2R_PGA_VOL_WIDTH 7 /* IN2R_PGA_VOL - [7:1] */ + +/* + * R788 (0x314) - IN3L Control + */ +#define WM5100_IN3_OSR 0x2000 /* IN3_OSR */ +#define WM5100_IN3_OSR_MASK 0x2000 /* IN3_OSR */ +#define WM5100_IN3_OSR_SHIFT 13 /* IN3_OSR */ +#define WM5100_IN3_OSR_WIDTH 1 /* IN3_OSR */ +#define WM5100_IN3_DMIC_SUP_MASK 0x1800 /* IN3_DMIC_SUP - [12:11] */ +#define WM5100_IN3_DMIC_SUP_SHIFT 11 /* IN3_DMIC_SUP - [12:11] */ +#define WM5100_IN3_DMIC_SUP_WIDTH 2 /* IN3_DMIC_SUP - [12:11] */ +#define WM5100_IN3_MODE_MASK 0x0600 /* IN3_MODE - [10:9] */ +#define WM5100_IN3_MODE_SHIFT 9 /* IN3_MODE - [10:9] */ +#define WM5100_IN3_MODE_WIDTH 2 /* IN3_MODE - [10:9] */ +#define WM5100_IN3L_PGA_VOL_MASK 0x00FE /* IN3L_PGA_VOL - [7:1] */ +#define WM5100_IN3L_PGA_VOL_SHIFT 1 /* IN3L_PGA_VOL - [7:1] */ +#define WM5100_IN3L_PGA_VOL_WIDTH 7 /* IN3L_PGA_VOL - [7:1] */ + +/* + * R789 (0x315) - IN3R Control + */ +#define WM5100_IN3R_PGA_VOL_MASK 0x00FE /* IN3R_PGA_VOL - [7:1] */ +#define WM5100_IN3R_PGA_VOL_SHIFT 1 /* IN3R_PGA_VOL - [7:1] */ +#define WM5100_IN3R_PGA_VOL_WIDTH 7 /* IN3R_PGA_VOL - [7:1] */ + +/* + * R790 (0x316) - IN4L Control + */ +#define WM5100_IN4_OSR 0x2000 /* IN4_OSR */ +#define WM5100_IN4_OSR_MASK 0x2000 /* IN4_OSR */ +#define WM5100_IN4_OSR_SHIFT 13 /* IN4_OSR */ +#define WM5100_IN4_OSR_WIDTH 1 /* IN4_OSR */ +#define WM5100_IN4_DMIC_SUP_MASK 0x1800 /* IN4_DMIC_SUP - [12:11] */ +#define WM5100_IN4_DMIC_SUP_SHIFT 11 /* IN4_DMIC_SUP - [12:11] */ +#define WM5100_IN4_DMIC_SUP_WIDTH 2 /* IN4_DMIC_SUP - [12:11] */ +#define WM5100_IN4_MODE_MASK 0x0600 /* IN4_MODE - [10:9] */ +#define WM5100_IN4_MODE_SHIFT 9 /* IN4_MODE - [10:9] */ +#define WM5100_IN4_MODE_WIDTH 2 /* IN4_MODE - [10:9] */ +#define WM5100_IN4L_PGA_VOL_MASK 0x00FE /* IN4L_PGA_VOL - [7:1] */ +#define WM5100_IN4L_PGA_VOL_SHIFT 1 /* IN4L_PGA_VOL - [7:1] */ +#define WM5100_IN4L_PGA_VOL_WIDTH 7 /* IN4L_PGA_VOL - [7:1] */ + +/* + * R791 (0x317) - IN4R Control + */ +#define WM5100_IN4R_PGA_VOL_MASK 0x00FE /* IN4R_PGA_VOL - [7:1] */ +#define WM5100_IN4R_PGA_VOL_SHIFT 1 /* IN4R_PGA_VOL - [7:1] */ +#define WM5100_IN4R_PGA_VOL_WIDTH 7 /* IN4R_PGA_VOL - [7:1] */ + +/* + * R792 (0x318) - RXANC_SRC + */ +#define WM5100_IN_RXANC_SEL_MASK 0x0007 /* IN_RXANC_SEL - [2:0] */ +#define WM5100_IN_RXANC_SEL_SHIFT 0 /* IN_RXANC_SEL - [2:0] */ +#define WM5100_IN_RXANC_SEL_WIDTH 3 /* IN_RXANC_SEL - [2:0] */ + +/* + * R793 (0x319) - Input Volume Ramp + */ +#define WM5100_IN_VD_RAMP_MASK 0x0070 /* IN_VD_RAMP - [6:4] */ +#define WM5100_IN_VD_RAMP_SHIFT 4 /* IN_VD_RAMP - [6:4] */ +#define WM5100_IN_VD_RAMP_WIDTH 3 /* IN_VD_RAMP - [6:4] */ +#define WM5100_IN_VI_RAMP_MASK 0x0007 /* IN_VI_RAMP - [2:0] */ +#define WM5100_IN_VI_RAMP_SHIFT 0 /* IN_VI_RAMP - [2:0] */ +#define WM5100_IN_VI_RAMP_WIDTH 3 /* IN_VI_RAMP - [2:0] */ + +/* + * R800 (0x320) - ADC Digital Volume 1L + */ +#define WM5100_IN_VU 0x0200 /* IN_VU */ +#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM5100_IN_VU_SHIFT 9 /* IN_VU */ +#define WM5100_IN_VU_WIDTH 1 /* IN_VU */ +#define WM5100_IN1L_MUTE 0x0100 /* IN1L_MUTE */ +#define WM5100_IN1L_MUTE_MASK 0x0100 /* IN1L_MUTE */ +#define WM5100_IN1L_MUTE_SHIFT 8 /* IN1L_MUTE */ +#define WM5100_IN1L_MUTE_WIDTH 1 /* IN1L_MUTE */ +#define WM5100_IN1L_VOL_MASK 0x00FF /* IN1L_VOL - [7:0] */ +#define WM5100_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [7:0] */ +#define WM5100_IN1L_VOL_WIDTH 8 /* IN1L_VOL - [7:0] */ + +/* + * R801 (0x321) - ADC Digital Volume 1R + */ +#define WM5100_IN_VU 0x0200 /* IN_VU */ +#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM5100_IN_VU_SHIFT 9 /* IN_VU */ +#define WM5100_IN_VU_WIDTH 1 /* IN_VU */ +#define WM5100_IN1R_MUTE 0x0100 /* IN1R_MUTE */ +#define WM5100_IN1R_MUTE_MASK 0x0100 /* IN1R_MUTE */ +#define WM5100_IN1R_MUTE_SHIFT 8 /* IN1R_MUTE */ +#define WM5100_IN1R_MUTE_WIDTH 1 /* IN1R_MUTE */ +#define WM5100_IN1R_VOL_MASK 0x00FF /* IN1R_VOL - [7:0] */ +#define WM5100_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [7:0] */ +#define WM5100_IN1R_VOL_WIDTH 8 /* IN1R_VOL - [7:0] */ + +/* + * R802 (0x322) - ADC Digital Volume 2L + */ +#define WM5100_IN_VU 0x0200 /* IN_VU */ +#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM5100_IN_VU_SHIFT 9 /* IN_VU */ +#define WM5100_IN_VU_WIDTH 1 /* IN_VU */ +#define WM5100_IN2L_MUTE 0x0100 /* IN2L_MUTE */ +#define WM5100_IN2L_MUTE_MASK 0x0100 /* IN2L_MUTE */ +#define WM5100_IN2L_MUTE_SHIFT 8 /* IN2L_MUTE */ +#define WM5100_IN2L_MUTE_WIDTH 1 /* IN2L_MUTE */ +#define WM5100_IN2L_VOL_MASK 0x00FF /* IN2L_VOL - [7:0] */ +#define WM5100_IN2L_VOL_SHIFT 0 /* IN2L_VOL - [7:0] */ +#define WM5100_IN2L_VOL_WIDTH 8 /* IN2L_VOL - [7:0] */ + +/* + * R803 (0x323) - ADC Digital Volume 2R + */ +#define WM5100_IN_VU 0x0200 /* IN_VU */ +#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM5100_IN_VU_SHIFT 9 /* IN_VU */ +#define WM5100_IN_VU_WIDTH 1 /* IN_VU */ +#define WM5100_IN2R_MUTE 0x0100 /* IN2R_MUTE */ +#define WM5100_IN2R_MUTE_MASK 0x0100 /* IN2R_MUTE */ +#define WM5100_IN2R_MUTE_SHIFT 8 /* IN2R_MUTE */ +#define WM5100_IN2R_MUTE_WIDTH 1 /* IN2R_MUTE */ +#define WM5100_IN2R_VOL_MASK 0x00FF /* IN2R_VOL - [7:0] */ +#define WM5100_IN2R_VOL_SHIFT 0 /* IN2R_VOL - [7:0] */ +#define WM5100_IN2R_VOL_WIDTH 8 /* IN2R_VOL - [7:0] */ + +/* + * R804 (0x324) - ADC Digital Volume 3L + */ +#define WM5100_IN_VU 0x0200 /* IN_VU */ +#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM5100_IN_VU_SHIFT 9 /* IN_VU */ +#define WM5100_IN_VU_WIDTH 1 /* IN_VU */ +#define WM5100_IN3L_MUTE 0x0100 /* IN3L_MUTE */ +#define WM5100_IN3L_MUTE_MASK 0x0100 /* IN3L_MUTE */ +#define WM5100_IN3L_MUTE_SHIFT 8 /* IN3L_MUTE */ +#define WM5100_IN3L_MUTE_WIDTH 1 /* IN3L_MUTE */ +#define WM5100_IN3L_VOL_MASK 0x00FF /* IN3L_VOL - [7:0] */ +#define WM5100_IN3L_VOL_SHIFT 0 /* IN3L_VOL - [7:0] */ +#define WM5100_IN3L_VOL_WIDTH 8 /* IN3L_VOL - [7:0] */ + +/* + * R805 (0x325) - ADC Digital Volume 3R + */ +#define WM5100_IN_VU 0x0200 /* IN_VU */ +#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM5100_IN_VU_SHIFT 9 /* IN_VU */ +#define WM5100_IN_VU_WIDTH 1 /* IN_VU */ +#define WM5100_IN3R_MUTE 0x0100 /* IN3R_MUTE */ +#define WM5100_IN3R_MUTE_MASK 0x0100 /* IN3R_MUTE */ +#define WM5100_IN3R_MUTE_SHIFT 8 /* IN3R_MUTE */ +#define WM5100_IN3R_MUTE_WIDTH 1 /* IN3R_MUTE */ +#define WM5100_IN3R_VOL_MASK 0x00FF /* IN3R_VOL - [7:0] */ +#define WM5100_IN3R_VOL_SHIFT 0 /* IN3R_VOL - [7:0] */ +#define WM5100_IN3R_VOL_WIDTH 8 /* IN3R_VOL - [7:0] */ + +/* + * R806 (0x326) - ADC Digital Volume 4L + */ +#define WM5100_IN_VU 0x0200 /* IN_VU */ +#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM5100_IN_VU_SHIFT 9 /* IN_VU */ +#define WM5100_IN_VU_WIDTH 1 /* IN_VU */ +#define WM5100_IN4L_MUTE 0x0100 /* IN4L_MUTE */ +#define WM5100_IN4L_MUTE_MASK 0x0100 /* IN4L_MUTE */ +#define WM5100_IN4L_MUTE_SHIFT 8 /* IN4L_MUTE */ +#define WM5100_IN4L_MUTE_WIDTH 1 /* IN4L_MUTE */ +#define WM5100_IN4L_VOL_MASK 0x00FF /* IN4L_VOL - [7:0] */ +#define WM5100_IN4L_VOL_SHIFT 0 /* IN4L_VOL - [7:0] */ +#define WM5100_IN4L_VOL_WIDTH 8 /* IN4L_VOL - [7:0] */ + +/* + * R807 (0x327) - ADC Digital Volume 4R + */ +#define WM5100_IN_VU 0x0200 /* IN_VU */ +#define WM5100_IN_VU_MASK 0x0200 /* IN_VU */ +#define WM5100_IN_VU_SHIFT 9 /* IN_VU */ +#define WM5100_IN_VU_WIDTH 1 /* IN_VU */ +#define WM5100_IN4R_MUTE 0x0100 /* IN4R_MUTE */ +#define WM5100_IN4R_MUTE_MASK 0x0100 /* IN4R_MUTE */ +#define WM5100_IN4R_MUTE_SHIFT 8 /* IN4R_MUTE */ +#define WM5100_IN4R_MUTE_WIDTH 1 /* IN4R_MUTE */ +#define WM5100_IN4R_VOL_MASK 0x00FF /* IN4R_VOL - [7:0] */ +#define WM5100_IN4R_VOL_SHIFT 0 /* IN4R_VOL - [7:0] */ +#define WM5100_IN4R_VOL_WIDTH 8 /* IN4R_VOL - [7:0] */ + +/* + * R1025 (0x401) - Output Enables 2 + */ +#define WM5100_OUT6L_ENA 0x0800 /* OUT6L_ENA */ +#define WM5100_OUT6L_ENA_MASK 0x0800 /* OUT6L_ENA */ +#define WM5100_OUT6L_ENA_SHIFT 11 /* OUT6L_ENA */ +#define WM5100_OUT6L_ENA_WIDTH 1 /* OUT6L_ENA */ +#define WM5100_OUT6R_ENA 0x0400 /* OUT6R_ENA */ +#define WM5100_OUT6R_ENA_MASK 0x0400 /* OUT6R_ENA */ +#define WM5100_OUT6R_ENA_SHIFT 10 /* OUT6R_ENA */ +#define WM5100_OUT6R_ENA_WIDTH 1 /* OUT6R_ENA */ +#define WM5100_OUT5L_ENA 0x0200 /* OUT5L_ENA */ +#define WM5100_OUT5L_ENA_MASK 0x0200 /* OUT5L_ENA */ +#define WM5100_OUT5L_ENA_SHIFT 9 /* OUT5L_ENA */ +#define WM5100_OUT5L_ENA_WIDTH 1 /* OUT5L_ENA */ +#define WM5100_OUT5R_ENA 0x0100 /* OUT5R_ENA */ +#define WM5100_OUT5R_ENA_MASK 0x0100 /* OUT5R_ENA */ +#define WM5100_OUT5R_ENA_SHIFT 8 /* OUT5R_ENA */ +#define WM5100_OUT5R_ENA_WIDTH 1 /* OUT5R_ENA */ +#define WM5100_OUT4L_ENA 0x0080 /* OUT4L_ENA */ +#define WM5100_OUT4L_ENA_MASK 0x0080 /* OUT4L_ENA */ +#define WM5100_OUT4L_ENA_SHIFT 7 /* OUT4L_ENA */ +#define WM5100_OUT4L_ENA_WIDTH 1 /* OUT4L_ENA */ +#define WM5100_OUT4R_ENA 0x0040 /* OUT4R_ENA */ +#define WM5100_OUT4R_ENA_MASK 0x0040 /* OUT4R_ENA */ +#define WM5100_OUT4R_ENA_SHIFT 6 /* OUT4R_ENA */ +#define WM5100_OUT4R_ENA_WIDTH 1 /* OUT4R_ENA */ + +/* + * R1026 (0x402) - Output Status 1 + */ +#define WM5100_OUT3L_ENA_STS 0x0020 /* OUT3L_ENA_STS */ +#define WM5100_OUT3L_ENA_STS_MASK 0x0020 /* OUT3L_ENA_STS */ +#define WM5100_OUT3L_ENA_STS_SHIFT 5 /* OUT3L_ENA_STS */ +#define WM5100_OUT3L_ENA_STS_WIDTH 1 /* OUT3L_ENA_STS */ +#define WM5100_OUT3R_ENA_STS 0x0010 /* OUT3R_ENA_STS */ +#define WM5100_OUT3R_ENA_STS_MASK 0x0010 /* OUT3R_ENA_STS */ +#define WM5100_OUT3R_ENA_STS_SHIFT 4 /* OUT3R_ENA_STS */ +#define WM5100_OUT3R_ENA_STS_WIDTH 1 /* OUT3R_ENA_STS */ +#define WM5100_OUT2L_ENA_STS 0x0008 /* OUT2L_ENA_STS */ +#define WM5100_OUT2L_ENA_STS_MASK 0x0008 /* OUT2L_ENA_STS */ +#define WM5100_OUT2L_ENA_STS_SHIFT 3 /* OUT2L_ENA_STS */ +#define WM5100_OUT2L_ENA_STS_WIDTH 1 /* OUT2L_ENA_STS */ +#define WM5100_OUT2R_ENA_STS 0x0004 /* OUT2R_ENA_STS */ +#define WM5100_OUT2R_ENA_STS_MASK 0x0004 /* OUT2R_ENA_STS */ +#define WM5100_OUT2R_ENA_STS_SHIFT 2 /* OUT2R_ENA_STS */ +#define WM5100_OUT2R_ENA_STS_WIDTH 1 /* OUT2R_ENA_STS */ +#define WM5100_OUT1L_ENA_STS 0x0002 /* OUT1L_ENA_STS */ +#define WM5100_OUT1L_ENA_STS_MASK 0x0002 /* OUT1L_ENA_STS */ +#define WM5100_OUT1L_ENA_STS_SHIFT 1 /* OUT1L_ENA_STS */ +#define WM5100_OUT1L_ENA_STS_WIDTH 1 /* OUT1L_ENA_STS */ +#define WM5100_OUT1R_ENA_STS 0x0001 /* OUT1R_ENA_STS */ +#define WM5100_OUT1R_ENA_STS_MASK 0x0001 /* OUT1R_ENA_STS */ +#define WM5100_OUT1R_ENA_STS_SHIFT 0 /* OUT1R_ENA_STS */ +#define WM5100_OUT1R_ENA_STS_WIDTH 1 /* OUT1R_ENA_STS */ + +/* + * R1027 (0x403) - Output Status 2 + */ +#define WM5100_OUT6L_ENA_STS 0x0800 /* OUT6L_ENA_STS */ +#define WM5100_OUT6L_ENA_STS_MASK 0x0800 /* OUT6L_ENA_STS */ +#define WM5100_OUT6L_ENA_STS_SHIFT 11 /* OUT6L_ENA_STS */ +#define WM5100_OUT6L_ENA_STS_WIDTH 1 /* OUT6L_ENA_STS */ +#define WM5100_OUT6R_ENA_STS 0x0400 /* OUT6R_ENA_STS */ +#define WM5100_OUT6R_ENA_STS_MASK 0x0400 /* OUT6R_ENA_STS */ +#define WM5100_OUT6R_ENA_STS_SHIFT 10 /* OUT6R_ENA_STS */ +#define WM5100_OUT6R_ENA_STS_WIDTH 1 /* OUT6R_ENA_STS */ +#define WM5100_OUT5L_ENA_STS 0x0200 /* OUT5L_ENA_STS */ +#define WM5100_OUT5L_ENA_STS_MASK 0x0200 /* OUT5L_ENA_STS */ +#define WM5100_OUT5L_ENA_STS_SHIFT 9 /* OUT5L_ENA_STS */ +#define WM5100_OUT5L_ENA_STS_WIDTH 1 /* OUT5L_ENA_STS */ +#define WM5100_OUT5R_ENA_STS 0x0100 /* OUT5R_ENA_STS */ +#define WM5100_OUT5R_ENA_STS_MASK 0x0100 /* OUT5R_ENA_STS */ +#define WM5100_OUT5R_ENA_STS_SHIFT 8 /* OUT5R_ENA_STS */ +#define WM5100_OUT5R_ENA_STS_WIDTH 1 /* OUT5R_ENA_STS */ +#define WM5100_OUT4L_ENA_STS 0x0080 /* OUT4L_ENA_STS */ +#define WM5100_OUT4L_ENA_STS_MASK 0x0080 /* OUT4L_ENA_STS */ +#define WM5100_OUT4L_ENA_STS_SHIFT 7 /* OUT4L_ENA_STS */ +#define WM5100_OUT4L_ENA_STS_WIDTH 1 /* OUT4L_ENA_STS */ +#define WM5100_OUT4R_ENA_STS 0x0040 /* OUT4R_ENA_STS */ +#define WM5100_OUT4R_ENA_STS_MASK 0x0040 /* OUT4R_ENA_STS */ +#define WM5100_OUT4R_ENA_STS_SHIFT 6 /* OUT4R_ENA_STS */ +#define WM5100_OUT4R_ENA_STS_WIDTH 1 /* OUT4R_ENA_STS */ + +/* + * R1032 (0x408) - Channel Enables 1 + */ +#define WM5100_HP3L_ENA 0x0020 /* HP3L_ENA */ +#define WM5100_HP3L_ENA_MASK 0x0020 /* HP3L_ENA */ +#define WM5100_HP3L_ENA_SHIFT 5 /* HP3L_ENA */ +#define WM5100_HP3L_ENA_WIDTH 1 /* HP3L_ENA */ +#define WM5100_HP3R_ENA 0x0010 /* HP3R_ENA */ +#define WM5100_HP3R_ENA_MASK 0x0010 /* HP3R_ENA */ +#define WM5100_HP3R_ENA_SHIFT 4 /* HP3R_ENA */ +#define WM5100_HP3R_ENA_WIDTH 1 /* HP3R_ENA */ +#define WM5100_HP2L_ENA 0x0008 /* HP2L_ENA */ +#define WM5100_HP2L_ENA_MASK 0x0008 /* HP2L_ENA */ +#define WM5100_HP2L_ENA_SHIFT 3 /* HP2L_ENA */ +#define WM5100_HP2L_ENA_WIDTH 1 /* HP2L_ENA */ +#define WM5100_HP2R_ENA 0x0004 /* HP2R_ENA */ +#define WM5100_HP2R_ENA_MASK 0x0004 /* HP2R_ENA */ +#define WM5100_HP2R_ENA_SHIFT 2 /* HP2R_ENA */ +#define WM5100_HP2R_ENA_WIDTH 1 /* HP2R_ENA */ +#define WM5100_HP1L_ENA 0x0002 /* HP1L_ENA */ +#define WM5100_HP1L_ENA_MASK 0x0002 /* HP1L_ENA */ +#define WM5100_HP1L_ENA_SHIFT 1 /* HP1L_ENA */ +#define WM5100_HP1L_ENA_WIDTH 1 /* HP1L_ENA */ +#define WM5100_HP1R_ENA 0x0001 /* HP1R_ENA */ +#define WM5100_HP1R_ENA_MASK 0x0001 /* HP1R_ENA */ +#define WM5100_HP1R_ENA_SHIFT 0 /* HP1R_ENA */ +#define WM5100_HP1R_ENA_WIDTH 1 /* HP1R_ENA */ + +/* + * R1040 (0x410) - Out Volume 1L + */ +#define WM5100_OUT_RATE_MASK 0xC000 /* OUT_RATE - [15:14] */ +#define WM5100_OUT_RATE_SHIFT 14 /* OUT_RATE - [15:14] */ +#define WM5100_OUT_RATE_WIDTH 2 /* OUT_RATE - [15:14] */ +#define WM5100_OUT1_OSR 0x2000 /* OUT1_OSR */ +#define WM5100_OUT1_OSR_MASK 0x2000 /* OUT1_OSR */ +#define WM5100_OUT1_OSR_SHIFT 13 /* OUT1_OSR */ +#define WM5100_OUT1_OSR_WIDTH 1 /* OUT1_OSR */ +#define WM5100_OUT1_MONO 0x1000 /* OUT1_MONO */ +#define WM5100_OUT1_MONO_MASK 0x1000 /* OUT1_MONO */ +#define WM5100_OUT1_MONO_SHIFT 12 /* OUT1_MONO */ +#define WM5100_OUT1_MONO_WIDTH 1 /* OUT1_MONO */ +#define WM5100_OUT1L_ANC_SRC 0x0800 /* OUT1L_ANC_SRC */ +#define WM5100_OUT1L_ANC_SRC_MASK 0x0800 /* OUT1L_ANC_SRC */ +#define WM5100_OUT1L_ANC_SRC_SHIFT 11 /* OUT1L_ANC_SRC */ +#define WM5100_OUT1L_ANC_SRC_WIDTH 1 /* OUT1L_ANC_SRC */ +#define WM5100_OUT1L_PGA_VOL_MASK 0x00FE /* OUT1L_PGA_VOL - [7:1] */ +#define WM5100_OUT1L_PGA_VOL_SHIFT 1 /* OUT1L_PGA_VOL - [7:1] */ +#define WM5100_OUT1L_PGA_VOL_WIDTH 7 /* OUT1L_PGA_VOL - [7:1] */ + +/* + * R1041 (0x411) - Out Volume 1R + */ +#define WM5100_OUT1R_ANC_SRC 0x0800 /* OUT1R_ANC_SRC */ +#define WM5100_OUT1R_ANC_SRC_MASK 0x0800 /* OUT1R_ANC_SRC */ +#define WM5100_OUT1R_ANC_SRC_SHIFT 11 /* OUT1R_ANC_SRC */ +#define WM5100_OUT1R_ANC_SRC_WIDTH 1 /* OUT1R_ANC_SRC */ +#define WM5100_OUT1R_PGA_VOL_MASK 0x00FE /* OUT1R_PGA_VOL - [7:1] */ +#define WM5100_OUT1R_PGA_VOL_SHIFT 1 /* OUT1R_PGA_VOL - [7:1] */ +#define WM5100_OUT1R_PGA_VOL_WIDTH 7 /* OUT1R_PGA_VOL - [7:1] */ + +/* + * R1042 (0x412) - DAC Volume Limit 1L + */ +#define WM5100_OUT1L_VOL_LIM_MASK 0x00FF /* OUT1L_VOL_LIM - [7:0] */ +#define WM5100_OUT1L_VOL_LIM_SHIFT 0 /* OUT1L_VOL_LIM - [7:0] */ +#define WM5100_OUT1L_VOL_LIM_WIDTH 8 /* OUT1L_VOL_LIM - [7:0] */ + +/* + * R1043 (0x413) - DAC Volume Limit 1R + */ +#define WM5100_OUT1R_VOL_LIM_MASK 0x00FF /* OUT1R_VOL_LIM - [7:0] */ +#define WM5100_OUT1R_VOL_LIM_SHIFT 0 /* OUT1R_VOL_LIM - [7:0] */ +#define WM5100_OUT1R_VOL_LIM_WIDTH 8 /* OUT1R_VOL_LIM - [7:0] */ + +/* + * R1044 (0x414) - Out Volume 2L + */ +#define WM5100_OUT2_OSR 0x2000 /* OUT2_OSR */ +#define WM5100_OUT2_OSR_MASK 0x2000 /* OUT2_OSR */ +#define WM5100_OUT2_OSR_SHIFT 13 /* OUT2_OSR */ +#define WM5100_OUT2_OSR_WIDTH 1 /* OUT2_OSR */ +#define WM5100_OUT2_MONO 0x1000 /* OUT2_MONO */ +#define WM5100_OUT2_MONO_MASK 0x1000 /* OUT2_MONO */ +#define WM5100_OUT2_MONO_SHIFT 12 /* OUT2_MONO */ +#define WM5100_OUT2_MONO_WIDTH 1 /* OUT2_MONO */ +#define WM5100_OUT2L_ANC_SRC 0x0800 /* OUT2L_ANC_SRC */ +#define WM5100_OUT2L_ANC_SRC_MASK 0x0800 /* OUT2L_ANC_SRC */ +#define WM5100_OUT2L_ANC_SRC_SHIFT 11 /* OUT2L_ANC_SRC */ +#define WM5100_OUT2L_ANC_SRC_WIDTH 1 /* OUT2L_ANC_SRC */ +#define WM5100_OUT2L_PGA_VOL_MASK 0x00FE /* OUT2L_PGA_VOL - [7:1] */ +#define WM5100_OUT2L_PGA_VOL_SHIFT 1 /* OUT2L_PGA_VOL - [7:1] */ +#define WM5100_OUT2L_PGA_VOL_WIDTH 7 /* OUT2L_PGA_VOL - [7:1] */ + +/* + * R1045 (0x415) - Out Volume 2R + */ +#define WM5100_OUT2R_ANC_SRC 0x0800 /* OUT2R_ANC_SRC */ +#define WM5100_OUT2R_ANC_SRC_MASK 0x0800 /* OUT2R_ANC_SRC */ +#define WM5100_OUT2R_ANC_SRC_SHIFT 11 /* OUT2R_ANC_SRC */ +#define WM5100_OUT2R_ANC_SRC_WIDTH 1 /* OUT2R_ANC_SRC */ +#define WM5100_OUT2R_PGA_VOL_MASK 0x00FE /* OUT2R_PGA_VOL - [7:1] */ +#define WM5100_OUT2R_PGA_VOL_SHIFT 1 /* OUT2R_PGA_VOL - [7:1] */ +#define WM5100_OUT2R_PGA_VOL_WIDTH 7 /* OUT2R_PGA_VOL - [7:1] */ + +/* + * R1046 (0x416) - DAC Volume Limit 2L + */ +#define WM5100_OUT2L_VOL_LIM_MASK 0x00FF /* OUT2L_VOL_LIM - [7:0] */ +#define WM5100_OUT2L_VOL_LIM_SHIFT 0 /* OUT2L_VOL_LIM - [7:0] */ +#define WM5100_OUT2L_VOL_LIM_WIDTH 8 /* OUT2L_VOL_LIM - [7:0] */ + +/* + * R1047 (0x417) - DAC Volume Limit 2R + */ +#define WM5100_OUT2R_VOL_LIM_MASK 0x00FF /* OUT2R_VOL_LIM - [7:0] */ +#define WM5100_OUT2R_VOL_LIM_SHIFT 0 /* OUT2R_VOL_LIM - [7:0] */ +#define WM5100_OUT2R_VOL_LIM_WIDTH 8 /* OUT2R_VOL_LIM - [7:0] */ + +/* + * R1048 (0x418) - Out Volume 3L + */ +#define WM5100_OUT3_OSR 0x2000 /* OUT3_OSR */ +#define WM5100_OUT3_OSR_MASK 0x2000 /* OUT3_OSR */ +#define WM5100_OUT3_OSR_SHIFT 13 /* OUT3_OSR */ +#define WM5100_OUT3_OSR_WIDTH 1 /* OUT3_OSR */ +#define WM5100_OUT3_MONO 0x1000 /* OUT3_MONO */ +#define WM5100_OUT3_MONO_MASK 0x1000 /* OUT3_MONO */ +#define WM5100_OUT3_MONO_SHIFT 12 /* OUT3_MONO */ +#define WM5100_OUT3_MONO_WIDTH 1 /* OUT3_MONO */ +#define WM5100_OUT3L_ANC_SRC 0x0800 /* OUT3L_ANC_SRC */ +#define WM5100_OUT3L_ANC_SRC_MASK 0x0800 /* OUT3L_ANC_SRC */ +#define WM5100_OUT3L_ANC_SRC_SHIFT 11 /* OUT3L_ANC_SRC */ +#define WM5100_OUT3L_ANC_SRC_WIDTH 1 /* OUT3L_ANC_SRC */ +#define WM5100_OUT3L_PGA_VOL_MASK 0x00FE /* OUT3L_PGA_VOL - [7:1] */ +#define WM5100_OUT3L_PGA_VOL_SHIFT 1 /* OUT3L_PGA_VOL - [7:1] */ +#define WM5100_OUT3L_PGA_VOL_WIDTH 7 /* OUT3L_PGA_VOL - [7:1] */ + +/* + * R1049 (0x419) - Out Volume 3R + */ +#define WM5100_OUT3R_ANC_SRC 0x0800 /* OUT3R_ANC_SRC */ +#define WM5100_OUT3R_ANC_SRC_MASK 0x0800 /* OUT3R_ANC_SRC */ +#define WM5100_OUT3R_ANC_SRC_SHIFT 11 /* OUT3R_ANC_SRC */ +#define WM5100_OUT3R_ANC_SRC_WIDTH 1 /* OUT3R_ANC_SRC */ +#define WM5100_OUT3R_PGA_VOL_MASK 0x00FE /* OUT3R_PGA_VOL - [7:1] */ +#define WM5100_OUT3R_PGA_VOL_SHIFT 1 /* OUT3R_PGA_VOL - [7:1] */ +#define WM5100_OUT3R_PGA_VOL_WIDTH 7 /* OUT3R_PGA_VOL - [7:1] */ + +/* + * R1050 (0x41A) - DAC Volume Limit 3L + */ +#define WM5100_OUT3L_VOL_LIM_MASK 0x00FF /* OUT3L_VOL_LIM - [7:0] */ +#define WM5100_OUT3L_VOL_LIM_SHIFT 0 /* OUT3L_VOL_LIM - [7:0] */ +#define WM5100_OUT3L_VOL_LIM_WIDTH 8 /* OUT3L_VOL_LIM - [7:0] */ + +/* + * R1051 (0x41B) - DAC Volume Limit 3R + */ +#define WM5100_OUT3R_VOL_LIM_MASK 0x00FF /* OUT3R_VOL_LIM - [7:0] */ +#define WM5100_OUT3R_VOL_LIM_SHIFT 0 /* OUT3R_VOL_LIM - [7:0] */ +#define WM5100_OUT3R_VOL_LIM_WIDTH 8 /* OUT3R_VOL_LIM - [7:0] */ + +/* + * R1052 (0x41C) - Out Volume 4L + */ +#define WM5100_OUT4_OSR 0x2000 /* OUT4_OSR */ +#define WM5100_OUT4_OSR_MASK 0x2000 /* OUT4_OSR */ +#define WM5100_OUT4_OSR_SHIFT 13 /* OUT4_OSR */ +#define WM5100_OUT4_OSR_WIDTH 1 /* OUT4_OSR */ +#define WM5100_OUT4L_ANC_SRC 0x0800 /* OUT4L_ANC_SRC */ +#define WM5100_OUT4L_ANC_SRC_MASK 0x0800 /* OUT4L_ANC_SRC */ +#define WM5100_OUT4L_ANC_SRC_SHIFT 11 /* OUT4L_ANC_SRC */ +#define WM5100_OUT4L_ANC_SRC_WIDTH 1 /* OUT4L_ANC_SRC */ +#define WM5100_OUT4L_VOL_LIM_MASK 0x00FF /* OUT4L_VOL_LIM - [7:0] */ +#define WM5100_OUT4L_VOL_LIM_SHIFT 0 /* OUT4L_VOL_LIM - [7:0] */ +#define WM5100_OUT4L_VOL_LIM_WIDTH 8 /* OUT4L_VOL_LIM - [7:0] */ + +/* + * R1053 (0x41D) - Out Volume 4R + */ +#define WM5100_OUT4R_ANC_SRC 0x0800 /* OUT4R_ANC_SRC */ +#define WM5100_OUT4R_ANC_SRC_MASK 0x0800 /* OUT4R_ANC_SRC */ +#define WM5100_OUT4R_ANC_SRC_SHIFT 11 /* OUT4R_ANC_SRC */ +#define WM5100_OUT4R_ANC_SRC_WIDTH 1 /* OUT4R_ANC_SRC */ +#define WM5100_OUT4R_VOL_LIM_MASK 0x00FF /* OUT4R_VOL_LIM - [7:0] */ +#define WM5100_OUT4R_VOL_LIM_SHIFT 0 /* OUT4R_VOL_LIM - [7:0] */ +#define WM5100_OUT4R_VOL_LIM_WIDTH 8 /* OUT4R_VOL_LIM - [7:0] */ + +/* + * R1054 (0x41E) - DAC Volume Limit 5L + */ +#define WM5100_OUT5_OSR 0x2000 /* OUT5_OSR */ +#define WM5100_OUT5_OSR_MASK 0x2000 /* OUT5_OSR */ +#define WM5100_OUT5_OSR_SHIFT 13 /* OUT5_OSR */ +#define WM5100_OUT5_OSR_WIDTH 1 /* OUT5_OSR */ +#define WM5100_OUT5L_ANC_SRC 0x0800 /* OUT5L_ANC_SRC */ +#define WM5100_OUT5L_ANC_SRC_MASK 0x0800 /* OUT5L_ANC_SRC */ +#define WM5100_OUT5L_ANC_SRC_SHIFT 11 /* OUT5L_ANC_SRC */ +#define WM5100_OUT5L_ANC_SRC_WIDTH 1 /* OUT5L_ANC_SRC */ +#define WM5100_OUT5L_VOL_LIM_MASK 0x00FF /* OUT5L_VOL_LIM - [7:0] */ +#define WM5100_OUT5L_VOL_LIM_SHIFT 0 /* OUT5L_VOL_LIM - [7:0] */ +#define WM5100_OUT5L_VOL_LIM_WIDTH 8 /* OUT5L_VOL_LIM - [7:0] */ + +/* + * R1055 (0x41F) - DAC Volume Limit 5R + */ +#define WM5100_OUT5R_ANC_SRC 0x0800 /* OUT5R_ANC_SRC */ +#define WM5100_OUT5R_ANC_SRC_MASK 0x0800 /* OUT5R_ANC_SRC */ +#define WM5100_OUT5R_ANC_SRC_SHIFT 11 /* OUT5R_ANC_SRC */ +#define WM5100_OUT5R_ANC_SRC_WIDTH 1 /* OUT5R_ANC_SRC */ +#define WM5100_OUT5R_VOL_LIM_MASK 0x00FF /* OUT5R_VOL_LIM - [7:0] */ +#define WM5100_OUT5R_VOL_LIM_SHIFT 0 /* OUT5R_VOL_LIM - [7:0] */ +#define WM5100_OUT5R_VOL_LIM_WIDTH 8 /* OUT5R_VOL_LIM - [7:0] */ + +/* + * R1056 (0x420) - DAC Volume Limit 6L + */ +#define WM5100_OUT6_OSR 0x2000 /* OUT6_OSR */ +#define WM5100_OUT6_OSR_MASK 0x2000 /* OUT6_OSR */ +#define WM5100_OUT6_OSR_SHIFT 13 /* OUT6_OSR */ +#define WM5100_OUT6_OSR_WIDTH 1 /* OUT6_OSR */ +#define WM5100_OUT6L_ANC_SRC 0x0800 /* OUT6L_ANC_SRC */ +#define WM5100_OUT6L_ANC_SRC_MASK 0x0800 /* OUT6L_ANC_SRC */ +#define WM5100_OUT6L_ANC_SRC_SHIFT 11 /* OUT6L_ANC_SRC */ +#define WM5100_OUT6L_ANC_SRC_WIDTH 1 /* OUT6L_ANC_SRC */ +#define WM5100_OUT6L_VOL_LIM_MASK 0x00FF /* OUT6L_VOL_LIM - [7:0] */ +#define WM5100_OUT6L_VOL_LIM_SHIFT 0 /* OUT6L_VOL_LIM - [7:0] */ +#define WM5100_OUT6L_VOL_LIM_WIDTH 8 /* OUT6L_VOL_LIM - [7:0] */ + +/* + * R1057 (0x421) - DAC Volume Limit 6R + */ +#define WM5100_OUT6R_ANC_SRC 0x0800 /* OUT6R_ANC_SRC */ +#define WM5100_OUT6R_ANC_SRC_MASK 0x0800 /* OUT6R_ANC_SRC */ +#define WM5100_OUT6R_ANC_SRC_SHIFT 11 /* OUT6R_ANC_SRC */ +#define WM5100_OUT6R_ANC_SRC_WIDTH 1 /* OUT6R_ANC_SRC */ +#define WM5100_OUT6R_VOL_LIM_MASK 0x00FF /* OUT6R_VOL_LIM - [7:0] */ +#define WM5100_OUT6R_VOL_LIM_SHIFT 0 /* OUT6R_VOL_LIM - [7:0] */ +#define WM5100_OUT6R_VOL_LIM_WIDTH 8 /* OUT6R_VOL_LIM - [7:0] */ + +/* + * R1088 (0x440) - DAC AEC Control 1 + */ +#define WM5100_AEC_LOOPBACK_SRC_MASK 0x003C /* AEC_LOOPBACK_SRC - [5:2] */ +#define WM5100_AEC_LOOPBACK_SRC_SHIFT 2 /* AEC_LOOPBACK_SRC - [5:2] */ +#define WM5100_AEC_LOOPBACK_SRC_WIDTH 4 /* AEC_LOOPBACK_SRC - [5:2] */ +#define WM5100_AEC_ENA_STS 0x0002 /* AEC_ENA_STS */ +#define WM5100_AEC_ENA_STS_MASK 0x0002 /* AEC_ENA_STS */ +#define WM5100_AEC_ENA_STS_SHIFT 1 /* AEC_ENA_STS */ +#define WM5100_AEC_ENA_STS_WIDTH 1 /* AEC_ENA_STS */ +#define WM5100_AEC_LOOPBACK_ENA 0x0001 /* AEC_LOOPBACK_ENA */ +#define WM5100_AEC_LOOPBACK_ENA_MASK 0x0001 /* AEC_LOOPBACK_ENA */ +#define WM5100_AEC_LOOPBACK_ENA_SHIFT 0 /* AEC_LOOPBACK_ENA */ +#define WM5100_AEC_LOOPBACK_ENA_WIDTH 1 /* AEC_LOOPBACK_ENA */ + +/* + * R1089 (0x441) - Output Volume Ramp + */ +#define WM5100_OUT_VD_RAMP_MASK 0x0070 /* OUT_VD_RAMP - [6:4] */ +#define WM5100_OUT_VD_RAMP_SHIFT 4 /* OUT_VD_RAMP - [6:4] */ +#define WM5100_OUT_VD_RAMP_WIDTH 3 /* OUT_VD_RAMP - [6:4] */ +#define WM5100_OUT_VI_RAMP_MASK 0x0007 /* OUT_VI_RAMP - [2:0] */ +#define WM5100_OUT_VI_RAMP_SHIFT 0 /* OUT_VI_RAMP - [2:0] */ +#define WM5100_OUT_VI_RAMP_WIDTH 3 /* OUT_VI_RAMP - [2:0] */ + +/* + * R1152 (0x480) - DAC Digital Volume 1L + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT1L_MUTE 0x0100 /* OUT1L_MUTE */ +#define WM5100_OUT1L_MUTE_MASK 0x0100 /* OUT1L_MUTE */ +#define WM5100_OUT1L_MUTE_SHIFT 8 /* OUT1L_MUTE */ +#define WM5100_OUT1L_MUTE_WIDTH 1 /* OUT1L_MUTE */ +#define WM5100_OUT1L_VOL_MASK 0x00FF /* OUT1L_VOL - [7:0] */ +#define WM5100_OUT1L_VOL_SHIFT 0 /* OUT1L_VOL - [7:0] */ +#define WM5100_OUT1L_VOL_WIDTH 8 /* OUT1L_VOL - [7:0] */ + +/* + * R1153 (0x481) - DAC Digital Volume 1R + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT1R_MUTE 0x0100 /* OUT1R_MUTE */ +#define WM5100_OUT1R_MUTE_MASK 0x0100 /* OUT1R_MUTE */ +#define WM5100_OUT1R_MUTE_SHIFT 8 /* OUT1R_MUTE */ +#define WM5100_OUT1R_MUTE_WIDTH 1 /* OUT1R_MUTE */ +#define WM5100_OUT1R_VOL_MASK 0x00FF /* OUT1R_VOL - [7:0] */ +#define WM5100_OUT1R_VOL_SHIFT 0 /* OUT1R_VOL - [7:0] */ +#define WM5100_OUT1R_VOL_WIDTH 8 /* OUT1R_VOL - [7:0] */ + +/* + * R1154 (0x482) - DAC Digital Volume 2L + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT2L_MUTE 0x0100 /* OUT2L_MUTE */ +#define WM5100_OUT2L_MUTE_MASK 0x0100 /* OUT2L_MUTE */ +#define WM5100_OUT2L_MUTE_SHIFT 8 /* OUT2L_MUTE */ +#define WM5100_OUT2L_MUTE_WIDTH 1 /* OUT2L_MUTE */ +#define WM5100_OUT2L_VOL_MASK 0x00FF /* OUT2L_VOL - [7:0] */ +#define WM5100_OUT2L_VOL_SHIFT 0 /* OUT2L_VOL - [7:0] */ +#define WM5100_OUT2L_VOL_WIDTH 8 /* OUT2L_VOL - [7:0] */ + +/* + * R1155 (0x483) - DAC Digital Volume 2R + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT2R_MUTE 0x0100 /* OUT2R_MUTE */ +#define WM5100_OUT2R_MUTE_MASK 0x0100 /* OUT2R_MUTE */ +#define WM5100_OUT2R_MUTE_SHIFT 8 /* OUT2R_MUTE */ +#define WM5100_OUT2R_MUTE_WIDTH 1 /* OUT2R_MUTE */ +#define WM5100_OUT2R_VOL_MASK 0x00FF /* OUT2R_VOL - [7:0] */ +#define WM5100_OUT2R_VOL_SHIFT 0 /* OUT2R_VOL - [7:0] */ +#define WM5100_OUT2R_VOL_WIDTH 8 /* OUT2R_VOL - [7:0] */ + +/* + * R1156 (0x484) - DAC Digital Volume 3L + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT3L_MUTE 0x0100 /* OUT3L_MUTE */ +#define WM5100_OUT3L_MUTE_MASK 0x0100 /* OUT3L_MUTE */ +#define WM5100_OUT3L_MUTE_SHIFT 8 /* OUT3L_MUTE */ +#define WM5100_OUT3L_MUTE_WIDTH 1 /* OUT3L_MUTE */ +#define WM5100_OUT3L_VOL_MASK 0x00FF /* OUT3L_VOL - [7:0] */ +#define WM5100_OUT3L_VOL_SHIFT 0 /* OUT3L_VOL - [7:0] */ +#define WM5100_OUT3L_VOL_WIDTH 8 /* OUT3L_VOL - [7:0] */ + +/* + * R1157 (0x485) - DAC Digital Volume 3R + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT3R_MUTE 0x0100 /* OUT3R_MUTE */ +#define WM5100_OUT3R_MUTE_MASK 0x0100 /* OUT3R_MUTE */ +#define WM5100_OUT3R_MUTE_SHIFT 8 /* OUT3R_MUTE */ +#define WM5100_OUT3R_MUTE_WIDTH 1 /* OUT3R_MUTE */ +#define WM5100_OUT3R_VOL_MASK 0x00FF /* OUT3R_VOL - [7:0] */ +#define WM5100_OUT3R_VOL_SHIFT 0 /* OUT3R_VOL - [7:0] */ +#define WM5100_OUT3R_VOL_WIDTH 8 /* OUT3R_VOL - [7:0] */ + +/* + * R1158 (0x486) - DAC Digital Volume 4L + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT4L_MUTE 0x0100 /* OUT4L_MUTE */ +#define WM5100_OUT4L_MUTE_MASK 0x0100 /* OUT4L_MUTE */ +#define WM5100_OUT4L_MUTE_SHIFT 8 /* OUT4L_MUTE */ +#define WM5100_OUT4L_MUTE_WIDTH 1 /* OUT4L_MUTE */ +#define WM5100_OUT4L_VOL_MASK 0x00FF /* OUT4L_VOL - [7:0] */ +#define WM5100_OUT4L_VOL_SHIFT 0 /* OUT4L_VOL - [7:0] */ +#define WM5100_OUT4L_VOL_WIDTH 8 /* OUT4L_VOL - [7:0] */ + +/* + * R1159 (0x487) - DAC Digital Volume 4R + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT4R_MUTE 0x0100 /* OUT4R_MUTE */ +#define WM5100_OUT4R_MUTE_MASK 0x0100 /* OUT4R_MUTE */ +#define WM5100_OUT4R_MUTE_SHIFT 8 /* OUT4R_MUTE */ +#define WM5100_OUT4R_MUTE_WIDTH 1 /* OUT4R_MUTE */ +#define WM5100_OUT4R_VOL_MASK 0x00FF /* OUT4R_VOL - [7:0] */ +#define WM5100_OUT4R_VOL_SHIFT 0 /* OUT4R_VOL - [7:0] */ +#define WM5100_OUT4R_VOL_WIDTH 8 /* OUT4R_VOL - [7:0] */ + +/* + * R1160 (0x488) - DAC Digital Volume 5L + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT5L_MUTE 0x0100 /* OUT5L_MUTE */ +#define WM5100_OUT5L_MUTE_MASK 0x0100 /* OUT5L_MUTE */ +#define WM5100_OUT5L_MUTE_SHIFT 8 /* OUT5L_MUTE */ +#define WM5100_OUT5L_MUTE_WIDTH 1 /* OUT5L_MUTE */ +#define WM5100_OUT5L_VOL_MASK 0x00FF /* OUT5L_VOL - [7:0] */ +#define WM5100_OUT5L_VOL_SHIFT 0 /* OUT5L_VOL - [7:0] */ +#define WM5100_OUT5L_VOL_WIDTH 8 /* OUT5L_VOL - [7:0] */ + +/* + * R1161 (0x489) - DAC Digital Volume 5R + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT5R_MUTE 0x0100 /* OUT5R_MUTE */ +#define WM5100_OUT5R_MUTE_MASK 0x0100 /* OUT5R_MUTE */ +#define WM5100_OUT5R_MUTE_SHIFT 8 /* OUT5R_MUTE */ +#define WM5100_OUT5R_MUTE_WIDTH 1 /* OUT5R_MUTE */ +#define WM5100_OUT5R_VOL_MASK 0x00FF /* OUT5R_VOL - [7:0] */ +#define WM5100_OUT5R_VOL_SHIFT 0 /* OUT5R_VOL - [7:0] */ +#define WM5100_OUT5R_VOL_WIDTH 8 /* OUT5R_VOL - [7:0] */ + +/* + * R1162 (0x48A) - DAC Digital Volume 6L + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT6L_MUTE 0x0100 /* OUT6L_MUTE */ +#define WM5100_OUT6L_MUTE_MASK 0x0100 /* OUT6L_MUTE */ +#define WM5100_OUT6L_MUTE_SHIFT 8 /* OUT6L_MUTE */ +#define WM5100_OUT6L_MUTE_WIDTH 1 /* OUT6L_MUTE */ +#define WM5100_OUT6L_VOL_MASK 0x00FF /* OUT6L_VOL - [7:0] */ +#define WM5100_OUT6L_VOL_SHIFT 0 /* OUT6L_VOL - [7:0] */ +#define WM5100_OUT6L_VOL_WIDTH 8 /* OUT6L_VOL - [7:0] */ + +/* + * R1163 (0x48B) - DAC Digital Volume 6R + */ +#define WM5100_OUT_VU 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_MASK 0x0200 /* OUT_VU */ +#define WM5100_OUT_VU_SHIFT 9 /* OUT_VU */ +#define WM5100_OUT_VU_WIDTH 1 /* OUT_VU */ +#define WM5100_OUT6R_MUTE 0x0100 /* OUT6R_MUTE */ +#define WM5100_OUT6R_MUTE_MASK 0x0100 /* OUT6R_MUTE */ +#define WM5100_OUT6R_MUTE_SHIFT 8 /* OUT6R_MUTE */ +#define WM5100_OUT6R_MUTE_WIDTH 1 /* OUT6R_MUTE */ +#define WM5100_OUT6R_VOL_MASK 0x00FF /* OUT6R_VOL - [7:0] */ +#define WM5100_OUT6R_VOL_SHIFT 0 /* OUT6R_VOL - [7:0] */ +#define WM5100_OUT6R_VOL_WIDTH 8 /* OUT6R_VOL - [7:0] */ + +/* + * R1216 (0x4C0) - PDM SPK1 CTRL 1 + */ +#define WM5100_SPK1R_MUTE 0x2000 /* SPK1R_MUTE */ +#define WM5100_SPK1R_MUTE_MASK 0x2000 /* SPK1R_MUTE */ +#define WM5100_SPK1R_MUTE_SHIFT 13 /* SPK1R_MUTE */ +#define WM5100_SPK1R_MUTE_WIDTH 1 /* SPK1R_MUTE */ +#define WM5100_SPK1L_MUTE 0x1000 /* SPK1L_MUTE */ +#define WM5100_SPK1L_MUTE_MASK 0x1000 /* SPK1L_MUTE */ +#define WM5100_SPK1L_MUTE_SHIFT 12 /* SPK1L_MUTE */ +#define WM5100_SPK1L_MUTE_WIDTH 1 /* SPK1L_MUTE */ +#define WM5100_SPK1_MUTE_ENDIAN 0x0100 /* SPK1_MUTE_ENDIAN */ +#define WM5100_SPK1_MUTE_ENDIAN_MASK 0x0100 /* SPK1_MUTE_ENDIAN */ +#define WM5100_SPK1_MUTE_ENDIAN_SHIFT 8 /* SPK1_MUTE_ENDIAN */ +#define WM5100_SPK1_MUTE_ENDIAN_WIDTH 1 /* SPK1_MUTE_ENDIAN */ +#define WM5100_SPK1_MUTE_SEQ1_MASK 0x00FF /* SPK1_MUTE_SEQ1 - [7:0] */ +#define WM5100_SPK1_MUTE_SEQ1_SHIFT 0 /* SPK1_MUTE_SEQ1 - [7:0] */ +#define WM5100_SPK1_MUTE_SEQ1_WIDTH 8 /* SPK1_MUTE_SEQ1 - [7:0] */ + +/* + * R1217 (0x4C1) - PDM SPK1 CTRL 2 + */ +#define WM5100_SPK1_FMT 0x0001 /* SPK1_FMT */ +#define WM5100_SPK1_FMT_MASK 0x0001 /* SPK1_FMT */ +#define WM5100_SPK1_FMT_SHIFT 0 /* SPK1_FMT */ +#define WM5100_SPK1_FMT_WIDTH 1 /* SPK1_FMT */ + +/* + * R1218 (0x4C2) - PDM SPK2 CTRL 1 + */ +#define WM5100_SPK2R_MUTE 0x2000 /* SPK2R_MUTE */ +#define WM5100_SPK2R_MUTE_MASK 0x2000 /* SPK2R_MUTE */ +#define WM5100_SPK2R_MUTE_SHIFT 13 /* SPK2R_MUTE */ +#define WM5100_SPK2R_MUTE_WIDTH 1 /* SPK2R_MUTE */ +#define WM5100_SPK2L_MUTE 0x1000 /* SPK2L_MUTE */ +#define WM5100_SPK2L_MUTE_MASK 0x1000 /* SPK2L_MUTE */ +#define WM5100_SPK2L_MUTE_SHIFT 12 /* SPK2L_MUTE */ +#define WM5100_SPK2L_MUTE_WIDTH 1 /* SPK2L_MUTE */ +#define WM5100_SPK2_MUTE_ENDIAN 0x0100 /* SPK2_MUTE_ENDIAN */ +#define WM5100_SPK2_MUTE_ENDIAN_MASK 0x0100 /* SPK2_MUTE_ENDIAN */ +#define WM5100_SPK2_MUTE_ENDIAN_SHIFT 8 /* SPK2_MUTE_ENDIAN */ +#define WM5100_SPK2_MUTE_ENDIAN_WIDTH 1 /* SPK2_MUTE_ENDIAN */ +#define WM5100_SPK2_MUTE_SEQ1_MASK 0x00FF /* SPK2_MUTE_SEQ1 - [7:0] */ +#define WM5100_SPK2_MUTE_SEQ1_SHIFT 0 /* SPK2_MUTE_SEQ1 - [7:0] */ +#define WM5100_SPK2_MUTE_SEQ1_WIDTH 8 /* SPK2_MUTE_SEQ1 - [7:0] */ + +/* + * R1219 (0x4C3) - PDM SPK2 CTRL 2 + */ +#define WM5100_SPK2_FMT 0x0001 /* SPK2_FMT */ +#define WM5100_SPK2_FMT_MASK 0x0001 /* SPK2_FMT */ +#define WM5100_SPK2_FMT_SHIFT 0 /* SPK2_FMT */ +#define WM5100_SPK2_FMT_WIDTH 1 /* SPK2_FMT */ + +/* + * R1280 (0x500) - Audio IF 1_1 + */ +#define WM5100_AIF1_BCLK_INV 0x0080 /* AIF1_BCLK_INV */ +#define WM5100_AIF1_BCLK_INV_MASK 0x0080 /* AIF1_BCLK_INV */ +#define WM5100_AIF1_BCLK_INV_SHIFT 7 /* AIF1_BCLK_INV */ +#define WM5100_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */ +#define WM5100_AIF1_BCLK_FRC 0x0040 /* AIF1_BCLK_FRC */ +#define WM5100_AIF1_BCLK_FRC_MASK 0x0040 /* AIF1_BCLK_FRC */ +#define WM5100_AIF1_BCLK_FRC_SHIFT 6 /* AIF1_BCLK_FRC */ +#define WM5100_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */ +#define WM5100_AIF1_BCLK_MSTR 0x0020 /* AIF1_BCLK_MSTR */ +#define WM5100_AIF1_BCLK_MSTR_MASK 0x0020 /* AIF1_BCLK_MSTR */ +#define WM5100_AIF1_BCLK_MSTR_SHIFT 5 /* AIF1_BCLK_MSTR */ +#define WM5100_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */ +#define WM5100_AIF1_BCLK_FREQ_MASK 0x001F /* AIF1_BCLK_FREQ - [4:0] */ +#define WM5100_AIF1_BCLK_FREQ_SHIFT 0 /* AIF1_BCLK_FREQ - [4:0] */ +#define WM5100_AIF1_BCLK_FREQ_WIDTH 5 /* AIF1_BCLK_FREQ - [4:0] */ + +/* + * R1281 (0x501) - Audio IF 1_2 + */ +#define WM5100_AIF1TX_DAT_TRI 0x0020 /* AIF1TX_DAT_TRI */ +#define WM5100_AIF1TX_DAT_TRI_MASK 0x0020 /* AIF1TX_DAT_TRI */ +#define WM5100_AIF1TX_DAT_TRI_SHIFT 5 /* AIF1TX_DAT_TRI */ +#define WM5100_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */ +#define WM5100_AIF1TX_LRCLK_SRC 0x0008 /* AIF1TX_LRCLK_SRC */ +#define WM5100_AIF1TX_LRCLK_SRC_MASK 0x0008 /* AIF1TX_LRCLK_SRC */ +#define WM5100_AIF1TX_LRCLK_SRC_SHIFT 3 /* AIF1TX_LRCLK_SRC */ +#define WM5100_AIF1TX_LRCLK_SRC_WIDTH 1 /* AIF1TX_LRCLK_SRC */ +#define WM5100_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */ +#define WM5100_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */ +#define WM5100_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */ +#define WM5100_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */ +#define WM5100_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */ +#define WM5100_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */ +#define WM5100_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */ +#define WM5100_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */ +#define WM5100_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */ +#define WM5100_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */ +#define WM5100_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */ +#define WM5100_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */ + +/* + * R1282 (0x502) - Audio IF 1_3 + */ +#define WM5100_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */ +#define WM5100_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */ +#define WM5100_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */ +#define WM5100_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */ +#define WM5100_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */ +#define WM5100_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */ +#define WM5100_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */ +#define WM5100_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */ +#define WM5100_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */ +#define WM5100_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */ +#define WM5100_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */ +#define WM5100_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */ + +/* + * R1283 (0x503) - Audio IF 1_4 + */ +#define WM5100_AIF1_TRI 0x0040 /* AIF1_TRI */ +#define WM5100_AIF1_TRI_MASK 0x0040 /* AIF1_TRI */ +#define WM5100_AIF1_TRI_SHIFT 6 /* AIF1_TRI */ +#define WM5100_AIF1_TRI_WIDTH 1 /* AIF1_TRI */ +#define WM5100_AIF1_RATE_MASK 0x0003 /* AIF1_RATE - [1:0] */ +#define WM5100_AIF1_RATE_SHIFT 0 /* AIF1_RATE - [1:0] */ +#define WM5100_AIF1_RATE_WIDTH 2 /* AIF1_RATE - [1:0] */ + +/* + * R1284 (0x504) - Audio IF 1_5 + */ +#define WM5100_AIF1_FMT_MASK 0x0007 /* AIF1_FMT - [2:0] */ +#define WM5100_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [2:0] */ +#define WM5100_AIF1_FMT_WIDTH 3 /* AIF1_FMT - [2:0] */ + +/* + * R1285 (0x505) - Audio IF 1_6 + */ +#define WM5100_AIF1TX_BCPF_MASK 0x1FFF /* AIF1TX_BCPF - [12:0] */ +#define WM5100_AIF1TX_BCPF_SHIFT 0 /* AIF1TX_BCPF - [12:0] */ +#define WM5100_AIF1TX_BCPF_WIDTH 13 /* AIF1TX_BCPF - [12:0] */ + +/* + * R1286 (0x506) - Audio IF 1_7 + */ +#define WM5100_AIF1RX_BCPF_MASK 0x1FFF /* AIF1RX_BCPF - [12:0] */ +#define WM5100_AIF1RX_BCPF_SHIFT 0 /* AIF1RX_BCPF - [12:0] */ +#define WM5100_AIF1RX_BCPF_WIDTH 13 /* AIF1RX_BCPF - [12:0] */ + +/* + * R1287 (0x507) - Audio IF 1_8 + */ +#define WM5100_AIF1TX_WL_MASK 0x3F00 /* AIF1TX_WL - [13:8] */ +#define WM5100_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [13:8] */ +#define WM5100_AIF1TX_WL_WIDTH 6 /* AIF1TX_WL - [13:8] */ +#define WM5100_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */ +#define WM5100_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */ +#define WM5100_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */ + +/* + * R1288 (0x508) - Audio IF 1_9 + */ +#define WM5100_AIF1RX_WL_MASK 0x3F00 /* AIF1RX_WL - [13:8] */ +#define WM5100_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [13:8] */ +#define WM5100_AIF1RX_WL_WIDTH 6 /* AIF1RX_WL - [13:8] */ +#define WM5100_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */ +#define WM5100_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */ +#define WM5100_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */ + +/* + * R1289 (0x509) - Audio IF 1_10 + */ +#define WM5100_AIF1TX1_SLOT_MASK 0x003F /* AIF1TX1_SLOT - [5:0] */ +#define WM5100_AIF1TX1_SLOT_SHIFT 0 /* AIF1TX1_SLOT - [5:0] */ +#define WM5100_AIF1TX1_SLOT_WIDTH 6 /* AIF1TX1_SLOT - [5:0] */ + +/* + * R1290 (0x50A) - Audio IF 1_11 + */ +#define WM5100_AIF1TX2_SLOT_MASK 0x003F /* AIF1TX2_SLOT - [5:0] */ +#define WM5100_AIF1TX2_SLOT_SHIFT 0 /* AIF1TX2_SLOT - [5:0] */ +#define WM5100_AIF1TX2_SLOT_WIDTH 6 /* AIF1TX2_SLOT - [5:0] */ + +/* + * R1291 (0x50B) - Audio IF 1_12 + */ +#define WM5100_AIF1TX3_SLOT_MASK 0x003F /* AIF1TX3_SLOT - [5:0] */ +#define WM5100_AIF1TX3_SLOT_SHIFT 0 /* AIF1TX3_SLOT - [5:0] */ +#define WM5100_AIF1TX3_SLOT_WIDTH 6 /* AIF1TX3_SLOT - [5:0] */ + +/* + * R1292 (0x50C) - Audio IF 1_13 + */ +#define WM5100_AIF1TX4_SLOT_MASK 0x003F /* AIF1TX4_SLOT - [5:0] */ +#define WM5100_AIF1TX4_SLOT_SHIFT 0 /* AIF1TX4_SLOT - [5:0] */ +#define WM5100_AIF1TX4_SLOT_WIDTH 6 /* AIF1TX4_SLOT - [5:0] */ + +/* + * R1293 (0x50D) - Audio IF 1_14 + */ +#define WM5100_AIF1TX5_SLOT_MASK 0x003F /* AIF1TX5_SLOT - [5:0] */ +#define WM5100_AIF1TX5_SLOT_SHIFT 0 /* AIF1TX5_SLOT - [5:0] */ +#define WM5100_AIF1TX5_SLOT_WIDTH 6 /* AIF1TX5_SLOT - [5:0] */ + +/* + * R1294 (0x50E) - Audio IF 1_15 + */ +#define WM5100_AIF1TX6_SLOT_MASK 0x003F /* AIF1TX6_SLOT - [5:0] */ +#define WM5100_AIF1TX6_SLOT_SHIFT 0 /* AIF1TX6_SLOT - [5:0] */ +#define WM5100_AIF1TX6_SLOT_WIDTH 6 /* AIF1TX6_SLOT - [5:0] */ + +/* + * R1295 (0x50F) - Audio IF 1_16 + */ +#define WM5100_AIF1TX7_SLOT_MASK 0x003F /* AIF1TX7_SLOT - [5:0] */ +#define WM5100_AIF1TX7_SLOT_SHIFT 0 /* AIF1TX7_SLOT - [5:0] */ +#define WM5100_AIF1TX7_SLOT_WIDTH 6 /* AIF1TX7_SLOT - [5:0] */ + +/* + * R1296 (0x510) - Audio IF 1_17 + */ +#define WM5100_AIF1TX8_SLOT_MASK 0x003F /* AIF1TX8_SLOT - [5:0] */ +#define WM5100_AIF1TX8_SLOT_SHIFT 0 /* AIF1TX8_SLOT - [5:0] */ +#define WM5100_AIF1TX8_SLOT_WIDTH 6 /* AIF1TX8_SLOT - [5:0] */ + +/* + * R1297 (0x511) - Audio IF 1_18 + */ +#define WM5100_AIF1RX1_SLOT_MASK 0x003F /* AIF1RX1_SLOT - [5:0] */ +#define WM5100_AIF1RX1_SLOT_SHIFT 0 /* AIF1RX1_SLOT - [5:0] */ +#define WM5100_AIF1RX1_SLOT_WIDTH 6 /* AIF1RX1_SLOT - [5:0] */ + +/* + * R1298 (0x512) - Audio IF 1_19 + */ +#define WM5100_AIF1RX2_SLOT_MASK 0x003F /* AIF1RX2_SLOT - [5:0] */ +#define WM5100_AIF1RX2_SLOT_SHIFT 0 /* AIF1RX2_SLOT - [5:0] */ +#define WM5100_AIF1RX2_SLOT_WIDTH 6 /* AIF1RX2_SLOT - [5:0] */ + +/* + * R1299 (0x513) - Audio IF 1_20 + */ +#define WM5100_AIF1RX3_SLOT_MASK 0x003F /* AIF1RX3_SLOT - [5:0] */ +#define WM5100_AIF1RX3_SLOT_SHIFT 0 /* AIF1RX3_SLOT - [5:0] */ +#define WM5100_AIF1RX3_SLOT_WIDTH 6 /* AIF1RX3_SLOT - [5:0] */ + +/* + * R1300 (0x514) - Audio IF 1_21 + */ +#define WM5100_AIF1RX4_SLOT_MASK 0x003F /* AIF1RX4_SLOT - [5:0] */ +#define WM5100_AIF1RX4_SLOT_SHIFT 0 /* AIF1RX4_SLOT - [5:0] */ +#define WM5100_AIF1RX4_SLOT_WIDTH 6 /* AIF1RX4_SLOT - [5:0] */ + +/* + * R1301 (0x515) - Audio IF 1_22 + */ +#define WM5100_AIF1RX5_SLOT_MASK 0x003F /* AIF1RX5_SLOT - [5:0] */ +#define WM5100_AIF1RX5_SLOT_SHIFT 0 /* AIF1RX5_SLOT - [5:0] */ +#define WM5100_AIF1RX5_SLOT_WIDTH 6 /* AIF1RX5_SLOT - [5:0] */ + +/* + * R1302 (0x516) - Audio IF 1_23 + */ +#define WM5100_AIF1RX6_SLOT_MASK 0x003F /* AIF1RX6_SLOT - [5:0] */ +#define WM5100_AIF1RX6_SLOT_SHIFT 0 /* AIF1RX6_SLOT - [5:0] */ +#define WM5100_AIF1RX6_SLOT_WIDTH 6 /* AIF1RX6_SLOT - [5:0] */ + +/* + * R1303 (0x517) - Audio IF 1_24 + */ +#define WM5100_AIF1RX7_SLOT_MASK 0x003F /* AIF1RX7_SLOT - [5:0] */ +#define WM5100_AIF1RX7_SLOT_SHIFT 0 /* AIF1RX7_SLOT - [5:0] */ +#define WM5100_AIF1RX7_SLOT_WIDTH 6 /* AIF1RX7_SLOT - [5:0] */ + +/* + * R1304 (0x518) - Audio IF 1_25 + */ +#define WM5100_AIF1RX8_SLOT_MASK 0x003F /* AIF1RX8_SLOT - [5:0] */ +#define WM5100_AIF1RX8_SLOT_SHIFT 0 /* AIF1RX8_SLOT - [5:0] */ +#define WM5100_AIF1RX8_SLOT_WIDTH 6 /* AIF1RX8_SLOT - [5:0] */ + +/* + * R1305 (0x519) - Audio IF 1_26 + */ +#define WM5100_AIF1TX8_ENA 0x0080 /* AIF1TX8_ENA */ +#define WM5100_AIF1TX8_ENA_MASK 0x0080 /* AIF1TX8_ENA */ +#define WM5100_AIF1TX8_ENA_SHIFT 7 /* AIF1TX8_ENA */ +#define WM5100_AIF1TX8_ENA_WIDTH 1 /* AIF1TX8_ENA */ +#define WM5100_AIF1TX7_ENA 0x0040 /* AIF1TX7_ENA */ +#define WM5100_AIF1TX7_ENA_MASK 0x0040 /* AIF1TX7_ENA */ +#define WM5100_AIF1TX7_ENA_SHIFT 6 /* AIF1TX7_ENA */ +#define WM5100_AIF1TX7_ENA_WIDTH 1 /* AIF1TX7_ENA */ +#define WM5100_AIF1TX6_ENA 0x0020 /* AIF1TX6_ENA */ +#define WM5100_AIF1TX6_ENA_MASK 0x0020 /* AIF1TX6_ENA */ +#define WM5100_AIF1TX6_ENA_SHIFT 5 /* AIF1TX6_ENA */ +#define WM5100_AIF1TX6_ENA_WIDTH 1 /* AIF1TX6_ENA */ +#define WM5100_AIF1TX5_ENA 0x0010 /* AIF1TX5_ENA */ +#define WM5100_AIF1TX5_ENA_MASK 0x0010 /* AIF1TX5_ENA */ +#define WM5100_AIF1TX5_ENA_SHIFT 4 /* AIF1TX5_ENA */ +#define WM5100_AIF1TX5_ENA_WIDTH 1 /* AIF1TX5_ENA */ +#define WM5100_AIF1TX4_ENA 0x0008 /* AIF1TX4_ENA */ +#define WM5100_AIF1TX4_ENA_MASK 0x0008 /* AIF1TX4_ENA */ +#define WM5100_AIF1TX4_ENA_SHIFT 3 /* AIF1TX4_ENA */ +#define WM5100_AIF1TX4_ENA_WIDTH 1 /* AIF1TX4_ENA */ +#define WM5100_AIF1TX3_ENA 0x0004 /* AIF1TX3_ENA */ +#define WM5100_AIF1TX3_ENA_MASK 0x0004 /* AIF1TX3_ENA */ +#define WM5100_AIF1TX3_ENA_SHIFT 2 /* AIF1TX3_ENA */ +#define WM5100_AIF1TX3_ENA_WIDTH 1 /* AIF1TX3_ENA */ +#define WM5100_AIF1TX2_ENA 0x0002 /* AIF1TX2_ENA */ +#define WM5100_AIF1TX2_ENA_MASK 0x0002 /* AIF1TX2_ENA */ +#define WM5100_AIF1TX2_ENA_SHIFT 1 /* AIF1TX2_ENA */ +#define WM5100_AIF1TX2_ENA_WIDTH 1 /* AIF1TX2_ENA */ +#define WM5100_AIF1TX1_ENA 0x0001 /* AIF1TX1_ENA */ +#define WM5100_AIF1TX1_ENA_MASK 0x0001 /* AIF1TX1_ENA */ +#define WM5100_AIF1TX1_ENA_SHIFT 0 /* AIF1TX1_ENA */ +#define WM5100_AIF1TX1_ENA_WIDTH 1 /* AIF1TX1_ENA */ + +/* + * R1306 (0x51A) - Audio IF 1_27 + */ +#define WM5100_AIF1RX8_ENA 0x0080 /* AIF1RX8_ENA */ +#define WM5100_AIF1RX8_ENA_MASK 0x0080 /* AIF1RX8_ENA */ +#define WM5100_AIF1RX8_ENA_SHIFT 7 /* AIF1RX8_ENA */ +#define WM5100_AIF1RX8_ENA_WIDTH 1 /* AIF1RX8_ENA */ +#define WM5100_AIF1RX7_ENA 0x0040 /* AIF1RX7_ENA */ +#define WM5100_AIF1RX7_ENA_MASK 0x0040 /* AIF1RX7_ENA */ +#define WM5100_AIF1RX7_ENA_SHIFT 6 /* AIF1RX7_ENA */ +#define WM5100_AIF1RX7_ENA_WIDTH 1 /* AIF1RX7_ENA */ +#define WM5100_AIF1RX6_ENA 0x0020 /* AIF1RX6_ENA */ +#define WM5100_AIF1RX6_ENA_MASK 0x0020 /* AIF1RX6_ENA */ +#define WM5100_AIF1RX6_ENA_SHIFT 5 /* AIF1RX6_ENA */ +#define WM5100_AIF1RX6_ENA_WIDTH 1 /* AIF1RX6_ENA */ +#define WM5100_AIF1RX5_ENA 0x0010 /* AIF1RX5_ENA */ +#define WM5100_AIF1RX5_ENA_MASK 0x0010 /* AIF1RX5_ENA */ +#define WM5100_AIF1RX5_ENA_SHIFT 4 /* AIF1RX5_ENA */ +#define WM5100_AIF1RX5_ENA_WIDTH 1 /* AIF1RX5_ENA */ +#define WM5100_AIF1RX4_ENA 0x0008 /* AIF1RX4_ENA */ +#define WM5100_AIF1RX4_ENA_MASK 0x0008 /* AIF1RX4_ENA */ +#define WM5100_AIF1RX4_ENA_SHIFT 3 /* AIF1RX4_ENA */ +#define WM5100_AIF1RX4_ENA_WIDTH 1 /* AIF1RX4_ENA */ +#define WM5100_AIF1RX3_ENA 0x0004 /* AIF1RX3_ENA */ +#define WM5100_AIF1RX3_ENA_MASK 0x0004 /* AIF1RX3_ENA */ +#define WM5100_AIF1RX3_ENA_SHIFT 2 /* AIF1RX3_ENA */ +#define WM5100_AIF1RX3_ENA_WIDTH 1 /* AIF1RX3_ENA */ +#define WM5100_AIF1RX2_ENA 0x0002 /* AIF1RX2_ENA */ +#define WM5100_AIF1RX2_ENA_MASK 0x0002 /* AIF1RX2_ENA */ +#define WM5100_AIF1RX2_ENA_SHIFT 1 /* AIF1RX2_ENA */ +#define WM5100_AIF1RX2_ENA_WIDTH 1 /* AIF1RX2_ENA */ +#define WM5100_AIF1RX1_ENA 0x0001 /* AIF1RX1_ENA */ +#define WM5100_AIF1RX1_ENA_MASK 0x0001 /* AIF1RX1_ENA */ +#define WM5100_AIF1RX1_ENA_SHIFT 0 /* AIF1RX1_ENA */ +#define WM5100_AIF1RX1_ENA_WIDTH 1 /* AIF1RX1_ENA */ + +/* + * R1344 (0x540) - Audio IF 2_1 + */ +#define WM5100_AIF2_BCLK_INV 0x0080 /* AIF2_BCLK_INV */ +#define WM5100_AIF2_BCLK_INV_MASK 0x0080 /* AIF2_BCLK_INV */ +#define WM5100_AIF2_BCLK_INV_SHIFT 7 /* AIF2_BCLK_INV */ +#define WM5100_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */ +#define WM5100_AIF2_BCLK_FRC 0x0040 /* AIF2_BCLK_FRC */ +#define WM5100_AIF2_BCLK_FRC_MASK 0x0040 /* AIF2_BCLK_FRC */ +#define WM5100_AIF2_BCLK_FRC_SHIFT 6 /* AIF2_BCLK_FRC */ +#define WM5100_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */ +#define WM5100_AIF2_BCLK_MSTR 0x0020 /* AIF2_BCLK_MSTR */ +#define WM5100_AIF2_BCLK_MSTR_MASK 0x0020 /* AIF2_BCLK_MSTR */ +#define WM5100_AIF2_BCLK_MSTR_SHIFT 5 /* AIF2_BCLK_MSTR */ +#define WM5100_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */ +#define WM5100_AIF2_BCLK_FREQ_MASK 0x001F /* AIF2_BCLK_FREQ - [4:0] */ +#define WM5100_AIF2_BCLK_FREQ_SHIFT 0 /* AIF2_BCLK_FREQ - [4:0] */ +#define WM5100_AIF2_BCLK_FREQ_WIDTH 5 /* AIF2_BCLK_FREQ - [4:0] */ + +/* + * R1345 (0x541) - Audio IF 2_2 + */ +#define WM5100_AIF2TX_DAT_TRI 0x0020 /* AIF2TX_DAT_TRI */ +#define WM5100_AIF2TX_DAT_TRI_MASK 0x0020 /* AIF2TX_DAT_TRI */ +#define WM5100_AIF2TX_DAT_TRI_SHIFT 5 /* AIF2TX_DAT_TRI */ +#define WM5100_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */ +#define WM5100_AIF2TX_LRCLK_SRC 0x0008 /* AIF2TX_LRCLK_SRC */ +#define WM5100_AIF2TX_LRCLK_SRC_MASK 0x0008 /* AIF2TX_LRCLK_SRC */ +#define WM5100_AIF2TX_LRCLK_SRC_SHIFT 3 /* AIF2TX_LRCLK_SRC */ +#define WM5100_AIF2TX_LRCLK_SRC_WIDTH 1 /* AIF2TX_LRCLK_SRC */ +#define WM5100_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */ +#define WM5100_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */ +#define WM5100_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */ +#define WM5100_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */ +#define WM5100_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */ +#define WM5100_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */ +#define WM5100_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */ +#define WM5100_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */ +#define WM5100_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */ +#define WM5100_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */ +#define WM5100_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */ +#define WM5100_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */ + +/* + * R1346 (0x542) - Audio IF 2_3 + */ +#define WM5100_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */ +#define WM5100_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */ +#define WM5100_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */ +#define WM5100_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */ +#define WM5100_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */ +#define WM5100_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */ +#define WM5100_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */ +#define WM5100_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */ +#define WM5100_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */ +#define WM5100_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */ +#define WM5100_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */ +#define WM5100_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */ + +/* + * R1347 (0x543) - Audio IF 2_4 + */ +#define WM5100_AIF2_TRI 0x0040 /* AIF2_TRI */ +#define WM5100_AIF2_TRI_MASK 0x0040 /* AIF2_TRI */ +#define WM5100_AIF2_TRI_SHIFT 6 /* AIF2_TRI */ +#define WM5100_AIF2_TRI_WIDTH 1 /* AIF2_TRI */ +#define WM5100_AIF2_RATE_MASK 0x0003 /* AIF2_RATE - [1:0] */ +#define WM5100_AIF2_RATE_SHIFT 0 /* AIF2_RATE - [1:0] */ +#define WM5100_AIF2_RATE_WIDTH 2 /* AIF2_RATE - [1:0] */ + +/* + * R1348 (0x544) - Audio IF 2_5 + */ +#define WM5100_AIF2_FMT_MASK 0x0007 /* AIF2_FMT - [2:0] */ +#define WM5100_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [2:0] */ +#define WM5100_AIF2_FMT_WIDTH 3 /* AIF2_FMT - [2:0] */ + +/* + * R1349 (0x545) - Audio IF 2_6 + */ +#define WM5100_AIF2TX_BCPF_MASK 0x1FFF /* AIF2TX_BCPF - [12:0] */ +#define WM5100_AIF2TX_BCPF_SHIFT 0 /* AIF2TX_BCPF - [12:0] */ +#define WM5100_AIF2TX_BCPF_WIDTH 13 /* AIF2TX_BCPF - [12:0] */ + +/* + * R1350 (0x546) - Audio IF 2_7 + */ +#define WM5100_AIF2RX_BCPF_MASK 0x1FFF /* AIF2RX_BCPF - [12:0] */ +#define WM5100_AIF2RX_BCPF_SHIFT 0 /* AIF2RX_BCPF - [12:0] */ +#define WM5100_AIF2RX_BCPF_WIDTH 13 /* AIF2RX_BCPF - [12:0] */ + +/* + * R1351 (0x547) - Audio IF 2_8 + */ +#define WM5100_AIF2TX_WL_MASK 0x3F00 /* AIF2TX_WL - [13:8] */ +#define WM5100_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [13:8] */ +#define WM5100_AIF2TX_WL_WIDTH 6 /* AIF2TX_WL - [13:8] */ +#define WM5100_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */ +#define WM5100_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */ +#define WM5100_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */ + +/* + * R1352 (0x548) - Audio IF 2_9 + */ +#define WM5100_AIF2RX_WL_MASK 0x3F00 /* AIF2RX_WL - [13:8] */ +#define WM5100_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [13:8] */ +#define WM5100_AIF2RX_WL_WIDTH 6 /* AIF2RX_WL - [13:8] */ +#define WM5100_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */ +#define WM5100_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */ +#define WM5100_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */ + +/* + * R1353 (0x549) - Audio IF 2_10 + */ +#define WM5100_AIF2TX1_SLOT_MASK 0x003F /* AIF2TX1_SLOT - [5:0] */ +#define WM5100_AIF2TX1_SLOT_SHIFT 0 /* AIF2TX1_SLOT - [5:0] */ +#define WM5100_AIF2TX1_SLOT_WIDTH 6 /* AIF2TX1_SLOT - [5:0] */ + +/* + * R1354 (0x54A) - Audio IF 2_11 + */ +#define WM5100_AIF2TX2_SLOT_MASK 0x003F /* AIF2TX2_SLOT - [5:0] */ +#define WM5100_AIF2TX2_SLOT_SHIFT 0 /* AIF2TX2_SLOT - [5:0] */ +#define WM5100_AIF2TX2_SLOT_WIDTH 6 /* AIF2TX2_SLOT - [5:0] */ + +/* + * R1361 (0x551) - Audio IF 2_18 + */ +#define WM5100_AIF2RX1_SLOT_MASK 0x003F /* AIF2RX1_SLOT - [5:0] */ +#define WM5100_AIF2RX1_SLOT_SHIFT 0 /* AIF2RX1_SLOT - [5:0] */ +#define WM5100_AIF2RX1_SLOT_WIDTH 6 /* AIF2RX1_SLOT - [5:0] */ + +/* + * R1362 (0x552) - Audio IF 2_19 + */ +#define WM5100_AIF2RX2_SLOT_MASK 0x003F /* AIF2RX2_SLOT - [5:0] */ +#define WM5100_AIF2RX2_SLOT_SHIFT 0 /* AIF2RX2_SLOT - [5:0] */ +#define WM5100_AIF2RX2_SLOT_WIDTH 6 /* AIF2RX2_SLOT - [5:0] */ + +/* + * R1369 (0x559) - Audio IF 2_26 + */ +#define WM5100_AIF2TX2_ENA 0x0002 /* AIF2TX2_ENA */ +#define WM5100_AIF2TX2_ENA_MASK 0x0002 /* AIF2TX2_ENA */ +#define WM5100_AIF2TX2_ENA_SHIFT 1 /* AIF2TX2_ENA */ +#define WM5100_AIF2TX2_ENA_WIDTH 1 /* AIF2TX2_ENA */ +#define WM5100_AIF2TX1_ENA 0x0001 /* AIF2TX1_ENA */ +#define WM5100_AIF2TX1_ENA_MASK 0x0001 /* AIF2TX1_ENA */ +#define WM5100_AIF2TX1_ENA_SHIFT 0 /* AIF2TX1_ENA */ +#define WM5100_AIF2TX1_ENA_WIDTH 1 /* AIF2TX1_ENA */ + +/* + * R1370 (0x55A) - Audio IF 2_27 + */ +#define WM5100_AIF2RX2_ENA 0x0002 /* AIF2RX2_ENA */ +#define WM5100_AIF2RX2_ENA_MASK 0x0002 /* AIF2RX2_ENA */ +#define WM5100_AIF2RX2_ENA_SHIFT 1 /* AIF2RX2_ENA */ +#define WM5100_AIF2RX2_ENA_WIDTH 1 /* AIF2RX2_ENA */ +#define WM5100_AIF2RX1_ENA 0x0001 /* AIF2RX1_ENA */ +#define WM5100_AIF2RX1_ENA_MASK 0x0001 /* AIF2RX1_ENA */ +#define WM5100_AIF2RX1_ENA_SHIFT 0 /* AIF2RX1_ENA */ +#define WM5100_AIF2RX1_ENA_WIDTH 1 /* AIF2RX1_ENA */ + +/* + * R1408 (0x580) - Audio IF 3_1 + */ +#define WM5100_AIF3_BCLK_INV 0x0080 /* AIF3_BCLK_INV */ +#define WM5100_AIF3_BCLK_INV_MASK 0x0080 /* AIF3_BCLK_INV */ +#define WM5100_AIF3_BCLK_INV_SHIFT 7 /* AIF3_BCLK_INV */ +#define WM5100_AIF3_BCLK_INV_WIDTH 1 /* AIF3_BCLK_INV */ +#define WM5100_AIF3_BCLK_FRC 0x0040 /* AIF3_BCLK_FRC */ +#define WM5100_AIF3_BCLK_FRC_MASK 0x0040 /* AIF3_BCLK_FRC */ +#define WM5100_AIF3_BCLK_FRC_SHIFT 6 /* AIF3_BCLK_FRC */ +#define WM5100_AIF3_BCLK_FRC_WIDTH 1 /* AIF3_BCLK_FRC */ +#define WM5100_AIF3_BCLK_MSTR 0x0020 /* AIF3_BCLK_MSTR */ +#define WM5100_AIF3_BCLK_MSTR_MASK 0x0020 /* AIF3_BCLK_MSTR */ +#define WM5100_AIF3_BCLK_MSTR_SHIFT 5 /* AIF3_BCLK_MSTR */ +#define WM5100_AIF3_BCLK_MSTR_WIDTH 1 /* AIF3_BCLK_MSTR */ +#define WM5100_AIF3_BCLK_FREQ_MASK 0x001F /* AIF3_BCLK_FREQ - [4:0] */ +#define WM5100_AIF3_BCLK_FREQ_SHIFT 0 /* AIF3_BCLK_FREQ - [4:0] */ +#define WM5100_AIF3_BCLK_FREQ_WIDTH 5 /* AIF3_BCLK_FREQ - [4:0] */ + +/* + * R1409 (0x581) - Audio IF 3_2 + */ +#define WM5100_AIF3TX_DAT_TRI 0x0020 /* AIF3TX_DAT_TRI */ +#define WM5100_AIF3TX_DAT_TRI_MASK 0x0020 /* AIF3TX_DAT_TRI */ +#define WM5100_AIF3TX_DAT_TRI_SHIFT 5 /* AIF3TX_DAT_TRI */ +#define WM5100_AIF3TX_DAT_TRI_WIDTH 1 /* AIF3TX_DAT_TRI */ +#define WM5100_AIF3TX_LRCLK_SRC 0x0008 /* AIF3TX_LRCLK_SRC */ +#define WM5100_AIF3TX_LRCLK_SRC_MASK 0x0008 /* AIF3TX_LRCLK_SRC */ +#define WM5100_AIF3TX_LRCLK_SRC_SHIFT 3 /* AIF3TX_LRCLK_SRC */ +#define WM5100_AIF3TX_LRCLK_SRC_WIDTH 1 /* AIF3TX_LRCLK_SRC */ +#define WM5100_AIF3TX_LRCLK_INV 0x0004 /* AIF3TX_LRCLK_INV */ +#define WM5100_AIF3TX_LRCLK_INV_MASK 0x0004 /* AIF3TX_LRCLK_INV */ +#define WM5100_AIF3TX_LRCLK_INV_SHIFT 2 /* AIF3TX_LRCLK_INV */ +#define WM5100_AIF3TX_LRCLK_INV_WIDTH 1 /* AIF3TX_LRCLK_INV */ +#define WM5100_AIF3TX_LRCLK_FRC 0x0002 /* AIF3TX_LRCLK_FRC */ +#define WM5100_AIF3TX_LRCLK_FRC_MASK 0x0002 /* AIF3TX_LRCLK_FRC */ +#define WM5100_AIF3TX_LRCLK_FRC_SHIFT 1 /* AIF3TX_LRCLK_FRC */ +#define WM5100_AIF3TX_LRCLK_FRC_WIDTH 1 /* AIF3TX_LRCLK_FRC */ +#define WM5100_AIF3TX_LRCLK_MSTR 0x0001 /* AIF3TX_LRCLK_MSTR */ +#define WM5100_AIF3TX_LRCLK_MSTR_MASK 0x0001 /* AIF3TX_LRCLK_MSTR */ +#define WM5100_AIF3TX_LRCLK_MSTR_SHIFT 0 /* AIF3TX_LRCLK_MSTR */ +#define WM5100_AIF3TX_LRCLK_MSTR_WIDTH 1 /* AIF3TX_LRCLK_MSTR */ + +/* + * R1410 (0x582) - Audio IF 3_3 + */ +#define WM5100_AIF3RX_LRCLK_INV 0x0004 /* AIF3RX_LRCLK_INV */ +#define WM5100_AIF3RX_LRCLK_INV_MASK 0x0004 /* AIF3RX_LRCLK_INV */ +#define WM5100_AIF3RX_LRCLK_INV_SHIFT 2 /* AIF3RX_LRCLK_INV */ +#define WM5100_AIF3RX_LRCLK_INV_WIDTH 1 /* AIF3RX_LRCLK_INV */ +#define WM5100_AIF3RX_LRCLK_FRC 0x0002 /* AIF3RX_LRCLK_FRC */ +#define WM5100_AIF3RX_LRCLK_FRC_MASK 0x0002 /* AIF3RX_LRCLK_FRC */ +#define WM5100_AIF3RX_LRCLK_FRC_SHIFT 1 /* AIF3RX_LRCLK_FRC */ +#define WM5100_AIF3RX_LRCLK_FRC_WIDTH 1 /* AIF3RX_LRCLK_FRC */ +#define WM5100_AIF3RX_LRCLK_MSTR 0x0001 /* AIF3RX_LRCLK_MSTR */ +#define WM5100_AIF3RX_LRCLK_MSTR_MASK 0x0001 /* AIF3RX_LRCLK_MSTR */ +#define WM5100_AIF3RX_LRCLK_MSTR_SHIFT 0 /* AIF3RX_LRCLK_MSTR */ +#define WM5100_AIF3RX_LRCLK_MSTR_WIDTH 1 /* AIF3RX_LRCLK_MSTR */ + +/* + * R1411 (0x583) - Audio IF 3_4 + */ +#define WM5100_AIF3_TRI 0x0040 /* AIF3_TRI */ +#define WM5100_AIF3_TRI_MASK 0x0040 /* AIF3_TRI */ +#define WM5100_AIF3_TRI_SHIFT 6 /* AIF3_TRI */ +#define WM5100_AIF3_TRI_WIDTH 1 /* AIF3_TRI */ +#define WM5100_AIF3_RATE_MASK 0x0003 /* AIF3_RATE - [1:0] */ +#define WM5100_AIF3_RATE_SHIFT 0 /* AIF3_RATE - [1:0] */ +#define WM5100_AIF3_RATE_WIDTH 2 /* AIF3_RATE - [1:0] */ + +/* + * R1412 (0x584) - Audio IF 3_5 + */ +#define WM5100_AIF3_FMT_MASK 0x0007 /* AIF3_FMT - [2:0] */ +#define WM5100_AIF3_FMT_SHIFT 0 /* AIF3_FMT - [2:0] */ +#define WM5100_AIF3_FMT_WIDTH 3 /* AIF3_FMT - [2:0] */ + +/* + * R1413 (0x585) - Audio IF 3_6 + */ +#define WM5100_AIF3TX_BCPF_MASK 0x1FFF /* AIF3TX_BCPF - [12:0] */ +#define WM5100_AIF3TX_BCPF_SHIFT 0 /* AIF3TX_BCPF - [12:0] */ +#define WM5100_AIF3TX_BCPF_WIDTH 13 /* AIF3TX_BCPF - [12:0] */ + +/* + * R1414 (0x586) - Audio IF 3_7 + */ +#define WM5100_AIF3RX_BCPF_MASK 0x1FFF /* AIF3RX_BCPF - [12:0] */ +#define WM5100_AIF3RX_BCPF_SHIFT 0 /* AIF3RX_BCPF - [12:0] */ +#define WM5100_AIF3RX_BCPF_WIDTH 13 /* AIF3RX_BCPF - [12:0] */ + +/* + * R1415 (0x587) - Audio IF 3_8 + */ +#define WM5100_AIF3TX_WL_MASK 0x3F00 /* AIF3TX_WL - [13:8] */ +#define WM5100_AIF3TX_WL_SHIFT 8 /* AIF3TX_WL - [13:8] */ +#define WM5100_AIF3TX_WL_WIDTH 6 /* AIF3TX_WL - [13:8] */ +#define WM5100_AIF3TX_SLOT_LEN_MASK 0x00FF /* AIF3TX_SLOT_LEN - [7:0] */ +#define WM5100_AIF3TX_SLOT_LEN_SHIFT 0 /* AIF3TX_SLOT_LEN - [7:0] */ +#define WM5100_AIF3TX_SLOT_LEN_WIDTH 8 /* AIF3TX_SLOT_LEN - [7:0] */ + +/* + * R1416 (0x588) - Audio IF 3_9 + */ +#define WM5100_AIF3RX_WL_MASK 0x3F00 /* AIF3RX_WL - [13:8] */ +#define WM5100_AIF3RX_WL_SHIFT 8 /* AIF3RX_WL - [13:8] */ +#define WM5100_AIF3RX_WL_WIDTH 6 /* AIF3RX_WL - [13:8] */ +#define WM5100_AIF3RX_SLOT_LEN_MASK 0x00FF /* AIF3RX_SLOT_LEN - [7:0] */ +#define WM5100_AIF3RX_SLOT_LEN_SHIFT 0 /* AIF3RX_SLOT_LEN - [7:0] */ +#define WM5100_AIF3RX_SLOT_LEN_WIDTH 8 /* AIF3RX_SLOT_LEN - [7:0] */ + +/* + * R1417 (0x589) - Audio IF 3_10 + */ +#define WM5100_AIF3TX1_SLOT_MASK 0x003F /* AIF3TX1_SLOT - [5:0] */ +#define WM5100_AIF3TX1_SLOT_SHIFT 0 /* AIF3TX1_SLOT - [5:0] */ +#define WM5100_AIF3TX1_SLOT_WIDTH 6 /* AIF3TX1_SLOT - [5:0] */ + +/* + * R1418 (0x58A) - Audio IF 3_11 + */ +#define WM5100_AIF3TX2_SLOT_MASK 0x003F /* AIF3TX2_SLOT - [5:0] */ +#define WM5100_AIF3TX2_SLOT_SHIFT 0 /* AIF3TX2_SLOT - [5:0] */ +#define WM5100_AIF3TX2_SLOT_WIDTH 6 /* AIF3TX2_SLOT - [5:0] */ + +/* + * R1425 (0x591) - Audio IF 3_18 + */ +#define WM5100_AIF3RX1_SLOT_MASK 0x003F /* AIF3RX1_SLOT - [5:0] */ +#define WM5100_AIF3RX1_SLOT_SHIFT 0 /* AIF3RX1_SLOT - [5:0] */ +#define WM5100_AIF3RX1_SLOT_WIDTH 6 /* AIF3RX1_SLOT - [5:0] */ + +/* + * R1426 (0x592) - Audio IF 3_19 + */ +#define WM5100_AIF3RX2_SLOT_MASK 0x003F /* AIF3RX2_SLOT - [5:0] */ +#define WM5100_AIF3RX2_SLOT_SHIFT 0 /* AIF3RX2_SLOT - [5:0] */ +#define WM5100_AIF3RX2_SLOT_WIDTH 6 /* AIF3RX2_SLOT - [5:0] */ + +/* + * R1433 (0x599) - Audio IF 3_26 + */ +#define WM5100_AIF3TX2_ENA 0x0002 /* AIF3TX2_ENA */ +#define WM5100_AIF3TX2_ENA_MASK 0x0002 /* AIF3TX2_ENA */ +#define WM5100_AIF3TX2_ENA_SHIFT 1 /* AIF3TX2_ENA */ +#define WM5100_AIF3TX2_ENA_WIDTH 1 /* AIF3TX2_ENA */ +#define WM5100_AIF3TX1_ENA 0x0001 /* AIF3TX1_ENA */ +#define WM5100_AIF3TX1_ENA_MASK 0x0001 /* AIF3TX1_ENA */ +#define WM5100_AIF3TX1_ENA_SHIFT 0 /* AIF3TX1_ENA */ +#define WM5100_AIF3TX1_ENA_WIDTH 1 /* AIF3TX1_ENA */ + +/* + * R1434 (0x59A) - Audio IF 3_27 + */ +#define WM5100_AIF3RX2_ENA 0x0002 /* AIF3RX2_ENA */ +#define WM5100_AIF3RX2_ENA_MASK 0x0002 /* AIF3RX2_ENA */ +#define WM5100_AIF3RX2_ENA_SHIFT 1 /* AIF3RX2_ENA */ +#define WM5100_AIF3RX2_ENA_WIDTH 1 /* AIF3RX2_ENA */ +#define WM5100_AIF3RX1_ENA 0x0001 /* AIF3RX1_ENA */ +#define WM5100_AIF3RX1_ENA_MASK 0x0001 /* AIF3RX1_ENA */ +#define WM5100_AIF3RX1_ENA_SHIFT 0 /* AIF3RX1_ENA */ +#define WM5100_AIF3RX1_ENA_WIDTH 1 /* AIF3RX1_ENA */ + +#define WM5100_MIXER_VOL_MASK 0x00FE /* MIXER_VOL - [7:1] */ +#define WM5100_MIXER_VOL_SHIFT 1 /* MIXER_VOL - [7:1] */ +#define WM5100_MIXER_VOL_WIDTH 7 /* MIXER_VOL - [7:1] */ + +/* + * R3072 (0xC00) - GPIO CTRL 1 + */ +#define WM5100_GP1_DIR 0x8000 /* GP1_DIR */ +#define WM5100_GP1_DIR_MASK 0x8000 /* GP1_DIR */ +#define WM5100_GP1_DIR_SHIFT 15 /* GP1_DIR */ +#define WM5100_GP1_DIR_WIDTH 1 /* GP1_DIR */ +#define WM5100_GP1_PU 0x4000 /* GP1_PU */ +#define WM5100_GP1_PU_MASK 0x4000 /* GP1_PU */ +#define WM5100_GP1_PU_SHIFT 14 /* GP1_PU */ +#define WM5100_GP1_PU_WIDTH 1 /* GP1_PU */ +#define WM5100_GP1_PD 0x2000 /* GP1_PD */ +#define WM5100_GP1_PD_MASK 0x2000 /* GP1_PD */ +#define WM5100_GP1_PD_SHIFT 13 /* GP1_PD */ +#define WM5100_GP1_PD_WIDTH 1 /* GP1_PD */ +#define WM5100_GP1_POL 0x0400 /* GP1_POL */ +#define WM5100_GP1_POL_MASK 0x0400 /* GP1_POL */ +#define WM5100_GP1_POL_SHIFT 10 /* GP1_POL */ +#define WM5100_GP1_POL_WIDTH 1 /* GP1_POL */ +#define WM5100_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */ +#define WM5100_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */ +#define WM5100_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */ +#define WM5100_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */ +#define WM5100_GP1_DB 0x0100 /* GP1_DB */ +#define WM5100_GP1_DB_MASK 0x0100 /* GP1_DB */ +#define WM5100_GP1_DB_SHIFT 8 /* GP1_DB */ +#define WM5100_GP1_DB_WIDTH 1 /* GP1_DB */ +#define WM5100_GP1_LVL 0x0040 /* GP1_LVL */ +#define WM5100_GP1_LVL_MASK 0x0040 /* GP1_LVL */ +#define WM5100_GP1_LVL_SHIFT 6 /* GP1_LVL */ +#define WM5100_GP1_LVL_WIDTH 1 /* GP1_LVL */ +#define WM5100_GP1_FN_MASK 0x003F /* GP1_FN - [5:0] */ +#define WM5100_GP1_FN_SHIFT 0 /* GP1_FN - [5:0] */ +#define WM5100_GP1_FN_WIDTH 6 /* GP1_FN - [5:0] */ + +/* + * R3073 (0xC01) - GPIO CTRL 2 + */ +#define WM5100_GP2_DIR 0x8000 /* GP2_DIR */ +#define WM5100_GP2_DIR_MASK 0x8000 /* GP2_DIR */ +#define WM5100_GP2_DIR_SHIFT 15 /* GP2_DIR */ +#define WM5100_GP2_DIR_WIDTH 1 /* GP2_DIR */ +#define WM5100_GP2_PU 0x4000 /* GP2_PU */ +#define WM5100_GP2_PU_MASK 0x4000 /* GP2_PU */ +#define WM5100_GP2_PU_SHIFT 14 /* GP2_PU */ +#define WM5100_GP2_PU_WIDTH 1 /* GP2_PU */ +#define WM5100_GP2_PD 0x2000 /* GP2_PD */ +#define WM5100_GP2_PD_MASK 0x2000 /* GP2_PD */ +#define WM5100_GP2_PD_SHIFT 13 /* GP2_PD */ +#define WM5100_GP2_PD_WIDTH 1 /* GP2_PD */ +#define WM5100_GP2_POL 0x0400 /* GP2_POL */ +#define WM5100_GP2_POL_MASK 0x0400 /* GP2_POL */ +#define WM5100_GP2_POL_SHIFT 10 /* GP2_POL */ +#define WM5100_GP2_POL_WIDTH 1 /* GP2_POL */ +#define WM5100_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */ +#define WM5100_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */ +#define WM5100_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */ +#define WM5100_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */ +#define WM5100_GP2_DB 0x0100 /* GP2_DB */ +#define WM5100_GP2_DB_MASK 0x0100 /* GP2_DB */ +#define WM5100_GP2_DB_SHIFT 8 /* GP2_DB */ +#define WM5100_GP2_DB_WIDTH 1 /* GP2_DB */ +#define WM5100_GP2_LVL 0x0040 /* GP2_LVL */ +#define WM5100_GP2_LVL_MASK 0x0040 /* GP2_LVL */ +#define WM5100_GP2_LVL_SHIFT 6 /* GP2_LVL */ +#define WM5100_GP2_LVL_WIDTH 1 /* GP2_LVL */ +#define WM5100_GP2_FN_MASK 0x003F /* GP2_FN - [5:0] */ +#define WM5100_GP2_FN_SHIFT 0 /* GP2_FN - [5:0] */ +#define WM5100_GP2_FN_WIDTH 6 /* GP2_FN - [5:0] */ + +/* + * R3074 (0xC02) - GPIO CTRL 3 + */ +#define WM5100_GP3_DIR 0x8000 /* GP3_DIR */ +#define WM5100_GP3_DIR_MASK 0x8000 /* GP3_DIR */ +#define WM5100_GP3_DIR_SHIFT 15 /* GP3_DIR */ +#define WM5100_GP3_DIR_WIDTH 1 /* GP3_DIR */ +#define WM5100_GP3_PU 0x4000 /* GP3_PU */ +#define WM5100_GP3_PU_MASK 0x4000 /* GP3_PU */ +#define WM5100_GP3_PU_SHIFT 14 /* GP3_PU */ +#define WM5100_GP3_PU_WIDTH 1 /* GP3_PU */ +#define WM5100_GP3_PD 0x2000 /* GP3_PD */ +#define WM5100_GP3_PD_MASK 0x2000 /* GP3_PD */ +#define WM5100_GP3_PD_SHIFT 13 /* GP3_PD */ +#define WM5100_GP3_PD_WIDTH 1 /* GP3_PD */ +#define WM5100_GP3_POL 0x0400 /* GP3_POL */ +#define WM5100_GP3_POL_MASK 0x0400 /* GP3_POL */ +#define WM5100_GP3_POL_SHIFT 10 /* GP3_POL */ +#define WM5100_GP3_POL_WIDTH 1 /* GP3_POL */ +#define WM5100_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */ +#define WM5100_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */ +#define WM5100_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */ +#define WM5100_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */ +#define WM5100_GP3_DB 0x0100 /* GP3_DB */ +#define WM5100_GP3_DB_MASK 0x0100 /* GP3_DB */ +#define WM5100_GP3_DB_SHIFT 8 /* GP3_DB */ +#define WM5100_GP3_DB_WIDTH 1 /* GP3_DB */ +#define WM5100_GP3_LVL 0x0040 /* GP3_LVL */ +#define WM5100_GP3_LVL_MASK 0x0040 /* GP3_LVL */ +#define WM5100_GP3_LVL_SHIFT 6 /* GP3_LVL */ +#define WM5100_GP3_LVL_WIDTH 1 /* GP3_LVL */ +#define WM5100_GP3_FN_MASK 0x003F /* GP3_FN - [5:0] */ +#define WM5100_GP3_FN_SHIFT 0 /* GP3_FN - [5:0] */ +#define WM5100_GP3_FN_WIDTH 6 /* GP3_FN - [5:0] */ + +/* + * R3075 (0xC03) - GPIO CTRL 4 + */ +#define WM5100_GP4_DIR 0x8000 /* GP4_DIR */ +#define WM5100_GP4_DIR_MASK 0x8000 /* GP4_DIR */ +#define WM5100_GP4_DIR_SHIFT 15 /* GP4_DIR */ +#define WM5100_GP4_DIR_WIDTH 1 /* GP4_DIR */ +#define WM5100_GP4_PU 0x4000 /* GP4_PU */ +#define WM5100_GP4_PU_MASK 0x4000 /* GP4_PU */ +#define WM5100_GP4_PU_SHIFT 14 /* GP4_PU */ +#define WM5100_GP4_PU_WIDTH 1 /* GP4_PU */ +#define WM5100_GP4_PD 0x2000 /* GP4_PD */ +#define WM5100_GP4_PD_MASK 0x2000 /* GP4_PD */ +#define WM5100_GP4_PD_SHIFT 13 /* GP4_PD */ +#define WM5100_GP4_PD_WIDTH 1 /* GP4_PD */ +#define WM5100_GP4_POL 0x0400 /* GP4_POL */ +#define WM5100_GP4_POL_MASK 0x0400 /* GP4_POL */ +#define WM5100_GP4_POL_SHIFT 10 /* GP4_POL */ +#define WM5100_GP4_POL_WIDTH 1 /* GP4_POL */ +#define WM5100_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */ +#define WM5100_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */ +#define WM5100_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */ +#define WM5100_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */ +#define WM5100_GP4_DB 0x0100 /* GP4_DB */ +#define WM5100_GP4_DB_MASK 0x0100 /* GP4_DB */ +#define WM5100_GP4_DB_SHIFT 8 /* GP4_DB */ +#define WM5100_GP4_DB_WIDTH 1 /* GP4_DB */ +#define WM5100_GP4_LVL 0x0040 /* GP4_LVL */ +#define WM5100_GP4_LVL_MASK 0x0040 /* GP4_LVL */ +#define WM5100_GP4_LVL_SHIFT 6 /* GP4_LVL */ +#define WM5100_GP4_LVL_WIDTH 1 /* GP4_LVL */ +#define WM5100_GP4_FN_MASK 0x003F /* GP4_FN - [5:0] */ +#define WM5100_GP4_FN_SHIFT 0 /* GP4_FN - [5:0] */ +#define WM5100_GP4_FN_WIDTH 6 /* GP4_FN - [5:0] */ + +/* + * R3076 (0xC04) - GPIO CTRL 5 + */ +#define WM5100_GP5_DIR 0x8000 /* GP5_DIR */ +#define WM5100_GP5_DIR_MASK 0x8000 /* GP5_DIR */ +#define WM5100_GP5_DIR_SHIFT 15 /* GP5_DIR */ +#define WM5100_GP5_DIR_WIDTH 1 /* GP5_DIR */ +#define WM5100_GP5_PU 0x4000 /* GP5_PU */ +#define WM5100_GP5_PU_MASK 0x4000 /* GP5_PU */ +#define WM5100_GP5_PU_SHIFT 14 /* GP5_PU */ +#define WM5100_GP5_PU_WIDTH 1 /* GP5_PU */ +#define WM5100_GP5_PD 0x2000 /* GP5_PD */ +#define WM5100_GP5_PD_MASK 0x2000 /* GP5_PD */ +#define WM5100_GP5_PD_SHIFT 13 /* GP5_PD */ +#define WM5100_GP5_PD_WIDTH 1 /* GP5_PD */ +#define WM5100_GP5_POL 0x0400 /* GP5_POL */ +#define WM5100_GP5_POL_MASK 0x0400 /* GP5_POL */ +#define WM5100_GP5_POL_SHIFT 10 /* GP5_POL */ +#define WM5100_GP5_POL_WIDTH 1 /* GP5_POL */ +#define WM5100_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */ +#define WM5100_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */ +#define WM5100_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */ +#define WM5100_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */ +#define WM5100_GP5_DB 0x0100 /* GP5_DB */ +#define WM5100_GP5_DB_MASK 0x0100 /* GP5_DB */ +#define WM5100_GP5_DB_SHIFT 8 /* GP5_DB */ +#define WM5100_GP5_DB_WIDTH 1 /* GP5_DB */ +#define WM5100_GP5_LVL 0x0040 /* GP5_LVL */ +#define WM5100_GP5_LVL_MASK 0x0040 /* GP5_LVL */ +#define WM5100_GP5_LVL_SHIFT 6 /* GP5_LVL */ +#define WM5100_GP5_LVL_WIDTH 1 /* GP5_LVL */ +#define WM5100_GP5_FN_MASK 0x003F /* GP5_FN - [5:0] */ +#define WM5100_GP5_FN_SHIFT 0 /* GP5_FN - [5:0] */ +#define WM5100_GP5_FN_WIDTH 6 /* GP5_FN - [5:0] */ + +/* + * R3077 (0xC05) - GPIO CTRL 6 + */ +#define WM5100_GP6_DIR 0x8000 /* GP6_DIR */ +#define WM5100_GP6_DIR_MASK 0x8000 /* GP6_DIR */ +#define WM5100_GP6_DIR_SHIFT 15 /* GP6_DIR */ +#define WM5100_GP6_DIR_WIDTH 1 /* GP6_DIR */ +#define WM5100_GP6_PU 0x4000 /* GP6_PU */ +#define WM5100_GP6_PU_MASK 0x4000 /* GP6_PU */ +#define WM5100_GP6_PU_SHIFT 14 /* GP6_PU */ +#define WM5100_GP6_PU_WIDTH 1 /* GP6_PU */ +#define WM5100_GP6_PD 0x2000 /* GP6_PD */ +#define WM5100_GP6_PD_MASK 0x2000 /* GP6_PD */ +#define WM5100_GP6_PD_SHIFT 13 /* GP6_PD */ +#define WM5100_GP6_PD_WIDTH 1 /* GP6_PD */ +#define WM5100_GP6_POL 0x0400 /* GP6_POL */ +#define WM5100_GP6_POL_MASK 0x0400 /* GP6_POL */ +#define WM5100_GP6_POL_SHIFT 10 /* GP6_POL */ +#define WM5100_GP6_POL_WIDTH 1 /* GP6_POL */ +#define WM5100_GP6_OP_CFG 0x0200 /* GP6_OP_CFG */ +#define WM5100_GP6_OP_CFG_MASK 0x0200 /* GP6_OP_CFG */ +#define WM5100_GP6_OP_CFG_SHIFT 9 /* GP6_OP_CFG */ +#define WM5100_GP6_OP_CFG_WIDTH 1 /* GP6_OP_CFG */ +#define WM5100_GP6_DB 0x0100 /* GP6_DB */ +#define WM5100_GP6_DB_MASK 0x0100 /* GP6_DB */ +#define WM5100_GP6_DB_SHIFT 8 /* GP6_DB */ +#define WM5100_GP6_DB_WIDTH 1 /* GP6_DB */ +#define WM5100_GP6_LVL 0x0040 /* GP6_LVL */ +#define WM5100_GP6_LVL_MASK 0x0040 /* GP6_LVL */ +#define WM5100_GP6_LVL_SHIFT 6 /* GP6_LVL */ +#define WM5100_GP6_LVL_WIDTH 1 /* GP6_LVL */ +#define WM5100_GP6_FN_MASK 0x003F /* GP6_FN - [5:0] */ +#define WM5100_GP6_FN_SHIFT 0 /* GP6_FN - [5:0] */ +#define WM5100_GP6_FN_WIDTH 6 /* GP6_FN - [5:0] */ + +/* + * R3107 (0xC23) - Misc Pad Ctrl 1 + */ +#define WM5100_LDO1ENA_PD 0x8000 /* LDO1ENA_PD */ +#define WM5100_LDO1ENA_PD_MASK 0x8000 /* LDO1ENA_PD */ +#define WM5100_LDO1ENA_PD_SHIFT 15 /* LDO1ENA_PD */ +#define WM5100_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */ +#define WM5100_MCLK2_PD 0x2000 /* MCLK2_PD */ +#define WM5100_MCLK2_PD_MASK 0x2000 /* MCLK2_PD */ +#define WM5100_MCLK2_PD_SHIFT 13 /* MCLK2_PD */ +#define WM5100_MCLK2_PD_WIDTH 1 /* MCLK2_PD */ +#define WM5100_MCLK1_PD 0x1000 /* MCLK1_PD */ +#define WM5100_MCLK1_PD_MASK 0x1000 /* MCLK1_PD */ +#define WM5100_MCLK1_PD_SHIFT 12 /* MCLK1_PD */ +#define WM5100_MCLK1_PD_WIDTH 1 /* MCLK1_PD */ +#define WM5100_RESET_PU 0x0002 /* RESET_PU */ +#define WM5100_RESET_PU_MASK 0x0002 /* RESET_PU */ +#define WM5100_RESET_PU_SHIFT 1 /* RESET_PU */ +#define WM5100_RESET_PU_WIDTH 1 /* RESET_PU */ +#define WM5100_ADDR_PD 0x0001 /* ADDR_PD */ +#define WM5100_ADDR_PD_MASK 0x0001 /* ADDR_PD */ +#define WM5100_ADDR_PD_SHIFT 0 /* ADDR_PD */ +#define WM5100_ADDR_PD_WIDTH 1 /* ADDR_PD */ + +/* + * R3108 (0xC24) - Misc Pad Ctrl 2 + */ +#define WM5100_DMICDAT4_PD 0x0008 /* DMICDAT4_PD */ +#define WM5100_DMICDAT4_PD_MASK 0x0008 /* DMICDAT4_PD */ +#define WM5100_DMICDAT4_PD_SHIFT 3 /* DMICDAT4_PD */ +#define WM5100_DMICDAT4_PD_WIDTH 1 /* DMICDAT4_PD */ +#define WM5100_DMICDAT3_PD 0x0004 /* DMICDAT3_PD */ +#define WM5100_DMICDAT3_PD_MASK 0x0004 /* DMICDAT3_PD */ +#define WM5100_DMICDAT3_PD_SHIFT 2 /* DMICDAT3_PD */ +#define WM5100_DMICDAT3_PD_WIDTH 1 /* DMICDAT3_PD */ +#define WM5100_DMICDAT2_PD 0x0002 /* DMICDAT2_PD */ +#define WM5100_DMICDAT2_PD_MASK 0x0002 /* DMICDAT2_PD */ +#define WM5100_DMICDAT2_PD_SHIFT 1 /* DMICDAT2_PD */ +#define WM5100_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */ +#define WM5100_DMICDAT1_PD 0x0001 /* DMICDAT1_PD */ +#define WM5100_DMICDAT1_PD_MASK 0x0001 /* DMICDAT1_PD */ +#define WM5100_DMICDAT1_PD_SHIFT 0 /* DMICDAT1_PD */ +#define WM5100_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */ + +/* + * R3109 (0xC25) - Misc Pad Ctrl 3 + */ +#define WM5100_AIF1RXLRCLK_PU 0x0020 /* AIF1RXLRCLK_PU */ +#define WM5100_AIF1RXLRCLK_PU_MASK 0x0020 /* AIF1RXLRCLK_PU */ +#define WM5100_AIF1RXLRCLK_PU_SHIFT 5 /* AIF1RXLRCLK_PU */ +#define WM5100_AIF1RXLRCLK_PU_WIDTH 1 /* AIF1RXLRCLK_PU */ +#define WM5100_AIF1RXLRCLK_PD 0x0010 /* AIF1RXLRCLK_PD */ +#define WM5100_AIF1RXLRCLK_PD_MASK 0x0010 /* AIF1RXLRCLK_PD */ +#define WM5100_AIF1RXLRCLK_PD_SHIFT 4 /* AIF1RXLRCLK_PD */ +#define WM5100_AIF1RXLRCLK_PD_WIDTH 1 /* AIF1RXLRCLK_PD */ +#define WM5100_AIF1BCLK_PU 0x0008 /* AIF1BCLK_PU */ +#define WM5100_AIF1BCLK_PU_MASK 0x0008 /* AIF1BCLK_PU */ +#define WM5100_AIF1BCLK_PU_SHIFT 3 /* AIF1BCLK_PU */ +#define WM5100_AIF1BCLK_PU_WIDTH 1 /* AIF1BCLK_PU */ +#define WM5100_AIF1BCLK_PD 0x0004 /* AIF1BCLK_PD */ +#define WM5100_AIF1BCLK_PD_MASK 0x0004 /* AIF1BCLK_PD */ +#define WM5100_AIF1BCLK_PD_SHIFT 2 /* AIF1BCLK_PD */ +#define WM5100_AIF1BCLK_PD_WIDTH 1 /* AIF1BCLK_PD */ +#define WM5100_AIF1RXDAT_PU 0x0002 /* AIF1RXDAT_PU */ +#define WM5100_AIF1RXDAT_PU_MASK 0x0002 /* AIF1RXDAT_PU */ +#define WM5100_AIF1RXDAT_PU_SHIFT 1 /* AIF1RXDAT_PU */ +#define WM5100_AIF1RXDAT_PU_WIDTH 1 /* AIF1RXDAT_PU */ +#define WM5100_AIF1RXDAT_PD 0x0001 /* AIF1RXDAT_PD */ +#define WM5100_AIF1RXDAT_PD_MASK 0x0001 /* AIF1RXDAT_PD */ +#define WM5100_AIF1RXDAT_PD_SHIFT 0 /* AIF1RXDAT_PD */ +#define WM5100_AIF1RXDAT_PD_WIDTH 1 /* AIF1RXDAT_PD */ + +/* + * R3110 (0xC26) - Misc Pad Ctrl 4 + */ +#define WM5100_AIF2RXLRCLK_PU 0x0020 /* AIF2RXLRCLK_PU */ +#define WM5100_AIF2RXLRCLK_PU_MASK 0x0020 /* AIF2RXLRCLK_PU */ +#define WM5100_AIF2RXLRCLK_PU_SHIFT 5 /* AIF2RXLRCLK_PU */ +#define WM5100_AIF2RXLRCLK_PU_WIDTH 1 /* AIF2RXLRCLK_PU */ +#define WM5100_AIF2RXLRCLK_PD 0x0010 /* AIF2RXLRCLK_PD */ +#define WM5100_AIF2RXLRCLK_PD_MASK 0x0010 /* AIF2RXLRCLK_PD */ +#define WM5100_AIF2RXLRCLK_PD_SHIFT 4 /* AIF2RXLRCLK_PD */ +#define WM5100_AIF2RXLRCLK_PD_WIDTH 1 /* AIF2RXLRCLK_PD */ +#define WM5100_AIF2BCLK_PU 0x0008 /* AIF2BCLK_PU */ +#define WM5100_AIF2BCLK_PU_MASK 0x0008 /* AIF2BCLK_PU */ +#define WM5100_AIF2BCLK_PU_SHIFT 3 /* AIF2BCLK_PU */ +#define WM5100_AIF2BCLK_PU_WIDTH 1 /* AIF2BCLK_PU */ +#define WM5100_AIF2BCLK_PD 0x0004 /* AIF2BCLK_PD */ +#define WM5100_AIF2BCLK_PD_MASK 0x0004 /* AIF2BCLK_PD */ +#define WM5100_AIF2BCLK_PD_SHIFT 2 /* AIF2BCLK_PD */ +#define WM5100_AIF2BCLK_PD_WIDTH 1 /* AIF2BCLK_PD */ +#define WM5100_AIF2RXDAT_PU 0x0002 /* AIF2RXDAT_PU */ +#define WM5100_AIF2RXDAT_PU_MASK 0x0002 /* AIF2RXDAT_PU */ +#define WM5100_AIF2RXDAT_PU_SHIFT 1 /* AIF2RXDAT_PU */ +#define WM5100_AIF2RXDAT_PU_WIDTH 1 /* AIF2RXDAT_PU */ +#define WM5100_AIF2RXDAT_PD 0x0001 /* AIF2RXDAT_PD */ +#define WM5100_AIF2RXDAT_PD_MASK 0x0001 /* AIF2RXDAT_PD */ +#define WM5100_AIF2RXDAT_PD_SHIFT 0 /* AIF2RXDAT_PD */ +#define WM5100_AIF2RXDAT_PD_WIDTH 1 /* AIF2RXDAT_PD */ + +/* + * R3111 (0xC27) - Misc Pad Ctrl 5 + */ +#define WM5100_AIF3RXLRCLK_PU 0x0020 /* AIF3RXLRCLK_PU */ +#define WM5100_AIF3RXLRCLK_PU_MASK 0x0020 /* AIF3RXLRCLK_PU */ +#define WM5100_AIF3RXLRCLK_PU_SHIFT 5 /* AIF3RXLRCLK_PU */ +#define WM5100_AIF3RXLRCLK_PU_WIDTH 1 /* AIF3RXLRCLK_PU */ +#define WM5100_AIF3RXLRCLK_PD 0x0010 /* AIF3RXLRCLK_PD */ +#define WM5100_AIF3RXLRCLK_PD_MASK 0x0010 /* AIF3RXLRCLK_PD */ +#define WM5100_AIF3RXLRCLK_PD_SHIFT 4 /* AIF3RXLRCLK_PD */ +#define WM5100_AIF3RXLRCLK_PD_WIDTH 1 /* AIF3RXLRCLK_PD */ +#define WM5100_AIF3BCLK_PU 0x0008 /* AIF3BCLK_PU */ +#define WM5100_AIF3BCLK_PU_MASK 0x0008 /* AIF3BCLK_PU */ +#define WM5100_AIF3BCLK_PU_SHIFT 3 /* AIF3BCLK_PU */ +#define WM5100_AIF3BCLK_PU_WIDTH 1 /* AIF3BCLK_PU */ +#define WM5100_AIF3BCLK_PD 0x0004 /* AIF3BCLK_PD */ +#define WM5100_AIF3BCLK_PD_MASK 0x0004 /* AIF3BCLK_PD */ +#define WM5100_AIF3BCLK_PD_SHIFT 2 /* AIF3BCLK_PD */ +#define WM5100_AIF3BCLK_PD_WIDTH 1 /* AIF3BCLK_PD */ +#define WM5100_AIF3RXDAT_PU 0x0002 /* AIF3RXDAT_PU */ +#define WM5100_AIF3RXDAT_PU_MASK 0x0002 /* AIF3RXDAT_PU */ +#define WM5100_AIF3RXDAT_PU_SHIFT 1 /* AIF3RXDAT_PU */ +#define WM5100_AIF3RXDAT_PU_WIDTH 1 /* AIF3RXDAT_PU */ +#define WM5100_AIF3RXDAT_PD 0x0001 /* AIF3RXDAT_PD */ +#define WM5100_AIF3RXDAT_PD_MASK 0x0001 /* AIF3RXDAT_PD */ +#define WM5100_AIF3RXDAT_PD_SHIFT 0 /* AIF3RXDAT_PD */ +#define WM5100_AIF3RXDAT_PD_WIDTH 1 /* AIF3RXDAT_PD */ + +/* + * R3112 (0xC28) - Misc GPIO 1 + */ +#define WM5100_OPCLK_SEL_MASK 0x0003 /* OPCLK_SEL - [1:0] */ +#define WM5100_OPCLK_SEL_SHIFT 0 /* OPCLK_SEL - [1:0] */ +#define WM5100_OPCLK_SEL_WIDTH 2 /* OPCLK_SEL - [1:0] */ + +/* + * R3328 (0xD00) - Interrupt Status 1 + */ +#define WM5100_GP6_EINT 0x0020 /* GP6_EINT */ +#define WM5100_GP6_EINT_MASK 0x0020 /* GP6_EINT */ +#define WM5100_GP6_EINT_SHIFT 5 /* GP6_EINT */ +#define WM5100_GP6_EINT_WIDTH 1 /* GP6_EINT */ +#define WM5100_GP5_EINT 0x0010 /* GP5_EINT */ +#define WM5100_GP5_EINT_MASK 0x0010 /* GP5_EINT */ +#define WM5100_GP5_EINT_SHIFT 4 /* GP5_EINT */ +#define WM5100_GP5_EINT_WIDTH 1 /* GP5_EINT */ +#define WM5100_GP4_EINT 0x0008 /* GP4_EINT */ +#define WM5100_GP4_EINT_MASK 0x0008 /* GP4_EINT */ +#define WM5100_GP4_EINT_SHIFT 3 /* GP4_EINT */ +#define WM5100_GP4_EINT_WIDTH 1 /* GP4_EINT */ +#define WM5100_GP3_EINT 0x0004 /* GP3_EINT */ +#define WM5100_GP3_EINT_MASK 0x0004 /* GP3_EINT */ +#define WM5100_GP3_EINT_SHIFT 2 /* GP3_EINT */ +#define WM5100_GP3_EINT_WIDTH 1 /* GP3_EINT */ +#define WM5100_GP2_EINT 0x0002 /* GP2_EINT */ +#define WM5100_GP2_EINT_MASK 0x0002 /* GP2_EINT */ +#define WM5100_GP2_EINT_SHIFT 1 /* GP2_EINT */ +#define WM5100_GP2_EINT_WIDTH 1 /* GP2_EINT */ +#define WM5100_GP1_EINT 0x0001 /* GP1_EINT */ +#define WM5100_GP1_EINT_MASK 0x0001 /* GP1_EINT */ +#define WM5100_GP1_EINT_SHIFT 0 /* GP1_EINT */ +#define WM5100_GP1_EINT_WIDTH 1 /* GP1_EINT */ + +/* + * R3329 (0xD01) - Interrupt Status 2 + */ +#define WM5100_DSP_IRQ6_EINT 0x0020 /* DSP_IRQ6_EINT */ +#define WM5100_DSP_IRQ6_EINT_MASK 0x0020 /* DSP_IRQ6_EINT */ +#define WM5100_DSP_IRQ6_EINT_SHIFT 5 /* DSP_IRQ6_EINT */ +#define WM5100_DSP_IRQ6_EINT_WIDTH 1 /* DSP_IRQ6_EINT */ +#define WM5100_DSP_IRQ5_EINT 0x0010 /* DSP_IRQ5_EINT */ +#define WM5100_DSP_IRQ5_EINT_MASK 0x0010 /* DSP_IRQ5_EINT */ +#define WM5100_DSP_IRQ5_EINT_SHIFT 4 /* DSP_IRQ5_EINT */ +#define WM5100_DSP_IRQ5_EINT_WIDTH 1 /* DSP_IRQ5_EINT */ +#define WM5100_DSP_IRQ4_EINT 0x0008 /* DSP_IRQ4_EINT */ +#define WM5100_DSP_IRQ4_EINT_MASK 0x0008 /* DSP_IRQ4_EINT */ +#define WM5100_DSP_IRQ4_EINT_SHIFT 3 /* DSP_IRQ4_EINT */ +#define WM5100_DSP_IRQ4_EINT_WIDTH 1 /* DSP_IRQ4_EINT */ +#define WM5100_DSP_IRQ3_EINT 0x0004 /* DSP_IRQ3_EINT */ +#define WM5100_DSP_IRQ3_EINT_MASK 0x0004 /* DSP_IRQ3_EINT */ +#define WM5100_DSP_IRQ3_EINT_SHIFT 2 /* DSP_IRQ3_EINT */ +#define WM5100_DSP_IRQ3_EINT_WIDTH 1 /* DSP_IRQ3_EINT */ +#define WM5100_DSP_IRQ2_EINT 0x0002 /* DSP_IRQ2_EINT */ +#define WM5100_DSP_IRQ2_EINT_MASK 0x0002 /* DSP_IRQ2_EINT */ +#define WM5100_DSP_IRQ2_EINT_SHIFT 1 /* DSP_IRQ2_EINT */ +#define WM5100_DSP_IRQ2_EINT_WIDTH 1 /* DSP_IRQ2_EINT */ +#define WM5100_DSP_IRQ1_EINT 0x0001 /* DSP_IRQ1_EINT */ +#define WM5100_DSP_IRQ1_EINT_MASK 0x0001 /* DSP_IRQ1_EINT */ +#define WM5100_DSP_IRQ1_EINT_SHIFT 0 /* DSP_IRQ1_EINT */ +#define WM5100_DSP_IRQ1_EINT_WIDTH 1 /* DSP_IRQ1_EINT */ + +/* + * R3330 (0xD02) - Interrupt Status 3 + */ +#define WM5100_SPK_SHUTDOWN_WARN_EINT 0x8000 /* SPK_SHUTDOWN_WARN_EINT */ +#define WM5100_SPK_SHUTDOWN_WARN_EINT_MASK 0x8000 /* SPK_SHUTDOWN_WARN_EINT */ +#define WM5100_SPK_SHUTDOWN_WARN_EINT_SHIFT 15 /* SPK_SHUTDOWN_WARN_EINT */ +#define WM5100_SPK_SHUTDOWN_WARN_EINT_WIDTH 1 /* SPK_SHUTDOWN_WARN_EINT */ +#define WM5100_SPK_SHUTDOWN_EINT 0x4000 /* SPK_SHUTDOWN_EINT */ +#define WM5100_SPK_SHUTDOWN_EINT_MASK 0x4000 /* SPK_SHUTDOWN_EINT */ +#define WM5100_SPK_SHUTDOWN_EINT_SHIFT 14 /* SPK_SHUTDOWN_EINT */ +#define WM5100_SPK_SHUTDOWN_EINT_WIDTH 1 /* SPK_SHUTDOWN_EINT */ +#define WM5100_HPDET_EINT 0x2000 /* HPDET_EINT */ +#define WM5100_HPDET_EINT_MASK 0x2000 /* HPDET_EINT */ +#define WM5100_HPDET_EINT_SHIFT 13 /* HPDET_EINT */ +#define WM5100_HPDET_EINT_WIDTH 1 /* HPDET_EINT */ +#define WM5100_ACCDET_EINT 0x1000 /* ACCDET_EINT */ +#define WM5100_ACCDET_EINT_MASK 0x1000 /* ACCDET_EINT */ +#define WM5100_ACCDET_EINT_SHIFT 12 /* ACCDET_EINT */ +#define WM5100_ACCDET_EINT_WIDTH 1 /* ACCDET_EINT */ +#define WM5100_DRC_SIG_DET_EINT 0x0200 /* DRC_SIG_DET_EINT */ +#define WM5100_DRC_SIG_DET_EINT_MASK 0x0200 /* DRC_SIG_DET_EINT */ +#define WM5100_DRC_SIG_DET_EINT_SHIFT 9 /* DRC_SIG_DET_EINT */ +#define WM5100_DRC_SIG_DET_EINT_WIDTH 1 /* DRC_SIG_DET_EINT */ +#define WM5100_ASRC2_LOCK_EINT 0x0100 /* ASRC2_LOCK_EINT */ +#define WM5100_ASRC2_LOCK_EINT_MASK 0x0100 /* ASRC2_LOCK_EINT */ +#define WM5100_ASRC2_LOCK_EINT_SHIFT 8 /* ASRC2_LOCK_EINT */ +#define WM5100_ASRC2_LOCK_EINT_WIDTH 1 /* ASRC2_LOCK_EINT */ +#define WM5100_ASRC1_LOCK_EINT 0x0080 /* ASRC1_LOCK_EINT */ +#define WM5100_ASRC1_LOCK_EINT_MASK 0x0080 /* ASRC1_LOCK_EINT */ +#define WM5100_ASRC1_LOCK_EINT_SHIFT 7 /* ASRC1_LOCK_EINT */ +#define WM5100_ASRC1_LOCK_EINT_WIDTH 1 /* ASRC1_LOCK_EINT */ +#define WM5100_FLL2_LOCK_EINT 0x0008 /* FLL2_LOCK_EINT */ +#define WM5100_FLL2_LOCK_EINT_MASK 0x0008 /* FLL2_LOCK_EINT */ +#define WM5100_FLL2_LOCK_EINT_SHIFT 3 /* FLL2_LOCK_EINT */ +#define WM5100_FLL2_LOCK_EINT_WIDTH 1 /* FLL2_LOCK_EINT */ +#define WM5100_FLL1_LOCK_EINT 0x0004 /* FLL1_LOCK_EINT */ +#define WM5100_FLL1_LOCK_EINT_MASK 0x0004 /* FLL1_LOCK_EINT */ +#define WM5100_FLL1_LOCK_EINT_SHIFT 2 /* FLL1_LOCK_EINT */ +#define WM5100_FLL1_LOCK_EINT_WIDTH 1 /* FLL1_LOCK_EINT */ +#define WM5100_CLKGEN_ERR_EINT 0x0002 /* CLKGEN_ERR_EINT */ +#define WM5100_CLKGEN_ERR_EINT_MASK 0x0002 /* CLKGEN_ERR_EINT */ +#define WM5100_CLKGEN_ERR_EINT_SHIFT 1 /* CLKGEN_ERR_EINT */ +#define WM5100_CLKGEN_ERR_EINT_WIDTH 1 /* CLKGEN_ERR_EINT */ +#define WM5100_CLKGEN_ERR_ASYNC_EINT 0x0001 /* CLKGEN_ERR_ASYNC_EINT */ +#define WM5100_CLKGEN_ERR_ASYNC_EINT_MASK 0x0001 /* CLKGEN_ERR_ASYNC_EINT */ +#define WM5100_CLKGEN_ERR_ASYNC_EINT_SHIFT 0 /* CLKGEN_ERR_ASYNC_EINT */ +#define WM5100_CLKGEN_ERR_ASYNC_EINT_WIDTH 1 /* CLKGEN_ERR_ASYNC_EINT */ + +/* + * R3331 (0xD03) - Interrupt Status 4 + */ +#define WM5100_AIF3_ERR_EINT 0x2000 /* AIF3_ERR_EINT */ +#define WM5100_AIF3_ERR_EINT_MASK 0x2000 /* AIF3_ERR_EINT */ +#define WM5100_AIF3_ERR_EINT_SHIFT 13 /* AIF3_ERR_EINT */ +#define WM5100_AIF3_ERR_EINT_WIDTH 1 /* AIF3_ERR_EINT */ +#define WM5100_AIF2_ERR_EINT 0x1000 /* AIF2_ERR_EINT */ +#define WM5100_AIF2_ERR_EINT_MASK 0x1000 /* AIF2_ERR_EINT */ +#define WM5100_AIF2_ERR_EINT_SHIFT 12 /* AIF2_ERR_EINT */ +#define WM5100_AIF2_ERR_EINT_WIDTH 1 /* AIF2_ERR_EINT */ +#define WM5100_AIF1_ERR_EINT 0x0800 /* AIF1_ERR_EINT */ +#define WM5100_AIF1_ERR_EINT_MASK 0x0800 /* AIF1_ERR_EINT */ +#define WM5100_AIF1_ERR_EINT_SHIFT 11 /* AIF1_ERR_EINT */ +#define WM5100_AIF1_ERR_EINT_WIDTH 1 /* AIF1_ERR_EINT */ +#define WM5100_CTRLIF_ERR_EINT 0x0400 /* CTRLIF_ERR_EINT */ +#define WM5100_CTRLIF_ERR_EINT_MASK 0x0400 /* CTRLIF_ERR_EINT */ +#define WM5100_CTRLIF_ERR_EINT_SHIFT 10 /* CTRLIF_ERR_EINT */ +#define WM5100_CTRLIF_ERR_EINT_WIDTH 1 /* CTRLIF_ERR_EINT */ +#define WM5100_ISRC2_UNDERCLOCKED_EINT 0x0200 /* ISRC2_UNDERCLOCKED_EINT */ +#define WM5100_ISRC2_UNDERCLOCKED_EINT_MASK 0x0200 /* ISRC2_UNDERCLOCKED_EINT */ +#define WM5100_ISRC2_UNDERCLOCKED_EINT_SHIFT 9 /* ISRC2_UNDERCLOCKED_EINT */ +#define WM5100_ISRC2_UNDERCLOCKED_EINT_WIDTH 1 /* ISRC2_UNDERCLOCKED_EINT */ +#define WM5100_ISRC1_UNDERCLOCKED_EINT 0x0100 /* ISRC1_UNDERCLOCKED_EINT */ +#define WM5100_ISRC1_UNDERCLOCKED_EINT_MASK 0x0100 /* ISRC1_UNDERCLOCKED_EINT */ +#define WM5100_ISRC1_UNDERCLOCKED_EINT_SHIFT 8 /* ISRC1_UNDERCLOCKED_EINT */ +#define WM5100_ISRC1_UNDERCLOCKED_EINT_WIDTH 1 /* ISRC1_UNDERCLOCKED_EINT */ +#define WM5100_FX_UNDERCLOCKED_EINT 0x0080 /* FX_UNDERCLOCKED_EINT */ +#define WM5100_FX_UNDERCLOCKED_EINT_MASK 0x0080 /* FX_UNDERCLOCKED_EINT */ +#define WM5100_FX_UNDERCLOCKED_EINT_SHIFT 7 /* FX_UNDERCLOCKED_EINT */ +#define WM5100_FX_UNDERCLOCKED_EINT_WIDTH 1 /* FX_UNDERCLOCKED_EINT */ +#define WM5100_AIF3_UNDERCLOCKED_EINT 0x0040 /* AIF3_UNDERCLOCKED_EINT */ +#define WM5100_AIF3_UNDERCLOCKED_EINT_MASK 0x0040 /* AIF3_UNDERCLOCKED_EINT */ +#define WM5100_AIF3_UNDERCLOCKED_EINT_SHIFT 6 /* AIF3_UNDERCLOCKED_EINT */ +#define WM5100_AIF3_UNDERCLOCKED_EINT_WIDTH 1 /* AIF3_UNDERCLOCKED_EINT */ +#define WM5100_AIF2_UNDERCLOCKED_EINT 0x0020 /* AIF2_UNDERCLOCKED_EINT */ +#define WM5100_AIF2_UNDERCLOCKED_EINT_MASK 0x0020 /* AIF2_UNDERCLOCKED_EINT */ +#define WM5100_AIF2_UNDERCLOCKED_EINT_SHIFT 5 /* AIF2_UNDERCLOCKED_EINT */ +#define WM5100_AIF2_UNDERCLOCKED_EINT_WIDTH 1 /* AIF2_UNDERCLOCKED_EINT */ +#define WM5100_AIF1_UNDERCLOCKED_EINT 0x0010 /* AIF1_UNDERCLOCKED_EINT */ +#define WM5100_AIF1_UNDERCLOCKED_EINT_MASK 0x0010 /* AIF1_UNDERCLOCKED_EINT */ +#define WM5100_AIF1_UNDERCLOCKED_EINT_SHIFT 4 /* AIF1_UNDERCLOCKED_EINT */ +#define WM5100_AIF1_UNDERCLOCKED_EINT_WIDTH 1 /* AIF1_UNDERCLOCKED_EINT */ +#define WM5100_ASRC_UNDERCLOCKED_EINT 0x0008 /* ASRC_UNDERCLOCKED_EINT */ +#define WM5100_ASRC_UNDERCLOCKED_EINT_MASK 0x0008 /* ASRC_UNDERCLOCKED_EINT */ +#define WM5100_ASRC_UNDERCLOCKED_EINT_SHIFT 3 /* ASRC_UNDERCLOCKED_EINT */ +#define WM5100_ASRC_UNDERCLOCKED_EINT_WIDTH 1 /* ASRC_UNDERCLOCKED_EINT */ +#define WM5100_DAC_UNDERCLOCKED_EINT 0x0004 /* DAC_UNDERCLOCKED_EINT */ +#define WM5100_DAC_UNDERCLOCKED_EINT_MASK 0x0004 /* DAC_UNDERCLOCKED_EINT */ +#define WM5100_DAC_UNDERCLOCKED_EINT_SHIFT 2 /* DAC_UNDERCLOCKED_EINT */ +#define WM5100_DAC_UNDERCLOCKED_EINT_WIDTH 1 /* DAC_UNDERCLOCKED_EINT */ +#define WM5100_ADC_UNDERCLOCKED_EINT 0x0002 /* ADC_UNDERCLOCKED_EINT */ +#define WM5100_ADC_UNDERCLOCKED_EINT_MASK 0x0002 /* ADC_UNDERCLOCKED_EINT */ +#define WM5100_ADC_UNDERCLOCKED_EINT_SHIFT 1 /* ADC_UNDERCLOCKED_EINT */ +#define WM5100_ADC_UNDERCLOCKED_EINT_WIDTH 1 /* ADC_UNDERCLOCKED_EINT */ +#define WM5100_MIXER_UNDERCLOCKED_EINT 0x0001 /* MIXER_UNDERCLOCKED_EINT */ +#define WM5100_MIXER_UNDERCLOCKED_EINT_MASK 0x0001 /* MIXER_UNDERCLOCKED_EINT */ +#define WM5100_MIXER_UNDERCLOCKED_EINT_SHIFT 0 /* MIXER_UNDERCLOCKED_EINT */ +#define WM5100_MIXER_UNDERCLOCKED_EINT_WIDTH 1 /* MIXER_UNDERCLOCKED_EINT */ + +/* + * R3332 (0xD04) - Interrupt Raw Status 2 + */ +#define WM5100_DSP_IRQ6_STS 0x0020 /* DSP_IRQ6_STS */ +#define WM5100_DSP_IRQ6_STS_MASK 0x0020 /* DSP_IRQ6_STS */ +#define WM5100_DSP_IRQ6_STS_SHIFT 5 /* DSP_IRQ6_STS */ +#define WM5100_DSP_IRQ6_STS_WIDTH 1 /* DSP_IRQ6_STS */ +#define WM5100_DSP_IRQ5_STS 0x0010 /* DSP_IRQ5_STS */ +#define WM5100_DSP_IRQ5_STS_MASK 0x0010 /* DSP_IRQ5_STS */ +#define WM5100_DSP_IRQ5_STS_SHIFT 4 /* DSP_IRQ5_STS */ +#define WM5100_DSP_IRQ5_STS_WIDTH 1 /* DSP_IRQ5_STS */ +#define WM5100_DSP_IRQ4_STS 0x0008 /* DSP_IRQ4_STS */ +#define WM5100_DSP_IRQ4_STS_MASK 0x0008 /* DSP_IRQ4_STS */ +#define WM5100_DSP_IRQ4_STS_SHIFT 3 /* DSP_IRQ4_STS */ +#define WM5100_DSP_IRQ4_STS_WIDTH 1 /* DSP_IRQ4_STS */ +#define WM5100_DSP_IRQ3_STS 0x0004 /* DSP_IRQ3_STS */ +#define WM5100_DSP_IRQ3_STS_MASK 0x0004 /* DSP_IRQ3_STS */ +#define WM5100_DSP_IRQ3_STS_SHIFT 2 /* DSP_IRQ3_STS */ +#define WM5100_DSP_IRQ3_STS_WIDTH 1 /* DSP_IRQ3_STS */ +#define WM5100_DSP_IRQ2_STS 0x0002 /* DSP_IRQ2_STS */ +#define WM5100_DSP_IRQ2_STS_MASK 0x0002 /* DSP_IRQ2_STS */ +#define WM5100_DSP_IRQ2_STS_SHIFT 1 /* DSP_IRQ2_STS */ +#define WM5100_DSP_IRQ2_STS_WIDTH 1 /* DSP_IRQ2_STS */ +#define WM5100_DSP_IRQ1_STS 0x0001 /* DSP_IRQ1_STS */ +#define WM5100_DSP_IRQ1_STS_MASK 0x0001 /* DSP_IRQ1_STS */ +#define WM5100_DSP_IRQ1_STS_SHIFT 0 /* DSP_IRQ1_STS */ +#define WM5100_DSP_IRQ1_STS_WIDTH 1 /* DSP_IRQ1_STS */ + +/* + * R3333 (0xD05) - Interrupt Raw Status 3 + */ +#define WM5100_SPK_SHUTDOWN_WARN_STS 0x8000 /* SPK_SHUTDOWN_WARN_STS */ +#define WM5100_SPK_SHUTDOWN_WARN_STS_MASK 0x8000 /* SPK_SHUTDOWN_WARN_STS */ +#define WM5100_SPK_SHUTDOWN_WARN_STS_SHIFT 15 /* SPK_SHUTDOWN_WARN_STS */ +#define WM5100_SPK_SHUTDOWN_WARN_STS_WIDTH 1 /* SPK_SHUTDOWN_WARN_STS */ +#define WM5100_SPK_SHUTDOWN_STS 0x4000 /* SPK_SHUTDOWN_STS */ +#define WM5100_SPK_SHUTDOWN_STS_MASK 0x4000 /* SPK_SHUTDOWN_STS */ +#define WM5100_SPK_SHUTDOWN_STS_SHIFT 14 /* SPK_SHUTDOWN_STS */ +#define WM5100_SPK_SHUTDOWN_STS_WIDTH 1 /* SPK_SHUTDOWN_STS */ +#define WM5100_HPDET_STS 0x2000 /* HPDET_STS */ +#define WM5100_HPDET_STS_MASK 0x2000 /* HPDET_STS */ +#define WM5100_HPDET_STS_SHIFT 13 /* HPDET_STS */ +#define WM5100_HPDET_STS_WIDTH 1 /* HPDET_STS */ +#define WM5100_DRC_SID_DET_STS 0x0200 /* DRC_SID_DET_STS */ +#define WM5100_DRC_SID_DET_STS_MASK 0x0200 /* DRC_SID_DET_STS */ +#define WM5100_DRC_SID_DET_STS_SHIFT 9 /* DRC_SID_DET_STS */ +#define WM5100_DRC_SID_DET_STS_WIDTH 1 /* DRC_SID_DET_STS */ +#define WM5100_ASRC2_LOCK_STS 0x0100 /* ASRC2_LOCK_STS */ +#define WM5100_ASRC2_LOCK_STS_MASK 0x0100 /* ASRC2_LOCK_STS */ +#define WM5100_ASRC2_LOCK_STS_SHIFT 8 /* ASRC2_LOCK_STS */ +#define WM5100_ASRC2_LOCK_STS_WIDTH 1 /* ASRC2_LOCK_STS */ +#define WM5100_ASRC1_LOCK_STS 0x0080 /* ASRC1_LOCK_STS */ +#define WM5100_ASRC1_LOCK_STS_MASK 0x0080 /* ASRC1_LOCK_STS */ +#define WM5100_ASRC1_LOCK_STS_SHIFT 7 /* ASRC1_LOCK_STS */ +#define WM5100_ASRC1_LOCK_STS_WIDTH 1 /* ASRC1_LOCK_STS */ +#define WM5100_FLL2_LOCK_STS 0x0008 /* FLL2_LOCK_STS */ +#define WM5100_FLL2_LOCK_STS_MASK 0x0008 /* FLL2_LOCK_STS */ +#define WM5100_FLL2_LOCK_STS_SHIFT 3 /* FLL2_LOCK_STS */ +#define WM5100_FLL2_LOCK_STS_WIDTH 1 /* FLL2_LOCK_STS */ +#define WM5100_FLL1_LOCK_STS 0x0004 /* FLL1_LOCK_STS */ +#define WM5100_FLL1_LOCK_STS_MASK 0x0004 /* FLL1_LOCK_STS */ +#define WM5100_FLL1_LOCK_STS_SHIFT 2 /* FLL1_LOCK_STS */ +#define WM5100_FLL1_LOCK_STS_WIDTH 1 /* FLL1_LOCK_STS */ +#define WM5100_CLKGEN_ERR_STS 0x0002 /* CLKGEN_ERR_STS */ +#define WM5100_CLKGEN_ERR_STS_MASK 0x0002 /* CLKGEN_ERR_STS */ +#define WM5100_CLKGEN_ERR_STS_SHIFT 1 /* CLKGEN_ERR_STS */ +#define WM5100_CLKGEN_ERR_STS_WIDTH 1 /* CLKGEN_ERR_STS */ +#define WM5100_CLKGEN_ERR_ASYNC_STS 0x0001 /* CLKGEN_ERR_ASYNC_STS */ +#define WM5100_CLKGEN_ERR_ASYNC_STS_MASK 0x0001 /* CLKGEN_ERR_ASYNC_STS */ +#define WM5100_CLKGEN_ERR_ASYNC_STS_SHIFT 0 /* CLKGEN_ERR_ASYNC_STS */ +#define WM5100_CLKGEN_ERR_ASYNC_STS_WIDTH 1 /* CLKGEN_ERR_ASYNC_STS */ + +/* + * R3334 (0xD06) - Interrupt Raw Status 4 + */ +#define WM5100_AIF3_ERR_STS 0x2000 /* AIF3_ERR_STS */ +#define WM5100_AIF3_ERR_STS_MASK 0x2000 /* AIF3_ERR_STS */ +#define WM5100_AIF3_ERR_STS_SHIFT 13 /* AIF3_ERR_STS */ +#define WM5100_AIF3_ERR_STS_WIDTH 1 /* AIF3_ERR_STS */ +#define WM5100_AIF2_ERR_STS 0x1000 /* AIF2_ERR_STS */ +#define WM5100_AIF2_ERR_STS_MASK 0x1000 /* AIF2_ERR_STS */ +#define WM5100_AIF2_ERR_STS_SHIFT 12 /* AIF2_ERR_STS */ +#define WM5100_AIF2_ERR_STS_WIDTH 1 /* AIF2_ERR_STS */ +#define WM5100_AIF1_ERR_STS 0x0800 /* AIF1_ERR_STS */ +#define WM5100_AIF1_ERR_STS_MASK 0x0800 /* AIF1_ERR_STS */ +#define WM5100_AIF1_ERR_STS_SHIFT 11 /* AIF1_ERR_STS */ +#define WM5100_AIF1_ERR_STS_WIDTH 1 /* AIF1_ERR_STS */ +#define WM5100_CTRLIF_ERR_STS 0x0400 /* CTRLIF_ERR_STS */ +#define WM5100_CTRLIF_ERR_STS_MASK 0x0400 /* CTRLIF_ERR_STS */ +#define WM5100_CTRLIF_ERR_STS_SHIFT 10 /* CTRLIF_ERR_STS */ +#define WM5100_CTRLIF_ERR_STS_WIDTH 1 /* CTRLIF_ERR_STS */ +#define WM5100_ISRC2_UNDERCLOCKED_STS 0x0200 /* ISRC2_UNDERCLOCKED_STS */ +#define WM5100_ISRC2_UNDERCLOCKED_STS_MASK 0x0200 /* ISRC2_UNDERCLOCKED_STS */ +#define WM5100_ISRC2_UNDERCLOCKED_STS_SHIFT 9 /* ISRC2_UNDERCLOCKED_STS */ +#define WM5100_ISRC2_UNDERCLOCKED_STS_WIDTH 1 /* ISRC2_UNDERCLOCKED_STS */ +#define WM5100_ISRC1_UNDERCLOCKED_STS 0x0100 /* ISRC1_UNDERCLOCKED_STS */ +#define WM5100_ISRC1_UNDERCLOCKED_STS_MASK 0x0100 /* ISRC1_UNDERCLOCKED_STS */ +#define WM5100_ISRC1_UNDERCLOCKED_STS_SHIFT 8 /* ISRC1_UNDERCLOCKED_STS */ +#define WM5100_ISRC1_UNDERCLOCKED_STS_WIDTH 1 /* ISRC1_UNDERCLOCKED_STS */ +#define WM5100_FX_UNDERCLOCKED_STS 0x0080 /* FX_UNDERCLOCKED_STS */ +#define WM5100_FX_UNDERCLOCKED_STS_MASK 0x0080 /* FX_UNDERCLOCKED_STS */ +#define WM5100_FX_UNDERCLOCKED_STS_SHIFT 7 /* FX_UNDERCLOCKED_STS */ +#define WM5100_FX_UNDERCLOCKED_STS_WIDTH 1 /* FX_UNDERCLOCKED_STS */ +#define WM5100_AIF3_UNDERCLOCKED_STS 0x0040 /* AIF3_UNDERCLOCKED_STS */ +#define WM5100_AIF3_UNDERCLOCKED_STS_MASK 0x0040 /* AIF3_UNDERCLOCKED_STS */ +#define WM5100_AIF3_UNDERCLOCKED_STS_SHIFT 6 /* AIF3_UNDERCLOCKED_STS */ +#define WM5100_AIF3_UNDERCLOCKED_STS_WIDTH 1 /* AIF3_UNDERCLOCKED_STS */ +#define WM5100_AIF2_UNDERCLOCKED_STS 0x0020 /* AIF2_UNDERCLOCKED_STS */ +#define WM5100_AIF2_UNDERCLOCKED_STS_MASK 0x0020 /* AIF2_UNDERCLOCKED_STS */ +#define WM5100_AIF2_UNDERCLOCKED_STS_SHIFT 5 /* AIF2_UNDERCLOCKED_STS */ +#define WM5100_AIF2_UNDERCLOCKED_STS_WIDTH 1 /* AIF2_UNDERCLOCKED_STS */ +#define WM5100_AIF1_UNDERCLOCKED_STS 0x0010 /* AIF1_UNDERCLOCKED_STS */ +#define WM5100_AIF1_UNDERCLOCKED_STS_MASK 0x0010 /* AIF1_UNDERCLOCKED_STS */ +#define WM5100_AIF1_UNDERCLOCKED_STS_SHIFT 4 /* AIF1_UNDERCLOCKED_STS */ +#define WM5100_AIF1_UNDERCLOCKED_STS_WIDTH 1 /* AIF1_UNDERCLOCKED_STS */ +#define WM5100_ASRC_UNDERCLOCKED_STS 0x0008 /* ASRC_UNDERCLOCKED_STS */ +#define WM5100_ASRC_UNDERCLOCKED_STS_MASK 0x0008 /* ASRC_UNDERCLOCKED_STS */ +#define WM5100_ASRC_UNDERCLOCKED_STS_SHIFT 3 /* ASRC_UNDERCLOCKED_STS */ +#define WM5100_ASRC_UNDERCLOCKED_STS_WIDTH 1 /* ASRC_UNDERCLOCKED_STS */ +#define WM5100_DAC_UNDERCLOCKED_STS 0x0004 /* DAC_UNDERCLOCKED_STS */ +#define WM5100_DAC_UNDERCLOCKED_STS_MASK 0x0004 /* DAC_UNDERCLOCKED_STS */ +#define WM5100_DAC_UNDERCLOCKED_STS_SHIFT 2 /* DAC_UNDERCLOCKED_STS */ +#define WM5100_DAC_UNDERCLOCKED_STS_WIDTH 1 /* DAC_UNDERCLOCKED_STS */ +#define WM5100_ADC_UNDERCLOCKED_STS 0x0002 /* ADC_UNDERCLOCKED_STS */ +#define WM5100_ADC_UNDERCLOCKED_STS_MASK 0x0002 /* ADC_UNDERCLOCKED_STS */ +#define WM5100_ADC_UNDERCLOCKED_STS_SHIFT 1 /* ADC_UNDERCLOCKED_STS */ +#define WM5100_ADC_UNDERCLOCKED_STS_WIDTH 1 /* ADC_UNDERCLOCKED_STS */ +#define WM5100_MIXER_UNDERCLOCKED_STS 0x0001 /* MIXER_UNDERCLOCKED_STS */ +#define WM5100_MIXER_UNDERCLOCKED_STS_MASK 0x0001 /* MIXER_UNDERCLOCKED_STS */ +#define WM5100_MIXER_UNDERCLOCKED_STS_SHIFT 0 /* MIXER_UNDERCLOCKED_STS */ +#define WM5100_MIXER_UNDERCLOCKED_STS_WIDTH 1 /* MIXER_UNDERCLOCKED_STS */ + +/* + * R3335 (0xD07) - Interrupt Status 1 Mask + */ +#define WM5100_IM_GP6_EINT 0x0020 /* IM_GP6_EINT */ +#define WM5100_IM_GP6_EINT_MASK 0x0020 /* IM_GP6_EINT */ +#define WM5100_IM_GP6_EINT_SHIFT 5 /* IM_GP6_EINT */ +#define WM5100_IM_GP6_EINT_WIDTH 1 /* IM_GP6_EINT */ +#define WM5100_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */ +#define WM5100_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */ +#define WM5100_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */ +#define WM5100_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */ +#define WM5100_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */ +#define WM5100_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */ +#define WM5100_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */ +#define WM5100_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */ +#define WM5100_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */ +#define WM5100_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */ +#define WM5100_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */ +#define WM5100_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */ +#define WM5100_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */ +#define WM5100_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */ +#define WM5100_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */ +#define WM5100_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */ +#define WM5100_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */ +#define WM5100_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */ +#define WM5100_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */ +#define WM5100_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */ + +/* + * R3336 (0xD08) - Interrupt Status 2 Mask + */ +#define WM5100_IM_DSP_IRQ6_EINT 0x0020 /* IM_DSP_IRQ6_EINT */ +#define WM5100_IM_DSP_IRQ6_EINT_MASK 0x0020 /* IM_DSP_IRQ6_EINT */ +#define WM5100_IM_DSP_IRQ6_EINT_SHIFT 5 /* IM_DSP_IRQ6_EINT */ +#define WM5100_IM_DSP_IRQ6_EINT_WIDTH 1 /* IM_DSP_IRQ6_EINT */ +#define WM5100_IM_DSP_IRQ5_EINT 0x0010 /* IM_DSP_IRQ5_EINT */ +#define WM5100_IM_DSP_IRQ5_EINT_MASK 0x0010 /* IM_DSP_IRQ5_EINT */ +#define WM5100_IM_DSP_IRQ5_EINT_SHIFT 4 /* IM_DSP_IRQ5_EINT */ +#define WM5100_IM_DSP_IRQ5_EINT_WIDTH 1 /* IM_DSP_IRQ5_EINT */ +#define WM5100_IM_DSP_IRQ4_EINT 0x0008 /* IM_DSP_IRQ4_EINT */ +#define WM5100_IM_DSP_IRQ4_EINT_MASK 0x0008 /* IM_DSP_IRQ4_EINT */ +#define WM5100_IM_DSP_IRQ4_EINT_SHIFT 3 /* IM_DSP_IRQ4_EINT */ +#define WM5100_IM_DSP_IRQ4_EINT_WIDTH 1 /* IM_DSP_IRQ4_EINT */ +#define WM5100_IM_DSP_IRQ3_EINT 0x0004 /* IM_DSP_IRQ3_EINT */ +#define WM5100_IM_DSP_IRQ3_EINT_MASK 0x0004 /* IM_DSP_IRQ3_EINT */ +#define WM5100_IM_DSP_IRQ3_EINT_SHIFT 2 /* IM_DSP_IRQ3_EINT */ +#define WM5100_IM_DSP_IRQ3_EINT_WIDTH 1 /* IM_DSP_IRQ3_EINT */ +#define WM5100_IM_DSP_IRQ2_EINT 0x0002 /* IM_DSP_IRQ2_EINT */ +#define WM5100_IM_DSP_IRQ2_EINT_MASK 0x0002 /* IM_DSP_IRQ2_EINT */ +#define WM5100_IM_DSP_IRQ2_EINT_SHIFT 1 /* IM_DSP_IRQ2_EINT */ +#define WM5100_IM_DSP_IRQ2_EINT_WIDTH 1 /* IM_DSP_IRQ2_EINT */ +#define WM5100_IM_DSP_IRQ1_EINT 0x0001 /* IM_DSP_IRQ1_EINT */ +#define WM5100_IM_DSP_IRQ1_EINT_MASK 0x0001 /* IM_DSP_IRQ1_EINT */ +#define WM5100_IM_DSP_IRQ1_EINT_SHIFT 0 /* IM_DSP_IRQ1_EINT */ +#define WM5100_IM_DSP_IRQ1_EINT_WIDTH 1 /* IM_DSP_IRQ1_EINT */ + +/* + * R3337 (0xD09) - Interrupt Status 3 Mask + */ +#define WM5100_IM_SPK_SHUTDOWN_WARN_EINT 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT */ +#define WM5100_IM_SPK_SHUTDOWN_WARN_EINT_MASK 0x8000 /* IM_SPK_SHUTDOWN_WARN_EINT */ +#define WM5100_IM_SPK_SHUTDOWN_WARN_EINT_SHIFT 15 /* IM_SPK_SHUTDOWN_WARN_EINT */ +#define WM5100_IM_SPK_SHUTDOWN_WARN_EINT_WIDTH 1 /* IM_SPK_SHUTDOWN_WARN_EINT */ +#define WM5100_IM_SPK_SHUTDOWN_EINT 0x4000 /* IM_SPK_SHUTDOWN_EINT */ +#define WM5100_IM_SPK_SHUTDOWN_EINT_MASK 0x4000 /* IM_SPK_SHUTDOWN_EINT */ +#define WM5100_IM_SPK_SHUTDOWN_EINT_SHIFT 14 /* IM_SPK_SHUTDOWN_EINT */ +#define WM5100_IM_SPK_SHUTDOWN_EINT_WIDTH 1 /* IM_SPK_SHUTDOWN_EINT */ +#define WM5100_IM_HPDET_EINT 0x2000 /* IM_HPDET_EINT */ +#define WM5100_IM_HPDET_EINT_MASK 0x2000 /* IM_HPDET_EINT */ +#define WM5100_IM_HPDET_EINT_SHIFT 13 /* IM_HPDET_EINT */ +#define WM5100_IM_HPDET_EINT_WIDTH 1 /* IM_HPDET_EINT */ +#define WM5100_IM_ACCDET_EINT 0x1000 /* IM_ACCDET_EINT */ +#define WM5100_IM_ACCDET_EINT_MASK 0x1000 /* IM_ACCDET_EINT */ +#define WM5100_IM_ACCDET_EINT_SHIFT 12 /* IM_ACCDET_EINT */ +#define WM5100_IM_ACCDET_EINT_WIDTH 1 /* IM_ACCDET_EINT */ +#define WM5100_IM_DRC_SIG_DET_EINT 0x0200 /* IM_DRC_SIG_DET_EINT */ +#define WM5100_IM_DRC_SIG_DET_EINT_MASK 0x0200 /* IM_DRC_SIG_DET_EINT */ +#define WM5100_IM_DRC_SIG_DET_EINT_SHIFT 9 /* IM_DRC_SIG_DET_EINT */ +#define WM5100_IM_DRC_SIG_DET_EINT_WIDTH 1 /* IM_DRC_SIG_DET_EINT */ +#define WM5100_IM_ASRC2_LOCK_EINT 0x0100 /* IM_ASRC2_LOCK_EINT */ +#define WM5100_IM_ASRC2_LOCK_EINT_MASK 0x0100 /* IM_ASRC2_LOCK_EINT */ +#define WM5100_IM_ASRC2_LOCK_EINT_SHIFT 8 /* IM_ASRC2_LOCK_EINT */ +#define WM5100_IM_ASRC2_LOCK_EINT_WIDTH 1 /* IM_ASRC2_LOCK_EINT */ +#define WM5100_IM_ASRC1_LOCK_EINT 0x0080 /* IM_ASRC1_LOCK_EINT */ +#define WM5100_IM_ASRC1_LOCK_EINT_MASK 0x0080 /* IM_ASRC1_LOCK_EINT */ +#define WM5100_IM_ASRC1_LOCK_EINT_SHIFT 7 /* IM_ASRC1_LOCK_EINT */ +#define WM5100_IM_ASRC1_LOCK_EINT_WIDTH 1 /* IM_ASRC1_LOCK_EINT */ +#define WM5100_IM_FLL2_LOCK_EINT 0x0008 /* IM_FLL2_LOCK_EINT */ +#define WM5100_IM_FLL2_LOCK_EINT_MASK 0x0008 /* IM_FLL2_LOCK_EINT */ +#define WM5100_IM_FLL2_LOCK_EINT_SHIFT 3 /* IM_FLL2_LOCK_EINT */ +#define WM5100_IM_FLL2_LOCK_EINT_WIDTH 1 /* IM_FLL2_LOCK_EINT */ +#define WM5100_IM_FLL1_LOCK_EINT 0x0004 /* IM_FLL1_LOCK_EINT */ +#define WM5100_IM_FLL1_LOCK_EINT_MASK 0x0004 /* IM_FLL1_LOCK_EINT */ +#define WM5100_IM_FLL1_LOCK_EINT_SHIFT 2 /* IM_FLL1_LOCK_EINT */ +#define WM5100_IM_FLL1_LOCK_EINT_WIDTH 1 /* IM_FLL1_LOCK_EINT */ +#define WM5100_IM_CLKGEN_ERR_EINT 0x0002 /* IM_CLKGEN_ERR_EINT */ +#define WM5100_IM_CLKGEN_ERR_EINT_MASK 0x0002 /* IM_CLKGEN_ERR_EINT */ +#define WM5100_IM_CLKGEN_ERR_EINT_SHIFT 1 /* IM_CLKGEN_ERR_EINT */ +#define WM5100_IM_CLKGEN_ERR_EINT_WIDTH 1 /* IM_CLKGEN_ERR_EINT */ +#define WM5100_IM_CLKGEN_ERR_ASYNC_EINT 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT */ +#define WM5100_IM_CLKGEN_ERR_ASYNC_EINT_MASK 0x0001 /* IM_CLKGEN_ERR_ASYNC_EINT */ +#define WM5100_IM_CLKGEN_ERR_ASYNC_EINT_SHIFT 0 /* IM_CLKGEN_ERR_ASYNC_EINT */ +#define WM5100_IM_CLKGEN_ERR_ASYNC_EINT_WIDTH 1 /* IM_CLKGEN_ERR_ASYNC_EINT */ + +/* + * R3338 (0xD0A) - Interrupt Status 4 Mask + */ +#define WM5100_IM_AIF3_ERR_EINT 0x2000 /* IM_AIF3_ERR_EINT */ +#define WM5100_IM_AIF3_ERR_EINT_MASK 0x2000 /* IM_AIF3_ERR_EINT */ +#define WM5100_IM_AIF3_ERR_EINT_SHIFT 13 /* IM_AIF3_ERR_EINT */ +#define WM5100_IM_AIF3_ERR_EINT_WIDTH 1 /* IM_AIF3_ERR_EINT */ +#define WM5100_IM_AIF2_ERR_EINT 0x1000 /* IM_AIF2_ERR_EINT */ +#define WM5100_IM_AIF2_ERR_EINT_MASK 0x1000 /* IM_AIF2_ERR_EINT */ +#define WM5100_IM_AIF2_ERR_EINT_SHIFT 12 /* IM_AIF2_ERR_EINT */ +#define WM5100_IM_AIF2_ERR_EINT_WIDTH 1 /* IM_AIF2_ERR_EINT */ +#define WM5100_IM_AIF1_ERR_EINT 0x0800 /* IM_AIF1_ERR_EINT */ +#define WM5100_IM_AIF1_ERR_EINT_MASK 0x0800 /* IM_AIF1_ERR_EINT */ +#define WM5100_IM_AIF1_ERR_EINT_SHIFT 11 /* IM_AIF1_ERR_EINT */ +#define WM5100_IM_AIF1_ERR_EINT_WIDTH 1 /* IM_AIF1_ERR_EINT */ +#define WM5100_IM_CTRLIF_ERR_EINT 0x0400 /* IM_CTRLIF_ERR_EINT */ +#define WM5100_IM_CTRLIF_ERR_EINT_MASK 0x0400 /* IM_CTRLIF_ERR_EINT */ +#define WM5100_IM_CTRLIF_ERR_EINT_SHIFT 10 /* IM_CTRLIF_ERR_EINT */ +#define WM5100_IM_CTRLIF_ERR_EINT_WIDTH 1 /* IM_CTRLIF_ERR_EINT */ +#define WM5100_IM_ISRC2_UNDERCLOCKED_EINT 0x0200 /* IM_ISRC2_UNDERCLOCKED_EINT */ +#define WM5100_IM_ISRC2_UNDERCLOCKED_EINT_MASK 0x0200 /* IM_ISRC2_UNDERCLOCKED_EINT */ +#define WM5100_IM_ISRC2_UNDERCLOCKED_EINT_SHIFT 9 /* IM_ISRC2_UNDERCLOCKED_EINT */ +#define WM5100_IM_ISRC2_UNDERCLOCKED_EINT_WIDTH 1 /* IM_ISRC2_UNDERCLOCKED_EINT */ +#define WM5100_IM_ISRC1_UNDERCLOCKED_EINT 0x0100 /* IM_ISRC1_UNDERCLOCKED_EINT */ +#define WM5100_IM_ISRC1_UNDERCLOCKED_EINT_MASK 0x0100 /* IM_ISRC1_UNDERCLOCKED_EINT */ +#define WM5100_IM_ISRC1_UNDERCLOCKED_EINT_SHIFT 8 /* IM_ISRC1_UNDERCLOCKED_EINT */ +#define WM5100_IM_ISRC1_UNDERCLOCKED_EINT_WIDTH 1 /* IM_ISRC1_UNDERCLOCKED_EINT */ +#define WM5100_IM_FX_UNDERCLOCKED_EINT 0x0080 /* IM_FX_UNDERCLOCKED_EINT */ +#define WM5100_IM_FX_UNDERCLOCKED_EINT_MASK 0x0080 /* IM_FX_UNDERCLOCKED_EINT */ +#define WM5100_IM_FX_UNDERCLOCKED_EINT_SHIFT 7 /* IM_FX_UNDERCLOCKED_EINT */ +#define WM5100_IM_FX_UNDERCLOCKED_EINT_WIDTH 1 /* IM_FX_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF3_UNDERCLOCKED_EINT 0x0040 /* IM_AIF3_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF3_UNDERCLOCKED_EINT_MASK 0x0040 /* IM_AIF3_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF3_UNDERCLOCKED_EINT_SHIFT 6 /* IM_AIF3_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF3_UNDERCLOCKED_EINT_WIDTH 1 /* IM_AIF3_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF2_UNDERCLOCKED_EINT 0x0020 /* IM_AIF2_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF2_UNDERCLOCKED_EINT_MASK 0x0020 /* IM_AIF2_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF2_UNDERCLOCKED_EINT_SHIFT 5 /* IM_AIF2_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF2_UNDERCLOCKED_EINT_WIDTH 1 /* IM_AIF2_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF1_UNDERCLOCKED_EINT 0x0010 /* IM_AIF1_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF1_UNDERCLOCKED_EINT_MASK 0x0010 /* IM_AIF1_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF1_UNDERCLOCKED_EINT_SHIFT 4 /* IM_AIF1_UNDERCLOCKED_EINT */ +#define WM5100_IM_AIF1_UNDERCLOCKED_EINT_WIDTH 1 /* IM_AIF1_UNDERCLOCKED_EINT */ +#define WM5100_IM_ASRC_UNDERCLOCKED_EINT 0x0008 /* IM_ASRC_UNDERCLOCKED_EINT */ +#define WM5100_IM_ASRC_UNDERCLOCKED_EINT_MASK 0x0008 /* IM_ASRC_UNDERCLOCKED_EINT */ +#define WM5100_IM_ASRC_UNDERCLOCKED_EINT_SHIFT 3 /* IM_ASRC_UNDERCLOCKED_EINT */ +#define WM5100_IM_ASRC_UNDERCLOCKED_EINT_WIDTH 1 /* IM_ASRC_UNDERCLOCKED_EINT */ +#define WM5100_IM_DAC_UNDERCLOCKED_EINT 0x0004 /* IM_DAC_UNDERCLOCKED_EINT */ +#define WM5100_IM_DAC_UNDERCLOCKED_EINT_MASK 0x0004 /* IM_DAC_UNDERCLOCKED_EINT */ +#define WM5100_IM_DAC_UNDERCLOCKED_EINT_SHIFT 2 /* IM_DAC_UNDERCLOCKED_EINT */ +#define WM5100_IM_DAC_UNDERCLOCKED_EINT_WIDTH 1 /* IM_DAC_UNDERCLOCKED_EINT */ +#define WM5100_IM_ADC_UNDERCLOCKED_EINT 0x0002 /* IM_ADC_UNDERCLOCKED_EINT */ +#define WM5100_IM_ADC_UNDERCLOCKED_EINT_MASK 0x0002 /* IM_ADC_UNDERCLOCKED_EINT */ +#define WM5100_IM_ADC_UNDERCLOCKED_EINT_SHIFT 1 /* IM_ADC_UNDERCLOCKED_EINT */ +#define WM5100_IM_ADC_UNDERCLOCKED_EINT_WIDTH 1 /* IM_ADC_UNDERCLOCKED_EINT */ +#define WM5100_IM_MIXER_UNDERCLOCKED_EINT 0x0001 /* IM_MIXER_UNDERCLOCKED_EINT */ +#define WM5100_IM_MIXER_UNDERCLOCKED_EINT_MASK 0x0001 /* IM_MIXER_UNDERCLOCKED_EINT */ +#define WM5100_IM_MIXER_UNDERCLOCKED_EINT_SHIFT 0 /* IM_MIXER_UNDERCLOCKED_EINT */ +#define WM5100_IM_MIXER_UNDERCLOCKED_EINT_WIDTH 1 /* IM_MIXER_UNDERCLOCKED_EINT */ + +/* + * R3359 (0xD1F) - Interrupt Control + */ +#define WM5100_IM_IRQ 0x0001 /* IM_IRQ */ +#define WM5100_IM_IRQ_MASK 0x0001 /* IM_IRQ */ +#define WM5100_IM_IRQ_SHIFT 0 /* IM_IRQ */ +#define WM5100_IM_IRQ_WIDTH 1 /* IM_IRQ */ + +/* + * R3360 (0xD20) - IRQ Debounce 1 + */ +#define WM5100_SPK_SHUTDOWN_WARN_DB 0x0200 /* SPK_SHUTDOWN_WARN_DB */ +#define WM5100_SPK_SHUTDOWN_WARN_DB_MASK 0x0200 /* SPK_SHUTDOWN_WARN_DB */ +#define WM5100_SPK_SHUTDOWN_WARN_DB_SHIFT 9 /* SPK_SHUTDOWN_WARN_DB */ +#define WM5100_SPK_SHUTDOWN_WARN_DB_WIDTH 1 /* SPK_SHUTDOWN_WARN_DB */ +#define WM5100_SPK_SHUTDOWN_DB 0x0100 /* SPK_SHUTDOWN_DB */ +#define WM5100_SPK_SHUTDOWN_DB_MASK 0x0100 /* SPK_SHUTDOWN_DB */ +#define WM5100_SPK_SHUTDOWN_DB_SHIFT 8 /* SPK_SHUTDOWN_DB */ +#define WM5100_SPK_SHUTDOWN_DB_WIDTH 1 /* SPK_SHUTDOWN_DB */ +#define WM5100_FLL1_LOCK_IRQ_DB 0x0008 /* FLL1_LOCK_IRQ_DB */ +#define WM5100_FLL1_LOCK_IRQ_DB_MASK 0x0008 /* FLL1_LOCK_IRQ_DB */ +#define WM5100_FLL1_LOCK_IRQ_DB_SHIFT 3 /* FLL1_LOCK_IRQ_DB */ +#define WM5100_FLL1_LOCK_IRQ_DB_WIDTH 1 /* FLL1_LOCK_IRQ_DB */ +#define WM5100_FLL2_LOCK_IRQ_DB 0x0004 /* FLL2_LOCK_IRQ_DB */ +#define WM5100_FLL2_LOCK_IRQ_DB_MASK 0x0004 /* FLL2_LOCK_IRQ_DB */ +#define WM5100_FLL2_LOCK_IRQ_DB_SHIFT 2 /* FLL2_LOCK_IRQ_DB */ +#define WM5100_FLL2_LOCK_IRQ_DB_WIDTH 1 /* FLL2_LOCK_IRQ_DB */ +#define WM5100_CLKGEN_ERR_IRQ_DB 0x0002 /* CLKGEN_ERR_IRQ_DB */ +#define WM5100_CLKGEN_ERR_IRQ_DB_MASK 0x0002 /* CLKGEN_ERR_IRQ_DB */ +#define WM5100_CLKGEN_ERR_IRQ_DB_SHIFT 1 /* CLKGEN_ERR_IRQ_DB */ +#define WM5100_CLKGEN_ERR_IRQ_DB_WIDTH 1 /* CLKGEN_ERR_IRQ_DB */ +#define WM5100_CLKGEN_ERR_ASYNC_IRQ_DB 0x0001 /* CLKGEN_ERR_ASYNC_IRQ_DB */ +#define WM5100_CLKGEN_ERR_ASYNC_IRQ_DB_MASK 0x0001 /* CLKGEN_ERR_ASYNC_IRQ_DB */ +#define WM5100_CLKGEN_ERR_ASYNC_IRQ_DB_SHIFT 0 /* CLKGEN_ERR_ASYNC_IRQ_DB */ +#define WM5100_CLKGEN_ERR_ASYNC_IRQ_DB_WIDTH 1 /* CLKGEN_ERR_ASYNC_IRQ_DB */ + +/* + * R3361 (0xD21) - IRQ Debounce 2 + */ +#define WM5100_AIF_ERR_DB 0x0001 /* AIF_ERR_DB */ +#define WM5100_AIF_ERR_DB_MASK 0x0001 /* AIF_ERR_DB */ +#define WM5100_AIF_ERR_DB_SHIFT 0 /* AIF_ERR_DB */ +#define WM5100_AIF_ERR_DB_WIDTH 1 /* AIF_ERR_DB */ + +/* + * R3584 (0xE00) - FX_Ctrl + */ +#define WM5100_FX_STS_MASK 0xFFC0 /* FX_STS - [15:6] */ +#define WM5100_FX_STS_SHIFT 6 /* FX_STS - [15:6] */ +#define WM5100_FX_STS_WIDTH 10 /* FX_STS - [15:6] */ +#define WM5100_FX_RATE_MASK 0x0003 /* FX_RATE - [1:0] */ +#define WM5100_FX_RATE_SHIFT 0 /* FX_RATE - [1:0] */ +#define WM5100_FX_RATE_WIDTH 2 /* FX_RATE - [1:0] */ + +/* + * R3600 (0xE10) - EQ1_1 + */ +#define WM5100_EQ1_B1_GAIN_MASK 0xF800 /* EQ1_B1_GAIN - [15:11] */ +#define WM5100_EQ1_B1_GAIN_SHIFT 11 /* EQ1_B1_GAIN - [15:11] */ +#define WM5100_EQ1_B1_GAIN_WIDTH 5 /* EQ1_B1_GAIN - [15:11] */ +#define WM5100_EQ1_B2_GAIN_MASK 0x07C0 /* EQ1_B2_GAIN - [10:6] */ +#define WM5100_EQ1_B2_GAIN_SHIFT 6 /* EQ1_B2_GAIN - [10:6] */ +#define WM5100_EQ1_B2_GAIN_WIDTH 5 /* EQ1_B2_GAIN - [10:6] */ +#define WM5100_EQ1_B3_GAIN_MASK 0x003E /* EQ1_B3_GAIN - [5:1] */ +#define WM5100_EQ1_B3_GAIN_SHIFT 1 /* EQ1_B3_GAIN - [5:1] */ +#define WM5100_EQ1_B3_GAIN_WIDTH 5 /* EQ1_B3_GAIN - [5:1] */ +#define WM5100_EQ1_ENA 0x0001 /* EQ1_ENA */ +#define WM5100_EQ1_ENA_MASK 0x0001 /* EQ1_ENA */ +#define WM5100_EQ1_ENA_SHIFT 0 /* EQ1_ENA */ +#define WM5100_EQ1_ENA_WIDTH 1 /* EQ1_ENA */ + +/* + * R3601 (0xE11) - EQ1_2 + */ +#define WM5100_EQ1_B4_GAIN_MASK 0xF800 /* EQ1_B4_GAIN - [15:11] */ +#define WM5100_EQ1_B4_GAIN_SHIFT 11 /* EQ1_B4_GAIN - [15:11] */ +#define WM5100_EQ1_B4_GAIN_WIDTH 5 /* EQ1_B4_GAIN - [15:11] */ +#define WM5100_EQ1_B5_GAIN_MASK 0x07C0 /* EQ1_B5_GAIN - [10:6] */ +#define WM5100_EQ1_B5_GAIN_SHIFT 6 /* EQ1_B5_GAIN - [10:6] */ +#define WM5100_EQ1_B5_GAIN_WIDTH 5 /* EQ1_B5_GAIN - [10:6] */ + +/* + * R3602 (0xE12) - EQ1_3 + */ +#define WM5100_EQ1_B1_A_MASK 0xFFFF /* EQ1_B1_A - [15:0] */ +#define WM5100_EQ1_B1_A_SHIFT 0 /* EQ1_B1_A - [15:0] */ +#define WM5100_EQ1_B1_A_WIDTH 16 /* EQ1_B1_A - [15:0] */ + +/* + * R3603 (0xE13) - EQ1_4 + */ +#define WM5100_EQ1_B1_B_MASK 0xFFFF /* EQ1_B1_B - [15:0] */ +#define WM5100_EQ1_B1_B_SHIFT 0 /* EQ1_B1_B - [15:0] */ +#define WM5100_EQ1_B1_B_WIDTH 16 /* EQ1_B1_B - [15:0] */ + +/* + * R3604 (0xE14) - EQ1_5 + */ +#define WM5100_EQ1_B1_PG_MASK 0xFFFF /* EQ1_B1_PG - [15:0] */ +#define WM5100_EQ1_B1_PG_SHIFT 0 /* EQ1_B1_PG - [15:0] */ +#define WM5100_EQ1_B1_PG_WIDTH 16 /* EQ1_B1_PG - [15:0] */ + +/* + * R3605 (0xE15) - EQ1_6 + */ +#define WM5100_EQ1_B2_A_MASK 0xFFFF /* EQ1_B2_A - [15:0] */ +#define WM5100_EQ1_B2_A_SHIFT 0 /* EQ1_B2_A - [15:0] */ +#define WM5100_EQ1_B2_A_WIDTH 16 /* EQ1_B2_A - [15:0] */ + +/* + * R3606 (0xE16) - EQ1_7 + */ +#define WM5100_EQ1_B2_B_MASK 0xFFFF /* EQ1_B2_B - [15:0] */ +#define WM5100_EQ1_B2_B_SHIFT 0 /* EQ1_B2_B - [15:0] */ +#define WM5100_EQ1_B2_B_WIDTH 16 /* EQ1_B2_B - [15:0] */ + +/* + * R3607 (0xE17) - EQ1_8 + */ +#define WM5100_EQ1_B2_C_MASK 0xFFFF /* EQ1_B2_C - [15:0] */ +#define WM5100_EQ1_B2_C_SHIFT 0 /* EQ1_B2_C - [15:0] */ +#define WM5100_EQ1_B2_C_WIDTH 16 /* EQ1_B2_C - [15:0] */ + +/* + * R3608 (0xE18) - EQ1_9 + */ +#define WM5100_EQ1_B2_PG_MASK 0xFFFF /* EQ1_B2_PG - [15:0] */ +#define WM5100_EQ1_B2_PG_SHIFT 0 /* EQ1_B2_PG - [15:0] */ +#define WM5100_EQ1_B2_PG_WIDTH 16 /* EQ1_B2_PG - [15:0] */ + +/* + * R3609 (0xE19) - EQ1_10 + */ +#define WM5100_EQ1_B3_A_MASK 0xFFFF /* EQ1_B3_A - [15:0] */ +#define WM5100_EQ1_B3_A_SHIFT 0 /* EQ1_B3_A - [15:0] */ +#define WM5100_EQ1_B3_A_WIDTH 16 /* EQ1_B3_A - [15:0] */ + +/* + * R3610 (0xE1A) - EQ1_11 + */ +#define WM5100_EQ1_B3_B_MASK 0xFFFF /* EQ1_B3_B - [15:0] */ +#define WM5100_EQ1_B3_B_SHIFT 0 /* EQ1_B3_B - [15:0] */ +#define WM5100_EQ1_B3_B_WIDTH 16 /* EQ1_B3_B - [15:0] */ + +/* + * R3611 (0xE1B) - EQ1_12 + */ +#define WM5100_EQ1_B3_C_MASK 0xFFFF /* EQ1_B3_C - [15:0] */ +#define WM5100_EQ1_B3_C_SHIFT 0 /* EQ1_B3_C - [15:0] */ +#define WM5100_EQ1_B3_C_WIDTH 16 /* EQ1_B3_C - [15:0] */ + +/* + * R3612 (0xE1C) - EQ1_13 + */ +#define WM5100_EQ1_B3_PG_MASK 0xFFFF /* EQ1_B3_PG - [15:0] */ +#define WM5100_EQ1_B3_PG_SHIFT 0 /* EQ1_B3_PG - [15:0] */ +#define WM5100_EQ1_B3_PG_WIDTH 16 /* EQ1_B3_PG - [15:0] */ + +/* + * R3613 (0xE1D) - EQ1_14 + */ +#define WM5100_EQ1_B4_A_MASK 0xFFFF /* EQ1_B4_A - [15:0] */ +#define WM5100_EQ1_B4_A_SHIFT 0 /* EQ1_B4_A - [15:0] */ +#define WM5100_EQ1_B4_A_WIDTH 16 /* EQ1_B4_A - [15:0] */ + +/* + * R3614 (0xE1E) - EQ1_15 + */ +#define WM5100_EQ1_B4_B_MASK 0xFFFF /* EQ1_B4_B - [15:0] */ +#define WM5100_EQ1_B4_B_SHIFT 0 /* EQ1_B4_B - [15:0] */ +#define WM5100_EQ1_B4_B_WIDTH 16 /* EQ1_B4_B - [15:0] */ + +/* + * R3615 (0xE1F) - EQ1_16 + */ +#define WM5100_EQ1_B4_C_MASK 0xFFFF /* EQ1_B4_C - [15:0] */ +#define WM5100_EQ1_B4_C_SHIFT 0 /* EQ1_B4_C - [15:0] */ +#define WM5100_EQ1_B4_C_WIDTH 16 /* EQ1_B4_C - [15:0] */ + +/* + * R3616 (0xE20) - EQ1_17 + */ +#define WM5100_EQ1_B4_PG_MASK 0xFFFF /* EQ1_B4_PG - [15:0] */ +#define WM5100_EQ1_B4_PG_SHIFT 0 /* EQ1_B4_PG - [15:0] */ +#define WM5100_EQ1_B4_PG_WIDTH 16 /* EQ1_B4_PG - [15:0] */ + +/* + * R3617 (0xE21) - EQ1_18 + */ +#define WM5100_EQ1_B5_A_MASK 0xFFFF /* EQ1_B5_A - [15:0] */ +#define WM5100_EQ1_B5_A_SHIFT 0 /* EQ1_B5_A - [15:0] */ +#define WM5100_EQ1_B5_A_WIDTH 16 /* EQ1_B5_A - [15:0] */ + +/* + * R3618 (0xE22) - EQ1_19 + */ +#define WM5100_EQ1_B5_B_MASK 0xFFFF /* EQ1_B5_B - [15:0] */ +#define WM5100_EQ1_B5_B_SHIFT 0 /* EQ1_B5_B - [15:0] */ +#define WM5100_EQ1_B5_B_WIDTH 16 /* EQ1_B5_B - [15:0] */ + +/* + * R3619 (0xE23) - EQ1_20 + */ +#define WM5100_EQ1_B5_PG_MASK 0xFFFF /* EQ1_B5_PG - [15:0] */ +#define WM5100_EQ1_B5_PG_SHIFT 0 /* EQ1_B5_PG - [15:0] */ +#define WM5100_EQ1_B5_PG_WIDTH 16 /* EQ1_B5_PG - [15:0] */ + +/* + * R3622 (0xE26) - EQ2_1 + */ +#define WM5100_EQ2_B1_GAIN_MASK 0xF800 /* EQ2_B1_GAIN - [15:11] */ +#define WM5100_EQ2_B1_GAIN_SHIFT 11 /* EQ2_B1_GAIN - [15:11] */ +#define WM5100_EQ2_B1_GAIN_WIDTH 5 /* EQ2_B1_GAIN - [15:11] */ +#define WM5100_EQ2_B2_GAIN_MASK 0x07C0 /* EQ2_B2_GAIN - [10:6] */ +#define WM5100_EQ2_B2_GAIN_SHIFT 6 /* EQ2_B2_GAIN - [10:6] */ +#define WM5100_EQ2_B2_GAIN_WIDTH 5 /* EQ2_B2_GAIN - [10:6] */ +#define WM5100_EQ2_B3_GAIN_MASK 0x003E /* EQ2_B3_GAIN - [5:1] */ +#define WM5100_EQ2_B3_GAIN_SHIFT 1 /* EQ2_B3_GAIN - [5:1] */ +#define WM5100_EQ2_B3_GAIN_WIDTH 5 /* EQ2_B3_GAIN - [5:1] */ +#define WM5100_EQ2_ENA 0x0001 /* EQ2_ENA */ +#define WM5100_EQ2_ENA_MASK 0x0001 /* EQ2_ENA */ +#define WM5100_EQ2_ENA_SHIFT 0 /* EQ2_ENA */ +#define WM5100_EQ2_ENA_WIDTH 1 /* EQ2_ENA */ + +/* + * R3623 (0xE27) - EQ2_2 + */ +#define WM5100_EQ2_B4_GAIN_MASK 0xF800 /* EQ2_B4_GAIN - [15:11] */ +#define WM5100_EQ2_B4_GAIN_SHIFT 11 /* EQ2_B4_GAIN - [15:11] */ +#define WM5100_EQ2_B4_GAIN_WIDTH 5 /* EQ2_B4_GAIN - [15:11] */ +#define WM5100_EQ2_B5_GAIN_MASK 0x07C0 /* EQ2_B5_GAIN - [10:6] */ +#define WM5100_EQ2_B5_GAIN_SHIFT 6 /* EQ2_B5_GAIN - [10:6] */ +#define WM5100_EQ2_B5_GAIN_WIDTH 5 /* EQ2_B5_GAIN - [10:6] */ + +/* + * R3624 (0xE28) - EQ2_3 + */ +#define WM5100_EQ2_B1_A_MASK 0xFFFF /* EQ2_B1_A - [15:0] */ +#define WM5100_EQ2_B1_A_SHIFT 0 /* EQ2_B1_A - [15:0] */ +#define WM5100_EQ2_B1_A_WIDTH 16 /* EQ2_B1_A - [15:0] */ + +/* + * R3625 (0xE29) - EQ2_4 + */ +#define WM5100_EQ2_B1_B_MASK 0xFFFF /* EQ2_B1_B - [15:0] */ +#define WM5100_EQ2_B1_B_SHIFT 0 /* EQ2_B1_B - [15:0] */ +#define WM5100_EQ2_B1_B_WIDTH 16 /* EQ2_B1_B - [15:0] */ + +/* + * R3626 (0xE2A) - EQ2_5 + */ +#define WM5100_EQ2_B1_PG_MASK 0xFFFF /* EQ2_B1_PG - [15:0] */ +#define WM5100_EQ2_B1_PG_SHIFT 0 /* EQ2_B1_PG - [15:0] */ +#define WM5100_EQ2_B1_PG_WIDTH 16 /* EQ2_B1_PG - [15:0] */ + +/* + * R3627 (0xE2B) - EQ2_6 + */ +#define WM5100_EQ2_B2_A_MASK 0xFFFF /* EQ2_B2_A - [15:0] */ +#define WM5100_EQ2_B2_A_SHIFT 0 /* EQ2_B2_A - [15:0] */ +#define WM5100_EQ2_B2_A_WIDTH 16 /* EQ2_B2_A - [15:0] */ + +/* + * R3628 (0xE2C) - EQ2_7 + */ +#define WM5100_EQ2_B2_B_MASK 0xFFFF /* EQ2_B2_B - [15:0] */ +#define WM5100_EQ2_B2_B_SHIFT 0 /* EQ2_B2_B - [15:0] */ +#define WM5100_EQ2_B2_B_WIDTH 16 /* EQ2_B2_B - [15:0] */ + +/* + * R3629 (0xE2D) - EQ2_8 + */ +#define WM5100_EQ2_B2_C_MASK 0xFFFF /* EQ2_B2_C - [15:0] */ +#define WM5100_EQ2_B2_C_SHIFT 0 /* EQ2_B2_C - [15:0] */ +#define WM5100_EQ2_B2_C_WIDTH 16 /* EQ2_B2_C - [15:0] */ + +/* + * R3630 (0xE2E) - EQ2_9 + */ +#define WM5100_EQ2_B2_PG_MASK 0xFFFF /* EQ2_B2_PG - [15:0] */ +#define WM5100_EQ2_B2_PG_SHIFT 0 /* EQ2_B2_PG - [15:0] */ +#define WM5100_EQ2_B2_PG_WIDTH 16 /* EQ2_B2_PG - [15:0] */ + +/* + * R3631 (0xE2F) - EQ2_10 + */ +#define WM5100_EQ2_B3_A_MASK 0xFFFF /* EQ2_B3_A - [15:0] */ +#define WM5100_EQ2_B3_A_SHIFT 0 /* EQ2_B3_A - [15:0] */ +#define WM5100_EQ2_B3_A_WIDTH 16 /* EQ2_B3_A - [15:0] */ + +/* + * R3632 (0xE30) - EQ2_11 + */ +#define WM5100_EQ2_B3_B_MASK 0xFFFF /* EQ2_B3_B - [15:0] */ +#define WM5100_EQ2_B3_B_SHIFT 0 /* EQ2_B3_B - [15:0] */ +#define WM5100_EQ2_B3_B_WIDTH 16 /* EQ2_B3_B - [15:0] */ + +/* + * R3633 (0xE31) - EQ2_12 + */ +#define WM5100_EQ2_B3_C_MASK 0xFFFF /* EQ2_B3_C - [15:0] */ +#define WM5100_EQ2_B3_C_SHIFT 0 /* EQ2_B3_C - [15:0] */ +#define WM5100_EQ2_B3_C_WIDTH 16 /* EQ2_B3_C - [15:0] */ + +/* + * R3634 (0xE32) - EQ2_13 + */ +#define WM5100_EQ2_B3_PG_MASK 0xFFFF /* EQ2_B3_PG - [15:0] */ +#define WM5100_EQ2_B3_PG_SHIFT 0 /* EQ2_B3_PG - [15:0] */ +#define WM5100_EQ2_B3_PG_WIDTH 16 /* EQ2_B3_PG - [15:0] */ + +/* + * R3635 (0xE33) - EQ2_14 + */ +#define WM5100_EQ2_B4_A_MASK 0xFFFF /* EQ2_B4_A - [15:0] */ +#define WM5100_EQ2_B4_A_SHIFT 0 /* EQ2_B4_A - [15:0] */ +#define WM5100_EQ2_B4_A_WIDTH 16 /* EQ2_B4_A - [15:0] */ + +/* + * R3636 (0xE34) - EQ2_15 + */ +#define WM5100_EQ2_B4_B_MASK 0xFFFF /* EQ2_B4_B - [15:0] */ +#define WM5100_EQ2_B4_B_SHIFT 0 /* EQ2_B4_B - [15:0] */ +#define WM5100_EQ2_B4_B_WIDTH 16 /* EQ2_B4_B - [15:0] */ + +/* + * R3637 (0xE35) - EQ2_16 + */ +#define WM5100_EQ2_B4_C_MASK 0xFFFF /* EQ2_B4_C - [15:0] */ +#define WM5100_EQ2_B4_C_SHIFT 0 /* EQ2_B4_C - [15:0] */ +#define WM5100_EQ2_B4_C_WIDTH 16 /* EQ2_B4_C - [15:0] */ + +/* + * R3638 (0xE36) - EQ2_17 + */ +#define WM5100_EQ2_B4_PG_MASK 0xFFFF /* EQ2_B4_PG - [15:0] */ +#define WM5100_EQ2_B4_PG_SHIFT 0 /* EQ2_B4_PG - [15:0] */ +#define WM5100_EQ2_B4_PG_WIDTH 16 /* EQ2_B4_PG - [15:0] */ + +/* + * R3639 (0xE37) - EQ2_18 + */ +#define WM5100_EQ2_B5_A_MASK 0xFFFF /* EQ2_B5_A - [15:0] */ +#define WM5100_EQ2_B5_A_SHIFT 0 /* EQ2_B5_A - [15:0] */ +#define WM5100_EQ2_B5_A_WIDTH 16 /* EQ2_B5_A - [15:0] */ + +/* + * R3640 (0xE38) - EQ2_19 + */ +#define WM5100_EQ2_B5_B_MASK 0xFFFF /* EQ2_B5_B - [15:0] */ +#define WM5100_EQ2_B5_B_SHIFT 0 /* EQ2_B5_B - [15:0] */ +#define WM5100_EQ2_B5_B_WIDTH 16 /* EQ2_B5_B - [15:0] */ + +/* + * R3641 (0xE39) - EQ2_20 + */ +#define WM5100_EQ2_B5_PG_MASK 0xFFFF /* EQ2_B5_PG - [15:0] */ +#define WM5100_EQ2_B5_PG_SHIFT 0 /* EQ2_B5_PG - [15:0] */ +#define WM5100_EQ2_B5_PG_WIDTH 16 /* EQ2_B5_PG - [15:0] */ + +/* + * R3644 (0xE3C) - EQ3_1 + */ +#define WM5100_EQ3_B1_GAIN_MASK 0xF800 /* EQ3_B1_GAIN - [15:11] */ +#define WM5100_EQ3_B1_GAIN_SHIFT 11 /* EQ3_B1_GAIN - [15:11] */ +#define WM5100_EQ3_B1_GAIN_WIDTH 5 /* EQ3_B1_GAIN - [15:11] */ +#define WM5100_EQ3_B2_GAIN_MASK 0x07C0 /* EQ3_B2_GAIN - [10:6] */ +#define WM5100_EQ3_B2_GAIN_SHIFT 6 /* EQ3_B2_GAIN - [10:6] */ +#define WM5100_EQ3_B2_GAIN_WIDTH 5 /* EQ3_B2_GAIN - [10:6] */ +#define WM5100_EQ3_B3_GAIN_MASK 0x003E /* EQ3_B3_GAIN - [5:1] */ +#define WM5100_EQ3_B3_GAIN_SHIFT 1 /* EQ3_B3_GAIN - [5:1] */ +#define WM5100_EQ3_B3_GAIN_WIDTH 5 /* EQ3_B3_GAIN - [5:1] */ +#define WM5100_EQ3_ENA 0x0001 /* EQ3_ENA */ +#define WM5100_EQ3_ENA_MASK 0x0001 /* EQ3_ENA */ +#define WM5100_EQ3_ENA_SHIFT 0 /* EQ3_ENA */ +#define WM5100_EQ3_ENA_WIDTH 1 /* EQ3_ENA */ + +/* + * R3645 (0xE3D) - EQ3_2 + */ +#define WM5100_EQ3_B4_GAIN_MASK 0xF800 /* EQ3_B4_GAIN - [15:11] */ +#define WM5100_EQ3_B4_GAIN_SHIFT 11 /* EQ3_B4_GAIN - [15:11] */ +#define WM5100_EQ3_B4_GAIN_WIDTH 5 /* EQ3_B4_GAIN - [15:11] */ +#define WM5100_EQ3_B5_GAIN_MASK 0x07C0 /* EQ3_B5_GAIN - [10:6] */ +#define WM5100_EQ3_B5_GAIN_SHIFT 6 /* EQ3_B5_GAIN - [10:6] */ +#define WM5100_EQ3_B5_GAIN_WIDTH 5 /* EQ3_B5_GAIN - [10:6] */ + +/* + * R3646 (0xE3E) - EQ3_3 + */ +#define WM5100_EQ3_B1_A_MASK 0xFFFF /* EQ3_B1_A - [15:0] */ +#define WM5100_EQ3_B1_A_SHIFT 0 /* EQ3_B1_A - [15:0] */ +#define WM5100_EQ3_B1_A_WIDTH 16 /* EQ3_B1_A - [15:0] */ + +/* + * R3647 (0xE3F) - EQ3_4 + */ +#define WM5100_EQ3_B1_B_MASK 0xFFFF /* EQ3_B1_B - [15:0] */ +#define WM5100_EQ3_B1_B_SHIFT 0 /* EQ3_B1_B - [15:0] */ +#define WM5100_EQ3_B1_B_WIDTH 16 /* EQ3_B1_B - [15:0] */ + +/* + * R3648 (0xE40) - EQ3_5 + */ +#define WM5100_EQ3_B1_PG_MASK 0xFFFF /* EQ3_B1_PG - [15:0] */ +#define WM5100_EQ3_B1_PG_SHIFT 0 /* EQ3_B1_PG - [15:0] */ +#define WM5100_EQ3_B1_PG_WIDTH 16 /* EQ3_B1_PG - [15:0] */ + +/* + * R3649 (0xE41) - EQ3_6 + */ +#define WM5100_EQ3_B2_A_MASK 0xFFFF /* EQ3_B2_A - [15:0] */ +#define WM5100_EQ3_B2_A_SHIFT 0 /* EQ3_B2_A - [15:0] */ +#define WM5100_EQ3_B2_A_WIDTH 16 /* EQ3_B2_A - [15:0] */ + +/* + * R3650 (0xE42) - EQ3_7 + */ +#define WM5100_EQ3_B2_B_MASK 0xFFFF /* EQ3_B2_B - [15:0] */ +#define WM5100_EQ3_B2_B_SHIFT 0 /* EQ3_B2_B - [15:0] */ +#define WM5100_EQ3_B2_B_WIDTH 16 /* EQ3_B2_B - [15:0] */ + +/* + * R3651 (0xE43) - EQ3_8 + */ +#define WM5100_EQ3_B2_C_MASK 0xFFFF /* EQ3_B2_C - [15:0] */ +#define WM5100_EQ3_B2_C_SHIFT 0 /* EQ3_B2_C - [15:0] */ +#define WM5100_EQ3_B2_C_WIDTH 16 /* EQ3_B2_C - [15:0] */ + +/* + * R3652 (0xE44) - EQ3_9 + */ +#define WM5100_EQ3_B2_PG_MASK 0xFFFF /* EQ3_B2_PG - [15:0] */ +#define WM5100_EQ3_B2_PG_SHIFT 0 /* EQ3_B2_PG - [15:0] */ +#define WM5100_EQ3_B2_PG_WIDTH 16 /* EQ3_B2_PG - [15:0] */ + +/* + * R3653 (0xE45) - EQ3_10 + */ +#define WM5100_EQ3_B3_A_MASK 0xFFFF /* EQ3_B3_A - [15:0] */ +#define WM5100_EQ3_B3_A_SHIFT 0 /* EQ3_B3_A - [15:0] */ +#define WM5100_EQ3_B3_A_WIDTH 16 /* EQ3_B3_A - [15:0] */ + +/* + * R3654 (0xE46) - EQ3_11 + */ +#define WM5100_EQ3_B3_B_MASK 0xFFFF /* EQ3_B3_B - [15:0] */ +#define WM5100_EQ3_B3_B_SHIFT 0 /* EQ3_B3_B - [15:0] */ +#define WM5100_EQ3_B3_B_WIDTH 16 /* EQ3_B3_B - [15:0] */ + +/* + * R3655 (0xE47) - EQ3_12 + */ +#define WM5100_EQ3_B3_C_MASK 0xFFFF /* EQ3_B3_C - [15:0] */ +#define WM5100_EQ3_B3_C_SHIFT 0 /* EQ3_B3_C - [15:0] */ +#define WM5100_EQ3_B3_C_WIDTH 16 /* EQ3_B3_C - [15:0] */ + +/* + * R3656 (0xE48) - EQ3_13 + */ +#define WM5100_EQ3_B3_PG_MASK 0xFFFF /* EQ3_B3_PG - [15:0] */ +#define WM5100_EQ3_B3_PG_SHIFT 0 /* EQ3_B3_PG - [15:0] */ +#define WM5100_EQ3_B3_PG_WIDTH 16 /* EQ3_B3_PG - [15:0] */ + +/* + * R3657 (0xE49) - EQ3_14 + */ +#define WM5100_EQ3_B4_A_MASK 0xFFFF /* EQ3_B4_A - [15:0] */ +#define WM5100_EQ3_B4_A_SHIFT 0 /* EQ3_B4_A - [15:0] */ +#define WM5100_EQ3_B4_A_WIDTH 16 /* EQ3_B4_A - [15:0] */ + +/* + * R3658 (0xE4A) - EQ3_15 + */ +#define WM5100_EQ3_B4_B_MASK 0xFFFF /* EQ3_B4_B - [15:0] */ +#define WM5100_EQ3_B4_B_SHIFT 0 /* EQ3_B4_B - [15:0] */ +#define WM5100_EQ3_B4_B_WIDTH 16 /* EQ3_B4_B - [15:0] */ + +/* + * R3659 (0xE4B) - EQ3_16 + */ +#define WM5100_EQ3_B4_C_MASK 0xFFFF /* EQ3_B4_C - [15:0] */ +#define WM5100_EQ3_B4_C_SHIFT 0 /* EQ3_B4_C - [15:0] */ +#define WM5100_EQ3_B4_C_WIDTH 16 /* EQ3_B4_C - [15:0] */ + +/* + * R3660 (0xE4C) - EQ3_17 + */ +#define WM5100_EQ3_B4_PG_MASK 0xFFFF /* EQ3_B4_PG - [15:0] */ +#define WM5100_EQ3_B4_PG_SHIFT 0 /* EQ3_B4_PG - [15:0] */ +#define WM5100_EQ3_B4_PG_WIDTH 16 /* EQ3_B4_PG - [15:0] */ + +/* + * R3661 (0xE4D) - EQ3_18 + */ +#define WM5100_EQ3_B5_A_MASK 0xFFFF /* EQ3_B5_A - [15:0] */ +#define WM5100_EQ3_B5_A_SHIFT 0 /* EQ3_B5_A - [15:0] */ +#define WM5100_EQ3_B5_A_WIDTH 16 /* EQ3_B5_A - [15:0] */ + +/* + * R3662 (0xE4E) - EQ3_19 + */ +#define WM5100_EQ3_B5_B_MASK 0xFFFF /* EQ3_B5_B - [15:0] */ +#define WM5100_EQ3_B5_B_SHIFT 0 /* EQ3_B5_B - [15:0] */ +#define WM5100_EQ3_B5_B_WIDTH 16 /* EQ3_B5_B - [15:0] */ + +/* + * R3663 (0xE4F) - EQ3_20 + */ +#define WM5100_EQ3_B5_PG_MASK 0xFFFF /* EQ3_B5_PG - [15:0] */ +#define WM5100_EQ3_B5_PG_SHIFT 0 /* EQ3_B5_PG - [15:0] */ +#define WM5100_EQ3_B5_PG_WIDTH 16 /* EQ3_B5_PG - [15:0] */ + +/* + * R3666 (0xE52) - EQ4_1 + */ +#define WM5100_EQ4_B1_GAIN_MASK 0xF800 /* EQ4_B1_GAIN - [15:11] */ +#define WM5100_EQ4_B1_GAIN_SHIFT 11 /* EQ4_B1_GAIN - [15:11] */ +#define WM5100_EQ4_B1_GAIN_WIDTH 5 /* EQ4_B1_GAIN - [15:11] */ +#define WM5100_EQ4_B2_GAIN_MASK 0x07C0 /* EQ4_B2_GAIN - [10:6] */ +#define WM5100_EQ4_B2_GAIN_SHIFT 6 /* EQ4_B2_GAIN - [10:6] */ +#define WM5100_EQ4_B2_GAIN_WIDTH 5 /* EQ4_B2_GAIN - [10:6] */ +#define WM5100_EQ4_B3_GAIN_MASK 0x003E /* EQ4_B3_GAIN - [5:1] */ +#define WM5100_EQ4_B3_GAIN_SHIFT 1 /* EQ4_B3_GAIN - [5:1] */ +#define WM5100_EQ4_B3_GAIN_WIDTH 5 /* EQ4_B3_GAIN - [5:1] */ +#define WM5100_EQ4_ENA 0x0001 /* EQ4_ENA */ +#define WM5100_EQ4_ENA_MASK 0x0001 /* EQ4_ENA */ +#define WM5100_EQ4_ENA_SHIFT 0 /* EQ4_ENA */ +#define WM5100_EQ4_ENA_WIDTH 1 /* EQ4_ENA */ + +/* + * R3667 (0xE53) - EQ4_2 + */ +#define WM5100_EQ4_B4_GAIN_MASK 0xF800 /* EQ4_B4_GAIN - [15:11] */ +#define WM5100_EQ4_B4_GAIN_SHIFT 11 /* EQ4_B4_GAIN - [15:11] */ +#define WM5100_EQ4_B4_GAIN_WIDTH 5 /* EQ4_B4_GAIN - [15:11] */ +#define WM5100_EQ4_B5_GAIN_MASK 0x07C0 /* EQ4_B5_GAIN - [10:6] */ +#define WM5100_EQ4_B5_GAIN_SHIFT 6 /* EQ4_B5_GAIN - [10:6] */ +#define WM5100_EQ4_B5_GAIN_WIDTH 5 /* EQ4_B5_GAIN - [10:6] */ + +/* + * R3668 (0xE54) - EQ4_3 + */ +#define WM5100_EQ4_B1_A_MASK 0xFFFF /* EQ4_B1_A - [15:0] */ +#define WM5100_EQ4_B1_A_SHIFT 0 /* EQ4_B1_A - [15:0] */ +#define WM5100_EQ4_B1_A_WIDTH 16 /* EQ4_B1_A - [15:0] */ + +/* + * R3669 (0xE55) - EQ4_4 + */ +#define WM5100_EQ4_B1_B_MASK 0xFFFF /* EQ4_B1_B - [15:0] */ +#define WM5100_EQ4_B1_B_SHIFT 0 /* EQ4_B1_B - [15:0] */ +#define WM5100_EQ4_B1_B_WIDTH 16 /* EQ4_B1_B - [15:0] */ + +/* + * R3670 (0xE56) - EQ4_5 + */ +#define WM5100_EQ4_B1_PG_MASK 0xFFFF /* EQ4_B1_PG - [15:0] */ +#define WM5100_EQ4_B1_PG_SHIFT 0 /* EQ4_B1_PG - [15:0] */ +#define WM5100_EQ4_B1_PG_WIDTH 16 /* EQ4_B1_PG - [15:0] */ + +/* + * R3671 (0xE57) - EQ4_6 + */ +#define WM5100_EQ4_B2_A_MASK 0xFFFF /* EQ4_B2_A - [15:0] */ +#define WM5100_EQ4_B2_A_SHIFT 0 /* EQ4_B2_A - [15:0] */ +#define WM5100_EQ4_B2_A_WIDTH 16 /* EQ4_B2_A - [15:0] */ + +/* + * R3672 (0xE58) - EQ4_7 + */ +#define WM5100_EQ4_B2_B_MASK 0xFFFF /* EQ4_B2_B - [15:0] */ +#define WM5100_EQ4_B2_B_SHIFT 0 /* EQ4_B2_B - [15:0] */ +#define WM5100_EQ4_B2_B_WIDTH 16 /* EQ4_B2_B - [15:0] */ + +/* + * R3673 (0xE59) - EQ4_8 + */ +#define WM5100_EQ4_B2_C_MASK 0xFFFF /* EQ4_B2_C - [15:0] */ +#define WM5100_EQ4_B2_C_SHIFT 0 /* EQ4_B2_C - [15:0] */ +#define WM5100_EQ4_B2_C_WIDTH 16 /* EQ4_B2_C - [15:0] */ + +/* + * R3674 (0xE5A) - EQ4_9 + */ +#define WM5100_EQ4_B2_PG_MASK 0xFFFF /* EQ4_B2_PG - [15:0] */ +#define WM5100_EQ4_B2_PG_SHIFT 0 /* EQ4_B2_PG - [15:0] */ +#define WM5100_EQ4_B2_PG_WIDTH 16 /* EQ4_B2_PG - [15:0] */ + +/* + * R3675 (0xE5B) - EQ4_10 + */ +#define WM5100_EQ4_B3_A_MASK 0xFFFF /* EQ4_B3_A - [15:0] */ +#define WM5100_EQ4_B3_A_SHIFT 0 /* EQ4_B3_A - [15:0] */ +#define WM5100_EQ4_B3_A_WIDTH 16 /* EQ4_B3_A - [15:0] */ + +/* + * R3676 (0xE5C) - EQ4_11 + */ +#define WM5100_EQ4_B3_B_MASK 0xFFFF /* EQ4_B3_B - [15:0] */ +#define WM5100_EQ4_B3_B_SHIFT 0 /* EQ4_B3_B - [15:0] */ +#define WM5100_EQ4_B3_B_WIDTH 16 /* EQ4_B3_B - [15:0] */ + +/* + * R3677 (0xE5D) - EQ4_12 + */ +#define WM5100_EQ4_B3_C_MASK 0xFFFF /* EQ4_B3_C - [15:0] */ +#define WM5100_EQ4_B3_C_SHIFT 0 /* EQ4_B3_C - [15:0] */ +#define WM5100_EQ4_B3_C_WIDTH 16 /* EQ4_B3_C - [15:0] */ + +/* + * R3678 (0xE5E) - EQ4_13 + */ +#define WM5100_EQ4_B3_PG_MASK 0xFFFF /* EQ4_B3_PG - [15:0] */ +#define WM5100_EQ4_B3_PG_SHIFT 0 /* EQ4_B3_PG - [15:0] */ +#define WM5100_EQ4_B3_PG_WIDTH 16 /* EQ4_B3_PG - [15:0] */ + +/* + * R3679 (0xE5F) - EQ4_14 + */ +#define WM5100_EQ4_B4_A_MASK 0xFFFF /* EQ4_B4_A - [15:0] */ +#define WM5100_EQ4_B4_A_SHIFT 0 /* EQ4_B4_A - [15:0] */ +#define WM5100_EQ4_B4_A_WIDTH 16 /* EQ4_B4_A - [15:0] */ + +/* + * R3680 (0xE60) - EQ4_15 + */ +#define WM5100_EQ4_B4_B_MASK 0xFFFF /* EQ4_B4_B - [15:0] */ +#define WM5100_EQ4_B4_B_SHIFT 0 /* EQ4_B4_B - [15:0] */ +#define WM5100_EQ4_B4_B_WIDTH 16 /* EQ4_B4_B - [15:0] */ + +/* + * R3681 (0xE61) - EQ4_16 + */ +#define WM5100_EQ4_B4_C_MASK 0xFFFF /* EQ4_B4_C - [15:0] */ +#define WM5100_EQ4_B4_C_SHIFT 0 /* EQ4_B4_C - [15:0] */ +#define WM5100_EQ4_B4_C_WIDTH 16 /* EQ4_B4_C - [15:0] */ + +/* + * R3682 (0xE62) - EQ4_17 + */ +#define WM5100_EQ4_B4_PG_MASK 0xFFFF /* EQ4_B4_PG - [15:0] */ +#define WM5100_EQ4_B4_PG_SHIFT 0 /* EQ4_B4_PG - [15:0] */ +#define WM5100_EQ4_B4_PG_WIDTH 16 /* EQ4_B4_PG - [15:0] */ + +/* + * R3683 (0xE63) - EQ4_18 + */ +#define WM5100_EQ4_B5_A_MASK 0xFFFF /* EQ4_B5_A - [15:0] */ +#define WM5100_EQ4_B5_A_SHIFT 0 /* EQ4_B5_A - [15:0] */ +#define WM5100_EQ4_B5_A_WIDTH 16 /* EQ4_B5_A - [15:0] */ + +/* + * R3684 (0xE64) - EQ4_19 + */ +#define WM5100_EQ4_B5_B_MASK 0xFFFF /* EQ4_B5_B - [15:0] */ +#define WM5100_EQ4_B5_B_SHIFT 0 /* EQ4_B5_B - [15:0] */ +#define WM5100_EQ4_B5_B_WIDTH 16 /* EQ4_B5_B - [15:0] */ + +/* + * R3685 (0xE65) - EQ4_20 + */ +#define WM5100_EQ4_B5_PG_MASK 0xFFFF /* EQ4_B5_PG - [15:0] */ +#define WM5100_EQ4_B5_PG_SHIFT 0 /* EQ4_B5_PG - [15:0] */ +#define WM5100_EQ4_B5_PG_WIDTH 16 /* EQ4_B5_PG - [15:0] */ + +/* + * R3712 (0xE80) - DRC1 ctrl1 + */ +#define WM5100_DRC_SIG_DET_RMS_MASK 0xF800 /* DRC_SIG_DET_RMS - [15:11] */ +#define WM5100_DRC_SIG_DET_RMS_SHIFT 11 /* DRC_SIG_DET_RMS - [15:11] */ +#define WM5100_DRC_SIG_DET_RMS_WIDTH 5 /* DRC_SIG_DET_RMS - [15:11] */ +#define WM5100_DRC_SIG_DET_PK_MASK 0x0600 /* DRC_SIG_DET_PK - [10:9] */ +#define WM5100_DRC_SIG_DET_PK_SHIFT 9 /* DRC_SIG_DET_PK - [10:9] */ +#define WM5100_DRC_SIG_DET_PK_WIDTH 2 /* DRC_SIG_DET_PK - [10:9] */ +#define WM5100_DRC_NG_ENA 0x0100 /* DRC_NG_ENA */ +#define WM5100_DRC_NG_ENA_MASK 0x0100 /* DRC_NG_ENA */ +#define WM5100_DRC_NG_ENA_SHIFT 8 /* DRC_NG_ENA */ +#define WM5100_DRC_NG_ENA_WIDTH 1 /* DRC_NG_ENA */ +#define WM5100_DRC_SIG_DET_MODE 0x0080 /* DRC_SIG_DET_MODE */ +#define WM5100_DRC_SIG_DET_MODE_MASK 0x0080 /* DRC_SIG_DET_MODE */ +#define WM5100_DRC_SIG_DET_MODE_SHIFT 7 /* DRC_SIG_DET_MODE */ +#define WM5100_DRC_SIG_DET_MODE_WIDTH 1 /* DRC_SIG_DET_MODE */ +#define WM5100_DRC_SIG_DET 0x0040 /* DRC_SIG_DET */ +#define WM5100_DRC_SIG_DET_MASK 0x0040 /* DRC_SIG_DET */ +#define WM5100_DRC_SIG_DET_SHIFT 6 /* DRC_SIG_DET */ +#define WM5100_DRC_SIG_DET_WIDTH 1 /* DRC_SIG_DET */ +#define WM5100_DRC_KNEE2_OP_ENA 0x0020 /* DRC_KNEE2_OP_ENA */ +#define WM5100_DRC_KNEE2_OP_ENA_MASK 0x0020 /* DRC_KNEE2_OP_ENA */ +#define WM5100_DRC_KNEE2_OP_ENA_SHIFT 5 /* DRC_KNEE2_OP_ENA */ +#define WM5100_DRC_KNEE2_OP_ENA_WIDTH 1 /* DRC_KNEE2_OP_ENA */ +#define WM5100_DRC_QR 0x0010 /* DRC_QR */ +#define WM5100_DRC_QR_MASK 0x0010 /* DRC_QR */ +#define WM5100_DRC_QR_SHIFT 4 /* DRC_QR */ +#define WM5100_DRC_QR_WIDTH 1 /* DRC_QR */ +#define WM5100_DRC_ANTICLIP 0x0008 /* DRC_ANTICLIP */ +#define WM5100_DRC_ANTICLIP_MASK 0x0008 /* DRC_ANTICLIP */ +#define WM5100_DRC_ANTICLIP_SHIFT 3 /* DRC_ANTICLIP */ +#define WM5100_DRC_ANTICLIP_WIDTH 1 /* DRC_ANTICLIP */ +#define WM5100_DRCL_ENA 0x0002 /* DRCL_ENA */ +#define WM5100_DRCL_ENA_MASK 0x0002 /* DRCL_ENA */ +#define WM5100_DRCL_ENA_SHIFT 1 /* DRCL_ENA */ +#define WM5100_DRCL_ENA_WIDTH 1 /* DRCL_ENA */ +#define WM5100_DRCR_ENA 0x0001 /* DRCR_ENA */ +#define WM5100_DRCR_ENA_MASK 0x0001 /* DRCR_ENA */ +#define WM5100_DRCR_ENA_SHIFT 0 /* DRCR_ENA */ +#define WM5100_DRCR_ENA_WIDTH 1 /* DRCR_ENA */ + +/* + * R3713 (0xE81) - DRC1 ctrl2 + */ +#define WM5100_DRC_ATK_MASK 0x1E00 /* DRC_ATK - [12:9] */ +#define WM5100_DRC_ATK_SHIFT 9 /* DRC_ATK - [12:9] */ +#define WM5100_DRC_ATK_WIDTH 4 /* DRC_ATK - [12:9] */ +#define WM5100_DRC_DCY_MASK 0x01E0 /* DRC_DCY - [8:5] */ +#define WM5100_DRC_DCY_SHIFT 5 /* DRC_DCY - [8:5] */ +#define WM5100_DRC_DCY_WIDTH 4 /* DRC_DCY - [8:5] */ +#define WM5100_DRC_MINGAIN_MASK 0x001C /* DRC_MINGAIN - [4:2] */ +#define WM5100_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [4:2] */ +#define WM5100_DRC_MINGAIN_WIDTH 3 /* DRC_MINGAIN - [4:2] */ +#define WM5100_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */ +#define WM5100_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */ +#define WM5100_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */ + +/* + * R3714 (0xE82) - DRC1 ctrl3 + */ +#define WM5100_DRC_NG_MINGAIN_MASK 0xF000 /* DRC_NG_MINGAIN - [15:12] */ +#define WM5100_DRC_NG_MINGAIN_SHIFT 12 /* DRC_NG_MINGAIN - [15:12] */ +#define WM5100_DRC_NG_MINGAIN_WIDTH 4 /* DRC_NG_MINGAIN - [15:12] */ +#define WM5100_DRC_NG_EXP_MASK 0x0C00 /* DRC_NG_EXP - [11:10] */ +#define WM5100_DRC_NG_EXP_SHIFT 10 /* DRC_NG_EXP - [11:10] */ +#define WM5100_DRC_NG_EXP_WIDTH 2 /* DRC_NG_EXP - [11:10] */ +#define WM5100_DRC_QR_THR_MASK 0x0300 /* DRC_QR_THR - [9:8] */ +#define WM5100_DRC_QR_THR_SHIFT 8 /* DRC_QR_THR - [9:8] */ +#define WM5100_DRC_QR_THR_WIDTH 2 /* DRC_QR_THR - [9:8] */ +#define WM5100_DRC_QR_DCY_MASK 0x00C0 /* DRC_QR_DCY - [7:6] */ +#define WM5100_DRC_QR_DCY_SHIFT 6 /* DRC_QR_DCY - [7:6] */ +#define WM5100_DRC_QR_DCY_WIDTH 2 /* DRC_QR_DCY - [7:6] */ +#define WM5100_DRC_HI_COMP_MASK 0x0038 /* DRC_HI_COMP - [5:3] */ +#define WM5100_DRC_HI_COMP_SHIFT 3 /* DRC_HI_COMP - [5:3] */ +#define WM5100_DRC_HI_COMP_WIDTH 3 /* DRC_HI_COMP - [5:3] */ +#define WM5100_DRC_LO_COMP_MASK 0x0007 /* DRC_LO_COMP - [2:0] */ +#define WM5100_DRC_LO_COMP_SHIFT 0 /* DRC_LO_COMP - [2:0] */ +#define WM5100_DRC_LO_COMP_WIDTH 3 /* DRC_LO_COMP - [2:0] */ + +/* + * R3715 (0xE83) - DRC1 ctrl4 + */ +#define WM5100_DRC_KNEE_IP_MASK 0x07E0 /* DRC_KNEE_IP - [10:5] */ +#define WM5100_DRC_KNEE_IP_SHIFT 5 /* DRC_KNEE_IP - [10:5] */ +#define WM5100_DRC_KNEE_IP_WIDTH 6 /* DRC_KNEE_IP - [10:5] */ +#define WM5100_DRC_KNEE_OP_MASK 0x001F /* DRC_KNEE_OP - [4:0] */ +#define WM5100_DRC_KNEE_OP_SHIFT 0 /* DRC_KNEE_OP - [4:0] */ +#define WM5100_DRC_KNEE_OP_WIDTH 5 /* DRC_KNEE_OP - [4:0] */ + +/* + * R3716 (0xE84) - DRC1 ctrl5 + */ +#define WM5100_DRC_KNEE2_IP_MASK 0x03E0 /* DRC_KNEE2_IP - [9:5] */ +#define WM5100_DRC_KNEE2_IP_SHIFT 5 /* DRC_KNEE2_IP - [9:5] */ +#define WM5100_DRC_KNEE2_IP_WIDTH 5 /* DRC_KNEE2_IP - [9:5] */ +#define WM5100_DRC_KNEE2_OP_MASK 0x001F /* DRC_KNEE2_OP - [4:0] */ +#define WM5100_DRC_KNEE2_OP_SHIFT 0 /* DRC_KNEE2_OP - [4:0] */ +#define WM5100_DRC_KNEE2_OP_WIDTH 5 /* DRC_KNEE2_OP - [4:0] */ + +/* + * R3776 (0xEC0) - HPLPF1_1 + */ +#define WM5100_LHPF1_MODE 0x0002 /* LHPF1_MODE */ +#define WM5100_LHPF1_MODE_MASK 0x0002 /* LHPF1_MODE */ +#define WM5100_LHPF1_MODE_SHIFT 1 /* LHPF1_MODE */ +#define WM5100_LHPF1_MODE_WIDTH 1 /* LHPF1_MODE */ +#define WM5100_LHPF1_ENA 0x0001 /* LHPF1_ENA */ +#define WM5100_LHPF1_ENA_MASK 0x0001 /* LHPF1_ENA */ +#define WM5100_LHPF1_ENA_SHIFT 0 /* LHPF1_ENA */ +#define WM5100_LHPF1_ENA_WIDTH 1 /* LHPF1_ENA */ + +/* + * R3777 (0xEC1) - HPLPF1_2 + */ +#define WM5100_LHPF1_COEFF_MASK 0xFFFF /* LHPF1_COEFF - [15:0] */ +#define WM5100_LHPF1_COEFF_SHIFT 0 /* LHPF1_COEFF - [15:0] */ +#define WM5100_LHPF1_COEFF_WIDTH 16 /* LHPF1_COEFF - [15:0] */ + +/* + * R3780 (0xEC4) - HPLPF2_1 + */ +#define WM5100_LHPF2_MODE 0x0002 /* LHPF2_MODE */ +#define WM5100_LHPF2_MODE_MASK 0x0002 /* LHPF2_MODE */ +#define WM5100_LHPF2_MODE_SHIFT 1 /* LHPF2_MODE */ +#define WM5100_LHPF2_MODE_WIDTH 1 /* LHPF2_MODE */ +#define WM5100_LHPF2_ENA 0x0001 /* LHPF2_ENA */ +#define WM5100_LHPF2_ENA_MASK 0x0001 /* LHPF2_ENA */ +#define WM5100_LHPF2_ENA_SHIFT 0 /* LHPF2_ENA */ +#define WM5100_LHPF2_ENA_WIDTH 1 /* LHPF2_ENA */ + +/* + * R3781 (0xEC5) - HPLPF2_2 + */ +#define WM5100_LHPF2_COEFF_MASK 0xFFFF /* LHPF2_COEFF - [15:0] */ +#define WM5100_LHPF2_COEFF_SHIFT 0 /* LHPF2_COEFF - [15:0] */ +#define WM5100_LHPF2_COEFF_WIDTH 16 /* LHPF2_COEFF - [15:0] */ + +/* + * R3784 (0xEC8) - HPLPF3_1 + */ +#define WM5100_LHPF3_MODE 0x0002 /* LHPF3_MODE */ +#define WM5100_LHPF3_MODE_MASK 0x0002 /* LHPF3_MODE */ +#define WM5100_LHPF3_MODE_SHIFT 1 /* LHPF3_MODE */ +#define WM5100_LHPF3_MODE_WIDTH 1 /* LHPF3_MODE */ +#define WM5100_LHPF3_ENA 0x0001 /* LHPF3_ENA */ +#define WM5100_LHPF3_ENA_MASK 0x0001 /* LHPF3_ENA */ +#define WM5100_LHPF3_ENA_SHIFT 0 /* LHPF3_ENA */ +#define WM5100_LHPF3_ENA_WIDTH 1 /* LHPF3_ENA */ + +/* + * R3785 (0xEC9) - HPLPF3_2 + */ +#define WM5100_LHPF3_COEFF_MASK 0xFFFF /* LHPF3_COEFF - [15:0] */ +#define WM5100_LHPF3_COEFF_SHIFT 0 /* LHPF3_COEFF - [15:0] */ +#define WM5100_LHPF3_COEFF_WIDTH 16 /* LHPF3_COEFF - [15:0] */ + +/* + * R3788 (0xECC) - HPLPF4_1 + */ +#define WM5100_LHPF4_MODE 0x0002 /* LHPF4_MODE */ +#define WM5100_LHPF4_MODE_MASK 0x0002 /* LHPF4_MODE */ +#define WM5100_LHPF4_MODE_SHIFT 1 /* LHPF4_MODE */ +#define WM5100_LHPF4_MODE_WIDTH 1 /* LHPF4_MODE */ +#define WM5100_LHPF4_ENA 0x0001 /* LHPF4_ENA */ +#define WM5100_LHPF4_ENA_MASK 0x0001 /* LHPF4_ENA */ +#define WM5100_LHPF4_ENA_SHIFT 0 /* LHPF4_ENA */ +#define WM5100_LHPF4_ENA_WIDTH 1 /* LHPF4_ENA */ + +/* + * R3789 (0xECD) - HPLPF4_2 + */ +#define WM5100_LHPF4_COEFF_MASK 0xFFFF /* LHPF4_COEFF - [15:0] */ +#define WM5100_LHPF4_COEFF_SHIFT 0 /* LHPF4_COEFF - [15:0] */ +#define WM5100_LHPF4_COEFF_WIDTH 16 /* LHPF4_COEFF - [15:0] */ + +/* + * R4132 (0x1024) - DSP2 Control 30 + */ +#define WM5100_DSP2_RATE_MASK 0xC000 /* DSP2_RATE - [15:14] */ +#define WM5100_DSP2_RATE_SHIFT 14 /* DSP2_RATE - [15:14] */ +#define WM5100_DSP2_RATE_WIDTH 2 /* DSP2_RATE - [15:14] */ +#define WM5100_DSP2_DBG_CLK_ENA 0x0008 /* DSP2_DBG_CLK_ENA */ +#define WM5100_DSP2_DBG_CLK_ENA_MASK 0x0008 /* DSP2_DBG_CLK_ENA */ +#define WM5100_DSP2_DBG_CLK_ENA_SHIFT 3 /* DSP2_DBG_CLK_ENA */ +#define WM5100_DSP2_DBG_CLK_ENA_WIDTH 1 /* DSP2_DBG_CLK_ENA */ +#define WM5100_DSP2_SYS_ENA 0x0004 /* DSP2_SYS_ENA */ +#define WM5100_DSP2_SYS_ENA_MASK 0x0004 /* DSP2_SYS_ENA */ +#define WM5100_DSP2_SYS_ENA_SHIFT 2 /* DSP2_SYS_ENA */ +#define WM5100_DSP2_SYS_ENA_WIDTH 1 /* DSP2_SYS_ENA */ +#define WM5100_DSP2_CORE_ENA 0x0002 /* DSP2_CORE_ENA */ +#define WM5100_DSP2_CORE_ENA_MASK 0x0002 /* DSP2_CORE_ENA */ +#define WM5100_DSP2_CORE_ENA_SHIFT 1 /* DSP2_CORE_ENA */ +#define WM5100_DSP2_CORE_ENA_WIDTH 1 /* DSP2_CORE_ENA */ +#define WM5100_DSP2_START 0x0001 /* DSP2_START */ +#define WM5100_DSP2_START_MASK 0x0001 /* DSP2_START */ +#define WM5100_DSP2_START_SHIFT 0 /* DSP2_START */ +#define WM5100_DSP2_START_WIDTH 1 /* DSP2_START */ + +/* + * R3876 (0xF24) - DSP1 Control 30 + */ +#define WM5100_DSP1_RATE_MASK 0xC000 /* DSP1_RATE - [15:14] */ +#define WM5100_DSP1_RATE_SHIFT 14 /* DSP1_RATE - [15:14] */ +#define WM5100_DSP1_RATE_WIDTH 2 /* DSP1_RATE - [15:14] */ +#define WM5100_DSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ +#define WM5100_DSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ +#define WM5100_DSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ +#define WM5100_DSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ +#define WM5100_DSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ +#define WM5100_DSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ +#define WM5100_DSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ +#define WM5100_DSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ +#define WM5100_DSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ +#define WM5100_DSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ +#define WM5100_DSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ +#define WM5100_DSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ +#define WM5100_DSP1_START 0x0001 /* DSP1_START */ +#define WM5100_DSP1_START_MASK 0x0001 /* DSP1_START */ +#define WM5100_DSP1_START_SHIFT 0 /* DSP1_START */ +#define WM5100_DSP1_START_WIDTH 1 /* DSP1_START */ + +/* + * R4388 (0x1124) - DSP3 Control 30 + */ +#define WM5100_DSP3_RATE_MASK 0xC000 /* DSP3_RATE - [15:14] */ +#define WM5100_DSP3_RATE_SHIFT 14 /* DSP3_RATE - [15:14] */ +#define WM5100_DSP3_RATE_WIDTH 2 /* DSP3_RATE - [15:14] */ +#define WM5100_DSP3_DBG_CLK_ENA 0x0008 /* DSP3_DBG_CLK_ENA */ +#define WM5100_DSP3_DBG_CLK_ENA_MASK 0x0008 /* DSP3_DBG_CLK_ENA */ +#define WM5100_DSP3_DBG_CLK_ENA_SHIFT 3 /* DSP3_DBG_CLK_ENA */ +#define WM5100_DSP3_DBG_CLK_ENA_WIDTH 1 /* DSP3_DBG_CLK_ENA */ +#define WM5100_DSP3_SYS_ENA 0x0004 /* DSP3_SYS_ENA */ +#define WM5100_DSP3_SYS_ENA_MASK 0x0004 /* DSP3_SYS_ENA */ +#define WM5100_DSP3_SYS_ENA_SHIFT 2 /* DSP3_SYS_ENA */ +#define WM5100_DSP3_SYS_ENA_WIDTH 1 /* DSP3_SYS_ENA */ +#define WM5100_DSP3_CORE_ENA 0x0002 /* DSP3_CORE_ENA */ +#define WM5100_DSP3_CORE_ENA_MASK 0x0002 /* DSP3_CORE_ENA */ +#define WM5100_DSP3_CORE_ENA_SHIFT 1 /* DSP3_CORE_ENA */ +#define WM5100_DSP3_CORE_ENA_WIDTH 1 /* DSP3_CORE_ENA */ +#define WM5100_DSP3_START 0x0001 /* DSP3_START */ +#define WM5100_DSP3_START_MASK 0x0001 /* DSP3_START */ +#define WM5100_DSP3_START_SHIFT 0 /* DSP3_START */ +#define WM5100_DSP3_START_WIDTH 1 /* DSP3_START */ + +/* + * R16384 (0x4000) - DSP1 DM 0 + */ +#define WM5100_DSP1_DM_START_1_MASK 0x00FF /* DSP1_DM_START - [7:0] */ +#define WM5100_DSP1_DM_START_1_SHIFT 0 /* DSP1_DM_START - [7:0] */ +#define WM5100_DSP1_DM_START_1_WIDTH 8 /* DSP1_DM_START - [7:0] */ + +/* + * R16385 (0x4001) - DSP1 DM 1 + */ +#define WM5100_DSP1_DM_START_MASK 0xFFFF /* DSP1_DM_START - [15:0] */ +#define WM5100_DSP1_DM_START_SHIFT 0 /* DSP1_DM_START - [15:0] */ +#define WM5100_DSP1_DM_START_WIDTH 16 /* DSP1_DM_START - [15:0] */ + +/* + * R16386 (0x4002) - DSP1 DM 2 + */ +#define WM5100_DSP1_DM_1_1_MASK 0x00FF /* DSP1_DM_1 - [7:0] */ +#define WM5100_DSP1_DM_1_1_SHIFT 0 /* DSP1_DM_1 - [7:0] */ +#define WM5100_DSP1_DM_1_1_WIDTH 8 /* DSP1_DM_1 - [7:0] */ + +/* + * R16387 (0x4003) - DSP1 DM 3 + */ +#define WM5100_DSP1_DM_1_MASK 0xFFFF /* DSP1_DM_1 - [15:0] */ +#define WM5100_DSP1_DM_1_SHIFT 0 /* DSP1_DM_1 - [15:0] */ +#define WM5100_DSP1_DM_1_WIDTH 16 /* DSP1_DM_1 - [15:0] */ + +/* + * R16892 (0x41FC) - DSP1 DM 508 + */ +#define WM5100_DSP1_DM_254_1_MASK 0x00FF /* DSP1_DM_254 - [7:0] */ +#define WM5100_DSP1_DM_254_1_SHIFT 0 /* DSP1_DM_254 - [7:0] */ +#define WM5100_DSP1_DM_254_1_WIDTH 8 /* DSP1_DM_254 - [7:0] */ + +/* + * R16893 (0x41FD) - DSP1 DM 509 + */ +#define WM5100_DSP1_DM_254_MASK 0xFFFF /* DSP1_DM_254 - [15:0] */ +#define WM5100_DSP1_DM_254_SHIFT 0 /* DSP1_DM_254 - [15:0] */ +#define WM5100_DSP1_DM_254_WIDTH 16 /* DSP1_DM_254 - [15:0] */ + +/* + * R16894 (0x41FE) - DSP1 DM 510 + */ +#define WM5100_DSP1_DM_END_1_MASK 0x00FF /* DSP1_DM_END - [7:0] */ +#define WM5100_DSP1_DM_END_1_SHIFT 0 /* DSP1_DM_END - [7:0] */ +#define WM5100_DSP1_DM_END_1_WIDTH 8 /* DSP1_DM_END - [7:0] */ + +/* + * R16895 (0x41FF) - DSP1 DM 511 + */ +#define WM5100_DSP1_DM_END_MASK 0xFFFF /* DSP1_DM_END - [15:0] */ +#define WM5100_DSP1_DM_END_SHIFT 0 /* DSP1_DM_END - [15:0] */ +#define WM5100_DSP1_DM_END_WIDTH 16 /* DSP1_DM_END - [15:0] */ + +/* + * R18432 (0x4800) - DSP1 PM 0 + */ +#define WM5100_DSP1_PM_START_2_MASK 0x00FF /* DSP1_PM_START - [7:0] */ +#define WM5100_DSP1_PM_START_2_SHIFT 0 /* DSP1_PM_START - [7:0] */ +#define WM5100_DSP1_PM_START_2_WIDTH 8 /* DSP1_PM_START - [7:0] */ + +/* + * R18433 (0x4801) - DSP1 PM 1 + */ +#define WM5100_DSP1_PM_START_1_MASK 0xFFFF /* DSP1_PM_START - [15:0] */ +#define WM5100_DSP1_PM_START_1_SHIFT 0 /* DSP1_PM_START - [15:0] */ +#define WM5100_DSP1_PM_START_1_WIDTH 16 /* DSP1_PM_START - [15:0] */ + +/* + * R18434 (0x4802) - DSP1 PM 2 + */ +#define WM5100_DSP1_PM_START_MASK 0xFFFF /* DSP1_PM_START - [15:0] */ +#define WM5100_DSP1_PM_START_SHIFT 0 /* DSP1_PM_START - [15:0] */ +#define WM5100_DSP1_PM_START_WIDTH 16 /* DSP1_PM_START - [15:0] */ + +/* + * R18435 (0x4803) - DSP1 PM 3 + */ +#define WM5100_DSP1_PM_1_2_MASK 0x00FF /* DSP1_PM_1 - [7:0] */ +#define WM5100_DSP1_PM_1_2_SHIFT 0 /* DSP1_PM_1 - [7:0] */ +#define WM5100_DSP1_PM_1_2_WIDTH 8 /* DSP1_PM_1 - [7:0] */ + +/* + * R18436 (0x4804) - DSP1 PM 4 + */ +#define WM5100_DSP1_PM_1_1_MASK 0xFFFF /* DSP1_PM_1 - [15:0] */ +#define WM5100_DSP1_PM_1_1_SHIFT 0 /* DSP1_PM_1 - [15:0] */ +#define WM5100_DSP1_PM_1_1_WIDTH 16 /* DSP1_PM_1 - [15:0] */ + +/* + * R18437 (0x4805) - DSP1 PM 5 + */ +#define WM5100_DSP1_PM_1_MASK 0xFFFF /* DSP1_PM_1 - [15:0] */ +#define WM5100_DSP1_PM_1_SHIFT 0 /* DSP1_PM_1 - [15:0] */ +#define WM5100_DSP1_PM_1_WIDTH 16 /* DSP1_PM_1 - [15:0] */ + +/* + * R19962 (0x4DFA) - DSP1 PM 1530 + */ +#define WM5100_DSP1_PM_510_2_MASK 0x00FF /* DSP1_PM_510 - [7:0] */ +#define WM5100_DSP1_PM_510_2_SHIFT 0 /* DSP1_PM_510 - [7:0] */ +#define WM5100_DSP1_PM_510_2_WIDTH 8 /* DSP1_PM_510 - [7:0] */ + +/* + * R19963 (0x4DFB) - DSP1 PM 1531 + */ +#define WM5100_DSP1_PM_510_1_MASK 0xFFFF /* DSP1_PM_510 - [15:0] */ +#define WM5100_DSP1_PM_510_1_SHIFT 0 /* DSP1_PM_510 - [15:0] */ +#define WM5100_DSP1_PM_510_1_WIDTH 16 /* DSP1_PM_510 - [15:0] */ + +/* + * R19964 (0x4DFC) - DSP1 PM 1532 + */ +#define WM5100_DSP1_PM_510_MASK 0xFFFF /* DSP1_PM_510 - [15:0] */ +#define WM5100_DSP1_PM_510_SHIFT 0 /* DSP1_PM_510 - [15:0] */ +#define WM5100_DSP1_PM_510_WIDTH 16 /* DSP1_PM_510 - [15:0] */ + +/* + * R19965 (0x4DFD) - DSP1 PM 1533 + */ +#define WM5100_DSP1_PM_END_2_MASK 0x00FF /* DSP1_PM_END - [7:0] */ +#define WM5100_DSP1_PM_END_2_SHIFT 0 /* DSP1_PM_END - [7:0] */ +#define WM5100_DSP1_PM_END_2_WIDTH 8 /* DSP1_PM_END - [7:0] */ + +/* + * R19966 (0x4DFE) - DSP1 PM 1534 + */ +#define WM5100_DSP1_PM_END_1_MASK 0xFFFF /* DSP1_PM_END - [15:0] */ +#define WM5100_DSP1_PM_END_1_SHIFT 0 /* DSP1_PM_END - [15:0] */ +#define WM5100_DSP1_PM_END_1_WIDTH 16 /* DSP1_PM_END - [15:0] */ + +/* + * R19967 (0x4DFF) - DSP1 PM 1535 + */ +#define WM5100_DSP1_PM_END_MASK 0xFFFF /* DSP1_PM_END - [15:0] */ +#define WM5100_DSP1_PM_END_SHIFT 0 /* DSP1_PM_END - [15:0] */ +#define WM5100_DSP1_PM_END_WIDTH 16 /* DSP1_PM_END - [15:0] */ + +/* + * R20480 (0x5000) - DSP1 ZM 0 + */ +#define WM5100_DSP1_ZM_START_1_MASK 0x00FF /* DSP1_ZM_START - [7:0] */ +#define WM5100_DSP1_ZM_START_1_SHIFT 0 /* DSP1_ZM_START - [7:0] */ +#define WM5100_DSP1_ZM_START_1_WIDTH 8 /* DSP1_ZM_START - [7:0] */ + +/* + * R20481 (0x5001) - DSP1 ZM 1 + */ +#define WM5100_DSP1_ZM_START_MASK 0xFFFF /* DSP1_ZM_START - [15:0] */ +#define WM5100_DSP1_ZM_START_SHIFT 0 /* DSP1_ZM_START - [15:0] */ +#define WM5100_DSP1_ZM_START_WIDTH 16 /* DSP1_ZM_START - [15:0] */ + +/* + * R20482 (0x5002) - DSP1 ZM 2 + */ +#define WM5100_DSP1_ZM_1_1_MASK 0x00FF /* DSP1_ZM_1 - [7:0] */ +#define WM5100_DSP1_ZM_1_1_SHIFT 0 /* DSP1_ZM_1 - [7:0] */ +#define WM5100_DSP1_ZM_1_1_WIDTH 8 /* DSP1_ZM_1 - [7:0] */ + +/* + * R20483 (0x5003) - DSP1 ZM 3 + */ +#define WM5100_DSP1_ZM_1_MASK 0xFFFF /* DSP1_ZM_1 - [15:0] */ +#define WM5100_DSP1_ZM_1_SHIFT 0 /* DSP1_ZM_1 - [15:0] */ +#define WM5100_DSP1_ZM_1_WIDTH 16 /* DSP1_ZM_1 - [15:0] */ + +/* + * R22524 (0x57FC) - DSP1 ZM 2044 + */ +#define WM5100_DSP1_ZM_1022_1_MASK 0x00FF /* DSP1_ZM_1022 - [7:0] */ +#define WM5100_DSP1_ZM_1022_1_SHIFT 0 /* DSP1_ZM_1022 - [7:0] */ +#define WM5100_DSP1_ZM_1022_1_WIDTH 8 /* DSP1_ZM_1022 - [7:0] */ + +/* + * R22525 (0x57FD) - DSP1 ZM 2045 + */ +#define WM5100_DSP1_ZM_1022_MASK 0xFFFF /* DSP1_ZM_1022 - [15:0] */ +#define WM5100_DSP1_ZM_1022_SHIFT 0 /* DSP1_ZM_1022 - [15:0] */ +#define WM5100_DSP1_ZM_1022_WIDTH 16 /* DSP1_ZM_1022 - [15:0] */ + +/* + * R22526 (0x57FE) - DSP1 ZM 2046 + */ +#define WM5100_DSP1_ZM_END_1_MASK 0x00FF /* DSP1_ZM_END - [7:0] */ +#define WM5100_DSP1_ZM_END_1_SHIFT 0 /* DSP1_ZM_END - [7:0] */ +#define WM5100_DSP1_ZM_END_1_WIDTH 8 /* DSP1_ZM_END - [7:0] */ + +/* + * R22527 (0x57FF) - DSP1 ZM 2047 + */ +#define WM5100_DSP1_ZM_END_MASK 0xFFFF /* DSP1_ZM_END - [15:0] */ +#define WM5100_DSP1_ZM_END_SHIFT 0 /* DSP1_ZM_END - [15:0] */ +#define WM5100_DSP1_ZM_END_WIDTH 16 /* DSP1_ZM_END - [15:0] */ + +/* + * R24576 (0x6000) - DSP2 DM 0 + */ +#define WM5100_DSP2_DM_START_1_MASK 0x00FF /* DSP2_DM_START - [7:0] */ +#define WM5100_DSP2_DM_START_1_SHIFT 0 /* DSP2_DM_START - [7:0] */ +#define WM5100_DSP2_DM_START_1_WIDTH 8 /* DSP2_DM_START - [7:0] */ + +/* + * R24577 (0x6001) - DSP2 DM 1 + */ +#define WM5100_DSP2_DM_START_MASK 0xFFFF /* DSP2_DM_START - [15:0] */ +#define WM5100_DSP2_DM_START_SHIFT 0 /* DSP2_DM_START - [15:0] */ +#define WM5100_DSP2_DM_START_WIDTH 16 /* DSP2_DM_START - [15:0] */ + +/* + * R24578 (0x6002) - DSP2 DM 2 + */ +#define WM5100_DSP2_DM_1_1_MASK 0x00FF /* DSP2_DM_1 - [7:0] */ +#define WM5100_DSP2_DM_1_1_SHIFT 0 /* DSP2_DM_1 - [7:0] */ +#define WM5100_DSP2_DM_1_1_WIDTH 8 /* DSP2_DM_1 - [7:0] */ + +/* + * R24579 (0x6003) - DSP2 DM 3 + */ +#define WM5100_DSP2_DM_1_MASK 0xFFFF /* DSP2_DM_1 - [15:0] */ +#define WM5100_DSP2_DM_1_SHIFT 0 /* DSP2_DM_1 - [15:0] */ +#define WM5100_DSP2_DM_1_WIDTH 16 /* DSP2_DM_1 - [15:0] */ + +/* + * R25084 (0x61FC) - DSP2 DM 508 + */ +#define WM5100_DSP2_DM_254_1_MASK 0x00FF /* DSP2_DM_254 - [7:0] */ +#define WM5100_DSP2_DM_254_1_SHIFT 0 /* DSP2_DM_254 - [7:0] */ +#define WM5100_DSP2_DM_254_1_WIDTH 8 /* DSP2_DM_254 - [7:0] */ + +/* + * R25085 (0x61FD) - DSP2 DM 509 + */ +#define WM5100_DSP2_DM_254_MASK 0xFFFF /* DSP2_DM_254 - [15:0] */ +#define WM5100_DSP2_DM_254_SHIFT 0 /* DSP2_DM_254 - [15:0] */ +#define WM5100_DSP2_DM_254_WIDTH 16 /* DSP2_DM_254 - [15:0] */ + +/* + * R25086 (0x61FE) - DSP2 DM 510 + */ +#define WM5100_DSP2_DM_END_1_MASK 0x00FF /* DSP2_DM_END - [7:0] */ +#define WM5100_DSP2_DM_END_1_SHIFT 0 /* DSP2_DM_END - [7:0] */ +#define WM5100_DSP2_DM_END_1_WIDTH 8 /* DSP2_DM_END - [7:0] */ + +/* + * R25087 (0x61FF) - DSP2 DM 511 + */ +#define WM5100_DSP2_DM_END_MASK 0xFFFF /* DSP2_DM_END - [15:0] */ +#define WM5100_DSP2_DM_END_SHIFT 0 /* DSP2_DM_END - [15:0] */ +#define WM5100_DSP2_DM_END_WIDTH 16 /* DSP2_DM_END - [15:0] */ + +/* + * R26624 (0x6800) - DSP2 PM 0 + */ +#define WM5100_DSP2_PM_START_2_MASK 0x00FF /* DSP2_PM_START - [7:0] */ +#define WM5100_DSP2_PM_START_2_SHIFT 0 /* DSP2_PM_START - [7:0] */ +#define WM5100_DSP2_PM_START_2_WIDTH 8 /* DSP2_PM_START - [7:0] */ + +/* + * R26625 (0x6801) - DSP2 PM 1 + */ +#define WM5100_DSP2_PM_START_1_MASK 0xFFFF /* DSP2_PM_START - [15:0] */ +#define WM5100_DSP2_PM_START_1_SHIFT 0 /* DSP2_PM_START - [15:0] */ +#define WM5100_DSP2_PM_START_1_WIDTH 16 /* DSP2_PM_START - [15:0] */ + +/* + * R26626 (0x6802) - DSP2 PM 2 + */ +#define WM5100_DSP2_PM_START_MASK 0xFFFF /* DSP2_PM_START - [15:0] */ +#define WM5100_DSP2_PM_START_SHIFT 0 /* DSP2_PM_START - [15:0] */ +#define WM5100_DSP2_PM_START_WIDTH 16 /* DSP2_PM_START - [15:0] */ + +/* + * R26627 (0x6803) - DSP2 PM 3 + */ +#define WM5100_DSP2_PM_1_2_MASK 0x00FF /* DSP2_PM_1 - [7:0] */ +#define WM5100_DSP2_PM_1_2_SHIFT 0 /* DSP2_PM_1 - [7:0] */ +#define WM5100_DSP2_PM_1_2_WIDTH 8 /* DSP2_PM_1 - [7:0] */ + +/* + * R26628 (0x6804) - DSP2 PM 4 + */ +#define WM5100_DSP2_PM_1_1_MASK 0xFFFF /* DSP2_PM_1 - [15:0] */ +#define WM5100_DSP2_PM_1_1_SHIFT 0 /* DSP2_PM_1 - [15:0] */ +#define WM5100_DSP2_PM_1_1_WIDTH 16 /* DSP2_PM_1 - [15:0] */ + +/* + * R26629 (0x6805) - DSP2 PM 5 + */ +#define WM5100_DSP2_PM_1_MASK 0xFFFF /* DSP2_PM_1 - [15:0] */ +#define WM5100_DSP2_PM_1_SHIFT 0 /* DSP2_PM_1 - [15:0] */ +#define WM5100_DSP2_PM_1_WIDTH 16 /* DSP2_PM_1 - [15:0] */ + +/* + * R28154 (0x6DFA) - DSP2 PM 1530 + */ +#define WM5100_DSP2_PM_510_2_MASK 0x00FF /* DSP2_PM_510 - [7:0] */ +#define WM5100_DSP2_PM_510_2_SHIFT 0 /* DSP2_PM_510 - [7:0] */ +#define WM5100_DSP2_PM_510_2_WIDTH 8 /* DSP2_PM_510 - [7:0] */ + +/* + * R28155 (0x6DFB) - DSP2 PM 1531 + */ +#define WM5100_DSP2_PM_510_1_MASK 0xFFFF /* DSP2_PM_510 - [15:0] */ +#define WM5100_DSP2_PM_510_1_SHIFT 0 /* DSP2_PM_510 - [15:0] */ +#define WM5100_DSP2_PM_510_1_WIDTH 16 /* DSP2_PM_510 - [15:0] */ + +/* + * R28156 (0x6DFC) - DSP2 PM 1532 + */ +#define WM5100_DSP2_PM_510_MASK 0xFFFF /* DSP2_PM_510 - [15:0] */ +#define WM5100_DSP2_PM_510_SHIFT 0 /* DSP2_PM_510 - [15:0] */ +#define WM5100_DSP2_PM_510_WIDTH 16 /* DSP2_PM_510 - [15:0] */ + +/* + * R28157 (0x6DFD) - DSP2 PM 1533 + */ +#define WM5100_DSP2_PM_END_2_MASK 0x00FF /* DSP2_PM_END - [7:0] */ +#define WM5100_DSP2_PM_END_2_SHIFT 0 /* DSP2_PM_END - [7:0] */ +#define WM5100_DSP2_PM_END_2_WIDTH 8 /* DSP2_PM_END - [7:0] */ + +/* + * R28158 (0x6DFE) - DSP2 PM 1534 + */ +#define WM5100_DSP2_PM_END_1_MASK 0xFFFF /* DSP2_PM_END - [15:0] */ +#define WM5100_DSP2_PM_END_1_SHIFT 0 /* DSP2_PM_END - [15:0] */ +#define WM5100_DSP2_PM_END_1_WIDTH 16 /* DSP2_PM_END - [15:0] */ + +/* + * R28159 (0x6DFF) - DSP2 PM 1535 + */ +#define WM5100_DSP2_PM_END_MASK 0xFFFF /* DSP2_PM_END - [15:0] */ +#define WM5100_DSP2_PM_END_SHIFT 0 /* DSP2_PM_END - [15:0] */ +#define WM5100_DSP2_PM_END_WIDTH 16 /* DSP2_PM_END - [15:0] */ + +/* + * R28672 (0x7000) - DSP2 ZM 0 + */ +#define WM5100_DSP2_ZM_START_1_MASK 0x00FF /* DSP2_ZM_START - [7:0] */ +#define WM5100_DSP2_ZM_START_1_SHIFT 0 /* DSP2_ZM_START - [7:0] */ +#define WM5100_DSP2_ZM_START_1_WIDTH 8 /* DSP2_ZM_START - [7:0] */ + +/* + * R28673 (0x7001) - DSP2 ZM 1 + */ +#define WM5100_DSP2_ZM_START_MASK 0xFFFF /* DSP2_ZM_START - [15:0] */ +#define WM5100_DSP2_ZM_START_SHIFT 0 /* DSP2_ZM_START - [15:0] */ +#define WM5100_DSP2_ZM_START_WIDTH 16 /* DSP2_ZM_START - [15:0] */ + +/* + * R28674 (0x7002) - DSP2 ZM 2 + */ +#define WM5100_DSP2_ZM_1_1_MASK 0x00FF /* DSP2_ZM_1 - [7:0] */ +#define WM5100_DSP2_ZM_1_1_SHIFT 0 /* DSP2_ZM_1 - [7:0] */ +#define WM5100_DSP2_ZM_1_1_WIDTH 8 /* DSP2_ZM_1 - [7:0] */ + +/* + * R28675 (0x7003) - DSP2 ZM 3 + */ +#define WM5100_DSP2_ZM_1_MASK 0xFFFF /* DSP2_ZM_1 - [15:0] */ +#define WM5100_DSP2_ZM_1_SHIFT 0 /* DSP2_ZM_1 - [15:0] */ +#define WM5100_DSP2_ZM_1_WIDTH 16 /* DSP2_ZM_1 - [15:0] */ + +/* + * R30716 (0x77FC) - DSP2 ZM 2044 + */ +#define WM5100_DSP2_ZM_1022_1_MASK 0x00FF /* DSP2_ZM_1022 - [7:0] */ +#define WM5100_DSP2_ZM_1022_1_SHIFT 0 /* DSP2_ZM_1022 - [7:0] */ +#define WM5100_DSP2_ZM_1022_1_WIDTH 8 /* DSP2_ZM_1022 - [7:0] */ + +/* + * R30717 (0x77FD) - DSP2 ZM 2045 + */ +#define WM5100_DSP2_ZM_1022_MASK 0xFFFF /* DSP2_ZM_1022 - [15:0] */ +#define WM5100_DSP2_ZM_1022_SHIFT 0 /* DSP2_ZM_1022 - [15:0] */ +#define WM5100_DSP2_ZM_1022_WIDTH 16 /* DSP2_ZM_1022 - [15:0] */ + +/* + * R30718 (0x77FE) - DSP2 ZM 2046 + */ +#define WM5100_DSP2_ZM_END_1_MASK 0x00FF /* DSP2_ZM_END - [7:0] */ +#define WM5100_DSP2_ZM_END_1_SHIFT 0 /* DSP2_ZM_END - [7:0] */ +#define WM5100_DSP2_ZM_END_1_WIDTH 8 /* DSP2_ZM_END - [7:0] */ + +/* + * R30719 (0x77FF) - DSP2 ZM 2047 + */ +#define WM5100_DSP2_ZM_END_MASK 0xFFFF /* DSP2_ZM_END - [15:0] */ +#define WM5100_DSP2_ZM_END_SHIFT 0 /* DSP2_ZM_END - [15:0] */ +#define WM5100_DSP2_ZM_END_WIDTH 16 /* DSP2_ZM_END - [15:0] */ + +/* + * R32768 (0x8000) - DSP3 DM 0 + */ +#define WM5100_DSP3_DM_START_1_MASK 0x00FF /* DSP3_DM_START - [7:0] */ +#define WM5100_DSP3_DM_START_1_SHIFT 0 /* DSP3_DM_START - [7:0] */ +#define WM5100_DSP3_DM_START_1_WIDTH 8 /* DSP3_DM_START - [7:0] */ + +/* + * R32769 (0x8001) - DSP3 DM 1 + */ +#define WM5100_DSP3_DM_START_MASK 0xFFFF /* DSP3_DM_START - [15:0] */ +#define WM5100_DSP3_DM_START_SHIFT 0 /* DSP3_DM_START - [15:0] */ +#define WM5100_DSP3_DM_START_WIDTH 16 /* DSP3_DM_START - [15:0] */ + +/* + * R32770 (0x8002) - DSP3 DM 2 + */ +#define WM5100_DSP3_DM_1_1_MASK 0x00FF /* DSP3_DM_1 - [7:0] */ +#define WM5100_DSP3_DM_1_1_SHIFT 0 /* DSP3_DM_1 - [7:0] */ +#define WM5100_DSP3_DM_1_1_WIDTH 8 /* DSP3_DM_1 - [7:0] */ + +/* + * R32771 (0x8003) - DSP3 DM 3 + */ +#define WM5100_DSP3_DM_1_MASK 0xFFFF /* DSP3_DM_1 - [15:0] */ +#define WM5100_DSP3_DM_1_SHIFT 0 /* DSP3_DM_1 - [15:0] */ +#define WM5100_DSP3_DM_1_WIDTH 16 /* DSP3_DM_1 - [15:0] */ + +/* + * R33276 (0x81FC) - DSP3 DM 508 + */ +#define WM5100_DSP3_DM_254_1_MASK 0x00FF /* DSP3_DM_254 - [7:0] */ +#define WM5100_DSP3_DM_254_1_SHIFT 0 /* DSP3_DM_254 - [7:0] */ +#define WM5100_DSP3_DM_254_1_WIDTH 8 /* DSP3_DM_254 - [7:0] */ + +/* + * R33277 (0x81FD) - DSP3 DM 509 + */ +#define WM5100_DSP3_DM_254_MASK 0xFFFF /* DSP3_DM_254 - [15:0] */ +#define WM5100_DSP3_DM_254_SHIFT 0 /* DSP3_DM_254 - [15:0] */ +#define WM5100_DSP3_DM_254_WIDTH 16 /* DSP3_DM_254 - [15:0] */ + +/* + * R33278 (0x81FE) - DSP3 DM 510 + */ +#define WM5100_DSP3_DM_END_1_MASK 0x00FF /* DSP3_DM_END - [7:0] */ +#define WM5100_DSP3_DM_END_1_SHIFT 0 /* DSP3_DM_END - [7:0] */ +#define WM5100_DSP3_DM_END_1_WIDTH 8 /* DSP3_DM_END - [7:0] */ + +/* + * R33279 (0x81FF) - DSP3 DM 511 + */ +#define WM5100_DSP3_DM_END_MASK 0xFFFF /* DSP3_DM_END - [15:0] */ +#define WM5100_DSP3_DM_END_SHIFT 0 /* DSP3_DM_END - [15:0] */ +#define WM5100_DSP3_DM_END_WIDTH 16 /* DSP3_DM_END - [15:0] */ + +/* + * R34816 (0x8800) - DSP3 PM 0 + */ +#define WM5100_DSP3_PM_START_2_MASK 0x00FF /* DSP3_PM_START - [7:0] */ +#define WM5100_DSP3_PM_START_2_SHIFT 0 /* DSP3_PM_START - [7:0] */ +#define WM5100_DSP3_PM_START_2_WIDTH 8 /* DSP3_PM_START - [7:0] */ + +/* + * R34817 (0x8801) - DSP3 PM 1 + */ +#define WM5100_DSP3_PM_START_1_MASK 0xFFFF /* DSP3_PM_START - [15:0] */ +#define WM5100_DSP3_PM_START_1_SHIFT 0 /* DSP3_PM_START - [15:0] */ +#define WM5100_DSP3_PM_START_1_WIDTH 16 /* DSP3_PM_START - [15:0] */ + +/* + * R34818 (0x8802) - DSP3 PM 2 + */ +#define WM5100_DSP3_PM_START_MASK 0xFFFF /* DSP3_PM_START - [15:0] */ +#define WM5100_DSP3_PM_START_SHIFT 0 /* DSP3_PM_START - [15:0] */ +#define WM5100_DSP3_PM_START_WIDTH 16 /* DSP3_PM_START - [15:0] */ + +/* + * R34819 (0x8803) - DSP3 PM 3 + */ +#define WM5100_DSP3_PM_1_2_MASK 0x00FF /* DSP3_PM_1 - [7:0] */ +#define WM5100_DSP3_PM_1_2_SHIFT 0 /* DSP3_PM_1 - [7:0] */ +#define WM5100_DSP3_PM_1_2_WIDTH 8 /* DSP3_PM_1 - [7:0] */ + +/* + * R34820 (0x8804) - DSP3 PM 4 + */ +#define WM5100_DSP3_PM_1_1_MASK 0xFFFF /* DSP3_PM_1 - [15:0] */ +#define WM5100_DSP3_PM_1_1_SHIFT 0 /* DSP3_PM_1 - [15:0] */ +#define WM5100_DSP3_PM_1_1_WIDTH 16 /* DSP3_PM_1 - [15:0] */ + +/* + * R34821 (0x8805) - DSP3 PM 5 + */ +#define WM5100_DSP3_PM_1_MASK 0xFFFF /* DSP3_PM_1 - [15:0] */ +#define WM5100_DSP3_PM_1_SHIFT 0 /* DSP3_PM_1 - [15:0] */ +#define WM5100_DSP3_PM_1_WIDTH 16 /* DSP3_PM_1 - [15:0] */ + +/* + * R36346 (0x8DFA) - DSP3 PM 1530 + */ +#define WM5100_DSP3_PM_510_2_MASK 0x00FF /* DSP3_PM_510 - [7:0] */ +#define WM5100_DSP3_PM_510_2_SHIFT 0 /* DSP3_PM_510 - [7:0] */ +#define WM5100_DSP3_PM_510_2_WIDTH 8 /* DSP3_PM_510 - [7:0] */ + +/* + * R36347 (0x8DFB) - DSP3 PM 1531 + */ +#define WM5100_DSP3_PM_510_1_MASK 0xFFFF /* DSP3_PM_510 - [15:0] */ +#define WM5100_DSP3_PM_510_1_SHIFT 0 /* DSP3_PM_510 - [15:0] */ +#define WM5100_DSP3_PM_510_1_WIDTH 16 /* DSP3_PM_510 - [15:0] */ + +/* + * R36348 (0x8DFC) - DSP3 PM 1532 + */ +#define WM5100_DSP3_PM_510_MASK 0xFFFF /* DSP3_PM_510 - [15:0] */ +#define WM5100_DSP3_PM_510_SHIFT 0 /* DSP3_PM_510 - [15:0] */ +#define WM5100_DSP3_PM_510_WIDTH 16 /* DSP3_PM_510 - [15:0] */ + +/* + * R36349 (0x8DFD) - DSP3 PM 1533 + */ +#define WM5100_DSP3_PM_END_2_MASK 0x00FF /* DSP3_PM_END - [7:0] */ +#define WM5100_DSP3_PM_END_2_SHIFT 0 /* DSP3_PM_END - [7:0] */ +#define WM5100_DSP3_PM_END_2_WIDTH 8 /* DSP3_PM_END - [7:0] */ + +/* + * R36350 (0x8DFE) - DSP3 PM 1534 + */ +#define WM5100_DSP3_PM_END_1_MASK 0xFFFF /* DSP3_PM_END - [15:0] */ +#define WM5100_DSP3_PM_END_1_SHIFT 0 /* DSP3_PM_END - [15:0] */ +#define WM5100_DSP3_PM_END_1_WIDTH 16 /* DSP3_PM_END - [15:0] */ + +/* + * R36351 (0x8DFF) - DSP3 PM 1535 + */ +#define WM5100_DSP3_PM_END_MASK 0xFFFF /* DSP3_PM_END - [15:0] */ +#define WM5100_DSP3_PM_END_SHIFT 0 /* DSP3_PM_END - [15:0] */ +#define WM5100_DSP3_PM_END_WIDTH 16 /* DSP3_PM_END - [15:0] */ + +/* + * R36864 (0x9000) - DSP3 ZM 0 + */ +#define WM5100_DSP3_ZM_START_1_MASK 0x00FF /* DSP3_ZM_START - [7:0] */ +#define WM5100_DSP3_ZM_START_1_SHIFT 0 /* DSP3_ZM_START - [7:0] */ +#define WM5100_DSP3_ZM_START_1_WIDTH 8 /* DSP3_ZM_START - [7:0] */ + +/* + * R36865 (0x9001) - DSP3 ZM 1 + */ +#define WM5100_DSP3_ZM_START_MASK 0xFFFF /* DSP3_ZM_START - [15:0] */ +#define WM5100_DSP3_ZM_START_SHIFT 0 /* DSP3_ZM_START - [15:0] */ +#define WM5100_DSP3_ZM_START_WIDTH 16 /* DSP3_ZM_START - [15:0] */ + +/* + * R36866 (0x9002) - DSP3 ZM 2 + */ +#define WM5100_DSP3_ZM_1_1_MASK 0x00FF /* DSP3_ZM_1 - [7:0] */ +#define WM5100_DSP3_ZM_1_1_SHIFT 0 /* DSP3_ZM_1 - [7:0] */ +#define WM5100_DSP3_ZM_1_1_WIDTH 8 /* DSP3_ZM_1 - [7:0] */ + +/* + * R36867 (0x9003) - DSP3 ZM 3 + */ +#define WM5100_DSP3_ZM_1_MASK 0xFFFF /* DSP3_ZM_1 - [15:0] */ +#define WM5100_DSP3_ZM_1_SHIFT 0 /* DSP3_ZM_1 - [15:0] */ +#define WM5100_DSP3_ZM_1_WIDTH 16 /* DSP3_ZM_1 - [15:0] */ + +/* + * R38908 (0x97FC) - DSP3 ZM 2044 + */ +#define WM5100_DSP3_ZM_1022_1_MASK 0x00FF /* DSP3_ZM_1022 - [7:0] */ +#define WM5100_DSP3_ZM_1022_1_SHIFT 0 /* DSP3_ZM_1022 - [7:0] */ +#define WM5100_DSP3_ZM_1022_1_WIDTH 8 /* DSP3_ZM_1022 - [7:0] */ + +/* + * R38909 (0x97FD) - DSP3 ZM 2045 + */ +#define WM5100_DSP3_ZM_1022_MASK 0xFFFF /* DSP3_ZM_1022 - [15:0] */ +#define WM5100_DSP3_ZM_1022_SHIFT 0 /* DSP3_ZM_1022 - [15:0] */ +#define WM5100_DSP3_ZM_1022_WIDTH 16 /* DSP3_ZM_1022 - [15:0] */ + +/* + * R38910 (0x97FE) - DSP3 ZM 2046 + */ +#define WM5100_DSP3_ZM_END_1_MASK 0x00FF /* DSP3_ZM_END - [7:0] */ +#define WM5100_DSP3_ZM_END_1_SHIFT 0 /* DSP3_ZM_END - [7:0] */ +#define WM5100_DSP3_ZM_END_1_WIDTH 8 /* DSP3_ZM_END - [7:0] */ + +/* + * R38911 (0x97FF) - DSP3 ZM 2047 + */ +#define WM5100_DSP3_ZM_END_MASK 0xFFFF /* DSP3_ZM_END - [15:0] */ +#define WM5100_DSP3_ZM_END_SHIFT 0 /* DSP3_ZM_END - [15:0] */ +#define WM5100_DSP3_ZM_END_WIDTH 16 /* DSP3_ZM_END - [15:0] */ + +bool wm5100_readable_register(struct device *dev, unsigned int reg); +bool wm5100_volatile_register(struct device *dev, unsigned int reg); + +extern struct reg_default wm5100_reg_defaults[WM5100_REGISTER_COUNT]; + +#endif diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c new file mode 100644 index 0000000000..4ecf07c744 --- /dev/null +++ b/sound/soc/codecs/wm5102.c @@ -0,0 +1,2185 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm5102.c -- WM5102 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "arizona.h" +#include "wm5102.h" +#include "wm_adsp.h" + +#define DRV_NAME "wm5102-codec" + +struct wm5102_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +static const struct cs_dsp_region wm5102_dsp1_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x100000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x180000 }, + { .type = WMFW_ADSP2_XM, .base = 0x190000 }, + { .type = WMFW_ADSP2_YM, .base = 0x1a8000 }, +}; + +static const struct reg_default wm5102_sysclk_reva_patch[] = { + { 0x3000, 0x2225 }, + { 0x3001, 0x3a03 }, + { 0x3002, 0x0225 }, + { 0x3003, 0x0801 }, + { 0x3004, 0x6249 }, + { 0x3005, 0x0c04 }, + { 0x3006, 0x0225 }, + { 0x3007, 0x5901 }, + { 0x3008, 0xe249 }, + { 0x3009, 0x030d }, + { 0x300a, 0x0249 }, + { 0x300b, 0x2c01 }, + { 0x300c, 0xe249 }, + { 0x300d, 0x4342 }, + { 0x300e, 0xe249 }, + { 0x300f, 0x73c0 }, + { 0x3010, 0x4249 }, + { 0x3011, 0x0c00 }, + { 0x3012, 0x0225 }, + { 0x3013, 0x1f01 }, + { 0x3014, 0x0225 }, + { 0x3015, 0x1e01 }, + { 0x3016, 0x0225 }, + { 0x3017, 0xfa00 }, + { 0x3018, 0x0000 }, + { 0x3019, 0xf000 }, + { 0x301a, 0x0000 }, + { 0x301b, 0xf000 }, + { 0x301c, 0x0000 }, + { 0x301d, 0xf000 }, + { 0x301e, 0x0000 }, + { 0x301f, 0xf000 }, + { 0x3020, 0x0000 }, + { 0x3021, 0xf000 }, + { 0x3022, 0x0000 }, + { 0x3023, 0xf000 }, + { 0x3024, 0x0000 }, + { 0x3025, 0xf000 }, + { 0x3026, 0x0000 }, + { 0x3027, 0xf000 }, + { 0x3028, 0x0000 }, + { 0x3029, 0xf000 }, + { 0x302a, 0x0000 }, + { 0x302b, 0xf000 }, + { 0x302c, 0x0000 }, + { 0x302d, 0xf000 }, + { 0x302e, 0x0000 }, + { 0x302f, 0xf000 }, + { 0x3030, 0x0225 }, + { 0x3031, 0x1a01 }, + { 0x3032, 0x0225 }, + { 0x3033, 0x1e00 }, + { 0x3034, 0x0225 }, + { 0x3035, 0x1f00 }, + { 0x3036, 0x6225 }, + { 0x3037, 0xf800 }, + { 0x3038, 0x0000 }, + { 0x3039, 0xf000 }, + { 0x303a, 0x0000 }, + { 0x303b, 0xf000 }, + { 0x303c, 0x0000 }, + { 0x303d, 0xf000 }, + { 0x303e, 0x0000 }, + { 0x303f, 0xf000 }, + { 0x3040, 0x2226 }, + { 0x3041, 0x3a03 }, + { 0x3042, 0x0226 }, + { 0x3043, 0x0801 }, + { 0x3044, 0x6249 }, + { 0x3045, 0x0c06 }, + { 0x3046, 0x0226 }, + { 0x3047, 0x5901 }, + { 0x3048, 0xe249 }, + { 0x3049, 0x030d }, + { 0x304a, 0x0249 }, + { 0x304b, 0x2c01 }, + { 0x304c, 0xe249 }, + { 0x304d, 0x4342 }, + { 0x304e, 0xe249 }, + { 0x304f, 0x73c0 }, + { 0x3050, 0x4249 }, + { 0x3051, 0x0c00 }, + { 0x3052, 0x0226 }, + { 0x3053, 0x1f01 }, + { 0x3054, 0x0226 }, + { 0x3055, 0x1e01 }, + { 0x3056, 0x0226 }, + { 0x3057, 0xfa00 }, + { 0x3058, 0x0000 }, + { 0x3059, 0xf000 }, + { 0x305a, 0x0000 }, + { 0x305b, 0xf000 }, + { 0x305c, 0x0000 }, + { 0x305d, 0xf000 }, + { 0x305e, 0x0000 }, + { 0x305f, 0xf000 }, + { 0x3060, 0x0000 }, + { 0x3061, 0xf000 }, + { 0x3062, 0x0000 }, + { 0x3063, 0xf000 }, + { 0x3064, 0x0000 }, + { 0x3065, 0xf000 }, + { 0x3066, 0x0000 }, + { 0x3067, 0xf000 }, + { 0x3068, 0x0000 }, + { 0x3069, 0xf000 }, + { 0x306a, 0x0000 }, + { 0x306b, 0xf000 }, + { 0x306c, 0x0000 }, + { 0x306d, 0xf000 }, + { 0x306e, 0x0000 }, + { 0x306f, 0xf000 }, + { 0x3070, 0x0226 }, + { 0x3071, 0x1a01 }, + { 0x3072, 0x0226 }, + { 0x3073, 0x1e00 }, + { 0x3074, 0x0226 }, + { 0x3075, 0x1f00 }, + { 0x3076, 0x6226 }, + { 0x3077, 0xf800 }, + { 0x3078, 0x0000 }, + { 0x3079, 0xf000 }, + { 0x307a, 0x0000 }, + { 0x307b, 0xf000 }, + { 0x307c, 0x0000 }, + { 0x307d, 0xf000 }, + { 0x307e, 0x0000 }, + { 0x307f, 0xf000 }, + { 0x3080, 0x2227 }, + { 0x3081, 0x3a03 }, + { 0x3082, 0x0227 }, + { 0x3083, 0x0801 }, + { 0x3084, 0x6255 }, + { 0x3085, 0x0c04 }, + { 0x3086, 0x0227 }, + { 0x3087, 0x5901 }, + { 0x3088, 0xe255 }, + { 0x3089, 0x030d }, + { 0x308a, 0x0255 }, + { 0x308b, 0x2c01 }, + { 0x308c, 0xe255 }, + { 0x308d, 0x4342 }, + { 0x308e, 0xe255 }, + { 0x308f, 0x73c0 }, + { 0x3090, 0x4255 }, + { 0x3091, 0x0c00 }, + { 0x3092, 0x0227 }, + { 0x3093, 0x1f01 }, + { 0x3094, 0x0227 }, + { 0x3095, 0x1e01 }, + { 0x3096, 0x0227 }, + { 0x3097, 0xfa00 }, + { 0x3098, 0x0000 }, + { 0x3099, 0xf000 }, + { 0x309a, 0x0000 }, + { 0x309b, 0xf000 }, + { 0x309c, 0x0000 }, + { 0x309d, 0xf000 }, + { 0x309e, 0x0000 }, + { 0x309f, 0xf000 }, + { 0x30a0, 0x0000 }, + { 0x30a1, 0xf000 }, + { 0x30a2, 0x0000 }, + { 0x30a3, 0xf000 }, + { 0x30a4, 0x0000 }, + { 0x30a5, 0xf000 }, + { 0x30a6, 0x0000 }, + { 0x30a7, 0xf000 }, + { 0x30a8, 0x0000 }, + { 0x30a9, 0xf000 }, + { 0x30aa, 0x0000 }, + { 0x30ab, 0xf000 }, + { 0x30ac, 0x0000 }, + { 0x30ad, 0xf000 }, + { 0x30ae, 0x0000 }, + { 0x30af, 0xf000 }, + { 0x30b0, 0x0227 }, + { 0x30b1, 0x1a01 }, + { 0x30b2, 0x0227 }, + { 0x30b3, 0x1e00 }, + { 0x30b4, 0x0227 }, + { 0x30b5, 0x1f00 }, + { 0x30b6, 0x6227 }, + { 0x30b7, 0xf800 }, + { 0x30b8, 0x0000 }, + { 0x30b9, 0xf000 }, + { 0x30ba, 0x0000 }, + { 0x30bb, 0xf000 }, + { 0x30bc, 0x0000 }, + { 0x30bd, 0xf000 }, + { 0x30be, 0x0000 }, + { 0x30bf, 0xf000 }, + { 0x30c0, 0x2228 }, + { 0x30c1, 0x3a03 }, + { 0x30c2, 0x0228 }, + { 0x30c3, 0x0801 }, + { 0x30c4, 0x6255 }, + { 0x30c5, 0x0c06 }, + { 0x30c6, 0x0228 }, + { 0x30c7, 0x5901 }, + { 0x30c8, 0xe255 }, + { 0x30c9, 0x030d }, + { 0x30ca, 0x0255 }, + { 0x30cb, 0x2c01 }, + { 0x30cc, 0xe255 }, + { 0x30cd, 0x4342 }, + { 0x30ce, 0xe255 }, + { 0x30cf, 0x73c0 }, + { 0x30d0, 0x4255 }, + { 0x30d1, 0x0c00 }, + { 0x30d2, 0x0228 }, + { 0x30d3, 0x1f01 }, + { 0x30d4, 0x0228 }, + { 0x30d5, 0x1e01 }, + { 0x30d6, 0x0228 }, + { 0x30d7, 0xfa00 }, + { 0x30d8, 0x0000 }, + { 0x30d9, 0xf000 }, + { 0x30da, 0x0000 }, + { 0x30db, 0xf000 }, + { 0x30dc, 0x0000 }, + { 0x30dd, 0xf000 }, + { 0x30de, 0x0000 }, + { 0x30df, 0xf000 }, + { 0x30e0, 0x0000 }, + { 0x30e1, 0xf000 }, + { 0x30e2, 0x0000 }, + { 0x30e3, 0xf000 }, + { 0x30e4, 0x0000 }, + { 0x30e5, 0xf000 }, + { 0x30e6, 0x0000 }, + { 0x30e7, 0xf000 }, + { 0x30e8, 0x0000 }, + { 0x30e9, 0xf000 }, + { 0x30ea, 0x0000 }, + { 0x30eb, 0xf000 }, + { 0x30ec, 0x0000 }, + { 0x30ed, 0xf000 }, + { 0x30ee, 0x0000 }, + { 0x30ef, 0xf000 }, + { 0x30f0, 0x0228 }, + { 0x30f1, 0x1a01 }, + { 0x30f2, 0x0228 }, + { 0x30f3, 0x1e00 }, + { 0x30f4, 0x0228 }, + { 0x30f5, 0x1f00 }, + { 0x30f6, 0x6228 }, + { 0x30f7, 0xf800 }, + { 0x30f8, 0x0000 }, + { 0x30f9, 0xf000 }, + { 0x30fa, 0x0000 }, + { 0x30fb, 0xf000 }, + { 0x30fc, 0x0000 }, + { 0x30fd, 0xf000 }, + { 0x30fe, 0x0000 }, + { 0x30ff, 0xf000 }, + { 0x3100, 0x222b }, + { 0x3101, 0x3a03 }, + { 0x3102, 0x222b }, + { 0x3103, 0x5803 }, + { 0x3104, 0xe26f }, + { 0x3105, 0x030d }, + { 0x3106, 0x626f }, + { 0x3107, 0x2c01 }, + { 0x3108, 0xe26f }, + { 0x3109, 0x4342 }, + { 0x310a, 0xe26f }, + { 0x310b, 0x73c0 }, + { 0x310c, 0x026f }, + { 0x310d, 0x0c00 }, + { 0x310e, 0x022b }, + { 0x310f, 0x1f01 }, + { 0x3110, 0x022b }, + { 0x3111, 0x1e01 }, + { 0x3112, 0x022b }, + { 0x3113, 0xfa00 }, + { 0x3114, 0x0000 }, + { 0x3115, 0xf000 }, + { 0x3116, 0x0000 }, + { 0x3117, 0xf000 }, + { 0x3118, 0x0000 }, + { 0x3119, 0xf000 }, + { 0x311a, 0x0000 }, + { 0x311b, 0xf000 }, + { 0x311c, 0x0000 }, + { 0x311d, 0xf000 }, + { 0x311e, 0x0000 }, + { 0x311f, 0xf000 }, + { 0x3120, 0x022b }, + { 0x3121, 0x0a01 }, + { 0x3122, 0x022b }, + { 0x3123, 0x1e00 }, + { 0x3124, 0x022b }, + { 0x3125, 0x1f00 }, + { 0x3126, 0x622b }, + { 0x3127, 0xf800 }, + { 0x3128, 0x0000 }, + { 0x3129, 0xf000 }, + { 0x312a, 0x0000 }, + { 0x312b, 0xf000 }, + { 0x312c, 0x0000 }, + { 0x312d, 0xf000 }, + { 0x312e, 0x0000 }, + { 0x312f, 0xf000 }, + { 0x3130, 0x0000 }, + { 0x3131, 0xf000 }, + { 0x3132, 0x0000 }, + { 0x3133, 0xf000 }, + { 0x3134, 0x0000 }, + { 0x3135, 0xf000 }, + { 0x3136, 0x0000 }, + { 0x3137, 0xf000 }, + { 0x3138, 0x0000 }, + { 0x3139, 0xf000 }, + { 0x313a, 0x0000 }, + { 0x313b, 0xf000 }, + { 0x313c, 0x0000 }, + { 0x313d, 0xf000 }, + { 0x313e, 0x0000 }, + { 0x313f, 0xf000 }, + { 0x3140, 0x0000 }, + { 0x3141, 0xf000 }, + { 0x3142, 0x0000 }, + { 0x3143, 0xf000 }, + { 0x3144, 0x0000 }, + { 0x3145, 0xf000 }, + { 0x3146, 0x0000 }, + { 0x3147, 0xf000 }, + { 0x3148, 0x0000 }, + { 0x3149, 0xf000 }, + { 0x314a, 0x0000 }, + { 0x314b, 0xf000 }, + { 0x314c, 0x0000 }, + { 0x314d, 0xf000 }, + { 0x314e, 0x0000 }, + { 0x314f, 0xf000 }, + { 0x3150, 0x0000 }, + { 0x3151, 0xf000 }, + { 0x3152, 0x0000 }, + { 0x3153, 0xf000 }, + { 0x3154, 0x0000 }, + { 0x3155, 0xf000 }, + { 0x3156, 0x0000 }, + { 0x3157, 0xf000 }, + { 0x3158, 0x0000 }, + { 0x3159, 0xf000 }, + { 0x315a, 0x0000 }, + { 0x315b, 0xf000 }, + { 0x315c, 0x0000 }, + { 0x315d, 0xf000 }, + { 0x315e, 0x0000 }, + { 0x315f, 0xf000 }, + { 0x3160, 0x0000 }, + { 0x3161, 0xf000 }, + { 0x3162, 0x0000 }, + { 0x3163, 0xf000 }, + { 0x3164, 0x0000 }, + { 0x3165, 0xf000 }, + { 0x3166, 0x0000 }, + { 0x3167, 0xf000 }, + { 0x3168, 0x0000 }, + { 0x3169, 0xf000 }, + { 0x316a, 0x0000 }, + { 0x316b, 0xf000 }, + { 0x316c, 0x0000 }, + { 0x316d, 0xf000 }, + { 0x316e, 0x0000 }, + { 0x316f, 0xf000 }, + { 0x3170, 0x0000 }, + { 0x3171, 0xf000 }, + { 0x3172, 0x0000 }, + { 0x3173, 0xf000 }, + { 0x3174, 0x0000 }, + { 0x3175, 0xf000 }, + { 0x3176, 0x0000 }, + { 0x3177, 0xf000 }, + { 0x3178, 0x0000 }, + { 0x3179, 0xf000 }, + { 0x317a, 0x0000 }, + { 0x317b, 0xf000 }, + { 0x317c, 0x0000 }, + { 0x317d, 0xf000 }, + { 0x317e, 0x0000 }, + { 0x317f, 0xf000 }, + { 0x3180, 0x2001 }, + { 0x3181, 0xf101 }, + { 0x3182, 0x0000 }, + { 0x3183, 0xf000 }, + { 0x3184, 0x0000 }, + { 0x3185, 0xf000 }, + { 0x3186, 0x0000 }, + { 0x3187, 0xf000 }, + { 0x3188, 0x0000 }, + { 0x3189, 0xf000 }, + { 0x318a, 0x0000 }, + { 0x318b, 0xf000 }, + { 0x318c, 0x0000 }, + { 0x318d, 0xf000 }, + { 0x318e, 0x0000 }, + { 0x318f, 0xf000 }, + { 0x3190, 0x0000 }, + { 0x3191, 0xf000 }, + { 0x3192, 0x0000 }, + { 0x3193, 0xf000 }, + { 0x3194, 0x0000 }, + { 0x3195, 0xf000 }, + { 0x3196, 0x0000 }, + { 0x3197, 0xf000 }, + { 0x3198, 0x0000 }, + { 0x3199, 0xf000 }, + { 0x319a, 0x0000 }, + { 0x319b, 0xf000 }, + { 0x319c, 0x0000 }, + { 0x319d, 0xf000 }, + { 0x319e, 0x0000 }, + { 0x319f, 0xf000 }, + { 0x31a0, 0x0000 }, + { 0x31a1, 0xf000 }, + { 0x31a2, 0x0000 }, + { 0x31a3, 0xf000 }, + { 0x31a4, 0x0000 }, + { 0x31a5, 0xf000 }, + { 0x31a6, 0x0000 }, + { 0x31a7, 0xf000 }, + { 0x31a8, 0x0000 }, + { 0x31a9, 0xf000 }, + { 0x31aa, 0x0000 }, + { 0x31ab, 0xf000 }, + { 0x31ac, 0x0000 }, + { 0x31ad, 0xf000 }, + { 0x31ae, 0x0000 }, + { 0x31af, 0xf000 }, + { 0x31b0, 0x0000 }, + { 0x31b1, 0xf000 }, + { 0x31b2, 0x0000 }, + { 0x31b3, 0xf000 }, + { 0x31b4, 0x0000 }, + { 0x31b5, 0xf000 }, + { 0x31b6, 0x0000 }, + { 0x31b7, 0xf000 }, + { 0x31b8, 0x0000 }, + { 0x31b9, 0xf000 }, + { 0x31ba, 0x0000 }, + { 0x31bb, 0xf000 }, + { 0x31bc, 0x0000 }, + { 0x31bd, 0xf000 }, + { 0x31be, 0x0000 }, + { 0x31bf, 0xf000 }, + { 0x31c0, 0x0000 }, + { 0x31c1, 0xf000 }, + { 0x31c2, 0x0000 }, + { 0x31c3, 0xf000 }, + { 0x31c4, 0x0000 }, + { 0x31c5, 0xf000 }, + { 0x31c6, 0x0000 }, + { 0x31c7, 0xf000 }, + { 0x31c8, 0x0000 }, + { 0x31c9, 0xf000 }, + { 0x31ca, 0x0000 }, + { 0x31cb, 0xf000 }, + { 0x31cc, 0x0000 }, + { 0x31cd, 0xf000 }, + { 0x31ce, 0x0000 }, + { 0x31cf, 0xf000 }, + { 0x31d0, 0x0000 }, + { 0x31d1, 0xf000 }, + { 0x31d2, 0x0000 }, + { 0x31d3, 0xf000 }, + { 0x31d4, 0x0000 }, + { 0x31d5, 0xf000 }, + { 0x31d6, 0x0000 }, + { 0x31d7, 0xf000 }, + { 0x31d8, 0x0000 }, + { 0x31d9, 0xf000 }, + { 0x31da, 0x0000 }, + { 0x31db, 0xf000 }, + { 0x31dc, 0x0000 }, + { 0x31dd, 0xf000 }, + { 0x31de, 0x0000 }, + { 0x31df, 0xf000 }, + { 0x31e0, 0x0000 }, + { 0x31e1, 0xf000 }, + { 0x31e2, 0x0000 }, + { 0x31e3, 0xf000 }, + { 0x31e4, 0x0000 }, + { 0x31e5, 0xf000 }, + { 0x31e6, 0x0000 }, + { 0x31e7, 0xf000 }, + { 0x31e8, 0x0000 }, + { 0x31e9, 0xf000 }, + { 0x31ea, 0x0000 }, + { 0x31eb, 0xf000 }, + { 0x31ec, 0x0000 }, + { 0x31ed, 0xf000 }, + { 0x31ee, 0x0000 }, + { 0x31ef, 0xf000 }, + { 0x31f0, 0x0000 }, + { 0x31f1, 0xf000 }, + { 0x31f2, 0x0000 }, + { 0x31f3, 0xf000 }, + { 0x31f4, 0x0000 }, + { 0x31f5, 0xf000 }, + { 0x31f6, 0x0000 }, + { 0x31f7, 0xf000 }, + { 0x31f8, 0x0000 }, + { 0x31f9, 0xf000 }, + { 0x31fa, 0x0000 }, + { 0x31fb, 0xf000 }, + { 0x31fc, 0x0000 }, + { 0x31fd, 0xf000 }, + { 0x31fe, 0x0000 }, + { 0x31ff, 0xf000 }, + { 0x024d, 0xff50 }, + { 0x0252, 0xff50 }, + { 0x0259, 0x0112 }, + { 0x025e, 0x0112 }, +}; + +static const struct reg_default wm5102_sysclk_revb_patch[] = { + { 0x3081, 0x08FE }, + { 0x3083, 0x00ED }, + { 0x30C1, 0x08FE }, + { 0x30C3, 0x00ED }, +}; + +static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + struct regmap *regmap = arizona->regmap; + const struct reg_default *patch = NULL; + int i, patch_size; + + switch (arizona->rev) { + case 0: + patch = wm5102_sysclk_reva_patch; + patch_size = ARRAY_SIZE(wm5102_sysclk_reva_patch); + break; + default: + patch = wm5102_sysclk_revb_patch; + patch_size = ARRAY_SIZE(wm5102_sysclk_revb_patch); + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (patch) + for (i = 0; i < patch_size; i++) + regmap_write_async(regmap, patch[i].reg, + patch[i].def); + break; + case SND_SOC_DAPM_PRE_PMD: + break; + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + return arizona_clk_ev(w, kcontrol, event); + default: + return 0; + } + + return arizona_dvfs_sysclk_ev(w, kcontrol, event); +} + +static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + unsigned int v = 0; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); + if (ret != 0) { + dev_err(component->dev, + "Failed to read SYSCLK state: %d\n", ret); + return -EIO; + } + + v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; + + if (v >= 3) { + ret = arizona_dvfs_up(component, ARIZONA_DVFS_ADSP1_RQ); + if (ret) { + dev_err(component->dev, + "Failed to raise DVFS: %d\n", ret); + return ret; + } + } + + wm_adsp2_set_dspclk(w, v); + break; + + case SND_SOC_DAPM_POST_PMD: + ret = arizona_dvfs_down(component, ARIZONA_DVFS_ADSP1_RQ); + if (ret) + dev_warn(component->dev, + "Failed to lower DVFS: %d\n", ret); + break; + + default: + break; + } + + return wm_adsp_early_event(w, kcontrol, event); +} + +static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + + mutex_lock(&arizona->dac_comp_lock); + put_unaligned_be16(arizona->dac_comp_coeff, + ucontrol->value.bytes.data); + mutex_unlock(&arizona->dac_comp_lock); + + return 0; +} + +static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + uint16_t dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data); + int ret = 0; + + mutex_lock(&arizona->dac_comp_lock); + if (arizona->dac_comp_coeff != dac_comp_coeff) { + arizona->dac_comp_coeff = dac_comp_coeff; + ret = 1; + } + mutex_unlock(&arizona->dac_comp_lock); + + return ret; +} + +static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + + mutex_lock(&arizona->dac_comp_lock); + ucontrol->value.integer.value[0] = arizona->dac_comp_enabled; + mutex_unlock(&arizona->dac_comp_lock); + + return 0; +} + +static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + int ret = 0; + + if (ucontrol->value.integer.value[0] > mc->max) + return -EINVAL; + + mutex_lock(&arizona->dac_comp_lock); + if (arizona->dac_comp_enabled != ucontrol->value.integer.value[0]) { + arizona->dac_comp_enabled = ucontrol->value.integer.value[0]; + ret = 1; + } + mutex_unlock(&arizona->dac_comp_lock); + + return ret; +} + +static const char * const wm5102_osr_text[] = { + "Low power", "Normal", "High performance", +}; + +static const unsigned int wm5102_osr_val[] = { + 0x0, 0x3, 0x5, +}; + +static const struct soc_enum wm5102_hpout_osr[] = { + SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L, + ARIZONA_OUT1_OSR_SHIFT, 0x7, + ARRAY_SIZE(wm5102_osr_text), + wm5102_osr_text, wm5102_osr_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_2L, + ARIZONA_OUT2_OSR_SHIFT, 0x7, + ARRAY_SIZE(wm5102_osr_text), + wm5102_osr_text, wm5102_osr_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L, + ARIZONA_OUT3_OSR_SHIFT, 0x7, + ARRAY_SIZE(wm5102_osr_text), + wm5102_osr_text, wm5102_osr_val), +}; + +#define WM5102_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + +static const struct snd_kcontrol_new wm5102_snd_controls[] = { +SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3_OSR_SHIFT, 1, 0), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ3 Coefficients", ARIZONA_EQ3_2), +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ4 Coefficients", ARIZONA_EQ4_2), +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2), +ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2), +ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2), +ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2), + +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), + +ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), + +ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv), + +ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT2L", ARIZONA_OUT2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT2R", ARIZONA_OUT2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L, + ARIZONA_OUT4_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L, + ARIZONA_OUT5_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_ENUM("HPOUT1 OSR", wm5102_hpout_osr[0]), +SOC_ENUM("HPOUT2 OSR", wm5102_hpout_osr[1]), +SOC_ENUM("EPOUT OSR", wm5102_hpout_osr[2]), + +SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0), +SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0), +SOC_SINGLE("EPOUT DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE3L_ENA_SHIFT, 1, 0), + +SOC_SINGLE("DRE Threshold", ARIZONA_DRE_CONTROL_2, + ARIZONA_DRE_T_LOW_SHIFT, 63, 0), + +SOC_SINGLE("DRE Low Level ABS", ARIZONA_DRE_CONTROL_3, + ARIZONA_DRE_LOW_LEVEL_ABS_SHIFT, 15, 0), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +SND_SOC_BYTES_EXT("Output Compensation Coefficient", 2, + wm5102_out_comp_coeff_get, wm5102_out_comp_coeff_put), + +SOC_SINGLE_EXT("Output Compensation Switch", 0, 0, 1, 0, + wm5102_out_comp_switch_get, wm5102_out_comp_switch_put), + +WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), +WM5102_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R), +WM5102_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM5102_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM5102_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM5102_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM5102_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), +}; + +ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE); + +ARIZONA_DSP_AUX_ENUMS(DSP1, ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE); + +static const char * const wm5102_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "EPOUT", + "SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R", +}; + +static const unsigned int wm5102_aec_loopback_values[] = { + 0, 1, 2, 3, 4, 6, 7, 8, 9, +}; + +static const struct soc_enum wm5102_aec_loopback = + SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(wm5102_aec_loopback_texts), + wm5102_aec_loopback_texts, + wm5102_aec_loopback_values); + +static const struct snd_kcontrol_new wm5102_aec_loopback_mux = + SOC_DAPM_ENUM("AEC Loopback", wm5102_aec_loopback); + +static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, + 0, wm5102_sysclk_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), +SND_SOC_DAPM_INPUT("IN3L"), +SND_SOC_DAPM_INPUT("IN3R"), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1, + ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX8_ENA_SHIFT, 0), + +ARIZONA_DSP_WIDGETS(DSP1, "DSP1"), + +SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, &wm5102_aec_loopback_mux), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"), +ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"), +ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"), +ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(Mic, "Mic"), +ARIZONA_MIXER_WIDGETS(Noise, "Noise"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +ARIZONA_MIXER_WIDGETS(OUT2L, "HPOUT2L"), +ARIZONA_MIXER_WIDGETS(OUT2R, "HPOUT2R"), +ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"), +ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), +ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"), +ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"), + +ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +WM_ADSP2("DSP1", 0, wm5102_adsp_power_ev), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2L"), +SND_SOC_DAPM_OUTPUT("HPOUT2R"), +SND_SOC_DAPM_OUTPUT("EPOUTN"), +SND_SOC_DAPM_OUTPUT("EPOUTP"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "IN3L", "IN3L PGA" }, \ + { name, "IN3R", "IN3R PGA" }, \ + { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "SLIMRX7", "SLIMRX7" }, \ + { name, "SLIMRX8", "SLIMRX8" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" } + +static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD3" }, + { "AIF3 Playback", NULL, "DBVDD3" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT2L", NULL, "CPVDD" }, + { "OUT2R", NULL, "CPVDD" }, + { "OUT3L", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDDL" }, + { "OUT4R", NULL, "SPKVDDR" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT4R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + { "IN3L", NULL, "SYSCLK" }, + { "IN3R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "SYSCLK" }, + { "ASRC1R", NULL, "SYSCLK" }, + { "ASRC2L", NULL, "SYSCLK" }, + { "ASRC2R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "ASYNCCLK" }, + { "ASRC1R", NULL, "ASYNCCLK" }, + { "ASRC2L", NULL, "ASYNCCLK" }, + { "ASRC2R", NULL, "ASYNCCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "Slim3 Capture", NULL, "SLIMTX7" }, + { "Slim3 Capture", NULL, "SLIMTX8" }, + + { "SLIMRX7", NULL, "Slim3 Playback" }, + { "SLIMRX8", NULL, "Slim3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + { "Slim3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + { "Slim3 Capture", NULL, "SYSCLK" }, + + { "Audio Trace DSP", NULL, "DSP1" }, + + { "IN1L PGA", NULL, "IN1L" }, + { "IN1R PGA", NULL, "IN1R" }, + + { "IN2L PGA", NULL, "IN2L" }, + { "IN2R PGA", NULL, "IN2R" }, + + { "IN3L PGA", NULL, "IN3L" }, + { "IN3R PGA", NULL, "IN3R" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"), + ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"), + ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"), + ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"), + ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"), + + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), + ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), + ARIZONA_MIXER_ROUTES("EQ3", "EQ3"), + ARIZONA_MIXER_ROUTES("EQ4", "EQ4"), + + ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"), + + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + ARIZONA_DSP_ROUTES("DSP1"), + + { "AEC Loopback", "HPOUT1L", "OUT1L" }, + { "AEC Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC Loopback", "HPOUT2L", "OUT2L" }, + { "AEC Loopback", "HPOUT2R", "OUT2R" }, + { "HPOUT2L", NULL, "OUT2L" }, + { "HPOUT2R", NULL, "OUT2R" }, + + { "AEC Loopback", "EPOUT", "OUT3L" }, + { "EPOUTN", NULL, "OUT3L" }, + { "EPOUTP", NULL, "OUT3L" }, + + { "AEC Loopback", "SPKOUTL", "OUT4L" }, + { "SPKOUTLN", NULL, "OUT4L" }, + { "SPKOUTLP", NULL, "OUT4L" }, + + { "AEC Loopback", "SPKOUTR", "OUT4R" }, + { "SPKOUTRN", NULL, "OUT4R" }, + { "SPKOUTRP", NULL, "OUT4R" }, + + { "AEC Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC Loopback", "SPKDAT1R", "OUT5R" }, + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "SYSCLK" }, + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, +}; + +static int wm5102_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int Fref, unsigned int Fout) +{ + struct wm5102_priv *wm5102 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case WM5102_FLL1: + return arizona_set_fll(&wm5102->fll[0], source, Fref, Fout); + case WM5102_FLL2: + return arizona_set_fll(&wm5102->fll[1], source, Fref, Fout); + case WM5102_FLL1_REFCLK: + return arizona_set_fll_refclk(&wm5102->fll[0], source, Fref, + Fout); + case WM5102_FLL2_REFCLK: + return arizona_set_fll_refclk(&wm5102->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +#define WM5102_RATES SNDRV_PCM_RATE_KNOT + +#define WM5102_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm5102_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + +static struct snd_soc_dai_driver wm5102_dai[] = { + { + .name = "wm5102-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm5102-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm5102-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm5102-slim1", + .id = 4, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm5102-slim2", + .id = 5, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm5102-slim3", + .id = 6, + .playback = { + .stream_name = "Slim3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "Slim3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm5102-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 4, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &wm5102_dai_ops, + }, + { + .name = "wm5102-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 4, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + }, +}; + +static int wm5102_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct wm5102_priv *priv = snd_soc_component_get_drvdata(component); + + return wm_adsp_compr_open(&priv->core.adsp[0], stream); +} + +static irqreturn_t wm5102_adsp2_irq(int irq, void *data) +{ + struct wm5102_priv *priv = data; + struct arizona *arizona = priv->core.arizona; + int ret; + + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[0]); + if (ret == -ENODEV) { + dev_err(arizona->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int wm5102_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm5102_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->core.arizona; + int ret; + + snd_soc_component_init_regmap(component, arizona->regmap); + + ret = wm_adsp2_component_probe(&priv->core.adsp[0], component); + if (ret) + return ret; + + ret = snd_soc_add_component_controls(component, + arizona_adsp2_rate_controls, 1); + if (ret) + goto err_adsp2_codec_probe; + + ret = arizona_init_spk(component); + if (ret < 0) + return ret; + + arizona_init_gpio(component); + + snd_soc_component_disable_pin(component, "HAPTICS"); + + priv->core.arizona->dapm = dapm; + + return 0; + +err_adsp2_codec_probe: + wm_adsp2_component_remove(&priv->core.adsp[0], component); + + return ret; +} + +static void wm5102_component_remove(struct snd_soc_component *component) +{ + struct wm5102_priv *priv = snd_soc_component_get_drvdata(component); + + wm_adsp2_component_remove(&priv->core.adsp[0], component); + + priv->core.arizona->dapm = NULL; +} + +#define WM5102_DIG_VU 0x0200 + +static unsigned int wm5102_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, +}; + +static const struct snd_compress_ops wm5102_compress_ops = { + .open = wm5102_open, + .free = wm_adsp_compr_free, + .set_params = wm_adsp_compr_set_params, + .get_caps = wm_adsp_compr_get_caps, + .trigger = wm_adsp_compr_trigger, + .pointer = wm_adsp_compr_pointer, + .copy = wm_adsp_compr_copy, +}; + +static const struct snd_soc_component_driver soc_component_dev_wm5102 = { + .probe = wm5102_component_probe, + .remove = wm5102_component_remove, + .set_sysclk = arizona_set_sysclk, + .set_pll = wm5102_set_fll, + .set_jack = arizona_jack_set_jack, + .name = DRV_NAME, + .compress_ops = &wm5102_compress_ops, + .controls = wm5102_snd_controls, + .num_controls = ARRAY_SIZE(wm5102_snd_controls), + .dapm_widgets = wm5102_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm5102_dapm_widgets), + .dapm_routes = wm5102_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm5102_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm5102_priv *wm5102; + int i, ret; + + wm5102 = devm_kzalloc(&pdev->dev, sizeof(struct wm5102_priv), + GFP_KERNEL); + if (wm5102 == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, wm5102); + + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + + mutex_init(&arizona->dac_comp_lock); + + wm5102->core.arizona = arizona; + wm5102->core.num_inputs = 6; + + arizona_init_dvfs(&wm5102->core); + + wm5102->core.adsp[0].part = "wm5102"; + wm5102->core.adsp[0].cs_dsp.num = 1; + wm5102->core.adsp[0].cs_dsp.type = WMFW_ADSP2; + wm5102->core.adsp[0].cs_dsp.base = ARIZONA_DSP1_CONTROL_1; + wm5102->core.adsp[0].cs_dsp.dev = arizona->dev; + wm5102->core.adsp[0].cs_dsp.regmap = arizona->regmap; + wm5102->core.adsp[0].cs_dsp.mem = wm5102_dsp1_regions; + wm5102->core.adsp[0].cs_dsp.num_mems = ARRAY_SIZE(wm5102_dsp1_regions); + + ret = wm_adsp2_init(&wm5102->core.adsp[0]); + if (ret != 0) + return ret; + + /* This may return -EPROBE_DEFER, so do this early on */ + ret = arizona_jack_codec_dev_probe(&wm5102->core, &pdev->dev); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(wm5102->fll); i++) + wm5102->fll[i].vco_mult = 1; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm5102->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm5102->fll[1]); + + /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */ + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2, + ARIZONA_SAMPLE_RATE_2_MASK, 0x11); + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3, + ARIZONA_SAMPLE_RATE_3_MASK, 0x12); + + for (i = 0; i < ARRAY_SIZE(wm5102_dai); i++) + arizona_init_dai(&wm5102->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm5102_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm5102_digital_vu[i], + WM5102_DIG_VU, WM5102_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", wm5102_adsp2_irq, + wm5102); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + goto err_jack_codec_dev; + } + + ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1); + if (ret != 0) + dev_warn(&pdev->dev, + "Failed to set compressed IRQ as a wake source: %d\n", + ret); + + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; + ret = arizona_init_spk_irqs(arizona); + if (ret < 0) + goto err_dsp_irq; + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm5102, + wm5102_dai, + ARRAY_SIZE(wm5102_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto err_spk_irqs; + } + + return ret; + +err_spk_irqs: + arizona_free_spk_irqs(arizona); +err_dsp_irq: + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102); +err_jack_codec_dev: + pm_runtime_disable(&pdev->dev); + arizona_jack_codec_dev_remove(&wm5102->core); + + return ret; +} + +static void wm5102_remove(struct platform_device *pdev) +{ + struct wm5102_priv *wm5102 = platform_get_drvdata(pdev); + struct arizona *arizona = wm5102->core.arizona; + + pm_runtime_disable(&pdev->dev); + + wm_adsp2_remove(&wm5102->core.adsp[0]); + + arizona_free_spk_irqs(arizona); + + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102); + + arizona_jack_codec_dev_remove(&wm5102->core); +} + +static struct platform_driver wm5102_codec_driver = { + .driver = { + .name = "wm5102-codec", + }, + .probe = wm5102_probe, + .remove_new = wm5102_remove, +}; + +module_platform_driver(wm5102_codec_driver); + +MODULE_DESCRIPTION("ASoC WM5102 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm5102-codec"); diff --git a/sound/soc/codecs/wm5102.h b/sound/soc/codecs/wm5102.h new file mode 100644 index 0000000000..34156cef64 --- /dev/null +++ b/sound/soc/codecs/wm5102.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm5102.h -- WM5102 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#ifndef _WM5102_H +#define _WM5102_H + +#include "arizona.h" + +#define WM5102_FLL1 1 +#define WM5102_FLL2 2 +#define WM5102_FLL1_REFCLK 3 +#define WM5102_FLL2_REFCLK 4 + +#endif diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c new file mode 100644 index 0000000000..ac1f2c8503 --- /dev/null +++ b/sound/soc/codecs/wm5110.c @@ -0,0 +1,2545 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm5110.c -- WM5110 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "arizona.h" +#include "wm_adsp.h" +#include "wm5110.h" + +#define WM5110_NUM_ADSP 4 + +#define DRV_NAME "wm5110-codec" + +struct wm5110_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; + + unsigned int in_value; + int in_pre_pending; + int in_post_pending; + + unsigned int in_pga_cache[6]; +}; + +static const struct cs_dsp_region wm5110_dsp1_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x100000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x180000 }, + { .type = WMFW_ADSP2_XM, .base = 0x190000 }, + { .type = WMFW_ADSP2_YM, .base = 0x1a8000 }, +}; + +static const struct cs_dsp_region wm5110_dsp2_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x200000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x280000 }, + { .type = WMFW_ADSP2_XM, .base = 0x290000 }, + { .type = WMFW_ADSP2_YM, .base = 0x2a8000 }, +}; + +static const struct cs_dsp_region wm5110_dsp3_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x300000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x380000 }, + { .type = WMFW_ADSP2_XM, .base = 0x390000 }, + { .type = WMFW_ADSP2_YM, .base = 0x3a8000 }, +}; + +static const struct cs_dsp_region wm5110_dsp4_regions[] = { + { .type = WMFW_ADSP2_PM, .base = 0x400000 }, + { .type = WMFW_ADSP2_ZM, .base = 0x480000 }, + { .type = WMFW_ADSP2_XM, .base = 0x490000 }, + { .type = WMFW_ADSP2_YM, .base = 0x4a8000 }, +}; + +static const struct cs_dsp_region *wm5110_dsp_regions[] = { + wm5110_dsp1_regions, + wm5110_dsp2_regions, + wm5110_dsp3_regions, + wm5110_dsp4_regions, +}; + +static const struct reg_default wm5110_sysclk_revd_patch[] = { + { 0x3093, 0x1001 }, + { 0x30E3, 0x1301 }, + { 0x3133, 0x1201 }, + { 0x3183, 0x1501 }, + { 0x31D3, 0x1401 }, + { 0x0049, 0x01ea }, + { 0x004a, 0x01f2 }, + { 0x0057, 0x01e7 }, + { 0x0058, 0x01fb }, + { 0x33ce, 0xc4f5 }, + { 0x33cf, 0x1361 }, + { 0x33d0, 0x0402 }, + { 0x33d1, 0x4700 }, + { 0x33d2, 0x026d }, + { 0x33d3, 0xff00 }, + { 0x33d4, 0x026d }, + { 0x33d5, 0x0101 }, + { 0x33d6, 0xc4f5 }, + { 0x33d7, 0x0361 }, + { 0x33d8, 0x0402 }, + { 0x33d9, 0x6701 }, + { 0x33da, 0xc4f5 }, + { 0x33db, 0x136f }, + { 0x33dc, 0xc4f5 }, + { 0x33dd, 0x134f }, + { 0x33de, 0xc4f5 }, + { 0x33df, 0x131f }, + { 0x33e0, 0x026d }, + { 0x33e1, 0x4f01 }, + { 0x33e2, 0x026d }, + { 0x33e3, 0xf100 }, + { 0x33e4, 0x026d }, + { 0x33e5, 0x0001 }, + { 0x33e6, 0xc4f5 }, + { 0x33e7, 0x0361 }, + { 0x33e8, 0x0402 }, + { 0x33e9, 0x6601 }, + { 0x33ea, 0xc4f5 }, + { 0x33eb, 0x136f }, + { 0x33ec, 0xc4f5 }, + { 0x33ed, 0x134f }, + { 0x33ee, 0xc4f5 }, + { 0x33ef, 0x131f }, + { 0x33f0, 0x026d }, + { 0x33f1, 0x4e01 }, + { 0x33f2, 0x026d }, + { 0x33f3, 0xf000 }, + { 0x33f6, 0xc4f5 }, + { 0x33f7, 0x1361 }, + { 0x33f8, 0x0402 }, + { 0x33f9, 0x4600 }, + { 0x33fa, 0x026d }, + { 0x33fb, 0xfe00 }, +}; + +static const struct reg_default wm5110_sysclk_reve_patch[] = { + { 0x3270, 0xE410 }, + { 0x3271, 0x3078 }, + { 0x3272, 0xE410 }, + { 0x3273, 0x3070 }, + { 0x3274, 0xE410 }, + { 0x3275, 0x3066 }, + { 0x3276, 0xE410 }, + { 0x3277, 0x3056 }, + { 0x327A, 0xE414 }, + { 0x327B, 0x3078 }, + { 0x327C, 0xE414 }, + { 0x327D, 0x3070 }, + { 0x327E, 0xE414 }, + { 0x327F, 0x3066 }, + { 0x3280, 0xE414 }, + { 0x3281, 0x3056 }, +}; + +static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + struct regmap *regmap = arizona->regmap; + const struct reg_default *patch = NULL; + int i, patch_size; + + switch (arizona->rev) { + case 3: + patch = wm5110_sysclk_revd_patch; + patch_size = ARRAY_SIZE(wm5110_sysclk_revd_patch); + break; + default: + patch = wm5110_sysclk_reve_patch; + patch_size = ARRAY_SIZE(wm5110_sysclk_reve_patch); + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (patch) + for (i = 0; i < patch_size; i++) + regmap_write_async(regmap, patch[i].reg, + patch[i].def); + break; + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + return arizona_clk_ev(w, kcontrol, event); + default: + break; + } + + return 0; +} + +static int wm5110_adsp_power_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + unsigned int v; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v); + if (ret != 0) { + dev_err(component->dev, "Failed to read SYSCLK state: %d\n", ret); + return ret; + } + + v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT; + + wm_adsp2_set_dspclk(w, v); + + return wm_adsp_early_event(w, kcontrol, event); +} + +static const struct reg_sequence wm5110_no_dre_left_enable[] = { + { 0x3024, 0xE410 }, + { 0x3025, 0x0056 }, + { 0x301B, 0x0224 }, + { 0x301F, 0x4263 }, + { 0x3021, 0x5291 }, + { 0x3030, 0xE410 }, + { 0x3031, 0x3066 }, + { 0x3032, 0xE410 }, + { 0x3033, 0x3070 }, + { 0x3034, 0xE410 }, + { 0x3035, 0x3078 }, + { 0x3036, 0xE410 }, + { 0x3037, 0x3080 }, + { 0x3038, 0xE410 }, + { 0x3039, 0x3080 }, +}; + +static const struct reg_sequence wm5110_dre_left_enable[] = { + { 0x3024, 0x0231 }, + { 0x3025, 0x0B00 }, + { 0x301B, 0x0227 }, + { 0x301F, 0x4266 }, + { 0x3021, 0x5294 }, + { 0x3030, 0xE231 }, + { 0x3031, 0x0266 }, + { 0x3032, 0x8231 }, + { 0x3033, 0x4B15 }, + { 0x3034, 0x8231 }, + { 0x3035, 0x0B15 }, + { 0x3036, 0xE231 }, + { 0x3037, 0x5294 }, + { 0x3038, 0x0231 }, + { 0x3039, 0x0B00 }, +}; + +static const struct reg_sequence wm5110_no_dre_right_enable[] = { + { 0x3074, 0xE414 }, + { 0x3075, 0x0056 }, + { 0x306B, 0x0224 }, + { 0x306F, 0x4263 }, + { 0x3071, 0x5291 }, + { 0x3080, 0xE414 }, + { 0x3081, 0x3066 }, + { 0x3082, 0xE414 }, + { 0x3083, 0x3070 }, + { 0x3084, 0xE414 }, + { 0x3085, 0x3078 }, + { 0x3086, 0xE414 }, + { 0x3087, 0x3080 }, + { 0x3088, 0xE414 }, + { 0x3089, 0x3080 }, +}; + +static const struct reg_sequence wm5110_dre_right_enable[] = { + { 0x3074, 0x0231 }, + { 0x3075, 0x0B00 }, + { 0x306B, 0x0227 }, + { 0x306F, 0x4266 }, + { 0x3071, 0x5294 }, + { 0x3080, 0xE231 }, + { 0x3081, 0x0266 }, + { 0x3082, 0x8231 }, + { 0x3083, 0x4B17 }, + { 0x3084, 0x8231 }, + { 0x3085, 0x0B17 }, + { 0x3086, 0xE231 }, + { 0x3087, 0x5294 }, + { 0x3088, 0x0231 }, + { 0x3089, 0x0B00 }, +}; + +static int wm5110_hp_pre_enable(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + unsigned int val = snd_soc_component_read(component, ARIZONA_DRE_ENABLE); + const struct reg_sequence *wseq; + int nregs; + + switch (w->shift) { + case ARIZONA_OUT1L_ENA_SHIFT: + if (val & ARIZONA_DRE1L_ENA_MASK) { + wseq = wm5110_dre_left_enable; + nregs = ARRAY_SIZE(wm5110_dre_left_enable); + } else { + wseq = wm5110_no_dre_left_enable; + nregs = ARRAY_SIZE(wm5110_no_dre_left_enable); + priv->out_up_delay += 10; + } + break; + case ARIZONA_OUT1R_ENA_SHIFT: + if (val & ARIZONA_DRE1R_ENA_MASK) { + wseq = wm5110_dre_right_enable; + nregs = ARRAY_SIZE(wm5110_dre_right_enable); + } else { + wseq = wm5110_no_dre_right_enable; + nregs = ARRAY_SIZE(wm5110_no_dre_right_enable); + priv->out_up_delay += 10; + } + break; + default: + return 0; + } + + return regmap_multi_reg_write(arizona->regmap, wseq, nregs); +} + +static int wm5110_hp_pre_disable(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int val = snd_soc_component_read(component, ARIZONA_DRE_ENABLE); + + switch (w->shift) { + case ARIZONA_OUT1L_ENA_SHIFT: + if (!(val & ARIZONA_DRE1L_ENA_MASK)) { + snd_soc_component_update_bits(component, + ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG1, + ARIZONA_WS_TRG1); + snd_soc_component_update_bits(component, + ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG1, 0); + priv->out_down_delay += 27; + } + break; + case ARIZONA_OUT1R_ENA_SHIFT: + if (!(val & ARIZONA_DRE1R_ENA_MASK)) { + snd_soc_component_update_bits(component, + ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG2, + ARIZONA_WS_TRG2); + snd_soc_component_update_bits(component, + ARIZONA_SPARE_TRIGGERS, + ARIZONA_WS_TRG2, 0); + priv->out_down_delay += 27; + } + break; + default: + break; + } + + return 0; +} + +static int wm5110_hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + + switch (priv->arizona->rev) { + case 0 ... 3: + break; + default: + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm5110_hp_pre_enable(w); + break; + case SND_SOC_DAPM_PRE_PMD: + wm5110_hp_pre_disable(w); + break; + default: + break; + } + break; + } + + return arizona_hp_ev(w, kcontrol, event); +} + +static int wm5110_clear_pga_volume(struct arizona *arizona, int output) +{ + unsigned int reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4; + int ret; + + ret = regmap_write(arizona->regmap, reg, 0x80); + if (ret) + dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n", + reg, ret); + + return ret; +} + +static int wm5110_put_dre(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int ena, dre; + unsigned int mask = (0x1 << mc->shift) | (0x1 << mc->rshift); + unsigned int lnew = (!!ucontrol->value.integer.value[0]) << mc->shift; + unsigned int rnew = (!!ucontrol->value.integer.value[1]) << mc->rshift; + unsigned int lold, rold; + unsigned int lena, rena; + bool change = false; + int ret; + + snd_soc_dapm_mutex_lock(dapm); + + ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &ena); + if (ret) { + dev_err(arizona->dev, "Failed to read output state: %d\n", ret); + goto err; + } + ret = regmap_read(arizona->regmap, ARIZONA_DRE_ENABLE, &dre); + if (ret) { + dev_err(arizona->dev, "Failed to read DRE state: %d\n", ret); + goto err; + } + + lold = dre & (1 << mc->shift); + rold = dre & (1 << mc->rshift); + /* Enables are channel wise swapped from the DRE enables */ + lena = ena & (1 << mc->rshift); + rena = ena & (1 << mc->shift); + + if ((lena && lnew != lold) || (rena && rnew != rold)) { + dev_err(arizona->dev, "Can't change DRE on active outputs\n"); + ret = -EBUSY; + goto err; + } + + ret = regmap_update_bits_check(arizona->regmap, ARIZONA_DRE_ENABLE, + mask, lnew | rnew, &change); + if (ret) { + dev_err(arizona->dev, "Failed to set DRE: %d\n", ret); + goto err; + } + + /* Force reset of PGA volumes, if turning DRE off */ + if (!lnew && lold) + wm5110_clear_pga_volume(arizona, mc->shift); + + if (!rnew && rold) + wm5110_clear_pga_volume(arizona, mc->rshift); + + if (change) + ret = 1; + +err: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + /* + * PGA Volume is also used as part of the enable sequence, so + * usage of it should be avoided whilst that is running. + */ + snd_soc_dapm_mutex_lock(dapm); + + ret = snd_soc_get_volsw_range(kcontrol, ucontrol); + + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + + /* + * PGA Volume is also used as part of the enable sequence, so + * usage of it should be avoided whilst that is running. + */ + snd_soc_dapm_mutex_lock(dapm); + + ret = snd_soc_put_volsw_range(kcontrol, ucontrol); + + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct wm5110_priv *wm5110 = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + unsigned int reg, mask; + struct reg_sequence analog_seq[] = { + { 0x80, 0x3 }, + { 0x35d, 0 }, + { 0x80, 0x0 }, + }; + + reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4); + mask = ARIZONA_IN1L_PGA_VOL_MASK; + + switch (event) { + case SND_SOC_DAPM_WILL_PMU: + wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2); + wm5110->in_pre_pending++; + wm5110->in_post_pending++; + return 0; + case SND_SOC_DAPM_PRE_PMU: + wm5110->in_pga_cache[w->shift] = snd_soc_component_read(component, reg); + + snd_soc_component_update_bits(component, reg, mask, + 0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT); + + wm5110->in_pre_pending--; + if (wm5110->in_pre_pending == 0) { + analog_seq[1].def = wm5110->in_value; + regmap_multi_reg_write_bypassed(arizona->regmap, + analog_seq, + ARRAY_SIZE(analog_seq)); + + msleep(55); + + wm5110->in_value = 0; + } + + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, reg, mask, + wm5110->in_pga_cache[w->shift]); + + wm5110->in_post_pending--; + if (wm5110->in_post_pending == 0) + regmap_multi_reg_write_bypassed(arizona->regmap, + analog_seq, + ARRAY_SIZE(analog_seq)); + break; + default: + break; + } + + return 0; +} + +static int wm5110_in_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->arizona; + + switch (arizona->rev) { + case 0 ... 4: + if (arizona_input_analog(component, w->shift)) + wm5110_in_analog_ev(w, kcontrol, event); + + break; + default: + break; + } + + return arizona_in_ev(w, kcontrol, event); +} + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define WM5110_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0) + +#define WM5110_RXANC_INPUT_ROUTES(widget, name) \ + { widget, NULL, name " NG Mux" }, \ + { name " NG Internal", NULL, "RXANC NG Clock" }, \ + { name " NG Internal", NULL, name " Channel" }, \ + { name " NG External", NULL, "RXANC NG External Clock" }, \ + { name " NG External", NULL, name " Channel" }, \ + { name " NG Mux", "None", name " Channel" }, \ + { name " NG Mux", "Internal", name " NG Internal" }, \ + { name " NG Mux", "External", name " NG External" }, \ + { name " Channel", "Left", name " Left Input" }, \ + { name " Channel", "Combine", name " Left Input" }, \ + { name " Channel", "Right", name " Right Input" }, \ + { name " Channel", "Combine", name " Right Input" }, \ + { name " Left Input", "IN1", "IN1L PGA" }, \ + { name " Right Input", "IN1", "IN1R PGA" }, \ + { name " Left Input", "IN2", "IN2L PGA" }, \ + { name " Right Input", "IN2", "IN2R PGA" }, \ + { name " Left Input", "IN3", "IN3L PGA" }, \ + { name " Right Input", "IN3", "IN3R PGA" }, \ + { name " Left Input", "IN4", "IN4L PGA" }, \ + { name " Right Input", "IN4", "IN4R PGA" } + +#define WM5110_RXANC_OUTPUT_ROUTES(widget, name) \ + { widget, NULL, name " ANC Source" }, \ + { name " ANC Source", "RXANCL", "RXANCL" }, \ + { name " ANC Source", "RXANCR", "RXANCR" } + +static const struct snd_kcontrol_new wm5110_snd_controls[] = { +SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), +SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]), +SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]), + +SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), +SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, + wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2L HPF Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2R HPF Switch", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN3L HPF Switch", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN3R HPF Switch", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN4L HPF Switch", ARIZONA_IN4L_CONTROL, + ARIZONA_IN4L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN4R HPF Switch", ARIZONA_IN4R_CONTROL, + ARIZONA_IN4R_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN4L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R, + ARIZONA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +SND_SOC_BYTES("RXANC Coefficients", ARIZONA_ANC_COEFF_START, + ARIZONA_ANC_COEFF_END - ARIZONA_ANC_COEFF_START + 1), +SND_SOC_BYTES("RXANCL Config", ARIZONA_FCL_FILTER_CONTROL, 1), +SND_SOC_BYTES("RXANCL Coefficients", ARIZONA_FCL_COEFF_START, + ARIZONA_FCL_COEFF_END - ARIZONA_FCL_COEFF_START + 1), +SND_SOC_BYTES("RXANCR Config", ARIZONA_FCR_FILTER_CONTROL, 1), +SND_SOC_BYTES("RXANCR Coefficients", ARIZONA_FCR_COEFF_START, + ARIZONA_FCR_COEFF_END - ARIZONA_FCR_COEFF_START + 1), + +ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ3 Coefficients", ARIZONA_EQ3_2), +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ4 Coefficients", ARIZONA_EQ4_2), +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), +SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5, + ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2), +ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2), +ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2), +ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), +SOC_ENUM("ISRC3 FSL", arizona_isrc_fsl[2]), +SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]), +SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]), +SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]), +SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1), + +WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), +WM_ADSP2_PRELOAD_SWITCH("DSP2", 2), +WM_ADSP2_PRELOAD_SWITCH("DSP3", 3), +WM_ADSP2_PRELOAD_SWITCH("DSP4", 4), + +ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP3R", ARIZONA_DSP3RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP4L", ARIZONA_DSP4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP4R", ARIZONA_DSP4RMIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv), + +ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT2L", ARIZONA_OUT2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT2R", ARIZONA_OUT2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT3L", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT3R", ARIZONA_OUT3RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT2L", ARIZONA_OUT6LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT2R", ARIZONA_OUT6RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("HPOUT1 SC Protect Switch", ARIZONA_HP1_SHORT_CIRCUIT_CTRL, + ARIZONA_HP1_SC_ENA_SHIFT, 1, 0), +SOC_SINGLE("HPOUT2 SC Protect Switch", ARIZONA_HP2_SHORT_CIRCUIT_CTRL, + ARIZONA_HP2_SC_ENA_SHIFT, 1, 0), +SOC_SINGLE("HPOUT3 SC Protect Switch", ARIZONA_HP3_SHORT_CIRCUIT_CTRL, + ARIZONA_HP3_SC_ENA_SHIFT, 1, 0), + +SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L, + ARIZONA_OUT5_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_6L, + ARIZONA_OUT6_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("HPOUT3 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_3R, ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_6L, + ARIZONA_DAC_DIGITAL_VOLUME_6R, ARIZONA_OUT6L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("HPOUT3 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_3R, ARIZONA_OUT3L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_6L, + ARIZONA_DAC_DIGITAL_VOLUME_6R, ARIZONA_OUT6L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), +SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT, + ARIZONA_SPK2R_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_EXT("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0, + snd_soc_get_volsw, wm5110_put_dre), +SOC_DOUBLE_EXT("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0, + snd_soc_get_volsw, wm5110_put_dre), +SOC_DOUBLE_EXT("HPOUT3 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE3L_ENA_SHIFT, ARIZONA_DRE3R_ENA_SHIFT, 1, 0, + snd_soc_get_volsw, wm5110_put_dre), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM5110_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM5110_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM5110_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), +WM5110_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R), +WM5110_NG_SRC("HPOUT3L", ARIZONA_NOISE_GATE_SELECT_3L), +WM5110_NG_SRC("HPOUT3R", ARIZONA_NOISE_GATE_SELECT_3R), +WM5110_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM5110_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM5110_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM5110_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), +WM5110_NG_SRC("SPKDAT2L", ARIZONA_NOISE_GATE_SELECT_6L), +WM5110_NG_SRC("SPKDAT2R", ARIZONA_NOISE_GATE_SELECT_6R), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), + +WM_ADSP_FW_CONTROL("DSP1", 0), +WM_ADSP_FW_CONTROL("DSP2", 1), +WM_ADSP_FW_CONTROL("DSP3", 2), +WM_ADSP_FW_CONTROL("DSP4", 3), +}; + +ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP1, ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP2L, ARIZONA_DSP2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP2R, ARIZONA_DSP2RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP2, ARIZONA_DSP2AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP3L, ARIZONA_DSP3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP3R, ARIZONA_DSP3RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP3, ARIZONA_DSP3AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DSP4L, ARIZONA_DSP4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DSP4R, ARIZONA_DSP4RMIX_INPUT_1_SOURCE); +ARIZONA_DSP_AUX_ENUMS(DSP4, ARIZONA_DSP4AUX1MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3L, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3R, ARIZONA_OUT3RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT2L, ARIZONA_OUT6LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT2R, ARIZONA_OUT6RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT3, ARIZONA_ISRC2INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT4, ARIZONA_ISRC2INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC3, ARIZONA_ISRC2DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC4, ARIZONA_ISRC2DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC3INT1, ARIZONA_ISRC3INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT2, ARIZONA_ISRC3INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT3, ARIZONA_ISRC3INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3INT4, ARIZONA_ISRC3INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC3DEC1, ARIZONA_ISRC3DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC2, ARIZONA_ISRC3DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC3, ARIZONA_ISRC3DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC3DEC4, ARIZONA_ISRC3DEC4MIX_INPUT_1_SOURCE); + +static const char * const wm5110_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R", + "SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R", "SPKDAT2L", "SPKDAT2R", +}; + +static const unsigned int wm5110_aec_loopback_values[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +}; + +static const struct soc_enum wm5110_aec_loopback = + SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(wm5110_aec_loopback_texts), + wm5110_aec_loopback_texts, + wm5110_aec_loopback_values); + +static const struct snd_kcontrol_new wm5110_aec_loopback_mux = + SOC_DAPM_ENUM("AEC Loopback", wm5110_aec_loopback); + +static const struct snd_kcontrol_new wm5110_anc_input_mux[] = { + SOC_DAPM_ENUM("RXANCL Input", arizona_anc_input_src[0]), + SOC_DAPM_ENUM("RXANCL Channel", arizona_anc_input_src[1]), + SOC_DAPM_ENUM("RXANCR Input", arizona_anc_input_src[2]), + SOC_DAPM_ENUM("RXANCR Channel", arizona_anc_input_src[3]), +}; + +static const struct snd_kcontrol_new wm5110_anc_ng_mux = + SOC_DAPM_ENUM("RXANC NG Source", arizona_anc_ng_enum); + +static const struct snd_kcontrol_new wm5110_output_anc_src[] = { + SOC_DAPM_ENUM("HPOUT1L ANC Source", arizona_output_anc_src[0]), + SOC_DAPM_ENUM("HPOUT1R ANC Source", arizona_output_anc_src[1]), + SOC_DAPM_ENUM("HPOUT2L ANC Source", arizona_output_anc_src[2]), + SOC_DAPM_ENUM("HPOUT2R ANC Source", arizona_output_anc_src[3]), + SOC_DAPM_ENUM("HPOUT3L ANC Source", arizona_output_anc_src[4]), + SOC_DAPM_ENUM("HPOUT3R ANC Source", arizona_output_anc_src[5]), + SOC_DAPM_ENUM("SPKOUTL ANC Source", arizona_output_anc_src[6]), + SOC_DAPM_ENUM("SPKOUTR ANC Source", arizona_output_anc_src[7]), + SOC_DAPM_ENUM("SPKDAT1L ANC Source", arizona_output_anc_src[8]), + SOC_DAPM_ENUM("SPKDAT1R ANC Source", arizona_output_anc_src[9]), + SOC_DAPM_ENUM("SPKDAT2L ANC Source", arizona_output_anc_src[10]), + SOC_DAPM_ENUM("SPKDAT2R ANC Source", arizona_output_anc_src[11]), +}; + +static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, + 0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), +SND_SOC_DAPM_INPUT("IN3L"), +SND_SOC_DAPM_INPUT("IN3R"), +SND_SOC_DAPM_INPUT("IN4L"), +SND_SOC_DAPM_INPUT("IN4R"), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + +SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"), + +SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0, + &arizona_voice_trigger_switch[2]), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, wm5110_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, wm5110_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, wm5110_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, + 0, NULL, 0, wm5110_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), +SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT, + 0, NULL, 0, wm5110_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), +SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT, + 0, NULL, 0, wm5110_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_WILL_PMU), +SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN4R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1, + ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0), + +WM_ADSP2("DSP1", 0, wm5110_adsp_power_ev), +WM_ADSP2("DSP2", 1, wm5110_adsp_power_ev), +WM_ADSP2("DSP3", 2, wm5110_adsp_power_ev), +WM_ADSP2("DSP4", 3, wm5110_adsp_power_ev), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT3", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT4", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC3", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC4", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3INT1", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT2", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT3", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3INT4", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC3DEC1", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC2", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC3", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC3DEC4", ARIZONA_ISRC_3_CTRL_3, + ARIZONA_ISRC3_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, &wm5110_aec_loopback_mux), + +SND_SOC_DAPM_SUPPLY("RXANC NG External Clock", SND_SOC_NOPM, + ARIZONA_EXT_NG_SEL_SET_SHIFT, 0, arizona_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA("RXANCL NG External", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("RXANCR NG External", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("RXANC NG Clock", SND_SOC_NOPM, + ARIZONA_CLK_NG_ENA_SET_SHIFT, 0, arizona_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA("RXANCL NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("RXANCR NG Internal", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MUX("RXANCL Left Input", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[0]), +SND_SOC_DAPM_MUX("RXANCL Right Input", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[0]), +SND_SOC_DAPM_MUX("RXANCL Channel", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[1]), +SND_SOC_DAPM_MUX("RXANCL NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux), +SND_SOC_DAPM_MUX("RXANCR Left Input", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[2]), +SND_SOC_DAPM_MUX("RXANCR Right Input", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[2]), +SND_SOC_DAPM_MUX("RXANCR Channel", SND_SOC_NOPM, 0, 0, + &wm5110_anc_input_mux[3]), +SND_SOC_DAPM_MUX("RXANCR NG Mux", SND_SOC_NOPM, 0, 0, &wm5110_anc_ng_mux), + +SND_SOC_DAPM_PGA_E("RXANCL", SND_SOC_NOPM, ARIZONA_CLK_L_ENA_SET_SHIFT, + 0, NULL, 0, arizona_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_E("RXANCR", SND_SOC_NOPM, ARIZONA_CLK_R_ENA_SET_SHIFT, + 0, NULL, 0, arizona_anc_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_MUX("HPOUT1L ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[0]), +SND_SOC_DAPM_MUX("HPOUT1R ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[1]), +SND_SOC_DAPM_MUX("HPOUT2L ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[2]), +SND_SOC_DAPM_MUX("HPOUT2R ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[3]), +SND_SOC_DAPM_MUX("HPOUT3L ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[4]), +SND_SOC_DAPM_MUX("HPOUT3R ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[5]), +SND_SOC_DAPM_MUX("SPKOUTL ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[6]), +SND_SOC_DAPM_MUX("SPKOUTR ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[7]), +SND_SOC_DAPM_MUX("SPKDAT1L ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[8]), +SND_SOC_DAPM_MUX("SPKDAT1R ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[9]), +SND_SOC_DAPM_MUX("SPKDAT2L ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[10]), +SND_SOC_DAPM_MUX("SPKDAT2R ANC Source", SND_SOC_NOPM, 0, 0, + &wm5110_output_anc_src[11]), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, wm5110_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT6L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT6L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT6R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT6R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"), +ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"), +ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"), +ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"), +ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"), +ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(Mic, "Mic"), +ARIZONA_MIXER_WIDGETS(Noise, "Noise"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +ARIZONA_MIXER_WIDGETS(OUT2L, "HPOUT2L"), +ARIZONA_MIXER_WIDGETS(OUT2R, "HPOUT2R"), +ARIZONA_MIXER_WIDGETS(OUT3L, "HPOUT3L"), +ARIZONA_MIXER_WIDGETS(OUT3R, "HPOUT3R"), +ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"), +ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), +ARIZONA_MIXER_WIDGETS(SPKDAT2L, "SPKDAT2L"), +ARIZONA_MIXER_WIDGETS(SPKDAT2R, "SPKDAT2R"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), +ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"), +ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"), + +ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), + +ARIZONA_DSP_WIDGETS(DSP1, "DSP1"), +ARIZONA_DSP_WIDGETS(DSP2, "DSP2"), +ARIZONA_DSP_WIDGETS(DSP3, "DSP3"), +ARIZONA_DSP_WIDGETS(DSP4, "DSP4"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), +ARIZONA_MUX_WIDGETS(ISRC2DEC3, "ISRC2DEC3"), +ARIZONA_MUX_WIDGETS(ISRC2DEC4, "ISRC2DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), +ARIZONA_MUX_WIDGETS(ISRC2INT3, "ISRC2INT3"), +ARIZONA_MUX_WIDGETS(ISRC2INT4, "ISRC2INT4"), + +ARIZONA_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"), +ARIZONA_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"), +ARIZONA_MUX_WIDGETS(ISRC3DEC3, "ISRC3DEC3"), +ARIZONA_MUX_WIDGETS(ISRC3DEC4, "ISRC3DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"), +ARIZONA_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"), +ARIZONA_MUX_WIDGETS(ISRC3INT3, "ISRC3INT3"), +ARIZONA_MUX_WIDGETS(ISRC3INT4, "ISRC3INT4"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2L"), +SND_SOC_DAPM_OUTPUT("HPOUT2R"), +SND_SOC_DAPM_OUTPUT("HPOUT3L"), +SND_SOC_DAPM_OUTPUT("HPOUT3R"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), +SND_SOC_DAPM_OUTPUT("SPKDAT2L"), +SND_SOC_DAPM_OUTPUT("SPKDAT2R"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "IN3L", "IN3L PGA" }, \ + { name, "IN3R", "IN3R PGA" }, \ + { name, "IN4L", "IN4L PGA" }, \ + { name, "IN4R", "IN4R PGA" }, \ + { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "SLIMRX7", "SLIMRX7" }, \ + { name, "SLIMRX8", "SLIMRX8" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "DRC2L", "DRC2L" }, \ + { name, "DRC2R", "DRC2R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2DEC3", "ISRC2DEC3" }, \ + { name, "ISRC2DEC4", "ISRC2DEC4" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ + { name, "ISRC2INT3", "ISRC2INT3" }, \ + { name, "ISRC2INT4", "ISRC2INT4" }, \ + { name, "ISRC3DEC1", "ISRC3DEC1" }, \ + { name, "ISRC3DEC2", "ISRC3DEC2" }, \ + { name, "ISRC3DEC3", "ISRC3DEC3" }, \ + { name, "ISRC3DEC4", "ISRC3DEC4" }, \ + { name, "ISRC3INT1", "ISRC3INT1" }, \ + { name, "ISRC3INT2", "ISRC3INT2" }, \ + { name, "ISRC3INT3", "ISRC3INT3" }, \ + { name, "ISRC3INT4", "ISRC3INT4" }, \ + { name, "DSP1.1", "DSP1" }, \ + { name, "DSP1.2", "DSP1" }, \ + { name, "DSP1.3", "DSP1" }, \ + { name, "DSP1.4", "DSP1" }, \ + { name, "DSP1.5", "DSP1" }, \ + { name, "DSP1.6", "DSP1" }, \ + { name, "DSP2.1", "DSP2" }, \ + { name, "DSP2.2", "DSP2" }, \ + { name, "DSP2.3", "DSP2" }, \ + { name, "DSP2.4", "DSP2" }, \ + { name, "DSP2.5", "DSP2" }, \ + { name, "DSP2.6", "DSP2" }, \ + { name, "DSP3.1", "DSP3" }, \ + { name, "DSP3.2", "DSP3" }, \ + { name, "DSP3.3", "DSP3" }, \ + { name, "DSP3.4", "DSP3" }, \ + { name, "DSP3.5", "DSP3" }, \ + { name, "DSP3.6", "DSP3" }, \ + { name, "DSP4.1", "DSP4" }, \ + { name, "DSP4.2", "DSP4" }, \ + { name, "DSP4.3", "DSP4" }, \ + { name, "DSP4.4", "DSP4" }, \ + { name, "DSP4.5", "DSP4" }, \ + { name, "DSP4.6", "DSP4" } + +static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD3" }, + { "AIF3 Playback", NULL, "DBVDD3" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT2L", NULL, "CPVDD" }, + { "OUT2R", NULL, "CPVDD" }, + { "OUT3L", NULL, "CPVDD" }, + { "OUT3R", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDDL" }, + { "OUT4R", NULL, "SPKVDDR" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT3R", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT4R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + { "OUT6L", NULL, "SYSCLK" }, + { "OUT6R", NULL, "SYSCLK" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + { "IN3L", NULL, "SYSCLK" }, + { "IN3R", NULL, "SYSCLK" }, + { "IN4L", NULL, "SYSCLK" }, + { "IN4R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "SYSCLK" }, + { "ASRC1R", NULL, "SYSCLK" }, + { "ASRC2L", NULL, "SYSCLK" }, + { "ASRC2R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "ASYNCCLK" }, + { "ASRC1R", NULL, "ASYNCCLK" }, + { "ASRC2L", NULL, "ASYNCCLK" }, + { "ASRC2R", NULL, "ASYNCCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "Slim3 Capture", NULL, "SLIMTX7" }, + { "Slim3 Capture", NULL, "SLIMTX8" }, + + { "SLIMRX7", NULL, "Slim3 Playback" }, + { "SLIMRX8", NULL, "Slim3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + { "Slim3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + { "Slim3 Capture", NULL, "SYSCLK" }, + + { "Voice Control DSP", NULL, "DSP3" }, + + { "Audio Trace DSP", NULL, "DSP1" }, + + { "IN1L PGA", NULL, "IN1L" }, + { "IN1R PGA", NULL, "IN1R" }, + + { "IN2L PGA", NULL, "IN2L" }, + { "IN2R PGA", NULL, "IN2R" }, + + { "IN3L PGA", NULL, "IN3L" }, + { "IN3R PGA", NULL, "IN3R" }, + + { "IN4L PGA", NULL, "IN4L" }, + { "IN4R PGA", NULL, "IN4R" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"), + ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"), + ARIZONA_MIXER_ROUTES("OUT3L", "HPOUT3L"), + ARIZONA_MIXER_ROUTES("OUT3R", "HPOUT3R"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"), + ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + ARIZONA_MIXER_ROUTES("OUT6L", "SPKDAT2L"), + ARIZONA_MIXER_ROUTES("OUT6R", "SPKDAT2R"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"), + ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"), + + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), + ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), + ARIZONA_MIXER_ROUTES("EQ3", "EQ3"), + ARIZONA_MIXER_ROUTES("EQ4", "EQ4"), + + ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"), + ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"), + ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"), + + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + + ARIZONA_DSP_ROUTES("DSP1"), + ARIZONA_DSP_ROUTES("DSP2"), + ARIZONA_DSP_ROUTES("DSP3"), + ARIZONA_DSP_ROUTES("DSP4"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + ARIZONA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"), + ARIZONA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + ARIZONA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"), + ARIZONA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"), + + ARIZONA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"), + ARIZONA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"), + ARIZONA_MUX_ROUTES("ISRC3INT3", "ISRC3INT3"), + ARIZONA_MUX_ROUTES("ISRC3INT4", "ISRC3INT4"), + + ARIZONA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"), + ARIZONA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"), + ARIZONA_MUX_ROUTES("ISRC3DEC3", "ISRC3DEC3"), + ARIZONA_MUX_ROUTES("ISRC3DEC4", "ISRC3DEC4"), + + { "AEC Loopback", "HPOUT1L", "OUT1L" }, + { "AEC Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC Loopback", "HPOUT2L", "OUT2L" }, + { "AEC Loopback", "HPOUT2R", "OUT2R" }, + { "HPOUT2L", NULL, "OUT2L" }, + { "HPOUT2R", NULL, "OUT2R" }, + + { "AEC Loopback", "HPOUT3L", "OUT3L" }, + { "AEC Loopback", "HPOUT3R", "OUT3R" }, + { "HPOUT3L", NULL, "OUT3L" }, + { "HPOUT3R", NULL, "OUT3R" }, + + { "AEC Loopback", "SPKOUTL", "OUT4L" }, + { "SPKOUTLN", NULL, "OUT4L" }, + { "SPKOUTLP", NULL, "OUT4L" }, + + { "AEC Loopback", "SPKOUTR", "OUT4R" }, + { "SPKOUTRN", NULL, "OUT4R" }, + { "SPKOUTRP", NULL, "OUT4R" }, + + { "AEC Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC Loopback", "SPKDAT1R", "OUT5R" }, + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + { "AEC Loopback", "SPKDAT2L", "OUT6L" }, + { "AEC Loopback", "SPKDAT2R", "OUT6R" }, + { "SPKDAT2L", NULL, "OUT6L" }, + { "SPKDAT2R", NULL, "OUT6R" }, + + WM5110_RXANC_INPUT_ROUTES("RXANCL", "RXANCL"), + WM5110_RXANC_INPUT_ROUTES("RXANCR", "RXANCR"), + + WM5110_RXANC_OUTPUT_ROUTES("OUT1L", "HPOUT1L"), + WM5110_RXANC_OUTPUT_ROUTES("OUT1R", "HPOUT1R"), + WM5110_RXANC_OUTPUT_ROUTES("OUT2L", "HPOUT2L"), + WM5110_RXANC_OUTPUT_ROUTES("OUT2R", "HPOUT2R"), + WM5110_RXANC_OUTPUT_ROUTES("OUT3L", "HPOUT3L"), + WM5110_RXANC_OUTPUT_ROUTES("OUT3R", "HPOUT3R"), + WM5110_RXANC_OUTPUT_ROUTES("OUT4L", "SPKOUTL"), + WM5110_RXANC_OUTPUT_ROUTES("OUT4R", "SPKOUTR"), + WM5110_RXANC_OUTPUT_ROUTES("OUT5L", "SPKDAT1L"), + WM5110_RXANC_OUTPUT_ROUTES("OUT5R", "SPKDAT1R"), + WM5110_RXANC_OUTPUT_ROUTES("OUT6L", "SPKDAT2L"), + WM5110_RXANC_OUTPUT_ROUTES("OUT6R", "SPKDAT2R"), + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "SYSCLK" }, + { "DRC2 Signal Activity", NULL, "SYSCLK" }, + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, + { "DRC2 Signal Activity", NULL, "DRC2L" }, + { "DRC2 Signal Activity", NULL, "DRC2R" }, + + { "DSP Voice Trigger", NULL, "SYSCLK" }, + { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" }, + { "DSP3 Voice Trigger", "Switch", "DSP3" }, +}; + +static int wm5110_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int Fref, unsigned int Fout) +{ + struct wm5110_priv *wm5110 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case WM5110_FLL1: + return arizona_set_fll(&wm5110->fll[0], source, Fref, Fout); + case WM5110_FLL2: + return arizona_set_fll(&wm5110->fll[1], source, Fref, Fout); + case WM5110_FLL1_REFCLK: + return arizona_set_fll_refclk(&wm5110->fll[0], source, Fref, + Fout); + case WM5110_FLL2_REFCLK: + return arizona_set_fll_refclk(&wm5110->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +#define WM5110_RATES SNDRV_PCM_RATE_KNOT + +#define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm5110_dai_ops = { + .compress_new = snd_soc_new_compress, +}; + +static struct snd_soc_dai_driver wm5110_dai[] = { + { + .name = "wm5110-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm5110-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm5110-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm5110-slim1", + .id = 4, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm5110-slim2", + .id = 5, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm5110-slim3", + .id = 6, + .playback = { + .stream_name = "Slim3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "Slim3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm5110-cpu-voicectrl", + .capture = { + .stream_name = "Voice Control CPU", + .channels_min = 1, + .channels_max = 1, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &wm5110_dai_ops, + }, + { + .name = "wm5110-dsp-voicectrl", + .capture = { + .stream_name = "Voice Control DSP", + .channels_min = 1, + .channels_max = 1, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + }, + { + .name = "wm5110-cpu-trace", + .capture = { + .stream_name = "Audio Trace CPU", + .channels_min = 1, + .channels_max = 6, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &wm5110_dai_ops, + }, + { + .name = "wm5110-dsp-trace", + .capture = { + .stream_name = "Audio Trace DSP", + .channels_min = 1, + .channels_max = 6, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + }, +}; + +static int wm5110_open(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct wm5110_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->core.arizona; + int n_adsp; + + if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "wm5110-dsp-voicectrl") == 0) { + n_adsp = 2; + } else if (strcmp(asoc_rtd_to_codec(rtd, 0)->name, "wm5110-dsp-trace") == 0) { + n_adsp = 0; + } else { + dev_err(arizona->dev, + "No suitable compressed stream for DAI '%s'\n", + asoc_rtd_to_codec(rtd, 0)->name); + return -EINVAL; + } + + return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream); +} + +static irqreturn_t wm5110_adsp2_irq(int irq, void *data) +{ + struct wm5110_priv *priv = data; + struct arizona *arizona = priv->core.arizona; + struct arizona_voice_trigger_info info; + int serviced = 0; + int i, ret; + + for (i = 0; i < WM5110_NUM_ADSP; ++i) { + ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]); + if (ret != -ENODEV) + serviced++; + if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) { + info.core = i; + arizona_call_notifiers(arizona, + ARIZONA_NOTIFY_VOICE_TRIGGER, + &info); + } + } + + if (!serviced) { + dev_err(arizona->dev, "Spurious compressed data IRQ\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int wm5110_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm5110_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->core.arizona; + int i, ret; + + arizona->dapm = dapm; + snd_soc_component_init_regmap(component, arizona->regmap); + + ret = arizona_init_spk(component); + if (ret < 0) + return ret; + + arizona_init_gpio(component); + arizona_init_mono(component); + + for (i = 0; i < WM5110_NUM_ADSP; ++i) { + ret = wm_adsp2_component_probe(&priv->core.adsp[i], component); + if (ret) + goto err_adsp2_codec_probe; + } + + ret = snd_soc_add_component_controls(component, + arizona_adsp2_rate_controls, + WM5110_NUM_ADSP); + if (ret) + goto err_adsp2_codec_probe; + + snd_soc_component_disable_pin(component, "HAPTICS"); + + return 0; + +err_adsp2_codec_probe: + for (--i; i >= 0; --i) + wm_adsp2_component_remove(&priv->core.adsp[i], component); + + return ret; +} + +static void wm5110_component_remove(struct snd_soc_component *component) +{ + struct wm5110_priv *priv = snd_soc_component_get_drvdata(component); + int i; + + for (i = 0; i < WM5110_NUM_ADSP; ++i) + wm_adsp2_component_remove(&priv->core.adsp[i], component); + + priv->core.arizona->dapm = NULL; +} + +#define WM5110_DIG_VU 0x0200 + +static unsigned int wm5110_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_3R, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, + ARIZONA_DAC_DIGITAL_VOLUME_6L, + ARIZONA_DAC_DIGITAL_VOLUME_6R, +}; + +static const struct snd_compress_ops wm5110_compress_ops = { + .open = wm5110_open, + .free = wm_adsp_compr_free, + .set_params = wm_adsp_compr_set_params, + .get_caps = wm_adsp_compr_get_caps, + .trigger = wm_adsp_compr_trigger, + .pointer = wm_adsp_compr_pointer, + .copy = wm_adsp_compr_copy, +}; + +static const struct snd_soc_component_driver soc_component_dev_wm5110 = { + .probe = wm5110_component_probe, + .remove = wm5110_component_remove, + .set_sysclk = arizona_set_sysclk, + .set_pll = wm5110_set_fll, + .set_jack = arizona_jack_set_jack, + .name = DRV_NAME, + .compress_ops = &wm5110_compress_ops, + .controls = wm5110_snd_controls, + .num_controls = ARRAY_SIZE(wm5110_snd_controls), + .dapm_widgets = wm5110_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm5110_dapm_widgets), + .dapm_routes = wm5110_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm5110_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm5110_priv *wm5110; + int i, ret; + + wm5110 = devm_kzalloc(&pdev->dev, sizeof(struct wm5110_priv), + GFP_KERNEL); + if (wm5110 == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, wm5110); + + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + + wm5110->core.arizona = arizona; + wm5110->core.num_inputs = 8; + + for (i = 0; i < WM5110_NUM_ADSP; i++) { + wm5110->core.adsp[i].part = "wm5110"; + wm5110->core.adsp[i].cs_dsp.num = i + 1; + wm5110->core.adsp[i].cs_dsp.type = WMFW_ADSP2; + wm5110->core.adsp[i].cs_dsp.dev = arizona->dev; + wm5110->core.adsp[i].cs_dsp.regmap = arizona->regmap; + + wm5110->core.adsp[i].cs_dsp.base = ARIZONA_DSP1_CONTROL_1 + + (0x100 * i); + wm5110->core.adsp[i].cs_dsp.mem = wm5110_dsp_regions[i]; + wm5110->core.adsp[i].cs_dsp.num_mems + = ARRAY_SIZE(wm5110_dsp1_regions); + + ret = wm_adsp2_init(&wm5110->core.adsp[i]); + if (ret != 0) + return ret; + } + + /* This may return -EPROBE_DEFER, so do this early on */ + ret = arizona_jack_codec_dev_probe(&wm5110->core, &pdev->dev); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(wm5110->fll); i++) + wm5110->fll[i].vco_mult = 3; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm5110->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm5110->fll[1]); + + /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */ + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2, + ARIZONA_SAMPLE_RATE_2_MASK, 0x11); + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3, + ARIZONA_SAMPLE_RATE_3_MASK, 0x12); + + for (i = 0; i < ARRAY_SIZE(wm5110_dai); i++) + arizona_init_dai(&wm5110->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm5110_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm5110_digital_vu[i], + WM5110_DIG_VU, WM5110_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, + "ADSP2 Compressed IRQ", wm5110_adsp2_irq, + wm5110); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret); + goto err_jack_codec_dev; + } + + ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1); + if (ret != 0) + dev_warn(&pdev->dev, + "Failed to set compressed IRQ as a wake source: %d\n", + ret); + + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; + ret = arizona_init_spk_irqs(arizona); + if (ret < 0) + goto err_dsp_irq; + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm5110, + wm5110_dai, + ARRAY_SIZE(wm5110_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto err_spk_irqs; + } + + return ret; + +err_spk_irqs: + arizona_free_spk_irqs(arizona); +err_dsp_irq: + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110); +err_jack_codec_dev: + pm_runtime_disable(&pdev->dev); + arizona_jack_codec_dev_remove(&wm5110->core); + + return ret; +} + +static void wm5110_remove(struct platform_device *pdev) +{ + struct wm5110_priv *wm5110 = platform_get_drvdata(pdev); + struct arizona *arizona = wm5110->core.arizona; + int i; + + pm_runtime_disable(&pdev->dev); + + for (i = 0; i < WM5110_NUM_ADSP; i++) + wm_adsp2_remove(&wm5110->core.adsp[i]); + + arizona_free_spk_irqs(arizona); + + arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0); + arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110); + + arizona_jack_codec_dev_remove(&wm5110->core); +} + +static struct platform_driver wm5110_codec_driver = { + .driver = { + .name = "wm5110-codec", + }, + .probe = wm5110_probe, + .remove_new = wm5110_remove, +}; + +module_platform_driver(wm5110_codec_driver); + +MODULE_DESCRIPTION("ASoC WM5110 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm5110-codec"); diff --git a/sound/soc/codecs/wm5110.h b/sound/soc/codecs/wm5110.h new file mode 100644 index 0000000000..2545e86131 --- /dev/null +++ b/sound/soc/codecs/wm5110.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm5110.h -- WM5110 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#ifndef _WM5110_H +#define _WM5110_H + +#include "arizona.h" + +#define WM5110_FLL1 1 +#define WM5110_FLL2 2 +#define WM5110_FLL1_REFCLK 3 +#define WM5110_FLL2_REFCLK 4 + +#endif diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c new file mode 100644 index 0000000000..66bd281095 --- /dev/null +++ b/sound/soc/codecs/wm8350.c @@ -0,0 +1,1637 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8350.c -- WM8350 ALSA SoC audio driver + * + * Copyright (C) 2007-12 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8350.h" + +#define WM8350_OUTn_0dB 0x39 + +#define WM8350_RAMP_NONE 0 +#define WM8350_RAMP_UP 1 +#define WM8350_RAMP_DOWN 2 + +/* We only include the analogue supplies here; the digital supplies + * need to be available well before this driver can be probed. + */ +static const char *supply_names[] = { + "AVDD", + "HPVDD", +}; + +struct wm8350_output { + u16 active; + u16 left_vol; + u16 right_vol; + u16 ramp; + u16 mute; +}; + +struct wm8350_jack_data { + struct snd_soc_jack *jack; + struct delayed_work work; + int report; + int short_report; +}; + +struct wm8350_data { + struct wm8350 *wm8350; + struct wm8350_output out1; + struct wm8350_output out2; + struct wm8350_jack_data hpl; + struct wm8350_jack_data hpr; + struct wm8350_jack_data mic; + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; + int fll_freq_out; + int fll_freq_in; + struct delayed_work pga_work; +}; + +/* + * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown. + */ +static inline int wm8350_out1_ramp_step(struct wm8350_data *wm8350_data) +{ + struct wm8350_output *out1 = &wm8350_data->out1; + struct wm8350 *wm8350 = wm8350_data->wm8350; + int left_complete = 0, right_complete = 0; + u16 reg, val; + + /* left channel */ + reg = wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME); + val = (reg & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; + + if (out1->ramp == WM8350_RAMP_UP) { + /* ramp step up */ + if (val < out1->left_vol) { + val++; + reg &= ~WM8350_OUT1L_VOL_MASK; + wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, + reg | (val << WM8350_OUT1L_VOL_SHIFT)); + } else + left_complete = 1; + } else if (out1->ramp == WM8350_RAMP_DOWN) { + /* ramp step down */ + if (val > 0) { + val--; + reg &= ~WM8350_OUT1L_VOL_MASK; + wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, + reg | (val << WM8350_OUT1L_VOL_SHIFT)); + } else + left_complete = 1; + } else + return 1; + + /* right channel */ + reg = wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME); + val = (reg & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; + if (out1->ramp == WM8350_RAMP_UP) { + /* ramp step up */ + if (val < out1->right_vol) { + val++; + reg &= ~WM8350_OUT1R_VOL_MASK; + wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, + reg | (val << WM8350_OUT1R_VOL_SHIFT)); + } else + right_complete = 1; + } else if (out1->ramp == WM8350_RAMP_DOWN) { + /* ramp step down */ + if (val > 0) { + val--; + reg &= ~WM8350_OUT1R_VOL_MASK; + wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, + reg | (val << WM8350_OUT1R_VOL_SHIFT)); + } else + right_complete = 1; + } + + /* only hit the update bit if either volume has changed this step */ + if (!left_complete || !right_complete) + wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU); + + return left_complete & right_complete; +} + +/* + * Ramp OUT2 PGA volume to minimise pops at stream startup and shutdown. + */ +static inline int wm8350_out2_ramp_step(struct wm8350_data *wm8350_data) +{ + struct wm8350_output *out2 = &wm8350_data->out2; + struct wm8350 *wm8350 = wm8350_data->wm8350; + int left_complete = 0, right_complete = 0; + u16 reg, val; + + /* left channel */ + reg = wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME); + val = (reg & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; + if (out2->ramp == WM8350_RAMP_UP) { + /* ramp step up */ + if (val < out2->left_vol) { + val++; + reg &= ~WM8350_OUT2L_VOL_MASK; + wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, + reg | (val << WM8350_OUT1L_VOL_SHIFT)); + } else + left_complete = 1; + } else if (out2->ramp == WM8350_RAMP_DOWN) { + /* ramp step down */ + if (val > 0) { + val--; + reg &= ~WM8350_OUT2L_VOL_MASK; + wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, + reg | (val << WM8350_OUT1L_VOL_SHIFT)); + } else + left_complete = 1; + } else + return 1; + + /* right channel */ + reg = wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME); + val = (reg & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; + if (out2->ramp == WM8350_RAMP_UP) { + /* ramp step up */ + if (val < out2->right_vol) { + val++; + reg &= ~WM8350_OUT2R_VOL_MASK; + wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, + reg | (val << WM8350_OUT1R_VOL_SHIFT)); + } else + right_complete = 1; + } else if (out2->ramp == WM8350_RAMP_DOWN) { + /* ramp step down */ + if (val > 0) { + val--; + reg &= ~WM8350_OUT2R_VOL_MASK; + wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, + reg | (val << WM8350_OUT1R_VOL_SHIFT)); + } else + right_complete = 1; + } + + /* only hit the update bit if either volume has changed this step */ + if (!left_complete || !right_complete) + wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU); + + return left_complete & right_complete; +} + +/* + * This work ramps both output PGAs at stream start/stop time to + * minimise pop associated with DAPM power switching. + * It's best to enable Zero Cross when ramping occurs to minimise any + * zipper noises. + */ +static void wm8350_pga_work(struct work_struct *work) +{ + struct wm8350_data *wm8350_data = + container_of(work, struct wm8350_data, pga_work.work); + struct wm8350_output *out1 = &wm8350_data->out1, + *out2 = &wm8350_data->out2; + int i, out1_complete, out2_complete; + + /* do we need to ramp at all ? */ + if (out1->ramp == WM8350_RAMP_NONE && out2->ramp == WM8350_RAMP_NONE) + return; + + /* PGA volumes have 6 bits of resolution to ramp */ + for (i = 0; i <= 63; i++) { + out1_complete = 1; + out2_complete = 1; + if (out1->ramp != WM8350_RAMP_NONE) + out1_complete = wm8350_out1_ramp_step(wm8350_data); + if (out2->ramp != WM8350_RAMP_NONE) + out2_complete = wm8350_out2_ramp_step(wm8350_data); + + /* ramp finished ? */ + if (out1_complete && out2_complete) + break; + + /* we need to delay longer on the up ramp */ + if (out1->ramp == WM8350_RAMP_UP || + out2->ramp == WM8350_RAMP_UP) { + /* delay is longer over 0dB as increases are larger */ + if (i >= WM8350_OUTn_0dB) + schedule_timeout_interruptible(msecs_to_jiffies + (2)); + else + schedule_timeout_interruptible(msecs_to_jiffies + (1)); + } else + udelay(50); /* doesn't matter if we delay longer */ + } + + out1->ramp = WM8350_RAMP_NONE; + out2->ramp = WM8350_RAMP_NONE; +} + +/* + * WM8350 Controls + */ + +static int pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8350_data *wm8350_data = snd_soc_component_get_drvdata(component); + struct wm8350_output *out; + + switch (w->shift) { + case 0: + case 1: + out = &wm8350_data->out1; + break; + case 2: + case 3: + out = &wm8350_data->out2; + break; + + default: + WARN(1, "Invalid shift %d\n", w->shift); + return -1; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + out->ramp = WM8350_RAMP_UP; + out->active = 1; + + schedule_delayed_work(&wm8350_data->pga_work, + msecs_to_jiffies(1)); + break; + + case SND_SOC_DAPM_PRE_PMD: + out->ramp = WM8350_RAMP_DOWN; + out->active = 0; + + schedule_delayed_work(&wm8350_data->pga_work, + msecs_to_jiffies(1)); + break; + } + + return 0; +} + +static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8350_data *wm8350_priv = snd_soc_component_get_drvdata(component); + struct wm8350_output *out = NULL; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int ret; + unsigned int reg = mc->reg; + u16 val; + + /* For OUT1 and OUT2 we shadow the values and only actually write + * them out when active in order to ensure the amplifier comes on + * as quietly as possible. */ + switch (reg) { + case WM8350_LOUT1_VOLUME: + out = &wm8350_priv->out1; + break; + case WM8350_LOUT2_VOLUME: + out = &wm8350_priv->out2; + break; + default: + break; + } + + if (out) { + out->left_vol = ucontrol->value.integer.value[0]; + out->right_vol = ucontrol->value.integer.value[1]; + if (!out->active) + return 1; + } + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + /* now hit the volume update bits (always bit 8) */ + val = snd_soc_component_read(component, reg); + snd_soc_component_write(component, reg, val | WM8350_OUT1_VU); + return 1; +} + +static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8350_data *wm8350_priv = snd_soc_component_get_drvdata(component); + struct wm8350_output *out1 = &wm8350_priv->out1; + struct wm8350_output *out2 = &wm8350_priv->out2; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + + /* If these are cached registers use the cache */ + switch (reg) { + case WM8350_LOUT1_VOLUME: + ucontrol->value.integer.value[0] = out1->left_vol; + ucontrol->value.integer.value[1] = out1->right_vol; + return 0; + + case WM8350_LOUT2_VOLUME: + ucontrol->value.integer.value[0] = out2->left_vol; + ucontrol->value.integer.value[1] = out2->right_vol; + return 0; + + default: + break; + } + + return snd_soc_get_volsw(kcontrol, ucontrol); +} + +static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" }; +static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" }; +static const char *wm8350_dacmutem[] = { "Normal", "Soft" }; +static const char *wm8350_dacmutes[] = { "Fast", "Slow" }; +static const char *wm8350_adcfilter[] = { "None", "High Pass" }; +static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" }; +static const char *wm8350_lr[] = { "Left", "Right" }; + +static const struct soc_enum wm8350_enum[] = { + SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 4, 4, wm8350_deemp), + SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol), + SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem), + SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes), + SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter), + SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp), + SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol), + SOC_ENUM_SINGLE(WM8350_INPUT_MIXER_VOLUME, 15, 2, wm8350_lr), +}; + +static DECLARE_TLV_DB_SCALE(pre_amp_tlv, -1200, 3525, 0); +static DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 600, 0); +static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1); +static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1); +static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1); + +static const DECLARE_TLV_DB_RANGE(capture_sd_tlv, + 0, 12, TLV_DB_SCALE_ITEM(-3600, 300, 1), + 13, 15, TLV_DB_SCALE_ITEM(0, 0, 0) +); + +static const struct snd_kcontrol_new wm8350_snd_controls[] = { + SOC_ENUM("Playback Deemphasis", wm8350_enum[0]), + SOC_ENUM("Playback DAC Inversion", wm8350_enum[1]), + SOC_DOUBLE_R_EXT_TLV("Playback PCM Volume", + WM8350_DAC_DIGITAL_VOLUME_L, + WM8350_DAC_DIGITAL_VOLUME_R, + 0, 255, 0, wm8350_get_volsw_2r, + wm8350_put_volsw_2r_vu, dac_pcm_tlv), + SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]), + SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]), + SOC_ENUM("Capture PCM Filter", wm8350_enum[4]), + SOC_ENUM("Capture PCM HP Filter", wm8350_enum[5]), + SOC_ENUM("Capture ADC Inversion", wm8350_enum[6]), + SOC_DOUBLE_R_EXT_TLV("Capture PCM Volume", + WM8350_ADC_DIGITAL_VOLUME_L, + WM8350_ADC_DIGITAL_VOLUME_R, + 0, 255, 0, wm8350_get_volsw_2r, + wm8350_put_volsw_2r_vu, adc_pcm_tlv), + SOC_DOUBLE_TLV("Capture Sidetone Volume", + WM8350_ADC_DIVIDER, + 8, 4, 15, 1, capture_sd_tlv), + SOC_DOUBLE_R_EXT_TLV("Capture Volume", + WM8350_LEFT_INPUT_VOLUME, + WM8350_RIGHT_INPUT_VOLUME, + 2, 63, 0, wm8350_get_volsw_2r, + wm8350_put_volsw_2r_vu, pre_amp_tlv), + SOC_DOUBLE_R("Capture ZC Switch", + WM8350_LEFT_INPUT_VOLUME, + WM8350_RIGHT_INPUT_VOLUME, 13, 1, 0), + SOC_SINGLE_TLV("Left Input Left Sidetone Volume", + WM8350_OUTPUT_LEFT_MIXER_VOLUME, 1, 7, 0, out_mix_tlv), + SOC_SINGLE_TLV("Left Input Right Sidetone Volume", + WM8350_OUTPUT_LEFT_MIXER_VOLUME, + 5, 7, 0, out_mix_tlv), + SOC_SINGLE_TLV("Left Input Bypass Volume", + WM8350_OUTPUT_LEFT_MIXER_VOLUME, + 9, 7, 0, out_mix_tlv), + SOC_SINGLE_TLV("Right Input Left Sidetone Volume", + WM8350_OUTPUT_RIGHT_MIXER_VOLUME, + 1, 7, 0, out_mix_tlv), + SOC_SINGLE_TLV("Right Input Right Sidetone Volume", + WM8350_OUTPUT_RIGHT_MIXER_VOLUME, + 5, 7, 0, out_mix_tlv), + SOC_SINGLE_TLV("Right Input Bypass Volume", + WM8350_OUTPUT_RIGHT_MIXER_VOLUME, + 13, 7, 0, out_mix_tlv), + SOC_SINGLE("Left Input Mixer +20dB Switch", + WM8350_INPUT_MIXER_VOLUME_L, 0, 1, 0), + SOC_SINGLE("Right Input Mixer +20dB Switch", + WM8350_INPUT_MIXER_VOLUME_R, 0, 1, 0), + SOC_SINGLE_TLV("Out4 Capture Volume", + WM8350_INPUT_MIXER_VOLUME, + 1, 7, 0, out_mix_tlv), + SOC_DOUBLE_R_EXT_TLV("Out1 Playback Volume", + WM8350_LOUT1_VOLUME, + WM8350_ROUT1_VOLUME, + 2, 63, 0, wm8350_get_volsw_2r, + wm8350_put_volsw_2r_vu, out_pga_tlv), + SOC_DOUBLE_R("Out1 Playback ZC Switch", + WM8350_LOUT1_VOLUME, + WM8350_ROUT1_VOLUME, 13, 1, 0), + SOC_DOUBLE_R_EXT_TLV("Out2 Playback Volume", + WM8350_LOUT2_VOLUME, + WM8350_ROUT2_VOLUME, + 2, 63, 0, wm8350_get_volsw_2r, + wm8350_put_volsw_2r_vu, out_pga_tlv), + SOC_DOUBLE_R("Out2 Playback ZC Switch", WM8350_LOUT2_VOLUME, + WM8350_ROUT2_VOLUME, 13, 1, 0), + SOC_SINGLE("Out2 Right Invert Switch", WM8350_ROUT2_VOLUME, 10, 1, 0), + SOC_SINGLE_TLV("Out2 Beep Volume", WM8350_BEEP_VOLUME, + 5, 7, 0, out_mix_tlv), + + SOC_DOUBLE_R("Out1 Playback Switch", + WM8350_LOUT1_VOLUME, + WM8350_ROUT1_VOLUME, + 14, 1, 1), + SOC_DOUBLE_R("Out2 Playback Switch", + WM8350_LOUT2_VOLUME, + WM8350_ROUT2_VOLUME, + 14, 1, 1), +}; + +/* + * DAPM Controls + */ + +/* Left Playback Mixer */ +static const struct snd_kcontrol_new wm8350_left_play_mixer_controls[] = { + SOC_DAPM_SINGLE("Playback Switch", + WM8350_LEFT_MIXER_CONTROL, 11, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", + WM8350_LEFT_MIXER_CONTROL, 2, 1, 0), + SOC_DAPM_SINGLE("Right Playback Switch", + WM8350_LEFT_MIXER_CONTROL, 12, 1, 0), + SOC_DAPM_SINGLE("Left Sidetone Switch", + WM8350_LEFT_MIXER_CONTROL, 0, 1, 0), + SOC_DAPM_SINGLE("Right Sidetone Switch", + WM8350_LEFT_MIXER_CONTROL, 1, 1, 0), +}; + +/* Right Playback Mixer */ +static const struct snd_kcontrol_new wm8350_right_play_mixer_controls[] = { + SOC_DAPM_SINGLE("Playback Switch", + WM8350_RIGHT_MIXER_CONTROL, 12, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", + WM8350_RIGHT_MIXER_CONTROL, 3, 1, 0), + SOC_DAPM_SINGLE("Left Playback Switch", + WM8350_RIGHT_MIXER_CONTROL, 11, 1, 0), + SOC_DAPM_SINGLE("Left Sidetone Switch", + WM8350_RIGHT_MIXER_CONTROL, 0, 1, 0), + SOC_DAPM_SINGLE("Right Sidetone Switch", + WM8350_RIGHT_MIXER_CONTROL, 1, 1, 0), +}; + +/* Out4 Mixer */ +static const struct snd_kcontrol_new wm8350_out4_mixer_controls[] = { + SOC_DAPM_SINGLE("Right Playback Switch", + WM8350_OUT4_MIXER_CONTROL, 12, 1, 0), + SOC_DAPM_SINGLE("Left Playback Switch", + WM8350_OUT4_MIXER_CONTROL, 11, 1, 0), + SOC_DAPM_SINGLE("Right Capture Switch", + WM8350_OUT4_MIXER_CONTROL, 9, 1, 0), + SOC_DAPM_SINGLE("Out3 Playback Switch", + WM8350_OUT4_MIXER_CONTROL, 2, 1, 0), + SOC_DAPM_SINGLE("Right Mixer Switch", + WM8350_OUT4_MIXER_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("Left Mixer Switch", + WM8350_OUT4_MIXER_CONTROL, 0, 1, 0), +}; + +/* Out3 Mixer */ +static const struct snd_kcontrol_new wm8350_out3_mixer_controls[] = { + SOC_DAPM_SINGLE("Left Playback Switch", + WM8350_OUT3_MIXER_CONTROL, 11, 1, 0), + SOC_DAPM_SINGLE("Left Capture Switch", + WM8350_OUT3_MIXER_CONTROL, 8, 1, 0), + SOC_DAPM_SINGLE("Out4 Playback Switch", + WM8350_OUT3_MIXER_CONTROL, 3, 1, 0), + SOC_DAPM_SINGLE("Left Mixer Switch", + WM8350_OUT3_MIXER_CONTROL, 0, 1, 0), +}; + +/* Left Input Mixer */ +static const struct snd_kcontrol_new wm8350_left_capt_mixer_controls[] = { + SOC_DAPM_SINGLE_TLV("L2 Capture Volume", + WM8350_INPUT_MIXER_VOLUME_L, 1, 7, 0, out_mix_tlv), + SOC_DAPM_SINGLE_TLV("L3 Capture Volume", + WM8350_INPUT_MIXER_VOLUME_L, 9, 7, 0, out_mix_tlv), + SOC_DAPM_SINGLE("PGA Capture Switch", + WM8350_LEFT_INPUT_VOLUME, 14, 1, 1), +}; + +/* Right Input Mixer */ +static const struct snd_kcontrol_new wm8350_right_capt_mixer_controls[] = { + SOC_DAPM_SINGLE_TLV("L2 Capture Volume", + WM8350_INPUT_MIXER_VOLUME_R, 5, 7, 0, out_mix_tlv), + SOC_DAPM_SINGLE_TLV("L3 Capture Volume", + WM8350_INPUT_MIXER_VOLUME_R, 13, 7, 0, out_mix_tlv), + SOC_DAPM_SINGLE("PGA Capture Switch", + WM8350_RIGHT_INPUT_VOLUME, 14, 1, 1), +}; + +/* Left Mic Mixer */ +static const struct snd_kcontrol_new wm8350_left_mic_mixer_controls[] = { + SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 0, 1, 0), + SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 2, 1, 0), +}; + +/* Right Mic Mixer */ +static const struct snd_kcontrol_new wm8350_right_mic_mixer_controls[] = { + SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 9, 1, 0), + SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 8, 1, 0), + SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 10, 1, 0), +}; + +/* Beep Switch */ +static const struct snd_kcontrol_new wm8350_beep_switch_controls = +SOC_DAPM_SINGLE("Switch", WM8350_BEEP_VOLUME, 15, 1, 1); + +/* Out4 Capture Mux */ +static const struct snd_kcontrol_new wm8350_out4_capture_controls = +SOC_DAPM_ENUM("Route", wm8350_enum[7]); + +static const struct snd_soc_dapm_widget wm8350_dapm_widgets[] = { + + SND_SOC_DAPM_PGA("IN3R PGA", WM8350_POWER_MGMT_2, 11, 0, NULL, 0), + SND_SOC_DAPM_PGA("IN3L PGA", WM8350_POWER_MGMT_2, 10, 0, NULL, 0), + SND_SOC_DAPM_PGA_E("Right Out2 PGA", WM8350_POWER_MGMT_3, 3, 0, NULL, + 0, pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("Left Out2 PGA", WM8350_POWER_MGMT_3, 2, 0, NULL, 0, + pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("Right Out1 PGA", WM8350_POWER_MGMT_3, 1, 0, NULL, + 0, pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("Left Out1 PGA", WM8350_POWER_MGMT_3, 0, 0, NULL, 0, + pga_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MIXER("Right Capture Mixer", WM8350_POWER_MGMT_2, + 7, 0, &wm8350_right_capt_mixer_controls[0], + ARRAY_SIZE(wm8350_right_capt_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Capture Mixer", WM8350_POWER_MGMT_2, + 6, 0, &wm8350_left_capt_mixer_controls[0], + ARRAY_SIZE(wm8350_left_capt_mixer_controls)), + + SND_SOC_DAPM_MIXER("Out4 Mixer", WM8350_POWER_MGMT_2, 5, 0, + &wm8350_out4_mixer_controls[0], + ARRAY_SIZE(wm8350_out4_mixer_controls)), + + SND_SOC_DAPM_MIXER("Out3 Mixer", WM8350_POWER_MGMT_2, 4, 0, + &wm8350_out3_mixer_controls[0], + ARRAY_SIZE(wm8350_out3_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Playback Mixer", WM8350_POWER_MGMT_2, 1, 0, + &wm8350_right_play_mixer_controls[0], + ARRAY_SIZE(wm8350_right_play_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Playback Mixer", WM8350_POWER_MGMT_2, 0, 0, + &wm8350_left_play_mixer_controls[0], + ARRAY_SIZE(wm8350_left_play_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Mic Mixer", WM8350_POWER_MGMT_2, 8, 0, + &wm8350_left_mic_mixer_controls[0], + ARRAY_SIZE(wm8350_left_mic_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Mic Mixer", WM8350_POWER_MGMT_2, 9, 0, + &wm8350_right_mic_mixer_controls[0], + ARRAY_SIZE(wm8350_right_mic_mixer_controls)), + + /* virtual mixer for Beep and Out2R */ + SND_SOC_DAPM_MIXER("Out2 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("Beep", WM8350_POWER_MGMT_3, 7, 0, + &wm8350_beep_switch_controls), + + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", + WM8350_POWER_MGMT_4, 3, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", + WM8350_POWER_MGMT_4, 2, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", + WM8350_POWER_MGMT_4, 5, 0), + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", + WM8350_POWER_MGMT_4, 4, 0), + + SND_SOC_DAPM_MICBIAS("Mic Bias", WM8350_POWER_MGMT_1, 4, 0), + + SND_SOC_DAPM_MUX("Out4 Capture Channel", SND_SOC_NOPM, 0, 0, + &wm8350_out4_capture_controls), + + SND_SOC_DAPM_OUTPUT("OUT1R"), + SND_SOC_DAPM_OUTPUT("OUT1L"), + SND_SOC_DAPM_OUTPUT("OUT2R"), + SND_SOC_DAPM_OUTPUT("OUT2L"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_OUTPUT("OUT4"), + + SND_SOC_DAPM_INPUT("IN1RN"), + SND_SOC_DAPM_INPUT("IN1RP"), + SND_SOC_DAPM_INPUT("IN2R"), + SND_SOC_DAPM_INPUT("IN1LP"), + SND_SOC_DAPM_INPUT("IN1LN"), + SND_SOC_DAPM_INPUT("IN2L"), + SND_SOC_DAPM_INPUT("IN3R"), + SND_SOC_DAPM_INPUT("IN3L"), +}; + +static const struct snd_soc_dapm_route wm8350_dapm_routes[] = { + + /* left playback mixer */ + {"Left Playback Mixer", "Playback Switch", "Left DAC"}, + {"Left Playback Mixer", "Left Bypass Switch", "IN3L PGA"}, + {"Left Playback Mixer", "Right Playback Switch", "Right DAC"}, + {"Left Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"}, + {"Left Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"}, + + /* right playback mixer */ + {"Right Playback Mixer", "Playback Switch", "Right DAC"}, + {"Right Playback Mixer", "Right Bypass Switch", "IN3R PGA"}, + {"Right Playback Mixer", "Left Playback Switch", "Left DAC"}, + {"Right Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"}, + {"Right Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"}, + + /* out4 playback mixer */ + {"Out4 Mixer", "Right Playback Switch", "Right DAC"}, + {"Out4 Mixer", "Left Playback Switch", "Left DAC"}, + {"Out4 Mixer", "Right Capture Switch", "Right Capture Mixer"}, + {"Out4 Mixer", "Out3 Playback Switch", "Out3 Mixer"}, + {"Out4 Mixer", "Right Mixer Switch", "Right Playback Mixer"}, + {"Out4 Mixer", "Left Mixer Switch", "Left Playback Mixer"}, + {"OUT4", NULL, "Out4 Mixer"}, + + /* out3 playback mixer */ + {"Out3 Mixer", "Left Playback Switch", "Left DAC"}, + {"Out3 Mixer", "Left Capture Switch", "Left Capture Mixer"}, + {"Out3 Mixer", "Left Mixer Switch", "Left Playback Mixer"}, + {"Out3 Mixer", "Out4 Playback Switch", "Out4 Mixer"}, + {"OUT3", NULL, "Out3 Mixer"}, + + /* out2 */ + {"Right Out2 PGA", NULL, "Right Playback Mixer"}, + {"Left Out2 PGA", NULL, "Left Playback Mixer"}, + {"OUT2L", NULL, "Left Out2 PGA"}, + {"OUT2R", NULL, "Right Out2 PGA"}, + + /* out1 */ + {"Right Out1 PGA", NULL, "Right Playback Mixer"}, + {"Left Out1 PGA", NULL, "Left Playback Mixer"}, + {"OUT1L", NULL, "Left Out1 PGA"}, + {"OUT1R", NULL, "Right Out1 PGA"}, + + /* ADCs */ + {"Left ADC", NULL, "Left Capture Mixer"}, + {"Right ADC", NULL, "Right Capture Mixer"}, + + /* Left capture mixer */ + {"Left Capture Mixer", "L2 Capture Volume", "IN2L"}, + {"Left Capture Mixer", "L3 Capture Volume", "IN3L PGA"}, + {"Left Capture Mixer", "PGA Capture Switch", "Left Mic Mixer"}, + {"Left Capture Mixer", NULL, "Out4 Capture Channel"}, + + /* Right capture mixer */ + {"Right Capture Mixer", "L2 Capture Volume", "IN2R"}, + {"Right Capture Mixer", "L3 Capture Volume", "IN3R PGA"}, + {"Right Capture Mixer", "PGA Capture Switch", "Right Mic Mixer"}, + {"Right Capture Mixer", NULL, "Out4 Capture Channel"}, + + /* L3 Inputs */ + {"IN3L PGA", NULL, "IN3L"}, + {"IN3R PGA", NULL, "IN3R"}, + + /* Left Mic mixer */ + {"Left Mic Mixer", "INN Capture Switch", "IN1LN"}, + {"Left Mic Mixer", "INP Capture Switch", "IN1LP"}, + {"Left Mic Mixer", "IN2 Capture Switch", "IN2L"}, + + /* Right Mic mixer */ + {"Right Mic Mixer", "INN Capture Switch", "IN1RN"}, + {"Right Mic Mixer", "INP Capture Switch", "IN1RP"}, + {"Right Mic Mixer", "IN2 Capture Switch", "IN2R"}, + + /* out 4 capture */ + {"Out4 Capture Channel", NULL, "Out4 Mixer"}, + + /* Beep */ + {"Beep", NULL, "IN3R PGA"}, +}; + +static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8350_data *wm8350_data = snd_soc_component_get_drvdata(component); + struct wm8350 *wm8350 = wm8350_data->wm8350; + u16 fll_4; + + switch (clk_id) { + case WM8350_MCLK_SEL_MCLK: + wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_1, + WM8350_MCLK_SEL); + break; + case WM8350_MCLK_SEL_PLL_MCLK: + case WM8350_MCLK_SEL_PLL_DAC: + case WM8350_MCLK_SEL_PLL_ADC: + case WM8350_MCLK_SEL_PLL_32K: + wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1, + WM8350_MCLK_SEL); + fll_4 = snd_soc_component_read(component, WM8350_FLL_CONTROL_4) & + ~WM8350_FLL_CLK_SRC_MASK; + snd_soc_component_write(component, WM8350_FLL_CONTROL_4, fll_4 | clk_id); + break; + } + + /* MCLK direction */ + if (dir == SND_SOC_CLOCK_OUT) + wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2, + WM8350_MCLK_DIR); + else + wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2, + WM8350_MCLK_DIR); + + return 0; +} + +static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + u16 val; + + switch (div_id) { + case WM8350_ADC_CLKDIV: + val = snd_soc_component_read(component, WM8350_ADC_DIVIDER) & + ~WM8350_ADC_CLKDIV_MASK; + snd_soc_component_write(component, WM8350_ADC_DIVIDER, val | div); + break; + case WM8350_DAC_CLKDIV: + val = snd_soc_component_read(component, WM8350_DAC_CLOCK_CONTROL) & + ~WM8350_DAC_CLKDIV_MASK; + snd_soc_component_write(component, WM8350_DAC_CLOCK_CONTROL, val | div); + break; + case WM8350_BCLK_CLKDIV: + val = snd_soc_component_read(component, WM8350_CLOCK_CONTROL_1) & + ~WM8350_BCLK_DIV_MASK; + snd_soc_component_write(component, WM8350_CLOCK_CONTROL_1, val | div); + break; + case WM8350_OPCLK_CLKDIV: + val = snd_soc_component_read(component, WM8350_CLOCK_CONTROL_1) & + ~WM8350_OPCLK_DIV_MASK; + snd_soc_component_write(component, WM8350_CLOCK_CONTROL_1, val | div); + break; + case WM8350_SYS_CLKDIV: + val = snd_soc_component_read(component, WM8350_CLOCK_CONTROL_1) & + ~WM8350_MCLK_DIV_MASK; + snd_soc_component_write(component, WM8350_CLOCK_CONTROL_1, val | div); + break; + case WM8350_DACLR_CLKDIV: + val = snd_soc_component_read(component, WM8350_DAC_LR_RATE) & + ~WM8350_DACLRC_RATE_MASK; + snd_soc_component_write(component, WM8350_DAC_LR_RATE, val | div); + break; + case WM8350_ADCLR_CLKDIV: + val = snd_soc_component_read(component, WM8350_ADC_LR_RATE) & + ~WM8350_ADCLRC_RATE_MASK; + snd_soc_component_write(component, WM8350_ADC_LR_RATE, val | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = snd_soc_component_read(component, WM8350_AI_FORMATING) & + ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK); + u16 master = snd_soc_component_read(component, WM8350_AI_DAC_CONTROL) & + ~WM8350_BCLK_MSTR; + u16 dac_lrc = snd_soc_component_read(component, WM8350_DAC_LR_RATE) & + ~WM8350_DACLRC_ENA; + u16 adc_lrc = snd_soc_component_read(component, WM8350_ADC_LR_RATE) & + ~WM8350_ADCLRC_ENA; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + master |= WM8350_BCLK_MSTR; + dac_lrc |= WM8350_DACLRC_ENA; + adc_lrc |= WM8350_ADCLRC_ENA; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x2 << 8; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x1 << 8; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x3 << 8; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x3 << 8 | WM8350_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= WM8350_AIF_LRCLK_INV | WM8350_AIF_BCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= WM8350_AIF_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= WM8350_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8350_AI_FORMATING, iface); + snd_soc_component_write(component, WM8350_AI_DAC_CONTROL, master); + snd_soc_component_write(component, WM8350_DAC_LR_RATE, dac_lrc); + snd_soc_component_write(component, WM8350_ADC_LR_RATE, adc_lrc); + return 0; +} + +static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *codec_dai) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8350_data *wm8350_data = snd_soc_component_get_drvdata(component); + struct wm8350 *wm8350 = wm8350_data->wm8350; + u16 iface = snd_soc_component_read(component, WM8350_AI_FORMATING) & + ~WM8350_AIF_WL_MASK; + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x1 << 10; + break; + case 24: + iface |= 0x2 << 10; + break; + case 32: + iface |= 0x3 << 10; + break; + } + + snd_soc_component_write(component, WM8350_AI_FORMATING, iface); + + /* The sloping stopband filter is recommended for use with + * lower sample rates to improve performance. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (params_rate(params) < 24000) + wm8350_set_bits(wm8350, WM8350_DAC_MUTE_VOLUME, + WM8350_DAC_SB_FILT); + else + wm8350_clear_bits(wm8350, WM8350_DAC_MUTE_VOLUME, + WM8350_DAC_SB_FILT); + } + + return 0; +} + +static int wm8350_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + unsigned int val; + + if (mute) + val = WM8350_DAC_MUTE_ENA; + else + val = 0; + + snd_soc_component_update_bits(component, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA, val); + + return 0; +} + +/* FLL divisors */ +struct _fll_div { + int div; /* FLL_OUTDIV */ + int n; + int k; + int ratio; /* FLL_FRATIO */ +}; + +/* The size in bits of the fll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +static inline int fll_factors(struct _fll_div *fll_div, unsigned int input, + unsigned int output) +{ + u64 Kpart; + unsigned int t1, t2, K, Nmod; + + if (output >= 2815250 && output <= 3125000) + fll_div->div = 0x4; + else if (output >= 5625000 && output <= 6250000) + fll_div->div = 0x3; + else if (output >= 11250000 && output <= 12500000) + fll_div->div = 0x2; + else if (output >= 22500000 && output <= 25000000) + fll_div->div = 0x1; + else { + printk(KERN_ERR "wm8350: fll freq %d out of range\n", output); + return -EINVAL; + } + + if (input > 48000) + fll_div->ratio = 1; + else + fll_div->ratio = 8; + + t1 = output * (1 << (fll_div->div + 1)); + t2 = input * fll_div->ratio; + + fll_div->n = t1 / t2; + Nmod = t1 % t2; + + if (Nmod) { + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + do_div(Kpart, t2); + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + fll_div->k = K; + } else + fll_div->k = 0; + + return 0; +} + +static int wm8350_set_fll(struct snd_soc_dai *codec_dai, + int pll_id, int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8350_data *priv = snd_soc_component_get_drvdata(component); + struct wm8350 *wm8350 = priv->wm8350; + struct _fll_div fll_div; + int ret = 0; + u16 fll_1, fll_4; + + if (freq_in == priv->fll_freq_in && freq_out == priv->fll_freq_out) + return 0; + + /* power down FLL - we need to do this for reconfiguration */ + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, + WM8350_FLL_ENA | WM8350_FLL_OSC_ENA); + + if (freq_out == 0 || freq_in == 0) + return ret; + + ret = fll_factors(&fll_div, freq_in, freq_out); + if (ret < 0) + return ret; + dev_dbg(wm8350->dev, + "FLL in %u FLL out %u N 0x%x K 0x%x div %d ratio %d", + freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div, + fll_div.ratio); + + /* set up N.K & dividers */ + fll_1 = snd_soc_component_read(component, WM8350_FLL_CONTROL_1) & + ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000); + snd_soc_component_write(component, WM8350_FLL_CONTROL_1, + fll_1 | (fll_div.div << 8) | 0x50); + snd_soc_component_write(component, WM8350_FLL_CONTROL_2, + (fll_div.ratio << 11) | (fll_div. + n & WM8350_FLL_N_MASK)); + snd_soc_component_write(component, WM8350_FLL_CONTROL_3, fll_div.k); + fll_4 = snd_soc_component_read(component, WM8350_FLL_CONTROL_4) & + ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF); + snd_soc_component_write(component, WM8350_FLL_CONTROL_4, + fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) | + (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0)); + + /* power FLL on */ + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA); + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA); + + priv->fll_freq_out = freq_out; + priv->fll_freq_in = freq_in; + + return 0; +} + +static int wm8350_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8350_data *priv = snd_soc_component_get_drvdata(component); + struct wm8350 *wm8350 = priv->wm8350; + struct wm8350_audio_platform_data *platform = + wm8350->codec.platform_data; + u16 pm1; + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & + ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK); + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, + pm1 | WM8350_VMID_50K | + platform->codec_current_on << 14); + break; + + case SND_SOC_BIAS_PREPARE: + pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1); + pm1 &= ~WM8350_VMID_MASK; + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, + pm1 | WM8350_VMID_50K); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret != 0) + return ret; + + /* Enable the system clock */ + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, + WM8350_SYSCLK_ENA); + + /* mute DAC & outputs */ + wm8350_set_bits(wm8350, WM8350_DAC_MUTE, + WM8350_DAC_MUTE_ENA); + + /* discharge cap memory */ + wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, + platform->dis_out1 | + (platform->dis_out2 << 2) | + (platform->dis_out3 << 4) | + (platform->dis_out4 << 6)); + + /* wait for discharge */ + schedule_timeout_interruptible(msecs_to_jiffies + (platform-> + cap_discharge_msecs)); + + /* enable antipop */ + wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, + (platform->vmid_s_curve << 8)); + + /* ramp up vmid */ + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, + (platform-> + codec_current_charge << 14) | + WM8350_VMID_5K | WM8350_VMIDEN | + WM8350_VBUFEN); + + /* wait for vmid */ + schedule_timeout_interruptible(msecs_to_jiffies + (platform-> + vmid_charge_msecs)); + + /* turn on vmid 300k */ + pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & + ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK); + pm1 |= WM8350_VMID_300K | + (platform->codec_current_standby << 14); + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, + pm1); + + + /* enable analogue bias */ + pm1 |= WM8350_BIASEN; + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1); + + /* disable antipop */ + wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0); + + } else { + /* turn on vmid 300k and reduce current */ + pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & + ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK); + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, + pm1 | WM8350_VMID_300K | + (platform-> + codec_current_standby << 14)); + + } + break; + + case SND_SOC_BIAS_OFF: + + /* mute DAC & enable outputs */ + wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA); + + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_3, + WM8350_OUT1L_ENA | WM8350_OUT1R_ENA | + WM8350_OUT2L_ENA | WM8350_OUT2R_ENA); + + /* enable anti pop S curve */ + wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, + (platform->vmid_s_curve << 8)); + + /* turn off vmid */ + pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & + ~WM8350_VMIDEN; + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1); + + /* wait */ + schedule_timeout_interruptible(msecs_to_jiffies + (platform-> + vmid_discharge_msecs)); + + wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, + (platform->vmid_s_curve << 8) | + platform->dis_out1 | + (platform->dis_out2 << 2) | + (platform->dis_out3 << 4) | + (platform->dis_out4 << 6)); + + /* turn off VBuf and drain */ + pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) & + ~(WM8350_VBUFEN | WM8350_VMID_MASK); + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, + pm1 | WM8350_OUTPUT_DRAIN_EN); + + /* wait */ + schedule_timeout_interruptible(msecs_to_jiffies + (platform->drain_msecs)); + + pm1 &= ~WM8350_BIASEN; + wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1); + + /* disable anti-pop */ + wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0); + + wm8350_clear_bits(wm8350, WM8350_LOUT1_VOLUME, + WM8350_OUT1L_ENA); + wm8350_clear_bits(wm8350, WM8350_ROUT1_VOLUME, + WM8350_OUT1R_ENA); + wm8350_clear_bits(wm8350, WM8350_LOUT2_VOLUME, + WM8350_OUT2L_ENA); + wm8350_clear_bits(wm8350, WM8350_ROUT2_VOLUME, + WM8350_OUT2R_ENA); + + /* disable clock gen */ + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, + WM8350_SYSCLK_ENA); + + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), + priv->supplies); + break; + } + return 0; +} + +static void wm8350_hp_work(struct wm8350_data *priv, + struct wm8350_jack_data *jack, + u16 mask) +{ + struct wm8350 *wm8350 = priv->wm8350; + u16 reg; + int report; + + reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS); + if (reg & mask) + report = jack->report; + else + report = 0; + + snd_soc_jack_report(jack->jack, report, jack->report); + +} + +static void wm8350_hpl_work(struct work_struct *work) +{ + struct wm8350_data *priv = + container_of(work, struct wm8350_data, hpl.work.work); + + wm8350_hp_work(priv, &priv->hpl, WM8350_JACK_L_LVL); +} + +static void wm8350_hpr_work(struct work_struct *work) +{ + struct wm8350_data *priv = + container_of(work, struct wm8350_data, hpr.work.work); + + wm8350_hp_work(priv, &priv->hpr, WM8350_JACK_R_LVL); +} + +static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data) +{ + struct wm8350_data *priv = data; + struct wm8350 *wm8350 = priv->wm8350; + +#ifndef CONFIG_SND_SOC_WM8350_MODULE + trace_snd_soc_jack_irq("WM8350 HPL"); +#endif + + if (device_may_wakeup(wm8350->dev)) + pm_wakeup_event(wm8350->dev, 250); + + queue_delayed_work(system_power_efficient_wq, + &priv->hpl.work, msecs_to_jiffies(200)); + + return IRQ_HANDLED; +} + +static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data) +{ + struct wm8350_data *priv = data; + struct wm8350 *wm8350 = priv->wm8350; + +#ifndef CONFIG_SND_SOC_WM8350_MODULE + trace_snd_soc_jack_irq("WM8350 HPR"); +#endif + + if (device_may_wakeup(wm8350->dev)) + pm_wakeup_event(wm8350->dev, 250); + + queue_delayed_work(system_power_efficient_wq, + &priv->hpr.work, msecs_to_jiffies(200)); + + return IRQ_HANDLED; +} + +/** + * wm8350_hp_jack_detect - Enable headphone jack detection. + * + * @component: WM8350 component + * @which: left or right jack detect signal + * @jack: jack to report detection events on + * @report: value to report + * + * Enables the headphone jack detection of the WM8350. If no report + * is specified then detection is disabled. + */ +int wm8350_hp_jack_detect(struct snd_soc_component *component, enum wm8350_jack which, + struct snd_soc_jack *jack, int report) +{ + struct wm8350_data *priv = snd_soc_component_get_drvdata(component); + struct wm8350 *wm8350 = priv->wm8350; + int ena; + + switch (which) { + case WM8350_JDL: + priv->hpl.jack = jack; + priv->hpl.report = report; + ena = WM8350_JDL_ENA; + break; + + case WM8350_JDR: + priv->hpr.jack = jack; + priv->hpr.report = report; + ena = WM8350_JDR_ENA; + break; + + default: + return -EINVAL; + } + + if (report) { + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA); + wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena); + } else { + wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, ena); + } + + /* Sync status */ + switch (which) { + case WM8350_JDL: + wm8350_hpl_jack_handler(0, priv); + break; + case WM8350_JDR: + wm8350_hpr_jack_handler(0, priv); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_hp_jack_detect); + +static irqreturn_t wm8350_mic_handler(int irq, void *data) +{ + struct wm8350_data *priv = data; + struct wm8350 *wm8350 = priv->wm8350; + u16 reg; + int report = 0; + +#ifndef CONFIG_SND_SOC_WM8350_MODULE + trace_snd_soc_jack_irq("WM8350 mic"); +#endif + + reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS); + if (reg & WM8350_JACK_MICSCD_LVL) + report |= priv->mic.short_report; + if (reg & WM8350_JACK_MICSD_LVL) + report |= priv->mic.report; + + snd_soc_jack_report(priv->mic.jack, report, + priv->mic.report | priv->mic.short_report); + + return IRQ_HANDLED; +} + +/** + * wm8350_mic_jack_detect - Enable microphone jack detection. + * + * @component: WM8350 component + * @jack: jack to report detection events on + * @detect_report: value to report when presence detected + * @short_report: value to report when microphone short detected + * + * Enables the microphone jack detection of the WM8350. If both reports + * are specified as zero then detection is disabled. + */ +int wm8350_mic_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, + int detect_report, int short_report) +{ + struct wm8350_data *priv = snd_soc_component_get_drvdata(component); + struct wm8350 *wm8350 = priv->wm8350; + + priv->mic.jack = jack; + priv->mic.report = detect_report; + priv->mic.short_report = short_report; + + if (detect_report || short_report) { + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA); + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_1, + WM8350_MIC_DET_ENA); + } else { + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_1, + WM8350_MIC_DET_ENA); + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_mic_jack_detect); + +#define WM8350_RATES (SNDRV_PCM_RATE_8000_96000) + +#define WM8350_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8350_dai_ops = { + .hw_params = wm8350_pcm_hw_params, + .mute_stream = wm8350_mute, + .set_fmt = wm8350_set_dai_fmt, + .set_sysclk = wm8350_set_dai_sysclk, + .set_pll = wm8350_set_fll, + .set_clkdiv = wm8350_set_clkdiv, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8350_dai = { + .name = "wm8350-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8350_RATES, + .formats = WM8350_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8350_RATES, + .formats = WM8350_FORMATS, + }, + .ops = &wm8350_dai_ops, +}; + +static int wm8350_component_probe(struct snd_soc_component *component) +{ + struct wm8350 *wm8350 = dev_get_platdata(component->dev); + struct wm8350_data *priv; + struct wm8350_output *out1; + struct wm8350_output *out2; + int ret, i; + + if (wm8350->codec.platform_data == NULL) { + dev_err(component->dev, "No audio platform data supplied\n"); + return -EINVAL; + } + + priv = devm_kzalloc(component->dev, sizeof(struct wm8350_data), + GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + snd_soc_component_init_regmap(component, wm8350->regmap); + snd_soc_component_set_drvdata(component, priv); + + priv->wm8350 = wm8350; + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + priv->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(wm8350->dev, ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret != 0) + return ret; + + /* Put the codec into reset if it wasn't already */ + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); + + INIT_DELAYED_WORK(&priv->pga_work, wm8350_pga_work); + INIT_DELAYED_WORK(&priv->hpl.work, wm8350_hpl_work); + INIT_DELAYED_WORK(&priv->hpr.work, wm8350_hpr_work); + + /* Enable the codec */ + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); + + /* Enable robust clocking mode in ADC */ + snd_soc_component_write(component, WM8350_SECURITY, 0xa7); + snd_soc_component_write(component, 0xde, 0x13); + snd_soc_component_write(component, WM8350_SECURITY, 0); + + /* read OUT1 & OUT2 volumes */ + out1 = &priv->out1; + out2 = &priv->out2; + out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) & + WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; + out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) & + WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; + out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) & + WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT; + out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) & + WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT; + wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0); + wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0); + wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0); + wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0); + + /* Latch VU bits & mute */ + wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, + WM8350_OUT1_VU | WM8350_OUT1L_MUTE); + wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, + WM8350_OUT2_VU | WM8350_OUT2L_MUTE); + wm8350_set_bits(wm8350, WM8350_ROUT1_VOLUME, + WM8350_OUT1_VU | WM8350_OUT1R_MUTE); + wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME, + WM8350_OUT2_VU | WM8350_OUT2R_MUTE); + + /* Make sure AIF tristating is disabled by default */ + wm8350_clear_bits(wm8350, WM8350_AI_FORMATING, WM8350_AIF_TRI); + + /* Make sure we've got a sane companding setup too */ + wm8350_clear_bits(wm8350, WM8350_ADC_DAC_COMP, + WM8350_DAC_COMP | WM8350_LOOPBACK); + + /* Make sure jack detect is disabled to start off with */ + wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, + WM8350_JDL_ENA | WM8350_JDR_ENA); + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, + wm8350_hpl_jack_handler, 0, "Left jack detect", + priv); + if (ret != 0) + goto err; + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, + wm8350_hpr_jack_handler, 0, "Right jack detect", + priv); + if (ret != 0) + goto free_jck_det_l; + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, + wm8350_mic_handler, 0, "Microphone short", priv); + if (ret != 0) + goto free_jck_det_r; + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD, + wm8350_mic_handler, 0, "Microphone detect", priv); + if (ret != 0) + goto free_micscd; + + return 0; + +free_micscd: + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, priv); +free_jck_det_r: + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv); +free_jck_det_l: + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv); +err: + return ret; +} + +static void wm8350_component_remove(struct snd_soc_component *component) +{ + struct wm8350_data *priv = snd_soc_component_get_drvdata(component); + struct wm8350 *wm8350 = dev_get_platdata(component->dev); + + wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, + WM8350_JDL_ENA | WM8350_JDR_ENA); + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA); + + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICD, priv); + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, priv); + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv); + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv); + + priv->hpl.jack = NULL; + priv->hpr.jack = NULL; + priv->mic.jack = NULL; + + cancel_delayed_work_sync(&priv->hpl.work); + cancel_delayed_work_sync(&priv->hpr.work); + + /* if there was any work waiting then we run it now and + * wait for its completion */ + flush_delayed_work(&priv->pga_work); + + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); +} + +static const struct snd_soc_component_driver soc_component_dev_wm8350 = { + .probe = wm8350_component_probe, + .remove = wm8350_component_remove, + .set_bias_level = wm8350_set_bias_level, + .controls = wm8350_snd_controls, + .num_controls = ARRAY_SIZE(wm8350_snd_controls), + .dapm_widgets = wm8350_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8350_dapm_widgets), + .dapm_routes = wm8350_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8350_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm8350_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm8350, + &wm8350_dai, 1); +} + +static struct platform_driver wm8350_codec_driver = { + .driver = { + .name = "wm8350-codec", + }, + .probe = wm8350_probe, +}; + +module_platform_driver(wm8350_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8350 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8350-codec"); diff --git a/sound/soc/codecs/wm8350.h b/sound/soc/codecs/wm8350.h new file mode 100644 index 0000000000..a13d18c92d --- /dev/null +++ b/sound/soc/codecs/wm8350.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * wm8350.h - WM8903 audio codec interface + * + * Copyright 2008 Wolfson Microelectronics PLC. + */ + +#ifndef _WM8350_H +#define _WM8350_H + +#include +#include + +enum wm8350_jack { + WM8350_JDL = 1, + WM8350_JDR = 2, +}; + +int wm8350_hp_jack_detect(struct snd_soc_component *component, enum wm8350_jack which, + struct snd_soc_jack *jack, int report); +int wm8350_mic_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, + int detect_report, int short_report); + +#endif diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c new file mode 100644 index 0000000000..19ce839f6e --- /dev/null +++ b/sound/soc/codecs/wm8400.c @@ -0,0 +1,1346 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * wm8400.c -- WM8400 ALSA Soc Audio driver + * + * Copyright 2008-11 Wolfson Microelectronics PLC. + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8400.h" + +static struct regulator_bulk_data power[] = { + { + .supply = "I2S1VDD", + }, + { + .supply = "I2S2VDD", + }, + { + .supply = "DCVDD", + }, + { + .supply = "AVDD", + }, + { + .supply = "FLLVDD", + }, + { + .supply = "HPVDD", + }, + { + .supply = "SPKVDD", + }, +}; + +/* codec private data */ +struct wm8400_priv { + struct wm8400 *wm8400; + u16 fake_register; + unsigned int sysclk; + unsigned int pcmclk; + int fll_in, fll_out; +}; + +static void wm8400_component_reset(struct snd_soc_component *component) +{ + struct wm8400_priv *wm8400 = snd_soc_component_get_drvdata(component); + + wm8400_reset_codec_reg_cache(wm8400->wm8400); +} + +static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1650, 3000, 0); + +static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -2100, 0, 0); + +static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -7300, 600, 0); + +static const DECLARE_TLV_DB_SCALE(out_dac_tlv, -7163, 0, 0); + +static const DECLARE_TLV_DB_SCALE(in_adc_tlv, -7163, 1763, 0); + +static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0); + +static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int reg = mc->reg; + int ret; + u16 val; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + /* now hit the volume update bits (always bit 8) */ + val = snd_soc_component_read(component, reg); + return snd_soc_component_write(component, reg, val | 0x0100); +} + +#define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \ + SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \ + snd_soc_get_volsw, wm8400_outpga_put_volsw_vu, tlv_array) + + +static const char *wm8400_digital_sidetone[] = + {"None", "Left ADC", "Right ADC", "Reserved"}; + +static SOC_ENUM_SINGLE_DECL(wm8400_left_digital_sidetone_enum, + WM8400_DIGITAL_SIDE_TONE, + WM8400_ADC_TO_DACL_SHIFT, + wm8400_digital_sidetone); + +static SOC_ENUM_SINGLE_DECL(wm8400_right_digital_sidetone_enum, + WM8400_DIGITAL_SIDE_TONE, + WM8400_ADC_TO_DACR_SHIFT, + wm8400_digital_sidetone); + +static const char *wm8400_adcmode[] = + {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"}; + +static SOC_ENUM_SINGLE_DECL(wm8400_right_adcmode_enum, + WM8400_ADC_CTRL, + WM8400_ADC_HPF_CUT_SHIFT, + wm8400_adcmode); + +static const struct snd_kcontrol_new wm8400_snd_controls[] = { +/* INMIXL */ +SOC_SINGLE("LIN12 PGA Boost", WM8400_INPUT_MIXER3, WM8400_L12MNBST_SHIFT, + 1, 0), +SOC_SINGLE("LIN34 PGA Boost", WM8400_INPUT_MIXER3, WM8400_L34MNBST_SHIFT, + 1, 0), +/* INMIXR */ +SOC_SINGLE("RIN12 PGA Boost", WM8400_INPUT_MIXER3, WM8400_R12MNBST_SHIFT, + 1, 0), +SOC_SINGLE("RIN34 PGA Boost", WM8400_INPUT_MIXER3, WM8400_R34MNBST_SHIFT, + 1, 0), + +/* LOMIX */ +SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8400_OUTPUT_MIXER3, + WM8400_LLI3LOVOL_SHIFT, 7, 0, out_mix_tlv), +SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER3, + WM8400_LR12LOVOL_SHIFT, 7, 0, out_mix_tlv), +SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER3, + WM8400_LL12LOVOL_SHIFT, 7, 0, out_mix_tlv), +SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8400_OUTPUT_MIXER5, + WM8400_LRI3LOVOL_SHIFT, 7, 0, out_mix_tlv), +SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8400_OUTPUT_MIXER5, + WM8400_LRBLOVOL_SHIFT, 7, 0, out_mix_tlv), +SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8400_OUTPUT_MIXER5, + WM8400_LRBLOVOL_SHIFT, 7, 0, out_mix_tlv), + +/* ROMIX */ +SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8400_OUTPUT_MIXER4, + WM8400_RRI3ROVOL_SHIFT, 7, 0, out_mix_tlv), +SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER4, + WM8400_RL12ROVOL_SHIFT, 7, 0, out_mix_tlv), +SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER4, + WM8400_RR12ROVOL_SHIFT, 7, 0, out_mix_tlv), +SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8400_OUTPUT_MIXER6, + WM8400_RLI3ROVOL_SHIFT, 7, 0, out_mix_tlv), +SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8400_OUTPUT_MIXER6, + WM8400_RLBROVOL_SHIFT, 7, 0, out_mix_tlv), +SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8400_OUTPUT_MIXER6, + WM8400_RRBROVOL_SHIFT, 7, 0, out_mix_tlv), + +/* LOUT */ +WM8400_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8400_LEFT_OUTPUT_VOLUME, + WM8400_LOUTVOL_SHIFT, WM8400_LOUTVOL_MASK, 0, out_pga_tlv), +SOC_SINGLE("LOUT ZC", WM8400_LEFT_OUTPUT_VOLUME, WM8400_LOZC_SHIFT, 1, 0), + +/* ROUT */ +WM8400_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8400_RIGHT_OUTPUT_VOLUME, + WM8400_ROUTVOL_SHIFT, WM8400_ROUTVOL_MASK, 0, out_pga_tlv), +SOC_SINGLE("ROUT ZC", WM8400_RIGHT_OUTPUT_VOLUME, WM8400_ROZC_SHIFT, 1, 0), + +/* LOPGA */ +WM8400_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8400_LEFT_OPGA_VOLUME, + WM8400_LOPGAVOL_SHIFT, WM8400_LOPGAVOL_MASK, 0, out_pga_tlv), +SOC_SINGLE("LOPGA ZC Switch", WM8400_LEFT_OPGA_VOLUME, + WM8400_LOPGAZC_SHIFT, 1, 0), + +/* ROPGA */ +WM8400_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8400_RIGHT_OPGA_VOLUME, + WM8400_ROPGAVOL_SHIFT, WM8400_ROPGAVOL_MASK, 0, out_pga_tlv), +SOC_SINGLE("ROPGA ZC Switch", WM8400_RIGHT_OPGA_VOLUME, + WM8400_ROPGAZC_SHIFT, 1, 0), + +SOC_SINGLE("LON Mute Switch", WM8400_LINE_OUTPUTS_VOLUME, + WM8400_LONMUTE_SHIFT, 1, 0), +SOC_SINGLE("LOP Mute Switch", WM8400_LINE_OUTPUTS_VOLUME, + WM8400_LOPMUTE_SHIFT, 1, 0), +SOC_SINGLE("LOP Attenuation Switch", WM8400_LINE_OUTPUTS_VOLUME, + WM8400_LOATTN_SHIFT, 1, 0), +SOC_SINGLE("RON Mute Switch", WM8400_LINE_OUTPUTS_VOLUME, + WM8400_RONMUTE_SHIFT, 1, 0), +SOC_SINGLE("ROP Mute Switch", WM8400_LINE_OUTPUTS_VOLUME, + WM8400_ROPMUTE_SHIFT, 1, 0), +SOC_SINGLE("ROP Attenuation Switch", WM8400_LINE_OUTPUTS_VOLUME, + WM8400_ROATTN_SHIFT, 1, 0), + +SOC_SINGLE("OUT3 Mute Switch", WM8400_OUT3_4_VOLUME, + WM8400_OUT3MUTE_SHIFT, 1, 0), +SOC_SINGLE("OUT3 Attenuation Switch", WM8400_OUT3_4_VOLUME, + WM8400_OUT3ATTN_SHIFT, 1, 0), + +SOC_SINGLE("OUT4 Mute Switch", WM8400_OUT3_4_VOLUME, + WM8400_OUT4MUTE_SHIFT, 1, 0), +SOC_SINGLE("OUT4 Attenuation Switch", WM8400_OUT3_4_VOLUME, + WM8400_OUT4ATTN_SHIFT, 1, 0), + +SOC_SINGLE("Speaker Mode Switch", WM8400_CLASSD1, + WM8400_CDMODE_SHIFT, 1, 0), + +SOC_SINGLE("Speaker Output Attenuation Volume", WM8400_SPEAKER_VOLUME, + WM8400_SPKATTN_SHIFT, WM8400_SPKATTN_MASK, 0), +SOC_SINGLE("Speaker DC Boost Volume", WM8400_CLASSD3, + WM8400_DCGAIN_SHIFT, 6, 0), +SOC_SINGLE("Speaker AC Boost Volume", WM8400_CLASSD3, + WM8400_ACGAIN_SHIFT, 6, 0), + +WM8400_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume", + WM8400_LEFT_DAC_DIGITAL_VOLUME, WM8400_DACL_VOL_SHIFT, + 127, 0, out_dac_tlv), + +WM8400_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume", + WM8400_RIGHT_DAC_DIGITAL_VOLUME, WM8400_DACR_VOL_SHIFT, + 127, 0, out_dac_tlv), + +SOC_ENUM("Left Digital Sidetone", wm8400_left_digital_sidetone_enum), +SOC_ENUM("Right Digital Sidetone", wm8400_right_digital_sidetone_enum), + +SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8400_DIGITAL_SIDE_TONE, + WM8400_ADCL_DAC_SVOL_SHIFT, 15, 0, out_sidetone_tlv), +SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8400_DIGITAL_SIDE_TONE, + WM8400_ADCR_DAC_SVOL_SHIFT, 15, 0, out_sidetone_tlv), + +SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8400_ADC_CTRL, + WM8400_ADC_HPF_ENA_SHIFT, 1, 0), + +SOC_ENUM("ADC HPF Mode", wm8400_right_adcmode_enum), + +WM8400_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume", + WM8400_LEFT_ADC_DIGITAL_VOLUME, + WM8400_ADCL_VOL_SHIFT, + WM8400_ADCL_VOL_MASK, + 0, + in_adc_tlv), + +WM8400_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume", + WM8400_RIGHT_ADC_DIGITAL_VOLUME, + WM8400_ADCR_VOL_SHIFT, + WM8400_ADCR_VOL_MASK, + 0, + in_adc_tlv), + +WM8400_OUTPGA_SINGLE_R_TLV("LIN12 Volume", + WM8400_LEFT_LINE_INPUT_1_2_VOLUME, + WM8400_LIN12VOL_SHIFT, + WM8400_LIN12VOL_MASK, + 0, + in_pga_tlv), + +SOC_SINGLE("LIN12 ZC Switch", WM8400_LEFT_LINE_INPUT_1_2_VOLUME, + WM8400_LI12ZC_SHIFT, 1, 0), + +SOC_SINGLE("LIN12 Mute Switch", WM8400_LEFT_LINE_INPUT_1_2_VOLUME, + WM8400_LI12MUTE_SHIFT, 1, 0), + +WM8400_OUTPGA_SINGLE_R_TLV("LIN34 Volume", + WM8400_LEFT_LINE_INPUT_3_4_VOLUME, + WM8400_LIN34VOL_SHIFT, + WM8400_LIN34VOL_MASK, + 0, + in_pga_tlv), + +SOC_SINGLE("LIN34 ZC Switch", WM8400_LEFT_LINE_INPUT_3_4_VOLUME, + WM8400_LI34ZC_SHIFT, 1, 0), + +SOC_SINGLE("LIN34 Mute Switch", WM8400_LEFT_LINE_INPUT_3_4_VOLUME, + WM8400_LI34MUTE_SHIFT, 1, 0), + +WM8400_OUTPGA_SINGLE_R_TLV("RIN12 Volume", + WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8400_RIN12VOL_SHIFT, + WM8400_RIN12VOL_MASK, + 0, + in_pga_tlv), + +SOC_SINGLE("RIN12 ZC Switch", WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8400_RI12ZC_SHIFT, 1, 0), + +SOC_SINGLE("RIN12 Mute Switch", WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8400_RI12MUTE_SHIFT, 1, 0), + +WM8400_OUTPGA_SINGLE_R_TLV("RIN34 Volume", + WM8400_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8400_RIN34VOL_SHIFT, + WM8400_RIN34VOL_MASK, + 0, + in_pga_tlv), + +SOC_SINGLE("RIN34 ZC Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8400_RI34ZC_SHIFT, 1, 0), + +SOC_SINGLE("RIN34 Mute Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8400_RI34MUTE_SHIFT, 1, 0), + +}; + +/* + * _DAPM_ Controls + */ + +static int outmixer_event (struct snd_soc_dapm_widget *w, + struct snd_kcontrol * kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + u32 reg_shift = mc->shift; + int ret = 0; + u16 reg; + + switch (reg_shift) { + case WM8400_SPEAKER_MIXER | (WM8400_LDSPK << 8) : + reg = snd_soc_component_read(component, WM8400_OUTPUT_MIXER1); + if (reg & WM8400_LDLO) { + printk(KERN_WARNING + "Cannot set as Output Mixer 1 LDLO Set\n"); + ret = -1; + } + break; + case WM8400_SPEAKER_MIXER | (WM8400_RDSPK << 8): + reg = snd_soc_component_read(component, WM8400_OUTPUT_MIXER2); + if (reg & WM8400_RDRO) { + printk(KERN_WARNING + "Cannot set as Output Mixer 2 RDRO Set\n"); + ret = -1; + } + break; + case WM8400_OUTPUT_MIXER1 | (WM8400_LDLO << 8): + reg = snd_soc_component_read(component, WM8400_SPEAKER_MIXER); + if (reg & WM8400_LDSPK) { + printk(KERN_WARNING + "Cannot set as Speaker Mixer LDSPK Set\n"); + ret = -1; + } + break; + case WM8400_OUTPUT_MIXER2 | (WM8400_RDRO << 8): + reg = snd_soc_component_read(component, WM8400_SPEAKER_MIXER); + if (reg & WM8400_RDSPK) { + printk(KERN_WARNING + "Cannot set as Speaker Mixer RDSPK Set\n"); + ret = -1; + } + break; + } + + return ret; +} + +/* INMIX dB values */ +static const DECLARE_TLV_DB_SCALE(in_mix_tlv, -1200, 600, 0); + +/* Left In PGA Connections */ +static const struct snd_kcontrol_new wm8400_dapm_lin12_pga_controls[] = { +SOC_DAPM_SINGLE("LIN1 Switch", WM8400_INPUT_MIXER2, WM8400_LMN1_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LIN2 Switch", WM8400_INPUT_MIXER2, WM8400_LMP2_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new wm8400_dapm_lin34_pga_controls[] = { +SOC_DAPM_SINGLE("LIN3 Switch", WM8400_INPUT_MIXER2, WM8400_LMN3_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LIN4 Switch", WM8400_INPUT_MIXER2, WM8400_LMP4_SHIFT, 1, 0), +}; + +/* Right In PGA Connections */ +static const struct snd_kcontrol_new wm8400_dapm_rin12_pga_controls[] = { +SOC_DAPM_SINGLE("RIN1 Switch", WM8400_INPUT_MIXER2, WM8400_RMN1_SHIFT, 1, 0), +SOC_DAPM_SINGLE("RIN2 Switch", WM8400_INPUT_MIXER2, WM8400_RMP2_SHIFT, 1, 0), +}; + +static const struct snd_kcontrol_new wm8400_dapm_rin34_pga_controls[] = { +SOC_DAPM_SINGLE("RIN3 Switch", WM8400_INPUT_MIXER2, WM8400_RMN3_SHIFT, 1, 0), +SOC_DAPM_SINGLE("RIN4 Switch", WM8400_INPUT_MIXER2, WM8400_RMP4_SHIFT, 1, 0), +}; + +/* INMIXL */ +static const struct snd_kcontrol_new wm8400_dapm_inmixl_controls[] = { +SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8400_INPUT_MIXER3, + WM8400_LDBVOL_SHIFT, WM8400_LDBVOL_MASK, 0, in_mix_tlv), +SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8400_INPUT_MIXER5, WM8400_LI2BVOL_SHIFT, + 7, 0, in_mix_tlv), +SOC_DAPM_SINGLE("LINPGA12 Switch", WM8400_INPUT_MIXER3, WM8400_L12MNB_SHIFT, + 1, 0), +SOC_DAPM_SINGLE("LINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT, + 1, 0), +}; + +/* INMIXR */ +static const struct snd_kcontrol_new wm8400_dapm_inmixr_controls[] = { +SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8400_INPUT_MIXER4, + WM8400_RDBVOL_SHIFT, WM8400_RDBVOL_MASK, 0, in_mix_tlv), +SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8400_INPUT_MIXER6, WM8400_RI2BVOL_SHIFT, + 7, 0, in_mix_tlv), +SOC_DAPM_SINGLE("RINPGA12 Switch", WM8400_INPUT_MIXER3, WM8400_L12MNB_SHIFT, + 1, 0), +SOC_DAPM_SINGLE("RINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT, + 1, 0), +}; + +/* AINLMUX */ +static const char *wm8400_ainlmux[] = + {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"}; + +static SOC_ENUM_SINGLE_DECL(wm8400_ainlmux_enum, + WM8400_INPUT_MIXER1, + WM8400_AINLMODE_SHIFT, + wm8400_ainlmux); + +static const struct snd_kcontrol_new wm8400_dapm_ainlmux_controls = +SOC_DAPM_ENUM("Route", wm8400_ainlmux_enum); + +/* DIFFINL */ + +/* AINRMUX */ +static const char *wm8400_ainrmux[] = + {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"}; + +static SOC_ENUM_SINGLE_DECL(wm8400_ainrmux_enum, + WM8400_INPUT_MIXER1, + WM8400_AINRMODE_SHIFT, + wm8400_ainrmux); + +static const struct snd_kcontrol_new wm8400_dapm_ainrmux_controls = +SOC_DAPM_ENUM("Route", wm8400_ainrmux_enum); + +/* LOMIX */ +static const struct snd_kcontrol_new wm8400_dapm_lomix_controls[] = { +SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8400_OUTPUT_MIXER1, + WM8400_LRBLO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8400_OUTPUT_MIXER1, + WM8400_LLBLO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8400_OUTPUT_MIXER1, + WM8400_LRI3LO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8400_OUTPUT_MIXER1, + WM8400_LLI3LO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER1, + WM8400_LR12LO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER1, + WM8400_LL12LO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8400_OUTPUT_MIXER1, + WM8400_LDLO_SHIFT, 1, 0), +}; + +/* ROMIX */ +static const struct snd_kcontrol_new wm8400_dapm_romix_controls[] = { +SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8400_OUTPUT_MIXER2, + WM8400_RLBRO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8400_OUTPUT_MIXER2, + WM8400_RRBRO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8400_OUTPUT_MIXER2, + WM8400_RLI3RO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8400_OUTPUT_MIXER2, + WM8400_RRI3RO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER2, + WM8400_RL12RO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER2, + WM8400_RR12RO_SHIFT, 1, 0), +SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8400_OUTPUT_MIXER2, + WM8400_RDRO_SHIFT, 1, 0), +}; + +/* LONMIX */ +static const struct snd_kcontrol_new wm8400_dapm_lonmix_controls[] = { +SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8400_LINE_MIXER1, + WM8400_LLOPGALON_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8400_LINE_MIXER1, + WM8400_LROPGALON_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8400_LINE_MIXER1, + WM8400_LOPLON_SHIFT, 1, 0), +}; + +/* LOPMIX */ +static const struct snd_kcontrol_new wm8400_dapm_lopmix_controls[] = { +SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8400_LINE_MIXER1, + WM8400_LR12LOP_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8400_LINE_MIXER1, + WM8400_LL12LOP_SHIFT, 1, 0), +SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8400_LINE_MIXER1, + WM8400_LLOPGALOP_SHIFT, 1, 0), +}; + +/* RONMIX */ +static const struct snd_kcontrol_new wm8400_dapm_ronmix_controls[] = { +SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8400_LINE_MIXER2, + WM8400_RROPGARON_SHIFT, 1, 0), +SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8400_LINE_MIXER2, + WM8400_RLOPGARON_SHIFT, 1, 0), +SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8400_LINE_MIXER2, + WM8400_ROPRON_SHIFT, 1, 0), +}; + +/* ROPMIX */ +static const struct snd_kcontrol_new wm8400_dapm_ropmix_controls[] = { +SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8400_LINE_MIXER2, + WM8400_RL12ROP_SHIFT, 1, 0), +SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8400_LINE_MIXER2, + WM8400_RR12ROP_SHIFT, 1, 0), +SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8400_LINE_MIXER2, + WM8400_RROPGAROP_SHIFT, 1, 0), +}; + +/* OUT3MIX */ +static const struct snd_kcontrol_new wm8400_dapm_out3mix_controls[] = { +SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8400_OUT3_4_MIXER, + WM8400_LI4O3_SHIFT, 1, 0), +SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8400_OUT3_4_MIXER, + WM8400_LPGAO3_SHIFT, 1, 0), +}; + +/* OUT4MIX */ +static const struct snd_kcontrol_new wm8400_dapm_out4mix_controls[] = { +SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8400_OUT3_4_MIXER, + WM8400_RPGAO4_SHIFT, 1, 0), +SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8400_OUT3_4_MIXER, + WM8400_RI4O4_SHIFT, 1, 0), +}; + +/* SPKMIX */ +static const struct snd_kcontrol_new wm8400_dapm_spkmix_controls[] = { +SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8400_SPEAKER_MIXER, + WM8400_LI2SPK_SHIFT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8400_SPEAKER_MIXER, + WM8400_LB2SPK_SHIFT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8400_SPEAKER_MIXER, + WM8400_LOPGASPK_SHIFT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8400_SPEAKER_MIXER, + WM8400_LDSPK_SHIFT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8400_SPEAKER_MIXER, + WM8400_RDSPK_SHIFT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8400_SPEAKER_MIXER, + WM8400_ROPGASPK_SHIFT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8400_SPEAKER_MIXER, + WM8400_RL12ROP_SHIFT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8400_SPEAKER_MIXER, + WM8400_RI2SPK_SHIFT, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8400_dapm_widgets[] = { +/* Input Side */ +/* Input Lines */ +SND_SOC_DAPM_INPUT("LIN1"), +SND_SOC_DAPM_INPUT("LIN2"), +SND_SOC_DAPM_INPUT("LIN3"), +SND_SOC_DAPM_INPUT("LIN4/RXN"), +SND_SOC_DAPM_INPUT("RIN3"), +SND_SOC_DAPM_INPUT("RIN4/RXP"), +SND_SOC_DAPM_INPUT("RIN1"), +SND_SOC_DAPM_INPUT("RIN2"), +SND_SOC_DAPM_INPUT("Internal ADC Source"), + +/* DACs */ +SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8400_POWER_MANAGEMENT_2, + WM8400_ADCL_ENA_SHIFT, 0), +SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8400_POWER_MANAGEMENT_2, + WM8400_ADCR_ENA_SHIFT, 0), + +/* Input PGAs */ +SND_SOC_DAPM_MIXER("LIN12 PGA", WM8400_POWER_MANAGEMENT_2, + WM8400_LIN12_ENA_SHIFT, + 0, &wm8400_dapm_lin12_pga_controls[0], + ARRAY_SIZE(wm8400_dapm_lin12_pga_controls)), +SND_SOC_DAPM_MIXER("LIN34 PGA", WM8400_POWER_MANAGEMENT_2, + WM8400_LIN34_ENA_SHIFT, + 0, &wm8400_dapm_lin34_pga_controls[0], + ARRAY_SIZE(wm8400_dapm_lin34_pga_controls)), +SND_SOC_DAPM_MIXER("RIN12 PGA", WM8400_POWER_MANAGEMENT_2, + WM8400_RIN12_ENA_SHIFT, + 0, &wm8400_dapm_rin12_pga_controls[0], + ARRAY_SIZE(wm8400_dapm_rin12_pga_controls)), +SND_SOC_DAPM_MIXER("RIN34 PGA", WM8400_POWER_MANAGEMENT_2, + WM8400_RIN34_ENA_SHIFT, + 0, &wm8400_dapm_rin34_pga_controls[0], + ARRAY_SIZE(wm8400_dapm_rin34_pga_controls)), + +SND_SOC_DAPM_SUPPLY("INL", WM8400_POWER_MANAGEMENT_2, WM8400_AINL_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("INR", WM8400_POWER_MANAGEMENT_2, WM8400_AINR_ENA_SHIFT, + 0, NULL, 0), + +/* INMIXL */ +SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0, + &wm8400_dapm_inmixl_controls[0], + ARRAY_SIZE(wm8400_dapm_inmixl_controls)), + +/* AINLMUX */ +SND_SOC_DAPM_MUX("AILNMUX", SND_SOC_NOPM, 0, 0, &wm8400_dapm_ainlmux_controls), + +/* INMIXR */ +SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0, + &wm8400_dapm_inmixr_controls[0], + ARRAY_SIZE(wm8400_dapm_inmixr_controls)), + +/* AINRMUX */ +SND_SOC_DAPM_MUX("AIRNMUX", SND_SOC_NOPM, 0, 0, &wm8400_dapm_ainrmux_controls), + +/* Output Side */ +/* DACs */ +SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8400_POWER_MANAGEMENT_3, + WM8400_DACL_ENA_SHIFT, 0), +SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8400_POWER_MANAGEMENT_3, + WM8400_DACR_ENA_SHIFT, 0), + +/* LOMIX */ +SND_SOC_DAPM_MIXER_E("LOMIX", WM8400_POWER_MANAGEMENT_3, + WM8400_LOMIX_ENA_SHIFT, + 0, &wm8400_dapm_lomix_controls[0], + ARRAY_SIZE(wm8400_dapm_lomix_controls), + outmixer_event, SND_SOC_DAPM_PRE_REG), + +/* LONMIX */ +SND_SOC_DAPM_MIXER("LONMIX", WM8400_POWER_MANAGEMENT_3, WM8400_LON_ENA_SHIFT, + 0, &wm8400_dapm_lonmix_controls[0], + ARRAY_SIZE(wm8400_dapm_lonmix_controls)), + +/* LOPMIX */ +SND_SOC_DAPM_MIXER("LOPMIX", WM8400_POWER_MANAGEMENT_3, WM8400_LOP_ENA_SHIFT, + 0, &wm8400_dapm_lopmix_controls[0], + ARRAY_SIZE(wm8400_dapm_lopmix_controls)), + +/* OUT3MIX */ +SND_SOC_DAPM_MIXER("OUT3MIX", WM8400_POWER_MANAGEMENT_1, WM8400_OUT3_ENA_SHIFT, + 0, &wm8400_dapm_out3mix_controls[0], + ARRAY_SIZE(wm8400_dapm_out3mix_controls)), + +/* SPKMIX */ +SND_SOC_DAPM_MIXER_E("SPKMIX", WM8400_POWER_MANAGEMENT_1, WM8400_SPK_ENA_SHIFT, + 0, &wm8400_dapm_spkmix_controls[0], + ARRAY_SIZE(wm8400_dapm_spkmix_controls), outmixer_event, + SND_SOC_DAPM_PRE_REG), + +/* OUT4MIX */ +SND_SOC_DAPM_MIXER("OUT4MIX", WM8400_POWER_MANAGEMENT_1, WM8400_OUT4_ENA_SHIFT, + 0, &wm8400_dapm_out4mix_controls[0], + ARRAY_SIZE(wm8400_dapm_out4mix_controls)), + +/* ROPMIX */ +SND_SOC_DAPM_MIXER("ROPMIX", WM8400_POWER_MANAGEMENT_3, WM8400_ROP_ENA_SHIFT, + 0, &wm8400_dapm_ropmix_controls[0], + ARRAY_SIZE(wm8400_dapm_ropmix_controls)), + +/* RONMIX */ +SND_SOC_DAPM_MIXER("RONMIX", WM8400_POWER_MANAGEMENT_3, WM8400_RON_ENA_SHIFT, + 0, &wm8400_dapm_ronmix_controls[0], + ARRAY_SIZE(wm8400_dapm_ronmix_controls)), + +/* ROMIX */ +SND_SOC_DAPM_MIXER_E("ROMIX", WM8400_POWER_MANAGEMENT_3, + WM8400_ROMIX_ENA_SHIFT, + 0, &wm8400_dapm_romix_controls[0], + ARRAY_SIZE(wm8400_dapm_romix_controls), + outmixer_event, SND_SOC_DAPM_PRE_REG), + +/* LOUT PGA */ +SND_SOC_DAPM_PGA("LOUT PGA", WM8400_POWER_MANAGEMENT_1, WM8400_LOUT_ENA_SHIFT, + 0, NULL, 0), + +/* ROUT PGA */ +SND_SOC_DAPM_PGA("ROUT PGA", WM8400_POWER_MANAGEMENT_1, WM8400_ROUT_ENA_SHIFT, + 0, NULL, 0), + +/* LOPGA */ +SND_SOC_DAPM_PGA("LOPGA", WM8400_POWER_MANAGEMENT_3, WM8400_LOPGA_ENA_SHIFT, 0, + NULL, 0), + +/* ROPGA */ +SND_SOC_DAPM_PGA("ROPGA", WM8400_POWER_MANAGEMENT_3, WM8400_ROPGA_ENA_SHIFT, 0, + NULL, 0), + +/* MICBIAS */ +SND_SOC_DAPM_SUPPLY("MICBIAS", WM8400_POWER_MANAGEMENT_1, + WM8400_MIC1BIAS_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_OUTPUT("LON"), +SND_SOC_DAPM_OUTPUT("LOP"), +SND_SOC_DAPM_OUTPUT("OUT3"), +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("SPKN"), +SND_SOC_DAPM_OUTPUT("SPKP"), +SND_SOC_DAPM_OUTPUT("ROUT"), +SND_SOC_DAPM_OUTPUT("OUT4"), +SND_SOC_DAPM_OUTPUT("ROP"), +SND_SOC_DAPM_OUTPUT("RON"), + +SND_SOC_DAPM_OUTPUT("Internal DAC Sink"), +}; + +static const struct snd_soc_dapm_route wm8400_dapm_routes[] = { + /* Make DACs turn on when playing even if not mixed into any outputs */ + {"Internal DAC Sink", NULL, "Left DAC"}, + {"Internal DAC Sink", NULL, "Right DAC"}, + + /* Make ADCs turn on when recording + * even if not mixed from any inputs */ + {"Left ADC", NULL, "Internal ADC Source"}, + {"Right ADC", NULL, "Internal ADC Source"}, + + /* Input Side */ + /* LIN12 PGA */ + {"LIN12 PGA", "LIN1 Switch", "LIN1"}, + {"LIN12 PGA", "LIN2 Switch", "LIN2"}, + /* LIN34 PGA */ + {"LIN34 PGA", "LIN3 Switch", "LIN3"}, + {"LIN34 PGA", "LIN4 Switch", "LIN4/RXN"}, + /* INMIXL */ + {"INMIXL", NULL, "INL"}, + {"INMIXL", "Record Left Volume", "LOMIX"}, + {"INMIXL", "LIN2 Volume", "LIN2"}, + {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"}, + {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"}, + /* AILNMUX */ + {"AILNMUX", NULL, "INL"}, + {"AILNMUX", "INMIXL Mix", "INMIXL"}, + {"AILNMUX", "DIFFINL Mix", "LIN12 PGA"}, + {"AILNMUX", "DIFFINL Mix", "LIN34 PGA"}, + {"AILNMUX", "RXVOICE Mix", "LIN4/RXN"}, + {"AILNMUX", "RXVOICE Mix", "RIN4/RXP"}, + /* ADC */ + {"Left ADC", NULL, "AILNMUX"}, + + /* RIN12 PGA */ + {"RIN12 PGA", "RIN1 Switch", "RIN1"}, + {"RIN12 PGA", "RIN2 Switch", "RIN2"}, + /* RIN34 PGA */ + {"RIN34 PGA", "RIN3 Switch", "RIN3"}, + {"RIN34 PGA", "RIN4 Switch", "RIN4/RXP"}, + /* INMIXR */ + {"INMIXR", NULL, "INR"}, + {"INMIXR", "Record Right Volume", "ROMIX"}, + {"INMIXR", "RIN2 Volume", "RIN2"}, + {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"}, + {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"}, + /* AIRNMUX */ + {"AIRNMUX", NULL, "INR"}, + {"AIRNMUX", "INMIXR Mix", "INMIXR"}, + {"AIRNMUX", "DIFFINR Mix", "RIN12 PGA"}, + {"AIRNMUX", "DIFFINR Mix", "RIN34 PGA"}, + {"AIRNMUX", "RXVOICE Mix", "LIN4/RXN"}, + {"AIRNMUX", "RXVOICE Mix", "RIN4/RXP"}, + /* ADC */ + {"Right ADC", NULL, "AIRNMUX"}, + + /* LOMIX */ + {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"}, + {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"}, + {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, + {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, + {"LOMIX", "LOMIX Right ADC Bypass Switch", "AIRNMUX"}, + {"LOMIX", "LOMIX Left ADC Bypass Switch", "AILNMUX"}, + {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"}, + + /* ROMIX */ + {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"}, + {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"}, + {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, + {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, + {"ROMIX", "ROMIX Right ADC Bypass Switch", "AIRNMUX"}, + {"ROMIX", "ROMIX Left ADC Bypass Switch", "AILNMUX"}, + {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"}, + + /* SPKMIX */ + {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"}, + {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"}, + {"SPKMIX", "SPKMIX LADC Bypass Switch", "AILNMUX"}, + {"SPKMIX", "SPKMIX RADC Bypass Switch", "AIRNMUX"}, + {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"}, + {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"}, + {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"}, + {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"}, + + /* LONMIX */ + {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"}, + {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"}, + {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"}, + + /* LOPMIX */ + {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"}, + {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"}, + {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"}, + + /* OUT3MIX */ + {"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXN"}, + {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"}, + + /* OUT4MIX */ + {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"}, + {"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"}, + + /* RONMIX */ + {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"}, + {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"}, + {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"}, + + /* ROPMIX */ + {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"}, + {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"}, + {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"}, + + /* Out Mixer PGAs */ + {"LOPGA", NULL, "LOMIX"}, + {"ROPGA", NULL, "ROMIX"}, + + {"LOUT PGA", NULL, "LOMIX"}, + {"ROUT PGA", NULL, "ROMIX"}, + + /* Output Pins */ + {"LON", NULL, "LONMIX"}, + {"LOP", NULL, "LOPMIX"}, + {"OUT3", NULL, "OUT3MIX"}, + {"LOUT", NULL, "LOUT PGA"}, + {"SPKN", NULL, "SPKMIX"}, + {"ROUT", NULL, "ROUT PGA"}, + {"OUT4", NULL, "OUT4MIX"}, + {"ROP", NULL, "ROPMIX"}, + {"RON", NULL, "RONMIX"}, +}; + +/* + * Clock after FLL and dividers + */ +static int wm8400_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8400_priv *wm8400 = snd_soc_component_get_drvdata(component); + + wm8400->sysclk = freq; + return 0; +} + +struct fll_factors { + u16 n; + u16 k; + u16 outdiv; + u16 fratio; + u16 freq_ref; +}; + +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors, + unsigned int Fref, unsigned int Fout) +{ + u64 Kpart; + unsigned int K, Nmod, target; + + factors->outdiv = 2; + while (Fout * factors->outdiv < 90000000 || + Fout * factors->outdiv > 100000000) { + factors->outdiv *= 2; + if (factors->outdiv > 32) { + dev_err(wm8400->wm8400->dev, + "Unsupported FLL output frequency %uHz\n", + Fout); + return -EINVAL; + } + } + target = Fout * factors->outdiv; + factors->outdiv = factors->outdiv >> 2; + + if (Fref < 48000) + factors->freq_ref = 1; + else + factors->freq_ref = 0; + + if (Fref < 1000000) + factors->fratio = 9; + else + factors->fratio = 0; + + /* Ensure we have a fractional part */ + do { + if (Fref < 1000000) + factors->fratio--; + else + factors->fratio++; + + if (factors->fratio < 1 || factors->fratio > 8) { + dev_err(wm8400->wm8400->dev, + "Unable to calculate FRATIO\n"); + return -EINVAL; + } + + factors->n = target / (Fref * factors->fratio); + Nmod = target % (Fref * factors->fratio); + } while (Nmod == 0); + + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, (Fref * factors->fratio)); + + K = Kpart & 0xFFFFFFFF; + + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + factors->k = K / 10; + + dev_dbg(wm8400->wm8400->dev, + "FLL: Fref=%u Fout=%u N=%x K=%x, FRATIO=%x OUTDIV=%x\n", + Fref, Fout, + factors->n, factors->k, factors->fratio, factors->outdiv); + + return 0; +} + +static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8400_priv *wm8400 = snd_soc_component_get_drvdata(component); + struct fll_factors factors; + int ret; + u16 reg; + + if (freq_in == wm8400->fll_in && freq_out == wm8400->fll_out) + return 0; + + if (freq_out) { + ret = fll_factors(wm8400, &factors, freq_in, freq_out); + if (ret != 0) + return ret; + } else { + /* Bodge GCC 4.4.0 uninitialised variable warning - it + * doesn't seem capable of working out that we exit if + * freq_out is 0 before any of the uses. */ + memset(&factors, 0, sizeof(factors)); + } + + wm8400->fll_out = freq_out; + wm8400->fll_in = freq_in; + + /* We *must* disable the FLL before any changes */ + reg = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_2); + reg &= ~WM8400_FLL_ENA; + snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_2, reg); + + reg = snd_soc_component_read(component, WM8400_FLL_CONTROL_1); + reg &= ~WM8400_FLL_OSC_ENA; + snd_soc_component_write(component, WM8400_FLL_CONTROL_1, reg); + + if (!freq_out) + return 0; + + reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK); + reg |= WM8400_FLL_FRAC | factors.fratio; + reg |= factors.freq_ref << WM8400_FLL_REF_FREQ_SHIFT; + snd_soc_component_write(component, WM8400_FLL_CONTROL_1, reg); + + snd_soc_component_write(component, WM8400_FLL_CONTROL_2, factors.k); + snd_soc_component_write(component, WM8400_FLL_CONTROL_3, factors.n); + + reg = snd_soc_component_read(component, WM8400_FLL_CONTROL_4); + reg &= ~WM8400_FLL_OUTDIV_MASK; + reg |= factors.outdiv; + snd_soc_component_write(component, WM8400_FLL_CONTROL_4, reg); + + return 0; +} + +/* + * Sets ADC and Voice DAC format. + */ +static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 audio1, audio3; + + audio1 = snd_soc_component_read(component, WM8400_AUDIO_INTERFACE_1); + audio3 = snd_soc_component_read(component, WM8400_AUDIO_INTERFACE_3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + audio3 &= ~WM8400_AIF_MSTR1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + audio3 |= WM8400_AIF_MSTR1; + break; + default: + return -EINVAL; + } + + audio1 &= ~WM8400_AIF_FMT_MASK; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + audio1 |= WM8400_AIF_FMT_I2S; + audio1 &= ~WM8400_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_RIGHT_J: + audio1 |= WM8400_AIF_FMT_RIGHTJ; + audio1 &= ~WM8400_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_LEFT_J: + audio1 |= WM8400_AIF_FMT_LEFTJ; + audio1 &= ~WM8400_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_A: + audio1 |= WM8400_AIF_FMT_DSP; + audio1 &= ~WM8400_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_B: + audio1 |= WM8400_AIF_FMT_DSP | WM8400_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8400_AUDIO_INTERFACE_1, audio1); + snd_soc_component_write(component, WM8400_AUDIO_INTERFACE_3, audio3); + return 0; +} + +static int wm8400_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + + switch (div_id) { + case WM8400_MCLK_DIV: + reg = snd_soc_component_read(component, WM8400_CLOCKING_2) & + ~WM8400_MCLK_DIV_MASK; + snd_soc_component_write(component, WM8400_CLOCKING_2, reg | div); + break; + case WM8400_DACCLK_DIV: + reg = snd_soc_component_read(component, WM8400_CLOCKING_2) & + ~WM8400_DAC_CLKDIV_MASK; + snd_soc_component_write(component, WM8400_CLOCKING_2, reg | div); + break; + case WM8400_ADCCLK_DIV: + reg = snd_soc_component_read(component, WM8400_CLOCKING_2) & + ~WM8400_ADC_CLKDIV_MASK; + snd_soc_component_write(component, WM8400_CLOCKING_2, reg | div); + break; + case WM8400_BCLK_DIV: + reg = snd_soc_component_read(component, WM8400_CLOCKING_1) & + ~WM8400_BCLK_DIV_MASK; + snd_soc_component_write(component, WM8400_CLOCKING_1, reg | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + */ +static int wm8400_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 audio1 = snd_soc_component_read(component, WM8400_AUDIO_INTERFACE_1); + + audio1 &= ~WM8400_AIF_WL_MASK; + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + audio1 |= WM8400_AIF_WL_20BITS; + break; + case 24: + audio1 |= WM8400_AIF_WL_24BITS; + break; + case 32: + audio1 |= WM8400_AIF_WL_32BITS; + break; + } + + snd_soc_component_write(component, WM8400_AUDIO_INTERFACE_1, audio1); + return 0; +} + +static int wm8400_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 val = snd_soc_component_read(component, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE; + + if (mute) + snd_soc_component_write(component, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); + else + snd_soc_component_write(component, WM8400_DAC_CTRL, val); + + return 0; +} + +/* TODO: set bias for best performance at standby */ +static int wm8400_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8400_priv *wm8400 = snd_soc_component_get_drvdata(component); + u16 val; + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID=2*50k */ + val = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1) & + ~WM8400_VMID_MODE_MASK; + snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val | 0x2); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(power), + &power[0]); + if (ret != 0) { + dev_err(wm8400->wm8400->dev, + "Failed to enable regulators: %d\n", + ret); + return ret; + } + + snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, + WM8400_CODEC_ENA | WM8400_SYSCLK_ENA); + + /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */ + snd_soc_component_write(component, WM8400_ANTIPOP2, WM8400_SOFTST | + WM8400_BUFDCOPEN | WM8400_POBCTRL); + + msleep(50); + + /* Enable VREF & VMID at 2x50k */ + val = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1); + val |= 0x2 | WM8400_VREF_ENA; + snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val); + + /* Enable BUFIOEN */ + snd_soc_component_write(component, WM8400_ANTIPOP2, WM8400_SOFTST | + WM8400_BUFDCOPEN | WM8400_POBCTRL | + WM8400_BUFIOEN); + + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_component_write(component, WM8400_ANTIPOP2, WM8400_BUFIOEN); + } + + /* VMID=2*300k */ + val = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1) & + ~WM8400_VMID_MODE_MASK; + snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val | 0x4); + break; + + case SND_SOC_BIAS_OFF: + /* Enable POBCTRL and SOFT_ST */ + snd_soc_component_write(component, WM8400_ANTIPOP2, WM8400_SOFTST | + WM8400_POBCTRL | WM8400_BUFIOEN); + + /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_component_write(component, WM8400_ANTIPOP2, WM8400_SOFTST | + WM8400_BUFDCOPEN | WM8400_POBCTRL | + WM8400_BUFIOEN); + + /* mute DAC */ + val = snd_soc_component_read(component, WM8400_DAC_CTRL); + snd_soc_component_write(component, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); + + /* Enable any disabled outputs */ + val = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1); + val |= WM8400_SPK_ENA | WM8400_OUT3_ENA | + WM8400_OUT4_ENA | WM8400_LOUT_ENA | + WM8400_ROUT_ENA; + snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val); + + /* Disable VMID */ + val &= ~WM8400_VMID_MODE_MASK; + snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val); + + msleep(300); + + /* Enable all output discharge bits */ + snd_soc_component_write(component, WM8400_ANTIPOP1, WM8400_DIS_LLINE | + WM8400_DIS_RLINE | WM8400_DIS_OUT3 | + WM8400_DIS_OUT4 | WM8400_DIS_LOUT | + WM8400_DIS_ROUT); + + /* Disable VREF */ + val &= ~WM8400_VREF_ENA; + snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, val); + + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_component_write(component, WM8400_ANTIPOP2, 0x0); + + ret = regulator_bulk_disable(ARRAY_SIZE(power), + &power[0]); + if (ret != 0) + return ret; + + break; + } + + return 0; +} + +#define WM8400_RATES SNDRV_PCM_RATE_8000_96000 + +#define WM8400_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8400_dai_ops = { + .hw_params = wm8400_hw_params, + .mute_stream = wm8400_mute, + .set_fmt = wm8400_set_dai_fmt, + .set_clkdiv = wm8400_set_dai_clkdiv, + .set_sysclk = wm8400_set_dai_sysclk, + .set_pll = wm8400_set_dai_pll, + .no_capture_mute = 1, +}; + +/* + * The WM8400 supports 2 different and mutually exclusive DAI + * configurations. + * + * 1. ADC/DAC on Primary Interface + * 2. ADC on Primary Interface/DAC on secondary + */ +static struct snd_soc_dai_driver wm8400_dai = { +/* ADC/DAC on primary */ + .name = "wm8400-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8400_RATES, + .formats = WM8400_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8400_RATES, + .formats = WM8400_FORMATS, + }, + .ops = &wm8400_dai_ops, +}; + +static int wm8400_component_probe(struct snd_soc_component *component) +{ + struct wm8400 *wm8400 = dev_get_platdata(component->dev); + struct wm8400_priv *priv; + int ret; + u16 reg; + + priv = devm_kzalloc(component->dev, sizeof(struct wm8400_priv), + GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + snd_soc_component_init_regmap(component, wm8400->regmap); + snd_soc_component_set_drvdata(component, priv); + priv->wm8400 = wm8400; + + ret = devm_regulator_bulk_get(wm8400->dev, + ARRAY_SIZE(power), &power[0]); + if (ret != 0) { + dev_err(component->dev, "Failed to get regulators: %d\n", ret); + return ret; + } + + wm8400_component_reset(component); + + reg = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1); + snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA); + + /* Latch volume update bits */ + reg = snd_soc_component_read(component, WM8400_LEFT_LINE_INPUT_1_2_VOLUME); + snd_soc_component_write(component, WM8400_LEFT_LINE_INPUT_1_2_VOLUME, + reg & WM8400_IPVU); + reg = snd_soc_component_read(component, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME); + snd_soc_component_write(component, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, + reg & WM8400_IPVU); + + snd_soc_component_write(component, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); + snd_soc_component_write(component, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); + + return 0; +} + +static void wm8400_component_remove(struct snd_soc_component *component) +{ + u16 reg; + + reg = snd_soc_component_read(component, WM8400_POWER_MANAGEMENT_1); + snd_soc_component_write(component, WM8400_POWER_MANAGEMENT_1, + reg & (~WM8400_CODEC_ENA)); +} + +static const struct snd_soc_component_driver soc_component_dev_wm8400 = { + .probe = wm8400_component_probe, + .remove = wm8400_component_remove, + .set_bias_level = wm8400_set_bias_level, + .controls = wm8400_snd_controls, + .num_controls = ARRAY_SIZE(wm8400_snd_controls), + .dapm_widgets = wm8400_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8400_dapm_widgets), + .dapm_routes = wm8400_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8400_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm8400_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm8400, + &wm8400_dai, 1); +} + +static struct platform_driver wm8400_codec_driver = { + .driver = { + .name = "wm8400-codec", + }, + .probe = wm8400_probe, +}; + +module_platform_driver(wm8400_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8400 driver"); +MODULE_AUTHOR("Mark Brown"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8400-codec"); diff --git a/sound/soc/codecs/wm8400.h b/sound/soc/codecs/wm8400.h new file mode 100644 index 0000000000..9e7bd4f767 --- /dev/null +++ b/sound/soc/codecs/wm8400.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * wm8400.h -- audio driver for WM8400 + * + * Copyright 2008 Wolfson Microelectronics PLC. + * Author: Mark Brown + */ + +#ifndef _WM8400_CODEC_H +#define _WM8400_CODEC_H + +#define WM8400_MCLK_DIV 0 +#define WM8400_DACCLK_DIV 1 +#define WM8400_ADCCLK_DIV 2 +#define WM8400_BCLK_DIV 3 + +#define WM8400_MCLK_DIV_1 0x400 +#define WM8400_MCLK_DIV_2 0x800 + +#define WM8400_DAC_CLKDIV_1 0x00 +#define WM8400_DAC_CLKDIV_1_5 0x04 +#define WM8400_DAC_CLKDIV_2 0x08 +#define WM8400_DAC_CLKDIV_3 0x0c +#define WM8400_DAC_CLKDIV_4 0x10 +#define WM8400_DAC_CLKDIV_5_5 0x14 +#define WM8400_DAC_CLKDIV_6 0x18 + +#define WM8400_ADC_CLKDIV_1 0x00 +#define WM8400_ADC_CLKDIV_1_5 0x20 +#define WM8400_ADC_CLKDIV_2 0x40 +#define WM8400_ADC_CLKDIV_3 0x60 +#define WM8400_ADC_CLKDIV_4 0x80 +#define WM8400_ADC_CLKDIV_5_5 0xa0 +#define WM8400_ADC_CLKDIV_6 0xc0 + + +#define WM8400_BCLK_DIV_1 (0x0 << 1) +#define WM8400_BCLK_DIV_1_5 (0x1 << 1) +#define WM8400_BCLK_DIV_2 (0x2 << 1) +#define WM8400_BCLK_DIV_3 (0x3 << 1) +#define WM8400_BCLK_DIV_4 (0x4 << 1) +#define WM8400_BCLK_DIV_5_5 (0x5 << 1) +#define WM8400_BCLK_DIV_6 (0x6 << 1) +#define WM8400_BCLK_DIV_8 (0x7 << 1) +#define WM8400_BCLK_DIV_11 (0x8 << 1) +#define WM8400_BCLK_DIV_12 (0x9 << 1) +#define WM8400_BCLK_DIV_16 (0xA << 1) +#define WM8400_BCLK_DIV_22 (0xB << 1) +#define WM8400_BCLK_DIV_24 (0xC << 1) +#define WM8400_BCLK_DIV_32 (0xD << 1) +#define WM8400_BCLK_DIV_44 (0xE << 1) +#define WM8400_BCLK_DIV_48 (0xF << 1) + +#endif diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c new file mode 100644 index 0000000000..6636a70f38 --- /dev/null +++ b/sound/soc/codecs/wm8510.c @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8510.c -- WM8510 ALSA Soc Audio driver + * + * Copyright 2006 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8510.h" + +/* + * wm8510 register cache + * We can't read the WM8510 register space when we are + * using 2 wire for device control, so we cache them instead. + */ +static const struct reg_default wm8510_reg_defaults[] = { + { 1, 0x0000 }, + { 2, 0x0000 }, + { 3, 0x0000 }, + { 4, 0x0050 }, + { 5, 0x0000 }, + { 6, 0x0140 }, + { 7, 0x0000 }, + { 8, 0x0000 }, + { 9, 0x0000 }, + { 10, 0x0000 }, + { 11, 0x00ff }, + { 12, 0x0000 }, + { 13, 0x0000 }, + { 14, 0x0100 }, + { 15, 0x00ff }, + { 16, 0x0000 }, + { 17, 0x0000 }, + { 18, 0x012c }, + { 19, 0x002c }, + { 20, 0x002c }, + { 21, 0x002c }, + { 22, 0x002c }, + { 23, 0x0000 }, + { 24, 0x0032 }, + { 25, 0x0000 }, + { 26, 0x0000 }, + { 27, 0x0000 }, + { 28, 0x0000 }, + { 29, 0x0000 }, + { 30, 0x0000 }, + { 31, 0x0000 }, + { 32, 0x0038 }, + { 33, 0x000b }, + { 34, 0x0032 }, + { 35, 0x0000 }, + { 36, 0x0008 }, + { 37, 0x000c }, + { 38, 0x0093 }, + { 39, 0x00e9 }, + { 40, 0x0000 }, + { 41, 0x0000 }, + { 42, 0x0000 }, + { 43, 0x0000 }, + { 44, 0x0003 }, + { 45, 0x0010 }, + { 46, 0x0000 }, + { 47, 0x0000 }, + { 48, 0x0000 }, + { 49, 0x0002 }, + { 50, 0x0001 }, + { 51, 0x0000 }, + { 52, 0x0000 }, + { 53, 0x0000 }, + { 54, 0x0039 }, + { 55, 0x0000 }, + { 56, 0x0001 }, +}; + +static bool wm8510_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8510_RESET: + return true; + default: + return false; + } +} + +#define WM8510_POWER1_BIASEN 0x08 +#define WM8510_POWER1_BUFIOEN 0x10 + +#define wm8510_reset(c) snd_soc_component_write(c, WM8510_RESET, 0) + +/* codec private data */ +struct wm8510_priv { + struct regmap *regmap; +}; + +static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" }; +static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" }; +static const char *wm8510_alc[] = { "ALC", "Limiter" }; + +static const struct soc_enum wm8510_enum[] = { + SOC_ENUM_SINGLE(WM8510_COMP, 1, 4, wm8510_companding), /* adc */ + SOC_ENUM_SINGLE(WM8510_COMP, 3, 4, wm8510_companding), /* dac */ + SOC_ENUM_SINGLE(WM8510_DAC, 4, 4, wm8510_deemp), + SOC_ENUM_SINGLE(WM8510_ALC3, 8, 2, wm8510_alc), +}; + +static const struct snd_kcontrol_new wm8510_snd_controls[] = { + +SOC_SINGLE("Digital Loopback Switch", WM8510_COMP, 0, 1, 0), + +SOC_ENUM("DAC Companding", wm8510_enum[1]), +SOC_ENUM("ADC Companding", wm8510_enum[0]), + +SOC_ENUM("Playback De-emphasis", wm8510_enum[2]), +SOC_SINGLE("DAC Inversion Switch", WM8510_DAC, 0, 1, 0), + +SOC_SINGLE("Master Playback Volume", WM8510_DACVOL, 0, 127, 0), + +SOC_SINGLE("High Pass Filter Switch", WM8510_ADC, 8, 1, 0), +SOC_SINGLE("High Pass Cut Off", WM8510_ADC, 4, 7, 0), +SOC_SINGLE("ADC Inversion Switch", WM8510_COMP, 0, 1, 0), + +SOC_SINGLE("Capture Volume", WM8510_ADCVOL, 0, 127, 0), + +SOC_SINGLE("DAC Playback Limiter Switch", WM8510_DACLIM1, 8, 1, 0), +SOC_SINGLE("DAC Playback Limiter Decay", WM8510_DACLIM1, 4, 15, 0), +SOC_SINGLE("DAC Playback Limiter Attack", WM8510_DACLIM1, 0, 15, 0), + +SOC_SINGLE("DAC Playback Limiter Threshold", WM8510_DACLIM2, 4, 7, 0), +SOC_SINGLE("DAC Playback Limiter Boost", WM8510_DACLIM2, 0, 15, 0), + +SOC_SINGLE("ALC Enable Switch", WM8510_ALC1, 8, 1, 0), +SOC_SINGLE("ALC Capture Max Gain", WM8510_ALC1, 3, 7, 0), +SOC_SINGLE("ALC Capture Min Gain", WM8510_ALC1, 0, 7, 0), + +SOC_SINGLE("ALC Capture ZC Switch", WM8510_ALC2, 8, 1, 0), +SOC_SINGLE("ALC Capture Hold", WM8510_ALC2, 4, 7, 0), +SOC_SINGLE("ALC Capture Target", WM8510_ALC2, 0, 15, 0), + +SOC_ENUM("ALC Capture Mode", wm8510_enum[3]), +SOC_SINGLE("ALC Capture Decay", WM8510_ALC3, 4, 15, 0), +SOC_SINGLE("ALC Capture Attack", WM8510_ALC3, 0, 15, 0), + +SOC_SINGLE("ALC Capture Noise Gate Switch", WM8510_NGATE, 3, 1, 0), +SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8510_NGATE, 0, 7, 0), + +SOC_SINGLE("Capture PGA ZC Switch", WM8510_INPPGA, 7, 1, 0), +SOC_SINGLE("Capture PGA Volume", WM8510_INPPGA, 0, 63, 0), + +SOC_SINGLE("Speaker Playback ZC Switch", WM8510_SPKVOL, 7, 1, 0), +SOC_SINGLE("Speaker Playback Switch", WM8510_SPKVOL, 6, 1, 1), +SOC_SINGLE("Speaker Playback Volume", WM8510_SPKVOL, 0, 63, 0), +SOC_SINGLE("Speaker Boost", WM8510_OUTPUT, 2, 1, 0), + +SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST, 8, 1, 0), +SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1), +}; + +/* Speaker Output Mixer */ +static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0), +SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0), +SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 0), +}; + +/* Mono Output Mixer */ +static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0), +SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0), +SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0), +}; + +static const struct snd_kcontrol_new wm8510_boost_controls[] = { +SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA, 6, 1, 1), +SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0), +SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0), +}; + +static const struct snd_kcontrol_new wm8510_micpga_controls[] = { +SOC_DAPM_SINGLE("MICP Switch", WM8510_INPUT, 0, 1, 0), +SOC_DAPM_SINGLE("MICN Switch", WM8510_INPUT, 1, 1, 0), +SOC_DAPM_SINGLE("AUX Switch", WM8510_INPUT, 2, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8510_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Speaker Mixer", WM8510_POWER3, 2, 0, + &wm8510_speaker_mixer_controls[0], + ARRAY_SIZE(wm8510_speaker_mixer_controls)), +SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_POWER3, 3, 0, + &wm8510_mono_mixer_controls[0], + ARRAY_SIZE(wm8510_mono_mixer_controls)), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0), +SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER2, 0, 0), +SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("Mic PGA", WM8510_POWER2, 2, 0, + &wm8510_micpga_controls[0], + ARRAY_SIZE(wm8510_micpga_controls)), +SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0, + &wm8510_boost_controls[0], + ARRAY_SIZE(wm8510_boost_controls)), + +SND_SOC_DAPM_MICBIAS("Mic Bias", WM8510_POWER1, 4, 0), + +SND_SOC_DAPM_INPUT("MICN"), +SND_SOC_DAPM_INPUT("MICP"), +SND_SOC_DAPM_INPUT("AUX"), +SND_SOC_DAPM_OUTPUT("MONOOUT"), +SND_SOC_DAPM_OUTPUT("SPKOUTP"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +}; + +static const struct snd_soc_dapm_route wm8510_dapm_routes[] = { + /* Mono output mixer */ + {"Mono Mixer", "PCM Playback Switch", "DAC"}, + {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, + {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, + + /* Speaker output mixer */ + {"Speaker Mixer", "PCM Playback Switch", "DAC"}, + {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, + {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, + + /* Outputs */ + {"Mono Out", NULL, "Mono Mixer"}, + {"MONOOUT", NULL, "Mono Out"}, + {"SpkN Out", NULL, "Speaker Mixer"}, + {"SpkP Out", NULL, "Speaker Mixer"}, + {"SPKOUTN", NULL, "SpkN Out"}, + {"SPKOUTP", NULL, "SpkP Out"}, + + /* Microphone PGA */ + {"Mic PGA", "MICN Switch", "MICN"}, + {"Mic PGA", "MICP Switch", "MICP"}, + { "Mic PGA", "AUX Switch", "Aux Input" }, + + /* Boost Mixer */ + {"Boost Mixer", "Mic PGA Switch", "Mic PGA"}, + {"Boost Mixer", "Mic Volume", "MICP"}, + {"Boost Mixer", "Aux Volume", "Aux Input"}, + + {"ADC", NULL, "Boost Mixer"}, +}; + +struct pll_ { + unsigned int pre_div:4; /* prescale - 1 */ + unsigned int n:4; + unsigned int k; +}; + +static struct pll_ pll_div; + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 24) * 10) + +static void pll_factors(unsigned int target, unsigned int source) +{ + unsigned long long Kpart; + unsigned int K, Ndiv, Nmod; + + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div.pre_div = 1; + Ndiv = target / source; + } else + pll_div.pre_div = 0; + + if ((Ndiv < 6) || (Ndiv > 12)) + printk(KERN_WARNING + "WM8510 N value %u outwith recommended range!d\n", + Ndiv); + + pll_div.n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div.k = K; +} + +static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + + if (freq_in == 0 || freq_out == 0) { + /* Clock CODEC directly from MCLK */ + reg = snd_soc_component_read(component, WM8510_CLOCK); + snd_soc_component_write(component, WM8510_CLOCK, reg & 0x0ff); + + /* Turn off PLL */ + reg = snd_soc_component_read(component, WM8510_POWER1); + snd_soc_component_write(component, WM8510_POWER1, reg & 0x1df); + return 0; + } + + pll_factors(freq_out*4, freq_in); + + snd_soc_component_write(component, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n); + snd_soc_component_write(component, WM8510_PLLK1, pll_div.k >> 18); + snd_soc_component_write(component, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff); + snd_soc_component_write(component, WM8510_PLLK3, pll_div.k & 0x1ff); + reg = snd_soc_component_read(component, WM8510_POWER1); + snd_soc_component_write(component, WM8510_POWER1, reg | 0x020); + + /* Run CODEC from PLL instead of MCLK */ + reg = snd_soc_component_read(component, WM8510_CLOCK); + snd_soc_component_write(component, WM8510_CLOCK, reg | 0x100); + + return 0; +} + +/* + * Configure WM8510 clock dividers. + */ +static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + + switch (div_id) { + case WM8510_OPCLKDIV: + reg = snd_soc_component_read(component, WM8510_GPIO) & 0x1cf; + snd_soc_component_write(component, WM8510_GPIO, reg | div); + break; + case WM8510_MCLKDIV: + reg = snd_soc_component_read(component, WM8510_CLOCK) & 0x11f; + snd_soc_component_write(component, WM8510_CLOCK, reg | div); + break; + case WM8510_ADCCLK: + reg = snd_soc_component_read(component, WM8510_ADC) & 0x1f7; + snd_soc_component_write(component, WM8510_ADC, reg | div); + break; + case WM8510_DACCLK: + reg = snd_soc_component_read(component, WM8510_DAC) & 0x1f7; + snd_soc_component_write(component, WM8510_DAC, reg | div); + break; + case WM8510_BCLKDIV: + reg = snd_soc_component_read(component, WM8510_CLOCK) & 0x1e3; + snd_soc_component_write(component, WM8510_CLOCK, reg | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = 0; + u16 clk = snd_soc_component_read(component, WM8510_CLOCK) & 0x1fe; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + clk |= 0x0001; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0010; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0008; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x00018; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0180; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0100; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0080; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8510_IFACE, iface); + snd_soc_component_write(component, WM8510_CLOCK, clk); + return 0; +} + +static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 iface = snd_soc_component_read(component, WM8510_IFACE) & 0x19f; + u16 adn = snd_soc_component_read(component, WM8510_ADD) & 0x1f1; + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x0020; + break; + case 24: + iface |= 0x0040; + break; + case 32: + iface |= 0x0060; + break; + } + + /* filter coefficient */ + switch (params_rate(params)) { + case 8000: + adn |= 0x5 << 1; + break; + case 11025: + adn |= 0x4 << 1; + break; + case 16000: + adn |= 0x3 << 1; + break; + case 22050: + adn |= 0x2 << 1; + break; + case 32000: + adn |= 0x1 << 1; + break; + case 44100: + case 48000: + break; + } + + snd_soc_component_write(component, WM8510_IFACE, iface); + snd_soc_component_write(component, WM8510_ADD, adn); + return 0; +} + +static int wm8510_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, WM8510_DAC) & 0xffbf; + + if (mute) + snd_soc_component_write(component, WM8510_DAC, mute_reg | 0x40); + else + snd_soc_component_write(component, WM8510_DAC, mute_reg); + return 0; +} + +/* liam need to make this lower power with dapm */ +static int wm8510_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8510_priv *wm8510 = snd_soc_component_get_drvdata(component); + u16 power1 = snd_soc_component_read(component, WM8510_POWER1) & ~0x3; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + power1 |= 0x1; /* VMID 50k */ + snd_soc_component_write(component, WM8510_POWER1, power1); + break; + + case SND_SOC_BIAS_STANDBY: + power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + regcache_sync(wm8510->regmap); + + /* Initial cap charge at VMID 5k */ + snd_soc_component_write(component, WM8510_POWER1, power1 | 0x3); + mdelay(100); + } + + power1 |= 0x2; /* VMID 500k */ + snd_soc_component_write(component, WM8510_POWER1, power1); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, WM8510_POWER1, 0); + snd_soc_component_write(component, WM8510_POWER2, 0); + snd_soc_component_write(component, WM8510_POWER3, 0); + break; + } + + return 0; +} + +#define WM8510_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8510_dai_ops = { + .hw_params = wm8510_pcm_hw_params, + .mute_stream = wm8510_mute, + .set_fmt = wm8510_set_dai_fmt, + .set_clkdiv = wm8510_set_dai_clkdiv, + .set_pll = wm8510_set_dai_pll, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8510_dai = { + .name = "wm8510-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8510_RATES, + .formats = WM8510_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = WM8510_RATES, + .formats = WM8510_FORMATS,}, + .ops = &wm8510_dai_ops, + .symmetric_rate = 1, +}; + +static int wm8510_probe(struct snd_soc_component *component) +{ + wm8510_reset(component); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8510 = { + .probe = wm8510_probe, + .set_bias_level = wm8510_set_bias_level, + .controls = wm8510_snd_controls, + .num_controls = ARRAY_SIZE(wm8510_snd_controls), + .dapm_widgets = wm8510_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8510_dapm_widgets), + .dapm_routes = wm8510_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8510_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8510_of_match[] = { + { .compatible = "wlf,wm8510" }, + { }, +}; +MODULE_DEVICE_TABLE(of, wm8510_of_match); + +static const struct regmap_config wm8510_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8510_MONOMIX, + + .reg_defaults = wm8510_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8510_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm8510_volatile, +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8510_spi_probe(struct spi_device *spi) +{ + struct wm8510_priv *wm8510; + int ret; + + wm8510 = devm_kzalloc(&spi->dev, sizeof(struct wm8510_priv), + GFP_KERNEL); + if (wm8510 == NULL) + return -ENOMEM; + + wm8510->regmap = devm_regmap_init_spi(spi, &wm8510_regmap); + if (IS_ERR(wm8510->regmap)) + return PTR_ERR(wm8510->regmap); + + spi_set_drvdata(spi, wm8510); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8510, &wm8510_dai, 1); + + return ret; +} + +static struct spi_driver wm8510_spi_driver = { + .driver = { + .name = "wm8510", + .of_match_table = wm8510_of_match, + }, + .probe = wm8510_spi_probe, +}; +#endif /* CONFIG_SPI_MASTER */ + +#if IS_ENABLED(CONFIG_I2C) +static int wm8510_i2c_probe(struct i2c_client *i2c) +{ + struct wm8510_priv *wm8510; + int ret; + + wm8510 = devm_kzalloc(&i2c->dev, sizeof(struct wm8510_priv), + GFP_KERNEL); + if (wm8510 == NULL) + return -ENOMEM; + + wm8510->regmap = devm_regmap_init_i2c(i2c, &wm8510_regmap); + if (IS_ERR(wm8510->regmap)) + return PTR_ERR(wm8510->regmap); + + i2c_set_clientdata(i2c, wm8510); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8510, &wm8510_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8510_i2c_id[] = { + { "wm8510", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id); + +static struct i2c_driver wm8510_i2c_driver = { + .driver = { + .name = "wm8510", + .of_match_table = wm8510_of_match, + }, + .probe = wm8510_i2c_probe, + .id_table = wm8510_i2c_id, +}; +#endif + +static int __init wm8510_modinit(void) +{ + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8510_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8510 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8510_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8510 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8510_modinit); + +static void __exit wm8510_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8510_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8510_spi_driver); +#endif +} +module_exit(wm8510_exit); + +MODULE_DESCRIPTION("ASoC WM8510 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h new file mode 100644 index 0000000000..1f43549473 --- /dev/null +++ b/sound/soc/codecs/wm8510.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8510.h -- WM8510 Soc Audio driver + */ + +#ifndef _WM8510_H +#define _WM8510_H + +/* WM8510 register space */ + +#define WM8510_RESET 0x0 +#define WM8510_POWER1 0x1 +#define WM8510_POWER2 0x2 +#define WM8510_POWER3 0x3 +#define WM8510_IFACE 0x4 +#define WM8510_COMP 0x5 +#define WM8510_CLOCK 0x6 +#define WM8510_ADD 0x7 +#define WM8510_GPIO 0x8 +#define WM8510_DAC 0xa +#define WM8510_DACVOL 0xb +#define WM8510_ADC 0xe +#define WM8510_ADCVOL 0xf +#define WM8510_EQ1 0x12 +#define WM8510_EQ2 0x13 +#define WM8510_EQ3 0x14 +#define WM8510_EQ4 0x15 +#define WM8510_EQ5 0x16 +#define WM8510_DACLIM1 0x18 +#define WM8510_DACLIM2 0x19 +#define WM8510_NOTCH1 0x1b +#define WM8510_NOTCH2 0x1c +#define WM8510_NOTCH3 0x1d +#define WM8510_NOTCH4 0x1e +#define WM8510_ALC1 0x20 +#define WM8510_ALC2 0x21 +#define WM8510_ALC3 0x22 +#define WM8510_NGATE 0x23 +#define WM8510_PLLN 0x24 +#define WM8510_PLLK1 0x25 +#define WM8510_PLLK2 0x26 +#define WM8510_PLLK3 0x27 +#define WM8510_ATTEN 0x28 +#define WM8510_INPUT 0x2c +#define WM8510_INPPGA 0x2d +#define WM8510_ADCBOOST 0x2f +#define WM8510_OUTPUT 0x31 +#define WM8510_SPKMIX 0x32 +#define WM8510_SPKVOL 0x36 +#define WM8510_MONOMIX 0x38 + +#define WM8510_CACHEREGNUM 57 + +/* Clock divider Id's */ +#define WM8510_OPCLKDIV 0 +#define WM8510_MCLKDIV 1 +#define WM8510_ADCCLK 2 +#define WM8510_DACCLK 3 +#define WM8510_BCLKDIV 4 + +/* DAC clock dividers */ +#define WM8510_DACCLK_F2 (1 << 3) +#define WM8510_DACCLK_F4 (0 << 3) + +/* ADC clock dividers */ +#define WM8510_ADCCLK_F2 (1 << 3) +#define WM8510_ADCCLK_F4 (0 << 3) + +/* PLL Out dividers */ +#define WM8510_OPCLKDIV_1 (0 << 4) +#define WM8510_OPCLKDIV_2 (1 << 4) +#define WM8510_OPCLKDIV_3 (2 << 4) +#define WM8510_OPCLKDIV_4 (3 << 4) + +/* BCLK clock dividers */ +#define WM8510_BCLKDIV_1 (0 << 2) +#define WM8510_BCLKDIV_2 (1 << 2) +#define WM8510_BCLKDIV_4 (2 << 2) +#define WM8510_BCLKDIV_8 (3 << 2) +#define WM8510_BCLKDIV_16 (4 << 2) +#define WM8510_BCLKDIV_32 (5 << 2) + +/* MCLK clock dividers */ +#define WM8510_MCLKDIV_1 (0 << 5) +#define WM8510_MCLKDIV_1_5 (1 << 5) +#define WM8510_MCLKDIV_2 (2 << 5) +#define WM8510_MCLKDIV_3 (3 << 5) +#define WM8510_MCLKDIV_4 (4 << 5) +#define WM8510_MCLKDIV_6 (5 << 5) +#define WM8510_MCLKDIV_8 (6 << 5) +#define WM8510_MCLKDIV_12 (7 << 5) + +struct wm8510_setup_data { + int spi; + int i2c_bus; + unsigned short i2c_address; +}; + +#endif diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c new file mode 100644 index 0000000000..ea87cd3cc0 --- /dev/null +++ b/sound/soc/codecs/wm8523.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8523.c -- WM8523 ALSA SoC Audio driver + * + * Copyright 2009 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8523.h" + +#define WM8523_NUM_SUPPLIES 2 +static const char *wm8523_supply_names[WM8523_NUM_SUPPLIES] = { + "AVDD", + "LINEVDD", +}; + +#define WM8523_NUM_RATES 7 + +/* codec private data */ +struct wm8523_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8523_NUM_SUPPLIES]; + unsigned int sysclk; + unsigned int rate_constraint_list[WM8523_NUM_RATES]; + struct snd_pcm_hw_constraint_list rate_constraint; +}; + +static const struct reg_default wm8523_reg_defaults[] = { + { 2, 0x0000 }, /* R2 - PSCTRL1 */ + { 3, 0x1812 }, /* R3 - AIF_CTRL1 */ + { 4, 0x0000 }, /* R4 - AIF_CTRL2 */ + { 5, 0x0001 }, /* R5 - DAC_CTRL3 */ + { 6, 0x0190 }, /* R6 - DAC_GAINL */ + { 7, 0x0190 }, /* R7 - DAC_GAINR */ + { 8, 0x0000 }, /* R8 - ZERO_DETECT */ +}; + +static bool wm8523_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8523_DEVICE_ID: + case WM8523_REVISION: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(dac_tlv, -10000, 25, 0); + +static const char *wm8523_zd_count_text[] = { + "1024", + "2048", +}; + +static SOC_ENUM_SINGLE_DECL(wm8523_zc_count, WM8523_ZERO_DETECT, 0, + wm8523_zd_count_text); + +static const struct snd_kcontrol_new wm8523_controls[] = { +SOC_DOUBLE_R_TLV("Playback Volume", WM8523_DAC_GAINL, WM8523_DAC_GAINR, + 0, 448, 0, dac_tlv), +SOC_SINGLE("ZC Switch", WM8523_DAC_CTRL3, 4, 1, 0), +SOC_SINGLE("Playback Deemphasis Switch", WM8523_AIF_CTRL1, 8, 1, 0), +SOC_DOUBLE("Playback Switch", WM8523_DAC_CTRL3, 2, 3, 1, 1), +SOC_SINGLE("Volume Ramp Up Switch", WM8523_DAC_CTRL3, 1, 1, 0), +SOC_SINGLE("Volume Ramp Down Switch", WM8523_DAC_CTRL3, 0, 1, 0), +SOC_ENUM("Zero Detect Count", wm8523_zc_count), +}; + +static const struct snd_soc_dapm_widget wm8523_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("LINEVOUTL"), +SND_SOC_DAPM_OUTPUT("LINEVOUTR"), +}; + +static const struct snd_soc_dapm_route wm8523_dapm_routes[] = { + { "LINEVOUTL", NULL, "DAC" }, + { "LINEVOUTR", NULL, "DAC" }, +}; + +static const struct { + int value; + int ratio; +} lrclk_ratios[WM8523_NUM_RATES] = { + { 1, 128 }, + { 2, 192 }, + { 3, 256 }, + { 4, 384 }, + { 5, 512 }, + { 6, 768 }, + { 7, 1152 }, +}; + +static const struct { + int value; + int ratio; +} bclk_ratios[] = { + { 2, 32 }, + { 3, 64 }, + { 4, 128 }, +}; + +static int wm8523_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8523_priv *wm8523 = snd_soc_component_get_drvdata(component); + + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + if (!wm8523->sysclk) { + dev_err(component->dev, + "No MCLK configured, call set_sysclk() on init\n"); + return -EINVAL; + } + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &wm8523->rate_constraint); + + return 0; +} + +static int wm8523_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8523_priv *wm8523 = snd_soc_component_get_drvdata(component); + int i; + u16 aifctrl1 = snd_soc_component_read(component, WM8523_AIF_CTRL1); + u16 aifctrl2 = snd_soc_component_read(component, WM8523_AIF_CTRL2); + + /* Find a supported LRCLK ratio */ + for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { + if (wm8523->sysclk / params_rate(params) == + lrclk_ratios[i].ratio) + break; + } + + /* Should never happen, should be handled by constraints */ + if (i == ARRAY_SIZE(lrclk_ratios)) { + dev_err(component->dev, "MCLK/fs ratio %d unsupported\n", + wm8523->sysclk / params_rate(params)); + return -EINVAL; + } + + aifctrl2 &= ~WM8523_SR_MASK; + aifctrl2 |= lrclk_ratios[i].value; + + if (aifctrl1 & WM8523_AIF_MSTR) { + /* Find a fs->bclk ratio */ + for (i = 0; i < ARRAY_SIZE(bclk_ratios); i++) + if (params_width(params) * 2 <= bclk_ratios[i].ratio) + break; + + if (i == ARRAY_SIZE(bclk_ratios)) { + dev_err(component->dev, + "No matching BCLK/fs ratio for word length %d\n", + params_width(params)); + return -EINVAL; + } + + aifctrl2 &= ~WM8523_BCLKDIV_MASK; + aifctrl2 |= bclk_ratios[i].value << WM8523_BCLKDIV_SHIFT; + } + + aifctrl1 &= ~WM8523_WL_MASK; + switch (params_width(params)) { + case 16: + break; + case 20: + aifctrl1 |= 0x8; + break; + case 24: + aifctrl1 |= 0x10; + break; + case 32: + aifctrl1 |= 0x18; + break; + } + + snd_soc_component_write(component, WM8523_AIF_CTRL1, aifctrl1); + snd_soc_component_write(component, WM8523_AIF_CTRL2, aifctrl2); + + return 0; +} + +static int wm8523_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8523_priv *wm8523 = snd_soc_component_get_drvdata(component); + unsigned int val; + int i; + + wm8523->sysclk = freq; + + wm8523->rate_constraint.count = 0; + for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { + val = freq / lrclk_ratios[i].ratio; + /* Check that it's a standard rate since core can't + * cope with others and having the odd rates confuses + * constraint matching. + */ + switch (val) { + case 8000: + case 11025: + case 16000: + case 22050: + case 32000: + case 44100: + case 48000: + case 64000: + case 88200: + case 96000: + case 176400: + case 192000: + dev_dbg(component->dev, "Supported sample rate: %dHz\n", + val); + wm8523->rate_constraint_list[i] = val; + wm8523->rate_constraint.count++; + break; + default: + dev_dbg(component->dev, "Skipping sample rate: %dHz\n", + val); + } + } + + /* Need at least one supported rate... */ + if (wm8523->rate_constraint.count == 0) + return -EINVAL; + + return 0; +} + + +static int wm8523_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 aifctrl1 = snd_soc_component_read(component, WM8523_AIF_CTRL1); + + aifctrl1 &= ~(WM8523_BCLK_INV_MASK | WM8523_LRCLK_INV_MASK | + WM8523_FMT_MASK | WM8523_AIF_MSTR_MASK); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aifctrl1 |= WM8523_AIF_MSTR; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + aifctrl1 |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aifctrl1 |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + aifctrl1 |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + aifctrl1 |= 0x0023; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aifctrl1 |= WM8523_BCLK_INV | WM8523_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aifctrl1 |= WM8523_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aifctrl1 |= WM8523_LRCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8523_AIF_CTRL1, aifctrl1); + + return 0; +} + +static int wm8523_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8523_priv *wm8523 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* Full power on */ + snd_soc_component_update_bits(component, WM8523_PSCTRL1, + WM8523_SYS_ENA_MASK, 3); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies), + wm8523->supplies); + if (ret != 0) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + + /* Sync back default/cached values */ + regcache_sync(wm8523->regmap); + + /* Initial power up */ + snd_soc_component_update_bits(component, WM8523_PSCTRL1, + WM8523_SYS_ENA_MASK, 1); + + msleep(100); + } + + /* Power up to mute */ + snd_soc_component_update_bits(component, WM8523_PSCTRL1, + WM8523_SYS_ENA_MASK, 2); + + break; + + case SND_SOC_BIAS_OFF: + /* The chip runs through the power down sequence for us. */ + snd_soc_component_update_bits(component, WM8523_PSCTRL1, + WM8523_SYS_ENA_MASK, 0); + msleep(100); + + regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), + wm8523->supplies); + break; + } + return 0; +} + +#define WM8523_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM8523_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8523_dai_ops = { + .startup = wm8523_startup, + .hw_params = wm8523_hw_params, + .set_sysclk = wm8523_set_dai_sysclk, + .set_fmt = wm8523_set_dai_fmt, +}; + +static struct snd_soc_dai_driver wm8523_dai = { + .name = "wm8523-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, /* Mono modes not yet supported */ + .channels_max = 2, + .rates = WM8523_RATES, + .formats = WM8523_FORMATS, + }, + .ops = &wm8523_dai_ops, +}; + +static int wm8523_probe(struct snd_soc_component *component) +{ + struct wm8523_priv *wm8523 = snd_soc_component_get_drvdata(component); + + wm8523->rate_constraint.list = &wm8523->rate_constraint_list[0]; + wm8523->rate_constraint.count = + ARRAY_SIZE(wm8523->rate_constraint_list); + + /* Change some default settings - latch VU and enable ZC */ + snd_soc_component_update_bits(component, WM8523_DAC_GAINR, + WM8523_DACR_VU, WM8523_DACR_VU); + snd_soc_component_update_bits(component, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8523 = { + .probe = wm8523_probe, + .set_bias_level = wm8523_set_bias_level, + .controls = wm8523_controls, + .num_controls = ARRAY_SIZE(wm8523_controls), + .dapm_widgets = wm8523_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8523_dapm_widgets), + .dapm_routes = wm8523_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8523_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8523_of_match[] = { + { .compatible = "wlf,wm8523" }, + { }, +}; +MODULE_DEVICE_TABLE(of, wm8523_of_match); + +static const struct regmap_config wm8523_regmap = { + .reg_bits = 8, + .val_bits = 16, + .max_register = WM8523_ZERO_DETECT, + + .reg_defaults = wm8523_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8523_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm8523_volatile_register, +}; + +static int wm8523_i2c_probe(struct i2c_client *i2c) +{ + struct wm8523_priv *wm8523; + unsigned int val; + int ret, i; + + wm8523 = devm_kzalloc(&i2c->dev, sizeof(struct wm8523_priv), + GFP_KERNEL); + if (wm8523 == NULL) + return -ENOMEM; + + wm8523->regmap = devm_regmap_init_i2c(i2c, &wm8523_regmap); + if (IS_ERR(wm8523->regmap)) { + ret = PTR_ERR(wm8523->regmap); + dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(wm8523->supplies); i++) + wm8523->supplies[i].supply = wm8523_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8523->supplies), + wm8523->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies), + wm8523->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = regmap_read(wm8523->regmap, WM8523_DEVICE_ID, &val); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID register\n"); + goto err_enable; + } + if (val != 0x8523) { + dev_err(&i2c->dev, "Device is not a WM8523, ID is %x\n", ret); + ret = -EINVAL; + goto err_enable; + } + + ret = regmap_read(wm8523->regmap, WM8523_REVISION, &val); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read revision register\n"); + goto err_enable; + } + dev_info(&i2c->dev, "revision %c\n", + (val & WM8523_CHIP_REV_MASK) + 'A'); + + ret = regmap_write(wm8523->regmap, WM8523_DEVICE_ID, 0x8523); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to reset device: %d\n", ret); + goto err_enable; + } + + regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); + + i2c_set_clientdata(i2c, wm8523); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8523, &wm8523_dai, 1); + + return ret; + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies); + return ret; +} + +static const struct i2c_device_id wm8523_i2c_id[] = { + { "wm8523", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id); + +static struct i2c_driver wm8523_i2c_driver = { + .driver = { + .name = "wm8523", + .of_match_table = wm8523_of_match, + }, + .probe = wm8523_i2c_probe, + .id_table = wm8523_i2c_id, +}; + +module_i2c_driver(wm8523_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8523 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8523.h b/sound/soc/codecs/wm8523.h new file mode 100644 index 0000000000..5f9bb3df18 --- /dev/null +++ b/sound/soc/codecs/wm8523.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8523.h -- WM8523 ASoC driver + * + * Copyright 2009 Wolfson Microelectronics, plc + * + * Author: Mark Brown + * + * Based on wm8753.h + */ + +#ifndef _WM8523_H +#define _WM8523_H + +/* + * Register values. + */ +#define WM8523_DEVICE_ID 0x00 +#define WM8523_REVISION 0x01 +#define WM8523_PSCTRL1 0x02 +#define WM8523_AIF_CTRL1 0x03 +#define WM8523_AIF_CTRL2 0x04 +#define WM8523_DAC_CTRL3 0x05 +#define WM8523_DAC_GAINL 0x06 +#define WM8523_DAC_GAINR 0x07 +#define WM8523_ZERO_DETECT 0x08 + +#define WM8523_REGISTER_COUNT 9 +#define WM8523_MAX_REGISTER 0x08 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - DEVICE_ID + */ +#define WM8523_CHIP_ID_MASK 0xFFFF /* CHIP_ID - [15:0] */ +#define WM8523_CHIP_ID_SHIFT 0 /* CHIP_ID - [15:0] */ +#define WM8523_CHIP_ID_WIDTH 16 /* CHIP_ID - [15:0] */ + +/* + * R1 (0x01) - REVISION + */ +#define WM8523_CHIP_REV_MASK 0x0007 /* CHIP_REV - [2:0] */ +#define WM8523_CHIP_REV_SHIFT 0 /* CHIP_REV - [2:0] */ +#define WM8523_CHIP_REV_WIDTH 3 /* CHIP_REV - [2:0] */ + +/* + * R2 (0x02) - PSCTRL1 + */ +#define WM8523_SYS_ENA_MASK 0x0003 /* SYS_ENA - [1:0] */ +#define WM8523_SYS_ENA_SHIFT 0 /* SYS_ENA - [1:0] */ +#define WM8523_SYS_ENA_WIDTH 2 /* SYS_ENA - [1:0] */ + +/* + * R3 (0x03) - AIF_CTRL1 + */ +#define WM8523_TDM_MODE_MASK 0x1800 /* TDM_MODE - [12:11] */ +#define WM8523_TDM_MODE_SHIFT 11 /* TDM_MODE - [12:11] */ +#define WM8523_TDM_MODE_WIDTH 2 /* TDM_MODE - [12:11] */ +#define WM8523_TDM_SLOT_MASK 0x0600 /* TDM_SLOT - [10:9] */ +#define WM8523_TDM_SLOT_SHIFT 9 /* TDM_SLOT - [10:9] */ +#define WM8523_TDM_SLOT_WIDTH 2 /* TDM_SLOT - [10:9] */ +#define WM8523_DEEMPH 0x0100 /* DEEMPH */ +#define WM8523_DEEMPH_MASK 0x0100 /* DEEMPH */ +#define WM8523_DEEMPH_SHIFT 8 /* DEEMPH */ +#define WM8523_DEEMPH_WIDTH 1 /* DEEMPH */ +#define WM8523_AIF_MSTR 0x0080 /* AIF_MSTR */ +#define WM8523_AIF_MSTR_MASK 0x0080 /* AIF_MSTR */ +#define WM8523_AIF_MSTR_SHIFT 7 /* AIF_MSTR */ +#define WM8523_AIF_MSTR_WIDTH 1 /* AIF_MSTR */ +#define WM8523_LRCLK_INV 0x0040 /* LRCLK_INV */ +#define WM8523_LRCLK_INV_MASK 0x0040 /* LRCLK_INV */ +#define WM8523_LRCLK_INV_SHIFT 6 /* LRCLK_INV */ +#define WM8523_LRCLK_INV_WIDTH 1 /* LRCLK_INV */ +#define WM8523_BCLK_INV 0x0020 /* BCLK_INV */ +#define WM8523_BCLK_INV_MASK 0x0020 /* BCLK_INV */ +#define WM8523_BCLK_INV_SHIFT 5 /* BCLK_INV */ +#define WM8523_BCLK_INV_WIDTH 1 /* BCLK_INV */ +#define WM8523_WL_MASK 0x0018 /* WL - [4:3] */ +#define WM8523_WL_SHIFT 3 /* WL - [4:3] */ +#define WM8523_WL_WIDTH 2 /* WL - [4:3] */ +#define WM8523_FMT_MASK 0x0007 /* FMT - [2:0] */ +#define WM8523_FMT_SHIFT 0 /* FMT - [2:0] */ +#define WM8523_FMT_WIDTH 3 /* FMT - [2:0] */ + +/* + * R4 (0x04) - AIF_CTRL2 + */ +#define WM8523_DAC_OP_MUX_MASK 0x00C0 /* DAC_OP_MUX - [7:6] */ +#define WM8523_DAC_OP_MUX_SHIFT 6 /* DAC_OP_MUX - [7:6] */ +#define WM8523_DAC_OP_MUX_WIDTH 2 /* DAC_OP_MUX - [7:6] */ +#define WM8523_BCLKDIV_MASK 0x0038 /* BCLKDIV - [5:3] */ +#define WM8523_BCLKDIV_SHIFT 3 /* BCLKDIV - [5:3] */ +#define WM8523_BCLKDIV_WIDTH 3 /* BCLKDIV - [5:3] */ +#define WM8523_SR_MASK 0x0007 /* SR - [2:0] */ +#define WM8523_SR_SHIFT 0 /* SR - [2:0] */ +#define WM8523_SR_WIDTH 3 /* SR - [2:0] */ + +/* + * R5 (0x05) - DAC_CTRL3 + */ +#define WM8523_ZC 0x0010 /* ZC */ +#define WM8523_ZC_MASK 0x0010 /* ZC */ +#define WM8523_ZC_SHIFT 4 /* ZC */ +#define WM8523_ZC_WIDTH 1 /* ZC */ +#define WM8523_DACR 0x0008 /* DACR */ +#define WM8523_DACR_MASK 0x0008 /* DACR */ +#define WM8523_DACR_SHIFT 3 /* DACR */ +#define WM8523_DACR_WIDTH 1 /* DACR */ +#define WM8523_DACL 0x0004 /* DACL */ +#define WM8523_DACL_MASK 0x0004 /* DACL */ +#define WM8523_DACL_SHIFT 2 /* DACL */ +#define WM8523_DACL_WIDTH 1 /* DACL */ +#define WM8523_VOL_UP_RAMP 0x0002 /* VOL_UP_RAMP */ +#define WM8523_VOL_UP_RAMP_MASK 0x0002 /* VOL_UP_RAMP */ +#define WM8523_VOL_UP_RAMP_SHIFT 1 /* VOL_UP_RAMP */ +#define WM8523_VOL_UP_RAMP_WIDTH 1 /* VOL_UP_RAMP */ +#define WM8523_VOL_DOWN_RAMP 0x0001 /* VOL_DOWN_RAMP */ +#define WM8523_VOL_DOWN_RAMP_MASK 0x0001 /* VOL_DOWN_RAMP */ +#define WM8523_VOL_DOWN_RAMP_SHIFT 0 /* VOL_DOWN_RAMP */ +#define WM8523_VOL_DOWN_RAMP_WIDTH 1 /* VOL_DOWN_RAMP */ + +/* + * R6 (0x06) - DAC_GAINL + */ +#define WM8523_DACL_VU 0x0200 /* DACL_VU */ +#define WM8523_DACL_VU_MASK 0x0200 /* DACL_VU */ +#define WM8523_DACL_VU_SHIFT 9 /* DACL_VU */ +#define WM8523_DACL_VU_WIDTH 1 /* DACL_VU */ +#define WM8523_DACL_VOL_MASK 0x01FF /* DACL_VOL - [8:0] */ +#define WM8523_DACL_VOL_SHIFT 0 /* DACL_VOL - [8:0] */ +#define WM8523_DACL_VOL_WIDTH 9 /* DACL_VOL - [8:0] */ + +/* + * R7 (0x07) - DAC_GAINR + */ +#define WM8523_DACR_VU 0x0200 /* DACR_VU */ +#define WM8523_DACR_VU_MASK 0x0200 /* DACR_VU */ +#define WM8523_DACR_VU_SHIFT 9 /* DACR_VU */ +#define WM8523_DACR_VU_WIDTH 1 /* DACR_VU */ +#define WM8523_DACR_VOL_MASK 0x01FF /* DACR_VOL - [8:0] */ +#define WM8523_DACR_VOL_SHIFT 0 /* DACR_VOL - [8:0] */ +#define WM8523_DACR_VOL_WIDTH 9 /* DACR_VOL - [8:0] */ + +/* + * R8 (0x08) - ZERO_DETECT + */ +#define WM8523_ZD_COUNT_MASK 0x0003 /* ZD_COUNT - [1:0] */ +#define WM8523_ZD_COUNT_SHIFT 0 /* ZD_COUNT - [1:0] */ +#define WM8523_ZD_COUNT_WIDTH 2 /* ZD_COUNT - [1:0] */ + +#endif diff --git a/sound/soc/codecs/wm8524.c b/sound/soc/codecs/wm8524.c new file mode 100644 index 0000000000..b56dcac602 --- /dev/null +++ b/sound/soc/codecs/wm8524.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8524.c -- WM8524 ALSA SoC Audio driver + * + * Copyright 2009 Wolfson Microelectronics plc + * Copyright 2017 NXP + * + * Based on WM8523 ALSA SoC Audio driver written by Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WM8524_NUM_RATES 7 + +/* codec private data */ +struct wm8524_priv { + struct gpio_desc *mute; + unsigned int sysclk; + unsigned int rate_constraint_list[WM8524_NUM_RATES]; + struct snd_pcm_hw_constraint_list rate_constraint; +}; + + +static const struct snd_soc_dapm_widget wm8524_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("LINEVOUTL"), +SND_SOC_DAPM_OUTPUT("LINEVOUTR"), +}; + +static const struct snd_soc_dapm_route wm8524_dapm_routes[] = { + { "LINEVOUTL", NULL, "DAC" }, + { "LINEVOUTR", NULL, "DAC" }, +}; + +static const struct { + int value; + int ratio; +} lrclk_ratios[WM8524_NUM_RATES] = { + { 1, 128 }, + { 2, 192 }, + { 3, 256 }, + { 4, 384 }, + { 5, 512 }, + { 6, 768 }, + { 7, 1152 }, +}; + +static int wm8524_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component); + + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + if (!wm8524->sysclk) { + dev_err(component->dev, + "No MCLK configured, call set_sysclk() on init\n"); + return -EINVAL; + } + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &wm8524->rate_constraint); + + gpiod_set_value_cansleep(wm8524->mute, 1); + + return 0; +} + +static void wm8524_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component); + + gpiod_set_value_cansleep(wm8524->mute, 0); +} + +static int wm8524_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component); + unsigned int val; + int i, j = 0; + + wm8524->sysclk = freq; + + wm8524->rate_constraint.count = 0; + for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { + val = freq / lrclk_ratios[i].ratio; + /* Check that it's a standard rate since core can't + * cope with others and having the odd rates confuses + * constraint matching. + */ + switch (val) { + case 8000: + case 32000: + case 44100: + case 48000: + case 88200: + case 96000: + case 176400: + case 192000: + dev_dbg(component->dev, "Supported sample rate: %dHz\n", + val); + wm8524->rate_constraint_list[j++] = val; + wm8524->rate_constraint.count++; + break; + default: + dev_dbg(component->dev, "Skipping sample rate: %dHz\n", + val); + } + } + + /* Need at least one supported rate... */ + if (wm8524->rate_constraint.count == 0) + return -EINVAL; + + return 0; +} + +static int wm8524_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK | + SND_SOC_DAIFMT_MASTER_MASK); + + if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS)) { + dev_err(codec_dai->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + return 0; +} + +static int wm8524_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(dai->component); + + if (wm8524->mute) + gpiod_set_value_cansleep(wm8524->mute, mute); + + return 0; +} + +#define WM8524_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8524_dai_ops = { + .startup = wm8524_startup, + .shutdown = wm8524_shutdown, + .set_sysclk = wm8524_set_dai_sysclk, + .set_fmt = wm8524_set_fmt, + .mute_stream = wm8524_mute_stream, +}; + +static struct snd_soc_dai_driver wm8524_dai = { + .name = "wm8524-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8524_RATES, + .formats = WM8524_FORMATS, + }, + .ops = &wm8524_dai_ops, +}; + +static int wm8524_probe(struct snd_soc_component *component) +{ + struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component); + + wm8524->rate_constraint.list = &wm8524->rate_constraint_list[0]; + wm8524->rate_constraint.count = + ARRAY_SIZE(wm8524->rate_constraint_list); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8524 = { + .probe = wm8524_probe, + .dapm_widgets = wm8524_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8524_dapm_widgets), + .dapm_routes = wm8524_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8524_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8524_of_match[] = { + { .compatible = "wlf,wm8524" }, + { /* sentinel*/ } +}; +MODULE_DEVICE_TABLE(of, wm8524_of_match); + +static int wm8524_codec_probe(struct platform_device *pdev) +{ + struct wm8524_priv *wm8524; + int ret; + + wm8524 = devm_kzalloc(&pdev->dev, sizeof(struct wm8524_priv), + GFP_KERNEL); + if (wm8524 == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, wm8524); + + wm8524->mute = devm_gpiod_get(&pdev->dev, "wlf,mute", GPIOD_OUT_LOW); + if (IS_ERR(wm8524->mute)) { + ret = PTR_ERR(wm8524->mute); + dev_err_probe(&pdev->dev, ret, "Failed to get mute line\n"); + return ret; + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm8524, &wm8524_dai, 1); + if (ret < 0) + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + + return ret; +} + +static struct platform_driver wm8524_codec_driver = { + .probe = wm8524_codec_probe, + .driver = { + .name = "wm8524-codec", + .of_match_table = wm8524_of_match, + }, +}; +module_platform_driver(wm8524_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8524 driver"); +MODULE_AUTHOR("Mihai Serban "); +MODULE_ALIAS("platform:wm8524-codec"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c new file mode 100644 index 0000000000..6d22f7d40e --- /dev/null +++ b/sound/soc/codecs/wm8580.c @@ -0,0 +1,1061 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * wm8580.c -- WM8580 and WM8581 ALSA Soc Audio driver + * + * Copyright 2008-12 Wolfson Microelectronics PLC. + * + * Notes: + * The WM8580 is a multichannel codec with S/PDIF support, featuring six + * DAC channels and two ADC channels. + * + * The WM8581 is a multichannel codec with S/PDIF support, featuring eight + * DAC channels and two ADC channels. + * + * Currently only the primary audio interface is supported - S/PDIF and + * the secondary audio interfaces are not. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "wm8580.h" + +/* WM8580 register space */ +#define WM8580_PLLA1 0x00 +#define WM8580_PLLA2 0x01 +#define WM8580_PLLA3 0x02 +#define WM8580_PLLA4 0x03 +#define WM8580_PLLB1 0x04 +#define WM8580_PLLB2 0x05 +#define WM8580_PLLB3 0x06 +#define WM8580_PLLB4 0x07 +#define WM8580_CLKSEL 0x08 +#define WM8580_PAIF1 0x09 +#define WM8580_PAIF2 0x0A +#define WM8580_SAIF1 0x0B +#define WM8580_PAIF3 0x0C +#define WM8580_PAIF4 0x0D +#define WM8580_SAIF2 0x0E +#define WM8580_DAC_CONTROL1 0x0F +#define WM8580_DAC_CONTROL2 0x10 +#define WM8580_DAC_CONTROL3 0x11 +#define WM8580_DAC_CONTROL4 0x12 +#define WM8580_DAC_CONTROL5 0x13 +#define WM8580_DIGITAL_ATTENUATION_DACL1 0x14 +#define WM8580_DIGITAL_ATTENUATION_DACR1 0x15 +#define WM8580_DIGITAL_ATTENUATION_DACL2 0x16 +#define WM8580_DIGITAL_ATTENUATION_DACR2 0x17 +#define WM8580_DIGITAL_ATTENUATION_DACL3 0x18 +#define WM8580_DIGITAL_ATTENUATION_DACR3 0x19 +#define WM8581_DIGITAL_ATTENUATION_DACL4 0x1A +#define WM8581_DIGITAL_ATTENUATION_DACR4 0x1B +#define WM8580_MASTER_DIGITAL_ATTENUATION 0x1C +#define WM8580_ADC_CONTROL1 0x1D +#define WM8580_SPDTXCHAN0 0x1E +#define WM8580_SPDTXCHAN1 0x1F +#define WM8580_SPDTXCHAN2 0x20 +#define WM8580_SPDTXCHAN3 0x21 +#define WM8580_SPDTXCHAN4 0x22 +#define WM8580_SPDTXCHAN5 0x23 +#define WM8580_SPDMODE 0x24 +#define WM8580_INTMASK 0x25 +#define WM8580_GPO1 0x26 +#define WM8580_GPO2 0x27 +#define WM8580_GPO3 0x28 +#define WM8580_GPO4 0x29 +#define WM8580_GPO5 0x2A +#define WM8580_INTSTAT 0x2B +#define WM8580_SPDRXCHAN1 0x2C +#define WM8580_SPDRXCHAN2 0x2D +#define WM8580_SPDRXCHAN3 0x2E +#define WM8580_SPDRXCHAN4 0x2F +#define WM8580_SPDRXCHAN5 0x30 +#define WM8580_SPDSTAT 0x31 +#define WM8580_PWRDN1 0x32 +#define WM8580_PWRDN2 0x33 +#define WM8580_READBACK 0x34 +#define WM8580_RESET 0x35 + +#define WM8580_MAX_REGISTER 0x35 + +#define WM8580_DACOSR 0x40 + +/* PLLB4 (register 7h) */ +#define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60 +#define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20 +#define WM8580_PLLB4_MCLKOUTSRC_PLLB 0x40 +#define WM8580_PLLB4_MCLKOUTSRC_OSC 0x60 + +#define WM8580_PLLB4_CLKOUTSRC_MASK 0x180 +#define WM8580_PLLB4_CLKOUTSRC_PLLACLK 0x080 +#define WM8580_PLLB4_CLKOUTSRC_PLLBCLK 0x100 +#define WM8580_PLLB4_CLKOUTSRC_OSCCLK 0x180 + +/* CLKSEL (register 8h) */ +#define WM8580_CLKSEL_DAC_CLKSEL_MASK 0x03 +#define WM8580_CLKSEL_DAC_CLKSEL_PLLA 0x01 +#define WM8580_CLKSEL_DAC_CLKSEL_PLLB 0x02 + +/* AIF control 1 (registers 9h-bh) */ +#define WM8580_AIF_RATE_MASK 0x7 +#define WM8580_AIF_BCLKSEL_MASK 0x18 + +#define WM8580_AIF_MS 0x20 + +#define WM8580_AIF_CLKSRC_MASK 0xc0 +#define WM8580_AIF_CLKSRC_PLLA 0x40 +#define WM8580_AIF_CLKSRC_PLLB 0x40 +#define WM8580_AIF_CLKSRC_MCLK 0xc0 + +/* AIF control 2 (registers ch-eh) */ +#define WM8580_AIF_FMT_MASK 0x03 +#define WM8580_AIF_FMT_RIGHTJ 0x00 +#define WM8580_AIF_FMT_LEFTJ 0x01 +#define WM8580_AIF_FMT_I2S 0x02 +#define WM8580_AIF_FMT_DSP 0x03 + +#define WM8580_AIF_LENGTH_MASK 0x0c +#define WM8580_AIF_LENGTH_16 0x00 +#define WM8580_AIF_LENGTH_20 0x04 +#define WM8580_AIF_LENGTH_24 0x08 +#define WM8580_AIF_LENGTH_32 0x0c + +#define WM8580_AIF_LRP 0x10 +#define WM8580_AIF_BCP 0x20 + +/* Powerdown Register 1 (register 32h) */ +#define WM8580_PWRDN1_PWDN 0x001 +#define WM8580_PWRDN1_ALLDACPD 0x040 + +/* Powerdown Register 2 (register 33h) */ +#define WM8580_PWRDN2_OSSCPD 0x001 +#define WM8580_PWRDN2_PLLAPD 0x002 +#define WM8580_PWRDN2_PLLBPD 0x004 +#define WM8580_PWRDN2_SPDIFPD 0x008 +#define WM8580_PWRDN2_SPDIFTXD 0x010 +#define WM8580_PWRDN2_SPDIFRXD 0x020 + +#define WM8580_DAC_CONTROL5_MUTEALL 0x10 + +/* + * wm8580 register cache + * We can't read the WM8580 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const struct reg_default wm8580_reg_defaults[] = { + { 0, 0x0121 }, + { 1, 0x017e }, + { 2, 0x007d }, + { 3, 0x0014 }, + { 4, 0x0121 }, + { 5, 0x017e }, + { 6, 0x007d }, + { 7, 0x0194 }, + { 8, 0x0010 }, + { 9, 0x0002 }, + { 10, 0x0002 }, + { 11, 0x00c2 }, + { 12, 0x0182 }, + { 13, 0x0082 }, + { 14, 0x000a }, + { 15, 0x0024 }, + { 16, 0x0009 }, + { 17, 0x0000 }, + { 18, 0x00ff }, + { 19, 0x0000 }, + { 20, 0x00ff }, + { 21, 0x00ff }, + { 22, 0x00ff }, + { 23, 0x00ff }, + { 24, 0x00ff }, + { 25, 0x00ff }, + { 26, 0x00ff }, + { 27, 0x00ff }, + { 28, 0x01f0 }, + { 29, 0x0040 }, + { 30, 0x0000 }, + { 31, 0x0000 }, + { 32, 0x0000 }, + { 33, 0x0000 }, + { 34, 0x0031 }, + { 35, 0x000b }, + { 36, 0x0039 }, + { 37, 0x0000 }, + { 38, 0x0010 }, + { 39, 0x0032 }, + { 40, 0x0054 }, + { 41, 0x0076 }, + { 42, 0x0098 }, + { 43, 0x0000 }, + { 44, 0x0000 }, + { 45, 0x0000 }, + { 46, 0x0000 }, + { 47, 0x0000 }, + { 48, 0x0000 }, + { 49, 0x0000 }, + { 50, 0x005e }, + { 51, 0x003e }, + { 52, 0x0000 }, +}; + +static bool wm8580_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8580_RESET: + return true; + default: + return false; + } +} + +struct pll_state { + unsigned int in; + unsigned int out; +}; + +#define WM8580_NUM_SUPPLIES 3 +static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = { + "AVDD", + "DVDD", + "PVDD", +}; + +struct wm8580_driver_data { + int num_dacs; +}; + +/* codec private data */ +struct wm8580_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES]; + struct pll_state a; + struct pll_state b; + const struct wm8580_driver_data *drvdata; + int sysclk[2]; +}; + +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); + +static int wm8580_out_vu(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); + unsigned int reg = mc->reg; + unsigned int reg2 = mc->rreg; + int ret; + + /* Clear the register cache VU so we write without VU set */ + regcache_cache_only(wm8580->regmap, true); + regmap_update_bits(wm8580->regmap, reg, 0x100, 0x000); + regmap_update_bits(wm8580->regmap, reg2, 0x100, 0x000); + regcache_cache_only(wm8580->regmap, false); + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + /* Now write again with the volume update bit set */ + snd_soc_component_update_bits(component, reg, 0x100, 0x100); + snd_soc_component_update_bits(component, reg2, 0x100, 0x100); + + return 0; +} + +static const struct snd_kcontrol_new wm8580_snd_controls[] = { +SOC_DOUBLE_R_EXT_TLV("DAC1 Playback Volume", + WM8580_DIGITAL_ATTENUATION_DACL1, + WM8580_DIGITAL_ATTENUATION_DACR1, + 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), +SOC_DOUBLE_R_EXT_TLV("DAC2 Playback Volume", + WM8580_DIGITAL_ATTENUATION_DACL2, + WM8580_DIGITAL_ATTENUATION_DACR2, + 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), +SOC_DOUBLE_R_EXT_TLV("DAC3 Playback Volume", + WM8580_DIGITAL_ATTENUATION_DACL3, + WM8580_DIGITAL_ATTENUATION_DACR3, + 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), + +SOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0), +SOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0), +SOC_SINGLE("DAC3 Deemphasis Switch", WM8580_DAC_CONTROL3, 2, 1, 0), + +SOC_DOUBLE("DAC1 Invert Switch", WM8580_DAC_CONTROL4, 0, 1, 1, 0), +SOC_DOUBLE("DAC2 Invert Switch", WM8580_DAC_CONTROL4, 2, 3, 1, 0), +SOC_DOUBLE("DAC3 Invert Switch", WM8580_DAC_CONTROL4, 4, 5, 1, 0), + +SOC_SINGLE("DAC ZC Switch", WM8580_DAC_CONTROL5, 5, 1, 0), +SOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 1), +SOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 1), +SOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 1), + +SOC_DOUBLE("Capture Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 1), +SOC_SINGLE("Capture High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), +}; + +static const struct snd_kcontrol_new wm8581_snd_controls[] = { +SOC_DOUBLE_R_EXT_TLV("DAC4 Playback Volume", + WM8581_DIGITAL_ATTENUATION_DACL4, + WM8581_DIGITAL_ATTENUATION_DACR4, + 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), + +SOC_SINGLE("DAC4 Deemphasis Switch", WM8580_DAC_CONTROL3, 3, 1, 0), + +SOC_DOUBLE("DAC4 Invert Switch", WM8580_DAC_CONTROL4, 8, 7, 1, 0), + +SOC_SINGLE("DAC4 Switch", WM8580_DAC_CONTROL5, 3, 1, 1), +}; + +static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1), +SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1), +SND_SOC_DAPM_DAC("DAC3", "Playback", WM8580_PWRDN1, 4, 1), + +SND_SOC_DAPM_OUTPUT("VOUT1L"), +SND_SOC_DAPM_OUTPUT("VOUT1R"), +SND_SOC_DAPM_OUTPUT("VOUT2L"), +SND_SOC_DAPM_OUTPUT("VOUT2R"), +SND_SOC_DAPM_OUTPUT("VOUT3L"), +SND_SOC_DAPM_OUTPUT("VOUT3R"), + +SND_SOC_DAPM_ADC("ADC", "Capture", WM8580_PWRDN1, 1, 1), + +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), +}; + +static const struct snd_soc_dapm_widget wm8581_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC4", "Playback", WM8580_PWRDN1, 5, 1), + +SND_SOC_DAPM_OUTPUT("VOUT4L"), +SND_SOC_DAPM_OUTPUT("VOUT4R"), +}; + +static const struct snd_soc_dapm_route wm8580_dapm_routes[] = { + { "VOUT1L", NULL, "DAC1" }, + { "VOUT1R", NULL, "DAC1" }, + + { "VOUT2L", NULL, "DAC2" }, + { "VOUT2R", NULL, "DAC2" }, + + { "VOUT3L", NULL, "DAC3" }, + { "VOUT3R", NULL, "DAC3" }, + + { "ADC", NULL, "AINL" }, + { "ADC", NULL, "AINR" }, +}; + +static const struct snd_soc_dapm_route wm8581_dapm_routes[] = { + { "VOUT4L", NULL, "DAC4" }, + { "VOUT4R", NULL, "DAC4" }, +}; + +/* PLL divisors */ +struct _pll_div { + u32 prescale:1; + u32 postscale:1; + u32 freqmode:2; + u32 n:4; + u32 k:24; +}; + +/* The size in bits of the pll divide */ +#define FIXED_PLL_SIZE (1 << 22) + +/* PLL rate to output rate divisions */ +static struct { + unsigned int div; + unsigned int freqmode; + unsigned int postscale; +} post_table[] = { + { 2, 0, 0 }, + { 4, 0, 1 }, + { 4, 1, 0 }, + { 8, 1, 1 }, + { 8, 2, 0 }, + { 16, 2, 1 }, + { 12, 3, 0 }, + { 24, 3, 1 } +}; + +static int pll_factors(struct _pll_div *pll_div, unsigned int target, + unsigned int source) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod; + int i; + + pr_debug("wm8580: PLL %uHz->%uHz\n", source, target); + + /* Scale the output frequency up; the PLL should run in the + * region of 90-100MHz. + */ + for (i = 0; i < ARRAY_SIZE(post_table); i++) { + if (target * post_table[i].div >= 90000000 && + target * post_table[i].div <= 100000000) { + pll_div->freqmode = post_table[i].freqmode; + pll_div->postscale = post_table[i].postscale; + target *= post_table[i].div; + break; + } + } + + if (i == ARRAY_SIZE(post_table)) { + printk(KERN_ERR "wm8580: Unable to scale output frequency " + "%u\n", target); + return -EINVAL; + } + + Ndiv = target / source; + + if (Ndiv < 5) { + source /= 2; + pll_div->prescale = 1; + Ndiv = target / source; + } else + pll_div->prescale = 0; + + if ((Ndiv < 5) || (Ndiv > 13)) { + printk(KERN_ERR + "WM8580 N=%u outside supported range\n", Ndiv); + return -EINVAL; + } + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + pll_div->k = K; + + pr_debug("PLL %x.%x prescale %d freqmode %d postscale %d\n", + pll_div->n, pll_div->k, pll_div->prescale, pll_div->freqmode, + pll_div->postscale); + + return 0; +} + +static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + int offset; + struct snd_soc_component *component = codec_dai->component; + struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); + struct pll_state *state; + struct _pll_div pll_div; + unsigned int reg; + unsigned int pwr_mask; + int ret; + + /* GCC isn't able to work out the ifs below for initialising/using + * pll_div so suppress warnings. + */ + memset(&pll_div, 0, sizeof(pll_div)); + + switch (pll_id) { + case WM8580_PLLA: + state = &wm8580->a; + offset = 0; + pwr_mask = WM8580_PWRDN2_PLLAPD; + break; + case WM8580_PLLB: + state = &wm8580->b; + offset = 4; + pwr_mask = WM8580_PWRDN2_PLLBPD; + break; + default: + return -ENODEV; + } + + if (freq_in && freq_out) { + ret = pll_factors(&pll_div, freq_out, freq_in); + if (ret != 0) + return ret; + } + + state->in = freq_in; + state->out = freq_out; + + /* Always disable the PLL - it is not safe to leave it running + * while reprogramming it. + */ + snd_soc_component_update_bits(component, WM8580_PWRDN2, pwr_mask, pwr_mask); + + if (!freq_in || !freq_out) + return 0; + + snd_soc_component_write(component, WM8580_PLLA1 + offset, pll_div.k & 0x1ff); + snd_soc_component_write(component, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0x1ff); + snd_soc_component_write(component, WM8580_PLLA3 + offset, + (pll_div.k >> 18 & 0xf) | (pll_div.n << 4)); + + reg = snd_soc_component_read(component, WM8580_PLLA4 + offset); + reg &= ~0x1b; + reg |= pll_div.prescale | pll_div.postscale << 1 | + pll_div.freqmode << 3; + + snd_soc_component_write(component, WM8580_PLLA4 + offset, reg); + + /* All done, turn it on */ + snd_soc_component_update_bits(component, WM8580_PWRDN2, pwr_mask, 0); + + return 0; +} + +static const int wm8580_sysclk_ratios[] = { + 128, 192, 256, 384, 512, 768, 1152, +}; + +/* + * Set PCM DAI bit size and sample rate. + */ +static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); + u16 paifa = 0; + u16 paifb = 0; + int i, ratio, osr; + + /* bit size */ + switch (params_width(params)) { + case 16: + paifa |= 0x8; + break; + case 20: + paifa |= 0x0; + paifb |= WM8580_AIF_LENGTH_20; + break; + case 24: + paifa |= 0x0; + paifb |= WM8580_AIF_LENGTH_24; + break; + case 32: + paifa |= 0x0; + paifb |= WM8580_AIF_LENGTH_32; + break; + default: + return -EINVAL; + } + + /* Look up the SYSCLK ratio; accept only exact matches */ + ratio = wm8580->sysclk[dai->driver->id] / params_rate(params); + for (i = 0; i < ARRAY_SIZE(wm8580_sysclk_ratios); i++) + if (ratio == wm8580_sysclk_ratios[i]) + break; + if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) { + dev_err(component->dev, "Invalid clock ratio %d/%d\n", + wm8580->sysclk[dai->driver->id], params_rate(params)); + return -EINVAL; + } + paifa |= i; + dev_dbg(component->dev, "Running at %dfs with %dHz clock\n", + wm8580_sysclk_ratios[i], wm8580->sysclk[dai->driver->id]); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (ratio) { + case 128: + case 192: + osr = WM8580_DACOSR; + dev_dbg(component->dev, "Selecting 64x OSR\n"); + break; + default: + osr = 0; + dev_dbg(component->dev, "Selecting 128x OSR\n"); + break; + } + + snd_soc_component_update_bits(component, WM8580_PAIF3, WM8580_DACOSR, osr); + } + + snd_soc_component_update_bits(component, WM8580_PAIF1 + dai->driver->id, + WM8580_AIF_RATE_MASK | WM8580_AIF_BCLKSEL_MASK, + paifa); + snd_soc_component_update_bits(component, WM8580_PAIF3 + dai->driver->id, + WM8580_AIF_LENGTH_MASK, paifb); + return 0; +} + +static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + unsigned int aifa; + unsigned int aifb; + int can_invert_lrclk; + + aifa = snd_soc_component_read(component, WM8580_PAIF1 + codec_dai->driver->id); + aifb = snd_soc_component_read(component, WM8580_PAIF3 + codec_dai->driver->id); + + aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + aifa &= ~WM8580_AIF_MS; + break; + case SND_SOC_DAIFMT_CBM_CFM: + aifa |= WM8580_AIF_MS; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + can_invert_lrclk = 1; + aifb |= WM8580_AIF_FMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + can_invert_lrclk = 1; + aifb |= WM8580_AIF_FMT_RIGHTJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + can_invert_lrclk = 1; + aifb |= WM8580_AIF_FMT_LEFTJ; + break; + case SND_SOC_DAIFMT_DSP_A: + can_invert_lrclk = 0; + aifb |= WM8580_AIF_FMT_DSP; + break; + case SND_SOC_DAIFMT_DSP_B: + can_invert_lrclk = 0; + aifb |= WM8580_AIF_FMT_DSP; + aifb |= WM8580_AIF_LRP; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + + case SND_SOC_DAIFMT_IB_IF: + if (!can_invert_lrclk) + return -EINVAL; + aifb |= WM8580_AIF_BCP; + aifb |= WM8580_AIF_LRP; + break; + + case SND_SOC_DAIFMT_IB_NF: + aifb |= WM8580_AIF_BCP; + break; + + case SND_SOC_DAIFMT_NB_IF: + if (!can_invert_lrclk) + return -EINVAL; + aifb |= WM8580_AIF_LRP; + break; + + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8580_PAIF1 + codec_dai->driver->id, aifa); + snd_soc_component_write(component, WM8580_PAIF3 + codec_dai->driver->id, aifb); + + return 0; +} + +static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + unsigned int reg; + + switch (div_id) { + case WM8580_MCLK: + reg = snd_soc_component_read(component, WM8580_PLLB4); + reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK; + + switch (div) { + case WM8580_CLKSRC_MCLK: + /* Input */ + break; + + case WM8580_CLKSRC_PLLA: + reg |= WM8580_PLLB4_MCLKOUTSRC_PLLA; + break; + case WM8580_CLKSRC_PLLB: + reg |= WM8580_PLLB4_MCLKOUTSRC_PLLB; + break; + + case WM8580_CLKSRC_OSC: + reg |= WM8580_PLLB4_MCLKOUTSRC_OSC; + break; + + default: + return -EINVAL; + } + snd_soc_component_write(component, WM8580_PLLB4, reg); + break; + + case WM8580_CLKOUTSRC: + reg = snd_soc_component_read(component, WM8580_PLLB4); + reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; + + switch (div) { + case WM8580_CLKSRC_NONE: + break; + + case WM8580_CLKSRC_PLLA: + reg |= WM8580_PLLB4_CLKOUTSRC_PLLACLK; + break; + + case WM8580_CLKSRC_PLLB: + reg |= WM8580_PLLB4_CLKOUTSRC_PLLBCLK; + break; + + case WM8580_CLKSRC_OSC: + reg |= WM8580_PLLB4_CLKOUTSRC_OSCCLK; + break; + + default: + return -EINVAL; + } + snd_soc_component_write(component, WM8580_PLLB4, reg); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); + int ret, sel, sel_mask, sel_shift; + + switch (dai->driver->id) { + case WM8580_DAI_PAIFRX: + sel_mask = 0x3; + sel_shift = 0; + break; + + case WM8580_DAI_PAIFTX: + sel_mask = 0xc; + sel_shift = 2; + break; + + default: + WARN(1, "Unknown DAI driver ID\n"); + return -EINVAL; + } + + switch (clk_id) { + case WM8580_CLKSRC_ADCMCLK: + if (dai->driver->id != WM8580_DAI_PAIFTX) + return -EINVAL; + sel = 0 << sel_shift; + break; + case WM8580_CLKSRC_PLLA: + sel = 1 << sel_shift; + break; + case WM8580_CLKSRC_PLLB: + sel = 2 << sel_shift; + break; + case WM8580_CLKSRC_MCLK: + sel = 3 << sel_shift; + break; + default: + dev_err(component->dev, "Unknown clock %d\n", clk_id); + return -EINVAL; + } + + /* We really should validate PLL settings but not yet */ + wm8580->sysclk[dai->driver->id] = freq; + + ret = snd_soc_component_update_bits(component, WM8580_CLKSEL, sel_mask, sel); + if (ret < 0) + return ret; + + return 0; +} + +static int wm8580_mute(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + unsigned int reg; + + reg = snd_soc_component_read(component, WM8580_DAC_CONTROL5); + + if (mute) + reg |= WM8580_DAC_CONTROL5_MUTEALL; + else + reg &= ~WM8580_DAC_CONTROL5_MUTEALL; + + snd_soc_component_write(component, WM8580_DAC_CONTROL5, reg); + + return 0; +} + +static int wm8580_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Power up and get individual control of the DACs */ + snd_soc_component_update_bits(component, WM8580_PWRDN1, + WM8580_PWRDN1_PWDN | + WM8580_PWRDN1_ALLDACPD, 0); + + /* Make VMID high impedance */ + snd_soc_component_update_bits(component, WM8580_ADC_CONTROL1, + 0x100, 0); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, WM8580_PWRDN1, + WM8580_PWRDN1_PWDN, WM8580_PWRDN1_PWDN); + break; + } + return 0; +} + +static int wm8580_playback_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); + + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, 1, wm8580->drvdata->num_dacs * 2); +} + +#define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8580_dai_ops_playback = { + .startup = wm8580_playback_startup, + .set_sysclk = wm8580_set_sysclk, + .hw_params = wm8580_paif_hw_params, + .set_fmt = wm8580_set_paif_dai_fmt, + .set_clkdiv = wm8580_set_dai_clkdiv, + .set_pll = wm8580_set_dai_pll, + .mute_stream = wm8580_mute, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops wm8580_dai_ops_capture = { + .set_sysclk = wm8580_set_sysclk, + .hw_params = wm8580_paif_hw_params, + .set_fmt = wm8580_set_paif_dai_fmt, + .set_clkdiv = wm8580_set_dai_clkdiv, + .set_pll = wm8580_set_dai_pll, +}; + +static struct snd_soc_dai_driver wm8580_dai[] = { + { + .name = "wm8580-hifi-playback", + .id = WM8580_DAI_PAIFRX, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = WM8580_FORMATS, + }, + .ops = &wm8580_dai_ops_playback, + }, + { + .name = "wm8580-hifi-capture", + .id = WM8580_DAI_PAIFTX, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = WM8580_FORMATS, + }, + .ops = &wm8580_dai_ops_capture, + }, +}; + +static int wm8580_probe(struct snd_soc_component *component) +{ + struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret = 0; + + switch (wm8580->drvdata->num_dacs) { + case 4: + snd_soc_add_component_controls(component, wm8581_snd_controls, + ARRAY_SIZE(wm8581_snd_controls)); + snd_soc_dapm_new_controls(dapm, wm8581_dapm_widgets, + ARRAY_SIZE(wm8581_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, wm8581_dapm_routes, + ARRAY_SIZE(wm8581_dapm_routes)); + break; + default: + break; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies), + wm8580->supplies); + if (ret != 0) { + dev_err(component->dev, "Failed to enable supplies: %d\n", ret); + goto err_regulator_get; + } + + /* Get the codec into a known state */ + ret = snd_soc_component_write(component, WM8580_RESET, 0); + if (ret != 0) { + dev_err(component->dev, "Failed to reset component: %d\n", ret); + goto err_regulator_enable; + } + + return 0; + +err_regulator_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); +err_regulator_get: + return ret; +} + +/* power down chip */ +static void wm8580_remove(struct snd_soc_component *component) +{ + struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); + + regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); +} + +static const struct snd_soc_component_driver soc_component_dev_wm8580 = { + .probe = wm8580_probe, + .remove = wm8580_remove, + .set_bias_level = wm8580_set_bias_level, + .controls = wm8580_snd_controls, + .num_controls = ARRAY_SIZE(wm8580_snd_controls), + .dapm_widgets = wm8580_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8580_dapm_widgets), + .dapm_routes = wm8580_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8580_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8580_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8580_MAX_REGISTER, + + .reg_defaults = wm8580_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8580_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm8580_volatile, +}; + +static const struct wm8580_driver_data wm8580_data = { + .num_dacs = 3, +}; + +static const struct wm8580_driver_data wm8581_data = { + .num_dacs = 4, +}; + +static const struct of_device_id wm8580_of_match[] = { + { .compatible = "wlf,wm8580", .data = &wm8580_data }, + { .compatible = "wlf,wm8581", .data = &wm8581_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, wm8580_of_match); + +static int wm8580_i2c_probe(struct i2c_client *i2c) +{ + const struct of_device_id *of_id; + struct wm8580_priv *wm8580; + int ret, i; + + wm8580 = devm_kzalloc(&i2c->dev, sizeof(struct wm8580_priv), + GFP_KERNEL); + if (wm8580 == NULL) + return -ENOMEM; + + wm8580->regmap = devm_regmap_init_i2c(i2c, &wm8580_regmap); + if (IS_ERR(wm8580->regmap)) + return PTR_ERR(wm8580->regmap); + + for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++) + wm8580->supplies[i].supply = wm8580_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8580->supplies), + wm8580->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, wm8580); + + of_id = of_match_device(wm8580_of_match, &i2c->dev); + if (of_id) + wm8580->drvdata = of_id->data; + + if (!wm8580->drvdata) { + dev_err(&i2c->dev, "failed to find driver data\n"); + return -EINVAL; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8580, wm8580_dai, ARRAY_SIZE(wm8580_dai)); + + return ret; +} + +static const struct i2c_device_id wm8580_i2c_id[] = { + { "wm8580", (kernel_ulong_t)&wm8580_data }, + { "wm8581", (kernel_ulong_t)&wm8581_data }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id); + +static struct i2c_driver wm8580_i2c_driver = { + .driver = { + .name = "wm8580", + .of_match_table = wm8580_of_match, + }, + .probe = wm8580_i2c_probe, + .id_table = wm8580_i2c_id, +}; + +module_i2c_driver(wm8580_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8580 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_AUTHOR("Matt Flax "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h new file mode 100644 index 0000000000..34f7fee6b9 --- /dev/null +++ b/sound/soc/codecs/wm8580.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * wm8580.h -- audio driver for WM8580 + * + * Copyright 2008 Samsung Electronics. + * Author: Ryu Euiyoul + * ryu.real@gmail.com + */ + +#ifndef _WM8580_H +#define _WM8580_H + +#define WM8580_PLLA 1 +#define WM8580_PLLB 2 + +#define WM8580_MCLK 1 +#define WM8580_CLKOUTSRC 2 + +#define WM8580_CLKSRC_MCLK 1 +#define WM8580_CLKSRC_PLLA 2 +#define WM8580_CLKSRC_PLLB 3 +#define WM8580_CLKSRC_OSC 4 +#define WM8580_CLKSRC_NONE 5 +#define WM8580_CLKSRC_ADCMCLK 6 + +#define WM8580_DAI_PAIFRX 0 +#define WM8580_DAI_PAIFTX 1 + +#endif + diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c new file mode 100644 index 0000000000..916f297164 --- /dev/null +++ b/sound/soc/codecs/wm8711.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8711.c -- WM8711 ALSA SoC Audio driver + * + * Copyright 2006 Wolfson Microelectronics + * + * Author: Mike Arthur + * + * Based on wm8731.c by Richard Purdie + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8711.h" + +/* codec private data */ +struct wm8711_priv { + struct regmap *regmap; + unsigned int sysclk; +}; + +/* + * wm8711 register cache + * We can't read the WM8711 register space when we are + * using 2 wire for device control, so we cache them instead. + * There is no point in caching the reset register + */ +static const struct reg_default wm8711_reg_defaults[] = { + { 0, 0x0079 }, { 1, 0x0079 }, { 2, 0x000a }, { 3, 0x0008 }, + { 4, 0x009f }, { 5, 0x000a }, { 6, 0x0000 }, { 7, 0x0000 }, +}; + +static bool wm8711_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8711_RESET: + return true; + default: + return false; + } +} + +#define wm8711_reset(c) snd_soc_component_write(c, WM8711_RESET, 0) + +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); + +static const struct snd_kcontrol_new wm8711_snd_controls[] = { + +SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V, + 0, 127, 0, out_tlv), +SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V, + 7, 1, 0), + +}; + +/* Output Mixer */ +static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0), +SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1, + &wm8711_output_mixer_controls[0], + ARRAY_SIZE(wm8711_output_mixer_controls)), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1), +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("LHPOUT"), +SND_SOC_DAPM_OUTPUT("ROUT"), +SND_SOC_DAPM_OUTPUT("RHPOUT"), +}; + +static const struct snd_soc_dapm_route wm8711_intercon[] = { + /* output mixer */ + {"Output Mixer", "Line Bypass Switch", "Line Input"}, + {"Output Mixer", "HiFi Playback Switch", "DAC"}, + + /* outputs */ + {"RHPOUT", NULL, "Output Mixer"}, + {"ROUT", NULL, "Output Mixer"}, + {"LHPOUT", NULL, "Output Mixer"}, + {"LOUT", NULL, "Output Mixer"}, +}; + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u8 sr:4; + u8 bosr:1; + u8 usb:1; +}; + +/* codec mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 48k */ + {12288000, 48000, 256, 0x0, 0x0, 0x0}, + {18432000, 48000, 384, 0x0, 0x1, 0x0}, + {12000000, 48000, 250, 0x0, 0x0, 0x1}, + + /* 32k */ + {12288000, 32000, 384, 0x6, 0x0, 0x0}, + {18432000, 32000, 576, 0x6, 0x1, 0x0}, + {12000000, 32000, 375, 0x6, 0x0, 0x1}, + + /* 8k */ + {12288000, 8000, 1536, 0x3, 0x0, 0x0}, + {18432000, 8000, 2304, 0x3, 0x1, 0x0}, + {11289600, 8000, 1408, 0xb, 0x0, 0x0}, + {16934400, 8000, 2112, 0xb, 0x1, 0x0}, + {12000000, 8000, 1500, 0x3, 0x0, 0x1}, + + /* 96k */ + {12288000, 96000, 128, 0x7, 0x0, 0x0}, + {18432000, 96000, 192, 0x7, 0x1, 0x0}, + {12000000, 96000, 125, 0x7, 0x0, 0x1}, + + /* 44.1k */ + {11289600, 44100, 256, 0x8, 0x0, 0x0}, + {16934400, 44100, 384, 0x8, 0x1, 0x0}, + {12000000, 44100, 272, 0x8, 0x1, 0x1}, + + /* 88.2k */ + {11289600, 88200, 128, 0xf, 0x0, 0x0}, + {16934400, 88200, 192, 0xf, 0x1, 0x0}, + {12000000, 88200, 136, 0xf, 0x1, 0x1}, +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return 0; +} + +static int wm8711_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component); + u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0xfff3; + int i = get_coeff(wm8711->sysclk, params_rate(params)); + u16 srate = (coeff_div[i].sr << 2) | + (coeff_div[i].bosr << 1) | coeff_div[i].usb; + + snd_soc_component_write(component, WM8711_SRATE, srate); + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x0004; + break; + case 24: + iface |= 0x0008; + break; + } + + snd_soc_component_write(component, WM8711_IFACE, iface); + return 0; +} + +static int wm8711_pcm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + /* set active */ + snd_soc_component_write(component, WM8711_ACTIVE, 0x0001); + + return 0; +} + +static void wm8711_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + /* deactivate */ + if (!snd_soc_component_active(component)) { + udelay(50); + snd_soc_component_write(component, WM8711_ACTIVE, 0x0); + } +} + +static int wm8711_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, WM8711_APDIGI) & 0xfff7; + + if (mute) + snd_soc_component_write(component, WM8711_APDIGI, mute_reg | 0x8); + else + snd_soc_component_write(component, WM8711_APDIGI, mute_reg); + + return 0; +} + +static int wm8711_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case 11289600: + case 12000000: + case 12288000: + case 16934400: + case 18432000: + wm8711->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = snd_soc_component_read(component, WM8711_IFACE) & 0x000c; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface |= 0x0040; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + /* set iface */ + snd_soc_component_write(component, WM8711_IFACE, iface); + return 0; +} + +static int wm8711_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8711_priv *wm8711 = snd_soc_component_get_drvdata(component); + u16 reg = snd_soc_component_read(component, WM8711_PWR) & 0xff7f; + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_write(component, WM8711_PWR, reg); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + regcache_sync(wm8711->regmap); + + snd_soc_component_write(component, WM8711_PWR, reg | 0x0040); + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, WM8711_ACTIVE, 0x0); + snd_soc_component_write(component, WM8711_PWR, 0xffff); + break; + } + return 0; +} + +#define WM8711_RATES SNDRV_PCM_RATE_8000_96000 + +#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8711_ops = { + .prepare = wm8711_pcm_prepare, + .hw_params = wm8711_hw_params, + .shutdown = wm8711_shutdown, + .mute_stream = wm8711_mute, + .set_sysclk = wm8711_set_dai_sysclk, + .set_fmt = wm8711_set_dai_fmt, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8711_dai = { + .name = "wm8711-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8711_RATES, + .formats = WM8711_FORMATS, + }, + .ops = &wm8711_ops, +}; + +static int wm8711_probe(struct snd_soc_component *component) +{ + int ret; + + ret = wm8711_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset\n"); + return ret; + } + + /* Latch the update bits */ + snd_soc_component_update_bits(component, WM8711_LOUT1V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8711_ROUT1V, 0x0100, 0x0100); + + return ret; + +} + +static const struct snd_soc_component_driver soc_component_dev_wm8711 = { + .probe = wm8711_probe, + .set_bias_level = wm8711_set_bias_level, + .controls = wm8711_snd_controls, + .num_controls = ARRAY_SIZE(wm8711_snd_controls), + .dapm_widgets = wm8711_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets), + .dapm_routes = wm8711_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8711_intercon), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8711_of_match[] = { + { .compatible = "wlf,wm8711", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8711_of_match); + +static const struct regmap_config wm8711_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8711_RESET, + + .reg_defaults = wm8711_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8711_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm8711_volatile, +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8711_spi_probe(struct spi_device *spi) +{ + struct wm8711_priv *wm8711; + int ret; + + wm8711 = devm_kzalloc(&spi->dev, sizeof(struct wm8711_priv), + GFP_KERNEL); + if (wm8711 == NULL) + return -ENOMEM; + + wm8711->regmap = devm_regmap_init_spi(spi, &wm8711_regmap); + if (IS_ERR(wm8711->regmap)) + return PTR_ERR(wm8711->regmap); + + spi_set_drvdata(spi, wm8711); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8711, &wm8711_dai, 1); + + return ret; +} + +static struct spi_driver wm8711_spi_driver = { + .driver = { + .name = "wm8711", + .of_match_table = wm8711_of_match, + }, + .probe = wm8711_spi_probe, +}; +#endif /* CONFIG_SPI_MASTER */ + +#if IS_ENABLED(CONFIG_I2C) +static int wm8711_i2c_probe(struct i2c_client *client) +{ + struct wm8711_priv *wm8711; + int ret; + + wm8711 = devm_kzalloc(&client->dev, sizeof(struct wm8711_priv), + GFP_KERNEL); + if (wm8711 == NULL) + return -ENOMEM; + + wm8711->regmap = devm_regmap_init_i2c(client, &wm8711_regmap); + if (IS_ERR(wm8711->regmap)) + return PTR_ERR(wm8711->regmap); + + i2c_set_clientdata(client, wm8711); + + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_wm8711, &wm8711_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8711_i2c_id[] = { + { "wm8711", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id); + +static struct i2c_driver wm8711_i2c_driver = { + .driver = { + .name = "wm8711", + .of_match_table = wm8711_of_match, + }, + .probe = wm8711_i2c_probe, + .id_table = wm8711_i2c_id, +}; +#endif + +static int __init wm8711_modinit(void) +{ + int ret; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8711_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8711_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n", + ret); + } +#endif + return 0; +} +module_init(wm8711_modinit); + +static void __exit wm8711_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8711_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8711_spi_driver); +#endif +} +module_exit(wm8711_exit); + +MODULE_DESCRIPTION("ASoC WM8711 driver"); +MODULE_AUTHOR("Mike Arthur"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8711.h b/sound/soc/codecs/wm8711.h new file mode 100644 index 0000000000..487a9f34d1 --- /dev/null +++ b/sound/soc/codecs/wm8711.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8711.h -- WM8711 Soc Audio driver + * + * Copyright 2006 Wolfson Microelectronics + * + * Author: Mike Arthur + * + * Based on wm8731.h + */ + +#ifndef _WM8711_H +#define _WM8711_H + +/* WM8711 register space */ + +#define WM8711_LOUT1V 0x02 +#define WM8711_ROUT1V 0x03 +#define WM8711_APANA 0x04 +#define WM8711_APDIGI 0x05 +#define WM8711_PWR 0x06 +#define WM8711_IFACE 0x07 +#define WM8711_SRATE 0x08 +#define WM8711_ACTIVE 0x09 +#define WM8711_RESET 0x0f + +#define WM8711_CACHEREGNUM 8 + +#define WM8711_SYSCLK 0 +#define WM8711_DAI 0 + +struct wm8711_setup_data { + unsigned short i2c_address; +}; + +#endif diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c new file mode 100644 index 0000000000..d6b0a570dd --- /dev/null +++ b/sound/soc/codecs/wm8727.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * wm8727.c + * + * Created on: 15-Oct-2009 + * Author: neil.jones@imgtec.com + * + * Copyright (C) 2009 Imagination Technologies Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct snd_soc_dapm_widget wm8727_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("VOUTL"), +SND_SOC_DAPM_OUTPUT("VOUTR"), +}; + +static const struct snd_soc_dapm_route wm8727_dapm_routes[] = { + { "VOUTL", NULL, "Playback" }, + { "VOUTR", NULL, "Playback" }, +}; + +/* + * Note this is a simple chip with no configuration interface, sample rate is + * determined automatically by examining the Master clock and Bit clock ratios + */ +#define WM8727_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) + +static struct snd_soc_dai_driver wm8727_dai = { + .name = "wm8727-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8727_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8727 = { + .dapm_widgets = wm8727_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets), + .dapm_routes = wm8727_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm8727_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm8727, &wm8727_dai, 1); +} + +static struct platform_driver wm8727_codec_driver = { + .driver = { + .name = "wm8727", + }, + + .probe = wm8727_probe, +}; + +module_platform_driver(wm8727_codec_driver); + +MODULE_DESCRIPTION("ASoC wm8727 driver"); +MODULE_AUTHOR("Neil Jones"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c new file mode 100644 index 0000000000..0c943e7d41 --- /dev/null +++ b/sound/soc/codecs/wm8728.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8728.c -- WM8728 ALSA SoC Audio driver + * + * Copyright 2008 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8728.h" + +/* + * We can't read the WM8728 register space so we cache them instead. + * Note that the defaults here aren't the physical defaults, we latch + * the volume update bits, mute the output and enable infinite zero + * detect. + */ +static const struct reg_default wm8728_reg_defaults[] = { + { 0, 0x1ff }, + { 1, 0x1ff }, + { 2, 0x001 }, + { 3, 0x100 }, +}; + +/* codec private data */ +struct wm8728_priv { + struct regmap *regmap; +}; + +static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1); + +static const struct snd_kcontrol_new wm8728_snd_controls[] = { + +SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL, + 0, 255, 0, wm8728_tlv), + +SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0), +}; + +/* + * DAPM controls. + */ +static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("VOUTL"), +SND_SOC_DAPM_OUTPUT("VOUTR"), +}; + +static const struct snd_soc_dapm_route wm8728_intercon[] = { + {"VOUTL", NULL, "DAC"}, + {"VOUTR", NULL, "DAC"}, +}; + +static int wm8728_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, WM8728_DACCTL); + + if (mute) + snd_soc_component_write(component, WM8728_DACCTL, mute_reg | 1); + else + snd_soc_component_write(component, WM8728_DACCTL, mute_reg & ~1); + + return 0; +} + +static int wm8728_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 dac = snd_soc_component_read(component, WM8728_DACCTL); + + dac &= ~0x18; + + switch (params_width(params)) { + case 16: + break; + case 20: + dac |= 0x10; + break; + case 24: + dac |= 0x08; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8728_DACCTL, dac); + + return 0; +} + +static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = snd_soc_component_read(component, WM8728_IFCTL); + + /* Currently only I2S is supported by the driver, though the + * hardware is more flexible. + */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 1; + break; + default: + return -EINVAL; + } + + /* The hardware only support full slave mode */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + iface &= ~0x22; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x20; + iface &= ~0x02; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x02; + iface &= ~0x20; + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x22; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8728_IFCTL, iface); + return 0; +} + +static int wm8728_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8728_priv *wm8728 = snd_soc_component_get_drvdata(component); + u16 reg; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Power everything up... */ + reg = snd_soc_component_read(component, WM8728_DACCTL); + snd_soc_component_write(component, WM8728_DACCTL, reg & ~0x4); + + /* ..then sync in the register cache. */ + regcache_sync(wm8728->regmap); + } + break; + + case SND_SOC_BIAS_OFF: + reg = snd_soc_component_read(component, WM8728_DACCTL); + snd_soc_component_write(component, WM8728_DACCTL, reg | 0x4); + break; + } + return 0; +} + +#define WM8728_RATES (SNDRV_PCM_RATE_8000_192000) + +#define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8728_dai_ops = { + .hw_params = wm8728_hw_params, + .mute_stream = wm8728_mute, + .set_fmt = wm8728_set_dai_fmt, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8728_dai = { + .name = "wm8728-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8728_RATES, + .formats = WM8728_FORMATS, + }, + .ops = &wm8728_dai_ops, +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8728 = { + .set_bias_level = wm8728_set_bias_level, + .controls = wm8728_snd_controls, + .num_controls = ARRAY_SIZE(wm8728_snd_controls), + .dapm_widgets = wm8728_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets), + .dapm_routes = wm8728_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8728_intercon), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8728_of_match[] = { + { .compatible = "wlf,wm8728", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8728_of_match); + +static const struct regmap_config wm8728_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8728_IFCTL, + + .reg_defaults = wm8728_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8728_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8728_spi_probe(struct spi_device *spi) +{ + struct wm8728_priv *wm8728; + int ret; + + wm8728 = devm_kzalloc(&spi->dev, sizeof(struct wm8728_priv), + GFP_KERNEL); + if (wm8728 == NULL) + return -ENOMEM; + + wm8728->regmap = devm_regmap_init_spi(spi, &wm8728_regmap); + if (IS_ERR(wm8728->regmap)) + return PTR_ERR(wm8728->regmap); + + spi_set_drvdata(spi, wm8728); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8728, &wm8728_dai, 1); + + return ret; +} + +static struct spi_driver wm8728_spi_driver = { + .driver = { + .name = "wm8728", + .of_match_table = wm8728_of_match, + }, + .probe = wm8728_spi_probe, +}; +#endif /* CONFIG_SPI_MASTER */ + +#if IS_ENABLED(CONFIG_I2C) +static int wm8728_i2c_probe(struct i2c_client *i2c) +{ + struct wm8728_priv *wm8728; + int ret; + + wm8728 = devm_kzalloc(&i2c->dev, sizeof(struct wm8728_priv), + GFP_KERNEL); + if (wm8728 == NULL) + return -ENOMEM; + + wm8728->regmap = devm_regmap_init_i2c(i2c, &wm8728_regmap); + if (IS_ERR(wm8728->regmap)) + return PTR_ERR(wm8728->regmap); + + i2c_set_clientdata(i2c, wm8728); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8728, &wm8728_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8728_i2c_id[] = { + { "wm8728", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id); + +static struct i2c_driver wm8728_i2c_driver = { + .driver = { + .name = "wm8728", + .of_match_table = wm8728_of_match, + }, + .probe = wm8728_i2c_probe, + .id_table = wm8728_i2c_id, +}; +#endif + +static int __init wm8728_modinit(void) +{ + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8728_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8728 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8728_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8728 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8728_modinit); + +static void __exit wm8728_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8728_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8728_spi_driver); +#endif +} +module_exit(wm8728_exit); + +MODULE_DESCRIPTION("ASoC WM8728 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8728.h b/sound/soc/codecs/wm8728.h new file mode 100644 index 0000000000..d926db5e4f --- /dev/null +++ b/sound/soc/codecs/wm8728.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8728.h -- WM8728 ASoC codec driver + * + * Copyright 2008 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#ifndef _WM8728_H +#define _WM8728_H + +#define WM8728_DACLVOL 0x00 +#define WM8728_DACRVOL 0x01 +#define WM8728_DACCTL 0x02 +#define WM8728_IFCTL 0x03 + +#endif diff --git a/sound/soc/codecs/wm8731-i2c.c b/sound/soc/codecs/wm8731-i2c.c new file mode 100644 index 0000000000..c39e637d81 --- /dev/null +++ b/sound/soc/codecs/wm8731-i2c.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8731-i2c.c -- WM8731 ALSA SoC Audio driver I2C code + * + * Copyright 2005 Openedhand Ltd. + * Copyright 2006-12 Wolfson Microelectronics, plc + * + * Author: Richard Purdie + * + * Based on wm8753.c by Liam Girdwood + */ + +#include +#include +#include + +#include "wm8731.h" + + +static const struct of_device_id wm8731_of_match[] = { + { .compatible = "wlf,wm8731", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8731_of_match); + +static int wm8731_i2c_probe(struct i2c_client *i2c) +{ + struct wm8731_priv *wm8731; + int ret; + + wm8731 = devm_kzalloc(&i2c->dev, sizeof(struct wm8731_priv), + GFP_KERNEL); + if (wm8731 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm8731); + + wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); + if (IS_ERR(wm8731->regmap)) { + ret = PTR_ERR(wm8731->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + return wm8731_init(&i2c->dev, wm8731); +} + +static const struct i2c_device_id wm8731_i2c_id[] = { + { "wm8731", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); + +static struct i2c_driver wm8731_i2c_driver = { + .driver = { + .name = "wm8731", + .of_match_table = wm8731_of_match, + }, + .probe = wm8731_i2c_probe, + .id_table = wm8731_i2c_id, +}; + +module_i2c_driver(wm8731_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8731 driver - I2C"); +MODULE_AUTHOR("Richard Purdie"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8731-spi.c b/sound/soc/codecs/wm8731-spi.c new file mode 100644 index 0000000000..542ed097d8 --- /dev/null +++ b/sound/soc/codecs/wm8731-spi.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8731.c -- WM8731 ALSA SoC Audio driver + * + * Copyright 2005 Openedhand Ltd. + * Copyright 2006-12 Wolfson Microelectronics, plc + * + * Author: Richard Purdie + * + * Based on wm8753.c by Liam Girdwood + */ + +#include +#include +#include + +#include "wm8731.h" + +static const struct of_device_id wm8731_of_match[] = { + { .compatible = "wlf,wm8731", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8731_of_match); + +static int wm8731_spi_probe(struct spi_device *spi) +{ + struct wm8731_priv *wm8731; + int ret; + + wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL); + if (wm8731 == NULL) + return -ENOMEM; + + spi_set_drvdata(spi, wm8731); + + wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); + if (IS_ERR(wm8731->regmap)) { + ret = PTR_ERR(wm8731->regmap); + dev_err(&spi->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + return wm8731_init(&spi->dev, wm8731); +} + +static struct spi_driver wm8731_spi_driver = { + .driver = { + .name = "wm8731", + .of_match_table = wm8731_of_match, + }, + .probe = wm8731_spi_probe, +}; + +module_spi_driver(wm8731_spi_driver); + +MODULE_DESCRIPTION("ASoC WM8731 driver - SPI"); +MODULE_AUTHOR("Richard Purdie"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c new file mode 100644 index 0000000000..efc160c75f --- /dev/null +++ b/sound/soc/codecs/wm8731.c @@ -0,0 +1,653 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8731.c -- WM8731 ALSA SoC Audio driver + * + * Copyright 2005 Openedhand Ltd. + * Copyright 2006-12 Wolfson Microelectronics, plc + * + * Author: Richard Purdie + * + * Based on wm8753.c by Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8731.h" + +static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { + "AVDD", + "HPVDD", + "DCVDD", + "DBVDD", +}; + +/* + * wm8731 register cache + */ +static const struct reg_default wm8731_reg_defaults[] = { + { 0, 0x0097 }, + { 1, 0x0097 }, + { 2, 0x0079 }, + { 3, 0x0079 }, + { 4, 0x000a }, + { 5, 0x0008 }, + { 6, 0x009f }, + { 7, 0x000a }, + { 8, 0x0000 }, + { 9, 0x0000 }, +}; + +static bool wm8731_volatile(struct device *dev, unsigned int reg) +{ + return reg == WM8731_RESET; +} + +#define wm8731_reset(m) regmap_write(m, WM8731_RESET, 0) + +static const char *wm8731_input_select[] = {"Line In", "Mic"}; + +static SOC_ENUM_SINGLE_DECL(wm8731_insel_enum, + WM8731_APANA, 2, wm8731_input_select); + +static int wm8731_deemph[] = { 0, 32000, 44100, 48000 }; + +static int wm8731_set_deemph(struct snd_soc_component *component) +{ + struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8731->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(wm8731_deemph); i++) { + if (abs(wm8731_deemph[i] - wm8731->playback_fs) < + abs(wm8731_deemph[best] - wm8731->playback_fs)) + best = i; + } + + val = best << 1; + } else { + best = 0; + val = 0; + } + + dev_dbg(component->dev, "Set deemphasis %d (%dHz)\n", + best, wm8731_deemph[best]); + + return snd_soc_component_update_bits(component, WM8731_APDIGI, 0x6, val); +} + +static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wm8731->deemph; + + return 0; +} + +static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; + int ret = 0; + + if (deemph > 1) + return -EINVAL; + + mutex_lock(&wm8731->lock); + if (wm8731->deemph != deemph) { + wm8731->deemph = deemph; + + wm8731_set_deemph(component); + + ret = 1; + } + mutex_unlock(&wm8731->lock); + + return ret; +} + +static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 2000, 0); + +static const struct snd_kcontrol_new wm8731_snd_controls[] = { + +SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V, + 0, 127, 0, out_tlv), +SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V, + 7, 1, 0), + +SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, + in_tlv), +SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), + +SOC_SINGLE_TLV("Mic Boost Volume", WM8731_APANA, 0, 1, 0, mic_tlv), +SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1), + +SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, + sidetone_tlv), + +SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), +SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), + +SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, + wm8731_get_deemph, wm8731_put_deemph), +}; + +/* Output Mixer */ +static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0), +SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0), +SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), +}; + +/* Input mux */ +static const struct snd_kcontrol_new wm8731_input_mux_controls = +SOC_DAPM_ENUM("Input Select", wm8731_insel_enum); + +static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("ACTIVE",WM8731_ACTIVE, 0, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0), +SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, + &wm8731_output_mixer_controls[0], + ARRAY_SIZE(wm8731_output_mixer_controls)), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1), +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("LHPOUT"), +SND_SOC_DAPM_OUTPUT("ROUT"), +SND_SOC_DAPM_OUTPUT("RHPOUT"), +SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1), +SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls), +SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1), +SND_SOC_DAPM_INPUT("MICIN"), +SND_SOC_DAPM_INPUT("RLINEIN"), +SND_SOC_DAPM_INPUT("LLINEIN"), +}; + +static int wm8731_check_osc(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); + + return wm8731->sysclk_type == WM8731_SYSCLK_XTAL; +} + +static const struct snd_soc_dapm_route wm8731_intercon[] = { + {"DAC", NULL, "OSC", wm8731_check_osc}, + {"ADC", NULL, "OSC", wm8731_check_osc}, + {"DAC", NULL, "ACTIVE"}, + {"ADC", NULL, "ACTIVE"}, + + /* output mixer */ + {"Output Mixer", "Line Bypass Switch", "Line Input"}, + {"Output Mixer", "HiFi Playback Switch", "DAC"}, + {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, + + /* outputs */ + {"RHPOUT", NULL, "Output Mixer"}, + {"ROUT", NULL, "Output Mixer"}, + {"LHPOUT", NULL, "Output Mixer"}, + {"LOUT", NULL, "Output Mixer"}, + + /* input mux */ + {"Input Mux", "Line In", "Line Input"}, + {"Input Mux", "Mic", "Mic Bias"}, + {"ADC", NULL, "Input Mux"}, + + /* inputs */ + {"Line Input", NULL, "LLINEIN"}, + {"Line Input", NULL, "RLINEIN"}, + {"Mic Bias", NULL, "MICIN"}, +}; + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u8 sr:4; + u8 bosr:1; + u8 usb:1; +}; + +/* codec mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 48k */ + {12288000, 48000, 256, 0x0, 0x0, 0x0}, + {18432000, 48000, 384, 0x0, 0x1, 0x0}, + {12000000, 48000, 250, 0x0, 0x0, 0x1}, + + /* 32k */ + {12288000, 32000, 384, 0x6, 0x0, 0x0}, + {18432000, 32000, 576, 0x6, 0x1, 0x0}, + {12000000, 32000, 375, 0x6, 0x0, 0x1}, + + /* 8k */ + {12288000, 8000, 1536, 0x3, 0x0, 0x0}, + {18432000, 8000, 2304, 0x3, 0x1, 0x0}, + {11289600, 8000, 1408, 0xb, 0x0, 0x0}, + {16934400, 8000, 2112, 0xb, 0x1, 0x0}, + {12000000, 8000, 1500, 0x3, 0x0, 0x1}, + + /* 96k */ + {12288000, 96000, 128, 0x7, 0x0, 0x0}, + {18432000, 96000, 192, 0x7, 0x1, 0x0}, + {12000000, 96000, 125, 0x7, 0x0, 0x1}, + + /* 44.1k */ + {11289600, 44100, 256, 0x8, 0x0, 0x0}, + {16934400, 44100, 384, 0x8, 0x1, 0x0}, + {12000000, 44100, 272, 0x8, 0x1, 0x1}, + + /* 88.2k */ + {11289600, 88200, 128, 0xf, 0x0, 0x0}, + {16934400, 88200, 192, 0xf, 0x1, 0x0}, + {12000000, 88200, 136, 0xf, 0x1, 0x1}, +}; + +/* rates constraints */ +static const unsigned int wm8731_rates_12000000[] = { + 8000, 32000, 44100, 48000, 96000, 88200, +}; + +static const unsigned int wm8731_rates_12288000_18432000[] = { + 8000, 32000, 48000, 96000, +}; + +static const unsigned int wm8731_rates_11289600_16934400[] = { + 8000, 44100, 88200, +}; + +static const struct snd_pcm_hw_constraint_list wm8731_constraints_12000000 = { + .list = wm8731_rates_12000000, + .count = ARRAY_SIZE(wm8731_rates_12000000), +}; + +static const +struct snd_pcm_hw_constraint_list wm8731_constraints_12288000_18432000 = { + .list = wm8731_rates_12288000_18432000, + .count = ARRAY_SIZE(wm8731_rates_12288000_18432000), +}; + +static const +struct snd_pcm_hw_constraint_list wm8731_constraints_11289600_16934400 = { + .list = wm8731_rates_11289600_16934400, + .count = ARRAY_SIZE(wm8731_rates_11289600_16934400), +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return 0; +} + +static int wm8731_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); + u16 iface = snd_soc_component_read(component, WM8731_IFACE) & 0xfff3; + int i = get_coeff(wm8731->sysclk, params_rate(params)); + u16 srate = (coeff_div[i].sr << 2) | + (coeff_div[i].bosr << 1) | coeff_div[i].usb; + + wm8731->playback_fs = params_rate(params); + + snd_soc_component_write(component, WM8731_SRATE, srate); + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x0004; + break; + case 24: + iface |= 0x0008; + break; + case 32: + iface |= 0x000c; + break; + } + + wm8731_set_deemph(component); + + snd_soc_component_write(component, WM8731_IFACE, iface); + return 0; +} + +static int wm8731_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, WM8731_APDIGI) & 0xfff7; + + if (mute) + snd_soc_component_write(component, WM8731_APDIGI, mute_reg | 0x8); + else + snd_soc_component_write(component, WM8731_APDIGI, mute_reg); + return 0; +} + +static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case WM8731_SYSCLK_XTAL: + case WM8731_SYSCLK_MCLK: + if (wm8731->mclk && clk_set_rate(wm8731->mclk, freq)) + return -EINVAL; + wm8731->sysclk_type = clk_id; + break; + default: + return -EINVAL; + } + + switch (freq) { + case 0: + wm8731->constraints = NULL; + break; + case 12000000: + wm8731->constraints = &wm8731_constraints_12000000; + break; + case 12288000: + case 18432000: + wm8731->constraints = &wm8731_constraints_12288000_18432000; + break; + case 16934400: + case 11289600: + wm8731->constraints = &wm8731_constraints_11289600_16934400; + break; + default: + return -EINVAL; + } + + wm8731->sysclk = freq; + + snd_soc_dapm_sync(dapm); + + return 0; +} + + +static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = 0; + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + iface |= 0x0040; + break; + case SND_SOC_DAIFMT_CBC_CFC: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0013; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0003; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + /* set iface */ + snd_soc_component_write(component, WM8731_IFACE, iface); + return 0; +} + +static int wm8731_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component); + int ret; + u16 reg; + + switch (level) { + case SND_SOC_BIAS_ON: + if (wm8731->mclk) { + ret = clk_prepare_enable(wm8731->mclk); + if (ret) + return ret; + } + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) + return ret; + + regcache_sync(wm8731->regmap); + } + + /* Clear PWROFF, gate CLKOUT, everything else as-is */ + reg = snd_soc_component_read(component, WM8731_PWR) & 0xff7f; + snd_soc_component_write(component, WM8731_PWR, reg | 0x0040); + break; + case SND_SOC_BIAS_OFF: + if (wm8731->mclk) + clk_disable_unprepare(wm8731->mclk); + snd_soc_component_write(component, WM8731_PWR, 0xffff); + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + regcache_mark_dirty(wm8731->regmap); + break; + } + return 0; +} + +static int wm8731_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(dai->component); + + if (wm8731->constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + wm8731->constraints); + + return 0; +} + +#define WM8731_RATES SNDRV_PCM_RATE_8000_96000 + +#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8731_dai_ops = { + .startup = wm8731_startup, + .hw_params = wm8731_hw_params, + .mute_stream = wm8731_mute, + .set_sysclk = wm8731_set_dai_sysclk, + .set_fmt = wm8731_set_dai_fmt, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8731_dai = { + .name = "wm8731-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8731_RATES, + .formats = WM8731_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8731_RATES, + .formats = WM8731_FORMATS,}, + .ops = &wm8731_dai_ops, + .symmetric_rate = 1, +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8731 = { + .set_bias_level = wm8731_set_bias_level, + .controls = wm8731_snd_controls, + .num_controls = ARRAY_SIZE(wm8731_snd_controls), + .dapm_widgets = wm8731_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets), + .dapm_routes = wm8731_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8731_intercon), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +int wm8731_init(struct device *dev, struct wm8731_priv *wm8731) +{ + int ret = 0, i; + + wm8731->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(wm8731->mclk)) { + ret = PTR_ERR(wm8731->mclk); + if (ret == -ENOENT) { + wm8731->mclk = NULL; + dev_warn(dev, "Assuming static MCLK\n"); + } else { + dev_err(dev, "Failed to get MCLK: %d\n", ret); + return ret; + } + } + + mutex_init(&wm8731->lock); + + for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++) + wm8731->supplies[i].supply = wm8731_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies), + wm8731->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = wm8731_reset(wm8731->regmap); + if (ret < 0) { + dev_err(dev, "Failed to issue reset: %d\n", ret); + goto err_regulator_enable; + } + + /* Clear POWEROFF, keep everything else disabled */ + regmap_write(wm8731->regmap, WM8731_PWR, 0x7f); + + /* Latch the update bits */ + regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0); + regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0); + + /* Disable bypass path by default */ + regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0); + + regcache_mark_dirty(wm8731->regmap); + + ret = devm_snd_soc_register_component(dev, + &soc_component_dev_wm8731, &wm8731_dai, 1); + if (ret != 0) { + dev_err(dev, "Failed to register CODEC: %d\n", ret); + goto err_regulator_enable; + } + + return 0; + +err_regulator_enable: + /* Regulators will be enabled by bias management */ + regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); + + return ret; +} +EXPORT_SYMBOL_GPL(wm8731_init); + +const struct regmap_config wm8731_regmap = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = WM8731_RESET, + .volatile_reg = wm8731_volatile, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm8731_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults), +}; +EXPORT_SYMBOL_GPL(wm8731_regmap); + +MODULE_DESCRIPTION("ASoC WM8731 driver"); +MODULE_AUTHOR("Richard Purdie"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h new file mode 100644 index 0000000000..8d5a7a9ca6 --- /dev/null +++ b/sound/soc/codecs/wm8731.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8731.h -- WM8731 Soc Audio driver + * + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * Based on wm8753.h + */ + +#ifndef _WM8731_H +#define _WM8731_H + +#include +#include +#include + +struct clk; +struct snd_pcm_hw_constraint_list; + +/* WM8731 register space */ + +#define WM8731_LINVOL 0x00 +#define WM8731_RINVOL 0x01 +#define WM8731_LOUT1V 0x02 +#define WM8731_ROUT1V 0x03 +#define WM8731_APANA 0x04 +#define WM8731_APDIGI 0x05 +#define WM8731_PWR 0x06 +#define WM8731_IFACE 0x07 +#define WM8731_SRATE 0x08 +#define WM8731_ACTIVE 0x09 +#define WM8731_RESET 0x0f + +#define WM8731_CACHEREGNUM 10 + +#define WM8731_SYSCLK_MCLK 0 +#define WM8731_SYSCLK_XTAL 1 + +#define WM8731_DAI 0 + +#define WM8731_NUM_SUPPLIES 4 + +/* codec private data */ +struct wm8731_priv { + struct regmap *regmap; + struct clk *mclk; + struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; + const struct snd_pcm_hw_constraint_list *constraints; + unsigned int sysclk; + int sysclk_type; + int playback_fs; + bool deemph; + + struct mutex lock; +}; + +extern const struct regmap_config wm8731_regmap; + +int wm8731_init(struct device *dev, struct wm8731_priv *wm8731); + +#endif diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c new file mode 100644 index 0000000000..0d231c289e --- /dev/null +++ b/sound/soc/codecs/wm8737.c @@ -0,0 +1,733 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8737.c -- WM8737 ALSA SoC Audio driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8737.h" + +#define WM8737_NUM_SUPPLIES 4 +static const char *wm8737_supply_names[WM8737_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "AVDD", + "MVDD", +}; + +/* codec private data */ +struct wm8737_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8737_NUM_SUPPLIES]; + unsigned int mclk; +}; + +static const struct reg_default wm8737_reg_defaults[] = { + { 0, 0x00C3 }, /* R0 - Left PGA volume */ + { 1, 0x00C3 }, /* R1 - Right PGA volume */ + { 2, 0x0007 }, /* R2 - AUDIO path L */ + { 3, 0x0007 }, /* R3 - AUDIO path R */ + { 4, 0x0000 }, /* R4 - 3D Enhance */ + { 5, 0x0000 }, /* R5 - ADC Control */ + { 6, 0x0000 }, /* R6 - Power Management */ + { 7, 0x000A }, /* R7 - Audio Format */ + { 8, 0x0000 }, /* R8 - Clocking */ + { 9, 0x000F }, /* R9 - MIC Preamp Control */ + { 10, 0x0003 }, /* R10 - Misc Bias Control */ + { 11, 0x0000 }, /* R11 - Noise Gate */ + { 12, 0x007C }, /* R12 - ALC1 */ + { 13, 0x0000 }, /* R13 - ALC2 */ + { 14, 0x0032 }, /* R14 - ALC3 */ +}; + +static bool wm8737_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8737_RESET: + return true; + default: + return false; + } +} + +static int wm8737_reset(struct snd_soc_component *component) +{ + return snd_soc_component_write(component, WM8737_RESET, 0); +} + +static const DECLARE_TLV_DB_RANGE(micboost_tlv, + 0, 0, TLV_DB_SCALE_ITEM(1300, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2800, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(3300, 0, 0) +); +static const DECLARE_TLV_DB_SCALE(pga_tlv, -9750, 50, 1); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(ng_tlv, -7800, 600, 0); +static const DECLARE_TLV_DB_SCALE(alc_max_tlv, -1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -1800, 100, 0); + +static const char *micbias_enum_text[] = { + "25%", + "50%", + "75%", + "100%", +}; + +static SOC_ENUM_SINGLE_DECL(micbias_enum, + WM8737_MIC_PREAMP_CONTROL, 0, micbias_enum_text); + +static const char *low_cutoff_text[] = { + "Low", "High" +}; + +static SOC_ENUM_SINGLE_DECL(low_3d, + WM8737_3D_ENHANCE, 6, low_cutoff_text); + +static const char *high_cutoff_text[] = { + "High", "Low" +}; + +static SOC_ENUM_SINGLE_DECL(high_3d, + WM8737_3D_ENHANCE, 5, high_cutoff_text); + +static const char *alc_fn_text[] = { + "Disabled", "Right", "Left", "Stereo" +}; + +static SOC_ENUM_SINGLE_DECL(alc_fn, + WM8737_ALC1, 7, alc_fn_text); + +static const char *alc_hold_text[] = { + "0", "2.67ms", "5.33ms", "10.66ms", "21.32ms", "42.64ms", "85.28ms", + "170.56ms", "341.12ms", "682.24ms", "1.364s", "2.728s", "5.458s", + "10.916s", "21.832s", "43.691s" +}; + +static SOC_ENUM_SINGLE_DECL(alc_hold, + WM8737_ALC2, 0, alc_hold_text); + +static const char *alc_atk_text[] = { + "8.4ms", "16.8ms", "33.6ms", "67.2ms", "134.4ms", "268.8ms", "537.6ms", + "1.075s", "2.15s", "4.3s", "8.6s" +}; + +static SOC_ENUM_SINGLE_DECL(alc_atk, + WM8737_ALC3, 0, alc_atk_text); + +static const char *alc_dcy_text[] = { + "33.6ms", "67.2ms", "134.4ms", "268.8ms", "537.6ms", "1.075s", "2.15s", + "4.3s", "8.6s", "17.2s", "34.41s" +}; + +static SOC_ENUM_SINGLE_DECL(alc_dcy, + WM8737_ALC3, 4, alc_dcy_text); + +static const struct snd_kcontrol_new wm8737_snd_controls[] = { +SOC_DOUBLE_R_TLV("Mic Boost Volume", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, + 6, 3, 0, micboost_tlv), +SOC_DOUBLE_R("Mic Boost Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, + 4, 1, 0), +SOC_DOUBLE("Mic ZC Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, + 3, 1, 0), + +SOC_DOUBLE_R_TLV("Capture Volume", WM8737_LEFT_PGA_VOLUME, + WM8737_RIGHT_PGA_VOLUME, 0, 255, 0, pga_tlv), +SOC_DOUBLE("Capture ZC Switch", WM8737_AUDIO_PATH_L, WM8737_AUDIO_PATH_R, + 2, 1, 0), + +SOC_DOUBLE("INPUT1 DC Bias Switch", WM8737_MISC_BIAS_CONTROL, 0, 1, 1, 0), + +SOC_ENUM("Mic PGA Bias", micbias_enum), +SOC_SINGLE("ADC Low Power Switch", WM8737_ADC_CONTROL, 2, 1, 0), +SOC_SINGLE("High Pass Filter Switch", WM8737_ADC_CONTROL, 0, 1, 1), +SOC_DOUBLE("Polarity Invert Switch", WM8737_ADC_CONTROL, 5, 6, 1, 0), + +SOC_SINGLE("3D Switch", WM8737_3D_ENHANCE, 0, 1, 0), +SOC_SINGLE("3D Depth", WM8737_3D_ENHANCE, 1, 15, 0), +SOC_ENUM("3D Low Cut-off", low_3d), +SOC_ENUM("3D High Cut-off", high_3d), +SOC_SINGLE_TLV("3D ADC Volume", WM8737_3D_ENHANCE, 7, 1, 1, adc_tlv), + +SOC_SINGLE("Noise Gate Switch", WM8737_NOISE_GATE, 0, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", WM8737_NOISE_GATE, 2, 7, 0, + ng_tlv), + +SOC_ENUM("ALC", alc_fn), +SOC_SINGLE_TLV("ALC Max Gain Volume", WM8737_ALC1, 4, 7, 0, alc_max_tlv), +SOC_SINGLE_TLV("ALC Target Volume", WM8737_ALC1, 0, 15, 0, alc_target_tlv), +SOC_ENUM("ALC Hold Time", alc_hold), +SOC_SINGLE("ALC ZC Switch", WM8737_ALC2, 4, 1, 0), +SOC_ENUM("ALC Attack Time", alc_atk), +SOC_ENUM("ALC Decay Time", alc_dcy), +}; + +static const char *linsel_text[] = { + "LINPUT1", "LINPUT2", "LINPUT3", "LINPUT1 DC", +}; + +static SOC_ENUM_SINGLE_DECL(linsel_enum, + WM8737_AUDIO_PATH_L, 7, linsel_text); + +static const struct snd_kcontrol_new linsel_mux = + SOC_DAPM_ENUM("LINSEL", linsel_enum); + + +static const char *rinsel_text[] = { + "RINPUT1", "RINPUT2", "RINPUT3", "RINPUT1 DC", +}; + +static SOC_ENUM_SINGLE_DECL(rinsel_enum, + WM8737_AUDIO_PATH_R, 7, rinsel_text); + +static const struct snd_kcontrol_new rinsel_mux = + SOC_DAPM_ENUM("RINSEL", rinsel_enum); + +static const char *bypass_text[] = { + "Direct", "Preamp" +}; + +static SOC_ENUM_SINGLE_DECL(lbypass_enum, + WM8737_MIC_PREAMP_CONTROL, 2, bypass_text); + +static const struct snd_kcontrol_new lbypass_mux = + SOC_DAPM_ENUM("Left Bypass", lbypass_enum); + + +static SOC_ENUM_SINGLE_DECL(rbypass_enum, + WM8737_MIC_PREAMP_CONTROL, 3, bypass_text); + +static const struct snd_kcontrol_new rbypass_mux = + SOC_DAPM_ENUM("Left Bypass", rbypass_enum); + +static const struct snd_soc_dapm_widget wm8737_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("LINPUT1"), +SND_SOC_DAPM_INPUT("LINPUT2"), +SND_SOC_DAPM_INPUT("LINPUT3"), +SND_SOC_DAPM_INPUT("RINPUT1"), +SND_SOC_DAPM_INPUT("RINPUT2"), +SND_SOC_DAPM_INPUT("RINPUT3"), +SND_SOC_DAPM_INPUT("LACIN"), +SND_SOC_DAPM_INPUT("RACIN"), + +SND_SOC_DAPM_MUX("LINSEL", SND_SOC_NOPM, 0, 0, &linsel_mux), +SND_SOC_DAPM_MUX("RINSEL", SND_SOC_NOPM, 0, 0, &rinsel_mux), + +SND_SOC_DAPM_MUX("Left Preamp Mux", SND_SOC_NOPM, 0, 0, &lbypass_mux), +SND_SOC_DAPM_MUX("Right Preamp Mux", SND_SOC_NOPM, 0, 0, &rbypass_mux), + +SND_SOC_DAPM_PGA("PGAL", WM8737_POWER_MANAGEMENT, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("PGAR", WM8737_POWER_MANAGEMENT, 4, 0, NULL, 0), + +SND_SOC_DAPM_DAC("ADCL", NULL, WM8737_POWER_MANAGEMENT, 3, 0), +SND_SOC_DAPM_DAC("ADCR", NULL, WM8737_POWER_MANAGEMENT, 2, 0), + +SND_SOC_DAPM_AIF_OUT("AIF", "Capture", 0, WM8737_POWER_MANAGEMENT, 6, 0), +}; + +static const struct snd_soc_dapm_route intercon[] = { + { "LINSEL", "LINPUT1", "LINPUT1" }, + { "LINSEL", "LINPUT2", "LINPUT2" }, + { "LINSEL", "LINPUT3", "LINPUT3" }, + { "LINSEL", "LINPUT1 DC", "LINPUT1" }, + + { "RINSEL", "RINPUT1", "RINPUT1" }, + { "RINSEL", "RINPUT2", "RINPUT2" }, + { "RINSEL", "RINPUT3", "RINPUT3" }, + { "RINSEL", "RINPUT1 DC", "RINPUT1" }, + + { "Left Preamp Mux", "Preamp", "LINSEL" }, + { "Left Preamp Mux", "Direct", "LACIN" }, + + { "Right Preamp Mux", "Preamp", "RINSEL" }, + { "Right Preamp Mux", "Direct", "RACIN" }, + + { "PGAL", NULL, "Left Preamp Mux" }, + { "PGAR", NULL, "Right Preamp Mux" }, + + { "ADCL", NULL, "PGAL" }, + { "ADCR", NULL, "PGAR" }, + + { "AIF", NULL, "ADCL" }, + { "AIF", NULL, "ADCR" }, +}; + +/* codec mclk clock divider coefficients */ +static const struct { + u32 mclk; + u32 rate; + u8 usb; + u8 sr; +} coeff_div[] = { + { 12288000, 8000, 0, 0x4 }, + { 12288000, 12000, 0, 0x8 }, + { 12288000, 16000, 0, 0xa }, + { 12288000, 24000, 0, 0x1c }, + { 12288000, 32000, 0, 0xc }, + { 12288000, 48000, 0, 0 }, + { 12288000, 96000, 0, 0xe }, + + { 11289600, 8000, 0, 0x14 }, + { 11289600, 11025, 0, 0x18 }, + { 11289600, 22050, 0, 0x1a }, + { 11289600, 44100, 0, 0x10 }, + { 11289600, 88200, 0, 0x1e }, + + { 18432000, 8000, 0, 0x5 }, + { 18432000, 12000, 0, 0x9 }, + { 18432000, 16000, 0, 0xb }, + { 18432000, 24000, 0, 0x1b }, + { 18432000, 32000, 0, 0xd }, + { 18432000, 48000, 0, 0x1 }, + { 18432000, 96000, 0, 0x1f }, + + { 16934400, 8000, 0, 0x15 }, + { 16934400, 11025, 0, 0x19 }, + { 16934400, 22050, 0, 0x1b }, + { 16934400, 44100, 0, 0x11 }, + { 16934400, 88200, 0, 0x1f }, + + { 12000000, 8000, 1, 0x4 }, + { 12000000, 11025, 1, 0x19 }, + { 12000000, 12000, 1, 0x8 }, + { 12000000, 16000, 1, 0xa }, + { 12000000, 22050, 1, 0x1b }, + { 12000000, 24000, 1, 0x1c }, + { 12000000, 32000, 1, 0xc }, + { 12000000, 44100, 1, 0x11 }, + { 12000000, 48000, 1, 0x0 }, + { 12000000, 88200, 1, 0x1f }, + { 12000000, 96000, 1, 0xe }, +}; + +static int wm8737_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8737_priv *wm8737 = snd_soc_component_get_drvdata(component); + int i; + u16 clocking = 0; + u16 af = 0; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate != params_rate(params)) + continue; + + if (coeff_div[i].mclk == wm8737->mclk) + break; + + if (coeff_div[i].mclk == wm8737->mclk * 2) { + clocking |= WM8737_CLKDIV2; + break; + } + } + + if (i == ARRAY_SIZE(coeff_div)) { + dev_err(component->dev, "%dHz MCLK can't support %dHz\n", + wm8737->mclk, params_rate(params)); + return -EINVAL; + } + + clocking |= coeff_div[i].usb | (coeff_div[i].sr << WM8737_SR_SHIFT); + + switch (params_width(params)) { + case 16: + break; + case 20: + af |= 0x8; + break; + case 24: + af |= 0x10; + break; + case 32: + af |= 0x18; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8737_AUDIO_FORMAT, WM8737_WL_MASK, af); + snd_soc_component_update_bits(component, WM8737_CLOCKING, + WM8737_USB_MODE | WM8737_CLKDIV2 | WM8737_SR_MASK, + clocking); + + return 0; +} + +static int wm8737_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8737_priv *wm8737 = snd_soc_component_get_drvdata(component); + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (freq == coeff_div[i].mclk || + freq == coeff_div[i].mclk * 2) { + wm8737->mclk = freq; + return 0; + } + } + + dev_err(component->dev, "MCLK rate %dHz not supported\n", freq); + + return -EINVAL; +} + + +static int wm8737_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 af = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + af |= WM8737_MS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + af |= 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + af |= 0x1; + break; + case SND_SOC_DAIFMT_DSP_A: + af |= 0x3; + break; + case SND_SOC_DAIFMT_DSP_B: + af |= 0x13; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + af |= WM8737_LRP; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8737_AUDIO_FORMAT, + WM8737_FORMAT_MASK | WM8737_LRP | WM8737_MS, af); + + return 0; +} + +static int wm8737_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8737_priv *wm8737 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID at 2*75k */ + snd_soc_component_update_bits(component, WM8737_MISC_BIAS_CONTROL, + WM8737_VMIDSEL_MASK, 0); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies), + wm8737->supplies); + if (ret != 0) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + + regcache_sync(wm8737->regmap); + + /* Fast VMID ramp at 2*2.5k */ + snd_soc_component_update_bits(component, WM8737_MISC_BIAS_CONTROL, + WM8737_VMIDSEL_MASK, + 2 << WM8737_VMIDSEL_SHIFT); + + /* Bring VMID up */ + snd_soc_component_update_bits(component, WM8737_POWER_MANAGEMENT, + WM8737_VMID_MASK | + WM8737_VREF_MASK, + WM8737_VMID_MASK | + WM8737_VREF_MASK); + + msleep(500); + } + + /* VMID at 2*300k */ + snd_soc_component_update_bits(component, WM8737_MISC_BIAS_CONTROL, + WM8737_VMIDSEL_MASK, + 1 << WM8737_VMIDSEL_SHIFT); + + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, WM8737_POWER_MANAGEMENT, + WM8737_VMID_MASK | WM8737_VREF_MASK, 0); + + regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), + wm8737->supplies); + break; + } + + return 0; +} + +#define WM8737_RATES SNDRV_PCM_RATE_8000_96000 + +#define WM8737_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8737_dai_ops = { + .hw_params = wm8737_hw_params, + .set_sysclk = wm8737_set_dai_sysclk, + .set_fmt = wm8737_set_dai_fmt, +}; + +static struct snd_soc_dai_driver wm8737_dai = { + .name = "wm8737", + .capture = { + .stream_name = "Capture", + .channels_min = 2, /* Mono modes not yet supported */ + .channels_max = 2, + .rates = WM8737_RATES, + .formats = WM8737_FORMATS, + }, + .ops = &wm8737_dai_ops, +}; + +static int wm8737_probe(struct snd_soc_component *component) +{ + struct wm8737_priv *wm8737 = snd_soc_component_get_drvdata(component); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8737->supplies), + wm8737->supplies); + if (ret != 0) { + dev_err(component->dev, "Failed to enable supplies: %d\n", ret); + goto err_get; + } + + ret = wm8737_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset\n"); + goto err_enable; + } + + snd_soc_component_update_bits(component, WM8737_LEFT_PGA_VOLUME, WM8737_LVU, + WM8737_LVU); + snd_soc_component_update_bits(component, WM8737_RIGHT_PGA_VOLUME, WM8737_RVU, + WM8737_RVU); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + /* Bias level configuration will have done an extra enable */ + regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); + + return 0; + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies); +err_get: + return ret; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8737 = { + .probe = wm8737_probe, + .set_bias_level = wm8737_set_bias_level, + .controls = wm8737_snd_controls, + .num_controls = ARRAY_SIZE(wm8737_snd_controls), + .dapm_widgets = wm8737_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8737_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8737_of_match[] = { + { .compatible = "wlf,wm8737", }, + { } +}; + +MODULE_DEVICE_TABLE(of, wm8737_of_match); + +static const struct regmap_config wm8737_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8737_MAX_REGISTER, + + .reg_defaults = wm8737_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8737_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm8737_volatile, +}; + +#if IS_ENABLED(CONFIG_I2C) +static int wm8737_i2c_probe(struct i2c_client *i2c) +{ + struct wm8737_priv *wm8737; + int ret, i; + + wm8737 = devm_kzalloc(&i2c->dev, sizeof(struct wm8737_priv), + GFP_KERNEL); + if (wm8737 == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(wm8737->supplies); i++) + wm8737->supplies[i].supply = wm8737_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8737->supplies), + wm8737->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8737->regmap = devm_regmap_init_i2c(i2c, &wm8737_regmap); + if (IS_ERR(wm8737->regmap)) + return PTR_ERR(wm8737->regmap); + + i2c_set_clientdata(i2c, wm8737); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8737, &wm8737_dai, 1); + + return ret; + +} + +static const struct i2c_device_id wm8737_i2c_id[] = { + { "wm8737", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8737_i2c_id); + +static struct i2c_driver wm8737_i2c_driver = { + .driver = { + .name = "wm8737", + .of_match_table = wm8737_of_match, + }, + .probe = wm8737_i2c_probe, + .id_table = wm8737_i2c_id, +}; +#endif + +#if defined(CONFIG_SPI_MASTER) +static int wm8737_spi_probe(struct spi_device *spi) +{ + struct wm8737_priv *wm8737; + int ret, i; + + wm8737 = devm_kzalloc(&spi->dev, sizeof(struct wm8737_priv), + GFP_KERNEL); + if (wm8737 == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(wm8737->supplies); i++) + wm8737->supplies[i].supply = wm8737_supply_names[i]; + + ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8737->supplies), + wm8737->supplies); + if (ret != 0) { + dev_err(&spi->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8737->regmap = devm_regmap_init_spi(spi, &wm8737_regmap); + if (IS_ERR(wm8737->regmap)) + return PTR_ERR(wm8737->regmap); + + spi_set_drvdata(spi, wm8737); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8737, &wm8737_dai, 1); + + return ret; +} + +static struct spi_driver wm8737_spi_driver = { + .driver = { + .name = "wm8737", + .of_match_table = wm8737_of_match, + }, + .probe = wm8737_spi_probe, +}; +#endif /* CONFIG_SPI_MASTER */ + +static int __init wm8737_modinit(void) +{ + int ret; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8737_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8737 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8737_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8737 SPI driver: %d\n", + ret); + } +#endif + return 0; +} +module_init(wm8737_modinit); + +static void __exit wm8737_exit(void) +{ +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8737_spi_driver); +#endif +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8737_i2c_driver); +#endif +} +module_exit(wm8737_exit); + +MODULE_DESCRIPTION("ASoC WM8737 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8737.h b/sound/soc/codecs/wm8737.h new file mode 100644 index 0000000000..b95b85e03f --- /dev/null +++ b/sound/soc/codecs/wm8737.h @@ -0,0 +1,319 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _WM8737_H +#define _WM8737_H + +/* + * wm8737.c -- WM8523 ALSA SoC Audio driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +/* + * Register values. + */ +#define WM8737_LEFT_PGA_VOLUME 0x00 +#define WM8737_RIGHT_PGA_VOLUME 0x01 +#define WM8737_AUDIO_PATH_L 0x02 +#define WM8737_AUDIO_PATH_R 0x03 +#define WM8737_3D_ENHANCE 0x04 +#define WM8737_ADC_CONTROL 0x05 +#define WM8737_POWER_MANAGEMENT 0x06 +#define WM8737_AUDIO_FORMAT 0x07 +#define WM8737_CLOCKING 0x08 +#define WM8737_MIC_PREAMP_CONTROL 0x09 +#define WM8737_MISC_BIAS_CONTROL 0x0A +#define WM8737_NOISE_GATE 0x0B +#define WM8737_ALC1 0x0C +#define WM8737_ALC2 0x0D +#define WM8737_ALC3 0x0E +#define WM8737_RESET 0x0F + +#define WM8737_REGISTER_COUNT 16 +#define WM8737_MAX_REGISTER 0x0F + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Left PGA volume + */ +#define WM8737_LVU 0x0100 /* LVU */ +#define WM8737_LVU_MASK 0x0100 /* LVU */ +#define WM8737_LVU_SHIFT 8 /* LVU */ +#define WM8737_LVU_WIDTH 1 /* LVU */ +#define WM8737_LINVOL_MASK 0x00FF /* LINVOL - [7:0] */ +#define WM8737_LINVOL_SHIFT 0 /* LINVOL - [7:0] */ +#define WM8737_LINVOL_WIDTH 8 /* LINVOL - [7:0] */ + +/* + * R1 (0x01) - Right PGA volume + */ +#define WM8737_RVU 0x0100 /* RVU */ +#define WM8737_RVU_MASK 0x0100 /* RVU */ +#define WM8737_RVU_SHIFT 8 /* RVU */ +#define WM8737_RVU_WIDTH 1 /* RVU */ +#define WM8737_RINVOL_MASK 0x00FF /* RINVOL - [7:0] */ +#define WM8737_RINVOL_SHIFT 0 /* RINVOL - [7:0] */ +#define WM8737_RINVOL_WIDTH 8 /* RINVOL - [7:0] */ + +/* + * R2 (0x02) - AUDIO path L + */ +#define WM8737_LINSEL_MASK 0x0180 /* LINSEL - [8:7] */ +#define WM8737_LINSEL_SHIFT 7 /* LINSEL - [8:7] */ +#define WM8737_LINSEL_WIDTH 2 /* LINSEL - [8:7] */ +#define WM8737_LMICBOOST_MASK 0x0060 /* LMICBOOST - [6:5] */ +#define WM8737_LMICBOOST_SHIFT 5 /* LMICBOOST - [6:5] */ +#define WM8737_LMICBOOST_WIDTH 2 /* LMICBOOST - [6:5] */ +#define WM8737_LMBE 0x0010 /* LMBE */ +#define WM8737_LMBE_MASK 0x0010 /* LMBE */ +#define WM8737_LMBE_SHIFT 4 /* LMBE */ +#define WM8737_LMBE_WIDTH 1 /* LMBE */ +#define WM8737_LMZC 0x0008 /* LMZC */ +#define WM8737_LMZC_MASK 0x0008 /* LMZC */ +#define WM8737_LMZC_SHIFT 3 /* LMZC */ +#define WM8737_LMZC_WIDTH 1 /* LMZC */ +#define WM8737_LPZC 0x0004 /* LPZC */ +#define WM8737_LPZC_MASK 0x0004 /* LPZC */ +#define WM8737_LPZC_SHIFT 2 /* LPZC */ +#define WM8737_LPZC_WIDTH 1 /* LPZC */ +#define WM8737_LZCTO_MASK 0x0003 /* LZCTO - [1:0] */ +#define WM8737_LZCTO_SHIFT 0 /* LZCTO - [1:0] */ +#define WM8737_LZCTO_WIDTH 2 /* LZCTO - [1:0] */ + +/* + * R3 (0x03) - AUDIO path R + */ +#define WM8737_RINSEL_MASK 0x0180 /* RINSEL - [8:7] */ +#define WM8737_RINSEL_SHIFT 7 /* RINSEL - [8:7] */ +#define WM8737_RINSEL_WIDTH 2 /* RINSEL - [8:7] */ +#define WM8737_RMICBOOST_MASK 0x0060 /* RMICBOOST - [6:5] */ +#define WM8737_RMICBOOST_SHIFT 5 /* RMICBOOST - [6:5] */ +#define WM8737_RMICBOOST_WIDTH 2 /* RMICBOOST - [6:5] */ +#define WM8737_RMBE 0x0010 /* RMBE */ +#define WM8737_RMBE_MASK 0x0010 /* RMBE */ +#define WM8737_RMBE_SHIFT 4 /* RMBE */ +#define WM8737_RMBE_WIDTH 1 /* RMBE */ +#define WM8737_RMZC 0x0008 /* RMZC */ +#define WM8737_RMZC_MASK 0x0008 /* RMZC */ +#define WM8737_RMZC_SHIFT 3 /* RMZC */ +#define WM8737_RMZC_WIDTH 1 /* RMZC */ +#define WM8737_RPZC 0x0004 /* RPZC */ +#define WM8737_RPZC_MASK 0x0004 /* RPZC */ +#define WM8737_RPZC_SHIFT 2 /* RPZC */ +#define WM8737_RPZC_WIDTH 1 /* RPZC */ +#define WM8737_RZCTO_MASK 0x0003 /* RZCTO - [1:0] */ +#define WM8737_RZCTO_SHIFT 0 /* RZCTO - [1:0] */ +#define WM8737_RZCTO_WIDTH 2 /* RZCTO - [1:0] */ + +/* + * R4 (0x04) - 3D Enhance + */ +#define WM8737_DIV2 0x0080 /* DIV2 */ +#define WM8737_DIV2_MASK 0x0080 /* DIV2 */ +#define WM8737_DIV2_SHIFT 7 /* DIV2 */ +#define WM8737_DIV2_WIDTH 1 /* DIV2 */ +#define WM8737_3DLC 0x0040 /* 3DLC */ +#define WM8737_3DLC_MASK 0x0040 /* 3DLC */ +#define WM8737_3DLC_SHIFT 6 /* 3DLC */ +#define WM8737_3DLC_WIDTH 1 /* 3DLC */ +#define WM8737_3DUC 0x0020 /* 3DUC */ +#define WM8737_3DUC_MASK 0x0020 /* 3DUC */ +#define WM8737_3DUC_SHIFT 5 /* 3DUC */ +#define WM8737_3DUC_WIDTH 1 /* 3DUC */ +#define WM8737_3DDEPTH_MASK 0x001E /* 3DDEPTH - [4:1] */ +#define WM8737_3DDEPTH_SHIFT 1 /* 3DDEPTH - [4:1] */ +#define WM8737_3DDEPTH_WIDTH 4 /* 3DDEPTH - [4:1] */ +#define WM8737_3DE 0x0001 /* 3DE */ +#define WM8737_3DE_MASK 0x0001 /* 3DE */ +#define WM8737_3DE_SHIFT 0 /* 3DE */ +#define WM8737_3DE_WIDTH 1 /* 3DE */ + +/* + * R5 (0x05) - ADC Control + */ +#define WM8737_MONOMIX_MASK 0x0180 /* MONOMIX - [8:7] */ +#define WM8737_MONOMIX_SHIFT 7 /* MONOMIX - [8:7] */ +#define WM8737_MONOMIX_WIDTH 2 /* MONOMIX - [8:7] */ +#define WM8737_POLARITY_MASK 0x0060 /* POLARITY - [6:5] */ +#define WM8737_POLARITY_SHIFT 5 /* POLARITY - [6:5] */ +#define WM8737_POLARITY_WIDTH 2 /* POLARITY - [6:5] */ +#define WM8737_HPOR 0x0010 /* HPOR */ +#define WM8737_HPOR_MASK 0x0010 /* HPOR */ +#define WM8737_HPOR_SHIFT 4 /* HPOR */ +#define WM8737_HPOR_WIDTH 1 /* HPOR */ +#define WM8737_LP 0x0004 /* LP */ +#define WM8737_LP_MASK 0x0004 /* LP */ +#define WM8737_LP_SHIFT 2 /* LP */ +#define WM8737_LP_WIDTH 1 /* LP */ +#define WM8737_MONOUT 0x0002 /* MONOUT */ +#define WM8737_MONOUT_MASK 0x0002 /* MONOUT */ +#define WM8737_MONOUT_SHIFT 1 /* MONOUT */ +#define WM8737_MONOUT_WIDTH 1 /* MONOUT */ +#define WM8737_ADCHPD 0x0001 /* ADCHPD */ +#define WM8737_ADCHPD_MASK 0x0001 /* ADCHPD */ +#define WM8737_ADCHPD_SHIFT 0 /* ADCHPD */ +#define WM8737_ADCHPD_WIDTH 1 /* ADCHPD */ + +/* + * R6 (0x06) - Power Management + */ +#define WM8737_VMID 0x0100 /* VMID */ +#define WM8737_VMID_MASK 0x0100 /* VMID */ +#define WM8737_VMID_SHIFT 8 /* VMID */ +#define WM8737_VMID_WIDTH 1 /* VMID */ +#define WM8737_VREF 0x0080 /* VREF */ +#define WM8737_VREF_MASK 0x0080 /* VREF */ +#define WM8737_VREF_SHIFT 7 /* VREF */ +#define WM8737_VREF_WIDTH 1 /* VREF */ +#define WM8737_AI 0x0040 /* AI */ +#define WM8737_AI_MASK 0x0040 /* AI */ +#define WM8737_AI_SHIFT 6 /* AI */ +#define WM8737_AI_WIDTH 1 /* AI */ +#define WM8737_PGL 0x0020 /* PGL */ +#define WM8737_PGL_MASK 0x0020 /* PGL */ +#define WM8737_PGL_SHIFT 5 /* PGL */ +#define WM8737_PGL_WIDTH 1 /* PGL */ +#define WM8737_PGR 0x0010 /* PGR */ +#define WM8737_PGR_MASK 0x0010 /* PGR */ +#define WM8737_PGR_SHIFT 4 /* PGR */ +#define WM8737_PGR_WIDTH 1 /* PGR */ +#define WM8737_ADL 0x0008 /* ADL */ +#define WM8737_ADL_MASK 0x0008 /* ADL */ +#define WM8737_ADL_SHIFT 3 /* ADL */ +#define WM8737_ADL_WIDTH 1 /* ADL */ +#define WM8737_ADR 0x0004 /* ADR */ +#define WM8737_ADR_MASK 0x0004 /* ADR */ +#define WM8737_ADR_SHIFT 2 /* ADR */ +#define WM8737_ADR_WIDTH 1 /* ADR */ +#define WM8737_MICBIAS_MASK 0x0003 /* MICBIAS - [1:0] */ +#define WM8737_MICBIAS_SHIFT 0 /* MICBIAS - [1:0] */ +#define WM8737_MICBIAS_WIDTH 2 /* MICBIAS - [1:0] */ + +/* + * R7 (0x07) - Audio Format + */ +#define WM8737_SDODIS 0x0080 /* SDODIS */ +#define WM8737_SDODIS_MASK 0x0080 /* SDODIS */ +#define WM8737_SDODIS_SHIFT 7 /* SDODIS */ +#define WM8737_SDODIS_WIDTH 1 /* SDODIS */ +#define WM8737_MS 0x0040 /* MS */ +#define WM8737_MS_MASK 0x0040 /* MS */ +#define WM8737_MS_SHIFT 6 /* MS */ +#define WM8737_MS_WIDTH 1 /* MS */ +#define WM8737_LRP 0x0010 /* LRP */ +#define WM8737_LRP_MASK 0x0010 /* LRP */ +#define WM8737_LRP_SHIFT 4 /* LRP */ +#define WM8737_LRP_WIDTH 1 /* LRP */ +#define WM8737_WL_MASK 0x000C /* WL - [3:2] */ +#define WM8737_WL_SHIFT 2 /* WL - [3:2] */ +#define WM8737_WL_WIDTH 2 /* WL - [3:2] */ +#define WM8737_FORMAT_MASK 0x0003 /* FORMAT - [1:0] */ +#define WM8737_FORMAT_SHIFT 0 /* FORMAT - [1:0] */ +#define WM8737_FORMAT_WIDTH 2 /* FORMAT - [1:0] */ + +/* + * R8 (0x08) - Clocking + */ +#define WM8737_AUTODETECT 0x0080 /* AUTODETECT */ +#define WM8737_AUTODETECT_MASK 0x0080 /* AUTODETECT */ +#define WM8737_AUTODETECT_SHIFT 7 /* AUTODETECT */ +#define WM8737_AUTODETECT_WIDTH 1 /* AUTODETECT */ +#define WM8737_CLKDIV2 0x0040 /* CLKDIV2 */ +#define WM8737_CLKDIV2_MASK 0x0040 /* CLKDIV2 */ +#define WM8737_CLKDIV2_SHIFT 6 /* CLKDIV2 */ +#define WM8737_CLKDIV2_WIDTH 1 /* CLKDIV2 */ +#define WM8737_SR_MASK 0x003E /* SR - [5:1] */ +#define WM8737_SR_SHIFT 1 /* SR - [5:1] */ +#define WM8737_SR_WIDTH 5 /* SR - [5:1] */ +#define WM8737_USB_MODE 0x0001 /* USB MODE */ +#define WM8737_USB_MODE_MASK 0x0001 /* USB MODE */ +#define WM8737_USB_MODE_SHIFT 0 /* USB MODE */ +#define WM8737_USB_MODE_WIDTH 1 /* USB MODE */ + +/* + * R9 (0x09) - MIC Preamp Control + */ +#define WM8737_RBYPEN 0x0008 /* RBYPEN */ +#define WM8737_RBYPEN_MASK 0x0008 /* RBYPEN */ +#define WM8737_RBYPEN_SHIFT 3 /* RBYPEN */ +#define WM8737_RBYPEN_WIDTH 1 /* RBYPEN */ +#define WM8737_LBYPEN 0x0004 /* LBYPEN */ +#define WM8737_LBYPEN_MASK 0x0004 /* LBYPEN */ +#define WM8737_LBYPEN_SHIFT 2 /* LBYPEN */ +#define WM8737_LBYPEN_WIDTH 1 /* LBYPEN */ +#define WM8737_MBCTRL_MASK 0x0003 /* MBCTRL - [1:0] */ +#define WM8737_MBCTRL_SHIFT 0 /* MBCTRL - [1:0] */ +#define WM8737_MBCTRL_WIDTH 2 /* MBCTRL - [1:0] */ + +/* + * R10 (0x0A) - Misc Bias Control + */ +#define WM8737_VMIDSEL_MASK 0x000C /* VMIDSEL - [3:2] */ +#define WM8737_VMIDSEL_SHIFT 2 /* VMIDSEL - [3:2] */ +#define WM8737_VMIDSEL_WIDTH 2 /* VMIDSEL - [3:2] */ +#define WM8737_LINPUT1_DC_BIAS_ENABLE 0x0002 /* LINPUT1 DC BIAS ENABLE */ +#define WM8737_LINPUT1_DC_BIAS_ENABLE_MASK 0x0002 /* LINPUT1 DC BIAS ENABLE */ +#define WM8737_LINPUT1_DC_BIAS_ENABLE_SHIFT 1 /* LINPUT1 DC BIAS ENABLE */ +#define WM8737_LINPUT1_DC_BIAS_ENABLE_WIDTH 1 /* LINPUT1 DC BIAS ENABLE */ +#define WM8737_RINPUT1_DC_BIAS_ENABLE 0x0001 /* RINPUT1 DC BIAS ENABLE */ +#define WM8737_RINPUT1_DC_BIAS_ENABLE_MASK 0x0001 /* RINPUT1 DC BIAS ENABLE */ +#define WM8737_RINPUT1_DC_BIAS_ENABLE_SHIFT 0 /* RINPUT1 DC BIAS ENABLE */ +#define WM8737_RINPUT1_DC_BIAS_ENABLE_WIDTH 1 /* RINPUT1 DC BIAS ENABLE */ + +/* + * R11 (0x0B) - Noise Gate + */ +#define WM8737_NGTH_MASK 0x001C /* NGTH - [4:2] */ +#define WM8737_NGTH_SHIFT 2 /* NGTH - [4:2] */ +#define WM8737_NGTH_WIDTH 3 /* NGTH - [4:2] */ +#define WM8737_NGAT 0x0001 /* NGAT */ +#define WM8737_NGAT_MASK 0x0001 /* NGAT */ +#define WM8737_NGAT_SHIFT 0 /* NGAT */ +#define WM8737_NGAT_WIDTH 1 /* NGAT */ + +/* + * R12 (0x0C) - ALC1 + */ +#define WM8737_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */ +#define WM8737_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */ +#define WM8737_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */ +#define WM8737_MAX_GAIN_MASK 0x0070 /* MAX GAIN - [6:4] */ +#define WM8737_MAX_GAIN_SHIFT 4 /* MAX GAIN - [6:4] */ +#define WM8737_MAX_GAIN_WIDTH 3 /* MAX GAIN - [6:4] */ +#define WM8737_ALCL_MASK 0x000F /* ALCL - [3:0] */ +#define WM8737_ALCL_SHIFT 0 /* ALCL - [3:0] */ +#define WM8737_ALCL_WIDTH 4 /* ALCL - [3:0] */ + +/* + * R13 (0x0D) - ALC2 + */ +#define WM8737_ALCZCE 0x0010 /* ALCZCE */ +#define WM8737_ALCZCE_MASK 0x0010 /* ALCZCE */ +#define WM8737_ALCZCE_SHIFT 4 /* ALCZCE */ +#define WM8737_ALCZCE_WIDTH 1 /* ALCZCE */ +#define WM8737_HLD_MASK 0x000F /* HLD - [3:0] */ +#define WM8737_HLD_SHIFT 0 /* HLD - [3:0] */ +#define WM8737_HLD_WIDTH 4 /* HLD - [3:0] */ + +/* + * R14 (0x0E) - ALC3 + */ +#define WM8737_DCY_MASK 0x00F0 /* DCY - [7:4] */ +#define WM8737_DCY_SHIFT 4 /* DCY - [7:4] */ +#define WM8737_DCY_WIDTH 4 /* DCY - [7:4] */ +#define WM8737_ATK_MASK 0x000F /* ATK - [3:0] */ +#define WM8737_ATK_SHIFT 0 /* ATK - [3:0] */ +#define WM8737_ATK_WIDTH 4 /* ATK - [3:0] */ + +/* + * R15 (0x0F) - Reset + */ +#define WM8737_RESET_MASK 0x01FF /* RESET - [8:0] */ +#define WM8737_RESET_SHIFT 0 /* RESET - [8:0] */ +#define WM8737_RESET_WIDTH 9 /* RESET - [8:0] */ + +#endif diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c new file mode 100644 index 0000000000..19e8fc4062 --- /dev/null +++ b/sound/soc/codecs/wm8741.c @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8741.c -- WM8741 ALSA SoC Audio driver + * + * Copyright 2010-1 Wolfson Microelectronics plc + * + * Author: Ian Lartey + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8741.h" + +#define WM8741_NUM_SUPPLIES 2 +static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = { + "AVDD", + "DVDD", +}; + +/* codec private data */ +struct wm8741_priv { + struct wm8741_platform_data pdata; + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES]; + unsigned int sysclk; + const struct snd_pcm_hw_constraint_list *sysclk_constraints; +}; + +static const struct reg_default wm8741_reg_defaults[] = { + { 0, 0x0000 }, /* R0 - DACLLSB Attenuation */ + { 1, 0x0000 }, /* R1 - DACLMSB Attenuation */ + { 2, 0x0000 }, /* R2 - DACRLSB Attenuation */ + { 3, 0x0000 }, /* R3 - DACRMSB Attenuation */ + { 4, 0x0000 }, /* R4 - Volume Control */ + { 5, 0x000A }, /* R5 - Format Control */ + { 6, 0x0000 }, /* R6 - Filter Control */ + { 7, 0x0000 }, /* R7 - Mode Control 1 */ + { 8, 0x0002 }, /* R8 - Mode Control 2 */ + { 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */ +}; + +static int wm8741_reset(struct snd_soc_component *component) +{ + return snd_soc_component_write(component, WM8741_RESET, 0); +} + +static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0); + +static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = { +SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, + WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine), +SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, + WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv), +}; + +static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = { +SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION, + 1, 255, 1, dac_tlv_fine), +SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION, + 0, 511, 1, dac_tlv), +}; + +static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = { +SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION, + 1, 255, 1, dac_tlv_fine), +SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION, + 0, 511, 1, dac_tlv), +}; + +static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_OUTPUT("VOUTLP"), +SND_SOC_DAPM_OUTPUT("VOUTLN"), +SND_SOC_DAPM_OUTPUT("VOUTRP"), +SND_SOC_DAPM_OUTPUT("VOUTRN"), +}; + +static const struct snd_soc_dapm_route wm8741_dapm_routes[] = { + { "VOUTLP", NULL, "DACL" }, + { "VOUTLN", NULL, "DACL" }, + { "VOUTRP", NULL, "DACR" }, + { "VOUTRN", NULL, "DACR" }, +}; + +static const unsigned int rates_11289[] = { + 44100, 88200, +}; + +static const struct snd_pcm_hw_constraint_list constraints_11289 = { + .count = ARRAY_SIZE(rates_11289), + .list = rates_11289, +}; + +static const unsigned int rates_12288[] = { + 32000, 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static const unsigned int rates_16384[] = { + 32000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_16384 = { + .count = ARRAY_SIZE(rates_16384), + .list = rates_16384, +}; + +static const unsigned int rates_16934[] = { + 44100, 88200, +}; + +static const struct snd_pcm_hw_constraint_list constraints_16934 = { + .count = ARRAY_SIZE(rates_16934), + .list = rates_16934, +}; + +static const unsigned int rates_18432[] = { + 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_18432 = { + .count = ARRAY_SIZE(rates_18432), + .list = rates_18432, +}; + +static const unsigned int rates_22579[] = { + 44100, 88200, 176400 +}; + +static const struct snd_pcm_hw_constraint_list constraints_22579 = { + .count = ARRAY_SIZE(rates_22579), + .list = rates_22579, +}; + +static const unsigned int rates_24576[] = { + 32000, 48000, 96000, 192000 +}; + +static const struct snd_pcm_hw_constraint_list constraints_24576 = { + .count = ARRAY_SIZE(rates_24576), + .list = rates_24576, +}; + +static const unsigned int rates_36864[] = { + 48000, 96000, 192000 +}; + +static const struct snd_pcm_hw_constraint_list constraints_36864 = { + .count = ARRAY_SIZE(rates_36864), + .list = rates_36864, +}; + +static int wm8741_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component); + + if (wm8741->sysclk) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + wm8741->sysclk_constraints); + + return 0; +} + +static int wm8741_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component); + unsigned int iface, mode; + int i; + + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + if (!wm8741->sysclk) { + dev_err(component->dev, + "No MCLK configured, call set_sysclk() on init or in hw_params\n"); + return -EINVAL; + } + + /* Find a supported LRCLK rate */ + for (i = 0; i < wm8741->sysclk_constraints->count; i++) { + if (wm8741->sysclk_constraints->list[i] == params_rate(params)) + break; + } + + if (i == wm8741->sysclk_constraints->count) { + dev_err(component->dev, "LRCLK %d unsupported with MCLK %d\n", + params_rate(params), wm8741->sysclk); + return -EINVAL; + } + + /* bit size */ + switch (params_width(params)) { + case 16: + iface = 0x0; + break; + case 20: + iface = 0x1; + break; + case 24: + iface = 0x2; + break; + case 32: + iface = 0x3; + break; + default: + dev_dbg(component->dev, "wm8741_hw_params: Unsupported bit size param = %d", + params_width(params)); + return -EINVAL; + } + + /* oversampling rate */ + if (params_rate(params) > 96000) + mode = 0x40; + else if (params_rate(params) > 48000) + mode = 0x20; + else + mode = 0x00; + + dev_dbg(component->dev, "wm8741_hw_params: bit size param = %d, rate param = %d", + params_width(params), params_rate(params)); + + snd_soc_component_update_bits(component, WM8741_FORMAT_CONTROL, WM8741_IWL_MASK, + iface); + snd_soc_component_update_bits(component, WM8741_MODE_CONTROL_1, WM8741_OSR_MASK, + mode); + + return 0; +} + +static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq); + + switch (freq) { + case 0: + wm8741->sysclk_constraints = NULL; + break; + case 11289600: + wm8741->sysclk_constraints = &constraints_11289; + break; + case 12288000: + wm8741->sysclk_constraints = &constraints_12288; + break; + case 16384000: + wm8741->sysclk_constraints = &constraints_16384; + break; + case 16934400: + wm8741->sysclk_constraints = &constraints_16934; + break; + case 18432000: + wm8741->sysclk_constraints = &constraints_18432; + break; + case 22579200: + case 33868800: + wm8741->sysclk_constraints = &constraints_22579; + break; + case 24576000: + wm8741->sysclk_constraints = &constraints_24576; + break; + case 36864000: + wm8741->sysclk_constraints = &constraints_36864; + break; + default: + return -EINVAL; + } + + wm8741->sysclk = freq; + return 0; +} + +static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + unsigned int iface; + + /* check master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface = 0x08; + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface = 0x00; + break; + case SND_SOC_DAIFMT_LEFT_J: + iface = 0x04; + break; + case SND_SOC_DAIFMT_DSP_A: + iface = 0x0C; + break; + case SND_SOC_DAIFMT_DSP_B: + iface = 0x1C; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x10; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x20; + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x30; + break; + default: + return -EINVAL; + } + + + dev_dbg(component->dev, "wm8741_set_dai_fmt: Format=%x, Clock Inv=%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK, + ((fmt & SND_SOC_DAIFMT_INV_MASK))); + + snd_soc_component_update_bits(component, WM8741_FORMAT_CONTROL, + WM8741_BCP_MASK | WM8741_LRP_MASK | WM8741_FMT_MASK, + iface); + + return 0; +} + +static int wm8741_mute(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + + snd_soc_component_update_bits(component, WM8741_VOLUME_CONTROL, + WM8741_SOFT_MASK, !!mute << WM8741_SOFT_SHIFT); + return 0; +} + +#define WM8741_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) + +#define WM8741_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8741_dai_ops = { + .startup = wm8741_startup, + .hw_params = wm8741_hw_params, + .set_sysclk = wm8741_set_dai_sysclk, + .set_fmt = wm8741_set_dai_fmt, + .mute_stream = wm8741_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8741_dai = { + .name = "wm8741", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8741_RATES, + .formats = WM8741_FORMATS, + }, + .ops = &wm8741_dai_ops, +}; + +#ifdef CONFIG_PM +static int wm8741_resume(struct snd_soc_component *component) +{ + snd_soc_component_cache_sync(component); + return 0; +} +#else +#define wm8741_resume NULL +#endif + +static int wm8741_configure(struct snd_soc_component *component) +{ + struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component); + + /* Configure differential mode */ + switch (wm8741->pdata.diff_mode) { + case WM8741_DIFF_MODE_STEREO: + case WM8741_DIFF_MODE_STEREO_REVERSED: + case WM8741_DIFF_MODE_MONO_LEFT: + case WM8741_DIFF_MODE_MONO_RIGHT: + snd_soc_component_update_bits(component, WM8741_MODE_CONTROL_2, + WM8741_DIFF_MASK, + wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT); + break; + default: + return -EINVAL; + } + + /* Change some default settings - latch VU */ + snd_soc_component_update_bits(component, WM8741_DACLLSB_ATTENUATION, + WM8741_UPDATELL, WM8741_UPDATELL); + snd_soc_component_update_bits(component, WM8741_DACLMSB_ATTENUATION, + WM8741_UPDATELM, WM8741_UPDATELM); + snd_soc_component_update_bits(component, WM8741_DACRLSB_ATTENUATION, + WM8741_UPDATERL, WM8741_UPDATERL); + snd_soc_component_update_bits(component, WM8741_DACRMSB_ATTENUATION, + WM8741_UPDATERM, WM8741_UPDATERM); + + return 0; +} + +static int wm8741_add_controls(struct snd_soc_component *component) +{ + struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component); + + switch (wm8741->pdata.diff_mode) { + case WM8741_DIFF_MODE_STEREO: + case WM8741_DIFF_MODE_STEREO_REVERSED: + snd_soc_add_component_controls(component, + wm8741_snd_controls_stereo, + ARRAY_SIZE(wm8741_snd_controls_stereo)); + break; + case WM8741_DIFF_MODE_MONO_LEFT: + snd_soc_add_component_controls(component, + wm8741_snd_controls_mono_left, + ARRAY_SIZE(wm8741_snd_controls_mono_left)); + break; + case WM8741_DIFF_MODE_MONO_RIGHT: + snd_soc_add_component_controls(component, + wm8741_snd_controls_mono_right, + ARRAY_SIZE(wm8741_snd_controls_mono_right)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8741_probe(struct snd_soc_component *component) +{ + struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component); + int ret = 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies), + wm8741->supplies); + if (ret != 0) { + dev_err(component->dev, "Failed to enable supplies: %d\n", ret); + goto err_get; + } + + ret = wm8741_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset\n"); + goto err_enable; + } + + ret = wm8741_configure(component); + if (ret < 0) { + dev_err(component->dev, "Failed to change default settings\n"); + goto err_enable; + } + + ret = wm8741_add_controls(component); + if (ret < 0) { + dev_err(component->dev, "Failed to add controls\n"); + goto err_enable; + } + + dev_dbg(component->dev, "Successful registration\n"); + return ret; + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies); +err_get: + return ret; +} + +static void wm8741_remove(struct snd_soc_component *component) +{ + struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component); + + regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies); +} + +static const struct snd_soc_component_driver soc_component_dev_wm8741 = { + .probe = wm8741_probe, + .remove = wm8741_remove, + .resume = wm8741_resume, + .dapm_widgets = wm8741_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets), + .dapm_routes = wm8741_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8741_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8741_of_match[] = { + { .compatible = "wlf,wm8741", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8741_of_match); + +static const struct regmap_config wm8741_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8741_MAX_REGISTER, + + .reg_defaults = wm8741_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) +{ + const struct wm8741_platform_data *pdata = dev_get_platdata(dev); + u32 diff_mode; + + if (dev->of_node) { + if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode) + >= 0) + wm8741->pdata.diff_mode = diff_mode; + } else { + if (pdata != NULL) + memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata)); + } + + return 0; +} + +#if IS_ENABLED(CONFIG_I2C) +static int wm8741_i2c_probe(struct i2c_client *i2c) +{ + struct wm8741_priv *wm8741; + int ret, i; + + wm8741 = devm_kzalloc(&i2c->dev, sizeof(struct wm8741_priv), + GFP_KERNEL); + if (wm8741 == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++) + wm8741->supplies[i].supply = wm8741_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8741->supplies), + wm8741->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8741->regmap = devm_regmap_init_i2c(i2c, &wm8741_regmap); + if (IS_ERR(wm8741->regmap)) { + ret = PTR_ERR(wm8741->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + ret = wm8741_set_pdata(&i2c->dev, wm8741); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, wm8741); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8741, &wm8741_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8741_i2c_id[] = { + { "wm8741", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id); + +static struct i2c_driver wm8741_i2c_driver = { + .driver = { + .name = "wm8741", + .of_match_table = wm8741_of_match, + }, + .probe = wm8741_i2c_probe, + .id_table = wm8741_i2c_id, +}; +#endif + +#if defined(CONFIG_SPI_MASTER) +static int wm8741_spi_probe(struct spi_device *spi) +{ + struct wm8741_priv *wm8741; + int ret, i; + + wm8741 = devm_kzalloc(&spi->dev, sizeof(struct wm8741_priv), + GFP_KERNEL); + if (wm8741 == NULL) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++) + wm8741->supplies[i].supply = wm8741_supply_names[i]; + + ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8741->supplies), + wm8741->supplies); + if (ret != 0) { + dev_err(&spi->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8741->regmap = devm_regmap_init_spi(spi, &wm8741_regmap); + if (IS_ERR(wm8741->regmap)) { + ret = PTR_ERR(wm8741->regmap); + dev_err(&spi->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + ret = wm8741_set_pdata(&spi->dev, wm8741); + if (ret != 0) { + dev_err(&spi->dev, "Failed to set pdata: %d\n", ret); + return ret; + } + + spi_set_drvdata(spi, wm8741); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8741, &wm8741_dai, 1); + return ret; +} + +static struct spi_driver wm8741_spi_driver = { + .driver = { + .name = "wm8741", + .of_match_table = wm8741_of_match, + }, + .probe = wm8741_spi_probe, +}; +#endif /* CONFIG_SPI_MASTER */ + +static int __init wm8741_modinit(void) +{ + int ret = 0; + +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8741_i2c_driver); + if (ret != 0) + pr_err("Failed to register WM8741 I2C driver: %d\n", ret); +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8741_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8741 SPI driver: %d\n", + ret); + } +#endif + + return ret; +} +module_init(wm8741_modinit); + +static void __exit wm8741_exit(void) +{ +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8741_spi_driver); +#endif +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8741_i2c_driver); +#endif +} +module_exit(wm8741_exit); + +MODULE_DESCRIPTION("ASoC WM8741 driver"); +MODULE_AUTHOR("Ian Lartey "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8741.h b/sound/soc/codecs/wm8741.h new file mode 100644 index 0000000000..8158432f01 --- /dev/null +++ b/sound/soc/codecs/wm8741.h @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8741.h -- WM8423 ASoC driver + * + * Copyright 2010 Wolfson Microelectronics, plc + * + * Author: Ian Lartey + * + * Based on wm8753.h + */ + +#ifndef _WM8741_H +#define _WM8741_H + +/* + * Register values. + */ +#define WM8741_DACLLSB_ATTENUATION 0x00 +#define WM8741_DACLMSB_ATTENUATION 0x01 +#define WM8741_DACRLSB_ATTENUATION 0x02 +#define WM8741_DACRMSB_ATTENUATION 0x03 +#define WM8741_VOLUME_CONTROL 0x04 +#define WM8741_FORMAT_CONTROL 0x05 +#define WM8741_FILTER_CONTROL 0x06 +#define WM8741_MODE_CONTROL_1 0x07 +#define WM8741_MODE_CONTROL_2 0x08 +#define WM8741_RESET 0x09 +#define WM8741_ADDITIONAL_CONTROL_1 0x20 + +#define WM8741_REGISTER_COUNT 11 +#define WM8741_MAX_REGISTER 0x20 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - DACLLSB_ATTENUATION + */ +#define WM8741_UPDATELL 0x0020 /* UPDATELL */ +#define WM8741_UPDATELL_MASK 0x0020 /* UPDATELL */ +#define WM8741_UPDATELL_SHIFT 5 /* UPDATELL */ +#define WM8741_UPDATELL_WIDTH 1 /* UPDATELL */ +#define WM8741_LAT_4_0_MASK 0x001F /* LAT[4:0] - [4:0] */ +#define WM8741_LAT_4_0_SHIFT 0 /* LAT[4:0] - [4:0] */ +#define WM8741_LAT_4_0_WIDTH 5 /* LAT[4:0] - [4:0] */ + +/* + * R1 (0x01) - DACLMSB_ATTENUATION + */ +#define WM8741_UPDATELM 0x0020 /* UPDATELM */ +#define WM8741_UPDATELM_MASK 0x0020 /* UPDATELM */ +#define WM8741_UPDATELM_SHIFT 5 /* UPDATELM */ +#define WM8741_UPDATELM_WIDTH 1 /* UPDATELM */ +#define WM8741_LAT_9_5_0_MASK 0x001F /* LAT[9:5] - [4:0] */ +#define WM8741_LAT_9_5_0_SHIFT 0 /* LAT[9:5] - [4:0] */ +#define WM8741_LAT_9_5_0_WIDTH 5 /* LAT[9:5] - [4:0] */ + +/* + * R2 (0x02) - DACRLSB_ATTENUATION + */ +#define WM8741_UPDATERL 0x0020 /* UPDATERL */ +#define WM8741_UPDATERL_MASK 0x0020 /* UPDATERL */ +#define WM8741_UPDATERL_SHIFT 5 /* UPDATERL */ +#define WM8741_UPDATERL_WIDTH 1 /* UPDATERL */ +#define WM8741_RAT_4_0_MASK 0x001F /* RAT[4:0] - [4:0] */ +#define WM8741_RAT_4_0_SHIFT 0 /* RAT[4:0] - [4:0] */ +#define WM8741_RAT_4_0_WIDTH 5 /* RAT[4:0] - [4:0] */ + +/* + * R3 (0x03) - DACRMSB_ATTENUATION + */ +#define WM8741_UPDATERM 0x0020 /* UPDATERM */ +#define WM8741_UPDATERM_MASK 0x0020 /* UPDATERM */ +#define WM8741_UPDATERM_SHIFT 5 /* UPDATERM */ +#define WM8741_UPDATERM_WIDTH 1 /* UPDATERM */ +#define WM8741_RAT_9_5_0_MASK 0x001F /* RAT[9:5] - [4:0] */ +#define WM8741_RAT_9_5_0_SHIFT 0 /* RAT[9:5] - [4:0] */ +#define WM8741_RAT_9_5_0_WIDTH 5 /* RAT[9:5] - [4:0] */ + +/* + * R4 (0x04) - VOLUME_CONTROL + */ +#define WM8741_AMUTE 0x0080 /* AMUTE */ +#define WM8741_AMUTE_MASK 0x0080 /* AMUTE */ +#define WM8741_AMUTE_SHIFT 7 /* AMUTE */ +#define WM8741_AMUTE_WIDTH 1 /* AMUTE */ +#define WM8741_ZFLAG_MASK 0x0060 /* ZFLAG - [6:5] */ +#define WM8741_ZFLAG_SHIFT 5 /* ZFLAG - [6:5] */ +#define WM8741_ZFLAG_WIDTH 2 /* ZFLAG - [6:5] */ +#define WM8741_IZD 0x0010 /* IZD */ +#define WM8741_IZD_MASK 0x0010 /* IZD */ +#define WM8741_IZD_SHIFT 4 /* IZD */ +#define WM8741_IZD_WIDTH 1 /* IZD */ +#define WM8741_SOFT 0x0008 /* SOFT MUTE */ +#define WM8741_SOFT_MASK 0x0008 /* SOFT MUTE */ +#define WM8741_SOFT_SHIFT 3 /* SOFT MUTE */ +#define WM8741_SOFT_WIDTH 1 /* SOFT MUTE */ +#define WM8741_ATC 0x0004 /* ATC */ +#define WM8741_ATC_MASK 0x0004 /* ATC */ +#define WM8741_ATC_SHIFT 2 /* ATC */ +#define WM8741_ATC_WIDTH 1 /* ATC */ +#define WM8741_ATT2DB 0x0002 /* ATT2DB */ +#define WM8741_ATT2DB_MASK 0x0002 /* ATT2DB */ +#define WM8741_ATT2DB_SHIFT 1 /* ATT2DB */ +#define WM8741_ATT2DB_WIDTH 1 /* ATT2DB */ +#define WM8741_VOL_RAMP 0x0001 /* VOL_RAMP */ +#define WM8741_VOL_RAMP_MASK 0x0001 /* VOL_RAMP */ +#define WM8741_VOL_RAMP_SHIFT 0 /* VOL_RAMP */ +#define WM8741_VOL_RAMP_WIDTH 1 /* VOL_RAMP */ + +/* + * R5 (0x05) - FORMAT_CONTROL + */ +#define WM8741_PWDN 0x0080 /* PWDN */ +#define WM8741_PWDN_MASK 0x0080 /* PWDN */ +#define WM8741_PWDN_SHIFT 7 /* PWDN */ +#define WM8741_PWDN_WIDTH 1 /* PWDN */ +#define WM8741_REV 0x0040 /* REV */ +#define WM8741_REV_MASK 0x0040 /* REV */ +#define WM8741_REV_SHIFT 6 /* REV */ +#define WM8741_REV_WIDTH 1 /* REV */ +#define WM8741_BCP 0x0020 /* BCP */ +#define WM8741_BCP_MASK 0x0020 /* BCP */ +#define WM8741_BCP_SHIFT 5 /* BCP */ +#define WM8741_BCP_WIDTH 1 /* BCP */ +#define WM8741_LRP 0x0010 /* LRP */ +#define WM8741_LRP_MASK 0x0010 /* LRP */ +#define WM8741_LRP_SHIFT 4 /* LRP */ +#define WM8741_LRP_WIDTH 1 /* LRP */ +#define WM8741_FMT_MASK 0x000C /* FMT - [3:2] */ +#define WM8741_FMT_SHIFT 2 /* FMT - [3:2] */ +#define WM8741_FMT_WIDTH 2 /* FMT - [3:2] */ +#define WM8741_IWL_MASK 0x0003 /* IWL - [1:0] */ +#define WM8741_IWL_SHIFT 0 /* IWL - [1:0] */ +#define WM8741_IWL_WIDTH 2 /* IWL - [1:0] */ + +/* + * R6 (0x06) - FILTER_CONTROL + */ +#define WM8741_ZFLAG_HI 0x0080 /* ZFLAG_HI */ +#define WM8741_ZFLAG_HI_MASK 0x0080 /* ZFLAG_HI */ +#define WM8741_ZFLAG_HI_SHIFT 7 /* ZFLAG_HI */ +#define WM8741_ZFLAG_HI_WIDTH 1 /* ZFLAG_HI */ +#define WM8741_DEEMPH_MASK 0x0060 /* DEEMPH - [6:5] */ +#define WM8741_DEEMPH_SHIFT 5 /* DEEMPH - [6:5] */ +#define WM8741_DEEMPH_WIDTH 2 /* DEEMPH - [6:5] */ +#define WM8741_DSDFILT_MASK 0x0018 /* DSDFILT - [4:3] */ +#define WM8741_DSDFILT_SHIFT 3 /* DSDFILT - [4:3] */ +#define WM8741_DSDFILT_WIDTH 2 /* DSDFILT - [4:3] */ +#define WM8741_FIRSEL_MASK 0x0007 /* FIRSEL - [2:0] */ +#define WM8741_FIRSEL_SHIFT 0 /* FIRSEL - [2:0] */ +#define WM8741_FIRSEL_WIDTH 3 /* FIRSEL - [2:0] */ + +/* + * R7 (0x07) - MODE_CONTROL_1 + */ +#define WM8741_MODE8X 0x0080 /* MODE8X */ +#define WM8741_MODE8X_MASK 0x0080 /* MODE8X */ +#define WM8741_MODE8X_SHIFT 7 /* MODE8X */ +#define WM8741_MODE8X_WIDTH 1 /* MODE8X */ +#define WM8741_OSR_MASK 0x0060 /* OSR - [6:5] */ +#define WM8741_OSR_SHIFT 5 /* OSR - [6:5] */ +#define WM8741_OSR_WIDTH 2 /* OSR - [6:5] */ +#define WM8741_SR_MASK 0x001C /* SR - [4:2] */ +#define WM8741_SR_SHIFT 2 /* SR - [4:2] */ +#define WM8741_SR_WIDTH 3 /* SR - [4:2] */ +#define WM8741_MODESEL_MASK 0x0003 /* MODESEL - [1:0] */ +#define WM8741_MODESEL_SHIFT 0 /* MODESEL - [1:0] */ +#define WM8741_MODESEL_WIDTH 2 /* MODESEL - [1:0] */ + +/* + * R8 (0x08) - MODE_CONTROL_2 + */ +#define WM8741_DSD_GAIN 0x0040 /* DSD_GAIN */ +#define WM8741_DSD_GAIN_MASK 0x0040 /* DSD_GAIN */ +#define WM8741_DSD_GAIN_SHIFT 6 /* DSD_GAIN */ +#define WM8741_DSD_GAIN_WIDTH 1 /* DSD_GAIN */ +#define WM8741_SDOUT 0x0020 /* SDOUT */ +#define WM8741_SDOUT_MASK 0x0020 /* SDOUT */ +#define WM8741_SDOUT_SHIFT 5 /* SDOUT */ +#define WM8741_SDOUT_WIDTH 1 /* SDOUT */ +#define WM8741_DOUT 0x0010 /* DOUT */ +#define WM8741_DOUT_MASK 0x0010 /* DOUT */ +#define WM8741_DOUT_SHIFT 4 /* DOUT */ +#define WM8741_DOUT_WIDTH 1 /* DOUT */ +#define WM8741_DIFF_MASK 0x000C /* DIFF - [3:2] */ +#define WM8741_DIFF_SHIFT 2 /* DIFF - [3:2] */ +#define WM8741_DIFF_WIDTH 2 /* DIFF - [3:2] */ +#define WM8741_DITHER_MASK 0x0003 /* DITHER - [1:0] */ +#define WM8741_DITHER_SHIFT 0 /* DITHER - [1:0] */ +#define WM8741_DITHER_WIDTH 2 /* DITHER - [1:0] */ + +/* DIFF field values */ +#define WM8741_DIFF_MODE_STEREO 0 /* stereo normal */ +#define WM8741_DIFF_MODE_STEREO_REVERSED 2 /* stereo reversed */ +#define WM8741_DIFF_MODE_MONO_LEFT 1 /* mono left */ +#define WM8741_DIFF_MODE_MONO_RIGHT 3 /* mono right */ + +/* + * R32 (0x20) - ADDITONAL_CONTROL_1 + */ +#define WM8741_DSD_LEVEL 0x0002 /* DSD_LEVEL */ +#define WM8741_DSD_LEVEL_MASK 0x0002 /* DSD_LEVEL */ +#define WM8741_DSD_LEVEL_SHIFT 1 /* DSD_LEVEL */ +#define WM8741_DSD_LEVEL_WIDTH 1 /* DSD_LEVEL */ +#define WM8741_DSD_NO_NOTCH 0x0001 /* DSD_NO_NOTCH */ +#define WM8741_DSD_NO_NOTCH_MASK 0x0001 /* DSD_NO_NOTCH */ +#define WM8741_DSD_NO_NOTCH_SHIFT 0 /* DSD_NO_NOTCH */ +#define WM8741_DSD_NO_NOTCH_WIDTH 1 /* DSD_NO_NOTCH */ + +#define WM8741_SYSCLK 0 + +struct wm8741_platform_data { + u32 diff_mode; /* Differential Output Mode */ +}; + +#endif diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c new file mode 100644 index 0000000000..2d2feaf95e --- /dev/null +++ b/sound/soc/codecs/wm8750.c @@ -0,0 +1,855 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8750.c -- WM8750 ALSA SoC audio driver + * + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * Based on WM8753.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8750.h" + +/* + * wm8750 register cache + * We can't read the WM8750 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const struct reg_default wm8750_reg_defaults[] = { + { 0, 0x0097 }, + { 1, 0x0097 }, + { 2, 0x0079 }, + { 3, 0x0079 }, + { 4, 0x0000 }, + { 5, 0x0008 }, + { 6, 0x0000 }, + { 7, 0x000a }, + { 8, 0x0000 }, + { 9, 0x0000 }, + { 10, 0x00ff }, + { 11, 0x00ff }, + { 12, 0x000f }, + { 13, 0x000f }, + { 14, 0x0000 }, + { 15, 0x0000 }, + { 16, 0x0000 }, + { 17, 0x007b }, + { 18, 0x0000 }, + { 19, 0x0032 }, + { 20, 0x0000 }, + { 21, 0x00c3 }, + { 22, 0x00c3 }, + { 23, 0x00c0 }, + { 24, 0x0000 }, + { 25, 0x0000 }, + { 26, 0x0000 }, + { 27, 0x0000 }, + { 28, 0x0000 }, + { 29, 0x0000 }, + { 30, 0x0000 }, + { 31, 0x0000 }, + { 32, 0x0000 }, + { 33, 0x0000 }, + { 34, 0x0050 }, + { 35, 0x0050 }, + { 36, 0x0050 }, + { 37, 0x0050 }, + { 38, 0x0050 }, + { 39, 0x0050 }, + { 40, 0x0079 }, + { 41, 0x0079 }, + { 42, 0x0079 }, +}; + +/* codec private data */ +struct wm8750_priv { + unsigned int sysclk; +}; + +#define wm8750_reset(c) snd_soc_component_write(c, WM8750_RESET, 0) + +/* + * WM8750 Controls + */ +static const char *wm8750_bass[] = {"Linear Control", "Adaptive Boost"}; +static const char *wm8750_bass_filter[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" }; +static const char *wm8750_treble[] = {"8kHz", "4kHz"}; +static const char *wm8750_3d_lc[] = {"200Hz", "500Hz"}; +static const char *wm8750_3d_uc[] = {"2.2kHz", "1.5kHz"}; +static const char *wm8750_3d_func[] = {"Capture", "Playback"}; +static const char *wm8750_alc_func[] = {"Off", "Right", "Left", "Stereo"}; +static const char *wm8750_ng_type[] = {"Constant PGA Gain", + "Mute ADC Output"}; +static const char *wm8750_line_mux[] = {"Line 1", "Line 2", "Line 3", "PGA", + "Differential"}; +static const char *wm8750_pga_sel[] = {"Line 1", "Line 2", "Line 3", + "Differential"}; +static const char *wm8750_out3[] = {"VREF", "ROUT1 + Vol", "MonoOut", + "ROUT1"}; +static const char *wm8750_diff_sel[] = {"Line 1", "Line 2"}; +static const char *wm8750_adcpol[] = {"Normal", "L Invert", "R Invert", + "L + R Invert"}; +static const char *wm8750_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; +static const char *wm8750_mono_mux[] = {"Stereo", "Mono (Left)", + "Mono (Right)", "Digital Mono"}; + +static const struct soc_enum wm8750_enum[] = { +SOC_ENUM_SINGLE(WM8750_BASS, 7, 2, wm8750_bass), +SOC_ENUM_SINGLE(WM8750_BASS, 6, 2, wm8750_bass_filter), +SOC_ENUM_SINGLE(WM8750_TREBLE, 6, 2, wm8750_treble), +SOC_ENUM_SINGLE(WM8750_3D, 5, 2, wm8750_3d_lc), +SOC_ENUM_SINGLE(WM8750_3D, 6, 2, wm8750_3d_uc), +SOC_ENUM_SINGLE(WM8750_3D, 7, 2, wm8750_3d_func), +SOC_ENUM_SINGLE(WM8750_ALC1, 7, 4, wm8750_alc_func), +SOC_ENUM_SINGLE(WM8750_NGATE, 1, 2, wm8750_ng_type), +SOC_ENUM_SINGLE(WM8750_LOUTM1, 0, 5, wm8750_line_mux), +SOC_ENUM_SINGLE(WM8750_ROUTM1, 0, 5, wm8750_line_mux), +SOC_ENUM_SINGLE(WM8750_LADCIN, 6, 4, wm8750_pga_sel), /* 10 */ +SOC_ENUM_SINGLE(WM8750_RADCIN, 6, 4, wm8750_pga_sel), +SOC_ENUM_SINGLE(WM8750_ADCTL2, 7, 4, wm8750_out3), +SOC_ENUM_SINGLE(WM8750_ADCIN, 8, 2, wm8750_diff_sel), +SOC_ENUM_SINGLE(WM8750_ADCDAC, 5, 4, wm8750_adcpol), +SOC_ENUM_SINGLE(WM8750_ADCDAC, 1, 4, wm8750_deemph), +SOC_ENUM_SINGLE(WM8750_ADCIN, 6, 4, wm8750_mono_mux), /* 16 */ + +}; + +static const struct snd_kcontrol_new wm8750_snd_controls[] = { + +SOC_DOUBLE_R("Capture Volume", WM8750_LINVOL, WM8750_RINVOL, 0, 63, 0), +SOC_DOUBLE_R("Capture ZC Switch", WM8750_LINVOL, WM8750_RINVOL, 6, 1, 0), +SOC_DOUBLE_R("Capture Switch", WM8750_LINVOL, WM8750_RINVOL, 7, 1, 1), + +SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8750_LOUT1V, + WM8750_ROUT1V, 7, 1, 0), +SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8750_LOUT2V, + WM8750_ROUT2V, 7, 1, 0), + +SOC_ENUM("Playback De-emphasis", wm8750_enum[15]), + +SOC_ENUM("Capture Polarity", wm8750_enum[14]), +SOC_SINGLE("Playback 6dB Attenuate", WM8750_ADCDAC, 7, 1, 0), +SOC_SINGLE("Capture 6dB Attenuate", WM8750_ADCDAC, 8, 1, 0), + +SOC_DOUBLE_R("PCM Volume", WM8750_LDAC, WM8750_RDAC, 0, 255, 0), + +SOC_ENUM("Bass Boost", wm8750_enum[0]), +SOC_ENUM("Bass Filter", wm8750_enum[1]), +SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1), + +SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 1), +SOC_ENUM("Treble Cut-off", wm8750_enum[2]), + +SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0), +SOC_SINGLE("3D Volume", WM8750_3D, 1, 15, 0), +SOC_ENUM("3D Lower Cut-off", wm8750_enum[3]), +SOC_ENUM("3D Upper Cut-off", wm8750_enum[4]), +SOC_ENUM("3D Mode", wm8750_enum[5]), + +SOC_SINGLE("ALC Capture Target Volume", WM8750_ALC1, 0, 7, 0), +SOC_SINGLE("ALC Capture Max Volume", WM8750_ALC1, 4, 7, 0), +SOC_ENUM("ALC Capture Function", wm8750_enum[6]), +SOC_SINGLE("ALC Capture ZC Switch", WM8750_ALC2, 7, 1, 0), +SOC_SINGLE("ALC Capture Hold Time", WM8750_ALC2, 0, 15, 0), +SOC_SINGLE("ALC Capture Decay Time", WM8750_ALC3, 4, 15, 0), +SOC_SINGLE("ALC Capture Attack Time", WM8750_ALC3, 0, 15, 0), +SOC_SINGLE("ALC Capture NG Threshold", WM8750_NGATE, 3, 31, 0), +SOC_ENUM("ALC Capture NG Type", wm8750_enum[4]), +SOC_SINGLE("ALC Capture NG Switch", WM8750_NGATE, 0, 1, 0), + +SOC_SINGLE("Left ADC Capture Volume", WM8750_LADC, 0, 255, 0), +SOC_SINGLE("Right ADC Capture Volume", WM8750_RADC, 0, 255, 0), + +SOC_SINGLE("ZC Timeout Switch", WM8750_ADCTL1, 0, 1, 0), +SOC_SINGLE("Playback Invert Switch", WM8750_ADCTL1, 1, 1, 0), + +SOC_SINGLE("Right Speaker Playback Invert Switch", WM8750_ADCTL2, 4, 1, 0), + +/* Unimplemented */ +/* ADCDAC Bit 0 - ADCHPD */ +/* ADCDAC Bit 4 - HPOR */ +/* ADCTL1 Bit 2,3 - DATSEL */ +/* ADCTL1 Bit 4,5 - DMONOMIX */ +/* ADCTL1 Bit 6,7 - VSEL */ +/* ADCTL2 Bit 2 - LRCM */ +/* ADCTL2 Bit 3 - TRI */ +/* ADCTL3 Bit 5 - HPFLREN */ +/* ADCTL3 Bit 6 - VROI */ +/* ADCTL3 Bit 7,8 - ADCLRM */ +/* ADCIN Bit 4 - LDCM */ +/* ADCIN Bit 5 - RDCM */ + +SOC_DOUBLE_R("Mic Boost", WM8750_LADCIN, WM8750_RADCIN, 4, 3, 0), + +SOC_DOUBLE_R("Bypass Left Playback Volume", WM8750_LOUTM1, + WM8750_LOUTM2, 4, 7, 1), +SOC_DOUBLE_R("Bypass Right Playback Volume", WM8750_ROUTM1, + WM8750_ROUTM2, 4, 7, 1), +SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8750_MOUTM1, + WM8750_MOUTM2, 4, 7, 1), + +SOC_SINGLE("Mono Playback ZC Switch", WM8750_MOUTV, 7, 1, 0), + +SOC_DOUBLE_R("Headphone Playback Volume", WM8750_LOUT1V, WM8750_ROUT1V, + 0, 127, 0), +SOC_DOUBLE_R("Speaker Playback Volume", WM8750_LOUT2V, WM8750_ROUT2V, + 0, 127, 0), + +SOC_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0), + +}; + +/* + * DAPM Controls + */ + +/* Left Mixer */ +static const struct snd_kcontrol_new wm8750_left_mixer_controls[] = { +SOC_DAPM_SINGLE("Playback Switch", WM8750_LOUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_LOUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8750_LOUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_LOUTM2, 7, 1, 0), +}; + +/* Right Mixer */ +static const struct snd_kcontrol_new wm8750_right_mixer_controls[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8750_ROUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_ROUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Playback Switch", WM8750_ROUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_ROUTM2, 7, 1, 0), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new wm8750_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8750_MOUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8750_MOUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8750_MOUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8750_MOUTM2, 7, 1, 0), +}; + +/* Left Line Mux */ +static const struct snd_kcontrol_new wm8750_left_line_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[8]); + +/* Right Line Mux */ +static const struct snd_kcontrol_new wm8750_right_line_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[9]); + +/* Left PGA Mux */ +static const struct snd_kcontrol_new wm8750_left_pga_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[10]); + +/* Right PGA Mux */ +static const struct snd_kcontrol_new wm8750_right_pga_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[11]); + +/* Out 3 Mux */ +static const struct snd_kcontrol_new wm8750_out3_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[12]); + +/* Differential Mux */ +static const struct snd_kcontrol_new wm8750_diffmux_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[13]); + +/* Mono ADC Mux */ +static const struct snd_kcontrol_new wm8750_monomux_controls = +SOC_DAPM_ENUM("Route", wm8750_enum[16]); + +static const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + &wm8750_left_mixer_controls[0], + ARRAY_SIZE(wm8750_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + &wm8750_right_mixer_controls[0], + ARRAY_SIZE(wm8750_right_mixer_controls)), + SND_SOC_DAPM_MIXER("Mono Mixer", WM8750_PWR2, 2, 0, + &wm8750_mono_mixer_controls[0], + ARRAY_SIZE(wm8750_mono_mixer_controls)), + + SND_SOC_DAPM_PGA("Right Out 2", WM8750_PWR2, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 2", WM8750_PWR2, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 1", WM8750_PWR2, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 1", WM8750_PWR2, 6, 0, NULL, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8750_PWR2, 7, 0), + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8750_PWR2, 8, 0), + + SND_SOC_DAPM_MICBIAS("Mic Bias", WM8750_PWR1, 1, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8750_PWR1, 2, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8750_PWR1, 3, 0), + + SND_SOC_DAPM_MUX("Left PGA Mux", WM8750_PWR1, 5, 0, + &wm8750_left_pga_controls), + SND_SOC_DAPM_MUX("Right PGA Mux", WM8750_PWR1, 4, 0, + &wm8750_right_pga_controls), + SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, + &wm8750_left_line_controls), + SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, + &wm8750_right_line_controls), + + SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8750_out3_controls), + SND_SOC_DAPM_PGA("Out 3", WM8750_PWR2, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono Out 1", WM8750_PWR2, 2, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &wm8750_diffmux_controls), + SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, + &wm8750_monomux_controls), + SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, + &wm8750_monomux_controls), + + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("MONO1"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_VMID("VREF"), + + SND_SOC_DAPM_INPUT("LINPUT1"), + SND_SOC_DAPM_INPUT("LINPUT2"), + SND_SOC_DAPM_INPUT("LINPUT3"), + SND_SOC_DAPM_INPUT("RINPUT1"), + SND_SOC_DAPM_INPUT("RINPUT2"), + SND_SOC_DAPM_INPUT("RINPUT3"), +}; + +static const struct snd_soc_dapm_route wm8750_dapm_routes[] = { + /* left mixer */ + {"Left Mixer", "Playback Switch", "Left DAC"}, + {"Left Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Left Mixer", "Right Playback Switch", "Right DAC"}, + {"Left Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* right mixer */ + {"Right Mixer", "Left Playback Switch", "Left DAC"}, + {"Right Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Right Mixer", "Playback Switch", "Right DAC"}, + {"Right Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* left out 1 */ + {"Left Out 1", NULL, "Left Mixer"}, + {"LOUT1", NULL, "Left Out 1"}, + + /* left out 2 */ + {"Left Out 2", NULL, "Left Mixer"}, + {"LOUT2", NULL, "Left Out 2"}, + + /* right out 1 */ + {"Right Out 1", NULL, "Right Mixer"}, + {"ROUT1", NULL, "Right Out 1"}, + + /* right out 2 */ + {"Right Out 2", NULL, "Right Mixer"}, + {"ROUT2", NULL, "Right Out 2"}, + + /* mono mixer */ + {"Mono Mixer", "Left Playback Switch", "Left DAC"}, + {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Mono Mixer", "Right Playback Switch", "Right DAC"}, + {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* mono out */ + {"Mono Out 1", NULL, "Mono Mixer"}, + {"MONO1", NULL, "Mono Out 1"}, + + /* out 3 */ + {"Out3 Mux", "VREF", "VREF"}, + {"Out3 Mux", "ROUT1 + Vol", "ROUT1"}, + {"Out3 Mux", "ROUT1", "Right Mixer"}, + {"Out3 Mux", "MonoOut", "MONO1"}, + {"Out 3", NULL, "Out3 Mux"}, + {"OUT3", NULL, "Out 3"}, + + /* Left Line Mux */ + {"Left Line Mux", "Line 1", "LINPUT1"}, + {"Left Line Mux", "Line 2", "LINPUT2"}, + {"Left Line Mux", "Line 3", "LINPUT3"}, + {"Left Line Mux", "PGA", "Left PGA Mux"}, + {"Left Line Mux", "Differential", "Differential Mux"}, + + /* Right Line Mux */ + {"Right Line Mux", "Line 1", "RINPUT1"}, + {"Right Line Mux", "Line 2", "RINPUT2"}, + {"Right Line Mux", "Line 3", "RINPUT3"}, + {"Right Line Mux", "PGA", "Right PGA Mux"}, + {"Right Line Mux", "Differential", "Differential Mux"}, + + /* Left PGA Mux */ + {"Left PGA Mux", "Line 1", "LINPUT1"}, + {"Left PGA Mux", "Line 2", "LINPUT2"}, + {"Left PGA Mux", "Line 3", "LINPUT3"}, + {"Left PGA Mux", "Differential", "Differential Mux"}, + + /* Right PGA Mux */ + {"Right PGA Mux", "Line 1", "RINPUT1"}, + {"Right PGA Mux", "Line 2", "RINPUT2"}, + {"Right PGA Mux", "Line 3", "RINPUT3"}, + {"Right PGA Mux", "Differential", "Differential Mux"}, + + /* Differential Mux */ + {"Differential Mux", "Line 1", "LINPUT1"}, + {"Differential Mux", "Line 1", "RINPUT1"}, + {"Differential Mux", "Line 2", "LINPUT2"}, + {"Differential Mux", "Line 2", "RINPUT2"}, + + /* Left ADC Mux */ + {"Left ADC Mux", "Stereo", "Left PGA Mux"}, + {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"}, + {"Left ADC Mux", "Digital Mono", "Left PGA Mux"}, + + /* Right ADC Mux */ + {"Right ADC Mux", "Stereo", "Right PGA Mux"}, + {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"}, + {"Right ADC Mux", "Digital Mono", "Right PGA Mux"}, + + /* ADC */ + {"Left ADC", NULL, "Left ADC Mux"}, + {"Right ADC", NULL, "Right ADC Mux"}, +}; + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u8 sr:5; + u8 usb:1; +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 8k */ + {12288000, 8000, 1536, 0x6, 0x0}, + {11289600, 8000, 1408, 0x16, 0x0}, + {18432000, 8000, 2304, 0x7, 0x0}, + {16934400, 8000, 2112, 0x17, 0x0}, + {12000000, 8000, 1500, 0x6, 0x1}, + + /* 11.025k */ + {11289600, 11025, 1024, 0x18, 0x0}, + {16934400, 11025, 1536, 0x19, 0x0}, + {12000000, 11025, 1088, 0x19, 0x1}, + + /* 16k */ + {12288000, 16000, 768, 0xa, 0x0}, + {18432000, 16000, 1152, 0xb, 0x0}, + {12000000, 16000, 750, 0xa, 0x1}, + + /* 22.05k */ + {11289600, 22050, 512, 0x1a, 0x0}, + {16934400, 22050, 768, 0x1b, 0x0}, + {12000000, 22050, 544, 0x1b, 0x1}, + + /* 32k */ + {12288000, 32000, 384, 0xc, 0x0}, + {18432000, 32000, 576, 0xd, 0x0}, + {12000000, 32000, 375, 0xa, 0x1}, + + /* 44.1k */ + {11289600, 44100, 256, 0x10, 0x0}, + {16934400, 44100, 384, 0x11, 0x0}, + {12000000, 44100, 272, 0x11, 0x1}, + + /* 48k */ + {12288000, 48000, 256, 0x0, 0x0}, + {18432000, 48000, 384, 0x1, 0x0}, + {12000000, 48000, 250, 0x0, 0x1}, + + /* 88.2k */ + {11289600, 88200, 128, 0x1e, 0x0}, + {16934400, 88200, 192, 0x1f, 0x0}, + {12000000, 88200, 136, 0x1f, 0x1}, + + /* 96k */ + {12288000, 96000, 128, 0xe, 0x0}, + {18432000, 96000, 192, 0xf, 0x0}, + {12000000, 96000, 125, 0xe, 0x1}, +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + + printk(KERN_ERR "wm8750: could not get coeff for mclk %d @ rate %d\n", + mclk, rate); + return -EINVAL; +} + +static int wm8750_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8750_priv *wm8750 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case 11289600: + case 12000000: + case 12288000: + case 16934400: + case 18432000: + wm8750->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0040; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8750_IFACE, iface); + return 0; +} + +static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8750_priv *wm8750 = snd_soc_component_get_drvdata(component); + u16 iface = snd_soc_component_read(component, WM8750_IFACE) & 0x1f3; + u16 srate = snd_soc_component_read(component, WM8750_SRATE) & 0x1c0; + int coeff = get_coeff(wm8750->sysclk, params_rate(params)); + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x0004; + break; + case 24: + iface |= 0x0008; + break; + case 32: + iface |= 0x000c; + break; + } + + /* set iface & srate */ + snd_soc_component_write(component, WM8750_IFACE, iface); + if (coeff >= 0) + snd_soc_component_write(component, WM8750_SRATE, srate | + (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb); + + return 0; +} + +static int wm8750_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, WM8750_ADCDAC) & 0xfff7; + + if (mute) + snd_soc_component_write(component, WM8750_ADCDAC, mute_reg | 0x8); + else + snd_soc_component_write(component, WM8750_ADCDAC, mute_reg); + return 0; +} + +static int wm8750_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + u16 pwr_reg = snd_soc_component_read(component, WM8750_PWR1) & 0xfe3e; + + switch (level) { + case SND_SOC_BIAS_ON: + /* set vmid to 50k and unmute dac */ + snd_soc_component_write(component, WM8750_PWR1, pwr_reg | 0x00c0); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + snd_soc_component_cache_sync(component); + + /* Set VMID to 5k */ + snd_soc_component_write(component, WM8750_PWR1, pwr_reg | 0x01c1); + + /* ...and ramp */ + msleep(1000); + } + + /* mute dac and set vmid to 500k, enable VREF */ + snd_soc_component_write(component, WM8750_PWR1, pwr_reg | 0x0141); + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, WM8750_PWR1, 0x0001); + break; + } + return 0; +} + +#define WM8750_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8750_dai_ops = { + .hw_params = wm8750_pcm_hw_params, + .mute_stream = wm8750_mute, + .set_fmt = wm8750_set_dai_fmt, + .set_sysclk = wm8750_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8750_dai = { + .name = "wm8750-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8750_RATES, + .formats = WM8750_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8750_RATES, + .formats = WM8750_FORMATS,}, + .ops = &wm8750_dai_ops, +}; + +static int wm8750_probe(struct snd_soc_component *component) +{ + int ret; + + ret = wm8750_reset(component); + if (ret < 0) { + printk(KERN_ERR "wm8750: failed to reset: %d\n", ret); + return ret; + } + + /* set the update bits */ + snd_soc_component_update_bits(component, WM8750_LDAC, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8750_RDAC, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8750_LOUT1V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8750_ROUT1V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8750_LOUT2V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8750_ROUT2V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8750_LINVOL, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8750_RINVOL, 0x0100, 0x0100); + + return ret; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8750 = { + .probe = wm8750_probe, + .set_bias_level = wm8750_set_bias_level, + .controls = wm8750_snd_controls, + .num_controls = ARRAY_SIZE(wm8750_snd_controls), + .dapm_widgets = wm8750_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets), + .dapm_routes = wm8750_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8750_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8750_of_match[] = { + { .compatible = "wlf,wm8750", }, + { .compatible = "wlf,wm8987", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8750_of_match); + +static const struct regmap_config wm8750_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8750_MOUTV, + + .reg_defaults = wm8750_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8750_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8750_spi_probe(struct spi_device *spi) +{ + struct wm8750_priv *wm8750; + struct regmap *regmap; + int ret; + + wm8750 = devm_kzalloc(&spi->dev, sizeof(struct wm8750_priv), + GFP_KERNEL); + if (wm8750 == NULL) + return -ENOMEM; + + regmap = devm_regmap_init_spi(spi, &wm8750_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + spi_set_drvdata(spi, wm8750); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8750, &wm8750_dai, 1); + return ret; +} + +static const struct spi_device_id wm8750_spi_ids[] = { + { "wm8750", 0 }, + { "wm8987", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, wm8750_spi_ids); + +static struct spi_driver wm8750_spi_driver = { + .driver = { + .name = "wm8750", + .of_match_table = wm8750_of_match, + }, + .id_table = wm8750_spi_ids, + .probe = wm8750_spi_probe, +}; +#endif /* CONFIG_SPI_MASTER */ + +#if IS_ENABLED(CONFIG_I2C) +static int wm8750_i2c_probe(struct i2c_client *i2c) +{ + struct wm8750_priv *wm8750; + struct regmap *regmap; + int ret; + + wm8750 = devm_kzalloc(&i2c->dev, sizeof(struct wm8750_priv), + GFP_KERNEL); + if (wm8750 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm8750); + + regmap = devm_regmap_init_i2c(i2c, &wm8750_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8750, &wm8750_dai, 1); + return ret; +} + +static const struct i2c_device_id wm8750_i2c_id[] = { + { "wm8750", 0 }, + { "wm8987", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id); + +static struct i2c_driver wm8750_i2c_driver = { + .driver = { + .name = "wm8750", + .of_match_table = wm8750_of_match, + }, + .probe = wm8750_i2c_probe, + .id_table = wm8750_i2c_id, +}; +#endif + +static int __init wm8750_modinit(void) +{ + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8750_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8750 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8750_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8750 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8750_modinit); + +static void __exit wm8750_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8750_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8750_spi_driver); +#endif +} +module_exit(wm8750_exit); + +MODULE_DESCRIPTION("ASoC WM8750 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h new file mode 100644 index 0000000000..325f58aa73 --- /dev/null +++ b/sound/soc/codecs/wm8750.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * Based on WM8753.h + */ + +#ifndef _WM8750_H +#define _WM8750_H + +/* WM8750 register space */ + +#define WM8750_LINVOL 0x00 +#define WM8750_RINVOL 0x01 +#define WM8750_LOUT1V 0x02 +#define WM8750_ROUT1V 0x03 +#define WM8750_ADCDAC 0x05 +#define WM8750_IFACE 0x07 +#define WM8750_SRATE 0x08 +#define WM8750_LDAC 0x0a +#define WM8750_RDAC 0x0b +#define WM8750_BASS 0x0c +#define WM8750_TREBLE 0x0d +#define WM8750_RESET 0x0f +#define WM8750_3D 0x10 +#define WM8750_ALC1 0x11 +#define WM8750_ALC2 0x12 +#define WM8750_ALC3 0x13 +#define WM8750_NGATE 0x14 +#define WM8750_LADC 0x15 +#define WM8750_RADC 0x16 +#define WM8750_ADCTL1 0x17 +#define WM8750_ADCTL2 0x18 +#define WM8750_PWR1 0x19 +#define WM8750_PWR2 0x1a +#define WM8750_ADCTL3 0x1b +#define WM8750_ADCIN 0x1f +#define WM8750_LADCIN 0x20 +#define WM8750_RADCIN 0x21 +#define WM8750_LOUTM1 0x22 +#define WM8750_LOUTM2 0x23 +#define WM8750_ROUTM1 0x24 +#define WM8750_ROUTM2 0x25 +#define WM8750_MOUTM1 0x26 +#define WM8750_MOUTM2 0x27 +#define WM8750_LOUT2V 0x28 +#define WM8750_ROUT2V 0x29 +#define WM8750_MOUTV 0x2a + +#define WM8750_CACHE_REGNUM 0x2a + +#define WM8750_SYSCLK 0 + +#endif diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c new file mode 100644 index 0000000000..b5d8290c37 --- /dev/null +++ b/sound/soc/codecs/wm8753.c @@ -0,0 +1,1632 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * wm8753.c -- WM8753 ALSA Soc Audio driver + * + * Copyright 2003-11 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * + * Notes: + * The WM8753 is a low power, high quality stereo codec with integrated PCM + * codec designed for portable digital telephony applications. + * + * Dual DAI:- + * + * This driver support 2 DAI PCM's. This makes the default PCM available for + * HiFi audio (e.g. MP3, ogg) playback/capture and the other PCM available for + * voice. + * + * Please note that the voice PCM can be connected directly to a Bluetooth + * codec or GSM modem and thus cannot be read or written to, although it is + * available to be configured with snd_hw_params(), etc and kcontrols in the + * normal alsa manner. + * + * Fast DAI switching:- + * + * The driver can now fast switch between the DAI configurations via a + * an alsa kcontrol. This allows the PCM to remain open. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8753.h" + +static int caps_charge = 2000; +module_param(caps_charge, int, 0); +MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)"); + +static int wm8753_hifi_write_dai_fmt(struct snd_soc_component *component, + unsigned int fmt); +static int wm8753_voice_write_dai_fmt(struct snd_soc_component *component, + unsigned int fmt); + +/* + * wm8753 register cache + * We can't read the WM8753 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const struct reg_default wm8753_reg_defaults[] = { + { 0x00, 0x0000 }, + { 0x01, 0x0008 }, + { 0x02, 0x0000 }, + { 0x03, 0x000a }, + { 0x04, 0x000a }, + { 0x05, 0x0033 }, + { 0x06, 0x0000 }, + { 0x07, 0x0007 }, + { 0x08, 0x00ff }, + { 0x09, 0x00ff }, + { 0x0a, 0x000f }, + { 0x0b, 0x000f }, + { 0x0c, 0x007b }, + { 0x0d, 0x0000 }, + { 0x0e, 0x0032 }, + { 0x0f, 0x0000 }, + { 0x10, 0x00c3 }, + { 0x11, 0x00c3 }, + { 0x12, 0x00c0 }, + { 0x13, 0x0000 }, + { 0x14, 0x0000 }, + { 0x15, 0x0000 }, + { 0x16, 0x0000 }, + { 0x17, 0x0000 }, + { 0x18, 0x0000 }, + { 0x19, 0x0000 }, + { 0x1a, 0x0000 }, + { 0x1b, 0x0000 }, + { 0x1c, 0x0000 }, + { 0x1d, 0x0000 }, + { 0x1e, 0x0000 }, + { 0x1f, 0x0000 }, + { 0x20, 0x0055 }, + { 0x21, 0x0005 }, + { 0x22, 0x0050 }, + { 0x23, 0x0055 }, + { 0x24, 0x0050 }, + { 0x25, 0x0055 }, + { 0x26, 0x0050 }, + { 0x27, 0x0055 }, + { 0x28, 0x0079 }, + { 0x29, 0x0079 }, + { 0x2a, 0x0079 }, + { 0x2b, 0x0079 }, + { 0x2c, 0x0079 }, + { 0x2d, 0x0000 }, + { 0x2e, 0x0000 }, + { 0x2f, 0x0000 }, + { 0x30, 0x0000 }, + { 0x31, 0x0097 }, + { 0x32, 0x0097 }, + { 0x33, 0x0000 }, + { 0x34, 0x0004 }, + { 0x35, 0x0000 }, + { 0x36, 0x0083 }, + { 0x37, 0x0024 }, + { 0x38, 0x01ba }, + { 0x39, 0x0000 }, + { 0x3a, 0x0083 }, + { 0x3b, 0x0024 }, + { 0x3c, 0x01ba }, + { 0x3d, 0x0000 }, + { 0x3e, 0x0000 }, + { 0x3f, 0x0000 }, +}; + +static bool wm8753_volatile(struct device *dev, unsigned int reg) +{ + return reg == WM8753_RESET; +} + +/* codec private data */ +struct wm8753_priv { + struct regmap *regmap; + unsigned int sysclk; + unsigned int pcmclk; + + unsigned int voice_fmt; + unsigned int hifi_fmt; + + int dai_func; + struct delayed_work charge_work; +}; + +#define wm8753_reset(c) snd_soc_component_write(c, WM8753_RESET, 0) + +/* + * WM8753 Controls + */ +static const char *wm8753_base[] = {"Linear Control", "Adaptive Boost"}; +static const char *wm8753_base_filter[] = + {"130Hz @ 48kHz", "200Hz @ 48kHz", "100Hz @ 16kHz", "400Hz @ 48kHz", + "100Hz @ 8kHz", "200Hz @ 8kHz"}; +static const char *wm8753_treble[] = {"8kHz", "4kHz"}; +static const char *wm8753_alc_func[] = {"Off", "Right", "Left", "Stereo"}; +static const char *wm8753_ng_type[] = {"Constant PGA Gain", "Mute ADC Output"}; +static const char *wm8753_3d_func[] = {"Capture", "Playback"}; +static const char *wm8753_3d_uc[] = {"2.2kHz", "1.5kHz"}; +static const char *wm8753_3d_lc[] = {"200Hz", "500Hz"}; +static const char *wm8753_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz"}; +static const char *wm8753_mono_mix[] = {"Stereo", "Left", "Right", "Mono"}; +static const char *wm8753_dac_phase[] = {"Non Inverted", "Inverted"}; +static const char *wm8753_line_mix[] = {"Line 1 + 2", "Line 1 - 2", + "Line 1", "Line 2"}; +static const char *wm8753_mono_mux[] = {"Line Mix", "Rx Mix"}; +static const char *wm8753_right_mux[] = {"Line 2", "Rx Mix"}; +static const char *wm8753_left_mux[] = {"Line 1", "Rx Mix"}; +static const char *wm8753_rxmsel[] = {"RXP - RXN", "RXP + RXN", "RXP", "RXN"}; +static const char *wm8753_sidetone_mux[] = {"Left PGA", "Mic 1", "Mic 2", + "Right PGA"}; +static const char *wm8753_mono2_src[] = {"Inverted Mono 1", "Left", "Right", + "Left + Right"}; +static const char *wm8753_out3[] = {"VREF", "ROUT2", "Left + Right"}; +static const char *wm8753_out4[] = {"VREF", "Capture ST", "LOUT2"}; +static const char *wm8753_radcsel[] = {"PGA", "Line or RXP-RXN", "Sidetone"}; +static const char *wm8753_ladcsel[] = {"PGA", "Line or RXP-RXN", "Line"}; +static const char *wm8753_mono_adc[] = {"Stereo", "Analogue Mix Left", + "Analogue Mix Right", "Digital Mono Mix"}; +static const char *wm8753_adc_hp[] = {"3.4Hz @ 48kHz", "82Hz @ 16k", + "82Hz @ 8kHz", "170Hz @ 8kHz"}; +static const char *wm8753_adc_filter[] = {"HiFi", "Voice"}; +static const char *wm8753_mic_sel[] = {"Mic 1", "Mic 2", "Mic 3"}; +static const char *wm8753_dai_mode[] = {"DAI 0", "DAI 1", "DAI 2", "DAI 3"}; +static const char *wm8753_dat_sel[] = {"Stereo", "Left ADC", "Right ADC", + "Channel Swap"}; +static const char *wm8753_rout2_phase[] = {"Non Inverted", "Inverted"}; + +static const struct soc_enum wm8753_enum[] = { +SOC_ENUM_SINGLE(WM8753_BASS, 7, 2, wm8753_base), +SOC_ENUM_SINGLE(WM8753_BASS, 4, 6, wm8753_base_filter), +SOC_ENUM_SINGLE(WM8753_TREBLE, 6, 2, wm8753_treble), +SOC_ENUM_SINGLE(WM8753_ALC1, 7, 4, wm8753_alc_func), +SOC_ENUM_SINGLE(WM8753_NGATE, 1, 2, wm8753_ng_type), +SOC_ENUM_SINGLE(WM8753_3D, 7, 2, wm8753_3d_func), +SOC_ENUM_SINGLE(WM8753_3D, 6, 2, wm8753_3d_uc), +SOC_ENUM_SINGLE(WM8753_3D, 5, 2, wm8753_3d_lc), +SOC_ENUM_SINGLE(WM8753_DAC, 1, 4, wm8753_deemp), +SOC_ENUM_SINGLE(WM8753_DAC, 4, 4, wm8753_mono_mix), +SOC_ENUM_SINGLE(WM8753_DAC, 6, 2, wm8753_dac_phase), +SOC_ENUM_SINGLE(WM8753_INCTL1, 3, 4, wm8753_line_mix), +SOC_ENUM_SINGLE(WM8753_INCTL1, 2, 2, wm8753_mono_mux), +SOC_ENUM_SINGLE(WM8753_INCTL1, 1, 2, wm8753_right_mux), +SOC_ENUM_SINGLE(WM8753_INCTL1, 0, 2, wm8753_left_mux), +SOC_ENUM_SINGLE(WM8753_INCTL2, 6, 4, wm8753_rxmsel), +SOC_ENUM_SINGLE(WM8753_INCTL2, 4, 4, wm8753_sidetone_mux), +SOC_ENUM_SINGLE(WM8753_OUTCTL, 7, 4, wm8753_mono2_src), +SOC_ENUM_SINGLE(WM8753_OUTCTL, 0, 3, wm8753_out3), +SOC_ENUM_SINGLE(WM8753_ADCTL2, 7, 3, wm8753_out4), +SOC_ENUM_SINGLE(WM8753_ADCIN, 2, 3, wm8753_radcsel), +SOC_ENUM_SINGLE(WM8753_ADCIN, 0, 3, wm8753_ladcsel), +SOC_ENUM_SINGLE(WM8753_ADCIN, 4, 4, wm8753_mono_adc), +SOC_ENUM_SINGLE(WM8753_ADC, 2, 4, wm8753_adc_hp), +SOC_ENUM_SINGLE(WM8753_ADC, 4, 2, wm8753_adc_filter), +SOC_ENUM_SINGLE(WM8753_MICBIAS, 6, 3, wm8753_mic_sel), +SOC_ENUM_SINGLE(WM8753_IOCTL, 2, 4, wm8753_dai_mode), +SOC_ENUM_SINGLE(WM8753_ADC, 7, 4, wm8753_dat_sel), +SOC_ENUM_SINGLE(WM8753_OUTCTL, 2, 2, wm8753_rout2_phase), +}; + + +static int wm8753_get_dai(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wm8753->dai_func; + return 0; +} + +static int wm8753_set_dai(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + u16 ioctl; + + if (wm8753->dai_func == ucontrol->value.enumerated.item[0]) + return 0; + + if (snd_soc_component_active(component)) + return -EBUSY; + + ioctl = snd_soc_component_read(component, WM8753_IOCTL); + + wm8753->dai_func = ucontrol->value.enumerated.item[0]; + + if (((ioctl >> 2) & 0x3) == wm8753->dai_func) + return 1; + + ioctl = (ioctl & 0x1f3) | (wm8753->dai_func << 2); + snd_soc_component_write(component, WM8753_IOCTL, ioctl); + + + wm8753_hifi_write_dai_fmt(component, wm8753->hifi_fmt); + wm8753_voice_write_dai_fmt(component, wm8753->voice_fmt); + + return 1; +} + +static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(mic_preamp_tlv, 1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_RANGE(out_tlv, + /* 0000000 - 0101111 = "Analogue mute" */ + 0, 48, TLV_DB_SCALE_ITEM(-25500, 0, 0), + 48, 127, TLV_DB_SCALE_ITEM(-7300, 100, 0) +); +static const DECLARE_TLV_DB_SCALE(mix_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(voice_mix_tlv, -1200, 300, 0); +static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0); + +static const struct snd_kcontrol_new wm8753_snd_controls[] = { +SOC_SINGLE("Hi-Fi DAC Left/Right channel Swap", WM8753_HIFI, 5, 1, 0), +SOC_DOUBLE_R_TLV("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0, dac_tlv), + +SOC_DOUBLE_R_TLV("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0, + adc_tlv), + +SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V, + 0, 127, 0, out_tlv), +SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0, + 127, 0, out_tlv), + +SOC_SINGLE_TLV("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0, out_tlv), + +SOC_DOUBLE_R_TLV("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7, + 1, mix_tlv), +SOC_DOUBLE_R_TLV("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4, + 7, 1, mix_tlv), +SOC_DOUBLE_R_TLV("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7, + 1, voice_mix_tlv), + +SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7, + 1, 0), +SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, + 1, 0), + +SOC_SINGLE_TLV("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1, mix_tlv), +SOC_SINGLE_TLV("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1, + mix_tlv), +SOC_SINGLE_TLV("Mono Voice Playback Volume", WM8753_MOUTM2, 0, 7, 1, + voice_mix_tlv), +SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0), + +SOC_ENUM("Bass Boost", wm8753_enum[0]), +SOC_ENUM("Bass Filter", wm8753_enum[1]), +SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1), + +SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1), +SOC_ENUM("Treble Cut-off", wm8753_enum[2]), + +SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1, + rec_mix_tlv), +SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1, + rec_mix_tlv), + +SOC_DOUBLE_R_TLV("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0, + pga_tlv), +SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0), +SOC_DOUBLE_R("Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 1), + +SOC_ENUM("Capture Filter Select", wm8753_enum[23]), +SOC_ENUM("Capture Filter Cut-off", wm8753_enum[24]), +SOC_SINGLE("Capture Filter Switch", WM8753_ADC, 0, 1, 1), + +SOC_SINGLE("ALC Capture Target Volume", WM8753_ALC1, 0, 7, 0), +SOC_SINGLE("ALC Capture Max Volume", WM8753_ALC1, 4, 7, 0), +SOC_ENUM("ALC Capture Function", wm8753_enum[3]), +SOC_SINGLE("ALC Capture ZC Switch", WM8753_ALC2, 8, 1, 0), +SOC_SINGLE("ALC Capture Hold Time", WM8753_ALC2, 0, 15, 1), +SOC_SINGLE("ALC Capture Decay Time", WM8753_ALC3, 4, 15, 1), +SOC_SINGLE("ALC Capture Attack Time", WM8753_ALC3, 0, 15, 0), +SOC_SINGLE("ALC Capture NG Threshold", WM8753_NGATE, 3, 31, 0), +SOC_ENUM("ALC Capture NG Type", wm8753_enum[4]), +SOC_SINGLE("ALC Capture NG Switch", WM8753_NGATE, 0, 1, 0), + +SOC_ENUM("3D Function", wm8753_enum[5]), +SOC_ENUM("3D Upper Cut-off", wm8753_enum[6]), +SOC_ENUM("3D Lower Cut-off", wm8753_enum[7]), +SOC_SINGLE("3D Volume", WM8753_3D, 1, 15, 0), +SOC_SINGLE("3D Switch", WM8753_3D, 0, 1, 0), + +SOC_SINGLE("Capture 6dB Attenuate", WM8753_ADCTL1, 2, 1, 0), +SOC_SINGLE("Playback 6dB Attenuate", WM8753_ADCTL1, 1, 1, 0), + +SOC_ENUM("De-emphasis", wm8753_enum[8]), +SOC_ENUM("Playback Mono Mix", wm8753_enum[9]), +SOC_ENUM("Playback Phase", wm8753_enum[10]), + +SOC_SINGLE_TLV("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0, mic_preamp_tlv), +SOC_SINGLE_TLV("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0, mic_preamp_tlv), + +SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai), + +SOC_ENUM("ADC Data Select", wm8753_enum[27]), +SOC_ENUM("ROUT2 Phase", wm8753_enum[28]), +}; + +/* + * _DAPM_ Controls + */ + +/* Left Mixer */ +static const struct snd_kcontrol_new wm8753_left_mixer_controls[] = { +SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_LOUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_LOUTM2, 7, 1, 0), +SOC_DAPM_SINGLE("Left Playback Switch", WM8753_LOUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_LOUTM1, 7, 1, 0), +}; + +/* Right mixer */ +static const struct snd_kcontrol_new wm8753_right_mixer_controls[] = { +SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_ROUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_ROUTM2, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8753_ROUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_ROUTM1, 7, 1, 0), +}; + +/* Mono mixer */ +static const struct snd_kcontrol_new wm8753_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8753_MOUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8753_MOUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_MOUTM2, 3, 1, 0), +SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_MOUTM2, 7, 1, 0), +SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_MOUTM1, 7, 1, 0), +}; + +/* Mono 2 Mux */ +static const struct snd_kcontrol_new wm8753_mono2_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[17]); + +/* Out 3 Mux */ +static const struct snd_kcontrol_new wm8753_out3_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[18]); + +/* Out 4 Mux */ +static const struct snd_kcontrol_new wm8753_out4_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[19]); + +/* ADC Mono Mix */ +static const struct snd_kcontrol_new wm8753_adc_mono_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[22]); + +/* Record mixer */ +static const struct snd_kcontrol_new wm8753_record_mixer_controls[] = { +SOC_DAPM_SINGLE("Voice Capture Switch", WM8753_RECMIX2, 3, 1, 0), +SOC_DAPM_SINGLE("Left Capture Switch", WM8753_RECMIX1, 3, 1, 0), +SOC_DAPM_SINGLE("Right Capture Switch", WM8753_RECMIX1, 7, 1, 0), +}; + +/* Left ADC mux */ +static const struct snd_kcontrol_new wm8753_adc_left_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[21]); + +/* Right ADC mux */ +static const struct snd_kcontrol_new wm8753_adc_right_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[20]); + +/* MIC mux */ +static const struct snd_kcontrol_new wm8753_mic_mux_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[16]); + +/* ALC mixer */ +static const struct snd_kcontrol_new wm8753_alc_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Capture Switch", WM8753_INCTL2, 3, 1, 0), +SOC_DAPM_SINGLE("Mic2 Capture Switch", WM8753_INCTL2, 2, 1, 0), +SOC_DAPM_SINGLE("Mic1 Capture Switch", WM8753_INCTL2, 1, 1, 0), +SOC_DAPM_SINGLE("Rx Capture Switch", WM8753_INCTL2, 0, 1, 0), +}; + +/* Left Line mux */ +static const struct snd_kcontrol_new wm8753_line_left_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[14]); + +/* Right Line mux */ +static const struct snd_kcontrol_new wm8753_line_right_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[13]); + +/* Mono Line mux */ +static const struct snd_kcontrol_new wm8753_line_mono_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[12]); + +/* Line mux and mixer */ +static const struct snd_kcontrol_new wm8753_line_mux_mix_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[11]); + +/* Rx mux and mixer */ +static const struct snd_kcontrol_new wm8753_rx_mux_mix_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[15]); + +/* Mic Selector Mux */ +static const struct snd_kcontrol_new wm8753_mic_sel_mux_controls = +SOC_DAPM_ENUM("Route", wm8753_enum[25]); + +static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = { +SND_SOC_DAPM_MICBIAS("Mic Bias", WM8753_PWR1, 5, 0), +SND_SOC_DAPM_MIXER("Left Mixer", WM8753_PWR4, 0, 0, + &wm8753_left_mixer_controls[0], ARRAY_SIZE(wm8753_left_mixer_controls)), +SND_SOC_DAPM_PGA("Left Out 1", WM8753_PWR3, 8, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left Out 2", WM8753_PWR3, 6, 0, NULL, 0), +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", WM8753_PWR1, 3, 0), +SND_SOC_DAPM_OUTPUT("LOUT1"), +SND_SOC_DAPM_OUTPUT("LOUT2"), +SND_SOC_DAPM_MIXER("Right Mixer", WM8753_PWR4, 1, 0, + &wm8753_right_mixer_controls[0], ARRAY_SIZE(wm8753_right_mixer_controls)), +SND_SOC_DAPM_PGA("Right Out 1", WM8753_PWR3, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Out 2", WM8753_PWR3, 5, 0, NULL, 0), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", WM8753_PWR1, 2, 0), +SND_SOC_DAPM_OUTPUT("ROUT1"), +SND_SOC_DAPM_OUTPUT("ROUT2"), +SND_SOC_DAPM_MIXER("Mono Mixer", WM8753_PWR4, 2, 0, + &wm8753_mono_mixer_controls[0], ARRAY_SIZE(wm8753_mono_mixer_controls)), +SND_SOC_DAPM_PGA("Mono Out 1", WM8753_PWR3, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("Mono Out 2", WM8753_PWR3, 1, 0, NULL, 0), +SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", WM8753_PWR1, 4, 0), +SND_SOC_DAPM_OUTPUT("MONO1"), +SND_SOC_DAPM_MUX("Mono 2 Mux", SND_SOC_NOPM, 0, 0, &wm8753_mono2_controls), +SND_SOC_DAPM_OUTPUT("MONO2"), +SND_SOC_DAPM_MIXER("Out3 Left + Right", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out3_controls), +SND_SOC_DAPM_PGA("Out 3", WM8753_PWR3, 4, 0, NULL, 0), +SND_SOC_DAPM_OUTPUT("OUT3"), +SND_SOC_DAPM_MUX("Out4 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out4_controls), +SND_SOC_DAPM_PGA("Out 4", WM8753_PWR3, 3, 0, NULL, 0), +SND_SOC_DAPM_OUTPUT("OUT4"), +SND_SOC_DAPM_MIXER("Playback Mixer", WM8753_PWR4, 3, 0, + &wm8753_record_mixer_controls[0], + ARRAY_SIZE(wm8753_record_mixer_controls)), +SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8753_PWR2, 3, 0), +SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8753_PWR2, 2, 0), +SND_SOC_DAPM_MUX("Capture Left Mixer", SND_SOC_NOPM, 0, 0, + &wm8753_adc_mono_controls), +SND_SOC_DAPM_MUX("Capture Right Mixer", SND_SOC_NOPM, 0, 0, + &wm8753_adc_mono_controls), +SND_SOC_DAPM_MUX("Capture Left Mux", SND_SOC_NOPM, 0, 0, + &wm8753_adc_left_controls), +SND_SOC_DAPM_MUX("Capture Right Mux", SND_SOC_NOPM, 0, 0, + &wm8753_adc_right_controls), +SND_SOC_DAPM_MUX("Mic Sidetone Mux", SND_SOC_NOPM, 0, 0, + &wm8753_mic_mux_controls), +SND_SOC_DAPM_PGA("Left Capture Volume", WM8753_PWR2, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Capture Volume", WM8753_PWR2, 4, 0, NULL, 0), +SND_SOC_DAPM_MIXER("ALC Mixer", WM8753_PWR2, 6, 0, + &wm8753_alc_mixer_controls[0], ARRAY_SIZE(wm8753_alc_mixer_controls)), +SND_SOC_DAPM_MUX("Line Left Mux", SND_SOC_NOPM, 0, 0, + &wm8753_line_left_controls), +SND_SOC_DAPM_MUX("Line Right Mux", SND_SOC_NOPM, 0, 0, + &wm8753_line_right_controls), +SND_SOC_DAPM_MUX("Line Mono Mux", SND_SOC_NOPM, 0, 0, + &wm8753_line_mono_controls), +SND_SOC_DAPM_MUX("Line Mixer", WM8753_PWR2, 0, 0, + &wm8753_line_mux_mix_controls), +SND_SOC_DAPM_MUX("Rx Mixer", WM8753_PWR2, 1, 0, + &wm8753_rx_mux_mix_controls), +SND_SOC_DAPM_PGA("Mic 1 Volume", WM8753_PWR2, 8, 0, NULL, 0), +SND_SOC_DAPM_PGA("Mic 2 Volume", WM8753_PWR2, 7, 0, NULL, 0), +SND_SOC_DAPM_MUX("Mic Selection Mux", SND_SOC_NOPM, 0, 0, + &wm8753_mic_sel_mux_controls), +SND_SOC_DAPM_INPUT("LINE1"), +SND_SOC_DAPM_INPUT("LINE2"), +SND_SOC_DAPM_INPUT("RXP"), +SND_SOC_DAPM_INPUT("RXN"), +SND_SOC_DAPM_INPUT("ACIN"), +SND_SOC_DAPM_OUTPUT("ACOP"), +SND_SOC_DAPM_INPUT("MIC1N"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2N"), +SND_SOC_DAPM_INPUT("MIC2"), +SND_SOC_DAPM_VMID("VREF"), +}; + +static const struct snd_soc_dapm_route wm8753_dapm_routes[] = { + /* left mixer */ + {"Left Mixer", "Left Playback Switch", "Left DAC"}, + {"Left Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Left Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"}, + {"Left Mixer", "Bypass Playback Switch", "Line Left Mux"}, + + /* right mixer */ + {"Right Mixer", "Right Playback Switch", "Right DAC"}, + {"Right Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Right Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"}, + {"Right Mixer", "Bypass Playback Switch", "Line Right Mux"}, + + /* mono mixer */ + {"Mono Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Mono Mixer", "Left Playback Switch", "Left DAC"}, + {"Mono Mixer", "Right Playback Switch", "Right DAC"}, + {"Mono Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"}, + {"Mono Mixer", "Bypass Playback Switch", "Line Mono Mux"}, + + /* left out */ + {"Left Out 1", NULL, "Left Mixer"}, + {"Left Out 2", NULL, "Left Mixer"}, + {"LOUT1", NULL, "Left Out 1"}, + {"LOUT2", NULL, "Left Out 2"}, + + /* right out */ + {"Right Out 1", NULL, "Right Mixer"}, + {"Right Out 2", NULL, "Right Mixer"}, + {"ROUT1", NULL, "Right Out 1"}, + {"ROUT2", NULL, "Right Out 2"}, + + /* mono 1 out */ + {"Mono Out 1", NULL, "Mono Mixer"}, + {"MONO1", NULL, "Mono Out 1"}, + + /* mono 2 out */ + {"Mono 2 Mux", "Left + Right", "Out3 Left + Right"}, + {"Mono 2 Mux", "Inverted Mono 1", "MONO1"}, + {"Mono 2 Mux", "Left", "Left Mixer"}, + {"Mono 2 Mux", "Right", "Right Mixer"}, + {"Mono Out 2", NULL, "Mono 2 Mux"}, + {"MONO2", NULL, "Mono Out 2"}, + + /* out 3 */ + {"Out3 Left + Right", NULL, "Left Mixer"}, + {"Out3 Left + Right", NULL, "Right Mixer"}, + {"Out3 Mux", "VREF", "VREF"}, + {"Out3 Mux", "Left + Right", "Out3 Left + Right"}, + {"Out3 Mux", "ROUT2", "ROUT2"}, + {"Out 3", NULL, "Out3 Mux"}, + {"OUT3", NULL, "Out 3"}, + + /* out 4 */ + {"Out4 Mux", "VREF", "VREF"}, + {"Out4 Mux", "Capture ST", "Playback Mixer"}, + {"Out4 Mux", "LOUT2", "LOUT2"}, + {"Out 4", NULL, "Out4 Mux"}, + {"OUT4", NULL, "Out 4"}, + + /* record mixer */ + {"Playback Mixer", "Left Capture Switch", "Left Mixer"}, + {"Playback Mixer", "Voice Capture Switch", "Mono Mixer"}, + {"Playback Mixer", "Right Capture Switch", "Right Mixer"}, + + /* Mic/SideTone Mux */ + {"Mic Sidetone Mux", "Left PGA", "Left Capture Volume"}, + {"Mic Sidetone Mux", "Right PGA", "Right Capture Volume"}, + {"Mic Sidetone Mux", "Mic 1", "Mic 1 Volume"}, + {"Mic Sidetone Mux", "Mic 2", "Mic 2 Volume"}, + + /* Capture Left Mux */ + {"Capture Left Mux", "PGA", "Left Capture Volume"}, + {"Capture Left Mux", "Line or RXP-RXN", "Line Left Mux"}, + {"Capture Left Mux", "Line", "LINE1"}, + + /* Capture Right Mux */ + {"Capture Right Mux", "PGA", "Right Capture Volume"}, + {"Capture Right Mux", "Line or RXP-RXN", "Line Right Mux"}, + {"Capture Right Mux", "Sidetone", "Playback Mixer"}, + + /* Mono Capture mixer-mux */ + {"Capture Right Mixer", "Stereo", "Capture Right Mux"}, + {"Capture Left Mixer", "Stereo", "Capture Left Mux"}, + {"Capture Left Mixer", "Analogue Mix Left", "Capture Left Mux"}, + {"Capture Left Mixer", "Analogue Mix Left", "Capture Right Mux"}, + {"Capture Right Mixer", "Analogue Mix Right", "Capture Left Mux"}, + {"Capture Right Mixer", "Analogue Mix Right", "Capture Right Mux"}, + {"Capture Left Mixer", "Digital Mono Mix", "Capture Left Mux"}, + {"Capture Left Mixer", "Digital Mono Mix", "Capture Right Mux"}, + {"Capture Right Mixer", "Digital Mono Mix", "Capture Left Mux"}, + {"Capture Right Mixer", "Digital Mono Mix", "Capture Right Mux"}, + + /* ADC */ + {"Left ADC", NULL, "Capture Left Mixer"}, + {"Right ADC", NULL, "Capture Right Mixer"}, + + /* Left Capture Volume */ + {"Left Capture Volume", NULL, "ACIN"}, + + /* Right Capture Volume */ + {"Right Capture Volume", NULL, "Mic 2 Volume"}, + + /* ALC Mixer */ + {"ALC Mixer", "Line Capture Switch", "Line Mixer"}, + {"ALC Mixer", "Mic2 Capture Switch", "Mic 2 Volume"}, + {"ALC Mixer", "Mic1 Capture Switch", "Mic 1 Volume"}, + {"ALC Mixer", "Rx Capture Switch", "Rx Mixer"}, + + /* Line Left Mux */ + {"Line Left Mux", "Line 1", "LINE1"}, + {"Line Left Mux", "Rx Mix", "Rx Mixer"}, + + /* Line Right Mux */ + {"Line Right Mux", "Line 2", "LINE2"}, + {"Line Right Mux", "Rx Mix", "Rx Mixer"}, + + /* Line Mono Mux */ + {"Line Mono Mux", "Line Mix", "Line Mixer"}, + {"Line Mono Mux", "Rx Mix", "Rx Mixer"}, + + /* Line Mixer/Mux */ + {"Line Mixer", "Line 1 + 2", "LINE1"}, + {"Line Mixer", "Line 1 - 2", "LINE1"}, + {"Line Mixer", "Line 1 + 2", "LINE2"}, + {"Line Mixer", "Line 1 - 2", "LINE2"}, + {"Line Mixer", "Line 1", "LINE1"}, + {"Line Mixer", "Line 2", "LINE2"}, + + /* Rx Mixer/Mux */ + {"Rx Mixer", "RXP - RXN", "RXP"}, + {"Rx Mixer", "RXP + RXN", "RXP"}, + {"Rx Mixer", "RXP - RXN", "RXN"}, + {"Rx Mixer", "RXP + RXN", "RXN"}, + {"Rx Mixer", "RXP", "RXP"}, + {"Rx Mixer", "RXN", "RXN"}, + + /* Mic 1 Volume */ + {"Mic 1 Volume", NULL, "MIC1N"}, + {"Mic 1 Volume", NULL, "Mic Selection Mux"}, + + /* Mic 2 Volume */ + {"Mic 2 Volume", NULL, "MIC2N"}, + {"Mic 2 Volume", NULL, "MIC2"}, + + /* Mic Selector Mux */ + {"Mic Selection Mux", "Mic 1", "MIC1"}, + {"Mic Selection Mux", "Mic 2", "MIC2N"}, + {"Mic Selection Mux", "Mic 3", "MIC2"}, + + /* ACOP */ + {"ACOP", NULL, "ALC Mixer"}, +}; + +/* PLL divisors */ +struct _pll_div { + u32 div2:1; + u32 n:4; + u32 k:24; +}; + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 22) * 10) + +static void pll_factors(struct _pll_div *pll_div, unsigned int target, + unsigned int source) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod; + + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div->div2 = 1; + Ndiv = target / source; + } else + pll_div->div2 = 0; + + if ((Ndiv < 6) || (Ndiv > 12)) + printk(KERN_WARNING + "wm8753: unsupported N = %u\n", Ndiv); + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; +} + +static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + u16 reg, enable; + int offset; + struct snd_soc_component *component = codec_dai->component; + + if (pll_id < WM8753_PLL1 || pll_id > WM8753_PLL2) + return -ENODEV; + + if (pll_id == WM8753_PLL1) { + offset = 0; + enable = 0x10; + reg = snd_soc_component_read(component, WM8753_CLOCK) & 0xffef; + } else { + offset = 4; + enable = 0x8; + reg = snd_soc_component_read(component, WM8753_CLOCK) & 0xfff7; + } + + if (!freq_in || !freq_out) { + /* disable PLL */ + snd_soc_component_write(component, WM8753_PLL1CTL1 + offset, 0x0026); + snd_soc_component_write(component, WM8753_CLOCK, reg); + return 0; + } else { + u16 value = 0; + struct _pll_div pll_div; + + pll_factors(&pll_div, freq_out * 8, freq_in); + + /* set up N and K PLL divisor ratios */ + /* bits 8:5 = PLL_N, bits 3:0 = PLL_K[21:18] */ + value = (pll_div.n << 5) + ((pll_div.k & 0x3c0000) >> 18); + snd_soc_component_write(component, WM8753_PLL1CTL2 + offset, value); + + /* bits 8:0 = PLL_K[17:9] */ + value = (pll_div.k & 0x03fe00) >> 9; + snd_soc_component_write(component, WM8753_PLL1CTL3 + offset, value); + + /* bits 8:0 = PLL_K[8:0] */ + value = pll_div.k & 0x0001ff; + snd_soc_component_write(component, WM8753_PLL1CTL4 + offset, value); + + /* set PLL as input and enable */ + snd_soc_component_write(component, WM8753_PLL1CTL1 + offset, 0x0027 | + (pll_div.div2 << 3)); + snd_soc_component_write(component, WM8753_CLOCK, reg | enable); + } + return 0; +} + +struct _coeff_div { + u32 mclk; + u32 rate; + u8 sr:5; + u8 usb:1; +}; + +/* codec hifi mclk (after PLL) clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 8k */ + {12288000, 8000, 0x6, 0x0}, + {11289600, 8000, 0x16, 0x0}, + {18432000, 8000, 0x7, 0x0}, + {16934400, 8000, 0x17, 0x0}, + {12000000, 8000, 0x6, 0x1}, + + /* 11.025k */ + {11289600, 11025, 0x18, 0x0}, + {16934400, 11025, 0x19, 0x0}, + {12000000, 11025, 0x19, 0x1}, + + /* 16k */ + {12288000, 16000, 0xa, 0x0}, + {18432000, 16000, 0xb, 0x0}, + {12000000, 16000, 0xa, 0x1}, + + /* 22.05k */ + {11289600, 22050, 0x1a, 0x0}, + {16934400, 22050, 0x1b, 0x0}, + {12000000, 22050, 0x1b, 0x1}, + + /* 32k */ + {12288000, 32000, 0xc, 0x0}, + {18432000, 32000, 0xd, 0x0}, + {12000000, 32000, 0xa, 0x1}, + + /* 44.1k */ + {11289600, 44100, 0x10, 0x0}, + {16934400, 44100, 0x11, 0x0}, + {12000000, 44100, 0x11, 0x1}, + + /* 48k */ + {12288000, 48000, 0x0, 0x0}, + {18432000, 48000, 0x1, 0x0}, + {12000000, 48000, 0x0, 0x1}, + + /* 88.2k */ + {11289600, 88200, 0x1e, 0x0}, + {16934400, 88200, 0x1f, 0x0}, + {12000000, 88200, 0x1f, 0x1}, + + /* 96k */ + {12288000, 96000, 0xe, 0x0}, + {18432000, 96000, 0xf, 0x0}, + {12000000, 96000, 0xe, 0x1}, +}; + +static int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return -EINVAL; +} + +/* + * Clock after PLL and dividers + */ +static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case 11289600: + case 12000000: + case 12288000: + case 16934400: + case 18432000: + if (clk_id == WM8753_MCLK) { + wm8753->sysclk = freq; + return 0; + } else if (clk_id == WM8753_PCMCLK) { + wm8753->pcmclk = freq; + return 0; + } + break; + } + return -EINVAL; +} + +/* + * Set's ADC and Voice DAC format. + */ +static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_component *component, + unsigned int fmt) +{ + u16 voice = snd_soc_component_read(component, WM8753_PCM) & 0x01ec; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + voice |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + voice |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + voice |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + voice |= 0x0013; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8753_PCM, voice); + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + */ +static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + u16 voice = snd_soc_component_read(component, WM8753_PCM) & 0x01f3; + u16 srate = snd_soc_component_read(component, WM8753_SRATE1) & 0x017f; + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + voice |= 0x0004; + break; + case 24: + voice |= 0x0008; + break; + case 32: + voice |= 0x000c; + break; + } + + /* sample rate */ + if (params_rate(params) * 384 == wm8753->pcmclk) + srate |= 0x80; + snd_soc_component_write(component, WM8753_SRATE1, srate); + + snd_soc_component_write(component, WM8753_PCM, voice); + return 0; +} + +/* + * Set's PCM dai fmt and BCLK. + */ +static int wm8753_pcm_set_dai_fmt(struct snd_soc_component *component, + unsigned int fmt) +{ + u16 voice, ioctl; + + voice = snd_soc_component_read(component, WM8753_PCM) & 0x011f; + ioctl = snd_soc_component_read(component, WM8753_IOCTL) & 0x015d; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + ioctl |= 0x2; + fallthrough; + case SND_SOC_DAIFMT_CBM_CFS: + voice |= 0x0040; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + voice |= 0x0080; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + voice &= ~0x0010; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + voice |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + voice |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + voice |= 0x0010; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8753_PCM, voice); + snd_soc_component_write(component, WM8753_IOCTL, ioctl); + return 0; +} + +static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + + switch (div_id) { + case WM8753_PCMDIV: + reg = snd_soc_component_read(component, WM8753_CLOCK) & 0x003f; + snd_soc_component_write(component, WM8753_CLOCK, reg | div); + break; + case WM8753_BCLKDIV: + reg = snd_soc_component_read(component, WM8753_SRATE2) & 0x01c7; + snd_soc_component_write(component, WM8753_SRATE2, reg | div); + break; + case WM8753_VXCLKDIV: + reg = snd_soc_component_read(component, WM8753_SRATE2) & 0x003f; + snd_soc_component_write(component, WM8753_SRATE2, reg | div); + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * Set's HiFi DAC format. + */ +static int wm8753_hdac_set_dai_fmt(struct snd_soc_component *component, + unsigned int fmt) +{ + u16 hifi = snd_soc_component_read(component, WM8753_HIFI) & 0x01e0; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + hifi |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + hifi |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + hifi |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + hifi |= 0x0013; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8753_HIFI, hifi); + return 0; +} + +/* + * Set's I2S DAI format. + */ +static int wm8753_i2s_set_dai_fmt(struct snd_soc_component *component, + unsigned int fmt) +{ + u16 ioctl, hifi; + + hifi = snd_soc_component_read(component, WM8753_HIFI) & 0x013f; + ioctl = snd_soc_component_read(component, WM8753_IOCTL) & 0x00ae; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + ioctl |= 0x1; + fallthrough; + case SND_SOC_DAIFMT_CBM_CFS: + hifi |= 0x0040; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + hifi |= 0x0080; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + hifi &= ~0x0010; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + hifi |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + hifi |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + hifi |= 0x0010; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8753_HIFI, hifi); + snd_soc_component_write(component, WM8753_IOCTL, ioctl); + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + */ +static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + u16 srate = snd_soc_component_read(component, WM8753_SRATE1) & 0x01c0; + u16 hifi = snd_soc_component_read(component, WM8753_HIFI) & 0x01f3; + int coeff; + + /* is digital filter coefficient valid ? */ + coeff = get_coeff(wm8753->sysclk, params_rate(params)); + if (coeff < 0) { + printk(KERN_ERR "wm8753 invalid MCLK or rate\n"); + return coeff; + } + snd_soc_component_write(component, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) | + coeff_div[coeff].usb); + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + hifi |= 0x0004; + break; + case 24: + hifi |= 0x0008; + break; + case 32: + hifi |= 0x000c; + break; + } + + snd_soc_component_write(component, WM8753_HIFI, hifi); + return 0; +} + +static int wm8753_mode1v_set_dai_fmt(struct snd_soc_component *component, + unsigned int fmt) +{ + u16 clock; + + /* set clk source as pcmclk */ + clock = snd_soc_component_read(component, WM8753_CLOCK) & 0xfffb; + snd_soc_component_write(component, WM8753_CLOCK, clock); + + return wm8753_vdac_adc_set_dai_fmt(component, fmt); +} + +static int wm8753_mode1h_set_dai_fmt(struct snd_soc_component *component, + unsigned int fmt) +{ + return wm8753_hdac_set_dai_fmt(component, fmt); +} + +static int wm8753_mode2_set_dai_fmt(struct snd_soc_component *component, + unsigned int fmt) +{ + u16 clock; + + /* set clk source as pcmclk */ + clock = snd_soc_component_read(component, WM8753_CLOCK) & 0xfffb; + snd_soc_component_write(component, WM8753_CLOCK, clock); + + return wm8753_vdac_adc_set_dai_fmt(component, fmt); +} + +static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_component *component, + unsigned int fmt) +{ + u16 clock; + + /* set clk source as mclk */ + clock = snd_soc_component_read(component, WM8753_CLOCK) & 0xfffb; + snd_soc_component_write(component, WM8753_CLOCK, clock | 0x4); + + if (wm8753_hdac_set_dai_fmt(component, fmt) < 0) + return -EINVAL; + return wm8753_vdac_adc_set_dai_fmt(component, fmt); +} + +static int wm8753_hifi_write_dai_fmt(struct snd_soc_component *component, + unsigned int fmt) +{ + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (wm8753->dai_func) { + case 0: + ret = wm8753_mode1h_set_dai_fmt(component, fmt); + break; + case 1: + ret = wm8753_mode2_set_dai_fmt(component, fmt); + break; + case 2: + case 3: + ret = wm8753_mode3_4_set_dai_fmt(component, fmt); + break; + default: + break; + } + if (ret) + return ret; + + return wm8753_i2s_set_dai_fmt(component, fmt); +} + +static int wm8753_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + + wm8753->hifi_fmt = fmt; + + return wm8753_hifi_write_dai_fmt(component, fmt); +}; + +static int wm8753_voice_write_dai_fmt(struct snd_soc_component *component, + unsigned int fmt) +{ + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + int ret = 0; + + if (wm8753->dai_func != 0) + return 0; + + ret = wm8753_mode1v_set_dai_fmt(component, fmt); + if (ret) + return ret; + ret = wm8753_pcm_set_dai_fmt(component, fmt); + if (ret) + return ret; + + return 0; +}; + +static int wm8753_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + + wm8753->voice_fmt = fmt; + + return wm8753_voice_write_dai_fmt(component, fmt); +}; + +static int wm8753_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, WM8753_DAC) & 0xfff7; + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + + /* the digital mute covers the HiFi and Voice DAC's on the WM8753. + * make sure we check if they are not both active when we mute */ + if (mute && wm8753->dai_func == 1) { + if (!snd_soc_component_active(component)) + snd_soc_component_write(component, WM8753_DAC, mute_reg | 0x8); + } else { + if (mute) + snd_soc_component_write(component, WM8753_DAC, mute_reg | 0x8); + else + snd_soc_component_write(component, WM8753_DAC, mute_reg); + } + + return 0; +} + +static void wm8753_charge_work(struct work_struct *work) +{ + struct wm8753_priv *wm8753 = + container_of(work, struct wm8753_priv, charge_work.work); + + /* Set to 500k */ + regmap_update_bits(wm8753->regmap, WM8753_PWR1, 0x0180, 0x0100); +} + +static int wm8753_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + u16 pwr_reg = snd_soc_component_read(component, WM8753_PWR1) & 0xfe3e; + + switch (level) { + case SND_SOC_BIAS_ON: + /* set vmid to 50k and unmute dac */ + snd_soc_component_write(component, WM8753_PWR1, pwr_reg | 0x00c0); + break; + case SND_SOC_BIAS_PREPARE: + /* Wait until fully charged */ + flush_delayed_work(&wm8753->charge_work); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* set vmid to 5k for quick power up */ + snd_soc_component_write(component, WM8753_PWR1, pwr_reg | 0x01c1); + schedule_delayed_work(&wm8753->charge_work, + msecs_to_jiffies(caps_charge)); + } else { + /* mute dac and set vmid to 500k, enable VREF */ + snd_soc_component_write(component, WM8753_PWR1, pwr_reg | 0x0141); + } + break; + case SND_SOC_BIAS_OFF: + cancel_delayed_work_sync(&wm8753->charge_work); + snd_soc_component_write(component, WM8753_PWR1, 0x0001); + break; + } + return 0; +} + +#define WM8753_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define WM8753_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +/* + * The WM8753 supports up to 4 different and mutually exclusive DAI + * configurations. This gives 2 PCM's available for use, hifi and voice. + * NOTE: The Voice PCM cannot play or capture audio to the CPU as it's DAI + * is connected between the wm8753 and a BT codec or GSM modem. + * + * 1. Voice over PCM DAI - HIFI DAC over HIFI DAI + * 2. Voice over HIFI DAI - HIFI disabled + * 3. Voice disabled - HIFI over HIFI + * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture + */ +static const struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = { + .hw_params = wm8753_i2s_hw_params, + .mute_stream = wm8753_mute, + .set_fmt = wm8753_hifi_set_dai_fmt, + .set_clkdiv = wm8753_set_dai_clkdiv, + .set_pll = wm8753_set_dai_pll, + .set_sysclk = wm8753_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = { + .hw_params = wm8753_pcm_hw_params, + .mute_stream = wm8753_mute, + .set_fmt = wm8753_voice_set_dai_fmt, + .set_clkdiv = wm8753_set_dai_clkdiv, + .set_pll = wm8753_set_dai_pll, + .set_sysclk = wm8753_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8753_dai[] = { +/* DAI HiFi mode 1 */ +{ .name = "wm8753-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8753_RATES, + .formats = WM8753_FORMATS + }, + .capture = { /* dummy for fast DAI switching */ + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8753_RATES, + .formats = WM8753_FORMATS + }, + .ops = &wm8753_dai_ops_hifi_mode, +}, +/* DAI Voice mode 1 */ +{ .name = "wm8753-voice", + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = WM8753_RATES, + .formats = WM8753_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8753_RATES, + .formats = WM8753_FORMATS, + }, + .ops = &wm8753_dai_ops_voice_mode, +}, +}; + +static int wm8753_resume(struct snd_soc_component *component) +{ + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + + regcache_sync(wm8753->regmap); + + return 0; +} + +static int wm8753_probe(struct snd_soc_component *component) +{ + struct wm8753_priv *wm8753 = snd_soc_component_get_drvdata(component); + int ret; + + INIT_DELAYED_WORK(&wm8753->charge_work, wm8753_charge_work); + + ret = wm8753_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + wm8753->dai_func = 0; + + /* set the update bits */ + snd_soc_component_update_bits(component, WM8753_LDAC, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8753_RDAC, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8753_LADC, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8753_RADC, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8753_LOUT1V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8753_ROUT1V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8753_LOUT2V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8753_ROUT2V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8753_LINVOL, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8753_RINVOL, 0x0100, 0x0100); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8753 = { + .probe = wm8753_probe, + .resume = wm8753_resume, + .set_bias_level = wm8753_set_bias_level, + .controls = wm8753_snd_controls, + .num_controls = ARRAY_SIZE(wm8753_snd_controls), + .dapm_widgets = wm8753_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8753_dapm_widgets), + .dapm_routes = wm8753_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8753_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8753_of_match[] = { + { .compatible = "wlf,wm8753", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8753_of_match); + +static const struct regmap_config wm8753_regmap = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = WM8753_ADCTL2, + .volatile_reg = wm8753_volatile, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm8753_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8753_reg_defaults), +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8753_spi_probe(struct spi_device *spi) +{ + struct wm8753_priv *wm8753; + int ret; + + wm8753 = devm_kzalloc(&spi->dev, sizeof(struct wm8753_priv), + GFP_KERNEL); + if (wm8753 == NULL) + return -ENOMEM; + + spi_set_drvdata(spi, wm8753); + + wm8753->regmap = devm_regmap_init_spi(spi, &wm8753_regmap); + if (IS_ERR(wm8753->regmap)) { + ret = PTR_ERR(wm8753->regmap); + dev_err(&spi->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = devm_snd_soc_register_component(&spi->dev, &soc_component_dev_wm8753, + wm8753_dai, ARRAY_SIZE(wm8753_dai)); + if (ret != 0) + dev_err(&spi->dev, "Failed to register CODEC: %d\n", ret); + + return ret; +} + +static struct spi_driver wm8753_spi_driver = { + .driver = { + .name = "wm8753", + .of_match_table = wm8753_of_match, + }, + .probe = wm8753_spi_probe, +}; +#endif /* CONFIG_SPI_MASTER */ + +#if IS_ENABLED(CONFIG_I2C) +static int wm8753_i2c_probe(struct i2c_client *i2c) +{ + struct wm8753_priv *wm8753; + int ret; + + wm8753 = devm_kzalloc(&i2c->dev, sizeof(struct wm8753_priv), + GFP_KERNEL); + if (wm8753 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm8753); + + wm8753->regmap = devm_regmap_init_i2c(i2c, &wm8753_regmap); + if (IS_ERR(wm8753->regmap)) { + ret = PTR_ERR(wm8753->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_wm8753, + wm8753_dai, ARRAY_SIZE(wm8753_dai)); + if (ret != 0) + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id wm8753_i2c_id[] = { + { "wm8753", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id); + +static struct i2c_driver wm8753_i2c_driver = { + .driver = { + .name = "wm8753", + .of_match_table = wm8753_of_match, + }, + .probe = wm8753_i2c_probe, + .id_table = wm8753_i2c_id, +}; +#endif + +static int __init wm8753_modinit(void) +{ + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8753_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8753 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8753_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8753 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8753_modinit); + +static void __exit wm8753_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8753_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8753_spi_driver); +#endif +} +module_exit(wm8753_exit); + +MODULE_DESCRIPTION("ASoC WM8753 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h new file mode 100644 index 0000000000..5a60452e1c --- /dev/null +++ b/sound/soc/codecs/wm8753.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * wm8753.h -- audio driver for WM8753 + * + * Copyright 2003 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + */ + +#ifndef _WM8753_H +#define _WM8753_H + +/* WM8753 register space */ + +#define WM8753_DAC 0x01 +#define WM8753_ADC 0x02 +#define WM8753_PCM 0x03 +#define WM8753_HIFI 0x04 +#define WM8753_IOCTL 0x05 +#define WM8753_SRATE1 0x06 +#define WM8753_SRATE2 0x07 +#define WM8753_LDAC 0x08 +#define WM8753_RDAC 0x09 +#define WM8753_BASS 0x0a +#define WM8753_TREBLE 0x0b +#define WM8753_ALC1 0x0c +#define WM8753_ALC2 0x0d +#define WM8753_ALC3 0x0e +#define WM8753_NGATE 0x0f +#define WM8753_LADC 0x10 +#define WM8753_RADC 0x11 +#define WM8753_ADCTL1 0x12 +#define WM8753_3D 0x13 +#define WM8753_PWR1 0x14 +#define WM8753_PWR2 0x15 +#define WM8753_PWR3 0x16 +#define WM8753_PWR4 0x17 +#define WM8753_ID 0x18 +#define WM8753_INTPOL 0x19 +#define WM8753_INTEN 0x1a +#define WM8753_GPIO1 0x1b +#define WM8753_GPIO2 0x1c +#define WM8753_RESET 0x1f +#define WM8753_RECMIX1 0x20 +#define WM8753_RECMIX2 0x21 +#define WM8753_LOUTM1 0x22 +#define WM8753_LOUTM2 0x23 +#define WM8753_ROUTM1 0x24 +#define WM8753_ROUTM2 0x25 +#define WM8753_MOUTM1 0x26 +#define WM8753_MOUTM2 0x27 +#define WM8753_LOUT1V 0x28 +#define WM8753_ROUT1V 0x29 +#define WM8753_LOUT2V 0x2a +#define WM8753_ROUT2V 0x2b +#define WM8753_MOUTV 0x2c +#define WM8753_OUTCTL 0x2d +#define WM8753_ADCIN 0x2e +#define WM8753_INCTL1 0x2f +#define WM8753_INCTL2 0x30 +#define WM8753_LINVOL 0x31 +#define WM8753_RINVOL 0x32 +#define WM8753_MICBIAS 0x33 +#define WM8753_CLOCK 0x34 +#define WM8753_PLL1CTL1 0x35 +#define WM8753_PLL1CTL2 0x36 +#define WM8753_PLL1CTL3 0x37 +#define WM8753_PLL1CTL4 0x38 +#define WM8753_PLL2CTL1 0x39 +#define WM8753_PLL2CTL2 0x3a +#define WM8753_PLL2CTL3 0x3b +#define WM8753_PLL2CTL4 0x3c +#define WM8753_BIASCTL 0x3d +#define WM8753_ADCTL2 0x3f + +#define WM8753_PLL1 0 +#define WM8753_PLL2 1 + +/* clock inputs */ +#define WM8753_MCLK 0 +#define WM8753_PCMCLK 1 + +/* clock divider id's */ +#define WM8753_PCMDIV 0 +#define WM8753_BCLKDIV 1 +#define WM8753_VXCLKDIV 2 + +/* PCM clock dividers */ +#define WM8753_PCM_DIV_1 (0 << 6) +#define WM8753_PCM_DIV_3 (2 << 6) +#define WM8753_PCM_DIV_5_5 (3 << 6) +#define WM8753_PCM_DIV_2 (4 << 6) +#define WM8753_PCM_DIV_4 (5 << 6) +#define WM8753_PCM_DIV_6 (6 << 6) +#define WM8753_PCM_DIV_8 (7 << 6) + +/* BCLK clock dividers */ +#define WM8753_BCLK_DIV_1 (0 << 3) +#define WM8753_BCLK_DIV_2 (1 << 3) +#define WM8753_BCLK_DIV_4 (2 << 3) +#define WM8753_BCLK_DIV_8 (3 << 3) +#define WM8753_BCLK_DIV_16 (4 << 3) + +/* VXCLK clock dividers */ +#define WM8753_VXCLK_DIV_1 (0 << 6) +#define WM8753_VXCLK_DIV_2 (1 << 6) +#define WM8753_VXCLK_DIV_4 (2 << 6) +#define WM8753_VXCLK_DIV_8 (3 << 6) +#define WM8753_VXCLK_DIV_16 (4 << 6) + +#endif diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c new file mode 100644 index 0000000000..2469f4f3be --- /dev/null +++ b/sound/soc/codecs/wm8770.c @@ -0,0 +1,700 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8770.c -- WM8770 ALSA SoC Audio driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8770.h" + +#define WM8770_NUM_SUPPLIES 3 +static const char *wm8770_supply_names[WM8770_NUM_SUPPLIES] = { + "AVDD1", + "AVDD2", + "DVDD" +}; + +static const struct reg_default wm8770_reg_defaults[] = { + { 0, 0x7f }, + { 1, 0x7f }, + { 2, 0x7f }, + { 3, 0x7f }, + { 4, 0x7f }, + { 5, 0x7f }, + { 6, 0x7f }, + { 7, 0x7f }, + { 8, 0x7f }, + { 9, 0xff }, + { 10, 0xff }, + { 11, 0xff }, + { 12, 0xff }, + { 13, 0xff }, + { 14, 0xff }, + { 15, 0xff }, + { 16, 0xff }, + { 17, 0xff }, + { 18, 0 }, + { 19, 0x90 }, + { 20, 0 }, + { 21, 0 }, + { 22, 0x22 }, + { 23, 0x22 }, + { 24, 0x3e }, + { 25, 0xc }, + { 26, 0xc }, + { 27, 0x100 }, + { 28, 0x189 }, + { 29, 0x189 }, + { 30, 0x8770 }, +}; + +static bool wm8770_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8770_RESET: + return true; + default: + return false; + } +} + +struct wm8770_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8770_NUM_SUPPLIES]; + struct notifier_block disable_nb[WM8770_NUM_SUPPLIES]; + struct snd_soc_component *component; + int sysclk; +}; + +static int vout12supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); +static int vout34supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +/* + * We can't use the same notifier block for more than one supply and + * there's no way I can see to get from a callback to the caller + * except container_of(). + */ +#define WM8770_REGULATOR_EVENT(n) \ +static int wm8770_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct wm8770_priv *wm8770 = container_of(nb, struct wm8770_priv, \ + disable_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + regcache_mark_dirty(wm8770->regmap); \ + } \ + return 0; \ +} + +WM8770_REGULATOR_EVENT(0) +WM8770_REGULATOR_EVENT(1) +WM8770_REGULATOR_EVENT(2) + +static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(dac_dig_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(dac_alg_tlv, -12700, 100, 1); + +static const char *dac_phase_text[][2] = { + { "DAC1 Normal", "DAC1 Inverted" }, + { "DAC2 Normal", "DAC2 Inverted" }, + { "DAC3 Normal", "DAC3 Inverted" }, + { "DAC4 Normal", "DAC4 Inverted" }, +}; + +static const struct soc_enum dac_phase[] = { + SOC_ENUM_DOUBLE(WM8770_DACPHASE, 0, 1, 2, dac_phase_text[0]), + SOC_ENUM_DOUBLE(WM8770_DACPHASE, 2, 3, 2, dac_phase_text[1]), + SOC_ENUM_DOUBLE(WM8770_DACPHASE, 4, 5, 2, dac_phase_text[2]), + SOC_ENUM_DOUBLE(WM8770_DACPHASE, 6, 7, 2, dac_phase_text[3]), +}; + +static const struct snd_kcontrol_new wm8770_snd_controls[] = { + /* global DAC playback controls */ + SOC_SINGLE_TLV("DAC Playback Volume", WM8770_MSDIGVOL, 0, 255, 0, + dac_dig_tlv), + SOC_SINGLE("DAC Playback Switch", WM8770_DACMUTE, 4, 1, 1), + SOC_SINGLE("DAC Playback ZC Switch", WM8770_DACCTRL1, 0, 1, 0), + + /* global VOUT playback controls */ + SOC_SINGLE_TLV("VOUT Playback Volume", WM8770_MSALGVOL, 0, 127, 0, + dac_alg_tlv), + SOC_SINGLE("VOUT Playback ZC Switch", WM8770_MSALGVOL, 7, 1, 0), + + /* VOUT1/2/3/4 specific controls */ + SOC_DOUBLE_R_TLV("VOUT1 Playback Volume", WM8770_VOUT1LVOL, + WM8770_VOUT1RVOL, 0, 127, 0, dac_alg_tlv), + SOC_DOUBLE_R("VOUT1 Playback ZC Switch", WM8770_VOUT1LVOL, + WM8770_VOUT1RVOL, 7, 1, 0), + SOC_DOUBLE_R_TLV("VOUT2 Playback Volume", WM8770_VOUT2LVOL, + WM8770_VOUT2RVOL, 0, 127, 0, dac_alg_tlv), + SOC_DOUBLE_R("VOUT2 Playback ZC Switch", WM8770_VOUT2LVOL, + WM8770_VOUT2RVOL, 7, 1, 0), + SOC_DOUBLE_R_TLV("VOUT3 Playback Volume", WM8770_VOUT3LVOL, + WM8770_VOUT3RVOL, 0, 127, 0, dac_alg_tlv), + SOC_DOUBLE_R("VOUT3 Playback ZC Switch", WM8770_VOUT3LVOL, + WM8770_VOUT3RVOL, 7, 1, 0), + SOC_DOUBLE_R_TLV("VOUT4 Playback Volume", WM8770_VOUT4LVOL, + WM8770_VOUT4RVOL, 0, 127, 0, dac_alg_tlv), + SOC_DOUBLE_R("VOUT4 Playback ZC Switch", WM8770_VOUT4LVOL, + WM8770_VOUT4RVOL, 7, 1, 0), + + /* DAC1/2/3/4 specific controls */ + SOC_DOUBLE_R_TLV("DAC1 Playback Volume", WM8770_DAC1LVOL, + WM8770_DAC1RVOL, 0, 255, 0, dac_dig_tlv), + SOC_SINGLE("DAC1 Deemphasis Switch", WM8770_DACCTRL2, 0, 1, 0), + SOC_ENUM("DAC1 Phase", dac_phase[0]), + SOC_DOUBLE_R_TLV("DAC2 Playback Volume", WM8770_DAC2LVOL, + WM8770_DAC2RVOL, 0, 255, 0, dac_dig_tlv), + SOC_SINGLE("DAC2 Deemphasis Switch", WM8770_DACCTRL2, 1, 1, 0), + SOC_ENUM("DAC2 Phase", dac_phase[1]), + SOC_DOUBLE_R_TLV("DAC3 Playback Volume", WM8770_DAC3LVOL, + WM8770_DAC3RVOL, 0, 255, 0, dac_dig_tlv), + SOC_SINGLE("DAC3 Deemphasis Switch", WM8770_DACCTRL2, 2, 1, 0), + SOC_ENUM("DAC3 Phase", dac_phase[2]), + SOC_DOUBLE_R_TLV("DAC4 Playback Volume", WM8770_DAC4LVOL, + WM8770_DAC4RVOL, 0, 255, 0, dac_dig_tlv), + SOC_SINGLE("DAC4 Deemphasis Switch", WM8770_DACCTRL2, 3, 1, 0), + SOC_ENUM("DAC4 Phase", dac_phase[3]), + + /* ADC specific controls */ + SOC_DOUBLE_R_TLV("Capture Volume", WM8770_ADCLCTRL, WM8770_ADCRCTRL, + 0, 31, 0, adc_tlv), + SOC_DOUBLE_R("Capture Switch", WM8770_ADCLCTRL, WM8770_ADCRCTRL, + 5, 1, 1), + + /* other controls */ + SOC_SINGLE("ADC 128x Oversampling Switch", WM8770_MSTRCTRL, 3, 1, 0), + SOC_SINGLE("ADC Highpass Filter Switch", WM8770_IFACECTRL, 8, 1, 1) +}; + +static const char *ain_text[] = { + "AIN1", "AIN2", "AIN3", "AIN4", + "AIN5", "AIN6", "AIN7", "AIN8" +}; + +static SOC_ENUM_DOUBLE_DECL(ain_enum, + WM8770_ADCMUX, 0, 4, ain_text); + +static const struct snd_kcontrol_new ain_mux = + SOC_DAPM_ENUM("Capture Mux", ain_enum); + +static const struct snd_kcontrol_new vout1_mix_controls[] = { + SOC_DAPM_SINGLE("DAC1 Switch", WM8770_OUTMUX1, 0, 1, 0), + SOC_DAPM_SINGLE("AUX1 Switch", WM8770_OUTMUX1, 1, 1, 0), + SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX1, 2, 1, 0) +}; + +static const struct snd_kcontrol_new vout2_mix_controls[] = { + SOC_DAPM_SINGLE("DAC2 Switch", WM8770_OUTMUX1, 3, 1, 0), + SOC_DAPM_SINGLE("AUX2 Switch", WM8770_OUTMUX1, 4, 1, 0), + SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX1, 5, 1, 0) +}; + +static const struct snd_kcontrol_new vout3_mix_controls[] = { + SOC_DAPM_SINGLE("DAC3 Switch", WM8770_OUTMUX2, 0, 1, 0), + SOC_DAPM_SINGLE("AUX3 Switch", WM8770_OUTMUX2, 1, 1, 0), + SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX2, 2, 1, 0) +}; + +static const struct snd_kcontrol_new vout4_mix_controls[] = { + SOC_DAPM_SINGLE("DAC4 Switch", WM8770_OUTMUX2, 3, 1, 0), + SOC_DAPM_SINGLE("Bypass Switch", WM8770_OUTMUX2, 4, 1, 0) +}; + +static const struct snd_soc_dapm_widget wm8770_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("AUX1"), + SND_SOC_DAPM_INPUT("AUX2"), + SND_SOC_DAPM_INPUT("AUX3"), + + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("AIN4"), + SND_SOC_DAPM_INPUT("AIN5"), + SND_SOC_DAPM_INPUT("AIN6"), + SND_SOC_DAPM_INPUT("AIN7"), + SND_SOC_DAPM_INPUT("AIN8"), + + SND_SOC_DAPM_MUX("Capture Mux", WM8770_ADCMUX, 8, 1, &ain_mux), + + SND_SOC_DAPM_ADC("ADC", "Capture", WM8770_PWDNCTRL, 1, 1), + + SND_SOC_DAPM_DAC("DAC1", "Playback", WM8770_PWDNCTRL, 2, 1), + SND_SOC_DAPM_DAC("DAC2", "Playback", WM8770_PWDNCTRL, 3, 1), + SND_SOC_DAPM_DAC("DAC3", "Playback", WM8770_PWDNCTRL, 4, 1), + SND_SOC_DAPM_DAC("DAC4", "Playback", WM8770_PWDNCTRL, 5, 1), + + SND_SOC_DAPM_SUPPLY("VOUT12 Supply", SND_SOC_NOPM, 0, 0, + vout12supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VOUT34 Supply", SND_SOC_NOPM, 0, 0, + vout34supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("VOUT1 Mixer", SND_SOC_NOPM, 0, 0, + vout1_mix_controls, ARRAY_SIZE(vout1_mix_controls)), + SND_SOC_DAPM_MIXER("VOUT2 Mixer", SND_SOC_NOPM, 0, 0, + vout2_mix_controls, ARRAY_SIZE(vout2_mix_controls)), + SND_SOC_DAPM_MIXER("VOUT3 Mixer", SND_SOC_NOPM, 0, 0, + vout3_mix_controls, ARRAY_SIZE(vout3_mix_controls)), + SND_SOC_DAPM_MIXER("VOUT4 Mixer", SND_SOC_NOPM, 0, 0, + vout4_mix_controls, ARRAY_SIZE(vout4_mix_controls)), + + SND_SOC_DAPM_OUTPUT("VOUT1"), + SND_SOC_DAPM_OUTPUT("VOUT2"), + SND_SOC_DAPM_OUTPUT("VOUT3"), + SND_SOC_DAPM_OUTPUT("VOUT4") +}; + +static const struct snd_soc_dapm_route wm8770_intercon[] = { + { "Capture Mux", "AIN1", "AIN1" }, + { "Capture Mux", "AIN2", "AIN2" }, + { "Capture Mux", "AIN3", "AIN3" }, + { "Capture Mux", "AIN4", "AIN4" }, + { "Capture Mux", "AIN5", "AIN5" }, + { "Capture Mux", "AIN6", "AIN6" }, + { "Capture Mux", "AIN7", "AIN7" }, + { "Capture Mux", "AIN8", "AIN8" }, + + { "ADC", NULL, "Capture Mux" }, + + { "VOUT1 Mixer", NULL, "VOUT12 Supply" }, + { "VOUT1 Mixer", "DAC1 Switch", "DAC1" }, + { "VOUT1 Mixer", "AUX1 Switch", "AUX1" }, + { "VOUT1 Mixer", "Bypass Switch", "Capture Mux" }, + + { "VOUT2 Mixer", NULL, "VOUT12 Supply" }, + { "VOUT2 Mixer", "DAC2 Switch", "DAC2" }, + { "VOUT2 Mixer", "AUX2 Switch", "AUX2" }, + { "VOUT2 Mixer", "Bypass Switch", "Capture Mux" }, + + { "VOUT3 Mixer", NULL, "VOUT34 Supply" }, + { "VOUT3 Mixer", "DAC3 Switch", "DAC3" }, + { "VOUT3 Mixer", "AUX3 Switch", "AUX3" }, + { "VOUT3 Mixer", "Bypass Switch", "Capture Mux" }, + + { "VOUT4 Mixer", NULL, "VOUT34 Supply" }, + { "VOUT4 Mixer", "DAC4 Switch", "DAC4" }, + { "VOUT4 Mixer", "Bypass Switch", "Capture Mux" }, + + { "VOUT1", NULL, "VOUT1 Mixer" }, + { "VOUT2", NULL, "VOUT2 Mixer" }, + { "VOUT3", NULL, "VOUT3 Mixer" }, + { "VOUT4", NULL, "VOUT4 Mixer" } +}; + +static int vout12supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, WM8770_OUTMUX1, 0x180, 0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, WM8770_OUTMUX1, 0x180, 0x180); + break; + } + + return 0; +} + +static int vout34supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, WM8770_OUTMUX2, 0x180, 0); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, WM8770_OUTMUX2, 0x180, 0x180); + break; + } + + return 0; +} + +static int wm8770_reset(struct snd_soc_component *component) +{ + return snd_soc_component_write(component, WM8770_RESET, 0); +} + +static int wm8770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component; + int iface, master; + + component = dai->component; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + master = 0x100; + break; + case SND_SOC_DAIFMT_CBS_CFS: + master = 0; + break; + default: + return -EINVAL; + } + + iface = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0xc; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x8; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x4; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8770_IFACECTRL, 0xf, iface); + snd_soc_component_update_bits(component, WM8770_MSTRCTRL, 0x100, master); + + return 0; +} + +static const int mclk_ratios[] = { + 128, + 192, + 256, + 384, + 512, + 768 +}; + +static int wm8770_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component; + struct wm8770_priv *wm8770; + int i; + int iface; + int shift; + int ratio; + + component = dai->component; + wm8770 = snd_soc_component_get_drvdata(component); + + iface = 0; + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x10; + break; + case 24: + iface |= 0x20; + break; + case 32: + iface |= 0x30; + break; + } + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + i = 0; + shift = 4; + break; + case SNDRV_PCM_STREAM_CAPTURE: + i = 2; + shift = 0; + break; + default: + return -EINVAL; + } + + /* Only need to set MCLK/LRCLK ratio if we're master */ + if (snd_soc_component_read(component, WM8770_MSTRCTRL) & 0x100) { + for (; i < ARRAY_SIZE(mclk_ratios); ++i) { + ratio = wm8770->sysclk / params_rate(params); + if (ratio == mclk_ratios[i]) + break; + } + + if (i == ARRAY_SIZE(mclk_ratios)) { + dev_err(component->dev, + "Unable to configure MCLK ratio %d/%d\n", + wm8770->sysclk, params_rate(params)); + return -EINVAL; + } + + dev_dbg(component->dev, "MCLK is %dfs\n", mclk_ratios[i]); + + snd_soc_component_update_bits(component, WM8770_MSTRCTRL, 0x7 << shift, + i << shift); + } + + snd_soc_component_update_bits(component, WM8770_IFACECTRL, 0x30, iface); + + return 0; +} + +static int wm8770_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component; + + component = dai->component; + return snd_soc_component_update_bits(component, WM8770_DACMUTE, 0x10, + !!mute << 4); +} + +static int wm8770_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component; + struct wm8770_priv *wm8770; + + component = dai->component; + wm8770 = snd_soc_component_get_drvdata(component); + wm8770->sysclk = freq; + return 0; +} + +static int wm8770_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int ret; + struct wm8770_priv *wm8770; + + wm8770 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies), + wm8770->supplies); + if (ret) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + + regcache_sync(wm8770->regmap); + + /* global powerup */ + snd_soc_component_write(component, WM8770_PWDNCTRL, 0); + } + break; + case SND_SOC_BIAS_OFF: + /* global powerdown */ + snd_soc_component_write(component, WM8770_PWDNCTRL, 1); + regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), + wm8770->supplies); + break; + } + + return 0; +} + +#define WM8770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8770_dai_ops = { + .mute_stream = wm8770_mute, + .hw_params = wm8770_hw_params, + .set_fmt = wm8770_set_fmt, + .set_sysclk = wm8770_set_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8770_dai = { + .name = "wm8770-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = WM8770_FORMATS + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8770_FORMATS + }, + .ops = &wm8770_dai_ops, + .symmetric_rate = 1 +}; + +static int wm8770_probe(struct snd_soc_component *component) +{ + struct wm8770_priv *wm8770; + int ret; + + wm8770 = snd_soc_component_get_drvdata(component); + wm8770->component = component; + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8770->supplies), + wm8770->supplies); + if (ret) { + dev_err(component->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = wm8770_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset: %d\n", ret); + goto err_reg_enable; + } + + /* latch the volume update bits */ + snd_soc_component_update_bits(component, WM8770_MSDIGVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8770_MSALGVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8770_VOUT1RVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8770_VOUT2RVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8770_VOUT3RVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8770_VOUT4RVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8770_DAC1RVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8770_DAC2RVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8770_DAC3RVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8770_DAC4RVOL, 0x100, 0x100); + + /* mute all DACs */ + snd_soc_component_update_bits(component, WM8770_DACMUTE, 0x10, 0x10); + +err_reg_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8770->supplies), wm8770->supplies); + return ret; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8770 = { + .probe = wm8770_probe, + .set_bias_level = wm8770_set_bias_level, + .controls = wm8770_snd_controls, + .num_controls = ARRAY_SIZE(wm8770_snd_controls), + .dapm_widgets = wm8770_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8770_dapm_widgets), + .dapm_routes = wm8770_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8770_intercon), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8770_of_match[] = { + { .compatible = "wlf,wm8770", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8770_of_match); + +static const struct regmap_config wm8770_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8770_RESET, + + .reg_defaults = wm8770_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8770_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm8770_volatile_reg, +}; + +static int wm8770_spi_probe(struct spi_device *spi) +{ + struct wm8770_priv *wm8770; + int ret, i; + + wm8770 = devm_kzalloc(&spi->dev, sizeof(struct wm8770_priv), + GFP_KERNEL); + if (!wm8770) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) + wm8770->supplies[i].supply = wm8770_supply_names[i]; + + ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8770->supplies), + wm8770->supplies); + if (ret) { + dev_err(&spi->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8770->disable_nb[0].notifier_call = wm8770_regulator_event_0; + wm8770->disable_nb[1].notifier_call = wm8770_regulator_event_1; + wm8770->disable_nb[2].notifier_call = wm8770_regulator_event_2; + + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8770->supplies); i++) { + ret = devm_regulator_register_notifier( + wm8770->supplies[i].consumer, + &wm8770->disable_nb[i]); + if (ret) { + dev_err(&spi->dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + + wm8770->regmap = devm_regmap_init_spi(spi, &wm8770_regmap); + if (IS_ERR(wm8770->regmap)) + return PTR_ERR(wm8770->regmap); + + spi_set_drvdata(spi, wm8770); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8770, &wm8770_dai, 1); + + return ret; +} + +static struct spi_driver wm8770_spi_driver = { + .driver = { + .name = "wm8770", + .of_match_table = wm8770_of_match, + }, + .probe = wm8770_spi_probe, +}; + +module_spi_driver(wm8770_spi_driver); + +MODULE_DESCRIPTION("ASoC WM8770 driver"); +MODULE_AUTHOR("Dimitris Papastamos "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8770.h b/sound/soc/codecs/wm8770.h new file mode 100644 index 0000000000..e0a3f5a146 --- /dev/null +++ b/sound/soc/codecs/wm8770.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8770.h -- WM8770 ASoC driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos + */ + +#ifndef _WM8770_H +#define _WM8770_H + +/* Registers */ +#define WM8770_VOUT1LVOL 0 +#define WM8770_VOUT1RVOL 0x1 +#define WM8770_VOUT2LVOL 0x2 +#define WM8770_VOUT2RVOL 0x3 +#define WM8770_VOUT3LVOL 0x4 +#define WM8770_VOUT3RVOL 0x5 +#define WM8770_VOUT4LVOL 0x6 +#define WM8770_VOUT4RVOL 0x7 +#define WM8770_MSALGVOL 0x8 +#define WM8770_DAC1LVOL 0x9 +#define WM8770_DAC1RVOL 0xa +#define WM8770_DAC2LVOL 0xb +#define WM8770_DAC2RVOL 0xc +#define WM8770_DAC3LVOL 0xd +#define WM8770_DAC3RVOL 0xe +#define WM8770_DAC4LVOL 0xf +#define WM8770_DAC4RVOL 0x10 +#define WM8770_MSDIGVOL 0x11 +#define WM8770_DACPHASE 0x12 +#define WM8770_DACCTRL1 0x13 +#define WM8770_DACMUTE 0x14 +#define WM8770_DACCTRL2 0x15 +#define WM8770_IFACECTRL 0x16 +#define WM8770_MSTRCTRL 0x17 +#define WM8770_PWDNCTRL 0x18 +#define WM8770_ADCLCTRL 0x19 +#define WM8770_ADCRCTRL 0x1a +#define WM8770_ADCMUX 0x1b +#define WM8770_OUTMUX1 0x1c +#define WM8770_OUTMUX2 0x1d +#define WM8770_RESET 0x31 + +#define WM8770_CACHEREGNUM 0x20 + +#endif diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c new file mode 100644 index 0000000000..0673bbd32b --- /dev/null +++ b/sound/soc/codecs/wm8776.c @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8776.c -- WM8776 ALSA SoC Audio driver + * + * Copyright 2009-12 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * TODO: Input ALC/limiter support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8776.h" + +enum wm8776_chip_type { + WM8775 = 1, + WM8776, +}; + +/* codec private data */ +struct wm8776_priv { + struct regmap *regmap; + int sysclk[2]; +}; + +static const struct reg_default wm8776_reg_defaults[] = { + { 0, 0x79 }, + { 1, 0x79 }, + { 2, 0x79 }, + { 3, 0xff }, + { 4, 0xff }, + { 5, 0xff }, + { 6, 0x00 }, + { 7, 0x90 }, + { 8, 0x00 }, + { 9, 0x00 }, + { 10, 0x22 }, + { 11, 0x22 }, + { 12, 0x22 }, + { 13, 0x08 }, + { 14, 0xcf }, + { 15, 0xcf }, + { 16, 0x7b }, + { 17, 0x00 }, + { 18, 0x32 }, + { 19, 0x00 }, + { 20, 0xa6 }, + { 21, 0x01 }, + { 22, 0x01 }, +}; + +static bool wm8776_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8776_RESET: + return true; + default: + return false; + } +} + +static int wm8776_reset(struct snd_soc_component *component) +{ + return snd_soc_component_write(component, WM8776_RESET, 0); +} + +static const DECLARE_TLV_DB_SCALE(hp_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1); + +static const struct snd_kcontrol_new wm8776_snd_controls[] = { +SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8776_HPLVOL, WM8776_HPRVOL, + 0, 127, 0, hp_tlv), +SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8776_DACLVOL, WM8776_DACRVOL, + 0, 255, 0, dac_tlv), +SOC_SINGLE("Digital Playback ZC Switch", WM8776_DACCTRL1, 0, 1, 0), + +SOC_SINGLE("Deemphasis Switch", WM8776_DACCTRL2, 0, 1, 0), + +SOC_DOUBLE_R_TLV("Capture Volume", WM8776_ADCLVOL, WM8776_ADCRVOL, + 0, 255, 0, adc_tlv), +SOC_DOUBLE("Capture Switch", WM8776_ADCMUX, 7, 6, 1, 1), +SOC_DOUBLE_R("Capture ZC Switch", WM8776_ADCLVOL, WM8776_ADCRVOL, 8, 1, 0), +SOC_SINGLE("Capture HPF Switch", WM8776_ADCIFCTRL, 8, 1, 1), +}; + +static const struct snd_kcontrol_new inmix_controls[] = { +SOC_DAPM_SINGLE("AIN1 Switch", WM8776_ADCMUX, 0, 1, 0), +SOC_DAPM_SINGLE("AIN2 Switch", WM8776_ADCMUX, 1, 1, 0), +SOC_DAPM_SINGLE("AIN3 Switch", WM8776_ADCMUX, 2, 1, 0), +SOC_DAPM_SINGLE("AIN4 Switch", WM8776_ADCMUX, 3, 1, 0), +SOC_DAPM_SINGLE("AIN5 Switch", WM8776_ADCMUX, 4, 1, 0), +}; + +static const struct snd_kcontrol_new outmix_controls[] = { +SOC_DAPM_SINGLE("DAC Switch", WM8776_OUTMUX, 0, 1, 0), +SOC_DAPM_SINGLE("AUX Switch", WM8776_OUTMUX, 1, 1, 0), +SOC_DAPM_SINGLE("Bypass Switch", WM8776_OUTMUX, 2, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8776_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AUX"), + +SND_SOC_DAPM_INPUT("AIN1"), +SND_SOC_DAPM_INPUT("AIN2"), +SND_SOC_DAPM_INPUT("AIN3"), +SND_SOC_DAPM_INPUT("AIN4"), +SND_SOC_DAPM_INPUT("AIN5"), + +SND_SOC_DAPM_MIXER("Input Mixer", WM8776_PWRDOWN, 6, 1, + inmix_controls, ARRAY_SIZE(inmix_controls)), + +SND_SOC_DAPM_ADC("ADC", "Capture", WM8776_PWRDOWN, 1, 1), +SND_SOC_DAPM_DAC("DAC", "Playback", WM8776_PWRDOWN, 2, 1), + +SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, + outmix_controls, ARRAY_SIZE(outmix_controls)), + +SND_SOC_DAPM_PGA("Headphone PGA", WM8776_PWRDOWN, 3, 1, NULL, 0), + +SND_SOC_DAPM_OUTPUT("VOUT"), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +}; + +static const struct snd_soc_dapm_route routes[] = { + { "Input Mixer", "AIN1 Switch", "AIN1" }, + { "Input Mixer", "AIN2 Switch", "AIN2" }, + { "Input Mixer", "AIN3 Switch", "AIN3" }, + { "Input Mixer", "AIN4 Switch", "AIN4" }, + { "Input Mixer", "AIN5 Switch", "AIN5" }, + + { "ADC", NULL, "Input Mixer" }, + + { "Output Mixer", "DAC Switch", "DAC" }, + { "Output Mixer", "AUX Switch", "AUX" }, + { "Output Mixer", "Bypass Switch", "Input Mixer" }, + + { "VOUT", NULL, "Output Mixer" }, + + { "Headphone PGA", NULL, "Output Mixer" }, + + { "HPOUTL", NULL, "Headphone PGA" }, + { "HPOUTR", NULL, "Headphone PGA" }, +}; + +static int wm8776_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + int reg, iface, master; + + switch (dai->driver->id) { + case WM8776_DAI_DAC: + reg = WM8776_DACIFCTRL; + master = 0x80; + break; + case WM8776_DAI_ADC: + reg = WM8776_ADCIFCTRL; + master = 0x100; + break; + default: + return -EINVAL; + } + + iface = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + case SND_SOC_DAIFMT_CBS_CFS: + master = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x00c; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x008; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x004; + break; + default: + return -EINVAL; + } + + /* Finally, write out the values */ + snd_soc_component_update_bits(component, reg, 0xf, iface); + snd_soc_component_update_bits(component, WM8776_MSTRCTRL, 0x180, master); + + return 0; +} + +static int mclk_ratios[] = { + 128, + 192, + 256, + 384, + 512, + 768, +}; + +static int wm8776_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component); + int iface_reg, iface; + int ratio_shift, master; + int i; + + switch (dai->driver->id) { + case WM8776_DAI_DAC: + iface_reg = WM8776_DACIFCTRL; + master = 0x80; + ratio_shift = 4; + break; + case WM8776_DAI_ADC: + iface_reg = WM8776_ADCIFCTRL; + master = 0x100; + ratio_shift = 0; + break; + default: + return -EINVAL; + } + + /* Set word length */ + switch (params_width(params)) { + case 16: + iface = 0; + break; + case 20: + iface = 0x10; + break; + case 24: + iface = 0x20; + break; + case 32: + iface = 0x30; + break; + default: + dev_err(component->dev, "Unsupported sample size: %i\n", + params_width(params)); + return -EINVAL; + } + + /* Only need to set MCLK/LRCLK ratio if we're master */ + if (snd_soc_component_read(component, WM8776_MSTRCTRL) & master) { + for (i = 0; i < ARRAY_SIZE(mclk_ratios); i++) { + if (wm8776->sysclk[dai->driver->id] / params_rate(params) + == mclk_ratios[i]) + break; + } + + if (i == ARRAY_SIZE(mclk_ratios)) { + dev_err(component->dev, + "Unable to configure MCLK ratio %d/%d\n", + wm8776->sysclk[dai->driver->id], params_rate(params)); + return -EINVAL; + } + + dev_dbg(component->dev, "MCLK is %dfs\n", mclk_ratios[i]); + + snd_soc_component_update_bits(component, WM8776_MSTRCTRL, + 0x7 << ratio_shift, i << ratio_shift); + } else { + dev_dbg(component->dev, "DAI in slave mode\n"); + } + + snd_soc_component_update_bits(component, iface_reg, 0x30, iface); + + return 0; +} + +static int wm8776_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + return snd_soc_component_write(component, WM8776_DACMUTE, !!mute); +} + +static int wm8776_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component); + + if (WARN_ON(dai->driver->id >= ARRAY_SIZE(wm8776->sysclk))) + return -EINVAL; + + wm8776->sysclk[dai->driver->id] = freq; + + return 0; +} + +static int wm8776_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8776_priv *wm8776 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + regcache_sync(wm8776->regmap); + + /* Disable the global powerdown; DAPM does the rest */ + snd_soc_component_update_bits(component, WM8776_PWRDOWN, 1, 0); + } + + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, WM8776_PWRDOWN, 1, 1); + break; + } + + return 0; +} + +#define WM8776_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8776_dac_ops = { + .mute_stream = wm8776_mute, + .hw_params = wm8776_hw_params, + .set_fmt = wm8776_set_fmt, + .set_sysclk = wm8776_set_sysclk, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops wm8776_adc_ops = { + .hw_params = wm8776_hw_params, + .set_fmt = wm8776_set_fmt, + .set_sysclk = wm8776_set_sysclk, +}; + +static struct snd_soc_dai_driver wm8776_dai[] = { + { + .name = "wm8776-hifi-playback", + .id = WM8776_DAI_DAC, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 32000, + .rate_max = 192000, + .formats = WM8776_FORMATS, + }, + .ops = &wm8776_dac_ops, + }, + { + .name = "wm8776-hifi-capture", + .id = WM8776_DAI_ADC, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 32000, + .rate_max = 96000, + .formats = WM8776_FORMATS, + }, + .ops = &wm8776_adc_ops, + }, +}; + +static int wm8776_probe(struct snd_soc_component *component) +{ + int ret = 0; + + ret = wm8776_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + /* Latch the update bits; right channel only since we always + * update both. */ + snd_soc_component_update_bits(component, WM8776_HPRVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8776_DACRVOL, 0x100, 0x100); + + return ret; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8776 = { + .probe = wm8776_probe, + .set_bias_level = wm8776_set_bias_level, + .controls = wm8776_snd_controls, + .num_controls = ARRAY_SIZE(wm8776_snd_controls), + .dapm_widgets = wm8776_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8776_dapm_widgets), + .dapm_routes = routes, + .num_dapm_routes = ARRAY_SIZE(routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct of_device_id wm8776_of_match[] = { + { .compatible = "wlf,wm8776", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8776_of_match); + +static const struct regmap_config wm8776_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8776_RESET, + + .reg_defaults = wm8776_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8776_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm8776_volatile, +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8776_spi_probe(struct spi_device *spi) +{ + struct wm8776_priv *wm8776; + int ret; + + wm8776 = devm_kzalloc(&spi->dev, sizeof(struct wm8776_priv), + GFP_KERNEL); + if (wm8776 == NULL) + return -ENOMEM; + + wm8776->regmap = devm_regmap_init_spi(spi, &wm8776_regmap); + if (IS_ERR(wm8776->regmap)) + return PTR_ERR(wm8776->regmap); + + spi_set_drvdata(spi, wm8776); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai)); + + return ret; +} + +static struct spi_driver wm8776_spi_driver = { + .driver = { + .name = "wm8776", + .of_match_table = wm8776_of_match, + }, + .probe = wm8776_spi_probe, +}; +#endif /* CONFIG_SPI_MASTER */ + +#if IS_ENABLED(CONFIG_I2C) +static int wm8776_i2c_probe(struct i2c_client *i2c) +{ + struct wm8776_priv *wm8776; + int ret; + + wm8776 = devm_kzalloc(&i2c->dev, sizeof(struct wm8776_priv), + GFP_KERNEL); + if (wm8776 == NULL) + return -ENOMEM; + + wm8776->regmap = devm_regmap_init_i2c(i2c, &wm8776_regmap); + if (IS_ERR(wm8776->regmap)) + return PTR_ERR(wm8776->regmap); + + i2c_set_clientdata(i2c, wm8776); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8776, wm8776_dai, ARRAY_SIZE(wm8776_dai)); + + return ret; +} + +static const struct i2c_device_id wm8776_i2c_id[] = { + { "wm8775", WM8775 }, + { "wm8776", WM8776 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id); + +static struct i2c_driver wm8776_i2c_driver = { + .driver = { + .name = "wm8776", + .of_match_table = wm8776_of_match, + }, + .probe = wm8776_i2c_probe, + .id_table = wm8776_i2c_id, +}; +#endif + +static int __init wm8776_modinit(void) +{ + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8776_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8776 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8776_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8776 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8776_modinit); + +static void __exit wm8776_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8776_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8776_spi_driver); +#endif +} +module_exit(wm8776_exit); + +MODULE_DESCRIPTION("ASoC WM8776 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8776.h b/sound/soc/codecs/wm8776.h new file mode 100644 index 0000000000..266a48a211 --- /dev/null +++ b/sound/soc/codecs/wm8776.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8776.h -- WM8776 ASoC driver + * + * Copyright 2009 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#ifndef _WM8776_H +#define _WM8776_H + +/* Registers */ + +#define WM8776_HPLVOL 0x00 +#define WM8776_HPRVOL 0x01 +#define WM8776_HPMASTER 0x02 +#define WM8776_DACLVOL 0x03 +#define WM8776_DACRVOL 0x04 +#define WM8776_DACMASTER 0x05 +#define WM8776_PHASESWAP 0x06 +#define WM8776_DACCTRL1 0x07 +#define WM8776_DACMUTE 0x08 +#define WM8776_DACCTRL2 0x09 +#define WM8776_DACIFCTRL 0x0a +#define WM8776_ADCIFCTRL 0x0b +#define WM8776_MSTRCTRL 0x0c +#define WM8776_PWRDOWN 0x0d +#define WM8776_ADCLVOL 0x0e +#define WM8776_ADCRVOL 0x0f +#define WM8776_ALCCTRL1 0x10 +#define WM8776_ALCCTRL2 0x11 +#define WM8776_ALCCTRL3 0x12 +#define WM8776_NOISEGATE 0x13 +#define WM8776_LIMITER 0x14 +#define WM8776_ADCMUX 0x15 +#define WM8776_OUTMUX 0x16 +#define WM8776_RESET 0x17 + +#define WM8776_CACHEREGNUM 0x17 + +#define WM8776_DAI_DAC 0 +#define WM8776_DAI_ADC 1 + +#endif diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c new file mode 100644 index 0000000000..95ff4339d1 --- /dev/null +++ b/sound/soc/codecs/wm8782.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * sound/soc/codecs/wm8782.c + * simple, strap-pin configured 24bit 2ch ADC + * + * Copyright: 2011 Raumfeld GmbH + * Author: Johannes Stezenbach + * + * based on ad73311.c + * Copyright: Analog Devices Inc. + * Author: Cliff Cai + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct snd_soc_dapm_widget wm8782_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), +}; + +static const struct snd_soc_dapm_route wm8782_dapm_routes[] = { + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, +}; + +static struct snd_soc_dai_driver wm8782_dai = { + .name = "wm8782", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + /* For configurations with FSAMPEN=0 */ + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, +}; + +/* regulator power supply names */ +static const char *supply_names[] = { + "Vdda", /* analog supply, 2.7V - 3.6V */ + "Vdd", /* digital supply, 2.7V - 5.5V */ +}; + +struct wm8782_priv { + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; +}; + +static int wm8782_soc_probe(struct snd_soc_component *component) +{ + struct wm8782_priv *priv = snd_soc_component_get_drvdata(component); + return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); +} + +static void wm8782_soc_remove(struct snd_soc_component *component) +{ + struct wm8782_priv *priv = snd_soc_component_get_drvdata(component); + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); +} + +#ifdef CONFIG_PM +static int wm8782_soc_suspend(struct snd_soc_component *component) +{ + struct wm8782_priv *priv = snd_soc_component_get_drvdata(component); + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); + return 0; +} + +static int wm8782_soc_resume(struct snd_soc_component *component) +{ + struct wm8782_priv *priv = snd_soc_component_get_drvdata(component); + return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); +} +#else +#define wm8782_soc_suspend NULL +#define wm8782_soc_resume NULL +#endif /* CONFIG_PM */ + +static const struct snd_soc_component_driver soc_component_dev_wm8782 = { + .probe = wm8782_soc_probe, + .remove = wm8782_soc_remove, + .suspend = wm8782_soc_suspend, + .resume = wm8782_soc_resume, + .dapm_widgets = wm8782_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets), + .dapm_routes = wm8782_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm8782_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wm8782_priv *priv; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + priv->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret < 0) + return ret; + + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm8782, &wm8782_dai, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id wm8782_of_match[] = { + { .compatible = "wlf,wm8782", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8782_of_match); +#endif + +static struct platform_driver wm8782_codec_driver = { + .driver = { + .name = "wm8782", + .of_match_table = of_match_ptr(wm8782_of_match), + }, + .probe = wm8782_probe, +}; + +module_platform_driver(wm8782_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8782 driver"); +MODULE_AUTHOR("Johannes Stezenbach "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c new file mode 100644 index 0000000000..7062a8b2f8 --- /dev/null +++ b/sound/soc/codecs/wm8804-i2c.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8804-i2c.c -- WM8804 S/PDIF transceiver driver - I2C + * + * Copyright 2015 Cirrus Logic Inc + * + * Author: Charles Keepax + */ + +#include +#include +#include +#include + +#include "wm8804.h" + +static int wm8804_i2c_probe(struct i2c_client *i2c) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &wm8804_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return wm8804_probe(&i2c->dev, regmap); +} + +static void wm8804_i2c_remove(struct i2c_client *i2c) +{ + wm8804_remove(&i2c->dev); +} + +static const struct i2c_device_id wm8804_i2c_id[] = { + { "wm8804", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id wm8804_of_match[] = { + { .compatible = "wlf,wm8804", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8804_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id wm8804_acpi_match[] = { + { "1AEC8804", 0 }, /* Wolfson PCI ID + part ID */ + { "10138804", 0 }, /* Cirrus Logic PCI ID + part ID */ + { }, +}; +MODULE_DEVICE_TABLE(acpi, wm8804_acpi_match); +#endif + +static struct i2c_driver wm8804_i2c_driver = { + .driver = { + .name = "wm8804", + .pm = &wm8804_pm, + .of_match_table = of_match_ptr(wm8804_of_match), + .acpi_match_table = ACPI_PTR(wm8804_acpi_match), + }, + .probe = wm8804_i2c_probe, + .remove = wm8804_i2c_remove, + .id_table = wm8804_i2c_id +}; + +module_i2c_driver(wm8804_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8804 driver - I2C"); +MODULE_AUTHOR("Charles Keepax "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8804-spi.c b/sound/soc/codecs/wm8804-spi.c new file mode 100644 index 0000000000..628568724c --- /dev/null +++ b/sound/soc/codecs/wm8804-spi.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8804-spi.c -- WM8804 S/PDIF transceiver driver - SPI + * + * Copyright 2015 Cirrus Logic Inc + * + * Author: Charles Keepax + */ + +#include +#include +#include + +#include "wm8804.h" + +static int wm8804_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &wm8804_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return wm8804_probe(&spi->dev, regmap); +} + +static void wm8804_spi_remove(struct spi_device *spi) +{ + wm8804_remove(&spi->dev); +} + +static const struct of_device_id wm8804_of_match[] = { + { .compatible = "wlf,wm8804", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8804_of_match); + +static struct spi_driver wm8804_spi_driver = { + .driver = { + .name = "wm8804", + .pm = &wm8804_pm, + .of_match_table = wm8804_of_match, + }, + .probe = wm8804_spi_probe, + .remove = wm8804_spi_remove +}; + +module_spi_driver(wm8804_spi_driver); + +MODULE_DESCRIPTION("ASoC WM8804 driver - SPI"); +MODULE_AUTHOR("Charles Keepax "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c new file mode 100644 index 0000000000..bbb4b6e3b4 --- /dev/null +++ b/sound/soc/codecs/wm8804.c @@ -0,0 +1,726 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8804.c -- WM8804 S/PDIF transceiver driver + * + * Copyright 2010-11 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8804.h" + +#define WM8804_NUM_SUPPLIES 2 +static const char *wm8804_supply_names[WM8804_NUM_SUPPLIES] = { + "PVDD", + "DVDD" +}; + +static const struct reg_default wm8804_reg_defaults[] = { + { 3, 0x21 }, /* R3 - PLL1 */ + { 4, 0xFD }, /* R4 - PLL2 */ + { 5, 0x36 }, /* R5 - PLL3 */ + { 6, 0x07 }, /* R6 - PLL4 */ + { 7, 0x16 }, /* R7 - PLL5 */ + { 8, 0x18 }, /* R8 - PLL6 */ + { 9, 0xFF }, /* R9 - SPDMODE */ + { 10, 0x00 }, /* R10 - INTMASK */ + { 18, 0x00 }, /* R18 - SPDTX1 */ + { 19, 0x00 }, /* R19 - SPDTX2 */ + { 20, 0x00 }, /* R20 - SPDTX3 */ + { 21, 0x71 }, /* R21 - SPDTX4 */ + { 22, 0x0B }, /* R22 - SPDTX5 */ + { 23, 0x70 }, /* R23 - GPO0 */ + { 24, 0x57 }, /* R24 - GPO1 */ + { 26, 0x42 }, /* R26 - GPO2 */ + { 27, 0x06 }, /* R27 - AIFTX */ + { 28, 0x06 }, /* R28 - AIFRX */ + { 29, 0x80 }, /* R29 - SPDRX1 */ + { 30, 0x07 }, /* R30 - PWRDN */ +}; + +struct wm8804_priv { + struct device *dev; + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES]; + struct notifier_block disable_nb[WM8804_NUM_SUPPLIES]; + int mclk_div; + + struct gpio_desc *reset; + + int aif_pwr; +}; + +static int txsrc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static int wm8804_aif_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +/* + * We can't use the same notifier block for more than one supply and + * there's no way I can see to get from a callback to the caller + * except container_of(). + */ +#define WM8804_REGULATOR_EVENT(n) \ +static int wm8804_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct wm8804_priv *wm8804 = container_of(nb, struct wm8804_priv, \ + disable_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + regcache_mark_dirty(wm8804->regmap); \ + } \ + return 0; \ +} + +WM8804_REGULATOR_EVENT(0) +WM8804_REGULATOR_EVENT(1) + +static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; +static SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text); + +static const struct snd_kcontrol_new wm8804_tx_source_mux[] = { + SOC_DAPM_ENUM_EXT("Input Source", txsrc, + snd_soc_dapm_get_enum_double, txsrc_put), +}; + +static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("SPDIF Out"), +SND_SOC_DAPM_INPUT("SPDIF In"), + +SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0), +SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0), + +SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux), + +SND_SOC_DAPM_AIF_OUT_E("AIFTX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_AIF_IN_E("AIFRX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route wm8804_dapm_routes[] = { + { "AIFRX", NULL, "Playback" }, + { "Tx Source", "AIF", "AIFRX" }, + + { "SPDIFRX", NULL, "SPDIF In" }, + { "Tx Source", "S/PDIF RX", "SPDIFRX" }, + + { "SPDIFTX", NULL, "Tx Source" }, + { "SPDIF Out", NULL, "SPDIFTX" }, + + { "AIFTX", NULL, "SPDIFRX" }, + { "Capture", NULL, "AIFTX" }, +}; + +static int wm8804_aif_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8804_priv *wm8804 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* power up the aif */ + if (!wm8804->aif_pwr) + snd_soc_component_update_bits(component, WM8804_PWRDN, 0x10, 0x0); + wm8804->aif_pwr++; + break; + case SND_SOC_DAPM_POST_PMD: + /* power down only both paths are disabled */ + wm8804->aif_pwr--; + if (!wm8804->aif_pwr) + snd_soc_component_update_bits(component, WM8804_PWRDN, 0x10, 0x10); + break; + } + + return 0; +} + +static int txsrc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l; + unsigned int mask = 1 << e->shift_l; + unsigned int txpwr; + + if (val != 0 && val != mask) + return -EINVAL; + + snd_soc_dapm_mutex_lock(dapm); + + if (snd_soc_component_test_bits(component, e->reg, mask, val)) { + /* save the current power state of the transmitter */ + txpwr = snd_soc_component_read(component, WM8804_PWRDN) & 0x4; + + /* power down the transmitter */ + snd_soc_component_update_bits(component, WM8804_PWRDN, 0x4, 0x4); + + /* set the tx source */ + snd_soc_component_update_bits(component, e->reg, mask, val); + + /* restore the transmitter's configuration */ + snd_soc_component_update_bits(component, WM8804_PWRDN, 0x4, txpwr); + } + + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + +static bool wm8804_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8804_RST_DEVID1: + case WM8804_DEVID2: + case WM8804_DEVREV: + case WM8804_INTSTAT: + case WM8804_SPDSTAT: + case WM8804_RXCHAN1: + case WM8804_RXCHAN2: + case WM8804_RXCHAN3: + case WM8804_RXCHAN4: + case WM8804_RXCHAN5: + return true; + default: + return false; + } +} + +static int wm8804_soft_reset(struct wm8804_priv *wm8804) +{ + return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0); +} + +static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component; + u16 format, master, bcp, lrp; + + component = dai->component; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + format = 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = 0x1; + break; + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + format = 0x3; + break; + default: + dev_err(dai->dev, "Unknown dai format\n"); + return -EINVAL; + } + + /* set data format */ + snd_soc_component_update_bits(component, WM8804_AIFTX, 0x3, format); + snd_soc_component_update_bits(component, WM8804_AIFRX, 0x3, format); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + master = 0; + break; + default: + dev_err(dai->dev, "Unknown master/slave configuration\n"); + return -EINVAL; + } + + /* set master/slave mode */ + snd_soc_component_update_bits(component, WM8804_AIFRX, 0x40, master << 6); + + bcp = lrp = 0; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + bcp = lrp = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + bcp = 1; + break; + case SND_SOC_DAIFMT_NB_IF: + lrp = 1; + break; + default: + dev_err(dai->dev, "Unknown polarity configuration\n"); + return -EINVAL; + } + + /* set frame inversion */ + snd_soc_component_update_bits(component, WM8804_AIFTX, 0x10 | 0x20, + (bcp << 4) | (lrp << 5)); + snd_soc_component_update_bits(component, WM8804_AIFRX, 0x10 | 0x20, + (bcp << 4) | (lrp << 5)); + return 0; +} + +static int wm8804_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component; + u16 blen; + + component = dai->component; + + switch (params_width(params)) { + case 16: + blen = 0x0; + break; + case 20: + blen = 0x1; + break; + case 24: + blen = 0x2; + break; + default: + dev_err(dai->dev, "Unsupported word length: %u\n", + params_width(params)); + return -EINVAL; + } + + /* set word length */ + snd_soc_component_update_bits(component, WM8804_AIFTX, 0xc, blen << 2); + snd_soc_component_update_bits(component, WM8804_AIFRX, 0xc, blen << 2); + + return 0; +} + +struct pll_div { + u32 prescale:1; + u32 mclkdiv:1; + u32 freqmode:2; + u32 n:4; + u32 k:22; +}; + +/* PLL rate to output rate divisions */ +static struct { + unsigned int div; + unsigned int freqmode; + unsigned int mclkdiv; +} post_table[] = { + { 2, 0, 0 }, + { 4, 0, 1 }, + { 4, 1, 0 }, + { 8, 1, 1 }, + { 8, 2, 0 }, + { 16, 2, 1 }, + { 12, 3, 0 }, + { 24, 3, 1 } +}; + +#define FIXED_PLL_SIZE ((1ULL << 22) * 10) +static int pll_factors(struct pll_div *pll_div, unsigned int target, + unsigned int source, unsigned int mclk_div) +{ + u64 Kpart; + unsigned long int K, Ndiv, Nmod, tmp; + int i; + + /* + * Scale the output frequency up; the PLL should run in the + * region of 90-100MHz. + */ + for (i = 0; i < ARRAY_SIZE(post_table); i++) { + tmp = target * post_table[i].div; + if ((tmp >= 90000000 && tmp <= 100000000) && + (mclk_div == post_table[i].mclkdiv)) { + pll_div->freqmode = post_table[i].freqmode; + pll_div->mclkdiv = post_table[i].mclkdiv; + target *= post_table[i].div; + break; + } + } + + if (i == ARRAY_SIZE(post_table)) { + pr_err("%s: Unable to scale output frequency: %uHz\n", + __func__, target); + return -EINVAL; + } + + pll_div->prescale = 0; + Ndiv = target / source; + if (Ndiv < 5) { + source >>= 1; + pll_div->prescale = 1; + Ndiv = target / source; + } + + if (Ndiv < 5 || Ndiv > 13) { + pr_err("%s: WM8804 N value is not within the recommended range: %lu\n", + __func__, Ndiv); + return -EINVAL; + } + pll_div->n = Ndiv; + + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (u64)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xffffffff; + if ((K % 10) >= 5) + K += 5; + K /= 10; + pll_div->k = K; + + return 0; +} + +static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct wm8804_priv *wm8804 = snd_soc_component_get_drvdata(component); + bool change; + + if (!freq_in || !freq_out) { + /* disable the PLL */ + regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN, + 0x1, 0x1, &change); + if (change) + pm_runtime_put(wm8804->dev); + } else { + int ret; + struct pll_div pll_div; + + ret = pll_factors(&pll_div, freq_out, freq_in, + wm8804->mclk_div); + if (ret) + return ret; + + /* power down the PLL before reprogramming it */ + regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN, + 0x1, 0x1, &change); + if (!change) + pm_runtime_get_sync(wm8804->dev); + + /* set PLLN and PRESCALE */ + snd_soc_component_update_bits(component, WM8804_PLL4, 0xf | 0x10, + pll_div.n | (pll_div.prescale << 4)); + /* set mclkdiv and freqmode */ + snd_soc_component_update_bits(component, WM8804_PLL5, 0x3 | 0x8, + pll_div.freqmode | (pll_div.mclkdiv << 3)); + /* set PLLK */ + snd_soc_component_write(component, WM8804_PLL1, pll_div.k & 0xff); + snd_soc_component_write(component, WM8804_PLL2, (pll_div.k >> 8) & 0xff); + snd_soc_component_write(component, WM8804_PLL3, pll_div.k >> 16); + + /* power up the PLL */ + snd_soc_component_update_bits(component, WM8804_PWRDN, 0x1, 0); + } + + return 0; +} + +static int wm8804_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component; + + component = dai->component; + + switch (clk_id) { + case WM8804_TX_CLKSRC_MCLK: + if ((freq >= 10000000 && freq <= 14400000) + || (freq >= 16280000 && freq <= 27000000)) + snd_soc_component_update_bits(component, WM8804_PLL6, 0x80, 0x80); + else { + dev_err(dai->dev, "OSCCLOCK is not within the " + "recommended range: %uHz\n", freq); + return -EINVAL; + } + break; + case WM8804_TX_CLKSRC_PLL: + snd_soc_component_update_bits(component, WM8804_PLL6, 0x80, 0); + break; + case WM8804_CLKOUT_SRC_CLK1: + snd_soc_component_update_bits(component, WM8804_PLL6, 0x8, 0); + break; + case WM8804_CLKOUT_SRC_OSCCLK: + snd_soc_component_update_bits(component, WM8804_PLL6, 0x8, 0x8); + break; + default: + dev_err(dai->dev, "Unknown clock source: %d\n", clk_id); + return -EINVAL; + } + + return 0; +} + +static int wm8804_set_clkdiv(struct snd_soc_dai *dai, + int div_id, int div) +{ + struct snd_soc_component *component; + struct wm8804_priv *wm8804; + + component = dai->component; + switch (div_id) { + case WM8804_CLKOUT_DIV: + snd_soc_component_update_bits(component, WM8804_PLL5, 0x30, + (div & 0x3) << 4); + break; + case WM8804_MCLK_DIV: + wm8804 = snd_soc_component_get_drvdata(component); + wm8804->mclk_div = div; + break; + default: + dev_err(dai->dev, "Unknown clock divider: %d\n", div_id); + return -EINVAL; + } + return 0; +} + +static const struct snd_soc_dai_ops wm8804_dai_ops = { + .hw_params = wm8804_hw_params, + .set_fmt = wm8804_set_fmt, + .set_sysclk = wm8804_set_sysclk, + .set_clkdiv = wm8804_set_clkdiv, + .set_pll = wm8804_set_pll +}; + +#define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define WM8804_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +static struct snd_soc_dai_driver wm8804_dai = { + .name = "wm8804-spdif", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8804_RATES, + .formats = WM8804_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = WM8804_RATES, + .formats = WM8804_FORMATS, + }, + .ops = &wm8804_dai_ops, + .symmetric_rate = 1 +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8804 = { + .dapm_widgets = wm8804_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets), + .dapm_routes = wm8804_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +const struct regmap_config wm8804_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = WM8804_MAX_REGISTER, + .volatile_reg = wm8804_volatile, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm8804_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults), +}; +EXPORT_SYMBOL_GPL(wm8804_regmap_config); + +int wm8804_probe(struct device *dev, struct regmap *regmap) +{ + struct wm8804_priv *wm8804; + unsigned int id1, id2; + int i, ret; + + wm8804 = devm_kzalloc(dev, sizeof(*wm8804), GFP_KERNEL); + if (!wm8804) + return -ENOMEM; + + dev_set_drvdata(dev, wm8804); + + wm8804->dev = dev; + wm8804->regmap = regmap; + + wm8804->reset = devm_gpiod_get_optional(dev, "wlf,reset", + GPIOD_OUT_LOW); + if (IS_ERR(wm8804->reset)) { + ret = PTR_ERR(wm8804->reset); + dev_err(dev, "Failed to get reset line: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) + wm8804->supplies[i].supply = wm8804_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); + if (ret) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0; + wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1; + + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { + struct regulator *regulator = wm8804->supplies[i].consumer; + + ret = devm_regulator_register_notifier(regulator, + &wm8804->disable_nb[i]); + if (ret != 0) { + dev_err(dev, + "Failed to register regulator notifier: %d\n", + ret); + return ret; + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); + if (ret) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(wm8804->reset, 1); + + ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1); + if (ret < 0) { + dev_err(dev, "Failed to read device ID: %d\n", ret); + goto err_reg_enable; + } + + ret = regmap_read(regmap, WM8804_DEVID2, &id2); + if (ret < 0) { + dev_err(dev, "Failed to read device ID: %d\n", ret); + goto err_reg_enable; + } + + id2 = (id2 << 8) | id1; + + if (id2 != 0x8805) { + dev_err(dev, "Invalid device ID: %#x\n", id2); + ret = -EINVAL; + goto err_reg_enable; + } + + ret = regmap_read(regmap, WM8804_DEVREV, &id1); + if (ret < 0) { + dev_err(dev, "Failed to read device revision: %d\n", + ret); + goto err_reg_enable; + } + dev_info(dev, "revision %c\n", id1 + 'A'); + + if (!wm8804->reset) { + ret = wm8804_soft_reset(wm8804); + if (ret < 0) { + dev_err(dev, "Failed to issue reset: %d\n", ret); + goto err_reg_enable; + } + } + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_wm8804, + &wm8804_dai, 1); + if (ret < 0) { + dev_err(dev, "Failed to register CODEC: %d\n", ret); + goto err_reg_enable; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + return 0; + +err_reg_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); + return ret; +} +EXPORT_SYMBOL_GPL(wm8804_probe); + +void wm8804_remove(struct device *dev) +{ + pm_runtime_disable(dev); +} +EXPORT_SYMBOL_GPL(wm8804_remove); + +#if IS_ENABLED(CONFIG_PM) +static int wm8804_runtime_resume(struct device *dev) +{ + struct wm8804_priv *wm8804 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); + if (ret) { + dev_err(wm8804->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + regcache_sync(wm8804->regmap); + + /* Power up OSCCLK */ + regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x0); + + return 0; +} + +static int wm8804_runtime_suspend(struct device *dev) +{ + struct wm8804_priv *wm8804 = dev_get_drvdata(dev); + + /* Power down OSCCLK */ + regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x8); + + regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); + + return 0; +} +#endif + +const struct dev_pm_ops wm8804_pm = { + SET_RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(wm8804_pm); + +MODULE_DESCRIPTION("ASoC WM8804 driver"); +MODULE_AUTHOR("Dimitris Papastamos "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8804.h b/sound/soc/codecs/wm8804.h new file mode 100644 index 0000000000..64f3ccc9ac --- /dev/null +++ b/sound/soc/codecs/wm8804.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8804.h -- WM8804 S/PDIF transceiver driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos + */ + +#ifndef _WM8804_H +#define _WM8804_H + +#include + +/* + * Register values. + */ +#define WM8804_RST_DEVID1 0x00 +#define WM8804_DEVID2 0x01 +#define WM8804_DEVREV 0x02 +#define WM8804_PLL1 0x03 +#define WM8804_PLL2 0x04 +#define WM8804_PLL3 0x05 +#define WM8804_PLL4 0x06 +#define WM8804_PLL5 0x07 +#define WM8804_PLL6 0x08 +#define WM8804_SPDMODE 0x09 +#define WM8804_INTMASK 0x0A +#define WM8804_INTSTAT 0x0B +#define WM8804_SPDSTAT 0x0C +#define WM8804_RXCHAN1 0x0D +#define WM8804_RXCHAN2 0x0E +#define WM8804_RXCHAN3 0x0F +#define WM8804_RXCHAN4 0x10 +#define WM8804_RXCHAN5 0x11 +#define WM8804_SPDTX1 0x12 +#define WM8804_SPDTX2 0x13 +#define WM8804_SPDTX3 0x14 +#define WM8804_SPDTX4 0x15 +#define WM8804_SPDTX5 0x16 +#define WM8804_GPO0 0x17 +#define WM8804_GPO1 0x18 +#define WM8804_GPO2 0x1A +#define WM8804_AIFTX 0x1B +#define WM8804_AIFRX 0x1C +#define WM8804_SPDRX1 0x1D +#define WM8804_PWRDN 0x1E + +#define WM8804_REGISTER_COUNT 30 +#define WM8804_MAX_REGISTER 0x1E + +#define WM8804_TX_CLKSRC_MCLK 1 +#define WM8804_TX_CLKSRC_PLL 2 + +#define WM8804_CLKOUT_SRC_CLK1 3 +#define WM8804_CLKOUT_SRC_OSCCLK 4 + +#define WM8804_CLKOUT_DIV 1 +#define WM8804_MCLK_DIV 2 + +#define WM8804_MCLKDIV_256FS 0 +#define WM8804_MCLKDIV_128FS 1 + +extern const struct regmap_config wm8804_regmap_config; +extern const struct dev_pm_ops wm8804_pm; + +int wm8804_probe(struct device *dev, struct regmap *regmap); +void wm8804_remove(struct device *dev); + +#endif /* _WM8804_H */ diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c new file mode 100644 index 0000000000..84d06c1904 --- /dev/null +++ b/sound/soc/codecs/wm8900.c @@ -0,0 +1,1338 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8900.c -- WM8900 ALSA Soc Audio driver + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * TODO: + * - Tristating. + * - TDM. + * - Jack detect. + * - FLL source configuration, currently only MCLK is supported. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8900.h" + +/* WM8900 register space */ +#define WM8900_REG_RESET 0x0 +#define WM8900_REG_ID 0x0 +#define WM8900_REG_POWER1 0x1 +#define WM8900_REG_POWER2 0x2 +#define WM8900_REG_POWER3 0x3 +#define WM8900_REG_AUDIO1 0x4 +#define WM8900_REG_AUDIO2 0x5 +#define WM8900_REG_CLOCKING1 0x6 +#define WM8900_REG_CLOCKING2 0x7 +#define WM8900_REG_AUDIO3 0x8 +#define WM8900_REG_AUDIO4 0x9 +#define WM8900_REG_DACCTRL 0xa +#define WM8900_REG_LDAC_DV 0xb +#define WM8900_REG_RDAC_DV 0xc +#define WM8900_REG_SIDETONE 0xd +#define WM8900_REG_ADCCTRL 0xe +#define WM8900_REG_LADC_DV 0xf +#define WM8900_REG_RADC_DV 0x10 +#define WM8900_REG_GPIO 0x12 +#define WM8900_REG_INCTL 0x15 +#define WM8900_REG_LINVOL 0x16 +#define WM8900_REG_RINVOL 0x17 +#define WM8900_REG_INBOOSTMIX1 0x18 +#define WM8900_REG_INBOOSTMIX2 0x19 +#define WM8900_REG_ADCPATH 0x1a +#define WM8900_REG_AUXBOOST 0x1b +#define WM8900_REG_ADDCTL 0x1e +#define WM8900_REG_FLLCTL1 0x24 +#define WM8900_REG_FLLCTL2 0x25 +#define WM8900_REG_FLLCTL3 0x26 +#define WM8900_REG_FLLCTL4 0x27 +#define WM8900_REG_FLLCTL5 0x28 +#define WM8900_REG_FLLCTL6 0x29 +#define WM8900_REG_LOUTMIXCTL1 0x2c +#define WM8900_REG_ROUTMIXCTL1 0x2d +#define WM8900_REG_BYPASS1 0x2e +#define WM8900_REG_BYPASS2 0x2f +#define WM8900_REG_AUXOUT_CTL 0x30 +#define WM8900_REG_LOUT1CTL 0x33 +#define WM8900_REG_ROUT1CTL 0x34 +#define WM8900_REG_LOUT2CTL 0x35 +#define WM8900_REG_ROUT2CTL 0x36 +#define WM8900_REG_HPCTL1 0x3a +#define WM8900_REG_OUTBIASCTL 0x73 + +#define WM8900_MAXREG 0x80 + +#define WM8900_REG_ADDCTL_OUT1_DIS 0x80 +#define WM8900_REG_ADDCTL_OUT2_DIS 0x40 +#define WM8900_REG_ADDCTL_VMID_DIS 0x20 +#define WM8900_REG_ADDCTL_BIAS_SRC 0x10 +#define WM8900_REG_ADDCTL_VMID_SOFTST 0x04 +#define WM8900_REG_ADDCTL_TEMP_SD 0x02 + +#define WM8900_REG_GPIO_TEMP_ENA 0x2 + +#define WM8900_REG_POWER1_STARTUP_BIAS_ENA 0x0100 +#define WM8900_REG_POWER1_BIAS_ENA 0x0008 +#define WM8900_REG_POWER1_VMID_BUF_ENA 0x0004 +#define WM8900_REG_POWER1_FLL_ENA 0x0040 + +#define WM8900_REG_POWER2_SYSCLK_ENA 0x8000 +#define WM8900_REG_POWER2_ADCL_ENA 0x0002 +#define WM8900_REG_POWER2_ADCR_ENA 0x0001 + +#define WM8900_REG_POWER3_DACL_ENA 0x0002 +#define WM8900_REG_POWER3_DACR_ENA 0x0001 + +#define WM8900_REG_AUDIO1_AIF_FMT_MASK 0x0018 +#define WM8900_REG_AUDIO1_LRCLK_INV 0x0080 +#define WM8900_REG_AUDIO1_BCLK_INV 0x0100 + +#define WM8900_REG_CLOCKING1_BCLK_DIR 0x1 +#define WM8900_REG_CLOCKING1_MCLK_SRC 0x100 +#define WM8900_REG_CLOCKING1_BCLK_MASK 0x01e +#define WM8900_REG_CLOCKING1_OPCLK_MASK 0x7000 + +#define WM8900_REG_CLOCKING2_ADC_CLKDIV 0xe0 +#define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c + +#define WM8900_REG_DACCTRL_MUTE 0x004 +#define WM8900_REG_DACCTRL_DAC_SB_FILT 0x100 +#define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400 + +#define WM8900_REG_AUDIO3_ADCLRC_DIR 0x0800 + +#define WM8900_REG_AUDIO4_DACLRC_DIR 0x0800 + +#define WM8900_REG_FLLCTL1_OSC_ENA 0x100 + +#define WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF 0x100 + +#define WM8900_REG_HPCTL1_HP_IPSTAGE_ENA 0x80 +#define WM8900_REG_HPCTL1_HP_OPSTAGE_ENA 0x40 +#define WM8900_REG_HPCTL1_HP_CLAMP_IP 0x20 +#define WM8900_REG_HPCTL1_HP_CLAMP_OP 0x10 +#define WM8900_REG_HPCTL1_HP_SHORT 0x08 +#define WM8900_REG_HPCTL1_HP_SHORT2 0x04 + +#define WM8900_LRC_MASK 0x03ff + +struct wm8900_priv { + struct regmap *regmap; + + u32 fll_in; /* FLL input frequency */ + u32 fll_out; /* FLL output frequency */ +}; + +/* + * wm8900 register cache. We can't read the entire register space and we + * have slow control buses so we cache the registers. + */ +static const struct reg_default wm8900_reg_defaults[] = { + { 1, 0x0000 }, + { 2, 0xc000 }, + { 3, 0x0000 }, + { 4, 0x4050 }, + { 5, 0x4000 }, + { 6, 0x0008 }, + { 7, 0x0000 }, + { 8, 0x0040 }, + { 9, 0x0040 }, + { 10, 0x1004 }, + { 11, 0x00c0 }, + { 12, 0x00c0 }, + { 13, 0x0000 }, + { 14, 0x0100 }, + { 15, 0x00c0 }, + { 16, 0x00c0 }, + { 17, 0x0000 }, + { 18, 0xb001 }, + { 19, 0x0000 }, + { 20, 0x0000 }, + { 21, 0x0044 }, + { 22, 0x004c }, + { 23, 0x004c }, + { 24, 0x0044 }, + { 25, 0x0044 }, + { 26, 0x0000 }, + { 27, 0x0044 }, + { 28, 0x0000 }, + { 29, 0x0000 }, + { 30, 0x0002 }, + { 31, 0x0000 }, + { 32, 0x0000 }, + { 33, 0x0000 }, + { 34, 0x0000 }, + { 35, 0x0000 }, + { 36, 0x0008 }, + { 37, 0x0000 }, + { 38, 0x0000 }, + { 39, 0x0008 }, + { 40, 0x0097 }, + { 41, 0x0100 }, + { 42, 0x0000 }, + { 43, 0x0000 }, + { 44, 0x0050 }, + { 45, 0x0050 }, + { 46, 0x0055 }, + { 47, 0x0055 }, + { 48, 0x0055 }, + { 49, 0x0000 }, + { 50, 0x0000 }, + { 51, 0x0079 }, + { 52, 0x0079 }, + { 53, 0x0079 }, + { 54, 0x0079 }, + { 55, 0x0000 }, +}; + +static bool wm8900_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8900_REG_ID: + return true; + default: + return false; + } +} + +static void wm8900_reset(struct snd_soc_component *component) +{ + snd_soc_component_write(component, WM8900_REG_RESET, 0); +} + +static int wm8900_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 hpctl1 = snd_soc_component_read(component, WM8900_REG_HPCTL1); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Clamp headphone outputs */ + hpctl1 = WM8900_REG_HPCTL1_HP_CLAMP_IP | + WM8900_REG_HPCTL1_HP_CLAMP_OP; + snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1); + break; + + case SND_SOC_DAPM_POST_PMU: + /* Enable the input stage */ + hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_IP; + hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT | + WM8900_REG_HPCTL1_HP_SHORT2 | + WM8900_REG_HPCTL1_HP_IPSTAGE_ENA; + snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1); + + msleep(400); + + /* Enable the output stage */ + hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_OP; + hpctl1 |= WM8900_REG_HPCTL1_HP_OPSTAGE_ENA; + snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1); + + /* Remove the shorts */ + hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT2; + snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1); + hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT; + snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1); + break; + + case SND_SOC_DAPM_PRE_PMD: + /* Short the output */ + hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT; + snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1); + + /* Disable the output stage */ + hpctl1 &= ~WM8900_REG_HPCTL1_HP_OPSTAGE_ENA; + snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1); + + /* Clamp the outputs and power down input */ + hpctl1 |= WM8900_REG_HPCTL1_HP_CLAMP_IP | + WM8900_REG_HPCTL1_HP_CLAMP_OP; + hpctl1 &= ~WM8900_REG_HPCTL1_HP_IPSTAGE_ENA; + snd_soc_component_write(component, WM8900_REG_HPCTL1, hpctl1); + break; + + case SND_SOC_DAPM_POST_PMD: + /* Disable everything */ + snd_soc_component_write(component, WM8900_REG_HPCTL1, 0); + break; + + default: + WARN(1, "Invalid event %d\n", event); + break; + } + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 100, 0); + +static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 0); + +static const DECLARE_TLV_DB_SCALE(in_boost_tlv, -1200, 600, 0); + +static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1200, 100, 0); + +static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0); + +static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 75, 1); + +static const DECLARE_TLV_DB_SCALE(adc_svol_tlv, -3600, 300, 0); + +static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1); + +static const char *mic_bias_level_txt[] = { "0.9*AVDD", "0.65*AVDD" }; + +static SOC_ENUM_SINGLE_DECL(mic_bias_level, + WM8900_REG_INCTL, 8, mic_bias_level_txt); + +static const char *dac_mute_rate_txt[] = { "Fast", "Slow" }; + +static SOC_ENUM_SINGLE_DECL(dac_mute_rate, + WM8900_REG_DACCTRL, 7, dac_mute_rate_txt); + +static const char *dac_deemphasis_txt[] = { + "Disabled", "32kHz", "44.1kHz", "48kHz" +}; + +static SOC_ENUM_SINGLE_DECL(dac_deemphasis, + WM8900_REG_DACCTRL, 4, dac_deemphasis_txt); + +static const char *adc_hpf_cut_txt[] = { + "Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3" +}; + +static SOC_ENUM_SINGLE_DECL(adc_hpf_cut, + WM8900_REG_ADCCTRL, 5, adc_hpf_cut_txt); + +static const char *lr_txt[] = { + "Left", "Right" +}; + +static SOC_ENUM_SINGLE_DECL(aifl_src, + WM8900_REG_AUDIO1, 15, lr_txt); + +static SOC_ENUM_SINGLE_DECL(aifr_src, + WM8900_REG_AUDIO1, 14, lr_txt); + +static SOC_ENUM_SINGLE_DECL(dacl_src, + WM8900_REG_AUDIO2, 15, lr_txt); + +static SOC_ENUM_SINGLE_DECL(dacr_src, + WM8900_REG_AUDIO2, 14, lr_txt); + +static const char *sidetone_txt[] = { + "Disabled", "Left ADC", "Right ADC" +}; + +static SOC_ENUM_SINGLE_DECL(dacl_sidetone, + WM8900_REG_SIDETONE, 2, sidetone_txt); + +static SOC_ENUM_SINGLE_DECL(dacr_sidetone, + WM8900_REG_SIDETONE, 0, sidetone_txt); + +static const struct snd_kcontrol_new wm8900_snd_controls[] = { +SOC_ENUM("Mic Bias Level", mic_bias_level), + +SOC_SINGLE_TLV("Left Input PGA Volume", WM8900_REG_LINVOL, 0, 31, 0, + in_pga_tlv), +SOC_SINGLE("Left Input PGA Switch", WM8900_REG_LINVOL, 6, 1, 1), +SOC_SINGLE("Left Input PGA ZC Switch", WM8900_REG_LINVOL, 7, 1, 0), + +SOC_SINGLE_TLV("Right Input PGA Volume", WM8900_REG_RINVOL, 0, 31, 0, + in_pga_tlv), +SOC_SINGLE("Right Input PGA Switch", WM8900_REG_RINVOL, 6, 1, 1), +SOC_SINGLE("Right Input PGA ZC Switch", WM8900_REG_RINVOL, 7, 1, 0), + +SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1), +SOC_ENUM("DAC Mute Rate", dac_mute_rate), +SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0), +SOC_ENUM("DAC Deemphasis", dac_deemphasis), +SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL, + 12, 1, 0), + +SOC_SINGLE("ADC HPF Switch", WM8900_REG_ADCCTRL, 8, 1, 0), +SOC_ENUM("ADC HPF Cut-Off", adc_hpf_cut), +SOC_DOUBLE("ADC Invert Switch", WM8900_REG_ADCCTRL, 1, 0, 1, 0), +SOC_SINGLE_TLV("Left ADC Sidetone Volume", WM8900_REG_SIDETONE, 9, 12, 0, + adc_svol_tlv), +SOC_SINGLE_TLV("Right ADC Sidetone Volume", WM8900_REG_SIDETONE, 5, 12, 0, + adc_svol_tlv), +SOC_ENUM("Left Digital Audio Source", aifl_src), +SOC_ENUM("Right Digital Audio Source", aifr_src), + +SOC_SINGLE_TLV("DAC Input Boost Volume", WM8900_REG_AUDIO2, 10, 4, 0, + dac_boost_tlv), +SOC_ENUM("Left DAC Source", dacl_src), +SOC_ENUM("Right DAC Source", dacr_src), +SOC_ENUM("Left DAC Sidetone", dacl_sidetone), +SOC_ENUM("Right DAC Sidetone", dacr_sidetone), +SOC_DOUBLE("DAC Invert Switch", WM8900_REG_DACCTRL, 1, 0, 1, 0), + +SOC_DOUBLE_R_TLV("Digital Playback Volume", + WM8900_REG_LDAC_DV, WM8900_REG_RDAC_DV, + 1, 96, 0, dac_tlv), +SOC_DOUBLE_R_TLV("Digital Capture Volume", + WM8900_REG_LADC_DV, WM8900_REG_RADC_DV, 1, 119, 0, adc_tlv), + +SOC_SINGLE_TLV("LINPUT3 Bypass Volume", WM8900_REG_LOUTMIXCTL1, 4, 7, 0, + out_mix_tlv), +SOC_SINGLE_TLV("RINPUT3 Bypass Volume", WM8900_REG_ROUTMIXCTL1, 4, 7, 0, + out_mix_tlv), +SOC_SINGLE_TLV("Left AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 4, 7, 0, + out_mix_tlv), +SOC_SINGLE_TLV("Right AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 0, 7, 0, + out_mix_tlv), + +SOC_SINGLE_TLV("LeftIn to RightOut Mixer Volume", WM8900_REG_BYPASS1, 0, 7, 0, + out_mix_tlv), +SOC_SINGLE_TLV("LeftIn to LeftOut Mixer Volume", WM8900_REG_BYPASS1, 4, 7, 0, + out_mix_tlv), +SOC_SINGLE_TLV("RightIn to LeftOut Mixer Volume", WM8900_REG_BYPASS2, 0, 7, 0, + out_mix_tlv), +SOC_SINGLE_TLV("RightIn to RightOut Mixer Volume", WM8900_REG_BYPASS2, 4, 7, 0, + out_mix_tlv), + +SOC_SINGLE_TLV("IN2L Boost Volume", WM8900_REG_INBOOSTMIX1, 0, 3, 0, + in_boost_tlv), +SOC_SINGLE_TLV("IN3L Boost Volume", WM8900_REG_INBOOSTMIX1, 4, 3, 0, + in_boost_tlv), +SOC_SINGLE_TLV("IN2R Boost Volume", WM8900_REG_INBOOSTMIX2, 0, 3, 0, + in_boost_tlv), +SOC_SINGLE_TLV("IN3R Boost Volume", WM8900_REG_INBOOSTMIX2, 4, 3, 0, + in_boost_tlv), +SOC_SINGLE_TLV("Left AUX Boost Volume", WM8900_REG_AUXBOOST, 4, 3, 0, + in_boost_tlv), +SOC_SINGLE_TLV("Right AUX Boost Volume", WM8900_REG_AUXBOOST, 0, 3, 0, + in_boost_tlv), + +SOC_DOUBLE_R_TLV("LINEOUT1 Volume", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL, + 0, 63, 0, out_pga_tlv), +SOC_DOUBLE_R("LINEOUT1 Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL, + 6, 1, 1), +SOC_DOUBLE_R("LINEOUT1 ZC Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL, + 7, 1, 0), + +SOC_DOUBLE_R_TLV("LINEOUT2 Volume", + WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, + 0, 63, 0, out_pga_tlv), +SOC_DOUBLE_R("LINEOUT2 Switch", + WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 6, 1, 1), +SOC_DOUBLE_R("LINEOUT2 ZC Switch", + WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 7, 1, 0), +SOC_SINGLE("LINEOUT2 LP -12dB", WM8900_REG_LOUTMIXCTL1, + 0, 1, 1), + +}; + +static const struct snd_kcontrol_new wm8900_loutmix_controls[] = { +SOC_DAPM_SINGLE("LINPUT3 Bypass Switch", WM8900_REG_LOUTMIXCTL1, 7, 1, 0), +SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 7, 1, 0), +SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 3, 1, 0), +SOC_DAPM_SINGLE("DACL Switch", WM8900_REG_LOUTMIXCTL1, 8, 1, 0), +}; + +static const struct snd_kcontrol_new wm8900_routmix_controls[] = { +SOC_DAPM_SINGLE("RINPUT3 Bypass Switch", WM8900_REG_ROUTMIXCTL1, 7, 1, 0), +SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 3, 1, 0), +SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 3, 1, 0), +SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 7, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8900_REG_ROUTMIXCTL1, 8, 1, 0), +}; + +static const struct snd_kcontrol_new wm8900_linmix_controls[] = { +SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INBOOSTMIX1, 2, 1, 1), +SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INBOOSTMIX1, 6, 1, 1), +SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 6, 1, 1), +SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 6, 1, 0), +}; + +static const struct snd_kcontrol_new wm8900_rinmix_controls[] = { +SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INBOOSTMIX2, 2, 1, 1), +SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INBOOSTMIX2, 6, 1, 1), +SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 2, 1, 1), +SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 2, 1, 0), +}; + +static const struct snd_kcontrol_new wm8900_linpga_controls[] = { +SOC_DAPM_SINGLE("LINPUT1 Switch", WM8900_REG_INCTL, 6, 1, 0), +SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INCTL, 5, 1, 0), +SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INCTL, 4, 1, 0), +}; + +static const struct snd_kcontrol_new wm8900_rinpga_controls[] = { +SOC_DAPM_SINGLE("RINPUT1 Switch", WM8900_REG_INCTL, 2, 1, 0), +SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INCTL, 1, 1, 0), +SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INCTL, 0, 1, 0), +}; + +static const char *wm8900_lp_mux[] = { "Disabled", "Enabled" }; + +static SOC_ENUM_SINGLE_DECL(wm8900_lineout2_lp_mux, + WM8900_REG_LOUTMIXCTL1, 1, wm8900_lp_mux); + +static const struct snd_kcontrol_new wm8900_lineout2_lp = +SOC_DAPM_ENUM("Route", wm8900_lineout2_lp_mux); + +static const struct snd_soc_dapm_widget wm8900_dapm_widgets[] = { + +/* Externally visible pins */ +SND_SOC_DAPM_OUTPUT("LINEOUT1L"), +SND_SOC_DAPM_OUTPUT("LINEOUT1R"), +SND_SOC_DAPM_OUTPUT("LINEOUT2L"), +SND_SOC_DAPM_OUTPUT("LINEOUT2R"), +SND_SOC_DAPM_OUTPUT("HP_L"), +SND_SOC_DAPM_OUTPUT("HP_R"), + +SND_SOC_DAPM_INPUT("RINPUT1"), +SND_SOC_DAPM_INPUT("LINPUT1"), +SND_SOC_DAPM_INPUT("RINPUT2"), +SND_SOC_DAPM_INPUT("LINPUT2"), +SND_SOC_DAPM_INPUT("RINPUT3"), +SND_SOC_DAPM_INPUT("LINPUT3"), +SND_SOC_DAPM_INPUT("AUX"), + +SND_SOC_DAPM_VMID("VMID"), + +/* Input */ +SND_SOC_DAPM_MIXER("Left Input PGA", WM8900_REG_POWER2, 3, 0, + wm8900_linpga_controls, + ARRAY_SIZE(wm8900_linpga_controls)), +SND_SOC_DAPM_MIXER("Right Input PGA", WM8900_REG_POWER2, 2, 0, + wm8900_rinpga_controls, + ARRAY_SIZE(wm8900_rinpga_controls)), + +SND_SOC_DAPM_MIXER("Left Input Mixer", WM8900_REG_POWER2, 5, 0, + wm8900_linmix_controls, + ARRAY_SIZE(wm8900_linmix_controls)), +SND_SOC_DAPM_MIXER("Right Input Mixer", WM8900_REG_POWER2, 4, 0, + wm8900_rinmix_controls, + ARRAY_SIZE(wm8900_rinmix_controls)), + +SND_SOC_DAPM_SUPPLY("Mic Bias", WM8900_REG_POWER1, 4, 0, NULL, 0), + +SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8900_REG_POWER2, 1, 0), +SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8900_REG_POWER2, 0, 0), + +/* Output */ +SND_SOC_DAPM_DAC("DACL", "Left HiFi Playback", WM8900_REG_POWER3, 1, 0), +SND_SOC_DAPM_DAC("DACR", "Right HiFi Playback", WM8900_REG_POWER3, 0, 0), + +SND_SOC_DAPM_PGA_E("Headphone Amplifier", WM8900_REG_POWER3, 7, 0, NULL, 0, + wm8900_hp_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_PGA("LINEOUT1L PGA", WM8900_REG_POWER2, 8, 0, NULL, 0), +SND_SOC_DAPM_PGA("LINEOUT1R PGA", WM8900_REG_POWER2, 7, 0, NULL, 0), + +SND_SOC_DAPM_MUX("LINEOUT2 LP", SND_SOC_NOPM, 0, 0, &wm8900_lineout2_lp), +SND_SOC_DAPM_PGA("LINEOUT2L PGA", WM8900_REG_POWER3, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("LINEOUT2R PGA", WM8900_REG_POWER3, 5, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("Left Output Mixer", WM8900_REG_POWER3, 3, 0, + wm8900_loutmix_controls, + ARRAY_SIZE(wm8900_loutmix_controls)), +SND_SOC_DAPM_MIXER("Right Output Mixer", WM8900_REG_POWER3, 2, 0, + wm8900_routmix_controls, + ARRAY_SIZE(wm8900_routmix_controls)), +}; + +/* Target, Path, Source */ +static const struct snd_soc_dapm_route wm8900_dapm_routes[] = { +/* Inputs */ +{"Left Input PGA", "LINPUT1 Switch", "LINPUT1"}, +{"Left Input PGA", "LINPUT2 Switch", "LINPUT2"}, +{"Left Input PGA", "LINPUT3 Switch", "LINPUT3"}, + +{"Right Input PGA", "RINPUT1 Switch", "RINPUT1"}, +{"Right Input PGA", "RINPUT2 Switch", "RINPUT2"}, +{"Right Input PGA", "RINPUT3 Switch", "RINPUT3"}, + +{"Left Input Mixer", "LINPUT2 Switch", "LINPUT2"}, +{"Left Input Mixer", "LINPUT3 Switch", "LINPUT3"}, +{"Left Input Mixer", "AUX Switch", "AUX"}, +{"Left Input Mixer", "Input PGA Switch", "Left Input PGA"}, + +{"Right Input Mixer", "RINPUT2 Switch", "RINPUT2"}, +{"Right Input Mixer", "RINPUT3 Switch", "RINPUT3"}, +{"Right Input Mixer", "AUX Switch", "AUX"}, +{"Right Input Mixer", "Input PGA Switch", "Right Input PGA"}, + +{"ADCL", NULL, "Left Input Mixer"}, +{"ADCR", NULL, "Right Input Mixer"}, + +/* Outputs */ +{"LINEOUT1L", NULL, "LINEOUT1L PGA"}, +{"LINEOUT1L PGA", NULL, "Left Output Mixer"}, +{"LINEOUT1R", NULL, "LINEOUT1R PGA"}, +{"LINEOUT1R PGA", NULL, "Right Output Mixer"}, + +{"LINEOUT2L PGA", NULL, "Left Output Mixer"}, +{"LINEOUT2 LP", "Disabled", "LINEOUT2L PGA"}, +{"LINEOUT2 LP", "Enabled", "Left Output Mixer"}, +{"LINEOUT2L", NULL, "LINEOUT2 LP"}, + +{"LINEOUT2R PGA", NULL, "Right Output Mixer"}, +{"LINEOUT2 LP", "Disabled", "LINEOUT2R PGA"}, +{"LINEOUT2 LP", "Enabled", "Right Output Mixer"}, +{"LINEOUT2R", NULL, "LINEOUT2 LP"}, + +{"Left Output Mixer", "LINPUT3 Bypass Switch", "LINPUT3"}, +{"Left Output Mixer", "AUX Bypass Switch", "AUX"}, +{"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"}, +{"Left Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"}, +{"Left Output Mixer", "DACL Switch", "DACL"}, + +{"Right Output Mixer", "RINPUT3 Bypass Switch", "RINPUT3"}, +{"Right Output Mixer", "AUX Bypass Switch", "AUX"}, +{"Right Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"}, +{"Right Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"}, +{"Right Output Mixer", "DACR Switch", "DACR"}, + +/* Note that the headphone output stage needs to be connected + * externally to LINEOUT2 via DC blocking capacitors. Other + * configurations are not supported. + * + * Note also that left and right headphone paths are treated as a + * mono path. + */ +{"Headphone Amplifier", NULL, "LINEOUT2 LP"}, +{"Headphone Amplifier", NULL, "LINEOUT2 LP"}, +{"HP_L", NULL, "Headphone Amplifier"}, +{"HP_R", NULL, "Headphone Amplifier"}, +}; + +static int wm8900_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 reg; + + reg = snd_soc_component_read(component, WM8900_REG_AUDIO1) & ~0x60; + + switch (params_width(params)) { + case 16: + break; + case 20: + reg |= 0x20; + break; + case 24: + reg |= 0x40; + break; + case 32: + reg |= 0x60; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8900_REG_AUDIO1, reg); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + reg = snd_soc_component_read(component, WM8900_REG_DACCTRL); + + if (params_rate(params) <= 24000) + reg |= WM8900_REG_DACCTRL_DAC_SB_FILT; + else + reg &= ~WM8900_REG_DACCTRL_DAC_SB_FILT; + + snd_soc_component_write(component, WM8900_REG_DACCTRL, reg); + } + + return 0; +} + +/* FLL divisors */ +struct _fll_div { + u16 fll_ratio; + u16 fllclk_div; + u16 fll_slow_lock_ref; + u16 n; + u16 k; +}; + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, + unsigned int Fout) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, target; + unsigned int div; + + if (WARN_ON(!Fout)) + return -EINVAL; + + /* The FLL must run at 90-100MHz which is then scaled down to + * the output value by FLLCLK_DIV. */ + target = Fout; + div = 1; + while (target < 90000000) { + div *= 2; + target *= 2; + } + + if (target > 100000000) + printk(KERN_WARNING "wm8900: FLL rate %u out of range, Fref=%u" + " Fout=%u\n", target, Fref, Fout); + if (div > 32) { + printk(KERN_ERR "wm8900: Invalid FLL division rate %u, " + "Fref=%u, Fout=%u, target=%u\n", + div, Fref, Fout, target); + return -EINVAL; + } + + fll_div->fllclk_div = div >> 2; + + if (Fref < 48000) + fll_div->fll_slow_lock_ref = 1; + else + fll_div->fll_slow_lock_ref = 0; + + Ndiv = target / Fref; + + if (Fref < 1000000) + fll_div->fll_ratio = 8; + else + fll_div->fll_ratio = 1; + + fll_div->n = Ndiv / fll_div->fll_ratio; + Nmod = (target / fll_div->fll_ratio) % Fref; + + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, Fref); + + K = Kpart & 0xFFFFFFFF; + + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + fll_div->k = K / 10; + + if (WARN_ON(target != Fout * (fll_div->fllclk_div << 2)) || + WARN_ON(!K && target != Fref * fll_div->fll_ratio * fll_div->n)) + return -EINVAL; + + return 0; +} + +static int wm8900_set_fll(struct snd_soc_component *component, + int fll_id, unsigned int freq_in, unsigned int freq_out) +{ + struct wm8900_priv *wm8900 = snd_soc_component_get_drvdata(component); + struct _fll_div fll_div; + + if (wm8900->fll_in == freq_in && wm8900->fll_out == freq_out) + return 0; + + /* The digital side should be disabled during any change. */ + snd_soc_component_update_bits(component, WM8900_REG_POWER1, + WM8900_REG_POWER1_FLL_ENA, 0); + + /* Disable the FLL? */ + if (!freq_in || !freq_out) { + snd_soc_component_update_bits(component, WM8900_REG_CLOCKING1, + WM8900_REG_CLOCKING1_MCLK_SRC, 0); + snd_soc_component_update_bits(component, WM8900_REG_FLLCTL1, + WM8900_REG_FLLCTL1_OSC_ENA, 0); + wm8900->fll_in = freq_in; + wm8900->fll_out = freq_out; + + return 0; + } + + if (fll_factors(&fll_div, freq_in, freq_out) != 0) + goto reenable; + + wm8900->fll_in = freq_in; + wm8900->fll_out = freq_out; + + /* The osclilator *MUST* be enabled before we enable the + * digital circuit. */ + snd_soc_component_write(component, WM8900_REG_FLLCTL1, + fll_div.fll_ratio | WM8900_REG_FLLCTL1_OSC_ENA); + + snd_soc_component_write(component, WM8900_REG_FLLCTL4, fll_div.n >> 5); + snd_soc_component_write(component, WM8900_REG_FLLCTL5, + (fll_div.fllclk_div << 6) | (fll_div.n & 0x1f)); + + if (fll_div.k) { + snd_soc_component_write(component, WM8900_REG_FLLCTL2, + (fll_div.k >> 8) | 0x100); + snd_soc_component_write(component, WM8900_REG_FLLCTL3, fll_div.k & 0xff); + } else + snd_soc_component_write(component, WM8900_REG_FLLCTL2, 0); + + if (fll_div.fll_slow_lock_ref) + snd_soc_component_write(component, WM8900_REG_FLLCTL6, + WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF); + else + snd_soc_component_write(component, WM8900_REG_FLLCTL6, 0); + + snd_soc_component_update_bits(component, WM8900_REG_POWER1, + WM8900_REG_POWER1_FLL_ENA, + WM8900_REG_POWER1_FLL_ENA); + +reenable: + snd_soc_component_update_bits(component, WM8900_REG_CLOCKING1, + WM8900_REG_CLOCKING1_MCLK_SRC, + WM8900_REG_CLOCKING1_MCLK_SRC); + return 0; +} + +static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + return wm8900_set_fll(codec_dai->component, pll_id, freq_in, freq_out); +} + +static int wm8900_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + + switch (div_id) { + case WM8900_BCLK_DIV: + snd_soc_component_update_bits(component, WM8900_REG_CLOCKING1, + WM8900_REG_CLOCKING1_BCLK_MASK, div); + break; + case WM8900_OPCLK_DIV: + snd_soc_component_update_bits(component, WM8900_REG_CLOCKING1, + WM8900_REG_CLOCKING1_OPCLK_MASK, div); + break; + case WM8900_DAC_LRCLK: + snd_soc_component_update_bits(component, WM8900_REG_AUDIO4, + WM8900_LRC_MASK, div); + break; + case WM8900_ADC_LRCLK: + snd_soc_component_update_bits(component, WM8900_REG_AUDIO3, + WM8900_LRC_MASK, div); + break; + case WM8900_DAC_CLKDIV: + snd_soc_component_update_bits(component, WM8900_REG_CLOCKING2, + WM8900_REG_CLOCKING2_DAC_CLKDIV, div); + break; + case WM8900_ADC_CLKDIV: + snd_soc_component_update_bits(component, WM8900_REG_CLOCKING2, + WM8900_REG_CLOCKING2_ADC_CLKDIV, div); + break; + case WM8900_LRCLK_MODE: + snd_soc_component_update_bits(component, WM8900_REG_DACCTRL, + WM8900_REG_DACCTRL_AIF_LRCLKRATE, div); + break; + default: + return -EINVAL; + } + + return 0; +} + + +static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + unsigned int clocking1, aif1, aif3, aif4; + + clocking1 = snd_soc_component_read(component, WM8900_REG_CLOCKING1); + aif1 = snd_soc_component_read(component, WM8900_REG_AUDIO1); + aif3 = snd_soc_component_read(component, WM8900_REG_AUDIO3); + aif4 = snd_soc_component_read(component, WM8900_REG_AUDIO4); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR; + aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR; + aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR; + break; + case SND_SOC_DAIFMT_CBS_CFM: + clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR; + aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR; + aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR; + aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR; + aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR; + aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR; + aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK; + aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_B: + aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK; + aif1 |= WM8900_REG_AUDIO1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_I2S: + aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK; + aif1 |= 0x10; + break; + case SND_SOC_DAIFMT_RIGHT_J: + aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK; + break; + case SND_SOC_DAIFMT_LEFT_J: + aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK; + aif1 |= 0x8; + break; + default: + return -EINVAL; + } + + /* Clock inversion */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8900_REG_AUDIO1_BCLK_INV; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV; + aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + aif1 |= WM8900_REG_AUDIO1_BCLK_INV; + aif1 |= WM8900_REG_AUDIO1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8900_REG_AUDIO1_BCLK_INV; + aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV; + aif1 |= WM8900_REG_AUDIO1_LRCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8900_REG_CLOCKING1, clocking1); + snd_soc_component_write(component, WM8900_REG_AUDIO1, aif1); + snd_soc_component_write(component, WM8900_REG_AUDIO3, aif3); + snd_soc_component_write(component, WM8900_REG_AUDIO4, aif4); + + return 0; +} + +static int wm8900_mute(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + + reg = snd_soc_component_read(component, WM8900_REG_DACCTRL); + + if (mute) + reg |= WM8900_REG_DACCTRL_MUTE; + else + reg &= ~WM8900_REG_DACCTRL_MUTE; + + snd_soc_component_write(component, WM8900_REG_DACCTRL, reg); + + return 0; +} + +#define WM8900_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +#define WM8900_PCM_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8900_dai_ops = { + .hw_params = wm8900_hw_params, + .set_clkdiv = wm8900_set_dai_clkdiv, + .set_pll = wm8900_set_dai_pll, + .set_fmt = wm8900_set_dai_fmt, + .mute_stream = wm8900_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8900_dai = { + .name = "wm8900-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8900_RATES, + .formats = WM8900_PCM_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8900_RATES, + .formats = WM8900_PCM_FORMATS, + }, + .ops = &wm8900_dai_ops, +}; + +static int wm8900_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + u16 reg; + + switch (level) { + case SND_SOC_BIAS_ON: + /* Enable thermal shutdown */ + snd_soc_component_update_bits(component, WM8900_REG_GPIO, + WM8900_REG_GPIO_TEMP_ENA, + WM8900_REG_GPIO_TEMP_ENA); + snd_soc_component_update_bits(component, WM8900_REG_ADDCTL, + WM8900_REG_ADDCTL_TEMP_SD, + WM8900_REG_ADDCTL_TEMP_SD); + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + /* Charge capacitors if initial power up */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* STARTUP_BIAS_ENA on */ + snd_soc_component_write(component, WM8900_REG_POWER1, + WM8900_REG_POWER1_STARTUP_BIAS_ENA); + + /* Startup bias mode */ + snd_soc_component_write(component, WM8900_REG_ADDCTL, + WM8900_REG_ADDCTL_BIAS_SRC | + WM8900_REG_ADDCTL_VMID_SOFTST); + + /* VMID 2x50k */ + snd_soc_component_write(component, WM8900_REG_POWER1, + WM8900_REG_POWER1_STARTUP_BIAS_ENA | 0x1); + + /* Allow capacitors to charge */ + schedule_timeout_interruptible(msecs_to_jiffies(400)); + + /* Enable bias */ + snd_soc_component_write(component, WM8900_REG_POWER1, + WM8900_REG_POWER1_STARTUP_BIAS_ENA | + WM8900_REG_POWER1_BIAS_ENA | 0x1); + + snd_soc_component_write(component, WM8900_REG_ADDCTL, 0); + + snd_soc_component_write(component, WM8900_REG_POWER1, + WM8900_REG_POWER1_BIAS_ENA | 0x1); + } + + reg = snd_soc_component_read(component, WM8900_REG_POWER1); + snd_soc_component_write(component, WM8900_REG_POWER1, + (reg & WM8900_REG_POWER1_FLL_ENA) | + WM8900_REG_POWER1_BIAS_ENA | 0x1); + snd_soc_component_write(component, WM8900_REG_POWER2, + WM8900_REG_POWER2_SYSCLK_ENA); + snd_soc_component_write(component, WM8900_REG_POWER3, 0); + break; + + case SND_SOC_BIAS_OFF: + /* Startup bias enable */ + reg = snd_soc_component_read(component, WM8900_REG_POWER1); + snd_soc_component_write(component, WM8900_REG_POWER1, + reg & WM8900_REG_POWER1_STARTUP_BIAS_ENA); + snd_soc_component_write(component, WM8900_REG_ADDCTL, + WM8900_REG_ADDCTL_BIAS_SRC | + WM8900_REG_ADDCTL_VMID_SOFTST); + + /* Discharge caps */ + snd_soc_component_write(component, WM8900_REG_POWER1, + WM8900_REG_POWER1_STARTUP_BIAS_ENA); + schedule_timeout_interruptible(msecs_to_jiffies(500)); + + /* Remove clamp */ + snd_soc_component_write(component, WM8900_REG_HPCTL1, 0); + + /* Power down */ + snd_soc_component_write(component, WM8900_REG_ADDCTL, 0); + snd_soc_component_write(component, WM8900_REG_POWER1, 0); + snd_soc_component_write(component, WM8900_REG_POWER2, 0); + snd_soc_component_write(component, WM8900_REG_POWER3, 0); + + /* Need to let things settle before stopping the clock + * to ensure that restart works, see "Stopping the + * master clock" in the datasheet. */ + schedule_timeout_interruptible(msecs_to_jiffies(1)); + snd_soc_component_write(component, WM8900_REG_POWER2, + WM8900_REG_POWER2_SYSCLK_ENA); + break; + } + return 0; +} + +static int wm8900_suspend(struct snd_soc_component *component) +{ + struct wm8900_priv *wm8900 = snd_soc_component_get_drvdata(component); + int fll_out = wm8900->fll_out; + int fll_in = wm8900->fll_in; + int ret; + + /* Stop the FLL in an orderly fashion */ + ret = wm8900_set_fll(component, 0, 0, 0); + if (ret != 0) { + dev_err(component->dev, "Failed to stop FLL\n"); + return ret; + } + + wm8900->fll_out = fll_out; + wm8900->fll_in = fll_in; + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + return 0; +} + +static int wm8900_resume(struct snd_soc_component *component) +{ + struct wm8900_priv *wm8900 = snd_soc_component_get_drvdata(component); + int ret; + + wm8900_reset(component); + + ret = regcache_sync(wm8900->regmap); + if (ret != 0) { + dev_err(component->dev, "Failed to restore cache: %d\n", ret); + return ret; + } + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + /* Restart the FLL? */ + if (wm8900->fll_out) { + int fll_out = wm8900->fll_out; + int fll_in = wm8900->fll_in; + + wm8900->fll_in = 0; + wm8900->fll_out = 0; + + ret = wm8900_set_fll(component, 0, fll_in, fll_out); + if (ret != 0) { + dev_err(component->dev, "Failed to restart FLL\n"); + return ret; + } + } + + return 0; +} + +static int wm8900_probe(struct snd_soc_component *component) +{ + int reg; + + reg = snd_soc_component_read(component, WM8900_REG_ID); + if (reg != 0x8900) { + dev_err(component->dev, "Device is not a WM8900 - ID %x\n", reg); + return -ENODEV; + } + + wm8900_reset(component); + + /* Turn the chip on */ + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + /* Latch the volume update bits */ + snd_soc_component_update_bits(component, WM8900_REG_LINVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8900_REG_RINVOL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8900_REG_LOUT1CTL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8900_REG_ROUT1CTL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8900_REG_LOUT2CTL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8900_REG_ROUT2CTL, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8900_REG_LDAC_DV, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8900_REG_RDAC_DV, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8900_REG_LADC_DV, 0x100, 0x100); + snd_soc_component_update_bits(component, WM8900_REG_RADC_DV, 0x100, 0x100); + + /* Set the DAC and mixer output bias */ + snd_soc_component_write(component, WM8900_REG_OUTBIASCTL, 0x81); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8900 = { + .probe = wm8900_probe, + .suspend = wm8900_suspend, + .resume = wm8900_resume, + .set_bias_level = wm8900_set_bias_level, + .controls = wm8900_snd_controls, + .num_controls = ARRAY_SIZE(wm8900_snd_controls), + .dapm_widgets = wm8900_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8900_dapm_widgets), + .dapm_routes = wm8900_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8900_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8900_regmap = { + .reg_bits = 8, + .val_bits = 16, + .max_register = WM8900_MAXREG, + + .reg_defaults = wm8900_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8900_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm8900_volatile_register, +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8900_spi_probe(struct spi_device *spi) +{ + struct wm8900_priv *wm8900; + int ret; + + wm8900 = devm_kzalloc(&spi->dev, sizeof(struct wm8900_priv), + GFP_KERNEL); + if (wm8900 == NULL) + return -ENOMEM; + + wm8900->regmap = devm_regmap_init_spi(spi, &wm8900_regmap); + if (IS_ERR(wm8900->regmap)) + return PTR_ERR(wm8900->regmap); + + spi_set_drvdata(spi, wm8900); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8900, &wm8900_dai, 1); + + return ret; +} + +static struct spi_driver wm8900_spi_driver = { + .driver = { + .name = "wm8900", + }, + .probe = wm8900_spi_probe, +}; +#endif /* CONFIG_SPI_MASTER */ + +#if IS_ENABLED(CONFIG_I2C) +static int wm8900_i2c_probe(struct i2c_client *i2c) +{ + struct wm8900_priv *wm8900; + int ret; + + wm8900 = devm_kzalloc(&i2c->dev, sizeof(struct wm8900_priv), + GFP_KERNEL); + if (wm8900 == NULL) + return -ENOMEM; + + wm8900->regmap = devm_regmap_init_i2c(i2c, &wm8900_regmap); + if (IS_ERR(wm8900->regmap)) + return PTR_ERR(wm8900->regmap); + + i2c_set_clientdata(i2c, wm8900); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8900, &wm8900_dai, 1); + + return ret; +} + +static void wm8900_i2c_remove(struct i2c_client *client) +{} + +static const struct i2c_device_id wm8900_i2c_id[] = { + { "wm8900", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id); + +static struct i2c_driver wm8900_i2c_driver = { + .driver = { + .name = "wm8900", + }, + .probe = wm8900_i2c_probe, + .remove = wm8900_i2c_remove, + .id_table = wm8900_i2c_id, +}; +#endif + +static int __init wm8900_modinit(void) +{ + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8900_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8900 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8900_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8900 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8900_modinit); + +static void __exit wm8900_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8900_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8900_spi_driver); +#endif +} +module_exit(wm8900_exit); + +MODULE_DESCRIPTION("ASoC WM8900 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8900.h b/sound/soc/codecs/wm8900.h new file mode 100644 index 0000000000..7bc95409a9 --- /dev/null +++ b/sound/soc/codecs/wm8900.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8900.h -- WM890 Soc Audio driver + */ + +#ifndef _WM8900_H +#define _WM8900_H + +#define WM8900_FLL 1 + +#define WM8900_BCLK_DIV 1 +#define WM8900_ADC_CLKDIV 2 +#define WM8900_DAC_CLKDIV 3 +#define WM8900_ADC_LRCLK 4 +#define WM8900_DAC_LRCLK 5 +#define WM8900_OPCLK_DIV 6 +#define WM8900_LRCLK_MODE 7 + +#define WM8900_BCLK_DIV_1 0x00 +#define WM8900_BCLK_DIV_1_5 0x02 +#define WM8900_BCLK_DIV_2 0x04 +#define WM8900_BCLK_DIV_3 0x06 +#define WM8900_BCLK_DIV_4 0x08 +#define WM8900_BCLK_DIV_5_5 0x0a +#define WM8900_BCLK_DIV_6 0x0c +#define WM8900_BCLK_DIV_8 0x0e +#define WM8900_BCLK_DIV_11 0x10 +#define WM8900_BCLK_DIV_12 0x12 +#define WM8900_BCLK_DIV_16 0x14 +#define WM8900_BCLK_DIV_22 0x16 +#define WM8900_BCLK_DIV_24 0x18 +#define WM8900_BCLK_DIV_32 0x1a +#define WM8900_BCLK_DIV_44 0x1c +#define WM8900_BCLK_DIV_48 0x1e + +#define WM8900_ADC_CLKDIV_1 0x00 +#define WM8900_ADC_CLKDIV_1_5 0x20 +#define WM8900_ADC_CLKDIV_2 0x40 +#define WM8900_ADC_CLKDIV_3 0x60 +#define WM8900_ADC_CLKDIV_4 0x80 +#define WM8900_ADC_CLKDIV_5_5 0xa0 +#define WM8900_ADC_CLKDIV_6 0xc0 + +#define WM8900_DAC_CLKDIV_1 0x00 +#define WM8900_DAC_CLKDIV_1_5 0x04 +#define WM8900_DAC_CLKDIV_2 0x08 +#define WM8900_DAC_CLKDIV_3 0x0c +#define WM8900_DAC_CLKDIV_4 0x10 +#define WM8900_DAC_CLKDIV_5_5 0x14 +#define WM8900_DAC_CLKDIV_6 0x18 + +#endif diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c new file mode 100644 index 0000000000..84ae1102ac --- /dev/null +++ b/sound/soc/codecs/wm8903.c @@ -0,0 +1,2221 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8903.c -- WM8903 ALSA SoC Audio driver + * + * Copyright 2008-12 Wolfson Microelectronics + * Copyright 2011-2012 NVIDIA, Inc. + * + * Author: Mark Brown + * + * TODO: + * - TDM mode configuration. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8903.h" + +/* Register defaults at reset */ +static const struct reg_default wm8903_reg_defaults[] = { + { 4, 0x0018 }, /* R4 - Bias Control 0 */ + { 5, 0x0000 }, /* R5 - VMID Control 0 */ + { 6, 0x0000 }, /* R6 - Mic Bias Control 0 */ + { 8, 0x0001 }, /* R8 - Analogue DAC 0 */ + { 10, 0x0001 }, /* R10 - Analogue ADC 0 */ + { 12, 0x0000 }, /* R12 - Power Management 0 */ + { 13, 0x0000 }, /* R13 - Power Management 1 */ + { 14, 0x0000 }, /* R14 - Power Management 2 */ + { 15, 0x0000 }, /* R15 - Power Management 3 */ + { 16, 0x0000 }, /* R16 - Power Management 4 */ + { 17, 0x0000 }, /* R17 - Power Management 5 */ + { 18, 0x0000 }, /* R18 - Power Management 6 */ + { 20, 0x0400 }, /* R20 - Clock Rates 0 */ + { 21, 0x0D07 }, /* R21 - Clock Rates 1 */ + { 22, 0x0000 }, /* R22 - Clock Rates 2 */ + { 24, 0x0050 }, /* R24 - Audio Interface 0 */ + { 25, 0x0242 }, /* R25 - Audio Interface 1 */ + { 26, 0x0008 }, /* R26 - Audio Interface 2 */ + { 27, 0x0022 }, /* R27 - Audio Interface 3 */ + { 30, 0x00C0 }, /* R30 - DAC Digital Volume Left */ + { 31, 0x00C0 }, /* R31 - DAC Digital Volume Right */ + { 32, 0x0000 }, /* R32 - DAC Digital 0 */ + { 33, 0x0000 }, /* R33 - DAC Digital 1 */ + { 36, 0x00C0 }, /* R36 - ADC Digital Volume Left */ + { 37, 0x00C0 }, /* R37 - ADC Digital Volume Right */ + { 38, 0x0000 }, /* R38 - ADC Digital 0 */ + { 39, 0x0073 }, /* R39 - Digital Microphone 0 */ + { 40, 0x09BF }, /* R40 - DRC 0 */ + { 41, 0x3241 }, /* R41 - DRC 1 */ + { 42, 0x0020 }, /* R42 - DRC 2 */ + { 43, 0x0000 }, /* R43 - DRC 3 */ + { 44, 0x0085 }, /* R44 - Analogue Left Input 0 */ + { 45, 0x0085 }, /* R45 - Analogue Right Input 0 */ + { 46, 0x0044 }, /* R46 - Analogue Left Input 1 */ + { 47, 0x0044 }, /* R47 - Analogue Right Input 1 */ + { 50, 0x0008 }, /* R50 - Analogue Left Mix 0 */ + { 51, 0x0004 }, /* R51 - Analogue Right Mix 0 */ + { 52, 0x0000 }, /* R52 - Analogue Spk Mix Left 0 */ + { 53, 0x0000 }, /* R53 - Analogue Spk Mix Left 1 */ + { 54, 0x0000 }, /* R54 - Analogue Spk Mix Right 0 */ + { 55, 0x0000 }, /* R55 - Analogue Spk Mix Right 1 */ + { 57, 0x002D }, /* R57 - Analogue OUT1 Left */ + { 58, 0x002D }, /* R58 - Analogue OUT1 Right */ + { 59, 0x0039 }, /* R59 - Analogue OUT2 Left */ + { 60, 0x0039 }, /* R60 - Analogue OUT2 Right */ + { 62, 0x0139 }, /* R62 - Analogue OUT3 Left */ + { 63, 0x0139 }, /* R63 - Analogue OUT3 Right */ + { 64, 0x0000 }, /* R65 - Analogue SPK Output Control 0 */ + { 67, 0x0010 }, /* R67 - DC Servo 0 */ + { 69, 0x00A4 }, /* R69 - DC Servo 2 */ + { 90, 0x0000 }, /* R90 - Analogue HP 0 */ + { 94, 0x0000 }, /* R94 - Analogue Lineout 0 */ + { 98, 0x0000 }, /* R98 - Charge Pump 0 */ + { 104, 0x0000 }, /* R104 - Class W 0 */ + { 108, 0x0000 }, /* R108 - Write Sequencer 0 */ + { 109, 0x0000 }, /* R109 - Write Sequencer 1 */ + { 110, 0x0000 }, /* R110 - Write Sequencer 2 */ + { 111, 0x0000 }, /* R111 - Write Sequencer 3 */ + { 112, 0x0000 }, /* R112 - Write Sequencer 4 */ + { 114, 0x0000 }, /* R114 - Control Interface */ + { 116, 0x00A8 }, /* R116 - GPIO Control 1 */ + { 117, 0x00A8 }, /* R117 - GPIO Control 2 */ + { 118, 0x00A8 }, /* R118 - GPIO Control 3 */ + { 119, 0x0220 }, /* R119 - GPIO Control 4 */ + { 120, 0x01A0 }, /* R120 - GPIO Control 5 */ + { 122, 0xFFFF }, /* R122 - Interrupt Status 1 Mask */ + { 123, 0x0000 }, /* R123 - Interrupt Polarity 1 */ + { 126, 0x0000 }, /* R126 - Interrupt Control */ + { 129, 0x0000 }, /* R129 - Control Interface Test 1 */ + { 149, 0x6810 }, /* R149 - Charge Pump Test 1 */ + { 164, 0x0028 }, /* R164 - Clock Rate Test 4 */ + { 172, 0x0000 }, /* R172 - Analogue Output Bias 0 */ +}; + +#define WM8903_NUM_SUPPLIES 4 +static const char *wm8903_supply_names[WM8903_NUM_SUPPLIES] = { + "AVDD", + "CPVDD", + "DBVDD", + "DCVDD", +}; + +struct wm8903_priv { + struct wm8903_platform_data *pdata; + struct device *dev; + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8903_NUM_SUPPLIES]; + + int sysclk; + int irq; + + struct mutex lock; + int fs; + int deemph; + + int dcs_pending; + int dcs_cache[4]; + + /* Reference count */ + int class_w_users; + + struct snd_soc_jack *mic_jack; + int mic_det; + int mic_short; + int mic_last_report; + int mic_delay; + +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif +}; + +static bool wm8903_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8903_SW_RESET_AND_ID: + case WM8903_REVISION_NUMBER: + case WM8903_BIAS_CONTROL_0: + case WM8903_VMID_CONTROL_0: + case WM8903_MIC_BIAS_CONTROL_0: + case WM8903_ANALOGUE_DAC_0: + case WM8903_ANALOGUE_ADC_0: + case WM8903_POWER_MANAGEMENT_0: + case WM8903_POWER_MANAGEMENT_1: + case WM8903_POWER_MANAGEMENT_2: + case WM8903_POWER_MANAGEMENT_3: + case WM8903_POWER_MANAGEMENT_4: + case WM8903_POWER_MANAGEMENT_5: + case WM8903_POWER_MANAGEMENT_6: + case WM8903_CLOCK_RATES_0: + case WM8903_CLOCK_RATES_1: + case WM8903_CLOCK_RATES_2: + case WM8903_AUDIO_INTERFACE_0: + case WM8903_AUDIO_INTERFACE_1: + case WM8903_AUDIO_INTERFACE_2: + case WM8903_AUDIO_INTERFACE_3: + case WM8903_DAC_DIGITAL_VOLUME_LEFT: + case WM8903_DAC_DIGITAL_VOLUME_RIGHT: + case WM8903_DAC_DIGITAL_0: + case WM8903_DAC_DIGITAL_1: + case WM8903_ADC_DIGITAL_VOLUME_LEFT: + case WM8903_ADC_DIGITAL_VOLUME_RIGHT: + case WM8903_ADC_DIGITAL_0: + case WM8903_DIGITAL_MICROPHONE_0: + case WM8903_DRC_0: + case WM8903_DRC_1: + case WM8903_DRC_2: + case WM8903_DRC_3: + case WM8903_ANALOGUE_LEFT_INPUT_0: + case WM8903_ANALOGUE_RIGHT_INPUT_0: + case WM8903_ANALOGUE_LEFT_INPUT_1: + case WM8903_ANALOGUE_RIGHT_INPUT_1: + case WM8903_ANALOGUE_LEFT_MIX_0: + case WM8903_ANALOGUE_RIGHT_MIX_0: + case WM8903_ANALOGUE_SPK_MIX_LEFT_0: + case WM8903_ANALOGUE_SPK_MIX_LEFT_1: + case WM8903_ANALOGUE_SPK_MIX_RIGHT_0: + case WM8903_ANALOGUE_SPK_MIX_RIGHT_1: + case WM8903_ANALOGUE_OUT1_LEFT: + case WM8903_ANALOGUE_OUT1_RIGHT: + case WM8903_ANALOGUE_OUT2_LEFT: + case WM8903_ANALOGUE_OUT2_RIGHT: + case WM8903_ANALOGUE_OUT3_LEFT: + case WM8903_ANALOGUE_OUT3_RIGHT: + case WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0: + case WM8903_DC_SERVO_0: + case WM8903_DC_SERVO_2: + case WM8903_DC_SERVO_READBACK_1: + case WM8903_DC_SERVO_READBACK_2: + case WM8903_DC_SERVO_READBACK_3: + case WM8903_DC_SERVO_READBACK_4: + case WM8903_ANALOGUE_HP_0: + case WM8903_ANALOGUE_LINEOUT_0: + case WM8903_CHARGE_PUMP_0: + case WM8903_CLASS_W_0: + case WM8903_WRITE_SEQUENCER_0: + case WM8903_WRITE_SEQUENCER_1: + case WM8903_WRITE_SEQUENCER_2: + case WM8903_WRITE_SEQUENCER_3: + case WM8903_WRITE_SEQUENCER_4: + case WM8903_CONTROL_INTERFACE: + case WM8903_GPIO_CONTROL_1: + case WM8903_GPIO_CONTROL_2: + case WM8903_GPIO_CONTROL_3: + case WM8903_GPIO_CONTROL_4: + case WM8903_GPIO_CONTROL_5: + case WM8903_INTERRUPT_STATUS_1: + case WM8903_INTERRUPT_STATUS_1_MASK: + case WM8903_INTERRUPT_POLARITY_1: + case WM8903_INTERRUPT_CONTROL: + case WM8903_CLOCK_RATE_TEST_4: + case WM8903_ANALOGUE_OUTPUT_BIAS_0: + return true; + default: + return false; + } +} + +static bool wm8903_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8903_SW_RESET_AND_ID: + case WM8903_REVISION_NUMBER: + case WM8903_INTERRUPT_STATUS_1: + case WM8903_WRITE_SEQUENCER_4: + case WM8903_DC_SERVO_READBACK_1: + case WM8903_DC_SERVO_READBACK_2: + case WM8903_DC_SERVO_READBACK_3: + case WM8903_DC_SERVO_READBACK_4: + return true; + + default: + return false; + } +} + +static int wm8903_cp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + WARN_ON(event != SND_SOC_DAPM_POST_PMU); + mdelay(4); + + return 0; +} + +static int wm8903_dcs_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wm8903->dcs_pending |= 1 << w->shift; + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, WM8903_DC_SERVO_0, + 1 << w->shift, 0); + break; + } + + return 0; +} + +#define WM8903_DCS_MODE_WRITE_STOP 0 +#define WM8903_DCS_MODE_START_STOP 2 + +static void wm8903_seq_notifier(struct snd_soc_component *component, + enum snd_soc_dapm_type event, int subseq) +{ + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + int dcs_mode = WM8903_DCS_MODE_WRITE_STOP; + int i, val; + + /* Complete any pending DC servo starts */ + if (wm8903->dcs_pending) { + dev_dbg(component->dev, "Starting DC servo for %x\n", + wm8903->dcs_pending); + + /* If we've no cached values then we need to do startup */ + for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) { + if (!(wm8903->dcs_pending & (1 << i))) + continue; + + if (wm8903->dcs_cache[i]) { + dev_dbg(component->dev, + "Restore DC servo %d value %x\n", + 3 - i, wm8903->dcs_cache[i]); + + snd_soc_component_write(component, WM8903_DC_SERVO_4 + i, + wm8903->dcs_cache[i] & 0xff); + } else { + dev_dbg(component->dev, + "Calibrate DC servo %d\n", 3 - i); + dcs_mode = WM8903_DCS_MODE_START_STOP; + } + } + + /* Don't trust the cache for analogue */ + if (wm8903->class_w_users) + dcs_mode = WM8903_DCS_MODE_START_STOP; + + snd_soc_component_update_bits(component, WM8903_DC_SERVO_2, + WM8903_DCS_MODE_MASK, dcs_mode); + + snd_soc_component_update_bits(component, WM8903_DC_SERVO_0, + WM8903_DCS_ENA_MASK, wm8903->dcs_pending); + + switch (dcs_mode) { + case WM8903_DCS_MODE_WRITE_STOP: + break; + + case WM8903_DCS_MODE_START_STOP: + msleep(270); + + /* Cache the measured offsets for digital */ + if (wm8903->class_w_users) + break; + + for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) { + if (!(wm8903->dcs_pending & (1 << i))) + continue; + + val = snd_soc_component_read(component, + WM8903_DC_SERVO_READBACK_1 + i); + dev_dbg(component->dev, "DC servo %d: %x\n", + 3 - i, val); + wm8903->dcs_cache[i] = val; + } + break; + + default: + pr_warn("DCS mode %d delay not set\n", dcs_mode); + break; + } + + wm8903->dcs_pending = 0; + } +} + +/* + * When used with DAC outputs only the WM8903 charge pump supports + * operation in class W mode, providing very low power consumption + * when used with digital sources. Enable and disable this mode + * automatically depending on the mixer configuration. + * + * All the relevant controls are simple switches. + */ +static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + u16 reg; + int ret; + + reg = snd_soc_component_read(component, WM8903_CLASS_W_0); + + /* Turn it off if we're about to enable bypass */ + if (ucontrol->value.integer.value[0]) { + if (wm8903->class_w_users == 0) { + dev_dbg(component->dev, "Disabling Class W\n"); + snd_soc_component_write(component, WM8903_CLASS_W_0, reg & + ~(WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V)); + } + wm8903->class_w_users++; + } + + /* Implement the change */ + ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); + + /* If we've just disabled the last bypass path turn Class W on */ + if (!ucontrol->value.integer.value[0]) { + if (wm8903->class_w_users == 1) { + dev_dbg(component->dev, "Enabling Class W\n"); + snd_soc_component_write(component, WM8903_CLASS_W_0, reg | + WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V); + } + wm8903->class_w_users--; + } + + dev_dbg(component->dev, "Bypass use count now %d\n", + wm8903->class_w_users); + + return ret; +} + +#define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \ + SOC_SINGLE_EXT(xname, reg, shift, max, invert, \ + snd_soc_dapm_get_volsw, wm8903_class_w_put) + + +static int wm8903_deemph[] = { 0, 32000, 44100, 48000 }; + +static int wm8903_set_deemph(struct snd_soc_component *component) +{ + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8903->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(wm8903_deemph); i++) { + if (abs(wm8903_deemph[i] - wm8903->fs) < + abs(wm8903_deemph[best] - wm8903->fs)) + best = i; + } + + val = best << WM8903_DEEMPH_SHIFT; + } else { + best = 0; + val = 0; + } + + dev_dbg(component->dev, "Set deemphasis %d (%dHz)\n", + best, wm8903_deemph[best]); + + return snd_soc_component_update_bits(component, WM8903_DAC_DIGITAL_1, + WM8903_DEEMPH_MASK, val); +} + +static int wm8903_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wm8903->deemph; + + return 0; +} + +static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; + int ret = 0; + + if (deemph > 1) + return -EINVAL; + + mutex_lock(&wm8903->lock); + if (wm8903->deemph != deemph) { + wm8903->deemph = deemph; + + wm8903_set_deemph(component); + + ret = 1; + } + mutex_unlock(&wm8903->lock); + + return ret; +} + +/* ALSA can only do steps of .01dB */ +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); + +static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0); + +static const DECLARE_TLV_DB_SCALE(digital_sidetone_tlv, -3600, 300, 0); +static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); + +static const DECLARE_TLV_DB_SCALE(drc_tlv_thresh, 0, 75, 0); +static const DECLARE_TLV_DB_SCALE(drc_tlv_amp, -2250, 75, 0); +static const DECLARE_TLV_DB_SCALE(drc_tlv_min, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(drc_tlv_max, 1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(drc_tlv_startup, -300, 50, 0); + +static const char *hpf_mode_text[] = { + "Hi-fi", "Voice 1", "Voice 2", "Voice 3" +}; + +static SOC_ENUM_SINGLE_DECL(hpf_mode, + WM8903_ADC_DIGITAL_0, 5, hpf_mode_text); + +static const char *osr_text[] = { + "Low power", "High performance" +}; + +static SOC_ENUM_SINGLE_DECL(adc_osr, + WM8903_ANALOGUE_ADC_0, 0, osr_text); + +static SOC_ENUM_SINGLE_DECL(dac_osr, + WM8903_DAC_DIGITAL_1, 0, osr_text); + +static const char *drc_slope_text[] = { + "1", "1/2", "1/4", "1/8", "1/16", "0" +}; + +static SOC_ENUM_SINGLE_DECL(drc_slope_r0, + WM8903_DRC_2, 3, drc_slope_text); + +static SOC_ENUM_SINGLE_DECL(drc_slope_r1, + WM8903_DRC_2, 0, drc_slope_text); + +static const char *drc_attack_text[] = { + "instantaneous", + "363us", "762us", "1.45ms", "2.9ms", "5.8ms", "11.6ms", "23.2ms", + "46.4ms", "92.8ms", "185.6ms" +}; + +static SOC_ENUM_SINGLE_DECL(drc_attack, + WM8903_DRC_1, 12, drc_attack_text); + +static const char *drc_decay_text[] = { + "186ms", "372ms", "743ms", "1.49s", "2.97s", "5.94s", "11.89s", + "23.87s", "47.56s" +}; + +static SOC_ENUM_SINGLE_DECL(drc_decay, + WM8903_DRC_1, 8, drc_decay_text); + +static const char *drc_ff_delay_text[] = { + "5 samples", "9 samples" +}; + +static SOC_ENUM_SINGLE_DECL(drc_ff_delay, + WM8903_DRC_0, 5, drc_ff_delay_text); + +static const char *drc_qr_decay_text[] = { + "0.725ms", "1.45ms", "5.8ms" +}; + +static SOC_ENUM_SINGLE_DECL(drc_qr_decay, + WM8903_DRC_1, 4, drc_qr_decay_text); + +static const char *drc_smoothing_text[] = { + "Low", "Medium", "High" +}; + +static SOC_ENUM_SINGLE_DECL(drc_smoothing, + WM8903_DRC_0, 11, drc_smoothing_text); + +static const char *soft_mute_text[] = { + "Fast (fs/2)", "Slow (fs/32)" +}; + +static SOC_ENUM_SINGLE_DECL(soft_mute, + WM8903_DAC_DIGITAL_1, 10, soft_mute_text); + +static const char *mute_mode_text[] = { + "Hard", "Soft" +}; + +static SOC_ENUM_SINGLE_DECL(mute_mode, + WM8903_DAC_DIGITAL_1, 9, mute_mode_text); + +static const char *companding_text[] = { + "ulaw", "alaw" +}; + +static SOC_ENUM_SINGLE_DECL(dac_companding, + WM8903_AUDIO_INTERFACE_0, 0, companding_text); + +static SOC_ENUM_SINGLE_DECL(adc_companding, + WM8903_AUDIO_INTERFACE_0, 2, companding_text); + +static const char *input_mode_text[] = { + "Single-Ended", "Differential Line", "Differential Mic" +}; + +static SOC_ENUM_SINGLE_DECL(linput_mode_enum, + WM8903_ANALOGUE_LEFT_INPUT_1, 0, input_mode_text); + +static SOC_ENUM_SINGLE_DECL(rinput_mode_enum, + WM8903_ANALOGUE_RIGHT_INPUT_1, 0, input_mode_text); + +static const char *linput_mux_text[] = { + "IN1L", "IN2L", "IN3L" +}; + +static SOC_ENUM_SINGLE_DECL(linput_enum, + WM8903_ANALOGUE_LEFT_INPUT_1, 2, linput_mux_text); + +static SOC_ENUM_SINGLE_DECL(linput_inv_enum, + WM8903_ANALOGUE_LEFT_INPUT_1, 4, linput_mux_text); + +static const char *rinput_mux_text[] = { + "IN1R", "IN2R", "IN3R" +}; + +static SOC_ENUM_SINGLE_DECL(rinput_enum, + WM8903_ANALOGUE_RIGHT_INPUT_1, 2, rinput_mux_text); + +static SOC_ENUM_SINGLE_DECL(rinput_inv_enum, + WM8903_ANALOGUE_RIGHT_INPUT_1, 4, rinput_mux_text); + + +static const char *sidetone_text[] = { + "None", "Left", "Right" +}; + +static SOC_ENUM_SINGLE_DECL(lsidetone_enum, + WM8903_DAC_DIGITAL_0, 2, sidetone_text); + +static SOC_ENUM_SINGLE_DECL(rsidetone_enum, + WM8903_DAC_DIGITAL_0, 0, sidetone_text); + +static const char *adcinput_text[] = { + "ADC", "DMIC" +}; + +static SOC_ENUM_SINGLE_DECL(adcinput_enum, + WM8903_CLOCK_RATE_TEST_4, 9, adcinput_text); + +static const char *aif_text[] = { + "Left", "Right" +}; + +static SOC_ENUM_SINGLE_DECL(lcapture_enum, + WM8903_AUDIO_INTERFACE_0, 7, aif_text); + +static SOC_ENUM_SINGLE_DECL(rcapture_enum, + WM8903_AUDIO_INTERFACE_0, 6, aif_text); + +static SOC_ENUM_SINGLE_DECL(lplay_enum, + WM8903_AUDIO_INTERFACE_0, 5, aif_text); + +static SOC_ENUM_SINGLE_DECL(rplay_enum, + WM8903_AUDIO_INTERFACE_0, 4, aif_text); + +static const struct snd_kcontrol_new wm8903_snd_controls[] = { + +/* Input PGAs - No TLV since the scale depends on PGA mode */ +SOC_SINGLE("Left Input PGA Switch", WM8903_ANALOGUE_LEFT_INPUT_0, + 7, 1, 1), +SOC_SINGLE("Left Input PGA Volume", WM8903_ANALOGUE_LEFT_INPUT_0, + 0, 31, 0), +SOC_SINGLE("Left Input PGA Common Mode Switch", WM8903_ANALOGUE_LEFT_INPUT_1, + 6, 1, 0), + +SOC_SINGLE("Right Input PGA Switch", WM8903_ANALOGUE_RIGHT_INPUT_0, + 7, 1, 1), +SOC_SINGLE("Right Input PGA Volume", WM8903_ANALOGUE_RIGHT_INPUT_0, + 0, 31, 0), +SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1, + 6, 1, 0), + +/* ADCs */ +SOC_ENUM("ADC OSR", adc_osr), +SOC_SINGLE("HPF Switch", WM8903_ADC_DIGITAL_0, 4, 1, 0), +SOC_ENUM("HPF Mode", hpf_mode), +SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0), +SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0), +SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1), +SOC_SINGLE_TLV("DRC Compressor Threshold Volume", WM8903_DRC_3, 5, 124, 1, + drc_tlv_thresh), +SOC_SINGLE_TLV("DRC Volume", WM8903_DRC_3, 0, 30, 1, drc_tlv_amp), +SOC_SINGLE_TLV("DRC Minimum Gain Volume", WM8903_DRC_1, 2, 3, 1, drc_tlv_min), +SOC_SINGLE_TLV("DRC Maximum Gain Volume", WM8903_DRC_1, 0, 3, 0, drc_tlv_max), +SOC_ENUM("DRC Attack Rate", drc_attack), +SOC_ENUM("DRC Decay Rate", drc_decay), +SOC_ENUM("DRC FF Delay", drc_ff_delay), +SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0), +SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0), +SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max), +SOC_ENUM("DRC QR Decay Rate", drc_qr_decay), +SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0), +SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0), +SOC_ENUM("DRC Smoothing Threshold", drc_smoothing), +SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup), + +SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT, + WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv), +SOC_ENUM("ADC Companding Mode", adc_companding), +SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0), + +SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8903_DAC_DIGITAL_0, 4, 8, + 12, 0, digital_sidetone_tlv), + +/* DAC */ +SOC_ENUM("DAC OSR", dac_osr), +SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT, + WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv), +SOC_ENUM("DAC Soft Mute Rate", soft_mute), +SOC_ENUM("DAC Mute Mode", mute_mode), +SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0), +SOC_ENUM("DAC Companding Mode", dac_companding), +SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0), +SOC_SINGLE_TLV("DAC Boost Volume", WM8903_AUDIO_INTERFACE_0, 9, 3, 0, + dac_boost_tlv), +SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, + wm8903_get_deemph, wm8903_put_deemph), + +/* Headphones */ +SOC_DOUBLE_R("Headphone Switch", + WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, + 8, 1, 1), +SOC_DOUBLE_R("Headphone ZC Switch", + WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, + 6, 1, 0), +SOC_DOUBLE_R_TLV("Headphone Volume", + WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, + 0, 63, 0, out_tlv), + +/* Line out */ +SOC_DOUBLE_R("Line Out Switch", + WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, + 8, 1, 1), +SOC_DOUBLE_R("Line Out ZC Switch", + WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, + 6, 1, 0), +SOC_DOUBLE_R_TLV("Line Out Volume", + WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, + 0, 63, 0, out_tlv), + +/* Speaker */ +SOC_DOUBLE_R("Speaker Switch", + WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 8, 1, 1), +SOC_DOUBLE_R("Speaker ZC Switch", + WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 6, 1, 0), +SOC_DOUBLE_R_TLV("Speaker Volume", + WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, + 0, 63, 0, out_tlv), +}; + +static const struct snd_kcontrol_new linput_mode_mux = + SOC_DAPM_ENUM("Left Input Mode Mux", linput_mode_enum); + +static const struct snd_kcontrol_new rinput_mode_mux = + SOC_DAPM_ENUM("Right Input Mode Mux", rinput_mode_enum); + +static const struct snd_kcontrol_new linput_mux = + SOC_DAPM_ENUM("Left Input Mux", linput_enum); + +static const struct snd_kcontrol_new linput_inv_mux = + SOC_DAPM_ENUM("Left Inverting Input Mux", linput_inv_enum); + +static const struct snd_kcontrol_new rinput_mux = + SOC_DAPM_ENUM("Right Input Mux", rinput_enum); + +static const struct snd_kcontrol_new rinput_inv_mux = + SOC_DAPM_ENUM("Right Inverting Input Mux", rinput_inv_enum); + +static const struct snd_kcontrol_new lsidetone_mux = + SOC_DAPM_ENUM("DACL Sidetone Mux", lsidetone_enum); + +static const struct snd_kcontrol_new rsidetone_mux = + SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum); + +static const struct snd_kcontrol_new adcinput_mux = + SOC_DAPM_ENUM("ADC Input", adcinput_enum); + +static const struct snd_kcontrol_new lcapture_mux = + SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum); + +static const struct snd_kcontrol_new rcapture_mux = + SOC_DAPM_ENUM("Right Capture Mux", rcapture_enum); + +static const struct snd_kcontrol_new lplay_mux = + SOC_DAPM_ENUM("Left Playback Mux", lplay_enum); + +static const struct snd_kcontrol_new rplay_mux = + SOC_DAPM_ENUM("Right Playback Mux", rplay_enum); + +static const struct snd_kcontrol_new left_output_mixer[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0), +SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0), +SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_output_mixer[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0), +SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0), +SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 0, 1, 0), +}; + +static const struct snd_kcontrol_new left_speaker_mixer[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_speaker_mixer[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 3, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, + 1, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, + 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), +SND_SOC_DAPM_INPUT("IN3L"), +SND_SOC_DAPM_INPUT("IN3R"), +SND_SOC_DAPM_INPUT("DMICDAT"), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("LINEOUTL"), +SND_SOC_DAPM_OUTPUT("LINEOUTR"), +SND_SOC_DAPM_OUTPUT("LOP"), +SND_SOC_DAPM_OUTPUT("LON"), +SND_SOC_DAPM_OUTPUT("ROP"), +SND_SOC_DAPM_OUTPUT("RON"), + +SND_SOC_DAPM_SUPPLY("MICBIAS", WM8903_MIC_BIAS_CONTROL_0, 0, 0, NULL, 0), + +SND_SOC_DAPM_MUX("Left Input Mux", SND_SOC_NOPM, 0, 0, &linput_mux), +SND_SOC_DAPM_MUX("Left Input Inverting Mux", SND_SOC_NOPM, 0, 0, + &linput_inv_mux), +SND_SOC_DAPM_MUX("Left Input Mode Mux", SND_SOC_NOPM, 0, 0, &linput_mode_mux), + +SND_SOC_DAPM_MUX("Right Input Mux", SND_SOC_NOPM, 0, 0, &rinput_mux), +SND_SOC_DAPM_MUX("Right Input Inverting Mux", SND_SOC_NOPM, 0, 0, + &rinput_inv_mux), +SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux), + +SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0), + +SND_SOC_DAPM_MUX("Left ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux), +SND_SOC_DAPM_MUX("Right ADC Input", SND_SOC_NOPM, 0, 0, &adcinput_mux), + +SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0), + +SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lcapture_mux), +SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rcapture_mux), + +SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux), +SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux), + +SND_SOC_DAPM_AIF_IN("AIFRXL", "Left Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFRXR", "Right Playback", 0, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_MUX("Left Playback Mux", SND_SOC_NOPM, 0, 0, &lplay_mux), +SND_SOC_DAPM_MUX("Right Playback Mux", SND_SOC_NOPM, 0, 0, &rplay_mux), + +SND_SOC_DAPM_DAC("DACL", NULL, WM8903_POWER_MANAGEMENT_6, 3, 0), +SND_SOC_DAPM_DAC("DACR", NULL, WM8903_POWER_MANAGEMENT_6, 2, 0), + +SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0, + left_output_mixer, ARRAY_SIZE(left_output_mixer)), +SND_SOC_DAPM_MIXER("Right Output Mixer", WM8903_POWER_MANAGEMENT_1, 0, 0, + right_output_mixer, ARRAY_SIZE(right_output_mixer)), + +SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0, + left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), +SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0, + right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), + +SND_SOC_DAPM_PGA_S("Left Headphone Output PGA", 0, WM8903_POWER_MANAGEMENT_2, + 1, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("Right Headphone Output PGA", 0, WM8903_POWER_MANAGEMENT_2, + 0, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("Left Line Output PGA", 0, WM8903_POWER_MANAGEMENT_3, 1, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("Right Line Output PGA", 0, WM8903_POWER_MANAGEMENT_3, 0, 0, + NULL, 0), + +SND_SOC_DAPM_PGA_S("HPL_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_ENA_DLY", 2, WM8903_ANALOGUE_HP_0, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_ENA", 1, WM8903_ANALOGUE_HP_0, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 3, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_ENA_DLY", 2, WM8903_ANALOGUE_HP_0, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_ENA", 1, WM8903_ANALOGUE_HP_0, 0, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("LINEOUTL_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 7, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 6, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_DLY", 2, WM8903_ANALOGUE_LINEOUT_0, 5, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTL_ENA", 1, WM8903_ANALOGUE_LINEOUT_0, 4, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 3, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 2, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 2, WM8903_ANALOGUE_LINEOUT_0, 1, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_ENA", 1, WM8903_ANALOGUE_LINEOUT_0, 0, 0, + NULL, 0), + +SND_SOC_DAPM_SUPPLY("DCS Master", WM8903_DC_SERVO_0, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_DCS", 3, SND_SOC_NOPM, 3, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("HPR_DCS", 3, SND_SOC_NOPM, 2, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("LINEOUTL_DCS", 3, SND_SOC_NOPM, 1, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, SND_SOC_NOPM, 0, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0, + NULL, 0), +SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0, + NULL, 0), + +SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0, + wm8903_cp_event, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route wm8903_intercon[] = { + + { "CLK_DSP", NULL, "CLK_SYS" }, + { "MICBIAS", NULL, "CLK_SYS" }, + { "HPL_DCS", NULL, "CLK_SYS" }, + { "HPR_DCS", NULL, "CLK_SYS" }, + { "LINEOUTL_DCS", NULL, "CLK_SYS" }, + { "LINEOUTR_DCS", NULL, "CLK_SYS" }, + + { "Left Input Mux", "IN1L", "IN1L" }, + { "Left Input Mux", "IN2L", "IN2L" }, + { "Left Input Mux", "IN3L", "IN3L" }, + + { "Left Input Inverting Mux", "IN1L", "IN1L" }, + { "Left Input Inverting Mux", "IN2L", "IN2L" }, + { "Left Input Inverting Mux", "IN3L", "IN3L" }, + + { "Right Input Mux", "IN1R", "IN1R" }, + { "Right Input Mux", "IN2R", "IN2R" }, + { "Right Input Mux", "IN3R", "IN3R" }, + + { "Right Input Inverting Mux", "IN1R", "IN1R" }, + { "Right Input Inverting Mux", "IN2R", "IN2R" }, + { "Right Input Inverting Mux", "IN3R", "IN3R" }, + + { "Left Input Mode Mux", "Single-Ended", "Left Input Inverting Mux" }, + { "Left Input Mode Mux", "Differential Line", + "Left Input Mux" }, + { "Left Input Mode Mux", "Differential Line", + "Left Input Inverting Mux" }, + { "Left Input Mode Mux", "Differential Mic", + "Left Input Mux" }, + { "Left Input Mode Mux", "Differential Mic", + "Left Input Inverting Mux" }, + + { "Right Input Mode Mux", "Single-Ended", + "Right Input Inverting Mux" }, + { "Right Input Mode Mux", "Differential Line", + "Right Input Mux" }, + { "Right Input Mode Mux", "Differential Line", + "Right Input Inverting Mux" }, + { "Right Input Mode Mux", "Differential Mic", + "Right Input Mux" }, + { "Right Input Mode Mux", "Differential Mic", + "Right Input Inverting Mux" }, + + { "Left Input PGA", NULL, "Left Input Mode Mux" }, + { "Right Input PGA", NULL, "Right Input Mode Mux" }, + + { "Left ADC Input", "ADC", "Left Input PGA" }, + { "Left ADC Input", "DMIC", "DMICDAT" }, + { "Right ADC Input", "ADC", "Right Input PGA" }, + { "Right ADC Input", "DMIC", "DMICDAT" }, + + { "Left Capture Mux", "Left", "ADCL" }, + { "Left Capture Mux", "Right", "ADCR" }, + + { "Right Capture Mux", "Left", "ADCL" }, + { "Right Capture Mux", "Right", "ADCR" }, + + { "AIFTXL", NULL, "Left Capture Mux" }, + { "AIFTXR", NULL, "Right Capture Mux" }, + + { "ADCL", NULL, "Left ADC Input" }, + { "ADCL", NULL, "CLK_DSP" }, + { "ADCR", NULL, "Right ADC Input" }, + { "ADCR", NULL, "CLK_DSP" }, + + { "Left Playback Mux", "Left", "AIFRXL" }, + { "Left Playback Mux", "Right", "AIFRXR" }, + + { "Right Playback Mux", "Left", "AIFRXL" }, + { "Right Playback Mux", "Right", "AIFRXR" }, + + { "DACL Sidetone", "Left", "ADCL" }, + { "DACL Sidetone", "Right", "ADCR" }, + { "DACR Sidetone", "Left", "ADCL" }, + { "DACR Sidetone", "Right", "ADCR" }, + + { "DACL", NULL, "Left Playback Mux" }, + { "DACL", NULL, "DACL Sidetone" }, + { "DACL", NULL, "CLK_DSP" }, + + { "DACR", NULL, "Right Playback Mux" }, + { "DACR", NULL, "DACR Sidetone" }, + { "DACR", NULL, "CLK_DSP" }, + + { "Left Output Mixer", "Left Bypass Switch", "Left Input PGA" }, + { "Left Output Mixer", "Right Bypass Switch", "Right Input PGA" }, + { "Left Output Mixer", "DACL Switch", "DACL" }, + { "Left Output Mixer", "DACR Switch", "DACR" }, + + { "Right Output Mixer", "Left Bypass Switch", "Left Input PGA" }, + { "Right Output Mixer", "Right Bypass Switch", "Right Input PGA" }, + { "Right Output Mixer", "DACL Switch", "DACL" }, + { "Right Output Mixer", "DACR Switch", "DACR" }, + + { "Left Speaker Mixer", "Left Bypass Switch", "Left Input PGA" }, + { "Left Speaker Mixer", "Right Bypass Switch", "Right Input PGA" }, + { "Left Speaker Mixer", "DACL Switch", "DACL" }, + { "Left Speaker Mixer", "DACR Switch", "DACR" }, + + { "Right Speaker Mixer", "Left Bypass Switch", "Left Input PGA" }, + { "Right Speaker Mixer", "Right Bypass Switch", "Right Input PGA" }, + { "Right Speaker Mixer", "DACL Switch", "DACL" }, + { "Right Speaker Mixer", "DACR Switch", "DACR" }, + + { "Left Line Output PGA", NULL, "Left Output Mixer" }, + { "Right Line Output PGA", NULL, "Right Output Mixer" }, + + { "Left Headphone Output PGA", NULL, "Left Output Mixer" }, + { "Right Headphone Output PGA", NULL, "Right Output Mixer" }, + + { "Left Speaker PGA", NULL, "Left Speaker Mixer" }, + { "Right Speaker PGA", NULL, "Right Speaker Mixer" }, + + { "HPL_ENA", NULL, "Left Headphone Output PGA" }, + { "HPR_ENA", NULL, "Right Headphone Output PGA" }, + { "HPL_ENA_DLY", NULL, "HPL_ENA" }, + { "HPR_ENA_DLY", NULL, "HPR_ENA" }, + { "LINEOUTL_ENA", NULL, "Left Line Output PGA" }, + { "LINEOUTR_ENA", NULL, "Right Line Output PGA" }, + { "LINEOUTL_ENA_DLY", NULL, "LINEOUTL_ENA" }, + { "LINEOUTR_ENA_DLY", NULL, "LINEOUTR_ENA" }, + + { "HPL_DCS", NULL, "DCS Master" }, + { "HPR_DCS", NULL, "DCS Master" }, + { "LINEOUTL_DCS", NULL, "DCS Master" }, + { "LINEOUTR_DCS", NULL, "DCS Master" }, + + { "HPL_DCS", NULL, "HPL_ENA_DLY" }, + { "HPR_DCS", NULL, "HPR_ENA_DLY" }, + { "LINEOUTL_DCS", NULL, "LINEOUTL_ENA_DLY" }, + { "LINEOUTR_DCS", NULL, "LINEOUTR_ENA_DLY" }, + + { "HPL_ENA_OUTP", NULL, "HPL_DCS" }, + { "HPR_ENA_OUTP", NULL, "HPR_DCS" }, + { "LINEOUTL_ENA_OUTP", NULL, "LINEOUTL_DCS" }, + { "LINEOUTR_ENA_OUTP", NULL, "LINEOUTR_DCS" }, + + { "HPL_RMV_SHORT", NULL, "HPL_ENA_OUTP" }, + { "HPR_RMV_SHORT", NULL, "HPR_ENA_OUTP" }, + { "LINEOUTL_RMV_SHORT", NULL, "LINEOUTL_ENA_OUTP" }, + { "LINEOUTR_RMV_SHORT", NULL, "LINEOUTR_ENA_OUTP" }, + + { "HPOUTL", NULL, "HPL_RMV_SHORT" }, + { "HPOUTR", NULL, "HPR_RMV_SHORT" }, + { "LINEOUTL", NULL, "LINEOUTL_RMV_SHORT" }, + { "LINEOUTR", NULL, "LINEOUTR_RMV_SHORT" }, + + { "LOP", NULL, "Left Speaker PGA" }, + { "LON", NULL, "Left Speaker PGA" }, + + { "ROP", NULL, "Right Speaker PGA" }, + { "RON", NULL, "Right Speaker PGA" }, + + { "Charge Pump", NULL, "CLK_DSP" }, + + { "Left Headphone Output PGA", NULL, "Charge Pump" }, + { "Right Headphone Output PGA", NULL, "Charge Pump" }, + { "Left Line Output PGA", NULL, "Charge Pump" }, + { "Right Line Output PGA", NULL, "Charge Pump" }, +}; + +static int wm8903_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_50K); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + snd_soc_component_update_bits(component, WM8903_BIAS_CONTROL_0, + WM8903_POBCTRL | WM8903_ISEL_MASK | + WM8903_STARTUP_BIAS_ENA | + WM8903_BIAS_ENA, + WM8903_POBCTRL | + (2 << WM8903_ISEL_SHIFT) | + WM8903_STARTUP_BIAS_ENA); + + snd_soc_component_update_bits(component, + WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0, + WM8903_SPK_DISCHARGE, + WM8903_SPK_DISCHARGE); + + msleep(33); + + snd_soc_component_update_bits(component, WM8903_POWER_MANAGEMENT_5, + WM8903_SPKL_ENA | WM8903_SPKR_ENA, + WM8903_SPKL_ENA | WM8903_SPKR_ENA); + + snd_soc_component_update_bits(component, + WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0, + WM8903_SPK_DISCHARGE, 0); + + snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0, + WM8903_VMID_TIE_ENA | + WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | + WM8903_VMID_SOFT_MASK | + WM8903_VMID_RES_MASK | + WM8903_VMID_BUF_ENA, + WM8903_VMID_TIE_ENA | + WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | + (2 << WM8903_VMID_SOFT_SHIFT) | + WM8903_VMID_RES_250K | + WM8903_VMID_BUF_ENA); + + msleep(129); + + snd_soc_component_update_bits(component, WM8903_POWER_MANAGEMENT_5, + WM8903_SPKL_ENA | WM8903_SPKR_ENA, + 0); + + snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0, + WM8903_VMID_SOFT_MASK, 0); + + snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_50K); + + snd_soc_component_update_bits(component, WM8903_BIAS_CONTROL_0, + WM8903_BIAS_ENA | WM8903_POBCTRL, + WM8903_BIAS_ENA); + + /* By default no bypass paths are enabled so + * enable Class W support. + */ + dev_dbg(component->dev, "Enabling Class W\n"); + snd_soc_component_update_bits(component, WM8903_CLASS_W_0, + WM8903_CP_DYN_FREQ | + WM8903_CP_DYN_V, + WM8903_CP_DYN_FREQ | + WM8903_CP_DYN_V); + } + + snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_250K); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, WM8903_BIAS_CONTROL_0, + WM8903_BIAS_ENA, 0); + + snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0, + WM8903_VMID_SOFT_MASK, + 2 << WM8903_VMID_SOFT_SHIFT); + + snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0, + WM8903_VMID_BUF_ENA, 0); + + msleep(290); + + snd_soc_component_update_bits(component, WM8903_VMID_CONTROL_0, + WM8903_VMID_TIE_ENA | WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | WM8903_VMID_RES_MASK | + WM8903_VMID_SOFT_MASK | + WM8903_VMID_BUF_ENA, 0); + + snd_soc_component_update_bits(component, WM8903_BIAS_CONTROL_0, + WM8903_STARTUP_BIAS_ENA, 0); + break; + } + + return 0; +} + +static int wm8903_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + + wm8903->sysclk = freq; + + return 0; +} + +static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 aif1 = snd_soc_component_read(component, WM8903_AUDIO_INTERFACE_1); + + aif1 &= ~(WM8903_LRCLK_DIR | WM8903_BCLK_DIR | WM8903_AIF_FMT_MASK | + WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBS_CFM: + aif1 |= WM8903_LRCLK_DIR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + aif1 |= WM8903_LRCLK_DIR | WM8903_BCLK_DIR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + aif1 |= WM8903_BCLK_DIR; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + aif1 |= 0x3; + break; + case SND_SOC_DAIFMT_DSP_B: + aif1 |= 0x3 | WM8903_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_I2S: + aif1 |= 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + aif1 |= 0x1; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + /* Clock inversion */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8903_AIF_BCLK_INV; + break; + default: + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif1 |= WM8903_AIF_BCLK_INV | WM8903_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8903_AIF_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif1 |= WM8903_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8903_AUDIO_INTERFACE_1, aif1); + + return 0; +} + +static int wm8903_mute(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + + reg = snd_soc_component_read(component, WM8903_DAC_DIGITAL_1); + + if (mute) + reg |= WM8903_DAC_MUTE; + else + reg &= ~WM8903_DAC_MUTE; + + snd_soc_component_write(component, WM8903_DAC_DIGITAL_1, reg); + + return 0; +} + +/* Lookup table for CLK_SYS/fs ratio. 256fs or more is recommended + * for optimal performance so we list the lower rates first and match + * on the last match we find. */ +static struct { + int div; + int rate; + int mode; + int mclk_div; +} clk_sys_ratios[] = { + { 64, 0x0, 0x0, 1 }, + { 68, 0x0, 0x1, 1 }, + { 125, 0x0, 0x2, 1 }, + { 128, 0x1, 0x0, 1 }, + { 136, 0x1, 0x1, 1 }, + { 192, 0x2, 0x0, 1 }, + { 204, 0x2, 0x1, 1 }, + + { 64, 0x0, 0x0, 2 }, + { 68, 0x0, 0x1, 2 }, + { 125, 0x0, 0x2, 2 }, + { 128, 0x1, 0x0, 2 }, + { 136, 0x1, 0x1, 2 }, + { 192, 0x2, 0x0, 2 }, + { 204, 0x2, 0x1, 2 }, + + { 250, 0x2, 0x2, 1 }, + { 256, 0x3, 0x0, 1 }, + { 272, 0x3, 0x1, 1 }, + { 384, 0x4, 0x0, 1 }, + { 408, 0x4, 0x1, 1 }, + { 375, 0x4, 0x2, 1 }, + { 512, 0x5, 0x0, 1 }, + { 544, 0x5, 0x1, 1 }, + { 500, 0x5, 0x2, 1 }, + { 768, 0x6, 0x0, 1 }, + { 816, 0x6, 0x1, 1 }, + { 750, 0x6, 0x2, 1 }, + { 1024, 0x7, 0x0, 1 }, + { 1088, 0x7, 0x1, 1 }, + { 1000, 0x7, 0x2, 1 }, + { 1408, 0x8, 0x0, 1 }, + { 1496, 0x8, 0x1, 1 }, + { 1536, 0x9, 0x0, 1 }, + { 1632, 0x9, 0x1, 1 }, + { 1500, 0x9, 0x2, 1 }, + + { 250, 0x2, 0x2, 2 }, + { 256, 0x3, 0x0, 2 }, + { 272, 0x3, 0x1, 2 }, + { 384, 0x4, 0x0, 2 }, + { 408, 0x4, 0x1, 2 }, + { 375, 0x4, 0x2, 2 }, + { 512, 0x5, 0x0, 2 }, + { 544, 0x5, 0x1, 2 }, + { 500, 0x5, 0x2, 2 }, + { 768, 0x6, 0x0, 2 }, + { 816, 0x6, 0x1, 2 }, + { 750, 0x6, 0x2, 2 }, + { 1024, 0x7, 0x0, 2 }, + { 1088, 0x7, 0x1, 2 }, + { 1000, 0x7, 0x2, 2 }, + { 1408, 0x8, 0x0, 2 }, + { 1496, 0x8, 0x1, 2 }, + { 1536, 0x9, 0x0, 2 }, + { 1632, 0x9, 0x1, 2 }, + { 1500, 0x9, 0x2, 2 }, +}; + +/* CLK_SYS/BCLK ratios - multiplied by 10 due to .5s */ +static struct { + int ratio; + int div; +} bclk_divs[] = { + { 10, 0 }, + { 20, 2 }, + { 30, 3 }, + { 40, 4 }, + { 50, 5 }, + { 60, 7 }, + { 80, 8 }, + { 100, 9 }, + { 120, 11 }, + { 160, 12 }, + { 200, 13 }, + { 220, 14 }, + { 240, 15 }, + { 300, 17 }, + { 320, 18 }, + { 440, 19 }, + { 480, 20 }, +}; + +/* Sample rates for DSP */ +static struct { + int rate; + int value; +} sample_rates[] = { + { 8000, 0 }, + { 11025, 1 }, + { 12000, 2 }, + { 16000, 3 }, + { 22050, 4 }, + { 24000, 5 }, + { 32000, 6 }, + { 44100, 7 }, + { 48000, 8 }, + { 88200, 9 }, + { 96000, 10 }, + { 0, 0 }, +}; + +static int wm8903_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + int fs = params_rate(params); + int bclk; + int bclk_div; + int i; + int dsp_config; + int clk_config; + int best_val; + int cur_val; + int clk_sys; + + u16 aif1 = snd_soc_component_read(component, WM8903_AUDIO_INTERFACE_1); + u16 aif2 = snd_soc_component_read(component, WM8903_AUDIO_INTERFACE_2); + u16 aif3 = snd_soc_component_read(component, WM8903_AUDIO_INTERFACE_3); + u16 clock0 = snd_soc_component_read(component, WM8903_CLOCK_RATES_0); + u16 clock1 = snd_soc_component_read(component, WM8903_CLOCK_RATES_1); + u16 dac_digital1 = snd_soc_component_read(component, WM8903_DAC_DIGITAL_1); + + /* Enable sloping stopband filter for low sample rates */ + if (fs <= 24000) + dac_digital1 |= WM8903_DAC_SB_FILT; + else + dac_digital1 &= ~WM8903_DAC_SB_FILT; + + /* Configure sample rate logic for DSP - choose nearest rate */ + dsp_config = 0; + best_val = abs(sample_rates[dsp_config].rate - fs); + for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { + cur_val = abs(sample_rates[i].rate - fs); + if (cur_val <= best_val) { + dsp_config = i; + best_val = cur_val; + } + } + + dev_dbg(component->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate); + clock1 &= ~WM8903_SAMPLE_RATE_MASK; + clock1 |= sample_rates[dsp_config].value; + + aif1 &= ~WM8903_AIF_WL_MASK; + bclk = 2 * fs; + switch (params_width(params)) { + case 16: + bclk *= 16; + break; + case 20: + bclk *= 20; + aif1 |= 0x4; + break; + case 24: + bclk *= 24; + aif1 |= 0x8; + break; + case 32: + bclk *= 32; + aif1 |= 0xc; + break; + default: + return -EINVAL; + } + + dev_dbg(component->dev, "MCLK = %dHz, target sample rate = %dHz\n", + wm8903->sysclk, fs); + + /* We may not have an MCLK which allows us to generate exactly + * the clock we want, particularly with USB derived inputs, so + * approximate. + */ + clk_config = 0; + best_val = abs((wm8903->sysclk / + (clk_sys_ratios[0].mclk_div * + clk_sys_ratios[0].div)) - fs); + for (i = 1; i < ARRAY_SIZE(clk_sys_ratios); i++) { + cur_val = abs((wm8903->sysclk / + (clk_sys_ratios[i].mclk_div * + clk_sys_ratios[i].div)) - fs); + + if (cur_val <= best_val) { + clk_config = i; + best_val = cur_val; + } + } + + if (clk_sys_ratios[clk_config].mclk_div == 2) { + clock0 |= WM8903_MCLKDIV2; + clk_sys = wm8903->sysclk / 2; + } else { + clock0 &= ~WM8903_MCLKDIV2; + clk_sys = wm8903->sysclk; + } + + clock1 &= ~(WM8903_CLK_SYS_RATE_MASK | + WM8903_CLK_SYS_MODE_MASK); + clock1 |= clk_sys_ratios[clk_config].rate << WM8903_CLK_SYS_RATE_SHIFT; + clock1 |= clk_sys_ratios[clk_config].mode << WM8903_CLK_SYS_MODE_SHIFT; + + dev_dbg(component->dev, "CLK_SYS_RATE=%x, CLK_SYS_MODE=%x div=%d\n", + clk_sys_ratios[clk_config].rate, + clk_sys_ratios[clk_config].mode, + clk_sys_ratios[clk_config].div); + + dev_dbg(component->dev, "Actual CLK_SYS = %dHz\n", clk_sys); + + /* We may not get quite the right frequency if using + * approximate clocks so look for the closest match that is + * higher than the target (we need to ensure that there enough + * BCLKs to clock out the samples). + */ + bclk_div = 0; + i = 1; + while (i < ARRAY_SIZE(bclk_divs)) { + cur_val = ((clk_sys * 10) / bclk_divs[i].ratio) - bclk; + if (cur_val < 0) /* BCLK table is sorted */ + break; + bclk_div = i; + i++; + } + + aif2 &= ~WM8903_BCLK_DIV_MASK; + aif3 &= ~WM8903_LRCLK_RATE_MASK; + + dev_dbg(component->dev, "BCLK ratio %d for %dHz - actual BCLK = %dHz\n", + bclk_divs[bclk_div].ratio / 10, bclk, + (clk_sys * 10) / bclk_divs[bclk_div].ratio); + + aif2 |= bclk_divs[bclk_div].div; + aif3 |= bclk / fs; + + wm8903->fs = params_rate(params); + wm8903_set_deemph(component); + + snd_soc_component_write(component, WM8903_CLOCK_RATES_0, clock0); + snd_soc_component_write(component, WM8903_CLOCK_RATES_1, clock1); + snd_soc_component_write(component, WM8903_AUDIO_INTERFACE_1, aif1); + snd_soc_component_write(component, WM8903_AUDIO_INTERFACE_2, aif2); + snd_soc_component_write(component, WM8903_AUDIO_INTERFACE_3, aif3); + snd_soc_component_write(component, WM8903_DAC_DIGITAL_1, dac_digital1); + + return 0; +} + +/** + * wm8903_mic_detect - Enable microphone detection via the WM8903 IRQ + * + * @component: WM8903 component + * @jack: jack to report detection events on + * @det: value to report for presence detection + * @shrt: value to report for short detection + * + * Enable microphone detection via IRQ on the WM8903. If GPIOs are + * being used to bring out signals to the processor then only platform + * data configuration is needed for WM8903 and processor GPIOs should + * be configured using snd_soc_jack_add_gpios() instead. + * + * The current threasholds for detection should be configured using + * micdet_cfg in the platform data. Using this function will force on + * the microphone bias for the device. + */ +int wm8903_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack, + int det, int shrt) +{ + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + int irq_mask = WM8903_MICDET_EINT | WM8903_MICSHRT_EINT; + + dev_dbg(component->dev, "Enabling microphone detection: %x %x\n", + det, shrt); + + /* Store the configuration */ + wm8903->mic_jack = jack; + wm8903->mic_det = det; + wm8903->mic_short = shrt; + + /* Enable interrupts we've got a report configured for */ + if (det) + irq_mask &= ~WM8903_MICDET_EINT; + if (shrt) + irq_mask &= ~WM8903_MICSHRT_EINT; + + snd_soc_component_update_bits(component, WM8903_INTERRUPT_STATUS_1_MASK, + WM8903_MICDET_EINT | WM8903_MICSHRT_EINT, + irq_mask); + + if (det || shrt) { + /* Enable mic detection, this may not have been set through + * platform data (eg, if the defaults are OK). */ + snd_soc_component_update_bits(component, WM8903_WRITE_SEQUENCER_0, + WM8903_WSEQ_ENA, WM8903_WSEQ_ENA); + snd_soc_component_update_bits(component, WM8903_MIC_BIAS_CONTROL_0, + WM8903_MICDET_ENA, WM8903_MICDET_ENA); + } else { + snd_soc_component_update_bits(component, WM8903_MIC_BIAS_CONTROL_0, + WM8903_MICDET_ENA, 0); + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm8903_mic_detect); + +static irqreturn_t wm8903_irq(int irq, void *data) +{ + struct wm8903_priv *wm8903 = data; + int mic_report, ret; + unsigned int int_val, mask, int_pol; + + ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_STATUS_1_MASK, + &mask); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to read IRQ mask: %d\n", ret); + return IRQ_NONE; + } + + ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_STATUS_1, &int_val); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to read IRQ status: %d\n", ret); + return IRQ_NONE; + } + + int_val &= ~mask; + + if (int_val & WM8903_WSEQ_BUSY_EINT) { + dev_warn(wm8903->dev, "Write sequencer done\n"); + } + + /* + * The rest is microphone jack detection. We need to manually + * invert the polarity of the interrupt after each event - to + * simplify the code keep track of the last state we reported + * and just invert the relevant bits in both the report and + * the polarity register. + */ + mic_report = wm8903->mic_last_report; + ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_POLARITY_1, + &int_pol); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to read interrupt polarity: %d\n", + ret); + return IRQ_HANDLED; + } + +#ifndef CONFIG_SND_SOC_WM8903_MODULE + if (int_val & (WM8903_MICSHRT_EINT | WM8903_MICDET_EINT)) + trace_snd_soc_jack_irq(dev_name(wm8903->dev)); +#endif + + if (int_val & WM8903_MICSHRT_EINT) { + dev_dbg(wm8903->dev, "Microphone short (pol=%x)\n", int_pol); + + mic_report ^= wm8903->mic_short; + int_pol ^= WM8903_MICSHRT_INV; + } + + if (int_val & WM8903_MICDET_EINT) { + dev_dbg(wm8903->dev, "Microphone detect (pol=%x)\n", int_pol); + + mic_report ^= wm8903->mic_det; + int_pol ^= WM8903_MICDET_INV; + + msleep(wm8903->mic_delay); + } + + regmap_update_bits(wm8903->regmap, WM8903_INTERRUPT_POLARITY_1, + WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol); + + snd_soc_jack_report(wm8903->mic_jack, mic_report, + wm8903->mic_short | wm8903->mic_det); + + wm8903->mic_last_report = mic_report; + + return IRQ_HANDLED; +} + +#define WM8903_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000) + +#define WM8903_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +#define WM8903_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8903_dai_ops = { + .hw_params = wm8903_hw_params, + .mute_stream = wm8903_mute, + .set_fmt = wm8903_set_dai_fmt, + .set_sysclk = wm8903_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8903_dai = { + .name = "wm8903-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8903_PLAYBACK_RATES, + .formats = WM8903_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = WM8903_CAPTURE_RATES, + .formats = WM8903_FORMATS, + }, + .ops = &wm8903_dai_ops, + .symmetric_rate = 1, +}; + +static int wm8903_resume(struct snd_soc_component *component) +{ + struct wm8903_priv *wm8903 = snd_soc_component_get_drvdata(component); + + regcache_sync(wm8903->regmap); + + return 0; +} + +#ifdef CONFIG_GPIOLIB +static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= WM8903_NUM_GPIO) + return -EINVAL; + + return 0; +} + +static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpiochip_get_data(chip); + unsigned int mask, val; + int ret; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK; + val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) | + WM8903_GP1_DIR; + + ret = regmap_update_bits(wm8903->regmap, + WM8903_GPIO_CONTROL_1 + offset, mask, val); + if (ret < 0) + return ret; + + return 0; +} + +static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpiochip_get_data(chip); + unsigned int reg; + + regmap_read(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, ®); + + return !!((reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT); +} + +static int wm8903_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpiochip_get_data(chip); + unsigned int mask, val; + int ret; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK; + val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) | + (value << WM8903_GP2_LVL_SHIFT); + + ret = regmap_update_bits(wm8903->regmap, + WM8903_GPIO_CONTROL_1 + offset, mask, val); + if (ret < 0) + return ret; + + return 0; +} + +static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpiochip_get_data(chip); + + regmap_update_bits(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, + WM8903_GP1_LVL_MASK, + !!value << WM8903_GP1_LVL_SHIFT); +} + +static const struct gpio_chip wm8903_template_chip = { + .label = "wm8903", + .owner = THIS_MODULE, + .request = wm8903_gpio_request, + .direction_input = wm8903_gpio_direction_in, + .get = wm8903_gpio_get, + .direction_output = wm8903_gpio_direction_out, + .set = wm8903_gpio_set, + .can_sleep = 1, +}; + +static void wm8903_init_gpio(struct wm8903_priv *wm8903) +{ + struct wm8903_platform_data *pdata = wm8903->pdata; + int ret; + + wm8903->gpio_chip = wm8903_template_chip; + wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO; + wm8903->gpio_chip.parent = wm8903->dev; + + if (pdata->gpio_base) + wm8903->gpio_chip.base = pdata->gpio_base; + else + wm8903->gpio_chip.base = -1; + + ret = gpiochip_add_data(&wm8903->gpio_chip, wm8903); + if (ret != 0) + dev_err(wm8903->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void wm8903_free_gpio(struct wm8903_priv *wm8903) +{ + gpiochip_remove(&wm8903->gpio_chip); +} +#else +static void wm8903_init_gpio(struct wm8903_priv *wm8903) +{ +} + +static void wm8903_free_gpio(struct wm8903_priv *wm8903) +{ +} +#endif + +static const struct snd_soc_component_driver soc_component_dev_wm8903 = { + .resume = wm8903_resume, + .set_bias_level = wm8903_set_bias_level, + .seq_notifier = wm8903_seq_notifier, + .controls = wm8903_snd_controls, + .num_controls = ARRAY_SIZE(wm8903_snd_controls), + .dapm_widgets = wm8903_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets), + .dapm_routes = wm8903_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8903_intercon), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8903_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM8903_MAX_REGISTER, + .volatile_reg = wm8903_volatile_register, + .readable_reg = wm8903_readable_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm8903_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8903_reg_defaults), +}; + +static int wm8903_set_pdata_irq_trigger(struct i2c_client *i2c, + struct wm8903_platform_data *pdata) +{ + struct irq_data *irq_data = irq_get_irq_data(i2c->irq); + if (!irq_data) { + dev_err(&i2c->dev, "Invalid IRQ: %d\n", + i2c->irq); + return -EINVAL; + } + + switch (irqd_get_trigger_type(irq_data)) { + case IRQ_TYPE_NONE: + default: + /* + * We assume the controller imposes no restrictions, + * so we are able to select active-high + */ + fallthrough; + case IRQ_TYPE_LEVEL_HIGH: + pdata->irq_active_low = false; + break; + case IRQ_TYPE_LEVEL_LOW: + pdata->irq_active_low = true; + break; + } + + return 0; +} + +static int wm8903_set_pdata_from_of(struct i2c_client *i2c, + struct wm8903_platform_data *pdata) +{ + const struct device_node *np = i2c->dev.of_node; + u32 val32; + int i; + + if (of_property_read_u32(np, "micdet-cfg", &val32) >= 0) + pdata->micdet_cfg = val32; + + if (of_property_read_u32(np, "micdet-delay", &val32) >= 0) + pdata->micdet_delay = val32; + + if (of_property_read_u32_array(np, "gpio-cfg", pdata->gpio_cfg, + ARRAY_SIZE(pdata->gpio_cfg)) >= 0) { + /* + * In device tree: 0 means "write 0", + * 0xffffffff means "don't touch". + * + * In platform data: 0 means "don't touch", + * 0x8000 means "write 0". + * + * Note: WM8903_GPIO_CONFIG_ZERO == 0x8000. + * + * Convert from DT to pdata representation here, + * so no other code needs to change. + */ + for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { + if (pdata->gpio_cfg[i] == 0) { + pdata->gpio_cfg[i] = WM8903_GPIO_CONFIG_ZERO; + } else if (pdata->gpio_cfg[i] == 0xffffffff) { + pdata->gpio_cfg[i] = 0; + } else if (pdata->gpio_cfg[i] > 0x7fff) { + dev_err(&i2c->dev, "Invalid gpio-cfg[%d] %x\n", + i, pdata->gpio_cfg[i]); + return -EINVAL; + } + } + } + + return 0; +} + +static int wm8903_i2c_probe(struct i2c_client *i2c) +{ + struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct wm8903_priv *wm8903; + int trigger; + bool mic_gpio = false; + unsigned int val, irq_pol; + int ret, i; + + wm8903 = devm_kzalloc(&i2c->dev, sizeof(*wm8903), GFP_KERNEL); + if (wm8903 == NULL) + return -ENOMEM; + + mutex_init(&wm8903->lock); + wm8903->dev = &i2c->dev; + + wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap); + if (IS_ERR(wm8903->regmap)) { + ret = PTR_ERR(wm8903->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + i2c_set_clientdata(i2c, wm8903); + + /* If no platform data was supplied, create storage for defaults */ + if (pdata) { + wm8903->pdata = pdata; + } else { + wm8903->pdata = devm_kzalloc(&i2c->dev, sizeof(*wm8903->pdata), + GFP_KERNEL); + if (!wm8903->pdata) + return -ENOMEM; + + if (i2c->irq) { + ret = wm8903_set_pdata_irq_trigger(i2c, wm8903->pdata); + if (ret != 0) + return ret; + } + + if (i2c->dev.of_node) { + ret = wm8903_set_pdata_from_of(i2c, wm8903->pdata); + if (ret != 0) + return ret; + } + } + + pdata = wm8903->pdata; + + for (i = 0; i < ARRAY_SIZE(wm8903->supplies); i++) + wm8903->supplies[i].supply = wm8903_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8903->supplies), + wm8903->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8903->supplies), + wm8903->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); + goto err; + } + if (val != 0x8903) { + dev_err(&i2c->dev, "Device with ID %x is not a WM8903\n", val); + ret = -ENODEV; + goto err; + } + + ret = regmap_read(wm8903->regmap, WM8903_REVISION_NUMBER, &val); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read chip revision: %d\n", ret); + goto err; + } + dev_info(&i2c->dev, "WM8903 revision %c\n", + (val & WM8903_CHIP_REV_MASK) + 'A'); + + /* Reset the device */ + regmap_write(wm8903->regmap, WM8903_SW_RESET_AND_ID, 0x8903); + + wm8903_init_gpio(wm8903); + + /* Set up GPIO pin state, detect if any are MIC detect outputs */ + for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { + if ((!pdata->gpio_cfg[i]) || + (pdata->gpio_cfg[i] > WM8903_GPIO_CONFIG_ZERO)) + continue; + + regmap_write(wm8903->regmap, WM8903_GPIO_CONTROL_1 + i, + pdata->gpio_cfg[i] & 0x7fff); + + val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK) + >> WM8903_GP1_FN_SHIFT; + + switch (val) { + case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT: + case WM8903_GPn_FN_MICBIAS_SHORT_DETECT: + mic_gpio = true; + break; + default: + break; + } + } + + /* Set up microphone detection */ + regmap_write(wm8903->regmap, WM8903_MIC_BIAS_CONTROL_0, + pdata->micdet_cfg); + + /* Microphone detection needs the WSEQ clock */ + if (pdata->micdet_cfg) + regmap_update_bits(wm8903->regmap, WM8903_WRITE_SEQUENCER_0, + WM8903_WSEQ_ENA, WM8903_WSEQ_ENA); + + /* If microphone detection is enabled by pdata but + * detected via IRQ then interrupts can be lost before + * the machine driver has set up microphone detection + * IRQs as the IRQs are clear on read. The detection + * will be enabled when the machine driver configures. + */ + WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA)); + + wm8903->mic_delay = pdata->micdet_delay; + + if (i2c->irq) { + if (pdata->irq_active_low) { + trigger = IRQF_TRIGGER_LOW; + irq_pol = WM8903_IRQ_POL; + } else { + trigger = IRQF_TRIGGER_HIGH; + irq_pol = 0; + } + + regmap_update_bits(wm8903->regmap, WM8903_INTERRUPT_CONTROL, + WM8903_IRQ_POL, irq_pol); + + ret = request_threaded_irq(i2c->irq, NULL, wm8903_irq, + trigger | IRQF_ONESHOT, + "wm8903", wm8903); + if (ret != 0) { + dev_err(wm8903->dev, "Failed to request IRQ: %d\n", + ret); + goto err; + } + + /* Enable write sequencer interrupts */ + regmap_update_bits(wm8903->regmap, + WM8903_INTERRUPT_STATUS_1_MASK, + WM8903_IM_WSEQ_BUSY_EINT, 0); + } + + /* Latch volume update bits */ + regmap_update_bits(wm8903->regmap, WM8903_ADC_DIGITAL_VOLUME_LEFT, + WM8903_ADCVU, WM8903_ADCVU); + regmap_update_bits(wm8903->regmap, WM8903_ADC_DIGITAL_VOLUME_RIGHT, + WM8903_ADCVU, WM8903_ADCVU); + + regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_VOLUME_LEFT, + WM8903_DACVU, WM8903_DACVU); + regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_VOLUME_RIGHT, + WM8903_DACVU, WM8903_DACVU); + + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT1_LEFT, + WM8903_HPOUTVU, WM8903_HPOUTVU); + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT1_RIGHT, + WM8903_HPOUTVU, WM8903_HPOUTVU); + + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT2_LEFT, + WM8903_LINEOUTVU, WM8903_LINEOUTVU); + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT2_RIGHT, + WM8903_LINEOUTVU, WM8903_LINEOUTVU); + + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT3_LEFT, + WM8903_SPKVU, WM8903_SPKVU); + regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT3_RIGHT, + WM8903_SPKVU, WM8903_SPKVU); + + /* Enable DAC soft mute by default */ + regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_1, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8903, &wm8903_dai, 1); + if (ret != 0) + goto err; + + return 0; +err: + regulator_bulk_disable(ARRAY_SIZE(wm8903->supplies), + wm8903->supplies); + return ret; +} + +static void wm8903_i2c_remove(struct i2c_client *client) +{ + struct wm8903_priv *wm8903 = i2c_get_clientdata(client); + + regulator_bulk_disable(ARRAY_SIZE(wm8903->supplies), + wm8903->supplies); + if (client->irq) + free_irq(client->irq, wm8903); + wm8903_free_gpio(wm8903); +} + +static const struct of_device_id wm8903_of_match[] = { + { .compatible = "wlf,wm8903", }, + {}, +}; +MODULE_DEVICE_TABLE(of, wm8903_of_match); + +static const struct i2c_device_id wm8903_i2c_id[] = { + { "wm8903", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id); + +static struct i2c_driver wm8903_i2c_driver = { + .driver = { + .name = "wm8903", + .of_match_table = wm8903_of_match, + }, + .probe = wm8903_i2c_probe, + .remove = wm8903_i2c_remove, + .id_table = wm8903_i2c_id, +}; + +module_i2c_driver(wm8903_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8903 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h new file mode 100644 index 0000000000..4b036f4b75 --- /dev/null +++ b/sound/soc/codecs/wm8903.h @@ -0,0 +1,1221 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * wm8903.h - WM8903 audio codec interface + * + * Copyright 2008 Wolfson Microelectronics PLC. + * Author: Mark Brown + */ + +#ifndef _WM8903_H +#define _WM8903_H + +#include + +extern int wm8903_mic_detect(struct snd_soc_component *component, + struct snd_soc_jack *jack, + int det, int shrt); + + +/* + * Register values. + */ +#define WM8903_SW_RESET_AND_ID 0x00 +#define WM8903_REVISION_NUMBER 0x01 +#define WM8903_BIAS_CONTROL_0 0x04 +#define WM8903_VMID_CONTROL_0 0x05 +#define WM8903_MIC_BIAS_CONTROL_0 0x06 +#define WM8903_ANALOGUE_DAC_0 0x08 +#define WM8903_ANALOGUE_ADC_0 0x0A +#define WM8903_POWER_MANAGEMENT_0 0x0C +#define WM8903_POWER_MANAGEMENT_1 0x0D +#define WM8903_POWER_MANAGEMENT_2 0x0E +#define WM8903_POWER_MANAGEMENT_3 0x0F +#define WM8903_POWER_MANAGEMENT_4 0x10 +#define WM8903_POWER_MANAGEMENT_5 0x11 +#define WM8903_POWER_MANAGEMENT_6 0x12 +#define WM8903_CLOCK_RATES_0 0x14 +#define WM8903_CLOCK_RATES_1 0x15 +#define WM8903_CLOCK_RATES_2 0x16 +#define WM8903_AUDIO_INTERFACE_0 0x18 +#define WM8903_AUDIO_INTERFACE_1 0x19 +#define WM8903_AUDIO_INTERFACE_2 0x1A +#define WM8903_AUDIO_INTERFACE_3 0x1B +#define WM8903_DAC_DIGITAL_VOLUME_LEFT 0x1E +#define WM8903_DAC_DIGITAL_VOLUME_RIGHT 0x1F +#define WM8903_DAC_DIGITAL_0 0x20 +#define WM8903_DAC_DIGITAL_1 0x21 +#define WM8903_ADC_DIGITAL_VOLUME_LEFT 0x24 +#define WM8903_ADC_DIGITAL_VOLUME_RIGHT 0x25 +#define WM8903_ADC_DIGITAL_0 0x26 +#define WM8903_DIGITAL_MICROPHONE_0 0x27 +#define WM8903_DRC_0 0x28 +#define WM8903_DRC_1 0x29 +#define WM8903_DRC_2 0x2A +#define WM8903_DRC_3 0x2B +#define WM8903_ANALOGUE_LEFT_INPUT_0 0x2C +#define WM8903_ANALOGUE_RIGHT_INPUT_0 0x2D +#define WM8903_ANALOGUE_LEFT_INPUT_1 0x2E +#define WM8903_ANALOGUE_RIGHT_INPUT_1 0x2F +#define WM8903_ANALOGUE_LEFT_MIX_0 0x32 +#define WM8903_ANALOGUE_RIGHT_MIX_0 0x33 +#define WM8903_ANALOGUE_SPK_MIX_LEFT_0 0x34 +#define WM8903_ANALOGUE_SPK_MIX_LEFT_1 0x35 +#define WM8903_ANALOGUE_SPK_MIX_RIGHT_0 0x36 +#define WM8903_ANALOGUE_SPK_MIX_RIGHT_1 0x37 +#define WM8903_ANALOGUE_OUT1_LEFT 0x39 +#define WM8903_ANALOGUE_OUT1_RIGHT 0x3A +#define WM8903_ANALOGUE_OUT2_LEFT 0x3B +#define WM8903_ANALOGUE_OUT2_RIGHT 0x3C +#define WM8903_ANALOGUE_OUT3_LEFT 0x3E +#define WM8903_ANALOGUE_OUT3_RIGHT 0x3F +#define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 0x41 +#define WM8903_DC_SERVO_0 0x43 +#define WM8903_DC_SERVO_2 0x45 +#define WM8903_DC_SERVO_4 0x47 +#define WM8903_DC_SERVO_5 0x48 +#define WM8903_DC_SERVO_6 0x49 +#define WM8903_DC_SERVO_7 0x4A +#define WM8903_DC_SERVO_READBACK_1 0x51 +#define WM8903_DC_SERVO_READBACK_2 0x52 +#define WM8903_DC_SERVO_READBACK_3 0x53 +#define WM8903_DC_SERVO_READBACK_4 0x54 +#define WM8903_ANALOGUE_HP_0 0x5A +#define WM8903_ANALOGUE_LINEOUT_0 0x5E +#define WM8903_CHARGE_PUMP_0 0x62 +#define WM8903_CLASS_W_0 0x68 +#define WM8903_WRITE_SEQUENCER_0 0x6C +#define WM8903_WRITE_SEQUENCER_1 0x6D +#define WM8903_WRITE_SEQUENCER_2 0x6E +#define WM8903_WRITE_SEQUENCER_3 0x6F +#define WM8903_WRITE_SEQUENCER_4 0x70 +#define WM8903_CONTROL_INTERFACE 0x72 +#define WM8903_GPIO_CONTROL_1 0x74 +#define WM8903_GPIO_CONTROL_2 0x75 +#define WM8903_GPIO_CONTROL_3 0x76 +#define WM8903_GPIO_CONTROL_4 0x77 +#define WM8903_GPIO_CONTROL_5 0x78 +#define WM8903_INTERRUPT_STATUS_1 0x79 +#define WM8903_INTERRUPT_STATUS_1_MASK 0x7A +#define WM8903_INTERRUPT_POLARITY_1 0x7B +#define WM8903_INTERRUPT_CONTROL 0x7E +#define WM8903_CLOCK_RATE_TEST_4 0xA4 +#define WM8903_ANALOGUE_OUTPUT_BIAS_0 0xAC + +#define WM8903_REGISTER_COUNT 75 +#define WM8903_MAX_REGISTER 0xAC + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - SW Reset and ID + */ +#define WM8903_SW_RESET_DEV_ID1_MASK 0xFFFF /* SW_RESET_DEV_ID1 - [15:0] */ +#define WM8903_SW_RESET_DEV_ID1_SHIFT 0 /* SW_RESET_DEV_ID1 - [15:0] */ +#define WM8903_SW_RESET_DEV_ID1_WIDTH 16 /* SW_RESET_DEV_ID1 - [15:0] */ + +/* + * R1 (0x01) - Revision Number + */ +#define WM8903_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */ +#define WM8903_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */ +#define WM8903_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */ + +/* + * R4 (0x04) - Bias Control 0 + */ +#define WM8903_POBCTRL 0x0010 /* POBCTRL */ +#define WM8903_POBCTRL_MASK 0x0010 /* POBCTRL */ +#define WM8903_POBCTRL_SHIFT 4 /* POBCTRL */ +#define WM8903_POBCTRL_WIDTH 1 /* POBCTRL */ +#define WM8903_ISEL_MASK 0x000C /* ISEL - [3:2] */ +#define WM8903_ISEL_SHIFT 2 /* ISEL - [3:2] */ +#define WM8903_ISEL_WIDTH 2 /* ISEL - [3:2] */ +#define WM8903_STARTUP_BIAS_ENA 0x0002 /* STARTUP_BIAS_ENA */ +#define WM8903_STARTUP_BIAS_ENA_MASK 0x0002 /* STARTUP_BIAS_ENA */ +#define WM8903_STARTUP_BIAS_ENA_SHIFT 1 /* STARTUP_BIAS_ENA */ +#define WM8903_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */ +#define WM8903_BIAS_ENA 0x0001 /* BIAS_ENA */ +#define WM8903_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */ +#define WM8903_BIAS_ENA_SHIFT 0 /* BIAS_ENA */ +#define WM8903_BIAS_ENA_WIDTH 1 /* BIAS_ENA */ + +/* + * R5 (0x05) - VMID Control 0 + */ +#define WM8903_VMID_TIE_ENA 0x0080 /* VMID_TIE_ENA */ +#define WM8903_VMID_TIE_ENA_MASK 0x0080 /* VMID_TIE_ENA */ +#define WM8903_VMID_TIE_ENA_SHIFT 7 /* VMID_TIE_ENA */ +#define WM8903_VMID_TIE_ENA_WIDTH 1 /* VMID_TIE_ENA */ +#define WM8903_BUFIO_ENA 0x0040 /* BUFIO_ENA */ +#define WM8903_BUFIO_ENA_MASK 0x0040 /* BUFIO_ENA */ +#define WM8903_BUFIO_ENA_SHIFT 6 /* BUFIO_ENA */ +#define WM8903_BUFIO_ENA_WIDTH 1 /* BUFIO_ENA */ +#define WM8903_VMID_IO_ENA 0x0020 /* VMID_IO_ENA */ +#define WM8903_VMID_IO_ENA_MASK 0x0020 /* VMID_IO_ENA */ +#define WM8903_VMID_IO_ENA_SHIFT 5 /* VMID_IO_ENA */ +#define WM8903_VMID_IO_ENA_WIDTH 1 /* VMID_IO_ENA */ +#define WM8903_VMID_SOFT_MASK 0x0018 /* VMID_SOFT - [4:3] */ +#define WM8903_VMID_SOFT_SHIFT 3 /* VMID_SOFT - [4:3] */ +#define WM8903_VMID_SOFT_WIDTH 2 /* VMID_SOFT - [4:3] */ +#define WM8903_VMID_RES_MASK 0x0006 /* VMID_RES - [2:1] */ +#define WM8903_VMID_RES_SHIFT 1 /* VMID_RES - [2:1] */ +#define WM8903_VMID_RES_WIDTH 2 /* VMID_RES - [2:1] */ +#define WM8903_VMID_BUF_ENA 0x0001 /* VMID_BUF_ENA */ +#define WM8903_VMID_BUF_ENA_MASK 0x0001 /* VMID_BUF_ENA */ +#define WM8903_VMID_BUF_ENA_SHIFT 0 /* VMID_BUF_ENA */ +#define WM8903_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */ + +#define WM8903_VMID_RES_50K 2 +#define WM8903_VMID_RES_250K 4 +#define WM8903_VMID_RES_5K 6 + +/* + * R8 (0x08) - Analogue DAC 0 + */ +#define WM8903_DACBIAS_SEL_MASK 0x0018 /* DACBIAS_SEL - [4:3] */ +#define WM8903_DACBIAS_SEL_SHIFT 3 /* DACBIAS_SEL - [4:3] */ +#define WM8903_DACBIAS_SEL_WIDTH 2 /* DACBIAS_SEL - [4:3] */ +#define WM8903_DACVMID_BIAS_SEL_MASK 0x0006 /* DACVMID_BIAS_SEL - [2:1] */ +#define WM8903_DACVMID_BIAS_SEL_SHIFT 1 /* DACVMID_BIAS_SEL - [2:1] */ +#define WM8903_DACVMID_BIAS_SEL_WIDTH 2 /* DACVMID_BIAS_SEL - [2:1] */ + +/* + * R10 (0x0A) - Analogue ADC 0 + */ +#define WM8903_ADC_OSR128 0x0001 /* ADC_OSR128 */ +#define WM8903_ADC_OSR128_MASK 0x0001 /* ADC_OSR128 */ +#define WM8903_ADC_OSR128_SHIFT 0 /* ADC_OSR128 */ +#define WM8903_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */ + +/* + * R12 (0x0C) - Power Management 0 + */ +#define WM8903_INL_ENA 0x0002 /* INL_ENA */ +#define WM8903_INL_ENA_MASK 0x0002 /* INL_ENA */ +#define WM8903_INL_ENA_SHIFT 1 /* INL_ENA */ +#define WM8903_INL_ENA_WIDTH 1 /* INL_ENA */ +#define WM8903_INR_ENA 0x0001 /* INR_ENA */ +#define WM8903_INR_ENA_MASK 0x0001 /* INR_ENA */ +#define WM8903_INR_ENA_SHIFT 0 /* INR_ENA */ +#define WM8903_INR_ENA_WIDTH 1 /* INR_ENA */ + +/* + * R13 (0x0D) - Power Management 1 + */ +#define WM8903_MIXOUTL_ENA 0x0002 /* MIXOUTL_ENA */ +#define WM8903_MIXOUTL_ENA_MASK 0x0002 /* MIXOUTL_ENA */ +#define WM8903_MIXOUTL_ENA_SHIFT 1 /* MIXOUTL_ENA */ +#define WM8903_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */ +#define WM8903_MIXOUTR_ENA 0x0001 /* MIXOUTR_ENA */ +#define WM8903_MIXOUTR_ENA_MASK 0x0001 /* MIXOUTR_ENA */ +#define WM8903_MIXOUTR_ENA_SHIFT 0 /* MIXOUTR_ENA */ +#define WM8903_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */ + +/* + * R14 (0x0E) - Power Management 2 + */ +#define WM8903_HPL_PGA_ENA 0x0002 /* HPL_PGA_ENA */ +#define WM8903_HPL_PGA_ENA_MASK 0x0002 /* HPL_PGA_ENA */ +#define WM8903_HPL_PGA_ENA_SHIFT 1 /* HPL_PGA_ENA */ +#define WM8903_HPL_PGA_ENA_WIDTH 1 /* HPL_PGA_ENA */ +#define WM8903_HPR_PGA_ENA 0x0001 /* HPR_PGA_ENA */ +#define WM8903_HPR_PGA_ENA_MASK 0x0001 /* HPR_PGA_ENA */ +#define WM8903_HPR_PGA_ENA_SHIFT 0 /* HPR_PGA_ENA */ +#define WM8903_HPR_PGA_ENA_WIDTH 1 /* HPR_PGA_ENA */ + +/* + * R15 (0x0F) - Power Management 3 + */ +#define WM8903_LINEOUTL_PGA_ENA 0x0002 /* LINEOUTL_PGA_ENA */ +#define WM8903_LINEOUTL_PGA_ENA_MASK 0x0002 /* LINEOUTL_PGA_ENA */ +#define WM8903_LINEOUTL_PGA_ENA_SHIFT 1 /* LINEOUTL_PGA_ENA */ +#define WM8903_LINEOUTL_PGA_ENA_WIDTH 1 /* LINEOUTL_PGA_ENA */ +#define WM8903_LINEOUTR_PGA_ENA 0x0001 /* LINEOUTR_PGA_ENA */ +#define WM8903_LINEOUTR_PGA_ENA_MASK 0x0001 /* LINEOUTR_PGA_ENA */ +#define WM8903_LINEOUTR_PGA_ENA_SHIFT 0 /* LINEOUTR_PGA_ENA */ +#define WM8903_LINEOUTR_PGA_ENA_WIDTH 1 /* LINEOUTR_PGA_ENA */ + +/* + * R16 (0x10) - Power Management 4 + */ +#define WM8903_MIXSPKL_ENA 0x0002 /* MIXSPKL_ENA */ +#define WM8903_MIXSPKL_ENA_MASK 0x0002 /* MIXSPKL_ENA */ +#define WM8903_MIXSPKL_ENA_SHIFT 1 /* MIXSPKL_ENA */ +#define WM8903_MIXSPKL_ENA_WIDTH 1 /* MIXSPKL_ENA */ +#define WM8903_MIXSPKR_ENA 0x0001 /* MIXSPKR_ENA */ +#define WM8903_MIXSPKR_ENA_MASK 0x0001 /* MIXSPKR_ENA */ +#define WM8903_MIXSPKR_ENA_SHIFT 0 /* MIXSPKR_ENA */ +#define WM8903_MIXSPKR_ENA_WIDTH 1 /* MIXSPKR_ENA */ + +/* + * R17 (0x11) - Power Management 5 + */ +#define WM8903_SPKL_ENA 0x0002 /* SPKL_ENA */ +#define WM8903_SPKL_ENA_MASK 0x0002 /* SPKL_ENA */ +#define WM8903_SPKL_ENA_SHIFT 1 /* SPKL_ENA */ +#define WM8903_SPKL_ENA_WIDTH 1 /* SPKL_ENA */ +#define WM8903_SPKR_ENA 0x0001 /* SPKR_ENA */ +#define WM8903_SPKR_ENA_MASK 0x0001 /* SPKR_ENA */ +#define WM8903_SPKR_ENA_SHIFT 0 /* SPKR_ENA */ +#define WM8903_SPKR_ENA_WIDTH 1 /* SPKR_ENA */ + +/* + * R18 (0x12) - Power Management 6 + */ +#define WM8903_DACL_ENA 0x0008 /* DACL_ENA */ +#define WM8903_DACL_ENA_MASK 0x0008 /* DACL_ENA */ +#define WM8903_DACL_ENA_SHIFT 3 /* DACL_ENA */ +#define WM8903_DACL_ENA_WIDTH 1 /* DACL_ENA */ +#define WM8903_DACR_ENA 0x0004 /* DACR_ENA */ +#define WM8903_DACR_ENA_MASK 0x0004 /* DACR_ENA */ +#define WM8903_DACR_ENA_SHIFT 2 /* DACR_ENA */ +#define WM8903_DACR_ENA_WIDTH 1 /* DACR_ENA */ +#define WM8903_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8903_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */ +#define WM8903_ADCL_ENA_SHIFT 1 /* ADCL_ENA */ +#define WM8903_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ +#define WM8903_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8903_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */ +#define WM8903_ADCR_ENA_SHIFT 0 /* ADCR_ENA */ +#define WM8903_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ + +/* + * R20 (0x14) - Clock Rates 0 + */ +#define WM8903_MCLKDIV2 0x0001 /* MCLKDIV2 */ +#define WM8903_MCLKDIV2_MASK 0x0001 /* MCLKDIV2 */ +#define WM8903_MCLKDIV2_SHIFT 0 /* MCLKDIV2 */ +#define WM8903_MCLKDIV2_WIDTH 1 /* MCLKDIV2 */ + +/* + * R21 (0x15) - Clock Rates 1 + */ +#define WM8903_CLK_SYS_RATE_MASK 0x3C00 /* CLK_SYS_RATE - [13:10] */ +#define WM8903_CLK_SYS_RATE_SHIFT 10 /* CLK_SYS_RATE - [13:10] */ +#define WM8903_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [13:10] */ +#define WM8903_CLK_SYS_MODE_MASK 0x0300 /* CLK_SYS_MODE - [9:8] */ +#define WM8903_CLK_SYS_MODE_SHIFT 8 /* CLK_SYS_MODE - [9:8] */ +#define WM8903_CLK_SYS_MODE_WIDTH 2 /* CLK_SYS_MODE - [9:8] */ +#define WM8903_SAMPLE_RATE_MASK 0x000F /* SAMPLE_RATE - [3:0] */ +#define WM8903_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [3:0] */ +#define WM8903_SAMPLE_RATE_WIDTH 4 /* SAMPLE_RATE - [3:0] */ + +/* + * R22 (0x16) - Clock Rates 2 + */ +#define WM8903_CLK_SYS_ENA 0x0004 /* CLK_SYS_ENA */ +#define WM8903_CLK_SYS_ENA_MASK 0x0004 /* CLK_SYS_ENA */ +#define WM8903_CLK_SYS_ENA_SHIFT 2 /* CLK_SYS_ENA */ +#define WM8903_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */ +#define WM8903_CLK_DSP_ENA 0x0002 /* CLK_DSP_ENA */ +#define WM8903_CLK_DSP_ENA_MASK 0x0002 /* CLK_DSP_ENA */ +#define WM8903_CLK_DSP_ENA_SHIFT 1 /* CLK_DSP_ENA */ +#define WM8903_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */ +#define WM8903_TO_ENA 0x0001 /* TO_ENA */ +#define WM8903_TO_ENA_MASK 0x0001 /* TO_ENA */ +#define WM8903_TO_ENA_SHIFT 0 /* TO_ENA */ +#define WM8903_TO_ENA_WIDTH 1 /* TO_ENA */ + +/* + * R24 (0x18) - Audio Interface 0 + */ +#define WM8903_DACL_DATINV 0x1000 /* DACL_DATINV */ +#define WM8903_DACL_DATINV_MASK 0x1000 /* DACL_DATINV */ +#define WM8903_DACL_DATINV_SHIFT 12 /* DACL_DATINV */ +#define WM8903_DACL_DATINV_WIDTH 1 /* DACL_DATINV */ +#define WM8903_DACR_DATINV 0x0800 /* DACR_DATINV */ +#define WM8903_DACR_DATINV_MASK 0x0800 /* DACR_DATINV */ +#define WM8903_DACR_DATINV_SHIFT 11 /* DACR_DATINV */ +#define WM8903_DACR_DATINV_WIDTH 1 /* DACR_DATINV */ +#define WM8903_DAC_BOOST_MASK 0x0600 /* DAC_BOOST - [10:9] */ +#define WM8903_DAC_BOOST_SHIFT 9 /* DAC_BOOST - [10:9] */ +#define WM8903_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [10:9] */ +#define WM8903_LOOPBACK 0x0100 /* LOOPBACK */ +#define WM8903_LOOPBACK_MASK 0x0100 /* LOOPBACK */ +#define WM8903_LOOPBACK_SHIFT 8 /* LOOPBACK */ +#define WM8903_LOOPBACK_WIDTH 1 /* LOOPBACK */ +#define WM8903_AIFADCL_SRC 0x0080 /* AIFADCL_SRC */ +#define WM8903_AIFADCL_SRC_MASK 0x0080 /* AIFADCL_SRC */ +#define WM8903_AIFADCL_SRC_SHIFT 7 /* AIFADCL_SRC */ +#define WM8903_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */ +#define WM8903_AIFADCR_SRC 0x0040 /* AIFADCR_SRC */ +#define WM8903_AIFADCR_SRC_MASK 0x0040 /* AIFADCR_SRC */ +#define WM8903_AIFADCR_SRC_SHIFT 6 /* AIFADCR_SRC */ +#define WM8903_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */ +#define WM8903_AIFDACL_SRC 0x0020 /* AIFDACL_SRC */ +#define WM8903_AIFDACL_SRC_MASK 0x0020 /* AIFDACL_SRC */ +#define WM8903_AIFDACL_SRC_SHIFT 5 /* AIFDACL_SRC */ +#define WM8903_AIFDACL_SRC_WIDTH 1 /* AIFDACL_SRC */ +#define WM8903_AIFDACR_SRC 0x0010 /* AIFDACR_SRC */ +#define WM8903_AIFDACR_SRC_MASK 0x0010 /* AIFDACR_SRC */ +#define WM8903_AIFDACR_SRC_SHIFT 4 /* AIFDACR_SRC */ +#define WM8903_AIFDACR_SRC_WIDTH 1 /* AIFDACR_SRC */ +#define WM8903_ADC_COMP 0x0008 /* ADC_COMP */ +#define WM8903_ADC_COMP_MASK 0x0008 /* ADC_COMP */ +#define WM8903_ADC_COMP_SHIFT 3 /* ADC_COMP */ +#define WM8903_ADC_COMP_WIDTH 1 /* ADC_COMP */ +#define WM8903_ADC_COMPMODE 0x0004 /* ADC_COMPMODE */ +#define WM8903_ADC_COMPMODE_MASK 0x0004 /* ADC_COMPMODE */ +#define WM8903_ADC_COMPMODE_SHIFT 2 /* ADC_COMPMODE */ +#define WM8903_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */ +#define WM8903_DAC_COMP 0x0002 /* DAC_COMP */ +#define WM8903_DAC_COMP_MASK 0x0002 /* DAC_COMP */ +#define WM8903_DAC_COMP_SHIFT 1 /* DAC_COMP */ +#define WM8903_DAC_COMP_WIDTH 1 /* DAC_COMP */ +#define WM8903_DAC_COMPMODE 0x0001 /* DAC_COMPMODE */ +#define WM8903_DAC_COMPMODE_MASK 0x0001 /* DAC_COMPMODE */ +#define WM8903_DAC_COMPMODE_SHIFT 0 /* DAC_COMPMODE */ +#define WM8903_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */ + +/* + * R25 (0x19) - Audio Interface 1 + */ +#define WM8903_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ +#define WM8903_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */ +#define WM8903_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */ +#define WM8903_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */ +#define WM8903_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8903_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8903_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */ +#define WM8903_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */ +#define WM8903_AIFADC_TDM 0x0800 /* AIFADC_TDM */ +#define WM8903_AIFADC_TDM_MASK 0x0800 /* AIFADC_TDM */ +#define WM8903_AIFADC_TDM_SHIFT 11 /* AIFADC_TDM */ +#define WM8903_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */ +#define WM8903_AIFADC_TDM_CHAN 0x0400 /* AIFADC_TDM_CHAN */ +#define WM8903_AIFADC_TDM_CHAN_MASK 0x0400 /* AIFADC_TDM_CHAN */ +#define WM8903_AIFADC_TDM_CHAN_SHIFT 10 /* AIFADC_TDM_CHAN */ +#define WM8903_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */ +#define WM8903_LRCLK_DIR 0x0200 /* LRCLK_DIR */ +#define WM8903_LRCLK_DIR_MASK 0x0200 /* LRCLK_DIR */ +#define WM8903_LRCLK_DIR_SHIFT 9 /* LRCLK_DIR */ +#define WM8903_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */ +#define WM8903_AIF_BCLK_INV 0x0080 /* AIF_BCLK_INV */ +#define WM8903_AIF_BCLK_INV_MASK 0x0080 /* AIF_BCLK_INV */ +#define WM8903_AIF_BCLK_INV_SHIFT 7 /* AIF_BCLK_INV */ +#define WM8903_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */ +#define WM8903_BCLK_DIR 0x0040 /* BCLK_DIR */ +#define WM8903_BCLK_DIR_MASK 0x0040 /* BCLK_DIR */ +#define WM8903_BCLK_DIR_SHIFT 6 /* BCLK_DIR */ +#define WM8903_BCLK_DIR_WIDTH 1 /* BCLK_DIR */ +#define WM8903_AIF_LRCLK_INV 0x0010 /* AIF_LRCLK_INV */ +#define WM8903_AIF_LRCLK_INV_MASK 0x0010 /* AIF_LRCLK_INV */ +#define WM8903_AIF_LRCLK_INV_SHIFT 4 /* AIF_LRCLK_INV */ +#define WM8903_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */ +#define WM8903_AIF_WL_MASK 0x000C /* AIF_WL - [3:2] */ +#define WM8903_AIF_WL_SHIFT 2 /* AIF_WL - [3:2] */ +#define WM8903_AIF_WL_WIDTH 2 /* AIF_WL - [3:2] */ +#define WM8903_AIF_FMT_MASK 0x0003 /* AIF_FMT - [1:0] */ +#define WM8903_AIF_FMT_SHIFT 0 /* AIF_FMT - [1:0] */ +#define WM8903_AIF_FMT_WIDTH 2 /* AIF_FMT - [1:0] */ + +/* + * R26 (0x1A) - Audio Interface 2 + */ +#define WM8903_BCLK_DIV_MASK 0x001F /* BCLK_DIV - [4:0] */ +#define WM8903_BCLK_DIV_SHIFT 0 /* BCLK_DIV - [4:0] */ +#define WM8903_BCLK_DIV_WIDTH 5 /* BCLK_DIV - [4:0] */ + +/* + * R27 (0x1B) - Audio Interface 3 + */ +#define WM8903_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */ +#define WM8903_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */ +#define WM8903_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */ + +/* + * R30 (0x1E) - DAC Digital Volume Left + */ +#define WM8903_DACVU 0x0100 /* DACVU */ +#define WM8903_DACVU_MASK 0x0100 /* DACVU */ +#define WM8903_DACVU_SHIFT 8 /* DACVU */ +#define WM8903_DACVU_WIDTH 1 /* DACVU */ +#define WM8903_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8903_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */ +#define WM8903_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */ + +/* + * R31 (0x1F) - DAC Digital Volume Right + */ +#define WM8903_DACVU 0x0100 /* DACVU */ +#define WM8903_DACVU_MASK 0x0100 /* DACVU */ +#define WM8903_DACVU_SHIFT 8 /* DACVU */ +#define WM8903_DACVU_WIDTH 1 /* DACVU */ +#define WM8903_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8903_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */ +#define WM8903_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */ + +/* + * R32 (0x20) - DAC Digital 0 + */ +#define WM8903_ADCL_DAC_SVOL_MASK 0x0F00 /* ADCL_DAC_SVOL - [11:8] */ +#define WM8903_ADCL_DAC_SVOL_SHIFT 8 /* ADCL_DAC_SVOL - [11:8] */ +#define WM8903_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [11:8] */ +#define WM8903_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8903_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8903_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8903_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */ +#define WM8903_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */ +#define WM8903_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */ +#define WM8903_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */ +#define WM8903_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */ +#define WM8903_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */ + +/* + * R33 (0x21) - DAC Digital 1 + */ +#define WM8903_DAC_MONO 0x1000 /* DAC_MONO */ +#define WM8903_DAC_MONO_MASK 0x1000 /* DAC_MONO */ +#define WM8903_DAC_MONO_SHIFT 12 /* DAC_MONO */ +#define WM8903_DAC_MONO_WIDTH 1 /* DAC_MONO */ +#define WM8903_DAC_SB_FILT 0x0800 /* DAC_SB_FILT */ +#define WM8903_DAC_SB_FILT_MASK 0x0800 /* DAC_SB_FILT */ +#define WM8903_DAC_SB_FILT_SHIFT 11 /* DAC_SB_FILT */ +#define WM8903_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */ +#define WM8903_DAC_MUTERATE 0x0400 /* DAC_MUTERATE */ +#define WM8903_DAC_MUTERATE_MASK 0x0400 /* DAC_MUTERATE */ +#define WM8903_DAC_MUTERATE_SHIFT 10 /* DAC_MUTERATE */ +#define WM8903_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ +#define WM8903_DAC_MUTEMODE 0x0200 /* DAC_MUTEMODE */ +#define WM8903_DAC_MUTEMODE_MASK 0x0200 /* DAC_MUTEMODE */ +#define WM8903_DAC_MUTEMODE_SHIFT 9 /* DAC_MUTEMODE */ +#define WM8903_DAC_MUTEMODE_WIDTH 1 /* DAC_MUTEMODE */ +#define WM8903_DAC_MUTE 0x0008 /* DAC_MUTE */ +#define WM8903_DAC_MUTE_MASK 0x0008 /* DAC_MUTE */ +#define WM8903_DAC_MUTE_SHIFT 3 /* DAC_MUTE */ +#define WM8903_DAC_MUTE_WIDTH 1 /* DAC_MUTE */ +#define WM8903_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */ +#define WM8903_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */ +#define WM8903_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */ + +/* + * R36 (0x24) - ADC Digital Volume Left + */ +#define WM8903_ADCVU 0x0100 /* ADCVU */ +#define WM8903_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8903_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8903_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8903_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8903_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */ +#define WM8903_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */ + +/* + * R37 (0x25) - ADC Digital Volume Right + */ +#define WM8903_ADCVU 0x0100 /* ADCVU */ +#define WM8903_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8903_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8903_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8903_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8903_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */ +#define WM8903_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */ + +/* + * R38 (0x26) - ADC Digital 0 + */ +#define WM8903_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */ +#define WM8903_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */ +#define WM8903_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */ +#define WM8903_ADC_HPF_ENA 0x0010 /* ADC_HPF_ENA */ +#define WM8903_ADC_HPF_ENA_MASK 0x0010 /* ADC_HPF_ENA */ +#define WM8903_ADC_HPF_ENA_SHIFT 4 /* ADC_HPF_ENA */ +#define WM8903_ADC_HPF_ENA_WIDTH 1 /* ADC_HPF_ENA */ +#define WM8903_ADCL_DATINV 0x0002 /* ADCL_DATINV */ +#define WM8903_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */ +#define WM8903_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */ +#define WM8903_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */ +#define WM8903_ADCR_DATINV 0x0001 /* ADCR_DATINV */ +#define WM8903_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */ +#define WM8903_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */ +#define WM8903_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */ + +/* + * R39 (0x27) - Digital Microphone 0 + */ +#define WM8903_DIGMIC_MODE_SEL 0x0100 /* DIGMIC_MODE_SEL */ +#define WM8903_DIGMIC_MODE_SEL_MASK 0x0100 /* DIGMIC_MODE_SEL */ +#define WM8903_DIGMIC_MODE_SEL_SHIFT 8 /* DIGMIC_MODE_SEL */ +#define WM8903_DIGMIC_MODE_SEL_WIDTH 1 /* DIGMIC_MODE_SEL */ +#define WM8903_DIGMIC_CLK_SEL_L_MASK 0x00C0 /* DIGMIC_CLK_SEL_L - [7:6] */ +#define WM8903_DIGMIC_CLK_SEL_L_SHIFT 6 /* DIGMIC_CLK_SEL_L - [7:6] */ +#define WM8903_DIGMIC_CLK_SEL_L_WIDTH 2 /* DIGMIC_CLK_SEL_L - [7:6] */ +#define WM8903_DIGMIC_CLK_SEL_R_MASK 0x0030 /* DIGMIC_CLK_SEL_R - [5:4] */ +#define WM8903_DIGMIC_CLK_SEL_R_SHIFT 4 /* DIGMIC_CLK_SEL_R - [5:4] */ +#define WM8903_DIGMIC_CLK_SEL_R_WIDTH 2 /* DIGMIC_CLK_SEL_R - [5:4] */ +#define WM8903_DIGMIC_CLK_SEL_RT_MASK 0x000C /* DIGMIC_CLK_SEL_RT - [3:2] */ +#define WM8903_DIGMIC_CLK_SEL_RT_SHIFT 2 /* DIGMIC_CLK_SEL_RT - [3:2] */ +#define WM8903_DIGMIC_CLK_SEL_RT_WIDTH 2 /* DIGMIC_CLK_SEL_RT - [3:2] */ +#define WM8903_DIGMIC_CLK_SEL_MASK 0x0003 /* DIGMIC_CLK_SEL - [1:0] */ +#define WM8903_DIGMIC_CLK_SEL_SHIFT 0 /* DIGMIC_CLK_SEL - [1:0] */ +#define WM8903_DIGMIC_CLK_SEL_WIDTH 2 /* DIGMIC_CLK_SEL - [1:0] */ + +/* + * R40 (0x28) - DRC 0 + */ +#define WM8903_DRC_ENA 0x8000 /* DRC_ENA */ +#define WM8903_DRC_ENA_MASK 0x8000 /* DRC_ENA */ +#define WM8903_DRC_ENA_SHIFT 15 /* DRC_ENA */ +#define WM8903_DRC_ENA_WIDTH 1 /* DRC_ENA */ +#define WM8903_DRC_THRESH_HYST_MASK 0x1800 /* DRC_THRESH_HYST - [12:11] */ +#define WM8903_DRC_THRESH_HYST_SHIFT 11 /* DRC_THRESH_HYST - [12:11] */ +#define WM8903_DRC_THRESH_HYST_WIDTH 2 /* DRC_THRESH_HYST - [12:11] */ +#define WM8903_DRC_STARTUP_GAIN_MASK 0x07C0 /* DRC_STARTUP_GAIN - [10:6] */ +#define WM8903_DRC_STARTUP_GAIN_SHIFT 6 /* DRC_STARTUP_GAIN - [10:6] */ +#define WM8903_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [10:6] */ +#define WM8903_DRC_FF_DELAY 0x0020 /* DRC_FF_DELAY */ +#define WM8903_DRC_FF_DELAY_MASK 0x0020 /* DRC_FF_DELAY */ +#define WM8903_DRC_FF_DELAY_SHIFT 5 /* DRC_FF_DELAY */ +#define WM8903_DRC_FF_DELAY_WIDTH 1 /* DRC_FF_DELAY */ +#define WM8903_DRC_SMOOTH_ENA 0x0008 /* DRC_SMOOTH_ENA */ +#define WM8903_DRC_SMOOTH_ENA_MASK 0x0008 /* DRC_SMOOTH_ENA */ +#define WM8903_DRC_SMOOTH_ENA_SHIFT 3 /* DRC_SMOOTH_ENA */ +#define WM8903_DRC_SMOOTH_ENA_WIDTH 1 /* DRC_SMOOTH_ENA */ +#define WM8903_DRC_QR_ENA 0x0004 /* DRC_QR_ENA */ +#define WM8903_DRC_QR_ENA_MASK 0x0004 /* DRC_QR_ENA */ +#define WM8903_DRC_QR_ENA_SHIFT 2 /* DRC_QR_ENA */ +#define WM8903_DRC_QR_ENA_WIDTH 1 /* DRC_QR_ENA */ +#define WM8903_DRC_ANTICLIP_ENA 0x0002 /* DRC_ANTICLIP_ENA */ +#define WM8903_DRC_ANTICLIP_ENA_MASK 0x0002 /* DRC_ANTICLIP_ENA */ +#define WM8903_DRC_ANTICLIP_ENA_SHIFT 1 /* DRC_ANTICLIP_ENA */ +#define WM8903_DRC_ANTICLIP_ENA_WIDTH 1 /* DRC_ANTICLIP_ENA */ +#define WM8903_DRC_HYST_ENA 0x0001 /* DRC_HYST_ENA */ +#define WM8903_DRC_HYST_ENA_MASK 0x0001 /* DRC_HYST_ENA */ +#define WM8903_DRC_HYST_ENA_SHIFT 0 /* DRC_HYST_ENA */ +#define WM8903_DRC_HYST_ENA_WIDTH 1 /* DRC_HYST_ENA */ + +/* + * R41 (0x29) - DRC 1 + */ +#define WM8903_DRC_ATTACK_RATE_MASK 0xF000 /* DRC_ATTACK_RATE - [15:12] */ +#define WM8903_DRC_ATTACK_RATE_SHIFT 12 /* DRC_ATTACK_RATE - [15:12] */ +#define WM8903_DRC_ATTACK_RATE_WIDTH 4 /* DRC_ATTACK_RATE - [15:12] */ +#define WM8903_DRC_DECAY_RATE_MASK 0x0F00 /* DRC_DECAY_RATE - [11:8] */ +#define WM8903_DRC_DECAY_RATE_SHIFT 8 /* DRC_DECAY_RATE - [11:8] */ +#define WM8903_DRC_DECAY_RATE_WIDTH 4 /* DRC_DECAY_RATE - [11:8] */ +#define WM8903_DRC_THRESH_QR_MASK 0x00C0 /* DRC_THRESH_QR - [7:6] */ +#define WM8903_DRC_THRESH_QR_SHIFT 6 /* DRC_THRESH_QR - [7:6] */ +#define WM8903_DRC_THRESH_QR_WIDTH 2 /* DRC_THRESH_QR - [7:6] */ +#define WM8903_DRC_RATE_QR_MASK 0x0030 /* DRC_RATE_QR - [5:4] */ +#define WM8903_DRC_RATE_QR_SHIFT 4 /* DRC_RATE_QR - [5:4] */ +#define WM8903_DRC_RATE_QR_WIDTH 2 /* DRC_RATE_QR - [5:4] */ +#define WM8903_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */ +#define WM8903_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */ +#define WM8903_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */ +#define WM8903_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */ +#define WM8903_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */ +#define WM8903_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */ + +/* + * R42 (0x2A) - DRC 2 + */ +#define WM8903_DRC_R0_SLOPE_COMP_MASK 0x0038 /* DRC_R0_SLOPE_COMP - [5:3] */ +#define WM8903_DRC_R0_SLOPE_COMP_SHIFT 3 /* DRC_R0_SLOPE_COMP - [5:3] */ +#define WM8903_DRC_R0_SLOPE_COMP_WIDTH 3 /* DRC_R0_SLOPE_COMP - [5:3] */ +#define WM8903_DRC_R1_SLOPE_COMP_MASK 0x0007 /* DRC_R1_SLOPE_COMP - [2:0] */ +#define WM8903_DRC_R1_SLOPE_COMP_SHIFT 0 /* DRC_R1_SLOPE_COMP - [2:0] */ +#define WM8903_DRC_R1_SLOPE_COMP_WIDTH 3 /* DRC_R1_SLOPE_COMP - [2:0] */ + +/* + * R43 (0x2B) - DRC 3 + */ +#define WM8903_DRC_THRESH_COMP_MASK 0x07E0 /* DRC_THRESH_COMP - [10:5] */ +#define WM8903_DRC_THRESH_COMP_SHIFT 5 /* DRC_THRESH_COMP - [10:5] */ +#define WM8903_DRC_THRESH_COMP_WIDTH 6 /* DRC_THRESH_COMP - [10:5] */ +#define WM8903_DRC_AMP_COMP_MASK 0x001F /* DRC_AMP_COMP - [4:0] */ +#define WM8903_DRC_AMP_COMP_SHIFT 0 /* DRC_AMP_COMP - [4:0] */ +#define WM8903_DRC_AMP_COMP_WIDTH 5 /* DRC_AMP_COMP - [4:0] */ + +/* + * R44 (0x2C) - Analogue Left Input 0 + */ +#define WM8903_LINMUTE 0x0080 /* LINMUTE */ +#define WM8903_LINMUTE_MASK 0x0080 /* LINMUTE */ +#define WM8903_LINMUTE_SHIFT 7 /* LINMUTE */ +#define WM8903_LINMUTE_WIDTH 1 /* LINMUTE */ +#define WM8903_LIN_VOL_MASK 0x001F /* LIN_VOL - [4:0] */ +#define WM8903_LIN_VOL_SHIFT 0 /* LIN_VOL - [4:0] */ +#define WM8903_LIN_VOL_WIDTH 5 /* LIN_VOL - [4:0] */ + +/* + * R45 (0x2D) - Analogue Right Input 0 + */ +#define WM8903_RINMUTE 0x0080 /* RINMUTE */ +#define WM8903_RINMUTE_MASK 0x0080 /* RINMUTE */ +#define WM8903_RINMUTE_SHIFT 7 /* RINMUTE */ +#define WM8903_RINMUTE_WIDTH 1 /* RINMUTE */ +#define WM8903_RIN_VOL_MASK 0x001F /* RIN_VOL - [4:0] */ +#define WM8903_RIN_VOL_SHIFT 0 /* RIN_VOL - [4:0] */ +#define WM8903_RIN_VOL_WIDTH 5 /* RIN_VOL - [4:0] */ + +/* + * R46 (0x2E) - Analogue Left Input 1 + */ +#define WM8903_INL_CM_ENA 0x0040 /* INL_CM_ENA */ +#define WM8903_INL_CM_ENA_MASK 0x0040 /* INL_CM_ENA */ +#define WM8903_INL_CM_ENA_SHIFT 6 /* INL_CM_ENA */ +#define WM8903_INL_CM_ENA_WIDTH 1 /* INL_CM_ENA */ +#define WM8903_L_IP_SEL_N_MASK 0x0030 /* L_IP_SEL_N - [5:4] */ +#define WM8903_L_IP_SEL_N_SHIFT 4 /* L_IP_SEL_N - [5:4] */ +#define WM8903_L_IP_SEL_N_WIDTH 2 /* L_IP_SEL_N - [5:4] */ +#define WM8903_L_IP_SEL_P_MASK 0x000C /* L_IP_SEL_P - [3:2] */ +#define WM8903_L_IP_SEL_P_SHIFT 2 /* L_IP_SEL_P - [3:2] */ +#define WM8903_L_IP_SEL_P_WIDTH 2 /* L_IP_SEL_P - [3:2] */ +#define WM8903_L_MODE_MASK 0x0003 /* L_MODE - [1:0] */ +#define WM8903_L_MODE_SHIFT 0 /* L_MODE - [1:0] */ +#define WM8903_L_MODE_WIDTH 2 /* L_MODE - [1:0] */ + +/* + * R47 (0x2F) - Analogue Right Input 1 + */ +#define WM8903_INR_CM_ENA 0x0040 /* INR_CM_ENA */ +#define WM8903_INR_CM_ENA_MASK 0x0040 /* INR_CM_ENA */ +#define WM8903_INR_CM_ENA_SHIFT 6 /* INR_CM_ENA */ +#define WM8903_INR_CM_ENA_WIDTH 1 /* INR_CM_ENA */ +#define WM8903_R_IP_SEL_N_MASK 0x0030 /* R_IP_SEL_N - [5:4] */ +#define WM8903_R_IP_SEL_N_SHIFT 4 /* R_IP_SEL_N - [5:4] */ +#define WM8903_R_IP_SEL_N_WIDTH 2 /* R_IP_SEL_N - [5:4] */ +#define WM8903_R_IP_SEL_P_MASK 0x000C /* R_IP_SEL_P - [3:2] */ +#define WM8903_R_IP_SEL_P_SHIFT 2 /* R_IP_SEL_P - [3:2] */ +#define WM8903_R_IP_SEL_P_WIDTH 2 /* R_IP_SEL_P - [3:2] */ +#define WM8903_R_MODE_MASK 0x0003 /* R_MODE - [1:0] */ +#define WM8903_R_MODE_SHIFT 0 /* R_MODE - [1:0] */ +#define WM8903_R_MODE_WIDTH 2 /* R_MODE - [1:0] */ + +/* + * R50 (0x32) - Analogue Left Mix 0 + */ +#define WM8903_DACL_TO_MIXOUTL 0x0008 /* DACL_TO_MIXOUTL */ +#define WM8903_DACL_TO_MIXOUTL_MASK 0x0008 /* DACL_TO_MIXOUTL */ +#define WM8903_DACL_TO_MIXOUTL_SHIFT 3 /* DACL_TO_MIXOUTL */ +#define WM8903_DACL_TO_MIXOUTL_WIDTH 1 /* DACL_TO_MIXOUTL */ +#define WM8903_DACR_TO_MIXOUTL 0x0004 /* DACR_TO_MIXOUTL */ +#define WM8903_DACR_TO_MIXOUTL_MASK 0x0004 /* DACR_TO_MIXOUTL */ +#define WM8903_DACR_TO_MIXOUTL_SHIFT 2 /* DACR_TO_MIXOUTL */ +#define WM8903_DACR_TO_MIXOUTL_WIDTH 1 /* DACR_TO_MIXOUTL */ +#define WM8903_BYPASSL_TO_MIXOUTL 0x0002 /* BYPASSL_TO_MIXOUTL */ +#define WM8903_BYPASSL_TO_MIXOUTL_MASK 0x0002 /* BYPASSL_TO_MIXOUTL */ +#define WM8903_BYPASSL_TO_MIXOUTL_SHIFT 1 /* BYPASSL_TO_MIXOUTL */ +#define WM8903_BYPASSL_TO_MIXOUTL_WIDTH 1 /* BYPASSL_TO_MIXOUTL */ +#define WM8903_BYPASSR_TO_MIXOUTL 0x0001 /* BYPASSR_TO_MIXOUTL */ +#define WM8903_BYPASSR_TO_MIXOUTL_MASK 0x0001 /* BYPASSR_TO_MIXOUTL */ +#define WM8903_BYPASSR_TO_MIXOUTL_SHIFT 0 /* BYPASSR_TO_MIXOUTL */ +#define WM8903_BYPASSR_TO_MIXOUTL_WIDTH 1 /* BYPASSR_TO_MIXOUTL */ + +/* + * R51 (0x33) - Analogue Right Mix 0 + */ +#define WM8903_DACL_TO_MIXOUTR 0x0008 /* DACL_TO_MIXOUTR */ +#define WM8903_DACL_TO_MIXOUTR_MASK 0x0008 /* DACL_TO_MIXOUTR */ +#define WM8903_DACL_TO_MIXOUTR_SHIFT 3 /* DACL_TO_MIXOUTR */ +#define WM8903_DACL_TO_MIXOUTR_WIDTH 1 /* DACL_TO_MIXOUTR */ +#define WM8903_DACR_TO_MIXOUTR 0x0004 /* DACR_TO_MIXOUTR */ +#define WM8903_DACR_TO_MIXOUTR_MASK 0x0004 /* DACR_TO_MIXOUTR */ +#define WM8903_DACR_TO_MIXOUTR_SHIFT 2 /* DACR_TO_MIXOUTR */ +#define WM8903_DACR_TO_MIXOUTR_WIDTH 1 /* DACR_TO_MIXOUTR */ +#define WM8903_BYPASSL_TO_MIXOUTR 0x0002 /* BYPASSL_TO_MIXOUTR */ +#define WM8903_BYPASSL_TO_MIXOUTR_MASK 0x0002 /* BYPASSL_TO_MIXOUTR */ +#define WM8903_BYPASSL_TO_MIXOUTR_SHIFT 1 /* BYPASSL_TO_MIXOUTR */ +#define WM8903_BYPASSL_TO_MIXOUTR_WIDTH 1 /* BYPASSL_TO_MIXOUTR */ +#define WM8903_BYPASSR_TO_MIXOUTR 0x0001 /* BYPASSR_TO_MIXOUTR */ +#define WM8903_BYPASSR_TO_MIXOUTR_MASK 0x0001 /* BYPASSR_TO_MIXOUTR */ +#define WM8903_BYPASSR_TO_MIXOUTR_SHIFT 0 /* BYPASSR_TO_MIXOUTR */ +#define WM8903_BYPASSR_TO_MIXOUTR_WIDTH 1 /* BYPASSR_TO_MIXOUTR */ + +/* + * R52 (0x34) - Analogue Spk Mix Left 0 + */ +#define WM8903_DACL_TO_MIXSPKL 0x0008 /* DACL_TO_MIXSPKL */ +#define WM8903_DACL_TO_MIXSPKL_MASK 0x0008 /* DACL_TO_MIXSPKL */ +#define WM8903_DACL_TO_MIXSPKL_SHIFT 3 /* DACL_TO_MIXSPKL */ +#define WM8903_DACL_TO_MIXSPKL_WIDTH 1 /* DACL_TO_MIXSPKL */ +#define WM8903_DACR_TO_MIXSPKL 0x0004 /* DACR_TO_MIXSPKL */ +#define WM8903_DACR_TO_MIXSPKL_MASK 0x0004 /* DACR_TO_MIXSPKL */ +#define WM8903_DACR_TO_MIXSPKL_SHIFT 2 /* DACR_TO_MIXSPKL */ +#define WM8903_DACR_TO_MIXSPKL_WIDTH 1 /* DACR_TO_MIXSPKL */ +#define WM8903_BYPASSL_TO_MIXSPKL 0x0002 /* BYPASSL_TO_MIXSPKL */ +#define WM8903_BYPASSL_TO_MIXSPKL_MASK 0x0002 /* BYPASSL_TO_MIXSPKL */ +#define WM8903_BYPASSL_TO_MIXSPKL_SHIFT 1 /* BYPASSL_TO_MIXSPKL */ +#define WM8903_BYPASSL_TO_MIXSPKL_WIDTH 1 /* BYPASSL_TO_MIXSPKL */ +#define WM8903_BYPASSR_TO_MIXSPKL 0x0001 /* BYPASSR_TO_MIXSPKL */ +#define WM8903_BYPASSR_TO_MIXSPKL_MASK 0x0001 /* BYPASSR_TO_MIXSPKL */ +#define WM8903_BYPASSR_TO_MIXSPKL_SHIFT 0 /* BYPASSR_TO_MIXSPKL */ +#define WM8903_BYPASSR_TO_MIXSPKL_WIDTH 1 /* BYPASSR_TO_MIXSPKL */ + +/* + * R53 (0x35) - Analogue Spk Mix Left 1 + */ +#define WM8903_DACL_MIXSPKL_VOL 0x0008 /* DACL_MIXSPKL_VOL */ +#define WM8903_DACL_MIXSPKL_VOL_MASK 0x0008 /* DACL_MIXSPKL_VOL */ +#define WM8903_DACL_MIXSPKL_VOL_SHIFT 3 /* DACL_MIXSPKL_VOL */ +#define WM8903_DACL_MIXSPKL_VOL_WIDTH 1 /* DACL_MIXSPKL_VOL */ +#define WM8903_DACR_MIXSPKL_VOL 0x0004 /* DACR_MIXSPKL_VOL */ +#define WM8903_DACR_MIXSPKL_VOL_MASK 0x0004 /* DACR_MIXSPKL_VOL */ +#define WM8903_DACR_MIXSPKL_VOL_SHIFT 2 /* DACR_MIXSPKL_VOL */ +#define WM8903_DACR_MIXSPKL_VOL_WIDTH 1 /* DACR_MIXSPKL_VOL */ +#define WM8903_BYPASSL_MIXSPKL_VOL 0x0002 /* BYPASSL_MIXSPKL_VOL */ +#define WM8903_BYPASSL_MIXSPKL_VOL_MASK 0x0002 /* BYPASSL_MIXSPKL_VOL */ +#define WM8903_BYPASSL_MIXSPKL_VOL_SHIFT 1 /* BYPASSL_MIXSPKL_VOL */ +#define WM8903_BYPASSL_MIXSPKL_VOL_WIDTH 1 /* BYPASSL_MIXSPKL_VOL */ +#define WM8903_BYPASSR_MIXSPKL_VOL 0x0001 /* BYPASSR_MIXSPKL_VOL */ +#define WM8903_BYPASSR_MIXSPKL_VOL_MASK 0x0001 /* BYPASSR_MIXSPKL_VOL */ +#define WM8903_BYPASSR_MIXSPKL_VOL_SHIFT 0 /* BYPASSR_MIXSPKL_VOL */ +#define WM8903_BYPASSR_MIXSPKL_VOL_WIDTH 1 /* BYPASSR_MIXSPKL_VOL */ + +/* + * R54 (0x36) - Analogue Spk Mix Right 0 + */ +#define WM8903_DACL_TO_MIXSPKR 0x0008 /* DACL_TO_MIXSPKR */ +#define WM8903_DACL_TO_MIXSPKR_MASK 0x0008 /* DACL_TO_MIXSPKR */ +#define WM8903_DACL_TO_MIXSPKR_SHIFT 3 /* DACL_TO_MIXSPKR */ +#define WM8903_DACL_TO_MIXSPKR_WIDTH 1 /* DACL_TO_MIXSPKR */ +#define WM8903_DACR_TO_MIXSPKR 0x0004 /* DACR_TO_MIXSPKR */ +#define WM8903_DACR_TO_MIXSPKR_MASK 0x0004 /* DACR_TO_MIXSPKR */ +#define WM8903_DACR_TO_MIXSPKR_SHIFT 2 /* DACR_TO_MIXSPKR */ +#define WM8903_DACR_TO_MIXSPKR_WIDTH 1 /* DACR_TO_MIXSPKR */ +#define WM8903_BYPASSL_TO_MIXSPKR 0x0002 /* BYPASSL_TO_MIXSPKR */ +#define WM8903_BYPASSL_TO_MIXSPKR_MASK 0x0002 /* BYPASSL_TO_MIXSPKR */ +#define WM8903_BYPASSL_TO_MIXSPKR_SHIFT 1 /* BYPASSL_TO_MIXSPKR */ +#define WM8903_BYPASSL_TO_MIXSPKR_WIDTH 1 /* BYPASSL_TO_MIXSPKR */ +#define WM8903_BYPASSR_TO_MIXSPKR 0x0001 /* BYPASSR_TO_MIXSPKR */ +#define WM8903_BYPASSR_TO_MIXSPKR_MASK 0x0001 /* BYPASSR_TO_MIXSPKR */ +#define WM8903_BYPASSR_TO_MIXSPKR_SHIFT 0 /* BYPASSR_TO_MIXSPKR */ +#define WM8903_BYPASSR_TO_MIXSPKR_WIDTH 1 /* BYPASSR_TO_MIXSPKR */ + +/* + * R55 (0x37) - Analogue Spk Mix Right 1 + */ +#define WM8903_DACL_MIXSPKR_VOL 0x0008 /* DACL_MIXSPKR_VOL */ +#define WM8903_DACL_MIXSPKR_VOL_MASK 0x0008 /* DACL_MIXSPKR_VOL */ +#define WM8903_DACL_MIXSPKR_VOL_SHIFT 3 /* DACL_MIXSPKR_VOL */ +#define WM8903_DACL_MIXSPKR_VOL_WIDTH 1 /* DACL_MIXSPKR_VOL */ +#define WM8903_DACR_MIXSPKR_VOL 0x0004 /* DACR_MIXSPKR_VOL */ +#define WM8903_DACR_MIXSPKR_VOL_MASK 0x0004 /* DACR_MIXSPKR_VOL */ +#define WM8903_DACR_MIXSPKR_VOL_SHIFT 2 /* DACR_MIXSPKR_VOL */ +#define WM8903_DACR_MIXSPKR_VOL_WIDTH 1 /* DACR_MIXSPKR_VOL */ +#define WM8903_BYPASSL_MIXSPKR_VOL 0x0002 /* BYPASSL_MIXSPKR_VOL */ +#define WM8903_BYPASSL_MIXSPKR_VOL_MASK 0x0002 /* BYPASSL_MIXSPKR_VOL */ +#define WM8903_BYPASSL_MIXSPKR_VOL_SHIFT 1 /* BYPASSL_MIXSPKR_VOL */ +#define WM8903_BYPASSL_MIXSPKR_VOL_WIDTH 1 /* BYPASSL_MIXSPKR_VOL */ +#define WM8903_BYPASSR_MIXSPKR_VOL 0x0001 /* BYPASSR_MIXSPKR_VOL */ +#define WM8903_BYPASSR_MIXSPKR_VOL_MASK 0x0001 /* BYPASSR_MIXSPKR_VOL */ +#define WM8903_BYPASSR_MIXSPKR_VOL_SHIFT 0 /* BYPASSR_MIXSPKR_VOL */ +#define WM8903_BYPASSR_MIXSPKR_VOL_WIDTH 1 /* BYPASSR_MIXSPKR_VOL */ + +/* + * R57 (0x39) - Analogue OUT1 Left + */ +#define WM8903_HPL_MUTE 0x0100 /* HPL_MUTE */ +#define WM8903_HPL_MUTE_MASK 0x0100 /* HPL_MUTE */ +#define WM8903_HPL_MUTE_SHIFT 8 /* HPL_MUTE */ +#define WM8903_HPL_MUTE_WIDTH 1 /* HPL_MUTE */ +#define WM8903_HPOUTVU 0x0080 /* HPOUTVU */ +#define WM8903_HPOUTVU_MASK 0x0080 /* HPOUTVU */ +#define WM8903_HPOUTVU_SHIFT 7 /* HPOUTVU */ +#define WM8903_HPOUTVU_WIDTH 1 /* HPOUTVU */ +#define WM8903_HPOUTLZC 0x0040 /* HPOUTLZC */ +#define WM8903_HPOUTLZC_MASK 0x0040 /* HPOUTLZC */ +#define WM8903_HPOUTLZC_SHIFT 6 /* HPOUTLZC */ +#define WM8903_HPOUTLZC_WIDTH 1 /* HPOUTLZC */ +#define WM8903_HPOUTL_VOL_MASK 0x003F /* HPOUTL_VOL - [5:0] */ +#define WM8903_HPOUTL_VOL_SHIFT 0 /* HPOUTL_VOL - [5:0] */ +#define WM8903_HPOUTL_VOL_WIDTH 6 /* HPOUTL_VOL - [5:0] */ + +/* + * R58 (0x3A) - Analogue OUT1 Right + */ +#define WM8903_HPR_MUTE 0x0100 /* HPR_MUTE */ +#define WM8903_HPR_MUTE_MASK 0x0100 /* HPR_MUTE */ +#define WM8903_HPR_MUTE_SHIFT 8 /* HPR_MUTE */ +#define WM8903_HPR_MUTE_WIDTH 1 /* HPR_MUTE */ +#define WM8903_HPOUTVU 0x0080 /* HPOUTVU */ +#define WM8903_HPOUTVU_MASK 0x0080 /* HPOUTVU */ +#define WM8903_HPOUTVU_SHIFT 7 /* HPOUTVU */ +#define WM8903_HPOUTVU_WIDTH 1 /* HPOUTVU */ +#define WM8903_HPOUTRZC 0x0040 /* HPOUTRZC */ +#define WM8903_HPOUTRZC_MASK 0x0040 /* HPOUTRZC */ +#define WM8903_HPOUTRZC_SHIFT 6 /* HPOUTRZC */ +#define WM8903_HPOUTRZC_WIDTH 1 /* HPOUTRZC */ +#define WM8903_HPOUTR_VOL_MASK 0x003F /* HPOUTR_VOL - [5:0] */ +#define WM8903_HPOUTR_VOL_SHIFT 0 /* HPOUTR_VOL - [5:0] */ +#define WM8903_HPOUTR_VOL_WIDTH 6 /* HPOUTR_VOL - [5:0] */ + +/* + * R59 (0x3B) - Analogue OUT2 Left + */ +#define WM8903_LINEOUTL_MUTE 0x0100 /* LINEOUTL_MUTE */ +#define WM8903_LINEOUTL_MUTE_MASK 0x0100 /* LINEOUTL_MUTE */ +#define WM8903_LINEOUTL_MUTE_SHIFT 8 /* LINEOUTL_MUTE */ +#define WM8903_LINEOUTL_MUTE_WIDTH 1 /* LINEOUTL_MUTE */ +#define WM8903_LINEOUTVU 0x0080 /* LINEOUTVU */ +#define WM8903_LINEOUTVU_MASK 0x0080 /* LINEOUTVU */ +#define WM8903_LINEOUTVU_SHIFT 7 /* LINEOUTVU */ +#define WM8903_LINEOUTVU_WIDTH 1 /* LINEOUTVU */ +#define WM8903_LINEOUTLZC 0x0040 /* LINEOUTLZC */ +#define WM8903_LINEOUTLZC_MASK 0x0040 /* LINEOUTLZC */ +#define WM8903_LINEOUTLZC_SHIFT 6 /* LINEOUTLZC */ +#define WM8903_LINEOUTLZC_WIDTH 1 /* LINEOUTLZC */ +#define WM8903_LINEOUTL_VOL_MASK 0x003F /* LINEOUTL_VOL - [5:0] */ +#define WM8903_LINEOUTL_VOL_SHIFT 0 /* LINEOUTL_VOL - [5:0] */ +#define WM8903_LINEOUTL_VOL_WIDTH 6 /* LINEOUTL_VOL - [5:0] */ + +/* + * R60 (0x3C) - Analogue OUT2 Right + */ +#define WM8903_LINEOUTR_MUTE 0x0100 /* LINEOUTR_MUTE */ +#define WM8903_LINEOUTR_MUTE_MASK 0x0100 /* LINEOUTR_MUTE */ +#define WM8903_LINEOUTR_MUTE_SHIFT 8 /* LINEOUTR_MUTE */ +#define WM8903_LINEOUTR_MUTE_WIDTH 1 /* LINEOUTR_MUTE */ +#define WM8903_LINEOUTVU 0x0080 /* LINEOUTVU */ +#define WM8903_LINEOUTVU_MASK 0x0080 /* LINEOUTVU */ +#define WM8903_LINEOUTVU_SHIFT 7 /* LINEOUTVU */ +#define WM8903_LINEOUTVU_WIDTH 1 /* LINEOUTVU */ +#define WM8903_LINEOUTRZC 0x0040 /* LINEOUTRZC */ +#define WM8903_LINEOUTRZC_MASK 0x0040 /* LINEOUTRZC */ +#define WM8903_LINEOUTRZC_SHIFT 6 /* LINEOUTRZC */ +#define WM8903_LINEOUTRZC_WIDTH 1 /* LINEOUTRZC */ +#define WM8903_LINEOUTR_VOL_MASK 0x003F /* LINEOUTR_VOL - [5:0] */ +#define WM8903_LINEOUTR_VOL_SHIFT 0 /* LINEOUTR_VOL - [5:0] */ +#define WM8903_LINEOUTR_VOL_WIDTH 6 /* LINEOUTR_VOL - [5:0] */ + +/* + * R62 (0x3E) - Analogue OUT3 Left + */ +#define WM8903_SPKL_MUTE 0x0100 /* SPKL_MUTE */ +#define WM8903_SPKL_MUTE_MASK 0x0100 /* SPKL_MUTE */ +#define WM8903_SPKL_MUTE_SHIFT 8 /* SPKL_MUTE */ +#define WM8903_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */ +#define WM8903_SPKVU 0x0080 /* SPKVU */ +#define WM8903_SPKVU_MASK 0x0080 /* SPKVU */ +#define WM8903_SPKVU_SHIFT 7 /* SPKVU */ +#define WM8903_SPKVU_WIDTH 1 /* SPKVU */ +#define WM8903_SPKLZC 0x0040 /* SPKLZC */ +#define WM8903_SPKLZC_MASK 0x0040 /* SPKLZC */ +#define WM8903_SPKLZC_SHIFT 6 /* SPKLZC */ +#define WM8903_SPKLZC_WIDTH 1 /* SPKLZC */ +#define WM8903_SPKL_VOL_MASK 0x003F /* SPKL_VOL - [5:0] */ +#define WM8903_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [5:0] */ +#define WM8903_SPKL_VOL_WIDTH 6 /* SPKL_VOL - [5:0] */ + +/* + * R63 (0x3F) - Analogue OUT3 Right + */ +#define WM8903_SPKR_MUTE 0x0100 /* SPKR_MUTE */ +#define WM8903_SPKR_MUTE_MASK 0x0100 /* SPKR_MUTE */ +#define WM8903_SPKR_MUTE_SHIFT 8 /* SPKR_MUTE */ +#define WM8903_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */ +#define WM8903_SPKVU 0x0080 /* SPKVU */ +#define WM8903_SPKVU_MASK 0x0080 /* SPKVU */ +#define WM8903_SPKVU_SHIFT 7 /* SPKVU */ +#define WM8903_SPKVU_WIDTH 1 /* SPKVU */ +#define WM8903_SPKRZC 0x0040 /* SPKRZC */ +#define WM8903_SPKRZC_MASK 0x0040 /* SPKRZC */ +#define WM8903_SPKRZC_SHIFT 6 /* SPKRZC */ +#define WM8903_SPKRZC_WIDTH 1 /* SPKRZC */ +#define WM8903_SPKR_VOL_MASK 0x003F /* SPKR_VOL - [5:0] */ +#define WM8903_SPKR_VOL_SHIFT 0 /* SPKR_VOL - [5:0] */ +#define WM8903_SPKR_VOL_WIDTH 6 /* SPKR_VOL - [5:0] */ + +/* + * R65 (0x41) - Analogue SPK Output Control 0 + */ +#define WM8903_SPK_DISCHARGE 0x0002 /* SPK_DISCHARGE */ +#define WM8903_SPK_DISCHARGE_MASK 0x0002 /* SPK_DISCHARGE */ +#define WM8903_SPK_DISCHARGE_SHIFT 1 /* SPK_DISCHARGE */ +#define WM8903_SPK_DISCHARGE_WIDTH 1 /* SPK_DISCHARGE */ +#define WM8903_VROI 0x0001 /* VROI */ +#define WM8903_VROI_MASK 0x0001 /* VROI */ +#define WM8903_VROI_SHIFT 0 /* VROI */ +#define WM8903_VROI_WIDTH 1 /* VROI */ + +/* + * R67 (0x43) - DC Servo 0 + */ +#define WM8903_DCS_MASTER_ENA 0x0010 /* DCS_MASTER_ENA */ +#define WM8903_DCS_MASTER_ENA_MASK 0x0010 /* DCS_MASTER_ENA */ +#define WM8903_DCS_MASTER_ENA_SHIFT 4 /* DCS_MASTER_ENA */ +#define WM8903_DCS_MASTER_ENA_WIDTH 1 /* DCS_MASTER_ENA */ +#define WM8903_DCS_ENA_MASK 0x000F /* DCS_ENA - [3:0] */ +#define WM8903_DCS_ENA_SHIFT 0 /* DCS_ENA - [3:0] */ +#define WM8903_DCS_ENA_WIDTH 4 /* DCS_ENA - [3:0] */ + +/* + * R69 (0x45) - DC Servo 2 + */ +#define WM8903_DCS_MODE_MASK 0x0003 /* DCS_MODE - [1:0] */ +#define WM8903_DCS_MODE_SHIFT 0 /* DCS_MODE - [1:0] */ +#define WM8903_DCS_MODE_WIDTH 2 /* DCS_MODE - [1:0] */ + +/* + * R90 (0x5A) - Analogue HP 0 + */ +#define WM8903_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */ +#define WM8903_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */ +#define WM8903_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */ +#define WM8903_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */ +#define WM8903_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */ +#define WM8903_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */ +#define WM8903_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */ +#define WM8903_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */ +#define WM8903_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */ +#define WM8903_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */ +#define WM8903_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */ +#define WM8903_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */ +#define WM8903_HPL_ENA 0x0010 /* HPL_ENA */ +#define WM8903_HPL_ENA_MASK 0x0010 /* HPL_ENA */ +#define WM8903_HPL_ENA_SHIFT 4 /* HPL_ENA */ +#define WM8903_HPL_ENA_WIDTH 1 /* HPL_ENA */ +#define WM8903_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */ +#define WM8903_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */ +#define WM8903_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */ +#define WM8903_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */ +#define WM8903_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */ +#define WM8903_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */ +#define WM8903_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */ +#define WM8903_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */ +#define WM8903_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */ +#define WM8903_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */ +#define WM8903_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */ +#define WM8903_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */ +#define WM8903_HPR_ENA 0x0001 /* HPR_ENA */ +#define WM8903_HPR_ENA_MASK 0x0001 /* HPR_ENA */ +#define WM8903_HPR_ENA_SHIFT 0 /* HPR_ENA */ +#define WM8903_HPR_ENA_WIDTH 1 /* HPR_ENA */ + +/* + * R94 (0x5E) - Analogue Lineout 0 + */ +#define WM8903_LINEOUTL_RMV_SHORT 0x0080 /* LINEOUTL_RMV_SHORT */ +#define WM8903_LINEOUTL_RMV_SHORT_MASK 0x0080 /* LINEOUTL_RMV_SHORT */ +#define WM8903_LINEOUTL_RMV_SHORT_SHIFT 7 /* LINEOUTL_RMV_SHORT */ +#define WM8903_LINEOUTL_RMV_SHORT_WIDTH 1 /* LINEOUTL_RMV_SHORT */ +#define WM8903_LINEOUTL_ENA_OUTP 0x0040 /* LINEOUTL_ENA_OUTP */ +#define WM8903_LINEOUTL_ENA_OUTP_MASK 0x0040 /* LINEOUTL_ENA_OUTP */ +#define WM8903_LINEOUTL_ENA_OUTP_SHIFT 6 /* LINEOUTL_ENA_OUTP */ +#define WM8903_LINEOUTL_ENA_OUTP_WIDTH 1 /* LINEOUTL_ENA_OUTP */ +#define WM8903_LINEOUTL_ENA_DLY 0x0020 /* LINEOUTL_ENA_DLY */ +#define WM8903_LINEOUTL_ENA_DLY_MASK 0x0020 /* LINEOUTL_ENA_DLY */ +#define WM8903_LINEOUTL_ENA_DLY_SHIFT 5 /* LINEOUTL_ENA_DLY */ +#define WM8903_LINEOUTL_ENA_DLY_WIDTH 1 /* LINEOUTL_ENA_DLY */ +#define WM8903_LINEOUTL_ENA 0x0010 /* LINEOUTL_ENA */ +#define WM8903_LINEOUTL_ENA_MASK 0x0010 /* LINEOUTL_ENA */ +#define WM8903_LINEOUTL_ENA_SHIFT 4 /* LINEOUTL_ENA */ +#define WM8903_LINEOUTL_ENA_WIDTH 1 /* LINEOUTL_ENA */ +#define WM8903_LINEOUTR_RMV_SHORT 0x0008 /* LINEOUTR_RMV_SHORT */ +#define WM8903_LINEOUTR_RMV_SHORT_MASK 0x0008 /* LINEOUTR_RMV_SHORT */ +#define WM8903_LINEOUTR_RMV_SHORT_SHIFT 3 /* LINEOUTR_RMV_SHORT */ +#define WM8903_LINEOUTR_RMV_SHORT_WIDTH 1 /* LINEOUTR_RMV_SHORT */ +#define WM8903_LINEOUTR_ENA_OUTP 0x0004 /* LINEOUTR_ENA_OUTP */ +#define WM8903_LINEOUTR_ENA_OUTP_MASK 0x0004 /* LINEOUTR_ENA_OUTP */ +#define WM8903_LINEOUTR_ENA_OUTP_SHIFT 2 /* LINEOUTR_ENA_OUTP */ +#define WM8903_LINEOUTR_ENA_OUTP_WIDTH 1 /* LINEOUTR_ENA_OUTP */ +#define WM8903_LINEOUTR_ENA_DLY 0x0002 /* LINEOUTR_ENA_DLY */ +#define WM8903_LINEOUTR_ENA_DLY_MASK 0x0002 /* LINEOUTR_ENA_DLY */ +#define WM8903_LINEOUTR_ENA_DLY_SHIFT 1 /* LINEOUTR_ENA_DLY */ +#define WM8903_LINEOUTR_ENA_DLY_WIDTH 1 /* LINEOUTR_ENA_DLY */ +#define WM8903_LINEOUTR_ENA 0x0001 /* LINEOUTR_ENA */ +#define WM8903_LINEOUTR_ENA_MASK 0x0001 /* LINEOUTR_ENA */ +#define WM8903_LINEOUTR_ENA_SHIFT 0 /* LINEOUTR_ENA */ +#define WM8903_LINEOUTR_ENA_WIDTH 1 /* LINEOUTR_ENA */ + +/* + * R98 (0x62) - Charge Pump 0 + */ +#define WM8903_CP_ENA 0x0001 /* CP_ENA */ +#define WM8903_CP_ENA_MASK 0x0001 /* CP_ENA */ +#define WM8903_CP_ENA_SHIFT 0 /* CP_ENA */ +#define WM8903_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R104 (0x68) - Class W 0 + */ +#define WM8903_CP_DYN_FREQ 0x0002 /* CP_DYN_FREQ */ +#define WM8903_CP_DYN_FREQ_MASK 0x0002 /* CP_DYN_FREQ */ +#define WM8903_CP_DYN_FREQ_SHIFT 1 /* CP_DYN_FREQ */ +#define WM8903_CP_DYN_FREQ_WIDTH 1 /* CP_DYN_FREQ */ +#define WM8903_CP_DYN_V 0x0001 /* CP_DYN_V */ +#define WM8903_CP_DYN_V_MASK 0x0001 /* CP_DYN_V */ +#define WM8903_CP_DYN_V_SHIFT 0 /* CP_DYN_V */ +#define WM8903_CP_DYN_V_WIDTH 1 /* CP_DYN_V */ + +/* + * R108 (0x6C) - Write Sequencer 0 + */ +#define WM8903_WSEQ_ENA 0x0100 /* WSEQ_ENA */ +#define WM8903_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */ +#define WM8903_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */ +#define WM8903_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM8903_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8903_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8903_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */ + +/* + * R109 (0x6D) - Write Sequencer 1 + */ +#define WM8903_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8903_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8903_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8903_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */ +#define WM8903_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */ +#define WM8903_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */ +#define WM8903_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */ +#define WM8903_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */ +#define WM8903_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */ + +/* + * R110 (0x6E) - Write Sequencer 2 + */ +#define WM8903_WSEQ_EOS 0x4000 /* WSEQ_EOS */ +#define WM8903_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */ +#define WM8903_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */ +#define WM8903_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */ +#define WM8903_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */ +#define WM8903_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */ +#define WM8903_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */ +#define WM8903_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */ +#define WM8903_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */ +#define WM8903_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */ + +/* + * R111 (0x6F) - Write Sequencer 3 + */ +#define WM8903_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */ +#define WM8903_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */ +#define WM8903_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */ +#define WM8903_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM8903_WSEQ_START 0x0100 /* WSEQ_START */ +#define WM8903_WSEQ_START_MASK 0x0100 /* WSEQ_START */ +#define WM8903_WSEQ_START_SHIFT 8 /* WSEQ_START */ +#define WM8903_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM8903_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */ +#define WM8903_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */ +#define WM8903_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */ + +/* + * R112 (0x70) - Write Sequencer 4 + */ +#define WM8903_WSEQ_CURRENT_INDEX_MASK 0x03F0 /* WSEQ_CURRENT_INDEX - [9:4] */ +#define WM8903_WSEQ_CURRENT_INDEX_SHIFT 4 /* WSEQ_CURRENT_INDEX - [9:4] */ +#define WM8903_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [9:4] */ +#define WM8903_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */ +#define WM8903_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */ +#define WM8903_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */ +#define WM8903_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ + +/* + * R114 (0x72) - Control Interface + */ +#define WM8903_MASK_WRITE_ENA 0x0001 /* MASK_WRITE_ENA */ +#define WM8903_MASK_WRITE_ENA_MASK 0x0001 /* MASK_WRITE_ENA */ +#define WM8903_MASK_WRITE_ENA_SHIFT 0 /* MASK_WRITE_ENA */ +#define WM8903_MASK_WRITE_ENA_WIDTH 1 /* MASK_WRITE_ENA */ + +/* + * R121 (0x79) - Interrupt Status 1 + */ +#define WM8903_MICSHRT_EINT 0x8000 /* MICSHRT_EINT */ +#define WM8903_MICSHRT_EINT_MASK 0x8000 /* MICSHRT_EINT */ +#define WM8903_MICSHRT_EINT_SHIFT 15 /* MICSHRT_EINT */ +#define WM8903_MICSHRT_EINT_WIDTH 1 /* MICSHRT_EINT */ +#define WM8903_MICDET_EINT 0x4000 /* MICDET_EINT */ +#define WM8903_MICDET_EINT_MASK 0x4000 /* MICDET_EINT */ +#define WM8903_MICDET_EINT_SHIFT 14 /* MICDET_EINT */ +#define WM8903_MICDET_EINT_WIDTH 1 /* MICDET_EINT */ +#define WM8903_WSEQ_BUSY_EINT 0x2000 /* WSEQ_BUSY_EINT */ +#define WM8903_WSEQ_BUSY_EINT_MASK 0x2000 /* WSEQ_BUSY_EINT */ +#define WM8903_WSEQ_BUSY_EINT_SHIFT 13 /* WSEQ_BUSY_EINT */ +#define WM8903_WSEQ_BUSY_EINT_WIDTH 1 /* WSEQ_BUSY_EINT */ +#define WM8903_GP5_EINT 0x0010 /* GP5_EINT */ +#define WM8903_GP5_EINT_MASK 0x0010 /* GP5_EINT */ +#define WM8903_GP5_EINT_SHIFT 4 /* GP5_EINT */ +#define WM8903_GP5_EINT_WIDTH 1 /* GP5_EINT */ +#define WM8903_GP4_EINT 0x0008 /* GP4_EINT */ +#define WM8903_GP4_EINT_MASK 0x0008 /* GP4_EINT */ +#define WM8903_GP4_EINT_SHIFT 3 /* GP4_EINT */ +#define WM8903_GP4_EINT_WIDTH 1 /* GP4_EINT */ +#define WM8903_GP3_EINT 0x0004 /* GP3_EINT */ +#define WM8903_GP3_EINT_MASK 0x0004 /* GP3_EINT */ +#define WM8903_GP3_EINT_SHIFT 2 /* GP3_EINT */ +#define WM8903_GP3_EINT_WIDTH 1 /* GP3_EINT */ +#define WM8903_GP2_EINT 0x0002 /* GP2_EINT */ +#define WM8903_GP2_EINT_MASK 0x0002 /* GP2_EINT */ +#define WM8903_GP2_EINT_SHIFT 1 /* GP2_EINT */ +#define WM8903_GP2_EINT_WIDTH 1 /* GP2_EINT */ +#define WM8903_GP1_EINT 0x0001 /* GP1_EINT */ +#define WM8903_GP1_EINT_MASK 0x0001 /* GP1_EINT */ +#define WM8903_GP1_EINT_SHIFT 0 /* GP1_EINT */ +#define WM8903_GP1_EINT_WIDTH 1 /* GP1_EINT */ + +/* + * R122 (0x7A) - Interrupt Status 1 Mask + */ +#define WM8903_IM_MICSHRT_EINT 0x8000 /* IM_MICSHRT_EINT */ +#define WM8903_IM_MICSHRT_EINT_MASK 0x8000 /* IM_MICSHRT_EINT */ +#define WM8903_IM_MICSHRT_EINT_SHIFT 15 /* IM_MICSHRT_EINT */ +#define WM8903_IM_MICSHRT_EINT_WIDTH 1 /* IM_MICSHRT_EINT */ +#define WM8903_IM_MICDET_EINT 0x4000 /* IM_MICDET_EINT */ +#define WM8903_IM_MICDET_EINT_MASK 0x4000 /* IM_MICDET_EINT */ +#define WM8903_IM_MICDET_EINT_SHIFT 14 /* IM_MICDET_EINT */ +#define WM8903_IM_MICDET_EINT_WIDTH 1 /* IM_MICDET_EINT */ +#define WM8903_IM_WSEQ_BUSY_EINT 0x2000 /* IM_WSEQ_BUSY_EINT */ +#define WM8903_IM_WSEQ_BUSY_EINT_MASK 0x2000 /* IM_WSEQ_BUSY_EINT */ +#define WM8903_IM_WSEQ_BUSY_EINT_SHIFT 13 /* IM_WSEQ_BUSY_EINT */ +#define WM8903_IM_WSEQ_BUSY_EINT_WIDTH 1 /* IM_WSEQ_BUSY_EINT */ +#define WM8903_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */ +#define WM8903_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */ +#define WM8903_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */ +#define WM8903_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */ +#define WM8903_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */ +#define WM8903_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */ +#define WM8903_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */ +#define WM8903_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */ +#define WM8903_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */ +#define WM8903_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */ +#define WM8903_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */ +#define WM8903_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */ +#define WM8903_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */ +#define WM8903_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */ +#define WM8903_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */ +#define WM8903_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */ +#define WM8903_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */ +#define WM8903_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */ +#define WM8903_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */ +#define WM8903_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */ + +/* + * R123 (0x7B) - Interrupt Polarity 1 + */ +#define WM8903_MICSHRT_INV 0x8000 /* MICSHRT_INV */ +#define WM8903_MICSHRT_INV_MASK 0x8000 /* MICSHRT_INV */ +#define WM8903_MICSHRT_INV_SHIFT 15 /* MICSHRT_INV */ +#define WM8903_MICSHRT_INV_WIDTH 1 /* MICSHRT_INV */ +#define WM8903_MICDET_INV 0x4000 /* MICDET_INV */ +#define WM8903_MICDET_INV_MASK 0x4000 /* MICDET_INV */ +#define WM8903_MICDET_INV_SHIFT 14 /* MICDET_INV */ +#define WM8903_MICDET_INV_WIDTH 1 /* MICDET_INV */ + +/* + * R126 (0x7E) - Interrupt Control + */ +#define WM8903_IRQ_POL 0x0001 /* IRQ_POL */ +#define WM8903_IRQ_POL_MASK 0x0001 /* IRQ_POL */ +#define WM8903_IRQ_POL_SHIFT 0 /* IRQ_POL */ +#define WM8903_IRQ_POL_WIDTH 1 /* IRQ_POL */ + +/* + * R164 (0xA4) - Clock Rate Test 4 + */ +#define WM8903_ADC_DIG_MIC 0x0200 /* ADC_DIG_MIC */ +#define WM8903_ADC_DIG_MIC_MASK 0x0200 /* ADC_DIG_MIC */ +#define WM8903_ADC_DIG_MIC_SHIFT 9 /* ADC_DIG_MIC */ +#define WM8903_ADC_DIG_MIC_WIDTH 1 /* ADC_DIG_MIC */ + +/* + * R172 (0xAC) - Analogue Output Bias 0 + */ +#define WM8903_PGA_BIAS_MASK 0x0070 /* PGA_BIAS - [6:4] */ +#define WM8903_PGA_BIAS_SHIFT 4 /* PGA_BIAS - [6:4] */ +#define WM8903_PGA_BIAS_WIDTH 3 /* PGA_BIAS - [6:4] */ + +#endif diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c new file mode 100644 index 0000000000..829bf05562 --- /dev/null +++ b/sound/soc/codecs/wm8904.c @@ -0,0 +1,2351 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8904.c -- WM8904 ALSA SoC Audio driver + * + * Copyright 2009-12 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8904.h" + +enum wm8904_type { + WM8904, + WM8912, +}; + +#define WM8904_NUM_DCS_CHANNELS 4 + +#define WM8904_NUM_SUPPLIES 5 +static const char *wm8904_supply_names[WM8904_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "AVDD", + "CPVDD", + "MICVDD", +}; + +/* codec private data */ +struct wm8904_priv { + struct regmap *regmap; + struct clk *mclk; + + enum wm8904_type devtype; + + struct regulator_bulk_data supplies[WM8904_NUM_SUPPLIES]; + + struct wm8904_pdata *pdata; + + int deemph; + + /* Platform provided DRC configuration */ + const char **drc_texts; + int drc_cfg; + struct soc_enum drc_enum; + + /* Platform provided ReTune mobile configuration */ + int num_retune_mobile_texts; + const char **retune_mobile_texts; + int retune_mobile_cfg; + struct soc_enum retune_mobile_enum; + + /* FLL setup */ + int fll_src; + int fll_fref; + int fll_fout; + + /* Clocking configuration */ + unsigned int mclk_rate; + int sysclk_src; + unsigned int sysclk_rate; + + int tdm_width; + int tdm_slots; + int bclk; + int fs; + + /* DC servo configuration - cached offset values */ + int dcs_state[WM8904_NUM_DCS_CHANNELS]; +}; + +static const struct reg_default wm8904_reg_defaults[] = { + { 4, 0x0018 }, /* R4 - Bias Control 0 */ + { 5, 0x0000 }, /* R5 - VMID Control 0 */ + { 6, 0x0000 }, /* R6 - Mic Bias Control 0 */ + { 7, 0x0000 }, /* R7 - Mic Bias Control 1 */ + { 8, 0x0001 }, /* R8 - Analogue DAC 0 */ + { 9, 0x9696 }, /* R9 - mic Filter Control */ + { 10, 0x0001 }, /* R10 - Analogue ADC 0 */ + { 12, 0x0000 }, /* R12 - Power Management 0 */ + { 14, 0x0000 }, /* R14 - Power Management 2 */ + { 15, 0x0000 }, /* R15 - Power Management 3 */ + { 18, 0x0000 }, /* R18 - Power Management 6 */ + { 20, 0x945E }, /* R20 - Clock Rates 0 */ + { 21, 0x0C05 }, /* R21 - Clock Rates 1 */ + { 22, 0x0006 }, /* R22 - Clock Rates 2 */ + { 24, 0x0050 }, /* R24 - Audio Interface 0 */ + { 25, 0x000A }, /* R25 - Audio Interface 1 */ + { 26, 0x00E4 }, /* R26 - Audio Interface 2 */ + { 27, 0x0040 }, /* R27 - Audio Interface 3 */ + { 30, 0x00C0 }, /* R30 - DAC Digital Volume Left */ + { 31, 0x00C0 }, /* R31 - DAC Digital Volume Right */ + { 32, 0x0000 }, /* R32 - DAC Digital 0 */ + { 33, 0x0008 }, /* R33 - DAC Digital 1 */ + { 36, 0x00C0 }, /* R36 - ADC Digital Volume Left */ + { 37, 0x00C0 }, /* R37 - ADC Digital Volume Right */ + { 38, 0x0010 }, /* R38 - ADC Digital 0 */ + { 39, 0x0000 }, /* R39 - Digital Microphone 0 */ + { 40, 0x01AF }, /* R40 - DRC 0 */ + { 41, 0x3248 }, /* R41 - DRC 1 */ + { 42, 0x0000 }, /* R42 - DRC 2 */ + { 43, 0x0000 }, /* R43 - DRC 3 */ + { 44, 0x0085 }, /* R44 - Analogue Left Input 0 */ + { 45, 0x0085 }, /* R45 - Analogue Right Input 0 */ + { 46, 0x0044 }, /* R46 - Analogue Left Input 1 */ + { 47, 0x0044 }, /* R47 - Analogue Right Input 1 */ + { 57, 0x002D }, /* R57 - Analogue OUT1 Left */ + { 58, 0x002D }, /* R58 - Analogue OUT1 Right */ + { 59, 0x0039 }, /* R59 - Analogue OUT2 Left */ + { 60, 0x0039 }, /* R60 - Analogue OUT2 Right */ + { 61, 0x0000 }, /* R61 - Analogue OUT12 ZC */ + { 67, 0x0000 }, /* R67 - DC Servo 0 */ + { 69, 0xAAAA }, /* R69 - DC Servo 2 */ + { 71, 0xAAAA }, /* R71 - DC Servo 4 */ + { 72, 0xAAAA }, /* R72 - DC Servo 5 */ + { 90, 0x0000 }, /* R90 - Analogue HP 0 */ + { 94, 0x0000 }, /* R94 - Analogue Lineout 0 */ + { 98, 0x0000 }, /* R98 - Charge Pump 0 */ + { 104, 0x0004 }, /* R104 - Class W 0 */ + { 108, 0x0000 }, /* R108 - Write Sequencer 0 */ + { 109, 0x0000 }, /* R109 - Write Sequencer 1 */ + { 110, 0x0000 }, /* R110 - Write Sequencer 2 */ + { 111, 0x0000 }, /* R111 - Write Sequencer 3 */ + { 112, 0x0000 }, /* R112 - Write Sequencer 4 */ + { 116, 0x0000 }, /* R116 - FLL Control 1 */ + { 117, 0x0007 }, /* R117 - FLL Control 2 */ + { 118, 0x0000 }, /* R118 - FLL Control 3 */ + { 119, 0x2EE0 }, /* R119 - FLL Control 4 */ + { 120, 0x0004 }, /* R120 - FLL Control 5 */ + { 121, 0x0014 }, /* R121 - GPIO Control 1 */ + { 122, 0x0010 }, /* R122 - GPIO Control 2 */ + { 123, 0x0010 }, /* R123 - GPIO Control 3 */ + { 124, 0x0000 }, /* R124 - GPIO Control 4 */ + { 126, 0x0000 }, /* R126 - Digital Pulls */ + { 128, 0xFFFF }, /* R128 - Interrupt Status Mask */ + { 129, 0x0000 }, /* R129 - Interrupt Polarity */ + { 130, 0x0000 }, /* R130 - Interrupt Debounce */ + { 134, 0x0000 }, /* R134 - EQ1 */ + { 135, 0x000C }, /* R135 - EQ2 */ + { 136, 0x000C }, /* R136 - EQ3 */ + { 137, 0x000C }, /* R137 - EQ4 */ + { 138, 0x000C }, /* R138 - EQ5 */ + { 139, 0x000C }, /* R139 - EQ6 */ + { 140, 0x0FCA }, /* R140 - EQ7 */ + { 141, 0x0400 }, /* R141 - EQ8 */ + { 142, 0x00D8 }, /* R142 - EQ9 */ + { 143, 0x1EB5 }, /* R143 - EQ10 */ + { 144, 0xF145 }, /* R144 - EQ11 */ + { 145, 0x0B75 }, /* R145 - EQ12 */ + { 146, 0x01C5 }, /* R146 - EQ13 */ + { 147, 0x1C58 }, /* R147 - EQ14 */ + { 148, 0xF373 }, /* R148 - EQ15 */ + { 149, 0x0A54 }, /* R149 - EQ16 */ + { 150, 0x0558 }, /* R150 - EQ17 */ + { 151, 0x168E }, /* R151 - EQ18 */ + { 152, 0xF829 }, /* R152 - EQ19 */ + { 153, 0x07AD }, /* R153 - EQ20 */ + { 154, 0x1103 }, /* R154 - EQ21 */ + { 155, 0x0564 }, /* R155 - EQ22 */ + { 156, 0x0559 }, /* R156 - EQ23 */ + { 157, 0x4000 }, /* R157 - EQ24 */ + { 161, 0x0000 }, /* R161 - Control Interface Test 1 */ + { 204, 0x0000 }, /* R204 - Analogue Output Bias 0 */ + { 247, 0x0000 }, /* R247 - FLL NCO Test 0 */ + { 248, 0x0019 }, /* R248 - FLL NCO Test 1 */ +}; + +static bool wm8904_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8904_SW_RESET_AND_ID: + case WM8904_REVISION: + case WM8904_DC_SERVO_1: + case WM8904_DC_SERVO_6: + case WM8904_DC_SERVO_7: + case WM8904_DC_SERVO_8: + case WM8904_DC_SERVO_9: + case WM8904_DC_SERVO_READBACK_0: + case WM8904_INTERRUPT_STATUS: + return true; + default: + return false; + } +} + +static bool wm8904_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8904_SW_RESET_AND_ID: + case WM8904_REVISION: + case WM8904_BIAS_CONTROL_0: + case WM8904_VMID_CONTROL_0: + case WM8904_MIC_BIAS_CONTROL_0: + case WM8904_MIC_BIAS_CONTROL_1: + case WM8904_ANALOGUE_DAC_0: + case WM8904_MIC_FILTER_CONTROL: + case WM8904_ANALOGUE_ADC_0: + case WM8904_POWER_MANAGEMENT_0: + case WM8904_POWER_MANAGEMENT_2: + case WM8904_POWER_MANAGEMENT_3: + case WM8904_POWER_MANAGEMENT_6: + case WM8904_CLOCK_RATES_0: + case WM8904_CLOCK_RATES_1: + case WM8904_CLOCK_RATES_2: + case WM8904_AUDIO_INTERFACE_0: + case WM8904_AUDIO_INTERFACE_1: + case WM8904_AUDIO_INTERFACE_2: + case WM8904_AUDIO_INTERFACE_3: + case WM8904_DAC_DIGITAL_VOLUME_LEFT: + case WM8904_DAC_DIGITAL_VOLUME_RIGHT: + case WM8904_DAC_DIGITAL_0: + case WM8904_DAC_DIGITAL_1: + case WM8904_ADC_DIGITAL_VOLUME_LEFT: + case WM8904_ADC_DIGITAL_VOLUME_RIGHT: + case WM8904_ADC_DIGITAL_0: + case WM8904_DIGITAL_MICROPHONE_0: + case WM8904_DRC_0: + case WM8904_DRC_1: + case WM8904_DRC_2: + case WM8904_DRC_3: + case WM8904_ANALOGUE_LEFT_INPUT_0: + case WM8904_ANALOGUE_RIGHT_INPUT_0: + case WM8904_ANALOGUE_LEFT_INPUT_1: + case WM8904_ANALOGUE_RIGHT_INPUT_1: + case WM8904_ANALOGUE_OUT1_LEFT: + case WM8904_ANALOGUE_OUT1_RIGHT: + case WM8904_ANALOGUE_OUT2_LEFT: + case WM8904_ANALOGUE_OUT2_RIGHT: + case WM8904_ANALOGUE_OUT12_ZC: + case WM8904_DC_SERVO_0: + case WM8904_DC_SERVO_1: + case WM8904_DC_SERVO_2: + case WM8904_DC_SERVO_4: + case WM8904_DC_SERVO_5: + case WM8904_DC_SERVO_6: + case WM8904_DC_SERVO_7: + case WM8904_DC_SERVO_8: + case WM8904_DC_SERVO_9: + case WM8904_DC_SERVO_READBACK_0: + case WM8904_ANALOGUE_HP_0: + case WM8904_ANALOGUE_LINEOUT_0: + case WM8904_CHARGE_PUMP_0: + case WM8904_CLASS_W_0: + case WM8904_WRITE_SEQUENCER_0: + case WM8904_WRITE_SEQUENCER_1: + case WM8904_WRITE_SEQUENCER_2: + case WM8904_WRITE_SEQUENCER_3: + case WM8904_WRITE_SEQUENCER_4: + case WM8904_FLL_CONTROL_1: + case WM8904_FLL_CONTROL_2: + case WM8904_FLL_CONTROL_3: + case WM8904_FLL_CONTROL_4: + case WM8904_FLL_CONTROL_5: + case WM8904_GPIO_CONTROL_1: + case WM8904_GPIO_CONTROL_2: + case WM8904_GPIO_CONTROL_3: + case WM8904_GPIO_CONTROL_4: + case WM8904_DIGITAL_PULLS: + case WM8904_INTERRUPT_STATUS: + case WM8904_INTERRUPT_STATUS_MASK: + case WM8904_INTERRUPT_POLARITY: + case WM8904_INTERRUPT_DEBOUNCE: + case WM8904_EQ1: + case WM8904_EQ2: + case WM8904_EQ3: + case WM8904_EQ4: + case WM8904_EQ5: + case WM8904_EQ6: + case WM8904_EQ7: + case WM8904_EQ8: + case WM8904_EQ9: + case WM8904_EQ10: + case WM8904_EQ11: + case WM8904_EQ12: + case WM8904_EQ13: + case WM8904_EQ14: + case WM8904_EQ15: + case WM8904_EQ16: + case WM8904_EQ17: + case WM8904_EQ18: + case WM8904_EQ19: + case WM8904_EQ20: + case WM8904_EQ21: + case WM8904_EQ22: + case WM8904_EQ23: + case WM8904_EQ24: + case WM8904_CONTROL_INTERFACE_TEST_1: + case WM8904_ADC_TEST_0: + case WM8904_ANALOGUE_OUTPUT_BIAS_0: + case WM8904_FLL_NCO_TEST_0: + case WM8904_FLL_NCO_TEST_1: + return true; + default: + return false; + } +} + +static int wm8904_configure_clocking(struct snd_soc_component *component) +{ + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + unsigned int clock0, clock2, rate; + + /* Gate the clock while we're updating to avoid misclocking */ + clock2 = snd_soc_component_read(component, WM8904_CLOCK_RATES_2); + snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2, + WM8904_SYSCLK_SRC, 0); + + /* This should be done on init() for bypass paths */ + switch (wm8904->sysclk_src) { + case WM8904_CLK_MCLK: + dev_dbg(component->dev, "Using %dHz MCLK\n", wm8904->mclk_rate); + + clock2 &= ~WM8904_SYSCLK_SRC; + rate = wm8904->mclk_rate; + + /* Ensure the FLL is stopped */ + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, + WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0); + break; + + case WM8904_CLK_FLL: + dev_dbg(component->dev, "Using %dHz FLL clock\n", + wm8904->fll_fout); + + clock2 |= WM8904_SYSCLK_SRC; + rate = wm8904->fll_fout; + break; + + default: + dev_err(component->dev, "System clock not configured\n"); + return -EINVAL; + } + + /* SYSCLK shouldn't be over 13.5MHz */ + if (rate > 13500000) { + clock0 = WM8904_MCLK_DIV; + wm8904->sysclk_rate = rate / 2; + } else { + clock0 = 0; + wm8904->sysclk_rate = rate; + } + + snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_0, WM8904_MCLK_DIV, + clock0); + + snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2, + WM8904_CLK_SYS_ENA | WM8904_SYSCLK_SRC, clock2); + + dev_dbg(component->dev, "CLK_SYS is %dHz\n", wm8904->sysclk_rate); + + return 0; +} + +static void wm8904_set_drc(struct snd_soc_component *component) +{ + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + struct wm8904_pdata *pdata = wm8904->pdata; + int save, i; + + /* Save any enables; the configuration should clear them. */ + save = snd_soc_component_read(component, WM8904_DRC_0); + + for (i = 0; i < WM8904_DRC_REGS; i++) + snd_soc_component_update_bits(component, WM8904_DRC_0 + i, 0xffff, + pdata->drc_cfgs[wm8904->drc_cfg].regs[i]); + + /* Reenable the DRC */ + snd_soc_component_update_bits(component, WM8904_DRC_0, + WM8904_DRC_ENA | WM8904_DRC_DAC_PATH, save); +} + +static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + struct wm8904_pdata *pdata = wm8904->pdata; + int value = ucontrol->value.enumerated.item[0]; + + if (value >= pdata->num_drc_cfgs) + return -EINVAL; + + wm8904->drc_cfg = value; + + wm8904_set_drc(component); + + return 0; +} + +static int wm8904_get_drc_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wm8904->drc_cfg; + + return 0; +} + +static void wm8904_set_retune_mobile(struct snd_soc_component *component) +{ + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + struct wm8904_pdata *pdata = wm8904->pdata; + int best, best_val, save, i, cfg; + + if (!pdata || !wm8904->num_retune_mobile_texts) + return; + + /* Find the version of the currently selected configuration + * with the nearest sample rate. */ + cfg = wm8904->retune_mobile_cfg; + best = 0; + best_val = INT_MAX; + for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) { + if (strcmp(pdata->retune_mobile_cfgs[i].name, + wm8904->retune_mobile_texts[cfg]) == 0 && + abs(pdata->retune_mobile_cfgs[i].rate + - wm8904->fs) < best_val) { + best = i; + best_val = abs(pdata->retune_mobile_cfgs[i].rate + - wm8904->fs); + } + } + + dev_dbg(component->dev, "ReTune Mobile %s/%dHz for %dHz sample rate\n", + pdata->retune_mobile_cfgs[best].name, + pdata->retune_mobile_cfgs[best].rate, + wm8904->fs); + + /* The EQ will be disabled while reconfiguring it, remember the + * current configuration. + */ + save = snd_soc_component_read(component, WM8904_EQ1); + + for (i = 0; i < WM8904_EQ_REGS; i++) + snd_soc_component_update_bits(component, WM8904_EQ1 + i, 0xffff, + pdata->retune_mobile_cfgs[best].regs[i]); + + snd_soc_component_update_bits(component, WM8904_EQ1, WM8904_EQ_ENA, save); +} + +static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + struct wm8904_pdata *pdata = wm8904->pdata; + int value = ucontrol->value.enumerated.item[0]; + + if (value >= pdata->num_retune_mobile_cfgs) + return -EINVAL; + + wm8904->retune_mobile_cfg = value; + + wm8904_set_retune_mobile(component); + + return 0; +} + +static int wm8904_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wm8904->retune_mobile_cfg; + + return 0; +} + +static int deemph_settings[] = { 0, 32000, 44100, 48000 }; + +static int wm8904_set_deemph(struct snd_soc_component *component) +{ + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8904->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { + if (abs(deemph_settings[i] - wm8904->fs) < + abs(deemph_settings[best] - wm8904->fs)) + best = i; + } + + val = best << WM8904_DEEMPH_SHIFT; + } else { + val = 0; + } + + dev_dbg(component->dev, "Set deemphasis %d\n", val); + + return snd_soc_component_update_bits(component, WM8904_DAC_DIGITAL_1, + WM8904_DEEMPH_MASK, val); +} + +static int wm8904_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wm8904->deemph; + return 0; +} + +static int wm8904_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; + + if (deemph > 1) + return -EINVAL; + + wm8904->deemph = deemph; + + return wm8904_set_deemph(component); +} + +static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); + +static const char *hpf_mode_text[] = { + "Hi-fi", "Voice 1", "Voice 2", "Voice 3" +}; + +static SOC_ENUM_SINGLE_DECL(hpf_mode, WM8904_ADC_DIGITAL_0, 5, + hpf_mode_text); + +static int wm8904_adc_osr_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int val; + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + if (ucontrol->value.integer.value[0]) + val = 0; + else + val = WM8904_ADC_128_OSR_TST_MODE | WM8904_ADC_BIASX1P5; + + snd_soc_component_update_bits(component, WM8904_ADC_TEST_0, + WM8904_ADC_128_OSR_TST_MODE | WM8904_ADC_BIASX1P5, + val); + + return ret; +} + +static const struct snd_kcontrol_new wm8904_adc_snd_controls[] = { +SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8904_ADC_DIGITAL_VOLUME_LEFT, + WM8904_ADC_DIGITAL_VOLUME_RIGHT, 1, 119, 0, digital_tlv), + +/* No TLV since it depends on mode */ +SOC_DOUBLE_R("Capture Volume", WM8904_ANALOGUE_LEFT_INPUT_0, + WM8904_ANALOGUE_RIGHT_INPUT_0, 0, 31, 0), +SOC_DOUBLE_R("Capture Switch", WM8904_ANALOGUE_LEFT_INPUT_0, + WM8904_ANALOGUE_RIGHT_INPUT_0, 7, 1, 1), + +SOC_SINGLE("High Pass Filter Switch", WM8904_ADC_DIGITAL_0, 4, 1, 0), +SOC_ENUM("High Pass Filter Mode", hpf_mode), +SOC_SINGLE_EXT("ADC 128x OSR Switch", WM8904_ANALOGUE_ADC_0, 0, 1, 0, + snd_soc_get_volsw, wm8904_adc_osr_put), +}; + +static const char *drc_path_text[] = { + "ADC", "DAC" +}; + +static SOC_ENUM_SINGLE_DECL(drc_path, WM8904_DRC_0, 14, drc_path_text); + +static const struct snd_kcontrol_new wm8904_dac_snd_controls[] = { +SOC_SINGLE_TLV("Digital Playback Boost Volume", + WM8904_AUDIO_INTERFACE_0, 9, 3, 0, dac_boost_tlv), +SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8904_DAC_DIGITAL_VOLUME_LEFT, + WM8904_DAC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv), + +SOC_DOUBLE_R_TLV("Headphone Volume", WM8904_ANALOGUE_OUT1_LEFT, + WM8904_ANALOGUE_OUT1_RIGHT, 0, 63, 0, out_tlv), +SOC_DOUBLE_R("Headphone Switch", WM8904_ANALOGUE_OUT1_LEFT, + WM8904_ANALOGUE_OUT1_RIGHT, 8, 1, 1), +SOC_DOUBLE_R("Headphone ZC Switch", WM8904_ANALOGUE_OUT1_LEFT, + WM8904_ANALOGUE_OUT1_RIGHT, 6, 1, 0), + +SOC_DOUBLE_R_TLV("Line Output Volume", WM8904_ANALOGUE_OUT2_LEFT, + WM8904_ANALOGUE_OUT2_RIGHT, 0, 63, 0, out_tlv), +SOC_DOUBLE_R("Line Output Switch", WM8904_ANALOGUE_OUT2_LEFT, + WM8904_ANALOGUE_OUT2_RIGHT, 8, 1, 1), +SOC_DOUBLE_R("Line Output ZC Switch", WM8904_ANALOGUE_OUT2_LEFT, + WM8904_ANALOGUE_OUT2_RIGHT, 6, 1, 0), + +SOC_SINGLE("EQ Switch", WM8904_EQ1, 0, 1, 0), +SOC_SINGLE("DRC Switch", WM8904_DRC_0, 15, 1, 0), +SOC_ENUM("DRC Path", drc_path), +SOC_SINGLE("DAC OSRx2 Switch", WM8904_DAC_DIGITAL_1, 6, 1, 0), +SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, + wm8904_get_deemph, wm8904_put_deemph), +}; + +static const struct snd_kcontrol_new wm8904_snd_controls[] = { +SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8904_DAC_DIGITAL_0, 4, 8, 15, 0, + sidetone_tlv), +}; + +static const struct snd_kcontrol_new wm8904_eq_controls[] = { +SOC_SINGLE_TLV("EQ1 Volume", WM8904_EQ2, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 Volume", WM8904_EQ3, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 Volume", WM8904_EQ4, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 Volume", WM8904_EQ5, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ5 Volume", WM8904_EQ6, 0, 24, 0, eq_tlv), +}; + +static int cp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (WARN_ON(event != SND_SOC_DAPM_POST_PMU)) + return -EINVAL; + + /* Maximum startup time */ + udelay(500); + + return 0; +} + +static int sysclk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* If we're using the FLL then we only start it when + * required; we assume that the configuration has been + * done previously and all we need to do is kick it + * off. + */ + switch (wm8904->sysclk_src) { + case WM8904_CLK_FLL: + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, + WM8904_FLL_OSC_ENA, + WM8904_FLL_OSC_ENA); + + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, + WM8904_FLL_ENA, + WM8904_FLL_ENA); + break; + + default: + break; + } + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, + WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0); + break; + } + + return 0; +} + +static int out_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + int reg, val; + int dcs_mask; + int dcs_l, dcs_r; + int dcs_l_reg, dcs_r_reg; + int an_out_reg; + int timeout; + int pwr_reg; + + /* This code is shared between HP and LINEOUT; we do all our + * power management in stereo pairs to avoid latency issues so + * we reuse shift to identify which rather than strcmp() the + * name. */ + reg = w->shift; + + switch (reg) { + case WM8904_ANALOGUE_HP_0: + pwr_reg = WM8904_POWER_MANAGEMENT_2; + dcs_mask = WM8904_DCS_ENA_CHAN_0 | WM8904_DCS_ENA_CHAN_1; + dcs_r_reg = WM8904_DC_SERVO_8; + dcs_l_reg = WM8904_DC_SERVO_9; + an_out_reg = WM8904_ANALOGUE_OUT1_LEFT; + dcs_l = 0; + dcs_r = 1; + break; + case WM8904_ANALOGUE_LINEOUT_0: + pwr_reg = WM8904_POWER_MANAGEMENT_3; + dcs_mask = WM8904_DCS_ENA_CHAN_2 | WM8904_DCS_ENA_CHAN_3; + dcs_r_reg = WM8904_DC_SERVO_6; + dcs_l_reg = WM8904_DC_SERVO_7; + an_out_reg = WM8904_ANALOGUE_OUT2_LEFT; + dcs_l = 2; + dcs_r = 3; + break; + default: + WARN(1, "Invalid reg %d\n", reg); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Power on the PGAs */ + snd_soc_component_update_bits(component, pwr_reg, + WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA, + WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA); + + /* Power on the amplifier */ + snd_soc_component_update_bits(component, reg, + WM8904_HPL_ENA | WM8904_HPR_ENA, + WM8904_HPL_ENA | WM8904_HPR_ENA); + + + /* Enable the first stage */ + snd_soc_component_update_bits(component, reg, + WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY, + WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY); + + /* Power up the DC servo */ + snd_soc_component_update_bits(component, WM8904_DC_SERVO_0, + dcs_mask, dcs_mask); + + /* Either calibrate the DC servo or restore cached state + * if we have that. + */ + if (wm8904->dcs_state[dcs_l] || wm8904->dcs_state[dcs_r]) { + dev_dbg(component->dev, "Restoring DC servo state\n"); + + snd_soc_component_write(component, dcs_l_reg, + wm8904->dcs_state[dcs_l]); + snd_soc_component_write(component, dcs_r_reg, + wm8904->dcs_state[dcs_r]); + + snd_soc_component_write(component, WM8904_DC_SERVO_1, dcs_mask); + + timeout = 20; + } else { + dev_dbg(component->dev, "Calibrating DC servo\n"); + + snd_soc_component_write(component, WM8904_DC_SERVO_1, + dcs_mask << WM8904_DCS_TRIG_STARTUP_0_SHIFT); + + timeout = 500; + } + + /* Wait for DC servo to complete */ + dcs_mask <<= WM8904_DCS_CAL_COMPLETE_SHIFT; + do { + val = snd_soc_component_read(component, WM8904_DC_SERVO_READBACK_0); + if ((val & dcs_mask) == dcs_mask) + break; + + msleep(1); + } while (--timeout); + + if ((val & dcs_mask) != dcs_mask) + dev_warn(component->dev, "DC servo timed out\n"); + else + dev_dbg(component->dev, "DC servo ready\n"); + + /* Enable the output stage */ + snd_soc_component_update_bits(component, reg, + WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP, + WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP); + + /* Update volume, requires PGA to be powered */ + val = snd_soc_component_read(component, an_out_reg); + snd_soc_component_write(component, an_out_reg, val); + break; + + case SND_SOC_DAPM_POST_PMU: + /* Unshort the output itself */ + snd_soc_component_update_bits(component, reg, + WM8904_HPL_RMV_SHORT | + WM8904_HPR_RMV_SHORT, + WM8904_HPL_RMV_SHORT | + WM8904_HPR_RMV_SHORT); + + break; + + case SND_SOC_DAPM_PRE_PMD: + /* Short the output */ + snd_soc_component_update_bits(component, reg, + WM8904_HPL_RMV_SHORT | + WM8904_HPR_RMV_SHORT, 0); + break; + + case SND_SOC_DAPM_POST_PMD: + /* Cache the DC servo configuration; this will be + * invalidated if we change the configuration. */ + wm8904->dcs_state[dcs_l] = snd_soc_component_read(component, dcs_l_reg); + wm8904->dcs_state[dcs_r] = snd_soc_component_read(component, dcs_r_reg); + + snd_soc_component_update_bits(component, WM8904_DC_SERVO_0, + dcs_mask, 0); + + /* Disable the amplifier input and output stages */ + snd_soc_component_update_bits(component, reg, + WM8904_HPL_ENA | WM8904_HPR_ENA | + WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY | + WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP, + 0); + + /* PGAs too */ + snd_soc_component_update_bits(component, pwr_reg, + WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA, + 0); + break; + } + + return 0; +} + +static const char *input_mode_text[] = { + "Single-Ended", "Differential Line", "Differential Mic" +}; + +static const char *lin_text[] = { + "IN1L", "IN2L", "IN3L" +}; + +static SOC_ENUM_SINGLE_DECL(lin_enum, WM8904_ANALOGUE_LEFT_INPUT_1, 2, + lin_text); + +static const struct snd_kcontrol_new lin_mux = + SOC_DAPM_ENUM("Left Capture Mux", lin_enum); + +static SOC_ENUM_SINGLE_DECL(lin_inv_enum, WM8904_ANALOGUE_LEFT_INPUT_1, 4, + lin_text); + +static const struct snd_kcontrol_new lin_inv_mux = + SOC_DAPM_ENUM("Left Capture Inverting Mux", lin_inv_enum); + +static SOC_ENUM_SINGLE_DECL(lin_mode_enum, + WM8904_ANALOGUE_LEFT_INPUT_1, 0, + input_mode_text); + +static const struct snd_kcontrol_new lin_mode = + SOC_DAPM_ENUM("Left Capture Mode", lin_mode_enum); + +static const char *rin_text[] = { + "IN1R", "IN2R", "IN3R" +}; + +static SOC_ENUM_SINGLE_DECL(rin_enum, WM8904_ANALOGUE_RIGHT_INPUT_1, 2, + rin_text); + +static const struct snd_kcontrol_new rin_mux = + SOC_DAPM_ENUM("Right Capture Mux", rin_enum); + +static SOC_ENUM_SINGLE_DECL(rin_inv_enum, WM8904_ANALOGUE_RIGHT_INPUT_1, 4, + rin_text); + +static const struct snd_kcontrol_new rin_inv_mux = + SOC_DAPM_ENUM("Right Capture Inverting Mux", rin_inv_enum); + +static SOC_ENUM_SINGLE_DECL(rin_mode_enum, + WM8904_ANALOGUE_RIGHT_INPUT_1, 0, + input_mode_text); + +static const struct snd_kcontrol_new rin_mode = + SOC_DAPM_ENUM("Right Capture Mode", rin_mode_enum); + +static const char *aif_text[] = { + "Left", "Right" +}; + +static SOC_ENUM_SINGLE_DECL(aifoutl_enum, WM8904_AUDIO_INTERFACE_0, 7, + aif_text); + +static const struct snd_kcontrol_new aifoutl_mux = + SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum); + +static SOC_ENUM_SINGLE_DECL(aifoutr_enum, WM8904_AUDIO_INTERFACE_0, 6, + aif_text); + +static const struct snd_kcontrol_new aifoutr_mux = + SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum); + +static SOC_ENUM_SINGLE_DECL(aifinl_enum, WM8904_AUDIO_INTERFACE_0, 5, + aif_text); + +static const struct snd_kcontrol_new aifinl_mux = + SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum); + +static SOC_ENUM_SINGLE_DECL(aifinr_enum, WM8904_AUDIO_INTERFACE_0, 4, + aif_text); + +static const struct snd_kcontrol_new aifinr_mux = + SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum); + +static const struct snd_soc_dapm_widget wm8904_core_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", WM8904_CLOCK_RATES_2, 2, 0, sysclk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8904_CLOCK_RATES_2, 1, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("TOCLK", WM8904_CLOCK_RATES_2, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_widget wm8904_adc_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), +SND_SOC_DAPM_INPUT("IN3L"), +SND_SOC_DAPM_INPUT("IN3R"), + +SND_SOC_DAPM_SUPPLY("MICBIAS", WM8904_MIC_BIAS_CONTROL_0, 0, 0, NULL, 0), + +SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lin_mux), +SND_SOC_DAPM_MUX("Left Capture Inverting Mux", SND_SOC_NOPM, 0, 0, + &lin_inv_mux), +SND_SOC_DAPM_MUX("Left Capture Mode", SND_SOC_NOPM, 0, 0, &lin_mode), +SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rin_mux), +SND_SOC_DAPM_MUX("Right Capture Inverting Mux", SND_SOC_NOPM, 0, 0, + &rin_inv_mux), +SND_SOC_DAPM_MUX("Right Capture Mode", SND_SOC_NOPM, 0, 0, &rin_mode), + +SND_SOC_DAPM_PGA("Left Capture PGA", WM8904_POWER_MANAGEMENT_0, 1, 0, + NULL, 0), +SND_SOC_DAPM_PGA("Right Capture PGA", WM8904_POWER_MANAGEMENT_0, 0, 0, + NULL, 0), + +SND_SOC_DAPM_ADC("ADCL", NULL, WM8904_POWER_MANAGEMENT_6, 1, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, WM8904_POWER_MANAGEMENT_6, 0, 0), + +SND_SOC_DAPM_MUX("AIFOUTL Mux", SND_SOC_NOPM, 0, 0, &aifoutl_mux), +SND_SOC_DAPM_MUX("AIFOUTR Mux", SND_SOC_NOPM, 0, 0, &aifoutr_mux), + +SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_widget wm8904_dac_dapm_widgets[] = { +SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux), +SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux), + +SND_SOC_DAPM_DAC("DACL", NULL, WM8904_POWER_MANAGEMENT_6, 3, 0), +SND_SOC_DAPM_DAC("DACR", NULL, WM8904_POWER_MANAGEMENT_6, 2, 0), + +SND_SOC_DAPM_SUPPLY("Charge pump", WM8904_CHARGE_PUMP_0, 0, 0, cp_event, + SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("HPL PGA", SND_SOC_NOPM, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("HPR PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_PGA("LINEL PGA", SND_SOC_NOPM, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("LINER PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM, WM8904_ANALOGUE_HP_0, + 0, NULL, 0, out_pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_PGA_E("Line Output", SND_SOC_NOPM, WM8904_ANALOGUE_LINEOUT_0, + 0, NULL, 0, out_pga_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("LINEOUTL"), +SND_SOC_DAPM_OUTPUT("LINEOUTR"), +}; + +static const char *out_mux_text[] = { + "DAC", "Bypass" +}; + +static SOC_ENUM_SINGLE_DECL(hpl_enum, WM8904_ANALOGUE_OUT12_ZC, 3, + out_mux_text); + +static const struct snd_kcontrol_new hpl_mux = + SOC_DAPM_ENUM("HPL Mux", hpl_enum); + +static SOC_ENUM_SINGLE_DECL(hpr_enum, WM8904_ANALOGUE_OUT12_ZC, 2, + out_mux_text); + +static const struct snd_kcontrol_new hpr_mux = + SOC_DAPM_ENUM("HPR Mux", hpr_enum); + +static SOC_ENUM_SINGLE_DECL(linel_enum, WM8904_ANALOGUE_OUT12_ZC, 1, + out_mux_text); + +static const struct snd_kcontrol_new linel_mux = + SOC_DAPM_ENUM("LINEL Mux", linel_enum); + +static SOC_ENUM_SINGLE_DECL(liner_enum, WM8904_ANALOGUE_OUT12_ZC, 0, + out_mux_text); + +static const struct snd_kcontrol_new liner_mux = + SOC_DAPM_ENUM("LINER Mux", liner_enum); + +static const char *sidetone_text[] = { + "None", "Left", "Right" +}; + +static SOC_ENUM_SINGLE_DECL(dacl_sidetone_enum, WM8904_DAC_DIGITAL_0, 2, + sidetone_text); + +static const struct snd_kcontrol_new dacl_sidetone_mux = + SOC_DAPM_ENUM("Left Sidetone Mux", dacl_sidetone_enum); + +static SOC_ENUM_SINGLE_DECL(dacr_sidetone_enum, WM8904_DAC_DIGITAL_0, 0, + sidetone_text); + +static const struct snd_kcontrol_new dacr_sidetone_mux = + SOC_DAPM_ENUM("Right Sidetone Mux", dacr_sidetone_enum); + +static const struct snd_soc_dapm_widget wm8904_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("Class G", WM8904_CLASS_W_0, 0, 1, NULL, 0), +SND_SOC_DAPM_PGA("Left Bypass", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Bypass", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &dacl_sidetone_mux), +SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &dacr_sidetone_mux), + +SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), +SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), +SND_SOC_DAPM_MUX("LINEL Mux", SND_SOC_NOPM, 0, 0, &linel_mux), +SND_SOC_DAPM_MUX("LINER Mux", SND_SOC_NOPM, 0, 0, &liner_mux), +}; + +static const struct snd_soc_dapm_route core_intercon[] = { + { "CLK_DSP", NULL, "SYSCLK" }, + { "TOCLK", NULL, "SYSCLK" }, +}; + +static const struct snd_soc_dapm_route adc_intercon[] = { + { "Left Capture Mux", "IN1L", "IN1L" }, + { "Left Capture Mux", "IN2L", "IN2L" }, + { "Left Capture Mux", "IN3L", "IN3L" }, + + { "Left Capture Inverting Mux", "IN1L", "IN1L" }, + { "Left Capture Inverting Mux", "IN2L", "IN2L" }, + { "Left Capture Inverting Mux", "IN3L", "IN3L" }, + + { "Left Capture Mode", "Single-Ended", "Left Capture Inverting Mux" }, + { "Left Capture Mode", "Differential Line", "Left Capture Mux" }, + { "Left Capture Mode", "Differential Line", "Left Capture Inverting Mux" }, + { "Left Capture Mode", "Differential Mic", "Left Capture Mux" }, + { "Left Capture Mode", "Differential Mic", "Left Capture Inverting Mux" }, + + { "Right Capture Mux", "IN1R", "IN1R" }, + { "Right Capture Mux", "IN2R", "IN2R" }, + { "Right Capture Mux", "IN3R", "IN3R" }, + + { "Right Capture Inverting Mux", "IN1R", "IN1R" }, + { "Right Capture Inverting Mux", "IN2R", "IN2R" }, + { "Right Capture Inverting Mux", "IN3R", "IN3R" }, + + { "Right Capture Mode", "Single-Ended", "Right Capture Inverting Mux" }, + { "Right Capture Mode", "Differential Line", "Right Capture Mux" }, + { "Right Capture Mode", "Differential Line", "Right Capture Inverting Mux" }, + { "Right Capture Mode", "Differential Mic", "Right Capture Mux" }, + { "Right Capture Mode", "Differential Mic", "Right Capture Inverting Mux" }, + + { "Left Capture PGA", NULL, "Left Capture Mode" }, + { "Right Capture PGA", NULL, "Right Capture Mode" }, + + { "AIFOUTL Mux", "Left", "ADCL" }, + { "AIFOUTL Mux", "Right", "ADCR" }, + { "AIFOUTR Mux", "Left", "ADCL" }, + { "AIFOUTR Mux", "Right", "ADCR" }, + + { "AIFOUTL", NULL, "AIFOUTL Mux" }, + { "AIFOUTR", NULL, "AIFOUTR Mux" }, + + { "ADCL", NULL, "CLK_DSP" }, + { "ADCL", NULL, "Left Capture PGA" }, + + { "ADCR", NULL, "CLK_DSP" }, + { "ADCR", NULL, "Right Capture PGA" }, +}; + +static const struct snd_soc_dapm_route dac_intercon[] = { + { "DACL Mux", "Left", "AIFINL" }, + { "DACL Mux", "Right", "AIFINR" }, + + { "DACR Mux", "Left", "AIFINL" }, + { "DACR Mux", "Right", "AIFINR" }, + + { "DACL", NULL, "DACL Mux" }, + { "DACL", NULL, "CLK_DSP" }, + + { "DACR", NULL, "DACR Mux" }, + { "DACR", NULL, "CLK_DSP" }, + + { "Charge pump", NULL, "SYSCLK" }, + + { "Headphone Output", NULL, "HPL PGA" }, + { "Headphone Output", NULL, "HPR PGA" }, + { "Headphone Output", NULL, "Charge pump" }, + { "Headphone Output", NULL, "TOCLK" }, + + { "Line Output", NULL, "LINEL PGA" }, + { "Line Output", NULL, "LINER PGA" }, + { "Line Output", NULL, "Charge pump" }, + { "Line Output", NULL, "TOCLK" }, + + { "HPOUTL", NULL, "Headphone Output" }, + { "HPOUTR", NULL, "Headphone Output" }, + + { "LINEOUTL", NULL, "Line Output" }, + { "LINEOUTR", NULL, "Line Output" }, +}; + +static const struct snd_soc_dapm_route wm8904_intercon[] = { + { "Left Sidetone", "Left", "ADCL" }, + { "Left Sidetone", "Right", "ADCR" }, + { "DACL", NULL, "Left Sidetone" }, + + { "Right Sidetone", "Left", "ADCL" }, + { "Right Sidetone", "Right", "ADCR" }, + { "DACR", NULL, "Right Sidetone" }, + + { "Left Bypass", NULL, "Class G" }, + { "Left Bypass", NULL, "Left Capture PGA" }, + + { "Right Bypass", NULL, "Class G" }, + { "Right Bypass", NULL, "Right Capture PGA" }, + + { "HPL Mux", "DAC", "DACL" }, + { "HPL Mux", "Bypass", "Left Bypass" }, + + { "HPR Mux", "DAC", "DACR" }, + { "HPR Mux", "Bypass", "Right Bypass" }, + + { "LINEL Mux", "DAC", "DACL" }, + { "LINEL Mux", "Bypass", "Left Bypass" }, + + { "LINER Mux", "DAC", "DACR" }, + { "LINER Mux", "Bypass", "Right Bypass" }, + + { "HPL PGA", NULL, "HPL Mux" }, + { "HPR PGA", NULL, "HPR Mux" }, + + { "LINEL PGA", NULL, "LINEL Mux" }, + { "LINER PGA", NULL, "LINER Mux" }, +}; + +static const struct snd_soc_dapm_route wm8912_intercon[] = { + { "HPL PGA", NULL, "DACL" }, + { "HPR PGA", NULL, "DACR" }, + + { "LINEL PGA", NULL, "DACL" }, + { "LINER PGA", NULL, "DACR" }, +}; + +static int wm8904_add_widgets(struct snd_soc_component *component) +{ + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_new_controls(dapm, wm8904_core_dapm_widgets, + ARRAY_SIZE(wm8904_core_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, core_intercon, + ARRAY_SIZE(core_intercon)); + + switch (wm8904->devtype) { + case WM8904: + snd_soc_add_component_controls(component, wm8904_adc_snd_controls, + ARRAY_SIZE(wm8904_adc_snd_controls)); + snd_soc_add_component_controls(component, wm8904_dac_snd_controls, + ARRAY_SIZE(wm8904_dac_snd_controls)); + snd_soc_add_component_controls(component, wm8904_snd_controls, + ARRAY_SIZE(wm8904_snd_controls)); + + snd_soc_dapm_new_controls(dapm, wm8904_adc_dapm_widgets, + ARRAY_SIZE(wm8904_adc_dapm_widgets)); + snd_soc_dapm_new_controls(dapm, wm8904_dac_dapm_widgets, + ARRAY_SIZE(wm8904_dac_dapm_widgets)); + snd_soc_dapm_new_controls(dapm, wm8904_dapm_widgets, + ARRAY_SIZE(wm8904_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, adc_intercon, + ARRAY_SIZE(adc_intercon)); + snd_soc_dapm_add_routes(dapm, dac_intercon, + ARRAY_SIZE(dac_intercon)); + snd_soc_dapm_add_routes(dapm, wm8904_intercon, + ARRAY_SIZE(wm8904_intercon)); + break; + + case WM8912: + snd_soc_add_component_controls(component, wm8904_dac_snd_controls, + ARRAY_SIZE(wm8904_dac_snd_controls)); + + snd_soc_dapm_new_controls(dapm, wm8904_dac_dapm_widgets, + ARRAY_SIZE(wm8904_dac_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, dac_intercon, + ARRAY_SIZE(dac_intercon)); + snd_soc_dapm_add_routes(dapm, wm8912_intercon, + ARRAY_SIZE(wm8912_intercon)); + break; + } + + return 0; +} + +static struct { + int ratio; + unsigned int clk_sys_rate; +} clk_sys_rates[] = { + { 64, 0 }, + { 128, 1 }, + { 192, 2 }, + { 256, 3 }, + { 384, 4 }, + { 512, 5 }, + { 786, 6 }, + { 1024, 7 }, + { 1408, 8 }, + { 1536, 9 }, +}; + +static struct { + int rate; + int sample_rate; +} sample_rates[] = { + { 8000, 0 }, + { 11025, 1 }, + { 12000, 1 }, + { 16000, 2 }, + { 22050, 3 }, + { 24000, 3 }, + { 32000, 4 }, + { 44100, 5 }, + { 48000, 5 }, +}; + +static struct { + int div; /* *10 due to .5s */ + int bclk_div; +} bclk_divs[] = { + { 10, 0 }, + { 15, 1 }, + { 20, 2 }, + { 30, 3 }, + { 40, 4 }, + { 50, 5 }, + { 55, 6 }, + { 60, 7 }, + { 80, 8 }, + { 100, 9 }, + { 110, 10 }, + { 120, 11 }, + { 160, 12 }, + { 200, 13 }, + { 220, 14 }, + { 240, 16 }, + { 200, 17 }, + { 320, 18 }, + { 440, 19 }, + { 480, 20 }, +}; + + +static int wm8904_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + int ret, i, best, best_val, cur_val; + unsigned int aif1 = 0; + unsigned int aif2 = 0; + unsigned int aif3 = 0; + unsigned int clock1 = 0; + unsigned int dac_digital1 = 0; + + /* What BCLK do we need? */ + wm8904->fs = params_rate(params); + if (wm8904->tdm_slots) { + dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n", + wm8904->tdm_slots, wm8904->tdm_width); + wm8904->bclk = snd_soc_calc_bclk(wm8904->fs, + wm8904->tdm_width, 2, + wm8904->tdm_slots); + } else { + wm8904->bclk = snd_soc_params_to_bclk(params); + } + + switch (params_width(params)) { + case 16: + break; + case 20: + aif1 |= 0x40; + break; + case 24: + aif1 |= 0x80; + break; + case 32: + aif1 |= 0xc0; + break; + default: + return -EINVAL; + } + + + dev_dbg(component->dev, "Target BCLK is %dHz\n", wm8904->bclk); + + ret = wm8904_configure_clocking(component); + if (ret != 0) + return ret; + + /* Select nearest CLK_SYS_RATE */ + best = 0; + best_val = abs((wm8904->sysclk_rate / clk_sys_rates[0].ratio) + - wm8904->fs); + for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) { + cur_val = abs((wm8904->sysclk_rate / + clk_sys_rates[i].ratio) - wm8904->fs); + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + dev_dbg(component->dev, "Selected CLK_SYS_RATIO of %d\n", + clk_sys_rates[best].ratio); + clock1 |= (clk_sys_rates[best].clk_sys_rate + << WM8904_CLK_SYS_RATE_SHIFT); + + /* SAMPLE_RATE */ + best = 0; + best_val = abs(wm8904->fs - sample_rates[0].rate); + for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { + /* Closest match */ + cur_val = abs(wm8904->fs - sample_rates[i].rate); + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + dev_dbg(component->dev, "Selected SAMPLE_RATE of %dHz\n", + sample_rates[best].rate); + clock1 |= (sample_rates[best].sample_rate + << WM8904_SAMPLE_RATE_SHIFT); + + /* Enable sloping stopband filter for low sample rates */ + if (wm8904->fs <= 24000) + dac_digital1 |= WM8904_DAC_SB_FILT; + + /* BCLK_DIV */ + best = 0; + best_val = INT_MAX; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = ((wm8904->sysclk_rate * 10) / bclk_divs[i].div) + - wm8904->bclk; + if (cur_val < 0) /* Table is sorted */ + break; + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + wm8904->bclk = (wm8904->sysclk_rate * 10) / bclk_divs[best].div; + dev_dbg(component->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n", + bclk_divs[best].div, wm8904->bclk); + aif2 |= bclk_divs[best].bclk_div; + + /* LRCLK is a simple fraction of BCLK */ + dev_dbg(component->dev, "LRCLK_RATE is %d\n", wm8904->bclk / wm8904->fs); + aif3 |= wm8904->bclk / wm8904->fs; + + /* Apply the settings */ + snd_soc_component_update_bits(component, WM8904_DAC_DIGITAL_1, + WM8904_DAC_SB_FILT, dac_digital1); + snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_1, + WM8904_AIF_WL_MASK, aif1); + snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_2, + WM8904_BCLK_DIV_MASK, aif2); + snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_3, + WM8904_LRCLK_RATE_MASK, aif3); + snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_1, + WM8904_SAMPLE_RATE_MASK | + WM8904_CLK_SYS_RATE_MASK, clock1); + + /* Update filters for the new settings */ + wm8904_set_retune_mobile(component); + wm8904_set_deemph(component); + + return 0; +} + +static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + unsigned int aif1 = 0; + unsigned int aif3 = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBS_CFM: + aif3 |= WM8904_LRCLK_DIR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + aif1 |= WM8904_BCLK_DIR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + aif1 |= WM8904_BCLK_DIR; + aif3 |= WM8904_LRCLK_DIR; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif1 |= 0x3 | WM8904_AIF_LRCLK_INV; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + aif1 |= 0x3; + break; + case SND_SOC_DAIFMT_I2S: + aif1 |= 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif1 |= 0x1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8904_AIF_BCLK_INV; + break; + default: + return -EINVAL; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif1 |= WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8904_AIF_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif1 |= WM8904_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_1, + WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV | + WM8904_AIF_FMT_MASK | WM8904_BCLK_DIR, aif1); + snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_3, + WM8904_LRCLK_DIR, aif3); + + return 0; +} + + +static int wm8904_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + int aif1 = 0; + + /* Don't need to validate anything if we're turning off TDM */ + if (slots == 0) + goto out; + + /* Note that we allow configurations we can't handle ourselves - + * for example, we can generate clocks for slots 2 and up even if + * we can't use those slots ourselves. + */ + aif1 |= WM8904_AIFADC_TDM | WM8904_AIFDAC_TDM; + + switch (rx_mask) { + case 3: + break; + case 0xc: + aif1 |= WM8904_AIFADC_TDM_CHAN; + break; + default: + return -EINVAL; + } + + + switch (tx_mask) { + case 3: + break; + case 0xc: + aif1 |= WM8904_AIFDAC_TDM_CHAN; + break; + default: + return -EINVAL; + } + +out: + wm8904->tdm_width = slot_width; + wm8904->tdm_slots = slots / 2; + + snd_soc_component_update_bits(component, WM8904_AUDIO_INTERFACE_1, + WM8904_AIFADC_TDM | WM8904_AIFADC_TDM_CHAN | + WM8904_AIFDAC_TDM | WM8904_AIFDAC_TDM_CHAN, aif1); + + return 0; +} + +struct _fll_div { + u16 fll_fratio; + u16 fll_outdiv; + u16 fll_clk_ref_div; + u16 n; + u16 k; +}; + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +static struct { + unsigned int min; + unsigned int max; + u16 fll_fratio; + int ratio; +} fll_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, + unsigned int Fout) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, target; + unsigned int div; + int i; + + /* Fref must be <=13.5MHz */ + div = 1; + fll_div->fll_clk_ref_div = 0; + while ((Fref / div) > 13500000) { + div *= 2; + fll_div->fll_clk_ref_div++; + + if (div > 8) { + pr_err("Can't scale %dMHz input down to <=13.5MHz\n", + Fref); + return -EINVAL; + } + } + + pr_debug("Fref=%u Fout=%u\n", Fref, Fout); + + /* Apply the division for our remaining calculations */ + Fref /= div; + + /* Fvco should be 90-100MHz; don't check the upper bound */ + div = 4; + while (Fout * div < 90000000) { + div++; + if (div > 64) { + pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + } + target = Fout * div; + fll_div->fll_outdiv = div - 1; + + pr_debug("Fvco=%dHz\n", target); + + /* Find an appropriate FLL_FRATIO and factor it out of the target */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + fll_div->fll_fratio = fll_fratios[i].fll_fratio; + target /= fll_fratios[i].ratio; + break; + } + } + if (i == ARRAY_SIZE(fll_fratios)) { + pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref); + return -EINVAL; + } + + /* Now, calculate N.K */ + Ndiv = target / Fref; + + fll_div->n = Ndiv; + Nmod = target % Fref; + pr_debug("Nmod=%d\n", Nmod); + + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, Fref); + + K = Kpart & 0xFFFFFFFF; + + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + fll_div->k = K / 10; + + pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n", + fll_div->n, fll_div->k, + fll_div->fll_fratio, fll_div->fll_outdiv, + fll_div->fll_clk_ref_div); + + return 0; +} + +static int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct snd_soc_component *component = dai->component; + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + struct _fll_div fll_div; + int ret, val; + int clock2, fll1; + + /* Any change? */ + if (source == wm8904->fll_src && Fref == wm8904->fll_fref && + Fout == wm8904->fll_fout) + return 0; + + clock2 = snd_soc_component_read(component, WM8904_CLOCK_RATES_2); + + if (Fout == 0) { + dev_dbg(component->dev, "FLL disabled\n"); + + wm8904->fll_fref = 0; + wm8904->fll_fout = 0; + + /* Gate SYSCLK to avoid glitches */ + snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2, + WM8904_CLK_SYS_ENA, 0); + + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, + WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0); + + goto out; + } + + /* Validate the FLL ID */ + switch (source) { + case WM8904_FLL_MCLK: + case WM8904_FLL_LRCLK: + case WM8904_FLL_BCLK: + ret = fll_factors(&fll_div, Fref, Fout); + if (ret != 0) + return ret; + break; + + case WM8904_FLL_FREE_RUNNING: + dev_dbg(component->dev, "Using free running FLL\n"); + /* Force 12MHz and output/4 for now */ + Fout = 12000000; + Fref = 12000000; + + memset(&fll_div, 0, sizeof(fll_div)); + fll_div.fll_outdiv = 3; + break; + + default: + dev_err(component->dev, "Unknown FLL ID %d\n", fll_id); + return -EINVAL; + } + + /* Save current state then disable the FLL and SYSCLK to avoid + * misclocking */ + fll1 = snd_soc_component_read(component, WM8904_FLL_CONTROL_1); + snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2, + WM8904_CLK_SYS_ENA, 0); + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, + WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0); + + /* Unlock forced oscilator control to switch it on/off */ + snd_soc_component_update_bits(component, WM8904_CONTROL_INTERFACE_TEST_1, + WM8904_USER_KEY, WM8904_USER_KEY); + + if (fll_id == WM8904_FLL_FREE_RUNNING) { + val = WM8904_FLL_FRC_NCO; + } else { + val = 0; + } + + snd_soc_component_update_bits(component, WM8904_FLL_NCO_TEST_1, WM8904_FLL_FRC_NCO, + val); + snd_soc_component_update_bits(component, WM8904_CONTROL_INTERFACE_TEST_1, + WM8904_USER_KEY, 0); + + switch (fll_id) { + case WM8904_FLL_MCLK: + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5, + WM8904_FLL_CLK_REF_SRC_MASK, 0); + break; + + case WM8904_FLL_LRCLK: + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5, + WM8904_FLL_CLK_REF_SRC_MASK, 1); + break; + + case WM8904_FLL_BCLK: + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5, + WM8904_FLL_CLK_REF_SRC_MASK, 2); + break; + } + + if (fll_div.k) + val = WM8904_FLL_FRACN_ENA; + else + val = 0; + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, + WM8904_FLL_FRACN_ENA, val); + + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_2, + WM8904_FLL_OUTDIV_MASK | WM8904_FLL_FRATIO_MASK, + (fll_div.fll_outdiv << WM8904_FLL_OUTDIV_SHIFT) | + (fll_div.fll_fratio << WM8904_FLL_FRATIO_SHIFT)); + + snd_soc_component_write(component, WM8904_FLL_CONTROL_3, fll_div.k); + + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_4, WM8904_FLL_N_MASK, + fll_div.n << WM8904_FLL_N_SHIFT); + + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5, + WM8904_FLL_CLK_REF_DIV_MASK, + fll_div.fll_clk_ref_div + << WM8904_FLL_CLK_REF_DIV_SHIFT); + + dev_dbg(component->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout); + + wm8904->fll_fref = Fref; + wm8904->fll_fout = Fout; + wm8904->fll_src = source; + + /* Enable the FLL if it was previously active */ + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, + WM8904_FLL_OSC_ENA, fll1); + snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1, + WM8904_FLL_ENA, fll1); + +out: + /* Reenable SYSCLK if it was previously active */ + snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2, + WM8904_CLK_SYS_ENA, clock2); + + return 0; +} + +static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8904_priv *priv = snd_soc_component_get_drvdata(component); + unsigned long mclk_freq; + int ret; + + switch (clk_id) { + case WM8904_CLK_AUTO: + /* We don't have any rate constraints, so just ignore the + * request to disable constraining. + */ + if (!freq) + return 0; + + mclk_freq = clk_get_rate(priv->mclk); + /* enable FLL if a different sysclk is desired */ + if (mclk_freq != freq) { + priv->sysclk_src = WM8904_CLK_FLL; + ret = wm8904_set_fll(dai, WM8904_FLL_MCLK, + WM8904_FLL_MCLK, + mclk_freq, freq); + if (ret) + return ret; + break; + } + clk_id = WM8904_CLK_MCLK; + fallthrough; + + case WM8904_CLK_MCLK: + priv->sysclk_src = clk_id; + priv->mclk_rate = freq; + break; + + case WM8904_CLK_FLL: + priv->sysclk_src = clk_id; + break; + + default: + return -EINVAL; + } + + dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq); + + wm8904_configure_clocking(component); + + return 0; +} + +static int wm8904_mute(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + int val; + + if (mute) + val = WM8904_DAC_MUTE; + else + val = 0; + + snd_soc_component_update_bits(component, WM8904_DAC_DIGITAL_1, WM8904_DAC_MUTE, val); + + return 0; +} + +static int wm8904_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID resistance 2*50k */ + snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0, + WM8904_VMID_RES_MASK, + 0x1 << WM8904_VMID_RES_SHIFT); + + /* Normal bias current */ + snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0, + WM8904_ISEL_MASK, 2 << WM8904_ISEL_SHIFT); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies), + wm8904->supplies); + if (ret != 0) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(wm8904->mclk); + if (ret) { + dev_err(component->dev, + "Failed to enable MCLK: %d\n", ret); + regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), + wm8904->supplies); + return ret; + } + + regcache_cache_only(wm8904->regmap, false); + regcache_sync(wm8904->regmap); + + /* Enable bias */ + snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0, + WM8904_BIAS_ENA, WM8904_BIAS_ENA); + + /* Enable VMID, VMID buffering, 2*5k resistance */ + snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0, + WM8904_VMID_ENA | + WM8904_VMID_RES_MASK, + WM8904_VMID_ENA | + 0x3 << WM8904_VMID_RES_SHIFT); + + /* Let VMID ramp */ + msleep(1); + } + + /* Maintain VMID with 2*250k */ + snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0, + WM8904_VMID_RES_MASK, + 0x2 << WM8904_VMID_RES_SHIFT); + + /* Bias current *0.5 */ + snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0, + WM8904_ISEL_MASK, 0); + break; + + case SND_SOC_BIAS_OFF: + /* Turn off VMID */ + snd_soc_component_update_bits(component, WM8904_VMID_CONTROL_0, + WM8904_VMID_RES_MASK | WM8904_VMID_ENA, 0); + + /* Stop bias generation */ + snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0, + WM8904_BIAS_ENA, 0); + + snd_soc_component_write(component, WM8904_SW_RESET_AND_ID, 0); + regcache_cache_only(wm8904->regmap, true); + regcache_mark_dirty(wm8904->regmap); + + regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), + wm8904->supplies); + clk_disable_unprepare(wm8904->mclk); + break; + } + return 0; +} + +#define WM8904_RATES SNDRV_PCM_RATE_8000_96000 + +#define WM8904_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8904_dai_ops = { + .set_sysclk = wm8904_set_sysclk, + .set_fmt = wm8904_set_fmt, + .set_tdm_slot = wm8904_set_tdm_slot, + .set_pll = wm8904_set_fll, + .hw_params = wm8904_hw_params, + .mute_stream = wm8904_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8904_dai = { + .name = "wm8904-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8904_RATES, + .formats = WM8904_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = WM8904_RATES, + .formats = WM8904_FORMATS, + }, + .ops = &wm8904_dai_ops, + .symmetric_rate = 1, +}; + +static void wm8904_handle_retune_mobile_pdata(struct snd_soc_component *component) +{ + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + struct wm8904_pdata *pdata = wm8904->pdata; + struct snd_kcontrol_new control = + SOC_ENUM_EXT("EQ Mode", + wm8904->retune_mobile_enum, + wm8904_get_retune_mobile_enum, + wm8904_put_retune_mobile_enum); + int ret, i, j; + const char **t; + + /* We need an array of texts for the enum API but the number + * of texts is likely to be less than the number of + * configurations due to the sample rate dependency of the + * configurations. */ + wm8904->num_retune_mobile_texts = 0; + wm8904->retune_mobile_texts = NULL; + for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) { + for (j = 0; j < wm8904->num_retune_mobile_texts; j++) { + if (strcmp(pdata->retune_mobile_cfgs[i].name, + wm8904->retune_mobile_texts[j]) == 0) + break; + } + + if (j != wm8904->num_retune_mobile_texts) + continue; + + /* Expand the array... */ + t = krealloc(wm8904->retune_mobile_texts, + sizeof(char *) * + (wm8904->num_retune_mobile_texts + 1), + GFP_KERNEL); + if (t == NULL) + continue; + + /* ...store the new entry... */ + t[wm8904->num_retune_mobile_texts] = + pdata->retune_mobile_cfgs[i].name; + + /* ...and remember the new version. */ + wm8904->num_retune_mobile_texts++; + wm8904->retune_mobile_texts = t; + } + + dev_dbg(component->dev, "Allocated %d unique ReTune Mobile names\n", + wm8904->num_retune_mobile_texts); + + wm8904->retune_mobile_enum.items = wm8904->num_retune_mobile_texts; + wm8904->retune_mobile_enum.texts = wm8904->retune_mobile_texts; + + ret = snd_soc_add_component_controls(component, &control, 1); + if (ret != 0) + dev_err(component->dev, + "Failed to add ReTune Mobile control: %d\n", ret); +} + +static void wm8904_handle_pdata(struct snd_soc_component *component) +{ + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + struct wm8904_pdata *pdata = wm8904->pdata; + int ret, i; + + if (!pdata) { + snd_soc_add_component_controls(component, wm8904_eq_controls, + ARRAY_SIZE(wm8904_eq_controls)); + return; + } + + dev_dbg(component->dev, "%d DRC configurations\n", pdata->num_drc_cfgs); + + if (pdata->num_drc_cfgs) { + struct snd_kcontrol_new control = + SOC_ENUM_EXT("DRC Mode", wm8904->drc_enum, + wm8904_get_drc_enum, wm8904_put_drc_enum); + + /* We need an array of texts for the enum API */ + wm8904->drc_texts = kmalloc_array(pdata->num_drc_cfgs, + sizeof(char *), + GFP_KERNEL); + if (!wm8904->drc_texts) + return; + + for (i = 0; i < pdata->num_drc_cfgs; i++) + wm8904->drc_texts[i] = pdata->drc_cfgs[i].name; + + wm8904->drc_enum.items = pdata->num_drc_cfgs; + wm8904->drc_enum.texts = wm8904->drc_texts; + + ret = snd_soc_add_component_controls(component, &control, 1); + if (ret != 0) + dev_err(component->dev, + "Failed to add DRC mode control: %d\n", ret); + + wm8904_set_drc(component); + } + + dev_dbg(component->dev, "%d ReTune Mobile configurations\n", + pdata->num_retune_mobile_cfgs); + + if (pdata->num_retune_mobile_cfgs) + wm8904_handle_retune_mobile_pdata(component); + else + snd_soc_add_component_controls(component, wm8904_eq_controls, + ARRAY_SIZE(wm8904_eq_controls)); +} + + +static int wm8904_probe(struct snd_soc_component *component) +{ + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + + switch (wm8904->devtype) { + case WM8904: + break; + case WM8912: + memset(&wm8904_dai.capture, 0, sizeof(wm8904_dai.capture)); + break; + default: + dev_err(component->dev, "Unknown device type %d\n", + wm8904->devtype); + return -EINVAL; + } + + wm8904_handle_pdata(component); + + wm8904_add_widgets(component); + + return 0; +} + +static void wm8904_remove(struct snd_soc_component *component) +{ + struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component); + + kfree(wm8904->retune_mobile_texts); + kfree(wm8904->drc_texts); +} + +static const struct snd_soc_component_driver soc_component_dev_wm8904 = { + .probe = wm8904_probe, + .remove = wm8904_remove, + .set_bias_level = wm8904_set_bias_level, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8904_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM8904_MAX_REGISTER, + .volatile_reg = wm8904_volatile_register, + .readable_reg = wm8904_readable_register, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm8904_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8904_reg_defaults), +}; + +#ifdef CONFIG_OF +static const struct of_device_id wm8904_of_match[] = { + { + .compatible = "wlf,wm8904", + .data = (void *)WM8904, + }, { + .compatible = "wlf,wm8912", + .data = (void *)WM8912, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, wm8904_of_match); +#endif + +static const struct i2c_device_id wm8904_i2c_id[]; + +static int wm8904_i2c_probe(struct i2c_client *i2c) +{ + struct wm8904_priv *wm8904; + unsigned int val; + int ret, i; + + wm8904 = devm_kzalloc(&i2c->dev, sizeof(struct wm8904_priv), + GFP_KERNEL); + if (wm8904 == NULL) + return -ENOMEM; + + wm8904->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (IS_ERR(wm8904->mclk)) { + ret = PTR_ERR(wm8904->mclk); + dev_err(&i2c->dev, "Failed to get MCLK\n"); + return ret; + } + + wm8904->regmap = devm_regmap_init_i2c(i2c, &wm8904_regmap); + if (IS_ERR(wm8904->regmap)) { + ret = PTR_ERR(wm8904->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + if (i2c->dev.of_node) { + const struct of_device_id *match; + + match = of_match_node(wm8904_of_match, i2c->dev.of_node); + if (match == NULL) + return -EINVAL; + wm8904->devtype = (uintptr_t)match->data; + } else { + const struct i2c_device_id *id = + i2c_match_id(wm8904_i2c_id, i2c); + wm8904->devtype = id->driver_data; + } + + i2c_set_clientdata(i2c, wm8904); + wm8904->pdata = i2c->dev.platform_data; + + for (i = 0; i < ARRAY_SIZE(wm8904->supplies); i++) + wm8904->supplies[i].supply = wm8904_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8904->supplies), + wm8904->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies), + wm8904->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = regmap_read(wm8904->regmap, WM8904_SW_RESET_AND_ID, &val); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret); + goto err_enable; + } + if (val != 0x8904) { + dev_err(&i2c->dev, "Device is not a WM8904, ID is %x\n", val); + ret = -EINVAL; + goto err_enable; + } + + ret = regmap_read(wm8904->regmap, WM8904_REVISION, &val); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read device revision: %d\n", + ret); + goto err_enable; + } + dev_info(&i2c->dev, "revision %c\n", val + 'A'); + + ret = regmap_write(wm8904->regmap, WM8904_SW_RESET_AND_ID, 0); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); + goto err_enable; + } + + /* Change some default settings - latch VU and enable ZC */ + regmap_update_bits(wm8904->regmap, WM8904_ADC_DIGITAL_VOLUME_LEFT, + WM8904_ADC_VU, WM8904_ADC_VU); + regmap_update_bits(wm8904->regmap, WM8904_ADC_DIGITAL_VOLUME_RIGHT, + WM8904_ADC_VU, WM8904_ADC_VU); + regmap_update_bits(wm8904->regmap, WM8904_DAC_DIGITAL_VOLUME_LEFT, + WM8904_DAC_VU, WM8904_DAC_VU); + regmap_update_bits(wm8904->regmap, WM8904_DAC_DIGITAL_VOLUME_RIGHT, + WM8904_DAC_VU, WM8904_DAC_VU); + regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT1_LEFT, + WM8904_HPOUT_VU | WM8904_HPOUTLZC, + WM8904_HPOUT_VU | WM8904_HPOUTLZC); + regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT1_RIGHT, + WM8904_HPOUT_VU | WM8904_HPOUTRZC, + WM8904_HPOUT_VU | WM8904_HPOUTRZC); + regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT2_LEFT, + WM8904_LINEOUT_VU | WM8904_LINEOUTLZC, + WM8904_LINEOUT_VU | WM8904_LINEOUTLZC); + regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT2_RIGHT, + WM8904_LINEOUT_VU | WM8904_LINEOUTRZC, + WM8904_LINEOUT_VU | WM8904_LINEOUTRZC); + regmap_update_bits(wm8904->regmap, WM8904_CLOCK_RATES_0, + WM8904_SR_MODE, 0); + + /* Apply configuration from the platform data. */ + if (wm8904->pdata) { + for (i = 0; i < WM8904_GPIO_REGS; i++) { + if (!wm8904->pdata->gpio_cfg[i]) + continue; + + regmap_update_bits(wm8904->regmap, + WM8904_GPIO_CONTROL_1 + i, + 0xffff, + wm8904->pdata->gpio_cfg[i]); + } + + /* Zero is the default value for these anyway */ + for (i = 0; i < WM8904_MIC_REGS; i++) + regmap_update_bits(wm8904->regmap, + WM8904_MIC_BIAS_CONTROL_0 + i, + 0xffff, + wm8904->pdata->mic_cfg[i]); + } + + /* Set Class W by default - this will be managed by the Class + * G widget at runtime where bypass paths are available. + */ + regmap_update_bits(wm8904->regmap, WM8904_CLASS_W_0, + WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR); + + /* Use normal bias source */ + regmap_update_bits(wm8904->regmap, WM8904_BIAS_CONTROL_0, + WM8904_POBCTRL, 0); + + /* Fill the cache for the ADC test register */ + regmap_read(wm8904->regmap, WM8904_ADC_TEST_0, &val); + + /* Can leave the device powered off until we need it */ + regcache_cache_only(wm8904->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8904, &wm8904_dai, 1); + if (ret != 0) + return ret; + + return 0; + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies); + return ret; +} + +static const struct i2c_device_id wm8904_i2c_id[] = { + { "wm8904", WM8904 }, + { "wm8912", WM8912 }, + { "wm8918", WM8904 }, /* Actually a subset, updates to follow */ + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id); + +static struct i2c_driver wm8904_i2c_driver = { + .driver = { + .name = "wm8904", + .of_match_table = of_match_ptr(wm8904_of_match), + }, + .probe = wm8904_i2c_probe, + .id_table = wm8904_i2c_id, +}; + +module_i2c_driver(wm8904_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8904 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8904.h b/sound/soc/codecs/wm8904.h new file mode 100644 index 0000000000..de6340446b --- /dev/null +++ b/sound/soc/codecs/wm8904.h @@ -0,0 +1,1590 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8904.h -- WM8904 ASoC driver + * + * Copyright 2009 Wolfson Microelectronics, plc + * + * Author: Mark Brown + */ + +#ifndef _WM8904_H +#define _WM8904_H + +#define WM8904_CLK_AUTO 0 +#define WM8904_CLK_MCLK 1 +#define WM8904_CLK_FLL 2 + +#define WM8904_FLL_MCLK 1 +#define WM8904_FLL_BCLK 2 +#define WM8904_FLL_LRCLK 3 +#define WM8904_FLL_FREE_RUNNING 4 + +/* + * Register values. + */ +#define WM8904_SW_RESET_AND_ID 0x00 +#define WM8904_REVISION 0x01 +#define WM8904_BIAS_CONTROL_0 0x04 +#define WM8904_VMID_CONTROL_0 0x05 +#define WM8904_MIC_BIAS_CONTROL_0 0x06 +#define WM8904_MIC_BIAS_CONTROL_1 0x07 +#define WM8904_ANALOGUE_DAC_0 0x08 +#define WM8904_MIC_FILTER_CONTROL 0x09 +#define WM8904_ANALOGUE_ADC_0 0x0A +#define WM8904_POWER_MANAGEMENT_0 0x0C +#define WM8904_POWER_MANAGEMENT_2 0x0E +#define WM8904_POWER_MANAGEMENT_3 0x0F +#define WM8904_POWER_MANAGEMENT_6 0x12 +#define WM8904_CLOCK_RATES_0 0x14 +#define WM8904_CLOCK_RATES_1 0x15 +#define WM8904_CLOCK_RATES_2 0x16 +#define WM8904_AUDIO_INTERFACE_0 0x18 +#define WM8904_AUDIO_INTERFACE_1 0x19 +#define WM8904_AUDIO_INTERFACE_2 0x1A +#define WM8904_AUDIO_INTERFACE_3 0x1B +#define WM8904_DAC_DIGITAL_VOLUME_LEFT 0x1E +#define WM8904_DAC_DIGITAL_VOLUME_RIGHT 0x1F +#define WM8904_DAC_DIGITAL_0 0x20 +#define WM8904_DAC_DIGITAL_1 0x21 +#define WM8904_ADC_DIGITAL_VOLUME_LEFT 0x24 +#define WM8904_ADC_DIGITAL_VOLUME_RIGHT 0x25 +#define WM8904_ADC_DIGITAL_0 0x26 +#define WM8904_DIGITAL_MICROPHONE_0 0x27 +#define WM8904_DRC_0 0x28 +#define WM8904_DRC_1 0x29 +#define WM8904_DRC_2 0x2A +#define WM8904_DRC_3 0x2B +#define WM8904_ANALOGUE_LEFT_INPUT_0 0x2C +#define WM8904_ANALOGUE_RIGHT_INPUT_0 0x2D +#define WM8904_ANALOGUE_LEFT_INPUT_1 0x2E +#define WM8904_ANALOGUE_RIGHT_INPUT_1 0x2F +#define WM8904_ANALOGUE_OUT1_LEFT 0x39 +#define WM8904_ANALOGUE_OUT1_RIGHT 0x3A +#define WM8904_ANALOGUE_OUT2_LEFT 0x3B +#define WM8904_ANALOGUE_OUT2_RIGHT 0x3C +#define WM8904_ANALOGUE_OUT12_ZC 0x3D +#define WM8904_DC_SERVO_0 0x43 +#define WM8904_DC_SERVO_1 0x44 +#define WM8904_DC_SERVO_2 0x45 +#define WM8904_DC_SERVO_4 0x47 +#define WM8904_DC_SERVO_5 0x48 +#define WM8904_DC_SERVO_6 0x49 +#define WM8904_DC_SERVO_7 0x4A +#define WM8904_DC_SERVO_8 0x4B +#define WM8904_DC_SERVO_9 0x4C +#define WM8904_DC_SERVO_READBACK_0 0x4D +#define WM8904_ANALOGUE_HP_0 0x5A +#define WM8904_ANALOGUE_LINEOUT_0 0x5E +#define WM8904_CHARGE_PUMP_0 0x62 +#define WM8904_CLASS_W_0 0x68 +#define WM8904_WRITE_SEQUENCER_0 0x6C +#define WM8904_WRITE_SEQUENCER_1 0x6D +#define WM8904_WRITE_SEQUENCER_2 0x6E +#define WM8904_WRITE_SEQUENCER_3 0x6F +#define WM8904_WRITE_SEQUENCER_4 0x70 +#define WM8904_FLL_CONTROL_1 0x74 +#define WM8904_FLL_CONTROL_2 0x75 +#define WM8904_FLL_CONTROL_3 0x76 +#define WM8904_FLL_CONTROL_4 0x77 +#define WM8904_FLL_CONTROL_5 0x78 +#define WM8904_GPIO_CONTROL_1 0x79 +#define WM8904_GPIO_CONTROL_2 0x7A +#define WM8904_GPIO_CONTROL_3 0x7B +#define WM8904_GPIO_CONTROL_4 0x7C +#define WM8904_DIGITAL_PULLS 0x7E +#define WM8904_INTERRUPT_STATUS 0x7F +#define WM8904_INTERRUPT_STATUS_MASK 0x80 +#define WM8904_INTERRUPT_POLARITY 0x81 +#define WM8904_INTERRUPT_DEBOUNCE 0x82 +#define WM8904_EQ1 0x86 +#define WM8904_EQ2 0x87 +#define WM8904_EQ3 0x88 +#define WM8904_EQ4 0x89 +#define WM8904_EQ5 0x8A +#define WM8904_EQ6 0x8B +#define WM8904_EQ7 0x8C +#define WM8904_EQ8 0x8D +#define WM8904_EQ9 0x8E +#define WM8904_EQ10 0x8F +#define WM8904_EQ11 0x90 +#define WM8904_EQ12 0x91 +#define WM8904_EQ13 0x92 +#define WM8904_EQ14 0x93 +#define WM8904_EQ15 0x94 +#define WM8904_EQ16 0x95 +#define WM8904_EQ17 0x96 +#define WM8904_EQ18 0x97 +#define WM8904_EQ19 0x98 +#define WM8904_EQ20 0x99 +#define WM8904_EQ21 0x9A +#define WM8904_EQ22 0x9B +#define WM8904_EQ23 0x9C +#define WM8904_EQ24 0x9D +#define WM8904_CONTROL_INTERFACE_TEST_1 0xA1 +#define WM8904_ADC_TEST_0 0xC6 +#define WM8904_ANALOGUE_OUTPUT_BIAS_0 0xCC +#define WM8904_FLL_NCO_TEST_0 0xF7 +#define WM8904_FLL_NCO_TEST_1 0xF8 + +#define WM8904_REGISTER_COUNT 101 +#define WM8904_MAX_REGISTER 0xF8 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - SW Reset and ID + */ +#define WM8904_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */ +#define WM8904_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */ +#define WM8904_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */ + +/* + * R1 (0x01) - Revision + */ +#define WM8904_REVISION_MASK 0x000F /* REVISION - [3:0] */ +#define WM8904_REVISION_SHIFT 0 /* REVISION - [3:0] */ +#define WM8904_REVISION_WIDTH 16 /* REVISION - [3:0] */ + +/* + * R4 (0x04) - Bias Control 0 + */ +#define WM8904_POBCTRL 0x0010 /* POBCTRL */ +#define WM8904_POBCTRL_MASK 0x0010 /* POBCTRL */ +#define WM8904_POBCTRL_SHIFT 4 /* POBCTRL */ +#define WM8904_POBCTRL_WIDTH 1 /* POBCTRL */ +#define WM8904_ISEL_MASK 0x000C /* ISEL - [3:2] */ +#define WM8904_ISEL_SHIFT 2 /* ISEL - [3:2] */ +#define WM8904_ISEL_WIDTH 2 /* ISEL - [3:2] */ +#define WM8904_STARTUP_BIAS_ENA 0x0002 /* STARTUP_BIAS_ENA */ +#define WM8904_STARTUP_BIAS_ENA_MASK 0x0002 /* STARTUP_BIAS_ENA */ +#define WM8904_STARTUP_BIAS_ENA_SHIFT 1 /* STARTUP_BIAS_ENA */ +#define WM8904_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */ +#define WM8904_BIAS_ENA 0x0001 /* BIAS_ENA */ +#define WM8904_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */ +#define WM8904_BIAS_ENA_SHIFT 0 /* BIAS_ENA */ +#define WM8904_BIAS_ENA_WIDTH 1 /* BIAS_ENA */ + +/* + * R5 (0x05) - VMID Control 0 + */ +#define WM8904_VMID_BUF_ENA 0x0040 /* VMID_BUF_ENA */ +#define WM8904_VMID_BUF_ENA_MASK 0x0040 /* VMID_BUF_ENA */ +#define WM8904_VMID_BUF_ENA_SHIFT 6 /* VMID_BUF_ENA */ +#define WM8904_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */ +#define WM8904_VMID_RES_MASK 0x0006 /* VMID_RES - [2:1] */ +#define WM8904_VMID_RES_SHIFT 1 /* VMID_RES - [2:1] */ +#define WM8904_VMID_RES_WIDTH 2 /* VMID_RES - [2:1] */ +#define WM8904_VMID_ENA 0x0001 /* VMID_ENA */ +#define WM8904_VMID_ENA_MASK 0x0001 /* VMID_ENA */ +#define WM8904_VMID_ENA_SHIFT 0 /* VMID_ENA */ +#define WM8904_VMID_ENA_WIDTH 1 /* VMID_ENA */ + +/* + * R8 (0x08) - Analogue DAC 0 + */ +#define WM8904_DAC_BIAS_SEL_MASK 0x0018 /* DAC_BIAS_SEL - [4:3] */ +#define WM8904_DAC_BIAS_SEL_SHIFT 3 /* DAC_BIAS_SEL - [4:3] */ +#define WM8904_DAC_BIAS_SEL_WIDTH 2 /* DAC_BIAS_SEL - [4:3] */ +#define WM8904_DAC_VMID_BIAS_SEL_MASK 0x0006 /* DAC_VMID_BIAS_SEL - [2:1] */ +#define WM8904_DAC_VMID_BIAS_SEL_SHIFT 1 /* DAC_VMID_BIAS_SEL - [2:1] */ +#define WM8904_DAC_VMID_BIAS_SEL_WIDTH 2 /* DAC_VMID_BIAS_SEL - [2:1] */ + +/* + * R9 (0x09) - mic Filter Control + */ +#define WM8904_MIC_DET_SET_THRESHOLD_MASK 0xF000 /* MIC_DET_SET_THRESHOLD - [15:12] */ +#define WM8904_MIC_DET_SET_THRESHOLD_SHIFT 12 /* MIC_DET_SET_THRESHOLD - [15:12] */ +#define WM8904_MIC_DET_SET_THRESHOLD_WIDTH 4 /* MIC_DET_SET_THRESHOLD - [15:12] */ +#define WM8904_MIC_DET_RESET_THRESHOLD_MASK 0x0F00 /* MIC_DET_RESET_THRESHOLD - [11:8] */ +#define WM8904_MIC_DET_RESET_THRESHOLD_SHIFT 8 /* MIC_DET_RESET_THRESHOLD - [11:8] */ +#define WM8904_MIC_DET_RESET_THRESHOLD_WIDTH 4 /* MIC_DET_RESET_THRESHOLD - [11:8] */ +#define WM8904_MIC_SHORT_SET_THRESHOLD_MASK 0x00F0 /* MIC_SHORT_SET_THRESHOLD - [7:4] */ +#define WM8904_MIC_SHORT_SET_THRESHOLD_SHIFT 4 /* MIC_SHORT_SET_THRESHOLD - [7:4] */ +#define WM8904_MIC_SHORT_SET_THRESHOLD_WIDTH 4 /* MIC_SHORT_SET_THRESHOLD - [7:4] */ +#define WM8904_MIC_SHORT_RESET_THRESHOLD_MASK 0x000F /* MIC_SHORT_RESET_THRESHOLD - [3:0] */ +#define WM8904_MIC_SHORT_RESET_THRESHOLD_SHIFT 0 /* MIC_SHORT_RESET_THRESHOLD - [3:0] */ +#define WM8904_MIC_SHORT_RESET_THRESHOLD_WIDTH 4 /* MIC_SHORT_RESET_THRESHOLD - [3:0] */ + +/* + * R10 (0x0A) - Analogue ADC 0 + */ +#define WM8904_ADC_OSR128 0x0001 /* ADC_OSR128 */ +#define WM8904_ADC_OSR128_MASK 0x0001 /* ADC_OSR128 */ +#define WM8904_ADC_OSR128_SHIFT 0 /* ADC_OSR128 */ +#define WM8904_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */ + +/* + * R12 (0x0C) - Power Management 0 + */ +#define WM8904_INL_ENA 0x0002 /* INL_ENA */ +#define WM8904_INL_ENA_MASK 0x0002 /* INL_ENA */ +#define WM8904_INL_ENA_SHIFT 1 /* INL_ENA */ +#define WM8904_INL_ENA_WIDTH 1 /* INL_ENA */ +#define WM8904_INR_ENA 0x0001 /* INR_ENA */ +#define WM8904_INR_ENA_MASK 0x0001 /* INR_ENA */ +#define WM8904_INR_ENA_SHIFT 0 /* INR_ENA */ +#define WM8904_INR_ENA_WIDTH 1 /* INR_ENA */ + +/* + * R14 (0x0E) - Power Management 2 + */ +#define WM8904_HPL_PGA_ENA 0x0002 /* HPL_PGA_ENA */ +#define WM8904_HPL_PGA_ENA_MASK 0x0002 /* HPL_PGA_ENA */ +#define WM8904_HPL_PGA_ENA_SHIFT 1 /* HPL_PGA_ENA */ +#define WM8904_HPL_PGA_ENA_WIDTH 1 /* HPL_PGA_ENA */ +#define WM8904_HPR_PGA_ENA 0x0001 /* HPR_PGA_ENA */ +#define WM8904_HPR_PGA_ENA_MASK 0x0001 /* HPR_PGA_ENA */ +#define WM8904_HPR_PGA_ENA_SHIFT 0 /* HPR_PGA_ENA */ +#define WM8904_HPR_PGA_ENA_WIDTH 1 /* HPR_PGA_ENA */ + +/* + * R15 (0x0F) - Power Management 3 + */ +#define WM8904_LINEOUTL_PGA_ENA 0x0002 /* LINEOUTL_PGA_ENA */ +#define WM8904_LINEOUTL_PGA_ENA_MASK 0x0002 /* LINEOUTL_PGA_ENA */ +#define WM8904_LINEOUTL_PGA_ENA_SHIFT 1 /* LINEOUTL_PGA_ENA */ +#define WM8904_LINEOUTL_PGA_ENA_WIDTH 1 /* LINEOUTL_PGA_ENA */ +#define WM8904_LINEOUTR_PGA_ENA 0x0001 /* LINEOUTR_PGA_ENA */ +#define WM8904_LINEOUTR_PGA_ENA_MASK 0x0001 /* LINEOUTR_PGA_ENA */ +#define WM8904_LINEOUTR_PGA_ENA_SHIFT 0 /* LINEOUTR_PGA_ENA */ +#define WM8904_LINEOUTR_PGA_ENA_WIDTH 1 /* LINEOUTR_PGA_ENA */ + +/* + * R18 (0x12) - Power Management 6 + */ +#define WM8904_DACL_ENA 0x0008 /* DACL_ENA */ +#define WM8904_DACL_ENA_MASK 0x0008 /* DACL_ENA */ +#define WM8904_DACL_ENA_SHIFT 3 /* DACL_ENA */ +#define WM8904_DACL_ENA_WIDTH 1 /* DACL_ENA */ +#define WM8904_DACR_ENA 0x0004 /* DACR_ENA */ +#define WM8904_DACR_ENA_MASK 0x0004 /* DACR_ENA */ +#define WM8904_DACR_ENA_SHIFT 2 /* DACR_ENA */ +#define WM8904_DACR_ENA_WIDTH 1 /* DACR_ENA */ +#define WM8904_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8904_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */ +#define WM8904_ADCL_ENA_SHIFT 1 /* ADCL_ENA */ +#define WM8904_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ +#define WM8904_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8904_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */ +#define WM8904_ADCR_ENA_SHIFT 0 /* ADCR_ENA */ +#define WM8904_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ + +/* + * R20 (0x14) - Clock Rates 0 + */ +#define WM8904_TOCLK_RATE_DIV16 0x4000 /* TOCLK_RATE_DIV16 */ +#define WM8904_TOCLK_RATE_DIV16_MASK 0x4000 /* TOCLK_RATE_DIV16 */ +#define WM8904_TOCLK_RATE_DIV16_SHIFT 14 /* TOCLK_RATE_DIV16 */ +#define WM8904_TOCLK_RATE_DIV16_WIDTH 1 /* TOCLK_RATE_DIV16 */ +#define WM8904_TOCLK_RATE_X4 0x2000 /* TOCLK_RATE_X4 */ +#define WM8904_TOCLK_RATE_X4_MASK 0x2000 /* TOCLK_RATE_X4 */ +#define WM8904_TOCLK_RATE_X4_SHIFT 13 /* TOCLK_RATE_X4 */ +#define WM8904_TOCLK_RATE_X4_WIDTH 1 /* TOCLK_RATE_X4 */ +#define WM8904_SR_MODE 0x1000 /* SR_MODE */ +#define WM8904_SR_MODE_MASK 0x1000 /* SR_MODE */ +#define WM8904_SR_MODE_SHIFT 12 /* SR_MODE */ +#define WM8904_SR_MODE_WIDTH 1 /* SR_MODE */ +#define WM8904_MCLK_DIV 0x0001 /* MCLK_DIV */ +#define WM8904_MCLK_DIV_MASK 0x0001 /* MCLK_DIV */ +#define WM8904_MCLK_DIV_SHIFT 0 /* MCLK_DIV */ +#define WM8904_MCLK_DIV_WIDTH 1 /* MCLK_DIV */ + +/* + * R21 (0x15) - Clock Rates 1 + */ +#define WM8904_CLK_SYS_RATE_MASK 0x3C00 /* CLK_SYS_RATE - [13:10] */ +#define WM8904_CLK_SYS_RATE_SHIFT 10 /* CLK_SYS_RATE - [13:10] */ +#define WM8904_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [13:10] */ +#define WM8904_SAMPLE_RATE_MASK 0x0007 /* SAMPLE_RATE - [2:0] */ +#define WM8904_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [2:0] */ +#define WM8904_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [2:0] */ + +/* + * R22 (0x16) - Clock Rates 2 + */ +#define WM8904_MCLK_INV 0x8000 /* MCLK_INV */ +#define WM8904_MCLK_INV_MASK 0x8000 /* MCLK_INV */ +#define WM8904_MCLK_INV_SHIFT 15 /* MCLK_INV */ +#define WM8904_MCLK_INV_WIDTH 1 /* MCLK_INV */ +#define WM8904_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */ +#define WM8904_SYSCLK_SRC_MASK 0x4000 /* SYSCLK_SRC */ +#define WM8904_SYSCLK_SRC_SHIFT 14 /* SYSCLK_SRC */ +#define WM8904_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */ +#define WM8904_TOCLK_RATE 0x1000 /* TOCLK_RATE */ +#define WM8904_TOCLK_RATE_MASK 0x1000 /* TOCLK_RATE */ +#define WM8904_TOCLK_RATE_SHIFT 12 /* TOCLK_RATE */ +#define WM8904_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */ +#define WM8904_OPCLK_ENA 0x0008 /* OPCLK_ENA */ +#define WM8904_OPCLK_ENA_MASK 0x0008 /* OPCLK_ENA */ +#define WM8904_OPCLK_ENA_SHIFT 3 /* OPCLK_ENA */ +#define WM8904_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define WM8904_CLK_SYS_ENA 0x0004 /* CLK_SYS_ENA */ +#define WM8904_CLK_SYS_ENA_MASK 0x0004 /* CLK_SYS_ENA */ +#define WM8904_CLK_SYS_ENA_SHIFT 2 /* CLK_SYS_ENA */ +#define WM8904_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */ +#define WM8904_CLK_DSP_ENA 0x0002 /* CLK_DSP_ENA */ +#define WM8904_CLK_DSP_ENA_MASK 0x0002 /* CLK_DSP_ENA */ +#define WM8904_CLK_DSP_ENA_SHIFT 1 /* CLK_DSP_ENA */ +#define WM8904_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */ +#define WM8904_TOCLK_ENA 0x0001 /* TOCLK_ENA */ +#define WM8904_TOCLK_ENA_MASK 0x0001 /* TOCLK_ENA */ +#define WM8904_TOCLK_ENA_SHIFT 0 /* TOCLK_ENA */ +#define WM8904_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */ + +/* + * R24 (0x18) - Audio Interface 0 + */ +#define WM8904_DACL_DATINV 0x1000 /* DACL_DATINV */ +#define WM8904_DACL_DATINV_MASK 0x1000 /* DACL_DATINV */ +#define WM8904_DACL_DATINV_SHIFT 12 /* DACL_DATINV */ +#define WM8904_DACL_DATINV_WIDTH 1 /* DACL_DATINV */ +#define WM8904_DACR_DATINV 0x0800 /* DACR_DATINV */ +#define WM8904_DACR_DATINV_MASK 0x0800 /* DACR_DATINV */ +#define WM8904_DACR_DATINV_SHIFT 11 /* DACR_DATINV */ +#define WM8904_DACR_DATINV_WIDTH 1 /* DACR_DATINV */ +#define WM8904_DAC_BOOST_MASK 0x0600 /* DAC_BOOST - [10:9] */ +#define WM8904_DAC_BOOST_SHIFT 9 /* DAC_BOOST - [10:9] */ +#define WM8904_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [10:9] */ +#define WM8904_LOOPBACK 0x0100 /* LOOPBACK */ +#define WM8904_LOOPBACK_MASK 0x0100 /* LOOPBACK */ +#define WM8904_LOOPBACK_SHIFT 8 /* LOOPBACK */ +#define WM8904_LOOPBACK_WIDTH 1 /* LOOPBACK */ +#define WM8904_AIFADCL_SRC 0x0080 /* AIFADCL_SRC */ +#define WM8904_AIFADCL_SRC_MASK 0x0080 /* AIFADCL_SRC */ +#define WM8904_AIFADCL_SRC_SHIFT 7 /* AIFADCL_SRC */ +#define WM8904_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */ +#define WM8904_AIFADCR_SRC 0x0040 /* AIFADCR_SRC */ +#define WM8904_AIFADCR_SRC_MASK 0x0040 /* AIFADCR_SRC */ +#define WM8904_AIFADCR_SRC_SHIFT 6 /* AIFADCR_SRC */ +#define WM8904_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */ +#define WM8904_AIFDACL_SRC 0x0020 /* AIFDACL_SRC */ +#define WM8904_AIFDACL_SRC_MASK 0x0020 /* AIFDACL_SRC */ +#define WM8904_AIFDACL_SRC_SHIFT 5 /* AIFDACL_SRC */ +#define WM8904_AIFDACL_SRC_WIDTH 1 /* AIFDACL_SRC */ +#define WM8904_AIFDACR_SRC 0x0010 /* AIFDACR_SRC */ +#define WM8904_AIFDACR_SRC_MASK 0x0010 /* AIFDACR_SRC */ +#define WM8904_AIFDACR_SRC_SHIFT 4 /* AIFDACR_SRC */ +#define WM8904_AIFDACR_SRC_WIDTH 1 /* AIFDACR_SRC */ +#define WM8904_ADC_COMP 0x0008 /* ADC_COMP */ +#define WM8904_ADC_COMP_MASK 0x0008 /* ADC_COMP */ +#define WM8904_ADC_COMP_SHIFT 3 /* ADC_COMP */ +#define WM8904_ADC_COMP_WIDTH 1 /* ADC_COMP */ +#define WM8904_ADC_COMPMODE 0x0004 /* ADC_COMPMODE */ +#define WM8904_ADC_COMPMODE_MASK 0x0004 /* ADC_COMPMODE */ +#define WM8904_ADC_COMPMODE_SHIFT 2 /* ADC_COMPMODE */ +#define WM8904_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */ +#define WM8904_DAC_COMP 0x0002 /* DAC_COMP */ +#define WM8904_DAC_COMP_MASK 0x0002 /* DAC_COMP */ +#define WM8904_DAC_COMP_SHIFT 1 /* DAC_COMP */ +#define WM8904_DAC_COMP_WIDTH 1 /* DAC_COMP */ +#define WM8904_DAC_COMPMODE 0x0001 /* DAC_COMPMODE */ +#define WM8904_DAC_COMPMODE_MASK 0x0001 /* DAC_COMPMODE */ +#define WM8904_DAC_COMPMODE_SHIFT 0 /* DAC_COMPMODE */ +#define WM8904_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */ + +/* + * R25 (0x19) - Audio Interface 1 + */ +#define WM8904_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ +#define WM8904_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */ +#define WM8904_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */ +#define WM8904_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */ +#define WM8904_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8904_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8904_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */ +#define WM8904_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */ +#define WM8904_AIFADC_TDM 0x0800 /* AIFADC_TDM */ +#define WM8904_AIFADC_TDM_MASK 0x0800 /* AIFADC_TDM */ +#define WM8904_AIFADC_TDM_SHIFT 11 /* AIFADC_TDM */ +#define WM8904_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */ +#define WM8904_AIFADC_TDM_CHAN 0x0400 /* AIFADC_TDM_CHAN */ +#define WM8904_AIFADC_TDM_CHAN_MASK 0x0400 /* AIFADC_TDM_CHAN */ +#define WM8904_AIFADC_TDM_CHAN_SHIFT 10 /* AIFADC_TDM_CHAN */ +#define WM8904_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */ +#define WM8904_AIF_TRIS 0x0100 /* AIF_TRIS */ +#define WM8904_AIF_TRIS_MASK 0x0100 /* AIF_TRIS */ +#define WM8904_AIF_TRIS_SHIFT 8 /* AIF_TRIS */ +#define WM8904_AIF_TRIS_WIDTH 1 /* AIF_TRIS */ +#define WM8904_AIF_BCLK_INV 0x0080 /* AIF_BCLK_INV */ +#define WM8904_AIF_BCLK_INV_MASK 0x0080 /* AIF_BCLK_INV */ +#define WM8904_AIF_BCLK_INV_SHIFT 7 /* AIF_BCLK_INV */ +#define WM8904_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */ +#define WM8904_BCLK_DIR 0x0040 /* BCLK_DIR */ +#define WM8904_BCLK_DIR_MASK 0x0040 /* BCLK_DIR */ +#define WM8904_BCLK_DIR_SHIFT 6 /* BCLK_DIR */ +#define WM8904_BCLK_DIR_WIDTH 1 /* BCLK_DIR */ +#define WM8904_AIF_LRCLK_INV 0x0010 /* AIF_LRCLK_INV */ +#define WM8904_AIF_LRCLK_INV_MASK 0x0010 /* AIF_LRCLK_INV */ +#define WM8904_AIF_LRCLK_INV_SHIFT 4 /* AIF_LRCLK_INV */ +#define WM8904_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */ +#define WM8904_AIF_WL_MASK 0x000C /* AIF_WL - [3:2] */ +#define WM8904_AIF_WL_SHIFT 2 /* AIF_WL - [3:2] */ +#define WM8904_AIF_WL_WIDTH 2 /* AIF_WL - [3:2] */ +#define WM8904_AIF_FMT_MASK 0x0003 /* AIF_FMT - [1:0] */ +#define WM8904_AIF_FMT_SHIFT 0 /* AIF_FMT - [1:0] */ +#define WM8904_AIF_FMT_WIDTH 2 /* AIF_FMT - [1:0] */ + +/* + * R26 (0x1A) - Audio Interface 2 + */ +#define WM8904_OPCLK_DIV_MASK 0x0F00 /* OPCLK_DIV - [11:8] */ +#define WM8904_OPCLK_DIV_SHIFT 8 /* OPCLK_DIV - [11:8] */ +#define WM8904_OPCLK_DIV_WIDTH 4 /* OPCLK_DIV - [11:8] */ +#define WM8904_BCLK_DIV_MASK 0x001F /* BCLK_DIV - [4:0] */ +#define WM8904_BCLK_DIV_SHIFT 0 /* BCLK_DIV - [4:0] */ +#define WM8904_BCLK_DIV_WIDTH 5 /* BCLK_DIV - [4:0] */ + +/* + * R27 (0x1B) - Audio Interface 3 + */ +#define WM8904_LRCLK_DIR 0x0800 /* LRCLK_DIR */ +#define WM8904_LRCLK_DIR_MASK 0x0800 /* LRCLK_DIR */ +#define WM8904_LRCLK_DIR_SHIFT 11 /* LRCLK_DIR */ +#define WM8904_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */ +#define WM8904_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */ +#define WM8904_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */ +#define WM8904_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */ + +/* + * R30 (0x1E) - DAC Digital Volume Left + */ +#define WM8904_DAC_VU 0x0100 /* DAC_VU */ +#define WM8904_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8904_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8904_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8904_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8904_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */ +#define WM8904_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */ + +/* + * R31 (0x1F) - DAC Digital Volume Right + */ +#define WM8904_DAC_VU 0x0100 /* DAC_VU */ +#define WM8904_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8904_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8904_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8904_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8904_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */ +#define WM8904_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */ + +/* + * R32 (0x20) - DAC Digital 0 + */ +#define WM8904_ADCL_DAC_SVOL_MASK 0x0F00 /* ADCL_DAC_SVOL - [11:8] */ +#define WM8904_ADCL_DAC_SVOL_SHIFT 8 /* ADCL_DAC_SVOL - [11:8] */ +#define WM8904_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [11:8] */ +#define WM8904_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8904_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8904_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8904_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */ +#define WM8904_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */ +#define WM8904_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */ +#define WM8904_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */ +#define WM8904_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */ +#define WM8904_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */ + +/* + * R33 (0x21) - DAC Digital 1 + */ +#define WM8904_DAC_MONO 0x1000 /* DAC_MONO */ +#define WM8904_DAC_MONO_MASK 0x1000 /* DAC_MONO */ +#define WM8904_DAC_MONO_SHIFT 12 /* DAC_MONO */ +#define WM8904_DAC_MONO_WIDTH 1 /* DAC_MONO */ +#define WM8904_DAC_SB_FILT 0x0800 /* DAC_SB_FILT */ +#define WM8904_DAC_SB_FILT_MASK 0x0800 /* DAC_SB_FILT */ +#define WM8904_DAC_SB_FILT_SHIFT 11 /* DAC_SB_FILT */ +#define WM8904_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */ +#define WM8904_DAC_MUTERATE 0x0400 /* DAC_MUTERATE */ +#define WM8904_DAC_MUTERATE_MASK 0x0400 /* DAC_MUTERATE */ +#define WM8904_DAC_MUTERATE_SHIFT 10 /* DAC_MUTERATE */ +#define WM8904_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ +#define WM8904_DAC_UNMUTE_RAMP 0x0200 /* DAC_UNMUTE_RAMP */ +#define WM8904_DAC_UNMUTE_RAMP_MASK 0x0200 /* DAC_UNMUTE_RAMP */ +#define WM8904_DAC_UNMUTE_RAMP_SHIFT 9 /* DAC_UNMUTE_RAMP */ +#define WM8904_DAC_UNMUTE_RAMP_WIDTH 1 /* DAC_UNMUTE_RAMP */ +#define WM8904_DAC_OSR128 0x0040 /* DAC_OSR128 */ +#define WM8904_DAC_OSR128_MASK 0x0040 /* DAC_OSR128 */ +#define WM8904_DAC_OSR128_SHIFT 6 /* DAC_OSR128 */ +#define WM8904_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */ +#define WM8904_DAC_MUTE 0x0008 /* DAC_MUTE */ +#define WM8904_DAC_MUTE_MASK 0x0008 /* DAC_MUTE */ +#define WM8904_DAC_MUTE_SHIFT 3 /* DAC_MUTE */ +#define WM8904_DAC_MUTE_WIDTH 1 /* DAC_MUTE */ +#define WM8904_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */ +#define WM8904_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */ +#define WM8904_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */ + +/* + * R36 (0x24) - ADC Digital Volume Left + */ +#define WM8904_ADC_VU 0x0100 /* ADC_VU */ +#define WM8904_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8904_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8904_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8904_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8904_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */ +#define WM8904_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */ + +/* + * R37 (0x25) - ADC Digital Volume Right + */ +#define WM8904_ADC_VU 0x0100 /* ADC_VU */ +#define WM8904_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8904_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8904_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8904_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8904_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */ +#define WM8904_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */ + +/* + * R38 (0x26) - ADC Digital 0 + */ +#define WM8904_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */ +#define WM8904_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */ +#define WM8904_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */ +#define WM8904_ADC_HPF 0x0010 /* ADC_HPF */ +#define WM8904_ADC_HPF_MASK 0x0010 /* ADC_HPF */ +#define WM8904_ADC_HPF_SHIFT 4 /* ADC_HPF */ +#define WM8904_ADC_HPF_WIDTH 1 /* ADC_HPF */ +#define WM8904_ADCL_DATINV 0x0002 /* ADCL_DATINV */ +#define WM8904_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */ +#define WM8904_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */ +#define WM8904_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */ +#define WM8904_ADCR_DATINV 0x0001 /* ADCR_DATINV */ +#define WM8904_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */ +#define WM8904_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */ +#define WM8904_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */ + +/* + * R39 (0x27) - Digital Microphone 0 + */ +#define WM8904_DMIC_ENA 0x1000 /* DMIC_ENA */ +#define WM8904_DMIC_ENA_MASK 0x1000 /* DMIC_ENA */ +#define WM8904_DMIC_ENA_SHIFT 12 /* DMIC_ENA */ +#define WM8904_DMIC_ENA_WIDTH 1 /* DMIC_ENA */ +#define WM8904_DMIC_SRC 0x0800 /* DMIC_SRC */ +#define WM8904_DMIC_SRC_MASK 0x0800 /* DMIC_SRC */ +#define WM8904_DMIC_SRC_SHIFT 11 /* DMIC_SRC */ +#define WM8904_DMIC_SRC_WIDTH 1 /* DMIC_SRC */ + +/* + * R40 (0x28) - DRC 0 + */ +#define WM8904_DRC_ENA 0x8000 /* DRC_ENA */ +#define WM8904_DRC_ENA_MASK 0x8000 /* DRC_ENA */ +#define WM8904_DRC_ENA_SHIFT 15 /* DRC_ENA */ +#define WM8904_DRC_ENA_WIDTH 1 /* DRC_ENA */ +#define WM8904_DRC_DAC_PATH 0x4000 /* DRC_DAC_PATH */ +#define WM8904_DRC_DAC_PATH_MASK 0x4000 /* DRC_DAC_PATH */ +#define WM8904_DRC_DAC_PATH_SHIFT 14 /* DRC_DAC_PATH */ +#define WM8904_DRC_DAC_PATH_WIDTH 1 /* DRC_DAC_PATH */ +#define WM8904_DRC_GS_HYST_LVL_MASK 0x1800 /* DRC_GS_HYST_LVL - [12:11] */ +#define WM8904_DRC_GS_HYST_LVL_SHIFT 11 /* DRC_GS_HYST_LVL - [12:11] */ +#define WM8904_DRC_GS_HYST_LVL_WIDTH 2 /* DRC_GS_HYST_LVL - [12:11] */ +#define WM8904_DRC_STARTUP_GAIN_MASK 0x07C0 /* DRC_STARTUP_GAIN - [10:6] */ +#define WM8904_DRC_STARTUP_GAIN_SHIFT 6 /* DRC_STARTUP_GAIN - [10:6] */ +#define WM8904_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [10:6] */ +#define WM8904_DRC_FF_DELAY 0x0020 /* DRC_FF_DELAY */ +#define WM8904_DRC_FF_DELAY_MASK 0x0020 /* DRC_FF_DELAY */ +#define WM8904_DRC_FF_DELAY_SHIFT 5 /* DRC_FF_DELAY */ +#define WM8904_DRC_FF_DELAY_WIDTH 1 /* DRC_FF_DELAY */ +#define WM8904_DRC_GS_ENA 0x0008 /* DRC_GS_ENA */ +#define WM8904_DRC_GS_ENA_MASK 0x0008 /* DRC_GS_ENA */ +#define WM8904_DRC_GS_ENA_SHIFT 3 /* DRC_GS_ENA */ +#define WM8904_DRC_GS_ENA_WIDTH 1 /* DRC_GS_ENA */ +#define WM8904_DRC_QR 0x0004 /* DRC_QR */ +#define WM8904_DRC_QR_MASK 0x0004 /* DRC_QR */ +#define WM8904_DRC_QR_SHIFT 2 /* DRC_QR */ +#define WM8904_DRC_QR_WIDTH 1 /* DRC_QR */ +#define WM8904_DRC_ANTICLIP 0x0002 /* DRC_ANTICLIP */ +#define WM8904_DRC_ANTICLIP_MASK 0x0002 /* DRC_ANTICLIP */ +#define WM8904_DRC_ANTICLIP_SHIFT 1 /* DRC_ANTICLIP */ +#define WM8904_DRC_ANTICLIP_WIDTH 1 /* DRC_ANTICLIP */ +#define WM8904_DRC_GS_HYST 0x0001 /* DRC_GS_HYST */ +#define WM8904_DRC_GS_HYST_MASK 0x0001 /* DRC_GS_HYST */ +#define WM8904_DRC_GS_HYST_SHIFT 0 /* DRC_GS_HYST */ +#define WM8904_DRC_GS_HYST_WIDTH 1 /* DRC_GS_HYST */ + +/* + * R41 (0x29) - DRC 1 + */ +#define WM8904_DRC_ATK_MASK 0xF000 /* DRC_ATK - [15:12] */ +#define WM8904_DRC_ATK_SHIFT 12 /* DRC_ATK - [15:12] */ +#define WM8904_DRC_ATK_WIDTH 4 /* DRC_ATK - [15:12] */ +#define WM8904_DRC_DCY_MASK 0x0F00 /* DRC_DCY - [11:8] */ +#define WM8904_DRC_DCY_SHIFT 8 /* DRC_DCY - [11:8] */ +#define WM8904_DRC_DCY_WIDTH 4 /* DRC_DCY - [11:8] */ +#define WM8904_DRC_QR_THR_MASK 0x00C0 /* DRC_QR_THR - [7:6] */ +#define WM8904_DRC_QR_THR_SHIFT 6 /* DRC_QR_THR - [7:6] */ +#define WM8904_DRC_QR_THR_WIDTH 2 /* DRC_QR_THR - [7:6] */ +#define WM8904_DRC_QR_DCY_MASK 0x0030 /* DRC_QR_DCY - [5:4] */ +#define WM8904_DRC_QR_DCY_SHIFT 4 /* DRC_QR_DCY - [5:4] */ +#define WM8904_DRC_QR_DCY_WIDTH 2 /* DRC_QR_DCY - [5:4] */ +#define WM8904_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */ +#define WM8904_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */ +#define WM8904_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */ +#define WM8904_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */ +#define WM8904_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */ +#define WM8904_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */ + +/* + * R42 (0x2A) - DRC 2 + */ +#define WM8904_DRC_HI_COMP_MASK 0x0038 /* DRC_HI_COMP - [5:3] */ +#define WM8904_DRC_HI_COMP_SHIFT 3 /* DRC_HI_COMP - [5:3] */ +#define WM8904_DRC_HI_COMP_WIDTH 3 /* DRC_HI_COMP - [5:3] */ +#define WM8904_DRC_LO_COMP_MASK 0x0007 /* DRC_LO_COMP - [2:0] */ +#define WM8904_DRC_LO_COMP_SHIFT 0 /* DRC_LO_COMP - [2:0] */ +#define WM8904_DRC_LO_COMP_WIDTH 3 /* DRC_LO_COMP - [2:0] */ + +/* + * R43 (0x2B) - DRC 3 + */ +#define WM8904_DRC_KNEE_IP_MASK 0x07E0 /* DRC_KNEE_IP - [10:5] */ +#define WM8904_DRC_KNEE_IP_SHIFT 5 /* DRC_KNEE_IP - [10:5] */ +#define WM8904_DRC_KNEE_IP_WIDTH 6 /* DRC_KNEE_IP - [10:5] */ +#define WM8904_DRC_KNEE_OP_MASK 0x001F /* DRC_KNEE_OP - [4:0] */ +#define WM8904_DRC_KNEE_OP_SHIFT 0 /* DRC_KNEE_OP - [4:0] */ +#define WM8904_DRC_KNEE_OP_WIDTH 5 /* DRC_KNEE_OP - [4:0] */ + +/* + * R44 (0x2C) - Analogue Left Input 0 + */ +#define WM8904_LINMUTE 0x0080 /* LINMUTE */ +#define WM8904_LINMUTE_MASK 0x0080 /* LINMUTE */ +#define WM8904_LINMUTE_SHIFT 7 /* LINMUTE */ +#define WM8904_LINMUTE_WIDTH 1 /* LINMUTE */ +#define WM8904_LIN_VOL_MASK 0x001F /* LIN_VOL - [4:0] */ +#define WM8904_LIN_VOL_SHIFT 0 /* LIN_VOL - [4:0] */ +#define WM8904_LIN_VOL_WIDTH 5 /* LIN_VOL - [4:0] */ + +/* + * R45 (0x2D) - Analogue Right Input 0 + */ +#define WM8904_RINMUTE 0x0080 /* RINMUTE */ +#define WM8904_RINMUTE_MASK 0x0080 /* RINMUTE */ +#define WM8904_RINMUTE_SHIFT 7 /* RINMUTE */ +#define WM8904_RINMUTE_WIDTH 1 /* RINMUTE */ +#define WM8904_RIN_VOL_MASK 0x001F /* RIN_VOL - [4:0] */ +#define WM8904_RIN_VOL_SHIFT 0 /* RIN_VOL - [4:0] */ +#define WM8904_RIN_VOL_WIDTH 5 /* RIN_VOL - [4:0] */ + +/* + * R46 (0x2E) - Analogue Left Input 1 + */ +#define WM8904_INL_CM_ENA 0x0040 /* INL_CM_ENA */ +#define WM8904_INL_CM_ENA_MASK 0x0040 /* INL_CM_ENA */ +#define WM8904_INL_CM_ENA_SHIFT 6 /* INL_CM_ENA */ +#define WM8904_INL_CM_ENA_WIDTH 1 /* INL_CM_ENA */ +#define WM8904_L_IP_SEL_N_MASK 0x0030 /* L_IP_SEL_N - [5:4] */ +#define WM8904_L_IP_SEL_N_SHIFT 4 /* L_IP_SEL_N - [5:4] */ +#define WM8904_L_IP_SEL_N_WIDTH 2 /* L_IP_SEL_N - [5:4] */ +#define WM8904_L_IP_SEL_P_MASK 0x000C /* L_IP_SEL_P - [3:2] */ +#define WM8904_L_IP_SEL_P_SHIFT 2 /* L_IP_SEL_P - [3:2] */ +#define WM8904_L_IP_SEL_P_WIDTH 2 /* L_IP_SEL_P - [3:2] */ +#define WM8904_L_MODE_MASK 0x0003 /* L_MODE - [1:0] */ +#define WM8904_L_MODE_SHIFT 0 /* L_MODE - [1:0] */ +#define WM8904_L_MODE_WIDTH 2 /* L_MODE - [1:0] */ + +/* + * R47 (0x2F) - Analogue Right Input 1 + */ +#define WM8904_INR_CM_ENA 0x0040 /* INR_CM_ENA */ +#define WM8904_INR_CM_ENA_MASK 0x0040 /* INR_CM_ENA */ +#define WM8904_INR_CM_ENA_SHIFT 6 /* INR_CM_ENA */ +#define WM8904_INR_CM_ENA_WIDTH 1 /* INR_CM_ENA */ +#define WM8904_R_IP_SEL_N_MASK 0x0030 /* R_IP_SEL_N - [5:4] */ +#define WM8904_R_IP_SEL_N_SHIFT 4 /* R_IP_SEL_N - [5:4] */ +#define WM8904_R_IP_SEL_N_WIDTH 2 /* R_IP_SEL_N - [5:4] */ +#define WM8904_R_IP_SEL_P_MASK 0x000C /* R_IP_SEL_P - [3:2] */ +#define WM8904_R_IP_SEL_P_SHIFT 2 /* R_IP_SEL_P - [3:2] */ +#define WM8904_R_IP_SEL_P_WIDTH 2 /* R_IP_SEL_P - [3:2] */ +#define WM8904_R_MODE_MASK 0x0003 /* R_MODE - [1:0] */ +#define WM8904_R_MODE_SHIFT 0 /* R_MODE - [1:0] */ +#define WM8904_R_MODE_WIDTH 2 /* R_MODE - [1:0] */ + +/* + * R57 (0x39) - Analogue OUT1 Left + */ +#define WM8904_HPOUTL_MUTE 0x0100 /* HPOUTL_MUTE */ +#define WM8904_HPOUTL_MUTE_MASK 0x0100 /* HPOUTL_MUTE */ +#define WM8904_HPOUTL_MUTE_SHIFT 8 /* HPOUTL_MUTE */ +#define WM8904_HPOUTL_MUTE_WIDTH 1 /* HPOUTL_MUTE */ +#define WM8904_HPOUT_VU 0x0080 /* HPOUT_VU */ +#define WM8904_HPOUT_VU_MASK 0x0080 /* HPOUT_VU */ +#define WM8904_HPOUT_VU_SHIFT 7 /* HPOUT_VU */ +#define WM8904_HPOUT_VU_WIDTH 1 /* HPOUT_VU */ +#define WM8904_HPOUTLZC 0x0040 /* HPOUTLZC */ +#define WM8904_HPOUTLZC_MASK 0x0040 /* HPOUTLZC */ +#define WM8904_HPOUTLZC_SHIFT 6 /* HPOUTLZC */ +#define WM8904_HPOUTLZC_WIDTH 1 /* HPOUTLZC */ +#define WM8904_HPOUTL_VOL_MASK 0x003F /* HPOUTL_VOL - [5:0] */ +#define WM8904_HPOUTL_VOL_SHIFT 0 /* HPOUTL_VOL - [5:0] */ +#define WM8904_HPOUTL_VOL_WIDTH 6 /* HPOUTL_VOL - [5:0] */ + +/* + * R58 (0x3A) - Analogue OUT1 Right + */ +#define WM8904_HPOUTR_MUTE 0x0100 /* HPOUTR_MUTE */ +#define WM8904_HPOUTR_MUTE_MASK 0x0100 /* HPOUTR_MUTE */ +#define WM8904_HPOUTR_MUTE_SHIFT 8 /* HPOUTR_MUTE */ +#define WM8904_HPOUTR_MUTE_WIDTH 1 /* HPOUTR_MUTE */ +#define WM8904_HPOUT_VU 0x0080 /* HPOUT_VU */ +#define WM8904_HPOUT_VU_MASK 0x0080 /* HPOUT_VU */ +#define WM8904_HPOUT_VU_SHIFT 7 /* HPOUT_VU */ +#define WM8904_HPOUT_VU_WIDTH 1 /* HPOUT_VU */ +#define WM8904_HPOUTRZC 0x0040 /* HPOUTRZC */ +#define WM8904_HPOUTRZC_MASK 0x0040 /* HPOUTRZC */ +#define WM8904_HPOUTRZC_SHIFT 6 /* HPOUTRZC */ +#define WM8904_HPOUTRZC_WIDTH 1 /* HPOUTRZC */ +#define WM8904_HPOUTR_VOL_MASK 0x003F /* HPOUTR_VOL - [5:0] */ +#define WM8904_HPOUTR_VOL_SHIFT 0 /* HPOUTR_VOL - [5:0] */ +#define WM8904_HPOUTR_VOL_WIDTH 6 /* HPOUTR_VOL - [5:0] */ + +/* + * R59 (0x3B) - Analogue OUT2 Left + */ +#define WM8904_LINEOUTL_MUTE 0x0100 /* LINEOUTL_MUTE */ +#define WM8904_LINEOUTL_MUTE_MASK 0x0100 /* LINEOUTL_MUTE */ +#define WM8904_LINEOUTL_MUTE_SHIFT 8 /* LINEOUTL_MUTE */ +#define WM8904_LINEOUTL_MUTE_WIDTH 1 /* LINEOUTL_MUTE */ +#define WM8904_LINEOUT_VU 0x0080 /* LINEOUT_VU */ +#define WM8904_LINEOUT_VU_MASK 0x0080 /* LINEOUT_VU */ +#define WM8904_LINEOUT_VU_SHIFT 7 /* LINEOUT_VU */ +#define WM8904_LINEOUT_VU_WIDTH 1 /* LINEOUT_VU */ +#define WM8904_LINEOUTLZC 0x0040 /* LINEOUTLZC */ +#define WM8904_LINEOUTLZC_MASK 0x0040 /* LINEOUTLZC */ +#define WM8904_LINEOUTLZC_SHIFT 6 /* LINEOUTLZC */ +#define WM8904_LINEOUTLZC_WIDTH 1 /* LINEOUTLZC */ +#define WM8904_LINEOUTL_VOL_MASK 0x003F /* LINEOUTL_VOL - [5:0] */ +#define WM8904_LINEOUTL_VOL_SHIFT 0 /* LINEOUTL_VOL - [5:0] */ +#define WM8904_LINEOUTL_VOL_WIDTH 6 /* LINEOUTL_VOL - [5:0] */ + +/* + * R60 (0x3C) - Analogue OUT2 Right + */ +#define WM8904_LINEOUTR_MUTE 0x0100 /* LINEOUTR_MUTE */ +#define WM8904_LINEOUTR_MUTE_MASK 0x0100 /* LINEOUTR_MUTE */ +#define WM8904_LINEOUTR_MUTE_SHIFT 8 /* LINEOUTR_MUTE */ +#define WM8904_LINEOUTR_MUTE_WIDTH 1 /* LINEOUTR_MUTE */ +#define WM8904_LINEOUT_VU 0x0080 /* LINEOUT_VU */ +#define WM8904_LINEOUT_VU_MASK 0x0080 /* LINEOUT_VU */ +#define WM8904_LINEOUT_VU_SHIFT 7 /* LINEOUT_VU */ +#define WM8904_LINEOUT_VU_WIDTH 1 /* LINEOUT_VU */ +#define WM8904_LINEOUTRZC 0x0040 /* LINEOUTRZC */ +#define WM8904_LINEOUTRZC_MASK 0x0040 /* LINEOUTRZC */ +#define WM8904_LINEOUTRZC_SHIFT 6 /* LINEOUTRZC */ +#define WM8904_LINEOUTRZC_WIDTH 1 /* LINEOUTRZC */ +#define WM8904_LINEOUTR_VOL_MASK 0x003F /* LINEOUTR_VOL - [5:0] */ +#define WM8904_LINEOUTR_VOL_SHIFT 0 /* LINEOUTR_VOL - [5:0] */ +#define WM8904_LINEOUTR_VOL_WIDTH 6 /* LINEOUTR_VOL - [5:0] */ + +/* + * R61 (0x3D) - Analogue OUT12 ZC + */ +#define WM8904_HPL_BYP_ENA 0x0008 /* HPL_BYP_ENA */ +#define WM8904_HPL_BYP_ENA_MASK 0x0008 /* HPL_BYP_ENA */ +#define WM8904_HPL_BYP_ENA_SHIFT 3 /* HPL_BYP_ENA */ +#define WM8904_HPL_BYP_ENA_WIDTH 1 /* HPL_BYP_ENA */ +#define WM8904_HPR_BYP_ENA 0x0004 /* HPR_BYP_ENA */ +#define WM8904_HPR_BYP_ENA_MASK 0x0004 /* HPR_BYP_ENA */ +#define WM8904_HPR_BYP_ENA_SHIFT 2 /* HPR_BYP_ENA */ +#define WM8904_HPR_BYP_ENA_WIDTH 1 /* HPR_BYP_ENA */ +#define WM8904_LINEOUTL_BYP_ENA 0x0002 /* LINEOUTL_BYP_ENA */ +#define WM8904_LINEOUTL_BYP_ENA_MASK 0x0002 /* LINEOUTL_BYP_ENA */ +#define WM8904_LINEOUTL_BYP_ENA_SHIFT 1 /* LINEOUTL_BYP_ENA */ +#define WM8904_LINEOUTL_BYP_ENA_WIDTH 1 /* LINEOUTL_BYP_ENA */ +#define WM8904_LINEOUTR_BYP_ENA 0x0001 /* LINEOUTR_BYP_ENA */ +#define WM8904_LINEOUTR_BYP_ENA_MASK 0x0001 /* LINEOUTR_BYP_ENA */ +#define WM8904_LINEOUTR_BYP_ENA_SHIFT 0 /* LINEOUTR_BYP_ENA */ +#define WM8904_LINEOUTR_BYP_ENA_WIDTH 1 /* LINEOUTR_BYP_ENA */ + +/* + * R67 (0x43) - DC Servo 0 + */ +#define WM8904_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */ +#define WM8904_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */ +#define WM8904_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */ +#define WM8904_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */ +#define WM8904_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */ +#define WM8904_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */ +#define WM8904_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */ +#define WM8904_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */ +#define WM8904_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8904_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8904_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */ +#define WM8904_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */ +#define WM8904_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8904_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8904_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */ +#define WM8904_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */ + +/* + * R68 (0x44) - DC Servo 1 + */ +#define WM8904_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */ +#define WM8904_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */ +#define WM8904_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */ +#define WM8904_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */ +#define WM8904_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */ +#define WM8904_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */ +#define WM8904_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */ +#define WM8904_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */ +#define WM8904_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8904_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8904_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */ +#define WM8904_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */ +#define WM8904_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8904_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8904_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */ +#define WM8904_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */ +#define WM8904_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */ +#define WM8904_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */ +#define WM8904_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */ +#define WM8904_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */ +#define WM8904_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */ +#define WM8904_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */ +#define WM8904_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */ +#define WM8904_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */ +#define WM8904_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8904_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8904_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */ +#define WM8904_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */ +#define WM8904_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8904_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8904_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */ +#define WM8904_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */ +#define WM8904_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */ +#define WM8904_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */ +#define WM8904_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */ +#define WM8904_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */ +#define WM8904_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */ +#define WM8904_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */ +#define WM8904_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */ +#define WM8904_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */ +#define WM8904_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8904_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8904_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */ +#define WM8904_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */ +#define WM8904_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8904_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8904_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */ +#define WM8904_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */ +#define WM8904_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */ +#define WM8904_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */ +#define WM8904_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */ +#define WM8904_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */ +#define WM8904_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */ +#define WM8904_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */ +#define WM8904_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */ +#define WM8904_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */ +#define WM8904_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */ +#define WM8904_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */ +#define WM8904_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM8904_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM8904_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */ +#define WM8904_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */ +#define WM8904_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */ +#define WM8904_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */ + +/* + * R69 (0x45) - DC Servo 2 + */ +#define WM8904_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8904_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8904_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8904_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8904_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8904_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */ + +/* + * R71 (0x47) - DC Servo 4 + */ +#define WM8904_DCS_SERIES_NO_23_MASK 0x007F /* DCS_SERIES_NO_23 - [6:0] */ +#define WM8904_DCS_SERIES_NO_23_SHIFT 0 /* DCS_SERIES_NO_23 - [6:0] */ +#define WM8904_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [6:0] */ + +/* + * R72 (0x48) - DC Servo 5 + */ +#define WM8904_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */ +#define WM8904_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */ +#define WM8904_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */ + +/* + * R73 (0x49) - DC Servo 6 + */ +#define WM8904_DCS_DAC_WR_VAL_3_MASK 0x00FF /* DCS_DAC_WR_VAL_3 - [7:0] */ +#define WM8904_DCS_DAC_WR_VAL_3_SHIFT 0 /* DCS_DAC_WR_VAL_3 - [7:0] */ +#define WM8904_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [7:0] */ + +/* + * R74 (0x4A) - DC Servo 7 + */ +#define WM8904_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */ +#define WM8904_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */ +#define WM8904_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */ + +/* + * R75 (0x4B) - DC Servo 8 + */ +#define WM8904_DCS_DAC_WR_VAL_1_MASK 0x00FF /* DCS_DAC_WR_VAL_1 - [7:0] */ +#define WM8904_DCS_DAC_WR_VAL_1_SHIFT 0 /* DCS_DAC_WR_VAL_1 - [7:0] */ +#define WM8904_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [7:0] */ + +/* + * R76 (0x4C) - DC Servo 9 + */ +#define WM8904_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8904_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8904_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */ + +/* + * R77 (0x4D) - DC Servo Readback 0 + */ +#define WM8904_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8904_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8904_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8904_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8904_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8904_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8904_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */ +#define WM8904_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */ +#define WM8904_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */ + +/* + * R90 (0x5A) - Analogue HP 0 + */ +#define WM8904_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */ +#define WM8904_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */ +#define WM8904_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */ +#define WM8904_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */ +#define WM8904_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */ +#define WM8904_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */ +#define WM8904_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */ +#define WM8904_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */ +#define WM8904_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */ +#define WM8904_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */ +#define WM8904_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */ +#define WM8904_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */ +#define WM8904_HPL_ENA 0x0010 /* HPL_ENA */ +#define WM8904_HPL_ENA_MASK 0x0010 /* HPL_ENA */ +#define WM8904_HPL_ENA_SHIFT 4 /* HPL_ENA */ +#define WM8904_HPL_ENA_WIDTH 1 /* HPL_ENA */ +#define WM8904_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */ +#define WM8904_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */ +#define WM8904_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */ +#define WM8904_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */ +#define WM8904_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */ +#define WM8904_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */ +#define WM8904_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */ +#define WM8904_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */ +#define WM8904_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */ +#define WM8904_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */ +#define WM8904_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */ +#define WM8904_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */ +#define WM8904_HPR_ENA 0x0001 /* HPR_ENA */ +#define WM8904_HPR_ENA_MASK 0x0001 /* HPR_ENA */ +#define WM8904_HPR_ENA_SHIFT 0 /* HPR_ENA */ +#define WM8904_HPR_ENA_WIDTH 1 /* HPR_ENA */ + +/* + * R94 (0x5E) - Analogue Lineout 0 + */ +#define WM8904_LINEOUTL_RMV_SHORT 0x0080 /* LINEOUTL_RMV_SHORT */ +#define WM8904_LINEOUTL_RMV_SHORT_MASK 0x0080 /* LINEOUTL_RMV_SHORT */ +#define WM8904_LINEOUTL_RMV_SHORT_SHIFT 7 /* LINEOUTL_RMV_SHORT */ +#define WM8904_LINEOUTL_RMV_SHORT_WIDTH 1 /* LINEOUTL_RMV_SHORT */ +#define WM8904_LINEOUTL_ENA_OUTP 0x0040 /* LINEOUTL_ENA_OUTP */ +#define WM8904_LINEOUTL_ENA_OUTP_MASK 0x0040 /* LINEOUTL_ENA_OUTP */ +#define WM8904_LINEOUTL_ENA_OUTP_SHIFT 6 /* LINEOUTL_ENA_OUTP */ +#define WM8904_LINEOUTL_ENA_OUTP_WIDTH 1 /* LINEOUTL_ENA_OUTP */ +#define WM8904_LINEOUTL_ENA_DLY 0x0020 /* LINEOUTL_ENA_DLY */ +#define WM8904_LINEOUTL_ENA_DLY_MASK 0x0020 /* LINEOUTL_ENA_DLY */ +#define WM8904_LINEOUTL_ENA_DLY_SHIFT 5 /* LINEOUTL_ENA_DLY */ +#define WM8904_LINEOUTL_ENA_DLY_WIDTH 1 /* LINEOUTL_ENA_DLY */ +#define WM8904_LINEOUTL_ENA 0x0010 /* LINEOUTL_ENA */ +#define WM8904_LINEOUTL_ENA_MASK 0x0010 /* LINEOUTL_ENA */ +#define WM8904_LINEOUTL_ENA_SHIFT 4 /* LINEOUTL_ENA */ +#define WM8904_LINEOUTL_ENA_WIDTH 1 /* LINEOUTL_ENA */ +#define WM8904_LINEOUTR_RMV_SHORT 0x0008 /* LINEOUTR_RMV_SHORT */ +#define WM8904_LINEOUTR_RMV_SHORT_MASK 0x0008 /* LINEOUTR_RMV_SHORT */ +#define WM8904_LINEOUTR_RMV_SHORT_SHIFT 3 /* LINEOUTR_RMV_SHORT */ +#define WM8904_LINEOUTR_RMV_SHORT_WIDTH 1 /* LINEOUTR_RMV_SHORT */ +#define WM8904_LINEOUTR_ENA_OUTP 0x0004 /* LINEOUTR_ENA_OUTP */ +#define WM8904_LINEOUTR_ENA_OUTP_MASK 0x0004 /* LINEOUTR_ENA_OUTP */ +#define WM8904_LINEOUTR_ENA_OUTP_SHIFT 2 /* LINEOUTR_ENA_OUTP */ +#define WM8904_LINEOUTR_ENA_OUTP_WIDTH 1 /* LINEOUTR_ENA_OUTP */ +#define WM8904_LINEOUTR_ENA_DLY 0x0002 /* LINEOUTR_ENA_DLY */ +#define WM8904_LINEOUTR_ENA_DLY_MASK 0x0002 /* LINEOUTR_ENA_DLY */ +#define WM8904_LINEOUTR_ENA_DLY_SHIFT 1 /* LINEOUTR_ENA_DLY */ +#define WM8904_LINEOUTR_ENA_DLY_WIDTH 1 /* LINEOUTR_ENA_DLY */ +#define WM8904_LINEOUTR_ENA 0x0001 /* LINEOUTR_ENA */ +#define WM8904_LINEOUTR_ENA_MASK 0x0001 /* LINEOUTR_ENA */ +#define WM8904_LINEOUTR_ENA_SHIFT 0 /* LINEOUTR_ENA */ +#define WM8904_LINEOUTR_ENA_WIDTH 1 /* LINEOUTR_ENA */ + +/* + * R98 (0x62) - Charge Pump 0 + */ +#define WM8904_CP_ENA 0x0001 /* CP_ENA */ +#define WM8904_CP_ENA_MASK 0x0001 /* CP_ENA */ +#define WM8904_CP_ENA_SHIFT 0 /* CP_ENA */ +#define WM8904_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R104 (0x68) - Class W 0 + */ +#define WM8904_CP_DYN_PWR 0x0001 /* CP_DYN_PWR */ +#define WM8904_CP_DYN_PWR_MASK 0x0001 /* CP_DYN_PWR */ +#define WM8904_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR */ +#define WM8904_CP_DYN_PWR_WIDTH 1 /* CP_DYN_PWR */ + +/* + * R108 (0x6C) - Write Sequencer 0 + */ +#define WM8904_WSEQ_ENA 0x0100 /* WSEQ_ENA */ +#define WM8904_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */ +#define WM8904_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */ +#define WM8904_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM8904_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8904_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8904_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */ + +/* + * R109 (0x6D) - Write Sequencer 1 + */ +#define WM8904_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8904_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8904_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8904_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */ +#define WM8904_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */ +#define WM8904_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */ +#define WM8904_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */ +#define WM8904_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */ +#define WM8904_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */ + +/* + * R110 (0x6E) - Write Sequencer 2 + */ +#define WM8904_WSEQ_EOS 0x4000 /* WSEQ_EOS */ +#define WM8904_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */ +#define WM8904_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */ +#define WM8904_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */ +#define WM8904_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */ +#define WM8904_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */ +#define WM8904_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */ +#define WM8904_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */ +#define WM8904_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */ +#define WM8904_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */ + +/* + * R111 (0x6F) - Write Sequencer 3 + */ +#define WM8904_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */ +#define WM8904_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */ +#define WM8904_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */ +#define WM8904_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM8904_WSEQ_START 0x0100 /* WSEQ_START */ +#define WM8904_WSEQ_START_MASK 0x0100 /* WSEQ_START */ +#define WM8904_WSEQ_START_SHIFT 8 /* WSEQ_START */ +#define WM8904_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM8904_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */ +#define WM8904_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */ +#define WM8904_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */ + +/* + * R112 (0x70) - Write Sequencer 4 + */ +#define WM8904_WSEQ_CURRENT_INDEX_MASK 0x03F0 /* WSEQ_CURRENT_INDEX - [9:4] */ +#define WM8904_WSEQ_CURRENT_INDEX_SHIFT 4 /* WSEQ_CURRENT_INDEX - [9:4] */ +#define WM8904_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [9:4] */ +#define WM8904_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */ +#define WM8904_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */ +#define WM8904_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */ +#define WM8904_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ + +/* + * R116 (0x74) - FLL Control 1 + */ +#define WM8904_FLL_FRACN_ENA 0x0004 /* FLL_FRACN_ENA */ +#define WM8904_FLL_FRACN_ENA_MASK 0x0004 /* FLL_FRACN_ENA */ +#define WM8904_FLL_FRACN_ENA_SHIFT 2 /* FLL_FRACN_ENA */ +#define WM8904_FLL_FRACN_ENA_WIDTH 1 /* FLL_FRACN_ENA */ +#define WM8904_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */ +#define WM8904_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */ +#define WM8904_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */ +#define WM8904_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */ +#define WM8904_FLL_ENA 0x0001 /* FLL_ENA */ +#define WM8904_FLL_ENA_MASK 0x0001 /* FLL_ENA */ +#define WM8904_FLL_ENA_SHIFT 0 /* FLL_ENA */ +#define WM8904_FLL_ENA_WIDTH 1 /* FLL_ENA */ + +/* + * R117 (0x75) - FLL Control 2 + */ +#define WM8904_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */ +#define WM8904_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */ +#define WM8904_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */ +#define WM8904_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */ +#define WM8904_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */ +#define WM8904_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */ +#define WM8904_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */ +#define WM8904_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */ +#define WM8904_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */ + +/* + * R118 (0x76) - FLL Control 3 + */ +#define WM8904_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */ +#define WM8904_FLL_K_SHIFT 0 /* FLL_K - [15:0] */ +#define WM8904_FLL_K_WIDTH 16 /* FLL_K - [15:0] */ + +/* + * R119 (0x77) - FLL Control 4 + */ +#define WM8904_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */ +#define WM8904_FLL_N_SHIFT 5 /* FLL_N - [14:5] */ +#define WM8904_FLL_N_WIDTH 10 /* FLL_N - [14:5] */ +#define WM8904_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */ +#define WM8904_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */ +#define WM8904_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */ + +/* + * R120 (0x78) - FLL Control 5 + */ +#define WM8904_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM8904_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM8904_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM8904_FLL_CLK_REF_SRC_MASK 0x0003 /* FLL_CLK_REF_SRC - [1:0] */ +#define WM8904_FLL_CLK_REF_SRC_SHIFT 0 /* FLL_CLK_REF_SRC - [1:0] */ +#define WM8904_FLL_CLK_REF_SRC_WIDTH 2 /* FLL_CLK_REF_SRC - [1:0] */ + +/* + * R126 (0x7E) - Digital Pulls + */ +#define WM8904_MCLK_PU 0x0080 /* MCLK_PU */ +#define WM8904_MCLK_PU_MASK 0x0080 /* MCLK_PU */ +#define WM8904_MCLK_PU_SHIFT 7 /* MCLK_PU */ +#define WM8904_MCLK_PU_WIDTH 1 /* MCLK_PU */ +#define WM8904_MCLK_PD 0x0040 /* MCLK_PD */ +#define WM8904_MCLK_PD_MASK 0x0040 /* MCLK_PD */ +#define WM8904_MCLK_PD_SHIFT 6 /* MCLK_PD */ +#define WM8904_MCLK_PD_WIDTH 1 /* MCLK_PD */ +#define WM8904_DACDAT_PU 0x0020 /* DACDAT_PU */ +#define WM8904_DACDAT_PU_MASK 0x0020 /* DACDAT_PU */ +#define WM8904_DACDAT_PU_SHIFT 5 /* DACDAT_PU */ +#define WM8904_DACDAT_PU_WIDTH 1 /* DACDAT_PU */ +#define WM8904_DACDAT_PD 0x0010 /* DACDAT_PD */ +#define WM8904_DACDAT_PD_MASK 0x0010 /* DACDAT_PD */ +#define WM8904_DACDAT_PD_SHIFT 4 /* DACDAT_PD */ +#define WM8904_DACDAT_PD_WIDTH 1 /* DACDAT_PD */ +#define WM8904_LRCLK_PU 0x0008 /* LRCLK_PU */ +#define WM8904_LRCLK_PU_MASK 0x0008 /* LRCLK_PU */ +#define WM8904_LRCLK_PU_SHIFT 3 /* LRCLK_PU */ +#define WM8904_LRCLK_PU_WIDTH 1 /* LRCLK_PU */ +#define WM8904_LRCLK_PD 0x0004 /* LRCLK_PD */ +#define WM8904_LRCLK_PD_MASK 0x0004 /* LRCLK_PD */ +#define WM8904_LRCLK_PD_SHIFT 2 /* LRCLK_PD */ +#define WM8904_LRCLK_PD_WIDTH 1 /* LRCLK_PD */ +#define WM8904_BCLK_PU 0x0002 /* BCLK_PU */ +#define WM8904_BCLK_PU_MASK 0x0002 /* BCLK_PU */ +#define WM8904_BCLK_PU_SHIFT 1 /* BCLK_PU */ +#define WM8904_BCLK_PU_WIDTH 1 /* BCLK_PU */ +#define WM8904_BCLK_PD 0x0001 /* BCLK_PD */ +#define WM8904_BCLK_PD_MASK 0x0001 /* BCLK_PD */ +#define WM8904_BCLK_PD_SHIFT 0 /* BCLK_PD */ +#define WM8904_BCLK_PD_WIDTH 1 /* BCLK_PD */ + +/* + * R127 (0x7F) - Interrupt Status + */ +#define WM8904_IRQ 0x0400 /* IRQ */ +#define WM8904_IRQ_MASK 0x0400 /* IRQ */ +#define WM8904_IRQ_SHIFT 10 /* IRQ */ +#define WM8904_IRQ_WIDTH 1 /* IRQ */ +#define WM8904_GPIO_BCLK_EINT 0x0200 /* GPIO_BCLK_EINT */ +#define WM8904_GPIO_BCLK_EINT_MASK 0x0200 /* GPIO_BCLK_EINT */ +#define WM8904_GPIO_BCLK_EINT_SHIFT 9 /* GPIO_BCLK_EINT */ +#define WM8904_GPIO_BCLK_EINT_WIDTH 1 /* GPIO_BCLK_EINT */ +#define WM8904_WSEQ_EINT 0x0100 /* WSEQ_EINT */ +#define WM8904_WSEQ_EINT_MASK 0x0100 /* WSEQ_EINT */ +#define WM8904_WSEQ_EINT_SHIFT 8 /* WSEQ_EINT */ +#define WM8904_WSEQ_EINT_WIDTH 1 /* WSEQ_EINT */ +#define WM8904_GPIO3_EINT 0x0080 /* GPIO3_EINT */ +#define WM8904_GPIO3_EINT_MASK 0x0080 /* GPIO3_EINT */ +#define WM8904_GPIO3_EINT_SHIFT 7 /* GPIO3_EINT */ +#define WM8904_GPIO3_EINT_WIDTH 1 /* GPIO3_EINT */ +#define WM8904_GPIO2_EINT 0x0040 /* GPIO2_EINT */ +#define WM8904_GPIO2_EINT_MASK 0x0040 /* GPIO2_EINT */ +#define WM8904_GPIO2_EINT_SHIFT 6 /* GPIO2_EINT */ +#define WM8904_GPIO2_EINT_WIDTH 1 /* GPIO2_EINT */ +#define WM8904_GPIO1_EINT 0x0020 /* GPIO1_EINT */ +#define WM8904_GPIO1_EINT_MASK 0x0020 /* GPIO1_EINT */ +#define WM8904_GPIO1_EINT_SHIFT 5 /* GPIO1_EINT */ +#define WM8904_GPIO1_EINT_WIDTH 1 /* GPIO1_EINT */ +#define WM8904_GPI8_EINT 0x0010 /* GPI8_EINT */ +#define WM8904_GPI8_EINT_MASK 0x0010 /* GPI8_EINT */ +#define WM8904_GPI8_EINT_SHIFT 4 /* GPI8_EINT */ +#define WM8904_GPI8_EINT_WIDTH 1 /* GPI8_EINT */ +#define WM8904_GPI7_EINT 0x0008 /* GPI7_EINT */ +#define WM8904_GPI7_EINT_MASK 0x0008 /* GPI7_EINT */ +#define WM8904_GPI7_EINT_SHIFT 3 /* GPI7_EINT */ +#define WM8904_GPI7_EINT_WIDTH 1 /* GPI7_EINT */ +#define WM8904_FLL_LOCK_EINT 0x0004 /* FLL_LOCK_EINT */ +#define WM8904_FLL_LOCK_EINT_MASK 0x0004 /* FLL_LOCK_EINT */ +#define WM8904_FLL_LOCK_EINT_SHIFT 2 /* FLL_LOCK_EINT */ +#define WM8904_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */ +#define WM8904_MIC_SHRT_EINT 0x0002 /* MIC_SHRT_EINT */ +#define WM8904_MIC_SHRT_EINT_MASK 0x0002 /* MIC_SHRT_EINT */ +#define WM8904_MIC_SHRT_EINT_SHIFT 1 /* MIC_SHRT_EINT */ +#define WM8904_MIC_SHRT_EINT_WIDTH 1 /* MIC_SHRT_EINT */ +#define WM8904_MIC_DET_EINT 0x0001 /* MIC_DET_EINT */ +#define WM8904_MIC_DET_EINT_MASK 0x0001 /* MIC_DET_EINT */ +#define WM8904_MIC_DET_EINT_SHIFT 0 /* MIC_DET_EINT */ +#define WM8904_MIC_DET_EINT_WIDTH 1 /* MIC_DET_EINT */ + +/* + * R128 (0x80) - Interrupt Status Mask + */ +#define WM8904_IM_GPIO_BCLK_EINT 0x0200 /* IM_GPIO_BCLK_EINT */ +#define WM8904_IM_GPIO_BCLK_EINT_MASK 0x0200 /* IM_GPIO_BCLK_EINT */ +#define WM8904_IM_GPIO_BCLK_EINT_SHIFT 9 /* IM_GPIO_BCLK_EINT */ +#define WM8904_IM_GPIO_BCLK_EINT_WIDTH 1 /* IM_GPIO_BCLK_EINT */ +#define WM8904_IM_WSEQ_EINT 0x0100 /* IM_WSEQ_EINT */ +#define WM8904_IM_WSEQ_EINT_MASK 0x0100 /* IM_WSEQ_EINT */ +#define WM8904_IM_WSEQ_EINT_SHIFT 8 /* IM_WSEQ_EINT */ +#define WM8904_IM_WSEQ_EINT_WIDTH 1 /* IM_WSEQ_EINT */ +#define WM8904_IM_GPIO3_EINT 0x0080 /* IM_GPIO3_EINT */ +#define WM8904_IM_GPIO3_EINT_MASK 0x0080 /* IM_GPIO3_EINT */ +#define WM8904_IM_GPIO3_EINT_SHIFT 7 /* IM_GPIO3_EINT */ +#define WM8904_IM_GPIO3_EINT_WIDTH 1 /* IM_GPIO3_EINT */ +#define WM8904_IM_GPIO2_EINT 0x0040 /* IM_GPIO2_EINT */ +#define WM8904_IM_GPIO2_EINT_MASK 0x0040 /* IM_GPIO2_EINT */ +#define WM8904_IM_GPIO2_EINT_SHIFT 6 /* IM_GPIO2_EINT */ +#define WM8904_IM_GPIO2_EINT_WIDTH 1 /* IM_GPIO2_EINT */ +#define WM8904_IM_GPIO1_EINT 0x0020 /* IM_GPIO1_EINT */ +#define WM8904_IM_GPIO1_EINT_MASK 0x0020 /* IM_GPIO1_EINT */ +#define WM8904_IM_GPIO1_EINT_SHIFT 5 /* IM_GPIO1_EINT */ +#define WM8904_IM_GPIO1_EINT_WIDTH 1 /* IM_GPIO1_EINT */ +#define WM8904_IM_GPI8_EINT 0x0010 /* IM_GPI8_EINT */ +#define WM8904_IM_GPI8_EINT_MASK 0x0010 /* IM_GPI8_EINT */ +#define WM8904_IM_GPI8_EINT_SHIFT 4 /* IM_GPI8_EINT */ +#define WM8904_IM_GPI8_EINT_WIDTH 1 /* IM_GPI8_EINT */ +#define WM8904_IM_GPI7_EINT 0x0008 /* IM_GPI7_EINT */ +#define WM8904_IM_GPI7_EINT_MASK 0x0008 /* IM_GPI7_EINT */ +#define WM8904_IM_GPI7_EINT_SHIFT 3 /* IM_GPI7_EINT */ +#define WM8904_IM_GPI7_EINT_WIDTH 1 /* IM_GPI7_EINT */ +#define WM8904_IM_FLL_LOCK_EINT 0x0004 /* IM_FLL_LOCK_EINT */ +#define WM8904_IM_FLL_LOCK_EINT_MASK 0x0004 /* IM_FLL_LOCK_EINT */ +#define WM8904_IM_FLL_LOCK_EINT_SHIFT 2 /* IM_FLL_LOCK_EINT */ +#define WM8904_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */ +#define WM8904_IM_MIC_SHRT_EINT 0x0002 /* IM_MIC_SHRT_EINT */ +#define WM8904_IM_MIC_SHRT_EINT_MASK 0x0002 /* IM_MIC_SHRT_EINT */ +#define WM8904_IM_MIC_SHRT_EINT_SHIFT 1 /* IM_MIC_SHRT_EINT */ +#define WM8904_IM_MIC_SHRT_EINT_WIDTH 1 /* IM_MIC_SHRT_EINT */ +#define WM8904_IM_MIC_DET_EINT 0x0001 /* IM_MIC_DET_EINT */ +#define WM8904_IM_MIC_DET_EINT_MASK 0x0001 /* IM_MIC_DET_EINT */ +#define WM8904_IM_MIC_DET_EINT_SHIFT 0 /* IM_MIC_DET_EINT */ +#define WM8904_IM_MIC_DET_EINT_WIDTH 1 /* IM_MIC_DET_EINT */ + +/* + * R129 (0x81) - Interrupt Polarity + */ +#define WM8904_GPIO_BCLK_EINT_POL 0x0200 /* GPIO_BCLK_EINT_POL */ +#define WM8904_GPIO_BCLK_EINT_POL_MASK 0x0200 /* GPIO_BCLK_EINT_POL */ +#define WM8904_GPIO_BCLK_EINT_POL_SHIFT 9 /* GPIO_BCLK_EINT_POL */ +#define WM8904_GPIO_BCLK_EINT_POL_WIDTH 1 /* GPIO_BCLK_EINT_POL */ +#define WM8904_WSEQ_EINT_POL 0x0100 /* WSEQ_EINT_POL */ +#define WM8904_WSEQ_EINT_POL_MASK 0x0100 /* WSEQ_EINT_POL */ +#define WM8904_WSEQ_EINT_POL_SHIFT 8 /* WSEQ_EINT_POL */ +#define WM8904_WSEQ_EINT_POL_WIDTH 1 /* WSEQ_EINT_POL */ +#define WM8904_GPIO3_EINT_POL 0x0080 /* GPIO3_EINT_POL */ +#define WM8904_GPIO3_EINT_POL_MASK 0x0080 /* GPIO3_EINT_POL */ +#define WM8904_GPIO3_EINT_POL_SHIFT 7 /* GPIO3_EINT_POL */ +#define WM8904_GPIO3_EINT_POL_WIDTH 1 /* GPIO3_EINT_POL */ +#define WM8904_GPIO2_EINT_POL 0x0040 /* GPIO2_EINT_POL */ +#define WM8904_GPIO2_EINT_POL_MASK 0x0040 /* GPIO2_EINT_POL */ +#define WM8904_GPIO2_EINT_POL_SHIFT 6 /* GPIO2_EINT_POL */ +#define WM8904_GPIO2_EINT_POL_WIDTH 1 /* GPIO2_EINT_POL */ +#define WM8904_GPIO1_EINT_POL 0x0020 /* GPIO1_EINT_POL */ +#define WM8904_GPIO1_EINT_POL_MASK 0x0020 /* GPIO1_EINT_POL */ +#define WM8904_GPIO1_EINT_POL_SHIFT 5 /* GPIO1_EINT_POL */ +#define WM8904_GPIO1_EINT_POL_WIDTH 1 /* GPIO1_EINT_POL */ +#define WM8904_GPI8_EINT_POL 0x0010 /* GPI8_EINT_POL */ +#define WM8904_GPI8_EINT_POL_MASK 0x0010 /* GPI8_EINT_POL */ +#define WM8904_GPI8_EINT_POL_SHIFT 4 /* GPI8_EINT_POL */ +#define WM8904_GPI8_EINT_POL_WIDTH 1 /* GPI8_EINT_POL */ +#define WM8904_GPI7_EINT_POL 0x0008 /* GPI7_EINT_POL */ +#define WM8904_GPI7_EINT_POL_MASK 0x0008 /* GPI7_EINT_POL */ +#define WM8904_GPI7_EINT_POL_SHIFT 3 /* GPI7_EINT_POL */ +#define WM8904_GPI7_EINT_POL_WIDTH 1 /* GPI7_EINT_POL */ +#define WM8904_FLL_LOCK_EINT_POL 0x0004 /* FLL_LOCK_EINT_POL */ +#define WM8904_FLL_LOCK_EINT_POL_MASK 0x0004 /* FLL_LOCK_EINT_POL */ +#define WM8904_FLL_LOCK_EINT_POL_SHIFT 2 /* FLL_LOCK_EINT_POL */ +#define WM8904_FLL_LOCK_EINT_POL_WIDTH 1 /* FLL_LOCK_EINT_POL */ +#define WM8904_MIC_SHRT_EINT_POL 0x0002 /* MIC_SHRT_EINT_POL */ +#define WM8904_MIC_SHRT_EINT_POL_MASK 0x0002 /* MIC_SHRT_EINT_POL */ +#define WM8904_MIC_SHRT_EINT_POL_SHIFT 1 /* MIC_SHRT_EINT_POL */ +#define WM8904_MIC_SHRT_EINT_POL_WIDTH 1 /* MIC_SHRT_EINT_POL */ +#define WM8904_MIC_DET_EINT_POL 0x0001 /* MIC_DET_EINT_POL */ +#define WM8904_MIC_DET_EINT_POL_MASK 0x0001 /* MIC_DET_EINT_POL */ +#define WM8904_MIC_DET_EINT_POL_SHIFT 0 /* MIC_DET_EINT_POL */ +#define WM8904_MIC_DET_EINT_POL_WIDTH 1 /* MIC_DET_EINT_POL */ + +/* + * R130 (0x82) - Interrupt Debounce + */ +#define WM8904_GPIO_BCLK_EINT_DB 0x0200 /* GPIO_BCLK_EINT_DB */ +#define WM8904_GPIO_BCLK_EINT_DB_MASK 0x0200 /* GPIO_BCLK_EINT_DB */ +#define WM8904_GPIO_BCLK_EINT_DB_SHIFT 9 /* GPIO_BCLK_EINT_DB */ +#define WM8904_GPIO_BCLK_EINT_DB_WIDTH 1 /* GPIO_BCLK_EINT_DB */ +#define WM8904_WSEQ_EINT_DB 0x0100 /* WSEQ_EINT_DB */ +#define WM8904_WSEQ_EINT_DB_MASK 0x0100 /* WSEQ_EINT_DB */ +#define WM8904_WSEQ_EINT_DB_SHIFT 8 /* WSEQ_EINT_DB */ +#define WM8904_WSEQ_EINT_DB_WIDTH 1 /* WSEQ_EINT_DB */ +#define WM8904_GPIO3_EINT_DB 0x0080 /* GPIO3_EINT_DB */ +#define WM8904_GPIO3_EINT_DB_MASK 0x0080 /* GPIO3_EINT_DB */ +#define WM8904_GPIO3_EINT_DB_SHIFT 7 /* GPIO3_EINT_DB */ +#define WM8904_GPIO3_EINT_DB_WIDTH 1 /* GPIO3_EINT_DB */ +#define WM8904_GPIO2_EINT_DB 0x0040 /* GPIO2_EINT_DB */ +#define WM8904_GPIO2_EINT_DB_MASK 0x0040 /* GPIO2_EINT_DB */ +#define WM8904_GPIO2_EINT_DB_SHIFT 6 /* GPIO2_EINT_DB */ +#define WM8904_GPIO2_EINT_DB_WIDTH 1 /* GPIO2_EINT_DB */ +#define WM8904_GPIO1_EINT_DB 0x0020 /* GPIO1_EINT_DB */ +#define WM8904_GPIO1_EINT_DB_MASK 0x0020 /* GPIO1_EINT_DB */ +#define WM8904_GPIO1_EINT_DB_SHIFT 5 /* GPIO1_EINT_DB */ +#define WM8904_GPIO1_EINT_DB_WIDTH 1 /* GPIO1_EINT_DB */ +#define WM8904_GPI8_EINT_DB 0x0010 /* GPI8_EINT_DB */ +#define WM8904_GPI8_EINT_DB_MASK 0x0010 /* GPI8_EINT_DB */ +#define WM8904_GPI8_EINT_DB_SHIFT 4 /* GPI8_EINT_DB */ +#define WM8904_GPI8_EINT_DB_WIDTH 1 /* GPI8_EINT_DB */ +#define WM8904_GPI7_EINT_DB 0x0008 /* GPI7_EINT_DB */ +#define WM8904_GPI7_EINT_DB_MASK 0x0008 /* GPI7_EINT_DB */ +#define WM8904_GPI7_EINT_DB_SHIFT 3 /* GPI7_EINT_DB */ +#define WM8904_GPI7_EINT_DB_WIDTH 1 /* GPI7_EINT_DB */ +#define WM8904_FLL_LOCK_EINT_DB 0x0004 /* FLL_LOCK_EINT_DB */ +#define WM8904_FLL_LOCK_EINT_DB_MASK 0x0004 /* FLL_LOCK_EINT_DB */ +#define WM8904_FLL_LOCK_EINT_DB_SHIFT 2 /* FLL_LOCK_EINT_DB */ +#define WM8904_FLL_LOCK_EINT_DB_WIDTH 1 /* FLL_LOCK_EINT_DB */ +#define WM8904_MIC_SHRT_EINT_DB 0x0002 /* MIC_SHRT_EINT_DB */ +#define WM8904_MIC_SHRT_EINT_DB_MASK 0x0002 /* MIC_SHRT_EINT_DB */ +#define WM8904_MIC_SHRT_EINT_DB_SHIFT 1 /* MIC_SHRT_EINT_DB */ +#define WM8904_MIC_SHRT_EINT_DB_WIDTH 1 /* MIC_SHRT_EINT_DB */ +#define WM8904_MIC_DET_EINT_DB 0x0001 /* MIC_DET_EINT_DB */ +#define WM8904_MIC_DET_EINT_DB_MASK 0x0001 /* MIC_DET_EINT_DB */ +#define WM8904_MIC_DET_EINT_DB_SHIFT 0 /* MIC_DET_EINT_DB */ +#define WM8904_MIC_DET_EINT_DB_WIDTH 1 /* MIC_DET_EINT_DB */ + +/* + * R134 (0x86) - EQ1 + */ +#define WM8904_EQ_ENA 0x0001 /* EQ_ENA */ +#define WM8904_EQ_ENA_MASK 0x0001 /* EQ_ENA */ +#define WM8904_EQ_ENA_SHIFT 0 /* EQ_ENA */ +#define WM8904_EQ_ENA_WIDTH 1 /* EQ_ENA */ + +/* + * R135 (0x87) - EQ2 + */ +#define WM8904_EQ_B1_GAIN_MASK 0x001F /* EQ_B1_GAIN - [4:0] */ +#define WM8904_EQ_B1_GAIN_SHIFT 0 /* EQ_B1_GAIN - [4:0] */ +#define WM8904_EQ_B1_GAIN_WIDTH 5 /* EQ_B1_GAIN - [4:0] */ + +/* + * R136 (0x88) - EQ3 + */ +#define WM8904_EQ_B2_GAIN_MASK 0x001F /* EQ_B2_GAIN - [4:0] */ +#define WM8904_EQ_B2_GAIN_SHIFT 0 /* EQ_B2_GAIN - [4:0] */ +#define WM8904_EQ_B2_GAIN_WIDTH 5 /* EQ_B2_GAIN - [4:0] */ + +/* + * R137 (0x89) - EQ4 + */ +#define WM8904_EQ_B3_GAIN_MASK 0x001F /* EQ_B3_GAIN - [4:0] */ +#define WM8904_EQ_B3_GAIN_SHIFT 0 /* EQ_B3_GAIN - [4:0] */ +#define WM8904_EQ_B3_GAIN_WIDTH 5 /* EQ_B3_GAIN - [4:0] */ + +/* + * R138 (0x8A) - EQ5 + */ +#define WM8904_EQ_B4_GAIN_MASK 0x001F /* EQ_B4_GAIN - [4:0] */ +#define WM8904_EQ_B4_GAIN_SHIFT 0 /* EQ_B4_GAIN - [4:0] */ +#define WM8904_EQ_B4_GAIN_WIDTH 5 /* EQ_B4_GAIN - [4:0] */ + +/* + * R139 (0x8B) - EQ6 + */ +#define WM8904_EQ_B5_GAIN_MASK 0x001F /* EQ_B5_GAIN - [4:0] */ +#define WM8904_EQ_B5_GAIN_SHIFT 0 /* EQ_B5_GAIN - [4:0] */ +#define WM8904_EQ_B5_GAIN_WIDTH 5 /* EQ_B5_GAIN - [4:0] */ + +/* + * R140 (0x8C) - EQ7 + */ +#define WM8904_EQ_B1_A_MASK 0xFFFF /* EQ_B1_A - [15:0] */ +#define WM8904_EQ_B1_A_SHIFT 0 /* EQ_B1_A - [15:0] */ +#define WM8904_EQ_B1_A_WIDTH 16 /* EQ_B1_A - [15:0] */ + +/* + * R141 (0x8D) - EQ8 + */ +#define WM8904_EQ_B1_B_MASK 0xFFFF /* EQ_B1_B - [15:0] */ +#define WM8904_EQ_B1_B_SHIFT 0 /* EQ_B1_B - [15:0] */ +#define WM8904_EQ_B1_B_WIDTH 16 /* EQ_B1_B - [15:0] */ + +/* + * R142 (0x8E) - EQ9 + */ +#define WM8904_EQ_B1_PG_MASK 0xFFFF /* EQ_B1_PG - [15:0] */ +#define WM8904_EQ_B1_PG_SHIFT 0 /* EQ_B1_PG - [15:0] */ +#define WM8904_EQ_B1_PG_WIDTH 16 /* EQ_B1_PG - [15:0] */ + +/* + * R143 (0x8F) - EQ10 + */ +#define WM8904_EQ_B2_A_MASK 0xFFFF /* EQ_B2_A - [15:0] */ +#define WM8904_EQ_B2_A_SHIFT 0 /* EQ_B2_A - [15:0] */ +#define WM8904_EQ_B2_A_WIDTH 16 /* EQ_B2_A - [15:0] */ + +/* + * R144 (0x90) - EQ11 + */ +#define WM8904_EQ_B2_B_MASK 0xFFFF /* EQ_B2_B - [15:0] */ +#define WM8904_EQ_B2_B_SHIFT 0 /* EQ_B2_B - [15:0] */ +#define WM8904_EQ_B2_B_WIDTH 16 /* EQ_B2_B - [15:0] */ + +/* + * R145 (0x91) - EQ12 + */ +#define WM8904_EQ_B2_C_MASK 0xFFFF /* EQ_B2_C - [15:0] */ +#define WM8904_EQ_B2_C_SHIFT 0 /* EQ_B2_C - [15:0] */ +#define WM8904_EQ_B2_C_WIDTH 16 /* EQ_B2_C - [15:0] */ + +/* + * R146 (0x92) - EQ13 + */ +#define WM8904_EQ_B2_PG_MASK 0xFFFF /* EQ_B2_PG - [15:0] */ +#define WM8904_EQ_B2_PG_SHIFT 0 /* EQ_B2_PG - [15:0] */ +#define WM8904_EQ_B2_PG_WIDTH 16 /* EQ_B2_PG - [15:0] */ + +/* + * R147 (0x93) - EQ14 + */ +#define WM8904_EQ_B3_A_MASK 0xFFFF /* EQ_B3_A - [15:0] */ +#define WM8904_EQ_B3_A_SHIFT 0 /* EQ_B3_A - [15:0] */ +#define WM8904_EQ_B3_A_WIDTH 16 /* EQ_B3_A - [15:0] */ + +/* + * R148 (0x94) - EQ15 + */ +#define WM8904_EQ_B3_B_MASK 0xFFFF /* EQ_B3_B - [15:0] */ +#define WM8904_EQ_B3_B_SHIFT 0 /* EQ_B3_B - [15:0] */ +#define WM8904_EQ_B3_B_WIDTH 16 /* EQ_B3_B - [15:0] */ + +/* + * R149 (0x95) - EQ16 + */ +#define WM8904_EQ_B3_C_MASK 0xFFFF /* EQ_B3_C - [15:0] */ +#define WM8904_EQ_B3_C_SHIFT 0 /* EQ_B3_C - [15:0] */ +#define WM8904_EQ_B3_C_WIDTH 16 /* EQ_B3_C - [15:0] */ + +/* + * R150 (0x96) - EQ17 + */ +#define WM8904_EQ_B3_PG_MASK 0xFFFF /* EQ_B3_PG - [15:0] */ +#define WM8904_EQ_B3_PG_SHIFT 0 /* EQ_B3_PG - [15:0] */ +#define WM8904_EQ_B3_PG_WIDTH 16 /* EQ_B3_PG - [15:0] */ + +/* + * R151 (0x97) - EQ18 + */ +#define WM8904_EQ_B4_A_MASK 0xFFFF /* EQ_B4_A - [15:0] */ +#define WM8904_EQ_B4_A_SHIFT 0 /* EQ_B4_A - [15:0] */ +#define WM8904_EQ_B4_A_WIDTH 16 /* EQ_B4_A - [15:0] */ + +/* + * R152 (0x98) - EQ19 + */ +#define WM8904_EQ_B4_B_MASK 0xFFFF /* EQ_B4_B - [15:0] */ +#define WM8904_EQ_B4_B_SHIFT 0 /* EQ_B4_B - [15:0] */ +#define WM8904_EQ_B4_B_WIDTH 16 /* EQ_B4_B - [15:0] */ + +/* + * R153 (0x99) - EQ20 + */ +#define WM8904_EQ_B4_C_MASK 0xFFFF /* EQ_B4_C - [15:0] */ +#define WM8904_EQ_B4_C_SHIFT 0 /* EQ_B4_C - [15:0] */ +#define WM8904_EQ_B4_C_WIDTH 16 /* EQ_B4_C - [15:0] */ + +/* + * R154 (0x9A) - EQ21 + */ +#define WM8904_EQ_B4_PG_MASK 0xFFFF /* EQ_B4_PG - [15:0] */ +#define WM8904_EQ_B4_PG_SHIFT 0 /* EQ_B4_PG - [15:0] */ +#define WM8904_EQ_B4_PG_WIDTH 16 /* EQ_B4_PG - [15:0] */ + +/* + * R155 (0x9B) - EQ22 + */ +#define WM8904_EQ_B5_A_MASK 0xFFFF /* EQ_B5_A - [15:0] */ +#define WM8904_EQ_B5_A_SHIFT 0 /* EQ_B5_A - [15:0] */ +#define WM8904_EQ_B5_A_WIDTH 16 /* EQ_B5_A - [15:0] */ + +/* + * R156 (0x9C) - EQ23 + */ +#define WM8904_EQ_B5_B_MASK 0xFFFF /* EQ_B5_B - [15:0] */ +#define WM8904_EQ_B5_B_SHIFT 0 /* EQ_B5_B - [15:0] */ +#define WM8904_EQ_B5_B_WIDTH 16 /* EQ_B5_B - [15:0] */ + +/* + * R157 (0x9D) - EQ24 + */ +#define WM8904_EQ_B5_PG_MASK 0xFFFF /* EQ_B5_PG - [15:0] */ +#define WM8904_EQ_B5_PG_SHIFT 0 /* EQ_B5_PG - [15:0] */ +#define WM8904_EQ_B5_PG_WIDTH 16 /* EQ_B5_PG - [15:0] */ + +/* + * R161 (0xA1) - Control Interface Test 1 + */ +#define WM8904_USER_KEY 0x0002 /* USER_KEY */ +#define WM8904_USER_KEY_MASK 0x0002 /* USER_KEY */ +#define WM8904_USER_KEY_SHIFT 1 /* USER_KEY */ +#define WM8904_USER_KEY_WIDTH 1 /* USER_KEY */ + +/* + * R198 (0xC6) - ADC Test 0 + */ +#define WM8904_ADC_128_OSR_TST_MODE 0x0004 /* ADC_128_OSR_TST_MODE */ +#define WM8904_ADC_128_OSR_TST_MODE_SHIFT 2 /* ADC_128_OSR_TST_MODE */ +#define WM8904_ADC_128_OSR_TST_MODE_WIDTH 1 /* ADC_128_OSR_TST_MODE */ +#define WM8904_ADC_BIASX1P5 0x0001 /* ADC_BIASX1P5 */ +#define WM8904_ADC_BIASX1P5_SHIFT 0 /* ADC_BIASX1P5 */ +#define WM8904_ADC_BIASX1P5_WIDTH 1 /* ADC_BIASX1P5 */ + +/* + * R204 (0xCC) - Analogue Output Bias 0 + */ +#define WM8904_PGA_BIAS_MASK 0x0070 /* PGA_BIAS - [6:4] */ +#define WM8904_PGA_BIAS_SHIFT 4 /* PGA_BIAS - [6:4] */ +#define WM8904_PGA_BIAS_WIDTH 3 /* PGA_BIAS - [6:4] */ + +/* + * R247 (0xF7) - FLL NCO Test 0 + */ +#define WM8904_FLL_FRC_NCO 0x0001 /* FLL_FRC_NCO */ +#define WM8904_FLL_FRC_NCO_MASK 0x0001 /* FLL_FRC_NCO */ +#define WM8904_FLL_FRC_NCO_SHIFT 0 /* FLL_FRC_NCO */ +#define WM8904_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */ + +/* + * R248 (0xF8) - FLL NCO Test 1 + */ +#define WM8904_FLL_FRC_NCO_VAL_MASK 0x003F /* FLL_FRC_NCO_VAL - [5:0] */ +#define WM8904_FLL_FRC_NCO_VAL_SHIFT 0 /* FLL_FRC_NCO_VAL - [5:0] */ +#define WM8904_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [5:0] */ + +#endif diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c new file mode 100644 index 0000000000..b9432f8b64 --- /dev/null +++ b/sound/soc/codecs/wm8940.c @@ -0,0 +1,871 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8940.c -- WM8940 ALSA Soc Audio driver + * + * Author: Jonathan Cameron + * + * Based on wm8510.c + * Copyright 2006 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * + * Not currently handled: + * Notch filter control + * AUXMode (inverting vs mixer) + * No means to obtain current gain if alc enabled. + * No use made of gpio + * Fast VMID discharge for power down + * Soft Start + * DLR and ALR Swaps not enabled + * Digital Sidetone not supported + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8940.h" + +struct wm8940_priv { + unsigned int mclk; + unsigned int fs; + + struct regmap *regmap; +}; + +static bool wm8940_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8940_SOFTRESET: + return true; + default: + return false; + } +} + +static bool wm8940_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8940_SOFTRESET: + case WM8940_POWER1: + case WM8940_POWER2: + case WM8940_POWER3: + case WM8940_IFACE: + case WM8940_COMPANDINGCTL: + case WM8940_CLOCK: + case WM8940_ADDCNTRL: + case WM8940_GPIO: + case WM8940_CTLINT: + case WM8940_DAC: + case WM8940_DACVOL: + case WM8940_ADC: + case WM8940_ADCVOL: + case WM8940_NOTCH1: + case WM8940_NOTCH2: + case WM8940_NOTCH3: + case WM8940_NOTCH4: + case WM8940_NOTCH5: + case WM8940_NOTCH6: + case WM8940_NOTCH7: + case WM8940_NOTCH8: + case WM8940_DACLIM1: + case WM8940_DACLIM2: + case WM8940_ALC1: + case WM8940_ALC2: + case WM8940_ALC3: + case WM8940_NOISEGATE: + case WM8940_PLLN: + case WM8940_PLLK1: + case WM8940_PLLK2: + case WM8940_PLLK3: + case WM8940_ALC4: + case WM8940_INPUTCTL: + case WM8940_PGAGAIN: + case WM8940_ADCBOOST: + case WM8940_OUTPUTCTL: + case WM8940_SPKMIX: + case WM8940_SPKVOL: + case WM8940_MONOMIX: + return true; + default: + return false; + } +} + +static const struct reg_default wm8940_reg_defaults[] = { + { 0x1, 0x0000 }, /* Power 1 */ + { 0x2, 0x0000 }, /* Power 2 */ + { 0x3, 0x0000 }, /* Power 3 */ + { 0x4, 0x0010 }, /* Interface Control */ + { 0x5, 0x0000 }, /* Companding Control */ + { 0x6, 0x0140 }, /* Clock Control */ + { 0x7, 0x0000 }, /* Additional Controls */ + { 0x8, 0x0000 }, /* GPIO Control */ + { 0x9, 0x0002 }, /* Auto Increment Control */ + { 0xa, 0x0000 }, /* DAC Control */ + { 0xb, 0x00FF }, /* DAC Volume */ + + { 0xe, 0x0100 }, /* ADC Control */ + { 0xf, 0x00FF }, /* ADC Volume */ + { 0x10, 0x0000 }, /* Notch Filter 1 Control 1 */ + { 0x11, 0x0000 }, /* Notch Filter 1 Control 2 */ + { 0x12, 0x0000 }, /* Notch Filter 2 Control 1 */ + { 0x13, 0x0000 }, /* Notch Filter 2 Control 2 */ + { 0x14, 0x0000 }, /* Notch Filter 3 Control 1 */ + { 0x15, 0x0000 }, /* Notch Filter 3 Control 2 */ + { 0x16, 0x0000 }, /* Notch Filter 4 Control 1 */ + { 0x17, 0x0000 }, /* Notch Filter 4 Control 2 */ + { 0x18, 0x0032 }, /* DAC Limit Control 1 */ + { 0x19, 0x0000 }, /* DAC Limit Control 2 */ + + { 0x20, 0x0038 }, /* ALC Control 1 */ + { 0x21, 0x000B }, /* ALC Control 2 */ + { 0x22, 0x0032 }, /* ALC Control 3 */ + { 0x23, 0x0000 }, /* Noise Gate */ + { 0x24, 0x0041 }, /* PLLN */ + { 0x25, 0x000C }, /* PLLK1 */ + { 0x26, 0x0093 }, /* PLLK2 */ + { 0x27, 0x00E9 }, /* PLLK3 */ + + { 0x2a, 0x0030 }, /* ALC Control 4 */ + + { 0x2c, 0x0002 }, /* Input Control */ + { 0x2d, 0x0050 }, /* PGA Gain */ + + { 0x2f, 0x0002 }, /* ADC Boost Control */ + + { 0x31, 0x0002 }, /* Output Control */ + { 0x32, 0x0000 }, /* Speaker Mixer Control */ + + { 0x36, 0x0079 }, /* Speaker Volume */ + + { 0x38, 0x0000 }, /* Mono Mixer Control */ +}; + +static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" }; +static SOC_ENUM_SINGLE_DECL(wm8940_adc_companding_enum, + WM8940_COMPANDINGCTL, 1, wm8940_companding); +static SOC_ENUM_SINGLE_DECL(wm8940_dac_companding_enum, + WM8940_COMPANDINGCTL, 3, wm8940_companding); + +static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"}; +static SOC_ENUM_SINGLE_DECL(wm8940_alc_mode_enum, + WM8940_ALC3, 8, wm8940_alc_mode_text); + +static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"}; +static SOC_ENUM_SINGLE_DECL(wm8940_mic_bias_level_enum, + WM8940_INPUTCTL, 8, wm8940_mic_bias_level_text); + +static const char *wm8940_filter_mode_text[] = {"Audio", "Application"}; +static SOC_ENUM_SINGLE_DECL(wm8940_filter_mode_enum, + WM8940_ADC, 7, wm8940_filter_mode_text); + +static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1); +static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0); +static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0); +static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0); +static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0); +static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0); +static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0); +static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1); +static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0); + +static const struct snd_kcontrol_new wm8940_snd_controls[] = { + SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL, + 6, 1, 0), + SOC_ENUM("DAC Companding", wm8940_dac_companding_enum), + SOC_ENUM("ADC Companding", wm8940_adc_companding_enum), + + SOC_ENUM("ALC Mode", wm8940_alc_mode_enum), + SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0), + SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1, + 3, 7, 1, wm8940_alc_max_tlv), + SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1, + 0, 7, 0, wm8940_alc_min_tlv), + SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2, + 0, 14, 0, wm8940_alc_tar_tlv), + SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0), + SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0), + SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0), + SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0), + SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE, + 3, 1, 0), + SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE, + 0, 7, 0), + + SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0), + SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0), + SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0), + SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2, + 4, 9, 1, wm8940_lim_thresh_tlv), + SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2, + 0, 12, 0, wm8940_lim_boost_tlv), + + SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0), + SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN, + 0, 63, 0, wm8940_pga_vol_tlv), + SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL, + 0, 255, 0, wm8940_adc_tlv), + SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL, + 0, 255, 0, wm8940_adc_tlv), + SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum), + SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST, + 8, 1, 0, wm8940_capture_boost_vol_tlv), + SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL, + 0, 63, 0, wm8940_spk_vol_tlv), + SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL, 6, 1, 1), + + SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL, + 8, 1, 1, wm8940_att_tlv), + SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0), + + SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1), + SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX, + 7, 1, 1, wm8940_att_tlv), + + SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0), + SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum), + SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0), + SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0), + SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0), + SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0), + SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0), + SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0), +}; + +static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0), + SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0), +}; + +static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1); +static const struct snd_kcontrol_new wm8940_input_boost_controls[] = { + SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1), + SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST, + 0, 7, 0, wm8940_boost_vol_tlv), + SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST, + 4, 7, 0, wm8940_boost_vol_tlv), +}; + +static const struct snd_kcontrol_new wm8940_micpga_controls[] = { + SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0), + SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0), + SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0, + &wm8940_speaker_mixer_controls[0], + ARRAY_SIZE(wm8940_speaker_mixer_controls)), + SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0, + &wm8940_mono_mixer_controls[0], + ARRAY_SIZE(wm8940_mono_mixer_controls)), + SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0), + + SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("MONOOUT"), + SND_SOC_DAPM_OUTPUT("SPKOUTP"), + SND_SOC_DAPM_OUTPUT("SPKOUTN"), + + SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0), + SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0), + SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0, + &wm8940_micpga_controls[0], + ARRAY_SIZE(wm8940_micpga_controls)), + SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0, + &wm8940_input_boost_controls[0], + ARRAY_SIZE(wm8940_input_boost_controls)), + SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0), + + SND_SOC_DAPM_INPUT("MICN"), + SND_SOC_DAPM_INPUT("MICP"), + SND_SOC_DAPM_INPUT("AUX"), +}; + +static const struct snd_soc_dapm_route wm8940_dapm_routes[] = { + /* Mono output mixer */ + {"Mono Mixer", "PCM Playback Switch", "DAC"}, + {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, + {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, + + /* Speaker output mixer */ + {"Speaker Mixer", "PCM Playback Switch", "DAC"}, + {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, + {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, + + /* Outputs */ + {"Mono Out", NULL, "Mono Mixer"}, + {"MONOOUT", NULL, "Mono Out"}, + {"SpkN Out", NULL, "Speaker Mixer"}, + {"SpkP Out", NULL, "Speaker Mixer"}, + {"SPKOUTN", NULL, "SpkN Out"}, + {"SPKOUTP", NULL, "SpkP Out"}, + + /* Microphone PGA */ + {"Mic PGA", "MICN Switch", "MICN"}, + {"Mic PGA", "MICP Switch", "MICP"}, + {"Mic PGA", "AUX Switch", "AUX"}, + + /* Boost Mixer */ + {"Boost Mixer", "Mic PGA Switch", "Mic PGA"}, + {"Boost Mixer", "Mic Volume", "MICP"}, + {"Boost Mixer", "Aux Volume", "Aux Input"}, + + {"ADC", NULL, "Boost Mixer"}, +}; + +#define wm8940_reset(c) snd_soc_component_write(c, WM8940_SOFTRESET, 0); + +static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFE67; + u16 clk = snd_soc_component_read(component, WM8940_CLOCK) & 0x1fe; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + clk |= 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + snd_soc_component_write(component, WM8940_CLOCK, clk); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= (2 << 3); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= (1 << 3); + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= (3 << 3); + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= (3 << 3) | (1 << 7); + break; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= (1 << 7); + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= (1 << 8); + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= (1 << 8) | (1 << 7); + break; + } + + snd_soc_component_write(component, WM8940_IFACE, iface); + + return 0; +} + +static int wm8940_update_clocks(struct snd_soc_dai *dai); +static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8940_priv *priv = snd_soc_component_get_drvdata(component); + u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFD9F; + u16 addcntrl = snd_soc_component_read(component, WM8940_ADDCNTRL) & 0xFFF1; + u16 companding = snd_soc_component_read(component, + WM8940_COMPANDINGCTL) & 0xFFDF; + int ret; + + priv->fs = params_rate(params); + ret = wm8940_update_clocks(dai); + if (ret) + return ret; + + /* LoutR control */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE + && params_channels(params) == 2) + iface |= (1 << 9); + + switch (params_rate(params)) { + case 8000: + addcntrl |= (0x5 << 1); + break; + case 11025: + addcntrl |= (0x4 << 1); + break; + case 16000: + addcntrl |= (0x3 << 1); + break; + case 22050: + addcntrl |= (0x2 << 1); + break; + case 32000: + addcntrl |= (0x1 << 1); + break; + case 44100: + case 48000: + break; + } + ret = snd_soc_component_write(component, WM8940_ADDCNTRL, addcntrl); + if (ret) + goto error_ret; + + switch (params_width(params)) { + case 8: + companding = companding | (1 << 5); + break; + case 16: + break; + case 20: + iface |= (1 << 5); + break; + case 24: + iface |= (2 << 5); + break; + case 32: + iface |= (3 << 5); + break; + } + ret = snd_soc_component_write(component, WM8940_COMPANDINGCTL, companding); + if (ret) + goto error_ret; + ret = snd_soc_component_write(component, WM8940_IFACE, iface); + +error_ret: + return ret; +} + +static int wm8940_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, WM8940_DAC) & 0xffbf; + + if (mute) + mute_reg |= 0x40; + + return snd_soc_component_write(component, WM8940_DAC, mute_reg); +} + +static int wm8940_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8940_priv *wm8940 = snd_soc_component_get_drvdata(component); + u16 val; + u16 pwr_reg = snd_soc_component_read(component, WM8940_POWER1) & 0x1F0; + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + /* ensure bufioen and biasen */ + pwr_reg |= (1 << 2) | (1 << 3); + /* Enable thermal shutdown */ + val = snd_soc_component_read(component, WM8940_OUTPUTCTL); + ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, val | 0x2); + if (ret) + break; + /* set vmid to 75k */ + ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x1); + break; + case SND_SOC_BIAS_PREPARE: + /* ensure bufioen and biasen */ + pwr_reg |= (1 << 2) | (1 << 3); + ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x1); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regcache_sync(wm8940->regmap); + if (ret < 0) { + dev_err(component->dev, "Failed to sync cache: %d\n", ret); + return ret; + } + } + + /* ensure bufioen and biasen */ + pwr_reg |= (1 << 2) | (1 << 3); + /* set vmid to 300k for standby */ + ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg | 0x2); + break; + case SND_SOC_BIAS_OFF: + ret = snd_soc_component_write(component, WM8940_POWER1, pwr_reg); + break; + } + + return ret; +} + +struct pll_ { + unsigned int pre_scale:2; + unsigned int n:4; + unsigned int k; +}; + +static struct pll_ pll_div; + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 24) * 10) +static void pll_factors(unsigned int target, unsigned int source) +{ + unsigned long long Kpart; + unsigned int K, Ndiv, Nmod; + /* The left shift ist to avoid accuracy loss when right shifting */ + Ndiv = target / source; + + if (Ndiv > 12) { + source <<= 1; + /* Multiply by 2 */ + pll_div.pre_scale = 0; + Ndiv = target / source; + } else if (Ndiv < 3) { + source >>= 2; + /* Divide by 4 */ + pll_div.pre_scale = 3; + Ndiv = target / source; + } else if (Ndiv < 6) { + source >>= 1; + /* divide by 2 */ + pll_div.pre_scale = 2; + Ndiv = target / source; + } else + pll_div.pre_scale = 1; + + if ((Ndiv < 6) || (Ndiv > 12)) + printk(KERN_WARNING + "WM8940 N value %d outwith recommended range!d\n", + Ndiv); + + pll_div.n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div.k = K; +} + +/* Untested at the moment */ +static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + + /* Turn off PLL */ + reg = snd_soc_component_read(component, WM8940_POWER1); + snd_soc_component_write(component, WM8940_POWER1, reg & 0x1df); + + if (freq_in == 0 || freq_out == 0) { + /* Clock CODEC directly from MCLK */ + reg = snd_soc_component_read(component, WM8940_CLOCK); + snd_soc_component_write(component, WM8940_CLOCK, reg & 0x0ff); + /* Pll power down */ + snd_soc_component_write(component, WM8940_PLLN, (1 << 7)); + return 0; + } + + /* Pll is followed by a frequency divide by 4 */ + pll_factors(freq_out*4, freq_in); + if (pll_div.k) + snd_soc_component_write(component, WM8940_PLLN, + (pll_div.pre_scale << 4) | pll_div.n | (1 << 6)); + else /* No factional component */ + snd_soc_component_write(component, WM8940_PLLN, + (pll_div.pre_scale << 4) | pll_div.n); + snd_soc_component_write(component, WM8940_PLLK1, pll_div.k >> 18); + snd_soc_component_write(component, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff); + snd_soc_component_write(component, WM8940_PLLK3, pll_div.k & 0x1ff); + /* Enable the PLL */ + reg = snd_soc_component_read(component, WM8940_POWER1); + snd_soc_component_write(component, WM8940_POWER1, reg | 0x020); + + /* Run CODEC from PLL instead of MCLK */ + reg = snd_soc_component_read(component, WM8940_CLOCK); + snd_soc_component_write(component, WM8940_CLOCK, reg | 0x100); + + return 0; +} + +static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + int ret = 0; + + switch (div_id) { + case WM8940_BCLKDIV: + reg = snd_soc_component_read(component, WM8940_CLOCK) & 0xFFE3; + ret = snd_soc_component_write(component, WM8940_CLOCK, reg | (div << 2)); + break; + case WM8940_MCLKDIV: + reg = snd_soc_component_read(component, WM8940_CLOCK) & 0xFF1F; + ret = snd_soc_component_write(component, WM8940_CLOCK, reg | (div << 5)); + break; + case WM8940_OPCLKDIV: + reg = snd_soc_component_read(component, WM8940_GPIO) & 0xFFCF; + ret = snd_soc_component_write(component, WM8940_GPIO, reg | (div << 4)); + break; + } + return ret; +} + +static unsigned int wm8940_get_mclkdiv(unsigned int f_in, unsigned int f_out, + int *mclkdiv) +{ + unsigned int ratio = 2 * f_in / f_out; + + if (ratio <= 2) { + *mclkdiv = WM8940_MCLKDIV_1; + ratio = 2; + } else if (ratio == 3) { + *mclkdiv = WM8940_MCLKDIV_1_5; + } else if (ratio == 4) { + *mclkdiv = WM8940_MCLKDIV_2; + } else if (ratio <= 6) { + *mclkdiv = WM8940_MCLKDIV_3; + ratio = 6; + } else if (ratio <= 8) { + *mclkdiv = WM8940_MCLKDIV_4; + ratio = 8; + } else if (ratio <= 12) { + *mclkdiv = WM8940_MCLKDIV_6; + ratio = 12; + } else if (ratio <= 16) { + *mclkdiv = WM8940_MCLKDIV_8; + ratio = 16; + } else { + *mclkdiv = WM8940_MCLKDIV_12; + ratio = 24; + } + + return f_out * ratio / 2; +} + +static int wm8940_update_clocks(struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec); + unsigned int fs256; + unsigned int fpll = 0; + unsigned int f; + int mclkdiv; + + if (!priv->mclk || !priv->fs) + return 0; + + fs256 = 256 * priv->fs; + + f = wm8940_get_mclkdiv(priv->mclk, fs256, &mclkdiv); + if (f != priv->mclk) { + /* The PLL performs best around 90MHz */ + fpll = wm8940_get_mclkdiv(22500000, fs256, &mclkdiv); + } + + wm8940_set_dai_pll(dai, 0, 0, priv->mclk, fpll); + wm8940_set_dai_clkdiv(dai, WM8940_MCLKDIV, mclkdiv); + + return 0; +} + +static int wm8940_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *codec = dai->component; + struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec); + + if (dir != SND_SOC_CLOCK_IN) + return -EINVAL; + + priv->mclk = freq; + + return wm8940_update_clocks(dai); +} + +#define WM8940_RATES SNDRV_PCM_RATE_8000_48000 + +#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8940_dai_ops = { + .hw_params = wm8940_i2s_hw_params, + .set_sysclk = wm8940_set_dai_sysclk, + .mute_stream = wm8940_mute, + .set_fmt = wm8940_set_dai_fmt, + .set_clkdiv = wm8940_set_dai_clkdiv, + .set_pll = wm8940_set_dai_pll, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8940_dai = { + .name = "wm8940-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8940_RATES, + .formats = WM8940_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8940_RATES, + .formats = WM8940_FORMATS, + }, + .ops = &wm8940_dai_ops, + .symmetric_rate = 1, +}; + +static int wm8940_probe(struct snd_soc_component *component) +{ + struct wm8940_setup_data *pdata = component->dev->platform_data; + int ret; + u16 reg; + + /* + * Check chip ID for wm8940 - value of 0x00 offset + * SOFTWARE_RESET on write + * CHIP_ID on read + */ + reg = snd_soc_component_read(component, WM8940_SOFTRESET); + if (reg != WM8940_CHIP_ID) { + dev_err(component->dev, "Wrong wm8940 chip ID: 0x%x\n", reg); + return -ENODEV; + } + + ret = wm8940_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset\n"); + return ret; + } + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + ret = snd_soc_component_write(component, WM8940_POWER1, 0x180); + if (ret < 0) + return ret; + + if (pdata) { + reg = snd_soc_component_read(component, WM8940_OUTPUTCTL); + ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, reg | pdata->vroi); + if (ret < 0) + return ret; + } + + return ret; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8940 = { + .probe = wm8940_probe, + .set_bias_level = wm8940_set_bias_level, + .controls = wm8940_snd_controls, + .num_controls = ARRAY_SIZE(wm8940_snd_controls), + .dapm_widgets = wm8940_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8940_dapm_widgets), + .dapm_routes = wm8940_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8940_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8940_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM8940_MONOMIX, + .reg_defaults = wm8940_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .readable_reg = wm8940_readable_register, + .volatile_reg = wm8940_volatile_register, +}; + +static int wm8940_i2c_probe(struct i2c_client *i2c) +{ + struct wm8940_priv *wm8940; + int ret; + + wm8940 = devm_kzalloc(&i2c->dev, sizeof(struct wm8940_priv), + GFP_KERNEL); + if (wm8940 == NULL) + return -ENOMEM; + + wm8940->regmap = devm_regmap_init_i2c(i2c, &wm8940_regmap); + if (IS_ERR(wm8940->regmap)) + return PTR_ERR(wm8940->regmap); + + i2c_set_clientdata(i2c, wm8940); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8940, &wm8940_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8940_i2c_id[] = { + { "wm8940", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id); + +static const struct of_device_id wm8940_of_match[] = { + { .compatible = "wlf,wm8940", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8940_of_match); + +static struct i2c_driver wm8940_i2c_driver = { + .driver = { + .name = "wm8940", + .of_match_table = wm8940_of_match, + }, + .probe = wm8940_i2c_probe, + .id_table = wm8940_i2c_id, +}; + +module_i2c_driver(wm8940_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8940 driver"); +MODULE_AUTHOR("Jonathan Cameron"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8940.h b/sound/soc/codecs/wm8940.h new file mode 100644 index 0000000000..86bbf902ef --- /dev/null +++ b/sound/soc/codecs/wm8940.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8940.h -- WM8940 Soc Audio driver + */ + +#ifndef _WM8940_H +#define _WM8940_H + +struct wm8940_setup_data { + /* Vref to analogue output resistance */ +#define WM8940_VROI_1K 0 +#define WM8940_VROI_30K 1 + unsigned int vroi:1; +}; + +/* WM8940 register space */ +#define WM8940_SOFTRESET 0x00 +#define WM8940_POWER1 0x01 +#define WM8940_POWER2 0x02 +#define WM8940_POWER3 0x03 +#define WM8940_IFACE 0x04 +#define WM8940_COMPANDINGCTL 0x05 +#define WM8940_CLOCK 0x06 +#define WM8940_ADDCNTRL 0x07 +#define WM8940_GPIO 0x08 +#define WM8940_CTLINT 0x09 +#define WM8940_DAC 0x0A +#define WM8940_DACVOL 0x0B + +#define WM8940_ADC 0x0E +#define WM8940_ADCVOL 0x0F +#define WM8940_NOTCH1 0x10 +#define WM8940_NOTCH2 0x11 +#define WM8940_NOTCH3 0x12 +#define WM8940_NOTCH4 0x13 +#define WM8940_NOTCH5 0x14 +#define WM8940_NOTCH6 0x15 +#define WM8940_NOTCH7 0x16 +#define WM8940_NOTCH8 0x17 +#define WM8940_DACLIM1 0x18 +#define WM8940_DACLIM2 0x19 + +#define WM8940_ALC1 0x20 +#define WM8940_ALC2 0x21 +#define WM8940_ALC3 0x22 +#define WM8940_NOISEGATE 0x23 +#define WM8940_PLLN 0x24 +#define WM8940_PLLK1 0x25 +#define WM8940_PLLK2 0x26 +#define WM8940_PLLK3 0x27 + +#define WM8940_ALC4 0x2A + +#define WM8940_INPUTCTL 0x2C +#define WM8940_PGAGAIN 0x2D + +#define WM8940_ADCBOOST 0x2F + +#define WM8940_OUTPUTCTL 0x31 +#define WM8940_SPKMIX 0x32 + +#define WM8940_SPKVOL 0x36 + +#define WM8940_MONOMIX 0x38 + +#define WM8940_CACHEREGNUM 0x57 + + +/* Clock divider Id's */ +#define WM8940_BCLKDIV 0 +#define WM8940_MCLKDIV 1 +#define WM8940_OPCLKDIV 2 + +/* MCLK clock dividers */ +#define WM8940_MCLKDIV_1 0 +#define WM8940_MCLKDIV_1_5 1 +#define WM8940_MCLKDIV_2 2 +#define WM8940_MCLKDIV_3 3 +#define WM8940_MCLKDIV_4 4 +#define WM8940_MCLKDIV_6 5 +#define WM8940_MCLKDIV_8 6 +#define WM8940_MCLKDIV_12 7 + +/* BCLK clock dividers */ +#define WM8940_BCLKDIV_1 0 +#define WM8940_BCLKDIV_2 1 +#define WM8940_BCLKDIV_4 2 +#define WM8940_BCLKDIV_8 3 +#define WM8940_BCLKDIV_16 4 +#define WM8940_BCLKDIV_32 5 + +/* PLL Out Dividers */ +#define WM8940_OPCLKDIV_1 0 +#define WM8940_OPCLKDIV_2 1 +#define WM8940_OPCLKDIV_3 2 +#define WM8940_OPCLKDIV_4 3 + +/* Chip ID */ +#define WM8940_CHIP_ID 0x8940 + +#endif /* _WM8940_H */ + diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c new file mode 100644 index 0000000000..4f43383264 --- /dev/null +++ b/sound/soc/codecs/wm8955.c @@ -0,0 +1,1014 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8955.c -- WM8955 ALSA SoC Audio driver + * + * Copyright 2009 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8955.h" + +#define WM8955_NUM_SUPPLIES 4 +static const char *wm8955_supply_names[WM8955_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "HPVDD", + "AVDD", +}; + +/* codec private data */ +struct wm8955_priv { + struct regmap *regmap; + + unsigned int mclk_rate; + + int deemph; + int fs; + + struct regulator_bulk_data supplies[WM8955_NUM_SUPPLIES]; +}; + +static const struct reg_default wm8955_reg_defaults[] = { + { 2, 0x0079 }, /* R2 - LOUT1 volume */ + { 3, 0x0079 }, /* R3 - ROUT1 volume */ + { 5, 0x0008 }, /* R5 - DAC Control */ + { 7, 0x000A }, /* R7 - Audio Interface */ + { 8, 0x0000 }, /* R8 - Sample Rate */ + { 10, 0x00FF }, /* R10 - Left DAC volume */ + { 11, 0x00FF }, /* R11 - Right DAC volume */ + { 12, 0x000F }, /* R12 - Bass control */ + { 13, 0x000F }, /* R13 - Treble control */ + { 23, 0x00C1 }, /* R23 - Additional control (1) */ + { 24, 0x0000 }, /* R24 - Additional control (2) */ + { 25, 0x0000 }, /* R25 - Power Management (1) */ + { 26, 0x0000 }, /* R26 - Power Management (2) */ + { 27, 0x0000 }, /* R27 - Additional Control (3) */ + { 34, 0x0050 }, /* R34 - Left out Mix (1) */ + { 35, 0x0050 }, /* R35 - Left out Mix (2) */ + { 36, 0x0050 }, /* R36 - Right out Mix (1) */ + { 37, 0x0050 }, /* R37 - Right Out Mix (2) */ + { 38, 0x0050 }, /* R38 - Mono out Mix (1) */ + { 39, 0x0050 }, /* R39 - Mono out Mix (2) */ + { 40, 0x0079 }, /* R40 - LOUT2 volume */ + { 41, 0x0079 }, /* R41 - ROUT2 volume */ + { 42, 0x0079 }, /* R42 - MONOOUT volume */ + { 43, 0x0000 }, /* R43 - Clocking / PLL */ + { 44, 0x0103 }, /* R44 - PLL Control 1 */ + { 45, 0x0024 }, /* R45 - PLL Control 2 */ + { 46, 0x01BA }, /* R46 - PLL Control 3 */ + { 59, 0x0000 }, /* R59 - PLL Control 4 */ +}; + +static bool wm8955_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8955_LOUT1_VOLUME: + case WM8955_ROUT1_VOLUME: + case WM8955_DAC_CONTROL: + case WM8955_AUDIO_INTERFACE: + case WM8955_SAMPLE_RATE: + case WM8955_LEFT_DAC_VOLUME: + case WM8955_RIGHT_DAC_VOLUME: + case WM8955_BASS_CONTROL: + case WM8955_TREBLE_CONTROL: + case WM8955_RESET: + case WM8955_ADDITIONAL_CONTROL_1: + case WM8955_ADDITIONAL_CONTROL_2: + case WM8955_POWER_MANAGEMENT_1: + case WM8955_POWER_MANAGEMENT_2: + case WM8955_ADDITIONAL_CONTROL_3: + case WM8955_LEFT_OUT_MIX_1: + case WM8955_LEFT_OUT_MIX_2: + case WM8955_RIGHT_OUT_MIX_1: + case WM8955_RIGHT_OUT_MIX_2: + case WM8955_MONO_OUT_MIX_1: + case WM8955_MONO_OUT_MIX_2: + case WM8955_LOUT2_VOLUME: + case WM8955_ROUT2_VOLUME: + case WM8955_MONOOUT_VOLUME: + case WM8955_CLOCKING_PLL: + case WM8955_PLL_CONTROL_1: + case WM8955_PLL_CONTROL_2: + case WM8955_PLL_CONTROL_3: + case WM8955_PLL_CONTROL_4: + return true; + default: + return false; + } +} + +static bool wm8955_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8955_RESET: + return true; + default: + return false; + } +} + +static int wm8955_reset(struct snd_soc_component *component) +{ + return snd_soc_component_write(component, WM8955_RESET, 0); +} + +struct pll_factors { + int n; + int k; + int outdiv; +}; + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 22) * 10) + +static int wm8955_pll_factors(struct device *dev, + int Fref, int Fout, struct pll_factors *pll) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, target; + + dev_dbg(dev, "Fref=%u Fout=%u\n", Fref, Fout); + + /* The oscilator should run at should be 90-100MHz, and + * there's a divide by 4 plus an optional divide by 2 in the + * output path to generate the system clock. The clock table + * is sortd so we should always generate a suitable target. */ + target = Fout * 4; + if (target < 90000000) { + pll->outdiv = 1; + target *= 2; + } else { + pll->outdiv = 0; + } + + WARN_ON(target < 90000000 || target > 100000000); + + dev_dbg(dev, "Fvco=%dHz\n", target); + + /* Now, calculate N.K */ + Ndiv = target / Fref; + + pll->n = Ndiv; + Nmod = target % Fref; + dev_dbg(dev, "Nmod=%d\n", Nmod); + + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, Fref); + + K = Kpart & 0xFFFFFFFF; + + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + pll->k = K / 10; + + dev_dbg(dev, "N=%x K=%x OUTDIV=%x\n", pll->n, pll->k, pll->outdiv); + + return 0; +} + +/* Lookup table specifying SRATE (table 25 in datasheet); some of the + * output frequencies have been rounded to the standard frequencies + * they are intended to match where the error is slight. */ +static struct { + int mclk; + int fs; + int usb; + int sr; +} clock_cfgs[] = { + { 18432000, 8000, 0, 3, }, + { 18432000, 12000, 0, 9, }, + { 18432000, 16000, 0, 11, }, + { 18432000, 24000, 0, 29, }, + { 18432000, 32000, 0, 13, }, + { 18432000, 48000, 0, 1, }, + { 18432000, 96000, 0, 15, }, + + { 16934400, 8018, 0, 19, }, + { 16934400, 11025, 0, 25, }, + { 16934400, 22050, 0, 27, }, + { 16934400, 44100, 0, 17, }, + { 16934400, 88200, 0, 31, }, + + { 12000000, 8000, 1, 2, }, + { 12000000, 11025, 1, 25, }, + { 12000000, 12000, 1, 8, }, + { 12000000, 16000, 1, 10, }, + { 12000000, 22050, 1, 27, }, + { 12000000, 24000, 1, 28, }, + { 12000000, 32000, 1, 12, }, + { 12000000, 44100, 1, 17, }, + { 12000000, 48000, 1, 0, }, + { 12000000, 88200, 1, 31, }, + { 12000000, 96000, 1, 14, }, + + { 12288000, 8000, 0, 2, }, + { 12288000, 12000, 0, 8, }, + { 12288000, 16000, 0, 10, }, + { 12288000, 24000, 0, 28, }, + { 12288000, 32000, 0, 12, }, + { 12288000, 48000, 0, 0, }, + { 12288000, 96000, 0, 14, }, + + { 12289600, 8018, 0, 18, }, + { 12289600, 11025, 0, 24, }, + { 12289600, 22050, 0, 26, }, + { 11289600, 44100, 0, 16, }, + { 11289600, 88200, 0, 31, }, +}; + +static int wm8955_configure_clocking(struct snd_soc_component *component) +{ + struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component); + int i, ret, val; + int clocking = 0; + int srate = 0; + int sr = -1; + struct pll_factors pll; + + /* If we're not running a sample rate currently just pick one */ + if (wm8955->fs == 0) + wm8955->fs = 8000; + + /* Can we generate an exact output? */ + for (i = 0; i < ARRAY_SIZE(clock_cfgs); i++) { + if (wm8955->fs != clock_cfgs[i].fs) + continue; + sr = i; + + if (wm8955->mclk_rate == clock_cfgs[i].mclk) + break; + } + + /* We should never get here with an unsupported sample rate */ + if (sr == -1) { + dev_err(component->dev, "Sample rate %dHz unsupported\n", + wm8955->fs); + WARN_ON(sr == -1); + return -EINVAL; + } + + if (i == ARRAY_SIZE(clock_cfgs)) { + /* If we can't generate the right clock from MCLK then + * we should configure the PLL to supply us with an + * appropriate clock. + */ + clocking |= WM8955_MCLKSEL; + + /* Use the last divider configuration we saw for the + * sample rate. */ + ret = wm8955_pll_factors(component->dev, wm8955->mclk_rate, + clock_cfgs[sr].mclk, &pll); + if (ret != 0) { + dev_err(component->dev, + "Unable to generate %dHz from %dHz MCLK\n", + wm8955->fs, wm8955->mclk_rate); + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8955_PLL_CONTROL_1, + WM8955_N_MASK | WM8955_K_21_18_MASK, + (pll.n << WM8955_N_SHIFT) | + pll.k >> 18); + snd_soc_component_update_bits(component, WM8955_PLL_CONTROL_2, + WM8955_K_17_9_MASK, + (pll.k >> 9) & WM8955_K_17_9_MASK); + snd_soc_component_update_bits(component, WM8955_PLL_CONTROL_3, + WM8955_K_8_0_MASK, + pll.k & WM8955_K_8_0_MASK); + if (pll.k) + snd_soc_component_update_bits(component, WM8955_PLL_CONTROL_4, + WM8955_KEN, WM8955_KEN); + else + snd_soc_component_update_bits(component, WM8955_PLL_CONTROL_4, + WM8955_KEN, 0); + + if (pll.outdiv) + val = WM8955_PLL_RB | WM8955_PLLOUTDIV2; + else + val = WM8955_PLL_RB; + + /* Now start the PLL running */ + snd_soc_component_update_bits(component, WM8955_CLOCKING_PLL, + WM8955_PLL_RB | WM8955_PLLOUTDIV2, val); + snd_soc_component_update_bits(component, WM8955_CLOCKING_PLL, + WM8955_PLLEN, WM8955_PLLEN); + } + + srate = clock_cfgs[sr].usb | (clock_cfgs[sr].sr << WM8955_SR_SHIFT); + + snd_soc_component_update_bits(component, WM8955_SAMPLE_RATE, + WM8955_USB | WM8955_SR_MASK, srate); + snd_soc_component_update_bits(component, WM8955_CLOCKING_PLL, + WM8955_MCLKSEL, clocking); + + return 0; +} + +static int wm8955_sysclk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int ret = 0; + + /* Always disable the clocks - if we're doing reconfiguration this + * avoids misclocking. + */ + snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1, + WM8955_DIGENB, 0); + snd_soc_component_update_bits(component, WM8955_CLOCKING_PLL, + WM8955_PLL_RB | WM8955_PLLEN, 0); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + break; + case SND_SOC_DAPM_PRE_PMU: + ret = wm8955_configure_clocking(component); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int deemph_settings[] = { 0, 32000, 44100, 48000 }; + +static int wm8955_set_deemph(struct snd_soc_component *component) +{ + struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8955->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { + if (abs(deemph_settings[i] - wm8955->fs) < + abs(deemph_settings[best] - wm8955->fs)) + best = i; + } + + val = best << WM8955_DEEMPH_SHIFT; + } else { + val = 0; + } + + dev_dbg(component->dev, "Set deemphasis %d\n", val); + + return snd_soc_component_update_bits(component, WM8955_DAC_CONTROL, + WM8955_DEEMPH_MASK, val); +} + +static int wm8955_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wm8955->deemph; + return 0; +} + +static int wm8955_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; + + if (deemph > 1) + return -EINVAL; + + wm8955->deemph = deemph; + + return wm8955_set_deemph(component); +} + +static const char *bass_mode_text[] = { + "Linear", "Adaptive", +}; + +static SOC_ENUM_SINGLE_DECL(bass_mode, WM8955_BASS_CONTROL, 7, bass_mode_text); + +static const char *bass_cutoff_text[] = { + "Low", "High" +}; + +static SOC_ENUM_SINGLE_DECL(bass_cutoff, WM8955_BASS_CONTROL, 6, + bass_cutoff_text); + +static const char *treble_cutoff_text[] = { + "High", "Low" +}; + +static SOC_ENUM_SINGLE_DECL(treble_cutoff, WM8955_TREBLE_CONTROL, 2, + treble_cutoff_text); + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(atten_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(mono_tlv, -2100, 300, 0); +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(treble_tlv, -1200, 150, 1); + +static const struct snd_kcontrol_new wm8955_snd_controls[] = { +SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8955_LEFT_DAC_VOLUME, + WM8955_RIGHT_DAC_VOLUME, 0, 255, 0, digital_tlv), +SOC_SINGLE_TLV("Playback Attenuation Volume", WM8955_DAC_CONTROL, 7, 1, 1, + atten_tlv), +SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, + wm8955_get_deemph, wm8955_put_deemph), + +SOC_ENUM("Bass Mode", bass_mode), +SOC_ENUM("Bass Cutoff", bass_cutoff), +SOC_SINGLE("Bass Volume", WM8955_BASS_CONTROL, 0, 15, 1), + +SOC_ENUM("Treble Cutoff", treble_cutoff), +SOC_SINGLE_TLV("Treble Volume", WM8955_TREBLE_CONTROL, 0, 14, 1, treble_tlv), + +SOC_SINGLE_TLV("Left Bypass Volume", WM8955_LEFT_OUT_MIX_1, 4, 7, 1, + bypass_tlv), +SOC_SINGLE_TLV("Left Mono Volume", WM8955_LEFT_OUT_MIX_2, 4, 7, 1, + bypass_tlv), + +SOC_SINGLE_TLV("Right Mono Volume", WM8955_RIGHT_OUT_MIX_1, 4, 7, 1, + bypass_tlv), +SOC_SINGLE_TLV("Right Bypass Volume", WM8955_RIGHT_OUT_MIX_2, 4, 7, 1, + bypass_tlv), + +/* Not a stereo pair so they line up with the DAPM switches */ +SOC_SINGLE_TLV("Mono Left Bypass Volume", WM8955_MONO_OUT_MIX_1, 4, 7, 1, + mono_tlv), +SOC_SINGLE_TLV("Mono Right Bypass Volume", WM8955_MONO_OUT_MIX_2, 4, 7, 1, + mono_tlv), + +SOC_DOUBLE_R_TLV("Headphone Volume", WM8955_LOUT1_VOLUME, + WM8955_ROUT1_VOLUME, 0, 127, 0, out_tlv), +SOC_DOUBLE_R("Headphone ZC Switch", WM8955_LOUT1_VOLUME, + WM8955_ROUT1_VOLUME, 7, 1, 0), + +SOC_DOUBLE_R_TLV("Speaker Volume", WM8955_LOUT2_VOLUME, + WM8955_ROUT2_VOLUME, 0, 127, 0, out_tlv), +SOC_DOUBLE_R("Speaker ZC Switch", WM8955_LOUT2_VOLUME, + WM8955_ROUT2_VOLUME, 7, 1, 0), + +SOC_SINGLE_TLV("Mono Volume", WM8955_MONOOUT_VOLUME, 0, 127, 0, out_tlv), +SOC_SINGLE("Mono ZC Switch", WM8955_MONOOUT_VOLUME, 7, 1, 0), +}; + +static const struct snd_kcontrol_new lmixer[] = { +SOC_DAPM_SINGLE("Playback Switch", WM8955_LEFT_OUT_MIX_1, 8, 1, 0), +SOC_DAPM_SINGLE("Bypass Switch", WM8955_LEFT_OUT_MIX_1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8955_LEFT_OUT_MIX_2, 8, 1, 0), +SOC_DAPM_SINGLE("Mono Switch", WM8955_LEFT_OUT_MIX_2, 7, 1, 0), +}; + +static const struct snd_kcontrol_new rmixer[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8955_RIGHT_OUT_MIX_1, 8, 1, 0), +SOC_DAPM_SINGLE("Mono Switch", WM8955_RIGHT_OUT_MIX_1, 7, 1, 0), +SOC_DAPM_SINGLE("Playback Switch", WM8955_RIGHT_OUT_MIX_2, 8, 1, 0), +SOC_DAPM_SINGLE("Bypass Switch", WM8955_RIGHT_OUT_MIX_2, 7, 1, 0), +}; + +static const struct snd_kcontrol_new mmixer[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8955_MONO_OUT_MIX_1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8955_MONO_OUT_MIX_1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8955_MONO_OUT_MIX_2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8955_MONO_OUT_MIX_2, 7, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8955_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("MONOIN-"), +SND_SOC_DAPM_INPUT("MONOIN+"), +SND_SOC_DAPM_INPUT("LINEINR"), +SND_SOC_DAPM_INPUT("LINEINL"), + +SND_SOC_DAPM_PGA("Mono Input", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("SYSCLK", WM8955_POWER_MANAGEMENT_1, 0, 1, wm8955_sysclk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("TSDEN", WM8955_ADDITIONAL_CONTROL_1, 8, 0, NULL, 0), + +SND_SOC_DAPM_DAC("DACL", "Playback", WM8955_POWER_MANAGEMENT_2, 8, 0), +SND_SOC_DAPM_DAC("DACR", "Playback", WM8955_POWER_MANAGEMENT_2, 7, 0), + +SND_SOC_DAPM_PGA("LOUT1 PGA", WM8955_POWER_MANAGEMENT_2, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("ROUT1 PGA", WM8955_POWER_MANAGEMENT_2, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("LOUT2 PGA", WM8955_POWER_MANAGEMENT_2, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA("ROUT2 PGA", WM8955_POWER_MANAGEMENT_2, 3, 0, NULL, 0), +SND_SOC_DAPM_PGA("MOUT PGA", WM8955_POWER_MANAGEMENT_2, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("OUT3 PGA", WM8955_POWER_MANAGEMENT_2, 1, 0, NULL, 0), + +/* The names are chosen to make the control names nice */ +SND_SOC_DAPM_MIXER("Left", SND_SOC_NOPM, 0, 0, + lmixer, ARRAY_SIZE(lmixer)), +SND_SOC_DAPM_MIXER("Right", SND_SOC_NOPM, 0, 0, + rmixer, ARRAY_SIZE(rmixer)), +SND_SOC_DAPM_MIXER("Mono", SND_SOC_NOPM, 0, 0, + mmixer, ARRAY_SIZE(mmixer)), + +SND_SOC_DAPM_OUTPUT("LOUT1"), +SND_SOC_DAPM_OUTPUT("ROUT1"), +SND_SOC_DAPM_OUTPUT("LOUT2"), +SND_SOC_DAPM_OUTPUT("ROUT2"), +SND_SOC_DAPM_OUTPUT("MONOOUT"), +SND_SOC_DAPM_OUTPUT("OUT3"), +}; + +static const struct snd_soc_dapm_route wm8955_dapm_routes[] = { + { "DACL", NULL, "SYSCLK" }, + { "DACR", NULL, "SYSCLK" }, + + { "Mono Input", NULL, "MONOIN-" }, + { "Mono Input", NULL, "MONOIN+" }, + + { "Left", "Playback Switch", "DACL" }, + { "Left", "Right Playback Switch", "DACR" }, + { "Left", "Bypass Switch", "LINEINL" }, + { "Left", "Mono Switch", "Mono Input" }, + + { "Right", "Playback Switch", "DACR" }, + { "Right", "Left Playback Switch", "DACL" }, + { "Right", "Bypass Switch", "LINEINR" }, + { "Right", "Mono Switch", "Mono Input" }, + + { "Mono", "Left Playback Switch", "DACL" }, + { "Mono", "Right Playback Switch", "DACR" }, + { "Mono", "Left Bypass Switch", "LINEINL" }, + { "Mono", "Right Bypass Switch", "LINEINR" }, + + { "LOUT1 PGA", NULL, "Left" }, + { "LOUT1", NULL, "TSDEN" }, + { "LOUT1", NULL, "LOUT1 PGA" }, + + { "ROUT1 PGA", NULL, "Right" }, + { "ROUT1", NULL, "TSDEN" }, + { "ROUT1", NULL, "ROUT1 PGA" }, + + { "LOUT2 PGA", NULL, "Left" }, + { "LOUT2", NULL, "TSDEN" }, + { "LOUT2", NULL, "LOUT2 PGA" }, + + { "ROUT2 PGA", NULL, "Right" }, + { "ROUT2", NULL, "TSDEN" }, + { "ROUT2", NULL, "ROUT2 PGA" }, + + { "MOUT PGA", NULL, "Mono" }, + { "MONOOUT", NULL, "MOUT PGA" }, + + /* OUT3 not currently implemented */ + { "OUT3", NULL, "OUT3 PGA" }, +}; + +static int wm8955_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component); + int ret; + int wl; + + switch (params_width(params)) { + case 16: + wl = 0; + break; + case 20: + wl = 0x4; + break; + case 24: + wl = 0x8; + break; + case 32: + wl = 0xc; + break; + default: + return -EINVAL; + } + snd_soc_component_update_bits(component, WM8955_AUDIO_INTERFACE, + WM8955_WL_MASK, wl); + + wm8955->fs = params_rate(params); + wm8955_set_deemph(component); + + /* If the chip is clocked then disable the clocks and force a + * reconfiguration, otherwise DAPM will power up the + * clocks for us later. */ + ret = snd_soc_component_read(component, WM8955_POWER_MANAGEMENT_1); + if (ret < 0) + return ret; + if (ret & WM8955_DIGENB) { + snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1, + WM8955_DIGENB, 0); + snd_soc_component_update_bits(component, WM8955_CLOCKING_PLL, + WM8955_PLL_RB | WM8955_PLLEN, 0); + + wm8955_configure_clocking(component); + } + + return 0; +} + + +static int wm8955_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8955_priv *priv = snd_soc_component_get_drvdata(component); + int div; + + switch (clk_id) { + case WM8955_CLK_MCLK: + if (freq > 15000000) { + priv->mclk_rate = freq /= 2; + div = WM8955_MCLKDIV2; + } else { + priv->mclk_rate = freq; + div = 0; + } + + snd_soc_component_update_bits(component, WM8955_SAMPLE_RATE, + WM8955_MCLKDIV2, div); + break; + + default: + return -EINVAL; + } + + dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq); + + return 0; +} + +static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u16 aif = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + aif |= WM8955_MS; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif |= WM8955_LRP; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + aif |= 0x3; + break; + case SND_SOC_DAIFMT_I2S: + aif |= 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif |= 0x1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8955_BCLKINV; + break; + default: + return -EINVAL; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif |= WM8955_BCLKINV | WM8955_LRP; + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8955_BCLKINV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif |= WM8955_LRP; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8955_AUDIO_INTERFACE, + WM8955_MS | WM8955_FORMAT_MASK | WM8955_BCLKINV | + WM8955_LRP, aif); + + return 0; +} + + +static int wm8955_mute(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + int val; + + if (mute) + val = WM8955_DACMU; + else + val = 0; + + snd_soc_component_update_bits(component, WM8955_DAC_CONTROL, WM8955_DACMU, val); + + return 0; +} + +static int wm8955_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID resistance 2*50k */ + snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1, + WM8955_VMIDSEL_MASK, + 0x1 << WM8955_VMIDSEL_SHIFT); + + /* Default bias current */ + snd_soc_component_update_bits(component, WM8955_ADDITIONAL_CONTROL_1, + WM8955_VSEL_MASK, + 0x2 << WM8955_VSEL_SHIFT); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies), + wm8955->supplies); + if (ret != 0) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + + regcache_sync(wm8955->regmap); + + /* Enable VREF and VMID */ + snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1, + WM8955_VREF | + WM8955_VMIDSEL_MASK, + WM8955_VREF | + 0x3 << WM8955_VREF_SHIFT); + + /* Let VMID ramp */ + msleep(500); + + /* High resistance VROI to maintain outputs */ + snd_soc_component_update_bits(component, + WM8955_ADDITIONAL_CONTROL_3, + WM8955_VROI, WM8955_VROI); + } + + /* Maintain VMID with 2*250k */ + snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1, + WM8955_VMIDSEL_MASK, + 0x2 << WM8955_VMIDSEL_SHIFT); + + /* Minimum bias current */ + snd_soc_component_update_bits(component, WM8955_ADDITIONAL_CONTROL_1, + WM8955_VSEL_MASK, 0); + break; + + case SND_SOC_BIAS_OFF: + /* Low resistance VROI to help discharge */ + snd_soc_component_update_bits(component, + WM8955_ADDITIONAL_CONTROL_3, + WM8955_VROI, 0); + + /* Turn off VMID and VREF */ + snd_soc_component_update_bits(component, WM8955_POWER_MANAGEMENT_1, + WM8955_VREF | + WM8955_VMIDSEL_MASK, 0); + + regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), + wm8955->supplies); + break; + } + return 0; +} + +#define WM8955_RATES SNDRV_PCM_RATE_8000_96000 + +#define WM8955_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8955_dai_ops = { + .set_sysclk = wm8955_set_sysclk, + .set_fmt = wm8955_set_fmt, + .hw_params = wm8955_hw_params, + .mute_stream = wm8955_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8955_dai = { + .name = "wm8955-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = WM8955_RATES, + .formats = WM8955_FORMATS, + }, + .ops = &wm8955_dai_ops, +}; + +static int wm8955_probe(struct snd_soc_component *component) +{ + struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component); + struct wm8955_pdata *pdata = dev_get_platdata(component->dev); + int ret, i; + + for (i = 0; i < ARRAY_SIZE(wm8955->supplies); i++) + wm8955->supplies[i].supply = wm8955_supply_names[i]; + + ret = devm_regulator_bulk_get(component->dev, ARRAY_SIZE(wm8955->supplies), + wm8955->supplies); + if (ret != 0) { + dev_err(component->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies), + wm8955->supplies); + if (ret != 0) { + dev_err(component->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = wm8955_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset: %d\n", ret); + goto err_enable; + } + + /* Change some default settings - latch VU and enable ZC */ + snd_soc_component_update_bits(component, WM8955_LEFT_DAC_VOLUME, + WM8955_LDVU, WM8955_LDVU); + snd_soc_component_update_bits(component, WM8955_RIGHT_DAC_VOLUME, + WM8955_RDVU, WM8955_RDVU); + snd_soc_component_update_bits(component, WM8955_LOUT1_VOLUME, + WM8955_LO1VU | WM8955_LO1ZC, + WM8955_LO1VU | WM8955_LO1ZC); + snd_soc_component_update_bits(component, WM8955_ROUT1_VOLUME, + WM8955_RO1VU | WM8955_RO1ZC, + WM8955_RO1VU | WM8955_RO1ZC); + snd_soc_component_update_bits(component, WM8955_LOUT2_VOLUME, + WM8955_LO2VU | WM8955_LO2ZC, + WM8955_LO2VU | WM8955_LO2ZC); + snd_soc_component_update_bits(component, WM8955_ROUT2_VOLUME, + WM8955_RO2VU | WM8955_RO2ZC, + WM8955_RO2VU | WM8955_RO2ZC); + snd_soc_component_update_bits(component, WM8955_MONOOUT_VOLUME, + WM8955_MOZC, WM8955_MOZC); + + /* Also enable adaptive bass boost by default */ + snd_soc_component_update_bits(component, WM8955_BASS_CONTROL, WM8955_BB, WM8955_BB); + + /* Set platform data values */ + if (pdata) { + if (pdata->out2_speaker) + snd_soc_component_update_bits(component, WM8955_ADDITIONAL_CONTROL_2, + WM8955_ROUT2INV, WM8955_ROUT2INV); + + if (pdata->monoin_diff) + snd_soc_component_update_bits(component, WM8955_MONO_OUT_MIX_1, + WM8955_DMEN, WM8955_DMEN); + } + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + /* Bias level configuration will have done an extra enable */ + regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies); + + return 0; + +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies); + return ret; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8955 = { + .probe = wm8955_probe, + .set_bias_level = wm8955_set_bias_level, + .controls = wm8955_snd_controls, + .num_controls = ARRAY_SIZE(wm8955_snd_controls), + .dapm_widgets = wm8955_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8955_dapm_widgets), + .dapm_routes = wm8955_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8955_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8955_regmap = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = WM8955_MAX_REGISTER, + .volatile_reg = wm8955_volatile, + .writeable_reg = wm8955_writeable, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm8955_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8955_reg_defaults), +}; + +static int wm8955_i2c_probe(struct i2c_client *i2c) +{ + struct wm8955_priv *wm8955; + int ret; + + wm8955 = devm_kzalloc(&i2c->dev, sizeof(struct wm8955_priv), + GFP_KERNEL); + if (wm8955 == NULL) + return -ENOMEM; + + wm8955->regmap = devm_regmap_init_i2c(i2c, &wm8955_regmap); + if (IS_ERR(wm8955->regmap)) { + ret = PTR_ERR(wm8955->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + i2c_set_clientdata(i2c, wm8955); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8955, &wm8955_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8955_i2c_id[] = { + { "wm8955", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8955_i2c_id); + +static struct i2c_driver wm8955_i2c_driver = { + .driver = { + .name = "wm8955", + }, + .probe = wm8955_i2c_probe, + .id_table = wm8955_i2c_id, +}; + +module_i2c_driver(wm8955_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8955 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8955.h b/sound/soc/codecs/wm8955.h new file mode 100644 index 0000000000..3d3f9be041 --- /dev/null +++ b/sound/soc/codecs/wm8955.h @@ -0,0 +1,483 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8955.h -- WM8904 ASoC driver + * + * Copyright 2009 Wolfson Microelectronics, plc + * + * Author: Mark Brown + */ + +#ifndef _WM8955_H +#define _WM8955_H + +#define WM8955_CLK_MCLK 1 + +/* + * Register values. + */ +#define WM8955_LOUT1_VOLUME 0x02 +#define WM8955_ROUT1_VOLUME 0x03 +#define WM8955_DAC_CONTROL 0x05 +#define WM8955_AUDIO_INTERFACE 0x07 +#define WM8955_SAMPLE_RATE 0x08 +#define WM8955_LEFT_DAC_VOLUME 0x0A +#define WM8955_RIGHT_DAC_VOLUME 0x0B +#define WM8955_BASS_CONTROL 0x0C +#define WM8955_TREBLE_CONTROL 0x0D +#define WM8955_RESET 0x0F +#define WM8955_ADDITIONAL_CONTROL_1 0x17 +#define WM8955_ADDITIONAL_CONTROL_2 0x18 +#define WM8955_POWER_MANAGEMENT_1 0x19 +#define WM8955_POWER_MANAGEMENT_2 0x1A +#define WM8955_ADDITIONAL_CONTROL_3 0x1B +#define WM8955_LEFT_OUT_MIX_1 0x22 +#define WM8955_LEFT_OUT_MIX_2 0x23 +#define WM8955_RIGHT_OUT_MIX_1 0x24 +#define WM8955_RIGHT_OUT_MIX_2 0x25 +#define WM8955_MONO_OUT_MIX_1 0x26 +#define WM8955_MONO_OUT_MIX_2 0x27 +#define WM8955_LOUT2_VOLUME 0x28 +#define WM8955_ROUT2_VOLUME 0x29 +#define WM8955_MONOOUT_VOLUME 0x2A +#define WM8955_CLOCKING_PLL 0x2B +#define WM8955_PLL_CONTROL_1 0x2C +#define WM8955_PLL_CONTROL_2 0x2D +#define WM8955_PLL_CONTROL_3 0x2E +#define WM8955_PLL_CONTROL_4 0x3B + +#define WM8955_REGISTER_COUNT 29 +#define WM8955_MAX_REGISTER 0x3B + +/* + * Field Definitions. + */ + +/* + * R2 (0x02) - LOUT1 volume + */ +#define WM8955_LO1VU 0x0100 /* LO1VU */ +#define WM8955_LO1VU_MASK 0x0100 /* LO1VU */ +#define WM8955_LO1VU_SHIFT 8 /* LO1VU */ +#define WM8955_LO1VU_WIDTH 1 /* LO1VU */ +#define WM8955_LO1ZC 0x0080 /* LO1ZC */ +#define WM8955_LO1ZC_MASK 0x0080 /* LO1ZC */ +#define WM8955_LO1ZC_SHIFT 7 /* LO1ZC */ +#define WM8955_LO1ZC_WIDTH 1 /* LO1ZC */ +#define WM8955_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */ +#define WM8955_LOUTVOL_SHIFT 0 /* LOUTVOL - [6:0] */ +#define WM8955_LOUTVOL_WIDTH 7 /* LOUTVOL - [6:0] */ + +/* + * R3 (0x03) - ROUT1 volume + */ +#define WM8955_RO1VU 0x0100 /* RO1VU */ +#define WM8955_RO1VU_MASK 0x0100 /* RO1VU */ +#define WM8955_RO1VU_SHIFT 8 /* RO1VU */ +#define WM8955_RO1VU_WIDTH 1 /* RO1VU */ +#define WM8955_RO1ZC 0x0080 /* RO1ZC */ +#define WM8955_RO1ZC_MASK 0x0080 /* RO1ZC */ +#define WM8955_RO1ZC_SHIFT 7 /* RO1ZC */ +#define WM8955_RO1ZC_WIDTH 1 /* RO1ZC */ +#define WM8955_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */ +#define WM8955_ROUTVOL_SHIFT 0 /* ROUTVOL - [6:0] */ +#define WM8955_ROUTVOL_WIDTH 7 /* ROUTVOL - [6:0] */ + +/* + * R5 (0x05) - DAC Control + */ +#define WM8955_DAT 0x0080 /* DAT */ +#define WM8955_DAT_MASK 0x0080 /* DAT */ +#define WM8955_DAT_SHIFT 7 /* DAT */ +#define WM8955_DAT_WIDTH 1 /* DAT */ +#define WM8955_DACMU 0x0008 /* DACMU */ +#define WM8955_DACMU_MASK 0x0008 /* DACMU */ +#define WM8955_DACMU_SHIFT 3 /* DACMU */ +#define WM8955_DACMU_WIDTH 1 /* DACMU */ +#define WM8955_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */ +#define WM8955_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */ +#define WM8955_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */ + +/* + * R7 (0x07) - Audio Interface + */ +#define WM8955_BCLKINV 0x0080 /* BCLKINV */ +#define WM8955_BCLKINV_MASK 0x0080 /* BCLKINV */ +#define WM8955_BCLKINV_SHIFT 7 /* BCLKINV */ +#define WM8955_BCLKINV_WIDTH 1 /* BCLKINV */ +#define WM8955_MS 0x0040 /* MS */ +#define WM8955_MS_MASK 0x0040 /* MS */ +#define WM8955_MS_SHIFT 6 /* MS */ +#define WM8955_MS_WIDTH 1 /* MS */ +#define WM8955_LRSWAP 0x0020 /* LRSWAP */ +#define WM8955_LRSWAP_MASK 0x0020 /* LRSWAP */ +#define WM8955_LRSWAP_SHIFT 5 /* LRSWAP */ +#define WM8955_LRSWAP_WIDTH 1 /* LRSWAP */ +#define WM8955_LRP 0x0010 /* LRP */ +#define WM8955_LRP_MASK 0x0010 /* LRP */ +#define WM8955_LRP_SHIFT 4 /* LRP */ +#define WM8955_LRP_WIDTH 1 /* LRP */ +#define WM8955_WL_MASK 0x000C /* WL - [3:2] */ +#define WM8955_WL_SHIFT 2 /* WL - [3:2] */ +#define WM8955_WL_WIDTH 2 /* WL - [3:2] */ +#define WM8955_FORMAT_MASK 0x0003 /* FORMAT - [1:0] */ +#define WM8955_FORMAT_SHIFT 0 /* FORMAT - [1:0] */ +#define WM8955_FORMAT_WIDTH 2 /* FORMAT - [1:0] */ + +/* + * R8 (0x08) - Sample Rate + */ +#define WM8955_BCLKDIV2 0x0080 /* BCLKDIV2 */ +#define WM8955_BCLKDIV2_MASK 0x0080 /* BCLKDIV2 */ +#define WM8955_BCLKDIV2_SHIFT 7 /* BCLKDIV2 */ +#define WM8955_BCLKDIV2_WIDTH 1 /* BCLKDIV2 */ +#define WM8955_MCLKDIV2 0x0040 /* MCLKDIV2 */ +#define WM8955_MCLKDIV2_MASK 0x0040 /* MCLKDIV2 */ +#define WM8955_MCLKDIV2_SHIFT 6 /* MCLKDIV2 */ +#define WM8955_MCLKDIV2_WIDTH 1 /* MCLKDIV2 */ +#define WM8955_SR_MASK 0x003E /* SR - [5:1] */ +#define WM8955_SR_SHIFT 1 /* SR - [5:1] */ +#define WM8955_SR_WIDTH 5 /* SR - [5:1] */ +#define WM8955_USB 0x0001 /* USB */ +#define WM8955_USB_MASK 0x0001 /* USB */ +#define WM8955_USB_SHIFT 0 /* USB */ +#define WM8955_USB_WIDTH 1 /* USB */ + +/* + * R10 (0x0A) - Left DAC volume + */ +#define WM8955_LDVU 0x0100 /* LDVU */ +#define WM8955_LDVU_MASK 0x0100 /* LDVU */ +#define WM8955_LDVU_SHIFT 8 /* LDVU */ +#define WM8955_LDVU_WIDTH 1 /* LDVU */ +#define WM8955_LDACVOL_MASK 0x00FF /* LDACVOL - [7:0] */ +#define WM8955_LDACVOL_SHIFT 0 /* LDACVOL - [7:0] */ +#define WM8955_LDACVOL_WIDTH 8 /* LDACVOL - [7:0] */ + +/* + * R11 (0x0B) - Right DAC volume + */ +#define WM8955_RDVU 0x0100 /* RDVU */ +#define WM8955_RDVU_MASK 0x0100 /* RDVU */ +#define WM8955_RDVU_SHIFT 8 /* RDVU */ +#define WM8955_RDVU_WIDTH 1 /* RDVU */ +#define WM8955_RDACVOL_MASK 0x00FF /* RDACVOL - [7:0] */ +#define WM8955_RDACVOL_SHIFT 0 /* RDACVOL - [7:0] */ +#define WM8955_RDACVOL_WIDTH 8 /* RDACVOL - [7:0] */ + +/* + * R12 (0x0C) - Bass control + */ +#define WM8955_BB 0x0080 /* BB */ +#define WM8955_BB_MASK 0x0080 /* BB */ +#define WM8955_BB_SHIFT 7 /* BB */ +#define WM8955_BB_WIDTH 1 /* BB */ +#define WM8955_BC 0x0040 /* BC */ +#define WM8955_BC_MASK 0x0040 /* BC */ +#define WM8955_BC_SHIFT 6 /* BC */ +#define WM8955_BC_WIDTH 1 /* BC */ +#define WM8955_BASS_MASK 0x000F /* BASS - [3:0] */ +#define WM8955_BASS_SHIFT 0 /* BASS - [3:0] */ +#define WM8955_BASS_WIDTH 4 /* BASS - [3:0] */ + +/* + * R13 (0x0D) - Treble control + */ +#define WM8955_TC 0x0040 /* TC */ +#define WM8955_TC_MASK 0x0040 /* TC */ +#define WM8955_TC_SHIFT 6 /* TC */ +#define WM8955_TC_WIDTH 1 /* TC */ +#define WM8955_TRBL_MASK 0x000F /* TRBL - [3:0] */ +#define WM8955_TRBL_SHIFT 0 /* TRBL - [3:0] */ +#define WM8955_TRBL_WIDTH 4 /* TRBL - [3:0] */ + +/* + * R15 (0x0F) - Reset + */ +#define WM8955_RESET_MASK 0x01FF /* RESET - [8:0] */ +#define WM8955_RESET_SHIFT 0 /* RESET - [8:0] */ +#define WM8955_RESET_WIDTH 9 /* RESET - [8:0] */ + +/* + * R23 (0x17) - Additional control (1) + */ +#define WM8955_TSDEN 0x0100 /* TSDEN */ +#define WM8955_TSDEN_MASK 0x0100 /* TSDEN */ +#define WM8955_TSDEN_SHIFT 8 /* TSDEN */ +#define WM8955_TSDEN_WIDTH 1 /* TSDEN */ +#define WM8955_VSEL_MASK 0x00C0 /* VSEL - [7:6] */ +#define WM8955_VSEL_SHIFT 6 /* VSEL - [7:6] */ +#define WM8955_VSEL_WIDTH 2 /* VSEL - [7:6] */ +#define WM8955_DMONOMIX_MASK 0x0030 /* DMONOMIX - [5:4] */ +#define WM8955_DMONOMIX_SHIFT 4 /* DMONOMIX - [5:4] */ +#define WM8955_DMONOMIX_WIDTH 2 /* DMONOMIX - [5:4] */ +#define WM8955_DACINV 0x0002 /* DACINV */ +#define WM8955_DACINV_MASK 0x0002 /* DACINV */ +#define WM8955_DACINV_SHIFT 1 /* DACINV */ +#define WM8955_DACINV_WIDTH 1 /* DACINV */ +#define WM8955_TOEN 0x0001 /* TOEN */ +#define WM8955_TOEN_MASK 0x0001 /* TOEN */ +#define WM8955_TOEN_SHIFT 0 /* TOEN */ +#define WM8955_TOEN_WIDTH 1 /* TOEN */ + +/* + * R24 (0x18) - Additional control (2) + */ +#define WM8955_OUT3SW_MASK 0x0180 /* OUT3SW - [8:7] */ +#define WM8955_OUT3SW_SHIFT 7 /* OUT3SW - [8:7] */ +#define WM8955_OUT3SW_WIDTH 2 /* OUT3SW - [8:7] */ +#define WM8955_ROUT2INV 0x0010 /* ROUT2INV */ +#define WM8955_ROUT2INV_MASK 0x0010 /* ROUT2INV */ +#define WM8955_ROUT2INV_SHIFT 4 /* ROUT2INV */ +#define WM8955_ROUT2INV_WIDTH 1 /* ROUT2INV */ +#define WM8955_DACOSR 0x0001 /* DACOSR */ +#define WM8955_DACOSR_MASK 0x0001 /* DACOSR */ +#define WM8955_DACOSR_SHIFT 0 /* DACOSR */ +#define WM8955_DACOSR_WIDTH 1 /* DACOSR */ + +/* + * R25 (0x19) - Power Management (1) + */ +#define WM8955_VMIDSEL_MASK 0x0180 /* VMIDSEL - [8:7] */ +#define WM8955_VMIDSEL_SHIFT 7 /* VMIDSEL - [8:7] */ +#define WM8955_VMIDSEL_WIDTH 2 /* VMIDSEL - [8:7] */ +#define WM8955_VREF 0x0040 /* VREF */ +#define WM8955_VREF_MASK 0x0040 /* VREF */ +#define WM8955_VREF_SHIFT 6 /* VREF */ +#define WM8955_VREF_WIDTH 1 /* VREF */ +#define WM8955_DIGENB 0x0001 /* DIGENB */ +#define WM8955_DIGENB_MASK 0x0001 /* DIGENB */ +#define WM8955_DIGENB_SHIFT 0 /* DIGENB */ +#define WM8955_DIGENB_WIDTH 1 /* DIGENB */ + +/* + * R26 (0x1A) - Power Management (2) + */ +#define WM8955_DACL 0x0100 /* DACL */ +#define WM8955_DACL_MASK 0x0100 /* DACL */ +#define WM8955_DACL_SHIFT 8 /* DACL */ +#define WM8955_DACL_WIDTH 1 /* DACL */ +#define WM8955_DACR 0x0080 /* DACR */ +#define WM8955_DACR_MASK 0x0080 /* DACR */ +#define WM8955_DACR_SHIFT 7 /* DACR */ +#define WM8955_DACR_WIDTH 1 /* DACR */ +#define WM8955_LOUT1 0x0040 /* LOUT1 */ +#define WM8955_LOUT1_MASK 0x0040 /* LOUT1 */ +#define WM8955_LOUT1_SHIFT 6 /* LOUT1 */ +#define WM8955_LOUT1_WIDTH 1 /* LOUT1 */ +#define WM8955_ROUT1 0x0020 /* ROUT1 */ +#define WM8955_ROUT1_MASK 0x0020 /* ROUT1 */ +#define WM8955_ROUT1_SHIFT 5 /* ROUT1 */ +#define WM8955_ROUT1_WIDTH 1 /* ROUT1 */ +#define WM8955_LOUT2 0x0010 /* LOUT2 */ +#define WM8955_LOUT2_MASK 0x0010 /* LOUT2 */ +#define WM8955_LOUT2_SHIFT 4 /* LOUT2 */ +#define WM8955_LOUT2_WIDTH 1 /* LOUT2 */ +#define WM8955_ROUT2 0x0008 /* ROUT2 */ +#define WM8955_ROUT2_MASK 0x0008 /* ROUT2 */ +#define WM8955_ROUT2_SHIFT 3 /* ROUT2 */ +#define WM8955_ROUT2_WIDTH 1 /* ROUT2 */ +#define WM8955_MONO 0x0004 /* MONO */ +#define WM8955_MONO_MASK 0x0004 /* MONO */ +#define WM8955_MONO_SHIFT 2 /* MONO */ +#define WM8955_MONO_WIDTH 1 /* MONO */ +#define WM8955_OUT3 0x0002 /* OUT3 */ +#define WM8955_OUT3_MASK 0x0002 /* OUT3 */ +#define WM8955_OUT3_SHIFT 1 /* OUT3 */ +#define WM8955_OUT3_WIDTH 1 /* OUT3 */ + +/* + * R27 (0x1B) - Additional Control (3) + */ +#define WM8955_VROI 0x0040 /* VROI */ +#define WM8955_VROI_MASK 0x0040 /* VROI */ +#define WM8955_VROI_SHIFT 6 /* VROI */ +#define WM8955_VROI_WIDTH 1 /* VROI */ + +/* + * R34 (0x22) - Left out Mix (1) + */ +#define WM8955_LD2LO 0x0100 /* LD2LO */ +#define WM8955_LD2LO_MASK 0x0100 /* LD2LO */ +#define WM8955_LD2LO_SHIFT 8 /* LD2LO */ +#define WM8955_LD2LO_WIDTH 1 /* LD2LO */ +#define WM8955_LI2LO 0x0080 /* LI2LO */ +#define WM8955_LI2LO_MASK 0x0080 /* LI2LO */ +#define WM8955_LI2LO_SHIFT 7 /* LI2LO */ +#define WM8955_LI2LO_WIDTH 1 /* LI2LO */ +#define WM8955_LI2LOVOL_MASK 0x0070 /* LI2LOVOL - [6:4] */ +#define WM8955_LI2LOVOL_SHIFT 4 /* LI2LOVOL - [6:4] */ +#define WM8955_LI2LOVOL_WIDTH 3 /* LI2LOVOL - [6:4] */ + +/* + * R35 (0x23) - Left out Mix (2) + */ +#define WM8955_RD2LO 0x0100 /* RD2LO */ +#define WM8955_RD2LO_MASK 0x0100 /* RD2LO */ +#define WM8955_RD2LO_SHIFT 8 /* RD2LO */ +#define WM8955_RD2LO_WIDTH 1 /* RD2LO */ +#define WM8955_RI2LO 0x0080 /* RI2LO */ +#define WM8955_RI2LO_MASK 0x0080 /* RI2LO */ +#define WM8955_RI2LO_SHIFT 7 /* RI2LO */ +#define WM8955_RI2LO_WIDTH 1 /* RI2LO */ +#define WM8955_RI2LOVOL_MASK 0x0070 /* RI2LOVOL - [6:4] */ +#define WM8955_RI2LOVOL_SHIFT 4 /* RI2LOVOL - [6:4] */ +#define WM8955_RI2LOVOL_WIDTH 3 /* RI2LOVOL - [6:4] */ + +/* + * R36 (0x24) - Right out Mix (1) + */ +#define WM8955_LD2RO 0x0100 /* LD2RO */ +#define WM8955_LD2RO_MASK 0x0100 /* LD2RO */ +#define WM8955_LD2RO_SHIFT 8 /* LD2RO */ +#define WM8955_LD2RO_WIDTH 1 /* LD2RO */ +#define WM8955_LI2RO 0x0080 /* LI2RO */ +#define WM8955_LI2RO_MASK 0x0080 /* LI2RO */ +#define WM8955_LI2RO_SHIFT 7 /* LI2RO */ +#define WM8955_LI2RO_WIDTH 1 /* LI2RO */ +#define WM8955_LI2ROVOL_MASK 0x0070 /* LI2ROVOL - [6:4] */ +#define WM8955_LI2ROVOL_SHIFT 4 /* LI2ROVOL - [6:4] */ +#define WM8955_LI2ROVOL_WIDTH 3 /* LI2ROVOL - [6:4] */ + +/* + * R37 (0x25) - Right Out Mix (2) + */ +#define WM8955_RD2RO 0x0100 /* RD2RO */ +#define WM8955_RD2RO_MASK 0x0100 /* RD2RO */ +#define WM8955_RD2RO_SHIFT 8 /* RD2RO */ +#define WM8955_RD2RO_WIDTH 1 /* RD2RO */ +#define WM8955_RI2RO 0x0080 /* RI2RO */ +#define WM8955_RI2RO_MASK 0x0080 /* RI2RO */ +#define WM8955_RI2RO_SHIFT 7 /* RI2RO */ +#define WM8955_RI2RO_WIDTH 1 /* RI2RO */ +#define WM8955_RI2ROVOL_MASK 0x0070 /* RI2ROVOL - [6:4] */ +#define WM8955_RI2ROVOL_SHIFT 4 /* RI2ROVOL - [6:4] */ +#define WM8955_RI2ROVOL_WIDTH 3 /* RI2ROVOL - [6:4] */ + +/* + * R38 (0x26) - Mono out Mix (1) + */ +#define WM8955_LD2MO 0x0100 /* LD2MO */ +#define WM8955_LD2MO_MASK 0x0100 /* LD2MO */ +#define WM8955_LD2MO_SHIFT 8 /* LD2MO */ +#define WM8955_LD2MO_WIDTH 1 /* LD2MO */ +#define WM8955_LI2MO 0x0080 /* LI2MO */ +#define WM8955_LI2MO_MASK 0x0080 /* LI2MO */ +#define WM8955_LI2MO_SHIFT 7 /* LI2MO */ +#define WM8955_LI2MO_WIDTH 1 /* LI2MO */ +#define WM8955_LI2MOVOL_MASK 0x0070 /* LI2MOVOL - [6:4] */ +#define WM8955_LI2MOVOL_SHIFT 4 /* LI2MOVOL - [6:4] */ +#define WM8955_LI2MOVOL_WIDTH 3 /* LI2MOVOL - [6:4] */ +#define WM8955_DMEN 0x0001 /* DMEN */ +#define WM8955_DMEN_MASK 0x0001 /* DMEN */ +#define WM8955_DMEN_SHIFT 0 /* DMEN */ +#define WM8955_DMEN_WIDTH 1 /* DMEN */ + +/* + * R39 (0x27) - Mono out Mix (2) + */ +#define WM8955_RD2MO 0x0100 /* RD2MO */ +#define WM8955_RD2MO_MASK 0x0100 /* RD2MO */ +#define WM8955_RD2MO_SHIFT 8 /* RD2MO */ +#define WM8955_RD2MO_WIDTH 1 /* RD2MO */ +#define WM8955_RI2MO 0x0080 /* RI2MO */ +#define WM8955_RI2MO_MASK 0x0080 /* RI2MO */ +#define WM8955_RI2MO_SHIFT 7 /* RI2MO */ +#define WM8955_RI2MO_WIDTH 1 /* RI2MO */ +#define WM8955_RI2MOVOL_MASK 0x0070 /* RI2MOVOL - [6:4] */ +#define WM8955_RI2MOVOL_SHIFT 4 /* RI2MOVOL - [6:4] */ +#define WM8955_RI2MOVOL_WIDTH 3 /* RI2MOVOL - [6:4] */ + +/* + * R40 (0x28) - LOUT2 volume + */ +#define WM8955_LO2VU 0x0100 /* LO2VU */ +#define WM8955_LO2VU_MASK 0x0100 /* LO2VU */ +#define WM8955_LO2VU_SHIFT 8 /* LO2VU */ +#define WM8955_LO2VU_WIDTH 1 /* LO2VU */ +#define WM8955_LO2ZC 0x0080 /* LO2ZC */ +#define WM8955_LO2ZC_MASK 0x0080 /* LO2ZC */ +#define WM8955_LO2ZC_SHIFT 7 /* LO2ZC */ +#define WM8955_LO2ZC_WIDTH 1 /* LO2ZC */ +#define WM8955_LOUT2VOL_MASK 0x007F /* LOUT2VOL - [6:0] */ +#define WM8955_LOUT2VOL_SHIFT 0 /* LOUT2VOL - [6:0] */ +#define WM8955_LOUT2VOL_WIDTH 7 /* LOUT2VOL - [6:0] */ + +/* + * R41 (0x29) - ROUT2 volume + */ +#define WM8955_RO2VU 0x0100 /* RO2VU */ +#define WM8955_RO2VU_MASK 0x0100 /* RO2VU */ +#define WM8955_RO2VU_SHIFT 8 /* RO2VU */ +#define WM8955_RO2VU_WIDTH 1 /* RO2VU */ +#define WM8955_RO2ZC 0x0080 /* RO2ZC */ +#define WM8955_RO2ZC_MASK 0x0080 /* RO2ZC */ +#define WM8955_RO2ZC_SHIFT 7 /* RO2ZC */ +#define WM8955_RO2ZC_WIDTH 1 /* RO2ZC */ +#define WM8955_ROUT2VOL_MASK 0x007F /* ROUT2VOL - [6:0] */ +#define WM8955_ROUT2VOL_SHIFT 0 /* ROUT2VOL - [6:0] */ +#define WM8955_ROUT2VOL_WIDTH 7 /* ROUT2VOL - [6:0] */ + +/* + * R42 (0x2A) - MONOOUT volume + */ +#define WM8955_MOZC 0x0080 /* MOZC */ +#define WM8955_MOZC_MASK 0x0080 /* MOZC */ +#define WM8955_MOZC_SHIFT 7 /* MOZC */ +#define WM8955_MOZC_WIDTH 1 /* MOZC */ +#define WM8955_MOUTVOL_MASK 0x007F /* MOUTVOL - [6:0] */ +#define WM8955_MOUTVOL_SHIFT 0 /* MOUTVOL - [6:0] */ +#define WM8955_MOUTVOL_WIDTH 7 /* MOUTVOL - [6:0] */ + +/* + * R43 (0x2B) - Clocking / PLL + */ +#define WM8955_MCLKSEL 0x0100 /* MCLKSEL */ +#define WM8955_MCLKSEL_MASK 0x0100 /* MCLKSEL */ +#define WM8955_MCLKSEL_SHIFT 8 /* MCLKSEL */ +#define WM8955_MCLKSEL_WIDTH 1 /* MCLKSEL */ +#define WM8955_PLLOUTDIV2 0x0020 /* PLLOUTDIV2 */ +#define WM8955_PLLOUTDIV2_MASK 0x0020 /* PLLOUTDIV2 */ +#define WM8955_PLLOUTDIV2_SHIFT 5 /* PLLOUTDIV2 */ +#define WM8955_PLLOUTDIV2_WIDTH 1 /* PLLOUTDIV2 */ +#define WM8955_PLL_RB 0x0010 /* PLL_RB */ +#define WM8955_PLL_RB_MASK 0x0010 /* PLL_RB */ +#define WM8955_PLL_RB_SHIFT 4 /* PLL_RB */ +#define WM8955_PLL_RB_WIDTH 1 /* PLL_RB */ +#define WM8955_PLLEN 0x0008 /* PLLEN */ +#define WM8955_PLLEN_MASK 0x0008 /* PLLEN */ +#define WM8955_PLLEN_SHIFT 3 /* PLLEN */ +#define WM8955_PLLEN_WIDTH 1 /* PLLEN */ + +/* + * R44 (0x2C) - PLL Control 1 + */ +#define WM8955_N_MASK 0x01E0 /* N - [8:5] */ +#define WM8955_N_SHIFT 5 /* N - [8:5] */ +#define WM8955_N_WIDTH 4 /* N - [8:5] */ +#define WM8955_K_21_18_MASK 0x000F /* K(21:18) - [3:0] */ +#define WM8955_K_21_18_SHIFT 0 /* K(21:18) - [3:0] */ +#define WM8955_K_21_18_WIDTH 4 /* K(21:18) - [3:0] */ + +/* + * R45 (0x2D) - PLL Control 2 + */ +#define WM8955_K_17_9_MASK 0x01FF /* K(17:9) - [8:0] */ +#define WM8955_K_17_9_SHIFT 0 /* K(17:9) - [8:0] */ +#define WM8955_K_17_9_WIDTH 9 /* K(17:9) - [8:0] */ + +/* + * R46 (0x2E) - PLL Control 3 + */ +#define WM8955_K_8_0_MASK 0x01FF /* K(8:0) - [8:0] */ +#define WM8955_K_8_0_SHIFT 0 /* K(8:0) - [8:0] */ +#define WM8955_K_8_0_WIDTH 9 /* K(8:0) - [8:0] */ + +/* + * R59 (0x3B) - PLL Control 4 + */ +#define WM8955_KEN 0x0080 /* KEN */ +#define WM8955_KEN_MASK 0x0080 /* KEN */ +#define WM8955_KEN_SHIFT 7 /* KEN */ +#define WM8955_KEN_WIDTH 1 /* KEN */ + +#endif diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c new file mode 100644 index 0000000000..7878c7a58f --- /dev/null +++ b/sound/soc/codecs/wm8958-dsp2.c @@ -0,0 +1,1032 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8958-dsp2.c -- WM8958 DSP2 support + * + * Copyright 2011 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "wm8994.h" + +#define WM_FW_BLOCK_INFO 0xff +#define WM_FW_BLOCK_PM 0x00 +#define WM_FW_BLOCK_X 0x01 +#define WM_FW_BLOCK_Y 0x02 +#define WM_FW_BLOCK_Z 0x03 +#define WM_FW_BLOCK_I 0x06 +#define WM_FW_BLOCK_A 0x08 +#define WM_FW_BLOCK_C 0x0c + +static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name, + const struct firmware *fw, bool check) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + u64 data64; + u32 data32; + const u8 *data; + char *str; + size_t block_len, len; + int ret = 0; + + /* Suppress unneeded downloads */ + if (wm8994->cur_fw == fw) + return 0; + + if (fw->size < 32) { + dev_err(component->dev, "%s: firmware too short (%zd bytes)\n", + name, fw->size); + goto err; + } + + if (memcmp(fw->data, "WMFW", 4) != 0) { + data32 = get_unaligned_be32(fw->data); + dev_err(component->dev, "%s: firmware has bad file magic %08x\n", + name, data32); + goto err; + } + + len = get_unaligned_be32(fw->data + 4); + data32 = get_unaligned_be32(fw->data + 8); + + if ((data32 >> 24) & 0xff) { + dev_err(component->dev, "%s: unsupported firmware version %d\n", + name, (data32 >> 24) & 0xff); + goto err; + } + if ((data32 & 0xffff) != 8958) { + dev_err(component->dev, "%s: unsupported target device %d\n", + name, data32 & 0xffff); + goto err; + } + if (((data32 >> 16) & 0xff) != 0xc) { + dev_err(component->dev, "%s: unsupported target core %d\n", + name, (data32 >> 16) & 0xff); + goto err; + } + + if (check) { + data64 = get_unaligned_be64(fw->data + 24); + dev_info(component->dev, "%s timestamp %llx\n", name, data64); + } else { + snd_soc_component_write(component, 0x102, 0x2); + snd_soc_component_write(component, 0x900, 0x2); + } + + data = fw->data + len; + len = fw->size - len; + while (len) { + if (len < 12) { + dev_err(component->dev, "%s short data block of %zd\n", + name, len); + goto err; + } + + block_len = get_unaligned_be32(data + 4); + if (block_len + 8 > len) { + dev_err(component->dev, "%zd byte block longer than file\n", + block_len); + goto err; + } + if (block_len == 0) { + dev_err(component->dev, "Zero length block\n"); + goto err; + } + + data32 = get_unaligned_be32(data); + + switch ((data32 >> 24) & 0xff) { + case WM_FW_BLOCK_INFO: + /* Informational text */ + if (!check) + break; + + str = kzalloc(block_len + 1, GFP_KERNEL); + if (str) { + memcpy(str, data + 8, block_len); + dev_info(component->dev, "%s: %s\n", name, str); + kfree(str); + } else { + dev_err(component->dev, "Out of memory\n"); + } + break; + case WM_FW_BLOCK_PM: + case WM_FW_BLOCK_X: + case WM_FW_BLOCK_Y: + case WM_FW_BLOCK_Z: + case WM_FW_BLOCK_I: + case WM_FW_BLOCK_A: + case WM_FW_BLOCK_C: + dev_dbg(component->dev, "%s: %zd bytes of %x@%x\n", name, + block_len, (data32 >> 24) & 0xff, + data32 & 0xffffff); + + if (check) + break; + + data32 &= 0xffffff; + + wm8994_bulk_write(wm8994->wm8994, + data32 & 0xffffff, + block_len / 2, + (void *)(data + 8)); + + break; + default: + dev_warn(component->dev, "%s: unknown block type %d\n", + name, (data32 >> 24) & 0xff); + break; + } + + /* Round up to the next 32 bit word */ + block_len += block_len % 4; + + data += block_len + 8; + len -= block_len + 8; + } + + if (!check) { + dev_dbg(component->dev, "%s: download done\n", name); + wm8994->cur_fw = fw; + } else { + dev_info(component->dev, "%s: got firmware\n", name); + } + + goto ok; + +err: + ret = -EINVAL; +ok: + if (!check) { + snd_soc_component_write(component, 0x900, 0x0); + snd_soc_component_write(component, 0x102, 0x0); + } + + return ret; +} + +static void wm8958_dsp_start_mbc(struct snd_soc_component *component, int path) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int i; + + /* If the DSP is already running then noop */ + if (snd_soc_component_read(component, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA) + return; + + /* If we have MBC firmware download it */ + if (wm8994->mbc) + wm8958_dsp2_fw(component, "MBC", wm8994->mbc, false); + + snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM, + WM8958_DSP2_ENA, WM8958_DSP2_ENA); + + /* If we've got user supplied MBC settings use them */ + if (control->pdata.num_mbc_cfgs) { + struct wm8958_mbc_cfg *cfg + = &control->pdata.mbc_cfgs[wm8994->mbc_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++) + snd_soc_component_write(component, i + WM8958_MBC_BAND_1_K_1, + cfg->coeff_regs[i]); + + for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++) + snd_soc_component_write(component, + i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1, + cfg->cutoff_regs[i]); + } + + /* Run the DSP */ + snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL, + WM8958_DSP2_RUNR); + + /* And we're off! */ + snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG, + WM8958_MBC_ENA | + WM8958_MBC_SEL_MASK, + path << WM8958_MBC_SEL_SHIFT | + WM8958_MBC_ENA); +} + +static void wm8958_dsp_start_vss(struct snd_soc_component *component, int path) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int i, ena; + + if (wm8994->mbc_vss) + wm8958_dsp2_fw(component, "MBC+VSS", wm8994->mbc_vss, false); + + snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM, + WM8958_DSP2_ENA, WM8958_DSP2_ENA); + + /* If we've got user supplied settings use them */ + if (control->pdata.num_mbc_cfgs) { + struct wm8958_mbc_cfg *cfg + = &control->pdata.mbc_cfgs[wm8994->mbc_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++) + snd_soc_component_write(component, i + 0x2800, + cfg->combined_regs[i]); + } + + if (control->pdata.num_vss_cfgs) { + struct wm8958_vss_cfg *cfg + = &control->pdata.vss_cfgs[wm8994->vss_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->regs); i++) + snd_soc_component_write(component, i + 0x2600, cfg->regs[i]); + } + + if (control->pdata.num_vss_hpf_cfgs) { + struct wm8958_vss_hpf_cfg *cfg + = &control->pdata.vss_hpf_cfgs[wm8994->vss_hpf_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->regs); i++) + snd_soc_component_write(component, i + 0x2400, cfg->regs[i]); + } + + /* Run the DSP */ + snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL, + WM8958_DSP2_RUNR); + + /* Enable the algorithms we've selected */ + ena = 0; + if (wm8994->mbc_ena[path]) + ena |= 0x8; + if (wm8994->hpf2_ena[path]) + ena |= 0x4; + if (wm8994->hpf1_ena[path]) + ena |= 0x2; + if (wm8994->vss_ena[path]) + ena |= 0x1; + + snd_soc_component_write(component, 0x2201, ena); + + /* Switch the DSP into the data path */ + snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG, + WM8958_MBC_SEL_MASK | WM8958_MBC_ENA, + path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA); +} + +static void wm8958_dsp_start_enh_eq(struct snd_soc_component *component, int path) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int i; + + wm8958_dsp2_fw(component, "ENH_EQ", wm8994->enh_eq, false); + + snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM, + WM8958_DSP2_ENA, WM8958_DSP2_ENA); + + /* If we've got user supplied settings use them */ + if (control->pdata.num_enh_eq_cfgs) { + struct wm8958_enh_eq_cfg *cfg + = &control->pdata.enh_eq_cfgs[wm8994->enh_eq_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->regs); i++) + snd_soc_component_write(component, i + 0x2200, + cfg->regs[i]); + } + + /* Run the DSP */ + snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL, + WM8958_DSP2_RUNR); + + /* Switch the DSP into the data path */ + snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG, + WM8958_MBC_SEL_MASK | WM8958_MBC_ENA, + path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA); +} + +static void wm8958_dsp_apply(struct snd_soc_component *component, int path, int start) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + int pwr_reg = snd_soc_component_read(component, WM8994_POWER_MANAGEMENT_5); + int ena, reg, aif; + + switch (path) { + case 0: + pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA); + aif = 0; + break; + case 1: + pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA); + aif = 0; + break; + case 2: + pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA); + aif = 1; + break; + default: + WARN(1, "Invalid path %d\n", path); + return; + } + + /* Do we have both an active AIF and an active algorithm? */ + ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] || + wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path] || + wm8994->enh_eq_ena[path]; + if (!pwr_reg) + ena = 0; + + reg = snd_soc_component_read(component, WM8958_DSP2_PROGRAM); + + dev_dbg(component->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n", + path, wm8994->dsp_active, start, pwr_reg, reg); + + if (start && ena) { + /* If the DSP is already running then noop */ + if (reg & WM8958_DSP2_ENA) + return; + + /* If either AIFnCLK is not yet enabled postpone */ + if (!(snd_soc_component_read(component, WM8994_AIF1_CLOCKING_1) + & WM8994_AIF1CLK_ENA_MASK) && + !(snd_soc_component_read(component, WM8994_AIF2_CLOCKING_1) + & WM8994_AIF2CLK_ENA_MASK)) + return; + + /* Switch the clock over to the appropriate AIF */ + snd_soc_component_update_bits(component, WM8994_CLOCKING_1, + WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA, + aif << WM8958_DSP2CLK_SRC_SHIFT | + WM8958_DSP2CLK_ENA); + + if (wm8994->enh_eq_ena[path]) + wm8958_dsp_start_enh_eq(component, path); + else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] || + wm8994->hpf2_ena[path]) + wm8958_dsp_start_vss(component, path); + else if (wm8994->mbc_ena[path]) + wm8958_dsp_start_mbc(component, path); + + wm8994->dsp_active = path; + + dev_dbg(component->dev, "DSP running in path %d\n", path); + } + + if (!start && wm8994->dsp_active == path) { + /* If the DSP is already stopped then noop */ + if (!(reg & WM8958_DSP2_ENA)) + return; + + snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG, + WM8958_MBC_ENA, 0); + snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL, + WM8958_DSP2_STOP); + snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM, + WM8958_DSP2_ENA, 0); + snd_soc_component_update_bits(component, WM8994_CLOCKING_1, + WM8958_DSP2CLK_ENA, 0); + + wm8994->dsp_active = -1; + + dev_dbg(component->dev, "DSP stopped\n"); + } +} + +int wm8958_aif_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8994 *control = dev_get_drvdata(component->dev->parent); + int i; + + if (control->type != WM8958) + return 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMU: + for (i = 0; i < 3; i++) + wm8958_dsp_apply(component, i, 1); + break; + case SND_SOC_DAPM_POST_PMD: + case SND_SOC_DAPM_PRE_PMD: + for (i = 0; i < 3; i++) + wm8958_dsp_apply(component, i, 0); + break; + } + + return 0; +} + +/* Check if DSP2 is in use on another AIF */ +static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) { + if (i == aif) + continue; + if (wm8994->mbc_ena[i] || wm8994->vss_ena[i] || + wm8994->hpf1_ena[i] || wm8994->hpf2_ena[i]) + return 1; + } + + return 0; +} + +static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int value = ucontrol->value.enumerated.item[0]; + int reg; + + /* Don't allow on the fly reconfiguration */ + reg = snd_soc_component_read(component, WM8994_CLOCKING_1); + if (reg < 0 || reg & WM8958_DSP2CLK_ENA) + return -EBUSY; + + if (value >= control->pdata.num_mbc_cfgs) + return -EINVAL; + + wm8994->mbc_cfg = value; + + return 0; +} + +static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg; + + return 0; +} + +static int wm8958_mbc_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm8958_mbc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mbc = kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc]; + + return 0; +} + +static int wm8958_mbc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mbc = kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0]) + return 0; + + if (ucontrol->value.integer.value[0] > 1) + return -EINVAL; + + if (wm8958_dsp2_busy(wm8994, mbc)) { + dev_dbg(component->dev, "DSP2 active on %d already\n", mbc); + return -EBUSY; + } + + if (wm8994->enh_eq_ena[mbc]) + return -EBUSY; + + wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0]; + + wm8958_dsp_apply(component, mbc, wm8994->mbc_ena[mbc]); + + return 1; +} + +#define WM8958_MBC_SWITCH(xname, xval) {\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .info = wm8958_mbc_info, \ + .get = wm8958_mbc_get, .put = wm8958_mbc_put, \ + .private_value = xval } + +static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int value = ucontrol->value.enumerated.item[0]; + int reg; + + /* Don't allow on the fly reconfiguration */ + reg = snd_soc_component_read(component, WM8994_CLOCKING_1); + if (reg < 0 || reg & WM8958_DSP2CLK_ENA) + return -EBUSY; + + if (value >= control->pdata.num_vss_cfgs) + return -EINVAL; + + wm8994->vss_cfg = value; + + return 0; +} + +static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wm8994->vss_cfg; + + return 0; +} + +static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int value = ucontrol->value.enumerated.item[0]; + int reg; + + /* Don't allow on the fly reconfiguration */ + reg = snd_soc_component_read(component, WM8994_CLOCKING_1); + if (reg < 0 || reg & WM8958_DSP2CLK_ENA) + return -EBUSY; + + if (value >= control->pdata.num_vss_hpf_cfgs) + return -EINVAL; + + wm8994->vss_hpf_cfg = value; + + return 0; +} + +static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg; + + return 0; +} + +static int wm8958_vss_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm8958_vss_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int vss = kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wm8994->vss_ena[vss]; + + return 0; +} + +static int wm8958_vss_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int vss = kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0]) + return 0; + + if (ucontrol->value.integer.value[0] > 1) + return -EINVAL; + + if (!wm8994->mbc_vss) + return -ENODEV; + + if (wm8958_dsp2_busy(wm8994, vss)) { + dev_dbg(component->dev, "DSP2 active on %d already\n", vss); + return -EBUSY; + } + + if (wm8994->enh_eq_ena[vss]) + return -EBUSY; + + wm8994->vss_ena[vss] = ucontrol->value.integer.value[0]; + + wm8958_dsp_apply(component, vss, wm8994->vss_ena[vss]); + + return 1; +} + + +#define WM8958_VSS_SWITCH(xname, xval) {\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .info = wm8958_vss_info, \ + .get = wm8958_vss_get, .put = wm8958_vss_put, \ + .private_value = xval } + +static int wm8958_hpf_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm8958_hpf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int hpf = kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (hpf < 3) + ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3]; + else + ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3]; + + return 0; +} + +static int wm8958_hpf_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int hpf = kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (hpf < 3) { + if (wm8994->hpf1_ena[hpf % 3] == + ucontrol->value.integer.value[0]) + return 0; + } else { + if (wm8994->hpf2_ena[hpf % 3] == + ucontrol->value.integer.value[0]) + return 0; + } + + if (ucontrol->value.integer.value[0] > 1) + return -EINVAL; + + if (!wm8994->mbc_vss) + return -ENODEV; + + if (wm8958_dsp2_busy(wm8994, hpf % 3)) { + dev_dbg(component->dev, "DSP2 active on %d already\n", hpf); + return -EBUSY; + } + + if (wm8994->enh_eq_ena[hpf % 3]) + return -EBUSY; + + if (hpf < 3) + wm8994->hpf1_ena[hpf % 3] = ucontrol->value.integer.value[0]; + else + wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0]; + + wm8958_dsp_apply(component, hpf % 3, ucontrol->value.integer.value[0]); + + return 1; +} + +#define WM8958_HPF_SWITCH(xname, xval) {\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .info = wm8958_hpf_info, \ + .get = wm8958_hpf_get, .put = wm8958_hpf_put, \ + .private_value = xval } + +static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int value = ucontrol->value.enumerated.item[0]; + int reg; + + /* Don't allow on the fly reconfiguration */ + reg = snd_soc_component_read(component, WM8994_CLOCKING_1); + if (reg < 0 || reg & WM8958_DSP2CLK_ENA) + return -EBUSY; + + if (value >= control->pdata.num_enh_eq_cfgs) + return -EINVAL; + + wm8994->enh_eq_cfg = value; + + return 0; +} + +static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg; + + return 0; +} + +static int wm8958_enh_eq_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq = kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq]; + + return 0; +} + +static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int eq = kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0]) + return 0; + + if (ucontrol->value.integer.value[0] > 1) + return -EINVAL; + + if (!wm8994->enh_eq) + return -ENODEV; + + if (wm8958_dsp2_busy(wm8994, eq)) { + dev_dbg(component->dev, "DSP2 active on %d already\n", eq); + return -EBUSY; + } + + if (wm8994->mbc_ena[eq] || wm8994->vss_ena[eq] || + wm8994->hpf1_ena[eq] || wm8994->hpf2_ena[eq]) + return -EBUSY; + + wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0]; + + wm8958_dsp_apply(component, eq, ucontrol->value.integer.value[0]); + + return 1; +} + +#define WM8958_ENH_EQ_SWITCH(xname, xval) {\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .info = wm8958_enh_eq_info, \ + .get = wm8958_enh_eq_get, .put = wm8958_enh_eq_put, \ + .private_value = xval } + +static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = { +WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0), +WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1), +WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2), +}; + +static const struct snd_kcontrol_new wm8958_vss_snd_controls[] = { +WM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0), +WM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1), +WM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2), +WM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0), +WM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1), +WM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2), +WM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3), +WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4), +WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5), +}; + +static const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = { +WM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0), +WM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1), +WM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2), +}; + +static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context) +{ + struct snd_soc_component *component = context; + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) { + mutex_lock(&wm8994->fw_lock); + wm8994->enh_eq = fw; + mutex_unlock(&wm8994->fw_lock); + } +} + +static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context) +{ + struct snd_soc_component *component = context; + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) { + mutex_lock(&wm8994->fw_lock); + wm8994->mbc_vss = fw; + mutex_unlock(&wm8994->fw_lock); + } +} + +static void wm8958_mbc_loaded(const struct firmware *fw, void *context) +{ + struct snd_soc_component *component = context; + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) { + mutex_lock(&wm8994->fw_lock); + wm8994->mbc = fw; + mutex_unlock(&wm8994->fw_lock); + } +} + +void wm8958_dsp2_init(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + struct wm8994_pdata *pdata = &control->pdata; + int ret, i; + + wm8994->dsp_active = -1; + + snd_soc_add_component_controls(component, wm8958_mbc_snd_controls, + ARRAY_SIZE(wm8958_mbc_snd_controls)); + snd_soc_add_component_controls(component, wm8958_vss_snd_controls, + ARRAY_SIZE(wm8958_vss_snd_controls)); + snd_soc_add_component_controls(component, wm8958_enh_eq_snd_controls, + ARRAY_SIZE(wm8958_enh_eq_snd_controls)); + + + /* We don't *require* firmware and don't want to delay boot */ + request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + "wm8958_mbc.wfw", component->dev, GFP_KERNEL, + component, wm8958_mbc_loaded); + request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + "wm8958_mbc_vss.wfw", component->dev, GFP_KERNEL, + component, wm8958_mbc_vss_loaded); + request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + "wm8958_enh_eq.wfw", component->dev, GFP_KERNEL, + component, wm8958_enh_eq_loaded); + + if (pdata->num_mbc_cfgs) { + struct snd_kcontrol_new mbc_control[] = { + SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum, + wm8958_get_mbc_enum, wm8958_put_mbc_enum), + }; + + /* We need an array of texts for the enum API */ + wm8994->mbc_texts = kmalloc_array(pdata->num_mbc_cfgs, + sizeof(char *), + GFP_KERNEL); + if (!wm8994->mbc_texts) + return; + + for (i = 0; i < pdata->num_mbc_cfgs; i++) + wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name; + + wm8994->mbc_enum.items = pdata->num_mbc_cfgs; + wm8994->mbc_enum.texts = wm8994->mbc_texts; + + ret = snd_soc_add_component_controls(wm8994->hubs.component, + mbc_control, 1); + if (ret != 0) + dev_err(wm8994->hubs.component->dev, + "Failed to add MBC mode controls: %d\n", ret); + } + + if (pdata->num_vss_cfgs) { + struct snd_kcontrol_new vss_control[] = { + SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum, + wm8958_get_vss_enum, wm8958_put_vss_enum), + }; + + /* We need an array of texts for the enum API */ + wm8994->vss_texts = kmalloc_array(pdata->num_vss_cfgs, + sizeof(char *), + GFP_KERNEL); + if (!wm8994->vss_texts) + return; + + for (i = 0; i < pdata->num_vss_cfgs; i++) + wm8994->vss_texts[i] = pdata->vss_cfgs[i].name; + + wm8994->vss_enum.items = pdata->num_vss_cfgs; + wm8994->vss_enum.texts = wm8994->vss_texts; + + ret = snd_soc_add_component_controls(wm8994->hubs.component, + vss_control, 1); + if (ret != 0) + dev_err(wm8994->hubs.component->dev, + "Failed to add VSS mode controls: %d\n", ret); + } + + if (pdata->num_vss_hpf_cfgs) { + struct snd_kcontrol_new hpf_control[] = { + SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum, + wm8958_get_vss_hpf_enum, + wm8958_put_vss_hpf_enum), + }; + + /* We need an array of texts for the enum API */ + wm8994->vss_hpf_texts = kmalloc_array(pdata->num_vss_hpf_cfgs, + sizeof(char *), + GFP_KERNEL); + if (!wm8994->vss_hpf_texts) + return; + + for (i = 0; i < pdata->num_vss_hpf_cfgs; i++) + wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name; + + wm8994->vss_hpf_enum.items = pdata->num_vss_hpf_cfgs; + wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts; + + ret = snd_soc_add_component_controls(wm8994->hubs.component, + hpf_control, 1); + if (ret != 0) + dev_err(wm8994->hubs.component->dev, + "Failed to add VSS HPFmode controls: %d\n", + ret); + } + + if (pdata->num_enh_eq_cfgs) { + struct snd_kcontrol_new eq_control[] = { + SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum, + wm8958_get_enh_eq_enum, + wm8958_put_enh_eq_enum), + }; + + /* We need an array of texts for the enum API */ + wm8994->enh_eq_texts = kmalloc_array(pdata->num_enh_eq_cfgs, + sizeof(char *), + GFP_KERNEL); + if (!wm8994->enh_eq_texts) + return; + + for (i = 0; i < pdata->num_enh_eq_cfgs; i++) + wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name; + + wm8994->enh_eq_enum.items = pdata->num_enh_eq_cfgs; + wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts; + + ret = snd_soc_add_component_controls(wm8994->hubs.component, + eq_control, 1); + if (ret != 0) + dev_err(wm8994->hubs.component->dev, + "Failed to add enhanced EQ controls: %d\n", + ret); + } +} diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c new file mode 100644 index 0000000000..7689fe3cc8 --- /dev/null +++ b/sound/soc/codecs/wm8960.c @@ -0,0 +1,1589 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8960.c -- WM8960 ALSA SoC Audio driver + * + * Copyright 2007-11 Wolfson Microelectronics, plc + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8960.h" + +/* R25 - Power 1 */ +#define WM8960_VMID_MASK 0x180 +#define WM8960_VREF 0x40 + +/* R26 - Power 2 */ +#define WM8960_PWR2_LOUT1 0x40 +#define WM8960_PWR2_ROUT1 0x20 +#define WM8960_PWR2_OUT3 0x02 + +/* R28 - Anti-pop 1 */ +#define WM8960_POBCTRL 0x80 +#define WM8960_BUFDCOPEN 0x10 +#define WM8960_BUFIOEN 0x08 +#define WM8960_SOFT_ST 0x04 +#define WM8960_HPSTBY 0x01 + +/* R29 - Anti-pop 2 */ +#define WM8960_DISOP 0x40 +#define WM8960_DRES_MASK 0x30 + +#define WM8960_DSCH_TOUT 600 /* discharge timeout, ms */ + +static bool is_pll_freq_available(unsigned int source, unsigned int target); +static int wm8960_set_pll(struct snd_soc_component *component, + unsigned int freq_in, unsigned int freq_out); +/* + * wm8960 register cache + * We can't read the WM8960 register space when we are + * using 2 wire for device control, so we cache them instead. + */ +static const struct reg_default wm8960_reg_defaults[] = { + { 0x0, 0x00a7 }, + { 0x1, 0x00a7 }, + { 0x2, 0x0000 }, + { 0x3, 0x0000 }, + { 0x4, 0x0000 }, + { 0x5, 0x0008 }, + { 0x6, 0x0000 }, + { 0x7, 0x000a }, + { 0x8, 0x01c0 }, + { 0x9, 0x0000 }, + { 0xa, 0x00ff }, + { 0xb, 0x00ff }, + + { 0x10, 0x0000 }, + { 0x11, 0x007b }, + { 0x12, 0x0100 }, + { 0x13, 0x0032 }, + { 0x14, 0x0000 }, + { 0x15, 0x00c3 }, + { 0x16, 0x00c3 }, + { 0x17, 0x01c0 }, + { 0x18, 0x0000 }, + { 0x19, 0x0000 }, + { 0x1a, 0x0000 }, + { 0x1b, 0x0000 }, + { 0x1c, 0x0000 }, + { 0x1d, 0x0000 }, + + { 0x20, 0x0100 }, + { 0x21, 0x0100 }, + { 0x22, 0x0050 }, + + { 0x25, 0x0050 }, + { 0x26, 0x0000 }, + { 0x27, 0x0000 }, + { 0x28, 0x0000 }, + { 0x29, 0x0000 }, + { 0x2a, 0x0040 }, + { 0x2b, 0x0000 }, + { 0x2c, 0x0000 }, + { 0x2d, 0x0050 }, + { 0x2e, 0x0050 }, + { 0x2f, 0x0000 }, + { 0x30, 0x0002 }, + { 0x31, 0x0037 }, + + { 0x33, 0x0080 }, + { 0x34, 0x0008 }, + { 0x35, 0x0031 }, + { 0x36, 0x0026 }, + { 0x37, 0x00e9 }, +}; + +static bool wm8960_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8960_RESET: + return true; + default: + return false; + } +} + +#define WM8960_NUM_SUPPLIES 5 +static const char *wm8960_supply_names[WM8960_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "AVDD", + "SPKVDD1", + "SPKVDD2", +}; + +struct wm8960_priv { + struct clk *mclk; + struct regmap *regmap; + int (*set_bias_level)(struct snd_soc_component *, + enum snd_soc_bias_level level); + struct snd_soc_dapm_widget *lout1; + struct snd_soc_dapm_widget *rout1; + struct snd_soc_dapm_widget *out3; + bool deemph; + int lrclk; + int bclk; + int sysclk; + int clk_id; + int freq_in; + bool is_stream_in_use[2]; + struct wm8960_data pdata; + ktime_t dsch_start; + struct regulator_bulk_data supplies[WM8960_NUM_SUPPLIES]; +}; + +#define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0) + +/* enumerated controls */ +static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted", + "Right Inverted", "Stereo Inversion"}; +static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"}; +static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"}; +static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"}; +static const char *wm8960_alcmode[] = {"ALC", "Limiter"}; +static const char *wm8960_adc_data_output_sel[] = { + "Left Data = Left ADC; Right Data = Right ADC", + "Left Data = Left ADC; Right Data = Left ADC", + "Left Data = Right ADC; Right Data = Right ADC", + "Left Data = Right ADC; Right Data = Left ADC", +}; +static const char *wm8960_dmonomix[] = {"Stereo", "Mono"}; +static const char *wm8960_dacslope[] = {"Normal", "Sloping"}; + +static const struct soc_enum wm8960_enum[] = { + SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), + SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity), + SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff), + SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff), + SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc), + SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), + SOC_ENUM_SINGLE(WM8960_ADDCTL1, 2, 4, wm8960_adc_data_output_sel), + SOC_ENUM_SINGLE(WM8960_ADDCTL1, 4, 2, wm8960_dmonomix), + SOC_ENUM_SINGLE(WM8960_DACCTL2, 1, 2, wm8960_dacslope), +}; + +static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; + +static int wm8960_set_deemph(struct snd_soc_component *component) +{ + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8960->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { + if (abs(deemph_settings[i] - wm8960->lrclk) < + abs(deemph_settings[best] - wm8960->lrclk)) + best = i; + } + + val = best << 1; + } else { + val = 0; + } + + dev_dbg(component->dev, "Set deemphasis %d\n", val); + + return snd_soc_component_update_bits(component, WM8960_DACCTL1, + 0x6, val); +} + +static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wm8960->deemph; + return 0; +} + +static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + unsigned int deemph = ucontrol->value.integer.value[0]; + + if (deemph > 1) + return -EINVAL; + + wm8960->deemph = deemph; + + return wm8960_set_deemph(component); +} + +static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(lineinboost_tlv, -1500, 300, 1); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(micboost_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 1300, 0), + 2, 3, TLV_DB_SCALE_ITEM(2000, 900, 0), +); + +static const struct snd_kcontrol_new wm8960_snd_controls[] = { +SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, + 0, 63, 0, inpga_tlv), +SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, + 6, 1, 0), +SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, + 7, 1, 1), + +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", + WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv), +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", + WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv), +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", + WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv), +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", + WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv), +SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume", + WM8960_RINPATH, 4, 3, 0, micboost_tlv), +SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT1 Volume", + WM8960_LINPATH, 4, 3, 0, micboost_tlv), + +SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, + 0, 255, 0, dac_tlv), + +SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1, + 0, 127, 0, out_tlv), +SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1, + 7, 1, 0), + +SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2, + 0, 127, 0, out_tlv), +SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2, + 7, 1, 0), +SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0), +SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), + +SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), +SOC_ENUM("ADC Polarity", wm8960_enum[0]), +SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), + +SOC_ENUM("DAC Polarity", wm8960_enum[1]), +SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, + wm8960_get_deemph, wm8960_put_deemph), + +SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), +SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), +SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), +SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), + +SOC_ENUM("ALC Function", wm8960_enum[4]), +SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), +SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), +SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), +SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), +SOC_ENUM("ALC Mode", wm8960_enum[5]), +SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), +SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), + +SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0), +SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0), + +SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC, + 0, 255, 0, adc_tlv), + +SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume", + WM8960_BYPASS1, 4, 7, 1, bypass_tlv), +SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume", + WM8960_LOUTMIX, 4, 7, 1, bypass_tlv), +SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume", + WM8960_BYPASS2, 4, 7, 1, bypass_tlv), +SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume", + WM8960_ROUTMIX, 4, 7, 1, bypass_tlv), + +SOC_ENUM("ADC Data Output Select", wm8960_enum[6]), +SOC_ENUM("DAC Mono Mix", wm8960_enum[7]), +SOC_ENUM("DAC Filter Characteristics", wm8960_enum[8]), +}; + +static const struct snd_kcontrol_new wm8960_lin_boost[] = { +SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0), +SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0), +SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_lin[] = { +SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_rin_boost[] = { +SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0), +SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0), +SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_rin[] = { +SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_loutput_mixer[] = { +SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0), +SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0), +SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_routput_mixer[] = { +SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0), +SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0), +SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0), +}; + +static const struct snd_kcontrol_new wm8960_mono_out[] = { +SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("LINPUT1"), +SND_SOC_DAPM_INPUT("RINPUT1"), +SND_SOC_DAPM_INPUT("LINPUT2"), +SND_SOC_DAPM_INPUT("RINPUT2"), +SND_SOC_DAPM_INPUT("LINPUT3"), +SND_SOC_DAPM_INPUT("RINPUT3"), + +SND_SOC_DAPM_SUPPLY("MICB", WM8960_POWER1, 1, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0, + wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)), +SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0, + wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)), + +SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0, + wm8960_lin, ARRAY_SIZE(wm8960_lin)), +SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0, + wm8960_rin, ARRAY_SIZE(wm8960_rin)), + +SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER1, 3, 0), +SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER1, 2, 0), + +SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0), +SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0), + +SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0, + &wm8960_loutput_mixer[0], + ARRAY_SIZE(wm8960_loutput_mixer)), +SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0, + &wm8960_routput_mixer[0], + ARRAY_SIZE(wm8960_routput_mixer)), + +SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0), + +SND_SOC_DAPM_OUTPUT("SPK_LP"), +SND_SOC_DAPM_OUTPUT("SPK_LN"), +SND_SOC_DAPM_OUTPUT("HP_L"), +SND_SOC_DAPM_OUTPUT("HP_R"), +SND_SOC_DAPM_OUTPUT("SPK_RP"), +SND_SOC_DAPM_OUTPUT("SPK_RN"), +SND_SOC_DAPM_OUTPUT("OUT3"), +}; + +static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = { +SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0, + &wm8960_mono_out[0], + ARRAY_SIZE(wm8960_mono_out)), +}; + +/* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */ +static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = { +SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route audio_paths[] = { + { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" }, + { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" }, + { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" }, + + { "Left Input Mixer", "Boost Switch", "Left Boost Mixer" }, + { "Left Input Mixer", "Boost Switch", "LINPUT1" }, /* Really Boost Switch */ + { "Left Input Mixer", NULL, "LINPUT2" }, + { "Left Input Mixer", NULL, "LINPUT3" }, + + { "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" }, + { "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" }, + { "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" }, + + { "Right Input Mixer", "Boost Switch", "Right Boost Mixer" }, + { "Right Input Mixer", "Boost Switch", "RINPUT1" }, /* Really Boost Switch */ + { "Right Input Mixer", NULL, "RINPUT2" }, + { "Right Input Mixer", NULL, "RINPUT3" }, + + { "Left ADC", NULL, "Left Input Mixer" }, + { "Right ADC", NULL, "Right Input Mixer" }, + + { "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" }, + { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer" }, + { "Left Output Mixer", "PCM Playback Switch", "Left DAC" }, + + { "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" }, + { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" }, + { "Right Output Mixer", "PCM Playback Switch", "Right DAC" }, + + { "LOUT1 PGA", NULL, "Left Output Mixer" }, + { "ROUT1 PGA", NULL, "Right Output Mixer" }, + + { "HP_L", NULL, "LOUT1 PGA" }, + { "HP_R", NULL, "ROUT1 PGA" }, + + { "Left Speaker PGA", NULL, "Left Output Mixer" }, + { "Right Speaker PGA", NULL, "Right Output Mixer" }, + + { "Left Speaker Output", NULL, "Left Speaker PGA" }, + { "Right Speaker Output", NULL, "Right Speaker PGA" }, + + { "SPK_LN", NULL, "Left Speaker Output" }, + { "SPK_LP", NULL, "Left Speaker Output" }, + { "SPK_RN", NULL, "Right Speaker Output" }, + { "SPK_RP", NULL, "Right Speaker Output" }, +}; + +static const struct snd_soc_dapm_route audio_paths_out3[] = { + { "Mono Output Mixer", "Left Switch", "Left Output Mixer" }, + { "Mono Output Mixer", "Right Switch", "Right Output Mixer" }, + + { "OUT3", NULL, "Mono Output Mixer", } +}; + +static const struct snd_soc_dapm_route audio_paths_capless[] = { + { "HP_L", NULL, "OUT3 VMID" }, + { "HP_R", NULL, "OUT3 VMID" }, + + { "OUT3 VMID", NULL, "Left Output Mixer" }, + { "OUT3 VMID", NULL, "Right Output Mixer" }, +}; + +static int wm8960_add_widgets(struct snd_soc_component *component) +{ + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + struct wm8960_data *pdata = &wm8960->pdata; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct snd_soc_dapm_widget *w; + + snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, + ARRAY_SIZE(wm8960_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); + + /* In capless mode OUT3 is used to provide VMID for the + * headphone outputs, otherwise it is used as a mono mixer. + */ + if (pdata && pdata->capless) { + snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless, + ARRAY_SIZE(wm8960_dapm_widgets_capless)); + + snd_soc_dapm_add_routes(dapm, audio_paths_capless, + ARRAY_SIZE(audio_paths_capless)); + } else { + snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3, + ARRAY_SIZE(wm8960_dapm_widgets_out3)); + + snd_soc_dapm_add_routes(dapm, audio_paths_out3, + ARRAY_SIZE(audio_paths_out3)); + } + + /* We need to power up the headphone output stage out of + * sequence for capless mode. To save scanning the widget + * list each time to find the desired power state do so now + * and save the result. + */ + list_for_each_entry(w, &component->card->widgets, list) { + if (w->dapm != dapm) + continue; + if (strcmp(w->name, "LOUT1 PGA") == 0) + wm8960->lout1 = w; + if (strcmp(w->name, "ROUT1 PGA") == 0) + wm8960->rout1 = w; + if (strcmp(w->name, "OUT3 VMID") == 0) + wm8960->out3 = w; + } + + return 0; +} + +static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface |= 0x0040; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + /* set iface */ + snd_soc_component_write(component, WM8960_IFACE1, iface); + return 0; +} + +static struct { + int rate; + unsigned int val; +} alc_rates[] = { + { 48000, 0 }, + { 44100, 0 }, + { 32000, 1 }, + { 22050, 2 }, + { 24000, 2 }, + { 16000, 3 }, + { 11025, 4 }, + { 12000, 4 }, + { 8000, 5 }, +}; + +/* -1 for reserved value */ +static const int sysclk_divs[] = { 1, -1, 2, -1 }; + +/* Multiply 256 for internal 256 div */ +static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; + +/* Multiply 10 to eliminate decimials */ +static const int bclk_divs[] = { + 10, 15, 20, 30, 40, 55, 60, 80, 110, + 120, 160, 220, 240, 320, 320, 320 +}; + +/** + * wm8960_configure_sysclk - checks if there is a sysclk frequency available + * The sysclk must be chosen such that: + * - sysclk = MCLK / sysclk_divs + * - lrclk = sysclk / dac_divs + * - 10 * bclk = sysclk / bclk_divs + * + * @wm8960: codec private data + * @mclk: MCLK used to derive sysclk + * @sysclk_idx: sysclk_divs index for found sysclk + * @dac_idx: dac_divs index for found lrclk + * @bclk_idx: bclk_divs index for found bclk + * + * Returns: + * -1, in case no sysclk frequency available found + * >=0, in case we could derive bclk and lrclk from sysclk using + * (@sysclk_idx, @dac_idx, @bclk_idx) dividers + */ +static +int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk, + int *sysclk_idx, int *dac_idx, int *bclk_idx) +{ + int sysclk, bclk, lrclk; + int i, j, k; + int diff; + + /* marker for no match */ + *bclk_idx = -1; + + bclk = wm8960->bclk; + lrclk = wm8960->lrclk; + + /* check if the sysclk frequency is available. */ + for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + if (sysclk_divs[i] == -1) + continue; + sysclk = mclk / sysclk_divs[i]; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { + if (sysclk != dac_divs[j] * lrclk) + continue; + for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) { + diff = sysclk - bclk * bclk_divs[k] / 10; + if (diff == 0) { + *sysclk_idx = i; + *dac_idx = j; + *bclk_idx = k; + break; + } + } + if (k != ARRAY_SIZE(bclk_divs)) + break; + } + if (j != ARRAY_SIZE(dac_divs)) + break; + } + return *bclk_idx; +} + +/** + * wm8960_configure_pll - checks if there is a PLL out frequency available + * The PLL out frequency must be chosen such that: + * - sysclk = lrclk * dac_divs + * - freq_out = sysclk * sysclk_divs + * - 10 * sysclk = bclk * bclk_divs + * + * If we cannot find an exact match for (sysclk, lrclk, bclk) + * triplet, we relax the bclk such that bclk is chosen as the + * closest available frequency greater than expected bclk. + * + * @component: component structure + * @freq_in: input frequency used to derive freq out via PLL + * @sysclk_idx: sysclk_divs index for found sysclk + * @dac_idx: dac_divs index for found lrclk + * @bclk_idx: bclk_divs index for found bclk + * + * Returns: + * < 0, in case no PLL frequency out available was found + * >=0, in case we could derive bclk, lrclk, sysclk from PLL out using + * (@sysclk_idx, @dac_idx, @bclk_idx) dividers + */ +static +int wm8960_configure_pll(struct snd_soc_component *component, int freq_in, + int *sysclk_idx, int *dac_idx, int *bclk_idx) +{ + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + int sysclk, bclk, lrclk, freq_out; + int diff, closest, best_freq_out; + int i, j, k; + + bclk = wm8960->bclk; + lrclk = wm8960->lrclk; + closest = freq_in; + + best_freq_out = -EINVAL; + *sysclk_idx = *dac_idx = *bclk_idx = -1; + + /* + * From Datasheet, the PLL performs best when f2 is between + * 90MHz and 100MHz, the desired sysclk output is 11.2896MHz + * or 12.288MHz, then sysclkdiv = 2 is the best choice. + * So search sysclk_divs from 2 to 1 other than from 1 to 2. + */ + for (i = ARRAY_SIZE(sysclk_divs) - 1; i >= 0; --i) { + if (sysclk_divs[i] == -1) + continue; + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { + sysclk = lrclk * dac_divs[j]; + freq_out = sysclk * sysclk_divs[i]; + + for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) { + if (!is_pll_freq_available(freq_in, freq_out)) + continue; + + diff = sysclk - bclk * bclk_divs[k] / 10; + if (diff == 0) { + *sysclk_idx = i; + *dac_idx = j; + *bclk_idx = k; + return freq_out; + } + if (diff > 0 && closest > diff) { + *sysclk_idx = i; + *dac_idx = j; + *bclk_idx = k; + closest = diff; + best_freq_out = freq_out; + } + } + } + } + + return best_freq_out; +} +static int wm8960_configure_clocking(struct snd_soc_component *component) +{ + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + int freq_out, freq_in; + u16 iface1 = snd_soc_component_read(component, WM8960_IFACE1); + int i, j, k; + int ret; + + /* + * For Slave mode clocking should still be configured, + * so this if statement should be removed, but some platform + * may not work if the sysclk is not configured, to avoid such + * compatible issue, just add '!wm8960->sysclk' condition in + * this if statement. + */ + if (!(iface1 & (1 << 6)) && !wm8960->sysclk) { + dev_warn(component->dev, + "slave mode, but proceeding with no clock configuration\n"); + return 0; + } + + if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) { + dev_err(component->dev, "No MCLK configured\n"); + return -EINVAL; + } + + freq_in = wm8960->freq_in; + /* + * If it's sysclk auto mode, check if the MCLK can provide sysclk or + * not. If MCLK can provide sysclk, using MCLK to provide sysclk + * directly. Otherwise, auto select a available pll out frequency + * and set PLL. + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO) { + /* disable the PLL and using MCLK to provide sysclk */ + wm8960_set_pll(component, 0, 0); + freq_out = freq_in; + } else if (wm8960->sysclk) { + freq_out = wm8960->sysclk; + } else { + dev_err(component->dev, "No SYSCLK configured\n"); + return -EINVAL; + } + + if (wm8960->clk_id != WM8960_SYSCLK_PLL) { + ret = wm8960_configure_sysclk(wm8960, freq_out, &i, &j, &k); + if (ret >= 0) { + goto configure_clock; + } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { + dev_err(component->dev, "failed to configure clock\n"); + return -EINVAL; + } + } + + freq_out = wm8960_configure_pll(component, freq_in, &i, &j, &k); + if (freq_out < 0) { + dev_err(component->dev, "failed to configure clock via PLL\n"); + return freq_out; + } + wm8960_set_pll(component, freq_in, freq_out); + +configure_clock: + /* configure sysclk clock */ + snd_soc_component_update_bits(component, WM8960_CLOCK1, 3 << 1, i << 1); + + /* configure frame clock */ + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x7 << 3, j << 3); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x7 << 6, j << 6); + + /* configure bit clock */ + snd_soc_component_update_bits(component, WM8960_CLOCK2, 0xf, k); + + return 0; +} + +static int wm8960_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + u16 iface = snd_soc_component_read(component, WM8960_IFACE1) & 0xfff3; + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + int i; + + wm8960->bclk = snd_soc_params_to_bclk(params); + if (params_channels(params) == 1) + wm8960->bclk *= 2; + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x0004; + break; + case 24: + iface |= 0x0008; + break; + case 32: + /* right justify mode does not support 32 word length */ + if ((iface & 0x3) != 0) { + iface |= 0x000c; + break; + } + fallthrough; + default: + dev_err(component->dev, "unsupported width %d\n", + params_width(params)); + return -EINVAL; + } + + wm8960->lrclk = params_rate(params); + /* Update filters for the new rate */ + if (tx) { + wm8960_set_deemph(component); + } else { + for (i = 0; i < ARRAY_SIZE(alc_rates); i++) + if (alc_rates[i].rate == params_rate(params)) + snd_soc_component_update_bits(component, + WM8960_ADDCTL3, 0x7, + alc_rates[i].val); + } + + /* set iface */ + snd_soc_component_write(component, WM8960_IFACE1, iface); + + wm8960->is_stream_in_use[tx] = true; + + if (!wm8960->is_stream_in_use[!tx]) + return wm8960_configure_clocking(component); + + return 0; +} + +static int wm8960_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + wm8960->is_stream_in_use[tx] = false; + + return 0; +} + +static int wm8960_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + if (mute) + snd_soc_component_update_bits(component, WM8960_DACCTL1, 0x8, 0x8); + else + snd_soc_component_update_bits(component, WM8960_DACCTL1, 0x8, 0); + return 0; +} + +static int wm8960_set_bias_level_out3(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + u16 pm2 = snd_soc_component_read(component, WM8960_POWER2); + int ret; + ktime_t tout; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + switch (snd_soc_component_get_bias_level(component)) { + case SND_SOC_BIAS_STANDBY: + if (!IS_ERR(wm8960->mclk)) { + ret = clk_prepare_enable(wm8960->mclk); + if (ret) { + dev_err(component->dev, + "Failed to enable MCLK: %d\n", + ret); + return ret; + } + } + + ret = wm8960_configure_clocking(component); + if (ret) + return ret; + + /* Set VMID to 2x50k */ + snd_soc_component_update_bits(component, WM8960_POWER1, 0x180, 0x80); + break; + + case SND_SOC_BIAS_ON: + /* + * If it's sysclk auto mode, and the pll is enabled, + * disable the pll + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) + wm8960_set_pll(component, 0, 0); + + if (!IS_ERR(wm8960->mclk)) + clk_disable_unprepare(wm8960->mclk); + break; + + default: + break; + } + + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* ensure discharge is complete */ + tout = WM8960_DSCH_TOUT - ktime_ms_delta(ktime_get(), wm8960->dsch_start); + if (tout > 0) + msleep(tout); + + regcache_sync(wm8960->regmap); + + /* Enable anti-pop features */ + snd_soc_component_write(component, WM8960_APOP1, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN | WM8960_BUFIOEN); + + /* Enable & ramp VMID at 2x50k */ + snd_soc_component_update_bits(component, WM8960_POWER1, 0x80, 0x80); + msleep(100); + + /* Enable VREF */ + snd_soc_component_update_bits(component, WM8960_POWER1, WM8960_VREF, + WM8960_VREF); + + /* Disable anti-pop features */ + snd_soc_component_write(component, WM8960_APOP1, WM8960_BUFIOEN); + } + + /* Set VMID to 2x250k */ + snd_soc_component_update_bits(component, WM8960_POWER1, 0x180, 0x100); + break; + + case SND_SOC_BIAS_OFF: + /* Enable anti-pop features */ + snd_soc_component_write(component, WM8960_APOP1, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN | WM8960_BUFIOEN); + + /* Disable VMID and VREF, mark discharge */ + snd_soc_component_write(component, WM8960_POWER1, 0); + wm8960->dsch_start = ktime_get(); + break; + } + + return 0; +} + +static int wm8960_set_bias_level_capless(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + u16 pm2 = snd_soc_component_read(component, WM8960_POWER2); + int reg, ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + switch (snd_soc_component_get_bias_level(component)) { + case SND_SOC_BIAS_STANDBY: + /* Enable anti pop mode */ + snd_soc_component_update_bits(component, WM8960_APOP1, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN); + + /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */ + reg = 0; + if (wm8960->lout1 && wm8960->lout1->power) + reg |= WM8960_PWR2_LOUT1; + if (wm8960->rout1 && wm8960->rout1->power) + reg |= WM8960_PWR2_ROUT1; + if (wm8960->out3 && wm8960->out3->power) + reg |= WM8960_PWR2_OUT3; + snd_soc_component_update_bits(component, WM8960_POWER2, + WM8960_PWR2_LOUT1 | + WM8960_PWR2_ROUT1 | + WM8960_PWR2_OUT3, reg); + + /* Enable VMID at 2*50k */ + snd_soc_component_update_bits(component, WM8960_POWER1, + WM8960_VMID_MASK, 0x80); + + /* Ramp */ + msleep(100); + + /* Enable VREF */ + snd_soc_component_update_bits(component, WM8960_POWER1, + WM8960_VREF, WM8960_VREF); + + msleep(100); + + if (!IS_ERR(wm8960->mclk)) { + ret = clk_prepare_enable(wm8960->mclk); + if (ret) { + dev_err(component->dev, + "Failed to enable MCLK: %d\n", + ret); + return ret; + } + } + + ret = wm8960_configure_clocking(component); + if (ret) + return ret; + + break; + + case SND_SOC_BIAS_ON: + /* + * If it's sysclk auto mode, and the pll is enabled, + * disable the pll + */ + if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) + wm8960_set_pll(component, 0, 0); + + if (!IS_ERR(wm8960->mclk)) + clk_disable_unprepare(wm8960->mclk); + + /* Enable anti-pop mode */ + snd_soc_component_update_bits(component, WM8960_APOP1, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN); + + /* Disable VMID and VREF */ + snd_soc_component_update_bits(component, WM8960_POWER1, + WM8960_VREF | WM8960_VMID_MASK, 0); + break; + + case SND_SOC_BIAS_OFF: + regcache_sync(wm8960->regmap); + break; + default: + break; + } + break; + + case SND_SOC_BIAS_STANDBY: + switch (snd_soc_component_get_bias_level(component)) { + case SND_SOC_BIAS_PREPARE: + /* Disable HP discharge */ + snd_soc_component_update_bits(component, WM8960_APOP2, + WM8960_DISOP | WM8960_DRES_MASK, + 0); + + /* Disable anti-pop features */ + snd_soc_component_update_bits(component, WM8960_APOP1, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN, + WM8960_POBCTRL | WM8960_SOFT_ST | + WM8960_BUFDCOPEN); + break; + + default: + break; + } + break; + + case SND_SOC_BIAS_OFF: + break; + } + + return 0; +} + +/* PLL divisors */ +struct _pll_div { + u32 pre_div:1; + u32 n:4; + u32 k:24; +}; + +static bool is_pll_freq_available(unsigned int source, unsigned int target) +{ + unsigned int Ndiv; + + if (source == 0 || target == 0) + return false; + + /* Scale up target to PLL operating frequency */ + target *= 4; + Ndiv = target / source; + + if (Ndiv < 6) { + source >>= 1; + Ndiv = target / source; + } + + if ((Ndiv < 6) || (Ndiv > 12)) + return false; + + return true; +} + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 24) * 10) + +static int pll_factors(unsigned int source, unsigned int target, + struct _pll_div *pll_div) +{ + unsigned long long Kpart; + unsigned int K, Ndiv, Nmod; + + pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target); + + /* Scale up target to PLL operating frequency */ + target *= 4; + + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div->pre_div = 1; + Ndiv = target / source; + } else + pll_div->pre_div = 0; + + if ((Ndiv < 6) || (Ndiv > 12)) { + pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv); + return -EINVAL; + } + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; + + pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n", + pll_div->n, pll_div->k, pll_div->pre_div); + + return 0; +} + +static int wm8960_set_pll(struct snd_soc_component *component, + unsigned int freq_in, unsigned int freq_out) +{ + u16 reg; + static struct _pll_div pll_div; + int ret; + + if (freq_in && freq_out) { + ret = pll_factors(freq_in, freq_out, &pll_div); + if (ret != 0) + return ret; + } + + /* Disable the PLL: even if we are changing the frequency the + * PLL needs to be disabled while we do so. */ + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, 0); + snd_soc_component_update_bits(component, WM8960_POWER2, 0x1, 0); + + if (!freq_in || !freq_out) + return 0; + + reg = snd_soc_component_read(component, WM8960_PLL1) & ~0x3f; + reg |= pll_div.pre_div << 4; + reg |= pll_div.n; + + if (pll_div.k) { + reg |= 0x20; + + snd_soc_component_write(component, WM8960_PLL2, (pll_div.k >> 16) & 0xff); + snd_soc_component_write(component, WM8960_PLL3, (pll_div.k >> 8) & 0xff); + snd_soc_component_write(component, WM8960_PLL4, pll_div.k & 0xff); + } + snd_soc_component_write(component, WM8960_PLL1, reg); + + /* Turn it on */ + snd_soc_component_update_bits(component, WM8960_POWER2, 0x1, 0x1); + msleep(250); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, 0x1); + + return 0; +} + +static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + + wm8960->freq_in = freq_in; + + if (pll_id == WM8960_SYSCLK_AUTO) + return 0; + + return wm8960_set_pll(component, freq_in, freq_out); +} + +static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + + switch (div_id) { + case WM8960_SYSCLKDIV: + reg = snd_soc_component_read(component, WM8960_CLOCK1) & 0x1f9; + snd_soc_component_write(component, WM8960_CLOCK1, reg | div); + break; + case WM8960_DACDIV: + reg = snd_soc_component_read(component, WM8960_CLOCK1) & 0x1c7; + snd_soc_component_write(component, WM8960_CLOCK1, reg | div); + break; + case WM8960_OPCLKDIV: + reg = snd_soc_component_read(component, WM8960_PLL1) & 0x03f; + snd_soc_component_write(component, WM8960_PLL1, reg | div); + break; + case WM8960_DCLKDIV: + reg = snd_soc_component_read(component, WM8960_CLOCK2) & 0x03f; + snd_soc_component_write(component, WM8960_CLOCK2, reg | div); + break; + case WM8960_TOCLKSEL: + reg = snd_soc_component_read(component, WM8960_ADDCTL1) & 0x1fd; + snd_soc_component_write(component, WM8960_ADDCTL1, reg | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8960_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + + return wm8960->set_bias_level(component, level); +} + +static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case WM8960_SYSCLK_MCLK: + snd_soc_component_update_bits(component, WM8960_CLOCK1, + 0x1, WM8960_SYSCLK_MCLK); + break; + case WM8960_SYSCLK_PLL: + snd_soc_component_update_bits(component, WM8960_CLOCK1, + 0x1, WM8960_SYSCLK_PLL); + break; + case WM8960_SYSCLK_AUTO: + break; + default: + return -EINVAL; + } + + wm8960->sysclk = freq; + wm8960->clk_id = clk_id; + + return 0; +} + +#define WM8960_RATES SNDRV_PCM_RATE_8000_48000 + +#define WM8960_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8960_dai_ops = { + .hw_params = wm8960_hw_params, + .hw_free = wm8960_hw_free, + .mute_stream = wm8960_mute, + .set_fmt = wm8960_set_dai_fmt, + .set_clkdiv = wm8960_set_dai_clkdiv, + .set_pll = wm8960_set_dai_pll, + .set_sysclk = wm8960_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8960_dai = { + .name = "wm8960-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8960_RATES, + .formats = WM8960_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8960_RATES, + .formats = WM8960_FORMATS,}, + .ops = &wm8960_dai_ops, + .symmetric_rate = 1, +}; + +static int wm8960_probe(struct snd_soc_component *component) +{ + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + struct wm8960_data *pdata = &wm8960->pdata; + + if (pdata->capless) + wm8960->set_bias_level = wm8960_set_bias_level_capless; + else + wm8960->set_bias_level = wm8960_set_bias_level_out3; + + snd_soc_add_component_controls(component, wm8960_snd_controls, + ARRAY_SIZE(wm8960_snd_controls)); + wm8960_add_widgets(component); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8960 = { + .probe = wm8960_probe, + .set_bias_level = wm8960_set_bias_level, + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8960_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8960_PLL4, + + .reg_defaults = wm8960_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm8960_volatile, +}; + +static void wm8960_set_pdata_from_of(struct i2c_client *i2c, + struct wm8960_data *pdata) +{ + const struct device_node *np = i2c->dev.of_node; + + if (of_property_read_bool(np, "wlf,capless")) + pdata->capless = true; + + if (of_property_read_bool(np, "wlf,shared-lrclk")) + pdata->shared_lrclk = true; + + of_property_read_u32_array(np, "wlf,gpio-cfg", pdata->gpio_cfg, + ARRAY_SIZE(pdata->gpio_cfg)); + + of_property_read_u32_array(np, "wlf,hp-cfg", pdata->hp_cfg, + ARRAY_SIZE(pdata->hp_cfg)); +} + +static int wm8960_i2c_probe(struct i2c_client *i2c) +{ + struct wm8960_data *pdata = dev_get_platdata(&i2c->dev); + struct wm8960_priv *wm8960; + unsigned int i; + int ret; + u8 val; + + wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv), + GFP_KERNEL); + if (wm8960 == NULL) + return -ENOMEM; + + wm8960->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (IS_ERR(wm8960->mclk)) { + if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } else { + ret = clk_get_rate(wm8960->mclk); + if (ret >= 0) { + wm8960->freq_in = ret; + } else { + dev_err(&i2c->dev, "Failed to read MCLK rate: %d\n", + ret); + } + } + + for (i = 0; i < ARRAY_SIZE(wm8960->supplies); i++) + wm8960->supplies[i].supply = wm8960_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8960->supplies), + wm8960->supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8960->supplies), + wm8960->supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap); + if (IS_ERR(wm8960->regmap)) { + ret = PTR_ERR(wm8960->regmap); + goto bulk_disable; + } + + if (pdata) + memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data)); + else if (i2c->dev.of_node) + wm8960_set_pdata_from_of(i2c, &wm8960->pdata); + + ret = i2c_master_recv(i2c, &val, sizeof(val)); + if (ret >= 0) { + dev_err(&i2c->dev, "Not wm8960, wm8960 reg can not read by i2c\n"); + ret = -EINVAL; + goto bulk_disable; + } + + ret = wm8960_reset(wm8960->regmap); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to issue reset\n"); + goto bulk_disable; + } + + if (wm8960->pdata.shared_lrclk) { + ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, + 0x4, 0x4); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable LRCM: %d\n", + ret); + goto bulk_disable; + } + } + + /* Latch the update bits */ + regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100); + regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100); + + /* ADCLRC pin configured as GPIO. */ + regmap_update_bits(wm8960->regmap, WM8960_IFACE2, 1 << 6, + wm8960->pdata.gpio_cfg[0] << 6); + regmap_update_bits(wm8960->regmap, WM8960_ADDCTL4, 0xF << 4, + wm8960->pdata.gpio_cfg[1] << 4); + + /* Enable headphone jack detect */ + regmap_update_bits(wm8960->regmap, WM8960_ADDCTL4, 3 << 2, + wm8960->pdata.hp_cfg[0] << 2); + regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, 3 << 5, + wm8960->pdata.hp_cfg[1] << 5); + regmap_update_bits(wm8960->regmap, WM8960_ADDCTL1, 3, + wm8960->pdata.hp_cfg[2]); + + i2c_set_clientdata(i2c, wm8960); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8960, &wm8960_dai, 1); + if (ret) + goto bulk_disable; + + return 0; + +bulk_disable: + regulator_bulk_disable(ARRAY_SIZE(wm8960->supplies), wm8960->supplies); + return ret; +} + +static void wm8960_i2c_remove(struct i2c_client *client) +{ + struct wm8960_priv *wm8960 = i2c_get_clientdata(client); + + regulator_bulk_disable(ARRAY_SIZE(wm8960->supplies), wm8960->supplies); +} + +static const struct i2c_device_id wm8960_i2c_id[] = { + { "wm8960", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id wm8960_of_match[] = { + { .compatible = "wlf,wm8960", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8960_of_match); +#endif + +#if defined(CONFIG_ACPI) +static const struct acpi_device_id wm8960_acpi_match[] = { + { "1AEC8960", 0 }, /* Wolfson PCI ID + part ID */ + { "10138960", 0 }, /* Cirrus Logic PCI ID + part ID */ + { }, +}; +MODULE_DEVICE_TABLE(acpi, wm8960_acpi_match); +#endif + +static struct i2c_driver wm8960_i2c_driver = { + .driver = { + .name = "wm8960", + .of_match_table = of_match_ptr(wm8960_of_match), + .acpi_match_table = ACPI_PTR(wm8960_acpi_match), + }, + .probe = wm8960_i2c_probe, + .remove = wm8960_i2c_remove, + .id_table = wm8960_i2c_id, +}; + +module_i2c_driver(wm8960_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8960 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h new file mode 100644 index 0000000000..e8ff33b188 --- /dev/null +++ b/sound/soc/codecs/wm8960.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8960.h -- WM8960 Soc Audio driver + */ + +#ifndef _WM8960_H +#define _WM8960_H + +/* WM8960 register space */ + + +#define WM8960_CACHEREGNUM 56 + +#define WM8960_LINVOL 0x0 +#define WM8960_RINVOL 0x1 +#define WM8960_LOUT1 0x2 +#define WM8960_ROUT1 0x3 +#define WM8960_CLOCK1 0x4 +#define WM8960_DACCTL1 0x5 +#define WM8960_DACCTL2 0x6 +#define WM8960_IFACE1 0x7 +#define WM8960_CLOCK2 0x8 +#define WM8960_IFACE2 0x9 +#define WM8960_LDAC 0xa +#define WM8960_RDAC 0xb + +#define WM8960_RESET 0xf +#define WM8960_3D 0x10 +#define WM8960_ALC1 0x11 +#define WM8960_ALC2 0x12 +#define WM8960_ALC3 0x13 +#define WM8960_NOISEG 0x14 +#define WM8960_LADC 0x15 +#define WM8960_RADC 0x16 +#define WM8960_ADDCTL1 0x17 +#define WM8960_ADDCTL2 0x18 +#define WM8960_POWER1 0x19 +#define WM8960_POWER2 0x1a +#define WM8960_ADDCTL3 0x1b +#define WM8960_APOP1 0x1c +#define WM8960_APOP2 0x1d + +#define WM8960_LINPATH 0x20 +#define WM8960_RINPATH 0x21 +#define WM8960_LOUTMIX 0x22 + +#define WM8960_ROUTMIX 0x25 +#define WM8960_MONOMIX1 0x26 +#define WM8960_MONOMIX2 0x27 +#define WM8960_LOUT2 0x28 +#define WM8960_ROUT2 0x29 +#define WM8960_MONO 0x2a +#define WM8960_INBMIX1 0x2b +#define WM8960_INBMIX2 0x2c +#define WM8960_BYPASS1 0x2d +#define WM8960_BYPASS2 0x2e +#define WM8960_POWER3 0x2f +#define WM8960_ADDCTL4 0x30 +#define WM8960_CLASSD1 0x31 + +#define WM8960_CLASSD3 0x33 +#define WM8960_PLL1 0x34 +#define WM8960_PLL2 0x35 +#define WM8960_PLL3 0x36 +#define WM8960_PLL4 0x37 + + +/* + * WM8960 Clock dividers + */ +#define WM8960_SYSCLKDIV 0 +#define WM8960_DACDIV 1 +#define WM8960_OPCLKDIV 2 +#define WM8960_DCLKDIV 3 +#define WM8960_TOCLKSEL 4 + +#define WM8960_SYSCLK_DIV_1 (0 << 1) +#define WM8960_SYSCLK_DIV_2 (2 << 1) + +#define WM8960_SYSCLK_AUTO (0 << 0) +#define WM8960_SYSCLK_PLL (1 << 0) +#define WM8960_SYSCLK_MCLK (2 << 0) + +#define WM8960_DAC_DIV_1 (0 << 3) +#define WM8960_DAC_DIV_1_5 (1 << 3) +#define WM8960_DAC_DIV_2 (2 << 3) +#define WM8960_DAC_DIV_3 (3 << 3) +#define WM8960_DAC_DIV_4 (4 << 3) +#define WM8960_DAC_DIV_5_5 (5 << 3) +#define WM8960_DAC_DIV_6 (6 << 3) + +#define WM8960_DCLK_DIV_1_5 (0 << 6) +#define WM8960_DCLK_DIV_2 (1 << 6) +#define WM8960_DCLK_DIV_3 (2 << 6) +#define WM8960_DCLK_DIV_4 (3 << 6) +#define WM8960_DCLK_DIV_6 (4 << 6) +#define WM8960_DCLK_DIV_8 (5 << 6) +#define WM8960_DCLK_DIV_12 (6 << 6) +#define WM8960_DCLK_DIV_16 (7 << 6) + +#define WM8960_TOCLK_F19 (0 << 1) +#define WM8960_TOCLK_F21 (1 << 1) + +#define WM8960_OPCLK_DIV_1 (0 << 0) +#define WM8960_OPCLK_DIV_2 (1 << 0) +#define WM8960_OPCLK_DIV_3 (2 << 0) +#define WM8960_OPCLK_DIV_4 (3 << 0) +#define WM8960_OPCLK_DIV_5_5 (4 << 0) +#define WM8960_OPCLK_DIV_6 (5 << 0) + +#endif diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c new file mode 100644 index 0000000000..8f8330efb3 --- /dev/null +++ b/sound/soc/codecs/wm8961.c @@ -0,0 +1,993 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8961.c -- WM8961 ALSA SoC Audio driver + * + * Copyright 2009-10 Wolfson Microelectronics, plc + * + * Author: Mark Brown + * + * Currently unimplemented features: + * - ALC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8961.h" + +#define WM8961_MAX_REGISTER 0xFC + +static const struct reg_default wm8961_reg_defaults[] = { + { 0, 0x009F }, /* R0 - Left Input volume */ + { 1, 0x009F }, /* R1 - Right Input volume */ + { 2, 0x0000 }, /* R2 - LOUT1 volume */ + { 3, 0x0000 }, /* R3 - ROUT1 volume */ + { 4, 0x0020 }, /* R4 - Clocking1 */ + { 5, 0x0008 }, /* R5 - ADC & DAC Control 1 */ + { 6, 0x0000 }, /* R6 - ADC & DAC Control 2 */ + { 7, 0x000A }, /* R7 - Audio Interface 0 */ + { 8, 0x01F4 }, /* R8 - Clocking2 */ + { 9, 0x0000 }, /* R9 - Audio Interface 1 */ + { 10, 0x00FF }, /* R10 - Left DAC volume */ + { 11, 0x00FF }, /* R11 - Right DAC volume */ + + { 14, 0x0040 }, /* R14 - Audio Interface 2 */ + + { 17, 0x007B }, /* R17 - ALC1 */ + { 18, 0x0000 }, /* R18 - ALC2 */ + { 19, 0x0032 }, /* R19 - ALC3 */ + { 20, 0x0000 }, /* R20 - Noise Gate */ + { 21, 0x00C0 }, /* R21 - Left ADC volume */ + { 22, 0x00C0 }, /* R22 - Right ADC volume */ + { 23, 0x0120 }, /* R23 - Additional control(1) */ + { 24, 0x0000 }, /* R24 - Additional control(2) */ + { 25, 0x0000 }, /* R25 - Pwr Mgmt (1) */ + { 26, 0x0000 }, /* R26 - Pwr Mgmt (2) */ + { 27, 0x0000 }, /* R27 - Additional Control (3) */ + { 28, 0x0000 }, /* R28 - Anti-pop */ + + { 30, 0x005F }, /* R30 - Clocking 3 */ + + { 32, 0x0000 }, /* R32 - ADCL signal path */ + { 33, 0x0000 }, /* R33 - ADCR signal path */ + + { 40, 0x0000 }, /* R40 - LOUT2 volume */ + { 41, 0x0000 }, /* R41 - ROUT2 volume */ + + { 47, 0x0000 }, /* R47 - Pwr Mgmt (3) */ + { 48, 0x0023 }, /* R48 - Additional Control (4) */ + { 49, 0x0000 }, /* R49 - Class D Control 1 */ + + { 51, 0x0003 }, /* R51 - Class D Control 2 */ + + { 56, 0x0106 }, /* R56 - Clocking 4 */ + { 57, 0x0000 }, /* R57 - DSP Sidetone 0 */ + { 58, 0x0000 }, /* R58 - DSP Sidetone 1 */ + + { 60, 0x0000 }, /* R60 - DC Servo 0 */ + { 61, 0x0000 }, /* R61 - DC Servo 1 */ + + { 63, 0x015E }, /* R63 - DC Servo 3 */ + + { 65, 0x0010 }, /* R65 - DC Servo 5 */ + + { 68, 0x0003 }, /* R68 - Analogue PGA Bias */ + { 69, 0x0000 }, /* R69 - Analogue HP 0 */ + + { 71, 0x01FB }, /* R71 - Analogue HP 2 */ + { 72, 0x0000 }, /* R72 - Charge Pump 1 */ + + { 82, 0x0000 }, /* R82 - Charge Pump B */ + + { 87, 0x0000 }, /* R87 - Write Sequencer 1 */ + { 88, 0x0000 }, /* R88 - Write Sequencer 2 */ + { 89, 0x0000 }, /* R89 - Write Sequencer 3 */ + { 90, 0x0000 }, /* R90 - Write Sequencer 4 */ + { 91, 0x0000 }, /* R91 - Write Sequencer 5 */ + { 92, 0x0000 }, /* R92 - Write Sequencer 6 */ + { 93, 0x0000 }, /* R93 - Write Sequencer 7 */ + + { 252, 0x0001 }, /* R252 - General test 1 */ +}; + +struct wm8961_priv { + struct regmap *regmap; + int sysclk; +}; + +static bool wm8961_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8961_SOFTWARE_RESET: + case WM8961_WRITE_SEQUENCER_7: + case WM8961_DC_SERVO_1: + return true; + + default: + return false; + } +} + +static bool wm8961_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8961_LEFT_INPUT_VOLUME: + case WM8961_RIGHT_INPUT_VOLUME: + case WM8961_LOUT1_VOLUME: + case WM8961_ROUT1_VOLUME: + case WM8961_CLOCKING1: + case WM8961_ADC_DAC_CONTROL_1: + case WM8961_ADC_DAC_CONTROL_2: + case WM8961_AUDIO_INTERFACE_0: + case WM8961_CLOCKING2: + case WM8961_AUDIO_INTERFACE_1: + case WM8961_LEFT_DAC_VOLUME: + case WM8961_RIGHT_DAC_VOLUME: + case WM8961_AUDIO_INTERFACE_2: + case WM8961_SOFTWARE_RESET: + case WM8961_ALC1: + case WM8961_ALC2: + case WM8961_ALC3: + case WM8961_NOISE_GATE: + case WM8961_LEFT_ADC_VOLUME: + case WM8961_RIGHT_ADC_VOLUME: + case WM8961_ADDITIONAL_CONTROL_1: + case WM8961_ADDITIONAL_CONTROL_2: + case WM8961_PWR_MGMT_1: + case WM8961_PWR_MGMT_2: + case WM8961_ADDITIONAL_CONTROL_3: + case WM8961_ANTI_POP: + case WM8961_CLOCKING_3: + case WM8961_ADCL_SIGNAL_PATH: + case WM8961_ADCR_SIGNAL_PATH: + case WM8961_LOUT2_VOLUME: + case WM8961_ROUT2_VOLUME: + case WM8961_PWR_MGMT_3: + case WM8961_ADDITIONAL_CONTROL_4: + case WM8961_CLASS_D_CONTROL_1: + case WM8961_CLASS_D_CONTROL_2: + case WM8961_CLOCKING_4: + case WM8961_DSP_SIDETONE_0: + case WM8961_DSP_SIDETONE_1: + case WM8961_DC_SERVO_0: + case WM8961_DC_SERVO_1: + case WM8961_DC_SERVO_3: + case WM8961_DC_SERVO_5: + case WM8961_ANALOGUE_PGA_BIAS: + case WM8961_ANALOGUE_HP_0: + case WM8961_ANALOGUE_HP_2: + case WM8961_CHARGE_PUMP_1: + case WM8961_CHARGE_PUMP_B: + case WM8961_WRITE_SEQUENCER_1: + case WM8961_WRITE_SEQUENCER_2: + case WM8961_WRITE_SEQUENCER_3: + case WM8961_WRITE_SEQUENCER_4: + case WM8961_WRITE_SEQUENCER_5: + case WM8961_WRITE_SEQUENCER_6: + case WM8961_WRITE_SEQUENCER_7: + case WM8961_GENERAL_TEST_1: + return true; + default: + return false; + } +} + +/* + * The headphone output supports special anti-pop sequences giving + * silent power up and power down. + */ +static int wm8961_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 hp_reg = snd_soc_component_read(component, WM8961_ANALOGUE_HP_0); + u16 cp_reg = snd_soc_component_read(component, WM8961_CHARGE_PUMP_1); + u16 pwr_reg = snd_soc_component_read(component, WM8961_PWR_MGMT_2); + u16 dcs_reg = snd_soc_component_read(component, WM8961_DC_SERVO_1); + int timeout = 500; + + if (event & SND_SOC_DAPM_POST_PMU) { + /* Make sure the output is shorted */ + hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT); + snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Enable the charge pump */ + cp_reg |= WM8961_CP_ENA; + snd_soc_component_write(component, WM8961_CHARGE_PUMP_1, cp_reg); + mdelay(5); + + /* Enable the PGA */ + pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA; + snd_soc_component_write(component, WM8961_PWR_MGMT_2, pwr_reg); + + /* Enable the amplifier */ + hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA; + snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Second stage enable */ + hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY; + snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Enable the DC servo & trigger startup */ + dcs_reg |= + WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR | + WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL; + dev_dbg(component->dev, "Enabling DC servo\n"); + + snd_soc_component_write(component, WM8961_DC_SERVO_1, dcs_reg); + do { + msleep(1); + dcs_reg = snd_soc_component_read(component, WM8961_DC_SERVO_1); + } while (--timeout && + dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR | + WM8961_DCS_TRIG_STARTUP_HPL)); + if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR | + WM8961_DCS_TRIG_STARTUP_HPL)) + dev_err(component->dev, "DC servo timed out\n"); + else + dev_dbg(component->dev, "DC servo startup complete\n"); + + /* Enable the output stage */ + hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP; + snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Remove the short on the output stage */ + hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT; + snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); + } + + if (event & SND_SOC_DAPM_PRE_PMD) { + /* Short the output */ + hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT); + snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Disable the output stage */ + hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP); + snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Disable DC offset cancellation */ + dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR | + WM8961_DCS_ENA_CHAN_HPL); + snd_soc_component_write(component, WM8961_DC_SERVO_1, dcs_reg); + + /* Finish up */ + hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA | + WM8961_HPL_ENA_DLY | WM8961_HPL_ENA); + snd_soc_component_write(component, WM8961_ANALOGUE_HP_0, hp_reg); + + /* Disable the PGA */ + pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA); + snd_soc_component_write(component, WM8961_PWR_MGMT_2, pwr_reg); + + /* Disable the charge pump */ + dev_dbg(component->dev, "Disabling charge pump\n"); + snd_soc_component_write(component, WM8961_CHARGE_PUMP_1, + cp_reg & ~WM8961_CP_ENA); + } + + return 0; +} + +static int wm8961_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 pwr_reg = snd_soc_component_read(component, WM8961_PWR_MGMT_2); + u16 spk_reg = snd_soc_component_read(component, WM8961_CLASS_D_CONTROL_1); + + if (event & SND_SOC_DAPM_POST_PMU) { + /* Enable the PGA */ + pwr_reg |= WM8961_SPKL_PGA | WM8961_SPKR_PGA; + snd_soc_component_write(component, WM8961_PWR_MGMT_2, pwr_reg); + + /* Enable the amplifier */ + spk_reg |= WM8961_SPKL_ENA | WM8961_SPKR_ENA; + snd_soc_component_write(component, WM8961_CLASS_D_CONTROL_1, spk_reg); + } + + if (event & SND_SOC_DAPM_PRE_PMD) { + /* Disable the amplifier */ + spk_reg &= ~(WM8961_SPKL_ENA | WM8961_SPKR_ENA); + snd_soc_component_write(component, WM8961_CLASS_D_CONTROL_1, spk_reg); + + /* Disable the PGA */ + pwr_reg &= ~(WM8961_SPKL_PGA | WM8961_SPKR_PGA); + snd_soc_component_write(component, WM8961_PWR_MGMT_2, pwr_reg); + } + + return 0; +} + +static const char *adc_hpf_text[] = { + "Hi-fi", "Voice 1", "Voice 2", "Voice 3", +}; + +static SOC_ENUM_SINGLE_DECL(adc_hpf, + WM8961_ADC_DAC_CONTROL_2, 7, adc_hpf_text); + +static const char *dac_deemph_text[] = { + "None", "32kHz", "44.1kHz", "48kHz", +}; + +static SOC_ENUM_SINGLE_DECL(dac_deemph, + WM8961_ADC_DAC_CONTROL_1, 1, dac_deemph_text); + +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0); +static const DECLARE_TLV_DB_RANGE(boost_tlv, + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(13, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(20, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(29, 0, 0) +); +static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0); + +static const struct snd_kcontrol_new wm8961_snd_controls[] = { +SOC_DOUBLE_R_TLV("Headphone Volume", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME, + 0, 127, 0, out_tlv), +SOC_DOUBLE_TLV("Headphone Secondary Volume", WM8961_ANALOGUE_HP_2, + 6, 3, 7, 0, hp_sec_tlv), +SOC_DOUBLE_R("Headphone ZC Switch", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME, + 7, 1, 0), + +SOC_DOUBLE_R_TLV("Speaker Volume", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME, + 0, 127, 0, out_tlv), +SOC_DOUBLE_R("Speaker ZC Switch", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME, + 7, 1, 0), +SOC_SINGLE("Speaker AC Gain", WM8961_CLASS_D_CONTROL_2, 0, 7, 0), + +SOC_SINGLE("DAC x128 OSR Switch", WM8961_ADC_DAC_CONTROL_2, 0, 1, 0), +SOC_ENUM("DAC Deemphasis", dac_deemph), +SOC_SINGLE("DAC Soft Mute Switch", WM8961_ADC_DAC_CONTROL_2, 3, 1, 0), + +SOC_DOUBLE_R_TLV("Sidetone Volume", WM8961_DSP_SIDETONE_0, + WM8961_DSP_SIDETONE_1, 4, 12, 0, sidetone_tlv), + +SOC_SINGLE("ADC High Pass Filter Switch", WM8961_ADC_DAC_CONTROL_1, 0, 1, 0), +SOC_ENUM("ADC High Pass Filter Mode", adc_hpf), + +SOC_DOUBLE_R_TLV("Capture Volume", + WM8961_LEFT_ADC_VOLUME, WM8961_RIGHT_ADC_VOLUME, + 1, 119, 0, adc_tlv), +SOC_DOUBLE_R_TLV("Capture Boost Volume", + WM8961_ADCL_SIGNAL_PATH, WM8961_ADCR_SIGNAL_PATH, + 4, 3, 0, boost_tlv), +SOC_DOUBLE_R_TLV("Capture PGA Volume", + WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, + 0, 62, 0, pga_tlv), +SOC_DOUBLE_R("Capture PGA ZC Switch", + WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, + 6, 1, 1), +SOC_DOUBLE_R("Capture PGA Switch", + WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME, + 7, 1, 1), +}; + +static const char *sidetone_text[] = { + "None", "Left", "Right" +}; + +static SOC_ENUM_SINGLE_DECL(dacl_sidetone, + WM8961_DSP_SIDETONE_0, 2, sidetone_text); + +static SOC_ENUM_SINGLE_DECL(dacr_sidetone, + WM8961_DSP_SIDETONE_1, 2, sidetone_text); + +static const struct snd_kcontrol_new dacl_mux = + SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone); + +static const struct snd_kcontrol_new dacr_mux = + SOC_DAPM_ENUM("DACR Sidetone", dacr_sidetone); + +static const struct snd_soc_dapm_widget wm8961_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("LINPUT"), +SND_SOC_DAPM_INPUT("RINPUT"), + +SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8961_CLOCKING2, 4, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Left Input", WM8961_PWR_MGMT_1, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Input", WM8961_PWR_MGMT_1, 4, 0, NULL, 0), + +SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", WM8961_PWR_MGMT_1, 3, 0), +SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", WM8961_PWR_MGMT_1, 2, 0), + +SND_SOC_DAPM_SUPPLY("MICBIAS", WM8961_PWR_MGMT_1, 1, 0, NULL, 0), + +SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &dacl_mux), +SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &dacr_mux), + +SND_SOC_DAPM_DAC("DACL", "HiFi Playback", WM8961_PWR_MGMT_2, 8, 0), +SND_SOC_DAPM_DAC("DACR", "HiFi Playback", WM8961_PWR_MGMT_2, 7, 0), + +/* Handle as a mono path for DCS */ +SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM, + 4, 0, NULL, 0, wm8961_hp_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_E("Speaker Output", SND_SOC_NOPM, + 4, 0, NULL, 0, wm8961_spk_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_OUTPUT("HP_L"), +SND_SOC_DAPM_OUTPUT("HP_R"), +SND_SOC_DAPM_OUTPUT("SPK_LN"), +SND_SOC_DAPM_OUTPUT("SPK_LP"), +SND_SOC_DAPM_OUTPUT("SPK_RN"), +SND_SOC_DAPM_OUTPUT("SPK_RP"), +}; + + +static const struct snd_soc_dapm_route audio_paths[] = { + { "DACL", NULL, "CLK_DSP" }, + { "DACL", NULL, "DACL Sidetone" }, + { "DACR", NULL, "CLK_DSP" }, + { "DACR", NULL, "DACR Sidetone" }, + + { "DACL Sidetone", "Left", "ADCL" }, + { "DACL Sidetone", "Right", "ADCR" }, + + { "DACR Sidetone", "Left", "ADCL" }, + { "DACR Sidetone", "Right", "ADCR" }, + + { "HP_L", NULL, "Headphone Output" }, + { "HP_R", NULL, "Headphone Output" }, + { "Headphone Output", NULL, "DACL" }, + { "Headphone Output", NULL, "DACR" }, + + { "SPK_LN", NULL, "Speaker Output" }, + { "SPK_LP", NULL, "Speaker Output" }, + { "SPK_RN", NULL, "Speaker Output" }, + { "SPK_RP", NULL, "Speaker Output" }, + + { "Speaker Output", NULL, "DACL" }, + { "Speaker Output", NULL, "DACR" }, + + { "ADCL", NULL, "Left Input" }, + { "ADCL", NULL, "CLK_DSP" }, + { "ADCR", NULL, "Right Input" }, + { "ADCR", NULL, "CLK_DSP" }, + + { "Left Input", NULL, "LINPUT" }, + { "Right Input", NULL, "RINPUT" }, + +}; + +/* Values for CLK_SYS_RATE */ +static struct { + int ratio; + u16 val; +} wm8961_clk_sys_ratio[] = { + { 64, 0 }, + { 128, 1 }, + { 192, 2 }, + { 256, 3 }, + { 384, 4 }, + { 512, 5 }, + { 768, 6 }, + { 1024, 7 }, + { 1408, 8 }, + { 1536, 9 }, +}; + +/* Values for SAMPLE_RATE */ +static struct { + int rate; + u16 val; +} wm8961_srate[] = { + { 48000, 0 }, + { 44100, 0 }, + { 32000, 1 }, + { 22050, 2 }, + { 24000, 2 }, + { 16000, 3 }, + { 11250, 4 }, + { 12000, 4 }, + { 8000, 5 }, +}; + +static int wm8961_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8961_priv *wm8961 = snd_soc_component_get_drvdata(component); + int i, best, target, fs; + u16 reg; + + fs = params_rate(params); + + if (!wm8961->sysclk) { + dev_err(component->dev, "MCLK has not been specified\n"); + return -EINVAL; + } + + /* Find the closest sample rate for the filters */ + best = 0; + for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) { + if (abs(wm8961_srate[i].rate - fs) < + abs(wm8961_srate[best].rate - fs)) + best = i; + } + reg = snd_soc_component_read(component, WM8961_ADDITIONAL_CONTROL_3); + reg &= ~WM8961_SAMPLE_RATE_MASK; + reg |= wm8961_srate[best].val; + snd_soc_component_write(component, WM8961_ADDITIONAL_CONTROL_3, reg); + dev_dbg(component->dev, "Selected SRATE %dHz for %dHz\n", + wm8961_srate[best].rate, fs); + + /* Select a CLK_SYS/fs ratio equal to or higher than required */ + target = wm8961->sysclk / fs; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) { + dev_err(component->dev, + "SYSCLK must be at least 64*fs for DAC\n"); + return -EINVAL; + } + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) { + dev_err(component->dev, + "SYSCLK must be at least 256*fs for ADC\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) { + if (wm8961_clk_sys_ratio[i].ratio >= target) + break; + } + if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) { + dev_err(component->dev, "Unable to generate CLK_SYS_RATE\n"); + return -EINVAL; + } + dev_dbg(component->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n", + wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs, + wm8961->sysclk / fs); + + reg = snd_soc_component_read(component, WM8961_CLOCKING_4); + reg &= ~WM8961_CLK_SYS_RATE_MASK; + reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT; + snd_soc_component_write(component, WM8961_CLOCKING_4, reg); + + reg = snd_soc_component_read(component, WM8961_AUDIO_INTERFACE_0); + reg &= ~WM8961_WL_MASK; + switch (params_width(params)) { + case 16: + break; + case 20: + reg |= 1 << WM8961_WL_SHIFT; + break; + case 24: + reg |= 2 << WM8961_WL_SHIFT; + break; + case 32: + reg |= 3 << WM8961_WL_SHIFT; + break; + default: + return -EINVAL; + } + snd_soc_component_write(component, WM8961_AUDIO_INTERFACE_0, reg); + + /* Sloping stop-band filter is recommended for <= 24kHz */ + reg = snd_soc_component_read(component, WM8961_ADC_DAC_CONTROL_2); + if (fs <= 24000) + reg |= WM8961_DACSLOPE; + else + reg &= ~WM8961_DACSLOPE; + snd_soc_component_write(component, WM8961_ADC_DAC_CONTROL_2, reg); + + return 0; +} + +static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, + int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8961_priv *wm8961 = snd_soc_component_get_drvdata(component); + u16 reg = snd_soc_component_read(component, WM8961_CLOCKING1); + + if (freq > 33000000) { + dev_err(component->dev, "MCLK must be <33MHz\n"); + return -EINVAL; + } + + if (freq > 16500000) { + dev_dbg(component->dev, "Using MCLK/2 for %dHz MCLK\n", freq); + reg |= WM8961_MCLKDIV; + freq /= 2; + } else { + dev_dbg(component->dev, "Using MCLK/1 for %dHz MCLK\n", freq); + reg &= ~WM8961_MCLKDIV; + } + + snd_soc_component_write(component, WM8961_CLOCKING1, reg); + + wm8961->sysclk = freq; + + return 0; +} + +static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u16 aif = snd_soc_component_read(component, WM8961_AUDIO_INTERFACE_0); + + aif &= ~(WM8961_BCLKINV | WM8961_LRP | + WM8961_MS | WM8961_FORMAT_MASK); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aif |= WM8961_MS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + break; + + case SND_SOC_DAIFMT_LEFT_J: + aif |= 1; + break; + + case SND_SOC_DAIFMT_I2S: + aif |= 2; + break; + + case SND_SOC_DAIFMT_DSP_B: + aif |= WM8961_LRP; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + aif |= 3; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_IB_NF: + break; + default: + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + aif |= WM8961_LRP; + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8961_BCLKINV; + break; + case SND_SOC_DAIFMT_IB_IF: + aif |= WM8961_BCLKINV | WM8961_LRP; + break; + default: + return -EINVAL; + } + + return snd_soc_component_write(component, WM8961_AUDIO_INTERFACE_0, aif); +} + +static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_component *component = dai->component; + u16 reg = snd_soc_component_read(component, WM8961_ADDITIONAL_CONTROL_2); + + if (tristate) + reg |= WM8961_TRIS; + else + reg &= ~WM8961_TRIS; + + return snd_soc_component_write(component, WM8961_ADDITIONAL_CONTROL_2, reg); +} + +static int wm8961_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 reg = snd_soc_component_read(component, WM8961_ADC_DAC_CONTROL_1); + + if (mute) + reg |= WM8961_DACMU; + else + reg &= ~WM8961_DACMU; + + msleep(17); + + return snd_soc_component_write(component, WM8961_ADC_DAC_CONTROL_1, reg); +} + +static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) +{ + struct snd_soc_component *component = dai->component; + u16 reg; + + switch (div_id) { + case WM8961_BCLK: + reg = snd_soc_component_read(component, WM8961_CLOCKING2); + reg &= ~WM8961_BCLKDIV_MASK; + reg |= div; + snd_soc_component_write(component, WM8961_CLOCKING2, reg); + break; + + case WM8961_LRCLK: + reg = snd_soc_component_read(component, WM8961_AUDIO_INTERFACE_2); + reg &= ~WM8961_LRCLK_RATE_MASK; + reg |= div; + snd_soc_component_write(component, WM8961_AUDIO_INTERFACE_2, reg); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int wm8961_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + u16 reg; + + /* This is all slightly unusual since we have no bypass paths + * and the output amplifier structure means we can just slam + * the biases straight up rather than having to ramp them + * slowly. + */ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) { + /* Enable bias generation */ + reg = snd_soc_component_read(component, WM8961_ANTI_POP); + reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN; + snd_soc_component_write(component, WM8961_ANTI_POP, reg); + + /* VMID=2*50k, VREF */ + reg = snd_soc_component_read(component, WM8961_PWR_MGMT_1); + reg &= ~WM8961_VMIDSEL_MASK; + reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF; + snd_soc_component_write(component, WM8961_PWR_MGMT_1, reg); + } + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) { + /* VREF off */ + reg = snd_soc_component_read(component, WM8961_PWR_MGMT_1); + reg &= ~WM8961_VREF; + snd_soc_component_write(component, WM8961_PWR_MGMT_1, reg); + + /* Bias generation off */ + reg = snd_soc_component_read(component, WM8961_ANTI_POP); + reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN); + snd_soc_component_write(component, WM8961_ANTI_POP, reg); + + /* VMID off */ + reg = snd_soc_component_read(component, WM8961_PWR_MGMT_1); + reg &= ~WM8961_VMIDSEL_MASK; + snd_soc_component_write(component, WM8961_PWR_MGMT_1, reg); + } + break; + + case SND_SOC_BIAS_OFF: + break; + } + + return 0; +} + + +#define WM8961_RATES SNDRV_PCM_RATE_8000_48000 + +#define WM8961_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8961_dai_ops = { + .hw_params = wm8961_hw_params, + .set_sysclk = wm8961_set_sysclk, + .set_fmt = wm8961_set_fmt, + .mute_stream = wm8961_mute, + .set_tristate = wm8961_set_tristate, + .set_clkdiv = wm8961_set_clkdiv, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8961_dai = { + .name = "wm8961-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8961_RATES, + .formats = WM8961_FORMATS,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8961_RATES, + .formats = WM8961_FORMATS,}, + .ops = &wm8961_dai_ops, +}; + +static int wm8961_probe(struct snd_soc_component *component) +{ + u16 reg; + + /* Enable class W */ + reg = snd_soc_component_read(component, WM8961_CHARGE_PUMP_B); + reg |= WM8961_CP_DYN_PWR_MASK; + snd_soc_component_write(component, WM8961_CHARGE_PUMP_B, reg); + + /* Latch volume update bits (right channel only, we always + * write both out) and default ZC on. */ + reg = snd_soc_component_read(component, WM8961_ROUT1_VOLUME); + snd_soc_component_write(component, WM8961_ROUT1_VOLUME, + reg | WM8961_LO1ZC | WM8961_OUT1VU); + snd_soc_component_write(component, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC); + reg = snd_soc_component_read(component, WM8961_ROUT2_VOLUME); + snd_soc_component_write(component, WM8961_ROUT2_VOLUME, + reg | WM8961_SPKRZC | WM8961_SPKVU); + snd_soc_component_write(component, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC); + + reg = snd_soc_component_read(component, WM8961_RIGHT_ADC_VOLUME); + snd_soc_component_write(component, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU); + reg = snd_soc_component_read(component, WM8961_RIGHT_INPUT_VOLUME); + snd_soc_component_write(component, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU); + + /* Use soft mute by default */ + reg = snd_soc_component_read(component, WM8961_ADC_DAC_CONTROL_2); + reg |= WM8961_DACSMM; + snd_soc_component_write(component, WM8961_ADC_DAC_CONTROL_2, reg); + + /* Use automatic clocking mode by default; for now this is all + * we support. + */ + reg = snd_soc_component_read(component, WM8961_CLOCKING_3); + reg &= ~WM8961_MANUAL_MODE; + snd_soc_component_write(component, WM8961_CLOCKING_3, reg); + + return 0; +} + +#ifdef CONFIG_PM + +static int wm8961_resume(struct snd_soc_component *component) +{ + snd_soc_component_cache_sync(component); + + return 0; +} +#else +#define wm8961_resume NULL +#endif + +static const struct snd_soc_component_driver soc_component_dev_wm8961 = { + .probe = wm8961_probe, + .resume = wm8961_resume, + .set_bias_level = wm8961_set_bias_level, + .controls = wm8961_snd_controls, + .num_controls = ARRAY_SIZE(wm8961_snd_controls), + .dapm_widgets = wm8961_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets), + .dapm_routes = audio_paths, + .num_dapm_routes = ARRAY_SIZE(audio_paths), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8961_regmap = { + .reg_bits = 8, + .val_bits = 16, + .max_register = WM8961_MAX_REGISTER, + + .reg_defaults = wm8961_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8961_reg_defaults), + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm8961_volatile, + .readable_reg = wm8961_readable, +}; + +static int wm8961_i2c_probe(struct i2c_client *i2c) +{ + struct wm8961_priv *wm8961; + unsigned int val; + int ret; + + wm8961 = devm_kzalloc(&i2c->dev, sizeof(struct wm8961_priv), + GFP_KERNEL); + if (wm8961 == NULL) + return -ENOMEM; + + wm8961->regmap = devm_regmap_init_i2c(i2c, &wm8961_regmap); + if (IS_ERR(wm8961->regmap)) + return PTR_ERR(wm8961->regmap); + + ret = regmap_read(wm8961->regmap, WM8961_SOFTWARE_RESET, &val); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); + return ret; + } + + if (val != 0x1801) { + dev_err(&i2c->dev, "Device is not a WM8961: ID=0x%x\n", val); + return -EINVAL; + } + + /* This isn't volatile - readback doesn't correspond to write */ + regcache_cache_bypass(wm8961->regmap, true); + ret = regmap_read(wm8961->regmap, WM8961_RIGHT_INPUT_VOLUME, &val); + regcache_cache_bypass(wm8961->regmap, false); + + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read chip revision: %d\n", ret); + return ret; + } + + dev_info(&i2c->dev, "WM8961 family %d revision %c\n", + (val & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT, + ((val & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT) + + 'A'); + + ret = regmap_write(wm8961->regmap, WM8961_SOFTWARE_RESET, 0x1801); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, wm8961); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8961, &wm8961_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8961_i2c_id[] = { + { "wm8961", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id); + +static const struct of_device_id wm8961_of_match[] __maybe_unused = { + { .compatible = "wlf,wm8961", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8961_of_match); + +static struct i2c_driver wm8961_i2c_driver = { + .driver = { + .name = "wm8961", + .of_match_table = of_match_ptr(wm8961_of_match), + }, + .probe = wm8961_i2c_probe, + .id_table = wm8961_i2c_id, +}; + +module_i2c_driver(wm8961_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8961 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8961.h b/sound/soc/codecs/wm8961.h new file mode 100644 index 0000000000..d4e00e5493 --- /dev/null +++ b/sound/soc/codecs/wm8961.h @@ -0,0 +1,860 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8961.h -- WM8961 Soc Audio driver + */ + +#ifndef _WM8961_H +#define _WM8961_H + +#include + +#define WM8961_BCLK 1 +#define WM8961_LRCLK 2 + +#define WM8961_BCLK_DIV_1 0 +#define WM8961_BCLK_DIV_1_5 1 +#define WM8961_BCLK_DIV_2 2 +#define WM8961_BCLK_DIV_3 3 +#define WM8961_BCLK_DIV_4 4 +#define WM8961_BCLK_DIV_5_5 5 +#define WM8961_BCLK_DIV_6 6 +#define WM8961_BCLK_DIV_8 7 +#define WM8961_BCLK_DIV_11 8 +#define WM8961_BCLK_DIV_12 9 +#define WM8961_BCLK_DIV_16 10 +#define WM8961_BCLK_DIV_24 11 +#define WM8961_BCLK_DIV_32 13 + + +/* + * Register values. + */ +#define WM8961_LEFT_INPUT_VOLUME 0x00 +#define WM8961_RIGHT_INPUT_VOLUME 0x01 +#define WM8961_LOUT1_VOLUME 0x02 +#define WM8961_ROUT1_VOLUME 0x03 +#define WM8961_CLOCKING1 0x04 +#define WM8961_ADC_DAC_CONTROL_1 0x05 +#define WM8961_ADC_DAC_CONTROL_2 0x06 +#define WM8961_AUDIO_INTERFACE_0 0x07 +#define WM8961_CLOCKING2 0x08 +#define WM8961_AUDIO_INTERFACE_1 0x09 +#define WM8961_LEFT_DAC_VOLUME 0x0A +#define WM8961_RIGHT_DAC_VOLUME 0x0B +#define WM8961_AUDIO_INTERFACE_2 0x0E +#define WM8961_SOFTWARE_RESET 0x0F +#define WM8961_ALC1 0x11 +#define WM8961_ALC2 0x12 +#define WM8961_ALC3 0x13 +#define WM8961_NOISE_GATE 0x14 +#define WM8961_LEFT_ADC_VOLUME 0x15 +#define WM8961_RIGHT_ADC_VOLUME 0x16 +#define WM8961_ADDITIONAL_CONTROL_1 0x17 +#define WM8961_ADDITIONAL_CONTROL_2 0x18 +#define WM8961_PWR_MGMT_1 0x19 +#define WM8961_PWR_MGMT_2 0x1A +#define WM8961_ADDITIONAL_CONTROL_3 0x1B +#define WM8961_ANTI_POP 0x1C +#define WM8961_CLOCKING_3 0x1E +#define WM8961_ADCL_SIGNAL_PATH 0x20 +#define WM8961_ADCR_SIGNAL_PATH 0x21 +#define WM8961_LOUT2_VOLUME 0x28 +#define WM8961_ROUT2_VOLUME 0x29 +#define WM8961_PWR_MGMT_3 0x2F +#define WM8961_ADDITIONAL_CONTROL_4 0x30 +#define WM8961_CLASS_D_CONTROL_1 0x31 +#define WM8961_CLASS_D_CONTROL_2 0x33 +#define WM8961_CLOCKING_4 0x38 +#define WM8961_DSP_SIDETONE_0 0x39 +#define WM8961_DSP_SIDETONE_1 0x3A +#define WM8961_DC_SERVO_0 0x3C +#define WM8961_DC_SERVO_1 0x3D +#define WM8961_DC_SERVO_3 0x3F +#define WM8961_DC_SERVO_5 0x41 +#define WM8961_ANALOGUE_PGA_BIAS 0x44 +#define WM8961_ANALOGUE_HP_0 0x45 +#define WM8961_ANALOGUE_HP_2 0x47 +#define WM8961_CHARGE_PUMP_1 0x48 +#define WM8961_CHARGE_PUMP_B 0x52 +#define WM8961_WRITE_SEQUENCER_1 0x57 +#define WM8961_WRITE_SEQUENCER_2 0x58 +#define WM8961_WRITE_SEQUENCER_3 0x59 +#define WM8961_WRITE_SEQUENCER_4 0x5A +#define WM8961_WRITE_SEQUENCER_5 0x5B +#define WM8961_WRITE_SEQUENCER_6 0x5C +#define WM8961_WRITE_SEQUENCER_7 0x5D +#define WM8961_GENERAL_TEST_1 0xFC + + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Left Input volume + */ +#define WM8961_IPVU 0x0100 /* IPVU */ +#define WM8961_IPVU_MASK 0x0100 /* IPVU */ +#define WM8961_IPVU_SHIFT 8 /* IPVU */ +#define WM8961_IPVU_WIDTH 1 /* IPVU */ +#define WM8961_LINMUTE 0x0080 /* LINMUTE */ +#define WM8961_LINMUTE_MASK 0x0080 /* LINMUTE */ +#define WM8961_LINMUTE_SHIFT 7 /* LINMUTE */ +#define WM8961_LINMUTE_WIDTH 1 /* LINMUTE */ +#define WM8961_LIZC 0x0040 /* LIZC */ +#define WM8961_LIZC_MASK 0x0040 /* LIZC */ +#define WM8961_LIZC_SHIFT 6 /* LIZC */ +#define WM8961_LIZC_WIDTH 1 /* LIZC */ +#define WM8961_LINVOL_MASK 0x003F /* LINVOL - [5:0] */ +#define WM8961_LINVOL_SHIFT 0 /* LINVOL - [5:0] */ +#define WM8961_LINVOL_WIDTH 6 /* LINVOL - [5:0] */ + +/* + * R1 (0x01) - Right Input volume + */ +#define WM8961_DEVICE_ID_MASK 0xF000 /* DEVICE_ID - [15:12] */ +#define WM8961_DEVICE_ID_SHIFT 12 /* DEVICE_ID - [15:12] */ +#define WM8961_DEVICE_ID_WIDTH 4 /* DEVICE_ID - [15:12] */ +#define WM8961_CHIP_REV_MASK 0x0E00 /* CHIP_REV - [11:9] */ +#define WM8961_CHIP_REV_SHIFT 9 /* CHIP_REV - [11:9] */ +#define WM8961_CHIP_REV_WIDTH 3 /* CHIP_REV - [11:9] */ +#define WM8961_IPVU 0x0100 /* IPVU */ +#define WM8961_IPVU_MASK 0x0100 /* IPVU */ +#define WM8961_IPVU_SHIFT 8 /* IPVU */ +#define WM8961_IPVU_WIDTH 1 /* IPVU */ +#define WM8961_RINMUTE 0x0080 /* RINMUTE */ +#define WM8961_RINMUTE_MASK 0x0080 /* RINMUTE */ +#define WM8961_RINMUTE_SHIFT 7 /* RINMUTE */ +#define WM8961_RINMUTE_WIDTH 1 /* RINMUTE */ +#define WM8961_RIZC 0x0040 /* RIZC */ +#define WM8961_RIZC_MASK 0x0040 /* RIZC */ +#define WM8961_RIZC_SHIFT 6 /* RIZC */ +#define WM8961_RIZC_WIDTH 1 /* RIZC */ +#define WM8961_RINVOL_MASK 0x003F /* RINVOL - [5:0] */ +#define WM8961_RINVOL_SHIFT 0 /* RINVOL - [5:0] */ +#define WM8961_RINVOL_WIDTH 6 /* RINVOL - [5:0] */ + +/* + * R2 (0x02) - LOUT1 volume + */ +#define WM8961_OUT1VU 0x0100 /* OUT1VU */ +#define WM8961_OUT1VU_MASK 0x0100 /* OUT1VU */ +#define WM8961_OUT1VU_SHIFT 8 /* OUT1VU */ +#define WM8961_OUT1VU_WIDTH 1 /* OUT1VU */ +#define WM8961_LO1ZC 0x0080 /* LO1ZC */ +#define WM8961_LO1ZC_MASK 0x0080 /* LO1ZC */ +#define WM8961_LO1ZC_SHIFT 7 /* LO1ZC */ +#define WM8961_LO1ZC_WIDTH 1 /* LO1ZC */ +#define WM8961_LOUT1VOL_MASK 0x007F /* LOUT1VOL - [6:0] */ +#define WM8961_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [6:0] */ +#define WM8961_LOUT1VOL_WIDTH 7 /* LOUT1VOL - [6:0] */ + +/* + * R3 (0x03) - ROUT1 volume + */ +#define WM8961_OUT1VU 0x0100 /* OUT1VU */ +#define WM8961_OUT1VU_MASK 0x0100 /* OUT1VU */ +#define WM8961_OUT1VU_SHIFT 8 /* OUT1VU */ +#define WM8961_OUT1VU_WIDTH 1 /* OUT1VU */ +#define WM8961_RO1ZC 0x0080 /* RO1ZC */ +#define WM8961_RO1ZC_MASK 0x0080 /* RO1ZC */ +#define WM8961_RO1ZC_SHIFT 7 /* RO1ZC */ +#define WM8961_RO1ZC_WIDTH 1 /* RO1ZC */ +#define WM8961_ROUT1VOL_MASK 0x007F /* ROUT1VOL - [6:0] */ +#define WM8961_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [6:0] */ +#define WM8961_ROUT1VOL_WIDTH 7 /* ROUT1VOL - [6:0] */ + +/* + * R4 (0x04) - Clocking1 + */ +#define WM8961_ADCDIV_MASK 0x01C0 /* ADCDIV - [8:6] */ +#define WM8961_ADCDIV_SHIFT 6 /* ADCDIV - [8:6] */ +#define WM8961_ADCDIV_WIDTH 3 /* ADCDIV - [8:6] */ +#define WM8961_DACDIV_MASK 0x0038 /* DACDIV - [5:3] */ +#define WM8961_DACDIV_SHIFT 3 /* DACDIV - [5:3] */ +#define WM8961_DACDIV_WIDTH 3 /* DACDIV - [5:3] */ +#define WM8961_MCLKDIV 0x0004 /* MCLKDIV */ +#define WM8961_MCLKDIV_MASK 0x0004 /* MCLKDIV */ +#define WM8961_MCLKDIV_SHIFT 2 /* MCLKDIV */ +#define WM8961_MCLKDIV_WIDTH 1 /* MCLKDIV */ + +/* + * R5 (0x05) - ADC & DAC Control 1 + */ +#define WM8961_ADCPOL_MASK 0x0060 /* ADCPOL - [6:5] */ +#define WM8961_ADCPOL_SHIFT 5 /* ADCPOL - [6:5] */ +#define WM8961_ADCPOL_WIDTH 2 /* ADCPOL - [6:5] */ +#define WM8961_DACMU 0x0008 /* DACMU */ +#define WM8961_DACMU_MASK 0x0008 /* DACMU */ +#define WM8961_DACMU_SHIFT 3 /* DACMU */ +#define WM8961_DACMU_WIDTH 1 /* DACMU */ +#define WM8961_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */ +#define WM8961_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */ +#define WM8961_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */ +#define WM8961_ADCHPD 0x0001 /* ADCHPD */ +#define WM8961_ADCHPD_MASK 0x0001 /* ADCHPD */ +#define WM8961_ADCHPD_SHIFT 0 /* ADCHPD */ +#define WM8961_ADCHPD_WIDTH 1 /* ADCHPD */ + +/* + * R6 (0x06) - ADC & DAC Control 2 + */ +#define WM8961_ADC_HPF_CUT_MASK 0x0180 /* ADC_HPF_CUT - [8:7] */ +#define WM8961_ADC_HPF_CUT_SHIFT 7 /* ADC_HPF_CUT - [8:7] */ +#define WM8961_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [8:7] */ +#define WM8961_DACPOL_MASK 0x0060 /* DACPOL - [6:5] */ +#define WM8961_DACPOL_SHIFT 5 /* DACPOL - [6:5] */ +#define WM8961_DACPOL_WIDTH 2 /* DACPOL - [6:5] */ +#define WM8961_DACSMM 0x0008 /* DACSMM */ +#define WM8961_DACSMM_MASK 0x0008 /* DACSMM */ +#define WM8961_DACSMM_SHIFT 3 /* DACSMM */ +#define WM8961_DACSMM_WIDTH 1 /* DACSMM */ +#define WM8961_DACMR 0x0004 /* DACMR */ +#define WM8961_DACMR_MASK 0x0004 /* DACMR */ +#define WM8961_DACMR_SHIFT 2 /* DACMR */ +#define WM8961_DACMR_WIDTH 1 /* DACMR */ +#define WM8961_DACSLOPE 0x0002 /* DACSLOPE */ +#define WM8961_DACSLOPE_MASK 0x0002 /* DACSLOPE */ +#define WM8961_DACSLOPE_SHIFT 1 /* DACSLOPE */ +#define WM8961_DACSLOPE_WIDTH 1 /* DACSLOPE */ +#define WM8961_DAC_OSR128 0x0001 /* DAC_OSR128 */ +#define WM8961_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */ +#define WM8961_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */ +#define WM8961_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */ + +/* + * R7 (0x07) - Audio Interface 0 + */ +#define WM8961_ALRSWAP 0x0100 /* ALRSWAP */ +#define WM8961_ALRSWAP_MASK 0x0100 /* ALRSWAP */ +#define WM8961_ALRSWAP_SHIFT 8 /* ALRSWAP */ +#define WM8961_ALRSWAP_WIDTH 1 /* ALRSWAP */ +#define WM8961_BCLKINV 0x0080 /* BCLKINV */ +#define WM8961_BCLKINV_MASK 0x0080 /* BCLKINV */ +#define WM8961_BCLKINV_SHIFT 7 /* BCLKINV */ +#define WM8961_BCLKINV_WIDTH 1 /* BCLKINV */ +#define WM8961_MS 0x0040 /* MS */ +#define WM8961_MS_MASK 0x0040 /* MS */ +#define WM8961_MS_SHIFT 6 /* MS */ +#define WM8961_MS_WIDTH 1 /* MS */ +#define WM8961_DLRSWAP 0x0020 /* DLRSWAP */ +#define WM8961_DLRSWAP_MASK 0x0020 /* DLRSWAP */ +#define WM8961_DLRSWAP_SHIFT 5 /* DLRSWAP */ +#define WM8961_DLRSWAP_WIDTH 1 /* DLRSWAP */ +#define WM8961_LRP 0x0010 /* LRP */ +#define WM8961_LRP_MASK 0x0010 /* LRP */ +#define WM8961_LRP_SHIFT 4 /* LRP */ +#define WM8961_LRP_WIDTH 1 /* LRP */ +#define WM8961_WL_MASK 0x000C /* WL - [3:2] */ +#define WM8961_WL_SHIFT 2 /* WL - [3:2] */ +#define WM8961_WL_WIDTH 2 /* WL - [3:2] */ +#define WM8961_FORMAT_MASK 0x0003 /* FORMAT - [1:0] */ +#define WM8961_FORMAT_SHIFT 0 /* FORMAT - [1:0] */ +#define WM8961_FORMAT_WIDTH 2 /* FORMAT - [1:0] */ + +/* + * R8 (0x08) - Clocking2 + */ +#define WM8961_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */ +#define WM8961_DCLKDIV_SHIFT 6 /* DCLKDIV - [8:6] */ +#define WM8961_DCLKDIV_WIDTH 3 /* DCLKDIV - [8:6] */ +#define WM8961_CLK_SYS_ENA 0x0020 /* CLK_SYS_ENA */ +#define WM8961_CLK_SYS_ENA_MASK 0x0020 /* CLK_SYS_ENA */ +#define WM8961_CLK_SYS_ENA_SHIFT 5 /* CLK_SYS_ENA */ +#define WM8961_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */ +#define WM8961_CLK_DSP_ENA 0x0010 /* CLK_DSP_ENA */ +#define WM8961_CLK_DSP_ENA_MASK 0x0010 /* CLK_DSP_ENA */ +#define WM8961_CLK_DSP_ENA_SHIFT 4 /* CLK_DSP_ENA */ +#define WM8961_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */ +#define WM8961_BCLKDIV_MASK 0x000F /* BCLKDIV - [3:0] */ +#define WM8961_BCLKDIV_SHIFT 0 /* BCLKDIV - [3:0] */ +#define WM8961_BCLKDIV_WIDTH 4 /* BCLKDIV - [3:0] */ + +/* + * R9 (0x09) - Audio Interface 1 + */ +#define WM8961_DACCOMP_MASK 0x0018 /* DACCOMP - [4:3] */ +#define WM8961_DACCOMP_SHIFT 3 /* DACCOMP - [4:3] */ +#define WM8961_DACCOMP_WIDTH 2 /* DACCOMP - [4:3] */ +#define WM8961_ADCCOMP_MASK 0x0006 /* ADCCOMP - [2:1] */ +#define WM8961_ADCCOMP_SHIFT 1 /* ADCCOMP - [2:1] */ +#define WM8961_ADCCOMP_WIDTH 2 /* ADCCOMP - [2:1] */ +#define WM8961_LOOPBACK 0x0001 /* LOOPBACK */ +#define WM8961_LOOPBACK_MASK 0x0001 /* LOOPBACK */ +#define WM8961_LOOPBACK_SHIFT 0 /* LOOPBACK */ +#define WM8961_LOOPBACK_WIDTH 1 /* LOOPBACK */ + +/* + * R10 (0x0A) - Left DAC volume + */ +#define WM8961_DACVU 0x0100 /* DACVU */ +#define WM8961_DACVU_MASK 0x0100 /* DACVU */ +#define WM8961_DACVU_SHIFT 8 /* DACVU */ +#define WM8961_DACVU_WIDTH 1 /* DACVU */ +#define WM8961_LDACVOL_MASK 0x00FF /* LDACVOL - [7:0] */ +#define WM8961_LDACVOL_SHIFT 0 /* LDACVOL - [7:0] */ +#define WM8961_LDACVOL_WIDTH 8 /* LDACVOL - [7:0] */ + +/* + * R11 (0x0B) - Right DAC volume + */ +#define WM8961_DACVU 0x0100 /* DACVU */ +#define WM8961_DACVU_MASK 0x0100 /* DACVU */ +#define WM8961_DACVU_SHIFT 8 /* DACVU */ +#define WM8961_DACVU_WIDTH 1 /* DACVU */ +#define WM8961_RDACVOL_MASK 0x00FF /* RDACVOL - [7:0] */ +#define WM8961_RDACVOL_SHIFT 0 /* RDACVOL - [7:0] */ +#define WM8961_RDACVOL_WIDTH 8 /* RDACVOL - [7:0] */ + +/* + * R14 (0x0E) - Audio Interface 2 + */ +#define WM8961_LRCLK_RATE_MASK 0x01FF /* LRCLK_RATE - [8:0] */ +#define WM8961_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [8:0] */ +#define WM8961_LRCLK_RATE_WIDTH 9 /* LRCLK_RATE - [8:0] */ + +/* + * R15 (0x0F) - Software Reset + */ +#define WM8961_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */ +#define WM8961_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */ +#define WM8961_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */ + +/* + * R17 (0x11) - ALC1 + */ +#define WM8961_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */ +#define WM8961_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */ +#define WM8961_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */ +#define WM8961_MAXGAIN_MASK 0x0070 /* MAXGAIN - [6:4] */ +#define WM8961_MAXGAIN_SHIFT 4 /* MAXGAIN - [6:4] */ +#define WM8961_MAXGAIN_WIDTH 3 /* MAXGAIN - [6:4] */ +#define WM8961_ALCL_MASK 0x000F /* ALCL - [3:0] */ +#define WM8961_ALCL_SHIFT 0 /* ALCL - [3:0] */ +#define WM8961_ALCL_WIDTH 4 /* ALCL - [3:0] */ + +/* + * R18 (0x12) - ALC2 + */ +#define WM8961_ALCZC 0x0080 /* ALCZC */ +#define WM8961_ALCZC_MASK 0x0080 /* ALCZC */ +#define WM8961_ALCZC_SHIFT 7 /* ALCZC */ +#define WM8961_ALCZC_WIDTH 1 /* ALCZC */ +#define WM8961_MINGAIN_MASK 0x0070 /* MINGAIN - [6:4] */ +#define WM8961_MINGAIN_SHIFT 4 /* MINGAIN - [6:4] */ +#define WM8961_MINGAIN_WIDTH 3 /* MINGAIN - [6:4] */ +#define WM8961_HLD_MASK 0x000F /* HLD - [3:0] */ +#define WM8961_HLD_SHIFT 0 /* HLD - [3:0] */ +#define WM8961_HLD_WIDTH 4 /* HLD - [3:0] */ + +/* + * R19 (0x13) - ALC3 + */ +#define WM8961_ALCMODE 0x0100 /* ALCMODE */ +#define WM8961_ALCMODE_MASK 0x0100 /* ALCMODE */ +#define WM8961_ALCMODE_SHIFT 8 /* ALCMODE */ +#define WM8961_ALCMODE_WIDTH 1 /* ALCMODE */ +#define WM8961_DCY_MASK 0x00F0 /* DCY - [7:4] */ +#define WM8961_DCY_SHIFT 4 /* DCY - [7:4] */ +#define WM8961_DCY_WIDTH 4 /* DCY - [7:4] */ +#define WM8961_ATK_MASK 0x000F /* ATK - [3:0] */ +#define WM8961_ATK_SHIFT 0 /* ATK - [3:0] */ +#define WM8961_ATK_WIDTH 4 /* ATK - [3:0] */ + +/* + * R20 (0x14) - Noise Gate + */ +#define WM8961_NGTH_MASK 0x00F8 /* NGTH - [7:3] */ +#define WM8961_NGTH_SHIFT 3 /* NGTH - [7:3] */ +#define WM8961_NGTH_WIDTH 5 /* NGTH - [7:3] */ +#define WM8961_NGG 0x0002 /* NGG */ +#define WM8961_NGG_MASK 0x0002 /* NGG */ +#define WM8961_NGG_SHIFT 1 /* NGG */ +#define WM8961_NGG_WIDTH 1 /* NGG */ +#define WM8961_NGAT 0x0001 /* NGAT */ +#define WM8961_NGAT_MASK 0x0001 /* NGAT */ +#define WM8961_NGAT_SHIFT 0 /* NGAT */ +#define WM8961_NGAT_WIDTH 1 /* NGAT */ + +/* + * R21 (0x15) - Left ADC volume + */ +#define WM8961_ADCVU 0x0100 /* ADCVU */ +#define WM8961_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8961_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8961_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8961_LADCVOL_MASK 0x00FF /* LADCVOL - [7:0] */ +#define WM8961_LADCVOL_SHIFT 0 /* LADCVOL - [7:0] */ +#define WM8961_LADCVOL_WIDTH 8 /* LADCVOL - [7:0] */ + +/* + * R22 (0x16) - Right ADC volume + */ +#define WM8961_ADCVU 0x0100 /* ADCVU */ +#define WM8961_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8961_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8961_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8961_RADCVOL_MASK 0x00FF /* RADCVOL - [7:0] */ +#define WM8961_RADCVOL_SHIFT 0 /* RADCVOL - [7:0] */ +#define WM8961_RADCVOL_WIDTH 8 /* RADCVOL - [7:0] */ + +/* + * R23 (0x17) - Additional control(1) + */ +#define WM8961_TSDEN 0x0100 /* TSDEN */ +#define WM8961_TSDEN_MASK 0x0100 /* TSDEN */ +#define WM8961_TSDEN_SHIFT 8 /* TSDEN */ +#define WM8961_TSDEN_WIDTH 1 /* TSDEN */ +#define WM8961_DMONOMIX 0x0010 /* DMONOMIX */ +#define WM8961_DMONOMIX_MASK 0x0010 /* DMONOMIX */ +#define WM8961_DMONOMIX_SHIFT 4 /* DMONOMIX */ +#define WM8961_DMONOMIX_WIDTH 1 /* DMONOMIX */ +#define WM8961_TOEN 0x0001 /* TOEN */ +#define WM8961_TOEN_MASK 0x0001 /* TOEN */ +#define WM8961_TOEN_SHIFT 0 /* TOEN */ +#define WM8961_TOEN_WIDTH 1 /* TOEN */ + +/* + * R24 (0x18) - Additional control(2) + */ +#define WM8961_TRIS 0x0008 /* TRIS */ +#define WM8961_TRIS_MASK 0x0008 /* TRIS */ +#define WM8961_TRIS_SHIFT 3 /* TRIS */ +#define WM8961_TRIS_WIDTH 1 /* TRIS */ + +/* + * R25 (0x19) - Pwr Mgmt (1) + */ +#define WM8961_VMIDSEL_MASK 0x0180 /* VMIDSEL - [8:7] */ +#define WM8961_VMIDSEL_SHIFT 7 /* VMIDSEL - [8:7] */ +#define WM8961_VMIDSEL_WIDTH 2 /* VMIDSEL - [8:7] */ +#define WM8961_VREF 0x0040 /* VREF */ +#define WM8961_VREF_MASK 0x0040 /* VREF */ +#define WM8961_VREF_SHIFT 6 /* VREF */ +#define WM8961_VREF_WIDTH 1 /* VREF */ +#define WM8961_AINL 0x0020 /* AINL */ +#define WM8961_AINL_MASK 0x0020 /* AINL */ +#define WM8961_AINL_SHIFT 5 /* AINL */ +#define WM8961_AINL_WIDTH 1 /* AINL */ +#define WM8961_AINR 0x0010 /* AINR */ +#define WM8961_AINR_MASK 0x0010 /* AINR */ +#define WM8961_AINR_SHIFT 4 /* AINR */ +#define WM8961_AINR_WIDTH 1 /* AINR */ +#define WM8961_ADCL 0x0008 /* ADCL */ +#define WM8961_ADCL_MASK 0x0008 /* ADCL */ +#define WM8961_ADCL_SHIFT 3 /* ADCL */ +#define WM8961_ADCL_WIDTH 1 /* ADCL */ +#define WM8961_ADCR 0x0004 /* ADCR */ +#define WM8961_ADCR_MASK 0x0004 /* ADCR */ +#define WM8961_ADCR_SHIFT 2 /* ADCR */ +#define WM8961_ADCR_WIDTH 1 /* ADCR */ +#define WM8961_MICB 0x0002 /* MICB */ +#define WM8961_MICB_MASK 0x0002 /* MICB */ +#define WM8961_MICB_SHIFT 1 /* MICB */ +#define WM8961_MICB_WIDTH 1 /* MICB */ + +/* + * R26 (0x1A) - Pwr Mgmt (2) + */ +#define WM8961_DACL 0x0100 /* DACL */ +#define WM8961_DACL_MASK 0x0100 /* DACL */ +#define WM8961_DACL_SHIFT 8 /* DACL */ +#define WM8961_DACL_WIDTH 1 /* DACL */ +#define WM8961_DACR 0x0080 /* DACR */ +#define WM8961_DACR_MASK 0x0080 /* DACR */ +#define WM8961_DACR_SHIFT 7 /* DACR */ +#define WM8961_DACR_WIDTH 1 /* DACR */ +#define WM8961_LOUT1_PGA 0x0040 /* LOUT1_PGA */ +#define WM8961_LOUT1_PGA_MASK 0x0040 /* LOUT1_PGA */ +#define WM8961_LOUT1_PGA_SHIFT 6 /* LOUT1_PGA */ +#define WM8961_LOUT1_PGA_WIDTH 1 /* LOUT1_PGA */ +#define WM8961_ROUT1_PGA 0x0020 /* ROUT1_PGA */ +#define WM8961_ROUT1_PGA_MASK 0x0020 /* ROUT1_PGA */ +#define WM8961_ROUT1_PGA_SHIFT 5 /* ROUT1_PGA */ +#define WM8961_ROUT1_PGA_WIDTH 1 /* ROUT1_PGA */ +#define WM8961_SPKL_PGA 0x0010 /* SPKL_PGA */ +#define WM8961_SPKL_PGA_MASK 0x0010 /* SPKL_PGA */ +#define WM8961_SPKL_PGA_SHIFT 4 /* SPKL_PGA */ +#define WM8961_SPKL_PGA_WIDTH 1 /* SPKL_PGA */ +#define WM8961_SPKR_PGA 0x0008 /* SPKR_PGA */ +#define WM8961_SPKR_PGA_MASK 0x0008 /* SPKR_PGA */ +#define WM8961_SPKR_PGA_SHIFT 3 /* SPKR_PGA */ +#define WM8961_SPKR_PGA_WIDTH 1 /* SPKR_PGA */ + +/* + * R27 (0x1B) - Additional Control (3) + */ +#define WM8961_SAMPLE_RATE_MASK 0x0007 /* SAMPLE_RATE - [2:0] */ +#define WM8961_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [2:0] */ +#define WM8961_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [2:0] */ + +/* + * R28 (0x1C) - Anti-pop + */ +#define WM8961_BUFDCOPEN 0x0010 /* BUFDCOPEN */ +#define WM8961_BUFDCOPEN_MASK 0x0010 /* BUFDCOPEN */ +#define WM8961_BUFDCOPEN_SHIFT 4 /* BUFDCOPEN */ +#define WM8961_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */ +#define WM8961_BUFIOEN 0x0008 /* BUFIOEN */ +#define WM8961_BUFIOEN_MASK 0x0008 /* BUFIOEN */ +#define WM8961_BUFIOEN_SHIFT 3 /* BUFIOEN */ +#define WM8961_BUFIOEN_WIDTH 1 /* BUFIOEN */ +#define WM8961_SOFT_ST 0x0004 /* SOFT_ST */ +#define WM8961_SOFT_ST_MASK 0x0004 /* SOFT_ST */ +#define WM8961_SOFT_ST_SHIFT 2 /* SOFT_ST */ +#define WM8961_SOFT_ST_WIDTH 1 /* SOFT_ST */ + +/* + * R30 (0x1E) - Clocking 3 + */ +#define WM8961_CLK_TO_DIV_MASK 0x0180 /* CLK_TO_DIV - [8:7] */ +#define WM8961_CLK_TO_DIV_SHIFT 7 /* CLK_TO_DIV - [8:7] */ +#define WM8961_CLK_TO_DIV_WIDTH 2 /* CLK_TO_DIV - [8:7] */ +#define WM8961_CLK_256K_DIV_MASK 0x007E /* CLK_256K_DIV - [6:1] */ +#define WM8961_CLK_256K_DIV_SHIFT 1 /* CLK_256K_DIV - [6:1] */ +#define WM8961_CLK_256K_DIV_WIDTH 6 /* CLK_256K_DIV - [6:1] */ +#define WM8961_MANUAL_MODE 0x0001 /* MANUAL_MODE */ +#define WM8961_MANUAL_MODE_MASK 0x0001 /* MANUAL_MODE */ +#define WM8961_MANUAL_MODE_SHIFT 0 /* MANUAL_MODE */ +#define WM8961_MANUAL_MODE_WIDTH 1 /* MANUAL_MODE */ + +/* + * R32 (0x20) - ADCL signal path + */ +#define WM8961_LMICBOOST_MASK 0x0030 /* LMICBOOST - [5:4] */ +#define WM8961_LMICBOOST_SHIFT 4 /* LMICBOOST - [5:4] */ +#define WM8961_LMICBOOST_WIDTH 2 /* LMICBOOST - [5:4] */ + +/* + * R33 (0x21) - ADCR signal path + */ +#define WM8961_RMICBOOST_MASK 0x0030 /* RMICBOOST - [5:4] */ +#define WM8961_RMICBOOST_SHIFT 4 /* RMICBOOST - [5:4] */ +#define WM8961_RMICBOOST_WIDTH 2 /* RMICBOOST - [5:4] */ + +/* + * R40 (0x28) - LOUT2 volume + */ +#define WM8961_SPKVU 0x0100 /* SPKVU */ +#define WM8961_SPKVU_MASK 0x0100 /* SPKVU */ +#define WM8961_SPKVU_SHIFT 8 /* SPKVU */ +#define WM8961_SPKVU_WIDTH 1 /* SPKVU */ +#define WM8961_SPKLZC 0x0080 /* SPKLZC */ +#define WM8961_SPKLZC_MASK 0x0080 /* SPKLZC */ +#define WM8961_SPKLZC_SHIFT 7 /* SPKLZC */ +#define WM8961_SPKLZC_WIDTH 1 /* SPKLZC */ +#define WM8961_SPKLVOL_MASK 0x007F /* SPKLVOL - [6:0] */ +#define WM8961_SPKLVOL_SHIFT 0 /* SPKLVOL - [6:0] */ +#define WM8961_SPKLVOL_WIDTH 7 /* SPKLVOL - [6:0] */ + +/* + * R41 (0x29) - ROUT2 volume + */ +#define WM8961_SPKVU 0x0100 /* SPKVU */ +#define WM8961_SPKVU_MASK 0x0100 /* SPKVU */ +#define WM8961_SPKVU_SHIFT 8 /* SPKVU */ +#define WM8961_SPKVU_WIDTH 1 /* SPKVU */ +#define WM8961_SPKRZC 0x0080 /* SPKRZC */ +#define WM8961_SPKRZC_MASK 0x0080 /* SPKRZC */ +#define WM8961_SPKRZC_SHIFT 7 /* SPKRZC */ +#define WM8961_SPKRZC_WIDTH 1 /* SPKRZC */ +#define WM8961_SPKRVOL_MASK 0x007F /* SPKRVOL - [6:0] */ +#define WM8961_SPKRVOL_SHIFT 0 /* SPKRVOL - [6:0] */ +#define WM8961_SPKRVOL_WIDTH 7 /* SPKRVOL - [6:0] */ + +/* + * R47 (0x2F) - Pwr Mgmt (3) + */ +#define WM8961_TEMP_SHUT 0x0002 /* TEMP_SHUT */ +#define WM8961_TEMP_SHUT_MASK 0x0002 /* TEMP_SHUT */ +#define WM8961_TEMP_SHUT_SHIFT 1 /* TEMP_SHUT */ +#define WM8961_TEMP_SHUT_WIDTH 1 /* TEMP_SHUT */ +#define WM8961_TEMP_WARN 0x0001 /* TEMP_WARN */ +#define WM8961_TEMP_WARN_MASK 0x0001 /* TEMP_WARN */ +#define WM8961_TEMP_WARN_SHIFT 0 /* TEMP_WARN */ +#define WM8961_TEMP_WARN_WIDTH 1 /* TEMP_WARN */ + +/* + * R48 (0x30) - Additional Control (4) + */ +#define WM8961_TSENSEN 0x0002 /* TSENSEN */ +#define WM8961_TSENSEN_MASK 0x0002 /* TSENSEN */ +#define WM8961_TSENSEN_SHIFT 1 /* TSENSEN */ +#define WM8961_TSENSEN_WIDTH 1 /* TSENSEN */ +#define WM8961_MBSEL 0x0001 /* MBSEL */ +#define WM8961_MBSEL_MASK 0x0001 /* MBSEL */ +#define WM8961_MBSEL_SHIFT 0 /* MBSEL */ +#define WM8961_MBSEL_WIDTH 1 /* MBSEL */ + +/* + * R49 (0x31) - Class D Control 1 + */ +#define WM8961_SPKR_ENA 0x0080 /* SPKR_ENA */ +#define WM8961_SPKR_ENA_MASK 0x0080 /* SPKR_ENA */ +#define WM8961_SPKR_ENA_SHIFT 7 /* SPKR_ENA */ +#define WM8961_SPKR_ENA_WIDTH 1 /* SPKR_ENA */ +#define WM8961_SPKL_ENA 0x0040 /* SPKL_ENA */ +#define WM8961_SPKL_ENA_MASK 0x0040 /* SPKL_ENA */ +#define WM8961_SPKL_ENA_SHIFT 6 /* SPKL_ENA */ +#define WM8961_SPKL_ENA_WIDTH 1 /* SPKL_ENA */ + +/* + * R51 (0x33) - Class D Control 2 + */ +#define WM8961_CLASSD_ACGAIN_MASK 0x0007 /* CLASSD_ACGAIN - [2:0] */ +#define WM8961_CLASSD_ACGAIN_SHIFT 0 /* CLASSD_ACGAIN - [2:0] */ +#define WM8961_CLASSD_ACGAIN_WIDTH 3 /* CLASSD_ACGAIN - [2:0] */ + +/* + * R56 (0x38) - Clocking 4 + */ +#define WM8961_CLK_DCS_DIV_MASK 0x01E0 /* CLK_DCS_DIV - [8:5] */ +#define WM8961_CLK_DCS_DIV_SHIFT 5 /* CLK_DCS_DIV - [8:5] */ +#define WM8961_CLK_DCS_DIV_WIDTH 4 /* CLK_DCS_DIV - [8:5] */ +#define WM8961_CLK_SYS_RATE_MASK 0x001E /* CLK_SYS_RATE - [4:1] */ +#define WM8961_CLK_SYS_RATE_SHIFT 1 /* CLK_SYS_RATE - [4:1] */ +#define WM8961_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [4:1] */ + +/* + * R57 (0x39) - DSP Sidetone 0 + */ +#define WM8961_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8961_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8961_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8961_ADC_TO_DACR_MASK 0x000C /* ADC_TO_DACR - [3:2] */ +#define WM8961_ADC_TO_DACR_SHIFT 2 /* ADC_TO_DACR - [3:2] */ +#define WM8961_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [3:2] */ + +/* + * R58 (0x3A) - DSP Sidetone 1 + */ +#define WM8961_ADCL_DAC_SVOL_MASK 0x00F0 /* ADCL_DAC_SVOL - [7:4] */ +#define WM8961_ADCL_DAC_SVOL_SHIFT 4 /* ADCL_DAC_SVOL - [7:4] */ +#define WM8961_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [7:4] */ +#define WM8961_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */ +#define WM8961_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */ +#define WM8961_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */ + +/* + * R60 (0x3C) - DC Servo 0 + */ +#define WM8961_DCS_ENA_CHAN_INL 0x0080 /* DCS_ENA_CHAN_INL */ +#define WM8961_DCS_ENA_CHAN_INL_MASK 0x0080 /* DCS_ENA_CHAN_INL */ +#define WM8961_DCS_ENA_CHAN_INL_SHIFT 7 /* DCS_ENA_CHAN_INL */ +#define WM8961_DCS_ENA_CHAN_INL_WIDTH 1 /* DCS_ENA_CHAN_INL */ +#define WM8961_DCS_TRIG_STARTUP_INL 0x0040 /* DCS_TRIG_STARTUP_INL */ +#define WM8961_DCS_TRIG_STARTUP_INL_MASK 0x0040 /* DCS_TRIG_STARTUP_INL */ +#define WM8961_DCS_TRIG_STARTUP_INL_SHIFT 6 /* DCS_TRIG_STARTUP_INL */ +#define WM8961_DCS_TRIG_STARTUP_INL_WIDTH 1 /* DCS_TRIG_STARTUP_INL */ +#define WM8961_DCS_TRIG_SERIES_INL 0x0010 /* DCS_TRIG_SERIES_INL */ +#define WM8961_DCS_TRIG_SERIES_INL_MASK 0x0010 /* DCS_TRIG_SERIES_INL */ +#define WM8961_DCS_TRIG_SERIES_INL_SHIFT 4 /* DCS_TRIG_SERIES_INL */ +#define WM8961_DCS_TRIG_SERIES_INL_WIDTH 1 /* DCS_TRIG_SERIES_INL */ +#define WM8961_DCS_ENA_CHAN_INR 0x0008 /* DCS_ENA_CHAN_INR */ +#define WM8961_DCS_ENA_CHAN_INR_MASK 0x0008 /* DCS_ENA_CHAN_INR */ +#define WM8961_DCS_ENA_CHAN_INR_SHIFT 3 /* DCS_ENA_CHAN_INR */ +#define WM8961_DCS_ENA_CHAN_INR_WIDTH 1 /* DCS_ENA_CHAN_INR */ +#define WM8961_DCS_TRIG_STARTUP_INR 0x0004 /* DCS_TRIG_STARTUP_INR */ +#define WM8961_DCS_TRIG_STARTUP_INR_MASK 0x0004 /* DCS_TRIG_STARTUP_INR */ +#define WM8961_DCS_TRIG_STARTUP_INR_SHIFT 2 /* DCS_TRIG_STARTUP_INR */ +#define WM8961_DCS_TRIG_STARTUP_INR_WIDTH 1 /* DCS_TRIG_STARTUP_INR */ +#define WM8961_DCS_TRIG_SERIES_INR 0x0001 /* DCS_TRIG_SERIES_INR */ +#define WM8961_DCS_TRIG_SERIES_INR_MASK 0x0001 /* DCS_TRIG_SERIES_INR */ +#define WM8961_DCS_TRIG_SERIES_INR_SHIFT 0 /* DCS_TRIG_SERIES_INR */ +#define WM8961_DCS_TRIG_SERIES_INR_WIDTH 1 /* DCS_TRIG_SERIES_INR */ + +/* + * R61 (0x3D) - DC Servo 1 + */ +#define WM8961_DCS_ENA_CHAN_HPL 0x0080 /* DCS_ENA_CHAN_HPL */ +#define WM8961_DCS_ENA_CHAN_HPL_MASK 0x0080 /* DCS_ENA_CHAN_HPL */ +#define WM8961_DCS_ENA_CHAN_HPL_SHIFT 7 /* DCS_ENA_CHAN_HPL */ +#define WM8961_DCS_ENA_CHAN_HPL_WIDTH 1 /* DCS_ENA_CHAN_HPL */ +#define WM8961_DCS_TRIG_STARTUP_HPL 0x0040 /* DCS_TRIG_STARTUP_HPL */ +#define WM8961_DCS_TRIG_STARTUP_HPL_MASK 0x0040 /* DCS_TRIG_STARTUP_HPL */ +#define WM8961_DCS_TRIG_STARTUP_HPL_SHIFT 6 /* DCS_TRIG_STARTUP_HPL */ +#define WM8961_DCS_TRIG_STARTUP_HPL_WIDTH 1 /* DCS_TRIG_STARTUP_HPL */ +#define WM8961_DCS_TRIG_SERIES_HPL 0x0010 /* DCS_TRIG_SERIES_HPL */ +#define WM8961_DCS_TRIG_SERIES_HPL_MASK 0x0010 /* DCS_TRIG_SERIES_HPL */ +#define WM8961_DCS_TRIG_SERIES_HPL_SHIFT 4 /* DCS_TRIG_SERIES_HPL */ +#define WM8961_DCS_TRIG_SERIES_HPL_WIDTH 1 /* DCS_TRIG_SERIES_HPL */ +#define WM8961_DCS_ENA_CHAN_HPR 0x0008 /* DCS_ENA_CHAN_HPR */ +#define WM8961_DCS_ENA_CHAN_HPR_MASK 0x0008 /* DCS_ENA_CHAN_HPR */ +#define WM8961_DCS_ENA_CHAN_HPR_SHIFT 3 /* DCS_ENA_CHAN_HPR */ +#define WM8961_DCS_ENA_CHAN_HPR_WIDTH 1 /* DCS_ENA_CHAN_HPR */ +#define WM8961_DCS_TRIG_STARTUP_HPR 0x0004 /* DCS_TRIG_STARTUP_HPR */ +#define WM8961_DCS_TRIG_STARTUP_HPR_MASK 0x0004 /* DCS_TRIG_STARTUP_HPR */ +#define WM8961_DCS_TRIG_STARTUP_HPR_SHIFT 2 /* DCS_TRIG_STARTUP_HPR */ +#define WM8961_DCS_TRIG_STARTUP_HPR_WIDTH 1 /* DCS_TRIG_STARTUP_HPR */ +#define WM8961_DCS_TRIG_SERIES_HPR 0x0001 /* DCS_TRIG_SERIES_HPR */ +#define WM8961_DCS_TRIG_SERIES_HPR_MASK 0x0001 /* DCS_TRIG_SERIES_HPR */ +#define WM8961_DCS_TRIG_SERIES_HPR_SHIFT 0 /* DCS_TRIG_SERIES_HPR */ +#define WM8961_DCS_TRIG_SERIES_HPR_WIDTH 1 /* DCS_TRIG_SERIES_HPR */ + +/* + * R63 (0x3F) - DC Servo 3 + */ +#define WM8961_DCS_FILT_BW_SERIES_MASK 0x0030 /* DCS_FILT_BW_SERIES - [5:4] */ +#define WM8961_DCS_FILT_BW_SERIES_SHIFT 4 /* DCS_FILT_BW_SERIES - [5:4] */ +#define WM8961_DCS_FILT_BW_SERIES_WIDTH 2 /* DCS_FILT_BW_SERIES - [5:4] */ + +/* + * R65 (0x41) - DC Servo 5 + */ +#define WM8961_DCS_SERIES_NO_HP_MASK 0x007F /* DCS_SERIES_NO_HP - [6:0] */ +#define WM8961_DCS_SERIES_NO_HP_SHIFT 0 /* DCS_SERIES_NO_HP - [6:0] */ +#define WM8961_DCS_SERIES_NO_HP_WIDTH 7 /* DCS_SERIES_NO_HP - [6:0] */ + +/* + * R68 (0x44) - Analogue PGA Bias + */ +#define WM8961_HP_PGAS_BIAS_MASK 0x0007 /* HP_PGAS_BIAS - [2:0] */ +#define WM8961_HP_PGAS_BIAS_SHIFT 0 /* HP_PGAS_BIAS - [2:0] */ +#define WM8961_HP_PGAS_BIAS_WIDTH 3 /* HP_PGAS_BIAS - [2:0] */ + +/* + * R69 (0x45) - Analogue HP 0 + */ +#define WM8961_HPL_RMV_SHORT 0x0080 /* HPL_RMV_SHORT */ +#define WM8961_HPL_RMV_SHORT_MASK 0x0080 /* HPL_RMV_SHORT */ +#define WM8961_HPL_RMV_SHORT_SHIFT 7 /* HPL_RMV_SHORT */ +#define WM8961_HPL_RMV_SHORT_WIDTH 1 /* HPL_RMV_SHORT */ +#define WM8961_HPL_ENA_OUTP 0x0040 /* HPL_ENA_OUTP */ +#define WM8961_HPL_ENA_OUTP_MASK 0x0040 /* HPL_ENA_OUTP */ +#define WM8961_HPL_ENA_OUTP_SHIFT 6 /* HPL_ENA_OUTP */ +#define WM8961_HPL_ENA_OUTP_WIDTH 1 /* HPL_ENA_OUTP */ +#define WM8961_HPL_ENA_DLY 0x0020 /* HPL_ENA_DLY */ +#define WM8961_HPL_ENA_DLY_MASK 0x0020 /* HPL_ENA_DLY */ +#define WM8961_HPL_ENA_DLY_SHIFT 5 /* HPL_ENA_DLY */ +#define WM8961_HPL_ENA_DLY_WIDTH 1 /* HPL_ENA_DLY */ +#define WM8961_HPL_ENA 0x0010 /* HPL_ENA */ +#define WM8961_HPL_ENA_MASK 0x0010 /* HPL_ENA */ +#define WM8961_HPL_ENA_SHIFT 4 /* HPL_ENA */ +#define WM8961_HPL_ENA_WIDTH 1 /* HPL_ENA */ +#define WM8961_HPR_RMV_SHORT 0x0008 /* HPR_RMV_SHORT */ +#define WM8961_HPR_RMV_SHORT_MASK 0x0008 /* HPR_RMV_SHORT */ +#define WM8961_HPR_RMV_SHORT_SHIFT 3 /* HPR_RMV_SHORT */ +#define WM8961_HPR_RMV_SHORT_WIDTH 1 /* HPR_RMV_SHORT */ +#define WM8961_HPR_ENA_OUTP 0x0004 /* HPR_ENA_OUTP */ +#define WM8961_HPR_ENA_OUTP_MASK 0x0004 /* HPR_ENA_OUTP */ +#define WM8961_HPR_ENA_OUTP_SHIFT 2 /* HPR_ENA_OUTP */ +#define WM8961_HPR_ENA_OUTP_WIDTH 1 /* HPR_ENA_OUTP */ +#define WM8961_HPR_ENA_DLY 0x0002 /* HPR_ENA_DLY */ +#define WM8961_HPR_ENA_DLY_MASK 0x0002 /* HPR_ENA_DLY */ +#define WM8961_HPR_ENA_DLY_SHIFT 1 /* HPR_ENA_DLY */ +#define WM8961_HPR_ENA_DLY_WIDTH 1 /* HPR_ENA_DLY */ +#define WM8961_HPR_ENA 0x0001 /* HPR_ENA */ +#define WM8961_HPR_ENA_MASK 0x0001 /* HPR_ENA */ +#define WM8961_HPR_ENA_SHIFT 0 /* HPR_ENA */ +#define WM8961_HPR_ENA_WIDTH 1 /* HPR_ENA */ + +/* + * R71 (0x47) - Analogue HP 2 + */ +#define WM8961_HPL_VOL_MASK 0x01C0 /* HPL_VOL - [8:6] */ +#define WM8961_HPL_VOL_SHIFT 6 /* HPL_VOL - [8:6] */ +#define WM8961_HPL_VOL_WIDTH 3 /* HPL_VOL - [8:6] */ +#define WM8961_HPR_VOL_MASK 0x0038 /* HPR_VOL - [5:3] */ +#define WM8961_HPR_VOL_SHIFT 3 /* HPR_VOL - [5:3] */ +#define WM8961_HPR_VOL_WIDTH 3 /* HPR_VOL - [5:3] */ +#define WM8961_HP_BIAS_BOOST_MASK 0x0007 /* HP_BIAS_BOOST - [2:0] */ +#define WM8961_HP_BIAS_BOOST_SHIFT 0 /* HP_BIAS_BOOST - [2:0] */ +#define WM8961_HP_BIAS_BOOST_WIDTH 3 /* HP_BIAS_BOOST - [2:0] */ + +/* + * R72 (0x48) - Charge Pump 1 + */ +#define WM8961_CP_ENA 0x0001 /* CP_ENA */ +#define WM8961_CP_ENA_MASK 0x0001 /* CP_ENA */ +#define WM8961_CP_ENA_SHIFT 0 /* CP_ENA */ +#define WM8961_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R82 (0x52) - Charge Pump B + */ +#define WM8961_CP_DYN_PWR_MASK 0x0003 /* CP_DYN_PWR - [1:0] */ +#define WM8961_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR - [1:0] */ +#define WM8961_CP_DYN_PWR_WIDTH 2 /* CP_DYN_PWR - [1:0] */ + +/* + * R87 (0x57) - Write Sequencer 1 + */ +#define WM8961_WSEQ_ENA 0x0020 /* WSEQ_ENA */ +#define WM8961_WSEQ_ENA_MASK 0x0020 /* WSEQ_ENA */ +#define WM8961_WSEQ_ENA_SHIFT 5 /* WSEQ_ENA */ +#define WM8961_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM8961_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8961_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8961_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */ + +/* + * R88 (0x58) - Write Sequencer 2 + */ +#define WM8961_WSEQ_EOS 0x0100 /* WSEQ_EOS */ +#define WM8961_WSEQ_EOS_MASK 0x0100 /* WSEQ_EOS */ +#define WM8961_WSEQ_EOS_SHIFT 8 /* WSEQ_EOS */ +#define WM8961_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */ +#define WM8961_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */ +#define WM8961_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */ +#define WM8961_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */ + +/* + * R89 (0x59) - Write Sequencer 3 + */ +#define WM8961_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */ +#define WM8961_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */ +#define WM8961_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */ + +/* + * R90 (0x5A) - Write Sequencer 4 + */ +#define WM8961_WSEQ_ABORT 0x0100 /* WSEQ_ABORT */ +#define WM8961_WSEQ_ABORT_MASK 0x0100 /* WSEQ_ABORT */ +#define WM8961_WSEQ_ABORT_SHIFT 8 /* WSEQ_ABORT */ +#define WM8961_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM8961_WSEQ_START 0x0080 /* WSEQ_START */ +#define WM8961_WSEQ_START_MASK 0x0080 /* WSEQ_START */ +#define WM8961_WSEQ_START_SHIFT 7 /* WSEQ_START */ +#define WM8961_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM8961_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */ +#define WM8961_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */ +#define WM8961_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */ + +/* + * R91 (0x5B) - Write Sequencer 5 + */ +#define WM8961_WSEQ_DATA_WIDTH_MASK 0x0070 /* WSEQ_DATA_WIDTH - [6:4] */ +#define WM8961_WSEQ_DATA_WIDTH_SHIFT 4 /* WSEQ_DATA_WIDTH - [6:4] */ +#define WM8961_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [6:4] */ +#define WM8961_WSEQ_DATA_START_MASK 0x000F /* WSEQ_DATA_START - [3:0] */ +#define WM8961_WSEQ_DATA_START_SHIFT 0 /* WSEQ_DATA_START - [3:0] */ +#define WM8961_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [3:0] */ + +/* + * R92 (0x5C) - Write Sequencer 6 + */ +#define WM8961_WSEQ_DELAY_MASK 0x000F /* WSEQ_DELAY - [3:0] */ +#define WM8961_WSEQ_DELAY_SHIFT 0 /* WSEQ_DELAY - [3:0] */ +#define WM8961_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [3:0] */ + +/* + * R93 (0x5D) - Write Sequencer 7 + */ +#define WM8961_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */ +#define WM8961_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */ +#define WM8961_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */ +#define WM8961_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ + +/* + * R252 (0xFC) - General test 1 + */ +#define WM8961_ARA_ENA 0x0002 /* ARA_ENA */ +#define WM8961_ARA_ENA_MASK 0x0002 /* ARA_ENA */ +#define WM8961_ARA_ENA_SHIFT 1 /* ARA_ENA */ +#define WM8961_ARA_ENA_WIDTH 1 /* ARA_ENA */ +#define WM8961_AUTO_INC 0x0001 /* AUTO_INC */ +#define WM8961_AUTO_INC_MASK 0x0001 /* AUTO_INC */ +#define WM8961_AUTO_INC_SHIFT 0 /* AUTO_INC */ +#define WM8961_AUTO_INC_WIDTH 1 /* AUTO_INC */ + +#endif diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c new file mode 100644 index 0000000000..83ce5dbecc --- /dev/null +++ b/sound/soc/codecs/wm8962.c @@ -0,0 +1,3958 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8962.c -- WM8962 ALSA SoC Audio driver + * + * Copyright 2010-2 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8962.h" + +#define WM8962_NUM_SUPPLIES 8 +static const char *wm8962_supply_names[WM8962_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "AVDD", + "CPVDD", + "MICVDD", + "PLLVDD", + "SPKVDD1", + "SPKVDD2", +}; + +/* codec private data */ +struct wm8962_priv { + struct wm8962_pdata pdata; + struct regmap *regmap; + struct snd_soc_component *component; + + int sysclk; + int sysclk_rate; + + int bclk; /* Desired BCLK */ + int lrclk; + + struct completion fll_lock; + int fll_src; + int fll_fref; + int fll_fout; + + struct mutex dsp2_ena_lock; + u16 dsp2_ena; + + struct delayed_work mic_work; + struct snd_soc_jack *jack; + + struct regulator_bulk_data supplies[WM8962_NUM_SUPPLIES]; + struct notifier_block disable_nb[WM8962_NUM_SUPPLIES]; + + struct input_dev *beep; + struct work_struct beep_work; + int beep_rate; + +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif + + int irq; +}; + +/* We can't use the same notifier block for more than one supply and + * there's no way I can see to get from a callback to the caller + * except container_of(). + */ +#define WM8962_REGULATOR_EVENT(n) \ +static int wm8962_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct wm8962_priv *wm8962 = container_of(nb, struct wm8962_priv, \ + disable_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + regcache_mark_dirty(wm8962->regmap); \ + } \ + return 0; \ +} + +WM8962_REGULATOR_EVENT(0) +WM8962_REGULATOR_EVENT(1) +WM8962_REGULATOR_EVENT(2) +WM8962_REGULATOR_EVENT(3) +WM8962_REGULATOR_EVENT(4) +WM8962_REGULATOR_EVENT(5) +WM8962_REGULATOR_EVENT(6) +WM8962_REGULATOR_EVENT(7) + +static const struct reg_default wm8962_reg[] = { + { 0, 0x009F }, /* R0 - Left Input volume */ + { 1, 0x049F }, /* R1 - Right Input volume */ + { 2, 0x0000 }, /* R2 - HPOUTL volume */ + { 3, 0x0000 }, /* R3 - HPOUTR volume */ + + { 5, 0x0018 }, /* R5 - ADC & DAC Control 1 */ + { 6, 0x2008 }, /* R6 - ADC & DAC Control 2 */ + { 7, 0x000A }, /* R7 - Audio Interface 0 */ + { 8, 0x01E4 }, /* R8 - Clocking2 */ + { 9, 0x0300 }, /* R9 - Audio Interface 1 */ + { 10, 0x00C0 }, /* R10 - Left DAC volume */ + { 11, 0x00C0 }, /* R11 - Right DAC volume */ + + { 14, 0x0040 }, /* R14 - Audio Interface 2 */ + { 15, 0x6243 }, /* R15 - Software Reset */ + + { 17, 0x007B }, /* R17 - ALC1 */ + { 18, 0x0000 }, /* R18 - ALC2 */ + { 19, 0x1C32 }, /* R19 - ALC3 */ + { 20, 0x3200 }, /* R20 - Noise Gate */ + { 21, 0x00C0 }, /* R21 - Left ADC volume */ + { 22, 0x00C0 }, /* R22 - Right ADC volume */ + { 23, 0x0160 }, /* R23 - Additional control(1) */ + { 24, 0x0000 }, /* R24 - Additional control(2) */ + { 25, 0x0000 }, /* R25 - Pwr Mgmt (1) */ + { 26, 0x0000 }, /* R26 - Pwr Mgmt (2) */ + { 27, 0x0010 }, /* R27 - Additional Control (3) */ + { 28, 0x0000 }, /* R28 - Anti-pop */ + + { 30, 0x005E }, /* R30 - Clocking 3 */ + { 31, 0x0000 }, /* R31 - Input mixer control (1) */ + { 32, 0x0145 }, /* R32 - Left input mixer volume */ + { 33, 0x0145 }, /* R33 - Right input mixer volume */ + { 34, 0x0009 }, /* R34 - Input mixer control (2) */ + { 35, 0x0003 }, /* R35 - Input bias control */ + { 37, 0x0008 }, /* R37 - Left input PGA control */ + { 38, 0x0008 }, /* R38 - Right input PGA control */ + + { 40, 0x0000 }, /* R40 - SPKOUTL volume */ + { 41, 0x0000 }, /* R41 - SPKOUTR volume */ + + { 49, 0x0010 }, /* R49 - Class D Control 1 */ + { 51, 0x0003 }, /* R51 - Class D Control 2 */ + + { 56, 0x0506 }, /* R56 - Clocking 4 */ + { 57, 0x0000 }, /* R57 - DAC DSP Mixing (1) */ + { 58, 0x0000 }, /* R58 - DAC DSP Mixing (2) */ + + { 60, 0x0300 }, /* R60 - DC Servo 0 */ + { 61, 0x0300 }, /* R61 - DC Servo 1 */ + + { 64, 0x0810 }, /* R64 - DC Servo 4 */ + + { 68, 0x001B }, /* R68 - Analogue PGA Bias */ + { 69, 0x0000 }, /* R69 - Analogue HP 0 */ + + { 71, 0x01FB }, /* R71 - Analogue HP 2 */ + { 72, 0x0000 }, /* R72 - Charge Pump 1 */ + + { 82, 0x0004 }, /* R82 - Charge Pump B */ + + { 87, 0x0000 }, /* R87 - Write Sequencer Control 1 */ + + { 90, 0x0000 }, /* R90 - Write Sequencer Control 2 */ + + { 93, 0x0000 }, /* R93 - Write Sequencer Control 3 */ + { 94, 0x0000 }, /* R94 - Control Interface */ + + { 99, 0x0000 }, /* R99 - Mixer Enables */ + { 100, 0x0000 }, /* R100 - Headphone Mixer (1) */ + { 101, 0x0000 }, /* R101 - Headphone Mixer (2) */ + { 102, 0x013F }, /* R102 - Headphone Mixer (3) */ + { 103, 0x013F }, /* R103 - Headphone Mixer (4) */ + + { 105, 0x0000 }, /* R105 - Speaker Mixer (1) */ + { 106, 0x0000 }, /* R106 - Speaker Mixer (2) */ + { 107, 0x013F }, /* R107 - Speaker Mixer (3) */ + { 108, 0x013F }, /* R108 - Speaker Mixer (4) */ + { 109, 0x0003 }, /* R109 - Speaker Mixer (5) */ + { 110, 0x0002 }, /* R110 - Beep Generator (1) */ + + { 115, 0x0006 }, /* R115 - Oscillator Trim (3) */ + { 116, 0x0026 }, /* R116 - Oscillator Trim (4) */ + + { 119, 0x0000 }, /* R119 - Oscillator Trim (7) */ + + { 124, 0x0011 }, /* R124 - Analogue Clocking1 */ + { 125, 0x004B }, /* R125 - Analogue Clocking2 */ + { 126, 0x000D }, /* R126 - Analogue Clocking3 */ + { 127, 0x0000 }, /* R127 - PLL Software Reset */ + + { 131, 0x0000 }, /* R131 - PLL 4 */ + + { 136, 0x0067 }, /* R136 - PLL 9 */ + { 137, 0x001C }, /* R137 - PLL 10 */ + { 138, 0x0071 }, /* R138 - PLL 11 */ + { 139, 0x00C7 }, /* R139 - PLL 12 */ + { 140, 0x0067 }, /* R140 - PLL 13 */ + { 141, 0x0048 }, /* R141 - PLL 14 */ + { 142, 0x0022 }, /* R142 - PLL 15 */ + { 143, 0x0097 }, /* R143 - PLL 16 */ + + { 155, 0x000C }, /* R155 - FLL Control (1) */ + { 156, 0x0039 }, /* R156 - FLL Control (2) */ + { 157, 0x0180 }, /* R157 - FLL Control (3) */ + + { 159, 0x0032 }, /* R159 - FLL Control (5) */ + { 160, 0x0018 }, /* R160 - FLL Control (6) */ + { 161, 0x007D }, /* R161 - FLL Control (7) */ + { 162, 0x0008 }, /* R162 - FLL Control (8) */ + + { 252, 0x0005 }, /* R252 - General test 1 */ + + { 256, 0x0000 }, /* R256 - DF1 */ + { 257, 0x0000 }, /* R257 - DF2 */ + { 258, 0x0000 }, /* R258 - DF3 */ + { 259, 0x0000 }, /* R259 - DF4 */ + { 260, 0x0000 }, /* R260 - DF5 */ + { 261, 0x0000 }, /* R261 - DF6 */ + { 262, 0x0000 }, /* R262 - DF7 */ + + { 264, 0x0000 }, /* R264 - LHPF1 */ + { 265, 0x0000 }, /* R265 - LHPF2 */ + + { 268, 0x0000 }, /* R268 - THREED1 */ + { 269, 0x0000 }, /* R269 - THREED2 */ + { 270, 0x0000 }, /* R270 - THREED3 */ + { 271, 0x0000 }, /* R271 - THREED4 */ + + { 276, 0x000C }, /* R276 - DRC 1 */ + { 277, 0x0925 }, /* R277 - DRC 2 */ + { 278, 0x0000 }, /* R278 - DRC 3 */ + { 279, 0x0000 }, /* R279 - DRC 4 */ + { 280, 0x0000 }, /* R280 - DRC 5 */ + + { 285, 0x0000 }, /* R285 - Tloopback */ + + { 335, 0x0004 }, /* R335 - EQ1 */ + { 336, 0x6318 }, /* R336 - EQ2 */ + { 337, 0x6300 }, /* R337 - EQ3 */ + { 338, 0x0FCA }, /* R338 - EQ4 */ + { 339, 0x0400 }, /* R339 - EQ5 */ + { 340, 0x00D8 }, /* R340 - EQ6 */ + { 341, 0x1EB5 }, /* R341 - EQ7 */ + { 342, 0xF145 }, /* R342 - EQ8 */ + { 343, 0x0B75 }, /* R343 - EQ9 */ + { 344, 0x01C5 }, /* R344 - EQ10 */ + { 345, 0x1C58 }, /* R345 - EQ11 */ + { 346, 0xF373 }, /* R346 - EQ12 */ + { 347, 0x0A54 }, /* R347 - EQ13 */ + { 348, 0x0558 }, /* R348 - EQ14 */ + { 349, 0x168E }, /* R349 - EQ15 */ + { 350, 0xF829 }, /* R350 - EQ16 */ + { 351, 0x07AD }, /* R351 - EQ17 */ + { 352, 0x1103 }, /* R352 - EQ18 */ + { 353, 0x0564 }, /* R353 - EQ19 */ + { 354, 0x0559 }, /* R354 - EQ20 */ + { 355, 0x4000 }, /* R355 - EQ21 */ + { 356, 0x6318 }, /* R356 - EQ22 */ + { 357, 0x6300 }, /* R357 - EQ23 */ + { 358, 0x0FCA }, /* R358 - EQ24 */ + { 359, 0x0400 }, /* R359 - EQ25 */ + { 360, 0x00D8 }, /* R360 - EQ26 */ + { 361, 0x1EB5 }, /* R361 - EQ27 */ + { 362, 0xF145 }, /* R362 - EQ28 */ + { 363, 0x0B75 }, /* R363 - EQ29 */ + { 364, 0x01C5 }, /* R364 - EQ30 */ + { 365, 0x1C58 }, /* R365 - EQ31 */ + { 366, 0xF373 }, /* R366 - EQ32 */ + { 367, 0x0A54 }, /* R367 - EQ33 */ + { 368, 0x0558 }, /* R368 - EQ34 */ + { 369, 0x168E }, /* R369 - EQ35 */ + { 370, 0xF829 }, /* R370 - EQ36 */ + { 371, 0x07AD }, /* R371 - EQ37 */ + { 372, 0x1103 }, /* R372 - EQ38 */ + { 373, 0x0564 }, /* R373 - EQ39 */ + { 374, 0x0559 }, /* R374 - EQ40 */ + { 375, 0x4000 }, /* R375 - EQ41 */ + + { 513, 0x0000 }, /* R513 - GPIO 2 */ + { 514, 0x0000 }, /* R514 - GPIO 3 */ + + { 516, 0x8100 }, /* R516 - GPIO 5 */ + { 517, 0x8100 }, /* R517 - GPIO 6 */ + + { 568, 0x0030 }, /* R568 - Interrupt Status 1 Mask */ + { 569, 0xFFED }, /* R569 - Interrupt Status 2 Mask */ + + { 576, 0x0000 }, /* R576 - Interrupt Control */ + + { 584, 0x002D }, /* R584 - IRQ Debounce */ + + { 586, 0x0000 }, /* R586 - MICINT Source Pol */ + + { 768, 0x1C00 }, /* R768 - DSP2 Power Management */ + + { 8192, 0x0000 }, /* R8192 - DSP2 Instruction RAM 0 */ + + { 9216, 0x0030 }, /* R9216 - DSP2 Address RAM 2 */ + { 9217, 0x0000 }, /* R9217 - DSP2 Address RAM 1 */ + { 9218, 0x0000 }, /* R9218 - DSP2 Address RAM 0 */ + + { 12288, 0x0000 }, /* R12288 - DSP2 Data1 RAM 1 */ + { 12289, 0x0000 }, /* R12289 - DSP2 Data1 RAM 0 */ + + { 13312, 0x0000 }, /* R13312 - DSP2 Data2 RAM 1 */ + { 13313, 0x0000 }, /* R13313 - DSP2 Data2 RAM 0 */ + + { 14336, 0x0000 }, /* R14336 - DSP2 Data3 RAM 1 */ + { 14337, 0x0000 }, /* R14337 - DSP2 Data3 RAM 0 */ + + { 15360, 0x000A }, /* R15360 - DSP2 Coeff RAM 0 */ + + { 16384, 0x0000 }, /* R16384 - RETUNEADC_SHARED_COEFF_1 */ + { 16385, 0x0000 }, /* R16385 - RETUNEADC_SHARED_COEFF_0 */ + { 16386, 0x0000 }, /* R16386 - RETUNEDAC_SHARED_COEFF_1 */ + { 16387, 0x0000 }, /* R16387 - RETUNEDAC_SHARED_COEFF_0 */ + { 16388, 0x0000 }, /* R16388 - SOUNDSTAGE_ENABLES_1 */ + { 16389, 0x0000 }, /* R16389 - SOUNDSTAGE_ENABLES_0 */ + + { 16896, 0x0002 }, /* R16896 - HDBASS_AI_1 */ + { 16897, 0xBD12 }, /* R16897 - HDBASS_AI_0 */ + { 16898, 0x007C }, /* R16898 - HDBASS_AR_1 */ + { 16899, 0x586C }, /* R16899 - HDBASS_AR_0 */ + { 16900, 0x0053 }, /* R16900 - HDBASS_B_1 */ + { 16901, 0x8121 }, /* R16901 - HDBASS_B_0 */ + { 16902, 0x003F }, /* R16902 - HDBASS_K_1 */ + { 16903, 0x8BD8 }, /* R16903 - HDBASS_K_0 */ + { 16904, 0x0032 }, /* R16904 - HDBASS_N1_1 */ + { 16905, 0xF52D }, /* R16905 - HDBASS_N1_0 */ + { 16906, 0x0065 }, /* R16906 - HDBASS_N2_1 */ + { 16907, 0xAC8C }, /* R16907 - HDBASS_N2_0 */ + { 16908, 0x006B }, /* R16908 - HDBASS_N3_1 */ + { 16909, 0xE087 }, /* R16909 - HDBASS_N3_0 */ + { 16910, 0x0072 }, /* R16910 - HDBASS_N4_1 */ + { 16911, 0x1483 }, /* R16911 - HDBASS_N4_0 */ + { 16912, 0x0072 }, /* R16912 - HDBASS_N5_1 */ + { 16913, 0x1483 }, /* R16913 - HDBASS_N5_0 */ + { 16914, 0x0043 }, /* R16914 - HDBASS_X1_1 */ + { 16915, 0x3525 }, /* R16915 - HDBASS_X1_0 */ + { 16916, 0x0006 }, /* R16916 - HDBASS_X2_1 */ + { 16917, 0x6A4A }, /* R16917 - HDBASS_X2_0 */ + { 16918, 0x0043 }, /* R16918 - HDBASS_X3_1 */ + { 16919, 0x6079 }, /* R16919 - HDBASS_X3_0 */ + { 16920, 0x0008 }, /* R16920 - HDBASS_ATK_1 */ + { 16921, 0x0000 }, /* R16921 - HDBASS_ATK_0 */ + { 16922, 0x0001 }, /* R16922 - HDBASS_DCY_1 */ + { 16923, 0x0000 }, /* R16923 - HDBASS_DCY_0 */ + { 16924, 0x0059 }, /* R16924 - HDBASS_PG_1 */ + { 16925, 0x999A }, /* R16925 - HDBASS_PG_0 */ + + { 17408, 0x0083 }, /* R17408 - HPF_C_1 */ + { 17409, 0x98AD }, /* R17409 - HPF_C_0 */ + + { 17920, 0x007F }, /* R17920 - ADCL_RETUNE_C1_1 */ + { 17921, 0xFFFF }, /* R17921 - ADCL_RETUNE_C1_0 */ + { 17922, 0x0000 }, /* R17922 - ADCL_RETUNE_C2_1 */ + { 17923, 0x0000 }, /* R17923 - ADCL_RETUNE_C2_0 */ + { 17924, 0x0000 }, /* R17924 - ADCL_RETUNE_C3_1 */ + { 17925, 0x0000 }, /* R17925 - ADCL_RETUNE_C3_0 */ + { 17926, 0x0000 }, /* R17926 - ADCL_RETUNE_C4_1 */ + { 17927, 0x0000 }, /* R17927 - ADCL_RETUNE_C4_0 */ + { 17928, 0x0000 }, /* R17928 - ADCL_RETUNE_C5_1 */ + { 17929, 0x0000 }, /* R17929 - ADCL_RETUNE_C5_0 */ + { 17930, 0x0000 }, /* R17930 - ADCL_RETUNE_C6_1 */ + { 17931, 0x0000 }, /* R17931 - ADCL_RETUNE_C6_0 */ + { 17932, 0x0000 }, /* R17932 - ADCL_RETUNE_C7_1 */ + { 17933, 0x0000 }, /* R17933 - ADCL_RETUNE_C7_0 */ + { 17934, 0x0000 }, /* R17934 - ADCL_RETUNE_C8_1 */ + { 17935, 0x0000 }, /* R17935 - ADCL_RETUNE_C8_0 */ + { 17936, 0x0000 }, /* R17936 - ADCL_RETUNE_C9_1 */ + { 17937, 0x0000 }, /* R17937 - ADCL_RETUNE_C9_0 */ + { 17938, 0x0000 }, /* R17938 - ADCL_RETUNE_C10_1 */ + { 17939, 0x0000 }, /* R17939 - ADCL_RETUNE_C10_0 */ + { 17940, 0x0000 }, /* R17940 - ADCL_RETUNE_C11_1 */ + { 17941, 0x0000 }, /* R17941 - ADCL_RETUNE_C11_0 */ + { 17942, 0x0000 }, /* R17942 - ADCL_RETUNE_C12_1 */ + { 17943, 0x0000 }, /* R17943 - ADCL_RETUNE_C12_0 */ + { 17944, 0x0000 }, /* R17944 - ADCL_RETUNE_C13_1 */ + { 17945, 0x0000 }, /* R17945 - ADCL_RETUNE_C13_0 */ + { 17946, 0x0000 }, /* R17946 - ADCL_RETUNE_C14_1 */ + { 17947, 0x0000 }, /* R17947 - ADCL_RETUNE_C14_0 */ + { 17948, 0x0000 }, /* R17948 - ADCL_RETUNE_C15_1 */ + { 17949, 0x0000 }, /* R17949 - ADCL_RETUNE_C15_0 */ + { 17950, 0x0000 }, /* R17950 - ADCL_RETUNE_C16_1 */ + { 17951, 0x0000 }, /* R17951 - ADCL_RETUNE_C16_0 */ + { 17952, 0x0000 }, /* R17952 - ADCL_RETUNE_C17_1 */ + { 17953, 0x0000 }, /* R17953 - ADCL_RETUNE_C17_0 */ + { 17954, 0x0000 }, /* R17954 - ADCL_RETUNE_C18_1 */ + { 17955, 0x0000 }, /* R17955 - ADCL_RETUNE_C18_0 */ + { 17956, 0x0000 }, /* R17956 - ADCL_RETUNE_C19_1 */ + { 17957, 0x0000 }, /* R17957 - ADCL_RETUNE_C19_0 */ + { 17958, 0x0000 }, /* R17958 - ADCL_RETUNE_C20_1 */ + { 17959, 0x0000 }, /* R17959 - ADCL_RETUNE_C20_0 */ + { 17960, 0x0000 }, /* R17960 - ADCL_RETUNE_C21_1 */ + { 17961, 0x0000 }, /* R17961 - ADCL_RETUNE_C21_0 */ + { 17962, 0x0000 }, /* R17962 - ADCL_RETUNE_C22_1 */ + { 17963, 0x0000 }, /* R17963 - ADCL_RETUNE_C22_0 */ + { 17964, 0x0000 }, /* R17964 - ADCL_RETUNE_C23_1 */ + { 17965, 0x0000 }, /* R17965 - ADCL_RETUNE_C23_0 */ + { 17966, 0x0000 }, /* R17966 - ADCL_RETUNE_C24_1 */ + { 17967, 0x0000 }, /* R17967 - ADCL_RETUNE_C24_0 */ + { 17968, 0x0000 }, /* R17968 - ADCL_RETUNE_C25_1 */ + { 17969, 0x0000 }, /* R17969 - ADCL_RETUNE_C25_0 */ + { 17970, 0x0000 }, /* R17970 - ADCL_RETUNE_C26_1 */ + { 17971, 0x0000 }, /* R17971 - ADCL_RETUNE_C26_0 */ + { 17972, 0x0000 }, /* R17972 - ADCL_RETUNE_C27_1 */ + { 17973, 0x0000 }, /* R17973 - ADCL_RETUNE_C27_0 */ + { 17974, 0x0000 }, /* R17974 - ADCL_RETUNE_C28_1 */ + { 17975, 0x0000 }, /* R17975 - ADCL_RETUNE_C28_0 */ + { 17976, 0x0000 }, /* R17976 - ADCL_RETUNE_C29_1 */ + { 17977, 0x0000 }, /* R17977 - ADCL_RETUNE_C29_0 */ + { 17978, 0x0000 }, /* R17978 - ADCL_RETUNE_C30_1 */ + { 17979, 0x0000 }, /* R17979 - ADCL_RETUNE_C30_0 */ + { 17980, 0x0000 }, /* R17980 - ADCL_RETUNE_C31_1 */ + { 17981, 0x0000 }, /* R17981 - ADCL_RETUNE_C31_0 */ + { 17982, 0x0000 }, /* R17982 - ADCL_RETUNE_C32_1 */ + { 17983, 0x0000 }, /* R17983 - ADCL_RETUNE_C32_0 */ + + { 18432, 0x0020 }, /* R18432 - RETUNEADC_PG2_1 */ + { 18433, 0x0000 }, /* R18433 - RETUNEADC_PG2_0 */ + { 18434, 0x0040 }, /* R18434 - RETUNEADC_PG_1 */ + { 18435, 0x0000 }, /* R18435 - RETUNEADC_PG_0 */ + + { 18944, 0x007F }, /* R18944 - ADCR_RETUNE_C1_1 */ + { 18945, 0xFFFF }, /* R18945 - ADCR_RETUNE_C1_0 */ + { 18946, 0x0000 }, /* R18946 - ADCR_RETUNE_C2_1 */ + { 18947, 0x0000 }, /* R18947 - ADCR_RETUNE_C2_0 */ + { 18948, 0x0000 }, /* R18948 - ADCR_RETUNE_C3_1 */ + { 18949, 0x0000 }, /* R18949 - ADCR_RETUNE_C3_0 */ + { 18950, 0x0000 }, /* R18950 - ADCR_RETUNE_C4_1 */ + { 18951, 0x0000 }, /* R18951 - ADCR_RETUNE_C4_0 */ + { 18952, 0x0000 }, /* R18952 - ADCR_RETUNE_C5_1 */ + { 18953, 0x0000 }, /* R18953 - ADCR_RETUNE_C5_0 */ + { 18954, 0x0000 }, /* R18954 - ADCR_RETUNE_C6_1 */ + { 18955, 0x0000 }, /* R18955 - ADCR_RETUNE_C6_0 */ + { 18956, 0x0000 }, /* R18956 - ADCR_RETUNE_C7_1 */ + { 18957, 0x0000 }, /* R18957 - ADCR_RETUNE_C7_0 */ + { 18958, 0x0000 }, /* R18958 - ADCR_RETUNE_C8_1 */ + { 18959, 0x0000 }, /* R18959 - ADCR_RETUNE_C8_0 */ + { 18960, 0x0000 }, /* R18960 - ADCR_RETUNE_C9_1 */ + { 18961, 0x0000 }, /* R18961 - ADCR_RETUNE_C9_0 */ + { 18962, 0x0000 }, /* R18962 - ADCR_RETUNE_C10_1 */ + { 18963, 0x0000 }, /* R18963 - ADCR_RETUNE_C10_0 */ + { 18964, 0x0000 }, /* R18964 - ADCR_RETUNE_C11_1 */ + { 18965, 0x0000 }, /* R18965 - ADCR_RETUNE_C11_0 */ + { 18966, 0x0000 }, /* R18966 - ADCR_RETUNE_C12_1 */ + { 18967, 0x0000 }, /* R18967 - ADCR_RETUNE_C12_0 */ + { 18968, 0x0000 }, /* R18968 - ADCR_RETUNE_C13_1 */ + { 18969, 0x0000 }, /* R18969 - ADCR_RETUNE_C13_0 */ + { 18970, 0x0000 }, /* R18970 - ADCR_RETUNE_C14_1 */ + { 18971, 0x0000 }, /* R18971 - ADCR_RETUNE_C14_0 */ + { 18972, 0x0000 }, /* R18972 - ADCR_RETUNE_C15_1 */ + { 18973, 0x0000 }, /* R18973 - ADCR_RETUNE_C15_0 */ + { 18974, 0x0000 }, /* R18974 - ADCR_RETUNE_C16_1 */ + { 18975, 0x0000 }, /* R18975 - ADCR_RETUNE_C16_0 */ + { 18976, 0x0000 }, /* R18976 - ADCR_RETUNE_C17_1 */ + { 18977, 0x0000 }, /* R18977 - ADCR_RETUNE_C17_0 */ + { 18978, 0x0000 }, /* R18978 - ADCR_RETUNE_C18_1 */ + { 18979, 0x0000 }, /* R18979 - ADCR_RETUNE_C18_0 */ + { 18980, 0x0000 }, /* R18980 - ADCR_RETUNE_C19_1 */ + { 18981, 0x0000 }, /* R18981 - ADCR_RETUNE_C19_0 */ + { 18982, 0x0000 }, /* R18982 - ADCR_RETUNE_C20_1 */ + { 18983, 0x0000 }, /* R18983 - ADCR_RETUNE_C20_0 */ + { 18984, 0x0000 }, /* R18984 - ADCR_RETUNE_C21_1 */ + { 18985, 0x0000 }, /* R18985 - ADCR_RETUNE_C21_0 */ + { 18986, 0x0000 }, /* R18986 - ADCR_RETUNE_C22_1 */ + { 18987, 0x0000 }, /* R18987 - ADCR_RETUNE_C22_0 */ + { 18988, 0x0000 }, /* R18988 - ADCR_RETUNE_C23_1 */ + { 18989, 0x0000 }, /* R18989 - ADCR_RETUNE_C23_0 */ + { 18990, 0x0000 }, /* R18990 - ADCR_RETUNE_C24_1 */ + { 18991, 0x0000 }, /* R18991 - ADCR_RETUNE_C24_0 */ + { 18992, 0x0000 }, /* R18992 - ADCR_RETUNE_C25_1 */ + { 18993, 0x0000 }, /* R18993 - ADCR_RETUNE_C25_0 */ + { 18994, 0x0000 }, /* R18994 - ADCR_RETUNE_C26_1 */ + { 18995, 0x0000 }, /* R18995 - ADCR_RETUNE_C26_0 */ + { 18996, 0x0000 }, /* R18996 - ADCR_RETUNE_C27_1 */ + { 18997, 0x0000 }, /* R18997 - ADCR_RETUNE_C27_0 */ + { 18998, 0x0000 }, /* R18998 - ADCR_RETUNE_C28_1 */ + { 18999, 0x0000 }, /* R18999 - ADCR_RETUNE_C28_0 */ + { 19000, 0x0000 }, /* R19000 - ADCR_RETUNE_C29_1 */ + { 19001, 0x0000 }, /* R19001 - ADCR_RETUNE_C29_0 */ + { 19002, 0x0000 }, /* R19002 - ADCR_RETUNE_C30_1 */ + { 19003, 0x0000 }, /* R19003 - ADCR_RETUNE_C30_0 */ + { 19004, 0x0000 }, /* R19004 - ADCR_RETUNE_C31_1 */ + { 19005, 0x0000 }, /* R19005 - ADCR_RETUNE_C31_0 */ + { 19006, 0x0000 }, /* R19006 - ADCR_RETUNE_C32_1 */ + { 19007, 0x0000 }, /* R19007 - ADCR_RETUNE_C32_0 */ + + { 19456, 0x007F }, /* R19456 - DACL_RETUNE_C1_1 */ + { 19457, 0xFFFF }, /* R19457 - DACL_RETUNE_C1_0 */ + { 19458, 0x0000 }, /* R19458 - DACL_RETUNE_C2_1 */ + { 19459, 0x0000 }, /* R19459 - DACL_RETUNE_C2_0 */ + { 19460, 0x0000 }, /* R19460 - DACL_RETUNE_C3_1 */ + { 19461, 0x0000 }, /* R19461 - DACL_RETUNE_C3_0 */ + { 19462, 0x0000 }, /* R19462 - DACL_RETUNE_C4_1 */ + { 19463, 0x0000 }, /* R19463 - DACL_RETUNE_C4_0 */ + { 19464, 0x0000 }, /* R19464 - DACL_RETUNE_C5_1 */ + { 19465, 0x0000 }, /* R19465 - DACL_RETUNE_C5_0 */ + { 19466, 0x0000 }, /* R19466 - DACL_RETUNE_C6_1 */ + { 19467, 0x0000 }, /* R19467 - DACL_RETUNE_C6_0 */ + { 19468, 0x0000 }, /* R19468 - DACL_RETUNE_C7_1 */ + { 19469, 0x0000 }, /* R19469 - DACL_RETUNE_C7_0 */ + { 19470, 0x0000 }, /* R19470 - DACL_RETUNE_C8_1 */ + { 19471, 0x0000 }, /* R19471 - DACL_RETUNE_C8_0 */ + { 19472, 0x0000 }, /* R19472 - DACL_RETUNE_C9_1 */ + { 19473, 0x0000 }, /* R19473 - DACL_RETUNE_C9_0 */ + { 19474, 0x0000 }, /* R19474 - DACL_RETUNE_C10_1 */ + { 19475, 0x0000 }, /* R19475 - DACL_RETUNE_C10_0 */ + { 19476, 0x0000 }, /* R19476 - DACL_RETUNE_C11_1 */ + { 19477, 0x0000 }, /* R19477 - DACL_RETUNE_C11_0 */ + { 19478, 0x0000 }, /* R19478 - DACL_RETUNE_C12_1 */ + { 19479, 0x0000 }, /* R19479 - DACL_RETUNE_C12_0 */ + { 19480, 0x0000 }, /* R19480 - DACL_RETUNE_C13_1 */ + { 19481, 0x0000 }, /* R19481 - DACL_RETUNE_C13_0 */ + { 19482, 0x0000 }, /* R19482 - DACL_RETUNE_C14_1 */ + { 19483, 0x0000 }, /* R19483 - DACL_RETUNE_C14_0 */ + { 19484, 0x0000 }, /* R19484 - DACL_RETUNE_C15_1 */ + { 19485, 0x0000 }, /* R19485 - DACL_RETUNE_C15_0 */ + { 19486, 0x0000 }, /* R19486 - DACL_RETUNE_C16_1 */ + { 19487, 0x0000 }, /* R19487 - DACL_RETUNE_C16_0 */ + { 19488, 0x0000 }, /* R19488 - DACL_RETUNE_C17_1 */ + { 19489, 0x0000 }, /* R19489 - DACL_RETUNE_C17_0 */ + { 19490, 0x0000 }, /* R19490 - DACL_RETUNE_C18_1 */ + { 19491, 0x0000 }, /* R19491 - DACL_RETUNE_C18_0 */ + { 19492, 0x0000 }, /* R19492 - DACL_RETUNE_C19_1 */ + { 19493, 0x0000 }, /* R19493 - DACL_RETUNE_C19_0 */ + { 19494, 0x0000 }, /* R19494 - DACL_RETUNE_C20_1 */ + { 19495, 0x0000 }, /* R19495 - DACL_RETUNE_C20_0 */ + { 19496, 0x0000 }, /* R19496 - DACL_RETUNE_C21_1 */ + { 19497, 0x0000 }, /* R19497 - DACL_RETUNE_C21_0 */ + { 19498, 0x0000 }, /* R19498 - DACL_RETUNE_C22_1 */ + { 19499, 0x0000 }, /* R19499 - DACL_RETUNE_C22_0 */ + { 19500, 0x0000 }, /* R19500 - DACL_RETUNE_C23_1 */ + { 19501, 0x0000 }, /* R19501 - DACL_RETUNE_C23_0 */ + { 19502, 0x0000 }, /* R19502 - DACL_RETUNE_C24_1 */ + { 19503, 0x0000 }, /* R19503 - DACL_RETUNE_C24_0 */ + { 19504, 0x0000 }, /* R19504 - DACL_RETUNE_C25_1 */ + { 19505, 0x0000 }, /* R19505 - DACL_RETUNE_C25_0 */ + { 19506, 0x0000 }, /* R19506 - DACL_RETUNE_C26_1 */ + { 19507, 0x0000 }, /* R19507 - DACL_RETUNE_C26_0 */ + { 19508, 0x0000 }, /* R19508 - DACL_RETUNE_C27_1 */ + { 19509, 0x0000 }, /* R19509 - DACL_RETUNE_C27_0 */ + { 19510, 0x0000 }, /* R19510 - DACL_RETUNE_C28_1 */ + { 19511, 0x0000 }, /* R19511 - DACL_RETUNE_C28_0 */ + { 19512, 0x0000 }, /* R19512 - DACL_RETUNE_C29_1 */ + { 19513, 0x0000 }, /* R19513 - DACL_RETUNE_C29_0 */ + { 19514, 0x0000 }, /* R19514 - DACL_RETUNE_C30_1 */ + { 19515, 0x0000 }, /* R19515 - DACL_RETUNE_C30_0 */ + { 19516, 0x0000 }, /* R19516 - DACL_RETUNE_C31_1 */ + { 19517, 0x0000 }, /* R19517 - DACL_RETUNE_C31_0 */ + { 19518, 0x0000 }, /* R19518 - DACL_RETUNE_C32_1 */ + { 19519, 0x0000 }, /* R19519 - DACL_RETUNE_C32_0 */ + + { 19968, 0x0020 }, /* R19968 - RETUNEDAC_PG2_1 */ + { 19969, 0x0000 }, /* R19969 - RETUNEDAC_PG2_0 */ + { 19970, 0x0040 }, /* R19970 - RETUNEDAC_PG_1 */ + { 19971, 0x0000 }, /* R19971 - RETUNEDAC_PG_0 */ + + { 20480, 0x007F }, /* R20480 - DACR_RETUNE_C1_1 */ + { 20481, 0xFFFF }, /* R20481 - DACR_RETUNE_C1_0 */ + { 20482, 0x0000 }, /* R20482 - DACR_RETUNE_C2_1 */ + { 20483, 0x0000 }, /* R20483 - DACR_RETUNE_C2_0 */ + { 20484, 0x0000 }, /* R20484 - DACR_RETUNE_C3_1 */ + { 20485, 0x0000 }, /* R20485 - DACR_RETUNE_C3_0 */ + { 20486, 0x0000 }, /* R20486 - DACR_RETUNE_C4_1 */ + { 20487, 0x0000 }, /* R20487 - DACR_RETUNE_C4_0 */ + { 20488, 0x0000 }, /* R20488 - DACR_RETUNE_C5_1 */ + { 20489, 0x0000 }, /* R20489 - DACR_RETUNE_C5_0 */ + { 20490, 0x0000 }, /* R20490 - DACR_RETUNE_C6_1 */ + { 20491, 0x0000 }, /* R20491 - DACR_RETUNE_C6_0 */ + { 20492, 0x0000 }, /* R20492 - DACR_RETUNE_C7_1 */ + { 20493, 0x0000 }, /* R20493 - DACR_RETUNE_C7_0 */ + { 20494, 0x0000 }, /* R20494 - DACR_RETUNE_C8_1 */ + { 20495, 0x0000 }, /* R20495 - DACR_RETUNE_C8_0 */ + { 20496, 0x0000 }, /* R20496 - DACR_RETUNE_C9_1 */ + { 20497, 0x0000 }, /* R20497 - DACR_RETUNE_C9_0 */ + { 20498, 0x0000 }, /* R20498 - DACR_RETUNE_C10_1 */ + { 20499, 0x0000 }, /* R20499 - DACR_RETUNE_C10_0 */ + { 20500, 0x0000 }, /* R20500 - DACR_RETUNE_C11_1 */ + { 20501, 0x0000 }, /* R20501 - DACR_RETUNE_C11_0 */ + { 20502, 0x0000 }, /* R20502 - DACR_RETUNE_C12_1 */ + { 20503, 0x0000 }, /* R20503 - DACR_RETUNE_C12_0 */ + { 20504, 0x0000 }, /* R20504 - DACR_RETUNE_C13_1 */ + { 20505, 0x0000 }, /* R20505 - DACR_RETUNE_C13_0 */ + { 20506, 0x0000 }, /* R20506 - DACR_RETUNE_C14_1 */ + { 20507, 0x0000 }, /* R20507 - DACR_RETUNE_C14_0 */ + { 20508, 0x0000 }, /* R20508 - DACR_RETUNE_C15_1 */ + { 20509, 0x0000 }, /* R20509 - DACR_RETUNE_C15_0 */ + { 20510, 0x0000 }, /* R20510 - DACR_RETUNE_C16_1 */ + { 20511, 0x0000 }, /* R20511 - DACR_RETUNE_C16_0 */ + { 20512, 0x0000 }, /* R20512 - DACR_RETUNE_C17_1 */ + { 20513, 0x0000 }, /* R20513 - DACR_RETUNE_C17_0 */ + { 20514, 0x0000 }, /* R20514 - DACR_RETUNE_C18_1 */ + { 20515, 0x0000 }, /* R20515 - DACR_RETUNE_C18_0 */ + { 20516, 0x0000 }, /* R20516 - DACR_RETUNE_C19_1 */ + { 20517, 0x0000 }, /* R20517 - DACR_RETUNE_C19_0 */ + { 20518, 0x0000 }, /* R20518 - DACR_RETUNE_C20_1 */ + { 20519, 0x0000 }, /* R20519 - DACR_RETUNE_C20_0 */ + { 20520, 0x0000 }, /* R20520 - DACR_RETUNE_C21_1 */ + { 20521, 0x0000 }, /* R20521 - DACR_RETUNE_C21_0 */ + { 20522, 0x0000 }, /* R20522 - DACR_RETUNE_C22_1 */ + { 20523, 0x0000 }, /* R20523 - DACR_RETUNE_C22_0 */ + { 20524, 0x0000 }, /* R20524 - DACR_RETUNE_C23_1 */ + { 20525, 0x0000 }, /* R20525 - DACR_RETUNE_C23_0 */ + { 20526, 0x0000 }, /* R20526 - DACR_RETUNE_C24_1 */ + { 20527, 0x0000 }, /* R20527 - DACR_RETUNE_C24_0 */ + { 20528, 0x0000 }, /* R20528 - DACR_RETUNE_C25_1 */ + { 20529, 0x0000 }, /* R20529 - DACR_RETUNE_C25_0 */ + { 20530, 0x0000 }, /* R20530 - DACR_RETUNE_C26_1 */ + { 20531, 0x0000 }, /* R20531 - DACR_RETUNE_C26_0 */ + { 20532, 0x0000 }, /* R20532 - DACR_RETUNE_C27_1 */ + { 20533, 0x0000 }, /* R20533 - DACR_RETUNE_C27_0 */ + { 20534, 0x0000 }, /* R20534 - DACR_RETUNE_C28_1 */ + { 20535, 0x0000 }, /* R20535 - DACR_RETUNE_C28_0 */ + { 20536, 0x0000 }, /* R20536 - DACR_RETUNE_C29_1 */ + { 20537, 0x0000 }, /* R20537 - DACR_RETUNE_C29_0 */ + { 20538, 0x0000 }, /* R20538 - DACR_RETUNE_C30_1 */ + { 20539, 0x0000 }, /* R20539 - DACR_RETUNE_C30_0 */ + { 20540, 0x0000 }, /* R20540 - DACR_RETUNE_C31_1 */ + { 20541, 0x0000 }, /* R20541 - DACR_RETUNE_C31_0 */ + { 20542, 0x0000 }, /* R20542 - DACR_RETUNE_C32_1 */ + { 20543, 0x0000 }, /* R20543 - DACR_RETUNE_C32_0 */ + + { 20992, 0x008C }, /* R20992 - VSS_XHD2_1 */ + { 20993, 0x0200 }, /* R20993 - VSS_XHD2_0 */ + { 20994, 0x0035 }, /* R20994 - VSS_XHD3_1 */ + { 20995, 0x0700 }, /* R20995 - VSS_XHD3_0 */ + { 20996, 0x003A }, /* R20996 - VSS_XHN1_1 */ + { 20997, 0x4100 }, /* R20997 - VSS_XHN1_0 */ + { 20998, 0x008B }, /* R20998 - VSS_XHN2_1 */ + { 20999, 0x7D00 }, /* R20999 - VSS_XHN2_0 */ + { 21000, 0x003A }, /* R21000 - VSS_XHN3_1 */ + { 21001, 0x4100 }, /* R21001 - VSS_XHN3_0 */ + { 21002, 0x008C }, /* R21002 - VSS_XLA_1 */ + { 21003, 0xFEE8 }, /* R21003 - VSS_XLA_0 */ + { 21004, 0x0078 }, /* R21004 - VSS_XLB_1 */ + { 21005, 0x0000 }, /* R21005 - VSS_XLB_0 */ + { 21006, 0x003F }, /* R21006 - VSS_XLG_1 */ + { 21007, 0xB260 }, /* R21007 - VSS_XLG_0 */ + { 21008, 0x002D }, /* R21008 - VSS_PG2_1 */ + { 21009, 0x1818 }, /* R21009 - VSS_PG2_0 */ + { 21010, 0x0020 }, /* R21010 - VSS_PG_1 */ + { 21011, 0x0000 }, /* R21011 - VSS_PG_0 */ + { 21012, 0x00F1 }, /* R21012 - VSS_XTD1_1 */ + { 21013, 0x8340 }, /* R21013 - VSS_XTD1_0 */ + { 21014, 0x00FB }, /* R21014 - VSS_XTD2_1 */ + { 21015, 0x8300 }, /* R21015 - VSS_XTD2_0 */ + { 21016, 0x00EE }, /* R21016 - VSS_XTD3_1 */ + { 21017, 0xAEC0 }, /* R21017 - VSS_XTD3_0 */ + { 21018, 0x00FB }, /* R21018 - VSS_XTD4_1 */ + { 21019, 0xAC40 }, /* R21019 - VSS_XTD4_0 */ + { 21020, 0x00F1 }, /* R21020 - VSS_XTD5_1 */ + { 21021, 0x7F80 }, /* R21021 - VSS_XTD5_0 */ + { 21022, 0x00F4 }, /* R21022 - VSS_XTD6_1 */ + { 21023, 0x3B40 }, /* R21023 - VSS_XTD6_0 */ + { 21024, 0x00F5 }, /* R21024 - VSS_XTD7_1 */ + { 21025, 0xFB00 }, /* R21025 - VSS_XTD7_0 */ + { 21026, 0x00EA }, /* R21026 - VSS_XTD8_1 */ + { 21027, 0x10C0 }, /* R21027 - VSS_XTD8_0 */ + { 21028, 0x00FC }, /* R21028 - VSS_XTD9_1 */ + { 21029, 0xC580 }, /* R21029 - VSS_XTD9_0 */ + { 21030, 0x00E2 }, /* R21030 - VSS_XTD10_1 */ + { 21031, 0x75C0 }, /* R21031 - VSS_XTD10_0 */ + { 21032, 0x0004 }, /* R21032 - VSS_XTD11_1 */ + { 21033, 0xB480 }, /* R21033 - VSS_XTD11_0 */ + { 21034, 0x00D4 }, /* R21034 - VSS_XTD12_1 */ + { 21035, 0xF980 }, /* R21035 - VSS_XTD12_0 */ + { 21036, 0x0004 }, /* R21036 - VSS_XTD13_1 */ + { 21037, 0x9140 }, /* R21037 - VSS_XTD13_0 */ + { 21038, 0x00D8 }, /* R21038 - VSS_XTD14_1 */ + { 21039, 0xA480 }, /* R21039 - VSS_XTD14_0 */ + { 21040, 0x0002 }, /* R21040 - VSS_XTD15_1 */ + { 21041, 0x3DC0 }, /* R21041 - VSS_XTD15_0 */ + { 21042, 0x00CF }, /* R21042 - VSS_XTD16_1 */ + { 21043, 0x7A80 }, /* R21043 - VSS_XTD16_0 */ + { 21044, 0x00DC }, /* R21044 - VSS_XTD17_1 */ + { 21045, 0x0600 }, /* R21045 - VSS_XTD17_0 */ + { 21046, 0x00F2 }, /* R21046 - VSS_XTD18_1 */ + { 21047, 0xDAC0 }, /* R21047 - VSS_XTD18_0 */ + { 21048, 0x00BA }, /* R21048 - VSS_XTD19_1 */ + { 21049, 0xF340 }, /* R21049 - VSS_XTD19_0 */ + { 21050, 0x000A }, /* R21050 - VSS_XTD20_1 */ + { 21051, 0x7940 }, /* R21051 - VSS_XTD20_0 */ + { 21052, 0x001C }, /* R21052 - VSS_XTD21_1 */ + { 21053, 0x0680 }, /* R21053 - VSS_XTD21_0 */ + { 21054, 0x00FD }, /* R21054 - VSS_XTD22_1 */ + { 21055, 0x2D00 }, /* R21055 - VSS_XTD22_0 */ + { 21056, 0x001C }, /* R21056 - VSS_XTD23_1 */ + { 21057, 0xE840 }, /* R21057 - VSS_XTD23_0 */ + { 21058, 0x000D }, /* R21058 - VSS_XTD24_1 */ + { 21059, 0xDC40 }, /* R21059 - VSS_XTD24_0 */ + { 21060, 0x00FC }, /* R21060 - VSS_XTD25_1 */ + { 21061, 0x9D00 }, /* R21061 - VSS_XTD25_0 */ + { 21062, 0x0009 }, /* R21062 - VSS_XTD26_1 */ + { 21063, 0x5580 }, /* R21063 - VSS_XTD26_0 */ + { 21064, 0x00FE }, /* R21064 - VSS_XTD27_1 */ + { 21065, 0x7E80 }, /* R21065 - VSS_XTD27_0 */ + { 21066, 0x000E }, /* R21066 - VSS_XTD28_1 */ + { 21067, 0xAB40 }, /* R21067 - VSS_XTD28_0 */ + { 21068, 0x00F9 }, /* R21068 - VSS_XTD29_1 */ + { 21069, 0x9880 }, /* R21069 - VSS_XTD29_0 */ + { 21070, 0x0009 }, /* R21070 - VSS_XTD30_1 */ + { 21071, 0x87C0 }, /* R21071 - VSS_XTD30_0 */ + { 21072, 0x00FD }, /* R21072 - VSS_XTD31_1 */ + { 21073, 0x2C40 }, /* R21073 - VSS_XTD31_0 */ + { 21074, 0x0009 }, /* R21074 - VSS_XTD32_1 */ + { 21075, 0x4800 }, /* R21075 - VSS_XTD32_0 */ + { 21076, 0x0003 }, /* R21076 - VSS_XTS1_1 */ + { 21077, 0x5F40 }, /* R21077 - VSS_XTS1_0 */ + { 21078, 0x0000 }, /* R21078 - VSS_XTS2_1 */ + { 21079, 0x8700 }, /* R21079 - VSS_XTS2_0 */ + { 21080, 0x00FA }, /* R21080 - VSS_XTS3_1 */ + { 21081, 0xE4C0 }, /* R21081 - VSS_XTS3_0 */ + { 21082, 0x0000 }, /* R21082 - VSS_XTS4_1 */ + { 21083, 0x0B40 }, /* R21083 - VSS_XTS4_0 */ + { 21084, 0x0004 }, /* R21084 - VSS_XTS5_1 */ + { 21085, 0xE180 }, /* R21085 - VSS_XTS5_0 */ + { 21086, 0x0001 }, /* R21086 - VSS_XTS6_1 */ + { 21087, 0x1F40 }, /* R21087 - VSS_XTS6_0 */ + { 21088, 0x00F8 }, /* R21088 - VSS_XTS7_1 */ + { 21089, 0xB000 }, /* R21089 - VSS_XTS7_0 */ + { 21090, 0x00FB }, /* R21090 - VSS_XTS8_1 */ + { 21091, 0xCBC0 }, /* R21091 - VSS_XTS8_0 */ + { 21092, 0x0004 }, /* R21092 - VSS_XTS9_1 */ + { 21093, 0xF380 }, /* R21093 - VSS_XTS9_0 */ + { 21094, 0x0007 }, /* R21094 - VSS_XTS10_1 */ + { 21095, 0xDF40 }, /* R21095 - VSS_XTS10_0 */ + { 21096, 0x00FF }, /* R21096 - VSS_XTS11_1 */ + { 21097, 0x0700 }, /* R21097 - VSS_XTS11_0 */ + { 21098, 0x00EF }, /* R21098 - VSS_XTS12_1 */ + { 21099, 0xD700 }, /* R21099 - VSS_XTS12_0 */ + { 21100, 0x00FB }, /* R21100 - VSS_XTS13_1 */ + { 21101, 0xAF40 }, /* R21101 - VSS_XTS13_0 */ + { 21102, 0x0010 }, /* R21102 - VSS_XTS14_1 */ + { 21103, 0x8A80 }, /* R21103 - VSS_XTS14_0 */ + { 21104, 0x0011 }, /* R21104 - VSS_XTS15_1 */ + { 21105, 0x07C0 }, /* R21105 - VSS_XTS15_0 */ + { 21106, 0x00E0 }, /* R21106 - VSS_XTS16_1 */ + { 21107, 0x0800 }, /* R21107 - VSS_XTS16_0 */ + { 21108, 0x00D2 }, /* R21108 - VSS_XTS17_1 */ + { 21109, 0x7600 }, /* R21109 - VSS_XTS17_0 */ + { 21110, 0x0020 }, /* R21110 - VSS_XTS18_1 */ + { 21111, 0xCF40 }, /* R21111 - VSS_XTS18_0 */ + { 21112, 0x0030 }, /* R21112 - VSS_XTS19_1 */ + { 21113, 0x2340 }, /* R21113 - VSS_XTS19_0 */ + { 21114, 0x00FD }, /* R21114 - VSS_XTS20_1 */ + { 21115, 0x69C0 }, /* R21115 - VSS_XTS20_0 */ + { 21116, 0x0028 }, /* R21116 - VSS_XTS21_1 */ + { 21117, 0x3500 }, /* R21117 - VSS_XTS21_0 */ + { 21118, 0x0006 }, /* R21118 - VSS_XTS22_1 */ + { 21119, 0x3300 }, /* R21119 - VSS_XTS22_0 */ + { 21120, 0x00D9 }, /* R21120 - VSS_XTS23_1 */ + { 21121, 0xF6C0 }, /* R21121 - VSS_XTS23_0 */ + { 21122, 0x00F3 }, /* R21122 - VSS_XTS24_1 */ + { 21123, 0x3340 }, /* R21123 - VSS_XTS24_0 */ + { 21124, 0x000F }, /* R21124 - VSS_XTS25_1 */ + { 21125, 0x4200 }, /* R21125 - VSS_XTS25_0 */ + { 21126, 0x0004 }, /* R21126 - VSS_XTS26_1 */ + { 21127, 0x0C80 }, /* R21127 - VSS_XTS26_0 */ + { 21128, 0x00FB }, /* R21128 - VSS_XTS27_1 */ + { 21129, 0x3F80 }, /* R21129 - VSS_XTS27_0 */ + { 21130, 0x00F7 }, /* R21130 - VSS_XTS28_1 */ + { 21131, 0x57C0 }, /* R21131 - VSS_XTS28_0 */ + { 21132, 0x0003 }, /* R21132 - VSS_XTS29_1 */ + { 21133, 0x5400 }, /* R21133 - VSS_XTS29_0 */ + { 21134, 0x0000 }, /* R21134 - VSS_XTS30_1 */ + { 21135, 0xC6C0 }, /* R21135 - VSS_XTS30_0 */ + { 21136, 0x0003 }, /* R21136 - VSS_XTS31_1 */ + { 21137, 0x12C0 }, /* R21137 - VSS_XTS31_0 */ + { 21138, 0x00FD }, /* R21138 - VSS_XTS32_1 */ + { 21139, 0x8580 }, /* R21139 - VSS_XTS32_0 */ +}; + +static bool wm8962_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8962_CLOCKING1: + case WM8962_SOFTWARE_RESET: + case WM8962_THERMAL_SHUTDOWN_STATUS: + case WM8962_ADDITIONAL_CONTROL_4: + case WM8962_DC_SERVO_6: + case WM8962_INTERRUPT_STATUS_1: + case WM8962_INTERRUPT_STATUS_2: + case WM8962_DSP2_EXECCONTROL: + return true; + default: + return false; + } +} + +static bool wm8962_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8962_LEFT_INPUT_VOLUME: + case WM8962_RIGHT_INPUT_VOLUME: + case WM8962_HPOUTL_VOLUME: + case WM8962_HPOUTR_VOLUME: + case WM8962_CLOCKING1: + case WM8962_ADC_DAC_CONTROL_1: + case WM8962_ADC_DAC_CONTROL_2: + case WM8962_AUDIO_INTERFACE_0: + case WM8962_CLOCKING2: + case WM8962_AUDIO_INTERFACE_1: + case WM8962_LEFT_DAC_VOLUME: + case WM8962_RIGHT_DAC_VOLUME: + case WM8962_AUDIO_INTERFACE_2: + case WM8962_SOFTWARE_RESET: + case WM8962_ALC1: + case WM8962_ALC2: + case WM8962_ALC3: + case WM8962_NOISE_GATE: + case WM8962_LEFT_ADC_VOLUME: + case WM8962_RIGHT_ADC_VOLUME: + case WM8962_ADDITIONAL_CONTROL_1: + case WM8962_ADDITIONAL_CONTROL_2: + case WM8962_PWR_MGMT_1: + case WM8962_PWR_MGMT_2: + case WM8962_ADDITIONAL_CONTROL_3: + case WM8962_ANTI_POP: + case WM8962_CLOCKING_3: + case WM8962_INPUT_MIXER_CONTROL_1: + case WM8962_LEFT_INPUT_MIXER_VOLUME: + case WM8962_RIGHT_INPUT_MIXER_VOLUME: + case WM8962_INPUT_MIXER_CONTROL_2: + case WM8962_INPUT_BIAS_CONTROL: + case WM8962_LEFT_INPUT_PGA_CONTROL: + case WM8962_RIGHT_INPUT_PGA_CONTROL: + case WM8962_SPKOUTL_VOLUME: + case WM8962_SPKOUTR_VOLUME: + case WM8962_THERMAL_SHUTDOWN_STATUS: + case WM8962_ADDITIONAL_CONTROL_4: + case WM8962_CLASS_D_CONTROL_1: + case WM8962_CLASS_D_CONTROL_2: + case WM8962_CLOCKING_4: + case WM8962_DAC_DSP_MIXING_1: + case WM8962_DAC_DSP_MIXING_2: + case WM8962_DC_SERVO_0: + case WM8962_DC_SERVO_1: + case WM8962_DC_SERVO_4: + case WM8962_DC_SERVO_6: + case WM8962_ANALOGUE_PGA_BIAS: + case WM8962_ANALOGUE_HP_0: + case WM8962_ANALOGUE_HP_2: + case WM8962_CHARGE_PUMP_1: + case WM8962_CHARGE_PUMP_B: + case WM8962_WRITE_SEQUENCER_CONTROL_1: + case WM8962_WRITE_SEQUENCER_CONTROL_2: + case WM8962_WRITE_SEQUENCER_CONTROL_3: + case WM8962_CONTROL_INTERFACE: + case WM8962_MIXER_ENABLES: + case WM8962_HEADPHONE_MIXER_1: + case WM8962_HEADPHONE_MIXER_2: + case WM8962_HEADPHONE_MIXER_3: + case WM8962_HEADPHONE_MIXER_4: + case WM8962_SPEAKER_MIXER_1: + case WM8962_SPEAKER_MIXER_2: + case WM8962_SPEAKER_MIXER_3: + case WM8962_SPEAKER_MIXER_4: + case WM8962_SPEAKER_MIXER_5: + case WM8962_BEEP_GENERATOR_1: + case WM8962_OSCILLATOR_TRIM_3: + case WM8962_OSCILLATOR_TRIM_4: + case WM8962_OSCILLATOR_TRIM_7: + case WM8962_ANALOGUE_CLOCKING1: + case WM8962_ANALOGUE_CLOCKING2: + case WM8962_ANALOGUE_CLOCKING3: + case WM8962_PLL_SOFTWARE_RESET: + case WM8962_PLL2: + case WM8962_PLL_4: + case WM8962_PLL_9: + case WM8962_PLL_10: + case WM8962_PLL_11: + case WM8962_PLL_12: + case WM8962_PLL_13: + case WM8962_PLL_14: + case WM8962_PLL_15: + case WM8962_PLL_16: + case WM8962_FLL_CONTROL_1: + case WM8962_FLL_CONTROL_2: + case WM8962_FLL_CONTROL_3: + case WM8962_FLL_CONTROL_5: + case WM8962_FLL_CONTROL_6: + case WM8962_FLL_CONTROL_7: + case WM8962_FLL_CONTROL_8: + case WM8962_GENERAL_TEST_1: + case WM8962_DF1: + case WM8962_DF2: + case WM8962_DF3: + case WM8962_DF4: + case WM8962_DF5: + case WM8962_DF6: + case WM8962_DF7: + case WM8962_LHPF1: + case WM8962_LHPF2: + case WM8962_THREED1: + case WM8962_THREED2: + case WM8962_THREED3: + case WM8962_THREED4: + case WM8962_DRC_1: + case WM8962_DRC_2: + case WM8962_DRC_3: + case WM8962_DRC_4: + case WM8962_DRC_5: + case WM8962_TLOOPBACK: + case WM8962_EQ1: + case WM8962_EQ2: + case WM8962_EQ3: + case WM8962_EQ4: + case WM8962_EQ5: + case WM8962_EQ6: + case WM8962_EQ7: + case WM8962_EQ8: + case WM8962_EQ9: + case WM8962_EQ10: + case WM8962_EQ11: + case WM8962_EQ12: + case WM8962_EQ13: + case WM8962_EQ14: + case WM8962_EQ15: + case WM8962_EQ16: + case WM8962_EQ17: + case WM8962_EQ18: + case WM8962_EQ19: + case WM8962_EQ20: + case WM8962_EQ21: + case WM8962_EQ22: + case WM8962_EQ23: + case WM8962_EQ24: + case WM8962_EQ25: + case WM8962_EQ26: + case WM8962_EQ27: + case WM8962_EQ28: + case WM8962_EQ29: + case WM8962_EQ30: + case WM8962_EQ31: + case WM8962_EQ32: + case WM8962_EQ33: + case WM8962_EQ34: + case WM8962_EQ35: + case WM8962_EQ36: + case WM8962_EQ37: + case WM8962_EQ38: + case WM8962_EQ39: + case WM8962_EQ40: + case WM8962_EQ41: + case WM8962_GPIO_2: + case WM8962_GPIO_3: + case WM8962_GPIO_5: + case WM8962_GPIO_6: + case WM8962_INTERRUPT_STATUS_1: + case WM8962_INTERRUPT_STATUS_2: + case WM8962_INTERRUPT_STATUS_1_MASK: + case WM8962_INTERRUPT_STATUS_2_MASK: + case WM8962_INTERRUPT_CONTROL: + case WM8962_IRQ_DEBOUNCE: + case WM8962_MICINT_SOURCE_POL: + case WM8962_DSP2_POWER_MANAGEMENT: + case WM8962_DSP2_EXECCONTROL: + case WM8962_DSP2_INSTRUCTION_RAM_0: + case WM8962_DSP2_ADDRESS_RAM_2: + case WM8962_DSP2_ADDRESS_RAM_1: + case WM8962_DSP2_ADDRESS_RAM_0: + case WM8962_DSP2_DATA1_RAM_1: + case WM8962_DSP2_DATA1_RAM_0: + case WM8962_DSP2_DATA2_RAM_1: + case WM8962_DSP2_DATA2_RAM_0: + case WM8962_DSP2_DATA3_RAM_1: + case WM8962_DSP2_DATA3_RAM_0: + case WM8962_DSP2_COEFF_RAM_0: + case WM8962_RETUNEADC_SHARED_COEFF_1: + case WM8962_RETUNEADC_SHARED_COEFF_0: + case WM8962_RETUNEDAC_SHARED_COEFF_1: + case WM8962_RETUNEDAC_SHARED_COEFF_0: + case WM8962_SOUNDSTAGE_ENABLES_1: + case WM8962_SOUNDSTAGE_ENABLES_0: + case WM8962_HDBASS_AI_1: + case WM8962_HDBASS_AI_0: + case WM8962_HDBASS_AR_1: + case WM8962_HDBASS_AR_0: + case WM8962_HDBASS_B_1: + case WM8962_HDBASS_B_0: + case WM8962_HDBASS_K_1: + case WM8962_HDBASS_K_0: + case WM8962_HDBASS_N1_1: + case WM8962_HDBASS_N1_0: + case WM8962_HDBASS_N2_1: + case WM8962_HDBASS_N2_0: + case WM8962_HDBASS_N3_1: + case WM8962_HDBASS_N3_0: + case WM8962_HDBASS_N4_1: + case WM8962_HDBASS_N4_0: + case WM8962_HDBASS_N5_1: + case WM8962_HDBASS_N5_0: + case WM8962_HDBASS_X1_1: + case WM8962_HDBASS_X1_0: + case WM8962_HDBASS_X2_1: + case WM8962_HDBASS_X2_0: + case WM8962_HDBASS_X3_1: + case WM8962_HDBASS_X3_0: + case WM8962_HDBASS_ATK_1: + case WM8962_HDBASS_ATK_0: + case WM8962_HDBASS_DCY_1: + case WM8962_HDBASS_DCY_0: + case WM8962_HDBASS_PG_1: + case WM8962_HDBASS_PG_0: + case WM8962_HPF_C_1: + case WM8962_HPF_C_0: + case WM8962_ADCL_RETUNE_C1_1: + case WM8962_ADCL_RETUNE_C1_0: + case WM8962_ADCL_RETUNE_C2_1: + case WM8962_ADCL_RETUNE_C2_0: + case WM8962_ADCL_RETUNE_C3_1: + case WM8962_ADCL_RETUNE_C3_0: + case WM8962_ADCL_RETUNE_C4_1: + case WM8962_ADCL_RETUNE_C4_0: + case WM8962_ADCL_RETUNE_C5_1: + case WM8962_ADCL_RETUNE_C5_0: + case WM8962_ADCL_RETUNE_C6_1: + case WM8962_ADCL_RETUNE_C6_0: + case WM8962_ADCL_RETUNE_C7_1: + case WM8962_ADCL_RETUNE_C7_0: + case WM8962_ADCL_RETUNE_C8_1: + case WM8962_ADCL_RETUNE_C8_0: + case WM8962_ADCL_RETUNE_C9_1: + case WM8962_ADCL_RETUNE_C9_0: + case WM8962_ADCL_RETUNE_C10_1: + case WM8962_ADCL_RETUNE_C10_0: + case WM8962_ADCL_RETUNE_C11_1: + case WM8962_ADCL_RETUNE_C11_0: + case WM8962_ADCL_RETUNE_C12_1: + case WM8962_ADCL_RETUNE_C12_0: + case WM8962_ADCL_RETUNE_C13_1: + case WM8962_ADCL_RETUNE_C13_0: + case WM8962_ADCL_RETUNE_C14_1: + case WM8962_ADCL_RETUNE_C14_0: + case WM8962_ADCL_RETUNE_C15_1: + case WM8962_ADCL_RETUNE_C15_0: + case WM8962_ADCL_RETUNE_C16_1: + case WM8962_ADCL_RETUNE_C16_0: + case WM8962_ADCL_RETUNE_C17_1: + case WM8962_ADCL_RETUNE_C17_0: + case WM8962_ADCL_RETUNE_C18_1: + case WM8962_ADCL_RETUNE_C18_0: + case WM8962_ADCL_RETUNE_C19_1: + case WM8962_ADCL_RETUNE_C19_0: + case WM8962_ADCL_RETUNE_C20_1: + case WM8962_ADCL_RETUNE_C20_0: + case WM8962_ADCL_RETUNE_C21_1: + case WM8962_ADCL_RETUNE_C21_0: + case WM8962_ADCL_RETUNE_C22_1: + case WM8962_ADCL_RETUNE_C22_0: + case WM8962_ADCL_RETUNE_C23_1: + case WM8962_ADCL_RETUNE_C23_0: + case WM8962_ADCL_RETUNE_C24_1: + case WM8962_ADCL_RETUNE_C24_0: + case WM8962_ADCL_RETUNE_C25_1: + case WM8962_ADCL_RETUNE_C25_0: + case WM8962_ADCL_RETUNE_C26_1: + case WM8962_ADCL_RETUNE_C26_0: + case WM8962_ADCL_RETUNE_C27_1: + case WM8962_ADCL_RETUNE_C27_0: + case WM8962_ADCL_RETUNE_C28_1: + case WM8962_ADCL_RETUNE_C28_0: + case WM8962_ADCL_RETUNE_C29_1: + case WM8962_ADCL_RETUNE_C29_0: + case WM8962_ADCL_RETUNE_C30_1: + case WM8962_ADCL_RETUNE_C30_0: + case WM8962_ADCL_RETUNE_C31_1: + case WM8962_ADCL_RETUNE_C31_0: + case WM8962_ADCL_RETUNE_C32_1: + case WM8962_ADCL_RETUNE_C32_0: + case WM8962_RETUNEADC_PG2_1: + case WM8962_RETUNEADC_PG2_0: + case WM8962_RETUNEADC_PG_1: + case WM8962_RETUNEADC_PG_0: + case WM8962_ADCR_RETUNE_C1_1: + case WM8962_ADCR_RETUNE_C1_0: + case WM8962_ADCR_RETUNE_C2_1: + case WM8962_ADCR_RETUNE_C2_0: + case WM8962_ADCR_RETUNE_C3_1: + case WM8962_ADCR_RETUNE_C3_0: + case WM8962_ADCR_RETUNE_C4_1: + case WM8962_ADCR_RETUNE_C4_0: + case WM8962_ADCR_RETUNE_C5_1: + case WM8962_ADCR_RETUNE_C5_0: + case WM8962_ADCR_RETUNE_C6_1: + case WM8962_ADCR_RETUNE_C6_0: + case WM8962_ADCR_RETUNE_C7_1: + case WM8962_ADCR_RETUNE_C7_0: + case WM8962_ADCR_RETUNE_C8_1: + case WM8962_ADCR_RETUNE_C8_0: + case WM8962_ADCR_RETUNE_C9_1: + case WM8962_ADCR_RETUNE_C9_0: + case WM8962_ADCR_RETUNE_C10_1: + case WM8962_ADCR_RETUNE_C10_0: + case WM8962_ADCR_RETUNE_C11_1: + case WM8962_ADCR_RETUNE_C11_0: + case WM8962_ADCR_RETUNE_C12_1: + case WM8962_ADCR_RETUNE_C12_0: + case WM8962_ADCR_RETUNE_C13_1: + case WM8962_ADCR_RETUNE_C13_0: + case WM8962_ADCR_RETUNE_C14_1: + case WM8962_ADCR_RETUNE_C14_0: + case WM8962_ADCR_RETUNE_C15_1: + case WM8962_ADCR_RETUNE_C15_0: + case WM8962_ADCR_RETUNE_C16_1: + case WM8962_ADCR_RETUNE_C16_0: + case WM8962_ADCR_RETUNE_C17_1: + case WM8962_ADCR_RETUNE_C17_0: + case WM8962_ADCR_RETUNE_C18_1: + case WM8962_ADCR_RETUNE_C18_0: + case WM8962_ADCR_RETUNE_C19_1: + case WM8962_ADCR_RETUNE_C19_0: + case WM8962_ADCR_RETUNE_C20_1: + case WM8962_ADCR_RETUNE_C20_0: + case WM8962_ADCR_RETUNE_C21_1: + case WM8962_ADCR_RETUNE_C21_0: + case WM8962_ADCR_RETUNE_C22_1: + case WM8962_ADCR_RETUNE_C22_0: + case WM8962_ADCR_RETUNE_C23_1: + case WM8962_ADCR_RETUNE_C23_0: + case WM8962_ADCR_RETUNE_C24_1: + case WM8962_ADCR_RETUNE_C24_0: + case WM8962_ADCR_RETUNE_C25_1: + case WM8962_ADCR_RETUNE_C25_0: + case WM8962_ADCR_RETUNE_C26_1: + case WM8962_ADCR_RETUNE_C26_0: + case WM8962_ADCR_RETUNE_C27_1: + case WM8962_ADCR_RETUNE_C27_0: + case WM8962_ADCR_RETUNE_C28_1: + case WM8962_ADCR_RETUNE_C28_0: + case WM8962_ADCR_RETUNE_C29_1: + case WM8962_ADCR_RETUNE_C29_0: + case WM8962_ADCR_RETUNE_C30_1: + case WM8962_ADCR_RETUNE_C30_0: + case WM8962_ADCR_RETUNE_C31_1: + case WM8962_ADCR_RETUNE_C31_0: + case WM8962_ADCR_RETUNE_C32_1: + case WM8962_ADCR_RETUNE_C32_0: + case WM8962_DACL_RETUNE_C1_1: + case WM8962_DACL_RETUNE_C1_0: + case WM8962_DACL_RETUNE_C2_1: + case WM8962_DACL_RETUNE_C2_0: + case WM8962_DACL_RETUNE_C3_1: + case WM8962_DACL_RETUNE_C3_0: + case WM8962_DACL_RETUNE_C4_1: + case WM8962_DACL_RETUNE_C4_0: + case WM8962_DACL_RETUNE_C5_1: + case WM8962_DACL_RETUNE_C5_0: + case WM8962_DACL_RETUNE_C6_1: + case WM8962_DACL_RETUNE_C6_0: + case WM8962_DACL_RETUNE_C7_1: + case WM8962_DACL_RETUNE_C7_0: + case WM8962_DACL_RETUNE_C8_1: + case WM8962_DACL_RETUNE_C8_0: + case WM8962_DACL_RETUNE_C9_1: + case WM8962_DACL_RETUNE_C9_0: + case WM8962_DACL_RETUNE_C10_1: + case WM8962_DACL_RETUNE_C10_0: + case WM8962_DACL_RETUNE_C11_1: + case WM8962_DACL_RETUNE_C11_0: + case WM8962_DACL_RETUNE_C12_1: + case WM8962_DACL_RETUNE_C12_0: + case WM8962_DACL_RETUNE_C13_1: + case WM8962_DACL_RETUNE_C13_0: + case WM8962_DACL_RETUNE_C14_1: + case WM8962_DACL_RETUNE_C14_0: + case WM8962_DACL_RETUNE_C15_1: + case WM8962_DACL_RETUNE_C15_0: + case WM8962_DACL_RETUNE_C16_1: + case WM8962_DACL_RETUNE_C16_0: + case WM8962_DACL_RETUNE_C17_1: + case WM8962_DACL_RETUNE_C17_0: + case WM8962_DACL_RETUNE_C18_1: + case WM8962_DACL_RETUNE_C18_0: + case WM8962_DACL_RETUNE_C19_1: + case WM8962_DACL_RETUNE_C19_0: + case WM8962_DACL_RETUNE_C20_1: + case WM8962_DACL_RETUNE_C20_0: + case WM8962_DACL_RETUNE_C21_1: + case WM8962_DACL_RETUNE_C21_0: + case WM8962_DACL_RETUNE_C22_1: + case WM8962_DACL_RETUNE_C22_0: + case WM8962_DACL_RETUNE_C23_1: + case WM8962_DACL_RETUNE_C23_0: + case WM8962_DACL_RETUNE_C24_1: + case WM8962_DACL_RETUNE_C24_0: + case WM8962_DACL_RETUNE_C25_1: + case WM8962_DACL_RETUNE_C25_0: + case WM8962_DACL_RETUNE_C26_1: + case WM8962_DACL_RETUNE_C26_0: + case WM8962_DACL_RETUNE_C27_1: + case WM8962_DACL_RETUNE_C27_0: + case WM8962_DACL_RETUNE_C28_1: + case WM8962_DACL_RETUNE_C28_0: + case WM8962_DACL_RETUNE_C29_1: + case WM8962_DACL_RETUNE_C29_0: + case WM8962_DACL_RETUNE_C30_1: + case WM8962_DACL_RETUNE_C30_0: + case WM8962_DACL_RETUNE_C31_1: + case WM8962_DACL_RETUNE_C31_0: + case WM8962_DACL_RETUNE_C32_1: + case WM8962_DACL_RETUNE_C32_0: + case WM8962_RETUNEDAC_PG2_1: + case WM8962_RETUNEDAC_PG2_0: + case WM8962_RETUNEDAC_PG_1: + case WM8962_RETUNEDAC_PG_0: + case WM8962_DACR_RETUNE_C1_1: + case WM8962_DACR_RETUNE_C1_0: + case WM8962_DACR_RETUNE_C2_1: + case WM8962_DACR_RETUNE_C2_0: + case WM8962_DACR_RETUNE_C3_1: + case WM8962_DACR_RETUNE_C3_0: + case WM8962_DACR_RETUNE_C4_1: + case WM8962_DACR_RETUNE_C4_0: + case WM8962_DACR_RETUNE_C5_1: + case WM8962_DACR_RETUNE_C5_0: + case WM8962_DACR_RETUNE_C6_1: + case WM8962_DACR_RETUNE_C6_0: + case WM8962_DACR_RETUNE_C7_1: + case WM8962_DACR_RETUNE_C7_0: + case WM8962_DACR_RETUNE_C8_1: + case WM8962_DACR_RETUNE_C8_0: + case WM8962_DACR_RETUNE_C9_1: + case WM8962_DACR_RETUNE_C9_0: + case WM8962_DACR_RETUNE_C10_1: + case WM8962_DACR_RETUNE_C10_0: + case WM8962_DACR_RETUNE_C11_1: + case WM8962_DACR_RETUNE_C11_0: + case WM8962_DACR_RETUNE_C12_1: + case WM8962_DACR_RETUNE_C12_0: + case WM8962_DACR_RETUNE_C13_1: + case WM8962_DACR_RETUNE_C13_0: + case WM8962_DACR_RETUNE_C14_1: + case WM8962_DACR_RETUNE_C14_0: + case WM8962_DACR_RETUNE_C15_1: + case WM8962_DACR_RETUNE_C15_0: + case WM8962_DACR_RETUNE_C16_1: + case WM8962_DACR_RETUNE_C16_0: + case WM8962_DACR_RETUNE_C17_1: + case WM8962_DACR_RETUNE_C17_0: + case WM8962_DACR_RETUNE_C18_1: + case WM8962_DACR_RETUNE_C18_0: + case WM8962_DACR_RETUNE_C19_1: + case WM8962_DACR_RETUNE_C19_0: + case WM8962_DACR_RETUNE_C20_1: + case WM8962_DACR_RETUNE_C20_0: + case WM8962_DACR_RETUNE_C21_1: + case WM8962_DACR_RETUNE_C21_0: + case WM8962_DACR_RETUNE_C22_1: + case WM8962_DACR_RETUNE_C22_0: + case WM8962_DACR_RETUNE_C23_1: + case WM8962_DACR_RETUNE_C23_0: + case WM8962_DACR_RETUNE_C24_1: + case WM8962_DACR_RETUNE_C24_0: + case WM8962_DACR_RETUNE_C25_1: + case WM8962_DACR_RETUNE_C25_0: + case WM8962_DACR_RETUNE_C26_1: + case WM8962_DACR_RETUNE_C26_0: + case WM8962_DACR_RETUNE_C27_1: + case WM8962_DACR_RETUNE_C27_0: + case WM8962_DACR_RETUNE_C28_1: + case WM8962_DACR_RETUNE_C28_0: + case WM8962_DACR_RETUNE_C29_1: + case WM8962_DACR_RETUNE_C29_0: + case WM8962_DACR_RETUNE_C30_1: + case WM8962_DACR_RETUNE_C30_0: + case WM8962_DACR_RETUNE_C31_1: + case WM8962_DACR_RETUNE_C31_0: + case WM8962_DACR_RETUNE_C32_1: + case WM8962_DACR_RETUNE_C32_0: + case WM8962_VSS_XHD2_1: + case WM8962_VSS_XHD2_0: + case WM8962_VSS_XHD3_1: + case WM8962_VSS_XHD3_0: + case WM8962_VSS_XHN1_1: + case WM8962_VSS_XHN1_0: + case WM8962_VSS_XHN2_1: + case WM8962_VSS_XHN2_0: + case WM8962_VSS_XHN3_1: + case WM8962_VSS_XHN3_0: + case WM8962_VSS_XLA_1: + case WM8962_VSS_XLA_0: + case WM8962_VSS_XLB_1: + case WM8962_VSS_XLB_0: + case WM8962_VSS_XLG_1: + case WM8962_VSS_XLG_0: + case WM8962_VSS_PG2_1: + case WM8962_VSS_PG2_0: + case WM8962_VSS_PG_1: + case WM8962_VSS_PG_0: + case WM8962_VSS_XTD1_1: + case WM8962_VSS_XTD1_0: + case WM8962_VSS_XTD2_1: + case WM8962_VSS_XTD2_0: + case WM8962_VSS_XTD3_1: + case WM8962_VSS_XTD3_0: + case WM8962_VSS_XTD4_1: + case WM8962_VSS_XTD4_0: + case WM8962_VSS_XTD5_1: + case WM8962_VSS_XTD5_0: + case WM8962_VSS_XTD6_1: + case WM8962_VSS_XTD6_0: + case WM8962_VSS_XTD7_1: + case WM8962_VSS_XTD7_0: + case WM8962_VSS_XTD8_1: + case WM8962_VSS_XTD8_0: + case WM8962_VSS_XTD9_1: + case WM8962_VSS_XTD9_0: + case WM8962_VSS_XTD10_1: + case WM8962_VSS_XTD10_0: + case WM8962_VSS_XTD11_1: + case WM8962_VSS_XTD11_0: + case WM8962_VSS_XTD12_1: + case WM8962_VSS_XTD12_0: + case WM8962_VSS_XTD13_1: + case WM8962_VSS_XTD13_0: + case WM8962_VSS_XTD14_1: + case WM8962_VSS_XTD14_0: + case WM8962_VSS_XTD15_1: + case WM8962_VSS_XTD15_0: + case WM8962_VSS_XTD16_1: + case WM8962_VSS_XTD16_0: + case WM8962_VSS_XTD17_1: + case WM8962_VSS_XTD17_0: + case WM8962_VSS_XTD18_1: + case WM8962_VSS_XTD18_0: + case WM8962_VSS_XTD19_1: + case WM8962_VSS_XTD19_0: + case WM8962_VSS_XTD20_1: + case WM8962_VSS_XTD20_0: + case WM8962_VSS_XTD21_1: + case WM8962_VSS_XTD21_0: + case WM8962_VSS_XTD22_1: + case WM8962_VSS_XTD22_0: + case WM8962_VSS_XTD23_1: + case WM8962_VSS_XTD23_0: + case WM8962_VSS_XTD24_1: + case WM8962_VSS_XTD24_0: + case WM8962_VSS_XTD25_1: + case WM8962_VSS_XTD25_0: + case WM8962_VSS_XTD26_1: + case WM8962_VSS_XTD26_0: + case WM8962_VSS_XTD27_1: + case WM8962_VSS_XTD27_0: + case WM8962_VSS_XTD28_1: + case WM8962_VSS_XTD28_0: + case WM8962_VSS_XTD29_1: + case WM8962_VSS_XTD29_0: + case WM8962_VSS_XTD30_1: + case WM8962_VSS_XTD30_0: + case WM8962_VSS_XTD31_1: + case WM8962_VSS_XTD31_0: + case WM8962_VSS_XTD32_1: + case WM8962_VSS_XTD32_0: + case WM8962_VSS_XTS1_1: + case WM8962_VSS_XTS1_0: + case WM8962_VSS_XTS2_1: + case WM8962_VSS_XTS2_0: + case WM8962_VSS_XTS3_1: + case WM8962_VSS_XTS3_0: + case WM8962_VSS_XTS4_1: + case WM8962_VSS_XTS4_0: + case WM8962_VSS_XTS5_1: + case WM8962_VSS_XTS5_0: + case WM8962_VSS_XTS6_1: + case WM8962_VSS_XTS6_0: + case WM8962_VSS_XTS7_1: + case WM8962_VSS_XTS7_0: + case WM8962_VSS_XTS8_1: + case WM8962_VSS_XTS8_0: + case WM8962_VSS_XTS9_1: + case WM8962_VSS_XTS9_0: + case WM8962_VSS_XTS10_1: + case WM8962_VSS_XTS10_0: + case WM8962_VSS_XTS11_1: + case WM8962_VSS_XTS11_0: + case WM8962_VSS_XTS12_1: + case WM8962_VSS_XTS12_0: + case WM8962_VSS_XTS13_1: + case WM8962_VSS_XTS13_0: + case WM8962_VSS_XTS14_1: + case WM8962_VSS_XTS14_0: + case WM8962_VSS_XTS15_1: + case WM8962_VSS_XTS15_0: + case WM8962_VSS_XTS16_1: + case WM8962_VSS_XTS16_0: + case WM8962_VSS_XTS17_1: + case WM8962_VSS_XTS17_0: + case WM8962_VSS_XTS18_1: + case WM8962_VSS_XTS18_0: + case WM8962_VSS_XTS19_1: + case WM8962_VSS_XTS19_0: + case WM8962_VSS_XTS20_1: + case WM8962_VSS_XTS20_0: + case WM8962_VSS_XTS21_1: + case WM8962_VSS_XTS21_0: + case WM8962_VSS_XTS22_1: + case WM8962_VSS_XTS22_0: + case WM8962_VSS_XTS23_1: + case WM8962_VSS_XTS23_0: + case WM8962_VSS_XTS24_1: + case WM8962_VSS_XTS24_0: + case WM8962_VSS_XTS25_1: + case WM8962_VSS_XTS25_0: + case WM8962_VSS_XTS26_1: + case WM8962_VSS_XTS26_0: + case WM8962_VSS_XTS27_1: + case WM8962_VSS_XTS27_0: + case WM8962_VSS_XTS28_1: + case WM8962_VSS_XTS28_0: + case WM8962_VSS_XTS29_1: + case WM8962_VSS_XTS29_0: + case WM8962_VSS_XTS30_1: + case WM8962_VSS_XTS30_0: + case WM8962_VSS_XTS31_1: + case WM8962_VSS_XTS31_0: + case WM8962_VSS_XTS32_1: + case WM8962_VSS_XTS32_0: + return true; + default: + return false; + } +} + +static int wm8962_reset(struct wm8962_priv *wm8962) +{ + int ret; + + ret = regmap_write(wm8962->regmap, WM8962_SOFTWARE_RESET, 0x6243); + if (ret != 0) + return ret; + + return regmap_write(wm8962->regmap, WM8962_PLL_SOFTWARE_RESET, 0); +} + +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -2325, 75, 0); +static const DECLARE_TLV_DB_SCALE(mixin_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_RANGE(mixinpga_tlv, + 0, 1, TLV_DB_SCALE_ITEM(0, 600, 0), + 2, 2, TLV_DB_SCALE_ITEM(1300, 1300, 0), + 3, 4, TLV_DB_SCALE_ITEM(1800, 200, 0), + 5, 5, TLV_DB_SCALE_ITEM(2400, 0, 0), + 6, 7, TLV_DB_SCALE_ITEM(2700, 300, 0) +); +static const DECLARE_TLV_DB_SCALE(beep_tlv, -9600, 600, 1); +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0); +static const DECLARE_TLV_DB_SCALE(inmix_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(hp_tlv, -700, 100, 0); +static const DECLARE_TLV_DB_RANGE(classd_tlv, + 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0), + 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0) +); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); + +static int wm8962_dsp2_write_config(struct snd_soc_component *component) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + + return regcache_sync_region(wm8962->regmap, + WM8962_HDBASS_AI_1, WM8962_MAX_REGISTER); +} + +static int wm8962_dsp2_set_enable(struct snd_soc_component *component, u16 val) +{ + u16 adcl = snd_soc_component_read(component, WM8962_LEFT_ADC_VOLUME); + u16 adcr = snd_soc_component_read(component, WM8962_RIGHT_ADC_VOLUME); + u16 dac = snd_soc_component_read(component, WM8962_ADC_DAC_CONTROL_1); + + /* Mute the ADCs and DACs */ + snd_soc_component_write(component, WM8962_LEFT_ADC_VOLUME, 0); + snd_soc_component_write(component, WM8962_RIGHT_ADC_VOLUME, WM8962_ADC_VU); + snd_soc_component_update_bits(component, WM8962_ADC_DAC_CONTROL_1, + WM8962_DAC_MUTE, WM8962_DAC_MUTE); + + snd_soc_component_write(component, WM8962_SOUNDSTAGE_ENABLES_0, val); + + /* Restore the ADCs and DACs */ + snd_soc_component_write(component, WM8962_LEFT_ADC_VOLUME, adcl); + snd_soc_component_write(component, WM8962_RIGHT_ADC_VOLUME, adcr); + snd_soc_component_update_bits(component, WM8962_ADC_DAC_CONTROL_1, + WM8962_DAC_MUTE, dac); + + return 0; +} + +static int wm8962_dsp2_start(struct snd_soc_component *component) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + + wm8962_dsp2_write_config(component); + + snd_soc_component_write(component, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_RUNR); + + wm8962_dsp2_set_enable(component, wm8962->dsp2_ena); + + return 0; +} + +static int wm8962_dsp2_stop(struct snd_soc_component *component) +{ + wm8962_dsp2_set_enable(component, 0); + + snd_soc_component_write(component, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_STOP); + + return 0; +} + +#define WM8962_DSP2_ENABLE(xname, xshift) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = wm8962_dsp2_ena_info, \ + .get = wm8962_dsp2_ena_get, .put = wm8962_dsp2_ena_put, \ + .private_value = xshift } + +static int wm8962_dsp2_ena_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static int wm8962_dsp2_ena_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int shift = kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift); + + return 0; +} + +static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int shift = kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + int old = wm8962->dsp2_ena; + int ret = 0; + int dsp2_running = snd_soc_component_read(component, WM8962_DSP2_POWER_MANAGEMENT) & + WM8962_DSP2_ENA; + + mutex_lock(&wm8962->dsp2_ena_lock); + + if (ucontrol->value.integer.value[0]) + wm8962->dsp2_ena |= 1 << shift; + else + wm8962->dsp2_ena &= ~(1 << shift); + + if (wm8962->dsp2_ena == old) + goto out; + + ret = 1; + + if (dsp2_running) { + if (wm8962->dsp2_ena) + wm8962_dsp2_set_enable(component, wm8962->dsp2_ena); + else + wm8962_dsp2_stop(component); + } + +out: + mutex_unlock(&wm8962->dsp2_ena_lock); + + return ret; +} + +/* The VU bits for the headphones are in a different register to the mute + * bits and only take effect on the PGA if it is actually powered. + */ +static int wm8962_put_hp_sw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int ret; + + /* Apply the update (if any) */ + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret == 0) + return 0; + + /* If the left PGA is enabled hit that VU bit... */ + ret = snd_soc_component_read(component, WM8962_PWR_MGMT_2); + if (ret & WM8962_HPOUTL_PGA_ENA) { + snd_soc_component_write(component, WM8962_HPOUTL_VOLUME, + snd_soc_component_read(component, WM8962_HPOUTL_VOLUME)); + return 1; + } + + /* ...otherwise the right. The VU is stereo. */ + if (ret & WM8962_HPOUTR_PGA_ENA) + snd_soc_component_write(component, WM8962_HPOUTR_VOLUME, + snd_soc_component_read(component, WM8962_HPOUTR_VOLUME)); + + return 1; +} + +/* The VU bits for the speakers are in a different register to the mute + * bits and only take effect on the PGA if it is actually powered. + */ +static int wm8962_put_spk_sw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int ret; + + /* Apply the update (if any) */ + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret == 0) + return 0; + + /* If the left PGA is enabled hit that VU bit... */ + ret = snd_soc_component_read(component, WM8962_PWR_MGMT_2); + if (ret & WM8962_SPKOUTL_PGA_ENA) { + snd_soc_component_write(component, WM8962_SPKOUTL_VOLUME, + snd_soc_component_read(component, WM8962_SPKOUTL_VOLUME)); + return 1; + } + + /* ...otherwise the right. The VU is stereo. */ + if (ret & WM8962_SPKOUTR_PGA_ENA) + snd_soc_component_write(component, WM8962_SPKOUTR_VOLUME, + snd_soc_component_read(component, WM8962_SPKOUTR_VOLUME)); + + return 1; +} + +static const char *cap_hpf_mode_text[] = { + "Hi-fi", "Application" +}; + +static SOC_ENUM_SINGLE_DECL(cap_hpf_mode, + WM8962_ADC_DAC_CONTROL_2, 10, cap_hpf_mode_text); + + +static const char *cap_lhpf_mode_text[] = { + "LPF", "HPF" +}; + +static SOC_ENUM_SINGLE_DECL(cap_lhpf_mode, + WM8962_LHPF1, 1, cap_lhpf_mode_text); + +static const struct snd_kcontrol_new wm8962_snd_controls[] = { +SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1), + +SOC_SINGLE_TLV("MIXINL IN2L Volume", WM8962_LEFT_INPUT_MIXER_VOLUME, 6, 7, 0, + mixin_tlv), +SOC_SINGLE_TLV("MIXINL PGA Volume", WM8962_LEFT_INPUT_MIXER_VOLUME, 3, 7, 0, + mixinpga_tlv), +SOC_SINGLE_TLV("MIXINL IN3L Volume", WM8962_LEFT_INPUT_MIXER_VOLUME, 0, 7, 0, + mixin_tlv), + +SOC_SINGLE_TLV("MIXINR IN2R Volume", WM8962_RIGHT_INPUT_MIXER_VOLUME, 6, 7, 0, + mixin_tlv), +SOC_SINGLE_TLV("MIXINR PGA Volume", WM8962_RIGHT_INPUT_MIXER_VOLUME, 3, 7, 0, + mixinpga_tlv), +SOC_SINGLE_TLV("MIXINR IN3R Volume", WM8962_RIGHT_INPUT_MIXER_VOLUME, 0, 7, 0, + mixin_tlv), + +SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8962_LEFT_ADC_VOLUME, + WM8962_RIGHT_ADC_VOLUME, 1, 127, 0, digital_tlv), +SOC_DOUBLE_R_TLV("Capture Volume", WM8962_LEFT_INPUT_VOLUME, + WM8962_RIGHT_INPUT_VOLUME, 0, 63, 0, inpga_tlv), +SOC_DOUBLE_R("Capture Switch", WM8962_LEFT_INPUT_VOLUME, + WM8962_RIGHT_INPUT_VOLUME, 7, 1, 1), +SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME, + WM8962_RIGHT_INPUT_VOLUME, 6, 1, 1), +SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1), +SOC_ENUM("Capture HPF Mode", cap_hpf_mode), +SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0), +SOC_SINGLE("Capture LHPF Switch", WM8962_LHPF1, 0, 1, 0), +SOC_ENUM("Capture LHPF Mode", cap_lhpf_mode), + +SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1, + WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv), + +SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8962_LEFT_DAC_VOLUME, + WM8962_RIGHT_DAC_VOLUME, 1, 127, 0, digital_tlv), +SOC_SINGLE("DAC High Performance Switch", WM8962_ADC_DAC_CONTROL_2, 0, 1, 0), +SOC_SINGLE("DAC L/R Swap Switch", WM8962_AUDIO_INTERFACE_0, 5, 1, 0), +SOC_SINGLE("ADC L/R Swap Switch", WM8962_AUDIO_INTERFACE_0, 8, 1, 0), +SOC_SINGLE("DAC Monomix Switch", WM8962_DAC_DSP_MIXING_1, WM8962_DAC_MONOMIX_SHIFT, 1, 0), +SOC_SINGLE("ADC Monomix Switch", WM8962_THREED1, WM8962_ADC_MONOMIX_SHIFT, 1, 0), + +SOC_SINGLE("ADC High Performance Switch", WM8962_ADDITIONAL_CONTROL_1, + 5, 1, 0), + +SOC_SINGLE_TLV("Beep Volume", WM8962_BEEP_GENERATOR_1, 4, 15, 0, beep_tlv), + +SOC_DOUBLE_R_TLV("Headphone Volume", WM8962_HPOUTL_VOLUME, + WM8962_HPOUTR_VOLUME, 0, 127, 0, out_tlv), +SOC_DOUBLE_EXT("Headphone Switch", WM8962_PWR_MGMT_2, 1, 0, 1, 1, + snd_soc_get_volsw, wm8962_put_hp_sw), +SOC_DOUBLE_R("Headphone ZC Switch", WM8962_HPOUTL_VOLUME, WM8962_HPOUTR_VOLUME, + 7, 1, 0), +SOC_DOUBLE_TLV("Headphone Aux Volume", WM8962_ANALOGUE_HP_2, 3, 6, 7, 0, + hp_tlv), + +SOC_DOUBLE_R("Headphone Mixer Switch", WM8962_HEADPHONE_MIXER_3, + WM8962_HEADPHONE_MIXER_4, 8, 1, 1), + +SOC_SINGLE_TLV("HPMIXL IN4L Volume", WM8962_HEADPHONE_MIXER_3, + 3, 7, 0, bypass_tlv), +SOC_SINGLE_TLV("HPMIXL IN4R Volume", WM8962_HEADPHONE_MIXER_3, + 0, 7, 0, bypass_tlv), +SOC_SINGLE_TLV("HPMIXL MIXINL Volume", WM8962_HEADPHONE_MIXER_3, + 7, 1, 1, inmix_tlv), +SOC_SINGLE_TLV("HPMIXL MIXINR Volume", WM8962_HEADPHONE_MIXER_3, + 6, 1, 1, inmix_tlv), + +SOC_SINGLE_TLV("HPMIXR IN4L Volume", WM8962_HEADPHONE_MIXER_4, + 3, 7, 0, bypass_tlv), +SOC_SINGLE_TLV("HPMIXR IN4R Volume", WM8962_HEADPHONE_MIXER_4, + 0, 7, 0, bypass_tlv), +SOC_SINGLE_TLV("HPMIXR MIXINL Volume", WM8962_HEADPHONE_MIXER_4, + 7, 1, 1, inmix_tlv), +SOC_SINGLE_TLV("HPMIXR MIXINR Volume", WM8962_HEADPHONE_MIXER_4, + 6, 1, 1, inmix_tlv), + +SOC_SINGLE_TLV("Speaker Boost Volume", WM8962_CLASS_D_CONTROL_2, 0, 7, 0, + classd_tlv), + +SOC_SINGLE("EQ Switch", WM8962_EQ1, WM8962_EQ_ENA_SHIFT, 1, 0), +SOC_DOUBLE_R_TLV("EQ1 Volume", WM8962_EQ2, WM8962_EQ22, + WM8962_EQL_B1_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ2 Volume", WM8962_EQ2, WM8962_EQ22, + WM8962_EQL_B2_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ3 Volume", WM8962_EQ2, WM8962_EQ22, + WM8962_EQL_B3_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23, + WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv), +SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23, + WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv), +SND_SOC_BYTES("EQL Coefficients", WM8962_EQ4, 18), +SND_SOC_BYTES("EQR Coefficients", WM8962_EQ24, 18), + + +SOC_SINGLE("3D Switch", WM8962_THREED1, 0, 1, 0), +SND_SOC_BYTES_MASK("3D Coefficients", WM8962_THREED1, 4, WM8962_THREED_ENA), + +SOC_SINGLE("DF1 Switch", WM8962_DF1, 0, 1, 0), +SND_SOC_BYTES_MASK("DF1 Coefficients", WM8962_DF1, 7, WM8962_DF1_ENA), + +SOC_SINGLE("DRC Switch", WM8962_DRC_1, 0, 1, 0), +SND_SOC_BYTES_MASK("DRC Coefficients", WM8962_DRC_1, 5, WM8962_DRC_ENA), + +WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT), +SND_SOC_BYTES("VSS Coefficients", WM8962_VSS_XHD2_1, 148), +WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT), +WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT), +SND_SOC_BYTES("HPF Coefficients", WM8962_LHPF2, 1), +WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT), +SND_SOC_BYTES("HD Bass Coefficients", WM8962_HDBASS_AI_1, 30), + +SOC_DOUBLE("ALC Switch", WM8962_ALC1, WM8962_ALCL_ENA_SHIFT, + WM8962_ALCR_ENA_SHIFT, 1, 0), +SND_SOC_BYTES_MASK("ALC Coefficients", WM8962_ALC1, 4, + WM8962_ALCL_ENA_MASK | WM8962_ALCR_ENA_MASK), +}; + +static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = { +SOC_SINGLE_TLV("Speaker Volume", WM8962_SPKOUTL_VOLUME, 0, 127, 0, out_tlv), +SOC_SINGLE_EXT("Speaker Switch", WM8962_CLASS_D_CONTROL_1, 1, 1, 1, + snd_soc_get_volsw, wm8962_put_spk_sw), +SOC_SINGLE("Speaker ZC Switch", WM8962_SPKOUTL_VOLUME, 7, 1, 0), + +SOC_SINGLE("Speaker Mixer Switch", WM8962_SPEAKER_MIXER_3, 8, 1, 1), +SOC_SINGLE_TLV("Speaker Mixer IN4L Volume", WM8962_SPEAKER_MIXER_3, + 3, 7, 0, bypass_tlv), +SOC_SINGLE_TLV("Speaker Mixer IN4R Volume", WM8962_SPEAKER_MIXER_3, + 0, 7, 0, bypass_tlv), +SOC_SINGLE_TLV("Speaker Mixer MIXINL Volume", WM8962_SPEAKER_MIXER_3, + 7, 1, 1, inmix_tlv), +SOC_SINGLE_TLV("Speaker Mixer MIXINR Volume", WM8962_SPEAKER_MIXER_3, + 6, 1, 1, inmix_tlv), +SOC_SINGLE_TLV("Speaker Mixer DACL Volume", WM8962_SPEAKER_MIXER_5, + 7, 1, 0, inmix_tlv), +SOC_SINGLE_TLV("Speaker Mixer DACR Volume", WM8962_SPEAKER_MIXER_5, + 6, 1, 0, inmix_tlv), +}; + +static const struct snd_kcontrol_new wm8962_spk_stereo_controls[] = { +SOC_DOUBLE_R_TLV("Speaker Volume", WM8962_SPKOUTL_VOLUME, + WM8962_SPKOUTR_VOLUME, 0, 127, 0, out_tlv), +SOC_DOUBLE_EXT("Speaker Switch", WM8962_CLASS_D_CONTROL_1, 1, 0, 1, 1, + snd_soc_get_volsw, wm8962_put_spk_sw), +SOC_DOUBLE_R("Speaker ZC Switch", WM8962_SPKOUTL_VOLUME, WM8962_SPKOUTR_VOLUME, + 7, 1, 0), + +SOC_DOUBLE_R("Speaker Mixer Switch", WM8962_SPEAKER_MIXER_3, + WM8962_SPEAKER_MIXER_4, 8, 1, 1), + +SOC_SINGLE_TLV("SPKOUTL Mixer IN4L Volume", WM8962_SPEAKER_MIXER_3, + 3, 7, 0, bypass_tlv), +SOC_SINGLE_TLV("SPKOUTL Mixer IN4R Volume", WM8962_SPEAKER_MIXER_3, + 0, 7, 0, bypass_tlv), +SOC_SINGLE_TLV("SPKOUTL Mixer MIXINL Volume", WM8962_SPEAKER_MIXER_3, + 7, 1, 1, inmix_tlv), +SOC_SINGLE_TLV("SPKOUTL Mixer MIXINR Volume", WM8962_SPEAKER_MIXER_3, + 6, 1, 1, inmix_tlv), +SOC_SINGLE_TLV("SPKOUTL Mixer DACL Volume", WM8962_SPEAKER_MIXER_5, + 7, 1, 0, inmix_tlv), +SOC_SINGLE_TLV("SPKOUTL Mixer DACR Volume", WM8962_SPEAKER_MIXER_5, + 6, 1, 0, inmix_tlv), + +SOC_SINGLE_TLV("SPKOUTR Mixer IN4L Volume", WM8962_SPEAKER_MIXER_4, + 3, 7, 0, bypass_tlv), +SOC_SINGLE_TLV("SPKOUTR Mixer IN4R Volume", WM8962_SPEAKER_MIXER_4, + 0, 7, 0, bypass_tlv), +SOC_SINGLE_TLV("SPKOUTR Mixer MIXINL Volume", WM8962_SPEAKER_MIXER_4, + 7, 1, 1, inmix_tlv), +SOC_SINGLE_TLV("SPKOUTR Mixer MIXINR Volume", WM8962_SPEAKER_MIXER_4, + 6, 1, 1, inmix_tlv), +SOC_SINGLE_TLV("SPKOUTR Mixer DACL Volume", WM8962_SPEAKER_MIXER_5, + 5, 1, 0, inmix_tlv), +SOC_SINGLE_TLV("SPKOUTR Mixer DACR Volume", WM8962_SPEAKER_MIXER_5, + 4, 1, 0, inmix_tlv), +}; + +static int tp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret, reg, val, mask; + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + dev_err(component->dev, "Failed to resume device: %d\n", ret); + return ret; + } + + reg = WM8962_ADDITIONAL_CONTROL_4; + + if (!strcmp(w->name, "TEMP_HP")) { + mask = WM8962_TEMP_ENA_HP_MASK; + val = WM8962_TEMP_ENA_HP; + } else if (!strcmp(w->name, "TEMP_SPK")) { + mask = WM8962_TEMP_ENA_SPK_MASK; + val = WM8962_TEMP_ENA_SPK; + } else { + pm_runtime_put(component->dev); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + val = 0; + fallthrough; + case SND_SOC_DAPM_POST_PMU: + ret = snd_soc_component_update_bits(component, reg, mask, val); + break; + default: + WARN(1, "Invalid event %d\n", event); + pm_runtime_put(component->dev); + return -EINVAL; + } + + pm_runtime_put(component->dev); + + return 0; +} + +static int cp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(5); + break; + + default: + WARN(1, "Invalid event %d\n", event); + return -EINVAL; + } + + return 0; +} + +static int hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int timeout; + int reg; + int expected = (WM8962_DCS_STARTUP_DONE_HP1L | + WM8962_DCS_STARTUP_DONE_HP1R); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0, + WM8962_HP1L_ENA | WM8962_HP1R_ENA, + WM8962_HP1L_ENA | WM8962_HP1R_ENA); + udelay(20); + + snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0, + WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY, + WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY); + + /* Start the DC servo */ + snd_soc_component_update_bits(component, WM8962_DC_SERVO_1, + WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA | + WM8962_HP1L_DCS_STARTUP | + WM8962_HP1R_DCS_STARTUP, + WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA | + WM8962_HP1L_DCS_STARTUP | + WM8962_HP1R_DCS_STARTUP); + + /* Wait for it to complete, should be well under 100ms */ + timeout = 0; + do { + msleep(1); + reg = snd_soc_component_read(component, WM8962_DC_SERVO_6); + if (reg < 0) { + dev_err(component->dev, + "Failed to read DCS status: %d\n", + reg); + continue; + } + dev_dbg(component->dev, "DCS status: %x\n", reg); + } while (++timeout < 200 && (reg & expected) != expected); + + if ((reg & expected) != expected) + dev_err(component->dev, "DC servo timed out\n"); + else + dev_dbg(component->dev, "DC servo complete after %dms\n", + timeout); + + snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0, + WM8962_HP1L_ENA_OUTP | + WM8962_HP1R_ENA_OUTP, + WM8962_HP1L_ENA_OUTP | + WM8962_HP1R_ENA_OUTP); + udelay(20); + + snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0, + WM8962_HP1L_RMV_SHORT | + WM8962_HP1R_RMV_SHORT, + WM8962_HP1L_RMV_SHORT | + WM8962_HP1R_RMV_SHORT); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0, + WM8962_HP1L_RMV_SHORT | + WM8962_HP1R_RMV_SHORT, 0); + + udelay(20); + + snd_soc_component_update_bits(component, WM8962_DC_SERVO_1, + WM8962_HP1L_DCS_ENA | WM8962_HP1R_DCS_ENA | + WM8962_HP1L_DCS_STARTUP | + WM8962_HP1R_DCS_STARTUP, + 0); + + snd_soc_component_update_bits(component, WM8962_ANALOGUE_HP_0, + WM8962_HP1L_ENA | WM8962_HP1R_ENA | + WM8962_HP1L_ENA_DLY | WM8962_HP1R_ENA_DLY | + WM8962_HP1L_ENA_OUTP | + WM8962_HP1R_ENA_OUTP, 0); + + break; + + default: + WARN(1, "Invalid event %d\n", event); + return -EINVAL; + + } + + return 0; +} + +/* VU bits for the output PGAs only take effect while the PGA is powered */ +static int out_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int reg; + + switch (w->shift) { + case WM8962_HPOUTR_PGA_ENA_SHIFT: + reg = WM8962_HPOUTR_VOLUME; + break; + case WM8962_HPOUTL_PGA_ENA_SHIFT: + reg = WM8962_HPOUTL_VOLUME; + break; + case WM8962_SPKOUTR_PGA_ENA_SHIFT: + reg = WM8962_SPKOUTR_VOLUME; + break; + case WM8962_SPKOUTL_PGA_ENA_SHIFT: + reg = WM8962_SPKOUTL_VOLUME; + break; + default: + WARN(1, "Invalid shift %d\n", w->shift); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + return snd_soc_component_write(component, reg, + snd_soc_component_read(component, reg)); + default: + WARN(1, "Invalid event %d\n", event); + return -EINVAL; + } +} + +static int dsp2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (wm8962->dsp2_ena) + wm8962_dsp2_start(component); + break; + + case SND_SOC_DAPM_PRE_PMD: + if (wm8962->dsp2_ena) + wm8962_dsp2_stop(component); + break; + + default: + WARN(1, "Invalid event %d\n", event); + return -EINVAL; + } + + return 0; +} + +static const char *st_text[] = { "None", "Left", "Right" }; + +static SOC_ENUM_SINGLE_DECL(str_enum, + WM8962_DAC_DSP_MIXING_1, 2, st_text); + +static const struct snd_kcontrol_new str_mux = + SOC_DAPM_ENUM("Right Sidetone", str_enum); + +static SOC_ENUM_SINGLE_DECL(stl_enum, + WM8962_DAC_DSP_MIXING_2, 2, st_text); + +static const struct snd_kcontrol_new stl_mux = + SOC_DAPM_ENUM("Left Sidetone", stl_enum); + +static const char *outmux_text[] = { "DAC", "Mixer" }; + +static SOC_ENUM_SINGLE_DECL(spkoutr_enum, + WM8962_SPEAKER_MIXER_2, 7, outmux_text); + +static const struct snd_kcontrol_new spkoutr_mux = + SOC_DAPM_ENUM("SPKOUTR Mux", spkoutr_enum); + +static SOC_ENUM_SINGLE_DECL(spkoutl_enum, + WM8962_SPEAKER_MIXER_1, 7, outmux_text); + +static const struct snd_kcontrol_new spkoutl_mux = + SOC_DAPM_ENUM("SPKOUTL Mux", spkoutl_enum); + +static SOC_ENUM_SINGLE_DECL(hpoutr_enum, + WM8962_HEADPHONE_MIXER_2, 7, outmux_text); + +static const struct snd_kcontrol_new hpoutr_mux = + SOC_DAPM_ENUM("HPOUTR Mux", hpoutr_enum); + +static SOC_ENUM_SINGLE_DECL(hpoutl_enum, + WM8962_HEADPHONE_MIXER_1, 7, outmux_text); + +static const struct snd_kcontrol_new hpoutl_mux = + SOC_DAPM_ENUM("HPOUTL Mux", hpoutl_enum); + +static const char * const input_mode_text[] = { "Analog", "Digital" }; + +static SOC_ENUM_SINGLE_VIRT_DECL(input_mode_enum, input_mode_text); + +static const struct snd_kcontrol_new input_mode_mux = + SOC_DAPM_ENUM("Input Mode", input_mode_enum); + +static const struct snd_kcontrol_new inpgal[] = { +SOC_DAPM_SINGLE("IN1L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 3, 1, 0), +SOC_DAPM_SINGLE("IN2L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 2, 1, 0), +SOC_DAPM_SINGLE("IN3L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 1, 1, 0), +SOC_DAPM_SINGLE("IN4L Switch", WM8962_LEFT_INPUT_PGA_CONTROL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new inpgar[] = { +SOC_DAPM_SINGLE("IN1R Switch", WM8962_RIGHT_INPUT_PGA_CONTROL, 3, 1, 0), +SOC_DAPM_SINGLE("IN2R Switch", WM8962_RIGHT_INPUT_PGA_CONTROL, 2, 1, 0), +SOC_DAPM_SINGLE("IN3R Switch", WM8962_RIGHT_INPUT_PGA_CONTROL, 1, 1, 0), +SOC_DAPM_SINGLE("IN4R Switch", WM8962_RIGHT_INPUT_PGA_CONTROL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new mixinl[] = { +SOC_DAPM_SINGLE("IN2L Switch", WM8962_INPUT_MIXER_CONTROL_2, 5, 1, 0), +SOC_DAPM_SINGLE("IN3L Switch", WM8962_INPUT_MIXER_CONTROL_2, 4, 1, 0), +SOC_DAPM_SINGLE("PGA Switch", WM8962_INPUT_MIXER_CONTROL_2, 3, 1, 0), +}; + +static const struct snd_kcontrol_new mixinr[] = { +SOC_DAPM_SINGLE("IN2R Switch", WM8962_INPUT_MIXER_CONTROL_2, 2, 1, 0), +SOC_DAPM_SINGLE("IN3R Switch", WM8962_INPUT_MIXER_CONTROL_2, 1, 1, 0), +SOC_DAPM_SINGLE("PGA Switch", WM8962_INPUT_MIXER_CONTROL_2, 0, 1, 0), +}; + +static const struct snd_kcontrol_new hpmixl[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8962_HEADPHONE_MIXER_1, 5, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8962_HEADPHONE_MIXER_1, 4, 1, 0), +SOC_DAPM_SINGLE("MIXINL Switch", WM8962_HEADPHONE_MIXER_1, 3, 1, 0), +SOC_DAPM_SINGLE("MIXINR Switch", WM8962_HEADPHONE_MIXER_1, 2, 1, 0), +SOC_DAPM_SINGLE("IN4L Switch", WM8962_HEADPHONE_MIXER_1, 1, 1, 0), +SOC_DAPM_SINGLE("IN4R Switch", WM8962_HEADPHONE_MIXER_1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new hpmixr[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8962_HEADPHONE_MIXER_2, 5, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8962_HEADPHONE_MIXER_2, 4, 1, 0), +SOC_DAPM_SINGLE("MIXINL Switch", WM8962_HEADPHONE_MIXER_2, 3, 1, 0), +SOC_DAPM_SINGLE("MIXINR Switch", WM8962_HEADPHONE_MIXER_2, 2, 1, 0), +SOC_DAPM_SINGLE("IN4L Switch", WM8962_HEADPHONE_MIXER_2, 1, 1, 0), +SOC_DAPM_SINGLE("IN4R Switch", WM8962_HEADPHONE_MIXER_2, 0, 1, 0), +}; + +static const struct snd_kcontrol_new spkmixl[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8962_SPEAKER_MIXER_1, 5, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8962_SPEAKER_MIXER_1, 4, 1, 0), +SOC_DAPM_SINGLE("MIXINL Switch", WM8962_SPEAKER_MIXER_1, 3, 1, 0), +SOC_DAPM_SINGLE("MIXINR Switch", WM8962_SPEAKER_MIXER_1, 2, 1, 0), +SOC_DAPM_SINGLE("IN4L Switch", WM8962_SPEAKER_MIXER_1, 1, 1, 0), +SOC_DAPM_SINGLE("IN4R Switch", WM8962_SPEAKER_MIXER_1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new spkmixr[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8962_SPEAKER_MIXER_2, 5, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8962_SPEAKER_MIXER_2, 4, 1, 0), +SOC_DAPM_SINGLE("MIXINL Switch", WM8962_SPEAKER_MIXER_2, 3, 1, 0), +SOC_DAPM_SINGLE("MIXINR Switch", WM8962_SPEAKER_MIXER_2, 2, 1, 0), +SOC_DAPM_SINGLE("IN4L Switch", WM8962_SPEAKER_MIXER_2, 1, 1, 0), +SOC_DAPM_SINGLE("IN4R Switch", WM8962_SPEAKER_MIXER_2, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8962_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), +SND_SOC_DAPM_INPUT("IN3L"), +SND_SOC_DAPM_INPUT("IN3R"), +SND_SOC_DAPM_INPUT("IN4L"), +SND_SOC_DAPM_INPUT("IN4R"), +SND_SOC_DAPM_SIGGEN("Beep"), +SND_SOC_DAPM_INPUT("DMICDAT"), + +SND_SOC_DAPM_SUPPLY("MICBIAS", WM8962_PWR_MGMT_1, 1, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("Class G", WM8962_CHARGE_PUMP_B, 0, 1, NULL, 0), +SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event, + SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY_S("DSP2", 1, WM8962_DSP2_POWER_MANAGEMENT, + WM8962_DSP2_ENA_SHIFT, 0, dsp2_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_SUPPLY("TEMP_HP", SND_SOC_NOPM, 0, 0, tp_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("TEMP_SPK", SND_SOC_NOPM, 0, 0, tp_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0, + inpgal, ARRAY_SIZE(inpgal)), +SND_SOC_DAPM_MIXER("INPGAR", WM8962_RIGHT_INPUT_PGA_CONTROL, 4, 0, + inpgar, ARRAY_SIZE(inpgar)), +SND_SOC_DAPM_MIXER("MIXINL", WM8962_PWR_MGMT_1, 5, 0, + mixinl, ARRAY_SIZE(mixinl)), +SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0, + mixinr, ARRAY_SIZE(mixinr)), + +SND_SOC_DAPM_AIF_IN("DMIC_ENA", NULL, 0, WM8962_PWR_MGMT_1, 10, 0), + +SND_SOC_DAPM_MUX("Input Mode L", SND_SOC_NOPM, 0, 0, &input_mode_mux), +SND_SOC_DAPM_MUX("Input Mode R", SND_SOC_NOPM, 0, 0, &input_mode_mux), + +SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0), +SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0), + +SND_SOC_DAPM_MUX("STL", SND_SOC_NOPM, 0, 0, &stl_mux), +SND_SOC_DAPM_MUX("STR", SND_SOC_NOPM, 0, 0, &str_mux), + +SND_SOC_DAPM_DAC("DACL", "Playback", WM8962_PWR_MGMT_2, 8, 0), +SND_SOC_DAPM_DAC("DACR", "Playback", WM8962_PWR_MGMT_2, 7, 0), + +SND_SOC_DAPM_PGA("Left Bypass", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Bypass", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("HPMIXL", WM8962_MIXER_ENABLES, 3, 0, + hpmixl, ARRAY_SIZE(hpmixl)), +SND_SOC_DAPM_MIXER("HPMIXR", WM8962_MIXER_ENABLES, 2, 0, + hpmixr, ARRAY_SIZE(hpmixr)), + +SND_SOC_DAPM_MUX_E("HPOUTL PGA", WM8962_PWR_MGMT_2, 6, 0, &hpoutl_mux, + out_pga_event, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_MUX_E("HPOUTR PGA", WM8962_PWR_MGMT_2, 5, 0, &hpoutr_mux, + out_pga_event, SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA_E("HPOUT", SND_SOC_NOPM, 0, 0, NULL, 0, hp_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +}; + +static const struct snd_soc_dapm_widget wm8962_dapm_spk_mono_widgets[] = { +SND_SOC_DAPM_MIXER("Speaker Mixer", WM8962_MIXER_ENABLES, 1, 0, + spkmixl, ARRAY_SIZE(spkmixl)), +SND_SOC_DAPM_MUX_E("Speaker PGA", WM8962_PWR_MGMT_2, 4, 0, &spkoutl_mux, + out_pga_event, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA("Speaker Output", WM8962_CLASS_D_CONTROL_1, 7, 0, NULL, 0), +SND_SOC_DAPM_OUTPUT("SPKOUT"), +}; + +static const struct snd_soc_dapm_widget wm8962_dapm_spk_stereo_widgets[] = { +SND_SOC_DAPM_MIXER("SPKOUTL Mixer", WM8962_MIXER_ENABLES, 1, 0, + spkmixl, ARRAY_SIZE(spkmixl)), +SND_SOC_DAPM_MIXER("SPKOUTR Mixer", WM8962_MIXER_ENABLES, 0, 0, + spkmixr, ARRAY_SIZE(spkmixr)), + +SND_SOC_DAPM_MUX_E("SPKOUTL PGA", WM8962_PWR_MGMT_2, 4, 0, &spkoutl_mux, + out_pga_event, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_MUX_E("SPKOUTR PGA", WM8962_PWR_MGMT_2, 3, 0, &spkoutr_mux, + out_pga_event, SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("SPKOUTR Output", WM8962_CLASS_D_CONTROL_1, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPKOUTL Output", WM8962_CLASS_D_CONTROL_1, 6, 0, NULL, 0), + +SND_SOC_DAPM_OUTPUT("SPKOUTL"), +SND_SOC_DAPM_OUTPUT("SPKOUTR"), +}; + +static const struct snd_soc_dapm_route wm8962_intercon[] = { + { "INPGAL", "IN1L Switch", "IN1L" }, + { "INPGAL", "IN2L Switch", "IN2L" }, + { "INPGAL", "IN3L Switch", "IN3L" }, + { "INPGAL", "IN4L Switch", "IN4L" }, + + { "INPGAR", "IN1R Switch", "IN1R" }, + { "INPGAR", "IN2R Switch", "IN2R" }, + { "INPGAR", "IN3R Switch", "IN3R" }, + { "INPGAR", "IN4R Switch", "IN4R" }, + + { "MIXINL", "IN2L Switch", "IN2L" }, + { "MIXINL", "IN3L Switch", "IN3L" }, + { "MIXINL", "PGA Switch", "INPGAL" }, + + { "MIXINR", "IN2R Switch", "IN2R" }, + { "MIXINR", "IN3R Switch", "IN3R" }, + { "MIXINR", "PGA Switch", "INPGAR" }, + + { "MICBIAS", NULL, "SYSCLK" }, + + { "DMIC_ENA", NULL, "DMICDAT" }, + + { "Input Mode L", "Analog", "MIXINL" }, + { "Input Mode L", "Digital", "DMIC_ENA" }, + { "Input Mode R", "Analog", "MIXINR" }, + { "Input Mode R", "Digital", "DMIC_ENA" }, + + { "ADCL", NULL, "SYSCLK" }, + { "ADCL", NULL, "TOCLK" }, + { "ADCL", NULL, "Input Mode L" }, + { "ADCL", NULL, "DSP2" }, + + { "ADCR", NULL, "SYSCLK" }, + { "ADCR", NULL, "TOCLK" }, + { "ADCR", NULL, "Input Mode R" }, + { "ADCR", NULL, "DSP2" }, + + { "STL", "Left", "ADCL" }, + { "STL", "Right", "ADCR" }, + { "STL", NULL, "Class G" }, + + { "STR", "Left", "ADCL" }, + { "STR", "Right", "ADCR" }, + { "STR", NULL, "Class G" }, + + { "DACL", NULL, "SYSCLK" }, + { "DACL", NULL, "TOCLK" }, + { "DACL", NULL, "Beep" }, + { "DACL", NULL, "STL" }, + { "DACL", NULL, "DSP2" }, + + { "DACR", NULL, "SYSCLK" }, + { "DACR", NULL, "TOCLK" }, + { "DACR", NULL, "Beep" }, + { "DACR", NULL, "STR" }, + { "DACR", NULL, "DSP2" }, + + { "HPMIXL", "IN4L Switch", "IN4L" }, + { "HPMIXL", "IN4R Switch", "IN4R" }, + { "HPMIXL", "DACL Switch", "DACL" }, + { "HPMIXL", "DACR Switch", "DACR" }, + { "HPMIXL", "MIXINL Switch", "MIXINL" }, + { "HPMIXL", "MIXINR Switch", "MIXINR" }, + + { "HPMIXR", "IN4L Switch", "IN4L" }, + { "HPMIXR", "IN4R Switch", "IN4R" }, + { "HPMIXR", "DACL Switch", "DACL" }, + { "HPMIXR", "DACR Switch", "DACR" }, + { "HPMIXR", "MIXINL Switch", "MIXINL" }, + { "HPMIXR", "MIXINR Switch", "MIXINR" }, + + { "Left Bypass", NULL, "HPMIXL" }, + { "Left Bypass", NULL, "Class G" }, + + { "Right Bypass", NULL, "HPMIXR" }, + { "Right Bypass", NULL, "Class G" }, + + { "HPOUTL PGA", "Mixer", "Left Bypass" }, + { "HPOUTL PGA", "DAC", "DACL" }, + + { "HPOUTR PGA", "Mixer", "Right Bypass" }, + { "HPOUTR PGA", "DAC", "DACR" }, + + { "HPOUT", NULL, "HPOUTL PGA" }, + { "HPOUT", NULL, "HPOUTR PGA" }, + { "HPOUT", NULL, "Charge Pump" }, + { "HPOUT", NULL, "SYSCLK" }, + { "HPOUT", NULL, "TOCLK" }, + + { "HPOUTL", NULL, "HPOUT" }, + { "HPOUTR", NULL, "HPOUT" }, + + { "HPOUTL", NULL, "TEMP_HP" }, + { "HPOUTR", NULL, "TEMP_HP" }, +}; + +static const struct snd_soc_dapm_route wm8962_spk_mono_intercon[] = { + { "Speaker Mixer", "IN4L Switch", "IN4L" }, + { "Speaker Mixer", "IN4R Switch", "IN4R" }, + { "Speaker Mixer", "DACL Switch", "DACL" }, + { "Speaker Mixer", "DACR Switch", "DACR" }, + { "Speaker Mixer", "MIXINL Switch", "MIXINL" }, + { "Speaker Mixer", "MIXINR Switch", "MIXINR" }, + + { "Speaker PGA", "Mixer", "Speaker Mixer" }, + { "Speaker PGA", "DAC", "DACL" }, + + { "Speaker Output", NULL, "Speaker PGA" }, + { "Speaker Output", NULL, "SYSCLK" }, + { "Speaker Output", NULL, "TOCLK" }, + { "Speaker Output", NULL, "TEMP_SPK" }, + + { "SPKOUT", NULL, "Speaker Output" }, +}; + +static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = { + { "SPKOUTL Mixer", "IN4L Switch", "IN4L" }, + { "SPKOUTL Mixer", "IN4R Switch", "IN4R" }, + { "SPKOUTL Mixer", "DACL Switch", "DACL" }, + { "SPKOUTL Mixer", "DACR Switch", "DACR" }, + { "SPKOUTL Mixer", "MIXINL Switch", "MIXINL" }, + { "SPKOUTL Mixer", "MIXINR Switch", "MIXINR" }, + + { "SPKOUTR Mixer", "IN4L Switch", "IN4L" }, + { "SPKOUTR Mixer", "IN4R Switch", "IN4R" }, + { "SPKOUTR Mixer", "DACL Switch", "DACL" }, + { "SPKOUTR Mixer", "DACR Switch", "DACR" }, + { "SPKOUTR Mixer", "MIXINL Switch", "MIXINL" }, + { "SPKOUTR Mixer", "MIXINR Switch", "MIXINR" }, + + { "SPKOUTL PGA", "Mixer", "SPKOUTL Mixer" }, + { "SPKOUTL PGA", "DAC", "DACL" }, + + { "SPKOUTR PGA", "Mixer", "SPKOUTR Mixer" }, + { "SPKOUTR PGA", "DAC", "DACR" }, + + { "SPKOUTL Output", NULL, "SPKOUTL PGA" }, + { "SPKOUTL Output", NULL, "SYSCLK" }, + { "SPKOUTL Output", NULL, "TOCLK" }, + { "SPKOUTL Output", NULL, "TEMP_SPK" }, + + { "SPKOUTR Output", NULL, "SPKOUTR PGA" }, + { "SPKOUTR Output", NULL, "SYSCLK" }, + { "SPKOUTR Output", NULL, "TOCLK" }, + { "SPKOUTR Output", NULL, "TEMP_SPK" }, + + { "SPKOUTL", NULL, "SPKOUTL Output" }, + { "SPKOUTR", NULL, "SPKOUTR Output" }, +}; + +static int wm8962_add_widgets(struct snd_soc_component *component) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + struct wm8962_pdata *pdata = &wm8962->pdata; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_add_component_controls(component, wm8962_snd_controls, + ARRAY_SIZE(wm8962_snd_controls)); + if (pdata->spk_mono) + snd_soc_add_component_controls(component, wm8962_spk_mono_controls, + ARRAY_SIZE(wm8962_spk_mono_controls)); + else + snd_soc_add_component_controls(component, wm8962_spk_stereo_controls, + ARRAY_SIZE(wm8962_spk_stereo_controls)); + + + snd_soc_dapm_new_controls(dapm, wm8962_dapm_widgets, + ARRAY_SIZE(wm8962_dapm_widgets)); + if (pdata->spk_mono) + snd_soc_dapm_new_controls(dapm, wm8962_dapm_spk_mono_widgets, + ARRAY_SIZE(wm8962_dapm_spk_mono_widgets)); + else + snd_soc_dapm_new_controls(dapm, wm8962_dapm_spk_stereo_widgets, + ARRAY_SIZE(wm8962_dapm_spk_stereo_widgets)); + + snd_soc_dapm_add_routes(dapm, wm8962_intercon, + ARRAY_SIZE(wm8962_intercon)); + if (pdata->spk_mono) + snd_soc_dapm_add_routes(dapm, wm8962_spk_mono_intercon, + ARRAY_SIZE(wm8962_spk_mono_intercon)); + else + snd_soc_dapm_add_routes(dapm, wm8962_spk_stereo_intercon, + ARRAY_SIZE(wm8962_spk_stereo_intercon)); + + + snd_soc_dapm_disable_pin(dapm, "Beep"); + + return 0; +} + +/* -1 for reserved values */ +static const int bclk_divs[] = { + 1, -1, 2, 3, 4, -1, 6, 8, -1, 12, 16, 24, -1, 32, 32, 32 +}; + +static const int sysclk_rates[] = { + 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536, 3072, 6144 +}; + +static void wm8962_configure_bclk(struct snd_soc_component *component) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + int best, min_diff, diff; + int dspclk, i; + int clocking2 = 0; + int clocking4 = 0; + int aif2 = 0; + + if (!wm8962->sysclk_rate) { + dev_dbg(component->dev, "No SYSCLK configured\n"); + return; + } + + if (!wm8962->bclk || !wm8962->lrclk) { + dev_dbg(component->dev, "No audio clocks configured\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) { + if (sysclk_rates[i] == wm8962->sysclk_rate / wm8962->lrclk) { + clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT; + break; + } + } + + if (i == ARRAY_SIZE(sysclk_rates)) { + dev_err(component->dev, "Unsupported sysclk ratio %d\n", + wm8962->sysclk_rate / wm8962->lrclk); + return; + } + + dev_dbg(component->dev, "Selected sysclk ratio %d\n", sysclk_rates[i]); + + snd_soc_component_update_bits(component, WM8962_CLOCKING_4, + WM8962_SYSCLK_RATE_MASK, clocking4); + + /* DSPCLK_DIV can be only generated correctly after enabling SYSCLK. + * So we here provisionally enable it and then disable it afterward + * if current bias_level hasn't reached SND_SOC_BIAS_ON. + */ + if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON) + snd_soc_component_update_bits(component, WM8962_CLOCKING2, + WM8962_SYSCLK_ENA_MASK, WM8962_SYSCLK_ENA); + + /* DSPCLK_DIV field in WM8962_CLOCKING1 register is used to generate + * correct frequency of LRCLK and BCLK. Sometimes the read-only value + * can't be updated timely after enabling SYSCLK. This results in wrong + * calculation values. Delay is introduced here to wait for newest + * value from register. The time of the delay should be at least + * 500~1000us according to test. + */ + usleep_range(500, 1000); + dspclk = snd_soc_component_read(component, WM8962_CLOCKING1); + + if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON) + snd_soc_component_update_bits(component, WM8962_CLOCKING2, + WM8962_SYSCLK_ENA_MASK, 0); + + if (dspclk < 0) { + dev_err(component->dev, "Failed to read DSPCLK: %d\n", dspclk); + return; + } + + dspclk = (dspclk & WM8962_DSPCLK_DIV_MASK) >> WM8962_DSPCLK_DIV_SHIFT; + switch (dspclk) { + case 0: + dspclk = wm8962->sysclk_rate; + break; + case 1: + dspclk = wm8962->sysclk_rate / 2; + break; + case 2: + dspclk = wm8962->sysclk_rate / 4; + break; + default: + dev_warn(component->dev, "Unknown DSPCLK divisor read back\n"); + dspclk = wm8962->sysclk_rate; + } + + dev_dbg(component->dev, "DSPCLK is %dHz, BCLK %d\n", dspclk, wm8962->bclk); + + /* Search a proper bclk, not exact match. */ + best = 0; + min_diff = INT_MAX; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + if (bclk_divs[i] < 0) + continue; + + diff = (dspclk / bclk_divs[i]) - wm8962->bclk; + if (diff < 0) /* Table is sorted */ + break; + if (diff < min_diff) { + best = i; + min_diff = diff; + } + } + wm8962->bclk = dspclk / bclk_divs[best]; + clocking2 |= best; + dev_dbg(component->dev, "Selected BCLK_DIV %d for %dHz\n", + bclk_divs[best], wm8962->bclk); + + aif2 |= wm8962->bclk / wm8962->lrclk; + dev_dbg(component->dev, "Selected LRCLK divisor %d for %dHz\n", + wm8962->bclk / wm8962->lrclk, wm8962->lrclk); + + snd_soc_component_update_bits(component, WM8962_CLOCKING2, + WM8962_BCLK_DIV_MASK, clocking2); + snd_soc_component_update_bits(component, WM8962_AUDIO_INTERFACE_2, + WM8962_AIF_RATE_MASK, aif2); +} + +static int wm8962_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID 2*50k */ + snd_soc_component_update_bits(component, WM8962_PWR_MGMT_1, + WM8962_VMID_SEL_MASK, 0x80); + + wm8962_configure_bclk(component); + break; + + case SND_SOC_BIAS_STANDBY: + /* VMID 2*250k */ + snd_soc_component_update_bits(component, WM8962_PWR_MGMT_1, + WM8962_VMID_SEL_MASK, 0x100); + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + msleep(100); + break; + + case SND_SOC_BIAS_OFF: + break; + } + + return 0; +} + +static const struct { + int rate; + int reg; +} sr_vals[] = { + { 48000, 0 }, + { 44100, 0 }, + { 32000, 1 }, + { 22050, 2 }, + { 24000, 2 }, + { 16000, 3 }, + { 11025, 4 }, + { 12000, 4 }, + { 8000, 5 }, + { 88200, 6 }, + { 96000, 6 }, +}; + +static int wm8962_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + int i; + int aif0 = 0; + int adctl3 = 0; + + wm8962->bclk = snd_soc_params_to_bclk(params); + if (params_channels(params) == 1) + wm8962->bclk *= 2; + + wm8962->lrclk = params_rate(params); + + for (i = 0; i < ARRAY_SIZE(sr_vals); i++) { + if (sr_vals[i].rate == wm8962->lrclk) { + adctl3 |= sr_vals[i].reg; + break; + } + } + if (i == ARRAY_SIZE(sr_vals)) { + dev_err(component->dev, "Unsupported rate %dHz\n", wm8962->lrclk); + return -EINVAL; + } + + if (wm8962->lrclk % 8000 == 0) + adctl3 |= WM8962_SAMPLE_RATE_INT_MODE; + + switch (params_width(params)) { + case 16: + break; + case 20: + aif0 |= 0x4; + break; + case 24: + aif0 |= 0x8; + break; + case 32: + aif0 |= 0xc; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8962_AUDIO_INTERFACE_0, + WM8962_WL_MASK, aif0); + snd_soc_component_update_bits(component, WM8962_ADDITIONAL_CONTROL_3, + WM8962_SAMPLE_RATE_INT_MODE | + WM8962_SAMPLE_RATE_MASK, adctl3); + + dev_dbg(component->dev, "hw_params set BCLK %dHz LRCLK %dHz\n", + wm8962->bclk, wm8962->lrclk); + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_ON) + wm8962_configure_bclk(component); + + return 0; +} + +static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + int src; + + switch (clk_id) { + case WM8962_SYSCLK_MCLK: + wm8962->sysclk = WM8962_SYSCLK_MCLK; + src = 0; + break; + case WM8962_SYSCLK_FLL: + wm8962->sysclk = WM8962_SYSCLK_FLL; + src = 1 << WM8962_SYSCLK_SRC_SHIFT; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8962_CLOCKING2, WM8962_SYSCLK_SRC_MASK, + src); + + wm8962->sysclk_rate = freq; + + return 0; +} + +static int wm8962_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + int aif0 = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif0 |= WM8962_LRCLK_INV | 3; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + aif0 |= 3; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_IB_NF: + break; + default: + return -EINVAL; + } + break; + + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif0 |= 1; + break; + case SND_SOC_DAIFMT_I2S: + aif0 |= 2; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif0 |= WM8962_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif0 |= WM8962_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + aif0 |= WM8962_BCLK_INV | WM8962_LRCLK_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aif0 |= WM8962_MSTR; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8962_AUDIO_INTERFACE_0, + WM8962_FMT_MASK | WM8962_BCLK_INV | WM8962_MSTR | + WM8962_LRCLK_INV, aif0); + + return 0; +} + +struct _fll_div { + u16 fll_fratio; + u16 fll_outdiv; + u16 fll_refclk_div; + u16 n; + u16 theta; + u16 lambda; +}; + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +static struct { + unsigned int min; + unsigned int max; + u16 fll_fratio; + int ratio; +} fll_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, + unsigned int Fout) +{ + unsigned int target; + unsigned int div; + unsigned int fratio, gcd_fll; + int i; + + /* Fref must be <=13.5MHz */ + div = 1; + fll_div->fll_refclk_div = 0; + while ((Fref / div) > 13500000) { + div *= 2; + fll_div->fll_refclk_div++; + + if (div > 4) { + pr_err("Can't scale %dMHz input down to <=13.5MHz\n", + Fref); + return -EINVAL; + } + } + + pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout); + + /* Apply the division for our remaining calculations */ + Fref /= div; + + /* Fvco should be 90-100MHz; don't check the upper bound */ + div = 2; + while (Fout * div < 90000000) { + div++; + if (div > 64) { + pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + } + target = Fout * div; + fll_div->fll_outdiv = div - 1; + + pr_debug("FLL Fvco=%dHz\n", target); + + /* Find an appropriate FLL_FRATIO and factor it out of the target */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + fll_div->fll_fratio = fll_fratios[i].fll_fratio; + fratio = fll_fratios[i].ratio; + break; + } + } + if (i == ARRAY_SIZE(fll_fratios)) { + pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref); + return -EINVAL; + } + + fll_div->n = target / (fratio * Fref); + + if (target % Fref == 0) { + fll_div->theta = 0; + fll_div->lambda = 1; + } else { + gcd_fll = gcd(target, fratio * Fref); + + fll_div->theta = (target - (fll_div->n * fratio * Fref)) + / gcd_fll; + fll_div->lambda = (fratio * Fref) / gcd_fll; + } + + pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n", + fll_div->n, fll_div->theta, fll_div->lambda); + pr_debug("FLL_FRATIO=%x FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n", + fll_div->fll_fratio, fll_div->fll_outdiv, + fll_div->fll_refclk_div); + + return 0; +} + +static int wm8962_set_fll(struct snd_soc_component *component, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + struct _fll_div fll_div; + unsigned long timeout; + int ret; + int fll1 = 0; + + /* Any change? */ + if (source == wm8962->fll_src && Fref == wm8962->fll_fref && + Fout == wm8962->fll_fout) + return 0; + + if (Fout == 0) { + dev_dbg(component->dev, "FLL disabled\n"); + + wm8962->fll_fref = 0; + wm8962->fll_fout = 0; + + snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1, + WM8962_FLL_ENA, 0); + + pm_runtime_put(component->dev); + + return 0; + } + + ret = fll_factors(&fll_div, Fref, Fout); + if (ret != 0) + return ret; + + /* Parameters good, disable so we can reprogram */ + snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, 0); + + switch (fll_id) { + case WM8962_FLL_MCLK: + case WM8962_FLL_BCLK: + case WM8962_FLL_OSC: + fll1 |= (fll_id - 1) << WM8962_FLL_REFCLK_SRC_SHIFT; + break; + case WM8962_FLL_INT: + snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1, + WM8962_FLL_OSC_ENA, WM8962_FLL_OSC_ENA); + snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_5, + WM8962_FLL_FRC_NCO, WM8962_FLL_FRC_NCO); + break; + default: + dev_err(component->dev, "Unknown FLL source %d\n", ret); + return -EINVAL; + } + + if (fll_div.theta) + fll1 |= WM8962_FLL_FRAC; + + /* Stop the FLL while we reconfigure */ + snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1, WM8962_FLL_ENA, 0); + + snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_2, + WM8962_FLL_OUTDIV_MASK | + WM8962_FLL_REFCLK_DIV_MASK, + (fll_div.fll_outdiv << WM8962_FLL_OUTDIV_SHIFT) | + (fll_div.fll_refclk_div)); + + snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_3, + WM8962_FLL_FRATIO_MASK, fll_div.fll_fratio); + + snd_soc_component_write(component, WM8962_FLL_CONTROL_6, fll_div.theta); + snd_soc_component_write(component, WM8962_FLL_CONTROL_7, fll_div.lambda); + snd_soc_component_write(component, WM8962_FLL_CONTROL_8, fll_div.n); + + reinit_completion(&wm8962->fll_lock); + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + dev_err(component->dev, "Failed to resume device: %d\n", ret); + return ret; + } + + snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1, + WM8962_FLL_FRAC | WM8962_FLL_REFCLK_SRC_MASK | + WM8962_FLL_ENA, fll1 | WM8962_FLL_ENA); + + dev_dbg(component->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout); + + /* This should be a massive overestimate but go even + * higher if we'll error out + */ + if (wm8962->irq) + timeout = msecs_to_jiffies(5); + else + timeout = msecs_to_jiffies(1); + + timeout = wait_for_completion_timeout(&wm8962->fll_lock, + timeout); + + if (timeout == 0 && wm8962->irq) { + dev_err(component->dev, "FLL lock timed out"); + snd_soc_component_update_bits(component, WM8962_FLL_CONTROL_1, + WM8962_FLL_ENA, 0); + pm_runtime_put(component->dev); + return -ETIMEDOUT; + } + + wm8962->fll_fref = Fref; + wm8962->fll_fout = Fout; + wm8962->fll_src = source; + + return 0; +} + +static int wm8962_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + int val, ret; + + if (mute) + val = WM8962_DAC_MUTE | WM8962_DAC_MUTE_ALT; + else + val = 0; + + /** + * The DAC mute bit is mirrored in two registers, update both to keep + * the register cache consistent. + */ + ret = snd_soc_component_update_bits(component, WM8962_CLASS_D_CONTROL_1, + WM8962_DAC_MUTE_ALT, val); + if (ret < 0) + return ret; + + return snd_soc_component_update_bits(component, WM8962_ADC_DAC_CONTROL_1, + WM8962_DAC_MUTE, val); +} + +#define WM8962_RATES (SNDRV_PCM_RATE_8000_48000 |\ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define WM8962_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8962_dai_ops = { + .hw_params = wm8962_hw_params, + .set_sysclk = wm8962_set_dai_sysclk, + .set_fmt = wm8962_set_dai_fmt, + .mute_stream = wm8962_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8962_dai = { + .name = "wm8962", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8962_RATES, + .formats = WM8962_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8962_RATES, + .formats = WM8962_FORMATS, + }, + .ops = &wm8962_dai_ops, + .symmetric_rate = 1, +}; + +static void wm8962_mic_work(struct work_struct *work) +{ + struct wm8962_priv *wm8962 = container_of(work, + struct wm8962_priv, + mic_work.work); + struct snd_soc_component *component = wm8962->component; + int status = 0; + int irq_pol = 0; + int reg; + + reg = snd_soc_component_read(component, WM8962_ADDITIONAL_CONTROL_4); + + if (reg & WM8962_MICDET_STS) { + status |= SND_JACK_MICROPHONE; + irq_pol |= WM8962_MICD_IRQ_POL; + } + + if (reg & WM8962_MICSHORT_STS) { + status |= SND_JACK_BTN_0; + irq_pol |= WM8962_MICSCD_IRQ_POL; + } + + snd_soc_jack_report(wm8962->jack, status, + SND_JACK_MICROPHONE | SND_JACK_BTN_0); + + snd_soc_component_update_bits(component, WM8962_MICINT_SOURCE_POL, + WM8962_MICSCD_IRQ_POL | + WM8962_MICD_IRQ_POL, irq_pol); +} + +static irqreturn_t wm8962_irq(int irq, void *data) +{ + struct device *dev = data; + struct wm8962_priv *wm8962 = dev_get_drvdata(dev); + unsigned int mask; + unsigned int active; + int reg, ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "Failed to resume: %d\n", ret); + return IRQ_NONE; + } + + ret = regmap_read(wm8962->regmap, WM8962_INTERRUPT_STATUS_2_MASK, + &mask); + if (ret != 0) { + pm_runtime_put(dev); + dev_err(dev, "Failed to read interrupt mask: %d\n", + ret); + return IRQ_NONE; + } + + ret = regmap_read(wm8962->regmap, WM8962_INTERRUPT_STATUS_2, &active); + if (ret != 0) { + pm_runtime_put(dev); + dev_err(dev, "Failed to read interrupt: %d\n", ret); + return IRQ_NONE; + } + + active &= ~mask; + + if (!active) { + pm_runtime_put(dev); + return IRQ_NONE; + } + + /* Acknowledge the interrupts */ + ret = regmap_write(wm8962->regmap, WM8962_INTERRUPT_STATUS_2, active); + if (ret != 0) + dev_warn(dev, "Failed to ack interrupt: %d\n", ret); + + if (active & WM8962_FLL_LOCK_EINT) { + dev_dbg(dev, "FLL locked\n"); + complete(&wm8962->fll_lock); + } + + if (active & WM8962_FIFOS_ERR_EINT) + dev_err(dev, "FIFO error\n"); + + if (active & WM8962_TEMP_SHUT_EINT) { + dev_crit(dev, "Thermal shutdown\n"); + + ret = regmap_read(wm8962->regmap, + WM8962_THERMAL_SHUTDOWN_STATUS, ®); + if (ret != 0) { + dev_warn(dev, "Failed to read thermal status: %d\n", + ret); + reg = 0; + } + + if (reg & WM8962_TEMP_ERR_HP) + dev_crit(dev, "Headphone thermal error\n"); + if (reg & WM8962_TEMP_WARN_HP) + dev_crit(dev, "Headphone thermal warning\n"); + if (reg & WM8962_TEMP_ERR_SPK) + dev_crit(dev, "Speaker thermal error\n"); + if (reg & WM8962_TEMP_WARN_SPK) + dev_crit(dev, "Speaker thermal warning\n"); + } + + if (active & (WM8962_MICSCD_EINT | WM8962_MICD_EINT)) { + dev_dbg(dev, "Microphone event detected\n"); + +#ifndef CONFIG_SND_SOC_WM8962_MODULE + trace_snd_soc_jack_irq(dev_name(dev)); +#endif + + pm_wakeup_event(dev, 300); + + queue_delayed_work(system_power_efficient_wq, + &wm8962->mic_work, + msecs_to_jiffies(250)); + } + + pm_runtime_put(dev); + + return IRQ_HANDLED; +} + +/** + * wm8962_mic_detect - Enable microphone detection via the WM8962 IRQ + * + * @component: WM8962 component + * @jack: jack to report detection events on + * + * Enable microphone detection via IRQ on the WM8962. If GPIOs are + * being used to bring out signals to the processor then only platform + * data configuration is needed for WM8962 and processor GPIOs should + * be configured using snd_soc_jack_add_gpios() instead. + * + * If no jack is supplied detection will be disabled. + */ +int wm8962_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int irq_mask, enable; + + wm8962->jack = jack; + if (jack) { + irq_mask = 0; + enable = WM8962_MICDET_ENA; + } else { + irq_mask = WM8962_MICD_EINT | WM8962_MICSCD_EINT; + enable = 0; + } + + snd_soc_component_update_bits(component, WM8962_INTERRUPT_STATUS_2_MASK, + WM8962_MICD_EINT | WM8962_MICSCD_EINT, irq_mask); + snd_soc_component_update_bits(component, WM8962_ADDITIONAL_CONTROL_4, + WM8962_MICDET_ENA, enable); + + /* Send an initial empty report */ + snd_soc_jack_report(wm8962->jack, 0, + SND_JACK_MICROPHONE | SND_JACK_BTN_0); + + snd_soc_dapm_mutex_lock(dapm); + + if (jack) { + snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS"); + } else { + snd_soc_dapm_disable_pin_unlocked(dapm, "SYSCLK"); + snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS"); + } + + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} +EXPORT_SYMBOL_GPL(wm8962_mic_detect); + +static int beep_rates[] = { + 500, 1000, 2000, 4000, +}; + +static void wm8962_beep_work(struct work_struct *work) +{ + struct wm8962_priv *wm8962 = + container_of(work, struct wm8962_priv, beep_work); + struct snd_soc_component *component = wm8962->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int i; + int reg = 0; + int best = 0; + + if (wm8962->beep_rate) { + for (i = 0; i < ARRAY_SIZE(beep_rates); i++) { + if (abs(wm8962->beep_rate - beep_rates[i]) < + abs(wm8962->beep_rate - beep_rates[best])) + best = i; + } + + dev_dbg(component->dev, "Set beep rate %dHz for requested %dHz\n", + beep_rates[best], wm8962->beep_rate); + + reg = WM8962_BEEP_ENA | (best << WM8962_BEEP_RATE_SHIFT); + + snd_soc_dapm_enable_pin(dapm, "Beep"); + } else { + dev_dbg(component->dev, "Disabling beep\n"); + snd_soc_dapm_disable_pin(dapm, "Beep"); + } + + snd_soc_component_update_bits(component, WM8962_BEEP_GENERATOR_1, + WM8962_BEEP_ENA | WM8962_BEEP_RATE_MASK, reg); + + snd_soc_dapm_sync(dapm); +} + +/* For usability define a way of injecting beep events for the device - + * many systems will not have a keyboard. + */ +static int wm8962_beep_event(struct input_dev *dev, unsigned int type, + unsigned int code, int hz) +{ + struct snd_soc_component *component = input_get_drvdata(dev); + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + + dev_dbg(component->dev, "Beep event %x %x\n", code, hz); + + switch (code) { + case SND_BELL: + if (hz) + hz = 1000; + fallthrough; + case SND_TONE: + break; + default: + return -1; + } + + /* Kick the beep from a workqueue */ + wm8962->beep_rate = hz; + schedule_work(&wm8962->beep_work); + return 0; +} + +static ssize_t beep_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wm8962_priv *wm8962 = dev_get_drvdata(dev); + long int time; + int ret; + + ret = kstrtol(buf, 10, &time); + if (ret != 0) + return ret; + + input_event(wm8962->beep, EV_SND, SND_TONE, time); + + return count; +} + +static DEVICE_ATTR_WO(beep); + +static void wm8962_init_beep(struct snd_soc_component *component) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + int ret; + + wm8962->beep = devm_input_allocate_device(component->dev); + if (!wm8962->beep) { + dev_err(component->dev, "Failed to allocate beep device\n"); + return; + } + + INIT_WORK(&wm8962->beep_work, wm8962_beep_work); + wm8962->beep_rate = 0; + + wm8962->beep->name = "WM8962 Beep Generator"; + wm8962->beep->phys = dev_name(component->dev); + wm8962->beep->id.bustype = BUS_I2C; + + wm8962->beep->evbit[0] = BIT_MASK(EV_SND); + wm8962->beep->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + wm8962->beep->event = wm8962_beep_event; + wm8962->beep->dev.parent = component->dev; + input_set_drvdata(wm8962->beep, component); + + ret = input_register_device(wm8962->beep); + if (ret != 0) { + wm8962->beep = NULL; + dev_err(component->dev, "Failed to register beep device\n"); + } + + ret = device_create_file(component->dev, &dev_attr_beep); + if (ret != 0) { + dev_err(component->dev, "Failed to create keyclick file: %d\n", + ret); + } +} + +static void wm8962_free_beep(struct snd_soc_component *component) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + + device_remove_file(component->dev, &dev_attr_beep); + cancel_work_sync(&wm8962->beep_work); + wm8962->beep = NULL; + + snd_soc_component_update_bits(component, WM8962_BEEP_GENERATOR_1, WM8962_BEEP_ENA,0); +} + +static void wm8962_set_gpio_mode(struct wm8962_priv *wm8962, int gpio) +{ + int mask = 0; + int val = 0; + + /* Some of the GPIOs are behind MFP configuration and need to + * be put into GPIO mode. */ + switch (gpio) { + case 2: + mask = WM8962_CLKOUT2_SEL_MASK; + val = 1 << WM8962_CLKOUT2_SEL_SHIFT; + break; + case 3: + mask = WM8962_CLKOUT3_SEL_MASK; + val = 1 << WM8962_CLKOUT3_SEL_SHIFT; + break; + default: + break; + } + + if (mask) + regmap_update_bits(wm8962->regmap, WM8962_ANALOGUE_CLOCKING1, + mask, val); +} + +#ifdef CONFIG_GPIOLIB +static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct wm8962_priv *wm8962 = gpiochip_get_data(chip); + + /* The WM8962 GPIOs aren't linearly numbered. For simplicity + * we export linear numbers and error out if the unsupported + * ones are requsted. + */ + switch (offset + 1) { + case 2: + case 3: + case 5: + case 6: + break; + default: + return -EINVAL; + } + + wm8962_set_gpio_mode(wm8962, offset + 1); + + return 0; +} + +static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm8962_priv *wm8962 = gpiochip_get_data(chip); + struct snd_soc_component *component = wm8962->component; + + snd_soc_component_update_bits(component, WM8962_GPIO_BASE + offset, + WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT); +} + +static int wm8962_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm8962_priv *wm8962 = gpiochip_get_data(chip); + struct snd_soc_component *component = wm8962->component; + int ret, val; + + /* Force function 1 (logic output) */ + val = (1 << WM8962_GP2_FN_SHIFT) | (value << WM8962_GP2_LVL_SHIFT); + + ret = snd_soc_component_update_bits(component, WM8962_GPIO_BASE + offset, + WM8962_GP2_FN_MASK | WM8962_GP2_LVL, val); + if (ret < 0) + return ret; + + return 0; +} + +static const struct gpio_chip wm8962_template_chip = { + .label = "wm8962", + .owner = THIS_MODULE, + .request = wm8962_gpio_request, + .direction_output = wm8962_gpio_direction_out, + .set = wm8962_gpio_set, + .can_sleep = 1, +}; + +static void wm8962_init_gpio(struct snd_soc_component *component) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + struct wm8962_pdata *pdata = &wm8962->pdata; + int ret; + + wm8962->gpio_chip = wm8962_template_chip; + wm8962->gpio_chip.ngpio = WM8962_MAX_GPIO; + wm8962->gpio_chip.parent = component->dev; + + if (pdata->gpio_base) + wm8962->gpio_chip.base = pdata->gpio_base; + else + wm8962->gpio_chip.base = -1; + + ret = gpiochip_add_data(&wm8962->gpio_chip, wm8962); + if (ret != 0) + dev_err(component->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void wm8962_free_gpio(struct snd_soc_component *component) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + + gpiochip_remove(&wm8962->gpio_chip); +} +#else +static void wm8962_init_gpio(struct snd_soc_component *component) +{ +} + +static void wm8962_free_gpio(struct snd_soc_component *component) +{ +} +#endif + +static int wm8962_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret; + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + int i; + bool dmicclk, dmicdat; + + wm8962->component = component; + + wm8962->disable_nb[0].notifier_call = wm8962_regulator_event_0; + wm8962->disable_nb[1].notifier_call = wm8962_regulator_event_1; + wm8962->disable_nb[2].notifier_call = wm8962_regulator_event_2; + wm8962->disable_nb[3].notifier_call = wm8962_regulator_event_3; + wm8962->disable_nb[4].notifier_call = wm8962_regulator_event_4; + wm8962->disable_nb[5].notifier_call = wm8962_regulator_event_5; + wm8962->disable_nb[6].notifier_call = wm8962_regulator_event_6; + wm8962->disable_nb[7].notifier_call = wm8962_regulator_event_7; + + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) { + ret = devm_regulator_register_notifier( + wm8962->supplies[i].consumer, + &wm8962->disable_nb[i]); + if (ret != 0) { + dev_err(component->dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + + wm8962_add_widgets(component); + + /* Save boards having to disable DMIC when not in use */ + dmicclk = false; + dmicdat = false; + for (i = 1; i < WM8962_MAX_GPIO; i++) { + /* + * Register 515 (WM8962_GPIO_BASE + 3) does not exist, + * so skip its access + */ + if (i == 3) + continue; + switch (snd_soc_component_read(component, WM8962_GPIO_BASE + i) + & WM8962_GP2_FN_MASK) { + case WM8962_GPIO_FN_DMICCLK: + dmicclk = true; + break; + case WM8962_GPIO_FN_DMICDAT: + dmicdat = true; + break; + default: + break; + } + } + if (!dmicclk || !dmicdat) { + dev_dbg(component->dev, "DMIC not in use, disabling\n"); + snd_soc_dapm_nc_pin(dapm, "DMICDAT"); + } + if (dmicclk != dmicdat) + dev_warn(component->dev, "DMIC GPIOs partially configured\n"); + + wm8962_init_beep(component); + wm8962_init_gpio(component); + + return 0; +} + +static void wm8962_remove(struct snd_soc_component *component) +{ + struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component); + + cancel_delayed_work_sync(&wm8962->mic_work); + + wm8962_free_gpio(component); + wm8962_free_beep(component); +} + +static const struct snd_soc_component_driver soc_component_dev_wm8962 = { + .probe = wm8962_probe, + .remove = wm8962_remove, + .set_bias_level = wm8962_set_bias_level, + .set_pll = wm8962_set_fll, + .use_pmdown_time = 1, + .endianness = 1, +}; + +/* Improve power consumption for IN4 DC measurement mode */ +static const struct reg_sequence wm8962_dc_measure[] = { + { 0xfd, 0x1 }, + { 0xcc, 0x40 }, + { 0xfd, 0 }, +}; + +static const struct regmap_config wm8962_regmap = { + .reg_bits = 16, + .val_bits = 16, + + .max_register = WM8962_MAX_REGISTER, + .reg_defaults = wm8962_reg, + .num_reg_defaults = ARRAY_SIZE(wm8962_reg), + .volatile_reg = wm8962_volatile_register, + .readable_reg = wm8962_readable_register, + .cache_type = REGCACHE_MAPLE, +}; + +static int wm8962_set_pdata_from_of(struct i2c_client *i2c, + struct wm8962_pdata *pdata) +{ + const struct device_node *np = i2c->dev.of_node; + u32 val32; + int i; + + if (of_property_read_bool(np, "spk-mono")) + pdata->spk_mono = true; + + if (of_property_read_u32(np, "mic-cfg", &val32) >= 0) + pdata->mic_cfg = val32; + + if (of_property_read_u32_array(np, "gpio-cfg", pdata->gpio_init, + ARRAY_SIZE(pdata->gpio_init)) >= 0) + for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) { + /* + * The range of GPIO register value is [0x0, 0xffff] + * While the default value of each register is 0x0 + * Any other value will be regarded as default value + */ + if (pdata->gpio_init[i] > 0xffff) + pdata->gpio_init[i] = 0x0; + } + + pdata->mclk = devm_clk_get_optional(&i2c->dev, NULL); + return PTR_ERR_OR_ZERO(pdata->mclk); +} + +static int wm8962_i2c_probe(struct i2c_client *i2c) +{ + struct wm8962_pdata *pdata = dev_get_platdata(&i2c->dev); + struct wm8962_priv *wm8962; + unsigned int reg; + int ret, i, irq_pol, trigger; + + wm8962 = devm_kzalloc(&i2c->dev, sizeof(*wm8962), GFP_KERNEL); + if (wm8962 == NULL) + return -ENOMEM; + + mutex_init(&wm8962->dsp2_ena_lock); + + i2c_set_clientdata(i2c, wm8962); + + INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work); + init_completion(&wm8962->fll_lock); + wm8962->irq = i2c->irq; + + /* If platform data was supplied, update the default data in priv */ + if (pdata) { + memcpy(&wm8962->pdata, pdata, sizeof(struct wm8962_pdata)); + } else if (i2c->dev.of_node) { + ret = wm8962_set_pdata_from_of(i2c, &wm8962->pdata); + if (ret != 0) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) + wm8962->supplies[i].supply = wm8962_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8962->supplies), + wm8962->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + goto err; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies), + wm8962->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + wm8962->regmap = devm_regmap_init_i2c(i2c, &wm8962_regmap); + if (IS_ERR(wm8962->regmap)) { + ret = PTR_ERR(wm8962->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + goto err_enable; + } + + /* + * We haven't marked the chip revision as volatile due to + * sharing a register with the right input volume; explicitly + * bypass the cache to read it. + */ + regcache_cache_bypass(wm8962->regmap, true); + + ret = regmap_read(wm8962->regmap, WM8962_SOFTWARE_RESET, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID register\n"); + goto err_enable; + } + if (reg != 0x6243) { + dev_err(&i2c->dev, + "Device is not a WM8962, ID %x != 0x6243\n", reg); + ret = -EINVAL; + goto err_enable; + } + + ret = regmap_read(wm8962->regmap, WM8962_RIGHT_INPUT_VOLUME, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read device revision: %d\n", + ret); + goto err_enable; + } + + dev_info(&i2c->dev, "customer id %x revision %c\n", + (reg & WM8962_CUST_ID_MASK) >> WM8962_CUST_ID_SHIFT, + ((reg & WM8962_CHIP_REV_MASK) >> WM8962_CHIP_REV_SHIFT) + + 'A'); + + regcache_cache_bypass(wm8962->regmap, false); + + ret = wm8962_reset(wm8962); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to issue reset\n"); + goto err_enable; + } + + /* SYSCLK defaults to on; make sure it is off so we can safely + * write to registers if the device is declocked. + */ + regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2, + WM8962_SYSCLK_ENA, 0); + + /* Ensure we have soft control over all registers */ + regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2, + WM8962_CLKREG_OVD, WM8962_CLKREG_OVD); + + /* Ensure that the oscillator and PLLs are disabled */ + regmap_update_bits(wm8962->regmap, WM8962_PLL2, + WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, + 0); + + /* Apply static configuration for GPIOs */ + for (i = 0; i < ARRAY_SIZE(wm8962->pdata.gpio_init); i++) + if (wm8962->pdata.gpio_init[i]) { + wm8962_set_gpio_mode(wm8962, i + 1); + regmap_write(wm8962->regmap, 0x200 + i, + wm8962->pdata.gpio_init[i] & 0xffff); + } + + + /* Put the speakers into mono mode? */ + if (wm8962->pdata.spk_mono) + regmap_update_bits(wm8962->regmap, WM8962_CLASS_D_CONTROL_2, + WM8962_SPK_MONO_MASK, WM8962_SPK_MONO); + + /* Micbias setup, detection enable and detection + * threasholds. */ + if (wm8962->pdata.mic_cfg) + regmap_update_bits(wm8962->regmap, WM8962_ADDITIONAL_CONTROL_4, + WM8962_MICDET_ENA | + WM8962_MICDET_THR_MASK | + WM8962_MICSHORT_THR_MASK | + WM8962_MICBIAS_LVL, + wm8962->pdata.mic_cfg); + + /* Latch volume update bits */ + regmap_update_bits(wm8962->regmap, WM8962_LEFT_INPUT_VOLUME, + WM8962_IN_VU, WM8962_IN_VU); + regmap_update_bits(wm8962->regmap, WM8962_RIGHT_INPUT_VOLUME, + WM8962_IN_VU, WM8962_IN_VU); + regmap_update_bits(wm8962->regmap, WM8962_LEFT_ADC_VOLUME, + WM8962_ADC_VU, WM8962_ADC_VU); + regmap_update_bits(wm8962->regmap, WM8962_RIGHT_ADC_VOLUME, + WM8962_ADC_VU, WM8962_ADC_VU); + regmap_update_bits(wm8962->regmap, WM8962_LEFT_DAC_VOLUME, + WM8962_DAC_VU, WM8962_DAC_VU); + regmap_update_bits(wm8962->regmap, WM8962_RIGHT_DAC_VOLUME, + WM8962_DAC_VU, WM8962_DAC_VU); + regmap_update_bits(wm8962->regmap, WM8962_SPKOUTL_VOLUME, + WM8962_SPKOUT_VU, WM8962_SPKOUT_VU); + regmap_update_bits(wm8962->regmap, WM8962_SPKOUTR_VOLUME, + WM8962_SPKOUT_VU, WM8962_SPKOUT_VU); + regmap_update_bits(wm8962->regmap, WM8962_HPOUTL_VOLUME, + WM8962_HPOUT_VU, WM8962_HPOUT_VU); + regmap_update_bits(wm8962->regmap, WM8962_HPOUTR_VOLUME, + WM8962_HPOUT_VU, WM8962_HPOUT_VU); + + /* Stereo control for EQ */ + regmap_update_bits(wm8962->regmap, WM8962_EQ1, + WM8962_EQ_SHARED_COEFF, 0); + + /* Don't debouce interrupts so we don't need SYSCLK */ + regmap_update_bits(wm8962->regmap, WM8962_IRQ_DEBOUNCE, + WM8962_FLL_LOCK_DB | WM8962_PLL3_LOCK_DB | + WM8962_PLL2_LOCK_DB | WM8962_TEMP_SHUT_DB, + 0); + + if (wm8962->pdata.in4_dc_measure) { + ret = regmap_register_patch(wm8962->regmap, + wm8962_dc_measure, + ARRAY_SIZE(wm8962_dc_measure)); + if (ret != 0) + dev_err(&i2c->dev, + "Failed to configure for DC measurement: %d\n", + ret); + } + + if (wm8962->irq) { + if (wm8962->pdata.irq_active_low) { + trigger = IRQF_TRIGGER_LOW; + irq_pol = WM8962_IRQ_POL; + } else { + trigger = IRQF_TRIGGER_HIGH; + irq_pol = 0; + } + + regmap_update_bits(wm8962->regmap, WM8962_INTERRUPT_CONTROL, + WM8962_IRQ_POL, irq_pol); + + ret = devm_request_threaded_irq(&i2c->dev, wm8962->irq, NULL, + wm8962_irq, + trigger | IRQF_ONESHOT, + "wm8962", &i2c->dev); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", + wm8962->irq, ret); + wm8962->irq = 0; + /* Non-fatal */ + } else { + /* Enable some IRQs by default */ + regmap_update_bits(wm8962->regmap, + WM8962_INTERRUPT_STATUS_2_MASK, + WM8962_FLL_LOCK_EINT | + WM8962_TEMP_SHUT_EINT | + WM8962_FIFOS_ERR_EINT, 0); + } + } + + pm_runtime_enable(&i2c->dev); + pm_request_idle(&i2c->dev); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8962, &wm8962_dai, 1); + if (ret < 0) + goto err_pm_runtime; + + regmap_update_bits(wm8962->regmap, WM8962_ADDITIONAL_CONTROL_4, + WM8962_TEMP_ENA_HP_MASK, 0); + regmap_update_bits(wm8962->regmap, WM8962_ADDITIONAL_CONTROL_4, + WM8962_TEMP_ENA_SPK_MASK, 0); + + regcache_cache_only(wm8962->regmap, true); + + /* The drivers should power up as needed */ + regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies); + + return 0; + +err_pm_runtime: + pm_runtime_disable(&i2c->dev); +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), wm8962->supplies); +err: + return ret; +} + +static void wm8962_i2c_remove(struct i2c_client *client) +{ + pm_runtime_disable(&client->dev); +} + +#ifdef CONFIG_PM +static int wm8962_runtime_resume(struct device *dev) +{ + struct wm8962_priv *wm8962 = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(wm8962->pdata.mclk); + if (ret) { + dev_err(dev, "Failed to enable MCLK: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8962->supplies), + wm8962->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + goto disable_clock; + } + + regcache_cache_only(wm8962->regmap, false); + + wm8962_reset(wm8962); + + regcache_mark_dirty(wm8962->regmap); + + /* SYSCLK defaults to on; make sure it is off so we can safely + * write to registers if the device is declocked. + */ + regmap_write_bits(wm8962->regmap, WM8962_CLOCKING2, + WM8962_SYSCLK_ENA, 0); + + /* Ensure we have soft control over all registers */ + regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2, + WM8962_CLKREG_OVD, WM8962_CLKREG_OVD); + + /* Ensure that the oscillator and PLLs are disabled */ + regmap_update_bits(wm8962->regmap, WM8962_PLL2, + WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, + 0); + + regcache_sync(wm8962->regmap); + + regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, + WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA, + WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA); + + /* Bias enable at 2*5k (fast start-up) */ + regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1, + WM8962_BIAS_ENA | WM8962_VMID_SEL_MASK, + WM8962_BIAS_ENA | 0x180); + + msleep(5); + + return 0; + +disable_clock: + clk_disable_unprepare(wm8962->pdata.mclk); + return ret; +} + +static int wm8962_runtime_suspend(struct device *dev) +{ + struct wm8962_priv *wm8962 = dev_get_drvdata(dev); + + regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1, + WM8962_VMID_SEL_MASK | WM8962_BIAS_ENA, 0); + + regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP, + WM8962_STARTUP_BIAS_ENA | + WM8962_VMID_BUF_ENA, 0); + + regcache_cache_only(wm8962->regmap, true); + + regulator_bulk_disable(ARRAY_SIZE(wm8962->supplies), + wm8962->supplies); + + clk_disable_unprepare(wm8962->pdata.mclk); + + return 0; +} +#endif + +static const struct dev_pm_ops wm8962_pm = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(wm8962_runtime_suspend, wm8962_runtime_resume, NULL) +}; + +static const struct i2c_device_id wm8962_i2c_id[] = { + { "wm8962", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8962_i2c_id); + +static const struct of_device_id wm8962_of_match[] = { + { .compatible = "wlf,wm8962", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8962_of_match); + +static struct i2c_driver wm8962_i2c_driver = { + .driver = { + .name = "wm8962", + .of_match_table = wm8962_of_match, + .pm = &wm8962_pm, + }, + .probe = wm8962_i2c_probe, + .remove = wm8962_i2c_remove, + .id_table = wm8962_i2c_id, +}; + +module_i2c_driver(wm8962_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8962 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8962.h b/sound/soc/codecs/wm8962.h new file mode 100644 index 0000000000..e7f4a70ab8 --- /dev/null +++ b/sound/soc/codecs/wm8962.h @@ -0,0 +1,3781 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8962.h -- WM8962 ASoC driver + * + * Copyright 2010 Wolfson Microelectronics, plc + * + * Author: Mark Brown + */ + +#ifndef _WM8962_H +#define _WM8962_H + +#include +#include + +#define WM8962_SYSCLK_MCLK 0 +#define WM8962_SYSCLK_FLL 1 +#define WM8962_SYSCLK_PLL3 2 + +#define WM8962_FLL 1 + +#define WM8962_FLL_MCLK 1 +#define WM8962_FLL_BCLK 2 +#define WM8962_FLL_OSC 3 +#define WM8962_FLL_INT 4 + +/* + * Register values. + */ +#define WM8962_LEFT_INPUT_VOLUME 0x00 +#define WM8962_RIGHT_INPUT_VOLUME 0x01 +#define WM8962_HPOUTL_VOLUME 0x02 +#define WM8962_HPOUTR_VOLUME 0x03 +#define WM8962_CLOCKING1 0x04 +#define WM8962_ADC_DAC_CONTROL_1 0x05 +#define WM8962_ADC_DAC_CONTROL_2 0x06 +#define WM8962_AUDIO_INTERFACE_0 0x07 +#define WM8962_CLOCKING2 0x08 +#define WM8962_AUDIO_INTERFACE_1 0x09 +#define WM8962_LEFT_DAC_VOLUME 0x0A +#define WM8962_RIGHT_DAC_VOLUME 0x0B +#define WM8962_AUDIO_INTERFACE_2 0x0E +#define WM8962_SOFTWARE_RESET 0x0F +#define WM8962_ALC1 0x11 +#define WM8962_ALC2 0x12 +#define WM8962_ALC3 0x13 +#define WM8962_NOISE_GATE 0x14 +#define WM8962_LEFT_ADC_VOLUME 0x15 +#define WM8962_RIGHT_ADC_VOLUME 0x16 +#define WM8962_ADDITIONAL_CONTROL_1 0x17 +#define WM8962_ADDITIONAL_CONTROL_2 0x18 +#define WM8962_PWR_MGMT_1 0x19 +#define WM8962_PWR_MGMT_2 0x1A +#define WM8962_ADDITIONAL_CONTROL_3 0x1B +#define WM8962_ANTI_POP 0x1C +#define WM8962_CLOCKING_3 0x1E +#define WM8962_INPUT_MIXER_CONTROL_1 0x1F +#define WM8962_LEFT_INPUT_MIXER_VOLUME 0x20 +#define WM8962_RIGHT_INPUT_MIXER_VOLUME 0x21 +#define WM8962_INPUT_MIXER_CONTROL_2 0x22 +#define WM8962_INPUT_BIAS_CONTROL 0x23 +#define WM8962_LEFT_INPUT_PGA_CONTROL 0x25 +#define WM8962_RIGHT_INPUT_PGA_CONTROL 0x26 +#define WM8962_SPKOUTL_VOLUME 0x28 +#define WM8962_SPKOUTR_VOLUME 0x29 +#define WM8962_THERMAL_SHUTDOWN_STATUS 0x2F +#define WM8962_ADDITIONAL_CONTROL_4 0x30 +#define WM8962_CLASS_D_CONTROL_1 0x31 +#define WM8962_CLASS_D_CONTROL_2 0x33 +#define WM8962_CLOCKING_4 0x38 +#define WM8962_DAC_DSP_MIXING_1 0x39 +#define WM8962_DAC_DSP_MIXING_2 0x3A +#define WM8962_DC_SERVO_0 0x3C +#define WM8962_DC_SERVO_1 0x3D +#define WM8962_DC_SERVO_4 0x40 +#define WM8962_DC_SERVO_6 0x42 +#define WM8962_ANALOGUE_PGA_BIAS 0x44 +#define WM8962_ANALOGUE_HP_0 0x45 +#define WM8962_ANALOGUE_HP_2 0x47 +#define WM8962_CHARGE_PUMP_1 0x48 +#define WM8962_CHARGE_PUMP_B 0x52 +#define WM8962_WRITE_SEQUENCER_CONTROL_1 0x57 +#define WM8962_WRITE_SEQUENCER_CONTROL_2 0x5A +#define WM8962_WRITE_SEQUENCER_CONTROL_3 0x5D +#define WM8962_CONTROL_INTERFACE 0x5E +#define WM8962_MIXER_ENABLES 0x63 +#define WM8962_HEADPHONE_MIXER_1 0x64 +#define WM8962_HEADPHONE_MIXER_2 0x65 +#define WM8962_HEADPHONE_MIXER_3 0x66 +#define WM8962_HEADPHONE_MIXER_4 0x67 +#define WM8962_SPEAKER_MIXER_1 0x69 +#define WM8962_SPEAKER_MIXER_2 0x6A +#define WM8962_SPEAKER_MIXER_3 0x6B +#define WM8962_SPEAKER_MIXER_4 0x6C +#define WM8962_SPEAKER_MIXER_5 0x6D +#define WM8962_BEEP_GENERATOR_1 0x6E +#define WM8962_OSCILLATOR_TRIM_3 0x73 +#define WM8962_OSCILLATOR_TRIM_4 0x74 +#define WM8962_OSCILLATOR_TRIM_7 0x77 +#define WM8962_ANALOGUE_CLOCKING1 0x7C +#define WM8962_ANALOGUE_CLOCKING2 0x7D +#define WM8962_ANALOGUE_CLOCKING3 0x7E +#define WM8962_PLL_SOFTWARE_RESET 0x7F +#define WM8962_PLL2 0x81 +#define WM8962_PLL_4 0x83 +#define WM8962_PLL_9 0x88 +#define WM8962_PLL_10 0x89 +#define WM8962_PLL_11 0x8A +#define WM8962_PLL_12 0x8B +#define WM8962_PLL_13 0x8C +#define WM8962_PLL_14 0x8D +#define WM8962_PLL_15 0x8E +#define WM8962_PLL_16 0x8F +#define WM8962_FLL_CONTROL_1 0x9B +#define WM8962_FLL_CONTROL_2 0x9C +#define WM8962_FLL_CONTROL_3 0x9D +#define WM8962_FLL_CONTROL_5 0x9F +#define WM8962_FLL_CONTROL_6 0xA0 +#define WM8962_FLL_CONTROL_7 0xA1 +#define WM8962_FLL_CONTROL_8 0xA2 +#define WM8962_GENERAL_TEST_1 0xFC +#define WM8962_DF1 0x100 +#define WM8962_DF2 0x101 +#define WM8962_DF3 0x102 +#define WM8962_DF4 0x103 +#define WM8962_DF5 0x104 +#define WM8962_DF6 0x105 +#define WM8962_DF7 0x106 +#define WM8962_LHPF1 0x108 +#define WM8962_LHPF2 0x109 +#define WM8962_THREED1 0x10C +#define WM8962_THREED2 0x10D +#define WM8962_THREED3 0x10E +#define WM8962_THREED4 0x10F +#define WM8962_DRC_1 0x114 +#define WM8962_DRC_2 0x115 +#define WM8962_DRC_3 0x116 +#define WM8962_DRC_4 0x117 +#define WM8962_DRC_5 0x118 +#define WM8962_TLOOPBACK 0x11D +#define WM8962_EQ1 0x14F +#define WM8962_EQ2 0x150 +#define WM8962_EQ3 0x151 +#define WM8962_EQ4 0x152 +#define WM8962_EQ5 0x153 +#define WM8962_EQ6 0x154 +#define WM8962_EQ7 0x155 +#define WM8962_EQ8 0x156 +#define WM8962_EQ9 0x157 +#define WM8962_EQ10 0x158 +#define WM8962_EQ11 0x159 +#define WM8962_EQ12 0x15A +#define WM8962_EQ13 0x15B +#define WM8962_EQ14 0x15C +#define WM8962_EQ15 0x15D +#define WM8962_EQ16 0x15E +#define WM8962_EQ17 0x15F +#define WM8962_EQ18 0x160 +#define WM8962_EQ19 0x161 +#define WM8962_EQ20 0x162 +#define WM8962_EQ21 0x163 +#define WM8962_EQ22 0x164 +#define WM8962_EQ23 0x165 +#define WM8962_EQ24 0x166 +#define WM8962_EQ25 0x167 +#define WM8962_EQ26 0x168 +#define WM8962_EQ27 0x169 +#define WM8962_EQ28 0x16A +#define WM8962_EQ29 0x16B +#define WM8962_EQ30 0x16C +#define WM8962_EQ31 0x16D +#define WM8962_EQ32 0x16E +#define WM8962_EQ33 0x16F +#define WM8962_EQ34 0x170 +#define WM8962_EQ35 0x171 +#define WM8962_EQ36 0x172 +#define WM8962_EQ37 0x173 +#define WM8962_EQ38 0x174 +#define WM8962_EQ39 0x175 +#define WM8962_EQ40 0x176 +#define WM8962_EQ41 0x177 +#define WM8962_GPIO_BASE 0x200 +#define WM8962_GPIO_2 0x201 +#define WM8962_GPIO_3 0x202 +#define WM8962_GPIO_5 0x204 +#define WM8962_GPIO_6 0x205 +#define WM8962_INTERRUPT_STATUS_1 0x230 +#define WM8962_INTERRUPT_STATUS_2 0x231 +#define WM8962_INTERRUPT_STATUS_1_MASK 0x238 +#define WM8962_INTERRUPT_STATUS_2_MASK 0x239 +#define WM8962_INTERRUPT_CONTROL 0x240 +#define WM8962_IRQ_DEBOUNCE 0x248 +#define WM8962_MICINT_SOURCE_POL 0x24A +#define WM8962_DSP2_POWER_MANAGEMENT 0x300 +#define WM8962_DSP2_EXECCONTROL 0x40D +#define WM8962_WRITE_SEQUENCER_0 0x1000 +#define WM8962_WRITE_SEQUENCER_1 0x1001 +#define WM8962_WRITE_SEQUENCER_2 0x1002 +#define WM8962_WRITE_SEQUENCER_3 0x1003 +#define WM8962_WRITE_SEQUENCER_4 0x1004 +#define WM8962_WRITE_SEQUENCER_5 0x1005 +#define WM8962_WRITE_SEQUENCER_6 0x1006 +#define WM8962_WRITE_SEQUENCER_7 0x1007 +#define WM8962_WRITE_SEQUENCER_8 0x1008 +#define WM8962_WRITE_SEQUENCER_9 0x1009 +#define WM8962_WRITE_SEQUENCER_10 0x100A +#define WM8962_WRITE_SEQUENCER_11 0x100B +#define WM8962_WRITE_SEQUENCER_12 0x100C +#define WM8962_WRITE_SEQUENCER_13 0x100D +#define WM8962_WRITE_SEQUENCER_14 0x100E +#define WM8962_WRITE_SEQUENCER_15 0x100F +#define WM8962_WRITE_SEQUENCER_16 0x1010 +#define WM8962_WRITE_SEQUENCER_17 0x1011 +#define WM8962_WRITE_SEQUENCER_18 0x1012 +#define WM8962_WRITE_SEQUENCER_19 0x1013 +#define WM8962_WRITE_SEQUENCER_20 0x1014 +#define WM8962_WRITE_SEQUENCER_21 0x1015 +#define WM8962_WRITE_SEQUENCER_22 0x1016 +#define WM8962_WRITE_SEQUENCER_23 0x1017 +#define WM8962_WRITE_SEQUENCER_24 0x1018 +#define WM8962_WRITE_SEQUENCER_25 0x1019 +#define WM8962_WRITE_SEQUENCER_26 0x101A +#define WM8962_WRITE_SEQUENCER_27 0x101B +#define WM8962_WRITE_SEQUENCER_28 0x101C +#define WM8962_WRITE_SEQUENCER_29 0x101D +#define WM8962_WRITE_SEQUENCER_30 0x101E +#define WM8962_WRITE_SEQUENCER_31 0x101F +#define WM8962_WRITE_SEQUENCER_32 0x1020 +#define WM8962_WRITE_SEQUENCER_33 0x1021 +#define WM8962_WRITE_SEQUENCER_34 0x1022 +#define WM8962_WRITE_SEQUENCER_35 0x1023 +#define WM8962_WRITE_SEQUENCER_36 0x1024 +#define WM8962_WRITE_SEQUENCER_37 0x1025 +#define WM8962_WRITE_SEQUENCER_38 0x1026 +#define WM8962_WRITE_SEQUENCER_39 0x1027 +#define WM8962_WRITE_SEQUENCER_40 0x1028 +#define WM8962_WRITE_SEQUENCER_41 0x1029 +#define WM8962_WRITE_SEQUENCER_42 0x102A +#define WM8962_WRITE_SEQUENCER_43 0x102B +#define WM8962_WRITE_SEQUENCER_44 0x102C +#define WM8962_WRITE_SEQUENCER_45 0x102D +#define WM8962_WRITE_SEQUENCER_46 0x102E +#define WM8962_WRITE_SEQUENCER_47 0x102F +#define WM8962_WRITE_SEQUENCER_48 0x1030 +#define WM8962_WRITE_SEQUENCER_49 0x1031 +#define WM8962_WRITE_SEQUENCER_50 0x1032 +#define WM8962_WRITE_SEQUENCER_51 0x1033 +#define WM8962_WRITE_SEQUENCER_52 0x1034 +#define WM8962_WRITE_SEQUENCER_53 0x1035 +#define WM8962_WRITE_SEQUENCER_54 0x1036 +#define WM8962_WRITE_SEQUENCER_55 0x1037 +#define WM8962_WRITE_SEQUENCER_56 0x1038 +#define WM8962_WRITE_SEQUENCER_57 0x1039 +#define WM8962_WRITE_SEQUENCER_58 0x103A +#define WM8962_WRITE_SEQUENCER_59 0x103B +#define WM8962_WRITE_SEQUENCER_60 0x103C +#define WM8962_WRITE_SEQUENCER_61 0x103D +#define WM8962_WRITE_SEQUENCER_62 0x103E +#define WM8962_WRITE_SEQUENCER_63 0x103F +#define WM8962_WRITE_SEQUENCER_64 0x1040 +#define WM8962_WRITE_SEQUENCER_65 0x1041 +#define WM8962_WRITE_SEQUENCER_66 0x1042 +#define WM8962_WRITE_SEQUENCER_67 0x1043 +#define WM8962_WRITE_SEQUENCER_68 0x1044 +#define WM8962_WRITE_SEQUENCER_69 0x1045 +#define WM8962_WRITE_SEQUENCER_70 0x1046 +#define WM8962_WRITE_SEQUENCER_71 0x1047 +#define WM8962_WRITE_SEQUENCER_72 0x1048 +#define WM8962_WRITE_SEQUENCER_73 0x1049 +#define WM8962_WRITE_SEQUENCER_74 0x104A +#define WM8962_WRITE_SEQUENCER_75 0x104B +#define WM8962_WRITE_SEQUENCER_76 0x104C +#define WM8962_WRITE_SEQUENCER_77 0x104D +#define WM8962_WRITE_SEQUENCER_78 0x104E +#define WM8962_WRITE_SEQUENCER_79 0x104F +#define WM8962_WRITE_SEQUENCER_80 0x1050 +#define WM8962_WRITE_SEQUENCER_81 0x1051 +#define WM8962_WRITE_SEQUENCER_82 0x1052 +#define WM8962_WRITE_SEQUENCER_83 0x1053 +#define WM8962_WRITE_SEQUENCER_84 0x1054 +#define WM8962_WRITE_SEQUENCER_85 0x1055 +#define WM8962_WRITE_SEQUENCER_86 0x1056 +#define WM8962_WRITE_SEQUENCER_87 0x1057 +#define WM8962_WRITE_SEQUENCER_88 0x1058 +#define WM8962_WRITE_SEQUENCER_89 0x1059 +#define WM8962_WRITE_SEQUENCER_90 0x105A +#define WM8962_WRITE_SEQUENCER_91 0x105B +#define WM8962_WRITE_SEQUENCER_92 0x105C +#define WM8962_WRITE_SEQUENCER_93 0x105D +#define WM8962_WRITE_SEQUENCER_94 0x105E +#define WM8962_WRITE_SEQUENCER_95 0x105F +#define WM8962_WRITE_SEQUENCER_96 0x1060 +#define WM8962_WRITE_SEQUENCER_97 0x1061 +#define WM8962_WRITE_SEQUENCER_98 0x1062 +#define WM8962_WRITE_SEQUENCER_99 0x1063 +#define WM8962_WRITE_SEQUENCER_100 0x1064 +#define WM8962_WRITE_SEQUENCER_101 0x1065 +#define WM8962_WRITE_SEQUENCER_102 0x1066 +#define WM8962_WRITE_SEQUENCER_103 0x1067 +#define WM8962_WRITE_SEQUENCER_104 0x1068 +#define WM8962_WRITE_SEQUENCER_105 0x1069 +#define WM8962_WRITE_SEQUENCER_106 0x106A +#define WM8962_WRITE_SEQUENCER_107 0x106B +#define WM8962_WRITE_SEQUENCER_108 0x106C +#define WM8962_WRITE_SEQUENCER_109 0x106D +#define WM8962_WRITE_SEQUENCER_110 0x106E +#define WM8962_WRITE_SEQUENCER_111 0x106F +#define WM8962_WRITE_SEQUENCER_112 0x1070 +#define WM8962_WRITE_SEQUENCER_113 0x1071 +#define WM8962_WRITE_SEQUENCER_114 0x1072 +#define WM8962_WRITE_SEQUENCER_115 0x1073 +#define WM8962_WRITE_SEQUENCER_116 0x1074 +#define WM8962_WRITE_SEQUENCER_117 0x1075 +#define WM8962_WRITE_SEQUENCER_118 0x1076 +#define WM8962_WRITE_SEQUENCER_119 0x1077 +#define WM8962_WRITE_SEQUENCER_120 0x1078 +#define WM8962_WRITE_SEQUENCER_121 0x1079 +#define WM8962_WRITE_SEQUENCER_122 0x107A +#define WM8962_WRITE_SEQUENCER_123 0x107B +#define WM8962_WRITE_SEQUENCER_124 0x107C +#define WM8962_WRITE_SEQUENCER_125 0x107D +#define WM8962_WRITE_SEQUENCER_126 0x107E +#define WM8962_WRITE_SEQUENCER_127 0x107F +#define WM8962_WRITE_SEQUENCER_128 0x1080 +#define WM8962_WRITE_SEQUENCER_129 0x1081 +#define WM8962_WRITE_SEQUENCER_130 0x1082 +#define WM8962_WRITE_SEQUENCER_131 0x1083 +#define WM8962_WRITE_SEQUENCER_132 0x1084 +#define WM8962_WRITE_SEQUENCER_133 0x1085 +#define WM8962_WRITE_SEQUENCER_134 0x1086 +#define WM8962_WRITE_SEQUENCER_135 0x1087 +#define WM8962_WRITE_SEQUENCER_136 0x1088 +#define WM8962_WRITE_SEQUENCER_137 0x1089 +#define WM8962_WRITE_SEQUENCER_138 0x108A +#define WM8962_WRITE_SEQUENCER_139 0x108B +#define WM8962_WRITE_SEQUENCER_140 0x108C +#define WM8962_WRITE_SEQUENCER_141 0x108D +#define WM8962_WRITE_SEQUENCER_142 0x108E +#define WM8962_WRITE_SEQUENCER_143 0x108F +#define WM8962_WRITE_SEQUENCER_144 0x1090 +#define WM8962_WRITE_SEQUENCER_145 0x1091 +#define WM8962_WRITE_SEQUENCER_146 0x1092 +#define WM8962_WRITE_SEQUENCER_147 0x1093 +#define WM8962_WRITE_SEQUENCER_148 0x1094 +#define WM8962_WRITE_SEQUENCER_149 0x1095 +#define WM8962_WRITE_SEQUENCER_150 0x1096 +#define WM8962_WRITE_SEQUENCER_151 0x1097 +#define WM8962_WRITE_SEQUENCER_152 0x1098 +#define WM8962_WRITE_SEQUENCER_153 0x1099 +#define WM8962_WRITE_SEQUENCER_154 0x109A +#define WM8962_WRITE_SEQUENCER_155 0x109B +#define WM8962_WRITE_SEQUENCER_156 0x109C +#define WM8962_WRITE_SEQUENCER_157 0x109D +#define WM8962_WRITE_SEQUENCER_158 0x109E +#define WM8962_WRITE_SEQUENCER_159 0x109F +#define WM8962_WRITE_SEQUENCER_160 0x10A0 +#define WM8962_WRITE_SEQUENCER_161 0x10A1 +#define WM8962_WRITE_SEQUENCER_162 0x10A2 +#define WM8962_WRITE_SEQUENCER_163 0x10A3 +#define WM8962_WRITE_SEQUENCER_164 0x10A4 +#define WM8962_WRITE_SEQUENCER_165 0x10A5 +#define WM8962_WRITE_SEQUENCER_166 0x10A6 +#define WM8962_WRITE_SEQUENCER_167 0x10A7 +#define WM8962_WRITE_SEQUENCER_168 0x10A8 +#define WM8962_WRITE_SEQUENCER_169 0x10A9 +#define WM8962_WRITE_SEQUENCER_170 0x10AA +#define WM8962_WRITE_SEQUENCER_171 0x10AB +#define WM8962_WRITE_SEQUENCER_172 0x10AC +#define WM8962_WRITE_SEQUENCER_173 0x10AD +#define WM8962_WRITE_SEQUENCER_174 0x10AE +#define WM8962_WRITE_SEQUENCER_175 0x10AF +#define WM8962_WRITE_SEQUENCER_176 0x10B0 +#define WM8962_WRITE_SEQUENCER_177 0x10B1 +#define WM8962_WRITE_SEQUENCER_178 0x10B2 +#define WM8962_WRITE_SEQUENCER_179 0x10B3 +#define WM8962_WRITE_SEQUENCER_180 0x10B4 +#define WM8962_WRITE_SEQUENCER_181 0x10B5 +#define WM8962_WRITE_SEQUENCER_182 0x10B6 +#define WM8962_WRITE_SEQUENCER_183 0x10B7 +#define WM8962_WRITE_SEQUENCER_184 0x10B8 +#define WM8962_WRITE_SEQUENCER_185 0x10B9 +#define WM8962_WRITE_SEQUENCER_186 0x10BA +#define WM8962_WRITE_SEQUENCER_187 0x10BB +#define WM8962_WRITE_SEQUENCER_188 0x10BC +#define WM8962_WRITE_SEQUENCER_189 0x10BD +#define WM8962_WRITE_SEQUENCER_190 0x10BE +#define WM8962_WRITE_SEQUENCER_191 0x10BF +#define WM8962_WRITE_SEQUENCER_192 0x10C0 +#define WM8962_WRITE_SEQUENCER_193 0x10C1 +#define WM8962_WRITE_SEQUENCER_194 0x10C2 +#define WM8962_WRITE_SEQUENCER_195 0x10C3 +#define WM8962_WRITE_SEQUENCER_196 0x10C4 +#define WM8962_WRITE_SEQUENCER_197 0x10C5 +#define WM8962_WRITE_SEQUENCER_198 0x10C6 +#define WM8962_WRITE_SEQUENCER_199 0x10C7 +#define WM8962_WRITE_SEQUENCER_200 0x10C8 +#define WM8962_WRITE_SEQUENCER_201 0x10C9 +#define WM8962_WRITE_SEQUENCER_202 0x10CA +#define WM8962_WRITE_SEQUENCER_203 0x10CB +#define WM8962_WRITE_SEQUENCER_204 0x10CC +#define WM8962_WRITE_SEQUENCER_205 0x10CD +#define WM8962_WRITE_SEQUENCER_206 0x10CE +#define WM8962_WRITE_SEQUENCER_207 0x10CF +#define WM8962_WRITE_SEQUENCER_208 0x10D0 +#define WM8962_WRITE_SEQUENCER_209 0x10D1 +#define WM8962_WRITE_SEQUENCER_210 0x10D2 +#define WM8962_WRITE_SEQUENCER_211 0x10D3 +#define WM8962_WRITE_SEQUENCER_212 0x10D4 +#define WM8962_WRITE_SEQUENCER_213 0x10D5 +#define WM8962_WRITE_SEQUENCER_214 0x10D6 +#define WM8962_WRITE_SEQUENCER_215 0x10D7 +#define WM8962_WRITE_SEQUENCER_216 0x10D8 +#define WM8962_WRITE_SEQUENCER_217 0x10D9 +#define WM8962_WRITE_SEQUENCER_218 0x10DA +#define WM8962_WRITE_SEQUENCER_219 0x10DB +#define WM8962_WRITE_SEQUENCER_220 0x10DC +#define WM8962_WRITE_SEQUENCER_221 0x10DD +#define WM8962_WRITE_SEQUENCER_222 0x10DE +#define WM8962_WRITE_SEQUENCER_223 0x10DF +#define WM8962_WRITE_SEQUENCER_224 0x10E0 +#define WM8962_WRITE_SEQUENCER_225 0x10E1 +#define WM8962_WRITE_SEQUENCER_226 0x10E2 +#define WM8962_WRITE_SEQUENCER_227 0x10E3 +#define WM8962_WRITE_SEQUENCER_228 0x10E4 +#define WM8962_WRITE_SEQUENCER_229 0x10E5 +#define WM8962_WRITE_SEQUENCER_230 0x10E6 +#define WM8962_WRITE_SEQUENCER_231 0x10E7 +#define WM8962_WRITE_SEQUENCER_232 0x10E8 +#define WM8962_WRITE_SEQUENCER_233 0x10E9 +#define WM8962_WRITE_SEQUENCER_234 0x10EA +#define WM8962_WRITE_SEQUENCER_235 0x10EB +#define WM8962_WRITE_SEQUENCER_236 0x10EC +#define WM8962_WRITE_SEQUENCER_237 0x10ED +#define WM8962_WRITE_SEQUENCER_238 0x10EE +#define WM8962_WRITE_SEQUENCER_239 0x10EF +#define WM8962_WRITE_SEQUENCER_240 0x10F0 +#define WM8962_WRITE_SEQUENCER_241 0x10F1 +#define WM8962_WRITE_SEQUENCER_242 0x10F2 +#define WM8962_WRITE_SEQUENCER_243 0x10F3 +#define WM8962_WRITE_SEQUENCER_244 0x10F4 +#define WM8962_WRITE_SEQUENCER_245 0x10F5 +#define WM8962_WRITE_SEQUENCER_246 0x10F6 +#define WM8962_WRITE_SEQUENCER_247 0x10F7 +#define WM8962_WRITE_SEQUENCER_248 0x10F8 +#define WM8962_WRITE_SEQUENCER_249 0x10F9 +#define WM8962_WRITE_SEQUENCER_250 0x10FA +#define WM8962_WRITE_SEQUENCER_251 0x10FB +#define WM8962_WRITE_SEQUENCER_252 0x10FC +#define WM8962_WRITE_SEQUENCER_253 0x10FD +#define WM8962_WRITE_SEQUENCER_254 0x10FE +#define WM8962_WRITE_SEQUENCER_255 0x10FF +#define WM8962_WRITE_SEQUENCER_256 0x1100 +#define WM8962_WRITE_SEQUENCER_257 0x1101 +#define WM8962_WRITE_SEQUENCER_258 0x1102 +#define WM8962_WRITE_SEQUENCER_259 0x1103 +#define WM8962_WRITE_SEQUENCER_260 0x1104 +#define WM8962_WRITE_SEQUENCER_261 0x1105 +#define WM8962_WRITE_SEQUENCER_262 0x1106 +#define WM8962_WRITE_SEQUENCER_263 0x1107 +#define WM8962_WRITE_SEQUENCER_264 0x1108 +#define WM8962_WRITE_SEQUENCER_265 0x1109 +#define WM8962_WRITE_SEQUENCER_266 0x110A +#define WM8962_WRITE_SEQUENCER_267 0x110B +#define WM8962_WRITE_SEQUENCER_268 0x110C +#define WM8962_WRITE_SEQUENCER_269 0x110D +#define WM8962_WRITE_SEQUENCER_270 0x110E +#define WM8962_WRITE_SEQUENCER_271 0x110F +#define WM8962_WRITE_SEQUENCER_272 0x1110 +#define WM8962_WRITE_SEQUENCER_273 0x1111 +#define WM8962_WRITE_SEQUENCER_274 0x1112 +#define WM8962_WRITE_SEQUENCER_275 0x1113 +#define WM8962_WRITE_SEQUENCER_276 0x1114 +#define WM8962_WRITE_SEQUENCER_277 0x1115 +#define WM8962_WRITE_SEQUENCER_278 0x1116 +#define WM8962_WRITE_SEQUENCER_279 0x1117 +#define WM8962_WRITE_SEQUENCER_280 0x1118 +#define WM8962_WRITE_SEQUENCER_281 0x1119 +#define WM8962_WRITE_SEQUENCER_282 0x111A +#define WM8962_WRITE_SEQUENCER_283 0x111B +#define WM8962_WRITE_SEQUENCER_284 0x111C +#define WM8962_WRITE_SEQUENCER_285 0x111D +#define WM8962_WRITE_SEQUENCER_286 0x111E +#define WM8962_WRITE_SEQUENCER_287 0x111F +#define WM8962_WRITE_SEQUENCER_288 0x1120 +#define WM8962_WRITE_SEQUENCER_289 0x1121 +#define WM8962_WRITE_SEQUENCER_290 0x1122 +#define WM8962_WRITE_SEQUENCER_291 0x1123 +#define WM8962_WRITE_SEQUENCER_292 0x1124 +#define WM8962_WRITE_SEQUENCER_293 0x1125 +#define WM8962_WRITE_SEQUENCER_294 0x1126 +#define WM8962_WRITE_SEQUENCER_295 0x1127 +#define WM8962_WRITE_SEQUENCER_296 0x1128 +#define WM8962_WRITE_SEQUENCER_297 0x1129 +#define WM8962_WRITE_SEQUENCER_298 0x112A +#define WM8962_WRITE_SEQUENCER_299 0x112B +#define WM8962_WRITE_SEQUENCER_300 0x112C +#define WM8962_WRITE_SEQUENCER_301 0x112D +#define WM8962_WRITE_SEQUENCER_302 0x112E +#define WM8962_WRITE_SEQUENCER_303 0x112F +#define WM8962_WRITE_SEQUENCER_304 0x1130 +#define WM8962_WRITE_SEQUENCER_305 0x1131 +#define WM8962_WRITE_SEQUENCER_306 0x1132 +#define WM8962_WRITE_SEQUENCER_307 0x1133 +#define WM8962_WRITE_SEQUENCER_308 0x1134 +#define WM8962_WRITE_SEQUENCER_309 0x1135 +#define WM8962_WRITE_SEQUENCER_310 0x1136 +#define WM8962_WRITE_SEQUENCER_311 0x1137 +#define WM8962_WRITE_SEQUENCER_312 0x1138 +#define WM8962_WRITE_SEQUENCER_313 0x1139 +#define WM8962_WRITE_SEQUENCER_314 0x113A +#define WM8962_WRITE_SEQUENCER_315 0x113B +#define WM8962_WRITE_SEQUENCER_316 0x113C +#define WM8962_WRITE_SEQUENCER_317 0x113D +#define WM8962_WRITE_SEQUENCER_318 0x113E +#define WM8962_WRITE_SEQUENCER_319 0x113F +#define WM8962_WRITE_SEQUENCER_320 0x1140 +#define WM8962_WRITE_SEQUENCER_321 0x1141 +#define WM8962_WRITE_SEQUENCER_322 0x1142 +#define WM8962_WRITE_SEQUENCER_323 0x1143 +#define WM8962_WRITE_SEQUENCER_324 0x1144 +#define WM8962_WRITE_SEQUENCER_325 0x1145 +#define WM8962_WRITE_SEQUENCER_326 0x1146 +#define WM8962_WRITE_SEQUENCER_327 0x1147 +#define WM8962_WRITE_SEQUENCER_328 0x1148 +#define WM8962_WRITE_SEQUENCER_329 0x1149 +#define WM8962_WRITE_SEQUENCER_330 0x114A +#define WM8962_WRITE_SEQUENCER_331 0x114B +#define WM8962_WRITE_SEQUENCER_332 0x114C +#define WM8962_WRITE_SEQUENCER_333 0x114D +#define WM8962_WRITE_SEQUENCER_334 0x114E +#define WM8962_WRITE_SEQUENCER_335 0x114F +#define WM8962_WRITE_SEQUENCER_336 0x1150 +#define WM8962_WRITE_SEQUENCER_337 0x1151 +#define WM8962_WRITE_SEQUENCER_338 0x1152 +#define WM8962_WRITE_SEQUENCER_339 0x1153 +#define WM8962_WRITE_SEQUENCER_340 0x1154 +#define WM8962_WRITE_SEQUENCER_341 0x1155 +#define WM8962_WRITE_SEQUENCER_342 0x1156 +#define WM8962_WRITE_SEQUENCER_343 0x1157 +#define WM8962_WRITE_SEQUENCER_344 0x1158 +#define WM8962_WRITE_SEQUENCER_345 0x1159 +#define WM8962_WRITE_SEQUENCER_346 0x115A +#define WM8962_WRITE_SEQUENCER_347 0x115B +#define WM8962_WRITE_SEQUENCER_348 0x115C +#define WM8962_WRITE_SEQUENCER_349 0x115D +#define WM8962_WRITE_SEQUENCER_350 0x115E +#define WM8962_WRITE_SEQUENCER_351 0x115F +#define WM8962_WRITE_SEQUENCER_352 0x1160 +#define WM8962_WRITE_SEQUENCER_353 0x1161 +#define WM8962_WRITE_SEQUENCER_354 0x1162 +#define WM8962_WRITE_SEQUENCER_355 0x1163 +#define WM8962_WRITE_SEQUENCER_356 0x1164 +#define WM8962_WRITE_SEQUENCER_357 0x1165 +#define WM8962_WRITE_SEQUENCER_358 0x1166 +#define WM8962_WRITE_SEQUENCER_359 0x1167 +#define WM8962_WRITE_SEQUENCER_360 0x1168 +#define WM8962_WRITE_SEQUENCER_361 0x1169 +#define WM8962_WRITE_SEQUENCER_362 0x116A +#define WM8962_WRITE_SEQUENCER_363 0x116B +#define WM8962_WRITE_SEQUENCER_364 0x116C +#define WM8962_WRITE_SEQUENCER_365 0x116D +#define WM8962_WRITE_SEQUENCER_366 0x116E +#define WM8962_WRITE_SEQUENCER_367 0x116F +#define WM8962_WRITE_SEQUENCER_368 0x1170 +#define WM8962_WRITE_SEQUENCER_369 0x1171 +#define WM8962_WRITE_SEQUENCER_370 0x1172 +#define WM8962_WRITE_SEQUENCER_371 0x1173 +#define WM8962_WRITE_SEQUENCER_372 0x1174 +#define WM8962_WRITE_SEQUENCER_373 0x1175 +#define WM8962_WRITE_SEQUENCER_374 0x1176 +#define WM8962_WRITE_SEQUENCER_375 0x1177 +#define WM8962_WRITE_SEQUENCER_376 0x1178 +#define WM8962_WRITE_SEQUENCER_377 0x1179 +#define WM8962_WRITE_SEQUENCER_378 0x117A +#define WM8962_WRITE_SEQUENCER_379 0x117B +#define WM8962_WRITE_SEQUENCER_380 0x117C +#define WM8962_WRITE_SEQUENCER_381 0x117D +#define WM8962_WRITE_SEQUENCER_382 0x117E +#define WM8962_WRITE_SEQUENCER_383 0x117F +#define WM8962_WRITE_SEQUENCER_384 0x1180 +#define WM8962_WRITE_SEQUENCER_385 0x1181 +#define WM8962_WRITE_SEQUENCER_386 0x1182 +#define WM8962_WRITE_SEQUENCER_387 0x1183 +#define WM8962_WRITE_SEQUENCER_388 0x1184 +#define WM8962_WRITE_SEQUENCER_389 0x1185 +#define WM8962_WRITE_SEQUENCER_390 0x1186 +#define WM8962_WRITE_SEQUENCER_391 0x1187 +#define WM8962_WRITE_SEQUENCER_392 0x1188 +#define WM8962_WRITE_SEQUENCER_393 0x1189 +#define WM8962_WRITE_SEQUENCER_394 0x118A +#define WM8962_WRITE_SEQUENCER_395 0x118B +#define WM8962_WRITE_SEQUENCER_396 0x118C +#define WM8962_WRITE_SEQUENCER_397 0x118D +#define WM8962_WRITE_SEQUENCER_398 0x118E +#define WM8962_WRITE_SEQUENCER_399 0x118F +#define WM8962_WRITE_SEQUENCER_400 0x1190 +#define WM8962_WRITE_SEQUENCER_401 0x1191 +#define WM8962_WRITE_SEQUENCER_402 0x1192 +#define WM8962_WRITE_SEQUENCER_403 0x1193 +#define WM8962_WRITE_SEQUENCER_404 0x1194 +#define WM8962_WRITE_SEQUENCER_405 0x1195 +#define WM8962_WRITE_SEQUENCER_406 0x1196 +#define WM8962_WRITE_SEQUENCER_407 0x1197 +#define WM8962_WRITE_SEQUENCER_408 0x1198 +#define WM8962_WRITE_SEQUENCER_409 0x1199 +#define WM8962_WRITE_SEQUENCER_410 0x119A +#define WM8962_WRITE_SEQUENCER_411 0x119B +#define WM8962_WRITE_SEQUENCER_412 0x119C +#define WM8962_WRITE_SEQUENCER_413 0x119D +#define WM8962_WRITE_SEQUENCER_414 0x119E +#define WM8962_WRITE_SEQUENCER_415 0x119F +#define WM8962_WRITE_SEQUENCER_416 0x11A0 +#define WM8962_WRITE_SEQUENCER_417 0x11A1 +#define WM8962_WRITE_SEQUENCER_418 0x11A2 +#define WM8962_WRITE_SEQUENCER_419 0x11A3 +#define WM8962_WRITE_SEQUENCER_420 0x11A4 +#define WM8962_WRITE_SEQUENCER_421 0x11A5 +#define WM8962_WRITE_SEQUENCER_422 0x11A6 +#define WM8962_WRITE_SEQUENCER_423 0x11A7 +#define WM8962_WRITE_SEQUENCER_424 0x11A8 +#define WM8962_WRITE_SEQUENCER_425 0x11A9 +#define WM8962_WRITE_SEQUENCER_426 0x11AA +#define WM8962_WRITE_SEQUENCER_427 0x11AB +#define WM8962_WRITE_SEQUENCER_428 0x11AC +#define WM8962_WRITE_SEQUENCER_429 0x11AD +#define WM8962_WRITE_SEQUENCER_430 0x11AE +#define WM8962_WRITE_SEQUENCER_431 0x11AF +#define WM8962_WRITE_SEQUENCER_432 0x11B0 +#define WM8962_WRITE_SEQUENCER_433 0x11B1 +#define WM8962_WRITE_SEQUENCER_434 0x11B2 +#define WM8962_WRITE_SEQUENCER_435 0x11B3 +#define WM8962_WRITE_SEQUENCER_436 0x11B4 +#define WM8962_WRITE_SEQUENCER_437 0x11B5 +#define WM8962_WRITE_SEQUENCER_438 0x11B6 +#define WM8962_WRITE_SEQUENCER_439 0x11B7 +#define WM8962_WRITE_SEQUENCER_440 0x11B8 +#define WM8962_WRITE_SEQUENCER_441 0x11B9 +#define WM8962_WRITE_SEQUENCER_442 0x11BA +#define WM8962_WRITE_SEQUENCER_443 0x11BB +#define WM8962_WRITE_SEQUENCER_444 0x11BC +#define WM8962_WRITE_SEQUENCER_445 0x11BD +#define WM8962_WRITE_SEQUENCER_446 0x11BE +#define WM8962_WRITE_SEQUENCER_447 0x11BF +#define WM8962_WRITE_SEQUENCER_448 0x11C0 +#define WM8962_WRITE_SEQUENCER_449 0x11C1 +#define WM8962_WRITE_SEQUENCER_450 0x11C2 +#define WM8962_WRITE_SEQUENCER_451 0x11C3 +#define WM8962_WRITE_SEQUENCER_452 0x11C4 +#define WM8962_WRITE_SEQUENCER_453 0x11C5 +#define WM8962_WRITE_SEQUENCER_454 0x11C6 +#define WM8962_WRITE_SEQUENCER_455 0x11C7 +#define WM8962_WRITE_SEQUENCER_456 0x11C8 +#define WM8962_WRITE_SEQUENCER_457 0x11C9 +#define WM8962_WRITE_SEQUENCER_458 0x11CA +#define WM8962_WRITE_SEQUENCER_459 0x11CB +#define WM8962_WRITE_SEQUENCER_460 0x11CC +#define WM8962_WRITE_SEQUENCER_461 0x11CD +#define WM8962_WRITE_SEQUENCER_462 0x11CE +#define WM8962_WRITE_SEQUENCER_463 0x11CF +#define WM8962_WRITE_SEQUENCER_464 0x11D0 +#define WM8962_WRITE_SEQUENCER_465 0x11D1 +#define WM8962_WRITE_SEQUENCER_466 0x11D2 +#define WM8962_WRITE_SEQUENCER_467 0x11D3 +#define WM8962_WRITE_SEQUENCER_468 0x11D4 +#define WM8962_WRITE_SEQUENCER_469 0x11D5 +#define WM8962_WRITE_SEQUENCER_470 0x11D6 +#define WM8962_WRITE_SEQUENCER_471 0x11D7 +#define WM8962_WRITE_SEQUENCER_472 0x11D8 +#define WM8962_WRITE_SEQUENCER_473 0x11D9 +#define WM8962_WRITE_SEQUENCER_474 0x11DA +#define WM8962_WRITE_SEQUENCER_475 0x11DB +#define WM8962_WRITE_SEQUENCER_476 0x11DC +#define WM8962_WRITE_SEQUENCER_477 0x11DD +#define WM8962_WRITE_SEQUENCER_478 0x11DE +#define WM8962_WRITE_SEQUENCER_479 0x11DF +#define WM8962_WRITE_SEQUENCER_480 0x11E0 +#define WM8962_WRITE_SEQUENCER_481 0x11E1 +#define WM8962_WRITE_SEQUENCER_482 0x11E2 +#define WM8962_WRITE_SEQUENCER_483 0x11E3 +#define WM8962_WRITE_SEQUENCER_484 0x11E4 +#define WM8962_WRITE_SEQUENCER_485 0x11E5 +#define WM8962_WRITE_SEQUENCER_486 0x11E6 +#define WM8962_WRITE_SEQUENCER_487 0x11E7 +#define WM8962_WRITE_SEQUENCER_488 0x11E8 +#define WM8962_WRITE_SEQUENCER_489 0x11E9 +#define WM8962_WRITE_SEQUENCER_490 0x11EA +#define WM8962_WRITE_SEQUENCER_491 0x11EB +#define WM8962_WRITE_SEQUENCER_492 0x11EC +#define WM8962_WRITE_SEQUENCER_493 0x11ED +#define WM8962_WRITE_SEQUENCER_494 0x11EE +#define WM8962_WRITE_SEQUENCER_495 0x11EF +#define WM8962_WRITE_SEQUENCER_496 0x11F0 +#define WM8962_WRITE_SEQUENCER_497 0x11F1 +#define WM8962_WRITE_SEQUENCER_498 0x11F2 +#define WM8962_WRITE_SEQUENCER_499 0x11F3 +#define WM8962_WRITE_SEQUENCER_500 0x11F4 +#define WM8962_WRITE_SEQUENCER_501 0x11F5 +#define WM8962_WRITE_SEQUENCER_502 0x11F6 +#define WM8962_WRITE_SEQUENCER_503 0x11F7 +#define WM8962_WRITE_SEQUENCER_504 0x11F8 +#define WM8962_WRITE_SEQUENCER_505 0x11F9 +#define WM8962_WRITE_SEQUENCER_506 0x11FA +#define WM8962_WRITE_SEQUENCER_507 0x11FB +#define WM8962_WRITE_SEQUENCER_508 0x11FC +#define WM8962_WRITE_SEQUENCER_509 0x11FD +#define WM8962_WRITE_SEQUENCER_510 0x11FE +#define WM8962_WRITE_SEQUENCER_511 0x11FF +#define WM8962_DSP2_INSTRUCTION_RAM_0 0x2000 +#define WM8962_DSP2_ADDRESS_RAM_2 0x2400 +#define WM8962_DSP2_ADDRESS_RAM_1 0x2401 +#define WM8962_DSP2_ADDRESS_RAM_0 0x2402 +#define WM8962_DSP2_DATA1_RAM_1 0x3000 +#define WM8962_DSP2_DATA1_RAM_0 0x3001 +#define WM8962_DSP2_DATA2_RAM_1 0x3400 +#define WM8962_DSP2_DATA2_RAM_0 0x3401 +#define WM8962_DSP2_DATA3_RAM_1 0x3800 +#define WM8962_DSP2_DATA3_RAM_0 0x3801 +#define WM8962_DSP2_COEFF_RAM_0 0x3C00 +#define WM8962_RETUNEADC_SHARED_COEFF_1 0x4000 +#define WM8962_RETUNEADC_SHARED_COEFF_0 0x4001 +#define WM8962_RETUNEDAC_SHARED_COEFF_1 0x4002 +#define WM8962_RETUNEDAC_SHARED_COEFF_0 0x4003 +#define WM8962_SOUNDSTAGE_ENABLES_1 0x4004 +#define WM8962_SOUNDSTAGE_ENABLES_0 0x4005 +#define WM8962_HDBASS_AI_1 0x4200 +#define WM8962_HDBASS_AI_0 0x4201 +#define WM8962_HDBASS_AR_1 0x4202 +#define WM8962_HDBASS_AR_0 0x4203 +#define WM8962_HDBASS_B_1 0x4204 +#define WM8962_HDBASS_B_0 0x4205 +#define WM8962_HDBASS_K_1 0x4206 +#define WM8962_HDBASS_K_0 0x4207 +#define WM8962_HDBASS_N1_1 0x4208 +#define WM8962_HDBASS_N1_0 0x4209 +#define WM8962_HDBASS_N2_1 0x420A +#define WM8962_HDBASS_N2_0 0x420B +#define WM8962_HDBASS_N3_1 0x420C +#define WM8962_HDBASS_N3_0 0x420D +#define WM8962_HDBASS_N4_1 0x420E +#define WM8962_HDBASS_N4_0 0x420F +#define WM8962_HDBASS_N5_1 0x4210 +#define WM8962_HDBASS_N5_0 0x4211 +#define WM8962_HDBASS_X1_1 0x4212 +#define WM8962_HDBASS_X1_0 0x4213 +#define WM8962_HDBASS_X2_1 0x4214 +#define WM8962_HDBASS_X2_0 0x4215 +#define WM8962_HDBASS_X3_1 0x4216 +#define WM8962_HDBASS_X3_0 0x4217 +#define WM8962_HDBASS_ATK_1 0x4218 +#define WM8962_HDBASS_ATK_0 0x4219 +#define WM8962_HDBASS_DCY_1 0x421A +#define WM8962_HDBASS_DCY_0 0x421B +#define WM8962_HDBASS_PG_1 0x421C +#define WM8962_HDBASS_PG_0 0x421D +#define WM8962_HPF_C_1 0x4400 +#define WM8962_HPF_C_0 0x4401 +#define WM8962_ADCL_RETUNE_C1_1 0x4600 +#define WM8962_ADCL_RETUNE_C1_0 0x4601 +#define WM8962_ADCL_RETUNE_C2_1 0x4602 +#define WM8962_ADCL_RETUNE_C2_0 0x4603 +#define WM8962_ADCL_RETUNE_C3_1 0x4604 +#define WM8962_ADCL_RETUNE_C3_0 0x4605 +#define WM8962_ADCL_RETUNE_C4_1 0x4606 +#define WM8962_ADCL_RETUNE_C4_0 0x4607 +#define WM8962_ADCL_RETUNE_C5_1 0x4608 +#define WM8962_ADCL_RETUNE_C5_0 0x4609 +#define WM8962_ADCL_RETUNE_C6_1 0x460A +#define WM8962_ADCL_RETUNE_C6_0 0x460B +#define WM8962_ADCL_RETUNE_C7_1 0x460C +#define WM8962_ADCL_RETUNE_C7_0 0x460D +#define WM8962_ADCL_RETUNE_C8_1 0x460E +#define WM8962_ADCL_RETUNE_C8_0 0x460F +#define WM8962_ADCL_RETUNE_C9_1 0x4610 +#define WM8962_ADCL_RETUNE_C9_0 0x4611 +#define WM8962_ADCL_RETUNE_C10_1 0x4612 +#define WM8962_ADCL_RETUNE_C10_0 0x4613 +#define WM8962_ADCL_RETUNE_C11_1 0x4614 +#define WM8962_ADCL_RETUNE_C11_0 0x4615 +#define WM8962_ADCL_RETUNE_C12_1 0x4616 +#define WM8962_ADCL_RETUNE_C12_0 0x4617 +#define WM8962_ADCL_RETUNE_C13_1 0x4618 +#define WM8962_ADCL_RETUNE_C13_0 0x4619 +#define WM8962_ADCL_RETUNE_C14_1 0x461A +#define WM8962_ADCL_RETUNE_C14_0 0x461B +#define WM8962_ADCL_RETUNE_C15_1 0x461C +#define WM8962_ADCL_RETUNE_C15_0 0x461D +#define WM8962_ADCL_RETUNE_C16_1 0x461E +#define WM8962_ADCL_RETUNE_C16_0 0x461F +#define WM8962_ADCL_RETUNE_C17_1 0x4620 +#define WM8962_ADCL_RETUNE_C17_0 0x4621 +#define WM8962_ADCL_RETUNE_C18_1 0x4622 +#define WM8962_ADCL_RETUNE_C18_0 0x4623 +#define WM8962_ADCL_RETUNE_C19_1 0x4624 +#define WM8962_ADCL_RETUNE_C19_0 0x4625 +#define WM8962_ADCL_RETUNE_C20_1 0x4626 +#define WM8962_ADCL_RETUNE_C20_0 0x4627 +#define WM8962_ADCL_RETUNE_C21_1 0x4628 +#define WM8962_ADCL_RETUNE_C21_0 0x4629 +#define WM8962_ADCL_RETUNE_C22_1 0x462A +#define WM8962_ADCL_RETUNE_C22_0 0x462B +#define WM8962_ADCL_RETUNE_C23_1 0x462C +#define WM8962_ADCL_RETUNE_C23_0 0x462D +#define WM8962_ADCL_RETUNE_C24_1 0x462E +#define WM8962_ADCL_RETUNE_C24_0 0x462F +#define WM8962_ADCL_RETUNE_C25_1 0x4630 +#define WM8962_ADCL_RETUNE_C25_0 0x4631 +#define WM8962_ADCL_RETUNE_C26_1 0x4632 +#define WM8962_ADCL_RETUNE_C26_0 0x4633 +#define WM8962_ADCL_RETUNE_C27_1 0x4634 +#define WM8962_ADCL_RETUNE_C27_0 0x4635 +#define WM8962_ADCL_RETUNE_C28_1 0x4636 +#define WM8962_ADCL_RETUNE_C28_0 0x4637 +#define WM8962_ADCL_RETUNE_C29_1 0x4638 +#define WM8962_ADCL_RETUNE_C29_0 0x4639 +#define WM8962_ADCL_RETUNE_C30_1 0x463A +#define WM8962_ADCL_RETUNE_C30_0 0x463B +#define WM8962_ADCL_RETUNE_C31_1 0x463C +#define WM8962_ADCL_RETUNE_C31_0 0x463D +#define WM8962_ADCL_RETUNE_C32_1 0x463E +#define WM8962_ADCL_RETUNE_C32_0 0x463F +#define WM8962_RETUNEADC_PG2_1 0x4800 +#define WM8962_RETUNEADC_PG2_0 0x4801 +#define WM8962_RETUNEADC_PG_1 0x4802 +#define WM8962_RETUNEADC_PG_0 0x4803 +#define WM8962_ADCR_RETUNE_C1_1 0x4A00 +#define WM8962_ADCR_RETUNE_C1_0 0x4A01 +#define WM8962_ADCR_RETUNE_C2_1 0x4A02 +#define WM8962_ADCR_RETUNE_C2_0 0x4A03 +#define WM8962_ADCR_RETUNE_C3_1 0x4A04 +#define WM8962_ADCR_RETUNE_C3_0 0x4A05 +#define WM8962_ADCR_RETUNE_C4_1 0x4A06 +#define WM8962_ADCR_RETUNE_C4_0 0x4A07 +#define WM8962_ADCR_RETUNE_C5_1 0x4A08 +#define WM8962_ADCR_RETUNE_C5_0 0x4A09 +#define WM8962_ADCR_RETUNE_C6_1 0x4A0A +#define WM8962_ADCR_RETUNE_C6_0 0x4A0B +#define WM8962_ADCR_RETUNE_C7_1 0x4A0C +#define WM8962_ADCR_RETUNE_C7_0 0x4A0D +#define WM8962_ADCR_RETUNE_C8_1 0x4A0E +#define WM8962_ADCR_RETUNE_C8_0 0x4A0F +#define WM8962_ADCR_RETUNE_C9_1 0x4A10 +#define WM8962_ADCR_RETUNE_C9_0 0x4A11 +#define WM8962_ADCR_RETUNE_C10_1 0x4A12 +#define WM8962_ADCR_RETUNE_C10_0 0x4A13 +#define WM8962_ADCR_RETUNE_C11_1 0x4A14 +#define WM8962_ADCR_RETUNE_C11_0 0x4A15 +#define WM8962_ADCR_RETUNE_C12_1 0x4A16 +#define WM8962_ADCR_RETUNE_C12_0 0x4A17 +#define WM8962_ADCR_RETUNE_C13_1 0x4A18 +#define WM8962_ADCR_RETUNE_C13_0 0x4A19 +#define WM8962_ADCR_RETUNE_C14_1 0x4A1A +#define WM8962_ADCR_RETUNE_C14_0 0x4A1B +#define WM8962_ADCR_RETUNE_C15_1 0x4A1C +#define WM8962_ADCR_RETUNE_C15_0 0x4A1D +#define WM8962_ADCR_RETUNE_C16_1 0x4A1E +#define WM8962_ADCR_RETUNE_C16_0 0x4A1F +#define WM8962_ADCR_RETUNE_C17_1 0x4A20 +#define WM8962_ADCR_RETUNE_C17_0 0x4A21 +#define WM8962_ADCR_RETUNE_C18_1 0x4A22 +#define WM8962_ADCR_RETUNE_C18_0 0x4A23 +#define WM8962_ADCR_RETUNE_C19_1 0x4A24 +#define WM8962_ADCR_RETUNE_C19_0 0x4A25 +#define WM8962_ADCR_RETUNE_C20_1 0x4A26 +#define WM8962_ADCR_RETUNE_C20_0 0x4A27 +#define WM8962_ADCR_RETUNE_C21_1 0x4A28 +#define WM8962_ADCR_RETUNE_C21_0 0x4A29 +#define WM8962_ADCR_RETUNE_C22_1 0x4A2A +#define WM8962_ADCR_RETUNE_C22_0 0x4A2B +#define WM8962_ADCR_RETUNE_C23_1 0x4A2C +#define WM8962_ADCR_RETUNE_C23_0 0x4A2D +#define WM8962_ADCR_RETUNE_C24_1 0x4A2E +#define WM8962_ADCR_RETUNE_C24_0 0x4A2F +#define WM8962_ADCR_RETUNE_C25_1 0x4A30 +#define WM8962_ADCR_RETUNE_C25_0 0x4A31 +#define WM8962_ADCR_RETUNE_C26_1 0x4A32 +#define WM8962_ADCR_RETUNE_C26_0 0x4A33 +#define WM8962_ADCR_RETUNE_C27_1 0x4A34 +#define WM8962_ADCR_RETUNE_C27_0 0x4A35 +#define WM8962_ADCR_RETUNE_C28_1 0x4A36 +#define WM8962_ADCR_RETUNE_C28_0 0x4A37 +#define WM8962_ADCR_RETUNE_C29_1 0x4A38 +#define WM8962_ADCR_RETUNE_C29_0 0x4A39 +#define WM8962_ADCR_RETUNE_C30_1 0x4A3A +#define WM8962_ADCR_RETUNE_C30_0 0x4A3B +#define WM8962_ADCR_RETUNE_C31_1 0x4A3C +#define WM8962_ADCR_RETUNE_C31_0 0x4A3D +#define WM8962_ADCR_RETUNE_C32_1 0x4A3E +#define WM8962_ADCR_RETUNE_C32_0 0x4A3F +#define WM8962_DACL_RETUNE_C1_1 0x4C00 +#define WM8962_DACL_RETUNE_C1_0 0x4C01 +#define WM8962_DACL_RETUNE_C2_1 0x4C02 +#define WM8962_DACL_RETUNE_C2_0 0x4C03 +#define WM8962_DACL_RETUNE_C3_1 0x4C04 +#define WM8962_DACL_RETUNE_C3_0 0x4C05 +#define WM8962_DACL_RETUNE_C4_1 0x4C06 +#define WM8962_DACL_RETUNE_C4_0 0x4C07 +#define WM8962_DACL_RETUNE_C5_1 0x4C08 +#define WM8962_DACL_RETUNE_C5_0 0x4C09 +#define WM8962_DACL_RETUNE_C6_1 0x4C0A +#define WM8962_DACL_RETUNE_C6_0 0x4C0B +#define WM8962_DACL_RETUNE_C7_1 0x4C0C +#define WM8962_DACL_RETUNE_C7_0 0x4C0D +#define WM8962_DACL_RETUNE_C8_1 0x4C0E +#define WM8962_DACL_RETUNE_C8_0 0x4C0F +#define WM8962_DACL_RETUNE_C9_1 0x4C10 +#define WM8962_DACL_RETUNE_C9_0 0x4C11 +#define WM8962_DACL_RETUNE_C10_1 0x4C12 +#define WM8962_DACL_RETUNE_C10_0 0x4C13 +#define WM8962_DACL_RETUNE_C11_1 0x4C14 +#define WM8962_DACL_RETUNE_C11_0 0x4C15 +#define WM8962_DACL_RETUNE_C12_1 0x4C16 +#define WM8962_DACL_RETUNE_C12_0 0x4C17 +#define WM8962_DACL_RETUNE_C13_1 0x4C18 +#define WM8962_DACL_RETUNE_C13_0 0x4C19 +#define WM8962_DACL_RETUNE_C14_1 0x4C1A +#define WM8962_DACL_RETUNE_C14_0 0x4C1B +#define WM8962_DACL_RETUNE_C15_1 0x4C1C +#define WM8962_DACL_RETUNE_C15_0 0x4C1D +#define WM8962_DACL_RETUNE_C16_1 0x4C1E +#define WM8962_DACL_RETUNE_C16_0 0x4C1F +#define WM8962_DACL_RETUNE_C17_1 0x4C20 +#define WM8962_DACL_RETUNE_C17_0 0x4C21 +#define WM8962_DACL_RETUNE_C18_1 0x4C22 +#define WM8962_DACL_RETUNE_C18_0 0x4C23 +#define WM8962_DACL_RETUNE_C19_1 0x4C24 +#define WM8962_DACL_RETUNE_C19_0 0x4C25 +#define WM8962_DACL_RETUNE_C20_1 0x4C26 +#define WM8962_DACL_RETUNE_C20_0 0x4C27 +#define WM8962_DACL_RETUNE_C21_1 0x4C28 +#define WM8962_DACL_RETUNE_C21_0 0x4C29 +#define WM8962_DACL_RETUNE_C22_1 0x4C2A +#define WM8962_DACL_RETUNE_C22_0 0x4C2B +#define WM8962_DACL_RETUNE_C23_1 0x4C2C +#define WM8962_DACL_RETUNE_C23_0 0x4C2D +#define WM8962_DACL_RETUNE_C24_1 0x4C2E +#define WM8962_DACL_RETUNE_C24_0 0x4C2F +#define WM8962_DACL_RETUNE_C25_1 0x4C30 +#define WM8962_DACL_RETUNE_C25_0 0x4C31 +#define WM8962_DACL_RETUNE_C26_1 0x4C32 +#define WM8962_DACL_RETUNE_C26_0 0x4C33 +#define WM8962_DACL_RETUNE_C27_1 0x4C34 +#define WM8962_DACL_RETUNE_C27_0 0x4C35 +#define WM8962_DACL_RETUNE_C28_1 0x4C36 +#define WM8962_DACL_RETUNE_C28_0 0x4C37 +#define WM8962_DACL_RETUNE_C29_1 0x4C38 +#define WM8962_DACL_RETUNE_C29_0 0x4C39 +#define WM8962_DACL_RETUNE_C30_1 0x4C3A +#define WM8962_DACL_RETUNE_C30_0 0x4C3B +#define WM8962_DACL_RETUNE_C31_1 0x4C3C +#define WM8962_DACL_RETUNE_C31_0 0x4C3D +#define WM8962_DACL_RETUNE_C32_1 0x4C3E +#define WM8962_DACL_RETUNE_C32_0 0x4C3F +#define WM8962_RETUNEDAC_PG2_1 0x4E00 +#define WM8962_RETUNEDAC_PG2_0 0x4E01 +#define WM8962_RETUNEDAC_PG_1 0x4E02 +#define WM8962_RETUNEDAC_PG_0 0x4E03 +#define WM8962_DACR_RETUNE_C1_1 0x5000 +#define WM8962_DACR_RETUNE_C1_0 0x5001 +#define WM8962_DACR_RETUNE_C2_1 0x5002 +#define WM8962_DACR_RETUNE_C2_0 0x5003 +#define WM8962_DACR_RETUNE_C3_1 0x5004 +#define WM8962_DACR_RETUNE_C3_0 0x5005 +#define WM8962_DACR_RETUNE_C4_1 0x5006 +#define WM8962_DACR_RETUNE_C4_0 0x5007 +#define WM8962_DACR_RETUNE_C5_1 0x5008 +#define WM8962_DACR_RETUNE_C5_0 0x5009 +#define WM8962_DACR_RETUNE_C6_1 0x500A +#define WM8962_DACR_RETUNE_C6_0 0x500B +#define WM8962_DACR_RETUNE_C7_1 0x500C +#define WM8962_DACR_RETUNE_C7_0 0x500D +#define WM8962_DACR_RETUNE_C8_1 0x500E +#define WM8962_DACR_RETUNE_C8_0 0x500F +#define WM8962_DACR_RETUNE_C9_1 0x5010 +#define WM8962_DACR_RETUNE_C9_0 0x5011 +#define WM8962_DACR_RETUNE_C10_1 0x5012 +#define WM8962_DACR_RETUNE_C10_0 0x5013 +#define WM8962_DACR_RETUNE_C11_1 0x5014 +#define WM8962_DACR_RETUNE_C11_0 0x5015 +#define WM8962_DACR_RETUNE_C12_1 0x5016 +#define WM8962_DACR_RETUNE_C12_0 0x5017 +#define WM8962_DACR_RETUNE_C13_1 0x5018 +#define WM8962_DACR_RETUNE_C13_0 0x5019 +#define WM8962_DACR_RETUNE_C14_1 0x501A +#define WM8962_DACR_RETUNE_C14_0 0x501B +#define WM8962_DACR_RETUNE_C15_1 0x501C +#define WM8962_DACR_RETUNE_C15_0 0x501D +#define WM8962_DACR_RETUNE_C16_1 0x501E +#define WM8962_DACR_RETUNE_C16_0 0x501F +#define WM8962_DACR_RETUNE_C17_1 0x5020 +#define WM8962_DACR_RETUNE_C17_0 0x5021 +#define WM8962_DACR_RETUNE_C18_1 0x5022 +#define WM8962_DACR_RETUNE_C18_0 0x5023 +#define WM8962_DACR_RETUNE_C19_1 0x5024 +#define WM8962_DACR_RETUNE_C19_0 0x5025 +#define WM8962_DACR_RETUNE_C20_1 0x5026 +#define WM8962_DACR_RETUNE_C20_0 0x5027 +#define WM8962_DACR_RETUNE_C21_1 0x5028 +#define WM8962_DACR_RETUNE_C21_0 0x5029 +#define WM8962_DACR_RETUNE_C22_1 0x502A +#define WM8962_DACR_RETUNE_C22_0 0x502B +#define WM8962_DACR_RETUNE_C23_1 0x502C +#define WM8962_DACR_RETUNE_C23_0 0x502D +#define WM8962_DACR_RETUNE_C24_1 0x502E +#define WM8962_DACR_RETUNE_C24_0 0x502F +#define WM8962_DACR_RETUNE_C25_1 0x5030 +#define WM8962_DACR_RETUNE_C25_0 0x5031 +#define WM8962_DACR_RETUNE_C26_1 0x5032 +#define WM8962_DACR_RETUNE_C26_0 0x5033 +#define WM8962_DACR_RETUNE_C27_1 0x5034 +#define WM8962_DACR_RETUNE_C27_0 0x5035 +#define WM8962_DACR_RETUNE_C28_1 0x5036 +#define WM8962_DACR_RETUNE_C28_0 0x5037 +#define WM8962_DACR_RETUNE_C29_1 0x5038 +#define WM8962_DACR_RETUNE_C29_0 0x5039 +#define WM8962_DACR_RETUNE_C30_1 0x503A +#define WM8962_DACR_RETUNE_C30_0 0x503B +#define WM8962_DACR_RETUNE_C31_1 0x503C +#define WM8962_DACR_RETUNE_C31_0 0x503D +#define WM8962_DACR_RETUNE_C32_1 0x503E +#define WM8962_DACR_RETUNE_C32_0 0x503F +#define WM8962_VSS_XHD2_1 0x5200 +#define WM8962_VSS_XHD2_0 0x5201 +#define WM8962_VSS_XHD3_1 0x5202 +#define WM8962_VSS_XHD3_0 0x5203 +#define WM8962_VSS_XHN1_1 0x5204 +#define WM8962_VSS_XHN1_0 0x5205 +#define WM8962_VSS_XHN2_1 0x5206 +#define WM8962_VSS_XHN2_0 0x5207 +#define WM8962_VSS_XHN3_1 0x5208 +#define WM8962_VSS_XHN3_0 0x5209 +#define WM8962_VSS_XLA_1 0x520A +#define WM8962_VSS_XLA_0 0x520B +#define WM8962_VSS_XLB_1 0x520C +#define WM8962_VSS_XLB_0 0x520D +#define WM8962_VSS_XLG_1 0x520E +#define WM8962_VSS_XLG_0 0x520F +#define WM8962_VSS_PG2_1 0x5210 +#define WM8962_VSS_PG2_0 0x5211 +#define WM8962_VSS_PG_1 0x5212 +#define WM8962_VSS_PG_0 0x5213 +#define WM8962_VSS_XTD1_1 0x5214 +#define WM8962_VSS_XTD1_0 0x5215 +#define WM8962_VSS_XTD2_1 0x5216 +#define WM8962_VSS_XTD2_0 0x5217 +#define WM8962_VSS_XTD3_1 0x5218 +#define WM8962_VSS_XTD3_0 0x5219 +#define WM8962_VSS_XTD4_1 0x521A +#define WM8962_VSS_XTD4_0 0x521B +#define WM8962_VSS_XTD5_1 0x521C +#define WM8962_VSS_XTD5_0 0x521D +#define WM8962_VSS_XTD6_1 0x521E +#define WM8962_VSS_XTD6_0 0x521F +#define WM8962_VSS_XTD7_1 0x5220 +#define WM8962_VSS_XTD7_0 0x5221 +#define WM8962_VSS_XTD8_1 0x5222 +#define WM8962_VSS_XTD8_0 0x5223 +#define WM8962_VSS_XTD9_1 0x5224 +#define WM8962_VSS_XTD9_0 0x5225 +#define WM8962_VSS_XTD10_1 0x5226 +#define WM8962_VSS_XTD10_0 0x5227 +#define WM8962_VSS_XTD11_1 0x5228 +#define WM8962_VSS_XTD11_0 0x5229 +#define WM8962_VSS_XTD12_1 0x522A +#define WM8962_VSS_XTD12_0 0x522B +#define WM8962_VSS_XTD13_1 0x522C +#define WM8962_VSS_XTD13_0 0x522D +#define WM8962_VSS_XTD14_1 0x522E +#define WM8962_VSS_XTD14_0 0x522F +#define WM8962_VSS_XTD15_1 0x5230 +#define WM8962_VSS_XTD15_0 0x5231 +#define WM8962_VSS_XTD16_1 0x5232 +#define WM8962_VSS_XTD16_0 0x5233 +#define WM8962_VSS_XTD17_1 0x5234 +#define WM8962_VSS_XTD17_0 0x5235 +#define WM8962_VSS_XTD18_1 0x5236 +#define WM8962_VSS_XTD18_0 0x5237 +#define WM8962_VSS_XTD19_1 0x5238 +#define WM8962_VSS_XTD19_0 0x5239 +#define WM8962_VSS_XTD20_1 0x523A +#define WM8962_VSS_XTD20_0 0x523B +#define WM8962_VSS_XTD21_1 0x523C +#define WM8962_VSS_XTD21_0 0x523D +#define WM8962_VSS_XTD22_1 0x523E +#define WM8962_VSS_XTD22_0 0x523F +#define WM8962_VSS_XTD23_1 0x5240 +#define WM8962_VSS_XTD23_0 0x5241 +#define WM8962_VSS_XTD24_1 0x5242 +#define WM8962_VSS_XTD24_0 0x5243 +#define WM8962_VSS_XTD25_1 0x5244 +#define WM8962_VSS_XTD25_0 0x5245 +#define WM8962_VSS_XTD26_1 0x5246 +#define WM8962_VSS_XTD26_0 0x5247 +#define WM8962_VSS_XTD27_1 0x5248 +#define WM8962_VSS_XTD27_0 0x5249 +#define WM8962_VSS_XTD28_1 0x524A +#define WM8962_VSS_XTD28_0 0x524B +#define WM8962_VSS_XTD29_1 0x524C +#define WM8962_VSS_XTD29_0 0x524D +#define WM8962_VSS_XTD30_1 0x524E +#define WM8962_VSS_XTD30_0 0x524F +#define WM8962_VSS_XTD31_1 0x5250 +#define WM8962_VSS_XTD31_0 0x5251 +#define WM8962_VSS_XTD32_1 0x5252 +#define WM8962_VSS_XTD32_0 0x5253 +#define WM8962_VSS_XTS1_1 0x5254 +#define WM8962_VSS_XTS1_0 0x5255 +#define WM8962_VSS_XTS2_1 0x5256 +#define WM8962_VSS_XTS2_0 0x5257 +#define WM8962_VSS_XTS3_1 0x5258 +#define WM8962_VSS_XTS3_0 0x5259 +#define WM8962_VSS_XTS4_1 0x525A +#define WM8962_VSS_XTS4_0 0x525B +#define WM8962_VSS_XTS5_1 0x525C +#define WM8962_VSS_XTS5_0 0x525D +#define WM8962_VSS_XTS6_1 0x525E +#define WM8962_VSS_XTS6_0 0x525F +#define WM8962_VSS_XTS7_1 0x5260 +#define WM8962_VSS_XTS7_0 0x5261 +#define WM8962_VSS_XTS8_1 0x5262 +#define WM8962_VSS_XTS8_0 0x5263 +#define WM8962_VSS_XTS9_1 0x5264 +#define WM8962_VSS_XTS9_0 0x5265 +#define WM8962_VSS_XTS10_1 0x5266 +#define WM8962_VSS_XTS10_0 0x5267 +#define WM8962_VSS_XTS11_1 0x5268 +#define WM8962_VSS_XTS11_0 0x5269 +#define WM8962_VSS_XTS12_1 0x526A +#define WM8962_VSS_XTS12_0 0x526B +#define WM8962_VSS_XTS13_1 0x526C +#define WM8962_VSS_XTS13_0 0x526D +#define WM8962_VSS_XTS14_1 0x526E +#define WM8962_VSS_XTS14_0 0x526F +#define WM8962_VSS_XTS15_1 0x5270 +#define WM8962_VSS_XTS15_0 0x5271 +#define WM8962_VSS_XTS16_1 0x5272 +#define WM8962_VSS_XTS16_0 0x5273 +#define WM8962_VSS_XTS17_1 0x5274 +#define WM8962_VSS_XTS17_0 0x5275 +#define WM8962_VSS_XTS18_1 0x5276 +#define WM8962_VSS_XTS18_0 0x5277 +#define WM8962_VSS_XTS19_1 0x5278 +#define WM8962_VSS_XTS19_0 0x5279 +#define WM8962_VSS_XTS20_1 0x527A +#define WM8962_VSS_XTS20_0 0x527B +#define WM8962_VSS_XTS21_1 0x527C +#define WM8962_VSS_XTS21_0 0x527D +#define WM8962_VSS_XTS22_1 0x527E +#define WM8962_VSS_XTS22_0 0x527F +#define WM8962_VSS_XTS23_1 0x5280 +#define WM8962_VSS_XTS23_0 0x5281 +#define WM8962_VSS_XTS24_1 0x5282 +#define WM8962_VSS_XTS24_0 0x5283 +#define WM8962_VSS_XTS25_1 0x5284 +#define WM8962_VSS_XTS25_0 0x5285 +#define WM8962_VSS_XTS26_1 0x5286 +#define WM8962_VSS_XTS26_0 0x5287 +#define WM8962_VSS_XTS27_1 0x5288 +#define WM8962_VSS_XTS27_0 0x5289 +#define WM8962_VSS_XTS28_1 0x528A +#define WM8962_VSS_XTS28_0 0x528B +#define WM8962_VSS_XTS29_1 0x528C +#define WM8962_VSS_XTS29_0 0x528D +#define WM8962_VSS_XTS30_1 0x528E +#define WM8962_VSS_XTS30_0 0x528F +#define WM8962_VSS_XTS31_1 0x5290 +#define WM8962_VSS_XTS31_0 0x5291 +#define WM8962_VSS_XTS32_1 0x5292 +#define WM8962_VSS_XTS32_0 0x5293 + +#define WM8962_REGISTER_COUNT 1138 +#define WM8962_MAX_REGISTER 0x5293 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Left Input volume + */ +#define WM8962_IN_VU 0x0100 /* IN_VU */ +#define WM8962_IN_VU_MASK 0x0100 /* IN_VU */ +#define WM8962_IN_VU_SHIFT 8 /* IN_VU */ +#define WM8962_IN_VU_WIDTH 1 /* IN_VU */ +#define WM8962_INPGAL_MUTE 0x0080 /* INPGAL_MUTE */ +#define WM8962_INPGAL_MUTE_MASK 0x0080 /* INPGAL_MUTE */ +#define WM8962_INPGAL_MUTE_SHIFT 7 /* INPGAL_MUTE */ +#define WM8962_INPGAL_MUTE_WIDTH 1 /* INPGAL_MUTE */ +#define WM8962_INL_ZC 0x0040 /* INL_ZC */ +#define WM8962_INL_ZC_MASK 0x0040 /* INL_ZC */ +#define WM8962_INL_ZC_SHIFT 6 /* INL_ZC */ +#define WM8962_INL_ZC_WIDTH 1 /* INL_ZC */ +#define WM8962_INL_VOL_MASK 0x003F /* INL_VOL - [5:0] */ +#define WM8962_INL_VOL_SHIFT 0 /* INL_VOL - [5:0] */ +#define WM8962_INL_VOL_WIDTH 6 /* INL_VOL - [5:0] */ + +/* + * R1 (0x01) - Right Input volume + */ +#define WM8962_CUST_ID_MASK 0xF000 /* CUST_ID - [15:12] */ +#define WM8962_CUST_ID_SHIFT 12 /* CUST_ID - [15:12] */ +#define WM8962_CUST_ID_WIDTH 4 /* CUST_ID - [15:12] */ +#define WM8962_CHIP_REV_MASK 0x0E00 /* CHIP_REV - [11:9] */ +#define WM8962_CHIP_REV_SHIFT 9 /* CHIP_REV - [11:9] */ +#define WM8962_CHIP_REV_WIDTH 3 /* CHIP_REV - [11:9] */ +#define WM8962_IN_VU 0x0100 /* IN_VU */ +#define WM8962_IN_VU_MASK 0x0100 /* IN_VU */ +#define WM8962_IN_VU_SHIFT 8 /* IN_VU */ +#define WM8962_IN_VU_WIDTH 1 /* IN_VU */ +#define WM8962_INPGAR_MUTE 0x0080 /* INPGAR_MUTE */ +#define WM8962_INPGAR_MUTE_MASK 0x0080 /* INPGAR_MUTE */ +#define WM8962_INPGAR_MUTE_SHIFT 7 /* INPGAR_MUTE */ +#define WM8962_INPGAR_MUTE_WIDTH 1 /* INPGAR_MUTE */ +#define WM8962_INR_ZC 0x0040 /* INR_ZC */ +#define WM8962_INR_ZC_MASK 0x0040 /* INR_ZC */ +#define WM8962_INR_ZC_SHIFT 6 /* INR_ZC */ +#define WM8962_INR_ZC_WIDTH 1 /* INR_ZC */ +#define WM8962_INR_VOL_MASK 0x003F /* INR_VOL - [5:0] */ +#define WM8962_INR_VOL_SHIFT 0 /* INR_VOL - [5:0] */ +#define WM8962_INR_VOL_WIDTH 6 /* INR_VOL - [5:0] */ + +/* + * R2 (0x02) - HPOUTL volume + */ +#define WM8962_HPOUT_VU 0x0100 /* HPOUT_VU */ +#define WM8962_HPOUT_VU_MASK 0x0100 /* HPOUT_VU */ +#define WM8962_HPOUT_VU_SHIFT 8 /* HPOUT_VU */ +#define WM8962_HPOUT_VU_WIDTH 1 /* HPOUT_VU */ +#define WM8962_HPOUTL_ZC 0x0080 /* HPOUTL_ZC */ +#define WM8962_HPOUTL_ZC_MASK 0x0080 /* HPOUTL_ZC */ +#define WM8962_HPOUTL_ZC_SHIFT 7 /* HPOUTL_ZC */ +#define WM8962_HPOUTL_ZC_WIDTH 1 /* HPOUTL_ZC */ +#define WM8962_HPOUTL_VOL_MASK 0x007F /* HPOUTL_VOL - [6:0] */ +#define WM8962_HPOUTL_VOL_SHIFT 0 /* HPOUTL_VOL - [6:0] */ +#define WM8962_HPOUTL_VOL_WIDTH 7 /* HPOUTL_VOL - [6:0] */ + +/* + * R3 (0x03) - HPOUTR volume + */ +#define WM8962_HPOUT_VU 0x0100 /* HPOUT_VU */ +#define WM8962_HPOUT_VU_MASK 0x0100 /* HPOUT_VU */ +#define WM8962_HPOUT_VU_SHIFT 8 /* HPOUT_VU */ +#define WM8962_HPOUT_VU_WIDTH 1 /* HPOUT_VU */ +#define WM8962_HPOUTR_ZC 0x0080 /* HPOUTR_ZC */ +#define WM8962_HPOUTR_ZC_MASK 0x0080 /* HPOUTR_ZC */ +#define WM8962_HPOUTR_ZC_SHIFT 7 /* HPOUTR_ZC */ +#define WM8962_HPOUTR_ZC_WIDTH 1 /* HPOUTR_ZC */ +#define WM8962_HPOUTR_VOL_MASK 0x007F /* HPOUTR_VOL - [6:0] */ +#define WM8962_HPOUTR_VOL_SHIFT 0 /* HPOUTR_VOL - [6:0] */ +#define WM8962_HPOUTR_VOL_WIDTH 7 /* HPOUTR_VOL - [6:0] */ + +/* + * R4 (0x04) - Clocking1 + */ +#define WM8962_DSPCLK_DIV_MASK 0x0600 /* DSPCLK_DIV - [10:9] */ +#define WM8962_DSPCLK_DIV_SHIFT 9 /* DSPCLK_DIV - [10:9] */ +#define WM8962_DSPCLK_DIV_WIDTH 2 /* DSPCLK_DIV - [10:9] */ +#define WM8962_ADCSYS_CLK_DIV_MASK 0x01C0 /* ADCSYS_CLK_DIV - [8:6] */ +#define WM8962_ADCSYS_CLK_DIV_SHIFT 6 /* ADCSYS_CLK_DIV - [8:6] */ +#define WM8962_ADCSYS_CLK_DIV_WIDTH 3 /* ADCSYS_CLK_DIV - [8:6] */ +#define WM8962_DACSYS_CLK_DIV_MASK 0x0038 /* DACSYS_CLK_DIV - [5:3] */ +#define WM8962_DACSYS_CLK_DIV_SHIFT 3 /* DACSYS_CLK_DIV - [5:3] */ +#define WM8962_DACSYS_CLK_DIV_WIDTH 3 /* DACSYS_CLK_DIV - [5:3] */ +#define WM8962_MCLKDIV_MASK 0x0006 /* MCLKDIV - [2:1] */ +#define WM8962_MCLKDIV_SHIFT 1 /* MCLKDIV - [2:1] */ +#define WM8962_MCLKDIV_WIDTH 2 /* MCLKDIV - [2:1] */ + +/* + * R5 (0x05) - ADC & DAC Control 1 + */ +#define WM8962_ADCR_DAT_INV 0x0040 /* ADCR_DAT_INV */ +#define WM8962_ADCR_DAT_INV_MASK 0x0040 /* ADCR_DAT_INV */ +#define WM8962_ADCR_DAT_INV_SHIFT 6 /* ADCR_DAT_INV */ +#define WM8962_ADCR_DAT_INV_WIDTH 1 /* ADCR_DAT_INV */ +#define WM8962_ADCL_DAT_INV 0x0020 /* ADCL_DAT_INV */ +#define WM8962_ADCL_DAT_INV_MASK 0x0020 /* ADCL_DAT_INV */ +#define WM8962_ADCL_DAT_INV_SHIFT 5 /* ADCL_DAT_INV */ +#define WM8962_ADCL_DAT_INV_WIDTH 1 /* ADCL_DAT_INV */ +#define WM8962_DAC_MUTE_RAMP 0x0010 /* DAC_MUTE_RAMP */ +#define WM8962_DAC_MUTE_RAMP_MASK 0x0010 /* DAC_MUTE_RAMP */ +#define WM8962_DAC_MUTE_RAMP_SHIFT 4 /* DAC_MUTE_RAMP */ +#define WM8962_DAC_MUTE_RAMP_WIDTH 1 /* DAC_MUTE_RAMP */ +#define WM8962_DAC_MUTE 0x0008 /* DAC_MUTE */ +#define WM8962_DAC_MUTE_MASK 0x0008 /* DAC_MUTE */ +#define WM8962_DAC_MUTE_SHIFT 3 /* DAC_MUTE */ +#define WM8962_DAC_MUTE_WIDTH 1 /* DAC_MUTE */ +#define WM8962_DAC_DEEMP_MASK 0x0006 /* DAC_DEEMP - [2:1] */ +#define WM8962_DAC_DEEMP_SHIFT 1 /* DAC_DEEMP - [2:1] */ +#define WM8962_DAC_DEEMP_WIDTH 2 /* DAC_DEEMP - [2:1] */ +#define WM8962_ADC_HPF_DIS 0x0001 /* ADC_HPF_DIS */ +#define WM8962_ADC_HPF_DIS_MASK 0x0001 /* ADC_HPF_DIS */ +#define WM8962_ADC_HPF_DIS_SHIFT 0 /* ADC_HPF_DIS */ +#define WM8962_ADC_HPF_DIS_WIDTH 1 /* ADC_HPF_DIS */ + +/* + * R6 (0x06) - ADC & DAC Control 2 + */ +#define WM8962_ADC_HPF_SR_MASK 0x3000 /* ADC_HPF_SR - [13:12] */ +#define WM8962_ADC_HPF_SR_SHIFT 12 /* ADC_HPF_SR - [13:12] */ +#define WM8962_ADC_HPF_SR_WIDTH 2 /* ADC_HPF_SR - [13:12] */ +#define WM8962_ADC_HPF_MODE 0x0400 /* ADC_HPF_MODE */ +#define WM8962_ADC_HPF_MODE_MASK 0x0400 /* ADC_HPF_MODE */ +#define WM8962_ADC_HPF_MODE_SHIFT 10 /* ADC_HPF_MODE */ +#define WM8962_ADC_HPF_MODE_WIDTH 1 /* ADC_HPF_MODE */ +#define WM8962_ADC_HPF_CUT_MASK 0x0380 /* ADC_HPF_CUT - [9:7] */ +#define WM8962_ADC_HPF_CUT_SHIFT 7 /* ADC_HPF_CUT - [9:7] */ +#define WM8962_ADC_HPF_CUT_WIDTH 3 /* ADC_HPF_CUT - [9:7] */ +#define WM8962_DACR_DAT_INV 0x0040 /* DACR_DAT_INV */ +#define WM8962_DACR_DAT_INV_MASK 0x0040 /* DACR_DAT_INV */ +#define WM8962_DACR_DAT_INV_SHIFT 6 /* DACR_DAT_INV */ +#define WM8962_DACR_DAT_INV_WIDTH 1 /* DACR_DAT_INV */ +#define WM8962_DACL_DAT_INV 0x0020 /* DACL_DAT_INV */ +#define WM8962_DACL_DAT_INV_MASK 0x0020 /* DACL_DAT_INV */ +#define WM8962_DACL_DAT_INV_SHIFT 5 /* DACL_DAT_INV */ +#define WM8962_DACL_DAT_INV_WIDTH 1 /* DACL_DAT_INV */ +#define WM8962_DAC_UNMUTE_RAMP 0x0008 /* DAC_UNMUTE_RAMP */ +#define WM8962_DAC_UNMUTE_RAMP_MASK 0x0008 /* DAC_UNMUTE_RAMP */ +#define WM8962_DAC_UNMUTE_RAMP_SHIFT 3 /* DAC_UNMUTE_RAMP */ +#define WM8962_DAC_UNMUTE_RAMP_WIDTH 1 /* DAC_UNMUTE_RAMP */ +#define WM8962_DAC_MUTERATE 0x0004 /* DAC_MUTERATE */ +#define WM8962_DAC_MUTERATE_MASK 0x0004 /* DAC_MUTERATE */ +#define WM8962_DAC_MUTERATE_SHIFT 2 /* DAC_MUTERATE */ +#define WM8962_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ +#define WM8962_DAC_HP 0x0001 /* DAC_HP */ +#define WM8962_DAC_HP_MASK 0x0001 /* DAC_HP */ +#define WM8962_DAC_HP_SHIFT 0 /* DAC_HP */ +#define WM8962_DAC_HP_WIDTH 1 /* DAC_HP */ + +/* + * R7 (0x07) - Audio Interface 0 + */ +#define WM8962_AIFDAC_TDM_MODE 0x1000 /* AIFDAC_TDM_MODE */ +#define WM8962_AIFDAC_TDM_MODE_MASK 0x1000 /* AIFDAC_TDM_MODE */ +#define WM8962_AIFDAC_TDM_MODE_SHIFT 12 /* AIFDAC_TDM_MODE */ +#define WM8962_AIFDAC_TDM_MODE_WIDTH 1 /* AIFDAC_TDM_MODE */ +#define WM8962_AIFDAC_TDM_SLOT 0x0800 /* AIFDAC_TDM_SLOT */ +#define WM8962_AIFDAC_TDM_SLOT_MASK 0x0800 /* AIFDAC_TDM_SLOT */ +#define WM8962_AIFDAC_TDM_SLOT_SHIFT 11 /* AIFDAC_TDM_SLOT */ +#define WM8962_AIFDAC_TDM_SLOT_WIDTH 1 /* AIFDAC_TDM_SLOT */ +#define WM8962_AIFADC_TDM_MODE 0x0400 /* AIFADC_TDM_MODE */ +#define WM8962_AIFADC_TDM_MODE_MASK 0x0400 /* AIFADC_TDM_MODE */ +#define WM8962_AIFADC_TDM_MODE_SHIFT 10 /* AIFADC_TDM_MODE */ +#define WM8962_AIFADC_TDM_MODE_WIDTH 1 /* AIFADC_TDM_MODE */ +#define WM8962_AIFADC_TDM_SLOT 0x0200 /* AIFADC_TDM_SLOT */ +#define WM8962_AIFADC_TDM_SLOT_MASK 0x0200 /* AIFADC_TDM_SLOT */ +#define WM8962_AIFADC_TDM_SLOT_SHIFT 9 /* AIFADC_TDM_SLOT */ +#define WM8962_AIFADC_TDM_SLOT_WIDTH 1 /* AIFADC_TDM_SLOT */ +#define WM8962_ADC_LRSWAP 0x0100 /* ADC_LRSWAP */ +#define WM8962_ADC_LRSWAP_MASK 0x0100 /* ADC_LRSWAP */ +#define WM8962_ADC_LRSWAP_SHIFT 8 /* ADC_LRSWAP */ +#define WM8962_ADC_LRSWAP_WIDTH 1 /* ADC_LRSWAP */ +#define WM8962_BCLK_INV 0x0080 /* BCLK_INV */ +#define WM8962_BCLK_INV_MASK 0x0080 /* BCLK_INV */ +#define WM8962_BCLK_INV_SHIFT 7 /* BCLK_INV */ +#define WM8962_BCLK_INV_WIDTH 1 /* BCLK_INV */ +#define WM8962_MSTR 0x0040 /* MSTR */ +#define WM8962_MSTR_MASK 0x0040 /* MSTR */ +#define WM8962_MSTR_SHIFT 6 /* MSTR */ +#define WM8962_MSTR_WIDTH 1 /* MSTR */ +#define WM8962_DAC_LRSWAP 0x0020 /* DAC_LRSWAP */ +#define WM8962_DAC_LRSWAP_MASK 0x0020 /* DAC_LRSWAP */ +#define WM8962_DAC_LRSWAP_SHIFT 5 /* DAC_LRSWAP */ +#define WM8962_DAC_LRSWAP_WIDTH 1 /* DAC_LRSWAP */ +#define WM8962_LRCLK_INV 0x0010 /* LRCLK_INV */ +#define WM8962_LRCLK_INV_MASK 0x0010 /* LRCLK_INV */ +#define WM8962_LRCLK_INV_SHIFT 4 /* LRCLK_INV */ +#define WM8962_LRCLK_INV_WIDTH 1 /* LRCLK_INV */ +#define WM8962_WL_MASK 0x000C /* WL - [3:2] */ +#define WM8962_WL_SHIFT 2 /* WL - [3:2] */ +#define WM8962_WL_WIDTH 2 /* WL - [3:2] */ +#define WM8962_FMT_MASK 0x0003 /* FMT - [1:0] */ +#define WM8962_FMT_SHIFT 0 /* FMT - [1:0] */ +#define WM8962_FMT_WIDTH 2 /* FMT - [1:0] */ + +/* + * R8 (0x08) - Clocking2 + */ +#define WM8962_CLKREG_OVD 0x0800 /* CLKREG_OVD */ +#define WM8962_CLKREG_OVD_MASK 0x0800 /* CLKREG_OVD */ +#define WM8962_CLKREG_OVD_SHIFT 11 /* CLKREG_OVD */ +#define WM8962_CLKREG_OVD_WIDTH 1 /* CLKREG_OVD */ +#define WM8962_SYSCLK_SRC_MASK 0x0600 /* SYSCLK_SRC - [10:9] */ +#define WM8962_SYSCLK_SRC_SHIFT 9 /* SYSCLK_SRC - [10:9] */ +#define WM8962_SYSCLK_SRC_WIDTH 2 /* SYSCLK_SRC - [10:9] */ +#define WM8962_CLASSD_CLK_DIV_MASK 0x01C0 /* CLASSD_CLK_DIV - [8:6] */ +#define WM8962_CLASSD_CLK_DIV_SHIFT 6 /* CLASSD_CLK_DIV - [8:6] */ +#define WM8962_CLASSD_CLK_DIV_WIDTH 3 /* CLASSD_CLK_DIV - [8:6] */ +#define WM8962_SYSCLK_ENA 0x0020 /* SYSCLK_ENA */ +#define WM8962_SYSCLK_ENA_MASK 0x0020 /* SYSCLK_ENA */ +#define WM8962_SYSCLK_ENA_SHIFT 5 /* SYSCLK_ENA */ +#define WM8962_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */ +#define WM8962_BCLK_DIV_MASK 0x000F /* BCLK_DIV - [3:0] */ +#define WM8962_BCLK_DIV_SHIFT 0 /* BCLK_DIV - [3:0] */ +#define WM8962_BCLK_DIV_WIDTH 4 /* BCLK_DIV - [3:0] */ + +/* + * R9 (0x09) - Audio Interface 1 + */ +#define WM8962_AUTOMUTE_STS 0x0800 /* AUTOMUTE_STS */ +#define WM8962_AUTOMUTE_STS_MASK 0x0800 /* AUTOMUTE_STS */ +#define WM8962_AUTOMUTE_STS_SHIFT 11 /* AUTOMUTE_STS */ +#define WM8962_AUTOMUTE_STS_WIDTH 1 /* AUTOMUTE_STS */ +#define WM8962_DAC_AUTOMUTE_SAMPLES_MASK 0x0300 /* DAC_AUTOMUTE_SAMPLES - [9:8] */ +#define WM8962_DAC_AUTOMUTE_SAMPLES_SHIFT 8 /* DAC_AUTOMUTE_SAMPLES - [9:8] */ +#define WM8962_DAC_AUTOMUTE_SAMPLES_WIDTH 2 /* DAC_AUTOMUTE_SAMPLES - [9:8] */ +#define WM8962_DAC_AUTOMUTE 0x0080 /* DAC_AUTOMUTE */ +#define WM8962_DAC_AUTOMUTE_MASK 0x0080 /* DAC_AUTOMUTE */ +#define WM8962_DAC_AUTOMUTE_SHIFT 7 /* DAC_AUTOMUTE */ +#define WM8962_DAC_AUTOMUTE_WIDTH 1 /* DAC_AUTOMUTE */ +#define WM8962_DAC_COMP 0x0010 /* DAC_COMP */ +#define WM8962_DAC_COMP_MASK 0x0010 /* DAC_COMP */ +#define WM8962_DAC_COMP_SHIFT 4 /* DAC_COMP */ +#define WM8962_DAC_COMP_WIDTH 1 /* DAC_COMP */ +#define WM8962_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */ +#define WM8962_DAC_COMPMODE_MASK 0x0008 /* DAC_COMPMODE */ +#define WM8962_DAC_COMPMODE_SHIFT 3 /* DAC_COMPMODE */ +#define WM8962_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */ +#define WM8962_ADC_COMP 0x0004 /* ADC_COMP */ +#define WM8962_ADC_COMP_MASK 0x0004 /* ADC_COMP */ +#define WM8962_ADC_COMP_SHIFT 2 /* ADC_COMP */ +#define WM8962_ADC_COMP_WIDTH 1 /* ADC_COMP */ +#define WM8962_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */ +#define WM8962_ADC_COMPMODE_MASK 0x0002 /* ADC_COMPMODE */ +#define WM8962_ADC_COMPMODE_SHIFT 1 /* ADC_COMPMODE */ +#define WM8962_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */ +#define WM8962_LOOPBACK 0x0001 /* LOOPBACK */ +#define WM8962_LOOPBACK_MASK 0x0001 /* LOOPBACK */ +#define WM8962_LOOPBACK_SHIFT 0 /* LOOPBACK */ +#define WM8962_LOOPBACK_WIDTH 1 /* LOOPBACK */ + +/* + * R10 (0x0A) - Left DAC volume + */ +#define WM8962_DAC_VU 0x0100 /* DAC_VU */ +#define WM8962_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8962_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8962_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8962_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8962_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */ +#define WM8962_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */ + +/* + * R11 (0x0B) - Right DAC volume + */ +#define WM8962_DAC_VU 0x0100 /* DAC_VU */ +#define WM8962_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8962_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8962_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8962_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8962_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */ +#define WM8962_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */ + +/* + * R14 (0x0E) - Audio Interface 2 + */ +#define WM8962_AIF_RATE_MASK 0x07FF /* AIF_RATE - [10:0] */ +#define WM8962_AIF_RATE_SHIFT 0 /* AIF_RATE - [10:0] */ +#define WM8962_AIF_RATE_WIDTH 11 /* AIF_RATE - [10:0] */ + +/* + * R15 (0x0F) - Software Reset + */ +#define WM8962_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */ +#define WM8962_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */ +#define WM8962_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */ + +/* + * R17 (0x11) - ALC1 + */ +#define WM8962_ALC_INACTIVE_ENA 0x0400 /* ALC_INACTIVE_ENA */ +#define WM8962_ALC_INACTIVE_ENA_MASK 0x0400 /* ALC_INACTIVE_ENA */ +#define WM8962_ALC_INACTIVE_ENA_SHIFT 10 /* ALC_INACTIVE_ENA */ +#define WM8962_ALC_INACTIVE_ENA_WIDTH 1 /* ALC_INACTIVE_ENA */ +#define WM8962_ALC_LVL_MODE 0x0200 /* ALC_LVL_MODE */ +#define WM8962_ALC_LVL_MODE_MASK 0x0200 /* ALC_LVL_MODE */ +#define WM8962_ALC_LVL_MODE_SHIFT 9 /* ALC_LVL_MODE */ +#define WM8962_ALC_LVL_MODE_WIDTH 1 /* ALC_LVL_MODE */ +#define WM8962_ALCL_ENA 0x0100 /* ALCL_ENA */ +#define WM8962_ALCL_ENA_MASK 0x0100 /* ALCL_ENA */ +#define WM8962_ALCL_ENA_SHIFT 8 /* ALCL_ENA */ +#define WM8962_ALCL_ENA_WIDTH 1 /* ALCL_ENA */ +#define WM8962_ALCR_ENA 0x0080 /* ALCR_ENA */ +#define WM8962_ALCR_ENA_MASK 0x0080 /* ALCR_ENA */ +#define WM8962_ALCR_ENA_SHIFT 7 /* ALCR_ENA */ +#define WM8962_ALCR_ENA_WIDTH 1 /* ALCR_ENA */ +#define WM8962_ALC_MAXGAIN_MASK 0x0070 /* ALC_MAXGAIN - [6:4] */ +#define WM8962_ALC_MAXGAIN_SHIFT 4 /* ALC_MAXGAIN - [6:4] */ +#define WM8962_ALC_MAXGAIN_WIDTH 3 /* ALC_MAXGAIN - [6:4] */ +#define WM8962_ALC_LVL_MASK 0x000F /* ALC_LVL - [3:0] */ +#define WM8962_ALC_LVL_SHIFT 0 /* ALC_LVL - [3:0] */ +#define WM8962_ALC_LVL_WIDTH 4 /* ALC_LVL - [3:0] */ + +/* + * R18 (0x12) - ALC2 + */ +#define WM8962_ALC_LOCK_STS 0x8000 /* ALC_LOCK_STS */ +#define WM8962_ALC_LOCK_STS_MASK 0x8000 /* ALC_LOCK_STS */ +#define WM8962_ALC_LOCK_STS_SHIFT 15 /* ALC_LOCK_STS */ +#define WM8962_ALC_LOCK_STS_WIDTH 1 /* ALC_LOCK_STS */ +#define WM8962_ALC_THRESH_STS 0x4000 /* ALC_THRESH_STS */ +#define WM8962_ALC_THRESH_STS_MASK 0x4000 /* ALC_THRESH_STS */ +#define WM8962_ALC_THRESH_STS_SHIFT 14 /* ALC_THRESH_STS */ +#define WM8962_ALC_THRESH_STS_WIDTH 1 /* ALC_THRESH_STS */ +#define WM8962_ALC_SAT_STS 0x2000 /* ALC_SAT_STS */ +#define WM8962_ALC_SAT_STS_MASK 0x2000 /* ALC_SAT_STS */ +#define WM8962_ALC_SAT_STS_SHIFT 13 /* ALC_SAT_STS */ +#define WM8962_ALC_SAT_STS_WIDTH 1 /* ALC_SAT_STS */ +#define WM8962_ALC_PKOVR_STS 0x1000 /* ALC_PKOVR_STS */ +#define WM8962_ALC_PKOVR_STS_MASK 0x1000 /* ALC_PKOVR_STS */ +#define WM8962_ALC_PKOVR_STS_SHIFT 12 /* ALC_PKOVR_STS */ +#define WM8962_ALC_PKOVR_STS_WIDTH 1 /* ALC_PKOVR_STS */ +#define WM8962_ALC_NGATE_STS 0x0800 /* ALC_NGATE_STS */ +#define WM8962_ALC_NGATE_STS_MASK 0x0800 /* ALC_NGATE_STS */ +#define WM8962_ALC_NGATE_STS_SHIFT 11 /* ALC_NGATE_STS */ +#define WM8962_ALC_NGATE_STS_WIDTH 1 /* ALC_NGATE_STS */ +#define WM8962_ALC_ZC 0x0080 /* ALC_ZC */ +#define WM8962_ALC_ZC_MASK 0x0080 /* ALC_ZC */ +#define WM8962_ALC_ZC_SHIFT 7 /* ALC_ZC */ +#define WM8962_ALC_ZC_WIDTH 1 /* ALC_ZC */ +#define WM8962_ALC_MINGAIN_MASK 0x0070 /* ALC_MINGAIN - [6:4] */ +#define WM8962_ALC_MINGAIN_SHIFT 4 /* ALC_MINGAIN - [6:4] */ +#define WM8962_ALC_MINGAIN_WIDTH 3 /* ALC_MINGAIN - [6:4] */ +#define WM8962_ALC_HLD_MASK 0x000F /* ALC_HLD - [3:0] */ +#define WM8962_ALC_HLD_SHIFT 0 /* ALC_HLD - [3:0] */ +#define WM8962_ALC_HLD_WIDTH 4 /* ALC_HLD - [3:0] */ + +/* + * R19 (0x13) - ALC3 + */ +#define WM8962_ALC_NGATE_GAIN_MASK 0x1C00 /* ALC_NGATE_GAIN - [12:10] */ +#define WM8962_ALC_NGATE_GAIN_SHIFT 10 /* ALC_NGATE_GAIN - [12:10] */ +#define WM8962_ALC_NGATE_GAIN_WIDTH 3 /* ALC_NGATE_GAIN - [12:10] */ +#define WM8962_ALC_MODE 0x0100 /* ALC_MODE */ +#define WM8962_ALC_MODE_MASK 0x0100 /* ALC_MODE */ +#define WM8962_ALC_MODE_SHIFT 8 /* ALC_MODE */ +#define WM8962_ALC_MODE_WIDTH 1 /* ALC_MODE */ +#define WM8962_ALC_DCY_MASK 0x00F0 /* ALC_DCY - [7:4] */ +#define WM8962_ALC_DCY_SHIFT 4 /* ALC_DCY - [7:4] */ +#define WM8962_ALC_DCY_WIDTH 4 /* ALC_DCY - [7:4] */ +#define WM8962_ALC_ATK_MASK 0x000F /* ALC_ATK - [3:0] */ +#define WM8962_ALC_ATK_SHIFT 0 /* ALC_ATK - [3:0] */ +#define WM8962_ALC_ATK_WIDTH 4 /* ALC_ATK - [3:0] */ + +/* + * R20 (0x14) - Noise Gate + */ +#define WM8962_ALC_NGATE_DCY_MASK 0xF000 /* ALC_NGATE_DCY - [15:12] */ +#define WM8962_ALC_NGATE_DCY_SHIFT 12 /* ALC_NGATE_DCY - [15:12] */ +#define WM8962_ALC_NGATE_DCY_WIDTH 4 /* ALC_NGATE_DCY - [15:12] */ +#define WM8962_ALC_NGATE_ATK_MASK 0x0F00 /* ALC_NGATE_ATK - [11:8] */ +#define WM8962_ALC_NGATE_ATK_SHIFT 8 /* ALC_NGATE_ATK - [11:8] */ +#define WM8962_ALC_NGATE_ATK_WIDTH 4 /* ALC_NGATE_ATK - [11:8] */ +#define WM8962_ALC_NGATE_THR_MASK 0x00F8 /* ALC_NGATE_THR - [7:3] */ +#define WM8962_ALC_NGATE_THR_SHIFT 3 /* ALC_NGATE_THR - [7:3] */ +#define WM8962_ALC_NGATE_THR_WIDTH 5 /* ALC_NGATE_THR - [7:3] */ +#define WM8962_ALC_NGATE_MODE_MASK 0x0006 /* ALC_NGATE_MODE - [2:1] */ +#define WM8962_ALC_NGATE_MODE_SHIFT 1 /* ALC_NGATE_MODE - [2:1] */ +#define WM8962_ALC_NGATE_MODE_WIDTH 2 /* ALC_NGATE_MODE - [2:1] */ +#define WM8962_ALC_NGATE_ENA 0x0001 /* ALC_NGATE_ENA */ +#define WM8962_ALC_NGATE_ENA_MASK 0x0001 /* ALC_NGATE_ENA */ +#define WM8962_ALC_NGATE_ENA_SHIFT 0 /* ALC_NGATE_ENA */ +#define WM8962_ALC_NGATE_ENA_WIDTH 1 /* ALC_NGATE_ENA */ + +/* + * R21 (0x15) - Left ADC volume + */ +#define WM8962_ADC_VU 0x0100 /* ADC_VU */ +#define WM8962_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8962_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8962_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8962_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8962_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */ +#define WM8962_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */ + +/* + * R22 (0x16) - Right ADC volume + */ +#define WM8962_ADC_VU 0x0100 /* ADC_VU */ +#define WM8962_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8962_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8962_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8962_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8962_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */ +#define WM8962_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */ + +/* + * R23 (0x17) - Additional control(1) + */ +#define WM8962_THERR_ACT 0x0100 /* THERR_ACT */ +#define WM8962_THERR_ACT_MASK 0x0100 /* THERR_ACT */ +#define WM8962_THERR_ACT_SHIFT 8 /* THERR_ACT */ +#define WM8962_THERR_ACT_WIDTH 1 /* THERR_ACT */ +#define WM8962_ADC_BIAS 0x0040 /* ADC_BIAS */ +#define WM8962_ADC_BIAS_MASK 0x0040 /* ADC_BIAS */ +#define WM8962_ADC_BIAS_SHIFT 6 /* ADC_BIAS */ +#define WM8962_ADC_BIAS_WIDTH 1 /* ADC_BIAS */ +#define WM8962_ADC_HP 0x0020 /* ADC_HP */ +#define WM8962_ADC_HP_MASK 0x0020 /* ADC_HP */ +#define WM8962_ADC_HP_SHIFT 5 /* ADC_HP */ +#define WM8962_ADC_HP_WIDTH 1 /* ADC_HP */ +#define WM8962_TOCLK_ENA 0x0001 /* TOCLK_ENA */ +#define WM8962_TOCLK_ENA_MASK 0x0001 /* TOCLK_ENA */ +#define WM8962_TOCLK_ENA_SHIFT 0 /* TOCLK_ENA */ +#define WM8962_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */ + +/* + * R24 (0x18) - Additional control(2) + */ +#define WM8962_AIF_TRI 0x0008 /* AIF_TRI */ +#define WM8962_AIF_TRI_MASK 0x0008 /* AIF_TRI */ +#define WM8962_AIF_TRI_SHIFT 3 /* AIF_TRI */ +#define WM8962_AIF_TRI_WIDTH 1 /* AIF_TRI */ + +/* + * R25 (0x19) - Pwr Mgmt (1) + */ +#define WM8962_DMIC_ENA 0x0400 /* DMIC_ENA */ +#define WM8962_DMIC_ENA_MASK 0x0400 /* DMIC_ENA */ +#define WM8962_DMIC_ENA_SHIFT 10 /* DMIC_ENA */ +#define WM8962_DMIC_ENA_WIDTH 1 /* DMIC_ENA */ +#define WM8962_OPCLK_ENA 0x0200 /* OPCLK_ENA */ +#define WM8962_OPCLK_ENA_MASK 0x0200 /* OPCLK_ENA */ +#define WM8962_OPCLK_ENA_SHIFT 9 /* OPCLK_ENA */ +#define WM8962_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define WM8962_VMID_SEL_MASK 0x0180 /* VMID_SEL - [8:7] */ +#define WM8962_VMID_SEL_SHIFT 7 /* VMID_SEL - [8:7] */ +#define WM8962_VMID_SEL_WIDTH 2 /* VMID_SEL - [8:7] */ +#define WM8962_BIAS_ENA 0x0040 /* BIAS_ENA */ +#define WM8962_BIAS_ENA_MASK 0x0040 /* BIAS_ENA */ +#define WM8962_BIAS_ENA_SHIFT 6 /* BIAS_ENA */ +#define WM8962_BIAS_ENA_WIDTH 1 /* BIAS_ENA */ +#define WM8962_INL_ENA 0x0020 /* INL_ENA */ +#define WM8962_INL_ENA_MASK 0x0020 /* INL_ENA */ +#define WM8962_INL_ENA_SHIFT 5 /* INL_ENA */ +#define WM8962_INL_ENA_WIDTH 1 /* INL_ENA */ +#define WM8962_INR_ENA 0x0010 /* INR_ENA */ +#define WM8962_INR_ENA_MASK 0x0010 /* INR_ENA */ +#define WM8962_INR_ENA_SHIFT 4 /* INR_ENA */ +#define WM8962_INR_ENA_WIDTH 1 /* INR_ENA */ +#define WM8962_ADCL_ENA 0x0008 /* ADCL_ENA */ +#define WM8962_ADCL_ENA_MASK 0x0008 /* ADCL_ENA */ +#define WM8962_ADCL_ENA_SHIFT 3 /* ADCL_ENA */ +#define WM8962_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ +#define WM8962_ADCR_ENA 0x0004 /* ADCR_ENA */ +#define WM8962_ADCR_ENA_MASK 0x0004 /* ADCR_ENA */ +#define WM8962_ADCR_ENA_SHIFT 2 /* ADCR_ENA */ +#define WM8962_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ +#define WM8962_MICBIAS_ENA 0x0002 /* MICBIAS_ENA */ +#define WM8962_MICBIAS_ENA_MASK 0x0002 /* MICBIAS_ENA */ +#define WM8962_MICBIAS_ENA_SHIFT 1 /* MICBIAS_ENA */ +#define WM8962_MICBIAS_ENA_WIDTH 1 /* MICBIAS_ENA */ + +/* + * R26 (0x1A) - Pwr Mgmt (2) + */ +#define WM8962_DACL_ENA 0x0100 /* DACL_ENA */ +#define WM8962_DACL_ENA_MASK 0x0100 /* DACL_ENA */ +#define WM8962_DACL_ENA_SHIFT 8 /* DACL_ENA */ +#define WM8962_DACL_ENA_WIDTH 1 /* DACL_ENA */ +#define WM8962_DACR_ENA 0x0080 /* DACR_ENA */ +#define WM8962_DACR_ENA_MASK 0x0080 /* DACR_ENA */ +#define WM8962_DACR_ENA_SHIFT 7 /* DACR_ENA */ +#define WM8962_DACR_ENA_WIDTH 1 /* DACR_ENA */ +#define WM8962_HPOUTL_PGA_ENA 0x0040 /* HPOUTL_PGA_ENA */ +#define WM8962_HPOUTL_PGA_ENA_MASK 0x0040 /* HPOUTL_PGA_ENA */ +#define WM8962_HPOUTL_PGA_ENA_SHIFT 6 /* HPOUTL_PGA_ENA */ +#define WM8962_HPOUTL_PGA_ENA_WIDTH 1 /* HPOUTL_PGA_ENA */ +#define WM8962_HPOUTR_PGA_ENA 0x0020 /* HPOUTR_PGA_ENA */ +#define WM8962_HPOUTR_PGA_ENA_MASK 0x0020 /* HPOUTR_PGA_ENA */ +#define WM8962_HPOUTR_PGA_ENA_SHIFT 5 /* HPOUTR_PGA_ENA */ +#define WM8962_HPOUTR_PGA_ENA_WIDTH 1 /* HPOUTR_PGA_ENA */ +#define WM8962_SPKOUTL_PGA_ENA 0x0010 /* SPKOUTL_PGA_ENA */ +#define WM8962_SPKOUTL_PGA_ENA_MASK 0x0010 /* SPKOUTL_PGA_ENA */ +#define WM8962_SPKOUTL_PGA_ENA_SHIFT 4 /* SPKOUTL_PGA_ENA */ +#define WM8962_SPKOUTL_PGA_ENA_WIDTH 1 /* SPKOUTL_PGA_ENA */ +#define WM8962_SPKOUTR_PGA_ENA 0x0008 /* SPKOUTR_PGA_ENA */ +#define WM8962_SPKOUTR_PGA_ENA_MASK 0x0008 /* SPKOUTR_PGA_ENA */ +#define WM8962_SPKOUTR_PGA_ENA_SHIFT 3 /* SPKOUTR_PGA_ENA */ +#define WM8962_SPKOUTR_PGA_ENA_WIDTH 1 /* SPKOUTR_PGA_ENA */ +#define WM8962_HPOUTL_PGA_MUTE 0x0002 /* HPOUTL_PGA_MUTE */ +#define WM8962_HPOUTL_PGA_MUTE_MASK 0x0002 /* HPOUTL_PGA_MUTE */ +#define WM8962_HPOUTL_PGA_MUTE_SHIFT 1 /* HPOUTL_PGA_MUTE */ +#define WM8962_HPOUTL_PGA_MUTE_WIDTH 1 /* HPOUTL_PGA_MUTE */ +#define WM8962_HPOUTR_PGA_MUTE 0x0001 /* HPOUTR_PGA_MUTE */ +#define WM8962_HPOUTR_PGA_MUTE_MASK 0x0001 /* HPOUTR_PGA_MUTE */ +#define WM8962_HPOUTR_PGA_MUTE_SHIFT 0 /* HPOUTR_PGA_MUTE */ +#define WM8962_HPOUTR_PGA_MUTE_WIDTH 1 /* HPOUTR_PGA_MUTE */ + +/* + * R27 (0x1B) - Additional Control (3) + */ +#define WM8962_SAMPLE_RATE_INT_MODE 0x0010 /* SAMPLE_RATE_INT_MODE */ +#define WM8962_SAMPLE_RATE_INT_MODE_MASK 0x0010 /* SAMPLE_RATE_INT_MODE */ +#define WM8962_SAMPLE_RATE_INT_MODE_SHIFT 4 /* SAMPLE_RATE_INT_MODE */ +#define WM8962_SAMPLE_RATE_INT_MODE_WIDTH 1 /* SAMPLE_RATE_INT_MODE */ +#define WM8962_SAMPLE_RATE_MASK 0x0007 /* SAMPLE_RATE - [2:0] */ +#define WM8962_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [2:0] */ +#define WM8962_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [2:0] */ + +/* + * R28 (0x1C) - Anti-pop + */ +#define WM8962_STARTUP_BIAS_ENA 0x0010 /* STARTUP_BIAS_ENA */ +#define WM8962_STARTUP_BIAS_ENA_MASK 0x0010 /* STARTUP_BIAS_ENA */ +#define WM8962_STARTUP_BIAS_ENA_SHIFT 4 /* STARTUP_BIAS_ENA */ +#define WM8962_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */ +#define WM8962_VMID_BUF_ENA 0x0008 /* VMID_BUF_ENA */ +#define WM8962_VMID_BUF_ENA_MASK 0x0008 /* VMID_BUF_ENA */ +#define WM8962_VMID_BUF_ENA_SHIFT 3 /* VMID_BUF_ENA */ +#define WM8962_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */ +#define WM8962_VMID_RAMP 0x0004 /* VMID_RAMP */ +#define WM8962_VMID_RAMP_MASK 0x0004 /* VMID_RAMP */ +#define WM8962_VMID_RAMP_SHIFT 2 /* VMID_RAMP */ +#define WM8962_VMID_RAMP_WIDTH 1 /* VMID_RAMP */ + +/* + * R30 (0x1E) - Clocking 3 + */ +#define WM8962_DBCLK_DIV_MASK 0xE000 /* DBCLK_DIV - [15:13] */ +#define WM8962_DBCLK_DIV_SHIFT 13 /* DBCLK_DIV - [15:13] */ +#define WM8962_DBCLK_DIV_WIDTH 3 /* DBCLK_DIV - [15:13] */ +#define WM8962_OPCLK_DIV_MASK 0x1C00 /* OPCLK_DIV - [12:10] */ +#define WM8962_OPCLK_DIV_SHIFT 10 /* OPCLK_DIV - [12:10] */ +#define WM8962_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [12:10] */ +#define WM8962_TOCLK_DIV_MASK 0x0380 /* TOCLK_DIV - [9:7] */ +#define WM8962_TOCLK_DIV_SHIFT 7 /* TOCLK_DIV - [9:7] */ +#define WM8962_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [9:7] */ +#define WM8962_F256KCLK_DIV_MASK 0x007E /* F256KCLK_DIV - [6:1] */ +#define WM8962_F256KCLK_DIV_SHIFT 1 /* F256KCLK_DIV - [6:1] */ +#define WM8962_F256KCLK_DIV_WIDTH 6 /* F256KCLK_DIV - [6:1] */ + +/* + * R31 (0x1F) - Input mixer control (1) + */ +#define WM8962_MIXINL_MUTE 0x0008 /* MIXINL_MUTE */ +#define WM8962_MIXINL_MUTE_MASK 0x0008 /* MIXINL_MUTE */ +#define WM8962_MIXINL_MUTE_SHIFT 3 /* MIXINL_MUTE */ +#define WM8962_MIXINL_MUTE_WIDTH 1 /* MIXINL_MUTE */ +#define WM8962_MIXINR_MUTE 0x0004 /* MIXINR_MUTE */ +#define WM8962_MIXINR_MUTE_MASK 0x0004 /* MIXINR_MUTE */ +#define WM8962_MIXINR_MUTE_SHIFT 2 /* MIXINR_MUTE */ +#define WM8962_MIXINR_MUTE_WIDTH 1 /* MIXINR_MUTE */ +#define WM8962_MIXINL_ENA 0x0002 /* MIXINL_ENA */ +#define WM8962_MIXINL_ENA_MASK 0x0002 /* MIXINL_ENA */ +#define WM8962_MIXINL_ENA_SHIFT 1 /* MIXINL_ENA */ +#define WM8962_MIXINL_ENA_WIDTH 1 /* MIXINL_ENA */ +#define WM8962_MIXINR_ENA 0x0001 /* MIXINR_ENA */ +#define WM8962_MIXINR_ENA_MASK 0x0001 /* MIXINR_ENA */ +#define WM8962_MIXINR_ENA_SHIFT 0 /* MIXINR_ENA */ +#define WM8962_MIXINR_ENA_WIDTH 1 /* MIXINR_ENA */ + +/* + * R32 (0x20) - Left input mixer volume + */ +#define WM8962_IN2L_MIXINL_VOL_MASK 0x01C0 /* IN2L_MIXINL_VOL - [8:6] */ +#define WM8962_IN2L_MIXINL_VOL_SHIFT 6 /* IN2L_MIXINL_VOL - [8:6] */ +#define WM8962_IN2L_MIXINL_VOL_WIDTH 3 /* IN2L_MIXINL_VOL - [8:6] */ +#define WM8962_INPGAL_MIXINL_VOL_MASK 0x0038 /* INPGAL_MIXINL_VOL - [5:3] */ +#define WM8962_INPGAL_MIXINL_VOL_SHIFT 3 /* INPGAL_MIXINL_VOL - [5:3] */ +#define WM8962_INPGAL_MIXINL_VOL_WIDTH 3 /* INPGAL_MIXINL_VOL - [5:3] */ +#define WM8962_IN3L_MIXINL_VOL_MASK 0x0007 /* IN3L_MIXINL_VOL - [2:0] */ +#define WM8962_IN3L_MIXINL_VOL_SHIFT 0 /* IN3L_MIXINL_VOL - [2:0] */ +#define WM8962_IN3L_MIXINL_VOL_WIDTH 3 /* IN3L_MIXINL_VOL - [2:0] */ + +/* + * R33 (0x21) - Right input mixer volume + */ +#define WM8962_IN2R_MIXINR_VOL_MASK 0x01C0 /* IN2R_MIXINR_VOL - [8:6] */ +#define WM8962_IN2R_MIXINR_VOL_SHIFT 6 /* IN2R_MIXINR_VOL - [8:6] */ +#define WM8962_IN2R_MIXINR_VOL_WIDTH 3 /* IN2R_MIXINR_VOL - [8:6] */ +#define WM8962_INPGAR_MIXINR_VOL_MASK 0x0038 /* INPGAR_MIXINR_VOL - [5:3] */ +#define WM8962_INPGAR_MIXINR_VOL_SHIFT 3 /* INPGAR_MIXINR_VOL - [5:3] */ +#define WM8962_INPGAR_MIXINR_VOL_WIDTH 3 /* INPGAR_MIXINR_VOL - [5:3] */ +#define WM8962_IN3R_MIXINR_VOL_MASK 0x0007 /* IN3R_MIXINR_VOL - [2:0] */ +#define WM8962_IN3R_MIXINR_VOL_SHIFT 0 /* IN3R_MIXINR_VOL - [2:0] */ +#define WM8962_IN3R_MIXINR_VOL_WIDTH 3 /* IN3R_MIXINR_VOL - [2:0] */ + +/* + * R34 (0x22) - Input mixer control (2) + */ +#define WM8962_IN2L_TO_MIXINL 0x0020 /* IN2L_TO_MIXINL */ +#define WM8962_IN2L_TO_MIXINL_MASK 0x0020 /* IN2L_TO_MIXINL */ +#define WM8962_IN2L_TO_MIXINL_SHIFT 5 /* IN2L_TO_MIXINL */ +#define WM8962_IN2L_TO_MIXINL_WIDTH 1 /* IN2L_TO_MIXINL */ +#define WM8962_IN3L_TO_MIXINL 0x0010 /* IN3L_TO_MIXINL */ +#define WM8962_IN3L_TO_MIXINL_MASK 0x0010 /* IN3L_TO_MIXINL */ +#define WM8962_IN3L_TO_MIXINL_SHIFT 4 /* IN3L_TO_MIXINL */ +#define WM8962_IN3L_TO_MIXINL_WIDTH 1 /* IN3L_TO_MIXINL */ +#define WM8962_INPGAL_TO_MIXINL 0x0008 /* INPGAL_TO_MIXINL */ +#define WM8962_INPGAL_TO_MIXINL_MASK 0x0008 /* INPGAL_TO_MIXINL */ +#define WM8962_INPGAL_TO_MIXINL_SHIFT 3 /* INPGAL_TO_MIXINL */ +#define WM8962_INPGAL_TO_MIXINL_WIDTH 1 /* INPGAL_TO_MIXINL */ +#define WM8962_IN2R_TO_MIXINR 0x0004 /* IN2R_TO_MIXINR */ +#define WM8962_IN2R_TO_MIXINR_MASK 0x0004 /* IN2R_TO_MIXINR */ +#define WM8962_IN2R_TO_MIXINR_SHIFT 2 /* IN2R_TO_MIXINR */ +#define WM8962_IN2R_TO_MIXINR_WIDTH 1 /* IN2R_TO_MIXINR */ +#define WM8962_IN3R_TO_MIXINR 0x0002 /* IN3R_TO_MIXINR */ +#define WM8962_IN3R_TO_MIXINR_MASK 0x0002 /* IN3R_TO_MIXINR */ +#define WM8962_IN3R_TO_MIXINR_SHIFT 1 /* IN3R_TO_MIXINR */ +#define WM8962_IN3R_TO_MIXINR_WIDTH 1 /* IN3R_TO_MIXINR */ +#define WM8962_INPGAR_TO_MIXINR 0x0001 /* INPGAR_TO_MIXINR */ +#define WM8962_INPGAR_TO_MIXINR_MASK 0x0001 /* INPGAR_TO_MIXINR */ +#define WM8962_INPGAR_TO_MIXINR_SHIFT 0 /* INPGAR_TO_MIXINR */ +#define WM8962_INPGAR_TO_MIXINR_WIDTH 1 /* INPGAR_TO_MIXINR */ + +/* + * R35 (0x23) - Input bias control + */ +#define WM8962_MIXIN_BIAS_MASK 0x0038 /* MIXIN_BIAS - [5:3] */ +#define WM8962_MIXIN_BIAS_SHIFT 3 /* MIXIN_BIAS - [5:3] */ +#define WM8962_MIXIN_BIAS_WIDTH 3 /* MIXIN_BIAS - [5:3] */ +#define WM8962_INPGA_BIAS_MASK 0x0007 /* INPGA_BIAS - [2:0] */ +#define WM8962_INPGA_BIAS_SHIFT 0 /* INPGA_BIAS - [2:0] */ +#define WM8962_INPGA_BIAS_WIDTH 3 /* INPGA_BIAS - [2:0] */ + +/* + * R37 (0x25) - Left input PGA control + */ +#define WM8962_INPGAL_ENA 0x0010 /* INPGAL_ENA */ +#define WM8962_INPGAL_ENA_MASK 0x0010 /* INPGAL_ENA */ +#define WM8962_INPGAL_ENA_SHIFT 4 /* INPGAL_ENA */ +#define WM8962_INPGAL_ENA_WIDTH 1 /* INPGAL_ENA */ +#define WM8962_IN1L_TO_INPGAL 0x0008 /* IN1L_TO_INPGAL */ +#define WM8962_IN1L_TO_INPGAL_MASK 0x0008 /* IN1L_TO_INPGAL */ +#define WM8962_IN1L_TO_INPGAL_SHIFT 3 /* IN1L_TO_INPGAL */ +#define WM8962_IN1L_TO_INPGAL_WIDTH 1 /* IN1L_TO_INPGAL */ +#define WM8962_IN2L_TO_INPGAL 0x0004 /* IN2L_TO_INPGAL */ +#define WM8962_IN2L_TO_INPGAL_MASK 0x0004 /* IN2L_TO_INPGAL */ +#define WM8962_IN2L_TO_INPGAL_SHIFT 2 /* IN2L_TO_INPGAL */ +#define WM8962_IN2L_TO_INPGAL_WIDTH 1 /* IN2L_TO_INPGAL */ +#define WM8962_IN3L_TO_INPGAL 0x0002 /* IN3L_TO_INPGAL */ +#define WM8962_IN3L_TO_INPGAL_MASK 0x0002 /* IN3L_TO_INPGAL */ +#define WM8962_IN3L_TO_INPGAL_SHIFT 1 /* IN3L_TO_INPGAL */ +#define WM8962_IN3L_TO_INPGAL_WIDTH 1 /* IN3L_TO_INPGAL */ +#define WM8962_IN4L_TO_INPGAL 0x0001 /* IN4L_TO_INPGAL */ +#define WM8962_IN4L_TO_INPGAL_MASK 0x0001 /* IN4L_TO_INPGAL */ +#define WM8962_IN4L_TO_INPGAL_SHIFT 0 /* IN4L_TO_INPGAL */ +#define WM8962_IN4L_TO_INPGAL_WIDTH 1 /* IN4L_TO_INPGAL */ + +/* + * R38 (0x26) - Right input PGA control + */ +#define WM8962_INPGAR_ENA 0x0010 /* INPGAR_ENA */ +#define WM8962_INPGAR_ENA_MASK 0x0010 /* INPGAR_ENA */ +#define WM8962_INPGAR_ENA_SHIFT 4 /* INPGAR_ENA */ +#define WM8962_INPGAR_ENA_WIDTH 1 /* INPGAR_ENA */ +#define WM8962_IN1R_TO_INPGAR 0x0008 /* IN1R_TO_INPGAR */ +#define WM8962_IN1R_TO_INPGAR_MASK 0x0008 /* IN1R_TO_INPGAR */ +#define WM8962_IN1R_TO_INPGAR_SHIFT 3 /* IN1R_TO_INPGAR */ +#define WM8962_IN1R_TO_INPGAR_WIDTH 1 /* IN1R_TO_INPGAR */ +#define WM8962_IN2R_TO_INPGAR 0x0004 /* IN2R_TO_INPGAR */ +#define WM8962_IN2R_TO_INPGAR_MASK 0x0004 /* IN2R_TO_INPGAR */ +#define WM8962_IN2R_TO_INPGAR_SHIFT 2 /* IN2R_TO_INPGAR */ +#define WM8962_IN2R_TO_INPGAR_WIDTH 1 /* IN2R_TO_INPGAR */ +#define WM8962_IN3R_TO_INPGAR 0x0002 /* IN3R_TO_INPGAR */ +#define WM8962_IN3R_TO_INPGAR_MASK 0x0002 /* IN3R_TO_INPGAR */ +#define WM8962_IN3R_TO_INPGAR_SHIFT 1 /* IN3R_TO_INPGAR */ +#define WM8962_IN3R_TO_INPGAR_WIDTH 1 /* IN3R_TO_INPGAR */ +#define WM8962_IN4R_TO_INPGAR 0x0001 /* IN4R_TO_INPGAR */ +#define WM8962_IN4R_TO_INPGAR_MASK 0x0001 /* IN4R_TO_INPGAR */ +#define WM8962_IN4R_TO_INPGAR_SHIFT 0 /* IN4R_TO_INPGAR */ +#define WM8962_IN4R_TO_INPGAR_WIDTH 1 /* IN4R_TO_INPGAR */ + +/* + * R40 (0x28) - SPKOUTL volume + */ +#define WM8962_SPKOUT_VU 0x0100 /* SPKOUT_VU */ +#define WM8962_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */ +#define WM8962_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */ +#define WM8962_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */ +#define WM8962_SPKOUTL_ZC 0x0080 /* SPKOUTL_ZC */ +#define WM8962_SPKOUTL_ZC_MASK 0x0080 /* SPKOUTL_ZC */ +#define WM8962_SPKOUTL_ZC_SHIFT 7 /* SPKOUTL_ZC */ +#define WM8962_SPKOUTL_ZC_WIDTH 1 /* SPKOUTL_ZC */ +#define WM8962_SPKOUTL_VOL_MASK 0x007F /* SPKOUTL_VOL - [6:0] */ +#define WM8962_SPKOUTL_VOL_SHIFT 0 /* SPKOUTL_VOL - [6:0] */ +#define WM8962_SPKOUTL_VOL_WIDTH 7 /* SPKOUTL_VOL - [6:0] */ + +/* + * R41 (0x29) - SPKOUTR volume + */ +#define WM8962_SPKOUTR_ZC 0x0080 /* SPKOUTR_ZC */ +#define WM8962_SPKOUTR_ZC_MASK 0x0080 /* SPKOUTR_ZC */ +#define WM8962_SPKOUTR_ZC_SHIFT 7 /* SPKOUTR_ZC */ +#define WM8962_SPKOUTR_ZC_WIDTH 1 /* SPKOUTR_ZC */ +#define WM8962_SPKOUTR_VOL_MASK 0x007F /* SPKOUTR_VOL - [6:0] */ +#define WM8962_SPKOUTR_VOL_SHIFT 0 /* SPKOUTR_VOL - [6:0] */ +#define WM8962_SPKOUTR_VOL_WIDTH 7 /* SPKOUTR_VOL - [6:0] */ + +/* + * R47 (0x2F) - Thermal Shutdown Status + */ +#define WM8962_TEMP_ERR_HP 0x0008 /* TEMP_ERR_HP */ +#define WM8962_TEMP_ERR_HP_MASK 0x0008 /* TEMP_ERR_HP */ +#define WM8962_TEMP_ERR_HP_SHIFT 3 /* TEMP_ERR_HP */ +#define WM8962_TEMP_ERR_HP_WIDTH 1 /* TEMP_ERR_HP */ +#define WM8962_TEMP_WARN_HP 0x0004 /* TEMP_WARN_HP */ +#define WM8962_TEMP_WARN_HP_MASK 0x0004 /* TEMP_WARN_HP */ +#define WM8962_TEMP_WARN_HP_SHIFT 2 /* TEMP_WARN_HP */ +#define WM8962_TEMP_WARN_HP_WIDTH 1 /* TEMP_WARN_HP */ +#define WM8962_TEMP_ERR_SPK 0x0002 /* TEMP_ERR_SPK */ +#define WM8962_TEMP_ERR_SPK_MASK 0x0002 /* TEMP_ERR_SPK */ +#define WM8962_TEMP_ERR_SPK_SHIFT 1 /* TEMP_ERR_SPK */ +#define WM8962_TEMP_ERR_SPK_WIDTH 1 /* TEMP_ERR_SPK */ +#define WM8962_TEMP_WARN_SPK 0x0001 /* TEMP_WARN_SPK */ +#define WM8962_TEMP_WARN_SPK_MASK 0x0001 /* TEMP_WARN_SPK */ +#define WM8962_TEMP_WARN_SPK_SHIFT 0 /* TEMP_WARN_SPK */ +#define WM8962_TEMP_WARN_SPK_WIDTH 1 /* TEMP_WARN_SPK */ + +/* + * R48 (0x30) - Additional Control (4) + */ +#define WM8962_MICDET_THR_MASK 0x7000 /* MICDET_THR - [14:12] */ +#define WM8962_MICDET_THR_SHIFT 12 /* MICDET_THR - [14:12] */ +#define WM8962_MICDET_THR_WIDTH 3 /* MICDET_THR - [14:12] */ +#define WM8962_MICSHORT_THR_MASK 0x0C00 /* MICSHORT_THR - [11:10] */ +#define WM8962_MICSHORT_THR_SHIFT 10 /* MICSHORT_THR - [11:10] */ +#define WM8962_MICSHORT_THR_WIDTH 2 /* MICSHORT_THR - [11:10] */ +#define WM8962_MICDET_ENA 0x0200 /* MICDET_ENA */ +#define WM8962_MICDET_ENA_MASK 0x0200 /* MICDET_ENA */ +#define WM8962_MICDET_ENA_SHIFT 9 /* MICDET_ENA */ +#define WM8962_MICDET_ENA_WIDTH 1 /* MICDET_ENA */ +#define WM8962_MICDET_STS 0x0080 /* MICDET_STS */ +#define WM8962_MICDET_STS_MASK 0x0080 /* MICDET_STS */ +#define WM8962_MICDET_STS_SHIFT 7 /* MICDET_STS */ +#define WM8962_MICDET_STS_WIDTH 1 /* MICDET_STS */ +#define WM8962_MICSHORT_STS 0x0040 /* MICSHORT_STS */ +#define WM8962_MICSHORT_STS_MASK 0x0040 /* MICSHORT_STS */ +#define WM8962_MICSHORT_STS_SHIFT 6 /* MICSHORT_STS */ +#define WM8962_MICSHORT_STS_WIDTH 1 /* MICSHORT_STS */ +#define WM8962_TEMP_ENA_HP 0x0004 /* TEMP_ENA_HP */ +#define WM8962_TEMP_ENA_HP_MASK 0x0004 /* TEMP_ENA_HP */ +#define WM8962_TEMP_ENA_HP_SHIFT 2 /* TEMP_ENA_HP */ +#define WM8962_TEMP_ENA_HP_WIDTH 1 /* TEMP_ENA_HP */ +#define WM8962_TEMP_ENA_SPK 0x0002 /* TEMP_ENA_SPK */ +#define WM8962_TEMP_ENA_SPK_MASK 0x0002 /* TEMP_ENA_SPK */ +#define WM8962_TEMP_ENA_SPK_SHIFT 1 /* TEMP_ENA_SPK */ +#define WM8962_TEMP_ENA_SPK_WIDTH 1 /* TEMP_ENA_SPK */ +#define WM8962_MICBIAS_LVL 0x0001 /* MICBIAS_LVL */ +#define WM8962_MICBIAS_LVL_MASK 0x0001 /* MICBIAS_LVL */ +#define WM8962_MICBIAS_LVL_SHIFT 0 /* MICBIAS_LVL */ +#define WM8962_MICBIAS_LVL_WIDTH 1 /* MICBIAS_LVL */ + +/* + * R49 (0x31) - Class D Control 1 + */ +#define WM8962_SPKOUTR_ENA 0x0080 /* SPKOUTR_ENA */ +#define WM8962_SPKOUTR_ENA_MASK 0x0080 /* SPKOUTR_ENA */ +#define WM8962_SPKOUTR_ENA_SHIFT 7 /* SPKOUTR_ENA */ +#define WM8962_SPKOUTR_ENA_WIDTH 1 /* SPKOUTR_ENA */ +#define WM8962_SPKOUTL_ENA 0x0040 /* SPKOUTL_ENA */ +#define WM8962_SPKOUTL_ENA_MASK 0x0040 /* SPKOUTL_ENA */ +#define WM8962_SPKOUTL_ENA_SHIFT 6 /* SPKOUTL_ENA */ +#define WM8962_SPKOUTL_ENA_WIDTH 1 /* SPKOUTL_ENA */ +#define WM8962_DAC_MUTE_ALT 0x0010 /* DAC_MUTE */ +#define WM8962_DAC_MUTE_ALT_MASK 0x0010 /* DAC_MUTE */ +#define WM8962_DAC_MUTE_ALT_SHIFT 4 /* DAC_MUTE */ +#define WM8962_DAC_MUTE_ALT_WIDTH 1 /* DAC_MUTE */ +#define WM8962_SPKOUTL_PGA_MUTE 0x0002 /* SPKOUTL_PGA_MUTE */ +#define WM8962_SPKOUTL_PGA_MUTE_MASK 0x0002 /* SPKOUTL_PGA_MUTE */ +#define WM8962_SPKOUTL_PGA_MUTE_SHIFT 1 /* SPKOUTL_PGA_MUTE */ +#define WM8962_SPKOUTL_PGA_MUTE_WIDTH 1 /* SPKOUTL_PGA_MUTE */ +#define WM8962_SPKOUTR_PGA_MUTE 0x0001 /* SPKOUTR_PGA_MUTE */ +#define WM8962_SPKOUTR_PGA_MUTE_MASK 0x0001 /* SPKOUTR_PGA_MUTE */ +#define WM8962_SPKOUTR_PGA_MUTE_SHIFT 0 /* SPKOUTR_PGA_MUTE */ +#define WM8962_SPKOUTR_PGA_MUTE_WIDTH 1 /* SPKOUTR_PGA_MUTE */ + +/* + * R51 (0x33) - Class D Control 2 + */ +#define WM8962_SPK_MONO 0x0040 /* SPK_MONO */ +#define WM8962_SPK_MONO_MASK 0x0040 /* SPK_MONO */ +#define WM8962_SPK_MONO_SHIFT 6 /* SPK_MONO */ +#define WM8962_SPK_MONO_WIDTH 1 /* SPK_MONO */ +#define WM8962_CLASSD_VOL_MASK 0x0007 /* CLASSD_VOL - [2:0] */ +#define WM8962_CLASSD_VOL_SHIFT 0 /* CLASSD_VOL - [2:0] */ +#define WM8962_CLASSD_VOL_WIDTH 3 /* CLASSD_VOL - [2:0] */ + +/* + * R56 (0x38) - Clocking 4 + */ +#define WM8962_SYSCLK_RATE_MASK 0x001E /* SYSCLK_RATE - [4:1] */ +#define WM8962_SYSCLK_RATE_SHIFT 1 /* SYSCLK_RATE - [4:1] */ +#define WM8962_SYSCLK_RATE_WIDTH 4 /* SYSCLK_RATE - [4:1] */ + +/* + * R57 (0x39) - DAC DSP Mixing (1) + */ +#define WM8962_DAC_MONOMIX 0x0200 /* DAC_MONOMIX */ +#define WM8962_DAC_MONOMIX_MASK 0x0200 /* DAC_MONOMIX */ +#define WM8962_DAC_MONOMIX_SHIFT 9 /* DAC_MONOMIX */ +#define WM8962_DAC_MONOMIX_WIDTH 1 /* DAC_MONOMIX */ +#define WM8962_ADCR_DAC_SVOL_MASK 0x00F0 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8962_ADCR_DAC_SVOL_SHIFT 4 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8962_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [7:4] */ +#define WM8962_ADC_TO_DACR_MASK 0x000C /* ADC_TO_DACR - [3:2] */ +#define WM8962_ADC_TO_DACR_SHIFT 2 /* ADC_TO_DACR - [3:2] */ +#define WM8962_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [3:2] */ + +/* + * R58 (0x3A) - DAC DSP Mixing (2) + */ +#define WM8962_ADCL_DAC_SVOL_MASK 0x00F0 /* ADCL_DAC_SVOL - [7:4] */ +#define WM8962_ADCL_DAC_SVOL_SHIFT 4 /* ADCL_DAC_SVOL - [7:4] */ +#define WM8962_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [7:4] */ +#define WM8962_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */ +#define WM8962_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */ +#define WM8962_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */ + +/* + * R60 (0x3C) - DC Servo 0 + */ +#define WM8962_INL_DCS_ENA 0x0080 /* INL_DCS_ENA */ +#define WM8962_INL_DCS_ENA_MASK 0x0080 /* INL_DCS_ENA */ +#define WM8962_INL_DCS_ENA_SHIFT 7 /* INL_DCS_ENA */ +#define WM8962_INL_DCS_ENA_WIDTH 1 /* INL_DCS_ENA */ +#define WM8962_INL_DCS_STARTUP 0x0040 /* INL_DCS_STARTUP */ +#define WM8962_INL_DCS_STARTUP_MASK 0x0040 /* INL_DCS_STARTUP */ +#define WM8962_INL_DCS_STARTUP_SHIFT 6 /* INL_DCS_STARTUP */ +#define WM8962_INL_DCS_STARTUP_WIDTH 1 /* INL_DCS_STARTUP */ +#define WM8962_INR_DCS_ENA 0x0008 /* INR_DCS_ENA */ +#define WM8962_INR_DCS_ENA_MASK 0x0008 /* INR_DCS_ENA */ +#define WM8962_INR_DCS_ENA_SHIFT 3 /* INR_DCS_ENA */ +#define WM8962_INR_DCS_ENA_WIDTH 1 /* INR_DCS_ENA */ +#define WM8962_INR_DCS_STARTUP 0x0004 /* INR_DCS_STARTUP */ +#define WM8962_INR_DCS_STARTUP_MASK 0x0004 /* INR_DCS_STARTUP */ +#define WM8962_INR_DCS_STARTUP_SHIFT 2 /* INR_DCS_STARTUP */ +#define WM8962_INR_DCS_STARTUP_WIDTH 1 /* INR_DCS_STARTUP */ + +/* + * R61 (0x3D) - DC Servo 1 + */ +#define WM8962_HP1L_DCS_ENA 0x0080 /* HP1L_DCS_ENA */ +#define WM8962_HP1L_DCS_ENA_MASK 0x0080 /* HP1L_DCS_ENA */ +#define WM8962_HP1L_DCS_ENA_SHIFT 7 /* HP1L_DCS_ENA */ +#define WM8962_HP1L_DCS_ENA_WIDTH 1 /* HP1L_DCS_ENA */ +#define WM8962_HP1L_DCS_STARTUP 0x0040 /* HP1L_DCS_STARTUP */ +#define WM8962_HP1L_DCS_STARTUP_MASK 0x0040 /* HP1L_DCS_STARTUP */ +#define WM8962_HP1L_DCS_STARTUP_SHIFT 6 /* HP1L_DCS_STARTUP */ +#define WM8962_HP1L_DCS_STARTUP_WIDTH 1 /* HP1L_DCS_STARTUP */ +#define WM8962_HP1L_DCS_SYNC 0x0010 /* HP1L_DCS_SYNC */ +#define WM8962_HP1L_DCS_SYNC_MASK 0x0010 /* HP1L_DCS_SYNC */ +#define WM8962_HP1L_DCS_SYNC_SHIFT 4 /* HP1L_DCS_SYNC */ +#define WM8962_HP1L_DCS_SYNC_WIDTH 1 /* HP1L_DCS_SYNC */ +#define WM8962_HP1R_DCS_ENA 0x0008 /* HP1R_DCS_ENA */ +#define WM8962_HP1R_DCS_ENA_MASK 0x0008 /* HP1R_DCS_ENA */ +#define WM8962_HP1R_DCS_ENA_SHIFT 3 /* HP1R_DCS_ENA */ +#define WM8962_HP1R_DCS_ENA_WIDTH 1 /* HP1R_DCS_ENA */ +#define WM8962_HP1R_DCS_STARTUP 0x0004 /* HP1R_DCS_STARTUP */ +#define WM8962_HP1R_DCS_STARTUP_MASK 0x0004 /* HP1R_DCS_STARTUP */ +#define WM8962_HP1R_DCS_STARTUP_SHIFT 2 /* HP1R_DCS_STARTUP */ +#define WM8962_HP1R_DCS_STARTUP_WIDTH 1 /* HP1R_DCS_STARTUP */ +#define WM8962_HP1R_DCS_SYNC 0x0001 /* HP1R_DCS_SYNC */ +#define WM8962_HP1R_DCS_SYNC_MASK 0x0001 /* HP1R_DCS_SYNC */ +#define WM8962_HP1R_DCS_SYNC_SHIFT 0 /* HP1R_DCS_SYNC */ +#define WM8962_HP1R_DCS_SYNC_WIDTH 1 /* HP1R_DCS_SYNC */ + +/* + * R64 (0x40) - DC Servo 4 + */ +#define WM8962_HP1_DCS_SYNC_STEPS_MASK 0x3F80 /* HP1_DCS_SYNC_STEPS - [13:7] */ +#define WM8962_HP1_DCS_SYNC_STEPS_SHIFT 7 /* HP1_DCS_SYNC_STEPS - [13:7] */ +#define WM8962_HP1_DCS_SYNC_STEPS_WIDTH 7 /* HP1_DCS_SYNC_STEPS - [13:7] */ + +/* + * R66 (0x42) - DC Servo 6 + */ +#define WM8962_DCS_STARTUP_DONE_INL 0x0400 /* DCS_STARTUP_DONE_INL */ +#define WM8962_DCS_STARTUP_DONE_INL_MASK 0x0400 /* DCS_STARTUP_DONE_INL */ +#define WM8962_DCS_STARTUP_DONE_INL_SHIFT 10 /* DCS_STARTUP_DONE_INL */ +#define WM8962_DCS_STARTUP_DONE_INL_WIDTH 1 /* DCS_STARTUP_DONE_INL */ +#define WM8962_DCS_STARTUP_DONE_INR 0x0200 /* DCS_STARTUP_DONE_INR */ +#define WM8962_DCS_STARTUP_DONE_INR_MASK 0x0200 /* DCS_STARTUP_DONE_INR */ +#define WM8962_DCS_STARTUP_DONE_INR_SHIFT 9 /* DCS_STARTUP_DONE_INR */ +#define WM8962_DCS_STARTUP_DONE_INR_WIDTH 1 /* DCS_STARTUP_DONE_INR */ +#define WM8962_DCS_STARTUP_DONE_HP1L 0x0100 /* DCS_STARTUP_DONE_HP1L */ +#define WM8962_DCS_STARTUP_DONE_HP1L_MASK 0x0100 /* DCS_STARTUP_DONE_HP1L */ +#define WM8962_DCS_STARTUP_DONE_HP1L_SHIFT 8 /* DCS_STARTUP_DONE_HP1L */ +#define WM8962_DCS_STARTUP_DONE_HP1L_WIDTH 1 /* DCS_STARTUP_DONE_HP1L */ +#define WM8962_DCS_STARTUP_DONE_HP1R 0x0080 /* DCS_STARTUP_DONE_HP1R */ +#define WM8962_DCS_STARTUP_DONE_HP1R_MASK 0x0080 /* DCS_STARTUP_DONE_HP1R */ +#define WM8962_DCS_STARTUP_DONE_HP1R_SHIFT 7 /* DCS_STARTUP_DONE_HP1R */ +#define WM8962_DCS_STARTUP_DONE_HP1R_WIDTH 1 /* DCS_STARTUP_DONE_HP1R */ + +/* + * R68 (0x44) - Analogue PGA Bias + */ +#define WM8962_HP_PGAS_BIAS_MASK 0x0007 /* HP_PGAS_BIAS - [2:0] */ +#define WM8962_HP_PGAS_BIAS_SHIFT 0 /* HP_PGAS_BIAS - [2:0] */ +#define WM8962_HP_PGAS_BIAS_WIDTH 3 /* HP_PGAS_BIAS - [2:0] */ + +/* + * R69 (0x45) - Analogue HP 0 + */ +#define WM8962_HP1L_RMV_SHORT 0x0080 /* HP1L_RMV_SHORT */ +#define WM8962_HP1L_RMV_SHORT_MASK 0x0080 /* HP1L_RMV_SHORT */ +#define WM8962_HP1L_RMV_SHORT_SHIFT 7 /* HP1L_RMV_SHORT */ +#define WM8962_HP1L_RMV_SHORT_WIDTH 1 /* HP1L_RMV_SHORT */ +#define WM8962_HP1L_ENA_OUTP 0x0040 /* HP1L_ENA_OUTP */ +#define WM8962_HP1L_ENA_OUTP_MASK 0x0040 /* HP1L_ENA_OUTP */ +#define WM8962_HP1L_ENA_OUTP_SHIFT 6 /* HP1L_ENA_OUTP */ +#define WM8962_HP1L_ENA_OUTP_WIDTH 1 /* HP1L_ENA_OUTP */ +#define WM8962_HP1L_ENA_DLY 0x0020 /* HP1L_ENA_DLY */ +#define WM8962_HP1L_ENA_DLY_MASK 0x0020 /* HP1L_ENA_DLY */ +#define WM8962_HP1L_ENA_DLY_SHIFT 5 /* HP1L_ENA_DLY */ +#define WM8962_HP1L_ENA_DLY_WIDTH 1 /* HP1L_ENA_DLY */ +#define WM8962_HP1L_ENA 0x0010 /* HP1L_ENA */ +#define WM8962_HP1L_ENA_MASK 0x0010 /* HP1L_ENA */ +#define WM8962_HP1L_ENA_SHIFT 4 /* HP1L_ENA */ +#define WM8962_HP1L_ENA_WIDTH 1 /* HP1L_ENA */ +#define WM8962_HP1R_RMV_SHORT 0x0008 /* HP1R_RMV_SHORT */ +#define WM8962_HP1R_RMV_SHORT_MASK 0x0008 /* HP1R_RMV_SHORT */ +#define WM8962_HP1R_RMV_SHORT_SHIFT 3 /* HP1R_RMV_SHORT */ +#define WM8962_HP1R_RMV_SHORT_WIDTH 1 /* HP1R_RMV_SHORT */ +#define WM8962_HP1R_ENA_OUTP 0x0004 /* HP1R_ENA_OUTP */ +#define WM8962_HP1R_ENA_OUTP_MASK 0x0004 /* HP1R_ENA_OUTP */ +#define WM8962_HP1R_ENA_OUTP_SHIFT 2 /* HP1R_ENA_OUTP */ +#define WM8962_HP1R_ENA_OUTP_WIDTH 1 /* HP1R_ENA_OUTP */ +#define WM8962_HP1R_ENA_DLY 0x0002 /* HP1R_ENA_DLY */ +#define WM8962_HP1R_ENA_DLY_MASK 0x0002 /* HP1R_ENA_DLY */ +#define WM8962_HP1R_ENA_DLY_SHIFT 1 /* HP1R_ENA_DLY */ +#define WM8962_HP1R_ENA_DLY_WIDTH 1 /* HP1R_ENA_DLY */ +#define WM8962_HP1R_ENA 0x0001 /* HP1R_ENA */ +#define WM8962_HP1R_ENA_MASK 0x0001 /* HP1R_ENA */ +#define WM8962_HP1R_ENA_SHIFT 0 /* HP1R_ENA */ +#define WM8962_HP1R_ENA_WIDTH 1 /* HP1R_ENA */ + +/* + * R71 (0x47) - Analogue HP 2 + */ +#define WM8962_HP1L_VOL_MASK 0x01C0 /* HP1L_VOL - [8:6] */ +#define WM8962_HP1L_VOL_SHIFT 6 /* HP1L_VOL - [8:6] */ +#define WM8962_HP1L_VOL_WIDTH 3 /* HP1L_VOL - [8:6] */ +#define WM8962_HP1R_VOL_MASK 0x0038 /* HP1R_VOL - [5:3] */ +#define WM8962_HP1R_VOL_SHIFT 3 /* HP1R_VOL - [5:3] */ +#define WM8962_HP1R_VOL_WIDTH 3 /* HP1R_VOL - [5:3] */ +#define WM8962_HP_BIAS_BOOST_MASK 0x0007 /* HP_BIAS_BOOST - [2:0] */ +#define WM8962_HP_BIAS_BOOST_SHIFT 0 /* HP_BIAS_BOOST - [2:0] */ +#define WM8962_HP_BIAS_BOOST_WIDTH 3 /* HP_BIAS_BOOST - [2:0] */ + +/* + * R72 (0x48) - Charge Pump 1 + */ +#define WM8962_CP_ENA 0x0001 /* CP_ENA */ +#define WM8962_CP_ENA_MASK 0x0001 /* CP_ENA */ +#define WM8962_CP_ENA_SHIFT 0 /* CP_ENA */ +#define WM8962_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R82 (0x52) - Charge Pump B + */ +#define WM8962_CP_DYN_PWR 0x0001 /* CP_DYN_PWR */ +#define WM8962_CP_DYN_PWR_MASK 0x0001 /* CP_DYN_PWR */ +#define WM8962_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR */ +#define WM8962_CP_DYN_PWR_WIDTH 1 /* CP_DYN_PWR */ + +/* + * R87 (0x57) - Write Sequencer Control 1 + */ +#define WM8962_WSEQ_AUTOSEQ_ENA 0x0080 /* WSEQ_AUTOSEQ_ENA */ +#define WM8962_WSEQ_AUTOSEQ_ENA_MASK 0x0080 /* WSEQ_AUTOSEQ_ENA */ +#define WM8962_WSEQ_AUTOSEQ_ENA_SHIFT 7 /* WSEQ_AUTOSEQ_ENA */ +#define WM8962_WSEQ_AUTOSEQ_ENA_WIDTH 1 /* WSEQ_AUTOSEQ_ENA */ +#define WM8962_WSEQ_ENA 0x0020 /* WSEQ_ENA */ +#define WM8962_WSEQ_ENA_MASK 0x0020 /* WSEQ_ENA */ +#define WM8962_WSEQ_ENA_SHIFT 5 /* WSEQ_ENA */ +#define WM8962_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ + +/* + * R90 (0x5A) - Write Sequencer Control 2 + */ +#define WM8962_WSEQ_ABORT 0x0100 /* WSEQ_ABORT */ +#define WM8962_WSEQ_ABORT_MASK 0x0100 /* WSEQ_ABORT */ +#define WM8962_WSEQ_ABORT_SHIFT 8 /* WSEQ_ABORT */ +#define WM8962_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM8962_WSEQ_START 0x0080 /* WSEQ_START */ +#define WM8962_WSEQ_START_MASK 0x0080 /* WSEQ_START */ +#define WM8962_WSEQ_START_SHIFT 7 /* WSEQ_START */ +#define WM8962_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM8962_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */ +#define WM8962_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */ +#define WM8962_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */ + +/* + * R93 (0x5D) - Write Sequencer Control 3 + */ +#define WM8962_WSEQ_CURRENT_INDEX_MASK 0x03F8 /* WSEQ_CURRENT_INDEX - [9:3] */ +#define WM8962_WSEQ_CURRENT_INDEX_SHIFT 3 /* WSEQ_CURRENT_INDEX - [9:3] */ +#define WM8962_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [9:3] */ +#define WM8962_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */ +#define WM8962_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */ +#define WM8962_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */ +#define WM8962_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ + +/* + * R94 (0x5E) - Control Interface + */ +#define WM8962_SPI_CONTRD 0x0040 /* SPI_CONTRD */ +#define WM8962_SPI_CONTRD_MASK 0x0040 /* SPI_CONTRD */ +#define WM8962_SPI_CONTRD_SHIFT 6 /* SPI_CONTRD */ +#define WM8962_SPI_CONTRD_WIDTH 1 /* SPI_CONTRD */ +#define WM8962_SPI_4WIRE 0x0020 /* SPI_4WIRE */ +#define WM8962_SPI_4WIRE_MASK 0x0020 /* SPI_4WIRE */ +#define WM8962_SPI_4WIRE_SHIFT 5 /* SPI_4WIRE */ +#define WM8962_SPI_4WIRE_WIDTH 1 /* SPI_4WIRE */ +#define WM8962_SPI_CFG 0x0010 /* SPI_CFG */ +#define WM8962_SPI_CFG_MASK 0x0010 /* SPI_CFG */ +#define WM8962_SPI_CFG_SHIFT 4 /* SPI_CFG */ +#define WM8962_SPI_CFG_WIDTH 1 /* SPI_CFG */ + +/* + * R99 (0x63) - Mixer Enables + */ +#define WM8962_HPMIXL_ENA 0x0008 /* HPMIXL_ENA */ +#define WM8962_HPMIXL_ENA_MASK 0x0008 /* HPMIXL_ENA */ +#define WM8962_HPMIXL_ENA_SHIFT 3 /* HPMIXL_ENA */ +#define WM8962_HPMIXL_ENA_WIDTH 1 /* HPMIXL_ENA */ +#define WM8962_HPMIXR_ENA 0x0004 /* HPMIXR_ENA */ +#define WM8962_HPMIXR_ENA_MASK 0x0004 /* HPMIXR_ENA */ +#define WM8962_HPMIXR_ENA_SHIFT 2 /* HPMIXR_ENA */ +#define WM8962_HPMIXR_ENA_WIDTH 1 /* HPMIXR_ENA */ +#define WM8962_SPKMIXL_ENA 0x0002 /* SPKMIXL_ENA */ +#define WM8962_SPKMIXL_ENA_MASK 0x0002 /* SPKMIXL_ENA */ +#define WM8962_SPKMIXL_ENA_SHIFT 1 /* SPKMIXL_ENA */ +#define WM8962_SPKMIXL_ENA_WIDTH 1 /* SPKMIXL_ENA */ +#define WM8962_SPKMIXR_ENA 0x0001 /* SPKMIXR_ENA */ +#define WM8962_SPKMIXR_ENA_MASK 0x0001 /* SPKMIXR_ENA */ +#define WM8962_SPKMIXR_ENA_SHIFT 0 /* SPKMIXR_ENA */ +#define WM8962_SPKMIXR_ENA_WIDTH 1 /* SPKMIXR_ENA */ + +/* + * R100 (0x64) - Headphone Mixer (1) + */ +#define WM8962_HPMIXL_TO_HPOUTL_PGA 0x0080 /* HPMIXL_TO_HPOUTL_PGA */ +#define WM8962_HPMIXL_TO_HPOUTL_PGA_MASK 0x0080 /* HPMIXL_TO_HPOUTL_PGA */ +#define WM8962_HPMIXL_TO_HPOUTL_PGA_SHIFT 7 /* HPMIXL_TO_HPOUTL_PGA */ +#define WM8962_HPMIXL_TO_HPOUTL_PGA_WIDTH 1 /* HPMIXL_TO_HPOUTL_PGA */ +#define WM8962_DACL_TO_HPMIXL 0x0020 /* DACL_TO_HPMIXL */ +#define WM8962_DACL_TO_HPMIXL_MASK 0x0020 /* DACL_TO_HPMIXL */ +#define WM8962_DACL_TO_HPMIXL_SHIFT 5 /* DACL_TO_HPMIXL */ +#define WM8962_DACL_TO_HPMIXL_WIDTH 1 /* DACL_TO_HPMIXL */ +#define WM8962_DACR_TO_HPMIXL 0x0010 /* DACR_TO_HPMIXL */ +#define WM8962_DACR_TO_HPMIXL_MASK 0x0010 /* DACR_TO_HPMIXL */ +#define WM8962_DACR_TO_HPMIXL_SHIFT 4 /* DACR_TO_HPMIXL */ +#define WM8962_DACR_TO_HPMIXL_WIDTH 1 /* DACR_TO_HPMIXL */ +#define WM8962_MIXINL_TO_HPMIXL 0x0008 /* MIXINL_TO_HPMIXL */ +#define WM8962_MIXINL_TO_HPMIXL_MASK 0x0008 /* MIXINL_TO_HPMIXL */ +#define WM8962_MIXINL_TO_HPMIXL_SHIFT 3 /* MIXINL_TO_HPMIXL */ +#define WM8962_MIXINL_TO_HPMIXL_WIDTH 1 /* MIXINL_TO_HPMIXL */ +#define WM8962_MIXINR_TO_HPMIXL 0x0004 /* MIXINR_TO_HPMIXL */ +#define WM8962_MIXINR_TO_HPMIXL_MASK 0x0004 /* MIXINR_TO_HPMIXL */ +#define WM8962_MIXINR_TO_HPMIXL_SHIFT 2 /* MIXINR_TO_HPMIXL */ +#define WM8962_MIXINR_TO_HPMIXL_WIDTH 1 /* MIXINR_TO_HPMIXL */ +#define WM8962_IN4L_TO_HPMIXL 0x0002 /* IN4L_TO_HPMIXL */ +#define WM8962_IN4L_TO_HPMIXL_MASK 0x0002 /* IN4L_TO_HPMIXL */ +#define WM8962_IN4L_TO_HPMIXL_SHIFT 1 /* IN4L_TO_HPMIXL */ +#define WM8962_IN4L_TO_HPMIXL_WIDTH 1 /* IN4L_TO_HPMIXL */ +#define WM8962_IN4R_TO_HPMIXL 0x0001 /* IN4R_TO_HPMIXL */ +#define WM8962_IN4R_TO_HPMIXL_MASK 0x0001 /* IN4R_TO_HPMIXL */ +#define WM8962_IN4R_TO_HPMIXL_SHIFT 0 /* IN4R_TO_HPMIXL */ +#define WM8962_IN4R_TO_HPMIXL_WIDTH 1 /* IN4R_TO_HPMIXL */ + +/* + * R101 (0x65) - Headphone Mixer (2) + */ +#define WM8962_HPMIXR_TO_HPOUTR_PGA 0x0080 /* HPMIXR_TO_HPOUTR_PGA */ +#define WM8962_HPMIXR_TO_HPOUTR_PGA_MASK 0x0080 /* HPMIXR_TO_HPOUTR_PGA */ +#define WM8962_HPMIXR_TO_HPOUTR_PGA_SHIFT 7 /* HPMIXR_TO_HPOUTR_PGA */ +#define WM8962_HPMIXR_TO_HPOUTR_PGA_WIDTH 1 /* HPMIXR_TO_HPOUTR_PGA */ +#define WM8962_DACL_TO_HPMIXR 0x0020 /* DACL_TO_HPMIXR */ +#define WM8962_DACL_TO_HPMIXR_MASK 0x0020 /* DACL_TO_HPMIXR */ +#define WM8962_DACL_TO_HPMIXR_SHIFT 5 /* DACL_TO_HPMIXR */ +#define WM8962_DACL_TO_HPMIXR_WIDTH 1 /* DACL_TO_HPMIXR */ +#define WM8962_DACR_TO_HPMIXR 0x0010 /* DACR_TO_HPMIXR */ +#define WM8962_DACR_TO_HPMIXR_MASK 0x0010 /* DACR_TO_HPMIXR */ +#define WM8962_DACR_TO_HPMIXR_SHIFT 4 /* DACR_TO_HPMIXR */ +#define WM8962_DACR_TO_HPMIXR_WIDTH 1 /* DACR_TO_HPMIXR */ +#define WM8962_MIXINL_TO_HPMIXR 0x0008 /* MIXINL_TO_HPMIXR */ +#define WM8962_MIXINL_TO_HPMIXR_MASK 0x0008 /* MIXINL_TO_HPMIXR */ +#define WM8962_MIXINL_TO_HPMIXR_SHIFT 3 /* MIXINL_TO_HPMIXR */ +#define WM8962_MIXINL_TO_HPMIXR_WIDTH 1 /* MIXINL_TO_HPMIXR */ +#define WM8962_MIXINR_TO_HPMIXR 0x0004 /* MIXINR_TO_HPMIXR */ +#define WM8962_MIXINR_TO_HPMIXR_MASK 0x0004 /* MIXINR_TO_HPMIXR */ +#define WM8962_MIXINR_TO_HPMIXR_SHIFT 2 /* MIXINR_TO_HPMIXR */ +#define WM8962_MIXINR_TO_HPMIXR_WIDTH 1 /* MIXINR_TO_HPMIXR */ +#define WM8962_IN4L_TO_HPMIXR 0x0002 /* IN4L_TO_HPMIXR */ +#define WM8962_IN4L_TO_HPMIXR_MASK 0x0002 /* IN4L_TO_HPMIXR */ +#define WM8962_IN4L_TO_HPMIXR_SHIFT 1 /* IN4L_TO_HPMIXR */ +#define WM8962_IN4L_TO_HPMIXR_WIDTH 1 /* IN4L_TO_HPMIXR */ +#define WM8962_IN4R_TO_HPMIXR 0x0001 /* IN4R_TO_HPMIXR */ +#define WM8962_IN4R_TO_HPMIXR_MASK 0x0001 /* IN4R_TO_HPMIXR */ +#define WM8962_IN4R_TO_HPMIXR_SHIFT 0 /* IN4R_TO_HPMIXR */ +#define WM8962_IN4R_TO_HPMIXR_WIDTH 1 /* IN4R_TO_HPMIXR */ + +/* + * R102 (0x66) - Headphone Mixer (3) + */ +#define WM8962_HPMIXL_MUTE 0x0100 /* HPMIXL_MUTE */ +#define WM8962_HPMIXL_MUTE_MASK 0x0100 /* HPMIXL_MUTE */ +#define WM8962_HPMIXL_MUTE_SHIFT 8 /* HPMIXL_MUTE */ +#define WM8962_HPMIXL_MUTE_WIDTH 1 /* HPMIXL_MUTE */ +#define WM8962_MIXINL_HPMIXL_VOL 0x0080 /* MIXINL_HPMIXL_VOL */ +#define WM8962_MIXINL_HPMIXL_VOL_MASK 0x0080 /* MIXINL_HPMIXL_VOL */ +#define WM8962_MIXINL_HPMIXL_VOL_SHIFT 7 /* MIXINL_HPMIXL_VOL */ +#define WM8962_MIXINL_HPMIXL_VOL_WIDTH 1 /* MIXINL_HPMIXL_VOL */ +#define WM8962_MIXINR_HPMIXL_VOL 0x0040 /* MIXINR_HPMIXL_VOL */ +#define WM8962_MIXINR_HPMIXL_VOL_MASK 0x0040 /* MIXINR_HPMIXL_VOL */ +#define WM8962_MIXINR_HPMIXL_VOL_SHIFT 6 /* MIXINR_HPMIXL_VOL */ +#define WM8962_MIXINR_HPMIXL_VOL_WIDTH 1 /* MIXINR_HPMIXL_VOL */ +#define WM8962_IN4L_HPMIXL_VOL_MASK 0x0038 /* IN4L_HPMIXL_VOL - [5:3] */ +#define WM8962_IN4L_HPMIXL_VOL_SHIFT 3 /* IN4L_HPMIXL_VOL - [5:3] */ +#define WM8962_IN4L_HPMIXL_VOL_WIDTH 3 /* IN4L_HPMIXL_VOL - [5:3] */ +#define WM8962_IN4R_HPMIXL_VOL_MASK 0x0007 /* IN4R_HPMIXL_VOL - [2:0] */ +#define WM8962_IN4R_HPMIXL_VOL_SHIFT 0 /* IN4R_HPMIXL_VOL - [2:0] */ +#define WM8962_IN4R_HPMIXL_VOL_WIDTH 3 /* IN4R_HPMIXL_VOL - [2:0] */ + +/* + * R103 (0x67) - Headphone Mixer (4) + */ +#define WM8962_HPMIXR_MUTE 0x0100 /* HPMIXR_MUTE */ +#define WM8962_HPMIXR_MUTE_MASK 0x0100 /* HPMIXR_MUTE */ +#define WM8962_HPMIXR_MUTE_SHIFT 8 /* HPMIXR_MUTE */ +#define WM8962_HPMIXR_MUTE_WIDTH 1 /* HPMIXR_MUTE */ +#define WM8962_MIXINL_HPMIXR_VOL 0x0080 /* MIXINL_HPMIXR_VOL */ +#define WM8962_MIXINL_HPMIXR_VOL_MASK 0x0080 /* MIXINL_HPMIXR_VOL */ +#define WM8962_MIXINL_HPMIXR_VOL_SHIFT 7 /* MIXINL_HPMIXR_VOL */ +#define WM8962_MIXINL_HPMIXR_VOL_WIDTH 1 /* MIXINL_HPMIXR_VOL */ +#define WM8962_MIXINR_HPMIXR_VOL 0x0040 /* MIXINR_HPMIXR_VOL */ +#define WM8962_MIXINR_HPMIXR_VOL_MASK 0x0040 /* MIXINR_HPMIXR_VOL */ +#define WM8962_MIXINR_HPMIXR_VOL_SHIFT 6 /* MIXINR_HPMIXR_VOL */ +#define WM8962_MIXINR_HPMIXR_VOL_WIDTH 1 /* MIXINR_HPMIXR_VOL */ +#define WM8962_IN4L_HPMIXR_VOL_MASK 0x0038 /* IN4L_HPMIXR_VOL - [5:3] */ +#define WM8962_IN4L_HPMIXR_VOL_SHIFT 3 /* IN4L_HPMIXR_VOL - [5:3] */ +#define WM8962_IN4L_HPMIXR_VOL_WIDTH 3 /* IN4L_HPMIXR_VOL - [5:3] */ +#define WM8962_IN4R_HPMIXR_VOL_MASK 0x0007 /* IN4R_HPMIXR_VOL - [2:0] */ +#define WM8962_IN4R_HPMIXR_VOL_SHIFT 0 /* IN4R_HPMIXR_VOL - [2:0] */ +#define WM8962_IN4R_HPMIXR_VOL_WIDTH 3 /* IN4R_HPMIXR_VOL - [2:0] */ + +/* + * R105 (0x69) - Speaker Mixer (1) + */ +#define WM8962_SPKMIXL_TO_SPKOUTL_PGA 0x0080 /* SPKMIXL_TO_SPKOUTL_PGA */ +#define WM8962_SPKMIXL_TO_SPKOUTL_PGA_MASK 0x0080 /* SPKMIXL_TO_SPKOUTL_PGA */ +#define WM8962_SPKMIXL_TO_SPKOUTL_PGA_SHIFT 7 /* SPKMIXL_TO_SPKOUTL_PGA */ +#define WM8962_SPKMIXL_TO_SPKOUTL_PGA_WIDTH 1 /* SPKMIXL_TO_SPKOUTL_PGA */ +#define WM8962_DACL_TO_SPKMIXL 0x0020 /* DACL_TO_SPKMIXL */ +#define WM8962_DACL_TO_SPKMIXL_MASK 0x0020 /* DACL_TO_SPKMIXL */ +#define WM8962_DACL_TO_SPKMIXL_SHIFT 5 /* DACL_TO_SPKMIXL */ +#define WM8962_DACL_TO_SPKMIXL_WIDTH 1 /* DACL_TO_SPKMIXL */ +#define WM8962_DACR_TO_SPKMIXL 0x0010 /* DACR_TO_SPKMIXL */ +#define WM8962_DACR_TO_SPKMIXL_MASK 0x0010 /* DACR_TO_SPKMIXL */ +#define WM8962_DACR_TO_SPKMIXL_SHIFT 4 /* DACR_TO_SPKMIXL */ +#define WM8962_DACR_TO_SPKMIXL_WIDTH 1 /* DACR_TO_SPKMIXL */ +#define WM8962_MIXINL_TO_SPKMIXL 0x0008 /* MIXINL_TO_SPKMIXL */ +#define WM8962_MIXINL_TO_SPKMIXL_MASK 0x0008 /* MIXINL_TO_SPKMIXL */ +#define WM8962_MIXINL_TO_SPKMIXL_SHIFT 3 /* MIXINL_TO_SPKMIXL */ +#define WM8962_MIXINL_TO_SPKMIXL_WIDTH 1 /* MIXINL_TO_SPKMIXL */ +#define WM8962_MIXINR_TO_SPKMIXL 0x0004 /* MIXINR_TO_SPKMIXL */ +#define WM8962_MIXINR_TO_SPKMIXL_MASK 0x0004 /* MIXINR_TO_SPKMIXL */ +#define WM8962_MIXINR_TO_SPKMIXL_SHIFT 2 /* MIXINR_TO_SPKMIXL */ +#define WM8962_MIXINR_TO_SPKMIXL_WIDTH 1 /* MIXINR_TO_SPKMIXL */ +#define WM8962_IN4L_TO_SPKMIXL 0x0002 /* IN4L_TO_SPKMIXL */ +#define WM8962_IN4L_TO_SPKMIXL_MASK 0x0002 /* IN4L_TO_SPKMIXL */ +#define WM8962_IN4L_TO_SPKMIXL_SHIFT 1 /* IN4L_TO_SPKMIXL */ +#define WM8962_IN4L_TO_SPKMIXL_WIDTH 1 /* IN4L_TO_SPKMIXL */ +#define WM8962_IN4R_TO_SPKMIXL 0x0001 /* IN4R_TO_SPKMIXL */ +#define WM8962_IN4R_TO_SPKMIXL_MASK 0x0001 /* IN4R_TO_SPKMIXL */ +#define WM8962_IN4R_TO_SPKMIXL_SHIFT 0 /* IN4R_TO_SPKMIXL */ +#define WM8962_IN4R_TO_SPKMIXL_WIDTH 1 /* IN4R_TO_SPKMIXL */ + +/* + * R106 (0x6A) - Speaker Mixer (2) + */ +#define WM8962_SPKMIXR_TO_SPKOUTR_PGA 0x0080 /* SPKMIXR_TO_SPKOUTR_PGA */ +#define WM8962_SPKMIXR_TO_SPKOUTR_PGA_MASK 0x0080 /* SPKMIXR_TO_SPKOUTR_PGA */ +#define WM8962_SPKMIXR_TO_SPKOUTR_PGA_SHIFT 7 /* SPKMIXR_TO_SPKOUTR_PGA */ +#define WM8962_SPKMIXR_TO_SPKOUTR_PGA_WIDTH 1 /* SPKMIXR_TO_SPKOUTR_PGA */ +#define WM8962_DACL_TO_SPKMIXR 0x0020 /* DACL_TO_SPKMIXR */ +#define WM8962_DACL_TO_SPKMIXR_MASK 0x0020 /* DACL_TO_SPKMIXR */ +#define WM8962_DACL_TO_SPKMIXR_SHIFT 5 /* DACL_TO_SPKMIXR */ +#define WM8962_DACL_TO_SPKMIXR_WIDTH 1 /* DACL_TO_SPKMIXR */ +#define WM8962_DACR_TO_SPKMIXR 0x0010 /* DACR_TO_SPKMIXR */ +#define WM8962_DACR_TO_SPKMIXR_MASK 0x0010 /* DACR_TO_SPKMIXR */ +#define WM8962_DACR_TO_SPKMIXR_SHIFT 4 /* DACR_TO_SPKMIXR */ +#define WM8962_DACR_TO_SPKMIXR_WIDTH 1 /* DACR_TO_SPKMIXR */ +#define WM8962_MIXINL_TO_SPKMIXR 0x0008 /* MIXINL_TO_SPKMIXR */ +#define WM8962_MIXINL_TO_SPKMIXR_MASK 0x0008 /* MIXINL_TO_SPKMIXR */ +#define WM8962_MIXINL_TO_SPKMIXR_SHIFT 3 /* MIXINL_TO_SPKMIXR */ +#define WM8962_MIXINL_TO_SPKMIXR_WIDTH 1 /* MIXINL_TO_SPKMIXR */ +#define WM8962_MIXINR_TO_SPKMIXR 0x0004 /* MIXINR_TO_SPKMIXR */ +#define WM8962_MIXINR_TO_SPKMIXR_MASK 0x0004 /* MIXINR_TO_SPKMIXR */ +#define WM8962_MIXINR_TO_SPKMIXR_SHIFT 2 /* MIXINR_TO_SPKMIXR */ +#define WM8962_MIXINR_TO_SPKMIXR_WIDTH 1 /* MIXINR_TO_SPKMIXR */ +#define WM8962_IN4L_TO_SPKMIXR 0x0002 /* IN4L_TO_SPKMIXR */ +#define WM8962_IN4L_TO_SPKMIXR_MASK 0x0002 /* IN4L_TO_SPKMIXR */ +#define WM8962_IN4L_TO_SPKMIXR_SHIFT 1 /* IN4L_TO_SPKMIXR */ +#define WM8962_IN4L_TO_SPKMIXR_WIDTH 1 /* IN4L_TO_SPKMIXR */ +#define WM8962_IN4R_TO_SPKMIXR 0x0001 /* IN4R_TO_SPKMIXR */ +#define WM8962_IN4R_TO_SPKMIXR_MASK 0x0001 /* IN4R_TO_SPKMIXR */ +#define WM8962_IN4R_TO_SPKMIXR_SHIFT 0 /* IN4R_TO_SPKMIXR */ +#define WM8962_IN4R_TO_SPKMIXR_WIDTH 1 /* IN4R_TO_SPKMIXR */ + +/* + * R107 (0x6B) - Speaker Mixer (3) + */ +#define WM8962_SPKMIXL_MUTE 0x0100 /* SPKMIXL_MUTE */ +#define WM8962_SPKMIXL_MUTE_MASK 0x0100 /* SPKMIXL_MUTE */ +#define WM8962_SPKMIXL_MUTE_SHIFT 8 /* SPKMIXL_MUTE */ +#define WM8962_SPKMIXL_MUTE_WIDTH 1 /* SPKMIXL_MUTE */ +#define WM8962_MIXINL_SPKMIXL_VOL 0x0080 /* MIXINL_SPKMIXL_VOL */ +#define WM8962_MIXINL_SPKMIXL_VOL_MASK 0x0080 /* MIXINL_SPKMIXL_VOL */ +#define WM8962_MIXINL_SPKMIXL_VOL_SHIFT 7 /* MIXINL_SPKMIXL_VOL */ +#define WM8962_MIXINL_SPKMIXL_VOL_WIDTH 1 /* MIXINL_SPKMIXL_VOL */ +#define WM8962_MIXINR_SPKMIXL_VOL 0x0040 /* MIXINR_SPKMIXL_VOL */ +#define WM8962_MIXINR_SPKMIXL_VOL_MASK 0x0040 /* MIXINR_SPKMIXL_VOL */ +#define WM8962_MIXINR_SPKMIXL_VOL_SHIFT 6 /* MIXINR_SPKMIXL_VOL */ +#define WM8962_MIXINR_SPKMIXL_VOL_WIDTH 1 /* MIXINR_SPKMIXL_VOL */ +#define WM8962_IN4L_SPKMIXL_VOL_MASK 0x0038 /* IN4L_SPKMIXL_VOL - [5:3] */ +#define WM8962_IN4L_SPKMIXL_VOL_SHIFT 3 /* IN4L_SPKMIXL_VOL - [5:3] */ +#define WM8962_IN4L_SPKMIXL_VOL_WIDTH 3 /* IN4L_SPKMIXL_VOL - [5:3] */ +#define WM8962_IN4R_SPKMIXL_VOL_MASK 0x0007 /* IN4R_SPKMIXL_VOL - [2:0] */ +#define WM8962_IN4R_SPKMIXL_VOL_SHIFT 0 /* IN4R_SPKMIXL_VOL - [2:0] */ +#define WM8962_IN4R_SPKMIXL_VOL_WIDTH 3 /* IN4R_SPKMIXL_VOL - [2:0] */ + +/* + * R108 (0x6C) - Speaker Mixer (4) + */ +#define WM8962_SPKMIXR_MUTE 0x0100 /* SPKMIXR_MUTE */ +#define WM8962_SPKMIXR_MUTE_MASK 0x0100 /* SPKMIXR_MUTE */ +#define WM8962_SPKMIXR_MUTE_SHIFT 8 /* SPKMIXR_MUTE */ +#define WM8962_SPKMIXR_MUTE_WIDTH 1 /* SPKMIXR_MUTE */ +#define WM8962_MIXINL_SPKMIXR_VOL 0x0080 /* MIXINL_SPKMIXR_VOL */ +#define WM8962_MIXINL_SPKMIXR_VOL_MASK 0x0080 /* MIXINL_SPKMIXR_VOL */ +#define WM8962_MIXINL_SPKMIXR_VOL_SHIFT 7 /* MIXINL_SPKMIXR_VOL */ +#define WM8962_MIXINL_SPKMIXR_VOL_WIDTH 1 /* MIXINL_SPKMIXR_VOL */ +#define WM8962_MIXINR_SPKMIXR_VOL 0x0040 /* MIXINR_SPKMIXR_VOL */ +#define WM8962_MIXINR_SPKMIXR_VOL_MASK 0x0040 /* MIXINR_SPKMIXR_VOL */ +#define WM8962_MIXINR_SPKMIXR_VOL_SHIFT 6 /* MIXINR_SPKMIXR_VOL */ +#define WM8962_MIXINR_SPKMIXR_VOL_WIDTH 1 /* MIXINR_SPKMIXR_VOL */ +#define WM8962_IN4L_SPKMIXR_VOL_MASK 0x0038 /* IN4L_SPKMIXR_VOL - [5:3] */ +#define WM8962_IN4L_SPKMIXR_VOL_SHIFT 3 /* IN4L_SPKMIXR_VOL - [5:3] */ +#define WM8962_IN4L_SPKMIXR_VOL_WIDTH 3 /* IN4L_SPKMIXR_VOL - [5:3] */ +#define WM8962_IN4R_SPKMIXR_VOL_MASK 0x0007 /* IN4R_SPKMIXR_VOL - [2:0] */ +#define WM8962_IN4R_SPKMIXR_VOL_SHIFT 0 /* IN4R_SPKMIXR_VOL - [2:0] */ +#define WM8962_IN4R_SPKMIXR_VOL_WIDTH 3 /* IN4R_SPKMIXR_VOL - [2:0] */ + +/* + * R109 (0x6D) - Speaker Mixer (5) + */ +#define WM8962_DACL_SPKMIXL_VOL 0x0080 /* DACL_SPKMIXL_VOL */ +#define WM8962_DACL_SPKMIXL_VOL_MASK 0x0080 /* DACL_SPKMIXL_VOL */ +#define WM8962_DACL_SPKMIXL_VOL_SHIFT 7 /* DACL_SPKMIXL_VOL */ +#define WM8962_DACL_SPKMIXL_VOL_WIDTH 1 /* DACL_SPKMIXL_VOL */ +#define WM8962_DACR_SPKMIXL_VOL 0x0040 /* DACR_SPKMIXL_VOL */ +#define WM8962_DACR_SPKMIXL_VOL_MASK 0x0040 /* DACR_SPKMIXL_VOL */ +#define WM8962_DACR_SPKMIXL_VOL_SHIFT 6 /* DACR_SPKMIXL_VOL */ +#define WM8962_DACR_SPKMIXL_VOL_WIDTH 1 /* DACR_SPKMIXL_VOL */ +#define WM8962_DACL_SPKMIXR_VOL 0x0020 /* DACL_SPKMIXR_VOL */ +#define WM8962_DACL_SPKMIXR_VOL_MASK 0x0020 /* DACL_SPKMIXR_VOL */ +#define WM8962_DACL_SPKMIXR_VOL_SHIFT 5 /* DACL_SPKMIXR_VOL */ +#define WM8962_DACL_SPKMIXR_VOL_WIDTH 1 /* DACL_SPKMIXR_VOL */ +#define WM8962_DACR_SPKMIXR_VOL 0x0010 /* DACR_SPKMIXR_VOL */ +#define WM8962_DACR_SPKMIXR_VOL_MASK 0x0010 /* DACR_SPKMIXR_VOL */ +#define WM8962_DACR_SPKMIXR_VOL_SHIFT 4 /* DACR_SPKMIXR_VOL */ +#define WM8962_DACR_SPKMIXR_VOL_WIDTH 1 /* DACR_SPKMIXR_VOL */ + +/* + * R110 (0x6E) - Beep Generator (1) + */ +#define WM8962_BEEP_GAIN_MASK 0x00F0 /* BEEP_GAIN - [7:4] */ +#define WM8962_BEEP_GAIN_SHIFT 4 /* BEEP_GAIN - [7:4] */ +#define WM8962_BEEP_GAIN_WIDTH 4 /* BEEP_GAIN - [7:4] */ +#define WM8962_BEEP_RATE_MASK 0x0006 /* BEEP_RATE - [2:1] */ +#define WM8962_BEEP_RATE_SHIFT 1 /* BEEP_RATE - [2:1] */ +#define WM8962_BEEP_RATE_WIDTH 2 /* BEEP_RATE - [2:1] */ +#define WM8962_BEEP_ENA 0x0001 /* BEEP_ENA */ +#define WM8962_BEEP_ENA_MASK 0x0001 /* BEEP_ENA */ +#define WM8962_BEEP_ENA_SHIFT 0 /* BEEP_ENA */ +#define WM8962_BEEP_ENA_WIDTH 1 /* BEEP_ENA */ + +/* + * R115 (0x73) - Oscillator Trim (3) + */ +#define WM8962_OSC_TRIM_XTI_MASK 0x001F /* OSC_TRIM_XTI - [4:0] */ +#define WM8962_OSC_TRIM_XTI_SHIFT 0 /* OSC_TRIM_XTI - [4:0] */ +#define WM8962_OSC_TRIM_XTI_WIDTH 5 /* OSC_TRIM_XTI - [4:0] */ + +/* + * R116 (0x74) - Oscillator Trim (4) + */ +#define WM8962_OSC_TRIM_XTO_MASK 0x001F /* OSC_TRIM_XTO - [4:0] */ +#define WM8962_OSC_TRIM_XTO_SHIFT 0 /* OSC_TRIM_XTO - [4:0] */ +#define WM8962_OSC_TRIM_XTO_WIDTH 5 /* OSC_TRIM_XTO - [4:0] */ + +/* + * R119 (0x77) - Oscillator Trim (7) + */ +#define WM8962_XTO_CAP_SEL_MASK 0x00F0 /* XTO_CAP_SEL - [7:4] */ +#define WM8962_XTO_CAP_SEL_SHIFT 4 /* XTO_CAP_SEL - [7:4] */ +#define WM8962_XTO_CAP_SEL_WIDTH 4 /* XTO_CAP_SEL - [7:4] */ +#define WM8962_XTI_CAP_SEL_MASK 0x000F /* XTI_CAP_SEL - [3:0] */ +#define WM8962_XTI_CAP_SEL_SHIFT 0 /* XTI_CAP_SEL - [3:0] */ +#define WM8962_XTI_CAP_SEL_WIDTH 4 /* XTI_CAP_SEL - [3:0] */ + +/* + * R124 (0x7C) - Analogue Clocking1 + */ +#define WM8962_CLKOUT2_SEL_MASK 0x0060 /* CLKOUT2_SEL - [6:5] */ +#define WM8962_CLKOUT2_SEL_SHIFT 5 /* CLKOUT2_SEL - [6:5] */ +#define WM8962_CLKOUT2_SEL_WIDTH 2 /* CLKOUT2_SEL - [6:5] */ +#define WM8962_CLKOUT3_SEL_MASK 0x0018 /* CLKOUT3_SEL - [4:3] */ +#define WM8962_CLKOUT3_SEL_SHIFT 3 /* CLKOUT3_SEL - [4:3] */ +#define WM8962_CLKOUT3_SEL_WIDTH 2 /* CLKOUT3_SEL - [4:3] */ +#define WM8962_CLKOUT5_SEL 0x0001 /* CLKOUT5_SEL */ +#define WM8962_CLKOUT5_SEL_MASK 0x0001 /* CLKOUT5_SEL */ +#define WM8962_CLKOUT5_SEL_SHIFT 0 /* CLKOUT5_SEL */ +#define WM8962_CLKOUT5_SEL_WIDTH 1 /* CLKOUT5_SEL */ + +/* + * R125 (0x7D) - Analogue Clocking2 + */ +#define WM8962_PLL2_OUTDIV 0x0080 /* PLL2_OUTDIV */ +#define WM8962_PLL2_OUTDIV_MASK 0x0080 /* PLL2_OUTDIV */ +#define WM8962_PLL2_OUTDIV_SHIFT 7 /* PLL2_OUTDIV */ +#define WM8962_PLL2_OUTDIV_WIDTH 1 /* PLL2_OUTDIV */ +#define WM8962_PLL3_OUTDIV 0x0040 /* PLL3_OUTDIV */ +#define WM8962_PLL3_OUTDIV_MASK 0x0040 /* PLL3_OUTDIV */ +#define WM8962_PLL3_OUTDIV_SHIFT 6 /* PLL3_OUTDIV */ +#define WM8962_PLL3_OUTDIV_WIDTH 1 /* PLL3_OUTDIV */ +#define WM8962_PLL_SYSCLK_DIV_MASK 0x0018 /* PLL_SYSCLK_DIV - [4:3] */ +#define WM8962_PLL_SYSCLK_DIV_SHIFT 3 /* PLL_SYSCLK_DIV - [4:3] */ +#define WM8962_PLL_SYSCLK_DIV_WIDTH 2 /* PLL_SYSCLK_DIV - [4:3] */ +#define WM8962_CLKOUT3_DIV 0x0004 /* CLKOUT3_DIV */ +#define WM8962_CLKOUT3_DIV_MASK 0x0004 /* CLKOUT3_DIV */ +#define WM8962_CLKOUT3_DIV_SHIFT 2 /* CLKOUT3_DIV */ +#define WM8962_CLKOUT3_DIV_WIDTH 1 /* CLKOUT3_DIV */ +#define WM8962_CLKOUT2_DIV 0x0002 /* CLKOUT2_DIV */ +#define WM8962_CLKOUT2_DIV_MASK 0x0002 /* CLKOUT2_DIV */ +#define WM8962_CLKOUT2_DIV_SHIFT 1 /* CLKOUT2_DIV */ +#define WM8962_CLKOUT2_DIV_WIDTH 1 /* CLKOUT2_DIV */ +#define WM8962_CLKOUT5_DIV 0x0001 /* CLKOUT5_DIV */ +#define WM8962_CLKOUT5_DIV_MASK 0x0001 /* CLKOUT5_DIV */ +#define WM8962_CLKOUT5_DIV_SHIFT 0 /* CLKOUT5_DIV */ +#define WM8962_CLKOUT5_DIV_WIDTH 1 /* CLKOUT5_DIV */ + +/* + * R126 (0x7E) - Analogue Clocking3 + */ +#define WM8962_CLKOUT2_OE 0x0008 /* CLKOUT2_OE */ +#define WM8962_CLKOUT2_OE_MASK 0x0008 /* CLKOUT2_OE */ +#define WM8962_CLKOUT2_OE_SHIFT 3 /* CLKOUT2_OE */ +#define WM8962_CLKOUT2_OE_WIDTH 1 /* CLKOUT2_OE */ +#define WM8962_CLKOUT3_OE 0x0004 /* CLKOUT3_OE */ +#define WM8962_CLKOUT3_OE_MASK 0x0004 /* CLKOUT3_OE */ +#define WM8962_CLKOUT3_OE_SHIFT 2 /* CLKOUT3_OE */ +#define WM8962_CLKOUT3_OE_WIDTH 1 /* CLKOUT3_OE */ +#define WM8962_CLKOUT5_OE 0x0001 /* CLKOUT5_OE */ +#define WM8962_CLKOUT5_OE_MASK 0x0001 /* CLKOUT5_OE */ +#define WM8962_CLKOUT5_OE_SHIFT 0 /* CLKOUT5_OE */ +#define WM8962_CLKOUT5_OE_WIDTH 1 /* CLKOUT5_OE */ + +/* + * R127 (0x7F) - PLL Software Reset + */ +#define WM8962_SW_RESET_PLL_MASK 0xFFFF /* SW_RESET_PLL - [15:0] */ +#define WM8962_SW_RESET_PLL_SHIFT 0 /* SW_RESET_PLL - [15:0] */ +#define WM8962_SW_RESET_PLL_WIDTH 16 /* SW_RESET_PLL - [15:0] */ + +/* + * R129 (0x81) - PLL2 + */ +#define WM8962_OSC_ENA 0x0080 /* OSC_ENA */ +#define WM8962_OSC_ENA_MASK 0x0080 /* OSC_ENA */ +#define WM8962_OSC_ENA_SHIFT 7 /* OSC_ENA */ +#define WM8962_OSC_ENA_WIDTH 1 /* OSC_ENA */ +#define WM8962_PLL2_ENA 0x0020 /* PLL2_ENA */ +#define WM8962_PLL2_ENA_MASK 0x0020 /* PLL2_ENA */ +#define WM8962_PLL2_ENA_SHIFT 5 /* PLL2_ENA */ +#define WM8962_PLL2_ENA_WIDTH 1 /* PLL2_ENA */ +#define WM8962_PLL3_ENA 0x0010 /* PLL3_ENA */ +#define WM8962_PLL3_ENA_MASK 0x0010 /* PLL3_ENA */ +#define WM8962_PLL3_ENA_SHIFT 4 /* PLL3_ENA */ +#define WM8962_PLL3_ENA_WIDTH 1 /* PLL3_ENA */ + +/* + * R131 (0x83) - PLL 4 + */ +#define WM8962_PLL_CLK_SRC 0x0002 /* PLL_CLK_SRC */ +#define WM8962_PLL_CLK_SRC_MASK 0x0002 /* PLL_CLK_SRC */ +#define WM8962_PLL_CLK_SRC_SHIFT 1 /* PLL_CLK_SRC */ +#define WM8962_PLL_CLK_SRC_WIDTH 1 /* PLL_CLK_SRC */ +#define WM8962_FLL_TO_PLL3 0x0001 /* FLL_TO_PLL3 */ +#define WM8962_FLL_TO_PLL3_MASK 0x0001 /* FLL_TO_PLL3 */ +#define WM8962_FLL_TO_PLL3_SHIFT 0 /* FLL_TO_PLL3 */ +#define WM8962_FLL_TO_PLL3_WIDTH 1 /* FLL_TO_PLL3 */ + +/* + * R136 (0x88) - PLL 9 + */ +#define WM8962_PLL2_FRAC 0x0040 /* PLL2_FRAC */ +#define WM8962_PLL2_FRAC_MASK 0x0040 /* PLL2_FRAC */ +#define WM8962_PLL2_FRAC_SHIFT 6 /* PLL2_FRAC */ +#define WM8962_PLL2_FRAC_WIDTH 1 /* PLL2_FRAC */ +#define WM8962_PLL2_N_MASK 0x001F /* PLL2_N - [4:0] */ +#define WM8962_PLL2_N_SHIFT 0 /* PLL2_N - [4:0] */ +#define WM8962_PLL2_N_WIDTH 5 /* PLL2_N - [4:0] */ + +/* + * R137 (0x89) - PLL 10 + */ +#define WM8962_PLL2_K_MASK 0x00FF /* PLL2_K - [7:0] */ +#define WM8962_PLL2_K_SHIFT 0 /* PLL2_K - [7:0] */ +#define WM8962_PLL2_K_WIDTH 8 /* PLL2_K - [7:0] */ + +/* + * R138 (0x8A) - PLL 11 + */ +#define WM8962_PLL2_K_MASK 0x00FF /* PLL2_K - [7:0] */ +#define WM8962_PLL2_K_SHIFT 0 /* PLL2_K - [7:0] */ +#define WM8962_PLL2_K_WIDTH 8 /* PLL2_K - [7:0] */ + +/* + * R139 (0x8B) - PLL 12 + */ +#define WM8962_PLL2_K_MASK 0x00FF /* PLL2_K - [7:0] */ +#define WM8962_PLL2_K_SHIFT 0 /* PLL2_K - [7:0] */ +#define WM8962_PLL2_K_WIDTH 8 /* PLL2_K - [7:0] */ + +/* + * R140 (0x8C) - PLL 13 + */ +#define WM8962_PLL3_FRAC 0x0040 /* PLL3_FRAC */ +#define WM8962_PLL3_FRAC_MASK 0x0040 /* PLL3_FRAC */ +#define WM8962_PLL3_FRAC_SHIFT 6 /* PLL3_FRAC */ +#define WM8962_PLL3_FRAC_WIDTH 1 /* PLL3_FRAC */ +#define WM8962_PLL3_N_MASK 0x001F /* PLL3_N - [4:0] */ +#define WM8962_PLL3_N_SHIFT 0 /* PLL3_N - [4:0] */ +#define WM8962_PLL3_N_WIDTH 5 /* PLL3_N - [4:0] */ + +/* + * R141 (0x8D) - PLL 14 + */ +#define WM8962_PLL3_K_MASK 0x00FF /* PLL3_K - [7:0] */ +#define WM8962_PLL3_K_SHIFT 0 /* PLL3_K - [7:0] */ +#define WM8962_PLL3_K_WIDTH 8 /* PLL3_K - [7:0] */ + +/* + * R142 (0x8E) - PLL 15 + */ +#define WM8962_PLL3_K_MASK 0x00FF /* PLL3_K - [7:0] */ +#define WM8962_PLL3_K_SHIFT 0 /* PLL3_K - [7:0] */ +#define WM8962_PLL3_K_WIDTH 8 /* PLL3_K - [7:0] */ + +/* + * R143 (0x8F) - PLL 16 + */ +#define WM8962_PLL3_K_MASK 0x00FF /* PLL3_K - [7:0] */ +#define WM8962_PLL3_K_SHIFT 0 /* PLL3_K - [7:0] */ +#define WM8962_PLL3_K_WIDTH 8 /* PLL3_K - [7:0] */ + +/* + * R155 (0x9B) - FLL Control (1) + */ +#define WM8962_FLL_REFCLK_SRC_MASK 0x0060 /* FLL_REFCLK_SRC - [6:5] */ +#define WM8962_FLL_REFCLK_SRC_SHIFT 5 /* FLL_REFCLK_SRC - [6:5] */ +#define WM8962_FLL_REFCLK_SRC_WIDTH 2 /* FLL_REFCLK_SRC - [6:5] */ +#define WM8962_FLL_FRAC 0x0004 /* FLL_FRAC */ +#define WM8962_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */ +#define WM8962_FLL_FRAC_SHIFT 2 /* FLL_FRAC */ +#define WM8962_FLL_FRAC_WIDTH 1 /* FLL_FRAC */ +#define WM8962_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */ +#define WM8962_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */ +#define WM8962_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */ +#define WM8962_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */ +#define WM8962_FLL_ENA 0x0001 /* FLL_ENA */ +#define WM8962_FLL_ENA_MASK 0x0001 /* FLL_ENA */ +#define WM8962_FLL_ENA_SHIFT 0 /* FLL_ENA */ +#define WM8962_FLL_ENA_WIDTH 1 /* FLL_ENA */ + +/* + * R156 (0x9C) - FLL Control (2) + */ +#define WM8962_FLL_OUTDIV_MASK 0x01F8 /* FLL_OUTDIV - [8:3] */ +#define WM8962_FLL_OUTDIV_SHIFT 3 /* FLL_OUTDIV - [8:3] */ +#define WM8962_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [8:3] */ +#define WM8962_FLL_REFCLK_DIV_MASK 0x0003 /* FLL_REFCLK_DIV - [1:0] */ +#define WM8962_FLL_REFCLK_DIV_SHIFT 0 /* FLL_REFCLK_DIV - [1:0] */ +#define WM8962_FLL_REFCLK_DIV_WIDTH 2 /* FLL_REFCLK_DIV - [1:0] */ + +/* + * R157 (0x9D) - FLL Control (3) + */ +#define WM8962_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */ +#define WM8962_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */ +#define WM8962_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */ + +/* + * R159 (0x9F) - FLL Control (5) + */ +#define WM8962_FLL_FRC_NCO_VAL_MASK 0x007E /* FLL_FRC_NCO_VAL - [6:1] */ +#define WM8962_FLL_FRC_NCO_VAL_SHIFT 1 /* FLL_FRC_NCO_VAL - [6:1] */ +#define WM8962_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [6:1] */ +#define WM8962_FLL_FRC_NCO 0x0001 /* FLL_FRC_NCO */ +#define WM8962_FLL_FRC_NCO_MASK 0x0001 /* FLL_FRC_NCO */ +#define WM8962_FLL_FRC_NCO_SHIFT 0 /* FLL_FRC_NCO */ +#define WM8962_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */ + +/* + * R160 (0xA0) - FLL Control (6) + */ +#define WM8962_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */ +#define WM8962_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */ +#define WM8962_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */ + +/* + * R161 (0xA1) - FLL Control (7) + */ +#define WM8962_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */ +#define WM8962_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */ +#define WM8962_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */ + +/* + * R162 (0xA2) - FLL Control (8) + */ +#define WM8962_FLL_N_MASK 0x03FF /* FLL_N - [9:0] */ +#define WM8962_FLL_N_SHIFT 0 /* FLL_N - [9:0] */ +#define WM8962_FLL_N_WIDTH 10 /* FLL_N - [9:0] */ + +/* + * R252 (0xFC) - General test 1 + */ +#define WM8962_REG_SYNC 0x0004 /* REG_SYNC */ +#define WM8962_REG_SYNC_MASK 0x0004 /* REG_SYNC */ +#define WM8962_REG_SYNC_SHIFT 2 /* REG_SYNC */ +#define WM8962_REG_SYNC_WIDTH 1 /* REG_SYNC */ +#define WM8962_AUTO_INC 0x0001 /* AUTO_INC */ +#define WM8962_AUTO_INC_MASK 0x0001 /* AUTO_INC */ +#define WM8962_AUTO_INC_SHIFT 0 /* AUTO_INC */ +#define WM8962_AUTO_INC_WIDTH 1 /* AUTO_INC */ + +/* + * R256 (0x100) - DF1 + */ +#define WM8962_DRC_DF1_ENA 0x0008 /* DRC_DF1_ENA */ +#define WM8962_DRC_DF1_ENA_MASK 0x0008 /* DRC_DF1_ENA */ +#define WM8962_DRC_DF1_ENA_SHIFT 3 /* DRC_DF1_ENA */ +#define WM8962_DRC_DF1_ENA_WIDTH 1 /* DRC_DF1_ENA */ +#define WM8962_DF1_SHARED_COEFF 0x0004 /* DF1_SHARED_COEFF */ +#define WM8962_DF1_SHARED_COEFF_MASK 0x0004 /* DF1_SHARED_COEFF */ +#define WM8962_DF1_SHARED_COEFF_SHIFT 2 /* DF1_SHARED_COEFF */ +#define WM8962_DF1_SHARED_COEFF_WIDTH 1 /* DF1_SHARED_COEFF */ +#define WM8962_DF1_SHARED_COEFF_SEL 0x0002 /* DF1_SHARED_COEFF_SEL */ +#define WM8962_DF1_SHARED_COEFF_SEL_MASK 0x0002 /* DF1_SHARED_COEFF_SEL */ +#define WM8962_DF1_SHARED_COEFF_SEL_SHIFT 1 /* DF1_SHARED_COEFF_SEL */ +#define WM8962_DF1_SHARED_COEFF_SEL_WIDTH 1 /* DF1_SHARED_COEFF_SEL */ +#define WM8962_DF1_ENA 0x0001 /* DF1_ENA */ +#define WM8962_DF1_ENA_MASK 0x0001 /* DF1_ENA */ +#define WM8962_DF1_ENA_SHIFT 0 /* DF1_ENA */ +#define WM8962_DF1_ENA_WIDTH 1 /* DF1_ENA */ + +/* + * R257 (0x101) - DF2 + */ +#define WM8962_DF1_COEFF_L0_MASK 0xFFFF /* DF1_COEFF_L0 - [15:0] */ +#define WM8962_DF1_COEFF_L0_SHIFT 0 /* DF1_COEFF_L0 - [15:0] */ +#define WM8962_DF1_COEFF_L0_WIDTH 16 /* DF1_COEFF_L0 - [15:0] */ + +/* + * R258 (0x102) - DF3 + */ +#define WM8962_DF1_COEFF_L1_MASK 0xFFFF /* DF1_COEFF_L1 - [15:0] */ +#define WM8962_DF1_COEFF_L1_SHIFT 0 /* DF1_COEFF_L1 - [15:0] */ +#define WM8962_DF1_COEFF_L1_WIDTH 16 /* DF1_COEFF_L1 - [15:0] */ + +/* + * R259 (0x103) - DF4 + */ +#define WM8962_DF1_COEFF_L2_MASK 0xFFFF /* DF1_COEFF_L2 - [15:0] */ +#define WM8962_DF1_COEFF_L2_SHIFT 0 /* DF1_COEFF_L2 - [15:0] */ +#define WM8962_DF1_COEFF_L2_WIDTH 16 /* DF1_COEFF_L2 - [15:0] */ + +/* + * R260 (0x104) - DF5 + */ +#define WM8962_DF1_COEFF_R0_MASK 0xFFFF /* DF1_COEFF_R0 - [15:0] */ +#define WM8962_DF1_COEFF_R0_SHIFT 0 /* DF1_COEFF_R0 - [15:0] */ +#define WM8962_DF1_COEFF_R0_WIDTH 16 /* DF1_COEFF_R0 - [15:0] */ + +/* + * R261 (0x105) - DF6 + */ +#define WM8962_DF1_COEFF_R1_MASK 0xFFFF /* DF1_COEFF_R1 - [15:0] */ +#define WM8962_DF1_COEFF_R1_SHIFT 0 /* DF1_COEFF_R1 - [15:0] */ +#define WM8962_DF1_COEFF_R1_WIDTH 16 /* DF1_COEFF_R1 - [15:0] */ + +/* + * R262 (0x106) - DF7 + */ +#define WM8962_DF1_COEFF_R2_MASK 0xFFFF /* DF1_COEFF_R2 - [15:0] */ +#define WM8962_DF1_COEFF_R2_SHIFT 0 /* DF1_COEFF_R2 - [15:0] */ +#define WM8962_DF1_COEFF_R2_WIDTH 16 /* DF1_COEFF_R2 - [15:0] */ + +/* + * R264 (0x108) - LHPF1 + */ +#define WM8962_LHPF_MODE 0x0002 /* LHPF_MODE */ +#define WM8962_LHPF_MODE_MASK 0x0002 /* LHPF_MODE */ +#define WM8962_LHPF_MODE_SHIFT 1 /* LHPF_MODE */ +#define WM8962_LHPF_MODE_WIDTH 1 /* LHPF_MODE */ +#define WM8962_LHPF_ENA 0x0001 /* LHPF_ENA */ +#define WM8962_LHPF_ENA_MASK 0x0001 /* LHPF_ENA */ +#define WM8962_LHPF_ENA_SHIFT 0 /* LHPF_ENA */ +#define WM8962_LHPF_ENA_WIDTH 1 /* LHPF_ENA */ + +/* + * R265 (0x109) - LHPF2 + */ +#define WM8962_LHPF_COEFF_MASK 0xFFFF /* LHPF_COEFF - [15:0] */ +#define WM8962_LHPF_COEFF_SHIFT 0 /* LHPF_COEFF - [15:0] */ +#define WM8962_LHPF_COEFF_WIDTH 16 /* LHPF_COEFF - [15:0] */ + +/* + * R268 (0x10C) - THREED1 + */ +#define WM8962_ADC_MONOMIX 0x0040 /* ADC_MONOMIX */ +#define WM8962_ADC_MONOMIX_MASK 0x0040 /* ADC_MONOMIX */ +#define WM8962_ADC_MONOMIX_SHIFT 6 /* ADC_MONOMIX */ +#define WM8962_ADC_MONOMIX_WIDTH 1 /* ADC_MONOMIX */ +#define WM8962_THREED_SIGN_L 0x0020 /* THREED_SIGN_L */ +#define WM8962_THREED_SIGN_L_MASK 0x0020 /* THREED_SIGN_L */ +#define WM8962_THREED_SIGN_L_SHIFT 5 /* THREED_SIGN_L */ +#define WM8962_THREED_SIGN_L_WIDTH 1 /* THREED_SIGN_L */ +#define WM8962_THREED_SIGN_R 0x0010 /* THREED_SIGN_R */ +#define WM8962_THREED_SIGN_R_MASK 0x0010 /* THREED_SIGN_R */ +#define WM8962_THREED_SIGN_R_SHIFT 4 /* THREED_SIGN_R */ +#define WM8962_THREED_SIGN_R_WIDTH 1 /* THREED_SIGN_R */ +#define WM8962_THREED_LHPF_MODE 0x0004 /* THREED_LHPF_MODE */ +#define WM8962_THREED_LHPF_MODE_MASK 0x0004 /* THREED_LHPF_MODE */ +#define WM8962_THREED_LHPF_MODE_SHIFT 2 /* THREED_LHPF_MODE */ +#define WM8962_THREED_LHPF_MODE_WIDTH 1 /* THREED_LHPF_MODE */ +#define WM8962_THREED_LHPF_ENA 0x0002 /* THREED_LHPF_ENA */ +#define WM8962_THREED_LHPF_ENA_MASK 0x0002 /* THREED_LHPF_ENA */ +#define WM8962_THREED_LHPF_ENA_SHIFT 1 /* THREED_LHPF_ENA */ +#define WM8962_THREED_LHPF_ENA_WIDTH 1 /* THREED_LHPF_ENA */ +#define WM8962_THREED_ENA 0x0001 /* THREED_ENA */ +#define WM8962_THREED_ENA_MASK 0x0001 /* THREED_ENA */ +#define WM8962_THREED_ENA_SHIFT 0 /* THREED_ENA */ +#define WM8962_THREED_ENA_WIDTH 1 /* THREED_ENA */ + +/* + * R269 (0x10D) - THREED2 + */ +#define WM8962_THREED_FGAINL_MASK 0xF800 /* THREED_FGAINL - [15:11] */ +#define WM8962_THREED_FGAINL_SHIFT 11 /* THREED_FGAINL - [15:11] */ +#define WM8962_THREED_FGAINL_WIDTH 5 /* THREED_FGAINL - [15:11] */ +#define WM8962_THREED_CGAINL_MASK 0x07C0 /* THREED_CGAINL - [10:6] */ +#define WM8962_THREED_CGAINL_SHIFT 6 /* THREED_CGAINL - [10:6] */ +#define WM8962_THREED_CGAINL_WIDTH 5 /* THREED_CGAINL - [10:6] */ +#define WM8962_THREED_DELAYL_MASK 0x003C /* THREED_DELAYL - [5:2] */ +#define WM8962_THREED_DELAYL_SHIFT 2 /* THREED_DELAYL - [5:2] */ +#define WM8962_THREED_DELAYL_WIDTH 4 /* THREED_DELAYL - [5:2] */ + +/* + * R270 (0x10E) - THREED3 + */ +#define WM8962_THREED_LHPF_COEFF_MASK 0xFFFF /* THREED_LHPF_COEFF - [15:0] */ +#define WM8962_THREED_LHPF_COEFF_SHIFT 0 /* THREED_LHPF_COEFF - [15:0] */ +#define WM8962_THREED_LHPF_COEFF_WIDTH 16 /* THREED_LHPF_COEFF - [15:0] */ + +/* + * R271 (0x10F) - THREED4 + */ +#define WM8962_THREED_FGAINR_MASK 0xF800 /* THREED_FGAINR - [15:11] */ +#define WM8962_THREED_FGAINR_SHIFT 11 /* THREED_FGAINR - [15:11] */ +#define WM8962_THREED_FGAINR_WIDTH 5 /* THREED_FGAINR - [15:11] */ +#define WM8962_THREED_CGAINR_MASK 0x07C0 /* THREED_CGAINR - [10:6] */ +#define WM8962_THREED_CGAINR_SHIFT 6 /* THREED_CGAINR - [10:6] */ +#define WM8962_THREED_CGAINR_WIDTH 5 /* THREED_CGAINR - [10:6] */ +#define WM8962_THREED_DELAYR_MASK 0x003C /* THREED_DELAYR - [5:2] */ +#define WM8962_THREED_DELAYR_SHIFT 2 /* THREED_DELAYR - [5:2] */ +#define WM8962_THREED_DELAYR_WIDTH 4 /* THREED_DELAYR - [5:2] */ + +/* + * R276 (0x114) - DRC 1 + */ +#define WM8962_DRC_SIG_DET_RMS_MASK 0x7C00 /* DRC_SIG_DET_RMS - [14:10] */ +#define WM8962_DRC_SIG_DET_RMS_SHIFT 10 /* DRC_SIG_DET_RMS - [14:10] */ +#define WM8962_DRC_SIG_DET_RMS_WIDTH 5 /* DRC_SIG_DET_RMS - [14:10] */ +#define WM8962_DRC_SIG_DET_PK_MASK 0x0300 /* DRC_SIG_DET_PK - [9:8] */ +#define WM8962_DRC_SIG_DET_PK_SHIFT 8 /* DRC_SIG_DET_PK - [9:8] */ +#define WM8962_DRC_SIG_DET_PK_WIDTH 2 /* DRC_SIG_DET_PK - [9:8] */ +#define WM8962_DRC_NG_ENA 0x0080 /* DRC_NG_ENA */ +#define WM8962_DRC_NG_ENA_MASK 0x0080 /* DRC_NG_ENA */ +#define WM8962_DRC_NG_ENA_SHIFT 7 /* DRC_NG_ENA */ +#define WM8962_DRC_NG_ENA_WIDTH 1 /* DRC_NG_ENA */ +#define WM8962_DRC_SIG_DET_MODE 0x0040 /* DRC_SIG_DET_MODE */ +#define WM8962_DRC_SIG_DET_MODE_MASK 0x0040 /* DRC_SIG_DET_MODE */ +#define WM8962_DRC_SIG_DET_MODE_SHIFT 6 /* DRC_SIG_DET_MODE */ +#define WM8962_DRC_SIG_DET_MODE_WIDTH 1 /* DRC_SIG_DET_MODE */ +#define WM8962_DRC_SIG_DET 0x0020 /* DRC_SIG_DET */ +#define WM8962_DRC_SIG_DET_MASK 0x0020 /* DRC_SIG_DET */ +#define WM8962_DRC_SIG_DET_SHIFT 5 /* DRC_SIG_DET */ +#define WM8962_DRC_SIG_DET_WIDTH 1 /* DRC_SIG_DET */ +#define WM8962_DRC_KNEE2_OP_ENA 0x0010 /* DRC_KNEE2_OP_ENA */ +#define WM8962_DRC_KNEE2_OP_ENA_MASK 0x0010 /* DRC_KNEE2_OP_ENA */ +#define WM8962_DRC_KNEE2_OP_ENA_SHIFT 4 /* DRC_KNEE2_OP_ENA */ +#define WM8962_DRC_KNEE2_OP_ENA_WIDTH 1 /* DRC_KNEE2_OP_ENA */ +#define WM8962_DRC_QR 0x0008 /* DRC_QR */ +#define WM8962_DRC_QR_MASK 0x0008 /* DRC_QR */ +#define WM8962_DRC_QR_SHIFT 3 /* DRC_QR */ +#define WM8962_DRC_QR_WIDTH 1 /* DRC_QR */ +#define WM8962_DRC_ANTICLIP 0x0004 /* DRC_ANTICLIP */ +#define WM8962_DRC_ANTICLIP_MASK 0x0004 /* DRC_ANTICLIP */ +#define WM8962_DRC_ANTICLIP_SHIFT 2 /* DRC_ANTICLIP */ +#define WM8962_DRC_ANTICLIP_WIDTH 1 /* DRC_ANTICLIP */ +#define WM8962_DRC_MODE 0x0002 /* DRC_MODE */ +#define WM8962_DRC_MODE_MASK 0x0002 /* DRC_MODE */ +#define WM8962_DRC_MODE_SHIFT 1 /* DRC_MODE */ +#define WM8962_DRC_MODE_WIDTH 1 /* DRC_MODE */ +#define WM8962_DRC_ENA 0x0001 /* DRC_ENA */ +#define WM8962_DRC_ENA_MASK 0x0001 /* DRC_ENA */ +#define WM8962_DRC_ENA_SHIFT 0 /* DRC_ENA */ +#define WM8962_DRC_ENA_WIDTH 1 /* DRC_ENA */ + +/* + * R277 (0x115) - DRC 2 + */ +#define WM8962_DRC_ATK_MASK 0x1E00 /* DRC_ATK - [12:9] */ +#define WM8962_DRC_ATK_SHIFT 9 /* DRC_ATK - [12:9] */ +#define WM8962_DRC_ATK_WIDTH 4 /* DRC_ATK - [12:9] */ +#define WM8962_DRC_DCY_MASK 0x01E0 /* DRC_DCY - [8:5] */ +#define WM8962_DRC_DCY_SHIFT 5 /* DRC_DCY - [8:5] */ +#define WM8962_DRC_DCY_WIDTH 4 /* DRC_DCY - [8:5] */ +#define WM8962_DRC_MINGAIN_MASK 0x001C /* DRC_MINGAIN - [4:2] */ +#define WM8962_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [4:2] */ +#define WM8962_DRC_MINGAIN_WIDTH 3 /* DRC_MINGAIN - [4:2] */ +#define WM8962_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */ +#define WM8962_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */ +#define WM8962_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */ + +/* + * R278 (0x116) - DRC 3 + */ +#define WM8962_DRC_NG_MINGAIN_MASK 0xF000 /* DRC_NG_MINGAIN - [15:12] */ +#define WM8962_DRC_NG_MINGAIN_SHIFT 12 /* DRC_NG_MINGAIN - [15:12] */ +#define WM8962_DRC_NG_MINGAIN_WIDTH 4 /* DRC_NG_MINGAIN - [15:12] */ +#define WM8962_DRC_QR_THR_MASK 0x0C00 /* DRC_QR_THR - [11:10] */ +#define WM8962_DRC_QR_THR_SHIFT 10 /* DRC_QR_THR - [11:10] */ +#define WM8962_DRC_QR_THR_WIDTH 2 /* DRC_QR_THR - [11:10] */ +#define WM8962_DRC_QR_DCY_MASK 0x0300 /* DRC_QR_DCY - [9:8] */ +#define WM8962_DRC_QR_DCY_SHIFT 8 /* DRC_QR_DCY - [9:8] */ +#define WM8962_DRC_QR_DCY_WIDTH 2 /* DRC_QR_DCY - [9:8] */ +#define WM8962_DRC_NG_EXP_MASK 0x00C0 /* DRC_NG_EXP - [7:6] */ +#define WM8962_DRC_NG_EXP_SHIFT 6 /* DRC_NG_EXP - [7:6] */ +#define WM8962_DRC_NG_EXP_WIDTH 2 /* DRC_NG_EXP - [7:6] */ +#define WM8962_DRC_HI_COMP_MASK 0x0038 /* DRC_HI_COMP - [5:3] */ +#define WM8962_DRC_HI_COMP_SHIFT 3 /* DRC_HI_COMP - [5:3] */ +#define WM8962_DRC_HI_COMP_WIDTH 3 /* DRC_HI_COMP - [5:3] */ +#define WM8962_DRC_LO_COMP_MASK 0x0007 /* DRC_LO_COMP - [2:0] */ +#define WM8962_DRC_LO_COMP_SHIFT 0 /* DRC_LO_COMP - [2:0] */ +#define WM8962_DRC_LO_COMP_WIDTH 3 /* DRC_LO_COMP - [2:0] */ + +/* + * R279 (0x117) - DRC 4 + */ +#define WM8962_DRC_KNEE_IP_MASK 0x07E0 /* DRC_KNEE_IP - [10:5] */ +#define WM8962_DRC_KNEE_IP_SHIFT 5 /* DRC_KNEE_IP - [10:5] */ +#define WM8962_DRC_KNEE_IP_WIDTH 6 /* DRC_KNEE_IP - [10:5] */ +#define WM8962_DRC_KNEE_OP_MASK 0x001F /* DRC_KNEE_OP - [4:0] */ +#define WM8962_DRC_KNEE_OP_SHIFT 0 /* DRC_KNEE_OP - [4:0] */ +#define WM8962_DRC_KNEE_OP_WIDTH 5 /* DRC_KNEE_OP - [4:0] */ + +/* + * R280 (0x118) - DRC 5 + */ +#define WM8962_DRC_KNEE2_IP_MASK 0x03E0 /* DRC_KNEE2_IP - [9:5] */ +#define WM8962_DRC_KNEE2_IP_SHIFT 5 /* DRC_KNEE2_IP - [9:5] */ +#define WM8962_DRC_KNEE2_IP_WIDTH 5 /* DRC_KNEE2_IP - [9:5] */ +#define WM8962_DRC_KNEE2_OP_MASK 0x001F /* DRC_KNEE2_OP - [4:0] */ +#define WM8962_DRC_KNEE2_OP_SHIFT 0 /* DRC_KNEE2_OP - [4:0] */ +#define WM8962_DRC_KNEE2_OP_WIDTH 5 /* DRC_KNEE2_OP - [4:0] */ + +/* + * R285 (0x11D) - Tloopback + */ +#define WM8962_TLB_ENA 0x0002 /* TLB_ENA */ +#define WM8962_TLB_ENA_MASK 0x0002 /* TLB_ENA */ +#define WM8962_TLB_ENA_SHIFT 1 /* TLB_ENA */ +#define WM8962_TLB_ENA_WIDTH 1 /* TLB_ENA */ +#define WM8962_TLB_MODE 0x0001 /* TLB_MODE */ +#define WM8962_TLB_MODE_MASK 0x0001 /* TLB_MODE */ +#define WM8962_TLB_MODE_SHIFT 0 /* TLB_MODE */ +#define WM8962_TLB_MODE_WIDTH 1 /* TLB_MODE */ + +/* + * R335 (0x14F) - EQ1 + */ +#define WM8962_EQ_SHARED_COEFF 0x0004 /* EQ_SHARED_COEFF */ +#define WM8962_EQ_SHARED_COEFF_MASK 0x0004 /* EQ_SHARED_COEFF */ +#define WM8962_EQ_SHARED_COEFF_SHIFT 2 /* EQ_SHARED_COEFF */ +#define WM8962_EQ_SHARED_COEFF_WIDTH 1 /* EQ_SHARED_COEFF */ +#define WM8962_EQ_SHARED_COEFF_SEL 0x0002 /* EQ_SHARED_COEFF_SEL */ +#define WM8962_EQ_SHARED_COEFF_SEL_MASK 0x0002 /* EQ_SHARED_COEFF_SEL */ +#define WM8962_EQ_SHARED_COEFF_SEL_SHIFT 1 /* EQ_SHARED_COEFF_SEL */ +#define WM8962_EQ_SHARED_COEFF_SEL_WIDTH 1 /* EQ_SHARED_COEFF_SEL */ +#define WM8962_EQ_ENA 0x0001 /* EQ_ENA */ +#define WM8962_EQ_ENA_MASK 0x0001 /* EQ_ENA */ +#define WM8962_EQ_ENA_SHIFT 0 /* EQ_ENA */ +#define WM8962_EQ_ENA_WIDTH 1 /* EQ_ENA */ + +/* + * R336 (0x150) - EQ2 + */ +#define WM8962_EQL_B1_GAIN_MASK 0xF800 /* EQL_B1_GAIN - [15:11] */ +#define WM8962_EQL_B1_GAIN_SHIFT 11 /* EQL_B1_GAIN - [15:11] */ +#define WM8962_EQL_B1_GAIN_WIDTH 5 /* EQL_B1_GAIN - [15:11] */ +#define WM8962_EQL_B2_GAIN_MASK 0x07C0 /* EQL_B2_GAIN - [10:6] */ +#define WM8962_EQL_B2_GAIN_SHIFT 6 /* EQL_B2_GAIN - [10:6] */ +#define WM8962_EQL_B2_GAIN_WIDTH 5 /* EQL_B2_GAIN - [10:6] */ +#define WM8962_EQL_B3_GAIN_MASK 0x003E /* EQL_B3_GAIN - [5:1] */ +#define WM8962_EQL_B3_GAIN_SHIFT 1 /* EQL_B3_GAIN - [5:1] */ +#define WM8962_EQL_B3_GAIN_WIDTH 5 /* EQL_B3_GAIN - [5:1] */ + +/* + * R337 (0x151) - EQ3 + */ +#define WM8962_EQL_B4_GAIN_MASK 0xF800 /* EQL_B4_GAIN - [15:11] */ +#define WM8962_EQL_B4_GAIN_SHIFT 11 /* EQL_B4_GAIN - [15:11] */ +#define WM8962_EQL_B4_GAIN_WIDTH 5 /* EQL_B4_GAIN - [15:11] */ +#define WM8962_EQL_B5_GAIN_MASK 0x07C0 /* EQL_B5_GAIN - [10:6] */ +#define WM8962_EQL_B5_GAIN_SHIFT 6 /* EQL_B5_GAIN - [10:6] */ +#define WM8962_EQL_B5_GAIN_WIDTH 5 /* EQL_B5_GAIN - [10:6] */ + +/* + * R338 (0x152) - EQ4 + */ +#define WM8962_EQL_B1_A_MASK 0xFFFF /* EQL_B1_A - [15:0] */ +#define WM8962_EQL_B1_A_SHIFT 0 /* EQL_B1_A - [15:0] */ +#define WM8962_EQL_B1_A_WIDTH 16 /* EQL_B1_A - [15:0] */ + +/* + * R339 (0x153) - EQ5 + */ +#define WM8962_EQL_B1_B_MASK 0xFFFF /* EQL_B1_B - [15:0] */ +#define WM8962_EQL_B1_B_SHIFT 0 /* EQL_B1_B - [15:0] */ +#define WM8962_EQL_B1_B_WIDTH 16 /* EQL_B1_B - [15:0] */ + +/* + * R340 (0x154) - EQ6 + */ +#define WM8962_EQL_B1_PG_MASK 0xFFFF /* EQL_B1_PG - [15:0] */ +#define WM8962_EQL_B1_PG_SHIFT 0 /* EQL_B1_PG - [15:0] */ +#define WM8962_EQL_B1_PG_WIDTH 16 /* EQL_B1_PG - [15:0] */ + +/* + * R341 (0x155) - EQ7 + */ +#define WM8962_EQL_B2_A_MASK 0xFFFF /* EQL_B2_A - [15:0] */ +#define WM8962_EQL_B2_A_SHIFT 0 /* EQL_B2_A - [15:0] */ +#define WM8962_EQL_B2_A_WIDTH 16 /* EQL_B2_A - [15:0] */ + +/* + * R342 (0x156) - EQ8 + */ +#define WM8962_EQL_B2_B_MASK 0xFFFF /* EQL_B2_B - [15:0] */ +#define WM8962_EQL_B2_B_SHIFT 0 /* EQL_B2_B - [15:0] */ +#define WM8962_EQL_B2_B_WIDTH 16 /* EQL_B2_B - [15:0] */ + +/* + * R343 (0x157) - EQ9 + */ +#define WM8962_EQL_B2_C_MASK 0xFFFF /* EQL_B2_C - [15:0] */ +#define WM8962_EQL_B2_C_SHIFT 0 /* EQL_B2_C - [15:0] */ +#define WM8962_EQL_B2_C_WIDTH 16 /* EQL_B2_C - [15:0] */ + +/* + * R344 (0x158) - EQ10 + */ +#define WM8962_EQL_B2_PG_MASK 0xFFFF /* EQL_B2_PG - [15:0] */ +#define WM8962_EQL_B2_PG_SHIFT 0 /* EQL_B2_PG - [15:0] */ +#define WM8962_EQL_B2_PG_WIDTH 16 /* EQL_B2_PG - [15:0] */ + +/* + * R345 (0x159) - EQ11 + */ +#define WM8962_EQL_B3_A_MASK 0xFFFF /* EQL_B3_A - [15:0] */ +#define WM8962_EQL_B3_A_SHIFT 0 /* EQL_B3_A - [15:0] */ +#define WM8962_EQL_B3_A_WIDTH 16 /* EQL_B3_A - [15:0] */ + +/* + * R346 (0x15A) - EQ12 + */ +#define WM8962_EQL_B3_B_MASK 0xFFFF /* EQL_B3_B - [15:0] */ +#define WM8962_EQL_B3_B_SHIFT 0 /* EQL_B3_B - [15:0] */ +#define WM8962_EQL_B3_B_WIDTH 16 /* EQL_B3_B - [15:0] */ + +/* + * R347 (0x15B) - EQ13 + */ +#define WM8962_EQL_B3_C_MASK 0xFFFF /* EQL_B3_C - [15:0] */ +#define WM8962_EQL_B3_C_SHIFT 0 /* EQL_B3_C - [15:0] */ +#define WM8962_EQL_B3_C_WIDTH 16 /* EQL_B3_C - [15:0] */ + +/* + * R348 (0x15C) - EQ14 + */ +#define WM8962_EQL_B3_PG_MASK 0xFFFF /* EQL_B3_PG - [15:0] */ +#define WM8962_EQL_B3_PG_SHIFT 0 /* EQL_B3_PG - [15:0] */ +#define WM8962_EQL_B3_PG_WIDTH 16 /* EQL_B3_PG - [15:0] */ + +/* + * R349 (0x15D) - EQ15 + */ +#define WM8962_EQL_B4_A_MASK 0xFFFF /* EQL_B4_A - [15:0] */ +#define WM8962_EQL_B4_A_SHIFT 0 /* EQL_B4_A - [15:0] */ +#define WM8962_EQL_B4_A_WIDTH 16 /* EQL_B4_A - [15:0] */ + +/* + * R350 (0x15E) - EQ16 + */ +#define WM8962_EQL_B4_B_MASK 0xFFFF /* EQL_B4_B - [15:0] */ +#define WM8962_EQL_B4_B_SHIFT 0 /* EQL_B4_B - [15:0] */ +#define WM8962_EQL_B4_B_WIDTH 16 /* EQL_B4_B - [15:0] */ + +/* + * R351 (0x15F) - EQ17 + */ +#define WM8962_EQL_B4_C_MASK 0xFFFF /* EQL_B4_C - [15:0] */ +#define WM8962_EQL_B4_C_SHIFT 0 /* EQL_B4_C - [15:0] */ +#define WM8962_EQL_B4_C_WIDTH 16 /* EQL_B4_C - [15:0] */ + +/* + * R352 (0x160) - EQ18 + */ +#define WM8962_EQL_B4_PG_MASK 0xFFFF /* EQL_B4_PG - [15:0] */ +#define WM8962_EQL_B4_PG_SHIFT 0 /* EQL_B4_PG - [15:0] */ +#define WM8962_EQL_B4_PG_WIDTH 16 /* EQL_B4_PG - [15:0] */ + +/* + * R353 (0x161) - EQ19 + */ +#define WM8962_EQL_B5_A_MASK 0xFFFF /* EQL_B5_A - [15:0] */ +#define WM8962_EQL_B5_A_SHIFT 0 /* EQL_B5_A - [15:0] */ +#define WM8962_EQL_B5_A_WIDTH 16 /* EQL_B5_A - [15:0] */ + +/* + * R354 (0x162) - EQ20 + */ +#define WM8962_EQL_B5_B_MASK 0xFFFF /* EQL_B5_B - [15:0] */ +#define WM8962_EQL_B5_B_SHIFT 0 /* EQL_B5_B - [15:0] */ +#define WM8962_EQL_B5_B_WIDTH 16 /* EQL_B5_B - [15:0] */ + +/* + * R355 (0x163) - EQ21 + */ +#define WM8962_EQL_B5_PG_MASK 0xFFFF /* EQL_B5_PG - [15:0] */ +#define WM8962_EQL_B5_PG_SHIFT 0 /* EQL_B5_PG - [15:0] */ +#define WM8962_EQL_B5_PG_WIDTH 16 /* EQL_B5_PG - [15:0] */ + +/* + * R356 (0x164) - EQ22 + */ +#define WM8962_EQR_B1_GAIN_MASK 0xF800 /* EQR_B1_GAIN - [15:11] */ +#define WM8962_EQR_B1_GAIN_SHIFT 11 /* EQR_B1_GAIN - [15:11] */ +#define WM8962_EQR_B1_GAIN_WIDTH 5 /* EQR_B1_GAIN - [15:11] */ +#define WM8962_EQR_B2_GAIN_MASK 0x07C0 /* EQR_B2_GAIN - [10:6] */ +#define WM8962_EQR_B2_GAIN_SHIFT 6 /* EQR_B2_GAIN - [10:6] */ +#define WM8962_EQR_B2_GAIN_WIDTH 5 /* EQR_B2_GAIN - [10:6] */ +#define WM8962_EQR_B3_GAIN_MASK 0x003E /* EQR_B3_GAIN - [5:1] */ +#define WM8962_EQR_B3_GAIN_SHIFT 1 /* EQR_B3_GAIN - [5:1] */ +#define WM8962_EQR_B3_GAIN_WIDTH 5 /* EQR_B3_GAIN - [5:1] */ + +/* + * R357 (0x165) - EQ23 + */ +#define WM8962_EQR_B4_GAIN_MASK 0xF800 /* EQR_B4_GAIN - [15:11] */ +#define WM8962_EQR_B4_GAIN_SHIFT 11 /* EQR_B4_GAIN - [15:11] */ +#define WM8962_EQR_B4_GAIN_WIDTH 5 /* EQR_B4_GAIN - [15:11] */ +#define WM8962_EQR_B5_GAIN_MASK 0x07C0 /* EQR_B5_GAIN - [10:6] */ +#define WM8962_EQR_B5_GAIN_SHIFT 6 /* EQR_B5_GAIN - [10:6] */ +#define WM8962_EQR_B5_GAIN_WIDTH 5 /* EQR_B5_GAIN - [10:6] */ + +/* + * R358 (0x166) - EQ24 + */ +#define WM8962_EQR_B1_A_MASK 0xFFFF /* EQR_B1_A - [15:0] */ +#define WM8962_EQR_B1_A_SHIFT 0 /* EQR_B1_A - [15:0] */ +#define WM8962_EQR_B1_A_WIDTH 16 /* EQR_B1_A - [15:0] */ + +/* + * R359 (0x167) - EQ25 + */ +#define WM8962_EQR_B1_B_MASK 0xFFFF /* EQR_B1_B - [15:0] */ +#define WM8962_EQR_B1_B_SHIFT 0 /* EQR_B1_B - [15:0] */ +#define WM8962_EQR_B1_B_WIDTH 16 /* EQR_B1_B - [15:0] */ + +/* + * R360 (0x168) - EQ26 + */ +#define WM8962_EQR_B1_PG_MASK 0xFFFF /* EQR_B1_PG - [15:0] */ +#define WM8962_EQR_B1_PG_SHIFT 0 /* EQR_B1_PG - [15:0] */ +#define WM8962_EQR_B1_PG_WIDTH 16 /* EQR_B1_PG - [15:0] */ + +/* + * R361 (0x169) - EQ27 + */ +#define WM8962_EQR_B2_A_MASK 0xFFFF /* EQR_B2_A - [15:0] */ +#define WM8962_EQR_B2_A_SHIFT 0 /* EQR_B2_A - [15:0] */ +#define WM8962_EQR_B2_A_WIDTH 16 /* EQR_B2_A - [15:0] */ + +/* + * R362 (0x16A) - EQ28 + */ +#define WM8962_EQR_B2_B_MASK 0xFFFF /* EQR_B2_B - [15:0] */ +#define WM8962_EQR_B2_B_SHIFT 0 /* EQR_B2_B - [15:0] */ +#define WM8962_EQR_B2_B_WIDTH 16 /* EQR_B2_B - [15:0] */ + +/* + * R363 (0x16B) - EQ29 + */ +#define WM8962_EQR_B2_C_MASK 0xFFFF /* EQR_B2_C - [15:0] */ +#define WM8962_EQR_B2_C_SHIFT 0 /* EQR_B2_C - [15:0] */ +#define WM8962_EQR_B2_C_WIDTH 16 /* EQR_B2_C - [15:0] */ + +/* + * R364 (0x16C) - EQ30 + */ +#define WM8962_EQR_B2_PG_MASK 0xFFFF /* EQR_B2_PG - [15:0] */ +#define WM8962_EQR_B2_PG_SHIFT 0 /* EQR_B2_PG - [15:0] */ +#define WM8962_EQR_B2_PG_WIDTH 16 /* EQR_B2_PG - [15:0] */ + +/* + * R365 (0x16D) - EQ31 + */ +#define WM8962_EQR_B3_A_MASK 0xFFFF /* EQR_B3_A - [15:0] */ +#define WM8962_EQR_B3_A_SHIFT 0 /* EQR_B3_A - [15:0] */ +#define WM8962_EQR_B3_A_WIDTH 16 /* EQR_B3_A - [15:0] */ + +/* + * R366 (0x16E) - EQ32 + */ +#define WM8962_EQR_B3_B_MASK 0xFFFF /* EQR_B3_B - [15:0] */ +#define WM8962_EQR_B3_B_SHIFT 0 /* EQR_B3_B - [15:0] */ +#define WM8962_EQR_B3_B_WIDTH 16 /* EQR_B3_B - [15:0] */ + +/* + * R367 (0x16F) - EQ33 + */ +#define WM8962_EQR_B3_C_MASK 0xFFFF /* EQR_B3_C - [15:0] */ +#define WM8962_EQR_B3_C_SHIFT 0 /* EQR_B3_C - [15:0] */ +#define WM8962_EQR_B3_C_WIDTH 16 /* EQR_B3_C - [15:0] */ + +/* + * R368 (0x170) - EQ34 + */ +#define WM8962_EQR_B3_PG_MASK 0xFFFF /* EQR_B3_PG - [15:0] */ +#define WM8962_EQR_B3_PG_SHIFT 0 /* EQR_B3_PG - [15:0] */ +#define WM8962_EQR_B3_PG_WIDTH 16 /* EQR_B3_PG - [15:0] */ + +/* + * R369 (0x171) - EQ35 + */ +#define WM8962_EQR_B4_A_MASK 0xFFFF /* EQR_B4_A - [15:0] */ +#define WM8962_EQR_B4_A_SHIFT 0 /* EQR_B4_A - [15:0] */ +#define WM8962_EQR_B4_A_WIDTH 16 /* EQR_B4_A - [15:0] */ + +/* + * R370 (0x172) - EQ36 + */ +#define WM8962_EQR_B4_B_MASK 0xFFFF /* EQR_B4_B - [15:0] */ +#define WM8962_EQR_B4_B_SHIFT 0 /* EQR_B4_B - [15:0] */ +#define WM8962_EQR_B4_B_WIDTH 16 /* EQR_B4_B - [15:0] */ + +/* + * R371 (0x173) - EQ37 + */ +#define WM8962_EQR_B4_C_MASK 0xFFFF /* EQR_B4_C - [15:0] */ +#define WM8962_EQR_B4_C_SHIFT 0 /* EQR_B4_C - [15:0] */ +#define WM8962_EQR_B4_C_WIDTH 16 /* EQR_B4_C - [15:0] */ + +/* + * R372 (0x174) - EQ38 + */ +#define WM8962_EQR_B4_PG_MASK 0xFFFF /* EQR_B4_PG - [15:0] */ +#define WM8962_EQR_B4_PG_SHIFT 0 /* EQR_B4_PG - [15:0] */ +#define WM8962_EQR_B4_PG_WIDTH 16 /* EQR_B4_PG - [15:0] */ + +/* + * R373 (0x175) - EQ39 + */ +#define WM8962_EQR_B5_A_MASK 0xFFFF /* EQR_B5_A - [15:0] */ +#define WM8962_EQR_B5_A_SHIFT 0 /* EQR_B5_A - [15:0] */ +#define WM8962_EQR_B5_A_WIDTH 16 /* EQR_B5_A - [15:0] */ + +/* + * R374 (0x176) - EQ40 + */ +#define WM8962_EQR_B5_B_MASK 0xFFFF /* EQR_B5_B - [15:0] */ +#define WM8962_EQR_B5_B_SHIFT 0 /* EQR_B5_B - [15:0] */ +#define WM8962_EQR_B5_B_WIDTH 16 /* EQR_B5_B - [15:0] */ + +/* + * R375 (0x177) - EQ41 + */ +#define WM8962_EQR_B5_PG_MASK 0xFFFF /* EQR_B5_PG - [15:0] */ +#define WM8962_EQR_B5_PG_SHIFT 0 /* EQR_B5_PG - [15:0] */ +#define WM8962_EQR_B5_PG_WIDTH 16 /* EQR_B5_PG - [15:0] */ + +/* + * R513 (0x201) - GPIO 2 + */ +#define WM8962_GP2_POL 0x0400 /* GP2_POL */ +#define WM8962_GP2_POL_MASK 0x0400 /* GP2_POL */ +#define WM8962_GP2_POL_SHIFT 10 /* GP2_POL */ +#define WM8962_GP2_POL_WIDTH 1 /* GP2_POL */ +#define WM8962_GP2_LVL 0x0040 /* GP2_LVL */ +#define WM8962_GP2_LVL_MASK 0x0040 /* GP2_LVL */ +#define WM8962_GP2_LVL_SHIFT 6 /* GP2_LVL */ +#define WM8962_GP2_LVL_WIDTH 1 /* GP2_LVL */ +#define WM8962_GP2_FN_MASK 0x001F /* GP2_FN - [4:0] */ +#define WM8962_GP2_FN_SHIFT 0 /* GP2_FN - [4:0] */ +#define WM8962_GP2_FN_WIDTH 5 /* GP2_FN - [4:0] */ + +/* + * R514 (0x202) - GPIO 3 + */ +#define WM8962_GP3_POL 0x0400 /* GP3_POL */ +#define WM8962_GP3_POL_MASK 0x0400 /* GP3_POL */ +#define WM8962_GP3_POL_SHIFT 10 /* GP3_POL */ +#define WM8962_GP3_POL_WIDTH 1 /* GP3_POL */ +#define WM8962_GP3_LVL 0x0040 /* GP3_LVL */ +#define WM8962_GP3_LVL_MASK 0x0040 /* GP3_LVL */ +#define WM8962_GP3_LVL_SHIFT 6 /* GP3_LVL */ +#define WM8962_GP3_LVL_WIDTH 1 /* GP3_LVL */ +#define WM8962_GP3_FN_MASK 0x001F /* GP3_FN - [4:0] */ +#define WM8962_GP3_FN_SHIFT 0 /* GP3_FN - [4:0] */ +#define WM8962_GP3_FN_WIDTH 5 /* GP3_FN - [4:0] */ + +/* + * R516 (0x204) - GPIO 5 + */ +#define WM8962_GP5_DIR 0x8000 /* GP5_DIR */ +#define WM8962_GP5_DIR_MASK 0x8000 /* GP5_DIR */ +#define WM8962_GP5_DIR_SHIFT 15 /* GP5_DIR */ +#define WM8962_GP5_DIR_WIDTH 1 /* GP5_DIR */ +#define WM8962_GP5_PU 0x4000 /* GP5_PU */ +#define WM8962_GP5_PU_MASK 0x4000 /* GP5_PU */ +#define WM8962_GP5_PU_SHIFT 14 /* GP5_PU */ +#define WM8962_GP5_PU_WIDTH 1 /* GP5_PU */ +#define WM8962_GP5_PD 0x2000 /* GP5_PD */ +#define WM8962_GP5_PD_MASK 0x2000 /* GP5_PD */ +#define WM8962_GP5_PD_SHIFT 13 /* GP5_PD */ +#define WM8962_GP5_PD_WIDTH 1 /* GP5_PD */ +#define WM8962_GP5_POL 0x0400 /* GP5_POL */ +#define WM8962_GP5_POL_MASK 0x0400 /* GP5_POL */ +#define WM8962_GP5_POL_SHIFT 10 /* GP5_POL */ +#define WM8962_GP5_POL_WIDTH 1 /* GP5_POL */ +#define WM8962_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */ +#define WM8962_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */ +#define WM8962_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */ +#define WM8962_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */ +#define WM8962_GP5_DB 0x0100 /* GP5_DB */ +#define WM8962_GP5_DB_MASK 0x0100 /* GP5_DB */ +#define WM8962_GP5_DB_SHIFT 8 /* GP5_DB */ +#define WM8962_GP5_DB_WIDTH 1 /* GP5_DB */ +#define WM8962_GP5_LVL 0x0040 /* GP5_LVL */ +#define WM8962_GP5_LVL_MASK 0x0040 /* GP5_LVL */ +#define WM8962_GP5_LVL_SHIFT 6 /* GP5_LVL */ +#define WM8962_GP5_LVL_WIDTH 1 /* GP5_LVL */ +#define WM8962_GP5_FN_MASK 0x001F /* GP5_FN - [4:0] */ +#define WM8962_GP5_FN_SHIFT 0 /* GP5_FN - [4:0] */ +#define WM8962_GP5_FN_WIDTH 5 /* GP5_FN - [4:0] */ + +/* + * R517 (0x205) - GPIO 6 + */ +#define WM8962_GP6_DIR 0x8000 /* GP6_DIR */ +#define WM8962_GP6_DIR_MASK 0x8000 /* GP6_DIR */ +#define WM8962_GP6_DIR_SHIFT 15 /* GP6_DIR */ +#define WM8962_GP6_DIR_WIDTH 1 /* GP6_DIR */ +#define WM8962_GP6_PU 0x4000 /* GP6_PU */ +#define WM8962_GP6_PU_MASK 0x4000 /* GP6_PU */ +#define WM8962_GP6_PU_SHIFT 14 /* GP6_PU */ +#define WM8962_GP6_PU_WIDTH 1 /* GP6_PU */ +#define WM8962_GP6_PD 0x2000 /* GP6_PD */ +#define WM8962_GP6_PD_MASK 0x2000 /* GP6_PD */ +#define WM8962_GP6_PD_SHIFT 13 /* GP6_PD */ +#define WM8962_GP6_PD_WIDTH 1 /* GP6_PD */ +#define WM8962_GP6_POL 0x0400 /* GP6_POL */ +#define WM8962_GP6_POL_MASK 0x0400 /* GP6_POL */ +#define WM8962_GP6_POL_SHIFT 10 /* GP6_POL */ +#define WM8962_GP6_POL_WIDTH 1 /* GP6_POL */ +#define WM8962_GP6_OP_CFG 0x0200 /* GP6_OP_CFG */ +#define WM8962_GP6_OP_CFG_MASK 0x0200 /* GP6_OP_CFG */ +#define WM8962_GP6_OP_CFG_SHIFT 9 /* GP6_OP_CFG */ +#define WM8962_GP6_OP_CFG_WIDTH 1 /* GP6_OP_CFG */ +#define WM8962_GP6_DB 0x0100 /* GP6_DB */ +#define WM8962_GP6_DB_MASK 0x0100 /* GP6_DB */ +#define WM8962_GP6_DB_SHIFT 8 /* GP6_DB */ +#define WM8962_GP6_DB_WIDTH 1 /* GP6_DB */ +#define WM8962_GP6_LVL 0x0040 /* GP6_LVL */ +#define WM8962_GP6_LVL_MASK 0x0040 /* GP6_LVL */ +#define WM8962_GP6_LVL_SHIFT 6 /* GP6_LVL */ +#define WM8962_GP6_LVL_WIDTH 1 /* GP6_LVL */ +#define WM8962_GP6_FN_MASK 0x001F /* GP6_FN - [4:0] */ +#define WM8962_GP6_FN_SHIFT 0 /* GP6_FN - [4:0] */ +#define WM8962_GP6_FN_WIDTH 5 /* GP6_FN - [4:0] */ + +/* + * R560 (0x230) - Interrupt Status 1 + */ +#define WM8962_GP6_EINT 0x0020 /* GP6_EINT */ +#define WM8962_GP6_EINT_MASK 0x0020 /* GP6_EINT */ +#define WM8962_GP6_EINT_SHIFT 5 /* GP6_EINT */ +#define WM8962_GP6_EINT_WIDTH 1 /* GP6_EINT */ +#define WM8962_GP5_EINT 0x0010 /* GP5_EINT */ +#define WM8962_GP5_EINT_MASK 0x0010 /* GP5_EINT */ +#define WM8962_GP5_EINT_SHIFT 4 /* GP5_EINT */ +#define WM8962_GP5_EINT_WIDTH 1 /* GP5_EINT */ + +/* + * R561 (0x231) - Interrupt Status 2 + */ +#define WM8962_MICSCD_EINT 0x8000 /* MICSCD_EINT */ +#define WM8962_MICSCD_EINT_MASK 0x8000 /* MICSCD_EINT */ +#define WM8962_MICSCD_EINT_SHIFT 15 /* MICSCD_EINT */ +#define WM8962_MICSCD_EINT_WIDTH 1 /* MICSCD_EINT */ +#define WM8962_MICD_EINT 0x4000 /* MICD_EINT */ +#define WM8962_MICD_EINT_MASK 0x4000 /* MICD_EINT */ +#define WM8962_MICD_EINT_SHIFT 14 /* MICD_EINT */ +#define WM8962_MICD_EINT_WIDTH 1 /* MICD_EINT */ +#define WM8962_FIFOS_ERR_EINT 0x2000 /* FIFOS_ERR_EINT */ +#define WM8962_FIFOS_ERR_EINT_MASK 0x2000 /* FIFOS_ERR_EINT */ +#define WM8962_FIFOS_ERR_EINT_SHIFT 13 /* FIFOS_ERR_EINT */ +#define WM8962_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */ +#define WM8962_ALC_LOCK_EINT 0x1000 /* ALC_LOCK_EINT */ +#define WM8962_ALC_LOCK_EINT_MASK 0x1000 /* ALC_LOCK_EINT */ +#define WM8962_ALC_LOCK_EINT_SHIFT 12 /* ALC_LOCK_EINT */ +#define WM8962_ALC_LOCK_EINT_WIDTH 1 /* ALC_LOCK_EINT */ +#define WM8962_ALC_THRESH_EINT 0x0800 /* ALC_THRESH_EINT */ +#define WM8962_ALC_THRESH_EINT_MASK 0x0800 /* ALC_THRESH_EINT */ +#define WM8962_ALC_THRESH_EINT_SHIFT 11 /* ALC_THRESH_EINT */ +#define WM8962_ALC_THRESH_EINT_WIDTH 1 /* ALC_THRESH_EINT */ +#define WM8962_ALC_SAT_EINT 0x0400 /* ALC_SAT_EINT */ +#define WM8962_ALC_SAT_EINT_MASK 0x0400 /* ALC_SAT_EINT */ +#define WM8962_ALC_SAT_EINT_SHIFT 10 /* ALC_SAT_EINT */ +#define WM8962_ALC_SAT_EINT_WIDTH 1 /* ALC_SAT_EINT */ +#define WM8962_ALC_PKOVR_EINT 0x0200 /* ALC_PKOVR_EINT */ +#define WM8962_ALC_PKOVR_EINT_MASK 0x0200 /* ALC_PKOVR_EINT */ +#define WM8962_ALC_PKOVR_EINT_SHIFT 9 /* ALC_PKOVR_EINT */ +#define WM8962_ALC_PKOVR_EINT_WIDTH 1 /* ALC_PKOVR_EINT */ +#define WM8962_ALC_NGATE_EINT 0x0100 /* ALC_NGATE_EINT */ +#define WM8962_ALC_NGATE_EINT_MASK 0x0100 /* ALC_NGATE_EINT */ +#define WM8962_ALC_NGATE_EINT_SHIFT 8 /* ALC_NGATE_EINT */ +#define WM8962_ALC_NGATE_EINT_WIDTH 1 /* ALC_NGATE_EINT */ +#define WM8962_WSEQ_DONE_EINT 0x0080 /* WSEQ_DONE_EINT */ +#define WM8962_WSEQ_DONE_EINT_MASK 0x0080 /* WSEQ_DONE_EINT */ +#define WM8962_WSEQ_DONE_EINT_SHIFT 7 /* WSEQ_DONE_EINT */ +#define WM8962_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */ +#define WM8962_DRC_ACTDET_EINT 0x0040 /* DRC_ACTDET_EINT */ +#define WM8962_DRC_ACTDET_EINT_MASK 0x0040 /* DRC_ACTDET_EINT */ +#define WM8962_DRC_ACTDET_EINT_SHIFT 6 /* DRC_ACTDET_EINT */ +#define WM8962_DRC_ACTDET_EINT_WIDTH 1 /* DRC_ACTDET_EINT */ +#define WM8962_FLL_LOCK_EINT 0x0020 /* FLL_LOCK_EINT */ +#define WM8962_FLL_LOCK_EINT_MASK 0x0020 /* FLL_LOCK_EINT */ +#define WM8962_FLL_LOCK_EINT_SHIFT 5 /* FLL_LOCK_EINT */ +#define WM8962_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */ +#define WM8962_PLL3_LOCK_EINT 0x0008 /* PLL3_LOCK_EINT */ +#define WM8962_PLL3_LOCK_EINT_MASK 0x0008 /* PLL3_LOCK_EINT */ +#define WM8962_PLL3_LOCK_EINT_SHIFT 3 /* PLL3_LOCK_EINT */ +#define WM8962_PLL3_LOCK_EINT_WIDTH 1 /* PLL3_LOCK_EINT */ +#define WM8962_PLL2_LOCK_EINT 0x0004 /* PLL2_LOCK_EINT */ +#define WM8962_PLL2_LOCK_EINT_MASK 0x0004 /* PLL2_LOCK_EINT */ +#define WM8962_PLL2_LOCK_EINT_SHIFT 2 /* PLL2_LOCK_EINT */ +#define WM8962_PLL2_LOCK_EINT_WIDTH 1 /* PLL2_LOCK_EINT */ +#define WM8962_TEMP_SHUT_EINT 0x0001 /* TEMP_SHUT_EINT */ +#define WM8962_TEMP_SHUT_EINT_MASK 0x0001 /* TEMP_SHUT_EINT */ +#define WM8962_TEMP_SHUT_EINT_SHIFT 0 /* TEMP_SHUT_EINT */ +#define WM8962_TEMP_SHUT_EINT_WIDTH 1 /* TEMP_SHUT_EINT */ + +/* + * R568 (0x238) - Interrupt Status 1 Mask + */ +#define WM8962_IM_GP6_EINT 0x0020 /* IM_GP6_EINT */ +#define WM8962_IM_GP6_EINT_MASK 0x0020 /* IM_GP6_EINT */ +#define WM8962_IM_GP6_EINT_SHIFT 5 /* IM_GP6_EINT */ +#define WM8962_IM_GP6_EINT_WIDTH 1 /* IM_GP6_EINT */ +#define WM8962_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */ +#define WM8962_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */ +#define WM8962_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */ +#define WM8962_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */ + +/* + * R569 (0x239) - Interrupt Status 2 Mask + */ +#define WM8962_IM_MICSCD_EINT 0x8000 /* IM_MICSCD_EINT */ +#define WM8962_IM_MICSCD_EINT_MASK 0x8000 /* IM_MICSCD_EINT */ +#define WM8962_IM_MICSCD_EINT_SHIFT 15 /* IM_MICSCD_EINT */ +#define WM8962_IM_MICSCD_EINT_WIDTH 1 /* IM_MICSCD_EINT */ +#define WM8962_IM_MICD_EINT 0x4000 /* IM_MICD_EINT */ +#define WM8962_IM_MICD_EINT_MASK 0x4000 /* IM_MICD_EINT */ +#define WM8962_IM_MICD_EINT_SHIFT 14 /* IM_MICD_EINT */ +#define WM8962_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */ +#define WM8962_IM_FIFOS_ERR_EINT 0x2000 /* IM_FIFOS_ERR_EINT */ +#define WM8962_IM_FIFOS_ERR_EINT_MASK 0x2000 /* IM_FIFOS_ERR_EINT */ +#define WM8962_IM_FIFOS_ERR_EINT_SHIFT 13 /* IM_FIFOS_ERR_EINT */ +#define WM8962_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */ +#define WM8962_IM_ALC_LOCK_EINT 0x1000 /* IM_ALC_LOCK_EINT */ +#define WM8962_IM_ALC_LOCK_EINT_MASK 0x1000 /* IM_ALC_LOCK_EINT */ +#define WM8962_IM_ALC_LOCK_EINT_SHIFT 12 /* IM_ALC_LOCK_EINT */ +#define WM8962_IM_ALC_LOCK_EINT_WIDTH 1 /* IM_ALC_LOCK_EINT */ +#define WM8962_IM_ALC_THRESH_EINT 0x0800 /* IM_ALC_THRESH_EINT */ +#define WM8962_IM_ALC_THRESH_EINT_MASK 0x0800 /* IM_ALC_THRESH_EINT */ +#define WM8962_IM_ALC_THRESH_EINT_SHIFT 11 /* IM_ALC_THRESH_EINT */ +#define WM8962_IM_ALC_THRESH_EINT_WIDTH 1 /* IM_ALC_THRESH_EINT */ +#define WM8962_IM_ALC_SAT_EINT 0x0400 /* IM_ALC_SAT_EINT */ +#define WM8962_IM_ALC_SAT_EINT_MASK 0x0400 /* IM_ALC_SAT_EINT */ +#define WM8962_IM_ALC_SAT_EINT_SHIFT 10 /* IM_ALC_SAT_EINT */ +#define WM8962_IM_ALC_SAT_EINT_WIDTH 1 /* IM_ALC_SAT_EINT */ +#define WM8962_IM_ALC_PKOVR_EINT 0x0200 /* IM_ALC_PKOVR_EINT */ +#define WM8962_IM_ALC_PKOVR_EINT_MASK 0x0200 /* IM_ALC_PKOVR_EINT */ +#define WM8962_IM_ALC_PKOVR_EINT_SHIFT 9 /* IM_ALC_PKOVR_EINT */ +#define WM8962_IM_ALC_PKOVR_EINT_WIDTH 1 /* IM_ALC_PKOVR_EINT */ +#define WM8962_IM_ALC_NGATE_EINT 0x0100 /* IM_ALC_NGATE_EINT */ +#define WM8962_IM_ALC_NGATE_EINT_MASK 0x0100 /* IM_ALC_NGATE_EINT */ +#define WM8962_IM_ALC_NGATE_EINT_SHIFT 8 /* IM_ALC_NGATE_EINT */ +#define WM8962_IM_ALC_NGATE_EINT_WIDTH 1 /* IM_ALC_NGATE_EINT */ +#define WM8962_IM_WSEQ_DONE_EINT 0x0080 /* IM_WSEQ_DONE_EINT */ +#define WM8962_IM_WSEQ_DONE_EINT_MASK 0x0080 /* IM_WSEQ_DONE_EINT */ +#define WM8962_IM_WSEQ_DONE_EINT_SHIFT 7 /* IM_WSEQ_DONE_EINT */ +#define WM8962_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */ +#define WM8962_IM_DRC_ACTDET_EINT 0x0040 /* IM_DRC_ACTDET_EINT */ +#define WM8962_IM_DRC_ACTDET_EINT_MASK 0x0040 /* IM_DRC_ACTDET_EINT */ +#define WM8962_IM_DRC_ACTDET_EINT_SHIFT 6 /* IM_DRC_ACTDET_EINT */ +#define WM8962_IM_DRC_ACTDET_EINT_WIDTH 1 /* IM_DRC_ACTDET_EINT */ +#define WM8962_IM_FLL_LOCK_EINT 0x0020 /* IM_FLL_LOCK_EINT */ +#define WM8962_IM_FLL_LOCK_EINT_MASK 0x0020 /* IM_FLL_LOCK_EINT */ +#define WM8962_IM_FLL_LOCK_EINT_SHIFT 5 /* IM_FLL_LOCK_EINT */ +#define WM8962_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */ +#define WM8962_IM_PLL3_LOCK_EINT 0x0008 /* IM_PLL3_LOCK_EINT */ +#define WM8962_IM_PLL3_LOCK_EINT_MASK 0x0008 /* IM_PLL3_LOCK_EINT */ +#define WM8962_IM_PLL3_LOCK_EINT_SHIFT 3 /* IM_PLL3_LOCK_EINT */ +#define WM8962_IM_PLL3_LOCK_EINT_WIDTH 1 /* IM_PLL3_LOCK_EINT */ +#define WM8962_IM_PLL2_LOCK_EINT 0x0004 /* IM_PLL2_LOCK_EINT */ +#define WM8962_IM_PLL2_LOCK_EINT_MASK 0x0004 /* IM_PLL2_LOCK_EINT */ +#define WM8962_IM_PLL2_LOCK_EINT_SHIFT 2 /* IM_PLL2_LOCK_EINT */ +#define WM8962_IM_PLL2_LOCK_EINT_WIDTH 1 /* IM_PLL2_LOCK_EINT */ +#define WM8962_IM_TEMP_SHUT_EINT 0x0001 /* IM_TEMP_SHUT_EINT */ +#define WM8962_IM_TEMP_SHUT_EINT_MASK 0x0001 /* IM_TEMP_SHUT_EINT */ +#define WM8962_IM_TEMP_SHUT_EINT_SHIFT 0 /* IM_TEMP_SHUT_EINT */ +#define WM8962_IM_TEMP_SHUT_EINT_WIDTH 1 /* IM_TEMP_SHUT_EINT */ + +/* + * R576 (0x240) - Interrupt Control + */ +#define WM8962_IRQ_POL 0x0001 /* IRQ_POL */ +#define WM8962_IRQ_POL_MASK 0x0001 /* IRQ_POL */ +#define WM8962_IRQ_POL_SHIFT 0 /* IRQ_POL */ +#define WM8962_IRQ_POL_WIDTH 1 /* IRQ_POL */ + +/* + * R584 (0x248) - IRQ Debounce + */ +#define WM8962_FLL_LOCK_DB 0x0020 /* FLL_LOCK_DB */ +#define WM8962_FLL_LOCK_DB_MASK 0x0020 /* FLL_LOCK_DB */ +#define WM8962_FLL_LOCK_DB_SHIFT 5 /* FLL_LOCK_DB */ +#define WM8962_FLL_LOCK_DB_WIDTH 1 /* FLL_LOCK_DB */ +#define WM8962_PLL3_LOCK_DB 0x0008 /* PLL3_LOCK_DB */ +#define WM8962_PLL3_LOCK_DB_MASK 0x0008 /* PLL3_LOCK_DB */ +#define WM8962_PLL3_LOCK_DB_SHIFT 3 /* PLL3_LOCK_DB */ +#define WM8962_PLL3_LOCK_DB_WIDTH 1 /* PLL3_LOCK_DB */ +#define WM8962_PLL2_LOCK_DB 0x0004 /* PLL2_LOCK_DB */ +#define WM8962_PLL2_LOCK_DB_MASK 0x0004 /* PLL2_LOCK_DB */ +#define WM8962_PLL2_LOCK_DB_SHIFT 2 /* PLL2_LOCK_DB */ +#define WM8962_PLL2_LOCK_DB_WIDTH 1 /* PLL2_LOCK_DB */ +#define WM8962_TEMP_SHUT_DB 0x0001 /* TEMP_SHUT_DB */ +#define WM8962_TEMP_SHUT_DB_MASK 0x0001 /* TEMP_SHUT_DB */ +#define WM8962_TEMP_SHUT_DB_SHIFT 0 /* TEMP_SHUT_DB */ +#define WM8962_TEMP_SHUT_DB_WIDTH 1 /* TEMP_SHUT_DB */ + +/* + * R586 (0x24A) - MICINT Source Pol + */ +#define WM8962_MICSCD_IRQ_POL 0x8000 /* MICSCD_IRQ_POL */ +#define WM8962_MICSCD_IRQ_POL_MASK 0x8000 /* MICSCD_IRQ_POL */ +#define WM8962_MICSCD_IRQ_POL_SHIFT 15 /* MICSCD_IRQ_POL */ +#define WM8962_MICSCD_IRQ_POL_WIDTH 1 /* MICSCD_IRQ_POL */ +#define WM8962_MICD_IRQ_POL 0x4000 /* MICD_IRQ_POL */ +#define WM8962_MICD_IRQ_POL_MASK 0x4000 /* MICD_IRQ_POL */ +#define WM8962_MICD_IRQ_POL_SHIFT 14 /* MICD_IRQ_POL */ +#define WM8962_MICD_IRQ_POL_WIDTH 1 /* MICD_IRQ_POL */ + +/* + * R768 (0x300) - DSP2 Power Management + */ +#define WM8962_DSP2_ENA 0x0001 /* DSP2_ENA */ +#define WM8962_DSP2_ENA_MASK 0x0001 /* DSP2_ENA */ +#define WM8962_DSP2_ENA_SHIFT 0 /* DSP2_ENA */ +#define WM8962_DSP2_ENA_WIDTH 1 /* DSP2_ENA */ + +/* + * R1037 (0x40D) - DSP2_ExecControl + */ +#define WM8962_DSP2_STOPC 0x0020 /* DSP2_STOPC */ +#define WM8962_DSP2_STOPC_MASK 0x0020 /* DSP2_STOPC */ +#define WM8962_DSP2_STOPC_SHIFT 5 /* DSP2_STOPC */ +#define WM8962_DSP2_STOPC_WIDTH 1 /* DSP2_STOPC */ +#define WM8962_DSP2_STOPS 0x0010 /* DSP2_STOPS */ +#define WM8962_DSP2_STOPS_MASK 0x0010 /* DSP2_STOPS */ +#define WM8962_DSP2_STOPS_SHIFT 4 /* DSP2_STOPS */ +#define WM8962_DSP2_STOPS_WIDTH 1 /* DSP2_STOPS */ +#define WM8962_DSP2_STOPI 0x0008 /* DSP2_STOPI */ +#define WM8962_DSP2_STOPI_MASK 0x0008 /* DSP2_STOPI */ +#define WM8962_DSP2_STOPI_SHIFT 3 /* DSP2_STOPI */ +#define WM8962_DSP2_STOPI_WIDTH 1 /* DSP2_STOPI */ +#define WM8962_DSP2_STOP 0x0004 /* DSP2_STOP */ +#define WM8962_DSP2_STOP_MASK 0x0004 /* DSP2_STOP */ +#define WM8962_DSP2_STOP_SHIFT 2 /* DSP2_STOP */ +#define WM8962_DSP2_STOP_WIDTH 1 /* DSP2_STOP */ +#define WM8962_DSP2_RUNR 0x0002 /* DSP2_RUNR */ +#define WM8962_DSP2_RUNR_MASK 0x0002 /* DSP2_RUNR */ +#define WM8962_DSP2_RUNR_SHIFT 1 /* DSP2_RUNR */ +#define WM8962_DSP2_RUNR_WIDTH 1 /* DSP2_RUNR */ +#define WM8962_DSP2_RUN 0x0001 /* DSP2_RUN */ +#define WM8962_DSP2_RUN_MASK 0x0001 /* DSP2_RUN */ +#define WM8962_DSP2_RUN_SHIFT 0 /* DSP2_RUN */ +#define WM8962_DSP2_RUN_WIDTH 1 /* DSP2_RUN */ + +/* + * R8192 (0x2000) - DSP2 Instruction RAM 0 + */ +#define WM8962_DSP2_INSTR_RAM_1024_10_9_0_MASK 0x03FF /* DSP2_INSTR_RAM_1024_10_9_0 - [9:0] */ +#define WM8962_DSP2_INSTR_RAM_1024_10_9_0_SHIFT 0 /* DSP2_INSTR_RAM_1024_10_9_0 - [9:0] */ +#define WM8962_DSP2_INSTR_RAM_1024_10_9_0_WIDTH 10 /* DSP2_INSTR_RAM_1024_10_9_0 - [9:0] */ + +/* + * R9216 (0x2400) - DSP2 Address RAM 2 + */ +#define WM8962_DSP2_ADDR_RAM_1024_38_37_32_MASK 0x003F /* DSP2_ADDR_RAM_1024_38_37_32 - [5:0] */ +#define WM8962_DSP2_ADDR_RAM_1024_38_37_32_SHIFT 0 /* DSP2_ADDR_RAM_1024_38_37_32 - [5:0] */ +#define WM8962_DSP2_ADDR_RAM_1024_38_37_32_WIDTH 6 /* DSP2_ADDR_RAM_1024_38_37_32 - [5:0] */ + +/* + * R9217 (0x2401) - DSP2 Address RAM 1 + */ +#define WM8962_DSP2_ADDR_RAM_1024_38_31_16_MASK 0xFFFF /* DSP2_ADDR_RAM_1024_38_31_16 - [15:0] */ +#define WM8962_DSP2_ADDR_RAM_1024_38_31_16_SHIFT 0 /* DSP2_ADDR_RAM_1024_38_31_16 - [15:0] */ +#define WM8962_DSP2_ADDR_RAM_1024_38_31_16_WIDTH 16 /* DSP2_ADDR_RAM_1024_38_31_16 - [15:0] */ + +/* + * R9218 (0x2402) - DSP2 Address RAM 0 + */ +#define WM8962_DSP2_ADDR_RAM_1024_38_15_0_MASK 0xFFFF /* DSP2_ADDR_RAM_1024_38_15_0 - [15:0] */ +#define WM8962_DSP2_ADDR_RAM_1024_38_15_0_SHIFT 0 /* DSP2_ADDR_RAM_1024_38_15_0 - [15:0] */ +#define WM8962_DSP2_ADDR_RAM_1024_38_15_0_WIDTH 16 /* DSP2_ADDR_RAM_1024_38_15_0 - [15:0] */ + +/* + * R12288 (0x3000) - DSP2 Data1 RAM 1 + */ +#define WM8962_DSP2_DATA1_RAM_384_24_23_16_MASK 0x00FF /* DSP2_DATA1_RAM_384_24_23_16 - [7:0] */ +#define WM8962_DSP2_DATA1_RAM_384_24_23_16_SHIFT 0 /* DSP2_DATA1_RAM_384_24_23_16 - [7:0] */ +#define WM8962_DSP2_DATA1_RAM_384_24_23_16_WIDTH 8 /* DSP2_DATA1_RAM_384_24_23_16 - [7:0] */ + +/* + * R12289 (0x3001) - DSP2 Data1 RAM 0 + */ +#define WM8962_DSP2_DATA1_RAM_384_24_15_0_MASK 0xFFFF /* DSP2_DATA1_RAM_384_24_15_0 - [15:0] */ +#define WM8962_DSP2_DATA1_RAM_384_24_15_0_SHIFT 0 /* DSP2_DATA1_RAM_384_24_15_0 - [15:0] */ +#define WM8962_DSP2_DATA1_RAM_384_24_15_0_WIDTH 16 /* DSP2_DATA1_RAM_384_24_15_0 - [15:0] */ + +/* + * R13312 (0x3400) - DSP2 Data2 RAM 1 + */ +#define WM8962_DSP2_DATA2_RAM_384_24_23_16_MASK 0x00FF /* DSP2_DATA2_RAM_384_24_23_16 - [7:0] */ +#define WM8962_DSP2_DATA2_RAM_384_24_23_16_SHIFT 0 /* DSP2_DATA2_RAM_384_24_23_16 - [7:0] */ +#define WM8962_DSP2_DATA2_RAM_384_24_23_16_WIDTH 8 /* DSP2_DATA2_RAM_384_24_23_16 - [7:0] */ + +/* + * R13313 (0x3401) - DSP2 Data2 RAM 0 + */ +#define WM8962_DSP2_DATA2_RAM_384_24_15_0_MASK 0xFFFF /* DSP2_DATA2_RAM_384_24_15_0 - [15:0] */ +#define WM8962_DSP2_DATA2_RAM_384_24_15_0_SHIFT 0 /* DSP2_DATA2_RAM_384_24_15_0 - [15:0] */ +#define WM8962_DSP2_DATA2_RAM_384_24_15_0_WIDTH 16 /* DSP2_DATA2_RAM_384_24_15_0 - [15:0] */ + +/* + * R14336 (0x3800) - DSP2 Data3 RAM 1 + */ +#define WM8962_DSP2_DATA3_RAM_384_24_23_16_MASK 0x00FF /* DSP2_DATA3_RAM_384_24_23_16 - [7:0] */ +#define WM8962_DSP2_DATA3_RAM_384_24_23_16_SHIFT 0 /* DSP2_DATA3_RAM_384_24_23_16 - [7:0] */ +#define WM8962_DSP2_DATA3_RAM_384_24_23_16_WIDTH 8 /* DSP2_DATA3_RAM_384_24_23_16 - [7:0] */ + +/* + * R14337 (0x3801) - DSP2 Data3 RAM 0 + */ +#define WM8962_DSP2_DATA3_RAM_384_24_15_0_MASK 0xFFFF /* DSP2_DATA3_RAM_384_24_15_0 - [15:0] */ +#define WM8962_DSP2_DATA3_RAM_384_24_15_0_SHIFT 0 /* DSP2_DATA3_RAM_384_24_15_0 - [15:0] */ +#define WM8962_DSP2_DATA3_RAM_384_24_15_0_WIDTH 16 /* DSP2_DATA3_RAM_384_24_15_0 - [15:0] */ + +/* + * R15360 (0x3C00) - DSP2 Coeff RAM 0 + */ +#define WM8962_DSP2_CMAP_RAM_384_11_10_0_MASK 0x07FF /* DSP2_CMAP_RAM_384_11_10_0 - [10:0] */ +#define WM8962_DSP2_CMAP_RAM_384_11_10_0_SHIFT 0 /* DSP2_CMAP_RAM_384_11_10_0 - [10:0] */ +#define WM8962_DSP2_CMAP_RAM_384_11_10_0_WIDTH 11 /* DSP2_CMAP_RAM_384_11_10_0 - [10:0] */ + +/* + * R16384 (0x4000) - RETUNEADC_SHARED_COEFF_1 + */ +#define WM8962_ADC_RETUNE_SCV 0x0080 /* ADC_RETUNE_SCV */ +#define WM8962_ADC_RETUNE_SCV_MASK 0x0080 /* ADC_RETUNE_SCV */ +#define WM8962_ADC_RETUNE_SCV_SHIFT 7 /* ADC_RETUNE_SCV */ +#define WM8962_ADC_RETUNE_SCV_WIDTH 1 /* ADC_RETUNE_SCV */ +#define WM8962_RETUNEADC_SHARED_COEFF_22_16_MASK 0x007F /* RETUNEADC_SHARED_COEFF_22_16 - [6:0] */ +#define WM8962_RETUNEADC_SHARED_COEFF_22_16_SHIFT 0 /* RETUNEADC_SHARED_COEFF_22_16 - [6:0] */ +#define WM8962_RETUNEADC_SHARED_COEFF_22_16_WIDTH 7 /* RETUNEADC_SHARED_COEFF_22_16 - [6:0] */ + +/* + * R16385 (0x4001) - RETUNEADC_SHARED_COEFF_0 + */ +#define WM8962_RETUNEADC_SHARED_COEFF_15_00_MASK 0xFFFF /* RETUNEADC_SHARED_COEFF_15_00 - [15:0] */ +#define WM8962_RETUNEADC_SHARED_COEFF_15_00_SHIFT 0 /* RETUNEADC_SHARED_COEFF_15_00 - [15:0] */ +#define WM8962_RETUNEADC_SHARED_COEFF_15_00_WIDTH 16 /* RETUNEADC_SHARED_COEFF_15_00 - [15:0] */ + +/* + * R16386 (0x4002) - RETUNEDAC_SHARED_COEFF_1 + */ +#define WM8962_DAC_RETUNE_SCV 0x0080 /* DAC_RETUNE_SCV */ +#define WM8962_DAC_RETUNE_SCV_MASK 0x0080 /* DAC_RETUNE_SCV */ +#define WM8962_DAC_RETUNE_SCV_SHIFT 7 /* DAC_RETUNE_SCV */ +#define WM8962_DAC_RETUNE_SCV_WIDTH 1 /* DAC_RETUNE_SCV */ +#define WM8962_RETUNEDAC_SHARED_COEFF_23_16_MASK 0x007F /* RETUNEDAC_SHARED_COEFF_23_16 - [6:0] */ +#define WM8962_RETUNEDAC_SHARED_COEFF_23_16_SHIFT 0 /* RETUNEDAC_SHARED_COEFF_23_16 - [6:0] */ +#define WM8962_RETUNEDAC_SHARED_COEFF_23_16_WIDTH 7 /* RETUNEDAC_SHARED_COEFF_23_16 - [6:0] */ + +/* + * R16387 (0x4003) - RETUNEDAC_SHARED_COEFF_0 + */ +#define WM8962_RETUNEDAC_SHARED_COEFF_15_00_MASK 0xFFFF /* RETUNEDAC_SHARED_COEFF_15_00 - [15:0] */ +#define WM8962_RETUNEDAC_SHARED_COEFF_15_00_SHIFT 0 /* RETUNEDAC_SHARED_COEFF_15_00 - [15:0] */ +#define WM8962_RETUNEDAC_SHARED_COEFF_15_00_WIDTH 16 /* RETUNEDAC_SHARED_COEFF_15_00 - [15:0] */ + +/* + * R16388 (0x4004) - SOUNDSTAGE_ENABLES_1 + */ +#define WM8962_SOUNDSTAGE_ENABLES_23_16_MASK 0x00FF /* SOUNDSTAGE_ENABLES_23_16 - [7:0] */ +#define WM8962_SOUNDSTAGE_ENABLES_23_16_SHIFT 0 /* SOUNDSTAGE_ENABLES_23_16 - [7:0] */ +#define WM8962_SOUNDSTAGE_ENABLES_23_16_WIDTH 8 /* SOUNDSTAGE_ENABLES_23_16 - [7:0] */ + +/* + * R16389 (0x4005) - SOUNDSTAGE_ENABLES_0 + */ +#define WM8962_SOUNDSTAGE_ENABLES_15_06_MASK 0xFFC0 /* SOUNDSTAGE_ENABLES_15_06 - [15:6] */ +#define WM8962_SOUNDSTAGE_ENABLES_15_06_SHIFT 6 /* SOUNDSTAGE_ENABLES_15_06 - [15:6] */ +#define WM8962_SOUNDSTAGE_ENABLES_15_06_WIDTH 10 /* SOUNDSTAGE_ENABLES_15_06 - [15:6] */ +#define WM8962_RTN_ADC_ENA 0x0020 /* RTN_ADC_ENA */ +#define WM8962_RTN_ADC_ENA_MASK 0x0020 /* RTN_ADC_ENA */ +#define WM8962_RTN_ADC_ENA_SHIFT 5 /* RTN_ADC_ENA */ +#define WM8962_RTN_ADC_ENA_WIDTH 1 /* RTN_ADC_ENA */ +#define WM8962_RTN_DAC_ENA 0x0010 /* RTN_DAC_ENA */ +#define WM8962_RTN_DAC_ENA_MASK 0x0010 /* RTN_DAC_ENA */ +#define WM8962_RTN_DAC_ENA_SHIFT 4 /* RTN_DAC_ENA */ +#define WM8962_RTN_DAC_ENA_WIDTH 1 /* RTN_DAC_ENA */ +#define WM8962_HDBASS_ENA 0x0008 /* HDBASS_ENA */ +#define WM8962_HDBASS_ENA_MASK 0x0008 /* HDBASS_ENA */ +#define WM8962_HDBASS_ENA_SHIFT 3 /* HDBASS_ENA */ +#define WM8962_HDBASS_ENA_WIDTH 1 /* HDBASS_ENA */ +#define WM8962_HPF2_ENA 0x0004 /* HPF2_ENA */ +#define WM8962_HPF2_ENA_MASK 0x0004 /* HPF2_ENA */ +#define WM8962_HPF2_ENA_SHIFT 2 /* HPF2_ENA */ +#define WM8962_HPF2_ENA_WIDTH 1 /* HPF2_ENA */ +#define WM8962_HPF1_ENA 0x0002 /* HPF1_ENA */ +#define WM8962_HPF1_ENA_MASK 0x0002 /* HPF1_ENA */ +#define WM8962_HPF1_ENA_SHIFT 1 /* HPF1_ENA */ +#define WM8962_HPF1_ENA_WIDTH 1 /* HPF1_ENA */ +#define WM8962_VSS_ENA 0x0001 /* VSS_ENA */ +#define WM8962_VSS_ENA_MASK 0x0001 /* VSS_ENA */ +#define WM8962_VSS_ENA_SHIFT 0 /* VSS_ENA */ +#define WM8962_VSS_ENA_WIDTH 1 /* VSS_ENA */ + +int wm8962_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack); + +#endif diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c new file mode 100644 index 0000000000..e88f323d28 --- /dev/null +++ b/sound/soc/codecs/wm8971.c @@ -0,0 +1,711 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * wm8971.c -- WM8971 ALSA SoC Audio driver + * + * Copyright 2005 Lab126, Inc. + * + * Author: Kenneth Kiraly + * + * Based on wm8753.c by Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8971.h" + +#define WM8971_REG_COUNT 43 + +/* codec private data */ +struct wm8971_priv { + unsigned int sysclk; + struct delayed_work charge_work; + struct regmap *regmap; +}; + +/* + * wm8971 register cache + * We can't read the WM8971 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const struct reg_default wm8971_reg_defaults[] = { + { 0, 0x0097 }, + { 1, 0x0097 }, + { 2, 0x0079 }, + { 3, 0x0079 }, + { 4, 0x0000 }, + { 5, 0x0008 }, + { 6, 0x0000 }, + { 7, 0x000a }, + { 8, 0x0000 }, + { 9, 0x0000 }, + { 10, 0x00ff }, + { 11, 0x00ff }, + { 12, 0x000f }, + { 13, 0x000f }, + { 14, 0x0000 }, + { 15, 0x0000 }, + { 16, 0x0000 }, + { 17, 0x007b }, + { 18, 0x0000 }, + { 19, 0x0032 }, + { 20, 0x0000 }, + { 21, 0x00c3 }, + { 22, 0x00c3 }, + { 23, 0x00c0 }, + { 24, 0x0000 }, + { 25, 0x0000 }, + { 26, 0x0000 }, + { 27, 0x0000 }, + { 28, 0x0000 }, + { 29, 0x0000 }, + { 30, 0x0000 }, + { 31, 0x0000 }, + { 32, 0x0000 }, + { 33, 0x0000 }, + { 34, 0x0050 }, + { 35, 0x0050 }, + { 36, 0x0050 }, + { 37, 0x0050 }, + { 38, 0x0050 }, + { 39, 0x0050 }, + { 40, 0x0079 }, + { 41, 0x0079 }, + { 42, 0x0079 }, +}; + +#define wm8971_reset(c) snd_soc_component_write(c, WM8971_RESET, 0) + +/* WM8971 Controls */ +static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" }; +static const char *wm8971_bass_filter[] = { "130Hz @ 48kHz", + "200Hz @ 48kHz" }; +static const char *wm8971_treble[] = { "8kHz", "4kHz" }; +static const char *wm8971_alc_func[] = { "Off", "Right", "Left", "Stereo" }; +static const char *wm8971_ng_type[] = { "Constant PGA Gain", + "Mute ADC Output" }; +static const char *wm8971_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" }; +static const char *wm8971_mono_mux[] = {"Stereo", "Mono (Left)", + "Mono (Right)", "Digital Mono"}; +static const char *wm8971_dac_phase[] = { "Non Inverted", "Inverted" }; +static const char *wm8971_lline_mux[] = {"Line", "NC", "NC", "PGA", + "Differential"}; +static const char *wm8971_rline_mux[] = {"Line", "Mic", "NC", "PGA", + "Differential"}; +static const char *wm8971_lpga_sel[] = {"Line", "NC", "NC", "Differential"}; +static const char *wm8971_rpga_sel[] = {"Line", "Mic", "NC", "Differential"}; +static const char *wm8971_adcpol[] = {"Normal", "L Invert", "R Invert", + "L + R Invert"}; + +static const struct soc_enum wm8971_enum[] = { + SOC_ENUM_SINGLE(WM8971_BASS, 7, 2, wm8971_bass), /* 0 */ + SOC_ENUM_SINGLE(WM8971_BASS, 6, 2, wm8971_bass_filter), + SOC_ENUM_SINGLE(WM8971_TREBLE, 6, 2, wm8971_treble), + SOC_ENUM_SINGLE(WM8971_ALC1, 7, 4, wm8971_alc_func), + SOC_ENUM_SINGLE(WM8971_NGATE, 1, 2, wm8971_ng_type), /* 4 */ + SOC_ENUM_SINGLE(WM8971_ADCDAC, 1, 4, wm8971_deemp), + SOC_ENUM_SINGLE(WM8971_ADCTL1, 4, 4, wm8971_mono_mux), + SOC_ENUM_SINGLE(WM8971_ADCTL1, 1, 2, wm8971_dac_phase), + SOC_ENUM_SINGLE(WM8971_LOUTM1, 0, 5, wm8971_lline_mux), /* 8 */ + SOC_ENUM_SINGLE(WM8971_ROUTM1, 0, 5, wm8971_rline_mux), + SOC_ENUM_SINGLE(WM8971_LADCIN, 6, 4, wm8971_lpga_sel), + SOC_ENUM_SINGLE(WM8971_RADCIN, 6, 4, wm8971_rpga_sel), + SOC_ENUM_SINGLE(WM8971_ADCDAC, 5, 4, wm8971_adcpol), /* 12 */ + SOC_ENUM_SINGLE(WM8971_ADCIN, 6, 4, wm8971_mono_mux), +}; + +static const struct snd_kcontrol_new wm8971_snd_controls[] = { + SOC_DOUBLE_R("Capture Volume", WM8971_LINVOL, WM8971_RINVOL, 0, 63, 0), + SOC_DOUBLE_R("Capture ZC Switch", WM8971_LINVOL, WM8971_RINVOL, + 6, 1, 0), + SOC_DOUBLE_R("Capture Switch", WM8971_LINVOL, WM8971_RINVOL, 7, 1, 1), + + SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8971_LOUT1V, + WM8971_ROUT1V, 7, 1, 0), + SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8971_LOUT2V, + WM8971_ROUT2V, 7, 1, 0), + SOC_SINGLE("Mono Playback ZC Switch", WM8971_MOUTV, 7, 1, 0), + + SOC_DOUBLE_R("PCM Volume", WM8971_LDAC, WM8971_RDAC, 0, 255, 0), + + SOC_DOUBLE_R("Bypass Left Playback Volume", WM8971_LOUTM1, + WM8971_LOUTM2, 4, 7, 1), + SOC_DOUBLE_R("Bypass Right Playback Volume", WM8971_ROUTM1, + WM8971_ROUTM2, 4, 7, 1), + SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8971_MOUTM1, + WM8971_MOUTM2, 4, 7, 1), + + SOC_DOUBLE_R("Headphone Playback Volume", WM8971_LOUT1V, + WM8971_ROUT1V, 0, 127, 0), + SOC_DOUBLE_R("Speaker Playback Volume", WM8971_LOUT2V, + WM8971_ROUT2V, 0, 127, 0), + + SOC_ENUM("Bass Boost", wm8971_enum[0]), + SOC_ENUM("Bass Filter", wm8971_enum[1]), + SOC_SINGLE("Bass Volume", WM8971_BASS, 0, 7, 1), + + SOC_SINGLE("Treble Volume", WM8971_TREBLE, 0, 7, 0), + SOC_ENUM("Treble Cut-off", wm8971_enum[2]), + + SOC_SINGLE("Capture Filter Switch", WM8971_ADCDAC, 0, 1, 1), + + SOC_SINGLE("ALC Target Volume", WM8971_ALC1, 0, 7, 0), + SOC_SINGLE("ALC Max Volume", WM8971_ALC1, 4, 7, 0), + + SOC_SINGLE("ALC Capture Target Volume", WM8971_ALC1, 0, 7, 0), + SOC_SINGLE("ALC Capture Max Volume", WM8971_ALC1, 4, 7, 0), + SOC_ENUM("ALC Capture Function", wm8971_enum[3]), + SOC_SINGLE("ALC Capture ZC Switch", WM8971_ALC2, 7, 1, 0), + SOC_SINGLE("ALC Capture Hold Time", WM8971_ALC2, 0, 15, 0), + SOC_SINGLE("ALC Capture Decay Time", WM8971_ALC3, 4, 15, 0), + SOC_SINGLE("ALC Capture Attack Time", WM8971_ALC3, 0, 15, 0), + SOC_SINGLE("ALC Capture NG Threshold", WM8971_NGATE, 3, 31, 0), + SOC_ENUM("ALC Capture NG Type", wm8971_enum[4]), + SOC_SINGLE("ALC Capture NG Switch", WM8971_NGATE, 0, 1, 0), + + SOC_SINGLE("Capture 6dB Attenuate", WM8971_ADCDAC, 8, 1, 0), + SOC_SINGLE("Playback 6dB Attenuate", WM8971_ADCDAC, 7, 1, 0), + + SOC_ENUM("Playback De-emphasis", wm8971_enum[5]), + SOC_ENUM("Playback Function", wm8971_enum[6]), + SOC_ENUM("Playback Phase", wm8971_enum[7]), + + SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0), +}; + +/* + * DAPM Controls + */ + +/* Left Mixer */ +static const struct snd_kcontrol_new wm8971_left_mixer_controls[] = { +SOC_DAPM_SINGLE("Playback Switch", WM8971_LOUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_LOUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8971_LOUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_LOUTM2, 7, 1, 0), +}; + +/* Right Mixer */ +static const struct snd_kcontrol_new wm8971_right_mixer_controls[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8971_ROUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_ROUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Playback Switch", WM8971_ROUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_ROUTM2, 7, 1, 0), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new wm8971_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8971_MOUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_MOUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8971_MOUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_MOUTM2, 7, 1, 0), +}; + +/* Left Line Mux */ +static const struct snd_kcontrol_new wm8971_left_line_controls = +SOC_DAPM_ENUM("Route", wm8971_enum[8]); + +/* Right Line Mux */ +static const struct snd_kcontrol_new wm8971_right_line_controls = +SOC_DAPM_ENUM("Route", wm8971_enum[9]); + +/* Left PGA Mux */ +static const struct snd_kcontrol_new wm8971_left_pga_controls = +SOC_DAPM_ENUM("Route", wm8971_enum[10]); + +/* Right PGA Mux */ +static const struct snd_kcontrol_new wm8971_right_pga_controls = +SOC_DAPM_ENUM("Route", wm8971_enum[11]); + +/* Mono ADC Mux */ +static const struct snd_kcontrol_new wm8971_monomux_controls = +SOC_DAPM_ENUM("Route", wm8971_enum[13]); + +static const struct snd_soc_dapm_widget wm8971_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + &wm8971_left_mixer_controls[0], + ARRAY_SIZE(wm8971_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + &wm8971_right_mixer_controls[0], + ARRAY_SIZE(wm8971_right_mixer_controls)), + SND_SOC_DAPM_MIXER("Mono Mixer", WM8971_PWR2, 2, 0, + &wm8971_mono_mixer_controls[0], + ARRAY_SIZE(wm8971_mono_mixer_controls)), + + SND_SOC_DAPM_PGA("Right Out 2", WM8971_PWR2, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 2", WM8971_PWR2, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 1", WM8971_PWR2, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 1", WM8971_PWR2, 6, 0, NULL, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8971_PWR2, 7, 0), + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8971_PWR2, 8, 0), + SND_SOC_DAPM_PGA("Mono Out 1", WM8971_PWR2, 2, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", WM8971_PWR1, 1, 0, NULL, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8971_PWR1, 2, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8971_PWR1, 3, 0), + + SND_SOC_DAPM_MUX("Left PGA Mux", WM8971_PWR1, 5, 0, + &wm8971_left_pga_controls), + SND_SOC_DAPM_MUX("Right PGA Mux", WM8971_PWR1, 4, 0, + &wm8971_right_pga_controls), + SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, + &wm8971_left_line_controls), + SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, + &wm8971_right_line_controls), + + SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, + &wm8971_monomux_controls), + SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, + &wm8971_monomux_controls), + + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("MONO"), + + SND_SOC_DAPM_INPUT("LINPUT1"), + SND_SOC_DAPM_INPUT("RINPUT1"), + SND_SOC_DAPM_INPUT("MIC"), +}; + +static const struct snd_soc_dapm_route wm8971_dapm_routes[] = { + /* left mixer */ + {"Left Mixer", "Playback Switch", "Left DAC"}, + {"Left Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Left Mixer", "Right Playback Switch", "Right DAC"}, + {"Left Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* right mixer */ + {"Right Mixer", "Left Playback Switch", "Left DAC"}, + {"Right Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Right Mixer", "Playback Switch", "Right DAC"}, + {"Right Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* left out 1 */ + {"Left Out 1", NULL, "Left Mixer"}, + {"LOUT1", NULL, "Left Out 1"}, + + /* left out 2 */ + {"Left Out 2", NULL, "Left Mixer"}, + {"LOUT2", NULL, "Left Out 2"}, + + /* right out 1 */ + {"Right Out 1", NULL, "Right Mixer"}, + {"ROUT1", NULL, "Right Out 1"}, + + /* right out 2 */ + {"Right Out 2", NULL, "Right Mixer"}, + {"ROUT2", NULL, "Right Out 2"}, + + /* mono mixer */ + {"Mono Mixer", "Left Playback Switch", "Left DAC"}, + {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Mono Mixer", "Right Playback Switch", "Right DAC"}, + {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* mono out */ + {"Mono Out", NULL, "Mono Mixer"}, + {"MONO1", NULL, "Mono Out"}, + + /* Left Line Mux */ + {"Left Line Mux", "Line", "LINPUT1"}, + {"Left Line Mux", "PGA", "Left PGA Mux"}, + {"Left Line Mux", "Differential", "Differential Mux"}, + + /* Right Line Mux */ + {"Right Line Mux", "Line", "RINPUT1"}, + {"Right Line Mux", "Mic", "MIC"}, + {"Right Line Mux", "PGA", "Right PGA Mux"}, + {"Right Line Mux", "Differential", "Differential Mux"}, + + /* Left PGA Mux */ + {"Left PGA Mux", "Line", "LINPUT1"}, + {"Left PGA Mux", "Differential", "Differential Mux"}, + + /* Right PGA Mux */ + {"Right PGA Mux", "Line", "RINPUT1"}, + {"Right PGA Mux", "Differential", "Differential Mux"}, + + /* Differential Mux */ + {"Differential Mux", "Line", "LINPUT1"}, + {"Differential Mux", "Line", "RINPUT1"}, + + /* Left ADC Mux */ + {"Left ADC Mux", "Stereo", "Left PGA Mux"}, + {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"}, + {"Left ADC Mux", "Digital Mono", "Left PGA Mux"}, + + /* Right ADC Mux */ + {"Right ADC Mux", "Stereo", "Right PGA Mux"}, + {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"}, + {"Right ADC Mux", "Digital Mono", "Right PGA Mux"}, + + /* ADC */ + {"Left ADC", NULL, "Left ADC Mux"}, + {"Right ADC", NULL, "Right ADC Mux"}, +}; + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u8 sr:5; + u8 usb:1; +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 8k */ + {12288000, 8000, 1536, 0x6, 0x0}, + {11289600, 8000, 1408, 0x16, 0x0}, + {18432000, 8000, 2304, 0x7, 0x0}, + {16934400, 8000, 2112, 0x17, 0x0}, + {12000000, 8000, 1500, 0x6, 0x1}, + + /* 11.025k */ + {11289600, 11025, 1024, 0x18, 0x0}, + {16934400, 11025, 1536, 0x19, 0x0}, + {12000000, 11025, 1088, 0x19, 0x1}, + + /* 16k */ + {12288000, 16000, 768, 0xa, 0x0}, + {18432000, 16000, 1152, 0xb, 0x0}, + {12000000, 16000, 750, 0xa, 0x1}, + + /* 22.05k */ + {11289600, 22050, 512, 0x1a, 0x0}, + {16934400, 22050, 768, 0x1b, 0x0}, + {12000000, 22050, 544, 0x1b, 0x1}, + + /* 32k */ + {12288000, 32000, 384, 0xc, 0x0}, + {18432000, 32000, 576, 0xd, 0x0}, + {12000000, 32000, 375, 0xa, 0x1}, + + /* 44.1k */ + {11289600, 44100, 256, 0x10, 0x0}, + {16934400, 44100, 384, 0x11, 0x0}, + {12000000, 44100, 272, 0x11, 0x1}, + + /* 48k */ + {12288000, 48000, 256, 0x0, 0x0}, + {18432000, 48000, 384, 0x1, 0x0}, + {12000000, 48000, 250, 0x0, 0x1}, + + /* 88.2k */ + {11289600, 88200, 128, 0x1e, 0x0}, + {16934400, 88200, 192, 0x1f, 0x0}, + {12000000, 88200, 136, 0x1f, 0x1}, + + /* 96k */ + {12288000, 96000, 128, 0xe, 0x0}, + {18432000, 96000, 192, 0xf, 0x0}, + {12000000, 96000, 125, 0xe, 0x1}, +}; + +static int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return -EINVAL; +} + +static int wm8971_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8971_priv *wm8971 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case 11289600: + case 12000000: + case 12288000: + case 16934400: + case 18432000: + wm8971->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0040; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8971_IFACE, iface); + return 0; +} + +static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8971_priv *wm8971 = snd_soc_component_get_drvdata(component); + u16 iface = snd_soc_component_read(component, WM8971_IFACE) & 0x1f3; + u16 srate = snd_soc_component_read(component, WM8971_SRATE) & 0x1c0; + int coeff = get_coeff(wm8971->sysclk, params_rate(params)); + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x0004; + break; + case 24: + iface |= 0x0008; + break; + case 32: + iface |= 0x000c; + break; + } + + /* set iface & srate */ + snd_soc_component_write(component, WM8971_IFACE, iface); + if (coeff >= 0) + snd_soc_component_write(component, WM8971_SRATE, srate | + (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb); + + return 0; +} + +static int wm8971_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, WM8971_ADCDAC) & 0xfff7; + + if (mute) + snd_soc_component_write(component, WM8971_ADCDAC, mute_reg | 0x8); + else + snd_soc_component_write(component, WM8971_ADCDAC, mute_reg); + return 0; +} + +static void wm8971_charge_work(struct work_struct *work) +{ + struct wm8971_priv *wm8971 = + container_of(work, struct wm8971_priv, charge_work.work); + + /* Set to 500k */ + regmap_update_bits(wm8971->regmap, WM8971_PWR1, 0x0180, 0x0100); +} + +static int wm8971_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8971_priv *wm8971 = snd_soc_component_get_drvdata(component); + u16 pwr_reg = snd_soc_component_read(component, WM8971_PWR1) & 0xfe3e; + + switch (level) { + case SND_SOC_BIAS_ON: + /* set vmid to 50k and unmute dac */ + snd_soc_component_write(component, WM8971_PWR1, pwr_reg | 0x00c1); + break; + case SND_SOC_BIAS_PREPARE: + /* Wait until fully charged */ + flush_delayed_work(&wm8971->charge_work); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + snd_soc_component_cache_sync(component); + /* charge output caps - set vmid to 5k for quick power up */ + snd_soc_component_write(component, WM8971_PWR1, pwr_reg | 0x01c0); + queue_delayed_work(system_power_efficient_wq, + &wm8971->charge_work, msecs_to_jiffies(1000)); + } else { + /* mute dac and set vmid to 500k, enable VREF */ + snd_soc_component_write(component, WM8971_PWR1, pwr_reg | 0x0140); + } + + break; + case SND_SOC_BIAS_OFF: + cancel_delayed_work_sync(&wm8971->charge_work); + snd_soc_component_write(component, WM8971_PWR1, 0x0001); + break; + } + return 0; +} + +#define WM8971_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8971_dai_ops = { + .hw_params = wm8971_pcm_hw_params, + .mute_stream = wm8971_mute, + .set_fmt = wm8971_set_dai_fmt, + .set_sysclk = wm8971_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8971_dai = { + .name = "wm8971-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8971_RATES, + .formats = WM8971_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8971_RATES, + .formats = WM8971_FORMATS,}, + .ops = &wm8971_dai_ops, +}; + +static int wm8971_probe(struct snd_soc_component *component) +{ + struct wm8971_priv *wm8971 = snd_soc_component_get_drvdata(component); + + INIT_DELAYED_WORK(&wm8971->charge_work, wm8971_charge_work); + + wm8971_reset(component); + + /* set the update bits */ + snd_soc_component_update_bits(component, WM8971_LDAC, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8971_RDAC, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8971_LOUT1V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8971_ROUT1V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8971_LOUT2V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8971_ROUT2V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8971_LINVOL, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8971_RINVOL, 0x0100, 0x0100); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8971 = { + .probe = wm8971_probe, + .set_bias_level = wm8971_set_bias_level, + .controls = wm8971_snd_controls, + .num_controls = ARRAY_SIZE(wm8971_snd_controls), + .dapm_widgets = wm8971_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8971_dapm_widgets), + .dapm_routes = wm8971_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8971_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8971_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8971_MOUTV, + + .reg_defaults = wm8971_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8971_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static int wm8971_i2c_probe(struct i2c_client *i2c) +{ + struct wm8971_priv *wm8971; + + wm8971 = devm_kzalloc(&i2c->dev, sizeof(struct wm8971_priv), + GFP_KERNEL); + if (wm8971 == NULL) + return -ENOMEM; + + wm8971->regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap); + if (IS_ERR(wm8971->regmap)) + return PTR_ERR(wm8971->regmap); + + i2c_set_clientdata(i2c, wm8971); + + return devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8971, &wm8971_dai, 1); +} + +static const struct i2c_device_id wm8971_i2c_id[] = { + { "wm8971", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id); + +static struct i2c_driver wm8971_i2c_driver = { + .driver = { + .name = "wm8971", + }, + .probe = wm8971_i2c_probe, + .id_table = wm8971_i2c_id, +}; + +module_i2c_driver(wm8971_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8971 driver"); +MODULE_AUTHOR("Lab126"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8971.h b/sound/soc/codecs/wm8971.h new file mode 100644 index 0000000000..46fa319786 --- /dev/null +++ b/sound/soc/codecs/wm8971.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * wm8971.h -- audio driver for WM8971 + * + * Copyright 2005 Lab126, Inc. + * + * Author: Kenneth Kiraly + */ + +#ifndef _WM8971_H +#define _WM8971_H + +#define WM8971_LINVOL 0x00 +#define WM8971_RINVOL 0x01 +#define WM8971_LOUT1V 0x02 +#define WM8971_ROUT1V 0x03 +#define WM8971_ADCDAC 0x05 +#define WM8971_IFACE 0x07 +#define WM8971_SRATE 0x08 +#define WM8971_LDAC 0x0a +#define WM8971_RDAC 0x0b +#define WM8971_BASS 0x0c +#define WM8971_TREBLE 0x0d +#define WM8971_RESET 0x0f +#define WM8971_ALC1 0x11 +#define WM8971_ALC2 0x12 +#define WM8971_ALC3 0x13 +#define WM8971_NGATE 0x14 +#define WM8971_LADC 0x15 +#define WM8971_RADC 0x16 +#define WM8971_ADCTL1 0x17 +#define WM8971_ADCTL2 0x18 +#define WM8971_PWR1 0x19 +#define WM8971_PWR2 0x1a +#define WM8971_ADCTL3 0x1b +#define WM8971_ADCIN 0x1f +#define WM8971_LADCIN 0x20 +#define WM8971_RADCIN 0x21 +#define WM8971_LOUTM1 0x22 +#define WM8971_LOUTM2 0x23 +#define WM8971_ROUTM1 0x24 +#define WM8971_ROUTM2 0x25 +#define WM8971_MOUTM1 0x26 +#define WM8971_MOUTM2 0x27 +#define WM8971_LOUT2V 0x28 +#define WM8971_ROUT2V 0x29 +#define WM8971_MOUTV 0x2A + +#define WM8971_SYSCLK 0 + +#endif diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c new file mode 100644 index 0000000000..260bac695b --- /dev/null +++ b/sound/soc/codecs/wm8974.c @@ -0,0 +1,734 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8974.c -- WM8974 ALSA Soc Audio driver + * + * Copyright 2006-2009 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8974.h" + +struct wm8974_priv { + unsigned int mclk; + unsigned int fs; +}; + +static const struct reg_default wm8974_reg_defaults[] = { + { 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 }, + { 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 }, + { 8, 0x0000 }, { 9, 0x0000 }, { 10, 0x0000 }, { 11, 0x00ff }, + { 12, 0x0000 }, { 13, 0x0000 }, { 14, 0x0100 }, { 15, 0x00ff }, + { 16, 0x0000 }, { 17, 0x0000 }, { 18, 0x012c }, { 19, 0x002c }, + { 20, 0x002c }, { 21, 0x002c }, { 22, 0x002c }, { 23, 0x0000 }, + { 24, 0x0032 }, { 25, 0x0000 }, { 26, 0x0000 }, { 27, 0x0000 }, + { 28, 0x0000 }, { 29, 0x0000 }, { 30, 0x0000 }, { 31, 0x0000 }, + { 32, 0x0038 }, { 33, 0x000b }, { 34, 0x0032 }, { 35, 0x0000 }, + { 36, 0x0008 }, { 37, 0x000c }, { 38, 0x0093 }, { 39, 0x00e9 }, + { 40, 0x0000 }, { 41, 0x0000 }, { 42, 0x0000 }, { 43, 0x0000 }, + { 44, 0x0003 }, { 45, 0x0010 }, { 46, 0x0000 }, { 47, 0x0000 }, + { 48, 0x0000 }, { 49, 0x0002 }, { 50, 0x0000 }, { 51, 0x0000 }, + { 52, 0x0000 }, { 53, 0x0000 }, { 54, 0x0039 }, { 55, 0x0000 }, + { 56, 0x0000 }, +}; + +#define WM8974_POWER1_BIASEN 0x08 +#define WM8974_POWER1_BUFIOEN 0x04 + +#define wm8974_reset(c) snd_soc_component_write(c, WM8974_RESET, 0) + +static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" }; +static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" }; +static const char *wm8974_eqmode[] = {"Capture", "Playback" }; +static const char *wm8974_bw[] = {"Narrow", "Wide" }; +static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" }; +static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" }; +static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" }; +static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" }; +static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" }; +static const char *wm8974_alc[] = {"ALC", "Limiter" }; + +static const struct soc_enum wm8974_enum[] = { + SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */ + SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */ + SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp), + SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode), + + SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1), + SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw), + SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2), + SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw), + + SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3), + SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw), + SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4), + SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw), + + SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5), + SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc), +}; + +static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" }; + +static SOC_ENUM_SINGLE_DECL(wm8974_auxmode, + WM8974_INPUT, 3, wm8974_auxmode_text); + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); + +static const struct snd_kcontrol_new wm8974_snd_controls[] = { + +SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0), + +SOC_ENUM("DAC Companding", wm8974_enum[1]), +SOC_ENUM("ADC Companding", wm8974_enum[0]), + +SOC_ENUM("Playback De-emphasis", wm8974_enum[2]), +SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0), + +SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv), + +SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0), +SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0), +SOC_SINGLE("ADC Inversion Switch", WM8974_ADC, 0, 1, 0), + +SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL, 0, 255, 0, digital_tlv), + +SOC_ENUM("Equaliser Function", wm8974_enum[3]), +SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]), +SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1, 0, 24, 1, eq_tlv), + +SOC_ENUM("Equaliser EQ2 Bandwidth", wm8974_enum[5]), +SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]), +SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2, 0, 24, 1, eq_tlv), + +SOC_ENUM("Equaliser EQ3 Bandwidth", wm8974_enum[7]), +SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]), +SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3, 0, 24, 1, eq_tlv), + +SOC_ENUM("Equaliser EQ4 Bandwidth", wm8974_enum[9]), +SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]), +SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4, 0, 24, 1, eq_tlv), + +SOC_ENUM("Equaliser EQ5 Bandwidth", wm8974_enum[11]), +SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]), +SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5, 0, 24, 1, eq_tlv), + +SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0), +SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0), +SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0), + +SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0), +SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0), + +SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0), +SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0), +SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0), + +SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0), +SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0), +SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0), + +SOC_ENUM("ALC Capture Mode", wm8974_enum[13]), +SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0), +SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0), + +SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0), +SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0), + +SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0), +SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0, inpga_tlv), + +SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0), +SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1), +SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0, spk_tlv), + +SOC_ENUM("Aux Mode", wm8974_auxmode), + +SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0), +SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1), + +/* DAC / ADC oversampling */ +SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0), +SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0), +}; + +/* Speaker Output Mixer */ +static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0), +SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0), +SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 0), +}; + +/* Mono Output Mixer */ +static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0), +SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0), +SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0), +}; + +/* Boost mixer */ +static const struct snd_kcontrol_new wm8974_boost_mixer[] = { +SOC_DAPM_SINGLE("PGA Switch", WM8974_INPPGA, 6, 1, 1), +}; + +/* Input PGA */ +static const struct snd_kcontrol_new wm8974_inpga[] = { +SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0), +SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0), +SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0, + &wm8974_speaker_mixer_controls[0], + ARRAY_SIZE(wm8974_speaker_mixer_controls)), +SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0, + &wm8974_mono_mixer_controls[0], + ARRAY_SIZE(wm8974_mono_mixer_controls)), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0), +SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0), +SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga, + ARRAY_SIZE(wm8974_inpga)), +SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, + wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)), + +SND_SOC_DAPM_SUPPLY("Mic Bias", WM8974_POWER1, 4, 0, NULL, 0), + +SND_SOC_DAPM_INPUT("MICN"), +SND_SOC_DAPM_INPUT("MICP"), +SND_SOC_DAPM_INPUT("AUX"), +SND_SOC_DAPM_OUTPUT("MONOOUT"), +SND_SOC_DAPM_OUTPUT("SPKOUTP"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +}; + +static const struct snd_soc_dapm_route wm8974_dapm_routes[] = { + /* Mono output mixer */ + {"Mono Mixer", "PCM Playback Switch", "DAC"}, + {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, + {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"}, + + /* Speaker output mixer */ + {"Speaker Mixer", "PCM Playback Switch", "DAC"}, + {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, + {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, + + /* Outputs */ + {"Mono Out", NULL, "Mono Mixer"}, + {"MONOOUT", NULL, "Mono Out"}, + {"SpkN Out", NULL, "Speaker Mixer"}, + {"SpkP Out", NULL, "Speaker Mixer"}, + {"SPKOUTN", NULL, "SpkN Out"}, + {"SPKOUTP", NULL, "SpkP Out"}, + + /* Boost Mixer */ + {"ADC", NULL, "Boost Mixer"}, + {"Boost Mixer", NULL, "Aux Input"}, + {"Boost Mixer", "PGA Switch", "Input PGA"}, + {"Boost Mixer", NULL, "MICP"}, + + /* Input PGA */ + {"Input PGA", "Aux Switch", "Aux Input"}, + {"Input PGA", "MicN Switch", "MICN"}, + {"Input PGA", "MicP Switch", "MICP"}, + + /* Inputs */ + {"Aux Input", NULL, "AUX"}, +}; + +struct pll_ { + unsigned int pre_div:1; + unsigned int n:4; + unsigned int k; +}; + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 24) * 10) + +static void pll_factors(struct pll_ *pll_div, + unsigned int target, unsigned int source) +{ + unsigned long long Kpart; + unsigned int K, Ndiv, Nmod; + + /* There is a fixed divide by 4 in the output path */ + target *= 4; + + Ndiv = target / source; + if (Ndiv < 6) { + source /= 2; + pll_div->pre_div = 1; + Ndiv = target / source; + } else + pll_div->pre_div = 0; + + if ((Ndiv < 6) || (Ndiv > 12)) + printk(KERN_WARNING + "WM8974 N value %u outwith recommended range!\n", + Ndiv); + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; +} + +static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = codec_dai->component; + struct pll_ pll_div; + u16 reg; + + if (freq_in == 0 || freq_out == 0) { + /* Clock CODEC directly from MCLK */ + reg = snd_soc_component_read(component, WM8974_CLOCK); + snd_soc_component_write(component, WM8974_CLOCK, reg & 0x0ff); + + /* Turn off PLL */ + reg = snd_soc_component_read(component, WM8974_POWER1); + snd_soc_component_write(component, WM8974_POWER1, reg & 0x1df); + return 0; + } + + pll_factors(&pll_div, freq_out, freq_in); + + snd_soc_component_write(component, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n); + snd_soc_component_write(component, WM8974_PLLK1, pll_div.k >> 18); + snd_soc_component_write(component, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff); + snd_soc_component_write(component, WM8974_PLLK3, pll_div.k & 0x1ff); + reg = snd_soc_component_read(component, WM8974_POWER1); + snd_soc_component_write(component, WM8974_POWER1, reg | 0x020); + + /* Run CODEC from PLL instead of MCLK */ + reg = snd_soc_component_read(component, WM8974_CLOCK); + snd_soc_component_write(component, WM8974_CLOCK, reg | 0x100); + + return 0; +} + +/* + * Configure WM8974 clock dividers. + */ +static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + + switch (div_id) { + case WM8974_OPCLKDIV: + reg = snd_soc_component_read(component, WM8974_GPIO) & 0x1cf; + snd_soc_component_write(component, WM8974_GPIO, reg | div); + break; + case WM8974_MCLKDIV: + reg = snd_soc_component_read(component, WM8974_CLOCK) & 0x11f; + snd_soc_component_write(component, WM8974_CLOCK, reg | div); + break; + case WM8974_BCLKDIV: + reg = snd_soc_component_read(component, WM8974_CLOCK) & 0x1e3; + snd_soc_component_write(component, WM8974_CLOCK, reg | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +static unsigned int wm8974_get_mclkdiv(unsigned int f_in, unsigned int f_out, + int *mclkdiv) +{ + unsigned int ratio = 2 * f_in / f_out; + + if (ratio <= 2) { + *mclkdiv = WM8974_MCLKDIV_1; + ratio = 2; + } else if (ratio == 3) { + *mclkdiv = WM8974_MCLKDIV_1_5; + } else if (ratio == 4) { + *mclkdiv = WM8974_MCLKDIV_2; + } else if (ratio <= 6) { + *mclkdiv = WM8974_MCLKDIV_3; + ratio = 6; + } else if (ratio <= 8) { + *mclkdiv = WM8974_MCLKDIV_4; + ratio = 8; + } else if (ratio <= 12) { + *mclkdiv = WM8974_MCLKDIV_6; + ratio = 12; + } else if (ratio <= 16) { + *mclkdiv = WM8974_MCLKDIV_8; + ratio = 16; + } else { + *mclkdiv = WM8974_MCLKDIV_12; + ratio = 24; + } + + return f_out * ratio / 2; +} + +static int wm8974_update_clocks(struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8974_priv *priv = snd_soc_component_get_drvdata(component); + unsigned int fs256; + unsigned int fpll = 0; + unsigned int f; + int mclkdiv; + + if (!priv->mclk || !priv->fs) + return 0; + + fs256 = 256 * priv->fs; + + f = wm8974_get_mclkdiv(priv->mclk, fs256, &mclkdiv); + + if (f != priv->mclk) { + /* The PLL performs best around 90MHz */ + fpll = wm8974_get_mclkdiv(22500000, fs256, &mclkdiv); + } + + wm8974_set_dai_pll(dai, 0, 0, priv->mclk, fpll); + wm8974_set_dai_clkdiv(dai, WM8974_MCLKDIV, mclkdiv); + + return 0; +} + +static int wm8974_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8974_priv *priv = snd_soc_component_get_drvdata(component); + + if (dir != SND_SOC_CLOCK_IN) + return -EINVAL; + + priv->mclk = freq; + + return wm8974_update_clocks(dai); +} + +static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = 0; + u16 clk = snd_soc_component_read(component, WM8974_CLOCK) & 0x1fe; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + clk |= 0x0001; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0010; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0008; + break; + case SND_SOC_DAIFMT_DSP_A: + if ((fmt & SND_SOC_DAIFMT_INV_MASK) == SND_SOC_DAIFMT_IB_IF || + (fmt & SND_SOC_DAIFMT_INV_MASK) == SND_SOC_DAIFMT_NB_IF) { + return -EINVAL; + } + iface |= 0x00018; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0180; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0100; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0080; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8974_IFACE, iface); + snd_soc_component_write(component, WM8974_CLOCK, clk); + return 0; +} + +static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8974_priv *priv = snd_soc_component_get_drvdata(component); + u16 iface = snd_soc_component_read(component, WM8974_IFACE) & 0x19f; + u16 adn = snd_soc_component_read(component, WM8974_ADD) & 0x1f1; + int err; + + priv->fs = params_rate(params); + err = wm8974_update_clocks(dai); + if (err) + return err; + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x0020; + break; + case 24: + iface |= 0x0040; + break; + case 32: + iface |= 0x0060; + break; + } + + /* filter coefficient */ + switch (params_rate(params)) { + case 8000: + adn |= 0x5 << 1; + break; + case 11025: + adn |= 0x4 << 1; + break; + case 16000: + adn |= 0x3 << 1; + break; + case 22050: + adn |= 0x2 << 1; + break; + case 32000: + adn |= 0x1 << 1; + break; + case 44100: + case 48000: + break; + } + + snd_soc_component_write(component, WM8974_IFACE, iface); + snd_soc_component_write(component, WM8974_ADD, adn); + return 0; +} + +static int wm8974_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, WM8974_DAC) & 0xffbf; + + if (mute) + snd_soc_component_write(component, WM8974_DAC, mute_reg | 0x40); + else + snd_soc_component_write(component, WM8974_DAC, mute_reg); + return 0; +} + +/* liam need to make this lower power with dapm */ +static int wm8974_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + u16 power1 = snd_soc_component_read(component, WM8974_POWER1) & ~0x3; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + power1 |= 0x1; /* VMID 50k */ + snd_soc_component_write(component, WM8974_POWER1, power1); + break; + + case SND_SOC_BIAS_STANDBY: + power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + regcache_sync(dev_get_regmap(component->dev, NULL)); + + /* Initial cap charge at VMID 5k */ + snd_soc_component_write(component, WM8974_POWER1, power1 | 0x3); + mdelay(100); + } + + power1 |= 0x2; /* VMID 500k */ + snd_soc_component_write(component, WM8974_POWER1, power1); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, WM8974_POWER1, 0); + snd_soc_component_write(component, WM8974_POWER2, 0); + snd_soc_component_write(component, WM8974_POWER3, 0); + break; + } + + return 0; +} + +#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000) + +#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8974_ops = { + .hw_params = wm8974_pcm_hw_params, + .mute_stream = wm8974_mute, + .set_fmt = wm8974_set_dai_fmt, + .set_clkdiv = wm8974_set_dai_clkdiv, + .set_pll = wm8974_set_dai_pll, + .set_sysclk = wm8974_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8974_dai = { + .name = "wm8974-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, /* Only 1 channel of data */ + .rates = WM8974_RATES, + .formats = WM8974_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, /* Only 1 channel of data */ + .rates = WM8974_RATES, + .formats = WM8974_FORMATS,}, + .ops = &wm8974_ops, + .symmetric_rate = 1, +}; + +static const struct regmap_config wm8974_regmap = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = WM8974_MONOMIX, + .reg_defaults = wm8974_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8974_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +static int wm8974_probe(struct snd_soc_component *component) +{ + int ret = 0; + + ret = wm8974_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset\n"); + return ret; + } + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8974 = { + .probe = wm8974_probe, + .set_bias_level = wm8974_set_bias_level, + .controls = wm8974_snd_controls, + .num_controls = ARRAY_SIZE(wm8974_snd_controls), + .dapm_widgets = wm8974_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8974_dapm_widgets), + .dapm_routes = wm8974_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8974_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm8974_i2c_probe(struct i2c_client *i2c) +{ + struct wm8974_priv *priv; + struct regmap *regmap; + int ret; + + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_clientdata(i2c, priv); + + regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8974, &wm8974_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8974_i2c_id[] = { + { "wm8974", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id); + +static const struct of_device_id wm8974_of_match[] = { + { .compatible = "wlf,wm8974", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8974_of_match); + +static struct i2c_driver wm8974_i2c_driver = { + .driver = { + .name = "wm8974", + .of_match_table = wm8974_of_match, + }, + .probe = wm8974_i2c_probe, + .id_table = wm8974_i2c_id, +}; + +module_i2c_driver(wm8974_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8974 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8974.h b/sound/soc/codecs/wm8974.h new file mode 100644 index 0000000000..d6175383f0 --- /dev/null +++ b/sound/soc/codecs/wm8974.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8974.h -- WM8974 Soc Audio driver + */ + +#ifndef _WM8974_H +#define _WM8974_H + +/* WM8974 register space */ + +#define WM8974_RESET 0x0 +#define WM8974_POWER1 0x1 +#define WM8974_POWER2 0x2 +#define WM8974_POWER3 0x3 +#define WM8974_IFACE 0x4 +#define WM8974_COMP 0x5 +#define WM8974_CLOCK 0x6 +#define WM8974_ADD 0x7 +#define WM8974_GPIO 0x8 +#define WM8974_DAC 0xa +#define WM8974_DACVOL 0xb +#define WM8974_ADC 0xe +#define WM8974_ADCVOL 0xf +#define WM8974_EQ1 0x12 +#define WM8974_EQ2 0x13 +#define WM8974_EQ3 0x14 +#define WM8974_EQ4 0x15 +#define WM8974_EQ5 0x16 +#define WM8974_DACLIM1 0x18 +#define WM8974_DACLIM2 0x19 +#define WM8974_NOTCH1 0x1b +#define WM8974_NOTCH2 0x1c +#define WM8974_NOTCH3 0x1d +#define WM8974_NOTCH4 0x1e +#define WM8974_ALC1 0x20 +#define WM8974_ALC2 0x21 +#define WM8974_ALC3 0x22 +#define WM8974_NGATE 0x23 +#define WM8974_PLLN 0x24 +#define WM8974_PLLK1 0x25 +#define WM8974_PLLK2 0x26 +#define WM8974_PLLK3 0x27 +#define WM8974_ATTEN 0x28 +#define WM8974_INPUT 0x2c +#define WM8974_INPPGA 0x2d +#define WM8974_ADCBOOST 0x2f +#define WM8974_OUTPUT 0x31 +#define WM8974_SPKMIX 0x32 +#define WM8974_SPKVOL 0x36 +#define WM8974_MONOMIX 0x38 + +#define WM8974_CACHEREGNUM 57 + +/* Clock divider Id's */ +#define WM8974_OPCLKDIV 0 +#define WM8974_MCLKDIV 1 +#define WM8974_BCLKDIV 2 + +/* PLL Out dividers */ +#define WM8974_OPCLKDIV_1 (0 << 4) +#define WM8974_OPCLKDIV_2 (1 << 4) +#define WM8974_OPCLKDIV_3 (2 << 4) +#define WM8974_OPCLKDIV_4 (3 << 4) + +/* BCLK clock dividers */ +#define WM8974_BCLKDIV_1 (0 << 2) +#define WM8974_BCLKDIV_2 (1 << 2) +#define WM8974_BCLKDIV_4 (2 << 2) +#define WM8974_BCLKDIV_8 (3 << 2) +#define WM8974_BCLKDIV_16 (4 << 2) +#define WM8974_BCLKDIV_32 (5 << 2) + +/* MCLK clock dividers */ +#define WM8974_MCLKDIV_1 (0 << 5) +#define WM8974_MCLKDIV_1_5 (1 << 5) +#define WM8974_MCLKDIV_2 (2 << 5) +#define WM8974_MCLKDIV_3 (3 << 5) +#define WM8974_MCLKDIV_4 (4 << 5) +#define WM8974_MCLKDIV_6 (5 << 5) +#define WM8974_MCLKDIV_8 (6 << 5) +#define WM8974_MCLKDIV_12 (7 << 5) + +#endif diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c new file mode 100644 index 0000000000..718bfef302 --- /dev/null +++ b/sound/soc/codecs/wm8978.c @@ -0,0 +1,1083 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8978.c -- WM8978 ALSA SoC Audio Codec driver + * + * Copyright (C) 2009-2010 Guennadi Liakhovetski + * Copyright (C) 2007 Carlos Munoz + * Copyright 2006-2009 Wolfson Microelectronics PLC. + * Based on wm8974 and wm8990 by Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8978.h" + +static const struct reg_default wm8978_reg_defaults[] = { + { 1, 0x0000 }, + { 2, 0x0000 }, + { 3, 0x0000 }, + { 4, 0x0050 }, + { 5, 0x0000 }, + { 6, 0x0140 }, + { 7, 0x0000 }, + { 8, 0x0000 }, + { 9, 0x0000 }, + { 10, 0x0000 }, + { 11, 0x00ff }, + { 12, 0x00ff }, + { 13, 0x0000 }, + { 14, 0x0100 }, + { 15, 0x00ff }, + { 16, 0x00ff }, + { 17, 0x0000 }, + { 18, 0x012c }, + { 19, 0x002c }, + { 20, 0x002c }, + { 21, 0x002c }, + { 22, 0x002c }, + { 23, 0x0000 }, + { 24, 0x0032 }, + { 25, 0x0000 }, + { 26, 0x0000 }, + { 27, 0x0000 }, + { 28, 0x0000 }, + { 29, 0x0000 }, + { 30, 0x0000 }, + { 31, 0x0000 }, + { 32, 0x0038 }, + { 33, 0x000b }, + { 34, 0x0032 }, + { 35, 0x0000 }, + { 36, 0x0008 }, + { 37, 0x000c }, + { 38, 0x0093 }, + { 39, 0x00e9 }, + { 40, 0x0000 }, + { 41, 0x0000 }, + { 42, 0x0000 }, + { 43, 0x0000 }, + { 44, 0x0033 }, + { 45, 0x0010 }, + { 46, 0x0010 }, + { 47, 0x0100 }, + { 48, 0x0100 }, + { 49, 0x0002 }, + { 50, 0x0001 }, + { 51, 0x0001 }, + { 52, 0x0039 }, + { 53, 0x0039 }, + { 54, 0x0039 }, + { 55, 0x0039 }, + { 56, 0x0001 }, + { 57, 0x0001 }, +}; + +static bool wm8978_volatile(struct device *dev, unsigned int reg) +{ + return reg == WM8978_RESET; +} + +/* codec private data */ +struct wm8978_priv { + struct regmap *regmap; + unsigned int f_pllout; + unsigned int f_mclk; + unsigned int f_256fs; + unsigned int f_opclk; + int mclk_idx; + enum wm8978_sysclk_src sysclk; +}; + +static const char *wm8978_companding[] = {"Off", "NC", "u-law", "A-law"}; +static const char *wm8978_eqmode[] = {"Capture", "Playback"}; +static const char *wm8978_bw[] = {"Narrow", "Wide"}; +static const char *wm8978_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz"}; +static const char *wm8978_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz"}; +static const char *wm8978_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz"}; +static const char *wm8978_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"}; +static const char *wm8978_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz"}; +static const char *wm8978_alc3[] = {"ALC", "Limiter"}; +static const char *wm8978_alc1[] = {"Off", "Right", "Left", "Both"}; + +static SOC_ENUM_SINGLE_DECL(adc_compand, WM8978_COMPANDING_CONTROL, 1, + wm8978_companding); +static SOC_ENUM_SINGLE_DECL(dac_compand, WM8978_COMPANDING_CONTROL, 3, + wm8978_companding); +static SOC_ENUM_SINGLE_DECL(eqmode, WM8978_EQ1, 8, wm8978_eqmode); +static SOC_ENUM_SINGLE_DECL(eq1, WM8978_EQ1, 5, wm8978_eq1); +static SOC_ENUM_SINGLE_DECL(eq2bw, WM8978_EQ2, 8, wm8978_bw); +static SOC_ENUM_SINGLE_DECL(eq2, WM8978_EQ2, 5, wm8978_eq2); +static SOC_ENUM_SINGLE_DECL(eq3bw, WM8978_EQ3, 8, wm8978_bw); +static SOC_ENUM_SINGLE_DECL(eq3, WM8978_EQ3, 5, wm8978_eq3); +static SOC_ENUM_SINGLE_DECL(eq4bw, WM8978_EQ4, 8, wm8978_bw); +static SOC_ENUM_SINGLE_DECL(eq4, WM8978_EQ4, 5, wm8978_eq4); +static SOC_ENUM_SINGLE_DECL(eq5, WM8978_EQ5, 5, wm8978_eq5); +static SOC_ENUM_SINGLE_DECL(alc3, WM8978_ALC_CONTROL_3, 8, wm8978_alc3); +static SOC_ENUM_SINGLE_DECL(alc1, WM8978_ALC_CONTROL_1, 7, wm8978_alc1); + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1); +static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0); + +static const struct snd_kcontrol_new wm8978_snd_controls[] = { + + SOC_SINGLE("Digital Loopback Switch", + WM8978_COMPANDING_CONTROL, 0, 1, 0), + + SOC_ENUM("ADC Companding", adc_compand), + SOC_ENUM("DAC Companding", dac_compand), + + SOC_DOUBLE("DAC Inversion Switch", WM8978_DAC_CONTROL, 0, 1, 1, 0), + + SOC_DOUBLE_R_TLV("PCM Volume", + WM8978_LEFT_DAC_DIGITAL_VOLUME, WM8978_RIGHT_DAC_DIGITAL_VOLUME, + 0, 255, 0, digital_tlv), + + SOC_SINGLE("High Pass Filter Switch", WM8978_ADC_CONTROL, 8, 1, 0), + SOC_SINGLE("High Pass Cut Off", WM8978_ADC_CONTROL, 4, 7, 0), + SOC_DOUBLE("ADC Inversion Switch", WM8978_ADC_CONTROL, 0, 1, 1, 0), + + SOC_DOUBLE_R_TLV("ADC Volume", + WM8978_LEFT_ADC_DIGITAL_VOLUME, WM8978_RIGHT_ADC_DIGITAL_VOLUME, + 0, 255, 0, digital_tlv), + + SOC_ENUM("Equaliser Function", eqmode), + SOC_ENUM("EQ1 Cut Off", eq1), + SOC_SINGLE_TLV("EQ1 Volume", WM8978_EQ1, 0, 24, 1, eq_tlv), + + SOC_ENUM("Equaliser EQ2 Bandwidth", eq2bw), + SOC_ENUM("EQ2 Cut Off", eq2), + SOC_SINGLE_TLV("EQ2 Volume", WM8978_EQ2, 0, 24, 1, eq_tlv), + + SOC_ENUM("Equaliser EQ3 Bandwidth", eq3bw), + SOC_ENUM("EQ3 Cut Off", eq3), + SOC_SINGLE_TLV("EQ3 Volume", WM8978_EQ3, 0, 24, 1, eq_tlv), + + SOC_ENUM("Equaliser EQ4 Bandwidth", eq4bw), + SOC_ENUM("EQ4 Cut Off", eq4), + SOC_SINGLE_TLV("EQ4 Volume", WM8978_EQ4, 0, 24, 1, eq_tlv), + + SOC_ENUM("EQ5 Cut Off", eq5), + SOC_SINGLE_TLV("EQ5 Volume", WM8978_EQ5, 0, 24, 1, eq_tlv), + + SOC_SINGLE("DAC Playback Limiter Switch", + WM8978_DAC_LIMITER_1, 8, 1, 0), + SOC_SINGLE("DAC Playback Limiter Decay", + WM8978_DAC_LIMITER_1, 4, 15, 0), + SOC_SINGLE("DAC Playback Limiter Attack", + WM8978_DAC_LIMITER_1, 0, 15, 0), + + SOC_SINGLE("DAC Playback Limiter Threshold", + WM8978_DAC_LIMITER_2, 4, 7, 0), + SOC_SINGLE_TLV("DAC Playback Limiter Volume", + WM8978_DAC_LIMITER_2, 0, 12, 0, limiter_tlv), + + SOC_ENUM("ALC Enable Switch", alc1), + SOC_SINGLE("ALC Capture Min Gain", WM8978_ALC_CONTROL_1, 0, 7, 0), + SOC_SINGLE("ALC Capture Max Gain", WM8978_ALC_CONTROL_1, 3, 7, 0), + + SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 10, 0), + SOC_SINGLE("ALC Capture Target", WM8978_ALC_CONTROL_2, 0, 15, 0), + + SOC_ENUM("ALC Capture Mode", alc3), + SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 10, 0), + SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 10, 0), + + SOC_SINGLE("ALC Capture Noise Gate Switch", WM8978_NOISE_GATE, 3, 1, 0), + SOC_SINGLE("ALC Capture Noise Gate Threshold", + WM8978_NOISE_GATE, 0, 7, 0), + + SOC_DOUBLE_R("Capture PGA ZC Switch", + WM8978_LEFT_INP_PGA_CONTROL, WM8978_RIGHT_INP_PGA_CONTROL, + 7, 1, 0), + + /* OUT1 - Headphones */ + SOC_DOUBLE_R("Headphone Playback ZC Switch", + WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL, 7, 1, 0), + + SOC_DOUBLE_R_TLV("Headphone Playback Volume", + WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL, + 0, 63, 0, spk_tlv), + + /* OUT2 - Speakers */ + SOC_DOUBLE_R("Speaker Playback ZC Switch", + WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 7, 1, 0), + + SOC_DOUBLE_R_TLV("Speaker Playback Volume", + WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, + 0, 63, 0, spk_tlv), + + /* OUT3/4 - Line Output */ + SOC_DOUBLE_R("Line Playback Switch", + WM8978_OUT3_MIXER_CONTROL, WM8978_OUT4_MIXER_CONTROL, 6, 1, 1), + + /* Mixer #3: Boost (Input) mixer */ + SOC_DOUBLE_R("PGA Boost (+20dB)", + WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL, + 8, 1, 0), + SOC_DOUBLE_R_TLV("L2/R2 Boost Volume", + WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL, + 4, 7, 0, boost_tlv), + SOC_DOUBLE_R_TLV("Aux Boost Volume", + WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL, + 0, 7, 0, boost_tlv), + + /* Input PGA volume */ + SOC_DOUBLE_R_TLV("Input PGA Volume", + WM8978_LEFT_INP_PGA_CONTROL, WM8978_RIGHT_INP_PGA_CONTROL, + 0, 63, 0, inpga_tlv), + + /* Headphone */ + SOC_DOUBLE_R("Headphone Switch", + WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL, 6, 1, 1), + + /* Speaker */ + SOC_DOUBLE_R("Speaker Switch", + WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 6, 1, 1), + + /* DAC / ADC oversampling */ + SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL, + 5, 1, 0), + SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL, + 5, 1, 0), +}; + +/* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */ +static const struct snd_kcontrol_new wm8978_left_out_mixer[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", WM8978_LEFT_MIXER_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("Aux Playback Switch", WM8978_LEFT_MIXER_CONTROL, 5, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", WM8978_LEFT_MIXER_CONTROL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new wm8978_right_out_mixer[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", WM8978_RIGHT_MIXER_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("Aux Playback Switch", WM8978_RIGHT_MIXER_CONTROL, 5, 1, 0), + SOC_DAPM_SINGLE("PCM Playback Switch", WM8978_RIGHT_MIXER_CONTROL, 0, 1, 0), +}; + +/* OUT3/OUT4 Mixer not implemented */ + +/* Mixer #2: Input PGA Mute */ +static const struct snd_kcontrol_new wm8978_left_input_mixer[] = { + SOC_DAPM_SINGLE("L2 Switch", WM8978_INPUT_CONTROL, 2, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", WM8978_INPUT_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", WM8978_INPUT_CONTROL, 0, 1, 0), +}; +static const struct snd_kcontrol_new wm8978_right_input_mixer[] = { + SOC_DAPM_SINGLE("R2 Switch", WM8978_INPUT_CONTROL, 6, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", WM8978_INPUT_CONTROL, 5, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", WM8978_INPUT_CONTROL, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8978_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", + WM8978_POWER_MANAGEMENT_3, 0, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", + WM8978_POWER_MANAGEMENT_3, 1, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", + WM8978_POWER_MANAGEMENT_2, 0, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", + WM8978_POWER_MANAGEMENT_2, 1, 0), + + /* Mixer #1: OUT1,2 */ + SOC_MIXER_ARRAY("Left Output Mixer", WM8978_POWER_MANAGEMENT_3, + 2, 0, wm8978_left_out_mixer), + SOC_MIXER_ARRAY("Right Output Mixer", WM8978_POWER_MANAGEMENT_3, + 3, 0, wm8978_right_out_mixer), + + SOC_MIXER_ARRAY("Left Input Mixer", WM8978_POWER_MANAGEMENT_2, + 2, 0, wm8978_left_input_mixer), + SOC_MIXER_ARRAY("Right Input Mixer", WM8978_POWER_MANAGEMENT_2, + 3, 0, wm8978_right_input_mixer), + + SND_SOC_DAPM_PGA("Left Boost Mixer", WM8978_POWER_MANAGEMENT_2, + 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Boost Mixer", WM8978_POWER_MANAGEMENT_2, + 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Left Capture PGA", WM8978_LEFT_INP_PGA_CONTROL, + 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right Capture PGA", WM8978_RIGHT_INP_PGA_CONTROL, + 6, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Left Headphone Out", WM8978_POWER_MANAGEMENT_2, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Headphone Out", WM8978_POWER_MANAGEMENT_2, + 8, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Left Speaker Out", WM8978_POWER_MANAGEMENT_3, + 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Speaker Out", WM8978_POWER_MANAGEMENT_3, + 5, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("OUT4 VMID", WM8978_POWER_MANAGEMENT_3, + 8, 0, NULL, 0), + + SND_SOC_DAPM_MICBIAS("Mic Bias", WM8978_POWER_MANAGEMENT_1, 4, 0), + + SND_SOC_DAPM_INPUT("LMICN"), + SND_SOC_DAPM_INPUT("LMICP"), + SND_SOC_DAPM_INPUT("RMICN"), + SND_SOC_DAPM_INPUT("RMICP"), + SND_SOC_DAPM_INPUT("LAUX"), + SND_SOC_DAPM_INPUT("RAUX"), + SND_SOC_DAPM_INPUT("L2"), + SND_SOC_DAPM_INPUT("R2"), + SND_SOC_DAPM_OUTPUT("LHP"), + SND_SOC_DAPM_OUTPUT("RHP"), + SND_SOC_DAPM_OUTPUT("LSPK"), + SND_SOC_DAPM_OUTPUT("RSPK"), +}; + +static const struct snd_soc_dapm_route wm8978_dapm_routes[] = { + /* Output mixer */ + {"Right Output Mixer", "PCM Playback Switch", "Right DAC"}, + {"Right Output Mixer", "Aux Playback Switch", "RAUX"}, + {"Right Output Mixer", "Line Bypass Switch", "Right Boost Mixer"}, + + {"Left Output Mixer", "PCM Playback Switch", "Left DAC"}, + {"Left Output Mixer", "Aux Playback Switch", "LAUX"}, + {"Left Output Mixer", "Line Bypass Switch", "Left Boost Mixer"}, + + /* Outputs */ + {"Right Headphone Out", NULL, "Right Output Mixer"}, + {"RHP", NULL, "Right Headphone Out"}, + + {"Left Headphone Out", NULL, "Left Output Mixer"}, + {"LHP", NULL, "Left Headphone Out"}, + + {"Right Speaker Out", NULL, "Right Output Mixer"}, + {"RSPK", NULL, "Right Speaker Out"}, + + {"Left Speaker Out", NULL, "Left Output Mixer"}, + {"LSPK", NULL, "Left Speaker Out"}, + + /* Boost Mixer */ + {"Right ADC", NULL, "Right Boost Mixer"}, + + {"Right Boost Mixer", NULL, "RAUX"}, + {"Right Boost Mixer", NULL, "Right Capture PGA"}, + {"Right Boost Mixer", NULL, "R2"}, + + {"Left ADC", NULL, "Left Boost Mixer"}, + + {"Left Boost Mixer", NULL, "LAUX"}, + {"Left Boost Mixer", NULL, "Left Capture PGA"}, + {"Left Boost Mixer", NULL, "L2"}, + + /* Input PGA */ + {"Right Capture PGA", NULL, "Right Input Mixer"}, + {"Left Capture PGA", NULL, "Left Input Mixer"}, + + {"Right Input Mixer", "R2 Switch", "R2"}, + {"Right Input Mixer", "MicN Switch", "RMICN"}, + {"Right Input Mixer", "MicP Switch", "RMICP"}, + + {"Left Input Mixer", "L2 Switch", "L2"}, + {"Left Input Mixer", "MicN Switch", "LMICN"}, + {"Left Input Mixer", "MicP Switch", "LMICP"}, +}; + +/* PLL divisors */ +struct wm8978_pll_div { + u32 k; + u8 n; + u8 div2; +}; + +#define FIXED_PLL_SIZE (1 << 24) + +static void pll_factors(struct snd_soc_component *component, + struct wm8978_pll_div *pll_div, unsigned int target, unsigned int source) +{ + u64 k_part; + unsigned int k, n_div, n_mod; + + n_div = target / source; + if (n_div < 6) { + source >>= 1; + pll_div->div2 = 1; + n_div = target / source; + } else { + pll_div->div2 = 0; + } + + if (n_div < 6 || n_div > 12) + dev_warn(component->dev, + "WM8978 N value exceeds recommended range! N = %u\n", + n_div); + + pll_div->n = n_div; + n_mod = target - source * n_div; + k_part = FIXED_PLL_SIZE * (long long)n_mod + source / 2; + + do_div(k_part, source); + + k = k_part & 0xFFFFFFFF; + + pll_div->k = k; +} + +/* MCLK dividers */ +static const int mclk_numerator[] = {1, 3, 2, 3, 4, 6, 8, 12}; +static const int mclk_denominator[] = {1, 2, 1, 1, 1, 1, 1, 1}; + +/* + * find index >= idx, such that, for a given f_out, + * 3 * f_mclk / 4 <= f_PLLOUT < 13 * f_mclk / 4 + * f_out can be f_256fs or f_opclk, currently only used for f_256fs. Can be + * generalised for f_opclk with suitable coefficient arrays, but currently + * the OPCLK divisor is calculated directly, not iteratively. + */ +static int wm8978_enum_mclk(unsigned int f_out, unsigned int f_mclk, + unsigned int *f_pllout) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) { + unsigned int f_pllout_x4 = 4 * f_out * mclk_numerator[i] / + mclk_denominator[i]; + if (3 * f_mclk <= f_pllout_x4 && f_pllout_x4 < 13 * f_mclk) { + *f_pllout = f_pllout_x4 / 4; + return i; + } + } + + return -EINVAL; +} + +/* + * Calculate internal frequencies and dividers, according to Figure 40 + * "PLL and Clock Select Circuit" in WM8978 datasheet Rev. 2.6 + */ +static int wm8978_configure_pll(struct snd_soc_component *component) +{ + struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component); + struct wm8978_pll_div pll_div; + unsigned int f_opclk = wm8978->f_opclk, f_mclk = wm8978->f_mclk, + f_256fs = wm8978->f_256fs; + unsigned int f2; + + if (!f_mclk) + return -EINVAL; + + if (f_opclk) { + unsigned int opclk_div; + /* Cannot set up MCLK divider now, do later */ + wm8978->mclk_idx = -1; + + /* + * The user needs OPCLK. Choose OPCLKDIV to put + * 6 <= R = f2 / f1 < 13, 1 <= OPCLKDIV <= 4. + * f_opclk = f_mclk * prescale * R / 4 / OPCLKDIV, where + * prescale = 1, or prescale = 2. Prescale is calculated inside + * pll_factors(). We have to select f_PLLOUT, such that + * f_mclk * 3 / 4 <= f_PLLOUT < f_mclk * 13 / 4. Must be + * f_mclk * 3 / 16 <= f_opclk < f_mclk * 13 / 4. + */ + if (16 * f_opclk < 3 * f_mclk || 4 * f_opclk >= 13 * f_mclk) + return -EINVAL; + + if (4 * f_opclk < 3 * f_mclk) + /* Have to use OPCLKDIV */ + opclk_div = DIV_ROUND_UP(3 * f_mclk / 4, f_opclk); + else + opclk_div = 1; + + dev_dbg(component->dev, "%s: OPCLKDIV=%d\n", __func__, opclk_div); + + snd_soc_component_update_bits(component, WM8978_GPIO_CONTROL, 0x30, + (opclk_div - 1) << 4); + + wm8978->f_pllout = f_opclk * opclk_div; + } else if (f_256fs) { + /* + * Not using OPCLK, but PLL is used for the codec, choose R: + * 6 <= R = f2 / f1 < 13, to put 1 <= MCLKDIV <= 12. + * f_256fs = f_mclk * prescale * R / 4 / MCLKDIV, where + * prescale = 1, or prescale = 2. Prescale is calculated inside + * pll_factors(). We have to select f_PLLOUT, such that + * f_mclk * 3 / 4 <= f_PLLOUT < f_mclk * 13 / 4. Must be + * f_mclk * 3 / 48 <= f_256fs < f_mclk * 13 / 4. This means MCLK + * must be 3.781MHz <= f_MCLK <= 32.768MHz + */ + int idx = wm8978_enum_mclk(f_256fs, f_mclk, &wm8978->f_pllout); + if (idx < 0) + return idx; + + wm8978->mclk_idx = idx; + } else { + return -EINVAL; + } + + f2 = wm8978->f_pllout * 4; + + dev_dbg(component->dev, "%s: f_MCLK=%uHz, f_PLLOUT=%uHz\n", __func__, + wm8978->f_mclk, wm8978->f_pllout); + + pll_factors(component, &pll_div, f2, wm8978->f_mclk); + + dev_dbg(component->dev, "%s: calculated PLL N=0x%x, K=0x%x, div2=%d\n", + __func__, pll_div.n, pll_div.k, pll_div.div2); + + /* Turn PLL off for configuration... */ + snd_soc_component_update_bits(component, WM8978_POWER_MANAGEMENT_1, 0x20, 0); + + snd_soc_component_write(component, WM8978_PLL_N, (pll_div.div2 << 4) | pll_div.n); + snd_soc_component_write(component, WM8978_PLL_K1, pll_div.k >> 18); + snd_soc_component_write(component, WM8978_PLL_K2, (pll_div.k >> 9) & 0x1ff); + snd_soc_component_write(component, WM8978_PLL_K3, pll_div.k & 0x1ff); + + /* ...and on again */ + snd_soc_component_update_bits(component, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20); + + if (f_opclk) + /* Output PLL (OPCLK) to GPIO1 */ + snd_soc_component_update_bits(component, WM8978_GPIO_CONTROL, 7, 4); + + return 0; +} + +/* + * Configure WM8978 clock dividers. + */ +static int wm8978_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component); + int ret = 0; + + switch (div_id) { + case WM8978_OPCLKRATE: + wm8978->f_opclk = div; + + if (wm8978->f_mclk) + /* + * We know the MCLK frequency, the user has requested + * OPCLK, configure the PLL based on that and start it + * and OPCLK immediately. We will configure PLL to match + * user-requested OPCLK frquency as good as possible. + * In fact, it is likely, that matching the sampling + * rate, when it becomes known, is more important, and + * we will not be reconfiguring PLL then, because we + * must not interrupt OPCLK. But it should be fine, + * because typically the user will request OPCLK to run + * at 256fs or 512fs, and for these cases we will also + * find an exact MCLK divider configuration - it will + * be equal to or double the OPCLK divisor. + */ + ret = wm8978_configure_pll(component); + break; + case WM8978_BCLKDIV: + if (div & ~0x1c) + return -EINVAL; + snd_soc_component_update_bits(component, WM8978_CLOCKING, 0x1c, div); + break; + default: + return -EINVAL; + } + + dev_dbg(component->dev, "%s: ID %d, value %u\n", __func__, div_id, div); + + return ret; +} + +/* + * @freq: when .set_pll() us not used, freq is codec MCLK input frequency + */ +static int wm8978_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component); + int ret = 0; + + dev_dbg(component->dev, "%s: ID %d, freq %u\n", __func__, clk_id, freq); + + if (freq) { + wm8978->f_mclk = freq; + + /* Even if MCLK is used for system clock, might have to drive OPCLK */ + if (wm8978->f_opclk) + ret = wm8978_configure_pll(component); + + /* Our sysclk is fixed to 256 * fs, will configure in .hw_params() */ + + if (!ret) + wm8978->sysclk = clk_id; + } + + if (wm8978->sysclk == WM8978_PLL && (!freq || clk_id == WM8978_MCLK)) { + /* Clock CODEC directly from MCLK */ + snd_soc_component_update_bits(component, WM8978_CLOCKING, 0x100, 0); + + /* GPIO1 into default mode as input - before configuring PLL */ + snd_soc_component_update_bits(component, WM8978_GPIO_CONTROL, 7, 0); + + /* Turn off PLL */ + snd_soc_component_update_bits(component, WM8978_POWER_MANAGEMENT_1, 0x20, 0); + wm8978->sysclk = WM8978_MCLK; + wm8978->f_pllout = 0; + wm8978->f_opclk = 0; + } + + return ret; +} + +/* + * Set ADC and Voice DAC format. + */ +static int wm8978_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + /* + * BCLK polarity mask = 0x100, LRC clock polarity mask = 0x80, + * Data Format mask = 0x18: all will be calculated anew + */ + u16 iface = snd_soc_component_read(component, WM8978_AUDIO_INTERFACE) & ~0x198; + u16 clk = snd_soc_component_read(component, WM8978_CLOCKING); + + dev_dbg(component->dev, "%s\n", __func__); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + clk |= 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + clk &= ~1; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x10; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x8; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x18; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x180; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x100; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x80; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8978_AUDIO_INTERFACE, iface); + snd_soc_component_write(component, WM8978_CLOCKING, clk); + + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + */ +static int wm8978_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component); + /* Word length mask = 0x60 */ + u16 iface_ctl = snd_soc_component_read(component, WM8978_AUDIO_INTERFACE) & ~0x60; + /* Sampling rate mask = 0xe (for filters) */ + u16 add_ctl = snd_soc_component_read(component, WM8978_ADDITIONAL_CONTROL) & ~0xe; + u16 clking = snd_soc_component_read(component, WM8978_CLOCKING); + enum wm8978_sysclk_src current_clk_id = (clking & 0x100) ? + WM8978_PLL : WM8978_MCLK; + unsigned int f_sel, diff, diff_best = INT_MAX; + int i, best = 0; + + if (!wm8978->f_mclk) + return -EINVAL; + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface_ctl |= 0x20; + break; + case 24: + iface_ctl |= 0x40; + break; + case 32: + iface_ctl |= 0x60; + break; + } + + /* filter coefficient */ + switch (params_rate(params)) { + case 8000: + add_ctl |= 0x5 << 1; + break; + case 11025: + add_ctl |= 0x4 << 1; + break; + case 16000: + add_ctl |= 0x3 << 1; + break; + case 22050: + add_ctl |= 0x2 << 1; + break; + case 32000: + add_ctl |= 0x1 << 1; + break; + case 44100: + case 48000: + break; + } + + /* Sampling rate is known now, can configure the MCLK divider */ + wm8978->f_256fs = params_rate(params) * 256; + + if (wm8978->sysclk == WM8978_MCLK) { + wm8978->mclk_idx = -1; + f_sel = wm8978->f_mclk; + } else { + if (!wm8978->f_opclk) { + /* We only enter here, if OPCLK is not used */ + int ret = wm8978_configure_pll(component); + if (ret < 0) + return ret; + } + f_sel = wm8978->f_pllout; + } + + if (wm8978->mclk_idx < 0) { + /* Either MCLK is used directly, or OPCLK is used */ + if (f_sel < wm8978->f_256fs || f_sel > 12 * wm8978->f_256fs) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) { + diff = abs(wm8978->f_256fs * 3 - + f_sel * 3 * mclk_denominator[i] / mclk_numerator[i]); + + if (diff < diff_best) { + diff_best = diff; + best = i; + } + + if (!diff) + break; + } + } else { + /* OPCLK not used, codec driven by PLL */ + best = wm8978->mclk_idx; + diff = 0; + } + + if (diff) + dev_warn(component->dev, "Imprecise sampling rate: %uHz%s\n", + f_sel * mclk_denominator[best] / mclk_numerator[best] / 256, + wm8978->sysclk == WM8978_MCLK ? + ", consider using PLL" : ""); + + dev_dbg(component->dev, "%s: width %d, rate %u, MCLK divisor #%d\n", __func__, + params_width(params), params_rate(params), best); + + /* MCLK divisor mask = 0xe0 */ + snd_soc_component_update_bits(component, WM8978_CLOCKING, 0xe0, best << 5); + + snd_soc_component_write(component, WM8978_AUDIO_INTERFACE, iface_ctl); + snd_soc_component_write(component, WM8978_ADDITIONAL_CONTROL, add_ctl); + + if (wm8978->sysclk != current_clk_id) { + if (wm8978->sysclk == WM8978_PLL) + /* Run CODEC from PLL instead of MCLK */ + snd_soc_component_update_bits(component, WM8978_CLOCKING, + 0x100, 0x100); + else + /* Clock CODEC directly from MCLK */ + snd_soc_component_update_bits(component, WM8978_CLOCKING, 0x100, 0); + } + + return 0; +} + +static int wm8978_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + dev_dbg(component->dev, "%s: %d\n", __func__, mute); + + if (mute) + snd_soc_component_update_bits(component, WM8978_DAC_CONTROL, 0x40, 0x40); + else + snd_soc_component_update_bits(component, WM8978_DAC_CONTROL, 0x40, 0); + + return 0; +} + +static int wm8978_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + u16 power1 = snd_soc_component_read(component, WM8978_POWER_MANAGEMENT_1) & ~3; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + power1 |= 1; /* VMID 75k */ + snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_1, power1); + break; + case SND_SOC_BIAS_STANDBY: + /* bit 3: enable bias, bit 2: enable I/O tie off buffer */ + power1 |= 0xc; + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Initial cap charge at VMID 5k */ + snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_1, + power1 | 0x3); + mdelay(100); + } + + power1 |= 0x2; /* VMID 500k */ + snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_1, power1); + break; + case SND_SOC_BIAS_OFF: + /* Preserve PLL - OPCLK may be used by someone */ + snd_soc_component_update_bits(component, WM8978_POWER_MANAGEMENT_1, ~0x20, 0); + snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_2, 0); + snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_3, 0); + break; + } + + dev_dbg(component->dev, "%s: %d, %x\n", __func__, level, power1); + + return 0; +} + +#define WM8978_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8978_dai_ops = { + .hw_params = wm8978_hw_params, + .mute_stream = wm8978_mute, + .set_fmt = wm8978_set_dai_fmt, + .set_clkdiv = wm8978_set_dai_clkdiv, + .set_sysclk = wm8978_set_dai_sysclk, + .no_capture_mute = 1, +}; + +/* Also supports 12kHz */ +static struct snd_soc_dai_driver wm8978_dai = { + .name = "wm8978-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8978_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8978_FORMATS, + }, + .ops = &wm8978_dai_ops, + .symmetric_rate = 1, +}; + +static int wm8978_suspend(struct snd_soc_component *component) +{ + struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + /* Also switch PLL off */ + snd_soc_component_write(component, WM8978_POWER_MANAGEMENT_1, 0); + + regcache_mark_dirty(wm8978->regmap); + + return 0; +} + +static int wm8978_resume(struct snd_soc_component *component) +{ + struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component); + + /* Sync reg_cache with the hardware */ + regcache_sync(wm8978->regmap); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + if (wm8978->f_pllout) + /* Switch PLL on */ + snd_soc_component_update_bits(component, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20); + + return 0; +} + +/* + * These registers contain an "update" bit - bit 8. This means, for example, + * that one can write new DAC digital volume for both channels, but only when + * the update bit is set, will also the volume be updated - simultaneously for + * both channels. + */ +static const int update_reg[] = { + WM8978_LEFT_DAC_DIGITAL_VOLUME, + WM8978_RIGHT_DAC_DIGITAL_VOLUME, + WM8978_LEFT_ADC_DIGITAL_VOLUME, + WM8978_RIGHT_ADC_DIGITAL_VOLUME, + WM8978_LEFT_INP_PGA_CONTROL, + WM8978_RIGHT_INP_PGA_CONTROL, + WM8978_LOUT1_HP_CONTROL, + WM8978_ROUT1_HP_CONTROL, + WM8978_LOUT2_SPK_CONTROL, + WM8978_ROUT2_SPK_CONTROL, +}; + +static int wm8978_probe(struct snd_soc_component *component) +{ + struct wm8978_priv *wm8978 = snd_soc_component_get_drvdata(component); + int i; + + /* + * Set default system clock to PLL, it is more precise, this is also the + * default hardware setting + */ + wm8978->sysclk = WM8978_PLL; + + /* + * Set the update bit in all registers, that have one. This way all + * writes to those registers will also cause the update bit to be + * written. + */ + for (i = 0; i < ARRAY_SIZE(update_reg); i++) + snd_soc_component_update_bits(component, update_reg[i], 0x100, 0x100); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8978 = { + .probe = wm8978_probe, + .suspend = wm8978_suspend, + .resume = wm8978_resume, + .set_bias_level = wm8978_set_bias_level, + .controls = wm8978_snd_controls, + .num_controls = ARRAY_SIZE(wm8978_snd_controls), + .dapm_widgets = wm8978_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8978_dapm_widgets), + .dapm_routes = wm8978_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8978_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8978_regmap_config = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = WM8978_MAX_REGISTER, + .volatile_reg = wm8978_volatile, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm8978_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8978_reg_defaults), +}; + +static int wm8978_i2c_probe(struct i2c_client *i2c) +{ + struct wm8978_priv *wm8978; + int ret; + + wm8978 = devm_kzalloc(&i2c->dev, sizeof(struct wm8978_priv), + GFP_KERNEL); + if (wm8978 == NULL) + return -ENOMEM; + + wm8978->regmap = devm_regmap_init_i2c(i2c, &wm8978_regmap_config); + if (IS_ERR(wm8978->regmap)) { + ret = PTR_ERR(wm8978->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, wm8978); + + /* Reset the codec */ + ret = regmap_write(wm8978->regmap, WM8978_RESET, 0); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8978, &wm8978_dai, 1); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id wm8978_i2c_id[] = { + { "wm8978", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id); + +static const struct of_device_id wm8978_of_match[] = { + { .compatible = "wlf,wm8978", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8978_of_match); + +static struct i2c_driver wm8978_i2c_driver = { + .driver = { + .name = "wm8978", + .of_match_table = wm8978_of_match, + }, + .probe = wm8978_i2c_probe, + .id_table = wm8978_i2c_id, +}; + +module_i2c_driver(wm8978_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8978 codec driver"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8978.h b/sound/soc/codecs/wm8978.h new file mode 100644 index 0000000000..e1986dcec4 --- /dev/null +++ b/sound/soc/codecs/wm8978.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8978.h -- codec driver for WM8978 + * + * Copyright 2009 Guennadi Liakhovetski + */ + +#ifndef __WM8978_H__ +#define __WM8978_H__ + +/* + * Register values. + */ +#define WM8978_RESET 0x00 +#define WM8978_POWER_MANAGEMENT_1 0x01 +#define WM8978_POWER_MANAGEMENT_2 0x02 +#define WM8978_POWER_MANAGEMENT_3 0x03 +#define WM8978_AUDIO_INTERFACE 0x04 +#define WM8978_COMPANDING_CONTROL 0x05 +#define WM8978_CLOCKING 0x06 +#define WM8978_ADDITIONAL_CONTROL 0x07 +#define WM8978_GPIO_CONTROL 0x08 +#define WM8978_JACK_DETECT_CONTROL_1 0x09 +#define WM8978_DAC_CONTROL 0x0A +#define WM8978_LEFT_DAC_DIGITAL_VOLUME 0x0B +#define WM8978_RIGHT_DAC_DIGITAL_VOLUME 0x0C +#define WM8978_JACK_DETECT_CONTROL_2 0x0D +#define WM8978_ADC_CONTROL 0x0E +#define WM8978_LEFT_ADC_DIGITAL_VOLUME 0x0F +#define WM8978_RIGHT_ADC_DIGITAL_VOLUME 0x10 +#define WM8978_EQ1 0x12 +#define WM8978_EQ2 0x13 +#define WM8978_EQ3 0x14 +#define WM8978_EQ4 0x15 +#define WM8978_EQ5 0x16 +#define WM8978_DAC_LIMITER_1 0x18 +#define WM8978_DAC_LIMITER_2 0x19 +#define WM8978_NOTCH_FILTER_1 0x1b +#define WM8978_NOTCH_FILTER_2 0x1c +#define WM8978_NOTCH_FILTER_3 0x1d +#define WM8978_NOTCH_FILTER_4 0x1e +#define WM8978_ALC_CONTROL_1 0x20 +#define WM8978_ALC_CONTROL_2 0x21 +#define WM8978_ALC_CONTROL_3 0x22 +#define WM8978_NOISE_GATE 0x23 +#define WM8978_PLL_N 0x24 +#define WM8978_PLL_K1 0x25 +#define WM8978_PLL_K2 0x26 +#define WM8978_PLL_K3 0x27 +#define WM8978_3D_CONTROL 0x29 +#define WM8978_BEEP_CONTROL 0x2b +#define WM8978_INPUT_CONTROL 0x2c +#define WM8978_LEFT_INP_PGA_CONTROL 0x2d +#define WM8978_RIGHT_INP_PGA_CONTROL 0x2e +#define WM8978_LEFT_ADC_BOOST_CONTROL 0x2f +#define WM8978_RIGHT_ADC_BOOST_CONTROL 0x30 +#define WM8978_OUTPUT_CONTROL 0x31 +#define WM8978_LEFT_MIXER_CONTROL 0x32 +#define WM8978_RIGHT_MIXER_CONTROL 0x33 +#define WM8978_LOUT1_HP_CONTROL 0x34 +#define WM8978_ROUT1_HP_CONTROL 0x35 +#define WM8978_LOUT2_SPK_CONTROL 0x36 +#define WM8978_ROUT2_SPK_CONTROL 0x37 +#define WM8978_OUT3_MIXER_CONTROL 0x38 +#define WM8978_OUT4_MIXER_CONTROL 0x39 + +#define WM8978_MAX_REGISTER 0x39 + +#define WM8978_CACHEREGNUM 58 + +/* Clock divider Id's */ +enum wm8978_clk_id { + WM8978_OPCLKRATE, + WM8978_BCLKDIV, +}; + +enum wm8978_sysclk_src { + WM8978_MCLK = 0, + WM8978_PLL, +}; + +#endif /* __WM8978_H__ */ diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c new file mode 100644 index 0000000000..b26d6a68e8 --- /dev/null +++ b/sound/soc/codecs/wm8983.c @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8983.c -- WM8983 ALSA SoC Audio driver + * + * Copyright 2011 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8983.h" + +static const struct reg_default wm8983_defaults[] = { + { 0x01, 0x0000 }, /* R1 - Power management 1 */ + { 0x02, 0x0000 }, /* R2 - Power management 2 */ + { 0x03, 0x0000 }, /* R3 - Power management 3 */ + { 0x04, 0x0050 }, /* R4 - Audio Interface */ + { 0x05, 0x0000 }, /* R5 - Companding control */ + { 0x06, 0x0140 }, /* R6 - Clock Gen control */ + { 0x07, 0x0000 }, /* R7 - Additional control */ + { 0x08, 0x0000 }, /* R8 - GPIO Control */ + { 0x09, 0x0000 }, /* R9 - Jack Detect Control 1 */ + { 0x0A, 0x0000 }, /* R10 - DAC Control */ + { 0x0B, 0x00FF }, /* R11 - Left DAC digital Vol */ + { 0x0C, 0x00FF }, /* R12 - Right DAC digital vol */ + { 0x0D, 0x0000 }, /* R13 - Jack Detect Control 2 */ + { 0x0E, 0x0100 }, /* R14 - ADC Control */ + { 0x0F, 0x00FF }, /* R15 - Left ADC Digital Vol */ + { 0x10, 0x00FF }, /* R16 - Right ADC Digital Vol */ + { 0x12, 0x012C }, /* R18 - EQ1 - low shelf */ + { 0x13, 0x002C }, /* R19 - EQ2 - peak 1 */ + { 0x14, 0x002C }, /* R20 - EQ3 - peak 2 */ + { 0x15, 0x002C }, /* R21 - EQ4 - peak 3 */ + { 0x16, 0x002C }, /* R22 - EQ5 - high shelf */ + { 0x18, 0x0032 }, /* R24 - DAC Limiter 1 */ + { 0x19, 0x0000 }, /* R25 - DAC Limiter 2 */ + { 0x1B, 0x0000 }, /* R27 - Notch Filter 1 */ + { 0x1C, 0x0000 }, /* R28 - Notch Filter 2 */ + { 0x1D, 0x0000 }, /* R29 - Notch Filter 3 */ + { 0x1E, 0x0000 }, /* R30 - Notch Filter 4 */ + { 0x20, 0x0038 }, /* R32 - ALC control 1 */ + { 0x21, 0x000B }, /* R33 - ALC control 2 */ + { 0x22, 0x0032 }, /* R34 - ALC control 3 */ + { 0x23, 0x0000 }, /* R35 - Noise Gate */ + { 0x24, 0x0008 }, /* R36 - PLL N */ + { 0x25, 0x000C }, /* R37 - PLL K 1 */ + { 0x26, 0x0093 }, /* R38 - PLL K 2 */ + { 0x27, 0x00E9 }, /* R39 - PLL K 3 */ + { 0x29, 0x0000 }, /* R41 - 3D control */ + { 0x2A, 0x0000 }, /* R42 - OUT4 to ADC */ + { 0x2B, 0x0000 }, /* R43 - Beep control */ + { 0x2C, 0x0033 }, /* R44 - Input ctrl */ + { 0x2D, 0x0010 }, /* R45 - Left INP PGA gain ctrl */ + { 0x2E, 0x0010 }, /* R46 - Right INP PGA gain ctrl */ + { 0x2F, 0x0100 }, /* R47 - Left ADC BOOST ctrl */ + { 0x30, 0x0100 }, /* R48 - Right ADC BOOST ctrl */ + { 0x31, 0x0002 }, /* R49 - Output ctrl */ + { 0x32, 0x0001 }, /* R50 - Left mixer ctrl */ + { 0x33, 0x0001 }, /* R51 - Right mixer ctrl */ + { 0x34, 0x0039 }, /* R52 - LOUT1 (HP) volume ctrl */ + { 0x35, 0x0039 }, /* R53 - ROUT1 (HP) volume ctrl */ + { 0x36, 0x0039 }, /* R54 - LOUT2 (SPK) volume ctrl */ + { 0x37, 0x0039 }, /* R55 - ROUT2 (SPK) volume ctrl */ + { 0x38, 0x0001 }, /* R56 - OUT3 mixer ctrl */ + { 0x39, 0x0001 }, /* R57 - OUT4 (MONO) mix ctrl */ + { 0x3D, 0x0000 }, /* R61 - BIAS CTRL */ +}; + +/* vol/gain update regs */ +static const int vol_update_regs[] = { + WM8983_LEFT_DAC_DIGITAL_VOL, + WM8983_RIGHT_DAC_DIGITAL_VOL, + WM8983_LEFT_ADC_DIGITAL_VOL, + WM8983_RIGHT_ADC_DIGITAL_VOL, + WM8983_LOUT1_HP_VOLUME_CTRL, + WM8983_ROUT1_HP_VOLUME_CTRL, + WM8983_LOUT2_SPK_VOLUME_CTRL, + WM8983_ROUT2_SPK_VOLUME_CTRL, + WM8983_LEFT_INP_PGA_GAIN_CTRL, + WM8983_RIGHT_INP_PGA_GAIN_CTRL +}; + +struct wm8983_priv { + struct regmap *regmap; + u32 sysclk; + u32 bclk; +}; + +static const struct { + int div; + int ratio; +} fs_ratios[] = { + { 10, 128 }, + { 15, 192 }, + { 20, 256 }, + { 30, 384 }, + { 40, 512 }, + { 60, 768 }, + { 80, 1024 }, + { 120, 1536 } +}; + +static const int srates[] = { 48000, 32000, 24000, 16000, 12000, 8000 }; + +static const int bclk_divs[] = { + 1, 2, 4, 8, 16, 32 +}; + +static int eqmode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int eqmode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -12700, 50, 1); +static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(lim_thresh_tlv, -600, 100, 0); +static const DECLARE_TLV_DB_SCALE(lim_boost_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(alc_min_tlv, -1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(alc_max_tlv, -675, 600, 0); +static const DECLARE_TLV_DB_SCALE(alc_tar_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(aux_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0); + +static const char *alc_sel_text[] = { "Off", "Right", "Left", "Stereo" }; +static SOC_ENUM_SINGLE_DECL(alc_sel, WM8983_ALC_CONTROL_1, 7, alc_sel_text); + +static const char *alc_mode_text[] = { "ALC", "Limiter" }; +static SOC_ENUM_SINGLE_DECL(alc_mode, WM8983_ALC_CONTROL_3, 8, alc_mode_text); + +static const char *filter_mode_text[] = { "Audio", "Application" }; +static SOC_ENUM_SINGLE_DECL(filter_mode, WM8983_ADC_CONTROL, 7, + filter_mode_text); + +static const char *eq_bw_text[] = { "Narrow", "Wide" }; +static const char *eqmode_text[] = { "Capture", "Playback" }; +static SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text); + +static const char *eq1_cutoff_text[] = { + "80Hz", "105Hz", "135Hz", "175Hz" +}; +static SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8983_EQ1_LOW_SHELF, 5, + eq1_cutoff_text); +static const char *eq2_cutoff_text[] = { + "230Hz", "300Hz", "385Hz", "500Hz" +}; +static SOC_ENUM_SINGLE_DECL(eq2_bw, WM8983_EQ2_PEAK_1, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8983_EQ2_PEAK_1, 5, eq2_cutoff_text); +static const char *eq3_cutoff_text[] = { + "650Hz", "850Hz", "1.1kHz", "1.4kHz" +}; +static SOC_ENUM_SINGLE_DECL(eq3_bw, WM8983_EQ3_PEAK_2, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8983_EQ3_PEAK_2, 5, eq3_cutoff_text); +static const char *eq4_cutoff_text[] = { + "1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" +}; +static SOC_ENUM_SINGLE_DECL(eq4_bw, WM8983_EQ4_PEAK_3, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8983_EQ4_PEAK_3, 5, eq4_cutoff_text); +static const char *eq5_cutoff_text[] = { + "5.3kHz", "6.9kHz", "9kHz", "11.7kHz" +}; +static SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8983_EQ5_HIGH_SHELF, 5, + eq5_cutoff_text); + +static const char *depth_3d_text[] = { + "Off", + "6.67%", + "13.3%", + "20%", + "26.7%", + "33.3%", + "40%", + "46.6%", + "53.3%", + "60%", + "66.7%", + "73.3%", + "80%", + "86.7%", + "93.3%", + "100%" +}; +static SOC_ENUM_SINGLE_DECL(depth_3d, WM8983_3D_CONTROL, 0, + depth_3d_text); + +static const struct snd_kcontrol_new wm8983_snd_controls[] = { + SOC_SINGLE("Digital Loopback Switch", WM8983_COMPANDING_CONTROL, + 0, 1, 0), + + SOC_ENUM("ALC Capture Function", alc_sel), + SOC_SINGLE_TLV("ALC Capture Max Volume", WM8983_ALC_CONTROL_1, + 3, 7, 0, alc_max_tlv), + SOC_SINGLE_TLV("ALC Capture Min Volume", WM8983_ALC_CONTROL_1, + 0, 7, 0, alc_min_tlv), + SOC_SINGLE_TLV("ALC Capture Target Volume", WM8983_ALC_CONTROL_2, + 0, 15, 0, alc_tar_tlv), + SOC_SINGLE("ALC Capture Attack", WM8983_ALC_CONTROL_3, 0, 10, 0), + SOC_SINGLE("ALC Capture Hold", WM8983_ALC_CONTROL_2, 4, 10, 0), + SOC_SINGLE("ALC Capture Decay", WM8983_ALC_CONTROL_3, 4, 10, 0), + SOC_ENUM("ALC Mode", alc_mode), + SOC_SINGLE("ALC Capture NG Switch", WM8983_NOISE_GATE, + 3, 1, 0), + SOC_SINGLE("ALC Capture NG Threshold", WM8983_NOISE_GATE, + 0, 7, 1), + + SOC_DOUBLE_R_TLV("Capture Volume", WM8983_LEFT_ADC_DIGITAL_VOL, + WM8983_RIGHT_ADC_DIGITAL_VOL, 0, 255, 0, adc_tlv), + SOC_DOUBLE_R("Capture PGA ZC Switch", WM8983_LEFT_INP_PGA_GAIN_CTRL, + WM8983_RIGHT_INP_PGA_GAIN_CTRL, 7, 1, 0), + SOC_DOUBLE_R_TLV("Capture PGA Volume", WM8983_LEFT_INP_PGA_GAIN_CTRL, + WM8983_RIGHT_INP_PGA_GAIN_CTRL, 0, 63, 0, pga_vol_tlv), + + SOC_DOUBLE_R_TLV("Capture PGA Boost Volume", + WM8983_LEFT_ADC_BOOST_CTRL, WM8983_RIGHT_ADC_BOOST_CTRL, + 8, 1, 0, pga_boost_tlv), + + SOC_DOUBLE("ADC Inversion Switch", WM8983_ADC_CONTROL, 0, 1, 1, 0), + SOC_SINGLE("ADC 128x Oversampling Switch", WM8983_ADC_CONTROL, 8, 1, 0), + + SOC_DOUBLE_R_TLV("Playback Volume", WM8983_LEFT_DAC_DIGITAL_VOL, + WM8983_RIGHT_DAC_DIGITAL_VOL, 0, 255, 0, dac_tlv), + + SOC_SINGLE("DAC Playback Limiter Switch", WM8983_DAC_LIMITER_1, 8, 1, 0), + SOC_SINGLE("DAC Playback Limiter Decay", WM8983_DAC_LIMITER_1, 4, 10, 0), + SOC_SINGLE("DAC Playback Limiter Attack", WM8983_DAC_LIMITER_1, 0, 11, 0), + SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8983_DAC_LIMITER_2, + 4, 7, 1, lim_thresh_tlv), + SOC_SINGLE_TLV("DAC Playback Limiter Boost Volume", WM8983_DAC_LIMITER_2, + 0, 12, 0, lim_boost_tlv), + SOC_DOUBLE("DAC Inversion Switch", WM8983_DAC_CONTROL, 0, 1, 1, 0), + SOC_SINGLE("DAC Auto Mute Switch", WM8983_DAC_CONTROL, 2, 1, 0), + SOC_SINGLE("DAC 128x Oversampling Switch", WM8983_DAC_CONTROL, 3, 1, 0), + + SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8983_LOUT1_HP_VOLUME_CTRL, + WM8983_ROUT1_HP_VOLUME_CTRL, 0, 63, 0, out_tlv), + SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8983_LOUT1_HP_VOLUME_CTRL, + WM8983_ROUT1_HP_VOLUME_CTRL, 7, 1, 0), + SOC_DOUBLE_R("Headphone Switch", WM8983_LOUT1_HP_VOLUME_CTRL, + WM8983_ROUT1_HP_VOLUME_CTRL, 6, 1, 1), + + SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8983_LOUT2_SPK_VOLUME_CTRL, + WM8983_ROUT2_SPK_VOLUME_CTRL, 0, 63, 0, out_tlv), + SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8983_LOUT2_SPK_VOLUME_CTRL, + WM8983_ROUT2_SPK_VOLUME_CTRL, 7, 1, 0), + SOC_DOUBLE_R("Speaker Switch", WM8983_LOUT2_SPK_VOLUME_CTRL, + WM8983_ROUT2_SPK_VOLUME_CTRL, 6, 1, 1), + + SOC_SINGLE("OUT3 Switch", WM8983_OUT3_MIXER_CTRL, + 6, 1, 1), + + SOC_SINGLE("OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL, + 6, 1, 1), + + SOC_SINGLE("High Pass Filter Switch", WM8983_ADC_CONTROL, 8, 1, 0), + SOC_ENUM("High Pass Filter Mode", filter_mode), + SOC_SINGLE("High Pass Filter Cutoff", WM8983_ADC_CONTROL, 4, 7, 0), + + SOC_DOUBLE_R_TLV("Aux Bypass Volume", + WM8983_LEFT_MIXER_CTRL, WM8983_RIGHT_MIXER_CTRL, 6, 7, 0, + aux_tlv), + + SOC_DOUBLE_R_TLV("Input PGA Bypass Volume", + WM8983_LEFT_MIXER_CTRL, WM8983_RIGHT_MIXER_CTRL, 2, 7, 0, + bypass_tlv), + + SOC_ENUM_EXT("Equalizer Function", eqmode, eqmode_get, eqmode_put), + SOC_ENUM("EQ1 Cutoff", eq1_cutoff), + SOC_SINGLE_TLV("EQ1 Volume", WM8983_EQ1_LOW_SHELF, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ2 Bandwidth", eq2_bw), + SOC_ENUM("EQ2 Cutoff", eq2_cutoff), + SOC_SINGLE_TLV("EQ2 Volume", WM8983_EQ2_PEAK_1, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ3 Bandwidth", eq3_bw), + SOC_ENUM("EQ3 Cutoff", eq3_cutoff), + SOC_SINGLE_TLV("EQ3 Volume", WM8983_EQ3_PEAK_2, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ4 Bandwidth", eq4_bw), + SOC_ENUM("EQ4 Cutoff", eq4_cutoff), + SOC_SINGLE_TLV("EQ4 Volume", WM8983_EQ4_PEAK_3, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ5 Cutoff", eq5_cutoff), + SOC_SINGLE_TLV("EQ5 Volume", WM8983_EQ5_HIGH_SHELF, 0, 24, 1, eq_tlv), + + SOC_ENUM("3D Depth", depth_3d), +}; + +static const struct snd_kcontrol_new left_out_mixer[] = { + SOC_DAPM_SINGLE("Line Switch", WM8983_LEFT_MIXER_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("Aux Switch", WM8983_LEFT_MIXER_CTRL, 5, 1, 0), + SOC_DAPM_SINGLE("PCM Switch", WM8983_LEFT_MIXER_CTRL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_out_mixer[] = { + SOC_DAPM_SINGLE("Line Switch", WM8983_RIGHT_MIXER_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("Aux Switch", WM8983_RIGHT_MIXER_CTRL, 5, 1, 0), + SOC_DAPM_SINGLE("PCM Switch", WM8983_RIGHT_MIXER_CTRL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new left_input_mixer[] = { + SOC_DAPM_SINGLE("L2 Switch", WM8983_INPUT_CTRL, 2, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", WM8983_INPUT_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", WM8983_INPUT_CTRL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_input_mixer[] = { + SOC_DAPM_SINGLE("R2 Switch", WM8983_INPUT_CTRL, 6, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", WM8983_INPUT_CTRL, 5, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", WM8983_INPUT_CTRL, 4, 1, 0), +}; + +static const struct snd_kcontrol_new left_boost_mixer[] = { + SOC_DAPM_SINGLE_TLV("L2 Volume", WM8983_LEFT_ADC_BOOST_CTRL, + 4, 7, 0, boost_tlv), + SOC_DAPM_SINGLE_TLV("AUXL Volume", WM8983_LEFT_ADC_BOOST_CTRL, + 0, 7, 0, boost_tlv) +}; + +static const struct snd_kcontrol_new out3_mixer[] = { + SOC_DAPM_SINGLE("LMIX2OUT3 Switch", WM8983_OUT3_MIXER_CTRL, + 1, 1, 0), + SOC_DAPM_SINGLE("LDAC2OUT3 Switch", WM8983_OUT3_MIXER_CTRL, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new out4_mixer[] = { + SOC_DAPM_SINGLE("LMIX2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL, + 4, 1, 0), + SOC_DAPM_SINGLE("RMIX2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL, + 1, 1, 0), + SOC_DAPM_SINGLE("LDAC2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL, + 3, 1, 0), + SOC_DAPM_SINGLE("RDAC2OUT4 Switch", WM8983_OUT4_MONO_MIX_CTRL, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_boost_mixer[] = { + SOC_DAPM_SINGLE_TLV("R2 Volume", WM8983_RIGHT_ADC_BOOST_CTRL, + 4, 7, 0, boost_tlv), + SOC_DAPM_SINGLE_TLV("AUXR Volume", WM8983_RIGHT_ADC_BOOST_CTRL, + 0, 7, 0, boost_tlv) +}; + +static const struct snd_soc_dapm_widget wm8983_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8983_POWER_MANAGEMENT_3, + 0, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8983_POWER_MANAGEMENT_3, + 1, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8983_POWER_MANAGEMENT_2, + 0, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8983_POWER_MANAGEMENT_2, + 1, 0), + + SND_SOC_DAPM_MIXER("Left Output Mixer", WM8983_POWER_MANAGEMENT_3, + 2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)), + SND_SOC_DAPM_MIXER("Right Output Mixer", WM8983_POWER_MANAGEMENT_3, + 3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)), + + SND_SOC_DAPM_MIXER("Left Input Mixer", WM8983_POWER_MANAGEMENT_2, + 2, 0, left_input_mixer, ARRAY_SIZE(left_input_mixer)), + SND_SOC_DAPM_MIXER("Right Input Mixer", WM8983_POWER_MANAGEMENT_2, + 3, 0, right_input_mixer, ARRAY_SIZE(right_input_mixer)), + + SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8983_POWER_MANAGEMENT_2, + 4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)), + SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8983_POWER_MANAGEMENT_2, + 5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)), + + SND_SOC_DAPM_MIXER("OUT3 Mixer", WM8983_POWER_MANAGEMENT_1, + 6, 0, out3_mixer, ARRAY_SIZE(out3_mixer)), + + SND_SOC_DAPM_MIXER("OUT4 Mixer", WM8983_POWER_MANAGEMENT_1, + 7, 0, out4_mixer, ARRAY_SIZE(out4_mixer)), + + SND_SOC_DAPM_PGA("Left Capture PGA", WM8983_LEFT_INP_PGA_GAIN_CTRL, + 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right Capture PGA", WM8983_RIGHT_INP_PGA_GAIN_CTRL, + 6, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Left Headphone Out", WM8983_POWER_MANAGEMENT_2, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Headphone Out", WM8983_POWER_MANAGEMENT_2, + 8, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Left Speaker Out", WM8983_POWER_MANAGEMENT_3, + 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Speaker Out", WM8983_POWER_MANAGEMENT_3, + 6, 0, NULL, 0), + + SND_SOC_DAPM_PGA("OUT3 Out", WM8983_POWER_MANAGEMENT_3, + 7, 0, NULL, 0), + + SND_SOC_DAPM_PGA("OUT4 Out", WM8983_POWER_MANAGEMENT_3, + 8, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", WM8983_POWER_MANAGEMENT_1, 4, 0, + NULL, 0), + + SND_SOC_DAPM_INPUT("LIN"), + SND_SOC_DAPM_INPUT("LIP"), + SND_SOC_DAPM_INPUT("RIN"), + SND_SOC_DAPM_INPUT("RIP"), + SND_SOC_DAPM_INPUT("AUXL"), + SND_SOC_DAPM_INPUT("AUXR"), + SND_SOC_DAPM_INPUT("L2"), + SND_SOC_DAPM_INPUT("R2"), + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_OUTPUT("OUT4") +}; + +static const struct snd_soc_dapm_route wm8983_audio_map[] = { + { "OUT3 Mixer", "LMIX2OUT3 Switch", "Left Output Mixer" }, + { "OUT3 Mixer", "LDAC2OUT3 Switch", "Left DAC" }, + + { "OUT3 Out", NULL, "OUT3 Mixer" }, + { "OUT3", NULL, "OUT3 Out" }, + + { "OUT4 Mixer", "LMIX2OUT4 Switch", "Left Output Mixer" }, + { "OUT4 Mixer", "RMIX2OUT4 Switch", "Right Output Mixer" }, + { "OUT4 Mixer", "LDAC2OUT4 Switch", "Left DAC" }, + { "OUT4 Mixer", "RDAC2OUT4 Switch", "Right DAC" }, + + { "OUT4 Out", NULL, "OUT4 Mixer" }, + { "OUT4", NULL, "OUT4 Out" }, + + { "Right Output Mixer", "PCM Switch", "Right DAC" }, + { "Right Output Mixer", "Aux Switch", "AUXR" }, + { "Right Output Mixer", "Line Switch", "Right Boost Mixer" }, + + { "Left Output Mixer", "PCM Switch", "Left DAC" }, + { "Left Output Mixer", "Aux Switch", "AUXL" }, + { "Left Output Mixer", "Line Switch", "Left Boost Mixer" }, + + { "Right Headphone Out", NULL, "Right Output Mixer" }, + { "HPR", NULL, "Right Headphone Out" }, + + { "Left Headphone Out", NULL, "Left Output Mixer" }, + { "HPL", NULL, "Left Headphone Out" }, + + { "Right Speaker Out", NULL, "Right Output Mixer" }, + { "SPKR", NULL, "Right Speaker Out" }, + + { "Left Speaker Out", NULL, "Left Output Mixer" }, + { "SPKL", NULL, "Left Speaker Out" }, + + { "Right ADC", NULL, "Right Boost Mixer" }, + + { "Right Boost Mixer", "AUXR Volume", "AUXR" }, + { "Right Boost Mixer", NULL, "Right Capture PGA" }, + { "Right Boost Mixer", "R2 Volume", "R2" }, + + { "Left ADC", NULL, "Left Boost Mixer" }, + + { "Left Boost Mixer", "AUXL Volume", "AUXL" }, + { "Left Boost Mixer", NULL, "Left Capture PGA" }, + { "Left Boost Mixer", "L2 Volume", "L2" }, + + { "Right Capture PGA", NULL, "Right Input Mixer" }, + { "Left Capture PGA", NULL, "Left Input Mixer" }, + + { "Right Input Mixer", "R2 Switch", "R2" }, + { "Right Input Mixer", "MicN Switch", "RIN" }, + { "Right Input Mixer", "MicP Switch", "RIP" }, + + { "Left Input Mixer", "L2 Switch", "L2" }, + { "Left Input Mixer", "MicN Switch", "LIN" }, + { "Left Input Mixer", "MicP Switch", "LIP" }, +}; + +static int eqmode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg; + + reg = snd_soc_component_read(component, WM8983_EQ1_LOW_SHELF); + if (reg & WM8983_EQ3DMODE) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + return 0; +} + +static int eqmode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int regpwr2, regpwr3; + unsigned int reg_eq; + + if (ucontrol->value.enumerated.item[0] != 0 + && ucontrol->value.enumerated.item[0] != 1) + return -EINVAL; + + reg_eq = snd_soc_component_read(component, WM8983_EQ1_LOW_SHELF); + switch ((reg_eq & WM8983_EQ3DMODE) >> WM8983_EQ3DMODE_SHIFT) { + case 0: + if (!ucontrol->value.enumerated.item[0]) + return 0; + break; + case 1: + if (ucontrol->value.enumerated.item[0]) + return 0; + break; + } + + regpwr2 = snd_soc_component_read(component, WM8983_POWER_MANAGEMENT_2); + regpwr3 = snd_soc_component_read(component, WM8983_POWER_MANAGEMENT_3); + /* disable the DACs and ADCs */ + snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_2, + WM8983_ADCENR_MASK | WM8983_ADCENL_MASK, 0); + snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_3, + WM8983_DACENR_MASK | WM8983_DACENL_MASK, 0); + /* set the desired eqmode */ + snd_soc_component_update_bits(component, WM8983_EQ1_LOW_SHELF, + WM8983_EQ3DMODE_MASK, + ucontrol->value.enumerated.item[0] + << WM8983_EQ3DMODE_SHIFT); + /* restore DAC/ADC configuration */ + snd_soc_component_write(component, WM8983_POWER_MANAGEMENT_2, regpwr2); + snd_soc_component_write(component, WM8983_POWER_MANAGEMENT_3, regpwr3); + return 0; +} + +static bool wm8983_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8983_SOFTWARE_RESET ... WM8983_RIGHT_ADC_DIGITAL_VOL: + case WM8983_EQ1_LOW_SHELF ... WM8983_DAC_LIMITER_2: + case WM8983_NOTCH_FILTER_1 ... WM8983_NOTCH_FILTER_4: + case WM8983_ALC_CONTROL_1 ... WM8983_PLL_K_3: + case WM8983_3D_CONTROL ... WM8983_OUT4_MONO_MIX_CTRL: + case WM8983_BIAS_CTRL: + return true; + default: + return false; + } +} + +static int wm8983_dac_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + return snd_soc_component_update_bits(component, WM8983_DAC_CONTROL, + WM8983_SOFTMUTE_MASK, + !!mute << WM8983_SOFTMUTE_SHIFT); +} + +static int wm8983_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u16 format, master, bcp, lrp; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + format = 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = 0x1; + break; + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + format = 0x3; + break; + default: + dev_err(dai->dev, "Unknown dai format\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8983_AUDIO_INTERFACE, + WM8983_FMT_MASK, format << WM8983_FMT_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + master = 0; + break; + default: + dev_err(dai->dev, "Unknown master/slave configuration\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL, + WM8983_MS_MASK, master << WM8983_MS_SHIFT); + + /* FIXME: We don't currently support DSP A/B modes */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + dev_err(dai->dev, "DSP A/B modes are not supported\n"); + return -EINVAL; + default: + break; + } + + bcp = lrp = 0; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + bcp = lrp = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + bcp = 1; + break; + case SND_SOC_DAIFMT_NB_IF: + lrp = 1; + break; + default: + dev_err(dai->dev, "Unknown polarity configuration\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8983_AUDIO_INTERFACE, + WM8983_LRCP_MASK, lrp << WM8983_LRCP_SHIFT); + snd_soc_component_update_bits(component, WM8983_AUDIO_INTERFACE, + WM8983_BCP_MASK, bcp << WM8983_BCP_SHIFT); + return 0; +} + +static int wm8983_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int i; + struct snd_soc_component *component = dai->component; + struct wm8983_priv *wm8983 = snd_soc_component_get_drvdata(component); + u16 blen, srate_idx; + u32 tmp; + int srate_best; + int ret; + + ret = snd_soc_params_to_bclk(params); + if (ret < 0) { + dev_err(component->dev, "Failed to convert params to bclk: %d\n", ret); + return ret; + } + + wm8983->bclk = ret; + + switch (params_width(params)) { + case 16: + blen = 0x0; + break; + case 20: + blen = 0x1; + break; + case 24: + blen = 0x2; + break; + case 32: + blen = 0x3; + break; + default: + dev_err(dai->dev, "Unsupported word length %u\n", + params_width(params)); + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8983_AUDIO_INTERFACE, + WM8983_WL_MASK, blen << WM8983_WL_SHIFT); + + /* + * match to the nearest possible sample rate and rely + * on the array index to configure the SR register + */ + srate_idx = 0; + srate_best = abs(srates[0] - params_rate(params)); + for (i = 1; i < ARRAY_SIZE(srates); ++i) { + if (abs(srates[i] - params_rate(params)) >= srate_best) + continue; + srate_idx = i; + srate_best = abs(srates[i] - params_rate(params)); + } + + dev_dbg(dai->dev, "Selected SRATE = %d\n", srates[srate_idx]); + snd_soc_component_update_bits(component, WM8983_ADDITIONAL_CONTROL, + WM8983_SR_MASK, srate_idx << WM8983_SR_SHIFT); + + dev_dbg(dai->dev, "Target BCLK = %uHz\n", wm8983->bclk); + dev_dbg(dai->dev, "SYSCLK = %uHz\n", wm8983->sysclk); + + for (i = 0; i < ARRAY_SIZE(fs_ratios); ++i) { + if (wm8983->sysclk / params_rate(params) + == fs_ratios[i].ratio) + break; + } + + if (i == ARRAY_SIZE(fs_ratios)) { + dev_err(dai->dev, "Unable to configure MCLK ratio %u/%u\n", + wm8983->sysclk, params_rate(params)); + return -EINVAL; + } + + dev_dbg(dai->dev, "MCLK ratio = %dfs\n", fs_ratios[i].ratio); + snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL, + WM8983_MCLKDIV_MASK, i << WM8983_MCLKDIV_SHIFT); + + /* select the appropriate bclk divider */ + tmp = (wm8983->sysclk / fs_ratios[i].div) * 10; + for (i = 0; i < ARRAY_SIZE(bclk_divs); ++i) { + if (wm8983->bclk == tmp / bclk_divs[i]) + break; + } + + if (i == ARRAY_SIZE(bclk_divs)) { + dev_err(dai->dev, "No matching BCLK divider found\n"); + return -EINVAL; + } + + dev_dbg(dai->dev, "BCLK div = %d\n", i); + snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL, + WM8983_BCLKDIV_MASK, i << WM8983_BCLKDIV_SHIFT); + + return 0; +} + +struct pll_div { + u32 div2:1; + u32 n:4; + u32 k:24; +}; + +#define FIXED_PLL_SIZE ((1ULL << 24) * 10) +static int pll_factors(struct pll_div *pll_div, unsigned int target, + unsigned int source) +{ + u64 Kpart; + unsigned long int K, Ndiv, Nmod; + + pll_div->div2 = 0; + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div->div2 = 1; + Ndiv = target / source; + } + + if (Ndiv < 6 || Ndiv > 12) { + printk(KERN_ERR "%s: WM8983 N value is not within" + " the recommended range: %lu\n", __func__, Ndiv); + return -EINVAL; + } + pll_div->n = Ndiv; + + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (u64)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xffffffff; + if ((K % 10) >= 5) + K += 5; + K /= 10; + pll_div->k = K; + return 0; +} + +static int wm8983_set_pll(struct snd_soc_dai *dai, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + int ret; + struct snd_soc_component *component; + struct pll_div pll_div; + + component = dai->component; + if (!freq_in || !freq_out) { + /* disable the PLL */ + snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1, + WM8983_PLLEN_MASK, 0); + return 0; + } else { + ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in); + if (ret) + return ret; + + /* disable the PLL before re-programming it */ + snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1, + WM8983_PLLEN_MASK, 0); + + /* set PLLN and PRESCALE */ + snd_soc_component_write(component, WM8983_PLL_N, + (pll_div.div2 << WM8983_PLL_PRESCALE_SHIFT) + | pll_div.n); + /* set PLLK */ + snd_soc_component_write(component, WM8983_PLL_K_3, pll_div.k & 0x1ff); + snd_soc_component_write(component, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff); + snd_soc_component_write(component, WM8983_PLL_K_1, (pll_div.k >> 18)); + /* enable the PLL */ + snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1, + WM8983_PLLEN_MASK, WM8983_PLLEN); + } + + return 0; +} + +static int wm8983_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8983_priv *wm8983 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case WM8983_CLKSRC_MCLK: + snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL, + WM8983_CLKSEL_MASK, 0); + break; + case WM8983_CLKSRC_PLL: + snd_soc_component_update_bits(component, WM8983_CLOCK_GEN_CONTROL, + WM8983_CLKSEL_MASK, WM8983_CLKSEL); + break; + default: + dev_err(dai->dev, "Unknown clock source: %d\n", clk_id); + return -EINVAL; + } + + wm8983->sysclk = freq; + return 0; +} + +static int wm8983_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8983_priv *wm8983 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + /* VMID at 100k */ + snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1, + WM8983_VMIDSEL_MASK, + 1 << WM8983_VMIDSEL_SHIFT); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regcache_sync(wm8983->regmap); + if (ret < 0) { + dev_err(component->dev, "Failed to sync cache: %d\n", ret); + return ret; + } + /* enable anti-pop features */ + snd_soc_component_update_bits(component, WM8983_OUT4_TO_ADC, + WM8983_POBCTRL_MASK | WM8983_DELEN_MASK, + WM8983_POBCTRL | WM8983_DELEN); + /* enable thermal shutdown */ + snd_soc_component_update_bits(component, WM8983_OUTPUT_CTRL, + WM8983_TSDEN_MASK, WM8983_TSDEN); + /* enable BIASEN */ + snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1, + WM8983_BIASEN_MASK, WM8983_BIASEN); + /* VMID at 100k */ + snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1, + WM8983_VMIDSEL_MASK, + 1 << WM8983_VMIDSEL_SHIFT); + msleep(250); + /* disable anti-pop features */ + snd_soc_component_update_bits(component, WM8983_OUT4_TO_ADC, + WM8983_POBCTRL_MASK | + WM8983_DELEN_MASK, 0); + } + + /* VMID at 500k */ + snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1, + WM8983_VMIDSEL_MASK, + 2 << WM8983_VMIDSEL_SHIFT); + break; + case SND_SOC_BIAS_OFF: + /* disable thermal shutdown */ + snd_soc_component_update_bits(component, WM8983_OUTPUT_CTRL, + WM8983_TSDEN_MASK, 0); + /* disable VMIDSEL and BIASEN */ + snd_soc_component_update_bits(component, WM8983_POWER_MANAGEMENT_1, + WM8983_VMIDSEL_MASK | WM8983_BIASEN_MASK, + 0); + /* wait for VMID to discharge */ + msleep(100); + snd_soc_component_write(component, WM8983_POWER_MANAGEMENT_1, 0); + snd_soc_component_write(component, WM8983_POWER_MANAGEMENT_2, 0); + snd_soc_component_write(component, WM8983_POWER_MANAGEMENT_3, 0); + break; + } + + return 0; +} + +static int wm8983_probe(struct snd_soc_component *component) +{ + int ret; + int i; + + ret = snd_soc_component_write(component, WM8983_SOFTWARE_RESET, 0); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + /* set the vol/gain update bits */ + for (i = 0; i < ARRAY_SIZE(vol_update_regs); ++i) + snd_soc_component_update_bits(component, vol_update_regs[i], + 0x100, 0x100); + + /* mute all outputs and set PGAs to minimum gain */ + for (i = WM8983_LOUT1_HP_VOLUME_CTRL; + i <= WM8983_OUT4_MONO_MIX_CTRL; ++i) + snd_soc_component_update_bits(component, i, 0x40, 0x40); + + /* enable soft mute */ + snd_soc_component_update_bits(component, WM8983_DAC_CONTROL, + WM8983_SOFTMUTE_MASK, + WM8983_SOFTMUTE); + + /* enable BIASCUT */ + snd_soc_component_update_bits(component, WM8983_BIAS_CTRL, + WM8983_BIASCUT, WM8983_BIASCUT); + return 0; +} + +static const struct snd_soc_dai_ops wm8983_dai_ops = { + .mute_stream = wm8983_dac_mute, + .hw_params = wm8983_hw_params, + .set_fmt = wm8983_set_fmt, + .set_sysclk = wm8983_set_sysclk, + .set_pll = wm8983_set_pll, + .no_capture_mute = 1, +}; + +#define WM8983_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8983_dai = { + .name = "wm8983-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8983_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8983_FORMATS, + }, + .ops = &wm8983_dai_ops, + .symmetric_rate = 1 +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8983 = { + .probe = wm8983_probe, + .set_bias_level = wm8983_set_bias_level, + .controls = wm8983_snd_controls, + .num_controls = ARRAY_SIZE(wm8983_snd_controls), + .dapm_widgets = wm8983_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8983_dapm_widgets), + .dapm_routes = wm8983_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm8983_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8983_regmap = { + .reg_bits = 7, + .val_bits = 9, + + .reg_defaults = wm8983_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8983_defaults), + .cache_type = REGCACHE_MAPLE, + .max_register = WM8983_MAX_REGISTER, + + .writeable_reg = wm8983_writeable, +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8983_spi_probe(struct spi_device *spi) +{ + struct wm8983_priv *wm8983; + int ret; + + wm8983 = devm_kzalloc(&spi->dev, sizeof *wm8983, GFP_KERNEL); + if (!wm8983) + return -ENOMEM; + + wm8983->regmap = devm_regmap_init_spi(spi, &wm8983_regmap); + if (IS_ERR(wm8983->regmap)) { + ret = PTR_ERR(wm8983->regmap); + dev_err(&spi->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + spi_set_drvdata(spi, wm8983); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8983, &wm8983_dai, 1); + return ret; +} + +static struct spi_driver wm8983_spi_driver = { + .driver = { + .name = "wm8983", + }, + .probe = wm8983_spi_probe, +}; +#endif + +#if IS_ENABLED(CONFIG_I2C) +static int wm8983_i2c_probe(struct i2c_client *i2c) +{ + struct wm8983_priv *wm8983; + int ret; + + wm8983 = devm_kzalloc(&i2c->dev, sizeof *wm8983, GFP_KERNEL); + if (!wm8983) + return -ENOMEM; + + wm8983->regmap = devm_regmap_init_i2c(i2c, &wm8983_regmap); + if (IS_ERR(wm8983->regmap)) { + ret = PTR_ERR(wm8983->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, wm8983); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8983, &wm8983_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8983_i2c_id[] = { + { "wm8983", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8983_i2c_id); + +static struct i2c_driver wm8983_i2c_driver = { + .driver = { + .name = "wm8983", + }, + .probe = wm8983_i2c_probe, + .id_table = wm8983_i2c_id +}; +#endif + +static int __init wm8983_modinit(void) +{ + int ret = 0; + +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8983_i2c_driver); + if (ret) { + printk(KERN_ERR "Failed to register wm8983 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8983_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8983 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8983_modinit); + +static void __exit wm8983_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8983_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8983_spi_driver); +#endif +} +module_exit(wm8983_exit); + +MODULE_DESCRIPTION("ASoC WM8983 driver"); +MODULE_AUTHOR("Dimitris Papastamos "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8983.h b/sound/soc/codecs/wm8983.h new file mode 100644 index 0000000000..994c01704d --- /dev/null +++ b/sound/soc/codecs/wm8983.h @@ -0,0 +1,1026 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8983.h -- WM8983 ALSA SoC Audio driver + * + * Copyright 2011 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos + */ + +#ifndef _WM8983_H +#define _WM8983_H + +/* + * Register values. + */ +#define WM8983_SOFTWARE_RESET 0x00 +#define WM8983_POWER_MANAGEMENT_1 0x01 +#define WM8983_POWER_MANAGEMENT_2 0x02 +#define WM8983_POWER_MANAGEMENT_3 0x03 +#define WM8983_AUDIO_INTERFACE 0x04 +#define WM8983_COMPANDING_CONTROL 0x05 +#define WM8983_CLOCK_GEN_CONTROL 0x06 +#define WM8983_ADDITIONAL_CONTROL 0x07 +#define WM8983_GPIO_CONTROL 0x08 +#define WM8983_JACK_DETECT_CONTROL_1 0x09 +#define WM8983_DAC_CONTROL 0x0A +#define WM8983_LEFT_DAC_DIGITAL_VOL 0x0B +#define WM8983_RIGHT_DAC_DIGITAL_VOL 0x0C +#define WM8983_JACK_DETECT_CONTROL_2 0x0D +#define WM8983_ADC_CONTROL 0x0E +#define WM8983_LEFT_ADC_DIGITAL_VOL 0x0F +#define WM8983_RIGHT_ADC_DIGITAL_VOL 0x10 +#define WM8983_EQ1_LOW_SHELF 0x12 +#define WM8983_EQ2_PEAK_1 0x13 +#define WM8983_EQ3_PEAK_2 0x14 +#define WM8983_EQ4_PEAK_3 0x15 +#define WM8983_EQ5_HIGH_SHELF 0x16 +#define WM8983_DAC_LIMITER_1 0x18 +#define WM8983_DAC_LIMITER_2 0x19 +#define WM8983_NOTCH_FILTER_1 0x1B +#define WM8983_NOTCH_FILTER_2 0x1C +#define WM8983_NOTCH_FILTER_3 0x1D +#define WM8983_NOTCH_FILTER_4 0x1E +#define WM8983_ALC_CONTROL_1 0x20 +#define WM8983_ALC_CONTROL_2 0x21 +#define WM8983_ALC_CONTROL_3 0x22 +#define WM8983_NOISE_GATE 0x23 +#define WM8983_PLL_N 0x24 +#define WM8983_PLL_K_1 0x25 +#define WM8983_PLL_K_2 0x26 +#define WM8983_PLL_K_3 0x27 +#define WM8983_3D_CONTROL 0x29 +#define WM8983_OUT4_TO_ADC 0x2A +#define WM8983_BEEP_CONTROL 0x2B +#define WM8983_INPUT_CTRL 0x2C +#define WM8983_LEFT_INP_PGA_GAIN_CTRL 0x2D +#define WM8983_RIGHT_INP_PGA_GAIN_CTRL 0x2E +#define WM8983_LEFT_ADC_BOOST_CTRL 0x2F +#define WM8983_RIGHT_ADC_BOOST_CTRL 0x30 +#define WM8983_OUTPUT_CTRL 0x31 +#define WM8983_LEFT_MIXER_CTRL 0x32 +#define WM8983_RIGHT_MIXER_CTRL 0x33 +#define WM8983_LOUT1_HP_VOLUME_CTRL 0x34 +#define WM8983_ROUT1_HP_VOLUME_CTRL 0x35 +#define WM8983_LOUT2_SPK_VOLUME_CTRL 0x36 +#define WM8983_ROUT2_SPK_VOLUME_CTRL 0x37 +#define WM8983_OUT3_MIXER_CTRL 0x38 +#define WM8983_OUT4_MONO_MIX_CTRL 0x39 +#define WM8983_BIAS_CTRL 0x3D + +#define WM8983_REGISTER_COUNT 59 +#define WM8983_MAX_REGISTER 0x3F + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +#define WM8983_SOFTWARE_RESET_MASK 0x01FF /* SOFTWARE_RESET - [8:0] */ +#define WM8983_SOFTWARE_RESET_SHIFT 0 /* SOFTWARE_RESET - [8:0] */ +#define WM8983_SOFTWARE_RESET_WIDTH 9 /* SOFTWARE_RESET - [8:0] */ + +/* + * R1 (0x01) - Power management 1 + */ +#define WM8983_BUFDCOPEN 0x0100 /* BUFDCOPEN */ +#define WM8983_BUFDCOPEN_MASK 0x0100 /* BUFDCOPEN */ +#define WM8983_BUFDCOPEN_SHIFT 8 /* BUFDCOPEN */ +#define WM8983_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */ +#define WM8983_OUT4MIXEN 0x0080 /* OUT4MIXEN */ +#define WM8983_OUT4MIXEN_MASK 0x0080 /* OUT4MIXEN */ +#define WM8983_OUT4MIXEN_SHIFT 7 /* OUT4MIXEN */ +#define WM8983_OUT4MIXEN_WIDTH 1 /* OUT4MIXEN */ +#define WM8983_OUT3MIXEN 0x0040 /* OUT3MIXEN */ +#define WM8983_OUT3MIXEN_MASK 0x0040 /* OUT3MIXEN */ +#define WM8983_OUT3MIXEN_SHIFT 6 /* OUT3MIXEN */ +#define WM8983_OUT3MIXEN_WIDTH 1 /* OUT3MIXEN */ +#define WM8983_PLLEN 0x0020 /* PLLEN */ +#define WM8983_PLLEN_MASK 0x0020 /* PLLEN */ +#define WM8983_PLLEN_SHIFT 5 /* PLLEN */ +#define WM8983_PLLEN_WIDTH 1 /* PLLEN */ +#define WM8983_MICBEN 0x0010 /* MICBEN */ +#define WM8983_MICBEN_MASK 0x0010 /* MICBEN */ +#define WM8983_MICBEN_SHIFT 4 /* MICBEN */ +#define WM8983_MICBEN_WIDTH 1 /* MICBEN */ +#define WM8983_BIASEN 0x0008 /* BIASEN */ +#define WM8983_BIASEN_MASK 0x0008 /* BIASEN */ +#define WM8983_BIASEN_SHIFT 3 /* BIASEN */ +#define WM8983_BIASEN_WIDTH 1 /* BIASEN */ +#define WM8983_BUFIOEN 0x0004 /* BUFIOEN */ +#define WM8983_BUFIOEN_MASK 0x0004 /* BUFIOEN */ +#define WM8983_BUFIOEN_SHIFT 2 /* BUFIOEN */ +#define WM8983_BUFIOEN_WIDTH 1 /* BUFIOEN */ +#define WM8983_VMIDSEL_MASK 0x0003 /* VMIDSEL - [1:0] */ +#define WM8983_VMIDSEL_SHIFT 0 /* VMIDSEL - [1:0] */ +#define WM8983_VMIDSEL_WIDTH 2 /* VMIDSEL - [1:0] */ + +/* + * R2 (0x02) - Power management 2 + */ +#define WM8983_ROUT1EN 0x0100 /* ROUT1EN */ +#define WM8983_ROUT1EN_MASK 0x0100 /* ROUT1EN */ +#define WM8983_ROUT1EN_SHIFT 8 /* ROUT1EN */ +#define WM8983_ROUT1EN_WIDTH 1 /* ROUT1EN */ +#define WM8983_LOUT1EN 0x0080 /* LOUT1EN */ +#define WM8983_LOUT1EN_MASK 0x0080 /* LOUT1EN */ +#define WM8983_LOUT1EN_SHIFT 7 /* LOUT1EN */ +#define WM8983_LOUT1EN_WIDTH 1 /* LOUT1EN */ +#define WM8983_SLEEP 0x0040 /* SLEEP */ +#define WM8983_SLEEP_MASK 0x0040 /* SLEEP */ +#define WM8983_SLEEP_SHIFT 6 /* SLEEP */ +#define WM8983_SLEEP_WIDTH 1 /* SLEEP */ +#define WM8983_BOOSTENR 0x0020 /* BOOSTENR */ +#define WM8983_BOOSTENR_MASK 0x0020 /* BOOSTENR */ +#define WM8983_BOOSTENR_SHIFT 5 /* BOOSTENR */ +#define WM8983_BOOSTENR_WIDTH 1 /* BOOSTENR */ +#define WM8983_BOOSTENL 0x0010 /* BOOSTENL */ +#define WM8983_BOOSTENL_MASK 0x0010 /* BOOSTENL */ +#define WM8983_BOOSTENL_SHIFT 4 /* BOOSTENL */ +#define WM8983_BOOSTENL_WIDTH 1 /* BOOSTENL */ +#define WM8983_INPGAENR 0x0008 /* INPGAENR */ +#define WM8983_INPGAENR_MASK 0x0008 /* INPGAENR */ +#define WM8983_INPGAENR_SHIFT 3 /* INPGAENR */ +#define WM8983_INPGAENR_WIDTH 1 /* INPGAENR */ +#define WM8983_INPPGAENL 0x0004 /* INPPGAENL */ +#define WM8983_INPPGAENL_MASK 0x0004 /* INPPGAENL */ +#define WM8983_INPPGAENL_SHIFT 2 /* INPPGAENL */ +#define WM8983_INPPGAENL_WIDTH 1 /* INPPGAENL */ +#define WM8983_ADCENR 0x0002 /* ADCENR */ +#define WM8983_ADCENR_MASK 0x0002 /* ADCENR */ +#define WM8983_ADCENR_SHIFT 1 /* ADCENR */ +#define WM8983_ADCENR_WIDTH 1 /* ADCENR */ +#define WM8983_ADCENL 0x0001 /* ADCENL */ +#define WM8983_ADCENL_MASK 0x0001 /* ADCENL */ +#define WM8983_ADCENL_SHIFT 0 /* ADCENL */ +#define WM8983_ADCENL_WIDTH 1 /* ADCENL */ + +/* + * R3 (0x03) - Power management 3 + */ +#define WM8983_OUT4EN 0x0100 /* OUT4EN */ +#define WM8983_OUT4EN_MASK 0x0100 /* OUT4EN */ +#define WM8983_OUT4EN_SHIFT 8 /* OUT4EN */ +#define WM8983_OUT4EN_WIDTH 1 /* OUT4EN */ +#define WM8983_OUT3EN 0x0080 /* OUT3EN */ +#define WM8983_OUT3EN_MASK 0x0080 /* OUT3EN */ +#define WM8983_OUT3EN_SHIFT 7 /* OUT3EN */ +#define WM8983_OUT3EN_WIDTH 1 /* OUT3EN */ +#define WM8983_LOUT2EN 0x0040 /* LOUT2EN */ +#define WM8983_LOUT2EN_MASK 0x0040 /* LOUT2EN */ +#define WM8983_LOUT2EN_SHIFT 6 /* LOUT2EN */ +#define WM8983_LOUT2EN_WIDTH 1 /* LOUT2EN */ +#define WM8983_ROUT2EN 0x0020 /* ROUT2EN */ +#define WM8983_ROUT2EN_MASK 0x0020 /* ROUT2EN */ +#define WM8983_ROUT2EN_SHIFT 5 /* ROUT2EN */ +#define WM8983_ROUT2EN_WIDTH 1 /* ROUT2EN */ +#define WM8983_RMIXEN 0x0008 /* RMIXEN */ +#define WM8983_RMIXEN_MASK 0x0008 /* RMIXEN */ +#define WM8983_RMIXEN_SHIFT 3 /* RMIXEN */ +#define WM8983_RMIXEN_WIDTH 1 /* RMIXEN */ +#define WM8983_LMIXEN 0x0004 /* LMIXEN */ +#define WM8983_LMIXEN_MASK 0x0004 /* LMIXEN */ +#define WM8983_LMIXEN_SHIFT 2 /* LMIXEN */ +#define WM8983_LMIXEN_WIDTH 1 /* LMIXEN */ +#define WM8983_DACENR 0x0002 /* DACENR */ +#define WM8983_DACENR_MASK 0x0002 /* DACENR */ +#define WM8983_DACENR_SHIFT 1 /* DACENR */ +#define WM8983_DACENR_WIDTH 1 /* DACENR */ +#define WM8983_DACENL 0x0001 /* DACENL */ +#define WM8983_DACENL_MASK 0x0001 /* DACENL */ +#define WM8983_DACENL_SHIFT 0 /* DACENL */ +#define WM8983_DACENL_WIDTH 1 /* DACENL */ + +/* + * R4 (0x04) - Audio Interface + */ +#define WM8983_BCP 0x0100 /* BCP */ +#define WM8983_BCP_MASK 0x0100 /* BCP */ +#define WM8983_BCP_SHIFT 8 /* BCP */ +#define WM8983_BCP_WIDTH 1 /* BCP */ +#define WM8983_LRCP 0x0080 /* LRCP */ +#define WM8983_LRCP_MASK 0x0080 /* LRCP */ +#define WM8983_LRCP_SHIFT 7 /* LRCP */ +#define WM8983_LRCP_WIDTH 1 /* LRCP */ +#define WM8983_WL_MASK 0x0060 /* WL - [6:5] */ +#define WM8983_WL_SHIFT 5 /* WL - [6:5] */ +#define WM8983_WL_WIDTH 2 /* WL - [6:5] */ +#define WM8983_FMT_MASK 0x0018 /* FMT - [4:3] */ +#define WM8983_FMT_SHIFT 3 /* FMT - [4:3] */ +#define WM8983_FMT_WIDTH 2 /* FMT - [4:3] */ +#define WM8983_DLRSWAP 0x0004 /* DLRSWAP */ +#define WM8983_DLRSWAP_MASK 0x0004 /* DLRSWAP */ +#define WM8983_DLRSWAP_SHIFT 2 /* DLRSWAP */ +#define WM8983_DLRSWAP_WIDTH 1 /* DLRSWAP */ +#define WM8983_ALRSWAP 0x0002 /* ALRSWAP */ +#define WM8983_ALRSWAP_MASK 0x0002 /* ALRSWAP */ +#define WM8983_ALRSWAP_SHIFT 1 /* ALRSWAP */ +#define WM8983_ALRSWAP_WIDTH 1 /* ALRSWAP */ +#define WM8983_MONO 0x0001 /* MONO */ +#define WM8983_MONO_MASK 0x0001 /* MONO */ +#define WM8983_MONO_SHIFT 0 /* MONO */ +#define WM8983_MONO_WIDTH 1 /* MONO */ + +/* + * R5 (0x05) - Companding control + */ +#define WM8983_WL8 0x0020 /* WL8 */ +#define WM8983_WL8_MASK 0x0020 /* WL8 */ +#define WM8983_WL8_SHIFT 5 /* WL8 */ +#define WM8983_WL8_WIDTH 1 /* WL8 */ +#define WM8983_DAC_COMP_MASK 0x0018 /* DAC_COMP - [4:3] */ +#define WM8983_DAC_COMP_SHIFT 3 /* DAC_COMP - [4:3] */ +#define WM8983_DAC_COMP_WIDTH 2 /* DAC_COMP - [4:3] */ +#define WM8983_ADC_COMP_MASK 0x0006 /* ADC_COMP - [2:1] */ +#define WM8983_ADC_COMP_SHIFT 1 /* ADC_COMP - [2:1] */ +#define WM8983_ADC_COMP_WIDTH 2 /* ADC_COMP - [2:1] */ +#define WM8983_LOOPBACK 0x0001 /* LOOPBACK */ +#define WM8983_LOOPBACK_MASK 0x0001 /* LOOPBACK */ +#define WM8983_LOOPBACK_SHIFT 0 /* LOOPBACK */ +#define WM8983_LOOPBACK_WIDTH 1 /* LOOPBACK */ + +/* + * R6 (0x06) - Clock Gen control + */ +#define WM8983_CLKSEL 0x0100 /* CLKSEL */ +#define WM8983_CLKSEL_MASK 0x0100 /* CLKSEL */ +#define WM8983_CLKSEL_SHIFT 8 /* CLKSEL */ +#define WM8983_CLKSEL_WIDTH 1 /* CLKSEL */ +#define WM8983_MCLKDIV_MASK 0x00E0 /* MCLKDIV - [7:5] */ +#define WM8983_MCLKDIV_SHIFT 5 /* MCLKDIV - [7:5] */ +#define WM8983_MCLKDIV_WIDTH 3 /* MCLKDIV - [7:5] */ +#define WM8983_BCLKDIV_MASK 0x001C /* BCLKDIV - [4:2] */ +#define WM8983_BCLKDIV_SHIFT 2 /* BCLKDIV - [4:2] */ +#define WM8983_BCLKDIV_WIDTH 3 /* BCLKDIV - [4:2] */ +#define WM8983_MS 0x0001 /* MS */ +#define WM8983_MS_MASK 0x0001 /* MS */ +#define WM8983_MS_SHIFT 0 /* MS */ +#define WM8983_MS_WIDTH 1 /* MS */ + +/* + * R7 (0x07) - Additional control + */ +#define WM8983_SR_MASK 0x000E /* SR - [3:1] */ +#define WM8983_SR_SHIFT 1 /* SR - [3:1] */ +#define WM8983_SR_WIDTH 3 /* SR - [3:1] */ +#define WM8983_SLOWCLKEN 0x0001 /* SLOWCLKEN */ +#define WM8983_SLOWCLKEN_MASK 0x0001 /* SLOWCLKEN */ +#define WM8983_SLOWCLKEN_SHIFT 0 /* SLOWCLKEN */ +#define WM8983_SLOWCLKEN_WIDTH 1 /* SLOWCLKEN */ + +/* + * R8 (0x08) - GPIO Control + */ +#define WM8983_OPCLKDIV_MASK 0x0030 /* OPCLKDIV - [5:4] */ +#define WM8983_OPCLKDIV_SHIFT 4 /* OPCLKDIV - [5:4] */ +#define WM8983_OPCLKDIV_WIDTH 2 /* OPCLKDIV - [5:4] */ +#define WM8983_GPIO1POL 0x0008 /* GPIO1POL */ +#define WM8983_GPIO1POL_MASK 0x0008 /* GPIO1POL */ +#define WM8983_GPIO1POL_SHIFT 3 /* GPIO1POL */ +#define WM8983_GPIO1POL_WIDTH 1 /* GPIO1POL */ +#define WM8983_GPIO1SEL_MASK 0x0007 /* GPIO1SEL - [2:0] */ +#define WM8983_GPIO1SEL_SHIFT 0 /* GPIO1SEL - [2:0] */ +#define WM8983_GPIO1SEL_WIDTH 3 /* GPIO1SEL - [2:0] */ + +/* + * R9 (0x09) - Jack Detect Control 1 + */ +#define WM8983_JD_VMID1 0x0100 /* JD_VMID1 */ +#define WM8983_JD_VMID1_MASK 0x0100 /* JD_VMID1 */ +#define WM8983_JD_VMID1_SHIFT 8 /* JD_VMID1 */ +#define WM8983_JD_VMID1_WIDTH 1 /* JD_VMID1 */ +#define WM8983_JD_VMID0 0x0080 /* JD_VMID0 */ +#define WM8983_JD_VMID0_MASK 0x0080 /* JD_VMID0 */ +#define WM8983_JD_VMID0_SHIFT 7 /* JD_VMID0 */ +#define WM8983_JD_VMID0_WIDTH 1 /* JD_VMID0 */ +#define WM8983_JD_EN 0x0040 /* JD_EN */ +#define WM8983_JD_EN_MASK 0x0040 /* JD_EN */ +#define WM8983_JD_EN_SHIFT 6 /* JD_EN */ +#define WM8983_JD_EN_WIDTH 1 /* JD_EN */ +#define WM8983_JD_SEL_MASK 0x0030 /* JD_SEL - [5:4] */ +#define WM8983_JD_SEL_SHIFT 4 /* JD_SEL - [5:4] */ +#define WM8983_JD_SEL_WIDTH 2 /* JD_SEL - [5:4] */ + +/* + * R10 (0x0A) - DAC Control + */ +#define WM8983_SOFTMUTE 0x0040 /* SOFTMUTE */ +#define WM8983_SOFTMUTE_MASK 0x0040 /* SOFTMUTE */ +#define WM8983_SOFTMUTE_SHIFT 6 /* SOFTMUTE */ +#define WM8983_SOFTMUTE_WIDTH 1 /* SOFTMUTE */ +#define WM8983_DACOSR128 0x0008 /* DACOSR128 */ +#define WM8983_DACOSR128_MASK 0x0008 /* DACOSR128 */ +#define WM8983_DACOSR128_SHIFT 3 /* DACOSR128 */ +#define WM8983_DACOSR128_WIDTH 1 /* DACOSR128 */ +#define WM8983_AMUTE 0x0004 /* AMUTE */ +#define WM8983_AMUTE_MASK 0x0004 /* AMUTE */ +#define WM8983_AMUTE_SHIFT 2 /* AMUTE */ +#define WM8983_AMUTE_WIDTH 1 /* AMUTE */ +#define WM8983_DACRPOL 0x0002 /* DACRPOL */ +#define WM8983_DACRPOL_MASK 0x0002 /* DACRPOL */ +#define WM8983_DACRPOL_SHIFT 1 /* DACRPOL */ +#define WM8983_DACRPOL_WIDTH 1 /* DACRPOL */ +#define WM8983_DACLPOL 0x0001 /* DACLPOL */ +#define WM8983_DACLPOL_MASK 0x0001 /* DACLPOL */ +#define WM8983_DACLPOL_SHIFT 0 /* DACLPOL */ +#define WM8983_DACLPOL_WIDTH 1 /* DACLPOL */ + +/* + * R11 (0x0B) - Left DAC digital Vol + */ +#define WM8983_DACVU 0x0100 /* DACVU */ +#define WM8983_DACVU_MASK 0x0100 /* DACVU */ +#define WM8983_DACVU_SHIFT 8 /* DACVU */ +#define WM8983_DACVU_WIDTH 1 /* DACVU */ +#define WM8983_DACLVOL_MASK 0x00FF /* DACLVOL - [7:0] */ +#define WM8983_DACLVOL_SHIFT 0 /* DACLVOL - [7:0] */ +#define WM8983_DACLVOL_WIDTH 8 /* DACLVOL - [7:0] */ + +/* + * R12 (0x0C) - Right DAC digital vol + */ +#define WM8983_DACVU 0x0100 /* DACVU */ +#define WM8983_DACVU_MASK 0x0100 /* DACVU */ +#define WM8983_DACVU_SHIFT 8 /* DACVU */ +#define WM8983_DACVU_WIDTH 1 /* DACVU */ +#define WM8983_DACRVOL_MASK 0x00FF /* DACRVOL - [7:0] */ +#define WM8983_DACRVOL_SHIFT 0 /* DACRVOL - [7:0] */ +#define WM8983_DACRVOL_WIDTH 8 /* DACRVOL - [7:0] */ + +/* + * R13 (0x0D) - Jack Detect Control 2 + */ +#define WM8983_JD_EN1_MASK 0x00F0 /* JD_EN1 - [7:4] */ +#define WM8983_JD_EN1_SHIFT 4 /* JD_EN1 - [7:4] */ +#define WM8983_JD_EN1_WIDTH 4 /* JD_EN1 - [7:4] */ +#define WM8983_JD_EN0_MASK 0x000F /* JD_EN0 - [3:0] */ +#define WM8983_JD_EN0_SHIFT 0 /* JD_EN0 - [3:0] */ +#define WM8983_JD_EN0_WIDTH 4 /* JD_EN0 - [3:0] */ + +/* + * R14 (0x0E) - ADC Control + */ +#define WM8983_HPFEN 0x0100 /* HPFEN */ +#define WM8983_HPFEN_MASK 0x0100 /* HPFEN */ +#define WM8983_HPFEN_SHIFT 8 /* HPFEN */ +#define WM8983_HPFEN_WIDTH 1 /* HPFEN */ +#define WM8983_HPFAPP 0x0080 /* HPFAPP */ +#define WM8983_HPFAPP_MASK 0x0080 /* HPFAPP */ +#define WM8983_HPFAPP_SHIFT 7 /* HPFAPP */ +#define WM8983_HPFAPP_WIDTH 1 /* HPFAPP */ +#define WM8983_HPFCUT_MASK 0x0070 /* HPFCUT - [6:4] */ +#define WM8983_HPFCUT_SHIFT 4 /* HPFCUT - [6:4] */ +#define WM8983_HPFCUT_WIDTH 3 /* HPFCUT - [6:4] */ +#define WM8983_ADCOSR128 0x0008 /* ADCOSR128 */ +#define WM8983_ADCOSR128_MASK 0x0008 /* ADCOSR128 */ +#define WM8983_ADCOSR128_SHIFT 3 /* ADCOSR128 */ +#define WM8983_ADCOSR128_WIDTH 1 /* ADCOSR128 */ +#define WM8983_ADCRPOL 0x0002 /* ADCRPOL */ +#define WM8983_ADCRPOL_MASK 0x0002 /* ADCRPOL */ +#define WM8983_ADCRPOL_SHIFT 1 /* ADCRPOL */ +#define WM8983_ADCRPOL_WIDTH 1 /* ADCRPOL */ +#define WM8983_ADCLPOL 0x0001 /* ADCLPOL */ +#define WM8983_ADCLPOL_MASK 0x0001 /* ADCLPOL */ +#define WM8983_ADCLPOL_SHIFT 0 /* ADCLPOL */ +#define WM8983_ADCLPOL_WIDTH 1 /* ADCLPOL */ + +/* + * R15 (0x0F) - Left ADC Digital Vol + */ +#define WM8983_ADCVU 0x0100 /* ADCVU */ +#define WM8983_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8983_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8983_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8983_ADCLVOL_MASK 0x00FF /* ADCLVOL - [7:0] */ +#define WM8983_ADCLVOL_SHIFT 0 /* ADCLVOL - [7:0] */ +#define WM8983_ADCLVOL_WIDTH 8 /* ADCLVOL - [7:0] */ + +/* + * R16 (0x10) - Right ADC Digital Vol + */ +#define WM8983_ADCVU 0x0100 /* ADCVU */ +#define WM8983_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8983_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8983_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8983_ADCRVOL_MASK 0x00FF /* ADCRVOL - [7:0] */ +#define WM8983_ADCRVOL_SHIFT 0 /* ADCRVOL - [7:0] */ +#define WM8983_ADCRVOL_WIDTH 8 /* ADCRVOL - [7:0] */ + +/* + * R18 (0x12) - EQ1 - low shelf + */ +#define WM8983_EQ3DMODE 0x0100 /* EQ3DMODE */ +#define WM8983_EQ3DMODE_MASK 0x0100 /* EQ3DMODE */ +#define WM8983_EQ3DMODE_SHIFT 8 /* EQ3DMODE */ +#define WM8983_EQ3DMODE_WIDTH 1 /* EQ3DMODE */ +#define WM8983_EQ1C_MASK 0x0060 /* EQ1C - [6:5] */ +#define WM8983_EQ1C_SHIFT 5 /* EQ1C - [6:5] */ +#define WM8983_EQ1C_WIDTH 2 /* EQ1C - [6:5] */ +#define WM8983_EQ1G_MASK 0x001F /* EQ1G - [4:0] */ +#define WM8983_EQ1G_SHIFT 0 /* EQ1G - [4:0] */ +#define WM8983_EQ1G_WIDTH 5 /* EQ1G - [4:0] */ + +/* + * R19 (0x13) - EQ2 - peak 1 + */ +#define WM8983_EQ2BW 0x0100 /* EQ2BW */ +#define WM8983_EQ2BW_MASK 0x0100 /* EQ2BW */ +#define WM8983_EQ2BW_SHIFT 8 /* EQ2BW */ +#define WM8983_EQ2BW_WIDTH 1 /* EQ2BW */ +#define WM8983_EQ2C_MASK 0x0060 /* EQ2C - [6:5] */ +#define WM8983_EQ2C_SHIFT 5 /* EQ2C - [6:5] */ +#define WM8983_EQ2C_WIDTH 2 /* EQ2C - [6:5] */ +#define WM8983_EQ2G_MASK 0x001F /* EQ2G - [4:0] */ +#define WM8983_EQ2G_SHIFT 0 /* EQ2G - [4:0] */ +#define WM8983_EQ2G_WIDTH 5 /* EQ2G - [4:0] */ + +/* + * R20 (0x14) - EQ3 - peak 2 + */ +#define WM8983_EQ3BW 0x0100 /* EQ3BW */ +#define WM8983_EQ3BW_MASK 0x0100 /* EQ3BW */ +#define WM8983_EQ3BW_SHIFT 8 /* EQ3BW */ +#define WM8983_EQ3BW_WIDTH 1 /* EQ3BW */ +#define WM8983_EQ3C_MASK 0x0060 /* EQ3C - [6:5] */ +#define WM8983_EQ3C_SHIFT 5 /* EQ3C - [6:5] */ +#define WM8983_EQ3C_WIDTH 2 /* EQ3C - [6:5] */ +#define WM8983_EQ3G_MASK 0x001F /* EQ3G - [4:0] */ +#define WM8983_EQ3G_SHIFT 0 /* EQ3G - [4:0] */ +#define WM8983_EQ3G_WIDTH 5 /* EQ3G - [4:0] */ + +/* + * R21 (0x15) - EQ4 - peak 3 + */ +#define WM8983_EQ4BW 0x0100 /* EQ4BW */ +#define WM8983_EQ4BW_MASK 0x0100 /* EQ4BW */ +#define WM8983_EQ4BW_SHIFT 8 /* EQ4BW */ +#define WM8983_EQ4BW_WIDTH 1 /* EQ4BW */ +#define WM8983_EQ4C_MASK 0x0060 /* EQ4C - [6:5] */ +#define WM8983_EQ4C_SHIFT 5 /* EQ4C - [6:5] */ +#define WM8983_EQ4C_WIDTH 2 /* EQ4C - [6:5] */ +#define WM8983_EQ4G_MASK 0x001F /* EQ4G - [4:0] */ +#define WM8983_EQ4G_SHIFT 0 /* EQ4G - [4:0] */ +#define WM8983_EQ4G_WIDTH 5 /* EQ4G - [4:0] */ + +/* + * R22 (0x16) - EQ5 - high shelf + */ +#define WM8983_EQ5C_MASK 0x0060 /* EQ5C - [6:5] */ +#define WM8983_EQ5C_SHIFT 5 /* EQ5C - [6:5] */ +#define WM8983_EQ5C_WIDTH 2 /* EQ5C - [6:5] */ +#define WM8983_EQ5G_MASK 0x001F /* EQ5G - [4:0] */ +#define WM8983_EQ5G_SHIFT 0 /* EQ5G - [4:0] */ +#define WM8983_EQ5G_WIDTH 5 /* EQ5G - [4:0] */ + +/* + * R24 (0x18) - DAC Limiter 1 + */ +#define WM8983_LIMEN 0x0100 /* LIMEN */ +#define WM8983_LIMEN_MASK 0x0100 /* LIMEN */ +#define WM8983_LIMEN_SHIFT 8 /* LIMEN */ +#define WM8983_LIMEN_WIDTH 1 /* LIMEN */ +#define WM8983_LIMDCY_MASK 0x00F0 /* LIMDCY - [7:4] */ +#define WM8983_LIMDCY_SHIFT 4 /* LIMDCY - [7:4] */ +#define WM8983_LIMDCY_WIDTH 4 /* LIMDCY - [7:4] */ +#define WM8983_LIMATK_MASK 0x000F /* LIMATK - [3:0] */ +#define WM8983_LIMATK_SHIFT 0 /* LIMATK - [3:0] */ +#define WM8983_LIMATK_WIDTH 4 /* LIMATK - [3:0] */ + +/* + * R25 (0x19) - DAC Limiter 2 + */ +#define WM8983_LIMLVL_MASK 0x0070 /* LIMLVL - [6:4] */ +#define WM8983_LIMLVL_SHIFT 4 /* LIMLVL - [6:4] */ +#define WM8983_LIMLVL_WIDTH 3 /* LIMLVL - [6:4] */ +#define WM8983_LIMBOOST_MASK 0x000F /* LIMBOOST - [3:0] */ +#define WM8983_LIMBOOST_SHIFT 0 /* LIMBOOST - [3:0] */ +#define WM8983_LIMBOOST_WIDTH 4 /* LIMBOOST - [3:0] */ + +/* + * R27 (0x1B) - Notch Filter 1 + */ +#define WM8983_NFU 0x0100 /* NFU */ +#define WM8983_NFU_MASK 0x0100 /* NFU */ +#define WM8983_NFU_SHIFT 8 /* NFU */ +#define WM8983_NFU_WIDTH 1 /* NFU */ +#define WM8983_NFEN 0x0080 /* NFEN */ +#define WM8983_NFEN_MASK 0x0080 /* NFEN */ +#define WM8983_NFEN_SHIFT 7 /* NFEN */ +#define WM8983_NFEN_WIDTH 1 /* NFEN */ +#define WM8983_NFA0_13_7_MASK 0x007F /* NFA0(13:7) - [6:0] */ +#define WM8983_NFA0_13_7_SHIFT 0 /* NFA0(13:7) - [6:0] */ +#define WM8983_NFA0_13_7_WIDTH 7 /* NFA0(13:7) - [6:0] */ + +/* + * R28 (0x1C) - Notch Filter 2 + */ +#define WM8983_NFU 0x0100 /* NFU */ +#define WM8983_NFU_MASK 0x0100 /* NFU */ +#define WM8983_NFU_SHIFT 8 /* NFU */ +#define WM8983_NFU_WIDTH 1 /* NFU */ +#define WM8983_NFA0_6_0_MASK 0x007F /* NFA0(6:0) - [6:0] */ +#define WM8983_NFA0_6_0_SHIFT 0 /* NFA0(6:0) - [6:0] */ +#define WM8983_NFA0_6_0_WIDTH 7 /* NFA0(6:0) - [6:0] */ + +/* + * R29 (0x1D) - Notch Filter 3 + */ +#define WM8983_NFU 0x0100 /* NFU */ +#define WM8983_NFU_MASK 0x0100 /* NFU */ +#define WM8983_NFU_SHIFT 8 /* NFU */ +#define WM8983_NFU_WIDTH 1 /* NFU */ +#define WM8983_NFA1_13_7_MASK 0x007F /* NFA1(13:7) - [6:0] */ +#define WM8983_NFA1_13_7_SHIFT 0 /* NFA1(13:7) - [6:0] */ +#define WM8983_NFA1_13_7_WIDTH 7 /* NFA1(13:7) - [6:0] */ + +/* + * R30 (0x1E) - Notch Filter 4 + */ +#define WM8983_NFU 0x0100 /* NFU */ +#define WM8983_NFU_MASK 0x0100 /* NFU */ +#define WM8983_NFU_SHIFT 8 /* NFU */ +#define WM8983_NFU_WIDTH 1 /* NFU */ +#define WM8983_NFA1_6_0_MASK 0x007F /* NFA1(6:0) - [6:0] */ +#define WM8983_NFA1_6_0_SHIFT 0 /* NFA1(6:0) - [6:0] */ +#define WM8983_NFA1_6_0_WIDTH 7 /* NFA1(6:0) - [6:0] */ + +/* + * R32 (0x20) - ALC control 1 + */ +#define WM8983_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */ +#define WM8983_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */ +#define WM8983_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */ +#define WM8983_ALCMAX_MASK 0x0038 /* ALCMAX - [5:3] */ +#define WM8983_ALCMAX_SHIFT 3 /* ALCMAX - [5:3] */ +#define WM8983_ALCMAX_WIDTH 3 /* ALCMAX - [5:3] */ +#define WM8983_ALCMIN_MASK 0x0007 /* ALCMIN - [2:0] */ +#define WM8983_ALCMIN_SHIFT 0 /* ALCMIN - [2:0] */ +#define WM8983_ALCMIN_WIDTH 3 /* ALCMIN - [2:0] */ + +/* + * R33 (0x21) - ALC control 2 + */ +#define WM8983_ALCHLD_MASK 0x00F0 /* ALCHLD - [7:4] */ +#define WM8983_ALCHLD_SHIFT 4 /* ALCHLD - [7:4] */ +#define WM8983_ALCHLD_WIDTH 4 /* ALCHLD - [7:4] */ +#define WM8983_ALCLVL_MASK 0x000F /* ALCLVL - [3:0] */ +#define WM8983_ALCLVL_SHIFT 0 /* ALCLVL - [3:0] */ +#define WM8983_ALCLVL_WIDTH 4 /* ALCLVL - [3:0] */ + +/* + * R34 (0x22) - ALC control 3 + */ +#define WM8983_ALCMODE 0x0100 /* ALCMODE */ +#define WM8983_ALCMODE_MASK 0x0100 /* ALCMODE */ +#define WM8983_ALCMODE_SHIFT 8 /* ALCMODE */ +#define WM8983_ALCMODE_WIDTH 1 /* ALCMODE */ +#define WM8983_ALCDCY_MASK 0x00F0 /* ALCDCY - [7:4] */ +#define WM8983_ALCDCY_SHIFT 4 /* ALCDCY - [7:4] */ +#define WM8983_ALCDCY_WIDTH 4 /* ALCDCY - [7:4] */ +#define WM8983_ALCATK_MASK 0x000F /* ALCATK - [3:0] */ +#define WM8983_ALCATK_SHIFT 0 /* ALCATK - [3:0] */ +#define WM8983_ALCATK_WIDTH 4 /* ALCATK - [3:0] */ + +/* + * R35 (0x23) - Noise Gate + */ +#define WM8983_NGEN 0x0008 /* NGEN */ +#define WM8983_NGEN_MASK 0x0008 /* NGEN */ +#define WM8983_NGEN_SHIFT 3 /* NGEN */ +#define WM8983_NGEN_WIDTH 1 /* NGEN */ +#define WM8983_NGTH_MASK 0x0007 /* NGTH - [2:0] */ +#define WM8983_NGTH_SHIFT 0 /* NGTH - [2:0] */ +#define WM8983_NGTH_WIDTH 3 /* NGTH - [2:0] */ + +/* + * R36 (0x24) - PLL N + */ +#define WM8983_PLL_PRESCALE 0x0010 /* PLL_PRESCALE */ +#define WM8983_PLL_PRESCALE_MASK 0x0010 /* PLL_PRESCALE */ +#define WM8983_PLL_PRESCALE_SHIFT 4 /* PLL_PRESCALE */ +#define WM8983_PLL_PRESCALE_WIDTH 1 /* PLL_PRESCALE */ +#define WM8983_PLLN_MASK 0x000F /* PLLN - [3:0] */ +#define WM8983_PLLN_SHIFT 0 /* PLLN - [3:0] */ +#define WM8983_PLLN_WIDTH 4 /* PLLN - [3:0] */ + +/* + * R37 (0x25) - PLL K 1 + */ +#define WM8983_PLLK_23_18_MASK 0x003F /* PLLK(23:18) - [5:0] */ +#define WM8983_PLLK_23_18_SHIFT 0 /* PLLK(23:18) - [5:0] */ +#define WM8983_PLLK_23_18_WIDTH 6 /* PLLK(23:18) - [5:0] */ + +/* + * R38 (0x26) - PLL K 2 + */ +#define WM8983_PLLK_17_9_MASK 0x01FF /* PLLK(17:9) - [8:0] */ +#define WM8983_PLLK_17_9_SHIFT 0 /* PLLK(17:9) - [8:0] */ +#define WM8983_PLLK_17_9_WIDTH 9 /* PLLK(17:9) - [8:0] */ + +/* + * R39 (0x27) - PLL K 3 + */ +#define WM8983_PLLK_8_0_MASK 0x01FF /* PLLK(8:0) - [8:0] */ +#define WM8983_PLLK_8_0_SHIFT 0 /* PLLK(8:0) - [8:0] */ +#define WM8983_PLLK_8_0_WIDTH 9 /* PLLK(8:0) - [8:0] */ + +/* + * R41 (0x29) - 3D control + */ +#define WM8983_DEPTH3D_MASK 0x000F /* DEPTH3D - [3:0] */ +#define WM8983_DEPTH3D_SHIFT 0 /* DEPTH3D - [3:0] */ +#define WM8983_DEPTH3D_WIDTH 4 /* DEPTH3D - [3:0] */ + +/* + * R42 (0x2A) - OUT4 to ADC + */ +#define WM8983_OUT4_2ADCVOL_MASK 0x01C0 /* OUT4_2ADCVOL - [8:6] */ +#define WM8983_OUT4_2ADCVOL_SHIFT 6 /* OUT4_2ADCVOL - [8:6] */ +#define WM8983_OUT4_2ADCVOL_WIDTH 3 /* OUT4_2ADCVOL - [8:6] */ +#define WM8983_OUT4_2LNR 0x0020 /* OUT4_2LNR */ +#define WM8983_OUT4_2LNR_MASK 0x0020 /* OUT4_2LNR */ +#define WM8983_OUT4_2LNR_SHIFT 5 /* OUT4_2LNR */ +#define WM8983_OUT4_2LNR_WIDTH 1 /* OUT4_2LNR */ +#define WM8983_POBCTRL 0x0004 /* POBCTRL */ +#define WM8983_POBCTRL_MASK 0x0004 /* POBCTRL */ +#define WM8983_POBCTRL_SHIFT 2 /* POBCTRL */ +#define WM8983_POBCTRL_WIDTH 1 /* POBCTRL */ +#define WM8983_DELEN 0x0002 /* DELEN */ +#define WM8983_DELEN_MASK 0x0002 /* DELEN */ +#define WM8983_DELEN_SHIFT 1 /* DELEN */ +#define WM8983_DELEN_WIDTH 1 /* DELEN */ +#define WM8983_OUT1DEL 0x0001 /* OUT1DEL */ +#define WM8983_OUT1DEL_MASK 0x0001 /* OUT1DEL */ +#define WM8983_OUT1DEL_SHIFT 0 /* OUT1DEL */ +#define WM8983_OUT1DEL_WIDTH 1 /* OUT1DEL */ + +/* + * R43 (0x2B) - Beep control + */ +#define WM8983_BYPL2RMIX 0x0100 /* BYPL2RMIX */ +#define WM8983_BYPL2RMIX_MASK 0x0100 /* BYPL2RMIX */ +#define WM8983_BYPL2RMIX_SHIFT 8 /* BYPL2RMIX */ +#define WM8983_BYPL2RMIX_WIDTH 1 /* BYPL2RMIX */ +#define WM8983_BYPR2LMIX 0x0080 /* BYPR2LMIX */ +#define WM8983_BYPR2LMIX_MASK 0x0080 /* BYPR2LMIX */ +#define WM8983_BYPR2LMIX_SHIFT 7 /* BYPR2LMIX */ +#define WM8983_BYPR2LMIX_WIDTH 1 /* BYPR2LMIX */ +#define WM8983_MUTERPGA2INV 0x0020 /* MUTERPGA2INV */ +#define WM8983_MUTERPGA2INV_MASK 0x0020 /* MUTERPGA2INV */ +#define WM8983_MUTERPGA2INV_SHIFT 5 /* MUTERPGA2INV */ +#define WM8983_MUTERPGA2INV_WIDTH 1 /* MUTERPGA2INV */ +#define WM8983_INVROUT2 0x0010 /* INVROUT2 */ +#define WM8983_INVROUT2_MASK 0x0010 /* INVROUT2 */ +#define WM8983_INVROUT2_SHIFT 4 /* INVROUT2 */ +#define WM8983_INVROUT2_WIDTH 1 /* INVROUT2 */ +#define WM8983_BEEPVOL_MASK 0x000E /* BEEPVOL - [3:1] */ +#define WM8983_BEEPVOL_SHIFT 1 /* BEEPVOL - [3:1] */ +#define WM8983_BEEPVOL_WIDTH 3 /* BEEPVOL - [3:1] */ +#define WM8983_BEEPEN 0x0001 /* BEEPEN */ +#define WM8983_BEEPEN_MASK 0x0001 /* BEEPEN */ +#define WM8983_BEEPEN_SHIFT 0 /* BEEPEN */ +#define WM8983_BEEPEN_WIDTH 1 /* BEEPEN */ + +/* + * R44 (0x2C) - Input ctrl + */ +#define WM8983_MBVSEL 0x0100 /* MBVSEL */ +#define WM8983_MBVSEL_MASK 0x0100 /* MBVSEL */ +#define WM8983_MBVSEL_SHIFT 8 /* MBVSEL */ +#define WM8983_MBVSEL_WIDTH 1 /* MBVSEL */ +#define WM8983_R2_2INPPGA 0x0040 /* R2_2INPPGA */ +#define WM8983_R2_2INPPGA_MASK 0x0040 /* R2_2INPPGA */ +#define WM8983_R2_2INPPGA_SHIFT 6 /* R2_2INPPGA */ +#define WM8983_R2_2INPPGA_WIDTH 1 /* R2_2INPPGA */ +#define WM8983_RIN2INPPGA 0x0020 /* RIN2INPPGA */ +#define WM8983_RIN2INPPGA_MASK 0x0020 /* RIN2INPPGA */ +#define WM8983_RIN2INPPGA_SHIFT 5 /* RIN2INPPGA */ +#define WM8983_RIN2INPPGA_WIDTH 1 /* RIN2INPPGA */ +#define WM8983_RIP2INPPGA 0x0010 /* RIP2INPPGA */ +#define WM8983_RIP2INPPGA_MASK 0x0010 /* RIP2INPPGA */ +#define WM8983_RIP2INPPGA_SHIFT 4 /* RIP2INPPGA */ +#define WM8983_RIP2INPPGA_WIDTH 1 /* RIP2INPPGA */ +#define WM8983_L2_2INPPGA 0x0004 /* L2_2INPPGA */ +#define WM8983_L2_2INPPGA_MASK 0x0004 /* L2_2INPPGA */ +#define WM8983_L2_2INPPGA_SHIFT 2 /* L2_2INPPGA */ +#define WM8983_L2_2INPPGA_WIDTH 1 /* L2_2INPPGA */ +#define WM8983_LIN2INPPGA 0x0002 /* LIN2INPPGA */ +#define WM8983_LIN2INPPGA_MASK 0x0002 /* LIN2INPPGA */ +#define WM8983_LIN2INPPGA_SHIFT 1 /* LIN2INPPGA */ +#define WM8983_LIN2INPPGA_WIDTH 1 /* LIN2INPPGA */ +#define WM8983_LIP2INPPGA 0x0001 /* LIP2INPPGA */ +#define WM8983_LIP2INPPGA_MASK 0x0001 /* LIP2INPPGA */ +#define WM8983_LIP2INPPGA_SHIFT 0 /* LIP2INPPGA */ +#define WM8983_LIP2INPPGA_WIDTH 1 /* LIP2INPPGA */ + +/* + * R45 (0x2D) - Left INP PGA gain ctrl + */ +#define WM8983_INPGAVU 0x0100 /* INPGAVU */ +#define WM8983_INPGAVU_MASK 0x0100 /* INPGAVU */ +#define WM8983_INPGAVU_SHIFT 8 /* INPGAVU */ +#define WM8983_INPGAVU_WIDTH 1 /* INPGAVU */ +#define WM8983_INPPGAZCL 0x0080 /* INPPGAZCL */ +#define WM8983_INPPGAZCL_MASK 0x0080 /* INPPGAZCL */ +#define WM8983_INPPGAZCL_SHIFT 7 /* INPPGAZCL */ +#define WM8983_INPPGAZCL_WIDTH 1 /* INPPGAZCL */ +#define WM8983_INPPGAMUTEL 0x0040 /* INPPGAMUTEL */ +#define WM8983_INPPGAMUTEL_MASK 0x0040 /* INPPGAMUTEL */ +#define WM8983_INPPGAMUTEL_SHIFT 6 /* INPPGAMUTEL */ +#define WM8983_INPPGAMUTEL_WIDTH 1 /* INPPGAMUTEL */ +#define WM8983_INPPGAVOLL_MASK 0x003F /* INPPGAVOLL - [5:0] */ +#define WM8983_INPPGAVOLL_SHIFT 0 /* INPPGAVOLL - [5:0] */ +#define WM8983_INPPGAVOLL_WIDTH 6 /* INPPGAVOLL - [5:0] */ + +/* + * R46 (0x2E) - Right INP PGA gain ctrl + */ +#define WM8983_INPGAVU 0x0100 /* INPGAVU */ +#define WM8983_INPGAVU_MASK 0x0100 /* INPGAVU */ +#define WM8983_INPGAVU_SHIFT 8 /* INPGAVU */ +#define WM8983_INPGAVU_WIDTH 1 /* INPGAVU */ +#define WM8983_INPPGAZCR 0x0080 /* INPPGAZCR */ +#define WM8983_INPPGAZCR_MASK 0x0080 /* INPPGAZCR */ +#define WM8983_INPPGAZCR_SHIFT 7 /* INPPGAZCR */ +#define WM8983_INPPGAZCR_WIDTH 1 /* INPPGAZCR */ +#define WM8983_INPPGAMUTER 0x0040 /* INPPGAMUTER */ +#define WM8983_INPPGAMUTER_MASK 0x0040 /* INPPGAMUTER */ +#define WM8983_INPPGAMUTER_SHIFT 6 /* INPPGAMUTER */ +#define WM8983_INPPGAMUTER_WIDTH 1 /* INPPGAMUTER */ +#define WM8983_INPPGAVOLR_MASK 0x003F /* INPPGAVOLR - [5:0] */ +#define WM8983_INPPGAVOLR_SHIFT 0 /* INPPGAVOLR - [5:0] */ +#define WM8983_INPPGAVOLR_WIDTH 6 /* INPPGAVOLR - [5:0] */ + +/* + * R47 (0x2F) - Left ADC BOOST ctrl + */ +#define WM8983_PGABOOSTL 0x0100 /* PGABOOSTL */ +#define WM8983_PGABOOSTL_MASK 0x0100 /* PGABOOSTL */ +#define WM8983_PGABOOSTL_SHIFT 8 /* PGABOOSTL */ +#define WM8983_PGABOOSTL_WIDTH 1 /* PGABOOSTL */ +#define WM8983_L2_2BOOSTVOL_MASK 0x0070 /* L2_2BOOSTVOL - [6:4] */ +#define WM8983_L2_2BOOSTVOL_SHIFT 4 /* L2_2BOOSTVOL - [6:4] */ +#define WM8983_L2_2BOOSTVOL_WIDTH 3 /* L2_2BOOSTVOL - [6:4] */ +#define WM8983_AUXL2BOOSTVOL_MASK 0x0007 /* AUXL2BOOSTVOL - [2:0] */ +#define WM8983_AUXL2BOOSTVOL_SHIFT 0 /* AUXL2BOOSTVOL - [2:0] */ +#define WM8983_AUXL2BOOSTVOL_WIDTH 3 /* AUXL2BOOSTVOL - [2:0] */ + +/* + * R48 (0x30) - Right ADC BOOST ctrl + */ +#define WM8983_PGABOOSTR 0x0100 /* PGABOOSTR */ +#define WM8983_PGABOOSTR_MASK 0x0100 /* PGABOOSTR */ +#define WM8983_PGABOOSTR_SHIFT 8 /* PGABOOSTR */ +#define WM8983_PGABOOSTR_WIDTH 1 /* PGABOOSTR */ +#define WM8983_R2_2BOOSTVOL_MASK 0x0070 /* R2_2BOOSTVOL - [6:4] */ +#define WM8983_R2_2BOOSTVOL_SHIFT 4 /* R2_2BOOSTVOL - [6:4] */ +#define WM8983_R2_2BOOSTVOL_WIDTH 3 /* R2_2BOOSTVOL - [6:4] */ +#define WM8983_AUXR2BOOSTVOL_MASK 0x0007 /* AUXR2BOOSTVOL - [2:0] */ +#define WM8983_AUXR2BOOSTVOL_SHIFT 0 /* AUXR2BOOSTVOL - [2:0] */ +#define WM8983_AUXR2BOOSTVOL_WIDTH 3 /* AUXR2BOOSTVOL - [2:0] */ + +/* + * R49 (0x31) - Output ctrl + */ +#define WM8983_DACL2RMIX 0x0040 /* DACL2RMIX */ +#define WM8983_DACL2RMIX_MASK 0x0040 /* DACL2RMIX */ +#define WM8983_DACL2RMIX_SHIFT 6 /* DACL2RMIX */ +#define WM8983_DACL2RMIX_WIDTH 1 /* DACL2RMIX */ +#define WM8983_DACR2LMIX 0x0020 /* DACR2LMIX */ +#define WM8983_DACR2LMIX_MASK 0x0020 /* DACR2LMIX */ +#define WM8983_DACR2LMIX_SHIFT 5 /* DACR2LMIX */ +#define WM8983_DACR2LMIX_WIDTH 1 /* DACR2LMIX */ +#define WM8983_OUT4BOOST 0x0010 /* OUT4BOOST */ +#define WM8983_OUT4BOOST_MASK 0x0010 /* OUT4BOOST */ +#define WM8983_OUT4BOOST_SHIFT 4 /* OUT4BOOST */ +#define WM8983_OUT4BOOST_WIDTH 1 /* OUT4BOOST */ +#define WM8983_OUT3BOOST 0x0008 /* OUT3BOOST */ +#define WM8983_OUT3BOOST_MASK 0x0008 /* OUT3BOOST */ +#define WM8983_OUT3BOOST_SHIFT 3 /* OUT3BOOST */ +#define WM8983_OUT3BOOST_WIDTH 1 /* OUT3BOOST */ +#define WM8983_SPKBOOST 0x0004 /* SPKBOOST */ +#define WM8983_SPKBOOST_MASK 0x0004 /* SPKBOOST */ +#define WM8983_SPKBOOST_SHIFT 2 /* SPKBOOST */ +#define WM8983_SPKBOOST_WIDTH 1 /* SPKBOOST */ +#define WM8983_TSDEN 0x0002 /* TSDEN */ +#define WM8983_TSDEN_MASK 0x0002 /* TSDEN */ +#define WM8983_TSDEN_SHIFT 1 /* TSDEN */ +#define WM8983_TSDEN_WIDTH 1 /* TSDEN */ +#define WM8983_VROI 0x0001 /* VROI */ +#define WM8983_VROI_MASK 0x0001 /* VROI */ +#define WM8983_VROI_SHIFT 0 /* VROI */ +#define WM8983_VROI_WIDTH 1 /* VROI */ + +/* + * R50 (0x32) - Left mixer ctrl + */ +#define WM8983_AUXLMIXVOL_MASK 0x01C0 /* AUXLMIXVOL - [8:6] */ +#define WM8983_AUXLMIXVOL_SHIFT 6 /* AUXLMIXVOL - [8:6] */ +#define WM8983_AUXLMIXVOL_WIDTH 3 /* AUXLMIXVOL - [8:6] */ +#define WM8983_AUXL2LMIX 0x0020 /* AUXL2LMIX */ +#define WM8983_AUXL2LMIX_MASK 0x0020 /* AUXL2LMIX */ +#define WM8983_AUXL2LMIX_SHIFT 5 /* AUXL2LMIX */ +#define WM8983_AUXL2LMIX_WIDTH 1 /* AUXL2LMIX */ +#define WM8983_BYPLMIXVOL_MASK 0x001C /* BYPLMIXVOL - [4:2] */ +#define WM8983_BYPLMIXVOL_SHIFT 2 /* BYPLMIXVOL - [4:2] */ +#define WM8983_BYPLMIXVOL_WIDTH 3 /* BYPLMIXVOL - [4:2] */ +#define WM8983_BYPL2LMIX 0x0002 /* BYPL2LMIX */ +#define WM8983_BYPL2LMIX_MASK 0x0002 /* BYPL2LMIX */ +#define WM8983_BYPL2LMIX_SHIFT 1 /* BYPL2LMIX */ +#define WM8983_BYPL2LMIX_WIDTH 1 /* BYPL2LMIX */ +#define WM8983_DACL2LMIX 0x0001 /* DACL2LMIX */ +#define WM8983_DACL2LMIX_MASK 0x0001 /* DACL2LMIX */ +#define WM8983_DACL2LMIX_SHIFT 0 /* DACL2LMIX */ +#define WM8983_DACL2LMIX_WIDTH 1 /* DACL2LMIX */ + +/* + * R51 (0x33) - Right mixer ctrl + */ +#define WM8983_AUXRMIXVOL_MASK 0x01C0 /* AUXRMIXVOL - [8:6] */ +#define WM8983_AUXRMIXVOL_SHIFT 6 /* AUXRMIXVOL - [8:6] */ +#define WM8983_AUXRMIXVOL_WIDTH 3 /* AUXRMIXVOL - [8:6] */ +#define WM8983_AUXR2RMIX 0x0020 /* AUXR2RMIX */ +#define WM8983_AUXR2RMIX_MASK 0x0020 /* AUXR2RMIX */ +#define WM8983_AUXR2RMIX_SHIFT 5 /* AUXR2RMIX */ +#define WM8983_AUXR2RMIX_WIDTH 1 /* AUXR2RMIX */ +#define WM8983_BYPRMIXVOL_MASK 0x001C /* BYPRMIXVOL - [4:2] */ +#define WM8983_BYPRMIXVOL_SHIFT 2 /* BYPRMIXVOL - [4:2] */ +#define WM8983_BYPRMIXVOL_WIDTH 3 /* BYPRMIXVOL - [4:2] */ +#define WM8983_BYPR2RMIX 0x0002 /* BYPR2RMIX */ +#define WM8983_BYPR2RMIX_MASK 0x0002 /* BYPR2RMIX */ +#define WM8983_BYPR2RMIX_SHIFT 1 /* BYPR2RMIX */ +#define WM8983_BYPR2RMIX_WIDTH 1 /* BYPR2RMIX */ +#define WM8983_DACR2RMIX 0x0001 /* DACR2RMIX */ +#define WM8983_DACR2RMIX_MASK 0x0001 /* DACR2RMIX */ +#define WM8983_DACR2RMIX_SHIFT 0 /* DACR2RMIX */ +#define WM8983_DACR2RMIX_WIDTH 1 /* DACR2RMIX */ + +/* + * R52 (0x34) - LOUT1 (HP) volume ctrl + */ +#define WM8983_OUT1VU 0x0100 /* OUT1VU */ +#define WM8983_OUT1VU_MASK 0x0100 /* OUT1VU */ +#define WM8983_OUT1VU_SHIFT 8 /* OUT1VU */ +#define WM8983_OUT1VU_WIDTH 1 /* OUT1VU */ +#define WM8983_LOUT1ZC 0x0080 /* LOUT1ZC */ +#define WM8983_LOUT1ZC_MASK 0x0080 /* LOUT1ZC */ +#define WM8983_LOUT1ZC_SHIFT 7 /* LOUT1ZC */ +#define WM8983_LOUT1ZC_WIDTH 1 /* LOUT1ZC */ +#define WM8983_LOUT1MUTE 0x0040 /* LOUT1MUTE */ +#define WM8983_LOUT1MUTE_MASK 0x0040 /* LOUT1MUTE */ +#define WM8983_LOUT1MUTE_SHIFT 6 /* LOUT1MUTE */ +#define WM8983_LOUT1MUTE_WIDTH 1 /* LOUT1MUTE */ +#define WM8983_LOUT1VOL_MASK 0x003F /* LOUT1VOL - [5:0] */ +#define WM8983_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [5:0] */ +#define WM8983_LOUT1VOL_WIDTH 6 /* LOUT1VOL - [5:0] */ + +/* + * R53 (0x35) - ROUT1 (HP) volume ctrl + */ +#define WM8983_OUT1VU 0x0100 /* OUT1VU */ +#define WM8983_OUT1VU_MASK 0x0100 /* OUT1VU */ +#define WM8983_OUT1VU_SHIFT 8 /* OUT1VU */ +#define WM8983_OUT1VU_WIDTH 1 /* OUT1VU */ +#define WM8983_ROUT1ZC 0x0080 /* ROUT1ZC */ +#define WM8983_ROUT1ZC_MASK 0x0080 /* ROUT1ZC */ +#define WM8983_ROUT1ZC_SHIFT 7 /* ROUT1ZC */ +#define WM8983_ROUT1ZC_WIDTH 1 /* ROUT1ZC */ +#define WM8983_ROUT1MUTE 0x0040 /* ROUT1MUTE */ +#define WM8983_ROUT1MUTE_MASK 0x0040 /* ROUT1MUTE */ +#define WM8983_ROUT1MUTE_SHIFT 6 /* ROUT1MUTE */ +#define WM8983_ROUT1MUTE_WIDTH 1 /* ROUT1MUTE */ +#define WM8983_ROUT1VOL_MASK 0x003F /* ROUT1VOL - [5:0] */ +#define WM8983_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [5:0] */ +#define WM8983_ROUT1VOL_WIDTH 6 /* ROUT1VOL - [5:0] */ + +/* + * R54 (0x36) - LOUT2 (SPK) volume ctrl + */ +#define WM8983_OUT2VU 0x0100 /* OUT2VU */ +#define WM8983_OUT2VU_MASK 0x0100 /* OUT2VU */ +#define WM8983_OUT2VU_SHIFT 8 /* OUT2VU */ +#define WM8983_OUT2VU_WIDTH 1 /* OUT2VU */ +#define WM8983_LOUT2ZC 0x0080 /* LOUT2ZC */ +#define WM8983_LOUT2ZC_MASK 0x0080 /* LOUT2ZC */ +#define WM8983_LOUT2ZC_SHIFT 7 /* LOUT2ZC */ +#define WM8983_LOUT2ZC_WIDTH 1 /* LOUT2ZC */ +#define WM8983_LOUT2MUTE 0x0040 /* LOUT2MUTE */ +#define WM8983_LOUT2MUTE_MASK 0x0040 /* LOUT2MUTE */ +#define WM8983_LOUT2MUTE_SHIFT 6 /* LOUT2MUTE */ +#define WM8983_LOUT2MUTE_WIDTH 1 /* LOUT2MUTE */ +#define WM8983_LOUT2VOL_MASK 0x003F /* LOUT2VOL - [5:0] */ +#define WM8983_LOUT2VOL_SHIFT 0 /* LOUT2VOL - [5:0] */ +#define WM8983_LOUT2VOL_WIDTH 6 /* LOUT2VOL - [5:0] */ + +/* + * R55 (0x37) - ROUT2 (SPK) volume ctrl + */ +#define WM8983_OUT2VU 0x0100 /* OUT2VU */ +#define WM8983_OUT2VU_MASK 0x0100 /* OUT2VU */ +#define WM8983_OUT2VU_SHIFT 8 /* OUT2VU */ +#define WM8983_OUT2VU_WIDTH 1 /* OUT2VU */ +#define WM8983_ROUT2ZC 0x0080 /* ROUT2ZC */ +#define WM8983_ROUT2ZC_MASK 0x0080 /* ROUT2ZC */ +#define WM8983_ROUT2ZC_SHIFT 7 /* ROUT2ZC */ +#define WM8983_ROUT2ZC_WIDTH 1 /* ROUT2ZC */ +#define WM8983_ROUT2MUTE 0x0040 /* ROUT2MUTE */ +#define WM8983_ROUT2MUTE_MASK 0x0040 /* ROUT2MUTE */ +#define WM8983_ROUT2MUTE_SHIFT 6 /* ROUT2MUTE */ +#define WM8983_ROUT2MUTE_WIDTH 1 /* ROUT2MUTE */ +#define WM8983_ROUT2VOL_MASK 0x003F /* ROUT2VOL - [5:0] */ +#define WM8983_ROUT2VOL_SHIFT 0 /* ROUT2VOL - [5:0] */ +#define WM8983_ROUT2VOL_WIDTH 6 /* ROUT2VOL - [5:0] */ + +/* + * R56 (0x38) - OUT3 mixer ctrl + */ +#define WM8983_OUT3MUTE 0x0040 /* OUT3MUTE */ +#define WM8983_OUT3MUTE_MASK 0x0040 /* OUT3MUTE */ +#define WM8983_OUT3MUTE_SHIFT 6 /* OUT3MUTE */ +#define WM8983_OUT3MUTE_WIDTH 1 /* OUT3MUTE */ +#define WM8983_OUT4_2OUT3 0x0008 /* OUT4_2OUT3 */ +#define WM8983_OUT4_2OUT3_MASK 0x0008 /* OUT4_2OUT3 */ +#define WM8983_OUT4_2OUT3_SHIFT 3 /* OUT4_2OUT3 */ +#define WM8983_OUT4_2OUT3_WIDTH 1 /* OUT4_2OUT3 */ +#define WM8983_BYPL2OUT3 0x0004 /* BYPL2OUT3 */ +#define WM8983_BYPL2OUT3_MASK 0x0004 /* BYPL2OUT3 */ +#define WM8983_BYPL2OUT3_SHIFT 2 /* BYPL2OUT3 */ +#define WM8983_BYPL2OUT3_WIDTH 1 /* BYPL2OUT3 */ +#define WM8983_LMIX2OUT3 0x0002 /* LMIX2OUT3 */ +#define WM8983_LMIX2OUT3_MASK 0x0002 /* LMIX2OUT3 */ +#define WM8983_LMIX2OUT3_SHIFT 1 /* LMIX2OUT3 */ +#define WM8983_LMIX2OUT3_WIDTH 1 /* LMIX2OUT3 */ +#define WM8983_LDAC2OUT3 0x0001 /* LDAC2OUT3 */ +#define WM8983_LDAC2OUT3_MASK 0x0001 /* LDAC2OUT3 */ +#define WM8983_LDAC2OUT3_SHIFT 0 /* LDAC2OUT3 */ +#define WM8983_LDAC2OUT3_WIDTH 1 /* LDAC2OUT3 */ + +/* + * R57 (0x39) - OUT4 (MONO) mix ctrl + */ +#define WM8983_OUT3_2OUT4 0x0080 /* OUT3_2OUT4 */ +#define WM8983_OUT3_2OUT4_MASK 0x0080 /* OUT3_2OUT4 */ +#define WM8983_OUT3_2OUT4_SHIFT 7 /* OUT3_2OUT4 */ +#define WM8983_OUT3_2OUT4_WIDTH 1 /* OUT3_2OUT4 */ +#define WM8983_OUT4MUTE 0x0040 /* OUT4MUTE */ +#define WM8983_OUT4MUTE_MASK 0x0040 /* OUT4MUTE */ +#define WM8983_OUT4MUTE_SHIFT 6 /* OUT4MUTE */ +#define WM8983_OUT4MUTE_WIDTH 1 /* OUT4MUTE */ +#define WM8983_OUT4ATTN 0x0020 /* OUT4ATTN */ +#define WM8983_OUT4ATTN_MASK 0x0020 /* OUT4ATTN */ +#define WM8983_OUT4ATTN_SHIFT 5 /* OUT4ATTN */ +#define WM8983_OUT4ATTN_WIDTH 1 /* OUT4ATTN */ +#define WM8983_LMIX2OUT4 0x0010 /* LMIX2OUT4 */ +#define WM8983_LMIX2OUT4_MASK 0x0010 /* LMIX2OUT4 */ +#define WM8983_LMIX2OUT4_SHIFT 4 /* LMIX2OUT4 */ +#define WM8983_LMIX2OUT4_WIDTH 1 /* LMIX2OUT4 */ +#define WM8983_LDAC2OUT4 0x0008 /* LDAC2OUT4 */ +#define WM8983_LDAC2OUT4_MASK 0x0008 /* LDAC2OUT4 */ +#define WM8983_LDAC2OUT4_SHIFT 3 /* LDAC2OUT4 */ +#define WM8983_LDAC2OUT4_WIDTH 1 /* LDAC2OUT4 */ +#define WM8983_BYPR2OUT4 0x0004 /* BYPR2OUT4 */ +#define WM8983_BYPR2OUT4_MASK 0x0004 /* BYPR2OUT4 */ +#define WM8983_BYPR2OUT4_SHIFT 2 /* BYPR2OUT4 */ +#define WM8983_BYPR2OUT4_WIDTH 1 /* BYPR2OUT4 */ +#define WM8983_RMIX2OUT4 0x0002 /* RMIX2OUT4 */ +#define WM8983_RMIX2OUT4_MASK 0x0002 /* RMIX2OUT4 */ +#define WM8983_RMIX2OUT4_SHIFT 1 /* RMIX2OUT4 */ +#define WM8983_RMIX2OUT4_WIDTH 1 /* RMIX2OUT4 */ +#define WM8983_RDAC2OUT4 0x0001 /* RDAC2OUT4 */ +#define WM8983_RDAC2OUT4_MASK 0x0001 /* RDAC2OUT4 */ +#define WM8983_RDAC2OUT4_SHIFT 0 /* RDAC2OUT4 */ +#define WM8983_RDAC2OUT4_WIDTH 1 /* RDAC2OUT4 */ + +/* + * R61 (0x3D) - BIAS CTRL + */ +#define WM8983_BIASCUT 0x0100 /* BIASCUT */ +#define WM8983_BIASCUT_MASK 0x0100 /* BIASCUT */ +#define WM8983_BIASCUT_SHIFT 8 /* BIASCUT */ +#define WM8983_BIASCUT_WIDTH 1 /* BIASCUT */ +#define WM8983_HALFIPBIAS 0x0080 /* HALFIPBIAS */ +#define WM8983_HALFIPBIAS_MASK 0x0080 /* HALFIPBIAS */ +#define WM8983_HALFIPBIAS_SHIFT 7 /* HALFIPBIAS */ +#define WM8983_HALFIPBIAS_WIDTH 1 /* HALFIPBIAS */ +#define WM8983_VBBIASTST_MASK 0x0060 /* VBBIASTST - [6:5] */ +#define WM8983_VBBIASTST_SHIFT 5 /* VBBIASTST - [6:5] */ +#define WM8983_VBBIASTST_WIDTH 2 /* VBBIASTST - [6:5] */ +#define WM8983_BUFBIAS_MASK 0x0018 /* BUFBIAS - [4:3] */ +#define WM8983_BUFBIAS_SHIFT 3 /* BUFBIAS - [4:3] */ +#define WM8983_BUFBIAS_WIDTH 2 /* BUFBIAS - [4:3] */ +#define WM8983_ADCBIAS_MASK 0x0006 /* ADCBIAS - [2:1] */ +#define WM8983_ADCBIAS_SHIFT 1 /* ADCBIAS - [2:1] */ +#define WM8983_ADCBIAS_WIDTH 2 /* ADCBIAS - [2:1] */ +#define WM8983_HALFOPBIAS 0x0001 /* HALFOPBIAS */ +#define WM8983_HALFOPBIAS_MASK 0x0001 /* HALFOPBIAS */ +#define WM8983_HALFOPBIAS_SHIFT 0 /* HALFOPBIAS */ +#define WM8983_HALFOPBIAS_WIDTH 1 /* HALFOPBIAS */ + +enum clk_src { + WM8983_CLKSRC_MCLK, + WM8983_CLKSRC_PLL +}; + +#endif /* _WM8983_H */ diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c new file mode 100644 index 0000000000..8606e0752a --- /dev/null +++ b/sound/soc/codecs/wm8985.c @@ -0,0 +1,1249 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8985.c -- WM8985 / WM8758 ALSA SoC Audio driver + * + * Copyright 2010 Wolfson Microelectronics plc + * Author: Dimitris Papastamos + * + * WM8758 support: + * Copyright: 2016 Barix AG + * Author: Petr Kulhavy + * + * TODO: + * o Add OUT3/OUT4 mixer controls. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8985.h" + +#define WM8985_NUM_SUPPLIES 4 +static const char *wm8985_supply_names[WM8985_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "AVDD1", + "AVDD2" +}; + +enum wm8985_type { + WM8985, + WM8758, +}; + +static const struct reg_default wm8985_reg_defaults[] = { + { 1, 0x0000 }, /* R1 - Power management 1 */ + { 2, 0x0000 }, /* R2 - Power management 2 */ + { 3, 0x0000 }, /* R3 - Power management 3 */ + { 4, 0x0050 }, /* R4 - Audio Interface */ + { 5, 0x0000 }, /* R5 - Companding control */ + { 6, 0x0140 }, /* R6 - Clock Gen control */ + { 7, 0x0000 }, /* R7 - Additional control */ + { 8, 0x0000 }, /* R8 - GPIO Control */ + { 9, 0x0000 }, /* R9 - Jack Detect Control 1 */ + { 10, 0x0000 }, /* R10 - DAC Control */ + { 11, 0x00FF }, /* R11 - Left DAC digital Vol */ + { 12, 0x00FF }, /* R12 - Right DAC digital vol */ + { 13, 0x0000 }, /* R13 - Jack Detect Control 2 */ + { 14, 0x0100 }, /* R14 - ADC Control */ + { 15, 0x00FF }, /* R15 - Left ADC Digital Vol */ + { 16, 0x00FF }, /* R16 - Right ADC Digital Vol */ + { 18, 0x012C }, /* R18 - EQ1 - low shelf */ + { 19, 0x002C }, /* R19 - EQ2 - peak 1 */ + { 20, 0x002C }, /* R20 - EQ3 - peak 2 */ + { 21, 0x002C }, /* R21 - EQ4 - peak 3 */ + { 22, 0x002C }, /* R22 - EQ5 - high shelf */ + { 24, 0x0032 }, /* R24 - DAC Limiter 1 */ + { 25, 0x0000 }, /* R25 - DAC Limiter 2 */ + { 27, 0x0000 }, /* R27 - Notch Filter 1 */ + { 28, 0x0000 }, /* R28 - Notch Filter 2 */ + { 29, 0x0000 }, /* R29 - Notch Filter 3 */ + { 30, 0x0000 }, /* R30 - Notch Filter 4 */ + { 32, 0x0038 }, /* R32 - ALC control 1 */ + { 33, 0x000B }, /* R33 - ALC control 2 */ + { 34, 0x0032 }, /* R34 - ALC control 3 */ + { 35, 0x0000 }, /* R35 - Noise Gate */ + { 36, 0x0008 }, /* R36 - PLL N */ + { 37, 0x000C }, /* R37 - PLL K 1 */ + { 38, 0x0093 }, /* R38 - PLL K 2 */ + { 39, 0x00E9 }, /* R39 - PLL K 3 */ + { 41, 0x0000 }, /* R41 - 3D control */ + { 42, 0x0000 }, /* R42 - OUT4 to ADC */ + { 43, 0x0000 }, /* R43 - Beep control */ + { 44, 0x0033 }, /* R44 - Input ctrl */ + { 45, 0x0010 }, /* R45 - Left INP PGA gain ctrl */ + { 46, 0x0010 }, /* R46 - Right INP PGA gain ctrl */ + { 47, 0x0100 }, /* R47 - Left ADC BOOST ctrl */ + { 48, 0x0100 }, /* R48 - Right ADC BOOST ctrl */ + { 49, 0x0002 }, /* R49 - Output ctrl */ + { 50, 0x0001 }, /* R50 - Left mixer ctrl */ + { 51, 0x0001 }, /* R51 - Right mixer ctrl */ + { 52, 0x0039 }, /* R52 - LOUT1 (HP) volume ctrl */ + { 53, 0x0039 }, /* R53 - ROUT1 (HP) volume ctrl */ + { 54, 0x0039 }, /* R54 - LOUT2 (SPK) volume ctrl */ + { 55, 0x0039 }, /* R55 - ROUT2 (SPK) volume ctrl */ + { 56, 0x0001 }, /* R56 - OUT3 mixer ctrl */ + { 57, 0x0001 }, /* R57 - OUT4 (MONO) mix ctrl */ + { 60, 0x0004 }, /* R60 - OUTPUT ctrl */ + { 61, 0x0000 }, /* R61 - BIAS CTRL */ +}; + +static bool wm8985_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8985_SOFTWARE_RESET: + case WM8985_POWER_MANAGEMENT_1: + case WM8985_POWER_MANAGEMENT_2: + case WM8985_POWER_MANAGEMENT_3: + case WM8985_AUDIO_INTERFACE: + case WM8985_COMPANDING_CONTROL: + case WM8985_CLOCK_GEN_CONTROL: + case WM8985_ADDITIONAL_CONTROL: + case WM8985_GPIO_CONTROL: + case WM8985_JACK_DETECT_CONTROL_1: + case WM8985_DAC_CONTROL: + case WM8985_LEFT_DAC_DIGITAL_VOL: + case WM8985_RIGHT_DAC_DIGITAL_VOL: + case WM8985_JACK_DETECT_CONTROL_2: + case WM8985_ADC_CONTROL: + case WM8985_LEFT_ADC_DIGITAL_VOL: + case WM8985_RIGHT_ADC_DIGITAL_VOL: + case WM8985_EQ1_LOW_SHELF: + case WM8985_EQ2_PEAK_1: + case WM8985_EQ3_PEAK_2: + case WM8985_EQ4_PEAK_3: + case WM8985_EQ5_HIGH_SHELF: + case WM8985_DAC_LIMITER_1: + case WM8985_DAC_LIMITER_2: + case WM8985_NOTCH_FILTER_1: + case WM8985_NOTCH_FILTER_2: + case WM8985_NOTCH_FILTER_3: + case WM8985_NOTCH_FILTER_4: + case WM8985_ALC_CONTROL_1: + case WM8985_ALC_CONTROL_2: + case WM8985_ALC_CONTROL_3: + case WM8985_NOISE_GATE: + case WM8985_PLL_N: + case WM8985_PLL_K_1: + case WM8985_PLL_K_2: + case WM8985_PLL_K_3: + case WM8985_3D_CONTROL: + case WM8985_OUT4_TO_ADC: + case WM8985_BEEP_CONTROL: + case WM8985_INPUT_CTRL: + case WM8985_LEFT_INP_PGA_GAIN_CTRL: + case WM8985_RIGHT_INP_PGA_GAIN_CTRL: + case WM8985_LEFT_ADC_BOOST_CTRL: + case WM8985_RIGHT_ADC_BOOST_CTRL: + case WM8985_OUTPUT_CTRL0: + case WM8985_LEFT_MIXER_CTRL: + case WM8985_RIGHT_MIXER_CTRL: + case WM8985_LOUT1_HP_VOLUME_CTRL: + case WM8985_ROUT1_HP_VOLUME_CTRL: + case WM8985_LOUT2_SPK_VOLUME_CTRL: + case WM8985_ROUT2_SPK_VOLUME_CTRL: + case WM8985_OUT3_MIXER_CTRL: + case WM8985_OUT4_MONO_MIX_CTRL: + case WM8985_OUTPUT_CTRL1: + case WM8985_BIAS_CTRL: + return true; + default: + return false; + } +} + +/* + * latch bit 8 of these registers to ensure instant + * volume updates + */ +static const int volume_update_regs[] = { + WM8985_LEFT_DAC_DIGITAL_VOL, + WM8985_RIGHT_DAC_DIGITAL_VOL, + WM8985_LEFT_ADC_DIGITAL_VOL, + WM8985_RIGHT_ADC_DIGITAL_VOL, + WM8985_LOUT2_SPK_VOLUME_CTRL, + WM8985_ROUT2_SPK_VOLUME_CTRL, + WM8985_LOUT1_HP_VOLUME_CTRL, + WM8985_ROUT1_HP_VOLUME_CTRL, + WM8985_LEFT_INP_PGA_GAIN_CTRL, + WM8985_RIGHT_INP_PGA_GAIN_CTRL +}; + +struct wm8985_priv { + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8985_NUM_SUPPLIES]; + enum wm8985_type dev_type; + unsigned int sysclk; + unsigned int bclk; +}; + +static const struct { + int div; + int ratio; +} fs_ratios[] = { + { 10, 128 }, + { 15, 192 }, + { 20, 256 }, + { 30, 384 }, + { 40, 512 }, + { 60, 768 }, + { 80, 1024 }, + { 120, 1536 } +}; + +static const int srates[] = { 48000, 32000, 24000, 16000, 12000, 8000 }; + +static const int bclk_divs[] = { + 1, 2, 4, 8, 16, 32 +}; + +static int eqmode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +static int eqmode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -12700, 50, 1); +static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(lim_thresh_tlv, -600, 100, 0); +static const DECLARE_TLV_DB_SCALE(lim_boost_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(alc_min_tlv, -1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(alc_max_tlv, -675, 600, 0); +static const DECLARE_TLV_DB_SCALE(alc_tar_tlv, -2250, 150, 0); +static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_tlv, -1200, 300, 1); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(aux_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0); + +static const char *alc_sel_text[] = { "Off", "Right", "Left", "Stereo" }; +static SOC_ENUM_SINGLE_DECL(alc_sel, WM8985_ALC_CONTROL_1, 7, alc_sel_text); + +static const char *alc_mode_text[] = { "ALC", "Limiter" }; +static SOC_ENUM_SINGLE_DECL(alc_mode, WM8985_ALC_CONTROL_3, 8, alc_mode_text); + +static const char *filter_mode_text[] = { "Audio", "Application" }; +static SOC_ENUM_SINGLE_DECL(filter_mode, WM8985_ADC_CONTROL, 7, + filter_mode_text); + +static const char *eq_bw_text[] = { "Narrow", "Wide" }; +static const char *eqmode_text[] = { "Capture", "Playback" }; +static SOC_ENUM_SINGLE_EXT_DECL(eqmode, eqmode_text); + +static const char *eq1_cutoff_text[] = { + "80Hz", "105Hz", "135Hz", "175Hz" +}; +static SOC_ENUM_SINGLE_DECL(eq1_cutoff, WM8985_EQ1_LOW_SHELF, 5, + eq1_cutoff_text); +static const char *eq2_cutoff_text[] = { + "230Hz", "300Hz", "385Hz", "500Hz" +}; +static SOC_ENUM_SINGLE_DECL(eq2_bw, WM8985_EQ2_PEAK_1, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq2_cutoff, WM8985_EQ2_PEAK_1, 5, eq2_cutoff_text); +static const char *eq3_cutoff_text[] = { + "650Hz", "850Hz", "1.1kHz", "1.4kHz" +}; +static SOC_ENUM_SINGLE_DECL(eq3_bw, WM8985_EQ3_PEAK_2, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq3_cutoff, WM8985_EQ3_PEAK_2, 5, + eq3_cutoff_text); +static const char *eq4_cutoff_text[] = { + "1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" +}; +static SOC_ENUM_SINGLE_DECL(eq4_bw, WM8985_EQ4_PEAK_3, 8, eq_bw_text); +static SOC_ENUM_SINGLE_DECL(eq4_cutoff, WM8985_EQ4_PEAK_3, 5, eq4_cutoff_text); +static const char *eq5_cutoff_text[] = { + "5.3kHz", "6.9kHz", "9kHz", "11.7kHz" +}; +static SOC_ENUM_SINGLE_DECL(eq5_cutoff, WM8985_EQ5_HIGH_SHELF, 5, + eq5_cutoff_text); + +static const char *speaker_mode_text[] = { "Class A/B", "Class D" }; +static SOC_ENUM_SINGLE_DECL(speaker_mode, 0x17, 8, speaker_mode_text); + +static const char *depth_3d_text[] = { + "Off", + "6.67%", + "13.3%", + "20%", + "26.7%", + "33.3%", + "40%", + "46.6%", + "53.3%", + "60%", + "66.7%", + "73.3%", + "80%", + "86.7%", + "93.3%", + "100%" +}; +static SOC_ENUM_SINGLE_DECL(depth_3d, WM8985_3D_CONTROL, 0, depth_3d_text); + +static const struct snd_kcontrol_new wm8985_common_snd_controls[] = { + SOC_SINGLE("Digital Loopback Switch", WM8985_COMPANDING_CONTROL, + 0, 1, 0), + + SOC_ENUM("ALC Capture Function", alc_sel), + SOC_SINGLE_TLV("ALC Capture Max Volume", WM8985_ALC_CONTROL_1, + 3, 7, 0, alc_max_tlv), + SOC_SINGLE_TLV("ALC Capture Min Volume", WM8985_ALC_CONTROL_1, + 0, 7, 0, alc_min_tlv), + SOC_SINGLE_TLV("ALC Capture Target Volume", WM8985_ALC_CONTROL_2, + 0, 15, 0, alc_tar_tlv), + SOC_SINGLE("ALC Capture Attack", WM8985_ALC_CONTROL_3, 0, 10, 0), + SOC_SINGLE("ALC Capture Hold", WM8985_ALC_CONTROL_2, 4, 10, 0), + SOC_SINGLE("ALC Capture Decay", WM8985_ALC_CONTROL_3, 4, 10, 0), + SOC_ENUM("ALC Mode", alc_mode), + SOC_SINGLE("ALC Capture NG Switch", WM8985_NOISE_GATE, + 3, 1, 0), + SOC_SINGLE("ALC Capture NG Threshold", WM8985_NOISE_GATE, + 0, 7, 1), + + SOC_DOUBLE_R_TLV("Capture Volume", WM8985_LEFT_ADC_DIGITAL_VOL, + WM8985_RIGHT_ADC_DIGITAL_VOL, 0, 255, 0, adc_tlv), + SOC_DOUBLE_R("Capture PGA ZC Switch", WM8985_LEFT_INP_PGA_GAIN_CTRL, + WM8985_RIGHT_INP_PGA_GAIN_CTRL, 7, 1, 0), + SOC_DOUBLE_R_TLV("Capture PGA Volume", WM8985_LEFT_INP_PGA_GAIN_CTRL, + WM8985_RIGHT_INP_PGA_GAIN_CTRL, 0, 63, 0, pga_vol_tlv), + + SOC_DOUBLE_R_TLV("Capture PGA Boost Volume", + WM8985_LEFT_ADC_BOOST_CTRL, WM8985_RIGHT_ADC_BOOST_CTRL, + 8, 1, 0, pga_boost_tlv), + + SOC_DOUBLE("ADC Inversion Switch", WM8985_ADC_CONTROL, 0, 1, 1, 0), + SOC_SINGLE("ADC 128x Oversampling Switch", WM8985_ADC_CONTROL, 8, 1, 0), + + SOC_DOUBLE_R_TLV("Playback Volume", WM8985_LEFT_DAC_DIGITAL_VOL, + WM8985_RIGHT_DAC_DIGITAL_VOL, 0, 255, 0, dac_tlv), + + SOC_SINGLE("DAC Playback Limiter Switch", WM8985_DAC_LIMITER_1, 8, 1, 0), + SOC_SINGLE("DAC Playback Limiter Decay", WM8985_DAC_LIMITER_1, 4, 10, 0), + SOC_SINGLE("DAC Playback Limiter Attack", WM8985_DAC_LIMITER_1, 0, 11, 0), + SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8985_DAC_LIMITER_2, + 4, 7, 1, lim_thresh_tlv), + SOC_SINGLE_TLV("DAC Playback Limiter Boost Volume", WM8985_DAC_LIMITER_2, + 0, 12, 0, lim_boost_tlv), + SOC_DOUBLE("DAC Inversion Switch", WM8985_DAC_CONTROL, 0, 1, 1, 0), + SOC_SINGLE("DAC Auto Mute Switch", WM8985_DAC_CONTROL, 2, 1, 0), + SOC_SINGLE("DAC 128x Oversampling Switch", WM8985_DAC_CONTROL, 3, 1, 0), + + SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8985_LOUT1_HP_VOLUME_CTRL, + WM8985_ROUT1_HP_VOLUME_CTRL, 0, 63, 0, out_tlv), + SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8985_LOUT1_HP_VOLUME_CTRL, + WM8985_ROUT1_HP_VOLUME_CTRL, 7, 1, 0), + SOC_DOUBLE_R("Headphone Switch", WM8985_LOUT1_HP_VOLUME_CTRL, + WM8985_ROUT1_HP_VOLUME_CTRL, 6, 1, 1), + + SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8985_LOUT2_SPK_VOLUME_CTRL, + WM8985_ROUT2_SPK_VOLUME_CTRL, 0, 63, 0, out_tlv), + SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8985_LOUT2_SPK_VOLUME_CTRL, + WM8985_ROUT2_SPK_VOLUME_CTRL, 7, 1, 0), + SOC_DOUBLE_R("Speaker Switch", WM8985_LOUT2_SPK_VOLUME_CTRL, + WM8985_ROUT2_SPK_VOLUME_CTRL, 6, 1, 1), + + SOC_SINGLE("High Pass Filter Switch", WM8985_ADC_CONTROL, 8, 1, 0), + SOC_ENUM("High Pass Filter Mode", filter_mode), + SOC_SINGLE("High Pass Filter Cutoff", WM8985_ADC_CONTROL, 4, 7, 0), + + SOC_DOUBLE_R_TLV("Input PGA Bypass Volume", + WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 2, 7, 0, + bypass_tlv), + + SOC_ENUM_EXT("Equalizer Function", eqmode, eqmode_get, eqmode_put), + SOC_ENUM("EQ1 Cutoff", eq1_cutoff), + SOC_SINGLE_TLV("EQ1 Volume", WM8985_EQ1_LOW_SHELF, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ2 Bandwidth", eq2_bw), + SOC_ENUM("EQ2 Cutoff", eq2_cutoff), + SOC_SINGLE_TLV("EQ2 Volume", WM8985_EQ2_PEAK_1, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ3 Bandwidth", eq3_bw), + SOC_ENUM("EQ3 Cutoff", eq3_cutoff), + SOC_SINGLE_TLV("EQ3 Volume", WM8985_EQ3_PEAK_2, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ4 Bandwidth", eq4_bw), + SOC_ENUM("EQ4 Cutoff", eq4_cutoff), + SOC_SINGLE_TLV("EQ4 Volume", WM8985_EQ4_PEAK_3, 0, 24, 1, eq_tlv), + SOC_ENUM("EQ5 Cutoff", eq5_cutoff), + SOC_SINGLE_TLV("EQ5 Volume", WM8985_EQ5_HIGH_SHELF, 0, 24, 1, eq_tlv), + + SOC_ENUM("3D Depth", depth_3d), +}; + +static const struct snd_kcontrol_new wm8985_specific_snd_controls[] = { + SOC_DOUBLE_R_TLV("Aux Bypass Volume", + WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 6, 7, 0, + aux_tlv), + + SOC_ENUM("Speaker Mode", speaker_mode) +}; + +static const struct snd_kcontrol_new left_out_mixer[] = { + SOC_DAPM_SINGLE("Line Switch", WM8985_LEFT_MIXER_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("PCM Switch", WM8985_LEFT_MIXER_CTRL, 0, 1, 0), + + /* --- WM8985 only --- */ + SOC_DAPM_SINGLE("Aux Switch", WM8985_LEFT_MIXER_CTRL, 5, 1, 0), +}; + +static const struct snd_kcontrol_new right_out_mixer[] = { + SOC_DAPM_SINGLE("Line Switch", WM8985_RIGHT_MIXER_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("PCM Switch", WM8985_RIGHT_MIXER_CTRL, 0, 1, 0), + + /* --- WM8985 only --- */ + SOC_DAPM_SINGLE("Aux Switch", WM8985_RIGHT_MIXER_CTRL, 5, 1, 0), +}; + +static const struct snd_kcontrol_new left_input_mixer[] = { + SOC_DAPM_SINGLE("L2 Switch", WM8985_INPUT_CTRL, 2, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", WM8985_INPUT_CTRL, 1, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", WM8985_INPUT_CTRL, 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_input_mixer[] = { + SOC_DAPM_SINGLE("R2 Switch", WM8985_INPUT_CTRL, 6, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", WM8985_INPUT_CTRL, 5, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", WM8985_INPUT_CTRL, 4, 1, 0), +}; + +static const struct snd_kcontrol_new left_boost_mixer[] = { + SOC_DAPM_SINGLE_TLV("L2 Volume", WM8985_LEFT_ADC_BOOST_CTRL, + 4, 7, 0, boost_tlv), + + /* --- WM8985 only --- */ + SOC_DAPM_SINGLE_TLV("AUXL Volume", WM8985_LEFT_ADC_BOOST_CTRL, + 0, 7, 0, boost_tlv) +}; + +static const struct snd_kcontrol_new right_boost_mixer[] = { + SOC_DAPM_SINGLE_TLV("R2 Volume", WM8985_RIGHT_ADC_BOOST_CTRL, + 4, 7, 0, boost_tlv), + + /* --- WM8985 only --- */ + SOC_DAPM_SINGLE_TLV("AUXR Volume", WM8985_RIGHT_ADC_BOOST_CTRL, + 0, 7, 0, boost_tlv) +}; + +static const struct snd_soc_dapm_widget wm8985_common_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8985_POWER_MANAGEMENT_3, + 0, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8985_POWER_MANAGEMENT_3, + 1, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8985_POWER_MANAGEMENT_2, + 0, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8985_POWER_MANAGEMENT_2, + 1, 0), + + SND_SOC_DAPM_MIXER("Left Input Mixer", WM8985_POWER_MANAGEMENT_2, + 2, 0, left_input_mixer, ARRAY_SIZE(left_input_mixer)), + SND_SOC_DAPM_MIXER("Right Input Mixer", WM8985_POWER_MANAGEMENT_2, + 3, 0, right_input_mixer, ARRAY_SIZE(right_input_mixer)), + + SND_SOC_DAPM_PGA("Left Capture PGA", WM8985_LEFT_INP_PGA_GAIN_CTRL, + 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right Capture PGA", WM8985_RIGHT_INP_PGA_GAIN_CTRL, + 6, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Left Headphone Out", WM8985_POWER_MANAGEMENT_2, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Headphone Out", WM8985_POWER_MANAGEMENT_2, + 8, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Left Speaker Out", WM8985_POWER_MANAGEMENT_3, + 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Speaker Out", WM8985_POWER_MANAGEMENT_3, + 6, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", WM8985_POWER_MANAGEMENT_1, 4, 0, + NULL, 0), + + SND_SOC_DAPM_INPUT("LIN"), + SND_SOC_DAPM_INPUT("LIP"), + SND_SOC_DAPM_INPUT("RIN"), + SND_SOC_DAPM_INPUT("RIP"), + SND_SOC_DAPM_INPUT("L2"), + SND_SOC_DAPM_INPUT("R2"), + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR") +}; + +static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3, + 2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)), + SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3, + 3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)), + + SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2, + 4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)), + SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2, + 5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)), + + SND_SOC_DAPM_INPUT("AUXL"), + SND_SOC_DAPM_INPUT("AUXR"), +}; + +static const struct snd_soc_dapm_widget wm8758_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3, + 2, 0, left_out_mixer, + ARRAY_SIZE(left_out_mixer) - 1), + SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3, + 3, 0, right_out_mixer, + ARRAY_SIZE(right_out_mixer) - 1), + + SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2, + 4, 0, left_boost_mixer, + ARRAY_SIZE(left_boost_mixer) - 1), + SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2, + 5, 0, right_boost_mixer, + ARRAY_SIZE(right_boost_mixer) - 1), +}; + +static const struct snd_soc_dapm_route wm8985_common_dapm_routes[] = { + { "Right Output Mixer", "PCM Switch", "Right DAC" }, + { "Right Output Mixer", "Line Switch", "Right Boost Mixer" }, + + { "Left Output Mixer", "PCM Switch", "Left DAC" }, + { "Left Output Mixer", "Line Switch", "Left Boost Mixer" }, + + { "Right Headphone Out", NULL, "Right Output Mixer" }, + { "HPR", NULL, "Right Headphone Out" }, + + { "Left Headphone Out", NULL, "Left Output Mixer" }, + { "HPL", NULL, "Left Headphone Out" }, + + { "Right Speaker Out", NULL, "Right Output Mixer" }, + { "SPKR", NULL, "Right Speaker Out" }, + + { "Left Speaker Out", NULL, "Left Output Mixer" }, + { "SPKL", NULL, "Left Speaker Out" }, + + { "Right ADC", NULL, "Right Boost Mixer" }, + + { "Right Boost Mixer", NULL, "Right Capture PGA" }, + { "Right Boost Mixer", "R2 Volume", "R2" }, + + { "Left ADC", NULL, "Left Boost Mixer" }, + + { "Left Boost Mixer", NULL, "Left Capture PGA" }, + { "Left Boost Mixer", "L2 Volume", "L2" }, + + { "Right Capture PGA", NULL, "Right Input Mixer" }, + { "Left Capture PGA", NULL, "Left Input Mixer" }, + + { "Right Input Mixer", "R2 Switch", "R2" }, + { "Right Input Mixer", "MicN Switch", "RIN" }, + { "Right Input Mixer", "MicP Switch", "RIP" }, + + { "Left Input Mixer", "L2 Switch", "L2" }, + { "Left Input Mixer", "MicN Switch", "LIN" }, + { "Left Input Mixer", "MicP Switch", "LIP" }, +}; +static const struct snd_soc_dapm_route wm8985_aux_dapm_routes[] = { + { "Right Output Mixer", "Aux Switch", "AUXR" }, + { "Left Output Mixer", "Aux Switch", "AUXL" }, + + { "Right Boost Mixer", "AUXR Volume", "AUXR" }, + { "Left Boost Mixer", "AUXL Volume", "AUXL" }, +}; + +static int wm8985_add_widgets(struct snd_soc_component *component) +{ + struct wm8985_priv *wm8985 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + switch (wm8985->dev_type) { + case WM8758: + snd_soc_dapm_new_controls(dapm, wm8758_dapm_widgets, + ARRAY_SIZE(wm8758_dapm_widgets)); + break; + + case WM8985: + snd_soc_add_component_controls(component, wm8985_specific_snd_controls, + ARRAY_SIZE(wm8985_specific_snd_controls)); + + snd_soc_dapm_new_controls(dapm, wm8985_dapm_widgets, + ARRAY_SIZE(wm8985_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, wm8985_aux_dapm_routes, + ARRAY_SIZE(wm8985_aux_dapm_routes)); + break; + } + + return 0; +} + +static int eqmode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg; + + reg = snd_soc_component_read(component, WM8985_EQ1_LOW_SHELF); + if (reg & WM8985_EQ3DMODE) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + return 0; +} + +static int eqmode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int regpwr2, regpwr3; + unsigned int reg_eq; + + if (ucontrol->value.enumerated.item[0] != 0 + && ucontrol->value.enumerated.item[0] != 1) + return -EINVAL; + + reg_eq = snd_soc_component_read(component, WM8985_EQ1_LOW_SHELF); + switch ((reg_eq & WM8985_EQ3DMODE) >> WM8985_EQ3DMODE_SHIFT) { + case 0: + if (!ucontrol->value.enumerated.item[0]) + return 0; + break; + case 1: + if (ucontrol->value.enumerated.item[0]) + return 0; + break; + } + + regpwr2 = snd_soc_component_read(component, WM8985_POWER_MANAGEMENT_2); + regpwr3 = snd_soc_component_read(component, WM8985_POWER_MANAGEMENT_3); + /* disable the DACs and ADCs */ + snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_2, + WM8985_ADCENR_MASK | WM8985_ADCENL_MASK, 0); + snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_3, + WM8985_DACENR_MASK | WM8985_DACENL_MASK, 0); + snd_soc_component_update_bits(component, WM8985_ADDITIONAL_CONTROL, + WM8985_M128ENB_MASK, WM8985_M128ENB); + /* set the desired eqmode */ + snd_soc_component_update_bits(component, WM8985_EQ1_LOW_SHELF, + WM8985_EQ3DMODE_MASK, + ucontrol->value.enumerated.item[0] + << WM8985_EQ3DMODE_SHIFT); + /* restore DAC/ADC configuration */ + snd_soc_component_write(component, WM8985_POWER_MANAGEMENT_2, regpwr2); + snd_soc_component_write(component, WM8985_POWER_MANAGEMENT_3, regpwr3); + return 0; +} + +static int wm8985_reset(struct snd_soc_component *component) +{ + return snd_soc_component_write(component, WM8985_SOFTWARE_RESET, 0x0); +} + +static int wm8985_dac_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + + return snd_soc_component_update_bits(component, WM8985_DAC_CONTROL, + WM8985_SOFTMUTE_MASK, + !!mute << WM8985_SOFTMUTE_SHIFT); +} + +static int wm8985_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component; + u16 format, master, bcp, lrp; + + component = dai->component; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + format = 0x0; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = 0x1; + break; + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + format = 0x3; + break; + default: + dev_err(dai->dev, "Unknown dai format\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8985_AUDIO_INTERFACE, + WM8985_FMT_MASK, format << WM8985_FMT_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + master = 0; + break; + default: + dev_err(dai->dev, "Unknown master/slave configuration\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL, + WM8985_MS_MASK, master << WM8985_MS_SHIFT); + + /* frame inversion is not valid for dsp modes */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + case SND_SOC_DAIFMT_NB_IF: + return -EINVAL; + default: + break; + } + break; + default: + break; + } + + bcp = lrp = 0; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + bcp = lrp = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + bcp = 1; + break; + case SND_SOC_DAIFMT_NB_IF: + lrp = 1; + break; + default: + dev_err(dai->dev, "Unknown polarity configuration\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8985_AUDIO_INTERFACE, + WM8985_LRP_MASK, lrp << WM8985_LRP_SHIFT); + snd_soc_component_update_bits(component, WM8985_AUDIO_INTERFACE, + WM8985_BCP_MASK, bcp << WM8985_BCP_SHIFT); + return 0; +} + +static int wm8985_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int i; + struct snd_soc_component *component; + struct wm8985_priv *wm8985; + u16 blen, srate_idx; + unsigned int tmp; + int srate_best; + + component = dai->component; + wm8985 = snd_soc_component_get_drvdata(component); + + wm8985->bclk = snd_soc_params_to_bclk(params); + if ((int)wm8985->bclk < 0) + return wm8985->bclk; + + switch (params_width(params)) { + case 16: + blen = 0x0; + break; + case 20: + blen = 0x1; + break; + case 24: + blen = 0x2; + break; + case 32: + blen = 0x3; + break; + default: + dev_err(dai->dev, "Unsupported word length %u\n", + params_width(params)); + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8985_AUDIO_INTERFACE, + WM8985_WL_MASK, blen << WM8985_WL_SHIFT); + + /* + * match to the nearest possible sample rate and rely + * on the array index to configure the SR register + */ + srate_idx = 0; + srate_best = abs(srates[0] - params_rate(params)); + for (i = 1; i < ARRAY_SIZE(srates); ++i) { + if (abs(srates[i] - params_rate(params)) >= srate_best) + continue; + srate_idx = i; + srate_best = abs(srates[i] - params_rate(params)); + } + + dev_dbg(dai->dev, "Selected SRATE = %d\n", srates[srate_idx]); + snd_soc_component_update_bits(component, WM8985_ADDITIONAL_CONTROL, + WM8985_SR_MASK, srate_idx << WM8985_SR_SHIFT); + + dev_dbg(dai->dev, "Target BCLK = %uHz\n", wm8985->bclk); + dev_dbg(dai->dev, "SYSCLK = %uHz\n", wm8985->sysclk); + + for (i = 0; i < ARRAY_SIZE(fs_ratios); ++i) { + if (wm8985->sysclk / params_rate(params) + == fs_ratios[i].ratio) + break; + } + + if (i == ARRAY_SIZE(fs_ratios)) { + dev_err(dai->dev, "Unable to configure MCLK ratio %u/%u\n", + wm8985->sysclk, params_rate(params)); + return -EINVAL; + } + + dev_dbg(dai->dev, "MCLK ratio = %dfs\n", fs_ratios[i].ratio); + snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL, + WM8985_MCLKDIV_MASK, i << WM8985_MCLKDIV_SHIFT); + + /* select the appropriate bclk divider */ + tmp = (wm8985->sysclk / fs_ratios[i].div) * 10; + for (i = 0; i < ARRAY_SIZE(bclk_divs); ++i) { + if (wm8985->bclk == tmp / bclk_divs[i]) + break; + } + + if (i == ARRAY_SIZE(bclk_divs)) { + dev_err(dai->dev, "No matching BCLK divider found\n"); + return -EINVAL; + } + + dev_dbg(dai->dev, "BCLK div = %d\n", i); + snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL, + WM8985_BCLKDIV_MASK, i << WM8985_BCLKDIV_SHIFT); + return 0; +} + +struct pll_div { + u32 div2:1; + u32 n:4; + u32 k:24; +}; + +#define FIXED_PLL_SIZE ((1ULL << 24) * 10) +static int pll_factors(struct pll_div *pll_div, unsigned int target, + unsigned int source) +{ + u64 Kpart; + unsigned long int K, Ndiv, Nmod; + + pll_div->div2 = 0; + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div->div2 = 1; + Ndiv = target / source; + } + + if (Ndiv < 6 || Ndiv > 12) { + printk(KERN_ERR "%s: WM8985 N value is not within" + " the recommended range: %lu\n", __func__, Ndiv); + return -EINVAL; + } + pll_div->n = Ndiv; + + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (u64)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xffffffff; + if ((K % 10) >= 5) + K += 5; + K /= 10; + pll_div->k = K; + + return 0; +} + +static int wm8985_set_pll(struct snd_soc_dai *dai, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) +{ + int ret; + struct snd_soc_component *component; + struct pll_div pll_div; + + component = dai->component; + if (!freq_in || !freq_out) { + /* disable the PLL */ + snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1, + WM8985_PLLEN_MASK, 0); + } else { + ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in); + if (ret) + return ret; + + /* set PLLN and PRESCALE */ + snd_soc_component_write(component, WM8985_PLL_N, + (pll_div.div2 << WM8985_PLL_PRESCALE_SHIFT) + | pll_div.n); + /* set PLLK */ + snd_soc_component_write(component, WM8985_PLL_K_3, pll_div.k & 0x1ff); + snd_soc_component_write(component, WM8985_PLL_K_2, (pll_div.k >> 9) & 0x1ff); + snd_soc_component_write(component, WM8985_PLL_K_1, (pll_div.k >> 18)); + /* set the source of the clock to be the PLL */ + snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL, + WM8985_CLKSEL_MASK, WM8985_CLKSEL); + /* enable the PLL */ + snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1, + WM8985_PLLEN_MASK, WM8985_PLLEN); + } + return 0; +} + +static int wm8985_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component; + struct wm8985_priv *wm8985; + + component = dai->component; + wm8985 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case WM8985_CLKSRC_MCLK: + snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL, + WM8985_CLKSEL_MASK, 0); + snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1, + WM8985_PLLEN_MASK, 0); + break; + case WM8985_CLKSRC_PLL: + snd_soc_component_update_bits(component, WM8985_CLOCK_GEN_CONTROL, + WM8985_CLKSEL_MASK, WM8985_CLKSEL); + break; + default: + dev_err(dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } + + wm8985->sysclk = freq; + return 0; +} + +static int wm8985_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int ret; + struct wm8985_priv *wm8985; + + wm8985 = snd_soc_component_get_drvdata(component); + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + /* VMID at 75k */ + snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1, + WM8985_VMIDSEL_MASK, + 1 << WM8985_VMIDSEL_SHIFT); + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies), + wm8985->supplies); + if (ret) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + + regcache_sync(wm8985->regmap); + + /* enable anti-pop features */ + snd_soc_component_update_bits(component, WM8985_OUT4_TO_ADC, + WM8985_POBCTRL_MASK, + WM8985_POBCTRL); + /* enable thermal shutdown */ + snd_soc_component_update_bits(component, WM8985_OUTPUT_CTRL0, + WM8985_TSDEN_MASK, WM8985_TSDEN); + snd_soc_component_update_bits(component, WM8985_OUTPUT_CTRL0, + WM8985_TSOPCTRL_MASK, + WM8985_TSOPCTRL); + /* enable BIASEN */ + snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1, + WM8985_BIASEN_MASK, WM8985_BIASEN); + /* VMID at 75k */ + snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1, + WM8985_VMIDSEL_MASK, + 1 << WM8985_VMIDSEL_SHIFT); + msleep(500); + /* disable anti-pop features */ + snd_soc_component_update_bits(component, WM8985_OUT4_TO_ADC, + WM8985_POBCTRL_MASK, 0); + } + /* VMID at 300k */ + snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1, + WM8985_VMIDSEL_MASK, + 2 << WM8985_VMIDSEL_SHIFT); + break; + case SND_SOC_BIAS_OFF: + /* disable thermal shutdown */ + snd_soc_component_update_bits(component, WM8985_OUTPUT_CTRL0, + WM8985_TSOPCTRL_MASK, 0); + snd_soc_component_update_bits(component, WM8985_OUTPUT_CTRL0, + WM8985_TSDEN_MASK, 0); + /* disable VMIDSEL and BIASEN */ + snd_soc_component_update_bits(component, WM8985_POWER_MANAGEMENT_1, + WM8985_VMIDSEL_MASK | WM8985_BIASEN_MASK, + 0); + snd_soc_component_write(component, WM8985_POWER_MANAGEMENT_1, 0); + snd_soc_component_write(component, WM8985_POWER_MANAGEMENT_2, 0); + snd_soc_component_write(component, WM8985_POWER_MANAGEMENT_3, 0); + + regcache_mark_dirty(wm8985->regmap); + + regulator_bulk_disable(ARRAY_SIZE(wm8985->supplies), + wm8985->supplies); + break; + } + + return 0; +} + +static int wm8985_probe(struct snd_soc_component *component) +{ + size_t i; + struct wm8985_priv *wm8985; + int ret; + + wm8985 = snd_soc_component_get_drvdata(component); + + for (i = 0; i < ARRAY_SIZE(wm8985->supplies); i++) + wm8985->supplies[i].supply = wm8985_supply_names[i]; + + ret = devm_regulator_bulk_get(component->dev, ARRAY_SIZE(wm8985->supplies), + wm8985->supplies); + if (ret) { + dev_err(component->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies), + wm8985->supplies); + if (ret) { + dev_err(component->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = wm8985_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset: %d\n", ret); + goto err_reg_enable; + } + + /* latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(volume_update_regs); ++i) + snd_soc_component_update_bits(component, volume_update_regs[i], + 0x100, 0x100); + /* enable BIASCUT */ + snd_soc_component_update_bits(component, WM8985_BIAS_CTRL, WM8985_BIASCUT, + WM8985_BIASCUT); + + wm8985_add_widgets(component); + + return 0; + +err_reg_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8985->supplies), wm8985->supplies); + return ret; +} + +static const struct snd_soc_dai_ops wm8985_dai_ops = { + .mute_stream = wm8985_dac_mute, + .hw_params = wm8985_hw_params, + .set_fmt = wm8985_set_fmt, + .set_sysclk = wm8985_set_sysclk, + .set_pll = wm8985_set_pll, + .no_capture_mute = 1, +}; + +#define WM8985_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8985_dai = { + .name = "wm8985-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8985_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8985_FORMATS, + }, + .ops = &wm8985_dai_ops, + .symmetric_rate = 1 +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8985 = { + .probe = wm8985_probe, + .set_bias_level = wm8985_set_bias_level, + .controls = wm8985_common_snd_controls, + .num_controls = ARRAY_SIZE(wm8985_common_snd_controls), + .dapm_widgets = wm8985_common_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8985_common_dapm_widgets), + .dapm_routes = wm8985_common_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8985_common_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8985_regmap = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = WM8985_MAX_REGISTER, + .writeable_reg = wm8985_writeable, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm8985_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8985_reg_defaults), +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8985_spi_probe(struct spi_device *spi) +{ + struct wm8985_priv *wm8985; + int ret; + + wm8985 = devm_kzalloc(&spi->dev, sizeof *wm8985, GFP_KERNEL); + if (!wm8985) + return -ENOMEM; + + spi_set_drvdata(spi, wm8985); + + wm8985->dev_type = WM8985; + + wm8985->regmap = devm_regmap_init_spi(spi, &wm8985_regmap); + if (IS_ERR(wm8985->regmap)) { + ret = PTR_ERR(wm8985->regmap); + dev_err(&spi->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8985, &wm8985_dai, 1); + return ret; +} + +static struct spi_driver wm8985_spi_driver = { + .driver = { + .name = "wm8985", + }, + .probe = wm8985_spi_probe, +}; +#endif + +#if IS_ENABLED(CONFIG_I2C) +static const struct i2c_device_id wm8985_i2c_id[]; + +static int wm8985_i2c_probe(struct i2c_client *i2c) +{ + struct wm8985_priv *wm8985; + const struct i2c_device_id *id = i2c_match_id(wm8985_i2c_id, i2c); + int ret; + + wm8985 = devm_kzalloc(&i2c->dev, sizeof *wm8985, GFP_KERNEL); + if (!wm8985) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm8985); + + wm8985->dev_type = id->driver_data; + + wm8985->regmap = devm_regmap_init_i2c(i2c, &wm8985_regmap); + if (IS_ERR(wm8985->regmap)) { + ret = PTR_ERR(wm8985->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8985, &wm8985_dai, 1); + return ret; +} + +static const struct i2c_device_id wm8985_i2c_id[] = { + { "wm8985", WM8985 }, + { "wm8758", WM8758 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8985_i2c_id); + +static struct i2c_driver wm8985_i2c_driver = { + .driver = { + .name = "wm8985", + }, + .probe = wm8985_i2c_probe, + .id_table = wm8985_i2c_id +}; +#endif + +static int __init wm8985_modinit(void) +{ + int ret = 0; + +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8985_i2c_driver); + if (ret) { + printk(KERN_ERR "Failed to register wm8985 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8985_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register wm8985 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8985_modinit); + +static void __exit wm8985_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8985_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8985_spi_driver); +#endif +} +module_exit(wm8985_exit); + +MODULE_DESCRIPTION("ASoC WM8985 / WM8758 driver"); +MODULE_AUTHOR("Dimitris Papastamos "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8985.h b/sound/soc/codecs/wm8985.h new file mode 100644 index 0000000000..107fae9ce0 --- /dev/null +++ b/sound/soc/codecs/wm8985.h @@ -0,0 +1,1080 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8985.h -- WM8985 ASoC driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos + */ + +#ifndef _WM8985_H +#define _WM8985_H + +#define WM8985_SOFTWARE_RESET 0x00 +#define WM8985_POWER_MANAGEMENT_1 0x01 +#define WM8985_POWER_MANAGEMENT_2 0x02 +#define WM8985_POWER_MANAGEMENT_3 0x03 +#define WM8985_AUDIO_INTERFACE 0x04 +#define WM8985_COMPANDING_CONTROL 0x05 +#define WM8985_CLOCK_GEN_CONTROL 0x06 +#define WM8985_ADDITIONAL_CONTROL 0x07 +#define WM8985_GPIO_CONTROL 0x08 +#define WM8985_JACK_DETECT_CONTROL_1 0x09 +#define WM8985_DAC_CONTROL 0x0A +#define WM8985_LEFT_DAC_DIGITAL_VOL 0x0B +#define WM8985_RIGHT_DAC_DIGITAL_VOL 0x0C +#define WM8985_JACK_DETECT_CONTROL_2 0x0D +#define WM8985_ADC_CONTROL 0x0E +#define WM8985_LEFT_ADC_DIGITAL_VOL 0x0F +#define WM8985_RIGHT_ADC_DIGITAL_VOL 0x10 +#define WM8985_EQ1_LOW_SHELF 0x12 +#define WM8985_EQ2_PEAK_1 0x13 +#define WM8985_EQ3_PEAK_2 0x14 +#define WM8985_EQ4_PEAK_3 0x15 +#define WM8985_EQ5_HIGH_SHELF 0x16 +#define WM8985_DAC_LIMITER_1 0x18 +#define WM8985_DAC_LIMITER_2 0x19 +#define WM8985_NOTCH_FILTER_1 0x1B +#define WM8985_NOTCH_FILTER_2 0x1C +#define WM8985_NOTCH_FILTER_3 0x1D +#define WM8985_NOTCH_FILTER_4 0x1E +#define WM8985_ALC_CONTROL_1 0x20 +#define WM8985_ALC_CONTROL_2 0x21 +#define WM8985_ALC_CONTROL_3 0x22 +#define WM8985_NOISE_GATE 0x23 +#define WM8985_PLL_N 0x24 +#define WM8985_PLL_K_1 0x25 +#define WM8985_PLL_K_2 0x26 +#define WM8985_PLL_K_3 0x27 +#define WM8985_3D_CONTROL 0x29 +#define WM8985_OUT4_TO_ADC 0x2A +#define WM8985_BEEP_CONTROL 0x2B +#define WM8985_INPUT_CTRL 0x2C +#define WM8985_LEFT_INP_PGA_GAIN_CTRL 0x2D +#define WM8985_RIGHT_INP_PGA_GAIN_CTRL 0x2E +#define WM8985_LEFT_ADC_BOOST_CTRL 0x2F +#define WM8985_RIGHT_ADC_BOOST_CTRL 0x30 +#define WM8985_OUTPUT_CTRL0 0x31 +#define WM8985_LEFT_MIXER_CTRL 0x32 +#define WM8985_RIGHT_MIXER_CTRL 0x33 +#define WM8985_LOUT1_HP_VOLUME_CTRL 0x34 +#define WM8985_ROUT1_HP_VOLUME_CTRL 0x35 +#define WM8985_LOUT2_SPK_VOLUME_CTRL 0x36 +#define WM8985_ROUT2_SPK_VOLUME_CTRL 0x37 +#define WM8985_OUT3_MIXER_CTRL 0x38 +#define WM8985_OUT4_MONO_MIX_CTRL 0x39 +#define WM8985_OUTPUT_CTRL1 0x3C +#define WM8985_BIAS_CTRL 0x3D + +#define WM8985_REGISTER_COUNT 59 +#define WM8985_MAX_REGISTER 0x3F + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +#define WM8985_SOFTWARE_RESET_MASK 0x01FF /* SOFTWARE_RESET - [8:0] */ +#define WM8985_SOFTWARE_RESET_SHIFT 0 /* SOFTWARE_RESET - [8:0] */ +#define WM8985_SOFTWARE_RESET_WIDTH 9 /* SOFTWARE_RESET - [8:0] */ + +/* + * R1 (0x01) - Power management 1 + */ +#define WM8985_OUT4MIXEN 0x0080 /* OUT4MIXEN */ +#define WM8985_OUT4MIXEN_MASK 0x0080 /* OUT4MIXEN */ +#define WM8985_OUT4MIXEN_SHIFT 7 /* OUT4MIXEN */ +#define WM8985_OUT4MIXEN_WIDTH 1 /* OUT4MIXEN */ +#define WM8985_OUT3MIXEN 0x0040 /* OUT3MIXEN */ +#define WM8985_OUT3MIXEN_MASK 0x0040 /* OUT3MIXEN */ +#define WM8985_OUT3MIXEN_SHIFT 6 /* OUT3MIXEN */ +#define WM8985_OUT3MIXEN_WIDTH 1 /* OUT3MIXEN */ +#define WM8985_PLLEN 0x0020 /* PLLEN */ +#define WM8985_PLLEN_MASK 0x0020 /* PLLEN */ +#define WM8985_PLLEN_SHIFT 5 /* PLLEN */ +#define WM8985_PLLEN_WIDTH 1 /* PLLEN */ +#define WM8985_MICBEN 0x0010 /* MICBEN */ +#define WM8985_MICBEN_MASK 0x0010 /* MICBEN */ +#define WM8985_MICBEN_SHIFT 4 /* MICBEN */ +#define WM8985_MICBEN_WIDTH 1 /* MICBEN */ +#define WM8985_BIASEN 0x0008 /* BIASEN */ +#define WM8985_BIASEN_MASK 0x0008 /* BIASEN */ +#define WM8985_BIASEN_SHIFT 3 /* BIASEN */ +#define WM8985_BIASEN_WIDTH 1 /* BIASEN */ +#define WM8985_BUFIOEN 0x0004 /* BUFIOEN */ +#define WM8985_BUFIOEN_MASK 0x0004 /* BUFIOEN */ +#define WM8985_BUFIOEN_SHIFT 2 /* BUFIOEN */ +#define WM8985_BUFIOEN_WIDTH 1 /* BUFIOEN */ +#define WM8985_VMIDSEL 0x0003 /* VMIDSEL */ +#define WM8985_VMIDSEL_MASK 0x0003 /* VMIDSEL - [1:0] */ +#define WM8985_VMIDSEL_SHIFT 0 /* VMIDSEL - [1:0] */ +#define WM8985_VMIDSEL_WIDTH 2 /* VMIDSEL - [1:0] */ + +/* + * R2 (0x02) - Power management 2 + */ +#define WM8985_ROUT1EN 0x0100 /* ROUT1EN */ +#define WM8985_ROUT1EN_MASK 0x0100 /* ROUT1EN */ +#define WM8985_ROUT1EN_SHIFT 8 /* ROUT1EN */ +#define WM8985_ROUT1EN_WIDTH 1 /* ROUT1EN */ +#define WM8985_LOUT1EN 0x0080 /* LOUT1EN */ +#define WM8985_LOUT1EN_MASK 0x0080 /* LOUT1EN */ +#define WM8985_LOUT1EN_SHIFT 7 /* LOUT1EN */ +#define WM8985_LOUT1EN_WIDTH 1 /* LOUT1EN */ +#define WM8985_SLEEP 0x0040 /* SLEEP */ +#define WM8985_SLEEP_MASK 0x0040 /* SLEEP */ +#define WM8985_SLEEP_SHIFT 6 /* SLEEP */ +#define WM8985_SLEEP_WIDTH 1 /* SLEEP */ +#define WM8985_BOOSTENR 0x0020 /* BOOSTENR */ +#define WM8985_BOOSTENR_MASK 0x0020 /* BOOSTENR */ +#define WM8985_BOOSTENR_SHIFT 5 /* BOOSTENR */ +#define WM8985_BOOSTENR_WIDTH 1 /* BOOSTENR */ +#define WM8985_BOOSTENL 0x0010 /* BOOSTENL */ +#define WM8985_BOOSTENL_MASK 0x0010 /* BOOSTENL */ +#define WM8985_BOOSTENL_SHIFT 4 /* BOOSTENL */ +#define WM8985_BOOSTENL_WIDTH 1 /* BOOSTENL */ +#define WM8985_INPGAENR 0x0008 /* INPGAENR */ +#define WM8985_INPGAENR_MASK 0x0008 /* INPGAENR */ +#define WM8985_INPGAENR_SHIFT 3 /* INPGAENR */ +#define WM8985_INPGAENR_WIDTH 1 /* INPGAENR */ +#define WM8985_INPPGAENL 0x0004 /* INPPGAENL */ +#define WM8985_INPPGAENL_MASK 0x0004 /* INPPGAENL */ +#define WM8985_INPPGAENL_SHIFT 2 /* INPPGAENL */ +#define WM8985_INPPGAENL_WIDTH 1 /* INPPGAENL */ +#define WM8985_ADCENR 0x0002 /* ADCENR */ +#define WM8985_ADCENR_MASK 0x0002 /* ADCENR */ +#define WM8985_ADCENR_SHIFT 1 /* ADCENR */ +#define WM8985_ADCENR_WIDTH 1 /* ADCENR */ +#define WM8985_ADCENL 0x0001 /* ADCENL */ +#define WM8985_ADCENL_MASK 0x0001 /* ADCENL */ +#define WM8985_ADCENL_SHIFT 0 /* ADCENL */ +#define WM8985_ADCENL_WIDTH 1 /* ADCENL */ + +/* + * R3 (0x03) - Power management 3 + */ +#define WM8985_OUT4EN 0x0100 /* OUT4EN */ +#define WM8985_OUT4EN_MASK 0x0100 /* OUT4EN */ +#define WM8985_OUT4EN_SHIFT 8 /* OUT4EN */ +#define WM8985_OUT4EN_WIDTH 1 /* OUT4EN */ +#define WM8985_OUT3EN 0x0080 /* OUT3EN */ +#define WM8985_OUT3EN_MASK 0x0080 /* OUT3EN */ +#define WM8985_OUT3EN_SHIFT 7 /* OUT3EN */ +#define WM8985_OUT3EN_WIDTH 1 /* OUT3EN */ +#define WM8985_ROUT2EN 0x0040 /* ROUT2EN */ +#define WM8985_ROUT2EN_MASK 0x0040 /* ROUT2EN */ +#define WM8985_ROUT2EN_SHIFT 6 /* ROUT2EN */ +#define WM8985_ROUT2EN_WIDTH 1 /* ROUT2EN */ +#define WM8985_LOUT2EN 0x0020 /* LOUT2EN */ +#define WM8985_LOUT2EN_MASK 0x0020 /* LOUT2EN */ +#define WM8985_LOUT2EN_SHIFT 5 /* LOUT2EN */ +#define WM8985_LOUT2EN_WIDTH 1 /* LOUT2EN */ +#define WM8985_RMIXEN 0x0008 /* RMIXEN */ +#define WM8985_RMIXEN_MASK 0x0008 /* RMIXEN */ +#define WM8985_RMIXEN_SHIFT 3 /* RMIXEN */ +#define WM8985_RMIXEN_WIDTH 1 /* RMIXEN */ +#define WM8985_LMIXEN 0x0004 /* LMIXEN */ +#define WM8985_LMIXEN_MASK 0x0004 /* LMIXEN */ +#define WM8985_LMIXEN_SHIFT 2 /* LMIXEN */ +#define WM8985_LMIXEN_WIDTH 1 /* LMIXEN */ +#define WM8985_DACENR 0x0002 /* DACENR */ +#define WM8985_DACENR_MASK 0x0002 /* DACENR */ +#define WM8985_DACENR_SHIFT 1 /* DACENR */ +#define WM8985_DACENR_WIDTH 1 /* DACENR */ +#define WM8985_DACENL 0x0001 /* DACENL */ +#define WM8985_DACENL_MASK 0x0001 /* DACENL */ +#define WM8985_DACENL_SHIFT 0 /* DACENL */ +#define WM8985_DACENL_WIDTH 1 /* DACENL */ + +/* + * R4 (0x04) - Audio Interface + */ +#define WM8985_BCP 0x0100 /* BCP */ +#define WM8985_BCP_MASK 0x0100 /* BCP */ +#define WM8985_BCP_SHIFT 8 /* BCP */ +#define WM8985_BCP_WIDTH 1 /* BCP */ +#define WM8985_LRP 0x0080 /* LRP */ +#define WM8985_LRP_MASK 0x0080 /* LRP */ +#define WM8985_LRP_SHIFT 7 /* LRP */ +#define WM8985_LRP_WIDTH 1 /* LRP */ +#define WM8985_WL_MASK 0x0060 /* WL - [6:5] */ +#define WM8985_WL_SHIFT 5 /* WL - [6:5] */ +#define WM8985_WL_WIDTH 2 /* WL - [6:5] */ +#define WM8985_FMT_MASK 0x0018 /* FMT - [4:3] */ +#define WM8985_FMT_SHIFT 3 /* FMT - [4:3] */ +#define WM8985_FMT_WIDTH 2 /* FMT - [4:3] */ +#define WM8985_DLRSWAP 0x0004 /* DLRSWAP */ +#define WM8985_DLRSWAP_MASK 0x0004 /* DLRSWAP */ +#define WM8985_DLRSWAP_SHIFT 2 /* DLRSWAP */ +#define WM8985_DLRSWAP_WIDTH 1 /* DLRSWAP */ +#define WM8985_ALRSWAP 0x0002 /* ALRSWAP */ +#define WM8985_ALRSWAP_MASK 0x0002 /* ALRSWAP */ +#define WM8985_ALRSWAP_SHIFT 1 /* ALRSWAP */ +#define WM8985_ALRSWAP_WIDTH 1 /* ALRSWAP */ +#define WM8985_MONO 0x0001 /* MONO */ +#define WM8985_MONO_MASK 0x0001 /* MONO */ +#define WM8985_MONO_SHIFT 0 /* MONO */ +#define WM8985_MONO_WIDTH 1 /* MONO */ + +/* + * R5 (0x05) - Companding control + */ +#define WM8985_WL8 0x0020 /* WL8 */ +#define WM8985_WL8_MASK 0x0020 /* WL8 */ +#define WM8985_WL8_SHIFT 5 /* WL8 */ +#define WM8985_WL8_WIDTH 1 /* WL8 */ +#define WM8985_DAC_COMP_MASK 0x0018 /* DAC_COMP - [4:3] */ +#define WM8985_DAC_COMP_SHIFT 3 /* DAC_COMP - [4:3] */ +#define WM8985_DAC_COMP_WIDTH 2 /* DAC_COMP - [4:3] */ +#define WM8985_ADC_COMP_MASK 0x0006 /* ADC_COMP - [2:1] */ +#define WM8985_ADC_COMP_SHIFT 1 /* ADC_COMP - [2:1] */ +#define WM8985_ADC_COMP_WIDTH 2 /* ADC_COMP - [2:1] */ +#define WM8985_LOOPBACK 0x0001 /* LOOPBACK */ +#define WM8985_LOOPBACK_MASK 0x0001 /* LOOPBACK */ +#define WM8985_LOOPBACK_SHIFT 0 /* LOOPBACK */ +#define WM8985_LOOPBACK_WIDTH 1 /* LOOPBACK */ + +/* + * R6 (0x06) - Clock Gen control + */ +#define WM8985_CLKSEL 0x0100 /* CLKSEL */ +#define WM8985_CLKSEL_MASK 0x0100 /* CLKSEL */ +#define WM8985_CLKSEL_SHIFT 8 /* CLKSEL */ +#define WM8985_CLKSEL_WIDTH 1 /* CLKSEL */ +#define WM8985_MCLKDIV_MASK 0x00E0 /* MCLKDIV - [7:5] */ +#define WM8985_MCLKDIV_SHIFT 5 /* MCLKDIV - [7:5] */ +#define WM8985_MCLKDIV_WIDTH 3 /* MCLKDIV - [7:5] */ +#define WM8985_BCLKDIV_MASK 0x001C /* BCLKDIV - [4:2] */ +#define WM8985_BCLKDIV_SHIFT 2 /* BCLKDIV - [4:2] */ +#define WM8985_BCLKDIV_WIDTH 3 /* BCLKDIV - [4:2] */ +#define WM8985_MS 0x0001 /* MS */ +#define WM8985_MS_MASK 0x0001 /* MS */ +#define WM8985_MS_SHIFT 0 /* MS */ +#define WM8985_MS_WIDTH 1 /* MS */ + +/* + * R7 (0x07) - Additional control + */ +#define WM8985_M128ENB 0x0100 /* M128ENB */ +#define WM8985_M128ENB_MASK 0x0100 /* M128ENB */ +#define WM8985_M128ENB_SHIFT 8 /* M128ENB */ +#define WM8985_M128ENB_WIDTH 1 /* M128ENB */ +#define WM8985_DCLKDIV_MASK 0x00F0 /* DCLKDIV - [7:4] */ +#define WM8985_DCLKDIV_SHIFT 4 /* DCLKDIV - [7:4] */ +#define WM8985_DCLKDIV_WIDTH 4 /* DCLKDIV - [7:4] */ +#define WM8985_SR_MASK 0x000E /* SR - [3:1] */ +#define WM8985_SR_SHIFT 1 /* SR - [3:1] */ +#define WM8985_SR_WIDTH 3 /* SR - [3:1] */ +#define WM8985_SLOWCLKEN 0x0001 /* SLOWCLKEN */ +#define WM8985_SLOWCLKEN_MASK 0x0001 /* SLOWCLKEN */ +#define WM8985_SLOWCLKEN_SHIFT 0 /* SLOWCLKEN */ +#define WM8985_SLOWCLKEN_WIDTH 1 /* SLOWCLKEN */ + +/* + * R8 (0x08) - GPIO Control + */ +#define WM8985_GPIO1GP 0x0100 /* GPIO1GP */ +#define WM8985_GPIO1GP_MASK 0x0100 /* GPIO1GP */ +#define WM8985_GPIO1GP_SHIFT 8 /* GPIO1GP */ +#define WM8985_GPIO1GP_WIDTH 1 /* GPIO1GP */ +#define WM8985_GPIO1GPU 0x0080 /* GPIO1GPU */ +#define WM8985_GPIO1GPU_MASK 0x0080 /* GPIO1GPU */ +#define WM8985_GPIO1GPU_SHIFT 7 /* GPIO1GPU */ +#define WM8985_GPIO1GPU_WIDTH 1 /* GPIO1GPU */ +#define WM8985_GPIO1GPD 0x0040 /* GPIO1GPD */ +#define WM8985_GPIO1GPD_MASK 0x0040 /* GPIO1GPD */ +#define WM8985_GPIO1GPD_SHIFT 6 /* GPIO1GPD */ +#define WM8985_GPIO1GPD_WIDTH 1 /* GPIO1GPD */ +#define WM8758_OPCLKDIV_MASK 0x0030 /* OPCLKDIV - [1:0] */ +#define WM8758_OPCLKDIV_SHIFT 4 /* OPCLKDIV - [1:0] */ +#define WM8758_OPCLKDIV_WIDTH 2 /* OPCLKDIV - [1:0] */ +#define WM8985_GPIO1POL 0x0008 /* GPIO1POL */ +#define WM8985_GPIO1POL_MASK 0x0008 /* GPIO1POL */ +#define WM8985_GPIO1POL_SHIFT 3 /* GPIO1POL */ +#define WM8985_GPIO1POL_WIDTH 1 /* GPIO1POL */ +#define WM8985_GPIO1SEL_MASK 0x0007 /* GPIO1SEL - [2:0] */ +#define WM8985_GPIO1SEL_SHIFT 0 /* GPIO1SEL - [2:0] */ +#define WM8985_GPIO1SEL_WIDTH 3 /* GPIO1SEL - [2:0] */ + +/* + * R9 (0x09) - Jack Detect Control 1 + */ +#define WM8758_JD_VMID1_MASK 0x0100 /* JD_VMID1 */ +#define WM8758_JD_VMID1_SHIFT 8 /* JD_VMID1 */ +#define WM8758_JD_VMID1_WIDTH 1 /* JD_VMID1 */ +#define WM8758_JD_VMID0_MASK 0x0080 /* JD_VMID0 */ +#define WM8758_JD_VMID0_SHIFT 7 /* JD_VMID0 */ +#define WM8758_JD_VMID0_WIDTH 1 /* JD_VMID0 */ +#define WM8985_JD_EN 0x0040 /* JD_EN */ +#define WM8985_JD_EN_MASK 0x0040 /* JD_EN */ +#define WM8985_JD_EN_SHIFT 6 /* JD_EN */ +#define WM8985_JD_EN_WIDTH 1 /* JD_EN */ +#define WM8985_JD_SEL_MASK 0x0030 /* JD_SEL - [5:4] */ +#define WM8985_JD_SEL_SHIFT 4 /* JD_SEL - [5:4] */ +#define WM8985_JD_SEL_WIDTH 2 /* JD_SEL - [5:4] */ + +/* + * R10 (0x0A) - DAC Control + */ +#define WM8985_SOFTMUTE 0x0040 /* SOFTMUTE */ +#define WM8985_SOFTMUTE_MASK 0x0040 /* SOFTMUTE */ +#define WM8985_SOFTMUTE_SHIFT 6 /* SOFTMUTE */ +#define WM8985_SOFTMUTE_WIDTH 1 /* SOFTMUTE */ +#define WM8985_DACOSR128 0x0008 /* DACOSR128 */ +#define WM8985_DACOSR128_MASK 0x0008 /* DACOSR128 */ +#define WM8985_DACOSR128_SHIFT 3 /* DACOSR128 */ +#define WM8985_DACOSR128_WIDTH 1 /* DACOSR128 */ +#define WM8985_AMUTE 0x0004 /* AMUTE */ +#define WM8985_AMUTE_MASK 0x0004 /* AMUTE */ +#define WM8985_AMUTE_SHIFT 2 /* AMUTE */ +#define WM8985_AMUTE_WIDTH 1 /* AMUTE */ +#define WM8985_DACPOLR 0x0002 /* DACPOLR */ +#define WM8985_DACPOLR_MASK 0x0002 /* DACPOLR */ +#define WM8985_DACPOLR_SHIFT 1 /* DACPOLR */ +#define WM8985_DACPOLR_WIDTH 1 /* DACPOLR */ +#define WM8985_DACPOLL 0x0001 /* DACPOLL */ +#define WM8985_DACPOLL_MASK 0x0001 /* DACPOLL */ +#define WM8985_DACPOLL_SHIFT 0 /* DACPOLL */ +#define WM8985_DACPOLL_WIDTH 1 /* DACPOLL */ + +/* + * R11 (0x0B) - Left DAC digital Vol + */ +#define WM8985_DACVU 0x0100 /* DACVU */ +#define WM8985_DACVU_MASK 0x0100 /* DACVU */ +#define WM8985_DACVU_SHIFT 8 /* DACVU */ +#define WM8985_DACVU_WIDTH 1 /* DACVU */ +#define WM8985_DACVOLL_MASK 0x00FF /* DACVOLL - [7:0] */ +#define WM8985_DACVOLL_SHIFT 0 /* DACVOLL - [7:0] */ +#define WM8985_DACVOLL_WIDTH 8 /* DACVOLL - [7:0] */ + +/* + * R12 (0x0C) - Right DAC digital vol + */ +#define WM8985_DACVU 0x0100 /* DACVU */ +#define WM8985_DACVU_MASK 0x0100 /* DACVU */ +#define WM8985_DACVU_SHIFT 8 /* DACVU */ +#define WM8985_DACVU_WIDTH 1 /* DACVU */ +#define WM8985_DACVOLR_MASK 0x00FF /* DACVOLR - [7:0] */ +#define WM8985_DACVOLR_SHIFT 0 /* DACVOLR - [7:0] */ +#define WM8985_DACVOLR_WIDTH 8 /* DACVOLR - [7:0] */ + +/* + * R13 (0x0D) - Jack Detect Control 2 + */ +#define WM8985_JD_EN1_MASK 0x00F0 /* JD_EN1 - [7:4] */ +#define WM8985_JD_EN1_SHIFT 4 /* JD_EN1 - [7:4] */ +#define WM8985_JD_EN1_WIDTH 4 /* JD_EN1 - [7:4] */ +#define WM8985_JD_EN0_MASK 0x000F /* JD_EN0 - [3:0] */ +#define WM8985_JD_EN0_SHIFT 0 /* JD_EN0 - [3:0] */ +#define WM8985_JD_EN0_WIDTH 4 /* JD_EN0 - [3:0] */ + +/* + * R14 (0x0E) - ADC Control + */ +#define WM8985_HPFEN 0x0100 /* HPFEN */ +#define WM8985_HPFEN_MASK 0x0100 /* HPFEN */ +#define WM8985_HPFEN_SHIFT 8 /* HPFEN */ +#define WM8985_HPFEN_WIDTH 1 /* HPFEN */ +#define WM8985_HPFAPP 0x0080 /* HPFAPP */ +#define WM8985_HPFAPP_MASK 0x0080 /* HPFAPP */ +#define WM8985_HPFAPP_SHIFT 7 /* HPFAPP */ +#define WM8985_HPFAPP_WIDTH 1 /* HPFAPP */ +#define WM8985_HPFCUT_MASK 0x0070 /* HPFCUT - [6:4] */ +#define WM8985_HPFCUT_SHIFT 4 /* HPFCUT - [6:4] */ +#define WM8985_HPFCUT_WIDTH 3 /* HPFCUT - [6:4] */ +#define WM8985_ADCOSR128 0x0008 /* ADCOSR128 */ +#define WM8985_ADCOSR128_MASK 0x0008 /* ADCOSR128 */ +#define WM8985_ADCOSR128_SHIFT 3 /* ADCOSR128 */ +#define WM8985_ADCOSR128_WIDTH 1 /* ADCOSR128 */ +#define WM8985_ADCRPOL 0x0002 /* ADCRPOL */ +#define WM8985_ADCRPOL_MASK 0x0002 /* ADCRPOL */ +#define WM8985_ADCRPOL_SHIFT 1 /* ADCRPOL */ +#define WM8985_ADCRPOL_WIDTH 1 /* ADCRPOL */ +#define WM8985_ADCLPOL 0x0001 /* ADCLPOL */ +#define WM8985_ADCLPOL_MASK 0x0001 /* ADCLPOL */ +#define WM8985_ADCLPOL_SHIFT 0 /* ADCLPOL */ +#define WM8985_ADCLPOL_WIDTH 1 /* ADCLPOL */ + +/* + * R15 (0x0F) - Left ADC Digital Vol + */ +#define WM8985_ADCVU 0x0100 /* ADCVU */ +#define WM8985_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8985_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8985_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8985_ADCVOLL_MASK 0x00FF /* ADCVOLL - [7:0] */ +#define WM8985_ADCVOLL_SHIFT 0 /* ADCVOLL - [7:0] */ +#define WM8985_ADCVOLL_WIDTH 8 /* ADCVOLL - [7:0] */ + +/* + * R16 (0x10) - Right ADC Digital Vol + */ +#define WM8985_ADCVU 0x0100 /* ADCVU */ +#define WM8985_ADCVU_MASK 0x0100 /* ADCVU */ +#define WM8985_ADCVU_SHIFT 8 /* ADCVU */ +#define WM8985_ADCVU_WIDTH 1 /* ADCVU */ +#define WM8985_ADCVOLR_MASK 0x00FF /* ADCVOLR - [7:0] */ +#define WM8985_ADCVOLR_SHIFT 0 /* ADCVOLR - [7:0] */ +#define WM8985_ADCVOLR_WIDTH 8 /* ADCVOLR - [7:0] */ + +/* + * R18 (0x12) - EQ1 - low shelf + */ +#define WM8985_EQ3DMODE 0x0100 /* EQ3DMODE */ +#define WM8985_EQ3DMODE_MASK 0x0100 /* EQ3DMODE */ +#define WM8985_EQ3DMODE_SHIFT 8 /* EQ3DMODE */ +#define WM8985_EQ3DMODE_WIDTH 1 /* EQ3DMODE */ +#define WM8985_EQ1C_MASK 0x0060 /* EQ1C - [6:5] */ +#define WM8985_EQ1C_SHIFT 5 /* EQ1C - [6:5] */ +#define WM8985_EQ1C_WIDTH 2 /* EQ1C - [6:5] */ +#define WM8985_EQ1G_MASK 0x001F /* EQ1G - [4:0] */ +#define WM8985_EQ1G_SHIFT 0 /* EQ1G - [4:0] */ +#define WM8985_EQ1G_WIDTH 5 /* EQ1G - [4:0] */ + +/* + * R19 (0x13) - EQ2 - peak 1 + */ +#define WM8985_EQ2BW 0x0100 /* EQ2BW */ +#define WM8985_EQ2BW_MASK 0x0100 /* EQ2BW */ +#define WM8985_EQ2BW_SHIFT 8 /* EQ2BW */ +#define WM8985_EQ2BW_WIDTH 1 /* EQ2BW */ +#define WM8985_EQ2C_MASK 0x0060 /* EQ2C - [6:5] */ +#define WM8985_EQ2C_SHIFT 5 /* EQ2C - [6:5] */ +#define WM8985_EQ2C_WIDTH 2 /* EQ2C - [6:5] */ +#define WM8985_EQ2G_MASK 0x001F /* EQ2G - [4:0] */ +#define WM8985_EQ2G_SHIFT 0 /* EQ2G - [4:0] */ +#define WM8985_EQ2G_WIDTH 5 /* EQ2G - [4:0] */ + +/* + * R20 (0x14) - EQ3 - peak 2 + */ +#define WM8985_EQ3BW 0x0100 /* EQ3BW */ +#define WM8985_EQ3BW_MASK 0x0100 /* EQ3BW */ +#define WM8985_EQ3BW_SHIFT 8 /* EQ3BW */ +#define WM8985_EQ3BW_WIDTH 1 /* EQ3BW */ +#define WM8985_EQ3C_MASK 0x0060 /* EQ3C - [6:5] */ +#define WM8985_EQ3C_SHIFT 5 /* EQ3C - [6:5] */ +#define WM8985_EQ3C_WIDTH 2 /* EQ3C - [6:5] */ +#define WM8985_EQ3G_MASK 0x001F /* EQ3G - [4:0] */ +#define WM8985_EQ3G_SHIFT 0 /* EQ3G - [4:0] */ +#define WM8985_EQ3G_WIDTH 5 /* EQ3G - [4:0] */ + +/* + * R21 (0x15) - EQ4 - peak 3 + */ +#define WM8985_EQ4BW 0x0100 /* EQ4BW */ +#define WM8985_EQ4BW_MASK 0x0100 /* EQ4BW */ +#define WM8985_EQ4BW_SHIFT 8 /* EQ4BW */ +#define WM8985_EQ4BW_WIDTH 1 /* EQ4BW */ +#define WM8985_EQ4C_MASK 0x0060 /* EQ4C - [6:5] */ +#define WM8985_EQ4C_SHIFT 5 /* EQ4C - [6:5] */ +#define WM8985_EQ4C_WIDTH 2 /* EQ4C - [6:5] */ +#define WM8985_EQ4G_MASK 0x001F /* EQ4G - [4:0] */ +#define WM8985_EQ4G_SHIFT 0 /* EQ4G - [4:0] */ +#define WM8985_EQ4G_WIDTH 5 /* EQ4G - [4:0] */ + +/* + * R22 (0x16) - EQ5 - high shelf + */ +#define WM8985_EQ5C_MASK 0x0060 /* EQ5C - [6:5] */ +#define WM8985_EQ5C_SHIFT 5 /* EQ5C - [6:5] */ +#define WM8985_EQ5C_WIDTH 2 /* EQ5C - [6:5] */ +#define WM8985_EQ5G_MASK 0x001F /* EQ5G - [4:0] */ +#define WM8985_EQ5G_SHIFT 0 /* EQ5G - [4:0] */ +#define WM8985_EQ5G_WIDTH 5 /* EQ5G - [4:0] */ + +/* + * R24 (0x18) - DAC Limiter 1 + */ +#define WM8985_LIMEN 0x0100 /* LIMEN */ +#define WM8985_LIMEN_MASK 0x0100 /* LIMEN */ +#define WM8985_LIMEN_SHIFT 8 /* LIMEN */ +#define WM8985_LIMEN_WIDTH 1 /* LIMEN */ +#define WM8985_LIMDCY_MASK 0x00F0 /* LIMDCY - [7:4] */ +#define WM8985_LIMDCY_SHIFT 4 /* LIMDCY - [7:4] */ +#define WM8985_LIMDCY_WIDTH 4 /* LIMDCY - [7:4] */ +#define WM8985_LIMATK_MASK 0x000F /* LIMATK - [3:0] */ +#define WM8985_LIMATK_SHIFT 0 /* LIMATK - [3:0] */ +#define WM8985_LIMATK_WIDTH 4 /* LIMATK - [3:0] */ + +/* + * R25 (0x19) - DAC Limiter 2 + */ +#define WM8985_LIMLVL_MASK 0x0070 /* LIMLVL - [6:4] */ +#define WM8985_LIMLVL_SHIFT 4 /* LIMLVL - [6:4] */ +#define WM8985_LIMLVL_WIDTH 3 /* LIMLVL - [6:4] */ +#define WM8985_LIMBOOST_MASK 0x000F /* LIMBOOST - [3:0] */ +#define WM8985_LIMBOOST_SHIFT 0 /* LIMBOOST - [3:0] */ +#define WM8985_LIMBOOST_WIDTH 4 /* LIMBOOST - [3:0] */ + +/* + * R27 (0x1B) - Notch Filter 1 + */ +#define WM8985_NFU 0x0100 /* NFU */ +#define WM8985_NFU_MASK 0x0100 /* NFU */ +#define WM8985_NFU_SHIFT 8 /* NFU */ +#define WM8985_NFU_WIDTH 1 /* NFU */ +#define WM8985_NFEN 0x0080 /* NFEN */ +#define WM8985_NFEN_MASK 0x0080 /* NFEN */ +#define WM8985_NFEN_SHIFT 7 /* NFEN */ +#define WM8985_NFEN_WIDTH 1 /* NFEN */ +#define WM8985_NFA0_13_7_MASK 0x007F /* NFA0(13:7) - [6:0] */ +#define WM8985_NFA0_13_7_SHIFT 0 /* NFA0(13:7) - [6:0] */ +#define WM8985_NFA0_13_7_WIDTH 7 /* NFA0(13:7) - [6:0] */ + +/* + * R28 (0x1C) - Notch Filter 2 + */ +#define WM8985_NFU 0x0100 /* NFU */ +#define WM8985_NFU_MASK 0x0100 /* NFU */ +#define WM8985_NFU_SHIFT 8 /* NFU */ +#define WM8985_NFU_WIDTH 1 /* NFU */ +#define WM8985_NFA0_6_0_MASK 0x007F /* NFA0(6:0) - [6:0] */ +#define WM8985_NFA0_6_0_SHIFT 0 /* NFA0(6:0) - [6:0] */ +#define WM8985_NFA0_6_0_WIDTH 7 /* NFA0(6:0) - [6:0] */ + +/* + * R29 (0x1D) - Notch Filter 3 + */ +#define WM8985_NFU 0x0100 /* NFU */ +#define WM8985_NFU_MASK 0x0100 /* NFU */ +#define WM8985_NFU_SHIFT 8 /* NFU */ +#define WM8985_NFU_WIDTH 1 /* NFU */ +#define WM8985_NFA1_13_7_MASK 0x007F /* NFA1(13:7) - [6:0] */ +#define WM8985_NFA1_13_7_SHIFT 0 /* NFA1(13:7) - [6:0] */ +#define WM8985_NFA1_13_7_WIDTH 7 /* NFA1(13:7) - [6:0] */ + +/* + * R30 (0x1E) - Notch Filter 4 + */ +#define WM8985_NFU 0x0100 /* NFU */ +#define WM8985_NFU_MASK 0x0100 /* NFU */ +#define WM8985_NFU_SHIFT 8 /* NFU */ +#define WM8985_NFU_WIDTH 1 /* NFU */ +#define WM8985_NFA1_6_0_MASK 0x007F /* NFA1(6:0) - [6:0] */ +#define WM8985_NFA1_6_0_SHIFT 0 /* NFA1(6:0) - [6:0] */ +#define WM8985_NFA1_6_0_WIDTH 7 /* NFA1(6:0) - [6:0] */ + +/* + * R32 (0x20) - ALC control 1 + */ +#define WM8985_ALCSEL_MASK 0x0180 /* ALCSEL - [8:7] */ +#define WM8985_ALCSEL_SHIFT 7 /* ALCSEL - [8:7] */ +#define WM8985_ALCSEL_WIDTH 2 /* ALCSEL - [8:7] */ +#define WM8985_ALCMAX_MASK 0x0038 /* ALCMAX - [5:3] */ +#define WM8985_ALCMAX_SHIFT 3 /* ALCMAX - [5:3] */ +#define WM8985_ALCMAX_WIDTH 3 /* ALCMAX - [5:3] */ +#define WM8985_ALCMIN_MASK 0x0007 /* ALCMIN - [2:0] */ +#define WM8985_ALCMIN_SHIFT 0 /* ALCMIN - [2:0] */ +#define WM8985_ALCMIN_WIDTH 3 /* ALCMIN - [2:0] */ + +/* + * R33 (0x21) - ALC control 2 + */ +#define WM8985_ALCHLD_MASK 0x00F0 /* ALCHLD - [7:4] */ +#define WM8985_ALCHLD_SHIFT 4 /* ALCHLD - [7:4] */ +#define WM8985_ALCHLD_WIDTH 4 /* ALCHLD - [7:4] */ +#define WM8985_ALCLVL_MASK 0x000F /* ALCLVL - [3:0] */ +#define WM8985_ALCLVL_SHIFT 0 /* ALCLVL - [3:0] */ +#define WM8985_ALCLVL_WIDTH 4 /* ALCLVL - [3:0] */ + +/* + * R34 (0x22) - ALC control 3 + */ +#define WM8985_ALCMODE 0x0100 /* ALCMODE */ +#define WM8985_ALCMODE_MASK 0x0100 /* ALCMODE */ +#define WM8985_ALCMODE_SHIFT 8 /* ALCMODE */ +#define WM8985_ALCMODE_WIDTH 1 /* ALCMODE */ +#define WM8985_ALCDCY_MASK 0x00F0 /* ALCDCY - [7:4] */ +#define WM8985_ALCDCY_SHIFT 4 /* ALCDCY - [7:4] */ +#define WM8985_ALCDCY_WIDTH 4 /* ALCDCY - [7:4] */ +#define WM8985_ALCATK_MASK 0x000F /* ALCATK - [3:0] */ +#define WM8985_ALCATK_SHIFT 0 /* ALCATK - [3:0] */ +#define WM8985_ALCATK_WIDTH 4 /* ALCATK - [3:0] */ + +/* + * R35 (0x23) - Noise Gate + */ +#define WM8985_NGEN 0x0008 /* NGEN */ +#define WM8985_NGEN_MASK 0x0008 /* NGEN */ +#define WM8985_NGEN_SHIFT 3 /* NGEN */ +#define WM8985_NGEN_WIDTH 1 /* NGEN */ +#define WM8985_NGTH_MASK 0x0007 /* NGTH - [2:0] */ +#define WM8985_NGTH_SHIFT 0 /* NGTH - [2:0] */ +#define WM8985_NGTH_WIDTH 3 /* NGTH - [2:0] */ + +/* + * R36 (0x24) - PLL N + */ +#define WM8985_PLL_PRESCALE 0x0010 /* PLL_PRESCALE */ +#define WM8985_PLL_PRESCALE_MASK 0x0010 /* PLL_PRESCALE */ +#define WM8985_PLL_PRESCALE_SHIFT 4 /* PLL_PRESCALE */ +#define WM8985_PLL_PRESCALE_WIDTH 1 /* PLL_PRESCALE */ +#define WM8985_PLLN_MASK 0x000F /* PLLN - [3:0] */ +#define WM8985_PLLN_SHIFT 0 /* PLLN - [3:0] */ +#define WM8985_PLLN_WIDTH 4 /* PLLN - [3:0] */ + +/* + * R37 (0x25) - PLL K 1 + */ +#define WM8985_PLLK_23_18_MASK 0x003F /* PLLK(23:18) - [5:0] */ +#define WM8985_PLLK_23_18_SHIFT 0 /* PLLK(23:18) - [5:0] */ +#define WM8985_PLLK_23_18_WIDTH 6 /* PLLK(23:18) - [5:0] */ + +/* + * R38 (0x26) - PLL K 2 + */ +#define WM8985_PLLK_17_9_MASK 0x01FF /* PLLK(17:9) - [8:0] */ +#define WM8985_PLLK_17_9_SHIFT 0 /* PLLK(17:9) - [8:0] */ +#define WM8985_PLLK_17_9_WIDTH 9 /* PLLK(17:9) - [8:0] */ + +/* + * R39 (0x27) - PLL K 3 + */ +#define WM8985_PLLK_8_0_MASK 0x01FF /* PLLK(8:0) - [8:0] */ +#define WM8985_PLLK_8_0_SHIFT 0 /* PLLK(8:0) - [8:0] */ +#define WM8985_PLLK_8_0_WIDTH 9 /* PLLK(8:0) - [8:0] */ + +/* + * R41 (0x29) - 3D control + */ +#define WM8985_DEPTH3D_MASK 0x000F /* DEPTH3D - [3:0] */ +#define WM8985_DEPTH3D_SHIFT 0 /* DEPTH3D - [3:0] */ +#define WM8985_DEPTH3D_WIDTH 4 /* DEPTH3D - [3:0] */ + +/* + * R42 (0x2A) - OUT4 to ADC + */ +#define WM8985_OUT4_2ADCVOL_MASK 0x01C0 /* OUT4_2ADCVOL - [8:6] */ +#define WM8985_OUT4_2ADCVOL_SHIFT 6 /* OUT4_2ADCVOL - [8:6] */ +#define WM8985_OUT4_2ADCVOL_WIDTH 3 /* OUT4_2ADCVOL - [8:6] */ +#define WM8985_OUT4_2LNR 0x0020 /* OUT4_2LNR */ +#define WM8985_OUT4_2LNR_MASK 0x0020 /* OUT4_2LNR */ +#define WM8985_OUT4_2LNR_SHIFT 5 /* OUT4_2LNR */ +#define WM8985_OUT4_2LNR_WIDTH 1 /* OUT4_2LNR */ +#define WM8758_VMIDTOG_MASK 0x0010 /* VMIDTOG */ +#define WM8758_VMIDTOG_SHIFT 4 /* VMIDTOG */ +#define WM8758_VMIDTOG_WIDTH 1 /* VMIDTOG */ +#define WM8758_OUT2DEL_MASK 0x0008 /* OUT2DEL */ +#define WM8758_OUT2DEL_SHIFT 3 /* OUT2DEL */ +#define WM8758_OUT2DEL_WIDTH 1 /* OUT2DEL */ +#define WM8985_POBCTRL 0x0004 /* POBCTRL */ +#define WM8985_POBCTRL_MASK 0x0004 /* POBCTRL */ +#define WM8985_POBCTRL_SHIFT 2 /* POBCTRL */ +#define WM8985_POBCTRL_WIDTH 1 /* POBCTRL */ +#define WM8985_DELEN 0x0002 /* DELEN */ +#define WM8985_DELEN_MASK 0x0002 /* DELEN */ +#define WM8985_DELEN_SHIFT 1 /* DELEN */ +#define WM8985_DELEN_WIDTH 1 /* DELEN */ +#define WM8985_OUT1DEL 0x0001 /* OUT1DEL */ +#define WM8985_OUT1DEL_MASK 0x0001 /* OUT1DEL */ +#define WM8985_OUT1DEL_SHIFT 0 /* OUT1DEL */ +#define WM8985_OUT1DEL_WIDTH 1 /* OUT1DEL */ + +/* + * R43 (0x2B) - Beep control + */ +#define WM8985_BYPL2RMIX 0x0100 /* BYPL2RMIX */ +#define WM8985_BYPL2RMIX_MASK 0x0100 /* BYPL2RMIX */ +#define WM8985_BYPL2RMIX_SHIFT 8 /* BYPL2RMIX */ +#define WM8985_BYPL2RMIX_WIDTH 1 /* BYPL2RMIX */ +#define WM8985_BYPR2LMIX 0x0080 /* BYPR2LMIX */ +#define WM8985_BYPR2LMIX_MASK 0x0080 /* BYPR2LMIX */ +#define WM8985_BYPR2LMIX_SHIFT 7 /* BYPR2LMIX */ +#define WM8985_BYPR2LMIX_WIDTH 1 /* BYPR2LMIX */ +#define WM8985_MUTERPGA2INV 0x0020 /* MUTERPGA2INV */ +#define WM8985_MUTERPGA2INV_MASK 0x0020 /* MUTERPGA2INV */ +#define WM8985_MUTERPGA2INV_SHIFT 5 /* MUTERPGA2INV */ +#define WM8985_MUTERPGA2INV_WIDTH 1 /* MUTERPGA2INV */ +#define WM8985_INVROUT2 0x0010 /* INVROUT2 */ +#define WM8985_INVROUT2_MASK 0x0010 /* INVROUT2 */ +#define WM8985_INVROUT2_SHIFT 4 /* INVROUT2 */ +#define WM8985_INVROUT2_WIDTH 1 /* INVROUT2 */ +#define WM8985_BEEPVOL_MASK 0x000E /* BEEPVOL - [3:1] */ +#define WM8985_BEEPVOL_SHIFT 1 /* BEEPVOL - [3:1] */ +#define WM8985_BEEPVOL_WIDTH 3 /* BEEPVOL - [3:1] */ +#define WM8758_DELEN2_MASK 0x0004 /* DELEN2 */ +#define WM8758_DELEN2_SHIFT 2 /* DELEN2 */ +#define WM8758_DELEN2_WIDTH 1 /* DELEN2 */ +#define WM8985_BEEPEN 0x0001 /* BEEPEN */ +#define WM8985_BEEPEN_MASK 0x0001 /* BEEPEN */ +#define WM8985_BEEPEN_SHIFT 0 /* BEEPEN */ +#define WM8985_BEEPEN_WIDTH 1 /* BEEPEN */ + +/* + * R44 (0x2C) - Input ctrl + */ +#define WM8985_MBVSEL 0x0100 /* MBVSEL */ +#define WM8985_MBVSEL_MASK 0x0100 /* MBVSEL */ +#define WM8985_MBVSEL_SHIFT 8 /* MBVSEL */ +#define WM8985_MBVSEL_WIDTH 1 /* MBVSEL */ +#define WM8985_R2_2INPPGA 0x0040 /* R2_2INPPGA */ +#define WM8985_R2_2INPPGA_MASK 0x0040 /* R2_2INPPGA */ +#define WM8985_R2_2INPPGA_SHIFT 6 /* R2_2INPPGA */ +#define WM8985_R2_2INPPGA_WIDTH 1 /* R2_2INPPGA */ +#define WM8985_RIN2INPPGA 0x0020 /* RIN2INPPGA */ +#define WM8985_RIN2INPPGA_MASK 0x0020 /* RIN2INPPGA */ +#define WM8985_RIN2INPPGA_SHIFT 5 /* RIN2INPPGA */ +#define WM8985_RIN2INPPGA_WIDTH 1 /* RIN2INPPGA */ +#define WM8985_RIP2INPPGA 0x0010 /* RIP2INPPGA */ +#define WM8985_RIP2INPPGA_MASK 0x0010 /* RIP2INPPGA */ +#define WM8985_RIP2INPPGA_SHIFT 4 /* RIP2INPPGA */ +#define WM8985_RIP2INPPGA_WIDTH 1 /* RIP2INPPGA */ +#define WM8985_L2_2INPPGA 0x0004 /* L2_2INPPGA */ +#define WM8985_L2_2INPPGA_MASK 0x0004 /* L2_2INPPGA */ +#define WM8985_L2_2INPPGA_SHIFT 2 /* L2_2INPPGA */ +#define WM8985_L2_2INPPGA_WIDTH 1 /* L2_2INPPGA */ +#define WM8985_LIN2INPPGA 0x0002 /* LIN2INPPGA */ +#define WM8985_LIN2INPPGA_MASK 0x0002 /* LIN2INPPGA */ +#define WM8985_LIN2INPPGA_SHIFT 1 /* LIN2INPPGA */ +#define WM8985_LIN2INPPGA_WIDTH 1 /* LIN2INPPGA */ +#define WM8985_LIP2INPPGA 0x0001 /* LIP2INPPGA */ +#define WM8985_LIP2INPPGA_MASK 0x0001 /* LIP2INPPGA */ +#define WM8985_LIP2INPPGA_SHIFT 0 /* LIP2INPPGA */ +#define WM8985_LIP2INPPGA_WIDTH 1 /* LIP2INPPGA */ + +/* + * R45 (0x2D) - Left INP PGA gain ctrl + */ +#define WM8985_INPGAVU 0x0100 /* INPGAVU */ +#define WM8985_INPGAVU_MASK 0x0100 /* INPGAVU */ +#define WM8985_INPGAVU_SHIFT 8 /* INPGAVU */ +#define WM8985_INPGAVU_WIDTH 1 /* INPGAVU */ +#define WM8985_INPPGAZCL 0x0080 /* INPPGAZCL */ +#define WM8985_INPPGAZCL_MASK 0x0080 /* INPPGAZCL */ +#define WM8985_INPPGAZCL_SHIFT 7 /* INPPGAZCL */ +#define WM8985_INPPGAZCL_WIDTH 1 /* INPPGAZCL */ +#define WM8985_INPPGAMUTEL 0x0040 /* INPPGAMUTEL */ +#define WM8985_INPPGAMUTEL_MASK 0x0040 /* INPPGAMUTEL */ +#define WM8985_INPPGAMUTEL_SHIFT 6 /* INPPGAMUTEL */ +#define WM8985_INPPGAMUTEL_WIDTH 1 /* INPPGAMUTEL */ +#define WM8985_INPPGAVOLL_MASK 0x003F /* INPPGAVOLL - [5:0] */ +#define WM8985_INPPGAVOLL_SHIFT 0 /* INPPGAVOLL - [5:0] */ +#define WM8985_INPPGAVOLL_WIDTH 6 /* INPPGAVOLL - [5:0] */ + +/* + * R46 (0x2E) - Right INP PGA gain ctrl + */ +#define WM8985_INPGAVU 0x0100 /* INPGAVU */ +#define WM8985_INPGAVU_MASK 0x0100 /* INPGAVU */ +#define WM8985_INPGAVU_SHIFT 8 /* INPGAVU */ +#define WM8985_INPGAVU_WIDTH 1 /* INPGAVU */ +#define WM8985_INPPGAZCR 0x0080 /* INPPGAZCR */ +#define WM8985_INPPGAZCR_MASK 0x0080 /* INPPGAZCR */ +#define WM8985_INPPGAZCR_SHIFT 7 /* INPPGAZCR */ +#define WM8985_INPPGAZCR_WIDTH 1 /* INPPGAZCR */ +#define WM8985_INPPGAMUTER 0x0040 /* INPPGAMUTER */ +#define WM8985_INPPGAMUTER_MASK 0x0040 /* INPPGAMUTER */ +#define WM8985_INPPGAMUTER_SHIFT 6 /* INPPGAMUTER */ +#define WM8985_INPPGAMUTER_WIDTH 1 /* INPPGAMUTER */ +#define WM8985_INPPGAVOLR_MASK 0x003F /* INPPGAVOLR - [5:0] */ +#define WM8985_INPPGAVOLR_SHIFT 0 /* INPPGAVOLR - [5:0] */ +#define WM8985_INPPGAVOLR_WIDTH 6 /* INPPGAVOLR - [5:0] */ + +/* + * R47 (0x2F) - Left ADC BOOST ctrl + */ +#define WM8985_PGABOOSTL 0x0100 /* PGABOOSTL */ +#define WM8985_PGABOOSTL_MASK 0x0100 /* PGABOOSTL */ +#define WM8985_PGABOOSTL_SHIFT 8 /* PGABOOSTL */ +#define WM8985_PGABOOSTL_WIDTH 1 /* PGABOOSTL */ +#define WM8985_L2_2BOOSTVOL_MASK 0x0070 /* L2_2BOOSTVOL - [6:4] */ +#define WM8985_L2_2BOOSTVOL_SHIFT 4 /* L2_2BOOSTVOL - [6:4] */ +#define WM8985_L2_2BOOSTVOL_WIDTH 3 /* L2_2BOOSTVOL - [6:4] */ +#define WM8985_AUXL2BOOSTVOL_MASK 0x0007 /* AUXL2BOOSTVOL - [2:0] */ +#define WM8985_AUXL2BOOSTVOL_SHIFT 0 /* AUXL2BOOSTVOL - [2:0] */ +#define WM8985_AUXL2BOOSTVOL_WIDTH 3 /* AUXL2BOOSTVOL - [2:0] */ + +/* + * R48 (0x30) - Right ADC BOOST ctrl + */ +#define WM8985_PGABOOSTR 0x0100 /* PGABOOSTR */ +#define WM8985_PGABOOSTR_MASK 0x0100 /* PGABOOSTR */ +#define WM8985_PGABOOSTR_SHIFT 8 /* PGABOOSTR */ +#define WM8985_PGABOOSTR_WIDTH 1 /* PGABOOSTR */ +#define WM8985_R2_2BOOSTVOL_MASK 0x0070 /* R2_2BOOSTVOL - [6:4] */ +#define WM8985_R2_2BOOSTVOL_SHIFT 4 /* R2_2BOOSTVOL - [6:4] */ +#define WM8985_R2_2BOOSTVOL_WIDTH 3 /* R2_2BOOSTVOL - [6:4] */ +#define WM8985_AUXR2BOOSTVOL_MASK 0x0007 /* AUXR2BOOSTVOL - [2:0] */ +#define WM8985_AUXR2BOOSTVOL_SHIFT 0 /* AUXR2BOOSTVOL - [2:0] */ +#define WM8985_AUXR2BOOSTVOL_WIDTH 3 /* AUXR2BOOSTVOL - [2:0] */ + +/* + * R49 (0x31) - Output ctrl + */ +#define WM8758_HP_COM 0x0100 /* HP_COM */ +#define WM8758_HP_COM_MASK 0x0100 /* HP_COM */ +#define WM8758_HP_COM_SHIFT 8 /* HP_COM */ +#define WM8758_HP_COM_WIDTH 1 /* HP_COM */ +#define WM8758_LINE_COM 0x0080 /* LINE_COM */ +#define WM8758_LINE_COM_MASK 0x0080 /* LINE_COM */ +#define WM8758_LINE_COM_SHIFT 7 /* LINE_COM */ +#define WM8758_LINE_COM_WIDTH 1 /* LINE_COM */ +#define WM8985_DACL2RMIX 0x0040 /* DACL2RMIX */ +#define WM8985_DACL2RMIX_MASK 0x0040 /* DACL2RMIX */ +#define WM8985_DACL2RMIX_SHIFT 6 /* DACL2RMIX */ +#define WM8985_DACL2RMIX_WIDTH 1 /* DACL2RMIX */ +#define WM8985_DACR2LMIX 0x0020 /* DACR2LMIX */ +#define WM8985_DACR2LMIX_MASK 0x0020 /* DACR2LMIX */ +#define WM8985_DACR2LMIX_SHIFT 5 /* DACR2LMIX */ +#define WM8985_DACR2LMIX_WIDTH 1 /* DACR2LMIX */ +#define WM8985_OUT4BOOST 0x0010 /* OUT4BOOST */ +#define WM8985_OUT4BOOST_MASK 0x0010 /* OUT4BOOST */ +#define WM8985_OUT4BOOST_SHIFT 4 /* OUT4BOOST */ +#define WM8985_OUT4BOOST_WIDTH 1 /* OUT4BOOST */ +#define WM8985_OUT3BOOST 0x0008 /* OUT3BOOST */ +#define WM8985_OUT3BOOST_MASK 0x0008 /* OUT3BOOST */ +#define WM8985_OUT3BOOST_SHIFT 3 /* OUT3BOOST */ +#define WM8985_OUT3BOOST_WIDTH 1 /* OUT3BOOST */ +#define WM8758_OUT4ENDEL 0x0010 /* OUT4ENDEL */ +#define WM8758_OUT4ENDEL_MASK 0x0010 /* OUT4ENDEL */ +#define WM8758_OUT4ENDEL_SHIFT 4 /* OUT4ENDEL */ +#define WM8758_OUT4ENDEL_WIDTH 1 /* OUT4ENDEL */ +#define WM8758_OUT3ENDEL 0x0008 /* OUT3ENDEL */ +#define WM8758_OUT3ENDEL_MASK 0x0008 /* OUT3ENDEL */ +#define WM8758_OUT3ENDEL_SHIFT 3 /* OUT3ENDEL */ +#define WM8758_OUT3ENDEL_WIDTH 1 /* OUT3ENDEL */ +#define WM8985_TSOPCTRL 0x0004 /* TSOPCTRL */ +#define WM8985_TSOPCTRL_MASK 0x0004 /* TSOPCTRL */ +#define WM8985_TSOPCTRL_SHIFT 2 /* TSOPCTRL */ +#define WM8985_TSOPCTRL_WIDTH 1 /* TSOPCTRL */ +#define WM8985_TSDEN 0x0002 /* TSDEN */ +#define WM8985_TSDEN_MASK 0x0002 /* TSDEN */ +#define WM8985_TSDEN_SHIFT 1 /* TSDEN */ +#define WM8985_TSDEN_WIDTH 1 /* TSDEN */ +#define WM8985_VROI 0x0001 /* VROI */ +#define WM8985_VROI_MASK 0x0001 /* VROI */ +#define WM8985_VROI_SHIFT 0 /* VROI */ +#define WM8985_VROI_WIDTH 1 /* VROI */ + +/* + * R50 (0x32) - Left mixer ctrl + */ +#define WM8985_AUXLMIXVOL_MASK 0x01C0 /* AUXLMIXVOL - [8:6] */ +#define WM8985_AUXLMIXVOL_SHIFT 6 /* AUXLMIXVOL - [8:6] */ +#define WM8985_AUXLMIXVOL_WIDTH 3 /* AUXLMIXVOL - [8:6] */ +#define WM8985_AUXL2LMIX 0x0020 /* AUXL2LMIX */ +#define WM8985_AUXL2LMIX_MASK 0x0020 /* AUXL2LMIX */ +#define WM8985_AUXL2LMIX_SHIFT 5 /* AUXL2LMIX */ +#define WM8985_AUXL2LMIX_WIDTH 1 /* AUXL2LMIX */ +#define WM8985_BYPLMIXVOL_MASK 0x001C /* BYPLMIXVOL - [4:2] */ +#define WM8985_BYPLMIXVOL_SHIFT 2 /* BYPLMIXVOL - [4:2] */ +#define WM8985_BYPLMIXVOL_WIDTH 3 /* BYPLMIXVOL - [4:2] */ +#define WM8985_BYPL2LMIX 0x0002 /* BYPL2LMIX */ +#define WM8985_BYPL2LMIX_MASK 0x0002 /* BYPL2LMIX */ +#define WM8985_BYPL2LMIX_SHIFT 1 /* BYPL2LMIX */ +#define WM8985_BYPL2LMIX_WIDTH 1 /* BYPL2LMIX */ +#define WM8985_DACL2LMIX 0x0001 /* DACL2LMIX */ +#define WM8985_DACL2LMIX_MASK 0x0001 /* DACL2LMIX */ +#define WM8985_DACL2LMIX_SHIFT 0 /* DACL2LMIX */ +#define WM8985_DACL2LMIX_WIDTH 1 /* DACL2LMIX */ + +/* + * R51 (0x33) - Right mixer ctrl + */ +#define WM8985_AUXRMIXVOL_MASK 0x01C0 /* AUXRMIXVOL - [8:6] */ +#define WM8985_AUXRMIXVOL_SHIFT 6 /* AUXRMIXVOL - [8:6] */ +#define WM8985_AUXRMIXVOL_WIDTH 3 /* AUXRMIXVOL - [8:6] */ +#define WM8985_AUXR2RMIX 0x0020 /* AUXR2RMIX */ +#define WM8985_AUXR2RMIX_MASK 0x0020 /* AUXR2RMIX */ +#define WM8985_AUXR2RMIX_SHIFT 5 /* AUXR2RMIX */ +#define WM8985_AUXR2RMIX_WIDTH 1 /* AUXR2RMIX */ +#define WM8985_BYPRMIXVOL_MASK 0x001C /* BYPRMIXVOL - [4:2] */ +#define WM8985_BYPRMIXVOL_SHIFT 2 /* BYPRMIXVOL - [4:2] */ +#define WM8985_BYPRMIXVOL_WIDTH 3 /* BYPRMIXVOL - [4:2] */ +#define WM8985_BYPR2RMIX 0x0002 /* BYPR2RMIX */ +#define WM8985_BYPR2RMIX_MASK 0x0002 /* BYPR2RMIX */ +#define WM8985_BYPR2RMIX_SHIFT 1 /* BYPR2RMIX */ +#define WM8985_BYPR2RMIX_WIDTH 1 /* BYPR2RMIX */ +#define WM8985_DACR2RMIX 0x0001 /* DACR2RMIX */ +#define WM8985_DACR2RMIX_MASK 0x0001 /* DACR2RMIX */ +#define WM8985_DACR2RMIX_SHIFT 0 /* DACR2RMIX */ +#define WM8985_DACR2RMIX_WIDTH 1 /* DACR2RMIX */ + +/* + * R52 (0x34) - LOUT1 (HP) volume ctrl + */ +#define WM8985_OUT1VU 0x0100 /* OUT1VU */ +#define WM8985_OUT1VU_MASK 0x0100 /* OUT1VU */ +#define WM8985_OUT1VU_SHIFT 8 /* OUT1VU */ +#define WM8985_OUT1VU_WIDTH 1 /* OUT1VU */ +#define WM8985_LOUT1ZC 0x0080 /* LOUT1ZC */ +#define WM8985_LOUT1ZC_MASK 0x0080 /* LOUT1ZC */ +#define WM8985_LOUT1ZC_SHIFT 7 /* LOUT1ZC */ +#define WM8985_LOUT1ZC_WIDTH 1 /* LOUT1ZC */ +#define WM8985_LOUT1MUTE 0x0040 /* LOUT1MUTE */ +#define WM8985_LOUT1MUTE_MASK 0x0040 /* LOUT1MUTE */ +#define WM8985_LOUT1MUTE_SHIFT 6 /* LOUT1MUTE */ +#define WM8985_LOUT1MUTE_WIDTH 1 /* LOUT1MUTE */ +#define WM8985_LOUT1VOL_MASK 0x003F /* LOUT1VOL - [5:0] */ +#define WM8985_LOUT1VOL_SHIFT 0 /* LOUT1VOL - [5:0] */ +#define WM8985_LOUT1VOL_WIDTH 6 /* LOUT1VOL - [5:0] */ + +/* + * R53 (0x35) - ROUT1 (HP) volume ctrl + */ +#define WM8985_OUT1VU 0x0100 /* OUT1VU */ +#define WM8985_OUT1VU_MASK 0x0100 /* OUT1VU */ +#define WM8985_OUT1VU_SHIFT 8 /* OUT1VU */ +#define WM8985_OUT1VU_WIDTH 1 /* OUT1VU */ +#define WM8985_ROUT1ZC 0x0080 /* ROUT1ZC */ +#define WM8985_ROUT1ZC_MASK 0x0080 /* ROUT1ZC */ +#define WM8985_ROUT1ZC_SHIFT 7 /* ROUT1ZC */ +#define WM8985_ROUT1ZC_WIDTH 1 /* ROUT1ZC */ +#define WM8985_ROUT1MUTE 0x0040 /* ROUT1MUTE */ +#define WM8985_ROUT1MUTE_MASK 0x0040 /* ROUT1MUTE */ +#define WM8985_ROUT1MUTE_SHIFT 6 /* ROUT1MUTE */ +#define WM8985_ROUT1MUTE_WIDTH 1 /* ROUT1MUTE */ +#define WM8985_ROUT1VOL_MASK 0x003F /* ROUT1VOL - [5:0] */ +#define WM8985_ROUT1VOL_SHIFT 0 /* ROUT1VOL - [5:0] */ +#define WM8985_ROUT1VOL_WIDTH 6 /* ROUT1VOL - [5:0] */ + +/* + * R54 (0x36) - LOUT2 (SPK) volume ctrl + */ +#define WM8985_OUT2VU 0x0100 /* OUT2VU */ +#define WM8985_OUT2VU_MASK 0x0100 /* OUT2VU */ +#define WM8985_OUT2VU_SHIFT 8 /* OUT2VU */ +#define WM8985_OUT2VU_WIDTH 1 /* OUT2VU */ +#define WM8985_LOUT2ZC 0x0080 /* LOUT2ZC */ +#define WM8985_LOUT2ZC_MASK 0x0080 /* LOUT2ZC */ +#define WM8985_LOUT2ZC_SHIFT 7 /* LOUT2ZC */ +#define WM8985_LOUT2ZC_WIDTH 1 /* LOUT2ZC */ +#define WM8985_LOUT2MUTE 0x0040 /* LOUT2MUTE */ +#define WM8985_LOUT2MUTE_MASK 0x0040 /* LOUT2MUTE */ +#define WM8985_LOUT2MUTE_SHIFT 6 /* LOUT2MUTE */ +#define WM8985_LOUT2MUTE_WIDTH 1 /* LOUT2MUTE */ +#define WM8985_LOUT2VOL_MASK 0x003F /* LOUT2VOL - [5:0] */ +#define WM8985_LOUT2VOL_SHIFT 0 /* LOUT2VOL - [5:0] */ +#define WM8985_LOUT2VOL_WIDTH 6 /* LOUT2VOL - [5:0] */ + +/* + * R55 (0x37) - ROUT2 (SPK) volume ctrl + */ +#define WM8985_OUT2VU 0x0100 /* OUT2VU */ +#define WM8985_OUT2VU_MASK 0x0100 /* OUT2VU */ +#define WM8985_OUT2VU_SHIFT 8 /* OUT2VU */ +#define WM8985_OUT2VU_WIDTH 1 /* OUT2VU */ +#define WM8985_ROUT2ZC 0x0080 /* ROUT2ZC */ +#define WM8985_ROUT2ZC_MASK 0x0080 /* ROUT2ZC */ +#define WM8985_ROUT2ZC_SHIFT 7 /* ROUT2ZC */ +#define WM8985_ROUT2ZC_WIDTH 1 /* ROUT2ZC */ +#define WM8985_ROUT2MUTE 0x0040 /* ROUT2MUTE */ +#define WM8985_ROUT2MUTE_MASK 0x0040 /* ROUT2MUTE */ +#define WM8985_ROUT2MUTE_SHIFT 6 /* ROUT2MUTE */ +#define WM8985_ROUT2MUTE_WIDTH 1 /* ROUT2MUTE */ +#define WM8985_ROUT2VOL_MASK 0x003F /* ROUT2VOL - [5:0] */ +#define WM8985_ROUT2VOL_SHIFT 0 /* ROUT2VOL - [5:0] */ +#define WM8985_ROUT2VOL_WIDTH 6 /* ROUT2VOL - [5:0] */ + +/* + * R56 (0x38) - OUT3 mixer ctrl + */ +#define WM8985_OUT3MUTE 0x0040 /* OUT3MUTE */ +#define WM8985_OUT3MUTE_MASK 0x0040 /* OUT3MUTE */ +#define WM8985_OUT3MUTE_SHIFT 6 /* OUT3MUTE */ +#define WM8985_OUT3MUTE_WIDTH 1 /* OUT3MUTE */ +#define WM8985_OUT4_2OUT3 0x0008 /* OUT4_2OUT3 */ +#define WM8985_OUT4_2OUT3_MASK 0x0008 /* OUT4_2OUT3 */ +#define WM8985_OUT4_2OUT3_SHIFT 3 /* OUT4_2OUT3 */ +#define WM8985_OUT4_2OUT3_WIDTH 1 /* OUT4_2OUT3 */ +#define WM8985_BYPL2OUT3 0x0004 /* BYPL2OUT3 */ +#define WM8985_BYPL2OUT3_MASK 0x0004 /* BYPL2OUT3 */ +#define WM8985_BYPL2OUT3_SHIFT 2 /* BYPL2OUT3 */ +#define WM8985_BYPL2OUT3_WIDTH 1 /* BYPL2OUT3 */ +#define WM8985_LMIX2OUT3 0x0002 /* LMIX2OUT3 */ +#define WM8985_LMIX2OUT3_MASK 0x0002 /* LMIX2OUT3 */ +#define WM8985_LMIX2OUT3_SHIFT 1 /* LMIX2OUT3 */ +#define WM8985_LMIX2OUT3_WIDTH 1 /* LMIX2OUT3 */ +#define WM8985_LDAC2OUT3 0x0001 /* LDAC2OUT3 */ +#define WM8985_LDAC2OUT3_MASK 0x0001 /* LDAC2OUT3 */ +#define WM8985_LDAC2OUT3_SHIFT 0 /* LDAC2OUT3 */ +#define WM8985_LDAC2OUT3_WIDTH 1 /* LDAC2OUT3 */ + +/* + * R57 (0x39) - OUT4 (MONO) mix ctrl + */ +#define WM8985_OUT3_2OUT4 0x0080 /* OUT3_2OUT4 */ +#define WM8985_OUT3_2OUT4_MASK 0x0080 /* OUT3_2OUT4 */ +#define WM8985_OUT3_2OUT4_SHIFT 7 /* OUT3_2OUT4 */ +#define WM8985_OUT3_2OUT4_WIDTH 1 /* OUT3_2OUT4 */ +#define WM8985_OUT4MUTE 0x0040 /* OUT4MUTE */ +#define WM8985_OUT4MUTE_MASK 0x0040 /* OUT4MUTE */ +#define WM8985_OUT4MUTE_SHIFT 6 /* OUT4MUTE */ +#define WM8985_OUT4MUTE_WIDTH 1 /* OUT4MUTE */ +#define WM8985_OUT4ATTN 0x0020 /* OUT4ATTN */ +#define WM8985_OUT4ATTN_MASK 0x0020 /* OUT4ATTN */ +#define WM8985_OUT4ATTN_SHIFT 5 /* OUT4ATTN */ +#define WM8985_OUT4ATTN_WIDTH 1 /* OUT4ATTN */ +#define WM8985_LMIX2OUT4 0x0010 /* LMIX2OUT4 */ +#define WM8985_LMIX2OUT4_MASK 0x0010 /* LMIX2OUT4 */ +#define WM8985_LMIX2OUT4_SHIFT 4 /* LMIX2OUT4 */ +#define WM8985_LMIX2OUT4_WIDTH 1 /* LMIX2OUT4 */ +#define WM8985_LDAC2OUT4 0x0008 /* LDAC2OUT4 */ +#define WM8985_LDAC2OUT4_MASK 0x0008 /* LDAC2OUT4 */ +#define WM8985_LDAC2OUT4_SHIFT 3 /* LDAC2OUT4 */ +#define WM8985_LDAC2OUT4_WIDTH 1 /* LDAC2OUT4 */ +#define WM8985_BYPR2OUT4 0x0004 /* BYPR2OUT4 */ +#define WM8985_BYPR2OUT4_MASK 0x0004 /* BYPR2OUT4 */ +#define WM8985_BYPR2OUT4_SHIFT 2 /* BYPR2OUT4 */ +#define WM8985_BYPR2OUT4_WIDTH 1 /* BYPR2OUT4 */ +#define WM8985_RMIX2OUT4 0x0002 /* RMIX2OUT4 */ +#define WM8985_RMIX2OUT4_MASK 0x0002 /* RMIX2OUT4 */ +#define WM8985_RMIX2OUT4_SHIFT 1 /* RMIX2OUT4 */ +#define WM8985_RMIX2OUT4_WIDTH 1 /* RMIX2OUT4 */ +#define WM8985_RDAC2OUT4 0x0001 /* RDAC2OUT4 */ +#define WM8985_RDAC2OUT4_MASK 0x0001 /* RDAC2OUT4 */ +#define WM8985_RDAC2OUT4_SHIFT 0 /* RDAC2OUT4 */ +#define WM8985_RDAC2OUT4_WIDTH 1 /* RDAC2OUT4 */ + +/* + * R60 (0x3C) - OUTPUT ctrl + */ +#define WM8985_VIDBUFFTST_MASK 0x01E0 /* VIDBUFFTST - [8:5] */ +#define WM8985_VIDBUFFTST_SHIFT 5 /* VIDBUFFTST - [8:5] */ +#define WM8985_VIDBUFFTST_WIDTH 4 /* VIDBUFFTST - [8:5] */ +#define WM8985_HPTOG 0x0008 /* HPTOG */ +#define WM8985_HPTOG_MASK 0x0008 /* HPTOG */ +#define WM8985_HPTOG_SHIFT 3 /* HPTOG */ +#define WM8985_HPTOG_WIDTH 1 /* HPTOG */ + +/* + * R61 (0x3D) - BIAS CTRL + */ +#define WM8985_BIASCUT 0x0100 /* BIASCUT */ +#define WM8985_BIASCUT_MASK 0x0100 /* BIASCUT */ +#define WM8985_BIASCUT_SHIFT 8 /* BIASCUT */ +#define WM8985_BIASCUT_WIDTH 1 /* BIASCUT */ +#define WM8985_HALFIPBIAS 0x0080 /* HALFIPBIAS */ +#define WM8985_HALFIPBIAS_MASK 0x0080 /* HALFIPBIAS */ +#define WM8985_HALFIPBIAS_SHIFT 7 /* HALFIPBIAS */ +#define WM8985_HALFIPBIAS_WIDTH 1 /* HALFIPBIAS */ +#define WM8758_HALFIPBIAS 0x0040 /* HALFI_IPGA */ +#define WM8758_HALFI_IPGA_MASK 0x0040 /* HALFI_IPGA */ +#define WM8758_HALFI_IPGA_SHIFT 6 /* HALFI_IPGA */ +#define WM8758_HALFI_IPGA_WIDTH 1 /* HALFI_IPGA */ +#define WM8985_VBBIASTST_MASK 0x0060 /* VBBIASTST - [6:5] */ +#define WM8985_VBBIASTST_SHIFT 5 /* VBBIASTST - [6:5] */ +#define WM8985_VBBIASTST_WIDTH 2 /* VBBIASTST - [6:5] */ +#define WM8985_BUFBIAS_MASK 0x0018 /* BUFBIAS - [4:3] */ +#define WM8985_BUFBIAS_SHIFT 3 /* BUFBIAS - [4:3] */ +#define WM8985_BUFBIAS_WIDTH 2 /* BUFBIAS - [4:3] */ +#define WM8985_ADCBIAS_MASK 0x0006 /* ADCBIAS - [2:1] */ +#define WM8985_ADCBIAS_SHIFT 1 /* ADCBIAS - [2:1] */ +#define WM8985_ADCBIAS_WIDTH 2 /* ADCBIAS - [2:1] */ +#define WM8985_HALFOPBIAS 0x0001 /* HALFOPBIAS */ +#define WM8985_HALFOPBIAS_MASK 0x0001 /* HALFOPBIAS */ +#define WM8985_HALFOPBIAS_SHIFT 0 /* HALFOPBIAS */ +#define WM8985_HALFOPBIAS_WIDTH 1 /* HALFOPBIAS */ + +enum clk_src { + WM8985_CLKSRC_MCLK, + WM8985_CLKSRC_PLL +}; + +#define WM8985_PLL 0 + +#endif diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c new file mode 100644 index 0000000000..76f214f12c --- /dev/null +++ b/sound/soc/codecs/wm8988.c @@ -0,0 +1,948 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8988.c -- WM8988 ALSA SoC audio driver + * + * Copyright 2009 Wolfson Microelectronics plc + * Copyright 2005 Openedhand Ltd. + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8988.h" + +/* + * wm8988 register cache + * We can't read the WM8988 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const struct reg_default wm8988_reg_defaults[] = { + { 0, 0x0097 }, + { 1, 0x0097 }, + { 2, 0x0079 }, + { 3, 0x0079 }, + { 5, 0x0008 }, + { 7, 0x000a }, + { 8, 0x0000 }, + { 10, 0x00ff }, + { 11, 0x00ff }, + { 12, 0x000f }, + { 13, 0x000f }, + { 16, 0x0000 }, + { 17, 0x007b }, + { 18, 0x0000 }, + { 19, 0x0032 }, + { 20, 0x0000 }, + { 21, 0x00c3 }, + { 22, 0x00c3 }, + { 23, 0x00c0 }, + { 24, 0x0000 }, + { 25, 0x0000 }, + { 26, 0x0000 }, + { 27, 0x0000 }, + { 31, 0x0000 }, + { 32, 0x0000 }, + { 33, 0x0000 }, + { 34, 0x0050 }, + { 35, 0x0050 }, + { 36, 0x0050 }, + { 37, 0x0050 }, + { 40, 0x0079 }, + { 41, 0x0079 }, + { 42, 0x0079 }, +}; + +static bool wm8988_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8988_LINVOL: + case WM8988_RINVOL: + case WM8988_LOUT1V: + case WM8988_ROUT1V: + case WM8988_ADCDAC: + case WM8988_IFACE: + case WM8988_SRATE: + case WM8988_LDAC: + case WM8988_RDAC: + case WM8988_BASS: + case WM8988_TREBLE: + case WM8988_RESET: + case WM8988_3D: + case WM8988_ALC1: + case WM8988_ALC2: + case WM8988_ALC3: + case WM8988_NGATE: + case WM8988_LADC: + case WM8988_RADC: + case WM8988_ADCTL1: + case WM8988_ADCTL2: + case WM8988_PWR1: + case WM8988_PWR2: + case WM8988_ADCTL3: + case WM8988_ADCIN: + case WM8988_LADCIN: + case WM8988_RADCIN: + case WM8988_LOUTM1: + case WM8988_LOUTM2: + case WM8988_ROUTM1: + case WM8988_ROUTM2: + case WM8988_LOUT2V: + case WM8988_ROUT2V: + case WM8988_LPPB: + return true; + default: + return false; + } +} + +/* codec private data */ +struct wm8988_priv { + struct regmap *regmap; + unsigned int sysclk; + const struct snd_pcm_hw_constraint_list *sysclk_constraints; +}; + +#define wm8988_reset(c) snd_soc_component_write(c, WM8988_RESET, 0) + +/* + * WM8988 Controls + */ + +static const char *bass_boost_txt[] = {"Linear Control", "Adaptive Boost"}; +static SOC_ENUM_SINGLE_DECL(bass_boost, + WM8988_BASS, 7, bass_boost_txt); + +static const char *bass_filter_txt[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" }; +static SOC_ENUM_SINGLE_DECL(bass_filter, + WM8988_BASS, 6, bass_filter_txt); + +static const char *treble_txt[] = {"8kHz", "4kHz"}; +static SOC_ENUM_SINGLE_DECL(treble, + WM8988_TREBLE, 6, treble_txt); + +static const char *stereo_3d_lc_txt[] = {"200Hz", "500Hz"}; +static SOC_ENUM_SINGLE_DECL(stereo_3d_lc, + WM8988_3D, 5, stereo_3d_lc_txt); + +static const char *stereo_3d_uc_txt[] = {"2.2kHz", "1.5kHz"}; +static SOC_ENUM_SINGLE_DECL(stereo_3d_uc, + WM8988_3D, 6, stereo_3d_uc_txt); + +static const char *stereo_3d_func_txt[] = {"Capture", "Playback"}; +static SOC_ENUM_SINGLE_DECL(stereo_3d_func, + WM8988_3D, 7, stereo_3d_func_txt); + +static const char *alc_func_txt[] = {"Off", "Right", "Left", "Stereo"}; +static SOC_ENUM_SINGLE_DECL(alc_func, + WM8988_ALC1, 7, alc_func_txt); + +static const char *ng_type_txt[] = {"Constant PGA Gain", + "Mute ADC Output"}; +static SOC_ENUM_SINGLE_DECL(ng_type, + WM8988_NGATE, 1, ng_type_txt); + +static const char *deemph_txt[] = {"None", "32Khz", "44.1Khz", "48Khz"}; +static SOC_ENUM_SINGLE_DECL(deemph, + WM8988_ADCDAC, 1, deemph_txt); + +static const char *adcpol_txt[] = {"Normal", "L Invert", "R Invert", + "L + R Invert"}; +static SOC_ENUM_SINGLE_DECL(adcpol, + WM8988_ADCDAC, 5, adcpol_txt); + +static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0); + +static const struct snd_kcontrol_new wm8988_snd_controls[] = { + +SOC_ENUM("Bass Boost", bass_boost), +SOC_ENUM("Bass Filter", bass_filter), +SOC_SINGLE("Bass Volume", WM8988_BASS, 0, 15, 1), + +SOC_SINGLE("Treble Volume", WM8988_TREBLE, 0, 15, 0), +SOC_ENUM("Treble Cut-off", treble), + +SOC_SINGLE("3D Switch", WM8988_3D, 0, 1, 0), +SOC_SINGLE("3D Volume", WM8988_3D, 1, 15, 0), +SOC_ENUM("3D Lower Cut-off", stereo_3d_lc), +SOC_ENUM("3D Upper Cut-off", stereo_3d_uc), +SOC_ENUM("3D Mode", stereo_3d_func), + +SOC_SINGLE("ALC Capture Target Volume", WM8988_ALC1, 0, 7, 0), +SOC_SINGLE("ALC Capture Max Volume", WM8988_ALC1, 4, 7, 0), +SOC_ENUM("ALC Capture Function", alc_func), +SOC_SINGLE("ALC Capture ZC Switch", WM8988_ALC2, 7, 1, 0), +SOC_SINGLE("ALC Capture Hold Time", WM8988_ALC2, 0, 15, 0), +SOC_SINGLE("ALC Capture Decay Time", WM8988_ALC3, 4, 15, 0), +SOC_SINGLE("ALC Capture Attack Time", WM8988_ALC3, 0, 15, 0), +SOC_SINGLE("ALC Capture NG Threshold", WM8988_NGATE, 3, 31, 0), +SOC_ENUM("ALC Capture NG Type", ng_type), +SOC_SINGLE("ALC Capture NG Switch", WM8988_NGATE, 0, 1, 0), + +SOC_SINGLE("ZC Timeout Switch", WM8988_ADCTL1, 0, 1, 0), + +SOC_DOUBLE_R_TLV("Capture Digital Volume", WM8988_LADC, WM8988_RADC, + 0, 255, 0, adc_tlv), +SOC_DOUBLE_R_TLV("Capture Volume", WM8988_LINVOL, WM8988_RINVOL, + 0, 63, 0, pga_tlv), +SOC_DOUBLE_R("Capture ZC Switch", WM8988_LINVOL, WM8988_RINVOL, 6, 1, 0), +SOC_DOUBLE_R("Capture Switch", WM8988_LINVOL, WM8988_RINVOL, 7, 1, 1), + +SOC_ENUM("Playback De-emphasis", deemph), + +SOC_ENUM("Capture Polarity", adcpol), +SOC_SINGLE("Playback 6dB Attenuate", WM8988_ADCDAC, 7, 1, 0), +SOC_SINGLE("Capture 6dB Attenuate", WM8988_ADCDAC, 8, 1, 0), + +SOC_DOUBLE_R_TLV("PCM Volume", WM8988_LDAC, WM8988_RDAC, 0, 255, 0, dac_tlv), + +SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", WM8988_LOUTM1, 4, 7, 1, + bypass_tlv), +SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", WM8988_LOUTM2, 4, 7, 1, + bypass_tlv), +SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", WM8988_ROUTM1, 4, 7, 1, + bypass_tlv), +SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", WM8988_ROUTM2, 4, 7, 1, + bypass_tlv), + +SOC_DOUBLE_R("Output 1 Playback ZC Switch", WM8988_LOUT1V, + WM8988_ROUT1V, 7, 1, 0), +SOC_DOUBLE_R_TLV("Output 1 Playback Volume", WM8988_LOUT1V, WM8988_ROUT1V, + 0, 127, 0, out_tlv), + +SOC_DOUBLE_R("Output 2 Playback ZC Switch", WM8988_LOUT2V, + WM8988_ROUT2V, 7, 1, 0), +SOC_DOUBLE_R_TLV("Output 2 Playback Volume", WM8988_LOUT2V, WM8988_ROUT2V, + 0, 127, 0, out_tlv), + +}; + +/* + * DAPM Controls + */ + +static int wm8988_lrc_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 adctl2 = snd_soc_component_read(component, WM8988_ADCTL2); + + /* Use the DAC to gate LRC if active, otherwise use ADC */ + if (snd_soc_component_read(component, WM8988_PWR2) & 0x180) + adctl2 &= ~0x4; + else + adctl2 |= 0x4; + + return snd_soc_component_write(component, WM8988_ADCTL2, adctl2); +} + +static const char *wm8988_line_texts[] = { + "Line 1", "Line 2", "PGA", "Differential"}; + +static const unsigned int wm8988_line_values[] = { + 0, 1, 3, 4}; + +static const struct soc_enum wm8988_lline_enum = + SOC_VALUE_ENUM_SINGLE(WM8988_LOUTM1, 0, 7, + ARRAY_SIZE(wm8988_line_texts), + wm8988_line_texts, + wm8988_line_values); +static const struct snd_kcontrol_new wm8988_left_line_controls = + SOC_DAPM_ENUM("Route", wm8988_lline_enum); + +static const struct soc_enum wm8988_rline_enum = + SOC_VALUE_ENUM_SINGLE(WM8988_ROUTM1, 0, 7, + ARRAY_SIZE(wm8988_line_texts), + wm8988_line_texts, + wm8988_line_values); +static const struct snd_kcontrol_new wm8988_right_line_controls = + SOC_DAPM_ENUM("Route", wm8988_rline_enum); + +/* Left Mixer */ +static const struct snd_kcontrol_new wm8988_left_mixer_controls[] = { + SOC_DAPM_SINGLE("Playback Switch", WM8988_LOUTM1, 8, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_LOUTM1, 7, 1, 0), + SOC_DAPM_SINGLE("Right Playback Switch", WM8988_LOUTM2, 8, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_LOUTM2, 7, 1, 0), +}; + +/* Right Mixer */ +static const struct snd_kcontrol_new wm8988_right_mixer_controls[] = { + SOC_DAPM_SINGLE("Left Playback Switch", WM8988_ROUTM1, 8, 1, 0), + SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_ROUTM1, 7, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", WM8988_ROUTM2, 8, 1, 0), + SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_ROUTM2, 7, 1, 0), +}; + +static const char *wm8988_pga_sel[] = {"Line 1", "Line 2", "Differential"}; +static const unsigned int wm8988_pga_val[] = { 0, 1, 3 }; + +/* Left PGA Mux */ +static const struct soc_enum wm8988_lpga_enum = + SOC_VALUE_ENUM_SINGLE(WM8988_LADCIN, 6, 3, + ARRAY_SIZE(wm8988_pga_sel), + wm8988_pga_sel, + wm8988_pga_val); +static const struct snd_kcontrol_new wm8988_left_pga_controls = + SOC_DAPM_ENUM("Route", wm8988_lpga_enum); + +/* Right PGA Mux */ +static const struct soc_enum wm8988_rpga_enum = + SOC_VALUE_ENUM_SINGLE(WM8988_RADCIN, 6, 3, + ARRAY_SIZE(wm8988_pga_sel), + wm8988_pga_sel, + wm8988_pga_val); +static const struct snd_kcontrol_new wm8988_right_pga_controls = + SOC_DAPM_ENUM("Route", wm8988_rpga_enum); + +/* Differential Mux */ +static const char *wm8988_diff_sel[] = {"Line 1", "Line 2"}; +static SOC_ENUM_SINGLE_DECL(diffmux, + WM8988_ADCIN, 8, wm8988_diff_sel); +static const struct snd_kcontrol_new wm8988_diffmux_controls = + SOC_DAPM_ENUM("Route", diffmux); + +/* Mono ADC Mux */ +static const char *wm8988_mono_mux[] = {"Stereo", "Mono (Left)", + "Mono (Right)", "Digital Mono"}; +static SOC_ENUM_SINGLE_DECL(monomux, + WM8988_ADCIN, 6, wm8988_mono_mux); +static const struct snd_kcontrol_new wm8988_monomux_controls = + SOC_DAPM_ENUM("Route", monomux); + +static const struct snd_soc_dapm_widget wm8988_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Mic Bias", WM8988_PWR1, 1, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, + &wm8988_diffmux_controls), + SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, + &wm8988_monomux_controls), + SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, + &wm8988_monomux_controls), + + SND_SOC_DAPM_MUX("Left PGA Mux", WM8988_PWR1, 5, 0, + &wm8988_left_pga_controls), + SND_SOC_DAPM_MUX("Right PGA Mux", WM8988_PWR1, 4, 0, + &wm8988_right_pga_controls), + + SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, + &wm8988_left_line_controls), + SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, + &wm8988_right_line_controls), + + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8988_PWR1, 2, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8988_PWR1, 3, 0), + + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8988_PWR2, 7, 0), + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8988_PWR2, 8, 0), + + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + &wm8988_left_mixer_controls[0], + ARRAY_SIZE(wm8988_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + &wm8988_right_mixer_controls[0], + ARRAY_SIZE(wm8988_right_mixer_controls)), + + SND_SOC_DAPM_PGA("Right Out 2", WM8988_PWR2, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 2", WM8988_PWR2, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Out 1", WM8988_PWR2, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left Out 1", WM8988_PWR2, 6, 0, NULL, 0), + + SND_SOC_DAPM_POST("LRC control", wm8988_lrc_control), + + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + SND_SOC_DAPM_OUTPUT("LOUT2"), + SND_SOC_DAPM_OUTPUT("ROUT2"), + SND_SOC_DAPM_OUTPUT("VREF"), + + SND_SOC_DAPM_INPUT("LINPUT1"), + SND_SOC_DAPM_INPUT("LINPUT2"), + SND_SOC_DAPM_INPUT("RINPUT1"), + SND_SOC_DAPM_INPUT("RINPUT2"), +}; + +static const struct snd_soc_dapm_route wm8988_dapm_routes[] = { + + { "Left Line Mux", "Line 1", "LINPUT1" }, + { "Left Line Mux", "Line 2", "LINPUT2" }, + { "Left Line Mux", "PGA", "Left PGA Mux" }, + { "Left Line Mux", "Differential", "Differential Mux" }, + + { "Right Line Mux", "Line 1", "RINPUT1" }, + { "Right Line Mux", "Line 2", "RINPUT2" }, + { "Right Line Mux", "PGA", "Right PGA Mux" }, + { "Right Line Mux", "Differential", "Differential Mux" }, + + { "Left PGA Mux", "Line 1", "LINPUT1" }, + { "Left PGA Mux", "Line 2", "LINPUT2" }, + { "Left PGA Mux", "Differential", "Differential Mux" }, + + { "Right PGA Mux", "Line 1", "RINPUT1" }, + { "Right PGA Mux", "Line 2", "RINPUT2" }, + { "Right PGA Mux", "Differential", "Differential Mux" }, + + { "Differential Mux", "Line 1", "LINPUT1" }, + { "Differential Mux", "Line 1", "RINPUT1" }, + { "Differential Mux", "Line 2", "LINPUT2" }, + { "Differential Mux", "Line 2", "RINPUT2" }, + + { "Left ADC Mux", "Stereo", "Left PGA Mux" }, + { "Left ADC Mux", "Mono (Left)", "Left PGA Mux" }, + { "Left ADC Mux", "Digital Mono", "Left PGA Mux" }, + + { "Right ADC Mux", "Stereo", "Right PGA Mux" }, + { "Right ADC Mux", "Mono (Right)", "Right PGA Mux" }, + { "Right ADC Mux", "Digital Mono", "Right PGA Mux" }, + + { "Left ADC", NULL, "Left ADC Mux" }, + { "Right ADC", NULL, "Right ADC Mux" }, + + { "Left Line Mux", "Line 1", "LINPUT1" }, + { "Left Line Mux", "Line 2", "LINPUT2" }, + { "Left Line Mux", "PGA", "Left PGA Mux" }, + { "Left Line Mux", "Differential", "Differential Mux" }, + + { "Right Line Mux", "Line 1", "RINPUT1" }, + { "Right Line Mux", "Line 2", "RINPUT2" }, + { "Right Line Mux", "PGA", "Right PGA Mux" }, + { "Right Line Mux", "Differential", "Differential Mux" }, + + { "Left Mixer", "Playback Switch", "Left DAC" }, + { "Left Mixer", "Left Bypass Switch", "Left Line Mux" }, + { "Left Mixer", "Right Playback Switch", "Right DAC" }, + { "Left Mixer", "Right Bypass Switch", "Right Line Mux" }, + + { "Right Mixer", "Left Playback Switch", "Left DAC" }, + { "Right Mixer", "Left Bypass Switch", "Left Line Mux" }, + { "Right Mixer", "Playback Switch", "Right DAC" }, + { "Right Mixer", "Right Bypass Switch", "Right Line Mux" }, + + { "Left Out 1", NULL, "Left Mixer" }, + { "LOUT1", NULL, "Left Out 1" }, + { "Right Out 1", NULL, "Right Mixer" }, + { "ROUT1", NULL, "Right Out 1" }, + + { "Left Out 2", NULL, "Left Mixer" }, + { "LOUT2", NULL, "Left Out 2" }, + { "Right Out 2", NULL, "Right Mixer" }, + { "ROUT2", NULL, "Right Out 2" }, +}; + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u8 sr:5; + u8 usb:1; +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 8k */ + {12288000, 8000, 1536, 0x6, 0x0}, + {11289600, 8000, 1408, 0x16, 0x0}, + {18432000, 8000, 2304, 0x7, 0x0}, + {16934400, 8000, 2112, 0x17, 0x0}, + {12000000, 8000, 1500, 0x6, 0x1}, + + /* 11.025k */ + {11289600, 11025, 1024, 0x18, 0x0}, + {16934400, 11025, 1536, 0x19, 0x0}, + {12000000, 11025, 1088, 0x19, 0x1}, + + /* 16k */ + {12288000, 16000, 768, 0xa, 0x0}, + {18432000, 16000, 1152, 0xb, 0x0}, + {12000000, 16000, 750, 0xa, 0x1}, + + /* 22.05k */ + {11289600, 22050, 512, 0x1a, 0x0}, + {16934400, 22050, 768, 0x1b, 0x0}, + {12000000, 22050, 544, 0x1b, 0x1}, + + /* 32k */ + {12288000, 32000, 384, 0xc, 0x0}, + {18432000, 32000, 576, 0xd, 0x0}, + {12000000, 32000, 375, 0xa, 0x1}, + + /* 44.1k */ + {11289600, 44100, 256, 0x10, 0x0}, + {16934400, 44100, 384, 0x11, 0x0}, + {12000000, 44100, 272, 0x11, 0x1}, + + /* 48k */ + {12288000, 48000, 256, 0x0, 0x0}, + {18432000, 48000, 384, 0x1, 0x0}, + {12000000, 48000, 250, 0x0, 0x1}, + + /* 88.2k */ + {11289600, 88200, 128, 0x1e, 0x0}, + {16934400, 88200, 192, 0x1f, 0x0}, + {12000000, 88200, 136, 0x1f, 0x1}, + + /* 96k */ + {12288000, 96000, 128, 0xe, 0x0}, + {18432000, 96000, 192, 0xf, 0x0}, + {12000000, 96000, 125, 0xe, 0x1}, +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + + return -EINVAL; +} + +/* The set of rates we can generate from the above for each SYSCLK */ + +static const unsigned int rates_12288[] = { + 8000, 12000, 16000, 24000, 32000, 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static const unsigned int rates_112896[] = { + 8000, 11025, 22050, 44100, +}; + +static const struct snd_pcm_hw_constraint_list constraints_112896 = { + .count = ARRAY_SIZE(rates_112896), + .list = rates_112896, +}; + +static const unsigned int rates_12[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 41100, 48000, + 48000, 88235, 96000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_12 = { + .count = ARRAY_SIZE(rates_12), + .list = rates_12, +}; + +/* + * Note that this should be called from init rather than from hw_params. + */ +static int wm8988_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8988_priv *wm8988 = snd_soc_component_get_drvdata(component); + + switch (freq) { + case 11289600: + case 18432000: + case 22579200: + case 36864000: + wm8988->sysclk_constraints = &constraints_112896; + wm8988->sysclk = freq; + return 0; + + case 12288000: + case 16934400: + case 24576000: + case 33868800: + wm8988->sysclk_constraints = &constraints_12288; + wm8988->sysclk = freq; + return 0; + + case 12000000: + case 24000000: + wm8988->sysclk_constraints = &constraints_12; + wm8988->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0040; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8988_IFACE, iface); + return 0; +} + +static int wm8988_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8988_priv *wm8988 = snd_soc_component_get_drvdata(component); + + /* The set of sample rates that can be supported depends on the + * MCLK supplied to the CODEC - enforce this. + */ + if (!wm8988->sysclk) { + dev_err(component->dev, + "No MCLK configured, call set_sysclk() on init\n"); + return -EINVAL; + } + + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + wm8988->sysclk_constraints); + + return 0; +} + +static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8988_priv *wm8988 = snd_soc_component_get_drvdata(component); + u16 iface = snd_soc_component_read(component, WM8988_IFACE) & 0x1f3; + u16 srate = snd_soc_component_read(component, WM8988_SRATE) & 0x180; + int coeff; + + coeff = get_coeff(wm8988->sysclk, params_rate(params)); + if (coeff < 0) { + coeff = get_coeff(wm8988->sysclk / 2, params_rate(params)); + srate |= 0x40; + } + if (coeff < 0) { + dev_err(component->dev, + "Unable to configure sample rate %dHz with %dHz MCLK\n", + params_rate(params), wm8988->sysclk); + return coeff; + } + + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + iface |= 0x0004; + break; + case 24: + iface |= 0x0008; + break; + case 32: + iface |= 0x000c; + break; + } + + /* set iface & srate */ + snd_soc_component_write(component, WM8988_IFACE, iface); + if (coeff >= 0) + snd_soc_component_write(component, WM8988_SRATE, srate | + (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb); + + return 0; +} + +static int wm8988_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 mute_reg = snd_soc_component_read(component, WM8988_ADCDAC) & 0xfff7; + + if (mute) + snd_soc_component_write(component, WM8988_ADCDAC, mute_reg | 0x8); + else + snd_soc_component_write(component, WM8988_ADCDAC, mute_reg); + return 0; +} + +static int wm8988_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8988_priv *wm8988 = snd_soc_component_get_drvdata(component); + u16 pwr_reg = snd_soc_component_read(component, WM8988_PWR1) & ~0x1c1; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VREF, VMID=2x50k, digital enabled */ + snd_soc_component_write(component, WM8988_PWR1, pwr_reg | 0x00c0); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + regcache_sync(wm8988->regmap); + + /* VREF, VMID=2x5k */ + snd_soc_component_write(component, WM8988_PWR1, pwr_reg | 0x1c1); + + /* Charge caps */ + msleep(100); + } + + /* VREF, VMID=2*500k, digital stopped */ + snd_soc_component_write(component, WM8988_PWR1, pwr_reg | 0x0141); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, WM8988_PWR1, 0x0000); + break; + } + return 0; +} + +#define WM8988_RATES SNDRV_PCM_RATE_8000_96000 + +#define WM8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8988_ops = { + .startup = wm8988_pcm_startup, + .hw_params = wm8988_pcm_hw_params, + .set_fmt = wm8988_set_dai_fmt, + .set_sysclk = wm8988_set_dai_sysclk, + .mute_stream = wm8988_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8988_dai = { + .name = "wm8988-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8988_RATES, + .formats = WM8988_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8988_RATES, + .formats = WM8988_FORMATS, + }, + .ops = &wm8988_ops, + .symmetric_rate = 1, +}; + +static int wm8988_probe(struct snd_soc_component *component) +{ + int ret = 0; + + ret = wm8988_reset(component); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset\n"); + return ret; + } + + /* set the update bits (we always update left then right) */ + snd_soc_component_update_bits(component, WM8988_RADC, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8988_RDAC, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8988_ROUT1V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8988_ROUT2V, 0x0100, 0x0100); + snd_soc_component_update_bits(component, WM8988_RINVOL, 0x0100, 0x0100); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8988 = { + .probe = wm8988_probe, + .set_bias_level = wm8988_set_bias_level, + .controls = wm8988_snd_controls, + .num_controls = ARRAY_SIZE(wm8988_snd_controls), + .dapm_widgets = wm8988_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8988_dapm_widgets), + .dapm_routes = wm8988_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8988_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8988_regmap = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = WM8988_LPPB, + .writeable_reg = wm8988_writeable, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm8988_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8988_reg_defaults), +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8988_spi_probe(struct spi_device *spi) +{ + struct wm8988_priv *wm8988; + int ret; + + wm8988 = devm_kzalloc(&spi->dev, sizeof(struct wm8988_priv), + GFP_KERNEL); + if (wm8988 == NULL) + return -ENOMEM; + + wm8988->regmap = devm_regmap_init_spi(spi, &wm8988_regmap); + if (IS_ERR(wm8988->regmap)) { + ret = PTR_ERR(wm8988->regmap); + dev_err(&spi->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + spi_set_drvdata(spi, wm8988); + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8988, &wm8988_dai, 1); + return ret; +} + +static struct spi_driver wm8988_spi_driver = { + .driver = { + .name = "wm8988", + }, + .probe = wm8988_spi_probe, +}; +#endif /* CONFIG_SPI_MASTER */ + +#if IS_ENABLED(CONFIG_I2C) +static int wm8988_i2c_probe(struct i2c_client *i2c) +{ + struct wm8988_priv *wm8988; + int ret; + + wm8988 = devm_kzalloc(&i2c->dev, sizeof(struct wm8988_priv), + GFP_KERNEL); + if (wm8988 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm8988); + + wm8988->regmap = devm_regmap_init_i2c(i2c, &wm8988_regmap); + if (IS_ERR(wm8988->regmap)) { + ret = PTR_ERR(wm8988->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8988, &wm8988_dai, 1); + return ret; +} + +static const struct i2c_device_id wm8988_i2c_id[] = { + { "wm8988", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id); + +static struct i2c_driver wm8988_i2c_driver = { + .driver = { + .name = "wm8988", + }, + .probe = wm8988_i2c_probe, + .id_table = wm8988_i2c_id, +}; +#endif + +static int __init wm8988_modinit(void) +{ + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8988_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8988 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8988_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8988 SPI driver: %d\n", + ret); + } +#endif + return ret; +} +module_init(wm8988_modinit); + +static void __exit wm8988_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8988_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8988_spi_driver); +#endif +} +module_exit(wm8988_exit); + + +MODULE_DESCRIPTION("ASoC WM8988 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8988.h b/sound/soc/codecs/wm8988.h new file mode 100644 index 0000000000..bd8a30c134 --- /dev/null +++ b/sound/soc/codecs/wm8988.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * Based on WM8753.h + */ + +#ifndef _WM8988_H +#define _WM8988_H + +/* WM8988 register space */ + +#define WM8988_LINVOL 0x00 +#define WM8988_RINVOL 0x01 +#define WM8988_LOUT1V 0x02 +#define WM8988_ROUT1V 0x03 +#define WM8988_ADCDAC 0x05 +#define WM8988_IFACE 0x07 +#define WM8988_SRATE 0x08 +#define WM8988_LDAC 0x0a +#define WM8988_RDAC 0x0b +#define WM8988_BASS 0x0c +#define WM8988_TREBLE 0x0d +#define WM8988_RESET 0x0f +#define WM8988_3D 0x10 +#define WM8988_ALC1 0x11 +#define WM8988_ALC2 0x12 +#define WM8988_ALC3 0x13 +#define WM8988_NGATE 0x14 +#define WM8988_LADC 0x15 +#define WM8988_RADC 0x16 +#define WM8988_ADCTL1 0x17 +#define WM8988_ADCTL2 0x18 +#define WM8988_PWR1 0x19 +#define WM8988_PWR2 0x1a +#define WM8988_ADCTL3 0x1b +#define WM8988_ADCIN 0x1f +#define WM8988_LADCIN 0x20 +#define WM8988_RADCIN 0x21 +#define WM8988_LOUTM1 0x22 +#define WM8988_LOUTM2 0x23 +#define WM8988_ROUTM1 0x24 +#define WM8988_ROUTM2 0x25 +#define WM8988_LOUT2V 0x28 +#define WM8988_ROUT2V 0x29 +#define WM8988_LPPB 0x43 +#define WM8988_NUM_REG 0x44 + +#define WM8988_SYSCLK 0 + +#endif diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c new file mode 100644 index 0000000000..5a8e765090 --- /dev/null +++ b/sound/soc/codecs/wm8990.c @@ -0,0 +1,1258 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * wm8990.c -- WM8990 ALSA Soc Audio driver + * + * Copyright 2008 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8990.h" + +/* codec private data */ +struct wm8990_priv { + struct regmap *regmap; + unsigned int sysclk; + unsigned int pcmclk; +}; + +#define wm8990_reset(c) snd_soc_component_write(c, WM8990_RESET, 0) + +static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1650, 3000, 0); + +static const DECLARE_TLV_DB_SCALE(out_mix_tlv, 0, -2100, 0); + +static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -7300, 600, 0); + +static const DECLARE_TLV_DB_SCALE(out_dac_tlv, -7163, 0, 0); + +static const DECLARE_TLV_DB_SCALE(in_adc_tlv, -7163, 1763, 0); + +static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0); + +static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int reg = mc->reg; + int ret; + u16 val; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + /* now hit the volume update bits (always bit 8) */ + val = snd_soc_component_read(component, reg); + return snd_soc_component_write(component, reg, val | 0x0100); +} + +#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\ + tlv_array) \ + SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \ + snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array) + + +static const char *wm8990_digital_sidetone[] = + {"None", "Left ADC", "Right ADC", "Reserved"}; + +static SOC_ENUM_SINGLE_DECL(wm8990_left_digital_sidetone_enum, + WM8990_DIGITAL_SIDE_TONE, + WM8990_ADC_TO_DACL_SHIFT, + wm8990_digital_sidetone); + +static SOC_ENUM_SINGLE_DECL(wm8990_right_digital_sidetone_enum, + WM8990_DIGITAL_SIDE_TONE, + WM8990_ADC_TO_DACR_SHIFT, + wm8990_digital_sidetone); + +static const char *wm8990_adcmode[] = + {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"}; + +static SOC_ENUM_SINGLE_DECL(wm8990_right_adcmode_enum, + WM8990_ADC_CTRL, + WM8990_ADC_HPF_CUT_SHIFT, + wm8990_adcmode); + +static const struct snd_kcontrol_new wm8990_snd_controls[] = { +/* INMIXL */ +SOC_SINGLE("LIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L12MNBST_BIT, 1, 0), +SOC_SINGLE("LIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_L34MNBST_BIT, 1, 0), +/* INMIXR */ +SOC_SINGLE("RIN12 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R12MNBST_BIT, 1, 0), +SOC_SINGLE("RIN34 PGA Boost", WM8990_INPUT_MIXER3, WM8990_R34MNBST_BIT, 1, 0), + +/* LOMIX */ +SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER3, + WM8990_LLI3LOVOL_SHIFT, WM8990_LLI3LOVOL_MASK, 1, out_mix_tlv), +SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3, + WM8990_LR12LOVOL_SHIFT, WM8990_LR12LOVOL_MASK, 1, out_mix_tlv), +SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER3, + WM8990_LL12LOVOL_SHIFT, WM8990_LL12LOVOL_MASK, 1, out_mix_tlv), +SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER5, + WM8990_LRI3LOVOL_SHIFT, WM8990_LRI3LOVOL_MASK, 1, out_mix_tlv), +SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER5, + WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv), +SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER5, + WM8990_LRBLOVOL_SHIFT, WM8990_LRBLOVOL_MASK, 1, out_mix_tlv), + +/* ROMIX */ +SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8990_OUTPUT_MIXER4, + WM8990_RRI3ROVOL_SHIFT, WM8990_RRI3ROVOL_MASK, 1, out_mix_tlv), +SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4, + WM8990_RL12ROVOL_SHIFT, WM8990_RL12ROVOL_MASK, 1, out_mix_tlv), +SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8990_OUTPUT_MIXER4, + WM8990_RR12ROVOL_SHIFT, WM8990_RR12ROVOL_MASK, 1, out_mix_tlv), +SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8990_OUTPUT_MIXER6, + WM8990_RLI3ROVOL_SHIFT, WM8990_RLI3ROVOL_MASK, 1, out_mix_tlv), +SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8990_OUTPUT_MIXER6, + WM8990_RLBROVOL_SHIFT, WM8990_RLBROVOL_MASK, 1, out_mix_tlv), +SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8990_OUTPUT_MIXER6, + WM8990_RRBROVOL_SHIFT, WM8990_RRBROVOL_MASK, 1, out_mix_tlv), + +/* LOUT */ +SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8990_LEFT_OUTPUT_VOLUME, + WM8990_LOUTVOL_SHIFT, WM8990_LOUTVOL_MASK, 0, out_pga_tlv), +SOC_SINGLE("LOUT ZC", WM8990_LEFT_OUTPUT_VOLUME, WM8990_LOZC_BIT, 1, 0), + +/* ROUT */ +SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8990_RIGHT_OUTPUT_VOLUME, + WM8990_ROUTVOL_SHIFT, WM8990_ROUTVOL_MASK, 0, out_pga_tlv), +SOC_SINGLE("ROUT ZC", WM8990_RIGHT_OUTPUT_VOLUME, WM8990_ROZC_BIT, 1, 0), + +/* LOPGA */ +SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8990_LEFT_OPGA_VOLUME, + WM8990_LOPGAVOL_SHIFT, WM8990_LOPGAVOL_MASK, 0, out_pga_tlv), +SOC_SINGLE("LOPGA ZC Switch", WM8990_LEFT_OPGA_VOLUME, + WM8990_LOPGAZC_BIT, 1, 0), + +/* ROPGA */ +SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8990_RIGHT_OPGA_VOLUME, + WM8990_ROPGAVOL_SHIFT, WM8990_ROPGAVOL_MASK, 0, out_pga_tlv), +SOC_SINGLE("ROPGA ZC Switch", WM8990_RIGHT_OPGA_VOLUME, + WM8990_ROPGAZC_BIT, 1, 0), + +SOC_SINGLE("LON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME, + WM8990_LONMUTE_BIT, 1, 0), +SOC_SINGLE("LOP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME, + WM8990_LOPMUTE_BIT, 1, 0), +SOC_SINGLE("LOP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME, + WM8990_LOATTN_BIT, 1, 0), +SOC_SINGLE("RON Mute Switch", WM8990_LINE_OUTPUTS_VOLUME, + WM8990_RONMUTE_BIT, 1, 0), +SOC_SINGLE("ROP Mute Switch", WM8990_LINE_OUTPUTS_VOLUME, + WM8990_ROPMUTE_BIT, 1, 0), +SOC_SINGLE("ROP Attenuation Switch", WM8990_LINE_OUTPUTS_VOLUME, + WM8990_ROATTN_BIT, 1, 0), + +SOC_SINGLE("OUT3 Mute Switch", WM8990_OUT3_4_VOLUME, + WM8990_OUT3MUTE_BIT, 1, 0), +SOC_SINGLE("OUT3 Attenuation Switch", WM8990_OUT3_4_VOLUME, + WM8990_OUT3ATTN_BIT, 1, 0), + +SOC_SINGLE("OUT4 Mute Switch", WM8990_OUT3_4_VOLUME, + WM8990_OUT4MUTE_BIT, 1, 0), +SOC_SINGLE("OUT4 Attenuation Switch", WM8990_OUT3_4_VOLUME, + WM8990_OUT4ATTN_BIT, 1, 0), + +SOC_SINGLE("Speaker Mode Switch", WM8990_CLASSD1, + WM8990_CDMODE_BIT, 1, 0), + +SOC_SINGLE("Speaker Output Attenuation Volume", WM8990_SPEAKER_VOLUME, + WM8990_SPKATTN_SHIFT, WM8990_SPKATTN_MASK, 0), +SOC_SINGLE("Speaker DC Boost Volume", WM8990_CLASSD3, + WM8990_DCGAIN_SHIFT, WM8990_DCGAIN_MASK, 0), +SOC_SINGLE("Speaker AC Boost Volume", WM8990_CLASSD3, + WM8990_ACGAIN_SHIFT, WM8990_ACGAIN_MASK, 0), +SOC_SINGLE_TLV("Speaker Volume", WM8990_CLASSD4, + WM8990_SPKVOL_SHIFT, WM8990_SPKVOL_MASK, 0, out_pga_tlv), +SOC_SINGLE("Speaker ZC Switch", WM8990_CLASSD4, + WM8990_SPKZC_SHIFT, WM8990_SPKZC_MASK, 0), + +SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume", + WM8990_LEFT_DAC_DIGITAL_VOLUME, + WM8990_DACL_VOL_SHIFT, + WM8990_DACL_VOL_MASK, + 0, + out_dac_tlv), + +SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume", + WM8990_RIGHT_DAC_DIGITAL_VOLUME, + WM8990_DACR_VOL_SHIFT, + WM8990_DACR_VOL_MASK, + 0, + out_dac_tlv), + +SOC_ENUM("Left Digital Sidetone", wm8990_left_digital_sidetone_enum), +SOC_ENUM("Right Digital Sidetone", wm8990_right_digital_sidetone_enum), + +SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE, + WM8990_ADCL_DAC_SVOL_SHIFT, WM8990_ADCL_DAC_SVOL_MASK, 0, + out_sidetone_tlv), +SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8990_DIGITAL_SIDE_TONE, + WM8990_ADCR_DAC_SVOL_SHIFT, WM8990_ADCR_DAC_SVOL_MASK, 0, + out_sidetone_tlv), + +SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8990_ADC_CTRL, + WM8990_ADC_HPF_ENA_BIT, 1, 0), + +SOC_ENUM("ADC HPF Mode", wm8990_right_adcmode_enum), + +SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume", + WM8990_LEFT_ADC_DIGITAL_VOLUME, + WM8990_ADCL_VOL_SHIFT, + WM8990_ADCL_VOL_MASK, + 0, + in_adc_tlv), + +SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume", + WM8990_RIGHT_ADC_DIGITAL_VOLUME, + WM8990_ADCR_VOL_SHIFT, + WM8990_ADCR_VOL_MASK, + 0, + in_adc_tlv), + +SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume", + WM8990_LEFT_LINE_INPUT_1_2_VOLUME, + WM8990_LIN12VOL_SHIFT, + WM8990_LIN12VOL_MASK, + 0, + in_pga_tlv), + +SOC_SINGLE("LIN12 ZC Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME, + WM8990_LI12ZC_BIT, 1, 0), + +SOC_SINGLE("LIN12 Mute Switch", WM8990_LEFT_LINE_INPUT_1_2_VOLUME, + WM8990_LI12MUTE_BIT, 1, 0), + +SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume", + WM8990_LEFT_LINE_INPUT_3_4_VOLUME, + WM8990_LIN34VOL_SHIFT, + WM8990_LIN34VOL_MASK, + 0, + in_pga_tlv), + +SOC_SINGLE("LIN34 ZC Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME, + WM8990_LI34ZC_BIT, 1, 0), + +SOC_SINGLE("LIN34 Mute Switch", WM8990_LEFT_LINE_INPUT_3_4_VOLUME, + WM8990_LI34MUTE_BIT, 1, 0), + +SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume", + WM8990_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8990_RIN12VOL_SHIFT, + WM8990_RIN12VOL_MASK, + 0, + in_pga_tlv), + +SOC_SINGLE("RIN12 ZC Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8990_RI12ZC_BIT, 1, 0), + +SOC_SINGLE("RIN12 Mute Switch", WM8990_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8990_RI12MUTE_BIT, 1, 0), + +SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume", + WM8990_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8990_RIN34VOL_SHIFT, + WM8990_RIN34VOL_MASK, + 0, + in_pga_tlv), + +SOC_SINGLE("RIN34 ZC Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8990_RI34ZC_BIT, 1, 0), + +SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8990_RI34MUTE_BIT, 1, 0), + +}; + +/* + * _DAPM_ Controls + */ + +static int outmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u32 reg_shift = kcontrol->private_value & 0xfff; + int ret = 0; + u16 reg; + + switch (reg_shift) { + case WM8990_SPEAKER_MIXER | (WM8990_LDSPK_BIT << 8) : + reg = snd_soc_component_read(component, WM8990_OUTPUT_MIXER1); + if (reg & WM8990_LDLO) { + printk(KERN_WARNING + "Cannot set as Output Mixer 1 LDLO Set\n"); + ret = -1; + } + break; + case WM8990_SPEAKER_MIXER | (WM8990_RDSPK_BIT << 8): + reg = snd_soc_component_read(component, WM8990_OUTPUT_MIXER2); + if (reg & WM8990_RDRO) { + printk(KERN_WARNING + "Cannot set as Output Mixer 2 RDRO Set\n"); + ret = -1; + } + break; + case WM8990_OUTPUT_MIXER1 | (WM8990_LDLO_BIT << 8): + reg = snd_soc_component_read(component, WM8990_SPEAKER_MIXER); + if (reg & WM8990_LDSPK) { + printk(KERN_WARNING + "Cannot set as Speaker Mixer LDSPK Set\n"); + ret = -1; + } + break; + case WM8990_OUTPUT_MIXER2 | (WM8990_RDRO_BIT << 8): + reg = snd_soc_component_read(component, WM8990_SPEAKER_MIXER); + if (reg & WM8990_RDSPK) { + printk(KERN_WARNING + "Cannot set as Speaker Mixer RDSPK Set\n"); + ret = -1; + } + break; + } + + return ret; +} + +/* INMIX dB values */ +static const DECLARE_TLV_DB_SCALE(in_mix_tlv, -1200, 600, 0); + +/* Left In PGA Connections */ +static const struct snd_kcontrol_new wm8990_dapm_lin12_pga_controls[] = { +SOC_DAPM_SINGLE("LIN1 Switch", WM8990_INPUT_MIXER2, WM8990_LMN1_BIT, 1, 0), +SOC_DAPM_SINGLE("LIN2 Switch", WM8990_INPUT_MIXER2, WM8990_LMP2_BIT, 1, 0), +}; + +static const struct snd_kcontrol_new wm8990_dapm_lin34_pga_controls[] = { +SOC_DAPM_SINGLE("LIN3 Switch", WM8990_INPUT_MIXER2, WM8990_LMN3_BIT, 1, 0), +SOC_DAPM_SINGLE("LIN4 Switch", WM8990_INPUT_MIXER2, WM8990_LMP4_BIT, 1, 0), +}; + +/* Right In PGA Connections */ +static const struct snd_kcontrol_new wm8990_dapm_rin12_pga_controls[] = { +SOC_DAPM_SINGLE("RIN1 Switch", WM8990_INPUT_MIXER2, WM8990_RMN1_BIT, 1, 0), +SOC_DAPM_SINGLE("RIN2 Switch", WM8990_INPUT_MIXER2, WM8990_RMP2_BIT, 1, 0), +}; + +static const struct snd_kcontrol_new wm8990_dapm_rin34_pga_controls[] = { +SOC_DAPM_SINGLE("RIN3 Switch", WM8990_INPUT_MIXER2, WM8990_RMN3_BIT, 1, 0), +SOC_DAPM_SINGLE("RIN4 Switch", WM8990_INPUT_MIXER2, WM8990_RMP4_BIT, 1, 0), +}; + +/* INMIXL */ +static const struct snd_kcontrol_new wm8990_dapm_inmixl_controls[] = { +SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8990_INPUT_MIXER3, + WM8990_LDBVOL_SHIFT, WM8990_LDBVOL_MASK, 0, in_mix_tlv), +SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8990_INPUT_MIXER5, WM8990_LI2BVOL_SHIFT, + 7, 0, in_mix_tlv), +SOC_DAPM_SINGLE("LINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT, + 1, 0), +SOC_DAPM_SINGLE("LINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT, + 1, 0), +}; + +/* INMIXR */ +static const struct snd_kcontrol_new wm8990_dapm_inmixr_controls[] = { +SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8990_INPUT_MIXER4, + WM8990_RDBVOL_SHIFT, WM8990_RDBVOL_MASK, 0, in_mix_tlv), +SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8990_INPUT_MIXER6, WM8990_RI2BVOL_SHIFT, + 7, 0, in_mix_tlv), +SOC_DAPM_SINGLE("RINPGA12 Switch", WM8990_INPUT_MIXER3, WM8990_L12MNB_BIT, + 1, 0), +SOC_DAPM_SINGLE("RINPGA34 Switch", WM8990_INPUT_MIXER3, WM8990_L34MNB_BIT, + 1, 0), +}; + +/* AINLMUX */ +static const char *wm8990_ainlmux[] = + {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"}; + +static SOC_ENUM_SINGLE_DECL(wm8990_ainlmux_enum, + WM8990_INPUT_MIXER1, WM8990_AINLMODE_SHIFT, + wm8990_ainlmux); + +static const struct snd_kcontrol_new wm8990_dapm_ainlmux_controls = +SOC_DAPM_ENUM("Route", wm8990_ainlmux_enum); + +/* DIFFINL */ + +/* AINRMUX */ +static const char *wm8990_ainrmux[] = + {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"}; + +static SOC_ENUM_SINGLE_DECL(wm8990_ainrmux_enum, + WM8990_INPUT_MIXER1, WM8990_AINRMODE_SHIFT, + wm8990_ainrmux); + +static const struct snd_kcontrol_new wm8990_dapm_ainrmux_controls = +SOC_DAPM_ENUM("Route", wm8990_ainrmux_enum); + +/* LOMIX */ +static const struct snd_kcontrol_new wm8990_dapm_lomix_controls[] = { +SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER1, + WM8990_LRBLO_BIT, 1, 0), +SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER1, + WM8990_LLBLO_BIT, 1, 0), +SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER1, + WM8990_LRI3LO_BIT, 1, 0), +SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER1, + WM8990_LLI3LO_BIT, 1, 0), +SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1, + WM8990_LR12LO_BIT, 1, 0), +SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER1, + WM8990_LL12LO_BIT, 1, 0), +SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8990_OUTPUT_MIXER1, + WM8990_LDLO_BIT, 1, 0), +}; + +/* ROMIX */ +static const struct snd_kcontrol_new wm8990_dapm_romix_controls[] = { +SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8990_OUTPUT_MIXER2, + WM8990_RLBRO_BIT, 1, 0), +SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8990_OUTPUT_MIXER2, + WM8990_RRBRO_BIT, 1, 0), +SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8990_OUTPUT_MIXER2, + WM8990_RLI3RO_BIT, 1, 0), +SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8990_OUTPUT_MIXER2, + WM8990_RRI3RO_BIT, 1, 0), +SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2, + WM8990_RL12RO_BIT, 1, 0), +SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8990_OUTPUT_MIXER2, + WM8990_RR12RO_BIT, 1, 0), +SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8990_OUTPUT_MIXER2, + WM8990_RDRO_BIT, 1, 0), +}; + +/* LONMIX */ +static const struct snd_kcontrol_new wm8990_dapm_lonmix_controls[] = { +SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1, + WM8990_LLOPGALON_BIT, 1, 0), +SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER1, + WM8990_LROPGALON_BIT, 1, 0), +SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8990_LINE_MIXER1, + WM8990_LOPLON_BIT, 1, 0), +}; + +/* LOPMIX */ +static const struct snd_kcontrol_new wm8990_dapm_lopmix_controls[] = { +SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER1, + WM8990_LR12LOP_BIT, 1, 0), +SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER1, + WM8990_LL12LOP_BIT, 1, 0), +SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8990_LINE_MIXER1, + WM8990_LLOPGALOP_BIT, 1, 0), +}; + +/* RONMIX */ +static const struct snd_kcontrol_new wm8990_dapm_ronmix_controls[] = { +SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2, + WM8990_RROPGARON_BIT, 1, 0), +SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8990_LINE_MIXER2, + WM8990_RLOPGARON_BIT, 1, 0), +SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8990_LINE_MIXER2, + WM8990_ROPRON_BIT, 1, 0), +}; + +/* ROPMIX */ +static const struct snd_kcontrol_new wm8990_dapm_ropmix_controls[] = { +SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8990_LINE_MIXER2, + WM8990_RL12ROP_BIT, 1, 0), +SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8990_LINE_MIXER2, + WM8990_RR12ROP_BIT, 1, 0), +SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8990_LINE_MIXER2, + WM8990_RROPGAROP_BIT, 1, 0), +}; + +/* OUT3MIX */ +static const struct snd_kcontrol_new wm8990_dapm_out3mix_controls[] = { +SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER, + WM8990_LI4O3_BIT, 1, 0), +SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8990_OUT3_4_MIXER, + WM8990_LPGAO3_BIT, 1, 0), +}; + +/* OUT4MIX */ +static const struct snd_kcontrol_new wm8990_dapm_out4mix_controls[] = { +SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8990_OUT3_4_MIXER, + WM8990_RPGAO4_BIT, 1, 0), +SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8990_OUT3_4_MIXER, + WM8990_RI4O4_BIT, 1, 0), +}; + +/* SPKMIX */ +static const struct snd_kcontrol_new wm8990_dapm_spkmix_controls[] = { +SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8990_SPEAKER_MIXER, + WM8990_LI2SPK_BIT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8990_SPEAKER_MIXER, + WM8990_LB2SPK_BIT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8990_SPEAKER_MIXER, + WM8990_LOPGASPK_BIT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8990_SPEAKER_MIXER, + WM8990_LDSPK_BIT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8990_SPEAKER_MIXER, + WM8990_RDSPK_BIT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8990_SPEAKER_MIXER, + WM8990_ROPGASPK_BIT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8990_SPEAKER_MIXER, + WM8990_RL12ROP_BIT, 1, 0), +SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8990_SPEAKER_MIXER, + WM8990_RI2SPK_BIT, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8990_dapm_widgets[] = { +/* Input Side */ +/* Input Lines */ +SND_SOC_DAPM_INPUT("LIN1"), +SND_SOC_DAPM_INPUT("LIN2"), +SND_SOC_DAPM_INPUT("LIN3"), +SND_SOC_DAPM_INPUT("LIN4/RXN"), +SND_SOC_DAPM_INPUT("RIN3"), +SND_SOC_DAPM_INPUT("RIN4/RXP"), +SND_SOC_DAPM_INPUT("RIN1"), +SND_SOC_DAPM_INPUT("RIN2"), +SND_SOC_DAPM_INPUT("Internal ADC Source"), + +SND_SOC_DAPM_SUPPLY("INL", WM8990_POWER_MANAGEMENT_2, WM8990_AINL_ENA_BIT, 0, + NULL, 0), +SND_SOC_DAPM_SUPPLY("INR", WM8990_POWER_MANAGEMENT_2, WM8990_AINR_ENA_BIT, 0, + NULL, 0), + +/* DACs */ +SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8990_POWER_MANAGEMENT_2, + WM8990_ADCL_ENA_BIT, 0), +SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8990_POWER_MANAGEMENT_2, + WM8990_ADCR_ENA_BIT, 0), + +/* Input PGAs */ +SND_SOC_DAPM_MIXER("LIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN12_ENA_BIT, + 0, &wm8990_dapm_lin12_pga_controls[0], + ARRAY_SIZE(wm8990_dapm_lin12_pga_controls)), +SND_SOC_DAPM_MIXER("LIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_LIN34_ENA_BIT, + 0, &wm8990_dapm_lin34_pga_controls[0], + ARRAY_SIZE(wm8990_dapm_lin34_pga_controls)), +SND_SOC_DAPM_MIXER("RIN12 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN12_ENA_BIT, + 0, &wm8990_dapm_rin12_pga_controls[0], + ARRAY_SIZE(wm8990_dapm_rin12_pga_controls)), +SND_SOC_DAPM_MIXER("RIN34 PGA", WM8990_POWER_MANAGEMENT_2, WM8990_RIN34_ENA_BIT, + 0, &wm8990_dapm_rin34_pga_controls[0], + ARRAY_SIZE(wm8990_dapm_rin34_pga_controls)), + +/* INMIXL */ +SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0, + &wm8990_dapm_inmixl_controls[0], + ARRAY_SIZE(wm8990_dapm_inmixl_controls)), + +/* AINLMUX */ +SND_SOC_DAPM_MUX("AINLMUX", SND_SOC_NOPM, 0, 0, &wm8990_dapm_ainlmux_controls), + +/* INMIXR */ +SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0, + &wm8990_dapm_inmixr_controls[0], + ARRAY_SIZE(wm8990_dapm_inmixr_controls)), + +/* AINRMUX */ +SND_SOC_DAPM_MUX("AINRMUX", SND_SOC_NOPM, 0, 0, &wm8990_dapm_ainrmux_controls), + +/* Output Side */ +/* DACs */ +SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8990_POWER_MANAGEMENT_3, + WM8990_DACL_ENA_BIT, 0), +SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8990_POWER_MANAGEMENT_3, + WM8990_DACR_ENA_BIT, 0), + +/* LOMIX */ +SND_SOC_DAPM_MIXER_E("LOMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOMIX_ENA_BIT, + 0, &wm8990_dapm_lomix_controls[0], + ARRAY_SIZE(wm8990_dapm_lomix_controls), + outmixer_event, SND_SOC_DAPM_PRE_REG), + +/* LONMIX */ +SND_SOC_DAPM_MIXER("LONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LON_ENA_BIT, 0, + &wm8990_dapm_lonmix_controls[0], + ARRAY_SIZE(wm8990_dapm_lonmix_controls)), + +/* LOPMIX */ +SND_SOC_DAPM_MIXER("LOPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_LOP_ENA_BIT, 0, + &wm8990_dapm_lopmix_controls[0], + ARRAY_SIZE(wm8990_dapm_lopmix_controls)), + +/* OUT3MIX */ +SND_SOC_DAPM_MIXER("OUT3MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT3_ENA_BIT, 0, + &wm8990_dapm_out3mix_controls[0], + ARRAY_SIZE(wm8990_dapm_out3mix_controls)), + +/* SPKMIX */ +SND_SOC_DAPM_MIXER_E("SPKMIX", WM8990_POWER_MANAGEMENT_1, WM8990_SPK_ENA_BIT, 0, + &wm8990_dapm_spkmix_controls[0], + ARRAY_SIZE(wm8990_dapm_spkmix_controls), outmixer_event, + SND_SOC_DAPM_PRE_REG), + +/* OUT4MIX */ +SND_SOC_DAPM_MIXER("OUT4MIX", WM8990_POWER_MANAGEMENT_1, WM8990_OUT4_ENA_BIT, 0, + &wm8990_dapm_out4mix_controls[0], + ARRAY_SIZE(wm8990_dapm_out4mix_controls)), + +/* ROPMIX */ +SND_SOC_DAPM_MIXER("ROPMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROP_ENA_BIT, 0, + &wm8990_dapm_ropmix_controls[0], + ARRAY_SIZE(wm8990_dapm_ropmix_controls)), + +/* RONMIX */ +SND_SOC_DAPM_MIXER("RONMIX", WM8990_POWER_MANAGEMENT_3, WM8990_RON_ENA_BIT, 0, + &wm8990_dapm_ronmix_controls[0], + ARRAY_SIZE(wm8990_dapm_ronmix_controls)), + +/* ROMIX */ +SND_SOC_DAPM_MIXER_E("ROMIX", WM8990_POWER_MANAGEMENT_3, WM8990_ROMIX_ENA_BIT, + 0, &wm8990_dapm_romix_controls[0], + ARRAY_SIZE(wm8990_dapm_romix_controls), + outmixer_event, SND_SOC_DAPM_PRE_REG), + +/* LOUT PGA */ +SND_SOC_DAPM_PGA("LOUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_LOUT_ENA_BIT, 0, + NULL, 0), + +/* ROUT PGA */ +SND_SOC_DAPM_PGA("ROUT PGA", WM8990_POWER_MANAGEMENT_1, WM8990_ROUT_ENA_BIT, 0, + NULL, 0), + +/* LOPGA */ +SND_SOC_DAPM_PGA("LOPGA", WM8990_POWER_MANAGEMENT_3, WM8990_LOPGA_ENA_BIT, 0, + NULL, 0), + +/* ROPGA */ +SND_SOC_DAPM_PGA("ROPGA", WM8990_POWER_MANAGEMENT_3, WM8990_ROPGA_ENA_BIT, 0, + NULL, 0), + +/* MICBIAS */ +SND_SOC_DAPM_SUPPLY("MICBIAS", WM8990_POWER_MANAGEMENT_1, + WM8990_MICBIAS_ENA_BIT, 0, NULL, 0), + +SND_SOC_DAPM_OUTPUT("LON"), +SND_SOC_DAPM_OUTPUT("LOP"), +SND_SOC_DAPM_OUTPUT("OUT3"), +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("SPKN"), +SND_SOC_DAPM_OUTPUT("SPKP"), +SND_SOC_DAPM_OUTPUT("ROUT"), +SND_SOC_DAPM_OUTPUT("OUT4"), +SND_SOC_DAPM_OUTPUT("ROP"), +SND_SOC_DAPM_OUTPUT("RON"), + +SND_SOC_DAPM_OUTPUT("Internal DAC Sink"), +}; + +static const struct snd_soc_dapm_route wm8990_dapm_routes[] = { + /* Make DACs turn on when playing even if not mixed into any outputs */ + {"Internal DAC Sink", NULL, "Left DAC"}, + {"Internal DAC Sink", NULL, "Right DAC"}, + + /* Make ADCs turn on when recording even if not mixed from any inputs */ + {"Left ADC", NULL, "Internal ADC Source"}, + {"Right ADC", NULL, "Internal ADC Source"}, + + {"AINLMUX", NULL, "INL"}, + {"INMIXL", NULL, "INL"}, + {"AINRMUX", NULL, "INR"}, + {"INMIXR", NULL, "INR"}, + + /* Input Side */ + /* LIN12 PGA */ + {"LIN12 PGA", "LIN1 Switch", "LIN1"}, + {"LIN12 PGA", "LIN2 Switch", "LIN2"}, + /* LIN34 PGA */ + {"LIN34 PGA", "LIN3 Switch", "LIN3"}, + {"LIN34 PGA", "LIN4 Switch", "LIN4/RXN"}, + /* INMIXL */ + {"INMIXL", "Record Left Volume", "LOMIX"}, + {"INMIXL", "LIN2 Volume", "LIN2"}, + {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"}, + {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"}, + /* AINLMUX */ + {"AINLMUX", "INMIXL Mix", "INMIXL"}, + {"AINLMUX", "DIFFINL Mix", "LIN12 PGA"}, + {"AINLMUX", "DIFFINL Mix", "LIN34 PGA"}, + {"AINLMUX", "RXVOICE Mix", "LIN4/RXN"}, + {"AINLMUX", "RXVOICE Mix", "RIN4/RXP"}, + /* ADC */ + {"Left ADC", NULL, "AINLMUX"}, + + /* RIN12 PGA */ + {"RIN12 PGA", "RIN1 Switch", "RIN1"}, + {"RIN12 PGA", "RIN2 Switch", "RIN2"}, + /* RIN34 PGA */ + {"RIN34 PGA", "RIN3 Switch", "RIN3"}, + {"RIN34 PGA", "RIN4 Switch", "RIN4/RXP"}, + /* INMIXL */ + {"INMIXR", "Record Right Volume", "ROMIX"}, + {"INMIXR", "RIN2 Volume", "RIN2"}, + {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"}, + {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"}, + /* AINRMUX */ + {"AINRMUX", "INMIXR Mix", "INMIXR"}, + {"AINRMUX", "DIFFINR Mix", "RIN12 PGA"}, + {"AINRMUX", "DIFFINR Mix", "RIN34 PGA"}, + {"AINRMUX", "RXVOICE Mix", "LIN4/RXN"}, + {"AINRMUX", "RXVOICE Mix", "RIN4/RXP"}, + /* ADC */ + {"Right ADC", NULL, "AINRMUX"}, + + /* LOMIX */ + {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"}, + {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"}, + {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, + {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, + {"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"}, + {"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"}, + {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"}, + + /* ROMIX */ + {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"}, + {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"}, + {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, + {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, + {"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"}, + {"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"}, + {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"}, + + /* SPKMIX */ + {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"}, + {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"}, + {"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"}, + {"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"}, + {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"}, + {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"}, + {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"}, + {"SPKMIX", "SPKMIX Left DAC Switch", "Left DAC"}, + + /* LONMIX */ + {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"}, + {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"}, + {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"}, + + /* LOPMIX */ + {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"}, + {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"}, + {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"}, + + /* OUT3MIX */ + {"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXN"}, + {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"}, + + /* OUT4MIX */ + {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"}, + {"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"}, + + /* RONMIX */ + {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"}, + {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"}, + {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"}, + + /* ROPMIX */ + {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"}, + {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"}, + {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"}, + + /* Out Mixer PGAs */ + {"LOPGA", NULL, "LOMIX"}, + {"ROPGA", NULL, "ROMIX"}, + + {"LOUT PGA", NULL, "LOMIX"}, + {"ROUT PGA", NULL, "ROMIX"}, + + /* Output Pins */ + {"LON", NULL, "LONMIX"}, + {"LOP", NULL, "LOPMIX"}, + {"OUT3", NULL, "OUT3MIX"}, + {"LOUT", NULL, "LOUT PGA"}, + {"SPKN", NULL, "SPKMIX"}, + {"ROUT", NULL, "ROUT PGA"}, + {"OUT4", NULL, "OUT4MIX"}, + {"ROP", NULL, "ROPMIX"}, + {"RON", NULL, "RONMIX"}, +}; + +/* PLL divisors */ +struct _pll_div { + u32 div2; + u32 n; + u32 k; +}; + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 16) * 10) + +static void pll_factors(struct _pll_div *pll_div, unsigned int target, + unsigned int source) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod; + + + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div->div2 = 1; + Ndiv = target / source; + } else + pll_div->div2 = 0; + + if ((Ndiv < 6) || (Ndiv > 12)) + printk(KERN_WARNING + "WM8990 N value outwith recommended range! N = %u\n", Ndiv); + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; +} + +static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = codec_dai->component; + struct _pll_div pll_div; + + if (freq_in && freq_out) { + pll_factors(&pll_div, freq_out * 4, freq_in); + + /* Turn on PLL */ + snd_soc_component_update_bits(component, WM8990_POWER_MANAGEMENT_2, + WM8990_PLL_ENA, WM8990_PLL_ENA); + + /* sysclk comes from PLL */ + snd_soc_component_update_bits(component, WM8990_CLOCKING_2, + WM8990_SYSCLK_SRC, WM8990_SYSCLK_SRC); + + /* set up N , fractional mode and pre-divisor if necessary */ + snd_soc_component_write(component, WM8990_PLL1, pll_div.n | WM8990_SDM | + (pll_div.div2?WM8990_PRESCALE:0)); + snd_soc_component_write(component, WM8990_PLL2, (u8)(pll_div.k>>8)); + snd_soc_component_write(component, WM8990_PLL3, (u8)(pll_div.k & 0xFF)); + } else { + /* Turn off PLL */ + snd_soc_component_update_bits(component, WM8990_POWER_MANAGEMENT_2, + WM8990_PLL_ENA, 0); + } + return 0; +} + +/* + * Clock after PLL and dividers + */ +static int wm8990_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8990_priv *wm8990 = snd_soc_component_get_drvdata(component); + + wm8990->sysclk = freq; + return 0; +} + +/* + * Set's ADC and Voice DAC format. + */ +static int wm8990_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 audio1, audio3; + + audio1 = snd_soc_component_read(component, WM8990_AUDIO_INTERFACE_1); + audio3 = snd_soc_component_read(component, WM8990_AUDIO_INTERFACE_3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + audio3 &= ~WM8990_AIF_MSTR1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + audio3 |= WM8990_AIF_MSTR1; + break; + default: + return -EINVAL; + } + + audio1 &= ~WM8990_AIF_FMT_MASK; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + audio1 |= WM8990_AIF_TMF_I2S; + audio1 &= ~WM8990_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_RIGHT_J: + audio1 |= WM8990_AIF_TMF_RIGHTJ; + audio1 &= ~WM8990_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_LEFT_J: + audio1 |= WM8990_AIF_TMF_LEFTJ; + audio1 &= ~WM8990_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_A: + audio1 |= WM8990_AIF_TMF_DSP; + audio1 &= ~WM8990_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_B: + audio1 |= WM8990_AIF_TMF_DSP | WM8990_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8990_AUDIO_INTERFACE_1, audio1); + snd_soc_component_write(component, WM8990_AUDIO_INTERFACE_3, audio3); + return 0; +} + +static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + + switch (div_id) { + case WM8990_MCLK_DIV: + snd_soc_component_update_bits(component, WM8990_CLOCKING_2, + WM8990_MCLK_DIV_MASK, div); + break; + case WM8990_DACCLK_DIV: + snd_soc_component_update_bits(component, WM8990_CLOCKING_2, + WM8990_DAC_CLKDIV_MASK, div); + break; + case WM8990_ADCCLK_DIV: + snd_soc_component_update_bits(component, WM8990_CLOCKING_2, + WM8990_ADC_CLKDIV_MASK, div); + break; + case WM8990_BCLK_DIV: + snd_soc_component_update_bits(component, WM8990_CLOCKING_1, + WM8990_BCLK_DIV_MASK, div); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + */ +static int wm8990_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 audio1 = snd_soc_component_read(component, WM8990_AUDIO_INTERFACE_1); + + audio1 &= ~WM8990_AIF_WL_MASK; + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + audio1 |= WM8990_AIF_WL_20BITS; + break; + case 24: + audio1 |= WM8990_AIF_WL_24BITS; + break; + case 32: + audio1 |= WM8990_AIF_WL_32BITS; + break; + } + + snd_soc_component_write(component, WM8990_AUDIO_INTERFACE_1, audio1); + return 0; +} + +static int wm8990_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 val; + + val = snd_soc_component_read(component, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE; + + if (mute) + snd_soc_component_write(component, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE); + else + snd_soc_component_write(component, WM8990_DAC_CTRL, val); + + return 0; +} + +static int wm8990_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8990_priv *wm8990 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID=2*50k */ + snd_soc_component_update_bits(component, WM8990_POWER_MANAGEMENT_1, + WM8990_VMID_MODE_MASK, 0x2); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regcache_sync(wm8990->regmap); + if (ret < 0) { + dev_err(component->dev, "Failed to sync cache: %d\n", ret); + return ret; + } + + /* Enable all output discharge bits */ + snd_soc_component_write(component, WM8990_ANTIPOP1, WM8990_DIS_LLINE | + WM8990_DIS_RLINE | WM8990_DIS_OUT3 | + WM8990_DIS_OUT4 | WM8990_DIS_LOUT | + WM8990_DIS_ROUT); + + /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */ + snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_SOFTST | + WM8990_BUFDCOPEN | WM8990_POBCTRL | + WM8990_VMIDTOG); + + /* Delay to allow output caps to discharge */ + msleep(300); + + /* Disable VMIDTOG */ + snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_SOFTST | + WM8990_BUFDCOPEN | WM8990_POBCTRL); + + /* disable all output discharge bits */ + snd_soc_component_write(component, WM8990_ANTIPOP1, 0); + + /* Enable outputs */ + snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x1b00); + + msleep(50); + + /* Enable VMID at 2x50k */ + snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x1f02); + + msleep(100); + + /* Enable VREF */ + snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x1f03); + + msleep(600); + + /* Enable BUFIOEN */ + snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_SOFTST | + WM8990_BUFDCOPEN | WM8990_POBCTRL | + WM8990_BUFIOEN); + + /* Disable outputs */ + snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x3); + + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_BUFIOEN); + + /* Enable workaround for ADC clocking issue. */ + snd_soc_component_write(component, WM8990_EXT_ACCESS_ENA, 0x2); + snd_soc_component_write(component, WM8990_EXT_CTL1, 0xa003); + snd_soc_component_write(component, WM8990_EXT_ACCESS_ENA, 0); + } + + /* VMID=2*250k */ + snd_soc_component_update_bits(component, WM8990_POWER_MANAGEMENT_1, + WM8990_VMID_MODE_MASK, 0x4); + break; + + case SND_SOC_BIAS_OFF: + /* Enable POBCTRL and SOFT_ST */ + snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_SOFTST | + WM8990_POBCTRL | WM8990_BUFIOEN); + + /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_component_write(component, WM8990_ANTIPOP2, WM8990_SOFTST | + WM8990_BUFDCOPEN | WM8990_POBCTRL | + WM8990_BUFIOEN); + + /* mute DAC */ + snd_soc_component_update_bits(component, WM8990_DAC_CTRL, + WM8990_DAC_MUTE, WM8990_DAC_MUTE); + + /* Enable any disabled outputs */ + snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x1f03); + + /* Disable VMID */ + snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x1f01); + + msleep(300); + + /* Enable all output discharge bits */ + snd_soc_component_write(component, WM8990_ANTIPOP1, WM8990_DIS_LLINE | + WM8990_DIS_RLINE | WM8990_DIS_OUT3 | + WM8990_DIS_OUT4 | WM8990_DIS_LOUT | + WM8990_DIS_ROUT); + + /* Disable VREF */ + snd_soc_component_write(component, WM8990_POWER_MANAGEMENT_1, 0x0); + + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_component_write(component, WM8990_ANTIPOP2, 0x0); + + regcache_mark_dirty(wm8990->regmap); + break; + } + + return 0; +} + +#define WM8990_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +#define WM8990_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +/* + * The WM8990 supports 2 different and mutually exclusive DAI + * configurations. + * + * 1. ADC/DAC on Primary Interface + * 2. ADC on Primary Interface/DAC on secondary + */ +static const struct snd_soc_dai_ops wm8990_dai_ops = { + .hw_params = wm8990_hw_params, + .mute_stream = wm8990_mute, + .set_fmt = wm8990_set_dai_fmt, + .set_clkdiv = wm8990_set_dai_clkdiv, + .set_pll = wm8990_set_dai_pll, + .set_sysclk = wm8990_set_dai_sysclk, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver wm8990_dai = { +/* ADC/DAC on primary */ + .name = "wm8990-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8990_RATES, + .formats = WM8990_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8990_RATES, + .formats = WM8990_FORMATS,}, + .ops = &wm8990_dai_ops, +}; + +/* + * initialise the WM8990 driver + * register the mixer and dsp interfaces with the kernel + */ +static int wm8990_probe(struct snd_soc_component *component) +{ + wm8990_reset(component); + + /* charge output caps */ + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + snd_soc_component_update_bits(component, WM8990_AUDIO_INTERFACE_4, + WM8990_ALRCGPIO1, WM8990_ALRCGPIO1); + + snd_soc_component_update_bits(component, WM8990_GPIO1_GPIO2, + WM8990_GPIO1_SEL_MASK, 1); + + snd_soc_component_update_bits(component, WM8990_POWER_MANAGEMENT_2, + WM8990_OPCLK_ENA, WM8990_OPCLK_ENA); + + snd_soc_component_write(component, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); + snd_soc_component_write(component, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm8990 = { + .probe = wm8990_probe, + .set_bias_level = wm8990_set_bias_level, + .controls = wm8990_snd_controls, + .num_controls = ARRAY_SIZE(wm8990_snd_controls), + .dapm_widgets = wm8990_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8990_dapm_widgets), + .dapm_routes = wm8990_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8990_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm8990_i2c_probe(struct i2c_client *i2c) +{ + struct wm8990_priv *wm8990; + int ret; + + wm8990 = devm_kzalloc(&i2c->dev, sizeof(struct wm8990_priv), + GFP_KERNEL); + if (wm8990 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm8990); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8990, &wm8990_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8990_i2c_id[] = { + { "wm8990", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id); + +static struct i2c_driver wm8990_i2c_driver = { + .driver = { + .name = "wm8990", + }, + .probe = wm8990_i2c_probe, + .id_table = wm8990_i2c_id, +}; + +module_i2c_driver(wm8990_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8990 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h new file mode 100644 index 0000000000..315edc4b44 --- /dev/null +++ b/sound/soc/codecs/wm8990.h @@ -0,0 +1,821 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * wm8990.h -- audio driver for WM8990 + * + * Copyright 2007 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + */ + +#ifndef __WM8990REGISTERDEFS_H__ +#define __WM8990REGISTERDEFS_H__ + +/* + * Register values. + */ +#define WM8990_RESET 0x00 +#define WM8990_POWER_MANAGEMENT_1 0x01 +#define WM8990_POWER_MANAGEMENT_2 0x02 +#define WM8990_POWER_MANAGEMENT_3 0x03 +#define WM8990_AUDIO_INTERFACE_1 0x04 +#define WM8990_AUDIO_INTERFACE_2 0x05 +#define WM8990_CLOCKING_1 0x06 +#define WM8990_CLOCKING_2 0x07 +#define WM8990_AUDIO_INTERFACE_3 0x08 +#define WM8990_AUDIO_INTERFACE_4 0x09 +#define WM8990_DAC_CTRL 0x0A +#define WM8990_LEFT_DAC_DIGITAL_VOLUME 0x0B +#define WM8990_RIGHT_DAC_DIGITAL_VOLUME 0x0C +#define WM8990_DIGITAL_SIDE_TONE 0x0D +#define WM8990_ADC_CTRL 0x0E +#define WM8990_LEFT_ADC_DIGITAL_VOLUME 0x0F +#define WM8990_RIGHT_ADC_DIGITAL_VOLUME 0x10 +#define WM8990_GPIO_CTRL_1 0x12 +#define WM8990_GPIO1_GPIO2 0x13 +#define WM8990_GPIO3_GPIO4 0x14 +#define WM8990_GPIO5_GPIO6 0x15 +#define WM8990_GPIOCTRL_2 0x16 +#define WM8990_GPIO_POL 0x17 +#define WM8990_LEFT_LINE_INPUT_1_2_VOLUME 0x18 +#define WM8990_LEFT_LINE_INPUT_3_4_VOLUME 0x19 +#define WM8990_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A +#define WM8990_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B +#define WM8990_LEFT_OUTPUT_VOLUME 0x1C +#define WM8990_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8990_LINE_OUTPUTS_VOLUME 0x1E +#define WM8990_OUT3_4_VOLUME 0x1F +#define WM8990_LEFT_OPGA_VOLUME 0x20 +#define WM8990_RIGHT_OPGA_VOLUME 0x21 +#define WM8990_SPEAKER_VOLUME 0x22 +#define WM8990_CLASSD1 0x23 +#define WM8990_CLASSD3 0x25 +#define WM8990_CLASSD4 0x26 +#define WM8990_INPUT_MIXER1 0x27 +#define WM8990_INPUT_MIXER2 0x28 +#define WM8990_INPUT_MIXER3 0x29 +#define WM8990_INPUT_MIXER4 0x2A +#define WM8990_INPUT_MIXER5 0x2B +#define WM8990_INPUT_MIXER6 0x2C +#define WM8990_OUTPUT_MIXER1 0x2D +#define WM8990_OUTPUT_MIXER2 0x2E +#define WM8990_OUTPUT_MIXER3 0x2F +#define WM8990_OUTPUT_MIXER4 0x30 +#define WM8990_OUTPUT_MIXER5 0x31 +#define WM8990_OUTPUT_MIXER6 0x32 +#define WM8990_OUT3_4_MIXER 0x33 +#define WM8990_LINE_MIXER1 0x34 +#define WM8990_LINE_MIXER2 0x35 +#define WM8990_SPEAKER_MIXER 0x36 +#define WM8990_ADDITIONAL_CONTROL 0x37 +#define WM8990_ANTIPOP1 0x38 +#define WM8990_ANTIPOP2 0x39 +#define WM8990_MICBIAS 0x3A +#define WM8990_PLL1 0x3C +#define WM8990_PLL2 0x3D +#define WM8990_PLL3 0x3E + +#define WM8990_EXT_ACCESS_ENA 0x75 +#define WM8990_EXT_CTL1 0x7a + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Reset + */ +#define WM8990_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET_CHIP_ID */ + +/* + * R1 (0x01) - Power Management (1) + */ +#define WM8990_SPK_ENA 0x1000 /* SPK_ENA */ +#define WM8990_SPK_ENA_BIT 12 +#define WM8990_OUT3_ENA 0x0800 /* OUT3_ENA */ +#define WM8990_OUT3_ENA_BIT 11 +#define WM8990_OUT4_ENA 0x0400 /* OUT4_ENA */ +#define WM8990_OUT4_ENA_BIT 10 +#define WM8990_LOUT_ENA 0x0200 /* LOUT_ENA */ +#define WM8990_LOUT_ENA_BIT 9 +#define WM8990_ROUT_ENA 0x0100 /* ROUT_ENA */ +#define WM8990_ROUT_ENA_BIT 8 +#define WM8990_MICBIAS_ENA 0x0010 /* MICBIAS_ENA */ +#define WM8990_MICBIAS_ENA_BIT 4 +#define WM8990_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */ +#define WM8990_VREF_ENA 0x0001 /* VREF_ENA */ +#define WM8990_VREF_ENA_BIT 0 + +/* + * R2 (0x02) - Power Management (2) + */ +#define WM8990_PLL_ENA 0x8000 /* PLL_ENA */ +#define WM8990_PLL_ENA_BIT 15 +#define WM8990_TSHUT_ENA 0x4000 /* TSHUT_ENA */ +#define WM8990_TSHUT_ENA_BIT 14 +#define WM8990_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */ +#define WM8990_TSHUT_OPDIS_BIT 13 +#define WM8990_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8990_OPCLK_ENA_BIT 11 +#define WM8990_AINL_ENA 0x0200 /* AINL_ENA */ +#define WM8990_AINL_ENA_BIT 9 +#define WM8990_AINR_ENA 0x0100 /* AINR_ENA */ +#define WM8990_AINR_ENA_BIT 8 +#define WM8990_LIN34_ENA 0x0080 /* LIN34_ENA */ +#define WM8990_LIN34_ENA_BIT 7 +#define WM8990_LIN12_ENA 0x0040 /* LIN12_ENA */ +#define WM8990_LIN12_ENA_BIT 6 +#define WM8990_RIN34_ENA 0x0020 /* RIN34_ENA */ +#define WM8990_RIN34_ENA_BIT 5 +#define WM8990_RIN12_ENA 0x0010 /* RIN12_ENA */ +#define WM8990_RIN12_ENA_BIT 4 +#define WM8990_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8990_ADCL_ENA_BIT 1 +#define WM8990_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8990_ADCR_ENA_BIT 0 + +/* + * R3 (0x03) - Power Management (3) + */ +#define WM8990_LON_ENA 0x2000 /* LON_ENA */ +#define WM8990_LON_ENA_BIT 13 +#define WM8990_LOP_ENA 0x1000 /* LOP_ENA */ +#define WM8990_LOP_ENA_BIT 12 +#define WM8990_RON_ENA 0x0800 /* RON_ENA */ +#define WM8990_RON_ENA_BIT 11 +#define WM8990_ROP_ENA 0x0400 /* ROP_ENA */ +#define WM8990_ROP_ENA_BIT 10 +#define WM8990_LOPGA_ENA 0x0080 /* LOPGA_ENA */ +#define WM8990_LOPGA_ENA_BIT 7 +#define WM8990_ROPGA_ENA 0x0040 /* ROPGA_ENA */ +#define WM8990_ROPGA_ENA_BIT 6 +#define WM8990_LOMIX_ENA 0x0020 /* LOMIX_ENA */ +#define WM8990_LOMIX_ENA_BIT 5 +#define WM8990_ROMIX_ENA 0x0010 /* ROMIX_ENA */ +#define WM8990_ROMIX_ENA_BIT 4 +#define WM8990_DACL_ENA 0x0002 /* DACL_ENA */ +#define WM8990_DACL_ENA_BIT 1 +#define WM8990_DACR_ENA 0x0001 /* DACR_ENA */ +#define WM8990_DACR_ENA_BIT 0 + +/* + * R4 (0x04) - Audio Interface (1) + */ +#define WM8990_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */ +#define WM8990_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */ +#define WM8990_AIFADC_TDM 0x2000 /* AIFADC_TDM */ +#define WM8990_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8990_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */ +#define WM8990_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */ +#define WM8990_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */ +#define WM8990_AIF_WL_16BITS (0 << 5) +#define WM8990_AIF_WL_20BITS (1 << 5) +#define WM8990_AIF_WL_24BITS (2 << 5) +#define WM8990_AIF_WL_32BITS (3 << 5) +#define WM8990_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */ +#define WM8990_AIF_TMF_RIGHTJ (0 << 3) +#define WM8990_AIF_TMF_LEFTJ (1 << 3) +#define WM8990_AIF_TMF_I2S (2 << 3) +#define WM8990_AIF_TMF_DSP (3 << 3) + +/* + * R5 (0x05) - Audio Interface (2) + */ +#define WM8990_DACL_SRC 0x8000 /* DACL_SRC */ +#define WM8990_DACR_SRC 0x4000 /* DACR_SRC */ +#define WM8990_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ +#define WM8990_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8990_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST */ +#define WM8990_DAC_COMP 0x0010 /* DAC_COMP */ +#define WM8990_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */ +#define WM8990_ADC_COMP 0x0004 /* ADC_COMP */ +#define WM8990_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */ +#define WM8990_LOOPBACK 0x0001 /* LOOPBACK */ + +/* + * R6 (0x06) - Clocking (1) + */ +#define WM8990_TOCLK_RATE 0x8000 /* TOCLK_RATE */ +#define WM8990_TOCLK_ENA 0x4000 /* TOCLK_ENA */ +#define WM8990_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */ +#define WM8990_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */ +#define WM8990_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */ +#define WM8990_BCLK_DIV_1 (0x0 << 1) +#define WM8990_BCLK_DIV_1_5 (0x1 << 1) +#define WM8990_BCLK_DIV_2 (0x2 << 1) +#define WM8990_BCLK_DIV_3 (0x3 << 1) +#define WM8990_BCLK_DIV_4 (0x4 << 1) +#define WM8990_BCLK_DIV_5_5 (0x5 << 1) +#define WM8990_BCLK_DIV_6 (0x6 << 1) +#define WM8990_BCLK_DIV_8 (0x7 << 1) +#define WM8990_BCLK_DIV_11 (0x8 << 1) +#define WM8990_BCLK_DIV_12 (0x9 << 1) +#define WM8990_BCLK_DIV_16 (0xA << 1) +#define WM8990_BCLK_DIV_22 (0xB << 1) +#define WM8990_BCLK_DIV_24 (0xC << 1) +#define WM8990_BCLK_DIV_32 (0xD << 1) +#define WM8990_BCLK_DIV_44 (0xE << 1) +#define WM8990_BCLK_DIV_48 (0xF << 1) + +/* + * R7 (0x07) - Clocking (2) + */ +#define WM8990_MCLK_SRC 0x8000 /* MCLK_SRC */ +#define WM8990_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */ +#define WM8990_CLK_FORCE 0x2000 /* CLK_FORCE */ +#define WM8990_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */ +#define WM8990_MCLK_DIV_1 (0 << 11) +#define WM8990_MCLK_DIV_2 (2 << 11) +#define WM8990_MCLK_INV 0x0400 /* MCLK_INV */ +#define WM8990_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV */ +#define WM8990_ADC_CLKDIV_1 (0 << 5) +#define WM8990_ADC_CLKDIV_1_5 (1 << 5) +#define WM8990_ADC_CLKDIV_2 (2 << 5) +#define WM8990_ADC_CLKDIV_3 (3 << 5) +#define WM8990_ADC_CLKDIV_4 (4 << 5) +#define WM8990_ADC_CLKDIV_5_5 (5 << 5) +#define WM8990_ADC_CLKDIV_6 (6 << 5) +#define WM8990_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */ +#define WM8990_DAC_CLKDIV_1 (0 << 2) +#define WM8990_DAC_CLKDIV_1_5 (1 << 2) +#define WM8990_DAC_CLKDIV_2 (2 << 2) +#define WM8990_DAC_CLKDIV_3 (3 << 2) +#define WM8990_DAC_CLKDIV_4 (4 << 2) +#define WM8990_DAC_CLKDIV_5_5 (5 << 2) +#define WM8990_DAC_CLKDIV_6 (6 << 2) + +/* + * R8 (0x08) - Audio Interface (3) + */ +#define WM8990_AIF_MSTR1 0x8000 /* AIF_MSTR1 */ +#define WM8990_AIF_MSTR2 0x4000 /* AIF_MSTR2 */ +#define WM8990_AIF_SEL 0x2000 /* AIF_SEL */ +#define WM8990_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */ +#define WM8990_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE */ + +/* + * R9 (0x09) - Audio Interface (4) + */ +#define WM8990_ALRCGPIO1 0x8000 /* ALRCGPIO1 */ +#define WM8990_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */ +#define WM8990_AIF_TRIS 0x2000 /* AIF_TRIS */ +#define WM8990_DACLRC_DIR 0x0800 /* DACLRC_DIR */ +#define WM8990_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE */ + +/* + * R10 (0x0A) - DAC CTRL + */ +#define WM8990_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */ +#define WM8990_DAC_MONO 0x0200 /* DAC_MONO */ +#define WM8990_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */ +#define WM8990_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */ +#define WM8990_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */ +#define WM8990_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */ +#define WM8990_DAC_MUTE 0x0004 /* DAC_MUTE */ +#define WM8990_DACL_DATINV 0x0002 /* DACL_DATINV */ +#define WM8990_DACR_DATINV 0x0001 /* DACR_DATINV */ + +/* + * R11 (0x0B) - Left DAC Digital Volume + */ +#define WM8990_DAC_VU 0x0100 /* DAC_VU */ +#define WM8990_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8990_DACL_VOL_SHIFT 0 +/* + * R12 (0x0C) - Right DAC Digital Volume + */ +#define WM8990_DAC_VU 0x0100 /* DAC_VU */ +#define WM8990_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8990_DACR_VOL_SHIFT 0 +/* + * R13 (0x0D) - Digital Side Tone + */ +#define WM8990_ADCL_DAC_SVOL_MASK 0x0F /* ADCL_DAC_SVOL */ +#define WM8990_ADCL_DAC_SVOL_SHIFT 9 +#define WM8990_ADCR_DAC_SVOL_MASK 0x0F /* ADCR_DAC_SVOL */ +#define WM8990_ADCR_DAC_SVOL_SHIFT 5 +#define WM8990_ADC_TO_DACL_MASK 0x03 /* ADC_TO_DACL - [3:2] */ +#define WM8990_ADC_TO_DACL_SHIFT 2 +#define WM8990_ADC_TO_DACR_MASK 0x03 /* ADC_TO_DACR - [1:0] */ +#define WM8990_ADC_TO_DACR_SHIFT 0 + +/* + * R14 (0x0E) - ADC CTRL + */ +#define WM8990_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */ +#define WM8990_ADC_HPF_ENA_BIT 8 +#define WM8990_ADC_HPF_CUT_MASK 0x03 /* ADC_HPF_CUT - [6:5] */ +#define WM8990_ADC_HPF_CUT_SHIFT 5 +#define WM8990_ADCL_DATINV 0x0002 /* ADCL_DATINV */ +#define WM8990_ADCL_DATINV_BIT 1 +#define WM8990_ADCR_DATINV 0x0001 /* ADCR_DATINV */ +#define WM8990_ADCR_DATINV_BIT 0 + +/* + * R15 (0x0F) - Left ADC Digital Volume + */ +#define WM8990_ADC_VU 0x0100 /* ADC_VU */ +#define WM8990_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8990_ADCL_VOL_SHIFT 0 + +/* + * R16 (0x10) - Right ADC Digital Volume + */ +#define WM8990_ADC_VU 0x0100 /* ADC_VU */ +#define WM8990_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8990_ADCR_VOL_SHIFT 0 + +/* + * R18 (0x12) - GPIO CTRL 1 + */ +#define WM8990_IRQ 0x1000 /* IRQ */ +#define WM8990_TEMPOK 0x0800 /* TEMPOK */ +#define WM8990_MICSHRT 0x0400 /* MICSHRT */ +#define WM8990_MICDET 0x0200 /* MICDET */ +#define WM8990_PLL_LCK 0x0100 /* PLL_LCK */ +#define WM8990_GPI8_STATUS 0x0080 /* GPI8_STATUS */ +#define WM8990_GPI7_STATUS 0x0040 /* GPI7_STATUS */ +#define WM8990_GPIO6_STATUS 0x0020 /* GPIO6_STATUS */ +#define WM8990_GPIO5_STATUS 0x0010 /* GPIO5_STATUS */ +#define WM8990_GPIO4_STATUS 0x0008 /* GPIO4_STATUS */ +#define WM8990_GPIO3_STATUS 0x0004 /* GPIO3_STATUS */ +#define WM8990_GPIO2_STATUS 0x0002 /* GPIO2_STATUS */ +#define WM8990_GPIO1_STATUS 0x0001 /* GPIO1_STATUS */ + +/* + * R19 (0x13) - GPIO1 & GPIO2 + */ +#define WM8990_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */ +#define WM8990_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */ +#define WM8990_GPIO2_PU 0x2000 /* GPIO2_PU */ +#define WM8990_GPIO2_PD 0x1000 /* GPIO2_PD */ +#define WM8990_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */ +#define WM8990_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */ +#define WM8990_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */ +#define WM8990_GPIO1_PU 0x0020 /* GPIO1_PU */ +#define WM8990_GPIO1_PD 0x0010 /* GPIO1_PD */ +#define WM8990_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */ + +/* + * R20 (0x14) - GPIO3 & GPIO4 + */ +#define WM8990_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */ +#define WM8990_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */ +#define WM8990_GPIO4_PU 0x2000 /* GPIO4_PU */ +#define WM8990_GPIO4_PD 0x1000 /* GPIO4_PD */ +#define WM8990_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */ +#define WM8990_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */ +#define WM8990_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */ +#define WM8990_GPIO3_PU 0x0020 /* GPIO3_PU */ +#define WM8990_GPIO3_PD 0x0010 /* GPIO3_PD */ +#define WM8990_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */ + +/* + * R21 (0x15) - GPIO5 & GPIO6 + */ +#define WM8990_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */ +#define WM8990_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */ +#define WM8990_GPIO6_PU 0x2000 /* GPIO6_PU */ +#define WM8990_GPIO6_PD 0x1000 /* GPIO6_PD */ +#define WM8990_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */ +#define WM8990_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */ +#define WM8990_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */ +#define WM8990_GPIO5_PU 0x0020 /* GPIO5_PU */ +#define WM8990_GPIO5_PD 0x0010 /* GPIO5_PD */ +#define WM8990_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */ + +/* + * R22 (0x16) - GPIOCTRL 2 + */ +#define WM8990_RD_3W_ENA 0x8000 /* RD_3W_ENA */ +#define WM8990_MODE_3W4W 0x4000 /* MODE_3W4W */ +#define WM8990_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */ +#define WM8990_MICSHRT_IRQ_ENA 0x0400 /* MICSHRT_IRQ_ENA */ +#define WM8990_MICDET_IRQ_ENA 0x0200 /* MICDET_IRQ_ENA */ +#define WM8990_PLL_LCK_IRQ_ENA 0x0100 /* PLL_LCK_IRQ_ENA */ +#define WM8990_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */ +#define WM8990_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */ +#define WM8990_GPI8_ENA 0x0010 /* GPI8_ENA */ +#define WM8990_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */ +#define WM8990_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */ +#define WM8990_GPI7_ENA 0x0001 /* GPI7_ENA */ + +/* + * R23 (0x17) - GPIO_POL + */ +#define WM8990_IRQ_INV 0x1000 /* IRQ_INV */ +#define WM8990_TEMPOK_POL 0x0800 /* TEMPOK_POL */ +#define WM8990_MICSHRT_POL 0x0400 /* MICSHRT_POL */ +#define WM8990_MICDET_POL 0x0200 /* MICDET_POL */ +#define WM8990_PLL_LCK_POL 0x0100 /* PLL_LCK_POL */ +#define WM8990_GPI8_POL 0x0080 /* GPI8_POL */ +#define WM8990_GPI7_POL 0x0040 /* GPI7_POL */ +#define WM8990_GPIO6_POL 0x0020 /* GPIO6_POL */ +#define WM8990_GPIO5_POL 0x0010 /* GPIO5_POL */ +#define WM8990_GPIO4_POL 0x0008 /* GPIO4_POL */ +#define WM8990_GPIO3_POL 0x0004 /* GPIO3_POL */ +#define WM8990_GPIO2_POL 0x0002 /* GPIO2_POL */ +#define WM8990_GPIO1_POL 0x0001 /* GPIO1_POL */ + +/* + * R24 (0x18) - Left Line Input 1&2 Volume + */ +#define WM8990_IPVU 0x0100 /* IPVU */ +#define WM8990_LI12MUTE 0x0080 /* LI12MUTE */ +#define WM8990_LI12MUTE_BIT 7 +#define WM8990_LI12ZC 0x0040 /* LI12ZC */ +#define WM8990_LI12ZC_BIT 6 +#define WM8990_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */ +#define WM8990_LIN12VOL_SHIFT 0 +/* + * R25 (0x19) - Left Line Input 3&4 Volume + */ +#define WM8990_IPVU 0x0100 /* IPVU */ +#define WM8990_LI34MUTE 0x0080 /* LI34MUTE */ +#define WM8990_LI34MUTE_BIT 7 +#define WM8990_LI34ZC 0x0040 /* LI34ZC */ +#define WM8990_LI34ZC_BIT 6 +#define WM8990_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */ +#define WM8990_LIN34VOL_SHIFT 0 + +/* + * R26 (0x1A) - Right Line Input 1&2 Volume + */ +#define WM8990_IPVU 0x0100 /* IPVU */ +#define WM8990_RI12MUTE 0x0080 /* RI12MUTE */ +#define WM8990_RI12MUTE_BIT 7 +#define WM8990_RI12ZC 0x0040 /* RI12ZC */ +#define WM8990_RI12ZC_BIT 6 +#define WM8990_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */ +#define WM8990_RIN12VOL_SHIFT 0 + +/* + * R27 (0x1B) - Right Line Input 3&4 Volume + */ +#define WM8990_IPVU 0x0100 /* IPVU */ +#define WM8990_RI34MUTE 0x0080 /* RI34MUTE */ +#define WM8990_RI34MUTE_BIT 7 +#define WM8990_RI34ZC 0x0040 /* RI34ZC */ +#define WM8990_RI34ZC_BIT 6 +#define WM8990_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */ +#define WM8990_RIN34VOL_SHIFT 0 + +/* + * R28 (0x1C) - Left Output Volume + */ +#define WM8990_OPVU 0x0100 /* OPVU */ +#define WM8990_LOZC 0x0080 /* LOZC */ +#define WM8990_LOZC_BIT 7 +#define WM8990_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */ +#define WM8990_LOUTVOL_SHIFT 0 +/* + * R29 (0x1D) - Right Output Volume + */ +#define WM8990_OPVU 0x0100 /* OPVU */ +#define WM8990_ROZC 0x0080 /* ROZC */ +#define WM8990_ROZC_BIT 7 +#define WM8990_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */ +#define WM8990_ROUTVOL_SHIFT 0 +/* + * R30 (0x1E) - Line Outputs Volume + */ +#define WM8990_LONMUTE 0x0040 /* LONMUTE */ +#define WM8990_LONMUTE_BIT 6 +#define WM8990_LOPMUTE 0x0020 /* LOPMUTE */ +#define WM8990_LOPMUTE_BIT 5 +#define WM8990_LOATTN 0x0010 /* LOATTN */ +#define WM8990_LOATTN_BIT 4 +#define WM8990_RONMUTE 0x0004 /* RONMUTE */ +#define WM8990_RONMUTE_BIT 2 +#define WM8990_ROPMUTE 0x0002 /* ROPMUTE */ +#define WM8990_ROPMUTE_BIT 1 +#define WM8990_ROATTN 0x0001 /* ROATTN */ +#define WM8990_ROATTN_BIT 0 + +/* + * R31 (0x1F) - Out3/4 Volume + */ +#define WM8990_OUT3MUTE 0x0020 /* OUT3MUTE */ +#define WM8990_OUT3MUTE_BIT 5 +#define WM8990_OUT3ATTN 0x0010 /* OUT3ATTN */ +#define WM8990_OUT3ATTN_BIT 4 +#define WM8990_OUT4MUTE 0x0002 /* OUT4MUTE */ +#define WM8990_OUT4MUTE_BIT 1 +#define WM8990_OUT4ATTN 0x0001 /* OUT4ATTN */ +#define WM8990_OUT4ATTN_BIT 0 + +/* + * R32 (0x20) - Left OPGA Volume + */ +#define WM8990_OPVU 0x0100 /* OPVU */ +#define WM8990_LOPGAZC 0x0080 /* LOPGAZC */ +#define WM8990_LOPGAZC_BIT 7 +#define WM8990_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */ +#define WM8990_LOPGAVOL_SHIFT 0 + +/* + * R33 (0x21) - Right OPGA Volume + */ +#define WM8990_OPVU 0x0100 /* OPVU */ +#define WM8990_ROPGAZC 0x0080 /* ROPGAZC */ +#define WM8990_ROPGAZC_BIT 7 +#define WM8990_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */ +#define WM8990_ROPGAVOL_SHIFT 0 +/* + * R34 (0x22) - Speaker Volume + */ +#define WM8990_SPKATTN_MASK 0x0003 /* SPKATTN - [1:0] */ +#define WM8990_SPKATTN_SHIFT 0 + +/* + * R35 (0x23) - ClassD1 + */ +#define WM8990_CDMODE 0x0100 /* CDMODE */ +#define WM8990_CDMODE_BIT 8 + +/* + * R37 (0x25) - ClassD3 + */ +#define WM8990_DCGAIN_MASK 0x0007 /* DCGAIN - [5:3] */ +#define WM8990_DCGAIN_SHIFT 3 +#define WM8990_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */ +#define WM8990_ACGAIN_SHIFT 0 + +/* + * R38 (0x26) - ClassD4 + */ +#define WM8990_SPKZC_MASK 0x0001 /* SPKZC */ +#define WM8990_SPKZC_SHIFT 7 /* SPKZC */ +#define WM8990_SPKVOL_MASK 0x007F /* SPKVOL - [6:0] */ +#define WM8990_SPKVOL_SHIFT 0 /* SPKVOL - [6:0] */ + +/* + * R39 (0x27) - Input Mixer1 + */ +#define WM8990_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */ +#define WM8990_AINLMODE_SHIFT 2 +#define WM8990_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */ +#define WM8990_AINRMODE_SHIFT 0 + +/* + * R40 (0x28) - Input Mixer2 + */ +#define WM8990_LMP4 0x0080 /* LMP4 */ +#define WM8990_LMP4_BIT 7 /* LMP4 */ +#define WM8990_LMN3 0x0040 /* LMN3 */ +#define WM8990_LMN3_BIT 6 /* LMN3 */ +#define WM8990_LMP2 0x0020 /* LMP2 */ +#define WM8990_LMP2_BIT 5 /* LMP2 */ +#define WM8990_LMN1 0x0010 /* LMN1 */ +#define WM8990_LMN1_BIT 4 /* LMN1 */ +#define WM8990_RMP4 0x0008 /* RMP4 */ +#define WM8990_RMP4_BIT 3 /* RMP4 */ +#define WM8990_RMN3 0x0004 /* RMN3 */ +#define WM8990_RMN3_BIT 2 /* RMN3 */ +#define WM8990_RMP2 0x0002 /* RMP2 */ +#define WM8990_RMP2_BIT 1 /* RMP2 */ +#define WM8990_RMN1 0x0001 /* RMN1 */ +#define WM8990_RMN1_BIT 0 /* RMN1 */ + +/* + * R41 (0x29) - Input Mixer3 + */ +#define WM8990_L34MNB 0x0100 /* L34MNB */ +#define WM8990_L34MNB_BIT 8 +#define WM8990_L34MNBST 0x0080 /* L34MNBST */ +#define WM8990_L34MNBST_BIT 7 +#define WM8990_L12MNB 0x0020 /* L12MNB */ +#define WM8990_L12MNB_BIT 5 +#define WM8990_L12MNBST 0x0010 /* L12MNBST */ +#define WM8990_L12MNBST_BIT 4 +#define WM8990_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */ +#define WM8990_LDBVOL_SHIFT 0 + +/* + * R42 (0x2A) - Input Mixer4 + */ +#define WM8990_R34MNB 0x0100 /* R34MNB */ +#define WM8990_R34MNB_BIT 8 +#define WM8990_R34MNBST 0x0080 /* R34MNBST */ +#define WM8990_R34MNBST_BIT 7 +#define WM8990_R12MNB 0x0020 /* R12MNB */ +#define WM8990_R12MNB_BIT 5 +#define WM8990_R12MNBST 0x0010 /* R12MNBST */ +#define WM8990_R12MNBST_BIT 4 +#define WM8990_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */ +#define WM8990_RDBVOL_SHIFT 0 + +/* + * R43 (0x2B) - Input Mixer5 + */ +#define WM8990_LI2BVOL_MASK 0x07 /* LI2BVOL - [8:6] */ +#define WM8990_LI2BVOL_SHIFT 6 +#define WM8990_LR4BVOL_MASK 0x07 /* LR4BVOL - [5:3] */ +#define WM8990_LR4BVOL_SHIFT 3 +#define WM8990_LL4BVOL_MASK 0x07 /* LL4BVOL - [2:0] */ +#define WM8990_LL4BVOL_SHIFT 0 + +/* + * R44 (0x2C) - Input Mixer6 + */ +#define WM8990_RI2BVOL_MASK 0x07 /* RI2BVOL - [8:6] */ +#define WM8990_RI2BVOL_SHIFT 6 +#define WM8990_RL4BVOL_MASK 0x07 /* RL4BVOL - [5:3] */ +#define WM8990_RL4BVOL_SHIFT 3 +#define WM8990_RR4BVOL_MASK 0x07 /* RR4BVOL - [2:0] */ +#define WM8990_RR4BVOL_SHIFT 0 + +/* + * R45 (0x2D) - Output Mixer1 + */ +#define WM8990_LRBLO 0x0080 /* LRBLO */ +#define WM8990_LRBLO_BIT 7 +#define WM8990_LLBLO 0x0040 /* LLBLO */ +#define WM8990_LLBLO_BIT 6 +#define WM8990_LRI3LO 0x0020 /* LRI3LO */ +#define WM8990_LRI3LO_BIT 5 +#define WM8990_LLI3LO 0x0010 /* LLI3LO */ +#define WM8990_LLI3LO_BIT 4 +#define WM8990_LR12LO 0x0008 /* LR12LO */ +#define WM8990_LR12LO_BIT 3 +#define WM8990_LL12LO 0x0004 /* LL12LO */ +#define WM8990_LL12LO_BIT 2 +#define WM8990_LDLO 0x0001 /* LDLO */ +#define WM8990_LDLO_BIT 0 + +/* + * R46 (0x2E) - Output Mixer2 + */ +#define WM8990_RLBRO 0x0080 /* RLBRO */ +#define WM8990_RLBRO_BIT 7 +#define WM8990_RRBRO 0x0040 /* RRBRO */ +#define WM8990_RRBRO_BIT 6 +#define WM8990_RLI3RO 0x0020 /* RLI3RO */ +#define WM8990_RLI3RO_BIT 5 +#define WM8990_RRI3RO 0x0010 /* RRI3RO */ +#define WM8990_RRI3RO_BIT 4 +#define WM8990_RL12RO 0x0008 /* RL12RO */ +#define WM8990_RL12RO_BIT 3 +#define WM8990_RR12RO 0x0004 /* RR12RO */ +#define WM8990_RR12RO_BIT 2 +#define WM8990_RDRO 0x0001 /* RDRO */ +#define WM8990_RDRO_BIT 0 + +/* + * R47 (0x2F) - Output Mixer3 + */ +#define WM8990_LLI3LOVOL_MASK 0x07 /* LLI3LOVOL - [8:6] */ +#define WM8990_LLI3LOVOL_SHIFT 6 +#define WM8990_LR12LOVOL_MASK 0x07 /* LR12LOVOL - [5:3] */ +#define WM8990_LR12LOVOL_SHIFT 3 +#define WM8990_LL12LOVOL_MASK 0x07 /* LL12LOVOL - [2:0] */ +#define WM8990_LL12LOVOL_SHIFT 0 + +/* + * R48 (0x30) - Output Mixer4 + */ +#define WM8990_RRI3ROVOL_MASK 0x07 /* RRI3ROVOL - [8:6] */ +#define WM8990_RRI3ROVOL_SHIFT 6 +#define WM8990_RL12ROVOL_MASK 0x07 /* RL12ROVOL - [5:3] */ +#define WM8990_RL12ROVOL_SHIFT 3 +#define WM8990_RR12ROVOL_MASK 0x07 /* RR12ROVOL - [2:0] */ +#define WM8990_RR12ROVOL_SHIFT 0 + +/* + * R49 (0x31) - Output Mixer5 + */ +#define WM8990_LRI3LOVOL_MASK 0x07 /* LRI3LOVOL - [8:6] */ +#define WM8990_LRI3LOVOL_SHIFT 6 +#define WM8990_LRBLOVOL_MASK 0x07 /* LRBLOVOL - [5:3] */ +#define WM8990_LRBLOVOL_SHIFT 3 +#define WM8990_LLBLOVOL_MASK 0x07 /* LLBLOVOL - [2:0] */ +#define WM8990_LLBLOVOL_SHIFT 0 + +/* + * R50 (0x32) - Output Mixer6 + */ +#define WM8990_RLI3ROVOL_MASK 0x07 /* RLI3ROVOL - [8:6] */ +#define WM8990_RLI3ROVOL_SHIFT 6 +#define WM8990_RLBROVOL_MASK 0x07 /* RLBROVOL - [5:3] */ +#define WM8990_RLBROVOL_SHIFT 3 +#define WM8990_RRBROVOL_MASK 0x07 /* RRBROVOL - [2:0] */ +#define WM8990_RRBROVOL_SHIFT 0 + +/* + * R51 (0x33) - Out3/4 Mixer + */ +#define WM8990_VSEL_MASK 0x0180 /* VSEL - [8:7] */ +#define WM8990_LI4O3 0x0020 /* LI4O3 */ +#define WM8990_LI4O3_BIT 5 +#define WM8990_LPGAO3 0x0010 /* LPGAO3 */ +#define WM8990_LPGAO3_BIT 4 +#define WM8990_RI4O4 0x0002 /* RI4O4 */ +#define WM8990_RI4O4_BIT 1 +#define WM8990_RPGAO4 0x0001 /* RPGAO4 */ +#define WM8990_RPGAO4_BIT 0 +/* + * R52 (0x34) - Line Mixer1 + */ +#define WM8990_LLOPGALON 0x0040 /* LLOPGALON */ +#define WM8990_LLOPGALON_BIT 6 +#define WM8990_LROPGALON 0x0020 /* LROPGALON */ +#define WM8990_LROPGALON_BIT 5 +#define WM8990_LOPLON 0x0010 /* LOPLON */ +#define WM8990_LOPLON_BIT 4 +#define WM8990_LR12LOP 0x0004 /* LR12LOP */ +#define WM8990_LR12LOP_BIT 2 +#define WM8990_LL12LOP 0x0002 /* LL12LOP */ +#define WM8990_LL12LOP_BIT 1 +#define WM8990_LLOPGALOP 0x0001 /* LLOPGALOP */ +#define WM8990_LLOPGALOP_BIT 0 +/* + * R53 (0x35) - Line Mixer2 + */ +#define WM8990_RROPGARON 0x0040 /* RROPGARON */ +#define WM8990_RROPGARON_BIT 6 +#define WM8990_RLOPGARON 0x0020 /* RLOPGARON */ +#define WM8990_RLOPGARON_BIT 5 +#define WM8990_ROPRON 0x0010 /* ROPRON */ +#define WM8990_ROPRON_BIT 4 +#define WM8990_RL12ROP 0x0004 /* RL12ROP */ +#define WM8990_RL12ROP_BIT 2 +#define WM8990_RR12ROP 0x0002 /* RR12ROP */ +#define WM8990_RR12ROP_BIT 1 +#define WM8990_RROPGAROP 0x0001 /* RROPGAROP */ +#define WM8990_RROPGAROP_BIT 0 + +/* + * R54 (0x36) - Speaker Mixer + */ +#define WM8990_LB2SPK 0x0080 /* LB2SPK */ +#define WM8990_LB2SPK_BIT 7 +#define WM8990_RB2SPK 0x0040 /* RB2SPK */ +#define WM8990_RB2SPK_BIT 6 +#define WM8990_LI2SPK 0x0020 /* LI2SPK */ +#define WM8990_LI2SPK_BIT 5 +#define WM8990_RI2SPK 0x0010 /* RI2SPK */ +#define WM8990_RI2SPK_BIT 4 +#define WM8990_LOPGASPK 0x0008 /* LOPGASPK */ +#define WM8990_LOPGASPK_BIT 3 +#define WM8990_ROPGASPK 0x0004 /* ROPGASPK */ +#define WM8990_ROPGASPK_BIT 2 +#define WM8990_LDSPK 0x0002 /* LDSPK */ +#define WM8990_LDSPK_BIT 1 +#define WM8990_RDSPK 0x0001 /* RDSPK */ +#define WM8990_RDSPK_BIT 0 + +/* + * R55 (0x37) - Additional Control + */ +#define WM8990_VROI 0x0001 /* VROI */ + +/* + * R56 (0x38) - AntiPOP1 + */ +#define WM8990_DIS_LLINE 0x0020 /* DIS_LLINE */ +#define WM8990_DIS_RLINE 0x0010 /* DIS_RLINE */ +#define WM8990_DIS_OUT3 0x0008 /* DIS_OUT3 */ +#define WM8990_DIS_OUT4 0x0004 /* DIS_OUT4 */ +#define WM8990_DIS_LOUT 0x0002 /* DIS_LOUT */ +#define WM8990_DIS_ROUT 0x0001 /* DIS_ROUT */ + +/* + * R57 (0x39) - AntiPOP2 + */ +#define WM8990_SOFTST 0x0040 /* SOFTST */ +#define WM8990_BUFIOEN 0x0008 /* BUFIOEN */ +#define WM8990_BUFDCOPEN 0x0004 /* BUFDCOPEN */ +#define WM8990_POBCTRL 0x0002 /* POBCTRL */ +#define WM8990_VMIDTOG 0x0001 /* VMIDTOG */ + +/* + * R58 (0x3A) - MICBIAS + */ +#define WM8990_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */ +#define WM8990_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */ +#define WM8990_MCD 0x0004 /* MCD */ +#define WM8990_MBSEL 0x0001 /* MBSEL */ + +/* + * R60 (0x3C) - PLL1 + */ +#define WM8990_SDM 0x0080 /* SDM */ +#define WM8990_PRESCALE 0x0040 /* PRESCALE */ +#define WM8990_PLLN_MASK 0x000F /* PLLN - [3:0] */ + +/* + * R61 (0x3D) - PLL2 + */ +#define WM8990_PLLK1_MASK 0x00FF /* PLLK1 - [7:0] */ + +/* + * R62 (0x3E) - PLL3 + */ +#define WM8990_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */ + +#define WM8990_MCLK_DIV 0 +#define WM8990_DACCLK_DIV 1 +#define WM8990_ADCCLK_DIV 2 +#define WM8990_BCLK_DIV 3 + +#endif /* __WM8990REGISTERDEFS_H__ */ +/*------------------------------ END OF FILE ---------------------------------*/ diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c new file mode 100644 index 0000000000..590318aafa --- /dev/null +++ b/sound/soc/codecs/wm8991.c @@ -0,0 +1,1334 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * wm8991.c -- WM8991 ALSA Soc Audio driver + * + * Copyright 2007-2010 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * Graeme.Gregory@wolfsonmicro.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8991.h" + +struct wm8991_priv { + struct regmap *regmap; + unsigned int pcmclk; +}; + +static const struct reg_default wm8991_reg_defaults[] = { + { 1, 0x0000 }, /* R1 - Power Management (1) */ + { 2, 0x6000 }, /* R2 - Power Management (2) */ + { 3, 0x0000 }, /* R3 - Power Management (3) */ + { 4, 0x4050 }, /* R4 - Audio Interface (1) */ + { 5, 0x4000 }, /* R5 - Audio Interface (2) */ + { 6, 0x01C8 }, /* R6 - Clocking (1) */ + { 7, 0x0000 }, /* R7 - Clocking (2) */ + { 8, 0x0040 }, /* R8 - Audio Interface (3) */ + { 9, 0x0040 }, /* R9 - Audio Interface (4) */ + { 10, 0x0004 }, /* R10 - DAC CTRL */ + { 11, 0x00C0 }, /* R11 - Left DAC Digital Volume */ + { 12, 0x00C0 }, /* R12 - Right DAC Digital Volume */ + { 13, 0x0000 }, /* R13 - Digital Side Tone */ + { 14, 0x0100 }, /* R14 - ADC CTRL */ + { 15, 0x00C0 }, /* R15 - Left ADC Digital Volume */ + { 16, 0x00C0 }, /* R16 - Right ADC Digital Volume */ + + { 18, 0x0000 }, /* R18 - GPIO CTRL 1 */ + { 19, 0x1000 }, /* R19 - GPIO1 & GPIO2 */ + { 20, 0x1010 }, /* R20 - GPIO3 & GPIO4 */ + { 21, 0x1010 }, /* R21 - GPIO5 & GPIO6 */ + { 22, 0x8000 }, /* R22 - GPIOCTRL 2 */ + { 23, 0x0800 }, /* R23 - GPIO_POL */ + { 24, 0x008B }, /* R24 - Left Line Input 1&2 Volume */ + { 25, 0x008B }, /* R25 - Left Line Input 3&4 Volume */ + { 26, 0x008B }, /* R26 - Right Line Input 1&2 Volume */ + { 27, 0x008B }, /* R27 - Right Line Input 3&4 Volume */ + { 28, 0x0000 }, /* R28 - Left Output Volume */ + { 29, 0x0000 }, /* R29 - Right Output Volume */ + { 30, 0x0066 }, /* R30 - Line Outputs Volume */ + { 31, 0x0022 }, /* R31 - Out3/4 Volume */ + { 32, 0x0079 }, /* R32 - Left OPGA Volume */ + { 33, 0x0079 }, /* R33 - Right OPGA Volume */ + { 34, 0x0003 }, /* R34 - Speaker Volume */ + { 35, 0x0003 }, /* R35 - ClassD1 */ + + { 37, 0x0100 }, /* R37 - ClassD3 */ + + { 39, 0x0000 }, /* R39 - Input Mixer1 */ + { 40, 0x0000 }, /* R40 - Input Mixer2 */ + { 41, 0x0000 }, /* R41 - Input Mixer3 */ + { 42, 0x0000 }, /* R42 - Input Mixer4 */ + { 43, 0x0000 }, /* R43 - Input Mixer5 */ + { 44, 0x0000 }, /* R44 - Input Mixer6 */ + { 45, 0x0000 }, /* R45 - Output Mixer1 */ + { 46, 0x0000 }, /* R46 - Output Mixer2 */ + { 47, 0x0000 }, /* R47 - Output Mixer3 */ + { 48, 0x0000 }, /* R48 - Output Mixer4 */ + { 49, 0x0000 }, /* R49 - Output Mixer5 */ + { 50, 0x0000 }, /* R50 - Output Mixer6 */ + { 51, 0x0180 }, /* R51 - Out3/4 Mixer */ + { 52, 0x0000 }, /* R52 - Line Mixer1 */ + { 53, 0x0000 }, /* R53 - Line Mixer2 */ + { 54, 0x0000 }, /* R54 - Speaker Mixer */ + { 55, 0x0000 }, /* R55 - Additional Control */ + { 56, 0x0000 }, /* R56 - AntiPOP1 */ + { 57, 0x0000 }, /* R57 - AntiPOP2 */ + { 58, 0x0000 }, /* R58 - MICBIAS */ + + { 60, 0x0008 }, /* R60 - PLL1 */ + { 61, 0x0031 }, /* R61 - PLL2 */ + { 62, 0x0026 }, /* R62 - PLL3 */ +}; + +static bool wm8991_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8991_RESET: + return true; + default: + return false; + } +} + +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(in_pga_tlv, -1650, 150, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(out_mix_tlv, -2100, 300, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_pga_tlv, + 0x00, 0x2f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(SNDRV_CTL_TLVD_DB_GAIN_MUTE, 0, 1), + 0x30, 0x7f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-7300, 100, 0), +); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_dac_tlv, + 0x00, 0xbf, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-71625, 375, 1), + 0xc0, 0xff, SNDRV_CTL_TLVD_DB_SCALE_ITEM(0, 0, 0), +); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(in_adc_tlv, + 0x00, 0xef, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-71625, 375, 1), + 0xf0, 0xff, SNDRV_CTL_TLVD_DB_SCALE_ITEM(17625, 0, 0), +); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(out_sidetone_tlv, + 0x00, 0x0c, SNDRV_CTL_TLVD_DB_SCALE_ITEM(-3600, 300, 0), + 0x0d, 0x0f, SNDRV_CTL_TLVD_DB_SCALE_ITEM(0, 0, 0), +); + +static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int reg = kcontrol->private_value & 0xff; + int ret; + u16 val; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + /* now hit the volume update bits (always bit 8) */ + val = snd_soc_component_read(component, reg); + return snd_soc_component_write(component, reg, val | 0x0100); +} + +static const char *wm8991_digital_sidetone[] = +{"None", "Left ADC", "Right ADC", "Reserved"}; + +static SOC_ENUM_SINGLE_DECL(wm8991_left_digital_sidetone_enum, + WM8991_DIGITAL_SIDE_TONE, + WM8991_ADC_TO_DACL_SHIFT, + wm8991_digital_sidetone); + +static SOC_ENUM_SINGLE_DECL(wm8991_right_digital_sidetone_enum, + WM8991_DIGITAL_SIDE_TONE, + WM8991_ADC_TO_DACR_SHIFT, + wm8991_digital_sidetone); + +static const char *wm8991_adcmode[] = +{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"}; + +static SOC_ENUM_SINGLE_DECL(wm8991_right_adcmode_enum, + WM8991_ADC_CTRL, + WM8991_ADC_HPF_CUT_SHIFT, + wm8991_adcmode); + +static const struct snd_kcontrol_new wm8991_snd_controls[] = { + /* INMIXL */ + SOC_SINGLE("LIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L12MNBST_BIT, 1, 0), + SOC_SINGLE("LIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L34MNBST_BIT, 1, 0), + /* INMIXR */ + SOC_SINGLE("RIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R12MNBST_BIT, 1, 0), + SOC_SINGLE("RIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R34MNBST_BIT, 1, 0), + + /* LOMIX */ + SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER3, + WM8991_LLI3LOVOL_SHIFT, WM8991_LLI3LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3, + WM8991_LR12LOVOL_SHIFT, WM8991_LR12LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3, + WM8991_LL12LOVOL_SHIFT, WM8991_LL12LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER5, + WM8991_LRI3LOVOL_SHIFT, WM8991_LRI3LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER5, + WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER5, + WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv), + + /* ROMIX */ + SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER4, + WM8991_RRI3ROVOL_SHIFT, WM8991_RRI3ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4, + WM8991_RL12ROVOL_SHIFT, WM8991_RL12ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4, + WM8991_RR12ROVOL_SHIFT, WM8991_RR12ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER6, + WM8991_RLI3ROVOL_SHIFT, WM8991_RLI3ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER6, + WM8991_RLBROVOL_SHIFT, WM8991_RLBROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER6, + WM8991_RRBROVOL_SHIFT, WM8991_RRBROVOL_MASK, 1, out_mix_tlv), + + /* LOUT */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8991_LEFT_OUTPUT_VOLUME, + WM8991_LOUTVOL_SHIFT, WM8991_LOUTVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("LOUT ZC", WM8991_LEFT_OUTPUT_VOLUME, WM8991_LOZC_BIT, 1, 0), + + /* ROUT */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8991_RIGHT_OUTPUT_VOLUME, + WM8991_ROUTVOL_SHIFT, WM8991_ROUTVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("ROUT ZC", WM8991_RIGHT_OUTPUT_VOLUME, WM8991_ROZC_BIT, 1, 0), + + /* LOPGA */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8991_LEFT_OPGA_VOLUME, + WM8991_LOPGAVOL_SHIFT, WM8991_LOPGAVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("LOPGA ZC Switch", WM8991_LEFT_OPGA_VOLUME, + WM8991_LOPGAZC_BIT, 1, 0), + + /* ROPGA */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8991_RIGHT_OPGA_VOLUME, + WM8991_ROPGAVOL_SHIFT, WM8991_ROPGAVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("ROPGA ZC Switch", WM8991_RIGHT_OPGA_VOLUME, + WM8991_ROPGAZC_BIT, 1, 0), + + SOC_SINGLE("LON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_LONMUTE_BIT, 1, 0), + SOC_SINGLE("LOP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_LOPMUTE_BIT, 1, 0), + SOC_SINGLE("LOP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_LOATTN_BIT, 1, 0), + SOC_SINGLE("RON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_RONMUTE_BIT, 1, 0), + SOC_SINGLE("ROP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_ROPMUTE_BIT, 1, 0), + SOC_SINGLE("ROP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_ROATTN_BIT, 1, 0), + + SOC_SINGLE("OUT3 Mute Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT3MUTE_BIT, 1, 0), + SOC_SINGLE("OUT3 Attenuation Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT3ATTN_BIT, 1, 0), + + SOC_SINGLE("OUT4 Mute Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT4MUTE_BIT, 1, 0), + SOC_SINGLE("OUT4 Attenuation Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT4ATTN_BIT, 1, 0), + + SOC_SINGLE("Speaker Mode Switch", WM8991_CLASSD1, + WM8991_CDMODE_BIT, 1, 0), + + SOC_SINGLE("Speaker Output Attenuation Volume", WM8991_SPEAKER_VOLUME, + WM8991_SPKVOL_SHIFT, WM8991_SPKVOL_MASK, 0), + SOC_SINGLE("Speaker DC Boost Volume", WM8991_CLASSD3, + WM8991_DCGAIN_SHIFT, WM8991_DCGAIN_MASK, 0), + SOC_SINGLE("Speaker AC Boost Volume", WM8991_CLASSD3, + WM8991_ACGAIN_SHIFT, WM8991_ACGAIN_MASK, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume", + WM8991_LEFT_DAC_DIGITAL_VOLUME, + WM8991_DACL_VOL_SHIFT, + WM8991_DACL_VOL_MASK, + 0, + out_dac_tlv), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume", + WM8991_RIGHT_DAC_DIGITAL_VOLUME, + WM8991_DACR_VOL_SHIFT, + WM8991_DACR_VOL_MASK, + 0, + out_dac_tlv), + + SOC_ENUM("Left Digital Sidetone", wm8991_left_digital_sidetone_enum), + SOC_ENUM("Right Digital Sidetone", wm8991_right_digital_sidetone_enum), + + SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE, + WM8991_ADCL_DAC_SVOL_SHIFT, WM8991_ADCL_DAC_SVOL_MASK, 0, + out_sidetone_tlv), + SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE, + WM8991_ADCR_DAC_SVOL_SHIFT, WM8991_ADCR_DAC_SVOL_MASK, 0, + out_sidetone_tlv), + + SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8991_ADC_CTRL, + WM8991_ADC_HPF_ENA_BIT, 1, 0), + + SOC_ENUM("ADC HPF Mode", wm8991_right_adcmode_enum), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume", + WM8991_LEFT_ADC_DIGITAL_VOLUME, + WM8991_ADCL_VOL_SHIFT, + WM8991_ADCL_VOL_MASK, + 0, + in_adc_tlv), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume", + WM8991_RIGHT_ADC_DIGITAL_VOLUME, + WM8991_ADCR_VOL_SHIFT, + WM8991_ADCR_VOL_MASK, + 0, + in_adc_tlv), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume", + WM8991_LEFT_LINE_INPUT_1_2_VOLUME, + WM8991_LIN12VOL_SHIFT, + WM8991_LIN12VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("LIN12 ZC Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME, + WM8991_LI12ZC_BIT, 1, 0), + + SOC_SINGLE("LIN12 Mute Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME, + WM8991_LI12MUTE_BIT, 1, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume", + WM8991_LEFT_LINE_INPUT_3_4_VOLUME, + WM8991_LIN34VOL_SHIFT, + WM8991_LIN34VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("LIN34 ZC Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME, + WM8991_LI34ZC_BIT, 1, 0), + + SOC_SINGLE("LIN34 Mute Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME, + WM8991_LI34MUTE_BIT, 1, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume", + WM8991_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8991_RIN12VOL_SHIFT, + WM8991_RIN12VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("RIN12 ZC Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8991_RI12ZC_BIT, 1, 0), + + SOC_SINGLE("RIN12 Mute Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8991_RI12MUTE_BIT, 1, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume", + WM8991_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8991_RIN34VOL_SHIFT, + WM8991_RIN34VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("RIN34 ZC Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8991_RI34ZC_BIT, 1, 0), + + SOC_SINGLE("RIN34 Mute Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8991_RI34MUTE_BIT, 1, 0), +}; + +/* + * _DAPM_ Controls + */ +static int outmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u32 reg_shift = kcontrol->private_value & 0xfff; + int ret = 0; + u16 reg; + + switch (reg_shift) { + case WM8991_SPEAKER_MIXER | (WM8991_LDSPK_BIT << 8): + reg = snd_soc_component_read(component, WM8991_OUTPUT_MIXER1); + if (reg & WM8991_LDLO) { + printk(KERN_WARNING + "Cannot set as Output Mixer 1 LDLO Set\n"); + ret = -1; + } + break; + + case WM8991_SPEAKER_MIXER | (WM8991_RDSPK_BIT << 8): + reg = snd_soc_component_read(component, WM8991_OUTPUT_MIXER2); + if (reg & WM8991_RDRO) { + printk(KERN_WARNING + "Cannot set as Output Mixer 2 RDRO Set\n"); + ret = -1; + } + break; + + case WM8991_OUTPUT_MIXER1 | (WM8991_LDLO_BIT << 8): + reg = snd_soc_component_read(component, WM8991_SPEAKER_MIXER); + if (reg & WM8991_LDSPK) { + printk(KERN_WARNING + "Cannot set as Speaker Mixer LDSPK Set\n"); + ret = -1; + } + break; + + case WM8991_OUTPUT_MIXER2 | (WM8991_RDRO_BIT << 8): + reg = snd_soc_component_read(component, WM8991_SPEAKER_MIXER); + if (reg & WM8991_RDSPK) { + printk(KERN_WARNING + "Cannot set as Speaker Mixer RDSPK Set\n"); + ret = -1; + } + break; + } + + return ret; +} + +/* INMIX dB values */ +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(in_mix_tlv, -1200, 300, 1); + +/* Left In PGA Connections */ +static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = { + SOC_DAPM_SINGLE("LIN1 Switch", WM8991_INPUT_MIXER2, WM8991_LMN1_BIT, 1, 0), + SOC_DAPM_SINGLE("LIN2 Switch", WM8991_INPUT_MIXER2, WM8991_LMP2_BIT, 1, 0), +}; + +static const struct snd_kcontrol_new wm8991_dapm_lin34_pga_controls[] = { + SOC_DAPM_SINGLE("LIN3 Switch", WM8991_INPUT_MIXER2, WM8991_LMN3_BIT, 1, 0), + SOC_DAPM_SINGLE("LIN4 Switch", WM8991_INPUT_MIXER2, WM8991_LMP4_BIT, 1, 0), +}; + +/* Right In PGA Connections */ +static const struct snd_kcontrol_new wm8991_dapm_rin12_pga_controls[] = { + SOC_DAPM_SINGLE("RIN1 Switch", WM8991_INPUT_MIXER2, WM8991_RMN1_BIT, 1, 0), + SOC_DAPM_SINGLE("RIN2 Switch", WM8991_INPUT_MIXER2, WM8991_RMP2_BIT, 1, 0), +}; + +static const struct snd_kcontrol_new wm8991_dapm_rin34_pga_controls[] = { + SOC_DAPM_SINGLE("RIN3 Switch", WM8991_INPUT_MIXER2, WM8991_RMN3_BIT, 1, 0), + SOC_DAPM_SINGLE("RIN4 Switch", WM8991_INPUT_MIXER2, WM8991_RMP4_BIT, 1, 0), +}; + +/* INMIXL */ +static const struct snd_kcontrol_new wm8991_dapm_inmixl_controls[] = { + SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8991_INPUT_MIXER3, + WM8991_LDBVOL_SHIFT, WM8991_LDBVOL_MASK, 0, in_mix_tlv), + SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8991_INPUT_MIXER5, WM8991_LI2BVOL_SHIFT, + 7, 0, in_mix_tlv), + SOC_DAPM_SINGLE("LINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT, + 1, 0), + SOC_DAPM_SINGLE("LINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT, + 1, 0), +}; + +/* INMIXR */ +static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = { + SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8991_INPUT_MIXER4, + WM8991_RDBVOL_SHIFT, WM8991_RDBVOL_MASK, 0, in_mix_tlv), + SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8991_INPUT_MIXER6, WM8991_RI2BVOL_SHIFT, + 7, 0, in_mix_tlv), + SOC_DAPM_SINGLE("RINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT, + 1, 0), + SOC_DAPM_SINGLE("RINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT, + 1, 0), +}; + +/* AINLMUX */ +static const char *wm8991_ainlmux[] = +{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"}; + +static SOC_ENUM_SINGLE_DECL(wm8991_ainlmux_enum, + WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT, + wm8991_ainlmux); + +static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls = + SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum); + +/* DIFFINL */ + +/* AINRMUX */ +static const char *wm8991_ainrmux[] = +{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"}; + +static SOC_ENUM_SINGLE_DECL(wm8991_ainrmux_enum, + WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT, + wm8991_ainrmux); + +static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls = + SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum); + +/* LOMIX */ +static const struct snd_kcontrol_new wm8991_dapm_lomix_controls[] = { + SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LRBLO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LLBLO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LRI3LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LLI3LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LR12LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LL12LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8991_OUTPUT_MIXER1, + WM8991_LDLO_BIT, 1, 0), +}; + +/* ROMIX */ +static const struct snd_kcontrol_new wm8991_dapm_romix_controls[] = { + SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RLBRO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RRBRO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RLI3RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RRI3RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RL12RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RR12RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8991_OUTPUT_MIXER2, + WM8991_RDRO_BIT, 1, 0), +}; + +/* LONMIX */ +static const struct snd_kcontrol_new wm8991_dapm_lonmix_controls[] = { + SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1, + WM8991_LLOPGALON_BIT, 1, 0), + SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER1, + WM8991_LROPGALON_BIT, 1, 0), + SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8991_LINE_MIXER1, + WM8991_LOPLON_BIT, 1, 0), +}; + +/* LOPMIX */ +static const struct snd_kcontrol_new wm8991_dapm_lopmix_controls[] = { + SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER1, + WM8991_LR12LOP_BIT, 1, 0), + SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER1, + WM8991_LL12LOP_BIT, 1, 0), + SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1, + WM8991_LLOPGALOP_BIT, 1, 0), +}; + +/* RONMIX */ +static const struct snd_kcontrol_new wm8991_dapm_ronmix_controls[] = { + SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2, + WM8991_RROPGARON_BIT, 1, 0), + SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER2, + WM8991_RLOPGARON_BIT, 1, 0), + SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8991_LINE_MIXER2, + WM8991_ROPRON_BIT, 1, 0), +}; + +/* ROPMIX */ +static const struct snd_kcontrol_new wm8991_dapm_ropmix_controls[] = { + SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER2, + WM8991_RL12ROP_BIT, 1, 0), + SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER2, + WM8991_RR12ROP_BIT, 1, 0), + SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2, + WM8991_RROPGAROP_BIT, 1, 0), +}; + +/* OUT3MIX */ +static const struct snd_kcontrol_new wm8991_dapm_out3mix_controls[] = { + SOC_DAPM_SINGLE("OUT3MIX LIN4RXN Bypass Switch", WM8991_OUT3_4_MIXER, + WM8991_LI4O3_BIT, 1, 0), + SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8991_OUT3_4_MIXER, + WM8991_LPGAO3_BIT, 1, 0), +}; + +/* OUT4MIX */ +static const struct snd_kcontrol_new wm8991_dapm_out4mix_controls[] = { + SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8991_OUT3_4_MIXER, + WM8991_RPGAO4_BIT, 1, 0), + SOC_DAPM_SINGLE("OUT4MIX RIN4RXP Bypass Switch", WM8991_OUT3_4_MIXER, + WM8991_RI4O4_BIT, 1, 0), +}; + +/* SPKMIX */ +static const struct snd_kcontrol_new wm8991_dapm_spkmix_controls[] = { + SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_LI2SPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_LB2SPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8991_SPEAKER_MIXER, + WM8991_LOPGASPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8991_SPEAKER_MIXER, + WM8991_LDSPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8991_SPEAKER_MIXER, + WM8991_RDSPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8991_SPEAKER_MIXER, + WM8991_ROPGASPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_RL12ROP_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_RI2SPK_BIT, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = { + /* Input Side */ + /* Input Lines */ + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + SND_SOC_DAPM_INPUT("LIN3"), + SND_SOC_DAPM_INPUT("LIN4RXN"), + SND_SOC_DAPM_INPUT("RIN3"), + SND_SOC_DAPM_INPUT("RIN4RXP"), + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("RIN2"), + SND_SOC_DAPM_INPUT("Internal ADC Source"), + + SND_SOC_DAPM_SUPPLY("INL", WM8991_POWER_MANAGEMENT_2, + WM8991_AINL_ENA_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("INR", WM8991_POWER_MANAGEMENT_2, + WM8991_AINR_ENA_BIT, 0, NULL, 0), + + /* DACs */ + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8991_POWER_MANAGEMENT_2, + WM8991_ADCL_ENA_BIT, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8991_POWER_MANAGEMENT_2, + WM8991_ADCR_ENA_BIT, 0), + + /* Input PGAs */ + SND_SOC_DAPM_MIXER("LIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN12_ENA_BIT, + 0, &wm8991_dapm_lin12_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_lin12_pga_controls)), + SND_SOC_DAPM_MIXER("LIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN34_ENA_BIT, + 0, &wm8991_dapm_lin34_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_lin34_pga_controls)), + SND_SOC_DAPM_MIXER("RIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN12_ENA_BIT, + 0, &wm8991_dapm_rin12_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_rin12_pga_controls)), + SND_SOC_DAPM_MIXER("RIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN34_ENA_BIT, + 0, &wm8991_dapm_rin34_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)), + + /* INMIXL */ + SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0, + &wm8991_dapm_inmixl_controls[0], + ARRAY_SIZE(wm8991_dapm_inmixl_controls)), + + /* AINLMUX */ + SND_SOC_DAPM_MUX("AINLMUX", SND_SOC_NOPM, 0, 0, + &wm8991_dapm_ainlmux_controls), + + /* INMIXR */ + SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0, + &wm8991_dapm_inmixr_controls[0], + ARRAY_SIZE(wm8991_dapm_inmixr_controls)), + + /* AINRMUX */ + SND_SOC_DAPM_MUX("AINRMUX", SND_SOC_NOPM, 0, 0, + &wm8991_dapm_ainrmux_controls), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8991_POWER_MANAGEMENT_3, + WM8991_DACL_ENA_BIT, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8991_POWER_MANAGEMENT_3, + WM8991_DACR_ENA_BIT, 0), + + /* LOMIX */ + SND_SOC_DAPM_MIXER_E("LOMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOMIX_ENA_BIT, + 0, &wm8991_dapm_lomix_controls[0], + ARRAY_SIZE(wm8991_dapm_lomix_controls), + outmixer_event, SND_SOC_DAPM_PRE_REG), + + /* LONMIX */ + SND_SOC_DAPM_MIXER("LONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LON_ENA_BIT, 0, + &wm8991_dapm_lonmix_controls[0], + ARRAY_SIZE(wm8991_dapm_lonmix_controls)), + + /* LOPMIX */ + SND_SOC_DAPM_MIXER("LOPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOP_ENA_BIT, 0, + &wm8991_dapm_lopmix_controls[0], + ARRAY_SIZE(wm8991_dapm_lopmix_controls)), + + /* OUT3MIX */ + SND_SOC_DAPM_MIXER("OUT3MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT3_ENA_BIT, 0, + &wm8991_dapm_out3mix_controls[0], + ARRAY_SIZE(wm8991_dapm_out3mix_controls)), + + /* SPKMIX */ + SND_SOC_DAPM_MIXER_E("SPKMIX", WM8991_POWER_MANAGEMENT_1, WM8991_SPK_ENA_BIT, 0, + &wm8991_dapm_spkmix_controls[0], + ARRAY_SIZE(wm8991_dapm_spkmix_controls), outmixer_event, + SND_SOC_DAPM_PRE_REG), + + /* OUT4MIX */ + SND_SOC_DAPM_MIXER("OUT4MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT4_ENA_BIT, 0, + &wm8991_dapm_out4mix_controls[0], + ARRAY_SIZE(wm8991_dapm_out4mix_controls)), + + /* ROPMIX */ + SND_SOC_DAPM_MIXER("ROPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROP_ENA_BIT, 0, + &wm8991_dapm_ropmix_controls[0], + ARRAY_SIZE(wm8991_dapm_ropmix_controls)), + + /* RONMIX */ + SND_SOC_DAPM_MIXER("RONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_RON_ENA_BIT, 0, + &wm8991_dapm_ronmix_controls[0], + ARRAY_SIZE(wm8991_dapm_ronmix_controls)), + + /* ROMIX */ + SND_SOC_DAPM_MIXER_E("ROMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROMIX_ENA_BIT, + 0, &wm8991_dapm_romix_controls[0], + ARRAY_SIZE(wm8991_dapm_romix_controls), + outmixer_event, SND_SOC_DAPM_PRE_REG), + + /* LOUT PGA */ + SND_SOC_DAPM_PGA("LOUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_LOUT_ENA_BIT, 0, + NULL, 0), + + /* ROUT PGA */ + SND_SOC_DAPM_PGA("ROUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_ROUT_ENA_BIT, 0, + NULL, 0), + + /* LOPGA */ + SND_SOC_DAPM_PGA("LOPGA", WM8991_POWER_MANAGEMENT_3, WM8991_LOPGA_ENA_BIT, 0, + NULL, 0), + + /* ROPGA */ + SND_SOC_DAPM_PGA("ROPGA", WM8991_POWER_MANAGEMENT_3, WM8991_ROPGA_ENA_BIT, 0, + NULL, 0), + + /* MICBIAS */ + SND_SOC_DAPM_SUPPLY("MICBIAS", WM8991_POWER_MANAGEMENT_1, + WM8991_MICBIAS_ENA_BIT, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("LON"), + SND_SOC_DAPM_OUTPUT("LOP"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("SPKN"), + SND_SOC_DAPM_OUTPUT("SPKP"), + SND_SOC_DAPM_OUTPUT("ROUT"), + SND_SOC_DAPM_OUTPUT("OUT4"), + SND_SOC_DAPM_OUTPUT("ROP"), + SND_SOC_DAPM_OUTPUT("RON"), + SND_SOC_DAPM_OUTPUT("OUT"), + + SND_SOC_DAPM_OUTPUT("Internal DAC Sink"), +}; + +static const struct snd_soc_dapm_route wm8991_dapm_routes[] = { + /* Make DACs turn on when playing even if not mixed into any outputs */ + {"Internal DAC Sink", NULL, "Left DAC"}, + {"Internal DAC Sink", NULL, "Right DAC"}, + + /* Make ADCs turn on when recording even if not mixed from any inputs */ + {"Left ADC", NULL, "Internal ADC Source"}, + {"Right ADC", NULL, "Internal ADC Source"}, + + /* Input Side */ + {"INMIXL", NULL, "INL"}, + {"AINLMUX", NULL, "INL"}, + {"INMIXR", NULL, "INR"}, + {"AINRMUX", NULL, "INR"}, + /* LIN12 PGA */ + {"LIN12 PGA", "LIN1 Switch", "LIN1"}, + {"LIN12 PGA", "LIN2 Switch", "LIN2"}, + /* LIN34 PGA */ + {"LIN34 PGA", "LIN3 Switch", "LIN3"}, + {"LIN34 PGA", "LIN4 Switch", "LIN4RXN"}, + /* INMIXL */ + {"INMIXL", "Record Left Volume", "LOMIX"}, + {"INMIXL", "LIN2 Volume", "LIN2"}, + {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"}, + {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"}, + /* AINLMUX */ + {"AINLMUX", "INMIXL Mix", "INMIXL"}, + {"AINLMUX", "DIFFINL Mix", "LIN12 PGA"}, + {"AINLMUX", "DIFFINL Mix", "LIN34 PGA"}, + {"AINLMUX", "RXVOICE Mix", "LIN4RXN"}, + {"AINLMUX", "RXVOICE Mix", "RIN4RXP"}, + /* ADC */ + {"Left ADC", NULL, "AINLMUX"}, + + /* RIN12 PGA */ + {"RIN12 PGA", "RIN1 Switch", "RIN1"}, + {"RIN12 PGA", "RIN2 Switch", "RIN2"}, + /* RIN34 PGA */ + {"RIN34 PGA", "RIN3 Switch", "RIN3"}, + {"RIN34 PGA", "RIN4 Switch", "RIN4RXP"}, + /* INMIXL */ + {"INMIXR", "Record Right Volume", "ROMIX"}, + {"INMIXR", "RIN2 Volume", "RIN2"}, + {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"}, + {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"}, + /* AINRMUX */ + {"AINRMUX", "INMIXR Mix", "INMIXR"}, + {"AINRMUX", "DIFFINR Mix", "RIN12 PGA"}, + {"AINRMUX", "DIFFINR Mix", "RIN34 PGA"}, + {"AINRMUX", "RXVOICE Mix", "LIN4RXN"}, + {"AINRMUX", "RXVOICE Mix", "RIN4RXP"}, + /* ADC */ + {"Right ADC", NULL, "AINRMUX"}, + + /* LOMIX */ + {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"}, + {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"}, + {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, + {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, + {"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"}, + {"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"}, + {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"}, + + /* ROMIX */ + {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"}, + {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"}, + {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, + {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, + {"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"}, + {"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"}, + {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"}, + + /* SPKMIX */ + {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"}, + {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"}, + {"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"}, + {"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"}, + {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"}, + {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"}, + {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"}, + {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"}, + + /* LONMIX */ + {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"}, + {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"}, + {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"}, + + /* LOPMIX */ + {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"}, + {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"}, + {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"}, + + /* OUT3MIX */ + {"OUT3MIX", "OUT3MIX LIN4RXN Bypass Switch", "LIN4RXN"}, + {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"}, + + /* OUT4MIX */ + {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"}, + {"OUT4MIX", "OUT4MIX RIN4RXP Bypass Switch", "RIN4RXP"}, + + /* RONMIX */ + {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"}, + {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"}, + {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"}, + + /* ROPMIX */ + {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"}, + {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"}, + {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"}, + + /* Out Mixer PGAs */ + {"LOPGA", NULL, "LOMIX"}, + {"ROPGA", NULL, "ROMIX"}, + + {"LOUT PGA", NULL, "LOMIX"}, + {"ROUT PGA", NULL, "ROMIX"}, + + /* Output Pins */ + {"LON", NULL, "LONMIX"}, + {"LOP", NULL, "LOPMIX"}, + {"OUT", NULL, "OUT3MIX"}, + {"LOUT", NULL, "LOUT PGA"}, + {"SPKN", NULL, "SPKMIX"}, + {"ROUT", NULL, "ROUT PGA"}, + {"OUT4", NULL, "OUT4MIX"}, + {"ROP", NULL, "ROPMIX"}, + {"RON", NULL, "RONMIX"}, +}; + +/* PLL divisors */ +struct _pll_div { + u32 div2; + u32 n; + u32 k; +}; + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 16) * 10) + +static void pll_factors(struct _pll_div *pll_div, unsigned int target, + unsigned int source) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod; + + + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div->div2 = 1; + Ndiv = target / source; + } else + pll_div->div2 = 0; + + if ((Ndiv < 6) || (Ndiv > 12)) + printk(KERN_WARNING + "WM8991 N value outwith recommended range! N = %d\n", Ndiv); + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; +} + +static int wm8991_set_dai_pll(struct snd_soc_dai *codec_dai, + int pll_id, int src, unsigned int freq_in, unsigned int freq_out) +{ + u16 reg; + struct snd_soc_component *component = codec_dai->component; + struct _pll_div pll_div; + + if (freq_in && freq_out) { + pll_factors(&pll_div, freq_out * 4, freq_in); + + /* Turn on PLL */ + reg = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_2); + reg |= WM8991_PLL_ENA; + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_2, reg); + + /* sysclk comes from PLL */ + reg = snd_soc_component_read(component, WM8991_CLOCKING_2); + snd_soc_component_write(component, WM8991_CLOCKING_2, reg | WM8991_SYSCLK_SRC); + + /* set up N , fractional mode and pre-divisor if necessary */ + snd_soc_component_write(component, WM8991_PLL1, pll_div.n | WM8991_SDM | + (pll_div.div2 ? WM8991_PRESCALE : 0)); + snd_soc_component_write(component, WM8991_PLL2, (u8)(pll_div.k>>8)); + snd_soc_component_write(component, WM8991_PLL3, (u8)(pll_div.k & 0xFF)); + } else { + /* Turn on PLL */ + reg = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_2); + reg &= ~WM8991_PLL_ENA; + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_2, reg); + } + return 0; +} + +/* + * Set's ADC and Voice DAC format. + */ +static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 audio1, audio3; + + audio1 = snd_soc_component_read(component, WM8991_AUDIO_INTERFACE_1); + audio3 = snd_soc_component_read(component, WM8991_AUDIO_INTERFACE_3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + audio3 &= ~WM8991_AIF_MSTR1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + audio3 |= WM8991_AIF_MSTR1; + break; + default: + return -EINVAL; + } + + audio1 &= ~WM8991_AIF_FMT_MASK; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + audio1 |= WM8991_AIF_TMF_I2S; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_RIGHT_J: + audio1 |= WM8991_AIF_TMF_RIGHTJ; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_LEFT_J: + audio1 |= WM8991_AIF_TMF_LEFTJ; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_A: + audio1 |= WM8991_AIF_TMF_DSP; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_B: + audio1 |= WM8991_AIF_TMF_DSP | WM8991_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8991_AUDIO_INTERFACE_1, audio1); + snd_soc_component_write(component, WM8991_AUDIO_INTERFACE_3, audio3); + return 0; +} + +static int wm8991_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + u16 reg; + + switch (div_id) { + case WM8991_MCLK_DIV: + reg = snd_soc_component_read(component, WM8991_CLOCKING_2) & + ~WM8991_MCLK_DIV_MASK; + snd_soc_component_write(component, WM8991_CLOCKING_2, reg | div); + break; + case WM8991_DACCLK_DIV: + reg = snd_soc_component_read(component, WM8991_CLOCKING_2) & + ~WM8991_DAC_CLKDIV_MASK; + snd_soc_component_write(component, WM8991_CLOCKING_2, reg | div); + break; + case WM8991_ADCCLK_DIV: + reg = snd_soc_component_read(component, WM8991_CLOCKING_2) & + ~WM8991_ADC_CLKDIV_MASK; + snd_soc_component_write(component, WM8991_CLOCKING_2, reg | div); + break; + case WM8991_BCLK_DIV: + reg = snd_soc_component_read(component, WM8991_CLOCKING_1) & + ~WM8991_BCLK_DIV_MASK; + snd_soc_component_write(component, WM8991_CLOCKING_1, reg | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + */ +static int wm8991_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + u16 audio1 = snd_soc_component_read(component, WM8991_AUDIO_INTERFACE_1); + + audio1 &= ~WM8991_AIF_WL_MASK; + /* bit size */ + switch (params_width(params)) { + case 16: + break; + case 20: + audio1 |= WM8991_AIF_WL_20BITS; + break; + case 24: + audio1 |= WM8991_AIF_WL_24BITS; + break; + case 32: + audio1 |= WM8991_AIF_WL_32BITS; + break; + } + + snd_soc_component_write(component, WM8991_AUDIO_INTERFACE_1, audio1); + return 0; +} + +static int wm8991_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + u16 val; + + val = snd_soc_component_read(component, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE; + if (mute) + snd_soc_component_write(component, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE); + else + snd_soc_component_write(component, WM8991_DAC_CTRL, val); + return 0; +} + +static int wm8991_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8991_priv *wm8991 = snd_soc_component_get_drvdata(component); + u16 val; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID=2*50k */ + val = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_1) & + ~WM8991_VMID_MODE_MASK; + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, val | 0x2); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + regcache_sync(wm8991->regmap); + /* Enable all output discharge bits */ + snd_soc_component_write(component, WM8991_ANTIPOP1, WM8991_DIS_LLINE | + WM8991_DIS_RLINE | WM8991_DIS_OUT3 | + WM8991_DIS_OUT4 | WM8991_DIS_LOUT | + WM8991_DIS_ROUT); + + /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */ + snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL | + WM8991_VMIDTOG); + + /* Delay to allow output caps to discharge */ + msleep(300); + + /* Disable VMIDTOG */ + snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL); + + /* disable all output discharge bits */ + snd_soc_component_write(component, WM8991_ANTIPOP1, 0); + + /* Enable outputs */ + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1b00); + + msleep(50); + + /* Enable VMID at 2x50k */ + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f02); + + msleep(100); + + /* Enable VREF */ + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f03); + + msleep(600); + + /* Enable BUFIOEN */ + snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL | + WM8991_BUFIOEN); + + /* Disable outputs */ + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x3); + + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_BUFIOEN); + } + + /* VMID=2*250k */ + val = snd_soc_component_read(component, WM8991_POWER_MANAGEMENT_1) & + ~WM8991_VMID_MODE_MASK; + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, val | 0x4); + break; + + case SND_SOC_BIAS_OFF: + /* Enable POBCTRL and SOFT_ST */ + snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_POBCTRL | WM8991_BUFIOEN); + + /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_component_write(component, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL | + WM8991_BUFIOEN); + + /* mute DAC */ + val = snd_soc_component_read(component, WM8991_DAC_CTRL); + snd_soc_component_write(component, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE); + + /* Enable any disabled outputs */ + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f03); + + /* Disable VMID */ + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x1f01); + + msleep(300); + + /* Enable all output discharge bits */ + snd_soc_component_write(component, WM8991_ANTIPOP1, WM8991_DIS_LLINE | + WM8991_DIS_RLINE | WM8991_DIS_OUT3 | + WM8991_DIS_OUT4 | WM8991_DIS_LOUT | + WM8991_DIS_ROUT); + + /* Disable VREF */ + snd_soc_component_write(component, WM8991_POWER_MANAGEMENT_1, 0x0); + + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_component_write(component, WM8991_ANTIPOP2, 0x0); + regcache_mark_dirty(wm8991->regmap); + break; + } + + return 0; +} + +#define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm8991_ops = { + .hw_params = wm8991_hw_params, + .mute_stream = wm8991_mute, + .set_fmt = wm8991_set_dai_fmt, + .set_clkdiv = wm8991_set_dai_clkdiv, + .set_pll = wm8991_set_dai_pll, + .no_capture_mute = 1, +}; + +/* + * The WM8991 supports 2 different and mutually exclusive DAI + * configurations. + * + * 1. ADC/DAC on Primary Interface + * 2. ADC on Primary Interface/DAC on secondary + */ +static struct snd_soc_dai_driver wm8991_dai = { + /* ADC/DAC on primary */ + .name = "wm8991", + .id = 1, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8991_FORMATS + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8991_FORMATS + }, + .ops = &wm8991_ops +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8991 = { + .set_bias_level = wm8991_set_bias_level, + .controls = wm8991_snd_controls, + .num_controls = ARRAY_SIZE(wm8991_snd_controls), + .dapm_widgets = wm8991_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8991_dapm_widgets), + .dapm_routes = wm8991_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8991_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8991_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM8991_PLL3, + .volatile_reg = wm8991_volatile, + .reg_defaults = wm8991_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8991_reg_defaults), + .cache_type = REGCACHE_MAPLE, +}; + +static int wm8991_i2c_probe(struct i2c_client *i2c) +{ + struct wm8991_priv *wm8991; + unsigned int val; + int ret; + + wm8991 = devm_kzalloc(&i2c->dev, sizeof(*wm8991), GFP_KERNEL); + if (!wm8991) + return -ENOMEM; + + wm8991->regmap = devm_regmap_init_i2c(i2c, &wm8991_regmap); + if (IS_ERR(wm8991->regmap)) + return PTR_ERR(wm8991->regmap); + + i2c_set_clientdata(i2c, wm8991); + + ret = regmap_read(wm8991->regmap, WM8991_RESET, &val); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read device ID: %d\n", ret); + return ret; + } + if (val != 0x8991) { + dev_err(&i2c->dev, "Device with ID %x is not a WM8991\n", val); + return -EINVAL; + } + + ret = regmap_write(wm8991->regmap, WM8991_RESET, 0); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + regmap_update_bits(wm8991->regmap, WM8991_AUDIO_INTERFACE_4, + WM8991_ALRCGPIO1, WM8991_ALRCGPIO1); + + regmap_update_bits(wm8991->regmap, WM8991_GPIO1_GPIO2, + WM8991_GPIO1_SEL_MASK, 1); + + regmap_update_bits(wm8991->regmap, WM8991_POWER_MANAGEMENT_1, + WM8991_VREF_ENA | WM8991_VMID_MODE_MASK, + WM8991_VREF_ENA | WM8991_VMID_MODE_MASK); + + regmap_update_bits(wm8991->regmap, WM8991_POWER_MANAGEMENT_2, + WM8991_OPCLK_ENA, WM8991_OPCLK_ENA); + + regmap_write(wm8991->regmap, WM8991_DAC_CTRL, 0); + regmap_write(wm8991->regmap, WM8991_LEFT_OUTPUT_VOLUME, + 0x50 | (1<<8)); + regmap_write(wm8991->regmap, WM8991_RIGHT_OUTPUT_VOLUME, + 0x50 | (1<<8)); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8991, &wm8991_dai, 1); + + return ret; +} + +static const struct i2c_device_id wm8991_i2c_id[] = { + { "wm8991", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id); + +static struct i2c_driver wm8991_i2c_driver = { + .driver = { + .name = "wm8991", + }, + .probe = wm8991_i2c_probe, + .id_table = wm8991_i2c_id, +}; + +module_i2c_driver(wm8991_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8991 driver"); +MODULE_AUTHOR("Graeme Gregory"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h new file mode 100644 index 0000000000..8686f01d55 --- /dev/null +++ b/sound/soc/codecs/wm8991.h @@ -0,0 +1,815 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * wm8991.h -- audio driver for WM8991 + * + * Copyright 2007 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + */ + +#ifndef _WM8991_H +#define _WM8991_H + +/* + * Register values. + */ +#define WM8991_RESET 0x00 +#define WM8991_POWER_MANAGEMENT_1 0x01 +#define WM8991_POWER_MANAGEMENT_2 0x02 +#define WM8991_POWER_MANAGEMENT_3 0x03 +#define WM8991_AUDIO_INTERFACE_1 0x04 +#define WM8991_AUDIO_INTERFACE_2 0x05 +#define WM8991_CLOCKING_1 0x06 +#define WM8991_CLOCKING_2 0x07 +#define WM8991_AUDIO_INTERFACE_3 0x08 +#define WM8991_AUDIO_INTERFACE_4 0x09 +#define WM8991_DAC_CTRL 0x0A +#define WM8991_LEFT_DAC_DIGITAL_VOLUME 0x0B +#define WM8991_RIGHT_DAC_DIGITAL_VOLUME 0x0C +#define WM8991_DIGITAL_SIDE_TONE 0x0D +#define WM8991_ADC_CTRL 0x0E +#define WM8991_LEFT_ADC_DIGITAL_VOLUME 0x0F +#define WM8991_RIGHT_ADC_DIGITAL_VOLUME 0x10 +#define WM8991_GPIO_CTRL_1 0x12 +#define WM8991_GPIO1_GPIO2 0x13 +#define WM8991_GPIO3_GPIO4 0x14 +#define WM8991_GPIO5_GPIO6 0x15 +#define WM8991_GPIOCTRL_2 0x16 +#define WM8991_GPIO_POL 0x17 +#define WM8991_LEFT_LINE_INPUT_1_2_VOLUME 0x18 +#define WM8991_LEFT_LINE_INPUT_3_4_VOLUME 0x19 +#define WM8991_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A +#define WM8991_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B +#define WM8991_LEFT_OUTPUT_VOLUME 0x1C +#define WM8991_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8991_LINE_OUTPUTS_VOLUME 0x1E +#define WM8991_OUT3_4_VOLUME 0x1F +#define WM8991_LEFT_OPGA_VOLUME 0x20 +#define WM8991_RIGHT_OPGA_VOLUME 0x21 +#define WM8991_SPEAKER_VOLUME 0x22 +#define WM8991_CLASSD1 0x23 +#define WM8991_CLASSD3 0x25 +#define WM8991_INPUT_MIXER1 0x27 +#define WM8991_INPUT_MIXER2 0x28 +#define WM8991_INPUT_MIXER3 0x29 +#define WM8991_INPUT_MIXER4 0x2A +#define WM8991_INPUT_MIXER5 0x2B +#define WM8991_INPUT_MIXER6 0x2C +#define WM8991_OUTPUT_MIXER1 0x2D +#define WM8991_OUTPUT_MIXER2 0x2E +#define WM8991_OUTPUT_MIXER3 0x2F +#define WM8991_OUTPUT_MIXER4 0x30 +#define WM8991_OUTPUT_MIXER5 0x31 +#define WM8991_OUTPUT_MIXER6 0x32 +#define WM8991_OUT3_4_MIXER 0x33 +#define WM8991_LINE_MIXER1 0x34 +#define WM8991_LINE_MIXER2 0x35 +#define WM8991_SPEAKER_MIXER 0x36 +#define WM8991_ADDITIONAL_CONTROL 0x37 +#define WM8991_ANTIPOP1 0x38 +#define WM8991_ANTIPOP2 0x39 +#define WM8991_MICBIAS 0x3A +#define WM8991_PLL1 0x3C +#define WM8991_PLL2 0x3D +#define WM8991_PLL3 0x3E + +#define WM8991_REGISTER_COUNT 60 +#define WM8991_MAX_REGISTER 0x3F + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Reset + */ +#define WM8991_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET_CHIP_ID - [15:0] */ + +/* + * R1 (0x01) - Power Management (1) + */ +#define WM8991_SPK_ENA 0x1000 /* SPK_ENA */ +#define WM8991_SPK_ENA_BIT 12 +#define WM8991_OUT3_ENA 0x0800 /* OUT3_ENA */ +#define WM8991_OUT3_ENA_BIT 11 +#define WM8991_OUT4_ENA 0x0400 /* OUT4_ENA */ +#define WM8991_OUT4_ENA_BIT 10 +#define WM8991_LOUT_ENA 0x0200 /* LOUT_ENA */ +#define WM8991_LOUT_ENA_BIT 9 +#define WM8991_ROUT_ENA 0x0100 /* ROUT_ENA */ +#define WM8991_ROUT_ENA_BIT 8 +#define WM8991_MICBIAS_ENA 0x0010 /* MICBIAS_ENA */ +#define WM8991_MICBIAS_ENA_BIT 4 +#define WM8991_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */ +#define WM8991_VREF_ENA 0x0001 /* VREF_ENA */ +#define WM8991_VREF_ENA_BIT 0 + +/* + * R2 (0x02) - Power Management (2) + */ +#define WM8991_PLL_ENA 0x8000 /* PLL_ENA */ +#define WM8991_PLL_ENA_BIT 15 +#define WM8991_TSHUT_ENA 0x4000 /* TSHUT_ENA */ +#define WM8991_TSHUT_ENA_BIT 14 +#define WM8991_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */ +#define WM8991_TSHUT_OPDIS_BIT 13 +#define WM8991_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8991_OPCLK_ENA_BIT 11 +#define WM8991_AINL_ENA 0x0200 /* AINL_ENA */ +#define WM8991_AINL_ENA_BIT 9 +#define WM8991_AINR_ENA 0x0100 /* AINR_ENA */ +#define WM8991_AINR_ENA_BIT 8 +#define WM8991_LIN34_ENA 0x0080 /* LIN34_ENA */ +#define WM8991_LIN34_ENA_BIT 7 +#define WM8991_LIN12_ENA 0x0040 /* LIN12_ENA */ +#define WM8991_LIN12_ENA_BIT 6 +#define WM8991_RIN34_ENA 0x0020 /* RIN34_ENA */ +#define WM8991_RIN34_ENA_BIT 5 +#define WM8991_RIN12_ENA 0x0010 /* RIN12_ENA */ +#define WM8991_RIN12_ENA_BIT 4 +#define WM8991_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8991_ADCL_ENA_BIT 1 +#define WM8991_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8991_ADCR_ENA_BIT 0 + +/* + * R3 (0x03) - Power Management (3) + */ +#define WM8991_LON_ENA 0x2000 /* LON_ENA */ +#define WM8991_LON_ENA_BIT 13 +#define WM8991_LOP_ENA 0x1000 /* LOP_ENA */ +#define WM8991_LOP_ENA_BIT 12 +#define WM8991_RON_ENA 0x0800 /* RON_ENA */ +#define WM8991_RON_ENA_BIT 11 +#define WM8991_ROP_ENA 0x0400 /* ROP_ENA */ +#define WM8991_ROP_ENA_BIT 10 +#define WM8991_LOPGA_ENA 0x0080 /* LOPGA_ENA */ +#define WM8991_LOPGA_ENA_BIT 7 +#define WM8991_ROPGA_ENA 0x0040 /* ROPGA_ENA */ +#define WM8991_ROPGA_ENA_BIT 6 +#define WM8991_LOMIX_ENA 0x0020 /* LOMIX_ENA */ +#define WM8991_LOMIX_ENA_BIT 5 +#define WM8991_ROMIX_ENA 0x0010 /* ROMIX_ENA */ +#define WM8991_ROMIX_ENA_BIT 4 +#define WM8991_DACL_ENA 0x0002 /* DACL_ENA */ +#define WM8991_DACL_ENA_BIT 1 +#define WM8991_DACR_ENA 0x0001 /* DACR_ENA */ +#define WM8991_DACR_ENA_BIT 0 + +/* + * R4 (0x04) - Audio Interface (1) + */ +#define WM8991_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */ +#define WM8991_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */ +#define WM8991_AIFADC_TDM 0x2000 /* AIFADC_TDM */ +#define WM8991_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8991_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */ +#define WM8991_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */ +#define WM8991_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */ +#define WM8991_AIF_WL_16BITS (0 << 5) +#define WM8991_AIF_WL_20BITS (1 << 5) +#define WM8991_AIF_WL_24BITS (2 << 5) +#define WM8991_AIF_WL_32BITS (3 << 5) +#define WM8991_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */ +#define WM8991_AIF_TMF_RIGHTJ (0 << 3) +#define WM8991_AIF_TMF_LEFTJ (1 << 3) +#define WM8991_AIF_TMF_I2S (2 << 3) +#define WM8991_AIF_TMF_DSP (3 << 3) + +/* + * R5 (0x05) - Audio Interface (2) + */ +#define WM8991_DACL_SRC 0x8000 /* DACL_SRC */ +#define WM8991_DACR_SRC 0x4000 /* DACR_SRC */ +#define WM8991_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ +#define WM8991_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8991_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */ +#define WM8991_DAC_COMP 0x0010 /* DAC_COMP */ +#define WM8991_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */ +#define WM8991_ADC_COMP 0x0004 /* ADC_COMP */ +#define WM8991_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */ +#define WM8991_LOOPBACK 0x0001 /* LOOPBACK */ + +/* + * R6 (0x06) - Clocking (1) + */ +#define WM8991_TOCLK_RATE 0x8000 /* TOCLK_RATE */ +#define WM8991_TOCLK_ENA 0x4000 /* TOCLK_ENA */ +#define WM8991_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */ +#define WM8991_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */ +#define WM8991_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */ +#define WM8991_BCLK_DIV_1 (0x0 << 1) +#define WM8991_BCLK_DIV_1_5 (0x1 << 1) +#define WM8991_BCLK_DIV_2 (0x2 << 1) +#define WM8991_BCLK_DIV_3 (0x3 << 1) +#define WM8991_BCLK_DIV_4 (0x4 << 1) +#define WM8991_BCLK_DIV_5_5 (0x5 << 1) +#define WM8991_BCLK_DIV_6 (0x6 << 1) +#define WM8991_BCLK_DIV_8 (0x7 << 1) +#define WM8991_BCLK_DIV_11 (0x8 << 1) +#define WM8991_BCLK_DIV_12 (0x9 << 1) +#define WM8991_BCLK_DIV_16 (0xA << 1) +#define WM8991_BCLK_DIV_22 (0xB << 1) +#define WM8991_BCLK_DIV_24 (0xC << 1) +#define WM8991_BCLK_DIV_32 (0xD << 1) +#define WM8991_BCLK_DIV_44 (0xE << 1) +#define WM8991_BCLK_DIV_48 (0xF << 1) + +/* + * R7 (0x07) - Clocking (2) + */ +#define WM8991_MCLK_SRC 0x8000 /* MCLK_SRC */ +#define WM8991_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */ +#define WM8991_CLK_FORCE 0x2000 /* CLK_FORCE */ +#define WM8991_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */ +#define WM8991_MCLK_DIV_1 (0 << 11) +#define WM8991_MCLK_DIV_2 ( 2 << 11) +#define WM8991_MCLK_INV 0x0400 /* MCLK_INV */ +#define WM8991_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV - [7:5] */ +#define WM8991_ADC_CLKDIV_1 (0 << 5) +#define WM8991_ADC_CLKDIV_1_5 (1 << 5) +#define WM8991_ADC_CLKDIV_2 (2 << 5) +#define WM8991_ADC_CLKDIV_3 (3 << 5) +#define WM8991_ADC_CLKDIV_4 (4 << 5) +#define WM8991_ADC_CLKDIV_5_5 (5 << 5) +#define WM8991_ADC_CLKDIV_6 (6 << 5) +#define WM8991_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */ +#define WM8991_DAC_CLKDIV_1 (0 << 2) +#define WM8991_DAC_CLKDIV_1_5 (1 << 2) +#define WM8991_DAC_CLKDIV_2 (2 << 2) +#define WM8991_DAC_CLKDIV_3 (3 << 2) +#define WM8991_DAC_CLKDIV_4 (4 << 2) +#define WM8991_DAC_CLKDIV_5_5 (5 << 2) +#define WM8991_DAC_CLKDIV_6 (6 << 2) + +/* + * R8 (0x08) - Audio Interface (3) + */ +#define WM8991_AIF_MSTR1 0x8000 /* AIF_MSTR1 */ +#define WM8991_AIF_MSTR2 0x4000 /* AIF_MSTR2 */ +#define WM8991_AIF_SEL 0x2000 /* AIF_SEL */ +#define WM8991_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */ +#define WM8991_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE - [10:0] */ + +/* + * R9 (0x09) - Audio Interface (4) + */ +#define WM8991_ALRCGPIO1 0x8000 /* ALRCGPIO1 */ +#define WM8991_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */ +#define WM8991_AIF_TRIS 0x2000 /* AIF_TRIS */ +#define WM8991_DACLRC_DIR 0x0800 /* DACLRC_DIR */ +#define WM8991_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE - [10:0] */ + +/* + * R10 (0x0A) - DAC CTRL + */ +#define WM8991_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */ +#define WM8991_DAC_MONO 0x0200 /* DAC_MONO */ +#define WM8991_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */ +#define WM8991_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */ +#define WM8991_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */ +#define WM8991_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */ +#define WM8991_DAC_MUTE 0x0004 /* DAC_MUTE */ +#define WM8991_DACL_DATINV 0x0002 /* DACL_DATINV */ +#define WM8991_DACR_DATINV 0x0001 /* DACR_DATINV */ + +/* + * R11 (0x0B) - Left DAC Digital Volume + */ +#define WM8991_DAC_VU 0x0100 /* DAC_VU */ +#define WM8991_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8991_DACL_VOL_SHIFT 0 +/* + * R12 (0x0C) - Right DAC Digital Volume + */ +#define WM8991_DAC_VU 0x0100 /* DAC_VU */ +#define WM8991_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8991_DACR_VOL_SHIFT 0 +/* + * R13 (0x0D) - Digital Side Tone + */ +#define WM8991_ADCL_DAC_SVOL_MASK 0x0F /* ADCL_DAC_SVOL - [12:9] */ +#define WM8991_ADCL_DAC_SVOL_SHIFT 9 +#define WM8991_ADCR_DAC_SVOL_MASK 0x0F /* ADCR_DAC_SVOL - [8:5] */ +#define WM8991_ADCR_DAC_SVOL_SHIFT 5 +#define WM8991_ADC_TO_DACL_MASK 0x03 /* ADC_TO_DACL - [3:2] */ +#define WM8991_ADC_TO_DACL_SHIFT 2 +#define WM8991_ADC_TO_DACR_MASK 0x03 /* ADC_TO_DACR - [1:0] */ +#define WM8991_ADC_TO_DACR_SHIFT 0 + +/* + * R14 (0x0E) - ADC CTRL + */ +#define WM8991_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */ +#define WM8991_ADC_HPF_ENA_BIT 8 +#define WM8991_ADC_HPF_CUT_MASK 0x03 /* ADC_HPF_CUT - [6:5] */ +#define WM8991_ADC_HPF_CUT_SHIFT 5 +#define WM8991_ADCL_DATINV 0x0002 /* ADCL_DATINV */ +#define WM8991_ADCL_DATINV_BIT 1 +#define WM8991_ADCR_DATINV 0x0001 /* ADCR_DATINV */ +#define WM8991_ADCR_DATINV_BIT 0 + +/* + * R15 (0x0F) - Left ADC Digital Volume + */ +#define WM8991_ADC_VU 0x0100 /* ADC_VU */ +#define WM8991_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8991_ADCL_VOL_SHIFT 0 + +/* + * R16 (0x10) - Right ADC Digital Volume + */ +#define WM8991_ADC_VU 0x0100 /* ADC_VU */ +#define WM8991_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8991_ADCR_VOL_SHIFT 0 + +/* + * R18 (0x12) - GPIO CTRL 1 + */ +#define WM8991_IRQ 0x1000 /* IRQ */ +#define WM8991_TEMPOK 0x0800 /* TEMPOK */ +#define WM8991_MICSHRT 0x0400 /* MICSHRT */ +#define WM8991_MICDET 0x0200 /* MICDET */ +#define WM8991_PLL_LCK 0x0100 /* PLL_LCK */ +#define WM8991_GPI8_STATUS 0x0080 /* GPI8_STATUS */ +#define WM8991_GPI7_STATUS 0x0040 /* GPI7_STATUS */ +#define WM8991_GPIO6_STATUS 0x0020 /* GPIO6_STATUS */ +#define WM8991_GPIO5_STATUS 0x0010 /* GPIO5_STATUS */ +#define WM8991_GPIO4_STATUS 0x0008 /* GPIO4_STATUS */ +#define WM8991_GPIO3_STATUS 0x0004 /* GPIO3_STATUS */ +#define WM8991_GPIO2_STATUS 0x0002 /* GPIO2_STATUS */ +#define WM8991_GPIO1_STATUS 0x0001 /* GPIO1_STATUS */ + +/* + * R19 (0x13) - GPIO1 & GPIO2 + */ +#define WM8991_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */ +#define WM8991_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */ +#define WM8991_GPIO2_PU 0x2000 /* GPIO2_PU */ +#define WM8991_GPIO2_PD 0x1000 /* GPIO2_PD */ +#define WM8991_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */ +#define WM8991_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */ +#define WM8991_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */ +#define WM8991_GPIO1_PU 0x0020 /* GPIO1_PU */ +#define WM8991_GPIO1_PD 0x0010 /* GPIO1_PD */ +#define WM8991_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */ + +/* + * R20 (0x14) - GPIO3 & GPIO4 + */ +#define WM8991_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */ +#define WM8991_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */ +#define WM8991_GPIO4_PU 0x2000 /* GPIO4_PU */ +#define WM8991_GPIO4_PD 0x1000 /* GPIO4_PD */ +#define WM8991_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */ +#define WM8991_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */ +#define WM8991_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */ +#define WM8991_GPIO3_PU 0x0020 /* GPIO3_PU */ +#define WM8991_GPIO3_PD 0x0010 /* GPIO3_PD */ +#define WM8991_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */ + +/* + * R21 (0x15) - GPIO5 & GPIO6 + */ +#define WM8991_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */ +#define WM8991_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */ +#define WM8991_GPIO6_PU 0x2000 /* GPIO6_PU */ +#define WM8991_GPIO6_PD 0x1000 /* GPIO6_PD */ +#define WM8991_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */ +#define WM8991_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */ +#define WM8991_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */ +#define WM8991_GPIO5_PU 0x0020 /* GPIO5_PU */ +#define WM8991_GPIO5_PD 0x0010 /* GPIO5_PD */ +#define WM8991_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */ + +/* + * R22 (0x16) - GPIOCTRL 2 + */ +#define WM8991_RD_3W_ENA 0x8000 /* RD_3W_ENA */ +#define WM8991_MODE_3W4W 0x4000 /* MODE_3W4W */ +#define WM8991_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */ +#define WM8991_MICSHRT_IRQ_ENA 0x0400 /* MICSHRT_IRQ_ENA */ +#define WM8991_MICDET_IRQ_ENA 0x0200 /* MICDET_IRQ_ENA */ +#define WM8991_PLL_LCK_IRQ_ENA 0x0100 /* PLL_LCK_IRQ_ENA */ +#define WM8991_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */ +#define WM8991_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */ +#define WM8991_GPI8_ENA 0x0010 /* GPI8_ENA */ +#define WM8991_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */ +#define WM8991_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */ +#define WM8991_GPI7_ENA 0x0001 /* GPI7_ENA */ + +/* + * R23 (0x17) - GPIO_POL + */ +#define WM8991_IRQ_INV 0x1000 /* IRQ_INV */ +#define WM8991_TEMPOK_POL 0x0800 /* TEMPOK_POL */ +#define WM8991_MICSHRT_POL 0x0400 /* MICSHRT_POL */ +#define WM8991_MICDET_POL 0x0200 /* MICDET_POL */ +#define WM8991_PLL_LCK_POL 0x0100 /* PLL_LCK_POL */ +#define WM8991_GPI8_POL 0x0080 /* GPI8_POL */ +#define WM8991_GPI7_POL 0x0040 /* GPI7_POL */ +#define WM8991_GPIO6_POL 0x0020 /* GPIO6_POL */ +#define WM8991_GPIO5_POL 0x0010 /* GPIO5_POL */ +#define WM8991_GPIO4_POL 0x0008 /* GPIO4_POL */ +#define WM8991_GPIO3_POL 0x0004 /* GPIO3_POL */ +#define WM8991_GPIO2_POL 0x0002 /* GPIO2_POL */ +#define WM8991_GPIO1_POL 0x0001 /* GPIO1_POL */ + +/* + * R24 (0x18) - Left Line Input 1&2 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_LI12MUTE 0x0080 /* LI12MUTE */ +#define WM8991_LI12MUTE_BIT 7 +#define WM8991_LI12ZC 0x0040 /* LI12ZC */ +#define WM8991_LI12ZC_BIT 6 +#define WM8991_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */ +#define WM8991_LIN12VOL_SHIFT 0 +/* + * R25 (0x19) - Left Line Input 3&4 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_LI34MUTE 0x0080 /* LI34MUTE */ +#define WM8991_LI34MUTE_BIT 7 +#define WM8991_LI34ZC 0x0040 /* LI34ZC */ +#define WM8991_LI34ZC_BIT 6 +#define WM8991_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */ +#define WM8991_LIN34VOL_SHIFT 0 + +/* + * R26 (0x1A) - Right Line Input 1&2 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_RI12MUTE 0x0080 /* RI12MUTE */ +#define WM8991_RI12MUTE_BIT 7 +#define WM8991_RI12ZC 0x0040 /* RI12ZC */ +#define WM8991_RI12ZC_BIT 6 +#define WM8991_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */ +#define WM8991_RIN12VOL_SHIFT 0 + +/* + * R27 (0x1B) - Right Line Input 3&4 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_RI34MUTE 0x0080 /* RI34MUTE */ +#define WM8991_RI34MUTE_BIT 7 +#define WM8991_RI34ZC 0x0040 /* RI34ZC */ +#define WM8991_RI34ZC_BIT 6 +#define WM8991_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */ +#define WM8991_RIN34VOL_SHIFT 0 + +/* + * R28 (0x1C) - Left Output Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_LOZC 0x0080 /* LOZC */ +#define WM8991_LOZC_BIT 7 +#define WM8991_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */ +#define WM8991_LOUTVOL_SHIFT 0 +/* + * R29 (0x1D) - Right Output Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_ROZC 0x0080 /* ROZC */ +#define WM8991_ROZC_BIT 7 +#define WM8991_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */ +#define WM8991_ROUTVOL_SHIFT 0 +/* + * R30 (0x1E) - Line Outputs Volume + */ +#define WM8991_LONMUTE 0x0040 /* LONMUTE */ +#define WM8991_LONMUTE_BIT 6 +#define WM8991_LOPMUTE 0x0020 /* LOPMUTE */ +#define WM8991_LOPMUTE_BIT 5 +#define WM8991_LOATTN 0x0010 /* LOATTN */ +#define WM8991_LOATTN_BIT 4 +#define WM8991_RONMUTE 0x0004 /* RONMUTE */ +#define WM8991_RONMUTE_BIT 2 +#define WM8991_ROPMUTE 0x0002 /* ROPMUTE */ +#define WM8991_ROPMUTE_BIT 1 +#define WM8991_ROATTN 0x0001 /* ROATTN */ +#define WM8991_ROATTN_BIT 0 + +/* + * R31 (0x1F) - Out3/4 Volume + */ +#define WM8991_OUT3MUTE 0x0020 /* OUT3MUTE */ +#define WM8991_OUT3MUTE_BIT 5 +#define WM8991_OUT3ATTN 0x0010 /* OUT3ATTN */ +#define WM8991_OUT3ATTN_BIT 4 +#define WM8991_OUT4MUTE 0x0002 /* OUT4MUTE */ +#define WM8991_OUT4MUTE_BIT 1 +#define WM8991_OUT4ATTN 0x0001 /* OUT4ATTN */ +#define WM8991_OUT4ATTN_BIT 0 + +/* + * R32 (0x20) - Left OPGA Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_LOPGAZC 0x0080 /* LOPGAZC */ +#define WM8991_LOPGAZC_BIT 7 +#define WM8991_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */ +#define WM8991_LOPGAVOL_SHIFT 0 + +/* + * R33 (0x21) - Right OPGA Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_ROPGAZC 0x0080 /* ROPGAZC */ +#define WM8991_ROPGAZC_BIT 7 +#define WM8991_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */ +#define WM8991_ROPGAVOL_SHIFT 0 +/* + * R34 (0x22) - Speaker Volume + */ +#define WM8991_SPKVOL_MASK 0x0003 /* SPKVOL - [1:0] */ +#define WM8991_SPKVOL_SHIFT 0 + +/* + * R35 (0x23) - ClassD1 + */ +#define WM8991_CDMODE 0x0100 /* CDMODE */ +#define WM8991_CDMODE_BIT 8 + +/* + * R37 (0x25) - ClassD3 + */ +#define WM8991_DCGAIN_MASK 0x0007 /* DCGAIN - [5:3] */ +#define WM8991_DCGAIN_SHIFT 3 +#define WM8991_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */ +#define WM8991_ACGAIN_SHIFT 0 +/* + * R39 (0x27) - Input Mixer1 + */ +#define WM8991_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */ +#define WM8991_AINLMODE_SHIFT 2 +#define WM8991_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */ +#define WM8991_AINRMODE_SHIFT 0 + +/* + * R40 (0x28) - Input Mixer2 + */ +#define WM8991_LMP4 0x0080 /* LMP4 */ +#define WM8991_LMP4_BIT 7 /* LMP4 */ +#define WM8991_LMN3 0x0040 /* LMN3 */ +#define WM8991_LMN3_BIT 6 /* LMN3 */ +#define WM8991_LMP2 0x0020 /* LMP2 */ +#define WM8991_LMP2_BIT 5 /* LMP2 */ +#define WM8991_LMN1 0x0010 /* LMN1 */ +#define WM8991_LMN1_BIT 4 /* LMN1 */ +#define WM8991_RMP4 0x0008 /* RMP4 */ +#define WM8991_RMP4_BIT 3 /* RMP4 */ +#define WM8991_RMN3 0x0004 /* RMN3 */ +#define WM8991_RMN3_BIT 2 /* RMN3 */ +#define WM8991_RMP2 0x0002 /* RMP2 */ +#define WM8991_RMP2_BIT 1 /* RMP2 */ +#define WM8991_RMN1 0x0001 /* RMN1 */ +#define WM8991_RMN1_BIT 0 /* RMN1 */ + +/* + * R41 (0x29) - Input Mixer3 + */ +#define WM8991_L34MNB 0x0100 /* L34MNB */ +#define WM8991_L34MNB_BIT 8 +#define WM8991_L34MNBST 0x0080 /* L34MNBST */ +#define WM8991_L34MNBST_BIT 7 +#define WM8991_L12MNB 0x0020 /* L12MNB */ +#define WM8991_L12MNB_BIT 5 +#define WM8991_L12MNBST 0x0010 /* L12MNBST */ +#define WM8991_L12MNBST_BIT 4 +#define WM8991_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */ +#define WM8991_LDBVOL_SHIFT 0 + +/* + * R42 (0x2A) - Input Mixer4 + */ +#define WM8991_R34MNB 0x0100 /* R34MNB */ +#define WM8991_R34MNB_BIT 8 +#define WM8991_R34MNBST 0x0080 /* R34MNBST */ +#define WM8991_R34MNBST_BIT 7 +#define WM8991_R12MNB 0x0020 /* R12MNB */ +#define WM8991_R12MNB_BIT 5 +#define WM8991_R12MNBST 0x0010 /* R12MNBST */ +#define WM8991_R12MNBST_BIT 4 +#define WM8991_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */ +#define WM8991_RDBVOL_SHIFT 0 + +/* + * R43 (0x2B) - Input Mixer5 + */ +#define WM8991_LI2BVOL_MASK 0x07 /* LI2BVOL - [8:6] */ +#define WM8991_LI2BVOL_SHIFT 6 +#define WM8991_LR4BVOL_MASK 0x07 /* LR4BVOL - [5:3] */ +#define WM8991_LR4BVOL_SHIFT 3 +#define WM8991_LL4BVOL_MASK 0x07 /* LL4BVOL - [2:0] */ +#define WM8991_LL4BVOL_SHIFT 0 + +/* + * R44 (0x2C) - Input Mixer6 + */ +#define WM8991_RI2BVOL_MASK 0x07 /* RI2BVOL - [8:6] */ +#define WM8991_RI2BVOL_SHIFT 6 +#define WM8991_RL4BVOL_MASK 0x07 /* RL4BVOL - [5:3] */ +#define WM8991_RL4BVOL_SHIFT 3 +#define WM8991_RR4BVOL_MASK 0x07 /* RR4BVOL - [2:0] */ +#define WM8991_RR4BVOL_SHIFT 0 + +/* + * R45 (0x2D) - Output Mixer1 + */ +#define WM8991_LRBLO 0x0080 /* LRBLO */ +#define WM8991_LRBLO_BIT 7 +#define WM8991_LLBLO 0x0040 /* LLBLO */ +#define WM8991_LLBLO_BIT 6 +#define WM8991_LRI3LO 0x0020 /* LRI3LO */ +#define WM8991_LRI3LO_BIT 5 +#define WM8991_LLI3LO 0x0010 /* LLI3LO */ +#define WM8991_LLI3LO_BIT 4 +#define WM8991_LR12LO 0x0008 /* LR12LO */ +#define WM8991_LR12LO_BIT 3 +#define WM8991_LL12LO 0x0004 /* LL12LO */ +#define WM8991_LL12LO_BIT 2 +#define WM8991_LDLO 0x0001 /* LDLO */ +#define WM8991_LDLO_BIT 0 + +/* + * R46 (0x2E) - Output Mixer2 + */ +#define WM8991_RLBRO 0x0080 /* RLBRO */ +#define WM8991_RLBRO_BIT 7 +#define WM8991_RRBRO 0x0040 /* RRBRO */ +#define WM8991_RRBRO_BIT 6 +#define WM8991_RLI3RO 0x0020 /* RLI3RO */ +#define WM8991_RLI3RO_BIT 5 +#define WM8991_RRI3RO 0x0010 /* RRI3RO */ +#define WM8991_RRI3RO_BIT 4 +#define WM8991_RL12RO 0x0008 /* RL12RO */ +#define WM8991_RL12RO_BIT 3 +#define WM8991_RR12RO 0x0004 /* RR12RO */ +#define WM8991_RR12RO_BIT 2 +#define WM8991_RDRO 0x0001 /* RDRO */ +#define WM8991_RDRO_BIT 0 + +/* + * R47 (0x2F) - Output Mixer3 + */ +#define WM8991_LLI3LOVOL_MASK 0x07 /* LLI3LOVOL - [8:6] */ +#define WM8991_LLI3LOVOL_SHIFT 6 +#define WM8991_LR12LOVOL_MASK 0x07 /* LR12LOVOL - [5:3] */ +#define WM8991_LR12LOVOL_SHIFT 3 +#define WM8991_LL12LOVOL_MASK 0x07 /* LL12LOVOL - [2:0] */ +#define WM8991_LL12LOVOL_SHIFT 0 + +/* + * R48 (0x30) - Output Mixer4 + */ +#define WM8991_RRI3ROVOL_MASK 0x07 /* RRI3ROVOL - [8:6] */ +#define WM8991_RRI3ROVOL_SHIFT 6 +#define WM8991_RL12ROVOL_MASK 0x07 /* RL12ROVOL - [5:3] */ +#define WM8991_RL12ROVOL_SHIFT 3 +#define WM8991_RR12ROVOL_MASK 0x07 /* RR12ROVOL - [2:0] */ +#define WM8991_RR12ROVOL_SHIFT 0 + +/* + * R49 (0x31) - Output Mixer5 + */ +#define WM8991_LRI3LOVOL_MASK 0x07 /* LRI3LOVOL - [8:6] */ +#define WM8991_LRI3LOVOL_SHIFT 6 +#define WM8991_LRBLOVOL_MASK 0x07 /* LRBLOVOL - [5:3] */ +#define WM8991_LRBLOVOL_SHIFT 3 +#define WM8991_LLBLOVOL_MASK 0x07 /* LLBLOVOL - [2:0] */ +#define WM8991_LLBLOVOL_SHIFT 0 + +/* + * R50 (0x32) - Output Mixer6 + */ +#define WM8991_RLI3ROVOL_MASK 0x07 /* RLI3ROVOL - [8:6] */ +#define WM8991_RLI3ROVOL_SHIFT 6 +#define WM8991_RLBROVOL_MASK 0x07 /* RLBROVOL - [5:3] */ +#define WM8991_RLBROVOL_SHIFT 3 +#define WM8991_RRBROVOL_MASK 0x07 /* RRBROVOL - [2:0] */ +#define WM8991_RRBROVOL_SHIFT 0 + +/* + * R51 (0x33) - Out3/4 Mixer + */ +#define WM8991_VSEL_MASK 0x0180 /* VSEL - [8:7] */ +#define WM8991_LI4O3 0x0020 /* LI4O3 */ +#define WM8991_LI4O3_BIT 5 +#define WM8991_LPGAO3 0x0010 /* LPGAO3 */ +#define WM8991_LPGAO3_BIT 4 +#define WM8991_RI4O4 0x0002 /* RI4O4 */ +#define WM8991_RI4O4_BIT 1 +#define WM8991_RPGAO4 0x0001 /* RPGAO4 */ +#define WM8991_RPGAO4_BIT 0 +/* + * R52 (0x34) - Line Mixer1 + */ +#define WM8991_LLOPGALON 0x0040 /* LLOPGALON */ +#define WM8991_LLOPGALON_BIT 6 +#define WM8991_LROPGALON 0x0020 /* LROPGALON */ +#define WM8991_LROPGALON_BIT 5 +#define WM8991_LOPLON 0x0010 /* LOPLON */ +#define WM8991_LOPLON_BIT 4 +#define WM8991_LR12LOP 0x0004 /* LR12LOP */ +#define WM8991_LR12LOP_BIT 2 +#define WM8991_LL12LOP 0x0002 /* LL12LOP */ +#define WM8991_LL12LOP_BIT 1 +#define WM8991_LLOPGALOP 0x0001 /* LLOPGALOP */ +#define WM8991_LLOPGALOP_BIT 0 +/* + * R53 (0x35) - Line Mixer2 + */ +#define WM8991_RROPGARON 0x0040 /* RROPGARON */ +#define WM8991_RROPGARON_BIT 6 +#define WM8991_RLOPGARON 0x0020 /* RLOPGARON */ +#define WM8991_RLOPGARON_BIT 5 +#define WM8991_ROPRON 0x0010 /* ROPRON */ +#define WM8991_ROPRON_BIT 4 +#define WM8991_RL12ROP 0x0004 /* RL12ROP */ +#define WM8991_RL12ROP_BIT 2 +#define WM8991_RR12ROP 0x0002 /* RR12ROP */ +#define WM8991_RR12ROP_BIT 1 +#define WM8991_RROPGAROP 0x0001 /* RROPGAROP */ +#define WM8991_RROPGAROP_BIT 0 + +/* + * R54 (0x36) - Speaker Mixer + */ +#define WM8991_LB2SPK 0x0080 /* LB2SPK */ +#define WM8991_LB2SPK_BIT 7 +#define WM8991_RB2SPK 0x0040 /* RB2SPK */ +#define WM8991_RB2SPK_BIT 6 +#define WM8991_LI2SPK 0x0020 /* LI2SPK */ +#define WM8991_LI2SPK_BIT 5 +#define WM8991_RI2SPK 0x0010 /* RI2SPK */ +#define WM8991_RI2SPK_BIT 4 +#define WM8991_LOPGASPK 0x0008 /* LOPGASPK */ +#define WM8991_LOPGASPK_BIT 3 +#define WM8991_ROPGASPK 0x0004 /* ROPGASPK */ +#define WM8991_ROPGASPK_BIT 2 +#define WM8991_LDSPK 0x0002 /* LDSPK */ +#define WM8991_LDSPK_BIT 1 +#define WM8991_RDSPK 0x0001 /* RDSPK */ +#define WM8991_RDSPK_BIT 0 + +/* + * R55 (0x37) - Additional Control + */ +#define WM8991_VROI 0x0001 /* VROI */ + +/* + * R56 (0x38) - AntiPOP1 + */ +#define WM8991_DIS_LLINE 0x0020 /* DIS_LLINE */ +#define WM8991_DIS_RLINE 0x0010 /* DIS_RLINE */ +#define WM8991_DIS_OUT3 0x0008 /* DIS_OUT3 */ +#define WM8991_DIS_OUT4 0x0004 /* DIS_OUT4 */ +#define WM8991_DIS_LOUT 0x0002 /* DIS_LOUT */ +#define WM8991_DIS_ROUT 0x0001 /* DIS_ROUT */ + +/* + * R57 (0x39) - AntiPOP2 + */ +#define WM8991_SOFTST 0x0040 /* SOFTST */ +#define WM8991_BUFIOEN 0x0008 /* BUFIOEN */ +#define WM8991_BUFDCOPEN 0x0004 /* BUFDCOPEN */ +#define WM8991_POBCTRL 0x0002 /* POBCTRL */ +#define WM8991_VMIDTOG 0x0001 /* VMIDTOG */ + +/* + * R58 (0x3A) - MICBIAS + */ +#define WM8991_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */ +#define WM8991_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */ +#define WM8991_MCD 0x0004 /* MCD */ +#define WM8991_MBSEL 0x0001 /* MBSEL */ + +/* + * R60 (0x3C) - PLL1 + */ +#define WM8991_SDM 0x0080 /* SDM */ +#define WM8991_PRESCALE 0x0040 /* PRESCALE */ +#define WM8991_PLLN_MASK 0x000F /* PLLN - [3:0] */ + +/* + * R61 (0x3D) - PLL2 + */ +#define WM8991_PLLK1_MASK 0x00FF /* PLLK1 - [7:0] */ + +/* + * R62 (0x3E) - PLL3 + */ +#define WM8991_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */ + +#define WM8991_MCLK_DIV 0 +#define WM8991_DACCLK_DIV 1 +#define WM8991_ADCCLK_DIV 2 +#define WM8991_BCLK_DIV 3 + +#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\ + tlv_array) \ + SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \ + snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array) + +#endif /* _WM8991_H */ diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c new file mode 100644 index 0000000000..5b788f35e5 --- /dev/null +++ b/sound/soc/codecs/wm8993.c @@ -0,0 +1,1753 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8993.c -- WM8993 ALSA SoC audio driver + * + * Copyright 2009-12 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8993.h" +#include "wm_hubs.h" + +#define WM8993_NUM_SUPPLIES 6 +static const char *wm8993_supply_names[WM8993_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "AVDD1", + "AVDD2", + "CPVDD", + "SPKVDD", +}; + +static const struct reg_default wm8993_reg_defaults[] = { + { 1, 0x0000 }, /* R1 - Power Management (1) */ + { 2, 0x6000 }, /* R2 - Power Management (2) */ + { 3, 0x0000 }, /* R3 - Power Management (3) */ + { 4, 0x4050 }, /* R4 - Audio Interface (1) */ + { 5, 0x4000 }, /* R5 - Audio Interface (2) */ + { 6, 0x01C8 }, /* R6 - Clocking 1 */ + { 7, 0x0000 }, /* R7 - Clocking 2 */ + { 8, 0x0000 }, /* R8 - Audio Interface (3) */ + { 9, 0x0040 }, /* R9 - Audio Interface (4) */ + { 10, 0x0004 }, /* R10 - DAC CTRL */ + { 11, 0x00C0 }, /* R11 - Left DAC Digital Volume */ + { 12, 0x00C0 }, /* R12 - Right DAC Digital Volume */ + { 13, 0x0000 }, /* R13 - Digital Side Tone */ + { 14, 0x0300 }, /* R14 - ADC CTRL */ + { 15, 0x00C0 }, /* R15 - Left ADC Digital Volume */ + { 16, 0x00C0 }, /* R16 - Right ADC Digital Volume */ + { 18, 0x0000 }, /* R18 - GPIO CTRL 1 */ + { 19, 0x0010 }, /* R19 - GPIO1 */ + { 20, 0x0000 }, /* R20 - IRQ_DEBOUNCE */ + { 21, 0x0000 }, /* R21 - Inputs Clamp */ + { 22, 0x8000 }, /* R22 - GPIOCTRL 2 */ + { 23, 0x0800 }, /* R23 - GPIO_POL */ + { 24, 0x008B }, /* R24 - Left Line Input 1&2 Volume */ + { 25, 0x008B }, /* R25 - Left Line Input 3&4 Volume */ + { 26, 0x008B }, /* R26 - Right Line Input 1&2 Volume */ + { 27, 0x008B }, /* R27 - Right Line Input 3&4 Volume */ + { 28, 0x006D }, /* R28 - Left Output Volume */ + { 29, 0x006D }, /* R29 - Right Output Volume */ + { 30, 0x0066 }, /* R30 - Line Outputs Volume */ + { 31, 0x0020 }, /* R31 - HPOUT2 Volume */ + { 32, 0x0079 }, /* R32 - Left OPGA Volume */ + { 33, 0x0079 }, /* R33 - Right OPGA Volume */ + { 34, 0x0003 }, /* R34 - SPKMIXL Attenuation */ + { 35, 0x0003 }, /* R35 - SPKMIXR Attenuation */ + { 36, 0x0011 }, /* R36 - SPKOUT Mixers */ + { 37, 0x0100 }, /* R37 - SPKOUT Boost */ + { 38, 0x0079 }, /* R38 - Speaker Volume Left */ + { 39, 0x0079 }, /* R39 - Speaker Volume Right */ + { 40, 0x0000 }, /* R40 - Input Mixer2 */ + { 41, 0x0000 }, /* R41 - Input Mixer3 */ + { 42, 0x0000 }, /* R42 - Input Mixer4 */ + { 43, 0x0000 }, /* R43 - Input Mixer5 */ + { 44, 0x0000 }, /* R44 - Input Mixer6 */ + { 45, 0x0000 }, /* R45 - Output Mixer1 */ + { 46, 0x0000 }, /* R46 - Output Mixer2 */ + { 47, 0x0000 }, /* R47 - Output Mixer3 */ + { 48, 0x0000 }, /* R48 - Output Mixer4 */ + { 49, 0x0000 }, /* R49 - Output Mixer5 */ + { 50, 0x0000 }, /* R50 - Output Mixer6 */ + { 51, 0x0000 }, /* R51 - HPOUT2 Mixer */ + { 52, 0x0000 }, /* R52 - Line Mixer1 */ + { 53, 0x0000 }, /* R53 - Line Mixer2 */ + { 54, 0x0000 }, /* R54 - Speaker Mixer */ + { 55, 0x0000 }, /* R55 - Additional Control */ + { 56, 0x0000 }, /* R56 - AntiPOP1 */ + { 57, 0x0000 }, /* R57 - AntiPOP2 */ + { 58, 0x0000 }, /* R58 - MICBIAS */ + { 60, 0x0000 }, /* R60 - FLL Control 1 */ + { 61, 0x0000 }, /* R61 - FLL Control 2 */ + { 62, 0x0000 }, /* R62 - FLL Control 3 */ + { 63, 0x2EE0 }, /* R63 - FLL Control 4 */ + { 64, 0x0002 }, /* R64 - FLL Control 5 */ + { 65, 0x2287 }, /* R65 - Clocking 3 */ + { 66, 0x025F }, /* R66 - Clocking 4 */ + { 67, 0x0000 }, /* R67 - MW Slave Control */ + { 69, 0x0002 }, /* R69 - Bus Control 1 */ + { 70, 0x0000 }, /* R70 - Write Sequencer 0 */ + { 71, 0x0000 }, /* R71 - Write Sequencer 1 */ + { 72, 0x0000 }, /* R72 - Write Sequencer 2 */ + { 73, 0x0000 }, /* R73 - Write Sequencer 3 */ + { 74, 0x0000 }, /* R74 - Write Sequencer 4 */ + { 75, 0x0000 }, /* R75 - Write Sequencer 5 */ + { 76, 0x1F25 }, /* R76 - Charge Pump 1 */ + { 81, 0x0000 }, /* R81 - Class W 0 */ + { 85, 0x054A }, /* R85 - DC Servo 1 */ + { 87, 0x0000 }, /* R87 - DC Servo 3 */ + { 96, 0x0100 }, /* R96 - Analogue HP 0 */ + { 98, 0x0000 }, /* R98 - EQ1 */ + { 99, 0x000C }, /* R99 - EQ2 */ + { 100, 0x000C }, /* R100 - EQ3 */ + { 101, 0x000C }, /* R101 - EQ4 */ + { 102, 0x000C }, /* R102 - EQ5 */ + { 103, 0x000C }, /* R103 - EQ6 */ + { 104, 0x0FCA }, /* R104 - EQ7 */ + { 105, 0x0400 }, /* R105 - EQ8 */ + { 106, 0x00D8 }, /* R106 - EQ9 */ + { 107, 0x1EB5 }, /* R107 - EQ10 */ + { 108, 0xF145 }, /* R108 - EQ11 */ + { 109, 0x0B75 }, /* R109 - EQ12 */ + { 110, 0x01C5 }, /* R110 - EQ13 */ + { 111, 0x1C58 }, /* R111 - EQ14 */ + { 112, 0xF373 }, /* R112 - EQ15 */ + { 113, 0x0A54 }, /* R113 - EQ16 */ + { 114, 0x0558 }, /* R114 - EQ17 */ + { 115, 0x168E }, /* R115 - EQ18 */ + { 116, 0xF829 }, /* R116 - EQ19 */ + { 117, 0x07AD }, /* R117 - EQ20 */ + { 118, 0x1103 }, /* R118 - EQ21 */ + { 119, 0x0564 }, /* R119 - EQ22 */ + { 120, 0x0559 }, /* R120 - EQ23 */ + { 121, 0x4000 }, /* R121 - EQ24 */ + { 122, 0x0000 }, /* R122 - Digital Pulls */ + { 123, 0x0F08 }, /* R123 - DRC Control 1 */ + { 124, 0x0000 }, /* R124 - DRC Control 2 */ + { 125, 0x0080 }, /* R125 - DRC Control 3 */ + { 126, 0x0000 }, /* R126 - DRC Control 4 */ +}; + +static struct { + int ratio; + int clk_sys_rate; +} clk_sys_rates[] = { + { 64, 0 }, + { 128, 1 }, + { 192, 2 }, + { 256, 3 }, + { 384, 4 }, + { 512, 5 }, + { 768, 6 }, + { 1024, 7 }, + { 1408, 8 }, + { 1536, 9 }, +}; + +static struct { + int rate; + int sample_rate; +} sample_rates[] = { + { 8000, 0 }, + { 11025, 1 }, + { 12000, 1 }, + { 16000, 2 }, + { 22050, 3 }, + { 24000, 3 }, + { 32000, 4 }, + { 44100, 5 }, + { 48000, 5 }, +}; + +static struct { + int div; /* *10 due to .5s */ + int bclk_div; +} bclk_divs[] = { + { 10, 0 }, + { 15, 1 }, + { 20, 2 }, + { 30, 3 }, + { 40, 4 }, + { 55, 5 }, + { 60, 6 }, + { 80, 7 }, + { 110, 8 }, + { 120, 9 }, + { 160, 10 }, + { 220, 11 }, + { 240, 12 }, + { 320, 13 }, + { 440, 14 }, + { 480, 15 }, +}; + +struct wm8993_priv { + struct wm_hubs_data hubs_data; + struct device *dev; + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8993_NUM_SUPPLIES]; + struct wm8993_platform_data pdata; + struct completion fll_lock; + int master; + int sysclk_source; + int tdm_slots; + int tdm_width; + unsigned int mclk_rate; + unsigned int sysclk_rate; + unsigned int fs; + unsigned int bclk; + unsigned int fll_fref; + unsigned int fll_fout; + int fll_src; +}; + +static bool wm8993_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8993_SOFTWARE_RESET: + case WM8993_GPIO_CTRL_1: + case WM8993_DC_SERVO_0: + case WM8993_DC_SERVO_READBACK_0: + case WM8993_DC_SERVO_READBACK_1: + case WM8993_DC_SERVO_READBACK_2: + return true; + default: + return false; + } +} + +static bool wm8993_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8993_SOFTWARE_RESET: + case WM8993_POWER_MANAGEMENT_1: + case WM8993_POWER_MANAGEMENT_2: + case WM8993_POWER_MANAGEMENT_3: + case WM8993_AUDIO_INTERFACE_1: + case WM8993_AUDIO_INTERFACE_2: + case WM8993_CLOCKING_1: + case WM8993_CLOCKING_2: + case WM8993_AUDIO_INTERFACE_3: + case WM8993_AUDIO_INTERFACE_4: + case WM8993_DAC_CTRL: + case WM8993_LEFT_DAC_DIGITAL_VOLUME: + case WM8993_RIGHT_DAC_DIGITAL_VOLUME: + case WM8993_DIGITAL_SIDE_TONE: + case WM8993_ADC_CTRL: + case WM8993_LEFT_ADC_DIGITAL_VOLUME: + case WM8993_RIGHT_ADC_DIGITAL_VOLUME: + case WM8993_GPIO_CTRL_1: + case WM8993_GPIO1: + case WM8993_IRQ_DEBOUNCE: + case WM8993_GPIOCTRL_2: + case WM8993_GPIO_POL: + case WM8993_LEFT_LINE_INPUT_1_2_VOLUME: + case WM8993_LEFT_LINE_INPUT_3_4_VOLUME: + case WM8993_RIGHT_LINE_INPUT_1_2_VOLUME: + case WM8993_RIGHT_LINE_INPUT_3_4_VOLUME: + case WM8993_LEFT_OUTPUT_VOLUME: + case WM8993_RIGHT_OUTPUT_VOLUME: + case WM8993_LINE_OUTPUTS_VOLUME: + case WM8993_HPOUT2_VOLUME: + case WM8993_LEFT_OPGA_VOLUME: + case WM8993_RIGHT_OPGA_VOLUME: + case WM8993_SPKMIXL_ATTENUATION: + case WM8993_SPKMIXR_ATTENUATION: + case WM8993_SPKOUT_MIXERS: + case WM8993_SPKOUT_BOOST: + case WM8993_SPEAKER_VOLUME_LEFT: + case WM8993_SPEAKER_VOLUME_RIGHT: + case WM8993_INPUT_MIXER2: + case WM8993_INPUT_MIXER3: + case WM8993_INPUT_MIXER4: + case WM8993_INPUT_MIXER5: + case WM8993_INPUT_MIXER6: + case WM8993_OUTPUT_MIXER1: + case WM8993_OUTPUT_MIXER2: + case WM8993_OUTPUT_MIXER3: + case WM8993_OUTPUT_MIXER4: + case WM8993_OUTPUT_MIXER5: + case WM8993_OUTPUT_MIXER6: + case WM8993_HPOUT2_MIXER: + case WM8993_LINE_MIXER1: + case WM8993_LINE_MIXER2: + case WM8993_SPEAKER_MIXER: + case WM8993_ADDITIONAL_CONTROL: + case WM8993_ANTIPOP1: + case WM8993_ANTIPOP2: + case WM8993_MICBIAS: + case WM8993_FLL_CONTROL_1: + case WM8993_FLL_CONTROL_2: + case WM8993_FLL_CONTROL_3: + case WM8993_FLL_CONTROL_4: + case WM8993_FLL_CONTROL_5: + case WM8993_CLOCKING_3: + case WM8993_CLOCKING_4: + case WM8993_MW_SLAVE_CONTROL: + case WM8993_BUS_CONTROL_1: + case WM8993_WRITE_SEQUENCER_0: + case WM8993_WRITE_SEQUENCER_1: + case WM8993_WRITE_SEQUENCER_2: + case WM8993_WRITE_SEQUENCER_3: + case WM8993_WRITE_SEQUENCER_4: + case WM8993_WRITE_SEQUENCER_5: + case WM8993_CHARGE_PUMP_1: + case WM8993_CLASS_W_0: + case WM8993_DC_SERVO_0: + case WM8993_DC_SERVO_1: + case WM8993_DC_SERVO_3: + case WM8993_DC_SERVO_READBACK_0: + case WM8993_DC_SERVO_READBACK_1: + case WM8993_DC_SERVO_READBACK_2: + case WM8993_ANALOGUE_HP_0: + case WM8993_EQ1: + case WM8993_EQ2: + case WM8993_EQ3: + case WM8993_EQ4: + case WM8993_EQ5: + case WM8993_EQ6: + case WM8993_EQ7: + case WM8993_EQ8: + case WM8993_EQ9: + case WM8993_EQ10: + case WM8993_EQ11: + case WM8993_EQ12: + case WM8993_EQ13: + case WM8993_EQ14: + case WM8993_EQ15: + case WM8993_EQ16: + case WM8993_EQ17: + case WM8993_EQ18: + case WM8993_EQ19: + case WM8993_EQ20: + case WM8993_EQ21: + case WM8993_EQ22: + case WM8993_EQ23: + case WM8993_EQ24: + case WM8993_DIGITAL_PULLS: + case WM8993_DRC_CONTROL_1: + case WM8993_DRC_CONTROL_2: + case WM8993_DRC_CONTROL_3: + case WM8993_DRC_CONTROL_4: + return true; + default: + return false; + } +} + +struct _fll_div { + u16 fll_fratio; + u16 fll_outdiv; + u16 fll_clk_ref_div; + u16 n; + u16 k; +}; + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +static struct { + unsigned int min; + unsigned int max; + u16 fll_fratio; + int ratio; +} fll_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, + unsigned int Fout) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, target; + unsigned int div; + int i; + + /* Fref must be <=13.5MHz */ + div = 1; + fll_div->fll_clk_ref_div = 0; + while ((Fref / div) > 13500000) { + div *= 2; + fll_div->fll_clk_ref_div++; + + if (div > 8) { + pr_err("Can't scale %dMHz input down to <=13.5MHz\n", + Fref); + return -EINVAL; + } + } + + pr_debug("Fref=%u Fout=%u\n", Fref, Fout); + + /* Apply the division for our remaining calculations */ + Fref /= div; + + /* Fvco should be 90-100MHz; don't check the upper bound */ + div = 0; + target = Fout * 2; + while (target < 90000000) { + div++; + target *= 2; + if (div > 7) { + pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + } + fll_div->fll_outdiv = div; + + pr_debug("Fvco=%dHz\n", target); + + /* Find an appropriate FLL_FRATIO and factor it out of the target */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + fll_div->fll_fratio = fll_fratios[i].fll_fratio; + target /= fll_fratios[i].ratio; + break; + } + } + if (i == ARRAY_SIZE(fll_fratios)) { + pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref); + return -EINVAL; + } + + /* Now, calculate N.K */ + Ndiv = target / Fref; + + fll_div->n = Ndiv; + Nmod = target % Fref; + pr_debug("Nmod=%d\n", Nmod); + + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, Fref); + + K = Kpart & 0xFFFFFFFF; + + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + fll_div->k = K / 10; + + pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n", + fll_div->n, fll_div->k, + fll_div->fll_fratio, fll_div->fll_outdiv, + fll_div->fll_clk_ref_div); + + return 0; +} + +static int _wm8993_set_fll(struct snd_soc_component *component, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component); + struct i2c_client *i2c = to_i2c_client(component->dev); + u16 reg1, reg4, reg5; + struct _fll_div fll_div; + unsigned int timeout; + int ret; + + /* Any change? */ + if (Fref == wm8993->fll_fref && Fout == wm8993->fll_fout) + return 0; + + /* Disable the FLL */ + if (Fout == 0) { + dev_dbg(component->dev, "FLL disabled\n"); + wm8993->fll_fref = 0; + wm8993->fll_fout = 0; + + reg1 = snd_soc_component_read(component, WM8993_FLL_CONTROL_1); + reg1 &= ~WM8993_FLL_ENA; + snd_soc_component_write(component, WM8993_FLL_CONTROL_1, reg1); + + return 0; + } + + ret = fll_factors(&fll_div, Fref, Fout); + if (ret != 0) + return ret; + + reg5 = snd_soc_component_read(component, WM8993_FLL_CONTROL_5); + reg5 &= ~WM8993_FLL_CLK_SRC_MASK; + + switch (fll_id) { + case WM8993_FLL_MCLK: + break; + + case WM8993_FLL_LRCLK: + reg5 |= 1; + break; + + case WM8993_FLL_BCLK: + reg5 |= 2; + break; + + default: + dev_err(component->dev, "Unknown FLL ID %d\n", fll_id); + return -EINVAL; + } + + /* Any FLL configuration change requires that the FLL be + * disabled first. */ + reg1 = snd_soc_component_read(component, WM8993_FLL_CONTROL_1); + reg1 &= ~WM8993_FLL_ENA; + snd_soc_component_write(component, WM8993_FLL_CONTROL_1, reg1); + + /* Apply the configuration */ + if (fll_div.k) + reg1 |= WM8993_FLL_FRAC_MASK; + else + reg1 &= ~WM8993_FLL_FRAC_MASK; + snd_soc_component_write(component, WM8993_FLL_CONTROL_1, reg1); + + snd_soc_component_write(component, WM8993_FLL_CONTROL_2, + (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) | + (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT)); + snd_soc_component_write(component, WM8993_FLL_CONTROL_3, fll_div.k); + + reg4 = snd_soc_component_read(component, WM8993_FLL_CONTROL_4); + reg4 &= ~WM8993_FLL_N_MASK; + reg4 |= fll_div.n << WM8993_FLL_N_SHIFT; + snd_soc_component_write(component, WM8993_FLL_CONTROL_4, reg4); + + reg5 &= ~WM8993_FLL_CLK_REF_DIV_MASK; + reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT; + snd_soc_component_write(component, WM8993_FLL_CONTROL_5, reg5); + + /* If we've got an interrupt wired up make sure we get it */ + if (i2c->irq) + timeout = msecs_to_jiffies(20); + else if (Fref < 1000000) + timeout = msecs_to_jiffies(3); + else + timeout = msecs_to_jiffies(1); + + try_wait_for_completion(&wm8993->fll_lock); + + /* Enable the FLL */ + snd_soc_component_write(component, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA); + + timeout = wait_for_completion_timeout(&wm8993->fll_lock, timeout); + if (i2c->irq && !timeout) + dev_warn(component->dev, "Timed out waiting for FLL\n"); + + dev_dbg(component->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout); + + wm8993->fll_fref = Fref; + wm8993->fll_fout = Fout; + wm8993->fll_src = source; + + return 0; +} + +static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + return _wm8993_set_fll(dai->component, fll_id, source, Fref, Fout); +} + +static int configure_clock(struct snd_soc_component *component) +{ + struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component); + unsigned int reg; + + /* This should be done on init() for bypass paths */ + switch (wm8993->sysclk_source) { + case WM8993_SYSCLK_MCLK: + dev_dbg(component->dev, "Using %dHz MCLK\n", wm8993->mclk_rate); + + reg = snd_soc_component_read(component, WM8993_CLOCKING_2); + reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC); + if (wm8993->mclk_rate > 13500000) { + reg |= WM8993_MCLK_DIV; + wm8993->sysclk_rate = wm8993->mclk_rate / 2; + } else { + reg &= ~WM8993_MCLK_DIV; + wm8993->sysclk_rate = wm8993->mclk_rate; + } + snd_soc_component_write(component, WM8993_CLOCKING_2, reg); + break; + + case WM8993_SYSCLK_FLL: + dev_dbg(component->dev, "Using %dHz FLL clock\n", + wm8993->fll_fout); + + reg = snd_soc_component_read(component, WM8993_CLOCKING_2); + reg |= WM8993_SYSCLK_SRC; + if (wm8993->fll_fout > 13500000) { + reg |= WM8993_MCLK_DIV; + wm8993->sysclk_rate = wm8993->fll_fout / 2; + } else { + reg &= ~WM8993_MCLK_DIV; + wm8993->sysclk_rate = wm8993->fll_fout; + } + snd_soc_component_write(component, WM8993_CLOCKING_2, reg); + break; + + default: + dev_err(component->dev, "System clock not configured\n"); + return -EINVAL; + } + + dev_dbg(component->dev, "CLK_SYS is %dHz\n", wm8993->sysclk_rate); + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0); +static const DECLARE_TLV_DB_SCALE(drc_comp_threash, -4500, 75, 0); +static const DECLARE_TLV_DB_SCALE(drc_comp_amp, -2250, 75, 0); +static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0); +static const DECLARE_TLV_DB_RANGE(drc_max_tlv, + 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0), + 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0) +); +static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -1800, 300, 0); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0); + +static const char *dac_deemph_text[] = { + "None", + "32kHz", + "44.1kHz", + "48kHz", +}; + +static SOC_ENUM_SINGLE_DECL(dac_deemph, + WM8993_DAC_CTRL, 4, dac_deemph_text); + +static const char *adc_hpf_text[] = { + "Hi-Fi", + "Voice 1", + "Voice 2", + "Voice 3", +}; + +static SOC_ENUM_SINGLE_DECL(adc_hpf, + WM8993_ADC_CTRL, 5, adc_hpf_text); + +static const char *drc_path_text[] = { + "ADC", + "DAC" +}; + +static SOC_ENUM_SINGLE_DECL(drc_path, + WM8993_DRC_CONTROL_1, 14, drc_path_text); + +static const char *drc_r0_text[] = { + "1", + "1/2", + "1/4", + "1/8", + "1/16", + "0", +}; + +static SOC_ENUM_SINGLE_DECL(drc_r0, + WM8993_DRC_CONTROL_3, 8, drc_r0_text); + +static const char *drc_r1_text[] = { + "1", + "1/2", + "1/4", + "1/8", + "0", +}; + +static SOC_ENUM_SINGLE_DECL(drc_r1, + WM8993_DRC_CONTROL_4, 13, drc_r1_text); + +static const char *drc_attack_text[] = { + "Reserved", + "181us", + "363us", + "726us", + "1.45ms", + "2.9ms", + "5.8ms", + "11.6ms", + "23.2ms", + "46.4ms", + "92.8ms", + "185.6ms", +}; + +static SOC_ENUM_SINGLE_DECL(drc_attack, + WM8993_DRC_CONTROL_2, 12, drc_attack_text); + +static const char *drc_decay_text[] = { + "186ms", + "372ms", + "743ms", + "1.49s", + "2.97ms", + "5.94ms", + "11.89ms", + "23.78ms", + "47.56ms", +}; + +static SOC_ENUM_SINGLE_DECL(drc_decay, + WM8993_DRC_CONTROL_2, 8, drc_decay_text); + +static const char *drc_ff_text[] = { + "5 samples", + "9 samples", +}; + +static SOC_ENUM_SINGLE_DECL(drc_ff, + WM8993_DRC_CONTROL_3, 7, drc_ff_text); + +static const char *drc_qr_rate_text[] = { + "0.725ms", + "1.45ms", + "5.8ms", +}; + +static SOC_ENUM_SINGLE_DECL(drc_qr_rate, + WM8993_DRC_CONTROL_3, 0, drc_qr_rate_text); + +static const char *drc_smooth_text[] = { + "Low", + "Medium", + "High", +}; + +static SOC_ENUM_SINGLE_DECL(drc_smooth, + WM8993_DRC_CONTROL_1, 4, drc_smooth_text); + +static const struct snd_kcontrol_new wm8993_snd_controls[] = { +SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8993_DIGITAL_SIDE_TONE, + 5, 9, 12, 0, sidetone_tlv), + +SOC_SINGLE("DRC Switch", WM8993_DRC_CONTROL_1, 15, 1, 0), +SOC_ENUM("DRC Path", drc_path), +SOC_SINGLE_TLV("DRC Compressor Threshold Volume", WM8993_DRC_CONTROL_2, + 2, 60, 1, drc_comp_threash), +SOC_SINGLE_TLV("DRC Compressor Amplitude Volume", WM8993_DRC_CONTROL_3, + 11, 30, 1, drc_comp_amp), +SOC_ENUM("DRC R0", drc_r0), +SOC_ENUM("DRC R1", drc_r1), +SOC_SINGLE_TLV("DRC Minimum Volume", WM8993_DRC_CONTROL_1, 2, 3, 1, + drc_min_tlv), +SOC_SINGLE_TLV("DRC Maximum Volume", WM8993_DRC_CONTROL_1, 0, 3, 0, + drc_max_tlv), +SOC_ENUM("DRC Attack Rate", drc_attack), +SOC_ENUM("DRC Decay Rate", drc_decay), +SOC_ENUM("DRC FF Delay", drc_ff), +SOC_SINGLE("DRC Anti-clip Switch", WM8993_DRC_CONTROL_1, 9, 1, 0), +SOC_SINGLE("DRC Quick Release Switch", WM8993_DRC_CONTROL_1, 10, 1, 0), +SOC_SINGLE_TLV("DRC Quick Release Volume", WM8993_DRC_CONTROL_3, 2, 3, 0, + drc_qr_tlv), +SOC_ENUM("DRC Quick Release Rate", drc_qr_rate), +SOC_SINGLE("DRC Smoothing Switch", WM8993_DRC_CONTROL_1, 11, 1, 0), +SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8993_DRC_CONTROL_1, 8, 1, 0), +SOC_ENUM("DRC Smoothing Hysteresis Threshold", drc_smooth), +SOC_SINGLE_TLV("DRC Startup Volume", WM8993_DRC_CONTROL_4, 8, 18, 0, + drc_startup_tlv), + +SOC_SINGLE("EQ Switch", WM8993_EQ1, 0, 1, 0), + +SOC_DOUBLE_R_TLV("Capture Volume", WM8993_LEFT_ADC_DIGITAL_VOLUME, + WM8993_RIGHT_ADC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv), +SOC_SINGLE("ADC High Pass Filter Switch", WM8993_ADC_CTRL, 8, 1, 0), +SOC_ENUM("ADC High Pass Filter Mode", adc_hpf), + +SOC_DOUBLE_R_TLV("Playback Volume", WM8993_LEFT_DAC_DIGITAL_VOLUME, + WM8993_RIGHT_DAC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv), +SOC_SINGLE_TLV("Playback Boost Volume", WM8993_AUDIO_INTERFACE_2, 10, 3, 0, + dac_boost_tlv), +SOC_ENUM("DAC Deemphasis", dac_deemph), + +SOC_SINGLE_TLV("SPKL DAC Volume", WM8993_SPKMIXL_ATTENUATION, + 2, 1, 1, wm_hubs_spkmix_tlv), + +SOC_SINGLE_TLV("SPKR DAC Volume", WM8993_SPKMIXR_ATTENUATION, + 2, 1, 1, wm_hubs_spkmix_tlv), +}; + +static const struct snd_kcontrol_new wm8993_eq_controls[] = { +SOC_SINGLE_TLV("EQ1 Volume", WM8993_EQ2, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 Volume", WM8993_EQ3, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 Volume", WM8993_EQ4, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 Volume", WM8993_EQ5, 0, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ5 Volume", WM8993_EQ6, 0, 24, 0, eq_tlv), +}; + +static int clk_sys_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return configure_clock(component); + + case SND_SOC_DAPM_POST_PMD: + break; + } + + return 0; +} + +static const struct snd_kcontrol_new left_speaker_mixer[] = { +SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0), +SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0), +SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 6, 1, 0), +}; + +static const struct snd_kcontrol_new right_speaker_mixer[] = { +SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 6, 1, 0), +SOC_DAPM_SINGLE("IN1RP Switch", WM8993_SPEAKER_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 0, 1, 0), +}; + +static const char *aif_text[] = { + "Left", "Right" +}; + +static SOC_ENUM_SINGLE_DECL(aifoutl_enum, + WM8993_AUDIO_INTERFACE_1, 15, aif_text); + +static const struct snd_kcontrol_new aifoutl_mux = + SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum); + +static SOC_ENUM_SINGLE_DECL(aifoutr_enum, + WM8993_AUDIO_INTERFACE_1, 14, aif_text); + +static const struct snd_kcontrol_new aifoutr_mux = + SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum); + +static SOC_ENUM_SINGLE_DECL(aifinl_enum, + WM8993_AUDIO_INTERFACE_2, 15, aif_text); + +static const struct snd_kcontrol_new aifinl_mux = + SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum); + +static SOC_ENUM_SINGLE_DECL(aifinr_enum, + WM8993_AUDIO_INTERFACE_2, 14, aif_text); + +static const struct snd_kcontrol_new aifinr_mux = + SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum); + +static const char *sidetone_text[] = { + "None", "Left", "Right" +}; + +static SOC_ENUM_SINGLE_DECL(sidetonel_enum, + WM8993_DIGITAL_SIDE_TONE, 2, sidetone_text); + +static const struct snd_kcontrol_new sidetonel_mux = + SOC_DAPM_ENUM("Left Sidetone", sidetonel_enum); + +static SOC_ENUM_SINGLE_DECL(sidetoner_enum, + WM8993_DIGITAL_SIDE_TONE, 0, sidetone_text); + +static const struct snd_kcontrol_new sidetoner_mux = + SOC_DAPM_ENUM("Right Sidetone", sidetoner_enum); + +static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8993_BUS_CONTROL_1, 1, 0, clk_sys_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("TOCLK", WM8993_CLOCKING_1, 14, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8993_CLOCKING_3, 0, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("VMID", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_ADC("ADCL", NULL, WM8993_POWER_MANAGEMENT_2, 1, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, WM8993_POWER_MANAGEMENT_2, 0, 0), + +SND_SOC_DAPM_MUX("AIFOUTL Mux", SND_SOC_NOPM, 0, 0, &aifoutl_mux), +SND_SOC_DAPM_MUX("AIFOUTR Mux", SND_SOC_NOPM, 0, 0, &aifoutr_mux), + +SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux), +SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux), + +SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &sidetonel_mux), +SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &sidetoner_mux), + +SND_SOC_DAPM_DAC("DACL", NULL, WM8993_POWER_MANAGEMENT_3, 1, 0), +SND_SOC_DAPM_DAC("DACR", NULL, WM8993_POWER_MANAGEMENT_3, 0, 0), + +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux), + +SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0, + left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), +SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0, + right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), +SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route routes[] = { + { "MICBIAS1", NULL, "VMID" }, + { "MICBIAS2", NULL, "VMID" }, + + { "ADCL", NULL, "CLK_SYS" }, + { "ADCL", NULL, "CLK_DSP" }, + { "ADCR", NULL, "CLK_SYS" }, + { "ADCR", NULL, "CLK_DSP" }, + + { "AIFOUTL Mux", "Left", "ADCL" }, + { "AIFOUTL Mux", "Right", "ADCR" }, + { "AIFOUTR Mux", "Left", "ADCL" }, + { "AIFOUTR Mux", "Right", "ADCR" }, + + { "AIFOUTL", NULL, "AIFOUTL Mux" }, + { "AIFOUTR", NULL, "AIFOUTR Mux" }, + + { "DACL Mux", "Left", "AIFINL" }, + { "DACL Mux", "Right", "AIFINR" }, + { "DACR Mux", "Left", "AIFINL" }, + { "DACR Mux", "Right", "AIFINR" }, + + { "DACL Sidetone", "Left", "ADCL" }, + { "DACL Sidetone", "Right", "ADCR" }, + { "DACR Sidetone", "Left", "ADCL" }, + { "DACR Sidetone", "Right", "ADCR" }, + + { "DACL", NULL, "CLK_SYS" }, + { "DACL", NULL, "CLK_DSP" }, + { "DACL", NULL, "DACL Mux" }, + { "DACL", NULL, "DACL Sidetone" }, + { "DACR", NULL, "CLK_SYS" }, + { "DACR", NULL, "CLK_DSP" }, + { "DACR", NULL, "DACR Mux" }, + { "DACR", NULL, "DACR Sidetone" }, + + { "Left Output Mixer", "DAC Switch", "DACL" }, + + { "Right Output Mixer", "DAC Switch", "DACR" }, + + { "Left Output PGA", NULL, "CLK_SYS" }, + + { "Right Output PGA", NULL, "CLK_SYS" }, + + { "SPKL", "DAC Switch", "DACL" }, + { "SPKL", NULL, "CLK_SYS" }, + + { "SPKR", "DAC Switch", "DACR" }, + { "SPKR", NULL, "CLK_SYS" }, + + { "Left Headphone Mux", "DAC", "DACL" }, + { "Right Headphone Mux", "DAC", "DACR" }, +}; + +static int wm8993_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component); + int ret; + + wm_hubs_set_bias_level(component, level); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + /* VMID=2*40k */ + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1, + WM8993_VMID_SEL_MASK, 0x2); + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_2, + WM8993_TSHUT_ENA, WM8993_TSHUT_ENA); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies), + wm8993->supplies); + if (ret != 0) + return ret; + + regcache_cache_only(wm8993->regmap, false); + regcache_sync(wm8993->regmap); + + wm_hubs_vmid_ena(component); + + /* Bring up VMID with fast soft start */ + snd_soc_component_update_bits(component, WM8993_ANTIPOP2, + WM8993_STARTUP_BIAS_ENA | + WM8993_VMID_BUF_ENA | + WM8993_VMID_RAMP_MASK | + WM8993_BIAS_SRC, + WM8993_STARTUP_BIAS_ENA | + WM8993_VMID_BUF_ENA | + WM8993_VMID_RAMP_MASK | + WM8993_BIAS_SRC); + + /* If either line output is single ended we + * need the VMID buffer */ + if (!wm8993->pdata.lineout1_diff || + !wm8993->pdata.lineout2_diff) + snd_soc_component_update_bits(component, WM8993_ANTIPOP1, + WM8993_LINEOUT_VMID_BUF_ENA, + WM8993_LINEOUT_VMID_BUF_ENA); + + /* VMID=2*40k */ + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1, + WM8993_VMID_SEL_MASK | + WM8993_BIAS_ENA, + WM8993_BIAS_ENA | 0x2); + msleep(32); + + /* Switch to normal bias */ + snd_soc_component_update_bits(component, WM8993_ANTIPOP2, + WM8993_BIAS_SRC | + WM8993_STARTUP_BIAS_ENA, 0); + } + + /* VMID=2*240k */ + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1, + WM8993_VMID_SEL_MASK, 0x4); + + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_2, + WM8993_TSHUT_ENA, 0); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, WM8993_ANTIPOP1, + WM8993_LINEOUT_VMID_BUF_ENA, 0); + + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1, + WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA, + 0); + + snd_soc_component_update_bits(component, WM8993_ANTIPOP2, + WM8993_STARTUP_BIAS_ENA | + WM8993_VMID_BUF_ENA | + WM8993_VMID_RAMP_MASK | + WM8993_BIAS_SRC, 0); + + regcache_cache_only(wm8993->regmap, true); + regcache_mark_dirty(wm8993->regmap); + + regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), + wm8993->supplies); + break; + } + + return 0; +} + +static int wm8993_set_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = codec_dai->component; + struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case WM8993_SYSCLK_MCLK: + wm8993->mclk_rate = freq; + fallthrough; + case WM8993_SYSCLK_FLL: + wm8993->sysclk_source = clk_id; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int wm8993_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component); + unsigned int aif1 = snd_soc_component_read(component, WM8993_AUDIO_INTERFACE_1); + unsigned int aif4 = snd_soc_component_read(component, WM8993_AUDIO_INTERFACE_4); + + aif1 &= ~(WM8993_BCLK_DIR | WM8993_AIF_BCLK_INV | + WM8993_AIF_LRCLK_INV | WM8993_AIF_FMT_MASK); + aif4 &= ~WM8993_LRCLK_DIR; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + wm8993->master = 0; + break; + case SND_SOC_DAIFMT_CBS_CFM: + aif4 |= WM8993_LRCLK_DIR; + wm8993->master = 1; + break; + case SND_SOC_DAIFMT_CBM_CFS: + aif1 |= WM8993_BCLK_DIR; + wm8993->master = 1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + aif1 |= WM8993_BCLK_DIR; + aif4 |= WM8993_LRCLK_DIR; + wm8993->master = 1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif1 |= WM8993_AIF_LRCLK_INV; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + aif1 |= 0x18; + break; + case SND_SOC_DAIFMT_I2S: + aif1 |= 0x10; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif1 |= 0x8; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8993_AIF_BCLK_INV; + break; + default: + return -EINVAL; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif1 |= WM8993_AIF_BCLK_INV | WM8993_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8993_AIF_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif1 |= WM8993_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM8993_AUDIO_INTERFACE_1, aif1); + snd_soc_component_write(component, WM8993_AUDIO_INTERFACE_4, aif4); + + return 0; +} + +static int wm8993_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component); + int ret, i, best, best_val, cur_val; + unsigned int clocking1, clocking3, aif1, aif4; + + clocking1 = snd_soc_component_read(component, WM8993_CLOCKING_1); + clocking1 &= ~WM8993_BCLK_DIV_MASK; + + clocking3 = snd_soc_component_read(component, WM8993_CLOCKING_3); + clocking3 &= ~(WM8993_CLK_SYS_RATE_MASK | WM8993_SAMPLE_RATE_MASK); + + aif1 = snd_soc_component_read(component, WM8993_AUDIO_INTERFACE_1); + aif1 &= ~WM8993_AIF_WL_MASK; + + aif4 = snd_soc_component_read(component, WM8993_AUDIO_INTERFACE_4); + aif4 &= ~WM8993_LRCLK_RATE_MASK; + + /* What BCLK do we need? */ + wm8993->fs = params_rate(params); + wm8993->bclk = 2 * wm8993->fs; + if (wm8993->tdm_slots) { + dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n", + wm8993->tdm_slots, wm8993->tdm_width); + wm8993->bclk *= wm8993->tdm_width * wm8993->tdm_slots; + } else { + switch (params_width(params)) { + case 16: + wm8993->bclk *= 16; + break; + case 20: + wm8993->bclk *= 20; + aif1 |= 0x8; + break; + case 24: + wm8993->bclk *= 24; + aif1 |= 0x10; + break; + case 32: + wm8993->bclk *= 32; + aif1 |= 0x18; + break; + default: + return -EINVAL; + } + } + + dev_dbg(component->dev, "Target BCLK is %dHz\n", wm8993->bclk); + + ret = configure_clock(component); + if (ret != 0) + return ret; + + /* Select nearest CLK_SYS_RATE */ + best = 0; + best_val = abs((wm8993->sysclk_rate / clk_sys_rates[0].ratio) + - wm8993->fs); + for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) { + cur_val = abs((wm8993->sysclk_rate / + clk_sys_rates[i].ratio) - wm8993->fs); + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + dev_dbg(component->dev, "Selected CLK_SYS_RATIO of %d\n", + clk_sys_rates[best].ratio); + clocking3 |= (clk_sys_rates[best].clk_sys_rate + << WM8993_CLK_SYS_RATE_SHIFT); + + /* SAMPLE_RATE */ + best = 0; + best_val = abs(wm8993->fs - sample_rates[0].rate); + for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { + /* Closest match */ + cur_val = abs(wm8993->fs - sample_rates[i].rate); + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + dev_dbg(component->dev, "Selected SAMPLE_RATE of %dHz\n", + sample_rates[best].rate); + clocking3 |= (sample_rates[best].sample_rate + << WM8993_SAMPLE_RATE_SHIFT); + + /* BCLK_DIV */ + best = 0; + best_val = INT_MAX; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = ((wm8993->sysclk_rate * 10) / bclk_divs[i].div) + - wm8993->bclk; + if (cur_val < 0) /* Table is sorted */ + break; + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + wm8993->bclk = (wm8993->sysclk_rate * 10) / bclk_divs[best].div; + dev_dbg(component->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n", + bclk_divs[best].div, wm8993->bclk); + clocking1 |= bclk_divs[best].bclk_div << WM8993_BCLK_DIV_SHIFT; + + /* LRCLK is a simple fraction of BCLK */ + dev_dbg(component->dev, "LRCLK_RATE is %d\n", wm8993->bclk / wm8993->fs); + aif4 |= wm8993->bclk / wm8993->fs; + + snd_soc_component_write(component, WM8993_CLOCKING_1, clocking1); + snd_soc_component_write(component, WM8993_CLOCKING_3, clocking3); + snd_soc_component_write(component, WM8993_AUDIO_INTERFACE_1, aif1); + snd_soc_component_write(component, WM8993_AUDIO_INTERFACE_4, aif4); + + /* ReTune Mobile? */ + if (wm8993->pdata.num_retune_configs) { + u16 eq1 = snd_soc_component_read(component, WM8993_EQ1); + struct wm8993_retune_mobile_setting *s; + + best = 0; + best_val = abs(wm8993->pdata.retune_configs[0].rate + - wm8993->fs); + for (i = 0; i < wm8993->pdata.num_retune_configs; i++) { + cur_val = abs(wm8993->pdata.retune_configs[i].rate + - wm8993->fs); + if (cur_val < best_val) { + best_val = cur_val; + best = i; + } + } + s = &wm8993->pdata.retune_configs[best]; + + dev_dbg(component->dev, "ReTune Mobile %s tuned for %dHz\n", + s->name, s->rate); + + /* Disable EQ while we reconfigure */ + snd_soc_component_update_bits(component, WM8993_EQ1, WM8993_EQ_ENA, 0); + + for (i = 1; i < ARRAY_SIZE(s->config); i++) + snd_soc_component_write(component, WM8993_EQ1 + i, s->config[i]); + + snd_soc_component_update_bits(component, WM8993_EQ1, WM8993_EQ_ENA, eq1); + } + + return 0; +} + +static int wm8993_mute(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + unsigned int reg; + + reg = snd_soc_component_read(component, WM8993_DAC_CTRL); + + if (mute) + reg |= WM8993_DAC_MUTE; + else + reg &= ~WM8993_DAC_MUTE; + + snd_soc_component_write(component, WM8993_DAC_CTRL, reg); + + return 0; +} + +static int wm8993_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component); + int aif1 = 0; + int aif2 = 0; + + /* Don't need to validate anything if we're turning off TDM */ + if (slots == 0) { + wm8993->tdm_slots = 0; + goto out; + } + + /* Note that we allow configurations we can't handle ourselves - + * for example, we can generate clocks for slots 2 and up even if + * we can't use those slots ourselves. + */ + aif1 |= WM8993_AIFADC_TDM; + aif2 |= WM8993_AIFDAC_TDM; + + switch (rx_mask) { + case 3: + break; + case 0xc: + aif1 |= WM8993_AIFADC_TDM_CHAN; + break; + default: + return -EINVAL; + } + + + switch (tx_mask) { + case 3: + break; + case 0xc: + aif2 |= WM8993_AIFDAC_TDM_CHAN; + break; + default: + return -EINVAL; + } + +out: + wm8993->tdm_width = slot_width; + wm8993->tdm_slots = slots / 2; + + snd_soc_component_update_bits(component, WM8993_AUDIO_INTERFACE_1, + WM8993_AIFADC_TDM | WM8993_AIFADC_TDM_CHAN, aif1); + snd_soc_component_update_bits(component, WM8993_AUDIO_INTERFACE_2, + WM8993_AIFDAC_TDM | WM8993_AIFDAC_TDM_CHAN, aif2); + + return 0; +} + +static irqreturn_t wm8993_irq(int irq, void *data) +{ + struct wm8993_priv *wm8993 = data; + int mask, val, ret; + + ret = regmap_read(wm8993->regmap, WM8993_GPIO_CTRL_1, &val); + if (ret != 0) { + dev_err(wm8993->dev, "Failed to read interrupt status: %d\n", + ret); + return IRQ_NONE; + } + + ret = regmap_read(wm8993->regmap, WM8993_GPIOCTRL_2, &mask); + if (ret != 0) { + dev_err(wm8993->dev, "Failed to read interrupt mask: %d\n", + ret); + return IRQ_NONE; + } + + /* The IRQ pin status is visible in the register too */ + val &= ~(mask | WM8993_IRQ); + if (!val) + return IRQ_NONE; + + if (val & WM8993_TEMPOK_EINT) + dev_crit(wm8993->dev, "Thermal warning\n"); + + if (val & WM8993_FLL_LOCK_EINT) { + dev_dbg(wm8993->dev, "FLL locked\n"); + complete(&wm8993->fll_lock); + } + + ret = regmap_write(wm8993->regmap, WM8993_GPIO_CTRL_1, val); + if (ret != 0) + dev_err(wm8993->dev, "Failed to ack interrupt: %d\n", ret); + + return IRQ_HANDLED; +} + +static const struct snd_soc_dai_ops wm8993_ops = { + .set_sysclk = wm8993_set_sysclk, + .set_fmt = wm8993_set_dai_fmt, + .hw_params = wm8993_hw_params, + .mute_stream = wm8993_mute, + .set_pll = wm8993_set_fll, + .set_tdm_slot = wm8993_set_tdm_slot, + .no_capture_mute = 1, +}; + +#define WM8993_RATES SNDRV_PCM_RATE_8000_48000 + +#define WM8993_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8993_dai = { + .name = "wm8993-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8993_RATES, + .formats = WM8993_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8993_RATES, + .formats = WM8993_FORMATS, + .sig_bits = 24, + }, + .ops = &wm8993_ops, + .symmetric_rate = 1, +}; + +static int wm8993_probe(struct snd_soc_component *component) +{ + struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + wm8993->hubs_data.hp_startup_mode = 1; + wm8993->hubs_data.dcs_codes_l = -2; + wm8993->hubs_data.dcs_codes_r = -2; + wm8993->hubs_data.series_startup = 1; + + /* Latch volume update bits and default ZC on */ + snd_soc_component_update_bits(component, WM8993_RIGHT_DAC_DIGITAL_VOLUME, + WM8993_DAC_VU, WM8993_DAC_VU); + snd_soc_component_update_bits(component, WM8993_RIGHT_ADC_DIGITAL_VOLUME, + WM8993_ADC_VU, WM8993_ADC_VU); + + /* Manualy manage the HPOUT sequencing for independent stereo + * control. */ + snd_soc_component_update_bits(component, WM8993_ANALOGUE_HP_0, + WM8993_HPOUT1_AUTO_PU, 0); + + /* Use automatic clock configuration */ + snd_soc_component_update_bits(component, WM8993_CLOCKING_4, WM8993_SR_MODE, 0); + + wm_hubs_handle_analogue_pdata(component, wm8993->pdata.lineout1_diff, + wm8993->pdata.lineout2_diff, + wm8993->pdata.lineout1fb, + wm8993->pdata.lineout2fb, + wm8993->pdata.jd_scthr, + wm8993->pdata.jd_thr, + wm8993->pdata.micbias1_delay, + wm8993->pdata.micbias2_delay, + wm8993->pdata.micbias1_lvl, + wm8993->pdata.micbias2_lvl); + + snd_soc_add_component_controls(component, wm8993_snd_controls, + ARRAY_SIZE(wm8993_snd_controls)); + if (wm8993->pdata.num_retune_configs != 0) { + dev_dbg(component->dev, "Using ReTune Mobile\n"); + } else { + dev_dbg(component->dev, "No ReTune Mobile, using normal EQ\n"); + snd_soc_add_component_controls(component, wm8993_eq_controls, + ARRAY_SIZE(wm8993_eq_controls)); + } + + snd_soc_dapm_new_controls(dapm, wm8993_dapm_widgets, + ARRAY_SIZE(wm8993_dapm_widgets)); + wm_hubs_add_analogue_controls(component); + + snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes)); + wm_hubs_add_analogue_routes(component, wm8993->pdata.lineout1_diff, + wm8993->pdata.lineout2_diff); + + /* If the line outputs are differential then we aren't presenting + * VMID as an output and can disable it. + */ + if (wm8993->pdata.lineout1_diff && wm8993->pdata.lineout2_diff) + dapm->idle_bias_off = 1; + + return 0; + +} + +#ifdef CONFIG_PM +static int wm8993_suspend(struct snd_soc_component *component) +{ + struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component); + int fll_fout = wm8993->fll_fout; + int fll_fref = wm8993->fll_fref; + int ret; + + /* Stop the FLL in an orderly fashion */ + ret = _wm8993_set_fll(component, 0, 0, 0, 0); + if (ret != 0) { + dev_err(component->dev, "Failed to stop FLL\n"); + return ret; + } + + wm8993->fll_fout = fll_fout; + wm8993->fll_fref = fll_fref; + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + return 0; +} + +static int wm8993_resume(struct snd_soc_component *component) +{ + struct wm8993_priv *wm8993 = snd_soc_component_get_drvdata(component); + int ret; + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + /* Restart the FLL? */ + if (wm8993->fll_fout) { + int fll_fout = wm8993->fll_fout; + int fll_fref = wm8993->fll_fref; + + wm8993->fll_fref = 0; + wm8993->fll_fout = 0; + + ret = _wm8993_set_fll(component, 0, wm8993->fll_src, + fll_fref, fll_fout); + if (ret != 0) + dev_err(component->dev, "Failed to restart FLL\n"); + } + + return 0; +} +#else +#define wm8993_suspend NULL +#define wm8993_resume NULL +#endif + +/* Tune DC servo configuration */ +static const struct reg_sequence wm8993_regmap_patch[] = { + { 0x44, 3 }, + { 0x56, 3 }, + { 0x44, 0 }, +}; + +static const struct regmap_config wm8993_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM8993_MAX_REGISTER, + .volatile_reg = wm8993_volatile, + .readable_reg = wm8993_readable, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm8993_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8993_reg_defaults), +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8993 = { + .probe = wm8993_probe, + .suspend = wm8993_suspend, + .resume = wm8993_resume, + .set_bias_level = wm8993_set_bias_level, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm8993_i2c_probe(struct i2c_client *i2c) +{ + struct wm8993_priv *wm8993; + unsigned int reg; + int ret, i; + + wm8993 = devm_kzalloc(&i2c->dev, sizeof(struct wm8993_priv), + GFP_KERNEL); + if (wm8993 == NULL) + return -ENOMEM; + + wm8993->dev = &i2c->dev; + init_completion(&wm8993->fll_lock); + + wm8993->regmap = devm_regmap_init_i2c(i2c, &wm8993_regmap); + if (IS_ERR(wm8993->regmap)) { + ret = PTR_ERR(wm8993->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c, wm8993); + + for (i = 0; i < ARRAY_SIZE(wm8993->supplies); i++) + wm8993->supplies[i].supply = wm8993_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8993->supplies), + wm8993->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies), + wm8993->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = regmap_read(wm8993->regmap, WM8993_SOFTWARE_RESET, ®); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); + goto err_enable; + } + + if (reg != 0x8993) { + dev_err(&i2c->dev, "Invalid ID register value %x\n", reg); + ret = -EINVAL; + goto err_enable; + } + + ret = regmap_write(wm8993->regmap, WM8993_SOFTWARE_RESET, 0xffff); + if (ret != 0) + goto err_enable; + + ret = regmap_register_patch(wm8993->regmap, wm8993_regmap_patch, + ARRAY_SIZE(wm8993_regmap_patch)); + if (ret != 0) + dev_warn(wm8993->dev, "Failed to apply regmap patch: %d\n", + ret); + + if (i2c->irq) { + /* Put GPIO1 into interrupt mode (only GPIO1 can output IRQ) */ + ret = regmap_update_bits(wm8993->regmap, WM8993_GPIO1, + WM8993_GPIO1_PD | + WM8993_GPIO1_SEL_MASK, 7); + if (ret != 0) + goto err_enable; + + ret = request_threaded_irq(i2c->irq, NULL, wm8993_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "wm8993", wm8993); + if (ret != 0) + goto err_enable; + + } + + regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies); + + regcache_cache_only(wm8993->regmap, true); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8993, &wm8993_dai, 1); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + goto err_irq; + } + + return 0; + +err_irq: + if (i2c->irq) + free_irq(i2c->irq, wm8993); +err_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies); + return ret; +} + +static void wm8993_i2c_remove(struct i2c_client *i2c) +{ + struct wm8993_priv *wm8993 = i2c_get_clientdata(i2c); + + if (i2c->irq) + free_irq(i2c->irq, wm8993); + regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies); +} + +static const struct i2c_device_id wm8993_i2c_id[] = { + { "wm8993", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id); + +static struct i2c_driver wm8993_i2c_driver = { + .driver = { + .name = "wm8993", + }, + .probe = wm8993_i2c_probe, + .remove = wm8993_i2c_remove, + .id_table = wm8993_i2c_id, +}; + +module_i2c_driver(wm8993_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8993 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8993.h b/sound/soc/codecs/wm8993.h new file mode 100644 index 0000000000..91811aa158 --- /dev/null +++ b/sound/soc/codecs/wm8993.h @@ -0,0 +1,2139 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef WM8993_H +#define WM8993_H + +#define WM8993_SYSCLK_MCLK 1 +#define WM8993_SYSCLK_FLL 2 + +#define WM8993_FLL_MCLK 1 +#define WM8993_FLL_BCLK 2 +#define WM8993_FLL_LRCLK 3 + +/* + * Register values. + */ +#define WM8993_SOFTWARE_RESET 0x00 +#define WM8993_POWER_MANAGEMENT_1 0x01 +#define WM8993_POWER_MANAGEMENT_2 0x02 +#define WM8993_POWER_MANAGEMENT_3 0x03 +#define WM8993_AUDIO_INTERFACE_1 0x04 +#define WM8993_AUDIO_INTERFACE_2 0x05 +#define WM8993_CLOCKING_1 0x06 +#define WM8993_CLOCKING_2 0x07 +#define WM8993_AUDIO_INTERFACE_3 0x08 +#define WM8993_AUDIO_INTERFACE_4 0x09 +#define WM8993_DAC_CTRL 0x0A +#define WM8993_LEFT_DAC_DIGITAL_VOLUME 0x0B +#define WM8993_RIGHT_DAC_DIGITAL_VOLUME 0x0C +#define WM8993_DIGITAL_SIDE_TONE 0x0D +#define WM8993_ADC_CTRL 0x0E +#define WM8993_LEFT_ADC_DIGITAL_VOLUME 0x0F +#define WM8993_RIGHT_ADC_DIGITAL_VOLUME 0x10 +#define WM8993_GPIO_CTRL_1 0x12 +#define WM8993_GPIO1 0x13 +#define WM8993_IRQ_DEBOUNCE 0x14 +#define WM8993_INPUTS_CLAMP_REG 0x15 +#define WM8993_GPIOCTRL_2 0x16 +#define WM8993_GPIO_POL 0x17 +#define WM8993_LEFT_LINE_INPUT_1_2_VOLUME 0x18 +#define WM8993_LEFT_LINE_INPUT_3_4_VOLUME 0x19 +#define WM8993_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A +#define WM8993_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B +#define WM8993_LEFT_OUTPUT_VOLUME 0x1C +#define WM8993_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8993_LINE_OUTPUTS_VOLUME 0x1E +#define WM8993_HPOUT2_VOLUME 0x1F +#define WM8993_LEFT_OPGA_VOLUME 0x20 +#define WM8993_RIGHT_OPGA_VOLUME 0x21 +#define WM8993_SPKMIXL_ATTENUATION 0x22 +#define WM8993_SPKMIXR_ATTENUATION 0x23 +#define WM8993_SPKOUT_MIXERS 0x24 +#define WM8993_SPKOUT_BOOST 0x25 +#define WM8993_SPEAKER_VOLUME_LEFT 0x26 +#define WM8993_SPEAKER_VOLUME_RIGHT 0x27 +#define WM8993_INPUT_MIXER2 0x28 +#define WM8993_INPUT_MIXER3 0x29 +#define WM8993_INPUT_MIXER4 0x2A +#define WM8993_INPUT_MIXER5 0x2B +#define WM8993_INPUT_MIXER6 0x2C +#define WM8993_OUTPUT_MIXER1 0x2D +#define WM8993_OUTPUT_MIXER2 0x2E +#define WM8993_OUTPUT_MIXER3 0x2F +#define WM8993_OUTPUT_MIXER4 0x30 +#define WM8993_OUTPUT_MIXER5 0x31 +#define WM8993_OUTPUT_MIXER6 0x32 +#define WM8993_HPOUT2_MIXER 0x33 +#define WM8993_LINE_MIXER1 0x34 +#define WM8993_LINE_MIXER2 0x35 +#define WM8993_SPEAKER_MIXER 0x36 +#define WM8993_ADDITIONAL_CONTROL 0x37 +#define WM8993_ANTIPOP1 0x38 +#define WM8993_ANTIPOP2 0x39 +#define WM8993_MICBIAS 0x3A +#define WM8993_FLL_CONTROL_1 0x3C +#define WM8993_FLL_CONTROL_2 0x3D +#define WM8993_FLL_CONTROL_3 0x3E +#define WM8993_FLL_CONTROL_4 0x3F +#define WM8993_FLL_CONTROL_5 0x40 +#define WM8993_CLOCKING_3 0x41 +#define WM8993_CLOCKING_4 0x42 +#define WM8993_MW_SLAVE_CONTROL 0x43 +#define WM8993_BUS_CONTROL_1 0x45 +#define WM8993_WRITE_SEQUENCER_0 0x46 +#define WM8993_WRITE_SEQUENCER_1 0x47 +#define WM8993_WRITE_SEQUENCER_2 0x48 +#define WM8993_WRITE_SEQUENCER_3 0x49 +#define WM8993_WRITE_SEQUENCER_4 0x4A +#define WM8993_WRITE_SEQUENCER_5 0x4B +#define WM8993_CHARGE_PUMP_1 0x4C +#define WM8993_CLASS_W_0 0x51 +#define WM8993_DC_SERVO_0 0x54 +#define WM8993_DC_SERVO_1 0x55 +#define WM8993_DC_SERVO_3 0x57 +#define WM8993_DC_SERVO_READBACK_0 0x58 +#define WM8993_DC_SERVO_READBACK_1 0x59 +#define WM8993_DC_SERVO_READBACK_2 0x5A +#define WM8993_ANALOGUE_HP_0 0x60 +#define WM8993_EQ1 0x62 +#define WM8993_EQ2 0x63 +#define WM8993_EQ3 0x64 +#define WM8993_EQ4 0x65 +#define WM8993_EQ5 0x66 +#define WM8993_EQ6 0x67 +#define WM8993_EQ7 0x68 +#define WM8993_EQ8 0x69 +#define WM8993_EQ9 0x6A +#define WM8993_EQ10 0x6B +#define WM8993_EQ11 0x6C +#define WM8993_EQ12 0x6D +#define WM8993_EQ13 0x6E +#define WM8993_EQ14 0x6F +#define WM8993_EQ15 0x70 +#define WM8993_EQ16 0x71 +#define WM8993_EQ17 0x72 +#define WM8993_EQ18 0x73 +#define WM8993_EQ19 0x74 +#define WM8993_EQ20 0x75 +#define WM8993_EQ21 0x76 +#define WM8993_EQ22 0x77 +#define WM8993_EQ23 0x78 +#define WM8993_EQ24 0x79 +#define WM8993_DIGITAL_PULLS 0x7A +#define WM8993_DRC_CONTROL_1 0x7B +#define WM8993_DRC_CONTROL_2 0x7C +#define WM8993_DRC_CONTROL_3 0x7D +#define WM8993_DRC_CONTROL_4 0x7E + +#define WM8993_REGISTER_COUNT 0x7F +#define WM8993_MAX_REGISTER 0x7E + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +#define WM8993_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */ +#define WM8993_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */ +#define WM8993_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */ + +/* + * R1 (0x01) - Power Management (1) + */ +#define WM8993_SPKOUTR_ENA 0x2000 /* SPKOUTR_ENA */ +#define WM8993_SPKOUTR_ENA_MASK 0x2000 /* SPKOUTR_ENA */ +#define WM8993_SPKOUTR_ENA_SHIFT 13 /* SPKOUTR_ENA */ +#define WM8993_SPKOUTR_ENA_WIDTH 1 /* SPKOUTR_ENA */ +#define WM8993_SPKOUTL_ENA 0x1000 /* SPKOUTL_ENA */ +#define WM8993_SPKOUTL_ENA_MASK 0x1000 /* SPKOUTL_ENA */ +#define WM8993_SPKOUTL_ENA_SHIFT 12 /* SPKOUTL_ENA */ +#define WM8993_SPKOUTL_ENA_WIDTH 1 /* SPKOUTL_ENA */ +#define WM8993_HPOUT2_ENA 0x0800 /* HPOUT2_ENA */ +#define WM8993_HPOUT2_ENA_MASK 0x0800 /* HPOUT2_ENA */ +#define WM8993_HPOUT2_ENA_SHIFT 11 /* HPOUT2_ENA */ +#define WM8993_HPOUT2_ENA_WIDTH 1 /* HPOUT2_ENA */ +#define WM8993_HPOUT1L_ENA 0x0200 /* HPOUT1L_ENA */ +#define WM8993_HPOUT1L_ENA_MASK 0x0200 /* HPOUT1L_ENA */ +#define WM8993_HPOUT1L_ENA_SHIFT 9 /* HPOUT1L_ENA */ +#define WM8993_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */ +#define WM8993_HPOUT1R_ENA 0x0100 /* HPOUT1R_ENA */ +#define WM8993_HPOUT1R_ENA_MASK 0x0100 /* HPOUT1R_ENA */ +#define WM8993_HPOUT1R_ENA_SHIFT 8 /* HPOUT1R_ENA */ +#define WM8993_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */ +#define WM8993_MICB2_ENA 0x0020 /* MICB2_ENA */ +#define WM8993_MICB2_ENA_MASK 0x0020 /* MICB2_ENA */ +#define WM8993_MICB2_ENA_SHIFT 5 /* MICB2_ENA */ +#define WM8993_MICB2_ENA_WIDTH 1 /* MICB2_ENA */ +#define WM8993_MICB1_ENA 0x0010 /* MICB1_ENA */ +#define WM8993_MICB1_ENA_MASK 0x0010 /* MICB1_ENA */ +#define WM8993_MICB1_ENA_SHIFT 4 /* MICB1_ENA */ +#define WM8993_MICB1_ENA_WIDTH 1 /* MICB1_ENA */ +#define WM8993_VMID_SEL_MASK 0x0006 /* VMID_SEL - [2:1] */ +#define WM8993_VMID_SEL_SHIFT 1 /* VMID_SEL - [2:1] */ +#define WM8993_VMID_SEL_WIDTH 2 /* VMID_SEL - [2:1] */ +#define WM8993_BIAS_ENA 0x0001 /* BIAS_ENA */ +#define WM8993_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */ +#define WM8993_BIAS_ENA_SHIFT 0 /* BIAS_ENA */ +#define WM8993_BIAS_ENA_WIDTH 1 /* BIAS_ENA */ + +/* + * R2 (0x02) - Power Management (2) + */ +#define WM8993_TSHUT_ENA 0x4000 /* TSHUT_ENA */ +#define WM8993_TSHUT_ENA_MASK 0x4000 /* TSHUT_ENA */ +#define WM8993_TSHUT_ENA_SHIFT 14 /* TSHUT_ENA */ +#define WM8993_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */ +#define WM8993_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */ +#define WM8993_TSHUT_OPDIS_MASK 0x2000 /* TSHUT_OPDIS */ +#define WM8993_TSHUT_OPDIS_SHIFT 13 /* TSHUT_OPDIS */ +#define WM8993_TSHUT_OPDIS_WIDTH 1 /* TSHUT_OPDIS */ +#define WM8993_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8993_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */ +#define WM8993_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */ +#define WM8993_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define WM8993_MIXINL_ENA 0x0200 /* MIXINL_ENA */ +#define WM8993_MIXINL_ENA_MASK 0x0200 /* MIXINL_ENA */ +#define WM8993_MIXINL_ENA_SHIFT 9 /* MIXINL_ENA */ +#define WM8993_MIXINL_ENA_WIDTH 1 /* MIXINL_ENA */ +#define WM8993_MIXINR_ENA 0x0100 /* MIXINR_ENA */ +#define WM8993_MIXINR_ENA_MASK 0x0100 /* MIXINR_ENA */ +#define WM8993_MIXINR_ENA_SHIFT 8 /* MIXINR_ENA */ +#define WM8993_MIXINR_ENA_WIDTH 1 /* MIXINR_ENA */ +#define WM8993_IN2L_ENA 0x0080 /* IN2L_ENA */ +#define WM8993_IN2L_ENA_MASK 0x0080 /* IN2L_ENA */ +#define WM8993_IN2L_ENA_SHIFT 7 /* IN2L_ENA */ +#define WM8993_IN2L_ENA_WIDTH 1 /* IN2L_ENA */ +#define WM8993_IN1L_ENA 0x0040 /* IN1L_ENA */ +#define WM8993_IN1L_ENA_MASK 0x0040 /* IN1L_ENA */ +#define WM8993_IN1L_ENA_SHIFT 6 /* IN1L_ENA */ +#define WM8993_IN1L_ENA_WIDTH 1 /* IN1L_ENA */ +#define WM8993_IN2R_ENA 0x0020 /* IN2R_ENA */ +#define WM8993_IN2R_ENA_MASK 0x0020 /* IN2R_ENA */ +#define WM8993_IN2R_ENA_SHIFT 5 /* IN2R_ENA */ +#define WM8993_IN2R_ENA_WIDTH 1 /* IN2R_ENA */ +#define WM8993_IN1R_ENA 0x0010 /* IN1R_ENA */ +#define WM8993_IN1R_ENA_MASK 0x0010 /* IN1R_ENA */ +#define WM8993_IN1R_ENA_SHIFT 4 /* IN1R_ENA */ +#define WM8993_IN1R_ENA_WIDTH 1 /* IN1R_ENA */ +#define WM8993_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8993_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */ +#define WM8993_ADCL_ENA_SHIFT 1 /* ADCL_ENA */ +#define WM8993_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ +#define WM8993_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8993_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */ +#define WM8993_ADCR_ENA_SHIFT 0 /* ADCR_ENA */ +#define WM8993_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ + +/* + * R3 (0x03) - Power Management (3) + */ +#define WM8993_LINEOUT1N_ENA 0x2000 /* LINEOUT1N_ENA */ +#define WM8993_LINEOUT1N_ENA_MASK 0x2000 /* LINEOUT1N_ENA */ +#define WM8993_LINEOUT1N_ENA_SHIFT 13 /* LINEOUT1N_ENA */ +#define WM8993_LINEOUT1N_ENA_WIDTH 1 /* LINEOUT1N_ENA */ +#define WM8993_LINEOUT1P_ENA 0x1000 /* LINEOUT1P_ENA */ +#define WM8993_LINEOUT1P_ENA_MASK 0x1000 /* LINEOUT1P_ENA */ +#define WM8993_LINEOUT1P_ENA_SHIFT 12 /* LINEOUT1P_ENA */ +#define WM8993_LINEOUT1P_ENA_WIDTH 1 /* LINEOUT1P_ENA */ +#define WM8993_LINEOUT2N_ENA 0x0800 /* LINEOUT2N_ENA */ +#define WM8993_LINEOUT2N_ENA_MASK 0x0800 /* LINEOUT2N_ENA */ +#define WM8993_LINEOUT2N_ENA_SHIFT 11 /* LINEOUT2N_ENA */ +#define WM8993_LINEOUT2N_ENA_WIDTH 1 /* LINEOUT2N_ENA */ +#define WM8993_LINEOUT2P_ENA 0x0400 /* LINEOUT2P_ENA */ +#define WM8993_LINEOUT2P_ENA_MASK 0x0400 /* LINEOUT2P_ENA */ +#define WM8993_LINEOUT2P_ENA_SHIFT 10 /* LINEOUT2P_ENA */ +#define WM8993_LINEOUT2P_ENA_WIDTH 1 /* LINEOUT2P_ENA */ +#define WM8993_SPKRVOL_ENA 0x0200 /* SPKRVOL_ENA */ +#define WM8993_SPKRVOL_ENA_MASK 0x0200 /* SPKRVOL_ENA */ +#define WM8993_SPKRVOL_ENA_SHIFT 9 /* SPKRVOL_ENA */ +#define WM8993_SPKRVOL_ENA_WIDTH 1 /* SPKRVOL_ENA */ +#define WM8993_SPKLVOL_ENA 0x0100 /* SPKLVOL_ENA */ +#define WM8993_SPKLVOL_ENA_MASK 0x0100 /* SPKLVOL_ENA */ +#define WM8993_SPKLVOL_ENA_SHIFT 8 /* SPKLVOL_ENA */ +#define WM8993_SPKLVOL_ENA_WIDTH 1 /* SPKLVOL_ENA */ +#define WM8993_MIXOUTLVOL_ENA 0x0080 /* MIXOUTLVOL_ENA */ +#define WM8993_MIXOUTLVOL_ENA_MASK 0x0080 /* MIXOUTLVOL_ENA */ +#define WM8993_MIXOUTLVOL_ENA_SHIFT 7 /* MIXOUTLVOL_ENA */ +#define WM8993_MIXOUTLVOL_ENA_WIDTH 1 /* MIXOUTLVOL_ENA */ +#define WM8993_MIXOUTRVOL_ENA 0x0040 /* MIXOUTRVOL_ENA */ +#define WM8993_MIXOUTRVOL_ENA_MASK 0x0040 /* MIXOUTRVOL_ENA */ +#define WM8993_MIXOUTRVOL_ENA_SHIFT 6 /* MIXOUTRVOL_ENA */ +#define WM8993_MIXOUTRVOL_ENA_WIDTH 1 /* MIXOUTRVOL_ENA */ +#define WM8993_MIXOUTL_ENA 0x0020 /* MIXOUTL_ENA */ +#define WM8993_MIXOUTL_ENA_MASK 0x0020 /* MIXOUTL_ENA */ +#define WM8993_MIXOUTL_ENA_SHIFT 5 /* MIXOUTL_ENA */ +#define WM8993_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */ +#define WM8993_MIXOUTR_ENA 0x0010 /* MIXOUTR_ENA */ +#define WM8993_MIXOUTR_ENA_MASK 0x0010 /* MIXOUTR_ENA */ +#define WM8993_MIXOUTR_ENA_SHIFT 4 /* MIXOUTR_ENA */ +#define WM8993_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */ +#define WM8993_DACL_ENA 0x0002 /* DACL_ENA */ +#define WM8993_DACL_ENA_MASK 0x0002 /* DACL_ENA */ +#define WM8993_DACL_ENA_SHIFT 1 /* DACL_ENA */ +#define WM8993_DACL_ENA_WIDTH 1 /* DACL_ENA */ +#define WM8993_DACR_ENA 0x0001 /* DACR_ENA */ +#define WM8993_DACR_ENA_MASK 0x0001 /* DACR_ENA */ +#define WM8993_DACR_ENA_SHIFT 0 /* DACR_ENA */ +#define WM8993_DACR_ENA_WIDTH 1 /* DACR_ENA */ + +/* + * R4 (0x04) - Audio Interface (1) + */ +#define WM8993_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */ +#define WM8993_AIFADCL_SRC_MASK 0x8000 /* AIFADCL_SRC */ +#define WM8993_AIFADCL_SRC_SHIFT 15 /* AIFADCL_SRC */ +#define WM8993_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */ +#define WM8993_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */ +#define WM8993_AIFADCR_SRC_MASK 0x4000 /* AIFADCR_SRC */ +#define WM8993_AIFADCR_SRC_SHIFT 14 /* AIFADCR_SRC */ +#define WM8993_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */ +#define WM8993_AIFADC_TDM 0x2000 /* AIFADC_TDM */ +#define WM8993_AIFADC_TDM_MASK 0x2000 /* AIFADC_TDM */ +#define WM8993_AIFADC_TDM_SHIFT 13 /* AIFADC_TDM */ +#define WM8993_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */ +#define WM8993_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8993_AIFADC_TDM_CHAN_MASK 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8993_AIFADC_TDM_CHAN_SHIFT 12 /* AIFADC_TDM_CHAN */ +#define WM8993_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */ +#define WM8993_BCLK_DIR 0x0200 /* BCLK_DIR */ +#define WM8993_BCLK_DIR_MASK 0x0200 /* BCLK_DIR */ +#define WM8993_BCLK_DIR_SHIFT 9 /* BCLK_DIR */ +#define WM8993_BCLK_DIR_WIDTH 1 /* BCLK_DIR */ +#define WM8993_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */ +#define WM8993_AIF_BCLK_INV_MASK 0x0100 /* AIF_BCLK_INV */ +#define WM8993_AIF_BCLK_INV_SHIFT 8 /* AIF_BCLK_INV */ +#define WM8993_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */ +#define WM8993_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */ +#define WM8993_AIF_LRCLK_INV_MASK 0x0080 /* AIF_LRCLK_INV */ +#define WM8993_AIF_LRCLK_INV_SHIFT 7 /* AIF_LRCLK_INV */ +#define WM8993_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */ +#define WM8993_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */ +#define WM8993_AIF_WL_SHIFT 5 /* AIF_WL - [6:5] */ +#define WM8993_AIF_WL_WIDTH 2 /* AIF_WL - [6:5] */ +#define WM8993_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */ +#define WM8993_AIF_FMT_SHIFT 3 /* AIF_FMT - [4:3] */ +#define WM8993_AIF_FMT_WIDTH 2 /* AIF_FMT - [4:3] */ + +/* + * R5 (0x05) - Audio Interface (2) + */ +#define WM8993_AIFDACL_SRC 0x8000 /* AIFDACL_SRC */ +#define WM8993_AIFDACL_SRC_MASK 0x8000 /* AIFDACL_SRC */ +#define WM8993_AIFDACL_SRC_SHIFT 15 /* AIFDACL_SRC */ +#define WM8993_AIFDACL_SRC_WIDTH 1 /* AIFDACL_SRC */ +#define WM8993_AIFDACR_SRC 0x4000 /* AIFDACR_SRC */ +#define WM8993_AIFDACR_SRC_MASK 0x4000 /* AIFDACR_SRC */ +#define WM8993_AIFDACR_SRC_SHIFT 14 /* AIFDACR_SRC */ +#define WM8993_AIFDACR_SRC_WIDTH 1 /* AIFDACR_SRC */ +#define WM8993_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ +#define WM8993_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */ +#define WM8993_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */ +#define WM8993_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */ +#define WM8993_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8993_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8993_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */ +#define WM8993_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */ +#define WM8993_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */ +#define WM8993_DAC_BOOST_SHIFT 10 /* DAC_BOOST - [11:10] */ +#define WM8993_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [11:10] */ +#define WM8993_DAC_COMP 0x0010 /* DAC_COMP */ +#define WM8993_DAC_COMP_MASK 0x0010 /* DAC_COMP */ +#define WM8993_DAC_COMP_SHIFT 4 /* DAC_COMP */ +#define WM8993_DAC_COMP_WIDTH 1 /* DAC_COMP */ +#define WM8993_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */ +#define WM8993_DAC_COMPMODE_MASK 0x0008 /* DAC_COMPMODE */ +#define WM8993_DAC_COMPMODE_SHIFT 3 /* DAC_COMPMODE */ +#define WM8993_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */ +#define WM8993_ADC_COMP 0x0004 /* ADC_COMP */ +#define WM8993_ADC_COMP_MASK 0x0004 /* ADC_COMP */ +#define WM8993_ADC_COMP_SHIFT 2 /* ADC_COMP */ +#define WM8993_ADC_COMP_WIDTH 1 /* ADC_COMP */ +#define WM8993_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */ +#define WM8993_ADC_COMPMODE_MASK 0x0002 /* ADC_COMPMODE */ +#define WM8993_ADC_COMPMODE_SHIFT 1 /* ADC_COMPMODE */ +#define WM8993_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */ +#define WM8993_LOOPBACK 0x0001 /* LOOPBACK */ +#define WM8993_LOOPBACK_MASK 0x0001 /* LOOPBACK */ +#define WM8993_LOOPBACK_SHIFT 0 /* LOOPBACK */ +#define WM8993_LOOPBACK_WIDTH 1 /* LOOPBACK */ + +/* + * R6 (0x06) - Clocking 1 + */ +#define WM8993_TOCLK_RATE 0x8000 /* TOCLK_RATE */ +#define WM8993_TOCLK_RATE_MASK 0x8000 /* TOCLK_RATE */ +#define WM8993_TOCLK_RATE_SHIFT 15 /* TOCLK_RATE */ +#define WM8993_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */ +#define WM8993_TOCLK_ENA 0x4000 /* TOCLK_ENA */ +#define WM8993_TOCLK_ENA_MASK 0x4000 /* TOCLK_ENA */ +#define WM8993_TOCLK_ENA_SHIFT 14 /* TOCLK_ENA */ +#define WM8993_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */ +#define WM8993_OPCLK_DIV_MASK 0x1E00 /* OPCLK_DIV - [12:9] */ +#define WM8993_OPCLK_DIV_SHIFT 9 /* OPCLK_DIV - [12:9] */ +#define WM8993_OPCLK_DIV_WIDTH 4 /* OPCLK_DIV - [12:9] */ +#define WM8993_DCLK_DIV_MASK 0x01C0 /* DCLK_DIV - [8:6] */ +#define WM8993_DCLK_DIV_SHIFT 6 /* DCLK_DIV - [8:6] */ +#define WM8993_DCLK_DIV_WIDTH 3 /* DCLK_DIV - [8:6] */ +#define WM8993_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */ +#define WM8993_BCLK_DIV_SHIFT 1 /* BCLK_DIV - [4:1] */ +#define WM8993_BCLK_DIV_WIDTH 4 /* BCLK_DIV - [4:1] */ + +/* + * R7 (0x07) - Clocking 2 + */ +#define WM8993_MCLK_SRC 0x8000 /* MCLK_SRC */ +#define WM8993_MCLK_SRC_MASK 0x8000 /* MCLK_SRC */ +#define WM8993_MCLK_SRC_SHIFT 15 /* MCLK_SRC */ +#define WM8993_MCLK_SRC_WIDTH 1 /* MCLK_SRC */ +#define WM8993_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */ +#define WM8993_SYSCLK_SRC_MASK 0x4000 /* SYSCLK_SRC */ +#define WM8993_SYSCLK_SRC_SHIFT 14 /* SYSCLK_SRC */ +#define WM8993_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */ +#define WM8993_MCLK_DIV 0x1000 /* MCLK_DIV */ +#define WM8993_MCLK_DIV_MASK 0x1000 /* MCLK_DIV */ +#define WM8993_MCLK_DIV_SHIFT 12 /* MCLK_DIV */ +#define WM8993_MCLK_DIV_WIDTH 1 /* MCLK_DIV */ +#define WM8993_MCLK_INV 0x0400 /* MCLK_INV */ +#define WM8993_MCLK_INV_MASK 0x0400 /* MCLK_INV */ +#define WM8993_MCLK_INV_SHIFT 10 /* MCLK_INV */ +#define WM8993_MCLK_INV_WIDTH 1 /* MCLK_INV */ +#define WM8993_ADC_DIV_MASK 0x00E0 /* ADC_DIV - [7:5] */ +#define WM8993_ADC_DIV_SHIFT 5 /* ADC_DIV - [7:5] */ +#define WM8993_ADC_DIV_WIDTH 3 /* ADC_DIV - [7:5] */ +#define WM8993_DAC_DIV_MASK 0x001C /* DAC_DIV - [4:2] */ +#define WM8993_DAC_DIV_SHIFT 2 /* DAC_DIV - [4:2] */ +#define WM8993_DAC_DIV_WIDTH 3 /* DAC_DIV - [4:2] */ + +/* + * R8 (0x08) - Audio Interface (3) + */ +#define WM8993_AIF_MSTR1 0x8000 /* AIF_MSTR1 */ +#define WM8993_AIF_MSTR1_MASK 0x8000 /* AIF_MSTR1 */ +#define WM8993_AIF_MSTR1_SHIFT 15 /* AIF_MSTR1 */ +#define WM8993_AIF_MSTR1_WIDTH 1 /* AIF_MSTR1 */ + +/* + * R9 (0x09) - Audio Interface (4) + */ +#define WM8993_AIF_TRIS 0x2000 /* AIF_TRIS */ +#define WM8993_AIF_TRIS_MASK 0x2000 /* AIF_TRIS */ +#define WM8993_AIF_TRIS_SHIFT 13 /* AIF_TRIS */ +#define WM8993_AIF_TRIS_WIDTH 1 /* AIF_TRIS */ +#define WM8993_LRCLK_DIR 0x0800 /* LRCLK_DIR */ +#define WM8993_LRCLK_DIR_MASK 0x0800 /* LRCLK_DIR */ +#define WM8993_LRCLK_DIR_SHIFT 11 /* LRCLK_DIR */ +#define WM8993_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */ +#define WM8993_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */ +#define WM8993_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */ +#define WM8993_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */ + +/* + * R10 (0x0A) - DAC CTRL + */ +#define WM8993_DAC_OSR128 0x2000 /* DAC_OSR128 */ +#define WM8993_DAC_OSR128_MASK 0x2000 /* DAC_OSR128 */ +#define WM8993_DAC_OSR128_SHIFT 13 /* DAC_OSR128 */ +#define WM8993_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */ +#define WM8993_DAC_MONO 0x0200 /* DAC_MONO */ +#define WM8993_DAC_MONO_MASK 0x0200 /* DAC_MONO */ +#define WM8993_DAC_MONO_SHIFT 9 /* DAC_MONO */ +#define WM8993_DAC_MONO_WIDTH 1 /* DAC_MONO */ +#define WM8993_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */ +#define WM8993_DAC_SB_FILT_MASK 0x0100 /* DAC_SB_FILT */ +#define WM8993_DAC_SB_FILT_SHIFT 8 /* DAC_SB_FILT */ +#define WM8993_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */ +#define WM8993_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */ +#define WM8993_DAC_MUTERATE_MASK 0x0080 /* DAC_MUTERATE */ +#define WM8993_DAC_MUTERATE_SHIFT 7 /* DAC_MUTERATE */ +#define WM8993_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ +#define WM8993_DAC_UNMUTE_RAMP 0x0040 /* DAC_UNMUTE_RAMP */ +#define WM8993_DAC_UNMUTE_RAMP_MASK 0x0040 /* DAC_UNMUTE_RAMP */ +#define WM8993_DAC_UNMUTE_RAMP_SHIFT 6 /* DAC_UNMUTE_RAMP */ +#define WM8993_DAC_UNMUTE_RAMP_WIDTH 1 /* DAC_UNMUTE_RAMP */ +#define WM8993_DEEMPH_MASK 0x0030 /* DEEMPH - [5:4] */ +#define WM8993_DEEMPH_SHIFT 4 /* DEEMPH - [5:4] */ +#define WM8993_DEEMPH_WIDTH 2 /* DEEMPH - [5:4] */ +#define WM8993_DAC_MUTE 0x0004 /* DAC_MUTE */ +#define WM8993_DAC_MUTE_MASK 0x0004 /* DAC_MUTE */ +#define WM8993_DAC_MUTE_SHIFT 2 /* DAC_MUTE */ +#define WM8993_DAC_MUTE_WIDTH 1 /* DAC_MUTE */ +#define WM8993_DACL_DATINV 0x0002 /* DACL_DATINV */ +#define WM8993_DACL_DATINV_MASK 0x0002 /* DACL_DATINV */ +#define WM8993_DACL_DATINV_SHIFT 1 /* DACL_DATINV */ +#define WM8993_DACL_DATINV_WIDTH 1 /* DACL_DATINV */ +#define WM8993_DACR_DATINV 0x0001 /* DACR_DATINV */ +#define WM8993_DACR_DATINV_MASK 0x0001 /* DACR_DATINV */ +#define WM8993_DACR_DATINV_SHIFT 0 /* DACR_DATINV */ +#define WM8993_DACR_DATINV_WIDTH 1 /* DACR_DATINV */ + +/* + * R11 (0x0B) - Left DAC Digital Volume + */ +#define WM8993_DAC_VU 0x0100 /* DAC_VU */ +#define WM8993_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8993_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8993_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8993_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8993_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */ +#define WM8993_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */ + +/* + * R12 (0x0C) - Right DAC Digital Volume + */ +#define WM8993_DAC_VU 0x0100 /* DAC_VU */ +#define WM8993_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8993_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8993_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8993_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8993_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */ +#define WM8993_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */ + +/* + * R13 (0x0D) - Digital Side Tone + */ +#define WM8993_ADCL_DAC_SVOL_MASK 0x1E00 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8993_ADCL_DAC_SVOL_SHIFT 9 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8993_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8993_ADCR_DAC_SVOL_MASK 0x01E0 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8993_ADCR_DAC_SVOL_SHIFT 5 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8993_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8993_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */ +#define WM8993_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */ +#define WM8993_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */ +#define WM8993_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */ +#define WM8993_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */ +#define WM8993_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */ + +/* + * R14 (0x0E) - ADC CTRL + */ +#define WM8993_ADC_OSR128 0x0200 /* ADC_OSR128 */ +#define WM8993_ADC_OSR128_MASK 0x0200 /* ADC_OSR128 */ +#define WM8993_ADC_OSR128_SHIFT 9 /* ADC_OSR128 */ +#define WM8993_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */ +#define WM8993_ADC_HPF 0x0100 /* ADC_HPF */ +#define WM8993_ADC_HPF_MASK 0x0100 /* ADC_HPF */ +#define WM8993_ADC_HPF_SHIFT 8 /* ADC_HPF */ +#define WM8993_ADC_HPF_WIDTH 1 /* ADC_HPF */ +#define WM8993_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */ +#define WM8993_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */ +#define WM8993_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */ +#define WM8993_ADCL_DATINV 0x0002 /* ADCL_DATINV */ +#define WM8993_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */ +#define WM8993_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */ +#define WM8993_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */ +#define WM8993_ADCR_DATINV 0x0001 /* ADCR_DATINV */ +#define WM8993_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */ +#define WM8993_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */ +#define WM8993_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */ + +/* + * R15 (0x0F) - Left ADC Digital Volume + */ +#define WM8993_ADC_VU 0x0100 /* ADC_VU */ +#define WM8993_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8993_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8993_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8993_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8993_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */ +#define WM8993_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */ + +/* + * R16 (0x10) - Right ADC Digital Volume + */ +#define WM8993_ADC_VU 0x0100 /* ADC_VU */ +#define WM8993_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8993_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8993_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8993_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8993_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */ +#define WM8993_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */ + +/* + * R18 (0x12) - GPIO CTRL 1 + */ +#define WM8993_JD2_SC_EINT 0x8000 /* JD2_SC_EINT */ +#define WM8993_JD2_SC_EINT_MASK 0x8000 /* JD2_SC_EINT */ +#define WM8993_JD2_SC_EINT_SHIFT 15 /* JD2_SC_EINT */ +#define WM8993_JD2_SC_EINT_WIDTH 1 /* JD2_SC_EINT */ +#define WM8993_JD2_EINT 0x4000 /* JD2_EINT */ +#define WM8993_JD2_EINT_MASK 0x4000 /* JD2_EINT */ +#define WM8993_JD2_EINT_SHIFT 14 /* JD2_EINT */ +#define WM8993_JD2_EINT_WIDTH 1 /* JD2_EINT */ +#define WM8993_WSEQ_EINT 0x2000 /* WSEQ_EINT */ +#define WM8993_WSEQ_EINT_MASK 0x2000 /* WSEQ_EINT */ +#define WM8993_WSEQ_EINT_SHIFT 13 /* WSEQ_EINT */ +#define WM8993_WSEQ_EINT_WIDTH 1 /* WSEQ_EINT */ +#define WM8993_IRQ 0x1000 /* IRQ */ +#define WM8993_IRQ_MASK 0x1000 /* IRQ */ +#define WM8993_IRQ_SHIFT 12 /* IRQ */ +#define WM8993_IRQ_WIDTH 1 /* IRQ */ +#define WM8993_TEMPOK_EINT 0x0800 /* TEMPOK_EINT */ +#define WM8993_TEMPOK_EINT_MASK 0x0800 /* TEMPOK_EINT */ +#define WM8993_TEMPOK_EINT_SHIFT 11 /* TEMPOK_EINT */ +#define WM8993_TEMPOK_EINT_WIDTH 1 /* TEMPOK_EINT */ +#define WM8993_JD1_SC_EINT 0x0400 /* JD1_SC_EINT */ +#define WM8993_JD1_SC_EINT_MASK 0x0400 /* JD1_SC_EINT */ +#define WM8993_JD1_SC_EINT_SHIFT 10 /* JD1_SC_EINT */ +#define WM8993_JD1_SC_EINT_WIDTH 1 /* JD1_SC_EINT */ +#define WM8993_JD1_EINT 0x0200 /* JD1_EINT */ +#define WM8993_JD1_EINT_MASK 0x0200 /* JD1_EINT */ +#define WM8993_JD1_EINT_SHIFT 9 /* JD1_EINT */ +#define WM8993_JD1_EINT_WIDTH 1 /* JD1_EINT */ +#define WM8993_FLL_LOCK_EINT 0x0100 /* FLL_LOCK_EINT */ +#define WM8993_FLL_LOCK_EINT_MASK 0x0100 /* FLL_LOCK_EINT */ +#define WM8993_FLL_LOCK_EINT_SHIFT 8 /* FLL_LOCK_EINT */ +#define WM8993_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */ +#define WM8993_GPI8_EINT 0x0080 /* GPI8_EINT */ +#define WM8993_GPI8_EINT_MASK 0x0080 /* GPI8_EINT */ +#define WM8993_GPI8_EINT_SHIFT 7 /* GPI8_EINT */ +#define WM8993_GPI8_EINT_WIDTH 1 /* GPI8_EINT */ +#define WM8993_GPI7_EINT 0x0040 /* GPI7_EINT */ +#define WM8993_GPI7_EINT_MASK 0x0040 /* GPI7_EINT */ +#define WM8993_GPI7_EINT_SHIFT 6 /* GPI7_EINT */ +#define WM8993_GPI7_EINT_WIDTH 1 /* GPI7_EINT */ +#define WM8993_GPIO1_EINT 0x0001 /* GPIO1_EINT */ +#define WM8993_GPIO1_EINT_MASK 0x0001 /* GPIO1_EINT */ +#define WM8993_GPIO1_EINT_SHIFT 0 /* GPIO1_EINT */ +#define WM8993_GPIO1_EINT_WIDTH 1 /* GPIO1_EINT */ + +/* + * R19 (0x13) - GPIO1 + */ +#define WM8993_GPIO1_PU 0x0020 /* GPIO1_PU */ +#define WM8993_GPIO1_PU_MASK 0x0020 /* GPIO1_PU */ +#define WM8993_GPIO1_PU_SHIFT 5 /* GPIO1_PU */ +#define WM8993_GPIO1_PU_WIDTH 1 /* GPIO1_PU */ +#define WM8993_GPIO1_PD 0x0010 /* GPIO1_PD */ +#define WM8993_GPIO1_PD_MASK 0x0010 /* GPIO1_PD */ +#define WM8993_GPIO1_PD_SHIFT 4 /* GPIO1_PD */ +#define WM8993_GPIO1_PD_WIDTH 1 /* GPIO1_PD */ +#define WM8993_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */ +#define WM8993_GPIO1_SEL_SHIFT 0 /* GPIO1_SEL - [3:0] */ +#define WM8993_GPIO1_SEL_WIDTH 4 /* GPIO1_SEL - [3:0] */ + +/* + * R20 (0x14) - IRQ_DEBOUNCE + */ +#define WM8993_JD2_SC_DB 0x8000 /* JD2_SC_DB */ +#define WM8993_JD2_SC_DB_MASK 0x8000 /* JD2_SC_DB */ +#define WM8993_JD2_SC_DB_SHIFT 15 /* JD2_SC_DB */ +#define WM8993_JD2_SC_DB_WIDTH 1 /* JD2_SC_DB */ +#define WM8993_JD2_DB 0x4000 /* JD2_DB */ +#define WM8993_JD2_DB_MASK 0x4000 /* JD2_DB */ +#define WM8993_JD2_DB_SHIFT 14 /* JD2_DB */ +#define WM8993_JD2_DB_WIDTH 1 /* JD2_DB */ +#define WM8993_WSEQ_DB 0x2000 /* WSEQ_DB */ +#define WM8993_WSEQ_DB_MASK 0x2000 /* WSEQ_DB */ +#define WM8993_WSEQ_DB_SHIFT 13 /* WSEQ_DB */ +#define WM8993_WSEQ_DB_WIDTH 1 /* WSEQ_DB */ +#define WM8993_TEMPOK_DB 0x0800 /* TEMPOK_DB */ +#define WM8993_TEMPOK_DB_MASK 0x0800 /* TEMPOK_DB */ +#define WM8993_TEMPOK_DB_SHIFT 11 /* TEMPOK_DB */ +#define WM8993_TEMPOK_DB_WIDTH 1 /* TEMPOK_DB */ +#define WM8993_JD1_SC_DB 0x0400 /* JD1_SC_DB */ +#define WM8993_JD1_SC_DB_MASK 0x0400 /* JD1_SC_DB */ +#define WM8993_JD1_SC_DB_SHIFT 10 /* JD1_SC_DB */ +#define WM8993_JD1_SC_DB_WIDTH 1 /* JD1_SC_DB */ +#define WM8993_JD1_DB 0x0200 /* JD1_DB */ +#define WM8993_JD1_DB_MASK 0x0200 /* JD1_DB */ +#define WM8993_JD1_DB_SHIFT 9 /* JD1_DB */ +#define WM8993_JD1_DB_WIDTH 1 /* JD1_DB */ +#define WM8993_FLL_LOCK_DB 0x0100 /* FLL_LOCK_DB */ +#define WM8993_FLL_LOCK_DB_MASK 0x0100 /* FLL_LOCK_DB */ +#define WM8993_FLL_LOCK_DB_SHIFT 8 /* FLL_LOCK_DB */ +#define WM8993_FLL_LOCK_DB_WIDTH 1 /* FLL_LOCK_DB */ +#define WM8993_GPI8_DB 0x0080 /* GPI8_DB */ +#define WM8993_GPI8_DB_MASK 0x0080 /* GPI8_DB */ +#define WM8993_GPI8_DB_SHIFT 7 /* GPI8_DB */ +#define WM8993_GPI8_DB_WIDTH 1 /* GPI8_DB */ +#define WM8993_GPI7_DB 0x0008 /* GPI7_DB */ +#define WM8993_GPI7_DB_MASK 0x0008 /* GPI7_DB */ +#define WM8993_GPI7_DB_SHIFT 3 /* GPI7_DB */ +#define WM8993_GPI7_DB_WIDTH 1 /* GPI7_DB */ +#define WM8993_GPIO1_DB 0x0001 /* GPIO1_DB */ +#define WM8993_GPIO1_DB_MASK 0x0001 /* GPIO1_DB */ +#define WM8993_GPIO1_DB_SHIFT 0 /* GPIO1_DB */ +#define WM8993_GPIO1_DB_WIDTH 1 /* GPIO1_DB */ + +/* + * R21 (0x15) - Inputs Clamp + */ +#define WM8993_INPUTS_CLAMP 0x0040 /* INPUTS_CLAMP */ +#define WM8993_INPUTS_CLAMP_MASK 0x0040 /* INPUTS_CLAMP */ +#define WM8993_INPUTS_CLAMP_SHIFT 7 /* INPUTS_CLAMP */ +#define WM8993_INPUTS_CLAMP_WIDTH 1 /* INPUTS_CLAMP */ + +/* + * R22 (0x16) - GPIOCTRL 2 + */ +#define WM8993_IM_JD2_EINT 0x2000 /* IM_JD2_EINT */ +#define WM8993_IM_JD2_EINT_MASK 0x2000 /* IM_JD2_EINT */ +#define WM8993_IM_JD2_EINT_SHIFT 13 /* IM_JD2_EINT */ +#define WM8993_IM_JD2_EINT_WIDTH 1 /* IM_JD2_EINT */ +#define WM8993_IM_JD2_SC_EINT 0x1000 /* IM_JD2_SC_EINT */ +#define WM8993_IM_JD2_SC_EINT_MASK 0x1000 /* IM_JD2_SC_EINT */ +#define WM8993_IM_JD2_SC_EINT_SHIFT 12 /* IM_JD2_SC_EINT */ +#define WM8993_IM_JD2_SC_EINT_WIDTH 1 /* IM_JD2_SC_EINT */ +#define WM8993_IM_TEMPOK_EINT 0x0800 /* IM_TEMPOK_EINT */ +#define WM8993_IM_TEMPOK_EINT_MASK 0x0800 /* IM_TEMPOK_EINT */ +#define WM8993_IM_TEMPOK_EINT_SHIFT 11 /* IM_TEMPOK_EINT */ +#define WM8993_IM_TEMPOK_EINT_WIDTH 1 /* IM_TEMPOK_EINT */ +#define WM8993_IM_JD1_SC_EINT 0x0400 /* IM_JD1_SC_EINT */ +#define WM8993_IM_JD1_SC_EINT_MASK 0x0400 /* IM_JD1_SC_EINT */ +#define WM8993_IM_JD1_SC_EINT_SHIFT 10 /* IM_JD1_SC_EINT */ +#define WM8993_IM_JD1_SC_EINT_WIDTH 1 /* IM_JD1_SC_EINT */ +#define WM8993_IM_JD1_EINT 0x0200 /* IM_JD1_EINT */ +#define WM8993_IM_JD1_EINT_MASK 0x0200 /* IM_JD1_EINT */ +#define WM8993_IM_JD1_EINT_SHIFT 9 /* IM_JD1_EINT */ +#define WM8993_IM_JD1_EINT_WIDTH 1 /* IM_JD1_EINT */ +#define WM8993_IM_FLL_LOCK_EINT 0x0100 /* IM_FLL_LOCK_EINT */ +#define WM8993_IM_FLL_LOCK_EINT_MASK 0x0100 /* IM_FLL_LOCK_EINT */ +#define WM8993_IM_FLL_LOCK_EINT_SHIFT 8 /* IM_FLL_LOCK_EINT */ +#define WM8993_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */ +#define WM8993_IM_GPI8_EINT 0x0040 /* IM_GPI8_EINT */ +#define WM8993_IM_GPI8_EINT_MASK 0x0040 /* IM_GPI8_EINT */ +#define WM8993_IM_GPI8_EINT_SHIFT 6 /* IM_GPI8_EINT */ +#define WM8993_IM_GPI8_EINT_WIDTH 1 /* IM_GPI8_EINT */ +#define WM8993_IM_GPIO1_EINT 0x0020 /* IM_GPIO1_EINT */ +#define WM8993_IM_GPIO1_EINT_MASK 0x0020 /* IM_GPIO1_EINT */ +#define WM8993_IM_GPIO1_EINT_SHIFT 5 /* IM_GPIO1_EINT */ +#define WM8993_IM_GPIO1_EINT_WIDTH 1 /* IM_GPIO1_EINT */ +#define WM8993_GPI8_ENA 0x0010 /* GPI8_ENA */ +#define WM8993_GPI8_ENA_MASK 0x0010 /* GPI8_ENA */ +#define WM8993_GPI8_ENA_SHIFT 4 /* GPI8_ENA */ +#define WM8993_GPI8_ENA_WIDTH 1 /* GPI8_ENA */ +#define WM8993_IM_GPI7_EINT 0x0004 /* IM_GPI7_EINT */ +#define WM8993_IM_GPI7_EINT_MASK 0x0004 /* IM_GPI7_EINT */ +#define WM8993_IM_GPI7_EINT_SHIFT 2 /* IM_GPI7_EINT */ +#define WM8993_IM_GPI7_EINT_WIDTH 1 /* IM_GPI7_EINT */ +#define WM8993_IM_WSEQ_EINT 0x0002 /* IM_WSEQ_EINT */ +#define WM8993_IM_WSEQ_EINT_MASK 0x0002 /* IM_WSEQ_EINT */ +#define WM8993_IM_WSEQ_EINT_SHIFT 1 /* IM_WSEQ_EINT */ +#define WM8993_IM_WSEQ_EINT_WIDTH 1 /* IM_WSEQ_EINT */ +#define WM8993_GPI7_ENA 0x0001 /* GPI7_ENA */ +#define WM8993_GPI7_ENA_MASK 0x0001 /* GPI7_ENA */ +#define WM8993_GPI7_ENA_SHIFT 0 /* GPI7_ENA */ +#define WM8993_GPI7_ENA_WIDTH 1 /* GPI7_ENA */ + +/* + * R23 (0x17) - GPIO_POL + */ +#define WM8993_JD2_SC_POL 0x8000 /* JD2_SC_POL */ +#define WM8993_JD2_SC_POL_MASK 0x8000 /* JD2_SC_POL */ +#define WM8993_JD2_SC_POL_SHIFT 15 /* JD2_SC_POL */ +#define WM8993_JD2_SC_POL_WIDTH 1 /* JD2_SC_POL */ +#define WM8993_JD2_POL 0x4000 /* JD2_POL */ +#define WM8993_JD2_POL_MASK 0x4000 /* JD2_POL */ +#define WM8993_JD2_POL_SHIFT 14 /* JD2_POL */ +#define WM8993_JD2_POL_WIDTH 1 /* JD2_POL */ +#define WM8993_WSEQ_POL 0x2000 /* WSEQ_POL */ +#define WM8993_WSEQ_POL_MASK 0x2000 /* WSEQ_POL */ +#define WM8993_WSEQ_POL_SHIFT 13 /* WSEQ_POL */ +#define WM8993_WSEQ_POL_WIDTH 1 /* WSEQ_POL */ +#define WM8993_IRQ_POL 0x1000 /* IRQ_POL */ +#define WM8993_IRQ_POL_MASK 0x1000 /* IRQ_POL */ +#define WM8993_IRQ_POL_SHIFT 12 /* IRQ_POL */ +#define WM8993_IRQ_POL_WIDTH 1 /* IRQ_POL */ +#define WM8993_TEMPOK_POL 0x0800 /* TEMPOK_POL */ +#define WM8993_TEMPOK_POL_MASK 0x0800 /* TEMPOK_POL */ +#define WM8993_TEMPOK_POL_SHIFT 11 /* TEMPOK_POL */ +#define WM8993_TEMPOK_POL_WIDTH 1 /* TEMPOK_POL */ +#define WM8993_JD1_SC_POL 0x0400 /* JD1_SC_POL */ +#define WM8993_JD1_SC_POL_MASK 0x0400 /* JD1_SC_POL */ +#define WM8993_JD1_SC_POL_SHIFT 10 /* JD1_SC_POL */ +#define WM8993_JD1_SC_POL_WIDTH 1 /* JD1_SC_POL */ +#define WM8993_JD1_POL 0x0200 /* JD1_POL */ +#define WM8993_JD1_POL_MASK 0x0200 /* JD1_POL */ +#define WM8993_JD1_POL_SHIFT 9 /* JD1_POL */ +#define WM8993_JD1_POL_WIDTH 1 /* JD1_POL */ +#define WM8993_FLL_LOCK_POL 0x0100 /* FLL_LOCK_POL */ +#define WM8993_FLL_LOCK_POL_MASK 0x0100 /* FLL_LOCK_POL */ +#define WM8993_FLL_LOCK_POL_SHIFT 8 /* FLL_LOCK_POL */ +#define WM8993_FLL_LOCK_POL_WIDTH 1 /* FLL_LOCK_POL */ +#define WM8993_GPI8_POL 0x0080 /* GPI8_POL */ +#define WM8993_GPI8_POL_MASK 0x0080 /* GPI8_POL */ +#define WM8993_GPI8_POL_SHIFT 7 /* GPI8_POL */ +#define WM8993_GPI8_POL_WIDTH 1 /* GPI8_POL */ +#define WM8993_GPI7_POL 0x0040 /* GPI7_POL */ +#define WM8993_GPI7_POL_MASK 0x0040 /* GPI7_POL */ +#define WM8993_GPI7_POL_SHIFT 6 /* GPI7_POL */ +#define WM8993_GPI7_POL_WIDTH 1 /* GPI7_POL */ +#define WM8993_GPIO1_POL 0x0001 /* GPIO1_POL */ +#define WM8993_GPIO1_POL_MASK 0x0001 /* GPIO1_POL */ +#define WM8993_GPIO1_POL_SHIFT 0 /* GPIO1_POL */ +#define WM8993_GPIO1_POL_WIDTH 1 /* GPIO1_POL */ + +/* + * R24 (0x18) - Left Line Input 1&2 Volume + */ +#define WM8993_IN1_VU 0x0100 /* IN1_VU */ +#define WM8993_IN1_VU_MASK 0x0100 /* IN1_VU */ +#define WM8993_IN1_VU_SHIFT 8 /* IN1_VU */ +#define WM8993_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM8993_IN1L_MUTE 0x0080 /* IN1L_MUTE */ +#define WM8993_IN1L_MUTE_MASK 0x0080 /* IN1L_MUTE */ +#define WM8993_IN1L_MUTE_SHIFT 7 /* IN1L_MUTE */ +#define WM8993_IN1L_MUTE_WIDTH 1 /* IN1L_MUTE */ +#define WM8993_IN1L_ZC 0x0040 /* IN1L_ZC */ +#define WM8993_IN1L_ZC_MASK 0x0040 /* IN1L_ZC */ +#define WM8993_IN1L_ZC_SHIFT 6 /* IN1L_ZC */ +#define WM8993_IN1L_ZC_WIDTH 1 /* IN1L_ZC */ +#define WM8993_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */ +#define WM8993_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */ +#define WM8993_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */ + +/* + * R25 (0x19) - Left Line Input 3&4 Volume + */ +#define WM8993_IN2_VU 0x0100 /* IN2_VU */ +#define WM8993_IN2_VU_MASK 0x0100 /* IN2_VU */ +#define WM8993_IN2_VU_SHIFT 8 /* IN2_VU */ +#define WM8993_IN2_VU_WIDTH 1 /* IN2_VU */ +#define WM8993_IN2L_MUTE 0x0080 /* IN2L_MUTE */ +#define WM8993_IN2L_MUTE_MASK 0x0080 /* IN2L_MUTE */ +#define WM8993_IN2L_MUTE_SHIFT 7 /* IN2L_MUTE */ +#define WM8993_IN2L_MUTE_WIDTH 1 /* IN2L_MUTE */ +#define WM8993_IN2L_ZC 0x0040 /* IN2L_ZC */ +#define WM8993_IN2L_ZC_MASK 0x0040 /* IN2L_ZC */ +#define WM8993_IN2L_ZC_SHIFT 6 /* IN2L_ZC */ +#define WM8993_IN2L_ZC_WIDTH 1 /* IN2L_ZC */ +#define WM8993_IN2L_VOL_MASK 0x001F /* IN2L_VOL - [4:0] */ +#define WM8993_IN2L_VOL_SHIFT 0 /* IN2L_VOL - [4:0] */ +#define WM8993_IN2L_VOL_WIDTH 5 /* IN2L_VOL - [4:0] */ + +/* + * R26 (0x1A) - Right Line Input 1&2 Volume + */ +#define WM8993_IN1_VU 0x0100 /* IN1_VU */ +#define WM8993_IN1_VU_MASK 0x0100 /* IN1_VU */ +#define WM8993_IN1_VU_SHIFT 8 /* IN1_VU */ +#define WM8993_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM8993_IN1R_MUTE 0x0080 /* IN1R_MUTE */ +#define WM8993_IN1R_MUTE_MASK 0x0080 /* IN1R_MUTE */ +#define WM8993_IN1R_MUTE_SHIFT 7 /* IN1R_MUTE */ +#define WM8993_IN1R_MUTE_WIDTH 1 /* IN1R_MUTE */ +#define WM8993_IN1R_ZC 0x0040 /* IN1R_ZC */ +#define WM8993_IN1R_ZC_MASK 0x0040 /* IN1R_ZC */ +#define WM8993_IN1R_ZC_SHIFT 6 /* IN1R_ZC */ +#define WM8993_IN1R_ZC_WIDTH 1 /* IN1R_ZC */ +#define WM8993_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */ +#define WM8993_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */ +#define WM8993_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */ + +/* + * R27 (0x1B) - Right Line Input 3&4 Volume + */ +#define WM8993_IN2_VU 0x0100 /* IN2_VU */ +#define WM8993_IN2_VU_MASK 0x0100 /* IN2_VU */ +#define WM8993_IN2_VU_SHIFT 8 /* IN2_VU */ +#define WM8993_IN2_VU_WIDTH 1 /* IN2_VU */ +#define WM8993_IN2R_MUTE 0x0080 /* IN2R_MUTE */ +#define WM8993_IN2R_MUTE_MASK 0x0080 /* IN2R_MUTE */ +#define WM8993_IN2R_MUTE_SHIFT 7 /* IN2R_MUTE */ +#define WM8993_IN2R_MUTE_WIDTH 1 /* IN2R_MUTE */ +#define WM8993_IN2R_ZC 0x0040 /* IN2R_ZC */ +#define WM8993_IN2R_ZC_MASK 0x0040 /* IN2R_ZC */ +#define WM8993_IN2R_ZC_SHIFT 6 /* IN2R_ZC */ +#define WM8993_IN2R_ZC_WIDTH 1 /* IN2R_ZC */ +#define WM8993_IN2R_VOL_MASK 0x001F /* IN2R_VOL - [4:0] */ +#define WM8993_IN2R_VOL_SHIFT 0 /* IN2R_VOL - [4:0] */ +#define WM8993_IN2R_VOL_WIDTH 5 /* IN2R_VOL - [4:0] */ + +/* + * R28 (0x1C) - Left Output Volume + */ +#define WM8993_HPOUT1_VU 0x0100 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */ +#define WM8993_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */ +#define WM8993_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */ +#define WM8993_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */ +#define WM8993_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */ +#define WM8993_HPOUT1L_MUTE_N 0x0040 /* HPOUT1L_MUTE_N */ +#define WM8993_HPOUT1L_MUTE_N_MASK 0x0040 /* HPOUT1L_MUTE_N */ +#define WM8993_HPOUT1L_MUTE_N_SHIFT 6 /* HPOUT1L_MUTE_N */ +#define WM8993_HPOUT1L_MUTE_N_WIDTH 1 /* HPOUT1L_MUTE_N */ +#define WM8993_HPOUT1L_VOL_MASK 0x003F /* HPOUT1L_VOL - [5:0] */ +#define WM8993_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [5:0] */ +#define WM8993_HPOUT1L_VOL_WIDTH 6 /* HPOUT1L_VOL - [5:0] */ + +/* + * R29 (0x1D) - Right Output Volume + */ +#define WM8993_HPOUT1_VU 0x0100 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */ +#define WM8993_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */ +#define WM8993_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */ +#define WM8993_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */ +#define WM8993_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */ +#define WM8993_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */ +#define WM8993_HPOUT1R_MUTE_N 0x0040 /* HPOUT1R_MUTE_N */ +#define WM8993_HPOUT1R_MUTE_N_MASK 0x0040 /* HPOUT1R_MUTE_N */ +#define WM8993_HPOUT1R_MUTE_N_SHIFT 6 /* HPOUT1R_MUTE_N */ +#define WM8993_HPOUT1R_MUTE_N_WIDTH 1 /* HPOUT1R_MUTE_N */ +#define WM8993_HPOUT1R_VOL_MASK 0x003F /* HPOUT1R_VOL - [5:0] */ +#define WM8993_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [5:0] */ +#define WM8993_HPOUT1R_VOL_WIDTH 6 /* HPOUT1R_VOL - [5:0] */ + +/* + * R30 (0x1E) - Line Outputs Volume + */ +#define WM8993_LINEOUT1N_MUTE 0x0040 /* LINEOUT1N_MUTE */ +#define WM8993_LINEOUT1N_MUTE_MASK 0x0040 /* LINEOUT1N_MUTE */ +#define WM8993_LINEOUT1N_MUTE_SHIFT 6 /* LINEOUT1N_MUTE */ +#define WM8993_LINEOUT1N_MUTE_WIDTH 1 /* LINEOUT1N_MUTE */ +#define WM8993_LINEOUT1P_MUTE 0x0020 /* LINEOUT1P_MUTE */ +#define WM8993_LINEOUT1P_MUTE_MASK 0x0020 /* LINEOUT1P_MUTE */ +#define WM8993_LINEOUT1P_MUTE_SHIFT 5 /* LINEOUT1P_MUTE */ +#define WM8993_LINEOUT1P_MUTE_WIDTH 1 /* LINEOUT1P_MUTE */ +#define WM8993_LINEOUT1_VOL 0x0010 /* LINEOUT1_VOL */ +#define WM8993_LINEOUT1_VOL_MASK 0x0010 /* LINEOUT1_VOL */ +#define WM8993_LINEOUT1_VOL_SHIFT 4 /* LINEOUT1_VOL */ +#define WM8993_LINEOUT1_VOL_WIDTH 1 /* LINEOUT1_VOL */ +#define WM8993_LINEOUT2N_MUTE 0x0004 /* LINEOUT2N_MUTE */ +#define WM8993_LINEOUT2N_MUTE_MASK 0x0004 /* LINEOUT2N_MUTE */ +#define WM8993_LINEOUT2N_MUTE_SHIFT 2 /* LINEOUT2N_MUTE */ +#define WM8993_LINEOUT2N_MUTE_WIDTH 1 /* LINEOUT2N_MUTE */ +#define WM8993_LINEOUT2P_MUTE 0x0002 /* LINEOUT2P_MUTE */ +#define WM8993_LINEOUT2P_MUTE_MASK 0x0002 /* LINEOUT2P_MUTE */ +#define WM8993_LINEOUT2P_MUTE_SHIFT 1 /* LINEOUT2P_MUTE */ +#define WM8993_LINEOUT2P_MUTE_WIDTH 1 /* LINEOUT2P_MUTE */ +#define WM8993_LINEOUT2_VOL 0x0001 /* LINEOUT2_VOL */ +#define WM8993_LINEOUT2_VOL_MASK 0x0001 /* LINEOUT2_VOL */ +#define WM8993_LINEOUT2_VOL_SHIFT 0 /* LINEOUT2_VOL */ +#define WM8993_LINEOUT2_VOL_WIDTH 1 /* LINEOUT2_VOL */ + +/* + * R31 (0x1F) - HPOUT2 Volume + */ +#define WM8993_HPOUT2_MUTE 0x0020 /* HPOUT2_MUTE */ +#define WM8993_HPOUT2_MUTE_MASK 0x0020 /* HPOUT2_MUTE */ +#define WM8993_HPOUT2_MUTE_SHIFT 5 /* HPOUT2_MUTE */ +#define WM8993_HPOUT2_MUTE_WIDTH 1 /* HPOUT2_MUTE */ +#define WM8993_HPOUT2_VOL 0x0010 /* HPOUT2_VOL */ +#define WM8993_HPOUT2_VOL_MASK 0x0010 /* HPOUT2_VOL */ +#define WM8993_HPOUT2_VOL_SHIFT 4 /* HPOUT2_VOL */ +#define WM8993_HPOUT2_VOL_WIDTH 1 /* HPOUT2_VOL */ + +/* + * R32 (0x20) - Left OPGA Volume + */ +#define WM8993_MIXOUT_VU 0x0100 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_MASK 0x0100 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_SHIFT 8 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_WIDTH 1 /* MIXOUT_VU */ +#define WM8993_MIXOUTL_ZC 0x0080 /* MIXOUTL_ZC */ +#define WM8993_MIXOUTL_ZC_MASK 0x0080 /* MIXOUTL_ZC */ +#define WM8993_MIXOUTL_ZC_SHIFT 7 /* MIXOUTL_ZC */ +#define WM8993_MIXOUTL_ZC_WIDTH 1 /* MIXOUTL_ZC */ +#define WM8993_MIXOUTL_MUTE_N 0x0040 /* MIXOUTL_MUTE_N */ +#define WM8993_MIXOUTL_MUTE_N_MASK 0x0040 /* MIXOUTL_MUTE_N */ +#define WM8993_MIXOUTL_MUTE_N_SHIFT 6 /* MIXOUTL_MUTE_N */ +#define WM8993_MIXOUTL_MUTE_N_WIDTH 1 /* MIXOUTL_MUTE_N */ +#define WM8993_MIXOUTL_VOL_MASK 0x003F /* MIXOUTL_VOL - [5:0] */ +#define WM8993_MIXOUTL_VOL_SHIFT 0 /* MIXOUTL_VOL - [5:0] */ +#define WM8993_MIXOUTL_VOL_WIDTH 6 /* MIXOUTL_VOL - [5:0] */ + +/* + * R33 (0x21) - Right OPGA Volume + */ +#define WM8993_MIXOUT_VU 0x0100 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_MASK 0x0100 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_SHIFT 8 /* MIXOUT_VU */ +#define WM8993_MIXOUT_VU_WIDTH 1 /* MIXOUT_VU */ +#define WM8993_MIXOUTR_ZC 0x0080 /* MIXOUTR_ZC */ +#define WM8993_MIXOUTR_ZC_MASK 0x0080 /* MIXOUTR_ZC */ +#define WM8993_MIXOUTR_ZC_SHIFT 7 /* MIXOUTR_ZC */ +#define WM8993_MIXOUTR_ZC_WIDTH 1 /* MIXOUTR_ZC */ +#define WM8993_MIXOUTR_MUTE_N 0x0040 /* MIXOUTR_MUTE_N */ +#define WM8993_MIXOUTR_MUTE_N_MASK 0x0040 /* MIXOUTR_MUTE_N */ +#define WM8993_MIXOUTR_MUTE_N_SHIFT 6 /* MIXOUTR_MUTE_N */ +#define WM8993_MIXOUTR_MUTE_N_WIDTH 1 /* MIXOUTR_MUTE_N */ +#define WM8993_MIXOUTR_VOL_MASK 0x003F /* MIXOUTR_VOL - [5:0] */ +#define WM8993_MIXOUTR_VOL_SHIFT 0 /* MIXOUTR_VOL - [5:0] */ +#define WM8993_MIXOUTR_VOL_WIDTH 6 /* MIXOUTR_VOL - [5:0] */ + +/* + * R34 (0x22) - SPKMIXL Attenuation + */ +#define WM8993_MIXINL_SPKMIXL_VOL 0x0020 /* MIXINL_SPKMIXL_VOL */ +#define WM8993_MIXINL_SPKMIXL_VOL_MASK 0x0020 /* MIXINL_SPKMIXL_VOL */ +#define WM8993_MIXINL_SPKMIXL_VOL_SHIFT 5 /* MIXINL_SPKMIXL_VOL */ +#define WM8993_MIXINL_SPKMIXL_VOL_WIDTH 1 /* MIXINL_SPKMIXL_VOL */ +#define WM8993_IN1LP_SPKMIXL_VOL 0x0010 /* IN1LP_SPKMIXL_VOL */ +#define WM8993_IN1LP_SPKMIXL_VOL_MASK 0x0010 /* IN1LP_SPKMIXL_VOL */ +#define WM8993_IN1LP_SPKMIXL_VOL_SHIFT 4 /* IN1LP_SPKMIXL_VOL */ +#define WM8993_IN1LP_SPKMIXL_VOL_WIDTH 1 /* IN1LP_SPKMIXL_VOL */ +#define WM8993_MIXOUTL_SPKMIXL_VOL 0x0008 /* MIXOUTL_SPKMIXL_VOL */ +#define WM8993_MIXOUTL_SPKMIXL_VOL_MASK 0x0008 /* MIXOUTL_SPKMIXL_VOL */ +#define WM8993_MIXOUTL_SPKMIXL_VOL_SHIFT 3 /* MIXOUTL_SPKMIXL_VOL */ +#define WM8993_MIXOUTL_SPKMIXL_VOL_WIDTH 1 /* MIXOUTL_SPKMIXL_VOL */ +#define WM8993_DACL_SPKMIXL_VOL 0x0004 /* DACL_SPKMIXL_VOL */ +#define WM8993_DACL_SPKMIXL_VOL_MASK 0x0004 /* DACL_SPKMIXL_VOL */ +#define WM8993_DACL_SPKMIXL_VOL_SHIFT 2 /* DACL_SPKMIXL_VOL */ +#define WM8993_DACL_SPKMIXL_VOL_WIDTH 1 /* DACL_SPKMIXL_VOL */ +#define WM8993_SPKMIXL_VOL_MASK 0x0003 /* SPKMIXL_VOL - [1:0] */ +#define WM8993_SPKMIXL_VOL_SHIFT 0 /* SPKMIXL_VOL - [1:0] */ +#define WM8993_SPKMIXL_VOL_WIDTH 2 /* SPKMIXL_VOL - [1:0] */ + +/* + * R35 (0x23) - SPKMIXR Attenuation + */ +#define WM8993_SPKOUT_CLASSAB_MODE 0x0100 /* SPKOUT_CLASSAB_MODE */ +#define WM8993_SPKOUT_CLASSAB_MODE_MASK 0x0100 /* SPKOUT_CLASSAB_MODE */ +#define WM8993_SPKOUT_CLASSAB_MODE_SHIFT 8 /* SPKOUT_CLASSAB_MODE */ +#define WM8993_SPKOUT_CLASSAB_MODE_WIDTH 1 /* SPKOUT_CLASSAB_MODE */ +#define WM8993_MIXINR_SPKMIXR_VOL 0x0020 /* MIXINR_SPKMIXR_VOL */ +#define WM8993_MIXINR_SPKMIXR_VOL_MASK 0x0020 /* MIXINR_SPKMIXR_VOL */ +#define WM8993_MIXINR_SPKMIXR_VOL_SHIFT 5 /* MIXINR_SPKMIXR_VOL */ +#define WM8993_MIXINR_SPKMIXR_VOL_WIDTH 1 /* MIXINR_SPKMIXR_VOL */ +#define WM8993_IN1RP_SPKMIXR_VOL 0x0010 /* IN1RP_SPKMIXR_VOL */ +#define WM8993_IN1RP_SPKMIXR_VOL_MASK 0x0010 /* IN1RP_SPKMIXR_VOL */ +#define WM8993_IN1RP_SPKMIXR_VOL_SHIFT 4 /* IN1RP_SPKMIXR_VOL */ +#define WM8993_IN1RP_SPKMIXR_VOL_WIDTH 1 /* IN1RP_SPKMIXR_VOL */ +#define WM8993_MIXOUTR_SPKMIXR_VOL 0x0008 /* MIXOUTR_SPKMIXR_VOL */ +#define WM8993_MIXOUTR_SPKMIXR_VOL_MASK 0x0008 /* MIXOUTR_SPKMIXR_VOL */ +#define WM8993_MIXOUTR_SPKMIXR_VOL_SHIFT 3 /* MIXOUTR_SPKMIXR_VOL */ +#define WM8993_MIXOUTR_SPKMIXR_VOL_WIDTH 1 /* MIXOUTR_SPKMIXR_VOL */ +#define WM8993_DACR_SPKMIXR_VOL 0x0004 /* DACR_SPKMIXR_VOL */ +#define WM8993_DACR_SPKMIXR_VOL_MASK 0x0004 /* DACR_SPKMIXR_VOL */ +#define WM8993_DACR_SPKMIXR_VOL_SHIFT 2 /* DACR_SPKMIXR_VOL */ +#define WM8993_DACR_SPKMIXR_VOL_WIDTH 1 /* DACR_SPKMIXR_VOL */ +#define WM8993_SPKMIXR_VOL_MASK 0x0003 /* SPKMIXR_VOL - [1:0] */ +#define WM8993_SPKMIXR_VOL_SHIFT 0 /* SPKMIXR_VOL - [1:0] */ +#define WM8993_SPKMIXR_VOL_WIDTH 2 /* SPKMIXR_VOL - [1:0] */ + +/* + * R36 (0x24) - SPKOUT Mixers + */ +#define WM8993_VRX_TO_SPKOUTL 0x0020 /* VRX_TO_SPKOUTL */ +#define WM8993_VRX_TO_SPKOUTL_MASK 0x0020 /* VRX_TO_SPKOUTL */ +#define WM8993_VRX_TO_SPKOUTL_SHIFT 5 /* VRX_TO_SPKOUTL */ +#define WM8993_VRX_TO_SPKOUTL_WIDTH 1 /* VRX_TO_SPKOUTL */ +#define WM8993_SPKMIXL_TO_SPKOUTL 0x0010 /* SPKMIXL_TO_SPKOUTL */ +#define WM8993_SPKMIXL_TO_SPKOUTL_MASK 0x0010 /* SPKMIXL_TO_SPKOUTL */ +#define WM8993_SPKMIXL_TO_SPKOUTL_SHIFT 4 /* SPKMIXL_TO_SPKOUTL */ +#define WM8993_SPKMIXL_TO_SPKOUTL_WIDTH 1 /* SPKMIXL_TO_SPKOUTL */ +#define WM8993_SPKMIXR_TO_SPKOUTL 0x0008 /* SPKMIXR_TO_SPKOUTL */ +#define WM8993_SPKMIXR_TO_SPKOUTL_MASK 0x0008 /* SPKMIXR_TO_SPKOUTL */ +#define WM8993_SPKMIXR_TO_SPKOUTL_SHIFT 3 /* SPKMIXR_TO_SPKOUTL */ +#define WM8993_SPKMIXR_TO_SPKOUTL_WIDTH 1 /* SPKMIXR_TO_SPKOUTL */ +#define WM8993_VRX_TO_SPKOUTR 0x0004 /* VRX_TO_SPKOUTR */ +#define WM8993_VRX_TO_SPKOUTR_MASK 0x0004 /* VRX_TO_SPKOUTR */ +#define WM8993_VRX_TO_SPKOUTR_SHIFT 2 /* VRX_TO_SPKOUTR */ +#define WM8993_VRX_TO_SPKOUTR_WIDTH 1 /* VRX_TO_SPKOUTR */ +#define WM8993_SPKMIXL_TO_SPKOUTR 0x0002 /* SPKMIXL_TO_SPKOUTR */ +#define WM8993_SPKMIXL_TO_SPKOUTR_MASK 0x0002 /* SPKMIXL_TO_SPKOUTR */ +#define WM8993_SPKMIXL_TO_SPKOUTR_SHIFT 1 /* SPKMIXL_TO_SPKOUTR */ +#define WM8993_SPKMIXL_TO_SPKOUTR_WIDTH 1 /* SPKMIXL_TO_SPKOUTR */ +#define WM8993_SPKMIXR_TO_SPKOUTR 0x0001 /* SPKMIXR_TO_SPKOUTR */ +#define WM8993_SPKMIXR_TO_SPKOUTR_MASK 0x0001 /* SPKMIXR_TO_SPKOUTR */ +#define WM8993_SPKMIXR_TO_SPKOUTR_SHIFT 0 /* SPKMIXR_TO_SPKOUTR */ +#define WM8993_SPKMIXR_TO_SPKOUTR_WIDTH 1 /* SPKMIXR_TO_SPKOUTR */ + +/* + * R37 (0x25) - SPKOUT Boost + */ +#define WM8993_SPKOUTL_BOOST_MASK 0x0038 /* SPKOUTL_BOOST - [5:3] */ +#define WM8993_SPKOUTL_BOOST_SHIFT 3 /* SPKOUTL_BOOST - [5:3] */ +#define WM8993_SPKOUTL_BOOST_WIDTH 3 /* SPKOUTL_BOOST - [5:3] */ +#define WM8993_SPKOUTR_BOOST_MASK 0x0007 /* SPKOUTR_BOOST - [2:0] */ +#define WM8993_SPKOUTR_BOOST_SHIFT 0 /* SPKOUTR_BOOST - [2:0] */ +#define WM8993_SPKOUTR_BOOST_WIDTH 3 /* SPKOUTR_BOOST - [2:0] */ + +/* + * R38 (0x26) - Speaker Volume Left + */ +#define WM8993_SPKOUT_VU 0x0100 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */ +#define WM8993_SPKOUTL_ZC 0x0080 /* SPKOUTL_ZC */ +#define WM8993_SPKOUTL_ZC_MASK 0x0080 /* SPKOUTL_ZC */ +#define WM8993_SPKOUTL_ZC_SHIFT 7 /* SPKOUTL_ZC */ +#define WM8993_SPKOUTL_ZC_WIDTH 1 /* SPKOUTL_ZC */ +#define WM8993_SPKOUTL_MUTE_N 0x0040 /* SPKOUTL_MUTE_N */ +#define WM8993_SPKOUTL_MUTE_N_MASK 0x0040 /* SPKOUTL_MUTE_N */ +#define WM8993_SPKOUTL_MUTE_N_SHIFT 6 /* SPKOUTL_MUTE_N */ +#define WM8993_SPKOUTL_MUTE_N_WIDTH 1 /* SPKOUTL_MUTE_N */ +#define WM8993_SPKOUTL_VOL_MASK 0x003F /* SPKOUTL_VOL - [5:0] */ +#define WM8993_SPKOUTL_VOL_SHIFT 0 /* SPKOUTL_VOL - [5:0] */ +#define WM8993_SPKOUTL_VOL_WIDTH 6 /* SPKOUTL_VOL - [5:0] */ + +/* + * R39 (0x27) - Speaker Volume Right + */ +#define WM8993_SPKOUT_VU 0x0100 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */ +#define WM8993_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */ +#define WM8993_SPKOUTR_ZC 0x0080 /* SPKOUTR_ZC */ +#define WM8993_SPKOUTR_ZC_MASK 0x0080 /* SPKOUTR_ZC */ +#define WM8993_SPKOUTR_ZC_SHIFT 7 /* SPKOUTR_ZC */ +#define WM8993_SPKOUTR_ZC_WIDTH 1 /* SPKOUTR_ZC */ +#define WM8993_SPKOUTR_MUTE_N 0x0040 /* SPKOUTR_MUTE_N */ +#define WM8993_SPKOUTR_MUTE_N_MASK 0x0040 /* SPKOUTR_MUTE_N */ +#define WM8993_SPKOUTR_MUTE_N_SHIFT 6 /* SPKOUTR_MUTE_N */ +#define WM8993_SPKOUTR_MUTE_N_WIDTH 1 /* SPKOUTR_MUTE_N */ +#define WM8993_SPKOUTR_VOL_MASK 0x003F /* SPKOUTR_VOL - [5:0] */ +#define WM8993_SPKOUTR_VOL_SHIFT 0 /* SPKOUTR_VOL - [5:0] */ +#define WM8993_SPKOUTR_VOL_WIDTH 6 /* SPKOUTR_VOL - [5:0] */ + +/* + * R40 (0x28) - Input Mixer2 + */ +#define WM8993_IN2LP_TO_IN2L 0x0080 /* IN2LP_TO_IN2L */ +#define WM8993_IN2LP_TO_IN2L_MASK 0x0080 /* IN2LP_TO_IN2L */ +#define WM8993_IN2LP_TO_IN2L_SHIFT 7 /* IN2LP_TO_IN2L */ +#define WM8993_IN2LP_TO_IN2L_WIDTH 1 /* IN2LP_TO_IN2L */ +#define WM8993_IN2LN_TO_IN2L 0x0040 /* IN2LN_TO_IN2L */ +#define WM8993_IN2LN_TO_IN2L_MASK 0x0040 /* IN2LN_TO_IN2L */ +#define WM8993_IN2LN_TO_IN2L_SHIFT 6 /* IN2LN_TO_IN2L */ +#define WM8993_IN2LN_TO_IN2L_WIDTH 1 /* IN2LN_TO_IN2L */ +#define WM8993_IN1LP_TO_IN1L 0x0020 /* IN1LP_TO_IN1L */ +#define WM8993_IN1LP_TO_IN1L_MASK 0x0020 /* IN1LP_TO_IN1L */ +#define WM8993_IN1LP_TO_IN1L_SHIFT 5 /* IN1LP_TO_IN1L */ +#define WM8993_IN1LP_TO_IN1L_WIDTH 1 /* IN1LP_TO_IN1L */ +#define WM8993_IN1LN_TO_IN1L 0x0010 /* IN1LN_TO_IN1L */ +#define WM8993_IN1LN_TO_IN1L_MASK 0x0010 /* IN1LN_TO_IN1L */ +#define WM8993_IN1LN_TO_IN1L_SHIFT 4 /* IN1LN_TO_IN1L */ +#define WM8993_IN1LN_TO_IN1L_WIDTH 1 /* IN1LN_TO_IN1L */ +#define WM8993_IN2RP_TO_IN2R 0x0008 /* IN2RP_TO_IN2R */ +#define WM8993_IN2RP_TO_IN2R_MASK 0x0008 /* IN2RP_TO_IN2R */ +#define WM8993_IN2RP_TO_IN2R_SHIFT 3 /* IN2RP_TO_IN2R */ +#define WM8993_IN2RP_TO_IN2R_WIDTH 1 /* IN2RP_TO_IN2R */ +#define WM8993_IN2RN_TO_IN2R 0x0004 /* IN2RN_TO_IN2R */ +#define WM8993_IN2RN_TO_IN2R_MASK 0x0004 /* IN2RN_TO_IN2R */ +#define WM8993_IN2RN_TO_IN2R_SHIFT 2 /* IN2RN_TO_IN2R */ +#define WM8993_IN2RN_TO_IN2R_WIDTH 1 /* IN2RN_TO_IN2R */ +#define WM8993_IN1RP_TO_IN1R 0x0002 /* IN1RP_TO_IN1R */ +#define WM8993_IN1RP_TO_IN1R_MASK 0x0002 /* IN1RP_TO_IN1R */ +#define WM8993_IN1RP_TO_IN1R_SHIFT 1 /* IN1RP_TO_IN1R */ +#define WM8993_IN1RP_TO_IN1R_WIDTH 1 /* IN1RP_TO_IN1R */ +#define WM8993_IN1RN_TO_IN1R 0x0001 /* IN1RN_TO_IN1R */ +#define WM8993_IN1RN_TO_IN1R_MASK 0x0001 /* IN1RN_TO_IN1R */ +#define WM8993_IN1RN_TO_IN1R_SHIFT 0 /* IN1RN_TO_IN1R */ +#define WM8993_IN1RN_TO_IN1R_WIDTH 1 /* IN1RN_TO_IN1R */ + +/* + * R41 (0x29) - Input Mixer3 + */ +#define WM8993_IN2L_TO_MIXINL 0x0100 /* IN2L_TO_MIXINL */ +#define WM8993_IN2L_TO_MIXINL_MASK 0x0100 /* IN2L_TO_MIXINL */ +#define WM8993_IN2L_TO_MIXINL_SHIFT 8 /* IN2L_TO_MIXINL */ +#define WM8993_IN2L_TO_MIXINL_WIDTH 1 /* IN2L_TO_MIXINL */ +#define WM8993_IN2L_MIXINL_VOL 0x0080 /* IN2L_MIXINL_VOL */ +#define WM8993_IN2L_MIXINL_VOL_MASK 0x0080 /* IN2L_MIXINL_VOL */ +#define WM8993_IN2L_MIXINL_VOL_SHIFT 7 /* IN2L_MIXINL_VOL */ +#define WM8993_IN2L_MIXINL_VOL_WIDTH 1 /* IN2L_MIXINL_VOL */ +#define WM8993_IN1L_TO_MIXINL 0x0020 /* IN1L_TO_MIXINL */ +#define WM8993_IN1L_TO_MIXINL_MASK 0x0020 /* IN1L_TO_MIXINL */ +#define WM8993_IN1L_TO_MIXINL_SHIFT 5 /* IN1L_TO_MIXINL */ +#define WM8993_IN1L_TO_MIXINL_WIDTH 1 /* IN1L_TO_MIXINL */ +#define WM8993_IN1L_MIXINL_VOL 0x0010 /* IN1L_MIXINL_VOL */ +#define WM8993_IN1L_MIXINL_VOL_MASK 0x0010 /* IN1L_MIXINL_VOL */ +#define WM8993_IN1L_MIXINL_VOL_SHIFT 4 /* IN1L_MIXINL_VOL */ +#define WM8993_IN1L_MIXINL_VOL_WIDTH 1 /* IN1L_MIXINL_VOL */ +#define WM8993_MIXOUTL_MIXINL_VOL_MASK 0x0007 /* MIXOUTL_MIXINL_VOL - [2:0] */ +#define WM8993_MIXOUTL_MIXINL_VOL_SHIFT 0 /* MIXOUTL_MIXINL_VOL - [2:0] */ +#define WM8993_MIXOUTL_MIXINL_VOL_WIDTH 3 /* MIXOUTL_MIXINL_VOL - [2:0] */ + +/* + * R42 (0x2A) - Input Mixer4 + */ +#define WM8993_IN2R_TO_MIXINR 0x0100 /* IN2R_TO_MIXINR */ +#define WM8993_IN2R_TO_MIXINR_MASK 0x0100 /* IN2R_TO_MIXINR */ +#define WM8993_IN2R_TO_MIXINR_SHIFT 8 /* IN2R_TO_MIXINR */ +#define WM8993_IN2R_TO_MIXINR_WIDTH 1 /* IN2R_TO_MIXINR */ +#define WM8993_IN2R_MIXINR_VOL 0x0080 /* IN2R_MIXINR_VOL */ +#define WM8993_IN2R_MIXINR_VOL_MASK 0x0080 /* IN2R_MIXINR_VOL */ +#define WM8993_IN2R_MIXINR_VOL_SHIFT 7 /* IN2R_MIXINR_VOL */ +#define WM8993_IN2R_MIXINR_VOL_WIDTH 1 /* IN2R_MIXINR_VOL */ +#define WM8993_IN1R_TO_MIXINR 0x0020 /* IN1R_TO_MIXINR */ +#define WM8993_IN1R_TO_MIXINR_MASK 0x0020 /* IN1R_TO_MIXINR */ +#define WM8993_IN1R_TO_MIXINR_SHIFT 5 /* IN1R_TO_MIXINR */ +#define WM8993_IN1R_TO_MIXINR_WIDTH 1 /* IN1R_TO_MIXINR */ +#define WM8993_IN1R_MIXINR_VOL 0x0010 /* IN1R_MIXINR_VOL */ +#define WM8993_IN1R_MIXINR_VOL_MASK 0x0010 /* IN1R_MIXINR_VOL */ +#define WM8993_IN1R_MIXINR_VOL_SHIFT 4 /* IN1R_MIXINR_VOL */ +#define WM8993_IN1R_MIXINR_VOL_WIDTH 1 /* IN1R_MIXINR_VOL */ +#define WM8993_MIXOUTR_MIXINR_VOL_MASK 0x0007 /* MIXOUTR_MIXINR_VOL - [2:0] */ +#define WM8993_MIXOUTR_MIXINR_VOL_SHIFT 0 /* MIXOUTR_MIXINR_VOL - [2:0] */ +#define WM8993_MIXOUTR_MIXINR_VOL_WIDTH 3 /* MIXOUTR_MIXINR_VOL - [2:0] */ + +/* + * R43 (0x2B) - Input Mixer5 + */ +#define WM8993_IN1LP_MIXINL_VOL_MASK 0x01C0 /* IN1LP_MIXINL_VOL - [8:6] */ +#define WM8993_IN1LP_MIXINL_VOL_SHIFT 6 /* IN1LP_MIXINL_VOL - [8:6] */ +#define WM8993_IN1LP_MIXINL_VOL_WIDTH 3 /* IN1LP_MIXINL_VOL - [8:6] */ +#define WM8993_VRX_MIXINL_VOL_MASK 0x0007 /* VRX_MIXINL_VOL - [2:0] */ +#define WM8993_VRX_MIXINL_VOL_SHIFT 0 /* VRX_MIXINL_VOL - [2:0] */ +#define WM8993_VRX_MIXINL_VOL_WIDTH 3 /* VRX_MIXINL_VOL - [2:0] */ + +/* + * R44 (0x2C) - Input Mixer6 + */ +#define WM8993_IN1RP_MIXINR_VOL_MASK 0x01C0 /* IN1RP_MIXINR_VOL - [8:6] */ +#define WM8993_IN1RP_MIXINR_VOL_SHIFT 6 /* IN1RP_MIXINR_VOL - [8:6] */ +#define WM8993_IN1RP_MIXINR_VOL_WIDTH 3 /* IN1RP_MIXINR_VOL - [8:6] */ +#define WM8993_VRX_MIXINR_VOL_MASK 0x0007 /* VRX_MIXINR_VOL - [2:0] */ +#define WM8993_VRX_MIXINR_VOL_SHIFT 0 /* VRX_MIXINR_VOL - [2:0] */ +#define WM8993_VRX_MIXINR_VOL_WIDTH 3 /* VRX_MIXINR_VOL - [2:0] */ + +/* + * R45 (0x2D) - Output Mixer1 + */ +#define WM8993_DACL_TO_HPOUT1L 0x0100 /* DACL_TO_HPOUT1L */ +#define WM8993_DACL_TO_HPOUT1L_MASK 0x0100 /* DACL_TO_HPOUT1L */ +#define WM8993_DACL_TO_HPOUT1L_SHIFT 8 /* DACL_TO_HPOUT1L */ +#define WM8993_DACL_TO_HPOUT1L_WIDTH 1 /* DACL_TO_HPOUT1L */ +#define WM8993_MIXINR_TO_MIXOUTL 0x0080 /* MIXINR_TO_MIXOUTL */ +#define WM8993_MIXINR_TO_MIXOUTL_MASK 0x0080 /* MIXINR_TO_MIXOUTL */ +#define WM8993_MIXINR_TO_MIXOUTL_SHIFT 7 /* MIXINR_TO_MIXOUTL */ +#define WM8993_MIXINR_TO_MIXOUTL_WIDTH 1 /* MIXINR_TO_MIXOUTL */ +#define WM8993_MIXINL_TO_MIXOUTL 0x0040 /* MIXINL_TO_MIXOUTL */ +#define WM8993_MIXINL_TO_MIXOUTL_MASK 0x0040 /* MIXINL_TO_MIXOUTL */ +#define WM8993_MIXINL_TO_MIXOUTL_SHIFT 6 /* MIXINL_TO_MIXOUTL */ +#define WM8993_MIXINL_TO_MIXOUTL_WIDTH 1 /* MIXINL_TO_MIXOUTL */ +#define WM8993_IN2RN_TO_MIXOUTL 0x0020 /* IN2RN_TO_MIXOUTL */ +#define WM8993_IN2RN_TO_MIXOUTL_MASK 0x0020 /* IN2RN_TO_MIXOUTL */ +#define WM8993_IN2RN_TO_MIXOUTL_SHIFT 5 /* IN2RN_TO_MIXOUTL */ +#define WM8993_IN2RN_TO_MIXOUTL_WIDTH 1 /* IN2RN_TO_MIXOUTL */ +#define WM8993_IN2LN_TO_MIXOUTL 0x0010 /* IN2LN_TO_MIXOUTL */ +#define WM8993_IN2LN_TO_MIXOUTL_MASK 0x0010 /* IN2LN_TO_MIXOUTL */ +#define WM8993_IN2LN_TO_MIXOUTL_SHIFT 4 /* IN2LN_TO_MIXOUTL */ +#define WM8993_IN2LN_TO_MIXOUTL_WIDTH 1 /* IN2LN_TO_MIXOUTL */ +#define WM8993_IN1R_TO_MIXOUTL 0x0008 /* IN1R_TO_MIXOUTL */ +#define WM8993_IN1R_TO_MIXOUTL_MASK 0x0008 /* IN1R_TO_MIXOUTL */ +#define WM8993_IN1R_TO_MIXOUTL_SHIFT 3 /* IN1R_TO_MIXOUTL */ +#define WM8993_IN1R_TO_MIXOUTL_WIDTH 1 /* IN1R_TO_MIXOUTL */ +#define WM8993_IN1L_TO_MIXOUTL 0x0004 /* IN1L_TO_MIXOUTL */ +#define WM8993_IN1L_TO_MIXOUTL_MASK 0x0004 /* IN1L_TO_MIXOUTL */ +#define WM8993_IN1L_TO_MIXOUTL_SHIFT 2 /* IN1L_TO_MIXOUTL */ +#define WM8993_IN1L_TO_MIXOUTL_WIDTH 1 /* IN1L_TO_MIXOUTL */ +#define WM8993_IN2LP_TO_MIXOUTL 0x0002 /* IN2LP_TO_MIXOUTL */ +#define WM8993_IN2LP_TO_MIXOUTL_MASK 0x0002 /* IN2LP_TO_MIXOUTL */ +#define WM8993_IN2LP_TO_MIXOUTL_SHIFT 1 /* IN2LP_TO_MIXOUTL */ +#define WM8993_IN2LP_TO_MIXOUTL_WIDTH 1 /* IN2LP_TO_MIXOUTL */ +#define WM8993_DACL_TO_MIXOUTL 0x0001 /* DACL_TO_MIXOUTL */ +#define WM8993_DACL_TO_MIXOUTL_MASK 0x0001 /* DACL_TO_MIXOUTL */ +#define WM8993_DACL_TO_MIXOUTL_SHIFT 0 /* DACL_TO_MIXOUTL */ +#define WM8993_DACL_TO_MIXOUTL_WIDTH 1 /* DACL_TO_MIXOUTL */ + +/* + * R46 (0x2E) - Output Mixer2 + */ +#define WM8993_DACR_TO_HPOUT1R 0x0100 /* DACR_TO_HPOUT1R */ +#define WM8993_DACR_TO_HPOUT1R_MASK 0x0100 /* DACR_TO_HPOUT1R */ +#define WM8993_DACR_TO_HPOUT1R_SHIFT 8 /* DACR_TO_HPOUT1R */ +#define WM8993_DACR_TO_HPOUT1R_WIDTH 1 /* DACR_TO_HPOUT1R */ +#define WM8993_MIXINL_TO_MIXOUTR 0x0080 /* MIXINL_TO_MIXOUTR */ +#define WM8993_MIXINL_TO_MIXOUTR_MASK 0x0080 /* MIXINL_TO_MIXOUTR */ +#define WM8993_MIXINL_TO_MIXOUTR_SHIFT 7 /* MIXINL_TO_MIXOUTR */ +#define WM8993_MIXINL_TO_MIXOUTR_WIDTH 1 /* MIXINL_TO_MIXOUTR */ +#define WM8993_MIXINR_TO_MIXOUTR 0x0040 /* MIXINR_TO_MIXOUTR */ +#define WM8993_MIXINR_TO_MIXOUTR_MASK 0x0040 /* MIXINR_TO_MIXOUTR */ +#define WM8993_MIXINR_TO_MIXOUTR_SHIFT 6 /* MIXINR_TO_MIXOUTR */ +#define WM8993_MIXINR_TO_MIXOUTR_WIDTH 1 /* MIXINR_TO_MIXOUTR */ +#define WM8993_IN2LN_TO_MIXOUTR 0x0020 /* IN2LN_TO_MIXOUTR */ +#define WM8993_IN2LN_TO_MIXOUTR_MASK 0x0020 /* IN2LN_TO_MIXOUTR */ +#define WM8993_IN2LN_TO_MIXOUTR_SHIFT 5 /* IN2LN_TO_MIXOUTR */ +#define WM8993_IN2LN_TO_MIXOUTR_WIDTH 1 /* IN2LN_TO_MIXOUTR */ +#define WM8993_IN2RN_TO_MIXOUTR 0x0010 /* IN2RN_TO_MIXOUTR */ +#define WM8993_IN2RN_TO_MIXOUTR_MASK 0x0010 /* IN2RN_TO_MIXOUTR */ +#define WM8993_IN2RN_TO_MIXOUTR_SHIFT 4 /* IN2RN_TO_MIXOUTR */ +#define WM8993_IN2RN_TO_MIXOUTR_WIDTH 1 /* IN2RN_TO_MIXOUTR */ +#define WM8993_IN1L_TO_MIXOUTR 0x0008 /* IN1L_TO_MIXOUTR */ +#define WM8993_IN1L_TO_MIXOUTR_MASK 0x0008 /* IN1L_TO_MIXOUTR */ +#define WM8993_IN1L_TO_MIXOUTR_SHIFT 3 /* IN1L_TO_MIXOUTR */ +#define WM8993_IN1L_TO_MIXOUTR_WIDTH 1 /* IN1L_TO_MIXOUTR */ +#define WM8993_IN1R_TO_MIXOUTR 0x0004 /* IN1R_TO_MIXOUTR */ +#define WM8993_IN1R_TO_MIXOUTR_MASK 0x0004 /* IN1R_TO_MIXOUTR */ +#define WM8993_IN1R_TO_MIXOUTR_SHIFT 2 /* IN1R_TO_MIXOUTR */ +#define WM8993_IN1R_TO_MIXOUTR_WIDTH 1 /* IN1R_TO_MIXOUTR */ +#define WM8993_IN2RP_TO_MIXOUTR 0x0002 /* IN2RP_TO_MIXOUTR */ +#define WM8993_IN2RP_TO_MIXOUTR_MASK 0x0002 /* IN2RP_TO_MIXOUTR */ +#define WM8993_IN2RP_TO_MIXOUTR_SHIFT 1 /* IN2RP_TO_MIXOUTR */ +#define WM8993_IN2RP_TO_MIXOUTR_WIDTH 1 /* IN2RP_TO_MIXOUTR */ +#define WM8993_DACR_TO_MIXOUTR 0x0001 /* DACR_TO_MIXOUTR */ +#define WM8993_DACR_TO_MIXOUTR_MASK 0x0001 /* DACR_TO_MIXOUTR */ +#define WM8993_DACR_TO_MIXOUTR_SHIFT 0 /* DACR_TO_MIXOUTR */ +#define WM8993_DACR_TO_MIXOUTR_WIDTH 1 /* DACR_TO_MIXOUTR */ + +/* + * R47 (0x2F) - Output Mixer3 + */ +#define WM8993_IN2LP_MIXOUTL_VOL_MASK 0x0E00 /* IN2LP_MIXOUTL_VOL - [11:9] */ +#define WM8993_IN2LP_MIXOUTL_VOL_SHIFT 9 /* IN2LP_MIXOUTL_VOL - [11:9] */ +#define WM8993_IN2LP_MIXOUTL_VOL_WIDTH 3 /* IN2LP_MIXOUTL_VOL - [11:9] */ +#define WM8993_IN2LN_MIXOUTL_VOL_MASK 0x01C0 /* IN2LN_MIXOUTL_VOL - [8:6] */ +#define WM8993_IN2LN_MIXOUTL_VOL_SHIFT 6 /* IN2LN_MIXOUTL_VOL - [8:6] */ +#define WM8993_IN2LN_MIXOUTL_VOL_WIDTH 3 /* IN2LN_MIXOUTL_VOL - [8:6] */ +#define WM8993_IN1R_MIXOUTL_VOL_MASK 0x0038 /* IN1R_MIXOUTL_VOL - [5:3] */ +#define WM8993_IN1R_MIXOUTL_VOL_SHIFT 3 /* IN1R_MIXOUTL_VOL - [5:3] */ +#define WM8993_IN1R_MIXOUTL_VOL_WIDTH 3 /* IN1R_MIXOUTL_VOL - [5:3] */ +#define WM8993_IN1L_MIXOUTL_VOL_MASK 0x0007 /* IN1L_MIXOUTL_VOL - [2:0] */ +#define WM8993_IN1L_MIXOUTL_VOL_SHIFT 0 /* IN1L_MIXOUTL_VOL - [2:0] */ +#define WM8993_IN1L_MIXOUTL_VOL_WIDTH 3 /* IN1L_MIXOUTL_VOL - [2:0] */ + +/* + * R48 (0x30) - Output Mixer4 + */ +#define WM8993_IN2RP_MIXOUTR_VOL_MASK 0x0E00 /* IN2RP_MIXOUTR_VOL - [11:9] */ +#define WM8993_IN2RP_MIXOUTR_VOL_SHIFT 9 /* IN2RP_MIXOUTR_VOL - [11:9] */ +#define WM8993_IN2RP_MIXOUTR_VOL_WIDTH 3 /* IN2RP_MIXOUTR_VOL - [11:9] */ +#define WM8993_IN2RN_MIXOUTR_VOL_MASK 0x01C0 /* IN2RN_MIXOUTR_VOL - [8:6] */ +#define WM8993_IN2RN_MIXOUTR_VOL_SHIFT 6 /* IN2RN_MIXOUTR_VOL - [8:6] */ +#define WM8993_IN2RN_MIXOUTR_VOL_WIDTH 3 /* IN2RN_MIXOUTR_VOL - [8:6] */ +#define WM8993_IN1L_MIXOUTR_VOL_MASK 0x0038 /* IN1L_MIXOUTR_VOL - [5:3] */ +#define WM8993_IN1L_MIXOUTR_VOL_SHIFT 3 /* IN1L_MIXOUTR_VOL - [5:3] */ +#define WM8993_IN1L_MIXOUTR_VOL_WIDTH 3 /* IN1L_MIXOUTR_VOL - [5:3] */ +#define WM8993_IN1R_MIXOUTR_VOL_MASK 0x0007 /* IN1R_MIXOUTR_VOL - [2:0] */ +#define WM8993_IN1R_MIXOUTR_VOL_SHIFT 0 /* IN1R_MIXOUTR_VOL - [2:0] */ +#define WM8993_IN1R_MIXOUTR_VOL_WIDTH 3 /* IN1R_MIXOUTR_VOL - [2:0] */ + +/* + * R49 (0x31) - Output Mixer5 + */ +#define WM8993_DACL_MIXOUTL_VOL_MASK 0x0E00 /* DACL_MIXOUTL_VOL - [11:9] */ +#define WM8993_DACL_MIXOUTL_VOL_SHIFT 9 /* DACL_MIXOUTL_VOL - [11:9] */ +#define WM8993_DACL_MIXOUTL_VOL_WIDTH 3 /* DACL_MIXOUTL_VOL - [11:9] */ +#define WM8993_IN2RN_MIXOUTL_VOL_MASK 0x01C0 /* IN2RN_MIXOUTL_VOL - [8:6] */ +#define WM8993_IN2RN_MIXOUTL_VOL_SHIFT 6 /* IN2RN_MIXOUTL_VOL - [8:6] */ +#define WM8993_IN2RN_MIXOUTL_VOL_WIDTH 3 /* IN2RN_MIXOUTL_VOL - [8:6] */ +#define WM8993_MIXINR_MIXOUTL_VOL_MASK 0x0038 /* MIXINR_MIXOUTL_VOL - [5:3] */ +#define WM8993_MIXINR_MIXOUTL_VOL_SHIFT 3 /* MIXINR_MIXOUTL_VOL - [5:3] */ +#define WM8993_MIXINR_MIXOUTL_VOL_WIDTH 3 /* MIXINR_MIXOUTL_VOL - [5:3] */ +#define WM8993_MIXINL_MIXOUTL_VOL_MASK 0x0007 /* MIXINL_MIXOUTL_VOL - [2:0] */ +#define WM8993_MIXINL_MIXOUTL_VOL_SHIFT 0 /* MIXINL_MIXOUTL_VOL - [2:0] */ +#define WM8993_MIXINL_MIXOUTL_VOL_WIDTH 3 /* MIXINL_MIXOUTL_VOL - [2:0] */ + +/* + * R50 (0x32) - Output Mixer6 + */ +#define WM8993_DACR_MIXOUTR_VOL_MASK 0x0E00 /* DACR_MIXOUTR_VOL - [11:9] */ +#define WM8993_DACR_MIXOUTR_VOL_SHIFT 9 /* DACR_MIXOUTR_VOL - [11:9] */ +#define WM8993_DACR_MIXOUTR_VOL_WIDTH 3 /* DACR_MIXOUTR_VOL - [11:9] */ +#define WM8993_IN2LN_MIXOUTR_VOL_MASK 0x01C0 /* IN2LN_MIXOUTR_VOL - [8:6] */ +#define WM8993_IN2LN_MIXOUTR_VOL_SHIFT 6 /* IN2LN_MIXOUTR_VOL - [8:6] */ +#define WM8993_IN2LN_MIXOUTR_VOL_WIDTH 3 /* IN2LN_MIXOUTR_VOL - [8:6] */ +#define WM8993_MIXINL_MIXOUTR_VOL_MASK 0x0038 /* MIXINL_MIXOUTR_VOL - [5:3] */ +#define WM8993_MIXINL_MIXOUTR_VOL_SHIFT 3 /* MIXINL_MIXOUTR_VOL - [5:3] */ +#define WM8993_MIXINL_MIXOUTR_VOL_WIDTH 3 /* MIXINL_MIXOUTR_VOL - [5:3] */ +#define WM8993_MIXINR_MIXOUTR_VOL_MASK 0x0007 /* MIXINR_MIXOUTR_VOL - [2:0] */ +#define WM8993_MIXINR_MIXOUTR_VOL_SHIFT 0 /* MIXINR_MIXOUTR_VOL - [2:0] */ +#define WM8993_MIXINR_MIXOUTR_VOL_WIDTH 3 /* MIXINR_MIXOUTR_VOL - [2:0] */ + +/* + * R51 (0x33) - HPOUT2 Mixer + */ +#define WM8993_VRX_TO_HPOUT2 0x0020 /* VRX_TO_HPOUT2 */ +#define WM8993_VRX_TO_HPOUT2_MASK 0x0020 /* VRX_TO_HPOUT2 */ +#define WM8993_VRX_TO_HPOUT2_SHIFT 5 /* VRX_TO_HPOUT2 */ +#define WM8993_VRX_TO_HPOUT2_WIDTH 1 /* VRX_TO_HPOUT2 */ +#define WM8993_MIXOUTLVOL_TO_HPOUT2 0x0010 /* MIXOUTLVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTLVOL_TO_HPOUT2_MASK 0x0010 /* MIXOUTLVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTLVOL_TO_HPOUT2_SHIFT 4 /* MIXOUTLVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTLVOL_TO_HPOUT2_WIDTH 1 /* MIXOUTLVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTRVOL_TO_HPOUT2 0x0008 /* MIXOUTRVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTRVOL_TO_HPOUT2_MASK 0x0008 /* MIXOUTRVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTRVOL_TO_HPOUT2_SHIFT 3 /* MIXOUTRVOL_TO_HPOUT2 */ +#define WM8993_MIXOUTRVOL_TO_HPOUT2_WIDTH 1 /* MIXOUTRVOL_TO_HPOUT2 */ + +/* + * R52 (0x34) - Line Mixer1 + */ +#define WM8993_MIXOUTL_TO_LINEOUT1N 0x0040 /* MIXOUTL_TO_LINEOUT1N */ +#define WM8993_MIXOUTL_TO_LINEOUT1N_MASK 0x0040 /* MIXOUTL_TO_LINEOUT1N */ +#define WM8993_MIXOUTL_TO_LINEOUT1N_SHIFT 6 /* MIXOUTL_TO_LINEOUT1N */ +#define WM8993_MIXOUTL_TO_LINEOUT1N_WIDTH 1 /* MIXOUTL_TO_LINEOUT1N */ +#define WM8993_MIXOUTR_TO_LINEOUT1N 0x0020 /* MIXOUTR_TO_LINEOUT1N */ +#define WM8993_MIXOUTR_TO_LINEOUT1N_MASK 0x0020 /* MIXOUTR_TO_LINEOUT1N */ +#define WM8993_MIXOUTR_TO_LINEOUT1N_SHIFT 5 /* MIXOUTR_TO_LINEOUT1N */ +#define WM8993_MIXOUTR_TO_LINEOUT1N_WIDTH 1 /* MIXOUTR_TO_LINEOUT1N */ +#define WM8993_LINEOUT1_MODE 0x0010 /* LINEOUT1_MODE */ +#define WM8993_LINEOUT1_MODE_MASK 0x0010 /* LINEOUT1_MODE */ +#define WM8993_LINEOUT1_MODE_SHIFT 4 /* LINEOUT1_MODE */ +#define WM8993_LINEOUT1_MODE_WIDTH 1 /* LINEOUT1_MODE */ +#define WM8993_IN1R_TO_LINEOUT1P 0x0004 /* IN1R_TO_LINEOUT1P */ +#define WM8993_IN1R_TO_LINEOUT1P_MASK 0x0004 /* IN1R_TO_LINEOUT1P */ +#define WM8993_IN1R_TO_LINEOUT1P_SHIFT 2 /* IN1R_TO_LINEOUT1P */ +#define WM8993_IN1R_TO_LINEOUT1P_WIDTH 1 /* IN1R_TO_LINEOUT1P */ +#define WM8993_IN1L_TO_LINEOUT1P 0x0002 /* IN1L_TO_LINEOUT1P */ +#define WM8993_IN1L_TO_LINEOUT1P_MASK 0x0002 /* IN1L_TO_LINEOUT1P */ +#define WM8993_IN1L_TO_LINEOUT1P_SHIFT 1 /* IN1L_TO_LINEOUT1P */ +#define WM8993_IN1L_TO_LINEOUT1P_WIDTH 1 /* IN1L_TO_LINEOUT1P */ +#define WM8993_MIXOUTL_TO_LINEOUT1P 0x0001 /* MIXOUTL_TO_LINEOUT1P */ +#define WM8993_MIXOUTL_TO_LINEOUT1P_MASK 0x0001 /* MIXOUTL_TO_LINEOUT1P */ +#define WM8993_MIXOUTL_TO_LINEOUT1P_SHIFT 0 /* MIXOUTL_TO_LINEOUT1P */ +#define WM8993_MIXOUTL_TO_LINEOUT1P_WIDTH 1 /* MIXOUTL_TO_LINEOUT1P */ + +/* + * R53 (0x35) - Line Mixer2 + */ +#define WM8993_MIXOUTR_TO_LINEOUT2N 0x0040 /* MIXOUTR_TO_LINEOUT2N */ +#define WM8993_MIXOUTR_TO_LINEOUT2N_MASK 0x0040 /* MIXOUTR_TO_LINEOUT2N */ +#define WM8993_MIXOUTR_TO_LINEOUT2N_SHIFT 6 /* MIXOUTR_TO_LINEOUT2N */ +#define WM8993_MIXOUTR_TO_LINEOUT2N_WIDTH 1 /* MIXOUTR_TO_LINEOUT2N */ +#define WM8993_MIXOUTL_TO_LINEOUT2N 0x0020 /* MIXOUTL_TO_LINEOUT2N */ +#define WM8993_MIXOUTL_TO_LINEOUT2N_MASK 0x0020 /* MIXOUTL_TO_LINEOUT2N */ +#define WM8993_MIXOUTL_TO_LINEOUT2N_SHIFT 5 /* MIXOUTL_TO_LINEOUT2N */ +#define WM8993_MIXOUTL_TO_LINEOUT2N_WIDTH 1 /* MIXOUTL_TO_LINEOUT2N */ +#define WM8993_LINEOUT2_MODE 0x0010 /* LINEOUT2_MODE */ +#define WM8993_LINEOUT2_MODE_MASK 0x0010 /* LINEOUT2_MODE */ +#define WM8993_LINEOUT2_MODE_SHIFT 4 /* LINEOUT2_MODE */ +#define WM8993_LINEOUT2_MODE_WIDTH 1 /* LINEOUT2_MODE */ +#define WM8993_IN1L_TO_LINEOUT2P 0x0004 /* IN1L_TO_LINEOUT2P */ +#define WM8993_IN1L_TO_LINEOUT2P_MASK 0x0004 /* IN1L_TO_LINEOUT2P */ +#define WM8993_IN1L_TO_LINEOUT2P_SHIFT 2 /* IN1L_TO_LINEOUT2P */ +#define WM8993_IN1L_TO_LINEOUT2P_WIDTH 1 /* IN1L_TO_LINEOUT2P */ +#define WM8993_IN1R_TO_LINEOUT2P 0x0002 /* IN1R_TO_LINEOUT2P */ +#define WM8993_IN1R_TO_LINEOUT2P_MASK 0x0002 /* IN1R_TO_LINEOUT2P */ +#define WM8993_IN1R_TO_LINEOUT2P_SHIFT 1 /* IN1R_TO_LINEOUT2P */ +#define WM8993_IN1R_TO_LINEOUT2P_WIDTH 1 /* IN1R_TO_LINEOUT2P */ +#define WM8993_MIXOUTR_TO_LINEOUT2P 0x0001 /* MIXOUTR_TO_LINEOUT2P */ +#define WM8993_MIXOUTR_TO_LINEOUT2P_MASK 0x0001 /* MIXOUTR_TO_LINEOUT2P */ +#define WM8993_MIXOUTR_TO_LINEOUT2P_SHIFT 0 /* MIXOUTR_TO_LINEOUT2P */ +#define WM8993_MIXOUTR_TO_LINEOUT2P_WIDTH 1 /* MIXOUTR_TO_LINEOUT2P */ + +/* + * R54 (0x36) - Speaker Mixer + */ +#define WM8993_SPKAB_REF_SEL 0x0100 /* SPKAB_REF_SEL */ +#define WM8993_SPKAB_REF_SEL_MASK 0x0100 /* SPKAB_REF_SEL */ +#define WM8993_SPKAB_REF_SEL_SHIFT 8 /* SPKAB_REF_SEL */ +#define WM8993_SPKAB_REF_SEL_WIDTH 1 /* SPKAB_REF_SEL */ +#define WM8993_MIXINL_TO_SPKMIXL 0x0080 /* MIXINL_TO_SPKMIXL */ +#define WM8993_MIXINL_TO_SPKMIXL_MASK 0x0080 /* MIXINL_TO_SPKMIXL */ +#define WM8993_MIXINL_TO_SPKMIXL_SHIFT 7 /* MIXINL_TO_SPKMIXL */ +#define WM8993_MIXINL_TO_SPKMIXL_WIDTH 1 /* MIXINL_TO_SPKMIXL */ +#define WM8993_MIXINR_TO_SPKMIXR 0x0040 /* MIXINR_TO_SPKMIXR */ +#define WM8993_MIXINR_TO_SPKMIXR_MASK 0x0040 /* MIXINR_TO_SPKMIXR */ +#define WM8993_MIXINR_TO_SPKMIXR_SHIFT 6 /* MIXINR_TO_SPKMIXR */ +#define WM8993_MIXINR_TO_SPKMIXR_WIDTH 1 /* MIXINR_TO_SPKMIXR */ +#define WM8993_IN1LP_TO_SPKMIXL 0x0020 /* IN1LP_TO_SPKMIXL */ +#define WM8993_IN1LP_TO_SPKMIXL_MASK 0x0020 /* IN1LP_TO_SPKMIXL */ +#define WM8993_IN1LP_TO_SPKMIXL_SHIFT 5 /* IN1LP_TO_SPKMIXL */ +#define WM8993_IN1LP_TO_SPKMIXL_WIDTH 1 /* IN1LP_TO_SPKMIXL */ +#define WM8993_IN1RP_TO_SPKMIXR 0x0010 /* IN1RP_TO_SPKMIXR */ +#define WM8993_IN1RP_TO_SPKMIXR_MASK 0x0010 /* IN1RP_TO_SPKMIXR */ +#define WM8993_IN1RP_TO_SPKMIXR_SHIFT 4 /* IN1RP_TO_SPKMIXR */ +#define WM8993_IN1RP_TO_SPKMIXR_WIDTH 1 /* IN1RP_TO_SPKMIXR */ +#define WM8993_MIXOUTL_TO_SPKMIXL 0x0008 /* MIXOUTL_TO_SPKMIXL */ +#define WM8993_MIXOUTL_TO_SPKMIXL_MASK 0x0008 /* MIXOUTL_TO_SPKMIXL */ +#define WM8993_MIXOUTL_TO_SPKMIXL_SHIFT 3 /* MIXOUTL_TO_SPKMIXL */ +#define WM8993_MIXOUTL_TO_SPKMIXL_WIDTH 1 /* MIXOUTL_TO_SPKMIXL */ +#define WM8993_MIXOUTR_TO_SPKMIXR 0x0004 /* MIXOUTR_TO_SPKMIXR */ +#define WM8993_MIXOUTR_TO_SPKMIXR_MASK 0x0004 /* MIXOUTR_TO_SPKMIXR */ +#define WM8993_MIXOUTR_TO_SPKMIXR_SHIFT 2 /* MIXOUTR_TO_SPKMIXR */ +#define WM8993_MIXOUTR_TO_SPKMIXR_WIDTH 1 /* MIXOUTR_TO_SPKMIXR */ +#define WM8993_DACL_TO_SPKMIXL 0x0002 /* DACL_TO_SPKMIXL */ +#define WM8993_DACL_TO_SPKMIXL_MASK 0x0002 /* DACL_TO_SPKMIXL */ +#define WM8993_DACL_TO_SPKMIXL_SHIFT 1 /* DACL_TO_SPKMIXL */ +#define WM8993_DACL_TO_SPKMIXL_WIDTH 1 /* DACL_TO_SPKMIXL */ +#define WM8993_DACR_TO_SPKMIXR 0x0001 /* DACR_TO_SPKMIXR */ +#define WM8993_DACR_TO_SPKMIXR_MASK 0x0001 /* DACR_TO_SPKMIXR */ +#define WM8993_DACR_TO_SPKMIXR_SHIFT 0 /* DACR_TO_SPKMIXR */ +#define WM8993_DACR_TO_SPKMIXR_WIDTH 1 /* DACR_TO_SPKMIXR */ + +/* + * R55 (0x37) - Additional Control + */ +#define WM8993_LINEOUT1_FB 0x0080 /* LINEOUT1_FB */ +#define WM8993_LINEOUT1_FB_MASK 0x0080 /* LINEOUT1_FB */ +#define WM8993_LINEOUT1_FB_SHIFT 7 /* LINEOUT1_FB */ +#define WM8993_LINEOUT1_FB_WIDTH 1 /* LINEOUT1_FB */ +#define WM8993_LINEOUT2_FB 0x0040 /* LINEOUT2_FB */ +#define WM8993_LINEOUT2_FB_MASK 0x0040 /* LINEOUT2_FB */ +#define WM8993_LINEOUT2_FB_SHIFT 6 /* LINEOUT2_FB */ +#define WM8993_LINEOUT2_FB_WIDTH 1 /* LINEOUT2_FB */ +#define WM8993_VROI 0x0001 /* VROI */ +#define WM8993_VROI_MASK 0x0001 /* VROI */ +#define WM8993_VROI_SHIFT 0 /* VROI */ +#define WM8993_VROI_WIDTH 1 /* VROI */ + +/* + * R56 (0x38) - AntiPOP1 + */ +#define WM8993_LINEOUT_VMID_BUF_ENA 0x0080 /* LINEOUT_VMID_BUF_ENA */ +#define WM8993_LINEOUT_VMID_BUF_ENA_MASK 0x0080 /* LINEOUT_VMID_BUF_ENA */ +#define WM8993_LINEOUT_VMID_BUF_ENA_SHIFT 7 /* LINEOUT_VMID_BUF_ENA */ +#define WM8993_LINEOUT_VMID_BUF_ENA_WIDTH 1 /* LINEOUT_VMID_BUF_ENA */ +#define WM8993_HPOUT2_IN_ENA 0x0040 /* HPOUT2_IN_ENA */ +#define WM8993_HPOUT2_IN_ENA_MASK 0x0040 /* HPOUT2_IN_ENA */ +#define WM8993_HPOUT2_IN_ENA_SHIFT 6 /* HPOUT2_IN_ENA */ +#define WM8993_HPOUT2_IN_ENA_WIDTH 1 /* HPOUT2_IN_ENA */ +#define WM8993_LINEOUT1_DISCH 0x0020 /* LINEOUT1_DISCH */ +#define WM8993_LINEOUT1_DISCH_MASK 0x0020 /* LINEOUT1_DISCH */ +#define WM8993_LINEOUT1_DISCH_SHIFT 5 /* LINEOUT1_DISCH */ +#define WM8993_LINEOUT1_DISCH_WIDTH 1 /* LINEOUT1_DISCH */ +#define WM8993_LINEOUT2_DISCH 0x0010 /* LINEOUT2_DISCH */ +#define WM8993_LINEOUT2_DISCH_MASK 0x0010 /* LINEOUT2_DISCH */ +#define WM8993_LINEOUT2_DISCH_SHIFT 4 /* LINEOUT2_DISCH */ +#define WM8993_LINEOUT2_DISCH_WIDTH 1 /* LINEOUT2_DISCH */ + +/* + * R57 (0x39) - AntiPOP2 + */ +#define WM8993_VMID_RAMP_MASK 0x0060 /* VMID_RAMP - [6:5] */ +#define WM8993_VMID_RAMP_SHIFT 5 /* VMID_RAMP - [6:5] */ +#define WM8993_VMID_RAMP_WIDTH 2 /* VMID_RAMP - [6:5] */ +#define WM8993_VMID_BUF_ENA 0x0008 /* VMID_BUF_ENA */ +#define WM8993_VMID_BUF_ENA_MASK 0x0008 /* VMID_BUF_ENA */ +#define WM8993_VMID_BUF_ENA_SHIFT 3 /* VMID_BUF_ENA */ +#define WM8993_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */ +#define WM8993_STARTUP_BIAS_ENA 0x0004 /* STARTUP_BIAS_ENA */ +#define WM8993_STARTUP_BIAS_ENA_MASK 0x0004 /* STARTUP_BIAS_ENA */ +#define WM8993_STARTUP_BIAS_ENA_SHIFT 2 /* STARTUP_BIAS_ENA */ +#define WM8993_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */ +#define WM8993_BIAS_SRC 0x0002 /* BIAS_SRC */ +#define WM8993_BIAS_SRC_MASK 0x0002 /* BIAS_SRC */ +#define WM8993_BIAS_SRC_SHIFT 1 /* BIAS_SRC */ +#define WM8993_BIAS_SRC_WIDTH 1 /* BIAS_SRC */ +#define WM8993_VMID_DISCH 0x0001 /* VMID_DISCH */ +#define WM8993_VMID_DISCH_MASK 0x0001 /* VMID_DISCH */ +#define WM8993_VMID_DISCH_SHIFT 0 /* VMID_DISCH */ +#define WM8993_VMID_DISCH_WIDTH 1 /* VMID_DISCH */ + +/* + * R58 (0x3A) - MICBIAS + */ +#define WM8993_JD_SCTHR_MASK 0x00C0 /* JD_SCTHR - [7:6] */ +#define WM8993_JD_SCTHR_SHIFT 6 /* JD_SCTHR - [7:6] */ +#define WM8993_JD_SCTHR_WIDTH 2 /* JD_SCTHR - [7:6] */ +#define WM8993_JD_THR_MASK 0x0030 /* JD_THR - [5:4] */ +#define WM8993_JD_THR_SHIFT 4 /* JD_THR - [5:4] */ +#define WM8993_JD_THR_WIDTH 2 /* JD_THR - [5:4] */ +#define WM8993_JD_ENA 0x0004 /* JD_ENA */ +#define WM8993_JD_ENA_MASK 0x0004 /* JD_ENA */ +#define WM8993_JD_ENA_SHIFT 2 /* JD_ENA */ +#define WM8993_JD_ENA_WIDTH 1 /* JD_ENA */ +#define WM8993_MICB2_LVL 0x0002 /* MICB2_LVL */ +#define WM8993_MICB2_LVL_MASK 0x0002 /* MICB2_LVL */ +#define WM8993_MICB2_LVL_SHIFT 1 /* MICB2_LVL */ +#define WM8993_MICB2_LVL_WIDTH 1 /* MICB2_LVL */ +#define WM8993_MICB1_LVL 0x0001 /* MICB1_LVL */ +#define WM8993_MICB1_LVL_MASK 0x0001 /* MICB1_LVL */ +#define WM8993_MICB1_LVL_SHIFT 0 /* MICB1_LVL */ +#define WM8993_MICB1_LVL_WIDTH 1 /* MICB1_LVL */ + +/* + * R60 (0x3C) - FLL Control 1 + */ +#define WM8993_FLL_FRAC 0x0004 /* FLL_FRAC */ +#define WM8993_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */ +#define WM8993_FLL_FRAC_SHIFT 2 /* FLL_FRAC */ +#define WM8993_FLL_FRAC_WIDTH 1 /* FLL_FRAC */ +#define WM8993_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */ +#define WM8993_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */ +#define WM8993_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */ +#define WM8993_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */ +#define WM8993_FLL_ENA 0x0001 /* FLL_ENA */ +#define WM8993_FLL_ENA_MASK 0x0001 /* FLL_ENA */ +#define WM8993_FLL_ENA_SHIFT 0 /* FLL_ENA */ +#define WM8993_FLL_ENA_WIDTH 1 /* FLL_ENA */ + +/* + * R61 (0x3D) - FLL Control 2 + */ +#define WM8993_FLL_OUTDIV_MASK 0x0700 /* FLL_OUTDIV - [10:8] */ +#define WM8993_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [10:8] */ +#define WM8993_FLL_OUTDIV_WIDTH 3 /* FLL_OUTDIV - [10:8] */ +#define WM8993_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */ +#define WM8993_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */ +#define WM8993_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */ +#define WM8993_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */ +#define WM8993_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */ +#define WM8993_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */ + +/* + * R62 (0x3E) - FLL Control 3 + */ +#define WM8993_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */ +#define WM8993_FLL_K_SHIFT 0 /* FLL_K - [15:0] */ +#define WM8993_FLL_K_WIDTH 16 /* FLL_K - [15:0] */ + +/* + * R63 (0x3F) - FLL Control 4 + */ +#define WM8993_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */ +#define WM8993_FLL_N_SHIFT 5 /* FLL_N - [14:5] */ +#define WM8993_FLL_N_WIDTH 10 /* FLL_N - [14:5] */ +#define WM8993_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */ +#define WM8993_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */ +#define WM8993_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */ + +/* + * R64 (0x40) - FLL Control 5 + */ +#define WM8993_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */ +#define WM8993_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */ +#define WM8993_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */ +#define WM8993_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */ +#define WM8993_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */ +#define WM8993_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */ +#define WM8993_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */ +#define WM8993_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM8993_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM8993_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM8993_FLL_CLK_SRC_MASK 0x0003 /* FLL_CLK_SRC - [1:0] */ +#define WM8993_FLL_CLK_SRC_SHIFT 0 /* FLL_CLK_SRC - [1:0] */ +#define WM8993_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [1:0] */ + +/* + * R65 (0x41) - Clocking 3 + */ +#define WM8993_CLK_DCS_DIV_MASK 0x3C00 /* CLK_DCS_DIV - [13:10] */ +#define WM8993_CLK_DCS_DIV_SHIFT 10 /* CLK_DCS_DIV - [13:10] */ +#define WM8993_CLK_DCS_DIV_WIDTH 4 /* CLK_DCS_DIV - [13:10] */ +#define WM8993_SAMPLE_RATE_MASK 0x0380 /* SAMPLE_RATE - [9:7] */ +#define WM8993_SAMPLE_RATE_SHIFT 7 /* SAMPLE_RATE - [9:7] */ +#define WM8993_SAMPLE_RATE_WIDTH 3 /* SAMPLE_RATE - [9:7] */ +#define WM8993_CLK_SYS_RATE_MASK 0x001E /* CLK_SYS_RATE - [4:1] */ +#define WM8993_CLK_SYS_RATE_SHIFT 1 /* CLK_SYS_RATE - [4:1] */ +#define WM8993_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [4:1] */ +#define WM8993_CLK_DSP_ENA 0x0001 /* CLK_DSP_ENA */ +#define WM8993_CLK_DSP_ENA_MASK 0x0001 /* CLK_DSP_ENA */ +#define WM8993_CLK_DSP_ENA_SHIFT 0 /* CLK_DSP_ENA */ +#define WM8993_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */ + +/* + * R66 (0x42) - Clocking 4 + */ +#define WM8993_DAC_DIV4 0x0200 /* DAC_DIV4 */ +#define WM8993_DAC_DIV4_MASK 0x0200 /* DAC_DIV4 */ +#define WM8993_DAC_DIV4_SHIFT 9 /* DAC_DIV4 */ +#define WM8993_DAC_DIV4_WIDTH 1 /* DAC_DIV4 */ +#define WM8993_CLK_256K_DIV_MASK 0x007E /* CLK_256K_DIV - [6:1] */ +#define WM8993_CLK_256K_DIV_SHIFT 1 /* CLK_256K_DIV - [6:1] */ +#define WM8993_CLK_256K_DIV_WIDTH 6 /* CLK_256K_DIV - [6:1] */ +#define WM8993_SR_MODE 0x0001 /* SR_MODE */ +#define WM8993_SR_MODE_MASK 0x0001 /* SR_MODE */ +#define WM8993_SR_MODE_SHIFT 0 /* SR_MODE */ +#define WM8993_SR_MODE_WIDTH 1 /* SR_MODE */ + +/* + * R67 (0x43) - MW Slave Control + */ +#define WM8993_MASK_WRITE_ENA 0x0001 /* MASK_WRITE_ENA */ +#define WM8993_MASK_WRITE_ENA_MASK 0x0001 /* MASK_WRITE_ENA */ +#define WM8993_MASK_WRITE_ENA_SHIFT 0 /* MASK_WRITE_ENA */ +#define WM8993_MASK_WRITE_ENA_WIDTH 1 /* MASK_WRITE_ENA */ + +/* + * R69 (0x45) - Bus Control 1 + */ +#define WM8993_CLK_SYS_ENA 0x0002 /* CLK_SYS_ENA */ +#define WM8993_CLK_SYS_ENA_MASK 0x0002 /* CLK_SYS_ENA */ +#define WM8993_CLK_SYS_ENA_SHIFT 1 /* CLK_SYS_ENA */ +#define WM8993_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */ + +/* + * R70 (0x46) - Write Sequencer 0 + */ +#define WM8993_WSEQ_ENA 0x0100 /* WSEQ_ENA */ +#define WM8993_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */ +#define WM8993_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */ +#define WM8993_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM8993_WSEQ_WRITE_INDEX_MASK 0x001F /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8993_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8993_WSEQ_WRITE_INDEX_WIDTH 5 /* WSEQ_WRITE_INDEX - [4:0] */ + +/* + * R71 (0x47) - Write Sequencer 1 + */ +#define WM8993_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8993_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8993_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8993_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */ +#define WM8993_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */ +#define WM8993_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */ +#define WM8993_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */ +#define WM8993_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */ +#define WM8993_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */ + +/* + * R72 (0x48) - Write Sequencer 2 + */ +#define WM8993_WSEQ_EOS 0x4000 /* WSEQ_EOS */ +#define WM8993_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */ +#define WM8993_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */ +#define WM8993_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */ +#define WM8993_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */ +#define WM8993_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */ +#define WM8993_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */ +#define WM8993_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */ +#define WM8993_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */ +#define WM8993_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */ + +/* + * R73 (0x49) - Write Sequencer 3 + */ +#define WM8993_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */ +#define WM8993_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */ +#define WM8993_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */ +#define WM8993_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM8993_WSEQ_START 0x0100 /* WSEQ_START */ +#define WM8993_WSEQ_START_MASK 0x0100 /* WSEQ_START */ +#define WM8993_WSEQ_START_SHIFT 8 /* WSEQ_START */ +#define WM8993_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM8993_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */ +#define WM8993_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */ +#define WM8993_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */ + +/* + * R74 (0x4A) - Write Sequencer 4 + */ +#define WM8993_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */ +#define WM8993_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */ +#define WM8993_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */ +#define WM8993_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ + +/* + * R75 (0x4B) - Write Sequencer 5 + */ +#define WM8993_WSEQ_CURRENT_INDEX_MASK 0x003F /* WSEQ_CURRENT_INDEX - [5:0] */ +#define WM8993_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [5:0] */ +#define WM8993_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [5:0] */ + +/* + * R76 (0x4C) - Charge Pump 1 + */ +#define WM8993_CP_ENA 0x8000 /* CP_ENA */ +#define WM8993_CP_ENA_MASK 0x8000 /* CP_ENA */ +#define WM8993_CP_ENA_SHIFT 15 /* CP_ENA */ +#define WM8993_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R81 (0x51) - Class W 0 + */ +#define WM8993_CP_DYN_FREQ 0x0002 /* CP_DYN_FREQ */ +#define WM8993_CP_DYN_FREQ_MASK 0x0002 /* CP_DYN_FREQ */ +#define WM8993_CP_DYN_FREQ_SHIFT 1 /* CP_DYN_FREQ */ +#define WM8993_CP_DYN_FREQ_WIDTH 1 /* CP_DYN_FREQ */ +#define WM8993_CP_DYN_V 0x0001 /* CP_DYN_V */ +#define WM8993_CP_DYN_V_MASK 0x0001 /* CP_DYN_V */ +#define WM8993_CP_DYN_V_SHIFT 0 /* CP_DYN_V */ +#define WM8993_CP_DYN_V_WIDTH 1 /* CP_DYN_V */ + +/* + * R84 (0x54) - DC Servo 0 + */ +#define WM8993_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8993_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8993_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */ +#define WM8993_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */ +#define WM8993_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8993_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8993_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */ +#define WM8993_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */ +#define WM8993_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8993_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8993_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */ +#define WM8993_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */ +#define WM8993_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8993_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8993_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */ +#define WM8993_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */ +#define WM8993_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8993_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8993_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */ +#define WM8993_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */ +#define WM8993_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8993_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8993_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */ +#define WM8993_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */ +#define WM8993_DCS_TRIG_DAC_WR_1 0x0008 /* DCS_TRIG_DAC_WR_1 */ +#define WM8993_DCS_TRIG_DAC_WR_1_MASK 0x0008 /* DCS_TRIG_DAC_WR_1 */ +#define WM8993_DCS_TRIG_DAC_WR_1_SHIFT 3 /* DCS_TRIG_DAC_WR_1 */ +#define WM8993_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM8993_DCS_TRIG_DAC_WR_0 0x0004 /* DCS_TRIG_DAC_WR_0 */ +#define WM8993_DCS_TRIG_DAC_WR_0_MASK 0x0004 /* DCS_TRIG_DAC_WR_0 */ +#define WM8993_DCS_TRIG_DAC_WR_0_SHIFT 2 /* DCS_TRIG_DAC_WR_0 */ +#define WM8993_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */ +#define WM8993_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8993_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8993_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */ +#define WM8993_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */ +#define WM8993_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8993_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8993_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */ +#define WM8993_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */ + +/* + * R85 (0x55) - DC Servo 1 + */ +#define WM8993_DCS_SERIES_NO_01_MASK 0x0FE0 /* DCS_SERIES_NO_01 - [11:5] */ +#define WM8993_DCS_SERIES_NO_01_SHIFT 5 /* DCS_SERIES_NO_01 - [11:5] */ +#define WM8993_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [11:5] */ +#define WM8993_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8993_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8993_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */ + +/* + * R87 (0x57) - DC Servo 3 + */ +#define WM8993_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8993_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8993_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8993_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8993_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8993_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */ + +/* + * R88 (0x58) - DC Servo Readback 0 + */ +#define WM8993_DCS_DATAPATH_BUSY 0x4000 /* DCS_DATAPATH_BUSY */ +#define WM8993_DCS_DATAPATH_BUSY_MASK 0x4000 /* DCS_DATAPATH_BUSY */ +#define WM8993_DCS_DATAPATH_BUSY_SHIFT 14 /* DCS_DATAPATH_BUSY */ +#define WM8993_DCS_DATAPATH_BUSY_WIDTH 1 /* DCS_DATAPATH_BUSY */ +#define WM8993_DCS_CHANNEL_MASK 0x3000 /* DCS_CHANNEL - [13:12] */ +#define WM8993_DCS_CHANNEL_SHIFT 12 /* DCS_CHANNEL - [13:12] */ +#define WM8993_DCS_CHANNEL_WIDTH 2 /* DCS_CHANNEL - [13:12] */ +#define WM8993_DCS_CAL_COMPLETE_MASK 0x0300 /* DCS_CAL_COMPLETE - [9:8] */ +#define WM8993_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [9:8] */ +#define WM8993_DCS_CAL_COMPLETE_WIDTH 2 /* DCS_CAL_COMPLETE - [9:8] */ +#define WM8993_DCS_DAC_WR_COMPLETE_MASK 0x0030 /* DCS_DAC_WR_COMPLETE - [5:4] */ +#define WM8993_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [5:4] */ +#define WM8993_DCS_DAC_WR_COMPLETE_WIDTH 2 /* DCS_DAC_WR_COMPLETE - [5:4] */ +#define WM8993_DCS_STARTUP_COMPLETE_MASK 0x0003 /* DCS_STARTUP_COMPLETE - [1:0] */ +#define WM8993_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [1:0] */ +#define WM8993_DCS_STARTUP_COMPLETE_WIDTH 2 /* DCS_STARTUP_COMPLETE - [1:0] */ + +/* + * R89 (0x59) - DC Servo Readback 1 + */ +#define WM8993_DCS_INTEG_CHAN_1_MASK 0x00FF /* DCS_INTEG_CHAN_1 - [7:0] */ +#define WM8993_DCS_INTEG_CHAN_1_SHIFT 0 /* DCS_INTEG_CHAN_1 - [7:0] */ +#define WM8993_DCS_INTEG_CHAN_1_WIDTH 8 /* DCS_INTEG_CHAN_1 - [7:0] */ + +/* + * R90 (0x5A) - DC Servo Readback 2 + */ +#define WM8993_DCS_INTEG_CHAN_0_MASK 0x00FF /* DCS_INTEG_CHAN_0 - [7:0] */ +#define WM8993_DCS_INTEG_CHAN_0_SHIFT 0 /* DCS_INTEG_CHAN_0 - [7:0] */ +#define WM8993_DCS_INTEG_CHAN_0_WIDTH 8 /* DCS_INTEG_CHAN_0 - [7:0] */ + +/* + * R96 (0x60) - Analogue HP 0 + */ +#define WM8993_HPOUT1_AUTO_PU 0x0100 /* HPOUT1_AUTO_PU */ +#define WM8993_HPOUT1_AUTO_PU_MASK 0x0100 /* HPOUT1_AUTO_PU */ +#define WM8993_HPOUT1_AUTO_PU_SHIFT 8 /* HPOUT1_AUTO_PU */ +#define WM8993_HPOUT1_AUTO_PU_WIDTH 1 /* HPOUT1_AUTO_PU */ +#define WM8993_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM8993_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM8993_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */ +#define WM8993_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */ +#define WM8993_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */ +#define WM8993_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */ +#define WM8993_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */ +#define WM8993_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */ +#define WM8993_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */ +#define WM8993_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */ +#define WM8993_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */ +#define WM8993_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */ +#define WM8993_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM8993_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM8993_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */ +#define WM8993_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */ +#define WM8993_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */ +#define WM8993_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */ +#define WM8993_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */ +#define WM8993_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */ +#define WM8993_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */ +#define WM8993_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */ +#define WM8993_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */ +#define WM8993_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */ + +/* + * R98 (0x62) - EQ1 + */ +#define WM8993_EQ_ENA 0x0001 /* EQ_ENA */ +#define WM8993_EQ_ENA_MASK 0x0001 /* EQ_ENA */ +#define WM8993_EQ_ENA_SHIFT 0 /* EQ_ENA */ +#define WM8993_EQ_ENA_WIDTH 1 /* EQ_ENA */ + +/* + * R99 (0x63) - EQ2 + */ +#define WM8993_EQ_B1_GAIN_MASK 0x001F /* EQ_B1_GAIN - [4:0] */ +#define WM8993_EQ_B1_GAIN_SHIFT 0 /* EQ_B1_GAIN - [4:0] */ +#define WM8993_EQ_B1_GAIN_WIDTH 5 /* EQ_B1_GAIN - [4:0] */ + +/* + * R100 (0x64) - EQ3 + */ +#define WM8993_EQ_B2_GAIN_MASK 0x001F /* EQ_B2_GAIN - [4:0] */ +#define WM8993_EQ_B2_GAIN_SHIFT 0 /* EQ_B2_GAIN - [4:0] */ +#define WM8993_EQ_B2_GAIN_WIDTH 5 /* EQ_B2_GAIN - [4:0] */ + +/* + * R101 (0x65) - EQ4 + */ +#define WM8993_EQ_B3_GAIN_MASK 0x001F /* EQ_B3_GAIN - [4:0] */ +#define WM8993_EQ_B3_GAIN_SHIFT 0 /* EQ_B3_GAIN - [4:0] */ +#define WM8993_EQ_B3_GAIN_WIDTH 5 /* EQ_B3_GAIN - [4:0] */ + +/* + * R102 (0x66) - EQ5 + */ +#define WM8993_EQ_B4_GAIN_MASK 0x001F /* EQ_B4_GAIN - [4:0] */ +#define WM8993_EQ_B4_GAIN_SHIFT 0 /* EQ_B4_GAIN - [4:0] */ +#define WM8993_EQ_B4_GAIN_WIDTH 5 /* EQ_B4_GAIN - [4:0] */ + +/* + * R103 (0x67) - EQ6 + */ +#define WM8993_EQ_B5_GAIN_MASK 0x001F /* EQ_B5_GAIN - [4:0] */ +#define WM8993_EQ_B5_GAIN_SHIFT 0 /* EQ_B5_GAIN - [4:0] */ +#define WM8993_EQ_B5_GAIN_WIDTH 5 /* EQ_B5_GAIN - [4:0] */ + +/* + * R104 (0x68) - EQ7 + */ +#define WM8993_EQ_B1_A_MASK 0xFFFF /* EQ_B1_A - [15:0] */ +#define WM8993_EQ_B1_A_SHIFT 0 /* EQ_B1_A - [15:0] */ +#define WM8993_EQ_B1_A_WIDTH 16 /* EQ_B1_A - [15:0] */ + +/* + * R105 (0x69) - EQ8 + */ +#define WM8993_EQ_B1_B_MASK 0xFFFF /* EQ_B1_B - [15:0] */ +#define WM8993_EQ_B1_B_SHIFT 0 /* EQ_B1_B - [15:0] */ +#define WM8993_EQ_B1_B_WIDTH 16 /* EQ_B1_B - [15:0] */ + +/* + * R106 (0x6A) - EQ9 + */ +#define WM8993_EQ_B1_PG_MASK 0xFFFF /* EQ_B1_PG - [15:0] */ +#define WM8993_EQ_B1_PG_SHIFT 0 /* EQ_B1_PG - [15:0] */ +#define WM8993_EQ_B1_PG_WIDTH 16 /* EQ_B1_PG - [15:0] */ + +/* + * R107 (0x6B) - EQ10 + */ +#define WM8993_EQ_B2_A_MASK 0xFFFF /* EQ_B2_A - [15:0] */ +#define WM8993_EQ_B2_A_SHIFT 0 /* EQ_B2_A - [15:0] */ +#define WM8993_EQ_B2_A_WIDTH 16 /* EQ_B2_A - [15:0] */ + +/* + * R108 (0x6C) - EQ11 + */ +#define WM8993_EQ_B2_B_MASK 0xFFFF /* EQ_B2_B - [15:0] */ +#define WM8993_EQ_B2_B_SHIFT 0 /* EQ_B2_B - [15:0] */ +#define WM8993_EQ_B2_B_WIDTH 16 /* EQ_B2_B - [15:0] */ + +/* + * R109 (0x6D) - EQ12 + */ +#define WM8993_EQ_B2_C_MASK 0xFFFF /* EQ_B2_C - [15:0] */ +#define WM8993_EQ_B2_C_SHIFT 0 /* EQ_B2_C - [15:0] */ +#define WM8993_EQ_B2_C_WIDTH 16 /* EQ_B2_C - [15:0] */ + +/* + * R110 (0x6E) - EQ13 + */ +#define WM8993_EQ_B2_PG_MASK 0xFFFF /* EQ_B2_PG - [15:0] */ +#define WM8993_EQ_B2_PG_SHIFT 0 /* EQ_B2_PG - [15:0] */ +#define WM8993_EQ_B2_PG_WIDTH 16 /* EQ_B2_PG - [15:0] */ + +/* + * R111 (0x6F) - EQ14 + */ +#define WM8993_EQ_B3_A_MASK 0xFFFF /* EQ_B3_A - [15:0] */ +#define WM8993_EQ_B3_A_SHIFT 0 /* EQ_B3_A - [15:0] */ +#define WM8993_EQ_B3_A_WIDTH 16 /* EQ_B3_A - [15:0] */ + +/* + * R112 (0x70) - EQ15 + */ +#define WM8993_EQ_B3_B_MASK 0xFFFF /* EQ_B3_B - [15:0] */ +#define WM8993_EQ_B3_B_SHIFT 0 /* EQ_B3_B - [15:0] */ +#define WM8993_EQ_B3_B_WIDTH 16 /* EQ_B3_B - [15:0] */ + +/* + * R113 (0x71) - EQ16 + */ +#define WM8993_EQ_B3_C_MASK 0xFFFF /* EQ_B3_C - [15:0] */ +#define WM8993_EQ_B3_C_SHIFT 0 /* EQ_B3_C - [15:0] */ +#define WM8993_EQ_B3_C_WIDTH 16 /* EQ_B3_C - [15:0] */ + +/* + * R114 (0x72) - EQ17 + */ +#define WM8993_EQ_B3_PG_MASK 0xFFFF /* EQ_B3_PG - [15:0] */ +#define WM8993_EQ_B3_PG_SHIFT 0 /* EQ_B3_PG - [15:0] */ +#define WM8993_EQ_B3_PG_WIDTH 16 /* EQ_B3_PG - [15:0] */ + +/* + * R115 (0x73) - EQ18 + */ +#define WM8993_EQ_B4_A_MASK 0xFFFF /* EQ_B4_A - [15:0] */ +#define WM8993_EQ_B4_A_SHIFT 0 /* EQ_B4_A - [15:0] */ +#define WM8993_EQ_B4_A_WIDTH 16 /* EQ_B4_A - [15:0] */ + +/* + * R116 (0x74) - EQ19 + */ +#define WM8993_EQ_B4_B_MASK 0xFFFF /* EQ_B4_B - [15:0] */ +#define WM8993_EQ_B4_B_SHIFT 0 /* EQ_B4_B - [15:0] */ +#define WM8993_EQ_B4_B_WIDTH 16 /* EQ_B4_B - [15:0] */ + +/* + * R117 (0x75) - EQ20 + */ +#define WM8993_EQ_B4_C_MASK 0xFFFF /* EQ_B4_C - [15:0] */ +#define WM8993_EQ_B4_C_SHIFT 0 /* EQ_B4_C - [15:0] */ +#define WM8993_EQ_B4_C_WIDTH 16 /* EQ_B4_C - [15:0] */ + +/* + * R118 (0x76) - EQ21 + */ +#define WM8993_EQ_B4_PG_MASK 0xFFFF /* EQ_B4_PG - [15:0] */ +#define WM8993_EQ_B4_PG_SHIFT 0 /* EQ_B4_PG - [15:0] */ +#define WM8993_EQ_B4_PG_WIDTH 16 /* EQ_B4_PG - [15:0] */ + +/* + * R119 (0x77) - EQ22 + */ +#define WM8993_EQ_B5_A_MASK 0xFFFF /* EQ_B5_A - [15:0] */ +#define WM8993_EQ_B5_A_SHIFT 0 /* EQ_B5_A - [15:0] */ +#define WM8993_EQ_B5_A_WIDTH 16 /* EQ_B5_A - [15:0] */ + +/* + * R120 (0x78) - EQ23 + */ +#define WM8993_EQ_B5_B_MASK 0xFFFF /* EQ_B5_B - [15:0] */ +#define WM8993_EQ_B5_B_SHIFT 0 /* EQ_B5_B - [15:0] */ +#define WM8993_EQ_B5_B_WIDTH 16 /* EQ_B5_B - [15:0] */ + +/* + * R121 (0x79) - EQ24 + */ +#define WM8993_EQ_B5_PG_MASK 0xFFFF /* EQ_B5_PG - [15:0] */ +#define WM8993_EQ_B5_PG_SHIFT 0 /* EQ_B5_PG - [15:0] */ +#define WM8993_EQ_B5_PG_WIDTH 16 /* EQ_B5_PG - [15:0] */ + +/* + * R122 (0x7A) - Digital Pulls + */ +#define WM8993_MCLK_PU 0x0080 /* MCLK_PU */ +#define WM8993_MCLK_PU_MASK 0x0080 /* MCLK_PU */ +#define WM8993_MCLK_PU_SHIFT 7 /* MCLK_PU */ +#define WM8993_MCLK_PU_WIDTH 1 /* MCLK_PU */ +#define WM8993_MCLK_PD 0x0040 /* MCLK_PD */ +#define WM8993_MCLK_PD_MASK 0x0040 /* MCLK_PD */ +#define WM8993_MCLK_PD_SHIFT 6 /* MCLK_PD */ +#define WM8993_MCLK_PD_WIDTH 1 /* MCLK_PD */ +#define WM8993_DACDAT_PU 0x0020 /* DACDAT_PU */ +#define WM8993_DACDAT_PU_MASK 0x0020 /* DACDAT_PU */ +#define WM8993_DACDAT_PU_SHIFT 5 /* DACDAT_PU */ +#define WM8993_DACDAT_PU_WIDTH 1 /* DACDAT_PU */ +#define WM8993_DACDAT_PD 0x0010 /* DACDAT_PD */ +#define WM8993_DACDAT_PD_MASK 0x0010 /* DACDAT_PD */ +#define WM8993_DACDAT_PD_SHIFT 4 /* DACDAT_PD */ +#define WM8993_DACDAT_PD_WIDTH 1 /* DACDAT_PD */ +#define WM8993_LRCLK_PU 0x0008 /* LRCLK_PU */ +#define WM8993_LRCLK_PU_MASK 0x0008 /* LRCLK_PU */ +#define WM8993_LRCLK_PU_SHIFT 3 /* LRCLK_PU */ +#define WM8993_LRCLK_PU_WIDTH 1 /* LRCLK_PU */ +#define WM8993_LRCLK_PD 0x0004 /* LRCLK_PD */ +#define WM8993_LRCLK_PD_MASK 0x0004 /* LRCLK_PD */ +#define WM8993_LRCLK_PD_SHIFT 2 /* LRCLK_PD */ +#define WM8993_LRCLK_PD_WIDTH 1 /* LRCLK_PD */ +#define WM8993_BCLK_PU 0x0002 /* BCLK_PU */ +#define WM8993_BCLK_PU_MASK 0x0002 /* BCLK_PU */ +#define WM8993_BCLK_PU_SHIFT 1 /* BCLK_PU */ +#define WM8993_BCLK_PU_WIDTH 1 /* BCLK_PU */ +#define WM8993_BCLK_PD 0x0001 /* BCLK_PD */ +#define WM8993_BCLK_PD_MASK 0x0001 /* BCLK_PD */ +#define WM8993_BCLK_PD_SHIFT 0 /* BCLK_PD */ +#define WM8993_BCLK_PD_WIDTH 1 /* BCLK_PD */ + +/* + * R123 (0x7B) - DRC Control 1 + */ +#define WM8993_DRC_ENA 0x8000 /* DRC_ENA */ +#define WM8993_DRC_ENA_MASK 0x8000 /* DRC_ENA */ +#define WM8993_DRC_ENA_SHIFT 15 /* DRC_ENA */ +#define WM8993_DRC_ENA_WIDTH 1 /* DRC_ENA */ +#define WM8993_DRC_DAC_PATH 0x4000 /* DRC_DAC_PATH */ +#define WM8993_DRC_DAC_PATH_MASK 0x4000 /* DRC_DAC_PATH */ +#define WM8993_DRC_DAC_PATH_SHIFT 14 /* DRC_DAC_PATH */ +#define WM8993_DRC_DAC_PATH_WIDTH 1 /* DRC_DAC_PATH */ +#define WM8993_DRC_SMOOTH_ENA 0x0800 /* DRC_SMOOTH_ENA */ +#define WM8993_DRC_SMOOTH_ENA_MASK 0x0800 /* DRC_SMOOTH_ENA */ +#define WM8993_DRC_SMOOTH_ENA_SHIFT 11 /* DRC_SMOOTH_ENA */ +#define WM8993_DRC_SMOOTH_ENA_WIDTH 1 /* DRC_SMOOTH_ENA */ +#define WM8993_DRC_QR_ENA 0x0400 /* DRC_QR_ENA */ +#define WM8993_DRC_QR_ENA_MASK 0x0400 /* DRC_QR_ENA */ +#define WM8993_DRC_QR_ENA_SHIFT 10 /* DRC_QR_ENA */ +#define WM8993_DRC_QR_ENA_WIDTH 1 /* DRC_QR_ENA */ +#define WM8993_DRC_ANTICLIP_ENA 0x0200 /* DRC_ANTICLIP_ENA */ +#define WM8993_DRC_ANTICLIP_ENA_MASK 0x0200 /* DRC_ANTICLIP_ENA */ +#define WM8993_DRC_ANTICLIP_ENA_SHIFT 9 /* DRC_ANTICLIP_ENA */ +#define WM8993_DRC_ANTICLIP_ENA_WIDTH 1 /* DRC_ANTICLIP_ENA */ +#define WM8993_DRC_HYST_ENA 0x0100 /* DRC_HYST_ENA */ +#define WM8993_DRC_HYST_ENA_MASK 0x0100 /* DRC_HYST_ENA */ +#define WM8993_DRC_HYST_ENA_SHIFT 8 /* DRC_HYST_ENA */ +#define WM8993_DRC_HYST_ENA_WIDTH 1 /* DRC_HYST_ENA */ +#define WM8993_DRC_THRESH_HYST_MASK 0x0030 /* DRC_THRESH_HYST - [5:4] */ +#define WM8993_DRC_THRESH_HYST_SHIFT 4 /* DRC_THRESH_HYST - [5:4] */ +#define WM8993_DRC_THRESH_HYST_WIDTH 2 /* DRC_THRESH_HYST - [5:4] */ +#define WM8993_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */ +#define WM8993_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */ +#define WM8993_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */ +#define WM8993_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */ +#define WM8993_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */ +#define WM8993_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */ + +/* + * R124 (0x7C) - DRC Control 2 + */ +#define WM8993_DRC_ATTACK_RATE_MASK 0xF000 /* DRC_ATTACK_RATE - [15:12] */ +#define WM8993_DRC_ATTACK_RATE_SHIFT 12 /* DRC_ATTACK_RATE - [15:12] */ +#define WM8993_DRC_ATTACK_RATE_WIDTH 4 /* DRC_ATTACK_RATE - [15:12] */ +#define WM8993_DRC_DECAY_RATE_MASK 0x0F00 /* DRC_DECAY_RATE - [11:8] */ +#define WM8993_DRC_DECAY_RATE_SHIFT 8 /* DRC_DECAY_RATE - [11:8] */ +#define WM8993_DRC_DECAY_RATE_WIDTH 4 /* DRC_DECAY_RATE - [11:8] */ +#define WM8993_DRC_THRESH_COMP_MASK 0x00FC /* DRC_THRESH_COMP - [7:2] */ +#define WM8993_DRC_THRESH_COMP_SHIFT 2 /* DRC_THRESH_COMP - [7:2] */ +#define WM8993_DRC_THRESH_COMP_WIDTH 6 /* DRC_THRESH_COMP - [7:2] */ + +/* + * R125 (0x7D) - DRC Control 3 + */ +#define WM8993_DRC_AMP_COMP_MASK 0xF800 /* DRC_AMP_COMP - [15:11] */ +#define WM8993_DRC_AMP_COMP_SHIFT 11 /* DRC_AMP_COMP - [15:11] */ +#define WM8993_DRC_AMP_COMP_WIDTH 5 /* DRC_AMP_COMP - [15:11] */ +#define WM8993_DRC_R0_SLOPE_COMP_MASK 0x0700 /* DRC_R0_SLOPE_COMP - [10:8] */ +#define WM8993_DRC_R0_SLOPE_COMP_SHIFT 8 /* DRC_R0_SLOPE_COMP - [10:8] */ +#define WM8993_DRC_R0_SLOPE_COMP_WIDTH 3 /* DRC_R0_SLOPE_COMP - [10:8] */ +#define WM8993_DRC_FF_DELAY 0x0080 /* DRC_FF_DELAY */ +#define WM8993_DRC_FF_DELAY_MASK 0x0080 /* DRC_FF_DELAY */ +#define WM8993_DRC_FF_DELAY_SHIFT 7 /* DRC_FF_DELAY */ +#define WM8993_DRC_FF_DELAY_WIDTH 1 /* DRC_FF_DELAY */ +#define WM8993_DRC_THRESH_QR_MASK 0x000C /* DRC_THRESH_QR - [3:2] */ +#define WM8993_DRC_THRESH_QR_SHIFT 2 /* DRC_THRESH_QR - [3:2] */ +#define WM8993_DRC_THRESH_QR_WIDTH 2 /* DRC_THRESH_QR - [3:2] */ +#define WM8993_DRC_RATE_QR_MASK 0x0003 /* DRC_RATE_QR - [1:0] */ +#define WM8993_DRC_RATE_QR_SHIFT 0 /* DRC_RATE_QR - [1:0] */ +#define WM8993_DRC_RATE_QR_WIDTH 2 /* DRC_RATE_QR - [1:0] */ + +/* + * R126 (0x7E) - DRC Control 4 + */ +#define WM8993_DRC_R1_SLOPE_COMP_MASK 0xE000 /* DRC_R1_SLOPE_COMP - [15:13] */ +#define WM8993_DRC_R1_SLOPE_COMP_SHIFT 13 /* DRC_R1_SLOPE_COMP - [15:13] */ +#define WM8993_DRC_R1_SLOPE_COMP_WIDTH 3 /* DRC_R1_SLOPE_COMP - [15:13] */ +#define WM8993_DRC_STARTUP_GAIN_MASK 0x1F00 /* DRC_STARTUP_GAIN - [12:8] */ +#define WM8993_DRC_STARTUP_GAIN_SHIFT 8 /* DRC_STARTUP_GAIN - [12:8] */ +#define WM8993_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [12:8] */ + +#endif diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c new file mode 100644 index 0000000000..a48e904a97 --- /dev/null +++ b/sound/soc/codecs/wm8994.c @@ -0,0 +1,4710 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8994.c -- WM8994 ALSA SoC Audio driver + * + * Copyright 2009-12 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "wm8994.h" +#include "wm_hubs.h" + +#define WM1811_JACKDET_MODE_NONE 0x0000 +#define WM1811_JACKDET_MODE_JACK 0x0100 +#define WM1811_JACKDET_MODE_MIC 0x0080 +#define WM1811_JACKDET_MODE_AUDIO 0x0180 + +#define WM8994_NUM_DRC 3 +#define WM8994_NUM_EQ 3 + +struct wm8994_reg_mask { + unsigned int reg; + unsigned int mask; +}; + +static struct wm8994_reg_mask wm8994_vu_bits[] = { + { WM8994_LEFT_LINE_INPUT_1_2_VOLUME, WM8994_IN1_VU }, + { WM8994_RIGHT_LINE_INPUT_1_2_VOLUME, WM8994_IN1_VU }, + { WM8994_LEFT_LINE_INPUT_3_4_VOLUME, WM8994_IN2_VU }, + { WM8994_RIGHT_LINE_INPUT_3_4_VOLUME, WM8994_IN2_VU }, + { WM8994_SPEAKER_VOLUME_LEFT, WM8994_SPKOUT_VU }, + { WM8994_SPEAKER_VOLUME_RIGHT, WM8994_SPKOUT_VU }, + { WM8994_LEFT_OUTPUT_VOLUME, WM8994_HPOUT1_VU }, + { WM8994_RIGHT_OUTPUT_VOLUME, WM8994_HPOUT1_VU }, + { WM8994_LEFT_OPGA_VOLUME, WM8994_MIXOUT_VU }, + { WM8994_RIGHT_OPGA_VOLUME, WM8994_MIXOUT_VU }, + + { WM8994_AIF1_DAC1_LEFT_VOLUME, WM8994_AIF1DAC1_VU }, + { WM8994_AIF1_DAC1_RIGHT_VOLUME, WM8994_AIF1DAC1_VU }, + { WM8994_AIF2_DAC_LEFT_VOLUME, WM8994_AIF2DAC_VU }, + { WM8994_AIF2_DAC_RIGHT_VOLUME, WM8994_AIF2DAC_VU }, + { WM8994_AIF1_ADC1_LEFT_VOLUME, WM8994_AIF1ADC1_VU }, + { WM8994_AIF1_ADC1_RIGHT_VOLUME, WM8994_AIF1ADC1_VU }, + { WM8994_AIF2_ADC_LEFT_VOLUME, WM8994_AIF2ADC_VU }, + { WM8994_AIF2_ADC_RIGHT_VOLUME, WM8994_AIF1ADC2_VU }, + { WM8994_DAC1_LEFT_VOLUME, WM8994_DAC1_VU }, + { WM8994_DAC1_RIGHT_VOLUME, WM8994_DAC1_VU }, + { WM8994_DAC2_LEFT_VOLUME, WM8994_DAC2_VU }, + { WM8994_DAC2_RIGHT_VOLUME, WM8994_DAC2_VU }, +}; + +/* VU bitfields for ADC2, DAC2 not available on WM1811 */ +static struct wm8994_reg_mask wm8994_adc2_dac2_vu_bits[] = { + { WM8994_AIF1_DAC2_LEFT_VOLUME, WM8994_AIF1DAC2_VU }, + { WM8994_AIF1_DAC2_RIGHT_VOLUME, WM8994_AIF1DAC2_VU }, + { WM8994_AIF1_ADC2_LEFT_VOLUME, WM8994_AIF1ADC2_VU }, + { WM8994_AIF1_ADC2_RIGHT_VOLUME, WM8994_AIF1ADC2_VU }, +}; + +static int wm8994_drc_base[] = { + WM8994_AIF1_DRC1_1, + WM8994_AIF1_DRC2_1, + WM8994_AIF2_DRC_1, +}; + +static int wm8994_retune_mobile_base[] = { + WM8994_AIF1_DAC1_EQ_GAINS_1, + WM8994_AIF1_DAC2_EQ_GAINS_1, + WM8994_AIF2_EQ_GAINS_1, +}; + +static const struct wm8958_micd_rate micdet_rates[] = { + { 32768, true, 1, 4 }, + { 32768, false, 1, 1 }, + { 44100 * 256, true, 7, 10 }, + { 44100 * 256, false, 7, 10 }, +}; + +static const struct wm8958_micd_rate jackdet_rates[] = { + { 32768, true, 0, 1 }, + { 32768, false, 0, 1 }, + { 44100 * 256, true, 10, 10 }, + { 44100 * 256, false, 7, 8 }, +}; + +static void wm8958_micd_set_rate(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int best, i, sysclk, val; + bool idle; + const struct wm8958_micd_rate *rates; + int num_rates; + + idle = !wm8994->jack_mic; + + sysclk = snd_soc_component_read(component, WM8994_CLOCKING_1); + if (sysclk & WM8994_SYSCLK_SRC) + sysclk = wm8994->aifclk[1]; + else + sysclk = wm8994->aifclk[0]; + + if (control->pdata.micd_rates) { + rates = control->pdata.micd_rates; + num_rates = control->pdata.num_micd_rates; + } else if (wm8994->jackdet) { + rates = jackdet_rates; + num_rates = ARRAY_SIZE(jackdet_rates); + } else { + rates = micdet_rates; + num_rates = ARRAY_SIZE(micdet_rates); + } + + best = 0; + for (i = 0; i < num_rates; i++) { + if (rates[i].idle != idle) + continue; + if (abs(rates[i].sysclk - sysclk) < + abs(rates[best].sysclk - sysclk)) + best = i; + else if (rates[best].idle != idle) + best = i; + } + + val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT + | rates[best].rate << WM8958_MICD_RATE_SHIFT; + + dev_dbg(component->dev, "MICD rate %d,%d for %dHz %s\n", + rates[best].start, rates[best].rate, sysclk, + idle ? "idle" : "active"); + + snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, + WM8958_MICD_BIAS_STARTTIME_MASK | + WM8958_MICD_RATE_MASK, val); +} + +static int configure_aif_clock(struct snd_soc_component *component, int aif) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + int rate; + int reg1 = 0; + int offset; + + if (aif) + offset = 4; + else + offset = 0; + + switch (wm8994->sysclk[aif]) { + case WM8994_SYSCLK_MCLK1: + rate = wm8994->mclk_rate[0]; + break; + + case WM8994_SYSCLK_MCLK2: + reg1 |= 0x8; + rate = wm8994->mclk_rate[1]; + break; + + case WM8994_SYSCLK_FLL1: + reg1 |= 0x10; + rate = wm8994->fll[0].out; + break; + + case WM8994_SYSCLK_FLL2: + reg1 |= 0x18; + rate = wm8994->fll[1].out; + break; + + default: + return -EINVAL; + } + + if (rate >= 13500000) { + rate /= 2; + reg1 |= WM8994_AIF1CLK_DIV; + + dev_dbg(component->dev, "Dividing AIF%d clock to %dHz\n", + aif + 1, rate); + } + + wm8994->aifclk[aif] = rate; + + snd_soc_component_update_bits(component, WM8994_AIF1_CLOCKING_1 + offset, + WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV, + reg1); + + return 0; +} + +static int configure_clock(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + int change, new; + + /* Bring up the AIF clocks first */ + configure_aif_clock(component, 0); + configure_aif_clock(component, 1); + + /* Then switch CLK_SYS over to the higher of them; a change + * can only happen as a result of a clocking change which can + * only be made outside of DAPM so we can safely redo the + * clocking. + */ + + /* If they're equal it doesn't matter which is used */ + if (wm8994->aifclk[0] == wm8994->aifclk[1]) { + wm8958_micd_set_rate(component); + return 0; + } + + if (wm8994->aifclk[0] < wm8994->aifclk[1]) + new = WM8994_SYSCLK_SRC; + else + new = 0; + + change = snd_soc_component_update_bits(component, WM8994_CLOCKING_1, + WM8994_SYSCLK_SRC, new); + if (change) + snd_soc_dapm_sync(dapm); + + wm8958_micd_set_rate(component); + + return 0; +} + +static int check_clk_sys(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + int reg = snd_soc_component_read(component, WM8994_CLOCKING_1); + const char *clk; + + /* Check what we're currently using for CLK_SYS */ + if (reg & WM8994_SYSCLK_SRC) + clk = "AIF2CLK"; + else + clk = "AIF1CLK"; + + return strcmp(source->name, clk) == 0; +} + +static const char *sidetone_hpf_text[] = { + "2.7kHz", "1.35kHz", "675Hz", "370Hz", "180Hz", "90Hz", "45Hz" +}; + +static SOC_ENUM_SINGLE_DECL(sidetone_hpf, + WM8994_SIDETONE, 7, sidetone_hpf_text); + +static const char *adc_hpf_text[] = { + "HiFi", "Voice 1", "Voice 2", "Voice 3" +}; + +static SOC_ENUM_SINGLE_DECL(aif1adc1_hpf, + WM8994_AIF1_ADC1_FILTERS, 13, adc_hpf_text); + +static SOC_ENUM_SINGLE_DECL(aif1adc2_hpf, + WM8994_AIF1_ADC2_FILTERS, 13, adc_hpf_text); + +static SOC_ENUM_SINGLE_DECL(aif2adc_hpf, + WM8994_AIF2_ADC_FILTERS, 13, adc_hpf_text); + +static const DECLARE_TLV_DB_SCALE(aif_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0); +static const DECLARE_TLV_DB_SCALE(wm8994_3d_tlv, -1600, 183, 0); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define WM8994_DRC_SWITCH(xname, reg, shift) \ + SOC_SINGLE_EXT(xname, reg, shift, 1, 0, \ + snd_soc_get_volsw, wm8994_put_drc_sw) + +static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + int mask, ret; + + /* Can't enable both ADC and DAC paths simultaneously */ + if (mc->shift == WM8994_AIF1DAC1_DRC_ENA_SHIFT) + mask = WM8994_AIF1ADC1L_DRC_ENA_MASK | + WM8994_AIF1ADC1R_DRC_ENA_MASK; + else + mask = WM8994_AIF1DAC1_DRC_ENA_MASK; + + ret = snd_soc_component_read(component, mc->reg); + if (ret < 0) + return ret; + if (ret & mask) + return -EINVAL; + + return snd_soc_put_volsw(kcontrol, ucontrol); +} + +static void wm8994_set_drc(struct snd_soc_component *component, int drc) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + struct wm8994_pdata *pdata = &control->pdata; + int base = wm8994_drc_base[drc]; + int cfg = wm8994->drc_cfg[drc]; + int save, i; + + /* Save any enables; the configuration should clear them. */ + save = snd_soc_component_read(component, base); + save &= WM8994_AIF1DAC1_DRC_ENA | WM8994_AIF1ADC1L_DRC_ENA | + WM8994_AIF1ADC1R_DRC_ENA; + + for (i = 0; i < WM8994_DRC_REGS; i++) + snd_soc_component_update_bits(component, base + i, 0xffff, + pdata->drc_cfgs[cfg].regs[i]); + + snd_soc_component_update_bits(component, base, WM8994_AIF1DAC1_DRC_ENA | + WM8994_AIF1ADC1L_DRC_ENA | + WM8994_AIF1ADC1R_DRC_ENA, save); +} + +/* Icky as hell but saves code duplication */ +static int wm8994_get_drc(const char *name) +{ + if (strcmp(name, "AIF1DRC1 Mode") == 0) + return 0; + if (strcmp(name, "AIF1DRC2 Mode") == 0) + return 1; + if (strcmp(name, "AIF2DRC Mode") == 0) + return 2; + return -EINVAL; +} + +static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + struct wm8994_pdata *pdata = &control->pdata; + int drc = wm8994_get_drc(kcontrol->id.name); + int value = ucontrol->value.enumerated.item[0]; + + if (drc < 0) + return drc; + + if (value >= pdata->num_drc_cfgs) + return -EINVAL; + + wm8994->drc_cfg[drc] = value; + + wm8994_set_drc(component, drc); + + return 0; +} + +static int wm8994_get_drc_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + int drc = wm8994_get_drc(kcontrol->id.name); + + if (drc < 0) + return drc; + ucontrol->value.enumerated.item[0] = wm8994->drc_cfg[drc]; + + return 0; +} + +static void wm8994_set_retune_mobile(struct snd_soc_component *component, int block) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + struct wm8994_pdata *pdata = &control->pdata; + int base = wm8994_retune_mobile_base[block]; + int iface, best, best_val, save, i, cfg; + + if (!pdata || !wm8994->num_retune_mobile_texts) + return; + + switch (block) { + case 0: + case 1: + iface = 0; + break; + case 2: + iface = 1; + break; + default: + return; + } + + /* Find the version of the currently selected configuration + * with the nearest sample rate. */ + cfg = wm8994->retune_mobile_cfg[block]; + best = 0; + best_val = INT_MAX; + for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) { + if (strcmp(pdata->retune_mobile_cfgs[i].name, + wm8994->retune_mobile_texts[cfg]) == 0 && + abs(pdata->retune_mobile_cfgs[i].rate + - wm8994->dac_rates[iface]) < best_val) { + best = i; + best_val = abs(pdata->retune_mobile_cfgs[i].rate + - wm8994->dac_rates[iface]); + } + } + + dev_dbg(component->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n", + block, + pdata->retune_mobile_cfgs[best].name, + pdata->retune_mobile_cfgs[best].rate, + wm8994->dac_rates[iface]); + + /* The EQ will be disabled while reconfiguring it, remember the + * current configuration. + */ + save = snd_soc_component_read(component, base); + save &= WM8994_AIF1DAC1_EQ_ENA; + + for (i = 0; i < WM8994_EQ_REGS; i++) + snd_soc_component_update_bits(component, base + i, 0xffff, + pdata->retune_mobile_cfgs[best].regs[i]); + + snd_soc_component_update_bits(component, base, WM8994_AIF1DAC1_EQ_ENA, save); +} + +/* Icky as hell but saves code duplication */ +static int wm8994_get_retune_mobile_block(const char *name) +{ + if (strcmp(name, "AIF1.1 EQ Mode") == 0) + return 0; + if (strcmp(name, "AIF1.2 EQ Mode") == 0) + return 1; + if (strcmp(name, "AIF2 EQ Mode") == 0) + return 2; + return -EINVAL; +} + +static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + struct wm8994_pdata *pdata = &control->pdata; + int block = wm8994_get_retune_mobile_block(kcontrol->id.name); + int value = ucontrol->value.enumerated.item[0]; + + if (block < 0) + return block; + + if (value >= pdata->num_retune_mobile_cfgs) + return -EINVAL; + + wm8994->retune_mobile_cfg[block] = value; + + wm8994_set_retune_mobile(component, block); + + return 0; +} + +static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + int block = wm8994_get_retune_mobile_block(kcontrol->id.name); + + if (block < 0) + return block; + + ucontrol->value.enumerated.item[0] = wm8994->retune_mobile_cfg[block]; + + return 0; +} + +static const char *aif_chan_src_text[] = { + "Left", "Right" +}; + +static SOC_ENUM_SINGLE_DECL(aif1adcl_src, + WM8994_AIF1_CONTROL_1, 15, aif_chan_src_text); + +static SOC_ENUM_SINGLE_DECL(aif1adcr_src, + WM8994_AIF1_CONTROL_1, 14, aif_chan_src_text); + +static SOC_ENUM_SINGLE_DECL(aif2adcl_src, + WM8994_AIF2_CONTROL_1, 15, aif_chan_src_text); + +static SOC_ENUM_SINGLE_DECL(aif2adcr_src, + WM8994_AIF2_CONTROL_1, 14, aif_chan_src_text); + +static SOC_ENUM_SINGLE_DECL(aif1dacl_src, + WM8994_AIF1_CONTROL_2, 15, aif_chan_src_text); + +static SOC_ENUM_SINGLE_DECL(aif1dacr_src, + WM8994_AIF1_CONTROL_2, 14, aif_chan_src_text); + +static SOC_ENUM_SINGLE_DECL(aif2dacl_src, + WM8994_AIF2_CONTROL_2, 15, aif_chan_src_text); + +static SOC_ENUM_SINGLE_DECL(aif2dacr_src, + WM8994_AIF2_CONTROL_2, 14, aif_chan_src_text); + +static const char *osr_text[] = { + "Low Power", "High Performance", +}; + +static SOC_ENUM_SINGLE_DECL(dac_osr, + WM8994_OVERSAMPLING, 0, osr_text); + +static SOC_ENUM_SINGLE_DECL(adc_osr, + WM8994_OVERSAMPLING, 1, osr_text); + +static const struct snd_kcontrol_new wm8994_common_snd_controls[] = { +SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME, + WM8994_AIF1_ADC1_RIGHT_VOLUME, + 1, 119, 0, digital_tlv), +SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME, + WM8994_AIF2_ADC_RIGHT_VOLUME, + 1, 119, 0, digital_tlv), + +SOC_ENUM("AIF1ADCL Source", aif1adcl_src), +SOC_ENUM("AIF1ADCR Source", aif1adcr_src), +SOC_ENUM("AIF2ADCL Source", aif2adcl_src), +SOC_ENUM("AIF2ADCR Source", aif2adcr_src), + +SOC_ENUM("AIF1DACL Source", aif1dacl_src), +SOC_ENUM("AIF1DACR Source", aif1dacr_src), +SOC_ENUM("AIF2DACL Source", aif2dacl_src), +SOC_ENUM("AIF2DACR Source", aif2dacr_src), + +SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8994_AIF1_DAC1_LEFT_VOLUME, + WM8994_AIF1_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv), +SOC_DOUBLE_R_TLV("AIF2DAC Volume", WM8994_AIF2_DAC_LEFT_VOLUME, + WM8994_AIF2_DAC_RIGHT_VOLUME, 1, 96, 0, digital_tlv), + +SOC_SINGLE_TLV("AIF1 Boost Volume", WM8994_AIF1_CONTROL_2, 10, 3, 0, aif_tlv), +SOC_SINGLE_TLV("AIF2 Boost Volume", WM8994_AIF2_CONTROL_2, 10, 3, 0, aif_tlv), + +SOC_SINGLE("AIF1DAC1 EQ Switch", WM8994_AIF1_DAC1_EQ_GAINS_1, 0, 1, 0), +SOC_SINGLE("AIF2 EQ Switch", WM8994_AIF2_EQ_GAINS_1, 0, 1, 0), + +WM8994_DRC_SWITCH("AIF1DAC1 DRC Switch", WM8994_AIF1_DRC1_1, 2), +WM8994_DRC_SWITCH("AIF1ADC1L DRC Switch", WM8994_AIF1_DRC1_1, 1), +WM8994_DRC_SWITCH("AIF1ADC1R DRC Switch", WM8994_AIF1_DRC1_1, 0), + +WM8994_DRC_SWITCH("AIF2DAC DRC Switch", WM8994_AIF2_DRC_1, 2), +WM8994_DRC_SWITCH("AIF2ADCL DRC Switch", WM8994_AIF2_DRC_1, 1), +WM8994_DRC_SWITCH("AIF2ADCR DRC Switch", WM8994_AIF2_DRC_1, 0), + +SOC_SINGLE_TLV("DAC1 Right Sidetone Volume", WM8994_DAC1_MIXER_VOLUMES, + 5, 12, 0, st_tlv), +SOC_SINGLE_TLV("DAC1 Left Sidetone Volume", WM8994_DAC1_MIXER_VOLUMES, + 0, 12, 0, st_tlv), +SOC_SINGLE_TLV("DAC2 Right Sidetone Volume", WM8994_DAC2_MIXER_VOLUMES, + 5, 12, 0, st_tlv), +SOC_SINGLE_TLV("DAC2 Left Sidetone Volume", WM8994_DAC2_MIXER_VOLUMES, + 0, 12, 0, st_tlv), +SOC_ENUM("Sidetone HPF Mux", sidetone_hpf), +SOC_SINGLE("Sidetone HPF Switch", WM8994_SIDETONE, 6, 1, 0), + +SOC_ENUM("AIF1ADC1 HPF Mode", aif1adc1_hpf), +SOC_DOUBLE("AIF1ADC1 HPF Switch", WM8994_AIF1_ADC1_FILTERS, 12, 11, 1, 0), + +SOC_ENUM("AIF2ADC HPF Mode", aif2adc_hpf), +SOC_DOUBLE("AIF2ADC HPF Switch", WM8994_AIF2_ADC_FILTERS, 12, 11, 1, 0), + +SOC_ENUM("ADC OSR", adc_osr), +SOC_ENUM("DAC OSR", dac_osr), + +SOC_DOUBLE_R_TLV("DAC1 Volume", WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv), +SOC_DOUBLE_R("DAC1 Switch", WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_RIGHT_VOLUME, 9, 1, 1), + +SOC_DOUBLE_R_TLV("DAC2 Volume", WM8994_DAC2_LEFT_VOLUME, + WM8994_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv), +SOC_DOUBLE_R("DAC2 Switch", WM8994_DAC2_LEFT_VOLUME, + WM8994_DAC2_RIGHT_VOLUME, 9, 1, 1), + +SOC_SINGLE_TLV("SPKL DAC2 Volume", WM8994_SPKMIXL_ATTENUATION, + 6, 1, 1, wm_hubs_spkmix_tlv), +SOC_SINGLE_TLV("SPKL DAC1 Volume", WM8994_SPKMIXL_ATTENUATION, + 2, 1, 1, wm_hubs_spkmix_tlv), + +SOC_SINGLE_TLV("SPKR DAC2 Volume", WM8994_SPKMIXR_ATTENUATION, + 6, 1, 1, wm_hubs_spkmix_tlv), +SOC_SINGLE_TLV("SPKR DAC1 Volume", WM8994_SPKMIXR_ATTENUATION, + 2, 1, 1, wm_hubs_spkmix_tlv), + +SOC_SINGLE_TLV("AIF1DAC1 3D Stereo Volume", WM8994_AIF1_DAC1_FILTERS_2, + 10, 15, 0, wm8994_3d_tlv), +SOC_SINGLE("AIF1DAC1 3D Stereo Switch", WM8994_AIF1_DAC1_FILTERS_2, + 8, 1, 0), +SOC_SINGLE_TLV("AIF1DAC2 3D Stereo Volume", WM8994_AIF1_DAC2_FILTERS_2, + 10, 15, 0, wm8994_3d_tlv), +SOC_SINGLE("AIF1DAC2 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2, + 8, 1, 0), +SOC_SINGLE_TLV("AIF2DAC 3D Stereo Volume", WM8994_AIF2_DAC_FILTERS_2, + 10, 15, 0, wm8994_3d_tlv), +SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF2_DAC_FILTERS_2, + 8, 1, 0), +}; + +/* Controls not available on WM1811 */ +static const struct snd_kcontrol_new wm8994_snd_controls[] = { +SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME, + WM8994_AIF1_ADC2_RIGHT_VOLUME, + 1, 119, 0, digital_tlv), +SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8994_AIF1_DAC2_LEFT_VOLUME, + WM8994_AIF1_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv), + +SOC_SINGLE("AIF1DAC2 EQ Switch", WM8994_AIF1_DAC2_EQ_GAINS_1, 0, 1, 0), + +WM8994_DRC_SWITCH("AIF1DAC2 DRC Switch", WM8994_AIF1_DRC2_1, 2), +WM8994_DRC_SWITCH("AIF1ADC2L DRC Switch", WM8994_AIF1_DRC2_1, 1), +WM8994_DRC_SWITCH("AIF1ADC2R DRC Switch", WM8994_AIF1_DRC2_1, 0), + +SOC_ENUM("AIF1ADC2 HPF Mode", aif1adc2_hpf), +SOC_DOUBLE("AIF1ADC2 HPF Switch", WM8994_AIF1_ADC2_FILTERS, 12, 11, 1, 0), +}; + +static const struct snd_kcontrol_new wm8994_eq_controls[] = { +SOC_SINGLE_TLV("AIF1DAC1 EQ1 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 11, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF1DAC1 EQ2 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 6, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF1DAC1 EQ3 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 1, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF1DAC1 EQ4 Volume", WM8994_AIF1_DAC1_EQ_GAINS_2, 11, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF1DAC1 EQ5 Volume", WM8994_AIF1_DAC1_EQ_GAINS_2, 6, 31, 0, + eq_tlv), + +SOC_SINGLE_TLV("AIF1DAC2 EQ1 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 11, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF1DAC2 EQ2 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 6, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF1DAC2 EQ3 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 1, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF1DAC2 EQ4 Volume", WM8994_AIF1_DAC2_EQ_GAINS_2, 11, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF1DAC2 EQ5 Volume", WM8994_AIF1_DAC2_EQ_GAINS_2, 6, 31, 0, + eq_tlv), + +SOC_SINGLE_TLV("AIF2 EQ1 Volume", WM8994_AIF2_EQ_GAINS_1, 11, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF2 EQ2 Volume", WM8994_AIF2_EQ_GAINS_1, 6, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF2 EQ3 Volume", WM8994_AIF2_EQ_GAINS_1, 1, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF2 EQ4 Volume", WM8994_AIF2_EQ_GAINS_2, 11, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0, + eq_tlv), +}; + +static const struct snd_kcontrol_new wm8994_drc_controls[] = { +SND_SOC_BYTES_MASK("AIF1.1 DRC", WM8994_AIF1_DRC1_1, 5, + WM8994_AIF1DAC1_DRC_ENA | WM8994_AIF1ADC1L_DRC_ENA | + WM8994_AIF1ADC1R_DRC_ENA), +SND_SOC_BYTES_MASK("AIF1.2 DRC", WM8994_AIF1_DRC2_1, 5, + WM8994_AIF1DAC2_DRC_ENA | WM8994_AIF1ADC2L_DRC_ENA | + WM8994_AIF1ADC2R_DRC_ENA), +SND_SOC_BYTES_MASK("AIF2 DRC", WM8994_AIF2_DRC_1, 5, + WM8994_AIF2DAC_DRC_ENA | WM8994_AIF2ADCL_DRC_ENA | + WM8994_AIF2ADCR_DRC_ENA), +}; + +static const char *wm8958_ng_text[] = { + "30ms", "125ms", "250ms", "500ms", +}; + +static SOC_ENUM_SINGLE_DECL(wm8958_aif1dac1_ng_hold, + WM8958_AIF1_DAC1_NOISE_GATE, + WM8958_AIF1DAC1_NG_THR_SHIFT, + wm8958_ng_text); + +static SOC_ENUM_SINGLE_DECL(wm8958_aif1dac2_ng_hold, + WM8958_AIF1_DAC2_NOISE_GATE, + WM8958_AIF1DAC2_NG_THR_SHIFT, + wm8958_ng_text); + +static SOC_ENUM_SINGLE_DECL(wm8958_aif2dac_ng_hold, + WM8958_AIF2_DAC_NOISE_GATE, + WM8958_AIF2DAC_NG_THR_SHIFT, + wm8958_ng_text); + +static const struct snd_kcontrol_new wm8958_snd_controls[] = { +SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv), + +SOC_SINGLE("AIF1DAC1 Noise Gate Switch", WM8958_AIF1_DAC1_NOISE_GATE, + WM8958_AIF1DAC1_NG_ENA_SHIFT, 1, 0), +SOC_ENUM("AIF1DAC1 Noise Gate Hold Time", wm8958_aif1dac1_ng_hold), +SOC_SINGLE_TLV("AIF1DAC1 Noise Gate Threshold Volume", + WM8958_AIF1_DAC1_NOISE_GATE, WM8958_AIF1DAC1_NG_THR_SHIFT, + 7, 1, ng_tlv), + +SOC_SINGLE("AIF1DAC2 Noise Gate Switch", WM8958_AIF1_DAC2_NOISE_GATE, + WM8958_AIF1DAC2_NG_ENA_SHIFT, 1, 0), +SOC_ENUM("AIF1DAC2 Noise Gate Hold Time", wm8958_aif1dac2_ng_hold), +SOC_SINGLE_TLV("AIF1DAC2 Noise Gate Threshold Volume", + WM8958_AIF1_DAC2_NOISE_GATE, WM8958_AIF1DAC2_NG_THR_SHIFT, + 7, 1, ng_tlv), + +SOC_SINGLE("AIF2DAC Noise Gate Switch", WM8958_AIF2_DAC_NOISE_GATE, + WM8958_AIF2DAC_NG_ENA_SHIFT, 1, 0), +SOC_ENUM("AIF2DAC Noise Gate Hold Time", wm8958_aif2dac_ng_hold), +SOC_SINGLE_TLV("AIF2DAC Noise Gate Threshold Volume", + WM8958_AIF2_DAC_NOISE_GATE, WM8958_AIF2DAC_NG_THR_SHIFT, + 7, 1, ng_tlv), +}; + +/* We run all mode setting through a function to enforce audio mode */ +static void wm1811_jackdet_set_mode(struct snd_soc_component *component, u16 mode) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (!wm8994->jackdet || !wm8994->micdet[0].jack) + return; + + if (wm8994->active_refcount) + mode = WM1811_JACKDET_MODE_AUDIO; + + if (mode == wm8994->jackdet_mode) + return; + + wm8994->jackdet_mode = mode; + + /* Always use audio mode to detect while the system is active */ + if (mode != WM1811_JACKDET_MODE_NONE) + mode = WM1811_JACKDET_MODE_AUDIO; + + snd_soc_component_update_bits(component, WM8994_ANTIPOP_2, + WM1811_JACKDET_MODE_MASK, mode); +} + +static void active_reference(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + mutex_lock(&wm8994->accdet_lock); + + wm8994->active_refcount++; + + dev_dbg(component->dev, "Active refcount incremented, now %d\n", + wm8994->active_refcount); + + /* If we're using jack detection go into audio mode */ + wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_AUDIO); + + mutex_unlock(&wm8994->accdet_lock); +} + +static void active_dereference(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + u16 mode; + + mutex_lock(&wm8994->accdet_lock); + + wm8994->active_refcount--; + + dev_dbg(component->dev, "Active refcount decremented, now %d\n", + wm8994->active_refcount); + + if (wm8994->active_refcount == 0) { + /* Go into appropriate detection only mode */ + if (wm8994->jack_mic || wm8994->mic_detecting) + mode = WM1811_JACKDET_MODE_MIC; + else + mode = WM1811_JACKDET_MODE_JACK; + + wm1811_jackdet_set_mode(component, mode); + } + + mutex_unlock(&wm8994->accdet_lock); +} + +static int clk_sys_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return configure_clock(component); + + case SND_SOC_DAPM_POST_PMU: + /* + * JACKDET won't run until we start the clock and it + * only reports deltas, make sure we notify the state + * up the stack on startup. Use a *very* generous + * timeout for paranoia, there's no urgency and we + * don't want false reports. + */ + if (wm8994->jackdet && !wm8994->clk_has_run) { + queue_delayed_work(system_power_efficient_wq, + &wm8994->jackdet_bootstrap, + msecs_to_jiffies(1000)); + wm8994->clk_has_run = true; + } + break; + + case SND_SOC_DAPM_POST_PMD: + configure_clock(component); + break; + } + + return 0; +} + +static void vmid_reference(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + pm_runtime_get_sync(component->dev); + + wm8994->vmid_refcount++; + + dev_dbg(component->dev, "Referencing VMID, refcount is now %d\n", + wm8994->vmid_refcount); + + if (wm8994->vmid_refcount == 1) { + snd_soc_component_update_bits(component, WM8994_ANTIPOP_1, + WM8994_LINEOUT1_DISCH | + WM8994_LINEOUT2_DISCH, 0); + + wm_hubs_vmid_ena(component); + + switch (wm8994->vmid_mode) { + default: + WARN_ON(NULL == "Invalid VMID mode"); + fallthrough; + case WM8994_VMID_NORMAL: + /* Startup bias, VMID ramp & buffer */ + snd_soc_component_update_bits(component, WM8994_ANTIPOP_2, + WM8994_BIAS_SRC | + WM8994_VMID_DISCH | + WM8994_STARTUP_BIAS_ENA | + WM8994_VMID_BUF_ENA | + WM8994_VMID_RAMP_MASK, + WM8994_BIAS_SRC | + WM8994_STARTUP_BIAS_ENA | + WM8994_VMID_BUF_ENA | + (0x2 << WM8994_VMID_RAMP_SHIFT)); + + /* Main bias enable, VMID=2x40k */ + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_1, + WM8994_BIAS_ENA | + WM8994_VMID_SEL_MASK, + WM8994_BIAS_ENA | 0x2); + + msleep(300); + + snd_soc_component_update_bits(component, WM8994_ANTIPOP_2, + WM8994_VMID_RAMP_MASK | + WM8994_BIAS_SRC, + 0); + break; + + case WM8994_VMID_FORCE: + /* Startup bias, slow VMID ramp & buffer */ + snd_soc_component_update_bits(component, WM8994_ANTIPOP_2, + WM8994_BIAS_SRC | + WM8994_VMID_DISCH | + WM8994_STARTUP_BIAS_ENA | + WM8994_VMID_BUF_ENA | + WM8994_VMID_RAMP_MASK, + WM8994_BIAS_SRC | + WM8994_STARTUP_BIAS_ENA | + WM8994_VMID_BUF_ENA | + (0x2 << WM8994_VMID_RAMP_SHIFT)); + + /* Main bias enable, VMID=2x40k */ + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_1, + WM8994_BIAS_ENA | + WM8994_VMID_SEL_MASK, + WM8994_BIAS_ENA | 0x2); + + msleep(400); + + snd_soc_component_update_bits(component, WM8994_ANTIPOP_2, + WM8994_VMID_RAMP_MASK | + WM8994_BIAS_SRC, + 0); + break; + } + } +} + +static void vmid_dereference(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + wm8994->vmid_refcount--; + + dev_dbg(component->dev, "Dereferencing VMID, refcount is now %d\n", + wm8994->vmid_refcount); + + if (wm8994->vmid_refcount == 0) { + if (wm8994->hubs.lineout1_se) + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_3, + WM8994_LINEOUT1N_ENA | + WM8994_LINEOUT1P_ENA, + WM8994_LINEOUT1N_ENA | + WM8994_LINEOUT1P_ENA); + + if (wm8994->hubs.lineout2_se) + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_3, + WM8994_LINEOUT2N_ENA | + WM8994_LINEOUT2P_ENA, + WM8994_LINEOUT2N_ENA | + WM8994_LINEOUT2P_ENA); + + /* Start discharging VMID */ + snd_soc_component_update_bits(component, WM8994_ANTIPOP_2, + WM8994_BIAS_SRC | + WM8994_VMID_DISCH, + WM8994_BIAS_SRC | + WM8994_VMID_DISCH); + + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_1, + WM8994_VMID_SEL_MASK, 0); + + msleep(400); + + /* Active discharge */ + snd_soc_component_update_bits(component, WM8994_ANTIPOP_1, + WM8994_LINEOUT1_DISCH | + WM8994_LINEOUT2_DISCH, + WM8994_LINEOUT1_DISCH | + WM8994_LINEOUT2_DISCH); + + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_3, + WM8994_LINEOUT1N_ENA | + WM8994_LINEOUT1P_ENA | + WM8994_LINEOUT2N_ENA | + WM8994_LINEOUT2P_ENA, 0); + + /* Switch off startup biases */ + snd_soc_component_update_bits(component, WM8994_ANTIPOP_2, + WM8994_BIAS_SRC | + WM8994_STARTUP_BIAS_ENA | + WM8994_VMID_BUF_ENA | + WM8994_VMID_RAMP_MASK, 0); + + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_1, + WM8994_VMID_SEL_MASK, 0); + } + + pm_runtime_put(component->dev); +} + +static int vmid_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + vmid_reference(component); + break; + + case SND_SOC_DAPM_POST_PMD: + vmid_dereference(component); + break; + } + + return 0; +} + +static bool wm8994_check_class_w_digital(struct snd_soc_component *component) +{ + int source = 0; /* GCC flow analysis can't track enable */ + int reg, reg_r; + + /* We also need the same AIF source for L/R and only one path */ + reg = snd_soc_component_read(component, WM8994_DAC1_LEFT_MIXER_ROUTING); + switch (reg) { + case WM8994_AIF2DACL_TO_DAC1L: + dev_vdbg(component->dev, "Class W source AIF2DAC\n"); + source = 2 << WM8994_CP_DYN_SRC_SEL_SHIFT; + break; + case WM8994_AIF1DAC2L_TO_DAC1L: + dev_vdbg(component->dev, "Class W source AIF1DAC2\n"); + source = 1 << WM8994_CP_DYN_SRC_SEL_SHIFT; + break; + case WM8994_AIF1DAC1L_TO_DAC1L: + dev_vdbg(component->dev, "Class W source AIF1DAC1\n"); + source = 0 << WM8994_CP_DYN_SRC_SEL_SHIFT; + break; + default: + dev_vdbg(component->dev, "DAC mixer setting: %x\n", reg); + return false; + } + + reg_r = snd_soc_component_read(component, WM8994_DAC1_RIGHT_MIXER_ROUTING); + if (reg_r != reg) { + dev_vdbg(component->dev, "Left and right DAC mixers different\n"); + return false; + } + + /* Set the source up */ + snd_soc_component_update_bits(component, WM8994_CLASS_W_1, + WM8994_CP_DYN_SRC_SEL_MASK, source); + + return true; +} + +static void wm8994_update_vu_bits(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int i; + + for (i = 0; i < ARRAY_SIZE(wm8994_vu_bits); i++) + snd_soc_component_write(component, wm8994_vu_bits[i].reg, + snd_soc_component_read(component, + wm8994_vu_bits[i].reg)); + if (control->type == WM1811) + return; + + for (i = 0; i < ARRAY_SIZE(wm8994_adc2_dac2_vu_bits); i++) + snd_soc_component_write(component, + wm8994_adc2_dac2_vu_bits[i].reg, + snd_soc_component_read(component, + wm8994_adc2_dac2_vu_bits[i].reg)); +} + +static int aif_mclk_set(struct snd_soc_component *component, int aif, bool enable) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + unsigned int offset, val, clk_idx; + int ret; + + if (aif) + offset = 4; + else + offset = 0; + + val = snd_soc_component_read(component, WM8994_AIF1_CLOCKING_1 + offset); + val &= WM8994_AIF1CLK_SRC_MASK; + + switch (val) { + case 0: + clk_idx = WM8994_MCLK1; + break; + case 1: + clk_idx = WM8994_MCLK2; + break; + default: + return 0; + } + + if (enable) { + ret = clk_prepare_enable(wm8994->mclk[clk_idx].clk); + if (ret < 0) { + dev_err(component->dev, "Failed to enable MCLK%d\n", + clk_idx); + return ret; + } + } else { + clk_disable_unprepare(wm8994->mclk[clk_idx].clk); + } + + return 0; +} + +static int aif1clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int mask = WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA; + int ret; + int dac; + int adc; + int val; + + switch (control->type) { + case WM8994: + case WM8958: + mask |= WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA; + break; + default: + break; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = aif_mclk_set(component, 0, true); + if (ret < 0) + return ret; + + /* Don't enable timeslot 2 if not in use */ + if (wm8994->channels[0] <= 2) + mask &= ~(WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA); + + val = snd_soc_component_read(component, WM8994_AIF1_CONTROL_1); + if ((val & WM8994_AIF1ADCL_SRC) && + (val & WM8994_AIF1ADCR_SRC)) + adc = WM8994_AIF1ADC1R_ENA | WM8994_AIF1ADC2R_ENA; + else if (!(val & WM8994_AIF1ADCL_SRC) && + !(val & WM8994_AIF1ADCR_SRC)) + adc = WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC2L_ENA; + else + adc = WM8994_AIF1ADC1R_ENA | WM8994_AIF1ADC2R_ENA | + WM8994_AIF1ADC1L_ENA | WM8994_AIF1ADC2L_ENA; + + val = snd_soc_component_read(component, WM8994_AIF1_CONTROL_2); + if ((val & WM8994_AIF1DACL_SRC) && + (val & WM8994_AIF1DACR_SRC)) + dac = WM8994_AIF1DAC1R_ENA | WM8994_AIF1DAC2R_ENA; + else if (!(val & WM8994_AIF1DACL_SRC) && + !(val & WM8994_AIF1DACR_SRC)) + dac = WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC2L_ENA; + else + dac = WM8994_AIF1DAC1R_ENA | WM8994_AIF1DAC2R_ENA | + WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC2L_ENA; + + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4, + mask, adc); + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5, + mask, dac); + snd_soc_component_update_bits(component, WM8994_CLOCKING_1, + WM8994_AIF1DSPCLK_ENA | + WM8994_SYSDSPCLK_ENA, + WM8994_AIF1DSPCLK_ENA | + WM8994_SYSDSPCLK_ENA); + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4, mask, + WM8994_AIF1ADC1R_ENA | + WM8994_AIF1ADC1L_ENA | + WM8994_AIF1ADC2R_ENA | + WM8994_AIF1ADC2L_ENA); + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5, mask, + WM8994_AIF1DAC1R_ENA | + WM8994_AIF1DAC1L_ENA | + WM8994_AIF1DAC2R_ENA | + WM8994_AIF1DAC2L_ENA); + break; + + case SND_SOC_DAPM_POST_PMU: + wm8994_update_vu_bits(component); + break; + + case SND_SOC_DAPM_PRE_PMD: + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5, + mask, 0); + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4, + mask, 0); + + val = snd_soc_component_read(component, WM8994_CLOCKING_1); + if (val & WM8994_AIF2DSPCLK_ENA) + val = WM8994_SYSDSPCLK_ENA; + else + val = 0; + snd_soc_component_update_bits(component, WM8994_CLOCKING_1, + WM8994_SYSDSPCLK_ENA | + WM8994_AIF1DSPCLK_ENA, val); + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + aif_mclk_set(component, 0, false); + break; + } + + return 0; +} + +static int aif2clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int ret; + int dac; + int adc; + int val; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = aif_mclk_set(component, 1, true); + if (ret < 0) + return ret; + + val = snd_soc_component_read(component, WM8994_AIF2_CONTROL_1); + if ((val & WM8994_AIF2ADCL_SRC) && + (val & WM8994_AIF2ADCR_SRC)) + adc = WM8994_AIF2ADCR_ENA; + else if (!(val & WM8994_AIF2ADCL_SRC) && + !(val & WM8994_AIF2ADCR_SRC)) + adc = WM8994_AIF2ADCL_ENA; + else + adc = WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA; + + + val = snd_soc_component_read(component, WM8994_AIF2_CONTROL_2); + if ((val & WM8994_AIF2DACL_SRC) && + (val & WM8994_AIF2DACR_SRC)) + dac = WM8994_AIF2DACR_ENA; + else if (!(val & WM8994_AIF2DACL_SRC) && + !(val & WM8994_AIF2DACR_SRC)) + dac = WM8994_AIF2DACL_ENA; + else + dac = WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA; + + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4, + WM8994_AIF2ADCL_ENA | + WM8994_AIF2ADCR_ENA, adc); + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5, + WM8994_AIF2DACL_ENA | + WM8994_AIF2DACR_ENA, dac); + snd_soc_component_update_bits(component, WM8994_CLOCKING_1, + WM8994_AIF2DSPCLK_ENA | + WM8994_SYSDSPCLK_ENA, + WM8994_AIF2DSPCLK_ENA | + WM8994_SYSDSPCLK_ENA); + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4, + WM8994_AIF2ADCL_ENA | + WM8994_AIF2ADCR_ENA, + WM8994_AIF2ADCL_ENA | + WM8994_AIF2ADCR_ENA); + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5, + WM8994_AIF2DACL_ENA | + WM8994_AIF2DACR_ENA, + WM8994_AIF2DACL_ENA | + WM8994_AIF2DACR_ENA); + break; + + case SND_SOC_DAPM_POST_PMU: + wm8994_update_vu_bits(component); + break; + + case SND_SOC_DAPM_PRE_PMD: + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5, + WM8994_AIF2DACL_ENA | + WM8994_AIF2DACR_ENA, 0); + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_4, + WM8994_AIF2ADCL_ENA | + WM8994_AIF2ADCR_ENA, 0); + + val = snd_soc_component_read(component, WM8994_CLOCKING_1); + if (val & WM8994_AIF1DSPCLK_ENA) + val = WM8994_SYSDSPCLK_ENA; + else + val = 0; + snd_soc_component_update_bits(component, WM8994_CLOCKING_1, + WM8994_SYSDSPCLK_ENA | + WM8994_AIF2DSPCLK_ENA, val); + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + aif_mclk_set(component, 1, false); + break; + } + + return 0; +} + +static int aif1clk_late_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm8994->aif1clk_enable = 1; + break; + case SND_SOC_DAPM_POST_PMD: + wm8994->aif1clk_disable = 1; + break; + } + + return 0; +} + +static int aif2clk_late_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm8994->aif2clk_enable = 1; + break; + case SND_SOC_DAPM_POST_PMD: + wm8994->aif2clk_disable = 1; + break; + } + + return 0; +} + +static int late_enable_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wm8994->aif1clk_enable) { + aif1clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMU); + snd_soc_component_update_bits(component, WM8994_AIF1_CLOCKING_1, + WM8994_AIF1CLK_ENA_MASK, + WM8994_AIF1CLK_ENA); + aif1clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMU); + wm8994->aif1clk_enable = 0; + } + if (wm8994->aif2clk_enable) { + aif2clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMU); + snd_soc_component_update_bits(component, WM8994_AIF2_CLOCKING_1, + WM8994_AIF2CLK_ENA_MASK, + WM8994_AIF2CLK_ENA); + aif2clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMU); + wm8994->aif2clk_enable = 0; + } + break; + } + + /* We may also have postponed startup of DSP, handle that. */ + wm8958_aif_ev(w, kcontrol, event); + + return 0; +} + +static int late_disable_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + if (wm8994->aif1clk_disable) { + aif1clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMD); + snd_soc_component_update_bits(component, WM8994_AIF1_CLOCKING_1, + WM8994_AIF1CLK_ENA_MASK, 0); + aif1clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMD); + wm8994->aif1clk_disable = 0; + } + if (wm8994->aif2clk_disable) { + aif2clk_ev(w, kcontrol, SND_SOC_DAPM_PRE_PMD); + snd_soc_component_update_bits(component, WM8994_AIF2_CLOCKING_1, + WM8994_AIF2CLK_ENA_MASK, 0); + aif2clk_ev(w, kcontrol, SND_SOC_DAPM_POST_PMD); + wm8994->aif2clk_disable = 0; + } + break; + } + + return 0; +} + +static int adc_mux_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + late_enable_ev(w, kcontrol, event); + return 0; +} + +static int micbias_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + late_enable_ev(w, kcontrol, event); + return 0; +} + +static int dac_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int mask = 1 << w->shift; + + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_5, + mask, mask); + return 0; +} + +static const char *adc_mux_text[] = { + "ADC", + "DMIC", +}; + +static SOC_ENUM_SINGLE_VIRT_DECL(adc_enum, adc_mux_text); + +static const struct snd_kcontrol_new adcl_mux = + SOC_DAPM_ENUM("ADCL Mux", adc_enum); + +static const struct snd_kcontrol_new adcr_mux = + SOC_DAPM_ENUM("ADCR Mux", adc_enum); + +static const struct snd_kcontrol_new left_speaker_mixer[] = { +SOC_DAPM_SINGLE("DAC2 Switch", WM8994_SPEAKER_MIXER, 9, 1, 0), +SOC_DAPM_SINGLE("Input Switch", WM8994_SPEAKER_MIXER, 7, 1, 0), +SOC_DAPM_SINGLE("IN1LP Switch", WM8994_SPEAKER_MIXER, 5, 1, 0), +SOC_DAPM_SINGLE("Output Switch", WM8994_SPEAKER_MIXER, 3, 1, 0), +SOC_DAPM_SINGLE("DAC1 Switch", WM8994_SPEAKER_MIXER, 1, 1, 0), +}; + +static const struct snd_kcontrol_new right_speaker_mixer[] = { +SOC_DAPM_SINGLE("DAC2 Switch", WM8994_SPEAKER_MIXER, 8, 1, 0), +SOC_DAPM_SINGLE("Input Switch", WM8994_SPEAKER_MIXER, 6, 1, 0), +SOC_DAPM_SINGLE("IN1RP Switch", WM8994_SPEAKER_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("Output Switch", WM8994_SPEAKER_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("DAC1 Switch", WM8994_SPEAKER_MIXER, 0, 1, 0), +}; + +/* Debugging; dump chip status after DAPM transitions */ +static int post_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + dev_dbg(component->dev, "SRC status: %x\n", + snd_soc_component_read(component, + WM8994_RATE_STATUS)); + return 0; +} + +static const struct snd_kcontrol_new aif1adc1l_mix[] = { +SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8994_AIF1_ADC1_LEFT_MIXER_ROUTING, + 1, 1, 0), +SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC1_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif1adc1r_mix[] = { +SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING, + 1, 1, 0), +SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif1adc2l_mix[] = { +SOC_DAPM_SINGLE("DMIC Switch", WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING, + 1, 1, 0), +SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC2_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif1adc2r_mix[] = { +SOC_DAPM_SINGLE("DMIC Switch", WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING, + 1, 1, 0), +SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC2_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif2dac2l_mix[] = { +SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING, + 5, 1, 0), +SOC_DAPM_SINGLE("Left Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING, + 4, 1, 0), +SOC_DAPM_SINGLE("AIF2 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING, + 2, 1, 0), +SOC_DAPM_SINGLE("AIF1.2 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING, + 1, 1, 0), +SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif2dac2r_mix[] = { +SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, + 5, 1, 0), +SOC_DAPM_SINGLE("Left Sidetone Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, + 4, 1, 0), +SOC_DAPM_SINGLE("AIF2 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, + 2, 1, 0), +SOC_DAPM_SINGLE("AIF1.2 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, + 1, 1, 0), +SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +#define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \ + SOC_SINGLE_EXT(xname, reg, shift, max, invert, \ + snd_soc_dapm_get_volsw, wm8994_put_class_w) + +static int wm8994_put_class_w(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + int ret; + + ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); + + wm_hubs_update_class_w(component); + + return ret; +} + +static const struct snd_kcontrol_new dac1l_mix[] = { +WM8994_CLASS_W_SWITCH("Right Sidetone Switch", WM8994_DAC1_LEFT_MIXER_ROUTING, + 5, 1, 0), +WM8994_CLASS_W_SWITCH("Left Sidetone Switch", WM8994_DAC1_LEFT_MIXER_ROUTING, + 4, 1, 0), +WM8994_CLASS_W_SWITCH("AIF2 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING, + 2, 1, 0), +WM8994_CLASS_W_SWITCH("AIF1.2 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING, + 1, 1, 0), +WM8994_CLASS_W_SWITCH("AIF1.1 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new dac1r_mix[] = { +WM8994_CLASS_W_SWITCH("Right Sidetone Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING, + 5, 1, 0), +WM8994_CLASS_W_SWITCH("Left Sidetone Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING, + 4, 1, 0), +WM8994_CLASS_W_SWITCH("AIF2 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING, + 2, 1, 0), +WM8994_CLASS_W_SWITCH("AIF1.2 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING, + 1, 1, 0), +WM8994_CLASS_W_SWITCH("AIF1.1 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const char *sidetone_text[] = { + "ADC/DMIC1", "DMIC2", +}; + +static SOC_ENUM_SINGLE_DECL(sidetone1_enum, + WM8994_SIDETONE, 0, sidetone_text); + +static const struct snd_kcontrol_new sidetone1_mux = + SOC_DAPM_ENUM("Left Sidetone Mux", sidetone1_enum); + +static SOC_ENUM_SINGLE_DECL(sidetone2_enum, + WM8994_SIDETONE, 1, sidetone_text); + +static const struct snd_kcontrol_new sidetone2_mux = + SOC_DAPM_ENUM("Right Sidetone Mux", sidetone2_enum); + +static const char *aif1dac_text[] = { + "AIF1DACDAT", "AIF3DACDAT", +}; + +static const char *loopback_text[] = { + "None", "ADCDAT", +}; + +static SOC_ENUM_SINGLE_DECL(aif1_loopback_enum, + WM8994_AIF1_CONTROL_2, + WM8994_AIF1_LOOPBACK_SHIFT, + loopback_text); + +static const struct snd_kcontrol_new aif1_loopback = + SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum); + +static SOC_ENUM_SINGLE_DECL(aif2_loopback_enum, + WM8994_AIF2_CONTROL_2, + WM8994_AIF2_LOOPBACK_SHIFT, + loopback_text); + +static const struct snd_kcontrol_new aif2_loopback = + SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum); + +static SOC_ENUM_SINGLE_DECL(aif1dac_enum, + WM8994_POWER_MANAGEMENT_6, 0, aif1dac_text); + +static const struct snd_kcontrol_new aif1dac_mux = + SOC_DAPM_ENUM("AIF1DAC Mux", aif1dac_enum); + +static const char *aif2dac_text[] = { + "AIF2DACDAT", "AIF3DACDAT", +}; + +static SOC_ENUM_SINGLE_DECL(aif2dac_enum, + WM8994_POWER_MANAGEMENT_6, 1, aif2dac_text); + +static const struct snd_kcontrol_new aif2dac_mux = + SOC_DAPM_ENUM("AIF2DAC Mux", aif2dac_enum); + +static const char *aif2adc_text[] = { + "AIF2ADCDAT", "AIF3DACDAT", +}; + +static SOC_ENUM_SINGLE_DECL(aif2adc_enum, + WM8994_POWER_MANAGEMENT_6, 2, aif2adc_text); + +static const struct snd_kcontrol_new aif2adc_mux = + SOC_DAPM_ENUM("AIF2ADC Mux", aif2adc_enum); + +static const char *aif3adc_text[] = { + "AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT", "Mono PCM", +}; + +static SOC_ENUM_SINGLE_DECL(wm8994_aif3adc_enum, + WM8994_POWER_MANAGEMENT_6, 3, aif3adc_text); + +static const struct snd_kcontrol_new wm8994_aif3adc_mux = + SOC_DAPM_ENUM("AIF3ADC Mux", wm8994_aif3adc_enum); + +static SOC_ENUM_SINGLE_DECL(wm8958_aif3adc_enum, + WM8994_POWER_MANAGEMENT_6, 3, aif3adc_text); + +static const struct snd_kcontrol_new wm8958_aif3adc_mux = + SOC_DAPM_ENUM("AIF3ADC Mux", wm8958_aif3adc_enum); + +static const char *mono_pcm_out_text[] = { + "None", "AIF2ADCL", "AIF2ADCR", +}; + +static SOC_ENUM_SINGLE_DECL(mono_pcm_out_enum, + WM8994_POWER_MANAGEMENT_6, 9, mono_pcm_out_text); + +static const struct snd_kcontrol_new mono_pcm_out_mux = + SOC_DAPM_ENUM("Mono PCM Out Mux", mono_pcm_out_enum); + +static const char *aif2dac_src_text[] = { + "AIF2", "AIF3", +}; + +/* Note that these two control shouldn't be simultaneously switched to AIF3 */ +static SOC_ENUM_SINGLE_DECL(aif2dacl_src_enum, + WM8994_POWER_MANAGEMENT_6, 7, aif2dac_src_text); + +static const struct snd_kcontrol_new aif2dacl_src_mux = + SOC_DAPM_ENUM("AIF2DACL Mux", aif2dacl_src_enum); + +static SOC_ENUM_SINGLE_DECL(aif2dacr_src_enum, + WM8994_POWER_MANAGEMENT_6, 8, aif2dac_src_text); + +static const struct snd_kcontrol_new aif2dacr_src_mux = + SOC_DAPM_ENUM("AIF2DACR Mux", aif2dacr_src_enum); + +static const struct snd_soc_dapm_widget wm8994_lateclk_revd_widgets[] = { +SND_SOC_DAPM_SUPPLY("AIF1CLK", SND_SOC_NOPM, 0, 0, aif1clk_late_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF2CLK", SND_SOC_NOPM, 0, 0, aif2clk_late_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_PGA_E("Late DAC1L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC1R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC2L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC2R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_MIXER_E("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0, + left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer), + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_MIXER_E("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, + right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer), + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_MUX_E("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_MUX_E("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev) +}; + +static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = { +SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, aif1clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, aif2clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0, + left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), +SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0, + right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), +SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpl_mux), +SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux), +}; + +static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = { +SND_SOC_DAPM_DAC_E("DAC2L", NULL, SND_SOC_NOPM, 3, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC2R", NULL, SND_SOC_NOPM, 2, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC1L", NULL, SND_SOC_NOPM, 1, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC1R", NULL, SND_SOC_NOPM, 0, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +}; + +static const struct snd_soc_dapm_widget wm8994_dac_widgets[] = { +SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0), +SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0), +SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0), +SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0), +}; + +static const struct snd_soc_dapm_widget wm8994_adc_revd_widgets[] = { +SND_SOC_DAPM_MUX_E("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux, + adc_mux_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_MUX_E("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux, + adc_mux_ev, SND_SOC_DAPM_PRE_PMU), +}; + +static const struct snd_soc_dapm_widget wm8994_adc_widgets[] = { +SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux), +SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux), +}; + +static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("DMIC1DAT"), +SND_SOC_DAPM_INPUT("DMIC2DAT"), +SND_SOC_DAPM_INPUT("Clock"), + +SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev, + SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_SUPPLY("VMID", SND_SOC_NOPM, 0, 0, vmid_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_SUPPLY("DSP1CLK", SND_SOC_NOPM, 3, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSP2CLK", SND_SOC_NOPM, 2, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DSPINTCLK", SND_SOC_NOPM, 1, 0, NULL, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", NULL, + 0, SND_SOC_NOPM, 9, 0), +SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", NULL, + 0, SND_SOC_NOPM, 8, 0), +SND_SOC_DAPM_AIF_IN_E("AIF1DAC1L", NULL, 0, + SND_SOC_NOPM, 9, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_AIF_IN_E("AIF1DAC1R", NULL, 0, + SND_SOC_NOPM, 8, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", NULL, + 0, SND_SOC_NOPM, 11, 0), +SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", NULL, + 0, SND_SOC_NOPM, 10, 0), +SND_SOC_DAPM_AIF_IN_E("AIF1DAC2L", NULL, 0, + SND_SOC_NOPM, 11, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_AIF_IN_E("AIF1DAC2R", NULL, 0, + SND_SOC_NOPM, 10, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0, + aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)), +SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0, + aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)), + +SND_SOC_DAPM_MIXER("AIF1ADC2L Mixer", SND_SOC_NOPM, 0, 0, + aif1adc2l_mix, ARRAY_SIZE(aif1adc2l_mix)), +SND_SOC_DAPM_MIXER("AIF1ADC2R Mixer", SND_SOC_NOPM, 0, 0, + aif1adc2r_mix, ARRAY_SIZE(aif1adc2r_mix)), + +SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0, + aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)), +SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0, + aif2dac2r_mix, ARRAY_SIZE(aif2dac2r_mix)), + +SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &sidetone1_mux), +SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &sidetone2_mux), + +SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0, + dac1l_mix, ARRAY_SIZE(dac1l_mix)), +SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0, + dac1r_mix, ARRAY_SIZE(dac1r_mix)), + +SND_SOC_DAPM_AIF_OUT("AIF2ADCL", NULL, 0, + SND_SOC_NOPM, 13, 0), +SND_SOC_DAPM_AIF_OUT("AIF2ADCR", NULL, 0, + SND_SOC_NOPM, 12, 0), +SND_SOC_DAPM_AIF_IN_E("AIF2DACL", NULL, 0, + SND_SOC_NOPM, 13, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_AIF_IN_E("AIF2DACR", NULL, 0, + SND_SOC_NOPM, 12, 0, wm8958_aif_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_AIF_IN("AIF1DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIF2DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux), +SND_SOC_DAPM_MUX("AIF2DAC Mux", SND_SOC_NOPM, 0, 0, &aif2dac_mux), +SND_SOC_DAPM_MUX("AIF2ADC Mux", SND_SOC_NOPM, 0, 0, &aif2adc_mux), + +SND_SOC_DAPM_AIF_IN("AIF3DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIF3ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_SUPPLY("TOCLK", WM8994_CLOCKING_1, 4, 0, NULL, 0), + +SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8994_POWER_MANAGEMENT_4, 5, 0), +SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8994_POWER_MANAGEMENT_4, 4, 0), +SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8994_POWER_MANAGEMENT_4, 3, 0), +SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0), + +/* Power is done with the muxes since the ADC power also controls the + * downsampling chain, the chip will automatically manage the analogue + * specific portions. + */ +SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_MUX("AIF1 Loopback", SND_SOC_NOPM, 0, 0, &aif1_loopback), +SND_SOC_DAPM_MUX("AIF2 Loopback", SND_SOC_NOPM, 0, 0, &aif2_loopback), + +SND_SOC_DAPM_POST("Debug log", post_ev), +}; + +static const struct snd_soc_dapm_widget wm8994_specific_dapm_widgets[] = { +SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8994_aif3adc_mux), +}; + +static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("AIF3", WM8994_POWER_MANAGEMENT_6, 5, 1, NULL, 0), +SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux), +SND_SOC_DAPM_MUX("AIF2DACL Mux", SND_SOC_NOPM, 0, 0, &aif2dacl_src_mux), +SND_SOC_DAPM_MUX("AIF2DACR Mux", SND_SOC_NOPM, 0, 0, &aif2dacr_src_mux), +SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8958_aif3adc_mux), +}; + +static const struct snd_soc_dapm_route intercon[] = { + { "CLK_SYS", NULL, "AIF1CLK", check_clk_sys }, + { "CLK_SYS", NULL, "AIF2CLK", check_clk_sys }, + + { "DSP1CLK", NULL, "CLK_SYS" }, + { "DSP2CLK", NULL, "CLK_SYS" }, + { "DSPINTCLK", NULL, "CLK_SYS" }, + + { "AIF1ADC1L", NULL, "AIF1CLK" }, + { "AIF1ADC1L", NULL, "DSP1CLK" }, + { "AIF1ADC1R", NULL, "AIF1CLK" }, + { "AIF1ADC1R", NULL, "DSP1CLK" }, + { "AIF1ADC1R", NULL, "DSPINTCLK" }, + + { "AIF1DAC1L", NULL, "AIF1CLK" }, + { "AIF1DAC1L", NULL, "DSP1CLK" }, + { "AIF1DAC1R", NULL, "AIF1CLK" }, + { "AIF1DAC1R", NULL, "DSP1CLK" }, + { "AIF1DAC1R", NULL, "DSPINTCLK" }, + + { "AIF1ADC2L", NULL, "AIF1CLK" }, + { "AIF1ADC2L", NULL, "DSP1CLK" }, + { "AIF1ADC2R", NULL, "AIF1CLK" }, + { "AIF1ADC2R", NULL, "DSP1CLK" }, + { "AIF1ADC2R", NULL, "DSPINTCLK" }, + + { "AIF1DAC2L", NULL, "AIF1CLK" }, + { "AIF1DAC2L", NULL, "DSP1CLK" }, + { "AIF1DAC2R", NULL, "AIF1CLK" }, + { "AIF1DAC2R", NULL, "DSP1CLK" }, + { "AIF1DAC2R", NULL, "DSPINTCLK" }, + + { "AIF2ADCL", NULL, "AIF2CLK" }, + { "AIF2ADCL", NULL, "DSP2CLK" }, + { "AIF2ADCR", NULL, "AIF2CLK" }, + { "AIF2ADCR", NULL, "DSP2CLK" }, + { "AIF2ADCR", NULL, "DSPINTCLK" }, + + { "AIF2DACL", NULL, "AIF2CLK" }, + { "AIF2DACL", NULL, "DSP2CLK" }, + { "AIF2DACR", NULL, "AIF2CLK" }, + { "AIF2DACR", NULL, "DSP2CLK" }, + { "AIF2DACR", NULL, "DSPINTCLK" }, + + { "DMIC1L", NULL, "DMIC1DAT" }, + { "DMIC1L", NULL, "CLK_SYS" }, + { "DMIC1R", NULL, "DMIC1DAT" }, + { "DMIC1R", NULL, "CLK_SYS" }, + { "DMIC2L", NULL, "DMIC2DAT" }, + { "DMIC2L", NULL, "CLK_SYS" }, + { "DMIC2R", NULL, "DMIC2DAT" }, + { "DMIC2R", NULL, "CLK_SYS" }, + + { "ADCL", NULL, "AIF1CLK" }, + { "ADCL", NULL, "DSP1CLK" }, + { "ADCL", NULL, "DSPINTCLK" }, + + { "ADCR", NULL, "AIF1CLK" }, + { "ADCR", NULL, "DSP1CLK" }, + { "ADCR", NULL, "DSPINTCLK" }, + + { "ADCL Mux", "ADC", "ADCL" }, + { "ADCL Mux", "DMIC", "DMIC1L" }, + { "ADCR Mux", "ADC", "ADCR" }, + { "ADCR Mux", "DMIC", "DMIC1R" }, + + { "DAC1L", NULL, "AIF1CLK" }, + { "DAC1L", NULL, "DSP1CLK" }, + { "DAC1L", NULL, "DSPINTCLK" }, + + { "DAC1R", NULL, "AIF1CLK" }, + { "DAC1R", NULL, "DSP1CLK" }, + { "DAC1R", NULL, "DSPINTCLK" }, + + { "DAC2L", NULL, "AIF2CLK" }, + { "DAC2L", NULL, "DSP2CLK" }, + { "DAC2L", NULL, "DSPINTCLK" }, + + { "DAC2R", NULL, "AIF2DACR" }, + { "DAC2R", NULL, "AIF2CLK" }, + { "DAC2R", NULL, "DSP2CLK" }, + { "DAC2R", NULL, "DSPINTCLK" }, + + { "TOCLK", NULL, "CLK_SYS" }, + + { "AIF1DACDAT", NULL, "AIF1 Playback" }, + { "AIF2DACDAT", NULL, "AIF2 Playback" }, + { "AIF3DACDAT", NULL, "AIF3 Playback" }, + + { "AIF1 Capture", NULL, "AIF1ADCDAT" }, + { "AIF2 Capture", NULL, "AIF2ADCDAT" }, + { "AIF3 Capture", NULL, "AIF3ADCDAT" }, + + /* AIF1 outputs */ + { "AIF1ADC1L", NULL, "AIF1ADC1L Mixer" }, + { "AIF1ADC1L Mixer", "ADC/DMIC Switch", "ADCL Mux" }, + { "AIF1ADC1L Mixer", "AIF2 Switch", "AIF2DACL" }, + + { "AIF1ADC1R", NULL, "AIF1ADC1R Mixer" }, + { "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" }, + { "AIF1ADC1R Mixer", "AIF2 Switch", "AIF2DACR" }, + + { "AIF1ADC2L", NULL, "AIF1ADC2L Mixer" }, + { "AIF1ADC2L Mixer", "DMIC Switch", "DMIC2L" }, + { "AIF1ADC2L Mixer", "AIF2 Switch", "AIF2DACL" }, + + { "AIF1ADC2R", NULL, "AIF1ADC2R Mixer" }, + { "AIF1ADC2R Mixer", "DMIC Switch", "DMIC2R" }, + { "AIF1ADC2R Mixer", "AIF2 Switch", "AIF2DACR" }, + + /* Pin level routing for AIF3 */ + { "AIF1DAC1L", NULL, "AIF1DAC Mux" }, + { "AIF1DAC1R", NULL, "AIF1DAC Mux" }, + { "AIF1DAC2L", NULL, "AIF1DAC Mux" }, + { "AIF1DAC2R", NULL, "AIF1DAC Mux" }, + + { "AIF1DAC Mux", "AIF1DACDAT", "AIF1 Loopback" }, + { "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, + { "AIF2DAC Mux", "AIF2DACDAT", "AIF2 Loopback" }, + { "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, + { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" }, + { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" }, + { "AIF2ADC Mux", "AIF3DACDAT", "AIF3ADCDAT" }, + + /* DAC1 inputs */ + { "DAC1L Mixer", "AIF2 Switch", "AIF2DACL" }, + { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, + { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, + { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" }, + { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" }, + + { "DAC1R Mixer", "AIF2 Switch", "AIF2DACR" }, + { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, + { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, + { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" }, + { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" }, + + /* DAC2/AIF2 outputs */ + { "AIF2ADCL", NULL, "AIF2DAC2L Mixer" }, + { "AIF2DAC2L Mixer", "AIF2 Switch", "AIF2DACL" }, + { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, + { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, + { "AIF2DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" }, + { "AIF2DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" }, + + { "AIF2ADCR", NULL, "AIF2DAC2R Mixer" }, + { "AIF2DAC2R Mixer", "AIF2 Switch", "AIF2DACR" }, + { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, + { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, + { "AIF2DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" }, + { "AIF2DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" }, + + { "AIF1ADCDAT", NULL, "AIF1ADC1L" }, + { "AIF1ADCDAT", NULL, "AIF1ADC1R" }, + { "AIF1ADCDAT", NULL, "AIF1ADC2L" }, + { "AIF1ADCDAT", NULL, "AIF1ADC2R" }, + + { "AIF2ADCDAT", NULL, "AIF2ADC Mux" }, + + /* AIF3 output */ + { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC1L" }, + { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC1R" }, + { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC2L" }, + { "AIF3ADC Mux", "AIF1ADCDAT", "AIF1ADC2R" }, + { "AIF3ADC Mux", "AIF2ADCDAT", "AIF2ADCL" }, + { "AIF3ADC Mux", "AIF2ADCDAT", "AIF2ADCR" }, + { "AIF3ADC Mux", "AIF2DACDAT", "AIF2DACL" }, + { "AIF3ADC Mux", "AIF2DACDAT", "AIF2DACR" }, + + { "AIF3ADCDAT", NULL, "AIF3ADC Mux" }, + + /* Loopback */ + { "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" }, + { "AIF1 Loopback", "None", "AIF1DACDAT" }, + { "AIF2 Loopback", "ADCDAT", "AIF2ADCDAT" }, + { "AIF2 Loopback", "None", "AIF2DACDAT" }, + + /* Sidetone */ + { "Left Sidetone", "ADC/DMIC1", "ADCL Mux" }, + { "Left Sidetone", "DMIC2", "DMIC2L" }, + { "Right Sidetone", "ADC/DMIC1", "ADCR Mux" }, + { "Right Sidetone", "DMIC2", "DMIC2R" }, + + /* Output stages */ + { "Left Output Mixer", "DAC Switch", "DAC1L" }, + { "Right Output Mixer", "DAC Switch", "DAC1R" }, + + { "SPKL", "DAC1 Switch", "DAC1L" }, + { "SPKL", "DAC2 Switch", "DAC2L" }, + + { "SPKR", "DAC1 Switch", "DAC1R" }, + { "SPKR", "DAC2 Switch", "DAC2R" }, + + { "Left Headphone Mux", "DAC", "DAC1L" }, + { "Right Headphone Mux", "DAC", "DAC1R" }, +}; + +static const struct snd_soc_dapm_route wm8994_lateclk_revd_intercon[] = { + { "DAC1L", NULL, "Late DAC1L Enable PGA" }, + { "Late DAC1L Enable PGA", NULL, "DAC1L Mixer" }, + { "DAC1R", NULL, "Late DAC1R Enable PGA" }, + { "Late DAC1R Enable PGA", NULL, "DAC1R Mixer" }, + { "DAC2L", NULL, "Late DAC2L Enable PGA" }, + { "Late DAC2L Enable PGA", NULL, "AIF2DAC2L Mixer" }, + { "DAC2R", NULL, "Late DAC2R Enable PGA" }, + { "Late DAC2R Enable PGA", NULL, "AIF2DAC2R Mixer" } +}; + +static const struct snd_soc_dapm_route wm8994_lateclk_intercon[] = { + { "DAC1L", NULL, "DAC1L Mixer" }, + { "DAC1R", NULL, "DAC1R Mixer" }, + { "DAC2L", NULL, "AIF2DAC2L Mixer" }, + { "DAC2R", NULL, "AIF2DAC2R Mixer" }, +}; + +static const struct snd_soc_dapm_route wm8994_revd_intercon[] = { + { "AIF1DACDAT", NULL, "AIF2DACDAT" }, + { "AIF2DACDAT", NULL, "AIF1DACDAT" }, + { "AIF1ADCDAT", NULL, "AIF2ADCDAT" }, + { "AIF2ADCDAT", NULL, "AIF1ADCDAT" }, + { "MICBIAS1", NULL, "CLK_SYS" }, + { "MICBIAS1", NULL, "MICBIAS Supply" }, + { "MICBIAS2", NULL, "CLK_SYS" }, + { "MICBIAS2", NULL, "MICBIAS Supply" }, +}; + +static const struct snd_soc_dapm_route wm8994_intercon[] = { + { "AIF2DACL", NULL, "AIF2DAC Mux" }, + { "AIF2DACR", NULL, "AIF2DAC Mux" }, + { "MICBIAS1", NULL, "VMID" }, + { "MICBIAS2", NULL, "VMID" }, +}; + +static const struct snd_soc_dapm_route wm8958_intercon[] = { + { "AIF2DACL", NULL, "AIF2DACL Mux" }, + { "AIF2DACR", NULL, "AIF2DACR Mux" }, + + { "AIF2DACL Mux", "AIF2", "AIF2DAC Mux" }, + { "AIF2DACL Mux", "AIF3", "AIF3DACDAT" }, + { "AIF2DACR Mux", "AIF2", "AIF2DAC Mux" }, + { "AIF2DACR Mux", "AIF3", "AIF3DACDAT" }, + + { "AIF3DACDAT", NULL, "AIF3" }, + { "AIF3ADCDAT", NULL, "AIF3" }, + + { "Mono PCM Out Mux", "AIF2ADCL", "AIF2ADCL" }, + { "Mono PCM Out Mux", "AIF2ADCR", "AIF2ADCR" }, + + { "AIF3ADC Mux", "Mono PCM", "Mono PCM Out Mux" }, +}; + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +struct fll_div { + u16 outdiv; + u16 n; + u16 k; + u16 lambda; + u16 clk_ref_div; + u16 fll_fratio; +}; + +static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, + int freq_in, int freq_out) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, gcd_fll; + + pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out); + + /* Scale the input frequency down to <= 13.5MHz */ + fll->clk_ref_div = 0; + while (freq_in > 13500000) { + fll->clk_ref_div++; + freq_in /= 2; + + if (fll->clk_ref_div > 3) + return -EINVAL; + } + pr_debug("CLK_REF_DIV=%d, Fref=%dHz\n", fll->clk_ref_div, freq_in); + + /* Scale the output to give 90MHz<=Fvco<=100MHz */ + fll->outdiv = 3; + while (freq_out * (fll->outdiv + 1) < 90000000) { + fll->outdiv++; + if (fll->outdiv > 63) + return -EINVAL; + } + freq_out *= fll->outdiv + 1; + pr_debug("OUTDIV=%d, Fvco=%dHz\n", fll->outdiv, freq_out); + + if (freq_in > 1000000) { + fll->fll_fratio = 0; + } else if (freq_in > 256000) { + fll->fll_fratio = 1; + freq_in *= 2; + } else if (freq_in > 128000) { + fll->fll_fratio = 2; + freq_in *= 4; + } else if (freq_in > 64000) { + fll->fll_fratio = 3; + freq_in *= 8; + } else { + fll->fll_fratio = 4; + freq_in *= 16; + } + pr_debug("FLL_FRATIO=%d, Fref=%dHz\n", fll->fll_fratio, freq_in); + + /* Now, calculate N.K */ + Ndiv = freq_out / freq_in; + + fll->n = Ndiv; + Nmod = freq_out % freq_in; + pr_debug("Nmod=%d\n", Nmod); + + switch (control->type) { + case WM8994: + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, freq_in); + + K = Kpart & 0xFFFFFFFF; + + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + fll->k = K / 10; + fll->lambda = 0; + + pr_debug("N=%x K=%x\n", fll->n, fll->k); + break; + + default: + gcd_fll = gcd(freq_out, freq_in); + + fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll; + fll->lambda = freq_in / gcd_fll; + + } + + return 0; +} + +static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src, + unsigned int freq_in, unsigned int freq_out) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int reg_offset, ret; + struct fll_div fll; + u16 reg, clk1, aif_reg, aif_src; + unsigned long timeout; + bool was_enabled; + struct clk *mclk; + + switch (id) { + case WM8994_FLL1: + reg_offset = 0; + id = 0; + aif_src = 0x10; + break; + case WM8994_FLL2: + reg_offset = 0x20; + id = 1; + aif_src = 0x18; + break; + default: + return -EINVAL; + } + + reg = snd_soc_component_read(component, WM8994_FLL1_CONTROL_1 + reg_offset); + was_enabled = reg & WM8994_FLL1_ENA; + + switch (src) { + case 0: + /* Allow no source specification when stopping */ + if (freq_out) + return -EINVAL; + src = wm8994->fll[id].src; + break; + case WM8994_FLL_SRC_MCLK1: + case WM8994_FLL_SRC_MCLK2: + case WM8994_FLL_SRC_LRCLK: + case WM8994_FLL_SRC_BCLK: + break; + case WM8994_FLL_SRC_INTERNAL: + freq_in = 12000000; + freq_out = 12000000; + break; + default: + return -EINVAL; + } + + /* Are we changing anything? */ + if (wm8994->fll[id].src == src && + wm8994->fll[id].in == freq_in && wm8994->fll[id].out == freq_out) + return 0; + + /* If we're stopping the FLL redo the old config - no + * registers will actually be written but we avoid GCC flow + * analysis bugs spewing warnings. + */ + if (freq_out) + ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out); + else + ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in, + wm8994->fll[id].out); + if (ret < 0) + return ret; + + /* Make sure that we're not providing SYSCLK right now */ + clk1 = snd_soc_component_read(component, WM8994_CLOCKING_1); + if (clk1 & WM8994_SYSCLK_SRC) + aif_reg = WM8994_AIF2_CLOCKING_1; + else + aif_reg = WM8994_AIF1_CLOCKING_1; + reg = snd_soc_component_read(component, aif_reg); + + if ((reg & WM8994_AIF1CLK_ENA) && + (reg & WM8994_AIF1CLK_SRC_MASK) == aif_src) { + dev_err(component->dev, "FLL%d is currently providing SYSCLK\n", + id + 1); + return -EBUSY; + } + + /* We always need to disable the FLL while reconfiguring */ + snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset, + WM8994_FLL1_ENA, 0); + + /* Disable MCLK if needed before we possibly change to new clock parent */ + if (was_enabled) { + reg = snd_soc_component_read(component, WM8994_FLL1_CONTROL_5 + + reg_offset); + reg = ((reg & WM8994_FLL1_REFCLK_SRC_MASK) + >> WM8994_FLL1_REFCLK_SRC_SHIFT) + 1; + + switch (reg) { + case WM8994_FLL_SRC_MCLK1: + mclk = wm8994->mclk[WM8994_MCLK1].clk; + break; + case WM8994_FLL_SRC_MCLK2: + mclk = wm8994->mclk[WM8994_MCLK2].clk; + break; + default: + mclk = NULL; + } + + clk_disable_unprepare(mclk); + } + + if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK && + freq_in == freq_out && freq_out) { + dev_dbg(component->dev, "Bypassing FLL%d\n", id + 1); + snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_5 + reg_offset, + WM8958_FLL1_BYP, WM8958_FLL1_BYP); + goto out; + } + + reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) | + (fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT); + snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_2 + reg_offset, + WM8994_FLL1_OUTDIV_MASK | + WM8994_FLL1_FRATIO_MASK, reg); + + snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_3 + reg_offset, + WM8994_FLL1_K_MASK, fll.k); + + snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_4 + reg_offset, + WM8994_FLL1_N_MASK, + fll.n << WM8994_FLL1_N_SHIFT); + + if (fll.lambda) { + snd_soc_component_update_bits(component, WM8958_FLL1_EFS_1 + reg_offset, + WM8958_FLL1_LAMBDA_MASK, + fll.lambda); + snd_soc_component_update_bits(component, WM8958_FLL1_EFS_2 + reg_offset, + WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA); + } else { + snd_soc_component_update_bits(component, WM8958_FLL1_EFS_2 + reg_offset, + WM8958_FLL1_EFS_ENA, 0); + } + + snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_5 + reg_offset, + WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP | + WM8994_FLL1_REFCLK_DIV_MASK | + WM8994_FLL1_REFCLK_SRC_MASK, + ((src == WM8994_FLL_SRC_INTERNAL) + << WM8994_FLL1_FRC_NCO_SHIFT) | + (fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) | + (src - 1)); + + /* Clear any pending completion from a previous failure */ + try_wait_for_completion(&wm8994->fll_locked[id]); + + switch (src) { + case WM8994_FLL_SRC_MCLK1: + mclk = wm8994->mclk[WM8994_MCLK1].clk; + break; + case WM8994_FLL_SRC_MCLK2: + mclk = wm8994->mclk[WM8994_MCLK2].clk; + break; + default: + mclk = NULL; + } + + /* Enable (with fractional mode if required) */ + if (freq_out) { + ret = clk_prepare_enable(mclk); + if (ret < 0) { + dev_err(component->dev, "Failed to enable MCLK for FLL%d\n", + id + 1); + return ret; + } + + /* Enable VMID if we need it */ + if (!was_enabled) { + + active_reference(component); + + switch (control->type) { + case WM8994: + vmid_reference(component); + break; + case WM8958: + if (control->revision < 1) + vmid_reference(component); + break; + default: + break; + } + } + + reg = WM8994_FLL1_ENA; + + if (fll.k) + reg |= WM8994_FLL1_FRAC; + if (src == WM8994_FLL_SRC_INTERNAL) + reg |= WM8994_FLL1_OSC_ENA; + + snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset, + WM8994_FLL1_ENA | WM8994_FLL1_OSC_ENA | + WM8994_FLL1_FRAC, reg); + + if (wm8994->fll_locked_irq) { + timeout = wait_for_completion_timeout(&wm8994->fll_locked[id], + msecs_to_jiffies(10)); + if (timeout == 0) + dev_warn(component->dev, + "Timed out waiting for FLL lock\n"); + } else { + msleep(5); + } + } else { + if (was_enabled) { + switch (control->type) { + case WM8994: + vmid_dereference(component); + break; + case WM8958: + if (control->revision < 1) + vmid_dereference(component); + break; + default: + break; + } + + active_dereference(component); + } + } + +out: + wm8994->fll[id].in = freq_in; + wm8994->fll[id].out = freq_out; + wm8994->fll[id].src = src; + + configure_clock(component); + + /* + * If SYSCLK will be less than 50kHz adjust AIFnCLK dividers + * for detection. + */ + if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) { + dev_dbg(component->dev, "Configuring AIFs for 128fs\n"); + + wm8994->aifdiv[0] = snd_soc_component_read(component, WM8994_AIF1_RATE) + & WM8994_AIF1CLK_RATE_MASK; + wm8994->aifdiv[1] = snd_soc_component_read(component, WM8994_AIF2_RATE) + & WM8994_AIF1CLK_RATE_MASK; + + snd_soc_component_update_bits(component, WM8994_AIF1_RATE, + WM8994_AIF1CLK_RATE_MASK, 0x1); + snd_soc_component_update_bits(component, WM8994_AIF2_RATE, + WM8994_AIF2CLK_RATE_MASK, 0x1); + } else if (wm8994->aifdiv[0]) { + snd_soc_component_update_bits(component, WM8994_AIF1_RATE, + WM8994_AIF1CLK_RATE_MASK, + wm8994->aifdiv[0]); + snd_soc_component_update_bits(component, WM8994_AIF2_RATE, + WM8994_AIF2CLK_RATE_MASK, + wm8994->aifdiv[1]); + + wm8994->aifdiv[0] = 0; + wm8994->aifdiv[1] = 0; + } + + return 0; +} + +static irqreturn_t wm8994_fll_locked_irq(int irq, void *data) +{ + struct completion *completion = data; + + complete(completion); + + return IRQ_HANDLED; +} + +static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 }; + +static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src, + unsigned int freq_in, unsigned int freq_out) +{ + return _wm8994_set_fll(dai->component, id, src, freq_in, freq_out); +} + +static int wm8994_set_mclk_rate(struct wm8994_priv *wm8994, unsigned int id, + unsigned int *freq) +{ + int ret; + + if (!wm8994->mclk[id].clk || *freq == wm8994->mclk_rate[id]) + return 0; + + ret = clk_set_rate(wm8994->mclk[id].clk, *freq); + if (ret < 0) + return ret; + + *freq = clk_get_rate(wm8994->mclk[id].clk); + + return 0; +} + +static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + int ret, i; + + switch (dai->id) { + case 1: + case 2: + break; + + default: + /* AIF3 shares clocking with AIF1/2 */ + return -EINVAL; + } + + switch (clk_id) { + case WM8994_SYSCLK_MCLK1: + wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1; + + ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq); + if (ret < 0) + return ret; + + wm8994->mclk_rate[0] = freq; + dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n", + dai->id, freq); + break; + + case WM8994_SYSCLK_MCLK2: + /* TODO: Set GPIO AF */ + wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2; + + ret = wm8994_set_mclk_rate(wm8994, dai->id - 1, &freq); + if (ret < 0) + return ret; + + wm8994->mclk_rate[1] = freq; + dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n", + dai->id, freq); + break; + + case WM8994_SYSCLK_FLL1: + wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_FLL1; + dev_dbg(dai->dev, "AIF%d using FLL1\n", dai->id); + break; + + case WM8994_SYSCLK_FLL2: + wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_FLL2; + dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id); + break; + + case WM8994_SYSCLK_OPCLK: + /* Special case - a division (times 10) is given and + * no effect on main clocking. + */ + if (freq) { + for (i = 0; i < ARRAY_SIZE(opclk_divs); i++) + if (opclk_divs[i] == freq) + break; + if (i == ARRAY_SIZE(opclk_divs)) + return -EINVAL; + snd_soc_component_update_bits(component, WM8994_CLOCKING_2, + WM8994_OPCLK_DIV_MASK, i); + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_2, + WM8994_OPCLK_ENA, WM8994_OPCLK_ENA); + } else { + snd_soc_component_update_bits(component, WM8994_POWER_MANAGEMENT_2, + WM8994_OPCLK_ENA, 0); + } + break; + + default: + return -EINVAL; + } + + configure_clock(component); + + /* + * If SYSCLK will be less than 50kHz adjust AIFnCLK dividers + * for detection. + */ + if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) { + dev_dbg(component->dev, "Configuring AIFs for 128fs\n"); + + wm8994->aifdiv[0] = snd_soc_component_read(component, WM8994_AIF1_RATE) + & WM8994_AIF1CLK_RATE_MASK; + wm8994->aifdiv[1] = snd_soc_component_read(component, WM8994_AIF2_RATE) + & WM8994_AIF1CLK_RATE_MASK; + + snd_soc_component_update_bits(component, WM8994_AIF1_RATE, + WM8994_AIF1CLK_RATE_MASK, 0x1); + snd_soc_component_update_bits(component, WM8994_AIF2_RATE, + WM8994_AIF2CLK_RATE_MASK, 0x1); + } else if (wm8994->aifdiv[0]) { + snd_soc_component_update_bits(component, WM8994_AIF1_RATE, + WM8994_AIF1CLK_RATE_MASK, + wm8994->aifdiv[0]); + snd_soc_component_update_bits(component, WM8994_AIF2_RATE, + WM8994_AIF2CLK_RATE_MASK, + wm8994->aifdiv[1]); + + wm8994->aifdiv[0] = 0; + wm8994->aifdiv[1] = 0; + } + + return 0; +} + +static int wm8994_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + + wm_hubs_set_bias_level(component, level); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* MICBIAS into regulating mode */ + switch (control->type) { + case WM8958: + case WM1811: + snd_soc_component_update_bits(component, WM8958_MICBIAS1, + WM8958_MICB1_MODE, 0); + snd_soc_component_update_bits(component, WM8958_MICBIAS2, + WM8958_MICB2_MODE, 0); + break; + default: + break; + } + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) + active_reference(component); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + switch (control->type) { + case WM8958: + if (control->revision == 0) { + /* Optimise performance for rev A */ + snd_soc_component_update_bits(component, + WM8958_CHARGE_PUMP_2, + WM8958_CP_DISCH, + WM8958_CP_DISCH); + } + break; + + default: + break; + } + + /* Discharge LINEOUT1 & 2 */ + snd_soc_component_update_bits(component, WM8994_ANTIPOP_1, + WM8994_LINEOUT1_DISCH | + WM8994_LINEOUT2_DISCH, + WM8994_LINEOUT1_DISCH | + WM8994_LINEOUT2_DISCH); + } + + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_PREPARE) + active_dereference(component); + + /* MICBIAS into bypass mode on newer devices */ + switch (control->type) { + case WM8958: + case WM1811: + snd_soc_component_update_bits(component, WM8958_MICBIAS1, + WM8958_MICB1_MODE, + WM8958_MICB1_MODE); + snd_soc_component_update_bits(component, WM8958_MICBIAS2, + WM8958_MICB2_MODE, + WM8958_MICB2_MODE); + break; + default: + break; + } + break; + + case SND_SOC_BIAS_OFF: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) + wm8994->cur_fw = NULL; + break; + } + + return 0; +} + +int wm8994_vmid_mode(struct snd_soc_component *component, enum wm8994_vmid_mode mode) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + switch (mode) { + case WM8994_VMID_NORMAL: + snd_soc_dapm_mutex_lock(dapm); + + if (wm8994->hubs.lineout1_se) { + snd_soc_dapm_disable_pin_unlocked(dapm, + "LINEOUT1N Driver"); + snd_soc_dapm_disable_pin_unlocked(dapm, + "LINEOUT1P Driver"); + } + if (wm8994->hubs.lineout2_se) { + snd_soc_dapm_disable_pin_unlocked(dapm, + "LINEOUT2N Driver"); + snd_soc_dapm_disable_pin_unlocked(dapm, + "LINEOUT2P Driver"); + } + + /* Do the sync with the old mode to allow it to clean up */ + snd_soc_dapm_sync_unlocked(dapm); + wm8994->vmid_mode = mode; + + snd_soc_dapm_mutex_unlock(dapm); + break; + + case WM8994_VMID_FORCE: + snd_soc_dapm_mutex_lock(dapm); + + if (wm8994->hubs.lineout1_se) { + snd_soc_dapm_force_enable_pin_unlocked(dapm, + "LINEOUT1N Driver"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, + "LINEOUT1P Driver"); + } + if (wm8994->hubs.lineout2_se) { + snd_soc_dapm_force_enable_pin_unlocked(dapm, + "LINEOUT2N Driver"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, + "LINEOUT2P Driver"); + } + + wm8994->vmid_mode = mode; + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int ms_reg; + int aif1_reg; + int dac_reg; + int adc_reg; + int ms = 0; + int aif1 = 0; + int lrclk = 0; + + switch (dai->id) { + case 1: + ms_reg = WM8994_AIF1_MASTER_SLAVE; + aif1_reg = WM8994_AIF1_CONTROL_1; + dac_reg = WM8994_AIF1DAC_LRCLK; + adc_reg = WM8994_AIF1ADC_LRCLK; + break; + case 2: + ms_reg = WM8994_AIF2_MASTER_SLAVE; + aif1_reg = WM8994_AIF2_CONTROL_1; + dac_reg = WM8994_AIF1DAC_LRCLK; + adc_reg = WM8994_AIF1ADC_LRCLK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + ms = WM8994_AIF1_MSTR; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif1 |= WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + aif1 |= 0x18; + break; + case SND_SOC_DAIFMT_I2S: + aif1 |= 0x10; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif1 |= 0x8; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8994_AIF1_BCLK_INV; + break; + default: + return -EINVAL; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif1 |= WM8994_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif1 |= WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + /* The AIF2 format configuration needs to be mirrored to AIF3 + * on WM8958 if it's in use so just do it all the time. */ + switch (control->type) { + case WM1811: + case WM8958: + if (dai->id == 2) + snd_soc_component_update_bits(component, WM8958_AIF3_CONTROL_1, + WM8994_AIF1_LRCLK_INV | + WM8958_AIF3_FMT_MASK, aif1); + break; + + default: + break; + } + + snd_soc_component_update_bits(component, aif1_reg, + WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV | + WM8994_AIF1_FMT_MASK, + aif1); + snd_soc_component_update_bits(component, ms_reg, WM8994_AIF1_MSTR, + ms); + snd_soc_component_update_bits(component, dac_reg, + WM8958_AIF1_LRCLK_INV, lrclk); + snd_soc_component_update_bits(component, adc_reg, + WM8958_AIF1_LRCLK_INV, lrclk); + + return 0; +} + +static struct { + int val, rate; +} srs[] = { + { 0, 8000 }, + { 1, 11025 }, + { 2, 12000 }, + { 3, 16000 }, + { 4, 22050 }, + { 5, 24000 }, + { 6, 32000 }, + { 7, 44100 }, + { 8, 48000 }, + { 9, 88200 }, + { 10, 96000 }, +}; + +static int fs_ratios[] = { + 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536 +}; + +static int bclk_divs[] = { + 10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480, + 640, 880, 960, 1280, 1760, 1920 +}; + +static int wm8994_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + struct wm8994_pdata *pdata = &control->pdata; + int aif1_reg; + int aif2_reg; + int bclk_reg; + int lrclk_reg; + int rate_reg; + int aif1 = 0; + int aif2 = 0; + int bclk = 0; + int lrclk = 0; + int rate_val = 0; + int id = dai->id - 1; + + int i, cur_val, best_val, bclk_rate, best; + + switch (dai->id) { + case 1: + aif1_reg = WM8994_AIF1_CONTROL_1; + aif2_reg = WM8994_AIF1_CONTROL_2; + bclk_reg = WM8994_AIF1_BCLK; + rate_reg = WM8994_AIF1_RATE; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || + wm8994->lrclk_shared[0]) { + lrclk_reg = WM8994_AIF1DAC_LRCLK; + } else { + lrclk_reg = WM8994_AIF1ADC_LRCLK; + dev_dbg(component->dev, "AIF1 using split LRCLK\n"); + } + break; + case 2: + aif1_reg = WM8994_AIF2_CONTROL_1; + aif2_reg = WM8994_AIF2_CONTROL_2; + bclk_reg = WM8994_AIF2_BCLK; + rate_reg = WM8994_AIF2_RATE; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || + wm8994->lrclk_shared[1]) { + lrclk_reg = WM8994_AIF2DAC_LRCLK; + } else { + lrclk_reg = WM8994_AIF2ADC_LRCLK; + dev_dbg(component->dev, "AIF2 using split LRCLK\n"); + } + break; + default: + return -EINVAL; + } + + bclk_rate = params_rate(params); + switch (params_width(params)) { + case 16: + bclk_rate *= 16; + break; + case 20: + bclk_rate *= 20; + aif1 |= 0x20; + break; + case 24: + bclk_rate *= 24; + aif1 |= 0x40; + break; + case 32: + bclk_rate *= 32; + aif1 |= 0x60; + break; + default: + return -EINVAL; + } + + wm8994->channels[id] = params_channels(params); + if (pdata->max_channels_clocked[id] && + wm8994->channels[id] > pdata->max_channels_clocked[id]) { + dev_dbg(dai->dev, "Constraining channels to %d from %d\n", + pdata->max_channels_clocked[id], wm8994->channels[id]); + wm8994->channels[id] = pdata->max_channels_clocked[id]; + } + + switch (wm8994->channels[id]) { + case 1: + case 2: + bclk_rate *= 2; + break; + default: + bclk_rate *= 4; + break; + } + + /* Try to find an appropriate sample rate; look for an exact match. */ + for (i = 0; i < ARRAY_SIZE(srs); i++) + if (srs[i].rate == params_rate(params)) + break; + if (i == ARRAY_SIZE(srs)) + return -EINVAL; + rate_val |= srs[i].val << WM8994_AIF1_SR_SHIFT; + + dev_dbg(dai->dev, "Sample rate is %dHz\n", srs[i].rate); + dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n", + dai->id, wm8994->aifclk[id], bclk_rate); + + if (wm8994->channels[id] == 1 && + (snd_soc_component_read(component, aif1_reg) & 0x18) == 0x18) + aif2 |= WM8994_AIF1_MONO; + + if (wm8994->aifclk[id] == 0) { + dev_err(dai->dev, "AIF%dCLK not configured\n", dai->id); + return -EINVAL; + } + + /* AIFCLK/fs ratio; look for a close match in either direction */ + best = 0; + best_val = abs((fs_ratios[0] * params_rate(params)) + - wm8994->aifclk[id]); + for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) { + cur_val = abs((fs_ratios[i] * params_rate(params)) + - wm8994->aifclk[id]); + if (cur_val >= best_val) + continue; + best = i; + best_val = cur_val; + } + dev_dbg(dai->dev, "Selected AIF%dCLK/fs = %d\n", + dai->id, fs_ratios[best]); + rate_val |= best; + + /* We may not get quite the right frequency if using + * approximate clocks so look for the closest match that is + * higher than the target (we need to ensure that there enough + * BCLKs to clock out the samples). + */ + best = 0; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate; + if (cur_val < 0) /* BCLK table is sorted */ + break; + best = i; + } + bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best]; + dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n", + bclk_divs[best], bclk_rate); + bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT; + + lrclk = bclk_rate / params_rate(params); + if (!lrclk) { + dev_err(dai->dev, "Unable to generate LRCLK from %dHz BCLK\n", + bclk_rate); + return -EINVAL; + } + dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n", + lrclk, bclk_rate / lrclk); + + snd_soc_component_update_bits(component, aif1_reg, WM8994_AIF1_WL_MASK, aif1); + snd_soc_component_update_bits(component, aif2_reg, WM8994_AIF1_MONO, aif2); + snd_soc_component_update_bits(component, bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk); + snd_soc_component_update_bits(component, lrclk_reg, WM8994_AIF1DAC_RATE_MASK, + lrclk); + snd_soc_component_update_bits(component, rate_reg, WM8994_AIF1_SR_MASK | + WM8994_AIF1CLK_RATE_MASK, rate_val); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (dai->id) { + case 1: + wm8994->dac_rates[0] = params_rate(params); + wm8994_set_retune_mobile(component, 0); + wm8994_set_retune_mobile(component, 1); + break; + case 2: + wm8994->dac_rates[1] = params_rate(params); + wm8994_set_retune_mobile(component, 2); + break; + } + } + + return 0; +} + +static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int aif1_reg; + int aif1 = 0; + + switch (dai->id) { + case 3: + switch (control->type) { + case WM1811: + case WM8958: + aif1_reg = WM8958_AIF3_CONTROL_1; + break; + default: + return 0; + } + break; + default: + return 0; + } + + switch (params_width(params)) { + case 16: + break; + case 20: + aif1 |= 0x20; + break; + case 24: + aif1 |= 0x40; + break; + case 32: + aif1 |= 0x60; + break; + default: + return -EINVAL; + } + + return snd_soc_component_update_bits(component, aif1_reg, WM8994_AIF1_WL_MASK, aif1); +} + +static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute, + int direction) +{ + struct snd_soc_component *component = codec_dai->component; + int mute_reg; + int reg; + + switch (codec_dai->id) { + case 1: + mute_reg = WM8994_AIF1_DAC1_FILTERS_1; + break; + case 2: + mute_reg = WM8994_AIF2_DAC_FILTERS_1; + break; + default: + return -EINVAL; + } + + if (mute) + reg = WM8994_AIF1DAC1_MUTE; + else + reg = 0; + + snd_soc_component_update_bits(component, mute_reg, WM8994_AIF1DAC1_MUTE, reg); + + return 0; +} + +static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate) +{ + struct snd_soc_component *component = codec_dai->component; + int reg, val, mask; + + switch (codec_dai->id) { + case 1: + reg = WM8994_AIF1_MASTER_SLAVE; + mask = WM8994_AIF1_TRI; + break; + case 2: + reg = WM8994_AIF2_MASTER_SLAVE; + mask = WM8994_AIF2_TRI; + break; + default: + return -EINVAL; + } + + if (tristate) + val = mask; + else + val = 0; + + return snd_soc_component_update_bits(component, reg, mask, val); +} + +static int wm8994_aif2_probe(struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + /* Disable the pulls on the AIF if we're using it to save power. */ + snd_soc_component_update_bits(component, WM8994_GPIO_3, + WM8994_GPN_PU | WM8994_GPN_PD, 0); + snd_soc_component_update_bits(component, WM8994_GPIO_4, + WM8994_GPN_PU | WM8994_GPN_PD, 0); + snd_soc_component_update_bits(component, WM8994_GPIO_5, + WM8994_GPN_PU | WM8994_GPN_PD, 0); + + return 0; +} + +#define WM8994_RATES SNDRV_PCM_RATE_8000_96000 + +#define WM8994_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = { + .set_sysclk = wm8994_set_dai_sysclk, + .set_fmt = wm8994_set_dai_fmt, + .hw_params = wm8994_hw_params, + .mute_stream = wm8994_aif_mute, + .set_pll = wm8994_set_fll, + .set_tristate = wm8994_set_tristate, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = { + .probe = wm8994_aif2_probe, + .set_sysclk = wm8994_set_dai_sysclk, + .set_fmt = wm8994_set_dai_fmt, + .hw_params = wm8994_hw_params, + .mute_stream = wm8994_aif_mute, + .set_pll = wm8994_set_fll, + .set_tristate = wm8994_set_tristate, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops wm8994_aif3_dai_ops = { + .hw_params = wm8994_aif3_hw_params, +}; + +static struct snd_soc_dai_driver wm8994_dai[] = { + { + .name = "wm8994-aif1", + .id = 1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8994_RATES, + .formats = WM8994_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8994_RATES, + .formats = WM8994_FORMATS, + .sig_bits = 24, + }, + .ops = &wm8994_aif1_dai_ops, + }, + { + .name = "wm8994-aif2", + .id = 2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8994_RATES, + .formats = WM8994_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8994_RATES, + .formats = WM8994_FORMATS, + .sig_bits = 24, + }, + .ops = &wm8994_aif2_dai_ops, + }, + { + .name = "wm8994-aif3", + .id = 3, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8994_RATES, + .formats = WM8994_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8994_RATES, + .formats = WM8994_FORMATS, + .sig_bits = 24, + }, + .ops = &wm8994_aif3_dai_ops, + } +}; + +#ifdef CONFIG_PM +static int wm8994_component_suspend(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + int i, ret; + + for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { + memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i], + sizeof(struct wm8994_fll_config)); + ret = _wm8994_set_fll(component, i + 1, 0, 0, 0); + if (ret < 0) + dev_warn(component->dev, "Failed to stop FLL%d: %d\n", + i + 1, ret); + } + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + return 0; +} + +static int wm8994_component_resume(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + int i, ret; + + for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { + if (!wm8994->fll_suspend[i].out) + continue; + + ret = _wm8994_set_fll(component, i + 1, + wm8994->fll_suspend[i].src, + wm8994->fll_suspend[i].in, + wm8994->fll_suspend[i].out); + if (ret < 0) + dev_warn(component->dev, "Failed to restore FLL%d: %d\n", + i + 1, ret); + } + + return 0; +} +#else +#define wm8994_component_suspend NULL +#define wm8994_component_resume NULL +#endif + +static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994) +{ + struct snd_soc_component *component = wm8994->hubs.component; + struct wm8994 *control = wm8994->wm8994; + struct wm8994_pdata *pdata = &control->pdata; + struct snd_kcontrol_new controls[] = { + SOC_ENUM_EXT("AIF1.1 EQ Mode", + wm8994->retune_mobile_enum, + wm8994_get_retune_mobile_enum, + wm8994_put_retune_mobile_enum), + SOC_ENUM_EXT("AIF1.2 EQ Mode", + wm8994->retune_mobile_enum, + wm8994_get_retune_mobile_enum, + wm8994_put_retune_mobile_enum), + SOC_ENUM_EXT("AIF2 EQ Mode", + wm8994->retune_mobile_enum, + wm8994_get_retune_mobile_enum, + wm8994_put_retune_mobile_enum), + }; + int ret, i, j; + const char **t; + + /* We need an array of texts for the enum API but the number + * of texts is likely to be less than the number of + * configurations due to the sample rate dependency of the + * configurations. */ + wm8994->num_retune_mobile_texts = 0; + wm8994->retune_mobile_texts = NULL; + for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) { + for (j = 0; j < wm8994->num_retune_mobile_texts; j++) { + if (strcmp(pdata->retune_mobile_cfgs[i].name, + wm8994->retune_mobile_texts[j]) == 0) + break; + } + + if (j != wm8994->num_retune_mobile_texts) + continue; + + /* Expand the array... */ + t = krealloc(wm8994->retune_mobile_texts, + sizeof(char *) * + (wm8994->num_retune_mobile_texts + 1), + GFP_KERNEL); + if (t == NULL) + continue; + + /* ...store the new entry... */ + t[wm8994->num_retune_mobile_texts] = + pdata->retune_mobile_cfgs[i].name; + + /* ...and remember the new version. */ + wm8994->num_retune_mobile_texts++; + wm8994->retune_mobile_texts = t; + } + + dev_dbg(component->dev, "Allocated %d unique ReTune Mobile names\n", + wm8994->num_retune_mobile_texts); + + wm8994->retune_mobile_enum.items = wm8994->num_retune_mobile_texts; + wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts; + + ret = snd_soc_add_component_controls(wm8994->hubs.component, controls, + ARRAY_SIZE(controls)); + if (ret != 0) + dev_err(wm8994->hubs.component->dev, + "Failed to add ReTune Mobile controls: %d\n", ret); +} + +static void wm8994_handle_pdata(struct wm8994_priv *wm8994) +{ + struct snd_soc_component *component = wm8994->hubs.component; + struct wm8994 *control = wm8994->wm8994; + struct wm8994_pdata *pdata = &control->pdata; + int ret, i; + + if (!pdata) + return; + + wm_hubs_handle_analogue_pdata(component, pdata->lineout1_diff, + pdata->lineout2_diff, + pdata->lineout1fb, + pdata->lineout2fb, + pdata->jd_scthr, + pdata->jd_thr, + pdata->micb1_delay, + pdata->micb2_delay, + pdata->micbias1_lvl, + pdata->micbias2_lvl); + + dev_dbg(component->dev, "%d DRC configurations\n", pdata->num_drc_cfgs); + + if (pdata->num_drc_cfgs) { + struct snd_kcontrol_new controls[] = { + SOC_ENUM_EXT("AIF1DRC1 Mode", wm8994->drc_enum, + wm8994_get_drc_enum, wm8994_put_drc_enum), + SOC_ENUM_EXT("AIF1DRC2 Mode", wm8994->drc_enum, + wm8994_get_drc_enum, wm8994_put_drc_enum), + SOC_ENUM_EXT("AIF2DRC Mode", wm8994->drc_enum, + wm8994_get_drc_enum, wm8994_put_drc_enum), + }; + + /* We need an array of texts for the enum API */ + wm8994->drc_texts = devm_kcalloc(wm8994->hubs.component->dev, + pdata->num_drc_cfgs, sizeof(char *), GFP_KERNEL); + if (!wm8994->drc_texts) + return; + + for (i = 0; i < pdata->num_drc_cfgs; i++) + wm8994->drc_texts[i] = pdata->drc_cfgs[i].name; + + wm8994->drc_enum.items = pdata->num_drc_cfgs; + wm8994->drc_enum.texts = wm8994->drc_texts; + + ret = snd_soc_add_component_controls(wm8994->hubs.component, controls, + ARRAY_SIZE(controls)); + for (i = 0; i < WM8994_NUM_DRC; i++) + wm8994_set_drc(component, i); + } else { + ret = snd_soc_add_component_controls(wm8994->hubs.component, + wm8994_drc_controls, + ARRAY_SIZE(wm8994_drc_controls)); + } + + if (ret != 0) + dev_err(wm8994->hubs.component->dev, + "Failed to add DRC mode controls: %d\n", ret); + + + dev_dbg(component->dev, "%d ReTune Mobile configurations\n", + pdata->num_retune_mobile_cfgs); + + if (pdata->num_retune_mobile_cfgs) + wm8994_handle_retune_mobile_pdata(wm8994); + else + snd_soc_add_component_controls(wm8994->hubs.component, wm8994_eq_controls, + ARRAY_SIZE(wm8994_eq_controls)); + + for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) { + if (pdata->micbias[i]) { + snd_soc_component_write(component, WM8958_MICBIAS1 + i, + pdata->micbias[i] & 0xffff); + } + } +} + +/** + * wm8994_mic_detect - Enable microphone detection via the WM8994 IRQ + * + * @component: WM8994 component + * @jack: jack to report detection events on + * @micbias: microphone bias to detect on + * + * Enable microphone detection via IRQ on the WM8994. If GPIOs are + * being used to bring out signals to the processor then only platform + * data configuration is needed for WM8994 and processor GPIOs should + * be configured using snd_soc_jack_add_gpios() instead. + * + * Configuration of detection levels is available via the micbias1_lvl + * and micbias2_lvl platform data members. + */ +int wm8994_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack, + int micbias) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994_micdet *micdet; + struct wm8994 *control = wm8994->wm8994; + int reg, ret; + + if (control->type != WM8994) { + dev_warn(component->dev, "Not a WM8994\n"); + return -EINVAL; + } + + pm_runtime_get_sync(component->dev); + + switch (micbias) { + case 1: + micdet = &wm8994->micdet[0]; + if (jack) + ret = snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + else + ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + break; + case 2: + micdet = &wm8994->micdet[1]; + if (jack) + ret = snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + else + ret = snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + break; + default: + dev_warn(component->dev, "Invalid MICBIAS %d\n", micbias); + return -EINVAL; + } + + if (ret != 0) + dev_warn(component->dev, "Failed to configure MICBIAS%d: %d\n", + micbias, ret); + + dev_dbg(component->dev, "Configuring microphone detection on %d %p\n", + micbias, jack); + + /* Store the configuration */ + micdet->jack = jack; + micdet->detecting = true; + + /* If either of the jacks is set up then enable detection */ + if (wm8994->micdet[0].jack || wm8994->micdet[1].jack) + reg = WM8994_MICD_ENA; + else + reg = 0; + + snd_soc_component_update_bits(component, WM8994_MICBIAS, WM8994_MICD_ENA, reg); + + /* enable MICDET and MICSHRT deboune */ + snd_soc_component_update_bits(component, WM8994_IRQ_DEBOUNCE, + WM8994_MIC1_DET_DB_MASK | WM8994_MIC1_SHRT_DB_MASK | + WM8994_MIC2_DET_DB_MASK | WM8994_MIC2_SHRT_DB_MASK, + WM8994_MIC1_DET_DB | WM8994_MIC1_SHRT_DB); + + snd_soc_dapm_sync(dapm); + + pm_runtime_put(component->dev); + + return 0; +} +EXPORT_SYMBOL_GPL(wm8994_mic_detect); + +static void wm8994_mic_work(struct work_struct *work) +{ + struct wm8994_priv *priv = container_of(work, + struct wm8994_priv, + mic_work.work); + struct regmap *regmap = priv->wm8994->regmap; + struct device *dev = priv->wm8994->dev; + unsigned int reg; + int ret; + int report; + + pm_runtime_get_sync(dev); + + ret = regmap_read(regmap, WM8994_INTERRUPT_RAW_STATUS_2, ®); + if (ret < 0) { + dev_err(dev, "Failed to read microphone status: %d\n", + ret); + pm_runtime_put(dev); + return; + } + + dev_dbg(dev, "Microphone status: %x\n", reg); + + report = 0; + if (reg & WM8994_MIC1_DET_STS) { + if (priv->micdet[0].detecting) + report = SND_JACK_HEADSET; + } + if (reg & WM8994_MIC1_SHRT_STS) { + if (priv->micdet[0].detecting) + report = SND_JACK_HEADPHONE; + else + report |= SND_JACK_BTN_0; + } + if (report) + priv->micdet[0].detecting = false; + else + priv->micdet[0].detecting = true; + + snd_soc_jack_report(priv->micdet[0].jack, report, + SND_JACK_HEADSET | SND_JACK_BTN_0); + + report = 0; + if (reg & WM8994_MIC2_DET_STS) { + if (priv->micdet[1].detecting) + report = SND_JACK_HEADSET; + } + if (reg & WM8994_MIC2_SHRT_STS) { + if (priv->micdet[1].detecting) + report = SND_JACK_HEADPHONE; + else + report |= SND_JACK_BTN_0; + } + if (report) + priv->micdet[1].detecting = false; + else + priv->micdet[1].detecting = true; + + snd_soc_jack_report(priv->micdet[1].jack, report, + SND_JACK_HEADSET | SND_JACK_BTN_0); + + pm_runtime_put(dev); +} + +static irqreturn_t wm8994_mic_irq(int irq, void *data) +{ + struct wm8994_priv *priv = data; + struct snd_soc_component *component = priv->hubs.component; + +#ifndef CONFIG_SND_SOC_WM8994_MODULE + trace_snd_soc_jack_irq(dev_name(component->dev)); +#endif + + pm_wakeup_event(component->dev, 300); + + queue_delayed_work(system_power_efficient_wq, + &priv->mic_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +/* Should be called with accdet_lock held */ +static void wm1811_micd_stop(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + if (!wm8994->jackdet) + return; + + snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0); + + wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_JACK); + + if (wm8994->wm8994->pdata.jd_ext_cap) + snd_soc_dapm_disable_pin(dapm, "MICBIAS2"); +} + +static void wm8958_button_det(struct snd_soc_component *component, u16 status) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + int report; + + report = 0; + if (status & 0x4) + report |= SND_JACK_BTN_0; + + if (status & 0x8) + report |= SND_JACK_BTN_1; + + if (status & 0x10) + report |= SND_JACK_BTN_2; + + if (status & 0x20) + report |= SND_JACK_BTN_3; + + if (status & 0x40) + report |= SND_JACK_BTN_4; + + if (status & 0x80) + report |= SND_JACK_BTN_5; + + snd_soc_jack_report(wm8994->micdet[0].jack, report, + wm8994->btn_mask); +} + +static void wm8958_open_circuit_work(struct work_struct *work) +{ + struct wm8994_priv *wm8994 = container_of(work, + struct wm8994_priv, + open_circuit_work.work); + struct device *dev = wm8994->wm8994->dev; + + mutex_lock(&wm8994->accdet_lock); + + wm1811_micd_stop(wm8994->hubs.component); + + dev_dbg(dev, "Reporting open circuit\n"); + + wm8994->jack_mic = false; + wm8994->mic_detecting = true; + + wm8958_micd_set_rate(wm8994->hubs.component); + + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + wm8994->btn_mask | + SND_JACK_HEADSET); + + mutex_unlock(&wm8994->accdet_lock); +} + +static void wm8958_mic_id(void *data, u16 status) +{ + struct snd_soc_component *component = data; + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + + /* Either nothing present or just starting detection */ + if (!(status & WM8958_MICD_STS)) { + /* If nothing present then clear our statuses */ + dev_dbg(component->dev, "Detected open circuit\n"); + + queue_delayed_work(system_power_efficient_wq, + &wm8994->open_circuit_work, + msecs_to_jiffies(2500)); + return; + } + + /* If the measurement is showing a high impedence we've got a + * microphone. + */ + if (status & 0x600) { + dev_dbg(component->dev, "Detected microphone\n"); + + wm8994->mic_detecting = false; + wm8994->jack_mic = true; + + wm8958_micd_set_rate(component); + + snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET, + SND_JACK_HEADSET); + } + + + if (status & 0xfc) { + dev_dbg(component->dev, "Detected headphone\n"); + wm8994->mic_detecting = false; + + wm8958_micd_set_rate(component); + + /* If we have jackdet that will detect removal */ + wm1811_micd_stop(component); + + snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, + SND_JACK_HEADSET); + } +} + +/* Deferred mic detection to allow for extra settling time */ +static void wm1811_mic_work(struct work_struct *work) +{ + struct wm8994_priv *wm8994 = container_of(work, struct wm8994_priv, + mic_work.work); + struct wm8994 *control = wm8994->wm8994; + struct snd_soc_component *component = wm8994->hubs.component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + pm_runtime_get_sync(component->dev); + + /* If required for an external cap force MICBIAS on */ + if (control->pdata.jd_ext_cap) { + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS2"); + snd_soc_dapm_sync(dapm); + } + + mutex_lock(&wm8994->accdet_lock); + + dev_dbg(component->dev, "Starting mic detection\n"); + + /* Use a user-supplied callback if we have one */ + if (wm8994->micd_cb) { + wm8994->micd_cb(wm8994->micd_cb_data); + } else { + /* + * Start off measument of microphone impedence to find out + * what's actually there. + */ + wm8994->mic_detecting = true; + wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_MIC); + + snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, WM8958_MICD_ENA); + } + + mutex_unlock(&wm8994->accdet_lock); + + pm_runtime_put(component->dev); +} + +static irqreturn_t wm1811_jackdet_irq(int irq, void *data) +{ + struct wm8994_priv *wm8994 = data; + struct wm8994 *control = wm8994->wm8994; + struct snd_soc_component *component = wm8994->hubs.component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int reg, delay; + bool present; + + pm_runtime_get_sync(component->dev); + + cancel_delayed_work_sync(&wm8994->mic_complete_work); + + mutex_lock(&wm8994->accdet_lock); + + reg = snd_soc_component_read(component, WM1811_JACKDET_CTRL); + if (reg < 0) { + dev_err(component->dev, "Failed to read jack status: %d\n", reg); + mutex_unlock(&wm8994->accdet_lock); + pm_runtime_put(component->dev); + return IRQ_NONE; + } + + dev_dbg(component->dev, "JACKDET %x\n", reg); + + present = reg & WM1811_JACKDET_LVL; + + if (present) { + dev_dbg(component->dev, "Jack detected\n"); + + wm8958_micd_set_rate(component); + + snd_soc_component_update_bits(component, WM8958_MICBIAS2, + WM8958_MICB2_DISCH, 0); + + /* Disable debounce while inserted */ + snd_soc_component_update_bits(component, WM1811_JACKDET_CTRL, + WM1811_JACKDET_DB, 0); + + delay = control->pdata.micdet_delay; + queue_delayed_work(system_power_efficient_wq, + &wm8994->mic_work, + msecs_to_jiffies(delay)); + } else { + dev_dbg(component->dev, "Jack not detected\n"); + + /* Release wm8994->accdet_lock to avoid deadlock: + * cancel_delayed_work_sync() takes wm8994->mic_work internal + * lock and wm1811_mic_work takes wm8994->accdet_lock */ + mutex_unlock(&wm8994->accdet_lock); + cancel_delayed_work_sync(&wm8994->mic_work); + mutex_lock(&wm8994->accdet_lock); + + snd_soc_component_update_bits(component, WM8958_MICBIAS2, + WM8958_MICB2_DISCH, WM8958_MICB2_DISCH); + + /* Enable debounce while removed */ + snd_soc_component_update_bits(component, WM1811_JACKDET_CTRL, + WM1811_JACKDET_DB, WM1811_JACKDET_DB); + + wm8994->mic_detecting = false; + wm8994->jack_mic = false; + snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, 0); + wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_JACK); + } + + mutex_unlock(&wm8994->accdet_lock); + + /* Turn off MICBIAS if it was on for an external cap */ + if (control->pdata.jd_ext_cap && !present) + snd_soc_dapm_disable_pin(dapm, "MICBIAS2"); + + if (present) + snd_soc_jack_report(wm8994->micdet[0].jack, + SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); + else + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + SND_JACK_MECHANICAL | SND_JACK_HEADSET | + wm8994->btn_mask); + + /* Since we only report deltas force an update, ensures we + * avoid bootstrapping issues with the core. */ + snd_soc_jack_report(wm8994->micdet[0].jack, 0, 0); + + pm_runtime_put(component->dev); + return IRQ_HANDLED; +} + +static void wm1811_jackdet_bootstrap(struct work_struct *work) +{ + struct wm8994_priv *wm8994 = container_of(work, + struct wm8994_priv, + jackdet_bootstrap.work); + wm1811_jackdet_irq(0, wm8994); +} + +/** + * wm8958_mic_detect - Enable microphone detection via the WM8958 IRQ + * + * @component: WM8958 component + * @jack: jack to report detection events on + * @det_cb: detection callback + * @det_cb_data: data for detection callback + * @id_cb: mic id callback + * @id_cb_data: data for mic id callback + * + * Enable microphone detection functionality for the WM8958. By + * default simple detection which supports the detection of up to 6 + * buttons plus video and microphone functionality is supported. + * + * The WM8958 has an advanced jack detection facility which is able to + * support complex accessory detection, especially when used in + * conjunction with external circuitry. In order to provide maximum + * flexiblity a callback is provided which allows a completely custom + * detection algorithm. + */ +int wm8958_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack, + wm1811_micdet_cb det_cb, void *det_cb_data, + wm1811_mic_id_cb id_cb, void *id_cb_data) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + u16 micd_lvl_sel; + + switch (control->type) { + case WM1811: + case WM8958: + break; + default: + return -EINVAL; + } + + pm_runtime_get_sync(component->dev); + + if (jack) { + snd_soc_dapm_force_enable_pin(dapm, "CLK_SYS"); + snd_soc_dapm_sync(dapm); + + wm8994->micdet[0].jack = jack; + + if (det_cb) { + wm8994->micd_cb = det_cb; + wm8994->micd_cb_data = det_cb_data; + } else { + wm8994->mic_detecting = true; + wm8994->jack_mic = false; + } + + if (id_cb) { + wm8994->mic_id_cb = id_cb; + wm8994->mic_id_cb_data = id_cb_data; + } else { + wm8994->mic_id_cb = wm8958_mic_id; + wm8994->mic_id_cb_data = component; + } + + wm8958_micd_set_rate(component); + + /* Detect microphones and short circuits by default */ + if (control->pdata.micd_lvl_sel) + micd_lvl_sel = control->pdata.micd_lvl_sel; + else + micd_lvl_sel = 0x41; + + wm8994->btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4 | SND_JACK_BTN_5; + + snd_soc_component_update_bits(component, WM8958_MIC_DETECT_2, + WM8958_MICD_LVL_SEL_MASK, micd_lvl_sel); + + WARN_ON(snd_soc_component_get_bias_level(component) > SND_SOC_BIAS_STANDBY); + + /* + * If we can use jack detection start off with that, + * otherwise jump straight to microphone detection. + */ + if (wm8994->jackdet) { + /* Disable debounce for the initial detect */ + snd_soc_component_update_bits(component, WM1811_JACKDET_CTRL, + WM1811_JACKDET_DB, 0); + + snd_soc_component_update_bits(component, WM8958_MICBIAS2, + WM8958_MICB2_DISCH, + WM8958_MICB2_DISCH); + snd_soc_component_update_bits(component, WM8994_LDO_1, + WM8994_LDO1_DISCH, 0); + wm1811_jackdet_set_mode(component, + WM1811_JACKDET_MODE_JACK); + } else { + snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, WM8958_MICD_ENA); + } + + } else { + snd_soc_component_update_bits(component, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, 0); + wm1811_jackdet_set_mode(component, WM1811_JACKDET_MODE_NONE); + snd_soc_dapm_disable_pin(dapm, "CLK_SYS"); + snd_soc_dapm_sync(dapm); + } + + pm_runtime_put(component->dev); + + return 0; +} +EXPORT_SYMBOL_GPL(wm8958_mic_detect); + +static void wm8958_mic_work(struct work_struct *work) +{ + struct wm8994_priv *wm8994 = container_of(work, + struct wm8994_priv, + mic_complete_work.work); + struct snd_soc_component *component = wm8994->hubs.component; + + pm_runtime_get_sync(component->dev); + + mutex_lock(&wm8994->accdet_lock); + + wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status); + + mutex_unlock(&wm8994->accdet_lock); + + pm_runtime_put(component->dev); +} + +static irqreturn_t wm8958_mic_irq(int irq, void *data) +{ + struct wm8994_priv *wm8994 = data; + struct snd_soc_component *component = wm8994->hubs.component; + int reg, count, ret, id_delay; + + /* + * Jack detection may have detected a removal simulataneously + * with an update of the MICDET status; if so it will have + * stopped detection and we can ignore this interrupt. + */ + if (!(snd_soc_component_read(component, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA)) + return IRQ_HANDLED; + + cancel_delayed_work_sync(&wm8994->mic_complete_work); + cancel_delayed_work_sync(&wm8994->open_circuit_work); + + pm_runtime_get_sync(component->dev); + + /* We may occasionally read a detection without an impedence + * range being provided - if that happens loop again. + */ + count = 10; + do { + reg = snd_soc_component_read(component, WM8958_MIC_DETECT_3); + if (reg < 0) { + dev_err(component->dev, + "Failed to read mic detect status: %d\n", + reg); + pm_runtime_put(component->dev); + return IRQ_NONE; + } + + if (!(reg & WM8958_MICD_VALID)) { + dev_dbg(component->dev, "Mic detect data not valid\n"); + goto out; + } + + if (!(reg & WM8958_MICD_STS) || (reg & WM8958_MICD_LVL_MASK)) + break; + + msleep(1); + } while (count--); + + if (count == 0) + dev_warn(component->dev, "No impedance range reported for jack\n"); + +#ifndef CONFIG_SND_SOC_WM8994_MODULE + trace_snd_soc_jack_irq(dev_name(component->dev)); +#endif + + /* Avoid a transient report when the accessory is being removed */ + if (wm8994->jackdet) { + ret = snd_soc_component_read(component, WM1811_JACKDET_CTRL); + if (ret < 0) { + dev_err(component->dev, "Failed to read jack status: %d\n", + ret); + } else if (!(ret & WM1811_JACKDET_LVL)) { + dev_dbg(component->dev, "Ignoring removed jack\n"); + goto out; + } + } else if (!(reg & WM8958_MICD_STS)) { + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + SND_JACK_MECHANICAL | SND_JACK_HEADSET | + wm8994->btn_mask); + wm8994->mic_detecting = true; + goto out; + } + + wm8994->mic_status = reg; + id_delay = wm8994->wm8994->pdata.mic_id_delay; + + if (wm8994->mic_detecting) + queue_delayed_work(system_power_efficient_wq, + &wm8994->mic_complete_work, + msecs_to_jiffies(id_delay)); + else + wm8958_button_det(component, reg); + +out: + pm_runtime_put(component->dev); + return IRQ_HANDLED; +} + +static irqreturn_t wm8994_fifo_error(int irq, void *data) +{ + struct snd_soc_component *component = data; + + dev_err(component->dev, "FIFO error\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t wm8994_temp_warn(int irq, void *data) +{ + struct snd_soc_component *component = data; + + dev_err(component->dev, "Thermal warning\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t wm8994_temp_shut(int irq, void *data) +{ + struct snd_soc_component *component = data; + + dev_crit(component->dev, "Thermal shutdown\n"); + + return IRQ_HANDLED; +} + +static int wm8994_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8994 *control = dev_get_drvdata(component->dev->parent); + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + unsigned int reg; + int ret, i; + + snd_soc_component_init_regmap(component, control->regmap); + + wm8994->hubs.component = component; + + mutex_init(&wm8994->accdet_lock); + INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap, + wm1811_jackdet_bootstrap); + INIT_DELAYED_WORK(&wm8994->open_circuit_work, + wm8958_open_circuit_work); + + switch (control->type) { + case WM8994: + INIT_DELAYED_WORK(&wm8994->mic_work, wm8994_mic_work); + break; + case WM1811: + INIT_DELAYED_WORK(&wm8994->mic_work, wm1811_mic_work); + break; + default: + break; + } + + INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work); + + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) + init_completion(&wm8994->fll_locked[i]); + + wm8994->micdet_irq = control->pdata.micdet_irq; + + /* By default use idle_bias_off, will override for WM8994 */ + dapm->idle_bias_off = 1; + + /* Set revision-specific configuration */ + switch (control->type) { + case WM8994: + /* Single ended line outputs should have VMID on. */ + if (!control->pdata.lineout1_diff || + !control->pdata.lineout2_diff) + dapm->idle_bias_off = 0; + + switch (control->revision) { + case 2: + case 3: + wm8994->hubs.dcs_codes_l = -5; + wm8994->hubs.dcs_codes_r = -5; + wm8994->hubs.hp_startup_mode = 1; + wm8994->hubs.dcs_readback_mode = 1; + wm8994->hubs.series_startup = 1; + break; + default: + wm8994->hubs.dcs_readback_mode = 2; + break; + } + wm8994->hubs.micd_scthr = true; + break; + + case WM8958: + wm8994->hubs.dcs_readback_mode = 1; + wm8994->hubs.hp_startup_mode = 1; + wm8994->hubs.micd_scthr = true; + + switch (control->revision) { + case 0: + break; + default: + wm8994->fll_byp = true; + break; + } + break; + + case WM1811: + wm8994->hubs.dcs_readback_mode = 2; + wm8994->hubs.no_series_update = 1; + wm8994->hubs.hp_startup_mode = 1; + wm8994->hubs.no_cache_dac_hp_direct = true; + wm8994->fll_byp = true; + + wm8994->hubs.dcs_codes_l = -9; + wm8994->hubs.dcs_codes_r = -7; + + snd_soc_component_update_bits(component, WM8994_ANALOGUE_HP_1, + WM1811_HPOUT1_ATTN, WM1811_HPOUT1_ATTN); + break; + + default: + break; + } + + wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, + wm8994_fifo_error, "FIFO error", component); + wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, + wm8994_temp_warn, "Thermal warning", component); + wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, + wm8994_temp_shut, "Thermal shutdown", component); + + switch (control->type) { + case WM8994: + if (wm8994->micdet_irq) + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8994_mic_irq, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, + "Mic1 detect", + wm8994); + else + ret = wm8994_request_irq(wm8994->wm8994, + WM8994_IRQ_MIC1_DET, + wm8994_mic_irq, "Mic 1 detect", + wm8994); + + if (ret != 0) + dev_warn(component->dev, + "Failed to request Mic1 detect IRQ: %d\n", + ret); + + + ret = wm8994_request_irq(wm8994->wm8994, + WM8994_IRQ_MIC1_SHRT, + wm8994_mic_irq, "Mic 1 short", + wm8994); + if (ret != 0) + dev_warn(component->dev, + "Failed to request Mic1 short IRQ: %d\n", + ret); + + ret = wm8994_request_irq(wm8994->wm8994, + WM8994_IRQ_MIC2_DET, + wm8994_mic_irq, "Mic 2 detect", + wm8994); + if (ret != 0) + dev_warn(component->dev, + "Failed to request Mic2 detect IRQ: %d\n", + ret); + + ret = wm8994_request_irq(wm8994->wm8994, + WM8994_IRQ_MIC2_SHRT, + wm8994_mic_irq, "Mic 2 short", + wm8994); + if (ret != 0) + dev_warn(component->dev, + "Failed to request Mic2 short IRQ: %d\n", + ret); + break; + + case WM8958: + case WM1811: + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8958_mic_irq, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, + "Mic detect", + wm8994); + if (ret != 0) + dev_warn(component->dev, + "Failed to request Mic detect IRQ: %d\n", + ret); + } else { + wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_MIC1_DET, + wm8958_mic_irq, "Mic detect", + wm8994); + } + } + + switch (control->type) { + case WM1811: + if (control->cust_id > 1 || control->revision > 1) { + ret = wm8994_request_irq(wm8994->wm8994, + WM8994_IRQ_GPIO(6), + wm1811_jackdet_irq, "JACKDET", + wm8994); + if (ret == 0) + wm8994->jackdet = true; + } + break; + default: + break; + } + + wm8994->fll_locked_irq = true; + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) { + ret = wm8994_request_irq(wm8994->wm8994, + WM8994_IRQ_FLL1_LOCK + i, + wm8994_fll_locked_irq, "FLL lock", + &wm8994->fll_locked[i]); + if (ret != 0) + wm8994->fll_locked_irq = false; + } + + /* Make sure we can read from the GPIOs if they're inputs */ + pm_runtime_get_sync(component->dev); + + /* Remember if AIFnLRCLK is configured as a GPIO. This should be + * configured on init - if a system wants to do this dynamically + * at runtime we can deal with that then. + */ + ret = regmap_read(control->regmap, WM8994_GPIO_1, ®); + if (ret < 0) { + dev_err(component->dev, "Failed to read GPIO1 state: %d\n", ret); + goto err_irq; + } + if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) { + wm8994->lrclk_shared[0] = 1; + wm8994_dai[0].symmetric_rate = 1; + } else { + wm8994->lrclk_shared[0] = 0; + } + + ret = regmap_read(control->regmap, WM8994_GPIO_6, ®); + if (ret < 0) { + dev_err(component->dev, "Failed to read GPIO6 state: %d\n", ret); + goto err_irq; + } + if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) { + wm8994->lrclk_shared[1] = 1; + wm8994_dai[1].symmetric_rate = 1; + } else { + wm8994->lrclk_shared[1] = 0; + } + + pm_runtime_put(component->dev); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm8994_vu_bits); i++) + snd_soc_component_update_bits(component, wm8994_vu_bits[i].reg, + wm8994_vu_bits[i].mask, + wm8994_vu_bits[i].mask); + + if (control->type != WM1811) { + for (i = 0; i < ARRAY_SIZE(wm8994_adc2_dac2_vu_bits); i++) + snd_soc_component_update_bits(component, + wm8994_adc2_dac2_vu_bits[i].reg, + wm8994_adc2_dac2_vu_bits[i].mask, + wm8994_adc2_dac2_vu_bits[i].mask); + } + + /* Set the low bit of the 3D stereo depth so TLV matches */ + snd_soc_component_update_bits(component, WM8994_AIF1_DAC1_FILTERS_2, + 1 << WM8994_AIF1DAC1_3D_GAIN_SHIFT, + 1 << WM8994_AIF1DAC1_3D_GAIN_SHIFT); + snd_soc_component_update_bits(component, WM8994_AIF1_DAC2_FILTERS_2, + 1 << WM8994_AIF1DAC2_3D_GAIN_SHIFT, + 1 << WM8994_AIF1DAC2_3D_GAIN_SHIFT); + snd_soc_component_update_bits(component, WM8994_AIF2_DAC_FILTERS_2, + 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT, + 1 << WM8994_AIF2DAC_3D_GAIN_SHIFT); + + /* Unconditionally enable AIF1 ADC TDM mode on chips which can + * use this; it only affects behaviour on idle TDM clock + * cycles. */ + switch (control->type) { + case WM8994: + case WM8958: + snd_soc_component_update_bits(component, WM8994_AIF1_CONTROL_1, + WM8994_AIF1ADC_TDM, WM8994_AIF1ADC_TDM); + break; + default: + break; + } + + /* Put MICBIAS into bypass mode by default on newer devices */ + switch (control->type) { + case WM8958: + case WM1811: + snd_soc_component_update_bits(component, WM8958_MICBIAS1, + WM8958_MICB1_MODE, WM8958_MICB1_MODE); + snd_soc_component_update_bits(component, WM8958_MICBIAS2, + WM8958_MICB2_MODE, WM8958_MICB2_MODE); + break; + default: + break; + } + + wm8994->hubs.check_class_w_digital = wm8994_check_class_w_digital; + wm_hubs_update_class_w(component); + + wm8994_handle_pdata(wm8994); + + wm_hubs_add_analogue_controls(component); + snd_soc_add_component_controls(component, wm8994_common_snd_controls, + ARRAY_SIZE(wm8994_common_snd_controls)); + snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets, + ARRAY_SIZE(wm8994_dapm_widgets)); + + switch (control->type) { + case WM8994: + snd_soc_add_component_controls(component, wm8994_snd_controls, + ARRAY_SIZE(wm8994_snd_controls)); + snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets, + ARRAY_SIZE(wm8994_specific_dapm_widgets)); + if (control->revision < 4) { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets, + ARRAY_SIZE(wm8994_lateclk_revd_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets, + ARRAY_SIZE(wm8994_adc_revd_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets, + ARRAY_SIZE(wm8994_dac_revd_widgets)); + } else { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets, + ARRAY_SIZE(wm8994_lateclk_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets, + ARRAY_SIZE(wm8994_adc_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets, + ARRAY_SIZE(wm8994_dac_widgets)); + } + break; + case WM8958: + snd_soc_add_component_controls(component, wm8994_snd_controls, + ARRAY_SIZE(wm8994_snd_controls)); + snd_soc_add_component_controls(component, wm8958_snd_controls, + ARRAY_SIZE(wm8958_snd_controls)); + snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets, + ARRAY_SIZE(wm8958_dapm_widgets)); + if (control->revision < 1) { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets, + ARRAY_SIZE(wm8994_lateclk_revd_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets, + ARRAY_SIZE(wm8994_adc_revd_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets, + ARRAY_SIZE(wm8994_dac_revd_widgets)); + } else { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets, + ARRAY_SIZE(wm8994_lateclk_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets, + ARRAY_SIZE(wm8994_adc_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets, + ARRAY_SIZE(wm8994_dac_widgets)); + } + break; + + case WM1811: + snd_soc_add_component_controls(component, wm8958_snd_controls, + ARRAY_SIZE(wm8958_snd_controls)); + snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets, + ARRAY_SIZE(wm8958_dapm_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets, + ARRAY_SIZE(wm8994_lateclk_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets, + ARRAY_SIZE(wm8994_adc_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets, + ARRAY_SIZE(wm8994_dac_widgets)); + break; + } + + wm_hubs_add_analogue_routes(component, 0, 0); + ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, + wm_hubs_dcs_done, "DC servo done", + &wm8994->hubs); + if (ret == 0) + wm8994->hubs.dcs_done_irq = true; + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + + switch (control->type) { + case WM8994: + snd_soc_dapm_add_routes(dapm, wm8994_intercon, + ARRAY_SIZE(wm8994_intercon)); + + if (control->revision < 4) { + snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon, + ARRAY_SIZE(wm8994_revd_intercon)); + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon, + ARRAY_SIZE(wm8994_lateclk_revd_intercon)); + } else { + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon, + ARRAY_SIZE(wm8994_lateclk_intercon)); + } + break; + case WM8958: + if (control->revision < 1) { + snd_soc_dapm_add_routes(dapm, wm8994_intercon, + ARRAY_SIZE(wm8994_intercon)); + snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon, + ARRAY_SIZE(wm8994_revd_intercon)); + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon, + ARRAY_SIZE(wm8994_lateclk_revd_intercon)); + } else { + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon, + ARRAY_SIZE(wm8994_lateclk_intercon)); + snd_soc_dapm_add_routes(dapm, wm8958_intercon, + ARRAY_SIZE(wm8958_intercon)); + } + + wm8958_dsp2_init(component); + break; + case WM1811: + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon, + ARRAY_SIZE(wm8994_lateclk_intercon)); + snd_soc_dapm_add_routes(dapm, wm8958_intercon, + ARRAY_SIZE(wm8958_intercon)); + break; + } + + return 0; + +err_irq: + if (wm8994->jackdet) + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_GPIO(6), wm8994); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_SHRT, wm8994); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_DET, wm8994); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT, wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i, + &wm8994->fll_locked[i]); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, + &wm8994->hubs); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, component); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, component); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, component); + + return ret; +} + +static void wm8994_component_remove(struct snd_soc_component *component) +{ + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + struct wm8994 *control = wm8994->wm8994; + int i; + + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i, + &wm8994->fll_locked[i]); + + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, + &wm8994->hubs); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, component); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, component); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, component); + + if (wm8994->jackdet) + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_GPIO(6), wm8994); + + switch (control->type) { + case WM8994: + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_DET, + wm8994); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT, + wm8994); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_DET, + wm8994); + break; + + case WM1811: + case WM8958: + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); + break; + } + release_firmware(wm8994->mbc); + release_firmware(wm8994->mbc_vss); + release_firmware(wm8994->enh_eq); + kfree(wm8994->retune_mobile_texts); +} + +static const struct snd_soc_component_driver soc_component_dev_wm8994 = { + .probe = wm8994_component_probe, + .remove = wm8994_component_remove, + .suspend = wm8994_component_suspend, + .resume = wm8994_component_resume, + .set_bias_level = wm8994_set_bias_level, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm8994_probe(struct platform_device *pdev) +{ + struct wm8994_priv *wm8994; + int ret; + + wm8994 = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_priv), + GFP_KERNEL); + if (wm8994 == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, wm8994); + + mutex_init(&wm8994->fw_lock); + + wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent); + + wm8994->mclk[WM8994_MCLK1].id = "MCLK1"; + wm8994->mclk[WM8994_MCLK2].id = "MCLK2"; + + ret = devm_clk_bulk_get_optional(pdev->dev.parent, ARRAY_SIZE(wm8994->mclk), + wm8994->mclk); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get clocks: %d\n", ret); + return ret; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_wm8994, + wm8994_dai, ARRAY_SIZE(wm8994_dai)); + if (ret < 0) + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static void wm8994_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); +} + +#ifdef CONFIG_PM_SLEEP +static int wm8994_suspend(struct device *dev) +{ + struct wm8994_priv *wm8994 = dev_get_drvdata(dev); + + /* Drop down to power saving mode when system is suspended */ + if (wm8994->jackdet && !wm8994->active_refcount) + regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2, + WM1811_JACKDET_MODE_MASK, + wm8994->jackdet_mode); + + return 0; +} + +static int wm8994_resume(struct device *dev) +{ + struct wm8994_priv *wm8994 = dev_get_drvdata(dev); + + if (wm8994->jackdet && wm8994->jackdet_mode) + regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2, + WM1811_JACKDET_MODE_MASK, + WM1811_JACKDET_MODE_AUDIO); + + return 0; +} +#endif + +static const struct dev_pm_ops wm8994_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(wm8994_suspend, wm8994_resume) +}; + +static struct platform_driver wm8994_codec_driver = { + .driver = { + .name = "wm8994-codec", + .pm = &wm8994_pm_ops, + }, + .probe = wm8994_probe, + .remove_new = wm8994_remove, +}; + +module_platform_driver(wm8994_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8994 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8994-codec"); diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h new file mode 100644 index 0000000000..bc584b17bf --- /dev/null +++ b/sound/soc/codecs/wm8994.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8994.h -- WM8994 Soc Audio driver + */ + +#ifndef _WM8994_H +#define _WM8994_H + +#include +#include +#include +#include +#include +#include + +#include "wm_hubs.h" + +enum { + WM8994_MCLK1, + WM8994_MCLK2, + WM8994_NUM_MCLK +}; + +/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ +#define WM8994_SYSCLK_MCLK1 1 +#define WM8994_SYSCLK_MCLK2 2 +#define WM8994_SYSCLK_FLL1 3 +#define WM8994_SYSCLK_FLL2 4 + +/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */ +#define WM8994_SYSCLK_OPCLK 5 + +#define WM8994_FLL1 1 +#define WM8994_FLL2 2 + +#define WM8994_FLL_SRC_MCLK1 1 +#define WM8994_FLL_SRC_MCLK2 2 +#define WM8994_FLL_SRC_LRCLK 3 +#define WM8994_FLL_SRC_BCLK 4 +#define WM8994_FLL_SRC_INTERNAL 5 + +enum wm8994_vmid_mode { + WM8994_VMID_NORMAL, + WM8994_VMID_FORCE, +}; + +typedef void (*wm1811_micdet_cb)(void *data); +typedef void (*wm1811_mic_id_cb)(void *data, u16 status); + +int wm8994_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack, + int micbias); +int wm8958_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *jack, + wm1811_micdet_cb det_cb, void *det_cb_data, + wm1811_mic_id_cb id_cb, void *id_cb_data); + +int wm8994_vmid_mode(struct snd_soc_component *component, enum wm8994_vmid_mode mode); + +int wm8958_aif_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +void wm8958_dsp2_init(struct snd_soc_component *component); + +struct wm8994_micdet { + struct snd_soc_jack *jack; + bool detecting; +}; + +/* codec private data */ +struct wm8994_fll_config { + int src; + int in; + int out; +}; + +#define WM8994_NUM_DRC 3 +#define WM8994_NUM_EQ 3 + +struct wm8994; + +struct wm8994_priv { + struct wm_hubs_data hubs; + struct wm8994 *wm8994; + struct clk_bulk_data mclk[WM8994_NUM_MCLK]; + int sysclk[2]; + int sysclk_rate[2]; + int mclk_rate[2]; + int aifclk[2]; + int aifdiv[2]; + int channels[2]; + struct wm8994_fll_config fll[2], fll_suspend[2]; + struct completion fll_locked[2]; + bool fll_locked_irq; + bool fll_byp; + bool clk_has_run; + + int vmid_refcount; + int active_refcount; + enum wm8994_vmid_mode vmid_mode; + + int dac_rates[2]; + int lrclk_shared[2]; + + int mbc_ena[3]; + int hpf1_ena[3]; + int hpf2_ena[3]; + int vss_ena[3]; + int enh_eq_ena[3]; + + /* Platform dependant DRC configuration */ + const char **drc_texts; + int drc_cfg[WM8994_NUM_DRC]; + struct soc_enum drc_enum; + + /* Platform dependant ReTune mobile configuration */ + int num_retune_mobile_texts; + const char **retune_mobile_texts; + int retune_mobile_cfg[WM8994_NUM_EQ]; + struct soc_enum retune_mobile_enum; + + /* Platform dependant MBC configuration */ + int mbc_cfg; + const char **mbc_texts; + struct soc_enum mbc_enum; + + /* Platform dependant VSS configuration */ + int vss_cfg; + const char **vss_texts; + struct soc_enum vss_enum; + + /* Platform dependant VSS HPF configuration */ + int vss_hpf_cfg; + const char **vss_hpf_texts; + struct soc_enum vss_hpf_enum; + + /* Platform dependant enhanced EQ configuration */ + int enh_eq_cfg; + const char **enh_eq_texts; + struct soc_enum enh_eq_enum; + + struct mutex accdet_lock; + struct wm8994_micdet micdet[2]; + struct delayed_work mic_work; + struct delayed_work open_circuit_work; + struct delayed_work mic_complete_work; + u16 mic_status; + bool mic_detecting; + bool jack_mic; + int btn_mask; + bool jackdet; + int jackdet_mode; + struct delayed_work jackdet_bootstrap; + + int micdet_irq; + wm1811_micdet_cb micd_cb; + void *micd_cb_data; + wm1811_mic_id_cb mic_id_cb; + void *mic_id_cb_data; + + unsigned int aif1clk_enable:1; + unsigned int aif2clk_enable:1; + + unsigned int aif1clk_disable:1; + unsigned int aif2clk_disable:1; + + struct mutex fw_lock; + int dsp_active; + const struct firmware *cur_fw; + const struct firmware *mbc; + const struct firmware *mbc_vss; + const struct firmware *enh_eq; +}; + +#endif diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c new file mode 100644 index 0000000000..4ffa1896fa --- /dev/null +++ b/sound/soc/codecs/wm8995.c @@ -0,0 +1,2313 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8995.c -- WM8995 ALSA SoC Audio driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos + * + * Based on wm8994.c and wm_hubs.c by Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8995.h" + +#define WM8995_NUM_SUPPLIES 8 +static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD1", + "DBVDD2", + "DBVDD3", + "AVDD1", + "AVDD2", + "CPVDD", + "MICVDD" +}; + +static const struct reg_default wm8995_reg_defaults[] = { + { 0, 0x8995 }, + { 5, 0x0100 }, + { 16, 0x000b }, + { 17, 0x000b }, + { 24, 0x02c0 }, + { 25, 0x02c0 }, + { 26, 0x02c0 }, + { 27, 0x02c0 }, + { 28, 0x000f }, + { 32, 0x0005 }, + { 33, 0x0005 }, + { 40, 0x0003 }, + { 41, 0x0013 }, + { 48, 0x0004 }, + { 56, 0x09f8 }, + { 64, 0x1f25 }, + { 69, 0x0004 }, + { 82, 0xaaaa }, + { 84, 0x2a2a }, + { 146, 0x0060 }, + { 256, 0x0002 }, + { 257, 0x8004 }, + { 520, 0x0010 }, + { 528, 0x0083 }, + { 529, 0x0083 }, + { 548, 0x0c80 }, + { 580, 0x0c80 }, + { 768, 0x4050 }, + { 769, 0x4000 }, + { 771, 0x0040 }, + { 772, 0x0040 }, + { 773, 0x0040 }, + { 774, 0x0004 }, + { 775, 0x0100 }, + { 784, 0x4050 }, + { 785, 0x4000 }, + { 787, 0x0040 }, + { 788, 0x0040 }, + { 789, 0x0040 }, + { 1024, 0x00c0 }, + { 1025, 0x00c0 }, + { 1026, 0x00c0 }, + { 1027, 0x00c0 }, + { 1028, 0x00c0 }, + { 1029, 0x00c0 }, + { 1030, 0x00c0 }, + { 1031, 0x00c0 }, + { 1056, 0x0200 }, + { 1057, 0x0010 }, + { 1058, 0x0200 }, + { 1059, 0x0010 }, + { 1088, 0x0098 }, + { 1089, 0x0845 }, + { 1104, 0x0098 }, + { 1105, 0x0845 }, + { 1152, 0x6318 }, + { 1153, 0x6300 }, + { 1154, 0x0fca }, + { 1155, 0x0400 }, + { 1156, 0x00d8 }, + { 1157, 0x1eb5 }, + { 1158, 0xf145 }, + { 1159, 0x0b75 }, + { 1160, 0x01c5 }, + { 1161, 0x1c58 }, + { 1162, 0xf373 }, + { 1163, 0x0a54 }, + { 1164, 0x0558 }, + { 1165, 0x168e }, + { 1166, 0xf829 }, + { 1167, 0x07ad }, + { 1168, 0x1103 }, + { 1169, 0x0564 }, + { 1170, 0x0559 }, + { 1171, 0x4000 }, + { 1184, 0x6318 }, + { 1185, 0x6300 }, + { 1186, 0x0fca }, + { 1187, 0x0400 }, + { 1188, 0x00d8 }, + { 1189, 0x1eb5 }, + { 1190, 0xf145 }, + { 1191, 0x0b75 }, + { 1192, 0x01c5 }, + { 1193, 0x1c58 }, + { 1194, 0xf373 }, + { 1195, 0x0a54 }, + { 1196, 0x0558 }, + { 1197, 0x168e }, + { 1198, 0xf829 }, + { 1199, 0x07ad }, + { 1200, 0x1103 }, + { 1201, 0x0564 }, + { 1202, 0x0559 }, + { 1203, 0x4000 }, + { 1280, 0x00c0 }, + { 1281, 0x00c0 }, + { 1282, 0x00c0 }, + { 1283, 0x00c0 }, + { 1312, 0x0200 }, + { 1313, 0x0010 }, + { 1344, 0x0098 }, + { 1345, 0x0845 }, + { 1408, 0x6318 }, + { 1409, 0x6300 }, + { 1410, 0x0fca }, + { 1411, 0x0400 }, + { 1412, 0x00d8 }, + { 1413, 0x1eb5 }, + { 1414, 0xf145 }, + { 1415, 0x0b75 }, + { 1416, 0x01c5 }, + { 1417, 0x1c58 }, + { 1418, 0xf373 }, + { 1419, 0x0a54 }, + { 1420, 0x0558 }, + { 1421, 0x168e }, + { 1422, 0xf829 }, + { 1423, 0x07ad }, + { 1424, 0x1103 }, + { 1425, 0x0564 }, + { 1426, 0x0559 }, + { 1427, 0x4000 }, + { 1568, 0x0002 }, + { 1792, 0xa100 }, + { 1793, 0xa101 }, + { 1794, 0xa101 }, + { 1795, 0xa101 }, + { 1796, 0xa101 }, + { 1797, 0xa101 }, + { 1798, 0xa101 }, + { 1799, 0xa101 }, + { 1800, 0xa101 }, + { 1801, 0xa101 }, + { 1802, 0xa101 }, + { 1803, 0xa101 }, + { 1804, 0xa101 }, + { 1805, 0xa101 }, + { 1825, 0x0055 }, + { 1848, 0x3fff }, + { 1849, 0x1fff }, + { 2049, 0x0001 }, + { 2050, 0x0069 }, + { 2056, 0x0002 }, + { 2057, 0x0003 }, + { 2058, 0x0069 }, + { 12288, 0x0001 }, + { 12289, 0x0001 }, + { 12291, 0x0006 }, + { 12292, 0x0040 }, + { 12293, 0x0001 }, + { 12294, 0x000f }, + { 12295, 0x0006 }, + { 12296, 0x0001 }, + { 12297, 0x0003 }, + { 12298, 0x0104 }, + { 12300, 0x0060 }, + { 12301, 0x0011 }, + { 12302, 0x0401 }, + { 12304, 0x0050 }, + { 12305, 0x0003 }, + { 12306, 0x0100 }, + { 12308, 0x0051 }, + { 12309, 0x0003 }, + { 12310, 0x0104 }, + { 12311, 0x000a }, + { 12312, 0x0060 }, + { 12313, 0x003b }, + { 12314, 0x0502 }, + { 12315, 0x0100 }, + { 12316, 0x2fff }, + { 12320, 0x2fff }, + { 12324, 0x2fff }, + { 12328, 0x2fff }, + { 12332, 0x2fff }, + { 12336, 0x2fff }, + { 12340, 0x2fff }, + { 12344, 0x2fff }, + { 12348, 0x2fff }, + { 12352, 0x0001 }, + { 12353, 0x0001 }, + { 12355, 0x0006 }, + { 12356, 0x0040 }, + { 12357, 0x0001 }, + { 12358, 0x000f }, + { 12359, 0x0006 }, + { 12360, 0x0001 }, + { 12361, 0x0003 }, + { 12362, 0x0104 }, + { 12364, 0x0060 }, + { 12365, 0x0011 }, + { 12366, 0x0401 }, + { 12368, 0x0050 }, + { 12369, 0x0003 }, + { 12370, 0x0100 }, + { 12372, 0x0060 }, + { 12373, 0x003b }, + { 12374, 0x0502 }, + { 12375, 0x0100 }, + { 12376, 0x2fff }, + { 12380, 0x2fff }, + { 12384, 0x2fff }, + { 12388, 0x2fff }, + { 12392, 0x2fff }, + { 12396, 0x2fff }, + { 12400, 0x2fff }, + { 12404, 0x2fff }, + { 12408, 0x2fff }, + { 12412, 0x2fff }, + { 12416, 0x0001 }, + { 12417, 0x0001 }, + { 12419, 0x0006 }, + { 12420, 0x0040 }, + { 12421, 0x0001 }, + { 12422, 0x000f }, + { 12423, 0x0006 }, + { 12424, 0x0001 }, + { 12425, 0x0003 }, + { 12426, 0x0106 }, + { 12428, 0x0061 }, + { 12429, 0x0011 }, + { 12430, 0x0401 }, + { 12432, 0x0050 }, + { 12433, 0x0003 }, + { 12434, 0x0102 }, + { 12436, 0x0051 }, + { 12437, 0x0003 }, + { 12438, 0x0106 }, + { 12439, 0x000a }, + { 12440, 0x0061 }, + { 12441, 0x003b }, + { 12442, 0x0502 }, + { 12443, 0x0100 }, + { 12444, 0x2fff }, + { 12448, 0x2fff }, + { 12452, 0x2fff }, + { 12456, 0x2fff }, + { 12460, 0x2fff }, + { 12464, 0x2fff }, + { 12468, 0x2fff }, + { 12472, 0x2fff }, + { 12476, 0x2fff }, + { 12480, 0x0001 }, + { 12481, 0x0001 }, + { 12483, 0x0006 }, + { 12484, 0x0040 }, + { 12485, 0x0001 }, + { 12486, 0x000f }, + { 12487, 0x0006 }, + { 12488, 0x0001 }, + { 12489, 0x0003 }, + { 12490, 0x0106 }, + { 12492, 0x0061 }, + { 12493, 0x0011 }, + { 12494, 0x0401 }, + { 12496, 0x0050 }, + { 12497, 0x0003 }, + { 12498, 0x0102 }, + { 12500, 0x0061 }, + { 12501, 0x003b }, + { 12502, 0x0502 }, + { 12503, 0x0100 }, + { 12504, 0x2fff }, + { 12508, 0x2fff }, + { 12512, 0x2fff }, + { 12516, 0x2fff }, + { 12520, 0x2fff }, + { 12524, 0x2fff }, + { 12528, 0x2fff }, + { 12532, 0x2fff }, + { 12536, 0x2fff }, + { 12540, 0x2fff }, + { 12544, 0x0060 }, + { 12546, 0x0601 }, + { 12548, 0x0050 }, + { 12550, 0x0100 }, + { 12552, 0x0001 }, + { 12554, 0x0104 }, + { 12555, 0x0100 }, + { 12556, 0x2fff }, + { 12560, 0x2fff }, + { 12564, 0x2fff }, + { 12568, 0x2fff }, + { 12572, 0x2fff }, + { 12576, 0x2fff }, + { 12580, 0x2fff }, + { 12584, 0x2fff }, + { 12588, 0x2fff }, + { 12592, 0x2fff }, + { 12596, 0x2fff }, + { 12600, 0x2fff }, + { 12604, 0x2fff }, + { 12608, 0x0061 }, + { 12610, 0x0601 }, + { 12612, 0x0050 }, + { 12614, 0x0102 }, + { 12616, 0x0001 }, + { 12618, 0x0106 }, + { 12619, 0x0100 }, + { 12620, 0x2fff }, + { 12624, 0x2fff }, + { 12628, 0x2fff }, + { 12632, 0x2fff }, + { 12636, 0x2fff }, + { 12640, 0x2fff }, + { 12644, 0x2fff }, + { 12648, 0x2fff }, + { 12652, 0x2fff }, + { 12656, 0x2fff }, + { 12660, 0x2fff }, + { 12664, 0x2fff }, + { 12668, 0x2fff }, + { 12672, 0x0060 }, + { 12674, 0x0601 }, + { 12676, 0x0061 }, + { 12678, 0x0601 }, + { 12680, 0x0050 }, + { 12682, 0x0300 }, + { 12684, 0x0001 }, + { 12686, 0x0304 }, + { 12688, 0x0040 }, + { 12690, 0x000f }, + { 12692, 0x0001 }, + { 12695, 0x0100 }, +}; + +struct fll_config { + int src; + int in; + int out; +}; + +struct wm8995_priv { + struct regmap *regmap; + int sysclk[2]; + int mclk[2]; + int aifclk[2]; + struct fll_config fll[2], fll_suspend[2]; + struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES]; + struct notifier_block disable_nb[WM8995_NUM_SUPPLIES]; + struct snd_soc_component *component; +}; + +/* + * We can't use the same notifier block for more than one supply and + * there's no way I can see to get from a callback to the caller + * except container_of(). + */ +#define WM8995_REGULATOR_EVENT(n) \ +static int wm8995_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \ + disable_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + regcache_mark_dirty(wm8995->regmap); \ + } \ + return 0; \ +} + +WM8995_REGULATOR_EVENT(0) +WM8995_REGULATOR_EVENT(1) +WM8995_REGULATOR_EVENT(2) +WM8995_REGULATOR_EVENT(3) +WM8995_REGULATOR_EVENT(4) +WM8995_REGULATOR_EVENT(5) +WM8995_REGULATOR_EVENT(6) +WM8995_REGULATOR_EVENT(7) + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0); +static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0); + +static const char *in1l_text[] = { + "Differential", "Single-ended IN1LN", "Single-ended IN1LP" +}; + +static SOC_ENUM_SINGLE_DECL(in1l_enum, WM8995_LEFT_LINE_INPUT_CONTROL, + 2, in1l_text); + +static const char *in1r_text[] = { + "Differential", "Single-ended IN1RN", "Single-ended IN1RP" +}; + +static SOC_ENUM_SINGLE_DECL(in1r_enum, WM8995_LEFT_LINE_INPUT_CONTROL, + 0, in1r_text); + +static const char *dmic_src_text[] = { + "DMICDAT1", "DMICDAT2", "DMICDAT3" +}; + +static SOC_ENUM_SINGLE_DECL(dmic_src1_enum, WM8995_POWER_MANAGEMENT_5, + 8, dmic_src_text); +static SOC_ENUM_SINGLE_DECL(dmic_src2_enum, WM8995_POWER_MANAGEMENT_5, + 6, dmic_src_text); + +static const struct snd_kcontrol_new wm8995_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC1 Volume", WM8995_DAC1_LEFT_VOLUME, + WM8995_DAC1_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R("DAC1 Switch", WM8995_DAC1_LEFT_VOLUME, + WM8995_DAC1_RIGHT_VOLUME, 9, 1, 1), + + SOC_DOUBLE_R_TLV("DAC2 Volume", WM8995_DAC2_LEFT_VOLUME, + WM8995_DAC2_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R("DAC2 Switch", WM8995_DAC2_LEFT_VOLUME, + WM8995_DAC2_RIGHT_VOLUME, 9, 1, 1), + + SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8995_AIF1_DAC1_LEFT_VOLUME, + WM8995_AIF1_DAC1_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8995_AIF1_DAC2_LEFT_VOLUME, + WM8995_AIF1_DAC2_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R_TLV("AIF2DAC Volume", WM8995_AIF2_DAC_LEFT_VOLUME, + WM8995_AIF2_DAC_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + + SOC_DOUBLE_R_TLV("IN1LR Volume", WM8995_LEFT_LINE_INPUT_1_VOLUME, + WM8995_RIGHT_LINE_INPUT_1_VOLUME, 0, 31, 0, in1lr_pga_tlv), + + SOC_SINGLE_TLV("IN1L Boost", WM8995_LEFT_LINE_INPUT_CONTROL, + 4, 3, 0, in1l_boost_tlv), + + SOC_ENUM("IN1L Mode", in1l_enum), + SOC_ENUM("IN1R Mode", in1r_enum), + + SOC_ENUM("DMIC1 SRC", dmic_src1_enum), + SOC_ENUM("DMIC2 SRC", dmic_src2_enum), + + SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8995_DAC1_MIXER_VOLUMES, 0, 5, + 24, 0, sidetone_tlv), + SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8995_DAC2_MIXER_VOLUMES, 0, 5, + 24, 0, sidetone_tlv), + + SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8995_AIF1_ADC1_LEFT_VOLUME, + WM8995_AIF1_ADC1_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8995_AIF1_ADC2_LEFT_VOLUME, + WM8995_AIF1_ADC2_RIGHT_VOLUME, 0, 96, 0, digital_tlv), + SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8995_AIF2_ADC_LEFT_VOLUME, + WM8995_AIF2_ADC_RIGHT_VOLUME, 0, 96, 0, digital_tlv) +}; + +static void wm8995_update_class_w(struct snd_soc_component *component) +{ + int enable = 1; + int source = 0; /* GCC flow analysis can't track enable */ + int reg, reg_r; + + /* We also need the same setting for L/R and only one path */ + reg = snd_soc_component_read(component, WM8995_DAC1_LEFT_MIXER_ROUTING); + switch (reg) { + case WM8995_AIF2DACL_TO_DAC1L: + dev_dbg(component->dev, "Class W source AIF2DAC\n"); + source = 2 << WM8995_CP_DYN_SRC_SEL_SHIFT; + break; + case WM8995_AIF1DAC2L_TO_DAC1L: + dev_dbg(component->dev, "Class W source AIF1DAC2\n"); + source = 1 << WM8995_CP_DYN_SRC_SEL_SHIFT; + break; + case WM8995_AIF1DAC1L_TO_DAC1L: + dev_dbg(component->dev, "Class W source AIF1DAC1\n"); + source = 0 << WM8995_CP_DYN_SRC_SEL_SHIFT; + break; + default: + dev_dbg(component->dev, "DAC mixer setting: %x\n", reg); + enable = 0; + break; + } + + reg_r = snd_soc_component_read(component, WM8995_DAC1_RIGHT_MIXER_ROUTING); + if (reg_r != reg) { + dev_dbg(component->dev, "Left and right DAC mixers different\n"); + enable = 0; + } + + if (enable) { + dev_dbg(component->dev, "Class W enabled\n"); + snd_soc_component_update_bits(component, WM8995_CLASS_W_1, + WM8995_CP_DYN_PWR_MASK | + WM8995_CP_DYN_SRC_SEL_MASK, + source | WM8995_CP_DYN_PWR); + } else { + dev_dbg(component->dev, "Class W disabled\n"); + snd_soc_component_update_bits(component, WM8995_CLASS_W_1, + WM8995_CP_DYN_PWR_MASK, 0); + } +} + +static int check_clk_sys(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + unsigned int reg; + const char *clk; + + reg = snd_soc_component_read(component, WM8995_CLOCKING_1); + /* Check what we're currently using for CLK_SYS */ + if (reg & WM8995_SYSCLK_SRC) + clk = "AIF2CLK"; + else + clk = "AIF1CLK"; + return !strcmp(source->name, clk); +} + +static int wm8995_put_class_w(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + int ret; + + ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); + wm8995_update_class_w(component); + return ret; +} + +static int hp_supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enable the headphone amp */ + snd_soc_component_update_bits(component, WM8995_POWER_MANAGEMENT_1, + WM8995_HPOUT1L_ENA_MASK | + WM8995_HPOUT1R_ENA_MASK, + WM8995_HPOUT1L_ENA | + WM8995_HPOUT1R_ENA); + + /* Enable the second stage */ + snd_soc_component_update_bits(component, WM8995_ANALOGUE_HP_1, + WM8995_HPOUT1L_DLY_MASK | + WM8995_HPOUT1R_DLY_MASK, + WM8995_HPOUT1L_DLY | + WM8995_HPOUT1R_DLY); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, WM8995_CHARGE_PUMP_1, + WM8995_CP_ENA_MASK, 0); + break; + } + + return 0; +} + +static void dc_servo_cmd(struct snd_soc_component *component, + unsigned int reg, unsigned int val, unsigned int mask) +{ + int timeout = 10; + + dev_dbg(component->dev, "%s: reg = %#x, val = %#x, mask = %#x\n", + __func__, reg, val, mask); + + snd_soc_component_write(component, reg, val); + while (timeout--) { + msleep(10); + val = snd_soc_component_read(component, WM8995_DC_SERVO_READBACK_0); + if ((val & mask) == mask) + return; + } + + dev_err(component->dev, "Timed out waiting for DC Servo\n"); +} + +static int hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int reg; + + reg = snd_soc_component_read(component, WM8995_ANALOGUE_HP_1); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, WM8995_CHARGE_PUMP_1, + WM8995_CP_ENA_MASK, WM8995_CP_ENA); + + msleep(5); + + snd_soc_component_update_bits(component, WM8995_POWER_MANAGEMENT_1, + WM8995_HPOUT1L_ENA_MASK | + WM8995_HPOUT1R_ENA_MASK, + WM8995_HPOUT1L_ENA | WM8995_HPOUT1R_ENA); + + udelay(20); + + reg |= WM8995_HPOUT1L_DLY | WM8995_HPOUT1R_DLY; + snd_soc_component_write(component, WM8995_ANALOGUE_HP_1, reg); + + snd_soc_component_write(component, WM8995_DC_SERVO_1, WM8995_DCS_ENA_CHAN_0 | + WM8995_DCS_ENA_CHAN_1); + + dc_servo_cmd(component, WM8995_DC_SERVO_2, + WM8995_DCS_TRIG_STARTUP_0 | + WM8995_DCS_TRIG_STARTUP_1, + WM8995_DCS_TRIG_DAC_WR_0 | + WM8995_DCS_TRIG_DAC_WR_1); + + reg |= WM8995_HPOUT1R_OUTP | WM8995_HPOUT1R_RMV_SHORT | + WM8995_HPOUT1L_OUTP | WM8995_HPOUT1L_RMV_SHORT; + snd_soc_component_write(component, WM8995_ANALOGUE_HP_1, reg); + + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, WM8995_ANALOGUE_HP_1, + WM8995_HPOUT1L_OUTP_MASK | + WM8995_HPOUT1R_OUTP_MASK | + WM8995_HPOUT1L_RMV_SHORT_MASK | + WM8995_HPOUT1R_RMV_SHORT_MASK, 0); + + snd_soc_component_update_bits(component, WM8995_ANALOGUE_HP_1, + WM8995_HPOUT1L_DLY_MASK | + WM8995_HPOUT1R_DLY_MASK, 0); + + snd_soc_component_write(component, WM8995_DC_SERVO_1, 0); + + snd_soc_component_update_bits(component, WM8995_POWER_MANAGEMENT_1, + WM8995_HPOUT1L_ENA_MASK | + WM8995_HPOUT1R_ENA_MASK, + 0); + break; + } + + return 0; +} + +static int configure_aif_clock(struct snd_soc_component *component, int aif) +{ + struct wm8995_priv *wm8995; + int rate; + int reg1 = 0; + int offset; + + wm8995 = snd_soc_component_get_drvdata(component); + + if (aif) + offset = 4; + else + offset = 0; + + switch (wm8995->sysclk[aif]) { + case WM8995_SYSCLK_MCLK1: + rate = wm8995->mclk[0]; + break; + case WM8995_SYSCLK_MCLK2: + reg1 |= 0x8; + rate = wm8995->mclk[1]; + break; + case WM8995_SYSCLK_FLL1: + reg1 |= 0x10; + rate = wm8995->fll[0].out; + break; + case WM8995_SYSCLK_FLL2: + reg1 |= 0x18; + rate = wm8995->fll[1].out; + break; + default: + return -EINVAL; + } + + if (rate >= 13500000) { + rate /= 2; + reg1 |= WM8995_AIF1CLK_DIV; + + dev_dbg(component->dev, "Dividing AIF%d clock to %dHz\n", + aif + 1, rate); + } + + wm8995->aifclk[aif] = rate; + + snd_soc_component_update_bits(component, WM8995_AIF1_CLOCKING_1 + offset, + WM8995_AIF1CLK_SRC_MASK | WM8995_AIF1CLK_DIV_MASK, + reg1); + return 0; +} + +static int configure_clock(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8995_priv *wm8995; + int change, new; + + wm8995 = snd_soc_component_get_drvdata(component); + + /* Bring up the AIF clocks first */ + configure_aif_clock(component, 0); + configure_aif_clock(component, 1); + + /* + * Then switch CLK_SYS over to the higher of them; a change + * can only happen as a result of a clocking change which can + * only be made outside of DAPM so we can safely redo the + * clocking. + */ + + /* If they're equal it doesn't matter which is used */ + if (wm8995->aifclk[0] == wm8995->aifclk[1]) + return 0; + + if (wm8995->aifclk[0] < wm8995->aifclk[1]) + new = WM8995_SYSCLK_SRC; + else + new = 0; + + change = snd_soc_component_update_bits(component, WM8995_CLOCKING_1, + WM8995_SYSCLK_SRC_MASK, new); + if (!change) + return 0; + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int clk_sys_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return configure_clock(component); + + case SND_SOC_DAPM_POST_PMD: + configure_clock(component); + break; + } + + return 0; +} + +static const char *sidetone_text[] = { + "ADC/DMIC1", "DMIC2", +}; + +static SOC_ENUM_SINGLE_DECL(sidetone1_enum, WM8995_SIDETONE, 0, sidetone_text); + +static const struct snd_kcontrol_new sidetone1_mux = + SOC_DAPM_ENUM("Left Sidetone Mux", sidetone1_enum); + +static SOC_ENUM_SINGLE_DECL(sidetone2_enum, WM8995_SIDETONE, 1, sidetone_text); + +static const struct snd_kcontrol_new sidetone2_mux = + SOC_DAPM_ENUM("Right Sidetone Mux", sidetone2_enum); + +static const struct snd_kcontrol_new aif1adc1l_mix[] = { + SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif1adc1r_mix[] = { + SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif1adc2l_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif1adc2r_mix[] = { + SOC_DAPM_SINGLE("DMIC Switch", WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new dac1l_mix[] = { + WM8995_CLASS_W_SWITCH("Right Sidetone Switch", WM8995_DAC1_LEFT_MIXER_ROUTING, + 5, 1, 0), + WM8995_CLASS_W_SWITCH("Left Sidetone Switch", WM8995_DAC1_LEFT_MIXER_ROUTING, + 4, 1, 0), + WM8995_CLASS_W_SWITCH("AIF2 Switch", WM8995_DAC1_LEFT_MIXER_ROUTING, + 2, 1, 0), + WM8995_CLASS_W_SWITCH("AIF1.2 Switch", WM8995_DAC1_LEFT_MIXER_ROUTING, + 1, 1, 0), + WM8995_CLASS_W_SWITCH("AIF1.1 Switch", WM8995_DAC1_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new dac1r_mix[] = { + WM8995_CLASS_W_SWITCH("Right Sidetone Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING, + 5, 1, 0), + WM8995_CLASS_W_SWITCH("Left Sidetone Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING, + 4, 1, 0), + WM8995_CLASS_W_SWITCH("AIF2 Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING, + 2, 1, 0), + WM8995_CLASS_W_SWITCH("AIF1.2 Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING, + 1, 1, 0), + WM8995_CLASS_W_SWITCH("AIF1.1 Switch", WM8995_DAC1_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif2dac2l_mix[] = { + SOC_DAPM_SINGLE("Right Sidetone Switch", WM8995_DAC2_LEFT_MIXER_ROUTING, + 5, 1, 0), + SOC_DAPM_SINGLE("Left Sidetone Switch", WM8995_DAC2_LEFT_MIXER_ROUTING, + 4, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_DAC2_LEFT_MIXER_ROUTING, + 2, 1, 0), + SOC_DAPM_SINGLE("AIF1.2 Switch", WM8995_DAC2_LEFT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF1.1 Switch", WM8995_DAC2_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new aif2dac2r_mix[] = { + SOC_DAPM_SINGLE("Right Sidetone Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING, + 5, 1, 0), + SOC_DAPM_SINGLE("Left Sidetone Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING, + 4, 1, 0), + SOC_DAPM_SINGLE("AIF2 Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING, + 2, 1, 0), + SOC_DAPM_SINGLE("AIF1.2 Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING, + 1, 1, 0), + SOC_DAPM_SINGLE("AIF1.1 Switch", WM8995_DAC2_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new in1l_pga = + SOC_DAPM_SINGLE("IN1L Switch", WM8995_POWER_MANAGEMENT_2, 5, 1, 0); + +static const struct snd_kcontrol_new in1r_pga = + SOC_DAPM_SINGLE("IN1R Switch", WM8995_POWER_MANAGEMENT_2, 4, 1, 0); + +static const char *adc_mux_text[] = { + "ADC", + "DMIC", +}; + +static SOC_ENUM_SINGLE_VIRT_DECL(adc_enum, adc_mux_text); + +static const struct snd_kcontrol_new adcl_mux = + SOC_DAPM_ENUM("ADCL Mux", adc_enum); + +static const struct snd_kcontrol_new adcr_mux = + SOC_DAPM_ENUM("ADCR Mux", adc_enum); + +static const char *spk_src_text[] = { + "DAC1L", "DAC1R", "DAC2L", "DAC2R" +}; + +static SOC_ENUM_SINGLE_DECL(spk1l_src_enum, WM8995_LEFT_PDM_SPEAKER_1, + 0, spk_src_text); +static SOC_ENUM_SINGLE_DECL(spk1r_src_enum, WM8995_RIGHT_PDM_SPEAKER_1, + 0, spk_src_text); +static SOC_ENUM_SINGLE_DECL(spk2l_src_enum, WM8995_LEFT_PDM_SPEAKER_2, + 0, spk_src_text); +static SOC_ENUM_SINGLE_DECL(spk2r_src_enum, WM8995_RIGHT_PDM_SPEAKER_2, + 0, spk_src_text); + +static const struct snd_kcontrol_new spk1l_mux = + SOC_DAPM_ENUM("SPK1L SRC", spk1l_src_enum); +static const struct snd_kcontrol_new spk1r_mux = + SOC_DAPM_ENUM("SPK1R SRC", spk1r_src_enum); +static const struct snd_kcontrol_new spk2l_mux = + SOC_DAPM_ENUM("SPK2L SRC", spk2l_src_enum); +static const struct snd_kcontrol_new spk2r_mux = + SOC_DAPM_ENUM("SPK2R SRC", spk2r_src_enum); + +static const struct snd_soc_dapm_widget wm8995_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC1DAT"), + SND_SOC_DAPM_INPUT("DMIC2DAT"), + + SND_SOC_DAPM_INPUT("IN1L"), + SND_SOC_DAPM_INPUT("IN1R"), + + SND_SOC_DAPM_MIXER("IN1L PGA", SND_SOC_NOPM, 0, 0, + &in1l_pga, 1), + SND_SOC_DAPM_MIXER("IN1R PGA", SND_SOC_NOPM, 0, 0, + &in1r_pga, 1), + + SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8995_POWER_MANAGEMENT_1, 8, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8995_POWER_MANAGEMENT_1, 9, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8995_AIF1_CLOCKING_1, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8995_AIF2_CLOCKING_1, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSP1CLK", WM8995_CLOCKING_1, 3, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DSP2CLK", WM8995_CLOCKING_1, 2, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SYSDSPCLK", WM8995_CLOCKING_1, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", "AIF1 Capture", 0, + WM8995_POWER_MANAGEMENT_3, 9, 0), + SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", "AIF1 Capture", 0, + WM8995_POWER_MANAGEMENT_3, 8, 0), + SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", "AIF1 Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", "AIF1 Capture", + 0, WM8995_POWER_MANAGEMENT_3, 11, 0), + SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture", + 0, WM8995_POWER_MANAGEMENT_3, 10, 0), + + SND_SOC_DAPM_MUX("ADCL Mux", SND_SOC_NOPM, 1, 0, &adcl_mux), + SND_SOC_DAPM_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0, &adcr_mux), + + SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8995_POWER_MANAGEMENT_3, 5, 0), + SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8995_POWER_MANAGEMENT_3, 4, 0), + SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8995_POWER_MANAGEMENT_3, 3, 0), + SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8995_POWER_MANAGEMENT_3, 2, 0), + + SND_SOC_DAPM_ADC("ADCL", NULL, WM8995_POWER_MANAGEMENT_3, 1, 0), + SND_SOC_DAPM_ADC("ADCR", NULL, WM8995_POWER_MANAGEMENT_3, 0, 0), + + SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0, + aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)), + SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0, + aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)), + SND_SOC_DAPM_MIXER("AIF1ADC2L Mixer", SND_SOC_NOPM, 0, 0, + aif1adc2l_mix, ARRAY_SIZE(aif1adc2l_mix)), + SND_SOC_DAPM_MIXER("AIF1ADC2R Mixer", SND_SOC_NOPM, 0, 0, + aif1adc2r_mix, ARRAY_SIZE(aif1adc2r_mix)), + + SND_SOC_DAPM_AIF_IN("AIF1DAC1L", NULL, 0, WM8995_POWER_MANAGEMENT_4, + 9, 0), + SND_SOC_DAPM_AIF_IN("AIF1DAC1R", NULL, 0, WM8995_POWER_MANAGEMENT_4, + 8, 0), + SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, + 0, 0), + + SND_SOC_DAPM_AIF_IN("AIF1DAC2L", NULL, 0, WM8995_POWER_MANAGEMENT_4, + 11, 0), + SND_SOC_DAPM_AIF_IN("AIF1DAC2R", NULL, 0, WM8995_POWER_MANAGEMENT_4, + 10, 0), + + SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0, + aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)), + SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0, + aif2dac2r_mix, ARRAY_SIZE(aif2dac2r_mix)), + + SND_SOC_DAPM_DAC("DAC2L", NULL, WM8995_POWER_MANAGEMENT_4, 3, 0), + SND_SOC_DAPM_DAC("DAC2R", NULL, WM8995_POWER_MANAGEMENT_4, 2, 0), + SND_SOC_DAPM_DAC("DAC1L", NULL, WM8995_POWER_MANAGEMENT_4, 1, 0), + SND_SOC_DAPM_DAC("DAC1R", NULL, WM8995_POWER_MANAGEMENT_4, 0, 0), + + SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0, dac1l_mix, + ARRAY_SIZE(dac1l_mix)), + SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0, dac1r_mix, + ARRAY_SIZE(dac1r_mix)), + + SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &sidetone1_mux), + SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &sidetone2_mux), + + SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, + hp_supply_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX("SPK1L Driver", WM8995_LEFT_PDM_SPEAKER_1, + 4, 0, &spk1l_mux), + SND_SOC_DAPM_MUX("SPK1R Driver", WM8995_RIGHT_PDM_SPEAKER_1, + 4, 0, &spk1r_mux), + SND_SOC_DAPM_MUX("SPK2L Driver", WM8995_LEFT_PDM_SPEAKER_2, + 4, 0, &spk2l_mux), + SND_SOC_DAPM_MUX("SPK2R Driver", WM8995_RIGHT_PDM_SPEAKER_2, + 4, 0, &spk2r_mux), + + SND_SOC_DAPM_SUPPLY("LDO2", WM8995_POWER_MANAGEMENT_2, 1, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("HP1L"), + SND_SOC_DAPM_OUTPUT("HP1R"), + SND_SOC_DAPM_OUTPUT("SPK1L"), + SND_SOC_DAPM_OUTPUT("SPK1R"), + SND_SOC_DAPM_OUTPUT("SPK2L"), + SND_SOC_DAPM_OUTPUT("SPK2R") +}; + +static const struct snd_soc_dapm_route wm8995_intercon[] = { + { "CLK_SYS", NULL, "AIF1CLK", check_clk_sys }, + { "CLK_SYS", NULL, "AIF2CLK", check_clk_sys }, + + { "DSP1CLK", NULL, "CLK_SYS" }, + { "DSP2CLK", NULL, "CLK_SYS" }, + { "SYSDSPCLK", NULL, "CLK_SYS" }, + + { "AIF1ADC1L", NULL, "AIF1CLK" }, + { "AIF1ADC1L", NULL, "DSP1CLK" }, + { "AIF1ADC1R", NULL, "AIF1CLK" }, + { "AIF1ADC1R", NULL, "DSP1CLK" }, + { "AIF1ADC1R", NULL, "SYSDSPCLK" }, + + { "AIF1ADC2L", NULL, "AIF1CLK" }, + { "AIF1ADC2L", NULL, "DSP1CLK" }, + { "AIF1ADC2R", NULL, "AIF1CLK" }, + { "AIF1ADC2R", NULL, "DSP1CLK" }, + { "AIF1ADC2R", NULL, "SYSDSPCLK" }, + + { "DMIC1L", NULL, "DMIC1DAT" }, + { "DMIC1L", NULL, "CLK_SYS" }, + { "DMIC1R", NULL, "DMIC1DAT" }, + { "DMIC1R", NULL, "CLK_SYS" }, + { "DMIC2L", NULL, "DMIC2DAT" }, + { "DMIC2L", NULL, "CLK_SYS" }, + { "DMIC2R", NULL, "DMIC2DAT" }, + { "DMIC2R", NULL, "CLK_SYS" }, + + { "ADCL", NULL, "AIF1CLK" }, + { "ADCL", NULL, "DSP1CLK" }, + { "ADCL", NULL, "SYSDSPCLK" }, + + { "ADCR", NULL, "AIF1CLK" }, + { "ADCR", NULL, "DSP1CLK" }, + { "ADCR", NULL, "SYSDSPCLK" }, + + { "IN1L PGA", "IN1L Switch", "IN1L" }, + { "IN1R PGA", "IN1R Switch", "IN1R" }, + { "IN1L PGA", NULL, "LDO2" }, + { "IN1R PGA", NULL, "LDO2" }, + + { "ADCL", NULL, "IN1L PGA" }, + { "ADCR", NULL, "IN1R PGA" }, + + { "ADCL Mux", "ADC", "ADCL" }, + { "ADCL Mux", "DMIC", "DMIC1L" }, + { "ADCR Mux", "ADC", "ADCR" }, + { "ADCR Mux", "DMIC", "DMIC1R" }, + + /* AIF1 outputs */ + { "AIF1ADC1L", NULL, "AIF1ADC1L Mixer" }, + { "AIF1ADC1L Mixer", "ADC/DMIC Switch", "ADCL Mux" }, + + { "AIF1ADC1R", NULL, "AIF1ADC1R Mixer" }, + { "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" }, + + { "AIF1ADC2L", NULL, "AIF1ADC2L Mixer" }, + { "AIF1ADC2L Mixer", "DMIC Switch", "DMIC2L" }, + + { "AIF1ADC2R", NULL, "AIF1ADC2R Mixer" }, + { "AIF1ADC2R Mixer", "DMIC Switch", "DMIC2R" }, + + /* Sidetone */ + { "Left Sidetone", "ADC/DMIC1", "AIF1ADC1L" }, + { "Left Sidetone", "DMIC2", "AIF1ADC2L" }, + { "Right Sidetone", "ADC/DMIC1", "AIF1ADC1R" }, + { "Right Sidetone", "DMIC2", "AIF1ADC2R" }, + + { "AIF1DAC1L", NULL, "AIF1CLK" }, + { "AIF1DAC1L", NULL, "DSP1CLK" }, + { "AIF1DAC1R", NULL, "AIF1CLK" }, + { "AIF1DAC1R", NULL, "DSP1CLK" }, + { "AIF1DAC1R", NULL, "SYSDSPCLK" }, + + { "AIF1DAC2L", NULL, "AIF1CLK" }, + { "AIF1DAC2L", NULL, "DSP1CLK" }, + { "AIF1DAC2R", NULL, "AIF1CLK" }, + { "AIF1DAC2R", NULL, "DSP1CLK" }, + { "AIF1DAC2R", NULL, "SYSDSPCLK" }, + + { "DAC1L", NULL, "AIF1CLK" }, + { "DAC1L", NULL, "DSP1CLK" }, + { "DAC1L", NULL, "SYSDSPCLK" }, + + { "DAC1R", NULL, "AIF1CLK" }, + { "DAC1R", NULL, "DSP1CLK" }, + { "DAC1R", NULL, "SYSDSPCLK" }, + + { "AIF1DAC1L", NULL, "AIF1DACDAT" }, + { "AIF1DAC1R", NULL, "AIF1DACDAT" }, + { "AIF1DAC2L", NULL, "AIF1DACDAT" }, + { "AIF1DAC2R", NULL, "AIF1DACDAT" }, + + /* DAC1 inputs */ + { "DAC1L", NULL, "DAC1L Mixer" }, + { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, + { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, + { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" }, + { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" }, + + { "DAC1R", NULL, "DAC1R Mixer" }, + { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, + { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, + { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" }, + { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" }, + + /* DAC2/AIF2 outputs */ + { "DAC2L", NULL, "AIF2DAC2L Mixer" }, + { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, + { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, + + { "DAC2R", NULL, "AIF2DAC2R Mixer" }, + { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, + { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, + + /* Output stages */ + { "Headphone PGA", NULL, "DAC1L" }, + { "Headphone PGA", NULL, "DAC1R" }, + + { "Headphone PGA", NULL, "DAC2L" }, + { "Headphone PGA", NULL, "DAC2R" }, + + { "Headphone PGA", NULL, "Headphone Supply" }, + { "Headphone PGA", NULL, "CLK_SYS" }, + { "Headphone PGA", NULL, "LDO2" }, + + { "HP1L", NULL, "Headphone PGA" }, + { "HP1R", NULL, "Headphone PGA" }, + + { "SPK1L Driver", "DAC1L", "DAC1L" }, + { "SPK1L Driver", "DAC1R", "DAC1R" }, + { "SPK1L Driver", "DAC2L", "DAC2L" }, + { "SPK1L Driver", "DAC2R", "DAC2R" }, + { "SPK1L Driver", NULL, "CLK_SYS" }, + + { "SPK1R Driver", "DAC1L", "DAC1L" }, + { "SPK1R Driver", "DAC1R", "DAC1R" }, + { "SPK1R Driver", "DAC2L", "DAC2L" }, + { "SPK1R Driver", "DAC2R", "DAC2R" }, + { "SPK1R Driver", NULL, "CLK_SYS" }, + + { "SPK2L Driver", "DAC1L", "DAC1L" }, + { "SPK2L Driver", "DAC1R", "DAC1R" }, + { "SPK2L Driver", "DAC2L", "DAC2L" }, + { "SPK2L Driver", "DAC2R", "DAC2R" }, + { "SPK2L Driver", NULL, "CLK_SYS" }, + + { "SPK2R Driver", "DAC1L", "DAC1L" }, + { "SPK2R Driver", "DAC1R", "DAC1R" }, + { "SPK2R Driver", "DAC2L", "DAC2L" }, + { "SPK2R Driver", "DAC2R", "DAC2R" }, + { "SPK2R Driver", NULL, "CLK_SYS" }, + + { "SPK1L", NULL, "SPK1L Driver" }, + { "SPK1R", NULL, "SPK1R Driver" }, + { "SPK2L", NULL, "SPK2L Driver" }, + { "SPK2R", NULL, "SPK2R Driver" } +}; + +static bool wm8995_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8995_SOFTWARE_RESET: + case WM8995_POWER_MANAGEMENT_1: + case WM8995_POWER_MANAGEMENT_2: + case WM8995_POWER_MANAGEMENT_3: + case WM8995_POWER_MANAGEMENT_4: + case WM8995_POWER_MANAGEMENT_5: + case WM8995_LEFT_LINE_INPUT_1_VOLUME: + case WM8995_RIGHT_LINE_INPUT_1_VOLUME: + case WM8995_LEFT_LINE_INPUT_CONTROL: + case WM8995_DAC1_LEFT_VOLUME: + case WM8995_DAC1_RIGHT_VOLUME: + case WM8995_DAC2_LEFT_VOLUME: + case WM8995_DAC2_RIGHT_VOLUME: + case WM8995_OUTPUT_VOLUME_ZC_1: + case WM8995_MICBIAS_1: + case WM8995_MICBIAS_2: + case WM8995_LDO_1: + case WM8995_LDO_2: + case WM8995_ACCESSORY_DETECT_MODE1: + case WM8995_ACCESSORY_DETECT_MODE2: + case WM8995_HEADPHONE_DETECT1: + case WM8995_HEADPHONE_DETECT2: + case WM8995_MIC_DETECT_1: + case WM8995_MIC_DETECT_2: + case WM8995_CHARGE_PUMP_1: + case WM8995_CLASS_W_1: + case WM8995_DC_SERVO_1: + case WM8995_DC_SERVO_2: + case WM8995_DC_SERVO_3: + case WM8995_DC_SERVO_5: + case WM8995_DC_SERVO_6: + case WM8995_DC_SERVO_7: + case WM8995_DC_SERVO_READBACK_0: + case WM8995_ANALOGUE_HP_1: + case WM8995_ANALOGUE_HP_2: + case WM8995_CHIP_REVISION: + case WM8995_CONTROL_INTERFACE_1: + case WM8995_CONTROL_INTERFACE_2: + case WM8995_WRITE_SEQUENCER_CTRL_1: + case WM8995_WRITE_SEQUENCER_CTRL_2: + case WM8995_AIF1_CLOCKING_1: + case WM8995_AIF1_CLOCKING_2: + case WM8995_AIF2_CLOCKING_1: + case WM8995_AIF2_CLOCKING_2: + case WM8995_CLOCKING_1: + case WM8995_CLOCKING_2: + case WM8995_AIF1_RATE: + case WM8995_AIF2_RATE: + case WM8995_RATE_STATUS: + case WM8995_FLL1_CONTROL_1: + case WM8995_FLL1_CONTROL_2: + case WM8995_FLL1_CONTROL_3: + case WM8995_FLL1_CONTROL_4: + case WM8995_FLL1_CONTROL_5: + case WM8995_FLL2_CONTROL_1: + case WM8995_FLL2_CONTROL_2: + case WM8995_FLL2_CONTROL_3: + case WM8995_FLL2_CONTROL_4: + case WM8995_FLL2_CONTROL_5: + case WM8995_AIF1_CONTROL_1: + case WM8995_AIF1_CONTROL_2: + case WM8995_AIF1_MASTER_SLAVE: + case WM8995_AIF1_BCLK: + case WM8995_AIF1ADC_LRCLK: + case WM8995_AIF1DAC_LRCLK: + case WM8995_AIF1DAC_DATA: + case WM8995_AIF1ADC_DATA: + case WM8995_AIF2_CONTROL_1: + case WM8995_AIF2_CONTROL_2: + case WM8995_AIF2_MASTER_SLAVE: + case WM8995_AIF2_BCLK: + case WM8995_AIF2ADC_LRCLK: + case WM8995_AIF2DAC_LRCLK: + case WM8995_AIF2DAC_DATA: + case WM8995_AIF2ADC_DATA: + case WM8995_AIF1_ADC1_LEFT_VOLUME: + case WM8995_AIF1_ADC1_RIGHT_VOLUME: + case WM8995_AIF1_DAC1_LEFT_VOLUME: + case WM8995_AIF1_DAC1_RIGHT_VOLUME: + case WM8995_AIF1_ADC2_LEFT_VOLUME: + case WM8995_AIF1_ADC2_RIGHT_VOLUME: + case WM8995_AIF1_DAC2_LEFT_VOLUME: + case WM8995_AIF1_DAC2_RIGHT_VOLUME: + case WM8995_AIF1_ADC1_FILTERS: + case WM8995_AIF1_ADC2_FILTERS: + case WM8995_AIF1_DAC1_FILTERS_1: + case WM8995_AIF1_DAC1_FILTERS_2: + case WM8995_AIF1_DAC2_FILTERS_1: + case WM8995_AIF1_DAC2_FILTERS_2: + case WM8995_AIF1_DRC1_1: + case WM8995_AIF1_DRC1_2: + case WM8995_AIF1_DRC1_3: + case WM8995_AIF1_DRC1_4: + case WM8995_AIF1_DRC1_5: + case WM8995_AIF1_DRC2_1: + case WM8995_AIF1_DRC2_2: + case WM8995_AIF1_DRC2_3: + case WM8995_AIF1_DRC2_4: + case WM8995_AIF1_DRC2_5: + case WM8995_AIF1_DAC1_EQ_GAINS_1: + case WM8995_AIF1_DAC1_EQ_GAINS_2: + case WM8995_AIF1_DAC1_EQ_BAND_1_A: + case WM8995_AIF1_DAC1_EQ_BAND_1_B: + case WM8995_AIF1_DAC1_EQ_BAND_1_PG: + case WM8995_AIF1_DAC1_EQ_BAND_2_A: + case WM8995_AIF1_DAC1_EQ_BAND_2_B: + case WM8995_AIF1_DAC1_EQ_BAND_2_C: + case WM8995_AIF1_DAC1_EQ_BAND_2_PG: + case WM8995_AIF1_DAC1_EQ_BAND_3_A: + case WM8995_AIF1_DAC1_EQ_BAND_3_B: + case WM8995_AIF1_DAC1_EQ_BAND_3_C: + case WM8995_AIF1_DAC1_EQ_BAND_3_PG: + case WM8995_AIF1_DAC1_EQ_BAND_4_A: + case WM8995_AIF1_DAC1_EQ_BAND_4_B: + case WM8995_AIF1_DAC1_EQ_BAND_4_C: + case WM8995_AIF1_DAC1_EQ_BAND_4_PG: + case WM8995_AIF1_DAC1_EQ_BAND_5_A: + case WM8995_AIF1_DAC1_EQ_BAND_5_B: + case WM8995_AIF1_DAC1_EQ_BAND_5_PG: + case WM8995_AIF1_DAC2_EQ_GAINS_1: + case WM8995_AIF1_DAC2_EQ_GAINS_2: + case WM8995_AIF1_DAC2_EQ_BAND_1_A: + case WM8995_AIF1_DAC2_EQ_BAND_1_B: + case WM8995_AIF1_DAC2_EQ_BAND_1_PG: + case WM8995_AIF1_DAC2_EQ_BAND_2_A: + case WM8995_AIF1_DAC2_EQ_BAND_2_B: + case WM8995_AIF1_DAC2_EQ_BAND_2_C: + case WM8995_AIF1_DAC2_EQ_BAND_2_PG: + case WM8995_AIF1_DAC2_EQ_BAND_3_A: + case WM8995_AIF1_DAC2_EQ_BAND_3_B: + case WM8995_AIF1_DAC2_EQ_BAND_3_C: + case WM8995_AIF1_DAC2_EQ_BAND_3_PG: + case WM8995_AIF1_DAC2_EQ_BAND_4_A: + case WM8995_AIF1_DAC2_EQ_BAND_4_B: + case WM8995_AIF1_DAC2_EQ_BAND_4_C: + case WM8995_AIF1_DAC2_EQ_BAND_4_PG: + case WM8995_AIF1_DAC2_EQ_BAND_5_A: + case WM8995_AIF1_DAC2_EQ_BAND_5_B: + case WM8995_AIF1_DAC2_EQ_BAND_5_PG: + case WM8995_AIF2_ADC_LEFT_VOLUME: + case WM8995_AIF2_ADC_RIGHT_VOLUME: + case WM8995_AIF2_DAC_LEFT_VOLUME: + case WM8995_AIF2_DAC_RIGHT_VOLUME: + case WM8995_AIF2_ADC_FILTERS: + case WM8995_AIF2_DAC_FILTERS_1: + case WM8995_AIF2_DAC_FILTERS_2: + case WM8995_AIF2_DRC_1: + case WM8995_AIF2_DRC_2: + case WM8995_AIF2_DRC_3: + case WM8995_AIF2_DRC_4: + case WM8995_AIF2_DRC_5: + case WM8995_AIF2_EQ_GAINS_1: + case WM8995_AIF2_EQ_GAINS_2: + case WM8995_AIF2_EQ_BAND_1_A: + case WM8995_AIF2_EQ_BAND_1_B: + case WM8995_AIF2_EQ_BAND_1_PG: + case WM8995_AIF2_EQ_BAND_2_A: + case WM8995_AIF2_EQ_BAND_2_B: + case WM8995_AIF2_EQ_BAND_2_C: + case WM8995_AIF2_EQ_BAND_2_PG: + case WM8995_AIF2_EQ_BAND_3_A: + case WM8995_AIF2_EQ_BAND_3_B: + case WM8995_AIF2_EQ_BAND_3_C: + case WM8995_AIF2_EQ_BAND_3_PG: + case WM8995_AIF2_EQ_BAND_4_A: + case WM8995_AIF2_EQ_BAND_4_B: + case WM8995_AIF2_EQ_BAND_4_C: + case WM8995_AIF2_EQ_BAND_4_PG: + case WM8995_AIF2_EQ_BAND_5_A: + case WM8995_AIF2_EQ_BAND_5_B: + case WM8995_AIF2_EQ_BAND_5_PG: + case WM8995_DAC1_MIXER_VOLUMES: + case WM8995_DAC1_LEFT_MIXER_ROUTING: + case WM8995_DAC1_RIGHT_MIXER_ROUTING: + case WM8995_DAC2_MIXER_VOLUMES: + case WM8995_DAC2_LEFT_MIXER_ROUTING: + case WM8995_DAC2_RIGHT_MIXER_ROUTING: + case WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING: + case WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING: + case WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING: + case WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING: + case WM8995_DAC_SOFTMUTE: + case WM8995_OVERSAMPLING: + case WM8995_SIDETONE: + case WM8995_GPIO_1: + case WM8995_GPIO_2: + case WM8995_GPIO_3: + case WM8995_GPIO_4: + case WM8995_GPIO_5: + case WM8995_GPIO_6: + case WM8995_GPIO_7: + case WM8995_GPIO_8: + case WM8995_GPIO_9: + case WM8995_GPIO_10: + case WM8995_GPIO_11: + case WM8995_GPIO_12: + case WM8995_GPIO_13: + case WM8995_GPIO_14: + case WM8995_PULL_CONTROL_1: + case WM8995_PULL_CONTROL_2: + case WM8995_INTERRUPT_STATUS_1: + case WM8995_INTERRUPT_STATUS_2: + case WM8995_INTERRUPT_RAW_STATUS_2: + case WM8995_INTERRUPT_STATUS_1_MASK: + case WM8995_INTERRUPT_STATUS_2_MASK: + case WM8995_INTERRUPT_CONTROL: + case WM8995_LEFT_PDM_SPEAKER_1: + case WM8995_RIGHT_PDM_SPEAKER_1: + case WM8995_PDM_SPEAKER_1_MUTE_SEQUENCE: + case WM8995_LEFT_PDM_SPEAKER_2: + case WM8995_RIGHT_PDM_SPEAKER_2: + case WM8995_PDM_SPEAKER_2_MUTE_SEQUENCE: + return true; + default: + return false; + } +} + +static bool wm8995_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8995_SOFTWARE_RESET: + case WM8995_DC_SERVO_READBACK_0: + case WM8995_INTERRUPT_STATUS_1: + case WM8995_INTERRUPT_STATUS_2: + case WM8995_INTERRUPT_CONTROL: + case WM8995_ACCESSORY_DETECT_MODE1: + case WM8995_ACCESSORY_DETECT_MODE2: + case WM8995_HEADPHONE_DETECT1: + case WM8995_HEADPHONE_DETECT2: + case WM8995_RATE_STATUS: + return true; + default: + return false; + } +} + +static int wm8995_aif_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + int mute_reg; + + switch (dai->id) { + case 0: + mute_reg = WM8995_AIF1_DAC1_FILTERS_1; + break; + case 1: + mute_reg = WM8995_AIF2_DAC_FILTERS_1; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, mute_reg, WM8995_AIF1DAC1_MUTE_MASK, + !!mute << WM8995_AIF1DAC1_MUTE_SHIFT); + return 0; +} + +static int wm8995_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component; + int master; + int aif; + + component = dai->component; + + master = 0; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + master = WM8995_AIF1_MSTR; + break; + default: + dev_err(dai->dev, "Unknown master/slave configuration\n"); + return -EINVAL; + } + + aif = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif |= WM8995_AIF1_LRCLK_INV; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + aif |= (0x3 << WM8995_AIF1_FMT_SHIFT); + break; + case SND_SOC_DAIFMT_I2S: + aif |= (0x2 << WM8995_AIF1_FMT_SHIFT); + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif |= (0x1 << WM8995_AIF1_FMT_SHIFT); + break; + default: + dev_err(dai->dev, "Unknown dai format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8995_AIF1_BCLK_INV; + break; + default: + return -EINVAL; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif |= WM8995_AIF1_BCLK_INV | WM8995_AIF1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8995_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif |= WM8995_AIF1_LRCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, WM8995_AIF1_CONTROL_1, + WM8995_AIF1_BCLK_INV_MASK | + WM8995_AIF1_LRCLK_INV_MASK | + WM8995_AIF1_FMT_MASK, aif); + snd_soc_component_update_bits(component, WM8995_AIF1_MASTER_SLAVE, + WM8995_AIF1_MSTR_MASK, master); + return 0; +} + +static const int srs[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, + 48000, 88200, 96000 +}; + +static const int fs_ratios[] = { + -1 /* reserved */, + 128, 192, 256, 384, 512, 768, 1024, 1408, 1536 +}; + +static const int bclk_divs[] = { + 10, 15, 20, 30, 40, 55, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480 +}; + +static int wm8995_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component; + struct wm8995_priv *wm8995; + int aif1_reg; + int bclk_reg; + int lrclk_reg; + int rate_reg; + int bclk_rate; + int aif1; + int lrclk, bclk; + int i, rate_val, best, best_val, cur_val; + + component = dai->component; + wm8995 = snd_soc_component_get_drvdata(component); + + switch (dai->id) { + case 0: + aif1_reg = WM8995_AIF1_CONTROL_1; + bclk_reg = WM8995_AIF1_BCLK; + rate_reg = WM8995_AIF1_RATE; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK /* || + wm8995->lrclk_shared[0] */) { + lrclk_reg = WM8995_AIF1DAC_LRCLK; + } else { + lrclk_reg = WM8995_AIF1ADC_LRCLK; + dev_dbg(component->dev, "AIF1 using split LRCLK\n"); + } + break; + case 1: + aif1_reg = WM8995_AIF2_CONTROL_1; + bclk_reg = WM8995_AIF2_BCLK; + rate_reg = WM8995_AIF2_RATE; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK /* || + wm8995->lrclk_shared[1] */) { + lrclk_reg = WM8995_AIF2DAC_LRCLK; + } else { + lrclk_reg = WM8995_AIF2ADC_LRCLK; + dev_dbg(component->dev, "AIF2 using split LRCLK\n"); + } + break; + default: + return -EINVAL; + } + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) + return bclk_rate; + + aif1 = 0; + switch (params_width(params)) { + case 16: + break; + case 20: + aif1 |= (0x1 << WM8995_AIF1_WL_SHIFT); + break; + case 24: + aif1 |= (0x2 << WM8995_AIF1_WL_SHIFT); + break; + case 32: + aif1 |= (0x3 << WM8995_AIF1_WL_SHIFT); + break; + default: + dev_err(dai->dev, "Unsupported word length %u\n", + params_width(params)); + return -EINVAL; + } + + /* try to find a suitable sample rate */ + for (i = 0; i < ARRAY_SIZE(srs); ++i) + if (srs[i] == params_rate(params)) + break; + if (i == ARRAY_SIZE(srs)) { + dev_err(dai->dev, "Sample rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + rate_val = i << WM8995_AIF1_SR_SHIFT; + + dev_dbg(dai->dev, "Sample rate is %dHz\n", srs[i]); + dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n", + dai->id + 1, wm8995->aifclk[dai->id], bclk_rate); + + /* AIFCLK/fs ratio; look for a close match in either direction */ + best = 1; + best_val = abs((fs_ratios[1] * params_rate(params)) + - wm8995->aifclk[dai->id]); + for (i = 2; i < ARRAY_SIZE(fs_ratios); i++) { + cur_val = abs((fs_ratios[i] * params_rate(params)) + - wm8995->aifclk[dai->id]); + if (cur_val >= best_val) + continue; + best = i; + best_val = cur_val; + } + rate_val |= best; + + dev_dbg(dai->dev, "Selected AIF%dCLK/fs = %d\n", + dai->id + 1, fs_ratios[best]); + + /* + * We may not get quite the right frequency if using + * approximate clocks so look for the closest match that is + * higher than the target (we need to ensure that there enough + * BCLKs to clock out the samples). + */ + best = 0; + bclk = 0; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = (wm8995->aifclk[dai->id] * 10 / bclk_divs[i]) - bclk_rate; + if (cur_val < 0) /* BCLK table is sorted */ + break; + best = i; + } + bclk |= best << WM8995_AIF1_BCLK_DIV_SHIFT; + + bclk_rate = wm8995->aifclk[dai->id] * 10 / bclk_divs[best]; + dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n", + bclk_divs[best], bclk_rate); + + lrclk = bclk_rate / params_rate(params); + dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n", + lrclk, bclk_rate / lrclk); + + snd_soc_component_update_bits(component, aif1_reg, + WM8995_AIF1_WL_MASK, aif1); + snd_soc_component_update_bits(component, bclk_reg, + WM8995_AIF1_BCLK_DIV_MASK, bclk); + snd_soc_component_update_bits(component, lrclk_reg, + WM8995_AIF1DAC_RATE_MASK, lrclk); + snd_soc_component_update_bits(component, rate_reg, + WM8995_AIF1_SR_MASK | + WM8995_AIF1CLK_RATE_MASK, rate_val); + return 0; +} + +static int wm8995_set_tristate(struct snd_soc_dai *codec_dai, int tristate) +{ + struct snd_soc_component *component = codec_dai->component; + int reg, val, mask; + + switch (codec_dai->id) { + case 0: + reg = WM8995_AIF1_MASTER_SLAVE; + mask = WM8995_AIF1_TRI; + break; + case 1: + reg = WM8995_AIF2_MASTER_SLAVE; + mask = WM8995_AIF2_TRI; + break; + case 2: + reg = WM8995_POWER_MANAGEMENT_5; + mask = WM8995_AIF3_TRI; + break; + default: + return -EINVAL; + } + + if (tristate) + val = mask; + else + val = 0; + + return snd_soc_component_update_bits(component, reg, mask, val); +} + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +struct fll_div { + u16 outdiv; + u16 n; + u16 k; + u16 clk_ref_div; + u16 fll_fratio; +}; + +static int wm8995_get_fll_config(struct fll_div *fll, + int freq_in, int freq_out) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod; + + pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out); + + /* Scale the input frequency down to <= 13.5MHz */ + fll->clk_ref_div = 0; + while (freq_in > 13500000) { + fll->clk_ref_div++; + freq_in /= 2; + + if (fll->clk_ref_div > 3) + return -EINVAL; + } + pr_debug("CLK_REF_DIV=%d, Fref=%dHz\n", fll->clk_ref_div, freq_in); + + /* Scale the output to give 90MHz<=Fvco<=100MHz */ + fll->outdiv = 3; + while (freq_out * (fll->outdiv + 1) < 90000000) { + fll->outdiv++; + if (fll->outdiv > 63) + return -EINVAL; + } + freq_out *= fll->outdiv + 1; + pr_debug("OUTDIV=%d, Fvco=%dHz\n", fll->outdiv, freq_out); + + if (freq_in > 1000000) { + fll->fll_fratio = 0; + } else if (freq_in > 256000) { + fll->fll_fratio = 1; + freq_in *= 2; + } else if (freq_in > 128000) { + fll->fll_fratio = 2; + freq_in *= 4; + } else if (freq_in > 64000) { + fll->fll_fratio = 3; + freq_in *= 8; + } else { + fll->fll_fratio = 4; + freq_in *= 16; + } + pr_debug("FLL_FRATIO=%d, Fref=%dHz\n", fll->fll_fratio, freq_in); + + /* Now, calculate N.K */ + Ndiv = freq_out / freq_in; + + fll->n = Ndiv; + Nmod = freq_out % freq_in; + pr_debug("Nmod=%d\n", Nmod); + + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, freq_in); + + K = Kpart & 0xFFFFFFFF; + + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + fll->k = K / 10; + + pr_debug("N=%x K=%x\n", fll->n, fll->k); + + return 0; +} + +static int wm8995_set_fll(struct snd_soc_dai *dai, int id, + int src, unsigned int freq_in, + unsigned int freq_out) +{ + struct snd_soc_component *component; + struct wm8995_priv *wm8995; + int reg_offset, ret; + struct fll_div fll; + u16 reg, aif1, aif2; + + component = dai->component; + wm8995 = snd_soc_component_get_drvdata(component); + + aif1 = snd_soc_component_read(component, WM8995_AIF1_CLOCKING_1) + & WM8995_AIF1CLK_ENA; + + aif2 = snd_soc_component_read(component, WM8995_AIF2_CLOCKING_1) + & WM8995_AIF2CLK_ENA; + + switch (id) { + case WM8995_FLL1: + reg_offset = 0; + id = 0; + break; + case WM8995_FLL2: + reg_offset = 0x20; + id = 1; + break; + default: + return -EINVAL; + } + + switch (src) { + case 0: + /* Allow no source specification when stopping */ + if (freq_out) + return -EINVAL; + break; + case WM8995_FLL_SRC_MCLK1: + case WM8995_FLL_SRC_MCLK2: + case WM8995_FLL_SRC_LRCLK: + case WM8995_FLL_SRC_BCLK: + break; + default: + return -EINVAL; + } + + /* Are we changing anything? */ + if (wm8995->fll[id].src == src && + wm8995->fll[id].in == freq_in && wm8995->fll[id].out == freq_out) + return 0; + + /* If we're stopping the FLL redo the old config - no + * registers will actually be written but we avoid GCC flow + * analysis bugs spewing warnings. + */ + if (freq_out) + ret = wm8995_get_fll_config(&fll, freq_in, freq_out); + else + ret = wm8995_get_fll_config(&fll, wm8995->fll[id].in, + wm8995->fll[id].out); + if (ret < 0) + return ret; + + /* Gate the AIF clocks while we reclock */ + snd_soc_component_update_bits(component, WM8995_AIF1_CLOCKING_1, + WM8995_AIF1CLK_ENA_MASK, 0); + snd_soc_component_update_bits(component, WM8995_AIF2_CLOCKING_1, + WM8995_AIF2CLK_ENA_MASK, 0); + + /* We always need to disable the FLL while reconfiguring */ + snd_soc_component_update_bits(component, WM8995_FLL1_CONTROL_1 + reg_offset, + WM8995_FLL1_ENA_MASK, 0); + + reg = (fll.outdiv << WM8995_FLL1_OUTDIV_SHIFT) | + (fll.fll_fratio << WM8995_FLL1_FRATIO_SHIFT); + snd_soc_component_update_bits(component, WM8995_FLL1_CONTROL_2 + reg_offset, + WM8995_FLL1_OUTDIV_MASK | + WM8995_FLL1_FRATIO_MASK, reg); + + snd_soc_component_write(component, WM8995_FLL1_CONTROL_3 + reg_offset, fll.k); + + snd_soc_component_update_bits(component, WM8995_FLL1_CONTROL_4 + reg_offset, + WM8995_FLL1_N_MASK, + fll.n << WM8995_FLL1_N_SHIFT); + + snd_soc_component_update_bits(component, WM8995_FLL1_CONTROL_5 + reg_offset, + WM8995_FLL1_REFCLK_DIV_MASK | + WM8995_FLL1_REFCLK_SRC_MASK, + (fll.clk_ref_div << WM8995_FLL1_REFCLK_DIV_SHIFT) | + (src - 1)); + + if (freq_out) + snd_soc_component_update_bits(component, WM8995_FLL1_CONTROL_1 + reg_offset, + WM8995_FLL1_ENA_MASK, WM8995_FLL1_ENA); + + wm8995->fll[id].in = freq_in; + wm8995->fll[id].out = freq_out; + wm8995->fll[id].src = src; + + /* Enable any gated AIF clocks */ + snd_soc_component_update_bits(component, WM8995_AIF1_CLOCKING_1, + WM8995_AIF1CLK_ENA_MASK, aif1); + snd_soc_component_update_bits(component, WM8995_AIF2_CLOCKING_1, + WM8995_AIF2CLK_ENA_MASK, aif2); + + configure_clock(component); + + return 0; +} + +static int wm8995_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component; + struct wm8995_priv *wm8995; + + component = dai->component; + wm8995 = snd_soc_component_get_drvdata(component); + + switch (dai->id) { + case 0: + case 1: + break; + default: + /* AIF3 shares clocking with AIF1/2 */ + return -EINVAL; + } + + switch (clk_id) { + case WM8995_SYSCLK_MCLK1: + wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK1; + wm8995->mclk[0] = freq; + dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n", + dai->id + 1, freq); + break; + case WM8995_SYSCLK_MCLK2: + wm8995->sysclk[dai->id] = WM8995_SYSCLK_MCLK2; + wm8995->mclk[1] = freq; + dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n", + dai->id + 1, freq); + break; + case WM8995_SYSCLK_FLL1: + wm8995->sysclk[dai->id] = WM8995_SYSCLK_FLL1; + dev_dbg(dai->dev, "AIF%d using FLL1\n", dai->id + 1); + break; + case WM8995_SYSCLK_FLL2: + wm8995->sysclk[dai->id] = WM8995_SYSCLK_FLL2; + dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id + 1); + break; + case WM8995_SYSCLK_OPCLK: + default: + dev_err(dai->dev, "Unknown clock source %d\n", clk_id); + return -EINVAL; + } + + configure_clock(component); + + return 0; +} + +static int wm8995_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8995_priv *wm8995; + int ret; + + wm8995 = snd_soc_component_get_drvdata(component); + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + if (ret) + return ret; + + ret = regcache_sync(wm8995->regmap); + if (ret) { + dev_err(component->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + + snd_soc_component_update_bits(component, WM8995_POWER_MANAGEMENT_1, + WM8995_BG_ENA_MASK, WM8995_BG_ENA); + } + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, WM8995_POWER_MANAGEMENT_1, + WM8995_BG_ENA_MASK, 0); + regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + break; + } + + return 0; +} + +static int wm8995_probe(struct snd_soc_component *component) +{ + struct wm8995_priv *wm8995; + int i; + int ret; + + wm8995 = snd_soc_component_get_drvdata(component); + wm8995->component = component; + + for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) + wm8995->supplies[i].supply = wm8995_supply_names[i]; + + ret = devm_regulator_bulk_get(component->dev, + ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + if (ret) { + dev_err(component->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0; + wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1; + wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2; + wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3; + wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4; + wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5; + wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6; + wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7; + + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) { + ret = devm_regulator_register_notifier( + wm8995->supplies[i].consumer, + &wm8995->disable_nb[i]); + if (ret) { + dev_err(component->dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + if (ret) { + dev_err(component->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + + ret = snd_soc_component_read(component, WM8995_SOFTWARE_RESET); + if (ret < 0) { + dev_err(component->dev, "Failed to read device ID: %d\n", ret); + goto err_reg_enable; + } + + if (ret != 0x8995) { + dev_err(component->dev, "Invalid device ID: %#x\n", ret); + ret = -EINVAL; + goto err_reg_enable; + } + + ret = snd_soc_component_write(component, WM8995_SOFTWARE_RESET, 0); + if (ret < 0) { + dev_err(component->dev, "Failed to issue reset: %d\n", ret); + goto err_reg_enable; + } + + /* Latch volume updates (right only; we always do left then right). */ + snd_soc_component_update_bits(component, WM8995_AIF1_DAC1_RIGHT_VOLUME, + WM8995_AIF1DAC1_VU_MASK, WM8995_AIF1DAC1_VU); + snd_soc_component_update_bits(component, WM8995_AIF1_DAC2_RIGHT_VOLUME, + WM8995_AIF1DAC2_VU_MASK, WM8995_AIF1DAC2_VU); + snd_soc_component_update_bits(component, WM8995_AIF2_DAC_RIGHT_VOLUME, + WM8995_AIF2DAC_VU_MASK, WM8995_AIF2DAC_VU); + snd_soc_component_update_bits(component, WM8995_AIF1_ADC1_RIGHT_VOLUME, + WM8995_AIF1ADC1_VU_MASK, WM8995_AIF1ADC1_VU); + snd_soc_component_update_bits(component, WM8995_AIF1_ADC2_RIGHT_VOLUME, + WM8995_AIF1ADC2_VU_MASK, WM8995_AIF1ADC2_VU); + snd_soc_component_update_bits(component, WM8995_AIF2_ADC_RIGHT_VOLUME, + WM8995_AIF2ADC_VU_MASK, WM8995_AIF1ADC2_VU); + snd_soc_component_update_bits(component, WM8995_DAC1_RIGHT_VOLUME, + WM8995_DAC1_VU_MASK, WM8995_DAC1_VU); + snd_soc_component_update_bits(component, WM8995_DAC2_RIGHT_VOLUME, + WM8995_DAC2_VU_MASK, WM8995_DAC2_VU); + snd_soc_component_update_bits(component, WM8995_RIGHT_LINE_INPUT_1_VOLUME, + WM8995_IN1_VU_MASK, WM8995_IN1_VU); + + wm8995_update_class_w(component); + + return 0; + +err_reg_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); + return ret; +} + +#define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8995_aif1_dai_ops = { + .set_sysclk = wm8995_set_dai_sysclk, + .set_fmt = wm8995_set_dai_fmt, + .hw_params = wm8995_hw_params, + .mute_stream = wm8995_aif_mute, + .set_pll = wm8995_set_fll, + .set_tristate = wm8995_set_tristate, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops wm8995_aif2_dai_ops = { + .set_sysclk = wm8995_set_dai_sysclk, + .set_fmt = wm8995_set_dai_fmt, + .hw_params = wm8995_hw_params, + .mute_stream = wm8995_aif_mute, + .set_pll = wm8995_set_fll, + .set_tristate = wm8995_set_tristate, + .no_capture_mute = 1, +}; + +static const struct snd_soc_dai_ops wm8995_aif3_dai_ops = { + .set_tristate = wm8995_set_tristate, +}; + +static struct snd_soc_dai_driver wm8995_dai[] = { + { + .name = "wm8995-aif1", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8995_FORMATS + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8995_FORMATS + }, + .ops = &wm8995_aif1_dai_ops + }, + { + .name = "wm8995-aif2", + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8995_FORMATS + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8995_FORMATS + }, + .ops = &wm8995_aif2_dai_ops + }, + { + .name = "wm8995-aif3", + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8995_FORMATS + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = WM8995_FORMATS + }, + .ops = &wm8995_aif3_dai_ops + } +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8995 = { + .probe = wm8995_probe, + .set_bias_level = wm8995_set_bias_level, + .controls = wm8995_snd_controls, + .num_controls = ARRAY_SIZE(wm8995_snd_controls), + .dapm_widgets = wm8995_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8995_dapm_widgets), + .dapm_routes = wm8995_intercon, + .num_dapm_routes = ARRAY_SIZE(wm8995_intercon), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm8995_regmap = { + .reg_bits = 16, + .val_bits = 16, + + .max_register = WM8995_MAX_REGISTER, + .reg_defaults = wm8995_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8995_reg_defaults), + .volatile_reg = wm8995_volatile, + .readable_reg = wm8995_readable, + .cache_type = REGCACHE_MAPLE, +}; + +#if defined(CONFIG_SPI_MASTER) +static int wm8995_spi_probe(struct spi_device *spi) +{ + struct wm8995_priv *wm8995; + int ret; + + wm8995 = devm_kzalloc(&spi->dev, sizeof(*wm8995), GFP_KERNEL); + if (!wm8995) + return -ENOMEM; + + spi_set_drvdata(spi, wm8995); + + wm8995->regmap = devm_regmap_init_spi(spi, &wm8995_regmap); + if (IS_ERR(wm8995->regmap)) { + ret = PTR_ERR(wm8995->regmap); + dev_err(&spi->dev, "Failed to register regmap: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(&spi->dev, + &soc_component_dev_wm8995, wm8995_dai, + ARRAY_SIZE(wm8995_dai)); + return ret; +} + +static struct spi_driver wm8995_spi_driver = { + .driver = { + .name = "wm8995", + }, + .probe = wm8995_spi_probe, +}; +#endif + +#if IS_ENABLED(CONFIG_I2C) +static int wm8995_i2c_probe(struct i2c_client *i2c) +{ + struct wm8995_priv *wm8995; + int ret; + + wm8995 = devm_kzalloc(&i2c->dev, sizeof(*wm8995), GFP_KERNEL); + if (!wm8995) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm8995); + + wm8995->regmap = devm_regmap_init_i2c(i2c, &wm8995_regmap); + if (IS_ERR(wm8995->regmap)) { + ret = PTR_ERR(wm8995->regmap); + dev_err(&i2c->dev, "Failed to register regmap: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8995, wm8995_dai, + ARRAY_SIZE(wm8995_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id wm8995_i2c_id[] = { + {"wm8995", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, wm8995_i2c_id); + +static struct i2c_driver wm8995_i2c_driver = { + .driver = { + .name = "wm8995", + }, + .probe = wm8995_i2c_probe, + .id_table = wm8995_i2c_id +}; +#endif + +static int __init wm8995_modinit(void) +{ + int ret = 0; + +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8995_i2c_driver); + if (ret) { + printk(KERN_ERR "Failed to register wm8995 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8995_spi_driver); + if (ret) { + printk(KERN_ERR "Failed to register wm8995 SPI driver: %d\n", + ret); + } +#endif + return ret; +} + +module_init(wm8995_modinit); + +static void __exit wm8995_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8995_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8995_spi_driver); +#endif +} + +module_exit(wm8995_exit); + +MODULE_DESCRIPTION("ASoC WM8995 driver"); +MODULE_AUTHOR("Dimitris Papastamos "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8995.h b/sound/soc/codecs/wm8995.h new file mode 100644 index 0000000000..5a3cc8aec2 --- /dev/null +++ b/sound/soc/codecs/wm8995.h @@ -0,0 +1,4263 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8995.h -- WM8995 ALSA SoC Audio driver + * + * Copyright 2010 Wolfson Microelectronics plc + * + * Author: Dimitris Papastamos + */ + +#ifndef _WM8995_H +#define _WM8995_H + +#include + +/* + * Register values. + */ +#define WM8995_SOFTWARE_RESET 0x00 +#define WM8995_POWER_MANAGEMENT_1 0x01 +#define WM8995_POWER_MANAGEMENT_2 0x02 +#define WM8995_POWER_MANAGEMENT_3 0x03 +#define WM8995_POWER_MANAGEMENT_4 0x04 +#define WM8995_POWER_MANAGEMENT_5 0x05 +#define WM8995_LEFT_LINE_INPUT_1_VOLUME 0x10 +#define WM8995_RIGHT_LINE_INPUT_1_VOLUME 0x11 +#define WM8995_LEFT_LINE_INPUT_CONTROL 0x12 +#define WM8995_DAC1_LEFT_VOLUME 0x18 +#define WM8995_DAC1_RIGHT_VOLUME 0x19 +#define WM8995_DAC2_LEFT_VOLUME 0x1A +#define WM8995_DAC2_RIGHT_VOLUME 0x1B +#define WM8995_OUTPUT_VOLUME_ZC_1 0x1C +#define WM8995_MICBIAS_1 0x20 +#define WM8995_MICBIAS_2 0x21 +#define WM8995_LDO_1 0x28 +#define WM8995_LDO_2 0x29 +#define WM8995_ACCESSORY_DETECT_MODE1 0x30 +#define WM8995_ACCESSORY_DETECT_MODE2 0x31 +#define WM8995_HEADPHONE_DETECT1 0x34 +#define WM8995_HEADPHONE_DETECT2 0x35 +#define WM8995_MIC_DETECT_1 0x38 +#define WM8995_MIC_DETECT_2 0x39 +#define WM8995_CHARGE_PUMP_1 0x40 +#define WM8995_CLASS_W_1 0x45 +#define WM8995_DC_SERVO_1 0x50 +#define WM8995_DC_SERVO_2 0x51 +#define WM8995_DC_SERVO_3 0x52 +#define WM8995_DC_SERVO_5 0x54 +#define WM8995_DC_SERVO_6 0x55 +#define WM8995_DC_SERVO_7 0x56 +#define WM8995_DC_SERVO_READBACK_0 0x57 +#define WM8995_ANALOGUE_HP_1 0x60 +#define WM8995_ANALOGUE_HP_2 0x61 +#define WM8995_CHIP_REVISION 0x100 +#define WM8995_CONTROL_INTERFACE_1 0x101 +#define WM8995_CONTROL_INTERFACE_2 0x102 +#define WM8995_WRITE_SEQUENCER_CTRL_1 0x110 +#define WM8995_WRITE_SEQUENCER_CTRL_2 0x111 +#define WM8995_AIF1_CLOCKING_1 0x200 +#define WM8995_AIF1_CLOCKING_2 0x201 +#define WM8995_AIF2_CLOCKING_1 0x204 +#define WM8995_AIF2_CLOCKING_2 0x205 +#define WM8995_CLOCKING_1 0x208 +#define WM8995_CLOCKING_2 0x209 +#define WM8995_AIF1_RATE 0x210 +#define WM8995_AIF2_RATE 0x211 +#define WM8995_RATE_STATUS 0x212 +#define WM8995_FLL1_CONTROL_1 0x220 +#define WM8995_FLL1_CONTROL_2 0x221 +#define WM8995_FLL1_CONTROL_3 0x222 +#define WM8995_FLL1_CONTROL_4 0x223 +#define WM8995_FLL1_CONTROL_5 0x224 +#define WM8995_FLL2_CONTROL_1 0x240 +#define WM8995_FLL2_CONTROL_2 0x241 +#define WM8995_FLL2_CONTROL_3 0x242 +#define WM8995_FLL2_CONTROL_4 0x243 +#define WM8995_FLL2_CONTROL_5 0x244 +#define WM8995_AIF1_CONTROL_1 0x300 +#define WM8995_AIF1_CONTROL_2 0x301 +#define WM8995_AIF1_MASTER_SLAVE 0x302 +#define WM8995_AIF1_BCLK 0x303 +#define WM8995_AIF1ADC_LRCLK 0x304 +#define WM8995_AIF1DAC_LRCLK 0x305 +#define WM8995_AIF1DAC_DATA 0x306 +#define WM8995_AIF1ADC_DATA 0x307 +#define WM8995_AIF2_CONTROL_1 0x310 +#define WM8995_AIF2_CONTROL_2 0x311 +#define WM8995_AIF2_MASTER_SLAVE 0x312 +#define WM8995_AIF2_BCLK 0x313 +#define WM8995_AIF2ADC_LRCLK 0x314 +#define WM8995_AIF2DAC_LRCLK 0x315 +#define WM8995_AIF2DAC_DATA 0x316 +#define WM8995_AIF2ADC_DATA 0x317 +#define WM8995_AIF1_ADC1_LEFT_VOLUME 0x400 +#define WM8995_AIF1_ADC1_RIGHT_VOLUME 0x401 +#define WM8995_AIF1_DAC1_LEFT_VOLUME 0x402 +#define WM8995_AIF1_DAC1_RIGHT_VOLUME 0x403 +#define WM8995_AIF1_ADC2_LEFT_VOLUME 0x404 +#define WM8995_AIF1_ADC2_RIGHT_VOLUME 0x405 +#define WM8995_AIF1_DAC2_LEFT_VOLUME 0x406 +#define WM8995_AIF1_DAC2_RIGHT_VOLUME 0x407 +#define WM8995_AIF1_ADC1_FILTERS 0x410 +#define WM8995_AIF1_ADC2_FILTERS 0x411 +#define WM8995_AIF1_DAC1_FILTERS_1 0x420 +#define WM8995_AIF1_DAC1_FILTERS_2 0x421 +#define WM8995_AIF1_DAC2_FILTERS_1 0x422 +#define WM8995_AIF1_DAC2_FILTERS_2 0x423 +#define WM8995_AIF1_DRC1_1 0x440 +#define WM8995_AIF1_DRC1_2 0x441 +#define WM8995_AIF1_DRC1_3 0x442 +#define WM8995_AIF1_DRC1_4 0x443 +#define WM8995_AIF1_DRC1_5 0x444 +#define WM8995_AIF1_DRC2_1 0x450 +#define WM8995_AIF1_DRC2_2 0x451 +#define WM8995_AIF1_DRC2_3 0x452 +#define WM8995_AIF1_DRC2_4 0x453 +#define WM8995_AIF1_DRC2_5 0x454 +#define WM8995_AIF1_DAC1_EQ_GAINS_1 0x480 +#define WM8995_AIF1_DAC1_EQ_GAINS_2 0x481 +#define WM8995_AIF1_DAC1_EQ_BAND_1_A 0x482 +#define WM8995_AIF1_DAC1_EQ_BAND_1_B 0x483 +#define WM8995_AIF1_DAC1_EQ_BAND_1_PG 0x484 +#define WM8995_AIF1_DAC1_EQ_BAND_2_A 0x485 +#define WM8995_AIF1_DAC1_EQ_BAND_2_B 0x486 +#define WM8995_AIF1_DAC1_EQ_BAND_2_C 0x487 +#define WM8995_AIF1_DAC1_EQ_BAND_2_PG 0x488 +#define WM8995_AIF1_DAC1_EQ_BAND_3_A 0x489 +#define WM8995_AIF1_DAC1_EQ_BAND_3_B 0x48A +#define WM8995_AIF1_DAC1_EQ_BAND_3_C 0x48B +#define WM8995_AIF1_DAC1_EQ_BAND_3_PG 0x48C +#define WM8995_AIF1_DAC1_EQ_BAND_4_A 0x48D +#define WM8995_AIF1_DAC1_EQ_BAND_4_B 0x48E +#define WM8995_AIF1_DAC1_EQ_BAND_4_C 0x48F +#define WM8995_AIF1_DAC1_EQ_BAND_4_PG 0x490 +#define WM8995_AIF1_DAC1_EQ_BAND_5_A 0x491 +#define WM8995_AIF1_DAC1_EQ_BAND_5_B 0x492 +#define WM8995_AIF1_DAC1_EQ_BAND_5_PG 0x493 +#define WM8995_AIF1_DAC2_EQ_GAINS_1 0x4A0 +#define WM8995_AIF1_DAC2_EQ_GAINS_2 0x4A1 +#define WM8995_AIF1_DAC2_EQ_BAND_1_A 0x4A2 +#define WM8995_AIF1_DAC2_EQ_BAND_1_B 0x4A3 +#define WM8995_AIF1_DAC2_EQ_BAND_1_PG 0x4A4 +#define WM8995_AIF1_DAC2_EQ_BAND_2_A 0x4A5 +#define WM8995_AIF1_DAC2_EQ_BAND_2_B 0x4A6 +#define WM8995_AIF1_DAC2_EQ_BAND_2_C 0x4A7 +#define WM8995_AIF1_DAC2_EQ_BAND_2_PG 0x4A8 +#define WM8995_AIF1_DAC2_EQ_BAND_3_A 0x4A9 +#define WM8995_AIF1_DAC2_EQ_BAND_3_B 0x4AA +#define WM8995_AIF1_DAC2_EQ_BAND_3_C 0x4AB +#define WM8995_AIF1_DAC2_EQ_BAND_3_PG 0x4AC +#define WM8995_AIF1_DAC2_EQ_BAND_4_A 0x4AD +#define WM8995_AIF1_DAC2_EQ_BAND_4_B 0x4AE +#define WM8995_AIF1_DAC2_EQ_BAND_4_C 0x4AF +#define WM8995_AIF1_DAC2_EQ_BAND_4_PG 0x4B0 +#define WM8995_AIF1_DAC2_EQ_BAND_5_A 0x4B1 +#define WM8995_AIF1_DAC2_EQ_BAND_5_B 0x4B2 +#define WM8995_AIF1_DAC2_EQ_BAND_5_PG 0x4B3 +#define WM8995_AIF2_ADC_LEFT_VOLUME 0x500 +#define WM8995_AIF2_ADC_RIGHT_VOLUME 0x501 +#define WM8995_AIF2_DAC_LEFT_VOLUME 0x502 +#define WM8995_AIF2_DAC_RIGHT_VOLUME 0x503 +#define WM8995_AIF2_ADC_FILTERS 0x510 +#define WM8995_AIF2_DAC_FILTERS_1 0x520 +#define WM8995_AIF2_DAC_FILTERS_2 0x521 +#define WM8995_AIF2_DRC_1 0x540 +#define WM8995_AIF2_DRC_2 0x541 +#define WM8995_AIF2_DRC_3 0x542 +#define WM8995_AIF2_DRC_4 0x543 +#define WM8995_AIF2_DRC_5 0x544 +#define WM8995_AIF2_EQ_GAINS_1 0x580 +#define WM8995_AIF2_EQ_GAINS_2 0x581 +#define WM8995_AIF2_EQ_BAND_1_A 0x582 +#define WM8995_AIF2_EQ_BAND_1_B 0x583 +#define WM8995_AIF2_EQ_BAND_1_PG 0x584 +#define WM8995_AIF2_EQ_BAND_2_A 0x585 +#define WM8995_AIF2_EQ_BAND_2_B 0x586 +#define WM8995_AIF2_EQ_BAND_2_C 0x587 +#define WM8995_AIF2_EQ_BAND_2_PG 0x588 +#define WM8995_AIF2_EQ_BAND_3_A 0x589 +#define WM8995_AIF2_EQ_BAND_3_B 0x58A +#define WM8995_AIF2_EQ_BAND_3_C 0x58B +#define WM8995_AIF2_EQ_BAND_3_PG 0x58C +#define WM8995_AIF2_EQ_BAND_4_A 0x58D +#define WM8995_AIF2_EQ_BAND_4_B 0x58E +#define WM8995_AIF2_EQ_BAND_4_C 0x58F +#define WM8995_AIF2_EQ_BAND_4_PG 0x590 +#define WM8995_AIF2_EQ_BAND_5_A 0x591 +#define WM8995_AIF2_EQ_BAND_5_B 0x592 +#define WM8995_AIF2_EQ_BAND_5_PG 0x593 +#define WM8995_DAC1_MIXER_VOLUMES 0x600 +#define WM8995_DAC1_LEFT_MIXER_ROUTING 0x601 +#define WM8995_DAC1_RIGHT_MIXER_ROUTING 0x602 +#define WM8995_DAC2_MIXER_VOLUMES 0x603 +#define WM8995_DAC2_LEFT_MIXER_ROUTING 0x604 +#define WM8995_DAC2_RIGHT_MIXER_ROUTING 0x605 +#define WM8995_AIF1_ADC1_LEFT_MIXER_ROUTING 0x606 +#define WM8995_AIF1_ADC1_RIGHT_MIXER_ROUTING 0x607 +#define WM8995_AIF1_ADC2_LEFT_MIXER_ROUTING 0x608 +#define WM8995_AIF1_ADC2_RIGHT_MIXER_ROUTING 0x609 +#define WM8995_DAC_SOFTMUTE 0x610 +#define WM8995_OVERSAMPLING 0x620 +#define WM8995_SIDETONE 0x621 +#define WM8995_GPIO_1 0x700 +#define WM8995_GPIO_2 0x701 +#define WM8995_GPIO_3 0x702 +#define WM8995_GPIO_4 0x703 +#define WM8995_GPIO_5 0x704 +#define WM8995_GPIO_6 0x705 +#define WM8995_GPIO_7 0x706 +#define WM8995_GPIO_8 0x707 +#define WM8995_GPIO_9 0x708 +#define WM8995_GPIO_10 0x709 +#define WM8995_GPIO_11 0x70A +#define WM8995_GPIO_12 0x70B +#define WM8995_GPIO_13 0x70C +#define WM8995_GPIO_14 0x70D +#define WM8995_PULL_CONTROL_1 0x720 +#define WM8995_PULL_CONTROL_2 0x721 +#define WM8995_INTERRUPT_STATUS_1 0x730 +#define WM8995_INTERRUPT_STATUS_2 0x731 +#define WM8995_INTERRUPT_RAW_STATUS_2 0x732 +#define WM8995_INTERRUPT_STATUS_1_MASK 0x738 +#define WM8995_INTERRUPT_STATUS_2_MASK 0x739 +#define WM8995_INTERRUPT_CONTROL 0x740 +#define WM8995_LEFT_PDM_SPEAKER_1 0x800 +#define WM8995_RIGHT_PDM_SPEAKER_1 0x801 +#define WM8995_PDM_SPEAKER_1_MUTE_SEQUENCE 0x802 +#define WM8995_LEFT_PDM_SPEAKER_2 0x808 +#define WM8995_RIGHT_PDM_SPEAKER_2 0x809 +#define WM8995_PDM_SPEAKER_2_MUTE_SEQUENCE 0x80A +#define WM8995_WRITE_SEQUENCER_0 0x3000 +#define WM8995_WRITE_SEQUENCER_1 0x3001 +#define WM8995_WRITE_SEQUENCER_2 0x3002 +#define WM8995_WRITE_SEQUENCER_3 0x3003 +#define WM8995_WRITE_SEQUENCER_4 0x3004 +#define WM8995_WRITE_SEQUENCER_5 0x3005 +#define WM8995_WRITE_SEQUENCER_6 0x3006 +#define WM8995_WRITE_SEQUENCER_7 0x3007 +#define WM8995_WRITE_SEQUENCER_8 0x3008 +#define WM8995_WRITE_SEQUENCER_9 0x3009 +#define WM8995_WRITE_SEQUENCER_10 0x300A +#define WM8995_WRITE_SEQUENCER_11 0x300B +#define WM8995_WRITE_SEQUENCER_12 0x300C +#define WM8995_WRITE_SEQUENCER_13 0x300D +#define WM8995_WRITE_SEQUENCER_14 0x300E +#define WM8995_WRITE_SEQUENCER_15 0x300F +#define WM8995_WRITE_SEQUENCER_16 0x3010 +#define WM8995_WRITE_SEQUENCER_17 0x3011 +#define WM8995_WRITE_SEQUENCER_18 0x3012 +#define WM8995_WRITE_SEQUENCER_19 0x3013 +#define WM8995_WRITE_SEQUENCER_20 0x3014 +#define WM8995_WRITE_SEQUENCER_21 0x3015 +#define WM8995_WRITE_SEQUENCER_22 0x3016 +#define WM8995_WRITE_SEQUENCER_23 0x3017 +#define WM8995_WRITE_SEQUENCER_24 0x3018 +#define WM8995_WRITE_SEQUENCER_25 0x3019 +#define WM8995_WRITE_SEQUENCER_26 0x301A +#define WM8995_WRITE_SEQUENCER_27 0x301B +#define WM8995_WRITE_SEQUENCER_28 0x301C +#define WM8995_WRITE_SEQUENCER_29 0x301D +#define WM8995_WRITE_SEQUENCER_30 0x301E +#define WM8995_WRITE_SEQUENCER_31 0x301F +#define WM8995_WRITE_SEQUENCER_32 0x3020 +#define WM8995_WRITE_SEQUENCER_33 0x3021 +#define WM8995_WRITE_SEQUENCER_34 0x3022 +#define WM8995_WRITE_SEQUENCER_35 0x3023 +#define WM8995_WRITE_SEQUENCER_36 0x3024 +#define WM8995_WRITE_SEQUENCER_37 0x3025 +#define WM8995_WRITE_SEQUENCER_38 0x3026 +#define WM8995_WRITE_SEQUENCER_39 0x3027 +#define WM8995_WRITE_SEQUENCER_40 0x3028 +#define WM8995_WRITE_SEQUENCER_41 0x3029 +#define WM8995_WRITE_SEQUENCER_42 0x302A +#define WM8995_WRITE_SEQUENCER_43 0x302B +#define WM8995_WRITE_SEQUENCER_44 0x302C +#define WM8995_WRITE_SEQUENCER_45 0x302D +#define WM8995_WRITE_SEQUENCER_46 0x302E +#define WM8995_WRITE_SEQUENCER_47 0x302F +#define WM8995_WRITE_SEQUENCER_48 0x3030 +#define WM8995_WRITE_SEQUENCER_49 0x3031 +#define WM8995_WRITE_SEQUENCER_50 0x3032 +#define WM8995_WRITE_SEQUENCER_51 0x3033 +#define WM8995_WRITE_SEQUENCER_52 0x3034 +#define WM8995_WRITE_SEQUENCER_53 0x3035 +#define WM8995_WRITE_SEQUENCER_54 0x3036 +#define WM8995_WRITE_SEQUENCER_55 0x3037 +#define WM8995_WRITE_SEQUENCER_56 0x3038 +#define WM8995_WRITE_SEQUENCER_57 0x3039 +#define WM8995_WRITE_SEQUENCER_58 0x303A +#define WM8995_WRITE_SEQUENCER_59 0x303B +#define WM8995_WRITE_SEQUENCER_60 0x303C +#define WM8995_WRITE_SEQUENCER_61 0x303D +#define WM8995_WRITE_SEQUENCER_62 0x303E +#define WM8995_WRITE_SEQUENCER_63 0x303F +#define WM8995_WRITE_SEQUENCER_64 0x3040 +#define WM8995_WRITE_SEQUENCER_65 0x3041 +#define WM8995_WRITE_SEQUENCER_66 0x3042 +#define WM8995_WRITE_SEQUENCER_67 0x3043 +#define WM8995_WRITE_SEQUENCER_68 0x3044 +#define WM8995_WRITE_SEQUENCER_69 0x3045 +#define WM8995_WRITE_SEQUENCER_70 0x3046 +#define WM8995_WRITE_SEQUENCER_71 0x3047 +#define WM8995_WRITE_SEQUENCER_72 0x3048 +#define WM8995_WRITE_SEQUENCER_73 0x3049 +#define WM8995_WRITE_SEQUENCER_74 0x304A +#define WM8995_WRITE_SEQUENCER_75 0x304B +#define WM8995_WRITE_SEQUENCER_76 0x304C +#define WM8995_WRITE_SEQUENCER_77 0x304D +#define WM8995_WRITE_SEQUENCER_78 0x304E +#define WM8995_WRITE_SEQUENCER_79 0x304F +#define WM8995_WRITE_SEQUENCER_80 0x3050 +#define WM8995_WRITE_SEQUENCER_81 0x3051 +#define WM8995_WRITE_SEQUENCER_82 0x3052 +#define WM8995_WRITE_SEQUENCER_83 0x3053 +#define WM8995_WRITE_SEQUENCER_84 0x3054 +#define WM8995_WRITE_SEQUENCER_85 0x3055 +#define WM8995_WRITE_SEQUENCER_86 0x3056 +#define WM8995_WRITE_SEQUENCER_87 0x3057 +#define WM8995_WRITE_SEQUENCER_88 0x3058 +#define WM8995_WRITE_SEQUENCER_89 0x3059 +#define WM8995_WRITE_SEQUENCER_90 0x305A +#define WM8995_WRITE_SEQUENCER_91 0x305B +#define WM8995_WRITE_SEQUENCER_92 0x305C +#define WM8995_WRITE_SEQUENCER_93 0x305D +#define WM8995_WRITE_SEQUENCER_94 0x305E +#define WM8995_WRITE_SEQUENCER_95 0x305F +#define WM8995_WRITE_SEQUENCER_96 0x3060 +#define WM8995_WRITE_SEQUENCER_97 0x3061 +#define WM8995_WRITE_SEQUENCER_98 0x3062 +#define WM8995_WRITE_SEQUENCER_99 0x3063 +#define WM8995_WRITE_SEQUENCER_100 0x3064 +#define WM8995_WRITE_SEQUENCER_101 0x3065 +#define WM8995_WRITE_SEQUENCER_102 0x3066 +#define WM8995_WRITE_SEQUENCER_103 0x3067 +#define WM8995_WRITE_SEQUENCER_104 0x3068 +#define WM8995_WRITE_SEQUENCER_105 0x3069 +#define WM8995_WRITE_SEQUENCER_106 0x306A +#define WM8995_WRITE_SEQUENCER_107 0x306B +#define WM8995_WRITE_SEQUENCER_108 0x306C +#define WM8995_WRITE_SEQUENCER_109 0x306D +#define WM8995_WRITE_SEQUENCER_110 0x306E +#define WM8995_WRITE_SEQUENCER_111 0x306F +#define WM8995_WRITE_SEQUENCER_112 0x3070 +#define WM8995_WRITE_SEQUENCER_113 0x3071 +#define WM8995_WRITE_SEQUENCER_114 0x3072 +#define WM8995_WRITE_SEQUENCER_115 0x3073 +#define WM8995_WRITE_SEQUENCER_116 0x3074 +#define WM8995_WRITE_SEQUENCER_117 0x3075 +#define WM8995_WRITE_SEQUENCER_118 0x3076 +#define WM8995_WRITE_SEQUENCER_119 0x3077 +#define WM8995_WRITE_SEQUENCER_120 0x3078 +#define WM8995_WRITE_SEQUENCER_121 0x3079 +#define WM8995_WRITE_SEQUENCER_122 0x307A +#define WM8995_WRITE_SEQUENCER_123 0x307B +#define WM8995_WRITE_SEQUENCER_124 0x307C +#define WM8995_WRITE_SEQUENCER_125 0x307D +#define WM8995_WRITE_SEQUENCER_126 0x307E +#define WM8995_WRITE_SEQUENCER_127 0x307F +#define WM8995_WRITE_SEQUENCER_128 0x3080 +#define WM8995_WRITE_SEQUENCER_129 0x3081 +#define WM8995_WRITE_SEQUENCER_130 0x3082 +#define WM8995_WRITE_SEQUENCER_131 0x3083 +#define WM8995_WRITE_SEQUENCER_132 0x3084 +#define WM8995_WRITE_SEQUENCER_133 0x3085 +#define WM8995_WRITE_SEQUENCER_134 0x3086 +#define WM8995_WRITE_SEQUENCER_135 0x3087 +#define WM8995_WRITE_SEQUENCER_136 0x3088 +#define WM8995_WRITE_SEQUENCER_137 0x3089 +#define WM8995_WRITE_SEQUENCER_138 0x308A +#define WM8995_WRITE_SEQUENCER_139 0x308B +#define WM8995_WRITE_SEQUENCER_140 0x308C +#define WM8995_WRITE_SEQUENCER_141 0x308D +#define WM8995_WRITE_SEQUENCER_142 0x308E +#define WM8995_WRITE_SEQUENCER_143 0x308F +#define WM8995_WRITE_SEQUENCER_144 0x3090 +#define WM8995_WRITE_SEQUENCER_145 0x3091 +#define WM8995_WRITE_SEQUENCER_146 0x3092 +#define WM8995_WRITE_SEQUENCER_147 0x3093 +#define WM8995_WRITE_SEQUENCER_148 0x3094 +#define WM8995_WRITE_SEQUENCER_149 0x3095 +#define WM8995_WRITE_SEQUENCER_150 0x3096 +#define WM8995_WRITE_SEQUENCER_151 0x3097 +#define WM8995_WRITE_SEQUENCER_152 0x3098 +#define WM8995_WRITE_SEQUENCER_153 0x3099 +#define WM8995_WRITE_SEQUENCER_154 0x309A +#define WM8995_WRITE_SEQUENCER_155 0x309B +#define WM8995_WRITE_SEQUENCER_156 0x309C +#define WM8995_WRITE_SEQUENCER_157 0x309D +#define WM8995_WRITE_SEQUENCER_158 0x309E +#define WM8995_WRITE_SEQUENCER_159 0x309F +#define WM8995_WRITE_SEQUENCER_160 0x30A0 +#define WM8995_WRITE_SEQUENCER_161 0x30A1 +#define WM8995_WRITE_SEQUENCER_162 0x30A2 +#define WM8995_WRITE_SEQUENCER_163 0x30A3 +#define WM8995_WRITE_SEQUENCER_164 0x30A4 +#define WM8995_WRITE_SEQUENCER_165 0x30A5 +#define WM8995_WRITE_SEQUENCER_166 0x30A6 +#define WM8995_WRITE_SEQUENCER_167 0x30A7 +#define WM8995_WRITE_SEQUENCER_168 0x30A8 +#define WM8995_WRITE_SEQUENCER_169 0x30A9 +#define WM8995_WRITE_SEQUENCER_170 0x30AA +#define WM8995_WRITE_SEQUENCER_171 0x30AB +#define WM8995_WRITE_SEQUENCER_172 0x30AC +#define WM8995_WRITE_SEQUENCER_173 0x30AD +#define WM8995_WRITE_SEQUENCER_174 0x30AE +#define WM8995_WRITE_SEQUENCER_175 0x30AF +#define WM8995_WRITE_SEQUENCER_176 0x30B0 +#define WM8995_WRITE_SEQUENCER_177 0x30B1 +#define WM8995_WRITE_SEQUENCER_178 0x30B2 +#define WM8995_WRITE_SEQUENCER_179 0x30B3 +#define WM8995_WRITE_SEQUENCER_180 0x30B4 +#define WM8995_WRITE_SEQUENCER_181 0x30B5 +#define WM8995_WRITE_SEQUENCER_182 0x30B6 +#define WM8995_WRITE_SEQUENCER_183 0x30B7 +#define WM8995_WRITE_SEQUENCER_184 0x30B8 +#define WM8995_WRITE_SEQUENCER_185 0x30B9 +#define WM8995_WRITE_SEQUENCER_186 0x30BA +#define WM8995_WRITE_SEQUENCER_187 0x30BB +#define WM8995_WRITE_SEQUENCER_188 0x30BC +#define WM8995_WRITE_SEQUENCER_189 0x30BD +#define WM8995_WRITE_SEQUENCER_190 0x30BE +#define WM8995_WRITE_SEQUENCER_191 0x30BF +#define WM8995_WRITE_SEQUENCER_192 0x30C0 +#define WM8995_WRITE_SEQUENCER_193 0x30C1 +#define WM8995_WRITE_SEQUENCER_194 0x30C2 +#define WM8995_WRITE_SEQUENCER_195 0x30C3 +#define WM8995_WRITE_SEQUENCER_196 0x30C4 +#define WM8995_WRITE_SEQUENCER_197 0x30C5 +#define WM8995_WRITE_SEQUENCER_198 0x30C6 +#define WM8995_WRITE_SEQUENCER_199 0x30C7 +#define WM8995_WRITE_SEQUENCER_200 0x30C8 +#define WM8995_WRITE_SEQUENCER_201 0x30C9 +#define WM8995_WRITE_SEQUENCER_202 0x30CA +#define WM8995_WRITE_SEQUENCER_203 0x30CB +#define WM8995_WRITE_SEQUENCER_204 0x30CC +#define WM8995_WRITE_SEQUENCER_205 0x30CD +#define WM8995_WRITE_SEQUENCER_206 0x30CE +#define WM8995_WRITE_SEQUENCER_207 0x30CF +#define WM8995_WRITE_SEQUENCER_208 0x30D0 +#define WM8995_WRITE_SEQUENCER_209 0x30D1 +#define WM8995_WRITE_SEQUENCER_210 0x30D2 +#define WM8995_WRITE_SEQUENCER_211 0x30D3 +#define WM8995_WRITE_SEQUENCER_212 0x30D4 +#define WM8995_WRITE_SEQUENCER_213 0x30D5 +#define WM8995_WRITE_SEQUENCER_214 0x30D6 +#define WM8995_WRITE_SEQUENCER_215 0x30D7 +#define WM8995_WRITE_SEQUENCER_216 0x30D8 +#define WM8995_WRITE_SEQUENCER_217 0x30D9 +#define WM8995_WRITE_SEQUENCER_218 0x30DA +#define WM8995_WRITE_SEQUENCER_219 0x30DB +#define WM8995_WRITE_SEQUENCER_220 0x30DC +#define WM8995_WRITE_SEQUENCER_221 0x30DD +#define WM8995_WRITE_SEQUENCER_222 0x30DE +#define WM8995_WRITE_SEQUENCER_223 0x30DF +#define WM8995_WRITE_SEQUENCER_224 0x30E0 +#define WM8995_WRITE_SEQUENCER_225 0x30E1 +#define WM8995_WRITE_SEQUENCER_226 0x30E2 +#define WM8995_WRITE_SEQUENCER_227 0x30E3 +#define WM8995_WRITE_SEQUENCER_228 0x30E4 +#define WM8995_WRITE_SEQUENCER_229 0x30E5 +#define WM8995_WRITE_SEQUENCER_230 0x30E6 +#define WM8995_WRITE_SEQUENCER_231 0x30E7 +#define WM8995_WRITE_SEQUENCER_232 0x30E8 +#define WM8995_WRITE_SEQUENCER_233 0x30E9 +#define WM8995_WRITE_SEQUENCER_234 0x30EA +#define WM8995_WRITE_SEQUENCER_235 0x30EB +#define WM8995_WRITE_SEQUENCER_236 0x30EC +#define WM8995_WRITE_SEQUENCER_237 0x30ED +#define WM8995_WRITE_SEQUENCER_238 0x30EE +#define WM8995_WRITE_SEQUENCER_239 0x30EF +#define WM8995_WRITE_SEQUENCER_240 0x30F0 +#define WM8995_WRITE_SEQUENCER_241 0x30F1 +#define WM8995_WRITE_SEQUENCER_242 0x30F2 +#define WM8995_WRITE_SEQUENCER_243 0x30F3 +#define WM8995_WRITE_SEQUENCER_244 0x30F4 +#define WM8995_WRITE_SEQUENCER_245 0x30F5 +#define WM8995_WRITE_SEQUENCER_246 0x30F6 +#define WM8995_WRITE_SEQUENCER_247 0x30F7 +#define WM8995_WRITE_SEQUENCER_248 0x30F8 +#define WM8995_WRITE_SEQUENCER_249 0x30F9 +#define WM8995_WRITE_SEQUENCER_250 0x30FA +#define WM8995_WRITE_SEQUENCER_251 0x30FB +#define WM8995_WRITE_SEQUENCER_252 0x30FC +#define WM8995_WRITE_SEQUENCER_253 0x30FD +#define WM8995_WRITE_SEQUENCER_254 0x30FE +#define WM8995_WRITE_SEQUENCER_255 0x30FF +#define WM8995_WRITE_SEQUENCER_256 0x3100 +#define WM8995_WRITE_SEQUENCER_257 0x3101 +#define WM8995_WRITE_SEQUENCER_258 0x3102 +#define WM8995_WRITE_SEQUENCER_259 0x3103 +#define WM8995_WRITE_SEQUENCER_260 0x3104 +#define WM8995_WRITE_SEQUENCER_261 0x3105 +#define WM8995_WRITE_SEQUENCER_262 0x3106 +#define WM8995_WRITE_SEQUENCER_263 0x3107 +#define WM8995_WRITE_SEQUENCER_264 0x3108 +#define WM8995_WRITE_SEQUENCER_265 0x3109 +#define WM8995_WRITE_SEQUENCER_266 0x310A +#define WM8995_WRITE_SEQUENCER_267 0x310B +#define WM8995_WRITE_SEQUENCER_268 0x310C +#define WM8995_WRITE_SEQUENCER_269 0x310D +#define WM8995_WRITE_SEQUENCER_270 0x310E +#define WM8995_WRITE_SEQUENCER_271 0x310F +#define WM8995_WRITE_SEQUENCER_272 0x3110 +#define WM8995_WRITE_SEQUENCER_273 0x3111 +#define WM8995_WRITE_SEQUENCER_274 0x3112 +#define WM8995_WRITE_SEQUENCER_275 0x3113 +#define WM8995_WRITE_SEQUENCER_276 0x3114 +#define WM8995_WRITE_SEQUENCER_277 0x3115 +#define WM8995_WRITE_SEQUENCER_278 0x3116 +#define WM8995_WRITE_SEQUENCER_279 0x3117 +#define WM8995_WRITE_SEQUENCER_280 0x3118 +#define WM8995_WRITE_SEQUENCER_281 0x3119 +#define WM8995_WRITE_SEQUENCER_282 0x311A +#define WM8995_WRITE_SEQUENCER_283 0x311B +#define WM8995_WRITE_SEQUENCER_284 0x311C +#define WM8995_WRITE_SEQUENCER_285 0x311D +#define WM8995_WRITE_SEQUENCER_286 0x311E +#define WM8995_WRITE_SEQUENCER_287 0x311F +#define WM8995_WRITE_SEQUENCER_288 0x3120 +#define WM8995_WRITE_SEQUENCER_289 0x3121 +#define WM8995_WRITE_SEQUENCER_290 0x3122 +#define WM8995_WRITE_SEQUENCER_291 0x3123 +#define WM8995_WRITE_SEQUENCER_292 0x3124 +#define WM8995_WRITE_SEQUENCER_293 0x3125 +#define WM8995_WRITE_SEQUENCER_294 0x3126 +#define WM8995_WRITE_SEQUENCER_295 0x3127 +#define WM8995_WRITE_SEQUENCER_296 0x3128 +#define WM8995_WRITE_SEQUENCER_297 0x3129 +#define WM8995_WRITE_SEQUENCER_298 0x312A +#define WM8995_WRITE_SEQUENCER_299 0x312B +#define WM8995_WRITE_SEQUENCER_300 0x312C +#define WM8995_WRITE_SEQUENCER_301 0x312D +#define WM8995_WRITE_SEQUENCER_302 0x312E +#define WM8995_WRITE_SEQUENCER_303 0x312F +#define WM8995_WRITE_SEQUENCER_304 0x3130 +#define WM8995_WRITE_SEQUENCER_305 0x3131 +#define WM8995_WRITE_SEQUENCER_306 0x3132 +#define WM8995_WRITE_SEQUENCER_307 0x3133 +#define WM8995_WRITE_SEQUENCER_308 0x3134 +#define WM8995_WRITE_SEQUENCER_309 0x3135 +#define WM8995_WRITE_SEQUENCER_310 0x3136 +#define WM8995_WRITE_SEQUENCER_311 0x3137 +#define WM8995_WRITE_SEQUENCER_312 0x3138 +#define WM8995_WRITE_SEQUENCER_313 0x3139 +#define WM8995_WRITE_SEQUENCER_314 0x313A +#define WM8995_WRITE_SEQUENCER_315 0x313B +#define WM8995_WRITE_SEQUENCER_316 0x313C +#define WM8995_WRITE_SEQUENCER_317 0x313D +#define WM8995_WRITE_SEQUENCER_318 0x313E +#define WM8995_WRITE_SEQUENCER_319 0x313F +#define WM8995_WRITE_SEQUENCER_320 0x3140 +#define WM8995_WRITE_SEQUENCER_321 0x3141 +#define WM8995_WRITE_SEQUENCER_322 0x3142 +#define WM8995_WRITE_SEQUENCER_323 0x3143 +#define WM8995_WRITE_SEQUENCER_324 0x3144 +#define WM8995_WRITE_SEQUENCER_325 0x3145 +#define WM8995_WRITE_SEQUENCER_326 0x3146 +#define WM8995_WRITE_SEQUENCER_327 0x3147 +#define WM8995_WRITE_SEQUENCER_328 0x3148 +#define WM8995_WRITE_SEQUENCER_329 0x3149 +#define WM8995_WRITE_SEQUENCER_330 0x314A +#define WM8995_WRITE_SEQUENCER_331 0x314B +#define WM8995_WRITE_SEQUENCER_332 0x314C +#define WM8995_WRITE_SEQUENCER_333 0x314D +#define WM8995_WRITE_SEQUENCER_334 0x314E +#define WM8995_WRITE_SEQUENCER_335 0x314F +#define WM8995_WRITE_SEQUENCER_336 0x3150 +#define WM8995_WRITE_SEQUENCER_337 0x3151 +#define WM8995_WRITE_SEQUENCER_338 0x3152 +#define WM8995_WRITE_SEQUENCER_339 0x3153 +#define WM8995_WRITE_SEQUENCER_340 0x3154 +#define WM8995_WRITE_SEQUENCER_341 0x3155 +#define WM8995_WRITE_SEQUENCER_342 0x3156 +#define WM8995_WRITE_SEQUENCER_343 0x3157 +#define WM8995_WRITE_SEQUENCER_344 0x3158 +#define WM8995_WRITE_SEQUENCER_345 0x3159 +#define WM8995_WRITE_SEQUENCER_346 0x315A +#define WM8995_WRITE_SEQUENCER_347 0x315B +#define WM8995_WRITE_SEQUENCER_348 0x315C +#define WM8995_WRITE_SEQUENCER_349 0x315D +#define WM8995_WRITE_SEQUENCER_350 0x315E +#define WM8995_WRITE_SEQUENCER_351 0x315F +#define WM8995_WRITE_SEQUENCER_352 0x3160 +#define WM8995_WRITE_SEQUENCER_353 0x3161 +#define WM8995_WRITE_SEQUENCER_354 0x3162 +#define WM8995_WRITE_SEQUENCER_355 0x3163 +#define WM8995_WRITE_SEQUENCER_356 0x3164 +#define WM8995_WRITE_SEQUENCER_357 0x3165 +#define WM8995_WRITE_SEQUENCER_358 0x3166 +#define WM8995_WRITE_SEQUENCER_359 0x3167 +#define WM8995_WRITE_SEQUENCER_360 0x3168 +#define WM8995_WRITE_SEQUENCER_361 0x3169 +#define WM8995_WRITE_SEQUENCER_362 0x316A +#define WM8995_WRITE_SEQUENCER_363 0x316B +#define WM8995_WRITE_SEQUENCER_364 0x316C +#define WM8995_WRITE_SEQUENCER_365 0x316D +#define WM8995_WRITE_SEQUENCER_366 0x316E +#define WM8995_WRITE_SEQUENCER_367 0x316F +#define WM8995_WRITE_SEQUENCER_368 0x3170 +#define WM8995_WRITE_SEQUENCER_369 0x3171 +#define WM8995_WRITE_SEQUENCER_370 0x3172 +#define WM8995_WRITE_SEQUENCER_371 0x3173 +#define WM8995_WRITE_SEQUENCER_372 0x3174 +#define WM8995_WRITE_SEQUENCER_373 0x3175 +#define WM8995_WRITE_SEQUENCER_374 0x3176 +#define WM8995_WRITE_SEQUENCER_375 0x3177 +#define WM8995_WRITE_SEQUENCER_376 0x3178 +#define WM8995_WRITE_SEQUENCER_377 0x3179 +#define WM8995_WRITE_SEQUENCER_378 0x317A +#define WM8995_WRITE_SEQUENCER_379 0x317B +#define WM8995_WRITE_SEQUENCER_380 0x317C +#define WM8995_WRITE_SEQUENCER_381 0x317D +#define WM8995_WRITE_SEQUENCER_382 0x317E +#define WM8995_WRITE_SEQUENCER_383 0x317F +#define WM8995_WRITE_SEQUENCER_384 0x3180 +#define WM8995_WRITE_SEQUENCER_385 0x3181 +#define WM8995_WRITE_SEQUENCER_386 0x3182 +#define WM8995_WRITE_SEQUENCER_387 0x3183 +#define WM8995_WRITE_SEQUENCER_388 0x3184 +#define WM8995_WRITE_SEQUENCER_389 0x3185 +#define WM8995_WRITE_SEQUENCER_390 0x3186 +#define WM8995_WRITE_SEQUENCER_391 0x3187 +#define WM8995_WRITE_SEQUENCER_392 0x3188 +#define WM8995_WRITE_SEQUENCER_393 0x3189 +#define WM8995_WRITE_SEQUENCER_394 0x318A +#define WM8995_WRITE_SEQUENCER_395 0x318B +#define WM8995_WRITE_SEQUENCER_396 0x318C +#define WM8995_WRITE_SEQUENCER_397 0x318D +#define WM8995_WRITE_SEQUENCER_398 0x318E +#define WM8995_WRITE_SEQUENCER_399 0x318F +#define WM8995_WRITE_SEQUENCER_400 0x3190 +#define WM8995_WRITE_SEQUENCER_401 0x3191 +#define WM8995_WRITE_SEQUENCER_402 0x3192 +#define WM8995_WRITE_SEQUENCER_403 0x3193 +#define WM8995_WRITE_SEQUENCER_404 0x3194 +#define WM8995_WRITE_SEQUENCER_405 0x3195 +#define WM8995_WRITE_SEQUENCER_406 0x3196 +#define WM8995_WRITE_SEQUENCER_407 0x3197 +#define WM8995_WRITE_SEQUENCER_408 0x3198 +#define WM8995_WRITE_SEQUENCER_409 0x3199 +#define WM8995_WRITE_SEQUENCER_410 0x319A +#define WM8995_WRITE_SEQUENCER_411 0x319B +#define WM8995_WRITE_SEQUENCER_412 0x319C +#define WM8995_WRITE_SEQUENCER_413 0x319D +#define WM8995_WRITE_SEQUENCER_414 0x319E +#define WM8995_WRITE_SEQUENCER_415 0x319F +#define WM8995_WRITE_SEQUENCER_416 0x31A0 +#define WM8995_WRITE_SEQUENCER_417 0x31A1 +#define WM8995_WRITE_SEQUENCER_418 0x31A2 +#define WM8995_WRITE_SEQUENCER_419 0x31A3 +#define WM8995_WRITE_SEQUENCER_420 0x31A4 +#define WM8995_WRITE_SEQUENCER_421 0x31A5 +#define WM8995_WRITE_SEQUENCER_422 0x31A6 +#define WM8995_WRITE_SEQUENCER_423 0x31A7 +#define WM8995_WRITE_SEQUENCER_424 0x31A8 +#define WM8995_WRITE_SEQUENCER_425 0x31A9 +#define WM8995_WRITE_SEQUENCER_426 0x31AA +#define WM8995_WRITE_SEQUENCER_427 0x31AB +#define WM8995_WRITE_SEQUENCER_428 0x31AC +#define WM8995_WRITE_SEQUENCER_429 0x31AD +#define WM8995_WRITE_SEQUENCER_430 0x31AE +#define WM8995_WRITE_SEQUENCER_431 0x31AF +#define WM8995_WRITE_SEQUENCER_432 0x31B0 +#define WM8995_WRITE_SEQUENCER_433 0x31B1 +#define WM8995_WRITE_SEQUENCER_434 0x31B2 +#define WM8995_WRITE_SEQUENCER_435 0x31B3 +#define WM8995_WRITE_SEQUENCER_436 0x31B4 +#define WM8995_WRITE_SEQUENCER_437 0x31B5 +#define WM8995_WRITE_SEQUENCER_438 0x31B6 +#define WM8995_WRITE_SEQUENCER_439 0x31B7 +#define WM8995_WRITE_SEQUENCER_440 0x31B8 +#define WM8995_WRITE_SEQUENCER_441 0x31B9 +#define WM8995_WRITE_SEQUENCER_442 0x31BA +#define WM8995_WRITE_SEQUENCER_443 0x31BB +#define WM8995_WRITE_SEQUENCER_444 0x31BC +#define WM8995_WRITE_SEQUENCER_445 0x31BD +#define WM8995_WRITE_SEQUENCER_446 0x31BE +#define WM8995_WRITE_SEQUENCER_447 0x31BF +#define WM8995_WRITE_SEQUENCER_448 0x31C0 +#define WM8995_WRITE_SEQUENCER_449 0x31C1 +#define WM8995_WRITE_SEQUENCER_450 0x31C2 +#define WM8995_WRITE_SEQUENCER_451 0x31C3 +#define WM8995_WRITE_SEQUENCER_452 0x31C4 +#define WM8995_WRITE_SEQUENCER_453 0x31C5 +#define WM8995_WRITE_SEQUENCER_454 0x31C6 +#define WM8995_WRITE_SEQUENCER_455 0x31C7 +#define WM8995_WRITE_SEQUENCER_456 0x31C8 +#define WM8995_WRITE_SEQUENCER_457 0x31C9 +#define WM8995_WRITE_SEQUENCER_458 0x31CA +#define WM8995_WRITE_SEQUENCER_459 0x31CB +#define WM8995_WRITE_SEQUENCER_460 0x31CC +#define WM8995_WRITE_SEQUENCER_461 0x31CD +#define WM8995_WRITE_SEQUENCER_462 0x31CE +#define WM8995_WRITE_SEQUENCER_463 0x31CF +#define WM8995_WRITE_SEQUENCER_464 0x31D0 +#define WM8995_WRITE_SEQUENCER_465 0x31D1 +#define WM8995_WRITE_SEQUENCER_466 0x31D2 +#define WM8995_WRITE_SEQUENCER_467 0x31D3 +#define WM8995_WRITE_SEQUENCER_468 0x31D4 +#define WM8995_WRITE_SEQUENCER_469 0x31D5 +#define WM8995_WRITE_SEQUENCER_470 0x31D6 +#define WM8995_WRITE_SEQUENCER_471 0x31D7 +#define WM8995_WRITE_SEQUENCER_472 0x31D8 +#define WM8995_WRITE_SEQUENCER_473 0x31D9 +#define WM8995_WRITE_SEQUENCER_474 0x31DA +#define WM8995_WRITE_SEQUENCER_475 0x31DB +#define WM8995_WRITE_SEQUENCER_476 0x31DC +#define WM8995_WRITE_SEQUENCER_477 0x31DD +#define WM8995_WRITE_SEQUENCER_478 0x31DE +#define WM8995_WRITE_SEQUENCER_479 0x31DF +#define WM8995_WRITE_SEQUENCER_480 0x31E0 +#define WM8995_WRITE_SEQUENCER_481 0x31E1 +#define WM8995_WRITE_SEQUENCER_482 0x31E2 +#define WM8995_WRITE_SEQUENCER_483 0x31E3 +#define WM8995_WRITE_SEQUENCER_484 0x31E4 +#define WM8995_WRITE_SEQUENCER_485 0x31E5 +#define WM8995_WRITE_SEQUENCER_486 0x31E6 +#define WM8995_WRITE_SEQUENCER_487 0x31E7 +#define WM8995_WRITE_SEQUENCER_488 0x31E8 +#define WM8995_WRITE_SEQUENCER_489 0x31E9 +#define WM8995_WRITE_SEQUENCER_490 0x31EA +#define WM8995_WRITE_SEQUENCER_491 0x31EB +#define WM8995_WRITE_SEQUENCER_492 0x31EC +#define WM8995_WRITE_SEQUENCER_493 0x31ED +#define WM8995_WRITE_SEQUENCER_494 0x31EE +#define WM8995_WRITE_SEQUENCER_495 0x31EF +#define WM8995_WRITE_SEQUENCER_496 0x31F0 +#define WM8995_WRITE_SEQUENCER_497 0x31F1 +#define WM8995_WRITE_SEQUENCER_498 0x31F2 +#define WM8995_WRITE_SEQUENCER_499 0x31F3 +#define WM8995_WRITE_SEQUENCER_500 0x31F4 +#define WM8995_WRITE_SEQUENCER_501 0x31F5 +#define WM8995_WRITE_SEQUENCER_502 0x31F6 +#define WM8995_WRITE_SEQUENCER_503 0x31F7 +#define WM8995_WRITE_SEQUENCER_504 0x31F8 +#define WM8995_WRITE_SEQUENCER_505 0x31F9 +#define WM8995_WRITE_SEQUENCER_506 0x31FA +#define WM8995_WRITE_SEQUENCER_507 0x31FB +#define WM8995_WRITE_SEQUENCER_508 0x31FC +#define WM8995_WRITE_SEQUENCER_509 0x31FD +#define WM8995_WRITE_SEQUENCER_510 0x31FE +#define WM8995_WRITE_SEQUENCER_511 0x31FF + +#define WM8995_REGISTER_COUNT 725 +#define WM8995_MAX_REGISTER 0x31FF + +#define WM8995_MAX_CACHED_REGISTER WM8995_MAX_REGISTER + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +#define WM8995_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */ +#define WM8995_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */ +#define WM8995_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */ + +/* + * R1 (0x01) - Power Management (1) + */ +#define WM8995_MICB2_ENA 0x0200 /* MICB2_ENA */ +#define WM8995_MICB2_ENA_MASK 0x0200 /* MICB2_ENA */ +#define WM8995_MICB2_ENA_SHIFT 9 /* MICB2_ENA */ +#define WM8995_MICB2_ENA_WIDTH 1 /* MICB2_ENA */ +#define WM8995_MICB1_ENA 0x0100 /* MICB1_ENA */ +#define WM8995_MICB1_ENA_MASK 0x0100 /* MICB1_ENA */ +#define WM8995_MICB1_ENA_SHIFT 8 /* MICB1_ENA */ +#define WM8995_MICB1_ENA_WIDTH 1 /* MICB1_ENA */ +#define WM8995_HPOUT2L_ENA 0x0080 /* HPOUT2L_ENA */ +#define WM8995_HPOUT2L_ENA_MASK 0x0080 /* HPOUT2L_ENA */ +#define WM8995_HPOUT2L_ENA_SHIFT 7 /* HPOUT2L_ENA */ +#define WM8995_HPOUT2L_ENA_WIDTH 1 /* HPOUT2L_ENA */ +#define WM8995_HPOUT2R_ENA 0x0040 /* HPOUT2R_ENA */ +#define WM8995_HPOUT2R_ENA_MASK 0x0040 /* HPOUT2R_ENA */ +#define WM8995_HPOUT2R_ENA_SHIFT 6 /* HPOUT2R_ENA */ +#define WM8995_HPOUT2R_ENA_WIDTH 1 /* HPOUT2R_ENA */ +#define WM8995_HPOUT1L_ENA 0x0020 /* HPOUT1L_ENA */ +#define WM8995_HPOUT1L_ENA_MASK 0x0020 /* HPOUT1L_ENA */ +#define WM8995_HPOUT1L_ENA_SHIFT 5 /* HPOUT1L_ENA */ +#define WM8995_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */ +#define WM8995_HPOUT1R_ENA 0x0010 /* HPOUT1R_ENA */ +#define WM8995_HPOUT1R_ENA_MASK 0x0010 /* HPOUT1R_ENA */ +#define WM8995_HPOUT1R_ENA_SHIFT 4 /* HPOUT1R_ENA */ +#define WM8995_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */ +#define WM8995_BG_ENA 0x0001 /* BG_ENA */ +#define WM8995_BG_ENA_MASK 0x0001 /* BG_ENA */ +#define WM8995_BG_ENA_SHIFT 0 /* BG_ENA */ +#define WM8995_BG_ENA_WIDTH 1 /* BG_ENA */ + +/* + * R2 (0x02) - Power Management (2) + */ +#define WM8995_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8995_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */ +#define WM8995_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */ +#define WM8995_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define WM8995_IN1L_ENA 0x0020 /* IN1L_ENA */ +#define WM8995_IN1L_ENA_MASK 0x0020 /* IN1L_ENA */ +#define WM8995_IN1L_ENA_SHIFT 5 /* IN1L_ENA */ +#define WM8995_IN1L_ENA_WIDTH 1 /* IN1L_ENA */ +#define WM8995_IN1R_ENA 0x0010 /* IN1R_ENA */ +#define WM8995_IN1R_ENA_MASK 0x0010 /* IN1R_ENA */ +#define WM8995_IN1R_ENA_SHIFT 4 /* IN1R_ENA */ +#define WM8995_IN1R_ENA_WIDTH 1 /* IN1R_ENA */ +#define WM8995_LDO2_ENA 0x0002 /* LDO2_ENA */ +#define WM8995_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */ +#define WM8995_LDO2_ENA_SHIFT 1 /* LDO2_ENA */ +#define WM8995_LDO2_ENA_WIDTH 1 /* LDO2_ENA */ + +/* + * R3 (0x03) - Power Management (3) + */ +#define WM8995_AIF2ADCL_ENA 0x2000 /* AIF2ADCL_ENA */ +#define WM8995_AIF2ADCL_ENA_MASK 0x2000 /* AIF2ADCL_ENA */ +#define WM8995_AIF2ADCL_ENA_SHIFT 13 /* AIF2ADCL_ENA */ +#define WM8995_AIF2ADCL_ENA_WIDTH 1 /* AIF2ADCL_ENA */ +#define WM8995_AIF2ADCR_ENA 0x1000 /* AIF2ADCR_ENA */ +#define WM8995_AIF2ADCR_ENA_MASK 0x1000 /* AIF2ADCR_ENA */ +#define WM8995_AIF2ADCR_ENA_SHIFT 12 /* AIF2ADCR_ENA */ +#define WM8995_AIF2ADCR_ENA_WIDTH 1 /* AIF2ADCR_ENA */ +#define WM8995_AIF1ADC2L_ENA 0x0800 /* AIF1ADC2L_ENA */ +#define WM8995_AIF1ADC2L_ENA_MASK 0x0800 /* AIF1ADC2L_ENA */ +#define WM8995_AIF1ADC2L_ENA_SHIFT 11 /* AIF1ADC2L_ENA */ +#define WM8995_AIF1ADC2L_ENA_WIDTH 1 /* AIF1ADC2L_ENA */ +#define WM8995_AIF1ADC2R_ENA 0x0400 /* AIF1ADC2R_ENA */ +#define WM8995_AIF1ADC2R_ENA_MASK 0x0400 /* AIF1ADC2R_ENA */ +#define WM8995_AIF1ADC2R_ENA_SHIFT 10 /* AIF1ADC2R_ENA */ +#define WM8995_AIF1ADC2R_ENA_WIDTH 1 /* AIF1ADC2R_ENA */ +#define WM8995_AIF1ADC1L_ENA 0x0200 /* AIF1ADC1L_ENA */ +#define WM8995_AIF1ADC1L_ENA_MASK 0x0200 /* AIF1ADC1L_ENA */ +#define WM8995_AIF1ADC1L_ENA_SHIFT 9 /* AIF1ADC1L_ENA */ +#define WM8995_AIF1ADC1L_ENA_WIDTH 1 /* AIF1ADC1L_ENA */ +#define WM8995_AIF1ADC1R_ENA 0x0100 /* AIF1ADC1R_ENA */ +#define WM8995_AIF1ADC1R_ENA_MASK 0x0100 /* AIF1ADC1R_ENA */ +#define WM8995_AIF1ADC1R_ENA_SHIFT 8 /* AIF1ADC1R_ENA */ +#define WM8995_AIF1ADC1R_ENA_WIDTH 1 /* AIF1ADC1R_ENA */ +#define WM8995_DMIC3L_ENA 0x0080 /* DMIC3L_ENA */ +#define WM8995_DMIC3L_ENA_MASK 0x0080 /* DMIC3L_ENA */ +#define WM8995_DMIC3L_ENA_SHIFT 7 /* DMIC3L_ENA */ +#define WM8995_DMIC3L_ENA_WIDTH 1 /* DMIC3L_ENA */ +#define WM8995_DMIC3R_ENA 0x0040 /* DMIC3R_ENA */ +#define WM8995_DMIC3R_ENA_MASK 0x0040 /* DMIC3R_ENA */ +#define WM8995_DMIC3R_ENA_SHIFT 6 /* DMIC3R_ENA */ +#define WM8995_DMIC3R_ENA_WIDTH 1 /* DMIC3R_ENA */ +#define WM8995_DMIC2L_ENA 0x0020 /* DMIC2L_ENA */ +#define WM8995_DMIC2L_ENA_MASK 0x0020 /* DMIC2L_ENA */ +#define WM8995_DMIC2L_ENA_SHIFT 5 /* DMIC2L_ENA */ +#define WM8995_DMIC2L_ENA_WIDTH 1 /* DMIC2L_ENA */ +#define WM8995_DMIC2R_ENA 0x0010 /* DMIC2R_ENA */ +#define WM8995_DMIC2R_ENA_MASK 0x0010 /* DMIC2R_ENA */ +#define WM8995_DMIC2R_ENA_SHIFT 4 /* DMIC2R_ENA */ +#define WM8995_DMIC2R_ENA_WIDTH 1 /* DMIC2R_ENA */ +#define WM8995_DMIC1L_ENA 0x0008 /* DMIC1L_ENA */ +#define WM8995_DMIC1L_ENA_MASK 0x0008 /* DMIC1L_ENA */ +#define WM8995_DMIC1L_ENA_SHIFT 3 /* DMIC1L_ENA */ +#define WM8995_DMIC1L_ENA_WIDTH 1 /* DMIC1L_ENA */ +#define WM8995_DMIC1R_ENA 0x0004 /* DMIC1R_ENA */ +#define WM8995_DMIC1R_ENA_MASK 0x0004 /* DMIC1R_ENA */ +#define WM8995_DMIC1R_ENA_SHIFT 2 /* DMIC1R_ENA */ +#define WM8995_DMIC1R_ENA_WIDTH 1 /* DMIC1R_ENA */ +#define WM8995_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8995_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */ +#define WM8995_ADCL_ENA_SHIFT 1 /* ADCL_ENA */ +#define WM8995_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ +#define WM8995_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8995_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */ +#define WM8995_ADCR_ENA_SHIFT 0 /* ADCR_ENA */ +#define WM8995_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ + +/* + * R4 (0x04) - Power Management (4) + */ +#define WM8995_AIF2DACL_ENA 0x2000 /* AIF2DACL_ENA */ +#define WM8995_AIF2DACL_ENA_MASK 0x2000 /* AIF2DACL_ENA */ +#define WM8995_AIF2DACL_ENA_SHIFT 13 /* AIF2DACL_ENA */ +#define WM8995_AIF2DACL_ENA_WIDTH 1 /* AIF2DACL_ENA */ +#define WM8995_AIF2DACR_ENA 0x1000 /* AIF2DACR_ENA */ +#define WM8995_AIF2DACR_ENA_MASK 0x1000 /* AIF2DACR_ENA */ +#define WM8995_AIF2DACR_ENA_SHIFT 12 /* AIF2DACR_ENA */ +#define WM8995_AIF2DACR_ENA_WIDTH 1 /* AIF2DACR_ENA */ +#define WM8995_AIF1DAC2L_ENA 0x0800 /* AIF1DAC2L_ENA */ +#define WM8995_AIF1DAC2L_ENA_MASK 0x0800 /* AIF1DAC2L_ENA */ +#define WM8995_AIF1DAC2L_ENA_SHIFT 11 /* AIF1DAC2L_ENA */ +#define WM8995_AIF1DAC2L_ENA_WIDTH 1 /* AIF1DAC2L_ENA */ +#define WM8995_AIF1DAC2R_ENA 0x0400 /* AIF1DAC2R_ENA */ +#define WM8995_AIF1DAC2R_ENA_MASK 0x0400 /* AIF1DAC2R_ENA */ +#define WM8995_AIF1DAC2R_ENA_SHIFT 10 /* AIF1DAC2R_ENA */ +#define WM8995_AIF1DAC2R_ENA_WIDTH 1 /* AIF1DAC2R_ENA */ +#define WM8995_AIF1DAC1L_ENA 0x0200 /* AIF1DAC1L_ENA */ +#define WM8995_AIF1DAC1L_ENA_MASK 0x0200 /* AIF1DAC1L_ENA */ +#define WM8995_AIF1DAC1L_ENA_SHIFT 9 /* AIF1DAC1L_ENA */ +#define WM8995_AIF1DAC1L_ENA_WIDTH 1 /* AIF1DAC1L_ENA */ +#define WM8995_AIF1DAC1R_ENA 0x0100 /* AIF1DAC1R_ENA */ +#define WM8995_AIF1DAC1R_ENA_MASK 0x0100 /* AIF1DAC1R_ENA */ +#define WM8995_AIF1DAC1R_ENA_SHIFT 8 /* AIF1DAC1R_ENA */ +#define WM8995_AIF1DAC1R_ENA_WIDTH 1 /* AIF1DAC1R_ENA */ +#define WM8995_DAC2L_ENA 0x0008 /* DAC2L_ENA */ +#define WM8995_DAC2L_ENA_MASK 0x0008 /* DAC2L_ENA */ +#define WM8995_DAC2L_ENA_SHIFT 3 /* DAC2L_ENA */ +#define WM8995_DAC2L_ENA_WIDTH 1 /* DAC2L_ENA */ +#define WM8995_DAC2R_ENA 0x0004 /* DAC2R_ENA */ +#define WM8995_DAC2R_ENA_MASK 0x0004 /* DAC2R_ENA */ +#define WM8995_DAC2R_ENA_SHIFT 2 /* DAC2R_ENA */ +#define WM8995_DAC2R_ENA_WIDTH 1 /* DAC2R_ENA */ +#define WM8995_DAC1L_ENA 0x0002 /* DAC1L_ENA */ +#define WM8995_DAC1L_ENA_MASK 0x0002 /* DAC1L_ENA */ +#define WM8995_DAC1L_ENA_SHIFT 1 /* DAC1L_ENA */ +#define WM8995_DAC1L_ENA_WIDTH 1 /* DAC1L_ENA */ +#define WM8995_DAC1R_ENA 0x0001 /* DAC1R_ENA */ +#define WM8995_DAC1R_ENA_MASK 0x0001 /* DAC1R_ENA */ +#define WM8995_DAC1R_ENA_SHIFT 0 /* DAC1R_ENA */ +#define WM8995_DAC1R_ENA_WIDTH 1 /* DAC1R_ENA */ + +/* + * R5 (0x05) - Power Management (5) + */ +#define WM8995_DMIC_SRC2_MASK 0x0300 /* DMIC_SRC2 - [9:8] */ +#define WM8995_DMIC_SRC2_SHIFT 8 /* DMIC_SRC2 - [9:8] */ +#define WM8995_DMIC_SRC2_WIDTH 2 /* DMIC_SRC2 - [9:8] */ +#define WM8995_DMIC_SRC1_MASK 0x00C0 /* DMIC_SRC1 - [7:6] */ +#define WM8995_DMIC_SRC1_SHIFT 6 /* DMIC_SRC1 - [7:6] */ +#define WM8995_DMIC_SRC1_WIDTH 2 /* DMIC_SRC1 - [7:6] */ +#define WM8995_AIF3_TRI 0x0020 /* AIF3_TRI */ +#define WM8995_AIF3_TRI_MASK 0x0020 /* AIF3_TRI */ +#define WM8995_AIF3_TRI_SHIFT 5 /* AIF3_TRI */ +#define WM8995_AIF3_TRI_WIDTH 1 /* AIF3_TRI */ +#define WM8995_AIF3_ADCDAT_SRC_MASK 0x0018 /* AIF3_ADCDAT_SRC - [4:3] */ +#define WM8995_AIF3_ADCDAT_SRC_SHIFT 3 /* AIF3_ADCDAT_SRC - [4:3] */ +#define WM8995_AIF3_ADCDAT_SRC_WIDTH 2 /* AIF3_ADCDAT_SRC - [4:3] */ +#define WM8995_AIF2_ADCDAT_SRC 0x0004 /* AIF2_ADCDAT_SRC */ +#define WM8995_AIF2_ADCDAT_SRC_MASK 0x0004 /* AIF2_ADCDAT_SRC */ +#define WM8995_AIF2_ADCDAT_SRC_SHIFT 2 /* AIF2_ADCDAT_SRC */ +#define WM8995_AIF2_ADCDAT_SRC_WIDTH 1 /* AIF2_ADCDAT_SRC */ +#define WM8995_AIF2_DACDAT_SRC 0x0002 /* AIF2_DACDAT_SRC */ +#define WM8995_AIF2_DACDAT_SRC_MASK 0x0002 /* AIF2_DACDAT_SRC */ +#define WM8995_AIF2_DACDAT_SRC_SHIFT 1 /* AIF2_DACDAT_SRC */ +#define WM8995_AIF2_DACDAT_SRC_WIDTH 1 /* AIF2_DACDAT_SRC */ +#define WM8995_AIF1_DACDAT_SRC 0x0001 /* AIF1_DACDAT_SRC */ +#define WM8995_AIF1_DACDAT_SRC_MASK 0x0001 /* AIF1_DACDAT_SRC */ +#define WM8995_AIF1_DACDAT_SRC_SHIFT 0 /* AIF1_DACDAT_SRC */ +#define WM8995_AIF1_DACDAT_SRC_WIDTH 1 /* AIF1_DACDAT_SRC */ + +/* + * R16 (0x10) - Left Line Input 1 Volume + */ +#define WM8995_IN1_VU 0x0080 /* IN1_VU */ +#define WM8995_IN1_VU_MASK 0x0080 /* IN1_VU */ +#define WM8995_IN1_VU_SHIFT 7 /* IN1_VU */ +#define WM8995_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM8995_IN1L_ZC 0x0020 /* IN1L_ZC */ +#define WM8995_IN1L_ZC_MASK 0x0020 /* IN1L_ZC */ +#define WM8995_IN1L_ZC_SHIFT 5 /* IN1L_ZC */ +#define WM8995_IN1L_ZC_WIDTH 1 /* IN1L_ZC */ +#define WM8995_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */ +#define WM8995_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */ +#define WM8995_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */ + +/* + * R17 (0x11) - Right Line Input 1 Volume + */ +#define WM8995_IN1_VU 0x0080 /* IN1_VU */ +#define WM8995_IN1_VU_MASK 0x0080 /* IN1_VU */ +#define WM8995_IN1_VU_SHIFT 7 /* IN1_VU */ +#define WM8995_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM8995_IN1R_ZC 0x0020 /* IN1R_ZC */ +#define WM8995_IN1R_ZC_MASK 0x0020 /* IN1R_ZC */ +#define WM8995_IN1R_ZC_SHIFT 5 /* IN1R_ZC */ +#define WM8995_IN1R_ZC_WIDTH 1 /* IN1R_ZC */ +#define WM8995_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */ +#define WM8995_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */ +#define WM8995_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */ + +/* + * R18 (0x12) - Left Line Input Control + */ +#define WM8995_IN1L_BOOST_MASK 0x0030 /* IN1L_BOOST - [5:4] */ +#define WM8995_IN1L_BOOST_SHIFT 4 /* IN1L_BOOST - [5:4] */ +#define WM8995_IN1L_BOOST_WIDTH 2 /* IN1L_BOOST - [5:4] */ +#define WM8995_IN1L_MODE_MASK 0x000C /* IN1L_MODE - [3:2] */ +#define WM8995_IN1L_MODE_SHIFT 2 /* IN1L_MODE - [3:2] */ +#define WM8995_IN1L_MODE_WIDTH 2 /* IN1L_MODE - [3:2] */ +#define WM8995_IN1R_MODE_MASK 0x0003 /* IN1R_MODE - [1:0] */ +#define WM8995_IN1R_MODE_SHIFT 0 /* IN1R_MODE - [1:0] */ +#define WM8995_IN1R_MODE_WIDTH 2 /* IN1R_MODE - [1:0] */ + +/* + * R24 (0x18) - DAC1 Left Volume + */ +#define WM8995_DAC1L_MUTE 0x0200 /* DAC1L_MUTE */ +#define WM8995_DAC1L_MUTE_MASK 0x0200 /* DAC1L_MUTE */ +#define WM8995_DAC1L_MUTE_SHIFT 9 /* DAC1L_MUTE */ +#define WM8995_DAC1L_MUTE_WIDTH 1 /* DAC1L_MUTE */ +#define WM8995_DAC1_VU 0x0100 /* DAC1_VU */ +#define WM8995_DAC1_VU_MASK 0x0100 /* DAC1_VU */ +#define WM8995_DAC1_VU_SHIFT 8 /* DAC1_VU */ +#define WM8995_DAC1_VU_WIDTH 1 /* DAC1_VU */ +#define WM8995_DAC1L_VOL_MASK 0x00FF /* DAC1L_VOL - [7:0] */ +#define WM8995_DAC1L_VOL_SHIFT 0 /* DAC1L_VOL - [7:0] */ +#define WM8995_DAC1L_VOL_WIDTH 8 /* DAC1L_VOL - [7:0] */ + +/* + * R25 (0x19) - DAC1 Right Volume + */ +#define WM8995_DAC1R_MUTE 0x0200 /* DAC1R_MUTE */ +#define WM8995_DAC1R_MUTE_MASK 0x0200 /* DAC1R_MUTE */ +#define WM8995_DAC1R_MUTE_SHIFT 9 /* DAC1R_MUTE */ +#define WM8995_DAC1R_MUTE_WIDTH 1 /* DAC1R_MUTE */ +#define WM8995_DAC1_VU 0x0100 /* DAC1_VU */ +#define WM8995_DAC1_VU_MASK 0x0100 /* DAC1_VU */ +#define WM8995_DAC1_VU_SHIFT 8 /* DAC1_VU */ +#define WM8995_DAC1_VU_WIDTH 1 /* DAC1_VU */ +#define WM8995_DAC1R_VOL_MASK 0x00FF /* DAC1R_VOL - [7:0] */ +#define WM8995_DAC1R_VOL_SHIFT 0 /* DAC1R_VOL - [7:0] */ +#define WM8995_DAC1R_VOL_WIDTH 8 /* DAC1R_VOL - [7:0] */ + +/* + * R26 (0x1A) - DAC2 Left Volume + */ +#define WM8995_DAC2L_MUTE 0x0200 /* DAC2L_MUTE */ +#define WM8995_DAC2L_MUTE_MASK 0x0200 /* DAC2L_MUTE */ +#define WM8995_DAC2L_MUTE_SHIFT 9 /* DAC2L_MUTE */ +#define WM8995_DAC2L_MUTE_WIDTH 1 /* DAC2L_MUTE */ +#define WM8995_DAC2_VU 0x0100 /* DAC2_VU */ +#define WM8995_DAC2_VU_MASK 0x0100 /* DAC2_VU */ +#define WM8995_DAC2_VU_SHIFT 8 /* DAC2_VU */ +#define WM8995_DAC2_VU_WIDTH 1 /* DAC2_VU */ +#define WM8995_DAC2L_VOL_MASK 0x00FF /* DAC2L_VOL - [7:0] */ +#define WM8995_DAC2L_VOL_SHIFT 0 /* DAC2L_VOL - [7:0] */ +#define WM8995_DAC2L_VOL_WIDTH 8 /* DAC2L_VOL - [7:0] */ + +/* + * R27 (0x1B) - DAC2 Right Volume + */ +#define WM8995_DAC2R_MUTE 0x0200 /* DAC2R_MUTE */ +#define WM8995_DAC2R_MUTE_MASK 0x0200 /* DAC2R_MUTE */ +#define WM8995_DAC2R_MUTE_SHIFT 9 /* DAC2R_MUTE */ +#define WM8995_DAC2R_MUTE_WIDTH 1 /* DAC2R_MUTE */ +#define WM8995_DAC2_VU 0x0100 /* DAC2_VU */ +#define WM8995_DAC2_VU_MASK 0x0100 /* DAC2_VU */ +#define WM8995_DAC2_VU_SHIFT 8 /* DAC2_VU */ +#define WM8995_DAC2_VU_WIDTH 1 /* DAC2_VU */ +#define WM8995_DAC2R_VOL_MASK 0x00FF /* DAC2R_VOL - [7:0] */ +#define WM8995_DAC2R_VOL_SHIFT 0 /* DAC2R_VOL - [7:0] */ +#define WM8995_DAC2R_VOL_WIDTH 8 /* DAC2R_VOL - [7:0] */ + +/* + * R28 (0x1C) - Output Volume ZC (1) + */ +#define WM8995_HPOUT2L_ZC 0x0008 /* HPOUT2L_ZC */ +#define WM8995_HPOUT2L_ZC_MASK 0x0008 /* HPOUT2L_ZC */ +#define WM8995_HPOUT2L_ZC_SHIFT 3 /* HPOUT2L_ZC */ +#define WM8995_HPOUT2L_ZC_WIDTH 1 /* HPOUT2L_ZC */ +#define WM8995_HPOUT2R_ZC 0x0004 /* HPOUT2R_ZC */ +#define WM8995_HPOUT2R_ZC_MASK 0x0004 /* HPOUT2R_ZC */ +#define WM8995_HPOUT2R_ZC_SHIFT 2 /* HPOUT2R_ZC */ +#define WM8995_HPOUT2R_ZC_WIDTH 1 /* HPOUT2R_ZC */ +#define WM8995_HPOUT1L_ZC 0x0002 /* HPOUT1L_ZC */ +#define WM8995_HPOUT1L_ZC_MASK 0x0002 /* HPOUT1L_ZC */ +#define WM8995_HPOUT1L_ZC_SHIFT 1 /* HPOUT1L_ZC */ +#define WM8995_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */ +#define WM8995_HPOUT1R_ZC 0x0001 /* HPOUT1R_ZC */ +#define WM8995_HPOUT1R_ZC_MASK 0x0001 /* HPOUT1R_ZC */ +#define WM8995_HPOUT1R_ZC_SHIFT 0 /* HPOUT1R_ZC */ +#define WM8995_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */ + +/* + * R32 (0x20) - MICBIAS (1) + */ +#define WM8995_MICB1_MODE 0x0008 /* MICB1_MODE */ +#define WM8995_MICB1_MODE_MASK 0x0008 /* MICB1_MODE */ +#define WM8995_MICB1_MODE_SHIFT 3 /* MICB1_MODE */ +#define WM8995_MICB1_MODE_WIDTH 1 /* MICB1_MODE */ +#define WM8995_MICB1_LVL_MASK 0x0006 /* MICB1_LVL - [2:1] */ +#define WM8995_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [2:1] */ +#define WM8995_MICB1_LVL_WIDTH 2 /* MICB1_LVL - [2:1] */ +#define WM8995_MICB1_DISCH 0x0001 /* MICB1_DISCH */ +#define WM8995_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */ +#define WM8995_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */ +#define WM8995_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */ + +/* + * R33 (0x21) - MICBIAS (2) + */ +#define WM8995_MICB2_MODE 0x0008 /* MICB2_MODE */ +#define WM8995_MICB2_MODE_MASK 0x0008 /* MICB2_MODE */ +#define WM8995_MICB2_MODE_SHIFT 3 /* MICB2_MODE */ +#define WM8995_MICB2_MODE_WIDTH 1 /* MICB2_MODE */ +#define WM8995_MICB2_LVL_MASK 0x0006 /* MICB2_LVL - [2:1] */ +#define WM8995_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [2:1] */ +#define WM8995_MICB2_LVL_WIDTH 2 /* MICB2_LVL - [2:1] */ +#define WM8995_MICB2_DISCH 0x0001 /* MICB2_DISCH */ +#define WM8995_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */ +#define WM8995_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */ +#define WM8995_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */ + +/* + * R40 (0x28) - LDO 1 + */ +#define WM8995_LDO1_MODE 0x0020 /* LDO1_MODE */ +#define WM8995_LDO1_MODE_MASK 0x0020 /* LDO1_MODE */ +#define WM8995_LDO1_MODE_SHIFT 5 /* LDO1_MODE */ +#define WM8995_LDO1_MODE_WIDTH 1 /* LDO1_MODE */ +#define WM8995_LDO1_VSEL_MASK 0x0006 /* LDO1_VSEL - [2:1] */ +#define WM8995_LDO1_VSEL_SHIFT 1 /* LDO1_VSEL - [2:1] */ +#define WM8995_LDO1_VSEL_WIDTH 2 /* LDO1_VSEL - [2:1] */ +#define WM8995_LDO1_DISCH 0x0001 /* LDO1_DISCH */ +#define WM8995_LDO1_DISCH_MASK 0x0001 /* LDO1_DISCH */ +#define WM8995_LDO1_DISCH_SHIFT 0 /* LDO1_DISCH */ +#define WM8995_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */ + +/* + * R41 (0x29) - LDO 2 + */ +#define WM8995_LDO2_MODE 0x0020 /* LDO2_MODE */ +#define WM8995_LDO2_MODE_MASK 0x0020 /* LDO2_MODE */ +#define WM8995_LDO2_MODE_SHIFT 5 /* LDO2_MODE */ +#define WM8995_LDO2_MODE_WIDTH 1 /* LDO2_MODE */ +#define WM8995_LDO2_VSEL_MASK 0x001E /* LDO2_VSEL - [4:1] */ +#define WM8995_LDO2_VSEL_SHIFT 1 /* LDO2_VSEL - [4:1] */ +#define WM8995_LDO2_VSEL_WIDTH 4 /* LDO2_VSEL - [4:1] */ +#define WM8995_LDO2_DISCH 0x0001 /* LDO2_DISCH */ +#define WM8995_LDO2_DISCH_MASK 0x0001 /* LDO2_DISCH */ +#define WM8995_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */ +#define WM8995_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */ + +/* + * R48 (0x30) - Accessory Detect Mode1 + */ +#define WM8995_JD_MODE_MASK 0x0003 /* JD_MODE - [1:0] */ +#define WM8995_JD_MODE_SHIFT 0 /* JD_MODE - [1:0] */ +#define WM8995_JD_MODE_WIDTH 2 /* JD_MODE - [1:0] */ + +/* + * R49 (0x31) - Accessory Detect Mode2 + */ +#define WM8995_VID_ENA 0x0001 /* VID_ENA */ +#define WM8995_VID_ENA_MASK 0x0001 /* VID_ENA */ +#define WM8995_VID_ENA_SHIFT 0 /* VID_ENA */ +#define WM8995_VID_ENA_WIDTH 1 /* VID_ENA */ + +/* + * R52 (0x34) - Headphone Detect1 + */ +#define WM8995_HP_RAMPRATE 0x0002 /* HP_RAMPRATE */ +#define WM8995_HP_RAMPRATE_MASK 0x0002 /* HP_RAMPRATE */ +#define WM8995_HP_RAMPRATE_SHIFT 1 /* HP_RAMPRATE */ +#define WM8995_HP_RAMPRATE_WIDTH 1 /* HP_RAMPRATE */ +#define WM8995_HP_POLL 0x0001 /* HP_POLL */ +#define WM8995_HP_POLL_MASK 0x0001 /* HP_POLL */ +#define WM8995_HP_POLL_SHIFT 0 /* HP_POLL */ +#define WM8995_HP_POLL_WIDTH 1 /* HP_POLL */ + +/* + * R53 (0x35) - Headphone Detect2 + */ +#define WM8995_HP_DONE 0x0080 /* HP_DONE */ +#define WM8995_HP_DONE_MASK 0x0080 /* HP_DONE */ +#define WM8995_HP_DONE_SHIFT 7 /* HP_DONE */ +#define WM8995_HP_DONE_WIDTH 1 /* HP_DONE */ +#define WM8995_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */ +#define WM8995_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */ +#define WM8995_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */ + +/* + * R56 (0x38) - Mic Detect (1) + */ +#define WM8995_MICD_RATE_MASK 0x7800 /* MICD_RATE - [14:11] */ +#define WM8995_MICD_RATE_SHIFT 11 /* MICD_RATE - [14:11] */ +#define WM8995_MICD_RATE_WIDTH 4 /* MICD_RATE - [14:11] */ +#define WM8995_MICD_LVL_SEL_MASK 0x01F8 /* MICD_LVL_SEL - [8:3] */ +#define WM8995_MICD_LVL_SEL_SHIFT 3 /* MICD_LVL_SEL - [8:3] */ +#define WM8995_MICD_LVL_SEL_WIDTH 6 /* MICD_LVL_SEL - [8:3] */ +#define WM8995_MICD_DBTIME 0x0002 /* MICD_DBTIME */ +#define WM8995_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */ +#define WM8995_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */ +#define WM8995_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */ +#define WM8995_MICD_ENA 0x0001 /* MICD_ENA */ +#define WM8995_MICD_ENA_MASK 0x0001 /* MICD_ENA */ +#define WM8995_MICD_ENA_SHIFT 0 /* MICD_ENA */ +#define WM8995_MICD_ENA_WIDTH 1 /* MICD_ENA */ + +/* + * R57 (0x39) - Mic Detect (2) + */ +#define WM8995_MICD_LVL_MASK 0x01FC /* MICD_LVL - [8:2] */ +#define WM8995_MICD_LVL_SHIFT 2 /* MICD_LVL - [8:2] */ +#define WM8995_MICD_LVL_WIDTH 7 /* MICD_LVL - [8:2] */ +#define WM8995_MICD_VALID 0x0002 /* MICD_VALID */ +#define WM8995_MICD_VALID_MASK 0x0002 /* MICD_VALID */ +#define WM8995_MICD_VALID_SHIFT 1 /* MICD_VALID */ +#define WM8995_MICD_VALID_WIDTH 1 /* MICD_VALID */ +#define WM8995_MICD_STS 0x0001 /* MICD_STS */ +#define WM8995_MICD_STS_MASK 0x0001 /* MICD_STS */ +#define WM8995_MICD_STS_SHIFT 0 /* MICD_STS */ +#define WM8995_MICD_STS_WIDTH 1 /* MICD_STS */ + +/* + * R64 (0x40) - Charge Pump (1) + */ +#define WM8995_CP_ENA 0x8000 /* CP_ENA */ +#define WM8995_CP_ENA_MASK 0x8000 /* CP_ENA */ +#define WM8995_CP_ENA_SHIFT 15 /* CP_ENA */ +#define WM8995_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R69 (0x45) - Class W (1) + */ +#define WM8995_CP_DYN_SRC_SEL_MASK 0x0300 /* CP_DYN_SRC_SEL - [9:8] */ +#define WM8995_CP_DYN_SRC_SEL_SHIFT 8 /* CP_DYN_SRC_SEL - [9:8] */ +#define WM8995_CP_DYN_SRC_SEL_WIDTH 2 /* CP_DYN_SRC_SEL - [9:8] */ +#define WM8995_CP_DYN_PWR 0x0001 /* CP_DYN_PWR */ +#define WM8995_CP_DYN_PWR_MASK 0x0001 /* CP_DYN_PWR */ +#define WM8995_CP_DYN_PWR_SHIFT 0 /* CP_DYN_PWR */ +#define WM8995_CP_DYN_PWR_WIDTH 1 /* CP_DYN_PWR */ + +/* + * R80 (0x50) - DC Servo (1) + */ +#define WM8995_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */ +#define WM8995_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */ +#define WM8995_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */ +#define WM8995_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */ +#define WM8995_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */ +#define WM8995_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */ +#define WM8995_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */ +#define WM8995_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */ +#define WM8995_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8995_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8995_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */ +#define WM8995_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */ +#define WM8995_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8995_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8995_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */ +#define WM8995_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */ + +/* + * R81 (0x51) - DC Servo (2) + */ +#define WM8995_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */ +#define WM8995_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */ +#define WM8995_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */ +#define WM8995_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */ +#define WM8995_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */ +#define WM8995_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */ +#define WM8995_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */ +#define WM8995_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */ +#define WM8995_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8995_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8995_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */ +#define WM8995_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */ +#define WM8995_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8995_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8995_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */ +#define WM8995_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */ +#define WM8995_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */ +#define WM8995_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */ +#define WM8995_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */ +#define WM8995_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */ +#define WM8995_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */ +#define WM8995_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */ +#define WM8995_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */ +#define WM8995_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */ +#define WM8995_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8995_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8995_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */ +#define WM8995_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */ +#define WM8995_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8995_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8995_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */ +#define WM8995_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */ +#define WM8995_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */ +#define WM8995_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */ +#define WM8995_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */ +#define WM8995_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */ +#define WM8995_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */ +#define WM8995_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */ +#define WM8995_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */ +#define WM8995_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */ +#define WM8995_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8995_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8995_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */ +#define WM8995_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */ +#define WM8995_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8995_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8995_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */ +#define WM8995_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */ +#define WM8995_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */ +#define WM8995_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */ +#define WM8995_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */ +#define WM8995_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */ +#define WM8995_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */ +#define WM8995_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */ +#define WM8995_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */ +#define WM8995_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */ +#define WM8995_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */ +#define WM8995_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */ +#define WM8995_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM8995_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM8995_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */ +#define WM8995_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */ +#define WM8995_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */ +#define WM8995_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */ + +/* + * R82 (0x52) - DC Servo (3) + */ +#define WM8995_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8995_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8995_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8995_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8995_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8995_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */ + +/* + * R84 (0x54) - DC Servo (5) + */ +#define WM8995_DCS_SERIES_NO_23_MASK 0x7F00 /* DCS_SERIES_NO_23 - [14:8] */ +#define WM8995_DCS_SERIES_NO_23_SHIFT 8 /* DCS_SERIES_NO_23 - [14:8] */ +#define WM8995_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [14:8] */ +#define WM8995_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */ +#define WM8995_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */ +#define WM8995_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */ + +/* + * R85 (0x55) - DC Servo (6) + */ +#define WM8995_DCS_DAC_WR_VAL_3_MASK 0xFF00 /* DCS_DAC_WR_VAL_3 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_3_SHIFT 8 /* DCS_DAC_WR_VAL_3 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */ +#define WM8995_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */ +#define WM8995_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */ + +/* + * R86 (0x56) - DC Servo (7) + */ +#define WM8995_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8995_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8995_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8995_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */ + +/* + * R87 (0x57) - DC Servo Readback 0 + */ +#define WM8995_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8995_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8995_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8995_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8995_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8995_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8995_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */ +#define WM8995_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */ +#define WM8995_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */ + +/* + * R96 (0x60) - Analogue HP (1) + */ +#define WM8995_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM8995_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM8995_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */ +#define WM8995_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */ +#define WM8995_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */ +#define WM8995_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */ +#define WM8995_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */ +#define WM8995_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */ +#define WM8995_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */ +#define WM8995_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */ +#define WM8995_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */ +#define WM8995_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */ +#define WM8995_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM8995_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM8995_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */ +#define WM8995_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */ +#define WM8995_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */ +#define WM8995_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */ +#define WM8995_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */ +#define WM8995_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */ +#define WM8995_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */ +#define WM8995_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */ +#define WM8995_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */ +#define WM8995_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */ + +/* + * R97 (0x61) - Analogue HP (2) + */ +#define WM8995_HPOUT2L_RMV_SHORT 0x0080 /* HPOUT2L_RMV_SHORT */ +#define WM8995_HPOUT2L_RMV_SHORT_MASK 0x0080 /* HPOUT2L_RMV_SHORT */ +#define WM8995_HPOUT2L_RMV_SHORT_SHIFT 7 /* HPOUT2L_RMV_SHORT */ +#define WM8995_HPOUT2L_RMV_SHORT_WIDTH 1 /* HPOUT2L_RMV_SHORT */ +#define WM8995_HPOUT2L_OUTP 0x0040 /* HPOUT2L_OUTP */ +#define WM8995_HPOUT2L_OUTP_MASK 0x0040 /* HPOUT2L_OUTP */ +#define WM8995_HPOUT2L_OUTP_SHIFT 6 /* HPOUT2L_OUTP */ +#define WM8995_HPOUT2L_OUTP_WIDTH 1 /* HPOUT2L_OUTP */ +#define WM8995_HPOUT2L_DLY 0x0020 /* HPOUT2L_DLY */ +#define WM8995_HPOUT2L_DLY_MASK 0x0020 /* HPOUT2L_DLY */ +#define WM8995_HPOUT2L_DLY_SHIFT 5 /* HPOUT2L_DLY */ +#define WM8995_HPOUT2L_DLY_WIDTH 1 /* HPOUT2L_DLY */ +#define WM8995_HPOUT2R_RMV_SHORT 0x0008 /* HPOUT2R_RMV_SHORT */ +#define WM8995_HPOUT2R_RMV_SHORT_MASK 0x0008 /* HPOUT2R_RMV_SHORT */ +#define WM8995_HPOUT2R_RMV_SHORT_SHIFT 3 /* HPOUT2R_RMV_SHORT */ +#define WM8995_HPOUT2R_RMV_SHORT_WIDTH 1 /* HPOUT2R_RMV_SHORT */ +#define WM8995_HPOUT2R_OUTP 0x0004 /* HPOUT2R_OUTP */ +#define WM8995_HPOUT2R_OUTP_MASK 0x0004 /* HPOUT2R_OUTP */ +#define WM8995_HPOUT2R_OUTP_SHIFT 2 /* HPOUT2R_OUTP */ +#define WM8995_HPOUT2R_OUTP_WIDTH 1 /* HPOUT2R_OUTP */ +#define WM8995_HPOUT2R_DLY 0x0002 /* HPOUT2R_DLY */ +#define WM8995_HPOUT2R_DLY_MASK 0x0002 /* HPOUT2R_DLY */ +#define WM8995_HPOUT2R_DLY_SHIFT 1 /* HPOUT2R_DLY */ +#define WM8995_HPOUT2R_DLY_WIDTH 1 /* HPOUT2R_DLY */ + +/* + * R256 (0x100) - Chip Revision + */ +#define WM8995_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */ +#define WM8995_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */ +#define WM8995_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */ + +/* + * R257 (0x101) - Control Interface (1) + */ +#define WM8995_REG_SYNC 0x8000 /* REG_SYNC */ +#define WM8995_REG_SYNC_MASK 0x8000 /* REG_SYNC */ +#define WM8995_REG_SYNC_SHIFT 15 /* REG_SYNC */ +#define WM8995_REG_SYNC_WIDTH 1 /* REG_SYNC */ +#define WM8995_SPI_CONTRD 0x0040 /* SPI_CONTRD */ +#define WM8995_SPI_CONTRD_MASK 0x0040 /* SPI_CONTRD */ +#define WM8995_SPI_CONTRD_SHIFT 6 /* SPI_CONTRD */ +#define WM8995_SPI_CONTRD_WIDTH 1 /* SPI_CONTRD */ +#define WM8995_SPI_4WIRE 0x0020 /* SPI_4WIRE */ +#define WM8995_SPI_4WIRE_MASK 0x0020 /* SPI_4WIRE */ +#define WM8995_SPI_4WIRE_SHIFT 5 /* SPI_4WIRE */ +#define WM8995_SPI_4WIRE_WIDTH 1 /* SPI_4WIRE */ +#define WM8995_SPI_CFG 0x0010 /* SPI_CFG */ +#define WM8995_SPI_CFG_MASK 0x0010 /* SPI_CFG */ +#define WM8995_SPI_CFG_SHIFT 4 /* SPI_CFG */ +#define WM8995_SPI_CFG_WIDTH 1 /* SPI_CFG */ +#define WM8995_AUTO_INC 0x0004 /* AUTO_INC */ +#define WM8995_AUTO_INC_MASK 0x0004 /* AUTO_INC */ +#define WM8995_AUTO_INC_SHIFT 2 /* AUTO_INC */ +#define WM8995_AUTO_INC_WIDTH 1 /* AUTO_INC */ + +/* + * R258 (0x102) - Control Interface (2) + */ +#define WM8995_CTRL_IF_SRC 0x0001 /* CTRL_IF_SRC */ +#define WM8995_CTRL_IF_SRC_MASK 0x0001 /* CTRL_IF_SRC */ +#define WM8995_CTRL_IF_SRC_SHIFT 0 /* CTRL_IF_SRC */ +#define WM8995_CTRL_IF_SRC_WIDTH 1 /* CTRL_IF_SRC */ + +/* + * R272 (0x110) - Write Sequencer Ctrl (1) + */ +#define WM8995_WSEQ_ENA 0x8000 /* WSEQ_ENA */ +#define WM8995_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */ +#define WM8995_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */ +#define WM8995_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM8995_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */ +#define WM8995_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */ +#define WM8995_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */ +#define WM8995_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM8995_WSEQ_START 0x0100 /* WSEQ_START */ +#define WM8995_WSEQ_START_MASK 0x0100 /* WSEQ_START */ +#define WM8995_WSEQ_START_SHIFT 8 /* WSEQ_START */ +#define WM8995_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM8995_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */ +#define WM8995_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */ +#define WM8995_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */ + +/* + * R273 (0x111) - Write Sequencer Ctrl (2) + */ +#define WM8995_WSEQ_BUSY 0x0100 /* WSEQ_BUSY */ +#define WM8995_WSEQ_BUSY_MASK 0x0100 /* WSEQ_BUSY */ +#define WM8995_WSEQ_BUSY_SHIFT 8 /* WSEQ_BUSY */ +#define WM8995_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ +#define WM8995_WSEQ_CURRENT_INDEX_MASK 0x007F /* WSEQ_CURRENT_INDEX - [6:0] */ +#define WM8995_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [6:0] */ +#define WM8995_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [6:0] */ + +/* + * R512 (0x200) - AIF1 Clocking (1) + */ +#define WM8995_AIF1CLK_SRC_MASK 0x0018 /* AIF1CLK_SRC - [4:3] */ +#define WM8995_AIF1CLK_SRC_SHIFT 3 /* AIF1CLK_SRC - [4:3] */ +#define WM8995_AIF1CLK_SRC_WIDTH 2 /* AIF1CLK_SRC - [4:3] */ +#define WM8995_AIF1CLK_INV 0x0004 /* AIF1CLK_INV */ +#define WM8995_AIF1CLK_INV_MASK 0x0004 /* AIF1CLK_INV */ +#define WM8995_AIF1CLK_INV_SHIFT 2 /* AIF1CLK_INV */ +#define WM8995_AIF1CLK_INV_WIDTH 1 /* AIF1CLK_INV */ +#define WM8995_AIF1CLK_DIV 0x0002 /* AIF1CLK_DIV */ +#define WM8995_AIF1CLK_DIV_MASK 0x0002 /* AIF1CLK_DIV */ +#define WM8995_AIF1CLK_DIV_SHIFT 1 /* AIF1CLK_DIV */ +#define WM8995_AIF1CLK_DIV_WIDTH 1 /* AIF1CLK_DIV */ +#define WM8995_AIF1CLK_ENA 0x0001 /* AIF1CLK_ENA */ +#define WM8995_AIF1CLK_ENA_MASK 0x0001 /* AIF1CLK_ENA */ +#define WM8995_AIF1CLK_ENA_SHIFT 0 /* AIF1CLK_ENA */ +#define WM8995_AIF1CLK_ENA_WIDTH 1 /* AIF1CLK_ENA */ + +/* + * R513 (0x201) - AIF1 Clocking (2) + */ +#define WM8995_AIF1DAC_DIV_MASK 0x0038 /* AIF1DAC_DIV - [5:3] */ +#define WM8995_AIF1DAC_DIV_SHIFT 3 /* AIF1DAC_DIV - [5:3] */ +#define WM8995_AIF1DAC_DIV_WIDTH 3 /* AIF1DAC_DIV - [5:3] */ +#define WM8995_AIF1ADC_DIV_MASK 0x0007 /* AIF1ADC_DIV - [2:0] */ +#define WM8995_AIF1ADC_DIV_SHIFT 0 /* AIF1ADC_DIV - [2:0] */ +#define WM8995_AIF1ADC_DIV_WIDTH 3 /* AIF1ADC_DIV - [2:0] */ + +/* + * R516 (0x204) - AIF2 Clocking (1) + */ +#define WM8995_AIF2CLK_SRC_MASK 0x0018 /* AIF2CLK_SRC - [4:3] */ +#define WM8995_AIF2CLK_SRC_SHIFT 3 /* AIF2CLK_SRC - [4:3] */ +#define WM8995_AIF2CLK_SRC_WIDTH 2 /* AIF2CLK_SRC - [4:3] */ +#define WM8995_AIF2CLK_INV 0x0004 /* AIF2CLK_INV */ +#define WM8995_AIF2CLK_INV_MASK 0x0004 /* AIF2CLK_INV */ +#define WM8995_AIF2CLK_INV_SHIFT 2 /* AIF2CLK_INV */ +#define WM8995_AIF2CLK_INV_WIDTH 1 /* AIF2CLK_INV */ +#define WM8995_AIF2CLK_DIV 0x0002 /* AIF2CLK_DIV */ +#define WM8995_AIF2CLK_DIV_MASK 0x0002 /* AIF2CLK_DIV */ +#define WM8995_AIF2CLK_DIV_SHIFT 1 /* AIF2CLK_DIV */ +#define WM8995_AIF2CLK_DIV_WIDTH 1 /* AIF2CLK_DIV */ +#define WM8995_AIF2CLK_ENA 0x0001 /* AIF2CLK_ENA */ +#define WM8995_AIF2CLK_ENA_MASK 0x0001 /* AIF2CLK_ENA */ +#define WM8995_AIF2CLK_ENA_SHIFT 0 /* AIF2CLK_ENA */ +#define WM8995_AIF2CLK_ENA_WIDTH 1 /* AIF2CLK_ENA */ + +/* + * R517 (0x205) - AIF2 Clocking (2) + */ +#define WM8995_AIF2DAC_DIV_MASK 0x0038 /* AIF2DAC_DIV - [5:3] */ +#define WM8995_AIF2DAC_DIV_SHIFT 3 /* AIF2DAC_DIV - [5:3] */ +#define WM8995_AIF2DAC_DIV_WIDTH 3 /* AIF2DAC_DIV - [5:3] */ +#define WM8995_AIF2ADC_DIV_MASK 0x0007 /* AIF2ADC_DIV - [2:0] */ +#define WM8995_AIF2ADC_DIV_SHIFT 0 /* AIF2ADC_DIV - [2:0] */ +#define WM8995_AIF2ADC_DIV_WIDTH 3 /* AIF2ADC_DIV - [2:0] */ + +/* + * R520 (0x208) - Clocking (1) + */ +#define WM8995_LFCLK_ENA 0x0020 /* LFCLK_ENA */ +#define WM8995_LFCLK_ENA_MASK 0x0020 /* LFCLK_ENA */ +#define WM8995_LFCLK_ENA_SHIFT 5 /* LFCLK_ENA */ +#define WM8995_LFCLK_ENA_WIDTH 1 /* LFCLK_ENA */ +#define WM8995_TOCLK_ENA 0x0010 /* TOCLK_ENA */ +#define WM8995_TOCLK_ENA_MASK 0x0010 /* TOCLK_ENA */ +#define WM8995_TOCLK_ENA_SHIFT 4 /* TOCLK_ENA */ +#define WM8995_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */ +#define WM8995_AIF1DSPCLK_ENA 0x0008 /* AIF1DSPCLK_ENA */ +#define WM8995_AIF1DSPCLK_ENA_MASK 0x0008 /* AIF1DSPCLK_ENA */ +#define WM8995_AIF1DSPCLK_ENA_SHIFT 3 /* AIF1DSPCLK_ENA */ +#define WM8995_AIF1DSPCLK_ENA_WIDTH 1 /* AIF1DSPCLK_ENA */ +#define WM8995_AIF2DSPCLK_ENA 0x0004 /* AIF2DSPCLK_ENA */ +#define WM8995_AIF2DSPCLK_ENA_MASK 0x0004 /* AIF2DSPCLK_ENA */ +#define WM8995_AIF2DSPCLK_ENA_SHIFT 2 /* AIF2DSPCLK_ENA */ +#define WM8995_AIF2DSPCLK_ENA_WIDTH 1 /* AIF2DSPCLK_ENA */ +#define WM8995_SYSDSPCLK_ENA 0x0002 /* SYSDSPCLK_ENA */ +#define WM8995_SYSDSPCLK_ENA_MASK 0x0002 /* SYSDSPCLK_ENA */ +#define WM8995_SYSDSPCLK_ENA_SHIFT 1 /* SYSDSPCLK_ENA */ +#define WM8995_SYSDSPCLK_ENA_WIDTH 1 /* SYSDSPCLK_ENA */ +#define WM8995_SYSCLK_SRC 0x0001 /* SYSCLK_SRC */ +#define WM8995_SYSCLK_SRC_MASK 0x0001 /* SYSCLK_SRC */ +#define WM8995_SYSCLK_SRC_SHIFT 0 /* SYSCLK_SRC */ +#define WM8995_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */ + +/* + * R521 (0x209) - Clocking (2) + */ +#define WM8995_TOCLK_DIV_MASK 0x0700 /* TOCLK_DIV - [10:8] */ +#define WM8995_TOCLK_DIV_SHIFT 8 /* TOCLK_DIV - [10:8] */ +#define WM8995_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [10:8] */ +#define WM8995_DBCLK_DIV_MASK 0x00F0 /* DBCLK_DIV - [7:4] */ +#define WM8995_DBCLK_DIV_SHIFT 4 /* DBCLK_DIV - [7:4] */ +#define WM8995_DBCLK_DIV_WIDTH 4 /* DBCLK_DIV - [7:4] */ +#define WM8995_OPCLK_DIV_MASK 0x0007 /* OPCLK_DIV - [2:0] */ +#define WM8995_OPCLK_DIV_SHIFT 0 /* OPCLK_DIV - [2:0] */ +#define WM8995_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [2:0] */ + +/* + * R528 (0x210) - AIF1 Rate + */ +#define WM8995_AIF1_SR_MASK 0x00F0 /* AIF1_SR - [7:4] */ +#define WM8995_AIF1_SR_SHIFT 4 /* AIF1_SR - [7:4] */ +#define WM8995_AIF1_SR_WIDTH 4 /* AIF1_SR - [7:4] */ +#define WM8995_AIF1CLK_RATE_MASK 0x000F /* AIF1CLK_RATE - [3:0] */ +#define WM8995_AIF1CLK_RATE_SHIFT 0 /* AIF1CLK_RATE - [3:0] */ +#define WM8995_AIF1CLK_RATE_WIDTH 4 /* AIF1CLK_RATE - [3:0] */ + +/* + * R529 (0x211) - AIF2 Rate + */ +#define WM8995_AIF2_SR_MASK 0x00F0 /* AIF2_SR - [7:4] */ +#define WM8995_AIF2_SR_SHIFT 4 /* AIF2_SR - [7:4] */ +#define WM8995_AIF2_SR_WIDTH 4 /* AIF2_SR - [7:4] */ +#define WM8995_AIF2CLK_RATE_MASK 0x000F /* AIF2CLK_RATE - [3:0] */ +#define WM8995_AIF2CLK_RATE_SHIFT 0 /* AIF2CLK_RATE - [3:0] */ +#define WM8995_AIF2CLK_RATE_WIDTH 4 /* AIF2CLK_RATE - [3:0] */ + +/* + * R530 (0x212) - Rate Status + */ +#define WM8995_SR_ERROR_MASK 0x000F /* SR_ERROR - [3:0] */ +#define WM8995_SR_ERROR_SHIFT 0 /* SR_ERROR - [3:0] */ +#define WM8995_SR_ERROR_WIDTH 4 /* SR_ERROR - [3:0] */ + +/* + * R544 (0x220) - FLL1 Control (1) + */ +#define WM8995_FLL1_OSC_ENA 0x0002 /* FLL1_OSC_ENA */ +#define WM8995_FLL1_OSC_ENA_MASK 0x0002 /* FLL1_OSC_ENA */ +#define WM8995_FLL1_OSC_ENA_SHIFT 1 /* FLL1_OSC_ENA */ +#define WM8995_FLL1_OSC_ENA_WIDTH 1 /* FLL1_OSC_ENA */ +#define WM8995_FLL1_ENA 0x0001 /* FLL1_ENA */ +#define WM8995_FLL1_ENA_MASK 0x0001 /* FLL1_ENA */ +#define WM8995_FLL1_ENA_SHIFT 0 /* FLL1_ENA */ +#define WM8995_FLL1_ENA_WIDTH 1 /* FLL1_ENA */ + +/* + * R545 (0x221) - FLL1 Control (2) + */ +#define WM8995_FLL1_OUTDIV_MASK 0x3F00 /* FLL1_OUTDIV - [13:8] */ +#define WM8995_FLL1_OUTDIV_SHIFT 8 /* FLL1_OUTDIV - [13:8] */ +#define WM8995_FLL1_OUTDIV_WIDTH 6 /* FLL1_OUTDIV - [13:8] */ +#define WM8995_FLL1_CTRL_RATE_MASK 0x0070 /* FLL1_CTRL_RATE - [6:4] */ +#define WM8995_FLL1_CTRL_RATE_SHIFT 4 /* FLL1_CTRL_RATE - [6:4] */ +#define WM8995_FLL1_CTRL_RATE_WIDTH 3 /* FLL1_CTRL_RATE - [6:4] */ +#define WM8995_FLL1_FRATIO_MASK 0x0007 /* FLL1_FRATIO - [2:0] */ +#define WM8995_FLL1_FRATIO_SHIFT 0 /* FLL1_FRATIO - [2:0] */ +#define WM8995_FLL1_FRATIO_WIDTH 3 /* FLL1_FRATIO - [2:0] */ + +/* + * R546 (0x222) - FLL1 Control (3) + */ +#define WM8995_FLL1_K_MASK 0xFFFF /* FLL1_K - [15:0] */ +#define WM8995_FLL1_K_SHIFT 0 /* FLL1_K - [15:0] */ +#define WM8995_FLL1_K_WIDTH 16 /* FLL1_K - [15:0] */ + +/* + * R547 (0x223) - FLL1 Control (4) + */ +#define WM8995_FLL1_N_MASK 0x7FE0 /* FLL1_N - [14:5] */ +#define WM8995_FLL1_N_SHIFT 5 /* FLL1_N - [14:5] */ +#define WM8995_FLL1_N_WIDTH 10 /* FLL1_N - [14:5] */ +#define WM8995_FLL1_LOOP_GAIN_MASK 0x000F /* FLL1_LOOP_GAIN - [3:0] */ +#define WM8995_FLL1_LOOP_GAIN_SHIFT 0 /* FLL1_LOOP_GAIN - [3:0] */ +#define WM8995_FLL1_LOOP_GAIN_WIDTH 4 /* FLL1_LOOP_GAIN - [3:0] */ + +/* + * R548 (0x224) - FLL1 Control (5) + */ +#define WM8995_FLL1_FRC_NCO_VAL_MASK 0x1F80 /* FLL1_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL1_FRC_NCO_VAL_SHIFT 7 /* FLL1_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL1_FRC_NCO_VAL_WIDTH 6 /* FLL1_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL1_FRC_NCO 0x0040 /* FLL1_FRC_NCO */ +#define WM8995_FLL1_FRC_NCO_MASK 0x0040 /* FLL1_FRC_NCO */ +#define WM8995_FLL1_FRC_NCO_SHIFT 6 /* FLL1_FRC_NCO */ +#define WM8995_FLL1_FRC_NCO_WIDTH 1 /* FLL1_FRC_NCO */ +#define WM8995_FLL1_REFCLK_DIV_MASK 0x0018 /* FLL1_REFCLK_DIV - [4:3] */ +#define WM8995_FLL1_REFCLK_DIV_SHIFT 3 /* FLL1_REFCLK_DIV - [4:3] */ +#define WM8995_FLL1_REFCLK_DIV_WIDTH 2 /* FLL1_REFCLK_DIV - [4:3] */ +#define WM8995_FLL1_REFCLK_SRC_MASK 0x0003 /* FLL1_REFCLK_SRC - [1:0] */ +#define WM8995_FLL1_REFCLK_SRC_SHIFT 0 /* FLL1_REFCLK_SRC - [1:0] */ +#define WM8995_FLL1_REFCLK_SRC_WIDTH 2 /* FLL1_REFCLK_SRC - [1:0] */ + +/* + * R576 (0x240) - FLL2 Control (1) + */ +#define WM8995_FLL2_OSC_ENA 0x0002 /* FLL2_OSC_ENA */ +#define WM8995_FLL2_OSC_ENA_MASK 0x0002 /* FLL2_OSC_ENA */ +#define WM8995_FLL2_OSC_ENA_SHIFT 1 /* FLL2_OSC_ENA */ +#define WM8995_FLL2_OSC_ENA_WIDTH 1 /* FLL2_OSC_ENA */ +#define WM8995_FLL2_ENA 0x0001 /* FLL2_ENA */ +#define WM8995_FLL2_ENA_MASK 0x0001 /* FLL2_ENA */ +#define WM8995_FLL2_ENA_SHIFT 0 /* FLL2_ENA */ +#define WM8995_FLL2_ENA_WIDTH 1 /* FLL2_ENA */ + +/* + * R577 (0x241) - FLL2 Control (2) + */ +#define WM8995_FLL2_OUTDIV_MASK 0x3F00 /* FLL2_OUTDIV - [13:8] */ +#define WM8995_FLL2_OUTDIV_SHIFT 8 /* FLL2_OUTDIV - [13:8] */ +#define WM8995_FLL2_OUTDIV_WIDTH 6 /* FLL2_OUTDIV - [13:8] */ +#define WM8995_FLL2_CTRL_RATE_MASK 0x0070 /* FLL2_CTRL_RATE - [6:4] */ +#define WM8995_FLL2_CTRL_RATE_SHIFT 4 /* FLL2_CTRL_RATE - [6:4] */ +#define WM8995_FLL2_CTRL_RATE_WIDTH 3 /* FLL2_CTRL_RATE - [6:4] */ +#define WM8995_FLL2_FRATIO_MASK 0x0007 /* FLL2_FRATIO - [2:0] */ +#define WM8995_FLL2_FRATIO_SHIFT 0 /* FLL2_FRATIO - [2:0] */ +#define WM8995_FLL2_FRATIO_WIDTH 3 /* FLL2_FRATIO - [2:0] */ + +/* + * R578 (0x242) - FLL2 Control (3) + */ +#define WM8995_FLL2_K_MASK 0xFFFF /* FLL2_K - [15:0] */ +#define WM8995_FLL2_K_SHIFT 0 /* FLL2_K - [15:0] */ +#define WM8995_FLL2_K_WIDTH 16 /* FLL2_K - [15:0] */ + +/* + * R579 (0x243) - FLL2 Control (4) + */ +#define WM8995_FLL2_N_MASK 0x7FE0 /* FLL2_N - [14:5] */ +#define WM8995_FLL2_N_SHIFT 5 /* FLL2_N - [14:5] */ +#define WM8995_FLL2_N_WIDTH 10 /* FLL2_N - [14:5] */ +#define WM8995_FLL2_LOOP_GAIN_MASK 0x000F /* FLL2_LOOP_GAIN - [3:0] */ +#define WM8995_FLL2_LOOP_GAIN_SHIFT 0 /* FLL2_LOOP_GAIN - [3:0] */ +#define WM8995_FLL2_LOOP_GAIN_WIDTH 4 /* FLL2_LOOP_GAIN - [3:0] */ + +/* + * R580 (0x244) - FLL2 Control (5) + */ +#define WM8995_FLL2_FRC_NCO_VAL_MASK 0x1F80 /* FLL2_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL2_FRC_NCO_VAL_SHIFT 7 /* FLL2_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL2_FRC_NCO_VAL_WIDTH 6 /* FLL2_FRC_NCO_VAL - [12:7] */ +#define WM8995_FLL2_FRC_NCO 0x0040 /* FLL2_FRC_NCO */ +#define WM8995_FLL2_FRC_NCO_MASK 0x0040 /* FLL2_FRC_NCO */ +#define WM8995_FLL2_FRC_NCO_SHIFT 6 /* FLL2_FRC_NCO */ +#define WM8995_FLL2_FRC_NCO_WIDTH 1 /* FLL2_FRC_NCO */ +#define WM8995_FLL2_REFCLK_DIV_MASK 0x0018 /* FLL2_REFCLK_DIV - [4:3] */ +#define WM8995_FLL2_REFCLK_DIV_SHIFT 3 /* FLL2_REFCLK_DIV - [4:3] */ +#define WM8995_FLL2_REFCLK_DIV_WIDTH 2 /* FLL2_REFCLK_DIV - [4:3] */ +#define WM8995_FLL2_REFCLK_SRC_MASK 0x0003 /* FLL2_REFCLK_SRC - [1:0] */ +#define WM8995_FLL2_REFCLK_SRC_SHIFT 0 /* FLL2_REFCLK_SRC - [1:0] */ +#define WM8995_FLL2_REFCLK_SRC_WIDTH 2 /* FLL2_REFCLK_SRC - [1:0] */ + +/* + * R768 (0x300) - AIF1 Control (1) + */ +#define WM8995_AIF1ADCL_SRC 0x8000 /* AIF1ADCL_SRC */ +#define WM8995_AIF1ADCL_SRC_MASK 0x8000 /* AIF1ADCL_SRC */ +#define WM8995_AIF1ADCL_SRC_SHIFT 15 /* AIF1ADCL_SRC */ +#define WM8995_AIF1ADCL_SRC_WIDTH 1 /* AIF1ADCL_SRC */ +#define WM8995_AIF1ADCR_SRC 0x4000 /* AIF1ADCR_SRC */ +#define WM8995_AIF1ADCR_SRC_MASK 0x4000 /* AIF1ADCR_SRC */ +#define WM8995_AIF1ADCR_SRC_SHIFT 14 /* AIF1ADCR_SRC */ +#define WM8995_AIF1ADCR_SRC_WIDTH 1 /* AIF1ADCR_SRC */ +#define WM8995_AIF1ADC_TDM 0x2000 /* AIF1ADC_TDM */ +#define WM8995_AIF1ADC_TDM_MASK 0x2000 /* AIF1ADC_TDM */ +#define WM8995_AIF1ADC_TDM_SHIFT 13 /* AIF1ADC_TDM */ +#define WM8995_AIF1ADC_TDM_WIDTH 1 /* AIF1ADC_TDM */ +#define WM8995_AIF1_BCLK_INV 0x0100 /* AIF1_BCLK_INV */ +#define WM8995_AIF1_BCLK_INV_MASK 0x0100 /* AIF1_BCLK_INV */ +#define WM8995_AIF1_BCLK_INV_SHIFT 8 /* AIF1_BCLK_INV */ +#define WM8995_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */ +#define WM8995_AIF1_LRCLK_INV 0x0080 /* AIF1_LRCLK_INV */ +#define WM8995_AIF1_LRCLK_INV_MASK 0x0080 /* AIF1_LRCLK_INV */ +#define WM8995_AIF1_LRCLK_INV_SHIFT 7 /* AIF1_LRCLK_INV */ +#define WM8995_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */ +#define WM8995_AIF1_WL_MASK 0x0060 /* AIF1_WL - [6:5] */ +#define WM8995_AIF1_WL_SHIFT 5 /* AIF1_WL - [6:5] */ +#define WM8995_AIF1_WL_WIDTH 2 /* AIF1_WL - [6:5] */ +#define WM8995_AIF1_FMT_MASK 0x0018 /* AIF1_FMT - [4:3] */ +#define WM8995_AIF1_FMT_SHIFT 3 /* AIF1_FMT - [4:3] */ +#define WM8995_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [4:3] */ + +/* + * R769 (0x301) - AIF1 Control (2) + */ +#define WM8995_AIF1DACL_SRC 0x8000 /* AIF1DACL_SRC */ +#define WM8995_AIF1DACL_SRC_MASK 0x8000 /* AIF1DACL_SRC */ +#define WM8995_AIF1DACL_SRC_SHIFT 15 /* AIF1DACL_SRC */ +#define WM8995_AIF1DACL_SRC_WIDTH 1 /* AIF1DACL_SRC */ +#define WM8995_AIF1DACR_SRC 0x4000 /* AIF1DACR_SRC */ +#define WM8995_AIF1DACR_SRC_MASK 0x4000 /* AIF1DACR_SRC */ +#define WM8995_AIF1DACR_SRC_SHIFT 14 /* AIF1DACR_SRC */ +#define WM8995_AIF1DACR_SRC_WIDTH 1 /* AIF1DACR_SRC */ +#define WM8995_AIF1DAC_BOOST_MASK 0x0C00 /* AIF1DAC_BOOST - [11:10] */ +#define WM8995_AIF1DAC_BOOST_SHIFT 10 /* AIF1DAC_BOOST - [11:10] */ +#define WM8995_AIF1DAC_BOOST_WIDTH 2 /* AIF1DAC_BOOST - [11:10] */ +#define WM8995_AIF1DAC_COMP 0x0010 /* AIF1DAC_COMP */ +#define WM8995_AIF1DAC_COMP_MASK 0x0010 /* AIF1DAC_COMP */ +#define WM8995_AIF1DAC_COMP_SHIFT 4 /* AIF1DAC_COMP */ +#define WM8995_AIF1DAC_COMP_WIDTH 1 /* AIF1DAC_COMP */ +#define WM8995_AIF1DAC_COMPMODE 0x0008 /* AIF1DAC_COMPMODE */ +#define WM8995_AIF1DAC_COMPMODE_MASK 0x0008 /* AIF1DAC_COMPMODE */ +#define WM8995_AIF1DAC_COMPMODE_SHIFT 3 /* AIF1DAC_COMPMODE */ +#define WM8995_AIF1DAC_COMPMODE_WIDTH 1 /* AIF1DAC_COMPMODE */ +#define WM8995_AIF1ADC_COMP 0x0004 /* AIF1ADC_COMP */ +#define WM8995_AIF1ADC_COMP_MASK 0x0004 /* AIF1ADC_COMP */ +#define WM8995_AIF1ADC_COMP_SHIFT 2 /* AIF1ADC_COMP */ +#define WM8995_AIF1ADC_COMP_WIDTH 1 /* AIF1ADC_COMP */ +#define WM8995_AIF1ADC_COMPMODE 0x0002 /* AIF1ADC_COMPMODE */ +#define WM8995_AIF1ADC_COMPMODE_MASK 0x0002 /* AIF1ADC_COMPMODE */ +#define WM8995_AIF1ADC_COMPMODE_SHIFT 1 /* AIF1ADC_COMPMODE */ +#define WM8995_AIF1ADC_COMPMODE_WIDTH 1 /* AIF1ADC_COMPMODE */ +#define WM8995_AIF1_LOOPBACK 0x0001 /* AIF1_LOOPBACK */ +#define WM8995_AIF1_LOOPBACK_MASK 0x0001 /* AIF1_LOOPBACK */ +#define WM8995_AIF1_LOOPBACK_SHIFT 0 /* AIF1_LOOPBACK */ +#define WM8995_AIF1_LOOPBACK_WIDTH 1 /* AIF1_LOOPBACK */ + +/* + * R770 (0x302) - AIF1 Master/Slave + */ +#define WM8995_AIF1_TRI 0x8000 /* AIF1_TRI */ +#define WM8995_AIF1_TRI_MASK 0x8000 /* AIF1_TRI */ +#define WM8995_AIF1_TRI_SHIFT 15 /* AIF1_TRI */ +#define WM8995_AIF1_TRI_WIDTH 1 /* AIF1_TRI */ +#define WM8995_AIF1_MSTR 0x4000 /* AIF1_MSTR */ +#define WM8995_AIF1_MSTR_MASK 0x4000 /* AIF1_MSTR */ +#define WM8995_AIF1_MSTR_SHIFT 14 /* AIF1_MSTR */ +#define WM8995_AIF1_MSTR_WIDTH 1 /* AIF1_MSTR */ +#define WM8995_AIF1_CLK_FRC 0x2000 /* AIF1_CLK_FRC */ +#define WM8995_AIF1_CLK_FRC_MASK 0x2000 /* AIF1_CLK_FRC */ +#define WM8995_AIF1_CLK_FRC_SHIFT 13 /* AIF1_CLK_FRC */ +#define WM8995_AIF1_CLK_FRC_WIDTH 1 /* AIF1_CLK_FRC */ +#define WM8995_AIF1_LRCLK_FRC 0x1000 /* AIF1_LRCLK_FRC */ +#define WM8995_AIF1_LRCLK_FRC_MASK 0x1000 /* AIF1_LRCLK_FRC */ +#define WM8995_AIF1_LRCLK_FRC_SHIFT 12 /* AIF1_LRCLK_FRC */ +#define WM8995_AIF1_LRCLK_FRC_WIDTH 1 /* AIF1_LRCLK_FRC */ + +/* + * R771 (0x303) - AIF1 BCLK + */ +#define WM8995_AIF1_BCLK_DIV_MASK 0x00F0 /* AIF1_BCLK_DIV - [7:4] */ +#define WM8995_AIF1_BCLK_DIV_SHIFT 4 /* AIF1_BCLK_DIV - [7:4] */ +#define WM8995_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [7:4] */ + +/* + * R772 (0x304) - AIF1ADC LRCLK + */ +#define WM8995_AIF1ADC_LRCLK_DIR 0x0800 /* AIF1ADC_LRCLK_DIR */ +#define WM8995_AIF1ADC_LRCLK_DIR_MASK 0x0800 /* AIF1ADC_LRCLK_DIR */ +#define WM8995_AIF1ADC_LRCLK_DIR_SHIFT 11 /* AIF1ADC_LRCLK_DIR */ +#define WM8995_AIF1ADC_LRCLK_DIR_WIDTH 1 /* AIF1ADC_LRCLK_DIR */ +#define WM8995_AIF1ADC_RATE_MASK 0x07FF /* AIF1ADC_RATE - [10:0] */ +#define WM8995_AIF1ADC_RATE_SHIFT 0 /* AIF1ADC_RATE - [10:0] */ +#define WM8995_AIF1ADC_RATE_WIDTH 11 /* AIF1ADC_RATE - [10:0] */ + +/* + * R773 (0x305) - AIF1DAC LRCLK + */ +#define WM8995_AIF1DAC_LRCLK_DIR 0x0800 /* AIF1DAC_LRCLK_DIR */ +#define WM8995_AIF1DAC_LRCLK_DIR_MASK 0x0800 /* AIF1DAC_LRCLK_DIR */ +#define WM8995_AIF1DAC_LRCLK_DIR_SHIFT 11 /* AIF1DAC_LRCLK_DIR */ +#define WM8995_AIF1DAC_LRCLK_DIR_WIDTH 1 /* AIF1DAC_LRCLK_DIR */ +#define WM8995_AIF1DAC_RATE_MASK 0x07FF /* AIF1DAC_RATE - [10:0] */ +#define WM8995_AIF1DAC_RATE_SHIFT 0 /* AIF1DAC_RATE - [10:0] */ +#define WM8995_AIF1DAC_RATE_WIDTH 11 /* AIF1DAC_RATE - [10:0] */ + +/* + * R774 (0x306) - AIF1DAC Data + */ +#define WM8995_AIF1DACL_DAT_INV 0x0002 /* AIF1DACL_DAT_INV */ +#define WM8995_AIF1DACL_DAT_INV_MASK 0x0002 /* AIF1DACL_DAT_INV */ +#define WM8995_AIF1DACL_DAT_INV_SHIFT 1 /* AIF1DACL_DAT_INV */ +#define WM8995_AIF1DACL_DAT_INV_WIDTH 1 /* AIF1DACL_DAT_INV */ +#define WM8995_AIF1DACR_DAT_INV 0x0001 /* AIF1DACR_DAT_INV */ +#define WM8995_AIF1DACR_DAT_INV_MASK 0x0001 /* AIF1DACR_DAT_INV */ +#define WM8995_AIF1DACR_DAT_INV_SHIFT 0 /* AIF1DACR_DAT_INV */ +#define WM8995_AIF1DACR_DAT_INV_WIDTH 1 /* AIF1DACR_DAT_INV */ + +/* + * R775 (0x307) - AIF1ADC Data + */ +#define WM8995_AIF1ADCL_DAT_INV 0x0002 /* AIF1ADCL_DAT_INV */ +#define WM8995_AIF1ADCL_DAT_INV_MASK 0x0002 /* AIF1ADCL_DAT_INV */ +#define WM8995_AIF1ADCL_DAT_INV_SHIFT 1 /* AIF1ADCL_DAT_INV */ +#define WM8995_AIF1ADCL_DAT_INV_WIDTH 1 /* AIF1ADCL_DAT_INV */ +#define WM8995_AIF1ADCR_DAT_INV 0x0001 /* AIF1ADCR_DAT_INV */ +#define WM8995_AIF1ADCR_DAT_INV_MASK 0x0001 /* AIF1ADCR_DAT_INV */ +#define WM8995_AIF1ADCR_DAT_INV_SHIFT 0 /* AIF1ADCR_DAT_INV */ +#define WM8995_AIF1ADCR_DAT_INV_WIDTH 1 /* AIF1ADCR_DAT_INV */ + +/* + * R784 (0x310) - AIF2 Control (1) + */ +#define WM8995_AIF2ADCL_SRC 0x8000 /* AIF2ADCL_SRC */ +#define WM8995_AIF2ADCL_SRC_MASK 0x8000 /* AIF2ADCL_SRC */ +#define WM8995_AIF2ADCL_SRC_SHIFT 15 /* AIF2ADCL_SRC */ +#define WM8995_AIF2ADCL_SRC_WIDTH 1 /* AIF2ADCL_SRC */ +#define WM8995_AIF2ADCR_SRC 0x4000 /* AIF2ADCR_SRC */ +#define WM8995_AIF2ADCR_SRC_MASK 0x4000 /* AIF2ADCR_SRC */ +#define WM8995_AIF2ADCR_SRC_SHIFT 14 /* AIF2ADCR_SRC */ +#define WM8995_AIF2ADCR_SRC_WIDTH 1 /* AIF2ADCR_SRC */ +#define WM8995_AIF2ADC_TDM 0x2000 /* AIF2ADC_TDM */ +#define WM8995_AIF2ADC_TDM_MASK 0x2000 /* AIF2ADC_TDM */ +#define WM8995_AIF2ADC_TDM_SHIFT 13 /* AIF2ADC_TDM */ +#define WM8995_AIF2ADC_TDM_WIDTH 1 /* AIF2ADC_TDM */ +#define WM8995_AIF2ADC_TDM_CHAN 0x1000 /* AIF2ADC_TDM_CHAN */ +#define WM8995_AIF2ADC_TDM_CHAN_MASK 0x1000 /* AIF2ADC_TDM_CHAN */ +#define WM8995_AIF2ADC_TDM_CHAN_SHIFT 12 /* AIF2ADC_TDM_CHAN */ +#define WM8995_AIF2ADC_TDM_CHAN_WIDTH 1 /* AIF2ADC_TDM_CHAN */ +#define WM8995_AIF2_BCLK_INV 0x0100 /* AIF2_BCLK_INV */ +#define WM8995_AIF2_BCLK_INV_MASK 0x0100 /* AIF2_BCLK_INV */ +#define WM8995_AIF2_BCLK_INV_SHIFT 8 /* AIF2_BCLK_INV */ +#define WM8995_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */ +#define WM8995_AIF2_LRCLK_INV 0x0080 /* AIF2_LRCLK_INV */ +#define WM8995_AIF2_LRCLK_INV_MASK 0x0080 /* AIF2_LRCLK_INV */ +#define WM8995_AIF2_LRCLK_INV_SHIFT 7 /* AIF2_LRCLK_INV */ +#define WM8995_AIF2_LRCLK_INV_WIDTH 1 /* AIF2_LRCLK_INV */ +#define WM8995_AIF2_WL_MASK 0x0060 /* AIF2_WL - [6:5] */ +#define WM8995_AIF2_WL_SHIFT 5 /* AIF2_WL - [6:5] */ +#define WM8995_AIF2_WL_WIDTH 2 /* AIF2_WL - [6:5] */ +#define WM8995_AIF2_FMT_MASK 0x0018 /* AIF2_FMT - [4:3] */ +#define WM8995_AIF2_FMT_SHIFT 3 /* AIF2_FMT - [4:3] */ +#define WM8995_AIF2_FMT_WIDTH 2 /* AIF2_FMT - [4:3] */ + +/* + * R785 (0x311) - AIF2 Control (2) + */ +#define WM8995_AIF2DACL_SRC 0x8000 /* AIF2DACL_SRC */ +#define WM8995_AIF2DACL_SRC_MASK 0x8000 /* AIF2DACL_SRC */ +#define WM8995_AIF2DACL_SRC_SHIFT 15 /* AIF2DACL_SRC */ +#define WM8995_AIF2DACL_SRC_WIDTH 1 /* AIF2DACL_SRC */ +#define WM8995_AIF2DACR_SRC 0x4000 /* AIF2DACR_SRC */ +#define WM8995_AIF2DACR_SRC_MASK 0x4000 /* AIF2DACR_SRC */ +#define WM8995_AIF2DACR_SRC_SHIFT 14 /* AIF2DACR_SRC */ +#define WM8995_AIF2DACR_SRC_WIDTH 1 /* AIF2DACR_SRC */ +#define WM8995_AIF2DAC_TDM 0x2000 /* AIF2DAC_TDM */ +#define WM8995_AIF2DAC_TDM_MASK 0x2000 /* AIF2DAC_TDM */ +#define WM8995_AIF2DAC_TDM_SHIFT 13 /* AIF2DAC_TDM */ +#define WM8995_AIF2DAC_TDM_WIDTH 1 /* AIF2DAC_TDM */ +#define WM8995_AIF2DAC_TDM_CHAN 0x1000 /* AIF2DAC_TDM_CHAN */ +#define WM8995_AIF2DAC_TDM_CHAN_MASK 0x1000 /* AIF2DAC_TDM_CHAN */ +#define WM8995_AIF2DAC_TDM_CHAN_SHIFT 12 /* AIF2DAC_TDM_CHAN */ +#define WM8995_AIF2DAC_TDM_CHAN_WIDTH 1 /* AIF2DAC_TDM_CHAN */ +#define WM8995_AIF2DAC_BOOST_MASK 0x0C00 /* AIF2DAC_BOOST - [11:10] */ +#define WM8995_AIF2DAC_BOOST_SHIFT 10 /* AIF2DAC_BOOST - [11:10] */ +#define WM8995_AIF2DAC_BOOST_WIDTH 2 /* AIF2DAC_BOOST - [11:10] */ +#define WM8995_AIF2DAC_COMP 0x0010 /* AIF2DAC_COMP */ +#define WM8995_AIF2DAC_COMP_MASK 0x0010 /* AIF2DAC_COMP */ +#define WM8995_AIF2DAC_COMP_SHIFT 4 /* AIF2DAC_COMP */ +#define WM8995_AIF2DAC_COMP_WIDTH 1 /* AIF2DAC_COMP */ +#define WM8995_AIF2DAC_COMPMODE 0x0008 /* AIF2DAC_COMPMODE */ +#define WM8995_AIF2DAC_COMPMODE_MASK 0x0008 /* AIF2DAC_COMPMODE */ +#define WM8995_AIF2DAC_COMPMODE_SHIFT 3 /* AIF2DAC_COMPMODE */ +#define WM8995_AIF2DAC_COMPMODE_WIDTH 1 /* AIF2DAC_COMPMODE */ +#define WM8995_AIF2ADC_COMP 0x0004 /* AIF2ADC_COMP */ +#define WM8995_AIF2ADC_COMP_MASK 0x0004 /* AIF2ADC_COMP */ +#define WM8995_AIF2ADC_COMP_SHIFT 2 /* AIF2ADC_COMP */ +#define WM8995_AIF2ADC_COMP_WIDTH 1 /* AIF2ADC_COMP */ +#define WM8995_AIF2ADC_COMPMODE 0x0002 /* AIF2ADC_COMPMODE */ +#define WM8995_AIF2ADC_COMPMODE_MASK 0x0002 /* AIF2ADC_COMPMODE */ +#define WM8995_AIF2ADC_COMPMODE_SHIFT 1 /* AIF2ADC_COMPMODE */ +#define WM8995_AIF2ADC_COMPMODE_WIDTH 1 /* AIF2ADC_COMPMODE */ +#define WM8995_AIF2_LOOPBACK 0x0001 /* AIF2_LOOPBACK */ +#define WM8995_AIF2_LOOPBACK_MASK 0x0001 /* AIF2_LOOPBACK */ +#define WM8995_AIF2_LOOPBACK_SHIFT 0 /* AIF2_LOOPBACK */ +#define WM8995_AIF2_LOOPBACK_WIDTH 1 /* AIF2_LOOPBACK */ + +/* + * R786 (0x312) - AIF2 Master/Slave + */ +#define WM8995_AIF2_TRI 0x8000 /* AIF2_TRI */ +#define WM8995_AIF2_TRI_MASK 0x8000 /* AIF2_TRI */ +#define WM8995_AIF2_TRI_SHIFT 15 /* AIF2_TRI */ +#define WM8995_AIF2_TRI_WIDTH 1 /* AIF2_TRI */ +#define WM8995_AIF2_MSTR 0x4000 /* AIF2_MSTR */ +#define WM8995_AIF2_MSTR_MASK 0x4000 /* AIF2_MSTR */ +#define WM8995_AIF2_MSTR_SHIFT 14 /* AIF2_MSTR */ +#define WM8995_AIF2_MSTR_WIDTH 1 /* AIF2_MSTR */ +#define WM8995_AIF2_CLK_FRC 0x2000 /* AIF2_CLK_FRC */ +#define WM8995_AIF2_CLK_FRC_MASK 0x2000 /* AIF2_CLK_FRC */ +#define WM8995_AIF2_CLK_FRC_SHIFT 13 /* AIF2_CLK_FRC */ +#define WM8995_AIF2_CLK_FRC_WIDTH 1 /* AIF2_CLK_FRC */ +#define WM8995_AIF2_LRCLK_FRC 0x1000 /* AIF2_LRCLK_FRC */ +#define WM8995_AIF2_LRCLK_FRC_MASK 0x1000 /* AIF2_LRCLK_FRC */ +#define WM8995_AIF2_LRCLK_FRC_SHIFT 12 /* AIF2_LRCLK_FRC */ +#define WM8995_AIF2_LRCLK_FRC_WIDTH 1 /* AIF2_LRCLK_FRC */ + +/* + * R787 (0x313) - AIF2 BCLK + */ +#define WM8995_AIF2_BCLK_DIV_MASK 0x00F0 /* AIF2_BCLK_DIV - [7:4] */ +#define WM8995_AIF2_BCLK_DIV_SHIFT 4 /* AIF2_BCLK_DIV - [7:4] */ +#define WM8995_AIF2_BCLK_DIV_WIDTH 4 /* AIF2_BCLK_DIV - [7:4] */ + +/* + * R788 (0x314) - AIF2ADC LRCLK + */ +#define WM8995_AIF2ADC_LRCLK_DIR 0x0800 /* AIF2ADC_LRCLK_DIR */ +#define WM8995_AIF2ADC_LRCLK_DIR_MASK 0x0800 /* AIF2ADC_LRCLK_DIR */ +#define WM8995_AIF2ADC_LRCLK_DIR_SHIFT 11 /* AIF2ADC_LRCLK_DIR */ +#define WM8995_AIF2ADC_LRCLK_DIR_WIDTH 1 /* AIF2ADC_LRCLK_DIR */ +#define WM8995_AIF2ADC_RATE_MASK 0x07FF /* AIF2ADC_RATE - [10:0] */ +#define WM8995_AIF2ADC_RATE_SHIFT 0 /* AIF2ADC_RATE - [10:0] */ +#define WM8995_AIF2ADC_RATE_WIDTH 11 /* AIF2ADC_RATE - [10:0] */ + +/* + * R789 (0x315) - AIF2DAC LRCLK + */ +#define WM8995_AIF2DAC_LRCLK_DIR 0x0800 /* AIF2DAC_LRCLK_DIR */ +#define WM8995_AIF2DAC_LRCLK_DIR_MASK 0x0800 /* AIF2DAC_LRCLK_DIR */ +#define WM8995_AIF2DAC_LRCLK_DIR_SHIFT 11 /* AIF2DAC_LRCLK_DIR */ +#define WM8995_AIF2DAC_LRCLK_DIR_WIDTH 1 /* AIF2DAC_LRCLK_DIR */ +#define WM8995_AIF2DAC_RATE_MASK 0x07FF /* AIF2DAC_RATE - [10:0] */ +#define WM8995_AIF2DAC_RATE_SHIFT 0 /* AIF2DAC_RATE - [10:0] */ +#define WM8995_AIF2DAC_RATE_WIDTH 11 /* AIF2DAC_RATE - [10:0] */ + +/* + * R790 (0x316) - AIF2DAC Data + */ +#define WM8995_AIF2DACL_DAT_INV 0x0002 /* AIF2DACL_DAT_INV */ +#define WM8995_AIF2DACL_DAT_INV_MASK 0x0002 /* AIF2DACL_DAT_INV */ +#define WM8995_AIF2DACL_DAT_INV_SHIFT 1 /* AIF2DACL_DAT_INV */ +#define WM8995_AIF2DACL_DAT_INV_WIDTH 1 /* AIF2DACL_DAT_INV */ +#define WM8995_AIF2DACR_DAT_INV 0x0001 /* AIF2DACR_DAT_INV */ +#define WM8995_AIF2DACR_DAT_INV_MASK 0x0001 /* AIF2DACR_DAT_INV */ +#define WM8995_AIF2DACR_DAT_INV_SHIFT 0 /* AIF2DACR_DAT_INV */ +#define WM8995_AIF2DACR_DAT_INV_WIDTH 1 /* AIF2DACR_DAT_INV */ + +/* + * R791 (0x317) - AIF2ADC Data + */ +#define WM8995_AIF2ADCL_DAT_INV 0x0002 /* AIF2ADCL_DAT_INV */ +#define WM8995_AIF2ADCL_DAT_INV_MASK 0x0002 /* AIF2ADCL_DAT_INV */ +#define WM8995_AIF2ADCL_DAT_INV_SHIFT 1 /* AIF2ADCL_DAT_INV */ +#define WM8995_AIF2ADCL_DAT_INV_WIDTH 1 /* AIF2ADCL_DAT_INV */ +#define WM8995_AIF2ADCR_DAT_INV 0x0001 /* AIF2ADCR_DAT_INV */ +#define WM8995_AIF2ADCR_DAT_INV_MASK 0x0001 /* AIF2ADCR_DAT_INV */ +#define WM8995_AIF2ADCR_DAT_INV_SHIFT 0 /* AIF2ADCR_DAT_INV */ +#define WM8995_AIF2ADCR_DAT_INV_WIDTH 1 /* AIF2ADCR_DAT_INV */ + +/* + * R1024 (0x400) - AIF1 ADC1 Left Volume + */ +#define WM8995_AIF1ADC1_VU 0x0100 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_MASK 0x0100 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_SHIFT 8 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_WIDTH 1 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1L_VOL_MASK 0x00FF /* AIF1ADC1L_VOL - [7:0] */ +#define WM8995_AIF1ADC1L_VOL_SHIFT 0 /* AIF1ADC1L_VOL - [7:0] */ +#define WM8995_AIF1ADC1L_VOL_WIDTH 8 /* AIF1ADC1L_VOL - [7:0] */ + +/* + * R1025 (0x401) - AIF1 ADC1 Right Volume + */ +#define WM8995_AIF1ADC1_VU 0x0100 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_MASK 0x0100 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_SHIFT 8 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1_VU_WIDTH 1 /* AIF1ADC1_VU */ +#define WM8995_AIF1ADC1R_VOL_MASK 0x00FF /* AIF1ADC1R_VOL - [7:0] */ +#define WM8995_AIF1ADC1R_VOL_SHIFT 0 /* AIF1ADC1R_VOL - [7:0] */ +#define WM8995_AIF1ADC1R_VOL_WIDTH 8 /* AIF1ADC1R_VOL - [7:0] */ + +/* + * R1026 (0x402) - AIF1 DAC1 Left Volume + */ +#define WM8995_AIF1DAC1_VU 0x0100 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_MASK 0x0100 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_SHIFT 8 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_WIDTH 1 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1L_VOL_MASK 0x00FF /* AIF1DAC1L_VOL - [7:0] */ +#define WM8995_AIF1DAC1L_VOL_SHIFT 0 /* AIF1DAC1L_VOL - [7:0] */ +#define WM8995_AIF1DAC1L_VOL_WIDTH 8 /* AIF1DAC1L_VOL - [7:0] */ + +/* + * R1027 (0x403) - AIF1 DAC1 Right Volume + */ +#define WM8995_AIF1DAC1_VU 0x0100 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_MASK 0x0100 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_SHIFT 8 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1_VU_WIDTH 1 /* AIF1DAC1_VU */ +#define WM8995_AIF1DAC1R_VOL_MASK 0x00FF /* AIF1DAC1R_VOL - [7:0] */ +#define WM8995_AIF1DAC1R_VOL_SHIFT 0 /* AIF1DAC1R_VOL - [7:0] */ +#define WM8995_AIF1DAC1R_VOL_WIDTH 8 /* AIF1DAC1R_VOL - [7:0] */ + +/* + * R1028 (0x404) - AIF1 ADC2 Left Volume + */ +#define WM8995_AIF1ADC2_VU 0x0100 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_MASK 0x0100 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_SHIFT 8 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_WIDTH 1 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2L_VOL_MASK 0x00FF /* AIF1ADC2L_VOL - [7:0] */ +#define WM8995_AIF1ADC2L_VOL_SHIFT 0 /* AIF1ADC2L_VOL - [7:0] */ +#define WM8995_AIF1ADC2L_VOL_WIDTH 8 /* AIF1ADC2L_VOL - [7:0] */ + +/* + * R1029 (0x405) - AIF1 ADC2 Right Volume + */ +#define WM8995_AIF1ADC2_VU 0x0100 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_MASK 0x0100 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_SHIFT 8 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2_VU_WIDTH 1 /* AIF1ADC2_VU */ +#define WM8995_AIF1ADC2R_VOL_MASK 0x00FF /* AIF1ADC2R_VOL - [7:0] */ +#define WM8995_AIF1ADC2R_VOL_SHIFT 0 /* AIF1ADC2R_VOL - [7:0] */ +#define WM8995_AIF1ADC2R_VOL_WIDTH 8 /* AIF1ADC2R_VOL - [7:0] */ + +/* + * R1030 (0x406) - AIF1 DAC2 Left Volume + */ +#define WM8995_AIF1DAC2_VU 0x0100 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_MASK 0x0100 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_SHIFT 8 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_WIDTH 1 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2L_VOL_MASK 0x00FF /* AIF1DAC2L_VOL - [7:0] */ +#define WM8995_AIF1DAC2L_VOL_SHIFT 0 /* AIF1DAC2L_VOL - [7:0] */ +#define WM8995_AIF1DAC2L_VOL_WIDTH 8 /* AIF1DAC2L_VOL - [7:0] */ + +/* + * R1031 (0x407) - AIF1 DAC2 Right Volume + */ +#define WM8995_AIF1DAC2_VU 0x0100 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_MASK 0x0100 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_SHIFT 8 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2_VU_WIDTH 1 /* AIF1DAC2_VU */ +#define WM8995_AIF1DAC2R_VOL_MASK 0x00FF /* AIF1DAC2R_VOL - [7:0] */ +#define WM8995_AIF1DAC2R_VOL_SHIFT 0 /* AIF1DAC2R_VOL - [7:0] */ +#define WM8995_AIF1DAC2R_VOL_WIDTH 8 /* AIF1DAC2R_VOL - [7:0] */ + +/* + * R1040 (0x410) - AIF1 ADC1 Filters + */ +#define WM8995_AIF1ADC_4FS 0x8000 /* AIF1ADC_4FS */ +#define WM8995_AIF1ADC_4FS_MASK 0x8000 /* AIF1ADC_4FS */ +#define WM8995_AIF1ADC_4FS_SHIFT 15 /* AIF1ADC_4FS */ +#define WM8995_AIF1ADC_4FS_WIDTH 1 /* AIF1ADC_4FS */ +#define WM8995_AIF1ADC1L_HPF 0x1000 /* AIF1ADC1L_HPF */ +#define WM8995_AIF1ADC1L_HPF_MASK 0x1000 /* AIF1ADC1L_HPF */ +#define WM8995_AIF1ADC1L_HPF_SHIFT 12 /* AIF1ADC1L_HPF */ +#define WM8995_AIF1ADC1L_HPF_WIDTH 1 /* AIF1ADC1L_HPF */ +#define WM8995_AIF1ADC1R_HPF 0x0800 /* AIF1ADC1R_HPF */ +#define WM8995_AIF1ADC1R_HPF_MASK 0x0800 /* AIF1ADC1R_HPF */ +#define WM8995_AIF1ADC1R_HPF_SHIFT 11 /* AIF1ADC1R_HPF */ +#define WM8995_AIF1ADC1R_HPF_WIDTH 1 /* AIF1ADC1R_HPF */ +#define WM8995_AIF1ADC1_HPF_MODE 0x0008 /* AIF1ADC1_HPF_MODE */ +#define WM8995_AIF1ADC1_HPF_MODE_MASK 0x0008 /* AIF1ADC1_HPF_MODE */ +#define WM8995_AIF1ADC1_HPF_MODE_SHIFT 3 /* AIF1ADC1_HPF_MODE */ +#define WM8995_AIF1ADC1_HPF_MODE_WIDTH 1 /* AIF1ADC1_HPF_MODE */ +#define WM8995_AIF1ADC1_HPF_CUT_MASK 0x0007 /* AIF1ADC1_HPF_CUT - [2:0] */ +#define WM8995_AIF1ADC1_HPF_CUT_SHIFT 0 /* AIF1ADC1_HPF_CUT - [2:0] */ +#define WM8995_AIF1ADC1_HPF_CUT_WIDTH 3 /* AIF1ADC1_HPF_CUT - [2:0] */ + +/* + * R1041 (0x411) - AIF1 ADC2 Filters + */ +#define WM8995_AIF1ADC2L_HPF 0x1000 /* AIF1ADC2L_HPF */ +#define WM8995_AIF1ADC2L_HPF_MASK 0x1000 /* AIF1ADC2L_HPF */ +#define WM8995_AIF1ADC2L_HPF_SHIFT 12 /* AIF1ADC2L_HPF */ +#define WM8995_AIF1ADC2L_HPF_WIDTH 1 /* AIF1ADC2L_HPF */ +#define WM8995_AIF1ADC2R_HPF 0x0800 /* AIF1ADC2R_HPF */ +#define WM8995_AIF1ADC2R_HPF_MASK 0x0800 /* AIF1ADC2R_HPF */ +#define WM8995_AIF1ADC2R_HPF_SHIFT 11 /* AIF1ADC2R_HPF */ +#define WM8995_AIF1ADC2R_HPF_WIDTH 1 /* AIF1ADC2R_HPF */ +#define WM8995_AIF1ADC2_HPF_MODE 0x0008 /* AIF1ADC2_HPF_MODE */ +#define WM8995_AIF1ADC2_HPF_MODE_MASK 0x0008 /* AIF1ADC2_HPF_MODE */ +#define WM8995_AIF1ADC2_HPF_MODE_SHIFT 3 /* AIF1ADC2_HPF_MODE */ +#define WM8995_AIF1ADC2_HPF_MODE_WIDTH 1 /* AIF1ADC2_HPF_MODE */ +#define WM8995_AIF1ADC2_HPF_CUT_MASK 0x0007 /* AIF1ADC2_HPF_CUT - [2:0] */ +#define WM8995_AIF1ADC2_HPF_CUT_SHIFT 0 /* AIF1ADC2_HPF_CUT - [2:0] */ +#define WM8995_AIF1ADC2_HPF_CUT_WIDTH 3 /* AIF1ADC2_HPF_CUT - [2:0] */ + +/* + * R1056 (0x420) - AIF1 DAC1 Filters (1) + */ +#define WM8995_AIF1DAC1_MUTE 0x0200 /* AIF1DAC1_MUTE */ +#define WM8995_AIF1DAC1_MUTE_MASK 0x0200 /* AIF1DAC1_MUTE */ +#define WM8995_AIF1DAC1_MUTE_SHIFT 9 /* AIF1DAC1_MUTE */ +#define WM8995_AIF1DAC1_MUTE_WIDTH 1 /* AIF1DAC1_MUTE */ +#define WM8995_AIF1DAC1_MONO 0x0080 /* AIF1DAC1_MONO */ +#define WM8995_AIF1DAC1_MONO_MASK 0x0080 /* AIF1DAC1_MONO */ +#define WM8995_AIF1DAC1_MONO_SHIFT 7 /* AIF1DAC1_MONO */ +#define WM8995_AIF1DAC1_MONO_WIDTH 1 /* AIF1DAC1_MONO */ +#define WM8995_AIF1DAC1_MUTERATE 0x0020 /* AIF1DAC1_MUTERATE */ +#define WM8995_AIF1DAC1_MUTERATE_MASK 0x0020 /* AIF1DAC1_MUTERATE */ +#define WM8995_AIF1DAC1_MUTERATE_SHIFT 5 /* AIF1DAC1_MUTERATE */ +#define WM8995_AIF1DAC1_MUTERATE_WIDTH 1 /* AIF1DAC1_MUTERATE */ +#define WM8995_AIF1DAC1_UNMUTE_RAMP 0x0010 /* AIF1DAC1_UNMUTE_RAMP */ +#define WM8995_AIF1DAC1_UNMUTE_RAMP_MASK 0x0010 /* AIF1DAC1_UNMUTE_RAMP */ +#define WM8995_AIF1DAC1_UNMUTE_RAMP_SHIFT 4 /* AIF1DAC1_UNMUTE_RAMP */ +#define WM8995_AIF1DAC1_UNMUTE_RAMP_WIDTH 1 /* AIF1DAC1_UNMUTE_RAMP */ +#define WM8995_AIF1DAC1_DEEMP_MASK 0x0006 /* AIF1DAC1_DEEMP - [2:1] */ +#define WM8995_AIF1DAC1_DEEMP_SHIFT 1 /* AIF1DAC1_DEEMP - [2:1] */ +#define WM8995_AIF1DAC1_DEEMP_WIDTH 2 /* AIF1DAC1_DEEMP - [2:1] */ + +/* + * R1057 (0x421) - AIF1 DAC1 Filters (2) + */ +#define WM8995_AIF1DAC1_3D_GAIN_MASK 0x3E00 /* AIF1DAC1_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC1_3D_GAIN_SHIFT 9 /* AIF1DAC1_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC1_3D_GAIN_WIDTH 5 /* AIF1DAC1_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC1_3D_ENA 0x0100 /* AIF1DAC1_3D_ENA */ +#define WM8995_AIF1DAC1_3D_ENA_MASK 0x0100 /* AIF1DAC1_3D_ENA */ +#define WM8995_AIF1DAC1_3D_ENA_SHIFT 8 /* AIF1DAC1_3D_ENA */ +#define WM8995_AIF1DAC1_3D_ENA_WIDTH 1 /* AIF1DAC1_3D_ENA */ + +/* + * R1058 (0x422) - AIF1 DAC2 Filters (1) + */ +#define WM8995_AIF1DAC2_MUTE 0x0200 /* AIF1DAC2_MUTE */ +#define WM8995_AIF1DAC2_MUTE_MASK 0x0200 /* AIF1DAC2_MUTE */ +#define WM8995_AIF1DAC2_MUTE_SHIFT 9 /* AIF1DAC2_MUTE */ +#define WM8995_AIF1DAC2_MUTE_WIDTH 1 /* AIF1DAC2_MUTE */ +#define WM8995_AIF1DAC2_MONO 0x0080 /* AIF1DAC2_MONO */ +#define WM8995_AIF1DAC2_MONO_MASK 0x0080 /* AIF1DAC2_MONO */ +#define WM8995_AIF1DAC2_MONO_SHIFT 7 /* AIF1DAC2_MONO */ +#define WM8995_AIF1DAC2_MONO_WIDTH 1 /* AIF1DAC2_MONO */ +#define WM8995_AIF1DAC2_MUTERATE 0x0020 /* AIF1DAC2_MUTERATE */ +#define WM8995_AIF1DAC2_MUTERATE_MASK 0x0020 /* AIF1DAC2_MUTERATE */ +#define WM8995_AIF1DAC2_MUTERATE_SHIFT 5 /* AIF1DAC2_MUTERATE */ +#define WM8995_AIF1DAC2_MUTERATE_WIDTH 1 /* AIF1DAC2_MUTERATE */ +#define WM8995_AIF1DAC2_UNMUTE_RAMP 0x0010 /* AIF1DAC2_UNMUTE_RAMP */ +#define WM8995_AIF1DAC2_UNMUTE_RAMP_MASK 0x0010 /* AIF1DAC2_UNMUTE_RAMP */ +#define WM8995_AIF1DAC2_UNMUTE_RAMP_SHIFT 4 /* AIF1DAC2_UNMUTE_RAMP */ +#define WM8995_AIF1DAC2_UNMUTE_RAMP_WIDTH 1 /* AIF1DAC2_UNMUTE_RAMP */ +#define WM8995_AIF1DAC2_DEEMP_MASK 0x0006 /* AIF1DAC2_DEEMP - [2:1] */ +#define WM8995_AIF1DAC2_DEEMP_SHIFT 1 /* AIF1DAC2_DEEMP - [2:1] */ +#define WM8995_AIF1DAC2_DEEMP_WIDTH 2 /* AIF1DAC2_DEEMP - [2:1] */ + +/* + * R1059 (0x423) - AIF1 DAC2 Filters (2) + */ +#define WM8995_AIF1DAC2_3D_GAIN_MASK 0x3E00 /* AIF1DAC2_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC2_3D_GAIN_SHIFT 9 /* AIF1DAC2_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC2_3D_GAIN_WIDTH 5 /* AIF1DAC2_3D_GAIN - [13:9] */ +#define WM8995_AIF1DAC2_3D_ENA 0x0100 /* AIF1DAC2_3D_ENA */ +#define WM8995_AIF1DAC2_3D_ENA_MASK 0x0100 /* AIF1DAC2_3D_ENA */ +#define WM8995_AIF1DAC2_3D_ENA_SHIFT 8 /* AIF1DAC2_3D_ENA */ +#define WM8995_AIF1DAC2_3D_ENA_WIDTH 1 /* AIF1DAC2_3D_ENA */ + +/* + * R1088 (0x440) - AIF1 DRC1 (1) + */ +#define WM8995_AIF1DRC1_SIG_DET_RMS_MASK 0xF800 /* AIF1DRC1_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC1_SIG_DET_RMS_SHIFT 11 /* AIF1DRC1_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC1_SIG_DET_RMS_WIDTH 5 /* AIF1DRC1_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC1_SIG_DET_PK_MASK 0x0600 /* AIF1DRC1_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC1_SIG_DET_PK_SHIFT 9 /* AIF1DRC1_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC1_SIG_DET_PK_WIDTH 2 /* AIF1DRC1_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC1_NG_ENA 0x0100 /* AIF1DRC1_NG_ENA */ +#define WM8995_AIF1DRC1_NG_ENA_MASK 0x0100 /* AIF1DRC1_NG_ENA */ +#define WM8995_AIF1DRC1_NG_ENA_SHIFT 8 /* AIF1DRC1_NG_ENA */ +#define WM8995_AIF1DRC1_NG_ENA_WIDTH 1 /* AIF1DRC1_NG_ENA */ +#define WM8995_AIF1DRC1_SIG_DET_MODE 0x0080 /* AIF1DRC1_SIG_DET_MODE */ +#define WM8995_AIF1DRC1_SIG_DET_MODE_MASK 0x0080 /* AIF1DRC1_SIG_DET_MODE */ +#define WM8995_AIF1DRC1_SIG_DET_MODE_SHIFT 7 /* AIF1DRC1_SIG_DET_MODE */ +#define WM8995_AIF1DRC1_SIG_DET_MODE_WIDTH 1 /* AIF1DRC1_SIG_DET_MODE */ +#define WM8995_AIF1DRC1_SIG_DET 0x0040 /* AIF1DRC1_SIG_DET */ +#define WM8995_AIF1DRC1_SIG_DET_MASK 0x0040 /* AIF1DRC1_SIG_DET */ +#define WM8995_AIF1DRC1_SIG_DET_SHIFT 6 /* AIF1DRC1_SIG_DET */ +#define WM8995_AIF1DRC1_SIG_DET_WIDTH 1 /* AIF1DRC1_SIG_DET */ +#define WM8995_AIF1DRC1_KNEE2_OP_ENA 0x0020 /* AIF1DRC1_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC1_KNEE2_OP_ENA_MASK 0x0020 /* AIF1DRC1_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC1_KNEE2_OP_ENA_SHIFT 5 /* AIF1DRC1_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC1_KNEE2_OP_ENA_WIDTH 1 /* AIF1DRC1_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC1_QR 0x0010 /* AIF1DRC1_QR */ +#define WM8995_AIF1DRC1_QR_MASK 0x0010 /* AIF1DRC1_QR */ +#define WM8995_AIF1DRC1_QR_SHIFT 4 /* AIF1DRC1_QR */ +#define WM8995_AIF1DRC1_QR_WIDTH 1 /* AIF1DRC1_QR */ +#define WM8995_AIF1DRC1_ANTICLIP 0x0008 /* AIF1DRC1_ANTICLIP */ +#define WM8995_AIF1DRC1_ANTICLIP_MASK 0x0008 /* AIF1DRC1_ANTICLIP */ +#define WM8995_AIF1DRC1_ANTICLIP_SHIFT 3 /* AIF1DRC1_ANTICLIP */ +#define WM8995_AIF1DRC1_ANTICLIP_WIDTH 1 /* AIF1DRC1_ANTICLIP */ +#define WM8995_AIF1DAC1_DRC_ENA 0x0004 /* AIF1DAC1_DRC_ENA */ +#define WM8995_AIF1DAC1_DRC_ENA_MASK 0x0004 /* AIF1DAC1_DRC_ENA */ +#define WM8995_AIF1DAC1_DRC_ENA_SHIFT 2 /* AIF1DAC1_DRC_ENA */ +#define WM8995_AIF1DAC1_DRC_ENA_WIDTH 1 /* AIF1DAC1_DRC_ENA */ +#define WM8995_AIF1ADC1L_DRC_ENA 0x0002 /* AIF1ADC1L_DRC_ENA */ +#define WM8995_AIF1ADC1L_DRC_ENA_MASK 0x0002 /* AIF1ADC1L_DRC_ENA */ +#define WM8995_AIF1ADC1L_DRC_ENA_SHIFT 1 /* AIF1ADC1L_DRC_ENA */ +#define WM8995_AIF1ADC1L_DRC_ENA_WIDTH 1 /* AIF1ADC1L_DRC_ENA */ +#define WM8995_AIF1ADC1R_DRC_ENA 0x0001 /* AIF1ADC1R_DRC_ENA */ +#define WM8995_AIF1ADC1R_DRC_ENA_MASK 0x0001 /* AIF1ADC1R_DRC_ENA */ +#define WM8995_AIF1ADC1R_DRC_ENA_SHIFT 0 /* AIF1ADC1R_DRC_ENA */ +#define WM8995_AIF1ADC1R_DRC_ENA_WIDTH 1 /* AIF1ADC1R_DRC_ENA */ + +/* + * R1089 (0x441) - AIF1 DRC1 (2) + */ +#define WM8995_AIF1DRC1_ATK_MASK 0x1E00 /* AIF1DRC1_ATK - [12:9] */ +#define WM8995_AIF1DRC1_ATK_SHIFT 9 /* AIF1DRC1_ATK - [12:9] */ +#define WM8995_AIF1DRC1_ATK_WIDTH 4 /* AIF1DRC1_ATK - [12:9] */ +#define WM8995_AIF1DRC1_DCY_MASK 0x01E0 /* AIF1DRC1_DCY - [8:5] */ +#define WM8995_AIF1DRC1_DCY_SHIFT 5 /* AIF1DRC1_DCY - [8:5] */ +#define WM8995_AIF1DRC1_DCY_WIDTH 4 /* AIF1DRC1_DCY - [8:5] */ +#define WM8995_AIF1DRC1_MINGAIN_MASK 0x001C /* AIF1DRC1_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC1_MINGAIN_SHIFT 2 /* AIF1DRC1_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC1_MINGAIN_WIDTH 3 /* AIF1DRC1_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC1_MAXGAIN_MASK 0x0003 /* AIF1DRC1_MAXGAIN - [1:0] */ +#define WM8995_AIF1DRC1_MAXGAIN_SHIFT 0 /* AIF1DRC1_MAXGAIN - [1:0] */ +#define WM8995_AIF1DRC1_MAXGAIN_WIDTH 2 /* AIF1DRC1_MAXGAIN - [1:0] */ + +/* + * R1090 (0x442) - AIF1 DRC1 (3) + */ +#define WM8995_AIF1DRC1_NG_MINGAIN_MASK 0xF000 /* AIF1DRC1_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC1_NG_MINGAIN_SHIFT 12 /* AIF1DRC1_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC1_NG_MINGAIN_WIDTH 4 /* AIF1DRC1_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC1_NG_EXP_MASK 0x0C00 /* AIF1DRC1_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC1_NG_EXP_SHIFT 10 /* AIF1DRC1_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC1_NG_EXP_WIDTH 2 /* AIF1DRC1_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC1_QR_THR_MASK 0x0300 /* AIF1DRC1_QR_THR - [9:8] */ +#define WM8995_AIF1DRC1_QR_THR_SHIFT 8 /* AIF1DRC1_QR_THR - [9:8] */ +#define WM8995_AIF1DRC1_QR_THR_WIDTH 2 /* AIF1DRC1_QR_THR - [9:8] */ +#define WM8995_AIF1DRC1_QR_DCY_MASK 0x00C0 /* AIF1DRC1_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC1_QR_DCY_SHIFT 6 /* AIF1DRC1_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC1_QR_DCY_WIDTH 2 /* AIF1DRC1_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC1_HI_COMP_MASK 0x0038 /* AIF1DRC1_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC1_HI_COMP_SHIFT 3 /* AIF1DRC1_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC1_HI_COMP_WIDTH 3 /* AIF1DRC1_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC1_LO_COMP_MASK 0x0007 /* AIF1DRC1_LO_COMP - [2:0] */ +#define WM8995_AIF1DRC1_LO_COMP_SHIFT 0 /* AIF1DRC1_LO_COMP - [2:0] */ +#define WM8995_AIF1DRC1_LO_COMP_WIDTH 3 /* AIF1DRC1_LO_COMP - [2:0] */ + +/* + * R1091 (0x443) - AIF1 DRC1 (4) + */ +#define WM8995_AIF1DRC1_KNEE_IP_MASK 0x07E0 /* AIF1DRC1_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC1_KNEE_IP_SHIFT 5 /* AIF1DRC1_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC1_KNEE_IP_WIDTH 6 /* AIF1DRC1_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC1_KNEE_OP_MASK 0x001F /* AIF1DRC1_KNEE_OP - [4:0] */ +#define WM8995_AIF1DRC1_KNEE_OP_SHIFT 0 /* AIF1DRC1_KNEE_OP - [4:0] */ +#define WM8995_AIF1DRC1_KNEE_OP_WIDTH 5 /* AIF1DRC1_KNEE_OP - [4:0] */ + +/* + * R1092 (0x444) - AIF1 DRC1 (5) + */ +#define WM8995_AIF1DRC1_KNEE2_IP_MASK 0x03E0 /* AIF1DRC1_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC1_KNEE2_IP_SHIFT 5 /* AIF1DRC1_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC1_KNEE2_IP_WIDTH 5 /* AIF1DRC1_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC1_KNEE2_OP_MASK 0x001F /* AIF1DRC1_KNEE2_OP - [4:0] */ +#define WM8995_AIF1DRC1_KNEE2_OP_SHIFT 0 /* AIF1DRC1_KNEE2_OP - [4:0] */ +#define WM8995_AIF1DRC1_KNEE2_OP_WIDTH 5 /* AIF1DRC1_KNEE2_OP - [4:0] */ + +/* + * R1104 (0x450) - AIF1 DRC2 (1) + */ +#define WM8995_AIF1DRC2_SIG_DET_RMS_MASK 0xF800 /* AIF1DRC2_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC2_SIG_DET_RMS_SHIFT 11 /* AIF1DRC2_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC2_SIG_DET_RMS_WIDTH 5 /* AIF1DRC2_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF1DRC2_SIG_DET_PK_MASK 0x0600 /* AIF1DRC2_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC2_SIG_DET_PK_SHIFT 9 /* AIF1DRC2_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC2_SIG_DET_PK_WIDTH 2 /* AIF1DRC2_SIG_DET_PK - [10:9] */ +#define WM8995_AIF1DRC2_NG_ENA 0x0100 /* AIF1DRC2_NG_ENA */ +#define WM8995_AIF1DRC2_NG_ENA_MASK 0x0100 /* AIF1DRC2_NG_ENA */ +#define WM8995_AIF1DRC2_NG_ENA_SHIFT 8 /* AIF1DRC2_NG_ENA */ +#define WM8995_AIF1DRC2_NG_ENA_WIDTH 1 /* AIF1DRC2_NG_ENA */ +#define WM8995_AIF1DRC2_SIG_DET_MODE 0x0080 /* AIF1DRC2_SIG_DET_MODE */ +#define WM8995_AIF1DRC2_SIG_DET_MODE_MASK 0x0080 /* AIF1DRC2_SIG_DET_MODE */ +#define WM8995_AIF1DRC2_SIG_DET_MODE_SHIFT 7 /* AIF1DRC2_SIG_DET_MODE */ +#define WM8995_AIF1DRC2_SIG_DET_MODE_WIDTH 1 /* AIF1DRC2_SIG_DET_MODE */ +#define WM8995_AIF1DRC2_SIG_DET 0x0040 /* AIF1DRC2_SIG_DET */ +#define WM8995_AIF1DRC2_SIG_DET_MASK 0x0040 /* AIF1DRC2_SIG_DET */ +#define WM8995_AIF1DRC2_SIG_DET_SHIFT 6 /* AIF1DRC2_SIG_DET */ +#define WM8995_AIF1DRC2_SIG_DET_WIDTH 1 /* AIF1DRC2_SIG_DET */ +#define WM8995_AIF1DRC2_KNEE2_OP_ENA 0x0020 /* AIF1DRC2_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC2_KNEE2_OP_ENA_MASK 0x0020 /* AIF1DRC2_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC2_KNEE2_OP_ENA_SHIFT 5 /* AIF1DRC2_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC2_KNEE2_OP_ENA_WIDTH 1 /* AIF1DRC2_KNEE2_OP_ENA */ +#define WM8995_AIF1DRC2_QR 0x0010 /* AIF1DRC2_QR */ +#define WM8995_AIF1DRC2_QR_MASK 0x0010 /* AIF1DRC2_QR */ +#define WM8995_AIF1DRC2_QR_SHIFT 4 /* AIF1DRC2_QR */ +#define WM8995_AIF1DRC2_QR_WIDTH 1 /* AIF1DRC2_QR */ +#define WM8995_AIF1DRC2_ANTICLIP 0x0008 /* AIF1DRC2_ANTICLIP */ +#define WM8995_AIF1DRC2_ANTICLIP_MASK 0x0008 /* AIF1DRC2_ANTICLIP */ +#define WM8995_AIF1DRC2_ANTICLIP_SHIFT 3 /* AIF1DRC2_ANTICLIP */ +#define WM8995_AIF1DRC2_ANTICLIP_WIDTH 1 /* AIF1DRC2_ANTICLIP */ +#define WM8995_AIF1DAC2_DRC_ENA 0x0004 /* AIF1DAC2_DRC_ENA */ +#define WM8995_AIF1DAC2_DRC_ENA_MASK 0x0004 /* AIF1DAC2_DRC_ENA */ +#define WM8995_AIF1DAC2_DRC_ENA_SHIFT 2 /* AIF1DAC2_DRC_ENA */ +#define WM8995_AIF1DAC2_DRC_ENA_WIDTH 1 /* AIF1DAC2_DRC_ENA */ +#define WM8995_AIF1ADC2L_DRC_ENA 0x0002 /* AIF1ADC2L_DRC_ENA */ +#define WM8995_AIF1ADC2L_DRC_ENA_MASK 0x0002 /* AIF1ADC2L_DRC_ENA */ +#define WM8995_AIF1ADC2L_DRC_ENA_SHIFT 1 /* AIF1ADC2L_DRC_ENA */ +#define WM8995_AIF1ADC2L_DRC_ENA_WIDTH 1 /* AIF1ADC2L_DRC_ENA */ +#define WM8995_AIF1ADC2R_DRC_ENA 0x0001 /* AIF1ADC2R_DRC_ENA */ +#define WM8995_AIF1ADC2R_DRC_ENA_MASK 0x0001 /* AIF1ADC2R_DRC_ENA */ +#define WM8995_AIF1ADC2R_DRC_ENA_SHIFT 0 /* AIF1ADC2R_DRC_ENA */ +#define WM8995_AIF1ADC2R_DRC_ENA_WIDTH 1 /* AIF1ADC2R_DRC_ENA */ + +/* + * R1105 (0x451) - AIF1 DRC2 (2) + */ +#define WM8995_AIF1DRC2_ATK_MASK 0x1E00 /* AIF1DRC2_ATK - [12:9] */ +#define WM8995_AIF1DRC2_ATK_SHIFT 9 /* AIF1DRC2_ATK - [12:9] */ +#define WM8995_AIF1DRC2_ATK_WIDTH 4 /* AIF1DRC2_ATK - [12:9] */ +#define WM8995_AIF1DRC2_DCY_MASK 0x01E0 /* AIF1DRC2_DCY - [8:5] */ +#define WM8995_AIF1DRC2_DCY_SHIFT 5 /* AIF1DRC2_DCY - [8:5] */ +#define WM8995_AIF1DRC2_DCY_WIDTH 4 /* AIF1DRC2_DCY - [8:5] */ +#define WM8995_AIF1DRC2_MINGAIN_MASK 0x001C /* AIF1DRC2_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC2_MINGAIN_SHIFT 2 /* AIF1DRC2_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC2_MINGAIN_WIDTH 3 /* AIF1DRC2_MINGAIN - [4:2] */ +#define WM8995_AIF1DRC2_MAXGAIN_MASK 0x0003 /* AIF1DRC2_MAXGAIN - [1:0] */ +#define WM8995_AIF1DRC2_MAXGAIN_SHIFT 0 /* AIF1DRC2_MAXGAIN - [1:0] */ +#define WM8995_AIF1DRC2_MAXGAIN_WIDTH 2 /* AIF1DRC2_MAXGAIN - [1:0] */ + +/* + * R1106 (0x452) - AIF1 DRC2 (3) + */ +#define WM8995_AIF1DRC2_NG_MINGAIN_MASK 0xF000 /* AIF1DRC2_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC2_NG_MINGAIN_SHIFT 12 /* AIF1DRC2_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC2_NG_MINGAIN_WIDTH 4 /* AIF1DRC2_NG_MINGAIN - [15:12] */ +#define WM8995_AIF1DRC2_NG_EXP_MASK 0x0C00 /* AIF1DRC2_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC2_NG_EXP_SHIFT 10 /* AIF1DRC2_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC2_NG_EXP_WIDTH 2 /* AIF1DRC2_NG_EXP - [11:10] */ +#define WM8995_AIF1DRC2_QR_THR_MASK 0x0300 /* AIF1DRC2_QR_THR - [9:8] */ +#define WM8995_AIF1DRC2_QR_THR_SHIFT 8 /* AIF1DRC2_QR_THR - [9:8] */ +#define WM8995_AIF1DRC2_QR_THR_WIDTH 2 /* AIF1DRC2_QR_THR - [9:8] */ +#define WM8995_AIF1DRC2_QR_DCY_MASK 0x00C0 /* AIF1DRC2_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC2_QR_DCY_SHIFT 6 /* AIF1DRC2_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC2_QR_DCY_WIDTH 2 /* AIF1DRC2_QR_DCY - [7:6] */ +#define WM8995_AIF1DRC2_HI_COMP_MASK 0x0038 /* AIF1DRC2_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC2_HI_COMP_SHIFT 3 /* AIF1DRC2_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC2_HI_COMP_WIDTH 3 /* AIF1DRC2_HI_COMP - [5:3] */ +#define WM8995_AIF1DRC2_LO_COMP_MASK 0x0007 /* AIF1DRC2_LO_COMP - [2:0] */ +#define WM8995_AIF1DRC2_LO_COMP_SHIFT 0 /* AIF1DRC2_LO_COMP - [2:0] */ +#define WM8995_AIF1DRC2_LO_COMP_WIDTH 3 /* AIF1DRC2_LO_COMP - [2:0] */ + +/* + * R1107 (0x453) - AIF1 DRC2 (4) + */ +#define WM8995_AIF1DRC2_KNEE_IP_MASK 0x07E0 /* AIF1DRC2_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC2_KNEE_IP_SHIFT 5 /* AIF1DRC2_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC2_KNEE_IP_WIDTH 6 /* AIF1DRC2_KNEE_IP - [10:5] */ +#define WM8995_AIF1DRC2_KNEE_OP_MASK 0x001F /* AIF1DRC2_KNEE_OP - [4:0] */ +#define WM8995_AIF1DRC2_KNEE_OP_SHIFT 0 /* AIF1DRC2_KNEE_OP - [4:0] */ +#define WM8995_AIF1DRC2_KNEE_OP_WIDTH 5 /* AIF1DRC2_KNEE_OP - [4:0] */ + +/* + * R1108 (0x454) - AIF1 DRC2 (5) + */ +#define WM8995_AIF1DRC2_KNEE2_IP_MASK 0x03E0 /* AIF1DRC2_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC2_KNEE2_IP_SHIFT 5 /* AIF1DRC2_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC2_KNEE2_IP_WIDTH 5 /* AIF1DRC2_KNEE2_IP - [9:5] */ +#define WM8995_AIF1DRC2_KNEE2_OP_MASK 0x001F /* AIF1DRC2_KNEE2_OP - [4:0] */ +#define WM8995_AIF1DRC2_KNEE2_OP_SHIFT 0 /* AIF1DRC2_KNEE2_OP - [4:0] */ +#define WM8995_AIF1DRC2_KNEE2_OP_WIDTH 5 /* AIF1DRC2_KNEE2_OP - [4:0] */ + +/* + * R1152 (0x480) - AIF1 DAC1 EQ Gains (1) + */ +#define WM8995_AIF1DAC1_EQ_B1_GAIN_MASK 0xF800 /* AIF1DAC1_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B1_GAIN_SHIFT 11 /* AIF1DAC1_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B1_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B2_GAIN_MASK 0x07C0 /* AIF1DAC1_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC1_EQ_B2_GAIN_SHIFT 6 /* AIF1DAC1_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC1_EQ_B2_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC1_EQ_B3_GAIN_MASK 0x003E /* AIF1DAC1_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC1_EQ_B3_GAIN_SHIFT 1 /* AIF1DAC1_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC1_EQ_B3_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC1_EQ_ENA 0x0001 /* AIF1DAC1_EQ_ENA */ +#define WM8995_AIF1DAC1_EQ_ENA_MASK 0x0001 /* AIF1DAC1_EQ_ENA */ +#define WM8995_AIF1DAC1_EQ_ENA_SHIFT 0 /* AIF1DAC1_EQ_ENA */ +#define WM8995_AIF1DAC1_EQ_ENA_WIDTH 1 /* AIF1DAC1_EQ_ENA */ + +/* + * R1153 (0x481) - AIF1 DAC1 EQ Gains (2) + */ +#define WM8995_AIF1DAC1_EQ_B4_GAIN_MASK 0xF800 /* AIF1DAC1_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B4_GAIN_SHIFT 11 /* AIF1DAC1_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B4_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC1_EQ_B5_GAIN_MASK 0x07C0 /* AIF1DAC1_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF1DAC1_EQ_B5_GAIN_SHIFT 6 /* AIF1DAC1_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF1DAC1_EQ_B5_GAIN_WIDTH 5 /* AIF1DAC1_EQ_B5_GAIN - [10:6] */ + +/* + * R1154 (0x482) - AIF1 DAC1 EQ Band 1 A + */ +#define WM8995_AIF1DAC1_EQ_B1_A_MASK 0xFFFF /* AIF1DAC1_EQ_B1_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_A_SHIFT 0 /* AIF1DAC1_EQ_B1_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_A_WIDTH 16 /* AIF1DAC1_EQ_B1_A - [15:0] */ + +/* + * R1155 (0x483) - AIF1 DAC1 EQ Band 1 B + */ +#define WM8995_AIF1DAC1_EQ_B1_B_MASK 0xFFFF /* AIF1DAC1_EQ_B1_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_B_SHIFT 0 /* AIF1DAC1_EQ_B1_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_B_WIDTH 16 /* AIF1DAC1_EQ_B1_B - [15:0] */ + +/* + * R1156 (0x484) - AIF1 DAC1 EQ Band 1 PG + */ +#define WM8995_AIF1DAC1_EQ_B1_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B1_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_PG_SHIFT 0 /* AIF1DAC1_EQ_B1_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B1_PG_WIDTH 16 /* AIF1DAC1_EQ_B1_PG - [15:0] */ + +/* + * R1157 (0x485) - AIF1 DAC1 EQ Band 2 A + */ +#define WM8995_AIF1DAC1_EQ_B2_A_MASK 0xFFFF /* AIF1DAC1_EQ_B2_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_A_SHIFT 0 /* AIF1DAC1_EQ_B2_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_A_WIDTH 16 /* AIF1DAC1_EQ_B2_A - [15:0] */ + +/* + * R1158 (0x486) - AIF1 DAC1 EQ Band 2 B + */ +#define WM8995_AIF1DAC1_EQ_B2_B_MASK 0xFFFF /* AIF1DAC1_EQ_B2_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_B_SHIFT 0 /* AIF1DAC1_EQ_B2_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_B_WIDTH 16 /* AIF1DAC1_EQ_B2_B - [15:0] */ + +/* + * R1159 (0x487) - AIF1 DAC1 EQ Band 2 C + */ +#define WM8995_AIF1DAC1_EQ_B2_C_MASK 0xFFFF /* AIF1DAC1_EQ_B2_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_C_SHIFT 0 /* AIF1DAC1_EQ_B2_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_C_WIDTH 16 /* AIF1DAC1_EQ_B2_C - [15:0] */ + +/* + * R1160 (0x488) - AIF1 DAC1 EQ Band 2 PG + */ +#define WM8995_AIF1DAC1_EQ_B2_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B2_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_PG_SHIFT 0 /* AIF1DAC1_EQ_B2_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B2_PG_WIDTH 16 /* AIF1DAC1_EQ_B2_PG - [15:0] */ + +/* + * R1161 (0x489) - AIF1 DAC1 EQ Band 3 A + */ +#define WM8995_AIF1DAC1_EQ_B3_A_MASK 0xFFFF /* AIF1DAC1_EQ_B3_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_A_SHIFT 0 /* AIF1DAC1_EQ_B3_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_A_WIDTH 16 /* AIF1DAC1_EQ_B3_A - [15:0] */ + +/* + * R1162 (0x48A) - AIF1 DAC1 EQ Band 3 B + */ +#define WM8995_AIF1DAC1_EQ_B3_B_MASK 0xFFFF /* AIF1DAC1_EQ_B3_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_B_SHIFT 0 /* AIF1DAC1_EQ_B3_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_B_WIDTH 16 /* AIF1DAC1_EQ_B3_B - [15:0] */ + +/* + * R1163 (0x48B) - AIF1 DAC1 EQ Band 3 C + */ +#define WM8995_AIF1DAC1_EQ_B3_C_MASK 0xFFFF /* AIF1DAC1_EQ_B3_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_C_SHIFT 0 /* AIF1DAC1_EQ_B3_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_C_WIDTH 16 /* AIF1DAC1_EQ_B3_C - [15:0] */ + +/* + * R1164 (0x48C) - AIF1 DAC1 EQ Band 3 PG + */ +#define WM8995_AIF1DAC1_EQ_B3_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B3_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_PG_SHIFT 0 /* AIF1DAC1_EQ_B3_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B3_PG_WIDTH 16 /* AIF1DAC1_EQ_B3_PG - [15:0] */ + +/* + * R1165 (0x48D) - AIF1 DAC1 EQ Band 4 A + */ +#define WM8995_AIF1DAC1_EQ_B4_A_MASK 0xFFFF /* AIF1DAC1_EQ_B4_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_A_SHIFT 0 /* AIF1DAC1_EQ_B4_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_A_WIDTH 16 /* AIF1DAC1_EQ_B4_A - [15:0] */ + +/* + * R1166 (0x48E) - AIF1 DAC1 EQ Band 4 B + */ +#define WM8995_AIF1DAC1_EQ_B4_B_MASK 0xFFFF /* AIF1DAC1_EQ_B4_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_B_SHIFT 0 /* AIF1DAC1_EQ_B4_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_B_WIDTH 16 /* AIF1DAC1_EQ_B4_B - [15:0] */ + +/* + * R1167 (0x48F) - AIF1 DAC1 EQ Band 4 C + */ +#define WM8995_AIF1DAC1_EQ_B4_C_MASK 0xFFFF /* AIF1DAC1_EQ_B4_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_C_SHIFT 0 /* AIF1DAC1_EQ_B4_C - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_C_WIDTH 16 /* AIF1DAC1_EQ_B4_C - [15:0] */ + +/* + * R1168 (0x490) - AIF1 DAC1 EQ Band 4 PG + */ +#define WM8995_AIF1DAC1_EQ_B4_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B4_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_PG_SHIFT 0 /* AIF1DAC1_EQ_B4_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B4_PG_WIDTH 16 /* AIF1DAC1_EQ_B4_PG - [15:0] */ + +/* + * R1169 (0x491) - AIF1 DAC1 EQ Band 5 A + */ +#define WM8995_AIF1DAC1_EQ_B5_A_MASK 0xFFFF /* AIF1DAC1_EQ_B5_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_A_SHIFT 0 /* AIF1DAC1_EQ_B5_A - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_A_WIDTH 16 /* AIF1DAC1_EQ_B5_A - [15:0] */ + +/* + * R1170 (0x492) - AIF1 DAC1 EQ Band 5 B + */ +#define WM8995_AIF1DAC1_EQ_B5_B_MASK 0xFFFF /* AIF1DAC1_EQ_B5_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_B_SHIFT 0 /* AIF1DAC1_EQ_B5_B - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_B_WIDTH 16 /* AIF1DAC1_EQ_B5_B - [15:0] */ + +/* + * R1171 (0x493) - AIF1 DAC1 EQ Band 5 PG + */ +#define WM8995_AIF1DAC1_EQ_B5_PG_MASK 0xFFFF /* AIF1DAC1_EQ_B5_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_PG_SHIFT 0 /* AIF1DAC1_EQ_B5_PG - [15:0] */ +#define WM8995_AIF1DAC1_EQ_B5_PG_WIDTH 16 /* AIF1DAC1_EQ_B5_PG - [15:0] */ + +/* + * R1184 (0x4A0) - AIF1 DAC2 EQ Gains (1) + */ +#define WM8995_AIF1DAC2_EQ_B1_GAIN_MASK 0xF800 /* AIF1DAC2_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B1_GAIN_SHIFT 11 /* AIF1DAC2_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B1_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B2_GAIN_MASK 0x07C0 /* AIF1DAC2_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC2_EQ_B2_GAIN_SHIFT 6 /* AIF1DAC2_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC2_EQ_B2_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF1DAC2_EQ_B3_GAIN_MASK 0x003E /* AIF1DAC2_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC2_EQ_B3_GAIN_SHIFT 1 /* AIF1DAC2_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC2_EQ_B3_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF1DAC2_EQ_ENA 0x0001 /* AIF1DAC2_EQ_ENA */ +#define WM8995_AIF1DAC2_EQ_ENA_MASK 0x0001 /* AIF1DAC2_EQ_ENA */ +#define WM8995_AIF1DAC2_EQ_ENA_SHIFT 0 /* AIF1DAC2_EQ_ENA */ +#define WM8995_AIF1DAC2_EQ_ENA_WIDTH 1 /* AIF1DAC2_EQ_ENA */ + +/* + * R1185 (0x4A1) - AIF1 DAC2 EQ Gains (2) + */ +#define WM8995_AIF1DAC2_EQ_B4_GAIN_MASK 0xF800 /* AIF1DAC2_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B4_GAIN_SHIFT 11 /* AIF1DAC2_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B4_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF1DAC2_EQ_B5_GAIN_MASK 0x07C0 /* AIF1DAC2_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF1DAC2_EQ_B5_GAIN_SHIFT 6 /* AIF1DAC2_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF1DAC2_EQ_B5_GAIN_WIDTH 5 /* AIF1DAC2_EQ_B5_GAIN - [10:6] */ + +/* + * R1186 (0x4A2) - AIF1 DAC2 EQ Band 1 A + */ +#define WM8995_AIF1DAC2_EQ_B1_A_MASK 0xFFFF /* AIF1DAC2_EQ_B1_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_A_SHIFT 0 /* AIF1DAC2_EQ_B1_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_A_WIDTH 16 /* AIF1DAC2_EQ_B1_A - [15:0] */ + +/* + * R1187 (0x4A3) - AIF1 DAC2 EQ Band 1 B + */ +#define WM8995_AIF1DAC2_EQ_B1_B_MASK 0xFFFF /* AIF1DAC2_EQ_B1_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_B_SHIFT 0 /* AIF1DAC2_EQ_B1_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_B_WIDTH 16 /* AIF1DAC2_EQ_B1_B - [15:0] */ + +/* + * R1188 (0x4A4) - AIF1 DAC2 EQ Band 1 PG + */ +#define WM8995_AIF1DAC2_EQ_B1_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B1_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_PG_SHIFT 0 /* AIF1DAC2_EQ_B1_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B1_PG_WIDTH 16 /* AIF1DAC2_EQ_B1_PG - [15:0] */ + +/* + * R1189 (0x4A5) - AIF1 DAC2 EQ Band 2 A + */ +#define WM8995_AIF1DAC2_EQ_B2_A_MASK 0xFFFF /* AIF1DAC2_EQ_B2_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_A_SHIFT 0 /* AIF1DAC2_EQ_B2_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_A_WIDTH 16 /* AIF1DAC2_EQ_B2_A - [15:0] */ + +/* + * R1190 (0x4A6) - AIF1 DAC2 EQ Band 2 B + */ +#define WM8995_AIF1DAC2_EQ_B2_B_MASK 0xFFFF /* AIF1DAC2_EQ_B2_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_B_SHIFT 0 /* AIF1DAC2_EQ_B2_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_B_WIDTH 16 /* AIF1DAC2_EQ_B2_B - [15:0] */ + +/* + * R1191 (0x4A7) - AIF1 DAC2 EQ Band 2 C + */ +#define WM8995_AIF1DAC2_EQ_B2_C_MASK 0xFFFF /* AIF1DAC2_EQ_B2_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_C_SHIFT 0 /* AIF1DAC2_EQ_B2_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_C_WIDTH 16 /* AIF1DAC2_EQ_B2_C - [15:0] */ + +/* + * R1192 (0x4A8) - AIF1 DAC2 EQ Band 2 PG + */ +#define WM8995_AIF1DAC2_EQ_B2_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B2_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_PG_SHIFT 0 /* AIF1DAC2_EQ_B2_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B2_PG_WIDTH 16 /* AIF1DAC2_EQ_B2_PG - [15:0] */ + +/* + * R1193 (0x4A9) - AIF1 DAC2 EQ Band 3 A + */ +#define WM8995_AIF1DAC2_EQ_B3_A_MASK 0xFFFF /* AIF1DAC2_EQ_B3_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_A_SHIFT 0 /* AIF1DAC2_EQ_B3_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_A_WIDTH 16 /* AIF1DAC2_EQ_B3_A - [15:0] */ + +/* + * R1194 (0x4AA) - AIF1 DAC2 EQ Band 3 B + */ +#define WM8995_AIF1DAC2_EQ_B3_B_MASK 0xFFFF /* AIF1DAC2_EQ_B3_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_B_SHIFT 0 /* AIF1DAC2_EQ_B3_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_B_WIDTH 16 /* AIF1DAC2_EQ_B3_B - [15:0] */ + +/* + * R1195 (0x4AB) - AIF1 DAC2 EQ Band 3 C + */ +#define WM8995_AIF1DAC2_EQ_B3_C_MASK 0xFFFF /* AIF1DAC2_EQ_B3_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_C_SHIFT 0 /* AIF1DAC2_EQ_B3_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_C_WIDTH 16 /* AIF1DAC2_EQ_B3_C - [15:0] */ + +/* + * R1196 (0x4AC) - AIF1 DAC2 EQ Band 3 PG + */ +#define WM8995_AIF1DAC2_EQ_B3_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B3_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_PG_SHIFT 0 /* AIF1DAC2_EQ_B3_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B3_PG_WIDTH 16 /* AIF1DAC2_EQ_B3_PG - [15:0] */ + +/* + * R1197 (0x4AD) - AIF1 DAC2 EQ Band 4 A + */ +#define WM8995_AIF1DAC2_EQ_B4_A_MASK 0xFFFF /* AIF1DAC2_EQ_B4_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_A_SHIFT 0 /* AIF1DAC2_EQ_B4_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_A_WIDTH 16 /* AIF1DAC2_EQ_B4_A - [15:0] */ + +/* + * R1198 (0x4AE) - AIF1 DAC2 EQ Band 4 B + */ +#define WM8995_AIF1DAC2_EQ_B4_B_MASK 0xFFFF /* AIF1DAC2_EQ_B4_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_B_SHIFT 0 /* AIF1DAC2_EQ_B4_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_B_WIDTH 16 /* AIF1DAC2_EQ_B4_B - [15:0] */ + +/* + * R1199 (0x4AF) - AIF1 DAC2 EQ Band 4 C + */ +#define WM8995_AIF1DAC2_EQ_B4_C_MASK 0xFFFF /* AIF1DAC2_EQ_B4_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_C_SHIFT 0 /* AIF1DAC2_EQ_B4_C - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_C_WIDTH 16 /* AIF1DAC2_EQ_B4_C - [15:0] */ + +/* + * R1200 (0x4B0) - AIF1 DAC2 EQ Band 4 PG + */ +#define WM8995_AIF1DAC2_EQ_B4_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B4_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_PG_SHIFT 0 /* AIF1DAC2_EQ_B4_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B4_PG_WIDTH 16 /* AIF1DAC2_EQ_B4_PG - [15:0] */ + +/* + * R1201 (0x4B1) - AIF1 DAC2 EQ Band 5 A + */ +#define WM8995_AIF1DAC2_EQ_B5_A_MASK 0xFFFF /* AIF1DAC2_EQ_B5_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_A_SHIFT 0 /* AIF1DAC2_EQ_B5_A - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_A_WIDTH 16 /* AIF1DAC2_EQ_B5_A - [15:0] */ + +/* + * R1202 (0x4B2) - AIF1 DAC2 EQ Band 5 B + */ +#define WM8995_AIF1DAC2_EQ_B5_B_MASK 0xFFFF /* AIF1DAC2_EQ_B5_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_B_SHIFT 0 /* AIF1DAC2_EQ_B5_B - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_B_WIDTH 16 /* AIF1DAC2_EQ_B5_B - [15:0] */ + +/* + * R1203 (0x4B3) - AIF1 DAC2 EQ Band 5 PG + */ +#define WM8995_AIF1DAC2_EQ_B5_PG_MASK 0xFFFF /* AIF1DAC2_EQ_B5_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_PG_SHIFT 0 /* AIF1DAC2_EQ_B5_PG - [15:0] */ +#define WM8995_AIF1DAC2_EQ_B5_PG_WIDTH 16 /* AIF1DAC2_EQ_B5_PG - [15:0] */ + +/* + * R1280 (0x500) - AIF2 ADC Left Volume + */ +#define WM8995_AIF2ADC_VU 0x0100 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_MASK 0x0100 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_SHIFT 8 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_WIDTH 1 /* AIF2ADC_VU */ +#define WM8995_AIF2ADCL_VOL_MASK 0x00FF /* AIF2ADCL_VOL - [7:0] */ +#define WM8995_AIF2ADCL_VOL_SHIFT 0 /* AIF2ADCL_VOL - [7:0] */ +#define WM8995_AIF2ADCL_VOL_WIDTH 8 /* AIF2ADCL_VOL - [7:0] */ + +/* + * R1281 (0x501) - AIF2 ADC Right Volume + */ +#define WM8995_AIF2ADC_VU 0x0100 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_MASK 0x0100 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_SHIFT 8 /* AIF2ADC_VU */ +#define WM8995_AIF2ADC_VU_WIDTH 1 /* AIF2ADC_VU */ +#define WM8995_AIF2ADCR_VOL_MASK 0x00FF /* AIF2ADCR_VOL - [7:0] */ +#define WM8995_AIF2ADCR_VOL_SHIFT 0 /* AIF2ADCR_VOL - [7:0] */ +#define WM8995_AIF2ADCR_VOL_WIDTH 8 /* AIF2ADCR_VOL - [7:0] */ + +/* + * R1282 (0x502) - AIF2 DAC Left Volume + */ +#define WM8995_AIF2DAC_VU 0x0100 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_MASK 0x0100 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_SHIFT 8 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_WIDTH 1 /* AIF2DAC_VU */ +#define WM8995_AIF2DACL_VOL_MASK 0x00FF /* AIF2DACL_VOL - [7:0] */ +#define WM8995_AIF2DACL_VOL_SHIFT 0 /* AIF2DACL_VOL - [7:0] */ +#define WM8995_AIF2DACL_VOL_WIDTH 8 /* AIF2DACL_VOL - [7:0] */ + +/* + * R1283 (0x503) - AIF2 DAC Right Volume + */ +#define WM8995_AIF2DAC_VU 0x0100 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_MASK 0x0100 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_SHIFT 8 /* AIF2DAC_VU */ +#define WM8995_AIF2DAC_VU_WIDTH 1 /* AIF2DAC_VU */ +#define WM8995_AIF2DACR_VOL_MASK 0x00FF /* AIF2DACR_VOL - [7:0] */ +#define WM8995_AIF2DACR_VOL_SHIFT 0 /* AIF2DACR_VOL - [7:0] */ +#define WM8995_AIF2DACR_VOL_WIDTH 8 /* AIF2DACR_VOL - [7:0] */ + +/* + * R1296 (0x510) - AIF2 ADC Filters + */ +#define WM8995_AIF2ADC_4FS 0x8000 /* AIF2ADC_4FS */ +#define WM8995_AIF2ADC_4FS_MASK 0x8000 /* AIF2ADC_4FS */ +#define WM8995_AIF2ADC_4FS_SHIFT 15 /* AIF2ADC_4FS */ +#define WM8995_AIF2ADC_4FS_WIDTH 1 /* AIF2ADC_4FS */ +#define WM8995_AIF2ADCL_HPF 0x1000 /* AIF2ADCL_HPF */ +#define WM8995_AIF2ADCL_HPF_MASK 0x1000 /* AIF2ADCL_HPF */ +#define WM8995_AIF2ADCL_HPF_SHIFT 12 /* AIF2ADCL_HPF */ +#define WM8995_AIF2ADCL_HPF_WIDTH 1 /* AIF2ADCL_HPF */ +#define WM8995_AIF2ADCR_HPF 0x0800 /* AIF2ADCR_HPF */ +#define WM8995_AIF2ADCR_HPF_MASK 0x0800 /* AIF2ADCR_HPF */ +#define WM8995_AIF2ADCR_HPF_SHIFT 11 /* AIF2ADCR_HPF */ +#define WM8995_AIF2ADCR_HPF_WIDTH 1 /* AIF2ADCR_HPF */ +#define WM8995_AIF2ADC_HPF_MODE 0x0008 /* AIF2ADC_HPF_MODE */ +#define WM8995_AIF2ADC_HPF_MODE_MASK 0x0008 /* AIF2ADC_HPF_MODE */ +#define WM8995_AIF2ADC_HPF_MODE_SHIFT 3 /* AIF2ADC_HPF_MODE */ +#define WM8995_AIF2ADC_HPF_MODE_WIDTH 1 /* AIF2ADC_HPF_MODE */ +#define WM8995_AIF2ADC_HPF_CUT_MASK 0x0007 /* AIF2ADC_HPF_CUT - [2:0] */ +#define WM8995_AIF2ADC_HPF_CUT_SHIFT 0 /* AIF2ADC_HPF_CUT - [2:0] */ +#define WM8995_AIF2ADC_HPF_CUT_WIDTH 3 /* AIF2ADC_HPF_CUT - [2:0] */ + +/* + * R1312 (0x520) - AIF2 DAC Filters (1) + */ +#define WM8995_AIF2DAC_MUTE 0x0200 /* AIF2DAC_MUTE */ +#define WM8995_AIF2DAC_MUTE_MASK 0x0200 /* AIF2DAC_MUTE */ +#define WM8995_AIF2DAC_MUTE_SHIFT 9 /* AIF2DAC_MUTE */ +#define WM8995_AIF2DAC_MUTE_WIDTH 1 /* AIF2DAC_MUTE */ +#define WM8995_AIF2DAC_MONO 0x0080 /* AIF2DAC_MONO */ +#define WM8995_AIF2DAC_MONO_MASK 0x0080 /* AIF2DAC_MONO */ +#define WM8995_AIF2DAC_MONO_SHIFT 7 /* AIF2DAC_MONO */ +#define WM8995_AIF2DAC_MONO_WIDTH 1 /* AIF2DAC_MONO */ +#define WM8995_AIF2DAC_MUTERATE 0x0020 /* AIF2DAC_MUTERATE */ +#define WM8995_AIF2DAC_MUTERATE_MASK 0x0020 /* AIF2DAC_MUTERATE */ +#define WM8995_AIF2DAC_MUTERATE_SHIFT 5 /* AIF2DAC_MUTERATE */ +#define WM8995_AIF2DAC_MUTERATE_WIDTH 1 /* AIF2DAC_MUTERATE */ +#define WM8995_AIF2DAC_UNMUTE_RAMP 0x0010 /* AIF2DAC_UNMUTE_RAMP */ +#define WM8995_AIF2DAC_UNMUTE_RAMP_MASK 0x0010 /* AIF2DAC_UNMUTE_RAMP */ +#define WM8995_AIF2DAC_UNMUTE_RAMP_SHIFT 4 /* AIF2DAC_UNMUTE_RAMP */ +#define WM8995_AIF2DAC_UNMUTE_RAMP_WIDTH 1 /* AIF2DAC_UNMUTE_RAMP */ +#define WM8995_AIF2DAC_DEEMP_MASK 0x0006 /* AIF2DAC_DEEMP - [2:1] */ +#define WM8995_AIF2DAC_DEEMP_SHIFT 1 /* AIF2DAC_DEEMP - [2:1] */ +#define WM8995_AIF2DAC_DEEMP_WIDTH 2 /* AIF2DAC_DEEMP - [2:1] */ + +/* + * R1313 (0x521) - AIF2 DAC Filters (2) + */ +#define WM8995_AIF2DAC_3D_GAIN_MASK 0x3E00 /* AIF2DAC_3D_GAIN - [13:9] */ +#define WM8995_AIF2DAC_3D_GAIN_SHIFT 9 /* AIF2DAC_3D_GAIN - [13:9] */ +#define WM8995_AIF2DAC_3D_GAIN_WIDTH 5 /* AIF2DAC_3D_GAIN - [13:9] */ +#define WM8995_AIF2DAC_3D_ENA 0x0100 /* AIF2DAC_3D_ENA */ +#define WM8995_AIF2DAC_3D_ENA_MASK 0x0100 /* AIF2DAC_3D_ENA */ +#define WM8995_AIF2DAC_3D_ENA_SHIFT 8 /* AIF2DAC_3D_ENA */ +#define WM8995_AIF2DAC_3D_ENA_WIDTH 1 /* AIF2DAC_3D_ENA */ + +/* + * R1344 (0x540) - AIF2 DRC (1) + */ +#define WM8995_AIF2DRC_SIG_DET_RMS_MASK 0xF800 /* AIF2DRC_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF2DRC_SIG_DET_RMS_SHIFT 11 /* AIF2DRC_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF2DRC_SIG_DET_RMS_WIDTH 5 /* AIF2DRC_SIG_DET_RMS - [15:11] */ +#define WM8995_AIF2DRC_SIG_DET_PK_MASK 0x0600 /* AIF2DRC_SIG_DET_PK - [10:9] */ +#define WM8995_AIF2DRC_SIG_DET_PK_SHIFT 9 /* AIF2DRC_SIG_DET_PK - [10:9] */ +#define WM8995_AIF2DRC_SIG_DET_PK_WIDTH 2 /* AIF2DRC_SIG_DET_PK - [10:9] */ +#define WM8995_AIF2DRC_NG_ENA 0x0100 /* AIF2DRC_NG_ENA */ +#define WM8995_AIF2DRC_NG_ENA_MASK 0x0100 /* AIF2DRC_NG_ENA */ +#define WM8995_AIF2DRC_NG_ENA_SHIFT 8 /* AIF2DRC_NG_ENA */ +#define WM8995_AIF2DRC_NG_ENA_WIDTH 1 /* AIF2DRC_NG_ENA */ +#define WM8995_AIF2DRC_SIG_DET_MODE 0x0080 /* AIF2DRC_SIG_DET_MODE */ +#define WM8995_AIF2DRC_SIG_DET_MODE_MASK 0x0080 /* AIF2DRC_SIG_DET_MODE */ +#define WM8995_AIF2DRC_SIG_DET_MODE_SHIFT 7 /* AIF2DRC_SIG_DET_MODE */ +#define WM8995_AIF2DRC_SIG_DET_MODE_WIDTH 1 /* AIF2DRC_SIG_DET_MODE */ +#define WM8995_AIF2DRC_SIG_DET 0x0040 /* AIF2DRC_SIG_DET */ +#define WM8995_AIF2DRC_SIG_DET_MASK 0x0040 /* AIF2DRC_SIG_DET */ +#define WM8995_AIF2DRC_SIG_DET_SHIFT 6 /* AIF2DRC_SIG_DET */ +#define WM8995_AIF2DRC_SIG_DET_WIDTH 1 /* AIF2DRC_SIG_DET */ +#define WM8995_AIF2DRC_KNEE2_OP_ENA 0x0020 /* AIF2DRC_KNEE2_OP_ENA */ +#define WM8995_AIF2DRC_KNEE2_OP_ENA_MASK 0x0020 /* AIF2DRC_KNEE2_OP_ENA */ +#define WM8995_AIF2DRC_KNEE2_OP_ENA_SHIFT 5 /* AIF2DRC_KNEE2_OP_ENA */ +#define WM8995_AIF2DRC_KNEE2_OP_ENA_WIDTH 1 /* AIF2DRC_KNEE2_OP_ENA */ +#define WM8995_AIF2DRC_QR 0x0010 /* AIF2DRC_QR */ +#define WM8995_AIF2DRC_QR_MASK 0x0010 /* AIF2DRC_QR */ +#define WM8995_AIF2DRC_QR_SHIFT 4 /* AIF2DRC_QR */ +#define WM8995_AIF2DRC_QR_WIDTH 1 /* AIF2DRC_QR */ +#define WM8995_AIF2DRC_ANTICLIP 0x0008 /* AIF2DRC_ANTICLIP */ +#define WM8995_AIF2DRC_ANTICLIP_MASK 0x0008 /* AIF2DRC_ANTICLIP */ +#define WM8995_AIF2DRC_ANTICLIP_SHIFT 3 /* AIF2DRC_ANTICLIP */ +#define WM8995_AIF2DRC_ANTICLIP_WIDTH 1 /* AIF2DRC_ANTICLIP */ +#define WM8995_AIF2DAC_DRC_ENA 0x0004 /* AIF2DAC_DRC_ENA */ +#define WM8995_AIF2DAC_DRC_ENA_MASK 0x0004 /* AIF2DAC_DRC_ENA */ +#define WM8995_AIF2DAC_DRC_ENA_SHIFT 2 /* AIF2DAC_DRC_ENA */ +#define WM8995_AIF2DAC_DRC_ENA_WIDTH 1 /* AIF2DAC_DRC_ENA */ +#define WM8995_AIF2ADCL_DRC_ENA 0x0002 /* AIF2ADCL_DRC_ENA */ +#define WM8995_AIF2ADCL_DRC_ENA_MASK 0x0002 /* AIF2ADCL_DRC_ENA */ +#define WM8995_AIF2ADCL_DRC_ENA_SHIFT 1 /* AIF2ADCL_DRC_ENA */ +#define WM8995_AIF2ADCL_DRC_ENA_WIDTH 1 /* AIF2ADCL_DRC_ENA */ +#define WM8995_AIF2ADCR_DRC_ENA 0x0001 /* AIF2ADCR_DRC_ENA */ +#define WM8995_AIF2ADCR_DRC_ENA_MASK 0x0001 /* AIF2ADCR_DRC_ENA */ +#define WM8995_AIF2ADCR_DRC_ENA_SHIFT 0 /* AIF2ADCR_DRC_ENA */ +#define WM8995_AIF2ADCR_DRC_ENA_WIDTH 1 /* AIF2ADCR_DRC_ENA */ + +/* + * R1345 (0x541) - AIF2 DRC (2) + */ +#define WM8995_AIF2DRC_ATK_MASK 0x1E00 /* AIF2DRC_ATK - [12:9] */ +#define WM8995_AIF2DRC_ATK_SHIFT 9 /* AIF2DRC_ATK - [12:9] */ +#define WM8995_AIF2DRC_ATK_WIDTH 4 /* AIF2DRC_ATK - [12:9] */ +#define WM8995_AIF2DRC_DCY_MASK 0x01E0 /* AIF2DRC_DCY - [8:5] */ +#define WM8995_AIF2DRC_DCY_SHIFT 5 /* AIF2DRC_DCY - [8:5] */ +#define WM8995_AIF2DRC_DCY_WIDTH 4 /* AIF2DRC_DCY - [8:5] */ +#define WM8995_AIF2DRC_MINGAIN_MASK 0x001C /* AIF2DRC_MINGAIN - [4:2] */ +#define WM8995_AIF2DRC_MINGAIN_SHIFT 2 /* AIF2DRC_MINGAIN - [4:2] */ +#define WM8995_AIF2DRC_MINGAIN_WIDTH 3 /* AIF2DRC_MINGAIN - [4:2] */ +#define WM8995_AIF2DRC_MAXGAIN_MASK 0x0003 /* AIF2DRC_MAXGAIN - [1:0] */ +#define WM8995_AIF2DRC_MAXGAIN_SHIFT 0 /* AIF2DRC_MAXGAIN - [1:0] */ +#define WM8995_AIF2DRC_MAXGAIN_WIDTH 2 /* AIF2DRC_MAXGAIN - [1:0] */ + +/* + * R1346 (0x542) - AIF2 DRC (3) + */ +#define WM8995_AIF2DRC_NG_MINGAIN_MASK 0xF000 /* AIF2DRC_NG_MINGAIN - [15:12] */ +#define WM8995_AIF2DRC_NG_MINGAIN_SHIFT 12 /* AIF2DRC_NG_MINGAIN - [15:12] */ +#define WM8995_AIF2DRC_NG_MINGAIN_WIDTH 4 /* AIF2DRC_NG_MINGAIN - [15:12] */ +#define WM8995_AIF2DRC_NG_EXP_MASK 0x0C00 /* AIF2DRC_NG_EXP - [11:10] */ +#define WM8995_AIF2DRC_NG_EXP_SHIFT 10 /* AIF2DRC_NG_EXP - [11:10] */ +#define WM8995_AIF2DRC_NG_EXP_WIDTH 2 /* AIF2DRC_NG_EXP - [11:10] */ +#define WM8995_AIF2DRC_QR_THR_MASK 0x0300 /* AIF2DRC_QR_THR - [9:8] */ +#define WM8995_AIF2DRC_QR_THR_SHIFT 8 /* AIF2DRC_QR_THR - [9:8] */ +#define WM8995_AIF2DRC_QR_THR_WIDTH 2 /* AIF2DRC_QR_THR - [9:8] */ +#define WM8995_AIF2DRC_QR_DCY_MASK 0x00C0 /* AIF2DRC_QR_DCY - [7:6] */ +#define WM8995_AIF2DRC_QR_DCY_SHIFT 6 /* AIF2DRC_QR_DCY - [7:6] */ +#define WM8995_AIF2DRC_QR_DCY_WIDTH 2 /* AIF2DRC_QR_DCY - [7:6] */ +#define WM8995_AIF2DRC_HI_COMP_MASK 0x0038 /* AIF2DRC_HI_COMP - [5:3] */ +#define WM8995_AIF2DRC_HI_COMP_SHIFT 3 /* AIF2DRC_HI_COMP - [5:3] */ +#define WM8995_AIF2DRC_HI_COMP_WIDTH 3 /* AIF2DRC_HI_COMP - [5:3] */ +#define WM8995_AIF2DRC_LO_COMP_MASK 0x0007 /* AIF2DRC_LO_COMP - [2:0] */ +#define WM8995_AIF2DRC_LO_COMP_SHIFT 0 /* AIF2DRC_LO_COMP - [2:0] */ +#define WM8995_AIF2DRC_LO_COMP_WIDTH 3 /* AIF2DRC_LO_COMP - [2:0] */ + +/* + * R1347 (0x543) - AIF2 DRC (4) + */ +#define WM8995_AIF2DRC_KNEE_IP_MASK 0x07E0 /* AIF2DRC_KNEE_IP - [10:5] */ +#define WM8995_AIF2DRC_KNEE_IP_SHIFT 5 /* AIF2DRC_KNEE_IP - [10:5] */ +#define WM8995_AIF2DRC_KNEE_IP_WIDTH 6 /* AIF2DRC_KNEE_IP - [10:5] */ +#define WM8995_AIF2DRC_KNEE_OP_MASK 0x001F /* AIF2DRC_KNEE_OP - [4:0] */ +#define WM8995_AIF2DRC_KNEE_OP_SHIFT 0 /* AIF2DRC_KNEE_OP - [4:0] */ +#define WM8995_AIF2DRC_KNEE_OP_WIDTH 5 /* AIF2DRC_KNEE_OP - [4:0] */ + +/* + * R1348 (0x544) - AIF2 DRC (5) + */ +#define WM8995_AIF2DRC_KNEE2_IP_MASK 0x03E0 /* AIF2DRC_KNEE2_IP - [9:5] */ +#define WM8995_AIF2DRC_KNEE2_IP_SHIFT 5 /* AIF2DRC_KNEE2_IP - [9:5] */ +#define WM8995_AIF2DRC_KNEE2_IP_WIDTH 5 /* AIF2DRC_KNEE2_IP - [9:5] */ +#define WM8995_AIF2DRC_KNEE2_OP_MASK 0x001F /* AIF2DRC_KNEE2_OP - [4:0] */ +#define WM8995_AIF2DRC_KNEE2_OP_SHIFT 0 /* AIF2DRC_KNEE2_OP - [4:0] */ +#define WM8995_AIF2DRC_KNEE2_OP_WIDTH 5 /* AIF2DRC_KNEE2_OP - [4:0] */ + +/* + * R1408 (0x580) - AIF2 EQ Gains (1) + */ +#define WM8995_AIF2DAC_EQ_B1_GAIN_MASK 0xF800 /* AIF2DAC_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B1_GAIN_SHIFT 11 /* AIF2DAC_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B1_GAIN_WIDTH 5 /* AIF2DAC_EQ_B1_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B2_GAIN_MASK 0x07C0 /* AIF2DAC_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF2DAC_EQ_B2_GAIN_SHIFT 6 /* AIF2DAC_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF2DAC_EQ_B2_GAIN_WIDTH 5 /* AIF2DAC_EQ_B2_GAIN - [10:6] */ +#define WM8995_AIF2DAC_EQ_B3_GAIN_MASK 0x003E /* AIF2DAC_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF2DAC_EQ_B3_GAIN_SHIFT 1 /* AIF2DAC_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF2DAC_EQ_B3_GAIN_WIDTH 5 /* AIF2DAC_EQ_B3_GAIN - [5:1] */ +#define WM8995_AIF2DAC_EQ_ENA 0x0001 /* AIF2DAC_EQ_ENA */ +#define WM8995_AIF2DAC_EQ_ENA_MASK 0x0001 /* AIF2DAC_EQ_ENA */ +#define WM8995_AIF2DAC_EQ_ENA_SHIFT 0 /* AIF2DAC_EQ_ENA */ +#define WM8995_AIF2DAC_EQ_ENA_WIDTH 1 /* AIF2DAC_EQ_ENA */ + +/* + * R1409 (0x581) - AIF2 EQ Gains (2) + */ +#define WM8995_AIF2DAC_EQ_B4_GAIN_MASK 0xF800 /* AIF2DAC_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B4_GAIN_SHIFT 11 /* AIF2DAC_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B4_GAIN_WIDTH 5 /* AIF2DAC_EQ_B4_GAIN - [15:11] */ +#define WM8995_AIF2DAC_EQ_B5_GAIN_MASK 0x07C0 /* AIF2DAC_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF2DAC_EQ_B5_GAIN_SHIFT 6 /* AIF2DAC_EQ_B5_GAIN - [10:6] */ +#define WM8995_AIF2DAC_EQ_B5_GAIN_WIDTH 5 /* AIF2DAC_EQ_B5_GAIN - [10:6] */ + +/* + * R1410 (0x582) - AIF2 EQ Band 1 A + */ +#define WM8995_AIF2DAC_EQ_B1_A_MASK 0xFFFF /* AIF2DAC_EQ_B1_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_A_SHIFT 0 /* AIF2DAC_EQ_B1_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_A_WIDTH 16 /* AIF2DAC_EQ_B1_A - [15:0] */ + +/* + * R1411 (0x583) - AIF2 EQ Band 1 B + */ +#define WM8995_AIF2DAC_EQ_B1_B_MASK 0xFFFF /* AIF2DAC_EQ_B1_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_B_SHIFT 0 /* AIF2DAC_EQ_B1_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_B_WIDTH 16 /* AIF2DAC_EQ_B1_B - [15:0] */ + +/* + * R1412 (0x584) - AIF2 EQ Band 1 PG + */ +#define WM8995_AIF2DAC_EQ_B1_PG_MASK 0xFFFF /* AIF2DAC_EQ_B1_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_PG_SHIFT 0 /* AIF2DAC_EQ_B1_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B1_PG_WIDTH 16 /* AIF2DAC_EQ_B1_PG - [15:0] */ + +/* + * R1413 (0x585) - AIF2 EQ Band 2 A + */ +#define WM8995_AIF2DAC_EQ_B2_A_MASK 0xFFFF /* AIF2DAC_EQ_B2_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_A_SHIFT 0 /* AIF2DAC_EQ_B2_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_A_WIDTH 16 /* AIF2DAC_EQ_B2_A - [15:0] */ + +/* + * R1414 (0x586) - AIF2 EQ Band 2 B + */ +#define WM8995_AIF2DAC_EQ_B2_B_MASK 0xFFFF /* AIF2DAC_EQ_B2_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_B_SHIFT 0 /* AIF2DAC_EQ_B2_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_B_WIDTH 16 /* AIF2DAC_EQ_B2_B - [15:0] */ + +/* + * R1415 (0x587) - AIF2 EQ Band 2 C + */ +#define WM8995_AIF2DAC_EQ_B2_C_MASK 0xFFFF /* AIF2DAC_EQ_B2_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_C_SHIFT 0 /* AIF2DAC_EQ_B2_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_C_WIDTH 16 /* AIF2DAC_EQ_B2_C - [15:0] */ + +/* + * R1416 (0x588) - AIF2 EQ Band 2 PG + */ +#define WM8995_AIF2DAC_EQ_B2_PG_MASK 0xFFFF /* AIF2DAC_EQ_B2_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_PG_SHIFT 0 /* AIF2DAC_EQ_B2_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B2_PG_WIDTH 16 /* AIF2DAC_EQ_B2_PG - [15:0] */ + +/* + * R1417 (0x589) - AIF2 EQ Band 3 A + */ +#define WM8995_AIF2DAC_EQ_B3_A_MASK 0xFFFF /* AIF2DAC_EQ_B3_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_A_SHIFT 0 /* AIF2DAC_EQ_B3_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_A_WIDTH 16 /* AIF2DAC_EQ_B3_A - [15:0] */ + +/* + * R1418 (0x58A) - AIF2 EQ Band 3 B + */ +#define WM8995_AIF2DAC_EQ_B3_B_MASK 0xFFFF /* AIF2DAC_EQ_B3_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_B_SHIFT 0 /* AIF2DAC_EQ_B3_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_B_WIDTH 16 /* AIF2DAC_EQ_B3_B - [15:0] */ + +/* + * R1419 (0x58B) - AIF2 EQ Band 3 C + */ +#define WM8995_AIF2DAC_EQ_B3_C_MASK 0xFFFF /* AIF2DAC_EQ_B3_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_C_SHIFT 0 /* AIF2DAC_EQ_B3_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_C_WIDTH 16 /* AIF2DAC_EQ_B3_C - [15:0] */ + +/* + * R1420 (0x58C) - AIF2 EQ Band 3 PG + */ +#define WM8995_AIF2DAC_EQ_B3_PG_MASK 0xFFFF /* AIF2DAC_EQ_B3_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_PG_SHIFT 0 /* AIF2DAC_EQ_B3_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B3_PG_WIDTH 16 /* AIF2DAC_EQ_B3_PG - [15:0] */ + +/* + * R1421 (0x58D) - AIF2 EQ Band 4 A + */ +#define WM8995_AIF2DAC_EQ_B4_A_MASK 0xFFFF /* AIF2DAC_EQ_B4_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_A_SHIFT 0 /* AIF2DAC_EQ_B4_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_A_WIDTH 16 /* AIF2DAC_EQ_B4_A - [15:0] */ + +/* + * R1422 (0x58E) - AIF2 EQ Band 4 B + */ +#define WM8995_AIF2DAC_EQ_B4_B_MASK 0xFFFF /* AIF2DAC_EQ_B4_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_B_SHIFT 0 /* AIF2DAC_EQ_B4_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_B_WIDTH 16 /* AIF2DAC_EQ_B4_B - [15:0] */ + +/* + * R1423 (0x58F) - AIF2 EQ Band 4 C + */ +#define WM8995_AIF2DAC_EQ_B4_C_MASK 0xFFFF /* AIF2DAC_EQ_B4_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_C_SHIFT 0 /* AIF2DAC_EQ_B4_C - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_C_WIDTH 16 /* AIF2DAC_EQ_B4_C - [15:0] */ + +/* + * R1424 (0x590) - AIF2 EQ Band 4 PG + */ +#define WM8995_AIF2DAC_EQ_B4_PG_MASK 0xFFFF /* AIF2DAC_EQ_B4_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_PG_SHIFT 0 /* AIF2DAC_EQ_B4_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B4_PG_WIDTH 16 /* AIF2DAC_EQ_B4_PG - [15:0] */ + +/* + * R1425 (0x591) - AIF2 EQ Band 5 A + */ +#define WM8995_AIF2DAC_EQ_B5_A_MASK 0xFFFF /* AIF2DAC_EQ_B5_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_A_SHIFT 0 /* AIF2DAC_EQ_B5_A - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_A_WIDTH 16 /* AIF2DAC_EQ_B5_A - [15:0] */ + +/* + * R1426 (0x592) - AIF2 EQ Band 5 B + */ +#define WM8995_AIF2DAC_EQ_B5_B_MASK 0xFFFF /* AIF2DAC_EQ_B5_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_B_SHIFT 0 /* AIF2DAC_EQ_B5_B - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_B_WIDTH 16 /* AIF2DAC_EQ_B5_B - [15:0] */ + +/* + * R1427 (0x593) - AIF2 EQ Band 5 PG + */ +#define WM8995_AIF2DAC_EQ_B5_PG_MASK 0xFFFF /* AIF2DAC_EQ_B5_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_PG_SHIFT 0 /* AIF2DAC_EQ_B5_PG - [15:0] */ +#define WM8995_AIF2DAC_EQ_B5_PG_WIDTH 16 /* AIF2DAC_EQ_B5_PG - [15:0] */ + +/* + * R1536 (0x600) - DAC1 Mixer Volumes + */ +#define WM8995_ADCR_DAC1_VOL_MASK 0x03E0 /* ADCR_DAC1_VOL - [9:5] */ +#define WM8995_ADCR_DAC1_VOL_SHIFT 5 /* ADCR_DAC1_VOL - [9:5] */ +#define WM8995_ADCR_DAC1_VOL_WIDTH 5 /* ADCR_DAC1_VOL - [9:5] */ +#define WM8995_ADCL_DAC1_VOL_MASK 0x001F /* ADCL_DAC1_VOL - [4:0] */ +#define WM8995_ADCL_DAC1_VOL_SHIFT 0 /* ADCL_DAC1_VOL - [4:0] */ +#define WM8995_ADCL_DAC1_VOL_WIDTH 5 /* ADCL_DAC1_VOL - [4:0] */ + +/* + * R1537 (0x601) - DAC1 Left Mixer Routing + */ +#define WM8995_ADCR_TO_DAC1L 0x0020 /* ADCR_TO_DAC1L */ +#define WM8995_ADCR_TO_DAC1L_MASK 0x0020 /* ADCR_TO_DAC1L */ +#define WM8995_ADCR_TO_DAC1L_SHIFT 5 /* ADCR_TO_DAC1L */ +#define WM8995_ADCR_TO_DAC1L_WIDTH 1 /* ADCR_TO_DAC1L */ +#define WM8995_ADCL_TO_DAC1L 0x0010 /* ADCL_TO_DAC1L */ +#define WM8995_ADCL_TO_DAC1L_MASK 0x0010 /* ADCL_TO_DAC1L */ +#define WM8995_ADCL_TO_DAC1L_SHIFT 4 /* ADCL_TO_DAC1L */ +#define WM8995_ADCL_TO_DAC1L_WIDTH 1 /* ADCL_TO_DAC1L */ +#define WM8995_AIF2DACL_TO_DAC1L 0x0004 /* AIF2DACL_TO_DAC1L */ +#define WM8995_AIF2DACL_TO_DAC1L_MASK 0x0004 /* AIF2DACL_TO_DAC1L */ +#define WM8995_AIF2DACL_TO_DAC1L_SHIFT 2 /* AIF2DACL_TO_DAC1L */ +#define WM8995_AIF2DACL_TO_DAC1L_WIDTH 1 /* AIF2DACL_TO_DAC1L */ +#define WM8995_AIF1DAC2L_TO_DAC1L 0x0002 /* AIF1DAC2L_TO_DAC1L */ +#define WM8995_AIF1DAC2L_TO_DAC1L_MASK 0x0002 /* AIF1DAC2L_TO_DAC1L */ +#define WM8995_AIF1DAC2L_TO_DAC1L_SHIFT 1 /* AIF1DAC2L_TO_DAC1L */ +#define WM8995_AIF1DAC2L_TO_DAC1L_WIDTH 1 /* AIF1DAC2L_TO_DAC1L */ +#define WM8995_AIF1DAC1L_TO_DAC1L 0x0001 /* AIF1DAC1L_TO_DAC1L */ +#define WM8995_AIF1DAC1L_TO_DAC1L_MASK 0x0001 /* AIF1DAC1L_TO_DAC1L */ +#define WM8995_AIF1DAC1L_TO_DAC1L_SHIFT 0 /* AIF1DAC1L_TO_DAC1L */ +#define WM8995_AIF1DAC1L_TO_DAC1L_WIDTH 1 /* AIF1DAC1L_TO_DAC1L */ + +/* + * R1538 (0x602) - DAC1 Right Mixer Routing + */ +#define WM8995_ADCR_TO_DAC1R 0x0020 /* ADCR_TO_DAC1R */ +#define WM8995_ADCR_TO_DAC1R_MASK 0x0020 /* ADCR_TO_DAC1R */ +#define WM8995_ADCR_TO_DAC1R_SHIFT 5 /* ADCR_TO_DAC1R */ +#define WM8995_ADCR_TO_DAC1R_WIDTH 1 /* ADCR_TO_DAC1R */ +#define WM8995_ADCL_TO_DAC1R 0x0010 /* ADCL_TO_DAC1R */ +#define WM8995_ADCL_TO_DAC1R_MASK 0x0010 /* ADCL_TO_DAC1R */ +#define WM8995_ADCL_TO_DAC1R_SHIFT 4 /* ADCL_TO_DAC1R */ +#define WM8995_ADCL_TO_DAC1R_WIDTH 1 /* ADCL_TO_DAC1R */ +#define WM8995_AIF2DACR_TO_DAC1R 0x0004 /* AIF2DACR_TO_DAC1R */ +#define WM8995_AIF2DACR_TO_DAC1R_MASK 0x0004 /* AIF2DACR_TO_DAC1R */ +#define WM8995_AIF2DACR_TO_DAC1R_SHIFT 2 /* AIF2DACR_TO_DAC1R */ +#define WM8995_AIF2DACR_TO_DAC1R_WIDTH 1 /* AIF2DACR_TO_DAC1R */ +#define WM8995_AIF1DAC2R_TO_DAC1R 0x0002 /* AIF1DAC2R_TO_DAC1R */ +#define WM8995_AIF1DAC2R_TO_DAC1R_MASK 0x0002 /* AIF1DAC2R_TO_DAC1R */ +#define WM8995_AIF1DAC2R_TO_DAC1R_SHIFT 1 /* AIF1DAC2R_TO_DAC1R */ +#define WM8995_AIF1DAC2R_TO_DAC1R_WIDTH 1 /* AIF1DAC2R_TO_DAC1R */ +#define WM8995_AIF1DAC1R_TO_DAC1R 0x0001 /* AIF1DAC1R_TO_DAC1R */ +#define WM8995_AIF1DAC1R_TO_DAC1R_MASK 0x0001 /* AIF1DAC1R_TO_DAC1R */ +#define WM8995_AIF1DAC1R_TO_DAC1R_SHIFT 0 /* AIF1DAC1R_TO_DAC1R */ +#define WM8995_AIF1DAC1R_TO_DAC1R_WIDTH 1 /* AIF1DAC1R_TO_DAC1R */ + +/* + * R1539 (0x603) - DAC2 Mixer Volumes + */ +#define WM8995_ADCR_DAC2_VOL_MASK 0x03E0 /* ADCR_DAC2_VOL - [9:5] */ +#define WM8995_ADCR_DAC2_VOL_SHIFT 5 /* ADCR_DAC2_VOL - [9:5] */ +#define WM8995_ADCR_DAC2_VOL_WIDTH 5 /* ADCR_DAC2_VOL - [9:5] */ +#define WM8995_ADCL_DAC2_VOL_MASK 0x001F /* ADCL_DAC2_VOL - [4:0] */ +#define WM8995_ADCL_DAC2_VOL_SHIFT 0 /* ADCL_DAC2_VOL - [4:0] */ +#define WM8995_ADCL_DAC2_VOL_WIDTH 5 /* ADCL_DAC2_VOL - [4:0] */ + +/* + * R1540 (0x604) - DAC2 Left Mixer Routing + */ +#define WM8995_ADCR_TO_DAC2L 0x0020 /* ADCR_TO_DAC2L */ +#define WM8995_ADCR_TO_DAC2L_MASK 0x0020 /* ADCR_TO_DAC2L */ +#define WM8995_ADCR_TO_DAC2L_SHIFT 5 /* ADCR_TO_DAC2L */ +#define WM8995_ADCR_TO_DAC2L_WIDTH 1 /* ADCR_TO_DAC2L */ +#define WM8995_ADCL_TO_DAC2L 0x0010 /* ADCL_TO_DAC2L */ +#define WM8995_ADCL_TO_DAC2L_MASK 0x0010 /* ADCL_TO_DAC2L */ +#define WM8995_ADCL_TO_DAC2L_SHIFT 4 /* ADCL_TO_DAC2L */ +#define WM8995_ADCL_TO_DAC2L_WIDTH 1 /* ADCL_TO_DAC2L */ +#define WM8995_AIF2DACL_TO_DAC2L 0x0004 /* AIF2DACL_TO_DAC2L */ +#define WM8995_AIF2DACL_TO_DAC2L_MASK 0x0004 /* AIF2DACL_TO_DAC2L */ +#define WM8995_AIF2DACL_TO_DAC2L_SHIFT 2 /* AIF2DACL_TO_DAC2L */ +#define WM8995_AIF2DACL_TO_DAC2L_WIDTH 1 /* AIF2DACL_TO_DAC2L */ +#define WM8995_AIF1DAC2L_TO_DAC2L 0x0002 /* AIF1DAC2L_TO_DAC2L */ +#define WM8995_AIF1DAC2L_TO_DAC2L_MASK 0x0002 /* AIF1DAC2L_TO_DAC2L */ +#define WM8995_AIF1DAC2L_TO_DAC2L_SHIFT 1 /* AIF1DAC2L_TO_DAC2L */ +#define WM8995_AIF1DAC2L_TO_DAC2L_WIDTH 1 /* AIF1DAC2L_TO_DAC2L */ +#define WM8995_AIF1DAC1L_TO_DAC2L 0x0001 /* AIF1DAC1L_TO_DAC2L */ +#define WM8995_AIF1DAC1L_TO_DAC2L_MASK 0x0001 /* AIF1DAC1L_TO_DAC2L */ +#define WM8995_AIF1DAC1L_TO_DAC2L_SHIFT 0 /* AIF1DAC1L_TO_DAC2L */ +#define WM8995_AIF1DAC1L_TO_DAC2L_WIDTH 1 /* AIF1DAC1L_TO_DAC2L */ + +/* + * R1541 (0x605) - DAC2 Right Mixer Routing + */ +#define WM8995_ADCR_TO_DAC2R 0x0020 /* ADCR_TO_DAC2R */ +#define WM8995_ADCR_TO_DAC2R_MASK 0x0020 /* ADCR_TO_DAC2R */ +#define WM8995_ADCR_TO_DAC2R_SHIFT 5 /* ADCR_TO_DAC2R */ +#define WM8995_ADCR_TO_DAC2R_WIDTH 1 /* ADCR_TO_DAC2R */ +#define WM8995_ADCL_TO_DAC2R 0x0010 /* ADCL_TO_DAC2R */ +#define WM8995_ADCL_TO_DAC2R_MASK 0x0010 /* ADCL_TO_DAC2R */ +#define WM8995_ADCL_TO_DAC2R_SHIFT 4 /* ADCL_TO_DAC2R */ +#define WM8995_ADCL_TO_DAC2R_WIDTH 1 /* ADCL_TO_DAC2R */ +#define WM8995_AIF2DACR_TO_DAC2R 0x0004 /* AIF2DACR_TO_DAC2R */ +#define WM8995_AIF2DACR_TO_DAC2R_MASK 0x0004 /* AIF2DACR_TO_DAC2R */ +#define WM8995_AIF2DACR_TO_DAC2R_SHIFT 2 /* AIF2DACR_TO_DAC2R */ +#define WM8995_AIF2DACR_TO_DAC2R_WIDTH 1 /* AIF2DACR_TO_DAC2R */ +#define WM8995_AIF1DAC2R_TO_DAC2R 0x0002 /* AIF1DAC2R_TO_DAC2R */ +#define WM8995_AIF1DAC2R_TO_DAC2R_MASK 0x0002 /* AIF1DAC2R_TO_DAC2R */ +#define WM8995_AIF1DAC2R_TO_DAC2R_SHIFT 1 /* AIF1DAC2R_TO_DAC2R */ +#define WM8995_AIF1DAC2R_TO_DAC2R_WIDTH 1 /* AIF1DAC2R_TO_DAC2R */ +#define WM8995_AIF1DAC1R_TO_DAC2R 0x0001 /* AIF1DAC1R_TO_DAC2R */ +#define WM8995_AIF1DAC1R_TO_DAC2R_MASK 0x0001 /* AIF1DAC1R_TO_DAC2R */ +#define WM8995_AIF1DAC1R_TO_DAC2R_SHIFT 0 /* AIF1DAC1R_TO_DAC2R */ +#define WM8995_AIF1DAC1R_TO_DAC2R_WIDTH 1 /* AIF1DAC1R_TO_DAC2R */ + +/* + * R1542 (0x606) - AIF1 ADC1 Left Mixer Routing + */ +#define WM8995_ADC1L_TO_AIF1ADC1L 0x0002 /* ADC1L_TO_AIF1ADC1L */ +#define WM8995_ADC1L_TO_AIF1ADC1L_MASK 0x0002 /* ADC1L_TO_AIF1ADC1L */ +#define WM8995_ADC1L_TO_AIF1ADC1L_SHIFT 1 /* ADC1L_TO_AIF1ADC1L */ +#define WM8995_ADC1L_TO_AIF1ADC1L_WIDTH 1 /* ADC1L_TO_AIF1ADC1L */ +#define WM8995_AIF2DACL_TO_AIF1ADC1L 0x0001 /* AIF2DACL_TO_AIF1ADC1L */ +#define WM8995_AIF2DACL_TO_AIF1ADC1L_MASK 0x0001 /* AIF2DACL_TO_AIF1ADC1L */ +#define WM8995_AIF2DACL_TO_AIF1ADC1L_SHIFT 0 /* AIF2DACL_TO_AIF1ADC1L */ +#define WM8995_AIF2DACL_TO_AIF1ADC1L_WIDTH 1 /* AIF2DACL_TO_AIF1ADC1L */ + +/* + * R1543 (0x607) - AIF1 ADC1 Right Mixer Routing + */ +#define WM8995_ADC1R_TO_AIF1ADC1R 0x0002 /* ADC1R_TO_AIF1ADC1R */ +#define WM8995_ADC1R_TO_AIF1ADC1R_MASK 0x0002 /* ADC1R_TO_AIF1ADC1R */ +#define WM8995_ADC1R_TO_AIF1ADC1R_SHIFT 1 /* ADC1R_TO_AIF1ADC1R */ +#define WM8995_ADC1R_TO_AIF1ADC1R_WIDTH 1 /* ADC1R_TO_AIF1ADC1R */ +#define WM8995_AIF2DACR_TO_AIF1ADC1R 0x0001 /* AIF2DACR_TO_AIF1ADC1R */ +#define WM8995_AIF2DACR_TO_AIF1ADC1R_MASK 0x0001 /* AIF2DACR_TO_AIF1ADC1R */ +#define WM8995_AIF2DACR_TO_AIF1ADC1R_SHIFT 0 /* AIF2DACR_TO_AIF1ADC1R */ +#define WM8995_AIF2DACR_TO_AIF1ADC1R_WIDTH 1 /* AIF2DACR_TO_AIF1ADC1R */ + +/* + * R1544 (0x608) - AIF1 ADC2 Left Mixer Routing + */ +#define WM8995_ADC2L_TO_AIF1ADC2L 0x0002 /* ADC2L_TO_AIF1ADC2L */ +#define WM8995_ADC2L_TO_AIF1ADC2L_MASK 0x0002 /* ADC2L_TO_AIF1ADC2L */ +#define WM8995_ADC2L_TO_AIF1ADC2L_SHIFT 1 /* ADC2L_TO_AIF1ADC2L */ +#define WM8995_ADC2L_TO_AIF1ADC2L_WIDTH 1 /* ADC2L_TO_AIF1ADC2L */ +#define WM8995_AIF2DACL_TO_AIF1ADC2L 0x0001 /* AIF2DACL_TO_AIF1ADC2L */ +#define WM8995_AIF2DACL_TO_AIF1ADC2L_MASK 0x0001 /* AIF2DACL_TO_AIF1ADC2L */ +#define WM8995_AIF2DACL_TO_AIF1ADC2L_SHIFT 0 /* AIF2DACL_TO_AIF1ADC2L */ +#define WM8995_AIF2DACL_TO_AIF1ADC2L_WIDTH 1 /* AIF2DACL_TO_AIF1ADC2L */ + +/* + * R1545 (0x609) - AIF1 ADC2 Right mixer Routing + */ +#define WM8995_ADC2R_TO_AIF1ADC2R 0x0002 /* ADC2R_TO_AIF1ADC2R */ +#define WM8995_ADC2R_TO_AIF1ADC2R_MASK 0x0002 /* ADC2R_TO_AIF1ADC2R */ +#define WM8995_ADC2R_TO_AIF1ADC2R_SHIFT 1 /* ADC2R_TO_AIF1ADC2R */ +#define WM8995_ADC2R_TO_AIF1ADC2R_WIDTH 1 /* ADC2R_TO_AIF1ADC2R */ +#define WM8995_AIF2DACR_TO_AIF1ADC2R 0x0001 /* AIF2DACR_TO_AIF1ADC2R */ +#define WM8995_AIF2DACR_TO_AIF1ADC2R_MASK 0x0001 /* AIF2DACR_TO_AIF1ADC2R */ +#define WM8995_AIF2DACR_TO_AIF1ADC2R_SHIFT 0 /* AIF2DACR_TO_AIF1ADC2R */ +#define WM8995_AIF2DACR_TO_AIF1ADC2R_WIDTH 1 /* AIF2DACR_TO_AIF1ADC2R */ + +/* + * R1552 (0x610) - DAC Softmute + */ +#define WM8995_DAC_SOFTMUTEMODE 0x0002 /* DAC_SOFTMUTEMODE */ +#define WM8995_DAC_SOFTMUTEMODE_MASK 0x0002 /* DAC_SOFTMUTEMODE */ +#define WM8995_DAC_SOFTMUTEMODE_SHIFT 1 /* DAC_SOFTMUTEMODE */ +#define WM8995_DAC_SOFTMUTEMODE_WIDTH 1 /* DAC_SOFTMUTEMODE */ +#define WM8995_DAC_MUTERATE 0x0001 /* DAC_MUTERATE */ +#define WM8995_DAC_MUTERATE_MASK 0x0001 /* DAC_MUTERATE */ +#define WM8995_DAC_MUTERATE_SHIFT 0 /* DAC_MUTERATE */ +#define WM8995_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ + +/* + * R1568 (0x620) - Oversampling + */ +#define WM8995_ADC_OSR128 0x0002 /* ADC_OSR128 */ +#define WM8995_ADC_OSR128_MASK 0x0002 /* ADC_OSR128 */ +#define WM8995_ADC_OSR128_SHIFT 1 /* ADC_OSR128 */ +#define WM8995_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */ +#define WM8995_DAC_OSR128 0x0001 /* DAC_OSR128 */ +#define WM8995_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */ +#define WM8995_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */ +#define WM8995_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */ + +/* + * R1569 (0x621) - Sidetone + */ +#define WM8995_ST_LPF 0x1000 /* ST_LPF */ +#define WM8995_ST_LPF_MASK 0x1000 /* ST_LPF */ +#define WM8995_ST_LPF_SHIFT 12 /* ST_LPF */ +#define WM8995_ST_LPF_WIDTH 1 /* ST_LPF */ +#define WM8995_ST_HPF_CUT_MASK 0x0380 /* ST_HPF_CUT - [9:7] */ +#define WM8995_ST_HPF_CUT_SHIFT 7 /* ST_HPF_CUT - [9:7] */ +#define WM8995_ST_HPF_CUT_WIDTH 3 /* ST_HPF_CUT - [9:7] */ +#define WM8995_ST_HPF 0x0040 /* ST_HPF */ +#define WM8995_ST_HPF_MASK 0x0040 /* ST_HPF */ +#define WM8995_ST_HPF_SHIFT 6 /* ST_HPF */ +#define WM8995_ST_HPF_WIDTH 1 /* ST_HPF */ +#define WM8995_STR_SEL 0x0002 /* STR_SEL */ +#define WM8995_STR_SEL_MASK 0x0002 /* STR_SEL */ +#define WM8995_STR_SEL_SHIFT 1 /* STR_SEL */ +#define WM8995_STR_SEL_WIDTH 1 /* STR_SEL */ +#define WM8995_STL_SEL 0x0001 /* STL_SEL */ +#define WM8995_STL_SEL_MASK 0x0001 /* STL_SEL */ +#define WM8995_STL_SEL_SHIFT 0 /* STL_SEL */ +#define WM8995_STL_SEL_WIDTH 1 /* STL_SEL */ + +/* + * R1792 (0x700) - GPIO 1 + */ +#define WM8995_GP1_DIR 0x8000 /* GP1_DIR */ +#define WM8995_GP1_DIR_MASK 0x8000 /* GP1_DIR */ +#define WM8995_GP1_DIR_SHIFT 15 /* GP1_DIR */ +#define WM8995_GP1_DIR_WIDTH 1 /* GP1_DIR */ +#define WM8995_GP1_PU 0x4000 /* GP1_PU */ +#define WM8995_GP1_PU_MASK 0x4000 /* GP1_PU */ +#define WM8995_GP1_PU_SHIFT 14 /* GP1_PU */ +#define WM8995_GP1_PU_WIDTH 1 /* GP1_PU */ +#define WM8995_GP1_PD 0x2000 /* GP1_PD */ +#define WM8995_GP1_PD_MASK 0x2000 /* GP1_PD */ +#define WM8995_GP1_PD_SHIFT 13 /* GP1_PD */ +#define WM8995_GP1_PD_WIDTH 1 /* GP1_PD */ +#define WM8995_GP1_POL 0x0400 /* GP1_POL */ +#define WM8995_GP1_POL_MASK 0x0400 /* GP1_POL */ +#define WM8995_GP1_POL_SHIFT 10 /* GP1_POL */ +#define WM8995_GP1_POL_WIDTH 1 /* GP1_POL */ +#define WM8995_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */ +#define WM8995_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */ +#define WM8995_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */ +#define WM8995_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */ +#define WM8995_GP1_DB 0x0100 /* GP1_DB */ +#define WM8995_GP1_DB_MASK 0x0100 /* GP1_DB */ +#define WM8995_GP1_DB_SHIFT 8 /* GP1_DB */ +#define WM8995_GP1_DB_WIDTH 1 /* GP1_DB */ +#define WM8995_GP1_LVL 0x0040 /* GP1_LVL */ +#define WM8995_GP1_LVL_MASK 0x0040 /* GP1_LVL */ +#define WM8995_GP1_LVL_SHIFT 6 /* GP1_LVL */ +#define WM8995_GP1_LVL_WIDTH 1 /* GP1_LVL */ +#define WM8995_GP1_FN_MASK 0x001F /* GP1_FN - [4:0] */ +#define WM8995_GP1_FN_SHIFT 0 /* GP1_FN - [4:0] */ +#define WM8995_GP1_FN_WIDTH 5 /* GP1_FN - [4:0] */ + +/* + * R1793 (0x701) - GPIO 2 + */ +#define WM8995_GP2_DIR 0x8000 /* GP2_DIR */ +#define WM8995_GP2_DIR_MASK 0x8000 /* GP2_DIR */ +#define WM8995_GP2_DIR_SHIFT 15 /* GP2_DIR */ +#define WM8995_GP2_DIR_WIDTH 1 /* GP2_DIR */ +#define WM8995_GP2_PU 0x4000 /* GP2_PU */ +#define WM8995_GP2_PU_MASK 0x4000 /* GP2_PU */ +#define WM8995_GP2_PU_SHIFT 14 /* GP2_PU */ +#define WM8995_GP2_PU_WIDTH 1 /* GP2_PU */ +#define WM8995_GP2_PD 0x2000 /* GP2_PD */ +#define WM8995_GP2_PD_MASK 0x2000 /* GP2_PD */ +#define WM8995_GP2_PD_SHIFT 13 /* GP2_PD */ +#define WM8995_GP2_PD_WIDTH 1 /* GP2_PD */ +#define WM8995_GP2_POL 0x0400 /* GP2_POL */ +#define WM8995_GP2_POL_MASK 0x0400 /* GP2_POL */ +#define WM8995_GP2_POL_SHIFT 10 /* GP2_POL */ +#define WM8995_GP2_POL_WIDTH 1 /* GP2_POL */ +#define WM8995_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */ +#define WM8995_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */ +#define WM8995_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */ +#define WM8995_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */ +#define WM8995_GP2_DB 0x0100 /* GP2_DB */ +#define WM8995_GP2_DB_MASK 0x0100 /* GP2_DB */ +#define WM8995_GP2_DB_SHIFT 8 /* GP2_DB */ +#define WM8995_GP2_DB_WIDTH 1 /* GP2_DB */ +#define WM8995_GP2_LVL 0x0040 /* GP2_LVL */ +#define WM8995_GP2_LVL_MASK 0x0040 /* GP2_LVL */ +#define WM8995_GP2_LVL_SHIFT 6 /* GP2_LVL */ +#define WM8995_GP2_LVL_WIDTH 1 /* GP2_LVL */ +#define WM8995_GP2_FN_MASK 0x001F /* GP2_FN - [4:0] */ +#define WM8995_GP2_FN_SHIFT 0 /* GP2_FN - [4:0] */ +#define WM8995_GP2_FN_WIDTH 5 /* GP2_FN - [4:0] */ + +/* + * R1794 (0x702) - GPIO 3 + */ +#define WM8995_GP3_DIR 0x8000 /* GP3_DIR */ +#define WM8995_GP3_DIR_MASK 0x8000 /* GP3_DIR */ +#define WM8995_GP3_DIR_SHIFT 15 /* GP3_DIR */ +#define WM8995_GP3_DIR_WIDTH 1 /* GP3_DIR */ +#define WM8995_GP3_PU 0x4000 /* GP3_PU */ +#define WM8995_GP3_PU_MASK 0x4000 /* GP3_PU */ +#define WM8995_GP3_PU_SHIFT 14 /* GP3_PU */ +#define WM8995_GP3_PU_WIDTH 1 /* GP3_PU */ +#define WM8995_GP3_PD 0x2000 /* GP3_PD */ +#define WM8995_GP3_PD_MASK 0x2000 /* GP3_PD */ +#define WM8995_GP3_PD_SHIFT 13 /* GP3_PD */ +#define WM8995_GP3_PD_WIDTH 1 /* GP3_PD */ +#define WM8995_GP3_POL 0x0400 /* GP3_POL */ +#define WM8995_GP3_POL_MASK 0x0400 /* GP3_POL */ +#define WM8995_GP3_POL_SHIFT 10 /* GP3_POL */ +#define WM8995_GP3_POL_WIDTH 1 /* GP3_POL */ +#define WM8995_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */ +#define WM8995_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */ +#define WM8995_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */ +#define WM8995_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */ +#define WM8995_GP3_DB 0x0100 /* GP3_DB */ +#define WM8995_GP3_DB_MASK 0x0100 /* GP3_DB */ +#define WM8995_GP3_DB_SHIFT 8 /* GP3_DB */ +#define WM8995_GP3_DB_WIDTH 1 /* GP3_DB */ +#define WM8995_GP3_LVL 0x0040 /* GP3_LVL */ +#define WM8995_GP3_LVL_MASK 0x0040 /* GP3_LVL */ +#define WM8995_GP3_LVL_SHIFT 6 /* GP3_LVL */ +#define WM8995_GP3_LVL_WIDTH 1 /* GP3_LVL */ +#define WM8995_GP3_FN_MASK 0x001F /* GP3_FN - [4:0] */ +#define WM8995_GP3_FN_SHIFT 0 /* GP3_FN - [4:0] */ +#define WM8995_GP3_FN_WIDTH 5 /* GP3_FN - [4:0] */ + +/* + * R1795 (0x703) - GPIO 4 + */ +#define WM8995_GP4_DIR 0x8000 /* GP4_DIR */ +#define WM8995_GP4_DIR_MASK 0x8000 /* GP4_DIR */ +#define WM8995_GP4_DIR_SHIFT 15 /* GP4_DIR */ +#define WM8995_GP4_DIR_WIDTH 1 /* GP4_DIR */ +#define WM8995_GP4_PU 0x4000 /* GP4_PU */ +#define WM8995_GP4_PU_MASK 0x4000 /* GP4_PU */ +#define WM8995_GP4_PU_SHIFT 14 /* GP4_PU */ +#define WM8995_GP4_PU_WIDTH 1 /* GP4_PU */ +#define WM8995_GP4_PD 0x2000 /* GP4_PD */ +#define WM8995_GP4_PD_MASK 0x2000 /* GP4_PD */ +#define WM8995_GP4_PD_SHIFT 13 /* GP4_PD */ +#define WM8995_GP4_PD_WIDTH 1 /* GP4_PD */ +#define WM8995_GP4_POL 0x0400 /* GP4_POL */ +#define WM8995_GP4_POL_MASK 0x0400 /* GP4_POL */ +#define WM8995_GP4_POL_SHIFT 10 /* GP4_POL */ +#define WM8995_GP4_POL_WIDTH 1 /* GP4_POL */ +#define WM8995_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */ +#define WM8995_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */ +#define WM8995_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */ +#define WM8995_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */ +#define WM8995_GP4_DB 0x0100 /* GP4_DB */ +#define WM8995_GP4_DB_MASK 0x0100 /* GP4_DB */ +#define WM8995_GP4_DB_SHIFT 8 /* GP4_DB */ +#define WM8995_GP4_DB_WIDTH 1 /* GP4_DB */ +#define WM8995_GP4_LVL 0x0040 /* GP4_LVL */ +#define WM8995_GP4_LVL_MASK 0x0040 /* GP4_LVL */ +#define WM8995_GP4_LVL_SHIFT 6 /* GP4_LVL */ +#define WM8995_GP4_LVL_WIDTH 1 /* GP4_LVL */ +#define WM8995_GP4_FN_MASK 0x001F /* GP4_FN - [4:0] */ +#define WM8995_GP4_FN_SHIFT 0 /* GP4_FN - [4:0] */ +#define WM8995_GP4_FN_WIDTH 5 /* GP4_FN - [4:0] */ + +/* + * R1796 (0x704) - GPIO 5 + */ +#define WM8995_GP5_DIR 0x8000 /* GP5_DIR */ +#define WM8995_GP5_DIR_MASK 0x8000 /* GP5_DIR */ +#define WM8995_GP5_DIR_SHIFT 15 /* GP5_DIR */ +#define WM8995_GP5_DIR_WIDTH 1 /* GP5_DIR */ +#define WM8995_GP5_PU 0x4000 /* GP5_PU */ +#define WM8995_GP5_PU_MASK 0x4000 /* GP5_PU */ +#define WM8995_GP5_PU_SHIFT 14 /* GP5_PU */ +#define WM8995_GP5_PU_WIDTH 1 /* GP5_PU */ +#define WM8995_GP5_PD 0x2000 /* GP5_PD */ +#define WM8995_GP5_PD_MASK 0x2000 /* GP5_PD */ +#define WM8995_GP5_PD_SHIFT 13 /* GP5_PD */ +#define WM8995_GP5_PD_WIDTH 1 /* GP5_PD */ +#define WM8995_GP5_POL 0x0400 /* GP5_POL */ +#define WM8995_GP5_POL_MASK 0x0400 /* GP5_POL */ +#define WM8995_GP5_POL_SHIFT 10 /* GP5_POL */ +#define WM8995_GP5_POL_WIDTH 1 /* GP5_POL */ +#define WM8995_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */ +#define WM8995_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */ +#define WM8995_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */ +#define WM8995_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */ +#define WM8995_GP5_DB 0x0100 /* GP5_DB */ +#define WM8995_GP5_DB_MASK 0x0100 /* GP5_DB */ +#define WM8995_GP5_DB_SHIFT 8 /* GP5_DB */ +#define WM8995_GP5_DB_WIDTH 1 /* GP5_DB */ +#define WM8995_GP5_LVL 0x0040 /* GP5_LVL */ +#define WM8995_GP5_LVL_MASK 0x0040 /* GP5_LVL */ +#define WM8995_GP5_LVL_SHIFT 6 /* GP5_LVL */ +#define WM8995_GP5_LVL_WIDTH 1 /* GP5_LVL */ +#define WM8995_GP5_FN_MASK 0x001F /* GP5_FN - [4:0] */ +#define WM8995_GP5_FN_SHIFT 0 /* GP5_FN - [4:0] */ +#define WM8995_GP5_FN_WIDTH 5 /* GP5_FN - [4:0] */ + +/* + * R1797 (0x705) - GPIO 6 + */ +#define WM8995_GP6_DIR 0x8000 /* GP6_DIR */ +#define WM8995_GP6_DIR_MASK 0x8000 /* GP6_DIR */ +#define WM8995_GP6_DIR_SHIFT 15 /* GP6_DIR */ +#define WM8995_GP6_DIR_WIDTH 1 /* GP6_DIR */ +#define WM8995_GP6_PU 0x4000 /* GP6_PU */ +#define WM8995_GP6_PU_MASK 0x4000 /* GP6_PU */ +#define WM8995_GP6_PU_SHIFT 14 /* GP6_PU */ +#define WM8995_GP6_PU_WIDTH 1 /* GP6_PU */ +#define WM8995_GP6_PD 0x2000 /* GP6_PD */ +#define WM8995_GP6_PD_MASK 0x2000 /* GP6_PD */ +#define WM8995_GP6_PD_SHIFT 13 /* GP6_PD */ +#define WM8995_GP6_PD_WIDTH 1 /* GP6_PD */ +#define WM8995_GP6_POL 0x0400 /* GP6_POL */ +#define WM8995_GP6_POL_MASK 0x0400 /* GP6_POL */ +#define WM8995_GP6_POL_SHIFT 10 /* GP6_POL */ +#define WM8995_GP6_POL_WIDTH 1 /* GP6_POL */ +#define WM8995_GP6_OP_CFG 0x0200 /* GP6_OP_CFG */ +#define WM8995_GP6_OP_CFG_MASK 0x0200 /* GP6_OP_CFG */ +#define WM8995_GP6_OP_CFG_SHIFT 9 /* GP6_OP_CFG */ +#define WM8995_GP6_OP_CFG_WIDTH 1 /* GP6_OP_CFG */ +#define WM8995_GP6_DB 0x0100 /* GP6_DB */ +#define WM8995_GP6_DB_MASK 0x0100 /* GP6_DB */ +#define WM8995_GP6_DB_SHIFT 8 /* GP6_DB */ +#define WM8995_GP6_DB_WIDTH 1 /* GP6_DB */ +#define WM8995_GP6_LVL 0x0040 /* GP6_LVL */ +#define WM8995_GP6_LVL_MASK 0x0040 /* GP6_LVL */ +#define WM8995_GP6_LVL_SHIFT 6 /* GP6_LVL */ +#define WM8995_GP6_LVL_WIDTH 1 /* GP6_LVL */ +#define WM8995_GP6_FN_MASK 0x001F /* GP6_FN - [4:0] */ +#define WM8995_GP6_FN_SHIFT 0 /* GP6_FN - [4:0] */ +#define WM8995_GP6_FN_WIDTH 5 /* GP6_FN - [4:0] */ + +/* + * R1798 (0x706) - GPIO 7 + */ +#define WM8995_GP7_DIR 0x8000 /* GP7_DIR */ +#define WM8995_GP7_DIR_MASK 0x8000 /* GP7_DIR */ +#define WM8995_GP7_DIR_SHIFT 15 /* GP7_DIR */ +#define WM8995_GP7_DIR_WIDTH 1 /* GP7_DIR */ +#define WM8995_GP7_PU 0x4000 /* GP7_PU */ +#define WM8995_GP7_PU_MASK 0x4000 /* GP7_PU */ +#define WM8995_GP7_PU_SHIFT 14 /* GP7_PU */ +#define WM8995_GP7_PU_WIDTH 1 /* GP7_PU */ +#define WM8995_GP7_PD 0x2000 /* GP7_PD */ +#define WM8995_GP7_PD_MASK 0x2000 /* GP7_PD */ +#define WM8995_GP7_PD_SHIFT 13 /* GP7_PD */ +#define WM8995_GP7_PD_WIDTH 1 /* GP7_PD */ +#define WM8995_GP7_POL 0x0400 /* GP7_POL */ +#define WM8995_GP7_POL_MASK 0x0400 /* GP7_POL */ +#define WM8995_GP7_POL_SHIFT 10 /* GP7_POL */ +#define WM8995_GP7_POL_WIDTH 1 /* GP7_POL */ +#define WM8995_GP7_OP_CFG 0x0200 /* GP7_OP_CFG */ +#define WM8995_GP7_OP_CFG_MASK 0x0200 /* GP7_OP_CFG */ +#define WM8995_GP7_OP_CFG_SHIFT 9 /* GP7_OP_CFG */ +#define WM8995_GP7_OP_CFG_WIDTH 1 /* GP7_OP_CFG */ +#define WM8995_GP7_DB 0x0100 /* GP7_DB */ +#define WM8995_GP7_DB_MASK 0x0100 /* GP7_DB */ +#define WM8995_GP7_DB_SHIFT 8 /* GP7_DB */ +#define WM8995_GP7_DB_WIDTH 1 /* GP7_DB */ +#define WM8995_GP7_LVL 0x0040 /* GP7_LVL */ +#define WM8995_GP7_LVL_MASK 0x0040 /* GP7_LVL */ +#define WM8995_GP7_LVL_SHIFT 6 /* GP7_LVL */ +#define WM8995_GP7_LVL_WIDTH 1 /* GP7_LVL */ +#define WM8995_GP7_FN_MASK 0x001F /* GP7_FN - [4:0] */ +#define WM8995_GP7_FN_SHIFT 0 /* GP7_FN - [4:0] */ +#define WM8995_GP7_FN_WIDTH 5 /* GP7_FN - [4:0] */ + +/* + * R1799 (0x707) - GPIO 8 + */ +#define WM8995_GP8_DIR 0x8000 /* GP8_DIR */ +#define WM8995_GP8_DIR_MASK 0x8000 /* GP8_DIR */ +#define WM8995_GP8_DIR_SHIFT 15 /* GP8_DIR */ +#define WM8995_GP8_DIR_WIDTH 1 /* GP8_DIR */ +#define WM8995_GP8_PU 0x4000 /* GP8_PU */ +#define WM8995_GP8_PU_MASK 0x4000 /* GP8_PU */ +#define WM8995_GP8_PU_SHIFT 14 /* GP8_PU */ +#define WM8995_GP8_PU_WIDTH 1 /* GP8_PU */ +#define WM8995_GP8_PD 0x2000 /* GP8_PD */ +#define WM8995_GP8_PD_MASK 0x2000 /* GP8_PD */ +#define WM8995_GP8_PD_SHIFT 13 /* GP8_PD */ +#define WM8995_GP8_PD_WIDTH 1 /* GP8_PD */ +#define WM8995_GP8_POL 0x0400 /* GP8_POL */ +#define WM8995_GP8_POL_MASK 0x0400 /* GP8_POL */ +#define WM8995_GP8_POL_SHIFT 10 /* GP8_POL */ +#define WM8995_GP8_POL_WIDTH 1 /* GP8_POL */ +#define WM8995_GP8_OP_CFG 0x0200 /* GP8_OP_CFG */ +#define WM8995_GP8_OP_CFG_MASK 0x0200 /* GP8_OP_CFG */ +#define WM8995_GP8_OP_CFG_SHIFT 9 /* GP8_OP_CFG */ +#define WM8995_GP8_OP_CFG_WIDTH 1 /* GP8_OP_CFG */ +#define WM8995_GP8_DB 0x0100 /* GP8_DB */ +#define WM8995_GP8_DB_MASK 0x0100 /* GP8_DB */ +#define WM8995_GP8_DB_SHIFT 8 /* GP8_DB */ +#define WM8995_GP8_DB_WIDTH 1 /* GP8_DB */ +#define WM8995_GP8_LVL 0x0040 /* GP8_LVL */ +#define WM8995_GP8_LVL_MASK 0x0040 /* GP8_LVL */ +#define WM8995_GP8_LVL_SHIFT 6 /* GP8_LVL */ +#define WM8995_GP8_LVL_WIDTH 1 /* GP8_LVL */ +#define WM8995_GP8_FN_MASK 0x001F /* GP8_FN - [4:0] */ +#define WM8995_GP8_FN_SHIFT 0 /* GP8_FN - [4:0] */ +#define WM8995_GP8_FN_WIDTH 5 /* GP8_FN - [4:0] */ + +/* + * R1800 (0x708) - GPIO 9 + */ +#define WM8995_GP9_DIR 0x8000 /* GP9_DIR */ +#define WM8995_GP9_DIR_MASK 0x8000 /* GP9_DIR */ +#define WM8995_GP9_DIR_SHIFT 15 /* GP9_DIR */ +#define WM8995_GP9_DIR_WIDTH 1 /* GP9_DIR */ +#define WM8995_GP9_PU 0x4000 /* GP9_PU */ +#define WM8995_GP9_PU_MASK 0x4000 /* GP9_PU */ +#define WM8995_GP9_PU_SHIFT 14 /* GP9_PU */ +#define WM8995_GP9_PU_WIDTH 1 /* GP9_PU */ +#define WM8995_GP9_PD 0x2000 /* GP9_PD */ +#define WM8995_GP9_PD_MASK 0x2000 /* GP9_PD */ +#define WM8995_GP9_PD_SHIFT 13 /* GP9_PD */ +#define WM8995_GP9_PD_WIDTH 1 /* GP9_PD */ +#define WM8995_GP9_POL 0x0400 /* GP9_POL */ +#define WM8995_GP9_POL_MASK 0x0400 /* GP9_POL */ +#define WM8995_GP9_POL_SHIFT 10 /* GP9_POL */ +#define WM8995_GP9_POL_WIDTH 1 /* GP9_POL */ +#define WM8995_GP9_OP_CFG 0x0200 /* GP9_OP_CFG */ +#define WM8995_GP9_OP_CFG_MASK 0x0200 /* GP9_OP_CFG */ +#define WM8995_GP9_OP_CFG_SHIFT 9 /* GP9_OP_CFG */ +#define WM8995_GP9_OP_CFG_WIDTH 1 /* GP9_OP_CFG */ +#define WM8995_GP9_DB 0x0100 /* GP9_DB */ +#define WM8995_GP9_DB_MASK 0x0100 /* GP9_DB */ +#define WM8995_GP9_DB_SHIFT 8 /* GP9_DB */ +#define WM8995_GP9_DB_WIDTH 1 /* GP9_DB */ +#define WM8995_GP9_LVL 0x0040 /* GP9_LVL */ +#define WM8995_GP9_LVL_MASK 0x0040 /* GP9_LVL */ +#define WM8995_GP9_LVL_SHIFT 6 /* GP9_LVL */ +#define WM8995_GP9_LVL_WIDTH 1 /* GP9_LVL */ +#define WM8995_GP9_FN_MASK 0x001F /* GP9_FN - [4:0] */ +#define WM8995_GP9_FN_SHIFT 0 /* GP9_FN - [4:0] */ +#define WM8995_GP9_FN_WIDTH 5 /* GP9_FN - [4:0] */ + +/* + * R1801 (0x709) - GPIO 10 + */ +#define WM8995_GP10_DIR 0x8000 /* GP10_DIR */ +#define WM8995_GP10_DIR_MASK 0x8000 /* GP10_DIR */ +#define WM8995_GP10_DIR_SHIFT 15 /* GP10_DIR */ +#define WM8995_GP10_DIR_WIDTH 1 /* GP10_DIR */ +#define WM8995_GP10_PU 0x4000 /* GP10_PU */ +#define WM8995_GP10_PU_MASK 0x4000 /* GP10_PU */ +#define WM8995_GP10_PU_SHIFT 14 /* GP10_PU */ +#define WM8995_GP10_PU_WIDTH 1 /* GP10_PU */ +#define WM8995_GP10_PD 0x2000 /* GP10_PD */ +#define WM8995_GP10_PD_MASK 0x2000 /* GP10_PD */ +#define WM8995_GP10_PD_SHIFT 13 /* GP10_PD */ +#define WM8995_GP10_PD_WIDTH 1 /* GP10_PD */ +#define WM8995_GP10_POL 0x0400 /* GP10_POL */ +#define WM8995_GP10_POL_MASK 0x0400 /* GP10_POL */ +#define WM8995_GP10_POL_SHIFT 10 /* GP10_POL */ +#define WM8995_GP10_POL_WIDTH 1 /* GP10_POL */ +#define WM8995_GP10_OP_CFG 0x0200 /* GP10_OP_CFG */ +#define WM8995_GP10_OP_CFG_MASK 0x0200 /* GP10_OP_CFG */ +#define WM8995_GP10_OP_CFG_SHIFT 9 /* GP10_OP_CFG */ +#define WM8995_GP10_OP_CFG_WIDTH 1 /* GP10_OP_CFG */ +#define WM8995_GP10_DB 0x0100 /* GP10_DB */ +#define WM8995_GP10_DB_MASK 0x0100 /* GP10_DB */ +#define WM8995_GP10_DB_SHIFT 8 /* GP10_DB */ +#define WM8995_GP10_DB_WIDTH 1 /* GP10_DB */ +#define WM8995_GP10_LVL 0x0040 /* GP10_LVL */ +#define WM8995_GP10_LVL_MASK 0x0040 /* GP10_LVL */ +#define WM8995_GP10_LVL_SHIFT 6 /* GP10_LVL */ +#define WM8995_GP10_LVL_WIDTH 1 /* GP10_LVL */ +#define WM8995_GP10_FN_MASK 0x001F /* GP10_FN - [4:0] */ +#define WM8995_GP10_FN_SHIFT 0 /* GP10_FN - [4:0] */ +#define WM8995_GP10_FN_WIDTH 5 /* GP10_FN - [4:0] */ + +/* + * R1802 (0x70A) - GPIO 11 + */ +#define WM8995_GP11_DIR 0x8000 /* GP11_DIR */ +#define WM8995_GP11_DIR_MASK 0x8000 /* GP11_DIR */ +#define WM8995_GP11_DIR_SHIFT 15 /* GP11_DIR */ +#define WM8995_GP11_DIR_WIDTH 1 /* GP11_DIR */ +#define WM8995_GP11_PU 0x4000 /* GP11_PU */ +#define WM8995_GP11_PU_MASK 0x4000 /* GP11_PU */ +#define WM8995_GP11_PU_SHIFT 14 /* GP11_PU */ +#define WM8995_GP11_PU_WIDTH 1 /* GP11_PU */ +#define WM8995_GP11_PD 0x2000 /* GP11_PD */ +#define WM8995_GP11_PD_MASK 0x2000 /* GP11_PD */ +#define WM8995_GP11_PD_SHIFT 13 /* GP11_PD */ +#define WM8995_GP11_PD_WIDTH 1 /* GP11_PD */ +#define WM8995_GP11_POL 0x0400 /* GP11_POL */ +#define WM8995_GP11_POL_MASK 0x0400 /* GP11_POL */ +#define WM8995_GP11_POL_SHIFT 10 /* GP11_POL */ +#define WM8995_GP11_POL_WIDTH 1 /* GP11_POL */ +#define WM8995_GP11_OP_CFG 0x0200 /* GP11_OP_CFG */ +#define WM8995_GP11_OP_CFG_MASK 0x0200 /* GP11_OP_CFG */ +#define WM8995_GP11_OP_CFG_SHIFT 9 /* GP11_OP_CFG */ +#define WM8995_GP11_OP_CFG_WIDTH 1 /* GP11_OP_CFG */ +#define WM8995_GP11_DB 0x0100 /* GP11_DB */ +#define WM8995_GP11_DB_MASK 0x0100 /* GP11_DB */ +#define WM8995_GP11_DB_SHIFT 8 /* GP11_DB */ +#define WM8995_GP11_DB_WIDTH 1 /* GP11_DB */ +#define WM8995_GP11_LVL 0x0040 /* GP11_LVL */ +#define WM8995_GP11_LVL_MASK 0x0040 /* GP11_LVL */ +#define WM8995_GP11_LVL_SHIFT 6 /* GP11_LVL */ +#define WM8995_GP11_LVL_WIDTH 1 /* GP11_LVL */ +#define WM8995_GP11_FN_MASK 0x001F /* GP11_FN - [4:0] */ +#define WM8995_GP11_FN_SHIFT 0 /* GP11_FN - [4:0] */ +#define WM8995_GP11_FN_WIDTH 5 /* GP11_FN - [4:0] */ + +/* + * R1803 (0x70B) - GPIO 12 + */ +#define WM8995_GP12_DIR 0x8000 /* GP12_DIR */ +#define WM8995_GP12_DIR_MASK 0x8000 /* GP12_DIR */ +#define WM8995_GP12_DIR_SHIFT 15 /* GP12_DIR */ +#define WM8995_GP12_DIR_WIDTH 1 /* GP12_DIR */ +#define WM8995_GP12_PU 0x4000 /* GP12_PU */ +#define WM8995_GP12_PU_MASK 0x4000 /* GP12_PU */ +#define WM8995_GP12_PU_SHIFT 14 /* GP12_PU */ +#define WM8995_GP12_PU_WIDTH 1 /* GP12_PU */ +#define WM8995_GP12_PD 0x2000 /* GP12_PD */ +#define WM8995_GP12_PD_MASK 0x2000 /* GP12_PD */ +#define WM8995_GP12_PD_SHIFT 13 /* GP12_PD */ +#define WM8995_GP12_PD_WIDTH 1 /* GP12_PD */ +#define WM8995_GP12_POL 0x0400 /* GP12_POL */ +#define WM8995_GP12_POL_MASK 0x0400 /* GP12_POL */ +#define WM8995_GP12_POL_SHIFT 10 /* GP12_POL */ +#define WM8995_GP12_POL_WIDTH 1 /* GP12_POL */ +#define WM8995_GP12_OP_CFG 0x0200 /* GP12_OP_CFG */ +#define WM8995_GP12_OP_CFG_MASK 0x0200 /* GP12_OP_CFG */ +#define WM8995_GP12_OP_CFG_SHIFT 9 /* GP12_OP_CFG */ +#define WM8995_GP12_OP_CFG_WIDTH 1 /* GP12_OP_CFG */ +#define WM8995_GP12_DB 0x0100 /* GP12_DB */ +#define WM8995_GP12_DB_MASK 0x0100 /* GP12_DB */ +#define WM8995_GP12_DB_SHIFT 8 /* GP12_DB */ +#define WM8995_GP12_DB_WIDTH 1 /* GP12_DB */ +#define WM8995_GP12_LVL 0x0040 /* GP12_LVL */ +#define WM8995_GP12_LVL_MASK 0x0040 /* GP12_LVL */ +#define WM8995_GP12_LVL_SHIFT 6 /* GP12_LVL */ +#define WM8995_GP12_LVL_WIDTH 1 /* GP12_LVL */ +#define WM8995_GP12_FN_MASK 0x001F /* GP12_FN - [4:0] */ +#define WM8995_GP12_FN_SHIFT 0 /* GP12_FN - [4:0] */ +#define WM8995_GP12_FN_WIDTH 5 /* GP12_FN - [4:0] */ + +/* + * R1804 (0x70C) - GPIO 13 + */ +#define WM8995_GP13_DIR 0x8000 /* GP13_DIR */ +#define WM8995_GP13_DIR_MASK 0x8000 /* GP13_DIR */ +#define WM8995_GP13_DIR_SHIFT 15 /* GP13_DIR */ +#define WM8995_GP13_DIR_WIDTH 1 /* GP13_DIR */ +#define WM8995_GP13_PU 0x4000 /* GP13_PU */ +#define WM8995_GP13_PU_MASK 0x4000 /* GP13_PU */ +#define WM8995_GP13_PU_SHIFT 14 /* GP13_PU */ +#define WM8995_GP13_PU_WIDTH 1 /* GP13_PU */ +#define WM8995_GP13_PD 0x2000 /* GP13_PD */ +#define WM8995_GP13_PD_MASK 0x2000 /* GP13_PD */ +#define WM8995_GP13_PD_SHIFT 13 /* GP13_PD */ +#define WM8995_GP13_PD_WIDTH 1 /* GP13_PD */ +#define WM8995_GP13_POL 0x0400 /* GP13_POL */ +#define WM8995_GP13_POL_MASK 0x0400 /* GP13_POL */ +#define WM8995_GP13_POL_SHIFT 10 /* GP13_POL */ +#define WM8995_GP13_POL_WIDTH 1 /* GP13_POL */ +#define WM8995_GP13_OP_CFG 0x0200 /* GP13_OP_CFG */ +#define WM8995_GP13_OP_CFG_MASK 0x0200 /* GP13_OP_CFG */ +#define WM8995_GP13_OP_CFG_SHIFT 9 /* GP13_OP_CFG */ +#define WM8995_GP13_OP_CFG_WIDTH 1 /* GP13_OP_CFG */ +#define WM8995_GP13_DB 0x0100 /* GP13_DB */ +#define WM8995_GP13_DB_MASK 0x0100 /* GP13_DB */ +#define WM8995_GP13_DB_SHIFT 8 /* GP13_DB */ +#define WM8995_GP13_DB_WIDTH 1 /* GP13_DB */ +#define WM8995_GP13_LVL 0x0040 /* GP13_LVL */ +#define WM8995_GP13_LVL_MASK 0x0040 /* GP13_LVL */ +#define WM8995_GP13_LVL_SHIFT 6 /* GP13_LVL */ +#define WM8995_GP13_LVL_WIDTH 1 /* GP13_LVL */ +#define WM8995_GP13_FN_MASK 0x001F /* GP13_FN - [4:0] */ +#define WM8995_GP13_FN_SHIFT 0 /* GP13_FN - [4:0] */ +#define WM8995_GP13_FN_WIDTH 5 /* GP13_FN - [4:0] */ + +/* + * R1805 (0x70D) - GPIO 14 + */ +#define WM8995_GP14_DIR 0x8000 /* GP14_DIR */ +#define WM8995_GP14_DIR_MASK 0x8000 /* GP14_DIR */ +#define WM8995_GP14_DIR_SHIFT 15 /* GP14_DIR */ +#define WM8995_GP14_DIR_WIDTH 1 /* GP14_DIR */ +#define WM8995_GP14_PU 0x4000 /* GP14_PU */ +#define WM8995_GP14_PU_MASK 0x4000 /* GP14_PU */ +#define WM8995_GP14_PU_SHIFT 14 /* GP14_PU */ +#define WM8995_GP14_PU_WIDTH 1 /* GP14_PU */ +#define WM8995_GP14_PD 0x2000 /* GP14_PD */ +#define WM8995_GP14_PD_MASK 0x2000 /* GP14_PD */ +#define WM8995_GP14_PD_SHIFT 13 /* GP14_PD */ +#define WM8995_GP14_PD_WIDTH 1 /* GP14_PD */ +#define WM8995_GP14_POL 0x0400 /* GP14_POL */ +#define WM8995_GP14_POL_MASK 0x0400 /* GP14_POL */ +#define WM8995_GP14_POL_SHIFT 10 /* GP14_POL */ +#define WM8995_GP14_POL_WIDTH 1 /* GP14_POL */ +#define WM8995_GP14_OP_CFG 0x0200 /* GP14_OP_CFG */ +#define WM8995_GP14_OP_CFG_MASK 0x0200 /* GP14_OP_CFG */ +#define WM8995_GP14_OP_CFG_SHIFT 9 /* GP14_OP_CFG */ +#define WM8995_GP14_OP_CFG_WIDTH 1 /* GP14_OP_CFG */ +#define WM8995_GP14_DB 0x0100 /* GP14_DB */ +#define WM8995_GP14_DB_MASK 0x0100 /* GP14_DB */ +#define WM8995_GP14_DB_SHIFT 8 /* GP14_DB */ +#define WM8995_GP14_DB_WIDTH 1 /* GP14_DB */ +#define WM8995_GP14_LVL 0x0040 /* GP14_LVL */ +#define WM8995_GP14_LVL_MASK 0x0040 /* GP14_LVL */ +#define WM8995_GP14_LVL_SHIFT 6 /* GP14_LVL */ +#define WM8995_GP14_LVL_WIDTH 1 /* GP14_LVL */ +#define WM8995_GP14_FN_MASK 0x001F /* GP14_FN - [4:0] */ +#define WM8995_GP14_FN_SHIFT 0 /* GP14_FN - [4:0] */ +#define WM8995_GP14_FN_WIDTH 5 /* GP14_FN - [4:0] */ + +/* + * R1824 (0x720) - Pull Control (1) + */ +#define WM8995_DMICDAT3_PD 0x4000 /* DMICDAT3_PD */ +#define WM8995_DMICDAT3_PD_MASK 0x4000 /* DMICDAT3_PD */ +#define WM8995_DMICDAT3_PD_SHIFT 14 /* DMICDAT3_PD */ +#define WM8995_DMICDAT3_PD_WIDTH 1 /* DMICDAT3_PD */ +#define WM8995_DMICDAT2_PD 0x1000 /* DMICDAT2_PD */ +#define WM8995_DMICDAT2_PD_MASK 0x1000 /* DMICDAT2_PD */ +#define WM8995_DMICDAT2_PD_SHIFT 12 /* DMICDAT2_PD */ +#define WM8995_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */ +#define WM8995_DMICDAT1_PD 0x0400 /* DMICDAT1_PD */ +#define WM8995_DMICDAT1_PD_MASK 0x0400 /* DMICDAT1_PD */ +#define WM8995_DMICDAT1_PD_SHIFT 10 /* DMICDAT1_PD */ +#define WM8995_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */ +#define WM8995_MCLK2_PU 0x0200 /* MCLK2_PU */ +#define WM8995_MCLK2_PU_MASK 0x0200 /* MCLK2_PU */ +#define WM8995_MCLK2_PU_SHIFT 9 /* MCLK2_PU */ +#define WM8995_MCLK2_PU_WIDTH 1 /* MCLK2_PU */ +#define WM8995_MCLK2_PD 0x0100 /* MCLK2_PD */ +#define WM8995_MCLK2_PD_MASK 0x0100 /* MCLK2_PD */ +#define WM8995_MCLK2_PD_SHIFT 8 /* MCLK2_PD */ +#define WM8995_MCLK2_PD_WIDTH 1 /* MCLK2_PD */ +#define WM8995_MCLK1_PU 0x0080 /* MCLK1_PU */ +#define WM8995_MCLK1_PU_MASK 0x0080 /* MCLK1_PU */ +#define WM8995_MCLK1_PU_SHIFT 7 /* MCLK1_PU */ +#define WM8995_MCLK1_PU_WIDTH 1 /* MCLK1_PU */ +#define WM8995_MCLK1_PD 0x0040 /* MCLK1_PD */ +#define WM8995_MCLK1_PD_MASK 0x0040 /* MCLK1_PD */ +#define WM8995_MCLK1_PD_SHIFT 6 /* MCLK1_PD */ +#define WM8995_MCLK1_PD_WIDTH 1 /* MCLK1_PD */ +#define WM8995_DACDAT1_PU 0x0020 /* DACDAT1_PU */ +#define WM8995_DACDAT1_PU_MASK 0x0020 /* DACDAT1_PU */ +#define WM8995_DACDAT1_PU_SHIFT 5 /* DACDAT1_PU */ +#define WM8995_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */ +#define WM8995_DACDAT1_PD 0x0010 /* DACDAT1_PD */ +#define WM8995_DACDAT1_PD_MASK 0x0010 /* DACDAT1_PD */ +#define WM8995_DACDAT1_PD_SHIFT 4 /* DACDAT1_PD */ +#define WM8995_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */ +#define WM8995_DACLRCLK1_PU 0x0008 /* DACLRCLK1_PU */ +#define WM8995_DACLRCLK1_PU_MASK 0x0008 /* DACLRCLK1_PU */ +#define WM8995_DACLRCLK1_PU_SHIFT 3 /* DACLRCLK1_PU */ +#define WM8995_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */ +#define WM8995_DACLRCLK1_PD 0x0004 /* DACLRCLK1_PD */ +#define WM8995_DACLRCLK1_PD_MASK 0x0004 /* DACLRCLK1_PD */ +#define WM8995_DACLRCLK1_PD_SHIFT 2 /* DACLRCLK1_PD */ +#define WM8995_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */ +#define WM8995_BCLK1_PU 0x0002 /* BCLK1_PU */ +#define WM8995_BCLK1_PU_MASK 0x0002 /* BCLK1_PU */ +#define WM8995_BCLK1_PU_SHIFT 1 /* BCLK1_PU */ +#define WM8995_BCLK1_PU_WIDTH 1 /* BCLK1_PU */ +#define WM8995_BCLK1_PD 0x0001 /* BCLK1_PD */ +#define WM8995_BCLK1_PD_MASK 0x0001 /* BCLK1_PD */ +#define WM8995_BCLK1_PD_SHIFT 0 /* BCLK1_PD */ +#define WM8995_BCLK1_PD_WIDTH 1 /* BCLK1_PD */ + +/* + * R1825 (0x721) - Pull Control (2) + */ +#define WM8995_LDO1ENA_PD 0x0010 /* LDO1ENA_PD */ +#define WM8995_LDO1ENA_PD_MASK 0x0010 /* LDO1ENA_PD */ +#define WM8995_LDO1ENA_PD_SHIFT 4 /* LDO1ENA_PD */ +#define WM8995_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */ +#define WM8995_MODE_PD 0x0004 /* MODE_PD */ +#define WM8995_MODE_PD_MASK 0x0004 /* MODE_PD */ +#define WM8995_MODE_PD_SHIFT 2 /* MODE_PD */ +#define WM8995_MODE_PD_WIDTH 1 /* MODE_PD */ +#define WM8995_CSNADDR_PD 0x0001 /* CSNADDR_PD */ +#define WM8995_CSNADDR_PD_MASK 0x0001 /* CSNADDR_PD */ +#define WM8995_CSNADDR_PD_SHIFT 0 /* CSNADDR_PD */ +#define WM8995_CSNADDR_PD_WIDTH 1 /* CSNADDR_PD */ + +/* + * R1840 (0x730) - Interrupt Status 1 + */ +#define WM8995_GP14_EINT 0x2000 /* GP14_EINT */ +#define WM8995_GP14_EINT_MASK 0x2000 /* GP14_EINT */ +#define WM8995_GP14_EINT_SHIFT 13 /* GP14_EINT */ +#define WM8995_GP14_EINT_WIDTH 1 /* GP14_EINT */ +#define WM8995_GP13_EINT 0x1000 /* GP13_EINT */ +#define WM8995_GP13_EINT_MASK 0x1000 /* GP13_EINT */ +#define WM8995_GP13_EINT_SHIFT 12 /* GP13_EINT */ +#define WM8995_GP13_EINT_WIDTH 1 /* GP13_EINT */ +#define WM8995_GP12_EINT 0x0800 /* GP12_EINT */ +#define WM8995_GP12_EINT_MASK 0x0800 /* GP12_EINT */ +#define WM8995_GP12_EINT_SHIFT 11 /* GP12_EINT */ +#define WM8995_GP12_EINT_WIDTH 1 /* GP12_EINT */ +#define WM8995_GP11_EINT 0x0400 /* GP11_EINT */ +#define WM8995_GP11_EINT_MASK 0x0400 /* GP11_EINT */ +#define WM8995_GP11_EINT_SHIFT 10 /* GP11_EINT */ +#define WM8995_GP11_EINT_WIDTH 1 /* GP11_EINT */ +#define WM8995_GP10_EINT 0x0200 /* GP10_EINT */ +#define WM8995_GP10_EINT_MASK 0x0200 /* GP10_EINT */ +#define WM8995_GP10_EINT_SHIFT 9 /* GP10_EINT */ +#define WM8995_GP10_EINT_WIDTH 1 /* GP10_EINT */ +#define WM8995_GP9_EINT 0x0100 /* GP9_EINT */ +#define WM8995_GP9_EINT_MASK 0x0100 /* GP9_EINT */ +#define WM8995_GP9_EINT_SHIFT 8 /* GP9_EINT */ +#define WM8995_GP9_EINT_WIDTH 1 /* GP9_EINT */ +#define WM8995_GP8_EINT 0x0080 /* GP8_EINT */ +#define WM8995_GP8_EINT_MASK 0x0080 /* GP8_EINT */ +#define WM8995_GP8_EINT_SHIFT 7 /* GP8_EINT */ +#define WM8995_GP8_EINT_WIDTH 1 /* GP8_EINT */ +#define WM8995_GP7_EINT 0x0040 /* GP7_EINT */ +#define WM8995_GP7_EINT_MASK 0x0040 /* GP7_EINT */ +#define WM8995_GP7_EINT_SHIFT 6 /* GP7_EINT */ +#define WM8995_GP7_EINT_WIDTH 1 /* GP7_EINT */ +#define WM8995_GP6_EINT 0x0020 /* GP6_EINT */ +#define WM8995_GP6_EINT_MASK 0x0020 /* GP6_EINT */ +#define WM8995_GP6_EINT_SHIFT 5 /* GP6_EINT */ +#define WM8995_GP6_EINT_WIDTH 1 /* GP6_EINT */ +#define WM8995_GP5_EINT 0x0010 /* GP5_EINT */ +#define WM8995_GP5_EINT_MASK 0x0010 /* GP5_EINT */ +#define WM8995_GP5_EINT_SHIFT 4 /* GP5_EINT */ +#define WM8995_GP5_EINT_WIDTH 1 /* GP5_EINT */ +#define WM8995_GP4_EINT 0x0008 /* GP4_EINT */ +#define WM8995_GP4_EINT_MASK 0x0008 /* GP4_EINT */ +#define WM8995_GP4_EINT_SHIFT 3 /* GP4_EINT */ +#define WM8995_GP4_EINT_WIDTH 1 /* GP4_EINT */ +#define WM8995_GP3_EINT 0x0004 /* GP3_EINT */ +#define WM8995_GP3_EINT_MASK 0x0004 /* GP3_EINT */ +#define WM8995_GP3_EINT_SHIFT 2 /* GP3_EINT */ +#define WM8995_GP3_EINT_WIDTH 1 /* GP3_EINT */ +#define WM8995_GP2_EINT 0x0002 /* GP2_EINT */ +#define WM8995_GP2_EINT_MASK 0x0002 /* GP2_EINT */ +#define WM8995_GP2_EINT_SHIFT 1 /* GP2_EINT */ +#define WM8995_GP2_EINT_WIDTH 1 /* GP2_EINT */ +#define WM8995_GP1_EINT 0x0001 /* GP1_EINT */ +#define WM8995_GP1_EINT_MASK 0x0001 /* GP1_EINT */ +#define WM8995_GP1_EINT_SHIFT 0 /* GP1_EINT */ +#define WM8995_GP1_EINT_WIDTH 1 /* GP1_EINT */ + +/* + * R1841 (0x731) - Interrupt Status 2 + */ +#define WM8995_DCS_DONE_23_EINT 0x1000 /* DCS_DONE_23_EINT */ +#define WM8995_DCS_DONE_23_EINT_MASK 0x1000 /* DCS_DONE_23_EINT */ +#define WM8995_DCS_DONE_23_EINT_SHIFT 12 /* DCS_DONE_23_EINT */ +#define WM8995_DCS_DONE_23_EINT_WIDTH 1 /* DCS_DONE_23_EINT */ +#define WM8995_DCS_DONE_01_EINT 0x0800 /* DCS_DONE_01_EINT */ +#define WM8995_DCS_DONE_01_EINT_MASK 0x0800 /* DCS_DONE_01_EINT */ +#define WM8995_DCS_DONE_01_EINT_SHIFT 11 /* DCS_DONE_01_EINT */ +#define WM8995_DCS_DONE_01_EINT_WIDTH 1 /* DCS_DONE_01_EINT */ +#define WM8995_WSEQ_DONE_EINT 0x0400 /* WSEQ_DONE_EINT */ +#define WM8995_WSEQ_DONE_EINT_MASK 0x0400 /* WSEQ_DONE_EINT */ +#define WM8995_WSEQ_DONE_EINT_SHIFT 10 /* WSEQ_DONE_EINT */ +#define WM8995_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */ +#define WM8995_FIFOS_ERR_EINT 0x0200 /* FIFOS_ERR_EINT */ +#define WM8995_FIFOS_ERR_EINT_MASK 0x0200 /* FIFOS_ERR_EINT */ +#define WM8995_FIFOS_ERR_EINT_SHIFT 9 /* FIFOS_ERR_EINT */ +#define WM8995_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */ +#define WM8995_AIF2DRC_SIG_DET_EINT 0x0100 /* AIF2DRC_SIG_DET_EINT */ +#define WM8995_AIF2DRC_SIG_DET_EINT_MASK 0x0100 /* AIF2DRC_SIG_DET_EINT */ +#define WM8995_AIF2DRC_SIG_DET_EINT_SHIFT 8 /* AIF2DRC_SIG_DET_EINT */ +#define WM8995_AIF2DRC_SIG_DET_EINT_WIDTH 1 /* AIF2DRC_SIG_DET_EINT */ +#define WM8995_AIF1DRC2_SIG_DET_EINT 0x0080 /* AIF1DRC2_SIG_DET_EINT */ +#define WM8995_AIF1DRC2_SIG_DET_EINT_MASK 0x0080 /* AIF1DRC2_SIG_DET_EINT */ +#define WM8995_AIF1DRC2_SIG_DET_EINT_SHIFT 7 /* AIF1DRC2_SIG_DET_EINT */ +#define WM8995_AIF1DRC2_SIG_DET_EINT_WIDTH 1 /* AIF1DRC2_SIG_DET_EINT */ +#define WM8995_AIF1DRC1_SIG_DET_EINT 0x0040 /* AIF1DRC1_SIG_DET_EINT */ +#define WM8995_AIF1DRC1_SIG_DET_EINT_MASK 0x0040 /* AIF1DRC1_SIG_DET_EINT */ +#define WM8995_AIF1DRC1_SIG_DET_EINT_SHIFT 6 /* AIF1DRC1_SIG_DET_EINT */ +#define WM8995_AIF1DRC1_SIG_DET_EINT_WIDTH 1 /* AIF1DRC1_SIG_DET_EINT */ +#define WM8995_SRC2_LOCK_EINT 0x0020 /* SRC2_LOCK_EINT */ +#define WM8995_SRC2_LOCK_EINT_MASK 0x0020 /* SRC2_LOCK_EINT */ +#define WM8995_SRC2_LOCK_EINT_SHIFT 5 /* SRC2_LOCK_EINT */ +#define WM8995_SRC2_LOCK_EINT_WIDTH 1 /* SRC2_LOCK_EINT */ +#define WM8995_SRC1_LOCK_EINT 0x0010 /* SRC1_LOCK_EINT */ +#define WM8995_SRC1_LOCK_EINT_MASK 0x0010 /* SRC1_LOCK_EINT */ +#define WM8995_SRC1_LOCK_EINT_SHIFT 4 /* SRC1_LOCK_EINT */ +#define WM8995_SRC1_LOCK_EINT_WIDTH 1 /* SRC1_LOCK_EINT */ +#define WM8995_FLL2_LOCK_EINT 0x0008 /* FLL2_LOCK_EINT */ +#define WM8995_FLL2_LOCK_EINT_MASK 0x0008 /* FLL2_LOCK_EINT */ +#define WM8995_FLL2_LOCK_EINT_SHIFT 3 /* FLL2_LOCK_EINT */ +#define WM8995_FLL2_LOCK_EINT_WIDTH 1 /* FLL2_LOCK_EINT */ +#define WM8995_FLL1_LOCK_EINT 0x0004 /* FLL1_LOCK_EINT */ +#define WM8995_FLL1_LOCK_EINT_MASK 0x0004 /* FLL1_LOCK_EINT */ +#define WM8995_FLL1_LOCK_EINT_SHIFT 2 /* FLL1_LOCK_EINT */ +#define WM8995_FLL1_LOCK_EINT_WIDTH 1 /* FLL1_LOCK_EINT */ +#define WM8995_HP_DONE_EINT 0x0002 /* HP_DONE_EINT */ +#define WM8995_HP_DONE_EINT_MASK 0x0002 /* HP_DONE_EINT */ +#define WM8995_HP_DONE_EINT_SHIFT 1 /* HP_DONE_EINT */ +#define WM8995_HP_DONE_EINT_WIDTH 1 /* HP_DONE_EINT */ +#define WM8995_MICD_EINT 0x0001 /* MICD_EINT */ +#define WM8995_MICD_EINT_MASK 0x0001 /* MICD_EINT */ +#define WM8995_MICD_EINT_SHIFT 0 /* MICD_EINT */ +#define WM8995_MICD_EINT_WIDTH 1 /* MICD_EINT */ + +/* + * R1842 (0x732) - Interrupt Raw Status 2 + */ +#define WM8995_DCS_DONE_23_STS 0x1000 /* DCS_DONE_23_STS */ +#define WM8995_DCS_DONE_23_STS_MASK 0x1000 /* DCS_DONE_23_STS */ +#define WM8995_DCS_DONE_23_STS_SHIFT 12 /* DCS_DONE_23_STS */ +#define WM8995_DCS_DONE_23_STS_WIDTH 1 /* DCS_DONE_23_STS */ +#define WM8995_DCS_DONE_01_STS 0x0800 /* DCS_DONE_01_STS */ +#define WM8995_DCS_DONE_01_STS_MASK 0x0800 /* DCS_DONE_01_STS */ +#define WM8995_DCS_DONE_01_STS_SHIFT 11 /* DCS_DONE_01_STS */ +#define WM8995_DCS_DONE_01_STS_WIDTH 1 /* DCS_DONE_01_STS */ +#define WM8995_WSEQ_DONE_STS 0x0400 /* WSEQ_DONE_STS */ +#define WM8995_WSEQ_DONE_STS_MASK 0x0400 /* WSEQ_DONE_STS */ +#define WM8995_WSEQ_DONE_STS_SHIFT 10 /* WSEQ_DONE_STS */ +#define WM8995_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */ +#define WM8995_FIFOS_ERR_STS 0x0200 /* FIFOS_ERR_STS */ +#define WM8995_FIFOS_ERR_STS_MASK 0x0200 /* FIFOS_ERR_STS */ +#define WM8995_FIFOS_ERR_STS_SHIFT 9 /* FIFOS_ERR_STS */ +#define WM8995_FIFOS_ERR_STS_WIDTH 1 /* FIFOS_ERR_STS */ +#define WM8995_AIF2DRC_SIG_DET_STS 0x0100 /* AIF2DRC_SIG_DET_STS */ +#define WM8995_AIF2DRC_SIG_DET_STS_MASK 0x0100 /* AIF2DRC_SIG_DET_STS */ +#define WM8995_AIF2DRC_SIG_DET_STS_SHIFT 8 /* AIF2DRC_SIG_DET_STS */ +#define WM8995_AIF2DRC_SIG_DET_STS_WIDTH 1 /* AIF2DRC_SIG_DET_STS */ +#define WM8995_AIF1DRC2_SIG_DET_STS 0x0080 /* AIF1DRC2_SIG_DET_STS */ +#define WM8995_AIF1DRC2_SIG_DET_STS_MASK 0x0080 /* AIF1DRC2_SIG_DET_STS */ +#define WM8995_AIF1DRC2_SIG_DET_STS_SHIFT 7 /* AIF1DRC2_SIG_DET_STS */ +#define WM8995_AIF1DRC2_SIG_DET_STS_WIDTH 1 /* AIF1DRC2_SIG_DET_STS */ +#define WM8995_AIF1DRC1_SIG_DET_STS 0x0040 /* AIF1DRC1_SIG_DET_STS */ +#define WM8995_AIF1DRC1_SIG_DET_STS_MASK 0x0040 /* AIF1DRC1_SIG_DET_STS */ +#define WM8995_AIF1DRC1_SIG_DET_STS_SHIFT 6 /* AIF1DRC1_SIG_DET_STS */ +#define WM8995_AIF1DRC1_SIG_DET_STS_WIDTH 1 /* AIF1DRC1_SIG_DET_STS */ +#define WM8995_SRC2_LOCK_STS 0x0020 /* SRC2_LOCK_STS */ +#define WM8995_SRC2_LOCK_STS_MASK 0x0020 /* SRC2_LOCK_STS */ +#define WM8995_SRC2_LOCK_STS_SHIFT 5 /* SRC2_LOCK_STS */ +#define WM8995_SRC2_LOCK_STS_WIDTH 1 /* SRC2_LOCK_STS */ +#define WM8995_SRC1_LOCK_STS 0x0010 /* SRC1_LOCK_STS */ +#define WM8995_SRC1_LOCK_STS_MASK 0x0010 /* SRC1_LOCK_STS */ +#define WM8995_SRC1_LOCK_STS_SHIFT 4 /* SRC1_LOCK_STS */ +#define WM8995_SRC1_LOCK_STS_WIDTH 1 /* SRC1_LOCK_STS */ +#define WM8995_FLL2_LOCK_STS 0x0008 /* FLL2_LOCK_STS */ +#define WM8995_FLL2_LOCK_STS_MASK 0x0008 /* FLL2_LOCK_STS */ +#define WM8995_FLL2_LOCK_STS_SHIFT 3 /* FLL2_LOCK_STS */ +#define WM8995_FLL2_LOCK_STS_WIDTH 1 /* FLL2_LOCK_STS */ +#define WM8995_FLL1_LOCK_STS 0x0004 /* FLL1_LOCK_STS */ +#define WM8995_FLL1_LOCK_STS_MASK 0x0004 /* FLL1_LOCK_STS */ +#define WM8995_FLL1_LOCK_STS_SHIFT 2 /* FLL1_LOCK_STS */ +#define WM8995_FLL1_LOCK_STS_WIDTH 1 /* FLL1_LOCK_STS */ + +/* + * R1848 (0x738) - Interrupt Status 1 Mask + */ +#define WM8995_IM_GP14_EINT 0x2000 /* IM_GP14_EINT */ +#define WM8995_IM_GP14_EINT_MASK 0x2000 /* IM_GP14_EINT */ +#define WM8995_IM_GP14_EINT_SHIFT 13 /* IM_GP14_EINT */ +#define WM8995_IM_GP14_EINT_WIDTH 1 /* IM_GP14_EINT */ +#define WM8995_IM_GP13_EINT 0x1000 /* IM_GP13_EINT */ +#define WM8995_IM_GP13_EINT_MASK 0x1000 /* IM_GP13_EINT */ +#define WM8995_IM_GP13_EINT_SHIFT 12 /* IM_GP13_EINT */ +#define WM8995_IM_GP13_EINT_WIDTH 1 /* IM_GP13_EINT */ +#define WM8995_IM_GP12_EINT 0x0800 /* IM_GP12_EINT */ +#define WM8995_IM_GP12_EINT_MASK 0x0800 /* IM_GP12_EINT */ +#define WM8995_IM_GP12_EINT_SHIFT 11 /* IM_GP12_EINT */ +#define WM8995_IM_GP12_EINT_WIDTH 1 /* IM_GP12_EINT */ +#define WM8995_IM_GP11_EINT 0x0400 /* IM_GP11_EINT */ +#define WM8995_IM_GP11_EINT_MASK 0x0400 /* IM_GP11_EINT */ +#define WM8995_IM_GP11_EINT_SHIFT 10 /* IM_GP11_EINT */ +#define WM8995_IM_GP11_EINT_WIDTH 1 /* IM_GP11_EINT */ +#define WM8995_IM_GP10_EINT 0x0200 /* IM_GP10_EINT */ +#define WM8995_IM_GP10_EINT_MASK 0x0200 /* IM_GP10_EINT */ +#define WM8995_IM_GP10_EINT_SHIFT 9 /* IM_GP10_EINT */ +#define WM8995_IM_GP10_EINT_WIDTH 1 /* IM_GP10_EINT */ +#define WM8995_IM_GP9_EINT 0x0100 /* IM_GP9_EINT */ +#define WM8995_IM_GP9_EINT_MASK 0x0100 /* IM_GP9_EINT */ +#define WM8995_IM_GP9_EINT_SHIFT 8 /* IM_GP9_EINT */ +#define WM8995_IM_GP9_EINT_WIDTH 1 /* IM_GP9_EINT */ +#define WM8995_IM_GP8_EINT 0x0080 /* IM_GP8_EINT */ +#define WM8995_IM_GP8_EINT_MASK 0x0080 /* IM_GP8_EINT */ +#define WM8995_IM_GP8_EINT_SHIFT 7 /* IM_GP8_EINT */ +#define WM8995_IM_GP8_EINT_WIDTH 1 /* IM_GP8_EINT */ +#define WM8995_IM_GP7_EINT 0x0040 /* IM_GP7_EINT */ +#define WM8995_IM_GP7_EINT_MASK 0x0040 /* IM_GP7_EINT */ +#define WM8995_IM_GP7_EINT_SHIFT 6 /* IM_GP7_EINT */ +#define WM8995_IM_GP7_EINT_WIDTH 1 /* IM_GP7_EINT */ +#define WM8995_IM_GP6_EINT 0x0020 /* IM_GP6_EINT */ +#define WM8995_IM_GP6_EINT_MASK 0x0020 /* IM_GP6_EINT */ +#define WM8995_IM_GP6_EINT_SHIFT 5 /* IM_GP6_EINT */ +#define WM8995_IM_GP6_EINT_WIDTH 1 /* IM_GP6_EINT */ +#define WM8995_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */ +#define WM8995_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */ +#define WM8995_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */ +#define WM8995_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */ +#define WM8995_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */ +#define WM8995_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */ +#define WM8995_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */ +#define WM8995_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */ +#define WM8995_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */ +#define WM8995_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */ +#define WM8995_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */ +#define WM8995_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */ +#define WM8995_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */ +#define WM8995_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */ +#define WM8995_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */ +#define WM8995_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */ +#define WM8995_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */ +#define WM8995_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */ +#define WM8995_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */ +#define WM8995_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */ + +/* + * R1849 (0x739) - Interrupt Status 2 Mask + */ +#define WM8995_IM_DCS_DONE_23_EINT 0x1000 /* IM_DCS_DONE_23_EINT */ +#define WM8995_IM_DCS_DONE_23_EINT_MASK 0x1000 /* IM_DCS_DONE_23_EINT */ +#define WM8995_IM_DCS_DONE_23_EINT_SHIFT 12 /* IM_DCS_DONE_23_EINT */ +#define WM8995_IM_DCS_DONE_23_EINT_WIDTH 1 /* IM_DCS_DONE_23_EINT */ +#define WM8995_IM_DCS_DONE_01_EINT 0x0800 /* IM_DCS_DONE_01_EINT */ +#define WM8995_IM_DCS_DONE_01_EINT_MASK 0x0800 /* IM_DCS_DONE_01_EINT */ +#define WM8995_IM_DCS_DONE_01_EINT_SHIFT 11 /* IM_DCS_DONE_01_EINT */ +#define WM8995_IM_DCS_DONE_01_EINT_WIDTH 1 /* IM_DCS_DONE_01_EINT */ +#define WM8995_IM_WSEQ_DONE_EINT 0x0400 /* IM_WSEQ_DONE_EINT */ +#define WM8995_IM_WSEQ_DONE_EINT_MASK 0x0400 /* IM_WSEQ_DONE_EINT */ +#define WM8995_IM_WSEQ_DONE_EINT_SHIFT 10 /* IM_WSEQ_DONE_EINT */ +#define WM8995_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */ +#define WM8995_IM_FIFOS_ERR_EINT 0x0200 /* IM_FIFOS_ERR_EINT */ +#define WM8995_IM_FIFOS_ERR_EINT_MASK 0x0200 /* IM_FIFOS_ERR_EINT */ +#define WM8995_IM_FIFOS_ERR_EINT_SHIFT 9 /* IM_FIFOS_ERR_EINT */ +#define WM8995_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */ +#define WM8995_IM_AIF2DRC_SIG_DET_EINT 0x0100 /* IM_AIF2DRC_SIG_DET_EINT */ +#define WM8995_IM_AIF2DRC_SIG_DET_EINT_MASK 0x0100 /* IM_AIF2DRC_SIG_DET_EINT */ +#define WM8995_IM_AIF2DRC_SIG_DET_EINT_SHIFT 8 /* IM_AIF2DRC_SIG_DET_EINT */ +#define WM8995_IM_AIF2DRC_SIG_DET_EINT_WIDTH 1 /* IM_AIF2DRC_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC2_SIG_DET_EINT 0x0080 /* IM_AIF1DRC2_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC2_SIG_DET_EINT_MASK 0x0080 /* IM_AIF1DRC2_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC2_SIG_DET_EINT_SHIFT 7 /* IM_AIF1DRC2_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC2_SIG_DET_EINT_WIDTH 1 /* IM_AIF1DRC2_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC1_SIG_DET_EINT 0x0040 /* IM_AIF1DRC1_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC1_SIG_DET_EINT_MASK 0x0040 /* IM_AIF1DRC1_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC1_SIG_DET_EINT_SHIFT 6 /* IM_AIF1DRC1_SIG_DET_EINT */ +#define WM8995_IM_AIF1DRC1_SIG_DET_EINT_WIDTH 1 /* IM_AIF1DRC1_SIG_DET_EINT */ +#define WM8995_IM_SRC2_LOCK_EINT 0x0020 /* IM_SRC2_LOCK_EINT */ +#define WM8995_IM_SRC2_LOCK_EINT_MASK 0x0020 /* IM_SRC2_LOCK_EINT */ +#define WM8995_IM_SRC2_LOCK_EINT_SHIFT 5 /* IM_SRC2_LOCK_EINT */ +#define WM8995_IM_SRC2_LOCK_EINT_WIDTH 1 /* IM_SRC2_LOCK_EINT */ +#define WM8995_IM_SRC1_LOCK_EINT 0x0010 /* IM_SRC1_LOCK_EINT */ +#define WM8995_IM_SRC1_LOCK_EINT_MASK 0x0010 /* IM_SRC1_LOCK_EINT */ +#define WM8995_IM_SRC1_LOCK_EINT_SHIFT 4 /* IM_SRC1_LOCK_EINT */ +#define WM8995_IM_SRC1_LOCK_EINT_WIDTH 1 /* IM_SRC1_LOCK_EINT */ +#define WM8995_IM_FLL2_LOCK_EINT 0x0008 /* IM_FLL2_LOCK_EINT */ +#define WM8995_IM_FLL2_LOCK_EINT_MASK 0x0008 /* IM_FLL2_LOCK_EINT */ +#define WM8995_IM_FLL2_LOCK_EINT_SHIFT 3 /* IM_FLL2_LOCK_EINT */ +#define WM8995_IM_FLL2_LOCK_EINT_WIDTH 1 /* IM_FLL2_LOCK_EINT */ +#define WM8995_IM_FLL1_LOCK_EINT 0x0004 /* IM_FLL1_LOCK_EINT */ +#define WM8995_IM_FLL1_LOCK_EINT_MASK 0x0004 /* IM_FLL1_LOCK_EINT */ +#define WM8995_IM_FLL1_LOCK_EINT_SHIFT 2 /* IM_FLL1_LOCK_EINT */ +#define WM8995_IM_FLL1_LOCK_EINT_WIDTH 1 /* IM_FLL1_LOCK_EINT */ +#define WM8995_IM_HP_DONE_EINT 0x0002 /* IM_HP_DONE_EINT */ +#define WM8995_IM_HP_DONE_EINT_MASK 0x0002 /* IM_HP_DONE_EINT */ +#define WM8995_IM_HP_DONE_EINT_SHIFT 1 /* IM_HP_DONE_EINT */ +#define WM8995_IM_HP_DONE_EINT_WIDTH 1 /* IM_HP_DONE_EINT */ +#define WM8995_IM_MICD_EINT 0x0001 /* IM_MICD_EINT */ +#define WM8995_IM_MICD_EINT_MASK 0x0001 /* IM_MICD_EINT */ +#define WM8995_IM_MICD_EINT_SHIFT 0 /* IM_MICD_EINT */ +#define WM8995_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */ + +/* + * R1856 (0x740) - Interrupt Control + */ +#define WM8995_IM_IRQ 0x0001 /* IM_IRQ */ +#define WM8995_IM_IRQ_MASK 0x0001 /* IM_IRQ */ +#define WM8995_IM_IRQ_SHIFT 0 /* IM_IRQ */ +#define WM8995_IM_IRQ_WIDTH 1 /* IM_IRQ */ + +/* + * R2048 (0x800) - Left PDM Speaker 1 + */ +#define WM8995_SPK1L_ENA 0x0010 /* SPK1L_ENA */ +#define WM8995_SPK1L_ENA_MASK 0x0010 /* SPK1L_ENA */ +#define WM8995_SPK1L_ENA_SHIFT 4 /* SPK1L_ENA */ +#define WM8995_SPK1L_ENA_WIDTH 1 /* SPK1L_ENA */ +#define WM8995_SPK1L_MUTE 0x0008 /* SPK1L_MUTE */ +#define WM8995_SPK1L_MUTE_MASK 0x0008 /* SPK1L_MUTE */ +#define WM8995_SPK1L_MUTE_SHIFT 3 /* SPK1L_MUTE */ +#define WM8995_SPK1L_MUTE_WIDTH 1 /* SPK1L_MUTE */ +#define WM8995_SPK1L_MUTE_ZC 0x0004 /* SPK1L_MUTE_ZC */ +#define WM8995_SPK1L_MUTE_ZC_MASK 0x0004 /* SPK1L_MUTE_ZC */ +#define WM8995_SPK1L_MUTE_ZC_SHIFT 2 /* SPK1L_MUTE_ZC */ +#define WM8995_SPK1L_MUTE_ZC_WIDTH 1 /* SPK1L_MUTE_ZC */ +#define WM8995_SPK1L_SRC_MASK 0x0003 /* SPK1L_SRC - [1:0] */ +#define WM8995_SPK1L_SRC_SHIFT 0 /* SPK1L_SRC - [1:0] */ +#define WM8995_SPK1L_SRC_WIDTH 2 /* SPK1L_SRC - [1:0] */ + +/* + * R2049 (0x801) - Right PDM Speaker 1 + */ +#define WM8995_SPK1R_ENA 0x0010 /* SPK1R_ENA */ +#define WM8995_SPK1R_ENA_MASK 0x0010 /* SPK1R_ENA */ +#define WM8995_SPK1R_ENA_SHIFT 4 /* SPK1R_ENA */ +#define WM8995_SPK1R_ENA_WIDTH 1 /* SPK1R_ENA */ +#define WM8995_SPK1R_MUTE 0x0008 /* SPK1R_MUTE */ +#define WM8995_SPK1R_MUTE_MASK 0x0008 /* SPK1R_MUTE */ +#define WM8995_SPK1R_MUTE_SHIFT 3 /* SPK1R_MUTE */ +#define WM8995_SPK1R_MUTE_WIDTH 1 /* SPK1R_MUTE */ +#define WM8995_SPK1R_MUTE_ZC 0x0004 /* SPK1R_MUTE_ZC */ +#define WM8995_SPK1R_MUTE_ZC_MASK 0x0004 /* SPK1R_MUTE_ZC */ +#define WM8995_SPK1R_MUTE_ZC_SHIFT 2 /* SPK1R_MUTE_ZC */ +#define WM8995_SPK1R_MUTE_ZC_WIDTH 1 /* SPK1R_MUTE_ZC */ +#define WM8995_SPK1R_SRC_MASK 0x0003 /* SPK1R_SRC - [1:0] */ +#define WM8995_SPK1R_SRC_SHIFT 0 /* SPK1R_SRC - [1:0] */ +#define WM8995_SPK1R_SRC_WIDTH 2 /* SPK1R_SRC - [1:0] */ + +/* + * R2050 (0x802) - PDM Speaker 1 Mute Sequence + */ +#define WM8995_SPK1_MUTE_SEQ1_MASK 0x00FF /* SPK1_MUTE_SEQ1 - [7:0] */ +#define WM8995_SPK1_MUTE_SEQ1_SHIFT 0 /* SPK1_MUTE_SEQ1 - [7:0] */ +#define WM8995_SPK1_MUTE_SEQ1_WIDTH 8 /* SPK1_MUTE_SEQ1 - [7:0] */ + +/* + * R2056 (0x808) - Left PDM Speaker 2 + */ +#define WM8995_SPK2L_ENA 0x0010 /* SPK2L_ENA */ +#define WM8995_SPK2L_ENA_MASK 0x0010 /* SPK2L_ENA */ +#define WM8995_SPK2L_ENA_SHIFT 4 /* SPK2L_ENA */ +#define WM8995_SPK2L_ENA_WIDTH 1 /* SPK2L_ENA */ +#define WM8995_SPK2L_MUTE 0x0008 /* SPK2L_MUTE */ +#define WM8995_SPK2L_MUTE_MASK 0x0008 /* SPK2L_MUTE */ +#define WM8995_SPK2L_MUTE_SHIFT 3 /* SPK2L_MUTE */ +#define WM8995_SPK2L_MUTE_WIDTH 1 /* SPK2L_MUTE */ +#define WM8995_SPK2L_MUTE_ZC 0x0004 /* SPK2L_MUTE_ZC */ +#define WM8995_SPK2L_MUTE_ZC_MASK 0x0004 /* SPK2L_MUTE_ZC */ +#define WM8995_SPK2L_MUTE_ZC_SHIFT 2 /* SPK2L_MUTE_ZC */ +#define WM8995_SPK2L_MUTE_ZC_WIDTH 1 /* SPK2L_MUTE_ZC */ +#define WM8995_SPK2L_SRC_MASK 0x0003 /* SPK2L_SRC - [1:0] */ +#define WM8995_SPK2L_SRC_SHIFT 0 /* SPK2L_SRC - [1:0] */ +#define WM8995_SPK2L_SRC_WIDTH 2 /* SPK2L_SRC - [1:0] */ + +/* + * R2057 (0x809) - Right PDM Speaker 2 + */ +#define WM8995_SPK2R_ENA 0x0010 /* SPK2R_ENA */ +#define WM8995_SPK2R_ENA_MASK 0x0010 /* SPK2R_ENA */ +#define WM8995_SPK2R_ENA_SHIFT 4 /* SPK2R_ENA */ +#define WM8995_SPK2R_ENA_WIDTH 1 /* SPK2R_ENA */ +#define WM8995_SPK2R_MUTE 0x0008 /* SPK2R_MUTE */ +#define WM8995_SPK2R_MUTE_MASK 0x0008 /* SPK2R_MUTE */ +#define WM8995_SPK2R_MUTE_SHIFT 3 /* SPK2R_MUTE */ +#define WM8995_SPK2R_MUTE_WIDTH 1 /* SPK2R_MUTE */ +#define WM8995_SPK2R_MUTE_ZC 0x0004 /* SPK2R_MUTE_ZC */ +#define WM8995_SPK2R_MUTE_ZC_MASK 0x0004 /* SPK2R_MUTE_ZC */ +#define WM8995_SPK2R_MUTE_ZC_SHIFT 2 /* SPK2R_MUTE_ZC */ +#define WM8995_SPK2R_MUTE_ZC_WIDTH 1 /* SPK2R_MUTE_ZC */ +#define WM8995_SPK2R_SRC_MASK 0x0003 /* SPK2R_SRC - [1:0] */ +#define WM8995_SPK2R_SRC_SHIFT 0 /* SPK2R_SRC - [1:0] */ +#define WM8995_SPK2R_SRC_WIDTH 2 /* SPK2R_SRC - [1:0] */ + +/* + * R2058 (0x80A) - PDM Speaker 2 Mute Sequence + */ +#define WM8995_SPK2_MUTE_SEQ1_MASK 0x00FF /* SPK2_MUTE_SEQ1 - [7:0] */ +#define WM8995_SPK2_MUTE_SEQ1_SHIFT 0 /* SPK2_MUTE_SEQ1 - [7:0] */ +#define WM8995_SPK2_MUTE_SEQ1_WIDTH 8 /* SPK2_MUTE_SEQ1 - [7:0] */ + +#define WM8995_CLASS_W_SWITCH(xname, reg, shift, max, invert) \ + SOC_SINGLE_EXT(xname, reg, shift, max, invert, \ + snd_soc_dapm_get_volsw, wm8995_put_class_w) + +struct wm8995_reg_access { + u16 read; + u16 write; + u16 vol; +}; + +/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ +enum clk_src { + WM8995_SYSCLK_MCLK1 = 1, + WM8995_SYSCLK_MCLK2, + WM8995_SYSCLK_FLL1, + WM8995_SYSCLK_FLL2, + WM8995_SYSCLK_OPCLK +}; + +#define WM8995_FLL1 1 +#define WM8995_FLL2 2 + +#define WM8995_FLL_SRC_MCLK1 1 +#define WM8995_FLL_SRC_MCLK2 2 +#define WM8995_FLL_SRC_LRCLK 3 +#define WM8995_FLL_SRC_BCLK 4 + +#endif /* _WM8995_H */ diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c new file mode 100644 index 0000000000..df6195778c --- /dev/null +++ b/sound/soc/codecs/wm8996.c @@ -0,0 +1,3098 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * wm8996.c - WM8996 audio codec interface + * + * Copyright 2011-2 Wolfson Microelectronics PLC. + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "wm8996.h" + +#define WM8996_AIFS 2 + +#define HPOUT1L 1 +#define HPOUT1R 2 +#define HPOUT2L 4 +#define HPOUT2R 8 + +#define WM8996_NUM_SUPPLIES 3 +static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = { + "DBVDD", + "AVDD1", + "AVDD2", +}; + +struct wm8996_priv { + struct device *dev; + struct regmap *regmap; + struct snd_soc_component *component; + + int ldo1ena; + + int sysclk; + int sysclk_src; + + int fll_src; + int fll_fref; + int fll_fout; + + struct completion fll_lock; + + u16 dcs_pending; + struct completion dcs_done; + + u16 hpout_ena; + u16 hpout_pending; + + struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES]; + struct notifier_block disable_nb[WM8996_NUM_SUPPLIES]; + int bg_ena; + + struct wm8996_pdata pdata; + + int rx_rate[WM8996_AIFS]; + int bclk_rate[WM8996_AIFS]; + + /* Platform dependant ReTune mobile configuration */ + int num_retune_mobile_texts; + const char **retune_mobile_texts; + int retune_mobile_cfg[2]; + struct soc_enum retune_mobile_enum; + + struct snd_soc_jack *jack; + bool detecting; + bool jack_mic; + int jack_flips; + wm8996_polarity_fn polarity_cb; + +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif +}; + +/* We can't use the same notifier block for more than one supply and + * there's no way I can see to get from a callback to the caller + * except container_of(). + */ +#define WM8996_REGULATOR_EVENT(n) \ +static int wm8996_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct wm8996_priv *wm8996 = container_of(nb, struct wm8996_priv, \ + disable_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + regcache_mark_dirty(wm8996->regmap); \ + } \ + return 0; \ +} + +WM8996_REGULATOR_EVENT(0) +WM8996_REGULATOR_EVENT(1) +WM8996_REGULATOR_EVENT(2) + +static const struct reg_default wm8996_reg[] = { + { WM8996_POWER_MANAGEMENT_1, 0x0 }, + { WM8996_POWER_MANAGEMENT_2, 0x0 }, + { WM8996_POWER_MANAGEMENT_3, 0x0 }, + { WM8996_POWER_MANAGEMENT_4, 0x0 }, + { WM8996_POWER_MANAGEMENT_5, 0x0 }, + { WM8996_POWER_MANAGEMENT_6, 0x0 }, + { WM8996_POWER_MANAGEMENT_7, 0x10 }, + { WM8996_POWER_MANAGEMENT_8, 0x0 }, + { WM8996_LEFT_LINE_INPUT_VOLUME, 0x0 }, + { WM8996_RIGHT_LINE_INPUT_VOLUME, 0x0 }, + { WM8996_LINE_INPUT_CONTROL, 0x0 }, + { WM8996_DAC1_HPOUT1_VOLUME, 0x88 }, + { WM8996_DAC2_HPOUT2_VOLUME, 0x88 }, + { WM8996_DAC1_LEFT_VOLUME, 0x2c0 }, + { WM8996_DAC1_RIGHT_VOLUME, 0x2c0 }, + { WM8996_DAC2_LEFT_VOLUME, 0x2c0 }, + { WM8996_DAC2_RIGHT_VOLUME, 0x2c0 }, + { WM8996_OUTPUT1_LEFT_VOLUME, 0x80 }, + { WM8996_OUTPUT1_RIGHT_VOLUME, 0x80 }, + { WM8996_OUTPUT2_LEFT_VOLUME, 0x80 }, + { WM8996_OUTPUT2_RIGHT_VOLUME, 0x80 }, + { WM8996_MICBIAS_1, 0x39 }, + { WM8996_MICBIAS_2, 0x39 }, + { WM8996_LDO_1, 0x3 }, + { WM8996_LDO_2, 0x13 }, + { WM8996_ACCESSORY_DETECT_MODE_1, 0x4 }, + { WM8996_ACCESSORY_DETECT_MODE_2, 0x0 }, + { WM8996_HEADPHONE_DETECT_1, 0x20 }, + { WM8996_HEADPHONE_DETECT_2, 0x0 }, + { WM8996_MIC_DETECT_1, 0x7600 }, + { WM8996_MIC_DETECT_2, 0xbf }, + { WM8996_CHARGE_PUMP_1, 0x1f25 }, + { WM8996_CHARGE_PUMP_2, 0xab19 }, + { WM8996_DC_SERVO_1, 0x0 }, + { WM8996_DC_SERVO_3, 0x0 }, + { WM8996_DC_SERVO_5, 0x2a2a }, + { WM8996_DC_SERVO_6, 0x0 }, + { WM8996_DC_SERVO_7, 0x0 }, + { WM8996_ANALOGUE_HP_1, 0x0 }, + { WM8996_ANALOGUE_HP_2, 0x0 }, + { WM8996_CONTROL_INTERFACE_1, 0x8004 }, + { WM8996_WRITE_SEQUENCER_CTRL_1, 0x0 }, + { WM8996_WRITE_SEQUENCER_CTRL_2, 0x0 }, + { WM8996_AIF_CLOCKING_1, 0x0 }, + { WM8996_AIF_CLOCKING_2, 0x0 }, + { WM8996_CLOCKING_1, 0x10 }, + { WM8996_CLOCKING_2, 0x0 }, + { WM8996_AIF_RATE, 0x83 }, + { WM8996_FLL_CONTROL_1, 0x0 }, + { WM8996_FLL_CONTROL_2, 0x0 }, + { WM8996_FLL_CONTROL_3, 0x0 }, + { WM8996_FLL_CONTROL_4, 0x5dc0 }, + { WM8996_FLL_CONTROL_5, 0xc84 }, + { WM8996_FLL_EFS_1, 0x0 }, + { WM8996_FLL_EFS_2, 0x2 }, + { WM8996_AIF1_CONTROL, 0x0 }, + { WM8996_AIF1_BCLK, 0x0 }, + { WM8996_AIF1_TX_LRCLK_1, 0x80 }, + { WM8996_AIF1_TX_LRCLK_2, 0x8 }, + { WM8996_AIF1_RX_LRCLK_1, 0x80 }, + { WM8996_AIF1_RX_LRCLK_2, 0x0 }, + { WM8996_AIF1TX_DATA_CONFIGURATION_1, 0x1818 }, + { WM8996_AIF1TX_DATA_CONFIGURATION_2, 0 }, + { WM8996_AIF1RX_DATA_CONFIGURATION, 0x1818 }, + { WM8996_AIF1TX_CHANNEL_0_CONFIGURATION, 0x0 }, + { WM8996_AIF1TX_CHANNEL_1_CONFIGURATION, 0x0 }, + { WM8996_AIF1TX_CHANNEL_2_CONFIGURATION, 0x0 }, + { WM8996_AIF1TX_CHANNEL_3_CONFIGURATION, 0x0 }, + { WM8996_AIF1TX_CHANNEL_4_CONFIGURATION, 0x0 }, + { WM8996_AIF1TX_CHANNEL_5_CONFIGURATION, 0x0 }, + { WM8996_AIF1RX_CHANNEL_0_CONFIGURATION, 0x0 }, + { WM8996_AIF1RX_CHANNEL_1_CONFIGURATION, 0x0 }, + { WM8996_AIF1RX_CHANNEL_2_CONFIGURATION, 0x0 }, + { WM8996_AIF1RX_CHANNEL_3_CONFIGURATION, 0x0 }, + { WM8996_AIF1RX_CHANNEL_4_CONFIGURATION, 0x0 }, + { WM8996_AIF1RX_CHANNEL_5_CONFIGURATION, 0x0 }, + { WM8996_AIF1RX_MONO_CONFIGURATION, 0x0 }, + { WM8996_AIF1TX_TEST, 0x7 }, + { WM8996_AIF2_CONTROL, 0x0 }, + { WM8996_AIF2_BCLK, 0x0 }, + { WM8996_AIF2_TX_LRCLK_1, 0x80 }, + { WM8996_AIF2_TX_LRCLK_2, 0x8 }, + { WM8996_AIF2_RX_LRCLK_1, 0x80 }, + { WM8996_AIF2_RX_LRCLK_2, 0x0 }, + { WM8996_AIF2TX_DATA_CONFIGURATION_1, 0x1818 }, + { WM8996_AIF2RX_DATA_CONFIGURATION, 0x1818 }, + { WM8996_AIF2RX_DATA_CONFIGURATION, 0x0 }, + { WM8996_AIF2TX_CHANNEL_0_CONFIGURATION, 0x0 }, + { WM8996_AIF2TX_CHANNEL_1_CONFIGURATION, 0x0 }, + { WM8996_AIF2RX_CHANNEL_0_CONFIGURATION, 0x0 }, + { WM8996_AIF2RX_CHANNEL_1_CONFIGURATION, 0x0 }, + { WM8996_AIF2RX_MONO_CONFIGURATION, 0x0 }, + { WM8996_AIF2TX_TEST, 0x1 }, + { WM8996_DSP1_TX_LEFT_VOLUME, 0xc0 }, + { WM8996_DSP1_TX_RIGHT_VOLUME, 0xc0 }, + { WM8996_DSP1_RX_LEFT_VOLUME, 0xc0 }, + { WM8996_DSP1_RX_RIGHT_VOLUME, 0xc0 }, + { WM8996_DSP1_TX_FILTERS, 0x2000 }, + { WM8996_DSP1_RX_FILTERS_1, 0x200 }, + { WM8996_DSP1_RX_FILTERS_2, 0x10 }, + { WM8996_DSP1_DRC_1, 0x98 }, + { WM8996_DSP1_DRC_2, 0x845 }, + { WM8996_DSP1_RX_EQ_GAINS_1, 0x6318 }, + { WM8996_DSP1_RX_EQ_GAINS_2, 0x6300 }, + { WM8996_DSP1_RX_EQ_BAND_1_A, 0xfca }, + { WM8996_DSP1_RX_EQ_BAND_1_B, 0x400 }, + { WM8996_DSP1_RX_EQ_BAND_1_PG, 0xd8 }, + { WM8996_DSP1_RX_EQ_BAND_2_A, 0x1eb5 }, + { WM8996_DSP1_RX_EQ_BAND_2_B, 0xf145 }, + { WM8996_DSP1_RX_EQ_BAND_2_C, 0xb75 }, + { WM8996_DSP1_RX_EQ_BAND_2_PG, 0x1c5 }, + { WM8996_DSP1_RX_EQ_BAND_3_A, 0x1c58 }, + { WM8996_DSP1_RX_EQ_BAND_3_B, 0xf373 }, + { WM8996_DSP1_RX_EQ_BAND_3_C, 0xa54 }, + { WM8996_DSP1_RX_EQ_BAND_3_PG, 0x558 }, + { WM8996_DSP1_RX_EQ_BAND_4_A, 0x168e }, + { WM8996_DSP1_RX_EQ_BAND_4_B, 0xf829 }, + { WM8996_DSP1_RX_EQ_BAND_4_C, 0x7ad }, + { WM8996_DSP1_RX_EQ_BAND_4_PG, 0x1103 }, + { WM8996_DSP1_RX_EQ_BAND_5_A, 0x564 }, + { WM8996_DSP1_RX_EQ_BAND_5_B, 0x559 }, + { WM8996_DSP1_RX_EQ_BAND_5_PG, 0x4000 }, + { WM8996_DSP2_TX_LEFT_VOLUME, 0xc0 }, + { WM8996_DSP2_TX_RIGHT_VOLUME, 0xc0 }, + { WM8996_DSP2_RX_LEFT_VOLUME, 0xc0 }, + { WM8996_DSP2_RX_RIGHT_VOLUME, 0xc0 }, + { WM8996_DSP2_TX_FILTERS, 0x2000 }, + { WM8996_DSP2_RX_FILTERS_1, 0x200 }, + { WM8996_DSP2_RX_FILTERS_2, 0x10 }, + { WM8996_DSP2_DRC_1, 0x98 }, + { WM8996_DSP2_DRC_2, 0x845 }, + { WM8996_DSP2_RX_EQ_GAINS_1, 0x6318 }, + { WM8996_DSP2_RX_EQ_GAINS_2, 0x6300 }, + { WM8996_DSP2_RX_EQ_BAND_1_A, 0xfca }, + { WM8996_DSP2_RX_EQ_BAND_1_B, 0x400 }, + { WM8996_DSP2_RX_EQ_BAND_1_PG, 0xd8 }, + { WM8996_DSP2_RX_EQ_BAND_2_A, 0x1eb5 }, + { WM8996_DSP2_RX_EQ_BAND_2_B, 0xf145 }, + { WM8996_DSP2_RX_EQ_BAND_2_C, 0xb75 }, + { WM8996_DSP2_RX_EQ_BAND_2_PG, 0x1c5 }, + { WM8996_DSP2_RX_EQ_BAND_3_A, 0x1c58 }, + { WM8996_DSP2_RX_EQ_BAND_3_B, 0xf373 }, + { WM8996_DSP2_RX_EQ_BAND_3_C, 0xa54 }, + { WM8996_DSP2_RX_EQ_BAND_3_PG, 0x558 }, + { WM8996_DSP2_RX_EQ_BAND_4_A, 0x168e }, + { WM8996_DSP2_RX_EQ_BAND_4_B, 0xf829 }, + { WM8996_DSP2_RX_EQ_BAND_4_C, 0x7ad }, + { WM8996_DSP2_RX_EQ_BAND_4_PG, 0x1103 }, + { WM8996_DSP2_RX_EQ_BAND_5_A, 0x564 }, + { WM8996_DSP2_RX_EQ_BAND_5_B, 0x559 }, + { WM8996_DSP2_RX_EQ_BAND_5_PG, 0x4000 }, + { WM8996_DAC1_MIXER_VOLUMES, 0x0 }, + { WM8996_DAC1_LEFT_MIXER_ROUTING, 0x0 }, + { WM8996_DAC1_RIGHT_MIXER_ROUTING, 0x0 }, + { WM8996_DAC2_MIXER_VOLUMES, 0x0 }, + { WM8996_DAC2_LEFT_MIXER_ROUTING, 0x0 }, + { WM8996_DAC2_RIGHT_MIXER_ROUTING, 0x0 }, + { WM8996_DSP1_TX_LEFT_MIXER_ROUTING, 0x0 }, + { WM8996_DSP1_TX_RIGHT_MIXER_ROUTING, 0x0 }, + { WM8996_DSP2_TX_LEFT_MIXER_ROUTING, 0x0 }, + { WM8996_DSP2_TX_RIGHT_MIXER_ROUTING, 0x0 }, + { WM8996_DSP_TX_MIXER_SELECT, 0x0 }, + { WM8996_DAC_SOFTMUTE, 0x0 }, + { WM8996_OVERSAMPLING, 0xd }, + { WM8996_SIDETONE, 0x1040 }, + { WM8996_GPIO_1, 0xa101 }, + { WM8996_GPIO_2, 0xa101 }, + { WM8996_GPIO_3, 0xa101 }, + { WM8996_GPIO_4, 0xa101 }, + { WM8996_GPIO_5, 0xa101 }, + { WM8996_PULL_CONTROL_1, 0x0 }, + { WM8996_PULL_CONTROL_2, 0x140 }, + { WM8996_INTERRUPT_STATUS_1_MASK, 0x1f }, + { WM8996_INTERRUPT_STATUS_2_MASK, 0x1ecf }, + { WM8996_LEFT_PDM_SPEAKER, 0x0 }, + { WM8996_RIGHT_PDM_SPEAKER, 0x1 }, + { WM8996_PDM_SPEAKER_MUTE_SEQUENCE, 0x69 }, + { WM8996_PDM_SPEAKER_VOLUME, 0x66 }, +}; + +static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 150, 0); +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0); +static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0); +static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0); +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static const DECLARE_TLV_DB_SCALE(threedstereo_tlv, -1600, 183, 1); + +static const char *sidetone_hpf_text[] = { + "2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz" +}; + +static SOC_ENUM_SINGLE_DECL(sidetone_hpf, + WM8996_SIDETONE, 7, sidetone_hpf_text); + +static const char *hpf_mode_text[] = { + "HiFi", "Custom", "Voice" +}; + +static SOC_ENUM_SINGLE_DECL(dsp1tx_hpf_mode, + WM8996_DSP1_TX_FILTERS, 3, hpf_mode_text); + +static SOC_ENUM_SINGLE_DECL(dsp2tx_hpf_mode, + WM8996_DSP2_TX_FILTERS, 3, hpf_mode_text); + +static const char *hpf_cutoff_text[] = { + "50Hz", "75Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static SOC_ENUM_SINGLE_DECL(dsp1tx_hpf_cutoff, + WM8996_DSP1_TX_FILTERS, 0, hpf_cutoff_text); + +static SOC_ENUM_SINGLE_DECL(dsp2tx_hpf_cutoff, + WM8996_DSP2_TX_FILTERS, 0, hpf_cutoff_text); + +static void wm8996_set_retune_mobile(struct snd_soc_component *component, int block) +{ + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + struct wm8996_pdata *pdata = &wm8996->pdata; + int base, best, best_val, save, i, cfg, iface; + + if (!wm8996->num_retune_mobile_texts) + return; + + switch (block) { + case 0: + base = WM8996_DSP1_RX_EQ_GAINS_1; + if (snd_soc_component_read(component, WM8996_POWER_MANAGEMENT_8) & + WM8996_DSP1RX_SRC) + iface = 1; + else + iface = 0; + break; + case 1: + base = WM8996_DSP1_RX_EQ_GAINS_2; + if (snd_soc_component_read(component, WM8996_POWER_MANAGEMENT_8) & + WM8996_DSP2RX_SRC) + iface = 1; + else + iface = 0; + break; + default: + return; + } + + /* Find the version of the currently selected configuration + * with the nearest sample rate. */ + cfg = wm8996->retune_mobile_cfg[block]; + best = 0; + best_val = INT_MAX; + for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) { + if (strcmp(pdata->retune_mobile_cfgs[i].name, + wm8996->retune_mobile_texts[cfg]) == 0 && + abs(pdata->retune_mobile_cfgs[i].rate + - wm8996->rx_rate[iface]) < best_val) { + best = i; + best_val = abs(pdata->retune_mobile_cfgs[i].rate + - wm8996->rx_rate[iface]); + } + } + + dev_dbg(component->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n", + block, + pdata->retune_mobile_cfgs[best].name, + pdata->retune_mobile_cfgs[best].rate, + wm8996->rx_rate[iface]); + + /* The EQ will be disabled while reconfiguring it, remember the + * current configuration. + */ + save = snd_soc_component_read(component, base); + save &= WM8996_DSP1RX_EQ_ENA; + + for (i = 0; i < ARRAY_SIZE(pdata->retune_mobile_cfgs[best].regs); i++) + snd_soc_component_update_bits(component, base + i, 0xffff, + pdata->retune_mobile_cfgs[best].regs[i]); + + snd_soc_component_update_bits(component, base, WM8996_DSP1RX_EQ_ENA, save); +} + +/* Icky as hell but saves code duplication */ +static int wm8996_get_retune_mobile_block(const char *name) +{ + if (strcmp(name, "DSP1 EQ Mode") == 0) + return 0; + if (strcmp(name, "DSP2 EQ Mode") == 0) + return 1; + return -EINVAL; +} + +static int wm8996_put_retune_mobile_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + struct wm8996_pdata *pdata = &wm8996->pdata; + int block = wm8996_get_retune_mobile_block(kcontrol->id.name); + int value = ucontrol->value.enumerated.item[0]; + + if (block < 0) + return block; + + if (value >= pdata->num_retune_mobile_cfgs) + return -EINVAL; + + wm8996->retune_mobile_cfg[block] = value; + + wm8996_set_retune_mobile(component, block); + + return 0; +} + +static int wm8996_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + int block = wm8996_get_retune_mobile_block(kcontrol->id.name); + + if (block < 0) + return block; + ucontrol->value.enumerated.item[0] = wm8996->retune_mobile_cfg[block]; + + return 0; +} + +static const struct snd_kcontrol_new wm8996_snd_controls[] = { +SOC_DOUBLE_R_TLV("Capture Volume", WM8996_LEFT_LINE_INPUT_VOLUME, + WM8996_RIGHT_LINE_INPUT_VOLUME, 0, 31, 0, inpga_tlv), +SOC_DOUBLE_R("Capture ZC Switch", WM8996_LEFT_LINE_INPUT_VOLUME, + WM8996_RIGHT_LINE_INPUT_VOLUME, 5, 1, 0), + +SOC_DOUBLE_TLV("DAC1 Sidetone Volume", WM8996_DAC1_MIXER_VOLUMES, + 0, 5, 24, 0, sidetone_tlv), +SOC_DOUBLE_TLV("DAC2 Sidetone Volume", WM8996_DAC2_MIXER_VOLUMES, + 0, 5, 24, 0, sidetone_tlv), +SOC_SINGLE("Sidetone LPF Switch", WM8996_SIDETONE, 12, 1, 0), +SOC_ENUM("Sidetone HPF Cut-off", sidetone_hpf), +SOC_SINGLE("Sidetone HPF Switch", WM8996_SIDETONE, 6, 1, 0), + +SOC_DOUBLE_R_TLV("DSP1 Capture Volume", WM8996_DSP1_TX_LEFT_VOLUME, + WM8996_DSP1_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv), +SOC_DOUBLE_R_TLV("DSP2 Capture Volume", WM8996_DSP2_TX_LEFT_VOLUME, + WM8996_DSP2_TX_RIGHT_VOLUME, 1, 96, 0, digital_tlv), + +SOC_SINGLE("DSP1 Capture Notch Filter Switch", WM8996_DSP1_TX_FILTERS, + 13, 1, 0), +SOC_DOUBLE("DSP1 Capture HPF Switch", WM8996_DSP1_TX_FILTERS, 12, 11, 1, 0), +SOC_ENUM("DSP1 Capture HPF Mode", dsp1tx_hpf_mode), +SOC_ENUM("DSP1 Capture HPF Cutoff", dsp1tx_hpf_cutoff), + +SOC_SINGLE("DSP2 Capture Notch Filter Switch", WM8996_DSP2_TX_FILTERS, + 13, 1, 0), +SOC_DOUBLE("DSP2 Capture HPF Switch", WM8996_DSP2_TX_FILTERS, 12, 11, 1, 0), +SOC_ENUM("DSP2 Capture HPF Mode", dsp2tx_hpf_mode), +SOC_ENUM("DSP2 Capture HPF Cutoff", dsp2tx_hpf_cutoff), + +SOC_DOUBLE_R_TLV("DSP1 Playback Volume", WM8996_DSP1_RX_LEFT_VOLUME, + WM8996_DSP1_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv), +SOC_SINGLE("DSP1 Playback Switch", WM8996_DSP1_RX_FILTERS_1, 9, 1, 1), + +SOC_DOUBLE_R_TLV("DSP2 Playback Volume", WM8996_DSP2_RX_LEFT_VOLUME, + WM8996_DSP2_RX_RIGHT_VOLUME, 1, 112, 0, digital_tlv), +SOC_SINGLE("DSP2 Playback Switch", WM8996_DSP2_RX_FILTERS_1, 9, 1, 1), + +SOC_DOUBLE_R_TLV("DAC1 Volume", WM8996_DAC1_LEFT_VOLUME, + WM8996_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv), +SOC_DOUBLE_R("DAC1 Switch", WM8996_DAC1_LEFT_VOLUME, + WM8996_DAC1_RIGHT_VOLUME, 9, 1, 1), + +SOC_DOUBLE_R_TLV("DAC2 Volume", WM8996_DAC2_LEFT_VOLUME, + WM8996_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv), +SOC_DOUBLE_R("DAC2 Switch", WM8996_DAC2_LEFT_VOLUME, + WM8996_DAC2_RIGHT_VOLUME, 9, 1, 1), + +SOC_SINGLE("Speaker High Performance Switch", WM8996_OVERSAMPLING, 3, 1, 0), +SOC_SINGLE("DMIC High Performance Switch", WM8996_OVERSAMPLING, 2, 1, 0), +SOC_SINGLE("ADC High Performance Switch", WM8996_OVERSAMPLING, 1, 1, 0), +SOC_SINGLE("DAC High Performance Switch", WM8996_OVERSAMPLING, 0, 1, 0), + +SOC_SINGLE("DAC Soft Mute Switch", WM8996_DAC_SOFTMUTE, 1, 1, 0), +SOC_SINGLE("DAC Slow Soft Mute Switch", WM8996_DAC_SOFTMUTE, 0, 1, 0), + +SOC_SINGLE("DSP1 3D Stereo Switch", WM8996_DSP1_RX_FILTERS_2, 8, 1, 0), +SOC_SINGLE("DSP2 3D Stereo Switch", WM8996_DSP2_RX_FILTERS_2, 8, 1, 0), + +SOC_SINGLE_TLV("DSP1 3D Stereo Volume", WM8996_DSP1_RX_FILTERS_2, 10, 15, + 0, threedstereo_tlv), +SOC_SINGLE_TLV("DSP2 3D Stereo Volume", WM8996_DSP2_RX_FILTERS_2, 10, 15, + 0, threedstereo_tlv), + +SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8996_DAC1_HPOUT1_VOLUME, 0, 4, + 8, 0, out_digital_tlv), +SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8996_DAC2_HPOUT2_VOLUME, 0, 4, + 8, 0, out_digital_tlv), + +SOC_DOUBLE_R_TLV("Output 1 Volume", WM8996_OUTPUT1_LEFT_VOLUME, + WM8996_OUTPUT1_RIGHT_VOLUME, 0, 12, 0, out_tlv), +SOC_DOUBLE_R("Output 1 ZC Switch", WM8996_OUTPUT1_LEFT_VOLUME, + WM8996_OUTPUT1_RIGHT_VOLUME, 7, 1, 0), + +SOC_DOUBLE_R_TLV("Output 2 Volume", WM8996_OUTPUT2_LEFT_VOLUME, + WM8996_OUTPUT2_RIGHT_VOLUME, 0, 12, 0, out_tlv), +SOC_DOUBLE_R("Output 2 ZC Switch", WM8996_OUTPUT2_LEFT_VOLUME, + WM8996_OUTPUT2_RIGHT_VOLUME, 7, 1, 0), + +SOC_DOUBLE_TLV("Speaker Volume", WM8996_PDM_SPEAKER_VOLUME, 0, 4, 8, 0, + spk_tlv), +SOC_DOUBLE_R("Speaker Switch", WM8996_LEFT_PDM_SPEAKER, + WM8996_RIGHT_PDM_SPEAKER, 3, 1, 1), +SOC_DOUBLE_R("Speaker ZC Switch", WM8996_LEFT_PDM_SPEAKER, + WM8996_RIGHT_PDM_SPEAKER, 2, 1, 0), + +SOC_SINGLE("DSP1 EQ Switch", WM8996_DSP1_RX_EQ_GAINS_1, 0, 1, 0), +SOC_SINGLE("DSP2 EQ Switch", WM8996_DSP2_RX_EQ_GAINS_1, 0, 1, 0), + +SOC_SINGLE("DSP1 DRC TXL Switch", WM8996_DSP1_DRC_1, 0, 1, 0), +SOC_SINGLE("DSP1 DRC TXR Switch", WM8996_DSP1_DRC_1, 1, 1, 0), +SOC_SINGLE("DSP1 DRC RX Switch", WM8996_DSP1_DRC_1, 2, 1, 0), +SND_SOC_BYTES_MASK("DSP1 DRC", WM8996_DSP1_DRC_1, 5, + WM8996_DSP1RX_DRC_ENA | WM8996_DSP1TXL_DRC_ENA | + WM8996_DSP1TXR_DRC_ENA), + +SOC_SINGLE("DSP2 DRC TXL Switch", WM8996_DSP2_DRC_1, 0, 1, 0), +SOC_SINGLE("DSP2 DRC TXR Switch", WM8996_DSP2_DRC_1, 1, 1, 0), +SOC_SINGLE("DSP2 DRC RX Switch", WM8996_DSP2_DRC_1, 2, 1, 0), +SND_SOC_BYTES_MASK("DSP2 DRC", WM8996_DSP2_DRC_1, 5, + WM8996_DSP2RX_DRC_ENA | WM8996_DSP2TXL_DRC_ENA | + WM8996_DSP2TXR_DRC_ENA), +}; + +static const struct snd_kcontrol_new wm8996_eq_controls[] = { +SOC_SINGLE_TLV("DSP1 EQ B1 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 11, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("DSP1 EQ B2 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 6, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("DSP1 EQ B3 Volume", WM8996_DSP1_RX_EQ_GAINS_1, 1, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("DSP1 EQ B4 Volume", WM8996_DSP1_RX_EQ_GAINS_2, 11, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("DSP1 EQ B5 Volume", WM8996_DSP1_RX_EQ_GAINS_2, 6, 31, 0, + eq_tlv), + +SOC_SINGLE_TLV("DSP2 EQ B1 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 11, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("DSP2 EQ B2 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 6, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("DSP2 EQ B3 Volume", WM8996_DSP2_RX_EQ_GAINS_1, 1, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("DSP2 EQ B4 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 11, 31, 0, + eq_tlv), +SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 6, 31, 0, + eq_tlv), +}; + +static void wm8996_bg_enable(struct snd_soc_component *component) +{ + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + + wm8996->bg_ena++; + if (wm8996->bg_ena == 1) { + snd_soc_component_update_bits(component, WM8996_POWER_MANAGEMENT_1, + WM8996_BG_ENA, WM8996_BG_ENA); + msleep(2); + } +} + +static void wm8996_bg_disable(struct snd_soc_component *component) +{ + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + + wm8996->bg_ena--; + if (!wm8996->bg_ena) + snd_soc_component_update_bits(component, WM8996_POWER_MANAGEMENT_1, + WM8996_BG_ENA, 0); +} + +static int bg_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm8996_bg_enable(component); + break; + case SND_SOC_DAPM_POST_PMD: + wm8996_bg_disable(component); + break; + default: + WARN(1, "Invalid event %d\n", event); + ret = -EINVAL; + } + + return ret; +} + +static int cp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msleep(5); + break; + default: + WARN(1, "Invalid event %d\n", event); + } + + return 0; +} + +static int rmv_short_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + + /* Record which outputs we enabled */ + switch (event) { + case SND_SOC_DAPM_PRE_PMD: + wm8996->hpout_pending &= ~w->shift; + break; + case SND_SOC_DAPM_PRE_PMU: + wm8996->hpout_pending |= w->shift; + break; + default: + WARN(1, "Invalid event %d\n", event); + return -EINVAL; + } + + return 0; +} + +static void wait_for_dc_servo(struct snd_soc_component *component, u16 mask) +{ + struct i2c_client *i2c = to_i2c_client(component->dev); + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + int ret; + unsigned long timeout = 200; + + snd_soc_component_write(component, WM8996_DC_SERVO_2, mask); + + /* Use the interrupt if possible */ + do { + if (i2c->irq) { + timeout = wait_for_completion_timeout(&wm8996->dcs_done, + msecs_to_jiffies(200)); + if (timeout == 0) + dev_err(component->dev, "DC servo timed out\n"); + + } else { + msleep(1); + timeout--; + } + + ret = snd_soc_component_read(component, WM8996_DC_SERVO_2); + dev_dbg(component->dev, "DC servo state: %x\n", ret); + } while (timeout && ret & mask); + + if (timeout == 0) + dev_err(component->dev, "DC servo timed out for %x\n", mask); + else + dev_dbg(component->dev, "DC servo complete for %x\n", mask); +} + +static void wm8996_seq_notifier(struct snd_soc_component *component, + enum snd_soc_dapm_type event, int subseq) +{ + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + u16 val, mask; + + /* Complete any pending DC servo starts */ + if (wm8996->dcs_pending) { + dev_dbg(component->dev, "Starting DC servo for %x\n", + wm8996->dcs_pending); + + /* Trigger a startup sequence */ + wait_for_dc_servo(component, wm8996->dcs_pending + << WM8996_DCS_TRIG_STARTUP_0_SHIFT); + + wm8996->dcs_pending = 0; + } + + if (wm8996->hpout_pending != wm8996->hpout_ena) { + dev_dbg(component->dev, "Applying RMV_SHORTs %x->%x\n", + wm8996->hpout_ena, wm8996->hpout_pending); + + val = 0; + mask = 0; + if (wm8996->hpout_pending & HPOUT1L) { + val |= WM8996_HPOUT1L_RMV_SHORT | WM8996_HPOUT1L_OUTP; + mask |= WM8996_HPOUT1L_RMV_SHORT | WM8996_HPOUT1L_OUTP; + } else { + mask |= WM8996_HPOUT1L_RMV_SHORT | + WM8996_HPOUT1L_OUTP | + WM8996_HPOUT1L_DLY; + } + + if (wm8996->hpout_pending & HPOUT1R) { + val |= WM8996_HPOUT1R_RMV_SHORT | WM8996_HPOUT1R_OUTP; + mask |= WM8996_HPOUT1R_RMV_SHORT | WM8996_HPOUT1R_OUTP; + } else { + mask |= WM8996_HPOUT1R_RMV_SHORT | + WM8996_HPOUT1R_OUTP | + WM8996_HPOUT1R_DLY; + } + + snd_soc_component_update_bits(component, WM8996_ANALOGUE_HP_1, mask, val); + + val = 0; + mask = 0; + if (wm8996->hpout_pending & HPOUT2L) { + val |= WM8996_HPOUT2L_RMV_SHORT | WM8996_HPOUT2L_OUTP; + mask |= WM8996_HPOUT2L_RMV_SHORT | WM8996_HPOUT2L_OUTP; + } else { + mask |= WM8996_HPOUT2L_RMV_SHORT | + WM8996_HPOUT2L_OUTP | + WM8996_HPOUT2L_DLY; + } + + if (wm8996->hpout_pending & HPOUT2R) { + val |= WM8996_HPOUT2R_RMV_SHORT | WM8996_HPOUT2R_OUTP; + mask |= WM8996_HPOUT2R_RMV_SHORT | WM8996_HPOUT2R_OUTP; + } else { + mask |= WM8996_HPOUT2R_RMV_SHORT | + WM8996_HPOUT2R_OUTP | + WM8996_HPOUT2R_DLY; + } + + snd_soc_component_update_bits(component, WM8996_ANALOGUE_HP_2, mask, val); + + wm8996->hpout_ena = wm8996->hpout_pending; + } +} + +static int dcs_start(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wm8996->dcs_pending |= 1 << w->shift; + break; + default: + WARN(1, "Invalid event %d\n", event); + return -EINVAL; + } + + return 0; +} + +static const char *sidetone_text[] = { + "IN1", "IN2", +}; + +static SOC_ENUM_SINGLE_DECL(left_sidetone_enum, + WM8996_SIDETONE, 0, sidetone_text); + +static const struct snd_kcontrol_new left_sidetone = + SOC_DAPM_ENUM("Left Sidetone", left_sidetone_enum); + +static SOC_ENUM_SINGLE_DECL(right_sidetone_enum, + WM8996_SIDETONE, 1, sidetone_text); + +static const struct snd_kcontrol_new right_sidetone = + SOC_DAPM_ENUM("Right Sidetone", right_sidetone_enum); + +static const char *spk_text[] = { + "DAC1L", "DAC1R", "DAC2L", "DAC2R" +}; + +static SOC_ENUM_SINGLE_DECL(spkl_enum, + WM8996_LEFT_PDM_SPEAKER, 0, spk_text); + +static const struct snd_kcontrol_new spkl_mux = + SOC_DAPM_ENUM("SPKL", spkl_enum); + +static SOC_ENUM_SINGLE_DECL(spkr_enum, + WM8996_RIGHT_PDM_SPEAKER, 0, spk_text); + +static const struct snd_kcontrol_new spkr_mux = + SOC_DAPM_ENUM("SPKR", spkr_enum); + +static const char *dsp1rx_text[] = { + "AIF1", "AIF2" +}; + +static SOC_ENUM_SINGLE_DECL(dsp1rx_enum, + WM8996_POWER_MANAGEMENT_8, 0, dsp1rx_text); + +static const struct snd_kcontrol_new dsp1rx = + SOC_DAPM_ENUM("DSP1RX", dsp1rx_enum); + +static const char *dsp2rx_text[] = { + "AIF2", "AIF1" +}; + +static SOC_ENUM_SINGLE_DECL(dsp2rx_enum, + WM8996_POWER_MANAGEMENT_8, 4, dsp2rx_text); + +static const struct snd_kcontrol_new dsp2rx = + SOC_DAPM_ENUM("DSP2RX", dsp2rx_enum); + +static const char *aif2tx_text[] = { + "DSP2", "DSP1", "AIF1" +}; + +static SOC_ENUM_SINGLE_DECL(aif2tx_enum, + WM8996_POWER_MANAGEMENT_8, 6, aif2tx_text); + +static const struct snd_kcontrol_new aif2tx = + SOC_DAPM_ENUM("AIF2TX", aif2tx_enum); + +static const char *inmux_text[] = { + "ADC", "DMIC1", "DMIC2" +}; + +static SOC_ENUM_SINGLE_DECL(in1_enum, + WM8996_POWER_MANAGEMENT_7, 0, inmux_text); + +static const struct snd_kcontrol_new in1_mux = + SOC_DAPM_ENUM("IN1 Mux", in1_enum); + +static SOC_ENUM_SINGLE_DECL(in2_enum, + WM8996_POWER_MANAGEMENT_7, 4, inmux_text); + +static const struct snd_kcontrol_new in2_mux = + SOC_DAPM_ENUM("IN2 Mux", in2_enum); + +static const struct snd_kcontrol_new dac2r_mix[] = { +SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, + 5, 1, 0), +SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, + 4, 1, 0), +SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, 1, 1, 0), +SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC2_RIGHT_MIXER_ROUTING, 0, 1, 0), +}; + +static const struct snd_kcontrol_new dac2l_mix[] = { +SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, + 5, 1, 0), +SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, + 4, 1, 0), +SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, 1, 1, 0), +SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC2_LEFT_MIXER_ROUTING, 0, 1, 0), +}; + +static const struct snd_kcontrol_new dac1r_mix[] = { +SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, + 5, 1, 0), +SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, + 4, 1, 0), +SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, 1, 1, 0), +SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC1_RIGHT_MIXER_ROUTING, 0, 1, 0), +}; + +static const struct snd_kcontrol_new dac1l_mix[] = { +SOC_DAPM_SINGLE("Right Sidetone Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, + 5, 1, 0), +SOC_DAPM_SINGLE("Left Sidetone Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, + 4, 1, 0), +SOC_DAPM_SINGLE("DSP2 Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, 1, 1, 0), +SOC_DAPM_SINGLE("DSP1 Switch", WM8996_DAC1_LEFT_MIXER_ROUTING, 0, 1, 0), +}; + +static const struct snd_kcontrol_new dsp1txl[] = { +SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP1_TX_LEFT_MIXER_ROUTING, + 1, 1, 0), +SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP1_TX_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new dsp1txr[] = { +SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP1_TX_RIGHT_MIXER_ROUTING, + 1, 1, 0), +SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP1_TX_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new dsp2txl[] = { +SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP2_TX_LEFT_MIXER_ROUTING, + 1, 1, 0), +SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP2_TX_LEFT_MIXER_ROUTING, + 0, 1, 0), +}; + +static const struct snd_kcontrol_new dsp2txr[] = { +SOC_DAPM_SINGLE("IN1 Switch", WM8996_DSP2_TX_RIGHT_MIXER_ROUTING, + 1, 1, 0), +SOC_DAPM_SINGLE("DAC Switch", WM8996_DSP2_TX_RIGHT_MIXER_ROUTING, + 0, 1, 0), +}; + + +static const struct snd_soc_dapm_widget wm8996_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1LN"), +SND_SOC_DAPM_INPUT("IN1LP"), +SND_SOC_DAPM_INPUT("IN1RN"), +SND_SOC_DAPM_INPUT("IN1RP"), + +SND_SOC_DAPM_INPUT("IN2LN"), +SND_SOC_DAPM_INPUT("IN2LP"), +SND_SOC_DAPM_INPUT("IN2RN"), +SND_SOC_DAPM_INPUT("IN2RP"), + +SND_SOC_DAPM_INPUT("DMIC1DAT"), +SND_SOC_DAPM_INPUT("DMIC2DAT"), + +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event, + SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_SUPPLY("Bandgap", SND_SOC_NOPM, 0, 0, bg_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICB1 Audio", WM8996_MICBIAS_1, 4, 1, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICB2 Audio", WM8996_MICBIAS_2, 4, 1, NULL, 0), +SND_SOC_DAPM_MICBIAS("MICB2", WM8996_POWER_MANAGEMENT_1, 9, 0), +SND_SOC_DAPM_MICBIAS("MICB1", WM8996_POWER_MANAGEMENT_1, 8, 0), + +SND_SOC_DAPM_PGA("IN1L PGA", WM8996_POWER_MANAGEMENT_2, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("IN1R PGA", WM8996_POWER_MANAGEMENT_2, 4, 0, NULL, 0), + +SND_SOC_DAPM_MUX("IN1L Mux", WM8996_POWER_MANAGEMENT_7, 2, 0, &in1_mux), +SND_SOC_DAPM_MUX("IN1R Mux", WM8996_POWER_MANAGEMENT_7, 3, 0, &in1_mux), +SND_SOC_DAPM_MUX("IN2L Mux", WM8996_POWER_MANAGEMENT_7, 6, 0, &in2_mux), +SND_SOC_DAPM_MUX("IN2R Mux", WM8996_POWER_MANAGEMENT_7, 7, 0, &in2_mux), + +SND_SOC_DAPM_SUPPLY("DMIC2", WM8996_POWER_MANAGEMENT_7, 9, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("DMIC1", WM8996_POWER_MANAGEMENT_7, 8, 0, NULL, 0), + +SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8996_POWER_MANAGEMENT_3, 5, 0), +SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8996_POWER_MANAGEMENT_3, 4, 0), +SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8996_POWER_MANAGEMENT_3, 3, 0), +SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8996_POWER_MANAGEMENT_3, 2, 0), + +SND_SOC_DAPM_ADC("ADCL", NULL, WM8996_POWER_MANAGEMENT_3, 1, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, WM8996_POWER_MANAGEMENT_3, 0, 0), + +SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &left_sidetone), +SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &right_sidetone), + +SND_SOC_DAPM_AIF_IN("DSP2RXL", NULL, 0, WM8996_POWER_MANAGEMENT_3, 11, 0), +SND_SOC_DAPM_AIF_IN("DSP2RXR", NULL, 1, WM8996_POWER_MANAGEMENT_3, 10, 0), +SND_SOC_DAPM_AIF_IN("DSP1RXL", NULL, 0, WM8996_POWER_MANAGEMENT_3, 9, 0), +SND_SOC_DAPM_AIF_IN("DSP1RXR", NULL, 1, WM8996_POWER_MANAGEMENT_3, 8, 0), + +SND_SOC_DAPM_MIXER("DSP2TXL", WM8996_POWER_MANAGEMENT_5, 11, 0, + dsp2txl, ARRAY_SIZE(dsp2txl)), +SND_SOC_DAPM_MIXER("DSP2TXR", WM8996_POWER_MANAGEMENT_5, 10, 0, + dsp2txr, ARRAY_SIZE(dsp2txr)), +SND_SOC_DAPM_MIXER("DSP1TXL", WM8996_POWER_MANAGEMENT_5, 9, 0, + dsp1txl, ARRAY_SIZE(dsp1txl)), +SND_SOC_DAPM_MIXER("DSP1TXR", WM8996_POWER_MANAGEMENT_5, 8, 0, + dsp1txr, ARRAY_SIZE(dsp1txr)), + +SND_SOC_DAPM_MIXER("DAC2L Mixer", SND_SOC_NOPM, 0, 0, + dac2l_mix, ARRAY_SIZE(dac2l_mix)), +SND_SOC_DAPM_MIXER("DAC2R Mixer", SND_SOC_NOPM, 0, 0, + dac2r_mix, ARRAY_SIZE(dac2r_mix)), +SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0, + dac1l_mix, ARRAY_SIZE(dac1l_mix)), +SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0, + dac1r_mix, ARRAY_SIZE(dac1r_mix)), + +SND_SOC_DAPM_DAC("DAC2L", NULL, WM8996_POWER_MANAGEMENT_5, 3, 0), +SND_SOC_DAPM_DAC("DAC2R", NULL, WM8996_POWER_MANAGEMENT_5, 2, 0), +SND_SOC_DAPM_DAC("DAC1L", NULL, WM8996_POWER_MANAGEMENT_5, 1, 0), +SND_SOC_DAPM_DAC("DAC1R", NULL, WM8996_POWER_MANAGEMENT_5, 0, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, WM8996_POWER_MANAGEMENT_4, 9, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX0", NULL, 1, WM8996_POWER_MANAGEMENT_4, 8, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, WM8996_POWER_MANAGEMENT_6, 9, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX0", NULL, 1, WM8996_POWER_MANAGEMENT_6, 8, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 5, WM8996_POWER_MANAGEMENT_4, 5, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 4, WM8996_POWER_MANAGEMENT_4, 4, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 3, WM8996_POWER_MANAGEMENT_4, 3, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 2, WM8996_POWER_MANAGEMENT_4, 2, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 1, WM8996_POWER_MANAGEMENT_4, 1, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX0", NULL, 0, WM8996_POWER_MANAGEMENT_4, 0, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 5, WM8996_POWER_MANAGEMENT_6, 5, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 4, WM8996_POWER_MANAGEMENT_6, 4, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 3, WM8996_POWER_MANAGEMENT_6, 3, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 2, WM8996_POWER_MANAGEMENT_6, 2, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 1, WM8996_POWER_MANAGEMENT_6, 1, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX0", NULL, 0, WM8996_POWER_MANAGEMENT_6, 0, 0), + +/* We route as stereo pairs so define some dummy widgets to squash + * things down for now. RXA = 0,1, RXB = 2,3 and so on */ +SND_SOC_DAPM_PGA("AIF1RXA", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("AIF1RXB", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("AIF1RXC", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("AIF2RX", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_PGA("DSP2TX", SND_SOC_NOPM, 0, 0, NULL, 0), + +SND_SOC_DAPM_MUX("DSP1RX", SND_SOC_NOPM, 0, 0, &dsp1rx), +SND_SOC_DAPM_MUX("DSP2RX", SND_SOC_NOPM, 0, 0, &dsp2rx), +SND_SOC_DAPM_MUX("AIF2TX", SND_SOC_NOPM, 0, 0, &aif2tx), + +SND_SOC_DAPM_MUX("SPKL", SND_SOC_NOPM, 0, 0, &spkl_mux), +SND_SOC_DAPM_MUX("SPKR", SND_SOC_NOPM, 0, 0, &spkr_mux), +SND_SOC_DAPM_PGA("SPKL PGA", WM8996_LEFT_PDM_SPEAKER, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPKR PGA", WM8996_RIGHT_PDM_SPEAKER, 4, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("HPOUT2L PGA", 0, WM8996_POWER_MANAGEMENT_1, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPOUT2L_DLY", 1, WM8996_ANALOGUE_HP_2, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPOUT2L_DCS", 2, WM8996_DC_SERVO_1, 2, 0, dcs_start, + SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_S("HPOUT2L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2L, 0, + rmv_short_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_PGA_S("HPOUT2R PGA", 0, WM8996_POWER_MANAGEMENT_1, 6, 0,NULL, 0), +SND_SOC_DAPM_PGA_S("HPOUT2R_DLY", 1, WM8996_ANALOGUE_HP_2, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPOUT2R_DCS", 2, WM8996_DC_SERVO_1, 3, 0, dcs_start, + SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_S("HPOUT2R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT2R, 0, + rmv_short_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_PGA_S("HPOUT1L PGA", 0, WM8996_POWER_MANAGEMENT_1, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPOUT1L_DLY", 1, WM8996_ANALOGUE_HP_1, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPOUT1L_DCS", 2, WM8996_DC_SERVO_1, 0, 0, dcs_start, + SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_S("HPOUT1L_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1L, 0, + rmv_short_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_PGA_S("HPOUT1R PGA", 0, WM8996_POWER_MANAGEMENT_1, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPOUT1R_DLY", 1, WM8996_ANALOGUE_HP_1, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPOUT1R_DCS", 2, WM8996_DC_SERVO_1, 1, 0, dcs_start, + SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_S("HPOUT1R_RMV_SHORT", 3, SND_SOC_NOPM, HPOUT1R, 0, + rmv_short_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2L"), +SND_SOC_DAPM_OUTPUT("HPOUT2R"), +SND_SOC_DAPM_OUTPUT("SPKDAT"), +}; + +static const struct snd_soc_dapm_route wm8996_dapm_routes[] = { + { "AIFCLK", NULL, "SYSCLK" }, + { "SYSDSPCLK", NULL, "SYSCLK" }, + { "Charge Pump", NULL, "SYSCLK" }, + { "Charge Pump", NULL, "CPVDD" }, + + { "MICB1", NULL, "LDO2" }, + { "MICB1", NULL, "MICB1 Audio" }, + { "MICB1", NULL, "Bandgap" }, + { "MICB2", NULL, "LDO2" }, + { "MICB2", NULL, "MICB2 Audio" }, + { "MICB2", NULL, "Bandgap" }, + + { "AIF1RX0", NULL, "AIF1 Playback" }, + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + + { "AIF2RX0", NULL, "AIF2 Playback" }, + { "AIF2RX1", NULL, "AIF2 Playback" }, + + { "AIF1 Capture", NULL, "AIF1TX0" }, + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + + { "AIF2 Capture", NULL, "AIF2TX0" }, + { "AIF2 Capture", NULL, "AIF2TX1" }, + + { "IN1L PGA", NULL, "IN2LN" }, + { "IN1L PGA", NULL, "IN2LP" }, + { "IN1L PGA", NULL, "IN1LN" }, + { "IN1L PGA", NULL, "IN1LP" }, + { "IN1L PGA", NULL, "Bandgap" }, + + { "IN1R PGA", NULL, "IN2RN" }, + { "IN1R PGA", NULL, "IN2RP" }, + { "IN1R PGA", NULL, "IN1RN" }, + { "IN1R PGA", NULL, "IN1RP" }, + { "IN1R PGA", NULL, "Bandgap" }, + + { "ADCL", NULL, "IN1L PGA" }, + + { "ADCR", NULL, "IN1R PGA" }, + + { "DMIC1L", NULL, "DMIC1DAT" }, + { "DMIC1R", NULL, "DMIC1DAT" }, + { "DMIC2L", NULL, "DMIC2DAT" }, + { "DMIC2R", NULL, "DMIC2DAT" }, + + { "DMIC2L", NULL, "DMIC2" }, + { "DMIC2R", NULL, "DMIC2" }, + { "DMIC1L", NULL, "DMIC1" }, + { "DMIC1R", NULL, "DMIC1" }, + + { "IN1L Mux", "ADC", "ADCL" }, + { "IN1L Mux", "DMIC1", "DMIC1L" }, + { "IN1L Mux", "DMIC2", "DMIC2L" }, + + { "IN1R Mux", "ADC", "ADCR" }, + { "IN1R Mux", "DMIC1", "DMIC1R" }, + { "IN1R Mux", "DMIC2", "DMIC2R" }, + + { "IN2L Mux", "ADC", "ADCL" }, + { "IN2L Mux", "DMIC1", "DMIC1L" }, + { "IN2L Mux", "DMIC2", "DMIC2L" }, + + { "IN2R Mux", "ADC", "ADCR" }, + { "IN2R Mux", "DMIC1", "DMIC1R" }, + { "IN2R Mux", "DMIC2", "DMIC2R" }, + + { "Left Sidetone", "IN1", "IN1L Mux" }, + { "Left Sidetone", "IN2", "IN2L Mux" }, + + { "Right Sidetone", "IN1", "IN1R Mux" }, + { "Right Sidetone", "IN2", "IN2R Mux" }, + + { "DSP1TXL", "IN1 Switch", "IN1L Mux" }, + { "DSP1TXR", "IN1 Switch", "IN1R Mux" }, + + { "DSP2TXL", "IN1 Switch", "IN2L Mux" }, + { "DSP2TXR", "IN1 Switch", "IN2R Mux" }, + + { "AIF1TX0", NULL, "DSP1TXL" }, + { "AIF1TX1", NULL, "DSP1TXR" }, + { "AIF1TX2", NULL, "DSP2TXL" }, + { "AIF1TX3", NULL, "DSP2TXR" }, + { "AIF1TX4", NULL, "AIF2RX0" }, + { "AIF1TX5", NULL, "AIF2RX1" }, + + { "AIF1RX0", NULL, "AIFCLK" }, + { "AIF1RX1", NULL, "AIFCLK" }, + { "AIF1RX2", NULL, "AIFCLK" }, + { "AIF1RX3", NULL, "AIFCLK" }, + { "AIF1RX4", NULL, "AIFCLK" }, + { "AIF1RX5", NULL, "AIFCLK" }, + + { "AIF2RX0", NULL, "AIFCLK" }, + { "AIF2RX1", NULL, "AIFCLK" }, + + { "AIF1TX0", NULL, "AIFCLK" }, + { "AIF1TX1", NULL, "AIFCLK" }, + { "AIF1TX2", NULL, "AIFCLK" }, + { "AIF1TX3", NULL, "AIFCLK" }, + { "AIF1TX4", NULL, "AIFCLK" }, + { "AIF1TX5", NULL, "AIFCLK" }, + + { "AIF2TX0", NULL, "AIFCLK" }, + { "AIF2TX1", NULL, "AIFCLK" }, + + { "DSP1RXL", NULL, "SYSDSPCLK" }, + { "DSP1RXR", NULL, "SYSDSPCLK" }, + { "DSP2RXL", NULL, "SYSDSPCLK" }, + { "DSP2RXR", NULL, "SYSDSPCLK" }, + { "DSP1TXL", NULL, "SYSDSPCLK" }, + { "DSP1TXR", NULL, "SYSDSPCLK" }, + { "DSP2TXL", NULL, "SYSDSPCLK" }, + { "DSP2TXR", NULL, "SYSDSPCLK" }, + + { "AIF1RXA", NULL, "AIF1RX0" }, + { "AIF1RXA", NULL, "AIF1RX1" }, + { "AIF1RXB", NULL, "AIF1RX2" }, + { "AIF1RXB", NULL, "AIF1RX3" }, + { "AIF1RXC", NULL, "AIF1RX4" }, + { "AIF1RXC", NULL, "AIF1RX5" }, + + { "AIF2RX", NULL, "AIF2RX0" }, + { "AIF2RX", NULL, "AIF2RX1" }, + + { "AIF2TX", "DSP2", "DSP2TX" }, + { "AIF2TX", "DSP1", "DSP1RX" }, + { "AIF2TX", "AIF1", "AIF1RXC" }, + + { "DSP1RXL", NULL, "DSP1RX" }, + { "DSP1RXR", NULL, "DSP1RX" }, + { "DSP2RXL", NULL, "DSP2RX" }, + { "DSP2RXR", NULL, "DSP2RX" }, + + { "DSP2TX", NULL, "DSP2TXL" }, + { "DSP2TX", NULL, "DSP2TXR" }, + + { "DSP1RX", "AIF1", "AIF1RXA" }, + { "DSP1RX", "AIF2", "AIF2RX" }, + + { "DSP2RX", "AIF1", "AIF1RXB" }, + { "DSP2RX", "AIF2", "AIF2RX" }, + + { "DAC2L Mixer", "DSP2 Switch", "DSP2RXL" }, + { "DAC2L Mixer", "DSP1 Switch", "DSP1RXL" }, + { "DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" }, + { "DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" }, + + { "DAC2R Mixer", "DSP2 Switch", "DSP2RXR" }, + { "DAC2R Mixer", "DSP1 Switch", "DSP1RXR" }, + { "DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" }, + { "DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" }, + + { "DAC1L Mixer", "DSP2 Switch", "DSP2RXL" }, + { "DAC1L Mixer", "DSP1 Switch", "DSP1RXL" }, + { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" }, + { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" }, + + { "DAC1R Mixer", "DSP2 Switch", "DSP2RXR" }, + { "DAC1R Mixer", "DSP1 Switch", "DSP1RXR" }, + { "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" }, + { "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" }, + + { "DAC1L", NULL, "DAC1L Mixer" }, + { "DAC1R", NULL, "DAC1R Mixer" }, + { "DAC2L", NULL, "DAC2L Mixer" }, + { "DAC2R", NULL, "DAC2R Mixer" }, + + { "HPOUT2L PGA", NULL, "Charge Pump" }, + { "HPOUT2L PGA", NULL, "Bandgap" }, + { "HPOUT2L PGA", NULL, "DAC2L" }, + { "HPOUT2L_DLY", NULL, "HPOUT2L PGA" }, + { "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" }, + { "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_DCS" }, + + { "HPOUT2R PGA", NULL, "Charge Pump" }, + { "HPOUT2R PGA", NULL, "Bandgap" }, + { "HPOUT2R PGA", NULL, "DAC2R" }, + { "HPOUT2R_DLY", NULL, "HPOUT2R PGA" }, + { "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" }, + { "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_DCS" }, + + { "HPOUT1L PGA", NULL, "Charge Pump" }, + { "HPOUT1L PGA", NULL, "Bandgap" }, + { "HPOUT1L PGA", NULL, "DAC1L" }, + { "HPOUT1L_DLY", NULL, "HPOUT1L PGA" }, + { "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" }, + { "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_DCS" }, + + { "HPOUT1R PGA", NULL, "Charge Pump" }, + { "HPOUT1R PGA", NULL, "Bandgap" }, + { "HPOUT1R PGA", NULL, "DAC1R" }, + { "HPOUT1R_DLY", NULL, "HPOUT1R PGA" }, + { "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" }, + { "HPOUT1R_RMV_SHORT", NULL, "HPOUT1R_DCS" }, + + { "HPOUT2L", NULL, "HPOUT2L_RMV_SHORT" }, + { "HPOUT2R", NULL, "HPOUT2R_RMV_SHORT" }, + { "HPOUT1L", NULL, "HPOUT1L_RMV_SHORT" }, + { "HPOUT1R", NULL, "HPOUT1R_RMV_SHORT" }, + + { "SPKL", "DAC1L", "DAC1L" }, + { "SPKL", "DAC1R", "DAC1R" }, + { "SPKL", "DAC2L", "DAC2L" }, + { "SPKL", "DAC2R", "DAC2R" }, + + { "SPKR", "DAC1L", "DAC1L" }, + { "SPKR", "DAC1R", "DAC1R" }, + { "SPKR", "DAC2L", "DAC2L" }, + { "SPKR", "DAC2R", "DAC2R" }, + + { "SPKL PGA", NULL, "SPKL" }, + { "SPKR PGA", NULL, "SPKR" }, + + { "SPKDAT", NULL, "SPKL PGA" }, + { "SPKDAT", NULL, "SPKR PGA" }, +}; + +static bool wm8996_readable_register(struct device *dev, unsigned int reg) +{ + /* Due to the sparseness of the register map the compiler + * output from an explicit switch statement ends up being much + * more efficient than a table. + */ + switch (reg) { + case WM8996_SOFTWARE_RESET: + case WM8996_POWER_MANAGEMENT_1: + case WM8996_POWER_MANAGEMENT_2: + case WM8996_POWER_MANAGEMENT_3: + case WM8996_POWER_MANAGEMENT_4: + case WM8996_POWER_MANAGEMENT_5: + case WM8996_POWER_MANAGEMENT_6: + case WM8996_POWER_MANAGEMENT_7: + case WM8996_POWER_MANAGEMENT_8: + case WM8996_LEFT_LINE_INPUT_VOLUME: + case WM8996_RIGHT_LINE_INPUT_VOLUME: + case WM8996_LINE_INPUT_CONTROL: + case WM8996_DAC1_HPOUT1_VOLUME: + case WM8996_DAC2_HPOUT2_VOLUME: + case WM8996_DAC1_LEFT_VOLUME: + case WM8996_DAC1_RIGHT_VOLUME: + case WM8996_DAC2_LEFT_VOLUME: + case WM8996_DAC2_RIGHT_VOLUME: + case WM8996_OUTPUT1_LEFT_VOLUME: + case WM8996_OUTPUT1_RIGHT_VOLUME: + case WM8996_OUTPUT2_LEFT_VOLUME: + case WM8996_OUTPUT2_RIGHT_VOLUME: + case WM8996_MICBIAS_1: + case WM8996_MICBIAS_2: + case WM8996_LDO_1: + case WM8996_LDO_2: + case WM8996_ACCESSORY_DETECT_MODE_1: + case WM8996_ACCESSORY_DETECT_MODE_2: + case WM8996_HEADPHONE_DETECT_1: + case WM8996_HEADPHONE_DETECT_2: + case WM8996_MIC_DETECT_1: + case WM8996_MIC_DETECT_2: + case WM8996_MIC_DETECT_3: + case WM8996_CHARGE_PUMP_1: + case WM8996_CHARGE_PUMP_2: + case WM8996_DC_SERVO_1: + case WM8996_DC_SERVO_2: + case WM8996_DC_SERVO_3: + case WM8996_DC_SERVO_5: + case WM8996_DC_SERVO_6: + case WM8996_DC_SERVO_7: + case WM8996_DC_SERVO_READBACK_0: + case WM8996_ANALOGUE_HP_1: + case WM8996_ANALOGUE_HP_2: + case WM8996_CHIP_REVISION: + case WM8996_CONTROL_INTERFACE_1: + case WM8996_WRITE_SEQUENCER_CTRL_1: + case WM8996_WRITE_SEQUENCER_CTRL_2: + case WM8996_AIF_CLOCKING_1: + case WM8996_AIF_CLOCKING_2: + case WM8996_CLOCKING_1: + case WM8996_CLOCKING_2: + case WM8996_AIF_RATE: + case WM8996_FLL_CONTROL_1: + case WM8996_FLL_CONTROL_2: + case WM8996_FLL_CONTROL_3: + case WM8996_FLL_CONTROL_4: + case WM8996_FLL_CONTROL_5: + case WM8996_FLL_CONTROL_6: + case WM8996_FLL_EFS_1: + case WM8996_FLL_EFS_2: + case WM8996_AIF1_CONTROL: + case WM8996_AIF1_BCLK: + case WM8996_AIF1_TX_LRCLK_1: + case WM8996_AIF1_TX_LRCLK_2: + case WM8996_AIF1_RX_LRCLK_1: + case WM8996_AIF1_RX_LRCLK_2: + case WM8996_AIF1TX_DATA_CONFIGURATION_1: + case WM8996_AIF1TX_DATA_CONFIGURATION_2: + case WM8996_AIF1RX_DATA_CONFIGURATION: + case WM8996_AIF1TX_CHANNEL_0_CONFIGURATION: + case WM8996_AIF1TX_CHANNEL_1_CONFIGURATION: + case WM8996_AIF1TX_CHANNEL_2_CONFIGURATION: + case WM8996_AIF1TX_CHANNEL_3_CONFIGURATION: + case WM8996_AIF1TX_CHANNEL_4_CONFIGURATION: + case WM8996_AIF1TX_CHANNEL_5_CONFIGURATION: + case WM8996_AIF1RX_CHANNEL_0_CONFIGURATION: + case WM8996_AIF1RX_CHANNEL_1_CONFIGURATION: + case WM8996_AIF1RX_CHANNEL_2_CONFIGURATION: + case WM8996_AIF1RX_CHANNEL_3_CONFIGURATION: + case WM8996_AIF1RX_CHANNEL_4_CONFIGURATION: + case WM8996_AIF1RX_CHANNEL_5_CONFIGURATION: + case WM8996_AIF1RX_MONO_CONFIGURATION: + case WM8996_AIF1TX_TEST: + case WM8996_AIF2_CONTROL: + case WM8996_AIF2_BCLK: + case WM8996_AIF2_TX_LRCLK_1: + case WM8996_AIF2_TX_LRCLK_2: + case WM8996_AIF2_RX_LRCLK_1: + case WM8996_AIF2_RX_LRCLK_2: + case WM8996_AIF2TX_DATA_CONFIGURATION_1: + case WM8996_AIF2TX_DATA_CONFIGURATION_2: + case WM8996_AIF2RX_DATA_CONFIGURATION: + case WM8996_AIF2TX_CHANNEL_0_CONFIGURATION: + case WM8996_AIF2TX_CHANNEL_1_CONFIGURATION: + case WM8996_AIF2RX_CHANNEL_0_CONFIGURATION: + case WM8996_AIF2RX_CHANNEL_1_CONFIGURATION: + case WM8996_AIF2RX_MONO_CONFIGURATION: + case WM8996_AIF2TX_TEST: + case WM8996_DSP1_TX_LEFT_VOLUME: + case WM8996_DSP1_TX_RIGHT_VOLUME: + case WM8996_DSP1_RX_LEFT_VOLUME: + case WM8996_DSP1_RX_RIGHT_VOLUME: + case WM8996_DSP1_TX_FILTERS: + case WM8996_DSP1_RX_FILTERS_1: + case WM8996_DSP1_RX_FILTERS_2: + case WM8996_DSP1_DRC_1: + case WM8996_DSP1_DRC_2: + case WM8996_DSP1_DRC_3: + case WM8996_DSP1_DRC_4: + case WM8996_DSP1_DRC_5: + case WM8996_DSP1_RX_EQ_GAINS_1: + case WM8996_DSP1_RX_EQ_GAINS_2: + case WM8996_DSP1_RX_EQ_BAND_1_A: + case WM8996_DSP1_RX_EQ_BAND_1_B: + case WM8996_DSP1_RX_EQ_BAND_1_PG: + case WM8996_DSP1_RX_EQ_BAND_2_A: + case WM8996_DSP1_RX_EQ_BAND_2_B: + case WM8996_DSP1_RX_EQ_BAND_2_C: + case WM8996_DSP1_RX_EQ_BAND_2_PG: + case WM8996_DSP1_RX_EQ_BAND_3_A: + case WM8996_DSP1_RX_EQ_BAND_3_B: + case WM8996_DSP1_RX_EQ_BAND_3_C: + case WM8996_DSP1_RX_EQ_BAND_3_PG: + case WM8996_DSP1_RX_EQ_BAND_4_A: + case WM8996_DSP1_RX_EQ_BAND_4_B: + case WM8996_DSP1_RX_EQ_BAND_4_C: + case WM8996_DSP1_RX_EQ_BAND_4_PG: + case WM8996_DSP1_RX_EQ_BAND_5_A: + case WM8996_DSP1_RX_EQ_BAND_5_B: + case WM8996_DSP1_RX_EQ_BAND_5_PG: + case WM8996_DSP2_TX_LEFT_VOLUME: + case WM8996_DSP2_TX_RIGHT_VOLUME: + case WM8996_DSP2_RX_LEFT_VOLUME: + case WM8996_DSP2_RX_RIGHT_VOLUME: + case WM8996_DSP2_TX_FILTERS: + case WM8996_DSP2_RX_FILTERS_1: + case WM8996_DSP2_RX_FILTERS_2: + case WM8996_DSP2_DRC_1: + case WM8996_DSP2_DRC_2: + case WM8996_DSP2_DRC_3: + case WM8996_DSP2_DRC_4: + case WM8996_DSP2_DRC_5: + case WM8996_DSP2_RX_EQ_GAINS_1: + case WM8996_DSP2_RX_EQ_GAINS_2: + case WM8996_DSP2_RX_EQ_BAND_1_A: + case WM8996_DSP2_RX_EQ_BAND_1_B: + case WM8996_DSP2_RX_EQ_BAND_1_PG: + case WM8996_DSP2_RX_EQ_BAND_2_A: + case WM8996_DSP2_RX_EQ_BAND_2_B: + case WM8996_DSP2_RX_EQ_BAND_2_C: + case WM8996_DSP2_RX_EQ_BAND_2_PG: + case WM8996_DSP2_RX_EQ_BAND_3_A: + case WM8996_DSP2_RX_EQ_BAND_3_B: + case WM8996_DSP2_RX_EQ_BAND_3_C: + case WM8996_DSP2_RX_EQ_BAND_3_PG: + case WM8996_DSP2_RX_EQ_BAND_4_A: + case WM8996_DSP2_RX_EQ_BAND_4_B: + case WM8996_DSP2_RX_EQ_BAND_4_C: + case WM8996_DSP2_RX_EQ_BAND_4_PG: + case WM8996_DSP2_RX_EQ_BAND_5_A: + case WM8996_DSP2_RX_EQ_BAND_5_B: + case WM8996_DSP2_RX_EQ_BAND_5_PG: + case WM8996_DAC1_MIXER_VOLUMES: + case WM8996_DAC1_LEFT_MIXER_ROUTING: + case WM8996_DAC1_RIGHT_MIXER_ROUTING: + case WM8996_DAC2_MIXER_VOLUMES: + case WM8996_DAC2_LEFT_MIXER_ROUTING: + case WM8996_DAC2_RIGHT_MIXER_ROUTING: + case WM8996_DSP1_TX_LEFT_MIXER_ROUTING: + case WM8996_DSP1_TX_RIGHT_MIXER_ROUTING: + case WM8996_DSP2_TX_LEFT_MIXER_ROUTING: + case WM8996_DSP2_TX_RIGHT_MIXER_ROUTING: + case WM8996_DSP_TX_MIXER_SELECT: + case WM8996_DAC_SOFTMUTE: + case WM8996_OVERSAMPLING: + case WM8996_SIDETONE: + case WM8996_GPIO_1: + case WM8996_GPIO_2: + case WM8996_GPIO_3: + case WM8996_GPIO_4: + case WM8996_GPIO_5: + case WM8996_PULL_CONTROL_1: + case WM8996_PULL_CONTROL_2: + case WM8996_INTERRUPT_STATUS_1: + case WM8996_INTERRUPT_STATUS_2: + case WM8996_INTERRUPT_RAW_STATUS_2: + case WM8996_INTERRUPT_STATUS_1_MASK: + case WM8996_INTERRUPT_STATUS_2_MASK: + case WM8996_INTERRUPT_CONTROL: + case WM8996_LEFT_PDM_SPEAKER: + case WM8996_RIGHT_PDM_SPEAKER: + case WM8996_PDM_SPEAKER_MUTE_SEQUENCE: + case WM8996_PDM_SPEAKER_VOLUME: + return true; + default: + return false; + } +} + +static bool wm8996_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8996_SOFTWARE_RESET: + case WM8996_CHIP_REVISION: + case WM8996_LDO_1: + case WM8996_LDO_2: + case WM8996_INTERRUPT_STATUS_1: + case WM8996_INTERRUPT_STATUS_2: + case WM8996_INTERRUPT_RAW_STATUS_2: + case WM8996_DC_SERVO_READBACK_0: + case WM8996_DC_SERVO_2: + case WM8996_DC_SERVO_6: + case WM8996_DC_SERVO_7: + case WM8996_FLL_CONTROL_6: + case WM8996_MIC_DETECT_3: + case WM8996_HEADPHONE_DETECT_1: + case WM8996_HEADPHONE_DETECT_2: + return true; + default: + return false; + } +} + +static const int bclk_divs[] = { + 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96 +}; + +static void wm8996_update_bclk(struct snd_soc_component *component) +{ + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + int aif, best, cur_val, bclk_rate, bclk_reg, i; + + /* Don't bother if we're in a low frequency idle mode that + * can't support audio. + */ + if (wm8996->sysclk < 64000) + return; + + for (aif = 0; aif < WM8996_AIFS; aif++) { + switch (aif) { + case 0: + bclk_reg = WM8996_AIF1_BCLK; + break; + case 1: + bclk_reg = WM8996_AIF2_BCLK; + break; + } + + bclk_rate = wm8996->bclk_rate[aif]; + + /* Pick a divisor for BCLK as close as we can get to ideal */ + best = 0; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = (wm8996->sysclk / bclk_divs[i]) - bclk_rate; + if (cur_val < 0) /* BCLK table is sorted */ + break; + best = i; + } + bclk_rate = wm8996->sysclk / bclk_divs[best]; + dev_dbg(component->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n", + bclk_divs[best], bclk_rate); + + snd_soc_component_update_bits(component, bclk_reg, + WM8996_AIF1_BCLK_DIV_MASK, best); + } +} + +static int wm8996_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + /* Put the MICBIASes into regulating mode */ + snd_soc_component_update_bits(component, WM8996_MICBIAS_1, + WM8996_MICB1_MODE, 0); + snd_soc_component_update_bits(component, WM8996_MICBIAS_2, + WM8996_MICB2_MODE, 0); + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies), + wm8996->supplies); + if (ret != 0) { + dev_err(component->dev, + "Failed to enable supplies: %d\n", + ret); + return ret; + } + + if (wm8996->pdata.ldo_ena >= 0) { + gpio_set_value_cansleep(wm8996->pdata.ldo_ena, + 1); + msleep(5); + } + + regcache_cache_only(wm8996->regmap, false); + regcache_sync(wm8996->regmap); + } + + /* Bypass the MICBIASes for lowest power */ + snd_soc_component_update_bits(component, WM8996_MICBIAS_1, + WM8996_MICB1_MODE, WM8996_MICB1_MODE); + snd_soc_component_update_bits(component, WM8996_MICBIAS_2, + WM8996_MICB2_MODE, WM8996_MICB2_MODE); + break; + + case SND_SOC_BIAS_OFF: + regcache_cache_only(wm8996->regmap, true); + if (wm8996->pdata.ldo_ena >= 0) { + gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); + regcache_cache_only(wm8996->regmap, true); + } + regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), + wm8996->supplies); + break; + } + + return 0; +} + +static int wm8996_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + int aifctrl = 0; + int bclk = 0; + int lrclk_tx = 0; + int lrclk_rx = 0; + int aifctrl_reg, bclk_reg, lrclk_tx_reg, lrclk_rx_reg; + + switch (dai->id) { + case 0: + aifctrl_reg = WM8996_AIF1_CONTROL; + bclk_reg = WM8996_AIF1_BCLK; + lrclk_tx_reg = WM8996_AIF1_TX_LRCLK_2; + lrclk_rx_reg = WM8996_AIF1_RX_LRCLK_2; + break; + case 1: + aifctrl_reg = WM8996_AIF2_CONTROL; + bclk_reg = WM8996_AIF2_BCLK; + lrclk_tx_reg = WM8996_AIF2_TX_LRCLK_2; + lrclk_rx_reg = WM8996_AIF2_RX_LRCLK_2; + break; + default: + WARN(1, "Invalid dai id %d\n", dai->id); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + bclk |= WM8996_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + lrclk_tx |= WM8996_AIF1TX_LRCLK_INV; + lrclk_rx |= WM8996_AIF1RX_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + bclk |= WM8996_AIF1_BCLK_INV; + lrclk_tx |= WM8996_AIF1TX_LRCLK_INV; + lrclk_rx |= WM8996_AIF1RX_LRCLK_INV; + break; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBS_CFM: + lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR; + lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + bclk |= WM8996_AIF1_BCLK_MSTR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + bclk |= WM8996_AIF1_BCLK_MSTR; + lrclk_tx |= WM8996_AIF1TX_LRCLK_MSTR; + lrclk_rx |= WM8996_AIF1RX_LRCLK_MSTR; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + break; + case SND_SOC_DAIFMT_DSP_B: + aifctrl |= 1; + break; + case SND_SOC_DAIFMT_I2S: + aifctrl |= 2; + break; + case SND_SOC_DAIFMT_LEFT_J: + aifctrl |= 3; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, aifctrl_reg, WM8996_AIF1_FMT_MASK, aifctrl); + snd_soc_component_update_bits(component, bclk_reg, + WM8996_AIF1_BCLK_INV | WM8996_AIF1_BCLK_MSTR, + bclk); + snd_soc_component_update_bits(component, lrclk_tx_reg, + WM8996_AIF1TX_LRCLK_INV | + WM8996_AIF1TX_LRCLK_MSTR, + lrclk_tx); + snd_soc_component_update_bits(component, lrclk_rx_reg, + WM8996_AIF1RX_LRCLK_INV | + WM8996_AIF1RX_LRCLK_MSTR, + lrclk_rx); + + return 0; +} + +static const int dsp_divs[] = { + 48000, 32000, 16000, 8000 +}; + +static int wm8996_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + int bits, i, bclk_rate, best; + int aifdata = 0; + int lrclk = 0; + int dsp = 0; + int aifdata_reg, lrclk_reg, dsp_shift; + + switch (dai->id) { + case 0: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || + (snd_soc_component_read(component, WM8996_GPIO_1)) & WM8996_GP1_FN_MASK) { + aifdata_reg = WM8996_AIF1RX_DATA_CONFIGURATION; + lrclk_reg = WM8996_AIF1_RX_LRCLK_1; + } else { + aifdata_reg = WM8996_AIF1TX_DATA_CONFIGURATION_1; + lrclk_reg = WM8996_AIF1_TX_LRCLK_1; + } + dsp_shift = 0; + break; + case 1: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || + (snd_soc_component_read(component, WM8996_GPIO_2)) & WM8996_GP2_FN_MASK) { + aifdata_reg = WM8996_AIF2RX_DATA_CONFIGURATION; + lrclk_reg = WM8996_AIF2_RX_LRCLK_1; + } else { + aifdata_reg = WM8996_AIF2TX_DATA_CONFIGURATION_1; + lrclk_reg = WM8996_AIF2_TX_LRCLK_1; + } + dsp_shift = WM8996_DSP2_DIV_SHIFT; + break; + default: + WARN(1, "Invalid dai id %d\n", dai->id); + return -EINVAL; + } + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) { + dev_err(component->dev, "Unsupported BCLK rate: %d\n", bclk_rate); + return bclk_rate; + } + + wm8996->bclk_rate[dai->id] = bclk_rate; + wm8996->rx_rate[dai->id] = params_rate(params); + + /* Needs looking at for TDM */ + bits = params_width(params); + if (bits < 0) + return bits; + aifdata |= (bits << WM8996_AIF1TX_WL_SHIFT) | bits; + + best = 0; + for (i = 0; i < ARRAY_SIZE(dsp_divs); i++) { + if (abs(dsp_divs[i] - params_rate(params)) < + abs(dsp_divs[best] - params_rate(params))) + best = i; + } + dsp |= i << dsp_shift; + + wm8996_update_bclk(component); + + lrclk = bclk_rate / params_rate(params); + dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n", + lrclk, bclk_rate / lrclk); + + snd_soc_component_update_bits(component, aifdata_reg, + WM8996_AIF1TX_WL_MASK | + WM8996_AIF1TX_SLOT_LEN_MASK, + aifdata); + snd_soc_component_update_bits(component, lrclk_reg, WM8996_AIF1RX_RATE_MASK, + lrclk); + snd_soc_component_update_bits(component, WM8996_AIF_CLOCKING_2, + WM8996_DSP1_DIV_MASK << dsp_shift, dsp); + + return 0; +} + +static int wm8996_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + int lfclk = 0; + int ratediv = 0; + int sync = WM8996_REG_SYNC; + int src; + int old; + + if (freq == wm8996->sysclk && clk_id == wm8996->sysclk_src) + return 0; + + /* Disable SYSCLK while we reconfigure */ + old = snd_soc_component_read(component, WM8996_AIF_CLOCKING_1) & WM8996_SYSCLK_ENA; + snd_soc_component_update_bits(component, WM8996_AIF_CLOCKING_1, + WM8996_SYSCLK_ENA, 0); + + switch (clk_id) { + case WM8996_SYSCLK_MCLK1: + wm8996->sysclk = freq; + src = 0; + break; + case WM8996_SYSCLK_MCLK2: + wm8996->sysclk = freq; + src = 1; + break; + case WM8996_SYSCLK_FLL: + wm8996->sysclk = freq; + src = 2; + break; + default: + dev_err(component->dev, "Unsupported clock source %d\n", clk_id); + return -EINVAL; + } + + switch (wm8996->sysclk) { + case 5644800: + case 6144000: + snd_soc_component_update_bits(component, WM8996_AIF_RATE, + WM8996_SYSCLK_RATE, 0); + break; + case 22579200: + case 24576000: + ratediv = WM8996_SYSCLK_DIV; + wm8996->sysclk /= 2; + fallthrough; + case 11289600: + case 12288000: + snd_soc_component_update_bits(component, WM8996_AIF_RATE, + WM8996_SYSCLK_RATE, WM8996_SYSCLK_RATE); + break; + case 32000: + case 32768: + lfclk = WM8996_LFCLK_ENA; + sync = 0; + break; + default: + dev_warn(component->dev, "Unsupported clock rate %dHz\n", + wm8996->sysclk); + return -EINVAL; + } + + wm8996_update_bclk(component); + + snd_soc_component_update_bits(component, WM8996_AIF_CLOCKING_1, + WM8996_SYSCLK_SRC_MASK | WM8996_SYSCLK_DIV_MASK, + src << WM8996_SYSCLK_SRC_SHIFT | ratediv); + snd_soc_component_update_bits(component, WM8996_CLOCKING_1, WM8996_LFCLK_ENA, lfclk); + snd_soc_component_update_bits(component, WM8996_CONTROL_INTERFACE_1, + WM8996_REG_SYNC, sync); + snd_soc_component_update_bits(component, WM8996_AIF_CLOCKING_1, + WM8996_SYSCLK_ENA, old); + + wm8996->sysclk_src = clk_id; + + return 0; +} + +struct _fll_div { + u16 fll_fratio; + u16 fll_outdiv; + u16 fll_refclk_div; + u16 fll_loop_gain; + u16 fll_ref_freq; + u16 n; + u16 theta; + u16 lambda; +}; + +static struct { + unsigned int min; + unsigned int max; + u16 fll_fratio; + int ratio; +} fll_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, + unsigned int Fout) +{ + unsigned int target; + unsigned int div; + unsigned int fratio, gcd_fll; + int i; + + /* Fref must be <=13.5MHz */ + div = 1; + fll_div->fll_refclk_div = 0; + while ((Fref / div) > 13500000) { + div *= 2; + fll_div->fll_refclk_div++; + + if (div > 8) { + pr_err("Can't scale %dMHz input down to <=13.5MHz\n", + Fref); + return -EINVAL; + } + } + + pr_debug("FLL Fref=%u Fout=%u\n", Fref, Fout); + + /* Apply the division for our remaining calculations */ + Fref /= div; + + if (Fref >= 3000000) + fll_div->fll_loop_gain = 5; + else + fll_div->fll_loop_gain = 0; + + if (Fref >= 48000) + fll_div->fll_ref_freq = 0; + else + fll_div->fll_ref_freq = 1; + + /* Fvco should be 90-100MHz; don't check the upper bound */ + div = 2; + while (Fout * div < 90000000) { + div++; + if (div > 64) { + pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + } + target = Fout * div; + fll_div->fll_outdiv = div - 1; + + pr_debug("FLL Fvco=%dHz\n", target); + + /* Find an appropraite FLL_FRATIO and factor it out of the target */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + fll_div->fll_fratio = fll_fratios[i].fll_fratio; + fratio = fll_fratios[i].ratio; + break; + } + } + if (i == ARRAY_SIZE(fll_fratios)) { + pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref); + return -EINVAL; + } + + fll_div->n = target / (fratio * Fref); + + if (target % Fref == 0) { + fll_div->theta = 0; + fll_div->lambda = 0; + } else { + gcd_fll = gcd(target, fratio * Fref); + + fll_div->theta = (target - (fll_div->n * fratio * Fref)) + / gcd_fll; + fll_div->lambda = (fratio * Fref) / gcd_fll; + } + + pr_debug("FLL N=%x THETA=%x LAMBDA=%x\n", + fll_div->n, fll_div->theta, fll_div->lambda); + pr_debug("FLL_FRATIO=%x FLL_OUTDIV=%x FLL_REFCLK_DIV=%x\n", + fll_div->fll_fratio, fll_div->fll_outdiv, + fll_div->fll_refclk_div); + + return 0; +} + +static int wm8996_set_fll(struct snd_soc_component *component, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + struct i2c_client *i2c = to_i2c_client(component->dev); + struct _fll_div fll_div; + unsigned long timeout, time_left; + int ret, reg, retry; + + /* Any change? */ + if (source == wm8996->fll_src && Fref == wm8996->fll_fref && + Fout == wm8996->fll_fout) + return 0; + + if (Fout == 0) { + dev_dbg(component->dev, "FLL disabled\n"); + + wm8996->fll_fref = 0; + wm8996->fll_fout = 0; + + snd_soc_component_update_bits(component, WM8996_FLL_CONTROL_1, + WM8996_FLL_ENA, 0); + + wm8996_bg_disable(component); + + return 0; + } + + ret = fll_factors(&fll_div, Fref, Fout); + if (ret != 0) + return ret; + + switch (source) { + case WM8996_FLL_MCLK1: + reg = 0; + break; + case WM8996_FLL_MCLK2: + reg = 1; + break; + case WM8996_FLL_DACLRCLK1: + reg = 2; + break; + case WM8996_FLL_BCLK1: + reg = 3; + break; + default: + dev_err(component->dev, "Unknown FLL source %d\n", ret); + return -EINVAL; + } + + reg |= fll_div.fll_refclk_div << WM8996_FLL_REFCLK_DIV_SHIFT; + reg |= fll_div.fll_ref_freq << WM8996_FLL_REF_FREQ_SHIFT; + + snd_soc_component_update_bits(component, WM8996_FLL_CONTROL_5, + WM8996_FLL_REFCLK_DIV_MASK | WM8996_FLL_REF_FREQ | + WM8996_FLL_REFCLK_SRC_MASK, reg); + + reg = 0; + if (fll_div.theta || fll_div.lambda) + reg |= WM8996_FLL_EFS_ENA | (3 << WM8996_FLL_LFSR_SEL_SHIFT); + else + reg |= 1 << WM8996_FLL_LFSR_SEL_SHIFT; + snd_soc_component_write(component, WM8996_FLL_EFS_2, reg); + + snd_soc_component_update_bits(component, WM8996_FLL_CONTROL_2, + WM8996_FLL_OUTDIV_MASK | + WM8996_FLL_FRATIO_MASK, + (fll_div.fll_outdiv << WM8996_FLL_OUTDIV_SHIFT) | + (fll_div.fll_fratio)); + + snd_soc_component_write(component, WM8996_FLL_CONTROL_3, fll_div.theta); + + snd_soc_component_update_bits(component, WM8996_FLL_CONTROL_4, + WM8996_FLL_N_MASK | WM8996_FLL_LOOP_GAIN_MASK, + (fll_div.n << WM8996_FLL_N_SHIFT) | + fll_div.fll_loop_gain); + + snd_soc_component_write(component, WM8996_FLL_EFS_1, fll_div.lambda); + + /* Enable the bandgap if it's not already enabled */ + ret = snd_soc_component_read(component, WM8996_FLL_CONTROL_1); + if (!(ret & WM8996_FLL_ENA)) + wm8996_bg_enable(component); + + /* Clear any pending completions (eg, from failed startups) */ + try_wait_for_completion(&wm8996->fll_lock); + + snd_soc_component_update_bits(component, WM8996_FLL_CONTROL_1, + WM8996_FLL_ENA, WM8996_FLL_ENA); + + /* The FLL supports live reconfiguration - kick that in case we were + * already enabled. + */ + snd_soc_component_write(component, WM8996_FLL_CONTROL_6, WM8996_FLL_SWITCH_CLK); + + /* Wait for the FLL to lock, using the interrupt if possible */ + if (Fref > 1000000) + timeout = usecs_to_jiffies(300); + else + timeout = msecs_to_jiffies(2); + + /* Allow substantially longer if we've actually got the IRQ, poll + * at a slightly higher rate if we don't. + */ + if (i2c->irq) + timeout *= 10; + else + /* ensure timeout of atleast 1 jiffies */ + timeout = (timeout/2) ? : 1; + + for (retry = 0; retry < 10; retry++) { + time_left = wait_for_completion_timeout(&wm8996->fll_lock, + timeout); + if (time_left != 0) { + WARN_ON(!i2c->irq); + ret = 1; + break; + } + + ret = snd_soc_component_read(component, WM8996_INTERRUPT_RAW_STATUS_2); + if (ret & WM8996_FLL_LOCK_STS) + break; + } + if (retry == 10) { + dev_err(component->dev, "Timed out waiting for FLL\n"); + ret = -ETIMEDOUT; + } + + dev_dbg(component->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout); + + wm8996->fll_fref = Fref; + wm8996->fll_fout = Fout; + wm8996->fll_src = source; + + return ret; +} + +#ifdef CONFIG_GPIOLIB +static void wm8996_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm8996_priv *wm8996 = gpiochip_get_data(chip); + + regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset, + WM8996_GP1_LVL, !!value << WM8996_GP1_LVL_SHIFT); +} + +static int wm8996_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm8996_priv *wm8996 = gpiochip_get_data(chip); + int val; + + val = (1 << WM8996_GP1_FN_SHIFT) | (!!value << WM8996_GP1_LVL_SHIFT); + + return regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset, + WM8996_GP1_FN_MASK | WM8996_GP1_DIR | + WM8996_GP1_LVL, val); +} + +static int wm8996_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm8996_priv *wm8996 = gpiochip_get_data(chip); + unsigned int reg; + int ret; + + ret = regmap_read(wm8996->regmap, WM8996_GPIO_1 + offset, ®); + if (ret < 0) + return ret; + + return (reg & WM8996_GP1_LVL) != 0; +} + +static int wm8996_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm8996_priv *wm8996 = gpiochip_get_data(chip); + + return regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset, + WM8996_GP1_FN_MASK | WM8996_GP1_DIR, + (1 << WM8996_GP1_FN_SHIFT) | + (1 << WM8996_GP1_DIR_SHIFT)); +} + +static const struct gpio_chip wm8996_template_chip = { + .label = "wm8996", + .owner = THIS_MODULE, + .direction_output = wm8996_gpio_direction_out, + .set = wm8996_gpio_set, + .direction_input = wm8996_gpio_direction_in, + .get = wm8996_gpio_get, + .can_sleep = 1, +}; + +static void wm8996_init_gpio(struct wm8996_priv *wm8996) +{ + int ret; + + wm8996->gpio_chip = wm8996_template_chip; + wm8996->gpio_chip.ngpio = 5; + wm8996->gpio_chip.parent = wm8996->dev; + + if (wm8996->pdata.gpio_base) + wm8996->gpio_chip.base = wm8996->pdata.gpio_base; + else + wm8996->gpio_chip.base = -1; + + ret = gpiochip_add_data(&wm8996->gpio_chip, wm8996); + if (ret != 0) + dev_err(wm8996->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void wm8996_free_gpio(struct wm8996_priv *wm8996) +{ + gpiochip_remove(&wm8996->gpio_chip); +} +#else +static void wm8996_init_gpio(struct wm8996_priv *wm8996) +{ +} + +static void wm8996_free_gpio(struct wm8996_priv *wm8996) +{ +} +#endif + +/** + * wm8996_detect - Enable default WM8996 jack detection + * @component: ASoC component + * @jack: jack pointer + * @polarity_cb: polarity callback + * + * The WM8996 has advanced accessory detection support for headsets. + * This function provides a default implementation which integrates + * the majority of this functionality with minimal user configuration. + * + * This will detect headset, headphone and short circuit button and + * will also detect inverted microphone ground connections and update + * the polarity of the connections. + */ +int wm8996_detect(struct snd_soc_component *component, struct snd_soc_jack *jack, + wm8996_polarity_fn polarity_cb) +{ + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + wm8996->jack = jack; + wm8996->detecting = true; + wm8996->polarity_cb = polarity_cb; + wm8996->jack_flips = 0; + + if (wm8996->polarity_cb) + wm8996->polarity_cb(component, 0); + + /* Clear discarge to avoid noise during detection */ + snd_soc_component_update_bits(component, WM8996_MICBIAS_1, + WM8996_MICB1_DISCH, 0); + snd_soc_component_update_bits(component, WM8996_MICBIAS_2, + WM8996_MICB2_DISCH, 0); + + /* LDO2 powers the microphones, SYSCLK clocks detection */ + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_force_enable_pin_unlocked(dapm, "LDO2"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "SYSCLK"); + + snd_soc_dapm_mutex_unlock(dapm); + + /* We start off just enabling microphone detection - even a + * plain headphone will trigger detection. + */ + snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1, + WM8996_MICD_ENA, WM8996_MICD_ENA); + + /* Slowest detection rate, gives debounce for initial detection */ + snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1, + WM8996_MICD_RATE_MASK, + WM8996_MICD_RATE_MASK); + + /* Enable interrupts and we're off */ + snd_soc_component_update_bits(component, WM8996_INTERRUPT_STATUS_2_MASK, + WM8996_IM_MICD_EINT | WM8996_HP_DONE_EINT, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(wm8996_detect); + +static void wm8996_hpdet_irq(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + int val, reg, report; + + /* Assume headphone in error conditions; we need to report + * something or we stall our state machine. + */ + report = SND_JACK_HEADPHONE; + + reg = snd_soc_component_read(component, WM8996_HEADPHONE_DETECT_2); + if (reg < 0) { + dev_err(component->dev, "Failed to read HPDET status\n"); + goto out; + } + + if (!(reg & WM8996_HP_DONE)) { + dev_err(component->dev, "Got HPDET IRQ but HPDET is busy\n"); + goto out; + } + + val = reg & WM8996_HP_LVL_MASK; + + dev_dbg(component->dev, "HPDET measured %d ohms\n", val); + + /* If we've got high enough impedence then report as line, + * otherwise assume headphone. + */ + if (val >= 126) + report = SND_JACK_LINEOUT; + else + report = SND_JACK_HEADPHONE; + +out: + if (wm8996->jack_mic) + report |= SND_JACK_MICROPHONE; + + snd_soc_jack_report(wm8996->jack, report, + SND_JACK_LINEOUT | SND_JACK_HEADSET); + + wm8996->detecting = false; + + /* If the output isn't running re-clamp it */ + if (!(snd_soc_component_read(component, WM8996_POWER_MANAGEMENT_1) & + (WM8996_HPOUT1L_ENA | WM8996_HPOUT1R_RMV_SHORT))) + snd_soc_component_update_bits(component, WM8996_ANALOGUE_HP_1, + WM8996_HPOUT1L_RMV_SHORT | + WM8996_HPOUT1R_RMV_SHORT, 0); + + /* Go back to looking at the microphone */ + snd_soc_component_update_bits(component, WM8996_ACCESSORY_DETECT_MODE_1, + WM8996_JD_MODE_MASK, 0); + snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, + WM8996_MICD_ENA); + + snd_soc_dapm_disable_pin(dapm, "Bandgap"); + snd_soc_dapm_sync(dapm); +} + +static void wm8996_hpdet_start(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + /* Unclamp the output, we can't measure while we're shorting it */ + snd_soc_component_update_bits(component, WM8996_ANALOGUE_HP_1, + WM8996_HPOUT1L_RMV_SHORT | + WM8996_HPOUT1R_RMV_SHORT, + WM8996_HPOUT1L_RMV_SHORT | + WM8996_HPOUT1R_RMV_SHORT); + + /* We need bandgap for HPDET */ + snd_soc_dapm_force_enable_pin(dapm, "Bandgap"); + snd_soc_dapm_sync(dapm); + + /* Go into headphone detect left mode */ + snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0); + snd_soc_component_update_bits(component, WM8996_ACCESSORY_DETECT_MODE_1, + WM8996_JD_MODE_MASK, 1); + + /* Trigger a measurement */ + snd_soc_component_update_bits(component, WM8996_HEADPHONE_DETECT_1, + WM8996_HP_POLL, WM8996_HP_POLL); +} + +static void wm8996_report_headphone(struct snd_soc_component *component) +{ + dev_dbg(component->dev, "Headphone detected\n"); + wm8996_hpdet_start(component); + + /* Increase the detection rate a bit for responsiveness. */ + snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1, + WM8996_MICD_RATE_MASK | + WM8996_MICD_BIAS_STARTTIME_MASK, + 7 << WM8996_MICD_RATE_SHIFT | + 7 << WM8996_MICD_BIAS_STARTTIME_SHIFT); +} + +static void wm8996_micd(struct snd_soc_component *component) +{ + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + int val, reg; + + val = snd_soc_component_read(component, WM8996_MIC_DETECT_3); + + dev_dbg(component->dev, "Microphone event: %x\n", val); + + if (!(val & WM8996_MICD_VALID)) { + dev_warn(component->dev, "Microphone detection state invalid\n"); + return; + } + + /* No accessory, reset everything and report removal */ + if (!(val & WM8996_MICD_STS)) { + dev_dbg(component->dev, "Jack removal detected\n"); + wm8996->jack_mic = false; + wm8996->detecting = true; + wm8996->jack_flips = 0; + snd_soc_jack_report(wm8996->jack, 0, + SND_JACK_LINEOUT | SND_JACK_HEADSET | + SND_JACK_BTN_0); + + snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1, + WM8996_MICD_RATE_MASK | + WM8996_MICD_BIAS_STARTTIME_MASK, + WM8996_MICD_RATE_MASK | + 9 << WM8996_MICD_BIAS_STARTTIME_SHIFT); + return; + } + + /* If the measurement is very high we've got a microphone, + * either we just detected one or if we already reported then + * we've got a button release event. + */ + if (val & 0x400) { + if (wm8996->detecting) { + dev_dbg(component->dev, "Microphone detected\n"); + wm8996->jack_mic = true; + wm8996_hpdet_start(component); + + /* Increase poll rate to give better responsiveness + * for buttons */ + snd_soc_component_update_bits(component, WM8996_MIC_DETECT_1, + WM8996_MICD_RATE_MASK | + WM8996_MICD_BIAS_STARTTIME_MASK, + 5 << WM8996_MICD_RATE_SHIFT | + 7 << WM8996_MICD_BIAS_STARTTIME_SHIFT); + } else { + dev_dbg(component->dev, "Mic button up\n"); + snd_soc_jack_report(wm8996->jack, 0, SND_JACK_BTN_0); + } + + return; + } + + /* If we detected a lower impedence during initial startup + * then we probably have the wrong polarity, flip it. Don't + * do this for the lowest impedences to speed up detection of + * plain headphones. If both polarities report a low + * impedence then give up and report headphones. + */ + if (wm8996->detecting && (val & 0x3f0)) { + wm8996->jack_flips++; + + if (wm8996->jack_flips > 1) { + wm8996_report_headphone(component); + return; + } + + reg = snd_soc_component_read(component, WM8996_ACCESSORY_DETECT_MODE_2); + reg ^= WM8996_HPOUT1FB_SRC | WM8996_MICD_SRC | + WM8996_MICD_BIAS_SRC; + snd_soc_component_update_bits(component, WM8996_ACCESSORY_DETECT_MODE_2, + WM8996_HPOUT1FB_SRC | WM8996_MICD_SRC | + WM8996_MICD_BIAS_SRC, reg); + + if (wm8996->polarity_cb) + wm8996->polarity_cb(component, + (reg & WM8996_MICD_SRC) != 0); + + dev_dbg(component->dev, "Set microphone polarity to %d\n", + (reg & WM8996_MICD_SRC) != 0); + + return; + } + + /* Don't distinguish between buttons, just report any low + * impedence as BTN_0. + */ + if (val & 0x3fc) { + if (wm8996->jack_mic) { + dev_dbg(component->dev, "Mic button detected\n"); + snd_soc_jack_report(wm8996->jack, SND_JACK_BTN_0, + SND_JACK_BTN_0); + } else if (wm8996->detecting) { + wm8996_report_headphone(component); + } + } +} + +static irqreturn_t wm8996_irq(int irq, void *data) +{ + struct snd_soc_component *component = data; + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + int irq_val; + + irq_val = snd_soc_component_read(component, WM8996_INTERRUPT_STATUS_2); + if (irq_val < 0) { + dev_err(component->dev, "Failed to read IRQ status: %d\n", + irq_val); + return IRQ_NONE; + } + irq_val &= ~snd_soc_component_read(component, WM8996_INTERRUPT_STATUS_2_MASK); + + if (!irq_val) + return IRQ_NONE; + + snd_soc_component_write(component, WM8996_INTERRUPT_STATUS_2, irq_val); + + if (irq_val & (WM8996_DCS_DONE_01_EINT | WM8996_DCS_DONE_23_EINT)) { + dev_dbg(component->dev, "DC servo IRQ\n"); + complete(&wm8996->dcs_done); + } + + if (irq_val & WM8996_FIFOS_ERR_EINT) + dev_err(component->dev, "Digital core FIFO error\n"); + + if (irq_val & WM8996_FLL_LOCK_EINT) { + dev_dbg(component->dev, "FLL locked\n"); + complete(&wm8996->fll_lock); + } + + if (irq_val & WM8996_MICD_EINT) + wm8996_micd(component); + + if (irq_val & WM8996_HP_DONE_EINT) + wm8996_hpdet_irq(component); + + return IRQ_HANDLED; +} + +static irqreturn_t wm8996_edge_irq(int irq, void *data) +{ + irqreturn_t ret = IRQ_NONE; + irqreturn_t val; + + do { + val = wm8996_irq(irq, data); + if (val != IRQ_NONE) + ret = val; + } while (val != IRQ_NONE); + + return ret; +} + +static void wm8996_retune_mobile_pdata(struct snd_soc_component *component) +{ + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + struct wm8996_pdata *pdata = &wm8996->pdata; + + struct snd_kcontrol_new controls[] = { + SOC_ENUM_EXT("DSP1 EQ Mode", + wm8996->retune_mobile_enum, + wm8996_get_retune_mobile_enum, + wm8996_put_retune_mobile_enum), + SOC_ENUM_EXT("DSP2 EQ Mode", + wm8996->retune_mobile_enum, + wm8996_get_retune_mobile_enum, + wm8996_put_retune_mobile_enum), + }; + int ret, i, j; + const char **t; + + /* We need an array of texts for the enum API but the number + * of texts is likely to be less than the number of + * configurations due to the sample rate dependency of the + * configurations. */ + wm8996->num_retune_mobile_texts = 0; + wm8996->retune_mobile_texts = NULL; + for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) { + for (j = 0; j < wm8996->num_retune_mobile_texts; j++) { + if (strcmp(pdata->retune_mobile_cfgs[i].name, + wm8996->retune_mobile_texts[j]) == 0) + break; + } + + if (j != wm8996->num_retune_mobile_texts) + continue; + + /* Expand the array... */ + t = krealloc(wm8996->retune_mobile_texts, + sizeof(char *) * + (wm8996->num_retune_mobile_texts + 1), + GFP_KERNEL); + if (t == NULL) + continue; + + /* ...store the new entry... */ + t[wm8996->num_retune_mobile_texts] = + pdata->retune_mobile_cfgs[i].name; + + /* ...and remember the new version. */ + wm8996->num_retune_mobile_texts++; + wm8996->retune_mobile_texts = t; + } + + dev_dbg(component->dev, "Allocated %d unique ReTune Mobile names\n", + wm8996->num_retune_mobile_texts); + + wm8996->retune_mobile_enum.items = wm8996->num_retune_mobile_texts; + wm8996->retune_mobile_enum.texts = wm8996->retune_mobile_texts; + + ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls)); + if (ret != 0) + dev_err(component->dev, + "Failed to add ReTune Mobile controls: %d\n", ret); +} + +static const struct regmap_config wm8996_regmap = { + .reg_bits = 16, + .val_bits = 16, + + .max_register = WM8996_MAX_REGISTER, + .reg_defaults = wm8996_reg, + .num_reg_defaults = ARRAY_SIZE(wm8996_reg), + .volatile_reg = wm8996_volatile_register, + .readable_reg = wm8996_readable_register, + .cache_type = REGCACHE_MAPLE, +}; + +static int wm8996_probe(struct snd_soc_component *component) +{ + int ret; + struct wm8996_priv *wm8996 = snd_soc_component_get_drvdata(component); + struct i2c_client *i2c = to_i2c_client(component->dev); + int irq_flags; + + wm8996->component = component; + + init_completion(&wm8996->dcs_done); + init_completion(&wm8996->fll_lock); + + if (wm8996->pdata.num_retune_mobile_cfgs) + wm8996_retune_mobile_pdata(component); + else + snd_soc_add_component_controls(component, wm8996_eq_controls, + ARRAY_SIZE(wm8996_eq_controls)); + + if (i2c->irq) { + if (wm8996->pdata.irq_flags) + irq_flags = wm8996->pdata.irq_flags; + else + irq_flags = IRQF_TRIGGER_LOW; + + irq_flags |= IRQF_ONESHOT; + + if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) + ret = request_threaded_irq(i2c->irq, NULL, + wm8996_edge_irq, + irq_flags, "wm8996", component); + else + ret = request_threaded_irq(i2c->irq, NULL, wm8996_irq, + irq_flags, "wm8996", component); + + if (ret == 0) { + /* Unmask the interrupt */ + snd_soc_component_update_bits(component, WM8996_INTERRUPT_CONTROL, + WM8996_IM_IRQ, 0); + + /* Enable error reporting and DC servo status */ + snd_soc_component_update_bits(component, + WM8996_INTERRUPT_STATUS_2_MASK, + WM8996_IM_DCS_DONE_23_EINT | + WM8996_IM_DCS_DONE_01_EINT | + WM8996_IM_FLL_LOCK_EINT | + WM8996_IM_FIFOS_ERR_EINT, + 0); + } else { + dev_err(component->dev, "Failed to request IRQ: %d\n", + ret); + return ret; + } + } + + return 0; +} + +static void wm8996_remove(struct snd_soc_component *component) +{ + struct i2c_client *i2c = to_i2c_client(component->dev); + + snd_soc_component_update_bits(component, WM8996_INTERRUPT_CONTROL, + WM8996_IM_IRQ, WM8996_IM_IRQ); + + if (i2c->irq) + free_irq(i2c->irq, component); +} + +static const struct snd_soc_component_driver soc_component_dev_wm8996 = { + .probe = wm8996_probe, + .remove = wm8996_remove, + .set_bias_level = wm8996_set_bias_level, + .seq_notifier = wm8996_seq_notifier, + .controls = wm8996_snd_controls, + .num_controls = ARRAY_SIZE(wm8996_snd_controls), + .dapm_widgets = wm8996_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8996_dapm_widgets), + .dapm_routes = wm8996_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8996_dapm_routes), + .set_pll = wm8996_set_fll, + .use_pmdown_time = 1, + .endianness = 1, +}; + +#define WM8996_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000) +#define WM8996_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm8996_dai_ops = { + .set_fmt = wm8996_set_fmt, + .hw_params = wm8996_hw_params, + .set_sysclk = wm8996_set_sysclk, +}; + +static struct snd_soc_dai_driver wm8996_dai[] = { + { + .name = "wm8996-aif1", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = WM8996_RATES, + .formats = WM8996_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = WM8996_RATES, + .formats = WM8996_FORMATS, + .sig_bits = 24, + }, + .ops = &wm8996_dai_ops, + }, + { + .name = "wm8996-aif2", + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8996_RATES, + .formats = WM8996_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8996_RATES, + .formats = WM8996_FORMATS, + .sig_bits = 24, + }, + .ops = &wm8996_dai_ops, + }, +}; + +static int wm8996_i2c_probe(struct i2c_client *i2c) +{ + struct wm8996_priv *wm8996; + int ret, i; + unsigned int reg; + + wm8996 = devm_kzalloc(&i2c->dev, sizeof(struct wm8996_priv), + GFP_KERNEL); + if (wm8996 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm8996); + wm8996->dev = &i2c->dev; + + if (dev_get_platdata(&i2c->dev)) + memcpy(&wm8996->pdata, dev_get_platdata(&i2c->dev), + sizeof(wm8996->pdata)); + + if (wm8996->pdata.ldo_ena > 0) { + ret = gpio_request_one(wm8996->pdata.ldo_ena, + GPIOF_OUT_INIT_LOW, "WM8996 ENA"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request GPIO %d: %d\n", + wm8996->pdata.ldo_ena, ret); + goto err; + } + } + + for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) + wm8996->supplies[i].supply = wm8996_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8996->supplies), + wm8996->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + goto err_gpio; + } + + wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0; + wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1; + wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2; + + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) { + ret = devm_regulator_register_notifier( + wm8996->supplies[i].consumer, + &wm8996->disable_nb[i]); + if (ret != 0) { + dev_err(&i2c->dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies), + wm8996->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + goto err_gpio; + } + + if (wm8996->pdata.ldo_ena > 0) { + gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 1); + msleep(5); + } + + wm8996->regmap = devm_regmap_init_i2c(i2c, &wm8996_regmap); + if (IS_ERR(wm8996->regmap)) { + ret = PTR_ERR(wm8996->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + goto err_enable; + } + + ret = regmap_read(wm8996->regmap, WM8996_SOFTWARE_RESET, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret); + goto err_regmap; + } + if (reg != 0x8915) { + dev_err(&i2c->dev, "Device is not a WM8996, ID %x\n", reg); + ret = -EINVAL; + goto err_regmap; + } + + ret = regmap_read(wm8996->regmap, WM8996_CHIP_REVISION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read device revision: %d\n", + ret); + goto err_regmap; + } + + dev_info(&i2c->dev, "revision %c\n", + (reg & WM8996_CHIP_REV_MASK) + 'A'); + + if (wm8996->pdata.ldo_ena > 0) { + gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); + regcache_cache_only(wm8996->regmap, true); + } else { + ret = regmap_write(wm8996->regmap, WM8996_SOFTWARE_RESET, + 0x8915); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); + goto err_regmap; + } + } + + regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); + + /* Apply platform data settings */ + regmap_update_bits(wm8996->regmap, WM8996_LINE_INPUT_CONTROL, + WM8996_INL_MODE_MASK | WM8996_INR_MODE_MASK, + wm8996->pdata.inl_mode << WM8996_INL_MODE_SHIFT | + wm8996->pdata.inr_mode); + + for (i = 0; i < ARRAY_SIZE(wm8996->pdata.gpio_default); i++) { + if (!wm8996->pdata.gpio_default[i]) + continue; + + regmap_write(wm8996->regmap, WM8996_GPIO_1 + i, + wm8996->pdata.gpio_default[i] & 0xffff); + } + + if (wm8996->pdata.spkmute_seq) + regmap_update_bits(wm8996->regmap, + WM8996_PDM_SPEAKER_MUTE_SEQUENCE, + WM8996_SPK_MUTE_ENDIAN | + WM8996_SPK_MUTE_SEQ1_MASK, + wm8996->pdata.spkmute_seq); + + regmap_update_bits(wm8996->regmap, WM8996_ACCESSORY_DETECT_MODE_2, + WM8996_MICD_BIAS_SRC | WM8996_HPOUT1FB_SRC | + WM8996_MICD_SRC, wm8996->pdata.micdet_def); + + /* Latch volume update bits */ + regmap_update_bits(wm8996->regmap, WM8996_LEFT_LINE_INPUT_VOLUME, + WM8996_IN1_VU, WM8996_IN1_VU); + regmap_update_bits(wm8996->regmap, WM8996_RIGHT_LINE_INPUT_VOLUME, + WM8996_IN1_VU, WM8996_IN1_VU); + + regmap_update_bits(wm8996->regmap, WM8996_DAC1_LEFT_VOLUME, + WM8996_DAC1_VU, WM8996_DAC1_VU); + regmap_update_bits(wm8996->regmap, WM8996_DAC1_RIGHT_VOLUME, + WM8996_DAC1_VU, WM8996_DAC1_VU); + regmap_update_bits(wm8996->regmap, WM8996_DAC2_LEFT_VOLUME, + WM8996_DAC2_VU, WM8996_DAC2_VU); + regmap_update_bits(wm8996->regmap, WM8996_DAC2_RIGHT_VOLUME, + WM8996_DAC2_VU, WM8996_DAC2_VU); + + regmap_update_bits(wm8996->regmap, WM8996_OUTPUT1_LEFT_VOLUME, + WM8996_DAC1_VU, WM8996_DAC1_VU); + regmap_update_bits(wm8996->regmap, WM8996_OUTPUT1_RIGHT_VOLUME, + WM8996_DAC1_VU, WM8996_DAC1_VU); + regmap_update_bits(wm8996->regmap, WM8996_OUTPUT2_LEFT_VOLUME, + WM8996_DAC2_VU, WM8996_DAC2_VU); + regmap_update_bits(wm8996->regmap, WM8996_OUTPUT2_RIGHT_VOLUME, + WM8996_DAC2_VU, WM8996_DAC2_VU); + + regmap_update_bits(wm8996->regmap, WM8996_DSP1_TX_LEFT_VOLUME, + WM8996_DSP1TX_VU, WM8996_DSP1TX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP1_TX_RIGHT_VOLUME, + WM8996_DSP1TX_VU, WM8996_DSP1TX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP2_TX_LEFT_VOLUME, + WM8996_DSP2TX_VU, WM8996_DSP2TX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP2_TX_RIGHT_VOLUME, + WM8996_DSP2TX_VU, WM8996_DSP2TX_VU); + + regmap_update_bits(wm8996->regmap, WM8996_DSP1_RX_LEFT_VOLUME, + WM8996_DSP1RX_VU, WM8996_DSP1RX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP1_RX_RIGHT_VOLUME, + WM8996_DSP1RX_VU, WM8996_DSP1RX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP2_RX_LEFT_VOLUME, + WM8996_DSP2RX_VU, WM8996_DSP2RX_VU); + regmap_update_bits(wm8996->regmap, WM8996_DSP2_RX_RIGHT_VOLUME, + WM8996_DSP2RX_VU, WM8996_DSP2RX_VU); + + /* No support currently for the underclocked TDM modes and + * pick a default TDM layout with each channel pair working with + * slots 0 and 1. */ + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_0_CONFIGURATION, + WM8996_AIF1RX_CHAN0_SLOTS_MASK | + WM8996_AIF1RX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN0_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_1_CONFIGURATION, + WM8996_AIF1RX_CHAN1_SLOTS_MASK | + WM8996_AIF1RX_CHAN1_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN1_SLOTS_SHIFT | 1); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_2_CONFIGURATION, + WM8996_AIF1RX_CHAN2_SLOTS_MASK | + WM8996_AIF1RX_CHAN2_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN2_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_3_CONFIGURATION, + WM8996_AIF1RX_CHAN3_SLOTS_MASK | + WM8996_AIF1RX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN3_SLOTS_SHIFT | 1); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_4_CONFIGURATION, + WM8996_AIF1RX_CHAN4_SLOTS_MASK | + WM8996_AIF1RX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN4_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1RX_CHANNEL_5_CONFIGURATION, + WM8996_AIF1RX_CHAN5_SLOTS_MASK | + WM8996_AIF1RX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1RX_CHAN5_SLOTS_SHIFT | 1); + + regmap_update_bits(wm8996->regmap, + WM8996_AIF2RX_CHANNEL_0_CONFIGURATION, + WM8996_AIF2RX_CHAN0_SLOTS_MASK | + WM8996_AIF2RX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF2RX_CHAN0_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF2RX_CHANNEL_1_CONFIGURATION, + WM8996_AIF2RX_CHAN1_SLOTS_MASK | + WM8996_AIF2RX_CHAN1_START_SLOT_MASK, + 1 << WM8996_AIF2RX_CHAN1_SLOTS_SHIFT | 1); + + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_0_CONFIGURATION, + WM8996_AIF1TX_CHAN0_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN0_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_1_CONFIGURATION, + WM8996_AIF1TX_CHAN1_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_2_CONFIGURATION, + WM8996_AIF1TX_CHAN2_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN2_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_3_CONFIGURATION, + WM8996_AIF1TX_CHAN3_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN3_SLOTS_SHIFT | 1); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_4_CONFIGURATION, + WM8996_AIF1TX_CHAN4_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN4_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_5_CONFIGURATION, + WM8996_AIF1TX_CHAN5_SLOTS_MASK | + WM8996_AIF1TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN5_SLOTS_SHIFT | 1); + + regmap_update_bits(wm8996->regmap, + WM8996_AIF2TX_CHANNEL_0_CONFIGURATION, + WM8996_AIF2TX_CHAN0_SLOTS_MASK | + WM8996_AIF2TX_CHAN0_START_SLOT_MASK, + 1 << WM8996_AIF2TX_CHAN0_SLOTS_SHIFT | 0); + regmap_update_bits(wm8996->regmap, + WM8996_AIF1TX_CHANNEL_1_CONFIGURATION, + WM8996_AIF2TX_CHAN1_SLOTS_MASK | + WM8996_AIF2TX_CHAN1_START_SLOT_MASK, + 1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1); + + /* If the TX LRCLK pins are not in LRCLK mode configure the + * AIFs to source their clocks from the RX LRCLKs. + */ + ret = regmap_read(wm8996->regmap, WM8996_GPIO_1, ®); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read GPIO1: %d\n", ret); + goto err_regmap; + } + + if (reg & WM8996_GP1_FN_MASK) + regmap_update_bits(wm8996->regmap, WM8996_AIF1_TX_LRCLK_2, + WM8996_AIF1TX_LRCLK_MODE, + WM8996_AIF1TX_LRCLK_MODE); + + ret = regmap_read(wm8996->regmap, WM8996_GPIO_2, ®); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read GPIO2: %d\n", ret); + goto err_regmap; + } + + if (reg & WM8996_GP2_FN_MASK) + regmap_update_bits(wm8996->regmap, WM8996_AIF2_TX_LRCLK_2, + WM8996_AIF2TX_LRCLK_MODE, + WM8996_AIF2TX_LRCLK_MODE); + + wm8996_init_gpio(wm8996); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8996, wm8996_dai, + ARRAY_SIZE(wm8996_dai)); + if (ret < 0) + goto err_gpiolib; + + return ret; + +err_gpiolib: + wm8996_free_gpio(wm8996); +err_regmap: +err_enable: + if (wm8996->pdata.ldo_ena > 0) + gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); + regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies); +err_gpio: + if (wm8996->pdata.ldo_ena > 0) + gpio_free(wm8996->pdata.ldo_ena); +err: + + return ret; +} + +static void wm8996_i2c_remove(struct i2c_client *client) +{ + struct wm8996_priv *wm8996 = i2c_get_clientdata(client); + + wm8996_free_gpio(wm8996); + if (wm8996->pdata.ldo_ena > 0) { + gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0); + gpio_free(wm8996->pdata.ldo_ena); + } +} + +static const struct i2c_device_id wm8996_i2c_id[] = { + { "wm8996", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8996_i2c_id); + +static struct i2c_driver wm8996_i2c_driver = { + .driver = { + .name = "wm8996", + }, + .probe = wm8996_i2c_probe, + .remove = wm8996_i2c_remove, + .id_table = wm8996_i2c_id, +}; + +module_i2c_driver(wm8996_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8996 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8996.h b/sound/soc/codecs/wm8996.h new file mode 100644 index 0000000000..12bbbb1a9d --- /dev/null +++ b/sound/soc/codecs/wm8996.h @@ -0,0 +1,3717 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * wm8996.h - WM8996 audio codec interface + * + * Copyright 2011 Wolfson Microelectronics PLC. + * Author: Mark Brown + */ + +#ifndef _WM8996_H +#define _WM8996_H + +#define WM8996_SYSCLK_MCLK1 1 +#define WM8996_SYSCLK_MCLK2 2 +#define WM8996_SYSCLK_FLL 3 + +#define WM8996_FLL_MCLK1 1 +#define WM8996_FLL_MCLK2 2 +#define WM8996_FLL_DACLRCLK1 3 +#define WM8996_FLL_BCLK1 4 + +typedef void (*wm8996_polarity_fn)(struct snd_soc_component *component, int polarity); + +int wm8996_detect(struct snd_soc_component *component, struct snd_soc_jack *jack, + wm8996_polarity_fn polarity_cb); + +/* + * Register values. + */ +#define WM8996_SOFTWARE_RESET 0x00 +#define WM8996_POWER_MANAGEMENT_1 0x01 +#define WM8996_POWER_MANAGEMENT_2 0x02 +#define WM8996_POWER_MANAGEMENT_3 0x03 +#define WM8996_POWER_MANAGEMENT_4 0x04 +#define WM8996_POWER_MANAGEMENT_5 0x05 +#define WM8996_POWER_MANAGEMENT_6 0x06 +#define WM8996_POWER_MANAGEMENT_7 0x07 +#define WM8996_POWER_MANAGEMENT_8 0x08 +#define WM8996_LEFT_LINE_INPUT_VOLUME 0x10 +#define WM8996_RIGHT_LINE_INPUT_VOLUME 0x11 +#define WM8996_LINE_INPUT_CONTROL 0x12 +#define WM8996_DAC1_HPOUT1_VOLUME 0x15 +#define WM8996_DAC2_HPOUT2_VOLUME 0x16 +#define WM8996_DAC1_LEFT_VOLUME 0x18 +#define WM8996_DAC1_RIGHT_VOLUME 0x19 +#define WM8996_DAC2_LEFT_VOLUME 0x1A +#define WM8996_DAC2_RIGHT_VOLUME 0x1B +#define WM8996_OUTPUT1_LEFT_VOLUME 0x1C +#define WM8996_OUTPUT1_RIGHT_VOLUME 0x1D +#define WM8996_OUTPUT2_LEFT_VOLUME 0x1E +#define WM8996_OUTPUT2_RIGHT_VOLUME 0x1F +#define WM8996_MICBIAS_1 0x20 +#define WM8996_MICBIAS_2 0x21 +#define WM8996_LDO_1 0x28 +#define WM8996_LDO_2 0x29 +#define WM8996_ACCESSORY_DETECT_MODE_1 0x30 +#define WM8996_ACCESSORY_DETECT_MODE_2 0x31 +#define WM8996_HEADPHONE_DETECT_1 0x34 +#define WM8996_HEADPHONE_DETECT_2 0x35 +#define WM8996_MIC_DETECT_1 0x38 +#define WM8996_MIC_DETECT_2 0x39 +#define WM8996_MIC_DETECT_3 0x3A +#define WM8996_CHARGE_PUMP_1 0x40 +#define WM8996_CHARGE_PUMP_2 0x41 +#define WM8996_DC_SERVO_1 0x50 +#define WM8996_DC_SERVO_2 0x51 +#define WM8996_DC_SERVO_3 0x52 +#define WM8996_DC_SERVO_5 0x54 +#define WM8996_DC_SERVO_6 0x55 +#define WM8996_DC_SERVO_7 0x56 +#define WM8996_DC_SERVO_READBACK_0 0x57 +#define WM8996_ANALOGUE_HP_1 0x60 +#define WM8996_ANALOGUE_HP_2 0x61 +#define WM8996_CHIP_REVISION 0x100 +#define WM8996_CONTROL_INTERFACE_1 0x101 +#define WM8996_WRITE_SEQUENCER_CTRL_1 0x110 +#define WM8996_WRITE_SEQUENCER_CTRL_2 0x111 +#define WM8996_AIF_CLOCKING_1 0x200 +#define WM8996_AIF_CLOCKING_2 0x201 +#define WM8996_CLOCKING_1 0x208 +#define WM8996_CLOCKING_2 0x209 +#define WM8996_AIF_RATE 0x210 +#define WM8996_FLL_CONTROL_1 0x220 +#define WM8996_FLL_CONTROL_2 0x221 +#define WM8996_FLL_CONTROL_3 0x222 +#define WM8996_FLL_CONTROL_4 0x223 +#define WM8996_FLL_CONTROL_5 0x224 +#define WM8996_FLL_CONTROL_6 0x225 +#define WM8996_FLL_EFS_1 0x226 +#define WM8996_FLL_EFS_2 0x227 +#define WM8996_AIF1_CONTROL 0x300 +#define WM8996_AIF1_BCLK 0x301 +#define WM8996_AIF1_TX_LRCLK_1 0x302 +#define WM8996_AIF1_TX_LRCLK_2 0x303 +#define WM8996_AIF1_RX_LRCLK_1 0x304 +#define WM8996_AIF1_RX_LRCLK_2 0x305 +#define WM8996_AIF1TX_DATA_CONFIGURATION_1 0x306 +#define WM8996_AIF1TX_DATA_CONFIGURATION_2 0x307 +#define WM8996_AIF1RX_DATA_CONFIGURATION 0x308 +#define WM8996_AIF1TX_CHANNEL_0_CONFIGURATION 0x309 +#define WM8996_AIF1TX_CHANNEL_1_CONFIGURATION 0x30A +#define WM8996_AIF1TX_CHANNEL_2_CONFIGURATION 0x30B +#define WM8996_AIF1TX_CHANNEL_3_CONFIGURATION 0x30C +#define WM8996_AIF1TX_CHANNEL_4_CONFIGURATION 0x30D +#define WM8996_AIF1TX_CHANNEL_5_CONFIGURATION 0x30E +#define WM8996_AIF1RX_CHANNEL_0_CONFIGURATION 0x30F +#define WM8996_AIF1RX_CHANNEL_1_CONFIGURATION 0x310 +#define WM8996_AIF1RX_CHANNEL_2_CONFIGURATION 0x311 +#define WM8996_AIF1RX_CHANNEL_3_CONFIGURATION 0x312 +#define WM8996_AIF1RX_CHANNEL_4_CONFIGURATION 0x313 +#define WM8996_AIF1RX_CHANNEL_5_CONFIGURATION 0x314 +#define WM8996_AIF1RX_MONO_CONFIGURATION 0x315 +#define WM8996_AIF1TX_TEST 0x31A +#define WM8996_AIF2_CONTROL 0x320 +#define WM8996_AIF2_BCLK 0x321 +#define WM8996_AIF2_TX_LRCLK_1 0x322 +#define WM8996_AIF2_TX_LRCLK_2 0x323 +#define WM8996_AIF2_RX_LRCLK_1 0x324 +#define WM8996_AIF2_RX_LRCLK_2 0x325 +#define WM8996_AIF2TX_DATA_CONFIGURATION_1 0x326 +#define WM8996_AIF2TX_DATA_CONFIGURATION_2 0x327 +#define WM8996_AIF2RX_DATA_CONFIGURATION 0x328 +#define WM8996_AIF2TX_CHANNEL_0_CONFIGURATION 0x329 +#define WM8996_AIF2TX_CHANNEL_1_CONFIGURATION 0x32A +#define WM8996_AIF2RX_CHANNEL_0_CONFIGURATION 0x32B +#define WM8996_AIF2RX_CHANNEL_1_CONFIGURATION 0x32C +#define WM8996_AIF2RX_MONO_CONFIGURATION 0x32D +#define WM8996_AIF2TX_TEST 0x32F +#define WM8996_DSP1_TX_LEFT_VOLUME 0x400 +#define WM8996_DSP1_TX_RIGHT_VOLUME 0x401 +#define WM8996_DSP1_RX_LEFT_VOLUME 0x402 +#define WM8996_DSP1_RX_RIGHT_VOLUME 0x403 +#define WM8996_DSP1_TX_FILTERS 0x410 +#define WM8996_DSP1_RX_FILTERS_1 0x420 +#define WM8996_DSP1_RX_FILTERS_2 0x421 +#define WM8996_DSP1_DRC_1 0x440 +#define WM8996_DSP1_DRC_2 0x441 +#define WM8996_DSP1_DRC_3 0x442 +#define WM8996_DSP1_DRC_4 0x443 +#define WM8996_DSP1_DRC_5 0x444 +#define WM8996_DSP1_RX_EQ_GAINS_1 0x480 +#define WM8996_DSP1_RX_EQ_GAINS_2 0x481 +#define WM8996_DSP1_RX_EQ_BAND_1_A 0x482 +#define WM8996_DSP1_RX_EQ_BAND_1_B 0x483 +#define WM8996_DSP1_RX_EQ_BAND_1_PG 0x484 +#define WM8996_DSP1_RX_EQ_BAND_2_A 0x485 +#define WM8996_DSP1_RX_EQ_BAND_2_B 0x486 +#define WM8996_DSP1_RX_EQ_BAND_2_C 0x487 +#define WM8996_DSP1_RX_EQ_BAND_2_PG 0x488 +#define WM8996_DSP1_RX_EQ_BAND_3_A 0x489 +#define WM8996_DSP1_RX_EQ_BAND_3_B 0x48A +#define WM8996_DSP1_RX_EQ_BAND_3_C 0x48B +#define WM8996_DSP1_RX_EQ_BAND_3_PG 0x48C +#define WM8996_DSP1_RX_EQ_BAND_4_A 0x48D +#define WM8996_DSP1_RX_EQ_BAND_4_B 0x48E +#define WM8996_DSP1_RX_EQ_BAND_4_C 0x48F +#define WM8996_DSP1_RX_EQ_BAND_4_PG 0x490 +#define WM8996_DSP1_RX_EQ_BAND_5_A 0x491 +#define WM8996_DSP1_RX_EQ_BAND_5_B 0x492 +#define WM8996_DSP1_RX_EQ_BAND_5_PG 0x493 +#define WM8996_DSP2_TX_LEFT_VOLUME 0x500 +#define WM8996_DSP2_TX_RIGHT_VOLUME 0x501 +#define WM8996_DSP2_RX_LEFT_VOLUME 0x502 +#define WM8996_DSP2_RX_RIGHT_VOLUME 0x503 +#define WM8996_DSP2_TX_FILTERS 0x510 +#define WM8996_DSP2_RX_FILTERS_1 0x520 +#define WM8996_DSP2_RX_FILTERS_2 0x521 +#define WM8996_DSP2_DRC_1 0x540 +#define WM8996_DSP2_DRC_2 0x541 +#define WM8996_DSP2_DRC_3 0x542 +#define WM8996_DSP2_DRC_4 0x543 +#define WM8996_DSP2_DRC_5 0x544 +#define WM8996_DSP2_RX_EQ_GAINS_1 0x580 +#define WM8996_DSP2_RX_EQ_GAINS_2 0x581 +#define WM8996_DSP2_RX_EQ_BAND_1_A 0x582 +#define WM8996_DSP2_RX_EQ_BAND_1_B 0x583 +#define WM8996_DSP2_RX_EQ_BAND_1_PG 0x584 +#define WM8996_DSP2_RX_EQ_BAND_2_A 0x585 +#define WM8996_DSP2_RX_EQ_BAND_2_B 0x586 +#define WM8996_DSP2_RX_EQ_BAND_2_C 0x587 +#define WM8996_DSP2_RX_EQ_BAND_2_PG 0x588 +#define WM8996_DSP2_RX_EQ_BAND_3_A 0x589 +#define WM8996_DSP2_RX_EQ_BAND_3_B 0x58A +#define WM8996_DSP2_RX_EQ_BAND_3_C 0x58B +#define WM8996_DSP2_RX_EQ_BAND_3_PG 0x58C +#define WM8996_DSP2_RX_EQ_BAND_4_A 0x58D +#define WM8996_DSP2_RX_EQ_BAND_4_B 0x58E +#define WM8996_DSP2_RX_EQ_BAND_4_C 0x58F +#define WM8996_DSP2_RX_EQ_BAND_4_PG 0x590 +#define WM8996_DSP2_RX_EQ_BAND_5_A 0x591 +#define WM8996_DSP2_RX_EQ_BAND_5_B 0x592 +#define WM8996_DSP2_RX_EQ_BAND_5_PG 0x593 +#define WM8996_DAC1_MIXER_VOLUMES 0x600 +#define WM8996_DAC1_LEFT_MIXER_ROUTING 0x601 +#define WM8996_DAC1_RIGHT_MIXER_ROUTING 0x602 +#define WM8996_DAC2_MIXER_VOLUMES 0x603 +#define WM8996_DAC2_LEFT_MIXER_ROUTING 0x604 +#define WM8996_DAC2_RIGHT_MIXER_ROUTING 0x605 +#define WM8996_DSP1_TX_LEFT_MIXER_ROUTING 0x606 +#define WM8996_DSP1_TX_RIGHT_MIXER_ROUTING 0x607 +#define WM8996_DSP2_TX_LEFT_MIXER_ROUTING 0x608 +#define WM8996_DSP2_TX_RIGHT_MIXER_ROUTING 0x609 +#define WM8996_DSP_TX_MIXER_SELECT 0x60A +#define WM8996_DAC_SOFTMUTE 0x610 +#define WM8996_OVERSAMPLING 0x620 +#define WM8996_SIDETONE 0x621 +#define WM8996_GPIO_1 0x700 +#define WM8996_GPIO_2 0x701 +#define WM8996_GPIO_3 0x702 +#define WM8996_GPIO_4 0x703 +#define WM8996_GPIO_5 0x704 +#define WM8996_PULL_CONTROL_1 0x720 +#define WM8996_PULL_CONTROL_2 0x721 +#define WM8996_INTERRUPT_STATUS_1 0x730 +#define WM8996_INTERRUPT_STATUS_2 0x731 +#define WM8996_INTERRUPT_RAW_STATUS_2 0x732 +#define WM8996_INTERRUPT_STATUS_1_MASK 0x738 +#define WM8996_INTERRUPT_STATUS_2_MASK 0x739 +#define WM8996_INTERRUPT_CONTROL 0x740 +#define WM8996_LEFT_PDM_SPEAKER 0x800 +#define WM8996_RIGHT_PDM_SPEAKER 0x801 +#define WM8996_PDM_SPEAKER_MUTE_SEQUENCE 0x802 +#define WM8996_PDM_SPEAKER_VOLUME 0x803 +#define WM8996_WRITE_SEQUENCER_0 0x3000 +#define WM8996_WRITE_SEQUENCER_1 0x3001 +#define WM8996_WRITE_SEQUENCER_2 0x3002 +#define WM8996_WRITE_SEQUENCER_3 0x3003 +#define WM8996_WRITE_SEQUENCER_4 0x3004 +#define WM8996_WRITE_SEQUENCER_5 0x3005 +#define WM8996_WRITE_SEQUENCER_6 0x3006 +#define WM8996_WRITE_SEQUENCER_7 0x3007 +#define WM8996_WRITE_SEQUENCER_8 0x3008 +#define WM8996_WRITE_SEQUENCER_9 0x3009 +#define WM8996_WRITE_SEQUENCER_10 0x300A +#define WM8996_WRITE_SEQUENCER_11 0x300B +#define WM8996_WRITE_SEQUENCER_12 0x300C +#define WM8996_WRITE_SEQUENCER_13 0x300D +#define WM8996_WRITE_SEQUENCER_14 0x300E +#define WM8996_WRITE_SEQUENCER_15 0x300F +#define WM8996_WRITE_SEQUENCER_16 0x3010 +#define WM8996_WRITE_SEQUENCER_17 0x3011 +#define WM8996_WRITE_SEQUENCER_18 0x3012 +#define WM8996_WRITE_SEQUENCER_19 0x3013 +#define WM8996_WRITE_SEQUENCER_20 0x3014 +#define WM8996_WRITE_SEQUENCER_21 0x3015 +#define WM8996_WRITE_SEQUENCER_22 0x3016 +#define WM8996_WRITE_SEQUENCER_23 0x3017 +#define WM8996_WRITE_SEQUENCER_24 0x3018 +#define WM8996_WRITE_SEQUENCER_25 0x3019 +#define WM8996_WRITE_SEQUENCER_26 0x301A +#define WM8996_WRITE_SEQUENCER_27 0x301B +#define WM8996_WRITE_SEQUENCER_28 0x301C +#define WM8996_WRITE_SEQUENCER_29 0x301D +#define WM8996_WRITE_SEQUENCER_30 0x301E +#define WM8996_WRITE_SEQUENCER_31 0x301F +#define WM8996_WRITE_SEQUENCER_32 0x3020 +#define WM8996_WRITE_SEQUENCER_33 0x3021 +#define WM8996_WRITE_SEQUENCER_34 0x3022 +#define WM8996_WRITE_SEQUENCER_35 0x3023 +#define WM8996_WRITE_SEQUENCER_36 0x3024 +#define WM8996_WRITE_SEQUENCER_37 0x3025 +#define WM8996_WRITE_SEQUENCER_38 0x3026 +#define WM8996_WRITE_SEQUENCER_39 0x3027 +#define WM8996_WRITE_SEQUENCER_40 0x3028 +#define WM8996_WRITE_SEQUENCER_41 0x3029 +#define WM8996_WRITE_SEQUENCER_42 0x302A +#define WM8996_WRITE_SEQUENCER_43 0x302B +#define WM8996_WRITE_SEQUENCER_44 0x302C +#define WM8996_WRITE_SEQUENCER_45 0x302D +#define WM8996_WRITE_SEQUENCER_46 0x302E +#define WM8996_WRITE_SEQUENCER_47 0x302F +#define WM8996_WRITE_SEQUENCER_48 0x3030 +#define WM8996_WRITE_SEQUENCER_49 0x3031 +#define WM8996_WRITE_SEQUENCER_50 0x3032 +#define WM8996_WRITE_SEQUENCER_51 0x3033 +#define WM8996_WRITE_SEQUENCER_52 0x3034 +#define WM8996_WRITE_SEQUENCER_53 0x3035 +#define WM8996_WRITE_SEQUENCER_54 0x3036 +#define WM8996_WRITE_SEQUENCER_55 0x3037 +#define WM8996_WRITE_SEQUENCER_56 0x3038 +#define WM8996_WRITE_SEQUENCER_57 0x3039 +#define WM8996_WRITE_SEQUENCER_58 0x303A +#define WM8996_WRITE_SEQUENCER_59 0x303B +#define WM8996_WRITE_SEQUENCER_60 0x303C +#define WM8996_WRITE_SEQUENCER_61 0x303D +#define WM8996_WRITE_SEQUENCER_62 0x303E +#define WM8996_WRITE_SEQUENCER_63 0x303F +#define WM8996_WRITE_SEQUENCER_64 0x3040 +#define WM8996_WRITE_SEQUENCER_65 0x3041 +#define WM8996_WRITE_SEQUENCER_66 0x3042 +#define WM8996_WRITE_SEQUENCER_67 0x3043 +#define WM8996_WRITE_SEQUENCER_68 0x3044 +#define WM8996_WRITE_SEQUENCER_69 0x3045 +#define WM8996_WRITE_SEQUENCER_70 0x3046 +#define WM8996_WRITE_SEQUENCER_71 0x3047 +#define WM8996_WRITE_SEQUENCER_72 0x3048 +#define WM8996_WRITE_SEQUENCER_73 0x3049 +#define WM8996_WRITE_SEQUENCER_74 0x304A +#define WM8996_WRITE_SEQUENCER_75 0x304B +#define WM8996_WRITE_SEQUENCER_76 0x304C +#define WM8996_WRITE_SEQUENCER_77 0x304D +#define WM8996_WRITE_SEQUENCER_78 0x304E +#define WM8996_WRITE_SEQUENCER_79 0x304F +#define WM8996_WRITE_SEQUENCER_80 0x3050 +#define WM8996_WRITE_SEQUENCER_81 0x3051 +#define WM8996_WRITE_SEQUENCER_82 0x3052 +#define WM8996_WRITE_SEQUENCER_83 0x3053 +#define WM8996_WRITE_SEQUENCER_84 0x3054 +#define WM8996_WRITE_SEQUENCER_85 0x3055 +#define WM8996_WRITE_SEQUENCER_86 0x3056 +#define WM8996_WRITE_SEQUENCER_87 0x3057 +#define WM8996_WRITE_SEQUENCER_88 0x3058 +#define WM8996_WRITE_SEQUENCER_89 0x3059 +#define WM8996_WRITE_SEQUENCER_90 0x305A +#define WM8996_WRITE_SEQUENCER_91 0x305B +#define WM8996_WRITE_SEQUENCER_92 0x305C +#define WM8996_WRITE_SEQUENCER_93 0x305D +#define WM8996_WRITE_SEQUENCER_94 0x305E +#define WM8996_WRITE_SEQUENCER_95 0x305F +#define WM8996_WRITE_SEQUENCER_96 0x3060 +#define WM8996_WRITE_SEQUENCER_97 0x3061 +#define WM8996_WRITE_SEQUENCER_98 0x3062 +#define WM8996_WRITE_SEQUENCER_99 0x3063 +#define WM8996_WRITE_SEQUENCER_100 0x3064 +#define WM8996_WRITE_SEQUENCER_101 0x3065 +#define WM8996_WRITE_SEQUENCER_102 0x3066 +#define WM8996_WRITE_SEQUENCER_103 0x3067 +#define WM8996_WRITE_SEQUENCER_104 0x3068 +#define WM8996_WRITE_SEQUENCER_105 0x3069 +#define WM8996_WRITE_SEQUENCER_106 0x306A +#define WM8996_WRITE_SEQUENCER_107 0x306B +#define WM8996_WRITE_SEQUENCER_108 0x306C +#define WM8996_WRITE_SEQUENCER_109 0x306D +#define WM8996_WRITE_SEQUENCER_110 0x306E +#define WM8996_WRITE_SEQUENCER_111 0x306F +#define WM8996_WRITE_SEQUENCER_112 0x3070 +#define WM8996_WRITE_SEQUENCER_113 0x3071 +#define WM8996_WRITE_SEQUENCER_114 0x3072 +#define WM8996_WRITE_SEQUENCER_115 0x3073 +#define WM8996_WRITE_SEQUENCER_116 0x3074 +#define WM8996_WRITE_SEQUENCER_117 0x3075 +#define WM8996_WRITE_SEQUENCER_118 0x3076 +#define WM8996_WRITE_SEQUENCER_119 0x3077 +#define WM8996_WRITE_SEQUENCER_120 0x3078 +#define WM8996_WRITE_SEQUENCER_121 0x3079 +#define WM8996_WRITE_SEQUENCER_122 0x307A +#define WM8996_WRITE_SEQUENCER_123 0x307B +#define WM8996_WRITE_SEQUENCER_124 0x307C +#define WM8996_WRITE_SEQUENCER_125 0x307D +#define WM8996_WRITE_SEQUENCER_126 0x307E +#define WM8996_WRITE_SEQUENCER_127 0x307F +#define WM8996_WRITE_SEQUENCER_128 0x3080 +#define WM8996_WRITE_SEQUENCER_129 0x3081 +#define WM8996_WRITE_SEQUENCER_130 0x3082 +#define WM8996_WRITE_SEQUENCER_131 0x3083 +#define WM8996_WRITE_SEQUENCER_132 0x3084 +#define WM8996_WRITE_SEQUENCER_133 0x3085 +#define WM8996_WRITE_SEQUENCER_134 0x3086 +#define WM8996_WRITE_SEQUENCER_135 0x3087 +#define WM8996_WRITE_SEQUENCER_136 0x3088 +#define WM8996_WRITE_SEQUENCER_137 0x3089 +#define WM8996_WRITE_SEQUENCER_138 0x308A +#define WM8996_WRITE_SEQUENCER_139 0x308B +#define WM8996_WRITE_SEQUENCER_140 0x308C +#define WM8996_WRITE_SEQUENCER_141 0x308D +#define WM8996_WRITE_SEQUENCER_142 0x308E +#define WM8996_WRITE_SEQUENCER_143 0x308F +#define WM8996_WRITE_SEQUENCER_144 0x3090 +#define WM8996_WRITE_SEQUENCER_145 0x3091 +#define WM8996_WRITE_SEQUENCER_146 0x3092 +#define WM8996_WRITE_SEQUENCER_147 0x3093 +#define WM8996_WRITE_SEQUENCER_148 0x3094 +#define WM8996_WRITE_SEQUENCER_149 0x3095 +#define WM8996_WRITE_SEQUENCER_150 0x3096 +#define WM8996_WRITE_SEQUENCER_151 0x3097 +#define WM8996_WRITE_SEQUENCER_152 0x3098 +#define WM8996_WRITE_SEQUENCER_153 0x3099 +#define WM8996_WRITE_SEQUENCER_154 0x309A +#define WM8996_WRITE_SEQUENCER_155 0x309B +#define WM8996_WRITE_SEQUENCER_156 0x309C +#define WM8996_WRITE_SEQUENCER_157 0x309D +#define WM8996_WRITE_SEQUENCER_158 0x309E +#define WM8996_WRITE_SEQUENCER_159 0x309F +#define WM8996_WRITE_SEQUENCER_160 0x30A0 +#define WM8996_WRITE_SEQUENCER_161 0x30A1 +#define WM8996_WRITE_SEQUENCER_162 0x30A2 +#define WM8996_WRITE_SEQUENCER_163 0x30A3 +#define WM8996_WRITE_SEQUENCER_164 0x30A4 +#define WM8996_WRITE_SEQUENCER_165 0x30A5 +#define WM8996_WRITE_SEQUENCER_166 0x30A6 +#define WM8996_WRITE_SEQUENCER_167 0x30A7 +#define WM8996_WRITE_SEQUENCER_168 0x30A8 +#define WM8996_WRITE_SEQUENCER_169 0x30A9 +#define WM8996_WRITE_SEQUENCER_170 0x30AA +#define WM8996_WRITE_SEQUENCER_171 0x30AB +#define WM8996_WRITE_SEQUENCER_172 0x30AC +#define WM8996_WRITE_SEQUENCER_173 0x30AD +#define WM8996_WRITE_SEQUENCER_174 0x30AE +#define WM8996_WRITE_SEQUENCER_175 0x30AF +#define WM8996_WRITE_SEQUENCER_176 0x30B0 +#define WM8996_WRITE_SEQUENCER_177 0x30B1 +#define WM8996_WRITE_SEQUENCER_178 0x30B2 +#define WM8996_WRITE_SEQUENCER_179 0x30B3 +#define WM8996_WRITE_SEQUENCER_180 0x30B4 +#define WM8996_WRITE_SEQUENCER_181 0x30B5 +#define WM8996_WRITE_SEQUENCER_182 0x30B6 +#define WM8996_WRITE_SEQUENCER_183 0x30B7 +#define WM8996_WRITE_SEQUENCER_184 0x30B8 +#define WM8996_WRITE_SEQUENCER_185 0x30B9 +#define WM8996_WRITE_SEQUENCER_186 0x30BA +#define WM8996_WRITE_SEQUENCER_187 0x30BB +#define WM8996_WRITE_SEQUENCER_188 0x30BC +#define WM8996_WRITE_SEQUENCER_189 0x30BD +#define WM8996_WRITE_SEQUENCER_190 0x30BE +#define WM8996_WRITE_SEQUENCER_191 0x30BF +#define WM8996_WRITE_SEQUENCER_192 0x30C0 +#define WM8996_WRITE_SEQUENCER_193 0x30C1 +#define WM8996_WRITE_SEQUENCER_194 0x30C2 +#define WM8996_WRITE_SEQUENCER_195 0x30C3 +#define WM8996_WRITE_SEQUENCER_196 0x30C4 +#define WM8996_WRITE_SEQUENCER_197 0x30C5 +#define WM8996_WRITE_SEQUENCER_198 0x30C6 +#define WM8996_WRITE_SEQUENCER_199 0x30C7 +#define WM8996_WRITE_SEQUENCER_200 0x30C8 +#define WM8996_WRITE_SEQUENCER_201 0x30C9 +#define WM8996_WRITE_SEQUENCER_202 0x30CA +#define WM8996_WRITE_SEQUENCER_203 0x30CB +#define WM8996_WRITE_SEQUENCER_204 0x30CC +#define WM8996_WRITE_SEQUENCER_205 0x30CD +#define WM8996_WRITE_SEQUENCER_206 0x30CE +#define WM8996_WRITE_SEQUENCER_207 0x30CF +#define WM8996_WRITE_SEQUENCER_208 0x30D0 +#define WM8996_WRITE_SEQUENCER_209 0x30D1 +#define WM8996_WRITE_SEQUENCER_210 0x30D2 +#define WM8996_WRITE_SEQUENCER_211 0x30D3 +#define WM8996_WRITE_SEQUENCER_212 0x30D4 +#define WM8996_WRITE_SEQUENCER_213 0x30D5 +#define WM8996_WRITE_SEQUENCER_214 0x30D6 +#define WM8996_WRITE_SEQUENCER_215 0x30D7 +#define WM8996_WRITE_SEQUENCER_216 0x30D8 +#define WM8996_WRITE_SEQUENCER_217 0x30D9 +#define WM8996_WRITE_SEQUENCER_218 0x30DA +#define WM8996_WRITE_SEQUENCER_219 0x30DB +#define WM8996_WRITE_SEQUENCER_220 0x30DC +#define WM8996_WRITE_SEQUENCER_221 0x30DD +#define WM8996_WRITE_SEQUENCER_222 0x30DE +#define WM8996_WRITE_SEQUENCER_223 0x30DF +#define WM8996_WRITE_SEQUENCER_224 0x30E0 +#define WM8996_WRITE_SEQUENCER_225 0x30E1 +#define WM8996_WRITE_SEQUENCER_226 0x30E2 +#define WM8996_WRITE_SEQUENCER_227 0x30E3 +#define WM8996_WRITE_SEQUENCER_228 0x30E4 +#define WM8996_WRITE_SEQUENCER_229 0x30E5 +#define WM8996_WRITE_SEQUENCER_230 0x30E6 +#define WM8996_WRITE_SEQUENCER_231 0x30E7 +#define WM8996_WRITE_SEQUENCER_232 0x30E8 +#define WM8996_WRITE_SEQUENCER_233 0x30E9 +#define WM8996_WRITE_SEQUENCER_234 0x30EA +#define WM8996_WRITE_SEQUENCER_235 0x30EB +#define WM8996_WRITE_SEQUENCER_236 0x30EC +#define WM8996_WRITE_SEQUENCER_237 0x30ED +#define WM8996_WRITE_SEQUENCER_238 0x30EE +#define WM8996_WRITE_SEQUENCER_239 0x30EF +#define WM8996_WRITE_SEQUENCER_240 0x30F0 +#define WM8996_WRITE_SEQUENCER_241 0x30F1 +#define WM8996_WRITE_SEQUENCER_242 0x30F2 +#define WM8996_WRITE_SEQUENCER_243 0x30F3 +#define WM8996_WRITE_SEQUENCER_244 0x30F4 +#define WM8996_WRITE_SEQUENCER_245 0x30F5 +#define WM8996_WRITE_SEQUENCER_246 0x30F6 +#define WM8996_WRITE_SEQUENCER_247 0x30F7 +#define WM8996_WRITE_SEQUENCER_248 0x30F8 +#define WM8996_WRITE_SEQUENCER_249 0x30F9 +#define WM8996_WRITE_SEQUENCER_250 0x30FA +#define WM8996_WRITE_SEQUENCER_251 0x30FB +#define WM8996_WRITE_SEQUENCER_252 0x30FC +#define WM8996_WRITE_SEQUENCER_253 0x30FD +#define WM8996_WRITE_SEQUENCER_254 0x30FE +#define WM8996_WRITE_SEQUENCER_255 0x30FF +#define WM8996_WRITE_SEQUENCER_256 0x3100 +#define WM8996_WRITE_SEQUENCER_257 0x3101 +#define WM8996_WRITE_SEQUENCER_258 0x3102 +#define WM8996_WRITE_SEQUENCER_259 0x3103 +#define WM8996_WRITE_SEQUENCER_260 0x3104 +#define WM8996_WRITE_SEQUENCER_261 0x3105 +#define WM8996_WRITE_SEQUENCER_262 0x3106 +#define WM8996_WRITE_SEQUENCER_263 0x3107 +#define WM8996_WRITE_SEQUENCER_264 0x3108 +#define WM8996_WRITE_SEQUENCER_265 0x3109 +#define WM8996_WRITE_SEQUENCER_266 0x310A +#define WM8996_WRITE_SEQUENCER_267 0x310B +#define WM8996_WRITE_SEQUENCER_268 0x310C +#define WM8996_WRITE_SEQUENCER_269 0x310D +#define WM8996_WRITE_SEQUENCER_270 0x310E +#define WM8996_WRITE_SEQUENCER_271 0x310F +#define WM8996_WRITE_SEQUENCER_272 0x3110 +#define WM8996_WRITE_SEQUENCER_273 0x3111 +#define WM8996_WRITE_SEQUENCER_274 0x3112 +#define WM8996_WRITE_SEQUENCER_275 0x3113 +#define WM8996_WRITE_SEQUENCER_276 0x3114 +#define WM8996_WRITE_SEQUENCER_277 0x3115 +#define WM8996_WRITE_SEQUENCER_278 0x3116 +#define WM8996_WRITE_SEQUENCER_279 0x3117 +#define WM8996_WRITE_SEQUENCER_280 0x3118 +#define WM8996_WRITE_SEQUENCER_281 0x3119 +#define WM8996_WRITE_SEQUENCER_282 0x311A +#define WM8996_WRITE_SEQUENCER_283 0x311B +#define WM8996_WRITE_SEQUENCER_284 0x311C +#define WM8996_WRITE_SEQUENCER_285 0x311D +#define WM8996_WRITE_SEQUENCER_286 0x311E +#define WM8996_WRITE_SEQUENCER_287 0x311F +#define WM8996_WRITE_SEQUENCER_288 0x3120 +#define WM8996_WRITE_SEQUENCER_289 0x3121 +#define WM8996_WRITE_SEQUENCER_290 0x3122 +#define WM8996_WRITE_SEQUENCER_291 0x3123 +#define WM8996_WRITE_SEQUENCER_292 0x3124 +#define WM8996_WRITE_SEQUENCER_293 0x3125 +#define WM8996_WRITE_SEQUENCER_294 0x3126 +#define WM8996_WRITE_SEQUENCER_295 0x3127 +#define WM8996_WRITE_SEQUENCER_296 0x3128 +#define WM8996_WRITE_SEQUENCER_297 0x3129 +#define WM8996_WRITE_SEQUENCER_298 0x312A +#define WM8996_WRITE_SEQUENCER_299 0x312B +#define WM8996_WRITE_SEQUENCER_300 0x312C +#define WM8996_WRITE_SEQUENCER_301 0x312D +#define WM8996_WRITE_SEQUENCER_302 0x312E +#define WM8996_WRITE_SEQUENCER_303 0x312F +#define WM8996_WRITE_SEQUENCER_304 0x3130 +#define WM8996_WRITE_SEQUENCER_305 0x3131 +#define WM8996_WRITE_SEQUENCER_306 0x3132 +#define WM8996_WRITE_SEQUENCER_307 0x3133 +#define WM8996_WRITE_SEQUENCER_308 0x3134 +#define WM8996_WRITE_SEQUENCER_309 0x3135 +#define WM8996_WRITE_SEQUENCER_310 0x3136 +#define WM8996_WRITE_SEQUENCER_311 0x3137 +#define WM8996_WRITE_SEQUENCER_312 0x3138 +#define WM8996_WRITE_SEQUENCER_313 0x3139 +#define WM8996_WRITE_SEQUENCER_314 0x313A +#define WM8996_WRITE_SEQUENCER_315 0x313B +#define WM8996_WRITE_SEQUENCER_316 0x313C +#define WM8996_WRITE_SEQUENCER_317 0x313D +#define WM8996_WRITE_SEQUENCER_318 0x313E +#define WM8996_WRITE_SEQUENCER_319 0x313F +#define WM8996_WRITE_SEQUENCER_320 0x3140 +#define WM8996_WRITE_SEQUENCER_321 0x3141 +#define WM8996_WRITE_SEQUENCER_322 0x3142 +#define WM8996_WRITE_SEQUENCER_323 0x3143 +#define WM8996_WRITE_SEQUENCER_324 0x3144 +#define WM8996_WRITE_SEQUENCER_325 0x3145 +#define WM8996_WRITE_SEQUENCER_326 0x3146 +#define WM8996_WRITE_SEQUENCER_327 0x3147 +#define WM8996_WRITE_SEQUENCER_328 0x3148 +#define WM8996_WRITE_SEQUENCER_329 0x3149 +#define WM8996_WRITE_SEQUENCER_330 0x314A +#define WM8996_WRITE_SEQUENCER_331 0x314B +#define WM8996_WRITE_SEQUENCER_332 0x314C +#define WM8996_WRITE_SEQUENCER_333 0x314D +#define WM8996_WRITE_SEQUENCER_334 0x314E +#define WM8996_WRITE_SEQUENCER_335 0x314F +#define WM8996_WRITE_SEQUENCER_336 0x3150 +#define WM8996_WRITE_SEQUENCER_337 0x3151 +#define WM8996_WRITE_SEQUENCER_338 0x3152 +#define WM8996_WRITE_SEQUENCER_339 0x3153 +#define WM8996_WRITE_SEQUENCER_340 0x3154 +#define WM8996_WRITE_SEQUENCER_341 0x3155 +#define WM8996_WRITE_SEQUENCER_342 0x3156 +#define WM8996_WRITE_SEQUENCER_343 0x3157 +#define WM8996_WRITE_SEQUENCER_344 0x3158 +#define WM8996_WRITE_SEQUENCER_345 0x3159 +#define WM8996_WRITE_SEQUENCER_346 0x315A +#define WM8996_WRITE_SEQUENCER_347 0x315B +#define WM8996_WRITE_SEQUENCER_348 0x315C +#define WM8996_WRITE_SEQUENCER_349 0x315D +#define WM8996_WRITE_SEQUENCER_350 0x315E +#define WM8996_WRITE_SEQUENCER_351 0x315F +#define WM8996_WRITE_SEQUENCER_352 0x3160 +#define WM8996_WRITE_SEQUENCER_353 0x3161 +#define WM8996_WRITE_SEQUENCER_354 0x3162 +#define WM8996_WRITE_SEQUENCER_355 0x3163 +#define WM8996_WRITE_SEQUENCER_356 0x3164 +#define WM8996_WRITE_SEQUENCER_357 0x3165 +#define WM8996_WRITE_SEQUENCER_358 0x3166 +#define WM8996_WRITE_SEQUENCER_359 0x3167 +#define WM8996_WRITE_SEQUENCER_360 0x3168 +#define WM8996_WRITE_SEQUENCER_361 0x3169 +#define WM8996_WRITE_SEQUENCER_362 0x316A +#define WM8996_WRITE_SEQUENCER_363 0x316B +#define WM8996_WRITE_SEQUENCER_364 0x316C +#define WM8996_WRITE_SEQUENCER_365 0x316D +#define WM8996_WRITE_SEQUENCER_366 0x316E +#define WM8996_WRITE_SEQUENCER_367 0x316F +#define WM8996_WRITE_SEQUENCER_368 0x3170 +#define WM8996_WRITE_SEQUENCER_369 0x3171 +#define WM8996_WRITE_SEQUENCER_370 0x3172 +#define WM8996_WRITE_SEQUENCER_371 0x3173 +#define WM8996_WRITE_SEQUENCER_372 0x3174 +#define WM8996_WRITE_SEQUENCER_373 0x3175 +#define WM8996_WRITE_SEQUENCER_374 0x3176 +#define WM8996_WRITE_SEQUENCER_375 0x3177 +#define WM8996_WRITE_SEQUENCER_376 0x3178 +#define WM8996_WRITE_SEQUENCER_377 0x3179 +#define WM8996_WRITE_SEQUENCER_378 0x317A +#define WM8996_WRITE_SEQUENCER_379 0x317B +#define WM8996_WRITE_SEQUENCER_380 0x317C +#define WM8996_WRITE_SEQUENCER_381 0x317D +#define WM8996_WRITE_SEQUENCER_382 0x317E +#define WM8996_WRITE_SEQUENCER_383 0x317F +#define WM8996_WRITE_SEQUENCER_384 0x3180 +#define WM8996_WRITE_SEQUENCER_385 0x3181 +#define WM8996_WRITE_SEQUENCER_386 0x3182 +#define WM8996_WRITE_SEQUENCER_387 0x3183 +#define WM8996_WRITE_SEQUENCER_388 0x3184 +#define WM8996_WRITE_SEQUENCER_389 0x3185 +#define WM8996_WRITE_SEQUENCER_390 0x3186 +#define WM8996_WRITE_SEQUENCER_391 0x3187 +#define WM8996_WRITE_SEQUENCER_392 0x3188 +#define WM8996_WRITE_SEQUENCER_393 0x3189 +#define WM8996_WRITE_SEQUENCER_394 0x318A +#define WM8996_WRITE_SEQUENCER_395 0x318B +#define WM8996_WRITE_SEQUENCER_396 0x318C +#define WM8996_WRITE_SEQUENCER_397 0x318D +#define WM8996_WRITE_SEQUENCER_398 0x318E +#define WM8996_WRITE_SEQUENCER_399 0x318F +#define WM8996_WRITE_SEQUENCER_400 0x3190 +#define WM8996_WRITE_SEQUENCER_401 0x3191 +#define WM8996_WRITE_SEQUENCER_402 0x3192 +#define WM8996_WRITE_SEQUENCER_403 0x3193 +#define WM8996_WRITE_SEQUENCER_404 0x3194 +#define WM8996_WRITE_SEQUENCER_405 0x3195 +#define WM8996_WRITE_SEQUENCER_406 0x3196 +#define WM8996_WRITE_SEQUENCER_407 0x3197 +#define WM8996_WRITE_SEQUENCER_408 0x3198 +#define WM8996_WRITE_SEQUENCER_409 0x3199 +#define WM8996_WRITE_SEQUENCER_410 0x319A +#define WM8996_WRITE_SEQUENCER_411 0x319B +#define WM8996_WRITE_SEQUENCER_412 0x319C +#define WM8996_WRITE_SEQUENCER_413 0x319D +#define WM8996_WRITE_SEQUENCER_414 0x319E +#define WM8996_WRITE_SEQUENCER_415 0x319F +#define WM8996_WRITE_SEQUENCER_416 0x31A0 +#define WM8996_WRITE_SEQUENCER_417 0x31A1 +#define WM8996_WRITE_SEQUENCER_418 0x31A2 +#define WM8996_WRITE_SEQUENCER_419 0x31A3 +#define WM8996_WRITE_SEQUENCER_420 0x31A4 +#define WM8996_WRITE_SEQUENCER_421 0x31A5 +#define WM8996_WRITE_SEQUENCER_422 0x31A6 +#define WM8996_WRITE_SEQUENCER_423 0x31A7 +#define WM8996_WRITE_SEQUENCER_424 0x31A8 +#define WM8996_WRITE_SEQUENCER_425 0x31A9 +#define WM8996_WRITE_SEQUENCER_426 0x31AA +#define WM8996_WRITE_SEQUENCER_427 0x31AB +#define WM8996_WRITE_SEQUENCER_428 0x31AC +#define WM8996_WRITE_SEQUENCER_429 0x31AD +#define WM8996_WRITE_SEQUENCER_430 0x31AE +#define WM8996_WRITE_SEQUENCER_431 0x31AF +#define WM8996_WRITE_SEQUENCER_432 0x31B0 +#define WM8996_WRITE_SEQUENCER_433 0x31B1 +#define WM8996_WRITE_SEQUENCER_434 0x31B2 +#define WM8996_WRITE_SEQUENCER_435 0x31B3 +#define WM8996_WRITE_SEQUENCER_436 0x31B4 +#define WM8996_WRITE_SEQUENCER_437 0x31B5 +#define WM8996_WRITE_SEQUENCER_438 0x31B6 +#define WM8996_WRITE_SEQUENCER_439 0x31B7 +#define WM8996_WRITE_SEQUENCER_440 0x31B8 +#define WM8996_WRITE_SEQUENCER_441 0x31B9 +#define WM8996_WRITE_SEQUENCER_442 0x31BA +#define WM8996_WRITE_SEQUENCER_443 0x31BB +#define WM8996_WRITE_SEQUENCER_444 0x31BC +#define WM8996_WRITE_SEQUENCER_445 0x31BD +#define WM8996_WRITE_SEQUENCER_446 0x31BE +#define WM8996_WRITE_SEQUENCER_447 0x31BF +#define WM8996_WRITE_SEQUENCER_448 0x31C0 +#define WM8996_WRITE_SEQUENCER_449 0x31C1 +#define WM8996_WRITE_SEQUENCER_450 0x31C2 +#define WM8996_WRITE_SEQUENCER_451 0x31C3 +#define WM8996_WRITE_SEQUENCER_452 0x31C4 +#define WM8996_WRITE_SEQUENCER_453 0x31C5 +#define WM8996_WRITE_SEQUENCER_454 0x31C6 +#define WM8996_WRITE_SEQUENCER_455 0x31C7 +#define WM8996_WRITE_SEQUENCER_456 0x31C8 +#define WM8996_WRITE_SEQUENCER_457 0x31C9 +#define WM8996_WRITE_SEQUENCER_458 0x31CA +#define WM8996_WRITE_SEQUENCER_459 0x31CB +#define WM8996_WRITE_SEQUENCER_460 0x31CC +#define WM8996_WRITE_SEQUENCER_461 0x31CD +#define WM8996_WRITE_SEQUENCER_462 0x31CE +#define WM8996_WRITE_SEQUENCER_463 0x31CF +#define WM8996_WRITE_SEQUENCER_464 0x31D0 +#define WM8996_WRITE_SEQUENCER_465 0x31D1 +#define WM8996_WRITE_SEQUENCER_466 0x31D2 +#define WM8996_WRITE_SEQUENCER_467 0x31D3 +#define WM8996_WRITE_SEQUENCER_468 0x31D4 +#define WM8996_WRITE_SEQUENCER_469 0x31D5 +#define WM8996_WRITE_SEQUENCER_470 0x31D6 +#define WM8996_WRITE_SEQUENCER_471 0x31D7 +#define WM8996_WRITE_SEQUENCER_472 0x31D8 +#define WM8996_WRITE_SEQUENCER_473 0x31D9 +#define WM8996_WRITE_SEQUENCER_474 0x31DA +#define WM8996_WRITE_SEQUENCER_475 0x31DB +#define WM8996_WRITE_SEQUENCER_476 0x31DC +#define WM8996_WRITE_SEQUENCER_477 0x31DD +#define WM8996_WRITE_SEQUENCER_478 0x31DE +#define WM8996_WRITE_SEQUENCER_479 0x31DF +#define WM8996_WRITE_SEQUENCER_480 0x31E0 +#define WM8996_WRITE_SEQUENCER_481 0x31E1 +#define WM8996_WRITE_SEQUENCER_482 0x31E2 +#define WM8996_WRITE_SEQUENCER_483 0x31E3 +#define WM8996_WRITE_SEQUENCER_484 0x31E4 +#define WM8996_WRITE_SEQUENCER_485 0x31E5 +#define WM8996_WRITE_SEQUENCER_486 0x31E6 +#define WM8996_WRITE_SEQUENCER_487 0x31E7 +#define WM8996_WRITE_SEQUENCER_488 0x31E8 +#define WM8996_WRITE_SEQUENCER_489 0x31E9 +#define WM8996_WRITE_SEQUENCER_490 0x31EA +#define WM8996_WRITE_SEQUENCER_491 0x31EB +#define WM8996_WRITE_SEQUENCER_492 0x31EC +#define WM8996_WRITE_SEQUENCER_493 0x31ED +#define WM8996_WRITE_SEQUENCER_494 0x31EE +#define WM8996_WRITE_SEQUENCER_495 0x31EF +#define WM8996_WRITE_SEQUENCER_496 0x31F0 +#define WM8996_WRITE_SEQUENCER_497 0x31F1 +#define WM8996_WRITE_SEQUENCER_498 0x31F2 +#define WM8996_WRITE_SEQUENCER_499 0x31F3 +#define WM8996_WRITE_SEQUENCER_500 0x31F4 +#define WM8996_WRITE_SEQUENCER_501 0x31F5 +#define WM8996_WRITE_SEQUENCER_502 0x31F6 +#define WM8996_WRITE_SEQUENCER_503 0x31F7 +#define WM8996_WRITE_SEQUENCER_504 0x31F8 +#define WM8996_WRITE_SEQUENCER_505 0x31F9 +#define WM8996_WRITE_SEQUENCER_506 0x31FA +#define WM8996_WRITE_SEQUENCER_507 0x31FB +#define WM8996_WRITE_SEQUENCER_508 0x31FC +#define WM8996_WRITE_SEQUENCER_509 0x31FD +#define WM8996_WRITE_SEQUENCER_510 0x31FE +#define WM8996_WRITE_SEQUENCER_511 0x31FF + +#define WM8996_REGISTER_COUNT 706 +#define WM8996_MAX_REGISTER 0x31FF + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +#define WM8996_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */ +#define WM8996_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */ +#define WM8996_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */ + +/* + * R1 (0x01) - Power Management (1) + */ +#define WM8996_MICB2_ENA 0x0200 /* MICB2_ENA */ +#define WM8996_MICB2_ENA_MASK 0x0200 /* MICB2_ENA */ +#define WM8996_MICB2_ENA_SHIFT 9 /* MICB2_ENA */ +#define WM8996_MICB2_ENA_WIDTH 1 /* MICB2_ENA */ +#define WM8996_MICB1_ENA 0x0100 /* MICB1_ENA */ +#define WM8996_MICB1_ENA_MASK 0x0100 /* MICB1_ENA */ +#define WM8996_MICB1_ENA_SHIFT 8 /* MICB1_ENA */ +#define WM8996_MICB1_ENA_WIDTH 1 /* MICB1_ENA */ +#define WM8996_HPOUT2L_ENA 0x0080 /* HPOUT2L_ENA */ +#define WM8996_HPOUT2L_ENA_MASK 0x0080 /* HPOUT2L_ENA */ +#define WM8996_HPOUT2L_ENA_SHIFT 7 /* HPOUT2L_ENA */ +#define WM8996_HPOUT2L_ENA_WIDTH 1 /* HPOUT2L_ENA */ +#define WM8996_HPOUT2R_ENA 0x0040 /* HPOUT2R_ENA */ +#define WM8996_HPOUT2R_ENA_MASK 0x0040 /* HPOUT2R_ENA */ +#define WM8996_HPOUT2R_ENA_SHIFT 6 /* HPOUT2R_ENA */ +#define WM8996_HPOUT2R_ENA_WIDTH 1 /* HPOUT2R_ENA */ +#define WM8996_HPOUT1L_ENA 0x0020 /* HPOUT1L_ENA */ +#define WM8996_HPOUT1L_ENA_MASK 0x0020 /* HPOUT1L_ENA */ +#define WM8996_HPOUT1L_ENA_SHIFT 5 /* HPOUT1L_ENA */ +#define WM8996_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */ +#define WM8996_HPOUT1R_ENA 0x0010 /* HPOUT1R_ENA */ +#define WM8996_HPOUT1R_ENA_MASK 0x0010 /* HPOUT1R_ENA */ +#define WM8996_HPOUT1R_ENA_SHIFT 4 /* HPOUT1R_ENA */ +#define WM8996_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */ +#define WM8996_BG_ENA 0x0001 /* BG_ENA */ +#define WM8996_BG_ENA_MASK 0x0001 /* BG_ENA */ +#define WM8996_BG_ENA_SHIFT 0 /* BG_ENA */ +#define WM8996_BG_ENA_WIDTH 1 /* BG_ENA */ + +/* + * R2 (0x02) - Power Management (2) + */ +#define WM8996_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8996_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */ +#define WM8996_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */ +#define WM8996_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define WM8996_INL_ENA 0x0020 /* INL_ENA */ +#define WM8996_INL_ENA_MASK 0x0020 /* INL_ENA */ +#define WM8996_INL_ENA_SHIFT 5 /* INL_ENA */ +#define WM8996_INL_ENA_WIDTH 1 /* INL_ENA */ +#define WM8996_INR_ENA 0x0010 /* INR_ENA */ +#define WM8996_INR_ENA_MASK 0x0010 /* INR_ENA */ +#define WM8996_INR_ENA_SHIFT 4 /* INR_ENA */ +#define WM8996_INR_ENA_WIDTH 1 /* INR_ENA */ +#define WM8996_LDO2_ENA 0x0002 /* LDO2_ENA */ +#define WM8996_LDO2_ENA_MASK 0x0002 /* LDO2_ENA */ +#define WM8996_LDO2_ENA_SHIFT 1 /* LDO2_ENA */ +#define WM8996_LDO2_ENA_WIDTH 1 /* LDO2_ENA */ + +/* + * R3 (0x03) - Power Management (3) + */ +#define WM8996_DSP2RXL_ENA 0x0800 /* DSP2RXL_ENA */ +#define WM8996_DSP2RXL_ENA_MASK 0x0800 /* DSP2RXL_ENA */ +#define WM8996_DSP2RXL_ENA_SHIFT 11 /* DSP2RXL_ENA */ +#define WM8996_DSP2RXL_ENA_WIDTH 1 /* DSP2RXL_ENA */ +#define WM8996_DSP2RXR_ENA 0x0400 /* DSP2RXR_ENA */ +#define WM8996_DSP2RXR_ENA_MASK 0x0400 /* DSP2RXR_ENA */ +#define WM8996_DSP2RXR_ENA_SHIFT 10 /* DSP2RXR_ENA */ +#define WM8996_DSP2RXR_ENA_WIDTH 1 /* DSP2RXR_ENA */ +#define WM8996_DSP1RXL_ENA 0x0200 /* DSP1RXL_ENA */ +#define WM8996_DSP1RXL_ENA_MASK 0x0200 /* DSP1RXL_ENA */ +#define WM8996_DSP1RXL_ENA_SHIFT 9 /* DSP1RXL_ENA */ +#define WM8996_DSP1RXL_ENA_WIDTH 1 /* DSP1RXL_ENA */ +#define WM8996_DSP1RXR_ENA 0x0100 /* DSP1RXR_ENA */ +#define WM8996_DSP1RXR_ENA_MASK 0x0100 /* DSP1RXR_ENA */ +#define WM8996_DSP1RXR_ENA_SHIFT 8 /* DSP1RXR_ENA */ +#define WM8996_DSP1RXR_ENA_WIDTH 1 /* DSP1RXR_ENA */ +#define WM8996_DMIC2L_ENA 0x0020 /* DMIC2L_ENA */ +#define WM8996_DMIC2L_ENA_MASK 0x0020 /* DMIC2L_ENA */ +#define WM8996_DMIC2L_ENA_SHIFT 5 /* DMIC2L_ENA */ +#define WM8996_DMIC2L_ENA_WIDTH 1 /* DMIC2L_ENA */ +#define WM8996_DMIC2R_ENA 0x0010 /* DMIC2R_ENA */ +#define WM8996_DMIC2R_ENA_MASK 0x0010 /* DMIC2R_ENA */ +#define WM8996_DMIC2R_ENA_SHIFT 4 /* DMIC2R_ENA */ +#define WM8996_DMIC2R_ENA_WIDTH 1 /* DMIC2R_ENA */ +#define WM8996_DMIC1L_ENA 0x0008 /* DMIC1L_ENA */ +#define WM8996_DMIC1L_ENA_MASK 0x0008 /* DMIC1L_ENA */ +#define WM8996_DMIC1L_ENA_SHIFT 3 /* DMIC1L_ENA */ +#define WM8996_DMIC1L_ENA_WIDTH 1 /* DMIC1L_ENA */ +#define WM8996_DMIC1R_ENA 0x0004 /* DMIC1R_ENA */ +#define WM8996_DMIC1R_ENA_MASK 0x0004 /* DMIC1R_ENA */ +#define WM8996_DMIC1R_ENA_SHIFT 2 /* DMIC1R_ENA */ +#define WM8996_DMIC1R_ENA_WIDTH 1 /* DMIC1R_ENA */ +#define WM8996_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8996_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */ +#define WM8996_ADCL_ENA_SHIFT 1 /* ADCL_ENA */ +#define WM8996_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ +#define WM8996_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8996_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */ +#define WM8996_ADCR_ENA_SHIFT 0 /* ADCR_ENA */ +#define WM8996_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ + +/* + * R4 (0x04) - Power Management (4) + */ +#define WM8996_AIF2RX_CHAN1_ENA 0x0200 /* AIF2RX_CHAN1_ENA */ +#define WM8996_AIF2RX_CHAN1_ENA_MASK 0x0200 /* AIF2RX_CHAN1_ENA */ +#define WM8996_AIF2RX_CHAN1_ENA_SHIFT 9 /* AIF2RX_CHAN1_ENA */ +#define WM8996_AIF2RX_CHAN1_ENA_WIDTH 1 /* AIF2RX_CHAN1_ENA */ +#define WM8996_AIF2RX_CHAN0_ENA 0x0100 /* AIF2RX_CHAN0_ENA */ +#define WM8996_AIF2RX_CHAN0_ENA_MASK 0x0100 /* AIF2RX_CHAN0_ENA */ +#define WM8996_AIF2RX_CHAN0_ENA_SHIFT 8 /* AIF2RX_CHAN0_ENA */ +#define WM8996_AIF2RX_CHAN0_ENA_WIDTH 1 /* AIF2RX_CHAN0_ENA */ +#define WM8996_AIF1RX_CHAN5_ENA 0x0020 /* AIF1RX_CHAN5_ENA */ +#define WM8996_AIF1RX_CHAN5_ENA_MASK 0x0020 /* AIF1RX_CHAN5_ENA */ +#define WM8996_AIF1RX_CHAN5_ENA_SHIFT 5 /* AIF1RX_CHAN5_ENA */ +#define WM8996_AIF1RX_CHAN5_ENA_WIDTH 1 /* AIF1RX_CHAN5_ENA */ +#define WM8996_AIF1RX_CHAN4_ENA 0x0010 /* AIF1RX_CHAN4_ENA */ +#define WM8996_AIF1RX_CHAN4_ENA_MASK 0x0010 /* AIF1RX_CHAN4_ENA */ +#define WM8996_AIF1RX_CHAN4_ENA_SHIFT 4 /* AIF1RX_CHAN4_ENA */ +#define WM8996_AIF1RX_CHAN4_ENA_WIDTH 1 /* AIF1RX_CHAN4_ENA */ +#define WM8996_AIF1RX_CHAN3_ENA 0x0008 /* AIF1RX_CHAN3_ENA */ +#define WM8996_AIF1RX_CHAN3_ENA_MASK 0x0008 /* AIF1RX_CHAN3_ENA */ +#define WM8996_AIF1RX_CHAN3_ENA_SHIFT 3 /* AIF1RX_CHAN3_ENA */ +#define WM8996_AIF1RX_CHAN3_ENA_WIDTH 1 /* AIF1RX_CHAN3_ENA */ +#define WM8996_AIF1RX_CHAN2_ENA 0x0004 /* AIF1RX_CHAN2_ENA */ +#define WM8996_AIF1RX_CHAN2_ENA_MASK 0x0004 /* AIF1RX_CHAN2_ENA */ +#define WM8996_AIF1RX_CHAN2_ENA_SHIFT 2 /* AIF1RX_CHAN2_ENA */ +#define WM8996_AIF1RX_CHAN2_ENA_WIDTH 1 /* AIF1RX_CHAN2_ENA */ +#define WM8996_AIF1RX_CHAN1_ENA 0x0002 /* AIF1RX_CHAN1_ENA */ +#define WM8996_AIF1RX_CHAN1_ENA_MASK 0x0002 /* AIF1RX_CHAN1_ENA */ +#define WM8996_AIF1RX_CHAN1_ENA_SHIFT 1 /* AIF1RX_CHAN1_ENA */ +#define WM8996_AIF1RX_CHAN1_ENA_WIDTH 1 /* AIF1RX_CHAN1_ENA */ +#define WM8996_AIF1RX_CHAN0_ENA 0x0001 /* AIF1RX_CHAN0_ENA */ +#define WM8996_AIF1RX_CHAN0_ENA_MASK 0x0001 /* AIF1RX_CHAN0_ENA */ +#define WM8996_AIF1RX_CHAN0_ENA_SHIFT 0 /* AIF1RX_CHAN0_ENA */ +#define WM8996_AIF1RX_CHAN0_ENA_WIDTH 1 /* AIF1RX_CHAN0_ENA */ + +/* + * R5 (0x05) - Power Management (5) + */ +#define WM8996_DSP2TXL_ENA 0x0800 /* DSP2TXL_ENA */ +#define WM8996_DSP2TXL_ENA_MASK 0x0800 /* DSP2TXL_ENA */ +#define WM8996_DSP2TXL_ENA_SHIFT 11 /* DSP2TXL_ENA */ +#define WM8996_DSP2TXL_ENA_WIDTH 1 /* DSP2TXL_ENA */ +#define WM8996_DSP2TXR_ENA 0x0400 /* DSP2TXR_ENA */ +#define WM8996_DSP2TXR_ENA_MASK 0x0400 /* DSP2TXR_ENA */ +#define WM8996_DSP2TXR_ENA_SHIFT 10 /* DSP2TXR_ENA */ +#define WM8996_DSP2TXR_ENA_WIDTH 1 /* DSP2TXR_ENA */ +#define WM8996_DSP1TXL_ENA 0x0200 /* DSP1TXL_ENA */ +#define WM8996_DSP1TXL_ENA_MASK 0x0200 /* DSP1TXL_ENA */ +#define WM8996_DSP1TXL_ENA_SHIFT 9 /* DSP1TXL_ENA */ +#define WM8996_DSP1TXL_ENA_WIDTH 1 /* DSP1TXL_ENA */ +#define WM8996_DSP1TXR_ENA 0x0100 /* DSP1TXR_ENA */ +#define WM8996_DSP1TXR_ENA_MASK 0x0100 /* DSP1TXR_ENA */ +#define WM8996_DSP1TXR_ENA_SHIFT 8 /* DSP1TXR_ENA */ +#define WM8996_DSP1TXR_ENA_WIDTH 1 /* DSP1TXR_ENA */ +#define WM8996_DAC2L_ENA 0x0008 /* DAC2L_ENA */ +#define WM8996_DAC2L_ENA_MASK 0x0008 /* DAC2L_ENA */ +#define WM8996_DAC2L_ENA_SHIFT 3 /* DAC2L_ENA */ +#define WM8996_DAC2L_ENA_WIDTH 1 /* DAC2L_ENA */ +#define WM8996_DAC2R_ENA 0x0004 /* DAC2R_ENA */ +#define WM8996_DAC2R_ENA_MASK 0x0004 /* DAC2R_ENA */ +#define WM8996_DAC2R_ENA_SHIFT 2 /* DAC2R_ENA */ +#define WM8996_DAC2R_ENA_WIDTH 1 /* DAC2R_ENA */ +#define WM8996_DAC1L_ENA 0x0002 /* DAC1L_ENA */ +#define WM8996_DAC1L_ENA_MASK 0x0002 /* DAC1L_ENA */ +#define WM8996_DAC1L_ENA_SHIFT 1 /* DAC1L_ENA */ +#define WM8996_DAC1L_ENA_WIDTH 1 /* DAC1L_ENA */ +#define WM8996_DAC1R_ENA 0x0001 /* DAC1R_ENA */ +#define WM8996_DAC1R_ENA_MASK 0x0001 /* DAC1R_ENA */ +#define WM8996_DAC1R_ENA_SHIFT 0 /* DAC1R_ENA */ +#define WM8996_DAC1R_ENA_WIDTH 1 /* DAC1R_ENA */ + +/* + * R6 (0x06) - Power Management (6) + */ +#define WM8996_AIF2TX_CHAN1_ENA 0x0200 /* AIF2TX_CHAN1_ENA */ +#define WM8996_AIF2TX_CHAN1_ENA_MASK 0x0200 /* AIF2TX_CHAN1_ENA */ +#define WM8996_AIF2TX_CHAN1_ENA_SHIFT 9 /* AIF2TX_CHAN1_ENA */ +#define WM8996_AIF2TX_CHAN1_ENA_WIDTH 1 /* AIF2TX_CHAN1_ENA */ +#define WM8996_AIF2TX_CHAN0_ENA 0x0100 /* AIF2TX_CHAN0_ENA */ +#define WM8996_AIF2TX_CHAN0_ENA_MASK 0x0100 /* AIF2TX_CHAN0_ENA */ +#define WM8996_AIF2TX_CHAN0_ENA_SHIFT 8 /* AIF2TX_CHAN0_ENA */ +#define WM8996_AIF2TX_CHAN0_ENA_WIDTH 1 /* AIF2TX_CHAN0_ENA */ +#define WM8996_AIF1TX_CHAN5_ENA 0x0020 /* AIF1TX_CHAN5_ENA */ +#define WM8996_AIF1TX_CHAN5_ENA_MASK 0x0020 /* AIF1TX_CHAN5_ENA */ +#define WM8996_AIF1TX_CHAN5_ENA_SHIFT 5 /* AIF1TX_CHAN5_ENA */ +#define WM8996_AIF1TX_CHAN5_ENA_WIDTH 1 /* AIF1TX_CHAN5_ENA */ +#define WM8996_AIF1TX_CHAN4_ENA 0x0010 /* AIF1TX_CHAN4_ENA */ +#define WM8996_AIF1TX_CHAN4_ENA_MASK 0x0010 /* AIF1TX_CHAN4_ENA */ +#define WM8996_AIF1TX_CHAN4_ENA_SHIFT 4 /* AIF1TX_CHAN4_ENA */ +#define WM8996_AIF1TX_CHAN4_ENA_WIDTH 1 /* AIF1TX_CHAN4_ENA */ +#define WM8996_AIF1TX_CHAN3_ENA 0x0008 /* AIF1TX_CHAN3_ENA */ +#define WM8996_AIF1TX_CHAN3_ENA_MASK 0x0008 /* AIF1TX_CHAN3_ENA */ +#define WM8996_AIF1TX_CHAN3_ENA_SHIFT 3 /* AIF1TX_CHAN3_ENA */ +#define WM8996_AIF1TX_CHAN3_ENA_WIDTH 1 /* AIF1TX_CHAN3_ENA */ +#define WM8996_AIF1TX_CHAN2_ENA 0x0004 /* AIF1TX_CHAN2_ENA */ +#define WM8996_AIF1TX_CHAN2_ENA_MASK 0x0004 /* AIF1TX_CHAN2_ENA */ +#define WM8996_AIF1TX_CHAN2_ENA_SHIFT 2 /* AIF1TX_CHAN2_ENA */ +#define WM8996_AIF1TX_CHAN2_ENA_WIDTH 1 /* AIF1TX_CHAN2_ENA */ +#define WM8996_AIF1TX_CHAN1_ENA 0x0002 /* AIF1TX_CHAN1_ENA */ +#define WM8996_AIF1TX_CHAN1_ENA_MASK 0x0002 /* AIF1TX_CHAN1_ENA */ +#define WM8996_AIF1TX_CHAN1_ENA_SHIFT 1 /* AIF1TX_CHAN1_ENA */ +#define WM8996_AIF1TX_CHAN1_ENA_WIDTH 1 /* AIF1TX_CHAN1_ENA */ +#define WM8996_AIF1TX_CHAN0_ENA 0x0001 /* AIF1TX_CHAN0_ENA */ +#define WM8996_AIF1TX_CHAN0_ENA_MASK 0x0001 /* AIF1TX_CHAN0_ENA */ +#define WM8996_AIF1TX_CHAN0_ENA_SHIFT 0 /* AIF1TX_CHAN0_ENA */ +#define WM8996_AIF1TX_CHAN0_ENA_WIDTH 1 /* AIF1TX_CHAN0_ENA */ + +/* + * R7 (0x07) - Power Management (7) + */ +#define WM8996_DMIC2_FN 0x0200 /* DMIC2_FN */ +#define WM8996_DMIC2_FN_MASK 0x0200 /* DMIC2_FN */ +#define WM8996_DMIC2_FN_SHIFT 9 /* DMIC2_FN */ +#define WM8996_DMIC2_FN_WIDTH 1 /* DMIC2_FN */ +#define WM8996_DMIC1_FN 0x0100 /* DMIC1_FN */ +#define WM8996_DMIC1_FN_MASK 0x0100 /* DMIC1_FN */ +#define WM8996_DMIC1_FN_SHIFT 8 /* DMIC1_FN */ +#define WM8996_DMIC1_FN_WIDTH 1 /* DMIC1_FN */ +#define WM8996_ADC_DMIC_DSP2R_ENA 0x0080 /* ADC_DMIC_DSP2R_ENA */ +#define WM8996_ADC_DMIC_DSP2R_ENA_MASK 0x0080 /* ADC_DMIC_DSP2R_ENA */ +#define WM8996_ADC_DMIC_DSP2R_ENA_SHIFT 7 /* ADC_DMIC_DSP2R_ENA */ +#define WM8996_ADC_DMIC_DSP2R_ENA_WIDTH 1 /* ADC_DMIC_DSP2R_ENA */ +#define WM8996_ADC_DMIC_DSP2L_ENA 0x0040 /* ADC_DMIC_DSP2L_ENA */ +#define WM8996_ADC_DMIC_DSP2L_ENA_MASK 0x0040 /* ADC_DMIC_DSP2L_ENA */ +#define WM8996_ADC_DMIC_DSP2L_ENA_SHIFT 6 /* ADC_DMIC_DSP2L_ENA */ +#define WM8996_ADC_DMIC_DSP2L_ENA_WIDTH 1 /* ADC_DMIC_DSP2L_ENA */ +#define WM8996_ADC_DMIC_SRC2_MASK 0x0030 /* ADC_DMIC_SRC2 - [5:4] */ +#define WM8996_ADC_DMIC_SRC2_SHIFT 4 /* ADC_DMIC_SRC2 - [5:4] */ +#define WM8996_ADC_DMIC_SRC2_WIDTH 2 /* ADC_DMIC_SRC2 - [5:4] */ +#define WM8996_ADC_DMIC_DSP1R_ENA 0x0008 /* ADC_DMIC_DSP1R_ENA */ +#define WM8996_ADC_DMIC_DSP1R_ENA_MASK 0x0008 /* ADC_DMIC_DSP1R_ENA */ +#define WM8996_ADC_DMIC_DSP1R_ENA_SHIFT 3 /* ADC_DMIC_DSP1R_ENA */ +#define WM8996_ADC_DMIC_DSP1R_ENA_WIDTH 1 /* ADC_DMIC_DSP1R_ENA */ +#define WM8996_ADC_DMIC_DSP1L_ENA 0x0004 /* ADC_DMIC_DSP1L_ENA */ +#define WM8996_ADC_DMIC_DSP1L_ENA_MASK 0x0004 /* ADC_DMIC_DSP1L_ENA */ +#define WM8996_ADC_DMIC_DSP1L_ENA_SHIFT 2 /* ADC_DMIC_DSP1L_ENA */ +#define WM8996_ADC_DMIC_DSP1L_ENA_WIDTH 1 /* ADC_DMIC_DSP1L_ENA */ +#define WM8996_ADC_DMIC_SRC1_MASK 0x0003 /* ADC_DMIC_SRC1 - [1:0] */ +#define WM8996_ADC_DMIC_SRC1_SHIFT 0 /* ADC_DMIC_SRC1 - [1:0] */ +#define WM8996_ADC_DMIC_SRC1_WIDTH 2 /* ADC_DMIC_SRC1 - [1:0] */ + +/* + * R8 (0x08) - Power Management (8) + */ +#define WM8996_AIF2TX_SRC_MASK 0x00C0 /* AIF2TX_SRC - [7:6] */ +#define WM8996_AIF2TX_SRC_SHIFT 6 /* AIF2TX_SRC - [7:6] */ +#define WM8996_AIF2TX_SRC_WIDTH 2 /* AIF2TX_SRC - [7:6] */ +#define WM8996_DSP2RX_SRC 0x0010 /* DSP2RX_SRC */ +#define WM8996_DSP2RX_SRC_MASK 0x0010 /* DSP2RX_SRC */ +#define WM8996_DSP2RX_SRC_SHIFT 4 /* DSP2RX_SRC */ +#define WM8996_DSP2RX_SRC_WIDTH 1 /* DSP2RX_SRC */ +#define WM8996_DSP1RX_SRC 0x0001 /* DSP1RX_SRC */ +#define WM8996_DSP1RX_SRC_MASK 0x0001 /* DSP1RX_SRC */ +#define WM8996_DSP1RX_SRC_SHIFT 0 /* DSP1RX_SRC */ +#define WM8996_DSP1RX_SRC_WIDTH 1 /* DSP1RX_SRC */ + +/* + * R16 (0x10) - Left Line Input Volume + */ +#define WM8996_IN1_VU 0x0080 /* IN1_VU */ +#define WM8996_IN1_VU_MASK 0x0080 /* IN1_VU */ +#define WM8996_IN1_VU_SHIFT 7 /* IN1_VU */ +#define WM8996_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM8996_IN1L_ZC 0x0020 /* IN1L_ZC */ +#define WM8996_IN1L_ZC_MASK 0x0020 /* IN1L_ZC */ +#define WM8996_IN1L_ZC_SHIFT 5 /* IN1L_ZC */ +#define WM8996_IN1L_ZC_WIDTH 1 /* IN1L_ZC */ +#define WM8996_IN1L_VOL_MASK 0x001F /* IN1L_VOL - [4:0] */ +#define WM8996_IN1L_VOL_SHIFT 0 /* IN1L_VOL - [4:0] */ +#define WM8996_IN1L_VOL_WIDTH 5 /* IN1L_VOL - [4:0] */ + +/* + * R17 (0x11) - Right Line Input Volume + */ +#define WM8996_IN1_VU 0x0080 /* IN1_VU */ +#define WM8996_IN1_VU_MASK 0x0080 /* IN1_VU */ +#define WM8996_IN1_VU_SHIFT 7 /* IN1_VU */ +#define WM8996_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM8996_IN1R_ZC 0x0020 /* IN1R_ZC */ +#define WM8996_IN1R_ZC_MASK 0x0020 /* IN1R_ZC */ +#define WM8996_IN1R_ZC_SHIFT 5 /* IN1R_ZC */ +#define WM8996_IN1R_ZC_WIDTH 1 /* IN1R_ZC */ +#define WM8996_IN1R_VOL_MASK 0x001F /* IN1R_VOL - [4:0] */ +#define WM8996_IN1R_VOL_SHIFT 0 /* IN1R_VOL - [4:0] */ +#define WM8996_IN1R_VOL_WIDTH 5 /* IN1R_VOL - [4:0] */ + +/* + * R18 (0x12) - Line Input Control + */ +#define WM8996_INL_MODE_MASK 0x000C /* INL_MODE - [3:2] */ +#define WM8996_INL_MODE_SHIFT 2 /* INL_MODE - [3:2] */ +#define WM8996_INL_MODE_WIDTH 2 /* INL_MODE - [3:2] */ +#define WM8996_INR_MODE_MASK 0x0003 /* INR_MODE - [1:0] */ +#define WM8996_INR_MODE_SHIFT 0 /* INR_MODE - [1:0] */ +#define WM8996_INR_MODE_WIDTH 2 /* INR_MODE - [1:0] */ + +/* + * R21 (0x15) - DAC1 HPOUT1 Volume + */ +#define WM8996_DAC1R_HPOUT1R_VOL_MASK 0x00F0 /* DAC1R_HPOUT1R_VOL - [7:4] */ +#define WM8996_DAC1R_HPOUT1R_VOL_SHIFT 4 /* DAC1R_HPOUT1R_VOL - [7:4] */ +#define WM8996_DAC1R_HPOUT1R_VOL_WIDTH 4 /* DAC1R_HPOUT1R_VOL - [7:4] */ +#define WM8996_DAC1L_HPOUT1L_VOL_MASK 0x000F /* DAC1L_HPOUT1L_VOL - [3:0] */ +#define WM8996_DAC1L_HPOUT1L_VOL_SHIFT 0 /* DAC1L_HPOUT1L_VOL - [3:0] */ +#define WM8996_DAC1L_HPOUT1L_VOL_WIDTH 4 /* DAC1L_HPOUT1L_VOL - [3:0] */ + +/* + * R22 (0x16) - DAC2 HPOUT2 Volume + */ +#define WM8996_DAC2R_HPOUT2R_VOL_MASK 0x00F0 /* DAC2R_HPOUT2R_VOL - [7:4] */ +#define WM8996_DAC2R_HPOUT2R_VOL_SHIFT 4 /* DAC2R_HPOUT2R_VOL - [7:4] */ +#define WM8996_DAC2R_HPOUT2R_VOL_WIDTH 4 /* DAC2R_HPOUT2R_VOL - [7:4] */ +#define WM8996_DAC2L_HPOUT2L_VOL_MASK 0x000F /* DAC2L_HPOUT2L_VOL - [3:0] */ +#define WM8996_DAC2L_HPOUT2L_VOL_SHIFT 0 /* DAC2L_HPOUT2L_VOL - [3:0] */ +#define WM8996_DAC2L_HPOUT2L_VOL_WIDTH 4 /* DAC2L_HPOUT2L_VOL - [3:0] */ + +/* + * R24 (0x18) - DAC1 Left Volume + */ +#define WM8996_DAC1L_MUTE 0x0200 /* DAC1L_MUTE */ +#define WM8996_DAC1L_MUTE_MASK 0x0200 /* DAC1L_MUTE */ +#define WM8996_DAC1L_MUTE_SHIFT 9 /* DAC1L_MUTE */ +#define WM8996_DAC1L_MUTE_WIDTH 1 /* DAC1L_MUTE */ +#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */ +#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */ +#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */ +#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */ +#define WM8996_DAC1L_VOL_MASK 0x00FF /* DAC1L_VOL - [7:0] */ +#define WM8996_DAC1L_VOL_SHIFT 0 /* DAC1L_VOL - [7:0] */ +#define WM8996_DAC1L_VOL_WIDTH 8 /* DAC1L_VOL - [7:0] */ + +/* + * R25 (0x19) - DAC1 Right Volume + */ +#define WM8996_DAC1R_MUTE 0x0200 /* DAC1R_MUTE */ +#define WM8996_DAC1R_MUTE_MASK 0x0200 /* DAC1R_MUTE */ +#define WM8996_DAC1R_MUTE_SHIFT 9 /* DAC1R_MUTE */ +#define WM8996_DAC1R_MUTE_WIDTH 1 /* DAC1R_MUTE */ +#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */ +#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */ +#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */ +#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */ +#define WM8996_DAC1R_VOL_MASK 0x00FF /* DAC1R_VOL - [7:0] */ +#define WM8996_DAC1R_VOL_SHIFT 0 /* DAC1R_VOL - [7:0] */ +#define WM8996_DAC1R_VOL_WIDTH 8 /* DAC1R_VOL - [7:0] */ + +/* + * R26 (0x1A) - DAC2 Left Volume + */ +#define WM8996_DAC2L_MUTE 0x0200 /* DAC2L_MUTE */ +#define WM8996_DAC2L_MUTE_MASK 0x0200 /* DAC2L_MUTE */ +#define WM8996_DAC2L_MUTE_SHIFT 9 /* DAC2L_MUTE */ +#define WM8996_DAC2L_MUTE_WIDTH 1 /* DAC2L_MUTE */ +#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */ +#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */ +#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */ +#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */ +#define WM8996_DAC2L_VOL_MASK 0x00FF /* DAC2L_VOL - [7:0] */ +#define WM8996_DAC2L_VOL_SHIFT 0 /* DAC2L_VOL - [7:0] */ +#define WM8996_DAC2L_VOL_WIDTH 8 /* DAC2L_VOL - [7:0] */ + +/* + * R27 (0x1B) - DAC2 Right Volume + */ +#define WM8996_DAC2R_MUTE 0x0200 /* DAC2R_MUTE */ +#define WM8996_DAC2R_MUTE_MASK 0x0200 /* DAC2R_MUTE */ +#define WM8996_DAC2R_MUTE_SHIFT 9 /* DAC2R_MUTE */ +#define WM8996_DAC2R_MUTE_WIDTH 1 /* DAC2R_MUTE */ +#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */ +#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */ +#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */ +#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */ +#define WM8996_DAC2R_VOL_MASK 0x00FF /* DAC2R_VOL - [7:0] */ +#define WM8996_DAC2R_VOL_SHIFT 0 /* DAC2R_VOL - [7:0] */ +#define WM8996_DAC2R_VOL_WIDTH 8 /* DAC2R_VOL - [7:0] */ + +/* + * R28 (0x1C) - Output1 Left Volume + */ +#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */ +#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */ +#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */ +#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */ +#define WM8996_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */ +#define WM8996_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */ +#define WM8996_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */ +#define WM8996_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */ +#define WM8996_HPOUT1L_VOL_MASK 0x000F /* HPOUT1L_VOL - [3:0] */ +#define WM8996_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [3:0] */ +#define WM8996_HPOUT1L_VOL_WIDTH 4 /* HPOUT1L_VOL - [3:0] */ + +/* + * R29 (0x1D) - Output1 Right Volume + */ +#define WM8996_DAC1_VU 0x0100 /* DAC1_VU */ +#define WM8996_DAC1_VU_MASK 0x0100 /* DAC1_VU */ +#define WM8996_DAC1_VU_SHIFT 8 /* DAC1_VU */ +#define WM8996_DAC1_VU_WIDTH 1 /* DAC1_VU */ +#define WM8996_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */ +#define WM8996_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */ +#define WM8996_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */ +#define WM8996_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */ +#define WM8996_HPOUT1R_VOL_MASK 0x000F /* HPOUT1R_VOL - [3:0] */ +#define WM8996_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [3:0] */ +#define WM8996_HPOUT1R_VOL_WIDTH 4 /* HPOUT1R_VOL - [3:0] */ + +/* + * R30 (0x1E) - Output2 Left Volume + */ +#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */ +#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */ +#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */ +#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */ +#define WM8996_HPOUT2L_ZC 0x0080 /* HPOUT2L_ZC */ +#define WM8996_HPOUT2L_ZC_MASK 0x0080 /* HPOUT2L_ZC */ +#define WM8996_HPOUT2L_ZC_SHIFT 7 /* HPOUT2L_ZC */ +#define WM8996_HPOUT2L_ZC_WIDTH 1 /* HPOUT2L_ZC */ +#define WM8996_HPOUT2L_VOL_MASK 0x000F /* HPOUT2L_VOL - [3:0] */ +#define WM8996_HPOUT2L_VOL_SHIFT 0 /* HPOUT2L_VOL - [3:0] */ +#define WM8996_HPOUT2L_VOL_WIDTH 4 /* HPOUT2L_VOL - [3:0] */ + +/* + * R31 (0x1F) - Output2 Right Volume + */ +#define WM8996_DAC2_VU 0x0100 /* DAC2_VU */ +#define WM8996_DAC2_VU_MASK 0x0100 /* DAC2_VU */ +#define WM8996_DAC2_VU_SHIFT 8 /* DAC2_VU */ +#define WM8996_DAC2_VU_WIDTH 1 /* DAC2_VU */ +#define WM8996_HPOUT2R_ZC 0x0080 /* HPOUT2R_ZC */ +#define WM8996_HPOUT2R_ZC_MASK 0x0080 /* HPOUT2R_ZC */ +#define WM8996_HPOUT2R_ZC_SHIFT 7 /* HPOUT2R_ZC */ +#define WM8996_HPOUT2R_ZC_WIDTH 1 /* HPOUT2R_ZC */ +#define WM8996_HPOUT2R_VOL_MASK 0x000F /* HPOUT2R_VOL - [3:0] */ +#define WM8996_HPOUT2R_VOL_SHIFT 0 /* HPOUT2R_VOL - [3:0] */ +#define WM8996_HPOUT2R_VOL_WIDTH 4 /* HPOUT2R_VOL - [3:0] */ + +/* + * R32 (0x20) - MICBIAS (1) + */ +#define WM8996_MICB1_RATE 0x0020 /* MICB1_RATE */ +#define WM8996_MICB1_RATE_MASK 0x0020 /* MICB1_RATE */ +#define WM8996_MICB1_RATE_SHIFT 5 /* MICB1_RATE */ +#define WM8996_MICB1_RATE_WIDTH 1 /* MICB1_RATE */ +#define WM8996_MICB1_MODE 0x0010 /* MICB1_MODE */ +#define WM8996_MICB1_MODE_MASK 0x0010 /* MICB1_MODE */ +#define WM8996_MICB1_MODE_SHIFT 4 /* MICB1_MODE */ +#define WM8996_MICB1_MODE_WIDTH 1 /* MICB1_MODE */ +#define WM8996_MICB1_LVL_MASK 0x000E /* MICB1_LVL - [3:1] */ +#define WM8996_MICB1_LVL_SHIFT 1 /* MICB1_LVL - [3:1] */ +#define WM8996_MICB1_LVL_WIDTH 3 /* MICB1_LVL - [3:1] */ +#define WM8996_MICB1_DISCH 0x0001 /* MICB1_DISCH */ +#define WM8996_MICB1_DISCH_MASK 0x0001 /* MICB1_DISCH */ +#define WM8996_MICB1_DISCH_SHIFT 0 /* MICB1_DISCH */ +#define WM8996_MICB1_DISCH_WIDTH 1 /* MICB1_DISCH */ + +/* + * R33 (0x21) - MICBIAS (2) + */ +#define WM8996_MICB2_RATE 0x0020 /* MICB2_RATE */ +#define WM8996_MICB2_RATE_MASK 0x0020 /* MICB2_RATE */ +#define WM8996_MICB2_RATE_SHIFT 5 /* MICB2_RATE */ +#define WM8996_MICB2_RATE_WIDTH 1 /* MICB2_RATE */ +#define WM8996_MICB2_MODE 0x0010 /* MICB2_MODE */ +#define WM8996_MICB2_MODE_MASK 0x0010 /* MICB2_MODE */ +#define WM8996_MICB2_MODE_SHIFT 4 /* MICB2_MODE */ +#define WM8996_MICB2_MODE_WIDTH 1 /* MICB2_MODE */ +#define WM8996_MICB2_LVL_MASK 0x000E /* MICB2_LVL - [3:1] */ +#define WM8996_MICB2_LVL_SHIFT 1 /* MICB2_LVL - [3:1] */ +#define WM8996_MICB2_LVL_WIDTH 3 /* MICB2_LVL - [3:1] */ +#define WM8996_MICB2_DISCH 0x0001 /* MICB2_DISCH */ +#define WM8996_MICB2_DISCH_MASK 0x0001 /* MICB2_DISCH */ +#define WM8996_MICB2_DISCH_SHIFT 0 /* MICB2_DISCH */ +#define WM8996_MICB2_DISCH_WIDTH 1 /* MICB2_DISCH */ + +/* + * R40 (0x28) - LDO 1 + */ +#define WM8996_LDO1_MODE 0x0020 /* LDO1_MODE */ +#define WM8996_LDO1_MODE_MASK 0x0020 /* LDO1_MODE */ +#define WM8996_LDO1_MODE_SHIFT 5 /* LDO1_MODE */ +#define WM8996_LDO1_MODE_WIDTH 1 /* LDO1_MODE */ +#define WM8996_LDO1_VSEL_MASK 0x0006 /* LDO1_VSEL - [2:1] */ +#define WM8996_LDO1_VSEL_SHIFT 1 /* LDO1_VSEL - [2:1] */ +#define WM8996_LDO1_VSEL_WIDTH 2 /* LDO1_VSEL - [2:1] */ +#define WM8996_LDO1_DISCH 0x0001 /* LDO1_DISCH */ +#define WM8996_LDO1_DISCH_MASK 0x0001 /* LDO1_DISCH */ +#define WM8996_LDO1_DISCH_SHIFT 0 /* LDO1_DISCH */ +#define WM8996_LDO1_DISCH_WIDTH 1 /* LDO1_DISCH */ + +/* + * R41 (0x29) - LDO 2 + */ +#define WM8996_LDO2_MODE 0x0020 /* LDO2_MODE */ +#define WM8996_LDO2_MODE_MASK 0x0020 /* LDO2_MODE */ +#define WM8996_LDO2_MODE_SHIFT 5 /* LDO2_MODE */ +#define WM8996_LDO2_MODE_WIDTH 1 /* LDO2_MODE */ +#define WM8996_LDO2_VSEL_MASK 0x001E /* LDO2_VSEL - [4:1] */ +#define WM8996_LDO2_VSEL_SHIFT 1 /* LDO2_VSEL - [4:1] */ +#define WM8996_LDO2_VSEL_WIDTH 4 /* LDO2_VSEL - [4:1] */ +#define WM8996_LDO2_DISCH 0x0001 /* LDO2_DISCH */ +#define WM8996_LDO2_DISCH_MASK 0x0001 /* LDO2_DISCH */ +#define WM8996_LDO2_DISCH_SHIFT 0 /* LDO2_DISCH */ +#define WM8996_LDO2_DISCH_WIDTH 1 /* LDO2_DISCH */ + +/* + * R48 (0x30) - Accessory Detect Mode 1 + */ +#define WM8996_JD_MODE_MASK 0x0003 /* JD_MODE - [1:0] */ +#define WM8996_JD_MODE_SHIFT 0 /* JD_MODE - [1:0] */ +#define WM8996_JD_MODE_WIDTH 2 /* JD_MODE - [1:0] */ + +/* + * R49 (0x31) - Accessory Detect Mode 2 + */ +#define WM8996_HPOUT1FB_SRC 0x0004 /* HPOUT1FB_SRC */ +#define WM8996_HPOUT1FB_SRC_MASK 0x0004 /* HPOUT1FB_SRC */ +#define WM8996_HPOUT1FB_SRC_SHIFT 2 /* HPOUT1FB_SRC */ +#define WM8996_HPOUT1FB_SRC_WIDTH 1 /* HPOUT1FB_SRC */ +#define WM8996_MICD_SRC 0x0002 /* MICD_SRC */ +#define WM8996_MICD_SRC_MASK 0x0002 /* MICD_SRC */ +#define WM8996_MICD_SRC_SHIFT 1 /* MICD_SRC */ +#define WM8996_MICD_SRC_WIDTH 1 /* MICD_SRC */ +#define WM8996_MICD_BIAS_SRC 0x0001 /* MICD_BIAS_SRC */ +#define WM8996_MICD_BIAS_SRC_MASK 0x0001 /* MICD_BIAS_SRC */ +#define WM8996_MICD_BIAS_SRC_SHIFT 0 /* MICD_BIAS_SRC */ +#define WM8996_MICD_BIAS_SRC_WIDTH 1 /* MICD_BIAS_SRC */ + +/* + * R52 (0x34) - Headphone Detect 1 + */ +#define WM8996_HP_HOLDTIME_MASK 0x00E0 /* HP_HOLDTIME - [7:5] */ +#define WM8996_HP_HOLDTIME_SHIFT 5 /* HP_HOLDTIME - [7:5] */ +#define WM8996_HP_HOLDTIME_WIDTH 3 /* HP_HOLDTIME - [7:5] */ +#define WM8996_HP_CLK_DIV_MASK 0x0018 /* HP_CLK_DIV - [4:3] */ +#define WM8996_HP_CLK_DIV_SHIFT 3 /* HP_CLK_DIV - [4:3] */ +#define WM8996_HP_CLK_DIV_WIDTH 2 /* HP_CLK_DIV - [4:3] */ +#define WM8996_HP_STEP_SIZE 0x0002 /* HP_STEP_SIZE */ +#define WM8996_HP_STEP_SIZE_MASK 0x0002 /* HP_STEP_SIZE */ +#define WM8996_HP_STEP_SIZE_SHIFT 1 /* HP_STEP_SIZE */ +#define WM8996_HP_STEP_SIZE_WIDTH 1 /* HP_STEP_SIZE */ +#define WM8996_HP_POLL 0x0001 /* HP_POLL */ +#define WM8996_HP_POLL_MASK 0x0001 /* HP_POLL */ +#define WM8996_HP_POLL_SHIFT 0 /* HP_POLL */ +#define WM8996_HP_POLL_WIDTH 1 /* HP_POLL */ + +/* + * R53 (0x35) - Headphone Detect 2 + */ +#define WM8996_HP_DONE 0x0080 /* HP_DONE */ +#define WM8996_HP_DONE_MASK 0x0080 /* HP_DONE */ +#define WM8996_HP_DONE_SHIFT 7 /* HP_DONE */ +#define WM8996_HP_DONE_WIDTH 1 /* HP_DONE */ +#define WM8996_HP_LVL_MASK 0x007F /* HP_LVL - [6:0] */ +#define WM8996_HP_LVL_SHIFT 0 /* HP_LVL - [6:0] */ +#define WM8996_HP_LVL_WIDTH 7 /* HP_LVL - [6:0] */ + +/* + * R56 (0x38) - Mic Detect 1 + */ +#define WM8996_MICD_BIAS_STARTTIME_MASK 0xF000 /* MICD_BIAS_STARTTIME - [15:12] */ +#define WM8996_MICD_BIAS_STARTTIME_SHIFT 12 /* MICD_BIAS_STARTTIME - [15:12] */ +#define WM8996_MICD_BIAS_STARTTIME_WIDTH 4 /* MICD_BIAS_STARTTIME - [15:12] */ +#define WM8996_MICD_RATE_MASK 0x0F00 /* MICD_RATE - [11:8] */ +#define WM8996_MICD_RATE_SHIFT 8 /* MICD_RATE - [11:8] */ +#define WM8996_MICD_RATE_WIDTH 4 /* MICD_RATE - [11:8] */ +#define WM8996_MICD_DBTIME 0x0002 /* MICD_DBTIME */ +#define WM8996_MICD_DBTIME_MASK 0x0002 /* MICD_DBTIME */ +#define WM8996_MICD_DBTIME_SHIFT 1 /* MICD_DBTIME */ +#define WM8996_MICD_DBTIME_WIDTH 1 /* MICD_DBTIME */ +#define WM8996_MICD_ENA 0x0001 /* MICD_ENA */ +#define WM8996_MICD_ENA_MASK 0x0001 /* MICD_ENA */ +#define WM8996_MICD_ENA_SHIFT 0 /* MICD_ENA */ +#define WM8996_MICD_ENA_WIDTH 1 /* MICD_ENA */ + +/* + * R57 (0x39) - Mic Detect 2 + */ +#define WM8996_MICD_LVL_SEL_MASK 0x00FF /* MICD_LVL_SEL - [7:0] */ +#define WM8996_MICD_LVL_SEL_SHIFT 0 /* MICD_LVL_SEL - [7:0] */ +#define WM8996_MICD_LVL_SEL_WIDTH 8 /* MICD_LVL_SEL - [7:0] */ + +/* + * R58 (0x3A) - Mic Detect 3 + */ +#define WM8996_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */ +#define WM8996_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */ +#define WM8996_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */ +#define WM8996_MICD_VALID 0x0002 /* MICD_VALID */ +#define WM8996_MICD_VALID_MASK 0x0002 /* MICD_VALID */ +#define WM8996_MICD_VALID_SHIFT 1 /* MICD_VALID */ +#define WM8996_MICD_VALID_WIDTH 1 /* MICD_VALID */ +#define WM8996_MICD_STS 0x0001 /* MICD_STS */ +#define WM8996_MICD_STS_MASK 0x0001 /* MICD_STS */ +#define WM8996_MICD_STS_SHIFT 0 /* MICD_STS */ +#define WM8996_MICD_STS_WIDTH 1 /* MICD_STS */ + +/* + * R64 (0x40) - Charge Pump (1) + */ +#define WM8996_CP_ENA 0x8000 /* CP_ENA */ +#define WM8996_CP_ENA_MASK 0x8000 /* CP_ENA */ +#define WM8996_CP_ENA_SHIFT 15 /* CP_ENA */ +#define WM8996_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R65 (0x41) - Charge Pump (2) + */ +#define WM8996_CP_DISCH 0x8000 /* CP_DISCH */ +#define WM8996_CP_DISCH_MASK 0x8000 /* CP_DISCH */ +#define WM8996_CP_DISCH_SHIFT 15 /* CP_DISCH */ +#define WM8996_CP_DISCH_WIDTH 1 /* CP_DISCH */ + +/* + * R80 (0x50) - DC Servo (1) + */ +#define WM8996_DCS_ENA_CHAN_3 0x0008 /* DCS_ENA_CHAN_3 */ +#define WM8996_DCS_ENA_CHAN_3_MASK 0x0008 /* DCS_ENA_CHAN_3 */ +#define WM8996_DCS_ENA_CHAN_3_SHIFT 3 /* DCS_ENA_CHAN_3 */ +#define WM8996_DCS_ENA_CHAN_3_WIDTH 1 /* DCS_ENA_CHAN_3 */ +#define WM8996_DCS_ENA_CHAN_2 0x0004 /* DCS_ENA_CHAN_2 */ +#define WM8996_DCS_ENA_CHAN_2_MASK 0x0004 /* DCS_ENA_CHAN_2 */ +#define WM8996_DCS_ENA_CHAN_2_SHIFT 2 /* DCS_ENA_CHAN_2 */ +#define WM8996_DCS_ENA_CHAN_2_WIDTH 1 /* DCS_ENA_CHAN_2 */ +#define WM8996_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8996_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM8996_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */ +#define WM8996_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */ +#define WM8996_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8996_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM8996_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */ +#define WM8996_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */ + +/* + * R81 (0x51) - DC Servo (2) + */ +#define WM8996_DCS_TRIG_SINGLE_3 0x8000 /* DCS_TRIG_SINGLE_3 */ +#define WM8996_DCS_TRIG_SINGLE_3_MASK 0x8000 /* DCS_TRIG_SINGLE_3 */ +#define WM8996_DCS_TRIG_SINGLE_3_SHIFT 15 /* DCS_TRIG_SINGLE_3 */ +#define WM8996_DCS_TRIG_SINGLE_3_WIDTH 1 /* DCS_TRIG_SINGLE_3 */ +#define WM8996_DCS_TRIG_SINGLE_2 0x4000 /* DCS_TRIG_SINGLE_2 */ +#define WM8996_DCS_TRIG_SINGLE_2_MASK 0x4000 /* DCS_TRIG_SINGLE_2 */ +#define WM8996_DCS_TRIG_SINGLE_2_SHIFT 14 /* DCS_TRIG_SINGLE_2 */ +#define WM8996_DCS_TRIG_SINGLE_2_WIDTH 1 /* DCS_TRIG_SINGLE_2 */ +#define WM8996_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8996_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM8996_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */ +#define WM8996_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */ +#define WM8996_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8996_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM8996_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */ +#define WM8996_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */ +#define WM8996_DCS_TRIG_SERIES_3 0x0800 /* DCS_TRIG_SERIES_3 */ +#define WM8996_DCS_TRIG_SERIES_3_MASK 0x0800 /* DCS_TRIG_SERIES_3 */ +#define WM8996_DCS_TRIG_SERIES_3_SHIFT 11 /* DCS_TRIG_SERIES_3 */ +#define WM8996_DCS_TRIG_SERIES_3_WIDTH 1 /* DCS_TRIG_SERIES_3 */ +#define WM8996_DCS_TRIG_SERIES_2 0x0400 /* DCS_TRIG_SERIES_2 */ +#define WM8996_DCS_TRIG_SERIES_2_MASK 0x0400 /* DCS_TRIG_SERIES_2 */ +#define WM8996_DCS_TRIG_SERIES_2_SHIFT 10 /* DCS_TRIG_SERIES_2 */ +#define WM8996_DCS_TRIG_SERIES_2_WIDTH 1 /* DCS_TRIG_SERIES_2 */ +#define WM8996_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8996_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM8996_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */ +#define WM8996_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */ +#define WM8996_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8996_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM8996_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */ +#define WM8996_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */ +#define WM8996_DCS_TRIG_STARTUP_3 0x0080 /* DCS_TRIG_STARTUP_3 */ +#define WM8996_DCS_TRIG_STARTUP_3_MASK 0x0080 /* DCS_TRIG_STARTUP_3 */ +#define WM8996_DCS_TRIG_STARTUP_3_SHIFT 7 /* DCS_TRIG_STARTUP_3 */ +#define WM8996_DCS_TRIG_STARTUP_3_WIDTH 1 /* DCS_TRIG_STARTUP_3 */ +#define WM8996_DCS_TRIG_STARTUP_2 0x0040 /* DCS_TRIG_STARTUP_2 */ +#define WM8996_DCS_TRIG_STARTUP_2_MASK 0x0040 /* DCS_TRIG_STARTUP_2 */ +#define WM8996_DCS_TRIG_STARTUP_2_SHIFT 6 /* DCS_TRIG_STARTUP_2 */ +#define WM8996_DCS_TRIG_STARTUP_2_WIDTH 1 /* DCS_TRIG_STARTUP_2 */ +#define WM8996_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8996_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM8996_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */ +#define WM8996_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */ +#define WM8996_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8996_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM8996_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */ +#define WM8996_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */ +#define WM8996_DCS_TRIG_DAC_WR_3 0x0008 /* DCS_TRIG_DAC_WR_3 */ +#define WM8996_DCS_TRIG_DAC_WR_3_MASK 0x0008 /* DCS_TRIG_DAC_WR_3 */ +#define WM8996_DCS_TRIG_DAC_WR_3_SHIFT 3 /* DCS_TRIG_DAC_WR_3 */ +#define WM8996_DCS_TRIG_DAC_WR_3_WIDTH 1 /* DCS_TRIG_DAC_WR_3 */ +#define WM8996_DCS_TRIG_DAC_WR_2 0x0004 /* DCS_TRIG_DAC_WR_2 */ +#define WM8996_DCS_TRIG_DAC_WR_2_MASK 0x0004 /* DCS_TRIG_DAC_WR_2 */ +#define WM8996_DCS_TRIG_DAC_WR_2_SHIFT 2 /* DCS_TRIG_DAC_WR_2 */ +#define WM8996_DCS_TRIG_DAC_WR_2_WIDTH 1 /* DCS_TRIG_DAC_WR_2 */ +#define WM8996_DCS_TRIG_DAC_WR_1 0x0002 /* DCS_TRIG_DAC_WR_1 */ +#define WM8996_DCS_TRIG_DAC_WR_1_MASK 0x0002 /* DCS_TRIG_DAC_WR_1 */ +#define WM8996_DCS_TRIG_DAC_WR_1_SHIFT 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM8996_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM8996_DCS_TRIG_DAC_WR_0 0x0001 /* DCS_TRIG_DAC_WR_0 */ +#define WM8996_DCS_TRIG_DAC_WR_0_MASK 0x0001 /* DCS_TRIG_DAC_WR_0 */ +#define WM8996_DCS_TRIG_DAC_WR_0_SHIFT 0 /* DCS_TRIG_DAC_WR_0 */ +#define WM8996_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */ + +/* + * R82 (0x52) - DC Servo (3) + */ +#define WM8996_DCS_TIMER_PERIOD_23_MASK 0x0F00 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8996_DCS_TIMER_PERIOD_23_SHIFT 8 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8996_DCS_TIMER_PERIOD_23_WIDTH 4 /* DCS_TIMER_PERIOD_23 - [11:8] */ +#define WM8996_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8996_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM8996_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */ + +/* + * R84 (0x54) - DC Servo (5) + */ +#define WM8996_DCS_SERIES_NO_23_MASK 0x7F00 /* DCS_SERIES_NO_23 - [14:8] */ +#define WM8996_DCS_SERIES_NO_23_SHIFT 8 /* DCS_SERIES_NO_23 - [14:8] */ +#define WM8996_DCS_SERIES_NO_23_WIDTH 7 /* DCS_SERIES_NO_23 - [14:8] */ +#define WM8996_DCS_SERIES_NO_01_MASK 0x007F /* DCS_SERIES_NO_01 - [6:0] */ +#define WM8996_DCS_SERIES_NO_01_SHIFT 0 /* DCS_SERIES_NO_01 - [6:0] */ +#define WM8996_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [6:0] */ + +/* + * R85 (0x55) - DC Servo (6) + */ +#define WM8996_DCS_DAC_WR_VAL_3_MASK 0xFF00 /* DCS_DAC_WR_VAL_3 - [15:8] */ +#define WM8996_DCS_DAC_WR_VAL_3_SHIFT 8 /* DCS_DAC_WR_VAL_3 - [15:8] */ +#define WM8996_DCS_DAC_WR_VAL_3_WIDTH 8 /* DCS_DAC_WR_VAL_3 - [15:8] */ +#define WM8996_DCS_DAC_WR_VAL_2_MASK 0x00FF /* DCS_DAC_WR_VAL_2 - [7:0] */ +#define WM8996_DCS_DAC_WR_VAL_2_SHIFT 0 /* DCS_DAC_WR_VAL_2 - [7:0] */ +#define WM8996_DCS_DAC_WR_VAL_2_WIDTH 8 /* DCS_DAC_WR_VAL_2 - [7:0] */ + +/* + * R86 (0x56) - DC Servo (7) + */ +#define WM8996_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8996_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8996_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM8996_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8996_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM8996_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */ + +/* + * R87 (0x57) - DC Servo Readback 0 + */ +#define WM8996_DCS_CAL_COMPLETE_MASK 0x0F00 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8996_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8996_DCS_CAL_COMPLETE_WIDTH 4 /* DCS_CAL_COMPLETE - [11:8] */ +#define WM8996_DCS_DAC_WR_COMPLETE_MASK 0x00F0 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8996_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8996_DCS_DAC_WR_COMPLETE_WIDTH 4 /* DCS_DAC_WR_COMPLETE - [7:4] */ +#define WM8996_DCS_STARTUP_COMPLETE_MASK 0x000F /* DCS_STARTUP_COMPLETE - [3:0] */ +#define WM8996_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [3:0] */ +#define WM8996_DCS_STARTUP_COMPLETE_WIDTH 4 /* DCS_STARTUP_COMPLETE - [3:0] */ + +/* + * R96 (0x60) - Analogue HP (1) + */ +#define WM8996_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM8996_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM8996_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */ +#define WM8996_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */ +#define WM8996_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */ +#define WM8996_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */ +#define WM8996_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */ +#define WM8996_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */ +#define WM8996_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */ +#define WM8996_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */ +#define WM8996_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */ +#define WM8996_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */ +#define WM8996_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM8996_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM8996_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */ +#define WM8996_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */ +#define WM8996_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */ +#define WM8996_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */ +#define WM8996_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */ +#define WM8996_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */ +#define WM8996_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */ +#define WM8996_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */ +#define WM8996_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */ +#define WM8996_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */ + +/* + * R97 (0x61) - Analogue HP (2) + */ +#define WM8996_HPOUT2L_RMV_SHORT 0x0080 /* HPOUT2L_RMV_SHORT */ +#define WM8996_HPOUT2L_RMV_SHORT_MASK 0x0080 /* HPOUT2L_RMV_SHORT */ +#define WM8996_HPOUT2L_RMV_SHORT_SHIFT 7 /* HPOUT2L_RMV_SHORT */ +#define WM8996_HPOUT2L_RMV_SHORT_WIDTH 1 /* HPOUT2L_RMV_SHORT */ +#define WM8996_HPOUT2L_OUTP 0x0040 /* HPOUT2L_OUTP */ +#define WM8996_HPOUT2L_OUTP_MASK 0x0040 /* HPOUT2L_OUTP */ +#define WM8996_HPOUT2L_OUTP_SHIFT 6 /* HPOUT2L_OUTP */ +#define WM8996_HPOUT2L_OUTP_WIDTH 1 /* HPOUT2L_OUTP */ +#define WM8996_HPOUT2L_DLY 0x0020 /* HPOUT2L_DLY */ +#define WM8996_HPOUT2L_DLY_MASK 0x0020 /* HPOUT2L_DLY */ +#define WM8996_HPOUT2L_DLY_SHIFT 5 /* HPOUT2L_DLY */ +#define WM8996_HPOUT2L_DLY_WIDTH 1 /* HPOUT2L_DLY */ +#define WM8996_HPOUT2R_RMV_SHORT 0x0008 /* HPOUT2R_RMV_SHORT */ +#define WM8996_HPOUT2R_RMV_SHORT_MASK 0x0008 /* HPOUT2R_RMV_SHORT */ +#define WM8996_HPOUT2R_RMV_SHORT_SHIFT 3 /* HPOUT2R_RMV_SHORT */ +#define WM8996_HPOUT2R_RMV_SHORT_WIDTH 1 /* HPOUT2R_RMV_SHORT */ +#define WM8996_HPOUT2R_OUTP 0x0004 /* HPOUT2R_OUTP */ +#define WM8996_HPOUT2R_OUTP_MASK 0x0004 /* HPOUT2R_OUTP */ +#define WM8996_HPOUT2R_OUTP_SHIFT 2 /* HPOUT2R_OUTP */ +#define WM8996_HPOUT2R_OUTP_WIDTH 1 /* HPOUT2R_OUTP */ +#define WM8996_HPOUT2R_DLY 0x0002 /* HPOUT2R_DLY */ +#define WM8996_HPOUT2R_DLY_MASK 0x0002 /* HPOUT2R_DLY */ +#define WM8996_HPOUT2R_DLY_SHIFT 1 /* HPOUT2R_DLY */ +#define WM8996_HPOUT2R_DLY_WIDTH 1 /* HPOUT2R_DLY */ + +/* + * R256 (0x100) - Chip Revision + */ +#define WM8996_CHIP_REV_MASK 0x000F /* CHIP_REV - [3:0] */ +#define WM8996_CHIP_REV_SHIFT 0 /* CHIP_REV - [3:0] */ +#define WM8996_CHIP_REV_WIDTH 4 /* CHIP_REV - [3:0] */ + +/* + * R257 (0x101) - Control Interface (1) + */ +#define WM8996_REG_SYNC 0x8000 /* REG_SYNC */ +#define WM8996_REG_SYNC_MASK 0x8000 /* REG_SYNC */ +#define WM8996_REG_SYNC_SHIFT 15 /* REG_SYNC */ +#define WM8996_REG_SYNC_WIDTH 1 /* REG_SYNC */ +#define WM8996_AUTO_INC 0x0004 /* AUTO_INC */ +#define WM8996_AUTO_INC_MASK 0x0004 /* AUTO_INC */ +#define WM8996_AUTO_INC_SHIFT 2 /* AUTO_INC */ +#define WM8996_AUTO_INC_WIDTH 1 /* AUTO_INC */ + +/* + * R272 (0x110) - Write Sequencer Ctrl (1) + */ +#define WM8996_WSEQ_ENA 0x8000 /* WSEQ_ENA */ +#define WM8996_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */ +#define WM8996_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */ +#define WM8996_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM8996_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */ +#define WM8996_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */ +#define WM8996_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */ +#define WM8996_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM8996_WSEQ_START 0x0100 /* WSEQ_START */ +#define WM8996_WSEQ_START_MASK 0x0100 /* WSEQ_START */ +#define WM8996_WSEQ_START_SHIFT 8 /* WSEQ_START */ +#define WM8996_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM8996_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */ +#define WM8996_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */ +#define WM8996_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */ + +/* + * R273 (0x111) - Write Sequencer Ctrl (2) + */ +#define WM8996_WSEQ_BUSY 0x0100 /* WSEQ_BUSY */ +#define WM8996_WSEQ_BUSY_MASK 0x0100 /* WSEQ_BUSY */ +#define WM8996_WSEQ_BUSY_SHIFT 8 /* WSEQ_BUSY */ +#define WM8996_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ +#define WM8996_WSEQ_CURRENT_INDEX_MASK 0x007F /* WSEQ_CURRENT_INDEX - [6:0] */ +#define WM8996_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [6:0] */ +#define WM8996_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [6:0] */ + +/* + * R512 (0x200) - AIF Clocking (1) + */ +#define WM8996_SYSCLK_SRC_MASK 0x0018 /* SYSCLK_SRC - [4:3] */ +#define WM8996_SYSCLK_SRC_SHIFT 3 /* SYSCLK_SRC - [4:3] */ +#define WM8996_SYSCLK_SRC_WIDTH 2 /* SYSCLK_SRC - [4:3] */ +#define WM8996_SYSCLK_INV 0x0004 /* SYSCLK_INV */ +#define WM8996_SYSCLK_INV_MASK 0x0004 /* SYSCLK_INV */ +#define WM8996_SYSCLK_INV_SHIFT 2 /* SYSCLK_INV */ +#define WM8996_SYSCLK_INV_WIDTH 1 /* SYSCLK_INV */ +#define WM8996_SYSCLK_DIV 0x0002 /* SYSCLK_DIV */ +#define WM8996_SYSCLK_DIV_MASK 0x0002 /* SYSCLK_DIV */ +#define WM8996_SYSCLK_DIV_SHIFT 1 /* SYSCLK_DIV */ +#define WM8996_SYSCLK_DIV_WIDTH 1 /* SYSCLK_DIV */ +#define WM8996_SYSCLK_ENA 0x0001 /* SYSCLK_ENA */ +#define WM8996_SYSCLK_ENA_MASK 0x0001 /* SYSCLK_ENA */ +#define WM8996_SYSCLK_ENA_SHIFT 0 /* SYSCLK_ENA */ +#define WM8996_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */ + +/* + * R513 (0x201) - AIF Clocking (2) + */ +#define WM8996_DSP2_DIV_MASK 0x0018 /* DSP2_DIV - [4:3] */ +#define WM8996_DSP2_DIV_SHIFT 3 /* DSP2_DIV - [4:3] */ +#define WM8996_DSP2_DIV_WIDTH 2 /* DSP2_DIV - [4:3] */ +#define WM8996_DSP1_DIV_MASK 0x0003 /* DSP1_DIV - [1:0] */ +#define WM8996_DSP1_DIV_SHIFT 0 /* DSP1_DIV - [1:0] */ +#define WM8996_DSP1_DIV_WIDTH 2 /* DSP1_DIV - [1:0] */ + +/* + * R520 (0x208) - Clocking (1) + */ +#define WM8996_LFCLK_ENA 0x0020 /* LFCLK_ENA */ +#define WM8996_LFCLK_ENA_MASK 0x0020 /* LFCLK_ENA */ +#define WM8996_LFCLK_ENA_SHIFT 5 /* LFCLK_ENA */ +#define WM8996_LFCLK_ENA_WIDTH 1 /* LFCLK_ENA */ +#define WM8996_TOCLK_ENA 0x0010 /* TOCLK_ENA */ +#define WM8996_TOCLK_ENA_MASK 0x0010 /* TOCLK_ENA */ +#define WM8996_TOCLK_ENA_SHIFT 4 /* TOCLK_ENA */ +#define WM8996_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */ +#define WM8996_AIFCLK_ENA 0x0004 /* AIFCLK_ENA */ +#define WM8996_AIFCLK_ENA_MASK 0x0004 /* AIFCLK_ENA */ +#define WM8996_AIFCLK_ENA_SHIFT 2 /* AIFCLK_ENA */ +#define WM8996_AIFCLK_ENA_WIDTH 1 /* AIFCLK_ENA */ +#define WM8996_SYSDSPCLK_ENA 0x0002 /* SYSDSPCLK_ENA */ +#define WM8996_SYSDSPCLK_ENA_MASK 0x0002 /* SYSDSPCLK_ENA */ +#define WM8996_SYSDSPCLK_ENA_SHIFT 1 /* SYSDSPCLK_ENA */ +#define WM8996_SYSDSPCLK_ENA_WIDTH 1 /* SYSDSPCLK_ENA */ + +/* + * R521 (0x209) - Clocking (2) + */ +#define WM8996_TOCLK_DIV_MASK 0x0700 /* TOCLK_DIV - [10:8] */ +#define WM8996_TOCLK_DIV_SHIFT 8 /* TOCLK_DIV - [10:8] */ +#define WM8996_TOCLK_DIV_WIDTH 3 /* TOCLK_DIV - [10:8] */ +#define WM8996_DBCLK_DIV_MASK 0x00F0 /* DBCLK_DIV - [7:4] */ +#define WM8996_DBCLK_DIV_SHIFT 4 /* DBCLK_DIV - [7:4] */ +#define WM8996_DBCLK_DIV_WIDTH 4 /* DBCLK_DIV - [7:4] */ +#define WM8996_OPCLK_DIV_MASK 0x0007 /* OPCLK_DIV - [2:0] */ +#define WM8996_OPCLK_DIV_SHIFT 0 /* OPCLK_DIV - [2:0] */ +#define WM8996_OPCLK_DIV_WIDTH 3 /* OPCLK_DIV - [2:0] */ + +/* + * R528 (0x210) - AIF Rate + */ +#define WM8996_SYSCLK_RATE 0x0001 /* SYSCLK_RATE */ +#define WM8996_SYSCLK_RATE_MASK 0x0001 /* SYSCLK_RATE */ +#define WM8996_SYSCLK_RATE_SHIFT 0 /* SYSCLK_RATE */ +#define WM8996_SYSCLK_RATE_WIDTH 1 /* SYSCLK_RATE */ + +/* + * R544 (0x220) - FLL Control (1) + */ +#define WM8996_FLL_OSC_ENA 0x0002 /* FLL_OSC_ENA */ +#define WM8996_FLL_OSC_ENA_MASK 0x0002 /* FLL_OSC_ENA */ +#define WM8996_FLL_OSC_ENA_SHIFT 1 /* FLL_OSC_ENA */ +#define WM8996_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */ +#define WM8996_FLL_ENA 0x0001 /* FLL_ENA */ +#define WM8996_FLL_ENA_MASK 0x0001 /* FLL_ENA */ +#define WM8996_FLL_ENA_SHIFT 0 /* FLL_ENA */ +#define WM8996_FLL_ENA_WIDTH 1 /* FLL_ENA */ + +/* + * R545 (0x221) - FLL Control (2) + */ +#define WM8996_FLL_OUTDIV_MASK 0x3F00 /* FLL_OUTDIV - [13:8] */ +#define WM8996_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [13:8] */ +#define WM8996_FLL_OUTDIV_WIDTH 6 /* FLL_OUTDIV - [13:8] */ +#define WM8996_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */ +#define WM8996_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */ +#define WM8996_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */ + +/* + * R546 (0x222) - FLL Control (3) + */ +#define WM8996_FLL_THETA_MASK 0xFFFF /* FLL_THETA - [15:0] */ +#define WM8996_FLL_THETA_SHIFT 0 /* FLL_THETA - [15:0] */ +#define WM8996_FLL_THETA_WIDTH 16 /* FLL_THETA - [15:0] */ + +/* + * R547 (0x223) - FLL Control (4) + */ +#define WM8996_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */ +#define WM8996_FLL_N_SHIFT 5 /* FLL_N - [14:5] */ +#define WM8996_FLL_N_WIDTH 10 /* FLL_N - [14:5] */ +#define WM8996_FLL_LOOP_GAIN_MASK 0x000F /* FLL_LOOP_GAIN - [3:0] */ +#define WM8996_FLL_LOOP_GAIN_SHIFT 0 /* FLL_LOOP_GAIN - [3:0] */ +#define WM8996_FLL_LOOP_GAIN_WIDTH 4 /* FLL_LOOP_GAIN - [3:0] */ + +/* + * R548 (0x224) - FLL Control (5) + */ +#define WM8996_FLL_FRC_NCO_VAL_MASK 0x1F80 /* FLL_FRC_NCO_VAL - [12:7] */ +#define WM8996_FLL_FRC_NCO_VAL_SHIFT 7 /* FLL_FRC_NCO_VAL - [12:7] */ +#define WM8996_FLL_FRC_NCO_VAL_WIDTH 6 /* FLL_FRC_NCO_VAL - [12:7] */ +#define WM8996_FLL_FRC_NCO 0x0040 /* FLL_FRC_NCO */ +#define WM8996_FLL_FRC_NCO_MASK 0x0040 /* FLL_FRC_NCO */ +#define WM8996_FLL_FRC_NCO_SHIFT 6 /* FLL_FRC_NCO */ +#define WM8996_FLL_FRC_NCO_WIDTH 1 /* FLL_FRC_NCO */ +#define WM8996_FLL_REFCLK_DIV_MASK 0x0018 /* FLL_REFCLK_DIV - [4:3] */ +#define WM8996_FLL_REFCLK_DIV_SHIFT 3 /* FLL_REFCLK_DIV - [4:3] */ +#define WM8996_FLL_REFCLK_DIV_WIDTH 2 /* FLL_REFCLK_DIV - [4:3] */ +#define WM8996_FLL_REF_FREQ 0x0004 /* FLL_REF_FREQ */ +#define WM8996_FLL_REF_FREQ_MASK 0x0004 /* FLL_REF_FREQ */ +#define WM8996_FLL_REF_FREQ_SHIFT 2 /* FLL_REF_FREQ */ +#define WM8996_FLL_REF_FREQ_WIDTH 1 /* FLL_REF_FREQ */ +#define WM8996_FLL_REFCLK_SRC_MASK 0x0003 /* FLL_REFCLK_SRC - [1:0] */ +#define WM8996_FLL_REFCLK_SRC_SHIFT 0 /* FLL_REFCLK_SRC - [1:0] */ +#define WM8996_FLL_REFCLK_SRC_WIDTH 2 /* FLL_REFCLK_SRC - [1:0] */ + +/* + * R549 (0x225) - FLL Control (6) + */ +#define WM8996_FLL_REFCLK_SRC_STS_MASK 0x000C /* FLL_REFCLK_SRC_STS - [3:2] */ +#define WM8996_FLL_REFCLK_SRC_STS_SHIFT 2 /* FLL_REFCLK_SRC_STS - [3:2] */ +#define WM8996_FLL_REFCLK_SRC_STS_WIDTH 2 /* FLL_REFCLK_SRC_STS - [3:2] */ +#define WM8996_FLL_SWITCH_CLK 0x0001 /* FLL_SWITCH_CLK */ +#define WM8996_FLL_SWITCH_CLK_MASK 0x0001 /* FLL_SWITCH_CLK */ +#define WM8996_FLL_SWITCH_CLK_SHIFT 0 /* FLL_SWITCH_CLK */ +#define WM8996_FLL_SWITCH_CLK_WIDTH 1 /* FLL_SWITCH_CLK */ + +/* + * R550 (0x226) - FLL EFS 1 + */ +#define WM8996_FLL_LAMBDA_MASK 0xFFFF /* FLL_LAMBDA - [15:0] */ +#define WM8996_FLL_LAMBDA_SHIFT 0 /* FLL_LAMBDA - [15:0] */ +#define WM8996_FLL_LAMBDA_WIDTH 16 /* FLL_LAMBDA - [15:0] */ + +/* + * R551 (0x227) - FLL EFS 2 + */ +#define WM8996_FLL_LFSR_SEL_MASK 0x0006 /* FLL_LFSR_SEL - [2:1] */ +#define WM8996_FLL_LFSR_SEL_SHIFT 1 /* FLL_LFSR_SEL - [2:1] */ +#define WM8996_FLL_LFSR_SEL_WIDTH 2 /* FLL_LFSR_SEL - [2:1] */ +#define WM8996_FLL_EFS_ENA 0x0001 /* FLL_EFS_ENA */ +#define WM8996_FLL_EFS_ENA_MASK 0x0001 /* FLL_EFS_ENA */ +#define WM8996_FLL_EFS_ENA_SHIFT 0 /* FLL_EFS_ENA */ +#define WM8996_FLL_EFS_ENA_WIDTH 1 /* FLL_EFS_ENA */ + +/* + * R768 (0x300) - AIF1 Control + */ +#define WM8996_AIF1_TRI 0x0004 /* AIF1_TRI */ +#define WM8996_AIF1_TRI_MASK 0x0004 /* AIF1_TRI */ +#define WM8996_AIF1_TRI_SHIFT 2 /* AIF1_TRI */ +#define WM8996_AIF1_TRI_WIDTH 1 /* AIF1_TRI */ +#define WM8996_AIF1_FMT_MASK 0x0003 /* AIF1_FMT - [1:0] */ +#define WM8996_AIF1_FMT_SHIFT 0 /* AIF1_FMT - [1:0] */ +#define WM8996_AIF1_FMT_WIDTH 2 /* AIF1_FMT - [1:0] */ + +/* + * R769 (0x301) - AIF1 BCLK + */ +#define WM8996_AIF1_BCLK_INV 0x0400 /* AIF1_BCLK_INV */ +#define WM8996_AIF1_BCLK_INV_MASK 0x0400 /* AIF1_BCLK_INV */ +#define WM8996_AIF1_BCLK_INV_SHIFT 10 /* AIF1_BCLK_INV */ +#define WM8996_AIF1_BCLK_INV_WIDTH 1 /* AIF1_BCLK_INV */ +#define WM8996_AIF1_BCLK_FRC 0x0200 /* AIF1_BCLK_FRC */ +#define WM8996_AIF1_BCLK_FRC_MASK 0x0200 /* AIF1_BCLK_FRC */ +#define WM8996_AIF1_BCLK_FRC_SHIFT 9 /* AIF1_BCLK_FRC */ +#define WM8996_AIF1_BCLK_FRC_WIDTH 1 /* AIF1_BCLK_FRC */ +#define WM8996_AIF1_BCLK_MSTR 0x0100 /* AIF1_BCLK_MSTR */ +#define WM8996_AIF1_BCLK_MSTR_MASK 0x0100 /* AIF1_BCLK_MSTR */ +#define WM8996_AIF1_BCLK_MSTR_SHIFT 8 /* AIF1_BCLK_MSTR */ +#define WM8996_AIF1_BCLK_MSTR_WIDTH 1 /* AIF1_BCLK_MSTR */ +#define WM8996_AIF1_BCLK_DIV_MASK 0x000F /* AIF1_BCLK_DIV - [3:0] */ +#define WM8996_AIF1_BCLK_DIV_SHIFT 0 /* AIF1_BCLK_DIV - [3:0] */ +#define WM8996_AIF1_BCLK_DIV_WIDTH 4 /* AIF1_BCLK_DIV - [3:0] */ + +/* + * R770 (0x302) - AIF1 TX LRCLK(1) + */ +#define WM8996_AIF1TX_RATE_MASK 0x07FF /* AIF1TX_RATE - [10:0] */ +#define WM8996_AIF1TX_RATE_SHIFT 0 /* AIF1TX_RATE - [10:0] */ +#define WM8996_AIF1TX_RATE_WIDTH 11 /* AIF1TX_RATE - [10:0] */ + +/* + * R771 (0x303) - AIF1 TX LRCLK(2) + */ +#define WM8996_AIF1TX_LRCLK_MODE 0x0008 /* AIF1TX_LRCLK_MODE */ +#define WM8996_AIF1TX_LRCLK_MODE_MASK 0x0008 /* AIF1TX_LRCLK_MODE */ +#define WM8996_AIF1TX_LRCLK_MODE_SHIFT 3 /* AIF1TX_LRCLK_MODE */ +#define WM8996_AIF1TX_LRCLK_MODE_WIDTH 1 /* AIF1TX_LRCLK_MODE */ +#define WM8996_AIF1TX_LRCLK_INV 0x0004 /* AIF1TX_LRCLK_INV */ +#define WM8996_AIF1TX_LRCLK_INV_MASK 0x0004 /* AIF1TX_LRCLK_INV */ +#define WM8996_AIF1TX_LRCLK_INV_SHIFT 2 /* AIF1TX_LRCLK_INV */ +#define WM8996_AIF1TX_LRCLK_INV_WIDTH 1 /* AIF1TX_LRCLK_INV */ +#define WM8996_AIF1TX_LRCLK_FRC 0x0002 /* AIF1TX_LRCLK_FRC */ +#define WM8996_AIF1TX_LRCLK_FRC_MASK 0x0002 /* AIF1TX_LRCLK_FRC */ +#define WM8996_AIF1TX_LRCLK_FRC_SHIFT 1 /* AIF1TX_LRCLK_FRC */ +#define WM8996_AIF1TX_LRCLK_FRC_WIDTH 1 /* AIF1TX_LRCLK_FRC */ +#define WM8996_AIF1TX_LRCLK_MSTR 0x0001 /* AIF1TX_LRCLK_MSTR */ +#define WM8996_AIF1TX_LRCLK_MSTR_MASK 0x0001 /* AIF1TX_LRCLK_MSTR */ +#define WM8996_AIF1TX_LRCLK_MSTR_SHIFT 0 /* AIF1TX_LRCLK_MSTR */ +#define WM8996_AIF1TX_LRCLK_MSTR_WIDTH 1 /* AIF1TX_LRCLK_MSTR */ + +/* + * R772 (0x304) - AIF1 RX LRCLK(1) + */ +#define WM8996_AIF1RX_RATE_MASK 0x07FF /* AIF1RX_RATE - [10:0] */ +#define WM8996_AIF1RX_RATE_SHIFT 0 /* AIF1RX_RATE - [10:0] */ +#define WM8996_AIF1RX_RATE_WIDTH 11 /* AIF1RX_RATE - [10:0] */ + +/* + * R773 (0x305) - AIF1 RX LRCLK(2) + */ +#define WM8996_AIF1RX_LRCLK_INV 0x0004 /* AIF1RX_LRCLK_INV */ +#define WM8996_AIF1RX_LRCLK_INV_MASK 0x0004 /* AIF1RX_LRCLK_INV */ +#define WM8996_AIF1RX_LRCLK_INV_SHIFT 2 /* AIF1RX_LRCLK_INV */ +#define WM8996_AIF1RX_LRCLK_INV_WIDTH 1 /* AIF1RX_LRCLK_INV */ +#define WM8996_AIF1RX_LRCLK_FRC 0x0002 /* AIF1RX_LRCLK_FRC */ +#define WM8996_AIF1RX_LRCLK_FRC_MASK 0x0002 /* AIF1RX_LRCLK_FRC */ +#define WM8996_AIF1RX_LRCLK_FRC_SHIFT 1 /* AIF1RX_LRCLK_FRC */ +#define WM8996_AIF1RX_LRCLK_FRC_WIDTH 1 /* AIF1RX_LRCLK_FRC */ +#define WM8996_AIF1RX_LRCLK_MSTR 0x0001 /* AIF1RX_LRCLK_MSTR */ +#define WM8996_AIF1RX_LRCLK_MSTR_MASK 0x0001 /* AIF1RX_LRCLK_MSTR */ +#define WM8996_AIF1RX_LRCLK_MSTR_SHIFT 0 /* AIF1RX_LRCLK_MSTR */ +#define WM8996_AIF1RX_LRCLK_MSTR_WIDTH 1 /* AIF1RX_LRCLK_MSTR */ + +/* + * R774 (0x306) - AIF1TX Data Configuration (1) + */ +#define WM8996_AIF1TX_WL_MASK 0xFF00 /* AIF1TX_WL - [15:8] */ +#define WM8996_AIF1TX_WL_SHIFT 8 /* AIF1TX_WL - [15:8] */ +#define WM8996_AIF1TX_WL_WIDTH 8 /* AIF1TX_WL - [15:8] */ +#define WM8996_AIF1TX_SLOT_LEN_MASK 0x00FF /* AIF1TX_SLOT_LEN - [7:0] */ +#define WM8996_AIF1TX_SLOT_LEN_SHIFT 0 /* AIF1TX_SLOT_LEN - [7:0] */ +#define WM8996_AIF1TX_SLOT_LEN_WIDTH 8 /* AIF1TX_SLOT_LEN - [7:0] */ + +/* + * R775 (0x307) - AIF1TX Data Configuration (2) + */ +#define WM8996_AIF1TX_DAT_TRI 0x0001 /* AIF1TX_DAT_TRI */ +#define WM8996_AIF1TX_DAT_TRI_MASK 0x0001 /* AIF1TX_DAT_TRI */ +#define WM8996_AIF1TX_DAT_TRI_SHIFT 0 /* AIF1TX_DAT_TRI */ +#define WM8996_AIF1TX_DAT_TRI_WIDTH 1 /* AIF1TX_DAT_TRI */ + +/* + * R776 (0x308) - AIF1RX Data Configuration + */ +#define WM8996_AIF1RX_WL_MASK 0xFF00 /* AIF1RX_WL - [15:8] */ +#define WM8996_AIF1RX_WL_SHIFT 8 /* AIF1RX_WL - [15:8] */ +#define WM8996_AIF1RX_WL_WIDTH 8 /* AIF1RX_WL - [15:8] */ +#define WM8996_AIF1RX_SLOT_LEN_MASK 0x00FF /* AIF1RX_SLOT_LEN - [7:0] */ +#define WM8996_AIF1RX_SLOT_LEN_SHIFT 0 /* AIF1RX_SLOT_LEN - [7:0] */ +#define WM8996_AIF1RX_SLOT_LEN_WIDTH 8 /* AIF1RX_SLOT_LEN - [7:0] */ + +/* + * R777 (0x309) - AIF1TX Channel 0 Configuration + */ +#define WM8996_AIF1TX_CHAN0_DAT_INV 0x8000 /* AIF1TX_CHAN0_DAT_INV */ +#define WM8996_AIF1TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN0_DAT_INV */ +#define WM8996_AIF1TX_CHAN0_DAT_INV_SHIFT 15 /* AIF1TX_CHAN0_DAT_INV */ +#define WM8996_AIF1TX_CHAN0_DAT_INV_WIDTH 1 /* AIF1TX_CHAN0_DAT_INV */ +#define WM8996_AIF1TX_CHAN0_SPACING_MASK 0x7E00 /* AIF1TX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN0_SPACING_SHIFT 9 /* AIF1TX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN0_SPACING_WIDTH 6 /* AIF1TX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN0_SLOTS_SHIFT 6 /* AIF1TX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN0_SLOTS_WIDTH 3 /* AIF1TX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN0_START_SLOT_MASK 0x003F /* AIF1TX_CHAN0_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN0_START_SLOT_SHIFT 0 /* AIF1TX_CHAN0_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN0_START_SLOT_WIDTH 6 /* AIF1TX_CHAN0_START_SLOT - [5:0] */ + +/* + * R778 (0x30A) - AIF1TX Channel 1 Configuration + */ +#define WM8996_AIF1TX_CHAN1_DAT_INV 0x8000 /* AIF1TX_CHAN1_DAT_INV */ +#define WM8996_AIF1TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN1_DAT_INV */ +#define WM8996_AIF1TX_CHAN1_DAT_INV_SHIFT 15 /* AIF1TX_CHAN1_DAT_INV */ +#define WM8996_AIF1TX_CHAN1_DAT_INV_WIDTH 1 /* AIF1TX_CHAN1_DAT_INV */ +#define WM8996_AIF1TX_CHAN1_SPACING_MASK 0x7E00 /* AIF1TX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN1_SPACING_SHIFT 9 /* AIF1TX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN1_SPACING_WIDTH 6 /* AIF1TX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN1_SLOTS_SHIFT 6 /* AIF1TX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN1_SLOTS_WIDTH 3 /* AIF1TX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN1_START_SLOT_MASK 0x003F /* AIF1TX_CHAN1_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN1_START_SLOT_SHIFT 0 /* AIF1TX_CHAN1_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN1_START_SLOT_WIDTH 6 /* AIF1TX_CHAN1_START_SLOT - [5:0] */ + +/* + * R779 (0x30B) - AIF1TX Channel 2 Configuration + */ +#define WM8996_AIF1TX_CHAN2_DAT_INV 0x8000 /* AIF1TX_CHAN2_DAT_INV */ +#define WM8996_AIF1TX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN2_DAT_INV */ +#define WM8996_AIF1TX_CHAN2_DAT_INV_SHIFT 15 /* AIF1TX_CHAN2_DAT_INV */ +#define WM8996_AIF1TX_CHAN2_DAT_INV_WIDTH 1 /* AIF1TX_CHAN2_DAT_INV */ +#define WM8996_AIF1TX_CHAN2_SPACING_MASK 0x7E00 /* AIF1TX_CHAN2_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN2_SPACING_SHIFT 9 /* AIF1TX_CHAN2_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN2_SPACING_WIDTH 6 /* AIF1TX_CHAN2_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN2_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN2_SLOTS_SHIFT 6 /* AIF1TX_CHAN2_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN2_SLOTS_WIDTH 3 /* AIF1TX_CHAN2_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN2_START_SLOT_MASK 0x003F /* AIF1TX_CHAN2_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN2_START_SLOT_SHIFT 0 /* AIF1TX_CHAN2_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN2_START_SLOT_WIDTH 6 /* AIF1TX_CHAN2_START_SLOT - [5:0] */ + +/* + * R780 (0x30C) - AIF1TX Channel 3 Configuration + */ +#define WM8996_AIF1TX_CHAN3_DAT_INV 0x8000 /* AIF1TX_CHAN3_DAT_INV */ +#define WM8996_AIF1TX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN3_DAT_INV */ +#define WM8996_AIF1TX_CHAN3_DAT_INV_SHIFT 15 /* AIF1TX_CHAN3_DAT_INV */ +#define WM8996_AIF1TX_CHAN3_DAT_INV_WIDTH 1 /* AIF1TX_CHAN3_DAT_INV */ +#define WM8996_AIF1TX_CHAN3_SPACING_MASK 0x7E00 /* AIF1TX_CHAN3_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN3_SPACING_SHIFT 9 /* AIF1TX_CHAN3_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN3_SPACING_WIDTH 6 /* AIF1TX_CHAN3_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN3_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN3_SLOTS_SHIFT 6 /* AIF1TX_CHAN3_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN3_SLOTS_WIDTH 3 /* AIF1TX_CHAN3_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN3_START_SLOT_MASK 0x003F /* AIF1TX_CHAN3_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN3_START_SLOT_SHIFT 0 /* AIF1TX_CHAN3_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN3_START_SLOT_WIDTH 6 /* AIF1TX_CHAN3_START_SLOT - [5:0] */ + +/* + * R781 (0x30D) - AIF1TX Channel 4 Configuration + */ +#define WM8996_AIF1TX_CHAN4_DAT_INV 0x8000 /* AIF1TX_CHAN4_DAT_INV */ +#define WM8996_AIF1TX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN4_DAT_INV */ +#define WM8996_AIF1TX_CHAN4_DAT_INV_SHIFT 15 /* AIF1TX_CHAN4_DAT_INV */ +#define WM8996_AIF1TX_CHAN4_DAT_INV_WIDTH 1 /* AIF1TX_CHAN4_DAT_INV */ +#define WM8996_AIF1TX_CHAN4_SPACING_MASK 0x7E00 /* AIF1TX_CHAN4_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN4_SPACING_SHIFT 9 /* AIF1TX_CHAN4_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN4_SPACING_WIDTH 6 /* AIF1TX_CHAN4_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN4_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN4_SLOTS_SHIFT 6 /* AIF1TX_CHAN4_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN4_SLOTS_WIDTH 3 /* AIF1TX_CHAN4_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN4_START_SLOT_MASK 0x003F /* AIF1TX_CHAN4_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN4_START_SLOT_SHIFT 0 /* AIF1TX_CHAN4_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN4_START_SLOT_WIDTH 6 /* AIF1TX_CHAN4_START_SLOT - [5:0] */ + +/* + * R782 (0x30E) - AIF1TX Channel 5 Configuration + */ +#define WM8996_AIF1TX_CHAN5_DAT_INV 0x8000 /* AIF1TX_CHAN5_DAT_INV */ +#define WM8996_AIF1TX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1TX_CHAN5_DAT_INV */ +#define WM8996_AIF1TX_CHAN5_DAT_INV_SHIFT 15 /* AIF1TX_CHAN5_DAT_INV */ +#define WM8996_AIF1TX_CHAN5_DAT_INV_WIDTH 1 /* AIF1TX_CHAN5_DAT_INV */ +#define WM8996_AIF1TX_CHAN5_SPACING_MASK 0x7E00 /* AIF1TX_CHAN5_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN5_SPACING_SHIFT 9 /* AIF1TX_CHAN5_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN5_SPACING_WIDTH 6 /* AIF1TX_CHAN5_SPACING - [14:9] */ +#define WM8996_AIF1TX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1TX_CHAN5_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN5_SLOTS_SHIFT 6 /* AIF1TX_CHAN5_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN5_SLOTS_WIDTH 3 /* AIF1TX_CHAN5_SLOTS - [8:6] */ +#define WM8996_AIF1TX_CHAN5_START_SLOT_MASK 0x003F /* AIF1TX_CHAN5_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN5_START_SLOT_SHIFT 0 /* AIF1TX_CHAN5_START_SLOT - [5:0] */ +#define WM8996_AIF1TX_CHAN5_START_SLOT_WIDTH 6 /* AIF1TX_CHAN5_START_SLOT - [5:0] */ + +/* + * R783 (0x30F) - AIF1RX Channel 0 Configuration + */ +#define WM8996_AIF1RX_CHAN0_DAT_INV 0x8000 /* AIF1RX_CHAN0_DAT_INV */ +#define WM8996_AIF1RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN0_DAT_INV */ +#define WM8996_AIF1RX_CHAN0_DAT_INV_SHIFT 15 /* AIF1RX_CHAN0_DAT_INV */ +#define WM8996_AIF1RX_CHAN0_DAT_INV_WIDTH 1 /* AIF1RX_CHAN0_DAT_INV */ +#define WM8996_AIF1RX_CHAN0_SPACING_MASK 0x7E00 /* AIF1RX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN0_SPACING_SHIFT 9 /* AIF1RX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN0_SPACING_WIDTH 6 /* AIF1RX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN0_SLOTS_SHIFT 6 /* AIF1RX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN0_SLOTS_WIDTH 3 /* AIF1RX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN0_START_SLOT_MASK 0x003F /* AIF1RX_CHAN0_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN0_START_SLOT_SHIFT 0 /* AIF1RX_CHAN0_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN0_START_SLOT_WIDTH 6 /* AIF1RX_CHAN0_START_SLOT - [5:0] */ + +/* + * R784 (0x310) - AIF1RX Channel 1 Configuration + */ +#define WM8996_AIF1RX_CHAN1_DAT_INV 0x8000 /* AIF1RX_CHAN1_DAT_INV */ +#define WM8996_AIF1RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN1_DAT_INV */ +#define WM8996_AIF1RX_CHAN1_DAT_INV_SHIFT 15 /* AIF1RX_CHAN1_DAT_INV */ +#define WM8996_AIF1RX_CHAN1_DAT_INV_WIDTH 1 /* AIF1RX_CHAN1_DAT_INV */ +#define WM8996_AIF1RX_CHAN1_SPACING_MASK 0x7E00 /* AIF1RX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN1_SPACING_SHIFT 9 /* AIF1RX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN1_SPACING_WIDTH 6 /* AIF1RX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN1_SLOTS_SHIFT 6 /* AIF1RX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN1_SLOTS_WIDTH 3 /* AIF1RX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN1_START_SLOT_MASK 0x003F /* AIF1RX_CHAN1_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN1_START_SLOT_SHIFT 0 /* AIF1RX_CHAN1_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN1_START_SLOT_WIDTH 6 /* AIF1RX_CHAN1_START_SLOT - [5:0] */ + +/* + * R785 (0x311) - AIF1RX Channel 2 Configuration + */ +#define WM8996_AIF1RX_CHAN2_DAT_INV 0x8000 /* AIF1RX_CHAN2_DAT_INV */ +#define WM8996_AIF1RX_CHAN2_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN2_DAT_INV */ +#define WM8996_AIF1RX_CHAN2_DAT_INV_SHIFT 15 /* AIF1RX_CHAN2_DAT_INV */ +#define WM8996_AIF1RX_CHAN2_DAT_INV_WIDTH 1 /* AIF1RX_CHAN2_DAT_INV */ +#define WM8996_AIF1RX_CHAN2_SPACING_MASK 0x7E00 /* AIF1RX_CHAN2_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN2_SPACING_SHIFT 9 /* AIF1RX_CHAN2_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN2_SPACING_WIDTH 6 /* AIF1RX_CHAN2_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN2_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN2_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN2_SLOTS_SHIFT 6 /* AIF1RX_CHAN2_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN2_SLOTS_WIDTH 3 /* AIF1RX_CHAN2_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN2_START_SLOT_MASK 0x003F /* AIF1RX_CHAN2_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN2_START_SLOT_SHIFT 0 /* AIF1RX_CHAN2_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN2_START_SLOT_WIDTH 6 /* AIF1RX_CHAN2_START_SLOT - [5:0] */ + +/* + * R786 (0x312) - AIF1RX Channel 3 Configuration + */ +#define WM8996_AIF1RX_CHAN3_DAT_INV 0x8000 /* AIF1RX_CHAN3_DAT_INV */ +#define WM8996_AIF1RX_CHAN3_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN3_DAT_INV */ +#define WM8996_AIF1RX_CHAN3_DAT_INV_SHIFT 15 /* AIF1RX_CHAN3_DAT_INV */ +#define WM8996_AIF1RX_CHAN3_DAT_INV_WIDTH 1 /* AIF1RX_CHAN3_DAT_INV */ +#define WM8996_AIF1RX_CHAN3_SPACING_MASK 0x7E00 /* AIF1RX_CHAN3_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN3_SPACING_SHIFT 9 /* AIF1RX_CHAN3_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN3_SPACING_WIDTH 6 /* AIF1RX_CHAN3_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN3_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN3_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN3_SLOTS_SHIFT 6 /* AIF1RX_CHAN3_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN3_SLOTS_WIDTH 3 /* AIF1RX_CHAN3_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN3_START_SLOT_MASK 0x003F /* AIF1RX_CHAN3_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN3_START_SLOT_SHIFT 0 /* AIF1RX_CHAN3_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN3_START_SLOT_WIDTH 6 /* AIF1RX_CHAN3_START_SLOT - [5:0] */ + +/* + * R787 (0x313) - AIF1RX Channel 4 Configuration + */ +#define WM8996_AIF1RX_CHAN4_DAT_INV 0x8000 /* AIF1RX_CHAN4_DAT_INV */ +#define WM8996_AIF1RX_CHAN4_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN4_DAT_INV */ +#define WM8996_AIF1RX_CHAN4_DAT_INV_SHIFT 15 /* AIF1RX_CHAN4_DAT_INV */ +#define WM8996_AIF1RX_CHAN4_DAT_INV_WIDTH 1 /* AIF1RX_CHAN4_DAT_INV */ +#define WM8996_AIF1RX_CHAN4_SPACING_MASK 0x7E00 /* AIF1RX_CHAN4_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN4_SPACING_SHIFT 9 /* AIF1RX_CHAN4_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN4_SPACING_WIDTH 6 /* AIF1RX_CHAN4_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN4_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN4_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN4_SLOTS_SHIFT 6 /* AIF1RX_CHAN4_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN4_SLOTS_WIDTH 3 /* AIF1RX_CHAN4_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN4_START_SLOT_MASK 0x003F /* AIF1RX_CHAN4_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN4_START_SLOT_SHIFT 0 /* AIF1RX_CHAN4_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN4_START_SLOT_WIDTH 6 /* AIF1RX_CHAN4_START_SLOT - [5:0] */ + +/* + * R788 (0x314) - AIF1RX Channel 5 Configuration + */ +#define WM8996_AIF1RX_CHAN5_DAT_INV 0x8000 /* AIF1RX_CHAN5_DAT_INV */ +#define WM8996_AIF1RX_CHAN5_DAT_INV_MASK 0x8000 /* AIF1RX_CHAN5_DAT_INV */ +#define WM8996_AIF1RX_CHAN5_DAT_INV_SHIFT 15 /* AIF1RX_CHAN5_DAT_INV */ +#define WM8996_AIF1RX_CHAN5_DAT_INV_WIDTH 1 /* AIF1RX_CHAN5_DAT_INV */ +#define WM8996_AIF1RX_CHAN5_SPACING_MASK 0x7E00 /* AIF1RX_CHAN5_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN5_SPACING_SHIFT 9 /* AIF1RX_CHAN5_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN5_SPACING_WIDTH 6 /* AIF1RX_CHAN5_SPACING - [14:9] */ +#define WM8996_AIF1RX_CHAN5_SLOTS_MASK 0x01C0 /* AIF1RX_CHAN5_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN5_SLOTS_SHIFT 6 /* AIF1RX_CHAN5_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN5_SLOTS_WIDTH 3 /* AIF1RX_CHAN5_SLOTS - [8:6] */ +#define WM8996_AIF1RX_CHAN5_START_SLOT_MASK 0x003F /* AIF1RX_CHAN5_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN5_START_SLOT_SHIFT 0 /* AIF1RX_CHAN5_START_SLOT - [5:0] */ +#define WM8996_AIF1RX_CHAN5_START_SLOT_WIDTH 6 /* AIF1RX_CHAN5_START_SLOT - [5:0] */ + +/* + * R789 (0x315) - AIF1RX Mono Configuration + */ +#define WM8996_AIF1RX_CHAN4_MONO_MODE 0x0004 /* AIF1RX_CHAN4_MONO_MODE */ +#define WM8996_AIF1RX_CHAN4_MONO_MODE_MASK 0x0004 /* AIF1RX_CHAN4_MONO_MODE */ +#define WM8996_AIF1RX_CHAN4_MONO_MODE_SHIFT 2 /* AIF1RX_CHAN4_MONO_MODE */ +#define WM8996_AIF1RX_CHAN4_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN4_MONO_MODE */ +#define WM8996_AIF1RX_CHAN2_MONO_MODE 0x0002 /* AIF1RX_CHAN2_MONO_MODE */ +#define WM8996_AIF1RX_CHAN2_MONO_MODE_MASK 0x0002 /* AIF1RX_CHAN2_MONO_MODE */ +#define WM8996_AIF1RX_CHAN2_MONO_MODE_SHIFT 1 /* AIF1RX_CHAN2_MONO_MODE */ +#define WM8996_AIF1RX_CHAN2_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN2_MONO_MODE */ +#define WM8996_AIF1RX_CHAN0_MONO_MODE 0x0001 /* AIF1RX_CHAN0_MONO_MODE */ +#define WM8996_AIF1RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF1RX_CHAN0_MONO_MODE */ +#define WM8996_AIF1RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF1RX_CHAN0_MONO_MODE */ +#define WM8996_AIF1RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF1RX_CHAN0_MONO_MODE */ + +/* + * R794 (0x31A) - AIF1TX Test + */ +#define WM8996_AIF1TX45_DITHER_ENA 0x0004 /* AIF1TX45_DITHER_ENA */ +#define WM8996_AIF1TX45_DITHER_ENA_MASK 0x0004 /* AIF1TX45_DITHER_ENA */ +#define WM8996_AIF1TX45_DITHER_ENA_SHIFT 2 /* AIF1TX45_DITHER_ENA */ +#define WM8996_AIF1TX45_DITHER_ENA_WIDTH 1 /* AIF1TX45_DITHER_ENA */ +#define WM8996_AIF1TX23_DITHER_ENA 0x0002 /* AIF1TX23_DITHER_ENA */ +#define WM8996_AIF1TX23_DITHER_ENA_MASK 0x0002 /* AIF1TX23_DITHER_ENA */ +#define WM8996_AIF1TX23_DITHER_ENA_SHIFT 1 /* AIF1TX23_DITHER_ENA */ +#define WM8996_AIF1TX23_DITHER_ENA_WIDTH 1 /* AIF1TX23_DITHER_ENA */ +#define WM8996_AIF1TX01_DITHER_ENA 0x0001 /* AIF1TX01_DITHER_ENA */ +#define WM8996_AIF1TX01_DITHER_ENA_MASK 0x0001 /* AIF1TX01_DITHER_ENA */ +#define WM8996_AIF1TX01_DITHER_ENA_SHIFT 0 /* AIF1TX01_DITHER_ENA */ +#define WM8996_AIF1TX01_DITHER_ENA_WIDTH 1 /* AIF1TX01_DITHER_ENA */ + +/* + * R800 (0x320) - AIF2 Control + */ +#define WM8996_AIF2_TRI 0x0004 /* AIF2_TRI */ +#define WM8996_AIF2_TRI_MASK 0x0004 /* AIF2_TRI */ +#define WM8996_AIF2_TRI_SHIFT 2 /* AIF2_TRI */ +#define WM8996_AIF2_TRI_WIDTH 1 /* AIF2_TRI */ +#define WM8996_AIF2_FMT_MASK 0x0003 /* AIF2_FMT - [1:0] */ +#define WM8996_AIF2_FMT_SHIFT 0 /* AIF2_FMT - [1:0] */ +#define WM8996_AIF2_FMT_WIDTH 2 /* AIF2_FMT - [1:0] */ + +/* + * R801 (0x321) - AIF2 BCLK + */ +#define WM8996_AIF2_BCLK_INV 0x0400 /* AIF2_BCLK_INV */ +#define WM8996_AIF2_BCLK_INV_MASK 0x0400 /* AIF2_BCLK_INV */ +#define WM8996_AIF2_BCLK_INV_SHIFT 10 /* AIF2_BCLK_INV */ +#define WM8996_AIF2_BCLK_INV_WIDTH 1 /* AIF2_BCLK_INV */ +#define WM8996_AIF2_BCLK_FRC 0x0200 /* AIF2_BCLK_FRC */ +#define WM8996_AIF2_BCLK_FRC_MASK 0x0200 /* AIF2_BCLK_FRC */ +#define WM8996_AIF2_BCLK_FRC_SHIFT 9 /* AIF2_BCLK_FRC */ +#define WM8996_AIF2_BCLK_FRC_WIDTH 1 /* AIF2_BCLK_FRC */ +#define WM8996_AIF2_BCLK_MSTR 0x0100 /* AIF2_BCLK_MSTR */ +#define WM8996_AIF2_BCLK_MSTR_MASK 0x0100 /* AIF2_BCLK_MSTR */ +#define WM8996_AIF2_BCLK_MSTR_SHIFT 8 /* AIF2_BCLK_MSTR */ +#define WM8996_AIF2_BCLK_MSTR_WIDTH 1 /* AIF2_BCLK_MSTR */ +#define WM8996_AIF2_BCLK_DIV_MASK 0x000F /* AIF2_BCLK_DIV - [3:0] */ +#define WM8996_AIF2_BCLK_DIV_SHIFT 0 /* AIF2_BCLK_DIV - [3:0] */ +#define WM8996_AIF2_BCLK_DIV_WIDTH 4 /* AIF2_BCLK_DIV - [3:0] */ + +/* + * R802 (0x322) - AIF2 TX LRCLK(1) + */ +#define WM8996_AIF2TX_RATE_MASK 0x07FF /* AIF2TX_RATE - [10:0] */ +#define WM8996_AIF2TX_RATE_SHIFT 0 /* AIF2TX_RATE - [10:0] */ +#define WM8996_AIF2TX_RATE_WIDTH 11 /* AIF2TX_RATE - [10:0] */ + +/* + * R803 (0x323) - AIF2 TX LRCLK(2) + */ +#define WM8996_AIF2TX_LRCLK_MODE 0x0008 /* AIF2TX_LRCLK_MODE */ +#define WM8996_AIF2TX_LRCLK_MODE_MASK 0x0008 /* AIF2TX_LRCLK_MODE */ +#define WM8996_AIF2TX_LRCLK_MODE_SHIFT 3 /* AIF2TX_LRCLK_MODE */ +#define WM8996_AIF2TX_LRCLK_MODE_WIDTH 1 /* AIF2TX_LRCLK_MODE */ +#define WM8996_AIF2TX_LRCLK_INV 0x0004 /* AIF2TX_LRCLK_INV */ +#define WM8996_AIF2TX_LRCLK_INV_MASK 0x0004 /* AIF2TX_LRCLK_INV */ +#define WM8996_AIF2TX_LRCLK_INV_SHIFT 2 /* AIF2TX_LRCLK_INV */ +#define WM8996_AIF2TX_LRCLK_INV_WIDTH 1 /* AIF2TX_LRCLK_INV */ +#define WM8996_AIF2TX_LRCLK_FRC 0x0002 /* AIF2TX_LRCLK_FRC */ +#define WM8996_AIF2TX_LRCLK_FRC_MASK 0x0002 /* AIF2TX_LRCLK_FRC */ +#define WM8996_AIF2TX_LRCLK_FRC_SHIFT 1 /* AIF2TX_LRCLK_FRC */ +#define WM8996_AIF2TX_LRCLK_FRC_WIDTH 1 /* AIF2TX_LRCLK_FRC */ +#define WM8996_AIF2TX_LRCLK_MSTR 0x0001 /* AIF2TX_LRCLK_MSTR */ +#define WM8996_AIF2TX_LRCLK_MSTR_MASK 0x0001 /* AIF2TX_LRCLK_MSTR */ +#define WM8996_AIF2TX_LRCLK_MSTR_SHIFT 0 /* AIF2TX_LRCLK_MSTR */ +#define WM8996_AIF2TX_LRCLK_MSTR_WIDTH 1 /* AIF2TX_LRCLK_MSTR */ + +/* + * R804 (0x324) - AIF2 RX LRCLK(1) + */ +#define WM8996_AIF2RX_RATE_MASK 0x07FF /* AIF2RX_RATE - [10:0] */ +#define WM8996_AIF2RX_RATE_SHIFT 0 /* AIF2RX_RATE - [10:0] */ +#define WM8996_AIF2RX_RATE_WIDTH 11 /* AIF2RX_RATE - [10:0] */ + +/* + * R805 (0x325) - AIF2 RX LRCLK(2) + */ +#define WM8996_AIF2RX_LRCLK_INV 0x0004 /* AIF2RX_LRCLK_INV */ +#define WM8996_AIF2RX_LRCLK_INV_MASK 0x0004 /* AIF2RX_LRCLK_INV */ +#define WM8996_AIF2RX_LRCLK_INV_SHIFT 2 /* AIF2RX_LRCLK_INV */ +#define WM8996_AIF2RX_LRCLK_INV_WIDTH 1 /* AIF2RX_LRCLK_INV */ +#define WM8996_AIF2RX_LRCLK_FRC 0x0002 /* AIF2RX_LRCLK_FRC */ +#define WM8996_AIF2RX_LRCLK_FRC_MASK 0x0002 /* AIF2RX_LRCLK_FRC */ +#define WM8996_AIF2RX_LRCLK_FRC_SHIFT 1 /* AIF2RX_LRCLK_FRC */ +#define WM8996_AIF2RX_LRCLK_FRC_WIDTH 1 /* AIF2RX_LRCLK_FRC */ +#define WM8996_AIF2RX_LRCLK_MSTR 0x0001 /* AIF2RX_LRCLK_MSTR */ +#define WM8996_AIF2RX_LRCLK_MSTR_MASK 0x0001 /* AIF2RX_LRCLK_MSTR */ +#define WM8996_AIF2RX_LRCLK_MSTR_SHIFT 0 /* AIF2RX_LRCLK_MSTR */ +#define WM8996_AIF2RX_LRCLK_MSTR_WIDTH 1 /* AIF2RX_LRCLK_MSTR */ + +/* + * R806 (0x326) - AIF2TX Data Configuration (1) + */ +#define WM8996_AIF2TX_WL_MASK 0xFF00 /* AIF2TX_WL - [15:8] */ +#define WM8996_AIF2TX_WL_SHIFT 8 /* AIF2TX_WL - [15:8] */ +#define WM8996_AIF2TX_WL_WIDTH 8 /* AIF2TX_WL - [15:8] */ +#define WM8996_AIF2TX_SLOT_LEN_MASK 0x00FF /* AIF2TX_SLOT_LEN - [7:0] */ +#define WM8996_AIF2TX_SLOT_LEN_SHIFT 0 /* AIF2TX_SLOT_LEN - [7:0] */ +#define WM8996_AIF2TX_SLOT_LEN_WIDTH 8 /* AIF2TX_SLOT_LEN - [7:0] */ + +/* + * R807 (0x327) - AIF2TX Data Configuration (2) + */ +#define WM8996_AIF2TX_DAT_TRI 0x0001 /* AIF2TX_DAT_TRI */ +#define WM8996_AIF2TX_DAT_TRI_MASK 0x0001 /* AIF2TX_DAT_TRI */ +#define WM8996_AIF2TX_DAT_TRI_SHIFT 0 /* AIF2TX_DAT_TRI */ +#define WM8996_AIF2TX_DAT_TRI_WIDTH 1 /* AIF2TX_DAT_TRI */ + +/* + * R808 (0x328) - AIF2RX Data Configuration + */ +#define WM8996_AIF2RX_WL_MASK 0xFF00 /* AIF2RX_WL - [15:8] */ +#define WM8996_AIF2RX_WL_SHIFT 8 /* AIF2RX_WL - [15:8] */ +#define WM8996_AIF2RX_WL_WIDTH 8 /* AIF2RX_WL - [15:8] */ +#define WM8996_AIF2RX_SLOT_LEN_MASK 0x00FF /* AIF2RX_SLOT_LEN - [7:0] */ +#define WM8996_AIF2RX_SLOT_LEN_SHIFT 0 /* AIF2RX_SLOT_LEN - [7:0] */ +#define WM8996_AIF2RX_SLOT_LEN_WIDTH 8 /* AIF2RX_SLOT_LEN - [7:0] */ + +/* + * R809 (0x329) - AIF2TX Channel 0 Configuration + */ +#define WM8996_AIF2TX_CHAN0_DAT_INV 0x8000 /* AIF2TX_CHAN0_DAT_INV */ +#define WM8996_AIF2TX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN0_DAT_INV */ +#define WM8996_AIF2TX_CHAN0_DAT_INV_SHIFT 15 /* AIF2TX_CHAN0_DAT_INV */ +#define WM8996_AIF2TX_CHAN0_DAT_INV_WIDTH 1 /* AIF2TX_CHAN0_DAT_INV */ +#define WM8996_AIF2TX_CHAN0_SPACING_MASK 0x7E00 /* AIF2TX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF2TX_CHAN0_SPACING_SHIFT 9 /* AIF2TX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF2TX_CHAN0_SPACING_WIDTH 6 /* AIF2TX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF2TX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF2TX_CHAN0_SLOTS_SHIFT 6 /* AIF2TX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF2TX_CHAN0_SLOTS_WIDTH 3 /* AIF2TX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF2TX_CHAN0_START_SLOT_MASK 0x003F /* AIF2TX_CHAN0_START_SLOT - [5:0] */ +#define WM8996_AIF2TX_CHAN0_START_SLOT_SHIFT 0 /* AIF2TX_CHAN0_START_SLOT - [5:0] */ +#define WM8996_AIF2TX_CHAN0_START_SLOT_WIDTH 6 /* AIF2TX_CHAN0_START_SLOT - [5:0] */ + +/* + * R810 (0x32A) - AIF2TX Channel 1 Configuration + */ +#define WM8996_AIF2TX_CHAN1_DAT_INV 0x8000 /* AIF2TX_CHAN1_DAT_INV */ +#define WM8996_AIF2TX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2TX_CHAN1_DAT_INV */ +#define WM8996_AIF2TX_CHAN1_DAT_INV_SHIFT 15 /* AIF2TX_CHAN1_DAT_INV */ +#define WM8996_AIF2TX_CHAN1_DAT_INV_WIDTH 1 /* AIF2TX_CHAN1_DAT_INV */ +#define WM8996_AIF2TX_CHAN1_SPACING_MASK 0x7E00 /* AIF2TX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF2TX_CHAN1_SPACING_SHIFT 9 /* AIF2TX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF2TX_CHAN1_SPACING_WIDTH 6 /* AIF2TX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF2TX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2TX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF2TX_CHAN1_SLOTS_SHIFT 6 /* AIF2TX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF2TX_CHAN1_SLOTS_WIDTH 3 /* AIF2TX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF2TX_CHAN1_START_SLOT_MASK 0x003F /* AIF2TX_CHAN1_START_SLOT - [5:0] */ +#define WM8996_AIF2TX_CHAN1_START_SLOT_SHIFT 0 /* AIF2TX_CHAN1_START_SLOT - [5:0] */ +#define WM8996_AIF2TX_CHAN1_START_SLOT_WIDTH 6 /* AIF2TX_CHAN1_START_SLOT - [5:0] */ + +/* + * R811 (0x32B) - AIF2RX Channel 0 Configuration + */ +#define WM8996_AIF2RX_CHAN0_DAT_INV 0x8000 /* AIF2RX_CHAN0_DAT_INV */ +#define WM8996_AIF2RX_CHAN0_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN0_DAT_INV */ +#define WM8996_AIF2RX_CHAN0_DAT_INV_SHIFT 15 /* AIF2RX_CHAN0_DAT_INV */ +#define WM8996_AIF2RX_CHAN0_DAT_INV_WIDTH 1 /* AIF2RX_CHAN0_DAT_INV */ +#define WM8996_AIF2RX_CHAN0_SPACING_MASK 0x7E00 /* AIF2RX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF2RX_CHAN0_SPACING_SHIFT 9 /* AIF2RX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF2RX_CHAN0_SPACING_WIDTH 6 /* AIF2RX_CHAN0_SPACING - [14:9] */ +#define WM8996_AIF2RX_CHAN0_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF2RX_CHAN0_SLOTS_SHIFT 6 /* AIF2RX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF2RX_CHAN0_SLOTS_WIDTH 3 /* AIF2RX_CHAN0_SLOTS - [8:6] */ +#define WM8996_AIF2RX_CHAN0_START_SLOT_MASK 0x003F /* AIF2RX_CHAN0_START_SLOT - [5:0] */ +#define WM8996_AIF2RX_CHAN0_START_SLOT_SHIFT 0 /* AIF2RX_CHAN0_START_SLOT - [5:0] */ +#define WM8996_AIF2RX_CHAN0_START_SLOT_WIDTH 6 /* AIF2RX_CHAN0_START_SLOT - [5:0] */ + +/* + * R812 (0x32C) - AIF2RX Channel 1 Configuration + */ +#define WM8996_AIF2RX_CHAN1_DAT_INV 0x8000 /* AIF2RX_CHAN1_DAT_INV */ +#define WM8996_AIF2RX_CHAN1_DAT_INV_MASK 0x8000 /* AIF2RX_CHAN1_DAT_INV */ +#define WM8996_AIF2RX_CHAN1_DAT_INV_SHIFT 15 /* AIF2RX_CHAN1_DAT_INV */ +#define WM8996_AIF2RX_CHAN1_DAT_INV_WIDTH 1 /* AIF2RX_CHAN1_DAT_INV */ +#define WM8996_AIF2RX_CHAN1_SPACING_MASK 0x7E00 /* AIF2RX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF2RX_CHAN1_SPACING_SHIFT 9 /* AIF2RX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF2RX_CHAN1_SPACING_WIDTH 6 /* AIF2RX_CHAN1_SPACING - [14:9] */ +#define WM8996_AIF2RX_CHAN1_SLOTS_MASK 0x01C0 /* AIF2RX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF2RX_CHAN1_SLOTS_SHIFT 6 /* AIF2RX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF2RX_CHAN1_SLOTS_WIDTH 3 /* AIF2RX_CHAN1_SLOTS - [8:6] */ +#define WM8996_AIF2RX_CHAN1_START_SLOT_MASK 0x003F /* AIF2RX_CHAN1_START_SLOT - [5:0] */ +#define WM8996_AIF2RX_CHAN1_START_SLOT_SHIFT 0 /* AIF2RX_CHAN1_START_SLOT - [5:0] */ +#define WM8996_AIF2RX_CHAN1_START_SLOT_WIDTH 6 /* AIF2RX_CHAN1_START_SLOT - [5:0] */ + +/* + * R813 (0x32D) - AIF2RX Mono Configuration + */ +#define WM8996_AIF2RX_CHAN0_MONO_MODE 0x0001 /* AIF2RX_CHAN0_MONO_MODE */ +#define WM8996_AIF2RX_CHAN0_MONO_MODE_MASK 0x0001 /* AIF2RX_CHAN0_MONO_MODE */ +#define WM8996_AIF2RX_CHAN0_MONO_MODE_SHIFT 0 /* AIF2RX_CHAN0_MONO_MODE */ +#define WM8996_AIF2RX_CHAN0_MONO_MODE_WIDTH 1 /* AIF2RX_CHAN0_MONO_MODE */ + +/* + * R815 (0x32F) - AIF2TX Test + */ +#define WM8996_AIF2TX_DITHER_ENA 0x0001 /* AIF2TX_DITHER_ENA */ +#define WM8996_AIF2TX_DITHER_ENA_MASK 0x0001 /* AIF2TX_DITHER_ENA */ +#define WM8996_AIF2TX_DITHER_ENA_SHIFT 0 /* AIF2TX_DITHER_ENA */ +#define WM8996_AIF2TX_DITHER_ENA_WIDTH 1 /* AIF2TX_DITHER_ENA */ + +/* + * R1024 (0x400) - DSP1 TX Left Volume + */ +#define WM8996_DSP1TX_VU 0x0100 /* DSP1TX_VU */ +#define WM8996_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */ +#define WM8996_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */ +#define WM8996_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */ +#define WM8996_DSP1TXL_VOL_MASK 0x00FF /* DSP1TXL_VOL - [7:0] */ +#define WM8996_DSP1TXL_VOL_SHIFT 0 /* DSP1TXL_VOL - [7:0] */ +#define WM8996_DSP1TXL_VOL_WIDTH 8 /* DSP1TXL_VOL - [7:0] */ + +/* + * R1025 (0x401) - DSP1 TX Right Volume + */ +#define WM8996_DSP1TX_VU 0x0100 /* DSP1TX_VU */ +#define WM8996_DSP1TX_VU_MASK 0x0100 /* DSP1TX_VU */ +#define WM8996_DSP1TX_VU_SHIFT 8 /* DSP1TX_VU */ +#define WM8996_DSP1TX_VU_WIDTH 1 /* DSP1TX_VU */ +#define WM8996_DSP1TXR_VOL_MASK 0x00FF /* DSP1TXR_VOL - [7:0] */ +#define WM8996_DSP1TXR_VOL_SHIFT 0 /* DSP1TXR_VOL - [7:0] */ +#define WM8996_DSP1TXR_VOL_WIDTH 8 /* DSP1TXR_VOL - [7:0] */ + +/* + * R1026 (0x402) - DSP1 RX Left Volume + */ +#define WM8996_DSP1RX_VU 0x0100 /* DSP1RX_VU */ +#define WM8996_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */ +#define WM8996_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */ +#define WM8996_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */ +#define WM8996_DSP1RXL_VOL_MASK 0x00FF /* DSP1RXL_VOL - [7:0] */ +#define WM8996_DSP1RXL_VOL_SHIFT 0 /* DSP1RXL_VOL - [7:0] */ +#define WM8996_DSP1RXL_VOL_WIDTH 8 /* DSP1RXL_VOL - [7:0] */ + +/* + * R1027 (0x403) - DSP1 RX Right Volume + */ +#define WM8996_DSP1RX_VU 0x0100 /* DSP1RX_VU */ +#define WM8996_DSP1RX_VU_MASK 0x0100 /* DSP1RX_VU */ +#define WM8996_DSP1RX_VU_SHIFT 8 /* DSP1RX_VU */ +#define WM8996_DSP1RX_VU_WIDTH 1 /* DSP1RX_VU */ +#define WM8996_DSP1RXR_VOL_MASK 0x00FF /* DSP1RXR_VOL - [7:0] */ +#define WM8996_DSP1RXR_VOL_SHIFT 0 /* DSP1RXR_VOL - [7:0] */ +#define WM8996_DSP1RXR_VOL_WIDTH 8 /* DSP1RXR_VOL - [7:0] */ + +/* + * R1040 (0x410) - DSP1 TX Filters + */ +#define WM8996_DSP1TX_NF 0x2000 /* DSP1TX_NF */ +#define WM8996_DSP1TX_NF_MASK 0x2000 /* DSP1TX_NF */ +#define WM8996_DSP1TX_NF_SHIFT 13 /* DSP1TX_NF */ +#define WM8996_DSP1TX_NF_WIDTH 1 /* DSP1TX_NF */ +#define WM8996_DSP1TXL_HPF 0x1000 /* DSP1TXL_HPF */ +#define WM8996_DSP1TXL_HPF_MASK 0x1000 /* DSP1TXL_HPF */ +#define WM8996_DSP1TXL_HPF_SHIFT 12 /* DSP1TXL_HPF */ +#define WM8996_DSP1TXL_HPF_WIDTH 1 /* DSP1TXL_HPF */ +#define WM8996_DSP1TXR_HPF 0x0800 /* DSP1TXR_HPF */ +#define WM8996_DSP1TXR_HPF_MASK 0x0800 /* DSP1TXR_HPF */ +#define WM8996_DSP1TXR_HPF_SHIFT 11 /* DSP1TXR_HPF */ +#define WM8996_DSP1TXR_HPF_WIDTH 1 /* DSP1TXR_HPF */ +#define WM8996_DSP1TX_HPF_MODE_MASK 0x0018 /* DSP1TX_HPF_MODE - [4:3] */ +#define WM8996_DSP1TX_HPF_MODE_SHIFT 3 /* DSP1TX_HPF_MODE - [4:3] */ +#define WM8996_DSP1TX_HPF_MODE_WIDTH 2 /* DSP1TX_HPF_MODE - [4:3] */ +#define WM8996_DSP1TX_HPF_CUT_MASK 0x0007 /* DSP1TX_HPF_CUT - [2:0] */ +#define WM8996_DSP1TX_HPF_CUT_SHIFT 0 /* DSP1TX_HPF_CUT - [2:0] */ +#define WM8996_DSP1TX_HPF_CUT_WIDTH 3 /* DSP1TX_HPF_CUT - [2:0] */ + +/* + * R1056 (0x420) - DSP1 RX Filters (1) + */ +#define WM8996_DSP1RX_MUTE 0x0200 /* DSP1RX_MUTE */ +#define WM8996_DSP1RX_MUTE_MASK 0x0200 /* DSP1RX_MUTE */ +#define WM8996_DSP1RX_MUTE_SHIFT 9 /* DSP1RX_MUTE */ +#define WM8996_DSP1RX_MUTE_WIDTH 1 /* DSP1RX_MUTE */ +#define WM8996_DSP1RX_MONO 0x0080 /* DSP1RX_MONO */ +#define WM8996_DSP1RX_MONO_MASK 0x0080 /* DSP1RX_MONO */ +#define WM8996_DSP1RX_MONO_SHIFT 7 /* DSP1RX_MONO */ +#define WM8996_DSP1RX_MONO_WIDTH 1 /* DSP1RX_MONO */ +#define WM8996_DSP1RX_MUTERATE 0x0020 /* DSP1RX_MUTERATE */ +#define WM8996_DSP1RX_MUTERATE_MASK 0x0020 /* DSP1RX_MUTERATE */ +#define WM8996_DSP1RX_MUTERATE_SHIFT 5 /* DSP1RX_MUTERATE */ +#define WM8996_DSP1RX_MUTERATE_WIDTH 1 /* DSP1RX_MUTERATE */ +#define WM8996_DSP1RX_UNMUTE_RAMP 0x0010 /* DSP1RX_UNMUTE_RAMP */ +#define WM8996_DSP1RX_UNMUTE_RAMP_MASK 0x0010 /* DSP1RX_UNMUTE_RAMP */ +#define WM8996_DSP1RX_UNMUTE_RAMP_SHIFT 4 /* DSP1RX_UNMUTE_RAMP */ +#define WM8996_DSP1RX_UNMUTE_RAMP_WIDTH 1 /* DSP1RX_UNMUTE_RAMP */ + +/* + * R1057 (0x421) - DSP1 RX Filters (2) + */ +#define WM8996_DSP1RX_3D_GAIN_MASK 0x3E00 /* DSP1RX_3D_GAIN - [13:9] */ +#define WM8996_DSP1RX_3D_GAIN_SHIFT 9 /* DSP1RX_3D_GAIN - [13:9] */ +#define WM8996_DSP1RX_3D_GAIN_WIDTH 5 /* DSP1RX_3D_GAIN - [13:9] */ +#define WM8996_DSP1RX_3D_ENA 0x0100 /* DSP1RX_3D_ENA */ +#define WM8996_DSP1RX_3D_ENA_MASK 0x0100 /* DSP1RX_3D_ENA */ +#define WM8996_DSP1RX_3D_ENA_SHIFT 8 /* DSP1RX_3D_ENA */ +#define WM8996_DSP1RX_3D_ENA_WIDTH 1 /* DSP1RX_3D_ENA */ + +/* + * R1088 (0x440) - DSP1 DRC (1) + */ +#define WM8996_DSP1DRC_SIG_DET_RMS_MASK 0xF800 /* DSP1DRC_SIG_DET_RMS - [15:11] */ +#define WM8996_DSP1DRC_SIG_DET_RMS_SHIFT 11 /* DSP1DRC_SIG_DET_RMS - [15:11] */ +#define WM8996_DSP1DRC_SIG_DET_RMS_WIDTH 5 /* DSP1DRC_SIG_DET_RMS - [15:11] */ +#define WM8996_DSP1DRC_SIG_DET_PK_MASK 0x0600 /* DSP1DRC_SIG_DET_PK - [10:9] */ +#define WM8996_DSP1DRC_SIG_DET_PK_SHIFT 9 /* DSP1DRC_SIG_DET_PK - [10:9] */ +#define WM8996_DSP1DRC_SIG_DET_PK_WIDTH 2 /* DSP1DRC_SIG_DET_PK - [10:9] */ +#define WM8996_DSP1DRC_NG_ENA 0x0100 /* DSP1DRC_NG_ENA */ +#define WM8996_DSP1DRC_NG_ENA_MASK 0x0100 /* DSP1DRC_NG_ENA */ +#define WM8996_DSP1DRC_NG_ENA_SHIFT 8 /* DSP1DRC_NG_ENA */ +#define WM8996_DSP1DRC_NG_ENA_WIDTH 1 /* DSP1DRC_NG_ENA */ +#define WM8996_DSP1DRC_SIG_DET_MODE 0x0080 /* DSP1DRC_SIG_DET_MODE */ +#define WM8996_DSP1DRC_SIG_DET_MODE_MASK 0x0080 /* DSP1DRC_SIG_DET_MODE */ +#define WM8996_DSP1DRC_SIG_DET_MODE_SHIFT 7 /* DSP1DRC_SIG_DET_MODE */ +#define WM8996_DSP1DRC_SIG_DET_MODE_WIDTH 1 /* DSP1DRC_SIG_DET_MODE */ +#define WM8996_DSP1DRC_SIG_DET 0x0040 /* DSP1DRC_SIG_DET */ +#define WM8996_DSP1DRC_SIG_DET_MASK 0x0040 /* DSP1DRC_SIG_DET */ +#define WM8996_DSP1DRC_SIG_DET_SHIFT 6 /* DSP1DRC_SIG_DET */ +#define WM8996_DSP1DRC_SIG_DET_WIDTH 1 /* DSP1DRC_SIG_DET */ +#define WM8996_DSP1DRC_KNEE2_OP_ENA 0x0020 /* DSP1DRC_KNEE2_OP_ENA */ +#define WM8996_DSP1DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP1DRC_KNEE2_OP_ENA */ +#define WM8996_DSP1DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP1DRC_KNEE2_OP_ENA */ +#define WM8996_DSP1DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP1DRC_KNEE2_OP_ENA */ +#define WM8996_DSP1DRC_QR 0x0010 /* DSP1DRC_QR */ +#define WM8996_DSP1DRC_QR_MASK 0x0010 /* DSP1DRC_QR */ +#define WM8996_DSP1DRC_QR_SHIFT 4 /* DSP1DRC_QR */ +#define WM8996_DSP1DRC_QR_WIDTH 1 /* DSP1DRC_QR */ +#define WM8996_DSP1DRC_ANTICLIP 0x0008 /* DSP1DRC_ANTICLIP */ +#define WM8996_DSP1DRC_ANTICLIP_MASK 0x0008 /* DSP1DRC_ANTICLIP */ +#define WM8996_DSP1DRC_ANTICLIP_SHIFT 3 /* DSP1DRC_ANTICLIP */ +#define WM8996_DSP1DRC_ANTICLIP_WIDTH 1 /* DSP1DRC_ANTICLIP */ +#define WM8996_DSP1RX_DRC_ENA 0x0004 /* DSP1RX_DRC_ENA */ +#define WM8996_DSP1RX_DRC_ENA_MASK 0x0004 /* DSP1RX_DRC_ENA */ +#define WM8996_DSP1RX_DRC_ENA_SHIFT 2 /* DSP1RX_DRC_ENA */ +#define WM8996_DSP1RX_DRC_ENA_WIDTH 1 /* DSP1RX_DRC_ENA */ +#define WM8996_DSP1TXL_DRC_ENA 0x0002 /* DSP1TXL_DRC_ENA */ +#define WM8996_DSP1TXL_DRC_ENA_MASK 0x0002 /* DSP1TXL_DRC_ENA */ +#define WM8996_DSP1TXL_DRC_ENA_SHIFT 1 /* DSP1TXL_DRC_ENA */ +#define WM8996_DSP1TXL_DRC_ENA_WIDTH 1 /* DSP1TXL_DRC_ENA */ +#define WM8996_DSP1TXR_DRC_ENA 0x0001 /* DSP1TXR_DRC_ENA */ +#define WM8996_DSP1TXR_DRC_ENA_MASK 0x0001 /* DSP1TXR_DRC_ENA */ +#define WM8996_DSP1TXR_DRC_ENA_SHIFT 0 /* DSP1TXR_DRC_ENA */ +#define WM8996_DSP1TXR_DRC_ENA_WIDTH 1 /* DSP1TXR_DRC_ENA */ + +/* + * R1089 (0x441) - DSP1 DRC (2) + */ +#define WM8996_DSP1DRC_ATK_MASK 0x1E00 /* DSP1DRC_ATK - [12:9] */ +#define WM8996_DSP1DRC_ATK_SHIFT 9 /* DSP1DRC_ATK - [12:9] */ +#define WM8996_DSP1DRC_ATK_WIDTH 4 /* DSP1DRC_ATK - [12:9] */ +#define WM8996_DSP1DRC_DCY_MASK 0x01E0 /* DSP1DRC_DCY - [8:5] */ +#define WM8996_DSP1DRC_DCY_SHIFT 5 /* DSP1DRC_DCY - [8:5] */ +#define WM8996_DSP1DRC_DCY_WIDTH 4 /* DSP1DRC_DCY - [8:5] */ +#define WM8996_DSP1DRC_MINGAIN_MASK 0x001C /* DSP1DRC_MINGAIN - [4:2] */ +#define WM8996_DSP1DRC_MINGAIN_SHIFT 2 /* DSP1DRC_MINGAIN - [4:2] */ +#define WM8996_DSP1DRC_MINGAIN_WIDTH 3 /* DSP1DRC_MINGAIN - [4:2] */ +#define WM8996_DSP1DRC_MAXGAIN_MASK 0x0003 /* DSP1DRC_MAXGAIN - [1:0] */ +#define WM8996_DSP1DRC_MAXGAIN_SHIFT 0 /* DSP1DRC_MAXGAIN - [1:0] */ +#define WM8996_DSP1DRC_MAXGAIN_WIDTH 2 /* DSP1DRC_MAXGAIN - [1:0] */ + +/* + * R1090 (0x442) - DSP1 DRC (3) + */ +#define WM8996_DSP1DRC_NG_MINGAIN_MASK 0xF000 /* DSP1DRC_NG_MINGAIN - [15:12] */ +#define WM8996_DSP1DRC_NG_MINGAIN_SHIFT 12 /* DSP1DRC_NG_MINGAIN - [15:12] */ +#define WM8996_DSP1DRC_NG_MINGAIN_WIDTH 4 /* DSP1DRC_NG_MINGAIN - [15:12] */ +#define WM8996_DSP1DRC_NG_EXP_MASK 0x0C00 /* DSP1DRC_NG_EXP - [11:10] */ +#define WM8996_DSP1DRC_NG_EXP_SHIFT 10 /* DSP1DRC_NG_EXP - [11:10] */ +#define WM8996_DSP1DRC_NG_EXP_WIDTH 2 /* DSP1DRC_NG_EXP - [11:10] */ +#define WM8996_DSP1DRC_QR_THR_MASK 0x0300 /* DSP1DRC_QR_THR - [9:8] */ +#define WM8996_DSP1DRC_QR_THR_SHIFT 8 /* DSP1DRC_QR_THR - [9:8] */ +#define WM8996_DSP1DRC_QR_THR_WIDTH 2 /* DSP1DRC_QR_THR - [9:8] */ +#define WM8996_DSP1DRC_QR_DCY_MASK 0x00C0 /* DSP1DRC_QR_DCY - [7:6] */ +#define WM8996_DSP1DRC_QR_DCY_SHIFT 6 /* DSP1DRC_QR_DCY - [7:6] */ +#define WM8996_DSP1DRC_QR_DCY_WIDTH 2 /* DSP1DRC_QR_DCY - [7:6] */ +#define WM8996_DSP1DRC_HI_COMP_MASK 0x0038 /* DSP1DRC_HI_COMP - [5:3] */ +#define WM8996_DSP1DRC_HI_COMP_SHIFT 3 /* DSP1DRC_HI_COMP - [5:3] */ +#define WM8996_DSP1DRC_HI_COMP_WIDTH 3 /* DSP1DRC_HI_COMP - [5:3] */ +#define WM8996_DSP1DRC_LO_COMP_MASK 0x0007 /* DSP1DRC_LO_COMP - [2:0] */ +#define WM8996_DSP1DRC_LO_COMP_SHIFT 0 /* DSP1DRC_LO_COMP - [2:0] */ +#define WM8996_DSP1DRC_LO_COMP_WIDTH 3 /* DSP1DRC_LO_COMP - [2:0] */ + +/* + * R1091 (0x443) - DSP1 DRC (4) + */ +#define WM8996_DSP1DRC_KNEE_IP_MASK 0x07E0 /* DSP1DRC_KNEE_IP - [10:5] */ +#define WM8996_DSP1DRC_KNEE_IP_SHIFT 5 /* DSP1DRC_KNEE_IP - [10:5] */ +#define WM8996_DSP1DRC_KNEE_IP_WIDTH 6 /* DSP1DRC_KNEE_IP - [10:5] */ +#define WM8996_DSP1DRC_KNEE_OP_MASK 0x001F /* DSP1DRC_KNEE_OP - [4:0] */ +#define WM8996_DSP1DRC_KNEE_OP_SHIFT 0 /* DSP1DRC_KNEE_OP - [4:0] */ +#define WM8996_DSP1DRC_KNEE_OP_WIDTH 5 /* DSP1DRC_KNEE_OP - [4:0] */ + +/* + * R1092 (0x444) - DSP1 DRC (5) + */ +#define WM8996_DSP1DRC_KNEE2_IP_MASK 0x03E0 /* DSP1DRC_KNEE2_IP - [9:5] */ +#define WM8996_DSP1DRC_KNEE2_IP_SHIFT 5 /* DSP1DRC_KNEE2_IP - [9:5] */ +#define WM8996_DSP1DRC_KNEE2_IP_WIDTH 5 /* DSP1DRC_KNEE2_IP - [9:5] */ +#define WM8996_DSP1DRC_KNEE2_OP_MASK 0x001F /* DSP1DRC_KNEE2_OP - [4:0] */ +#define WM8996_DSP1DRC_KNEE2_OP_SHIFT 0 /* DSP1DRC_KNEE2_OP - [4:0] */ +#define WM8996_DSP1DRC_KNEE2_OP_WIDTH 5 /* DSP1DRC_KNEE2_OP - [4:0] */ + +/* + * R1152 (0x480) - DSP1 RX EQ Gains (1) + */ +#define WM8996_DSP1RX_EQ_B1_GAIN_MASK 0xF800 /* DSP1RX_EQ_B1_GAIN - [15:11] */ +#define WM8996_DSP1RX_EQ_B1_GAIN_SHIFT 11 /* DSP1RX_EQ_B1_GAIN - [15:11] */ +#define WM8996_DSP1RX_EQ_B1_GAIN_WIDTH 5 /* DSP1RX_EQ_B1_GAIN - [15:11] */ +#define WM8996_DSP1RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B2_GAIN - [10:6] */ +#define WM8996_DSP1RX_EQ_B2_GAIN_SHIFT 6 /* DSP1RX_EQ_B2_GAIN - [10:6] */ +#define WM8996_DSP1RX_EQ_B2_GAIN_WIDTH 5 /* DSP1RX_EQ_B2_GAIN - [10:6] */ +#define WM8996_DSP1RX_EQ_B3_GAIN_MASK 0x003E /* DSP1RX_EQ_B3_GAIN - [5:1] */ +#define WM8996_DSP1RX_EQ_B3_GAIN_SHIFT 1 /* DSP1RX_EQ_B3_GAIN - [5:1] */ +#define WM8996_DSP1RX_EQ_B3_GAIN_WIDTH 5 /* DSP1RX_EQ_B3_GAIN - [5:1] */ +#define WM8996_DSP1RX_EQ_ENA 0x0001 /* DSP1RX_EQ_ENA */ +#define WM8996_DSP1RX_EQ_ENA_MASK 0x0001 /* DSP1RX_EQ_ENA */ +#define WM8996_DSP1RX_EQ_ENA_SHIFT 0 /* DSP1RX_EQ_ENA */ +#define WM8996_DSP1RX_EQ_ENA_WIDTH 1 /* DSP1RX_EQ_ENA */ + +/* + * R1153 (0x481) - DSP1 RX EQ Gains (2) + */ +#define WM8996_DSP1RX_EQ_B4_GAIN_MASK 0xF800 /* DSP1RX_EQ_B4_GAIN - [15:11] */ +#define WM8996_DSP1RX_EQ_B4_GAIN_SHIFT 11 /* DSP1RX_EQ_B4_GAIN - [15:11] */ +#define WM8996_DSP1RX_EQ_B4_GAIN_WIDTH 5 /* DSP1RX_EQ_B4_GAIN - [15:11] */ +#define WM8996_DSP1RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP1RX_EQ_B5_GAIN - [10:6] */ +#define WM8996_DSP1RX_EQ_B5_GAIN_SHIFT 6 /* DSP1RX_EQ_B5_GAIN - [10:6] */ +#define WM8996_DSP1RX_EQ_B5_GAIN_WIDTH 5 /* DSP1RX_EQ_B5_GAIN - [10:6] */ + +/* + * R1154 (0x482) - DSP1 RX EQ Band 1 A + */ +#define WM8996_DSP1RX_EQ_B1_A_MASK 0xFFFF /* DSP1RX_EQ_B1_A - [15:0] */ +#define WM8996_DSP1RX_EQ_B1_A_SHIFT 0 /* DSP1RX_EQ_B1_A - [15:0] */ +#define WM8996_DSP1RX_EQ_B1_A_WIDTH 16 /* DSP1RX_EQ_B1_A - [15:0] */ + +/* + * R1155 (0x483) - DSP1 RX EQ Band 1 B + */ +#define WM8996_DSP1RX_EQ_B1_B_MASK 0xFFFF /* DSP1RX_EQ_B1_B - [15:0] */ +#define WM8996_DSP1RX_EQ_B1_B_SHIFT 0 /* DSP1RX_EQ_B1_B - [15:0] */ +#define WM8996_DSP1RX_EQ_B1_B_WIDTH 16 /* DSP1RX_EQ_B1_B - [15:0] */ + +/* + * R1156 (0x484) - DSP1 RX EQ Band 1 PG + */ +#define WM8996_DSP1RX_EQ_B1_PG_MASK 0xFFFF /* DSP1RX_EQ_B1_PG - [15:0] */ +#define WM8996_DSP1RX_EQ_B1_PG_SHIFT 0 /* DSP1RX_EQ_B1_PG - [15:0] */ +#define WM8996_DSP1RX_EQ_B1_PG_WIDTH 16 /* DSP1RX_EQ_B1_PG - [15:0] */ + +/* + * R1157 (0x485) - DSP1 RX EQ Band 2 A + */ +#define WM8996_DSP1RX_EQ_B2_A_MASK 0xFFFF /* DSP1RX_EQ_B2_A - [15:0] */ +#define WM8996_DSP1RX_EQ_B2_A_SHIFT 0 /* DSP1RX_EQ_B2_A - [15:0] */ +#define WM8996_DSP1RX_EQ_B2_A_WIDTH 16 /* DSP1RX_EQ_B2_A - [15:0] */ + +/* + * R1158 (0x486) - DSP1 RX EQ Band 2 B + */ +#define WM8996_DSP1RX_EQ_B2_B_MASK 0xFFFF /* DSP1RX_EQ_B2_B - [15:0] */ +#define WM8996_DSP1RX_EQ_B2_B_SHIFT 0 /* DSP1RX_EQ_B2_B - [15:0] */ +#define WM8996_DSP1RX_EQ_B2_B_WIDTH 16 /* DSP1RX_EQ_B2_B - [15:0] */ + +/* + * R1159 (0x487) - DSP1 RX EQ Band 2 C + */ +#define WM8996_DSP1RX_EQ_B2_C_MASK 0xFFFF /* DSP1RX_EQ_B2_C - [15:0] */ +#define WM8996_DSP1RX_EQ_B2_C_SHIFT 0 /* DSP1RX_EQ_B2_C - [15:0] */ +#define WM8996_DSP1RX_EQ_B2_C_WIDTH 16 /* DSP1RX_EQ_B2_C - [15:0] */ + +/* + * R1160 (0x488) - DSP1 RX EQ Band 2 PG + */ +#define WM8996_DSP1RX_EQ_B2_PG_MASK 0xFFFF /* DSP1RX_EQ_B2_PG - [15:0] */ +#define WM8996_DSP1RX_EQ_B2_PG_SHIFT 0 /* DSP1RX_EQ_B2_PG - [15:0] */ +#define WM8996_DSP1RX_EQ_B2_PG_WIDTH 16 /* DSP1RX_EQ_B2_PG - [15:0] */ + +/* + * R1161 (0x489) - DSP1 RX EQ Band 3 A + */ +#define WM8996_DSP1RX_EQ_B3_A_MASK 0xFFFF /* DSP1RX_EQ_B3_A - [15:0] */ +#define WM8996_DSP1RX_EQ_B3_A_SHIFT 0 /* DSP1RX_EQ_B3_A - [15:0] */ +#define WM8996_DSP1RX_EQ_B3_A_WIDTH 16 /* DSP1RX_EQ_B3_A - [15:0] */ + +/* + * R1162 (0x48A) - DSP1 RX EQ Band 3 B + */ +#define WM8996_DSP1RX_EQ_B3_B_MASK 0xFFFF /* DSP1RX_EQ_B3_B - [15:0] */ +#define WM8996_DSP1RX_EQ_B3_B_SHIFT 0 /* DSP1RX_EQ_B3_B - [15:0] */ +#define WM8996_DSP1RX_EQ_B3_B_WIDTH 16 /* DSP1RX_EQ_B3_B - [15:0] */ + +/* + * R1163 (0x48B) - DSP1 RX EQ Band 3 C + */ +#define WM8996_DSP1RX_EQ_B3_C_MASK 0xFFFF /* DSP1RX_EQ_B3_C - [15:0] */ +#define WM8996_DSP1RX_EQ_B3_C_SHIFT 0 /* DSP1RX_EQ_B3_C - [15:0] */ +#define WM8996_DSP1RX_EQ_B3_C_WIDTH 16 /* DSP1RX_EQ_B3_C - [15:0] */ + +/* + * R1164 (0x48C) - DSP1 RX EQ Band 3 PG + */ +#define WM8996_DSP1RX_EQ_B3_PG_MASK 0xFFFF /* DSP1RX_EQ_B3_PG - [15:0] */ +#define WM8996_DSP1RX_EQ_B3_PG_SHIFT 0 /* DSP1RX_EQ_B3_PG - [15:0] */ +#define WM8996_DSP1RX_EQ_B3_PG_WIDTH 16 /* DSP1RX_EQ_B3_PG - [15:0] */ + +/* + * R1165 (0x48D) - DSP1 RX EQ Band 4 A + */ +#define WM8996_DSP1RX_EQ_B4_A_MASK 0xFFFF /* DSP1RX_EQ_B4_A - [15:0] */ +#define WM8996_DSP1RX_EQ_B4_A_SHIFT 0 /* DSP1RX_EQ_B4_A - [15:0] */ +#define WM8996_DSP1RX_EQ_B4_A_WIDTH 16 /* DSP1RX_EQ_B4_A - [15:0] */ + +/* + * R1166 (0x48E) - DSP1 RX EQ Band 4 B + */ +#define WM8996_DSP1RX_EQ_B4_B_MASK 0xFFFF /* DSP1RX_EQ_B4_B - [15:0] */ +#define WM8996_DSP1RX_EQ_B4_B_SHIFT 0 /* DSP1RX_EQ_B4_B - [15:0] */ +#define WM8996_DSP1RX_EQ_B4_B_WIDTH 16 /* DSP1RX_EQ_B4_B - [15:0] */ + +/* + * R1167 (0x48F) - DSP1 RX EQ Band 4 C + */ +#define WM8996_DSP1RX_EQ_B4_C_MASK 0xFFFF /* DSP1RX_EQ_B4_C - [15:0] */ +#define WM8996_DSP1RX_EQ_B4_C_SHIFT 0 /* DSP1RX_EQ_B4_C - [15:0] */ +#define WM8996_DSP1RX_EQ_B4_C_WIDTH 16 /* DSP1RX_EQ_B4_C - [15:0] */ + +/* + * R1168 (0x490) - DSP1 RX EQ Band 4 PG + */ +#define WM8996_DSP1RX_EQ_B4_PG_MASK 0xFFFF /* DSP1RX_EQ_B4_PG - [15:0] */ +#define WM8996_DSP1RX_EQ_B4_PG_SHIFT 0 /* DSP1RX_EQ_B4_PG - [15:0] */ +#define WM8996_DSP1RX_EQ_B4_PG_WIDTH 16 /* DSP1RX_EQ_B4_PG - [15:0] */ + +/* + * R1169 (0x491) - DSP1 RX EQ Band 5 A + */ +#define WM8996_DSP1RX_EQ_B5_A_MASK 0xFFFF /* DSP1RX_EQ_B5_A - [15:0] */ +#define WM8996_DSP1RX_EQ_B5_A_SHIFT 0 /* DSP1RX_EQ_B5_A - [15:0] */ +#define WM8996_DSP1RX_EQ_B5_A_WIDTH 16 /* DSP1RX_EQ_B5_A - [15:0] */ + +/* + * R1170 (0x492) - DSP1 RX EQ Band 5 B + */ +#define WM8996_DSP1RX_EQ_B5_B_MASK 0xFFFF /* DSP1RX_EQ_B5_B - [15:0] */ +#define WM8996_DSP1RX_EQ_B5_B_SHIFT 0 /* DSP1RX_EQ_B5_B - [15:0] */ +#define WM8996_DSP1RX_EQ_B5_B_WIDTH 16 /* DSP1RX_EQ_B5_B - [15:0] */ + +/* + * R1171 (0x493) - DSP1 RX EQ Band 5 PG + */ +#define WM8996_DSP1RX_EQ_B5_PG_MASK 0xFFFF /* DSP1RX_EQ_B5_PG - [15:0] */ +#define WM8996_DSP1RX_EQ_B5_PG_SHIFT 0 /* DSP1RX_EQ_B5_PG - [15:0] */ +#define WM8996_DSP1RX_EQ_B5_PG_WIDTH 16 /* DSP1RX_EQ_B5_PG - [15:0] */ + +/* + * R1280 (0x500) - DSP2 TX Left Volume + */ +#define WM8996_DSP2TX_VU 0x0100 /* DSP2TX_VU */ +#define WM8996_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */ +#define WM8996_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */ +#define WM8996_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */ +#define WM8996_DSP2TXL_VOL_MASK 0x00FF /* DSP2TXL_VOL - [7:0] */ +#define WM8996_DSP2TXL_VOL_SHIFT 0 /* DSP2TXL_VOL - [7:0] */ +#define WM8996_DSP2TXL_VOL_WIDTH 8 /* DSP2TXL_VOL - [7:0] */ + +/* + * R1281 (0x501) - DSP2 TX Right Volume + */ +#define WM8996_DSP2TX_VU 0x0100 /* DSP2TX_VU */ +#define WM8996_DSP2TX_VU_MASK 0x0100 /* DSP2TX_VU */ +#define WM8996_DSP2TX_VU_SHIFT 8 /* DSP2TX_VU */ +#define WM8996_DSP2TX_VU_WIDTH 1 /* DSP2TX_VU */ +#define WM8996_DSP2TXR_VOL_MASK 0x00FF /* DSP2TXR_VOL - [7:0] */ +#define WM8996_DSP2TXR_VOL_SHIFT 0 /* DSP2TXR_VOL - [7:0] */ +#define WM8996_DSP2TXR_VOL_WIDTH 8 /* DSP2TXR_VOL - [7:0] */ + +/* + * R1282 (0x502) - DSP2 RX Left Volume + */ +#define WM8996_DSP2RX_VU 0x0100 /* DSP2RX_VU */ +#define WM8996_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */ +#define WM8996_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */ +#define WM8996_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */ +#define WM8996_DSP2RXL_VOL_MASK 0x00FF /* DSP2RXL_VOL - [7:0] */ +#define WM8996_DSP2RXL_VOL_SHIFT 0 /* DSP2RXL_VOL - [7:0] */ +#define WM8996_DSP2RXL_VOL_WIDTH 8 /* DSP2RXL_VOL - [7:0] */ + +/* + * R1283 (0x503) - DSP2 RX Right Volume + */ +#define WM8996_DSP2RX_VU 0x0100 /* DSP2RX_VU */ +#define WM8996_DSP2RX_VU_MASK 0x0100 /* DSP2RX_VU */ +#define WM8996_DSP2RX_VU_SHIFT 8 /* DSP2RX_VU */ +#define WM8996_DSP2RX_VU_WIDTH 1 /* DSP2RX_VU */ +#define WM8996_DSP2RXR_VOL_MASK 0x00FF /* DSP2RXR_VOL - [7:0] */ +#define WM8996_DSP2RXR_VOL_SHIFT 0 /* DSP2RXR_VOL - [7:0] */ +#define WM8996_DSP2RXR_VOL_WIDTH 8 /* DSP2RXR_VOL - [7:0] */ + +/* + * R1296 (0x510) - DSP2 TX Filters + */ +#define WM8996_DSP2TX_NF 0x2000 /* DSP2TX_NF */ +#define WM8996_DSP2TX_NF_MASK 0x2000 /* DSP2TX_NF */ +#define WM8996_DSP2TX_NF_SHIFT 13 /* DSP2TX_NF */ +#define WM8996_DSP2TX_NF_WIDTH 1 /* DSP2TX_NF */ +#define WM8996_DSP2TXL_HPF 0x1000 /* DSP2TXL_HPF */ +#define WM8996_DSP2TXL_HPF_MASK 0x1000 /* DSP2TXL_HPF */ +#define WM8996_DSP2TXL_HPF_SHIFT 12 /* DSP2TXL_HPF */ +#define WM8996_DSP2TXL_HPF_WIDTH 1 /* DSP2TXL_HPF */ +#define WM8996_DSP2TXR_HPF 0x0800 /* DSP2TXR_HPF */ +#define WM8996_DSP2TXR_HPF_MASK 0x0800 /* DSP2TXR_HPF */ +#define WM8996_DSP2TXR_HPF_SHIFT 11 /* DSP2TXR_HPF */ +#define WM8996_DSP2TXR_HPF_WIDTH 1 /* DSP2TXR_HPF */ +#define WM8996_DSP2TX_HPF_MODE_MASK 0x0018 /* DSP2TX_HPF_MODE - [4:3] */ +#define WM8996_DSP2TX_HPF_MODE_SHIFT 3 /* DSP2TX_HPF_MODE - [4:3] */ +#define WM8996_DSP2TX_HPF_MODE_WIDTH 2 /* DSP2TX_HPF_MODE - [4:3] */ +#define WM8996_DSP2TX_HPF_CUT_MASK 0x0007 /* DSP2TX_HPF_CUT - [2:0] */ +#define WM8996_DSP2TX_HPF_CUT_SHIFT 0 /* DSP2TX_HPF_CUT - [2:0] */ +#define WM8996_DSP2TX_HPF_CUT_WIDTH 3 /* DSP2TX_HPF_CUT - [2:0] */ + +/* + * R1312 (0x520) - DSP2 RX Filters (1) + */ +#define WM8996_DSP2RX_MUTE 0x0200 /* DSP2RX_MUTE */ +#define WM8996_DSP2RX_MUTE_MASK 0x0200 /* DSP2RX_MUTE */ +#define WM8996_DSP2RX_MUTE_SHIFT 9 /* DSP2RX_MUTE */ +#define WM8996_DSP2RX_MUTE_WIDTH 1 /* DSP2RX_MUTE */ +#define WM8996_DSP2RX_MONO 0x0080 /* DSP2RX_MONO */ +#define WM8996_DSP2RX_MONO_MASK 0x0080 /* DSP2RX_MONO */ +#define WM8996_DSP2RX_MONO_SHIFT 7 /* DSP2RX_MONO */ +#define WM8996_DSP2RX_MONO_WIDTH 1 /* DSP2RX_MONO */ +#define WM8996_DSP2RX_MUTERATE 0x0020 /* DSP2RX_MUTERATE */ +#define WM8996_DSP2RX_MUTERATE_MASK 0x0020 /* DSP2RX_MUTERATE */ +#define WM8996_DSP2RX_MUTERATE_SHIFT 5 /* DSP2RX_MUTERATE */ +#define WM8996_DSP2RX_MUTERATE_WIDTH 1 /* DSP2RX_MUTERATE */ +#define WM8996_DSP2RX_UNMUTE_RAMP 0x0010 /* DSP2RX_UNMUTE_RAMP */ +#define WM8996_DSP2RX_UNMUTE_RAMP_MASK 0x0010 /* DSP2RX_UNMUTE_RAMP */ +#define WM8996_DSP2RX_UNMUTE_RAMP_SHIFT 4 /* DSP2RX_UNMUTE_RAMP */ +#define WM8996_DSP2RX_UNMUTE_RAMP_WIDTH 1 /* DSP2RX_UNMUTE_RAMP */ + +/* + * R1313 (0x521) - DSP2 RX Filters (2) + */ +#define WM8996_DSP2RX_3D_GAIN_MASK 0x3E00 /* DSP2RX_3D_GAIN - [13:9] */ +#define WM8996_DSP2RX_3D_GAIN_SHIFT 9 /* DSP2RX_3D_GAIN - [13:9] */ +#define WM8996_DSP2RX_3D_GAIN_WIDTH 5 /* DSP2RX_3D_GAIN - [13:9] */ +#define WM8996_DSP2RX_3D_ENA 0x0100 /* DSP2RX_3D_ENA */ +#define WM8996_DSP2RX_3D_ENA_MASK 0x0100 /* DSP2RX_3D_ENA */ +#define WM8996_DSP2RX_3D_ENA_SHIFT 8 /* DSP2RX_3D_ENA */ +#define WM8996_DSP2RX_3D_ENA_WIDTH 1 /* DSP2RX_3D_ENA */ + +/* + * R1344 (0x540) - DSP2 DRC (1) + */ +#define WM8996_DSP2DRC_SIG_DET_RMS_MASK 0xF800 /* DSP2DRC_SIG_DET_RMS - [15:11] */ +#define WM8996_DSP2DRC_SIG_DET_RMS_SHIFT 11 /* DSP2DRC_SIG_DET_RMS - [15:11] */ +#define WM8996_DSP2DRC_SIG_DET_RMS_WIDTH 5 /* DSP2DRC_SIG_DET_RMS - [15:11] */ +#define WM8996_DSP2DRC_SIG_DET_PK_MASK 0x0600 /* DSP2DRC_SIG_DET_PK - [10:9] */ +#define WM8996_DSP2DRC_SIG_DET_PK_SHIFT 9 /* DSP2DRC_SIG_DET_PK - [10:9] */ +#define WM8996_DSP2DRC_SIG_DET_PK_WIDTH 2 /* DSP2DRC_SIG_DET_PK - [10:9] */ +#define WM8996_DSP2DRC_NG_ENA 0x0100 /* DSP2DRC_NG_ENA */ +#define WM8996_DSP2DRC_NG_ENA_MASK 0x0100 /* DSP2DRC_NG_ENA */ +#define WM8996_DSP2DRC_NG_ENA_SHIFT 8 /* DSP2DRC_NG_ENA */ +#define WM8996_DSP2DRC_NG_ENA_WIDTH 1 /* DSP2DRC_NG_ENA */ +#define WM8996_DSP2DRC_SIG_DET_MODE 0x0080 /* DSP2DRC_SIG_DET_MODE */ +#define WM8996_DSP2DRC_SIG_DET_MODE_MASK 0x0080 /* DSP2DRC_SIG_DET_MODE */ +#define WM8996_DSP2DRC_SIG_DET_MODE_SHIFT 7 /* DSP2DRC_SIG_DET_MODE */ +#define WM8996_DSP2DRC_SIG_DET_MODE_WIDTH 1 /* DSP2DRC_SIG_DET_MODE */ +#define WM8996_DSP2DRC_SIG_DET 0x0040 /* DSP2DRC_SIG_DET */ +#define WM8996_DSP2DRC_SIG_DET_MASK 0x0040 /* DSP2DRC_SIG_DET */ +#define WM8996_DSP2DRC_SIG_DET_SHIFT 6 /* DSP2DRC_SIG_DET */ +#define WM8996_DSP2DRC_SIG_DET_WIDTH 1 /* DSP2DRC_SIG_DET */ +#define WM8996_DSP2DRC_KNEE2_OP_ENA 0x0020 /* DSP2DRC_KNEE2_OP_ENA */ +#define WM8996_DSP2DRC_KNEE2_OP_ENA_MASK 0x0020 /* DSP2DRC_KNEE2_OP_ENA */ +#define WM8996_DSP2DRC_KNEE2_OP_ENA_SHIFT 5 /* DSP2DRC_KNEE2_OP_ENA */ +#define WM8996_DSP2DRC_KNEE2_OP_ENA_WIDTH 1 /* DSP2DRC_KNEE2_OP_ENA */ +#define WM8996_DSP2DRC_QR 0x0010 /* DSP2DRC_QR */ +#define WM8996_DSP2DRC_QR_MASK 0x0010 /* DSP2DRC_QR */ +#define WM8996_DSP2DRC_QR_SHIFT 4 /* DSP2DRC_QR */ +#define WM8996_DSP2DRC_QR_WIDTH 1 /* DSP2DRC_QR */ +#define WM8996_DSP2DRC_ANTICLIP 0x0008 /* DSP2DRC_ANTICLIP */ +#define WM8996_DSP2DRC_ANTICLIP_MASK 0x0008 /* DSP2DRC_ANTICLIP */ +#define WM8996_DSP2DRC_ANTICLIP_SHIFT 3 /* DSP2DRC_ANTICLIP */ +#define WM8996_DSP2DRC_ANTICLIP_WIDTH 1 /* DSP2DRC_ANTICLIP */ +#define WM8996_DSP2RX_DRC_ENA 0x0004 /* DSP2RX_DRC_ENA */ +#define WM8996_DSP2RX_DRC_ENA_MASK 0x0004 /* DSP2RX_DRC_ENA */ +#define WM8996_DSP2RX_DRC_ENA_SHIFT 2 /* DSP2RX_DRC_ENA */ +#define WM8996_DSP2RX_DRC_ENA_WIDTH 1 /* DSP2RX_DRC_ENA */ +#define WM8996_DSP2TXL_DRC_ENA 0x0002 /* DSP2TXL_DRC_ENA */ +#define WM8996_DSP2TXL_DRC_ENA_MASK 0x0002 /* DSP2TXL_DRC_ENA */ +#define WM8996_DSP2TXL_DRC_ENA_SHIFT 1 /* DSP2TXL_DRC_ENA */ +#define WM8996_DSP2TXL_DRC_ENA_WIDTH 1 /* DSP2TXL_DRC_ENA */ +#define WM8996_DSP2TXR_DRC_ENA 0x0001 /* DSP2TXR_DRC_ENA */ +#define WM8996_DSP2TXR_DRC_ENA_MASK 0x0001 /* DSP2TXR_DRC_ENA */ +#define WM8996_DSP2TXR_DRC_ENA_SHIFT 0 /* DSP2TXR_DRC_ENA */ +#define WM8996_DSP2TXR_DRC_ENA_WIDTH 1 /* DSP2TXR_DRC_ENA */ + +/* + * R1345 (0x541) - DSP2 DRC (2) + */ +#define WM8996_DSP2DRC_ATK_MASK 0x1E00 /* DSP2DRC_ATK - [12:9] */ +#define WM8996_DSP2DRC_ATK_SHIFT 9 /* DSP2DRC_ATK - [12:9] */ +#define WM8996_DSP2DRC_ATK_WIDTH 4 /* DSP2DRC_ATK - [12:9] */ +#define WM8996_DSP2DRC_DCY_MASK 0x01E0 /* DSP2DRC_DCY - [8:5] */ +#define WM8996_DSP2DRC_DCY_SHIFT 5 /* DSP2DRC_DCY - [8:5] */ +#define WM8996_DSP2DRC_DCY_WIDTH 4 /* DSP2DRC_DCY - [8:5] */ +#define WM8996_DSP2DRC_MINGAIN_MASK 0x001C /* DSP2DRC_MINGAIN - [4:2] */ +#define WM8996_DSP2DRC_MINGAIN_SHIFT 2 /* DSP2DRC_MINGAIN - [4:2] */ +#define WM8996_DSP2DRC_MINGAIN_WIDTH 3 /* DSP2DRC_MINGAIN - [4:2] */ +#define WM8996_DSP2DRC_MAXGAIN_MASK 0x0003 /* DSP2DRC_MAXGAIN - [1:0] */ +#define WM8996_DSP2DRC_MAXGAIN_SHIFT 0 /* DSP2DRC_MAXGAIN - [1:0] */ +#define WM8996_DSP2DRC_MAXGAIN_WIDTH 2 /* DSP2DRC_MAXGAIN - [1:0] */ + +/* + * R1346 (0x542) - DSP2 DRC (3) + */ +#define WM8996_DSP2DRC_NG_MINGAIN_MASK 0xF000 /* DSP2DRC_NG_MINGAIN - [15:12] */ +#define WM8996_DSP2DRC_NG_MINGAIN_SHIFT 12 /* DSP2DRC_NG_MINGAIN - [15:12] */ +#define WM8996_DSP2DRC_NG_MINGAIN_WIDTH 4 /* DSP2DRC_NG_MINGAIN - [15:12] */ +#define WM8996_DSP2DRC_NG_EXP_MASK 0x0C00 /* DSP2DRC_NG_EXP - [11:10] */ +#define WM8996_DSP2DRC_NG_EXP_SHIFT 10 /* DSP2DRC_NG_EXP - [11:10] */ +#define WM8996_DSP2DRC_NG_EXP_WIDTH 2 /* DSP2DRC_NG_EXP - [11:10] */ +#define WM8996_DSP2DRC_QR_THR_MASK 0x0300 /* DSP2DRC_QR_THR - [9:8] */ +#define WM8996_DSP2DRC_QR_THR_SHIFT 8 /* DSP2DRC_QR_THR - [9:8] */ +#define WM8996_DSP2DRC_QR_THR_WIDTH 2 /* DSP2DRC_QR_THR - [9:8] */ +#define WM8996_DSP2DRC_QR_DCY_MASK 0x00C0 /* DSP2DRC_QR_DCY - [7:6] */ +#define WM8996_DSP2DRC_QR_DCY_SHIFT 6 /* DSP2DRC_QR_DCY - [7:6] */ +#define WM8996_DSP2DRC_QR_DCY_WIDTH 2 /* DSP2DRC_QR_DCY - [7:6] */ +#define WM8996_DSP2DRC_HI_COMP_MASK 0x0038 /* DSP2DRC_HI_COMP - [5:3] */ +#define WM8996_DSP2DRC_HI_COMP_SHIFT 3 /* DSP2DRC_HI_COMP - [5:3] */ +#define WM8996_DSP2DRC_HI_COMP_WIDTH 3 /* DSP2DRC_HI_COMP - [5:3] */ +#define WM8996_DSP2DRC_LO_COMP_MASK 0x0007 /* DSP2DRC_LO_COMP - [2:0] */ +#define WM8996_DSP2DRC_LO_COMP_SHIFT 0 /* DSP2DRC_LO_COMP - [2:0] */ +#define WM8996_DSP2DRC_LO_COMP_WIDTH 3 /* DSP2DRC_LO_COMP - [2:0] */ + +/* + * R1347 (0x543) - DSP2 DRC (4) + */ +#define WM8996_DSP2DRC_KNEE_IP_MASK 0x07E0 /* DSP2DRC_KNEE_IP - [10:5] */ +#define WM8996_DSP2DRC_KNEE_IP_SHIFT 5 /* DSP2DRC_KNEE_IP - [10:5] */ +#define WM8996_DSP2DRC_KNEE_IP_WIDTH 6 /* DSP2DRC_KNEE_IP - [10:5] */ +#define WM8996_DSP2DRC_KNEE_OP_MASK 0x001F /* DSP2DRC_KNEE_OP - [4:0] */ +#define WM8996_DSP2DRC_KNEE_OP_SHIFT 0 /* DSP2DRC_KNEE_OP - [4:0] */ +#define WM8996_DSP2DRC_KNEE_OP_WIDTH 5 /* DSP2DRC_KNEE_OP - [4:0] */ + +/* + * R1348 (0x544) - DSP2 DRC (5) + */ +#define WM8996_DSP2DRC_KNEE2_IP_MASK 0x03E0 /* DSP2DRC_KNEE2_IP - [9:5] */ +#define WM8996_DSP2DRC_KNEE2_IP_SHIFT 5 /* DSP2DRC_KNEE2_IP - [9:5] */ +#define WM8996_DSP2DRC_KNEE2_IP_WIDTH 5 /* DSP2DRC_KNEE2_IP - [9:5] */ +#define WM8996_DSP2DRC_KNEE2_OP_MASK 0x001F /* DSP2DRC_KNEE2_OP - [4:0] */ +#define WM8996_DSP2DRC_KNEE2_OP_SHIFT 0 /* DSP2DRC_KNEE2_OP - [4:0] */ +#define WM8996_DSP2DRC_KNEE2_OP_WIDTH 5 /* DSP2DRC_KNEE2_OP - [4:0] */ + +/* + * R1408 (0x580) - DSP2 RX EQ Gains (1) + */ +#define WM8996_DSP2RX_EQ_B1_GAIN_MASK 0xF800 /* DSP2RX_EQ_B1_GAIN - [15:11] */ +#define WM8996_DSP2RX_EQ_B1_GAIN_SHIFT 11 /* DSP2RX_EQ_B1_GAIN - [15:11] */ +#define WM8996_DSP2RX_EQ_B1_GAIN_WIDTH 5 /* DSP2RX_EQ_B1_GAIN - [15:11] */ +#define WM8996_DSP2RX_EQ_B2_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B2_GAIN - [10:6] */ +#define WM8996_DSP2RX_EQ_B2_GAIN_SHIFT 6 /* DSP2RX_EQ_B2_GAIN - [10:6] */ +#define WM8996_DSP2RX_EQ_B2_GAIN_WIDTH 5 /* DSP2RX_EQ_B2_GAIN - [10:6] */ +#define WM8996_DSP2RX_EQ_B3_GAIN_MASK 0x003E /* DSP2RX_EQ_B3_GAIN - [5:1] */ +#define WM8996_DSP2RX_EQ_B3_GAIN_SHIFT 1 /* DSP2RX_EQ_B3_GAIN - [5:1] */ +#define WM8996_DSP2RX_EQ_B3_GAIN_WIDTH 5 /* DSP2RX_EQ_B3_GAIN - [5:1] */ +#define WM8996_DSP2RX_EQ_ENA 0x0001 /* DSP2RX_EQ_ENA */ +#define WM8996_DSP2RX_EQ_ENA_MASK 0x0001 /* DSP2RX_EQ_ENA */ +#define WM8996_DSP2RX_EQ_ENA_SHIFT 0 /* DSP2RX_EQ_ENA */ +#define WM8996_DSP2RX_EQ_ENA_WIDTH 1 /* DSP2RX_EQ_ENA */ + +/* + * R1409 (0x581) - DSP2 RX EQ Gains (2) + */ +#define WM8996_DSP2RX_EQ_B4_GAIN_MASK 0xF800 /* DSP2RX_EQ_B4_GAIN - [15:11] */ +#define WM8996_DSP2RX_EQ_B4_GAIN_SHIFT 11 /* DSP2RX_EQ_B4_GAIN - [15:11] */ +#define WM8996_DSP2RX_EQ_B4_GAIN_WIDTH 5 /* DSP2RX_EQ_B4_GAIN - [15:11] */ +#define WM8996_DSP2RX_EQ_B5_GAIN_MASK 0x07C0 /* DSP2RX_EQ_B5_GAIN - [10:6] */ +#define WM8996_DSP2RX_EQ_B5_GAIN_SHIFT 6 /* DSP2RX_EQ_B5_GAIN - [10:6] */ +#define WM8996_DSP2RX_EQ_B5_GAIN_WIDTH 5 /* DSP2RX_EQ_B5_GAIN - [10:6] */ + +/* + * R1410 (0x582) - DSP2 RX EQ Band 1 A + */ +#define WM8996_DSP2RX_EQ_B1_A_MASK 0xFFFF /* DSP2RX_EQ_B1_A - [15:0] */ +#define WM8996_DSP2RX_EQ_B1_A_SHIFT 0 /* DSP2RX_EQ_B1_A - [15:0] */ +#define WM8996_DSP2RX_EQ_B1_A_WIDTH 16 /* DSP2RX_EQ_B1_A - [15:0] */ + +/* + * R1411 (0x583) - DSP2 RX EQ Band 1 B + */ +#define WM8996_DSP2RX_EQ_B1_B_MASK 0xFFFF /* DSP2RX_EQ_B1_B - [15:0] */ +#define WM8996_DSP2RX_EQ_B1_B_SHIFT 0 /* DSP2RX_EQ_B1_B - [15:0] */ +#define WM8996_DSP2RX_EQ_B1_B_WIDTH 16 /* DSP2RX_EQ_B1_B - [15:0] */ + +/* + * R1412 (0x584) - DSP2 RX EQ Band 1 PG + */ +#define WM8996_DSP2RX_EQ_B1_PG_MASK 0xFFFF /* DSP2RX_EQ_B1_PG - [15:0] */ +#define WM8996_DSP2RX_EQ_B1_PG_SHIFT 0 /* DSP2RX_EQ_B1_PG - [15:0] */ +#define WM8996_DSP2RX_EQ_B1_PG_WIDTH 16 /* DSP2RX_EQ_B1_PG - [15:0] */ + +/* + * R1413 (0x585) - DSP2 RX EQ Band 2 A + */ +#define WM8996_DSP2RX_EQ_B2_A_MASK 0xFFFF /* DSP2RX_EQ_B2_A - [15:0] */ +#define WM8996_DSP2RX_EQ_B2_A_SHIFT 0 /* DSP2RX_EQ_B2_A - [15:0] */ +#define WM8996_DSP2RX_EQ_B2_A_WIDTH 16 /* DSP2RX_EQ_B2_A - [15:0] */ + +/* + * R1414 (0x586) - DSP2 RX EQ Band 2 B + */ +#define WM8996_DSP2RX_EQ_B2_B_MASK 0xFFFF /* DSP2RX_EQ_B2_B - [15:0] */ +#define WM8996_DSP2RX_EQ_B2_B_SHIFT 0 /* DSP2RX_EQ_B2_B - [15:0] */ +#define WM8996_DSP2RX_EQ_B2_B_WIDTH 16 /* DSP2RX_EQ_B2_B - [15:0] */ + +/* + * R1415 (0x587) - DSP2 RX EQ Band 2 C + */ +#define WM8996_DSP2RX_EQ_B2_C_MASK 0xFFFF /* DSP2RX_EQ_B2_C - [15:0] */ +#define WM8996_DSP2RX_EQ_B2_C_SHIFT 0 /* DSP2RX_EQ_B2_C - [15:0] */ +#define WM8996_DSP2RX_EQ_B2_C_WIDTH 16 /* DSP2RX_EQ_B2_C - [15:0] */ + +/* + * R1416 (0x588) - DSP2 RX EQ Band 2 PG + */ +#define WM8996_DSP2RX_EQ_B2_PG_MASK 0xFFFF /* DSP2RX_EQ_B2_PG - [15:0] */ +#define WM8996_DSP2RX_EQ_B2_PG_SHIFT 0 /* DSP2RX_EQ_B2_PG - [15:0] */ +#define WM8996_DSP2RX_EQ_B2_PG_WIDTH 16 /* DSP2RX_EQ_B2_PG - [15:0] */ + +/* + * R1417 (0x589) - DSP2 RX EQ Band 3 A + */ +#define WM8996_DSP2RX_EQ_B3_A_MASK 0xFFFF /* DSP2RX_EQ_B3_A - [15:0] */ +#define WM8996_DSP2RX_EQ_B3_A_SHIFT 0 /* DSP2RX_EQ_B3_A - [15:0] */ +#define WM8996_DSP2RX_EQ_B3_A_WIDTH 16 /* DSP2RX_EQ_B3_A - [15:0] */ + +/* + * R1418 (0x58A) - DSP2 RX EQ Band 3 B + */ +#define WM8996_DSP2RX_EQ_B3_B_MASK 0xFFFF /* DSP2RX_EQ_B3_B - [15:0] */ +#define WM8996_DSP2RX_EQ_B3_B_SHIFT 0 /* DSP2RX_EQ_B3_B - [15:0] */ +#define WM8996_DSP2RX_EQ_B3_B_WIDTH 16 /* DSP2RX_EQ_B3_B - [15:0] */ + +/* + * R1419 (0x58B) - DSP2 RX EQ Band 3 C + */ +#define WM8996_DSP2RX_EQ_B3_C_MASK 0xFFFF /* DSP2RX_EQ_B3_C - [15:0] */ +#define WM8996_DSP2RX_EQ_B3_C_SHIFT 0 /* DSP2RX_EQ_B3_C - [15:0] */ +#define WM8996_DSP2RX_EQ_B3_C_WIDTH 16 /* DSP2RX_EQ_B3_C - [15:0] */ + +/* + * R1420 (0x58C) - DSP2 RX EQ Band 3 PG + */ +#define WM8996_DSP2RX_EQ_B3_PG_MASK 0xFFFF /* DSP2RX_EQ_B3_PG - [15:0] */ +#define WM8996_DSP2RX_EQ_B3_PG_SHIFT 0 /* DSP2RX_EQ_B3_PG - [15:0] */ +#define WM8996_DSP2RX_EQ_B3_PG_WIDTH 16 /* DSP2RX_EQ_B3_PG - [15:0] */ + +/* + * R1421 (0x58D) - DSP2 RX EQ Band 4 A + */ +#define WM8996_DSP2RX_EQ_B4_A_MASK 0xFFFF /* DSP2RX_EQ_B4_A - [15:0] */ +#define WM8996_DSP2RX_EQ_B4_A_SHIFT 0 /* DSP2RX_EQ_B4_A - [15:0] */ +#define WM8996_DSP2RX_EQ_B4_A_WIDTH 16 /* DSP2RX_EQ_B4_A - [15:0] */ + +/* + * R1422 (0x58E) - DSP2 RX EQ Band 4 B + */ +#define WM8996_DSP2RX_EQ_B4_B_MASK 0xFFFF /* DSP2RX_EQ_B4_B - [15:0] */ +#define WM8996_DSP2RX_EQ_B4_B_SHIFT 0 /* DSP2RX_EQ_B4_B - [15:0] */ +#define WM8996_DSP2RX_EQ_B4_B_WIDTH 16 /* DSP2RX_EQ_B4_B - [15:0] */ + +/* + * R1423 (0x58F) - DSP2 RX EQ Band 4 C + */ +#define WM8996_DSP2RX_EQ_B4_C_MASK 0xFFFF /* DSP2RX_EQ_B4_C - [15:0] */ +#define WM8996_DSP2RX_EQ_B4_C_SHIFT 0 /* DSP2RX_EQ_B4_C - [15:0] */ +#define WM8996_DSP2RX_EQ_B4_C_WIDTH 16 /* DSP2RX_EQ_B4_C - [15:0] */ + +/* + * R1424 (0x590) - DSP2 RX EQ Band 4 PG + */ +#define WM8996_DSP2RX_EQ_B4_PG_MASK 0xFFFF /* DSP2RX_EQ_B4_PG - [15:0] */ +#define WM8996_DSP2RX_EQ_B4_PG_SHIFT 0 /* DSP2RX_EQ_B4_PG - [15:0] */ +#define WM8996_DSP2RX_EQ_B4_PG_WIDTH 16 /* DSP2RX_EQ_B4_PG - [15:0] */ + +/* + * R1425 (0x591) - DSP2 RX EQ Band 5 A + */ +#define WM8996_DSP2RX_EQ_B5_A_MASK 0xFFFF /* DSP2RX_EQ_B5_A - [15:0] */ +#define WM8996_DSP2RX_EQ_B5_A_SHIFT 0 /* DSP2RX_EQ_B5_A - [15:0] */ +#define WM8996_DSP2RX_EQ_B5_A_WIDTH 16 /* DSP2RX_EQ_B5_A - [15:0] */ + +/* + * R1426 (0x592) - DSP2 RX EQ Band 5 B + */ +#define WM8996_DSP2RX_EQ_B5_B_MASK 0xFFFF /* DSP2RX_EQ_B5_B - [15:0] */ +#define WM8996_DSP2RX_EQ_B5_B_SHIFT 0 /* DSP2RX_EQ_B5_B - [15:0] */ +#define WM8996_DSP2RX_EQ_B5_B_WIDTH 16 /* DSP2RX_EQ_B5_B - [15:0] */ + +/* + * R1427 (0x593) - DSP2 RX EQ Band 5 PG + */ +#define WM8996_DSP2RX_EQ_B5_PG_MASK 0xFFFF /* DSP2RX_EQ_B5_PG - [15:0] */ +#define WM8996_DSP2RX_EQ_B5_PG_SHIFT 0 /* DSP2RX_EQ_B5_PG - [15:0] */ +#define WM8996_DSP2RX_EQ_B5_PG_WIDTH 16 /* DSP2RX_EQ_B5_PG - [15:0] */ + +/* + * R1536 (0x600) - DAC1 Mixer Volumes + */ +#define WM8996_ADCR_DAC1_VOL_MASK 0x03E0 /* ADCR_DAC1_VOL - [9:5] */ +#define WM8996_ADCR_DAC1_VOL_SHIFT 5 /* ADCR_DAC1_VOL - [9:5] */ +#define WM8996_ADCR_DAC1_VOL_WIDTH 5 /* ADCR_DAC1_VOL - [9:5] */ +#define WM8996_ADCL_DAC1_VOL_MASK 0x001F /* ADCL_DAC1_VOL - [4:0] */ +#define WM8996_ADCL_DAC1_VOL_SHIFT 0 /* ADCL_DAC1_VOL - [4:0] */ +#define WM8996_ADCL_DAC1_VOL_WIDTH 5 /* ADCL_DAC1_VOL - [4:0] */ + +/* + * R1537 (0x601) - DAC1 Left Mixer Routing + */ +#define WM8996_ADCR_TO_DAC1L 0x0020 /* ADCR_TO_DAC1L */ +#define WM8996_ADCR_TO_DAC1L_MASK 0x0020 /* ADCR_TO_DAC1L */ +#define WM8996_ADCR_TO_DAC1L_SHIFT 5 /* ADCR_TO_DAC1L */ +#define WM8996_ADCR_TO_DAC1L_WIDTH 1 /* ADCR_TO_DAC1L */ +#define WM8996_ADCL_TO_DAC1L 0x0010 /* ADCL_TO_DAC1L */ +#define WM8996_ADCL_TO_DAC1L_MASK 0x0010 /* ADCL_TO_DAC1L */ +#define WM8996_ADCL_TO_DAC1L_SHIFT 4 /* ADCL_TO_DAC1L */ +#define WM8996_ADCL_TO_DAC1L_WIDTH 1 /* ADCL_TO_DAC1L */ +#define WM8996_DSP2RXL_TO_DAC1L 0x0002 /* DSP2RXL_TO_DAC1L */ +#define WM8996_DSP2RXL_TO_DAC1L_MASK 0x0002 /* DSP2RXL_TO_DAC1L */ +#define WM8996_DSP2RXL_TO_DAC1L_SHIFT 1 /* DSP2RXL_TO_DAC1L */ +#define WM8996_DSP2RXL_TO_DAC1L_WIDTH 1 /* DSP2RXL_TO_DAC1L */ +#define WM8996_DSP1RXL_TO_DAC1L 0x0001 /* DSP1RXL_TO_DAC1L */ +#define WM8996_DSP1RXL_TO_DAC1L_MASK 0x0001 /* DSP1RXL_TO_DAC1L */ +#define WM8996_DSP1RXL_TO_DAC1L_SHIFT 0 /* DSP1RXL_TO_DAC1L */ +#define WM8996_DSP1RXL_TO_DAC1L_WIDTH 1 /* DSP1RXL_TO_DAC1L */ + +/* + * R1538 (0x602) - DAC1 Right Mixer Routing + */ +#define WM8996_ADCR_TO_DAC1R 0x0020 /* ADCR_TO_DAC1R */ +#define WM8996_ADCR_TO_DAC1R_MASK 0x0020 /* ADCR_TO_DAC1R */ +#define WM8996_ADCR_TO_DAC1R_SHIFT 5 /* ADCR_TO_DAC1R */ +#define WM8996_ADCR_TO_DAC1R_WIDTH 1 /* ADCR_TO_DAC1R */ +#define WM8996_ADCL_TO_DAC1R 0x0010 /* ADCL_TO_DAC1R */ +#define WM8996_ADCL_TO_DAC1R_MASK 0x0010 /* ADCL_TO_DAC1R */ +#define WM8996_ADCL_TO_DAC1R_SHIFT 4 /* ADCL_TO_DAC1R */ +#define WM8996_ADCL_TO_DAC1R_WIDTH 1 /* ADCL_TO_DAC1R */ +#define WM8996_DSP2RXR_TO_DAC1R 0x0002 /* DSP2RXR_TO_DAC1R */ +#define WM8996_DSP2RXR_TO_DAC1R_MASK 0x0002 /* DSP2RXR_TO_DAC1R */ +#define WM8996_DSP2RXR_TO_DAC1R_SHIFT 1 /* DSP2RXR_TO_DAC1R */ +#define WM8996_DSP2RXR_TO_DAC1R_WIDTH 1 /* DSP2RXR_TO_DAC1R */ +#define WM8996_DSP1RXR_TO_DAC1R 0x0001 /* DSP1RXR_TO_DAC1R */ +#define WM8996_DSP1RXR_TO_DAC1R_MASK 0x0001 /* DSP1RXR_TO_DAC1R */ +#define WM8996_DSP1RXR_TO_DAC1R_SHIFT 0 /* DSP1RXR_TO_DAC1R */ +#define WM8996_DSP1RXR_TO_DAC1R_WIDTH 1 /* DSP1RXR_TO_DAC1R */ + +/* + * R1539 (0x603) - DAC2 Mixer Volumes + */ +#define WM8996_ADCR_DAC2_VOL_MASK 0x03E0 /* ADCR_DAC2_VOL - [9:5] */ +#define WM8996_ADCR_DAC2_VOL_SHIFT 5 /* ADCR_DAC2_VOL - [9:5] */ +#define WM8996_ADCR_DAC2_VOL_WIDTH 5 /* ADCR_DAC2_VOL - [9:5] */ +#define WM8996_ADCL_DAC2_VOL_MASK 0x001F /* ADCL_DAC2_VOL - [4:0] */ +#define WM8996_ADCL_DAC2_VOL_SHIFT 0 /* ADCL_DAC2_VOL - [4:0] */ +#define WM8996_ADCL_DAC2_VOL_WIDTH 5 /* ADCL_DAC2_VOL - [4:0] */ + +/* + * R1540 (0x604) - DAC2 Left Mixer Routing + */ +#define WM8996_ADCR_TO_DAC2L 0x0020 /* ADCR_TO_DAC2L */ +#define WM8996_ADCR_TO_DAC2L_MASK 0x0020 /* ADCR_TO_DAC2L */ +#define WM8996_ADCR_TO_DAC2L_SHIFT 5 /* ADCR_TO_DAC2L */ +#define WM8996_ADCR_TO_DAC2L_WIDTH 1 /* ADCR_TO_DAC2L */ +#define WM8996_ADCL_TO_DAC2L 0x0010 /* ADCL_TO_DAC2L */ +#define WM8996_ADCL_TO_DAC2L_MASK 0x0010 /* ADCL_TO_DAC2L */ +#define WM8996_ADCL_TO_DAC2L_SHIFT 4 /* ADCL_TO_DAC2L */ +#define WM8996_ADCL_TO_DAC2L_WIDTH 1 /* ADCL_TO_DAC2L */ +#define WM8996_DSP2RXL_TO_DAC2L 0x0002 /* DSP2RXL_TO_DAC2L */ +#define WM8996_DSP2RXL_TO_DAC2L_MASK 0x0002 /* DSP2RXL_TO_DAC2L */ +#define WM8996_DSP2RXL_TO_DAC2L_SHIFT 1 /* DSP2RXL_TO_DAC2L */ +#define WM8996_DSP2RXL_TO_DAC2L_WIDTH 1 /* DSP2RXL_TO_DAC2L */ +#define WM8996_DSP1RXL_TO_DAC2L 0x0001 /* DSP1RXL_TO_DAC2L */ +#define WM8996_DSP1RXL_TO_DAC2L_MASK 0x0001 /* DSP1RXL_TO_DAC2L */ +#define WM8996_DSP1RXL_TO_DAC2L_SHIFT 0 /* DSP1RXL_TO_DAC2L */ +#define WM8996_DSP1RXL_TO_DAC2L_WIDTH 1 /* DSP1RXL_TO_DAC2L */ + +/* + * R1541 (0x605) - DAC2 Right Mixer Routing + */ +#define WM8996_ADCR_TO_DAC2R 0x0020 /* ADCR_TO_DAC2R */ +#define WM8996_ADCR_TO_DAC2R_MASK 0x0020 /* ADCR_TO_DAC2R */ +#define WM8996_ADCR_TO_DAC2R_SHIFT 5 /* ADCR_TO_DAC2R */ +#define WM8996_ADCR_TO_DAC2R_WIDTH 1 /* ADCR_TO_DAC2R */ +#define WM8996_ADCL_TO_DAC2R 0x0010 /* ADCL_TO_DAC2R */ +#define WM8996_ADCL_TO_DAC2R_MASK 0x0010 /* ADCL_TO_DAC2R */ +#define WM8996_ADCL_TO_DAC2R_SHIFT 4 /* ADCL_TO_DAC2R */ +#define WM8996_ADCL_TO_DAC2R_WIDTH 1 /* ADCL_TO_DAC2R */ +#define WM8996_DSP2RXR_TO_DAC2R 0x0002 /* DSP2RXR_TO_DAC2R */ +#define WM8996_DSP2RXR_TO_DAC2R_MASK 0x0002 /* DSP2RXR_TO_DAC2R */ +#define WM8996_DSP2RXR_TO_DAC2R_SHIFT 1 /* DSP2RXR_TO_DAC2R */ +#define WM8996_DSP2RXR_TO_DAC2R_WIDTH 1 /* DSP2RXR_TO_DAC2R */ +#define WM8996_DSP1RXR_TO_DAC2R 0x0001 /* DSP1RXR_TO_DAC2R */ +#define WM8996_DSP1RXR_TO_DAC2R_MASK 0x0001 /* DSP1RXR_TO_DAC2R */ +#define WM8996_DSP1RXR_TO_DAC2R_SHIFT 0 /* DSP1RXR_TO_DAC2R */ +#define WM8996_DSP1RXR_TO_DAC2R_WIDTH 1 /* DSP1RXR_TO_DAC2R */ + +/* + * R1542 (0x606) - DSP1 TX Left Mixer Routing + */ +#define WM8996_ADC1L_TO_DSP1TXL 0x0002 /* ADC1L_TO_DSP1TXL */ +#define WM8996_ADC1L_TO_DSP1TXL_MASK 0x0002 /* ADC1L_TO_DSP1TXL */ +#define WM8996_ADC1L_TO_DSP1TXL_SHIFT 1 /* ADC1L_TO_DSP1TXL */ +#define WM8996_ADC1L_TO_DSP1TXL_WIDTH 1 /* ADC1L_TO_DSP1TXL */ +#define WM8996_DACL_TO_DSP1TXL 0x0001 /* DACL_TO_DSP1TXL */ +#define WM8996_DACL_TO_DSP1TXL_MASK 0x0001 /* DACL_TO_DSP1TXL */ +#define WM8996_DACL_TO_DSP1TXL_SHIFT 0 /* DACL_TO_DSP1TXL */ +#define WM8996_DACL_TO_DSP1TXL_WIDTH 1 /* DACL_TO_DSP1TXL */ + +/* + * R1543 (0x607) - DSP1 TX Right Mixer Routing + */ +#define WM8996_ADC1R_TO_DSP1TXR 0x0002 /* ADC1R_TO_DSP1TXR */ +#define WM8996_ADC1R_TO_DSP1TXR_MASK 0x0002 /* ADC1R_TO_DSP1TXR */ +#define WM8996_ADC1R_TO_DSP1TXR_SHIFT 1 /* ADC1R_TO_DSP1TXR */ +#define WM8996_ADC1R_TO_DSP1TXR_WIDTH 1 /* ADC1R_TO_DSP1TXR */ +#define WM8996_DACR_TO_DSP1TXR 0x0001 /* DACR_TO_DSP1TXR */ +#define WM8996_DACR_TO_DSP1TXR_MASK 0x0001 /* DACR_TO_DSP1TXR */ +#define WM8996_DACR_TO_DSP1TXR_SHIFT 0 /* DACR_TO_DSP1TXR */ +#define WM8996_DACR_TO_DSP1TXR_WIDTH 1 /* DACR_TO_DSP1TXR */ + +/* + * R1544 (0x608) - DSP2 TX Left Mixer Routing + */ +#define WM8996_ADC2L_TO_DSP2TXL 0x0002 /* ADC2L_TO_DSP2TXL */ +#define WM8996_ADC2L_TO_DSP2TXL_MASK 0x0002 /* ADC2L_TO_DSP2TXL */ +#define WM8996_ADC2L_TO_DSP2TXL_SHIFT 1 /* ADC2L_TO_DSP2TXL */ +#define WM8996_ADC2L_TO_DSP2TXL_WIDTH 1 /* ADC2L_TO_DSP2TXL */ +#define WM8996_DACL_TO_DSP2TXL 0x0001 /* DACL_TO_DSP2TXL */ +#define WM8996_DACL_TO_DSP2TXL_MASK 0x0001 /* DACL_TO_DSP2TXL */ +#define WM8996_DACL_TO_DSP2TXL_SHIFT 0 /* DACL_TO_DSP2TXL */ +#define WM8996_DACL_TO_DSP2TXL_WIDTH 1 /* DACL_TO_DSP2TXL */ + +/* + * R1545 (0x609) - DSP2 TX Right Mixer Routing + */ +#define WM8996_ADC2R_TO_DSP2TXR 0x0002 /* ADC2R_TO_DSP2TXR */ +#define WM8996_ADC2R_TO_DSP2TXR_MASK 0x0002 /* ADC2R_TO_DSP2TXR */ +#define WM8996_ADC2R_TO_DSP2TXR_SHIFT 1 /* ADC2R_TO_DSP2TXR */ +#define WM8996_ADC2R_TO_DSP2TXR_WIDTH 1 /* ADC2R_TO_DSP2TXR */ +#define WM8996_DACR_TO_DSP2TXR 0x0001 /* DACR_TO_DSP2TXR */ +#define WM8996_DACR_TO_DSP2TXR_MASK 0x0001 /* DACR_TO_DSP2TXR */ +#define WM8996_DACR_TO_DSP2TXR_SHIFT 0 /* DACR_TO_DSP2TXR */ +#define WM8996_DACR_TO_DSP2TXR_WIDTH 1 /* DACR_TO_DSP2TXR */ + +/* + * R1546 (0x60A) - DSP TX Mixer Select + */ +#define WM8996_DAC_TO_DSPTX_SRC 0x0001 /* DAC_TO_DSPTX_SRC */ +#define WM8996_DAC_TO_DSPTX_SRC_MASK 0x0001 /* DAC_TO_DSPTX_SRC */ +#define WM8996_DAC_TO_DSPTX_SRC_SHIFT 0 /* DAC_TO_DSPTX_SRC */ +#define WM8996_DAC_TO_DSPTX_SRC_WIDTH 1 /* DAC_TO_DSPTX_SRC */ + +/* + * R1552 (0x610) - DAC Softmute + */ +#define WM8996_DAC_SOFTMUTEMODE 0x0002 /* DAC_SOFTMUTEMODE */ +#define WM8996_DAC_SOFTMUTEMODE_MASK 0x0002 /* DAC_SOFTMUTEMODE */ +#define WM8996_DAC_SOFTMUTEMODE_SHIFT 1 /* DAC_SOFTMUTEMODE */ +#define WM8996_DAC_SOFTMUTEMODE_WIDTH 1 /* DAC_SOFTMUTEMODE */ +#define WM8996_DAC_MUTERATE 0x0001 /* DAC_MUTERATE */ +#define WM8996_DAC_MUTERATE_MASK 0x0001 /* DAC_MUTERATE */ +#define WM8996_DAC_MUTERATE_SHIFT 0 /* DAC_MUTERATE */ +#define WM8996_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ + +/* + * R1568 (0x620) - Oversampling + */ +#define WM8996_SPK_OSR128 0x0008 /* SPK_OSR128 */ +#define WM8996_SPK_OSR128_MASK 0x0008 /* SPK_OSR128 */ +#define WM8996_SPK_OSR128_SHIFT 3 /* SPK_OSR128 */ +#define WM8996_SPK_OSR128_WIDTH 1 /* SPK_OSR128 */ +#define WM8996_DMIC_OSR64 0x0004 /* DMIC_OSR64 */ +#define WM8996_DMIC_OSR64_MASK 0x0004 /* DMIC_OSR64 */ +#define WM8996_DMIC_OSR64_SHIFT 2 /* DMIC_OSR64 */ +#define WM8996_DMIC_OSR64_WIDTH 1 /* DMIC_OSR64 */ +#define WM8996_ADC_OSR128 0x0002 /* ADC_OSR128 */ +#define WM8996_ADC_OSR128_MASK 0x0002 /* ADC_OSR128 */ +#define WM8996_ADC_OSR128_SHIFT 1 /* ADC_OSR128 */ +#define WM8996_ADC_OSR128_WIDTH 1 /* ADC_OSR128 */ +#define WM8996_DAC_OSR128 0x0001 /* DAC_OSR128 */ +#define WM8996_DAC_OSR128_MASK 0x0001 /* DAC_OSR128 */ +#define WM8996_DAC_OSR128_SHIFT 0 /* DAC_OSR128 */ +#define WM8996_DAC_OSR128_WIDTH 1 /* DAC_OSR128 */ + +/* + * R1569 (0x621) - Sidetone + */ +#define WM8996_ST_LPF 0x1000 /* ST_LPF */ +#define WM8996_ST_LPF_MASK 0x1000 /* ST_LPF */ +#define WM8996_ST_LPF_SHIFT 12 /* ST_LPF */ +#define WM8996_ST_LPF_WIDTH 1 /* ST_LPF */ +#define WM8996_ST_HPF_CUT_MASK 0x0380 /* ST_HPF_CUT - [9:7] */ +#define WM8996_ST_HPF_CUT_SHIFT 7 /* ST_HPF_CUT - [9:7] */ +#define WM8996_ST_HPF_CUT_WIDTH 3 /* ST_HPF_CUT - [9:7] */ +#define WM8996_ST_HPF 0x0040 /* ST_HPF */ +#define WM8996_ST_HPF_MASK 0x0040 /* ST_HPF */ +#define WM8996_ST_HPF_SHIFT 6 /* ST_HPF */ +#define WM8996_ST_HPF_WIDTH 1 /* ST_HPF */ +#define WM8996_STR_SEL 0x0002 /* STR_SEL */ +#define WM8996_STR_SEL_MASK 0x0002 /* STR_SEL */ +#define WM8996_STR_SEL_SHIFT 1 /* STR_SEL */ +#define WM8996_STR_SEL_WIDTH 1 /* STR_SEL */ +#define WM8996_STL_SEL 0x0001 /* STL_SEL */ +#define WM8996_STL_SEL_MASK 0x0001 /* STL_SEL */ +#define WM8996_STL_SEL_SHIFT 0 /* STL_SEL */ +#define WM8996_STL_SEL_WIDTH 1 /* STL_SEL */ + +/* + * R1792 (0x700) - GPIO 1 + */ +#define WM8996_GP1_DIR 0x8000 /* GP1_DIR */ +#define WM8996_GP1_DIR_MASK 0x8000 /* GP1_DIR */ +#define WM8996_GP1_DIR_SHIFT 15 /* GP1_DIR */ +#define WM8996_GP1_DIR_WIDTH 1 /* GP1_DIR */ +#define WM8996_GP1_PU 0x4000 /* GP1_PU */ +#define WM8996_GP1_PU_MASK 0x4000 /* GP1_PU */ +#define WM8996_GP1_PU_SHIFT 14 /* GP1_PU */ +#define WM8996_GP1_PU_WIDTH 1 /* GP1_PU */ +#define WM8996_GP1_PD 0x2000 /* GP1_PD */ +#define WM8996_GP1_PD_MASK 0x2000 /* GP1_PD */ +#define WM8996_GP1_PD_SHIFT 13 /* GP1_PD */ +#define WM8996_GP1_PD_WIDTH 1 /* GP1_PD */ +#define WM8996_GP1_POL 0x0400 /* GP1_POL */ +#define WM8996_GP1_POL_MASK 0x0400 /* GP1_POL */ +#define WM8996_GP1_POL_SHIFT 10 /* GP1_POL */ +#define WM8996_GP1_POL_WIDTH 1 /* GP1_POL */ +#define WM8996_GP1_OP_CFG 0x0200 /* GP1_OP_CFG */ +#define WM8996_GP1_OP_CFG_MASK 0x0200 /* GP1_OP_CFG */ +#define WM8996_GP1_OP_CFG_SHIFT 9 /* GP1_OP_CFG */ +#define WM8996_GP1_OP_CFG_WIDTH 1 /* GP1_OP_CFG */ +#define WM8996_GP1_DB 0x0100 /* GP1_DB */ +#define WM8996_GP1_DB_MASK 0x0100 /* GP1_DB */ +#define WM8996_GP1_DB_SHIFT 8 /* GP1_DB */ +#define WM8996_GP1_DB_WIDTH 1 /* GP1_DB */ +#define WM8996_GP1_LVL 0x0040 /* GP1_LVL */ +#define WM8996_GP1_LVL_MASK 0x0040 /* GP1_LVL */ +#define WM8996_GP1_LVL_SHIFT 6 /* GP1_LVL */ +#define WM8996_GP1_LVL_WIDTH 1 /* GP1_LVL */ +#define WM8996_GP1_FN_MASK 0x000F /* GP1_FN - [3:0] */ +#define WM8996_GP1_FN_SHIFT 0 /* GP1_FN - [3:0] */ +#define WM8996_GP1_FN_WIDTH 4 /* GP1_FN - [3:0] */ + +/* + * R1793 (0x701) - GPIO 2 + */ +#define WM8996_GP2_DIR 0x8000 /* GP2_DIR */ +#define WM8996_GP2_DIR_MASK 0x8000 /* GP2_DIR */ +#define WM8996_GP2_DIR_SHIFT 15 /* GP2_DIR */ +#define WM8996_GP2_DIR_WIDTH 1 /* GP2_DIR */ +#define WM8996_GP2_PU 0x4000 /* GP2_PU */ +#define WM8996_GP2_PU_MASK 0x4000 /* GP2_PU */ +#define WM8996_GP2_PU_SHIFT 14 /* GP2_PU */ +#define WM8996_GP2_PU_WIDTH 1 /* GP2_PU */ +#define WM8996_GP2_PD 0x2000 /* GP2_PD */ +#define WM8996_GP2_PD_MASK 0x2000 /* GP2_PD */ +#define WM8996_GP2_PD_SHIFT 13 /* GP2_PD */ +#define WM8996_GP2_PD_WIDTH 1 /* GP2_PD */ +#define WM8996_GP2_POL 0x0400 /* GP2_POL */ +#define WM8996_GP2_POL_MASK 0x0400 /* GP2_POL */ +#define WM8996_GP2_POL_SHIFT 10 /* GP2_POL */ +#define WM8996_GP2_POL_WIDTH 1 /* GP2_POL */ +#define WM8996_GP2_OP_CFG 0x0200 /* GP2_OP_CFG */ +#define WM8996_GP2_OP_CFG_MASK 0x0200 /* GP2_OP_CFG */ +#define WM8996_GP2_OP_CFG_SHIFT 9 /* GP2_OP_CFG */ +#define WM8996_GP2_OP_CFG_WIDTH 1 /* GP2_OP_CFG */ +#define WM8996_GP2_DB 0x0100 /* GP2_DB */ +#define WM8996_GP2_DB_MASK 0x0100 /* GP2_DB */ +#define WM8996_GP2_DB_SHIFT 8 /* GP2_DB */ +#define WM8996_GP2_DB_WIDTH 1 /* GP2_DB */ +#define WM8996_GP2_LVL 0x0040 /* GP2_LVL */ +#define WM8996_GP2_LVL_MASK 0x0040 /* GP2_LVL */ +#define WM8996_GP2_LVL_SHIFT 6 /* GP2_LVL */ +#define WM8996_GP2_LVL_WIDTH 1 /* GP2_LVL */ +#define WM8996_GP2_FN_MASK 0x000F /* GP2_FN - [3:0] */ +#define WM8996_GP2_FN_SHIFT 0 /* GP2_FN - [3:0] */ +#define WM8996_GP2_FN_WIDTH 4 /* GP2_FN - [3:0] */ + +/* + * R1794 (0x702) - GPIO 3 + */ +#define WM8996_GP3_DIR 0x8000 /* GP3_DIR */ +#define WM8996_GP3_DIR_MASK 0x8000 /* GP3_DIR */ +#define WM8996_GP3_DIR_SHIFT 15 /* GP3_DIR */ +#define WM8996_GP3_DIR_WIDTH 1 /* GP3_DIR */ +#define WM8996_GP3_PU 0x4000 /* GP3_PU */ +#define WM8996_GP3_PU_MASK 0x4000 /* GP3_PU */ +#define WM8996_GP3_PU_SHIFT 14 /* GP3_PU */ +#define WM8996_GP3_PU_WIDTH 1 /* GP3_PU */ +#define WM8996_GP3_PD 0x2000 /* GP3_PD */ +#define WM8996_GP3_PD_MASK 0x2000 /* GP3_PD */ +#define WM8996_GP3_PD_SHIFT 13 /* GP3_PD */ +#define WM8996_GP3_PD_WIDTH 1 /* GP3_PD */ +#define WM8996_GP3_POL 0x0400 /* GP3_POL */ +#define WM8996_GP3_POL_MASK 0x0400 /* GP3_POL */ +#define WM8996_GP3_POL_SHIFT 10 /* GP3_POL */ +#define WM8996_GP3_POL_WIDTH 1 /* GP3_POL */ +#define WM8996_GP3_OP_CFG 0x0200 /* GP3_OP_CFG */ +#define WM8996_GP3_OP_CFG_MASK 0x0200 /* GP3_OP_CFG */ +#define WM8996_GP3_OP_CFG_SHIFT 9 /* GP3_OP_CFG */ +#define WM8996_GP3_OP_CFG_WIDTH 1 /* GP3_OP_CFG */ +#define WM8996_GP3_DB 0x0100 /* GP3_DB */ +#define WM8996_GP3_DB_MASK 0x0100 /* GP3_DB */ +#define WM8996_GP3_DB_SHIFT 8 /* GP3_DB */ +#define WM8996_GP3_DB_WIDTH 1 /* GP3_DB */ +#define WM8996_GP3_LVL 0x0040 /* GP3_LVL */ +#define WM8996_GP3_LVL_MASK 0x0040 /* GP3_LVL */ +#define WM8996_GP3_LVL_SHIFT 6 /* GP3_LVL */ +#define WM8996_GP3_LVL_WIDTH 1 /* GP3_LVL */ +#define WM8996_GP3_FN_MASK 0x000F /* GP3_FN - [3:0] */ +#define WM8996_GP3_FN_SHIFT 0 /* GP3_FN - [3:0] */ +#define WM8996_GP3_FN_WIDTH 4 /* GP3_FN - [3:0] */ + +/* + * R1795 (0x703) - GPIO 4 + */ +#define WM8996_GP4_DIR 0x8000 /* GP4_DIR */ +#define WM8996_GP4_DIR_MASK 0x8000 /* GP4_DIR */ +#define WM8996_GP4_DIR_SHIFT 15 /* GP4_DIR */ +#define WM8996_GP4_DIR_WIDTH 1 /* GP4_DIR */ +#define WM8996_GP4_PU 0x4000 /* GP4_PU */ +#define WM8996_GP4_PU_MASK 0x4000 /* GP4_PU */ +#define WM8996_GP4_PU_SHIFT 14 /* GP4_PU */ +#define WM8996_GP4_PU_WIDTH 1 /* GP4_PU */ +#define WM8996_GP4_PD 0x2000 /* GP4_PD */ +#define WM8996_GP4_PD_MASK 0x2000 /* GP4_PD */ +#define WM8996_GP4_PD_SHIFT 13 /* GP4_PD */ +#define WM8996_GP4_PD_WIDTH 1 /* GP4_PD */ +#define WM8996_GP4_POL 0x0400 /* GP4_POL */ +#define WM8996_GP4_POL_MASK 0x0400 /* GP4_POL */ +#define WM8996_GP4_POL_SHIFT 10 /* GP4_POL */ +#define WM8996_GP4_POL_WIDTH 1 /* GP4_POL */ +#define WM8996_GP4_OP_CFG 0x0200 /* GP4_OP_CFG */ +#define WM8996_GP4_OP_CFG_MASK 0x0200 /* GP4_OP_CFG */ +#define WM8996_GP4_OP_CFG_SHIFT 9 /* GP4_OP_CFG */ +#define WM8996_GP4_OP_CFG_WIDTH 1 /* GP4_OP_CFG */ +#define WM8996_GP4_DB 0x0100 /* GP4_DB */ +#define WM8996_GP4_DB_MASK 0x0100 /* GP4_DB */ +#define WM8996_GP4_DB_SHIFT 8 /* GP4_DB */ +#define WM8996_GP4_DB_WIDTH 1 /* GP4_DB */ +#define WM8996_GP4_LVL 0x0040 /* GP4_LVL */ +#define WM8996_GP4_LVL_MASK 0x0040 /* GP4_LVL */ +#define WM8996_GP4_LVL_SHIFT 6 /* GP4_LVL */ +#define WM8996_GP4_LVL_WIDTH 1 /* GP4_LVL */ +#define WM8996_GP4_FN_MASK 0x000F /* GP4_FN - [3:0] */ +#define WM8996_GP4_FN_SHIFT 0 /* GP4_FN - [3:0] */ +#define WM8996_GP4_FN_WIDTH 4 /* GP4_FN - [3:0] */ + +/* + * R1796 (0x704) - GPIO 5 + */ +#define WM8996_GP5_DIR 0x8000 /* GP5_DIR */ +#define WM8996_GP5_DIR_MASK 0x8000 /* GP5_DIR */ +#define WM8996_GP5_DIR_SHIFT 15 /* GP5_DIR */ +#define WM8996_GP5_DIR_WIDTH 1 /* GP5_DIR */ +#define WM8996_GP5_PU 0x4000 /* GP5_PU */ +#define WM8996_GP5_PU_MASK 0x4000 /* GP5_PU */ +#define WM8996_GP5_PU_SHIFT 14 /* GP5_PU */ +#define WM8996_GP5_PU_WIDTH 1 /* GP5_PU */ +#define WM8996_GP5_PD 0x2000 /* GP5_PD */ +#define WM8996_GP5_PD_MASK 0x2000 /* GP5_PD */ +#define WM8996_GP5_PD_SHIFT 13 /* GP5_PD */ +#define WM8996_GP5_PD_WIDTH 1 /* GP5_PD */ +#define WM8996_GP5_POL 0x0400 /* GP5_POL */ +#define WM8996_GP5_POL_MASK 0x0400 /* GP5_POL */ +#define WM8996_GP5_POL_SHIFT 10 /* GP5_POL */ +#define WM8996_GP5_POL_WIDTH 1 /* GP5_POL */ +#define WM8996_GP5_OP_CFG 0x0200 /* GP5_OP_CFG */ +#define WM8996_GP5_OP_CFG_MASK 0x0200 /* GP5_OP_CFG */ +#define WM8996_GP5_OP_CFG_SHIFT 9 /* GP5_OP_CFG */ +#define WM8996_GP5_OP_CFG_WIDTH 1 /* GP5_OP_CFG */ +#define WM8996_GP5_DB 0x0100 /* GP5_DB */ +#define WM8996_GP5_DB_MASK 0x0100 /* GP5_DB */ +#define WM8996_GP5_DB_SHIFT 8 /* GP5_DB */ +#define WM8996_GP5_DB_WIDTH 1 /* GP5_DB */ +#define WM8996_GP5_LVL 0x0040 /* GP5_LVL */ +#define WM8996_GP5_LVL_MASK 0x0040 /* GP5_LVL */ +#define WM8996_GP5_LVL_SHIFT 6 /* GP5_LVL */ +#define WM8996_GP5_LVL_WIDTH 1 /* GP5_LVL */ +#define WM8996_GP5_FN_MASK 0x000F /* GP5_FN - [3:0] */ +#define WM8996_GP5_FN_SHIFT 0 /* GP5_FN - [3:0] */ +#define WM8996_GP5_FN_WIDTH 4 /* GP5_FN - [3:0] */ + +/* + * R1824 (0x720) - Pull Control (1) + */ +#define WM8996_DMICDAT2_PD 0x1000 /* DMICDAT2_PD */ +#define WM8996_DMICDAT2_PD_MASK 0x1000 /* DMICDAT2_PD */ +#define WM8996_DMICDAT2_PD_SHIFT 12 /* DMICDAT2_PD */ +#define WM8996_DMICDAT2_PD_WIDTH 1 /* DMICDAT2_PD */ +#define WM8996_DMICDAT1_PD 0x0400 /* DMICDAT1_PD */ +#define WM8996_DMICDAT1_PD_MASK 0x0400 /* DMICDAT1_PD */ +#define WM8996_DMICDAT1_PD_SHIFT 10 /* DMICDAT1_PD */ +#define WM8996_DMICDAT1_PD_WIDTH 1 /* DMICDAT1_PD */ +#define WM8996_MCLK2_PU 0x0200 /* MCLK2_PU */ +#define WM8996_MCLK2_PU_MASK 0x0200 /* MCLK2_PU */ +#define WM8996_MCLK2_PU_SHIFT 9 /* MCLK2_PU */ +#define WM8996_MCLK2_PU_WIDTH 1 /* MCLK2_PU */ +#define WM8996_MCLK2_PD 0x0100 /* MCLK2_PD */ +#define WM8996_MCLK2_PD_MASK 0x0100 /* MCLK2_PD */ +#define WM8996_MCLK2_PD_SHIFT 8 /* MCLK2_PD */ +#define WM8996_MCLK2_PD_WIDTH 1 /* MCLK2_PD */ +#define WM8996_MCLK1_PU 0x0080 /* MCLK1_PU */ +#define WM8996_MCLK1_PU_MASK 0x0080 /* MCLK1_PU */ +#define WM8996_MCLK1_PU_SHIFT 7 /* MCLK1_PU */ +#define WM8996_MCLK1_PU_WIDTH 1 /* MCLK1_PU */ +#define WM8996_MCLK1_PD 0x0040 /* MCLK1_PD */ +#define WM8996_MCLK1_PD_MASK 0x0040 /* MCLK1_PD */ +#define WM8996_MCLK1_PD_SHIFT 6 /* MCLK1_PD */ +#define WM8996_MCLK1_PD_WIDTH 1 /* MCLK1_PD */ +#define WM8996_DACDAT1_PU 0x0020 /* DACDAT1_PU */ +#define WM8996_DACDAT1_PU_MASK 0x0020 /* DACDAT1_PU */ +#define WM8996_DACDAT1_PU_SHIFT 5 /* DACDAT1_PU */ +#define WM8996_DACDAT1_PU_WIDTH 1 /* DACDAT1_PU */ +#define WM8996_DACDAT1_PD 0x0010 /* DACDAT1_PD */ +#define WM8996_DACDAT1_PD_MASK 0x0010 /* DACDAT1_PD */ +#define WM8996_DACDAT1_PD_SHIFT 4 /* DACDAT1_PD */ +#define WM8996_DACDAT1_PD_WIDTH 1 /* DACDAT1_PD */ +#define WM8996_DACLRCLK1_PU 0x0008 /* DACLRCLK1_PU */ +#define WM8996_DACLRCLK1_PU_MASK 0x0008 /* DACLRCLK1_PU */ +#define WM8996_DACLRCLK1_PU_SHIFT 3 /* DACLRCLK1_PU */ +#define WM8996_DACLRCLK1_PU_WIDTH 1 /* DACLRCLK1_PU */ +#define WM8996_DACLRCLK1_PD 0x0004 /* DACLRCLK1_PD */ +#define WM8996_DACLRCLK1_PD_MASK 0x0004 /* DACLRCLK1_PD */ +#define WM8996_DACLRCLK1_PD_SHIFT 2 /* DACLRCLK1_PD */ +#define WM8996_DACLRCLK1_PD_WIDTH 1 /* DACLRCLK1_PD */ +#define WM8996_BCLK1_PU 0x0002 /* BCLK1_PU */ +#define WM8996_BCLK1_PU_MASK 0x0002 /* BCLK1_PU */ +#define WM8996_BCLK1_PU_SHIFT 1 /* BCLK1_PU */ +#define WM8996_BCLK1_PU_WIDTH 1 /* BCLK1_PU */ +#define WM8996_BCLK1_PD 0x0001 /* BCLK1_PD */ +#define WM8996_BCLK1_PD_MASK 0x0001 /* BCLK1_PD */ +#define WM8996_BCLK1_PD_SHIFT 0 /* BCLK1_PD */ +#define WM8996_BCLK1_PD_WIDTH 1 /* BCLK1_PD */ + +/* + * R1825 (0x721) - Pull Control (2) + */ +#define WM8996_LDO1ENA_PD 0x0100 /* LDO1ENA_PD */ +#define WM8996_LDO1ENA_PD_MASK 0x0100 /* LDO1ENA_PD */ +#define WM8996_LDO1ENA_PD_SHIFT 8 /* LDO1ENA_PD */ +#define WM8996_LDO1ENA_PD_WIDTH 1 /* LDO1ENA_PD */ +#define WM8996_ADDR_PD 0x0040 /* ADDR_PD */ +#define WM8996_ADDR_PD_MASK 0x0040 /* ADDR_PD */ +#define WM8996_ADDR_PD_SHIFT 6 /* ADDR_PD */ +#define WM8996_ADDR_PD_WIDTH 1 /* ADDR_PD */ +#define WM8996_DACDAT2_PU 0x0020 /* DACDAT2_PU */ +#define WM8996_DACDAT2_PU_MASK 0x0020 /* DACDAT2_PU */ +#define WM8996_DACDAT2_PU_SHIFT 5 /* DACDAT2_PU */ +#define WM8996_DACDAT2_PU_WIDTH 1 /* DACDAT2_PU */ +#define WM8996_DACDAT2_PD 0x0010 /* DACDAT2_PD */ +#define WM8996_DACDAT2_PD_MASK 0x0010 /* DACDAT2_PD */ +#define WM8996_DACDAT2_PD_SHIFT 4 /* DACDAT2_PD */ +#define WM8996_DACDAT2_PD_WIDTH 1 /* DACDAT2_PD */ +#define WM8996_DACLRCLK2_PU 0x0008 /* DACLRCLK2_PU */ +#define WM8996_DACLRCLK2_PU_MASK 0x0008 /* DACLRCLK2_PU */ +#define WM8996_DACLRCLK2_PU_SHIFT 3 /* DACLRCLK2_PU */ +#define WM8996_DACLRCLK2_PU_WIDTH 1 /* DACLRCLK2_PU */ +#define WM8996_DACLRCLK2_PD 0x0004 /* DACLRCLK2_PD */ +#define WM8996_DACLRCLK2_PD_MASK 0x0004 /* DACLRCLK2_PD */ +#define WM8996_DACLRCLK2_PD_SHIFT 2 /* DACLRCLK2_PD */ +#define WM8996_DACLRCLK2_PD_WIDTH 1 /* DACLRCLK2_PD */ +#define WM8996_BCLK2_PU 0x0002 /* BCLK2_PU */ +#define WM8996_BCLK2_PU_MASK 0x0002 /* BCLK2_PU */ +#define WM8996_BCLK2_PU_SHIFT 1 /* BCLK2_PU */ +#define WM8996_BCLK2_PU_WIDTH 1 /* BCLK2_PU */ +#define WM8996_BCLK2_PD 0x0001 /* BCLK2_PD */ +#define WM8996_BCLK2_PD_MASK 0x0001 /* BCLK2_PD */ +#define WM8996_BCLK2_PD_SHIFT 0 /* BCLK2_PD */ +#define WM8996_BCLK2_PD_WIDTH 1 /* BCLK2_PD */ + +/* + * R1840 (0x730) - Interrupt Status 1 + */ +#define WM8996_GP5_EINT 0x0010 /* GP5_EINT */ +#define WM8996_GP5_EINT_MASK 0x0010 /* GP5_EINT */ +#define WM8996_GP5_EINT_SHIFT 4 /* GP5_EINT */ +#define WM8996_GP5_EINT_WIDTH 1 /* GP5_EINT */ +#define WM8996_GP4_EINT 0x0008 /* GP4_EINT */ +#define WM8996_GP4_EINT_MASK 0x0008 /* GP4_EINT */ +#define WM8996_GP4_EINT_SHIFT 3 /* GP4_EINT */ +#define WM8996_GP4_EINT_WIDTH 1 /* GP4_EINT */ +#define WM8996_GP3_EINT 0x0004 /* GP3_EINT */ +#define WM8996_GP3_EINT_MASK 0x0004 /* GP3_EINT */ +#define WM8996_GP3_EINT_SHIFT 2 /* GP3_EINT */ +#define WM8996_GP3_EINT_WIDTH 1 /* GP3_EINT */ +#define WM8996_GP2_EINT 0x0002 /* GP2_EINT */ +#define WM8996_GP2_EINT_MASK 0x0002 /* GP2_EINT */ +#define WM8996_GP2_EINT_SHIFT 1 /* GP2_EINT */ +#define WM8996_GP2_EINT_WIDTH 1 /* GP2_EINT */ +#define WM8996_GP1_EINT 0x0001 /* GP1_EINT */ +#define WM8996_GP1_EINT_MASK 0x0001 /* GP1_EINT */ +#define WM8996_GP1_EINT_SHIFT 0 /* GP1_EINT */ +#define WM8996_GP1_EINT_WIDTH 1 /* GP1_EINT */ + +/* + * R1841 (0x731) - Interrupt Status 2 + */ +#define WM8996_DCS_DONE_23_EINT 0x1000 /* DCS_DONE_23_EINT */ +#define WM8996_DCS_DONE_23_EINT_MASK 0x1000 /* DCS_DONE_23_EINT */ +#define WM8996_DCS_DONE_23_EINT_SHIFT 12 /* DCS_DONE_23_EINT */ +#define WM8996_DCS_DONE_23_EINT_WIDTH 1 /* DCS_DONE_23_EINT */ +#define WM8996_DCS_DONE_01_EINT 0x0800 /* DCS_DONE_01_EINT */ +#define WM8996_DCS_DONE_01_EINT_MASK 0x0800 /* DCS_DONE_01_EINT */ +#define WM8996_DCS_DONE_01_EINT_SHIFT 11 /* DCS_DONE_01_EINT */ +#define WM8996_DCS_DONE_01_EINT_WIDTH 1 /* DCS_DONE_01_EINT */ +#define WM8996_WSEQ_DONE_EINT 0x0400 /* WSEQ_DONE_EINT */ +#define WM8996_WSEQ_DONE_EINT_MASK 0x0400 /* WSEQ_DONE_EINT */ +#define WM8996_WSEQ_DONE_EINT_SHIFT 10 /* WSEQ_DONE_EINT */ +#define WM8996_WSEQ_DONE_EINT_WIDTH 1 /* WSEQ_DONE_EINT */ +#define WM8996_FIFOS_ERR_EINT 0x0200 /* FIFOS_ERR_EINT */ +#define WM8996_FIFOS_ERR_EINT_MASK 0x0200 /* FIFOS_ERR_EINT */ +#define WM8996_FIFOS_ERR_EINT_SHIFT 9 /* FIFOS_ERR_EINT */ +#define WM8996_FIFOS_ERR_EINT_WIDTH 1 /* FIFOS_ERR_EINT */ +#define WM8996_DSP2DRC_SIG_DET_EINT 0x0080 /* DSP2DRC_SIG_DET_EINT */ +#define WM8996_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* DSP2DRC_SIG_DET_EINT */ +#define WM8996_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* DSP2DRC_SIG_DET_EINT */ +#define WM8996_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* DSP2DRC_SIG_DET_EINT */ +#define WM8996_DSP1DRC_SIG_DET_EINT 0x0040 /* DSP1DRC_SIG_DET_EINT */ +#define WM8996_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* DSP1DRC_SIG_DET_EINT */ +#define WM8996_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* DSP1DRC_SIG_DET_EINT */ +#define WM8996_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* DSP1DRC_SIG_DET_EINT */ +#define WM8996_FLL_SW_CLK_DONE_EINT 0x0008 /* FLL_SW_CLK_DONE_EINT */ +#define WM8996_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* FLL_SW_CLK_DONE_EINT */ +#define WM8996_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* FLL_SW_CLK_DONE_EINT */ +#define WM8996_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* FLL_SW_CLK_DONE_EINT */ +#define WM8996_FLL_LOCK_EINT 0x0004 /* FLL_LOCK_EINT */ +#define WM8996_FLL_LOCK_EINT_MASK 0x0004 /* FLL_LOCK_EINT */ +#define WM8996_FLL_LOCK_EINT_SHIFT 2 /* FLL_LOCK_EINT */ +#define WM8996_FLL_LOCK_EINT_WIDTH 1 /* FLL_LOCK_EINT */ +#define WM8996_HP_DONE_EINT 0x0002 /* HP_DONE_EINT */ +#define WM8996_HP_DONE_EINT_MASK 0x0002 /* HP_DONE_EINT */ +#define WM8996_HP_DONE_EINT_SHIFT 1 /* HP_DONE_EINT */ +#define WM8996_HP_DONE_EINT_WIDTH 1 /* HP_DONE_EINT */ +#define WM8996_MICD_EINT 0x0001 /* MICD_EINT */ +#define WM8996_MICD_EINT_MASK 0x0001 /* MICD_EINT */ +#define WM8996_MICD_EINT_SHIFT 0 /* MICD_EINT */ +#define WM8996_MICD_EINT_WIDTH 1 /* MICD_EINT */ + +/* + * R1842 (0x732) - Interrupt Raw Status 2 + */ +#define WM8996_DCS_DONE_23_STS 0x1000 /* DCS_DONE_23_STS */ +#define WM8996_DCS_DONE_23_STS_MASK 0x1000 /* DCS_DONE_23_STS */ +#define WM8996_DCS_DONE_23_STS_SHIFT 12 /* DCS_DONE_23_STS */ +#define WM8996_DCS_DONE_23_STS_WIDTH 1 /* DCS_DONE_23_STS */ +#define WM8996_DCS_DONE_01_STS 0x0800 /* DCS_DONE_01_STS */ +#define WM8996_DCS_DONE_01_STS_MASK 0x0800 /* DCS_DONE_01_STS */ +#define WM8996_DCS_DONE_01_STS_SHIFT 11 /* DCS_DONE_01_STS */ +#define WM8996_DCS_DONE_01_STS_WIDTH 1 /* DCS_DONE_01_STS */ +#define WM8996_WSEQ_DONE_STS 0x0400 /* WSEQ_DONE_STS */ +#define WM8996_WSEQ_DONE_STS_MASK 0x0400 /* WSEQ_DONE_STS */ +#define WM8996_WSEQ_DONE_STS_SHIFT 10 /* WSEQ_DONE_STS */ +#define WM8996_WSEQ_DONE_STS_WIDTH 1 /* WSEQ_DONE_STS */ +#define WM8996_FIFOS_ERR_STS 0x0200 /* FIFOS_ERR_STS */ +#define WM8996_FIFOS_ERR_STS_MASK 0x0200 /* FIFOS_ERR_STS */ +#define WM8996_FIFOS_ERR_STS_SHIFT 9 /* FIFOS_ERR_STS */ +#define WM8996_FIFOS_ERR_STS_WIDTH 1 /* FIFOS_ERR_STS */ +#define WM8996_DSP2DRC_SIG_DET_STS 0x0080 /* DSP2DRC_SIG_DET_STS */ +#define WM8996_DSP2DRC_SIG_DET_STS_MASK 0x0080 /* DSP2DRC_SIG_DET_STS */ +#define WM8996_DSP2DRC_SIG_DET_STS_SHIFT 7 /* DSP2DRC_SIG_DET_STS */ +#define WM8996_DSP2DRC_SIG_DET_STS_WIDTH 1 /* DSP2DRC_SIG_DET_STS */ +#define WM8996_DSP1DRC_SIG_DET_STS 0x0040 /* DSP1DRC_SIG_DET_STS */ +#define WM8996_DSP1DRC_SIG_DET_STS_MASK 0x0040 /* DSP1DRC_SIG_DET_STS */ +#define WM8996_DSP1DRC_SIG_DET_STS_SHIFT 6 /* DSP1DRC_SIG_DET_STS */ +#define WM8996_DSP1DRC_SIG_DET_STS_WIDTH 1 /* DSP1DRC_SIG_DET_STS */ +#define WM8996_FLL_LOCK_STS 0x0004 /* FLL_LOCK_STS */ +#define WM8996_FLL_LOCK_STS_MASK 0x0004 /* FLL_LOCK_STS */ +#define WM8996_FLL_LOCK_STS_SHIFT 2 /* FLL_LOCK_STS */ +#define WM8996_FLL_LOCK_STS_WIDTH 1 /* FLL_LOCK_STS */ + +/* + * R1848 (0x738) - Interrupt Status 1 Mask + */ +#define WM8996_IM_GP5_EINT 0x0010 /* IM_GP5_EINT */ +#define WM8996_IM_GP5_EINT_MASK 0x0010 /* IM_GP5_EINT */ +#define WM8996_IM_GP5_EINT_SHIFT 4 /* IM_GP5_EINT */ +#define WM8996_IM_GP5_EINT_WIDTH 1 /* IM_GP5_EINT */ +#define WM8996_IM_GP4_EINT 0x0008 /* IM_GP4_EINT */ +#define WM8996_IM_GP4_EINT_MASK 0x0008 /* IM_GP4_EINT */ +#define WM8996_IM_GP4_EINT_SHIFT 3 /* IM_GP4_EINT */ +#define WM8996_IM_GP4_EINT_WIDTH 1 /* IM_GP4_EINT */ +#define WM8996_IM_GP3_EINT 0x0004 /* IM_GP3_EINT */ +#define WM8996_IM_GP3_EINT_MASK 0x0004 /* IM_GP3_EINT */ +#define WM8996_IM_GP3_EINT_SHIFT 2 /* IM_GP3_EINT */ +#define WM8996_IM_GP3_EINT_WIDTH 1 /* IM_GP3_EINT */ +#define WM8996_IM_GP2_EINT 0x0002 /* IM_GP2_EINT */ +#define WM8996_IM_GP2_EINT_MASK 0x0002 /* IM_GP2_EINT */ +#define WM8996_IM_GP2_EINT_SHIFT 1 /* IM_GP2_EINT */ +#define WM8996_IM_GP2_EINT_WIDTH 1 /* IM_GP2_EINT */ +#define WM8996_IM_GP1_EINT 0x0001 /* IM_GP1_EINT */ +#define WM8996_IM_GP1_EINT_MASK 0x0001 /* IM_GP1_EINT */ +#define WM8996_IM_GP1_EINT_SHIFT 0 /* IM_GP1_EINT */ +#define WM8996_IM_GP1_EINT_WIDTH 1 /* IM_GP1_EINT */ + +/* + * R1849 (0x739) - Interrupt Status 2 Mask + */ +#define WM8996_IM_DCS_DONE_23_EINT 0x1000 /* IM_DCS_DONE_23_EINT */ +#define WM8996_IM_DCS_DONE_23_EINT_MASK 0x1000 /* IM_DCS_DONE_23_EINT */ +#define WM8996_IM_DCS_DONE_23_EINT_SHIFT 12 /* IM_DCS_DONE_23_EINT */ +#define WM8996_IM_DCS_DONE_23_EINT_WIDTH 1 /* IM_DCS_DONE_23_EINT */ +#define WM8996_IM_DCS_DONE_01_EINT 0x0800 /* IM_DCS_DONE_01_EINT */ +#define WM8996_IM_DCS_DONE_01_EINT_MASK 0x0800 /* IM_DCS_DONE_01_EINT */ +#define WM8996_IM_DCS_DONE_01_EINT_SHIFT 11 /* IM_DCS_DONE_01_EINT */ +#define WM8996_IM_DCS_DONE_01_EINT_WIDTH 1 /* IM_DCS_DONE_01_EINT */ +#define WM8996_IM_WSEQ_DONE_EINT 0x0400 /* IM_WSEQ_DONE_EINT */ +#define WM8996_IM_WSEQ_DONE_EINT_MASK 0x0400 /* IM_WSEQ_DONE_EINT */ +#define WM8996_IM_WSEQ_DONE_EINT_SHIFT 10 /* IM_WSEQ_DONE_EINT */ +#define WM8996_IM_WSEQ_DONE_EINT_WIDTH 1 /* IM_WSEQ_DONE_EINT */ +#define WM8996_IM_FIFOS_ERR_EINT 0x0200 /* IM_FIFOS_ERR_EINT */ +#define WM8996_IM_FIFOS_ERR_EINT_MASK 0x0200 /* IM_FIFOS_ERR_EINT */ +#define WM8996_IM_FIFOS_ERR_EINT_SHIFT 9 /* IM_FIFOS_ERR_EINT */ +#define WM8996_IM_FIFOS_ERR_EINT_WIDTH 1 /* IM_FIFOS_ERR_EINT */ +#define WM8996_IM_DSP2DRC_SIG_DET_EINT 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */ +#define WM8996_IM_DSP2DRC_SIG_DET_EINT_MASK 0x0080 /* IM_DSP2DRC_SIG_DET_EINT */ +#define WM8996_IM_DSP2DRC_SIG_DET_EINT_SHIFT 7 /* IM_DSP2DRC_SIG_DET_EINT */ +#define WM8996_IM_DSP2DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP2DRC_SIG_DET_EINT */ +#define WM8996_IM_DSP1DRC_SIG_DET_EINT 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */ +#define WM8996_IM_DSP1DRC_SIG_DET_EINT_MASK 0x0040 /* IM_DSP1DRC_SIG_DET_EINT */ +#define WM8996_IM_DSP1DRC_SIG_DET_EINT_SHIFT 6 /* IM_DSP1DRC_SIG_DET_EINT */ +#define WM8996_IM_DSP1DRC_SIG_DET_EINT_WIDTH 1 /* IM_DSP1DRC_SIG_DET_EINT */ +#define WM8996_IM_FLL_SW_CLK_DONE_EINT 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */ +#define WM8996_IM_FLL_SW_CLK_DONE_EINT_MASK 0x0008 /* IM_FLL_SW_CLK_DONE_EINT */ +#define WM8996_IM_FLL_SW_CLK_DONE_EINT_SHIFT 3 /* IM_FLL_SW_CLK_DONE_EINT */ +#define WM8996_IM_FLL_SW_CLK_DONE_EINT_WIDTH 1 /* IM_FLL_SW_CLK_DONE_EINT */ +#define WM8996_IM_FLL_LOCK_EINT 0x0004 /* IM_FLL_LOCK_EINT */ +#define WM8996_IM_FLL_LOCK_EINT_MASK 0x0004 /* IM_FLL_LOCK_EINT */ +#define WM8996_IM_FLL_LOCK_EINT_SHIFT 2 /* IM_FLL_LOCK_EINT */ +#define WM8996_IM_FLL_LOCK_EINT_WIDTH 1 /* IM_FLL_LOCK_EINT */ +#define WM8996_IM_HP_DONE_EINT 0x0002 /* IM_HP_DONE_EINT */ +#define WM8996_IM_HP_DONE_EINT_MASK 0x0002 /* IM_HP_DONE_EINT */ +#define WM8996_IM_HP_DONE_EINT_SHIFT 1 /* IM_HP_DONE_EINT */ +#define WM8996_IM_HP_DONE_EINT_WIDTH 1 /* IM_HP_DONE_EINT */ +#define WM8996_IM_MICD_EINT 0x0001 /* IM_MICD_EINT */ +#define WM8996_IM_MICD_EINT_MASK 0x0001 /* IM_MICD_EINT */ +#define WM8996_IM_MICD_EINT_SHIFT 0 /* IM_MICD_EINT */ +#define WM8996_IM_MICD_EINT_WIDTH 1 /* IM_MICD_EINT */ + +/* + * R1856 (0x740) - Interrupt Control + */ +#define WM8996_IM_IRQ 0x0001 /* IM_IRQ */ +#define WM8996_IM_IRQ_MASK 0x0001 /* IM_IRQ */ +#define WM8996_IM_IRQ_SHIFT 0 /* IM_IRQ */ +#define WM8996_IM_IRQ_WIDTH 1 /* IM_IRQ */ + +/* + * R2048 (0x800) - Left PDM Speaker + */ +#define WM8996_SPKL_ENA 0x0010 /* SPKL_ENA */ +#define WM8996_SPKL_ENA_MASK 0x0010 /* SPKL_ENA */ +#define WM8996_SPKL_ENA_SHIFT 4 /* SPKL_ENA */ +#define WM8996_SPKL_ENA_WIDTH 1 /* SPKL_ENA */ +#define WM8996_SPKL_MUTE 0x0008 /* SPKL_MUTE */ +#define WM8996_SPKL_MUTE_MASK 0x0008 /* SPKL_MUTE */ +#define WM8996_SPKL_MUTE_SHIFT 3 /* SPKL_MUTE */ +#define WM8996_SPKL_MUTE_WIDTH 1 /* SPKL_MUTE */ +#define WM8996_SPKL_MUTE_ZC 0x0004 /* SPKL_MUTE_ZC */ +#define WM8996_SPKL_MUTE_ZC_MASK 0x0004 /* SPKL_MUTE_ZC */ +#define WM8996_SPKL_MUTE_ZC_SHIFT 2 /* SPKL_MUTE_ZC */ +#define WM8996_SPKL_MUTE_ZC_WIDTH 1 /* SPKL_MUTE_ZC */ +#define WM8996_SPKL_SRC_MASK 0x0003 /* SPKL_SRC - [1:0] */ +#define WM8996_SPKL_SRC_SHIFT 0 /* SPKL_SRC - [1:0] */ +#define WM8996_SPKL_SRC_WIDTH 2 /* SPKL_SRC - [1:0] */ + +/* + * R2049 (0x801) - Right PDM Speaker + */ +#define WM8996_SPKR_ENA 0x0010 /* SPKR_ENA */ +#define WM8996_SPKR_ENA_MASK 0x0010 /* SPKR_ENA */ +#define WM8996_SPKR_ENA_SHIFT 4 /* SPKR_ENA */ +#define WM8996_SPKR_ENA_WIDTH 1 /* SPKR_ENA */ +#define WM8996_SPKR_MUTE 0x0008 /* SPKR_MUTE */ +#define WM8996_SPKR_MUTE_MASK 0x0008 /* SPKR_MUTE */ +#define WM8996_SPKR_MUTE_SHIFT 3 /* SPKR_MUTE */ +#define WM8996_SPKR_MUTE_WIDTH 1 /* SPKR_MUTE */ +#define WM8996_SPKR_MUTE_ZC 0x0004 /* SPKR_MUTE_ZC */ +#define WM8996_SPKR_MUTE_ZC_MASK 0x0004 /* SPKR_MUTE_ZC */ +#define WM8996_SPKR_MUTE_ZC_SHIFT 2 /* SPKR_MUTE_ZC */ +#define WM8996_SPKR_MUTE_ZC_WIDTH 1 /* SPKR_MUTE_ZC */ +#define WM8996_SPKR_SRC_MASK 0x0003 /* SPKR_SRC - [1:0] */ +#define WM8996_SPKR_SRC_SHIFT 0 /* SPKR_SRC - [1:0] */ +#define WM8996_SPKR_SRC_WIDTH 2 /* SPKR_SRC - [1:0] */ + +/* + * R2050 (0x802) - PDM Speaker Mute Sequence + */ +#define WM8996_SPK_MUTE_ENDIAN 0x0100 /* SPK_MUTE_ENDIAN */ +#define WM8996_SPK_MUTE_ENDIAN_MASK 0x0100 /* SPK_MUTE_ENDIAN */ +#define WM8996_SPK_MUTE_ENDIAN_SHIFT 8 /* SPK_MUTE_ENDIAN */ +#define WM8996_SPK_MUTE_ENDIAN_WIDTH 1 /* SPK_MUTE_ENDIAN */ +#define WM8996_SPK_MUTE_SEQ1_MASK 0x00FF /* SPK_MUTE_SEQ1 - [7:0] */ +#define WM8996_SPK_MUTE_SEQ1_SHIFT 0 /* SPK_MUTE_SEQ1 - [7:0] */ +#define WM8996_SPK_MUTE_SEQ1_WIDTH 8 /* SPK_MUTE_SEQ1 - [7:0] */ + +/* + * R2051 (0x803) - PDM Speaker Volume + */ +#define WM8996_SPKR_VOL_MASK 0x00F0 /* SPKR_VOL - [7:4] */ +#define WM8996_SPKR_VOL_SHIFT 4 /* SPKR_VOL - [7:4] */ +#define WM8996_SPKR_VOL_WIDTH 4 /* SPKR_VOL - [7:4] */ +#define WM8996_SPKL_VOL_MASK 0x000F /* SPKL_VOL - [3:0] */ +#define WM8996_SPKL_VOL_SHIFT 0 /* SPKL_VOL - [3:0] */ +#define WM8996_SPKL_VOL_WIDTH 4 /* SPKL_VOL - [3:0] */ + +#endif diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c new file mode 100644 index 0000000000..87442840f0 --- /dev/null +++ b/sound/soc/codecs/wm8997.c @@ -0,0 +1,1221 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8997.c -- WM8997 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Charles Keepax + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "arizona.h" +#include "wm8997.h" + +struct wm8997_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +static const struct reg_default wm8997_sysclk_reva_patch[] = { + { 0x301D, 0x7B15 }, + { 0x301B, 0x0050 }, + { 0x305D, 0x7B17 }, + { 0x305B, 0x0050 }, + { 0x3001, 0x08FE }, + { 0x3003, 0x00F4 }, + { 0x3041, 0x08FF }, + { 0x3043, 0x0005 }, + { 0x3020, 0x0225 }, + { 0x3021, 0x0A00 }, + { 0x3022, 0xE24D }, + { 0x3023, 0x0800 }, + { 0x3024, 0xE24D }, + { 0x3025, 0xF000 }, + { 0x3060, 0x0226 }, + { 0x3061, 0x0A00 }, + { 0x3062, 0xE252 }, + { 0x3063, 0x0800 }, + { 0x3064, 0xE252 }, + { 0x3065, 0xF000 }, + { 0x3116, 0x022B }, + { 0x3117, 0xFA00 }, + { 0x3110, 0x246C }, + { 0x3111, 0x0A03 }, + { 0x3112, 0x246E }, + { 0x3113, 0x0A03 }, + { 0x3114, 0x2470 }, + { 0x3115, 0x0A03 }, + { 0x3126, 0x246C }, + { 0x3127, 0x0A02 }, + { 0x3128, 0x246E }, + { 0x3129, 0x0A02 }, + { 0x312A, 0x2470 }, + { 0x312B, 0xFA02 }, + { 0x3125, 0x0800 }, +}; + +static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct arizona *arizona = dev_get_drvdata(component->dev->parent); + struct regmap *regmap = arizona->regmap; + const struct reg_default *patch = NULL; + int i, patch_size; + + switch (arizona->rev) { + case 0: + patch = wm8997_sysclk_reva_patch; + patch_size = ARRAY_SIZE(wm8997_sysclk_reva_patch); + break; + default: + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (patch) + for (i = 0; i < patch_size; i++) + regmap_write_async(regmap, patch[i].reg, + patch[i].def); + break; + case SND_SOC_DAPM_PRE_PMD: + break; + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + return arizona_clk_ev(w, kcontrol, event); + default: + return 0; + } + + return arizona_dvfs_sysclk_ev(w, kcontrol, event); +} + +static const char * const wm8997_osr_text[] = { + "Low power", "Normal", "High performance", +}; + +static const unsigned int wm8997_osr_val[] = { + 0x0, 0x3, 0x5, +}; + +static const struct soc_enum wm8997_hpout_osr[] = { + SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L, + ARIZONA_OUT1_OSR_SHIFT, 0x7, + ARRAY_SIZE(wm8997_osr_text), + wm8997_osr_text, wm8997_osr_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L, + ARIZONA_OUT3_OSR_SHIFT, 0x7, + ARRAY_SIZE(wm8997_osr_text), + wm8997_osr_text, wm8997_osr_val), +}; + +#define WM8997_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + +static const struct snd_kcontrol_new wm8997_snd_controls[] = { +SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2_OSR_SHIFT, 1, 0), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +ARIZONA_EQ_CONTROL("EQ1 Coefficients", ARIZONA_EQ1_2), +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ2 Coefficients", ARIZONA_EQ2_2), +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ3 Coefficients", ARIZONA_EQ3_2), +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_EQ_CONTROL("EQ4 Coefficients", ARIZONA_EQ4_2), +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +ARIZONA_LHPF_CONTROL("LHPF1 Coefficients", ARIZONA_HPLPF1_2), +ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2), +ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2), +ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2), + +SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), + +ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv), + +ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L, + ARIZONA_OUT4_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L, + ARIZONA_OUT5_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_ENUM("HPOUT1 OSR", wm8997_hpout_osr[0]), +SOC_ENUM("EPOUT OSR", wm8997_hpout_osr[1]), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM8997_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM8997_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM8997_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM8997_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L), +WM8997_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM8997_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + +static const char *wm8997_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "EPOUT", "SPKOUT", "SPKDAT1L", "SPKDAT1R", +}; + +static const unsigned int wm8997_aec_loopback_values[] = { + 0, 1, 4, 6, 8, 9, +}; + +static const struct soc_enum wm8997_aec_loopback = + SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(wm8997_aec_loopback_texts), + wm8997_aec_loopback_texts, + wm8997_aec_loopback_values); + +static const struct snd_kcontrol_new wm8997_aec_loopback_mux = + SOC_DAPM_ENUM("AEC Loopback", wm8997_aec_loopback); + +static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, + 0, wm8997_sysclk_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1, + ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 6, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 7, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 4, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 5, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 6, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 7, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, &wm8997_aec_loopback_mux), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"), +ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"), +ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"), +ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(Mic, "Mic"), +ARIZONA_MIXER_WIDGETS(Noise, "Noise"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"), +ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), + +ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), +ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"), +ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("EPOUTN"), +SND_SOC_DAPM_OUTPUT("EPOUTP"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +SND_SOC_DAPM_OUTPUT("SPKOUTP"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "SLIMRX7", "SLIMRX7" }, \ + { name, "SLIMRX8", "SLIMRX8" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" } + +static const struct snd_soc_dapm_route wm8997_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT3L", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDD" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "Slim3 Capture", NULL, "SLIMTX7" }, + { "Slim3 Capture", NULL, "SLIMTX8" }, + + { "SLIMRX7", NULL, "Slim3 Playback" }, + { "SLIMRX8", NULL, "Slim3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + { "Slim3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + { "Slim3 Capture", NULL, "SYSCLK" }, + + { "IN1L PGA", NULL, "IN1L" }, + { "IN1R PGA", NULL, "IN1R" }, + + { "IN2L PGA", NULL, "IN2L" }, + { "IN2R PGA", NULL, "IN2R" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + + ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"), + ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"), + + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), + ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), + ARIZONA_MIXER_ROUTES("EQ3", "EQ3"), + ARIZONA_MIXER_ROUTES("EQ4", "EQ4"), + + ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + { "AEC Loopback", "HPOUT1L", "OUT1L" }, + { "AEC Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC Loopback", "EPOUT", "OUT3L" }, + { "EPOUTN", NULL, "OUT3L" }, + { "EPOUTP", NULL, "OUT3L" }, + + { "AEC Loopback", "SPKOUT", "OUT4L" }, + { "SPKOUTN", NULL, "OUT4L" }, + { "SPKOUTP", NULL, "OUT4L" }, + + { "AEC Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC Loopback", "SPKDAT1R", "OUT5R" }, + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + { "MICSUPP", NULL, "SYSCLK" }, +}; + +static int wm8997_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int Fref, unsigned int Fout) +{ + struct wm8997_priv *wm8997 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case WM8997_FLL1: + return arizona_set_fll(&wm8997->fll[0], source, Fref, Fout); + case WM8997_FLL2: + return arizona_set_fll(&wm8997->fll[1], source, Fref, Fout); + case WM8997_FLL1_REFCLK: + return arizona_set_fll_refclk(&wm8997->fll[0], source, Fref, + Fout); + case WM8997_FLL2_REFCLK: + return arizona_set_fll_refclk(&wm8997->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +#define WM8997_RATES SNDRV_PCM_RATE_KNOT + +#define WM8997_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8997_dai[] = { + { + .name = "wm8997-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm8997-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm8997-slim1", + .id = 3, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm8997-slim2", + .id = 4, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm8997-slim3", + .id = 5, + .playback = { + .stream_name = "Slim3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "Slim3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, +}; + +static int wm8997_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8997_priv *priv = snd_soc_component_get_drvdata(component); + struct arizona *arizona = priv->core.arizona; + int ret; + + snd_soc_component_init_regmap(component, arizona->regmap); + + ret = arizona_init_spk(component); + if (ret < 0) + return ret; + + snd_soc_component_disable_pin(component, "HAPTICS"); + + priv->core.arizona->dapm = dapm; + + return 0; +} + +static void wm8997_component_remove(struct snd_soc_component *component) +{ + struct wm8997_priv *priv = snd_soc_component_get_drvdata(component); + + priv->core.arizona->dapm = NULL; +} + +#define WM8997_DIG_VU 0x0200 + +static unsigned int wm8997_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8997 = { + .probe = wm8997_component_probe, + .remove = wm8997_component_remove, + .set_sysclk = arizona_set_sysclk, + .set_pll = wm8997_set_fll, + .set_jack = arizona_jack_set_jack, + .controls = wm8997_snd_controls, + .num_controls = ARRAY_SIZE(wm8997_snd_controls), + .dapm_widgets = wm8997_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets), + .dapm_routes = wm8997_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm8997_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm8997_priv *wm8997; + int i, ret; + + wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv), + GFP_KERNEL); + if (wm8997 == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, wm8997); + + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + + wm8997->core.arizona = arizona; + wm8997->core.num_inputs = 4; + + arizona_init_dvfs(&wm8997->core); + + /* This may return -EPROBE_DEFER, so do this early on */ + ret = arizona_jack_codec_dev_probe(&wm8997->core, &pdev->dev); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++) + wm8997->fll[i].vco_mult = 1; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm8997->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm8997->fll[1]); + + /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */ + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2, + ARIZONA_SAMPLE_RATE_2_MASK, 0x11); + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3, + ARIZONA_SAMPLE_RATE_3_MASK, 0x12); + + for (i = 0; i < ARRAY_SIZE(wm8997_dai); i++) + arizona_init_dai(&wm8997->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm8997_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm8997_digital_vu[i], + WM8997_DIG_VU, WM8997_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_jack_codec_dev; + ret = arizona_init_spk_irqs(arizona); + if (ret < 0) + goto err_jack_codec_dev; + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm8997, + wm8997_dai, + ARRAY_SIZE(wm8997_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto err_spk_irqs; + } + + return ret; + +err_spk_irqs: + arizona_free_spk_irqs(arizona); +err_jack_codec_dev: + pm_runtime_disable(&pdev->dev); + arizona_jack_codec_dev_remove(&wm8997->core); + + return ret; +} + +static void wm8997_remove(struct platform_device *pdev) +{ + struct wm8997_priv *wm8997 = platform_get_drvdata(pdev); + struct arizona *arizona = wm8997->core.arizona; + + pm_runtime_disable(&pdev->dev); + + arizona_free_spk_irqs(arizona); + + arizona_jack_codec_dev_remove(&wm8997->core); +} + +static struct platform_driver wm8997_codec_driver = { + .driver = { + .name = "wm8997-codec", + }, + .probe = wm8997_probe, + .remove_new = wm8997_remove, +}; + +module_platform_driver(wm8997_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8997 driver"); +MODULE_AUTHOR("Charles Keepax "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8997-codec"); diff --git a/sound/soc/codecs/wm8997.h b/sound/soc/codecs/wm8997.h new file mode 100644 index 0000000000..6fd7e30636 --- /dev/null +++ b/sound/soc/codecs/wm8997.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8997.h -- WM8997 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#ifndef _WM8997_H +#define _WM8997_H + +#include "arizona.h" + +#define WM8997_FLL1 1 +#define WM8997_FLL2 2 +#define WM8997_FLL1_REFCLK 3 +#define WM8997_FLL2_REFCLK 4 + +#endif diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c new file mode 100644 index 0000000000..3c2c4d12c0 --- /dev/null +++ b/sound/soc/codecs/wm8998.c @@ -0,0 +1,1437 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm8998.c -- ALSA SoC Audio driver for WM8998 codecs + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Richard Fitzgerald + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "arizona.h" +#include "wm8998.h" + +struct wm8998_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int val; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + val = snd_soc_component_read(component, ARIZONA_ASRC_RATE1); + val &= ARIZONA_ASRC_RATE1_MASK; + val >>= ARIZONA_ASRC_RATE1_SHIFT; + + switch (val) { + case 0: + case 1: + case 2: + val = snd_soc_component_read(component, + ARIZONA_SAMPLE_RATE_1 + val); + if (val >= 0x11) { + dev_warn(component->dev, + "Unsupported ASRC rate1 (%s)\n", + arizona_sample_rate_val_to_name(val)); + return -EINVAL; + } + break; + default: + dev_err(component->dev, + "Illegal ASRC rate1 selector (0x%x)\n", + val); + return -EINVAL; + } + + val = snd_soc_component_read(component, ARIZONA_ASRC_RATE2); + val &= ARIZONA_ASRC_RATE2_MASK; + val >>= ARIZONA_ASRC_RATE2_SHIFT; + + switch (val) { + case 8: + case 9: + val -= 0x8; + val = snd_soc_component_read(component, + ARIZONA_ASYNC_SAMPLE_RATE_1 + val); + if (val >= 0x11) { + dev_warn(component->dev, + "Unsupported ASRC rate2 (%s)\n", + arizona_sample_rate_val_to_name(val)); + return -EINVAL; + } + break; + default: + dev_err(component->dev, + "Illegal ASRC rate2 selector (0x%x)\n", + val); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8998_inmux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8998_priv *wm8998 = snd_soc_component_get_drvdata(component); + struct arizona *arizona = wm8998->core.arizona; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int mode_reg, mode_index; + unsigned int mux, inmode, src_val, mode_val; + int change, ret; + + mux = ucontrol->value.enumerated.item[0]; + if (mux > 1) + return -EINVAL; + + switch (e->reg) { + case ARIZONA_ADC_DIGITAL_VOLUME_2L: + mode_reg = ARIZONA_IN2L_CONTROL; + mode_index = 1 + (2 * mux); + break; + default: + mode_reg = ARIZONA_IN1L_CONTROL; + mode_index = (2 * mux); + break; + } + + inmode = arizona->pdata.inmode[mode_index]; + if (inmode & ARIZONA_INMODE_DMIC) + mode_val = 1 << ARIZONA_IN1_MODE_SHIFT; + else + mode_val = 0; + + src_val = mux << ARIZONA_IN1L_SRC_SHIFT; + if (inmode & ARIZONA_INMODE_SE) + src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT; + + snd_soc_component_update_bits(component, mode_reg, + ARIZONA_IN1_MODE_MASK, mode_val); + + change = snd_soc_component_update_bits(component, e->reg, + ARIZONA_IN1L_SRC_MASK | + ARIZONA_IN1L_SRC_SE_MASK, + src_val); + + ret = snd_soc_dapm_mux_update_power(dapm, kcontrol, + ucontrol->value.enumerated.item[0], + e, NULL); + if (ret < 0) { + dev_err(arizona->dev, "Failed to update demux power state: %d\n", ret); + return ret; + } + + return change; +} + +static const char * const wm8998_inmux_texts[] = { + "A", + "B", +}; + +static SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_SRC_SHIFT, + wm8998_inmux_texts); + +static SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum, + ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_SRC_SHIFT, + wm8998_inmux_texts); + +static SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum, + ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_SRC_SHIFT, + wm8998_inmux_texts); + +static const struct snd_kcontrol_new wm8998_in1mux[2] = { + SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum, + snd_soc_dapm_get_enum_double, wm8998_inmux_put), + SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum, + snd_soc_dapm_get_enum_double, wm8998_inmux_put), +}; + +static const struct snd_kcontrol_new wm8998_in2mux = + SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum, + snd_soc_dapm_get_enum_double, wm8998_inmux_put); + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define WM8998_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUTL Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUTR Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG LINEOUTL Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG LINEOUTR Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0) + +static const struct snd_kcontrol_new wm8998_snd_controls[] = { +SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum), + +SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_HPF_SHIFT, 1, 0), +SOC_SINGLE("IN2 HPF Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_HPF_SHIFT, 1, 0), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_GAINMUX_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19), +SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19), +SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19), +SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19), +SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE_SHIFT, 1, 0), +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_GAINMUX_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1), +SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1), +SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), +SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]), +SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]), +SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1), + +ARIZONA_MIXER_CONTROLS("HPOUTL", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUTR", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LINEOUTL", ARIZONA_OUT2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LINEOUTR", ARIZONA_OUT2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDATL", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDATR", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_DOUBLE_R("HPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("LINEOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("LINEOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_DOUBLE("SPKDAT Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM8998_NG_SRC("HPOUTL", ARIZONA_NOISE_GATE_SELECT_1L), +WM8998_NG_SRC("HPOUTR", ARIZONA_NOISE_GATE_SELECT_1R), +WM8998_NG_SRC("LINEOUTL", ARIZONA_NOISE_GATE_SELECT_2L), +WM8998_NG_SRC("LINEOUTR", ARIZONA_NOISE_GATE_SELECT_2R), +WM8998_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM8998_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM8998_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM8998_NG_SRC("SPKDATL", ARIZONA_NOISE_GATE_SELECT_5L), +WM8998_NG_SRC("SPKDATR", ARIZONA_NOISE_GATE_SELECT_5R), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +ARIZONA_GAINMUX_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), + +ARIZONA_GAINMUX_CONTROLS("SPDIFTX1", ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE), +ARIZONA_GAINMUX_CONTROLS("SPDIFTX2", ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MUX_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDATL, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDATR, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(SPD1TX1, ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(SPD1TX2, ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + +static const char * const wm8998_aec_loopback_texts[] = { + "HPOUTL", "HPOUTR", "LINEOUTL", "LINEOUTR", "EPOUT", + "SPKOUTL", "SPKOUTR", "SPKDATL", "SPKDATR", +}; + +static const unsigned int wm8998_aec_loopback_values[] = { + 0, 1, 2, 3, 4, 6, 7, 8, 9, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback, + ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); + +static SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback, + ARIZONA_DAC_AEC_CONTROL_2, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + wm8998_aec_loopback_texts, + wm8998_aec_loopback_values); + +static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = { + SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback), + SOC_DAPM_ENUM("AEC2 Loopback", wm8998_aec2_loopback), +}; + +static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, + ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1AL"), +SND_SOC_DAPM_INPUT("IN1AR"), +SND_SOC_DAPM_INPUT("IN1BL"), +SND_SOC_DAPM_INPUT("IN1BR"), +SND_SOC_DAPM_INPUT("IN2A"), +SND_SOC_DAPM_INPUT("IN2B"), + +SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[0]), +SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[1]), +SND_SOC_DAPM_MUX("IN2 Mux", SND_SOC_NOPM, 0, 0, &wm8998_in2mux), + +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2 PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA_E("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, + NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_MUX("AEC1 Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &wm8998_aec_loopback_mux[0]), + +SND_SOC_DAPM_MUX("AEC2 Loopback", ARIZONA_DAC_AEC_CONTROL_2, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &wm8998_aec_loopback_mux[1]), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 1, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 2, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 3, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 1, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 2, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 3, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 4, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 5, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, + ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, + ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_PGA("SPD1TX1", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_VAL1_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("SPD1TX2", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_VAL2_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPD1", ARIZONA_SPD1_TX_CONTROL, + ARIZONA_SPD1_ENA_SHIFT, 0, NULL, 0), + +ARIZONA_MUX_WIDGETS(EQ1, "EQ1"), +ARIZONA_MUX_WIDGETS(EQ2, "EQ2"), +ARIZONA_MUX_WIDGETS(EQ3, "EQ3"), +ARIZONA_MUX_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MUX_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MUX_WIDGETS(DRC1R, "DRC1R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUTL"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUTR"), +ARIZONA_MIXER_WIDGETS(OUT2L, "LINEOUTL"), +ARIZONA_MIXER_WIDGETS(OUT2R, "LINEOUTR"), +ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"), +ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"), +ARIZONA_MIXER_WIDGETS(SPKDATL, "SPKDATL"), +ARIZONA_MIXER_WIDGETS(SPKDATR, "SPKDATR"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), +ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"), +ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"), +ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"), +ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"), + +ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), +ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), + +ARIZONA_MUX_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MUX_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MUX_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MUX_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MUX_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MUX_WIDGETS(SLIMTX6, "SLIMTX6"), + +ARIZONA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"), +ARIZONA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"), + +ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), +ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), +ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), +ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), +ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"), +ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), +ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"), +ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("LINEOUTL"), +SND_SOC_DAPM_OUTPUT("LINEOUTR"), +SND_SOC_DAPM_OUTPUT("EPOUT"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKDATL"), +SND_SOC_DAPM_OUTPUT("SPKDATR"), +SND_SOC_DAPM_OUTPUT("SPDIF"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC1 Loopback" }, \ + { name, "AEC2", "AEC2 Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2 PGA" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "AIF2RX3", "AIF2RX3" }, \ + { name, "AIF2RX4", "AIF2RX4" }, \ + { name, "AIF2RX5", "AIF2RX5" }, \ + { name, "AIF2RX6", "AIF2RX6" }, \ + { name, "AIF3RX1", "AIF3RX1" }, \ + { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ASRC1L", "ASRC1L" }, \ + { name, "ASRC1R", "ASRC1R" }, \ + { name, "ASRC2L", "ASRC2L" }, \ + { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1DEC3", "ISRC1DEC3" }, \ + { name, "ISRC1DEC4", "ISRC1DEC4" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC1INT3", "ISRC1INT3" }, \ + { name, "ISRC1INT4", "ISRC1INT4" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" } + +static const struct snd_soc_dapm_route wm8998_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "AIF3 Capture", NULL, "DBVDD3" }, + { "AIF3 Playback", NULL, "DBVDD3" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT2L", NULL, "CPVDD" }, + { "OUT2R", NULL, "CPVDD" }, + { "OUT3", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDDL" }, + { "OUT4R", NULL, "SPKVDDR" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT2L", NULL, "SYSCLK" }, + { "OUT2R", NULL, "SYSCLK" }, + { "OUT3", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + { "OUT4R", NULL, "SYSCLK" }, + { "OUT5L", NULL, "SYSCLK" }, + { "OUT5R", NULL, "SYSCLK" }, + + { "IN1AL", NULL, "SYSCLK" }, + { "IN1AR", NULL, "SYSCLK" }, + { "IN1BL", NULL, "SYSCLK" }, + { "IN1BR", NULL, "SYSCLK" }, + { "IN2A", NULL, "SYSCLK" }, + { "IN2B", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "SYSCLK" }, + { "ASRC1R", NULL, "SYSCLK" }, + { "ASRC2L", NULL, "SYSCLK" }, + { "ASRC2R", NULL, "SYSCLK" }, + + { "ASRC1L", NULL, "ASYNCCLK" }, + { "ASRC1R", NULL, "ASYNCCLK" }, + { "ASRC2L", NULL, "ASYNCCLK" }, + { "ASRC2R", NULL, "ASYNCCLK" }, + + { "SPD1", NULL, "SYSCLK" }, + { "SPD1", NULL, "SPD1TX1" }, + { "SPD1", NULL, "SPD1TX2" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + { "AIF2 Capture", NULL, "AIF2TX3" }, + { "AIF2 Capture", NULL, "AIF2TX4" }, + { "AIF2 Capture", NULL, "AIF2TX5" }, + { "AIF2 Capture", NULL, "AIF2TX6" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + { "AIF2RX3", NULL, "AIF2 Playback" }, + { "AIF2RX4", NULL, "AIF2 Playback" }, + { "AIF2RX5", NULL, "AIF2 Playback" }, + { "AIF2RX6", NULL, "AIF2 Playback" }, + + { "AIF3 Capture", NULL, "AIF3TX1" }, + { "AIF3 Capture", NULL, "AIF3TX2" }, + + { "AIF3RX1", NULL, "AIF3 Playback" }, + { "AIF3RX2", NULL, "AIF3 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + + { "SLIMRX3", NULL, "Slim2 Playback" }, + { "SLIMRX4", NULL, "Slim2 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "AIF3 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "AIF3 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + + { "IN1L Mux", "A", "IN1AL" }, + { "IN1R Mux", "A", "IN1AR" }, + { "IN1L Mux", "B", "IN1BL" }, + { "IN1R Mux", "B", "IN1BR" }, + + { "IN2 Mux", "A", "IN2A" }, + { "IN2 Mux", "B", "IN2B" }, + + { "IN1L PGA", NULL, "IN1L Mux" }, + { "IN1R PGA", NULL, "IN1R Mux" }, + { "IN2 PGA", NULL, "IN2 Mux" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUTL"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUTR"), + ARIZONA_MIXER_ROUTES("OUT2L", "LINEOUTL"), + ARIZONA_MIXER_ROUTES("OUT2R", "LINEOUTR"), + ARIZONA_MIXER_ROUTES("OUT3", "EPOUT"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"), + ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDATL"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDATR"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"), + ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"), + ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"), + ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"), + + ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), + ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + + ARIZONA_MUX_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MUX_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MUX_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MUX_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MUX_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MUX_ROUTES("SLIMTX6", "SLIMTX6"), + + ARIZONA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"), + ARIZONA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"), + + ARIZONA_MUX_ROUTES("EQ1", "EQ1"), + ARIZONA_MUX_ROUTES("EQ2", "EQ2"), + ARIZONA_MUX_ROUTES("EQ3", "EQ3"), + ARIZONA_MUX_ROUTES("EQ4", "EQ4"), + + ARIZONA_MUX_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MUX_ROUTES("DRC1R", "DRC1R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), + ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"), + ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"), + ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + { "AEC1 Loopback", "HPOUTL", "OUT1L" }, + { "AEC1 Loopback", "HPOUTR", "OUT1R" }, + { "AEC2 Loopback", "HPOUTL", "OUT1L" }, + { "AEC2 Loopback", "HPOUTR", "OUT1R" }, + { "HPOUTL", NULL, "OUT1L" }, + { "HPOUTR", NULL, "OUT1R" }, + + { "AEC1 Loopback", "LINEOUTL", "OUT2L" }, + { "AEC1 Loopback", "LINEOUTR", "OUT2R" }, + { "AEC2 Loopback", "LINEOUTL", "OUT2L" }, + { "AEC2 Loopback", "LINEOUTR", "OUT2R" }, + { "LINEOUTL", NULL, "OUT2L" }, + { "LINEOUTR", NULL, "OUT2R" }, + + { "AEC1 Loopback", "EPOUT", "OUT3" }, + { "AEC2 Loopback", "EPOUT", "OUT3" }, + { "EPOUT", NULL, "OUT3" }, + + { "AEC1 Loopback", "SPKOUTL", "OUT4L" }, + { "AEC2 Loopback", "SPKOUTL", "OUT4L" }, + { "SPKOUTLN", NULL, "OUT4L" }, + { "SPKOUTLP", NULL, "OUT4L" }, + + { "AEC1 Loopback", "SPKOUTR", "OUT4R" }, + { "AEC2 Loopback", "SPKOUTR", "OUT4R" }, + { "SPKOUTRN", NULL, "OUT4R" }, + { "SPKOUTRP", NULL, "OUT4R" }, + + { "SPDIF", NULL, "SPD1" }, + + { "AEC1 Loopback", "SPKDATL", "OUT5L" }, + { "AEC1 Loopback", "SPKDATR", "OUT5R" }, + { "AEC2 Loopback", "SPKDATL", "OUT5L" }, + { "AEC2 Loopback", "SPKDATR", "OUT5R" }, + { "SPKDATL", NULL, "OUT5L" }, + { "SPKDATR", NULL, "OUT5R" }, + + { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "SYSCLK" }, + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, +}; + +#define WM8998_RATES SNDRV_PCM_RATE_KNOT + +#define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8998_dai[] = { + { + .name = "wm8998-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm8998-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 6, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm8998-aif3", + .id = 3, + .base = ARIZONA_AIF3_BCLK_CTRL, + .playback = { + .stream_name = "AIF3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "AIF3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + }, + { + .name = "wm8998-slim1", + .id = 4, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm8998-slim2", + .id = 5, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8998_RATES, + .formats = WM8998_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, +}; + +static int wm8998_set_fll(struct snd_soc_component *component, int fll_id, + int source, unsigned int Fref, unsigned int Fout) +{ + struct wm8998_priv *wm8998 = snd_soc_component_get_drvdata(component); + + switch (fll_id) { + case WM8998_FLL1: + return arizona_set_fll(&wm8998->fll[0], source, Fref, Fout); + case WM8998_FLL2: + return arizona_set_fll(&wm8998->fll[1], source, Fref, Fout); + case WM8998_FLL1_REFCLK: + return arizona_set_fll_refclk(&wm8998->fll[0], source, Fref, + Fout); + case WM8998_FLL2_REFCLK: + return arizona_set_fll_refclk(&wm8998->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +static int wm8998_component_probe(struct snd_soc_component *component) +{ + struct wm8998_priv *priv = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct arizona *arizona = priv->core.arizona; + int ret; + + arizona->dapm = dapm; + snd_soc_component_init_regmap(component, arizona->regmap); + + ret = arizona_init_spk(component); + if (ret < 0) + return ret; + + arizona_init_gpio(component); + + snd_soc_component_disable_pin(component, "HAPTICS"); + + return 0; +} + +static void wm8998_component_remove(struct snd_soc_component *component) +{ + struct wm8998_priv *priv = snd_soc_component_get_drvdata(component); + + priv->core.arizona->dapm = NULL; +} + +#define WM8998_DIG_VU 0x0200 + +static unsigned int wm8998_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_2L, + ARIZONA_DAC_DIGITAL_VOLUME_2R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_4R, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, +}; + +static const struct snd_soc_component_driver soc_component_dev_wm8998 = { + .probe = wm8998_component_probe, + .remove = wm8998_component_remove, + .set_sysclk = arizona_set_sysclk, + .set_pll = wm8998_set_fll, + .set_jack = arizona_jack_set_jack, + .controls = wm8998_snd_controls, + .num_controls = ARRAY_SIZE(wm8998_snd_controls), + .dapm_widgets = wm8998_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets), + .dapm_routes = wm8998_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm8998_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm8998_priv *wm8998; + int i, ret; + + wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv), + GFP_KERNEL); + if (!wm8998) + return -ENOMEM; + platform_set_drvdata(pdev, wm8998); + + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + + wm8998->core.arizona = arizona; + wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */ + + /* This may return -EPROBE_DEFER, so do this early on */ + ret = arizona_jack_codec_dev_probe(&wm8998->core, &pdev->dev); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(wm8998->fll); i++) + wm8998->fll[i].vco_mult = 1; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm8998->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm8998->fll[1]); + + for (i = 0; i < ARRAY_SIZE(wm8998_dai); i++) + arizona_init_dai(&wm8998->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm8998_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm8998_digital_vu[i], + WM8998_DIG_VU, WM8998_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + arizona_init_common(arizona); + + ret = arizona_init_spk_irqs(arizona); + if (ret < 0) + goto err_pm_disable; + + ret = devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm8998, + wm8998_dai, + ARRAY_SIZE(wm8998_dai)); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register component: %d\n", ret); + goto err_spk_irqs; + } + + return ret; + +err_spk_irqs: + arizona_free_spk_irqs(arizona); +err_pm_disable: + pm_runtime_disable(&pdev->dev); + arizona_jack_codec_dev_remove(&wm8998->core); + + return ret; +} + +static void wm8998_remove(struct platform_device *pdev) +{ + struct wm8998_priv *wm8998 = platform_get_drvdata(pdev); + struct arizona *arizona = wm8998->core.arizona; + + pm_runtime_disable(&pdev->dev); + + arizona_free_spk_irqs(arizona); + + arizona_jack_codec_dev_remove(&wm8998->core); +} + +static struct platform_driver wm8998_codec_driver = { + .driver = { + .name = "wm8998-codec", + }, + .probe = wm8998_probe, + .remove_new = wm8998_remove, +}; + +module_platform_driver(wm8998_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8998 driver"); +MODULE_AUTHOR("Richard Fitzgerald "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:wm8998-codec"); diff --git a/sound/soc/codecs/wm8998.h b/sound/soc/codecs/wm8998.h new file mode 100644 index 0000000000..a7f9391312 --- /dev/null +++ b/sound/soc/codecs/wm8998.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm8998.h -- ALSA SoC Audio driver for WM8998 codecs + * + * Copyright 2015 Cirrus Logic, Inc. + * + * Author: Richard Fitzgerald + */ + +#ifndef _WM8998_H +#define _WM8998_H + +#include "arizona.h" + +#define WM8998_FLL1 1 +#define WM8998_FLL2 2 +#define WM8998_FLL1_REFCLK 3 +#define WM8998_FLL2_REFCLK 4 + +#endif diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c new file mode 100644 index 0000000000..e7ec799573 --- /dev/null +++ b/sound/soc/codecs/wm9081.c @@ -0,0 +1,1381 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm9081.c -- WM9081 ALSA SoC Audio driver + * + * Author: Mark Brown + * + * Copyright 2009-12 Wolfson Microelectronics plc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "wm9081.h" + +static const struct reg_default wm9081_reg[] = { + { 2, 0x00B9 }, /* R2 - Analogue Lineout */ + { 3, 0x00B9 }, /* R3 - Analogue Speaker PGA */ + { 4, 0x0001 }, /* R4 - VMID Control */ + { 5, 0x0068 }, /* R5 - Bias Control 1 */ + { 7, 0x0000 }, /* R7 - Analogue Mixer */ + { 8, 0x0000 }, /* R8 - Anti Pop Control */ + { 9, 0x01DB }, /* R9 - Analogue Speaker 1 */ + { 10, 0x0018 }, /* R10 - Analogue Speaker 2 */ + { 11, 0x0180 }, /* R11 - Power Management */ + { 12, 0x0000 }, /* R12 - Clock Control 1 */ + { 13, 0x0038 }, /* R13 - Clock Control 2 */ + { 14, 0x4000 }, /* R14 - Clock Control 3 */ + { 16, 0x0000 }, /* R16 - FLL Control 1 */ + { 17, 0x0200 }, /* R17 - FLL Control 2 */ + { 18, 0x0000 }, /* R18 - FLL Control 3 */ + { 19, 0x0204 }, /* R19 - FLL Control 4 */ + { 20, 0x0000 }, /* R20 - FLL Control 5 */ + { 22, 0x0000 }, /* R22 - Audio Interface 1 */ + { 23, 0x0002 }, /* R23 - Audio Interface 2 */ + { 24, 0x0008 }, /* R24 - Audio Interface 3 */ + { 25, 0x0022 }, /* R25 - Audio Interface 4 */ + { 27, 0x0006 }, /* R27 - Interrupt Status Mask */ + { 28, 0x0000 }, /* R28 - Interrupt Polarity */ + { 29, 0x0000 }, /* R29 - Interrupt Control */ + { 30, 0x00C0 }, /* R30 - DAC Digital 1 */ + { 31, 0x0008 }, /* R31 - DAC Digital 2 */ + { 32, 0x09AF }, /* R32 - DRC 1 */ + { 33, 0x4201 }, /* R33 - DRC 2 */ + { 34, 0x0000 }, /* R34 - DRC 3 */ + { 35, 0x0000 }, /* R35 - DRC 4 */ + { 38, 0x0000 }, /* R38 - Write Sequencer 1 */ + { 39, 0x0000 }, /* R39 - Write Sequencer 2 */ + { 40, 0x0002 }, /* R40 - MW Slave 1 */ + { 42, 0x0000 }, /* R42 - EQ 1 */ + { 43, 0x0000 }, /* R43 - EQ 2 */ + { 44, 0x0FCA }, /* R44 - EQ 3 */ + { 45, 0x0400 }, /* R45 - EQ 4 */ + { 46, 0x00B8 }, /* R46 - EQ 5 */ + { 47, 0x1EB5 }, /* R47 - EQ 6 */ + { 48, 0xF145 }, /* R48 - EQ 7 */ + { 49, 0x0B75 }, /* R49 - EQ 8 */ + { 50, 0x01C5 }, /* R50 - EQ 9 */ + { 51, 0x169E }, /* R51 - EQ 10 */ + { 52, 0xF829 }, /* R52 - EQ 11 */ + { 53, 0x07AD }, /* R53 - EQ 12 */ + { 54, 0x1103 }, /* R54 - EQ 13 */ + { 55, 0x1C58 }, /* R55 - EQ 14 */ + { 56, 0xF373 }, /* R56 - EQ 15 */ + { 57, 0x0A54 }, /* R57 - EQ 16 */ + { 58, 0x0558 }, /* R58 - EQ 17 */ + { 59, 0x0564 }, /* R59 - EQ 18 */ + { 60, 0x0559 }, /* R60 - EQ 19 */ + { 61, 0x4000 }, /* R61 - EQ 20 */ +}; + +static struct { + int ratio; + int clk_sys_rate; +} clk_sys_rates[] = { + { 64, 0 }, + { 128, 1 }, + { 192, 2 }, + { 256, 3 }, + { 384, 4 }, + { 512, 5 }, + { 768, 6 }, + { 1024, 7 }, + { 1408, 8 }, + { 1536, 9 }, +}; + +static struct { + int rate; + int sample_rate; +} sample_rates[] = { + { 8000, 0 }, + { 11025, 1 }, + { 12000, 2 }, + { 16000, 3 }, + { 22050, 4 }, + { 24000, 5 }, + { 32000, 6 }, + { 44100, 7 }, + { 48000, 8 }, + { 88200, 9 }, + { 96000, 10 }, +}; + +static struct { + int div; /* *10 due to .5s */ + int bclk_div; +} bclk_divs[] = { + { 10, 0 }, + { 15, 1 }, + { 20, 2 }, + { 30, 3 }, + { 40, 4 }, + { 50, 5 }, + { 55, 6 }, + { 60, 7 }, + { 80, 8 }, + { 100, 9 }, + { 110, 10 }, + { 120, 11 }, + { 160, 12 }, + { 200, 13 }, + { 220, 14 }, + { 240, 15 }, + { 250, 16 }, + { 300, 17 }, + { 320, 18 }, + { 440, 19 }, + { 480, 20 }, +}; + +struct wm9081_priv { + struct regmap *regmap; + int sysclk_source; + int mclk_rate; + int sysclk_rate; + int fs; + int bclk; + int master; + int fll_fref; + int fll_fout; + int tdm_width; + struct wm9081_pdata pdata; +}; + +static bool wm9081_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM9081_SOFTWARE_RESET: + case WM9081_INTERRUPT_STATUS: + return true; + default: + return false; + } +} + +static bool wm9081_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM9081_SOFTWARE_RESET: + case WM9081_ANALOGUE_LINEOUT: + case WM9081_ANALOGUE_SPEAKER_PGA: + case WM9081_VMID_CONTROL: + case WM9081_BIAS_CONTROL_1: + case WM9081_ANALOGUE_MIXER: + case WM9081_ANTI_POP_CONTROL: + case WM9081_ANALOGUE_SPEAKER_1: + case WM9081_ANALOGUE_SPEAKER_2: + case WM9081_POWER_MANAGEMENT: + case WM9081_CLOCK_CONTROL_1: + case WM9081_CLOCK_CONTROL_2: + case WM9081_CLOCK_CONTROL_3: + case WM9081_FLL_CONTROL_1: + case WM9081_FLL_CONTROL_2: + case WM9081_FLL_CONTROL_3: + case WM9081_FLL_CONTROL_4: + case WM9081_FLL_CONTROL_5: + case WM9081_AUDIO_INTERFACE_1: + case WM9081_AUDIO_INTERFACE_2: + case WM9081_AUDIO_INTERFACE_3: + case WM9081_AUDIO_INTERFACE_4: + case WM9081_INTERRUPT_STATUS: + case WM9081_INTERRUPT_STATUS_MASK: + case WM9081_INTERRUPT_POLARITY: + case WM9081_INTERRUPT_CONTROL: + case WM9081_DAC_DIGITAL_1: + case WM9081_DAC_DIGITAL_2: + case WM9081_DRC_1: + case WM9081_DRC_2: + case WM9081_DRC_3: + case WM9081_DRC_4: + case WM9081_WRITE_SEQUENCER_1: + case WM9081_WRITE_SEQUENCER_2: + case WM9081_MW_SLAVE_1: + case WM9081_EQ_1: + case WM9081_EQ_2: + case WM9081_EQ_3: + case WM9081_EQ_4: + case WM9081_EQ_5: + case WM9081_EQ_6: + case WM9081_EQ_7: + case WM9081_EQ_8: + case WM9081_EQ_9: + case WM9081_EQ_10: + case WM9081_EQ_11: + case WM9081_EQ_12: + case WM9081_EQ_13: + case WM9081_EQ_14: + case WM9081_EQ_15: + case WM9081_EQ_16: + case WM9081_EQ_17: + case WM9081_EQ_18: + case WM9081_EQ_19: + case WM9081_EQ_20: + return true; + default: + return false; + } +} + +static int wm9081_reset(struct regmap *map) +{ + return regmap_write(map, WM9081_SOFTWARE_RESET, 0x9081); +} + +static const DECLARE_TLV_DB_SCALE(drc_in_tlv, -4500, 75, 0); +static const DECLARE_TLV_DB_SCALE(drc_out_tlv, -2250, 75, 0); +static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0); +static const DECLARE_TLV_DB_RANGE(drc_max_tlv, + 0, 0, TLV_DB_SCALE_ITEM(1200, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0) +); +static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -300, 50, 0); + +static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); + +static const DECLARE_TLV_DB_SCALE(in_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 75, 1); +static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); + +static const char *drc_high_text[] = { + "1", + "1/2", + "1/4", + "1/8", + "1/16", + "0", +}; + +static SOC_ENUM_SINGLE_DECL(drc_high, WM9081_DRC_3, 3, drc_high_text); + +static const char *drc_low_text[] = { + "1", + "1/2", + "1/4", + "1/8", + "0", +}; + +static SOC_ENUM_SINGLE_DECL(drc_low, WM9081_DRC_3, 0, drc_low_text); + +static const char *drc_atk_text[] = { + "181us", + "181us", + "363us", + "726us", + "1.45ms", + "2.9ms", + "5.8ms", + "11.6ms", + "23.2ms", + "46.4ms", + "92.8ms", + "185.6ms", +}; + +static SOC_ENUM_SINGLE_DECL(drc_atk, WM9081_DRC_2, 12, drc_atk_text); + +static const char *drc_dcy_text[] = { + "186ms", + "372ms", + "743ms", + "1.49s", + "2.97s", + "5.94s", + "11.89s", + "23.78s", + "47.56s", +}; + +static SOC_ENUM_SINGLE_DECL(drc_dcy, WM9081_DRC_2, 8, drc_dcy_text); + +static const char *drc_qr_dcy_text[] = { + "0.725ms", + "1.45ms", + "5.8ms", +}; + +static SOC_ENUM_SINGLE_DECL(drc_qr_dcy, WM9081_DRC_2, 4, drc_qr_dcy_text); + +static const char *dac_deemph_text[] = { + "None", + "32kHz", + "44.1kHz", + "48kHz", +}; + +static SOC_ENUM_SINGLE_DECL(dac_deemph, WM9081_DAC_DIGITAL_2, 1, + dac_deemph_text); + +static const char *speaker_mode_text[] = { + "Class D", + "Class AB", +}; + +static SOC_ENUM_SINGLE_DECL(speaker_mode, WM9081_ANALOGUE_SPEAKER_2, 6, + speaker_mode_text); + +static int speaker_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg; + + reg = snd_soc_component_read(component, WM9081_ANALOGUE_SPEAKER_2); + if (reg & WM9081_SPK_MODE) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + return 0; +} + +/* + * Stop any attempts to change speaker mode while the speaker is enabled. + * + * We also have some special anti-pop controls dependent on speaker + * mode which must be changed along with the mode. + */ +static int speaker_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int reg_pwr = snd_soc_component_read(component, WM9081_POWER_MANAGEMENT); + unsigned int reg2 = snd_soc_component_read(component, WM9081_ANALOGUE_SPEAKER_2); + + /* Are we changing anything? */ + if (ucontrol->value.enumerated.item[0] == + ((reg2 & WM9081_SPK_MODE) != 0)) + return 0; + + /* Don't try to change modes while enabled */ + if (reg_pwr & WM9081_SPK_ENA) + return -EINVAL; + + if (ucontrol->value.enumerated.item[0]) { + /* Class AB */ + reg2 &= ~(WM9081_SPK_INV_MUTE | WM9081_OUT_SPK_CTRL); + reg2 |= WM9081_SPK_MODE; + } else { + /* Class D */ + reg2 |= WM9081_SPK_INV_MUTE | WM9081_OUT_SPK_CTRL; + reg2 &= ~WM9081_SPK_MODE; + } + + snd_soc_component_write(component, WM9081_ANALOGUE_SPEAKER_2, reg2); + + return 0; +} + +static const struct snd_kcontrol_new wm9081_snd_controls[] = { +SOC_SINGLE_TLV("IN1 Volume", WM9081_ANALOGUE_MIXER, 1, 1, 1, in_tlv), +SOC_SINGLE_TLV("IN2 Volume", WM9081_ANALOGUE_MIXER, 3, 1, 1, in_tlv), + +SOC_SINGLE_TLV("Playback Volume", WM9081_DAC_DIGITAL_1, 1, 96, 0, dac_tlv), + +SOC_SINGLE("LINEOUT Switch", WM9081_ANALOGUE_LINEOUT, 7, 1, 1), +SOC_SINGLE("LINEOUT ZC Switch", WM9081_ANALOGUE_LINEOUT, 6, 1, 0), +SOC_SINGLE_TLV("LINEOUT Volume", WM9081_ANALOGUE_LINEOUT, 0, 63, 0, out_tlv), + +SOC_SINGLE("DRC Switch", WM9081_DRC_1, 15, 1, 0), +SOC_ENUM("DRC High Slope", drc_high), +SOC_ENUM("DRC Low Slope", drc_low), +SOC_SINGLE_TLV("DRC Input Volume", WM9081_DRC_4, 5, 60, 1, drc_in_tlv), +SOC_SINGLE_TLV("DRC Output Volume", WM9081_DRC_4, 0, 30, 1, drc_out_tlv), +SOC_SINGLE_TLV("DRC Minimum Volume", WM9081_DRC_2, 2, 3, 1, drc_min_tlv), +SOC_SINGLE_TLV("DRC Maximum Volume", WM9081_DRC_2, 0, 3, 0, drc_max_tlv), +SOC_ENUM("DRC Attack", drc_atk), +SOC_ENUM("DRC Decay", drc_dcy), +SOC_SINGLE("DRC Quick Release Switch", WM9081_DRC_1, 2, 1, 0), +SOC_SINGLE_TLV("DRC Quick Release Volume", WM9081_DRC_2, 6, 3, 0, drc_qr_tlv), +SOC_ENUM("DRC Quick Release Decay", drc_qr_dcy), +SOC_SINGLE_TLV("DRC Startup Volume", WM9081_DRC_1, 6, 18, 0, drc_startup_tlv), + +SOC_SINGLE("EQ Switch", WM9081_EQ_1, 0, 1, 0), + +SOC_SINGLE("Speaker DC Volume", WM9081_ANALOGUE_SPEAKER_1, 3, 5, 0), +SOC_SINGLE("Speaker AC Volume", WM9081_ANALOGUE_SPEAKER_1, 0, 5, 0), +SOC_SINGLE("Speaker Switch", WM9081_ANALOGUE_SPEAKER_PGA, 7, 1, 1), +SOC_SINGLE("Speaker ZC Switch", WM9081_ANALOGUE_SPEAKER_PGA, 6, 1, 0), +SOC_SINGLE_TLV("Speaker Volume", WM9081_ANALOGUE_SPEAKER_PGA, 0, 63, 0, + out_tlv), +SOC_ENUM("DAC Deemphasis", dac_deemph), +SOC_ENUM_EXT("Speaker Mode", speaker_mode, speaker_mode_get, speaker_mode_put), +}; + +static const struct snd_kcontrol_new wm9081_eq_controls[] = { +SOC_SINGLE_TLV("EQ1 Volume", WM9081_EQ_1, 11, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 Volume", WM9081_EQ_1, 6, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 Volume", WM9081_EQ_1, 1, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 Volume", WM9081_EQ_2, 11, 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ5 Volume", WM9081_EQ_2, 6, 24, 0, eq_tlv), +}; + +static const struct snd_kcontrol_new mixer[] = { +SOC_DAPM_SINGLE("IN1 Switch", WM9081_ANALOGUE_MIXER, 0, 1, 0), +SOC_DAPM_SINGLE("IN2 Switch", WM9081_ANALOGUE_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("Playback Switch", WM9081_ANALOGUE_MIXER, 4, 1, 0), +}; + +struct _fll_div { + u16 fll_fratio; + u16 fll_outdiv; + u16 fll_clk_ref_div; + u16 n; + u16 k; +}; + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +static struct { + unsigned int min; + unsigned int max; + u16 fll_fratio; + int ratio; +} fll_fratios[] = { + { 0, 64000, 4, 16 }, + { 64000, 128000, 3, 8 }, + { 128000, 256000, 2, 4 }, + { 256000, 1000000, 1, 2 }, + { 1000000, 13500000, 0, 1 }, +}; + +static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, + unsigned int Fout) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, target; + unsigned int div; + int i; + + /* Fref must be <=13.5MHz */ + div = 1; + while ((Fref / div) > 13500000) { + div *= 2; + + if (div > 8) { + pr_err("Can't scale %dMHz input down to <=13.5MHz\n", + Fref); + return -EINVAL; + } + } + fll_div->fll_clk_ref_div = div / 2; + + pr_debug("Fref=%u Fout=%u\n", Fref, Fout); + + /* Apply the division for our remaining calculations */ + Fref /= div; + + /* Fvco should be 90-100MHz; don't check the upper bound */ + div = 0; + target = Fout * 2; + while (target < 90000000) { + div++; + target *= 2; + if (div > 7) { + pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n", + Fout); + return -EINVAL; + } + } + fll_div->fll_outdiv = div; + + pr_debug("Fvco=%dHz\n", target); + + /* Find an appropriate FLL_FRATIO and factor it out of the target */ + for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) { + if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) { + fll_div->fll_fratio = fll_fratios[i].fll_fratio; + target /= fll_fratios[i].ratio; + break; + } + } + if (i == ARRAY_SIZE(fll_fratios)) { + pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref); + return -EINVAL; + } + + /* Now, calculate N.K */ + Ndiv = target / Fref; + + fll_div->n = Ndiv; + Nmod = target % Fref; + pr_debug("Nmod=%d\n", Nmod); + + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, Fref); + + K = Kpart & 0xFFFFFFFF; + + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + fll_div->k = K / 10; + + pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n", + fll_div->n, fll_div->k, + fll_div->fll_fratio, fll_div->fll_outdiv, + fll_div->fll_clk_ref_div); + + return 0; +} + +static int wm9081_set_fll(struct snd_soc_component *component, int fll_id, + unsigned int Fref, unsigned int Fout) +{ + struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component); + u16 reg1, reg4, reg5; + struct _fll_div fll_div; + int ret; + int clk_sys_reg; + + /* Any change? */ + if (Fref == wm9081->fll_fref && Fout == wm9081->fll_fout) + return 0; + + /* Disable the FLL */ + if (Fout == 0) { + dev_dbg(component->dev, "FLL disabled\n"); + wm9081->fll_fref = 0; + wm9081->fll_fout = 0; + + return 0; + } + + ret = fll_factors(&fll_div, Fref, Fout); + if (ret != 0) + return ret; + + reg5 = snd_soc_component_read(component, WM9081_FLL_CONTROL_5); + reg5 &= ~WM9081_FLL_CLK_SRC_MASK; + + switch (fll_id) { + case WM9081_SYSCLK_FLL_MCLK: + reg5 |= 0x1; + break; + + default: + dev_err(component->dev, "Unknown FLL ID %d\n", fll_id); + return -EINVAL; + } + + /* Disable CLK_SYS while we reconfigure */ + clk_sys_reg = snd_soc_component_read(component, WM9081_CLOCK_CONTROL_3); + if (clk_sys_reg & WM9081_CLK_SYS_ENA) + snd_soc_component_write(component, WM9081_CLOCK_CONTROL_3, + clk_sys_reg & ~WM9081_CLK_SYS_ENA); + + /* Any FLL configuration change requires that the FLL be + * disabled first. */ + reg1 = snd_soc_component_read(component, WM9081_FLL_CONTROL_1); + reg1 &= ~WM9081_FLL_ENA; + snd_soc_component_write(component, WM9081_FLL_CONTROL_1, reg1); + + /* Apply the configuration */ + if (fll_div.k) + reg1 |= WM9081_FLL_FRAC_MASK; + else + reg1 &= ~WM9081_FLL_FRAC_MASK; + snd_soc_component_write(component, WM9081_FLL_CONTROL_1, reg1); + + snd_soc_component_write(component, WM9081_FLL_CONTROL_2, + (fll_div.fll_outdiv << WM9081_FLL_OUTDIV_SHIFT) | + (fll_div.fll_fratio << WM9081_FLL_FRATIO_SHIFT)); + snd_soc_component_write(component, WM9081_FLL_CONTROL_3, fll_div.k); + + reg4 = snd_soc_component_read(component, WM9081_FLL_CONTROL_4); + reg4 &= ~WM9081_FLL_N_MASK; + reg4 |= fll_div.n << WM9081_FLL_N_SHIFT; + snd_soc_component_write(component, WM9081_FLL_CONTROL_4, reg4); + + reg5 &= ~WM9081_FLL_CLK_REF_DIV_MASK; + reg5 |= fll_div.fll_clk_ref_div << WM9081_FLL_CLK_REF_DIV_SHIFT; + snd_soc_component_write(component, WM9081_FLL_CONTROL_5, reg5); + + /* Set gain to the recommended value */ + snd_soc_component_update_bits(component, WM9081_FLL_CONTROL_4, + WM9081_FLL_GAIN_MASK, 0); + + /* Enable the FLL */ + snd_soc_component_write(component, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA); + + /* Then bring CLK_SYS up again if it was disabled */ + if (clk_sys_reg & WM9081_CLK_SYS_ENA) + snd_soc_component_write(component, WM9081_CLOCK_CONTROL_3, clk_sys_reg); + + dev_dbg(component->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout); + + wm9081->fll_fref = Fref; + wm9081->fll_fout = Fout; + + return 0; +} + +static int configure_clock(struct snd_soc_component *component) +{ + struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component); + int new_sysclk, i, target; + unsigned int reg; + int ret = 0; + int mclkdiv = 0; + int fll = 0; + + switch (wm9081->sysclk_source) { + case WM9081_SYSCLK_MCLK: + if (wm9081->mclk_rate > 12225000) { + mclkdiv = 1; + wm9081->sysclk_rate = wm9081->mclk_rate / 2; + } else { + wm9081->sysclk_rate = wm9081->mclk_rate; + } + wm9081_set_fll(component, WM9081_SYSCLK_FLL_MCLK, 0, 0); + break; + + case WM9081_SYSCLK_FLL_MCLK: + /* If we have a sample rate calculate a CLK_SYS that + * gives us a suitable DAC configuration, plus BCLK. + * Ideally we would check to see if we can clock + * directly from MCLK and only use the FLL if this is + * not the case, though care must be taken with free + * running mode. + */ + if (wm9081->master && wm9081->bclk) { + /* Make sure we can generate CLK_SYS and BCLK + * and that we've got 3MHz for optimal + * performance. */ + for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) { + target = wm9081->fs * clk_sys_rates[i].ratio; + new_sysclk = target; + if (target >= wm9081->bclk && + target > 3000000) + break; + } + + if (i == ARRAY_SIZE(clk_sys_rates)) + return -EINVAL; + + } else if (wm9081->fs) { + for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) { + new_sysclk = clk_sys_rates[i].ratio + * wm9081->fs; + if (new_sysclk > 3000000) + break; + } + + if (i == ARRAY_SIZE(clk_sys_rates)) + return -EINVAL; + + } else { + new_sysclk = 12288000; + } + + ret = wm9081_set_fll(component, WM9081_SYSCLK_FLL_MCLK, + wm9081->mclk_rate, new_sysclk); + if (ret == 0) { + wm9081->sysclk_rate = new_sysclk; + + /* Switch SYSCLK over to FLL */ + fll = 1; + } else { + wm9081->sysclk_rate = wm9081->mclk_rate; + } + break; + + default: + return -EINVAL; + } + + reg = snd_soc_component_read(component, WM9081_CLOCK_CONTROL_1); + if (mclkdiv) + reg |= WM9081_MCLKDIV2; + else + reg &= ~WM9081_MCLKDIV2; + snd_soc_component_write(component, WM9081_CLOCK_CONTROL_1, reg); + + reg = snd_soc_component_read(component, WM9081_CLOCK_CONTROL_3); + if (fll) + reg |= WM9081_CLK_SRC_SEL; + else + reg &= ~WM9081_CLK_SRC_SEL; + snd_soc_component_write(component, WM9081_CLOCK_CONTROL_3, reg); + + dev_dbg(component->dev, "CLK_SYS is %dHz\n", wm9081->sysclk_rate); + + return ret; +} + +static int clk_sys_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component); + + /* This should be done on init() for bypass paths */ + switch (wm9081->sysclk_source) { + case WM9081_SYSCLK_MCLK: + dev_dbg(component->dev, "Using %dHz MCLK\n", wm9081->mclk_rate); + break; + case WM9081_SYSCLK_FLL_MCLK: + dev_dbg(component->dev, "Using %dHz MCLK with FLL\n", + wm9081->mclk_rate); + break; + default: + dev_err(component->dev, "System clock not configured\n"); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + configure_clock(component); + break; + + case SND_SOC_DAPM_POST_PMD: + /* Disable the FLL if it's running */ + wm9081_set_fll(component, 0, 0, 0); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget wm9081_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1"), +SND_SOC_DAPM_INPUT("IN2"), + +SND_SOC_DAPM_DAC("DAC", NULL, WM9081_POWER_MANAGEMENT, 0, 0), + +SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0, + mixer, ARRAY_SIZE(mixer)), + +SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0), + +SND_SOC_DAPM_OUTPUT("LINEOUT"), +SND_SOC_DAPM_OUTPUT("SPKN"), +SND_SOC_DAPM_OUTPUT("SPKP"), + +SND_SOC_DAPM_SUPPLY("CLK_SYS", WM9081_CLOCK_CONTROL_3, 0, 0, clk_sys_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("CLK_DSP", WM9081_CLOCK_CONTROL_3, 1, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("TOCLK", WM9081_CLOCK_CONTROL_3, 2, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("TSENSE", WM9081_POWER_MANAGEMENT, 7, 0, NULL, 0), +}; + + +static const struct snd_soc_dapm_route wm9081_audio_paths[] = { + { "DAC", NULL, "CLK_SYS" }, + { "DAC", NULL, "CLK_DSP" }, + { "DAC", NULL, "AIF" }, + + { "Mixer", "IN1 Switch", "IN1" }, + { "Mixer", "IN2 Switch", "IN2" }, + { "Mixer", "Playback Switch", "DAC" }, + + { "LINEOUT PGA", NULL, "Mixer" }, + { "LINEOUT PGA", NULL, "TOCLK" }, + { "LINEOUT PGA", NULL, "CLK_SYS" }, + + { "LINEOUT", NULL, "LINEOUT PGA" }, + + { "Speaker PGA", NULL, "Mixer" }, + { "Speaker PGA", NULL, "TOCLK" }, + { "Speaker PGA", NULL, "CLK_SYS" }, + + { "Speaker", NULL, "Speaker PGA" }, + { "Speaker", NULL, "TSENSE" }, + + { "SPKN", NULL, "Speaker" }, + { "SPKP", NULL, "Speaker" }, +}; + +static int wm9081_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID=2*40k */ + snd_soc_component_update_bits(component, WM9081_VMID_CONTROL, + WM9081_VMID_SEL_MASK, 0x2); + + /* Normal bias current */ + snd_soc_component_update_bits(component, WM9081_BIAS_CONTROL_1, + WM9081_STBY_BIAS_ENA, 0); + break; + + case SND_SOC_BIAS_STANDBY: + /* Initial cold start */ + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + regcache_cache_only(wm9081->regmap, false); + regcache_sync(wm9081->regmap); + + /* Disable LINEOUT discharge */ + snd_soc_component_update_bits(component, WM9081_ANTI_POP_CONTROL, + WM9081_LINEOUT_DISCH, 0); + + /* Select startup bias source */ + snd_soc_component_update_bits(component, WM9081_BIAS_CONTROL_1, + WM9081_BIAS_SRC | WM9081_BIAS_ENA, + WM9081_BIAS_SRC | WM9081_BIAS_ENA); + + /* VMID 2*4k; Soft VMID ramp enable */ + snd_soc_component_update_bits(component, WM9081_VMID_CONTROL, + WM9081_VMID_RAMP | + WM9081_VMID_SEL_MASK, + WM9081_VMID_RAMP | 0x6); + + mdelay(100); + + /* Normal bias enable & soft start off */ + snd_soc_component_update_bits(component, WM9081_VMID_CONTROL, + WM9081_VMID_RAMP, 0); + + /* Standard bias source */ + snd_soc_component_update_bits(component, WM9081_BIAS_CONTROL_1, + WM9081_BIAS_SRC, 0); + } + + /* VMID 2*240k */ + snd_soc_component_update_bits(component, WM9081_VMID_CONTROL, + WM9081_VMID_SEL_MASK, 0x04); + + /* Standby bias current on */ + snd_soc_component_update_bits(component, WM9081_BIAS_CONTROL_1, + WM9081_STBY_BIAS_ENA, + WM9081_STBY_BIAS_ENA); + break; + + case SND_SOC_BIAS_OFF: + /* Startup bias source and disable bias */ + snd_soc_component_update_bits(component, WM9081_BIAS_CONTROL_1, + WM9081_BIAS_SRC | WM9081_BIAS_ENA, + WM9081_BIAS_SRC); + + /* Disable VMID with soft ramping */ + snd_soc_component_update_bits(component, WM9081_VMID_CONTROL, + WM9081_VMID_RAMP | WM9081_VMID_SEL_MASK, + WM9081_VMID_RAMP); + + /* Actively discharge LINEOUT */ + snd_soc_component_update_bits(component, WM9081_ANTI_POP_CONTROL, + WM9081_LINEOUT_DISCH, + WM9081_LINEOUT_DISCH); + + regcache_cache_only(wm9081->regmap, true); + break; + } + + return 0; +} + +static int wm9081_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component); + unsigned int aif2 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_2); + + aif2 &= ~(WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV | + WM9081_BCLK_DIR | WM9081_LRCLK_DIR | WM9081_AIF_FMT_MASK); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + wm9081->master = 0; + break; + case SND_SOC_DAIFMT_CBS_CFM: + aif2 |= WM9081_LRCLK_DIR; + wm9081->master = 1; + break; + case SND_SOC_DAIFMT_CBM_CFS: + aif2 |= WM9081_BCLK_DIR; + wm9081->master = 1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + aif2 |= WM9081_LRCLK_DIR | WM9081_BCLK_DIR; + wm9081->master = 1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif2 |= WM9081_AIF_LRCLK_INV; + fallthrough; + case SND_SOC_DAIFMT_DSP_A: + aif2 |= 0x3; + break; + case SND_SOC_DAIFMT_I2S: + aif2 |= 0x2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif2 |= 0x1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif2 |= WM9081_AIF_BCLK_INV; + break; + default: + return -EINVAL; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif2 |= WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif2 |= WM9081_AIF_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif2 |= WM9081_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM9081_AUDIO_INTERFACE_2, aif2); + + return 0; +} + +static int wm9081_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component); + int ret, i, best, best_val, cur_val; + unsigned int clk_ctrl2, aif1, aif2, aif3, aif4; + + clk_ctrl2 = snd_soc_component_read(component, WM9081_CLOCK_CONTROL_2); + clk_ctrl2 &= ~(WM9081_CLK_SYS_RATE_MASK | WM9081_SAMPLE_RATE_MASK); + + aif1 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_1); + + aif2 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_2); + aif2 &= ~WM9081_AIF_WL_MASK; + + aif3 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_3); + aif3 &= ~WM9081_BCLK_DIV_MASK; + + aif4 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_4); + aif4 &= ~WM9081_LRCLK_RATE_MASK; + + wm9081->fs = params_rate(params); + + if (wm9081->tdm_width) { + /* If TDM is set up then that fixes our BCLK. */ + int slots = ((aif1 & WM9081_AIFDAC_TDM_MODE_MASK) >> + WM9081_AIFDAC_TDM_MODE_SHIFT) + 1; + + wm9081->bclk = wm9081->fs * wm9081->tdm_width * slots; + } else { + /* Otherwise work out a BCLK from the sample size */ + wm9081->bclk = 2 * wm9081->fs; + + switch (params_width(params)) { + case 16: + wm9081->bclk *= 16; + break; + case 20: + wm9081->bclk *= 20; + aif2 |= 0x4; + break; + case 24: + wm9081->bclk *= 24; + aif2 |= 0x8; + break; + case 32: + wm9081->bclk *= 32; + aif2 |= 0xc; + break; + default: + return -EINVAL; + } + } + + dev_dbg(component->dev, "Target BCLK is %dHz\n", wm9081->bclk); + + ret = configure_clock(component); + if (ret != 0) + return ret; + + /* Select nearest CLK_SYS_RATE */ + best = 0; + best_val = abs((wm9081->sysclk_rate / clk_sys_rates[0].ratio) + - wm9081->fs); + for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) { + cur_val = abs((wm9081->sysclk_rate / + clk_sys_rates[i].ratio) - wm9081->fs); + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + dev_dbg(component->dev, "Selected CLK_SYS_RATIO of %d\n", + clk_sys_rates[best].ratio); + clk_ctrl2 |= (clk_sys_rates[best].clk_sys_rate + << WM9081_CLK_SYS_RATE_SHIFT); + + /* SAMPLE_RATE */ + best = 0; + best_val = abs(wm9081->fs - sample_rates[0].rate); + for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { + /* Closest match */ + cur_val = abs(wm9081->fs - sample_rates[i].rate); + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + dev_dbg(component->dev, "Selected SAMPLE_RATE of %dHz\n", + sample_rates[best].rate); + clk_ctrl2 |= (sample_rates[best].sample_rate + << WM9081_SAMPLE_RATE_SHIFT); + + /* BCLK_DIV */ + best = 0; + best_val = INT_MAX; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = ((wm9081->sysclk_rate * 10) / bclk_divs[i].div) + - wm9081->bclk; + if (cur_val < 0) /* Table is sorted */ + break; + if (cur_val < best_val) { + best = i; + best_val = cur_val; + } + } + wm9081->bclk = (wm9081->sysclk_rate * 10) / bclk_divs[best].div; + dev_dbg(component->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n", + bclk_divs[best].div, wm9081->bclk); + aif3 |= bclk_divs[best].bclk_div; + + /* LRCLK is a simple fraction of BCLK */ + dev_dbg(component->dev, "LRCLK_RATE is %d\n", wm9081->bclk / wm9081->fs); + aif4 |= wm9081->bclk / wm9081->fs; + + /* Apply a ReTune Mobile configuration if it's in use */ + if (wm9081->pdata.num_retune_configs) { + struct wm9081_pdata *pdata = &wm9081->pdata; + struct wm9081_retune_mobile_setting *s; + int eq1; + + best = 0; + best_val = abs(pdata->retune_configs[0].rate - wm9081->fs); + for (i = 0; i < pdata->num_retune_configs; i++) { + cur_val = abs(pdata->retune_configs[i].rate - + wm9081->fs); + if (cur_val < best_val) { + best_val = cur_val; + best = i; + } + } + s = &pdata->retune_configs[best]; + + dev_dbg(component->dev, "ReTune Mobile %s tuned for %dHz\n", + s->name, s->rate); + + /* If the EQ is enabled then disable it while we write out */ + eq1 = snd_soc_component_read(component, WM9081_EQ_1) & WM9081_EQ_ENA; + if (eq1 & WM9081_EQ_ENA) + snd_soc_component_write(component, WM9081_EQ_1, 0); + + /* Write out the other values */ + for (i = 1; i < ARRAY_SIZE(s->config); i++) + snd_soc_component_write(component, WM9081_EQ_1 + i, s->config[i]); + + eq1 |= (s->config[0] & ~WM9081_EQ_ENA); + snd_soc_component_write(component, WM9081_EQ_1, eq1); + } + + snd_soc_component_write(component, WM9081_CLOCK_CONTROL_2, clk_ctrl2); + snd_soc_component_write(component, WM9081_AUDIO_INTERFACE_2, aif2); + snd_soc_component_write(component, WM9081_AUDIO_INTERFACE_3, aif3); + snd_soc_component_write(component, WM9081_AUDIO_INTERFACE_4, aif4); + + return 0; +} + +static int wm9081_mute(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + unsigned int reg; + + reg = snd_soc_component_read(component, WM9081_DAC_DIGITAL_2); + + if (mute) + reg |= WM9081_DAC_MUTE; + else + reg &= ~WM9081_DAC_MUTE; + + snd_soc_component_write(component, WM9081_DAC_DIGITAL_2, reg); + + return 0; +} + +static int wm9081_set_sysclk(struct snd_soc_component *component, int clk_id, + int source, unsigned int freq, int dir) +{ + struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component); + + switch (clk_id) { + case WM9081_SYSCLK_MCLK: + case WM9081_SYSCLK_FLL_MCLK: + wm9081->sysclk_source = clk_id; + wm9081->mclk_rate = freq; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int wm9081_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component); + unsigned int aif1 = snd_soc_component_read(component, WM9081_AUDIO_INTERFACE_1); + + aif1 &= ~(WM9081_AIFDAC_TDM_SLOT_MASK | WM9081_AIFDAC_TDM_MODE_MASK); + + if (slots < 0 || slots > 4) + return -EINVAL; + + wm9081->tdm_width = slot_width; + + if (slots == 0) + slots = 1; + + aif1 |= (slots - 1) << WM9081_AIFDAC_TDM_MODE_SHIFT; + + switch (rx_mask) { + case 1: + break; + case 2: + aif1 |= 0x10; + break; + case 4: + aif1 |= 0x20; + break; + case 8: + aif1 |= 0x30; + break; + default: + return -EINVAL; + } + + snd_soc_component_write(component, WM9081_AUDIO_INTERFACE_1, aif1); + + return 0; +} + +#define WM9081_RATES SNDRV_PCM_RATE_8000_96000 + +#define WM9081_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops wm9081_dai_ops = { + .hw_params = wm9081_hw_params, + .set_fmt = wm9081_set_dai_fmt, + .mute_stream = wm9081_mute, + .set_tdm_slot = wm9081_set_tdm_slot, + .no_capture_mute = 1, +}; + +/* We report two channels because the CODEC processes a stereo signal, even + * though it is only capable of handling a mono output. + */ +static struct snd_soc_dai_driver wm9081_dai = { + .name = "wm9081-hifi", + .playback = { + .stream_name = "AIF", + .channels_min = 1, + .channels_max = 2, + .rates = WM9081_RATES, + .formats = WM9081_FORMATS, + }, + .ops = &wm9081_dai_ops, +}; + +static int wm9081_probe(struct snd_soc_component *component) +{ + struct wm9081_priv *wm9081 = snd_soc_component_get_drvdata(component); + + /* Enable zero cross by default */ + snd_soc_component_update_bits(component, WM9081_ANALOGUE_LINEOUT, + WM9081_LINEOUTZC, WM9081_LINEOUTZC); + snd_soc_component_update_bits(component, WM9081_ANALOGUE_SPEAKER_PGA, + WM9081_SPKPGAZC, WM9081_SPKPGAZC); + + if (!wm9081->pdata.num_retune_configs) { + dev_dbg(component->dev, + "No ReTune Mobile data, using normal EQ\n"); + snd_soc_add_component_controls(component, wm9081_eq_controls, + ARRAY_SIZE(wm9081_eq_controls)); + } + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm9081 = { + .probe = wm9081_probe, + .set_sysclk = wm9081_set_sysclk, + .set_bias_level = wm9081_set_bias_level, + .controls = wm9081_snd_controls, + .num_controls = ARRAY_SIZE(wm9081_snd_controls), + .dapm_widgets = wm9081_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets), + .dapm_routes = wm9081_audio_paths, + .num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static const struct regmap_config wm9081_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM9081_MAX_REGISTER, + .reg_defaults = wm9081_reg, + .num_reg_defaults = ARRAY_SIZE(wm9081_reg), + .volatile_reg = wm9081_volatile_register, + .readable_reg = wm9081_readable_register, + .cache_type = REGCACHE_MAPLE, +}; + +static int wm9081_i2c_probe(struct i2c_client *i2c) +{ + struct wm9081_priv *wm9081; + unsigned int reg; + int ret; + + wm9081 = devm_kzalloc(&i2c->dev, sizeof(struct wm9081_priv), + GFP_KERNEL); + if (wm9081 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, wm9081); + + wm9081->regmap = devm_regmap_init_i2c(i2c, &wm9081_regmap); + if (IS_ERR(wm9081->regmap)) { + ret = PTR_ERR(wm9081->regmap); + dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = regmap_read(wm9081->regmap, WM9081_SOFTWARE_RESET, ®); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); + return ret; + } + if (reg != 0x9081) { + dev_err(&i2c->dev, "Device is not a WM9081: ID=0x%x\n", reg); + return -EINVAL; + } + + ret = wm9081_reset(wm9081->regmap); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to issue reset\n"); + return ret; + } + + if (dev_get_platdata(&i2c->dev)) + memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev), + sizeof(wm9081->pdata)); + + reg = 0; + if (wm9081->pdata.irq_high) + reg |= WM9081_IRQ_POL; + if (!wm9081->pdata.irq_cmos) + reg |= WM9081_IRQ_OP_CTRL; + regmap_update_bits(wm9081->regmap, WM9081_INTERRUPT_CONTROL, + WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg); + + regcache_cache_only(wm9081->regmap, true); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm9081, &wm9081_dai, 1); + if (ret < 0) + return ret; + + return 0; +} + +static void wm9081_i2c_remove(struct i2c_client *client) +{} + +static const struct i2c_device_id wm9081_i2c_id[] = { + { "wm9081", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id); + +static struct i2c_driver wm9081_i2c_driver = { + .driver = { + .name = "wm9081", + }, + .probe = wm9081_i2c_probe, + .remove = wm9081_i2c_remove, + .id_table = wm9081_i2c_id, +}; + +module_i2c_driver(wm9081_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM9081 driver"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm9081.h b/sound/soc/codecs/wm9081.h new file mode 100644 index 0000000000..dc55807d9a --- /dev/null +++ b/sound/soc/codecs/wm9081.h @@ -0,0 +1,781 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef WM9081_H +#define WM9081_H + +/* + * wm9081.c -- WM9081 ALSA SoC Audio driver + * + * Author: Mark Brown + * + * Copyright 2009 Wolfson Microelectronics plc + */ + +#include + +/* + * SYSCLK sources + */ +#define WM9081_SYSCLK_MCLK 1 /* Use MCLK without FLL */ +#define WM9081_SYSCLK_FLL_MCLK 2 /* Use MCLK, enabling FLL if required */ + +/* + * Register values. + */ +#define WM9081_SOFTWARE_RESET 0x00 +#define WM9081_ANALOGUE_LINEOUT 0x02 +#define WM9081_ANALOGUE_SPEAKER_PGA 0x03 +#define WM9081_VMID_CONTROL 0x04 +#define WM9081_BIAS_CONTROL_1 0x05 +#define WM9081_ANALOGUE_MIXER 0x07 +#define WM9081_ANTI_POP_CONTROL 0x08 +#define WM9081_ANALOGUE_SPEAKER_1 0x09 +#define WM9081_ANALOGUE_SPEAKER_2 0x0A +#define WM9081_POWER_MANAGEMENT 0x0B +#define WM9081_CLOCK_CONTROL_1 0x0C +#define WM9081_CLOCK_CONTROL_2 0x0D +#define WM9081_CLOCK_CONTROL_3 0x0E +#define WM9081_FLL_CONTROL_1 0x10 +#define WM9081_FLL_CONTROL_2 0x11 +#define WM9081_FLL_CONTROL_3 0x12 +#define WM9081_FLL_CONTROL_4 0x13 +#define WM9081_FLL_CONTROL_5 0x14 +#define WM9081_AUDIO_INTERFACE_1 0x16 +#define WM9081_AUDIO_INTERFACE_2 0x17 +#define WM9081_AUDIO_INTERFACE_3 0x18 +#define WM9081_AUDIO_INTERFACE_4 0x19 +#define WM9081_INTERRUPT_STATUS 0x1A +#define WM9081_INTERRUPT_STATUS_MASK 0x1B +#define WM9081_INTERRUPT_POLARITY 0x1C +#define WM9081_INTERRUPT_CONTROL 0x1D +#define WM9081_DAC_DIGITAL_1 0x1E +#define WM9081_DAC_DIGITAL_2 0x1F +#define WM9081_DRC_1 0x20 +#define WM9081_DRC_2 0x21 +#define WM9081_DRC_3 0x22 +#define WM9081_DRC_4 0x23 +#define WM9081_WRITE_SEQUENCER_1 0x26 +#define WM9081_WRITE_SEQUENCER_2 0x27 +#define WM9081_MW_SLAVE_1 0x28 +#define WM9081_EQ_1 0x2A +#define WM9081_EQ_2 0x2B +#define WM9081_EQ_3 0x2C +#define WM9081_EQ_4 0x2D +#define WM9081_EQ_5 0x2E +#define WM9081_EQ_6 0x2F +#define WM9081_EQ_7 0x30 +#define WM9081_EQ_8 0x31 +#define WM9081_EQ_9 0x32 +#define WM9081_EQ_10 0x33 +#define WM9081_EQ_11 0x34 +#define WM9081_EQ_12 0x35 +#define WM9081_EQ_13 0x36 +#define WM9081_EQ_14 0x37 +#define WM9081_EQ_15 0x38 +#define WM9081_EQ_16 0x39 +#define WM9081_EQ_17 0x3A +#define WM9081_EQ_18 0x3B +#define WM9081_EQ_19 0x3C +#define WM9081_EQ_20 0x3D + +#define WM9081_REGISTER_COUNT 55 +#define WM9081_MAX_REGISTER 0x3D + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +#define WM9081_SW_RST_DEV_ID1_MASK 0xFFFF /* SW_RST_DEV_ID1 - [15:0] */ +#define WM9081_SW_RST_DEV_ID1_SHIFT 0 /* SW_RST_DEV_ID1 - [15:0] */ +#define WM9081_SW_RST_DEV_ID1_WIDTH 16 /* SW_RST_DEV_ID1 - [15:0] */ + +/* + * R2 (0x02) - Analogue Lineout + */ +#define WM9081_LINEOUT_MUTE 0x0080 /* LINEOUT_MUTE */ +#define WM9081_LINEOUT_MUTE_MASK 0x0080 /* LINEOUT_MUTE */ +#define WM9081_LINEOUT_MUTE_SHIFT 7 /* LINEOUT_MUTE */ +#define WM9081_LINEOUT_MUTE_WIDTH 1 /* LINEOUT_MUTE */ +#define WM9081_LINEOUTZC 0x0040 /* LINEOUTZC */ +#define WM9081_LINEOUTZC_MASK 0x0040 /* LINEOUTZC */ +#define WM9081_LINEOUTZC_SHIFT 6 /* LINEOUTZC */ +#define WM9081_LINEOUTZC_WIDTH 1 /* LINEOUTZC */ +#define WM9081_LINEOUT_VOL_MASK 0x003F /* LINEOUT_VOL - [5:0] */ +#define WM9081_LINEOUT_VOL_SHIFT 0 /* LINEOUT_VOL - [5:0] */ +#define WM9081_LINEOUT_VOL_WIDTH 6 /* LINEOUT_VOL - [5:0] */ + +/* + * R3 (0x03) - Analogue Speaker PGA + */ +#define WM9081_SPKPGA_MUTE 0x0080 /* SPKPGA_MUTE */ +#define WM9081_SPKPGA_MUTE_MASK 0x0080 /* SPKPGA_MUTE */ +#define WM9081_SPKPGA_MUTE_SHIFT 7 /* SPKPGA_MUTE */ +#define WM9081_SPKPGA_MUTE_WIDTH 1 /* SPKPGA_MUTE */ +#define WM9081_SPKPGAZC 0x0040 /* SPKPGAZC */ +#define WM9081_SPKPGAZC_MASK 0x0040 /* SPKPGAZC */ +#define WM9081_SPKPGAZC_SHIFT 6 /* SPKPGAZC */ +#define WM9081_SPKPGAZC_WIDTH 1 /* SPKPGAZC */ +#define WM9081_SPKPGA_VOL_MASK 0x003F /* SPKPGA_VOL - [5:0] */ +#define WM9081_SPKPGA_VOL_SHIFT 0 /* SPKPGA_VOL - [5:0] */ +#define WM9081_SPKPGA_VOL_WIDTH 6 /* SPKPGA_VOL - [5:0] */ + +/* + * R4 (0x04) - VMID Control + */ +#define WM9081_VMID_BUF_ENA 0x0020 /* VMID_BUF_ENA */ +#define WM9081_VMID_BUF_ENA_MASK 0x0020 /* VMID_BUF_ENA */ +#define WM9081_VMID_BUF_ENA_SHIFT 5 /* VMID_BUF_ENA */ +#define WM9081_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */ +#define WM9081_VMID_RAMP 0x0008 /* VMID_RAMP */ +#define WM9081_VMID_RAMP_MASK 0x0008 /* VMID_RAMP */ +#define WM9081_VMID_RAMP_SHIFT 3 /* VMID_RAMP */ +#define WM9081_VMID_RAMP_WIDTH 1 /* VMID_RAMP */ +#define WM9081_VMID_SEL_MASK 0x0006 /* VMID_SEL - [2:1] */ +#define WM9081_VMID_SEL_SHIFT 1 /* VMID_SEL - [2:1] */ +#define WM9081_VMID_SEL_WIDTH 2 /* VMID_SEL - [2:1] */ +#define WM9081_VMID_FAST_ST 0x0001 /* VMID_FAST_ST */ +#define WM9081_VMID_FAST_ST_MASK 0x0001 /* VMID_FAST_ST */ +#define WM9081_VMID_FAST_ST_SHIFT 0 /* VMID_FAST_ST */ +#define WM9081_VMID_FAST_ST_WIDTH 1 /* VMID_FAST_ST */ + +/* + * R5 (0x05) - Bias Control 1 + */ +#define WM9081_BIAS_SRC 0x0040 /* BIAS_SRC */ +#define WM9081_BIAS_SRC_MASK 0x0040 /* BIAS_SRC */ +#define WM9081_BIAS_SRC_SHIFT 6 /* BIAS_SRC */ +#define WM9081_BIAS_SRC_WIDTH 1 /* BIAS_SRC */ +#define WM9081_STBY_BIAS_LVL 0x0020 /* STBY_BIAS_LVL */ +#define WM9081_STBY_BIAS_LVL_MASK 0x0020 /* STBY_BIAS_LVL */ +#define WM9081_STBY_BIAS_LVL_SHIFT 5 /* STBY_BIAS_LVL */ +#define WM9081_STBY_BIAS_LVL_WIDTH 1 /* STBY_BIAS_LVL */ +#define WM9081_STBY_BIAS_ENA 0x0010 /* STBY_BIAS_ENA */ +#define WM9081_STBY_BIAS_ENA_MASK 0x0010 /* STBY_BIAS_ENA */ +#define WM9081_STBY_BIAS_ENA_SHIFT 4 /* STBY_BIAS_ENA */ +#define WM9081_STBY_BIAS_ENA_WIDTH 1 /* STBY_BIAS_ENA */ +#define WM9081_BIAS_LVL_MASK 0x000C /* BIAS_LVL - [3:2] */ +#define WM9081_BIAS_LVL_SHIFT 2 /* BIAS_LVL - [3:2] */ +#define WM9081_BIAS_LVL_WIDTH 2 /* BIAS_LVL - [3:2] */ +#define WM9081_BIAS_ENA 0x0002 /* BIAS_ENA */ +#define WM9081_BIAS_ENA_MASK 0x0002 /* BIAS_ENA */ +#define WM9081_BIAS_ENA_SHIFT 1 /* BIAS_ENA */ +#define WM9081_BIAS_ENA_WIDTH 1 /* BIAS_ENA */ +#define WM9081_STARTUP_BIAS_ENA 0x0001 /* STARTUP_BIAS_ENA */ +#define WM9081_STARTUP_BIAS_ENA_MASK 0x0001 /* STARTUP_BIAS_ENA */ +#define WM9081_STARTUP_BIAS_ENA_SHIFT 0 /* STARTUP_BIAS_ENA */ +#define WM9081_STARTUP_BIAS_ENA_WIDTH 1 /* STARTUP_BIAS_ENA */ + +/* + * R7 (0x07) - Analogue Mixer + */ +#define WM9081_DAC_SEL 0x0010 /* DAC_SEL */ +#define WM9081_DAC_SEL_MASK 0x0010 /* DAC_SEL */ +#define WM9081_DAC_SEL_SHIFT 4 /* DAC_SEL */ +#define WM9081_DAC_SEL_WIDTH 1 /* DAC_SEL */ +#define WM9081_IN2_VOL 0x0008 /* IN2_VOL */ +#define WM9081_IN2_VOL_MASK 0x0008 /* IN2_VOL */ +#define WM9081_IN2_VOL_SHIFT 3 /* IN2_VOL */ +#define WM9081_IN2_VOL_WIDTH 1 /* IN2_VOL */ +#define WM9081_IN2_ENA 0x0004 /* IN2_ENA */ +#define WM9081_IN2_ENA_MASK 0x0004 /* IN2_ENA */ +#define WM9081_IN2_ENA_SHIFT 2 /* IN2_ENA */ +#define WM9081_IN2_ENA_WIDTH 1 /* IN2_ENA */ +#define WM9081_IN1_VOL 0x0002 /* IN1_VOL */ +#define WM9081_IN1_VOL_MASK 0x0002 /* IN1_VOL */ +#define WM9081_IN1_VOL_SHIFT 1 /* IN1_VOL */ +#define WM9081_IN1_VOL_WIDTH 1 /* IN1_VOL */ +#define WM9081_IN1_ENA 0x0001 /* IN1_ENA */ +#define WM9081_IN1_ENA_MASK 0x0001 /* IN1_ENA */ +#define WM9081_IN1_ENA_SHIFT 0 /* IN1_ENA */ +#define WM9081_IN1_ENA_WIDTH 1 /* IN1_ENA */ + +/* + * R8 (0x08) - Anti Pop Control + */ +#define WM9081_LINEOUT_DISCH 0x0004 /* LINEOUT_DISCH */ +#define WM9081_LINEOUT_DISCH_MASK 0x0004 /* LINEOUT_DISCH */ +#define WM9081_LINEOUT_DISCH_SHIFT 2 /* LINEOUT_DISCH */ +#define WM9081_LINEOUT_DISCH_WIDTH 1 /* LINEOUT_DISCH */ +#define WM9081_LINEOUT_VROI 0x0002 /* LINEOUT_VROI */ +#define WM9081_LINEOUT_VROI_MASK 0x0002 /* LINEOUT_VROI */ +#define WM9081_LINEOUT_VROI_SHIFT 1 /* LINEOUT_VROI */ +#define WM9081_LINEOUT_VROI_WIDTH 1 /* LINEOUT_VROI */ +#define WM9081_LINEOUT_CLAMP 0x0001 /* LINEOUT_CLAMP */ +#define WM9081_LINEOUT_CLAMP_MASK 0x0001 /* LINEOUT_CLAMP */ +#define WM9081_LINEOUT_CLAMP_SHIFT 0 /* LINEOUT_CLAMP */ +#define WM9081_LINEOUT_CLAMP_WIDTH 1 /* LINEOUT_CLAMP */ + +/* + * R9 (0x09) - Analogue Speaker 1 + */ +#define WM9081_SPK_DCGAIN_MASK 0x0038 /* SPK_DCGAIN - [5:3] */ +#define WM9081_SPK_DCGAIN_SHIFT 3 /* SPK_DCGAIN - [5:3] */ +#define WM9081_SPK_DCGAIN_WIDTH 3 /* SPK_DCGAIN - [5:3] */ +#define WM9081_SPK_ACGAIN_MASK 0x0007 /* SPK_ACGAIN - [2:0] */ +#define WM9081_SPK_ACGAIN_SHIFT 0 /* SPK_ACGAIN - [2:0] */ +#define WM9081_SPK_ACGAIN_WIDTH 3 /* SPK_ACGAIN - [2:0] */ + +/* + * R10 (0x0A) - Analogue Speaker 2 + */ +#define WM9081_SPK_MODE 0x0040 /* SPK_MODE */ +#define WM9081_SPK_MODE_MASK 0x0040 /* SPK_MODE */ +#define WM9081_SPK_MODE_SHIFT 6 /* SPK_MODE */ +#define WM9081_SPK_MODE_WIDTH 1 /* SPK_MODE */ +#define WM9081_SPK_INV_MUTE 0x0010 /* SPK_INV_MUTE */ +#define WM9081_SPK_INV_MUTE_MASK 0x0010 /* SPK_INV_MUTE */ +#define WM9081_SPK_INV_MUTE_SHIFT 4 /* SPK_INV_MUTE */ +#define WM9081_SPK_INV_MUTE_WIDTH 1 /* SPK_INV_MUTE */ +#define WM9081_OUT_SPK_CTRL 0x0008 /* OUT_SPK_CTRL */ +#define WM9081_OUT_SPK_CTRL_MASK 0x0008 /* OUT_SPK_CTRL */ +#define WM9081_OUT_SPK_CTRL_SHIFT 3 /* OUT_SPK_CTRL */ +#define WM9081_OUT_SPK_CTRL_WIDTH 1 /* OUT_SPK_CTRL */ + +/* + * R11 (0x0B) - Power Management + */ +#define WM9081_TSHUT_ENA 0x0100 /* TSHUT_ENA */ +#define WM9081_TSHUT_ENA_MASK 0x0100 /* TSHUT_ENA */ +#define WM9081_TSHUT_ENA_SHIFT 8 /* TSHUT_ENA */ +#define WM9081_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */ +#define WM9081_TSENSE_ENA 0x0080 /* TSENSE_ENA */ +#define WM9081_TSENSE_ENA_MASK 0x0080 /* TSENSE_ENA */ +#define WM9081_TSENSE_ENA_SHIFT 7 /* TSENSE_ENA */ +#define WM9081_TSENSE_ENA_WIDTH 1 /* TSENSE_ENA */ +#define WM9081_TEMP_SHUT 0x0040 /* TEMP_SHUT */ +#define WM9081_TEMP_SHUT_MASK 0x0040 /* TEMP_SHUT */ +#define WM9081_TEMP_SHUT_SHIFT 6 /* TEMP_SHUT */ +#define WM9081_TEMP_SHUT_WIDTH 1 /* TEMP_SHUT */ +#define WM9081_LINEOUT_ENA 0x0010 /* LINEOUT_ENA */ +#define WM9081_LINEOUT_ENA_MASK 0x0010 /* LINEOUT_ENA */ +#define WM9081_LINEOUT_ENA_SHIFT 4 /* LINEOUT_ENA */ +#define WM9081_LINEOUT_ENA_WIDTH 1 /* LINEOUT_ENA */ +#define WM9081_SPKPGA_ENA 0x0004 /* SPKPGA_ENA */ +#define WM9081_SPKPGA_ENA_MASK 0x0004 /* SPKPGA_ENA */ +#define WM9081_SPKPGA_ENA_SHIFT 2 /* SPKPGA_ENA */ +#define WM9081_SPKPGA_ENA_WIDTH 1 /* SPKPGA_ENA */ +#define WM9081_SPK_ENA 0x0002 /* SPK_ENA */ +#define WM9081_SPK_ENA_MASK 0x0002 /* SPK_ENA */ +#define WM9081_SPK_ENA_SHIFT 1 /* SPK_ENA */ +#define WM9081_SPK_ENA_WIDTH 1 /* SPK_ENA */ +#define WM9081_DAC_ENA 0x0001 /* DAC_ENA */ +#define WM9081_DAC_ENA_MASK 0x0001 /* DAC_ENA */ +#define WM9081_DAC_ENA_SHIFT 0 /* DAC_ENA */ +#define WM9081_DAC_ENA_WIDTH 1 /* DAC_ENA */ + +/* + * R12 (0x0C) - Clock Control 1 + */ +#define WM9081_CLK_OP_DIV_MASK 0x1C00 /* CLK_OP_DIV - [12:10] */ +#define WM9081_CLK_OP_DIV_SHIFT 10 /* CLK_OP_DIV - [12:10] */ +#define WM9081_CLK_OP_DIV_WIDTH 3 /* CLK_OP_DIV - [12:10] */ +#define WM9081_CLK_TO_DIV_MASK 0x0300 /* CLK_TO_DIV - [9:8] */ +#define WM9081_CLK_TO_DIV_SHIFT 8 /* CLK_TO_DIV - [9:8] */ +#define WM9081_CLK_TO_DIV_WIDTH 2 /* CLK_TO_DIV - [9:8] */ +#define WM9081_MCLKDIV2 0x0080 /* MCLKDIV2 */ +#define WM9081_MCLKDIV2_MASK 0x0080 /* MCLKDIV2 */ +#define WM9081_MCLKDIV2_SHIFT 7 /* MCLKDIV2 */ +#define WM9081_MCLKDIV2_WIDTH 1 /* MCLKDIV2 */ + +/* + * R13 (0x0D) - Clock Control 2 + */ +#define WM9081_CLK_SYS_RATE_MASK 0x00F0 /* CLK_SYS_RATE - [7:4] */ +#define WM9081_CLK_SYS_RATE_SHIFT 4 /* CLK_SYS_RATE - [7:4] */ +#define WM9081_CLK_SYS_RATE_WIDTH 4 /* CLK_SYS_RATE - [7:4] */ +#define WM9081_SAMPLE_RATE_MASK 0x000F /* SAMPLE_RATE - [3:0] */ +#define WM9081_SAMPLE_RATE_SHIFT 0 /* SAMPLE_RATE - [3:0] */ +#define WM9081_SAMPLE_RATE_WIDTH 4 /* SAMPLE_RATE - [3:0] */ + +/* + * R14 (0x0E) - Clock Control 3 + */ +#define WM9081_CLK_SRC_SEL 0x2000 /* CLK_SRC_SEL */ +#define WM9081_CLK_SRC_SEL_MASK 0x2000 /* CLK_SRC_SEL */ +#define WM9081_CLK_SRC_SEL_SHIFT 13 /* CLK_SRC_SEL */ +#define WM9081_CLK_SRC_SEL_WIDTH 1 /* CLK_SRC_SEL */ +#define WM9081_CLK_OP_ENA 0x0020 /* CLK_OP_ENA */ +#define WM9081_CLK_OP_ENA_MASK 0x0020 /* CLK_OP_ENA */ +#define WM9081_CLK_OP_ENA_SHIFT 5 /* CLK_OP_ENA */ +#define WM9081_CLK_OP_ENA_WIDTH 1 /* CLK_OP_ENA */ +#define WM9081_CLK_TO_ENA 0x0004 /* CLK_TO_ENA */ +#define WM9081_CLK_TO_ENA_MASK 0x0004 /* CLK_TO_ENA */ +#define WM9081_CLK_TO_ENA_SHIFT 2 /* CLK_TO_ENA */ +#define WM9081_CLK_TO_ENA_WIDTH 1 /* CLK_TO_ENA */ +#define WM9081_CLK_DSP_ENA 0x0002 /* CLK_DSP_ENA */ +#define WM9081_CLK_DSP_ENA_MASK 0x0002 /* CLK_DSP_ENA */ +#define WM9081_CLK_DSP_ENA_SHIFT 1 /* CLK_DSP_ENA */ +#define WM9081_CLK_DSP_ENA_WIDTH 1 /* CLK_DSP_ENA */ +#define WM9081_CLK_SYS_ENA 0x0001 /* CLK_SYS_ENA */ +#define WM9081_CLK_SYS_ENA_MASK 0x0001 /* CLK_SYS_ENA */ +#define WM9081_CLK_SYS_ENA_SHIFT 0 /* CLK_SYS_ENA */ +#define WM9081_CLK_SYS_ENA_WIDTH 1 /* CLK_SYS_ENA */ + +/* + * R16 (0x10) - FLL Control 1 + */ +#define WM9081_FLL_HOLD 0x0008 /* FLL_HOLD */ +#define WM9081_FLL_HOLD_MASK 0x0008 /* FLL_HOLD */ +#define WM9081_FLL_HOLD_SHIFT 3 /* FLL_HOLD */ +#define WM9081_FLL_HOLD_WIDTH 1 /* FLL_HOLD */ +#define WM9081_FLL_FRAC 0x0004 /* FLL_FRAC */ +#define WM9081_FLL_FRAC_MASK 0x0004 /* FLL_FRAC */ +#define WM9081_FLL_FRAC_SHIFT 2 /* FLL_FRAC */ +#define WM9081_FLL_FRAC_WIDTH 1 /* FLL_FRAC */ +#define WM9081_FLL_ENA 0x0001 /* FLL_ENA */ +#define WM9081_FLL_ENA_MASK 0x0001 /* FLL_ENA */ +#define WM9081_FLL_ENA_SHIFT 0 /* FLL_ENA */ +#define WM9081_FLL_ENA_WIDTH 1 /* FLL_ENA */ + +/* + * R17 (0x11) - FLL Control 2 + */ +#define WM9081_FLL_OUTDIV_MASK 0x0700 /* FLL_OUTDIV - [10:8] */ +#define WM9081_FLL_OUTDIV_SHIFT 8 /* FLL_OUTDIV - [10:8] */ +#define WM9081_FLL_OUTDIV_WIDTH 3 /* FLL_OUTDIV - [10:8] */ +#define WM9081_FLL_CTRL_RATE_MASK 0x0070 /* FLL_CTRL_RATE - [6:4] */ +#define WM9081_FLL_CTRL_RATE_SHIFT 4 /* FLL_CTRL_RATE - [6:4] */ +#define WM9081_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [6:4] */ +#define WM9081_FLL_FRATIO_MASK 0x0007 /* FLL_FRATIO - [2:0] */ +#define WM9081_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [2:0] */ +#define WM9081_FLL_FRATIO_WIDTH 3 /* FLL_FRATIO - [2:0] */ + +/* + * R18 (0x12) - FLL Control 3 + */ +#define WM9081_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */ +#define WM9081_FLL_K_SHIFT 0 /* FLL_K - [15:0] */ +#define WM9081_FLL_K_WIDTH 16 /* FLL_K - [15:0] */ + +/* + * R19 (0x13) - FLL Control 4 + */ +#define WM9081_FLL_N_MASK 0x7FE0 /* FLL_N - [14:5] */ +#define WM9081_FLL_N_SHIFT 5 /* FLL_N - [14:5] */ +#define WM9081_FLL_N_WIDTH 10 /* FLL_N - [14:5] */ +#define WM9081_FLL_GAIN_MASK 0x000F /* FLL_GAIN - [3:0] */ +#define WM9081_FLL_GAIN_SHIFT 0 /* FLL_GAIN - [3:0] */ +#define WM9081_FLL_GAIN_WIDTH 4 /* FLL_GAIN - [3:0] */ + +/* + * R20 (0x14) - FLL Control 5 + */ +#define WM9081_FLL_CLK_REF_DIV_MASK 0x0018 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM9081_FLL_CLK_REF_DIV_SHIFT 3 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM9081_FLL_CLK_REF_DIV_WIDTH 2 /* FLL_CLK_REF_DIV - [4:3] */ +#define WM9081_FLL_CLK_SRC_MASK 0x0003 /* FLL_CLK_SRC - [1:0] */ +#define WM9081_FLL_CLK_SRC_SHIFT 0 /* FLL_CLK_SRC - [1:0] */ +#define WM9081_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [1:0] */ + +/* + * R22 (0x16) - Audio Interface 1 + */ +#define WM9081_AIFDAC_CHAN 0x0040 /* AIFDAC_CHAN */ +#define WM9081_AIFDAC_CHAN_MASK 0x0040 /* AIFDAC_CHAN */ +#define WM9081_AIFDAC_CHAN_SHIFT 6 /* AIFDAC_CHAN */ +#define WM9081_AIFDAC_CHAN_WIDTH 1 /* AIFDAC_CHAN */ +#define WM9081_AIFDAC_TDM_SLOT_MASK 0x0030 /* AIFDAC_TDM_SLOT - [5:4] */ +#define WM9081_AIFDAC_TDM_SLOT_SHIFT 4 /* AIFDAC_TDM_SLOT - [5:4] */ +#define WM9081_AIFDAC_TDM_SLOT_WIDTH 2 /* AIFDAC_TDM_SLOT - [5:4] */ +#define WM9081_AIFDAC_TDM_MODE_MASK 0x000C /* AIFDAC_TDM_MODE - [3:2] */ +#define WM9081_AIFDAC_TDM_MODE_SHIFT 2 /* AIFDAC_TDM_MODE - [3:2] */ +#define WM9081_AIFDAC_TDM_MODE_WIDTH 2 /* AIFDAC_TDM_MODE - [3:2] */ +#define WM9081_DAC_COMP 0x0002 /* DAC_COMP */ +#define WM9081_DAC_COMP_MASK 0x0002 /* DAC_COMP */ +#define WM9081_DAC_COMP_SHIFT 1 /* DAC_COMP */ +#define WM9081_DAC_COMP_WIDTH 1 /* DAC_COMP */ +#define WM9081_DAC_COMPMODE 0x0001 /* DAC_COMPMODE */ +#define WM9081_DAC_COMPMODE_MASK 0x0001 /* DAC_COMPMODE */ +#define WM9081_DAC_COMPMODE_SHIFT 0 /* DAC_COMPMODE */ +#define WM9081_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */ + +/* + * R23 (0x17) - Audio Interface 2 + */ +#define WM9081_AIF_TRIS 0x0200 /* AIF_TRIS */ +#define WM9081_AIF_TRIS_MASK 0x0200 /* AIF_TRIS */ +#define WM9081_AIF_TRIS_SHIFT 9 /* AIF_TRIS */ +#define WM9081_AIF_TRIS_WIDTH 1 /* AIF_TRIS */ +#define WM9081_DAC_DAT_INV 0x0100 /* DAC_DAT_INV */ +#define WM9081_DAC_DAT_INV_MASK 0x0100 /* DAC_DAT_INV */ +#define WM9081_DAC_DAT_INV_SHIFT 8 /* DAC_DAT_INV */ +#define WM9081_DAC_DAT_INV_WIDTH 1 /* DAC_DAT_INV */ +#define WM9081_AIF_BCLK_INV 0x0080 /* AIF_BCLK_INV */ +#define WM9081_AIF_BCLK_INV_MASK 0x0080 /* AIF_BCLK_INV */ +#define WM9081_AIF_BCLK_INV_SHIFT 7 /* AIF_BCLK_INV */ +#define WM9081_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */ +#define WM9081_BCLK_DIR 0x0040 /* BCLK_DIR */ +#define WM9081_BCLK_DIR_MASK 0x0040 /* BCLK_DIR */ +#define WM9081_BCLK_DIR_SHIFT 6 /* BCLK_DIR */ +#define WM9081_BCLK_DIR_WIDTH 1 /* BCLK_DIR */ +#define WM9081_LRCLK_DIR 0x0020 /* LRCLK_DIR */ +#define WM9081_LRCLK_DIR_MASK 0x0020 /* LRCLK_DIR */ +#define WM9081_LRCLK_DIR_SHIFT 5 /* LRCLK_DIR */ +#define WM9081_LRCLK_DIR_WIDTH 1 /* LRCLK_DIR */ +#define WM9081_AIF_LRCLK_INV 0x0010 /* AIF_LRCLK_INV */ +#define WM9081_AIF_LRCLK_INV_MASK 0x0010 /* AIF_LRCLK_INV */ +#define WM9081_AIF_LRCLK_INV_SHIFT 4 /* AIF_LRCLK_INV */ +#define WM9081_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */ +#define WM9081_AIF_WL_MASK 0x000C /* AIF_WL - [3:2] */ +#define WM9081_AIF_WL_SHIFT 2 /* AIF_WL - [3:2] */ +#define WM9081_AIF_WL_WIDTH 2 /* AIF_WL - [3:2] */ +#define WM9081_AIF_FMT_MASK 0x0003 /* AIF_FMT - [1:0] */ +#define WM9081_AIF_FMT_SHIFT 0 /* AIF_FMT - [1:0] */ +#define WM9081_AIF_FMT_WIDTH 2 /* AIF_FMT - [1:0] */ + +/* + * R24 (0x18) - Audio Interface 3 + */ +#define WM9081_BCLK_DIV_MASK 0x001F /* BCLK_DIV - [4:0] */ +#define WM9081_BCLK_DIV_SHIFT 0 /* BCLK_DIV - [4:0] */ +#define WM9081_BCLK_DIV_WIDTH 5 /* BCLK_DIV - [4:0] */ + +/* + * R25 (0x19) - Audio Interface 4 + */ +#define WM9081_LRCLK_RATE_MASK 0x07FF /* LRCLK_RATE - [10:0] */ +#define WM9081_LRCLK_RATE_SHIFT 0 /* LRCLK_RATE - [10:0] */ +#define WM9081_LRCLK_RATE_WIDTH 11 /* LRCLK_RATE - [10:0] */ + +/* + * R26 (0x1A) - Interrupt Status + */ +#define WM9081_WSEQ_BUSY_EINT 0x0004 /* WSEQ_BUSY_EINT */ +#define WM9081_WSEQ_BUSY_EINT_MASK 0x0004 /* WSEQ_BUSY_EINT */ +#define WM9081_WSEQ_BUSY_EINT_SHIFT 2 /* WSEQ_BUSY_EINT */ +#define WM9081_WSEQ_BUSY_EINT_WIDTH 1 /* WSEQ_BUSY_EINT */ +#define WM9081_TSHUT_EINT 0x0001 /* TSHUT_EINT */ +#define WM9081_TSHUT_EINT_MASK 0x0001 /* TSHUT_EINT */ +#define WM9081_TSHUT_EINT_SHIFT 0 /* TSHUT_EINT */ +#define WM9081_TSHUT_EINT_WIDTH 1 /* TSHUT_EINT */ + +/* + * R27 (0x1B) - Interrupt Status Mask + */ +#define WM9081_IM_WSEQ_BUSY_EINT 0x0004 /* IM_WSEQ_BUSY_EINT */ +#define WM9081_IM_WSEQ_BUSY_EINT_MASK 0x0004 /* IM_WSEQ_BUSY_EINT */ +#define WM9081_IM_WSEQ_BUSY_EINT_SHIFT 2 /* IM_WSEQ_BUSY_EINT */ +#define WM9081_IM_WSEQ_BUSY_EINT_WIDTH 1 /* IM_WSEQ_BUSY_EINT */ +#define WM9081_IM_TSHUT_EINT 0x0001 /* IM_TSHUT_EINT */ +#define WM9081_IM_TSHUT_EINT_MASK 0x0001 /* IM_TSHUT_EINT */ +#define WM9081_IM_TSHUT_EINT_SHIFT 0 /* IM_TSHUT_EINT */ +#define WM9081_IM_TSHUT_EINT_WIDTH 1 /* IM_TSHUT_EINT */ + +/* + * R28 (0x1C) - Interrupt Polarity + */ +#define WM9081_TSHUT_INV 0x0001 /* TSHUT_INV */ +#define WM9081_TSHUT_INV_MASK 0x0001 /* TSHUT_INV */ +#define WM9081_TSHUT_INV_SHIFT 0 /* TSHUT_INV */ +#define WM9081_TSHUT_INV_WIDTH 1 /* TSHUT_INV */ + +/* + * R29 (0x1D) - Interrupt Control + */ +#define WM9081_IRQ_POL 0x8000 /* IRQ_POL */ +#define WM9081_IRQ_POL_MASK 0x8000 /* IRQ_POL */ +#define WM9081_IRQ_POL_SHIFT 15 /* IRQ_POL */ +#define WM9081_IRQ_POL_WIDTH 1 /* IRQ_POL */ +#define WM9081_IRQ_OP_CTRL 0x0001 /* IRQ_OP_CTRL */ +#define WM9081_IRQ_OP_CTRL_MASK 0x0001 /* IRQ_OP_CTRL */ +#define WM9081_IRQ_OP_CTRL_SHIFT 0 /* IRQ_OP_CTRL */ +#define WM9081_IRQ_OP_CTRL_WIDTH 1 /* IRQ_OP_CTRL */ + +/* + * R30 (0x1E) - DAC Digital 1 + */ +#define WM9081_DAC_VOL_MASK 0x00FF /* DAC_VOL - [7:0] */ +#define WM9081_DAC_VOL_SHIFT 0 /* DAC_VOL - [7:0] */ +#define WM9081_DAC_VOL_WIDTH 8 /* DAC_VOL - [7:0] */ + +/* + * R31 (0x1F) - DAC Digital 2 + */ +#define WM9081_DAC_MUTERATE 0x0400 /* DAC_MUTERATE */ +#define WM9081_DAC_MUTERATE_MASK 0x0400 /* DAC_MUTERATE */ +#define WM9081_DAC_MUTERATE_SHIFT 10 /* DAC_MUTERATE */ +#define WM9081_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ +#define WM9081_DAC_MUTEMODE 0x0200 /* DAC_MUTEMODE */ +#define WM9081_DAC_MUTEMODE_MASK 0x0200 /* DAC_MUTEMODE */ +#define WM9081_DAC_MUTEMODE_SHIFT 9 /* DAC_MUTEMODE */ +#define WM9081_DAC_MUTEMODE_WIDTH 1 /* DAC_MUTEMODE */ +#define WM9081_DAC_MUTE 0x0008 /* DAC_MUTE */ +#define WM9081_DAC_MUTE_MASK 0x0008 /* DAC_MUTE */ +#define WM9081_DAC_MUTE_SHIFT 3 /* DAC_MUTE */ +#define WM9081_DAC_MUTE_WIDTH 1 /* DAC_MUTE */ +#define WM9081_DEEMPH_MASK 0x0006 /* DEEMPH - [2:1] */ +#define WM9081_DEEMPH_SHIFT 1 /* DEEMPH - [2:1] */ +#define WM9081_DEEMPH_WIDTH 2 /* DEEMPH - [2:1] */ + +/* + * R32 (0x20) - DRC 1 + */ +#define WM9081_DRC_ENA 0x8000 /* DRC_ENA */ +#define WM9081_DRC_ENA_MASK 0x8000 /* DRC_ENA */ +#define WM9081_DRC_ENA_SHIFT 15 /* DRC_ENA */ +#define WM9081_DRC_ENA_WIDTH 1 /* DRC_ENA */ +#define WM9081_DRC_STARTUP_GAIN_MASK 0x07C0 /* DRC_STARTUP_GAIN - [10:6] */ +#define WM9081_DRC_STARTUP_GAIN_SHIFT 6 /* DRC_STARTUP_GAIN - [10:6] */ +#define WM9081_DRC_STARTUP_GAIN_WIDTH 5 /* DRC_STARTUP_GAIN - [10:6] */ +#define WM9081_DRC_FF_DLY 0x0020 /* DRC_FF_DLY */ +#define WM9081_DRC_FF_DLY_MASK 0x0020 /* DRC_FF_DLY */ +#define WM9081_DRC_FF_DLY_SHIFT 5 /* DRC_FF_DLY */ +#define WM9081_DRC_FF_DLY_WIDTH 1 /* DRC_FF_DLY */ +#define WM9081_DRC_QR 0x0004 /* DRC_QR */ +#define WM9081_DRC_QR_MASK 0x0004 /* DRC_QR */ +#define WM9081_DRC_QR_SHIFT 2 /* DRC_QR */ +#define WM9081_DRC_QR_WIDTH 1 /* DRC_QR */ +#define WM9081_DRC_ANTICLIP 0x0002 /* DRC_ANTICLIP */ +#define WM9081_DRC_ANTICLIP_MASK 0x0002 /* DRC_ANTICLIP */ +#define WM9081_DRC_ANTICLIP_SHIFT 1 /* DRC_ANTICLIP */ +#define WM9081_DRC_ANTICLIP_WIDTH 1 /* DRC_ANTICLIP */ + +/* + * R33 (0x21) - DRC 2 + */ +#define WM9081_DRC_ATK_MASK 0xF000 /* DRC_ATK - [15:12] */ +#define WM9081_DRC_ATK_SHIFT 12 /* DRC_ATK - [15:12] */ +#define WM9081_DRC_ATK_WIDTH 4 /* DRC_ATK - [15:12] */ +#define WM9081_DRC_DCY_MASK 0x0F00 /* DRC_DCY - [11:8] */ +#define WM9081_DRC_DCY_SHIFT 8 /* DRC_DCY - [11:8] */ +#define WM9081_DRC_DCY_WIDTH 4 /* DRC_DCY - [11:8] */ +#define WM9081_DRC_QR_THR_MASK 0x00C0 /* DRC_QR_THR - [7:6] */ +#define WM9081_DRC_QR_THR_SHIFT 6 /* DRC_QR_THR - [7:6] */ +#define WM9081_DRC_QR_THR_WIDTH 2 /* DRC_QR_THR - [7:6] */ +#define WM9081_DRC_QR_DCY_MASK 0x0030 /* DRC_QR_DCY - [5:4] */ +#define WM9081_DRC_QR_DCY_SHIFT 4 /* DRC_QR_DCY - [5:4] */ +#define WM9081_DRC_QR_DCY_WIDTH 2 /* DRC_QR_DCY - [5:4] */ +#define WM9081_DRC_MINGAIN_MASK 0x000C /* DRC_MINGAIN - [3:2] */ +#define WM9081_DRC_MINGAIN_SHIFT 2 /* DRC_MINGAIN - [3:2] */ +#define WM9081_DRC_MINGAIN_WIDTH 2 /* DRC_MINGAIN - [3:2] */ +#define WM9081_DRC_MAXGAIN_MASK 0x0003 /* DRC_MAXGAIN - [1:0] */ +#define WM9081_DRC_MAXGAIN_SHIFT 0 /* DRC_MAXGAIN - [1:0] */ +#define WM9081_DRC_MAXGAIN_WIDTH 2 /* DRC_MAXGAIN - [1:0] */ + +/* + * R34 (0x22) - DRC 3 + */ +#define WM9081_DRC_HI_COMP_MASK 0x0038 /* DRC_HI_COMP - [5:3] */ +#define WM9081_DRC_HI_COMP_SHIFT 3 /* DRC_HI_COMP - [5:3] */ +#define WM9081_DRC_HI_COMP_WIDTH 3 /* DRC_HI_COMP - [5:3] */ +#define WM9081_DRC_LO_COMP_MASK 0x0007 /* DRC_LO_COMP - [2:0] */ +#define WM9081_DRC_LO_COMP_SHIFT 0 /* DRC_LO_COMP - [2:0] */ +#define WM9081_DRC_LO_COMP_WIDTH 3 /* DRC_LO_COMP - [2:0] */ + +/* + * R35 (0x23) - DRC 4 + */ +#define WM9081_DRC_KNEE_IP_MASK 0x07E0 /* DRC_KNEE_IP - [10:5] */ +#define WM9081_DRC_KNEE_IP_SHIFT 5 /* DRC_KNEE_IP - [10:5] */ +#define WM9081_DRC_KNEE_IP_WIDTH 6 /* DRC_KNEE_IP - [10:5] */ +#define WM9081_DRC_KNEE_OP_MASK 0x001F /* DRC_KNEE_OP - [4:0] */ +#define WM9081_DRC_KNEE_OP_SHIFT 0 /* DRC_KNEE_OP - [4:0] */ +#define WM9081_DRC_KNEE_OP_WIDTH 5 /* DRC_KNEE_OP - [4:0] */ + +/* + * R38 (0x26) - Write Sequencer 1 + */ +#define WM9081_WSEQ_ENA 0x8000 /* WSEQ_ENA */ +#define WM9081_WSEQ_ENA_MASK 0x8000 /* WSEQ_ENA */ +#define WM9081_WSEQ_ENA_SHIFT 15 /* WSEQ_ENA */ +#define WM9081_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM9081_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */ +#define WM9081_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */ +#define WM9081_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */ +#define WM9081_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM9081_WSEQ_START 0x0100 /* WSEQ_START */ +#define WM9081_WSEQ_START_MASK 0x0100 /* WSEQ_START */ +#define WM9081_WSEQ_START_SHIFT 8 /* WSEQ_START */ +#define WM9081_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM9081_WSEQ_START_INDEX_MASK 0x007F /* WSEQ_START_INDEX - [6:0] */ +#define WM9081_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [6:0] */ +#define WM9081_WSEQ_START_INDEX_WIDTH 7 /* WSEQ_START_INDEX - [6:0] */ + +/* + * R39 (0x27) - Write Sequencer 2 + */ +#define WM9081_WSEQ_CURRENT_INDEX_MASK 0x07F0 /* WSEQ_CURRENT_INDEX - [10:4] */ +#define WM9081_WSEQ_CURRENT_INDEX_SHIFT 4 /* WSEQ_CURRENT_INDEX - [10:4] */ +#define WM9081_WSEQ_CURRENT_INDEX_WIDTH 7 /* WSEQ_CURRENT_INDEX - [10:4] */ +#define WM9081_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */ +#define WM9081_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */ +#define WM9081_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */ +#define WM9081_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ + +/* + * R40 (0x28) - MW Slave 1 + */ +#define WM9081_SPI_CFG 0x0020 /* SPI_CFG */ +#define WM9081_SPI_CFG_MASK 0x0020 /* SPI_CFG */ +#define WM9081_SPI_CFG_SHIFT 5 /* SPI_CFG */ +#define WM9081_SPI_CFG_WIDTH 1 /* SPI_CFG */ +#define WM9081_SPI_4WIRE 0x0010 /* SPI_4WIRE */ +#define WM9081_SPI_4WIRE_MASK 0x0010 /* SPI_4WIRE */ +#define WM9081_SPI_4WIRE_SHIFT 4 /* SPI_4WIRE */ +#define WM9081_SPI_4WIRE_WIDTH 1 /* SPI_4WIRE */ +#define WM9081_ARA_ENA 0x0008 /* ARA_ENA */ +#define WM9081_ARA_ENA_MASK 0x0008 /* ARA_ENA */ +#define WM9081_ARA_ENA_SHIFT 3 /* ARA_ENA */ +#define WM9081_ARA_ENA_WIDTH 1 /* ARA_ENA */ +#define WM9081_AUTO_INC 0x0002 /* AUTO_INC */ +#define WM9081_AUTO_INC_MASK 0x0002 /* AUTO_INC */ +#define WM9081_AUTO_INC_SHIFT 1 /* AUTO_INC */ +#define WM9081_AUTO_INC_WIDTH 1 /* AUTO_INC */ + +/* + * R42 (0x2A) - EQ 1 + */ +#define WM9081_EQ_B1_GAIN_MASK 0xF800 /* EQ_B1_GAIN - [15:11] */ +#define WM9081_EQ_B1_GAIN_SHIFT 11 /* EQ_B1_GAIN - [15:11] */ +#define WM9081_EQ_B1_GAIN_WIDTH 5 /* EQ_B1_GAIN - [15:11] */ +#define WM9081_EQ_B2_GAIN_MASK 0x07C0 /* EQ_B2_GAIN - [10:6] */ +#define WM9081_EQ_B2_GAIN_SHIFT 6 /* EQ_B2_GAIN - [10:6] */ +#define WM9081_EQ_B2_GAIN_WIDTH 5 /* EQ_B2_GAIN - [10:6] */ +#define WM9081_EQ_B4_GAIN_MASK 0x003E /* EQ_B4_GAIN - [5:1] */ +#define WM9081_EQ_B4_GAIN_SHIFT 1 /* EQ_B4_GAIN - [5:1] */ +#define WM9081_EQ_B4_GAIN_WIDTH 5 /* EQ_B4_GAIN - [5:1] */ +#define WM9081_EQ_ENA 0x0001 /* EQ_ENA */ +#define WM9081_EQ_ENA_MASK 0x0001 /* EQ_ENA */ +#define WM9081_EQ_ENA_SHIFT 0 /* EQ_ENA */ +#define WM9081_EQ_ENA_WIDTH 1 /* EQ_ENA */ + +/* + * R43 (0x2B) - EQ 2 + */ +#define WM9081_EQ_B3_GAIN_MASK 0xF800 /* EQ_B3_GAIN - [15:11] */ +#define WM9081_EQ_B3_GAIN_SHIFT 11 /* EQ_B3_GAIN - [15:11] */ +#define WM9081_EQ_B3_GAIN_WIDTH 5 /* EQ_B3_GAIN - [15:11] */ +#define WM9081_EQ_B5_GAIN_MASK 0x07C0 /* EQ_B5_GAIN - [10:6] */ +#define WM9081_EQ_B5_GAIN_SHIFT 6 /* EQ_B5_GAIN - [10:6] */ +#define WM9081_EQ_B5_GAIN_WIDTH 5 /* EQ_B5_GAIN - [10:6] */ + +/* + * R44 (0x2C) - EQ 3 + */ +#define WM9081_EQ_B1_A_MASK 0xFFFF /* EQ_B1_A - [15:0] */ +#define WM9081_EQ_B1_A_SHIFT 0 /* EQ_B1_A - [15:0] */ +#define WM9081_EQ_B1_A_WIDTH 16 /* EQ_B1_A - [15:0] */ + +/* + * R45 (0x2D) - EQ 4 + */ +#define WM9081_EQ_B1_B_MASK 0xFFFF /* EQ_B1_B - [15:0] */ +#define WM9081_EQ_B1_B_SHIFT 0 /* EQ_B1_B - [15:0] */ +#define WM9081_EQ_B1_B_WIDTH 16 /* EQ_B1_B - [15:0] */ + +/* + * R46 (0x2E) - EQ 5 + */ +#define WM9081_EQ_B1_PG_MASK 0xFFFF /* EQ_B1_PG - [15:0] */ +#define WM9081_EQ_B1_PG_SHIFT 0 /* EQ_B1_PG - [15:0] */ +#define WM9081_EQ_B1_PG_WIDTH 16 /* EQ_B1_PG - [15:0] */ + +/* + * R47 (0x2F) - EQ 6 + */ +#define WM9081_EQ_B2_A_MASK 0xFFFF /* EQ_B2_A - [15:0] */ +#define WM9081_EQ_B2_A_SHIFT 0 /* EQ_B2_A - [15:0] */ +#define WM9081_EQ_B2_A_WIDTH 16 /* EQ_B2_A - [15:0] */ + +/* + * R48 (0x30) - EQ 7 + */ +#define WM9081_EQ_B2_B_MASK 0xFFFF /* EQ_B2_B - [15:0] */ +#define WM9081_EQ_B2_B_SHIFT 0 /* EQ_B2_B - [15:0] */ +#define WM9081_EQ_B2_B_WIDTH 16 /* EQ_B2_B - [15:0] */ + +/* + * R49 (0x31) - EQ 8 + */ +#define WM9081_EQ_B2_C_MASK 0xFFFF /* EQ_B2_C - [15:0] */ +#define WM9081_EQ_B2_C_SHIFT 0 /* EQ_B2_C - [15:0] */ +#define WM9081_EQ_B2_C_WIDTH 16 /* EQ_B2_C - [15:0] */ + +/* + * R50 (0x32) - EQ 9 + */ +#define WM9081_EQ_B2_PG_MASK 0xFFFF /* EQ_B2_PG - [15:0] */ +#define WM9081_EQ_B2_PG_SHIFT 0 /* EQ_B2_PG - [15:0] */ +#define WM9081_EQ_B2_PG_WIDTH 16 /* EQ_B2_PG - [15:0] */ + +/* + * R51 (0x33) - EQ 10 + */ +#define WM9081_EQ_B4_A_MASK 0xFFFF /* EQ_B4_A - [15:0] */ +#define WM9081_EQ_B4_A_SHIFT 0 /* EQ_B4_A - [15:0] */ +#define WM9081_EQ_B4_A_WIDTH 16 /* EQ_B4_A - [15:0] */ + +/* + * R52 (0x34) - EQ 11 + */ +#define WM9081_EQ_B4_B_MASK 0xFFFF /* EQ_B4_B - [15:0] */ +#define WM9081_EQ_B4_B_SHIFT 0 /* EQ_B4_B - [15:0] */ +#define WM9081_EQ_B4_B_WIDTH 16 /* EQ_B4_B - [15:0] */ + +/* + * R53 (0x35) - EQ 12 + */ +#define WM9081_EQ_B4_C_MASK 0xFFFF /* EQ_B4_C - [15:0] */ +#define WM9081_EQ_B4_C_SHIFT 0 /* EQ_B4_C - [15:0] */ +#define WM9081_EQ_B4_C_WIDTH 16 /* EQ_B4_C - [15:0] */ + +/* + * R54 (0x36) - EQ 13 + */ +#define WM9081_EQ_B4_PG_MASK 0xFFFF /* EQ_B4_PG - [15:0] */ +#define WM9081_EQ_B4_PG_SHIFT 0 /* EQ_B4_PG - [15:0] */ +#define WM9081_EQ_B4_PG_WIDTH 16 /* EQ_B4_PG - [15:0] */ + +/* + * R55 (0x37) - EQ 14 + */ +#define WM9081_EQ_B3_A_MASK 0xFFFF /* EQ_B3_A - [15:0] */ +#define WM9081_EQ_B3_A_SHIFT 0 /* EQ_B3_A - [15:0] */ +#define WM9081_EQ_B3_A_WIDTH 16 /* EQ_B3_A - [15:0] */ + +/* + * R56 (0x38) - EQ 15 + */ +#define WM9081_EQ_B3_B_MASK 0xFFFF /* EQ_B3_B - [15:0] */ +#define WM9081_EQ_B3_B_SHIFT 0 /* EQ_B3_B - [15:0] */ +#define WM9081_EQ_B3_B_WIDTH 16 /* EQ_B3_B - [15:0] */ + +/* + * R57 (0x39) - EQ 16 + */ +#define WM9081_EQ_B3_C_MASK 0xFFFF /* EQ_B3_C - [15:0] */ +#define WM9081_EQ_B3_C_SHIFT 0 /* EQ_B3_C - [15:0] */ +#define WM9081_EQ_B3_C_WIDTH 16 /* EQ_B3_C - [15:0] */ + +/* + * R58 (0x3A) - EQ 17 + */ +#define WM9081_EQ_B3_PG_MASK 0xFFFF /* EQ_B3_PG - [15:0] */ +#define WM9081_EQ_B3_PG_SHIFT 0 /* EQ_B3_PG - [15:0] */ +#define WM9081_EQ_B3_PG_WIDTH 16 /* EQ_B3_PG - [15:0] */ + +/* + * R59 (0x3B) - EQ 18 + */ +#define WM9081_EQ_B5_A_MASK 0xFFFF /* EQ_B5_A - [15:0] */ +#define WM9081_EQ_B5_A_SHIFT 0 /* EQ_B5_A - [15:0] */ +#define WM9081_EQ_B5_A_WIDTH 16 /* EQ_B5_A - [15:0] */ + +/* + * R60 (0x3C) - EQ 19 + */ +#define WM9081_EQ_B5_B_MASK 0xFFFF /* EQ_B5_B - [15:0] */ +#define WM9081_EQ_B5_B_SHIFT 0 /* EQ_B5_B - [15:0] */ +#define WM9081_EQ_B5_B_WIDTH 16 /* EQ_B5_B - [15:0] */ + +/* + * R61 (0x3D) - EQ 20 + */ +#define WM9081_EQ_B5_PG_MASK 0xFFFF /* EQ_B5_PG - [15:0] */ +#define WM9081_EQ_B5_PG_SHIFT 0 /* EQ_B5_PG - [15:0] */ +#define WM9081_EQ_B5_PG_WIDTH 16 /* EQ_B5_PG - [15:0] */ + + +#endif diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c new file mode 100644 index 0000000000..50c1cbccfd --- /dev/null +++ b/sound/soc/codecs/wm9090.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ALSA SoC WM9090 driver + * + * Copyright 2009-12 Wolfson Microelectronics + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm9090.h" + +static const struct reg_default wm9090_reg_defaults[] = { + { 1, 0x0006 }, /* R1 - Power Management (1) */ + { 2, 0x6000 }, /* R2 - Power Management (2) */ + { 3, 0x0000 }, /* R3 - Power Management (3) */ + { 6, 0x01C0 }, /* R6 - Clocking 1 */ + { 22, 0x0003 }, /* R22 - IN1 Line Control */ + { 23, 0x0003 }, /* R23 - IN2 Line Control */ + { 24, 0x0083 }, /* R24 - IN1 Line Input A Volume */ + { 25, 0x0083 }, /* R25 - IN1 Line Input B Volume */ + { 26, 0x0083 }, /* R26 - IN2 Line Input A Volume */ + { 27, 0x0083 }, /* R27 - IN2 Line Input B Volume */ + { 28, 0x002D }, /* R28 - Left Output Volume */ + { 29, 0x002D }, /* R29 - Right Output Volume */ + { 34, 0x0100 }, /* R34 - SPKMIXL Attenuation */ + { 35, 0x0010 }, /* R36 - SPKOUT Mixers */ + { 37, 0x0140 }, /* R37 - ClassD3 */ + { 38, 0x0039 }, /* R38 - Speaker Volume Left */ + { 45, 0x0000 }, /* R45 - Output Mixer1 */ + { 46, 0x0000 }, /* R46 - Output Mixer2 */ + { 47, 0x0100 }, /* R47 - Output Mixer3 */ + { 48, 0x0100 }, /* R48 - Output Mixer4 */ + { 54, 0x0000 }, /* R54 - Speaker Mixer */ + { 57, 0x000D }, /* R57 - AntiPOP2 */ + { 70, 0x0000 }, /* R70 - Write Sequencer 0 */ + { 71, 0x0000 }, /* R71 - Write Sequencer 1 */ + { 72, 0x0000 }, /* R72 - Write Sequencer 2 */ + { 73, 0x0000 }, /* R73 - Write Sequencer 3 */ + { 74, 0x0000 }, /* R74 - Write Sequencer 4 */ + { 75, 0x0000 }, /* R75 - Write Sequencer 5 */ + { 76, 0x1F25 }, /* R76 - Charge Pump 1 */ + { 85, 0x054A }, /* R85 - DC Servo 1 */ + { 87, 0x0000 }, /* R87 - DC Servo 3 */ + { 96, 0x0100 }, /* R96 - Analogue HP 0 */ + { 98, 0x8640 }, /* R98 - AGC Control 0 */ + { 99, 0xC000 }, /* R99 - AGC Control 1 */ + { 100, 0x0200 }, /* R100 - AGC Control 2 */ +}; + +/* This struct is used to save the context */ +struct wm9090_priv { + struct wm9090_platform_data pdata; + struct regmap *regmap; +}; + +static bool wm9090_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM9090_SOFTWARE_RESET: + case WM9090_DC_SERVO_0: + case WM9090_DC_SERVO_READBACK_0: + case WM9090_DC_SERVO_READBACK_1: + case WM9090_DC_SERVO_READBACK_2: + return true; + + default: + return false; + } +} + +static bool wm9090_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM9090_SOFTWARE_RESET: + case WM9090_POWER_MANAGEMENT_1: + case WM9090_POWER_MANAGEMENT_2: + case WM9090_POWER_MANAGEMENT_3: + case WM9090_CLOCKING_1: + case WM9090_IN1_LINE_CONTROL: + case WM9090_IN2_LINE_CONTROL: + case WM9090_IN1_LINE_INPUT_A_VOLUME: + case WM9090_IN1_LINE_INPUT_B_VOLUME: + case WM9090_IN2_LINE_INPUT_A_VOLUME: + case WM9090_IN2_LINE_INPUT_B_VOLUME: + case WM9090_LEFT_OUTPUT_VOLUME: + case WM9090_RIGHT_OUTPUT_VOLUME: + case WM9090_SPKMIXL_ATTENUATION: + case WM9090_SPKOUT_MIXERS: + case WM9090_CLASSD3: + case WM9090_SPEAKER_VOLUME_LEFT: + case WM9090_OUTPUT_MIXER1: + case WM9090_OUTPUT_MIXER2: + case WM9090_OUTPUT_MIXER3: + case WM9090_OUTPUT_MIXER4: + case WM9090_SPEAKER_MIXER: + case WM9090_ANTIPOP2: + case WM9090_WRITE_SEQUENCER_0: + case WM9090_WRITE_SEQUENCER_1: + case WM9090_WRITE_SEQUENCER_2: + case WM9090_WRITE_SEQUENCER_3: + case WM9090_WRITE_SEQUENCER_4: + case WM9090_WRITE_SEQUENCER_5: + case WM9090_CHARGE_PUMP_1: + case WM9090_DC_SERVO_0: + case WM9090_DC_SERVO_1: + case WM9090_DC_SERVO_3: + case WM9090_DC_SERVO_READBACK_0: + case WM9090_DC_SERVO_READBACK_1: + case WM9090_DC_SERVO_READBACK_2: + case WM9090_ANALOGUE_HP_0: + case WM9090_AGC_CONTROL_0: + case WM9090_AGC_CONTROL_1: + case WM9090_AGC_CONTROL_2: + return true; + + default: + return false; + } +} + +static void wait_for_dc_servo(struct snd_soc_component *component) +{ + unsigned int reg; + int count = 0; + + dev_dbg(component->dev, "Waiting for DC servo...\n"); + do { + count++; + msleep(1); + reg = snd_soc_component_read(component, WM9090_DC_SERVO_READBACK_0); + dev_dbg(component->dev, "DC servo status: %x\n", reg); + } while ((reg & WM9090_DCS_CAL_COMPLETE_MASK) + != WM9090_DCS_CAL_COMPLETE_MASK && count < 1000); + + if ((reg & WM9090_DCS_CAL_COMPLETE_MASK) + != WM9090_DCS_CAL_COMPLETE_MASK) + dev_err(component->dev, "Timed out waiting for DC Servo\n"); +} + +static const DECLARE_TLV_DB_RANGE(in_tlv, + 0, 0, TLV_DB_SCALE_ITEM(-600, 0, 0), + 1, 3, TLV_DB_SCALE_ITEM(-350, 350, 0), + 4, 6, TLV_DB_SCALE_ITEM(600, 600, 0) +); +static const DECLARE_TLV_DB_RANGE(mix_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-1200, 300, 0), + 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0) +); +static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_RANGE(spkboost_tlv, + 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0), + 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0) +); + +static const struct snd_kcontrol_new wm9090_controls[] = { +SOC_SINGLE_TLV("IN1A Volume", WM9090_IN1_LINE_INPUT_A_VOLUME, 0, 6, 0, + in_tlv), +SOC_SINGLE("IN1A Switch", WM9090_IN1_LINE_INPUT_A_VOLUME, 7, 1, 1), +SOC_SINGLE("IN1A ZC Switch", WM9090_IN1_LINE_INPUT_A_VOLUME, 6, 1, 0), + +SOC_SINGLE_TLV("IN2A Volume", WM9090_IN2_LINE_INPUT_A_VOLUME, 0, 6, 0, + in_tlv), +SOC_SINGLE("IN2A Switch", WM9090_IN2_LINE_INPUT_A_VOLUME, 7, 1, 1), +SOC_SINGLE("IN2A ZC Switch", WM9090_IN2_LINE_INPUT_A_VOLUME, 6, 1, 0), + +SOC_SINGLE("MIXOUTL Switch", WM9090_OUTPUT_MIXER3, 8, 1, 1), +SOC_SINGLE_TLV("MIXOUTL IN1A Volume", WM9090_OUTPUT_MIXER3, 6, 3, 1, + mix_tlv), +SOC_SINGLE_TLV("MIXOUTL IN2A Volume", WM9090_OUTPUT_MIXER3, 2, 3, 1, + mix_tlv), + +SOC_SINGLE("MIXOUTR Switch", WM9090_OUTPUT_MIXER4, 8, 1, 1), +SOC_SINGLE_TLV("MIXOUTR IN1A Volume", WM9090_OUTPUT_MIXER4, 6, 3, 1, + mix_tlv), +SOC_SINGLE_TLV("MIXOUTR IN2A Volume", WM9090_OUTPUT_MIXER4, 2, 3, 1, + mix_tlv), + +SOC_SINGLE("SPKMIX Switch", WM9090_SPKMIXL_ATTENUATION, 8, 1, 1), +SOC_SINGLE_TLV("SPKMIX IN1A Volume", WM9090_SPKMIXL_ATTENUATION, 6, 3, 1, + mix_tlv), +SOC_SINGLE_TLV("SPKMIX IN2A Volume", WM9090_SPKMIXL_ATTENUATION, 2, 3, 1, + mix_tlv), + +SOC_DOUBLE_R_TLV("Headphone Volume", WM9090_LEFT_OUTPUT_VOLUME, + WM9090_RIGHT_OUTPUT_VOLUME, 0, 63, 0, out_tlv), +SOC_DOUBLE_R("Headphone Switch", WM9090_LEFT_OUTPUT_VOLUME, + WM9090_RIGHT_OUTPUT_VOLUME, 6, 1, 1), +SOC_DOUBLE_R("Headphone ZC Switch", WM9090_LEFT_OUTPUT_VOLUME, + WM9090_RIGHT_OUTPUT_VOLUME, 7, 1, 0), + +SOC_SINGLE_TLV("Speaker Volume", WM9090_SPEAKER_VOLUME_LEFT, 0, 63, 0, + out_tlv), +SOC_SINGLE("Speaker Switch", WM9090_SPEAKER_VOLUME_LEFT, 6, 1, 1), +SOC_SINGLE("Speaker ZC Switch", WM9090_SPEAKER_VOLUME_LEFT, 7, 1, 0), +SOC_SINGLE_TLV("Speaker Boost Volume", WM9090_CLASSD3, 3, 7, 0, spkboost_tlv), +}; + +static const struct snd_kcontrol_new wm9090_in1_se_controls[] = { +SOC_SINGLE_TLV("IN1B Volume", WM9090_IN1_LINE_INPUT_B_VOLUME, 0, 6, 0, + in_tlv), +SOC_SINGLE("IN1B Switch", WM9090_IN1_LINE_INPUT_B_VOLUME, 7, 1, 1), +SOC_SINGLE("IN1B ZC Switch", WM9090_IN1_LINE_INPUT_B_VOLUME, 6, 1, 0), + +SOC_SINGLE_TLV("SPKMIX IN1B Volume", WM9090_SPKMIXL_ATTENUATION, 4, 3, 1, + mix_tlv), +SOC_SINGLE_TLV("MIXOUTL IN1B Volume", WM9090_OUTPUT_MIXER3, 4, 3, 1, + mix_tlv), +SOC_SINGLE_TLV("MIXOUTR IN1B Volume", WM9090_OUTPUT_MIXER4, 4, 3, 1, + mix_tlv), +}; + +static const struct snd_kcontrol_new wm9090_in2_se_controls[] = { +SOC_SINGLE_TLV("IN2B Volume", WM9090_IN2_LINE_INPUT_B_VOLUME, 0, 6, 0, + in_tlv), +SOC_SINGLE("IN2B Switch", WM9090_IN2_LINE_INPUT_B_VOLUME, 7, 1, 1), +SOC_SINGLE("IN2B ZC Switch", WM9090_IN2_LINE_INPUT_B_VOLUME, 6, 1, 0), + +SOC_SINGLE_TLV("SPKMIX IN2B Volume", WM9090_SPKMIXL_ATTENUATION, 0, 3, 1, + mix_tlv), +SOC_SINGLE_TLV("MIXOUTL IN2B Volume", WM9090_OUTPUT_MIXER3, 0, 3, 1, + mix_tlv), +SOC_SINGLE_TLV("MIXOUTR IN2B Volume", WM9090_OUTPUT_MIXER4, 0, 3, 1, + mix_tlv), +}; + +static int hp_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int reg = snd_soc_component_read(component, WM9090_ANALOGUE_HP_0); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, WM9090_CHARGE_PUMP_1, + WM9090_CP_ENA, WM9090_CP_ENA); + + msleep(5); + + snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1, + WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA, + WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA); + + reg |= WM9090_HPOUT1L_DLY | WM9090_HPOUT1R_DLY; + snd_soc_component_write(component, WM9090_ANALOGUE_HP_0, reg); + + /* Start the DC servo. We don't currently use the + * ability to save the state since we don't have full + * control of the analogue paths and they can change + * DC offsets; see the WM8904 driver for an example of + * doing so. + */ + snd_soc_component_write(component, WM9090_DC_SERVO_0, + WM9090_DCS_ENA_CHAN_0 | + WM9090_DCS_ENA_CHAN_1 | + WM9090_DCS_TRIG_STARTUP_1 | + WM9090_DCS_TRIG_STARTUP_0); + wait_for_dc_servo(component); + + reg |= WM9090_HPOUT1R_OUTP | WM9090_HPOUT1R_RMV_SHORT | + WM9090_HPOUT1L_OUTP | WM9090_HPOUT1L_RMV_SHORT; + snd_soc_component_write(component, WM9090_ANALOGUE_HP_0, reg); + break; + + case SND_SOC_DAPM_PRE_PMD: + reg &= ~(WM9090_HPOUT1L_RMV_SHORT | + WM9090_HPOUT1L_DLY | + WM9090_HPOUT1L_OUTP | + WM9090_HPOUT1R_RMV_SHORT | + WM9090_HPOUT1R_DLY | + WM9090_HPOUT1R_OUTP); + + snd_soc_component_write(component, WM9090_ANALOGUE_HP_0, reg); + + snd_soc_component_write(component, WM9090_DC_SERVO_0, 0); + + snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1, + WM9090_HPOUT1L_ENA | WM9090_HPOUT1R_ENA, + 0); + + snd_soc_component_update_bits(component, WM9090_CHARGE_PUMP_1, + WM9090_CP_ENA, 0); + break; + } + + return 0; +} + +static const struct snd_kcontrol_new spkmix[] = { +SOC_DAPM_SINGLE("IN1A Switch", WM9090_SPEAKER_MIXER, 6, 1, 0), +SOC_DAPM_SINGLE("IN1B Switch", WM9090_SPEAKER_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("IN2A Switch", WM9090_SPEAKER_MIXER, 2, 1, 0), +SOC_DAPM_SINGLE("IN2B Switch", WM9090_SPEAKER_MIXER, 0, 1, 0), +}; + +static const struct snd_kcontrol_new spkout[] = { +SOC_DAPM_SINGLE("Mixer Switch", WM9090_SPKOUT_MIXERS, 4, 1, 0), +}; + +static const struct snd_kcontrol_new mixoutl[] = { +SOC_DAPM_SINGLE("IN1A Switch", WM9090_OUTPUT_MIXER1, 6, 1, 0), +SOC_DAPM_SINGLE("IN1B Switch", WM9090_OUTPUT_MIXER1, 4, 1, 0), +SOC_DAPM_SINGLE("IN2A Switch", WM9090_OUTPUT_MIXER1, 2, 1, 0), +SOC_DAPM_SINGLE("IN2B Switch", WM9090_OUTPUT_MIXER1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new mixoutr[] = { +SOC_DAPM_SINGLE("IN1A Switch", WM9090_OUTPUT_MIXER2, 6, 1, 0), +SOC_DAPM_SINGLE("IN1B Switch", WM9090_OUTPUT_MIXER2, 4, 1, 0), +SOC_DAPM_SINGLE("IN2A Switch", WM9090_OUTPUT_MIXER2, 2, 1, 0), +SOC_DAPM_SINGLE("IN2B Switch", WM9090_OUTPUT_MIXER2, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm9090_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1+"), +SND_SOC_DAPM_INPUT("IN1-"), +SND_SOC_DAPM_INPUT("IN2+"), +SND_SOC_DAPM_INPUT("IN2-"), + +SND_SOC_DAPM_SUPPLY("OSC", WM9090_POWER_MANAGEMENT_1, 3, 0, NULL, 0), + +SND_SOC_DAPM_PGA("IN1A PGA", WM9090_POWER_MANAGEMENT_2, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("IN1B PGA", WM9090_POWER_MANAGEMENT_2, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("IN2A PGA", WM9090_POWER_MANAGEMENT_2, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA("IN2B PGA", WM9090_POWER_MANAGEMENT_2, 4, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("SPKMIX", WM9090_POWER_MANAGEMENT_3, 3, 0, + spkmix, ARRAY_SIZE(spkmix)), +SND_SOC_DAPM_MIXER("MIXOUTL", WM9090_POWER_MANAGEMENT_3, 5, 0, + mixoutl, ARRAY_SIZE(mixoutl)), +SND_SOC_DAPM_MIXER("MIXOUTR", WM9090_POWER_MANAGEMENT_3, 4, 0, + mixoutr, ARRAY_SIZE(mixoutr)), + +SND_SOC_DAPM_PGA_E("HP PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + hp_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_PGA("SPKPGA", WM9090_POWER_MANAGEMENT_3, 8, 0, NULL, 0), +SND_SOC_DAPM_MIXER("SPKOUT", WM9090_POWER_MANAGEMENT_1, 12, 0, + spkout, ARRAY_SIZE(spkout)), + +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("Speaker"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + { "IN1A PGA", NULL, "IN1+" }, + { "IN2A PGA", NULL, "IN2+" }, + + { "SPKMIX", "IN1A Switch", "IN1A PGA" }, + { "SPKMIX", "IN2A Switch", "IN2A PGA" }, + + { "MIXOUTL", "IN1A Switch", "IN1A PGA" }, + { "MIXOUTL", "IN2A Switch", "IN2A PGA" }, + + { "MIXOUTR", "IN1A Switch", "IN1A PGA" }, + { "MIXOUTR", "IN2A Switch", "IN2A PGA" }, + + { "HP PGA", NULL, "OSC" }, + { "HP PGA", NULL, "MIXOUTL" }, + { "HP PGA", NULL, "MIXOUTR" }, + + { "HPL", NULL, "HP PGA" }, + { "HPR", NULL, "HP PGA" }, + + { "SPKPGA", NULL, "OSC" }, + { "SPKPGA", NULL, "SPKMIX" }, + + { "SPKOUT", "Mixer Switch", "SPKPGA" }, + + { "Speaker", NULL, "SPKOUT" }, +}; + +static const struct snd_soc_dapm_route audio_map_in1_se[] = { + { "IN1B PGA", NULL, "IN1-" }, + + { "SPKMIX", "IN1B Switch", "IN1B PGA" }, + { "MIXOUTL", "IN1B Switch", "IN1B PGA" }, + { "MIXOUTR", "IN1B Switch", "IN1B PGA" }, +}; + +static const struct snd_soc_dapm_route audio_map_in1_diff[] = { + { "IN1A PGA", NULL, "IN1-" }, +}; + +static const struct snd_soc_dapm_route audio_map_in2_se[] = { + { "IN2B PGA", NULL, "IN2-" }, + + { "SPKMIX", "IN2B Switch", "IN2B PGA" }, + { "MIXOUTL", "IN2B Switch", "IN2B PGA" }, + { "MIXOUTR", "IN2B Switch", "IN2B PGA" }, +}; + +static const struct snd_soc_dapm_route audio_map_in2_diff[] = { + { "IN2A PGA", NULL, "IN2-" }, +}; + +static int wm9090_add_controls(struct snd_soc_component *component) +{ + struct wm9090_priv *wm9090 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int i; + + snd_soc_dapm_new_controls(dapm, wm9090_dapm_widgets, + ARRAY_SIZE(wm9090_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + + snd_soc_add_component_controls(component, wm9090_controls, + ARRAY_SIZE(wm9090_controls)); + + if (wm9090->pdata.lin1_diff) { + snd_soc_dapm_add_routes(dapm, audio_map_in1_diff, + ARRAY_SIZE(audio_map_in1_diff)); + } else { + snd_soc_dapm_add_routes(dapm, audio_map_in1_se, + ARRAY_SIZE(audio_map_in1_se)); + snd_soc_add_component_controls(component, wm9090_in1_se_controls, + ARRAY_SIZE(wm9090_in1_se_controls)); + } + + if (wm9090->pdata.lin2_diff) { + snd_soc_dapm_add_routes(dapm, audio_map_in2_diff, + ARRAY_SIZE(audio_map_in2_diff)); + } else { + snd_soc_dapm_add_routes(dapm, audio_map_in2_se, + ARRAY_SIZE(audio_map_in2_se)); + snd_soc_add_component_controls(component, wm9090_in2_se_controls, + ARRAY_SIZE(wm9090_in2_se_controls)); + } + + if (wm9090->pdata.agc_ena) { + for (i = 0; i < ARRAY_SIZE(wm9090->pdata.agc); i++) + snd_soc_component_write(component, WM9090_AGC_CONTROL_0 + i, + wm9090->pdata.agc[i]); + snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_3, + WM9090_AGC_ENA, WM9090_AGC_ENA); + } else { + snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_3, + WM9090_AGC_ENA, 0); + } + + return 0; + +} + +/* + * The machine driver should call this from their set_bias_level; if there + * isn't one then this can just be set as the set_bias_level function. + */ +static int wm9090_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm9090_priv *wm9090 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, WM9090_ANTIPOP2, WM9090_VMID_ENA, + WM9090_VMID_ENA); + snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1, + WM9090_BIAS_ENA | + WM9090_VMID_RES_MASK, + WM9090_BIAS_ENA | + 1 << WM9090_VMID_RES_SHIFT); + msleep(1); /* Probably an overestimate */ + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + /* Restore the register cache */ + regcache_sync(wm9090->regmap); + } + + /* We keep VMID off during standby since the combination of + * ground referenced outputs and class D speaker mean that + * latency is not an issue. + */ + snd_soc_component_update_bits(component, WM9090_POWER_MANAGEMENT_1, + WM9090_BIAS_ENA | WM9090_VMID_RES_MASK, 0); + snd_soc_component_update_bits(component, WM9090_ANTIPOP2, + WM9090_VMID_ENA, 0); + break; + + case SND_SOC_BIAS_OFF: + break; + } + + return 0; +} + +static int wm9090_probe(struct snd_soc_component *component) +{ + /* Configure some defaults; they will be written out when we + * bring the bias up. + */ + snd_soc_component_update_bits(component, WM9090_IN1_LINE_INPUT_A_VOLUME, + WM9090_IN1_VU | WM9090_IN1A_ZC, + WM9090_IN1_VU | WM9090_IN1A_ZC); + snd_soc_component_update_bits(component, WM9090_IN1_LINE_INPUT_B_VOLUME, + WM9090_IN1_VU | WM9090_IN1B_ZC, + WM9090_IN1_VU | WM9090_IN1B_ZC); + snd_soc_component_update_bits(component, WM9090_IN2_LINE_INPUT_A_VOLUME, + WM9090_IN2_VU | WM9090_IN2A_ZC, + WM9090_IN2_VU | WM9090_IN2A_ZC); + snd_soc_component_update_bits(component, WM9090_IN2_LINE_INPUT_B_VOLUME, + WM9090_IN2_VU | WM9090_IN2B_ZC, + WM9090_IN2_VU | WM9090_IN2B_ZC); + snd_soc_component_update_bits(component, WM9090_SPEAKER_VOLUME_LEFT, + WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC, + WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC); + snd_soc_component_update_bits(component, WM9090_LEFT_OUTPUT_VOLUME, + WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC, + WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC); + snd_soc_component_update_bits(component, WM9090_RIGHT_OUTPUT_VOLUME, + WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC, + WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC); + + snd_soc_component_update_bits(component, WM9090_CLOCKING_1, + WM9090_TOCLK_ENA, WM9090_TOCLK_ENA); + + wm9090_add_controls(component); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_wm9090 = { + .probe = wm9090_probe, + .set_bias_level = wm9090_set_bias_level, + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, +}; + +static const struct regmap_config wm9090_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = WM9090_MAX_REGISTER, + .volatile_reg = wm9090_volatile, + .readable_reg = wm9090_readable, + + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wm9090_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9090_reg_defaults), +}; + + +static int wm9090_i2c_probe(struct i2c_client *i2c) +{ + struct wm9090_priv *wm9090; + unsigned int reg; + int ret; + + wm9090 = devm_kzalloc(&i2c->dev, sizeof(*wm9090), GFP_KERNEL); + if (!wm9090) + return -ENOMEM; + + wm9090->regmap = devm_regmap_init_i2c(i2c, &wm9090_regmap); + if (IS_ERR(wm9090->regmap)) { + ret = PTR_ERR(wm9090->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + ret = regmap_read(wm9090->regmap, WM9090_SOFTWARE_RESET, ®); + if (ret < 0) + return ret; + + if (reg != 0x9093) { + dev_err(&i2c->dev, "Device is not a WM9090, ID=%x\n", reg); + return -ENODEV; + } + + ret = regmap_write(wm9090->regmap, WM9090_SOFTWARE_RESET, 0); + if (ret < 0) + return ret; + + if (i2c->dev.platform_data) + memcpy(&wm9090->pdata, i2c->dev.platform_data, + sizeof(wm9090->pdata)); + + i2c_set_clientdata(i2c, wm9090); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm9090, NULL, 0); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id wm9090_id[] = { + { "wm9090", 0 }, + { "wm9093", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm9090_id); + +static struct i2c_driver wm9090_i2c_driver = { + .driver = { + .name = "wm9090", + }, + .probe = wm9090_i2c_probe, + .id_table = wm9090_id, +}; + +module_i2c_driver(wm9090_i2c_driver); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("WM9090 ASoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm9090.h b/sound/soc/codecs/wm9090.h new file mode 100644 index 0000000000..342068e93c --- /dev/null +++ b/sound/soc/codecs/wm9090.h @@ -0,0 +1,700 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC WM9090 driver + * + * Copyright 2009 Wolfson Microelectronics + * + * Author: Mark Brown + */ + +#ifndef __WM9090_H +#define __WM9090_H + +/* + * Register values. + */ +#define WM9090_SOFTWARE_RESET 0x00 +#define WM9090_POWER_MANAGEMENT_1 0x01 +#define WM9090_POWER_MANAGEMENT_2 0x02 +#define WM9090_POWER_MANAGEMENT_3 0x03 +#define WM9090_CLOCKING_1 0x06 +#define WM9090_IN1_LINE_CONTROL 0x16 +#define WM9090_IN2_LINE_CONTROL 0x17 +#define WM9090_IN1_LINE_INPUT_A_VOLUME 0x18 +#define WM9090_IN1_LINE_INPUT_B_VOLUME 0x19 +#define WM9090_IN2_LINE_INPUT_A_VOLUME 0x1A +#define WM9090_IN2_LINE_INPUT_B_VOLUME 0x1B +#define WM9090_LEFT_OUTPUT_VOLUME 0x1C +#define WM9090_RIGHT_OUTPUT_VOLUME 0x1D +#define WM9090_SPKMIXL_ATTENUATION 0x22 +#define WM9090_SPKOUT_MIXERS 0x24 +#define WM9090_CLASSD3 0x25 +#define WM9090_SPEAKER_VOLUME_LEFT 0x26 +#define WM9090_OUTPUT_MIXER1 0x2D +#define WM9090_OUTPUT_MIXER2 0x2E +#define WM9090_OUTPUT_MIXER3 0x2F +#define WM9090_OUTPUT_MIXER4 0x30 +#define WM9090_SPEAKER_MIXER 0x36 +#define WM9090_ANTIPOP2 0x39 +#define WM9090_WRITE_SEQUENCER_0 0x46 +#define WM9090_WRITE_SEQUENCER_1 0x47 +#define WM9090_WRITE_SEQUENCER_2 0x48 +#define WM9090_WRITE_SEQUENCER_3 0x49 +#define WM9090_WRITE_SEQUENCER_4 0x4A +#define WM9090_WRITE_SEQUENCER_5 0x4B +#define WM9090_CHARGE_PUMP_1 0x4C +#define WM9090_DC_SERVO_0 0x54 +#define WM9090_DC_SERVO_1 0x55 +#define WM9090_DC_SERVO_3 0x57 +#define WM9090_DC_SERVO_READBACK_0 0x58 +#define WM9090_DC_SERVO_READBACK_1 0x59 +#define WM9090_DC_SERVO_READBACK_2 0x5A +#define WM9090_ANALOGUE_HP_0 0x60 +#define WM9090_AGC_CONTROL_0 0x62 +#define WM9090_AGC_CONTROL_1 0x63 +#define WM9090_AGC_CONTROL_2 0x64 + +#define WM9090_REGISTER_COUNT 40 +#define WM9090_MAX_REGISTER 0x64 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +#define WM9090_SW_RESET_MASK 0xFFFF /* SW_RESET - [15:0] */ +#define WM9090_SW_RESET_SHIFT 0 /* SW_RESET - [15:0] */ +#define WM9090_SW_RESET_WIDTH 16 /* SW_RESET - [15:0] */ + +/* + * R1 (0x01) - Power Management (1) + */ +#define WM9090_SPKOUTL_ENA 0x1000 /* SPKOUTL_ENA */ +#define WM9090_SPKOUTL_ENA_MASK 0x1000 /* SPKOUTL_ENA */ +#define WM9090_SPKOUTL_ENA_SHIFT 12 /* SPKOUTL_ENA */ +#define WM9090_SPKOUTL_ENA_WIDTH 1 /* SPKOUTL_ENA */ +#define WM9090_HPOUT1L_ENA 0x0200 /* HPOUT1L_ENA */ +#define WM9090_HPOUT1L_ENA_MASK 0x0200 /* HPOUT1L_ENA */ +#define WM9090_HPOUT1L_ENA_SHIFT 9 /* HPOUT1L_ENA */ +#define WM9090_HPOUT1L_ENA_WIDTH 1 /* HPOUT1L_ENA */ +#define WM9090_HPOUT1R_ENA 0x0100 /* HPOUT1R_ENA */ +#define WM9090_HPOUT1R_ENA_MASK 0x0100 /* HPOUT1R_ENA */ +#define WM9090_HPOUT1R_ENA_SHIFT 8 /* HPOUT1R_ENA */ +#define WM9090_HPOUT1R_ENA_WIDTH 1 /* HPOUT1R_ENA */ +#define WM9090_OSC_ENA 0x0008 /* OSC_ENA */ +#define WM9090_OSC_ENA_MASK 0x0008 /* OSC_ENA */ +#define WM9090_OSC_ENA_SHIFT 3 /* OSC_ENA */ +#define WM9090_OSC_ENA_WIDTH 1 /* OSC_ENA */ +#define WM9090_VMID_RES_MASK 0x0006 /* VMID_RES - [2:1] */ +#define WM9090_VMID_RES_SHIFT 1 /* VMID_RES - [2:1] */ +#define WM9090_VMID_RES_WIDTH 2 /* VMID_RES - [2:1] */ +#define WM9090_BIAS_ENA 0x0001 /* BIAS_ENA */ +#define WM9090_BIAS_ENA_MASK 0x0001 /* BIAS_ENA */ +#define WM9090_BIAS_ENA_SHIFT 0 /* BIAS_ENA */ +#define WM9090_BIAS_ENA_WIDTH 1 /* BIAS_ENA */ + +/* + * R2 (0x02) - Power Management (2) + */ +#define WM9090_TSHUT 0x8000 /* TSHUT */ +#define WM9090_TSHUT_MASK 0x8000 /* TSHUT */ +#define WM9090_TSHUT_SHIFT 15 /* TSHUT */ +#define WM9090_TSHUT_WIDTH 1 /* TSHUT */ +#define WM9090_TSHUT_ENA 0x4000 /* TSHUT_ENA */ +#define WM9090_TSHUT_ENA_MASK 0x4000 /* TSHUT_ENA */ +#define WM9090_TSHUT_ENA_SHIFT 14 /* TSHUT_ENA */ +#define WM9090_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */ +#define WM9090_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */ +#define WM9090_TSHUT_OPDIS_MASK 0x2000 /* TSHUT_OPDIS */ +#define WM9090_TSHUT_OPDIS_SHIFT 13 /* TSHUT_OPDIS */ +#define WM9090_TSHUT_OPDIS_WIDTH 1 /* TSHUT_OPDIS */ +#define WM9090_IN1A_ENA 0x0080 /* IN1A_ENA */ +#define WM9090_IN1A_ENA_MASK 0x0080 /* IN1A_ENA */ +#define WM9090_IN1A_ENA_SHIFT 7 /* IN1A_ENA */ +#define WM9090_IN1A_ENA_WIDTH 1 /* IN1A_ENA */ +#define WM9090_IN1B_ENA 0x0040 /* IN1B_ENA */ +#define WM9090_IN1B_ENA_MASK 0x0040 /* IN1B_ENA */ +#define WM9090_IN1B_ENA_SHIFT 6 /* IN1B_ENA */ +#define WM9090_IN1B_ENA_WIDTH 1 /* IN1B_ENA */ +#define WM9090_IN2A_ENA 0x0020 /* IN2A_ENA */ +#define WM9090_IN2A_ENA_MASK 0x0020 /* IN2A_ENA */ +#define WM9090_IN2A_ENA_SHIFT 5 /* IN2A_ENA */ +#define WM9090_IN2A_ENA_WIDTH 1 /* IN2A_ENA */ +#define WM9090_IN2B_ENA 0x0010 /* IN2B_ENA */ +#define WM9090_IN2B_ENA_MASK 0x0010 /* IN2B_ENA */ +#define WM9090_IN2B_ENA_SHIFT 4 /* IN2B_ENA */ +#define WM9090_IN2B_ENA_WIDTH 1 /* IN2B_ENA */ + +/* + * R3 (0x03) - Power Management (3) + */ +#define WM9090_AGC_ENA 0x4000 /* AGC_ENA */ +#define WM9090_AGC_ENA_MASK 0x4000 /* AGC_ENA */ +#define WM9090_AGC_ENA_SHIFT 14 /* AGC_ENA */ +#define WM9090_AGC_ENA_WIDTH 1 /* AGC_ENA */ +#define WM9090_SPKLVOL_ENA 0x0100 /* SPKLVOL_ENA */ +#define WM9090_SPKLVOL_ENA_MASK 0x0100 /* SPKLVOL_ENA */ +#define WM9090_SPKLVOL_ENA_SHIFT 8 /* SPKLVOL_ENA */ +#define WM9090_SPKLVOL_ENA_WIDTH 1 /* SPKLVOL_ENA */ +#define WM9090_MIXOUTL_ENA 0x0020 /* MIXOUTL_ENA */ +#define WM9090_MIXOUTL_ENA_MASK 0x0020 /* MIXOUTL_ENA */ +#define WM9090_MIXOUTL_ENA_SHIFT 5 /* MIXOUTL_ENA */ +#define WM9090_MIXOUTL_ENA_WIDTH 1 /* MIXOUTL_ENA */ +#define WM9090_MIXOUTR_ENA 0x0010 /* MIXOUTR_ENA */ +#define WM9090_MIXOUTR_ENA_MASK 0x0010 /* MIXOUTR_ENA */ +#define WM9090_MIXOUTR_ENA_SHIFT 4 /* MIXOUTR_ENA */ +#define WM9090_MIXOUTR_ENA_WIDTH 1 /* MIXOUTR_ENA */ +#define WM9090_SPKMIX_ENA 0x0008 /* SPKMIX_ENA */ +#define WM9090_SPKMIX_ENA_MASK 0x0008 /* SPKMIX_ENA */ +#define WM9090_SPKMIX_ENA_SHIFT 3 /* SPKMIX_ENA */ +#define WM9090_SPKMIX_ENA_WIDTH 1 /* SPKMIX_ENA */ + +/* + * R6 (0x06) - Clocking 1 + */ +#define WM9090_TOCLK_RATE 0x8000 /* TOCLK_RATE */ +#define WM9090_TOCLK_RATE_MASK 0x8000 /* TOCLK_RATE */ +#define WM9090_TOCLK_RATE_SHIFT 15 /* TOCLK_RATE */ +#define WM9090_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */ +#define WM9090_TOCLK_ENA 0x4000 /* TOCLK_ENA */ +#define WM9090_TOCLK_ENA_MASK 0x4000 /* TOCLK_ENA */ +#define WM9090_TOCLK_ENA_SHIFT 14 /* TOCLK_ENA */ +#define WM9090_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */ + +/* + * R22 (0x16) - IN1 Line Control + */ +#define WM9090_IN1_DIFF 0x0002 /* IN1_DIFF */ +#define WM9090_IN1_DIFF_MASK 0x0002 /* IN1_DIFF */ +#define WM9090_IN1_DIFF_SHIFT 1 /* IN1_DIFF */ +#define WM9090_IN1_DIFF_WIDTH 1 /* IN1_DIFF */ +#define WM9090_IN1_CLAMP 0x0001 /* IN1_CLAMP */ +#define WM9090_IN1_CLAMP_MASK 0x0001 /* IN1_CLAMP */ +#define WM9090_IN1_CLAMP_SHIFT 0 /* IN1_CLAMP */ +#define WM9090_IN1_CLAMP_WIDTH 1 /* IN1_CLAMP */ + +/* + * R23 (0x17) - IN2 Line Control + */ +#define WM9090_IN2_DIFF 0x0002 /* IN2_DIFF */ +#define WM9090_IN2_DIFF_MASK 0x0002 /* IN2_DIFF */ +#define WM9090_IN2_DIFF_SHIFT 1 /* IN2_DIFF */ +#define WM9090_IN2_DIFF_WIDTH 1 /* IN2_DIFF */ +#define WM9090_IN2_CLAMP 0x0001 /* IN2_CLAMP */ +#define WM9090_IN2_CLAMP_MASK 0x0001 /* IN2_CLAMP */ +#define WM9090_IN2_CLAMP_SHIFT 0 /* IN2_CLAMP */ +#define WM9090_IN2_CLAMP_WIDTH 1 /* IN2_CLAMP */ + +/* + * R24 (0x18) - IN1 Line Input A Volume + */ +#define WM9090_IN1_VU 0x0100 /* IN1_VU */ +#define WM9090_IN1_VU_MASK 0x0100 /* IN1_VU */ +#define WM9090_IN1_VU_SHIFT 8 /* IN1_VU */ +#define WM9090_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM9090_IN1A_MUTE 0x0080 /* IN1A_MUTE */ +#define WM9090_IN1A_MUTE_MASK 0x0080 /* IN1A_MUTE */ +#define WM9090_IN1A_MUTE_SHIFT 7 /* IN1A_MUTE */ +#define WM9090_IN1A_MUTE_WIDTH 1 /* IN1A_MUTE */ +#define WM9090_IN1A_ZC 0x0040 /* IN1A_ZC */ +#define WM9090_IN1A_ZC_MASK 0x0040 /* IN1A_ZC */ +#define WM9090_IN1A_ZC_SHIFT 6 /* IN1A_ZC */ +#define WM9090_IN1A_ZC_WIDTH 1 /* IN1A_ZC */ +#define WM9090_IN1A_VOL_MASK 0x0007 /* IN1A_VOL - [2:0] */ +#define WM9090_IN1A_VOL_SHIFT 0 /* IN1A_VOL - [2:0] */ +#define WM9090_IN1A_VOL_WIDTH 3 /* IN1A_VOL - [2:0] */ + +/* + * R25 (0x19) - IN1 Line Input B Volume + */ +#define WM9090_IN1_VU 0x0100 /* IN1_VU */ +#define WM9090_IN1_VU_MASK 0x0100 /* IN1_VU */ +#define WM9090_IN1_VU_SHIFT 8 /* IN1_VU */ +#define WM9090_IN1_VU_WIDTH 1 /* IN1_VU */ +#define WM9090_IN1B_MUTE 0x0080 /* IN1B_MUTE */ +#define WM9090_IN1B_MUTE_MASK 0x0080 /* IN1B_MUTE */ +#define WM9090_IN1B_MUTE_SHIFT 7 /* IN1B_MUTE */ +#define WM9090_IN1B_MUTE_WIDTH 1 /* IN1B_MUTE */ +#define WM9090_IN1B_ZC 0x0040 /* IN1B_ZC */ +#define WM9090_IN1B_ZC_MASK 0x0040 /* IN1B_ZC */ +#define WM9090_IN1B_ZC_SHIFT 6 /* IN1B_ZC */ +#define WM9090_IN1B_ZC_WIDTH 1 /* IN1B_ZC */ +#define WM9090_IN1B_VOL_MASK 0x0007 /* IN1B_VOL - [2:0] */ +#define WM9090_IN1B_VOL_SHIFT 0 /* IN1B_VOL - [2:0] */ +#define WM9090_IN1B_VOL_WIDTH 3 /* IN1B_VOL - [2:0] */ + +/* + * R26 (0x1A) - IN2 Line Input A Volume + */ +#define WM9090_IN2_VU 0x0100 /* IN2_VU */ +#define WM9090_IN2_VU_MASK 0x0100 /* IN2_VU */ +#define WM9090_IN2_VU_SHIFT 8 /* IN2_VU */ +#define WM9090_IN2_VU_WIDTH 1 /* IN2_VU */ +#define WM9090_IN2A_MUTE 0x0080 /* IN2A_MUTE */ +#define WM9090_IN2A_MUTE_MASK 0x0080 /* IN2A_MUTE */ +#define WM9090_IN2A_MUTE_SHIFT 7 /* IN2A_MUTE */ +#define WM9090_IN2A_MUTE_WIDTH 1 /* IN2A_MUTE */ +#define WM9090_IN2A_ZC 0x0040 /* IN2A_ZC */ +#define WM9090_IN2A_ZC_MASK 0x0040 /* IN2A_ZC */ +#define WM9090_IN2A_ZC_SHIFT 6 /* IN2A_ZC */ +#define WM9090_IN2A_ZC_WIDTH 1 /* IN2A_ZC */ +#define WM9090_IN2A_VOL_MASK 0x0007 /* IN2A_VOL - [2:0] */ +#define WM9090_IN2A_VOL_SHIFT 0 /* IN2A_VOL - [2:0] */ +#define WM9090_IN2A_VOL_WIDTH 3 /* IN2A_VOL - [2:0] */ + +/* + * R27 (0x1B) - IN2 Line Input B Volume + */ +#define WM9090_IN2_VU 0x0100 /* IN2_VU */ +#define WM9090_IN2_VU_MASK 0x0100 /* IN2_VU */ +#define WM9090_IN2_VU_SHIFT 8 /* IN2_VU */ +#define WM9090_IN2_VU_WIDTH 1 /* IN2_VU */ +#define WM9090_IN2B_MUTE 0x0080 /* IN2B_MUTE */ +#define WM9090_IN2B_MUTE_MASK 0x0080 /* IN2B_MUTE */ +#define WM9090_IN2B_MUTE_SHIFT 7 /* IN2B_MUTE */ +#define WM9090_IN2B_MUTE_WIDTH 1 /* IN2B_MUTE */ +#define WM9090_IN2B_ZC 0x0040 /* IN2B_ZC */ +#define WM9090_IN2B_ZC_MASK 0x0040 /* IN2B_ZC */ +#define WM9090_IN2B_ZC_SHIFT 6 /* IN2B_ZC */ +#define WM9090_IN2B_ZC_WIDTH 1 /* IN2B_ZC */ +#define WM9090_IN2B_VOL_MASK 0x0007 /* IN2B_VOL - [2:0] */ +#define WM9090_IN2B_VOL_SHIFT 0 /* IN2B_VOL - [2:0] */ +#define WM9090_IN2B_VOL_WIDTH 3 /* IN2B_VOL - [2:0] */ + +/* + * R28 (0x1C) - Left Output Volume + */ +#define WM9090_HPOUT1_VU 0x0100 /* HPOUT1_VU */ +#define WM9090_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */ +#define WM9090_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */ +#define WM9090_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */ +#define WM9090_HPOUT1L_ZC 0x0080 /* HPOUT1L_ZC */ +#define WM9090_HPOUT1L_ZC_MASK 0x0080 /* HPOUT1L_ZC */ +#define WM9090_HPOUT1L_ZC_SHIFT 7 /* HPOUT1L_ZC */ +#define WM9090_HPOUT1L_ZC_WIDTH 1 /* HPOUT1L_ZC */ +#define WM9090_HPOUT1L_MUTE 0x0040 /* HPOUT1L_MUTE */ +#define WM9090_HPOUT1L_MUTE_MASK 0x0040 /* HPOUT1L_MUTE */ +#define WM9090_HPOUT1L_MUTE_SHIFT 6 /* HPOUT1L_MUTE */ +#define WM9090_HPOUT1L_MUTE_WIDTH 1 /* HPOUT1L_MUTE */ +#define WM9090_HPOUT1L_VOL_MASK 0x003F /* HPOUT1L_VOL - [5:0] */ +#define WM9090_HPOUT1L_VOL_SHIFT 0 /* HPOUT1L_VOL - [5:0] */ +#define WM9090_HPOUT1L_VOL_WIDTH 6 /* HPOUT1L_VOL - [5:0] */ + +/* + * R29 (0x1D) - Right Output Volume + */ +#define WM9090_HPOUT1_VU 0x0100 /* HPOUT1_VU */ +#define WM9090_HPOUT1_VU_MASK 0x0100 /* HPOUT1_VU */ +#define WM9090_HPOUT1_VU_SHIFT 8 /* HPOUT1_VU */ +#define WM9090_HPOUT1_VU_WIDTH 1 /* HPOUT1_VU */ +#define WM9090_HPOUT1R_ZC 0x0080 /* HPOUT1R_ZC */ +#define WM9090_HPOUT1R_ZC_MASK 0x0080 /* HPOUT1R_ZC */ +#define WM9090_HPOUT1R_ZC_SHIFT 7 /* HPOUT1R_ZC */ +#define WM9090_HPOUT1R_ZC_WIDTH 1 /* HPOUT1R_ZC */ +#define WM9090_HPOUT1R_MUTE 0x0040 /* HPOUT1R_MUTE */ +#define WM9090_HPOUT1R_MUTE_MASK 0x0040 /* HPOUT1R_MUTE */ +#define WM9090_HPOUT1R_MUTE_SHIFT 6 /* HPOUT1R_MUTE */ +#define WM9090_HPOUT1R_MUTE_WIDTH 1 /* HPOUT1R_MUTE */ +#define WM9090_HPOUT1R_VOL_MASK 0x003F /* HPOUT1R_VOL - [5:0] */ +#define WM9090_HPOUT1R_VOL_SHIFT 0 /* HPOUT1R_VOL - [5:0] */ +#define WM9090_HPOUT1R_VOL_WIDTH 6 /* HPOUT1R_VOL - [5:0] */ + +/* + * R34 (0x22) - SPKMIXL Attenuation + */ +#define WM9090_SPKMIX_MUTE 0x0100 /* SPKMIX_MUTE */ +#define WM9090_SPKMIX_MUTE_MASK 0x0100 /* SPKMIX_MUTE */ +#define WM9090_SPKMIX_MUTE_SHIFT 8 /* SPKMIX_MUTE */ +#define WM9090_SPKMIX_MUTE_WIDTH 1 /* SPKMIX_MUTE */ +#define WM9090_IN1A_SPKMIX_VOL_MASK 0x00C0 /* IN1A_SPKMIX_VOL - [7:6] */ +#define WM9090_IN1A_SPKMIX_VOL_SHIFT 6 /* IN1A_SPKMIX_VOL - [7:6] */ +#define WM9090_IN1A_SPKMIX_VOL_WIDTH 2 /* IN1A_SPKMIX_VOL - [7:6] */ +#define WM9090_IN1B_SPKMIX_VOL_MASK 0x0030 /* IN1B_SPKMIX_VOL - [5:4] */ +#define WM9090_IN1B_SPKMIX_VOL_SHIFT 4 /* IN1B_SPKMIX_VOL - [5:4] */ +#define WM9090_IN1B_SPKMIX_VOL_WIDTH 2 /* IN1B_SPKMIX_VOL - [5:4] */ +#define WM9090_IN2A_SPKMIX_VOL_MASK 0x000C /* IN2A_SPKMIX_VOL - [3:2] */ +#define WM9090_IN2A_SPKMIX_VOL_SHIFT 2 /* IN2A_SPKMIX_VOL - [3:2] */ +#define WM9090_IN2A_SPKMIX_VOL_WIDTH 2 /* IN2A_SPKMIX_VOL - [3:2] */ +#define WM9090_IN2B_SPKMIX_VOL_MASK 0x0003 /* IN2B_SPKMIX_VOL - [1:0] */ +#define WM9090_IN2B_SPKMIX_VOL_SHIFT 0 /* IN2B_SPKMIX_VOL - [1:0] */ +#define WM9090_IN2B_SPKMIX_VOL_WIDTH 2 /* IN2B_SPKMIX_VOL - [1:0] */ + +/* + * R36 (0x24) - SPKOUT Mixers + */ +#define WM9090_SPKMIXL_TO_SPKOUTL 0x0010 /* SPKMIXL_TO_SPKOUTL */ +#define WM9090_SPKMIXL_TO_SPKOUTL_MASK 0x0010 /* SPKMIXL_TO_SPKOUTL */ +#define WM9090_SPKMIXL_TO_SPKOUTL_SHIFT 4 /* SPKMIXL_TO_SPKOUTL */ +#define WM9090_SPKMIXL_TO_SPKOUTL_WIDTH 1 /* SPKMIXL_TO_SPKOUTL */ + +/* + * R37 (0x25) - ClassD3 + */ +#define WM9090_SPKOUTL_BOOST_MASK 0x0038 /* SPKOUTL_BOOST - [5:3] */ +#define WM9090_SPKOUTL_BOOST_SHIFT 3 /* SPKOUTL_BOOST - [5:3] */ +#define WM9090_SPKOUTL_BOOST_WIDTH 3 /* SPKOUTL_BOOST - [5:3] */ + +/* + * R38 (0x26) - Speaker Volume Left + */ +#define WM9090_SPKOUT_VU 0x0100 /* SPKOUT_VU */ +#define WM9090_SPKOUT_VU_MASK 0x0100 /* SPKOUT_VU */ +#define WM9090_SPKOUT_VU_SHIFT 8 /* SPKOUT_VU */ +#define WM9090_SPKOUT_VU_WIDTH 1 /* SPKOUT_VU */ +#define WM9090_SPKOUTL_ZC 0x0080 /* SPKOUTL_ZC */ +#define WM9090_SPKOUTL_ZC_MASK 0x0080 /* SPKOUTL_ZC */ +#define WM9090_SPKOUTL_ZC_SHIFT 7 /* SPKOUTL_ZC */ +#define WM9090_SPKOUTL_ZC_WIDTH 1 /* SPKOUTL_ZC */ +#define WM9090_SPKOUTL_MUTE 0x0040 /* SPKOUTL_MUTE */ +#define WM9090_SPKOUTL_MUTE_MASK 0x0040 /* SPKOUTL_MUTE */ +#define WM9090_SPKOUTL_MUTE_SHIFT 6 /* SPKOUTL_MUTE */ +#define WM9090_SPKOUTL_MUTE_WIDTH 1 /* SPKOUTL_MUTE */ +#define WM9090_SPKOUTL_VOL_MASK 0x003F /* SPKOUTL_VOL - [5:0] */ +#define WM9090_SPKOUTL_VOL_SHIFT 0 /* SPKOUTL_VOL - [5:0] */ +#define WM9090_SPKOUTL_VOL_WIDTH 6 /* SPKOUTL_VOL - [5:0] */ + +/* + * R45 (0x2D) - Output Mixer1 + */ +#define WM9090_IN1A_TO_MIXOUTL 0x0040 /* IN1A_TO_MIXOUTL */ +#define WM9090_IN1A_TO_MIXOUTL_MASK 0x0040 /* IN1A_TO_MIXOUTL */ +#define WM9090_IN1A_TO_MIXOUTL_SHIFT 6 /* IN1A_TO_MIXOUTL */ +#define WM9090_IN1A_TO_MIXOUTL_WIDTH 1 /* IN1A_TO_MIXOUTL */ +#define WM9090_IN2A_TO_MIXOUTL 0x0004 /* IN2A_TO_MIXOUTL */ +#define WM9090_IN2A_TO_MIXOUTL_MASK 0x0004 /* IN2A_TO_MIXOUTL */ +#define WM9090_IN2A_TO_MIXOUTL_SHIFT 2 /* IN2A_TO_MIXOUTL */ +#define WM9090_IN2A_TO_MIXOUTL_WIDTH 1 /* IN2A_TO_MIXOUTL */ + +/* + * R46 (0x2E) - Output Mixer2 + */ +#define WM9090_IN1A_TO_MIXOUTR 0x0040 /* IN1A_TO_MIXOUTR */ +#define WM9090_IN1A_TO_MIXOUTR_MASK 0x0040 /* IN1A_TO_MIXOUTR */ +#define WM9090_IN1A_TO_MIXOUTR_SHIFT 6 /* IN1A_TO_MIXOUTR */ +#define WM9090_IN1A_TO_MIXOUTR_WIDTH 1 /* IN1A_TO_MIXOUTR */ +#define WM9090_IN1B_TO_MIXOUTR 0x0010 /* IN1B_TO_MIXOUTR */ +#define WM9090_IN1B_TO_MIXOUTR_MASK 0x0010 /* IN1B_TO_MIXOUTR */ +#define WM9090_IN1B_TO_MIXOUTR_SHIFT 4 /* IN1B_TO_MIXOUTR */ +#define WM9090_IN1B_TO_MIXOUTR_WIDTH 1 /* IN1B_TO_MIXOUTR */ +#define WM9090_IN2A_TO_MIXOUTR 0x0004 /* IN2A_TO_MIXOUTR */ +#define WM9090_IN2A_TO_MIXOUTR_MASK 0x0004 /* IN2A_TO_MIXOUTR */ +#define WM9090_IN2A_TO_MIXOUTR_SHIFT 2 /* IN2A_TO_MIXOUTR */ +#define WM9090_IN2A_TO_MIXOUTR_WIDTH 1 /* IN2A_TO_MIXOUTR */ +#define WM9090_IN2B_TO_MIXOUTR 0x0001 /* IN2B_TO_MIXOUTR */ +#define WM9090_IN2B_TO_MIXOUTR_MASK 0x0001 /* IN2B_TO_MIXOUTR */ +#define WM9090_IN2B_TO_MIXOUTR_SHIFT 0 /* IN2B_TO_MIXOUTR */ +#define WM9090_IN2B_TO_MIXOUTR_WIDTH 1 /* IN2B_TO_MIXOUTR */ + +/* + * R47 (0x2F) - Output Mixer3 + */ +#define WM9090_MIXOUTL_MUTE 0x0100 /* MIXOUTL_MUTE */ +#define WM9090_MIXOUTL_MUTE_MASK 0x0100 /* MIXOUTL_MUTE */ +#define WM9090_MIXOUTL_MUTE_SHIFT 8 /* MIXOUTL_MUTE */ +#define WM9090_MIXOUTL_MUTE_WIDTH 1 /* MIXOUTL_MUTE */ +#define WM9090_IN1A_MIXOUTL_VOL_MASK 0x00C0 /* IN1A_MIXOUTL_VOL - [7:6] */ +#define WM9090_IN1A_MIXOUTL_VOL_SHIFT 6 /* IN1A_MIXOUTL_VOL - [7:6] */ +#define WM9090_IN1A_MIXOUTL_VOL_WIDTH 2 /* IN1A_MIXOUTL_VOL - [7:6] */ +#define WM9090_IN2A_MIXOUTL_VOL_MASK 0x000C /* IN2A_MIXOUTL_VOL - [3:2] */ +#define WM9090_IN2A_MIXOUTL_VOL_SHIFT 2 /* IN2A_MIXOUTL_VOL - [3:2] */ +#define WM9090_IN2A_MIXOUTL_VOL_WIDTH 2 /* IN2A_MIXOUTL_VOL - [3:2] */ + +/* + * R48 (0x30) - Output Mixer4 + */ +#define WM9090_MIXOUTR_MUTE 0x0100 /* MIXOUTR_MUTE */ +#define WM9090_MIXOUTR_MUTE_MASK 0x0100 /* MIXOUTR_MUTE */ +#define WM9090_MIXOUTR_MUTE_SHIFT 8 /* MIXOUTR_MUTE */ +#define WM9090_MIXOUTR_MUTE_WIDTH 1 /* MIXOUTR_MUTE */ +#define WM9090_IN1A_MIXOUTR_VOL_MASK 0x00C0 /* IN1A_MIXOUTR_VOL - [7:6] */ +#define WM9090_IN1A_MIXOUTR_VOL_SHIFT 6 /* IN1A_MIXOUTR_VOL - [7:6] */ +#define WM9090_IN1A_MIXOUTR_VOL_WIDTH 2 /* IN1A_MIXOUTR_VOL - [7:6] */ +#define WM9090_IN1B_MIXOUTR_VOL_MASK 0x0030 /* IN1B_MIXOUTR_VOL - [5:4] */ +#define WM9090_IN1B_MIXOUTR_VOL_SHIFT 4 /* IN1B_MIXOUTR_VOL - [5:4] */ +#define WM9090_IN1B_MIXOUTR_VOL_WIDTH 2 /* IN1B_MIXOUTR_VOL - [5:4] */ +#define WM9090_IN2A_MIXOUTR_VOL_MASK 0x000C /* IN2A_MIXOUTR_VOL - [3:2] */ +#define WM9090_IN2A_MIXOUTR_VOL_SHIFT 2 /* IN2A_MIXOUTR_VOL - [3:2] */ +#define WM9090_IN2A_MIXOUTR_VOL_WIDTH 2 /* IN2A_MIXOUTR_VOL - [3:2] */ +#define WM9090_IN2B_MIXOUTR_VOL_MASK 0x0003 /* IN2B_MIXOUTR_VOL - [1:0] */ +#define WM9090_IN2B_MIXOUTR_VOL_SHIFT 0 /* IN2B_MIXOUTR_VOL - [1:0] */ +#define WM9090_IN2B_MIXOUTR_VOL_WIDTH 2 /* IN2B_MIXOUTR_VOL - [1:0] */ + +/* + * R54 (0x36) - Speaker Mixer + */ +#define WM9090_IN1A_TO_SPKMIX 0x0040 /* IN1A_TO_SPKMIX */ +#define WM9090_IN1A_TO_SPKMIX_MASK 0x0040 /* IN1A_TO_SPKMIX */ +#define WM9090_IN1A_TO_SPKMIX_SHIFT 6 /* IN1A_TO_SPKMIX */ +#define WM9090_IN1A_TO_SPKMIX_WIDTH 1 /* IN1A_TO_SPKMIX */ +#define WM9090_IN1B_TO_SPKMIX 0x0010 /* IN1B_TO_SPKMIX */ +#define WM9090_IN1B_TO_SPKMIX_MASK 0x0010 /* IN1B_TO_SPKMIX */ +#define WM9090_IN1B_TO_SPKMIX_SHIFT 4 /* IN1B_TO_SPKMIX */ +#define WM9090_IN1B_TO_SPKMIX_WIDTH 1 /* IN1B_TO_SPKMIX */ +#define WM9090_IN2A_TO_SPKMIX 0x0004 /* IN2A_TO_SPKMIX */ +#define WM9090_IN2A_TO_SPKMIX_MASK 0x0004 /* IN2A_TO_SPKMIX */ +#define WM9090_IN2A_TO_SPKMIX_SHIFT 2 /* IN2A_TO_SPKMIX */ +#define WM9090_IN2A_TO_SPKMIX_WIDTH 1 /* IN2A_TO_SPKMIX */ +#define WM9090_IN2B_TO_SPKMIX 0x0001 /* IN2B_TO_SPKMIX */ +#define WM9090_IN2B_TO_SPKMIX_MASK 0x0001 /* IN2B_TO_SPKMIX */ +#define WM9090_IN2B_TO_SPKMIX_SHIFT 0 /* IN2B_TO_SPKMIX */ +#define WM9090_IN2B_TO_SPKMIX_WIDTH 1 /* IN2B_TO_SPKMIX */ + +/* + * R57 (0x39) - AntiPOP2 + */ +#define WM9090_VMID_BUF_ENA 0x0008 /* VMID_BUF_ENA */ +#define WM9090_VMID_BUF_ENA_MASK 0x0008 /* VMID_BUF_ENA */ +#define WM9090_VMID_BUF_ENA_SHIFT 3 /* VMID_BUF_ENA */ +#define WM9090_VMID_BUF_ENA_WIDTH 1 /* VMID_BUF_ENA */ +#define WM9090_VMID_ENA 0x0001 /* VMID_ENA */ +#define WM9090_VMID_ENA_MASK 0x0001 /* VMID_ENA */ +#define WM9090_VMID_ENA_SHIFT 0 /* VMID_ENA */ +#define WM9090_VMID_ENA_WIDTH 1 /* VMID_ENA */ + +/* + * R70 (0x46) - Write Sequencer 0 + */ +#define WM9090_WSEQ_ENA 0x0100 /* WSEQ_ENA */ +#define WM9090_WSEQ_ENA_MASK 0x0100 /* WSEQ_ENA */ +#define WM9090_WSEQ_ENA_SHIFT 8 /* WSEQ_ENA */ +#define WM9090_WSEQ_ENA_WIDTH 1 /* WSEQ_ENA */ +#define WM9090_WSEQ_WRITE_INDEX_MASK 0x000F /* WSEQ_WRITE_INDEX - [3:0] */ +#define WM9090_WSEQ_WRITE_INDEX_SHIFT 0 /* WSEQ_WRITE_INDEX - [3:0] */ +#define WM9090_WSEQ_WRITE_INDEX_WIDTH 4 /* WSEQ_WRITE_INDEX - [3:0] */ + +/* + * R71 (0x47) - Write Sequencer 1 + */ +#define WM9090_WSEQ_DATA_WIDTH_MASK 0x7000 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM9090_WSEQ_DATA_WIDTH_SHIFT 12 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM9090_WSEQ_DATA_WIDTH_WIDTH 3 /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM9090_WSEQ_DATA_START_MASK 0x0F00 /* WSEQ_DATA_START - [11:8] */ +#define WM9090_WSEQ_DATA_START_SHIFT 8 /* WSEQ_DATA_START - [11:8] */ +#define WM9090_WSEQ_DATA_START_WIDTH 4 /* WSEQ_DATA_START - [11:8] */ +#define WM9090_WSEQ_ADDR_MASK 0x00FF /* WSEQ_ADDR - [7:0] */ +#define WM9090_WSEQ_ADDR_SHIFT 0 /* WSEQ_ADDR - [7:0] */ +#define WM9090_WSEQ_ADDR_WIDTH 8 /* WSEQ_ADDR - [7:0] */ + +/* + * R72 (0x48) - Write Sequencer 2 + */ +#define WM9090_WSEQ_EOS 0x4000 /* WSEQ_EOS */ +#define WM9090_WSEQ_EOS_MASK 0x4000 /* WSEQ_EOS */ +#define WM9090_WSEQ_EOS_SHIFT 14 /* WSEQ_EOS */ +#define WM9090_WSEQ_EOS_WIDTH 1 /* WSEQ_EOS */ +#define WM9090_WSEQ_DELAY_MASK 0x0F00 /* WSEQ_DELAY - [11:8] */ +#define WM9090_WSEQ_DELAY_SHIFT 8 /* WSEQ_DELAY - [11:8] */ +#define WM9090_WSEQ_DELAY_WIDTH 4 /* WSEQ_DELAY - [11:8] */ +#define WM9090_WSEQ_DATA_MASK 0x00FF /* WSEQ_DATA - [7:0] */ +#define WM9090_WSEQ_DATA_SHIFT 0 /* WSEQ_DATA - [7:0] */ +#define WM9090_WSEQ_DATA_WIDTH 8 /* WSEQ_DATA - [7:0] */ + +/* + * R73 (0x49) - Write Sequencer 3 + */ +#define WM9090_WSEQ_ABORT 0x0200 /* WSEQ_ABORT */ +#define WM9090_WSEQ_ABORT_MASK 0x0200 /* WSEQ_ABORT */ +#define WM9090_WSEQ_ABORT_SHIFT 9 /* WSEQ_ABORT */ +#define WM9090_WSEQ_ABORT_WIDTH 1 /* WSEQ_ABORT */ +#define WM9090_WSEQ_START 0x0100 /* WSEQ_START */ +#define WM9090_WSEQ_START_MASK 0x0100 /* WSEQ_START */ +#define WM9090_WSEQ_START_SHIFT 8 /* WSEQ_START */ +#define WM9090_WSEQ_START_WIDTH 1 /* WSEQ_START */ +#define WM9090_WSEQ_START_INDEX_MASK 0x003F /* WSEQ_START_INDEX - [5:0] */ +#define WM9090_WSEQ_START_INDEX_SHIFT 0 /* WSEQ_START_INDEX - [5:0] */ +#define WM9090_WSEQ_START_INDEX_WIDTH 6 /* WSEQ_START_INDEX - [5:0] */ + +/* + * R74 (0x4A) - Write Sequencer 4 + */ +#define WM9090_WSEQ_BUSY 0x0001 /* WSEQ_BUSY */ +#define WM9090_WSEQ_BUSY_MASK 0x0001 /* WSEQ_BUSY */ +#define WM9090_WSEQ_BUSY_SHIFT 0 /* WSEQ_BUSY */ +#define WM9090_WSEQ_BUSY_WIDTH 1 /* WSEQ_BUSY */ + +/* + * R75 (0x4B) - Write Sequencer 5 + */ +#define WM9090_WSEQ_CURRENT_INDEX_MASK 0x003F /* WSEQ_CURRENT_INDEX - [5:0] */ +#define WM9090_WSEQ_CURRENT_INDEX_SHIFT 0 /* WSEQ_CURRENT_INDEX - [5:0] */ +#define WM9090_WSEQ_CURRENT_INDEX_WIDTH 6 /* WSEQ_CURRENT_INDEX - [5:0] */ + +/* + * R76 (0x4C) - Charge Pump 1 + */ +#define WM9090_CP_ENA 0x8000 /* CP_ENA */ +#define WM9090_CP_ENA_MASK 0x8000 /* CP_ENA */ +#define WM9090_CP_ENA_SHIFT 15 /* CP_ENA */ +#define WM9090_CP_ENA_WIDTH 1 /* CP_ENA */ + +/* + * R84 (0x54) - DC Servo 0 + */ +#define WM9090_DCS_TRIG_SINGLE_1 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM9090_DCS_TRIG_SINGLE_1_MASK 0x2000 /* DCS_TRIG_SINGLE_1 */ +#define WM9090_DCS_TRIG_SINGLE_1_SHIFT 13 /* DCS_TRIG_SINGLE_1 */ +#define WM9090_DCS_TRIG_SINGLE_1_WIDTH 1 /* DCS_TRIG_SINGLE_1 */ +#define WM9090_DCS_TRIG_SINGLE_0 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM9090_DCS_TRIG_SINGLE_0_MASK 0x1000 /* DCS_TRIG_SINGLE_0 */ +#define WM9090_DCS_TRIG_SINGLE_0_SHIFT 12 /* DCS_TRIG_SINGLE_0 */ +#define WM9090_DCS_TRIG_SINGLE_0_WIDTH 1 /* DCS_TRIG_SINGLE_0 */ +#define WM9090_DCS_TRIG_SERIES_1 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM9090_DCS_TRIG_SERIES_1_MASK 0x0200 /* DCS_TRIG_SERIES_1 */ +#define WM9090_DCS_TRIG_SERIES_1_SHIFT 9 /* DCS_TRIG_SERIES_1 */ +#define WM9090_DCS_TRIG_SERIES_1_WIDTH 1 /* DCS_TRIG_SERIES_1 */ +#define WM9090_DCS_TRIG_SERIES_0 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM9090_DCS_TRIG_SERIES_0_MASK 0x0100 /* DCS_TRIG_SERIES_0 */ +#define WM9090_DCS_TRIG_SERIES_0_SHIFT 8 /* DCS_TRIG_SERIES_0 */ +#define WM9090_DCS_TRIG_SERIES_0_WIDTH 1 /* DCS_TRIG_SERIES_0 */ +#define WM9090_DCS_TRIG_STARTUP_1 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM9090_DCS_TRIG_STARTUP_1_MASK 0x0020 /* DCS_TRIG_STARTUP_1 */ +#define WM9090_DCS_TRIG_STARTUP_1_SHIFT 5 /* DCS_TRIG_STARTUP_1 */ +#define WM9090_DCS_TRIG_STARTUP_1_WIDTH 1 /* DCS_TRIG_STARTUP_1 */ +#define WM9090_DCS_TRIG_STARTUP_0 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM9090_DCS_TRIG_STARTUP_0_MASK 0x0010 /* DCS_TRIG_STARTUP_0 */ +#define WM9090_DCS_TRIG_STARTUP_0_SHIFT 4 /* DCS_TRIG_STARTUP_0 */ +#define WM9090_DCS_TRIG_STARTUP_0_WIDTH 1 /* DCS_TRIG_STARTUP_0 */ +#define WM9090_DCS_TRIG_DAC_WR_1 0x0008 /* DCS_TRIG_DAC_WR_1 */ +#define WM9090_DCS_TRIG_DAC_WR_1_MASK 0x0008 /* DCS_TRIG_DAC_WR_1 */ +#define WM9090_DCS_TRIG_DAC_WR_1_SHIFT 3 /* DCS_TRIG_DAC_WR_1 */ +#define WM9090_DCS_TRIG_DAC_WR_1_WIDTH 1 /* DCS_TRIG_DAC_WR_1 */ +#define WM9090_DCS_TRIG_DAC_WR_0 0x0004 /* DCS_TRIG_DAC_WR_0 */ +#define WM9090_DCS_TRIG_DAC_WR_0_MASK 0x0004 /* DCS_TRIG_DAC_WR_0 */ +#define WM9090_DCS_TRIG_DAC_WR_0_SHIFT 2 /* DCS_TRIG_DAC_WR_0 */ +#define WM9090_DCS_TRIG_DAC_WR_0_WIDTH 1 /* DCS_TRIG_DAC_WR_0 */ +#define WM9090_DCS_ENA_CHAN_1 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM9090_DCS_ENA_CHAN_1_MASK 0x0002 /* DCS_ENA_CHAN_1 */ +#define WM9090_DCS_ENA_CHAN_1_SHIFT 1 /* DCS_ENA_CHAN_1 */ +#define WM9090_DCS_ENA_CHAN_1_WIDTH 1 /* DCS_ENA_CHAN_1 */ +#define WM9090_DCS_ENA_CHAN_0 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM9090_DCS_ENA_CHAN_0_MASK 0x0001 /* DCS_ENA_CHAN_0 */ +#define WM9090_DCS_ENA_CHAN_0_SHIFT 0 /* DCS_ENA_CHAN_0 */ +#define WM9090_DCS_ENA_CHAN_0_WIDTH 1 /* DCS_ENA_CHAN_0 */ + +/* + * R85 (0x55) - DC Servo 1 + */ +#define WM9090_DCS_SERIES_NO_01_MASK 0x0FE0 /* DCS_SERIES_NO_01 - [11:5] */ +#define WM9090_DCS_SERIES_NO_01_SHIFT 5 /* DCS_SERIES_NO_01 - [11:5] */ +#define WM9090_DCS_SERIES_NO_01_WIDTH 7 /* DCS_SERIES_NO_01 - [11:5] */ +#define WM9090_DCS_TIMER_PERIOD_01_MASK 0x000F /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM9090_DCS_TIMER_PERIOD_01_SHIFT 0 /* DCS_TIMER_PERIOD_01 - [3:0] */ +#define WM9090_DCS_TIMER_PERIOD_01_WIDTH 4 /* DCS_TIMER_PERIOD_01 - [3:0] */ + +/* + * R87 (0x57) - DC Servo 3 + */ +#define WM9090_DCS_DAC_WR_VAL_1_MASK 0xFF00 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM9090_DCS_DAC_WR_VAL_1_SHIFT 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM9090_DCS_DAC_WR_VAL_1_WIDTH 8 /* DCS_DAC_WR_VAL_1 - [15:8] */ +#define WM9090_DCS_DAC_WR_VAL_0_MASK 0x00FF /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM9090_DCS_DAC_WR_VAL_0_SHIFT 0 /* DCS_DAC_WR_VAL_0 - [7:0] */ +#define WM9090_DCS_DAC_WR_VAL_0_WIDTH 8 /* DCS_DAC_WR_VAL_0 - [7:0] */ + +/* + * R88 (0x58) - DC Servo Readback 0 + */ +#define WM9090_DCS_CAL_COMPLETE_MASK 0x0300 /* DCS_CAL_COMPLETE - [9:8] */ +#define WM9090_DCS_CAL_COMPLETE_SHIFT 8 /* DCS_CAL_COMPLETE - [9:8] */ +#define WM9090_DCS_CAL_COMPLETE_WIDTH 2 /* DCS_CAL_COMPLETE - [9:8] */ +#define WM9090_DCS_DAC_WR_COMPLETE_MASK 0x0030 /* DCS_DAC_WR_COMPLETE - [5:4] */ +#define WM9090_DCS_DAC_WR_COMPLETE_SHIFT 4 /* DCS_DAC_WR_COMPLETE - [5:4] */ +#define WM9090_DCS_DAC_WR_COMPLETE_WIDTH 2 /* DCS_DAC_WR_COMPLETE - [5:4] */ +#define WM9090_DCS_STARTUP_COMPLETE_MASK 0x0003 /* DCS_STARTUP_COMPLETE - [1:0] */ +#define WM9090_DCS_STARTUP_COMPLETE_SHIFT 0 /* DCS_STARTUP_COMPLETE - [1:0] */ +#define WM9090_DCS_STARTUP_COMPLETE_WIDTH 2 /* DCS_STARTUP_COMPLETE - [1:0] */ + +/* + * R89 (0x59) - DC Servo Readback 1 + */ +#define WM9090_DCS_DAC_WR_VAL_1_RD_MASK 0x00FF /* DCS_DAC_WR_VAL_1_RD - [7:0] */ +#define WM9090_DCS_DAC_WR_VAL_1_RD_SHIFT 0 /* DCS_DAC_WR_VAL_1_RD - [7:0] */ +#define WM9090_DCS_DAC_WR_VAL_1_RD_WIDTH 8 /* DCS_DAC_WR_VAL_1_RD - [7:0] */ + +/* + * R90 (0x5A) - DC Servo Readback 2 + */ +#define WM9090_DCS_DAC_WR_VAL_0_RD_MASK 0x00FF /* DCS_DAC_WR_VAL_0_RD - [7:0] */ +#define WM9090_DCS_DAC_WR_VAL_0_RD_SHIFT 0 /* DCS_DAC_WR_VAL_0_RD - [7:0] */ +#define WM9090_DCS_DAC_WR_VAL_0_RD_WIDTH 8 /* DCS_DAC_WR_VAL_0_RD - [7:0] */ + +/* + * R96 (0x60) - Analogue HP 0 + */ +#define WM9090_HPOUT1L_RMV_SHORT 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM9090_HPOUT1L_RMV_SHORT_MASK 0x0080 /* HPOUT1L_RMV_SHORT */ +#define WM9090_HPOUT1L_RMV_SHORT_SHIFT 7 /* HPOUT1L_RMV_SHORT */ +#define WM9090_HPOUT1L_RMV_SHORT_WIDTH 1 /* HPOUT1L_RMV_SHORT */ +#define WM9090_HPOUT1L_OUTP 0x0040 /* HPOUT1L_OUTP */ +#define WM9090_HPOUT1L_OUTP_MASK 0x0040 /* HPOUT1L_OUTP */ +#define WM9090_HPOUT1L_OUTP_SHIFT 6 /* HPOUT1L_OUTP */ +#define WM9090_HPOUT1L_OUTP_WIDTH 1 /* HPOUT1L_OUTP */ +#define WM9090_HPOUT1L_DLY 0x0020 /* HPOUT1L_DLY */ +#define WM9090_HPOUT1L_DLY_MASK 0x0020 /* HPOUT1L_DLY */ +#define WM9090_HPOUT1L_DLY_SHIFT 5 /* HPOUT1L_DLY */ +#define WM9090_HPOUT1L_DLY_WIDTH 1 /* HPOUT1L_DLY */ +#define WM9090_HPOUT1R_RMV_SHORT 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM9090_HPOUT1R_RMV_SHORT_MASK 0x0008 /* HPOUT1R_RMV_SHORT */ +#define WM9090_HPOUT1R_RMV_SHORT_SHIFT 3 /* HPOUT1R_RMV_SHORT */ +#define WM9090_HPOUT1R_RMV_SHORT_WIDTH 1 /* HPOUT1R_RMV_SHORT */ +#define WM9090_HPOUT1R_OUTP 0x0004 /* HPOUT1R_OUTP */ +#define WM9090_HPOUT1R_OUTP_MASK 0x0004 /* HPOUT1R_OUTP */ +#define WM9090_HPOUT1R_OUTP_SHIFT 2 /* HPOUT1R_OUTP */ +#define WM9090_HPOUT1R_OUTP_WIDTH 1 /* HPOUT1R_OUTP */ +#define WM9090_HPOUT1R_DLY 0x0002 /* HPOUT1R_DLY */ +#define WM9090_HPOUT1R_DLY_MASK 0x0002 /* HPOUT1R_DLY */ +#define WM9090_HPOUT1R_DLY_SHIFT 1 /* HPOUT1R_DLY */ +#define WM9090_HPOUT1R_DLY_WIDTH 1 /* HPOUT1R_DLY */ + +/* + * R98 (0x62) - AGC Control 0 + */ +#define WM9090_AGC_CLIP_ENA 0x8000 /* AGC_CLIP_ENA */ +#define WM9090_AGC_CLIP_ENA_MASK 0x8000 /* AGC_CLIP_ENA */ +#define WM9090_AGC_CLIP_ENA_SHIFT 15 /* AGC_CLIP_ENA */ +#define WM9090_AGC_CLIP_ENA_WIDTH 1 /* AGC_CLIP_ENA */ +#define WM9090_AGC_CLIP_THR_MASK 0x0F00 /* AGC_CLIP_THR - [11:8] */ +#define WM9090_AGC_CLIP_THR_SHIFT 8 /* AGC_CLIP_THR - [11:8] */ +#define WM9090_AGC_CLIP_THR_WIDTH 4 /* AGC_CLIP_THR - [11:8] */ +#define WM9090_AGC_CLIP_ATK_MASK 0x0070 /* AGC_CLIP_ATK - [6:4] */ +#define WM9090_AGC_CLIP_ATK_SHIFT 4 /* AGC_CLIP_ATK - [6:4] */ +#define WM9090_AGC_CLIP_ATK_WIDTH 3 /* AGC_CLIP_ATK - [6:4] */ +#define WM9090_AGC_CLIP_DCY_MASK 0x0007 /* AGC_CLIP_DCY - [2:0] */ +#define WM9090_AGC_CLIP_DCY_SHIFT 0 /* AGC_CLIP_DCY - [2:0] */ +#define WM9090_AGC_CLIP_DCY_WIDTH 3 /* AGC_CLIP_DCY - [2:0] */ + +/* + * R99 (0x63) - AGC Control 1 + */ +#define WM9090_AGC_PWR_ENA 0x8000 /* AGC_PWR_ENA */ +#define WM9090_AGC_PWR_ENA_MASK 0x8000 /* AGC_PWR_ENA */ +#define WM9090_AGC_PWR_ENA_SHIFT 15 /* AGC_PWR_ENA */ +#define WM9090_AGC_PWR_ENA_WIDTH 1 /* AGC_PWR_ENA */ +#define WM9090_AGC_PWR_AVG 0x1000 /* AGC_PWR_AVG */ +#define WM9090_AGC_PWR_AVG_MASK 0x1000 /* AGC_PWR_AVG */ +#define WM9090_AGC_PWR_AVG_SHIFT 12 /* AGC_PWR_AVG */ +#define WM9090_AGC_PWR_AVG_WIDTH 1 /* AGC_PWR_AVG */ +#define WM9090_AGC_PWR_THR_MASK 0x0F00 /* AGC_PWR_THR - [11:8] */ +#define WM9090_AGC_PWR_THR_SHIFT 8 /* AGC_PWR_THR - [11:8] */ +#define WM9090_AGC_PWR_THR_WIDTH 4 /* AGC_PWR_THR - [11:8] */ +#define WM9090_AGC_PWR_ATK_MASK 0x0070 /* AGC_PWR_ATK - [6:4] */ +#define WM9090_AGC_PWR_ATK_SHIFT 4 /* AGC_PWR_ATK - [6:4] */ +#define WM9090_AGC_PWR_ATK_WIDTH 3 /* AGC_PWR_ATK - [6:4] */ +#define WM9090_AGC_PWR_DCY_MASK 0x0007 /* AGC_PWR_DCY - [2:0] */ +#define WM9090_AGC_PWR_DCY_SHIFT 0 /* AGC_PWR_DCY - [2:0] */ +#define WM9090_AGC_PWR_DCY_WIDTH 3 /* AGC_PWR_DCY - [2:0] */ + +/* + * R100 (0x64) - AGC Control 2 + */ +#define WM9090_AGC_RAMP 0x0100 /* AGC_RAMP */ +#define WM9090_AGC_RAMP_MASK 0x0100 /* AGC_RAMP */ +#define WM9090_AGC_RAMP_SHIFT 8 /* AGC_RAMP */ +#define WM9090_AGC_RAMP_WIDTH 1 /* AGC_RAMP */ +#define WM9090_AGC_MINGAIN_MASK 0x003F /* AGC_MINGAIN - [5:0] */ +#define WM9090_AGC_MINGAIN_SHIFT 0 /* AGC_MINGAIN - [5:0] */ +#define WM9090_AGC_MINGAIN_WIDTH 6 /* AGC_MINGAIN - [5:0] */ + +#endif diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c new file mode 100644 index 0000000000..5c6aebe29c --- /dev/null +++ b/sound/soc/codecs/wm9705.c @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm9705.c -- ALSA Soc WM9705 codec support + * + * Copyright 2008 Ian Molton + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WM9705_VENDOR_ID 0x574d4c05 +#define WM9705_VENDOR_ID_MASK 0xffffffff + +struct wm9705_priv { + struct snd_ac97 *ac97; + struct wm97xx_platform_data *mfd_pdata; +}; + +static const struct reg_default wm9705_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x0a, 0x8000 }, + { 0x0c, 0x8008 }, + { 0x0e, 0x8008 }, + { 0x10, 0x8808 }, + { 0x12, 0x8808 }, + { 0x14, 0x8808 }, + { 0x16, 0x8808 }, + { 0x18, 0x8808 }, + { 0x1a, 0x0000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0000 }, + { 0x2c, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x5a, 0x0000 }, + { 0x5c, 0x0000 }, + { 0x72, 0x0808 }, + { 0x74, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0000 }, + { 0x7a, 0x0000 }, +}; + +static const struct regmap_config wm9705_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = regmap_ac97_default_volatile, + + .reg_defaults = wm9705_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9705_reg_defaults), +}; + +static const struct snd_kcontrol_new wm9705_snd_ac97_controls[] = { + SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1), + SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1), + SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), + SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1), + SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1), + SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1), + SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1), + SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), + SOC_SINGLE("PCBeep Playback Volume", AC97_PC_BEEP, 1, 15, 1), + SOC_SINGLE("Phone Playback Volume", AC97_PHONE, 0, 31, 1), + SOC_DOUBLE("Line Playback Volume", AC97_LINE, 8, 0, 31, 1), + SOC_DOUBLE("CD Playback Volume", AC97_CD, 8, 0, 31, 1), + SOC_SINGLE("Mic Playback Volume", AC97_MIC, 0, 31, 1), + SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 6, 1, 0), + SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0), + SOC_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1), +}; + +static const char *wm9705_mic[] = {"Mic 1", "Mic 2"}; +static const char *wm9705_rec_sel[] = {"Mic", "CD", "NC", "NC", + "Line", "Stereo Mix", "Mono Mix", "Phone"}; + +static SOC_ENUM_SINGLE_DECL(wm9705_enum_mic, + AC97_GENERAL_PURPOSE, 8, wm9705_mic); +static SOC_ENUM_SINGLE_DECL(wm9705_enum_rec_l, + AC97_REC_SEL, 8, wm9705_rec_sel); +static SOC_ENUM_SINGLE_DECL(wm9705_enum_rec_r, + AC97_REC_SEL, 0, wm9705_rec_sel); + +/* Headphone Mixer */ +static const struct snd_kcontrol_new wm9705_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("PCBeep Playback Switch", AC97_PC_BEEP, 15, 1, 1), + SOC_DAPM_SINGLE("CD Playback Switch", AC97_CD, 15, 1, 1), + SOC_DAPM_SINGLE("Mic Playback Switch", AC97_MIC, 15, 1, 1), + SOC_DAPM_SINGLE("Phone Playback Switch", AC97_PHONE, 15, 1, 1), + SOC_DAPM_SINGLE("Line Playback Switch", AC97_LINE, 15, 1, 1), +}; + +/* Mic source */ +static const struct snd_kcontrol_new wm9705_mic_src_controls = + SOC_DAPM_ENUM("Route", wm9705_enum_mic); + +/* Capture source */ +static const struct snd_kcontrol_new wm9705_capture_selectl_controls = + SOC_DAPM_ENUM("Route", wm9705_enum_rec_l); +static const struct snd_kcontrol_new wm9705_capture_selectr_controls = + SOC_DAPM_ENUM("Route", wm9705_enum_rec_r); + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget wm9705_dapm_widgets[] = { + SND_SOC_DAPM_MUX("Mic Source", SND_SOC_NOPM, 0, 0, + &wm9705_mic_src_controls), + SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0, + &wm9705_capture_selectl_controls), + SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0, + &wm9705_capture_selectr_controls), + SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MIXER_NAMED_CTL("HP Mixer", SND_SOC_NOPM, 0, 0, + &wm9705_hp_mixer_controls[0], + ARRAY_SIZE(wm9705_hp_mixer_controls)), + SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("Headphone PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Line PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Line out PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mono PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Phone PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Mic PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PCBEEP PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("CD PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC PGA", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), + SND_SOC_DAPM_OUTPUT("MONOOUT"), + SND_SOC_DAPM_INPUT("PHONE"), + SND_SOC_DAPM_INPUT("LINEINL"), + SND_SOC_DAPM_INPUT("LINEINR"), + SND_SOC_DAPM_INPUT("CDINL"), + SND_SOC_DAPM_INPUT("CDINR"), + SND_SOC_DAPM_INPUT("PCBEEP"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), +}; + +/* Audio map + * WM9705 has no switches to disable the route from the inputs to the HP mixer + * so in order to prevent active inputs from forcing the audio outputs to be + * constantly enabled, we use the mutes on those inputs to simulate such + * controls. + */ +static const struct snd_soc_dapm_route wm9705_audio_map[] = { + /* HP mixer */ + {"HP Mixer", "PCBeep Playback Switch", "PCBEEP PGA"}, + {"HP Mixer", "CD Playback Switch", "CD PGA"}, + {"HP Mixer", "Mic Playback Switch", "Mic PGA"}, + {"HP Mixer", "Phone Playback Switch", "Phone PGA"}, + {"HP Mixer", "Line Playback Switch", "Line PGA"}, + {"HP Mixer", NULL, "Left DAC"}, + {"HP Mixer", NULL, "Right DAC"}, + + /* mono mixer */ + {"Mono Mixer", NULL, "HP Mixer"}, + + /* outputs */ + {"Headphone PGA", NULL, "HP Mixer"}, + {"HPOUTL", NULL, "Headphone PGA"}, + {"HPOUTR", NULL, "Headphone PGA"}, + {"Line out PGA", NULL, "HP Mixer"}, + {"LOUT", NULL, "Line out PGA"}, + {"ROUT", NULL, "Line out PGA"}, + {"Mono PGA", NULL, "Mono Mixer"}, + {"MONOOUT", NULL, "Mono PGA"}, + + /* inputs */ + {"CD PGA", NULL, "CDINL"}, + {"CD PGA", NULL, "CDINR"}, + {"Line PGA", NULL, "LINEINL"}, + {"Line PGA", NULL, "LINEINR"}, + {"Phone PGA", NULL, "PHONE"}, + {"Mic Source", "Mic 1", "MIC1"}, + {"Mic Source", "Mic 2", "MIC2"}, + {"Mic PGA", NULL, "Mic Source"}, + {"PCBEEP PGA", NULL, "PCBEEP"}, + + /* Left capture selector */ + {"Left Capture Source", "Mic", "Mic Source"}, + {"Left Capture Source", "CD", "CDINL"}, + {"Left Capture Source", "Line", "LINEINL"}, + {"Left Capture Source", "Stereo Mix", "HP Mixer"}, + {"Left Capture Source", "Mono Mix", "HP Mixer"}, + {"Left Capture Source", "Phone", "PHONE"}, + + /* Right capture source */ + {"Right Capture Source", "Mic", "Mic Source"}, + {"Right Capture Source", "CD", "CDINR"}, + {"Right Capture Source", "Line", "LINEINR"}, + {"Right Capture Source", "Stereo Mix", "HP Mixer"}, + {"Right Capture Source", "Mono Mix", "HP Mixer"}, + {"Right Capture Source", "Phone", "PHONE"}, + + {"ADC PGA", NULL, "Left Capture Source"}, + {"ADC PGA", NULL, "Right Capture Source"}, + + /* ADC's */ + {"Left ADC", NULL, "ADC PGA"}, + {"Right ADC", NULL, "ADC PGA"}, +}; + +static int ac97_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int reg; + + snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x1, 0x1); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = AC97_PCM_FRONT_DAC_RATE; + else + reg = AC97_PCM_LR_ADC_RATE; + + return snd_soc_component_write(component, reg, substream->runtime->rate); +} + +#define WM9705_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +static const struct snd_soc_dai_ops wm9705_dai_ops = { + .prepare = ac97_prepare, +}; + +static struct snd_soc_dai_driver wm9705_dai[] = { + { + .name = "wm9705-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM9705_AC97_RATES, + .formats = SND_SOC_STD_AC97_FMTS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM9705_AC97_RATES, + .formats = SND_SOC_STD_AC97_FMTS, + }, + .ops = &wm9705_dai_ops, + }, + { + .name = "wm9705-aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 1, + .rates = WM9705_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + } +}; + +#ifdef CONFIG_PM +static int wm9705_soc_suspend(struct snd_soc_component *component) +{ + regcache_cache_bypass(component->regmap, true); + snd_soc_component_write(component, AC97_POWERDOWN, 0xffff); + regcache_cache_bypass(component->regmap, false); + + return 0; +} + +static int wm9705_soc_resume(struct snd_soc_component *component) +{ + struct wm9705_priv *wm9705 = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_ac97_reset(wm9705->ac97, true, WM9705_VENDOR_ID, + WM9705_VENDOR_ID_MASK); + if (ret < 0) + return ret; + + snd_soc_component_cache_sync(component); + + return 0; +} +#else +#define wm9705_soc_suspend NULL +#define wm9705_soc_resume NULL +#endif + +static int wm9705_soc_probe(struct snd_soc_component *component) +{ + struct wm9705_priv *wm9705 = snd_soc_component_get_drvdata(component); + struct regmap *regmap; + + if (wm9705->mfd_pdata) { + wm9705->ac97 = wm9705->mfd_pdata->ac97; + regmap = wm9705->mfd_pdata->regmap; + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { + wm9705->ac97 = snd_soc_new_ac97_component(component, WM9705_VENDOR_ID, + WM9705_VENDOR_ID_MASK); + if (IS_ERR(wm9705->ac97)) { + dev_err(component->dev, "Failed to register AC97 codec\n"); + return PTR_ERR(wm9705->ac97); + } + + regmap = regmap_init_ac97(wm9705->ac97, &wm9705_regmap_config); + if (IS_ERR(regmap)) { + snd_soc_free_ac97_component(wm9705->ac97); + return PTR_ERR(regmap); + } + } else { + return -ENXIO; + } + + snd_soc_component_set_drvdata(component, wm9705->ac97); + snd_soc_component_init_regmap(component, regmap); + + return 0; +} + +static void wm9705_soc_remove(struct snd_soc_component *component) +{ + struct wm9705_priv *wm9705 = snd_soc_component_get_drvdata(component); + + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9705->mfd_pdata) { + snd_soc_component_exit_regmap(component); + snd_soc_free_ac97_component(wm9705->ac97); + } +} + +static const struct snd_soc_component_driver soc_component_dev_wm9705 = { + .probe = wm9705_soc_probe, + .remove = wm9705_soc_remove, + .suspend = wm9705_soc_suspend, + .resume = wm9705_soc_resume, + .controls = wm9705_snd_ac97_controls, + .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls), + .dapm_widgets = wm9705_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets), + .dapm_routes = wm9705_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm9705_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm9705_probe(struct platform_device *pdev) +{ + struct wm9705_priv *wm9705; + + wm9705 = devm_kzalloc(&pdev->dev, sizeof(*wm9705), GFP_KERNEL); + if (wm9705 == NULL) + return -ENOMEM; + + wm9705->mfd_pdata = dev_get_platdata(&pdev->dev); + platform_set_drvdata(pdev, wm9705); + + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm9705, wm9705_dai, ARRAY_SIZE(wm9705_dai)); +} + +static struct platform_driver wm9705_codec_driver = { + .driver = { + .name = "wm9705-codec", + }, + + .probe = wm9705_probe, +}; + +module_platform_driver(wm9705_codec_driver); + +MODULE_DESCRIPTION("ASoC WM9705 driver"); +MODULE_AUTHOR("Ian Molton"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c new file mode 100644 index 0000000000..e63921de0c --- /dev/null +++ b/sound/soc/codecs/wm9712.c @@ -0,0 +1,726 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * wm9712.c -- ALSA Soc WM9712 codec support + * + * Copyright 2006-12 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WM9712_VENDOR_ID 0x574d4c12 +#define WM9712_VENDOR_ID_MASK 0xffffffff + +struct wm9712_priv { + struct snd_ac97 *ac97; + unsigned int hp_mixer[2]; + struct mutex lock; + struct wm97xx_platform_data *mfd_pdata; +}; + +static const struct reg_default wm9712_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x08, 0x0f0f }, + { 0x0a, 0xaaa0 }, + { 0x0c, 0xc008 }, + { 0x0e, 0x6808 }, + { 0x10, 0xe808 }, + { 0x12, 0xaaa0 }, + { 0x14, 0xad00 }, + { 0x16, 0x8000 }, + { 0x18, 0xe808 }, + { 0x1a, 0x3000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0410 }, + { 0x2c, 0xbb80 }, + { 0x2e, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x4c, 0xf83e }, + { 0x4e, 0xffff }, + { 0x50, 0x0000 }, + { 0x52, 0x0000 }, + { 0x56, 0xf83e }, + { 0x58, 0x0008 }, + { 0x5c, 0x0000 }, + { 0x60, 0xb032 }, + { 0x62, 0x3e00 }, + { 0x64, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0001 }, + { 0x7a, 0x0000 }, +}; + +static bool wm9712_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_REC_GAIN: + return true; + default: + return regmap_ac97_default_volatile(dev, reg); + } +} + +static const struct regmap_config wm9712_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_MAPLE, + + .volatile_reg = wm9712_volatile_reg, + + .reg_defaults = wm9712_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults), +}; + +#define HPL_MIXER 0x0 +#define HPR_MIXER 0x1 + +static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"}; +static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"}; +static const char *wm9712_out3_src[] = {"Left", "VREF", "Left + Right", + "Mono"}; +static const char *wm9712_spk_src[] = {"Speaker Mix", "Headphone Mix"}; +static const char *wm9712_rec_adc[] = {"Stereo", "Left", "Right", "Mute"}; +static const char *wm9712_base[] = {"Linear Control", "Adaptive Boost"}; +static const char *wm9712_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"}; +static const char *wm9712_mic[] = {"Mic 1", "Differential", "Mic 2", + "Stereo"}; +static const char *wm9712_rec_sel[] = {"Mic", "NC", "NC", "Speaker Mixer", + "Line", "Headphone Mixer", "Phone Mixer", "Phone"}; +static const char *wm9712_ng_type[] = {"Constant Gain", "Mute"}; +static const char *wm9712_diff_sel[] = {"Mic", "Line"}; + +static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 2000, 0); + +static const struct soc_enum wm9712_enum[] = { +SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9712_alc_select), +SOC_ENUM_SINGLE(AC97_VIDEO, 12, 4, wm9712_alc_mux), +SOC_ENUM_SINGLE(AC97_AUX, 9, 4, wm9712_out3_src), +SOC_ENUM_SINGLE(AC97_AUX, 8, 2, wm9712_spk_src), +SOC_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9712_rec_adc), +SOC_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9712_base), +SOC_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9712_rec_gain), +SOC_ENUM_SINGLE(AC97_MIC, 5, 4, wm9712_mic), +SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9712_rec_sel), +SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9712_rec_sel), +SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9712_ng_type), +SOC_ENUM_SINGLE(0x5c, 8, 2, wm9712_diff_sel), +}; + +static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = { +SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1), +SOC_SINGLE("Speaker Playback Switch", AC97_MASTER, 15, 1, 1), +SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), +SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1), +SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1), + +SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0), +SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0), +SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0), +SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0), +SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1), +SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), + +SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), +SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), +SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0), +SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0), +SOC_ENUM("ALC Function", wm9712_enum[0]), +SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0), +SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1), +SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0), +SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0), +SOC_ENUM("ALC NG Type", wm9712_enum[10]), +SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1), + +SOC_SINGLE("Mic Headphone Volume", AC97_VIDEO, 12, 7, 1), +SOC_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1), + +SOC_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1), +SOC_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 1), +SOC_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1), + +SOC_SINGLE("PCBeep Bypass Headphone Volume", AC97_PC_BEEP, 12, 7, 1), +SOC_SINGLE("PCBeep Bypass Speaker Volume", AC97_PC_BEEP, 8, 7, 1), +SOC_SINGLE("PCBeep Bypass Phone Volume", AC97_PC_BEEP, 4, 7, 1), + +SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1), +SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1), +SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1), + +SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1), +SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1), + +SOC_SINGLE_TLV("Capture Boost Switch", AC97_REC_SEL, 14, 1, 0, boost_tlv), +SOC_SINGLE_TLV("Capture to Phone Boost Switch", AC97_REC_SEL, 11, 1, 1, + boost_tlv), + +SOC_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1), +SOC_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1), +SOC_SINGLE("3D Playback Volume", AC97_3D_CONTROL, 0, 15, 0), + +SOC_ENUM("Bass Control", wm9712_enum[5]), +SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1), +SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1), +SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0), +SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1), +SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1), + +SOC_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1), +SOC_ENUM("Capture Volume Steps", wm9712_enum[6]), +SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 0), +SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0), + +SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv), +SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv), +SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv), +}; + +static const unsigned int wm9712_mixer_mute_regs[] = { + AC97_VIDEO, + AC97_PCM, + AC97_LINE, + AC97_PHONE, + AC97_CD, + AC97_PC_BEEP, +}; + +/* We have to create a fake left and right HP mixers because + * the codec only has a single control that is shared by both channels. + * This makes it impossible to determine the audio path. + */ +static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component); + unsigned int val = ucontrol->value.integer.value[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mixer, mask, shift, old; + struct snd_soc_dapm_update update = {}; + bool change; + + mixer = mc->shift >> 8; + shift = mc->shift & 0xff; + mask = 1 << shift; + + mutex_lock(&wm9712->lock); + old = wm9712->hp_mixer[mixer]; + if (ucontrol->value.integer.value[0]) + wm9712->hp_mixer[mixer] |= mask; + else + wm9712->hp_mixer[mixer] &= ~mask; + + change = old != wm9712->hp_mixer[mixer]; + if (change) { + update.kcontrol = kcontrol; + update.reg = wm9712_mixer_mute_regs[shift]; + update.mask = 0x8000; + if ((wm9712->hp_mixer[0] & mask) || + (wm9712->hp_mixer[1] & mask)) + update.val = 0x0; + else + update.val = 0x8000; + + snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, + &update); + } + + mutex_unlock(&wm9712->lock); + + return change; +} + +static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int shift, mixer; + + mixer = mc->shift >> 8; + shift = mc->shift & 0xff; + + ucontrol->value.integer.value[0] = + (wm9712->hp_mixer[mixer] >> shift) & 1; + + return 0; +} + +#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \ + .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \ + (xmixer << 8) | xshift, 1, 0, 0) \ +} + +/* Left Headphone Mixers */ +static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = { + WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5), + WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4), + WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3), + WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2), + WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1), + WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0), +}; + +/* Right Headphone Mixers */ +static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = { + WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5), + WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4), + WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3), + WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2), + WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1), + WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0), +}; + +/* Speaker Mixer */ +static const struct snd_kcontrol_new wm9712_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 11, 1, 1), + SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 11, 1, 1), + SOC_DAPM_SINGLE("Phone Bypass Switch", AC97_PHONE, 14, 1, 1), + SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 14, 1, 1), + SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 14, 1, 1), +}; + +/* Phone Mixer */ +static const struct snd_kcontrol_new wm9712_phone_mixer_controls[] = { + SOC_DAPM_SINGLE("PCBeep Bypass Switch", AC97_PC_BEEP, 7, 1, 1), + SOC_DAPM_SINGLE("Aux Playback Switch", AC97_CD, 7, 1, 1), + SOC_DAPM_SINGLE("Line Bypass Switch", AC97_LINE, 13, 1, 1), + SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PCM, 13, 1, 1), + SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_MIC, 14, 1, 1), + SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_MIC, 13, 1, 1), +}; + +/* ALC headphone mux */ +static const struct snd_kcontrol_new wm9712_alc_mux_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[1]); + +/* out 3 mux */ +static const struct snd_kcontrol_new wm9712_out3_mux_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[2]); + +/* spk mux */ +static const struct snd_kcontrol_new wm9712_spk_mux_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[3]); + +/* Capture to Phone mux */ +static const struct snd_kcontrol_new wm9712_capture_phone_mux_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[4]); + +/* Capture left select */ +static const struct snd_kcontrol_new wm9712_capture_selectl_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[8]); + +/* Capture right select */ +static const struct snd_kcontrol_new wm9712_capture_selectr_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[9]); + +/* Mic select */ +static const struct snd_kcontrol_new wm9712_mic_src_controls = +SOC_DAPM_ENUM("Mic Source Select", wm9712_enum[7]); + +/* diff select */ +static const struct snd_kcontrol_new wm9712_diff_sel_controls = +SOC_DAPM_ENUM("Route", wm9712_enum[11]); + +static const struct snd_soc_dapm_widget wm9712_dapm_widgets[] = { +SND_SOC_DAPM_MUX("ALC Sidetone Mux", SND_SOC_NOPM, 0, 0, + &wm9712_alc_mux_controls), +SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, + &wm9712_out3_mux_controls), +SND_SOC_DAPM_MUX("Speaker Mux", SND_SOC_NOPM, 0, 0, + &wm9712_spk_mux_controls), +SND_SOC_DAPM_MUX("Capture Phone Mux", SND_SOC_NOPM, 0, 0, + &wm9712_capture_phone_mux_controls), +SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0, + &wm9712_capture_selectl_controls), +SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0, + &wm9712_capture_selectr_controls), +SND_SOC_DAPM_MUX("Left Mic Select Source", SND_SOC_NOPM, 0, 0, + &wm9712_mic_src_controls), +SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0, + &wm9712_mic_src_controls), +SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0, + &wm9712_diff_sel_controls), +SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1, + &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)), +SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1, + &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)), +SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1, + &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)), +SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1, + &wm9712_speaker_mixer_controls[0], + ARRAY_SIZE(wm9712_speaker_mixer_controls)), +SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_INT_PAGING, 14, 1), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_INT_PAGING, 13, 1), +SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_INT_PAGING, 12, 1), +SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_INT_PAGING, 11, 1), +SND_SOC_DAPM_PGA("Headphone PGA", AC97_INT_PAGING, 4, 1, NULL, 0), +SND_SOC_DAPM_PGA("Speaker PGA", AC97_INT_PAGING, 3, 1, NULL, 0), +SND_SOC_DAPM_PGA("Out 3 PGA", AC97_INT_PAGING, 5, 1, NULL, 0), +SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0), +SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0), +SND_SOC_DAPM_PGA("Differential Mic", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1), +SND_SOC_DAPM_OUTPUT("MONOOUT"), +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("LOUT2"), +SND_SOC_DAPM_OUTPUT("ROUT2"), +SND_SOC_DAPM_OUTPUT("OUT3"), +SND_SOC_DAPM_INPUT("LINEINL"), +SND_SOC_DAPM_INPUT("LINEINR"), +SND_SOC_DAPM_INPUT("PHONE"), +SND_SOC_DAPM_INPUT("PCBEEP"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +}; + +static const struct snd_soc_dapm_route wm9712_audio_map[] = { + /* virtual mixer - mixes left & right channels for spk and mono */ + {"AC97 Mixer", NULL, "Left DAC"}, + {"AC97 Mixer", NULL, "Right DAC"}, + + /* Left HP mixer */ + {"Left HP Mixer", "PCBeep Bypass Switch", "PCBEEP"}, + {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Left HP Mixer", "Phone Bypass Switch", "Phone PGA"}, + {"Left HP Mixer", "Line Bypass Switch", "Line PGA"}, + {"Left HP Mixer", "PCM Playback Switch", "Left DAC"}, + {"Left HP Mixer", "Mic Sidetone Switch", "Mic PGA"}, + {"Left HP Mixer", NULL, "ALC Sidetone Mux"}, + + /* Right HP mixer */ + {"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"}, + {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Right HP Mixer", "Phone Bypass Switch", "Phone PGA"}, + {"Right HP Mixer", "Line Bypass Switch", "Line PGA"}, + {"Right HP Mixer", "PCM Playback Switch", "Right DAC"}, + {"Right HP Mixer", "Mic Sidetone Switch", "Mic PGA"}, + {"Right HP Mixer", NULL, "ALC Sidetone Mux"}, + + /* speaker mixer */ + {"Speaker Mixer", "PCBeep Bypass Switch", "PCBEEP"}, + {"Speaker Mixer", "Line Bypass Switch", "Line PGA"}, + {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Speaker Mixer", "Phone Bypass Switch", "Phone PGA"}, + {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"}, + + /* Phone mixer */ + {"Phone Mixer", "PCBeep Bypass Switch", "PCBEEP"}, + {"Phone Mixer", "Line Bypass Switch", "Line PGA"}, + {"Phone Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Phone Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Phone Mixer", "Mic 1 Sidetone Switch", "Mic PGA"}, + {"Phone Mixer", "Mic 2 Sidetone Switch", "Mic PGA"}, + + /* inputs */ + {"Line PGA", NULL, "LINEINL"}, + {"Line PGA", NULL, "LINEINR"}, + {"Phone PGA", NULL, "PHONE"}, + {"Mic PGA", NULL, "MIC1"}, + {"Mic PGA", NULL, "MIC2"}, + + /* microphones */ + {"Differential Mic", NULL, "MIC1"}, + {"Differential Mic", NULL, "MIC2"}, + {"Left Mic Select Source", "Mic 1", "MIC1"}, + {"Left Mic Select Source", "Mic 2", "MIC2"}, + {"Left Mic Select Source", "Stereo", "MIC1"}, + {"Left Mic Select Source", "Differential", "Differential Mic"}, + {"Right Mic Select Source", "Mic 1", "MIC1"}, + {"Right Mic Select Source", "Mic 2", "MIC2"}, + {"Right Mic Select Source", "Stereo", "MIC2"}, + {"Right Mic Select Source", "Differential", "Differential Mic"}, + + /* left capture selector */ + {"Left Capture Select", "Mic", "MIC1"}, + {"Left Capture Select", "Speaker Mixer", "Speaker Mixer"}, + {"Left Capture Select", "Line", "LINEINL"}, + {"Left Capture Select", "Headphone Mixer", "Left HP Mixer"}, + {"Left Capture Select", "Phone Mixer", "Phone Mixer"}, + {"Left Capture Select", "Phone", "PHONE"}, + + /* right capture selector */ + {"Right Capture Select", "Mic", "MIC2"}, + {"Right Capture Select", "Speaker Mixer", "Speaker Mixer"}, + {"Right Capture Select", "Line", "LINEINR"}, + {"Right Capture Select", "Headphone Mixer", "Right HP Mixer"}, + {"Right Capture Select", "Phone Mixer", "Phone Mixer"}, + {"Right Capture Select", "Phone", "PHONE"}, + + /* ALC Sidetone */ + {"ALC Sidetone Mux", "Stereo", "Left Capture Select"}, + {"ALC Sidetone Mux", "Stereo", "Right Capture Select"}, + {"ALC Sidetone Mux", "Left", "Left Capture Select"}, + {"ALC Sidetone Mux", "Right", "Right Capture Select"}, + + /* ADC's */ + {"Left ADC", NULL, "Left Capture Select"}, + {"Right ADC", NULL, "Right Capture Select"}, + + /* outputs */ + {"MONOOUT", NULL, "Phone Mixer"}, + {"HPOUTL", NULL, "Headphone PGA"}, + {"Headphone PGA", NULL, "Left HP Mixer"}, + {"HPOUTR", NULL, "Headphone PGA"}, + {"Headphone PGA", NULL, "Right HP Mixer"}, + + /* mono mixer */ + {"Mono Mixer", NULL, "Left HP Mixer"}, + {"Mono Mixer", NULL, "Right HP Mixer"}, + + /* Out3 Mux */ + {"Out3 Mux", "Left", "Left HP Mixer"}, + {"Out3 Mux", "Mono", "Phone Mixer"}, + {"Out3 Mux", "Left + Right", "Mono Mixer"}, + {"Out 3 PGA", NULL, "Out3 Mux"}, + {"OUT3", NULL, "Out 3 PGA"}, + + /* speaker Mux */ + {"Speaker Mux", "Speaker Mix", "Speaker Mixer"}, + {"Speaker Mux", "Headphone Mix", "Mono Mixer"}, + {"Speaker PGA", NULL, "Speaker Mux"}, + {"LOUT2", NULL, "Speaker PGA"}, + {"ROUT2", NULL, "Speaker PGA"}, +}; + +static int ac97_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int reg; + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x1, 0x1); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = AC97_PCM_FRONT_DAC_RATE; + else + reg = AC97_PCM_LR_ADC_RATE; + + return snd_soc_component_write(component, reg, runtime->rate); +} + +static int ac97_aux_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x1, 0x1); + snd_soc_component_update_bits(component, AC97_PCI_SID, 0x8000, 0x8000); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENODEV; + + return snd_soc_component_write(component, AC97_PCM_SURR_DAC_RATE, runtime->rate); +} + +#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000) + +static const struct snd_soc_dai_ops wm9712_dai_ops_hifi = { + .prepare = ac97_prepare, +}; + +static const struct snd_soc_dai_ops wm9712_dai_ops_aux = { + .prepare = ac97_aux_prepare, +}; + +static struct snd_soc_dai_driver wm9712_dai[] = { +{ + .name = "wm9712-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM9712_AC97_RATES, + .formats = SND_SOC_STD_AC97_FMTS,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM9712_AC97_RATES, + .formats = SND_SOC_STD_AC97_FMTS,}, + .ops = &wm9712_dai_ops_hifi, +}, +{ + .name = "wm9712-aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 1, + .rates = WM9712_AC97_RATES, + .formats = SND_SOC_STD_AC97_FMTS,}, + .ops = &wm9712_dai_ops_aux, +} +}; + +static int wm9712_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + snd_soc_component_write(component, AC97_POWERDOWN, 0x0000); + break; + case SND_SOC_BIAS_OFF: + /* disable everything including AC link */ + snd_soc_component_write(component, AC97_EXTENDED_MSTATUS, 0xffff); + snd_soc_component_write(component, AC97_POWERDOWN, 0xffff); + break; + } + return 0; +} + +static int wm9712_soc_resume(struct snd_soc_component *component) +{ + struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_ac97_reset(wm9712->ac97, true, WM9712_VENDOR_ID, + WM9712_VENDOR_ID_MASK); + if (ret < 0) + return ret; + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + if (ret == 0) + snd_soc_component_cache_sync(component); + + return ret; +} + +static int wm9712_soc_probe(struct snd_soc_component *component) +{ + struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component); + struct regmap *regmap; + + if (wm9712->mfd_pdata) { + wm9712->ac97 = wm9712->mfd_pdata->ac97; + regmap = wm9712->mfd_pdata->regmap; + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { + int ret; + + wm9712->ac97 = snd_soc_new_ac97_component(component, WM9712_VENDOR_ID, + WM9712_VENDOR_ID_MASK); + if (IS_ERR(wm9712->ac97)) { + ret = PTR_ERR(wm9712->ac97); + dev_err(component->dev, + "Failed to register AC97 codec: %d\n", ret); + return ret; + } + + regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config); + if (IS_ERR(regmap)) { + snd_soc_free_ac97_component(wm9712->ac97); + return PTR_ERR(regmap); + } + } else { + return -ENXIO; + } + + snd_soc_component_init_regmap(component, regmap); + + /* set alc mux to none */ + snd_soc_component_update_bits(component, AC97_VIDEO, 0x3000, 0x3000); + + return 0; +} + +static void wm9712_soc_remove(struct snd_soc_component *component) +{ + struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component); + + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9712->mfd_pdata) { + snd_soc_component_exit_regmap(component); + snd_soc_free_ac97_component(wm9712->ac97); + } +} + +static const struct snd_soc_component_driver soc_component_dev_wm9712 = { + .probe = wm9712_soc_probe, + .remove = wm9712_soc_remove, + .resume = wm9712_soc_resume, + .set_bias_level = wm9712_set_bias_level, + .controls = wm9712_snd_ac97_controls, + .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls), + .dapm_widgets = wm9712_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets), + .dapm_routes = wm9712_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm9712_probe(struct platform_device *pdev) +{ + struct wm9712_priv *wm9712; + + wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL); + if (wm9712 == NULL) + return -ENOMEM; + + mutex_init(&wm9712->lock); + + wm9712->mfd_pdata = dev_get_platdata(&pdev->dev); + platform_set_drvdata(pdev, wm9712); + + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai)); +} + +static struct platform_driver wm9712_component_driver = { + .driver = { + .name = "wm9712-codec", + }, + + .probe = wm9712_probe, +}; + +module_platform_driver(wm9712_component_driver); + +MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c new file mode 100644 index 0000000000..64b69316e4 --- /dev/null +++ b/sound/soc/codecs/wm9713.c @@ -0,0 +1,1291 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * wm9713.c -- ALSA Soc WM9713 codec support + * + * Copyright 2006-10 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * + * Features:- + * + * o Support for AC97 Codec, Voice DAC and Aux DAC + * o Support for DAPM + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm9713.h" + +#define WM9713_VENDOR_ID 0x574d4c13 +#define WM9713_VENDOR_ID_MASK 0xffffffff + +struct wm9713_priv { + struct snd_ac97 *ac97; + u32 pll_in; /* PLL input frequency */ + unsigned int hp_mixer[2]; + struct mutex lock; + struct wm97xx_platform_data *mfd_pdata; +}; + +#define HPL_MIXER 0 +#define HPR_MIXER 1 + +static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; +static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; +static const char *wm9713_rec_src[] = + {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker", + "Mono Out", "Zh"}; +static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"}; +static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"}; +static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv"}; +static const char *wm9713_spk_pga[] = + {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid", + "Speaker Vmid", "Inv Vmid"}; +static const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone", + "Headphone Vmid"}; +static const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"}; +static const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"}; +static const char *wm9713_dac_inv[] = + {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone", + "Headphone Mono", "NC", "Vmid"}; +static const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"}; +static const char *wm9713_ng_type[] = {"Constant Gain", "Mute"}; +static const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"}; +static const char *wm9713_micb_select[] = {"MPB", "MPA"}; + +static const struct soc_enum wm9713_enum[] = { +SOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), /* record mux mono 2 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src), /* record mux left 3 */ +SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src), /* record mux right 4*/ +SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */ +SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */ +SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */ +SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */ +SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */ +SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */ +SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */ +}; + +static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(misc_tlv, -1500, 300, 0); +static const DECLARE_TLV_DB_RANGE(mic_tlv, + 0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0), + 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0) +); + +static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = { +SOC_DOUBLE_TLV("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1, out_tlv), +SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1), +SOC_DOUBLE_TLV("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1, + out_tlv), +SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1), +SOC_DOUBLE_TLV("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1, main_tlv), +SOC_DOUBLE_TLV("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1, main_tlv), +SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv), +SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv), +SOC_SINGLE_TLV("Mic 1 Preamp Volume", AC97_3D_CONTROL, 10, 3, 0, mic_tlv), +SOC_SINGLE_TLV("Mic 2 Preamp Volume", AC97_3D_CONTROL, 12, 3, 0, mic_tlv), + +SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0), +SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1), + +SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1), +SOC_ENUM("Capture Volume Steps", wm9713_enum[5]), +SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0), +SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0), + +SOC_SINGLE_TLV("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1, misc_tlv), +SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0), +SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0), + +SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), +SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), +SOC_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0), +SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0), +SOC_ENUM("ALC Function", wm9713_enum[6]), +SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0), +SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0), +SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0), +SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0), +SOC_ENUM("ALC NG Type", wm9713_enum[17]), +SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0), + +SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0), +SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0), + +SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1), +SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0), +SOC_SINGLE_TLV("Out4 Playback Volume", AC97_MASTER_MONO, 8, 31, 1, out_tlv), + +SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1), +SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0), +SOC_SINGLE_TLV("Out3 Playback Volume", AC97_MASTER_MONO, 0, 31, 1, out_tlv), + +SOC_SINGLE_TLV("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1, main_tlv), +SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1), +SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0), +SOC_SINGLE_TLV("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1, out_tlv), + +SOC_SINGLE_TLV("Headphone Mixer Beep Playback Volume", AC97_AUX, 12, 7, 1, + misc_tlv), +SOC_SINGLE_TLV("Speaker Mixer Beep Playback Volume", AC97_AUX, 8, 7, 1, + misc_tlv), +SOC_SINGLE_TLV("Mono Mixer Beep Playback Volume", AC97_AUX, 4, 7, 1, misc_tlv), + +SOC_SINGLE_TLV("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1, + misc_tlv), +SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1), +SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1), + +SOC_SINGLE_TLV("Headphone Mixer Aux Playback Volume", AC97_REC_SEL, 12, 7, 1, + misc_tlv), + +SOC_SINGLE_TLV("Speaker Mixer Voice Playback Volume", AC97_PCM, 8, 7, 1, + misc_tlv), +SOC_SINGLE_TLV("Speaker Mixer Aux Playback Volume", AC97_REC_SEL, 8, 7, 1, + misc_tlv), + +SOC_SINGLE_TLV("Mono Mixer Voice Playback Volume", AC97_PCM, 4, 7, 1, + misc_tlv), +SOC_SINGLE_TLV("Mono Mixer Aux Playback Volume", AC97_REC_SEL, 4, 7, 1, + misc_tlv), + +SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1), +SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1), + +SOC_ENUM("Bass Control", wm9713_enum[16]), +SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1), +SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1), +SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0), +SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1), +SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1), + +SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0), +SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0), +SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1), +}; + +static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + if (WARN_ON(event != SND_SOC_DAPM_PRE_PMD)) + return -EINVAL; + + /* Gracefully shut down the voice interface. */ + snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0f00, 0x0200); + schedule_timeout_interruptible(msecs_to_jiffies(1)); + snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0f00, 0x0f00); + snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x1000, 0x1000); + + return 0; +} + +static const unsigned int wm9713_mixer_mute_regs[] = { + AC97_PC_BEEP, + AC97_MASTER_TONE, + AC97_PHONE, + AC97_REC_SEL, + AC97_PCM, + AC97_AUX, +}; + +/* We have to create a fake left and right HP mixers because + * the codec only has a single control that is shared by both channels. + * This makes it impossible to determine the audio path using the current + * register map, thus we add a new (virtual) register to help determine the + * audio route within the device. + */ +static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); + unsigned int val = ucontrol->value.integer.value[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mixer, mask, shift, old; + struct snd_soc_dapm_update update = {}; + bool change; + + mixer = mc->shift >> 8; + shift = mc->shift & 0xff; + mask = (1 << shift); + + mutex_lock(&wm9713->lock); + old = wm9713->hp_mixer[mixer]; + if (ucontrol->value.integer.value[0]) + wm9713->hp_mixer[mixer] |= mask; + else + wm9713->hp_mixer[mixer] &= ~mask; + + change = old != wm9713->hp_mixer[mixer]; + if (change) { + update.kcontrol = kcontrol; + update.reg = wm9713_mixer_mute_regs[shift]; + update.mask = 0x8000; + if ((wm9713->hp_mixer[0] & mask) || + (wm9713->hp_mixer[1] & mask)) + update.val = 0x0; + else + update.val = 0x8000; + + snd_soc_dapm_mixer_update_power(dapm, kcontrol, val, + &update); + } + + mutex_unlock(&wm9713->lock); + + return change; +} + +static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); + struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mixer, shift; + + mixer = mc->shift >> 8; + shift = mc->shift & 0xff; + + ucontrol->value.integer.value[0] = + (wm9713->hp_mixer[mixer] >> shift) & 1; + + return 0; +} + +#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \ + .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \ + xshift, xmixer, 1, 0, 0) \ +} + +/* Left Headphone Mixers */ +static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = { +WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5), +WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4), +WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3), +WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2), +WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1), +WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0), +}; + +/* Right Headphone Mixers */ +static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = { +WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5), +WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4), +WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3), +WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2), +WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1), +WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0), +}; + +/* headphone capture mux */ +static const struct snd_kcontrol_new wm9713_hp_rec_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[1]); + +/* headphone mic mux */ +static const struct snd_kcontrol_new wm9713_hp_mic_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[0]); + +/* Speaker Mixer */ +static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = { +SOC_DAPM_SINGLE("Beep Playback Switch", AC97_AUX, 11, 1, 1), +SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1), +SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1), +SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1), +SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1), +SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("Beep Playback Switch", AC97_AUX, 7, 1, 1), +SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1), +SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1), +SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1), +SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1), +SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1), +SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1), +SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1), +}; + +/* mono mic mux */ +static const struct snd_kcontrol_new wm9713_mono_mic_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[2]); + +/* mono output mux */ +static const struct snd_kcontrol_new wm9713_mono_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[7]); + +/* speaker left output mux */ +static const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[8]); + +/* speaker right output mux */ +static const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[9]); + +/* headphone left output mux */ +static const struct snd_kcontrol_new wm9713_hpl_out_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[10]); + +/* headphone right output mux */ +static const struct snd_kcontrol_new wm9713_hpr_out_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[11]); + +/* Out3 mux */ +static const struct snd_kcontrol_new wm9713_out3_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[12]); + +/* Out4 mux */ +static const struct snd_kcontrol_new wm9713_out4_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[13]); + +/* DAC inv mux 1 */ +static const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[14]); + +/* DAC inv mux 2 */ +static const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[15]); + +/* Capture source left */ +static const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[3]); + +/* Capture source right */ +static const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[4]); + +/* mic source */ +static const struct snd_kcontrol_new wm9713_mic_sel_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[18]); + +/* mic source B virtual control */ +static const struct snd_kcontrol_new wm9713_micb_sel_mux_controls = +SOC_DAPM_ENUM("Route", wm9713_enum[19]); + +static const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = { +SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_rec_mux_controls), +SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_mic_mux_controls), +SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0, + &wm9713_mono_mic_mux_controls), +SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_mono_mux_controls), +SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_spkl_mux_controls), +SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hp_spkr_mux_controls), +SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hpl_out_mux_controls), +SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0, + &wm9713_hpr_out_mux_controls), +SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0, + &wm9713_out3_mux_controls), +SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0, + &wm9713_out4_mux_controls), +SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0, + &wm9713_dac_inv1_mux_controls), +SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0, + &wm9713_dac_inv2_mux_controls), +SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0, + &wm9713_rec_srcl_mux_controls), +SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0, + &wm9713_rec_srcr_mux_controls), +SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0, + &wm9713_mic_sel_mux_controls), +SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0, + &wm9713_micb_sel_mux_controls), +SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1, + &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)), +SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1, + &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)), +SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1, + &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)), +SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1, + &wm9713_speaker_mixer_controls[0], + ARRAY_SIZE(wm9713_speaker_mixer_controls)), +SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1), +SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1), +SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_DAC_E("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1, + wm9713_voice_shutdown, SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1), +SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0), +SND_SOC_DAPM_ADC("Left HiFi ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Right HiFi ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Left Voice ADC", "Left Voice Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Right Voice ADC", "Right Voice Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0), +SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0), +SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0), +SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0), +SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0), +SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1), +SND_SOC_DAPM_OUTPUT("MONO"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_OUTPUT("SPKL"), +SND_SOC_DAPM_OUTPUT("SPKR"), +SND_SOC_DAPM_OUTPUT("OUT3"), +SND_SOC_DAPM_OUTPUT("OUT4"), +SND_SOC_DAPM_INPUT("LINEL"), +SND_SOC_DAPM_INPUT("LINER"), +SND_SOC_DAPM_INPUT("MONOIN"), +SND_SOC_DAPM_INPUT("PCBEEP"), +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2A"), +SND_SOC_DAPM_INPUT("MIC2B"), +SND_SOC_DAPM_VMID("VMID"), +}; + +static const struct snd_soc_dapm_route wm9713_audio_map[] = { + /* left HP mixer */ + {"Left HP Mixer", "Beep Playback Switch", "PCBEEP"}, + {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"}, + {"Left HP Mixer", "PCM Playback Switch", "Left DAC"}, + {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"}, + {"Left HP Mixer", NULL, "Capture Headphone Mux"}, + + /* right HP mixer */ + {"Right HP Mixer", "Beep Playback Switch", "PCBEEP"}, + {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"}, + {"Right HP Mixer", "PCM Playback Switch", "Right DAC"}, + {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"}, + {"Right HP Mixer", NULL, "Capture Headphone Mux"}, + + /* virtual mixer - mixes left & right channels for spk and mono */ + {"AC97 Mixer", NULL, "Left DAC"}, + {"AC97 Mixer", NULL, "Right DAC"}, + {"Line Mixer", NULL, "Right Line In"}, + {"Line Mixer", NULL, "Left Line In"}, + {"HP Mixer", NULL, "Left HP Mixer"}, + {"HP Mixer", NULL, "Right HP Mixer"}, + {"Capture Mixer", NULL, "Left Capture Source"}, + {"Capture Mixer", NULL, "Right Capture Source"}, + + /* speaker mixer */ + {"Speaker Mixer", "Beep Playback Switch", "PCBEEP"}, + {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"}, + {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"}, + + /* mono mixer */ + {"Mono Mixer", "Beep Playback Switch", "PCBEEP"}, + {"Mono Mixer", "Voice Playback Switch", "Voice DAC"}, + {"Mono Mixer", "Aux Playback Switch", "Aux DAC"}, + {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"}, + {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"}, + {"Mono Mixer", "Mic 1 Sidetone Switch", "Mic A PGA"}, + {"Mono Mixer", "Mic 2 Sidetone Switch", "Mic B PGA"}, + {"Mono Mixer", NULL, "Capture Mono Mux"}, + + /* DAC inv mux 1 */ + {"DAC Inv Mux 1", "Mono", "Mono Mixer"}, + {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"}, + {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"}, + {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"}, + {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"}, + + /* DAC inv mux 2 */ + {"DAC Inv Mux 2", "Mono", "Mono Mixer"}, + {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"}, + {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"}, + {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"}, + {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"}, + + /* headphone left mux */ + {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"}, + + /* headphone right mux */ + {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"}, + + /* speaker left mux */ + {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"}, + {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"}, + {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"}, + + /* speaker right mux */ + {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"}, + {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"}, + {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"}, + + /* mono mux */ + {"Mono Out Mux", "Mono", "Mono Mixer"}, + {"Mono Out Mux", "Inv", "DAC Inv Mux 1"}, + + /* out 3 mux */ + {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"}, + + /* out 4 mux */ + {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"}, + + /* output pga */ + {"HPL", NULL, "Left Headphone"}, + {"Left Headphone", NULL, "Left Headphone Out Mux"}, + {"HPR", NULL, "Right Headphone"}, + {"Right Headphone", NULL, "Right Headphone Out Mux"}, + {"OUT3", NULL, "Out 3"}, + {"Out 3", NULL, "Out 3 Mux"}, + {"OUT4", NULL, "Out 4"}, + {"Out 4", NULL, "Out 4 Mux"}, + {"SPKL", NULL, "Left Speaker"}, + {"Left Speaker", NULL, "Left Speaker Out Mux"}, + {"SPKR", NULL, "Right Speaker"}, + {"Right Speaker", NULL, "Right Speaker Out Mux"}, + {"MONO", NULL, "Mono Out"}, + {"Mono Out", NULL, "Mono Out Mux"}, + + /* input pga */ + {"Left Line In", NULL, "LINEL"}, + {"Right Line In", NULL, "LINER"}, + {"Mono In", NULL, "MONOIN"}, + {"Mic A PGA", NULL, "Mic A Pre Amp"}, + {"Mic B PGA", NULL, "Mic B Pre Amp"}, + + /* left capture select */ + {"Left Capture Source", "Mic 1", "Mic A Pre Amp"}, + {"Left Capture Source", "Mic 2", "Mic B Pre Amp"}, + {"Left Capture Source", "Line", "LINEL"}, + {"Left Capture Source", "Mono In", "MONOIN"}, + {"Left Capture Source", "Headphone", "Left HP Mixer"}, + {"Left Capture Source", "Speaker", "Speaker Mixer"}, + {"Left Capture Source", "Mono Out", "Mono Mixer"}, + + /* right capture select */ + {"Right Capture Source", "Mic 1", "Mic A Pre Amp"}, + {"Right Capture Source", "Mic 2", "Mic B Pre Amp"}, + {"Right Capture Source", "Line", "LINER"}, + {"Right Capture Source", "Mono In", "MONOIN"}, + {"Right Capture Source", "Headphone", "Right HP Mixer"}, + {"Right Capture Source", "Speaker", "Speaker Mixer"}, + {"Right Capture Source", "Mono Out", "Mono Mixer"}, + + /* left ADC */ + {"Left ADC", NULL, "Left Capture Source"}, + {"Left Voice ADC", NULL, "Left ADC"}, + {"Left HiFi ADC", NULL, "Left ADC"}, + + /* right ADC */ + {"Right ADC", NULL, "Right Capture Source"}, + {"Right Voice ADC", NULL, "Right ADC"}, + {"Right HiFi ADC", NULL, "Right ADC"}, + + /* mic */ + {"Mic A Pre Amp", NULL, "Mic A Source"}, + {"Mic A Source", "Mic 1", "MIC1"}, + {"Mic A Source", "Mic 2 A", "MIC2A"}, + {"Mic A Source", "Mic 2 B", "Mic B Source"}, + {"Mic B Pre Amp", "MPB", "Mic B Source"}, + {"Mic B Source", NULL, "MIC2B"}, + + /* headphone capture */ + {"Capture Headphone Mux", "Stereo", "Capture Mixer"}, + {"Capture Headphone Mux", "Left", "Left Capture Source"}, + {"Capture Headphone Mux", "Right", "Right Capture Source"}, + + /* mono capture */ + {"Capture Mono Mux", "Stereo", "Capture Mixer"}, + {"Capture Mono Mux", "Left", "Left Capture Source"}, + {"Capture Mono Mux", "Right", "Right Capture Source"}, +}; + +static bool wm9713_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_RESET ... AC97_PCM_SURR_DAC_RATE: + case AC97_PCM_LR_ADC_RATE: + case AC97_CENTER_LFE_MASTER: + case AC97_SPDIF ... AC97_LINE1_LEVEL: + case AC97_GPIO_CFG ... 0x5c: + case AC97_CODEC_CLASS_REV ... AC97_PCI_SID: + case 0x74 ... AC97_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool wm9713_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return false; + default: + return wm9713_readable_reg(dev, reg); + } +} + +static const struct reg_default wm9713_reg_defaults[] = { + { 0x02, 0x8080 }, /* Speaker Output Volume */ + { 0x04, 0x8080 }, /* Headphone Output Volume */ + { 0x06, 0x8080 }, /* Out3/OUT4 Volume */ + { 0x08, 0xc880 }, /* Mono Volume */ + { 0x0a, 0xe808 }, /* LINEIN Volume */ + { 0x0c, 0xe808 }, /* DAC PGA Volume */ + { 0x0e, 0x0808 }, /* MIC PGA Volume */ + { 0x10, 0x00da }, /* MIC Routing Control */ + { 0x12, 0x8000 }, /* Record PGA Volume */ + { 0x14, 0xd600 }, /* Record Routing */ + { 0x16, 0xaaa0 }, /* PCBEEP Volume */ + { 0x18, 0xaaa0 }, /* VxDAC Volume */ + { 0x1a, 0xaaa0 }, /* AUXDAC Volume */ + { 0x1c, 0x0000 }, /* Output PGA Mux */ + { 0x1e, 0x0000 }, /* DAC 3D control */ + { 0x20, 0x0f0f }, /* DAC Tone Control*/ + { 0x22, 0x0040 }, /* MIC Input Select & Bias */ + { 0x24, 0x0000 }, /* Output Volume Mapping & Jack */ + { 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/ + { 0x28, 0x0405 }, /* Extended Audio ID */ + { 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */ + { 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */ + { 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */ + { 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */ + { 0x36, 0x4523 }, /* PCM codec control */ + { 0x3a, 0x2000 }, /* SPDIF control */ + { 0x3c, 0xfdff }, /* Powerdown 1 */ + { 0x3e, 0xffff }, /* Powerdown 2 */ + { 0x40, 0x0000 }, /* General Purpose */ + { 0x42, 0x0000 }, /* Fast Power-Up Control */ + { 0x44, 0x0080 }, /* MCLK/PLL Control */ + { 0x46, 0x0000 }, /* MCLK/PLL Control */ + { 0x4c, 0xfffe }, /* GPIO Pin Configuration */ + { 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */ + { 0x50, 0x0000 }, /* GPIO Pin Sticky */ + { 0x52, 0x0000 }, /* GPIO Pin Wake-Up */ + /* GPIO Pin Status */ + { 0x56, 0xfffe }, /* GPIO Pin Sharing */ + { 0x58, 0x4000 }, /* GPIO PullUp/PullDown */ + { 0x5a, 0x0000 }, /* Additional Functions 1 */ + { 0x5c, 0x0000 }, /* Additional Functions 2 */ + { 0x60, 0xb032 }, /* ALC Control */ + { 0x62, 0x3e00 }, /* ALC / Noise Gate Control */ + { 0x64, 0x0000 }, /* AUXDAC input control */ + { 0x74, 0x0000 }, /* Digitiser Reg 1 */ + { 0x76, 0x0006 }, /* Digitiser Reg 2 */ + { 0x78, 0x0001 }, /* Digitiser Reg 3 */ + { 0x7a, 0x0000 }, /* Digitiser Read Back */ +}; + +static const struct regmap_config wm9713_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_MAPLE, + + .reg_defaults = wm9713_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm9713_readable_reg, + .writeable_reg = wm9713_writeable_reg, +}; + +/* PLL divisors */ +struct _pll_div { + u32 divsel:1; + u32 divctl:1; + u32 lf:1; + u32 n:4; + u32 k:24; +}; + +/* The size in bits of the PLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 22) * 10) + +static void pll_factors(struct snd_soc_component *component, + struct _pll_div *pll_div, unsigned int source) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod, target; + + /* The PLL output is always 98.304MHz. */ + target = 98304000; + + /* If the input frequency is over 14.4MHz then scale it down. */ + if (source > 14400000) { + source >>= 1; + pll_div->divsel = 1; + + if (source > 14400000) { + source >>= 1; + pll_div->divctl = 1; + } else + pll_div->divctl = 0; + + } else { + pll_div->divsel = 0; + pll_div->divctl = 0; + } + + /* Low frequency sources require an additional divide in the + * loop. + */ + if (source < 8192000) { + pll_div->lf = 1; + target >>= 2; + } else + pll_div->lf = 0; + + Ndiv = target / source; + if ((Ndiv < 5) || (Ndiv > 12)) + dev_warn(component->dev, + "WM9713 PLL N value %u out of recommended range!\n", + Ndiv); + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; +} + +/* + * Please note that changing the PLL input frequency may require + * resynchronisation with the AC97 controller. + */ +static int wm9713_set_pll(struct snd_soc_component *component, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); + u16 reg, reg2; + struct _pll_div pll_div; + + /* turn PLL off ? */ + if (freq_in == 0) { + /* disable PLL power and select ext source */ + snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0080, 0x0080); + snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x0200, 0x0200); + wm9713->pll_in = 0; + return 0; + } + + pll_factors(component, &pll_div, freq_in); + + if (pll_div.k == 0) { + reg = (pll_div.n << 12) | (pll_div.lf << 11) | + (pll_div.divsel << 9) | (pll_div.divctl << 8); + snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); + } else { + /* write the fractional k to the reg 0x46 pages */ + reg2 = (pll_div.n << 12) | (pll_div.lf << 11) | (1 << 10) | + (pll_div.divsel << 9) | (pll_div.divctl << 8); + + /* K [21:20] */ + reg = reg2 | (0x5 << 4) | (pll_div.k >> 20); + snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); + + /* K [19:16] */ + reg = reg2 | (0x4 << 4) | ((pll_div.k >> 16) & 0xf); + snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); + + /* K [15:12] */ + reg = reg2 | (0x3 << 4) | ((pll_div.k >> 12) & 0xf); + snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); + + /* K [11:8] */ + reg = reg2 | (0x2 << 4) | ((pll_div.k >> 8) & 0xf); + snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); + + /* K [7:4] */ + reg = reg2 | (0x1 << 4) | ((pll_div.k >> 4) & 0xf); + snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); + + reg = reg2 | (0x0 << 4) | (pll_div.k & 0xf); /* K [3:0] */ + snd_soc_component_write(component, AC97_LINE1_LEVEL, reg); + } + + /* turn PLL on and select as source */ + snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x0200, 0x0000); + snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0080, 0x0000); + wm9713->pll_in = freq_in; + + /* wait 10ms AC97 link frames for the link to stabilise */ + schedule_timeout_interruptible(msecs_to_jiffies(10)); + return 0; +} + +static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = codec_dai->component; + return wm9713_set_pll(component, pll_id, freq_in, freq_out); +} + +/* + * Tristate the PCM DAI lines, tristate can be disabled by calling + * wm9713_set_dai_fmt() + */ +static int wm9713_set_dai_tristate(struct snd_soc_dai *codec_dai, + int tristate) +{ + struct snd_soc_component *component = codec_dai->component; + + if (tristate) + snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, + 0x6000, 0x0000); + + return 0; +} + +/* + * Configure WM9713 clock dividers. + * Voice DAC needs 256 FS + */ +static int wm9713_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_component *component = codec_dai->component; + + switch (div_id) { + case WM9713_PCMCLK_DIV: + snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0f00, div); + break; + case WM9713_CLKA_MULT: + snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0002, div); + break; + case WM9713_CLKB_MULT: + snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x0004, div); + break; + case WM9713_HIFI_DIV: + snd_soc_component_update_bits(component, AC97_HANDSET_RATE, 0x7000, div); + break; + case WM9713_PCMBCLK_DIV: + snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, 0x0e00, div); + break; + case WM9713_PCMCLK_PLL_DIV: + snd_soc_component_update_bits(component, AC97_LINE1_LEVEL, + 0x007f, div | 0x60); + break; + case WM9713_HIFI_PLL_DIV: + snd_soc_component_update_bits(component, AC97_LINE1_LEVEL, + 0x007f, div | 0x70); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + u16 gpio = snd_soc_component_read(component, AC97_GPIO_CFG) & 0xffc5; + u16 reg = 0x8000; + + /* clock masters */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + reg |= 0x4000; + gpio |= 0x0010; + break; + case SND_SOC_DAIFMT_CBM_CFS: + reg |= 0x6000; + gpio |= 0x0018; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg |= 0x2000; + gpio |= 0x001a; + break; + case SND_SOC_DAIFMT_CBS_CFM: + gpio |= 0x0012; + break; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + reg |= 0x00c0; + break; + case SND_SOC_DAIFMT_IB_NF: + reg |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + reg |= 0x0040; + break; + } + + /* DAI format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + reg |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + reg |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + reg |= 0x0043; + break; + } + + snd_soc_component_write(component, AC97_GPIO_CFG, gpio); + snd_soc_component_write(component, AC97_CENTER_LFE_MASTER, reg); + return 0; +} + +static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + + /* enable PCM interface in master mode */ + switch (params_width(params)) { + case 16: + break; + case 20: + snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, + 0x000c, 0x0004); + break; + case 24: + snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, + 0x000c, 0x0008); + break; + case 32: + snd_soc_component_update_bits(component, AC97_CENTER_LFE_MASTER, + 0x000c, 0x000c); + break; + } + return 0; +} + +static int ac97_hifi_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct snd_pcm_runtime *runtime = substream->runtime; + int reg; + + snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x0001, 0x0001); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = AC97_PCM_FRONT_DAC_RATE; + else + reg = AC97_PCM_LR_ADC_RATE; + + return snd_soc_component_write(component, reg, runtime->rate); +} + +static int ac97_aux_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x0001, 0x0001); + snd_soc_component_update_bits(component, AC97_PCI_SID, 0x8000, 0x8000); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -ENODEV; + + return snd_soc_component_write(component, AC97_PCM_SURR_DAC_RATE, runtime->rate); +} + +#define WM9713_RATES (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +#define WM9713_PCM_RATES (SNDRV_PCM_RATE_8000 | \ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +#define WM9713_PCM_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops wm9713_dai_ops_hifi = { + .prepare = ac97_hifi_prepare, + .set_clkdiv = wm9713_set_dai_clkdiv, + .set_pll = wm9713_set_dai_pll, +}; + +static const struct snd_soc_dai_ops wm9713_dai_ops_aux = { + .prepare = ac97_aux_prepare, + .set_clkdiv = wm9713_set_dai_clkdiv, + .set_pll = wm9713_set_dai_pll, +}; + +static const struct snd_soc_dai_ops wm9713_dai_ops_voice = { + .hw_params = wm9713_pcm_hw_params, + .set_clkdiv = wm9713_set_dai_clkdiv, + .set_pll = wm9713_set_dai_pll, + .set_fmt = wm9713_set_dai_fmt, + .set_tristate = wm9713_set_dai_tristate, +}; + +static struct snd_soc_dai_driver wm9713_dai[] = { +{ + .name = "wm9713-hifi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM9713_RATES, + .formats = SND_SOC_STD_AC97_FMTS,}, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM9713_RATES, + .formats = SND_SOC_STD_AC97_FMTS,}, + .ops = &wm9713_dai_ops_hifi, + }, + { + .name = "wm9713-aux", + .playback = { + .stream_name = "Aux Playback", + .channels_min = 1, + .channels_max = 1, + .rates = WM9713_RATES, + .formats = SND_SOC_STD_AC97_FMTS,}, + .ops = &wm9713_dai_ops_aux, + }, + { + .name = "wm9713-voice", + .playback = { + .stream_name = "Voice Playback", + .channels_min = 1, + .channels_max = 1, + .rates = WM9713_PCM_RATES, + .formats = WM9713_PCM_FORMATS,}, + .capture = { + .stream_name = "Voice Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM9713_PCM_RATES, + .formats = WM9713_PCM_FORMATS,}, + .ops = &wm9713_dai_ops_voice, + .symmetric_rate = 1, + }, +}; + +static int wm9713_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + /* enable thermal shutdown */ + snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0xe400, 0x0000); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* enable master bias and vmid */ + snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0xc400, 0x0000); + snd_soc_component_write(component, AC97_POWERDOWN, 0x0000); + break; + case SND_SOC_BIAS_OFF: + /* disable everything including AC link */ + snd_soc_component_write(component, AC97_EXTENDED_MID, 0xffff); + snd_soc_component_write(component, AC97_EXTENDED_MSTATUS, 0xffff); + snd_soc_component_write(component, AC97_POWERDOWN, 0xffff); + break; + } + return 0; +} + +static int wm9713_soc_suspend(struct snd_soc_component *component) +{ + /* Disable everything except touchpanel - that will be handled + * by the touch driver and left disabled if touch is not in + * use. */ + snd_soc_component_update_bits(component, AC97_EXTENDED_MID, 0x7fff, + 0x7fff); + snd_soc_component_write(component, AC97_EXTENDED_MSTATUS, 0xffff); + snd_soc_component_write(component, AC97_POWERDOWN, 0x6f00); + snd_soc_component_write(component, AC97_POWERDOWN, 0xffff); + + return 0; +} + +static int wm9713_soc_resume(struct snd_soc_component *component) +{ + struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_ac97_reset(wm9713->ac97, true, WM9713_VENDOR_ID, + WM9713_VENDOR_ID_MASK); + if (ret < 0) + return ret; + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + /* do we need to re-start the PLL ? */ + if (wm9713->pll_in) + wm9713_set_pll(component, 0, wm9713->pll_in, 0); + + /* only synchronise the codec if warm reset failed */ + if (ret == 0) { + regcache_mark_dirty(component->regmap); + snd_soc_component_cache_sync(component); + } + + return ret; +} + +static int wm9713_soc_probe(struct snd_soc_component *component) +{ + struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); + struct regmap *regmap = NULL; + + if (wm9713->mfd_pdata) { + wm9713->ac97 = wm9713->mfd_pdata->ac97; + regmap = wm9713->mfd_pdata->regmap; + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { + wm9713->ac97 = snd_soc_new_ac97_component(component, WM9713_VENDOR_ID, + WM9713_VENDOR_ID_MASK); + if (IS_ERR(wm9713->ac97)) + return PTR_ERR(wm9713->ac97); + regmap = regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); + if (IS_ERR(regmap)) { + snd_soc_free_ac97_component(wm9713->ac97); + return PTR_ERR(regmap); + } + } else { + return -ENXIO; + } + + snd_soc_component_init_regmap(component, regmap); + + /* unmute the adc - move to kcontrol */ + snd_soc_component_update_bits(component, AC97_CD, 0x7fff, 0x0000); + + return 0; +} + +static void wm9713_soc_remove(struct snd_soc_component *component) +{ + struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); + + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9713->mfd_pdata) { + snd_soc_component_exit_regmap(component); + snd_soc_free_ac97_component(wm9713->ac97); + } +} + +static const struct snd_soc_component_driver soc_component_dev_wm9713 = { + .probe = wm9713_soc_probe, + .remove = wm9713_soc_remove, + .suspend = wm9713_soc_suspend, + .resume = wm9713_soc_resume, + .set_bias_level = wm9713_set_bias_level, + .controls = wm9713_snd_ac97_controls, + .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls), + .dapm_widgets = wm9713_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets), + .dapm_routes = wm9713_audio_map, + .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int wm9713_probe(struct platform_device *pdev) +{ + struct wm9713_priv *wm9713; + + wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL); + if (wm9713 == NULL) + return -ENOMEM; + + mutex_init(&wm9713->lock); + + wm9713->mfd_pdata = dev_get_platdata(&pdev->dev); + platform_set_drvdata(pdev, wm9713); + + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai)); +} + +static struct platform_driver wm9713_codec_driver = { + .driver = { + .name = "wm9713-codec", + }, + + .probe = wm9713_probe, +}; + +module_platform_driver(wm9713_codec_driver); + +MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver"); +MODULE_AUTHOR("Liam Girdwood"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm9713.h b/sound/soc/codecs/wm9713.h new file mode 100644 index 0000000000..f0800dcca9 --- /dev/null +++ b/sound/soc/codecs/wm9713.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * wm9713.h -- WM9713 Soc Audio driver + */ + +#ifndef _WM9713_H +#define _WM9713_H + +/* clock inputs */ +#define WM9713_CLKA_PIN 0 +#define WM9713_CLKB_PIN 1 + +/* clock divider ID's */ +#define WM9713_PCMCLK_DIV 0 +#define WM9713_CLKA_MULT 1 +#define WM9713_CLKB_MULT 2 +#define WM9713_HIFI_DIV 3 +#define WM9713_PCMBCLK_DIV 4 +#define WM9713_PCMCLK_PLL_DIV 5 +#define WM9713_HIFI_PLL_DIV 6 + +/* Calculate the appropriate bit mask for the external PCM clock divider */ +#define WM9713_PCMDIV(x) ((x - 1) << 8) + +/* Calculate the appropriate bit mask for the external HiFi clock divider */ +#define WM9713_HIFIDIV(x) ((x - 1) << 12) + +/* MCLK clock mulitipliers */ +#define WM9713_CLKA_X1 (0 << 1) +#define WM9713_CLKA_X2 (1 << 1) +#define WM9713_CLKB_X1 (0 << 2) +#define WM9713_CLKB_X2 (1 << 2) + +/* MCLK clock MUX */ +#define WM9713_CLK_MUX_A (0 << 0) +#define WM9713_CLK_MUX_B (1 << 0) + +/* Voice DAI BCLK divider */ +#define WM9713_PCMBCLK_DIV_1 (0 << 9) +#define WM9713_PCMBCLK_DIV_2 (1 << 9) +#define WM9713_PCMBCLK_DIV_4 (2 << 9) +#define WM9713_PCMBCLK_DIV_8 (3 << 9) +#define WM9713_PCMBCLK_DIV_16 (4 << 9) + +#endif diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c new file mode 100644 index 0000000000..cb654f1b09 --- /dev/null +++ b/sound/soc/codecs/wm_adsp.c @@ -0,0 +1,2107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm_adsp.c -- Wolfson ADSP support + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm_adsp.h" + +#define adsp_crit(_dsp, fmt, ...) \ + dev_crit(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) +#define adsp_err(_dsp, fmt, ...) \ + dev_err(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) +#define adsp_warn(_dsp, fmt, ...) \ + dev_warn(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) +#define adsp_info(_dsp, fmt, ...) \ + dev_info(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) +#define adsp_dbg(_dsp, fmt, ...) \ + dev_dbg(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) + +#define compr_err(_obj, fmt, ...) \ + adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ + ##__VA_ARGS__) +#define compr_dbg(_obj, fmt, ...) \ + adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ + ##__VA_ARGS__) + +#define ADSP_MAX_STD_CTRL_SIZE 512 + +static const struct cs_dsp_client_ops wm_adsp1_client_ops; +static const struct cs_dsp_client_ops wm_adsp2_client_ops; + +#define WM_ADSP_FW_MBC_VSS 0 +#define WM_ADSP_FW_HIFI 1 +#define WM_ADSP_FW_TX 2 +#define WM_ADSP_FW_TX_SPK 3 +#define WM_ADSP_FW_RX 4 +#define WM_ADSP_FW_RX_ANC 5 +#define WM_ADSP_FW_CTRL 6 +#define WM_ADSP_FW_ASR 7 +#define WM_ADSP_FW_TRACE 8 +#define WM_ADSP_FW_SPK_PROT 9 +#define WM_ADSP_FW_SPK_CALI 10 +#define WM_ADSP_FW_SPK_DIAG 11 +#define WM_ADSP_FW_MISC 12 + +#define WM_ADSP_NUM_FW 13 + +static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { + [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", + [WM_ADSP_FW_HIFI] = "MasterHiFi", + [WM_ADSP_FW_TX] = "Tx", + [WM_ADSP_FW_TX_SPK] = "Tx Speaker", + [WM_ADSP_FW_RX] = "Rx", + [WM_ADSP_FW_RX_ANC] = "Rx ANC", + [WM_ADSP_FW_CTRL] = "Voice Ctrl", + [WM_ADSP_FW_ASR] = "ASR Assist", + [WM_ADSP_FW_TRACE] = "Dbg Trace", + [WM_ADSP_FW_SPK_PROT] = "Protection", + [WM_ADSP_FW_SPK_CALI] = "Calibration", + [WM_ADSP_FW_SPK_DIAG] = "Diagnostic", + [WM_ADSP_FW_MISC] = "Misc", +}; + +struct wm_adsp_system_config_xm_hdr { + __be32 sys_enable; + __be32 fw_id; + __be32 fw_rev; + __be32 boot_status; + __be32 watchdog; + __be32 dma_buffer_size; + __be32 rdma[6]; + __be32 wdma[8]; + __be32 build_job_name[3]; + __be32 build_job_number; +} __packed; + +struct wm_halo_system_config_xm_hdr { + __be32 halo_heartbeat; + __be32 build_job_name[3]; + __be32 build_job_number; +} __packed; + +struct wm_adsp_alg_xm_struct { + __be32 magic; + __be32 smoothing; + __be32 threshold; + __be32 host_buf_ptr; + __be32 start_seq; + __be32 high_water_mark; + __be32 low_water_mark; + __be64 smoothed_power; +} __packed; + +struct wm_adsp_host_buf_coeff_v1 { + __be32 host_buf_ptr; /* Host buffer pointer */ + __be32 versions; /* Version numbers */ + __be32 name[4]; /* The buffer name */ +} __packed; + +struct wm_adsp_buffer { + __be32 buf1_base; /* Base addr of first buffer area */ + __be32 buf1_size; /* Size of buf1 area in DSP words */ + __be32 buf2_base; /* Base addr of 2nd buffer area */ + __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ + __be32 buf3_base; /* Base addr of buf3 area */ + __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ + __be32 high_water_mark; /* Point at which IRQ is asserted */ + __be32 irq_count; /* bits 1-31 count IRQ assertions */ + __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ + __be32 next_write_index; /* word index of next write */ + __be32 next_read_index; /* word index of next read */ + __be32 error; /* error if any */ + __be32 oldest_block_index; /* word index of oldest surviving */ + __be32 requested_rewind; /* how many blocks rewind was done */ + __be32 reserved_space; /* internal */ + __be32 min_free; /* min free space since stream start */ + __be32 blocks_written[2]; /* total blocks written (64 bit) */ + __be32 words_written[2]; /* total words written (64 bit) */ +} __packed; + +struct wm_adsp_compr; + +struct wm_adsp_compr_buf { + struct list_head list; + struct wm_adsp *dsp; + struct wm_adsp_compr *compr; + + struct wm_adsp_buffer_region *regions; + u32 host_buf_ptr; + + u32 error; + u32 irq_count; + int read_index; + int avail; + int host_buf_mem_type; + + char *name; +}; + +struct wm_adsp_compr { + struct list_head list; + struct wm_adsp *dsp; + struct wm_adsp_compr_buf *buf; + + struct snd_compr_stream *stream; + struct snd_compressed_buffer size; + + u32 *raw_buf; + unsigned int copied_total; + + unsigned int sample_rate; + + const char *name; +}; + +#define WM_ADSP_MIN_FRAGMENTS 1 +#define WM_ADSP_MAX_FRAGMENTS 256 +#define WM_ADSP_MIN_FRAGMENT_SIZE (16 * CS_DSP_DATA_WORD_SIZE) +#define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * CS_DSP_DATA_WORD_SIZE) + +#define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 + +#define HOST_BUFFER_FIELD(field) \ + (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) + +#define ALG_XM_FIELD(field) \ + (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) + +#define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 + +#define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 +#define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 + +static int wm_adsp_buffer_init(struct wm_adsp *dsp); +static int wm_adsp_buffer_free(struct wm_adsp *dsp); + +struct wm_adsp_buffer_region { + unsigned int offset; + unsigned int cumulative_size; + unsigned int mem_type; + unsigned int base_addr; +}; + +struct wm_adsp_buffer_region_def { + unsigned int mem_type; + unsigned int base_offset; + unsigned int size_offset; +}; + +static const struct wm_adsp_buffer_region_def default_regions[] = { + { + .mem_type = WMFW_ADSP2_XM, + .base_offset = HOST_BUFFER_FIELD(buf1_base), + .size_offset = HOST_BUFFER_FIELD(buf1_size), + }, + { + .mem_type = WMFW_ADSP2_XM, + .base_offset = HOST_BUFFER_FIELD(buf2_base), + .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), + }, + { + .mem_type = WMFW_ADSP2_YM, + .base_offset = HOST_BUFFER_FIELD(buf3_base), + .size_offset = HOST_BUFFER_FIELD(buf_total_size), + }, +}; + +struct wm_adsp_fw_caps { + u32 id; + struct snd_codec_desc desc; + int num_regions; + const struct wm_adsp_buffer_region_def *region_defs; +}; + +static const struct wm_adsp_fw_caps ctrl_caps[] = { + { + .id = SND_AUDIOCODEC_BESPOKE, + .desc = { + .max_ch = 8, + .sample_rates = { 16000 }, + .num_sample_rates = 1, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .num_regions = ARRAY_SIZE(default_regions), + .region_defs = default_regions, + }, +}; + +static const struct wm_adsp_fw_caps trace_caps[] = { + { + .id = SND_AUDIOCODEC_BESPOKE, + .desc = { + .max_ch = 8, + .sample_rates = { + 4000, 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000, 64000, 88200, + 96000, 176400, 192000 + }, + .num_sample_rates = 15, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .num_regions = ARRAY_SIZE(default_regions), + .region_defs = default_regions, + }, +}; + +static const struct { + const char *file; + int compr_direction; + int num_caps; + const struct wm_adsp_fw_caps *caps; + bool voice_trigger; +} wm_adsp_fw[WM_ADSP_NUM_FW] = { + [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, + [WM_ADSP_FW_HIFI] = { .file = "hifi" }, + [WM_ADSP_FW_TX] = { .file = "tx" }, + [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, + [WM_ADSP_FW_RX] = { .file = "rx" }, + [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, + [WM_ADSP_FW_CTRL] = { + .file = "ctrl", + .compr_direction = SND_COMPRESS_CAPTURE, + .num_caps = ARRAY_SIZE(ctrl_caps), + .caps = ctrl_caps, + .voice_trigger = true, + }, + [WM_ADSP_FW_ASR] = { .file = "asr" }, + [WM_ADSP_FW_TRACE] = { + .file = "trace", + .compr_direction = SND_COMPRESS_CAPTURE, + .num_caps = ARRAY_SIZE(trace_caps), + .caps = trace_caps, + }, + [WM_ADSP_FW_SPK_PROT] = { + .file = "spk-prot", + .compr_direction = SND_COMPRESS_CAPTURE, + .num_caps = ARRAY_SIZE(trace_caps), + .caps = trace_caps, + }, + [WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" }, + [WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" }, + [WM_ADSP_FW_MISC] = { .file = "misc" }, +}; + +struct wm_coeff_ctl { + const char *name; + struct cs_dsp_coeff_ctl *cs_ctl; + struct soc_bytes_ext bytes_ext; + struct work_struct work; +}; + +int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp_fw_get); + +int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); + int ret = 1; + + if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) + return 0; + + if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) + return -EINVAL; + + mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock); + + if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list)) + ret = -EBUSY; + else + dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; + + mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_fw_put); + +const struct soc_enum wm_adsp_fw_enum[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), +}; +EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); + +static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) +{ + return container_of(ext, struct wm_coeff_ctl, bytes_ext); +} + +static int wm_coeff_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + + switch (cs_ctl->type) { + case WMFW_CTL_TYPE_ACKED: + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE; + uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE; + uinfo->value.integer.step = 1; + uinfo->count = 1; + break; + default: + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = cs_ctl->len; + break; + } + + return 0; +} + +static int wm_coeff_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + char *p = ucontrol->value.bytes.data; + int ret = 0; + + mutex_lock(&cs_ctl->dsp->pwr_lock); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len); + mutex_unlock(&cs_ctl->dsp->pwr_lock); + + return ret; +} + +static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, + const unsigned int __user *bytes, unsigned int size) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + void *scratch; + int ret = 0; + + scratch = vmalloc(size); + if (!scratch) + return -ENOMEM; + + if (copy_from_user(scratch, bytes, size)) { + ret = -EFAULT; + } else { + mutex_lock(&cs_ctl->dsp->pwr_lock); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, scratch, size); + mutex_unlock(&cs_ctl->dsp->pwr_lock); + } + vfree(scratch); + + return ret; +} + +static int wm_coeff_put_acked(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + unsigned int val = ucontrol->value.integer.value[0]; + int ret; + + if (val == 0) + return 0; /* 0 means no event */ + + mutex_lock(&cs_ctl->dsp->pwr_lock); + + if (cs_ctl->enabled) + ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); + else + ret = -EPERM; + + mutex_unlock(&cs_ctl->dsp->pwr_lock); + + if (ret < 0) + return ret; + + return 1; +} + +static int wm_coeff_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + char *p = ucontrol->value.bytes.data; + int ret; + + mutex_lock(&cs_ctl->dsp->pwr_lock); + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len); + mutex_unlock(&cs_ctl->dsp->pwr_lock); + + return ret; +} + +static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, + unsigned int __user *bytes, unsigned int size) +{ + struct soc_bytes_ext *bytes_ext = + (struct soc_bytes_ext *)kctl->private_value; + struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + int ret = 0; + + mutex_lock(&cs_ctl->dsp->pwr_lock); + + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); + + if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) + ret = -EFAULT; + + mutex_unlock(&cs_ctl->dsp->pwr_lock); + + return ret; +} + +static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* + * Although it's not useful to read an acked control, we must satisfy + * user-side assumptions that all controls are readable and that a + * write of the same value should be filtered out (it's valid to send + * the same event number again to the firmware). We therefore return 0, + * meaning "no event" so valid event numbers will always be a change + */ + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) +{ + unsigned int out, rd, wr, vol; + + if (len > ADSP_MAX_STD_CTRL_SIZE) { + rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; + wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; + vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + } else { + rd = SNDRV_CTL_ELEM_ACCESS_READ; + wr = SNDRV_CTL_ELEM_ACCESS_WRITE; + vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + out = 0; + } + + if (in) { + out |= rd; + if (in & WMFW_CTL_FLAG_WRITEABLE) + out |= wr; + if (in & WMFW_CTL_FLAG_VOLATILE) + out |= vol; + } else { + out |= rd | wr | vol; + } + + return out; +} + +static void wm_adsp_ctl_work(struct work_struct *work) +{ + struct wm_coeff_ctl *ctl = container_of(work, + struct wm_coeff_ctl, + work); + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; + struct wm_adsp *dsp = container_of(cs_ctl->dsp, + struct wm_adsp, + cs_dsp); + struct snd_kcontrol_new *kcontrol; + + kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); + if (!kcontrol) + return; + + kcontrol->name = ctl->name; + kcontrol->info = wm_coeff_info; + kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + kcontrol->tlv.c = snd_soc_bytes_tlv_callback; + kcontrol->private_value = (unsigned long)&ctl->bytes_ext; + kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len); + + switch (cs_ctl->type) { + case WMFW_CTL_TYPE_ACKED: + kcontrol->get = wm_coeff_get_acked; + kcontrol->put = wm_coeff_put_acked; + break; + default: + if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + ctl->bytes_ext.max = cs_ctl->len; + ctl->bytes_ext.get = wm_coeff_tlv_get; + ctl->bytes_ext.put = wm_coeff_tlv_put; + } else { + kcontrol->get = wm_coeff_get; + kcontrol->put = wm_coeff_put; + } + break; + } + + snd_soc_add_component_controls(dsp->component, kcontrol, 1); + + kfree(kcontrol); +} + +static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl) +{ + struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp); + struct cs_dsp *cs_dsp = &dsp->cs_dsp; + struct wm_coeff_ctl *ctl; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const char *region_name; + int ret; + + if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) + return 0; + + region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type); + if (!region_name) { + adsp_err(dsp, "Unknown region type: %d\n", cs_ctl->alg_region.type); + return -EINVAL; + } + + switch (cs_dsp->fw_ver) { + case 0: + case 1: + ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "%s %s %x", cs_dsp->name, region_name, + cs_ctl->alg_region.alg); + break; + case 2: + ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "%s%c %.12s %x", cs_dsp->name, *region_name, + wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg); + break; + default: + ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "%s %.12s %x", cs_dsp->name, + wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg); + break; + } + + if (cs_ctl->subname) { + int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; + int skip = 0; + + if (dsp->component->name_prefix) + avail -= strlen(dsp->component->name_prefix) + 1; + + /* Truncate the subname from the start if it is too long */ + if (cs_ctl->subname_len > avail) + skip = cs_ctl->subname_len - avail; + + snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, + " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip); + } + + ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); + if (!ctl) + return -ENOMEM; + ctl->cs_ctl = cs_ctl; + + ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); + if (!ctl->name) { + ret = -ENOMEM; + goto err_ctl; + } + + cs_ctl->priv = ctl; + + INIT_WORK(&ctl->work, wm_adsp_ctl_work); + schedule_work(&ctl->work); + + return 0; + +err_ctl: + kfree(ctl); + + return ret; +} + +static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) +{ + struct wm_coeff_ctl *ctl = cs_ctl->priv; + + cancel_work_sync(&ctl->work); + + kfree(ctl->name); + kfree(ctl); +} + +int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, + unsigned int alg, void *buf, size_t len) +{ + struct cs_dsp_coeff_ctl *cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); + struct wm_coeff_ctl *ctl; + int ret; + + mutex_lock(&dsp->cs_dsp.pwr_lock); + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); + mutex_unlock(&dsp->cs_dsp.pwr_lock); + + if (ret < 0) + return ret; + + if (ret == 0 || (cs_ctl->flags & WMFW_CTL_FLAG_SYS)) + return 0; + + ctl = cs_ctl->priv; + + return snd_soc_component_notify_control(dsp->component, ctl->name); +} +EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); + +int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, + unsigned int alg, void *buf, size_t len) +{ + int ret; + + mutex_lock(&dsp->cs_dsp.pwr_lock); + ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), + 0, buf, len); + mutex_unlock(&dsp->cs_dsp.pwr_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); + +static void wm_adsp_release_firmware_files(struct wm_adsp *dsp, + const struct firmware *wmfw_firmware, + char *wmfw_filename, + const struct firmware *coeff_firmware, + char *coeff_filename) +{ + if (wmfw_firmware) + release_firmware(wmfw_firmware); + kfree(wmfw_filename); + + if (coeff_firmware) + release_firmware(coeff_firmware); + kfree(coeff_filename); +} + +static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, + const struct firmware **firmware, char **filename, + const char *dir, const char *system_name, + const char *asoc_component_prefix, + const char *filetype) +{ + struct cs_dsp *cs_dsp = &dsp->cs_dsp; + char *s, c; + int ret = 0; + + if (system_name && asoc_component_prefix) + *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, dsp->part, + dsp->fwf_name, wm_adsp_fw[dsp->fw].file, system_name, + asoc_component_prefix, filetype); + else if (system_name) + *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part, + dsp->fwf_name, wm_adsp_fw[dsp->fw].file, system_name, + filetype); + else + *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, dsp->fwf_name, + wm_adsp_fw[dsp->fw].file, filetype); + + if (*filename == NULL) + return -ENOMEM; + + /* + * Make sure that filename is lower-case and any non alpha-numeric + * characters except full stop and forward slash are replaced with + * hyphens. + */ + s = *filename; + while (*s) { + c = *s; + if (isalnum(c)) + *s = tolower(c); + else if ((c != '.') && (c != '/')) + *s = '-'; + s++; + } + + ret = firmware_request_nowarn(firmware, *filename, cs_dsp->dev); + if (ret != 0) { + adsp_dbg(dsp, "Failed to request '%s'\n", *filename); + kfree(*filename); + *filename = NULL; + } else { + adsp_dbg(dsp, "Found '%s'\n", *filename); + } + + return ret; +} + +static const char *cirrus_dir = "cirrus/"; +static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, + const struct firmware **wmfw_firmware, + char **wmfw_filename, + const struct firmware **coeff_firmware, + char **coeff_filename) +{ + const char *system_name = dsp->system_name; + const char *asoc_component_prefix = dsp->component->name_prefix; + int ret = 0; + + if (system_name && asoc_component_prefix) { + if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, + cirrus_dir, system_name, + asoc_component_prefix, "wmfw")) { + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + cirrus_dir, system_name, + asoc_component_prefix, "bin"); + return 0; + } + } + + if (system_name) { + if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, + cirrus_dir, system_name, + NULL, "wmfw")) { + if (asoc_component_prefix) + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + cirrus_dir, system_name, + asoc_component_prefix, "bin"); + + if (!*coeff_firmware) + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + cirrus_dir, system_name, + NULL, "bin"); + return 0; + } + } + + if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, + "", NULL, NULL, "wmfw")) { + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + "", NULL, NULL, "bin"); + return 0; + } + + ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, + cirrus_dir, NULL, NULL, "wmfw"); + if (!ret) { + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + cirrus_dir, NULL, NULL, "bin"); + return 0; + } + + if (dsp->wmfw_optional) { + if (system_name) { + if (asoc_component_prefix) + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + cirrus_dir, system_name, + asoc_component_prefix, "bin"); + + if (!*coeff_firmware) + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + cirrus_dir, system_name, + NULL, "bin"); + } + + if (!*coeff_firmware) + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + "", NULL, NULL, "bin"); + + if (!*coeff_firmware) + wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, + cirrus_dir, NULL, NULL, "bin"); + + return 0; + } + + adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n", + cirrus_dir, dsp->part, dsp->fwf_name, wm_adsp_fw[dsp->fw].file, + system_name, asoc_component_prefix); + + return -ENOENT; +} + +static int wm_adsp_common_init(struct wm_adsp *dsp) +{ + char *p; + + INIT_LIST_HEAD(&dsp->compr_list); + INIT_LIST_HEAD(&dsp->buffer_list); + + if (!dsp->fwf_name) { + p = devm_kstrdup(dsp->cs_dsp.dev, dsp->cs_dsp.name, GFP_KERNEL); + if (!p) + return -ENOMEM; + + dsp->fwf_name = p; + for (; *p != 0; ++p) + *p = tolower(*p); + } + + return 0; +} + +int wm_adsp1_init(struct wm_adsp *dsp) +{ + int ret; + + dsp->cs_dsp.client_ops = &wm_adsp1_client_ops; + + ret = cs_dsp_adsp1_init(&dsp->cs_dsp); + if (ret) + return ret; + + return wm_adsp_common_init(dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp1_init); + +int wm_adsp1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); + struct wm_adsp *dsp = &dsps[w->shift]; + int ret = 0; + char *wmfw_filename = NULL; + const struct firmware *wmfw_firmware = NULL; + char *coeff_filename = NULL; + const struct firmware *coeff_firmware = NULL; + + dsp->component = component; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = wm_adsp_request_firmware_files(dsp, + &wmfw_firmware, &wmfw_filename, + &coeff_firmware, &coeff_filename); + if (ret) + break; + + ret = cs_dsp_adsp1_power_up(&dsp->cs_dsp, + wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename, + wm_adsp_fw_text[dsp->fw]); + + wm_adsp_release_firmware_files(dsp, + wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename); + break; + case SND_SOC_DAPM_PRE_PMD: + cs_dsp_adsp1_power_down(&dsp->cs_dsp); + break; + default: + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp1_event); + +int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); + struct wm_adsp *dsp = &dsps[w->shift]; + + return cs_dsp_set_dspclk(&dsp->cs_dsp, freq); +} +EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk); + +int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct wm_adsp *dsp = &dsps[mc->shift - 1]; + + ucontrol->value.integer.value[0] = dsp->preloaded; + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); + +int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct wm_adsp *dsp = &dsps[mc->shift - 1]; + char preload[32]; + + if (dsp->preloaded == ucontrol->value.integer.value[0]) + return 0; + + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); + + if (ucontrol->value.integer.value[0] || dsp->toggle_preload) + snd_soc_component_force_enable_pin(component, preload); + else + snd_soc_component_disable_pin(component, preload); + + snd_soc_dapm_sync(dapm); + + flush_work(&dsp->boot_work); + + dsp->preloaded = ucontrol->value.integer.value[0]; + + if (dsp->toggle_preload) { + snd_soc_component_disable_pin(component, preload); + snd_soc_dapm_sync(dapm); + } + + return 1; +} +EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); + +int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware) +{ + int ret = 0; + char *wmfw_filename = NULL; + const struct firmware *wmfw_firmware = NULL; + char *coeff_filename = NULL; + const struct firmware *coeff_firmware = NULL; + + if (load_firmware) { + ret = wm_adsp_request_firmware_files(dsp, + &wmfw_firmware, &wmfw_filename, + &coeff_firmware, &coeff_filename); + if (ret) + return ret; + } + + ret = cs_dsp_power_up(&dsp->cs_dsp, + wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename, + wm_adsp_fw_text[dsp->fw]); + + wm_adsp_release_firmware_files(dsp, + wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_power_up); + +void wm_adsp_power_down(struct wm_adsp *dsp) +{ + cs_dsp_power_down(&dsp->cs_dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp_power_down); + +static void wm_adsp_boot_work(struct work_struct *work) +{ + struct wm_adsp *dsp = container_of(work, + struct wm_adsp, + boot_work); + + wm_adsp_power_up(dsp, true); +} + +int wm_adsp_early_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); + struct wm_adsp *dsp = &dsps[w->shift]; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + queue_work(system_unbound_wq, &dsp->boot_work); + break; + case SND_SOC_DAPM_PRE_PMD: + wm_adsp_power_down(dsp); + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp_early_event); + +static int wm_adsp_pre_run(struct cs_dsp *cs_dsp) +{ + struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); + + if (!dsp->pre_run) + return 0; + + return (*dsp->pre_run)(dsp); +} + +static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp) +{ + struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); + + if (wm_adsp_fw[dsp->fw].num_caps != 0) + return wm_adsp_buffer_init(dsp); + + return 0; +} + +static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp) +{ + struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); + + if (wm_adsp_fw[dsp->fw].num_caps != 0) + wm_adsp_buffer_free(dsp); + + dsp->fatal_error = false; +} + +int wm_adsp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); + struct wm_adsp *dsp = &dsps[w->shift]; + int ret = 0; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + flush_work(&dsp->boot_work); + ret = cs_dsp_run(&dsp->cs_dsp); + break; + case SND_SOC_DAPM_PRE_PMD: + cs_dsp_stop(&dsp->cs_dsp); + break; + default: + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_event); + +int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) +{ + char preload[32]; + + if (!dsp->cs_dsp.no_core_startstop) { + snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); + snd_soc_component_disable_pin(component, preload); + } + + cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root); + + dsp->component = component; + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); + +int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) +{ + cs_dsp_cleanup_debugfs(&dsp->cs_dsp); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp2_component_remove); + +int wm_adsp2_init(struct wm_adsp *dsp) +{ + int ret; + + INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); + + dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr); + dsp->cs_dsp.client_ops = &wm_adsp2_client_ops; + + ret = cs_dsp_adsp2_init(&dsp->cs_dsp); + if (ret) + return ret; + + return wm_adsp_common_init(dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp2_init); + +int wm_halo_init(struct wm_adsp *dsp) +{ + int ret; + + INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); + + dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr); + dsp->cs_dsp.client_ops = &wm_adsp2_client_ops; + + ret = cs_dsp_halo_init(&dsp->cs_dsp); + if (ret) + return ret; + + return wm_adsp_common_init(dsp); +} +EXPORT_SYMBOL_GPL(wm_halo_init); + +void wm_adsp2_remove(struct wm_adsp *dsp) +{ + cs_dsp_remove(&dsp->cs_dsp); +} +EXPORT_SYMBOL_GPL(wm_adsp2_remove); + +static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) +{ + return compr->buf != NULL; +} + +static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) +{ + struct wm_adsp_compr_buf *buf = NULL, *tmp; + + if (compr->dsp->fatal_error) + return -EINVAL; + + list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { + if (!tmp->name || !strcmp(compr->name, tmp->name)) { + buf = tmp; + break; + } + } + + if (!buf) + return -EINVAL; + + compr->buf = buf; + buf->compr = compr; + + return 0; +} + +static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) +{ + if (!compr) + return; + + /* Wake the poll so it can see buffer is no longer attached */ + if (compr->stream) + snd_compr_fragment_elapsed(compr->stream); + + if (wm_adsp_compr_attached(compr)) { + compr->buf->compr = NULL; + compr->buf = NULL; + } +} + +int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) +{ + struct wm_adsp_compr *compr, *tmp; + struct snd_soc_pcm_runtime *rtd = stream->private_data; + int ret = 0; + + mutex_lock(&dsp->cs_dsp.pwr_lock); + + if (wm_adsp_fw[dsp->fw].num_caps == 0) { + adsp_err(dsp, "%s: Firmware does not support compressed API\n", + asoc_rtd_to_codec(rtd, 0)->name); + ret = -ENXIO; + goto out; + } + + if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { + adsp_err(dsp, "%s: Firmware does not support stream direction\n", + asoc_rtd_to_codec(rtd, 0)->name); + ret = -EINVAL; + goto out; + } + + list_for_each_entry(tmp, &dsp->compr_list, list) { + if (!strcmp(tmp->name, asoc_rtd_to_codec(rtd, 0)->name)) { + adsp_err(dsp, "%s: Only a single stream supported per dai\n", + asoc_rtd_to_codec(rtd, 0)->name); + ret = -EBUSY; + goto out; + } + } + + compr = kzalloc(sizeof(*compr), GFP_KERNEL); + if (!compr) { + ret = -ENOMEM; + goto out; + } + + compr->dsp = dsp; + compr->stream = stream; + compr->name = asoc_rtd_to_codec(rtd, 0)->name; + + list_add_tail(&compr->list, &dsp->compr_list); + + stream->runtime->private_data = compr; + +out: + mutex_unlock(&dsp->cs_dsp.pwr_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_open); + +int wm_adsp_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *stream) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + struct wm_adsp *dsp = compr->dsp; + + mutex_lock(&dsp->cs_dsp.pwr_lock); + + wm_adsp_compr_detach(compr); + list_del(&compr->list); + + kfree(compr->raw_buf); + kfree(compr); + + mutex_unlock(&dsp->cs_dsp.pwr_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_free); + +static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + struct wm_adsp *dsp = compr->dsp; + const struct wm_adsp_fw_caps *caps; + const struct snd_codec_desc *desc; + int i, j; + + if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || + params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || + params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || + params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || + params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) { + compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", + params->buffer.fragment_size, + params->buffer.fragments); + + return -EINVAL; + } + + for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { + caps = &wm_adsp_fw[dsp->fw].caps[i]; + desc = &caps->desc; + + if (caps->id != params->codec.id) + continue; + + if (stream->direction == SND_COMPRESS_PLAYBACK) { + if (desc->max_ch < params->codec.ch_out) + continue; + } else { + if (desc->max_ch < params->codec.ch_in) + continue; + } + + if (!(desc->formats & (1 << params->codec.format))) + continue; + + for (j = 0; j < desc->num_sample_rates; ++j) + if (desc->sample_rates[j] == params->codec.sample_rate) + return 0; + } + + compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", + params->codec.id, params->codec.ch_in, params->codec.ch_out, + params->codec.sample_rate, params->codec.format); + return -EINVAL; +} + +static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) +{ + return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE; +} + +int wm_adsp_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + unsigned int size; + int ret; + + ret = wm_adsp_compr_check_params(stream, params); + if (ret) + return ret; + + compr->size = params->buffer; + + compr_dbg(compr, "fragment_size=%d fragments=%d\n", + compr->size.fragment_size, compr->size.fragments); + + size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); + compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); + if (!compr->raw_buf) + return -ENOMEM; + + compr->sample_rate = params->codec.sample_rate; + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); + +int wm_adsp_compr_get_caps(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_caps *caps) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + int fw = compr->dsp->fw; + int i; + + if (wm_adsp_fw[fw].caps) { + for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) + caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; + + caps->num_codecs = i; + caps->direction = wm_adsp_fw[fw].compr_direction; + + caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; + caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; + caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; + caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); + +static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, + unsigned int field_offset, u32 *data) +{ + return cs_dsp_read_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type, + buf->host_buf_ptr + field_offset, data); +} + +static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, + unsigned int field_offset, u32 data) +{ + return cs_dsp_write_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type, + buf->host_buf_ptr + field_offset, + data); +} + +static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) +{ + const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; + struct wm_adsp_buffer_region *region; + u32 offset = 0; + int i, ret; + + buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions), + GFP_KERNEL); + if (!buf->regions) + return -ENOMEM; + + for (i = 0; i < caps->num_regions; ++i) { + region = &buf->regions[i]; + + region->offset = offset; + region->mem_type = caps->region_defs[i].mem_type; + + ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, + ®ion->base_addr); + if (ret < 0) + goto err; + + ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, + &offset); + if (ret < 0) + goto err; + + region->cumulative_size = offset; + + compr_dbg(buf, + "region=%d type=%d base=%08x off=%08x size=%08x\n", + i, region->mem_type, region->base_addr, + region->offset, region->cumulative_size); + } + + return 0; + +err: + kfree(buf->regions); + return ret; +} + +static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) +{ + buf->irq_count = 0xFFFFFFFF; + buf->read_index = -1; + buf->avail = 0; +} + +static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) +{ + struct wm_adsp_compr_buf *buf; + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + + buf->dsp = dsp; + + wm_adsp_buffer_clear(buf); + + return buf; +} + +static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) +{ + struct cs_dsp_alg_region *alg_region; + struct wm_adsp_compr_buf *buf; + u32 xmalg, addr, magic; + int i, ret; + + alg_region = cs_dsp_find_alg_region(&dsp->cs_dsp, WMFW_ADSP2_XM, dsp->cs_dsp.fw_id); + if (!alg_region) { + adsp_err(dsp, "No algorithm region found\n"); + return -EINVAL; + } + + xmalg = dsp->sys_config_size / sizeof(__be32); + + addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); + ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, &magic); + if (ret < 0) + return ret; + + if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) + return -ENODEV; + + buf = wm_adsp_buffer_alloc(dsp); + if (!buf) + return -ENOMEM; + + addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); + for (i = 0; i < 5; ++i) { + ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, + &buf->host_buf_ptr); + if (ret < 0) + goto err; + + if (buf->host_buf_ptr) + break; + + usleep_range(1000, 2000); + } + + if (!buf->host_buf_ptr) { + ret = -EIO; + goto err; + } + + buf->host_buf_mem_type = WMFW_ADSP2_XM; + + ret = wm_adsp_buffer_populate(buf); + if (ret < 0) + goto err; + + list_add_tail(&buf->list, &dsp->buffer_list); + + compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); + + return 0; + +err: + kfree(buf); + + return ret; +} + +static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) +{ + struct wm_adsp_host_buf_coeff_v1 coeff_v1; + struct wm_adsp_compr_buf *buf; + struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp); + unsigned int version = 0; + int ret, i; + + for (i = 0; i < 5; ++i) { + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1, + min(cs_ctl->len, sizeof(coeff_v1))); + if (ret < 0) + return ret; + + if (coeff_v1.host_buf_ptr) + break; + + usleep_range(1000, 2000); + } + + if (!coeff_v1.host_buf_ptr) { + adsp_err(dsp, "Failed to acquire host buffer\n"); + return -EIO; + } + + buf = wm_adsp_buffer_alloc(dsp); + if (!buf) + return -ENOMEM; + + buf->host_buf_mem_type = cs_ctl->alg_region.type; + buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr); + + ret = wm_adsp_buffer_populate(buf); + if (ret < 0) + goto err; + + /* + * v0 host_buffer coefficients didn't have versioning, so if the + * control is one word, assume version 0. + */ + if (cs_ctl->len == 4) + goto done; + + version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK; + version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; + + if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { + adsp_err(dsp, + "Host buffer coeff ver %u > supported version %u\n", + version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); + ret = -EINVAL; + goto err; + } + + cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name)); + + buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part, + (char *)&coeff_v1.name); + +done: + list_add_tail(&buf->list, &dsp->buffer_list); + + compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", + buf->host_buf_ptr, version); + + return version; + +err: + kfree(buf); + + return ret; +} + +static int wm_adsp_buffer_init(struct wm_adsp *dsp) +{ + struct cs_dsp_coeff_ctl *cs_ctl; + int ret; + + list_for_each_entry(cs_ctl, &dsp->cs_dsp.ctl_list, list) { + if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) + continue; + + if (!cs_ctl->enabled) + continue; + + ret = wm_adsp_buffer_parse_coeff(cs_ctl); + if (ret < 0) { + adsp_err(dsp, "Failed to parse coeff: %d\n", ret); + goto error; + } else if (ret == 0) { + /* Only one buffer supported for version 0 */ + return 0; + } + } + + if (list_empty(&dsp->buffer_list)) { + /* Fall back to legacy support */ + ret = wm_adsp_buffer_parse_legacy(dsp); + if (ret == -ENODEV) + adsp_info(dsp, "Legacy support not available\n"); + else if (ret) + adsp_warn(dsp, "Failed to parse legacy: %d\n", ret); + } + + return 0; + +error: + wm_adsp_buffer_free(dsp); + return ret; +} + +static int wm_adsp_buffer_free(struct wm_adsp *dsp) +{ + struct wm_adsp_compr_buf *buf, *tmp; + + list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { + wm_adsp_compr_detach(buf->compr); + + kfree(buf->name); + kfree(buf->regions); + list_del(&buf->list); + kfree(buf); + } + + return 0; +} + +static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) +{ + int ret; + + ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); + if (ret < 0) { + compr_err(buf, "Failed to check buffer error: %d\n", ret); + return ret; + } + if (buf->error != 0) { + compr_err(buf, "Buffer error occurred: %d\n", buf->error); + return -EIO; + } + + return 0; +} + +int wm_adsp_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *stream, int cmd) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + struct wm_adsp *dsp = compr->dsp; + int ret = 0; + + compr_dbg(compr, "Trigger: %d\n", cmd); + + mutex_lock(&dsp->cs_dsp.pwr_lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (!wm_adsp_compr_attached(compr)) { + ret = wm_adsp_compr_attach(compr); + if (ret < 0) { + compr_err(compr, "Failed to link buffer and stream: %d\n", + ret); + break; + } + } + + ret = wm_adsp_buffer_get_error(compr->buf); + if (ret < 0) + break; + + /* Trigger the IRQ at one fragment of data */ + ret = wm_adsp_buffer_write(compr->buf, + HOST_BUFFER_FIELD(high_water_mark), + wm_adsp_compr_frag_words(compr)); + if (ret < 0) { + compr_err(compr, "Failed to set high water mark: %d\n", + ret); + break; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (wm_adsp_compr_attached(compr)) + wm_adsp_buffer_clear(compr->buf); + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&dsp->cs_dsp.pwr_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); + +static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) +{ + int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; + + return buf->regions[last_region].cumulative_size; +} + +static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) +{ + u32 next_read_index, next_write_index; + int write_index, read_index, avail; + int ret; + + /* Only sync read index if we haven't already read a valid index */ + if (buf->read_index < 0) { + ret = wm_adsp_buffer_read(buf, + HOST_BUFFER_FIELD(next_read_index), + &next_read_index); + if (ret < 0) + return ret; + + read_index = sign_extend32(next_read_index, 23); + + if (read_index < 0) { + compr_dbg(buf, "Avail check on unstarted stream\n"); + return 0; + } + + buf->read_index = read_index; + } + + ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), + &next_write_index); + if (ret < 0) + return ret; + + write_index = sign_extend32(next_write_index, 23); + + avail = write_index - buf->read_index; + if (avail < 0) + avail += wm_adsp_buffer_size(buf); + + compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", + buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE); + + buf->avail = avail; + + return 0; +} + +int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) +{ + struct wm_adsp_compr_buf *buf; + struct wm_adsp_compr *compr; + int ret = 0; + + mutex_lock(&dsp->cs_dsp.pwr_lock); + + if (list_empty(&dsp->buffer_list)) { + ret = -ENODEV; + goto out; + } + + adsp_dbg(dsp, "Handling buffer IRQ\n"); + + list_for_each_entry(buf, &dsp->buffer_list, list) { + compr = buf->compr; + + ret = wm_adsp_buffer_get_error(buf); + if (ret < 0) + goto out_notify; /* Wake poll to report error */ + + ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), + &buf->irq_count); + if (ret < 0) { + compr_err(buf, "Failed to get irq_count: %d\n", ret); + goto out; + } + + ret = wm_adsp_buffer_update_avail(buf); + if (ret < 0) { + compr_err(buf, "Error reading avail: %d\n", ret); + goto out; + } + + if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) + ret = WM_ADSP_COMPR_VOICE_TRIGGER; + +out_notify: + if (compr && compr->stream) + snd_compr_fragment_elapsed(compr->stream); + } + +out: + mutex_unlock(&dsp->cs_dsp.pwr_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); + +static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) +{ + if (buf->irq_count & 0x01) + return 0; + + compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count); + + buf->irq_count |= 0x01; + + return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), + buf->irq_count); +} + +int wm_adsp_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + struct wm_adsp *dsp = compr->dsp; + struct wm_adsp_compr_buf *buf; + int ret = 0; + + compr_dbg(compr, "Pointer request\n"); + + mutex_lock(&dsp->cs_dsp.pwr_lock); + + buf = compr->buf; + + if (dsp->fatal_error || !buf || buf->error) { + snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); + ret = -EIO; + goto out; + } + + if (buf->avail < wm_adsp_compr_frag_words(compr)) { + ret = wm_adsp_buffer_update_avail(buf); + if (ret < 0) { + compr_err(compr, "Error reading avail: %d\n", ret); + goto out; + } + + /* + * If we really have less than 1 fragment available tell the + * DSP to inform us once a whole fragment is available. + */ + if (buf->avail < wm_adsp_compr_frag_words(compr)) { + ret = wm_adsp_buffer_get_error(buf); + if (ret < 0) { + if (buf->error) + snd_compr_stop_error(stream, + SNDRV_PCM_STATE_XRUN); + goto out; + } + + ret = wm_adsp_buffer_reenable_irq(buf); + if (ret < 0) { + compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", + ret); + goto out; + } + } + } + + tstamp->copied_total = compr->copied_total; + tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; + tstamp->sampling_rate = compr->sample_rate; + +out: + mutex_unlock(&dsp->cs_dsp.pwr_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); + +static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) +{ + struct wm_adsp_compr_buf *buf = compr->buf; + unsigned int adsp_addr; + int mem_type, nwords, max_read; + int i, ret; + + /* Calculate read parameters */ + for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) + if (buf->read_index < buf->regions[i].cumulative_size) + break; + + if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) + return -EINVAL; + + mem_type = buf->regions[i].mem_type; + adsp_addr = buf->regions[i].base_addr + + (buf->read_index - buf->regions[i].offset); + + max_read = wm_adsp_compr_frag_words(compr); + nwords = buf->regions[i].cumulative_size - buf->read_index; + + if (nwords > target) + nwords = target; + if (nwords > buf->avail) + nwords = buf->avail; + if (nwords > max_read) + nwords = max_read; + if (!nwords) + return 0; + + /* Read data from DSP */ + ret = cs_dsp_read_raw_data_block(&buf->dsp->cs_dsp, mem_type, adsp_addr, + nwords, (__be32 *)compr->raw_buf); + if (ret < 0) + return ret; + + cs_dsp_remove_padding(compr->raw_buf, nwords); + + /* update read index to account for words read */ + buf->read_index += nwords; + if (buf->read_index == wm_adsp_buffer_size(buf)) + buf->read_index = 0; + + ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), + buf->read_index); + if (ret < 0) + return ret; + + /* update avail to account for words read */ + buf->avail -= nwords; + + return nwords; +} + +static int wm_adsp_compr_read(struct wm_adsp_compr *compr, + char __user *buf, size_t count) +{ + struct wm_adsp *dsp = compr->dsp; + int ntotal = 0; + int nwords, nbytes; + + compr_dbg(compr, "Requested read of %zu bytes\n", count); + + if (dsp->fatal_error || !compr->buf || compr->buf->error) { + snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); + return -EIO; + } + + count /= CS_DSP_DATA_WORD_SIZE; + + do { + nwords = wm_adsp_buffer_capture_block(compr, count); + if (nwords < 0) { + compr_err(compr, "Failed to capture block: %d\n", + nwords); + return nwords; + } + + nbytes = nwords * CS_DSP_DATA_WORD_SIZE; + + compr_dbg(compr, "Read %d bytes\n", nbytes); + + if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { + compr_err(compr, "Failed to copy data to user: %d, %d\n", + ntotal, nbytes); + return -EFAULT; + } + + count -= nwords; + ntotal += nbytes; + } while (nwords > 0 && count > 0); + + compr->copied_total += ntotal; + + return ntotal; +} + +int wm_adsp_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *stream, char __user *buf, + size_t count) +{ + struct wm_adsp_compr *compr = stream->runtime->private_data; + struct wm_adsp *dsp = compr->dsp; + int ret; + + mutex_lock(&dsp->cs_dsp.pwr_lock); + + if (stream->direction == SND_COMPRESS_CAPTURE) + ret = wm_adsp_compr_read(compr, buf, count); + else + ret = -ENOTSUPP; + + mutex_unlock(&dsp->cs_dsp.pwr_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); + +static void wm_adsp_fatal_error(struct cs_dsp *cs_dsp) +{ + struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); + struct wm_adsp_compr *compr; + + dsp->fatal_error = true; + + list_for_each_entry(compr, &dsp->compr_list, list) { + if (compr->stream) + snd_compr_fragment_elapsed(compr->stream); + } +} + +irqreturn_t wm_adsp2_bus_error(int irq, void *data) +{ + struct wm_adsp *dsp = (struct wm_adsp *)data; + + cs_dsp_adsp2_bus_error(&dsp->cs_dsp); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); + +irqreturn_t wm_halo_bus_error(int irq, void *data) +{ + struct wm_adsp *dsp = (struct wm_adsp *)data; + + cs_dsp_halo_bus_error(&dsp->cs_dsp); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(wm_halo_bus_error); + +irqreturn_t wm_halo_wdt_expire(int irq, void *data) +{ + struct wm_adsp *dsp = data; + + cs_dsp_halo_wdt_expire(&dsp->cs_dsp); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(wm_halo_wdt_expire); + +static const struct cs_dsp_client_ops wm_adsp1_client_ops = { + .control_add = wm_adsp_control_add, + .control_remove = wm_adsp_control_remove, +}; + +static const struct cs_dsp_client_ops wm_adsp2_client_ops = { + .control_add = wm_adsp_control_add, + .control_remove = wm_adsp_control_remove, + .pre_run = wm_adsp_pre_run, + .post_run = wm_adsp_event_post_run, + .post_stop = wm_adsp_event_post_stop, + .watchdog_expired = wm_adsp_fatal_error, +}; + +MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(FW_CS_DSP); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h new file mode 100644 index 0000000000..067d807a7c --- /dev/null +++ b/sound/soc/codecs/wm_adsp.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm_adsp.h -- Wolfson ADSP support + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#ifndef __WM_ADSP_H +#define __WM_ADSP_H + +#include +#include + +#include +#include +#include + +/* Return values for wm_adsp_compr_handle_irq */ +#define WM_ADSP_COMPR_OK 0 +#define WM_ADSP_COMPR_VOICE_TRIGGER 1 + +struct wm_adsp_compr; +struct wm_adsp_compr_buf; + +struct wm_adsp { + struct cs_dsp cs_dsp; + const char *part; + const char *fwf_name; + const char *system_name; + struct snd_soc_component *component; + + unsigned int sys_config_size; + + int fw; + bool wmfw_optional; + + struct work_struct boot_work; + int (*pre_run)(struct wm_adsp *dsp); + + bool preloaded; + bool fatal_error; + + struct list_head compr_list; + struct list_head buffer_list; + + /* + * Flag indicating the preloader widget only needs power toggled + * on state change rather than held on for the duration of the + * preload, useful for devices that can retain firmware memory + * across power down. + */ + bool toggle_preload; +}; + +#define WM_ADSP1(wname, num) \ + SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \ + wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) + +#define WM_ADSP2_PRELOAD_SWITCH(wname, num) \ + SOC_SINGLE_EXT(wname " Preload Switch", SND_SOC_NOPM, num, 1, 0, \ + wm_adsp2_preloader_get, wm_adsp2_preloader_put) + +#define WM_ADSP2(wname, num, event_fn) \ + SND_SOC_DAPM_SPK(wname " Preload", NULL), \ +{ .id = snd_soc_dapm_supply, .name = wname " Preloader", \ + .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \ + .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD, \ + .subseq = 100, /* Ensure we run after SYSCLK supply widget */ }, \ +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp_event, \ + .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } + +#define WM_ADSP_FW_CONTROL(dspname, num) \ + SOC_ENUM_EXT(dspname " Firmware", wm_adsp_fw_enum[num], \ + wm_adsp_fw_get, wm_adsp_fw_put) + +extern const struct soc_enum wm_adsp_fw_enum[]; + +int wm_adsp1_init(struct wm_adsp *dsp); +int wm_adsp2_init(struct wm_adsp *dsp); +void wm_adsp2_remove(struct wm_adsp *dsp); +int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component); +int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component); +int wm_halo_init(struct wm_adsp *dsp); + +int wm_adsp1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +int wm_adsp_early_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware); +void wm_adsp_power_down(struct wm_adsp *dsp); + +irqreturn_t wm_adsp2_bus_error(int irq, void *data); +irqreturn_t wm_halo_bus_error(int irq, void *data); +irqreturn_t wm_halo_wdt_expire(int irq, void *data); + +int wm_adsp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + +int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq); + +int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream); +int wm_adsp_compr_free(struct snd_soc_component *component, + struct snd_compr_stream *stream); +int wm_adsp_compr_set_params(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_params *params); +int wm_adsp_compr_get_caps(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_caps *caps); +int wm_adsp_compr_trigger(struct snd_soc_component *component, + struct snd_compr_stream *stream, int cmd); +int wm_adsp_compr_handle_irq(struct wm_adsp *dsp); +int wm_adsp_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp); +int wm_adsp_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *stream, + char __user *buf, size_t count); +int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, + unsigned int alg, void *buf, size_t len); +int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, + unsigned int alg, void *buf, size_t len); + +#endif diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c new file mode 100644 index 0000000000..0c881846f4 --- /dev/null +++ b/sound/soc/codecs/wm_hubs.c @@ -0,0 +1,1309 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * wm_hubs.c -- WM8993/4 common code + * + * Copyright 2009-12 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm8993.h" +#include "wm_hubs.h" + +const DECLARE_TLV_DB_SCALE(wm_hubs_spkmix_tlv, -300, 300, 0); +EXPORT_SYMBOL_GPL(wm_hubs_spkmix_tlv); + +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1650, 150, 0); +static const DECLARE_TLV_DB_SCALE(inmix_sw_tlv, 0, 3000, 0); +static const DECLARE_TLV_DB_SCALE(inmix_tlv, -1500, 300, 1); +static const DECLARE_TLV_DB_SCALE(earpiece_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(outmix_tlv, -2100, 300, 0); +static const DECLARE_TLV_DB_SCALE(spkmixout_tlv, -1800, 600, 1); +static const DECLARE_TLV_DB_SCALE(outpga_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_RANGE(spkboost_tlv, + 0, 6, TLV_DB_SCALE_ITEM(0, 150, 0), + 7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0) +); +static const DECLARE_TLV_DB_SCALE(line_tlv, -600, 600, 0); + +static const char *speaker_ref_text[] = { + "SPKVDD/2", + "VMID", +}; + +static SOC_ENUM_SINGLE_DECL(speaker_ref, + WM8993_SPEAKER_MIXER, 8, speaker_ref_text); + +static const char *speaker_mode_text[] = { + "Class D", + "Class AB", +}; + +static SOC_ENUM_SINGLE_DECL(speaker_mode, + WM8993_SPKMIXR_ATTENUATION, 8, speaker_mode_text); + +static void wait_for_dc_servo(struct snd_soc_component *component, unsigned int op) +{ + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + unsigned int reg; + int count = 0; + int timeout; + unsigned int val; + + val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1; + + /* Trigger the command */ + snd_soc_component_write(component, WM8993_DC_SERVO_0, val); + + dev_dbg(component->dev, "Waiting for DC servo...\n"); + + if (hubs->dcs_done_irq) + timeout = 4; + else + timeout = 400; + + do { + count++; + + if (hubs->dcs_done_irq) + wait_for_completion_timeout(&hubs->dcs_done, + msecs_to_jiffies(250)); + else + msleep(1); + + reg = snd_soc_component_read(component, WM8993_DC_SERVO_0); + dev_dbg(component->dev, "DC servo: %x\n", reg); + } while (reg & op && count < timeout); + + if (reg & op) + dev_err(component->dev, "Timed out waiting for DC Servo %x\n", + op); +} + +irqreturn_t wm_hubs_dcs_done(int irq, void *data) +{ + struct wm_hubs_data *hubs = data; + + complete(&hubs->dcs_done); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(wm_hubs_dcs_done); + +static bool wm_hubs_dac_hp_direct(struct snd_soc_component *component) +{ + int reg; + + /* If we're going via the mixer we'll need to do additional checks */ + reg = snd_soc_component_read(component, WM8993_OUTPUT_MIXER1); + if (!(reg & WM8993_DACL_TO_HPOUT1L)) { + if (reg & ~WM8993_DACL_TO_MIXOUTL) { + dev_vdbg(component->dev, "Analogue paths connected: %x\n", + reg & ~WM8993_DACL_TO_HPOUT1L); + return false; + } else { + dev_vdbg(component->dev, "HPL connected to mixer\n"); + } + } else { + dev_vdbg(component->dev, "HPL connected to DAC\n"); + } + + reg = snd_soc_component_read(component, WM8993_OUTPUT_MIXER2); + if (!(reg & WM8993_DACR_TO_HPOUT1R)) { + if (reg & ~WM8993_DACR_TO_MIXOUTR) { + dev_vdbg(component->dev, "Analogue paths connected: %x\n", + reg & ~WM8993_DACR_TO_HPOUT1R); + return false; + } else { + dev_vdbg(component->dev, "HPR connected to mixer\n"); + } + } else { + dev_vdbg(component->dev, "HPR connected to DAC\n"); + } + + return true; +} + +struct wm_hubs_dcs_cache { + struct list_head list; + unsigned int left; + unsigned int right; + u16 dcs_cfg; +}; + +static bool wm_hubs_dcs_cache_get(struct snd_soc_component *component, + struct wm_hubs_dcs_cache **entry) +{ + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + struct wm_hubs_dcs_cache *cache; + unsigned int left, right; + + left = snd_soc_component_read(component, WM8993_LEFT_OUTPUT_VOLUME); + left &= WM8993_HPOUT1L_VOL_MASK; + + right = snd_soc_component_read(component, WM8993_RIGHT_OUTPUT_VOLUME); + right &= WM8993_HPOUT1R_VOL_MASK; + + list_for_each_entry(cache, &hubs->dcs_cache, list) { + if (cache->left != left || cache->right != right) + continue; + + *entry = cache; + return true; + } + + return false; +} + +static void wm_hubs_dcs_cache_set(struct snd_soc_component *component, u16 dcs_cfg) +{ + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + struct wm_hubs_dcs_cache *cache; + + if (hubs->no_cache_dac_hp_direct) + return; + + cache = devm_kzalloc(component->dev, sizeof(*cache), GFP_KERNEL); + if (!cache) + return; + + cache->left = snd_soc_component_read(component, WM8993_LEFT_OUTPUT_VOLUME); + cache->left &= WM8993_HPOUT1L_VOL_MASK; + + cache->right = snd_soc_component_read(component, WM8993_RIGHT_OUTPUT_VOLUME); + cache->right &= WM8993_HPOUT1R_VOL_MASK; + + cache->dcs_cfg = dcs_cfg; + + list_add_tail(&cache->list, &hubs->dcs_cache); +} + +static int wm_hubs_read_dc_servo(struct snd_soc_component *component, + u16 *reg_l, u16 *reg_r) +{ + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + u16 dcs_reg, reg; + int ret = 0; + + switch (hubs->dcs_readback_mode) { + case 2: + dcs_reg = WM8994_DC_SERVO_4E; + break; + case 1: + dcs_reg = WM8994_DC_SERVO_READBACK; + break; + default: + dcs_reg = WM8993_DC_SERVO_3; + break; + } + + /* Different chips in the family support different readback + * methods. + */ + switch (hubs->dcs_readback_mode) { + case 0: + *reg_l = snd_soc_component_read(component, WM8993_DC_SERVO_READBACK_1) + & WM8993_DCS_INTEG_CHAN_0_MASK; + *reg_r = snd_soc_component_read(component, WM8993_DC_SERVO_READBACK_2) + & WM8993_DCS_INTEG_CHAN_1_MASK; + break; + case 2: + case 1: + reg = snd_soc_component_read(component, dcs_reg); + *reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK) + >> WM8993_DCS_DAC_WR_VAL_1_SHIFT; + *reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK; + break; + default: + WARN(1, "Unknown DCS readback method\n"); + ret = -1; + } + return ret; +} + +/* + * Startup calibration of the DC servo + */ +static void enable_dc_servo(struct snd_soc_component *component) +{ + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + struct wm_hubs_dcs_cache *cache; + s8 offset; + u16 reg_l, reg_r, dcs_cfg, dcs_reg; + + switch (hubs->dcs_readback_mode) { + case 2: + dcs_reg = WM8994_DC_SERVO_4E; + break; + default: + dcs_reg = WM8993_DC_SERVO_3; + break; + } + + /* If we're using a digital only path and have a previously + * callibrated DC servo offset stored then use that. */ + if (wm_hubs_dac_hp_direct(component) && + wm_hubs_dcs_cache_get(component, &cache)) { + dev_dbg(component->dev, "Using cached DCS offset %x for %d,%d\n", + cache->dcs_cfg, cache->left, cache->right); + snd_soc_component_write(component, dcs_reg, cache->dcs_cfg); + wait_for_dc_servo(component, + WM8993_DCS_TRIG_DAC_WR_0 | + WM8993_DCS_TRIG_DAC_WR_1); + return; + } + + if (hubs->series_startup) { + /* Set for 32 series updates */ + snd_soc_component_update_bits(component, WM8993_DC_SERVO_1, + WM8993_DCS_SERIES_NO_01_MASK, + 32 << WM8993_DCS_SERIES_NO_01_SHIFT); + wait_for_dc_servo(component, + WM8993_DCS_TRIG_SERIES_0 | + WM8993_DCS_TRIG_SERIES_1); + } else { + wait_for_dc_servo(component, + WM8993_DCS_TRIG_STARTUP_0 | + WM8993_DCS_TRIG_STARTUP_1); + } + + if (wm_hubs_read_dc_servo(component, ®_l, ®_r) < 0) + return; + + dev_dbg(component->dev, "DCS input: %x %x\n", reg_l, reg_r); + + /* Apply correction to DC servo result */ + if (hubs->dcs_codes_l || hubs->dcs_codes_r) { + dev_dbg(component->dev, + "Applying %d/%d code DC servo correction\n", + hubs->dcs_codes_l, hubs->dcs_codes_r); + + /* HPOUT1R */ + offset = (s8)reg_r; + dev_dbg(component->dev, "DCS right %d->%d\n", offset, + offset + hubs->dcs_codes_r); + offset += hubs->dcs_codes_r; + dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT; + + /* HPOUT1L */ + offset = (s8)reg_l; + dev_dbg(component->dev, "DCS left %d->%d\n", offset, + offset + hubs->dcs_codes_l); + offset += hubs->dcs_codes_l; + dcs_cfg |= (u8)offset; + + dev_dbg(component->dev, "DCS result: %x\n", dcs_cfg); + + /* Do it */ + snd_soc_component_write(component, dcs_reg, dcs_cfg); + wait_for_dc_servo(component, + WM8993_DCS_TRIG_DAC_WR_0 | + WM8993_DCS_TRIG_DAC_WR_1); + } else { + dcs_cfg = reg_r << WM8993_DCS_DAC_WR_VAL_1_SHIFT; + dcs_cfg |= reg_l; + } + + /* Save the callibrated offset if we're in class W mode and + * therefore don't have any analogue signal mixed in. */ + if (wm_hubs_dac_hp_direct(component)) + wm_hubs_dcs_cache_set(component, dcs_cfg); +} + +/* + * Update the DC servo calibration on gain changes + */ +static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + + /* If we're applying an offset correction then updating the + * callibration would be likely to introduce further offsets. */ + if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update) + return ret; + + /* Only need to do this if the outputs are active */ + if (snd_soc_component_read(component, WM8993_POWER_MANAGEMENT_1) + & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA)) + snd_soc_component_update_bits(component, + WM8993_DC_SERVO_0, + WM8993_DCS_TRIG_SINGLE_0 | + WM8993_DCS_TRIG_SINGLE_1, + WM8993_DCS_TRIG_SINGLE_0 | + WM8993_DCS_TRIG_SINGLE_1); + + return ret; +} + +static const struct snd_kcontrol_new analogue_snd_controls[] = { +SOC_SINGLE_TLV("IN1L Volume", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 0, 31, 0, + inpga_tlv), +SOC_SINGLE("IN1L Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 1), +SOC_SINGLE("IN1L ZC Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 6, 1, 0), + +SOC_SINGLE_TLV("IN1R Volume", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 0, 31, 0, + inpga_tlv), +SOC_SINGLE("IN1R Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 1), +SOC_SINGLE("IN1R ZC Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 6, 1, 0), + + +SOC_SINGLE_TLV("IN2L Volume", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 0, 31, 0, + inpga_tlv), +SOC_SINGLE("IN2L Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 1), +SOC_SINGLE("IN2L ZC Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 6, 1, 0), + +SOC_SINGLE_TLV("IN2R Volume", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 0, 31, 0, + inpga_tlv), +SOC_SINGLE("IN2R Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 1), +SOC_SINGLE("IN2R ZC Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 6, 1, 0), + +SOC_SINGLE_TLV("MIXINL IN2L Volume", WM8993_INPUT_MIXER3, 7, 1, 0, + inmix_sw_tlv), +SOC_SINGLE_TLV("MIXINL IN1L Volume", WM8993_INPUT_MIXER3, 4, 1, 0, + inmix_sw_tlv), +SOC_SINGLE_TLV("MIXINL Output Record Volume", WM8993_INPUT_MIXER3, 0, 7, 0, + inmix_tlv), +SOC_SINGLE_TLV("MIXINL IN1LP Volume", WM8993_INPUT_MIXER5, 6, 7, 0, inmix_tlv), +SOC_SINGLE_TLV("MIXINL Direct Voice Volume", WM8993_INPUT_MIXER5, 0, 6, 0, + inmix_tlv), + +SOC_SINGLE_TLV("MIXINR IN2R Volume", WM8993_INPUT_MIXER4, 7, 1, 0, + inmix_sw_tlv), +SOC_SINGLE_TLV("MIXINR IN1R Volume", WM8993_INPUT_MIXER4, 4, 1, 0, + inmix_sw_tlv), +SOC_SINGLE_TLV("MIXINR Output Record Volume", WM8993_INPUT_MIXER4, 0, 7, 0, + inmix_tlv), +SOC_SINGLE_TLV("MIXINR IN1RP Volume", WM8993_INPUT_MIXER6, 6, 7, 0, inmix_tlv), +SOC_SINGLE_TLV("MIXINR Direct Voice Volume", WM8993_INPUT_MIXER6, 0, 6, 0, + inmix_tlv), + +SOC_SINGLE_TLV("Left Output Mixer IN2RN Volume", WM8993_OUTPUT_MIXER5, 6, 7, 1, + outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer IN2LN Volume", WM8993_OUTPUT_MIXER3, 6, 7, 1, + outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer IN2LP Volume", WM8993_OUTPUT_MIXER3, 9, 7, 1, + outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer IN1L Volume", WM8993_OUTPUT_MIXER3, 0, 7, 1, + outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer IN1R Volume", WM8993_OUTPUT_MIXER3, 3, 7, 1, + outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer Right Input Volume", + WM8993_OUTPUT_MIXER5, 3, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer Left Input Volume", + WM8993_OUTPUT_MIXER5, 0, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Left Output Mixer DAC Volume", WM8993_OUTPUT_MIXER5, 9, 7, 1, + outmix_tlv), + +SOC_SINGLE_TLV("Right Output Mixer IN2LN Volume", + WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer IN2RN Volume", + WM8993_OUTPUT_MIXER4, 6, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer IN1L Volume", + WM8993_OUTPUT_MIXER4, 3, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer IN1R Volume", + WM8993_OUTPUT_MIXER4, 0, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer IN2RP Volume", + WM8993_OUTPUT_MIXER4, 9, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer Left Input Volume", + WM8993_OUTPUT_MIXER6, 3, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer Right Input Volume", + WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv), +SOC_SINGLE_TLV("Right Output Mixer DAC Volume", + WM8993_OUTPUT_MIXER6, 9, 7, 1, outmix_tlv), + +SOC_DOUBLE_R_TLV("Output Volume", WM8993_LEFT_OPGA_VOLUME, + WM8993_RIGHT_OPGA_VOLUME, 0, 63, 0, outpga_tlv), +SOC_DOUBLE_R("Output Switch", WM8993_LEFT_OPGA_VOLUME, + WM8993_RIGHT_OPGA_VOLUME, 6, 1, 0), +SOC_DOUBLE_R("Output ZC Switch", WM8993_LEFT_OPGA_VOLUME, + WM8993_RIGHT_OPGA_VOLUME, 7, 1, 0), + +SOC_SINGLE("Earpiece Switch", WM8993_HPOUT2_VOLUME, 5, 1, 1), +SOC_SINGLE_TLV("Earpiece Volume", WM8993_HPOUT2_VOLUME, 4, 1, 1, earpiece_tlv), + +SOC_SINGLE_TLV("SPKL Input Volume", WM8993_SPKMIXL_ATTENUATION, + 5, 1, 1, wm_hubs_spkmix_tlv), +SOC_SINGLE_TLV("SPKL IN1LP Volume", WM8993_SPKMIXL_ATTENUATION, + 4, 1, 1, wm_hubs_spkmix_tlv), +SOC_SINGLE_TLV("SPKL Output Volume", WM8993_SPKMIXL_ATTENUATION, + 3, 1, 1, wm_hubs_spkmix_tlv), + +SOC_SINGLE_TLV("SPKR Input Volume", WM8993_SPKMIXR_ATTENUATION, + 5, 1, 1, wm_hubs_spkmix_tlv), +SOC_SINGLE_TLV("SPKR IN1RP Volume", WM8993_SPKMIXR_ATTENUATION, + 4, 1, 1, wm_hubs_spkmix_tlv), +SOC_SINGLE_TLV("SPKR Output Volume", WM8993_SPKMIXR_ATTENUATION, + 3, 1, 1, wm_hubs_spkmix_tlv), + +SOC_DOUBLE_R_TLV("Speaker Mixer Volume", + WM8993_SPKMIXL_ATTENUATION, WM8993_SPKMIXR_ATTENUATION, + 0, 3, 1, spkmixout_tlv), +SOC_DOUBLE_R_TLV("Speaker Volume", + WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT, + 0, 63, 0, outpga_tlv), +SOC_DOUBLE_R("Speaker Switch", + WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT, + 6, 1, 0), +SOC_DOUBLE_R("Speaker ZC Switch", + WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT, + 7, 1, 0), +SOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 3, 0, 7, 0, + spkboost_tlv), +SOC_ENUM("Speaker Reference", speaker_ref), +SOC_ENUM("Speaker Mode", speaker_mode), + +SOC_DOUBLE_R_EXT_TLV("Headphone Volume", + WM8993_LEFT_OUTPUT_VOLUME, WM8993_RIGHT_OUTPUT_VOLUME, + 0, 63, 0, snd_soc_get_volsw, wm8993_put_dc_servo, + outpga_tlv), + +SOC_DOUBLE_R("Headphone Switch", WM8993_LEFT_OUTPUT_VOLUME, + WM8993_RIGHT_OUTPUT_VOLUME, 6, 1, 0), +SOC_DOUBLE_R("Headphone ZC Switch", WM8993_LEFT_OUTPUT_VOLUME, + WM8993_RIGHT_OUTPUT_VOLUME, 7, 1, 0), + +SOC_SINGLE("LINEOUT1N Switch", WM8993_LINE_OUTPUTS_VOLUME, 6, 1, 1), +SOC_SINGLE("LINEOUT1P Switch", WM8993_LINE_OUTPUTS_VOLUME, 5, 1, 1), +SOC_SINGLE_TLV("LINEOUT1 Volume", WM8993_LINE_OUTPUTS_VOLUME, 4, 1, 1, + line_tlv), + +SOC_SINGLE("LINEOUT2N Switch", WM8993_LINE_OUTPUTS_VOLUME, 2, 1, 1), +SOC_SINGLE("LINEOUT2P Switch", WM8993_LINE_OUTPUTS_VOLUME, 1, 1, 1), +SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1, + line_tlv), +}; + +static int hp_supply_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (hubs->hp_startup_mode) { + case 0: + break; + case 1: + /* Enable the headphone amp */ + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1, + WM8993_HPOUT1L_ENA | + WM8993_HPOUT1R_ENA, + WM8993_HPOUT1L_ENA | + WM8993_HPOUT1R_ENA); + + /* Enable the second stage */ + snd_soc_component_update_bits(component, WM8993_ANALOGUE_HP_0, + WM8993_HPOUT1L_DLY | + WM8993_HPOUT1R_DLY, + WM8993_HPOUT1L_DLY | + WM8993_HPOUT1R_DLY); + break; + default: + dev_err(component->dev, "Unknown HP startup mode %d\n", + hubs->hp_startup_mode); + break; + } + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, WM8993_CHARGE_PUMP_1, + WM8993_CP_ENA, 0); + break; + } + + return 0; +} + +static int hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + unsigned int reg = snd_soc_component_read(component, WM8993_ANALOGUE_HP_0); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_component_update_bits(component, WM8993_CHARGE_PUMP_1, + WM8993_CP_ENA, WM8993_CP_ENA); + + msleep(5); + + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1, + WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA, + WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA); + + reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY; + snd_soc_component_write(component, WM8993_ANALOGUE_HP_0, reg); + + snd_soc_component_update_bits(component, WM8993_DC_SERVO_1, + WM8993_DCS_TIMER_PERIOD_01_MASK, 0); + + enable_dc_servo(component); + + reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT | + WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT; + snd_soc_component_write(component, WM8993_ANALOGUE_HP_0, reg); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_update_bits(component, WM8993_ANALOGUE_HP_0, + WM8993_HPOUT1L_OUTP | + WM8993_HPOUT1R_OUTP | + WM8993_HPOUT1L_RMV_SHORT | + WM8993_HPOUT1R_RMV_SHORT, 0); + + snd_soc_component_update_bits(component, WM8993_ANALOGUE_HP_0, + WM8993_HPOUT1L_DLY | + WM8993_HPOUT1R_DLY, 0); + + snd_soc_component_write(component, WM8993_DC_SERVO_0, 0); + + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_1, + WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA, + 0); + break; + } + + return 0; +} + +static int earpiece_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u16 reg = snd_soc_component_read(component, WM8993_ANTIPOP1) & ~WM8993_HPOUT2_IN_ENA; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + reg |= WM8993_HPOUT2_IN_ENA; + snd_soc_component_write(component, WM8993_ANTIPOP1, reg); + udelay(50); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_write(component, WM8993_ANTIPOP1, reg); + break; + + default: + WARN(1, "Invalid event %d\n", event); + break; + } + + return 0; +} + +static int lineout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + bool *flag; + + switch (w->shift) { + case WM8993_LINEOUT1N_ENA_SHIFT: + flag = &hubs->lineout1n_ena; + break; + case WM8993_LINEOUT1P_ENA_SHIFT: + flag = &hubs->lineout1p_ena; + break; + case WM8993_LINEOUT2N_ENA_SHIFT: + flag = &hubs->lineout2n_ena; + break; + case WM8993_LINEOUT2P_ENA_SHIFT: + flag = &hubs->lineout2p_ena; + break; + default: + WARN(1, "Unknown line output"); + return -EINVAL; + } + + *flag = SND_SOC_DAPM_EVENT_ON(event); + + return 0; +} + +static int micbias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + + switch (w->shift) { + case WM8993_MICB1_ENA_SHIFT: + if (hubs->micb1_delay) + msleep(hubs->micb1_delay); + break; + case WM8993_MICB2_ENA_SHIFT: + if (hubs->micb2_delay) + msleep(hubs->micb2_delay); + break; + default: + return -EINVAL; + } + + return 0; +} + +void wm_hubs_update_class_w(struct snd_soc_component *component) +{ + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + int enable = WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ; + + if (!wm_hubs_dac_hp_direct(component)) + enable = false; + + if (hubs->check_class_w_digital && !hubs->check_class_w_digital(component)) + enable = false; + + dev_vdbg(component->dev, "Class W %s\n", enable ? "enabled" : "disabled"); + + snd_soc_component_update_bits(component, WM8993_CLASS_W_0, + WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable); + + snd_soc_component_write(component, WM8993_LEFT_OUTPUT_VOLUME, + snd_soc_component_read(component, WM8993_LEFT_OUTPUT_VOLUME)); + snd_soc_component_write(component, WM8993_RIGHT_OUTPUT_VOLUME, + snd_soc_component_read(component, WM8993_RIGHT_OUTPUT_VOLUME)); +} +EXPORT_SYMBOL_GPL(wm_hubs_update_class_w); + +#define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \ + SOC_SINGLE_EXT(xname, reg, shift, max, invert, \ + snd_soc_dapm_get_volsw, class_w_put_volsw) + +static int class_w_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + int ret; + + ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); + + wm_hubs_update_class_w(component); + + return ret; +} + +#define WM_HUBS_ENUM_W(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_double, \ + .put = class_w_put_double, \ + .private_value = (unsigned long)&xenum } + +static int class_w_put_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_dapm_kcontrol_component(kcontrol); + int ret; + + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + + wm_hubs_update_class_w(component); + + return ret; +} + +static const char *hp_mux_text[] = { + "Mixer", + "DAC", +}; + +static SOC_ENUM_SINGLE_DECL(hpl_enum, + WM8993_OUTPUT_MIXER1, 8, hp_mux_text); + +const struct snd_kcontrol_new wm_hubs_hpl_mux = + WM_HUBS_ENUM_W("Left Headphone Mux", hpl_enum); +EXPORT_SYMBOL_GPL(wm_hubs_hpl_mux); + +static SOC_ENUM_SINGLE_DECL(hpr_enum, + WM8993_OUTPUT_MIXER2, 8, hp_mux_text); + +const struct snd_kcontrol_new wm_hubs_hpr_mux = + WM_HUBS_ENUM_W("Right Headphone Mux", hpr_enum); +EXPORT_SYMBOL_GPL(wm_hubs_hpr_mux); + +static const struct snd_kcontrol_new in1l_pga[] = { +SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0), +SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0), +}; + +static const struct snd_kcontrol_new in1r_pga[] = { +SOC_DAPM_SINGLE("IN1RP Switch", WM8993_INPUT_MIXER2, 1, 1, 0), +SOC_DAPM_SINGLE("IN1RN Switch", WM8993_INPUT_MIXER2, 0, 1, 0), +}; + +static const struct snd_kcontrol_new in2l_pga[] = { +SOC_DAPM_SINGLE("IN2LP Switch", WM8993_INPUT_MIXER2, 7, 1, 0), +SOC_DAPM_SINGLE("IN2LN Switch", WM8993_INPUT_MIXER2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new in2r_pga[] = { +SOC_DAPM_SINGLE("IN2RP Switch", WM8993_INPUT_MIXER2, 3, 1, 0), +SOC_DAPM_SINGLE("IN2RN Switch", WM8993_INPUT_MIXER2, 2, 1, 0), +}; + +static const struct snd_kcontrol_new mixinl[] = { +SOC_DAPM_SINGLE("IN2L Switch", WM8993_INPUT_MIXER3, 8, 1, 0), +SOC_DAPM_SINGLE("IN1L Switch", WM8993_INPUT_MIXER3, 5, 1, 0), +}; + +static const struct snd_kcontrol_new mixinr[] = { +SOC_DAPM_SINGLE("IN2R Switch", WM8993_INPUT_MIXER4, 8, 1, 0), +SOC_DAPM_SINGLE("IN1R Switch", WM8993_INPUT_MIXER4, 5, 1, 0), +}; + +static const struct snd_kcontrol_new left_output_mixer[] = { +WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0), +WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0), +WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0), +WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0), +WM_HUBS_SINGLE_W("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0), +WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0), +WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0), +WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new right_output_mixer[] = { +WM_HUBS_SINGLE_W("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0), +WM_HUBS_SINGLE_W("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0), +WM_HUBS_SINGLE_W("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0), +WM_HUBS_SINGLE_W("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0), +WM_HUBS_SINGLE_W("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0), +WM_HUBS_SINGLE_W("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0), +WM_HUBS_SINGLE_W("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0), +WM_HUBS_SINGLE_W("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0), +}; + +static const struct snd_kcontrol_new earpiece_mixer[] = { +SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_HPOUT2_MIXER, 5, 1, 0), +SOC_DAPM_SINGLE("Left Output Switch", WM8993_HPOUT2_MIXER, 4, 1, 0), +SOC_DAPM_SINGLE("Right Output Switch", WM8993_HPOUT2_MIXER, 3, 1, 0), +}; + +static const struct snd_kcontrol_new left_speaker_boost[] = { +SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 5, 1, 0), +SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 4, 1, 0), +SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 3, 1, 0), +}; + +static const struct snd_kcontrol_new right_speaker_boost[] = { +SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 2, 1, 0), +SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 1, 1, 0), +SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 0, 1, 0), +}; + +static const struct snd_kcontrol_new line1_mix[] = { +SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER1, 2, 1, 0), +SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER1, 1, 1, 0), +SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new line1n_mix[] = { +SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 6, 1, 0), +SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER1, 5, 1, 0), +}; + +static const struct snd_kcontrol_new line1p_mix[] = { +SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0), +}; + +static const struct snd_kcontrol_new line2_mix[] = { +SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER2, 2, 1, 0), +SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER2, 1, 1, 0), +SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0), +}; + +static const struct snd_kcontrol_new line2n_mix[] = { +SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 5, 1, 0), +SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 6, 1, 0), +}; + +static const struct snd_kcontrol_new line2p_mix[] = { +SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 0, 1, 0), +}; + +static const struct snd_soc_dapm_widget analogue_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1LN"), +SND_SOC_DAPM_INPUT("IN1LP"), +SND_SOC_DAPM_INPUT("IN2LN"), +SND_SOC_DAPM_INPUT("IN2LP:VXRN"), +SND_SOC_DAPM_INPUT("IN1RN"), +SND_SOC_DAPM_INPUT("IN1RP"), +SND_SOC_DAPM_INPUT("IN2RN"), +SND_SOC_DAPM_INPUT("IN2RP:VXRP"), + +SND_SOC_DAPM_SUPPLY("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0, + micbias_event, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_SUPPLY("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0, + micbias_event, SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0, + in1l_pga, ARRAY_SIZE(in1l_pga)), +SND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0, + in1r_pga, ARRAY_SIZE(in1r_pga)), + +SND_SOC_DAPM_MIXER("IN2L PGA", WM8993_POWER_MANAGEMENT_2, 7, 0, + in2l_pga, ARRAY_SIZE(in2l_pga)), +SND_SOC_DAPM_MIXER("IN2R PGA", WM8993_POWER_MANAGEMENT_2, 5, 0, + in2r_pga, ARRAY_SIZE(in2r_pga)), + +SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0, + mixinl, ARRAY_SIZE(mixinl)), +SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0, + mixinr, ARRAY_SIZE(mixinr)), + +SND_SOC_DAPM_MIXER("Left Output Mixer", WM8993_POWER_MANAGEMENT_3, 5, 0, + left_output_mixer, ARRAY_SIZE(left_output_mixer)), +SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0, + right_output_mixer, ARRAY_SIZE(right_output_mixer)), + +SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0), + +SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_OUT_DRV_E("Headphone PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0, + earpiece_mixer, ARRAY_SIZE(earpiece_mixer)), +SND_SOC_DAPM_PGA_E("Earpiece Driver", WM8993_POWER_MANAGEMENT_1, 11, 0, + NULL, 0, earpiece_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_MIXER("SPKL Boost", SND_SOC_NOPM, 0, 0, + left_speaker_boost, ARRAY_SIZE(left_speaker_boost)), +SND_SOC_DAPM_MIXER("SPKR Boost", SND_SOC_NOPM, 0, 0, + right_speaker_boost, ARRAY_SIZE(right_speaker_boost)), + +SND_SOC_DAPM_SUPPLY("TSHUT", WM8993_POWER_MANAGEMENT_2, 14, 0, NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPKL Driver", WM8993_POWER_MANAGEMENT_1, 12, 0, + NULL, 0), +SND_SOC_DAPM_OUT_DRV("SPKR Driver", WM8993_POWER_MANAGEMENT_1, 13, 0, + NULL, 0), + +SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0, + line1_mix, ARRAY_SIZE(line1_mix)), +SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0, + line2_mix, ARRAY_SIZE(line2_mix)), + +SND_SOC_DAPM_MIXER("LINEOUT1N Mixer", SND_SOC_NOPM, 0, 0, + line1n_mix, ARRAY_SIZE(line1n_mix)), +SND_SOC_DAPM_MIXER("LINEOUT1P Mixer", SND_SOC_NOPM, 0, 0, + line1p_mix, ARRAY_SIZE(line1p_mix)), +SND_SOC_DAPM_MIXER("LINEOUT2N Mixer", SND_SOC_NOPM, 0, 0, + line2n_mix, ARRAY_SIZE(line2n_mix)), +SND_SOC_DAPM_MIXER("LINEOUT2P Mixer", SND_SOC_NOPM, 0, 0, + line2p_mix, ARRAY_SIZE(line2p_mix)), + +SND_SOC_DAPM_OUT_DRV_E("LINEOUT1N Driver", WM8993_POWER_MANAGEMENT_3, 13, 0, + NULL, 0, lineout_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_OUT_DRV_E("LINEOUT1P Driver", WM8993_POWER_MANAGEMENT_3, 12, 0, + NULL, 0, lineout_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_OUT_DRV_E("LINEOUT2N Driver", WM8993_POWER_MANAGEMENT_3, 11, 0, + NULL, 0, lineout_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_OUT_DRV_E("LINEOUT2P Driver", WM8993_POWER_MANAGEMENT_3, 10, 0, + NULL, 0, lineout_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + +SND_SOC_DAPM_OUTPUT("SPKOUTLP"), +SND_SOC_DAPM_OUTPUT("SPKOUTLN"), +SND_SOC_DAPM_OUTPUT("SPKOUTRP"), +SND_SOC_DAPM_OUTPUT("SPKOUTRN"), +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("HPOUT2P"), +SND_SOC_DAPM_OUTPUT("HPOUT2N"), +SND_SOC_DAPM_OUTPUT("LINEOUT1P"), +SND_SOC_DAPM_OUTPUT("LINEOUT1N"), +SND_SOC_DAPM_OUTPUT("LINEOUT2P"), +SND_SOC_DAPM_OUTPUT("LINEOUT2N"), +}; + +static const struct snd_soc_dapm_route analogue_routes[] = { + { "MICBIAS1", NULL, "CLK_SYS" }, + { "MICBIAS2", NULL, "CLK_SYS" }, + + { "IN1L PGA", "IN1LP Switch", "IN1LP" }, + { "IN1L PGA", "IN1LN Switch", "IN1LN" }, + + { "IN1L PGA", NULL, "VMID" }, + { "IN1R PGA", NULL, "VMID" }, + { "IN2L PGA", NULL, "VMID" }, + { "IN2R PGA", NULL, "VMID" }, + + { "IN1R PGA", "IN1RP Switch", "IN1RP" }, + { "IN1R PGA", "IN1RN Switch", "IN1RN" }, + + { "IN2L PGA", "IN2LP Switch", "IN2LP:VXRN" }, + { "IN2L PGA", "IN2LN Switch", "IN2LN" }, + + { "IN2R PGA", "IN2RP Switch", "IN2RP:VXRP" }, + { "IN2R PGA", "IN2RN Switch", "IN2RN" }, + + { "Direct Voice", NULL, "IN2LP:VXRN" }, + { "Direct Voice", NULL, "IN2RP:VXRP" }, + + { "MIXINL", "IN1L Switch", "IN1L PGA" }, + { "MIXINL", "IN2L Switch", "IN2L PGA" }, + { "MIXINL", NULL, "Direct Voice" }, + { "MIXINL", NULL, "IN1LP" }, + { "MIXINL", NULL, "Left Output Mixer" }, + { "MIXINL", NULL, "VMID" }, + + { "MIXINR", "IN1R Switch", "IN1R PGA" }, + { "MIXINR", "IN2R Switch", "IN2R PGA" }, + { "MIXINR", NULL, "Direct Voice" }, + { "MIXINR", NULL, "IN1RP" }, + { "MIXINR", NULL, "Right Output Mixer" }, + { "MIXINR", NULL, "VMID" }, + + { "ADCL", NULL, "MIXINL" }, + { "ADCR", NULL, "MIXINR" }, + + { "Left Output Mixer", "Left Input Switch", "MIXINL" }, + { "Left Output Mixer", "Right Input Switch", "MIXINR" }, + { "Left Output Mixer", "IN2RN Switch", "IN2RN" }, + { "Left Output Mixer", "IN2LN Switch", "IN2LN" }, + { "Left Output Mixer", "IN2LP Switch", "IN2LP:VXRN" }, + { "Left Output Mixer", "IN1L Switch", "IN1L PGA" }, + { "Left Output Mixer", "IN1R Switch", "IN1R PGA" }, + + { "Right Output Mixer", "Left Input Switch", "MIXINL" }, + { "Right Output Mixer", "Right Input Switch", "MIXINR" }, + { "Right Output Mixer", "IN2LN Switch", "IN2LN" }, + { "Right Output Mixer", "IN2RN Switch", "IN2RN" }, + { "Right Output Mixer", "IN2RP Switch", "IN2RP:VXRP" }, + { "Right Output Mixer", "IN1L Switch", "IN1L PGA" }, + { "Right Output Mixer", "IN1R Switch", "IN1R PGA" }, + + { "Left Output PGA", NULL, "Left Output Mixer" }, + { "Left Output PGA", NULL, "TOCLK" }, + + { "Right Output PGA", NULL, "Right Output Mixer" }, + { "Right Output PGA", NULL, "TOCLK" }, + + { "Earpiece Mixer", "Direct Voice Switch", "Direct Voice" }, + { "Earpiece Mixer", "Left Output Switch", "Left Output PGA" }, + { "Earpiece Mixer", "Right Output Switch", "Right Output PGA" }, + + { "Earpiece Driver", NULL, "VMID" }, + { "Earpiece Driver", NULL, "Earpiece Mixer" }, + { "HPOUT2N", NULL, "Earpiece Driver" }, + { "HPOUT2P", NULL, "Earpiece Driver" }, + + { "SPKL", "Input Switch", "MIXINL" }, + { "SPKL", "IN1LP Switch", "IN1LP" }, + { "SPKL", "Output Switch", "Left Output PGA" }, + { "SPKL", NULL, "TOCLK" }, + + { "SPKR", "Input Switch", "MIXINR" }, + { "SPKR", "IN1RP Switch", "IN1RP" }, + { "SPKR", "Output Switch", "Right Output PGA" }, + { "SPKR", NULL, "TOCLK" }, + + { "SPKL Boost", "Direct Voice Switch", "Direct Voice" }, + { "SPKL Boost", "SPKL Switch", "SPKL" }, + { "SPKL Boost", "SPKR Switch", "SPKR" }, + + { "SPKR Boost", "Direct Voice Switch", "Direct Voice" }, + { "SPKR Boost", "SPKR Switch", "SPKR" }, + { "SPKR Boost", "SPKL Switch", "SPKL" }, + + { "SPKL Driver", NULL, "VMID" }, + { "SPKL Driver", NULL, "SPKL Boost" }, + { "SPKL Driver", NULL, "CLK_SYS" }, + { "SPKL Driver", NULL, "TSHUT" }, + + { "SPKR Driver", NULL, "VMID" }, + { "SPKR Driver", NULL, "SPKR Boost" }, + { "SPKR Driver", NULL, "CLK_SYS" }, + { "SPKR Driver", NULL, "TSHUT" }, + + { "SPKOUTLP", NULL, "SPKL Driver" }, + { "SPKOUTLN", NULL, "SPKL Driver" }, + { "SPKOUTRP", NULL, "SPKR Driver" }, + { "SPKOUTRN", NULL, "SPKR Driver" }, + + { "Left Headphone Mux", "Mixer", "Left Output PGA" }, + { "Right Headphone Mux", "Mixer", "Right Output PGA" }, + + { "Headphone PGA", NULL, "Left Headphone Mux" }, + { "Headphone PGA", NULL, "Right Headphone Mux" }, + { "Headphone PGA", NULL, "VMID" }, + { "Headphone PGA", NULL, "CLK_SYS" }, + { "Headphone PGA", NULL, "Headphone Supply" }, + + { "HPOUT1L", NULL, "Headphone PGA" }, + { "HPOUT1R", NULL, "Headphone PGA" }, + + { "LINEOUT1N Driver", NULL, "VMID" }, + { "LINEOUT1P Driver", NULL, "VMID" }, + { "LINEOUT2N Driver", NULL, "VMID" }, + { "LINEOUT2P Driver", NULL, "VMID" }, + + { "LINEOUT1N", NULL, "LINEOUT1N Driver" }, + { "LINEOUT1P", NULL, "LINEOUT1P Driver" }, + { "LINEOUT2N", NULL, "LINEOUT2N Driver" }, + { "LINEOUT2P", NULL, "LINEOUT2P Driver" }, +}; + +static const struct snd_soc_dapm_route lineout1_diff_routes[] = { + { "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" }, + { "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" }, + { "LINEOUT1 Mixer", "Output Switch", "Left Output PGA" }, + + { "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" }, + { "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" }, +}; + +static const struct snd_soc_dapm_route lineout1_se_routes[] = { + { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" }, + { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" }, + + { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" }, + + { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" }, + { "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" }, +}; + +static const struct snd_soc_dapm_route lineout2_diff_routes[] = { + { "LINEOUT2 Mixer", "IN1L Switch", "IN1L PGA" }, + { "LINEOUT2 Mixer", "IN1R Switch", "IN1R PGA" }, + { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" }, + + { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" }, + { "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" }, +}; + +static const struct snd_soc_dapm_route lineout2_se_routes[] = { + { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" }, + { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" }, + + { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" }, + + { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" }, + { "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" }, +}; + +int wm_hubs_add_analogue_controls(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + /* Latch volume update bits & default ZC on */ + snd_soc_component_update_bits(component, WM8993_LEFT_LINE_INPUT_1_2_VOLUME, + WM8993_IN1_VU, WM8993_IN1_VU); + snd_soc_component_update_bits(component, WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8993_IN1_VU, WM8993_IN1_VU); + snd_soc_component_update_bits(component, WM8993_LEFT_LINE_INPUT_3_4_VOLUME, + WM8993_IN2_VU, WM8993_IN2_VU); + snd_soc_component_update_bits(component, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8993_IN2_VU, WM8993_IN2_VU); + + snd_soc_component_update_bits(component, WM8993_SPEAKER_VOLUME_LEFT, + WM8993_SPKOUT_VU, WM8993_SPKOUT_VU); + snd_soc_component_update_bits(component, WM8993_SPEAKER_VOLUME_RIGHT, + WM8993_SPKOUT_VU, WM8993_SPKOUT_VU); + + snd_soc_component_update_bits(component, WM8993_LEFT_OUTPUT_VOLUME, + WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC, + WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC); + snd_soc_component_update_bits(component, WM8993_RIGHT_OUTPUT_VOLUME, + WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC, + WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC); + + snd_soc_component_update_bits(component, WM8993_LEFT_OPGA_VOLUME, + WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU, + WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU); + snd_soc_component_update_bits(component, WM8993_RIGHT_OPGA_VOLUME, + WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU, + WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU); + + snd_soc_add_component_controls(component, analogue_snd_controls, + ARRAY_SIZE(analogue_snd_controls)); + + snd_soc_dapm_new_controls(dapm, analogue_dapm_widgets, + ARRAY_SIZE(analogue_dapm_widgets)); + return 0; +} +EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_controls); + +int wm_hubs_add_analogue_routes(struct snd_soc_component *component, + int lineout1_diff, int lineout2_diff) +{ + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + hubs->component = component; + + INIT_LIST_HEAD(&hubs->dcs_cache); + init_completion(&hubs->dcs_done); + + snd_soc_dapm_add_routes(dapm, analogue_routes, + ARRAY_SIZE(analogue_routes)); + + if (lineout1_diff) + snd_soc_dapm_add_routes(dapm, + lineout1_diff_routes, + ARRAY_SIZE(lineout1_diff_routes)); + else + snd_soc_dapm_add_routes(dapm, + lineout1_se_routes, + ARRAY_SIZE(lineout1_se_routes)); + + if (lineout2_diff) + snd_soc_dapm_add_routes(dapm, + lineout2_diff_routes, + ARRAY_SIZE(lineout2_diff_routes)); + else + snd_soc_dapm_add_routes(dapm, + lineout2_se_routes, + ARRAY_SIZE(lineout2_se_routes)); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes); + +int wm_hubs_handle_analogue_pdata(struct snd_soc_component *component, + int lineout1_diff, int lineout2_diff, + int lineout1fb, int lineout2fb, + int jd_scthr, int jd_thr, + int micbias1_delay, int micbias2_delay, + int micbias1_lvl, int micbias2_lvl) +{ + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + + hubs->lineout1_se = !lineout1_diff; + hubs->lineout2_se = !lineout2_diff; + hubs->micb1_delay = micbias1_delay; + hubs->micb2_delay = micbias2_delay; + + if (!lineout1_diff) + snd_soc_component_update_bits(component, WM8993_LINE_MIXER1, + WM8993_LINEOUT1_MODE, + WM8993_LINEOUT1_MODE); + if (!lineout2_diff) + snd_soc_component_update_bits(component, WM8993_LINE_MIXER2, + WM8993_LINEOUT2_MODE, + WM8993_LINEOUT2_MODE); + + if (!lineout1_diff && !lineout2_diff) + snd_soc_component_update_bits(component, WM8993_ANTIPOP1, + WM8993_LINEOUT_VMID_BUF_ENA, + WM8993_LINEOUT_VMID_BUF_ENA); + + if (lineout1fb) + snd_soc_component_update_bits(component, WM8993_ADDITIONAL_CONTROL, + WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB); + + if (lineout2fb) + snd_soc_component_update_bits(component, WM8993_ADDITIONAL_CONTROL, + WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB); + + if (!hubs->micd_scthr) + return 0; + + snd_soc_component_update_bits(component, WM8993_MICBIAS, + WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK | + WM8993_MICB1_LVL | WM8993_MICB2_LVL, + jd_scthr << WM8993_JD_SCTHR_SHIFT | + jd_thr << WM8993_JD_THR_SHIFT | + micbias1_lvl | + micbias2_lvl << WM8993_MICB2_LVL_SHIFT); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata); + +void wm_hubs_vmid_ena(struct snd_soc_component *component) +{ + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + int val = 0; + + if (hubs->lineout1_se) + val |= WM8993_LINEOUT1N_ENA | WM8993_LINEOUT1P_ENA; + + if (hubs->lineout2_se) + val |= WM8993_LINEOUT2N_ENA | WM8993_LINEOUT2P_ENA; + + /* Enable the line outputs while we power up */ + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_3, val, val); +} +EXPORT_SYMBOL_GPL(wm_hubs_vmid_ena); + +void wm_hubs_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct wm_hubs_data *hubs = snd_soc_component_get_drvdata(component); + int mask, val; + + switch (level) { + case SND_SOC_BIAS_STANDBY: + /* Clamp the inputs to VMID while we ramp to charge caps */ + snd_soc_component_update_bits(component, WM8993_INPUTS_CLAMP_REG, + WM8993_INPUTS_CLAMP, WM8993_INPUTS_CLAMP); + break; + + case SND_SOC_BIAS_ON: + /* Turn off any unneeded single ended outputs */ + val = 0; + mask = 0; + + if (hubs->lineout1_se) + mask |= WM8993_LINEOUT1N_ENA | WM8993_LINEOUT1P_ENA; + + if (hubs->lineout2_se) + mask |= WM8993_LINEOUT2N_ENA | WM8993_LINEOUT2P_ENA; + + if (hubs->lineout1_se && hubs->lineout1n_ena) + val |= WM8993_LINEOUT1N_ENA; + + if (hubs->lineout1_se && hubs->lineout1p_ena) + val |= WM8993_LINEOUT1P_ENA; + + if (hubs->lineout2_se && hubs->lineout2n_ena) + val |= WM8993_LINEOUT2N_ENA; + + if (hubs->lineout2_se && hubs->lineout2p_ena) + val |= WM8993_LINEOUT2P_ENA; + + snd_soc_component_update_bits(component, WM8993_POWER_MANAGEMENT_3, + mask, val); + + /* Remove the input clamps */ + snd_soc_component_update_bits(component, WM8993_INPUTS_CLAMP_REG, + WM8993_INPUTS_CLAMP, 0); + break; + + default: + break; + } +} +EXPORT_SYMBOL_GPL(wm_hubs_set_bias_level); + +MODULE_DESCRIPTION("Shared support for Wolfson hubs products"); +MODULE_AUTHOR("Mark Brown "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h new file mode 100644 index 0000000000..a4ed9bd314 --- /dev/null +++ b/sound/soc/codecs/wm_hubs.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * wm_hubs.h -- WM899x common code + * + * Copyright 2009 Wolfson Microelectronics plc + * + * Author: Mark Brown + */ + +#ifndef _WM_HUBS_H +#define _WM_HUBS_H + +#include +#include +#include +#include + +struct snd_soc_component; + +extern const unsigned int wm_hubs_spkmix_tlv[]; + +/* This *must* be the first element of the codec->private_data struct */ +struct wm_hubs_data { + int dcs_codes_l; + int dcs_codes_r; + int dcs_readback_mode; + int hp_startup_mode; + int series_startup; + int no_series_update; + bool micd_scthr; + + bool no_cache_dac_hp_direct; + struct list_head dcs_cache; + bool (*check_class_w_digital)(struct snd_soc_component *); + + int micb1_delay; + int micb2_delay; + + bool lineout1_se; + bool lineout1n_ena; + bool lineout1p_ena; + + bool lineout2_se; + bool lineout2n_ena; + bool lineout2p_ena; + + bool dcs_done_irq; + struct completion dcs_done; + + struct snd_soc_component *component; +}; + +extern int wm_hubs_add_analogue_controls(struct snd_soc_component *); +extern int wm_hubs_add_analogue_routes(struct snd_soc_component *, int, int); +extern int wm_hubs_handle_analogue_pdata(struct snd_soc_component *, + int lineout1_diff, int lineout2_diff, + int lineout1fb, int lineout2fb, + int jd_scthr, int jd_thr, + int micbias1_delay, int micbias2_delay, + int micbias1_lvl, int micbias2_lvl); + +extern irqreturn_t wm_hubs_dcs_done(int irq, void *data); +extern void wm_hubs_vmid_ena(struct snd_soc_component *component); +extern void wm_hubs_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level); +extern void wm_hubs_update_class_w(struct snd_soc_component *component); + +extern const struct snd_kcontrol_new wm_hubs_hpl_mux; +extern const struct snd_kcontrol_new wm_hubs_hpr_mux; + +#endif diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c new file mode 100644 index 0000000000..3c025dabaf --- /dev/null +++ b/sound/soc/codecs/wsa881x.c @@ -0,0 +1,1235 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2015-2017, The Linux Foundation. +// Copyright (c) 2019, Linaro Limited + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WSA881X_DIGITAL_BASE 0x3000 +#define WSA881X_ANALOG_BASE 0x3100 + +/* Digital register address space */ +#define WSA881X_CHIP_ID0 (WSA881X_DIGITAL_BASE + 0x0000) +#define WSA881X_CHIP_ID1 (WSA881X_DIGITAL_BASE + 0x0001) +#define WSA881X_CHIP_ID2 (WSA881X_DIGITAL_BASE + 0x0002) +#define WSA881X_CHIP_ID3 (WSA881X_DIGITAL_BASE + 0x0003) +#define WSA881X_BUS_ID (WSA881X_DIGITAL_BASE + 0x0004) +#define WSA881X_CDC_RST_CTL (WSA881X_DIGITAL_BASE + 0x0005) +#define WSA881X_CDC_TOP_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0006) +#define WSA881X_CDC_ANA_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0007) +#define WSA881X_CDC_DIG_CLK_CTL (WSA881X_DIGITAL_BASE + 0x0008) +#define WSA881X_CLOCK_CONFIG (WSA881X_DIGITAL_BASE + 0x0009) +#define WSA881X_ANA_CTL (WSA881X_DIGITAL_BASE + 0x000A) +#define WSA881X_SWR_RESET_EN (WSA881X_DIGITAL_BASE + 0x000B) +#define WSA881X_RESET_CTL (WSA881X_DIGITAL_BASE + 0x000C) +#define WSA881X_TADC_VALUE_CTL (WSA881X_DIGITAL_BASE + 0x000F) +#define WSA881X_TEMP_DETECT_CTL (WSA881X_DIGITAL_BASE + 0x0010) +#define WSA881X_TEMP_MSB (WSA881X_DIGITAL_BASE + 0x0011) +#define WSA881X_TEMP_LSB (WSA881X_DIGITAL_BASE + 0x0012) +#define WSA881X_TEMP_CONFIG0 (WSA881X_DIGITAL_BASE + 0x0013) +#define WSA881X_TEMP_CONFIG1 (WSA881X_DIGITAL_BASE + 0x0014) +#define WSA881X_CDC_CLIP_CTL (WSA881X_DIGITAL_BASE + 0x0015) +#define WSA881X_SDM_PDM9_LSB (WSA881X_DIGITAL_BASE + 0x0016) +#define WSA881X_SDM_PDM9_MSB (WSA881X_DIGITAL_BASE + 0x0017) +#define WSA881X_CDC_RX_CTL (WSA881X_DIGITAL_BASE + 0x0018) +#define WSA881X_DEM_BYPASS_DATA0 (WSA881X_DIGITAL_BASE + 0x0019) +#define WSA881X_DEM_BYPASS_DATA1 (WSA881X_DIGITAL_BASE + 0x001A) +#define WSA881X_DEM_BYPASS_DATA2 (WSA881X_DIGITAL_BASE + 0x001B) +#define WSA881X_DEM_BYPASS_DATA3 (WSA881X_DIGITAL_BASE + 0x001C) +#define WSA881X_OTP_CTRL0 (WSA881X_DIGITAL_BASE + 0x001D) +#define WSA881X_OTP_CTRL1 (WSA881X_DIGITAL_BASE + 0x001E) +#define WSA881X_HDRIVE_CTL_GROUP1 (WSA881X_DIGITAL_BASE + 0x001F) +#define WSA881X_INTR_MODE (WSA881X_DIGITAL_BASE + 0x0020) +#define WSA881X_INTR_MASK (WSA881X_DIGITAL_BASE + 0x0021) +#define WSA881X_INTR_STATUS (WSA881X_DIGITAL_BASE + 0x0022) +#define WSA881X_INTR_CLEAR (WSA881X_DIGITAL_BASE + 0x0023) +#define WSA881X_INTR_LEVEL (WSA881X_DIGITAL_BASE + 0x0024) +#define WSA881X_INTR_SET (WSA881X_DIGITAL_BASE + 0x0025) +#define WSA881X_INTR_TEST (WSA881X_DIGITAL_BASE + 0x0026) +#define WSA881X_PDM_TEST_MODE (WSA881X_DIGITAL_BASE + 0x0030) +#define WSA881X_ATE_TEST_MODE (WSA881X_DIGITAL_BASE + 0x0031) +#define WSA881X_PIN_CTL_MODE (WSA881X_DIGITAL_BASE + 0x0032) +#define WSA881X_PIN_CTL_OE (WSA881X_DIGITAL_BASE + 0x0033) +#define WSA881X_PIN_WDATA_IOPAD (WSA881X_DIGITAL_BASE + 0x0034) +#define WSA881X_PIN_STATUS (WSA881X_DIGITAL_BASE + 0x0035) +#define WSA881X_DIG_DEBUG_MODE (WSA881X_DIGITAL_BASE + 0x0037) +#define WSA881X_DIG_DEBUG_SEL (WSA881X_DIGITAL_BASE + 0x0038) +#define WSA881X_DIG_DEBUG_EN (WSA881X_DIGITAL_BASE + 0x0039) +#define WSA881X_SWR_HM_TEST1 (WSA881X_DIGITAL_BASE + 0x003B) +#define WSA881X_SWR_HM_TEST2 (WSA881X_DIGITAL_BASE + 0x003C) +#define WSA881X_TEMP_DETECT_DBG_CTL (WSA881X_DIGITAL_BASE + 0x003D) +#define WSA881X_TEMP_DEBUG_MSB (WSA881X_DIGITAL_BASE + 0x003E) +#define WSA881X_TEMP_DEBUG_LSB (WSA881X_DIGITAL_BASE + 0x003F) +#define WSA881X_SAMPLE_EDGE_SEL (WSA881X_DIGITAL_BASE + 0x0044) +#define WSA881X_IOPAD_CTL (WSA881X_DIGITAL_BASE + 0x0045) +#define WSA881X_SPARE_0 (WSA881X_DIGITAL_BASE + 0x0050) +#define WSA881X_SPARE_1 (WSA881X_DIGITAL_BASE + 0x0051) +#define WSA881X_SPARE_2 (WSA881X_DIGITAL_BASE + 0x0052) +#define WSA881X_OTP_REG_0 (WSA881X_DIGITAL_BASE + 0x0080) +#define WSA881X_OTP_REG_1 (WSA881X_DIGITAL_BASE + 0x0081) +#define WSA881X_OTP_REG_2 (WSA881X_DIGITAL_BASE + 0x0082) +#define WSA881X_OTP_REG_3 (WSA881X_DIGITAL_BASE + 0x0083) +#define WSA881X_OTP_REG_4 (WSA881X_DIGITAL_BASE + 0x0084) +#define WSA881X_OTP_REG_5 (WSA881X_DIGITAL_BASE + 0x0085) +#define WSA881X_OTP_REG_6 (WSA881X_DIGITAL_BASE + 0x0086) +#define WSA881X_OTP_REG_7 (WSA881X_DIGITAL_BASE + 0x0087) +#define WSA881X_OTP_REG_8 (WSA881X_DIGITAL_BASE + 0x0088) +#define WSA881X_OTP_REG_9 (WSA881X_DIGITAL_BASE + 0x0089) +#define WSA881X_OTP_REG_10 (WSA881X_DIGITAL_BASE + 0x008A) +#define WSA881X_OTP_REG_11 (WSA881X_DIGITAL_BASE + 0x008B) +#define WSA881X_OTP_REG_12 (WSA881X_DIGITAL_BASE + 0x008C) +#define WSA881X_OTP_REG_13 (WSA881X_DIGITAL_BASE + 0x008D) +#define WSA881X_OTP_REG_14 (WSA881X_DIGITAL_BASE + 0x008E) +#define WSA881X_OTP_REG_15 (WSA881X_DIGITAL_BASE + 0x008F) +#define WSA881X_OTP_REG_16 (WSA881X_DIGITAL_BASE + 0x0090) +#define WSA881X_OTP_REG_17 (WSA881X_DIGITAL_BASE + 0x0091) +#define WSA881X_OTP_REG_18 (WSA881X_DIGITAL_BASE + 0x0092) +#define WSA881X_OTP_REG_19 (WSA881X_DIGITAL_BASE + 0x0093) +#define WSA881X_OTP_REG_20 (WSA881X_DIGITAL_BASE + 0x0094) +#define WSA881X_OTP_REG_21 (WSA881X_DIGITAL_BASE + 0x0095) +#define WSA881X_OTP_REG_22 (WSA881X_DIGITAL_BASE + 0x0096) +#define WSA881X_OTP_REG_23 (WSA881X_DIGITAL_BASE + 0x0097) +#define WSA881X_OTP_REG_24 (WSA881X_DIGITAL_BASE + 0x0098) +#define WSA881X_OTP_REG_25 (WSA881X_DIGITAL_BASE + 0x0099) +#define WSA881X_OTP_REG_26 (WSA881X_DIGITAL_BASE + 0x009A) +#define WSA881X_OTP_REG_27 (WSA881X_DIGITAL_BASE + 0x009B) +#define WSA881X_OTP_REG_28 (WSA881X_DIGITAL_BASE + 0x009C) +#define WSA881X_OTP_REG_29 (WSA881X_DIGITAL_BASE + 0x009D) +#define WSA881X_OTP_REG_30 (WSA881X_DIGITAL_BASE + 0x009E) +#define WSA881X_OTP_REG_31 (WSA881X_DIGITAL_BASE + 0x009F) +#define WSA881X_OTP_REG_63 (WSA881X_DIGITAL_BASE + 0x00BF) + +/* Analog Register address space */ +#define WSA881X_BIAS_REF_CTRL (WSA881X_ANALOG_BASE + 0x0000) +#define WSA881X_BIAS_TEST (WSA881X_ANALOG_BASE + 0x0001) +#define WSA881X_BIAS_BIAS (WSA881X_ANALOG_BASE + 0x0002) +#define WSA881X_TEMP_OP (WSA881X_ANALOG_BASE + 0x0003) +#define WSA881X_TEMP_IREF_CTRL (WSA881X_ANALOG_BASE + 0x0004) +#define WSA881X_TEMP_ISENS_CTRL (WSA881X_ANALOG_BASE + 0x0005) +#define WSA881X_TEMP_CLK_CTRL (WSA881X_ANALOG_BASE + 0x0006) +#define WSA881X_TEMP_TEST (WSA881X_ANALOG_BASE + 0x0007) +#define WSA881X_TEMP_BIAS (WSA881X_ANALOG_BASE + 0x0008) +#define WSA881X_TEMP_ADC_CTRL (WSA881X_ANALOG_BASE + 0x0009) +#define WSA881X_TEMP_DOUT_MSB (WSA881X_ANALOG_BASE + 0x000A) +#define WSA881X_TEMP_DOUT_LSB (WSA881X_ANALOG_BASE + 0x000B) +#define WSA881X_ADC_EN_MODU_V (WSA881X_ANALOG_BASE + 0x0010) +#define WSA881X_ADC_EN_MODU_I (WSA881X_ANALOG_BASE + 0x0011) +#define WSA881X_ADC_EN_DET_TEST_V (WSA881X_ANALOG_BASE + 0x0012) +#define WSA881X_ADC_EN_DET_TEST_I (WSA881X_ANALOG_BASE + 0x0013) +#define WSA881X_ADC_SEL_IBIAS (WSA881X_ANALOG_BASE + 0x0014) +#define WSA881X_ADC_EN_SEL_IBAIS (WSA881X_ANALOG_BASE + 0x0015) +#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE + 0x001A) +#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE + 0x001B) +#define WSA881X_PA_GAIN_SEL_MASK BIT(3) +#define WSA881X_PA_GAIN_SEL_REG BIT(3) +#define WSA881X_PA_GAIN_SEL_DRE 0 +#define WSA881X_SPKR_PAG_GAIN_MASK GENMASK(7, 4) +#define WSA881X_SPKR_DAC_CTL (WSA881X_ANALOG_BASE + 0x001C) +#define WSA881X_SPKR_DRV_DBG (WSA881X_ANALOG_BASE + 0x001D) +#define WSA881X_SPKR_PWRSTG_DBG (WSA881X_ANALOG_BASE + 0x001E) +#define WSA881X_SPKR_OCP_CTL (WSA881X_ANALOG_BASE + 0x001F) +#define WSA881X_SPKR_OCP_MASK GENMASK(7, 6) +#define WSA881X_SPKR_OCP_EN BIT(7) +#define WSA881X_SPKR_OCP_HOLD BIT(6) +#define WSA881X_SPKR_CLIP_CTL (WSA881X_ANALOG_BASE + 0x0020) +#define WSA881X_SPKR_BBM_CTL (WSA881X_ANALOG_BASE + 0x0021) +#define WSA881X_SPKR_MISC_CTL1 (WSA881X_ANALOG_BASE + 0x0022) +#define WSA881X_SPKR_MISC_CTL2 (WSA881X_ANALOG_BASE + 0x0023) +#define WSA881X_SPKR_BIAS_INT (WSA881X_ANALOG_BASE + 0x0024) +#define WSA881X_SPKR_PA_INT (WSA881X_ANALOG_BASE + 0x0025) +#define WSA881X_SPKR_BIAS_CAL (WSA881X_ANALOG_BASE + 0x0026) +#define WSA881X_SPKR_BIAS_PSRR (WSA881X_ANALOG_BASE + 0x0027) +#define WSA881X_SPKR_STATUS1 (WSA881X_ANALOG_BASE + 0x0028) +#define WSA881X_SPKR_STATUS2 (WSA881X_ANALOG_BASE + 0x0029) +#define WSA881X_BOOST_EN_CTL (WSA881X_ANALOG_BASE + 0x002A) +#define WSA881X_BOOST_EN_MASK BIT(7) +#define WSA881X_BOOST_EN BIT(7) +#define WSA881X_BOOST_CURRENT_LIMIT (WSA881X_ANALOG_BASE + 0x002B) +#define WSA881X_BOOST_PS_CTL (WSA881X_ANALOG_BASE + 0x002C) +#define WSA881X_BOOST_PRESET_OUT1 (WSA881X_ANALOG_BASE + 0x002D) +#define WSA881X_BOOST_PRESET_OUT2 (WSA881X_ANALOG_BASE + 0x002E) +#define WSA881X_BOOST_FORCE_OUT (WSA881X_ANALOG_BASE + 0x002F) +#define WSA881X_BOOST_LDO_PROG (WSA881X_ANALOG_BASE + 0x0030) +#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB (WSA881X_ANALOG_BASE + 0x0031) +#define WSA881X_BOOST_RON_CTL (WSA881X_ANALOG_BASE + 0x0032) +#define WSA881X_BOOST_LOOP_STABILITY (WSA881X_ANALOG_BASE + 0x0033) +#define WSA881X_BOOST_ZX_CTL (WSA881X_ANALOG_BASE + 0x0034) +#define WSA881X_BOOST_START_CTL (WSA881X_ANALOG_BASE + 0x0035) +#define WSA881X_BOOST_MISC1_CTL (WSA881X_ANALOG_BASE + 0x0036) +#define WSA881X_BOOST_MISC2_CTL (WSA881X_ANALOG_BASE + 0x0037) +#define WSA881X_BOOST_MISC3_CTL (WSA881X_ANALOG_BASE + 0x0038) +#define WSA881X_BOOST_ATEST_CTL (WSA881X_ANALOG_BASE + 0x0039) +#define WSA881X_SPKR_PROT_FE_GAIN (WSA881X_ANALOG_BASE + 0x003A) +#define WSA881X_SPKR_PROT_FE_CM_LDO_SET (WSA881X_ANALOG_BASE + 0x003B) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 (WSA881X_ANALOG_BASE + 0x003C) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 (WSA881X_ANALOG_BASE + 0x003D) +#define WSA881X_SPKR_PROT_ATEST1 (WSA881X_ANALOG_BASE + 0x003E) +#define WSA881X_SPKR_PROT_ATEST2 (WSA881X_ANALOG_BASE + 0x003F) +#define WSA881X_SPKR_PROT_FE_VSENSE_VCM (WSA881X_ANALOG_BASE + 0x0040) +#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 (WSA881X_ANALOG_BASE + 0x0041) +#define WSA881X_BONGO_RESRV_REG1 (WSA881X_ANALOG_BASE + 0x0042) +#define WSA881X_BONGO_RESRV_REG2 (WSA881X_ANALOG_BASE + 0x0043) +#define WSA881X_SPKR_PROT_SAR (WSA881X_ANALOG_BASE + 0x0044) +#define WSA881X_SPKR_STATUS3 (WSA881X_ANALOG_BASE + 0x0045) + +#define SWRS_SCP_FRAME_CTRL_BANK(m) (0x60 + 0x10 * (m)) +#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (0xE0 + 0x10 * (m)) +#define SWR_SLV_MAX_REG_ADDR 0x390 +#define SWR_SLV_START_REG_ADDR 0x40 +#define SWR_SLV_MAX_BUF_LEN 20 +#define BYTES_PER_LINE 12 +#define SWR_SLV_RD_BUF_LEN 8 +#define SWR_SLV_WR_BUF_LEN 32 +#define SWR_SLV_MAX_DEVICES 2 +#define WSA881X_MAX_SWR_PORTS 4 +#define WSA881X_VERSION_ENTRY_SIZE 27 +#define WSA881X_OCP_CTL_TIMER_SEC 2 +#define WSA881X_OCP_CTL_TEMP_CELSIUS 25 +#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60 +#define WSA881X_PROBE_TIMEOUT 1000 + +#define WSA881X_PA_GAIN_TLV(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ + .put = wsa881x_put_pa_gain, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } + +static struct reg_default wsa881x_defaults[] = { + { WSA881X_CHIP_ID0, 0x00 }, + { WSA881X_CHIP_ID1, 0x00 }, + { WSA881X_CHIP_ID2, 0x00 }, + { WSA881X_CHIP_ID3, 0x02 }, + { WSA881X_BUS_ID, 0x00 }, + { WSA881X_CDC_RST_CTL, 0x00 }, + { WSA881X_CDC_TOP_CLK_CTL, 0x03 }, + { WSA881X_CDC_ANA_CLK_CTL, 0x00 }, + { WSA881X_CDC_DIG_CLK_CTL, 0x00 }, + { WSA881X_CLOCK_CONFIG, 0x00 }, + { WSA881X_ANA_CTL, 0x08 }, + { WSA881X_SWR_RESET_EN, 0x00 }, + { WSA881X_TEMP_DETECT_CTL, 0x01 }, + { WSA881X_TEMP_MSB, 0x00 }, + { WSA881X_TEMP_LSB, 0x00 }, + { WSA881X_TEMP_CONFIG0, 0x00 }, + { WSA881X_TEMP_CONFIG1, 0x00 }, + { WSA881X_CDC_CLIP_CTL, 0x03 }, + { WSA881X_SDM_PDM9_LSB, 0x00 }, + { WSA881X_SDM_PDM9_MSB, 0x00 }, + { WSA881X_CDC_RX_CTL, 0x7E }, + { WSA881X_DEM_BYPASS_DATA0, 0x00 }, + { WSA881X_DEM_BYPASS_DATA1, 0x00 }, + { WSA881X_DEM_BYPASS_DATA2, 0x00 }, + { WSA881X_DEM_BYPASS_DATA3, 0x00 }, + { WSA881X_OTP_CTRL0, 0x00 }, + { WSA881X_OTP_CTRL1, 0x00 }, + { WSA881X_HDRIVE_CTL_GROUP1, 0x00 }, + { WSA881X_INTR_MODE, 0x00 }, + { WSA881X_INTR_STATUS, 0x00 }, + { WSA881X_INTR_CLEAR, 0x00 }, + { WSA881X_INTR_LEVEL, 0x00 }, + { WSA881X_INTR_SET, 0x00 }, + { WSA881X_INTR_TEST, 0x00 }, + { WSA881X_PDM_TEST_MODE, 0x00 }, + { WSA881X_ATE_TEST_MODE, 0x00 }, + { WSA881X_PIN_CTL_MODE, 0x00 }, + { WSA881X_PIN_CTL_OE, 0x00 }, + { WSA881X_PIN_WDATA_IOPAD, 0x00 }, + { WSA881X_PIN_STATUS, 0x00 }, + { WSA881X_DIG_DEBUG_MODE, 0x00 }, + { WSA881X_DIG_DEBUG_SEL, 0x00 }, + { WSA881X_DIG_DEBUG_EN, 0x00 }, + { WSA881X_SWR_HM_TEST1, 0x08 }, + { WSA881X_SWR_HM_TEST2, 0x00 }, + { WSA881X_TEMP_DETECT_DBG_CTL, 0x00 }, + { WSA881X_TEMP_DEBUG_MSB, 0x00 }, + { WSA881X_TEMP_DEBUG_LSB, 0x00 }, + { WSA881X_SAMPLE_EDGE_SEL, 0x0C }, + { WSA881X_SPARE_0, 0x00 }, + { WSA881X_SPARE_1, 0x00 }, + { WSA881X_SPARE_2, 0x00 }, + { WSA881X_OTP_REG_0, 0x01 }, + { WSA881X_OTP_REG_1, 0xFF }, + { WSA881X_OTP_REG_2, 0xC0 }, + { WSA881X_OTP_REG_3, 0xFF }, + { WSA881X_OTP_REG_4, 0xC0 }, + { WSA881X_OTP_REG_5, 0xFF }, + { WSA881X_OTP_REG_6, 0xFF }, + { WSA881X_OTP_REG_7, 0xFF }, + { WSA881X_OTP_REG_8, 0xFF }, + { WSA881X_OTP_REG_9, 0xFF }, + { WSA881X_OTP_REG_10, 0xFF }, + { WSA881X_OTP_REG_11, 0xFF }, + { WSA881X_OTP_REG_12, 0xFF }, + { WSA881X_OTP_REG_13, 0xFF }, + { WSA881X_OTP_REG_14, 0xFF }, + { WSA881X_OTP_REG_15, 0xFF }, + { WSA881X_OTP_REG_16, 0xFF }, + { WSA881X_OTP_REG_17, 0xFF }, + { WSA881X_OTP_REG_18, 0xFF }, + { WSA881X_OTP_REG_19, 0xFF }, + { WSA881X_OTP_REG_20, 0xFF }, + { WSA881X_OTP_REG_21, 0xFF }, + { WSA881X_OTP_REG_22, 0xFF }, + { WSA881X_OTP_REG_23, 0xFF }, + { WSA881X_OTP_REG_24, 0x03 }, + { WSA881X_OTP_REG_25, 0x01 }, + { WSA881X_OTP_REG_26, 0x03 }, + { WSA881X_OTP_REG_27, 0x11 }, + { WSA881X_OTP_REG_63, 0x40 }, + /* WSA881x Analog registers */ + { WSA881X_BIAS_REF_CTRL, 0x6C }, + { WSA881X_BIAS_TEST, 0x16 }, + { WSA881X_BIAS_BIAS, 0xF0 }, + { WSA881X_TEMP_OP, 0x00 }, + { WSA881X_TEMP_IREF_CTRL, 0x56 }, + { WSA881X_TEMP_ISENS_CTRL, 0x47 }, + { WSA881X_TEMP_CLK_CTRL, 0x87 }, + { WSA881X_TEMP_TEST, 0x00 }, + { WSA881X_TEMP_BIAS, 0x51 }, + { WSA881X_TEMP_DOUT_MSB, 0x00 }, + { WSA881X_TEMP_DOUT_LSB, 0x00 }, + { WSA881X_ADC_EN_MODU_V, 0x00 }, + { WSA881X_ADC_EN_MODU_I, 0x00 }, + { WSA881X_ADC_EN_DET_TEST_V, 0x00 }, + { WSA881X_ADC_EN_DET_TEST_I, 0x00 }, + { WSA881X_ADC_EN_SEL_IBAIS, 0x10 }, + { WSA881X_SPKR_DRV_EN, 0x74 }, + { WSA881X_SPKR_DRV_DBG, 0x15 }, + { WSA881X_SPKR_PWRSTG_DBG, 0x00 }, + { WSA881X_SPKR_OCP_CTL, 0xD4 }, + { WSA881X_SPKR_CLIP_CTL, 0x90 }, + { WSA881X_SPKR_PA_INT, 0x54 }, + { WSA881X_SPKR_BIAS_CAL, 0xAC }, + { WSA881X_SPKR_STATUS1, 0x00 }, + { WSA881X_SPKR_STATUS2, 0x00 }, + { WSA881X_BOOST_EN_CTL, 0x18 }, + { WSA881X_BOOST_CURRENT_LIMIT, 0x7A }, + { WSA881X_BOOST_PRESET_OUT2, 0x70 }, + { WSA881X_BOOST_FORCE_OUT, 0x0E }, + { WSA881X_BOOST_LDO_PROG, 0x16 }, + { WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71 }, + { WSA881X_BOOST_RON_CTL, 0x0F }, + { WSA881X_BOOST_ZX_CTL, 0x34 }, + { WSA881X_BOOST_START_CTL, 0x23 }, + { WSA881X_BOOST_MISC1_CTL, 0x80 }, + { WSA881X_BOOST_MISC2_CTL, 0x00 }, + { WSA881X_BOOST_MISC3_CTL, 0x00 }, + { WSA881X_BOOST_ATEST_CTL, 0x00 }, + { WSA881X_SPKR_PROT_FE_GAIN, 0x46 }, + { WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B }, + { WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D }, + { WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D }, + { WSA881X_SPKR_PROT_ATEST1, 0x01 }, + { WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D }, + { WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D }, + { WSA881X_SPKR_PROT_SAR, 0x00 }, + { WSA881X_SPKR_STATUS3, 0x00 }, +}; + +static const struct reg_sequence wsa881x_pre_pmu_pa_2_0[] = { + { WSA881X_SPKR_DRV_GAIN, 0x41, 0 }, + { WSA881X_SPKR_MISC_CTL1, 0x87, 0 }, +}; + +static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = { + { WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0 }, + { WSA881X_SPKR_PROT_ATEST2, 0x0A, 0 }, + { WSA881X_SPKR_PROT_FE_GAIN, 0x47, 0 }, +}; + +/* Default register reset values for WSA881x rev 2.0 */ +static struct reg_sequence wsa881x_rev_2_0[] = { + { WSA881X_RESET_CTL, 0x00, 0x00 }, + { WSA881X_TADC_VALUE_CTL, 0x01, 0x00 }, + { WSA881X_INTR_MASK, 0x1B, 0x00 }, + { WSA881X_IOPAD_CTL, 0x00, 0x00 }, + { WSA881X_OTP_REG_28, 0x3F, 0x00 }, + { WSA881X_OTP_REG_29, 0x3F, 0x00 }, + { WSA881X_OTP_REG_30, 0x01, 0x00 }, + { WSA881X_OTP_REG_31, 0x01, 0x00 }, + { WSA881X_TEMP_ADC_CTRL, 0x03, 0x00 }, + { WSA881X_ADC_SEL_IBIAS, 0x45, 0x00 }, + { WSA881X_SPKR_DRV_GAIN, 0xC1, 0x00 }, + { WSA881X_SPKR_DAC_CTL, 0x42, 0x00 }, + { WSA881X_SPKR_BBM_CTL, 0x02, 0x00 }, + { WSA881X_SPKR_MISC_CTL1, 0x40, 0x00 }, + { WSA881X_SPKR_MISC_CTL2, 0x07, 0x00 }, + { WSA881X_SPKR_BIAS_INT, 0x5F, 0x00 }, + { WSA881X_SPKR_BIAS_PSRR, 0x44, 0x00 }, + { WSA881X_BOOST_PS_CTL, 0xA0, 0x00 }, + { WSA881X_BOOST_PRESET_OUT1, 0xB7, 0x00 }, + { WSA881X_BOOST_LOOP_STABILITY, 0x8D, 0x00 }, + { WSA881X_SPKR_PROT_ATEST2, 0x02, 0x00 }, + { WSA881X_BONGO_RESRV_REG1, 0x5E, 0x00 }, + { WSA881X_BONGO_RESRV_REG2, 0x07, 0x00 }, +}; + +enum wsa_port_ids { + WSA881X_PORT_DAC, + WSA881X_PORT_COMP, + WSA881X_PORT_BOOST, + WSA881X_PORT_VISENSE, +}; + +/* 4 ports */ +static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = { + { + /* DAC */ + .num = 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + /* COMP */ + .num = 2, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + /* BOOST */ + .num = 3, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + /* VISENSE */ + .num = 4, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + } +}; + +static const struct sdw_port_config wsa881x_pconfig[WSA881X_MAX_SWR_PORTS] = { + { + .num = 1, + .ch_mask = 0x1, + }, { + .num = 2, + .ch_mask = 0xf, + }, { + .num = 3, + .ch_mask = 0x3, + }, { /* IV feedback */ + .num = 4, + .ch_mask = 0x3, + }, +}; + +static bool wsa881x_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA881X_CHIP_ID0: + case WSA881X_CHIP_ID1: + case WSA881X_CHIP_ID2: + case WSA881X_CHIP_ID3: + case WSA881X_BUS_ID: + case WSA881X_CDC_RST_CTL: + case WSA881X_CDC_TOP_CLK_CTL: + case WSA881X_CDC_ANA_CLK_CTL: + case WSA881X_CDC_DIG_CLK_CTL: + case WSA881X_CLOCK_CONFIG: + case WSA881X_ANA_CTL: + case WSA881X_SWR_RESET_EN: + case WSA881X_RESET_CTL: + case WSA881X_TADC_VALUE_CTL: + case WSA881X_TEMP_DETECT_CTL: + case WSA881X_TEMP_MSB: + case WSA881X_TEMP_LSB: + case WSA881X_TEMP_CONFIG0: + case WSA881X_TEMP_CONFIG1: + case WSA881X_CDC_CLIP_CTL: + case WSA881X_SDM_PDM9_LSB: + case WSA881X_SDM_PDM9_MSB: + case WSA881X_CDC_RX_CTL: + case WSA881X_DEM_BYPASS_DATA0: + case WSA881X_DEM_BYPASS_DATA1: + case WSA881X_DEM_BYPASS_DATA2: + case WSA881X_DEM_BYPASS_DATA3: + case WSA881X_OTP_CTRL0: + case WSA881X_OTP_CTRL1: + case WSA881X_HDRIVE_CTL_GROUP1: + case WSA881X_INTR_MODE: + case WSA881X_INTR_MASK: + case WSA881X_INTR_STATUS: + case WSA881X_INTR_CLEAR: + case WSA881X_INTR_LEVEL: + case WSA881X_INTR_SET: + case WSA881X_INTR_TEST: + case WSA881X_PDM_TEST_MODE: + case WSA881X_ATE_TEST_MODE: + case WSA881X_PIN_CTL_MODE: + case WSA881X_PIN_CTL_OE: + case WSA881X_PIN_WDATA_IOPAD: + case WSA881X_PIN_STATUS: + case WSA881X_DIG_DEBUG_MODE: + case WSA881X_DIG_DEBUG_SEL: + case WSA881X_DIG_DEBUG_EN: + case WSA881X_SWR_HM_TEST1: + case WSA881X_SWR_HM_TEST2: + case WSA881X_TEMP_DETECT_DBG_CTL: + case WSA881X_TEMP_DEBUG_MSB: + case WSA881X_TEMP_DEBUG_LSB: + case WSA881X_SAMPLE_EDGE_SEL: + case WSA881X_IOPAD_CTL: + case WSA881X_SPARE_0: + case WSA881X_SPARE_1: + case WSA881X_SPARE_2: + case WSA881X_OTP_REG_0: + case WSA881X_OTP_REG_1: + case WSA881X_OTP_REG_2: + case WSA881X_OTP_REG_3: + case WSA881X_OTP_REG_4: + case WSA881X_OTP_REG_5: + case WSA881X_OTP_REG_6: + case WSA881X_OTP_REG_7: + case WSA881X_OTP_REG_8: + case WSA881X_OTP_REG_9: + case WSA881X_OTP_REG_10: + case WSA881X_OTP_REG_11: + case WSA881X_OTP_REG_12: + case WSA881X_OTP_REG_13: + case WSA881X_OTP_REG_14: + case WSA881X_OTP_REG_15: + case WSA881X_OTP_REG_16: + case WSA881X_OTP_REG_17: + case WSA881X_OTP_REG_18: + case WSA881X_OTP_REG_19: + case WSA881X_OTP_REG_20: + case WSA881X_OTP_REG_21: + case WSA881X_OTP_REG_22: + case WSA881X_OTP_REG_23: + case WSA881X_OTP_REG_24: + case WSA881X_OTP_REG_25: + case WSA881X_OTP_REG_26: + case WSA881X_OTP_REG_27: + case WSA881X_OTP_REG_28: + case WSA881X_OTP_REG_29: + case WSA881X_OTP_REG_30: + case WSA881X_OTP_REG_31: + case WSA881X_OTP_REG_63: + case WSA881X_BIAS_REF_CTRL: + case WSA881X_BIAS_TEST: + case WSA881X_BIAS_BIAS: + case WSA881X_TEMP_OP: + case WSA881X_TEMP_IREF_CTRL: + case WSA881X_TEMP_ISENS_CTRL: + case WSA881X_TEMP_CLK_CTRL: + case WSA881X_TEMP_TEST: + case WSA881X_TEMP_BIAS: + case WSA881X_TEMP_ADC_CTRL: + case WSA881X_TEMP_DOUT_MSB: + case WSA881X_TEMP_DOUT_LSB: + case WSA881X_ADC_EN_MODU_V: + case WSA881X_ADC_EN_MODU_I: + case WSA881X_ADC_EN_DET_TEST_V: + case WSA881X_ADC_EN_DET_TEST_I: + case WSA881X_ADC_SEL_IBIAS: + case WSA881X_ADC_EN_SEL_IBAIS: + case WSA881X_SPKR_DRV_EN: + case WSA881X_SPKR_DRV_GAIN: + case WSA881X_SPKR_DAC_CTL: + case WSA881X_SPKR_DRV_DBG: + case WSA881X_SPKR_PWRSTG_DBG: + case WSA881X_SPKR_OCP_CTL: + case WSA881X_SPKR_CLIP_CTL: + case WSA881X_SPKR_BBM_CTL: + case WSA881X_SPKR_MISC_CTL1: + case WSA881X_SPKR_MISC_CTL2: + case WSA881X_SPKR_BIAS_INT: + case WSA881X_SPKR_PA_INT: + case WSA881X_SPKR_BIAS_CAL: + case WSA881X_SPKR_BIAS_PSRR: + case WSA881X_SPKR_STATUS1: + case WSA881X_SPKR_STATUS2: + case WSA881X_BOOST_EN_CTL: + case WSA881X_BOOST_CURRENT_LIMIT: + case WSA881X_BOOST_PS_CTL: + case WSA881X_BOOST_PRESET_OUT1: + case WSA881X_BOOST_PRESET_OUT2: + case WSA881X_BOOST_FORCE_OUT: + case WSA881X_BOOST_LDO_PROG: + case WSA881X_BOOST_SLOPE_COMP_ISENSE_FB: + case WSA881X_BOOST_RON_CTL: + case WSA881X_BOOST_LOOP_STABILITY: + case WSA881X_BOOST_ZX_CTL: + case WSA881X_BOOST_START_CTL: + case WSA881X_BOOST_MISC1_CTL: + case WSA881X_BOOST_MISC2_CTL: + case WSA881X_BOOST_MISC3_CTL: + case WSA881X_BOOST_ATEST_CTL: + case WSA881X_SPKR_PROT_FE_GAIN: + case WSA881X_SPKR_PROT_FE_CM_LDO_SET: + case WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1: + case WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2: + case WSA881X_SPKR_PROT_ATEST1: + case WSA881X_SPKR_PROT_ATEST2: + case WSA881X_SPKR_PROT_FE_VSENSE_VCM: + case WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1: + case WSA881X_BONGO_RESRV_REG1: + case WSA881X_BONGO_RESRV_REG2: + case WSA881X_SPKR_PROT_SAR: + case WSA881X_SPKR_STATUS3: + return true; + default: + return false; + } +} + +static bool wsa881x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA881X_CHIP_ID0: + case WSA881X_CHIP_ID1: + case WSA881X_CHIP_ID2: + case WSA881X_CHIP_ID3: + case WSA881X_BUS_ID: + case WSA881X_TEMP_MSB: + case WSA881X_TEMP_LSB: + case WSA881X_SDM_PDM9_LSB: + case WSA881X_SDM_PDM9_MSB: + case WSA881X_OTP_CTRL1: + case WSA881X_INTR_STATUS: + case WSA881X_ATE_TEST_MODE: + case WSA881X_PIN_STATUS: + case WSA881X_SWR_HM_TEST2: + case WSA881X_SPKR_STATUS1: + case WSA881X_SPKR_STATUS2: + case WSA881X_SPKR_STATUS3: + case WSA881X_OTP_REG_0: + case WSA881X_OTP_REG_1: + case WSA881X_OTP_REG_2: + case WSA881X_OTP_REG_3: + case WSA881X_OTP_REG_4: + case WSA881X_OTP_REG_5: + case WSA881X_OTP_REG_31: + case WSA881X_TEMP_DOUT_MSB: + case WSA881X_TEMP_DOUT_LSB: + case WSA881X_TEMP_OP: + case WSA881X_SPKR_PROT_SAR: + return true; + default: + return false; + } +} + +static struct regmap_config wsa881x_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wsa881x_defaults, + .max_register = WSA881X_SPKR_STATUS3, + .num_reg_defaults = ARRAY_SIZE(wsa881x_defaults), + .volatile_reg = wsa881x_volatile_register, + .readable_reg = wsa881x_readable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; + +enum { + G_18DB = 0, + G_16P5DB, + G_15DB, + G_13P5DB, + G_12DB, + G_10P5DB, + G_9DB, + G_7P5DB, + G_6DB, + G_4P5DB, + G_3DB, + G_1P5DB, + G_0DB, +}; + +/* + * Private data Structure for wsa881x. All parameters related to + * WSA881X codec needs to be defined here. + */ +struct wsa881x_priv { + struct regmap *regmap; + struct device *dev; + struct sdw_slave *slave; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WSA881X_MAX_SWR_PORTS]; + struct gpio_desc *sd_n; + /* + * Logical state for SD_N GPIO: high for shutdown, low for enable. + * For backwards compatibility. + */ + unsigned int sd_n_val; + int version; + int active_ports; + bool port_prepared[WSA881X_MAX_SWR_PORTS]; + bool port_enable[WSA881X_MAX_SWR_PORTS]; +}; + +static void wsa881x_init(struct wsa881x_priv *wsa881x) +{ + struct regmap *rm = wsa881x->regmap; + unsigned int val = 0; + + regmap_read(rm, WSA881X_CHIP_ID1, &wsa881x->version); + regmap_register_patch(wsa881x->regmap, wsa881x_rev_2_0, + ARRAY_SIZE(wsa881x_rev_2_0)); + + /* Enable software reset output from soundwire slave */ + regmap_update_bits(rm, WSA881X_SWR_RESET_EN, 0x07, 0x07); + + /* Bring out of analog reset */ + regmap_update_bits(rm, WSA881X_CDC_RST_CTL, 0x02, 0x02); + + /* Bring out of digital reset */ + regmap_update_bits(rm, WSA881X_CDC_RST_CTL, 0x01, 0x01); + regmap_update_bits(rm, WSA881X_CLOCK_CONFIG, 0x10, 0x10); + regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x02, 0x02); + regmap_update_bits(rm, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80); + regmap_update_bits(rm, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06); + regmap_update_bits(rm, WSA881X_SPKR_BIAS_INT, 0xFF, 0x00); + regmap_update_bits(rm, WSA881X_SPKR_PA_INT, 0xF0, 0x40); + regmap_update_bits(rm, WSA881X_SPKR_PA_INT, 0x0E, 0x0E); + regmap_update_bits(rm, WSA881X_BOOST_LOOP_STABILITY, 0x03, 0x03); + regmap_update_bits(rm, WSA881X_BOOST_MISC2_CTL, 0xFF, 0x14); + regmap_update_bits(rm, WSA881X_BOOST_START_CTL, 0x80, 0x80); + regmap_update_bits(rm, WSA881X_BOOST_START_CTL, 0x03, 0x00); + regmap_update_bits(rm, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x0C, 0x04); + regmap_update_bits(rm, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x03, 0x00); + + regmap_read(rm, WSA881X_OTP_REG_0, &val); + if (val) + regmap_update_bits(rm, WSA881X_BOOST_PRESET_OUT1, 0xF0, 0x70); + + regmap_update_bits(rm, WSA881X_BOOST_PRESET_OUT2, 0xF0, 0x30); + regmap_update_bits(rm, WSA881X_SPKR_DRV_EN, 0x08, 0x08); + regmap_update_bits(rm, WSA881X_BOOST_CURRENT_LIMIT, 0x0F, 0x08); + regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x30, 0x30); + regmap_update_bits(rm, WSA881X_SPKR_OCP_CTL, 0x0C, 0x00); + regmap_update_bits(rm, WSA881X_OTP_REG_28, 0x3F, 0x3A); + regmap_update_bits(rm, WSA881X_BONGO_RESRV_REG1, 0xFF, 0xB2); + regmap_update_bits(rm, WSA881X_BONGO_RESRV_REG2, 0xFF, 0x05); +} + +static int wsa881x_component_probe(struct snd_soc_component *comp) +{ + struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa881x->regmap); + + return 0; +} + +static int wsa881x_put_pa_gain(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kc); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kc->private_value; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + int val, ret, min_gain, max_gain; + + ret = pm_runtime_resume_and_get(comp->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + max_gain = (max - ucontrol->value.integer.value[0]) & mask; + /* + * Gain has to set incrementally in 4 steps + * as per HW sequence + */ + if (max_gain > G_4P5DB) + min_gain = G_0DB; + else + min_gain = max_gain + 3; + /* + * 1ms delay is needed before change in gain + * as per HW requirement. + */ + usleep_range(1000, 1010); + + for (val = min_gain; max_gain <= val; val--) { + ret = snd_soc_component_update_bits(comp, + WSA881X_SPKR_DRV_GAIN, + WSA881X_SPKR_PAG_GAIN_MASK, + val << 4); + if (ret < 0) + dev_err(comp->dev, "Failed to change PA gain"); + + usleep_range(1000, 1010); + } + + pm_runtime_mark_last_busy(comp->dev); + pm_runtime_put_autosuspend(comp->dev); + + return 1; +} + +static int wsa881x_get_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa881x_priv *data = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + ucontrol->value.integer.value[0] = data->port_enable[portidx]; + + + return 0; +} + +static int wsa881x_boost_ctrl(struct snd_soc_component *comp, bool enable) +{ + if (enable) + snd_soc_component_update_bits(comp, WSA881X_BOOST_EN_CTL, + WSA881X_BOOST_EN_MASK, + WSA881X_BOOST_EN); + else + snd_soc_component_update_bits(comp, WSA881X_BOOST_EN_CTL, + WSA881X_BOOST_EN_MASK, 0); + /* + * 1.5ms sleep is needed after boost enable/disable as per + * HW requirement + */ + usleep_range(1500, 1510); + return 0; +} + +static int wsa881x_set_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa881x_priv *data = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = + (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + if (ucontrol->value.integer.value[0]) { + if (data->port_enable[portidx]) + return 0; + + data->port_enable[portidx] = true; + } else { + if (!data->port_enable[portidx]) + return 0; + + data->port_enable[portidx] = false; + } + + if (portidx == WSA881X_PORT_BOOST) /* Boost Switch */ + wsa881x_boost_ctrl(comp, data->port_enable[portidx]); + + return 1; +} + +static const char * const smart_boost_lvl_text[] = { + "6.625 V", "6.750 V", "6.875 V", "7.000 V", + "7.125 V", "7.250 V", "7.375 V", "7.500 V", + "7.625 V", "7.750 V", "7.875 V", "8.000 V", + "8.125 V", "8.250 V", "8.375 V", "8.500 V" +}; + +static const struct soc_enum smart_boost_lvl_enum = + SOC_ENUM_SINGLE(WSA881X_BOOST_PRESET_OUT1, 0, + ARRAY_SIZE(smart_boost_lvl_text), + smart_boost_lvl_text); + +static const DECLARE_TLV_DB_SCALE(pa_gain, 0, 150, 0); + +static const struct snd_kcontrol_new wsa881x_snd_controls[] = { + SOC_ENUM("Smart Boost Level", smart_boost_lvl_enum), + WSA881X_PA_GAIN_TLV("PA Volume", WSA881X_SPKR_DRV_GAIN, + 4, 0xC, 1, pa_gain), + SOC_SINGLE_EXT("DAC Switch", WSA881X_PORT_DAC, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), + SOC_SINGLE_EXT("COMP Switch", WSA881X_PORT_COMP, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), + SOC_SINGLE_EXT("BOOST Switch", WSA881X_PORT_BOOST, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), + SOC_SINGLE_EXT("VISENSE Switch", WSA881X_PORT_VISENSE, 0, 1, 0, + wsa881x_get_port, wsa881x_set_port), +}; + +static const struct snd_soc_dapm_route wsa881x_audio_map[] = { + { "RDAC", NULL, "IN" }, + { "RDAC", NULL, "DCLK" }, + { "RDAC", NULL, "ACLK" }, + { "RDAC", NULL, "Bandgap" }, + { "SPKR PGA", NULL, "RDAC" }, + { "SPKR", NULL, "SPKR PGA" }, +}; + +static int wsa881x_visense_txfe_ctrl(struct snd_soc_component *comp, + bool enable) +{ + struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp); + + if (enable) { + regmap_multi_reg_write(wsa881x->regmap, wsa881x_vi_txfe_en_2_0, + ARRAY_SIZE(wsa881x_vi_txfe_en_2_0)); + } else { + snd_soc_component_update_bits(comp, + WSA881X_SPKR_PROT_FE_VSENSE_VCM, + 0x08, 0x08); + /* + * 200us sleep is needed after visense txfe disable as per + * HW requirement. + */ + usleep_range(200, 210); + snd_soc_component_update_bits(comp, WSA881X_SPKR_PROT_FE_GAIN, + 0x01, 0x00); + } + return 0; +} + +static int wsa881x_visense_adc_ctrl(struct snd_soc_component *comp, + bool enable) +{ + snd_soc_component_update_bits(comp, WSA881X_ADC_EN_MODU_V, BIT(7), + (enable << 7)); + snd_soc_component_update_bits(comp, WSA881X_ADC_EN_MODU_I, BIT(7), + (enable << 7)); + return 0; +} + +static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_component_get_drvdata(comp); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(comp, WSA881X_SPKR_OCP_CTL, + WSA881X_SPKR_OCP_MASK, + WSA881X_SPKR_OCP_EN); + regmap_multi_reg_write(wsa881x->regmap, wsa881x_pre_pmu_pa_2_0, + ARRAY_SIZE(wsa881x_pre_pmu_pa_2_0)); + + snd_soc_component_update_bits(comp, WSA881X_SPKR_DRV_GAIN, + WSA881X_PA_GAIN_SEL_MASK, + WSA881X_PA_GAIN_SEL_REG); + break; + case SND_SOC_DAPM_POST_PMU: + if (wsa881x->port_prepared[WSA881X_PORT_VISENSE]) { + wsa881x_visense_txfe_ctrl(comp, true); + snd_soc_component_update_bits(comp, + WSA881X_ADC_EN_SEL_IBAIS, + 0x07, 0x01); + wsa881x_visense_adc_ctrl(comp, true); + } + + break; + case SND_SOC_DAPM_POST_PMD: + if (wsa881x->port_prepared[WSA881X_PORT_VISENSE]) { + wsa881x_visense_adc_ctrl(comp, false); + wsa881x_visense_txfe_ctrl(comp, false); + } + + snd_soc_component_update_bits(comp, WSA881X_SPKR_OCP_CTL, + WSA881X_SPKR_OCP_MASK, + WSA881X_SPKR_OCP_EN | + WSA881X_SPKR_OCP_HOLD); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_DAC_E("RDAC", NULL, WSA881X_SPKR_DAC_CTL, 7, 0, + NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("SPKR PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + wsa881x_spkr_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DCLK", WSA881X_CDC_DIG_CLK_CTL, 0, 0, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("ACLK", WSA881X_CDC_ANA_CLK_CTL, 0, 0, NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("Bandgap", WSA881X_TEMP_OP, 3, 0, + NULL, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("SPKR"), +}; + +static int wsa881x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + int i; + + wsa881x->active_ports = 0; + for (i = 0; i < WSA881X_MAX_SWR_PORTS; i++) { + if (!wsa881x->port_enable[i]) + continue; + + wsa881x->port_config[wsa881x->active_ports] = + wsa881x_pconfig[i]; + wsa881x->active_ports++; + } + + return sdw_stream_add_slave(wsa881x->slave, &wsa881x->sconfig, + wsa881x->port_config, wsa881x->active_ports, + wsa881x->sruntime); +} + +static int wsa881x_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + + sdw_stream_remove_slave(wsa881x->slave, wsa881x->sruntime); + + return 0; +} + +static int wsa881x_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + + wsa881x->sruntime = stream; + + return 0; +} + +static int wsa881x_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(dai->dev); + + if (mute) + regmap_update_bits(wsa881x->regmap, WSA881X_SPKR_DRV_EN, 0x80, + 0x00); + else + regmap_update_bits(wsa881x->regmap, WSA881X_SPKR_DRV_EN, 0x80, + 0x80); + + return 0; +} + +static const struct snd_soc_dai_ops wsa881x_dai_ops = { + .hw_params = wsa881x_hw_params, + .hw_free = wsa881x_hw_free, + .mute_stream = wsa881x_digital_mute, + .set_stream = wsa881x_set_sdw_stream, +}; + +static struct snd_soc_dai_driver wsa881x_dais[] = { + { + .name = "SPKR", + .id = 0, + .playback = { + .stream_name = "SPKR Playback", + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &wsa881x_dai_ops, + }, +}; + +static const struct snd_soc_component_driver wsa881x_component_drv = { + .name = "WSA881x", + .probe = wsa881x_component_probe, + .controls = wsa881x_snd_controls, + .num_controls = ARRAY_SIZE(wsa881x_snd_controls), + .dapm_widgets = wsa881x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets), + .dapm_routes = wsa881x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map), + .endianness = 1, +}; + +static int wsa881x_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_ATTACHED && slave->dev_num > 0) + wsa881x_init(wsa881x); + + return 0; +} + +static int wsa881x_port_prep(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops state) +{ + struct wsa881x_priv *wsa881x = dev_get_drvdata(&slave->dev); + + if (state == SDW_OPS_PORT_POST_PREP) + wsa881x->port_prepared[prepare_ch->num - 1] = true; + else + wsa881x->port_prepared[prepare_ch->num - 1] = false; + + return 0; +} + +static int wsa881x_bus_config(struct sdw_slave *slave, + struct sdw_bus_params *params) +{ + sdw_write(slave, SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(params->next_bank), + 0x01); + + return 0; +} + +static const struct sdw_slave_ops wsa881x_slave_ops = { + .update_status = wsa881x_update_status, + .bus_config = wsa881x_bus_config, + .port_prep = wsa881x_port_prep, +}; + +static int wsa881x_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct wsa881x_priv *wsa881x; + struct device *dev = &pdev->dev; + + wsa881x = devm_kzalloc(dev, sizeof(*wsa881x), GFP_KERNEL); + if (!wsa881x) + return -ENOMEM; + + wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_FLAGS_BIT_NONEXCLUSIVE); + if (IS_ERR(wsa881x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n), + "Shutdown Control GPIO not found\n"); + + /* + * Backwards compatibility work-around. + * + * The SD_N GPIO is active low, however upstream DTS used always active + * high. Changing the flag in driver and DTS will break backwards + * compatibility, so add a simple value inversion to work with both old + * and new DTS. + * + * This won't work properly with DTS using the flags properly in cases: + * 1. Old DTS with proper ACTIVE_LOW, however such case was broken + * before as the driver required the active high. + * 2. New DTS with proper ACTIVE_HIGH (intended), which is rare case + * (not existing upstream) but possible. This is the price of + * backwards compatibility, therefore this hack should be removed at + * some point. + */ + wsa881x->sd_n_val = gpiod_is_active_low(wsa881x->sd_n); + if (!wsa881x->sd_n_val) + dev_warn(dev, "Using ACTIVE_HIGH for shutdown GPIO. Your DTB might be outdated or you use unsupported configuration for the GPIO."); + + dev_set_drvdata(dev, wsa881x); + wsa881x->slave = pdev; + wsa881x->dev = dev; + wsa881x->sconfig.ch_count = 1; + wsa881x->sconfig.bps = 1; + wsa881x->sconfig.frame_rate = 48000; + wsa881x->sconfig.direction = SDW_DATA_DIR_RX; + wsa881x->sconfig.type = SDW_STREAM_PDM; + pdev->prop.sink_ports = GENMASK(WSA881X_MAX_SWR_PORTS, 0); + pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; + pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val); + + wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config); + if (IS_ERR(wsa881x->regmap)) + return dev_err_probe(dev, PTR_ERR(wsa881x->regmap), "regmap_init failed\n"); + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return devm_snd_soc_register_component(dev, + &wsa881x_component_drv, + wsa881x_dais, + ARRAY_SIZE(wsa881x_dais)); +} + +static int __maybe_unused wsa881x_runtime_suspend(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); + + gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val); + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + + return 0; +} + +static int __maybe_unused wsa881x_runtime_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct regmap *regmap = dev_get_regmap(dev, NULL); + struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); + unsigned long time; + + gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val); + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(WSA881X_PROBE_TIMEOUT)); + if (!time) { + dev_err(dev, "Initialization not complete, timed out\n"); + gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val); + return -ETIMEDOUT; + } + + regcache_cache_only(regmap, false); + regcache_sync(regmap); + + return 0; +} + +static const struct dev_pm_ops wsa881x_pm_ops = { + SET_RUNTIME_PM_OPS(wsa881x_runtime_suspend, wsa881x_runtime_resume, NULL) +}; + +static const struct sdw_device_id wsa881x_slave_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x2010, 0), + SDW_SLAVE_ENTRY(0x0217, 0x2110, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, wsa881x_slave_id); + +static struct sdw_driver wsa881x_codec_driver = { + .probe = wsa881x_probe, + .ops = &wsa881x_slave_ops, + .id_table = wsa881x_slave_id, + .driver = { + .name = "wsa881x-codec", + .pm = &wsa881x_pm_ops, + } +}; +module_sdw_driver(wsa881x_codec_driver); + +MODULE_DESCRIPTION("WSA881x codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c new file mode 100644 index 0000000000..cb83c569e1 --- /dev/null +++ b/sound/soc/codecs/wsa883x.c @@ -0,0 +1,1474 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WSA883X_BASE 0x3000 +#define WSA883X_ANA_BG_TSADC_BASE (WSA883X_BASE + 0x00000001) +#define WSA883X_REF_CTRL (WSA883X_ANA_BG_TSADC_BASE + 0x0000) +#define WSA883X_TEST_CTL_0 (WSA883X_ANA_BG_TSADC_BASE + 0x0001) +#define WSA883X_BIAS_0 (WSA883X_ANA_BG_TSADC_BASE + 0x0002) +#define WSA883X_OP_CTL (WSA883X_ANA_BG_TSADC_BASE + 0x0003) +#define WSA883X_IREF_CTL (WSA883X_ANA_BG_TSADC_BASE + 0x0004) +#define WSA883X_ISENS_CTL (WSA883X_ANA_BG_TSADC_BASE + 0x0005) +#define WSA883X_CLK_CTL (WSA883X_ANA_BG_TSADC_BASE + 0x0006) +#define WSA883X_TEST_CTL_1 (WSA883X_ANA_BG_TSADC_BASE + 0x0007) +#define WSA883X_BIAS_1 (WSA883X_ANA_BG_TSADC_BASE + 0x0008) +#define WSA883X_ADC_CTL (WSA883X_ANA_BG_TSADC_BASE + 0x0009) +#define WSA883X_DOUT_MSB (WSA883X_ANA_BG_TSADC_BASE + 0x000A) +#define WSA883X_DOUT_LSB (WSA883X_ANA_BG_TSADC_BASE + 0x000B) +#define WSA883X_VBAT_SNS (WSA883X_ANA_BG_TSADC_BASE + 0x000C) +#define WSA883X_ITRIM_CODE (WSA883X_ANA_BG_TSADC_BASE + 0x000D) + +#define WSA883X_ANA_IVSENSE_BASE (WSA883X_BASE + 0x0000000F) +#define WSA883X_EN (WSA883X_ANA_IVSENSE_BASE + 0x0000) +#define WSA883X_OVERRIDE1 (WSA883X_ANA_IVSENSE_BASE + 0x0001) +#define WSA883X_OVERRIDE2 (WSA883X_ANA_IVSENSE_BASE + 0x0002) +#define WSA883X_VSENSE1 (WSA883X_ANA_IVSENSE_BASE + 0x0003) +#define WSA883X_ISENSE1 (WSA883X_ANA_IVSENSE_BASE + 0x0004) +#define WSA883X_ISENSE2 (WSA883X_ANA_IVSENSE_BASE + 0x0005) +#define WSA883X_ISENSE_CAL (WSA883X_ANA_IVSENSE_BASE + 0x0006) +#define WSA883X_MISC (WSA883X_ANA_IVSENSE_BASE + 0x0007) +#define WSA883X_ADC_0 (WSA883X_ANA_IVSENSE_BASE + 0x0008) +#define WSA883X_ADC_1 (WSA883X_ANA_IVSENSE_BASE + 0x0009) +#define WSA883X_ADC_2 (WSA883X_ANA_IVSENSE_BASE + 0x000A) +#define WSA883X_ADC_3 (WSA883X_ANA_IVSENSE_BASE + 0x000B) +#define WSA883X_ADC_4 (WSA883X_ANA_IVSENSE_BASE + 0x000C) +#define WSA883X_ADC_5 (WSA883X_ANA_IVSENSE_BASE + 0x000D) +#define WSA883X_ADC_6 (WSA883X_ANA_IVSENSE_BASE + 0x000E) +#define WSA883X_ADC_7 (WSA883X_ANA_IVSENSE_BASE + 0x000F) +#define WSA883X_STATUS (WSA883X_ANA_IVSENSE_BASE + 0x0010) + +#define WSA883X_ANA_SPK_TOP_BASE (WSA883X_BASE + 0x00000025) +#define WSA883X_DAC_CTRL_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0000) +#define WSA883X_DAC_EN_DEBUG_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0001) +#define WSA883X_DAC_OPAMP_BIAS1_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0002) +#define WSA883X_DAC_OPAMP_BIAS2_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0003) +#define WSA883X_DAC_VCM_CTRL_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0004) +#define WSA883X_DAC_VOLTAGE_CTRL_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0005) +#define WSA883X_ATEST1_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0006) +#define WSA883X_ATEST2_REG (WSA883X_ANA_SPK_TOP_BASE + 0x0007) +#define WSA883X_SPKR_TOP_BIAS_REG1 (WSA883X_ANA_SPK_TOP_BASE + 0x0008) +#define WSA883X_SPKR_TOP_BIAS_REG2 (WSA883X_ANA_SPK_TOP_BASE + 0x0009) +#define WSA883X_SPKR_TOP_BIAS_REG3 (WSA883X_ANA_SPK_TOP_BASE + 0x000A) +#define WSA883X_SPKR_TOP_BIAS_REG4 (WSA883X_ANA_SPK_TOP_BASE + 0x000B) +#define WSA883X_SPKR_CLIP_DET_REG (WSA883X_ANA_SPK_TOP_BASE + 0x000C) +#define WSA883X_SPKR_DRV_LF_BLK_EN (WSA883X_ANA_SPK_TOP_BASE + 0x000D) +#define WSA883X_SPKR_DRV_LF_EN (WSA883X_ANA_SPK_TOP_BASE + 0x000E) +#define WSA883X_SPKR_DRV_LF_MASK_DCC_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x000F) +#define WSA883X_SPKR_DRV_LF_MISC_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0010) +#define WSA883X_SPKR_DRV_LF_REG_GAIN (WSA883X_ANA_SPK_TOP_BASE + 0x0011) +#define WSA883X_SPKR_DRV_OS_CAL_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0012) +#define WSA883X_SPKR_DRV_OS_CAL_CTL1 (WSA883X_ANA_SPK_TOP_BASE + 0x0013) +#define WSA883X_SPKR_PWM_CLK_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0014) +#define WSA883X_SPKR_PWM_FREQ_SEL_MASK BIT(3) +#define WSA883X_SPKR_PWM_FREQ_F300KHZ 0 +#define WSA883X_SPKR_PWM_FREQ_F600KHZ 1 +#define WSA883X_SPKR_PDRV_HS_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0015) +#define WSA883X_SPKR_PDRV_LS_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0016) +#define WSA883X_SPKR_PWRSTG_DBG (WSA883X_ANA_SPK_TOP_BASE + 0x0017) +#define WSA883X_SPKR_OCP_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0018) +#define WSA883X_SPKR_BBM_CTL (WSA883X_ANA_SPK_TOP_BASE + 0x0019) +#define WSA883X_PA_STATUS0 (WSA883X_ANA_SPK_TOP_BASE + 0x001A) +#define WSA883X_PA_STATUS1 (WSA883X_ANA_SPK_TOP_BASE + 0x001B) +#define WSA883X_PA_STATUS2 (WSA883X_ANA_SPK_TOP_BASE + 0x001C) + +#define WSA883X_ANA_BOOST_BASE (WSA883X_BASE + 0x00000043) +#define WSA883X_EN_CTRL (WSA883X_ANA_BOOST_BASE + 0x0000) +#define WSA883X_CURRENT_LIMIT (WSA883X_ANA_BOOST_BASE + 0x0001) +#define WSA883X_IBIAS1 (WSA883X_ANA_BOOST_BASE + 0x0002) +#define WSA883X_IBIAS2 (WSA883X_ANA_BOOST_BASE + 0x0003) +#define WSA883X_IBIAS3 (WSA883X_ANA_BOOST_BASE + 0x0004) +#define WSA883X_LDO_PROG (WSA883X_ANA_BOOST_BASE + 0x0005) +#define WSA883X_STABILITY_CTRL1 (WSA883X_ANA_BOOST_BASE + 0x0006) +#define WSA883X_STABILITY_CTRL2 (WSA883X_ANA_BOOST_BASE + 0x0007) +#define WSA883X_PWRSTAGE_CTRL1 (WSA883X_ANA_BOOST_BASE + 0x0008) +#define WSA883X_PWRSTAGE_CTRL2 (WSA883X_ANA_BOOST_BASE + 0x0009) +#define WSA883X_BYPASS_1 (WSA883X_ANA_BOOST_BASE + 0x000A) +#define WSA883X_BYPASS_2 (WSA883X_ANA_BOOST_BASE + 0x000B) +#define WSA883X_ZX_CTRL_1 (WSA883X_ANA_BOOST_BASE + 0x000C) +#define WSA883X_ZX_CTRL_2 (WSA883X_ANA_BOOST_BASE + 0x000D) +#define WSA883X_MISC1 (WSA883X_ANA_BOOST_BASE + 0x000E) +#define WSA883X_MISC2 (WSA883X_ANA_BOOST_BASE + 0x000F) +#define WSA883X_GMAMP_SUP1 (WSA883X_ANA_BOOST_BASE + 0x0010) +#define WSA883X_PWRSTAGE_CTRL3 (WSA883X_ANA_BOOST_BASE + 0x0011) +#define WSA883X_PWRSTAGE_CTRL4 (WSA883X_ANA_BOOST_BASE + 0x0012) +#define WSA883X_TEST1 (WSA883X_ANA_BOOST_BASE + 0x0013) +#define WSA883X_SPARE1 (WSA883X_ANA_BOOST_BASE + 0x0014) +#define WSA883X_SPARE2 (WSA883X_ANA_BOOST_BASE + 0x0015) + +#define WSA883X_ANA_PON_LDOL_BASE (WSA883X_BASE + 0x00000059) +#define WSA883X_PON_CTL_0 (WSA883X_ANA_PON_LDOL_BASE + 0x0000) +#define WSA883X_PON_CLT_1 (WSA883X_ANA_PON_LDOL_BASE + 0x0001) +#define WSA883X_PON_CTL_2 (WSA883X_ANA_PON_LDOL_BASE + 0x0002) +#define WSA883X_PON_CTL_3 (WSA883X_ANA_PON_LDOL_BASE + 0x0003) +#define WSA883X_CKWD_CTL_0 (WSA883X_ANA_PON_LDOL_BASE + 0x0004) +#define WSA883X_CKWD_CTL_1 (WSA883X_ANA_PON_LDOL_BASE + 0x0005) +#define WSA883X_CKWD_CTL_2 (WSA883X_ANA_PON_LDOL_BASE + 0x0006) +#define WSA883X_CKSK_CTL_0 (WSA883X_ANA_PON_LDOL_BASE + 0x0007) +#define WSA883X_PADSW_CTL_0 (WSA883X_ANA_PON_LDOL_BASE + 0x0008) +#define WSA883X_TEST_0 (WSA883X_ANA_PON_LDOL_BASE + 0x0009) +#define WSA883X_TEST_1 (WSA883X_ANA_PON_LDOL_BASE + 0x000A) +#define WSA883X_STATUS_0 (WSA883X_ANA_PON_LDOL_BASE + 0x000B) +#define WSA883X_STATUS_1 (WSA883X_ANA_PON_LDOL_BASE + 0x000C) + +#define WSA883X_DIG_CTRL_BASE (WSA883X_BASE + 0x00000400) +#define WSA883X_CHIP_ID0 (WSA883X_DIG_CTRL_BASE + 0x0001) +#define WSA883X_CHIP_ID1 (WSA883X_DIG_CTRL_BASE + 0x0002) +#define WSA883X_CHIP_ID2 (WSA883X_DIG_CTRL_BASE + 0x0003) +#define WSA883X_CHIP_ID3 (WSA883X_DIG_CTRL_BASE + 0x0004) +#define WSA883X_BUS_ID (WSA883X_DIG_CTRL_BASE + 0x0005) +#define WSA883X_CDC_RST_CTL (WSA883X_DIG_CTRL_BASE + 0x0006) +#define WSA883X_TOP_CLK_CFG (WSA883X_DIG_CTRL_BASE + 0x0007) +#define WSA883X_CDC_PATH_MODE (WSA883X_DIG_CTRL_BASE + 0x0008) +#define WSA883X_RXD_MODE_MASK BIT(1) +#define WSA883X_RXD_MODE_NORMAL 0 +#define WSA883X_RXD_MODE_HIFI 1 +#define WSA883X_CDC_CLK_CTL (WSA883X_DIG_CTRL_BASE + 0x0009) +#define WSA883X_SWR_RESET_EN (WSA883X_DIG_CTRL_BASE + 0x000A) +#define WSA883X_RESET_CTL (WSA883X_DIG_CTRL_BASE + 0x000B) +#define WSA883X_PA_FSM_CTL (WSA883X_DIG_CTRL_BASE + 0x0010) +#define WSA883X_GLOBAL_PA_EN_MASK BIT(0) +#define WSA883X_GLOBAL_PA_ENABLE 1 +#define WSA883X_PA_FSM_TIMER0 (WSA883X_DIG_CTRL_BASE + 0x0011) +#define WSA883X_PA_FSM_TIMER1 (WSA883X_DIG_CTRL_BASE + 0x0012) +#define WSA883X_PA_FSM_STA (WSA883X_DIG_CTRL_BASE + 0x0013) +#define WSA883X_PA_FSM_ERR_COND (WSA883X_DIG_CTRL_BASE + 0x0014) +#define WSA883X_PA_FSM_MSK (WSA883X_DIG_CTRL_BASE + 0x0015) +#define WSA883X_PA_FSM_BYP (WSA883X_DIG_CTRL_BASE + 0x0016) +#define WSA883X_PA_FSM_DBG (WSA883X_DIG_CTRL_BASE + 0x0017) +#define WSA883X_TADC_VALUE_CTL (WSA883X_DIG_CTRL_BASE + 0x0020) +#define WSA883X_TEMP_DETECT_CTL (WSA883X_DIG_CTRL_BASE + 0x0021) +#define WSA883X_TEMP_MSB (WSA883X_DIG_CTRL_BASE + 0x0022) +#define WSA883X_TEMP_LSB (WSA883X_DIG_CTRL_BASE + 0x0023) +#define WSA883X_TEMP_CONFIG0 (WSA883X_DIG_CTRL_BASE + 0x0024) +#define WSA883X_TEMP_CONFIG1 (WSA883X_DIG_CTRL_BASE + 0x0025) +#define WSA883X_VBAT_ADC_FLT_CTL (WSA883X_DIG_CTRL_BASE + 0x0026) +#define WSA883X_VBAT_ADC_FLT_EN_MASK BIT(0) +#define WSA883X_VBAT_ADC_COEF_SEL_MASK GENMASK(3, 1) +#define WSA883X_VBAT_ADC_COEF_F_1DIV2 0x0 +#define WSA883X_VBAT_ADC_COEF_F_1DIV16 0x3 +#define WSA883X_VBAT_DIN_MSB (WSA883X_DIG_CTRL_BASE + 0x0027) +#define WSA883X_VBAT_DIN_LSB (WSA883X_DIG_CTRL_BASE + 0x0028) +#define WSA883X_VBAT_DOUT (WSA883X_DIG_CTRL_BASE + 0x0029) +#define WSA883X_SDM_PDM9_LSB (WSA883X_DIG_CTRL_BASE + 0x002A) +#define WSA883X_SDM_PDM9_MSB (WSA883X_DIG_CTRL_BASE + 0x002B) +#define WSA883X_CDC_RX_CTL (WSA883X_DIG_CTRL_BASE + 0x0030) +#define WSA883X_CDC_SPK_DSM_A1_0 (WSA883X_DIG_CTRL_BASE + 0x0031) +#define WSA883X_CDC_SPK_DSM_A1_1 (WSA883X_DIG_CTRL_BASE + 0x0032) +#define WSA883X_CDC_SPK_DSM_A2_0 (WSA883X_DIG_CTRL_BASE + 0x0033) +#define WSA883X_CDC_SPK_DSM_A2_1 (WSA883X_DIG_CTRL_BASE + 0x0034) +#define WSA883X_CDC_SPK_DSM_A3_0 (WSA883X_DIG_CTRL_BASE + 0x0035) +#define WSA883X_CDC_SPK_DSM_A3_1 (WSA883X_DIG_CTRL_BASE + 0x0036) +#define WSA883X_CDC_SPK_DSM_A4_0 (WSA883X_DIG_CTRL_BASE + 0x0037) +#define WSA883X_CDC_SPK_DSM_A4_1 (WSA883X_DIG_CTRL_BASE + 0x0038) +#define WSA883X_CDC_SPK_DSM_A5_0 (WSA883X_DIG_CTRL_BASE + 0x0039) +#define WSA883X_CDC_SPK_DSM_A5_1 (WSA883X_DIG_CTRL_BASE + 0x003A) +#define WSA883X_CDC_SPK_DSM_A6_0 (WSA883X_DIG_CTRL_BASE + 0x003B) +#define WSA883X_CDC_SPK_DSM_A7_0 (WSA883X_DIG_CTRL_BASE + 0x003C) +#define WSA883X_CDC_SPK_DSM_C_0 (WSA883X_DIG_CTRL_BASE + 0x003D) +#define WSA883X_CDC_SPK_DSM_C_1 (WSA883X_DIG_CTRL_BASE + 0x003E) +#define WSA883X_CDC_SPK_DSM_C_2 (WSA883X_DIG_CTRL_BASE + 0x003F) +#define WSA883X_CDC_SPK_DSM_C_3 (WSA883X_DIG_CTRL_BASE + 0x0040) +#define WSA883X_CDC_SPK_DSM_R1 (WSA883X_DIG_CTRL_BASE + 0x0041) +#define WSA883X_CDC_SPK_DSM_R2 (WSA883X_DIG_CTRL_BASE + 0x0042) +#define WSA883X_CDC_SPK_DSM_R3 (WSA883X_DIG_CTRL_BASE + 0x0043) +#define WSA883X_CDC_SPK_DSM_R4 (WSA883X_DIG_CTRL_BASE + 0x0044) +#define WSA883X_CDC_SPK_DSM_R5 (WSA883X_DIG_CTRL_BASE + 0x0045) +#define WSA883X_CDC_SPK_DSM_R6 (WSA883X_DIG_CTRL_BASE + 0x0046) +#define WSA883X_CDC_SPK_DSM_R7 (WSA883X_DIG_CTRL_BASE + 0x0047) +#define WSA883X_CDC_SPK_GAIN_PDM_0 (WSA883X_DIG_CTRL_BASE + 0x0048) +#define WSA883X_CDC_SPK_GAIN_PDM_1 (WSA883X_DIG_CTRL_BASE + 0x0049) +#define WSA883X_CDC_SPK_GAIN_PDM_2 (WSA883X_DIG_CTRL_BASE + 0x004A) +#define WSA883X_PDM_WD_CTL (WSA883X_DIG_CTRL_BASE + 0x004B) +#define WSA883X_PDM_EN_MASK BIT(0) +#define WSA883X_PDM_ENABLE BIT(0) +#define WSA883X_DEM_BYPASS_DATA0 (WSA883X_DIG_CTRL_BASE + 0x004C) +#define WSA883X_DEM_BYPASS_DATA1 (WSA883X_DIG_CTRL_BASE + 0x004D) +#define WSA883X_DEM_BYPASS_DATA2 (WSA883X_DIG_CTRL_BASE + 0x004E) +#define WSA883X_DEM_BYPASS_DATA3 (WSA883X_DIG_CTRL_BASE + 0x004F) +#define WSA883X_WAVG_CTL (WSA883X_DIG_CTRL_BASE + 0x0050) +#define WSA883X_WAVG_LRA_PER_0 (WSA883X_DIG_CTRL_BASE + 0x0051) +#define WSA883X_WAVG_LRA_PER_1 (WSA883X_DIG_CTRL_BASE + 0x0052) +#define WSA883X_WAVG_DELTA_THETA_0 (WSA883X_DIG_CTRL_BASE + 0x0053) +#define WSA883X_WAVG_DELTA_THETA_1 (WSA883X_DIG_CTRL_BASE + 0x0054) +#define WSA883X_WAVG_DIRECT_AMP_0 (WSA883X_DIG_CTRL_BASE + 0x0055) +#define WSA883X_WAVG_DIRECT_AMP_1 (WSA883X_DIG_CTRL_BASE + 0x0056) +#define WSA883X_WAVG_PTRN_AMP0_0 (WSA883X_DIG_CTRL_BASE + 0x0057) +#define WSA883X_WAVG_PTRN_AMP0_1 (WSA883X_DIG_CTRL_BASE + 0x0058) +#define WSA883X_WAVG_PTRN_AMP1_0 (WSA883X_DIG_CTRL_BASE + 0x0059) +#define WSA883X_WAVG_PTRN_AMP1_1 (WSA883X_DIG_CTRL_BASE + 0x005A) +#define WSA883X_WAVG_PTRN_AMP2_0 (WSA883X_DIG_CTRL_BASE + 0x005B) +#define WSA883X_WAVG_PTRN_AMP2_1 (WSA883X_DIG_CTRL_BASE + 0x005C) +#define WSA883X_WAVG_PTRN_AMP3_0 (WSA883X_DIG_CTRL_BASE + 0x005D) +#define WSA883X_WAVG_PTRN_AMP3_1 (WSA883X_DIG_CTRL_BASE + 0x005E) +#define WSA883X_WAVG_PTRN_AMP4_0 (WSA883X_DIG_CTRL_BASE + 0x005F) +#define WSA883X_WAVG_PTRN_AMP4_1 (WSA883X_DIG_CTRL_BASE + 0x0060) +#define WSA883X_WAVG_PTRN_AMP5_0 (WSA883X_DIG_CTRL_BASE + 0x0061) +#define WSA883X_WAVG_PTRN_AMP5_1 (WSA883X_DIG_CTRL_BASE + 0x0062) +#define WSA883X_WAVG_PTRN_AMP6_0 (WSA883X_DIG_CTRL_BASE + 0x0063) +#define WSA883X_WAVG_PTRN_AMP6_1 (WSA883X_DIG_CTRL_BASE + 0x0064) +#define WSA883X_WAVG_PTRN_AMP7_0 (WSA883X_DIG_CTRL_BASE + 0x0065) +#define WSA883X_WAVG_PTRN_AMP7_1 (WSA883X_DIG_CTRL_BASE + 0x0066) +#define WSA883X_WAVG_PER_0_1 (WSA883X_DIG_CTRL_BASE + 0x0067) +#define WSA883X_WAVG_PER_2_3 (WSA883X_DIG_CTRL_BASE + 0x0068) +#define WSA883X_WAVG_PER_4_5 (WSA883X_DIG_CTRL_BASE + 0x0069) +#define WSA883X_WAVG_PER_6_7 (WSA883X_DIG_CTRL_BASE + 0x006A) +#define WSA883X_WAVG_STA (WSA883X_DIG_CTRL_BASE + 0x006B) +#define WSA883X_DRE_CTL_0 (WSA883X_DIG_CTRL_BASE + 0x006C) +#define WSA883X_DRE_OFFSET_MASK GENMASK(2, 0) +#define WSA883X_DRE_PROG_DELAY_MASK GENMASK(7, 4) +#define WSA883X_DRE_CTL_1 (WSA883X_DIG_CTRL_BASE + 0x006D) +#define WSA883X_DRE_GAIN_EN_MASK BIT(0) +#define WSA883X_DRE_GAIN_FROM_CSR 1 +#define WSA883X_DRE_IDLE_DET_CTL (WSA883X_DIG_CTRL_BASE + 0x006E) +#define WSA883X_CLSH_CTL_0 (WSA883X_DIG_CTRL_BASE + 0x0070) +#define WSA883X_CLSH_CTL_1 (WSA883X_DIG_CTRL_BASE + 0x0071) +#define WSA883X_CLSH_V_HD_PA (WSA883X_DIG_CTRL_BASE + 0x0072) +#define WSA883X_CLSH_V_PA_MIN (WSA883X_DIG_CTRL_BASE + 0x0073) +#define WSA883X_CLSH_OVRD_VAL (WSA883X_DIG_CTRL_BASE + 0x0074) +#define WSA883X_CLSH_HARD_MAX (WSA883X_DIG_CTRL_BASE + 0x0075) +#define WSA883X_CLSH_SOFT_MAX (WSA883X_DIG_CTRL_BASE + 0x0076) +#define WSA883X_CLSH_SIG_DP (WSA883X_DIG_CTRL_BASE + 0x0077) +#define WSA883X_TAGC_CTL (WSA883X_DIG_CTRL_BASE + 0x0078) +#define WSA883X_TAGC_TIME (WSA883X_DIG_CTRL_BASE + 0x0079) +#define WSA883X_TAGC_E2E_GAIN (WSA883X_DIG_CTRL_BASE + 0x007A) +#define WSA883X_TAGC_FORCE_VAL (WSA883X_DIG_CTRL_BASE + 0x007B) +#define WSA883X_VAGC_CTL (WSA883X_DIG_CTRL_BASE + 0x007C) +#define WSA883X_VAGC_TIME (WSA883X_DIG_CTRL_BASE + 0x007D) +#define WSA883X_VAGC_ATTN_LVL_1_2 (WSA883X_DIG_CTRL_BASE + 0x007E) +#define WSA883X_VAGC_ATTN_LVL_3 (WSA883X_DIG_CTRL_BASE + 0x007F) +#define WSA883X_INTR_MODE (WSA883X_DIG_CTRL_BASE + 0x0080) +#define WSA883X_INTR_MASK0 (WSA883X_DIG_CTRL_BASE + 0x0081) +#define WSA883X_INTR_MASK1 (WSA883X_DIG_CTRL_BASE + 0x0082) +#define WSA883X_INTR_STATUS0 (WSA883X_DIG_CTRL_BASE + 0x0083) +#define WSA883X_INTR_STATUS1 (WSA883X_DIG_CTRL_BASE + 0x0084) +#define WSA883X_INTR_CLEAR0 (WSA883X_DIG_CTRL_BASE + 0x0085) +#define WSA883X_INTR_CLEAR1 (WSA883X_DIG_CTRL_BASE + 0x0086) +#define WSA883X_INTR_LEVEL0 (WSA883X_DIG_CTRL_BASE + 0x0087) +#define WSA883X_INTR_LEVEL1 (WSA883X_DIG_CTRL_BASE + 0x0088) +#define WSA883X_INTR_SET0 (WSA883X_DIG_CTRL_BASE + 0x0089) +#define WSA883X_INTR_SET1 (WSA883X_DIG_CTRL_BASE + 0x008A) +#define WSA883X_INTR_TEST0 (WSA883X_DIG_CTRL_BASE + 0x008B) +#define WSA883X_INTR_TEST1 (WSA883X_DIG_CTRL_BASE + 0x008C) +#define WSA883X_OTP_CTRL0 (WSA883X_DIG_CTRL_BASE + 0x0090) +#define WSA883X_OTP_CTRL1 (WSA883X_DIG_CTRL_BASE + 0x0091) +#define WSA883X_HDRIVE_CTL_GROUP1 (WSA883X_DIG_CTRL_BASE + 0x0092) +#define WSA883X_PIN_CTL (WSA883X_DIG_CTRL_BASE + 0x0093) +#define WSA883X_PIN_CTL_OE (WSA883X_DIG_CTRL_BASE + 0x0094) +#define WSA883X_PIN_WDATA_IOPAD (WSA883X_DIG_CTRL_BASE + 0x0095) +#define WSA883X_PIN_STATUS (WSA883X_DIG_CTRL_BASE + 0x0096) +#define WSA883X_I2C_SLAVE_CTL (WSA883X_DIG_CTRL_BASE + 0x0097) +#define WSA883X_PDM_TEST_MODE (WSA883X_DIG_CTRL_BASE + 0x00A0) +#define WSA883X_ATE_TEST_MODE (WSA883X_DIG_CTRL_BASE + 0x00A1) +#define WSA883X_DIG_DEBUG_MODE (WSA883X_DIG_CTRL_BASE + 0x00A3) +#define WSA883X_DIG_DEBUG_SEL (WSA883X_DIG_CTRL_BASE + 0x00A4) +#define WSA883X_DIG_DEBUG_EN (WSA883X_DIG_CTRL_BASE + 0x00A5) +#define WSA883X_SWR_HM_TEST0 (WSA883X_DIG_CTRL_BASE + 0x00A6) +#define WSA883X_SWR_HM_TEST1 (WSA883X_DIG_CTRL_BASE + 0x00A7) +#define WSA883X_SWR_PAD_CTL (WSA883X_DIG_CTRL_BASE + 0x00A8) +#define WSA883X_TADC_DETECT_DBG_CTL (WSA883X_DIG_CTRL_BASE + 0x00A9) +#define WSA883X_TADC_DEBUG_MSB (WSA883X_DIG_CTRL_BASE + 0x00AA) +#define WSA883X_TADC_DEBUG_LSB (WSA883X_DIG_CTRL_BASE + 0x00AB) +#define WSA883X_SAMPLE_EDGE_SEL (WSA883X_DIG_CTRL_BASE + 0x00AC) +#define WSA883X_SWR_EDGE_SEL (WSA883X_DIG_CTRL_BASE + 0x00AD) +#define WSA883X_TEST_MODE_CTL (WSA883X_DIG_CTRL_BASE + 0x00AE) +#define WSA883X_IOPAD_CTL (WSA883X_DIG_CTRL_BASE + 0x00AF) +#define WSA883X_ANA_CSR_DBG_ADD (WSA883X_DIG_CTRL_BASE + 0x00B0) +#define WSA883X_ANA_CSR_DBG_CTL (WSA883X_DIG_CTRL_BASE + 0x00B1) +#define WSA883X_SPARE_R (WSA883X_DIG_CTRL_BASE + 0x00BC) +#define WSA883X_SPARE_0 (WSA883X_DIG_CTRL_BASE + 0x00BD) +#define WSA883X_SPARE_1 (WSA883X_DIG_CTRL_BASE + 0x00BE) +#define WSA883X_SPARE_2 (WSA883X_DIG_CTRL_BASE + 0x00BF) +#define WSA883X_SCODE (WSA883X_DIG_CTRL_BASE + 0x00C0) + +#define WSA883X_DIG_TRIM_BASE (WSA883X_BASE + 0x00000500) +#define WSA883X_OTP_REG_0 (WSA883X_DIG_TRIM_BASE + 0x0080) +#define WSA883X_ID_MASK GENMASK(3, 0) +#define WSA883X_OTP_REG_1 (WSA883X_DIG_TRIM_BASE + 0x0081) +#define WSA883X_OTP_REG_2 (WSA883X_DIG_TRIM_BASE + 0x0082) +#define WSA883X_OTP_REG_3 (WSA883X_DIG_TRIM_BASE + 0x0083) +#define WSA883X_OTP_REG_4 (WSA883X_DIG_TRIM_BASE + 0x0084) +#define WSA883X_OTP_REG_5 (WSA883X_DIG_TRIM_BASE + 0x0085) +#define WSA883X_OTP_REG_6 (WSA883X_DIG_TRIM_BASE + 0x0086) +#define WSA883X_OTP_REG_7 (WSA883X_DIG_TRIM_BASE + 0x0087) +#define WSA883X_OTP_REG_8 (WSA883X_DIG_TRIM_BASE + 0x0088) +#define WSA883X_OTP_REG_9 (WSA883X_DIG_TRIM_BASE + 0x0089) +#define WSA883X_OTP_REG_10 (WSA883X_DIG_TRIM_BASE + 0x008A) +#define WSA883X_OTP_REG_11 (WSA883X_DIG_TRIM_BASE + 0x008B) +#define WSA883X_OTP_REG_12 (WSA883X_DIG_TRIM_BASE + 0x008C) +#define WSA883X_OTP_REG_13 (WSA883X_DIG_TRIM_BASE + 0x008D) +#define WSA883X_OTP_REG_14 (WSA883X_DIG_TRIM_BASE + 0x008E) +#define WSA883X_OTP_REG_15 (WSA883X_DIG_TRIM_BASE + 0x008F) +#define WSA883X_OTP_REG_16 (WSA883X_DIG_TRIM_BASE + 0x0090) +#define WSA883X_OTP_REG_17 (WSA883X_DIG_TRIM_BASE + 0x0091) +#define WSA883X_OTP_REG_18 (WSA883X_DIG_TRIM_BASE + 0x0092) +#define WSA883X_OTP_REG_19 (WSA883X_DIG_TRIM_BASE + 0x0093) +#define WSA883X_OTP_REG_20 (WSA883X_DIG_TRIM_BASE + 0x0094) +#define WSA883X_OTP_REG_21 (WSA883X_DIG_TRIM_BASE + 0x0095) +#define WSA883X_OTP_REG_22 (WSA883X_DIG_TRIM_BASE + 0x0096) +#define WSA883X_OTP_REG_23 (WSA883X_DIG_TRIM_BASE + 0x0097) +#define WSA883X_OTP_REG_24 (WSA883X_DIG_TRIM_BASE + 0x0098) +#define WSA883X_OTP_REG_25 (WSA883X_DIG_TRIM_BASE + 0x0099) +#define WSA883X_OTP_REG_26 (WSA883X_DIG_TRIM_BASE + 0x009A) +#define WSA883X_OTP_REG_27 (WSA883X_DIG_TRIM_BASE + 0x009B) +#define WSA883X_OTP_REG_28 (WSA883X_DIG_TRIM_BASE + 0x009C) +#define WSA883X_OTP_REG_29 (WSA883X_DIG_TRIM_BASE + 0x009D) +#define WSA883X_OTP_REG_30 (WSA883X_DIG_TRIM_BASE + 0x009E) +#define WSA883X_OTP_REG_31 (WSA883X_DIG_TRIM_BASE + 0x009F) +#define WSA883X_OTP_REG_32 (WSA883X_DIG_TRIM_BASE + 0x00A0) +#define WSA883X_OTP_REG_33 (WSA883X_DIG_TRIM_BASE + 0x00A1) +#define WSA883X_OTP_REG_34 (WSA883X_DIG_TRIM_BASE + 0x00A2) +#define WSA883X_OTP_REG_35 (WSA883X_DIG_TRIM_BASE + 0x00A3) +#define WSA883X_OTP_REG_63 (WSA883X_DIG_TRIM_BASE + 0x00BF) + +#define WSA883X_DIG_EMEM_BASE (WSA883X_BASE + 0x000005C0) +#define WSA883X_EMEM_0 (WSA883X_DIG_EMEM_BASE + 0x0000) +#define WSA883X_EMEM_1 (WSA883X_DIG_EMEM_BASE + 0x0001) +#define WSA883X_EMEM_2 (WSA883X_DIG_EMEM_BASE + 0x0002) +#define WSA883X_EMEM_3 (WSA883X_DIG_EMEM_BASE + 0x0003) +#define WSA883X_EMEM_4 (WSA883X_DIG_EMEM_BASE + 0x0004) +#define WSA883X_EMEM_5 (WSA883X_DIG_EMEM_BASE + 0x0005) +#define WSA883X_EMEM_6 (WSA883X_DIG_EMEM_BASE + 0x0006) +#define WSA883X_EMEM_7 (WSA883X_DIG_EMEM_BASE + 0x0007) +#define WSA883X_EMEM_8 (WSA883X_DIG_EMEM_BASE + 0x0008) +#define WSA883X_EMEM_9 (WSA883X_DIG_EMEM_BASE + 0x0009) +#define WSA883X_EMEM_10 (WSA883X_DIG_EMEM_BASE + 0x000A) +#define WSA883X_EMEM_11 (WSA883X_DIG_EMEM_BASE + 0x000B) +#define WSA883X_EMEM_12 (WSA883X_DIG_EMEM_BASE + 0x000C) +#define WSA883X_EMEM_13 (WSA883X_DIG_EMEM_BASE + 0x000D) +#define WSA883X_EMEM_14 (WSA883X_DIG_EMEM_BASE + 0x000E) +#define WSA883X_EMEM_15 (WSA883X_DIG_EMEM_BASE + 0x000F) +#define WSA883X_EMEM_16 (WSA883X_DIG_EMEM_BASE + 0x0010) +#define WSA883X_EMEM_17 (WSA883X_DIG_EMEM_BASE + 0x0011) +#define WSA883X_EMEM_18 (WSA883X_DIG_EMEM_BASE + 0x0012) +#define WSA883X_EMEM_19 (WSA883X_DIG_EMEM_BASE + 0x0013) +#define WSA883X_EMEM_20 (WSA883X_DIG_EMEM_BASE + 0x0014) +#define WSA883X_EMEM_21 (WSA883X_DIG_EMEM_BASE + 0x0015) +#define WSA883X_EMEM_22 (WSA883X_DIG_EMEM_BASE + 0x0016) +#define WSA883X_EMEM_23 (WSA883X_DIG_EMEM_BASE + 0x0017) +#define WSA883X_EMEM_24 (WSA883X_DIG_EMEM_BASE + 0x0018) +#define WSA883X_EMEM_25 (WSA883X_DIG_EMEM_BASE + 0x0019) +#define WSA883X_EMEM_26 (WSA883X_DIG_EMEM_BASE + 0x001A) +#define WSA883X_EMEM_27 (WSA883X_DIG_EMEM_BASE + 0x001B) +#define WSA883X_EMEM_28 (WSA883X_DIG_EMEM_BASE + 0x001C) +#define WSA883X_EMEM_29 (WSA883X_DIG_EMEM_BASE + 0x001D) +#define WSA883X_EMEM_30 (WSA883X_DIG_EMEM_BASE + 0x001E) +#define WSA883X_EMEM_31 (WSA883X_DIG_EMEM_BASE + 0x001F) +#define WSA883X_EMEM_32 (WSA883X_DIG_EMEM_BASE + 0x0020) +#define WSA883X_EMEM_33 (WSA883X_DIG_EMEM_BASE + 0x0021) +#define WSA883X_EMEM_34 (WSA883X_DIG_EMEM_BASE + 0x0022) +#define WSA883X_EMEM_35 (WSA883X_DIG_EMEM_BASE + 0x0023) +#define WSA883X_EMEM_36 (WSA883X_DIG_EMEM_BASE + 0x0024) +#define WSA883X_EMEM_37 (WSA883X_DIG_EMEM_BASE + 0x0025) +#define WSA883X_EMEM_38 (WSA883X_DIG_EMEM_BASE + 0x0026) +#define WSA883X_EMEM_39 (WSA883X_DIG_EMEM_BASE + 0x0027) +#define WSA883X_EMEM_40 (WSA883X_DIG_EMEM_BASE + 0x0028) +#define WSA883X_EMEM_41 (WSA883X_DIG_EMEM_BASE + 0x0029) +#define WSA883X_EMEM_42 (WSA883X_DIG_EMEM_BASE + 0x002A) +#define WSA883X_EMEM_43 (WSA883X_DIG_EMEM_BASE + 0x002B) +#define WSA883X_EMEM_44 (WSA883X_DIG_EMEM_BASE + 0x002C) +#define WSA883X_EMEM_45 (WSA883X_DIG_EMEM_BASE + 0x002D) +#define WSA883X_EMEM_46 (WSA883X_DIG_EMEM_BASE + 0x002E) +#define WSA883X_EMEM_47 (WSA883X_DIG_EMEM_BASE + 0x002F) +#define WSA883X_EMEM_48 (WSA883X_DIG_EMEM_BASE + 0x0030) +#define WSA883X_EMEM_49 (WSA883X_DIG_EMEM_BASE + 0x0031) +#define WSA883X_EMEM_50 (WSA883X_DIG_EMEM_BASE + 0x0032) +#define WSA883X_EMEM_51 (WSA883X_DIG_EMEM_BASE + 0x0033) +#define WSA883X_EMEM_52 (WSA883X_DIG_EMEM_BASE + 0x0034) +#define WSA883X_EMEM_53 (WSA883X_DIG_EMEM_BASE + 0x0035) +#define WSA883X_EMEM_54 (WSA883X_DIG_EMEM_BASE + 0x0036) +#define WSA883X_EMEM_55 (WSA883X_DIG_EMEM_BASE + 0x0037) +#define WSA883X_EMEM_56 (WSA883X_DIG_EMEM_BASE + 0x0038) +#define WSA883X_EMEM_57 (WSA883X_DIG_EMEM_BASE + 0x0039) +#define WSA883X_EMEM_58 (WSA883X_DIG_EMEM_BASE + 0x003A) +#define WSA883X_EMEM_59 (WSA883X_DIG_EMEM_BASE + 0x003B) +#define WSA883X_EMEM_60 (WSA883X_DIG_EMEM_BASE + 0x003C) +#define WSA883X_EMEM_61 (WSA883X_DIG_EMEM_BASE + 0x003D) +#define WSA883X_EMEM_62 (WSA883X_DIG_EMEM_BASE + 0x003E) +#define WSA883X_EMEM_63 (WSA883X_DIG_EMEM_BASE + 0x003F) + +#define WSA883X_NUM_REGISTERS (WSA883X_EMEM_63 + 1) +#define WSA883X_MAX_REGISTER (WSA883X_NUM_REGISTERS - 1) + +#define WSA883X_VERSION_1_0 0 +#define WSA883X_VERSION_1_1 1 + +#define WSA883X_MAX_SWR_PORTS 4 +#define WSA883X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WSA883X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) + +#define WSA883X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct wsa883x_priv { + struct regmap *regmap; + struct device *dev; + struct regulator *vdd; + struct sdw_slave *slave; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WSA883X_MAX_SWR_PORTS]; + struct gpio_desc *sd_n; + bool port_prepared[WSA883X_MAX_SWR_PORTS]; + bool port_enable[WSA883X_MAX_SWR_PORTS]; + int version; + int variant; + int active_ports; + int dev_mode; + int comp_offset; +}; + +enum { + WSA8830 = 0, + WSA8835, + WSA8832, + WSA8835_V2 = 5, +}; + +enum { + COMP_OFFSET0, + COMP_OFFSET1, + COMP_OFFSET2, + COMP_OFFSET3, + COMP_OFFSET4, +}; + +enum wsa_port_ids { + WSA883X_PORT_DAC, + WSA883X_PORT_COMP, + WSA883X_PORT_BOOST, + WSA883X_PORT_VISENSE, +}; + +static const char * const wsa_dev_mode_text[] = { + "Speaker", "Receiver", "Ultrasound" +}; + +enum { + SPEAKER, + RECEIVER, + ULTRASOUND, +}; + +static const struct soc_enum wsa_dev_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa_dev_mode_text), wsa_dev_mode_text); + +/* 4 ports */ +static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA883X_MAX_SWR_PORTS] = { + { + /* DAC */ + .num = 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + /* COMP */ + .num = 2, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + /* BOOST */ + .num = 3, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + /* VISENSE */ + .num = 4, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + } +}; + +static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = { + { + .num = 1, + .ch_mask = 0x1, + }, { + .num = 2, + .ch_mask = 0xf, + }, { + .num = 3, + .ch_mask = 0x3, + }, { /* IV feedback */ + .num = 4, + .ch_mask = 0x3, + }, +}; + +static struct reg_default wsa883x_defaults[] = { + { WSA883X_REF_CTRL, 0xD5 }, + { WSA883X_TEST_CTL_0, 0x06 }, + { WSA883X_BIAS_0, 0xD2 }, + { WSA883X_OP_CTL, 0xE0 }, + { WSA883X_IREF_CTL, 0x57 }, + { WSA883X_ISENS_CTL, 0x47 }, + { WSA883X_CLK_CTL, 0x87 }, + { WSA883X_TEST_CTL_1, 0x00 }, + { WSA883X_BIAS_1, 0x51 }, + { WSA883X_ADC_CTL, 0x01 }, + { WSA883X_DOUT_MSB, 0x00 }, + { WSA883X_DOUT_LSB, 0x00 }, + { WSA883X_VBAT_SNS, 0x40 }, + { WSA883X_ITRIM_CODE, 0x9F }, + { WSA883X_EN, 0x20 }, + { WSA883X_OVERRIDE1, 0x00 }, + { WSA883X_OVERRIDE2, 0x08 }, + { WSA883X_VSENSE1, 0xD3 }, + { WSA883X_ISENSE1, 0xD4 }, + { WSA883X_ISENSE2, 0x20 }, + { WSA883X_ISENSE_CAL, 0x00 }, + { WSA883X_MISC, 0x08 }, + { WSA883X_ADC_0, 0x00 }, + { WSA883X_ADC_1, 0x00 }, + { WSA883X_ADC_2, 0x40 }, + { WSA883X_ADC_3, 0x80 }, + { WSA883X_ADC_4, 0x25 }, + { WSA883X_ADC_5, 0x25 }, + { WSA883X_ADC_6, 0x08 }, + { WSA883X_ADC_7, 0x81 }, + { WSA883X_STATUS, 0x00 }, + { WSA883X_DAC_CTRL_REG, 0x53 }, + { WSA883X_DAC_EN_DEBUG_REG, 0x00 }, + { WSA883X_DAC_OPAMP_BIAS1_REG, 0x48 }, + { WSA883X_DAC_OPAMP_BIAS2_REG, 0x48 }, + { WSA883X_DAC_VCM_CTRL_REG, 0x88 }, + { WSA883X_DAC_VOLTAGE_CTRL_REG, 0xA5 }, + { WSA883X_ATEST1_REG, 0x00 }, + { WSA883X_ATEST2_REG, 0x00 }, + { WSA883X_SPKR_TOP_BIAS_REG1, 0x6A }, + { WSA883X_SPKR_TOP_BIAS_REG2, 0x65 }, + { WSA883X_SPKR_TOP_BIAS_REG3, 0x55 }, + { WSA883X_SPKR_TOP_BIAS_REG4, 0xA9 }, + { WSA883X_SPKR_CLIP_DET_REG, 0x9C }, + { WSA883X_SPKR_DRV_LF_BLK_EN, 0x0F }, + { WSA883X_SPKR_DRV_LF_EN, 0x0A }, + { WSA883X_SPKR_DRV_LF_MASK_DCC_CTL, 0x00 }, + { WSA883X_SPKR_DRV_LF_MISC_CTL, 0x3A }, + { WSA883X_SPKR_DRV_LF_REG_GAIN, 0x00 }, + { WSA883X_SPKR_DRV_OS_CAL_CTL, 0x00 }, + { WSA883X_SPKR_DRV_OS_CAL_CTL1, 0x90 }, + { WSA883X_SPKR_PWM_CLK_CTL, 0x00 }, + { WSA883X_SPKR_PDRV_HS_CTL, 0x52 }, + { WSA883X_SPKR_PDRV_LS_CTL, 0x48 }, + { WSA883X_SPKR_PWRSTG_DBG, 0x08 }, + { WSA883X_SPKR_OCP_CTL, 0xE2 }, + { WSA883X_SPKR_BBM_CTL, 0x92 }, + { WSA883X_PA_STATUS0, 0x00 }, + { WSA883X_PA_STATUS1, 0x00 }, + { WSA883X_PA_STATUS2, 0x80 }, + { WSA883X_EN_CTRL, 0x44 }, + { WSA883X_CURRENT_LIMIT, 0xCC }, + { WSA883X_IBIAS1, 0x00 }, + { WSA883X_IBIAS2, 0x00 }, + { WSA883X_IBIAS3, 0x00 }, + { WSA883X_LDO_PROG, 0x02 }, + { WSA883X_STABILITY_CTRL1, 0x8E }, + { WSA883X_STABILITY_CTRL2, 0x10 }, + { WSA883X_PWRSTAGE_CTRL1, 0x06 }, + { WSA883X_PWRSTAGE_CTRL2, 0x00 }, + { WSA883X_BYPASS_1, 0x19 }, + { WSA883X_BYPASS_2, 0x13 }, + { WSA883X_ZX_CTRL_1, 0xF0 }, + { WSA883X_ZX_CTRL_2, 0x04 }, + { WSA883X_MISC1, 0x06 }, + { WSA883X_MISC2, 0xA0 }, + { WSA883X_GMAMP_SUP1, 0x82 }, + { WSA883X_PWRSTAGE_CTRL3, 0x39 }, + { WSA883X_PWRSTAGE_CTRL4, 0x5F }, + { WSA883X_TEST1, 0x00 }, + { WSA883X_SPARE1, 0x00 }, + { WSA883X_SPARE2, 0x00 }, + { WSA883X_PON_CTL_0, 0x10 }, + { WSA883X_PON_CLT_1, 0xE0 }, + { WSA883X_PON_CTL_2, 0x90 }, + { WSA883X_PON_CTL_3, 0x70 }, + { WSA883X_CKWD_CTL_0, 0x34 }, + { WSA883X_CKWD_CTL_1, 0x0F }, + { WSA883X_CKWD_CTL_2, 0x00 }, + { WSA883X_CKSK_CTL_0, 0x00 }, + { WSA883X_PADSW_CTL_0, 0x00 }, + { WSA883X_TEST_0, 0x00 }, + { WSA883X_TEST_1, 0x00 }, + { WSA883X_STATUS_0, 0x00 }, + { WSA883X_STATUS_1, 0x00 }, + { WSA883X_CHIP_ID0, 0x00 }, + { WSA883X_CHIP_ID1, 0x00 }, + { WSA883X_CHIP_ID2, 0x02 }, + { WSA883X_CHIP_ID3, 0x02 }, + { WSA883X_BUS_ID, 0x00 }, + { WSA883X_CDC_RST_CTL, 0x01 }, + { WSA883X_TOP_CLK_CFG, 0x00 }, + { WSA883X_CDC_PATH_MODE, 0x00 }, + { WSA883X_CDC_CLK_CTL, 0xFF }, + { WSA883X_SWR_RESET_EN, 0x00 }, + { WSA883X_RESET_CTL, 0x00 }, + { WSA883X_PA_FSM_CTL, 0x00 }, + { WSA883X_PA_FSM_TIMER0, 0x80 }, + { WSA883X_PA_FSM_TIMER1, 0x80 }, + { WSA883X_PA_FSM_STA, 0x00 }, + { WSA883X_PA_FSM_ERR_COND, 0x00 }, + { WSA883X_PA_FSM_MSK, 0x00 }, + { WSA883X_PA_FSM_BYP, 0x01 }, + { WSA883X_PA_FSM_DBG, 0x00 }, + { WSA883X_TADC_VALUE_CTL, 0x03 }, + { WSA883X_TEMP_DETECT_CTL, 0x01 }, + { WSA883X_TEMP_MSB, 0x00 }, + { WSA883X_TEMP_LSB, 0x00 }, + { WSA883X_TEMP_CONFIG0, 0x00 }, + { WSA883X_TEMP_CONFIG1, 0x00 }, + { WSA883X_VBAT_ADC_FLT_CTL, 0x00 }, + { WSA883X_VBAT_DIN_MSB, 0x00 }, + { WSA883X_VBAT_DIN_LSB, 0x00 }, + { WSA883X_VBAT_DOUT, 0x00 }, + { WSA883X_SDM_PDM9_LSB, 0x00 }, + { WSA883X_SDM_PDM9_MSB, 0x00 }, + { WSA883X_CDC_RX_CTL, 0xFE }, + { WSA883X_CDC_SPK_DSM_A1_0, 0x00 }, + { WSA883X_CDC_SPK_DSM_A1_1, 0x01 }, + { WSA883X_CDC_SPK_DSM_A2_0, 0x96 }, + { WSA883X_CDC_SPK_DSM_A2_1, 0x09 }, + { WSA883X_CDC_SPK_DSM_A3_0, 0xAB }, + { WSA883X_CDC_SPK_DSM_A3_1, 0x05 }, + { WSA883X_CDC_SPK_DSM_A4_0, 0x1C }, + { WSA883X_CDC_SPK_DSM_A4_1, 0x02 }, + { WSA883X_CDC_SPK_DSM_A5_0, 0x17 }, + { WSA883X_CDC_SPK_DSM_A5_1, 0x02 }, + { WSA883X_CDC_SPK_DSM_A6_0, 0xAA }, + { WSA883X_CDC_SPK_DSM_A7_0, 0xE3 }, + { WSA883X_CDC_SPK_DSM_C_0, 0x69 }, + { WSA883X_CDC_SPK_DSM_C_1, 0x54 }, + { WSA883X_CDC_SPK_DSM_C_2, 0x02 }, + { WSA883X_CDC_SPK_DSM_C_3, 0x15 }, + { WSA883X_CDC_SPK_DSM_R1, 0xA4 }, + { WSA883X_CDC_SPK_DSM_R2, 0xB5 }, + { WSA883X_CDC_SPK_DSM_R3, 0x86 }, + { WSA883X_CDC_SPK_DSM_R4, 0x85 }, + { WSA883X_CDC_SPK_DSM_R5, 0xAA }, + { WSA883X_CDC_SPK_DSM_R6, 0xE2 }, + { WSA883X_CDC_SPK_DSM_R7, 0x62 }, + { WSA883X_CDC_SPK_GAIN_PDM_0, 0x00 }, + { WSA883X_CDC_SPK_GAIN_PDM_1, 0xFC }, + { WSA883X_CDC_SPK_GAIN_PDM_2, 0x05 }, + { WSA883X_PDM_WD_CTL, 0x00 }, + { WSA883X_DEM_BYPASS_DATA0, 0x00 }, + { WSA883X_DEM_BYPASS_DATA1, 0x00 }, + { WSA883X_DEM_BYPASS_DATA2, 0x00 }, + { WSA883X_DEM_BYPASS_DATA3, 0x00 }, + { WSA883X_WAVG_CTL, 0x06 }, + { WSA883X_WAVG_LRA_PER_0, 0xD1 }, + { WSA883X_WAVG_LRA_PER_1, 0x00 }, + { WSA883X_WAVG_DELTA_THETA_0, 0xE6 }, + { WSA883X_WAVG_DELTA_THETA_1, 0x04 }, + { WSA883X_WAVG_DIRECT_AMP_0, 0x50 }, + { WSA883X_WAVG_DIRECT_AMP_1, 0x00 }, + { WSA883X_WAVG_PTRN_AMP0_0, 0x50 }, + { WSA883X_WAVG_PTRN_AMP0_1, 0x00 }, + { WSA883X_WAVG_PTRN_AMP1_0, 0x50 }, + { WSA883X_WAVG_PTRN_AMP1_1, 0x00 }, + { WSA883X_WAVG_PTRN_AMP2_0, 0x50 }, + { WSA883X_WAVG_PTRN_AMP2_1, 0x00 }, + { WSA883X_WAVG_PTRN_AMP3_0, 0x50 }, + { WSA883X_WAVG_PTRN_AMP3_1, 0x00 }, + { WSA883X_WAVG_PTRN_AMP4_0, 0x50 }, + { WSA883X_WAVG_PTRN_AMP4_1, 0x00 }, + { WSA883X_WAVG_PTRN_AMP5_0, 0x50 }, + { WSA883X_WAVG_PTRN_AMP5_1, 0x00 }, + { WSA883X_WAVG_PTRN_AMP6_0, 0x50 }, + { WSA883X_WAVG_PTRN_AMP6_1, 0x00 }, + { WSA883X_WAVG_PTRN_AMP7_0, 0x50 }, + { WSA883X_WAVG_PTRN_AMP7_1, 0x00 }, + { WSA883X_WAVG_PER_0_1, 0x88 }, + { WSA883X_WAVG_PER_2_3, 0x88 }, + { WSA883X_WAVG_PER_4_5, 0x88 }, + { WSA883X_WAVG_PER_6_7, 0x88 }, + { WSA883X_WAVG_STA, 0x00 }, + { WSA883X_DRE_CTL_0, 0x70 }, + { WSA883X_DRE_CTL_1, 0x08 }, + { WSA883X_DRE_IDLE_DET_CTL, 0x1F }, + { WSA883X_CLSH_CTL_0, 0x37 }, + { WSA883X_CLSH_CTL_1, 0x81 }, + { WSA883X_CLSH_V_HD_PA, 0x0F }, + { WSA883X_CLSH_V_PA_MIN, 0x00 }, + { WSA883X_CLSH_OVRD_VAL, 0x00 }, + { WSA883X_CLSH_HARD_MAX, 0xFF }, + { WSA883X_CLSH_SOFT_MAX, 0xF5 }, + { WSA883X_CLSH_SIG_DP, 0x00 }, + { WSA883X_TAGC_CTL, 0x10 }, + { WSA883X_TAGC_TIME, 0x20 }, + { WSA883X_TAGC_E2E_GAIN, 0x02 }, + { WSA883X_TAGC_FORCE_VAL, 0x00 }, + { WSA883X_VAGC_CTL, 0x00 }, + { WSA883X_VAGC_TIME, 0x08 }, + { WSA883X_VAGC_ATTN_LVL_1_2, 0x21 }, + { WSA883X_VAGC_ATTN_LVL_3, 0x03 }, + { WSA883X_INTR_MODE, 0x00 }, + { WSA883X_INTR_MASK0, 0x90 }, + { WSA883X_INTR_MASK1, 0x00 }, + { WSA883X_INTR_STATUS0, 0x00 }, + { WSA883X_INTR_STATUS1, 0x00 }, + { WSA883X_INTR_CLEAR0, 0x00 }, + { WSA883X_INTR_CLEAR1, 0x00 }, + { WSA883X_INTR_LEVEL0, 0x00 }, + { WSA883X_INTR_LEVEL1, 0x00 }, + { WSA883X_INTR_SET0, 0x00 }, + { WSA883X_INTR_SET1, 0x00 }, + { WSA883X_INTR_TEST0, 0x00 }, + { WSA883X_INTR_TEST1, 0x00 }, + { WSA883X_OTP_CTRL0, 0x00 }, + { WSA883X_OTP_CTRL1, 0x00 }, + { WSA883X_HDRIVE_CTL_GROUP1, 0x00 }, + { WSA883X_PIN_CTL, 0x04 }, + { WSA883X_PIN_CTL_OE, 0x00 }, + { WSA883X_PIN_WDATA_IOPAD, 0x00 }, + { WSA883X_PIN_STATUS, 0x00 }, + { WSA883X_I2C_SLAVE_CTL, 0x00 }, + { WSA883X_PDM_TEST_MODE, 0x00 }, + { WSA883X_ATE_TEST_MODE, 0x00 }, + { WSA883X_DIG_DEBUG_MODE, 0x00 }, + { WSA883X_DIG_DEBUG_SEL, 0x00 }, + { WSA883X_DIG_DEBUG_EN, 0x00 }, + { WSA883X_SWR_HM_TEST0, 0x08 }, + { WSA883X_SWR_HM_TEST1, 0x00 }, + { WSA883X_SWR_PAD_CTL, 0x37 }, + { WSA883X_TADC_DETECT_DBG_CTL, 0x00 }, + { WSA883X_TADC_DEBUG_MSB, 0x00 }, + { WSA883X_TADC_DEBUG_LSB, 0x00 }, + { WSA883X_SAMPLE_EDGE_SEL, 0x7F }, + { WSA883X_SWR_EDGE_SEL, 0x00 }, + { WSA883X_TEST_MODE_CTL, 0x04 }, + { WSA883X_IOPAD_CTL, 0x00 }, + { WSA883X_ANA_CSR_DBG_ADD, 0x00 }, + { WSA883X_ANA_CSR_DBG_CTL, 0x12 }, + { WSA883X_SPARE_R, 0x00 }, + { WSA883X_SPARE_0, 0x00 }, + { WSA883X_SPARE_1, 0x00 }, + { WSA883X_SPARE_2, 0x00 }, + { WSA883X_SCODE, 0x00 }, + { WSA883X_OTP_REG_0, 0x05 }, + { WSA883X_OTP_REG_1, 0xFF }, + { WSA883X_OTP_REG_2, 0xC0 }, + { WSA883X_OTP_REG_3, 0xFF }, + { WSA883X_OTP_REG_4, 0xC0 }, + { WSA883X_OTP_REG_5, 0xFF }, + { WSA883X_OTP_REG_6, 0xFF }, + { WSA883X_OTP_REG_7, 0xFF }, + { WSA883X_OTP_REG_8, 0xFF }, + { WSA883X_OTP_REG_9, 0xFF }, + { WSA883X_OTP_REG_10, 0xFF }, + { WSA883X_OTP_REG_11, 0xFF }, + { WSA883X_OTP_REG_12, 0xFF }, + { WSA883X_OTP_REG_13, 0xFF }, + { WSA883X_OTP_REG_14, 0xFF }, + { WSA883X_OTP_REG_15, 0xFF }, + { WSA883X_OTP_REG_16, 0xFF }, + { WSA883X_OTP_REG_17, 0xFF }, + { WSA883X_OTP_REG_18, 0xFF }, + { WSA883X_OTP_REG_19, 0xFF }, + { WSA883X_OTP_REG_20, 0xFF }, + { WSA883X_OTP_REG_21, 0xFF }, + { WSA883X_OTP_REG_22, 0xFF }, + { WSA883X_OTP_REG_23, 0xFF }, + { WSA883X_OTP_REG_24, 0x37 }, + { WSA883X_OTP_REG_25, 0x3F }, + { WSA883X_OTP_REG_26, 0x03 }, + { WSA883X_OTP_REG_27, 0x00 }, + { WSA883X_OTP_REG_28, 0x00 }, + { WSA883X_OTP_REG_29, 0x00 }, + { WSA883X_OTP_REG_30, 0x00 }, + { WSA883X_OTP_REG_31, 0x03 }, + { WSA883X_OTP_REG_32, 0x00 }, + { WSA883X_OTP_REG_33, 0xFF }, + { WSA883X_OTP_REG_34, 0x00 }, + { WSA883X_OTP_REG_35, 0x00 }, + { WSA883X_OTP_REG_63, 0x40 }, + { WSA883X_EMEM_0, 0x00 }, + { WSA883X_EMEM_1, 0x00 }, + { WSA883X_EMEM_2, 0x00 }, + { WSA883X_EMEM_3, 0x00 }, + { WSA883X_EMEM_4, 0x00 }, + { WSA883X_EMEM_5, 0x00 }, + { WSA883X_EMEM_6, 0x00 }, + { WSA883X_EMEM_7, 0x00 }, + { WSA883X_EMEM_8, 0x00 }, + { WSA883X_EMEM_9, 0x00 }, + { WSA883X_EMEM_10, 0x00 }, + { WSA883X_EMEM_11, 0x00 }, + { WSA883X_EMEM_12, 0x00 }, + { WSA883X_EMEM_13, 0x00 }, + { WSA883X_EMEM_14, 0x00 }, + { WSA883X_EMEM_15, 0x00 }, + { WSA883X_EMEM_16, 0x00 }, + { WSA883X_EMEM_17, 0x00 }, + { WSA883X_EMEM_18, 0x00 }, + { WSA883X_EMEM_19, 0x00 }, + { WSA883X_EMEM_20, 0x00 }, + { WSA883X_EMEM_21, 0x00 }, + { WSA883X_EMEM_22, 0x00 }, + { WSA883X_EMEM_23, 0x00 }, + { WSA883X_EMEM_24, 0x00 }, + { WSA883X_EMEM_25, 0x00 }, + { WSA883X_EMEM_26, 0x00 }, + { WSA883X_EMEM_27, 0x00 }, + { WSA883X_EMEM_28, 0x00 }, + { WSA883X_EMEM_29, 0x00 }, + { WSA883X_EMEM_30, 0x00 }, + { WSA883X_EMEM_31, 0x00 }, + { WSA883X_EMEM_32, 0x00 }, + { WSA883X_EMEM_33, 0x00 }, + { WSA883X_EMEM_34, 0x00 }, + { WSA883X_EMEM_35, 0x00 }, + { WSA883X_EMEM_36, 0x00 }, + { WSA883X_EMEM_37, 0x00 }, + { WSA883X_EMEM_38, 0x00 }, + { WSA883X_EMEM_39, 0x00 }, + { WSA883X_EMEM_40, 0x00 }, + { WSA883X_EMEM_41, 0x00 }, + { WSA883X_EMEM_42, 0x00 }, + { WSA883X_EMEM_43, 0x00 }, + { WSA883X_EMEM_44, 0x00 }, + { WSA883X_EMEM_45, 0x00 }, + { WSA883X_EMEM_46, 0x00 }, + { WSA883X_EMEM_47, 0x00 }, + { WSA883X_EMEM_48, 0x00 }, + { WSA883X_EMEM_49, 0x00 }, + { WSA883X_EMEM_50, 0x00 }, + { WSA883X_EMEM_51, 0x00 }, + { WSA883X_EMEM_52, 0x00 }, + { WSA883X_EMEM_53, 0x00 }, + { WSA883X_EMEM_54, 0x00 }, + { WSA883X_EMEM_55, 0x00 }, + { WSA883X_EMEM_56, 0x00 }, + { WSA883X_EMEM_57, 0x00 }, + { WSA883X_EMEM_58, 0x00 }, + { WSA883X_EMEM_59, 0x00 }, + { WSA883X_EMEM_60, 0x00 }, + { WSA883X_EMEM_61, 0x00 }, + { WSA883X_EMEM_62, 0x00 }, + { WSA883X_EMEM_63, 0x00 }, +}; + +static bool wsa883x_readonly_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA883X_DOUT_MSB: + case WSA883X_DOUT_LSB: + case WSA883X_STATUS: + case WSA883X_PA_STATUS0: + case WSA883X_PA_STATUS1: + case WSA883X_PA_STATUS2: + case WSA883X_STATUS_0: + case WSA883X_STATUS_1: + case WSA883X_CHIP_ID0: + case WSA883X_CHIP_ID1: + case WSA883X_CHIP_ID2: + case WSA883X_CHIP_ID3: + case WSA883X_BUS_ID: + case WSA883X_PA_FSM_STA: + case WSA883X_PA_FSM_ERR_COND: + case WSA883X_TEMP_MSB: + case WSA883X_TEMP_LSB: + case WSA883X_VBAT_DIN_MSB: + case WSA883X_VBAT_DIN_LSB: + case WSA883X_VBAT_DOUT: + case WSA883X_SDM_PDM9_LSB: + case WSA883X_SDM_PDM9_MSB: + case WSA883X_WAVG_STA: + case WSA883X_INTR_STATUS0: + case WSA883X_INTR_STATUS1: + case WSA883X_OTP_CTRL1: + case WSA883X_PIN_STATUS: + case WSA883X_ATE_TEST_MODE: + case WSA883X_SWR_HM_TEST1: + case WSA883X_SPARE_R: + case WSA883X_OTP_REG_0: + return true; + } + return false; +} + +static bool wsa883x_writeable_register(struct device *dev, unsigned int reg) +{ + return !wsa883x_readonly_register(dev, reg); +} + +static bool wsa883x_volatile_register(struct device *dev, unsigned int reg) +{ + return wsa883x_readonly_register(dev, reg); +} + +static struct regmap_config wsa883x_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wsa883x_defaults, + .max_register = WSA883X_MAX_REGISTER, + .num_reg_defaults = ARRAY_SIZE(wsa883x_defaults), + .volatile_reg = wsa883x_volatile_register, + .writeable_reg = wsa883x_writeable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .use_single_read = true, +}; + +static const struct reg_sequence reg_init[] = { + {WSA883X_PA_FSM_BYP, 0x00}, + {WSA883X_ADC_6, 0x02}, + {WSA883X_CDC_SPK_DSM_A2_0, 0x0A}, + {WSA883X_CDC_SPK_DSM_A2_1, 0x08}, + {WSA883X_CDC_SPK_DSM_A3_0, 0xF3}, + {WSA883X_CDC_SPK_DSM_A3_1, 0x07}, + {WSA883X_CDC_SPK_DSM_A4_0, 0x79}, + {WSA883X_CDC_SPK_DSM_A4_1, 0x02}, + {WSA883X_CDC_SPK_DSM_A5_0, 0x0B}, + {WSA883X_CDC_SPK_DSM_A5_1, 0x02}, + {WSA883X_CDC_SPK_DSM_A6_0, 0x8A}, + {WSA883X_CDC_SPK_DSM_A7_0, 0x9B}, + {WSA883X_CDC_SPK_DSM_C_0, 0x68}, + {WSA883X_CDC_SPK_DSM_C_1, 0x54}, + {WSA883X_CDC_SPK_DSM_C_2, 0xF2}, + {WSA883X_CDC_SPK_DSM_C_3, 0x20}, + {WSA883X_CDC_SPK_DSM_R1, 0x83}, + {WSA883X_CDC_SPK_DSM_R2, 0x7F}, + {WSA883X_CDC_SPK_DSM_R3, 0x9D}, + {WSA883X_CDC_SPK_DSM_R4, 0x82}, + {WSA883X_CDC_SPK_DSM_R5, 0x8B}, + {WSA883X_CDC_SPK_DSM_R6, 0x9B}, + {WSA883X_CDC_SPK_DSM_R7, 0x3F}, + {WSA883X_VBAT_SNS, 0x20}, + {WSA883X_DRE_CTL_0, 0x92}, + {WSA883X_DRE_IDLE_DET_CTL, 0x0F}, + {WSA883X_CURRENT_LIMIT, 0xC4}, + {WSA883X_VAGC_TIME, 0x0F}, + {WSA883X_VAGC_ATTN_LVL_1_2, 0x00}, + {WSA883X_VAGC_ATTN_LVL_3, 0x01}, + {WSA883X_VAGC_CTL, 0x01}, + {WSA883X_TAGC_CTL, 0x1A}, + {WSA883X_TAGC_TIME, 0x2C}, + {WSA883X_TEMP_CONFIG0, 0x02}, + {WSA883X_TEMP_CONFIG1, 0x02}, + {WSA883X_OTP_REG_1, 0x49}, + {WSA883X_OTP_REG_2, 0x80}, + {WSA883X_OTP_REG_3, 0xC9}, + {WSA883X_OTP_REG_4, 0x40}, + {WSA883X_TAGC_CTL, 0x1B}, + {WSA883X_ADC_2, 0x00}, + {WSA883X_ADC_7, 0x85}, + {WSA883X_ADC_7, 0x87}, + {WSA883X_CKWD_CTL_0, 0x14}, + {WSA883X_CKWD_CTL_1, 0x1B}, + {WSA883X_GMAMP_SUP1, 0xE2}, +}; + +static void wsa883x_init(struct wsa883x_priv *wsa883x) +{ + struct regmap *regmap = wsa883x->regmap; + int variant, version; + + regmap_read(regmap, WSA883X_OTP_REG_0, &variant); + wsa883x->variant = variant & WSA883X_ID_MASK; + + regmap_read(regmap, WSA883X_CHIP_ID0, &version); + wsa883x->version = version; + + switch (wsa883x->variant) { + case WSA8830: + dev_info(wsa883x->dev, "WSA883X Version 1_%d, Variant: WSA8830\n", + wsa883x->version); + break; + case WSA8835: + dev_info(wsa883x->dev, "WSA883X Version 1_%d, Variant: WSA8835\n", + wsa883x->version); + break; + case WSA8832: + dev_info(wsa883x->dev, "WSA883X Version 1_%d, Variant: WSA8832\n", + wsa883x->version); + break; + case WSA8835_V2: + dev_info(wsa883x->dev, "WSA883X Version 1_%d, Variant: WSA8835_V2\n", + wsa883x->version); + break; + default: + break; + } + + wsa883x->comp_offset = COMP_OFFSET2; + + /* Initial settings */ + regmap_multi_reg_write(regmap, reg_init, ARRAY_SIZE(reg_init)); + + if (wsa883x->variant == WSA8830 || wsa883x->variant == WSA8832) { + wsa883x->comp_offset = COMP_OFFSET3; + regmap_update_bits(regmap, WSA883X_DRE_CTL_0, + WSA883X_DRE_OFFSET_MASK, + wsa883x->comp_offset); + } +} + +static int wsa883x_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wsa883x_priv *wsa883x = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_ATTACHED && slave->dev_num > 0) + wsa883x_init(wsa883x); + + return 0; +} + +static int wsa883x_port_prep(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops state) +{ + struct wsa883x_priv *wsa883x = dev_get_drvdata(&slave->dev); + + if (state == SDW_OPS_PORT_POST_PREP) + wsa883x->port_prepared[prepare_ch->num - 1] = true; + else + wsa883x->port_prepared[prepare_ch->num - 1] = false; + + return 0; +} + +static const struct sdw_slave_ops wsa883x_slave_ops = { + .update_status = wsa883x_update_status, + .port_prep = wsa883x_port_prep, +}; + +static int wsa_dev_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wsa883x->dev_mode; + + return 0; +} + +static int wsa_dev_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component); + + if (wsa883x->dev_mode == ucontrol->value.enumerated.item[0]) + return 0; + + wsa883x->dev_mode = ucontrol->value.enumerated.item[0]; + + return 1; +} + +static const DECLARE_TLV_DB_SCALE(pa_gain, -300, 150, -300); + +static int wsa883x_get_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa883x_priv *data = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + ucontrol->value.integer.value[0] = data->port_enable[portidx]; + + return 0; +} + +static int wsa883x_set_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa883x_priv *data = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + if (ucontrol->value.integer.value[0]) { + if (data->port_enable[portidx]) + return 0; + + data->port_enable[portidx] = true; + } else { + if (!data->port_enable[portidx]) + return 0; + + data->port_enable[portidx] = false; + } + + return 1; +} + +static int wsa883x_get_comp_offset(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = wsa883x->comp_offset; + + return 0; +} + +static int wsa883x_set_comp_offset(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component); + + if (wsa883x->comp_offset == ucontrol->value.integer.value[0]) + return 0; + + wsa883x->comp_offset = ucontrol->value.integer.value[0]; + + return 1; +} + +static int wsa883x_codec_probe(struct snd_soc_component *comp) +{ + struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa883x->regmap); + + return 0; +} + +static int wsa883x_spkr_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + switch (wsa883x->dev_mode) { + case RECEIVER: + snd_soc_component_write_field(component, WSA883X_CDC_PATH_MODE, + WSA883X_RXD_MODE_MASK, + WSA883X_RXD_MODE_HIFI); + snd_soc_component_write_field(component, WSA883X_SPKR_PWM_CLK_CTL, + WSA883X_SPKR_PWM_FREQ_SEL_MASK, + WSA883X_SPKR_PWM_FREQ_F600KHZ); + snd_soc_component_write_field(component, WSA883X_DRE_CTL_0, + WSA883X_DRE_PROG_DELAY_MASK, 0x0); + break; + case SPEAKER: + snd_soc_component_write_field(component, WSA883X_CDC_PATH_MODE, + WSA883X_RXD_MODE_MASK, + WSA883X_RXD_MODE_NORMAL); + snd_soc_component_write_field(component, WSA883X_SPKR_PWM_CLK_CTL, + WSA883X_SPKR_PWM_FREQ_SEL_MASK, + WSA883X_SPKR_PWM_FREQ_F300KHZ); + snd_soc_component_write_field(component, WSA883X_DRE_CTL_0, + WSA883X_DRE_PROG_DELAY_MASK, 0x9); + break; + default: + break; + } + + if (wsa883x->port_enable[WSA883X_PORT_COMP]) + snd_soc_component_write_field(component, WSA883X_DRE_CTL_0, + WSA883X_DRE_OFFSET_MASK, + wsa883x->comp_offset); + snd_soc_component_write_field(component, WSA883X_VBAT_ADC_FLT_CTL, + WSA883X_VBAT_ADC_COEF_SEL_MASK, + WSA883X_VBAT_ADC_COEF_F_1DIV16); + snd_soc_component_write_field(component, WSA883X_VBAT_ADC_FLT_CTL, + WSA883X_VBAT_ADC_FLT_EN_MASK, 0x1); + snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, + WSA883X_PDM_EN_MASK, + WSA883X_PDM_ENABLE); + + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write_field(component, WSA883X_VBAT_ADC_FLT_CTL, + WSA883X_VBAT_ADC_FLT_EN_MASK, 0x0); + snd_soc_component_write_field(component, WSA883X_VBAT_ADC_FLT_CTL, + WSA883X_VBAT_ADC_COEF_SEL_MASK, + WSA883X_VBAT_ADC_COEF_F_1DIV2); + snd_soc_component_write_field(component, WSA883X_PA_FSM_CTL, + WSA883X_GLOBAL_PA_EN_MASK, 0); + snd_soc_component_write_field(component, WSA883X_PDM_WD_CTL, + WSA883X_PDM_EN_MASK, 0); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget wsa883x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_SPK("SPKR", wsa883x_spkr_event), +}; + +static const struct snd_kcontrol_new wsa883x_snd_controls[] = { + SOC_SINGLE_RANGE_TLV("PA Volume", WSA883X_DRE_CTL_1, 1, + 0x0, 0x1f, 1, pa_gain), + SOC_ENUM_EXT("WSA MODE", wsa_dev_mode_enum, + wsa_dev_mode_get, wsa_dev_mode_put), + SOC_SINGLE_EXT("COMP Offset", SND_SOC_NOPM, 0, 4, 0, + wsa883x_get_comp_offset, wsa883x_set_comp_offset), + SOC_SINGLE_EXT("DAC Switch", WSA883X_PORT_DAC, 0, 1, 0, + wsa883x_get_swr_port, wsa883x_set_swr_port), + SOC_SINGLE_EXT("COMP Switch", WSA883X_PORT_COMP, 0, 1, 0, + wsa883x_get_swr_port, wsa883x_set_swr_port), + SOC_SINGLE_EXT("BOOST Switch", WSA883X_PORT_BOOST, 0, 1, 0, + wsa883x_get_swr_port, wsa883x_set_swr_port), + SOC_SINGLE_EXT("VISENSE Switch", WSA883X_PORT_VISENSE, 0, 1, 0, + wsa883x_get_swr_port, wsa883x_set_swr_port), +}; + +static const struct snd_soc_dapm_route wsa883x_audio_map[] = { + {"SPKR", NULL, "IN"}, +}; + +static const struct snd_soc_component_driver wsa883x_component_drv = { + .name = "WSA883x", + .probe = wsa883x_codec_probe, + .controls = wsa883x_snd_controls, + .num_controls = ARRAY_SIZE(wsa883x_snd_controls), + .dapm_widgets = wsa883x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa883x_dapm_widgets), + .dapm_routes = wsa883x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa883x_audio_map), +}; + +static int wsa883x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wsa883x_priv *wsa883x = dev_get_drvdata(dai->dev); + int i; + + wsa883x->active_ports = 0; + for (i = 0; i < WSA883X_MAX_SWR_PORTS; i++) { + if (!wsa883x->port_enable[i]) + continue; + + wsa883x->port_config[wsa883x->active_ports] = wsa883x_pconfig[i]; + wsa883x->active_ports++; + } + + wsa883x->sconfig.frame_rate = params_rate(params); + + return sdw_stream_add_slave(wsa883x->slave, &wsa883x->sconfig, + wsa883x->port_config, wsa883x->active_ports, + wsa883x->sruntime); +} + +static int wsa883x_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wsa883x_priv *wsa883x = dev_get_drvdata(dai->dev); + + sdw_stream_remove_slave(wsa883x->slave, wsa883x->sruntime); + + return 0; +} + +static int wsa883x_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wsa883x_priv *wsa883x = dev_get_drvdata(dai->dev); + + wsa883x->sruntime = stream; + + return 0; +} + +static int wsa883x_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + + if (mute) { + snd_soc_component_write_field(component, WSA883X_DRE_CTL_1, + WSA883X_DRE_GAIN_EN_MASK, 0); + snd_soc_component_write_field(component, WSA883X_PA_FSM_CTL, + WSA883X_GLOBAL_PA_EN_MASK, 0); + + } else { + snd_soc_component_write_field(component, WSA883X_DRE_CTL_1, + WSA883X_DRE_GAIN_EN_MASK, + WSA883X_DRE_GAIN_FROM_CSR); + snd_soc_component_write_field(component, WSA883X_PA_FSM_CTL, + WSA883X_GLOBAL_PA_EN_MASK, + WSA883X_GLOBAL_PA_ENABLE); + + } + + return 0; +} + +static const struct snd_soc_dai_ops wsa883x_dai_ops = { + .hw_params = wsa883x_hw_params, + .hw_free = wsa883x_hw_free, + .mute_stream = wsa883x_digital_mute, + .set_stream = wsa883x_set_sdw_stream, + .mute_unmute_on_trigger = true, +}; + +static struct snd_soc_dai_driver wsa883x_dais[] = { + { + .name = "SPKR", + .playback = { + .stream_name = "SPKR Playback", + .rates = WSA883X_RATES | WSA883X_FRAC_RATES, + .formats = WSA883X_FORMATS, + .rate_min = 8000, + .rate_max = 352800, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &wsa883x_dai_ops, + }, +}; + +static int wsa883x_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct wsa883x_priv *wsa883x; + struct device *dev = &pdev->dev; + int ret; + + wsa883x = devm_kzalloc(dev, sizeof(*wsa883x), GFP_KERNEL); + if (!wsa883x) + return -ENOMEM; + + wsa883x->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(wsa883x->vdd)) + return dev_err_probe(dev, PTR_ERR(wsa883x->vdd), + "No vdd regulator found\n"); + + ret = regulator_enable(wsa883x->vdd); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable vdd regulator\n"); + + wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_FLAGS_BIT_NONEXCLUSIVE | GPIOD_OUT_HIGH); + if (IS_ERR(wsa883x->sd_n)) { + ret = dev_err_probe(dev, PTR_ERR(wsa883x->sd_n), + "Shutdown Control GPIO not found\n"); + goto err; + } + + dev_set_drvdata(dev, wsa883x); + wsa883x->slave = pdev; + wsa883x->dev = dev; + wsa883x->sconfig.ch_count = 1; + wsa883x->sconfig.bps = 1; + wsa883x->sconfig.direction = SDW_DATA_DIR_RX; + wsa883x->sconfig.type = SDW_STREAM_PDM; + + pdev->prop.sink_ports = GENMASK(WSA883X_MAX_SWR_PORTS, 0); + pdev->prop.simple_clk_stop_capable = true; + pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; + pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + gpiod_direction_output(wsa883x->sd_n, 0); + + wsa883x->regmap = devm_regmap_init_sdw(pdev, &wsa883x_regmap_config); + if (IS_ERR(wsa883x->regmap)) { + gpiod_direction_output(wsa883x->sd_n, 1); + ret = dev_err_probe(dev, PTR_ERR(wsa883x->regmap), + "regmap_init failed\n"); + goto err; + } + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = devm_snd_soc_register_component(dev, + &wsa883x_component_drv, + wsa883x_dais, + ARRAY_SIZE(wsa883x_dais)); +err: + if (ret) + regulator_disable(wsa883x->vdd); + + return ret; + +} + +static int __maybe_unused wsa883x_runtime_suspend(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + + return 0; +} + +static int __maybe_unused wsa883x_runtime_resume(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + + regcache_cache_only(regmap, false); + regcache_sync(regmap); + + return 0; +} + +static const struct dev_pm_ops wsa883x_pm_ops = { + SET_RUNTIME_PM_OPS(wsa883x_runtime_suspend, wsa883x_runtime_resume, NULL) +}; + +static const struct sdw_device_id wsa883x_swr_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x0202, 0), + {}, +}; + +MODULE_DEVICE_TABLE(sdw, wsa883x_swr_id); + +static struct sdw_driver wsa883x_codec_driver = { + .driver = { + .name = "wsa883x-codec", + .pm = &wsa883x_pm_ops, + .suppress_bind_attrs = true, + }, + .probe = wsa883x_probe, + .ops = &wsa883x_slave_ops, + .id_table = wsa883x_swr_id, +}; + +module_sdw_driver(wsa883x_codec_driver); + +MODULE_DESCRIPTION("WSA883x codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c new file mode 100644 index 0000000000..993d76b18b --- /dev/null +++ b/sound/soc/codecs/wsa884x.c @@ -0,0 +1,1936 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WSA884X_BASE 0x3000 +#define WSA884X_ANA_BG_TSADC_BASE (WSA884X_BASE + 0x0001) +#define WSA884X_BG_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x00) +#define WSA884X_ADC_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x01) +#define WSA884X_BOP1_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x02) +#define WSA884X_BOP2_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x03) +#define WSA884X_BOP2_PROG_BOP2_VTH_MASK 0xf0 +#define WSA884X_BOP2_PROG_BOP2_VTH_SHIFT 4 +#define WSA884X_BOP2_PROG_BOP2_HYST_MASK 0x0f +#define WSA884X_BOP2_PROG_BOP2_HYST_SHIFT 0 +#define WSA884X_UVLO_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x04) +#define WSA884X_UVLO_PROG1 (WSA884X_ANA_BG_TSADC_BASE + 0x05) +#define WSA884X_SPARE_CTRL_0 (WSA884X_ANA_BG_TSADC_BASE + 0x06) +#define WSA884X_SPARE_CTRL_1 (WSA884X_ANA_BG_TSADC_BASE + 0x07) +#define WSA884X_SPARE_CTRL_2 (WSA884X_ANA_BG_TSADC_BASE + 0x08) +#define WSA884X_SPARE_CTRL_3 (WSA884X_ANA_BG_TSADC_BASE + 0x09) +#define WSA884X_REF_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x0a) +#define WSA884X_REF_CTRL_BG_RDY_SEL_MASK 0x03 +#define WSA884X_REF_CTRL_BG_RDY_SEL_SHIFT 0 +#define WSA884X_BG_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0b) +#define WSA884X_BG_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x0c) +#define WSA884X_ADC_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x0d) +#define WSA884X_ADC_IREF_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0e) +#define WSA884X_ADC_ISENS_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0f) +#define WSA884X_ADC_CLK_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x10) +#define WSA884X_ADC_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x11) +#define WSA884X_ADC_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x12) +#define WSA884X_VBAT_SNS (WSA884X_ANA_BG_TSADC_BASE + 0x13) +#define WSA884X_DOUT_MSB (WSA884X_ANA_BG_TSADC_BASE + 0x14) +#define WSA884X_DOUT_LSB (WSA884X_ANA_BG_TSADC_BASE + 0x15) +#define WSA884X_BOP_ATEST_SEL (WSA884X_ANA_BG_TSADC_BASE + 0x16) +#define WSA884X_MISC0 (WSA884X_ANA_BG_TSADC_BASE + 0x17) +#define WSA884X_MISC1 (WSA884X_ANA_BG_TSADC_BASE + 0x18) +#define WSA884X_MISC2 (WSA884X_ANA_BG_TSADC_BASE + 0x19) +#define WSA884X_MISC3 (WSA884X_ANA_BG_TSADC_BASE + 0x1a) +#define WSA884X_SPARE_TSBG_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1b) +#define WSA884X_SPARE_TUNE_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1c) +#define WSA884X_SPARE_TUNE_1 (WSA884X_ANA_BG_TSADC_BASE + 0x1d) + +#define WSA884X_ANA_IVSENSE_BASE (WSA884X_BASE + 0x0020) +#define WSA884X_VSENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x00) +#define WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK 0xe0 +#define WSA884X_VSENSE1_GAIN_VSENSE_FE_SHIFT 5 +#define WSA884X_ISENSE2 (WSA884X_ANA_IVSENSE_BASE + 0x01) +#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK 0xe0 +#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_SHIFT 5 + +#define WSA884X_SPARE_CTL_1 (WSA884X_ANA_IVSENSE_BASE + 0x02) +#define WSA884X_SPARE_CTL_2 (WSA884X_ANA_IVSENSE_BASE + 0x03) +#define WSA884X_SPARE_CTL_3 (WSA884X_ANA_IVSENSE_BASE + 0x04) +#define WSA884X_SPARE_CTL_4 (WSA884X_ANA_IVSENSE_BASE + 0x05) +#define WSA884X_EN (WSA884X_ANA_IVSENSE_BASE + 0x06) +#define WSA884X_OVERRIDE1 (WSA884X_ANA_IVSENSE_BASE + 0x07) +#define WSA884X_OVERRIDE2 (WSA884X_ANA_IVSENSE_BASE + 0x08) +#define WSA884X_ISENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x09) +#define WSA884X_ISENSE_CAL (WSA884X_ANA_IVSENSE_BASE + 0x0a) +#define WSA884X_MISC (WSA884X_ANA_IVSENSE_BASE + 0x0b) +#define WSA884X_ADC_0 (WSA884X_ANA_IVSENSE_BASE + 0x0c) +#define WSA884X_ADC_1 (WSA884X_ANA_IVSENSE_BASE + 0x0d) +#define WSA884X_ADC_2 (WSA884X_ANA_IVSENSE_BASE + 0x0e) +#define WSA884X_ADC_3 (WSA884X_ANA_IVSENSE_BASE + 0x0f) +#define WSA884X_ADC_4 (WSA884X_ANA_IVSENSE_BASE + 0x10) +#define WSA884X_ADC_5 (WSA884X_ANA_IVSENSE_BASE + 0x11) +#define WSA884X_ADC_6 (WSA884X_ANA_IVSENSE_BASE + 0x12) +#define WSA884X_ADC_7 (WSA884X_ANA_IVSENSE_BASE + 0x13) +#define WSA884X_STATUS (WSA884X_ANA_IVSENSE_BASE + 0x14) +#define WSA884X_IVSENSE_SPARE_TUNE_1 (WSA884X_ANA_IVSENSE_BASE + 0x15) +#define WSA884X_SPARE_TUNE_2 (WSA884X_ANA_IVSENSE_BASE + 0x16) +#define WSA884X_SPARE_TUNE_3 (WSA884X_ANA_IVSENSE_BASE + 0x17) +#define WSA884X_SPARE_TUNE_4 (WSA884X_ANA_IVSENSE_BASE + 0x18) + +#define WSA884X_ANA_SPK_TOP_BASE (WSA884X_BASE + 0x0040) +#define WSA884X_TOP_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x00) +#define WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK 0x01 +#define WSA884X_CLIP_DET_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x01) +#define WSA884X_CLIP_DET_CTRL2 (WSA884X_ANA_SPK_TOP_BASE + 0x02) +#define WSA884X_DAC_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x03) +#define WSA884X_DAC_VCM_CTRL_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x04) +#define WSA884X_DAC_VCM_CTRL_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x05) +#define WSA884X_DAC_VCM_CTRL_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x06) +#define WSA884X_DAC_VCM_CTRL_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x07) +#define WSA884X_DAC_VCM_CTRL_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x08) +#define WSA884X_DAC_VCM_CTRL_REG6 (WSA884X_ANA_SPK_TOP_BASE + 0x09) +#define WSA884X_PWM_CLK_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0a) +#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_MASK 0x80 +#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_SHIFT 7 +#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_MASK 0x40 +#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_SHIFT 6 +#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_MASK 0x30 +#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_SHIFT 4 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK 0x08 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_SHIFT 3 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_MASK 0x06 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_SHIFT 1 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_MASK 0x01 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_SHIFT 0 +#define WSA884X_DRV_LF_LDO_SEL (WSA884X_ANA_SPK_TOP_BASE + 0x0b) +#define WSA884X_OCP_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0c) +#define WSA884X_PDRV_HS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0d) +#define WSA884X_PDRV_LS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0e) +#define WSA884X_SPK_TOP_SPARE_CTL_1 (WSA884X_ANA_SPK_TOP_BASE + 0x0f) +#define WSA884X_SPK_TOP_SPARE_CTL_2 (WSA884X_ANA_SPK_TOP_BASE + 0x10) +#define WSA884X_SPK_TOP_SPARE_CTL_3 (WSA884X_ANA_SPK_TOP_BASE + 0x11) +#define WSA884X_SPK_TOP_SPARE_CTL_4 (WSA884X_ANA_SPK_TOP_BASE + 0x12) +#define WSA884X_SPARE_CTL_5 (WSA884X_ANA_SPK_TOP_BASE + 0x13) +#define WSA884X_DAC_EN_DEBUG_REG (WSA884X_ANA_SPK_TOP_BASE + 0x14) +#define WSA884X_DAC_OPAMP_BIAS1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x15) +#define WSA884X_DAC_OPAMP_BIAS2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x16) +#define WSA884X_DAC_TUNE1 (WSA884X_ANA_SPK_TOP_BASE + 0x17) +#define WSA884X_DAC_VOLTAGE_CTRL_REG (WSA884X_ANA_SPK_TOP_BASE + 0x18) +#define WSA884X_ATEST1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x19) +#define WSA884X_ATEST2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x1a) +#define WSA884X_TOP_BIAS_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x1b) +#define WSA884X_TOP_BIAS_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1c) +#define WSA884X_TOP_BIAS_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x1d) +#define WSA884X_TOP_BIAS_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x1e) +#define WSA884X_PWRSTG_DBG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1f) +#define WSA884X_DRV_LF_BLK_EN (WSA884X_ANA_SPK_TOP_BASE + 0x20) +#define WSA884X_DRV_LF_EN (WSA884X_ANA_SPK_TOP_BASE + 0x21) +#define WSA884X_DRV_LF_MASK_DCC_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x22) +#define WSA884X_DRV_LF_MISC_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x23) +#define WSA884X_DRV_LF_REG_GAIN (WSA884X_ANA_SPK_TOP_BASE + 0x24) +#define WSA884X_DRV_OS_CAL_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x25) +#define WSA884X_DRV_OS_CAL_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x26) +#define WSA884X_PWRSTG_DBG (WSA884X_ANA_SPK_TOP_BASE + 0x27) +#define WSA884X_BBM_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x28) +#define WSA884X_TOP_MISC1 (WSA884X_ANA_SPK_TOP_BASE + 0x29) +#define WSA884X_DAC_VCM_CTRL_REG7 (WSA884X_ANA_SPK_TOP_BASE + 0x2a) +#define WSA884X_TOP_BIAS_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x2b) +#define WSA884X_DRV_LF_MISC_CTL2 (WSA884X_ANA_SPK_TOP_BASE + 0x2c) +#define WSA884X_SPK_TOP_SPARE_TUNE_2 (WSA884X_ANA_SPK_TOP_BASE + 0x2d) +#define WSA884X_SPK_TOP_SPARE_TUNE_3 (WSA884X_ANA_SPK_TOP_BASE + 0x2e) +#define WSA884X_SPK_TOP_SPARE_TUNE_4 (WSA884X_ANA_SPK_TOP_BASE + 0x2f) +#define WSA884X_SPARE_TUNE_5 (WSA884X_ANA_SPK_TOP_BASE + 0x30) +#define WSA884X_SPARE_TUNE_6 (WSA884X_ANA_SPK_TOP_BASE + 0x31) +#define WSA884X_SPARE_TUNE_7 (WSA884X_ANA_SPK_TOP_BASE + 0x32) +#define WSA884X_SPARE_TUNE_8 (WSA884X_ANA_SPK_TOP_BASE + 0x33) +#define WSA884X_SPARE_TUNE_9 (WSA884X_ANA_SPK_TOP_BASE + 0x34) +#define WSA884X_SPARE_TUNE_10 (WSA884X_ANA_SPK_TOP_BASE + 0x35) +#define WSA884X_PA_STATUS0 (WSA884X_ANA_SPK_TOP_BASE + 0x36) +#define WSA884X_PA_STATUS1 (WSA884X_ANA_SPK_TOP_BASE + 0x37) +#define WSA884X_PA_STATUS2 (WSA884X_ANA_SPK_TOP_BASE + 0x38) +#define WSA884X_PA_STATUS3 (WSA884X_ANA_SPK_TOP_BASE + 0x39) +#define WSA884X_PA_STATUS4 (WSA884X_ANA_SPK_TOP_BASE + 0x3a) +#define WSA884X_PA_STATUS5 (WSA884X_ANA_SPK_TOP_BASE + 0x3b) +#define WSA884X_SPARE_RO_1 (WSA884X_ANA_SPK_TOP_BASE + 0x3c) +#define WSA884X_SPARE_RO_2 (WSA884X_ANA_SPK_TOP_BASE + 0x3d) +#define WSA884X_SPARE_RO_3 (WSA884X_ANA_SPK_TOP_BASE + 0x3e) + +#define WSA884X_ANA_BOOST_BASE (WSA884X_BASE + 0x0090) +#define WSA884X_STB_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x00) +#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK 0xf8 +#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_SHIFT 3 +#define WSA884X_STB_CTRL1_VOUT_FS_MASK 0x07 +#define WSA884X_STB_CTRL1_VOUT_FS_SHIFT 0 +#define WSA884X_CURRENT_LIMIT (WSA884X_ANA_BOOST_BASE + 0x01) +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK 0x80 +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_SHIFT 7 +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK 0x7c +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_SHIFT 2 +#define WSA884X_CURRENT_LIMIT_CLK_PHASE_SHIFT 0 +#define WSA884X_BYP_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x02) +#define WSA884X_SPARE_CTL_0 (WSA884X_ANA_BOOST_BASE + 0x03) +#define WSA884X_BOOST_SPARE_CTL_1 (WSA884X_ANA_BOOST_BASE + 0x04) +#define WSA884X_SPARE_RO_0 (WSA884X_ANA_BOOST_BASE + 0x05) +#define WSA884X_BOOST_SPARE_RO_1 (WSA884X_ANA_BOOST_BASE + 0x06) +#define WSA884X_IBIAS1 (WSA884X_ANA_BOOST_BASE + 0x07) +#define WSA884X_IBIAS2 (WSA884X_ANA_BOOST_BASE + 0x08) +#define WSA884X_IBIAS3 (WSA884X_ANA_BOOST_BASE + 0x09) +#define WSA884X_EN_CTRL (WSA884X_ANA_BOOST_BASE + 0x0a) +#define WSA884X_STB_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0b) +#define WSA884X_STB_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0c) +#define WSA884X_STB_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x0d) +#define WSA884X_BYP_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0e) +#define WSA884X_BYP_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0f) +#define WSA884X_ZX_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x10) +#define WSA884X_ZX_CTRL1_ZX_DET_EN_MASK 0x80 +#define WSA884X_ZX_CTRL1_ZX_DET_EN_SHIFT 7 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_MASK 0x40 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_SHIFT 6 +#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_MASK 0x20 +#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_SHIFT 5 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK 0x18 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_SHIFT 3 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_MASK 0x04 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_SHIFT 2 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_MASK 0x02 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_SHIFT 1 +#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_MASK 0x01 +#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_SHIFT 0 +#define WSA884X_ZX_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x11) +#define WSA884X_BLEEDER_CTRL (WSA884X_ANA_BOOST_BASE + 0x12) +#define WSA884X_BOOST_MISC (WSA884X_ANA_BOOST_BASE + 0x13) +#define WSA884X_PWRSTAGE_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x14) +#define WSA884X_PWRSTAGE_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x15) +#define WSA884X_PWRSTAGE_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x16) +#define WSA884X_PWRSTAGE_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x17) +#define WSA884X_MAXD_REG1 (WSA884X_ANA_BOOST_BASE + 0x18) +#define WSA884X_MAXD_REG2 (WSA884X_ANA_BOOST_BASE + 0x19) +#define WSA884X_ILIM_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1a) +#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_MASK 0x80 +#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_SHIFT 0x07 +#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_MASK 0x40 +#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_SHIFT 0x06 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_MASK 0x38 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_SHIFT 0x03 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK 0x07 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_SHIFT 0x00 +#define WSA884X_ILIM_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1b) +#define WSA884X_TEST_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1c) +#define WSA884X_TEST_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1d) +#define WSA884X_SPARE1 (WSA884X_ANA_BOOST_BASE + 0x1e) +#define WSA884X_BOOT_CAP_CHECK (WSA884X_ANA_BOOST_BASE + 0x1f) + +#define WSA884X_ANA_PON_LDOL_BASE (WSA884X_BASE + 0x00b0) +#define WSA884X_PON_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x00) +#define WSA884X_PWRSAV_CTL (WSA884X_ANA_PON_LDOL_BASE + 0x01) +#define WSA884X_PON_LDOL_SPARE_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x02) +#define WSA884X_PON_LDOL_SPARE_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x03) +#define WSA884X_PON_LDOL_SPARE_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x04) +#define WSA884X_PON_LDOL_SPARE_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x05) +#define WSA884X_PON_CLT_1 (WSA884X_ANA_PON_LDOL_BASE + 0x06) +#define WSA884X_PON_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x07) +#define WSA884X_PON_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x08) +#define WSA884X_CKWD_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x09) +#define WSA884X_CKWD_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0a) +#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK 0x20 +#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_SHIFT 5 +#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK 0x1f +#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_SHIFT 0 +#define WSA884X_CKWD_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x0b) +#define WSA884X_CKSK_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0c) +#define WSA884X_PADSW_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0d) +#define WSA884X_TEST_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0e) +#define WSA884X_TEST_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0f) +#define WSA884X_STATUS_0 (WSA884X_ANA_PON_LDOL_BASE + 0x10) +#define WSA884X_STATUS_1 (WSA884X_ANA_PON_LDOL_BASE + 0x11) +#define WSA884X_PON_LDOL_SPARE_TUNE_0 (WSA884X_ANA_PON_LDOL_BASE + 0x12) +#define WSA884X_PON_LDOL_SPARE_TUNE_1 (WSA884X_ANA_PON_LDOL_BASE + 0x13) +#define WSA884X_PON_LDOL_SPARE_TUNE_2 (WSA884X_ANA_PON_LDOL_BASE + 0x14) +#define WSA884X_PON_LDOL_SPARE_TUNE_3 (WSA884X_ANA_PON_LDOL_BASE + 0x15) +#define WSA884X_PON_LDOL_SPARE_TUNE_4 (WSA884X_ANA_PON_LDOL_BASE + 0x16) + +#define WSA884X_DIG_CTRL0_BASE (WSA884X_BASE + 0x0400) +#define WSA884X_DIG_CTRL0_PAGE (WSA884X_DIG_CTRL0_BASE + 0x00) +#define WSA884X_CHIP_ID0 (WSA884X_DIG_CTRL0_BASE + 0x01) +#define WSA884X_CHIP_ID1 (WSA884X_DIG_CTRL0_BASE + 0x02) +#define WSA884X_CHIP_ID2 (WSA884X_DIG_CTRL0_BASE + 0x03) +#define WSA884X_CHIP_ID3 (WSA884X_DIG_CTRL0_BASE + 0x04) +#define WSA884X_BUS_ID (WSA884X_DIG_CTRL0_BASE + 0x05) +#define WSA884X_CDC_RST_CTL (WSA884X_DIG_CTRL0_BASE + 0x10) +#define WSA884X_SWR_RESET_EN (WSA884X_DIG_CTRL0_BASE + 0x14) +#define WSA884X_TOP_CLK_CFG (WSA884X_DIG_CTRL0_BASE + 0x18) +#define WSA884X_SWR_CLK_RATE (WSA884X_DIG_CTRL0_BASE + 0x19) +#define WSA884X_CDC_PATH_MODE (WSA884X_DIG_CTRL0_BASE + 0x1a) +#define WSA884X_CDC_PATH_MODE_RXD_MODE_MASK 0x02 +#define WSA884X_CDC_PATH_MODE_RXD_MODE_SHIFT 0 +#define WSA884X_CDC_PATH_MODE_TXD_MODE_MASK 0x01 +#define WSA884X_CDC_PATH_MODE_TXD_MODE_SHIFT 0 +#define WSA884X_CDC_CLK_CTL (WSA884X_DIG_CTRL0_BASE + 0x1c) +#define WSA884X_PA_FSM_EN (WSA884X_DIG_CTRL0_BASE + 0x30) +#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK 0x01 +#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_SHIFT 0 +#define WSA884X_PA_FSM_CTL0 (WSA884X_DIG_CTRL0_BASE + 0x31) +#define WSA884X_PA_FSM_CTL1 (WSA884X_DIG_CTRL0_BASE + 0x32) +#define WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK 0x38 +#define WSA884X_PA_FSM_TIMER0 (WSA884X_DIG_CTRL0_BASE + 0x33) +#define WSA884X_PA_FSM_TIMER1 (WSA884X_DIG_CTRL0_BASE + 0x34) +#define WSA884X_PA_FSM_STA0 (WSA884X_DIG_CTRL0_BASE + 0x35) +#define WSA884X_PA_FSM_STA1 (WSA884X_DIG_CTRL0_BASE + 0x36) +#define WSA884X_PA_FSM_ERR_CTL (WSA884X_DIG_CTRL0_BASE + 0x37) +#define WSA884X_PA_FSM_ERR_COND0 (WSA884X_DIG_CTRL0_BASE + 0x38) +#define WSA884X_PA_FSM_ERR_COND1 (WSA884X_DIG_CTRL0_BASE + 0x39) +#define WSA884X_PA_FSM_MSK0 (WSA884X_DIG_CTRL0_BASE + 0x3a) +#define WSA884X_PA_FSM_MSK1 (WSA884X_DIG_CTRL0_BASE + 0x3b) +#define WSA884X_PA_FSM_BYP_CTL (WSA884X_DIG_CTRL0_BASE + 0x3c) +#define WSA884X_PA_FSM_BYP0 (WSA884X_DIG_CTRL0_BASE + 0x3d) +#define WSA884X_PA_FSM_BYP1 (WSA884X_DIG_CTRL0_BASE + 0x3e) +#define WSA884X_TADC_VALUE_CTL (WSA884X_DIG_CTRL0_BASE + 0x50) +#define WSA884X_TEMP_DETECT_CTL (WSA884X_DIG_CTRL0_BASE + 0x51) +#define WSA884X_TEMP_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x52) +#define WSA884X_TEMP_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x53) +#define WSA884X_TEMP_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x54) +#define WSA884X_TEMP_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x55) +#define WSA884X_TEMP_CONFIG0 (WSA884X_DIG_CTRL0_BASE + 0x56) +#define WSA884X_TEMP_CONFIG1 (WSA884X_DIG_CTRL0_BASE + 0x57) +#define WSA884X_VBAT_THRM_FLT_CTL (WSA884X_DIG_CTRL0_BASE + 0x58) +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_MASK 0xe0 +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_SHIFT 5 +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_FLT_EN_SHIFT 4 +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK 0x0e +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_SHIFT 1 +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_FLT_EN_SHIFT 0 +#define WSA884X_VBAT_CAL_CTL (WSA884X_DIG_CTRL0_BASE + 0x59) +#define WSA884X_VBAT_CAL_CTL_RESERVE_MASK 0x0e +#define WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK 0x01 +#define WSA884X_VBAT_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x5a) +#define WSA884X_VBAT_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x5b) +#define WSA884X_VBAT_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x5c) +#define WSA884X_VBAT_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x5d) +#define WSA884X_VBAT_CAL_MSB (WSA884X_DIG_CTRL0_BASE + 0x5e) +#define WSA884X_VBAT_CAL_LSB (WSA884X_DIG_CTRL0_BASE + 0x5f) +#define WSA884X_UVLO_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x60) +#define WSA884X_BOP_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x61) +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK 0x1e +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_SHIFT 1 +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK 0x1 +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_SHIFT 0 +#define WSA884X_VBAT_ZONE_DETC_CTL (WSA884X_DIG_CTRL0_BASE + 0x64) +#define WSA884X_CPS_CTL (WSA884X_DIG_CTRL0_BASE + 0x68) +#define WSA884X_CDC_RX_CTL (WSA884X_DIG_CTRL0_BASE + 0x70) +#define WSA884X_CDC_SPK_DSM_A1_0 (WSA884X_DIG_CTRL0_BASE + 0x71) +#define WSA884X_CDC_SPK_DSM_A1_1 (WSA884X_DIG_CTRL0_BASE + 0x72) +#define WSA884X_CDC_SPK_DSM_A2_0 (WSA884X_DIG_CTRL0_BASE + 0x73) +#define WSA884X_CDC_SPK_DSM_A2_1 (WSA884X_DIG_CTRL0_BASE + 0x74) +#define WSA884X_CDC_SPK_DSM_A3_0 (WSA884X_DIG_CTRL0_BASE + 0x75) +#define WSA884X_CDC_SPK_DSM_A3_1 (WSA884X_DIG_CTRL0_BASE + 0x76) +#define WSA884X_CDC_SPK_DSM_A4_0 (WSA884X_DIG_CTRL0_BASE + 0x77) +#define WSA884X_CDC_SPK_DSM_A4_1 (WSA884X_DIG_CTRL0_BASE + 0x78) +#define WSA884X_CDC_SPK_DSM_A5_0 (WSA884X_DIG_CTRL0_BASE + 0x79) +#define WSA884X_CDC_SPK_DSM_A5_1 (WSA884X_DIG_CTRL0_BASE + 0x7a) +#define WSA884X_CDC_SPK_DSM_A6_0 (WSA884X_DIG_CTRL0_BASE + 0x7b) +#define WSA884X_CDC_SPK_DSM_A7_0 (WSA884X_DIG_CTRL0_BASE + 0x7c) +#define WSA884X_CDC_SPK_DSM_C_0 (WSA884X_DIG_CTRL0_BASE + 0x7d) +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK 0xf0 +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_SHIFT 4 +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK 0x0f +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_C_1 (WSA884X_DIG_CTRL0_BASE + 0x7e) +#define WSA884X_CDC_SPK_DSM_C_2 (WSA884X_DIG_CTRL0_BASE + 0x7f) +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK 0xf0 +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_SHIFT 4 +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_MASK 0x0f +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_C_3 (WSA884X_DIG_CTRL0_BASE + 0x80) +#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK 0x3f +#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_R1 (WSA884X_DIG_CTRL0_BASE + 0x81) +#define WSA884X_CDC_SPK_DSM_R2 (WSA884X_DIG_CTRL0_BASE + 0x82) +#define WSA884X_CDC_SPK_DSM_R3 (WSA884X_DIG_CTRL0_BASE + 0x83) +#define WSA884X_CDC_SPK_DSM_R4 (WSA884X_DIG_CTRL0_BASE + 0x84) +#define WSA884X_CDC_SPK_DSM_R5 (WSA884X_DIG_CTRL0_BASE + 0x85) +#define WSA884X_CDC_SPK_DSM_R6 (WSA884X_DIG_CTRL0_BASE + 0x86) +#define WSA884X_CDC_SPK_DSM_R7 (WSA884X_DIG_CTRL0_BASE + 0x87) +#define WSA884X_CDC_SPK_GAIN_PDM_0 (WSA884X_DIG_CTRL0_BASE + 0x88) +#define WSA884X_CDC_SPK_GAIN_PDM_1 (WSA884X_DIG_CTRL0_BASE + 0x89) +#define WSA884X_CDC_SPK_GAIN_PDM_2 (WSA884X_DIG_CTRL0_BASE + 0x8a) +#define WSA884X_PDM_WD_CTL (WSA884X_DIG_CTRL0_BASE + 0x8b) +#define WSA884X_PDM_WD_CTL_HOLD_OFF_MASK 0x04 +#define WSA884X_PDM_WD_CTL_HOLD_OFF_SHIFT 2 +#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_MASK 0x02 +#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_SHIFT 1 +#define WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK 0x01 +#define WSA884X_PDM_WD_CTL_PDM_WD_EN_SHIFT 0 +#define WSA884X_DEM_BYPASS_DATA0 (WSA884X_DIG_CTRL0_BASE + 0x90) +#define WSA884X_DEM_BYPASS_DATA1 (WSA884X_DIG_CTRL0_BASE + 0x91) +#define WSA884X_DEM_BYPASS_DATA2 (WSA884X_DIG_CTRL0_BASE + 0x92) +#define WSA884X_DEM_BYPASS_DATA3 (WSA884X_DIG_CTRL0_BASE + 0x93) +#define WSA884X_DRE_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xb0) +#define WSA884X_DRE_CTL_0_PROG_DELAY_MASK 0xf0 +#define WSA884X_DRE_CTL_0_PROG_DELAY_SHIFT 4 +#define WSA884X_DRE_CTL_0_OFFSET_MASK 0x07 +#define WSA884X_DRE_CTL_0_OFFSET_SHIFT 0 +#define WSA884X_DRE_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xb1) +#define WSA884X_DRE_CTL_1_CSR_GAIN_MASK 0x3e +#define WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT 1 +#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK 0x01 +#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_SHIFT 0 +#define WSA884X_DRE_IDLE_DET_CTL (WSA884X_DIG_CTRL0_BASE + 0xb2) +#define WSA884X_GAIN_RAMPING_CTL (WSA884X_DIG_CTRL0_BASE + 0xb8) +#define WSA884X_GAIN_RAMPING_MIN (WSA884X_DIG_CTRL0_BASE + 0xb9) +#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK 0x1f +#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_SHIFT 0 +#define WSA884X_TAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc0) +#define WSA884X_TAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc1) +#define WSA884X_TAGC_FORCE_VAL (WSA884X_DIG_CTRL0_BASE + 0xc2) +#define WSA884X_VAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc8) +#define WSA884X_VAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc9) +#define WSA884X_VAGC_ATTN_LVL_1 (WSA884X_DIG_CTRL0_BASE + 0xca) +#define WSA884X_VAGC_ATTN_LVL_2 (WSA884X_DIG_CTRL0_BASE + 0xcb) +#define WSA884X_VAGC_ATTN_LVL_3 (WSA884X_DIG_CTRL0_BASE + 0xcc) +#define WSA884X_CLSH_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xd0) +#define WSA884X_CLSH_CTL_0_CSR_GAIN_EN_SHIFT 7 +#define WSA884X_CLSH_CTL_0_DLY_CODE_MASK 0x70 +#define WSA884X_CLSH_CTL_0_DLY_CODE_SHIFT 4 +#define WSA884X_CLSH_CTL_0_DLY_RST_SHIFT 3 +#define WSA884X_CLSH_CTL_0_DLY_EN_SHIFT 2 +#define WSA884X_CLSH_CTL_0_INPUT_EN_SHIFT 1 +#define WSA884X_CLSH_CTL_0_CLSH_EN_SHIFT 0 +#define WSA884X_CLSH_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xd1) +#define WSA884X_CLSH_V_HD_PA (WSA884X_DIG_CTRL0_BASE + 0xd2) +#define WSA884X_CLSH_V_PA_MIN (WSA884X_DIG_CTRL0_BASE + 0xd3) +#define WSA884X_CLSH_OVRD_VAL (WSA884X_DIG_CTRL0_BASE + 0xd4) +#define WSA884X_CLSH_HARD_MAX (WSA884X_DIG_CTRL0_BASE + 0xd5) +#define WSA884X_CLSH_SOFT_MAX (WSA884X_DIG_CTRL0_BASE + 0xd6) +#define WSA884X_CLSH_SIG_DP (WSA884X_DIG_CTRL0_BASE + 0xd7) +#define WSA884X_PBR_DELAY_CTL (WSA884X_DIG_CTRL0_BASE + 0xd8) +#define WSA884X_CLSH_SRL_MAX_PBR (WSA884X_DIG_CTRL0_BASE + 0xe0) +#define WSA884X_PBR_MAX_VOLTAGE 20 +#define WSA884X_PBR_MAX_CODE 255 +#define WSA884X_VTH_TO_REG(vth) \ + ((vth) != 0 ? (((vth) - 150) * WSA884X_PBR_MAX_CODE / (WSA884X_PBR_MAX_VOLTAGE * 100) + 1) : 0) +#define WSA884X_CLSH_VTH1 (WSA884X_DIG_CTRL0_BASE + 0xe1) +#define WSA884X_CLSH_VTH2 (WSA884X_DIG_CTRL0_BASE + 0xe2) +#define WSA884X_CLSH_VTH3 (WSA884X_DIG_CTRL0_BASE + 0xe3) +#define WSA884X_CLSH_VTH4 (WSA884X_DIG_CTRL0_BASE + 0xe4) +#define WSA884X_CLSH_VTH5 (WSA884X_DIG_CTRL0_BASE + 0xe5) +#define WSA884X_CLSH_VTH6 (WSA884X_DIG_CTRL0_BASE + 0xe6) +#define WSA884X_CLSH_VTH7 (WSA884X_DIG_CTRL0_BASE + 0xe7) +#define WSA884X_CLSH_VTH8 (WSA884X_DIG_CTRL0_BASE + 0xe8) +#define WSA884X_CLSH_VTH9 (WSA884X_DIG_CTRL0_BASE + 0xe9) +#define WSA884X_CLSH_VTH10 (WSA884X_DIG_CTRL0_BASE + 0xea) +#define WSA884X_CLSH_VTH11 (WSA884X_DIG_CTRL0_BASE + 0xeb) +#define WSA884X_CLSH_VTH12 (WSA884X_DIG_CTRL0_BASE + 0xec) +#define WSA884X_CLSH_VTH13 (WSA884X_DIG_CTRL0_BASE + 0xed) +#define WSA884X_CLSH_VTH14 (WSA884X_DIG_CTRL0_BASE + 0xee) +#define WSA884X_CLSH_VTH15 (WSA884X_DIG_CTRL0_BASE + 0xef) + +#define WSA884X_DIG_CTRL1_BASE (WSA884X_BASE + 0x0500) +#define WSA884X_DIG_CTRL1_PAGE (WSA884X_DIG_CTRL1_BASE + 0x00) +#define WSA884X_VPHX_SYS_EN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x01) +#define WSA884X_ANA_WO_CTL_0 (WSA884X_DIG_CTRL1_BASE + 0x04) +#define WSA884X_ANA_WO_CTL_0_MODE_SHIFT 0 +#define WSA884X_ANA_WO_CTL_0_VPHX_SYS_EN_MASK 0xc0 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_DISABLE 0x0 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB 0xa +#define WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB 0x7 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK 0x3c +#define WSA884X_ANA_WO_CTL_0_PA_MIN_GAIN_BYP_MASK 0x02 +#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER 0x1 +#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK 0x01 +#define WSA884X_ANA_WO_CTL_1 (WSA884X_DIG_CTRL1_BASE + 0x05) +#define WSA884X_PIN_CTL (WSA884X_DIG_CTRL1_BASE + 0x10) +#define WSA884X_PIN_CTL_OE (WSA884X_DIG_CTRL1_BASE + 0x11) +#define WSA884X_PIN_WDATA_IOPAD (WSA884X_DIG_CTRL1_BASE + 0x12) +#define WSA884X_PIN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x13) +#define WSA884X_I2C_SLAVE_CTL (WSA884X_DIG_CTRL1_BASE + 0x14) +#define WSA884X_SPMI_PAD_CTL0 (WSA884X_DIG_CTRL1_BASE + 0x15) +#define WSA884X_SPMI_PAD_CTL1 (WSA884X_DIG_CTRL1_BASE + 0x16) +#define WSA884X_SPMI_PAD_CTL2 (WSA884X_DIG_CTRL1_BASE + 0x17) +#define WSA884X_MEM_CTL (WSA884X_DIG_CTRL1_BASE + 0x18) +#define WSA884X_SWR_HM_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x19) +#define WSA884X_SWR_HM_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x1a) +#define WSA884X_OTP_CTRL0 (WSA884X_DIG_CTRL1_BASE + 0x30) +#define WSA884X_OTP_CTRL1 (WSA884X_DIG_CTRL1_BASE + 0x31) +#define WSA884X_OTP_CTRL2 (WSA884X_DIG_CTRL1_BASE + 0x32) +#define WSA884X_OTP_STAT (WSA884X_DIG_CTRL1_BASE + 0x33) +#define WSA884X_OTP_PRG_TCSP0 (WSA884X_DIG_CTRL1_BASE + 0x34) +#define WSA884X_OTP_PRG_TCSP1 (WSA884X_DIG_CTRL1_BASE + 0x35) +#define WSA884X_OTP_PRG_TPPS (WSA884X_DIG_CTRL1_BASE + 0x36) +#define WSA884X_OTP_PRG_TVPS (WSA884X_DIG_CTRL1_BASE + 0x37) +#define WSA884X_OTP_PRG_TVPH (WSA884X_DIG_CTRL1_BASE + 0x38) +#define WSA884X_OTP_PRG_TPPR0 (WSA884X_DIG_CTRL1_BASE + 0x39) +#define WSA884X_OTP_PRG_TPPR1 (WSA884X_DIG_CTRL1_BASE + 0x3a) +#define WSA884X_OTP_PRG_TPPH (WSA884X_DIG_CTRL1_BASE + 0x3b) +#define WSA884X_OTP_PRG_END (WSA884X_DIG_CTRL1_BASE + 0x3c) +#define WSA884X_WAVG_PLAY (WSA884X_DIG_CTRL1_BASE + 0x40) +#define WSA884X_WAVG_CTL (WSA884X_DIG_CTRL1_BASE + 0x41) +#define WSA884X_WAVG_LRA_PER_0 (WSA884X_DIG_CTRL1_BASE + 0x43) +#define WSA884X_WAVG_LRA_PER_1 (WSA884X_DIG_CTRL1_BASE + 0x44) +#define WSA884X_WAVG_DELTA_THETA_0 (WSA884X_DIG_CTRL1_BASE + 0x45) +#define WSA884X_WAVG_DELTA_THETA_1 (WSA884X_DIG_CTRL1_BASE + 0x46) +#define WSA884X_WAVG_DIRECT_AMP_0 (WSA884X_DIG_CTRL1_BASE + 0x47) +#define WSA884X_WAVG_DIRECT_AMP_1 (WSA884X_DIG_CTRL1_BASE + 0x48) +#define WSA884X_WAVG_PTRN_AMP0_0 (WSA884X_DIG_CTRL1_BASE + 0x49) +#define WSA884X_WAVG_PTRN_AMP0_1 (WSA884X_DIG_CTRL1_BASE + 0x4a) +#define WSA884X_WAVG_PTRN_AMP1_0 (WSA884X_DIG_CTRL1_BASE + 0x4b) +#define WSA884X_WAVG_PTRN_AMP1_1 (WSA884X_DIG_CTRL1_BASE + 0x4c) +#define WSA884X_WAVG_PTRN_AMP2_0 (WSA884X_DIG_CTRL1_BASE + 0x4d) +#define WSA884X_WAVG_PTRN_AMP2_1 (WSA884X_DIG_CTRL1_BASE + 0x4e) +#define WSA884X_WAVG_PTRN_AMP3_0 (WSA884X_DIG_CTRL1_BASE + 0x4f) +#define WSA884X_WAVG_PTRN_AMP3_1 (WSA884X_DIG_CTRL1_BASE + 0x50) +#define WSA884X_WAVG_PTRN_AMP4_0 (WSA884X_DIG_CTRL1_BASE + 0x51) +#define WSA884X_WAVG_PTRN_AMP4_1 (WSA884X_DIG_CTRL1_BASE + 0x52) +#define WSA884X_WAVG_PTRN_AMP5_0 (WSA884X_DIG_CTRL1_BASE + 0x53) +#define WSA884X_WAVG_PTRN_AMP5_1 (WSA884X_DIG_CTRL1_BASE + 0x54) +#define WSA884X_WAVG_PTRN_AMP6_0 (WSA884X_DIG_CTRL1_BASE + 0x55) +#define WSA884X_WAVG_PTRN_AMP6_1 (WSA884X_DIG_CTRL1_BASE + 0x56) +#define WSA884X_WAVG_PTRN_AMP7_0 (WSA884X_DIG_CTRL1_BASE + 0x57) +#define WSA884X_WAVG_PTRN_AMP7_1 (WSA884X_DIG_CTRL1_BASE + 0x58) +#define WSA884X_WAVG_PER_0_1 (WSA884X_DIG_CTRL1_BASE + 0x59) +#define WSA884X_WAVG_PER_2_3 (WSA884X_DIG_CTRL1_BASE + 0x5a) +#define WSA884X_WAVG_PER_4_5 (WSA884X_DIG_CTRL1_BASE + 0x5b) +#define WSA884X_WAVG_PER_6_7 (WSA884X_DIG_CTRL1_BASE + 0x5c) +#define WSA884X_WAVG_STA (WSA884X_DIG_CTRL1_BASE + 0x5d) +#define WSA884X_INTR_MODE (WSA884X_DIG_CTRL1_BASE + 0x80) +#define WSA884X_INTR_MASK0 (WSA884X_DIG_CTRL1_BASE + 0x81) +#define WSA884X_INTR_MASK1 (WSA884X_DIG_CTRL1_BASE + 0x82) +#define WSA884X_INTR_STATUS0 (WSA884X_DIG_CTRL1_BASE + 0x83) +#define WSA884X_INTR_STATUS1 (WSA884X_DIG_CTRL1_BASE + 0x84) +#define WSA884X_INTR_CLEAR0 (WSA884X_DIG_CTRL1_BASE + 0x85) +#define WSA884X_INTR_CLEAR1 (WSA884X_DIG_CTRL1_BASE + 0x86) +#define WSA884X_INTR_LEVEL0 (WSA884X_DIG_CTRL1_BASE + 0x87) +#define WSA884X_INTR_LEVEL1 (WSA884X_DIG_CTRL1_BASE + 0x88) +#define WSA884X_INTR_SET0 (WSA884X_DIG_CTRL1_BASE + 0x89) +#define WSA884X_INTR_SET1 (WSA884X_DIG_CTRL1_BASE + 0x8a) +#define WSA884X_INTR_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x8b) +#define WSA884X_INTR_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x8c) +#define WSA884X_PDM_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc0) +#define WSA884X_ATE_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc1) +#define WSA884X_PA_FSM_DBG (WSA884X_DIG_CTRL1_BASE + 0xc2) +#define WSA884X_DIG_DEBUG_MODE (WSA884X_DIG_CTRL1_BASE + 0xc3) +#define WSA884X_DIG_DEBUG_SEL (WSA884X_DIG_CTRL1_BASE + 0xc4) +#define WSA884X_DIG_DEBUG_EN (WSA884X_DIG_CTRL1_BASE + 0xc5) +#define WSA884X_TADC_DETECT_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xc9) +#define WSA884X_TADC_DEBUG_MSB (WSA884X_DIG_CTRL1_BASE + 0xca) +#define WSA884X_TADC_DEBUG_LSB (WSA884X_DIG_CTRL1_BASE + 0xcb) +#define WSA884X_SAMPLE_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcc) +#define WSA884X_SWR_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcd) +#define WSA884X_TEST_MODE_CTL (WSA884X_DIG_CTRL1_BASE + 0xce) +#define WSA884X_IOPAD_CTL (WSA884X_DIG_CTRL1_BASE + 0xcf) +#define WSA884X_ANA_CSR_DBG_ADD (WSA884X_DIG_CTRL1_BASE + 0xd0) +#define WSA884X_ANA_CSR_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd1) +#define WSA884X_CLK_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd2) +#define WSA884X_SPARE_R (WSA884X_DIG_CTRL1_BASE + 0xf0) +#define WSA884X_SPARE_0 (WSA884X_DIG_CTRL1_BASE + 0xf1) +#define WSA884X_SPARE_1 (WSA884X_DIG_CTRL1_BASE + 0xf2) +#define WSA884X_SPARE_2 (WSA884X_DIG_CTRL1_BASE + 0xf3) +#define WSA884X_SCODE (WSA884X_DIG_CTRL1_BASE + 0xff) + +#define WSA884X_DIG_TRIM_BASE (WSA884X_BASE + 0x0800) +#define WSA884X_DIG_TRIM_PAGE (WSA884X_DIG_TRIM_BASE + 0x00) +#define WSA884X_OTP_REG_0 (WSA884X_DIG_TRIM_BASE + 0x80) +#define WSA884X_OTP_ID_WSA8840 0x0 +#define WSA884X_OTP_ID_WSA8845 0x5 +#define WSA884X_OTP_ID_WSA8845H 0xc +#define WSA884X_OTP_REG_0_ID_MASK 0x0f +#define WSA884X_OTP_REG_1 (WSA884X_DIG_TRIM_BASE + 0x81) +#define WSA884X_OTP_REG_2 (WSA884X_DIG_TRIM_BASE + 0x82) +#define WSA884X_OTP_REG_3 (WSA884X_DIG_TRIM_BASE + 0x83) +#define WSA884X_OTP_REG_4 (WSA884X_DIG_TRIM_BASE + 0x84) +#define WSA884X_OTP_REG_5 (WSA884X_DIG_TRIM_BASE + 0x85) +#define WSA884X_OTP_REG_6 (WSA884X_DIG_TRIM_BASE + 0x86) +#define WSA884X_OTP_REG_7 (WSA884X_DIG_TRIM_BASE + 0x87) +#define WSA884X_OTP_REG_8 (WSA884X_DIG_TRIM_BASE + 0x88) +#define WSA884X_OTP_REG_9 (WSA884X_DIG_TRIM_BASE + 0x89) +#define WSA884X_OTP_REG_10 (WSA884X_DIG_TRIM_BASE + 0x8a) +#define WSA884X_OTP_REG_11 (WSA884X_DIG_TRIM_BASE + 0x8b) +#define WSA884X_OTP_REG_12 (WSA884X_DIG_TRIM_BASE + 0x8c) +#define WSA884X_OTP_REG_13 (WSA884X_DIG_TRIM_BASE + 0x8d) +#define WSA884X_OTP_REG_14 (WSA884X_DIG_TRIM_BASE + 0x8e) +#define WSA884X_OTP_REG_15 (WSA884X_DIG_TRIM_BASE + 0x8f) +#define WSA884X_OTP_REG_16 (WSA884X_DIG_TRIM_BASE + 0x90) +#define WSA884X_OTP_REG_17 (WSA884X_DIG_TRIM_BASE + 0x91) +#define WSA884X_OTP_REG_18 (WSA884X_DIG_TRIM_BASE + 0x92) +#define WSA884X_OTP_REG_19 (WSA884X_DIG_TRIM_BASE + 0x93) +#define WSA884X_OTP_REG_20 (WSA884X_DIG_TRIM_BASE + 0x94) +#define WSA884X_OTP_REG_21 (WSA884X_DIG_TRIM_BASE + 0x95) +#define WSA884X_OTP_REG_22 (WSA884X_DIG_TRIM_BASE + 0x96) +#define WSA884X_OTP_REG_23 (WSA884X_DIG_TRIM_BASE + 0x97) +#define WSA884X_OTP_REG_24 (WSA884X_DIG_TRIM_BASE + 0x98) +#define WSA884X_OTP_REG_25 (WSA884X_DIG_TRIM_BASE + 0x99) +#define WSA884X_OTP_REG_26 (WSA884X_DIG_TRIM_BASE + 0x9a) +#define WSA884X_OTP_REG_27 (WSA884X_DIG_TRIM_BASE + 0x9b) +#define WSA884X_OTP_REG_28 (WSA884X_DIG_TRIM_BASE + 0x9c) +#define WSA884X_OTP_REG_29 (WSA884X_DIG_TRIM_BASE + 0x9d) +#define WSA884X_OTP_REG_30 (WSA884X_DIG_TRIM_BASE + 0x9e) +#define WSA884X_OTP_REG_31 (WSA884X_DIG_TRIM_BASE + 0x9f) +#define WSA884X_OTP_REG_32 (WSA884X_DIG_TRIM_BASE + 0xa0) +#define WSA884X_OTP_REG_33 (WSA884X_DIG_TRIM_BASE + 0xa1) +#define WSA884X_OTP_REG_34 (WSA884X_DIG_TRIM_BASE + 0xa2) +#define WSA884X_OTP_REG_35 (WSA884X_DIG_TRIM_BASE + 0xa3) +#define WSA884X_OTP_REG_36 (WSA884X_DIG_TRIM_BASE + 0xa4) +#define WSA884X_OTP_REG_37 (WSA884X_DIG_TRIM_BASE + 0xa5) +#define WSA884X_OTP_REG_38 (WSA884X_DIG_TRIM_BASE + 0xa6) +#define WSA884X_OTP_REG_38_RESERVER_MASK 0xf0 +#define WSA884X_OTP_REG_38_RESERVER_SHIFT 4 +#define WSA884X_OTP_REG_38_BST_CFG_SEL_MASK 0x08 +#define WSA884X_OTP_REG_38_BST_CFG_SEL_SHIFT 3 +#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_MASK 0x07 +#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_SHIFT 0 +#define WSA884X_OTP_REG_39 (WSA884X_DIG_TRIM_BASE + 0xa7) +#define WSA884X_OTP_REG_40 (WSA884X_DIG_TRIM_BASE + 0xa8) +#define WSA884X_OTP_REG_40_SPARE_TYPE2_MASK 0xc0 +#define WSA884X_OTP_REG_40_SPARE_TYPE2_SHIFT 6 +#define WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK 0x3c +#define WSA884X_OTP_REG_40_ISENSE_RESCAL_SHIFT 2 +#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_MASK 0x2 +#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_SHIFT 1 +#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_MASK 0x1 +#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_SHIFT 0 +#define WSA884X_OTP_REG_41 (WSA884X_DIG_TRIM_BASE + 0xa9) +#define WSA884X_OTP_REG_63 (WSA884X_DIG_TRIM_BASE + 0xbf) + +#define WSA884X_DIG_EMEM_BASE (WSA884X_BASE + 0x08C0) +#define WSA884X_EMEM_0 (WSA884X_DIG_EMEM_BASE + 0x00) +#define WSA884X_EMEM_1 (WSA884X_DIG_EMEM_BASE + 0x01) +#define WSA884X_EMEM_2 (WSA884X_DIG_EMEM_BASE + 0x02) +#define WSA884X_EMEM_3 (WSA884X_DIG_EMEM_BASE + 0x03) +#define WSA884X_EMEM_4 (WSA884X_DIG_EMEM_BASE + 0x04) +#define WSA884X_EMEM_5 (WSA884X_DIG_EMEM_BASE + 0x05) +#define WSA884X_EMEM_6 (WSA884X_DIG_EMEM_BASE + 0x06) +#define WSA884X_EMEM_7 (WSA884X_DIG_EMEM_BASE + 0x07) +#define WSA884X_EMEM_8 (WSA884X_DIG_EMEM_BASE + 0x08) +#define WSA884X_EMEM_9 (WSA884X_DIG_EMEM_BASE + 0x09) +#define WSA884X_EMEM_10 (WSA884X_DIG_EMEM_BASE + 0x0a) +#define WSA884X_EMEM_11 (WSA884X_DIG_EMEM_BASE + 0x0b) +#define WSA884X_EMEM_12 (WSA884X_DIG_EMEM_BASE + 0x0c) +#define WSA884X_EMEM_13 (WSA884X_DIG_EMEM_BASE + 0x0d) +#define WSA884X_EMEM_14 (WSA884X_DIG_EMEM_BASE + 0x0e) +#define WSA884X_EMEM_15 (WSA884X_DIG_EMEM_BASE + 0x0f) +#define WSA884X_EMEM_16 (WSA884X_DIG_EMEM_BASE + 0x10) +#define WSA884X_EMEM_17 (WSA884X_DIG_EMEM_BASE + 0x11) +#define WSA884X_EMEM_18 (WSA884X_DIG_EMEM_BASE + 0x12) +#define WSA884X_EMEM_19 (WSA884X_DIG_EMEM_BASE + 0x13) +#define WSA884X_EMEM_20 (WSA884X_DIG_EMEM_BASE + 0x14) +#define WSA884X_EMEM_21 (WSA884X_DIG_EMEM_BASE + 0x15) +#define WSA884X_EMEM_22 (WSA884X_DIG_EMEM_BASE + 0x16) +#define WSA884X_EMEM_23 (WSA884X_DIG_EMEM_BASE + 0x17) +#define WSA884X_EMEM_24 (WSA884X_DIG_EMEM_BASE + 0x18) +#define WSA884X_EMEM_25 (WSA884X_DIG_EMEM_BASE + 0x19) +#define WSA884X_EMEM_26 (WSA884X_DIG_EMEM_BASE + 0x1a) +#define WSA884X_EMEM_27 (WSA884X_DIG_EMEM_BASE + 0x1b) +#define WSA884X_EMEM_28 (WSA884X_DIG_EMEM_BASE + 0x1c) +#define WSA884X_EMEM_29 (WSA884X_DIG_EMEM_BASE + 0x1d) +#define WSA884X_EMEM_30 (WSA884X_DIG_EMEM_BASE + 0x1e) +#define WSA884X_EMEM_31 (WSA884X_DIG_EMEM_BASE + 0x1f) +#define WSA884X_EMEM_32 (WSA884X_DIG_EMEM_BASE + 0x20) +#define WSA884X_EMEM_33 (WSA884X_DIG_EMEM_BASE + 0x21) +#define WSA884X_EMEM_34 (WSA884X_DIG_EMEM_BASE + 0x22) +#define WSA884X_EMEM_35 (WSA884X_DIG_EMEM_BASE + 0x23) +#define WSA884X_EMEM_36 (WSA884X_DIG_EMEM_BASE + 0x24) +#define WSA884X_EMEM_37 (WSA884X_DIG_EMEM_BASE + 0x25) +#define WSA884X_EMEM_38 (WSA884X_DIG_EMEM_BASE + 0x26) +#define WSA884X_EMEM_39 (WSA884X_DIG_EMEM_BASE + 0x27) +#define WSA884X_EMEM_40 (WSA884X_DIG_EMEM_BASE + 0x28) +#define WSA884X_EMEM_41 (WSA884X_DIG_EMEM_BASE + 0x29) +#define WSA884X_EMEM_42 (WSA884X_DIG_EMEM_BASE + 0x2a) +#define WSA884X_EMEM_43 (WSA884X_DIG_EMEM_BASE + 0x2b) +#define WSA884X_EMEM_44 (WSA884X_DIG_EMEM_BASE + 0x2c) +#define WSA884X_EMEM_45 (WSA884X_DIG_EMEM_BASE + 0x2d) +#define WSA884X_EMEM_46 (WSA884X_DIG_EMEM_BASE + 0x2e) +#define WSA884X_EMEM_47 (WSA884X_DIG_EMEM_BASE + 0x2f) +#define WSA884X_EMEM_48 (WSA884X_DIG_EMEM_BASE + 0x30) +#define WSA884X_EMEM_49 (WSA884X_DIG_EMEM_BASE + 0x31) +#define WSA884X_EMEM_50 (WSA884X_DIG_EMEM_BASE + 0x32) +#define WSA884X_EMEM_51 (WSA884X_DIG_EMEM_BASE + 0x33) +#define WSA884X_EMEM_52 (WSA884X_DIG_EMEM_BASE + 0x34) +#define WSA884X_EMEM_53 (WSA884X_DIG_EMEM_BASE + 0x35) +#define WSA884X_EMEM_54 (WSA884X_DIG_EMEM_BASE + 0x36) +#define WSA884X_EMEM_55 (WSA884X_DIG_EMEM_BASE + 0x37) +#define WSA884X_EMEM_56 (WSA884X_DIG_EMEM_BASE + 0x38) +#define WSA884X_EMEM_57 (WSA884X_DIG_EMEM_BASE + 0x39) +#define WSA884X_EMEM_58 (WSA884X_DIG_EMEM_BASE + 0x3a) +#define WSA884X_EMEM_59 (WSA884X_DIG_EMEM_BASE + 0x3b) +#define WSA884X_EMEM_60 (WSA884X_DIG_EMEM_BASE + 0x3c) +#define WSA884X_EMEM_61 (WSA884X_DIG_EMEM_BASE + 0x3d) +#define WSA884X_EMEM_62 (WSA884X_DIG_EMEM_BASE + 0x3e) +#define WSA884X_EMEM_63 (WSA884X_DIG_EMEM_BASE + 0x3f) + +#define WSA884X_NUM_REGISTERS (WSA884X_EMEM_63 + 1) +#define WSA884X_MAX_REGISTER (WSA884X_NUM_REGISTERS - 1) + +#define WSA884X_SUPPLIES_NUM 2 +#define WSA884X_MAX_SWR_PORTS 6 +#define WSA884X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WSA884X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) + +#define WSA884X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct wsa884x_priv { + struct regmap *regmap; + struct device *dev; + struct regulator_bulk_data supplies[WSA884X_SUPPLIES_NUM]; + struct sdw_slave *slave; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS]; + struct gpio_desc *sd_n; + bool port_prepared[WSA884X_MAX_SWR_PORTS]; + bool port_enable[WSA884X_MAX_SWR_PORTS]; + unsigned int variant; + int active_ports; + int dev_mode; + bool hw_init; +}; + +enum { + COMP_OFFSET0, + COMP_OFFSET1, + COMP_OFFSET2, + COMP_OFFSET3, + COMP_OFFSET4, +}; + +enum wsa884x_gain { + G_21_DB = 0, + G_19P5_DB, + G_18_DB, + G_16P5_DB, + G_15_DB, + G_13P5_DB, + G_12_DB, + G_10P5_DB, + G_9_DB, + G_7P5_DB, + G_6_DB, + G_4P5_DB, + G_3_DB, + G_1P5_DB, + G_0_DB, + G_M1P5_DB, + G_M3_DB, + G_M4P5_DB, + G_M6_DB, + G_MAX_DB, +}; + +enum wsa884x_isense { + ISENSE_6_DB = 0, + ISENSE_12_DB, + ISENSE_15_DB, + ISENSE_18_DB, +}; + +enum wsa884x_vsense { + VSENSE_M12_DB = 0, + VSENSE_M15_DB, + VSENSE_M18_DB, + VSENSE_M21_DB, + VSENSE_M24_DB, +}; + +enum wsa884x_port_ids { + WSA884X_PORT_DAC, + WSA884X_PORT_COMP, + WSA884X_PORT_BOOST, + WSA884X_PORT_PBR, + WSA884X_PORT_VISENSE, + WSA884X_PORT_CPS, +}; + +static const char * const wsa884x_supply_name[] = { + "vdd-io", + "vdd-1p8", +}; + +static const char * const wsa884x_dev_mode_text[] = { + "Speaker", "Receiver" +}; + +enum wsa884x_mode { + WSA884X_SPEAKER, + WSA884X_RECEIVER, +}; + +static const struct soc_enum wsa884x_dev_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa884x_dev_mode_text), wsa884x_dev_mode_text); + +static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = { + { + .num = WSA884X_PORT_DAC + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_COMP + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_BOOST + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_PBR + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_VISENSE + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_CPS + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + } +}; + +static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = { + { + .num = WSA884X_PORT_DAC + 1, + .ch_mask = 0x1, + }, { + .num = WSA884X_PORT_COMP + 1, + .ch_mask = 0xf, + }, { + .num = WSA884X_PORT_BOOST + 1, + .ch_mask = 0x3, + }, { + .num = WSA884X_PORT_PBR + 1, + .ch_mask = 0x1, + }, { + .num = WSA884X_PORT_VISENSE + 1, + .ch_mask = 0x3, + }, { + .num = WSA884X_PORT_CPS + 1, + .ch_mask = 0x3, + }, +}; + +static struct reg_default wsa884x_defaults[] = { + { WSA884X_BG_CTRL, 0xa5 }, + { WSA884X_ADC_CTRL, 0x00 }, + { WSA884X_BOP1_PROG, 0x22 }, + { WSA884X_BOP2_PROG, 0x44 }, + { WSA884X_UVLO_PROG, 0x99 }, + { WSA884X_UVLO_PROG1, 0x70 }, + { WSA884X_SPARE_CTRL_0, 0x00 }, + { WSA884X_SPARE_CTRL_1, 0x00 }, + { WSA884X_SPARE_CTRL_2, 0x00 }, + { WSA884X_SPARE_CTRL_3, 0x00 }, + { WSA884X_REF_CTRL, 0xd2 }, + { WSA884X_BG_TEST_CTL, 0x06 }, + { WSA884X_BG_BIAS, 0xd7 }, + { WSA884X_ADC_PROG, 0x08 }, + { WSA884X_ADC_IREF_CTL, 0x57 }, + { WSA884X_ADC_ISENS_CTL, 0x47 }, + { WSA884X_ADC_CLK_CTL, 0x87 }, + { WSA884X_ADC_TEST_CTL, 0x00 }, + { WSA884X_ADC_BIAS, 0x51 }, + { WSA884X_VBAT_SNS, 0xa0 }, + { WSA884X_BOP_ATEST_SEL, 0x00 }, + { WSA884X_MISC0, 0x04 }, + { WSA884X_MISC1, 0x75 }, + { WSA884X_MISC2, 0x00 }, + { WSA884X_MISC3, 0x10 }, + { WSA884X_SPARE_TSBG_0, 0x00 }, + { WSA884X_SPARE_TUNE_0, 0x00 }, + { WSA884X_SPARE_TUNE_1, 0x00 }, + { WSA884X_VSENSE1, 0xe7 }, + { WSA884X_ISENSE2, 0x27 }, + { WSA884X_SPARE_CTL_1, 0x00 }, + { WSA884X_SPARE_CTL_2, 0x00 }, + { WSA884X_SPARE_CTL_3, 0x00 }, + { WSA884X_SPARE_CTL_4, 0x00 }, + { WSA884X_EN, 0x10 }, + { WSA884X_OVERRIDE1, 0x00 }, + { WSA884X_OVERRIDE2, 0x08 }, + { WSA884X_ISENSE1, 0xd4 }, + { WSA884X_ISENSE_CAL, 0x00 }, + { WSA884X_MISC, 0x00 }, + { WSA884X_ADC_0, 0x00 }, + { WSA884X_ADC_1, 0x00 }, + { WSA884X_ADC_2, 0x40 }, + { WSA884X_ADC_3, 0x80 }, + { WSA884X_ADC_4, 0x25 }, + { WSA884X_ADC_5, 0x24 }, + { WSA884X_ADC_6, 0x0a }, + { WSA884X_ADC_7, 0x81 }, + { WSA884X_IVSENSE_SPARE_TUNE_1, 0x00 }, + { WSA884X_SPARE_TUNE_2, 0x00 }, + { WSA884X_SPARE_TUNE_3, 0x00 }, + { WSA884X_SPARE_TUNE_4, 0x00 }, + { WSA884X_TOP_CTRL1, 0xd3 }, + { WSA884X_CLIP_DET_CTRL1, 0x7e }, + { WSA884X_CLIP_DET_CTRL2, 0x4c }, + { WSA884X_DAC_CTRL1, 0xa4 }, + { WSA884X_DAC_VCM_CTRL_REG1, 0x02 }, + { WSA884X_DAC_VCM_CTRL_REG2, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG3, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG4, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG5, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG6, 0x00 }, + { WSA884X_PWM_CLK_CTL, 0x20 }, + { WSA884X_DRV_LF_LDO_SEL, 0xaa }, + { WSA884X_OCP_CTL, 0xc6 }, + { WSA884X_PDRV_HS_CTL, 0x52 }, + { WSA884X_PDRV_LS_CTL, 0x4a }, + { WSA884X_SPK_TOP_SPARE_CTL_1, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_2, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_3, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_4, 0x00 }, + { WSA884X_SPARE_CTL_5, 0x00 }, + { WSA884X_DAC_EN_DEBUG_REG, 0x00 }, + { WSA884X_DAC_OPAMP_BIAS1_REG, 0x48 }, + { WSA884X_DAC_OPAMP_BIAS2_REG, 0x48 }, + { WSA884X_DAC_TUNE1, 0x02 }, + { WSA884X_DAC_VOLTAGE_CTRL_REG, 0x05 }, + { WSA884X_ATEST1_REG, 0x00 }, + { WSA884X_ATEST2_REG, 0x00 }, + { WSA884X_TOP_BIAS_REG1, 0x6a }, + { WSA884X_TOP_BIAS_REG2, 0x65 }, + { WSA884X_TOP_BIAS_REG3, 0x55 }, + { WSA884X_TOP_BIAS_REG4, 0xa9 }, + { WSA884X_PWRSTG_DBG2, 0x21 }, + { WSA884X_DRV_LF_BLK_EN, 0x0f }, + { WSA884X_DRV_LF_EN, 0x0a }, + { WSA884X_DRV_LF_MASK_DCC_CTL, 0x08 }, + { WSA884X_DRV_LF_MISC_CTL1, 0x30 }, + { WSA884X_DRV_LF_REG_GAIN, 0x00 }, + { WSA884X_DRV_OS_CAL_CTL, 0x00 }, + { WSA884X_DRV_OS_CAL_CTL1, 0x90 }, + { WSA884X_PWRSTG_DBG, 0x08 }, + { WSA884X_BBM_CTL, 0x92 }, + { WSA884X_TOP_MISC1, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG7, 0x00 }, + { WSA884X_TOP_BIAS_REG5, 0x15 }, + { WSA884X_DRV_LF_MISC_CTL2, 0x00 }, + { WSA884X_STB_CTRL1, 0x42 }, + { WSA884X_CURRENT_LIMIT, 0x54 }, + { WSA884X_BYP_CTRL1, 0x01 }, + { WSA884X_SPARE_CTL_0, 0x00 }, + { WSA884X_BOOST_SPARE_CTL_1, 0x00 }, + { WSA884X_IBIAS1, 0x00 }, + { WSA884X_IBIAS2, 0x00 }, + { WSA884X_IBIAS3, 0x00 }, + { WSA884X_EN_CTRL, 0x42 }, + { WSA884X_STB_CTRL2, 0x03 }, + { WSA884X_STB_CTRL3, 0x3c }, + { WSA884X_STB_CTRL4, 0x30 }, + { WSA884X_BYP_CTRL2, 0x97 }, + { WSA884X_BYP_CTRL3, 0x11 }, + { WSA884X_ZX_CTRL1, 0xf0 }, + { WSA884X_ZX_CTRL2, 0x04 }, + { WSA884X_BLEEDER_CTRL, 0x04 }, + { WSA884X_BOOST_MISC, 0x62 }, + { WSA884X_PWRSTAGE_CTRL1, 0x00 }, + { WSA884X_PWRSTAGE_CTRL2, 0x31 }, + { WSA884X_PWRSTAGE_CTRL3, 0x81 }, + { WSA884X_PWRSTAGE_CTRL4, 0x5f }, + { WSA884X_MAXD_REG1, 0x00 }, + { WSA884X_MAXD_REG2, 0x5b }, + { WSA884X_ILIM_CTRL1, 0xe2 }, + { WSA884X_ILIM_CTRL2, 0x90 }, + { WSA884X_TEST_CTRL1, 0x00 }, + { WSA884X_TEST_CTRL2, 0x00 }, + { WSA884X_SPARE1, 0x00 }, + { WSA884X_BOOT_CAP_CHECK, 0x01 }, + { WSA884X_PON_CTL_0, 0x12 }, + { WSA884X_PWRSAV_CTL, 0xaa }, + { WSA884X_PON_LDOL_SPARE_CTL_0, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_2, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_3, 0x00 }, + { WSA884X_PON_CLT_1, 0xe1 }, + { WSA884X_PON_CTL_2, 0x00 }, + { WSA884X_PON_CTL_3, 0x70 }, + { WSA884X_CKWD_CTL_0, 0x14 }, + { WSA884X_CKWD_CTL_1, 0x3b }, + { WSA884X_CKWD_CTL_2, 0x00 }, + { WSA884X_CKSK_CTL_0, 0x00 }, + { WSA884X_PADSW_CTL_0, 0x00 }, + { WSA884X_TEST_0, 0x00 }, + { WSA884X_TEST_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_0, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_2, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_3, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_4, 0x00 }, + { WSA884X_DIG_CTRL0_PAGE, 0x00 }, + { WSA884X_CDC_RST_CTL, 0x01 }, + { WSA884X_SWR_RESET_EN, 0x00 }, + { WSA884X_TOP_CLK_CFG, 0x00 }, + { WSA884X_SWR_CLK_RATE, 0x00 }, + { WSA884X_CDC_PATH_MODE, 0x00 }, + { WSA884X_CDC_CLK_CTL, 0x1f }, + { WSA884X_PA_FSM_EN, 0x00 }, + { WSA884X_PA_FSM_CTL0, 0x00 }, + { WSA884X_PA_FSM_CTL1, 0xfe }, + { WSA884X_PA_FSM_TIMER0, 0x80 }, + { WSA884X_PA_FSM_TIMER1, 0x80 }, + { WSA884X_PA_FSM_ERR_CTL, 0x00 }, + { WSA884X_PA_FSM_MSK0, 0x00 }, + { WSA884X_PA_FSM_MSK1, 0x00 }, + { WSA884X_PA_FSM_BYP_CTL, 0x00 }, + { WSA884X_PA_FSM_BYP0, 0x00 }, + { WSA884X_PA_FSM_BYP1, 0x00 }, + { WSA884X_TADC_VALUE_CTL, 0x03 }, + { WSA884X_TEMP_DETECT_CTL, 0x01 }, + { WSA884X_TEMP_CONFIG0, 0x00 }, + { WSA884X_TEMP_CONFIG1, 0x00 }, + { WSA884X_VBAT_THRM_FLT_CTL, 0x7f }, + { WSA884X_VBAT_CAL_CTL, 0x01 }, + { WSA884X_UVLO_DEGLITCH_CTL, 0x05 }, + { WSA884X_BOP_DEGLITCH_CTL, 0x05 }, + { WSA884X_VBAT_ZONE_DETC_CTL, 0x31 }, + { WSA884X_CPS_CTL, 0x00 }, + { WSA884X_CDC_RX_CTL, 0xfe }, + { WSA884X_CDC_SPK_DSM_A1_0, 0x00 }, + { WSA884X_CDC_SPK_DSM_A1_1, 0x01 }, + { WSA884X_CDC_SPK_DSM_A2_0, 0x96 }, + { WSA884X_CDC_SPK_DSM_A2_1, 0x09 }, + { WSA884X_CDC_SPK_DSM_A3_0, 0xab }, + { WSA884X_CDC_SPK_DSM_A3_1, 0x05 }, + { WSA884X_CDC_SPK_DSM_A4_0, 0x1c }, + { WSA884X_CDC_SPK_DSM_A4_1, 0x02 }, + { WSA884X_CDC_SPK_DSM_A5_0, 0x17 }, + { WSA884X_CDC_SPK_DSM_A5_1, 0x02 }, + { WSA884X_CDC_SPK_DSM_A6_0, 0xaa }, + { WSA884X_CDC_SPK_DSM_A7_0, 0xe3 }, + { WSA884X_CDC_SPK_DSM_C_0, 0x69 }, + { WSA884X_CDC_SPK_DSM_C_1, 0x54 }, + { WSA884X_CDC_SPK_DSM_C_2, 0x02 }, + { WSA884X_CDC_SPK_DSM_C_3, 0x15 }, + { WSA884X_CDC_SPK_DSM_R1, 0xa4 }, + { WSA884X_CDC_SPK_DSM_R2, 0xb5 }, + { WSA884X_CDC_SPK_DSM_R3, 0x86 }, + { WSA884X_CDC_SPK_DSM_R4, 0x85 }, + { WSA884X_CDC_SPK_DSM_R5, 0xaa }, + { WSA884X_CDC_SPK_DSM_R6, 0xe2 }, + { WSA884X_CDC_SPK_DSM_R7, 0x62 }, + { WSA884X_CDC_SPK_GAIN_PDM_0, 0x00 }, + { WSA884X_CDC_SPK_GAIN_PDM_1, 0xfc }, + { WSA884X_CDC_SPK_GAIN_PDM_2, 0x05 }, + { WSA884X_PDM_WD_CTL, 0x00 }, + { WSA884X_DEM_BYPASS_DATA0, 0x00 }, + { WSA884X_DEM_BYPASS_DATA1, 0x00 }, + { WSA884X_DEM_BYPASS_DATA2, 0x00 }, + { WSA884X_DEM_BYPASS_DATA3, 0x00 }, + { WSA884X_DRE_CTL_0, 0x70 }, + { WSA884X_DRE_CTL_1, 0x04 }, + { WSA884X_DRE_IDLE_DET_CTL, 0x2f }, + { WSA884X_GAIN_RAMPING_CTL, 0x50 }, + { WSA884X_GAIN_RAMPING_MIN, 0x12 }, + { WSA884X_TAGC_CTL, 0x15 }, + { WSA884X_TAGC_TIME, 0xbc }, + { WSA884X_TAGC_FORCE_VAL, 0x00 }, + { WSA884X_VAGC_CTL, 0x01 }, + { WSA884X_VAGC_TIME, 0x0f }, + { WSA884X_VAGC_ATTN_LVL_1, 0x03 }, + { WSA884X_VAGC_ATTN_LVL_2, 0x06 }, + { WSA884X_VAGC_ATTN_LVL_3, 0x09 }, + { WSA884X_CLSH_CTL_0, 0x37 }, + { WSA884X_CLSH_CTL_1, 0x81 }, + { WSA884X_CLSH_V_HD_PA, 0x0c }, + { WSA884X_CLSH_V_PA_MIN, 0x00 }, + { WSA884X_CLSH_OVRD_VAL, 0x00 }, + { WSA884X_CLSH_HARD_MAX, 0xff }, + { WSA884X_CLSH_SOFT_MAX, 0xf5 }, + { WSA884X_CLSH_SIG_DP, 0x00 }, + { WSA884X_PBR_DELAY_CTL, 0x07 }, + { WSA884X_CLSH_SRL_MAX_PBR, 0x02 }, + { WSA884X_CLSH_VTH1, 0x00 }, + { WSA884X_CLSH_VTH2, 0x00 }, + { WSA884X_CLSH_VTH3, 0x00 }, + { WSA884X_CLSH_VTH4, 0x00 }, + { WSA884X_CLSH_VTH5, 0x00 }, + { WSA884X_CLSH_VTH6, 0x00 }, + { WSA884X_CLSH_VTH7, 0x00 }, + { WSA884X_CLSH_VTH8, 0x00 }, + { WSA884X_CLSH_VTH9, 0x00 }, + { WSA884X_CLSH_VTH10, 0x00 }, + { WSA884X_CLSH_VTH11, 0x00 }, + { WSA884X_CLSH_VTH12, 0x00 }, + { WSA884X_CLSH_VTH13, 0x00 }, + { WSA884X_CLSH_VTH14, 0x00 }, + { WSA884X_CLSH_VTH15, 0x00 }, + { WSA884X_DIG_CTRL1_PAGE, 0x00 }, + { WSA884X_PIN_CTL, 0x04 }, + { WSA884X_PIN_CTL_OE, 0x00 }, + { WSA884X_PIN_WDATA_IOPAD, 0x00 }, + { WSA884X_I2C_SLAVE_CTL, 0x00 }, + { WSA884X_SPMI_PAD_CTL0, 0x2f }, + { WSA884X_SPMI_PAD_CTL1, 0x2f }, + { WSA884X_SPMI_PAD_CTL2, 0x2f }, + { WSA884X_MEM_CTL, 0x00 }, + { WSA884X_SWR_HM_TEST0, 0x08 }, + { WSA884X_OTP_CTRL0, 0x00 }, + { WSA884X_OTP_CTRL2, 0x00 }, + { WSA884X_OTP_PRG_TCSP0, 0x77 }, + { WSA884X_OTP_PRG_TCSP1, 0x00 }, + { WSA884X_OTP_PRG_TPPS, 0x47 }, + { WSA884X_OTP_PRG_TVPS, 0x3b }, + { WSA884X_OTP_PRG_TVPH, 0x47 }, + { WSA884X_OTP_PRG_TPPR0, 0x47 }, + { WSA884X_OTP_PRG_TPPR1, 0x00 }, + { WSA884X_OTP_PRG_TPPH, 0x47 }, + { WSA884X_OTP_PRG_END, 0x47 }, + { WSA884X_WAVG_PLAY, 0x00 }, + { WSA884X_WAVG_CTL, 0x06 }, + { WSA884X_WAVG_LRA_PER_0, 0xd1 }, + { WSA884X_WAVG_LRA_PER_1, 0x00 }, + { WSA884X_WAVG_DELTA_THETA_0, 0xe6 }, + { WSA884X_WAVG_DELTA_THETA_1, 0x04 }, + { WSA884X_WAVG_DIRECT_AMP_0, 0x50 }, + { WSA884X_WAVG_DIRECT_AMP_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP0_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP0_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP1_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP1_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP2_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP2_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP3_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP3_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP4_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP4_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP5_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP5_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP6_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP6_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP7_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP7_1, 0x00 }, + { WSA884X_WAVG_PER_0_1, 0x88 }, + { WSA884X_WAVG_PER_2_3, 0x88 }, + { WSA884X_WAVG_PER_4_5, 0x88 }, + { WSA884X_WAVG_PER_6_7, 0x88 }, + { WSA884X_INTR_MODE, 0x00 }, + { WSA884X_INTR_MASK0, 0x90 }, + { WSA884X_INTR_MASK1, 0x00 }, + { WSA884X_INTR_CLEAR0, 0x00 }, + { WSA884X_INTR_CLEAR1, 0x00 }, + { WSA884X_INTR_LEVEL0, 0x04 }, + { WSA884X_INTR_LEVEL1, 0x00 }, + { WSA884X_INTR_SET0, 0x00 }, + { WSA884X_INTR_SET1, 0x00 }, + { WSA884X_INTR_TEST0, 0x00 }, + { WSA884X_INTR_TEST1, 0x00 }, + { WSA884X_PDM_TEST_MODE, 0x00 }, + { WSA884X_PA_FSM_DBG, 0x00 }, + { WSA884X_DIG_DEBUG_MODE, 0x00 }, + { WSA884X_DIG_DEBUG_SEL, 0x00 }, + { WSA884X_DIG_DEBUG_EN, 0x00 }, + { WSA884X_TADC_DETECT_DBG_CTL, 0x00 }, + { WSA884X_TADC_DEBUG_MSB, 0x00 }, + { WSA884X_TADC_DEBUG_LSB, 0x00 }, + { WSA884X_SAMPLE_EDGE_SEL, 0x7f }, + { WSA884X_SWR_EDGE_SEL, 0x00 }, + { WSA884X_TEST_MODE_CTL, 0x05 }, + { WSA884X_IOPAD_CTL, 0x00 }, + { WSA884X_ANA_CSR_DBG_ADD, 0x00 }, + { WSA884X_ANA_CSR_DBG_CTL, 0x12 }, + { WSA884X_CLK_DBG_CTL, 0x00 }, + { WSA884X_SPARE_0, 0x00 }, + { WSA884X_SPARE_1, 0x00 }, + { WSA884X_SPARE_2, 0x00 }, + { WSA884X_SCODE, 0x00 }, + { WSA884X_DIG_TRIM_PAGE, 0x00 }, + { WSA884X_EMEM_0, 0x00 }, + { WSA884X_EMEM_1, 0x00 }, + { WSA884X_EMEM_2, 0x00 }, + { WSA884X_EMEM_3, 0x00 }, + { WSA884X_EMEM_4, 0x00 }, + { WSA884X_EMEM_5, 0x00 }, + { WSA884X_EMEM_6, 0x00 }, + { WSA884X_EMEM_7, 0x00 }, + { WSA884X_EMEM_8, 0x00 }, + { WSA884X_EMEM_9, 0x00 }, + { WSA884X_EMEM_10, 0x00 }, + { WSA884X_EMEM_11, 0x00 }, + { WSA884X_EMEM_12, 0x00 }, + { WSA884X_EMEM_13, 0x00 }, + { WSA884X_EMEM_14, 0x00 }, + { WSA884X_EMEM_15, 0x00 }, + { WSA884X_EMEM_16, 0x00 }, + { WSA884X_EMEM_17, 0x00 }, + { WSA884X_EMEM_18, 0x00 }, + { WSA884X_EMEM_19, 0x00 }, + { WSA884X_EMEM_20, 0x00 }, + { WSA884X_EMEM_21, 0x00 }, + { WSA884X_EMEM_22, 0x00 }, + { WSA884X_EMEM_23, 0x00 }, + { WSA884X_EMEM_24, 0x00 }, + { WSA884X_EMEM_25, 0x00 }, + { WSA884X_EMEM_26, 0x00 }, + { WSA884X_EMEM_27, 0x00 }, + { WSA884X_EMEM_28, 0x00 }, + { WSA884X_EMEM_29, 0x00 }, + { WSA884X_EMEM_30, 0x00 }, + { WSA884X_EMEM_31, 0x00 }, + { WSA884X_EMEM_32, 0x00 }, + { WSA884X_EMEM_33, 0x00 }, + { WSA884X_EMEM_34, 0x00 }, + { WSA884X_EMEM_35, 0x00 }, + { WSA884X_EMEM_36, 0x00 }, + { WSA884X_EMEM_37, 0x00 }, + { WSA884X_EMEM_38, 0x00 }, + { WSA884X_EMEM_39, 0x00 }, + { WSA884X_EMEM_40, 0x00 }, + { WSA884X_EMEM_41, 0x00 }, + { WSA884X_EMEM_42, 0x00 }, + { WSA884X_EMEM_43, 0x00 }, + { WSA884X_EMEM_44, 0x00 }, + { WSA884X_EMEM_45, 0x00 }, + { WSA884X_EMEM_46, 0x00 }, + { WSA884X_EMEM_47, 0x00 }, + { WSA884X_EMEM_48, 0x00 }, + { WSA884X_EMEM_49, 0x00 }, + { WSA884X_EMEM_50, 0x00 }, + { WSA884X_EMEM_51, 0x00 }, + { WSA884X_EMEM_52, 0x00 }, + { WSA884X_EMEM_53, 0x00 }, + { WSA884X_EMEM_54, 0x00 }, + { WSA884X_EMEM_55, 0x00 }, + { WSA884X_EMEM_56, 0x00 }, + { WSA884X_EMEM_57, 0x00 }, + { WSA884X_EMEM_58, 0x00 }, + { WSA884X_EMEM_59, 0x00 }, + { WSA884X_EMEM_60, 0x00 }, + { WSA884X_EMEM_61, 0x00 }, + { WSA884X_EMEM_62, 0x00 }, + { WSA884X_EMEM_63, 0x00 }, +}; + +static bool wsa884x_readonly_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA884X_DOUT_MSB: + case WSA884X_DOUT_LSB: + case WSA884X_STATUS: + case WSA884X_SPK_TOP_SPARE_TUNE_2: + case WSA884X_SPK_TOP_SPARE_TUNE_3: + case WSA884X_SPK_TOP_SPARE_TUNE_4: + case WSA884X_SPARE_TUNE_5: + case WSA884X_SPARE_TUNE_6: + case WSA884X_SPARE_TUNE_7: + case WSA884X_SPARE_TUNE_8: + case WSA884X_SPARE_TUNE_9: + case WSA884X_SPARE_TUNE_10: + case WSA884X_PA_STATUS0: + case WSA884X_PA_STATUS1: + case WSA884X_PA_STATUS2: + case WSA884X_PA_STATUS3: + case WSA884X_PA_STATUS4: + case WSA884X_PA_STATUS5: + case WSA884X_SPARE_RO_1: + case WSA884X_SPARE_RO_2: + case WSA884X_SPARE_RO_3: + case WSA884X_SPARE_RO_0: + case WSA884X_BOOST_SPARE_RO_1: + case WSA884X_STATUS_0: + case WSA884X_STATUS_1: + case WSA884X_CHIP_ID0: + case WSA884X_CHIP_ID1: + case WSA884X_CHIP_ID2: + case WSA884X_CHIP_ID3: + case WSA884X_BUS_ID: + case WSA884X_PA_FSM_STA0: + case WSA884X_PA_FSM_STA1: + case WSA884X_PA_FSM_ERR_COND0: + case WSA884X_PA_FSM_ERR_COND1: + case WSA884X_TEMP_DIN_MSB: + case WSA884X_TEMP_DIN_LSB: + case WSA884X_TEMP_DOUT_MSB: + case WSA884X_TEMP_DOUT_LSB: + case WSA884X_VBAT_DIN_MSB: + case WSA884X_VBAT_DIN_LSB: + case WSA884X_VBAT_DOUT_MSB: + case WSA884X_VBAT_DOUT_LSB: + case WSA884X_VBAT_CAL_MSB: + case WSA884X_VBAT_CAL_LSB: + case WSA884X_VPHX_SYS_EN_STATUS: + case WSA884X_PIN_STATUS: + case WSA884X_SWR_HM_TEST1: + case WSA884X_OTP_CTRL1: + case WSA884X_OTP_STAT: + case WSA884X_WAVG_STA: + case WSA884X_INTR_STATUS0: + case WSA884X_INTR_STATUS1: + case WSA884X_ATE_TEST_MODE: + case WSA884X_SPARE_R: + return true; + } + return false; +} + +static bool wsa884x_writeable_register(struct device *dev, unsigned int reg) +{ + return !wsa884x_readonly_register(dev, reg); +} + +static bool wsa884x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA884X_ANA_WO_CTL_0: + case WSA884X_ANA_WO_CTL_1: + return true; + } + return wsa884x_readonly_register(dev, reg); +} + +static struct regmap_config wsa884x_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wsa884x_defaults, + .max_register = WSA884X_MAX_REGISTER, + .num_reg_defaults = ARRAY_SIZE(wsa884x_defaults), + .volatile_reg = wsa884x_volatile_register, + .writeable_reg = wsa884x_writeable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .use_single_read = true, +}; + +static const struct reg_sequence wsa884x_reg_init[] = { + { WSA884X_BOP2_PROG, FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_VTH_MASK, 0x6) | + FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_HYST_MASK, 0x6) }, + { WSA884X_REF_CTRL, (0xd2 & ~WSA884X_REF_CTRL_BG_RDY_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_REF_CTRL_BG_RDY_SEL_MASK, 0x1) }, + /* + * Downstream suggests for batteries different than 1-Stacked (1S): + * { WSA884X_TOP_CTRL1, 0xd3 & ~WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK }, + */ + { WSA884X_STB_CTRL1, (0x42 & ~WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK) | + FIELD_PREP_CONST(WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK, 0xd) }, + { WSA884X_CURRENT_LIMIT, (0x54 & ~WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK) | + FIELD_PREP_CONST(WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK, 0x9) }, + { WSA884X_ZX_CTRL1, (0xf0 & ~WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK, 0x3) }, + { WSA884X_ILIM_CTRL1, (0xe2 & ~WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK) | + FIELD_PREP_CONST(WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK, 0x3) }, + { WSA884X_CKWD_CTL_1, FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK, 0x0) | + FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK, 0x13) }, + { WSA884X_PA_FSM_CTL1, (0xfe & ~WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK) | + FIELD_PREP_CONST(WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK, 0x4) }, /* == 0xfe */ + { WSA884X_VBAT_THRM_FLT_CTL, (0x7f & ~WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK, 0x4) }, + { WSA884X_VBAT_CAL_CTL, FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_RESERVE_MASK, 0x2) | + FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK, 0x1) }, + { WSA884X_BOP_DEGLITCH_CTL, FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK, 0x8) | + FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK, 0x1) }, + { WSA884X_CDC_SPK_DSM_A2_0, 0x0a }, + { WSA884X_CDC_SPK_DSM_A2_1, 0x08 }, + { WSA884X_CDC_SPK_DSM_A3_0, 0xf3 }, + { WSA884X_CDC_SPK_DSM_A3_1, 0x07 }, + { WSA884X_CDC_SPK_DSM_A4_0, 0x79 }, + { WSA884X_CDC_SPK_DSM_A5_0, 0x0b }, + { WSA884X_CDC_SPK_DSM_A6_0, 0x8a }, + { WSA884X_CDC_SPK_DSM_A7_0, 0x9b }, + { WSA884X_CDC_SPK_DSM_C_0, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK, 0x6) | + FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK, 0x8) }, + { WSA884X_CDC_SPK_DSM_C_2, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK, 0xf) }, + { WSA884X_CDC_SPK_DSM_C_3, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK, 0x20) }, + { WSA884X_CDC_SPK_DSM_R1, 0x83 }, + { WSA884X_CDC_SPK_DSM_R2, 0x7f }, + { WSA884X_CDC_SPK_DSM_R3, 0x9d }, + { WSA884X_CDC_SPK_DSM_R4, 0x82 }, + { WSA884X_CDC_SPK_DSM_R5, 0x8b }, + { WSA884X_CDC_SPK_DSM_R6, 0x9b }, + { WSA884X_CDC_SPK_DSM_R7, 0x3f }, + /* Speaker mode by default */ + { WSA884X_DRE_CTL_0, FIELD_PREP_CONST(WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x7) }, + { WSA884X_CLSH_CTL_0, (0x37 & ~WSA884X_CLSH_CTL_0_DLY_CODE_MASK) | + FIELD_PREP_CONST(WSA884X_CLSH_CTL_0_DLY_CODE_MASK, 0x6) }, + /* + * WSA884X_CLSH_VTH values for speaker mode with G_21_DB system gain, + * battery 1S and rload 8 Ohms. + */ + { WSA884X_CLSH_VTH1, WSA884X_VTH_TO_REG(863), }, + { WSA884X_CLSH_VTH2, WSA884X_VTH_TO_REG(918), }, + { WSA884X_CLSH_VTH3, WSA884X_VTH_TO_REG(980), }, + { WSA884X_CLSH_VTH4, WSA884X_VTH_TO_REG(1043), }, + { WSA884X_CLSH_VTH5, WSA884X_VTH_TO_REG(1098), }, + { WSA884X_CLSH_VTH6, WSA884X_VTH_TO_REG(1137), }, + { WSA884X_CLSH_VTH7, WSA884X_VTH_TO_REG(1184), }, + { WSA884X_CLSH_VTH8, WSA884X_VTH_TO_REG(1239), }, + { WSA884X_CLSH_VTH9, WSA884X_VTH_TO_REG(1278), }, + { WSA884X_CLSH_VTH10, WSA884X_VTH_TO_REG(1380), }, + { WSA884X_CLSH_VTH11, WSA884X_VTH_TO_REG(1482), }, + { WSA884X_CLSH_VTH12, WSA884X_VTH_TO_REG(1584), }, + { WSA884X_CLSH_VTH13, WSA884X_VTH_TO_REG(1663), }, + { WSA884X_CLSH_VTH14, WSA884X_VTH_TO_REG(1780), }, + { WSA884X_CLSH_VTH15, WSA884X_VTH_TO_REG(2000), }, + { WSA884X_ANA_WO_CTL_1, 0x00 }, + { WSA884X_OTP_REG_38, 0x00 }, + { WSA884X_OTP_REG_40, FIELD_PREP_CONST(WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK, 0x8) }, +}; + +static void wsa884x_set_gain_parameters(struct wsa884x_priv *wsa884x) +{ + struct regmap *regmap = wsa884x->regmap; + unsigned int min_gain, igain, vgain, comp_offset; + + /* + * Downstream sets gain parameters customized per boards per use-case. + * Choose here some sane values matching knowon users, like QRD8550 + * board:. + * + * Values match here downstream: + * For WSA884X_RECEIVER - G_7P5_DB system gain + * For WSA884X_SPEAKER - G_21_DB system gain + */ + if (wsa884x->dev_mode == WSA884X_RECEIVER) { + comp_offset = COMP_OFFSET4; + min_gain = G_M6_DB; + igain = ISENSE_18_DB; + vgain = VSENSE_M12_DB; + } else { + /* WSA884X_SPEAKER */ + comp_offset = COMP_OFFSET0; + min_gain = G_0_DB; + igain = ISENSE_12_DB; + vgain = VSENSE_M24_DB; + } + + regmap_update_bits(regmap, WSA884X_ISENSE2, + WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, + FIELD_PREP(WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, igain)); + regmap_update_bits(regmap, WSA884X_VSENSE1, + WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK, + FIELD_PREP(WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK, vgain)); + regmap_update_bits(regmap, WSA884X_GAIN_RAMPING_MIN, + WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK, + FIELD_PREP(WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK, min_gain)); + + if (wsa884x->port_enable[WSA884X_PORT_COMP]) { + regmap_update_bits(regmap, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_OFFSET_MASK, + FIELD_PREP(WSA884X_DRE_CTL_0_OFFSET_MASK, comp_offset)); + + regmap_update_bits(regmap, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x0)); + } else { + regmap_update_bits(regmap, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x1)); + } +} + +static void wsa884x_init(struct wsa884x_priv *wsa884x) +{ + unsigned int wo_ctl_0; + unsigned int variant = 0; + + if (!regmap_read(wsa884x->regmap, WSA884X_OTP_REG_0, &variant)) + wsa884x->variant = variant & WSA884X_OTP_REG_0_ID_MASK; + + regmap_multi_reg_write(wsa884x->regmap, wsa884x_reg_init, + ARRAY_SIZE(wsa884x_reg_init)); + + wo_ctl_0 = 0xc; + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK, + WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER); + /* Assume that compander is enabled by default unless it is haptics sku */ + if (wsa884x->variant == WSA884X_OTP_ID_WSA8845H) + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK, + WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB); + else + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK, + WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB); + regmap_write(wsa884x->regmap, WSA884X_ANA_WO_CTL_0, wo_ctl_0); + + wsa884x_set_gain_parameters(wsa884x); + + wsa884x->hw_init = false; +} + +static int wsa884x_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev); + int ret; + + if (status == SDW_SLAVE_UNATTACHED) { + wsa884x->hw_init = false; + regcache_cache_only(wsa884x->regmap, true); + regcache_mark_dirty(wsa884x->regmap); + return 0; + } + + if (wsa884x->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + regcache_cache_only(wsa884x->regmap, false); + ret = regcache_sync(wsa884x->regmap); + if (ret < 0) { + dev_err(&slave->dev, "Cannot sync regmap cache\n"); + return ret; + } + + wsa884x_init(wsa884x); + + return 0; +} + +static int wsa884x_port_prep(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops state) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev); + + if (state == SDW_OPS_PORT_POST_PREP) + wsa884x->port_prepared[prepare_ch->num - 1] = true; + else + wsa884x->port_prepared[prepare_ch->num - 1] = false; + + return 0; +} + +static const struct sdw_slave_ops wsa884x_slave_ops = { + .update_status = wsa884x_update_status, + .port_prep = wsa884x_port_prep, +}; + +static int wsa884x_dev_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wsa884x->dev_mode; + + return 0; +} + +static int wsa884x_dev_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + if (wsa884x->dev_mode == ucontrol->value.enumerated.item[0]) + return 0; + + wsa884x->dev_mode = ucontrol->value.enumerated.item[0]; + + return 1; +} + +static int wsa884x_get_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + ucontrol->value.integer.value[0] = wsa884x->port_enable[portidx]; + + return 0; +} + +static int wsa884x_set_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + if (ucontrol->value.integer.value[0]) { + if (wsa884x->port_enable[portidx]) + return 0; + + wsa884x->port_enable[portidx] = true; + } else { + if (!wsa884x->port_enable[portidx]) + return 0; + + wsa884x->port_enable[portidx] = false; + } + + return 1; +} + +static int wsa884x_codec_probe(struct snd_soc_component *comp) +{ + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa884x->regmap); + + return 0; +} + +static void wsa884x_spkr_post_pmu(struct snd_soc_component *component, + struct wsa884x_priv *wsa884x) +{ + unsigned int curr_limit, curr_ovrd_en; + + wsa884x_set_gain_parameters(wsa884x); + if (wsa884x->dev_mode == WSA884X_RECEIVER) { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x3); + snd_soc_component_write_field(component, WSA884X_CDC_PATH_MODE, + WSA884X_CDC_PATH_MODE_RXD_MODE_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PWM_CLK_CTL, + WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK, + 0x1); + } else { + /* WSA884X_SPEAKER */ + snd_soc_component_write_field(component, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0xf); + } + + if (wsa884x->port_enable[WSA884X_PORT_PBR]) { + curr_ovrd_en = 0x0; + curr_limit = 0x15; + } else { + curr_ovrd_en = 0x1; + if (wsa884x->dev_mode == WSA884X_RECEIVER) + curr_limit = 0x9; + else + curr_limit = 0x15; + } + snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT, + WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK, + curr_ovrd_en); + snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT, + WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK, + curr_limit); +} + +static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wsa884x_spkr_post_pmu(component, wsa884x); + + snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x1); + + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x0); + snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x0); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget wsa884x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_SPK("SPKR", wsa884x_spkr_event), +}; + +static const DECLARE_TLV_DB_SCALE(pa_gain, -900, 150, -900); + +static const struct snd_kcontrol_new wsa884x_snd_controls[] = { + SOC_SINGLE_RANGE_TLV("PA Volume", WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT, + 0x0, 0x1f, 1, pa_gain), + SOC_ENUM_EXT("WSA MODE", wsa884x_dev_mode_enum, + wsa884x_dev_mode_get, wsa884x_dev_mode_put), + SOC_SINGLE_EXT("DAC Switch", WSA884X_PORT_DAC, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("COMP Switch", WSA884X_PORT_COMP, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("BOOST Switch", WSA884X_PORT_BOOST, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("PBR Switch", WSA884X_PORT_PBR, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("VISENSE Switch", WSA884X_PORT_VISENSE, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("CPS Switch", WSA884X_PORT_CPS, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), +}; + +static const struct snd_soc_dapm_route wsa884x_audio_map[] = { + {"SPKR", NULL, "IN"}, +}; + +static const struct snd_soc_component_driver wsa884x_component_drv = { + .name = "WSA884x", + .probe = wsa884x_codec_probe, + .controls = wsa884x_snd_controls, + .num_controls = ARRAY_SIZE(wsa884x_snd_controls), + .dapm_widgets = wsa884x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa884x_dapm_widgets), + .dapm_routes = wsa884x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa884x_audio_map), +}; + +static int wsa884x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + int i; + + wsa884x->active_ports = 0; + for (i = 0; i < WSA884X_MAX_SWR_PORTS; i++) { + if (!wsa884x->port_enable[i]) + continue; + + wsa884x->port_config[wsa884x->active_ports] = wsa884x_pconfig[i]; + wsa884x->active_ports++; + } + + wsa884x->sconfig.frame_rate = params_rate(params); + + return sdw_stream_add_slave(wsa884x->slave, &wsa884x->sconfig, + wsa884x->port_config, wsa884x->active_ports, + wsa884x->sruntime); +} + +static int wsa884x_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + + sdw_stream_remove_slave(wsa884x->slave, wsa884x->sruntime); + + return 0; +} + +static int wsa884x_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + + if (mute) { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + 0x0); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x0); + + } else { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x1); + } + + return 0; +} + +static int wsa884x_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + + wsa884x->sruntime = stream; + + return 0; +} + +static const struct snd_soc_dai_ops wsa884x_dai_ops = { + .hw_params = wsa884x_hw_params, + .hw_free = wsa884x_hw_free, + .mute_stream = wsa884x_mute_stream, + .set_stream = wsa884x_set_stream, +}; + +static struct snd_soc_dai_driver wsa884x_dais[] = { + { + .name = "SPKR", + .playback = { + .stream_name = "SPKR Playback", + .rates = WSA884X_RATES | WSA884X_FRAC_RATES, + .formats = WSA884X_FORMATS, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &wsa884x_dai_ops, + }, +}; + +static void wsa884x_gpio_powerdown(void *data) +{ + gpiod_direction_output(data, 1); +} + +static void wsa884x_regulator_disable(void *data) +{ + regulator_bulk_disable(WSA884X_SUPPLIES_NUM, data); +} + +static int wsa884x_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct device *dev = &pdev->dev; + struct wsa884x_priv *wsa884x; + unsigned int i; + int ret; + + wsa884x = devm_kzalloc(dev, sizeof(*wsa884x), GFP_KERNEL); + if (!wsa884x) + return -ENOMEM; + + for (i = 0; i < WSA884X_SUPPLIES_NUM; i++) + wsa884x->supplies[i].supply = wsa884x_supply_name[i]; + + ret = devm_regulator_bulk_get(dev, WSA884X_SUPPLIES_NUM, + wsa884x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ret = regulator_bulk_enable(WSA884X_SUPPLIES_NUM, wsa884x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + ret = devm_add_action_or_reset(dev, wsa884x_regulator_disable, + wsa884x->supplies); + if (ret) + return ret; + + wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(wsa884x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n), + "Shutdown Control GPIO not found\n"); + + dev_set_drvdata(dev, wsa884x); + wsa884x->slave = pdev; + wsa884x->dev = dev; + wsa884x->dev_mode = WSA884X_SPEAKER; + wsa884x->sconfig.ch_count = 1; + wsa884x->sconfig.bps = 1; + wsa884x->sconfig.direction = SDW_DATA_DIR_RX; + wsa884x->sconfig.type = SDW_STREAM_PDM; + + pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS, 0); + pdev->prop.simple_clk_stop_capable = true; + pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; + pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + + /* Bring out of reset */ + gpiod_direction_output(wsa884x->sd_n, 0); + ret = devm_add_action_or_reset(dev, wsa884x_gpio_powerdown, wsa884x->sd_n); + if (ret) + return ret; + + wsa884x->regmap = devm_regmap_init_sdw(pdev, &wsa884x_regmap_config); + if (IS_ERR(wsa884x->regmap)) + return dev_err_probe(dev, PTR_ERR(wsa884x->regmap), + "regmap_init failed\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(wsa884x->regmap, true); + wsa884x->hw_init = true; + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return devm_snd_soc_register_component(dev, + &wsa884x_component_drv, + wsa884x_dais, + ARRAY_SIZE(wsa884x_dais)); +} + +static int __maybe_unused wsa884x_runtime_suspend(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + + return 0; +} + +static int __maybe_unused wsa884x_runtime_resume(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + + regcache_cache_only(regmap, false); + regcache_sync(regmap); + + return 0; +} + +static const struct dev_pm_ops wsa884x_pm_ops = { + SET_RUNTIME_PM_OPS(wsa884x_runtime_suspend, wsa884x_runtime_resume, NULL) +}; + +static const struct sdw_device_id wsa884x_swr_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x204, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, wsa884x_swr_id); + +static struct sdw_driver wsa884x_codec_driver = { + .driver = { + .name = "wsa884x-codec", + .pm = &wsa884x_pm_ops, + }, + .probe = wsa884x_probe, + .ops = &wsa884x_slave_ops, + .id_table = wsa884x_swr_id, +}; +module_sdw_driver(wsa884x_codec_driver); + +MODULE_AUTHOR("Krzysztof Kozlowski "); +MODULE_DESCRIPTION("WSA884x codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c new file mode 100644 index 0000000000..28c92d9029 --- /dev/null +++ b/sound/soc/codecs/zl38060.c @@ -0,0 +1,635 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Codec driver for Microsemi ZL38060 Connected Home Audio Processor. +// +// Copyright(c) 2020 Sven Van Asbroeck + +// The ZL38060 is very flexible and configurable. This driver implements only a +// tiny subset of the chip's possible configurations: +// +// - DSP block bypassed: DAI routed straight to DACs +// microphone routed straight to DAI +// - chip's internal clock is driven by a 12 MHz external crystal +// - chip's DAI connected to CPU is I2S, and bit + frame clock master +// - chip must be strapped for "host boot": in this mode, firmware will be +// provided by this driver. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DRV_NAME "zl38060" + +#define ZL38_RATES (SNDRV_PCM_RATE_8000 |\ + SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_48000) +#define ZL38_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +#define HBI_FIRMWARE_PAGE 0xFF +#define ZL38_MAX_RAW_XFER 0x100 + +#define REG_TDMA_CFG_CLK 0x0262 +#define CFG_CLK_PCLK_SHIFT 4 +#define CFG_CLK_PCLK_MASK (0x7ff << CFG_CLK_PCLK_SHIFT) +#define CFG_CLK_PCLK(bits) ((bits - 1) << CFG_CLK_PCLK_SHIFT) +#define CFG_CLK_MASTER BIT(15) +#define CFG_CLK_FSRATE_MASK 0x7 +#define CFG_CLK_FSRATE_8KHZ 0x1 +#define CFG_CLK_FSRATE_16KHZ 0x2 +#define CFG_CLK_FSRATE_48KHZ 0x6 + +#define REG_CLK_CFG 0x0016 +#define CLK_CFG_SOURCE_XTAL BIT(15) + +#define REG_CLK_STATUS 0x0014 +#define CLK_STATUS_HWRST BIT(0) + +#define REG_PARAM_RESULT 0x0034 +#define PARAM_RESULT_READY 0xD3D3 + +#define REG_PG255_BASE_HI 0x000C +#define REG_PG255_OFFS(addr) ((HBI_FIRMWARE_PAGE << 8) | (addr & 0xFF)) +#define REG_FWR_EXEC 0x012C + +#define REG_CMD 0x0032 +#define REG_HW_REV 0x0020 +#define REG_FW_PROD 0x0022 +#define REG_FW_REV 0x0024 + +#define REG_SEMA_FLAGS 0x0006 +#define SEMA_FLAGS_BOOT_CMD BIT(0) +#define SEMA_FLAGS_APP_REBOOT BIT(1) + +#define REG_HW_REV 0x0020 +#define REG_FW_PROD 0x0022 +#define REG_FW_REV 0x0024 +#define REG_GPIO_DIR 0x02DC +#define REG_GPIO_DAT 0x02DA + +#define BOOTCMD_LOAD_COMPLETE 0x000D +#define BOOTCMD_FW_GO 0x0008 + +#define FIRMWARE_MAJOR 2 +#define FIRMWARE_MINOR 2 + +struct zl38_codec_priv { + struct device *dev; + struct regmap *regmap; + bool is_stream_in_use[2]; + struct gpio_chip *gpio_chip; +}; + +static int zl38_fw_issue_command(struct regmap *regmap, u16 cmd) +{ + unsigned int val; + int err; + + err = regmap_read_poll_timeout(regmap, REG_SEMA_FLAGS, val, + !(val & SEMA_FLAGS_BOOT_CMD), 10000, + 10000 * 100); + if (err) + return err; + err = regmap_write(regmap, REG_CMD, cmd); + if (err) + return err; + err = regmap_update_bits(regmap, REG_SEMA_FLAGS, SEMA_FLAGS_BOOT_CMD, + SEMA_FLAGS_BOOT_CMD); + if (err) + return err; + + return regmap_read_poll_timeout(regmap, REG_CMD, val, !val, 10000, + 10000 * 100); +} + +static int zl38_fw_go(struct regmap *regmap) +{ + int err; + + err = zl38_fw_issue_command(regmap, BOOTCMD_LOAD_COMPLETE); + if (err) + return err; + + return zl38_fw_issue_command(regmap, BOOTCMD_FW_GO); +} + +static int zl38_fw_enter_boot_mode(struct regmap *regmap) +{ + unsigned int val; + int err; + + err = regmap_update_bits(regmap, REG_CLK_STATUS, CLK_STATUS_HWRST, + CLK_STATUS_HWRST); + if (err) + return err; + + return regmap_read_poll_timeout(regmap, REG_PARAM_RESULT, val, + val == PARAM_RESULT_READY, 1000, 50000); +} + +static int +zl38_fw_send_data(struct regmap *regmap, u32 addr, const void *data, u16 len) +{ + __be32 addr_base = cpu_to_be32(addr & ~0xFF); + int err; + + err = regmap_raw_write(regmap, REG_PG255_BASE_HI, &addr_base, + sizeof(addr_base)); + if (err) + return err; + return regmap_raw_write(regmap, REG_PG255_OFFS(addr), data, len); +} + +static int zl38_fw_send_xaddr(struct regmap *regmap, const void *data) +{ + /* execution address from ihex: 32-bit little endian. + * device register expects 32-bit big endian. + */ + u32 addr = le32_to_cpup(data); + __be32 baddr = cpu_to_be32(addr); + + return regmap_raw_write(regmap, REG_FWR_EXEC, &baddr, sizeof(baddr)); +} + +static int zl38_load_firmware(struct device *dev, struct regmap *regmap) +{ + const struct ihex_binrec *rec; + const struct firmware *fw; + u32 addr; + u16 len; + int err; + + /* how to get this firmware: + * 1. request and download chip firmware from Microsemi + * (provided by Microsemi in srec format) + * 2. convert downloaded firmware from srec to ihex. Simple tool: + * https://gitlab.com/TheSven73/s3-to-irec + * 3. convert ihex to binary (.fw) using ihex2fw tool which is included + * with the Linux kernel sources + */ + err = request_ihex_firmware(&fw, "zl38060.fw", dev); + if (err) + return err; + err = zl38_fw_enter_boot_mode(regmap); + if (err) + goto out; + rec = (const struct ihex_binrec *)fw->data; + while (rec) { + addr = be32_to_cpu(rec->addr); + len = be16_to_cpu(rec->len); + if (addr) { + /* regular data ihex record */ + err = zl38_fw_send_data(regmap, addr, rec->data, len); + } else if (len == 4) { + /* execution address ihex record */ + err = zl38_fw_send_xaddr(regmap, rec->data); + } else { + err = -EINVAL; + } + if (err) + goto out; + /* next ! */ + rec = ihex_next_binrec(rec); + } + err = zl38_fw_go(regmap); + +out: + release_firmware(fw); + return err; +} + + +static int zl38_software_reset(struct regmap *regmap) +{ + unsigned int val; + int err; + + err = regmap_update_bits(regmap, REG_SEMA_FLAGS, SEMA_FLAGS_APP_REBOOT, + SEMA_FLAGS_APP_REBOOT); + if (err) + return err; + + /* wait for host bus interface to settle. + * Not sure if this is required: Microsemi's vendor driver does this, + * but the firmware manual does not mention it. Leave it in, there's + * little downside, apart from a slower reset. + */ + msleep(50); + + return regmap_read_poll_timeout(regmap, REG_SEMA_FLAGS, val, + !(val & SEMA_FLAGS_APP_REBOOT), 10000, + 10000 * 100); +} + +static int zl38_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai); + int err; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* firmware default is normal i2s */ + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + /* firmware default is normal bitclock and frame */ + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_CBP_CFP: + /* always 32 bits per frame (= 16 bits/channel, 2 channels) */ + err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK, + CFG_CLK_MASTER | CFG_CLK_PCLK_MASK, + CFG_CLK_MASTER | CFG_CLK_PCLK(32)); + if (err) + return err; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int zl38_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int fsrate; + int err; + + /* We cannot change hw_params while the dai is already in use - the + * software reset will corrupt the audio. However, this is not required, + * as the chip's TDM buses are fully symmetric, which mandates identical + * rates, channels, and samplebits for record and playback. + */ + if (priv->is_stream_in_use[!tx]) + goto skip_setup; + + switch (params_rate(params)) { + case 8000: + fsrate = CFG_CLK_FSRATE_8KHZ; + break; + case 16000: + fsrate = CFG_CLK_FSRATE_16KHZ; + break; + case 48000: + fsrate = CFG_CLK_FSRATE_48KHZ; + break; + default: + return -EINVAL; + } + + err = regmap_update_bits(priv->regmap, REG_TDMA_CFG_CLK, + CFG_CLK_FSRATE_MASK, fsrate); + if (err) + return err; + + /* chip requires a software reset to apply audio register changes */ + err = zl38_software_reset(priv->regmap); + if (err) + return err; + +skip_setup: + priv->is_stream_in_use[tx] = true; + + return 0; +} + +static int zl38_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct zl38_codec_priv *priv = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + priv->is_stream_in_use[tx] = false; + + return 0; +} + +/* stereo bypass with no AEC */ +static const struct reg_sequence cp_config_stereo_bypass[] = { + /* interconnects must be programmed first */ + { 0x0210, 0x0005 }, /* DAC1 in <= I2S1-L */ + { 0x0212, 0x0006 }, /* DAC2 in <= I2S1-R */ + { 0x0214, 0x0001 }, /* I2S1-L in <= MIC1 */ + { 0x0216, 0x0001 }, /* I2S1-R in <= MIC1 */ + { 0x0224, 0x0000 }, /* AEC-S in <= n/a */ + { 0x0226, 0x0000 }, /* AEC-R in <= n/a */ + /* output enables must be programmed next */ + { 0x0202, 0x000F }, /* enable I2S1 + DAC */ +}; + +static const struct snd_soc_dai_ops zl38_dai_ops = { + .set_fmt = zl38_set_fmt, + .hw_params = zl38_hw_params, + .hw_free = zl38_hw_free, +}; + +static struct snd_soc_dai_driver zl38_dai = { + .name = "zl38060-tdma", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = ZL38_RATES, + .formats = ZL38_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = ZL38_RATES, + .formats = ZL38_FORMATS, + }, + .ops = &zl38_dai_ops, + .symmetric_rate = 1, + .symmetric_sample_bits = 1, + .symmetric_channels = 1, +}; + +static const struct snd_soc_dapm_widget zl38_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("DAC1"), + SND_SOC_DAPM_OUTPUT("DAC2"), + + SND_SOC_DAPM_INPUT("DMICL"), +}; + +static const struct snd_soc_dapm_route zl38_dapm_routes[] = { + { "DAC1", NULL, "Playback" }, + { "DAC2", NULL, "Playback" }, + + { "Capture", NULL, "DMICL" }, +}; + +static const struct snd_soc_component_driver zl38_component_dev = { + .dapm_widgets = zl38_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(zl38_dapm_widgets), + .dapm_routes = zl38_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(zl38_dapm_routes), + .endianness = 1, +}; + +static void chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) +{ + struct regmap *regmap = gpiochip_get_data(c); + unsigned int mask = BIT(offset); + + regmap_update_bits(regmap, REG_GPIO_DAT, mask, val ? mask : 0); +} + +static int chip_gpio_get(struct gpio_chip *c, unsigned int offset) +{ + struct regmap *regmap = gpiochip_get_data(c); + unsigned int mask = BIT(offset); + unsigned int val; + int err; + + err = regmap_read(regmap, REG_GPIO_DAT, &val); + if (err) + return err; + + return !!(val & mask); +} + +static int chip_direction_input(struct gpio_chip *c, unsigned int offset) +{ + struct regmap *regmap = gpiochip_get_data(c); + unsigned int mask = BIT(offset); + + return regmap_update_bits(regmap, REG_GPIO_DIR, mask, 0); +} + +static int +chip_direction_output(struct gpio_chip *c, unsigned int offset, int val) +{ + struct regmap *regmap = gpiochip_get_data(c); + unsigned int mask = BIT(offset); + + chip_gpio_set(c, offset, val); + return regmap_update_bits(regmap, REG_GPIO_DIR, mask, mask); +} + +static const struct gpio_chip template_chip = { + .owner = THIS_MODULE, + .label = DRV_NAME, + + .base = -1, + .ngpio = 14, + .direction_input = chip_direction_input, + .direction_output = chip_direction_output, + .get = chip_gpio_get, + .set = chip_gpio_set, + + .can_sleep = true, +}; + +static int zl38_check_revision(struct device *dev, struct regmap *regmap) +{ + unsigned int hwrev, fwprod, fwrev; + int fw_major, fw_minor, fw_micro; + int err; + + err = regmap_read(regmap, REG_HW_REV, &hwrev); + if (err) + return err; + err = regmap_read(regmap, REG_FW_PROD, &fwprod); + if (err) + return err; + err = regmap_read(regmap, REG_FW_REV, &fwrev); + if (err) + return err; + + fw_major = (fwrev >> 12) & 0xF; + fw_minor = (fwrev >> 8) & 0xF; + fw_micro = fwrev & 0xFF; + dev_info(dev, "hw rev 0x%x, fw product code %d, firmware rev %d.%d.%d", + hwrev & 0x1F, fwprod, fw_major, fw_minor, fw_micro); + + if (fw_major != FIRMWARE_MAJOR || fw_minor < FIRMWARE_MINOR) { + dev_err(dev, "unsupported firmware. driver supports %d.%d", + FIRMWARE_MAJOR, FIRMWARE_MINOR); + return -EINVAL; + } + + return 0; +} + +static int zl38_bus_read(void *context, + const void *reg_buf, size_t reg_size, + void *val_buf, size_t val_size) +{ + struct spi_device *spi = context; + const u8 *reg_buf8 = reg_buf; + size_t len = 0; + u8 offs, page; + u8 txbuf[4]; + + if (reg_size != 2 || val_size > ZL38_MAX_RAW_XFER) + return -EINVAL; + + offs = reg_buf8[1] >> 1; + page = reg_buf8[0]; + + if (page) { + txbuf[len++] = 0xFE; + txbuf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1; + txbuf[len++] = offs; + txbuf[len++] = val_size / 2 - 1; + } else { + txbuf[len++] = offs | 0x80; + txbuf[len++] = val_size / 2 - 1; + } + + return spi_write_then_read(spi, txbuf, len, val_buf, val_size); +} + +static int zl38_bus_write(void *context, const void *data, size_t count) +{ + struct spi_device *spi = context; + u8 buf[4 + ZL38_MAX_RAW_XFER]; + size_t val_len, len = 0; + const u8 *data8 = data; + u8 offs, page; + + if (count > (2 + ZL38_MAX_RAW_XFER) || count < 4) + return -EINVAL; + val_len = count - 2; + offs = data8[1] >> 1; + page = data8[0]; + + if (page) { + buf[len++] = 0xFE; + buf[len++] = page == HBI_FIRMWARE_PAGE ? 0xFF : page - 1; + buf[len++] = offs; + buf[len++] = (val_len / 2 - 1) | 0x80; + } else { + buf[len++] = offs | 0x80; + buf[len++] = (val_len / 2 - 1) | 0x80; + } + memcpy(buf + len, data8 + 2, val_len); + len += val_len; + + return spi_write(spi, buf, len); +} + +static const struct regmap_bus zl38_regmap_bus = { + .read = zl38_bus_read, + .write = zl38_bus_write, + .max_raw_write = ZL38_MAX_RAW_XFER, + .max_raw_read = ZL38_MAX_RAW_XFER, +}; + +static const struct regmap_config zl38_regmap_conf = { + .reg_bits = 16, + .val_bits = 16, + .reg_stride = 2, + .use_single_read = true, + .use_single_write = true, +}; + +static int zl38_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct zl38_codec_priv *priv; + struct gpio_desc *reset_gpio; + int err; + + /* get the chip to a known state by putting it in reset */ + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); + if (reset_gpio) { + /* datasheet: need > 10us for a digital + analog reset */ + usleep_range(15, 50); + /* take the chip out of reset */ + gpiod_set_value_cansleep(reset_gpio, 0); + /* datasheet: need > 3ms for digital section to become stable */ + usleep_range(3000, 10000); + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + dev_set_drvdata(dev, priv); + priv->regmap = devm_regmap_init(dev, &zl38_regmap_bus, spi, + &zl38_regmap_conf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + err = zl38_load_firmware(dev, priv->regmap); + if (err) + return err; + + err = zl38_check_revision(dev, priv->regmap); + if (err) + return err; + + priv->gpio_chip = devm_kmemdup(dev, &template_chip, + sizeof(template_chip), GFP_KERNEL); + if (!priv->gpio_chip) + return -ENOMEM; + priv->gpio_chip->parent = dev; + err = devm_gpiochip_add_data(dev, priv->gpio_chip, priv->regmap); + if (err) + return err; + + /* setup the cross-point switch for stereo bypass */ + err = regmap_multi_reg_write(priv->regmap, cp_config_stereo_bypass, + ARRAY_SIZE(cp_config_stereo_bypass)); + if (err) + return err; + /* setup for 12MHz crystal connected to the chip */ + err = regmap_update_bits(priv->regmap, REG_CLK_CFG, CLK_CFG_SOURCE_XTAL, + CLK_CFG_SOURCE_XTAL); + if (err) + return err; + + return devm_snd_soc_register_component(dev, &zl38_component_dev, + &zl38_dai, 1); +} + +static const struct of_device_id zl38_dt_ids[] __maybe_unused = { + { .compatible = "mscc,zl38060", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, zl38_dt_ids); + +static const struct spi_device_id zl38_spi_ids[] = { + { "zl38060", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, zl38_spi_ids); + +static struct spi_driver zl38060_spi_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(zl38_dt_ids), + }, + .probe = zl38_spi_probe, + .id_table = zl38_spi_ids, +}; +module_spi_driver(zl38060_spi_driver); + +MODULE_DESCRIPTION("ASoC ZL38060 driver"); +MODULE_AUTHOR("Sven Van Asbroeck "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3